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