blob: 513d2bf6e8758c244576b64f9dac124a9f1e178c [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>
Mohsin Kazmib31ddb52021-04-06 19:24:00 +020019#include <vnet/ip6-nd/ip6_nd_inline.h>
Neale Rannscbe25aa2019-09-30 10:53:31 +000020
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
39typedef 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
49static ip6_link_delegate_id_t ip6_nd_delegate_id;
50static ip6_nd_t *ip6_nd_pool;
51
Neale Rannscbe25aa2019-09-30 10:53:31 +000052static_always_inline uword
53icmp6_neighbor_solicitation_or_advertisement (vlib_main_t * vm,
54 vlib_node_runtime_t * node,
55 vlib_frame_t * frame,
56 uword is_solicitation)
57{
Neale Rannscbe25aa2019-09-30 10:53:31 +000058 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);
Neale Rannscbe25aa2019-09-30 10:53:31 +000065
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;
Neale Rannsfd2417b2021-07-16 14:00:16 +000093 ip_neighbor_counter_type_t c_type;
Neale Rannscbe25aa2019-09-30 10:53:31 +000094 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 {
Neale Rannsdc617b82020-08-20 08:22:56 +0000152 /* *INDENT-OFF* */
Neale Rannscbe25aa2019-09-30 10:53:31 +0000153 ip_neighbor_learn_t learn = {
154 .sw_if_index = sw_if_index0,
Neale Rannsdc617b82020-08-20 08:22:56 +0000155 .ip = {
156 .version = AF_IP6,
157 .ip.ip6 = (is_solicitation ?
158 ip0->src_address :
159 h0->target_address),
160 }
Neale Rannscbe25aa2019-09-30 10:53:31 +0000161 };
Neale Rannsdc617b82020-08-20 08:22:56 +0000162 /* *INDENT-ON* */
Neale Rannscbe25aa2019-09-30 10:53:31 +0000163 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 }
Neale Rannse2b67362021-04-02 07:34:39 +0000210 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))
Neale Rannscbe25aa2019-09-30 10:53:31 +0000219 {
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)
Neale Rannsfd2417b2021-07-16 14:00:16 +0000234 {
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 }
Neale Rannscbe25aa2019-09-30 10:53:31 +0000240 else
241 {
242 next0 = 0;
243 error0 = error0 == ICMP6_ERROR_NONE ?
244 ICMP6_ERROR_NEIGHBOR_ADVERTISEMENTS_RX : error0;
Neale Rannsfd2417b2021-07-16 14:00:16 +0000245 c_type = IP_NEIGHBOR_CTR_REPLY;
Neale Rannscbe25aa2019-09-30 10:53:31 +0000246 }
247
Neale Rannsfd2417b2021-07-16 14:00:16 +0000248 vlib_increment_simple_counter (
249 &ip_neighbor_counters[AF_IP6].ipnc[VLIB_RX][c_type],
250 vm->thread_index, sw_if_index0, 1);
251
Neale Rannscbe25aa2019-09-30 10:53:31 +0000252 if (is_solicitation && error0 == ICMP6_ERROR_NONE)
253 {
Mohsin Kazmib31ddb52021-04-06 19:24:00 +0200254 icmp6_send_neighbor_advertisement (vm, p0, ip0, h0, o0,
255 sw_if_index0);
Neale Rannscbe25aa2019-09-30 10:53:31 +0000256 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
277static const ethernet_interface_t *
278ip6_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 */
295static void
296ip6_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
317static void
318ip6_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
327static uword
328icmp6_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
336static uword
337icmp6_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* */
347VLIB_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
363VLIB_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 = {
Alexander Chernavin86d6df62020-06-11 09:57:33 -0400374 [0] = "ip6-punt",
Neale Rannscbe25aa2019-09-30 10:53:31 +0000375 },
376};
377/* *INDENT-ON* */
378
379static u8 *
380format_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 */
401const 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 */
409const 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
415static clib_error_t *
416ip6_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
Neale Rannsdc617b82020-08-20 08:22:56 +0000423 ip_neighbor_register (AF_IP6, &ip6_nd_impl_vft);
Neale Rannscbe25aa2019-09-30 10:53:31 +0000424
425 ip6_nd_delegate_id = ip6_link_delegate_register (&ip6_nd_delegate_vft);
426
427 return 0;
428}
429
430/* *INDENT-OFF* */
431VLIB_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 */