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