udp: fix csum computation when offload disabled
[vpp.git] / src / vnet / ip6-nd / ip6_nd.c
1 /*
2  * ip/ip6_neighbor.c: IP6 neighbor handling
3  *
4  * Copyright (c) 2010 Cisco and/or its affiliates.
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #include <vnet/ip6-nd/ip6_nd.h>
19 #include <vnet/ip6-nd/ip6_nd_inline.h>
20
21 #include <vnet/ip-neighbor/ip_neighbor.h>
22 #include <vnet/ip-neighbor/ip_neighbor_dp.h>
23
24 #include <vnet/fib/ip6_fib.h>
25 #include <vnet/ip/ip6_link.h>
26 #include <vnet/ip/ip6_ll_table.h>
27
28 /**
29  * @file
30  * @brief IPv6 Neighbor Adjacency and Neighbor Discovery.
31  *
32  * The files contains the API and CLI code for managing IPv6 neighbor
33  * adjacency tables and neighbor discovery logic.
34  */
35
36 #define DEF_MAX_RADV_INTERVAL 200
37 #define DEF_MIN_RADV_INTERVAL .75 * DEF_MAX_RADV_INTERVAL
38
39 typedef struct ip6_nd_t_
40 {
41   /* local information */
42   u32 sw_if_index;
43
44   /* stats */
45   u32 n_solicitations_rcvd;
46   u32 n_solicitations_dropped;
47 } ip6_nd_t;
48
49 static ip6_link_delegate_id_t ip6_nd_delegate_id;
50 static ip6_nd_t *ip6_nd_pool;
51
52 static_always_inline uword
53 icmp6_neighbor_solicitation_or_advertisement (vlib_main_t * vm,
54                                               vlib_node_runtime_t * node,
55                                               vlib_frame_t * frame,
56                                               uword is_solicitation)
57 {
58   ip6_main_t *im = &ip6_main;
59   uword n_packets = frame->n_vectors;
60   u32 *from, *to_next;
61   u32 n_left_from, n_left_to_next, next_index, n_advertisements_sent;
62   icmp6_neighbor_discovery_option_type_t option_type;
63   vlib_node_runtime_t *error_node =
64     vlib_node_get_runtime (vm, ip6_icmp_input_node.index);
65
66   from = vlib_frame_vector_args (frame);
67   n_left_from = n_packets;
68   next_index = node->cached_next_index;
69
70   if (node->flags & VLIB_NODE_FLAG_TRACE)
71     vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
72                                    /* stride */ 1,
73                                    sizeof (icmp6_input_trace_t));
74
75   option_type =
76     (is_solicitation
77      ? ICMP6_NEIGHBOR_DISCOVERY_OPTION_source_link_layer_address
78      : ICMP6_NEIGHBOR_DISCOVERY_OPTION_target_link_layer_address);
79   n_advertisements_sent = 0;
80
81   while (n_left_from > 0)
82     {
83       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
84
85       while (n_left_from > 0 && n_left_to_next > 0)
86         {
87           vlib_buffer_t *p0;
88           ip6_header_t *ip0;
89           icmp6_neighbor_solicitation_or_advertisement_header_t *h0;
90           icmp6_neighbor_discovery_ethernet_link_layer_address_option_t *o0;
91           u32 bi0, options_len0, sw_if_index0, next0, error0;
92           u32 ip6_sadd_link_local, ip6_sadd_unspecified;
93           ip_neighbor_counter_type_t c_type;
94           int is_rewrite0;
95           u32 ni0;
96
97           bi0 = to_next[0] = from[0];
98
99           from += 1;
100           to_next += 1;
101           n_left_from -= 1;
102           n_left_to_next -= 1;
103
104           p0 = vlib_get_buffer (vm, bi0);
105           ip0 = vlib_buffer_get_current (p0);
106           h0 = ip6_next_header (ip0);
107           options_len0 =
108             clib_net_to_host_u16 (ip0->payload_length) - sizeof (h0[0]);
109
110           error0 = ICMP6_ERROR_NONE;
111           sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
112           ip6_sadd_link_local =
113             ip6_address_is_link_local_unicast (&ip0->src_address);
114           ip6_sadd_unspecified =
115             ip6_address_is_unspecified (&ip0->src_address);
116
117           /* Check that source address is unspecified, link-local or else on-link. */
118           if (!ip6_sadd_unspecified && !ip6_sadd_link_local)
119             {
120               u32 src_adj_index0 = ip6_src_lookup_for_packet (im, p0, ip0);
121
122               if (ADJ_INDEX_INVALID != src_adj_index0)
123                 {
124                   ip_adjacency_t *adj0 = adj_get (src_adj_index0);
125
126                   /* Allow all realistic-looking rewrite adjacencies to pass */
127                   ni0 = adj0->lookup_next_index;
128                   is_rewrite0 = (ni0 >= IP_LOOKUP_NEXT_ARP) &&
129                     (ni0 < IP6_LOOKUP_N_NEXT);
130
131                   error0 = ((adj0->rewrite_header.sw_if_index != sw_if_index0
132                              || !is_rewrite0)
133                             ?
134                             ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_NOT_ON_LINK
135                             : error0);
136                 }
137               else
138                 {
139                   error0 =
140                     ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_NOT_ON_LINK;
141                 }
142             }
143
144           o0 = (void *) (h0 + 1);
145           o0 = ((options_len0 == 8 && o0->header.type == option_type
146                  && o0->header.n_data_u64s == 1) ? o0 : 0);
147
148           /* If src address unspecified or link local, donot learn neighbor MAC */
149           if (PREDICT_TRUE (error0 == ICMP6_ERROR_NONE && o0 != 0 &&
150                             !ip6_sadd_unspecified))
151             {
152               ip_neighbor_learn_t learn = {
153                 .sw_if_index = sw_if_index0,
154                 .ip = {
155                   .version = AF_IP6,
156                   .ip.ip6 = (is_solicitation ?
157                              ip0->src_address :
158                              h0->target_address),
159                 }
160               };
161               memcpy (&learn.mac, o0->ethernet_address, sizeof (learn.mac));
162               ip_neighbor_learn_dp (&learn);
163             }
164
165           if (is_solicitation && error0 == ICMP6_ERROR_NONE)
166             {
167               /* Check that target address is local to this router. */
168               fib_node_index_t fei;
169               u32 fib_index;
170
171               fib_index =
172                 ip6_fib_table_get_index_for_sw_if_index (sw_if_index0);
173
174               if (~0 == fib_index)
175                 {
176                   error0 = ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_UNKNOWN;
177                 }
178               else
179                 {
180                   if (ip6_address_is_link_local_unicast (&h0->target_address))
181                     {
182                       fei = ip6_fib_table_lookup_exact_match
183                         (ip6_ll_fib_get (sw_if_index0),
184                          &h0->target_address, 128);
185                     }
186                   else
187                     {
188                       fei = ip6_fib_table_lookup_exact_match (fib_index,
189                                                               &h0->target_address,
190                                                               128);
191                     }
192
193                   if (FIB_NODE_INDEX_INVALID == fei)
194                     {
195                       /* The target address is not in the FIB */
196                       error0 =
197                         ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_UNKNOWN;
198                     }
199                   else
200                     {
201                       if (FIB_ENTRY_FLAG_LOCAL &
202                           fib_entry_get_flags_for_source (fei,
203                                                           FIB_SOURCE_INTERFACE))
204                         {
205                           /* It's an address that belongs to one of our interfaces
206                            * that's good. */
207                         }
208                       else if (FIB_ENTRY_FLAG_LOCAL &
209                                fib_entry_get_flags_for_source (
210                                  fei, FIB_SOURCE_IP6_ND))
211                         {
212                           /* It's one of our link local addresses
213                            * that's good. */
214                         }
215                       else if (fib_entry_is_sourced (fei,
216                                                      FIB_SOURCE_IP6_ND_PROXY))
217                         {
218                           /* The address was added by IPv6 Proxy ND config.
219                            * We should only respond to these if the NS arrived on
220                            * the link that has a matching covering prefix */
221                         }
222                       else
223                         {
224                           error0 =
225                             ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_UNKNOWN;
226                         }
227                     }
228                 }
229             }
230
231           if (is_solicitation)
232             {
233               next0 = (error0 != ICMP6_ERROR_NONE ?
234                                ICMP6_NEIGHBOR_SOLICITATION_NEXT_DROP :
235                                ICMP6_NEIGHBOR_SOLICITATION_NEXT_REPLY);
236               c_type = IP_NEIGHBOR_CTR_REQUEST;
237             }
238           else
239             {
240               next0 = 0;
241               error0 = error0 == ICMP6_ERROR_NONE ?
242                 ICMP6_ERROR_NEIGHBOR_ADVERTISEMENTS_RX : error0;
243               c_type = IP_NEIGHBOR_CTR_REPLY;
244             }
245
246           vlib_increment_simple_counter (
247             &ip_neighbor_counters[AF_IP6].ipnc[VLIB_RX][c_type],
248             vm->thread_index, sw_if_index0, 1);
249
250           if (is_solicitation && error0 == ICMP6_ERROR_NONE)
251             {
252               icmp6_send_neighbor_advertisement (vm, p0, ip0, h0, o0,
253                                                  sw_if_index0);
254               n_advertisements_sent++;
255             }
256
257           p0->error = error_node->errors[error0];
258
259           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
260                                            to_next, n_left_to_next,
261                                            bi0, next0);
262         }
263
264       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
265     }
266
267   /* Account for advertisements sent. */
268   vlib_error_count (vm, error_node->node_index,
269                     ICMP6_ERROR_NEIGHBOR_ADVERTISEMENTS_TX,
270                     n_advertisements_sent);
271
272   return frame->n_vectors;
273 }
274
275 static const ethernet_interface_t *
276 ip6_nd_get_eth_itf (u32 sw_if_index)
277 {
278   const vnet_sw_interface_t *sw;
279
280   /* lookup radv container  - ethernet interfaces only */
281   sw = vnet_get_sup_sw_interface (vnet_get_main (), sw_if_index);
282   if (sw->type == VNET_SW_INTERFACE_TYPE_HARDWARE)
283     return (ethernet_get_interface (&ethernet_main, sw->hw_if_index));
284
285   return (NULL);
286 }
287
288 /**
289  * @brief called when IP6 is enabled on a link.
290  * create and initialize router advertisement parameters with default
291  * values for this intfc
292  */
293 static void
294 ip6_nd_link_enable (u32 sw_if_index)
295 {
296   const ethernet_interface_t *eth;
297   ip6_nd_t *ind;
298
299   eth = ip6_nd_get_eth_itf (sw_if_index);
300
301   if (NULL == eth)
302     return;
303
304   ASSERT (INDEX_INVALID == ip6_link_delegate_get (sw_if_index,
305                                                   ip6_nd_delegate_id));
306
307   pool_get_zero (ip6_nd_pool, ind);
308
309   ind->sw_if_index = sw_if_index;
310
311   ip6_link_delegate_update (sw_if_index, ip6_nd_delegate_id,
312                             ind - ip6_nd_pool);
313 }
314
315 static void
316 ip6_nd_delegate_disable (index_t indi)
317 {
318   ip6_nd_t *ind;
319
320   ind = pool_elt_at_index (ip6_nd_pool, indi);
321
322   pool_put (ip6_nd_pool, ind);
323 }
324
325 static uword
326 icmp6_neighbor_solicitation (vlib_main_t * vm,
327                              vlib_node_runtime_t * node, vlib_frame_t * frame)
328 {
329   return icmp6_neighbor_solicitation_or_advertisement (vm, node, frame,
330                                                        /* is_solicitation */
331                                                        1);
332 }
333
334 static uword
335 icmp6_neighbor_advertisement (vlib_main_t * vm,
336                               vlib_node_runtime_t * node,
337                               vlib_frame_t * frame)
338 {
339   return icmp6_neighbor_solicitation_or_advertisement (vm, node, frame,
340                                                        /* is_solicitation */
341                                                        0);
342 }
343
344 VLIB_REGISTER_NODE (ip6_icmp_neighbor_solicitation_node,static) =
345 {
346   .function = icmp6_neighbor_solicitation,
347   .name = "icmp6-neighbor-solicitation",
348
349   .vector_size = sizeof (u32),
350
351   .format_trace = format_icmp6_input_trace,
352
353   .n_next_nodes = ICMP6_NEIGHBOR_SOLICITATION_N_NEXT,
354   .next_nodes = {
355     [ICMP6_NEIGHBOR_SOLICITATION_NEXT_DROP] = "ip6-drop",
356     [ICMP6_NEIGHBOR_SOLICITATION_NEXT_REPLY] = "interface-output",
357   },
358 };
359
360 VLIB_REGISTER_NODE (ip6_icmp_neighbor_advertisement_node,static) =
361 {
362   .function = icmp6_neighbor_advertisement,
363   .name = "icmp6-neighbor-advertisement",
364
365   .vector_size = sizeof (u32),
366
367   .format_trace = format_icmp6_input_trace,
368
369   .n_next_nodes = 1,
370   .next_nodes = {
371     [0] = "ip6-punt",
372   },
373 };
374
375 static u8 *
376 format_ip6_nd (u8 * s, va_list * args)
377 {
378   CLIB_UNUSED (index_t indi) = va_arg (*args, index_t);
379   u32 indent = va_arg (*args, u32);
380
381   s = format (s, "%UNeighbor Discovery: enabled\n",
382               format_white_space, indent);
383
384   s = format (s, "%UICMP redirects are disabled\n",
385               format_white_space, indent + 2);
386   s = format (s, "%UICMP unreachables are not sent\n",
387               format_white_space, indent + 2);
388   s = format (s, "%UND DAD is disabled\n", format_white_space, indent + 2);
389   //s = format (s, "%UND reachable time is %d milliseconds\n",);
390
391   return (s);
392 }
393
394 /**
395  * VFT to act as an implementation of a neighbour protocol
396  */
397 const static ip_neighbor_vft_t ip6_nd_impl_vft = {
398   .inv_proxy6_add = ip6_nd_proxy_add,
399   .inv_proxy6_del = ip6_nd_proxy_del,
400 };
401
402 /**
403  * VFT for registering as a delegate to an IP6 link
404  */
405 const static ip6_link_delegate_vft_t ip6_nd_delegate_vft = {
406   .ildv_disable = ip6_nd_delegate_disable,
407   .ildv_enable = ip6_nd_link_enable,
408   .ildv_format = format_ip6_nd,
409 };
410
411 static clib_error_t *
412 ip6_nd_init (vlib_main_t * vm)
413 {
414   icmp6_register_type (vm, ICMP6_neighbor_solicitation,
415                        ip6_icmp_neighbor_solicitation_node.index);
416   icmp6_register_type (vm, ICMP6_neighbor_advertisement,
417                        ip6_icmp_neighbor_advertisement_node.index);
418
419   ip_neighbor_register (AF_IP6, &ip6_nd_impl_vft);
420
421   ip6_nd_delegate_id = ip6_link_delegate_register (&ip6_nd_delegate_vft);
422
423   return 0;
424 }
425
426 VLIB_INIT_FUNCTION (ip6_nd_init) =
427 {
428   .runs_after = VLIB_INITS("icmp6_init"),
429 };
430
431 /*
432  * fd.io coding-style-patch-verification: ON
433  *
434  * Local Variables:
435  * eval: (c-set-style "gnu")
436  * End:
437  */