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