Line data Source code
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 1901 : 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 1901 : ip6_main_t *im = &ip6_main;
59 1901 : 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 1901 : vlib_node_get_runtime (vm, ip6_icmp_input_node.index);
65 :
66 1901 : from = vlib_frame_vector_args (frame);
67 1901 : n_left_from = n_packets;
68 1901 : next_index = node->cached_next_index;
69 :
70 1901 : if (node->flags & VLIB_NODE_FLAG_TRACE)
71 1656 : vlib_trace_frame_buffers_only (vm, node, from, frame->n_vectors,
72 : /* stride */ 1,
73 : sizeof (icmp6_input_trace_t));
74 :
75 1901 : option_type =
76 : (is_solicitation
77 : ? ICMP6_NEIGHBOR_DISCOVERY_OPTION_source_link_layer_address
78 1901 : : ICMP6_NEIGHBOR_DISCOVERY_OPTION_target_link_layer_address);
79 1901 : n_advertisements_sent = 0;
80 :
81 3802 : while (n_left_from > 0)
82 : {
83 1901 : vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
84 :
85 3806 : 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 1905 : bi0 = to_next[0] = from[0];
98 :
99 1905 : from += 1;
100 1905 : to_next += 1;
101 1905 : n_left_from -= 1;
102 1905 : n_left_to_next -= 1;
103 :
104 1905 : p0 = vlib_get_buffer (vm, bi0);
105 1905 : ip0 = vlib_buffer_get_current (p0);
106 1905 : h0 = ip6_next_header (ip0);
107 1905 : options_len0 =
108 1905 : clib_net_to_host_u16 (ip0->payload_length) - sizeof (h0[0]);
109 :
110 1905 : error0 = ICMP6_ERROR_NONE;
111 1905 : sw_if_index0 = vnet_buffer (p0)->sw_if_index[VLIB_RX];
112 1905 : ip6_sadd_link_local =
113 1905 : ip6_address_is_link_local_unicast (&ip0->src_address);
114 1905 : ip6_sadd_unspecified =
115 1905 : ip6_address_is_unspecified (&ip0->src_address);
116 :
117 : /* Check that source address is unspecified, link-local or else on-link. */
118 1905 : if (!ip6_sadd_unspecified && !ip6_sadd_link_local)
119 : {
120 1754 : u32 src_adj_index0 = ip6_src_lookup_for_packet (im, p0, ip0);
121 :
122 1754 : if (ADJ_INDEX_INVALID != src_adj_index0)
123 : {
124 1752 : ip_adjacency_t *adj0 = adj_get (src_adj_index0);
125 :
126 : /* Allow all realistic-looking rewrite adjacencies to pass */
127 1752 : ni0 = adj0->lookup_next_index;
128 1752 : is_rewrite0 = (ni0 >= IP_LOOKUP_NEXT_ARP) &&
129 : (ni0 < IP6_LOOKUP_N_NEXT);
130 :
131 1752 : error0 = ((adj0->rewrite_header.sw_if_index != sw_if_index0
132 1749 : || !is_rewrite0)
133 : ?
134 : ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_NOT_ON_LINK
135 3501 : : error0);
136 : }
137 : else
138 : {
139 2 : error0 =
140 : ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_NOT_ON_LINK;
141 : }
142 : }
143 :
144 1905 : o0 = (void *) (h0 + 1);
145 1905 : o0 = ((options_len0 == 8 && o0->header.type == option_type
146 3810 : && o0->header.n_data_u64s == 1) ? o0 : 0);
147 :
148 : /* If src address unspecified or link local, donot learn neighbor MAC */
149 1905 : if (PREDICT_TRUE (error0 == ICMP6_ERROR_NONE && o0 != 0 &&
150 : !ip6_sadd_unspecified))
151 : {
152 : /* *INDENT-OFF* */
153 1752 : 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 1752 : memcpy (&learn.mac, o0->ethernet_address, sizeof (learn.mac));
164 1752 : ip_neighbor_learn_dp (&learn);
165 : }
166 :
167 1905 : 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 1868 : ip6_fib_table_get_index_for_sw_if_index (sw_if_index0);
175 :
176 1868 : if (~0 == fib_index)
177 : {
178 0 : error0 = ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_UNKNOWN;
179 : }
180 : else
181 : {
182 1868 : if (ip6_address_is_link_local_unicast (&h0->target_address))
183 : {
184 74 : fei = ip6_fib_table_lookup_exact_match
185 : (ip6_ll_fib_get (sw_if_index0),
186 74 : &h0->target_address, 128);
187 : }
188 : else
189 : {
190 1794 : fei = ip6_fib_table_lookup_exact_match (fib_index,
191 1794 : &h0->target_address,
192 : 128);
193 : }
194 :
195 1868 : if (FIB_NODE_INDEX_INVALID == fei)
196 : {
197 : /* The target address is not in the FIB */
198 150 : error0 =
199 : ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_UNKNOWN;
200 : }
201 : else
202 : {
203 1718 : if (FIB_ENTRY_FLAG_LOCAL &
204 1718 : 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 4 : else if (FIB_ENTRY_FLAG_LOCAL &
211 4 : 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 2 : 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 1 : error0 =
227 : ICMP6_ERROR_NEIGHBOR_SOLICITATION_SOURCE_UNKNOWN;
228 : }
229 : }
230 : }
231 : }
232 :
233 1905 : if (is_solicitation)
234 : {
235 1873 : next0 = (error0 != ICMP6_ERROR_NONE ?
236 1873 : ICMP6_NEIGHBOR_SOLICITATION_NEXT_DROP :
237 : ICMP6_NEIGHBOR_SOLICITATION_NEXT_REPLY);
238 1873 : c_type = IP_NEIGHBOR_CTR_REQUEST;
239 : }
240 : else
241 : {
242 32 : next0 = 0;
243 32 : error0 = error0 == ICMP6_ERROR_NONE ?
244 32 : ICMP6_ERROR_NEIGHBOR_ADVERTISEMENTS_RX : error0;
245 32 : c_type = IP_NEIGHBOR_CTR_REPLY;
246 : }
247 :
248 1905 : 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 1905 : if (is_solicitation && error0 == ICMP6_ERROR_NONE)
253 : {
254 1717 : icmp6_send_neighbor_advertisement (vm, p0, ip0, h0, o0,
255 : sw_if_index0);
256 1717 : n_advertisements_sent++;
257 : }
258 :
259 1905 : p0->error = error_node->errors[error0];
260 :
261 1905 : vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
262 : to_next, n_left_to_next,
263 : bi0, next0);
264 : }
265 :
266 1901 : vlib_put_next_frame (vm, node, next_index, n_left_to_next);
267 : }
268 :
269 : /* Account for advertisements sent. */
270 1901 : vlib_error_count (vm, error_node->node_index,
271 : ICMP6_ERROR_NEIGHBOR_ADVERTISEMENTS_TX,
272 : n_advertisements_sent);
273 :
274 1901 : return frame->n_vectors;
275 : }
276 :
277 : static const ethernet_interface_t *
278 2113 : 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 2113 : sw = vnet_get_sup_sw_interface (vnet_get_main (), sw_if_index);
284 2113 : if (sw->type == VNET_SW_INTERFACE_TYPE_HARDWARE)
285 2113 : return (ethernet_get_interface (ðernet_main, sw->hw_if_index));
286 :
287 0 : 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 2113 : ip6_nd_link_enable (u32 sw_if_index)
297 : {
298 : const ethernet_interface_t *eth;
299 : ip6_nd_t *ind;
300 :
301 2113 : eth = ip6_nd_get_eth_itf (sw_if_index);
302 :
303 2113 : if (NULL == eth)
304 155 : return;
305 :
306 1958 : ASSERT (INDEX_INVALID == ip6_link_delegate_get (sw_if_index,
307 : ip6_nd_delegate_id));
308 :
309 1958 : pool_get_zero (ip6_nd_pool, ind);
310 :
311 1958 : ind->sw_if_index = sw_if_index;
312 :
313 1958 : ip6_link_delegate_update (sw_if_index, ip6_nd_delegate_id,
314 1958 : ind - ip6_nd_pool);
315 : }
316 :
317 : static void
318 1787 : ip6_nd_delegate_disable (index_t indi)
319 : {
320 : ip6_nd_t *ind;
321 :
322 1787 : ind = pool_elt_at_index (ip6_nd_pool, indi);
323 :
324 1787 : pool_put (ip6_nd_pool, ind);
325 1787 : }
326 :
327 : static uword
328 1869 : icmp6_neighbor_solicitation (vlib_main_t * vm,
329 : vlib_node_runtime_t * node, vlib_frame_t * frame)
330 : {
331 1869 : return icmp6_neighbor_solicitation_or_advertisement (vm, node, frame,
332 : /* is_solicitation */
333 : 1);
334 : }
335 :
336 : static uword
337 32 : icmp6_neighbor_advertisement (vlib_main_t * vm,
338 : vlib_node_runtime_t * node,
339 : vlib_frame_t * frame)
340 : {
341 32 : return icmp6_neighbor_solicitation_or_advertisement (vm, node, frame,
342 : /* is_solicitation */
343 : 0);
344 : }
345 :
346 : /* *INDENT-OFF* */
347 183788 : 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 183788 : 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 0 : format_ip6_nd (u8 * s, va_list * args)
381 : {
382 0 : CLIB_UNUSED (index_t indi) = va_arg (*args, index_t);
383 0 : u32 indent = va_arg (*args, u32);
384 :
385 0 : s = format (s, "%UNeighbor Discovery: enabled\n",
386 : format_white_space, indent);
387 :
388 0 : s = format (s, "%UICMP redirects are disabled\n",
389 : format_white_space, indent + 2);
390 0 : s = format (s, "%UICMP unreachables are not sent\n",
391 : format_white_space, indent + 2);
392 0 : 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 0 : 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 575 : ip6_nd_init (vlib_main_t * vm)
417 : {
418 575 : icmp6_register_type (vm, ICMP6_neighbor_solicitation,
419 : ip6_icmp_neighbor_solicitation_node.index);
420 575 : icmp6_register_type (vm, ICMP6_neighbor_advertisement,
421 : ip6_icmp_neighbor_advertisement_node.index);
422 :
423 575 : ip_neighbor_register (AF_IP6, &ip6_nd_impl_vft);
424 :
425 575 : ip6_nd_delegate_id = ip6_link_delegate_register (&ip6_nd_delegate_vft);
426 :
427 575 : return 0;
428 : }
429 :
430 : /* *INDENT-OFF* */
431 97343 : 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 : */
|