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 :
20 : #include <vnet/ip/ip.h>
21 : #include <vnet/ip-neighbor/ip_neighbor_dp.h>
22 :
23 : #include <vnet/ip/ip6_link.h>
24 : #include <vnet/ip/ip6_ll_table.h>
25 :
26 : #include <vnet/ethernet/ethernet.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 : /* *INDENT-OFF*/
37 : /* multicast listener report packet format for ethernet. */
38 : typedef CLIB_PACKED (struct
39 : {
40 : ip6_hop_by_hop_ext_t ext_hdr;
41 : ip6_router_alert_option_t alert;
42 : ip6_padN_option_t pad;
43 : icmp46_header_t icmp;
44 : u16 rsvd;
45 : u16 num_addr_records;
46 : icmp6_multicast_address_record_t records[0];
47 : }) icmp6_multicast_listener_report_header_t;
48 :
49 : typedef CLIB_PACKED (struct
50 : {
51 : ip6_header_t ip;
52 : icmp6_multicast_listener_report_header_t report_hdr;
53 : }) icmp6_multicast_listener_report_packet_t;
54 : /* *INDENT-ON*/
55 :
56 : typedef struct
57 : {
58 : /* group information */
59 : u16 num_sources;
60 : u8 type;
61 : ip6_address_t mcast_address;
62 : ip6_address_t *mcast_source_address_pool;
63 : } ip6_mldp_group_t;
64 :
65 : typedef struct ip6_nd_t_
66 : {
67 : /* local information */
68 : u32 sw_if_index;
69 : int all_routers_mcast;
70 :
71 : /* MLDP group information */
72 : ip6_mldp_group_t *mldp_group_pool;
73 :
74 : /* Hash table mapping address to index in mldp address pool. */
75 : mhash_t address_to_mldp_index;
76 :
77 : } ip6_mld_t;
78 :
79 :
80 : static ip6_link_delegate_id_t ip6_mld_delegate_id;
81 : static ip6_mld_t *ip6_mld_pool;
82 :
83 : /////
84 :
85 : static inline ip6_mld_t *
86 1793 : ip6_mld_get_itf (u32 sw_if_index)
87 : {
88 : index_t imi;
89 :
90 1793 : imi = ip6_link_delegate_get (sw_if_index, ip6_mld_delegate_id);
91 :
92 1793 : if (INDEX_INVALID != imi)
93 1793 : return (pool_elt_at_index (ip6_mld_pool, imi));
94 :
95 0 : return (NULL);
96 : }
97 :
98 : /**
99 : * @brief Add a multicast Address to the advertised MLD set
100 : */
101 : static void
102 7862 : ip6_neighbor_add_mld_prefix (ip6_mld_t * imd, ip6_address_t * addr)
103 : {
104 : ip6_mldp_group_t *mcast_group_info;
105 : uword *p;
106 :
107 : /* lookup mldp info for this interface */
108 7862 : p = mhash_get (&imd->address_to_mldp_index, addr);
109 7862 : mcast_group_info = p ? pool_elt_at_index (imd->mldp_group_pool, p[0]) : 0;
110 :
111 : /* add address */
112 7862 : if (!mcast_group_info)
113 : {
114 : /* add */
115 : u32 mi;
116 7847 : pool_get_zero (imd->mldp_group_pool, mcast_group_info);
117 :
118 7847 : mi = mcast_group_info - imd->mldp_group_pool;
119 7847 : mhash_set (&imd->address_to_mldp_index, addr, mi, /* old_value */
120 : 0);
121 :
122 7847 : mcast_group_info->type = 4;
123 7847 : mcast_group_info->mcast_source_address_pool = 0;
124 7847 : mcast_group_info->num_sources = 0;
125 7847 : clib_memcpy (&mcast_group_info->mcast_address, addr,
126 : sizeof (ip6_address_t));
127 : }
128 7862 : }
129 :
130 : /**
131 : * @brief Delete a multicast Address from the advertised MLD set
132 : */
133 : static void
134 1822 : ip6_neighbor_del_mld_prefix (ip6_mld_t * imd, ip6_address_t * addr)
135 : {
136 : ip6_mldp_group_t *mcast_group_info;
137 : uword *p;
138 :
139 1822 : p = mhash_get (&imd->address_to_mldp_index, addr);
140 1822 : mcast_group_info = p ? pool_elt_at_index (imd->mldp_group_pool, p[0]) : 0;
141 :
142 1822 : if (mcast_group_info)
143 : {
144 1807 : mhash_unset (&imd->address_to_mldp_index, addr,
145 : /* old_value */ 0);
146 1807 : pool_put (imd->mldp_group_pool, mcast_group_info);
147 : }
148 1822 : }
149 :
150 : /**
151 : * @brief Add a multicast Address to the advertised MLD set
152 : */
153 : static void
154 5874 : ip6_neighbor_add_mld_grp (ip6_mld_t * a,
155 : ip6_multicast_address_scope_t scope,
156 : ip6_multicast_link_local_group_id_t group)
157 : {
158 : ip6_address_t addr;
159 :
160 5874 : ip6_set_reserved_multicast_address (&addr, scope, group);
161 :
162 5874 : ip6_neighbor_add_mld_prefix (a, &addr);
163 5874 : }
164 :
165 : static const ethernet_interface_t *
166 2113 : ip6_mld_get_eth_itf (u32 sw_if_index)
167 : {
168 : const vnet_sw_interface_t *sw;
169 :
170 : /* lookup radv container - ethernet interfaces only */
171 2113 : sw = vnet_get_sup_sw_interface (vnet_get_main (), sw_if_index);
172 2113 : if (sw->type == VNET_SW_INTERFACE_TYPE_HARDWARE)
173 2113 : return (ethernet_get_interface (ðernet_main, sw->hw_if_index));
174 :
175 0 : return (NULL);
176 : }
177 :
178 : /**
179 : * @brief create and initialize router advertisement parameters with default
180 : * values for this intfc
181 : */
182 : static void
183 2113 : ip6_mld_link_enable (u32 sw_if_index)
184 : {
185 : const ethernet_interface_t *eth;
186 : ip6_mld_t *imd;
187 :
188 2113 : eth = ip6_mld_get_eth_itf (sw_if_index);
189 :
190 2113 : if (NULL == eth)
191 155 : return;
192 :
193 1958 : ASSERT (INDEX_INVALID == ip6_link_delegate_get (sw_if_index,
194 : ip6_mld_delegate_id));
195 :
196 1958 : pool_get_zero (ip6_mld_pool, imd);
197 :
198 1958 : imd->sw_if_index = sw_if_index;
199 :
200 1958 : mhash_init (&imd->address_to_mldp_index, sizeof (uword),
201 : sizeof (ip6_address_t));
202 :
203 : /* add multicast groups we will always be reporting */
204 1958 : ip6_neighbor_add_mld_grp (imd,
205 : IP6_MULTICAST_SCOPE_link_local,
206 : IP6_MULTICAST_GROUP_ID_all_hosts);
207 1958 : ip6_neighbor_add_mld_grp (imd,
208 : IP6_MULTICAST_SCOPE_link_local,
209 : IP6_MULTICAST_GROUP_ID_all_routers);
210 1958 : ip6_neighbor_add_mld_grp (imd,
211 : IP6_MULTICAST_SCOPE_link_local,
212 : IP6_MULTICAST_GROUP_ID_mldv2_routers);
213 :
214 1958 : ip6_link_delegate_update (sw_if_index, ip6_mld_delegate_id,
215 1958 : imd - ip6_mld_pool);
216 : }
217 :
218 : static void
219 1787 : ip6_mld_delegate_disable (index_t imdi)
220 : {
221 : ip6_mldp_group_t *m;
222 : ip6_mld_t *imd;
223 :
224 1787 : imd = pool_elt_at_index (ip6_mld_pool, imdi);
225 :
226 : /* clean MLD pools */
227 : /* *INDENT-OFF* */
228 12509 : pool_flush (m, imd->mldp_group_pool,
229 : ({
230 : mhash_unset (&imd->address_to_mldp_index, &m->mcast_address, 0);
231 : }));
232 : /* *INDENT-ON* */
233 :
234 1787 : pool_free (imd->mldp_group_pool);
235 :
236 1787 : mhash_free (&imd->address_to_mldp_index);
237 :
238 1787 : pool_put (ip6_mld_pool, imd);
239 1787 : }
240 :
241 : /* send an mldpv2 report */
242 : static void
243 1793 : ip6_neighbor_send_mldpv2_report (u32 sw_if_index)
244 : {
245 1793 : vnet_main_t *vnm = vnet_get_main ();
246 1793 : vlib_main_t *vm = vnm->vlib_main;
247 : int bogus_length;
248 :
249 : ip6_mld_t *imd;
250 : u16 payload_length;
251 : vlib_buffer_t *b0;
252 : ip6_header_t *ip0;
253 : u32 *to_next;
254 : vlib_frame_t *f;
255 : u32 bo0;
256 1793 : u32 n_to_alloc = 1;
257 :
258 : icmp6_multicast_listener_report_header_t *rh0;
259 : icmp6_multicast_listener_report_packet_t *rp0;
260 :
261 1793 : if (!vnet_sw_interface_is_admin_up (vnm, sw_if_index))
262 0 : return;
263 :
264 1793 : imd = ip6_mld_get_itf (sw_if_index);
265 :
266 1793 : if (NULL == imd)
267 0 : return;
268 :
269 : /* send report now - build a mldpv2 report packet */
270 1793 : if (0 == vlib_buffer_alloc (vm, &bo0, n_to_alloc))
271 : {
272 0 : alloc_fail:
273 0 : clib_warning ("buffer allocation failure");
274 0 : return;
275 : }
276 :
277 1793 : b0 = vlib_get_buffer (vm, bo0);
278 :
279 : /* adjust the sizeof the buffer to just include the ipv6 header */
280 1793 : b0->current_length = sizeof (icmp6_multicast_listener_report_packet_t);
281 :
282 1793 : payload_length = sizeof (icmp6_multicast_listener_report_header_t);
283 :
284 1793 : b0->error = ICMP6_ERROR_NONE;
285 :
286 1793 : rp0 = vlib_buffer_get_current (b0);
287 1793 : ip0 = (ip6_header_t *) & rp0->ip;
288 1793 : rh0 = (icmp6_multicast_listener_report_header_t *) & rp0->report_hdr;
289 :
290 1793 : clib_memset (rp0, 0x0, sizeof (icmp6_multicast_listener_report_packet_t));
291 :
292 1793 : ip0->ip_version_traffic_class_and_flow_label =
293 1793 : clib_host_to_net_u32 (0x6 << 28);
294 :
295 1793 : ip0->protocol = IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS;
296 : /* for DEBUG - vnet driver won't seem to emit router alerts */
297 : /* ip0->protocol = IP_PROTOCOL_ICMP6; */
298 1793 : ip0->hop_limit = 1;
299 :
300 1793 : rh0->icmp.type = ICMP6_multicast_listener_report_v2;
301 :
302 : /* source address MUST be the link-local address */
303 1793 : ip6_address_copy (&ip0->src_address,
304 : ip6_get_link_local_address (sw_if_index));
305 :
306 : /* destination is all mldpv2 routers */
307 1793 : ip6_set_reserved_multicast_address (&ip0->dst_address,
308 : IP6_MULTICAST_SCOPE_link_local,
309 : IP6_MULTICAST_GROUP_ID_mldv2_routers);
310 :
311 : /* add reports here */
312 : ip6_mldp_group_t *m;
313 1793 : int num_addr_records = 0;
314 : icmp6_multicast_address_record_t rr;
315 :
316 : /* fill in the hop-by-hop extension header (router alert) info */
317 1793 : rh0->ext_hdr.next_hdr = IP_PROTOCOL_ICMP6;
318 1793 : rh0->ext_hdr.n_data_u64s = 0;
319 :
320 1793 : rh0->alert.type = IP6_MLDP_ALERT_TYPE;
321 1793 : rh0->alert.len = 2;
322 1793 : rh0->alert.value = 0;
323 :
324 1793 : rh0->pad.type = 1;
325 1793 : rh0->pad.len = 0;
326 :
327 1793 : rh0->icmp.checksum = 0;
328 :
329 : /* *INDENT-OFF* */
330 8951 : pool_foreach (m, imd->mldp_group_pool)
331 : {
332 7158 : rr.type = m->type;
333 7158 : rr.aux_data_len_u32s = 0;
334 7158 : rr.num_sources = clib_host_to_net_u16 (m->num_sources);
335 7158 : clib_memcpy(&rr.mcast_addr, &m->mcast_address, sizeof(ip6_address_t));
336 :
337 7158 : num_addr_records++;
338 :
339 7158 : if(vlib_buffer_add_data (vm, &bo0, (void *)&rr,
340 : sizeof(icmp6_multicast_address_record_t)))
341 : {
342 0 : vlib_buffer_free (vm, &bo0, 1);
343 0 : goto alloc_fail;
344 : }
345 :
346 7158 : payload_length += sizeof( icmp6_multicast_address_record_t);
347 : }
348 : /* *INDENT-ON* */
349 :
350 1793 : rh0->rsvd = 0;
351 1793 : rh0->num_addr_records = clib_host_to_net_u16 (num_addr_records);
352 :
353 : /* update lengths */
354 1793 : ip0->payload_length = clib_host_to_net_u16 (payload_length);
355 :
356 1793 : rh0->icmp.checksum = ip6_tcp_udp_icmp_compute_checksum (vm, b0, ip0,
357 : &bogus_length);
358 1793 : ASSERT (bogus_length == 0);
359 :
360 : /*
361 : * OK to override w/ no regard for actual FIB, because
362 : * ip6-rewrite only looks at the adjacency.
363 : */
364 1793 : vnet_buffer (b0)->sw_if_index[VLIB_RX] =
365 1793 : vnet_main.local_interface_sw_if_index;
366 :
367 3586 : vnet_buffer (b0)->ip.adj_index[VLIB_TX] =
368 1793 : ip6_link_get_mcast_adj (sw_if_index);
369 1793 : b0->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
370 :
371 1793 : vlib_node_t *node = vlib_get_node_by_name (vm, (u8 *) "ip6-rewrite-mcast");
372 :
373 1793 : f = vlib_get_frame_to_node (vm, node->index);
374 1793 : to_next = vlib_frame_vector_args (f);
375 1793 : to_next[0] = bo0;
376 1793 : f->n_vectors = 1;
377 :
378 1793 : vlib_put_frame_to_node (vm, node->index, f);
379 1793 : return;
380 : }
381 :
382 : /* send a RA or update the timer info etc.. */
383 : static uword
384 7327 : ip6_mld_timer_event (vlib_main_t * vm,
385 : vlib_node_runtime_t * node, vlib_frame_t * frame)
386 : {
387 7327 : vnet_main_t *vnm = vnet_get_main ();
388 : ip6_mld_t *imd;
389 :
390 : /* Interface ip6 radv info list */
391 : /* *INDENT-OFF* */
392 19650 : pool_foreach (imd, ip6_mld_pool)
393 : {
394 12323 : if (!vnet_sw_interface_is_admin_up (vnm, imd->sw_if_index))
395 : {
396 48 : imd->all_routers_mcast = 0;
397 48 : continue;
398 : }
399 :
400 : /* Make sure that we've joined the all-routers multicast group */
401 12275 : if (!imd->all_routers_mcast)
402 : {
403 : /* send MDLP_REPORT_EVENT message */
404 1793 : ip6_neighbor_send_mldpv2_report(imd->sw_if_index);
405 1793 : imd->all_routers_mcast = 1;
406 : }
407 : }
408 : /* *INDENT-ON* */
409 :
410 7327 : return 0;
411 : }
412 :
413 : static uword
414 7902 : ip6_mld_event_process (vlib_main_t * vm,
415 : vlib_node_runtime_t * node, vlib_frame_t * frame)
416 : {
417 : uword event_type;
418 :
419 : /* init code here */
420 :
421 : while (1)
422 : {
423 7902 : vlib_process_wait_for_event_or_clock (vm, 1. /* seconds */ );
424 :
425 7327 : if (!vlib_process_get_event_data (vm, &event_type))
426 : {
427 : /* No events found: timer expired. */
428 : /* process interface list and send RAs as appropriate, update timer info */
429 7327 : ip6_mld_timer_event (vm, node, frame);
430 : }
431 : /* else; no events */
432 : }
433 : return frame->n_vectors;
434 : }
435 :
436 : /* *INDENT-OFF* */
437 183788 : VLIB_REGISTER_NODE (ip6_mld_event_process_node) = {
438 : .function = ip6_mld_event_process,
439 : .name = "ip6-mld-process",
440 : .type = VLIB_NODE_TYPE_PROCESS,
441 : };
442 : /* *INDENT-ON* */
443 :
444 : static u8 *
445 0 : format_ip6_mld (u8 * s, va_list * args)
446 : {
447 0 : index_t imi = va_arg (*args, index_t);
448 0 : u32 indent = va_arg (*args, u32);
449 : ip6_mldp_group_t *m;
450 : ip6_mld_t *imd;
451 :
452 0 : imd = pool_elt_at_index (ip6_mld_pool, imi);
453 :
454 0 : s = format (s, "%UJoined group address(es):\n", format_white_space, indent);
455 :
456 : /* *INDENT-OFF* */
457 0 : pool_foreach (m, imd->mldp_group_pool)
458 : {
459 0 : s = format (s, "%U%U\n",
460 : format_white_space, indent+2,
461 : format_ip6_address,
462 : &m->mcast_address);
463 : }
464 : /* *INDENT-ON* */
465 :
466 0 : return (s);
467 : }
468 :
469 : /**
470 : * @brief callback when an interface address is added or deleted
471 : */
472 : static void
473 1988 : ip6_mld_address_add (u32 imi,
474 : const ip6_address_t * address, u8 address_oength)
475 : {
476 : ip6_mld_t *imd;
477 : ip6_address_t a;
478 :
479 1988 : imd = pool_elt_at_index (ip6_mld_pool, imi);
480 :
481 : /* create solicited node multicast address for this interface address */
482 1988 : ip6_set_solicited_node_multicast_address (&a, 0);
483 :
484 1988 : a.as_u8[0xd] = address->as_u8[0xd];
485 1988 : a.as_u8[0xe] = address->as_u8[0xe];
486 1988 : a.as_u8[0xf] = address->as_u8[0xf];
487 :
488 1988 : ip6_neighbor_add_mld_prefix (imd, &a);
489 1988 : }
490 :
491 : static void
492 1822 : ip6_mld_address_del (u32 imi,
493 : const ip6_address_t * address, u8 address_oength)
494 : {
495 : ip6_mld_t *imd;
496 : ip6_address_t a;
497 :
498 1822 : imd = pool_elt_at_index (ip6_mld_pool, imi);
499 :
500 : /* create solicited node multicast address for this interface address */
501 1822 : ip6_set_solicited_node_multicast_address (&a, 0);
502 :
503 1822 : a.as_u8[0xd] = address->as_u8[0xd];
504 1822 : a.as_u8[0xe] = address->as_u8[0xe];
505 1822 : a.as_u8[0xf] = address->as_u8[0xf];
506 :
507 1822 : ip6_neighbor_del_mld_prefix (imd, &a);
508 1822 : }
509 :
510 : /**
511 : * VFT for registering as a delegate to an IP6 link
512 : */
513 : const static ip6_link_delegate_vft_t ip6_mld_delegate_vft = {
514 : .ildv_disable = ip6_mld_delegate_disable,
515 : .ildv_enable = ip6_mld_link_enable,
516 : .ildv_format = format_ip6_mld,
517 : .ildv_addr_add = ip6_mld_address_add,
518 : .ildv_addr_del = ip6_mld_address_del,
519 : };
520 :
521 : static clib_error_t *
522 575 : ip6_mld_init (vlib_main_t * vm)
523 : {
524 575 : ip6_mld_delegate_id = ip6_link_delegate_register (&ip6_mld_delegate_vft);
525 :
526 575 : return (NULL);
527 : }
528 :
529 : /* *INDENT-OFF* */
530 96767 : VLIB_INIT_FUNCTION (ip6_mld_init) =
531 : {
532 : .runs_after = VLIB_INITS("icmp6_init"),
533 : };
534 : /* *INDENT-ON* */
535 :
536 : /*
537 : * fd.io coding-style-patch-verification: ON
538 : *
539 : * Local Variables:
540 : * eval: (c-set-style "gnu")
541 : * End:
542 : */
|