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