vrrp: add stats support and update API
[vpp.git] / src / plugins / vrrp / vrrp_packet.c
1 /*
2  * vrrp.c - vrrp plugin action functions
3  *
4  * Copyright 2019-2020 Rubicon Communications, LLC (Netgate)
5  *
6  * SPDX-License-Identifier: Apache-2.0
7  *
8  */
9
10 #include <vnet/vnet.h>
11 #include <vnet/plugin/plugin.h>
12 #include <vnet/mfib/mfib_entry.h>
13 #include <vnet/mfib/mfib_table.h>
14 #include <vnet/adj/adj.h>
15 #include <vnet/adj/adj_mcast.h>
16 #include <vnet/fib/fib_table.h>
17 #include <vnet/fib/fib_sas.h>
18 #include <vnet/ip/igmp_packet.h>
19 #include <vnet/ip/ip6_link.h>
20 #include <vnet/ethernet/arp_packet.h>
21
22 #include <vrrp/vrrp.h>
23 #include <vrrp/vrrp_packet.h>
24
25 #include <vpp/app/version.h>
26
27 static const u8 vrrp4_dst_mac[6] = { 0x1, 0x0, 0x5e, 0x0, 0x0, 0x12 };
28 static const u8 vrrp6_dst_mac[6] = { 0x33, 0x33, 0x0, 0x0, 0x0, 0x12 };
29 static const u8 vrrp_src_mac_prefix[4] = { 0x0, 0x0, 0x5e, 0x0 };
30
31 static int
32 vrrp_adv_l2_build_multicast (vrrp_vr_t * vr, vlib_buffer_t * b)
33 {
34   vnet_main_t *vnm = vnet_get_main ();
35   vnet_link_t link_type;
36   ethernet_header_t *eth;
37   int n_bytes = 0;
38   const void *dst_mac;
39   u8 mac_byte_ipver;
40   u8 *rewrite;
41
42   eth = vlib_buffer_get_current (b);
43
44   if (vrrp_vr_is_ipv6 (vr))
45     {
46       dst_mac = vrrp6_dst_mac;
47       link_type = VNET_LINK_IP6;
48       mac_byte_ipver = 0x2;
49     }
50   else
51     {
52       dst_mac = vrrp4_dst_mac;
53       link_type = VNET_LINK_IP4;
54       mac_byte_ipver = 0x1;
55     }
56
57   rewrite = ethernet_build_rewrite (vnm, vr->config.sw_if_index, link_type,
58                                     dst_mac);
59   clib_memcpy (eth, rewrite, vec_len (rewrite));
60
61   /* change the source mac from the HW addr to the VRRP virtual MAC */
62   clib_memcpy
63     (eth->src_address, vrrp_src_mac_prefix, sizeof (vrrp_src_mac_prefix));
64   eth->src_address[4] = mac_byte_ipver;
65   eth->src_address[5] = vr->config.vr_id;
66
67   n_bytes += vec_len (rewrite);
68
69   vlib_buffer_chain_increase_length (b, b, n_bytes);
70   vlib_buffer_advance (b, n_bytes);
71
72   vec_free (rewrite);
73
74   return n_bytes;
75 }
76
77 #define VRRP4_MCAST_ADDR_AS_U8 { 224, 0, 0, 18 }
78 #define VRRP6_MCAST_ADDR_AS_U8 \
79 { 0xff, 0x2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x12 }
80
81 static const ip46_address_t vrrp4_mcast_addr = {
82   .ip4 = {.as_u8 = VRRP4_MCAST_ADDR_AS_U8,},
83 };
84
85 static const ip46_address_t vrrp6_mcast_addr = {
86   .ip6 = {.as_u8 = VRRP6_MCAST_ADDR_AS_U8,},
87 };
88
89 /* size of static parts of header + (# addrs * addr length) */
90 always_inline u16
91 vrrp_adv_payload_len (vrrp_vr_t * vr)
92 {
93   u16 addr_len = vrrp_vr_is_ipv6 (vr) ? 16 : 4;
94
95   return sizeof (vrrp_header_t) + (vec_len (vr->config.vr_addrs) * addr_len);
96 }
97
98 static int
99 vrrp_adv_l3_build (vrrp_vr_t * vr, vlib_buffer_t * b,
100                    const ip46_address_t * dst)
101 {
102   if (!vrrp_vr_is_ipv6 (vr))    /* IPv4 */
103     {
104       ip4_header_t *ip4 = vlib_buffer_get_current (b);
105       ip4_address_t *src4;
106
107       clib_memset (ip4, 0, sizeof (*ip4));
108       ip4->ip_version_and_header_length = 0x45;
109       ip4->ttl = 255;
110       ip4->protocol = IP_PROTOCOL_VRRP;
111       clib_memcpy (&ip4->dst_address, &dst->ip4, sizeof (dst->ip4));
112
113       /* RFC 5798 Section 5.1.1.1 - Source Address "is the primary IPv4
114        * address of the interface the packet is being sent from". Assume
115        * this is the first address on the interface.
116        */
117       src4 = ip_interface_get_first_ip (vr->config.sw_if_index, 1);
118       if (!src4)
119         {
120           return -1;
121         }
122       ip4->src_address.as_u32 = src4->as_u32;
123       ip4->length = clib_host_to_net_u16 (sizeof (*ip4) +
124                                           vrrp_adv_payload_len (vr));
125       ip4->checksum = ip4_header_checksum (ip4);
126
127       vlib_buffer_chain_increase_length (b, b, sizeof (*ip4));
128       vlib_buffer_advance (b, sizeof (*ip4));
129
130       return sizeof (*ip4);
131     }
132   else
133     {
134       ip6_header_t *ip6 = vlib_buffer_get_current (b);
135
136       clib_memset (ip6, 0, sizeof (*ip6));
137       ip6->ip_version_traffic_class_and_flow_label = 0x00000060;
138       ip6->hop_limit = 255;
139       ip6->protocol = IP_PROTOCOL_VRRP;
140       clib_memcpy (&ip6->dst_address, &dst->ip6, sizeof (dst->ip6));
141       ip6_address_copy (&ip6->src_address,
142                         ip6_get_link_local_address (vr->config.sw_if_index));
143       ip6->payload_length = clib_host_to_net_u16 (vrrp_adv_payload_len (vr));
144
145       vlib_buffer_chain_increase_length (b, b, sizeof (*ip6));
146       vlib_buffer_advance (b, sizeof (*ip6));
147
148       return sizeof (*ip6);
149     }
150 }
151
152
153 u16
154 vrrp_adv_csum (void *l3_hdr, void *payload, u8 is_ipv6, u16 len)
155 {
156   ip_csum_t csum = 0;
157   u8 proto = IP_PROTOCOL_VRRP;
158   int addr_len;
159   int word_size = sizeof (uword);
160   void *src_addr;
161   int i;
162
163   if (is_ipv6)
164     {
165       addr_len = 16;
166       src_addr = &(((ip6_header_t *) l3_hdr)->src_address);
167     }
168   else
169     {
170       addr_len = 4;
171       src_addr = &(((ip4_header_t *) l3_hdr)->src_address);
172     }
173
174   for (i = 0; i < (2 * addr_len); i += word_size)
175     {
176       if (word_size == sizeof (u64))
177         csum =
178           ip_csum_with_carry (csum, clib_mem_unaligned (src_addr + i, u64));
179       else
180         csum =
181           ip_csum_with_carry (csum, clib_mem_unaligned (src_addr + i, u32));
182     }
183
184   csum = ip_csum_with_carry (csum,
185                              clib_host_to_net_u32 (len + (proto << 16)));
186
187   /* now do the payload */
188   csum = ip_incremental_checksum (csum, payload, len);
189
190   csum = ~ip_csum_fold (csum);
191
192   return (u16) csum;
193 }
194
195 static int
196 vrrp_adv_payload_build (vrrp_vr_t * vr, vlib_buffer_t * b, int shutdown)
197 {
198   vrrp_header_t *vrrp = vlib_buffer_get_current (b);
199   void *l3_hdr;
200   ip46_address_t *vr_addr;
201   void *hdr_addr;
202   u8 is_ipv6;
203   u8 n_addrs;
204   int len;
205
206   n_addrs = vec_len (vr->config.vr_addrs);
207   is_ipv6 = vrrp_vr_is_ipv6 (vr);
208
209   if (is_ipv6)
210     {
211       ip6_header_t *ip6;
212
213       len = sizeof (*vrrp) + n_addrs * sizeof (ip6_address_t);;
214       l3_hdr = vlib_buffer_get_current (b) - sizeof (ip6_header_t);
215       ip6 = l3_hdr;
216       ip6->payload_length = clib_host_to_net_u16 (len);
217     }
218   else
219     {
220       len = sizeof (*vrrp) + n_addrs * sizeof (ip4_address_t);
221       l3_hdr = vlib_buffer_get_current (b) - sizeof (ip4_header_t);
222     }
223
224   vrrp->vrrp_version_and_type = 0x31;
225   vrrp->vr_id = vr->config.vr_id;
226   vrrp->priority = (shutdown) ? 0 : vrrp_vr_priority (vr);
227   vrrp->n_addrs = vec_len (vr->config.vr_addrs);
228   vrrp->rsvd_and_max_adv_int = clib_host_to_net_u16 (vr->config.adv_interval);
229   vrrp->checksum = 0;
230
231   hdr_addr = (void *) (vrrp + 1);
232
233   vec_foreach (vr_addr, vr->config.vr_addrs)
234   {
235     if (is_ipv6)
236       {
237         clib_memcpy (hdr_addr, &vr_addr->ip6, 16);
238         hdr_addr += 16;
239       }
240     else
241       {
242         clib_memcpy (hdr_addr, &vr_addr->ip4, 4);
243         hdr_addr += 4;
244       }
245   }
246
247   vlib_buffer_chain_increase_length (b, b, vrrp_adv_payload_len (vr));
248
249   vrrp->checksum =
250     vrrp_adv_csum (l3_hdr, vrrp, is_ipv6, vrrp_adv_payload_len (vr));
251
252   return len;
253 }
254
255 static_always_inline u32
256 vrrp_adv_next_node (vrrp_vr_t * vr)
257 {
258   if (vrrp_vr_is_unicast (vr))
259     {
260       if (vrrp_vr_is_ipv6 (vr))
261         return ip6_lookup_node.index;
262       else
263         return ip4_lookup_node.index;
264     }
265   else
266     {
267       vrrp_main_t *vmp = &vrrp_main;
268
269       return vmp->intf_output_node_idx;
270     }
271 }
272
273 static_always_inline const ip46_address_t *
274 vrrp_adv_mcast_addr (vrrp_vr_t * vr)
275 {
276   if (vrrp_vr_is_ipv6 (vr))
277     return &vrrp6_mcast_addr;
278
279   return &vrrp4_mcast_addr;
280 }
281
282 int
283 vrrp_adv_send (vrrp_vr_t * vr, int shutdown)
284 {
285   vlib_main_t *vm = vlib_get_main ();
286   vlib_frame_t *to_frame;
287   int i, n_buffers = 1;
288   u32 node_index, *to_next, *bi = 0;
289   u8 is_unicast = vrrp_vr_is_unicast (vr);
290
291   node_index = vrrp_adv_next_node (vr);
292
293   if (is_unicast)
294     n_buffers = vec_len (vr->config.peer_addrs);
295
296   if (n_buffers < 1)
297     {
298       /* A unicast VR will not start without peers added so this should
299        * not happen. Just avoiding a crash if it happened somehow.
300        */
301       clib_warning ("Unicast VR configuration corrupted for %U",
302                     format_vrrp_vr_key, vr);
303       return -1;
304     }
305
306   vec_validate (bi, n_buffers - 1);
307   if (vlib_buffer_alloc (vm, bi, n_buffers) != n_buffers)
308     {
309       clib_warning ("Buffer allocation failed for %U", format_vrrp_vr_key,
310                     vr);
311       vec_free (bi);
312       return -1;
313     }
314
315   to_frame = vlib_get_frame_to_node (vm, node_index);
316   to_next = vlib_frame_vector_args (to_frame);
317
318   for (i = 0; i < n_buffers; i++)
319     {
320       vlib_buffer_t *b;
321       u32 bi0;
322       const ip46_address_t *dst = vrrp_adv_mcast_addr (vr);
323
324       bi0 = vec_elt (bi, i);
325       b = vlib_get_buffer (vm, bi0);
326
327       b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
328       vnet_buffer (b)->sw_if_index[VLIB_RX] = 0;
329       vnet_buffer (b)->sw_if_index[VLIB_TX] = vr->config.sw_if_index;
330
331       if (is_unicast)
332         {
333           dst = vec_elt_at_index (vr->config.peer_addrs, i);
334           vnet_buffer (b)->sw_if_index[VLIB_TX] = ~0;
335         }
336       else
337         vrrp_adv_l2_build_multicast (vr, b);
338
339       if (-1 == vrrp_adv_l3_build (vr, b, dst))
340         {
341           vlib_frame_free (vm, vlib_node_get_runtime (vm, node_index),
342                            to_frame);
343           vlib_buffer_free (vm, bi, n_buffers);
344           return -1;
345         }
346       vrrp_adv_payload_build (vr, b, shutdown);
347
348       vlib_buffer_reset (b);
349
350       to_next[i] = bi0;
351     }
352
353   to_frame->n_vectors = n_buffers;
354
355   vlib_put_frame_to_node (vm, node_index, to_frame);
356
357   vrrp_incr_stat_counter (VRRP_STAT_COUNTER_ADV_SENT, vr->stat_index);
358   if (shutdown)
359     {
360       vrrp_incr_stat_counter (VRRP_STAT_COUNTER_PRIO0_SENT, vr->stat_index);
361     }
362
363   vec_free (bi);
364
365   return 0;
366 }
367
368 static void
369 vrrp6_na_pkt_build (vrrp_vr_t * vr, vlib_buffer_t * b, ip6_address_t * addr6)
370 {
371   vnet_main_t *vnm = vnet_get_main ();
372   vlib_main_t *vm = vlib_get_main ();
373   ethernet_header_t *eth;
374   ip6_header_t *ip6;
375   icmp6_neighbor_solicitation_or_advertisement_header_t *na;
376   icmp6_neighbor_discovery_ethernet_link_layer_address_option_t *ll_opt;
377   int payload_length, bogus_length;
378   int rewrite_bytes = 0;
379   u8 *rewrite;
380   u8 dst_mac[6];
381
382   /* L2 headers */
383   eth = vlib_buffer_get_current (b);
384
385   ip6_multicast_ethernet_address (dst_mac, IP6_MULTICAST_GROUP_ID_all_hosts);
386   rewrite =
387     ethernet_build_rewrite (vnm, vr->config.sw_if_index, VNET_LINK_IP6,
388                             dst_mac);
389   rewrite_bytes += vec_len (rewrite);
390   clib_memcpy (eth, rewrite, vec_len (rewrite));
391   vec_free (rewrite);
392
393   b->current_length += rewrite_bytes;
394   vlib_buffer_advance (b, rewrite_bytes);
395
396   /* IPv6 */
397   ip6 = vlib_buffer_get_current (b);
398
399   b->current_length += sizeof (*ip6);
400   clib_memset (ip6, 0, sizeof (*ip6));
401
402   ip6->ip_version_traffic_class_and_flow_label = 0x00000060;
403   ip6->protocol = IP_PROTOCOL_ICMP6;
404   ip6->hop_limit = 255;
405   ip6_set_reserved_multicast_address (&ip6->dst_address,
406                                       IP6_MULTICAST_SCOPE_link_local,
407                                       IP6_MULTICAST_GROUP_ID_all_hosts);
408   ip6_address_copy (&ip6->src_address,
409                     ip6_get_link_local_address (vr->config.sw_if_index));
410
411
412   /* ICMPv6 */
413   na = (icmp6_neighbor_solicitation_or_advertisement_header_t *) (ip6 + 1);
414   ll_opt =
415     (icmp6_neighbor_discovery_ethernet_link_layer_address_option_t *) (na +
416                                                                        1);
417
418   payload_length = sizeof (*na) + sizeof (*ll_opt);
419   b->current_length += payload_length;
420   clib_memset (na, 0, payload_length);
421
422   na->icmp.type = ICMP6_neighbor_advertisement; /* icmp code, csum are 0 */
423   na->target_address = *addr6;
424   na->advertisement_flags = clib_host_to_net_u32
425     (ICMP6_NEIGHBOR_ADVERTISEMENT_FLAG_OVERRIDE
426      | ICMP6_NEIGHBOR_ADVERTISEMENT_FLAG_ROUTER);
427
428   ll_opt->header.type =
429     ICMP6_NEIGHBOR_DISCOVERY_OPTION_target_link_layer_address;
430   ll_opt->header.n_data_u64s = 1;
431   clib_memcpy (ll_opt->ethernet_address, vr->runtime.mac.bytes,
432                sizeof (vr->runtime.mac));
433
434   ip6->payload_length = clib_host_to_net_u16 (payload_length);
435   na->icmp.checksum =
436     ip6_tcp_udp_icmp_compute_checksum (vm, b, ip6, &bogus_length);
437 }
438
439 const mac_address_t broadcast_mac = {
440   .bytes = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff,},
441 };
442
443 static void
444 vrrp4_garp_pkt_build (vrrp_vr_t * vr, vlib_buffer_t * b, ip4_address_t * ip4)
445 {
446   vnet_main_t *vnm = vnet_get_main ();
447   ethernet_header_t *eth;
448   ethernet_arp_header_t *arp;
449   int rewrite_bytes;
450   u8 *rewrite;
451
452   eth = vlib_buffer_get_current (b);
453
454   rewrite =
455     ethernet_build_rewrite (vnm, vr->config.sw_if_index, VNET_LINK_ARP,
456                             broadcast_mac.bytes);
457   rewrite_bytes = vec_len (rewrite);
458   clib_memcpy (eth, rewrite, rewrite_bytes);
459   vec_free (rewrite);
460
461   b->current_length += rewrite_bytes;
462   vlib_buffer_advance (b, rewrite_bytes);
463
464   arp = vlib_buffer_get_current (b);
465   b->current_length += sizeof (*arp);
466
467   clib_memset (arp, 0, sizeof (*arp));
468
469   arp->l2_type = clib_host_to_net_u16 (ETHERNET_ARP_HARDWARE_TYPE_ethernet);
470   arp->l3_type = clib_host_to_net_u16 (ETHERNET_TYPE_IP4);
471   arp->n_l2_address_bytes = 6;
472   arp->n_l3_address_bytes = 4;
473   arp->opcode = clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_request);
474   arp->ip4_over_ethernet[0].mac = vr->runtime.mac;
475   arp->ip4_over_ethernet[0].ip4 = *ip4;
476   arp->ip4_over_ethernet[1].mac = broadcast_mac;
477   arp->ip4_over_ethernet[1].ip4 = *ip4;
478 }
479
480 int
481 vrrp_garp_or_na_send (vrrp_vr_t * vr)
482 {
483   vlib_main_t *vm = vlib_get_main ();
484   vrrp_main_t *vmp = &vrrp_main;
485   vlib_frame_t *to_frame;
486   u32 *bi = 0;
487   u32 n_buffers;
488   u32 *to_next;
489   int i;
490
491   if (vec_len (vr->config.peer_addrs))
492     return 0;                   /* unicast is used in routed environments - don't garp */
493
494   n_buffers = vec_len (vr->config.vr_addrs);
495   if (!n_buffers)
496     {
497       clib_warning ("Unable to send gratuitous ARP for VR %U - no addresses",
498                     format_vrrp_vr_key, vr);
499       return -1;
500     }
501
502   /* need to send a packet for each VR address */
503   vec_validate (bi, n_buffers - 1);
504
505   if (vlib_buffer_alloc (vm, bi, n_buffers) != n_buffers)
506     {
507       clib_warning ("Buffer allocation failed for %U", format_vrrp_vr_key,
508                     vr);
509       vec_free (bi);
510       return -1;
511     }
512
513   to_frame = vlib_get_frame_to_node (vm, vmp->intf_output_node_idx);
514   to_frame->n_vectors = 0;
515   to_next = vlib_frame_vector_args (to_frame);
516
517   for (i = 0; i < n_buffers; i++)
518     {
519       vlib_buffer_t *b;
520       ip46_address_t *addr;
521
522       addr = vec_elt_at_index (vr->config.vr_addrs, i);
523       b = vlib_get_buffer (vm, bi[i]);
524
525       b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
526       vnet_buffer (b)->sw_if_index[VLIB_RX] = 0;
527       vnet_buffer (b)->sw_if_index[VLIB_TX] = vr->config.sw_if_index;
528
529       if (vrrp_vr_is_ipv6 (vr))
530         vrrp6_na_pkt_build (vr, b, &addr->ip6);
531       else
532         vrrp4_garp_pkt_build (vr, b, &addr->ip4);
533
534       vlib_buffer_reset (b);
535
536       to_next[i] = bi[i];
537       to_frame->n_vectors++;
538     }
539
540   vlib_put_frame_to_node (vm, vmp->intf_output_node_idx, to_frame);
541
542   return 0;
543 }
544
545 #define IGMP4_MCAST_ADDR_AS_U8 { 224, 0, 0, 22 }
546
547 static const ip4_header_t igmp_ip4_mcast = {
548   .ip_version_and_header_length = 0x46, /* there's options! */
549   .ttl = 1,
550   .protocol = IP_PROTOCOL_IGMP,
551   .tos = 0xc0,
552   .dst_address = {.as_u8 = IGMP4_MCAST_ADDR_AS_U8,},
553 };
554
555 static int
556 vrrp_igmp_pkt_build (vrrp_vr_t *vr, vlib_buffer_t *b)
557 {
558   ip4_header_t *ip4;
559   u8 *ip4_options;
560   igmp_membership_report_v3_t *report;
561   igmp_membership_group_v3_t *group;
562   ip4_address_t *src4;
563
564   ip4 = vlib_buffer_get_current (b);
565   clib_memcpy (ip4, &igmp_ip4_mcast, sizeof (*ip4));
566
567   /* Use the source address advertisements will use to join mcast group */
568   src4 = ip_interface_get_first_ip (vr->config.sw_if_index, 1);
569   if (!src4)
570     {
571       return -1;
572     }
573   ip4->src_address.as_u32 = src4->as_u32;
574
575   vlib_buffer_chain_increase_length (b, b, sizeof (*ip4));
576   vlib_buffer_advance (b, sizeof (*ip4));
577
578   ip4_options = (u8 *) (ip4 + 1);
579   ip4_options[0] = 0x94;        /* 10010100 == the router alert option */
580   ip4_options[1] = 0x04;        /* length == 4 bytes */
581   ip4_options[2] = 0x0;         /* value == Router shall examine packet */
582   ip4_options[3] = 0x0;         /* reserved */
583
584   vlib_buffer_chain_increase_length (b, b, 4);
585   vlib_buffer_advance (b, 4);
586
587   report = vlib_buffer_get_current (b);
588
589   report->header.type = IGMP_TYPE_membership_report_v3;
590   report->header.code = 0;
591   report->header.checksum = 0;
592   report->unused = 0;
593   report->n_groups = clib_host_to_net_u16 (1);
594
595   vlib_buffer_chain_increase_length (b, b, sizeof (*report));
596   vlib_buffer_advance (b, sizeof (*report));
597
598   group = vlib_buffer_get_current (b);
599   group->type = IGMP_MEMBERSHIP_GROUP_change_to_exclude;
600   group->n_aux_u32s = 0;
601   group->n_src_addresses = 0;
602   group->group_address.as_u32 = clib_host_to_net_u32 (0xe0000012);
603
604   vlib_buffer_chain_increase_length (b, b, sizeof (*group));
605   vlib_buffer_advance (b, sizeof (*group));
606
607   ip4->length = clib_host_to_net_u16 (b->current_data);
608   ip4->checksum = ip4_header_checksum (ip4);
609
610   int payload_len = vlib_buffer_get_current (b) - ((void *) report);
611   report->header.checksum =
612     ~ip_csum_fold (ip_incremental_checksum (0, report, payload_len));
613
614   vlib_buffer_reset (b);
615   return 0;
616 }
617
618 /* multicast listener report packet format for ethernet. */
619 typedef CLIB_PACKED (struct
620                      {
621                      ip6_hop_by_hop_ext_t ext_hdr;
622                      ip6_router_alert_option_t alert;
623                      ip6_padN_option_t pad;
624                      icmp46_header_t icmp;
625                      u16 rsvd;
626                      u16 num_addr_records;
627                      icmp6_multicast_address_record_t records[0];
628                      }) icmp6_multicast_listener_report_header_t;
629
630 static void
631 vrrp_icmp6_mlr_pkt_build (vrrp_vr_t * vr, vlib_buffer_t * b)
632 {
633   vlib_main_t *vm = vlib_get_main ();
634   ip6_header_t *ip6;
635   icmp6_multicast_listener_report_header_t *rh;
636   icmp6_multicast_address_record_t *rr;
637   ip46_address_t *vr_addr;
638   int bogus_length, n_addrs;
639   u16 payload_length;
640
641   n_addrs = vec_len (vr->config.vr_addrs) + 1;
642   payload_length = sizeof (*rh) + (n_addrs * sizeof (*rr));
643   b->current_length = sizeof (*ip6) + payload_length;
644   b->error = ICMP6_ERROR_NONE;
645
646   ip6 = vlib_buffer_get_current (b);
647   rh = (icmp6_multicast_listener_report_header_t *) (ip6 + 1);
648   rr = (icmp6_multicast_address_record_t *) (rh + 1);
649
650   /* IP header */
651   clib_memset (ip6, 0, b->current_length);
652   ip6->ip_version_traffic_class_and_flow_label =
653     clib_host_to_net_u32 (0x60000000);
654   ip6->hop_limit = 1;
655   ip6->protocol = IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS;
656   ip6_set_reserved_multicast_address (&ip6->dst_address,
657                                       IP6_MULTICAST_SCOPE_link_local,
658                                       IP6_MULTICAST_GROUP_ID_mldv2_routers);
659   ip6_address_copy (&ip6->src_address,
660                     ip6_get_link_local_address (vr->config.sw_if_index));
661
662   clib_memset (rh, 0, sizeof (*rh));
663
664   /* v6 hop by hop extension header */
665   rh->ext_hdr.next_hdr = IP_PROTOCOL_ICMP6;
666   rh->ext_hdr.n_data_u64s = 0;
667
668   rh->alert.type = IP6_MLDP_ALERT_TYPE;
669   rh->alert.len = 2;
670   rh->alert.value = 0;
671
672   rh->pad.type = 1;
673   rh->pad.len = 0;
674
675   /* icmp6 header */
676   rh->icmp.type = ICMP6_multicast_listener_report_v2;
677   rh->icmp.checksum = 0;
678
679   rh->rsvd = 0;
680   rh->num_addr_records = clib_host_to_net_u16 (n_addrs);
681
682   /* group addresses */
683
684   /* All VRRP routers group */
685   rr->type = 4;
686   rr->aux_data_len_u32s = 0;
687   rr->num_sources = 0;
688   clib_memcpy
689     (&rr->mcast_addr, &vrrp6_mcast_addr.ip6, sizeof (ip6_address_t));
690
691   /* solicited node multicast addresses for VR addrs */
692   vec_foreach (vr_addr, vr->config.vr_addrs)
693   {
694     u32 id;
695
696     rr++;
697     rr->type = 4;
698     rr->aux_data_len_u32s = 0;
699     rr->num_sources = 0;
700
701     id = clib_net_to_host_u32 (vr_addr->ip6.as_u32[3]) & 0x00ffffff;
702     ip6_set_solicited_node_multicast_address (&rr->mcast_addr, id);
703   }
704
705   ip6->payload_length = clib_host_to_net_u16 (payload_length);
706   rh->icmp.checksum = ip6_tcp_udp_icmp_compute_checksum (vm, b, ip6,
707                                                          &bogus_length);
708 }
709
710 int
711 vrrp_vr_multicast_group_join (vrrp_vr_t * vr)
712 {
713   vlib_main_t *vm = vlib_get_main ();
714   vlib_buffer_t *b;
715   vlib_frame_t *f;
716   vnet_main_t *vnm = vnet_get_main ();
717   vrrp_intf_t *intf;
718   u32 bi = 0, *to_next;
719   int n_buffers = 1;
720   u8 is_ipv6;
721   u32 node_index;
722
723   if (!vnet_sw_interface_is_up (vnm, vr->config.sw_if_index))
724     return 0;
725
726   is_ipv6 = vrrp_vr_is_ipv6 (vr);
727
728   if (is_ipv6 && ip6_link_is_enabled (vr->config.sw_if_index) == 0)
729     return 0;
730
731   if (vlib_buffer_alloc (vm, &bi, n_buffers) != n_buffers)
732     {
733       clib_warning ("Buffer allocation failed for %U", format_vrrp_vr_key,
734                     vr);
735       return -1;
736     }
737
738   b = vlib_get_buffer (vm, bi);
739
740   b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
741
742   vnet_buffer (b)->sw_if_index[VLIB_RX] = 0;
743   vnet_buffer (b)->sw_if_index[VLIB_TX] = vr->config.sw_if_index;
744
745   intf = vrrp_intf_get (vr->config.sw_if_index);
746   vnet_buffer (b)->ip.adj_index[VLIB_TX] = intf->mcast_adj_index[is_ipv6];
747
748   if (is_ipv6)
749     {
750       vrrp_icmp6_mlr_pkt_build (vr, b);
751       node_index = ip6_rewrite_mcast_node.index;
752     }
753   else
754     {
755       if (-1 == vrrp_igmp_pkt_build (vr, b))
756         {
757           clib_warning ("IGMP packet build failed for %U", format_vrrp_vr_key,
758                         vr);
759           vlib_buffer_free (vm, &bi, 1);
760           return -1;
761         }
762       node_index = ip4_rewrite_mcast_node.index;
763     }
764
765   f = vlib_get_frame_to_node (vm, node_index);
766   to_next = vlib_frame_vector_args (f);
767   to_next[0] = bi;
768   f->n_vectors = 1;
769
770   vlib_put_frame_to_node (vm, node_index, f);
771
772   return f->n_vectors;
773 }
774
775
776 /*
777  * fd.io coding-style-patch-verification: ON
778  *
779  * Local Variables:
780  * eval: (c-set-style "gnu")
781  * End:
782  */