blob: 311cbf743f7ec19b4d9cf504ceeb3e1f7a24ad7f [file] [log] [blame]
Neale Rannscbe25aa2019-09-30 10:53:31 +00001/*
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
38typedef 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
48static ip6_link_delegate_id_t ip6_nd_delegate_id;
49static ip6_nd_t *ip6_nd_pool;
50
51
52typedef 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
59static_always_inline uword
60icmp6_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 {
Neale Rannsdc617b82020-08-20 08:22:56 +0000160 /* *INDENT-OFF* */
Neale Rannscbe25aa2019-09-30 10:53:31 +0000161 ip_neighbor_learn_t learn = {
162 .sw_if_index = sw_if_index0,
Neale Rannsdc617b82020-08-20 08:22:56 +0000163 .ip = {
164 .version = AF_IP6,
165 .ip.ip6 = (is_solicitation ?
166 ip0->src_address :
167 h0->target_address),
168 }
Neale Rannscbe25aa2019-09-30 10:53:31 +0000169 };
Neale Rannsdc617b82020-08-20 08:22:56 +0000170 /* *INDENT-ON* */
Neale Rannscbe25aa2019-09-30 10:53:31 +0000171 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 }
Neale Rannse2b67362021-04-02 07:34:39 +0000218 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))
Neale Rannscbe25aa2019-09-30 10:53:31 +0000227 {
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 {
Benoît Ganneb44c77d2020-10-20 16:24:17 +0200276 clib_memcpy (o0->ethernet_address, &eth_if0->address, 6);
Neale Rannscbe25aa2019-09-30 10:53:31 +0000277 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)
Benoît Ganneb44c77d2020-10-20 16:24:17 +0200297 clib_memcpy (eth0->src_address, &eth_if0->address, 6);
Neale Rannscbe25aa2019-09-30 10:53:31 +0000298
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
326static const ethernet_interface_t *
327ip6_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 */
344static void
345ip6_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
366static void
367ip6_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
376static uword
377icmp6_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
385static uword
386icmp6_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* */
396VLIB_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
412VLIB_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 = {
Alexander Chernavin86d6df62020-06-11 09:57:33 -0400423 [0] = "ip6-punt",
Neale Rannscbe25aa2019-09-30 10:53:31 +0000424 },
425};
426/* *INDENT-ON* */
427
428static u8 *
429format_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 */
450const 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 */
458const 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
464static clib_error_t *
465ip6_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
Neale Rannsdc617b82020-08-20 08:22:56 +0000472 ip_neighbor_register (AF_IP6, &ip6_nd_impl_vft);
Neale Rannscbe25aa2019-09-30 10:53:31 +0000473
474 ip6_nd_delegate_id = ip6_link_delegate_register (&ip6_nd_delegate_vft);
475
476 return 0;
477}
478
479/* *INDENT-OFF* */
480VLIB_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 */