772c811ae209cd6a299393d933ad740814e35857
[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           int is_rewrite0;
94           u32 ni0;
95
96           bi0 = to_next[0] = from[0];
97
98           from += 1;
99           to_next += 1;
100           n_left_from -= 1;
101           n_left_to_next -= 1;
102
103           p0 = vlib_get_buffer (vm, bi0);
104           ip0 = vlib_buffer_get_current (p0);
105           h0 = ip6_next_header (ip0);
106           options_len0 =
107             clib_net_to_host_u16 (ip0->payload_length) - sizeof (h0[0]);
108
109           error0 = ICMP6_ERROR_NONE;
110           sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
111           ip6_sadd_link_local =
112             ip6_address_is_link_local_unicast (&ip0->src_address);
113           ip6_sadd_unspecified =
114             ip6_address_is_unspecified (&ip0->src_address);
115
116           /* Check that source address is unspecified, link-local or else on-link. */
117           if (!ip6_sadd_unspecified && !ip6_sadd_link_local)
118             {
119               u32 src_adj_index0 = ip6_src_lookup_for_packet (im, p0, ip0);
120
121               if (ADJ_INDEX_INVALID != src_adj_index0)
122                 {
123                   ip_adjacency_t *adj0 = adj_get (src_adj_index0);
124
125                   /* Allow all realistic-looking rewrite adjacencies to pass */
126                   ni0 = adj0->lookup_next_index;
127                   is_rewrite0 = (ni0 >= IP_LOOKUP_NEXT_ARP) &&
128                     (ni0 < IP6_LOOKUP_N_NEXT);
129
130                   error0 = ((adj0->rewrite_header.sw_if_index != sw_if_index0
131                              || !is_rewrite0)
132                             ?
133                             ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_NOT_ON_LINK
134                             : error0);
135                 }
136               else
137                 {
138                   error0 =
139                     ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_NOT_ON_LINK;
140                 }
141             }
142
143           o0 = (void *) (h0 + 1);
144           o0 = ((options_len0 == 8 && o0->header.type == option_type
145                  && o0->header.n_data_u64s == 1) ? o0 : 0);
146
147           /* If src address unspecified or link local, donot learn neighbor MAC */
148           if (PREDICT_TRUE (error0 == ICMP6_ERROR_NONE && o0 != 0 &&
149                             !ip6_sadd_unspecified))
150             {
151               /* *INDENT-OFF* */
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               /* *INDENT-ON* */
162               memcpy (&learn.mac, o0->ethernet_address, sizeof (learn.mac));
163               ip_neighbor_learn_dp (&learn);
164             }
165
166           if (is_solicitation && error0 == ICMP6_ERROR_NONE)
167             {
168               /* Check that target address is local to this router. */
169               fib_node_index_t fei;
170               u32 fib_index;
171
172               fib_index =
173                 ip6_fib_table_get_index_for_sw_if_index (sw_if_index0);
174
175               if (~0 == fib_index)
176                 {
177                   error0 = ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_UNKNOWN;
178                 }
179               else
180                 {
181                   if (ip6_address_is_link_local_unicast (&h0->target_address))
182                     {
183                       fei = ip6_fib_table_lookup_exact_match
184                         (ip6_ll_fib_get (sw_if_index0),
185                          &h0->target_address, 128);
186                     }
187                   else
188                     {
189                       fei = ip6_fib_table_lookup_exact_match (fib_index,
190                                                               &h0->target_address,
191                                                               128);
192                     }
193
194                   if (FIB_NODE_INDEX_INVALID == fei)
195                     {
196                       /* The target address is not in the FIB */
197                       error0 =
198                         ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_UNKNOWN;
199                     }
200                   else
201                     {
202                       if (FIB_ENTRY_FLAG_LOCAL &
203                           fib_entry_get_flags_for_source (fei,
204                                                           FIB_SOURCE_INTERFACE))
205                         {
206                           /* It's an address that belongs to one of our interfaces
207                            * that's good. */
208                         }
209                       else if (FIB_ENTRY_FLAG_LOCAL &
210                                fib_entry_get_flags_for_source (
211                                  fei, FIB_SOURCE_IP6_ND))
212                         {
213                           /* It's one of our link local addresses
214                            * that's good. */
215                         }
216                       else if (fib_entry_is_sourced (fei,
217                                                      FIB_SOURCE_IP6_ND_PROXY))
218                         {
219                           /* The address was added by IPv6 Proxy ND config.
220                            * We should only respond to these if the NS arrived on
221                            * the link that has a matching covering prefix */
222                         }
223                       else
224                         {
225                           error0 =
226                             ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_UNKNOWN;
227                         }
228                     }
229                 }
230             }
231
232           if (is_solicitation)
233             next0 = (error0 != ICMP6_ERROR_NONE
234                      ? ICMP6_NEIGHBOR_SOLICITATION_NEXT_DROP
235                      : ICMP6_NEIGHBOR_SOLICITATION_NEXT_REPLY);
236           else
237             {
238               next0 = 0;
239               error0 = error0 == ICMP6_ERROR_NONE ?
240                 ICMP6_ERROR_NEIGHBOR_ADVERTISEMENTS_RX : error0;
241             }
242
243           if (is_solicitation && error0 == ICMP6_ERROR_NONE)
244             {
245               icmp6_send_neighbor_advertisement (vm, p0, ip0, h0, o0,
246                                                  sw_if_index0);
247               n_advertisements_sent++;
248             }
249
250           p0->error = error_node->errors[error0];
251
252           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
253                                            to_next, n_left_to_next,
254                                            bi0, next0);
255         }
256
257       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
258     }
259
260   /* Account for advertisements sent. */
261   vlib_error_count (vm, error_node->node_index,
262                     ICMP6_ERROR_NEIGHBOR_ADVERTISEMENTS_TX,
263                     n_advertisements_sent);
264
265   return frame->n_vectors;
266 }
267
268 static const ethernet_interface_t *
269 ip6_nd_get_eth_itf (u32 sw_if_index)
270 {
271   const vnet_sw_interface_t *sw;
272
273   /* lookup radv container  - ethernet interfaces only */
274   sw = vnet_get_sup_sw_interface (vnet_get_main (), sw_if_index);
275   if (sw->type == VNET_SW_INTERFACE_TYPE_HARDWARE)
276     return (ethernet_get_interface (&ethernet_main, sw->hw_if_index));
277
278   return (NULL);
279 }
280
281 /**
282  * @brief called when IP6 is enabled on a link.
283  * create and initialize router advertisement parameters with default
284  * values for this intfc
285  */
286 static void
287 ip6_nd_link_enable (u32 sw_if_index)
288 {
289   const ethernet_interface_t *eth;
290   ip6_nd_t *ind;
291
292   eth = ip6_nd_get_eth_itf (sw_if_index);
293
294   if (NULL == eth)
295     return;
296
297   ASSERT (INDEX_INVALID == ip6_link_delegate_get (sw_if_index,
298                                                   ip6_nd_delegate_id));
299
300   pool_get_zero (ip6_nd_pool, ind);
301
302   ind->sw_if_index = sw_if_index;
303
304   ip6_link_delegate_update (sw_if_index, ip6_nd_delegate_id,
305                             ind - ip6_nd_pool);
306 }
307
308 static void
309 ip6_nd_delegate_disable (index_t indi)
310 {
311   ip6_nd_t *ind;
312
313   ind = pool_elt_at_index (ip6_nd_pool, indi);
314
315   pool_put (ip6_nd_pool, ind);
316 }
317
318 static uword
319 icmp6_neighbor_solicitation (vlib_main_t * vm,
320                              vlib_node_runtime_t * node, vlib_frame_t * frame)
321 {
322   return icmp6_neighbor_solicitation_or_advertisement (vm, node, frame,
323                                                        /* is_solicitation */
324                                                        1);
325 }
326
327 static uword
328 icmp6_neighbor_advertisement (vlib_main_t * vm,
329                               vlib_node_runtime_t * node,
330                               vlib_frame_t * frame)
331 {
332   return icmp6_neighbor_solicitation_or_advertisement (vm, node, frame,
333                                                        /* is_solicitation */
334                                                        0);
335 }
336
337 /* *INDENT-OFF* */
338 VLIB_REGISTER_NODE (ip6_icmp_neighbor_solicitation_node,static) =
339 {
340   .function = icmp6_neighbor_solicitation,
341   .name = "icmp6-neighbor-solicitation",
342
343   .vector_size = sizeof (u32),
344
345   .format_trace = format_icmp6_input_trace,
346
347   .n_next_nodes = ICMP6_NEIGHBOR_SOLICITATION_N_NEXT,
348   .next_nodes = {
349     [ICMP6_NEIGHBOR_SOLICITATION_NEXT_DROP] = "ip6-drop",
350     [ICMP6_NEIGHBOR_SOLICITATION_NEXT_REPLY] = "interface-output",
351   },
352 };
353
354 VLIB_REGISTER_NODE (ip6_icmp_neighbor_advertisement_node,static) =
355 {
356   .function = icmp6_neighbor_advertisement,
357   .name = "icmp6-neighbor-advertisement",
358
359   .vector_size = sizeof (u32),
360
361   .format_trace = format_icmp6_input_trace,
362
363   .n_next_nodes = 1,
364   .next_nodes = {
365     [0] = "ip6-punt",
366   },
367 };
368 /* *INDENT-ON* */
369
370 static u8 *
371 format_ip6_nd (u8 * s, va_list * args)
372 {
373   CLIB_UNUSED (index_t indi) = va_arg (*args, index_t);
374   u32 indent = va_arg (*args, u32);
375
376   s = format (s, "%UNeighbor Discovery: enabled\n",
377               format_white_space, indent);
378
379   s = format (s, "%UICMP redirects are disabled\n",
380               format_white_space, indent + 2);
381   s = format (s, "%UICMP unreachables are not sent\n",
382               format_white_space, indent + 2);
383   s = format (s, "%UND DAD is disabled\n", format_white_space, indent + 2);
384   //s = format (s, "%UND reachable time is %d milliseconds\n",);
385
386   return (s);
387 }
388
389 /**
390  * VFT to act as an implementation of a neighbour protocol
391  */
392 const static ip_neighbor_vft_t ip6_nd_impl_vft = {
393   .inv_proxy6_add = ip6_nd_proxy_add,
394   .inv_proxy6_del = ip6_nd_proxy_del,
395 };
396
397 /**
398  * VFT for registering as a delegate to an IP6 link
399  */
400 const static ip6_link_delegate_vft_t ip6_nd_delegate_vft = {
401   .ildv_disable = ip6_nd_delegate_disable,
402   .ildv_enable = ip6_nd_link_enable,
403   .ildv_format = format_ip6_nd,
404 };
405
406 static clib_error_t *
407 ip6_nd_init (vlib_main_t * vm)
408 {
409   icmp6_register_type (vm, ICMP6_neighbor_solicitation,
410                        ip6_icmp_neighbor_solicitation_node.index);
411   icmp6_register_type (vm, ICMP6_neighbor_advertisement,
412                        ip6_icmp_neighbor_advertisement_node.index);
413
414   ip_neighbor_register (AF_IP6, &ip6_nd_impl_vft);
415
416   ip6_nd_delegate_id = ip6_link_delegate_register (&ip6_nd_delegate_vft);
417
418   return 0;
419 }
420
421 /* *INDENT-OFF* */
422 VLIB_INIT_FUNCTION (ip6_nd_init) =
423 {
424   .runs_after = VLIB_INITS("icmp6_init"),
425 };
426 /* *INDENT-ON* */
427
428 /*
429  * fd.io coding-style-patch-verification: ON
430  *
431  * Local Variables:
432  * eval: (c-set-style "gnu")
433  * End:
434  */