summaryrefslogtreecommitdiffstats
path: root/doxygen/siphon-generate
blob: bdfd58d8bbbc6bd74e3fe7806826e224867d9362 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#!/usr/bin/env python
# Copyright (c) 2016 Comcast Cable Communications Management, LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Looks for preprocessor macros with struct initializers and siphons them
# off into another file for later parsing; ostensibly to generate
# documentation from struct initializer data.

import os, sys, argparse, logging
import siphon

DEFAULT_LOGFILE = None
DEFAULT_LOGLEVEL = "info"
DEFAULT_OUTPUT = "build-root/docs/siphons"
DEFAULT_PREFIX = os.getcwd()

ap = argparse.ArgumentParser()
ap.add_argument("--log-file", default=DEFAULT_LOGFILE,
        help="Log file [%s]" % DEFAULT_LOGFILE)
ap.add_argument("--log-level", default=DEFAULT_LOGLEVEL,
        choices=["debug", "info", "warning", "error", "critical"],
        help="Logging level [%s]" % DEFAULT_LOGLEVEL)

ap.add_argument("--output", '-o', metavar="directory", default=DEFAULT_OUTPUT,
        help="Output directory for .siphon files [%s]" % DEFAULT_OUTPUT)
ap.add_argument("--input-prefix", metavar="path", default=DEFAULT_PREFIX,
        help="Prefix to strip from input pathnames [%s]" % DEFAULT_PREFIX)
ap.add_argument("input", nargs='+', metavar="input_file",
        help="Input C source files")
args = ap.parse_args()

logging.basicConfig(filename=args.log_file,
        level=getattr(logging, args.log_level.upper(), None))
log = logging.getLogger("siphon_generate")


generate = siphon.generate.Generate(output_directory=args.output,
    input_prefix=args.input_prefix)

# Pre-process file names in case they indicate a file with
# a list of files
files = []
for filename in args.input:
    if filename.startswith('@'):
        with open(filename[1:], 'r') as fp:
            lines = fp.readlines()
            for line in lines:
                file = line.strip()
                if file not in files:
                    files.append(file)
            lines = None
    else:
        if filename not in files:
            files.append(filename)

# Iterate all the input files we've been given
for filename in files:
    generate.parse(filename)

# Write the extracted data
generate.deliver()

# All done
' href='#n656'>656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744
/*
 * vrrp.c - vrrp plugin action functions
 *
 * Copyright 2019-2020 Rubicon Communications, LLC (Netgate)
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 */

#include <vnet/vnet.h>
#include <vnet/plugin/plugin.h>
#include <vnet/mfib/mfib_entry.h>
#include <vnet/mfib/mfib_table.h>
#include <vnet/adj/adj.h>
#include <vnet/adj/adj_mcast.h>
#include <vnet/fib/fib_table.h>
#include <vnet/fib/fib_sas.h>
#include <vnet/ip/igmp_packet.h>
#include <vnet/ip/ip6_link.h>
#include <vnet/ethernet/arp_packet.h>

#include <vrrp/vrrp.h>
#include <vrrp/vrrp_packet.h>

#include <vpp/app/version.h>

static const u8 vrrp4_dst_mac[6] = { 0x1, 0x0, 0x5e, 0x0, 0x0, 0x12 };
static const u8 vrrp6_dst_mac[6] = { 0x33, 0x33, 0x0, 0x0, 0x0, 0x12 };
static const u8 vrrp_src_mac_prefix[4] = { 0x0, 0x0, 0x5e, 0x0 };

static int
vrrp_adv_l2_build_multicast (vrrp_vr_t * vr, vlib_buffer_t * b)
{
  vnet_main_t *vnm = vnet_get_main ();
  vnet_link_t link_type;
  ethernet_header_t *eth;
  int n_bytes = 0;
  const void *dst_mac;
  u8 mac_byte_ipver;
  u8 *rewrite;

  eth = vlib_buffer_get_current (b);

  if (vrrp_vr_is_ipv6 (vr))
    {
      dst_mac = vrrp6_dst_mac;
      link_type = VNET_LINK_IP6;
      mac_byte_ipver = 0x2;
    }
  else
    {
      dst_mac = vrrp4_dst_mac;
      link_type = VNET_LINK_IP4;
      mac_byte_ipver = 0x1;
    }

  rewrite = ethernet_build_rewrite (vnm, vr->config.sw_if_index, link_type,
				    dst_mac);
  clib_memcpy (eth, rewrite, vec_len (rewrite));

  /* change the source mac from the HW addr to the VRRP virtual MAC */
  clib_memcpy
    (eth->src_address, vrrp_src_mac_prefix, sizeof (vrrp_src_mac_prefix));
  eth->src_address[4] = mac_byte_ipver;
  eth->src_address[5] = vr->config.vr_id;

  n_bytes += vec_len (rewrite);

  vlib_buffer_chain_increase_length (b, b, n_bytes);
  vlib_buffer_advance (b, n_bytes);

  vec_free (rewrite);

  return n_bytes;
}

#define VRRP4_MCAST_ADDR_AS_U8 { 224, 0, 0, 18 }
#define VRRP6_MCAST_ADDR_AS_U8 \
{ 0xff, 0x2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x12 }

static const ip46_address_t vrrp4_mcast_addr = {
  .ip4 = {.as_u8 = VRRP4_MCAST_ADDR_AS_U8,},
};

static const ip46_address_t vrrp6_mcast_addr = {
  .ip6 = {.as_u8 = VRRP6_MCAST_ADDR_AS_U8,},
};

/* size of static parts of header + (# addrs * addr length) */
always_inline u16
vrrp_adv_payload_len (vrrp_vr_t * vr)
{
  u16 addr_len = vrrp_vr_is_ipv6 (vr) ? 16 : 4;

  return sizeof (vrrp_header_t) + (vec_len (vr->config.vr_addrs) * addr_len);
}

static int
vrrp_adv_l3_build (vrrp_vr_t * vr, vlib_buffer_t * b,
		   const ip46_address_t * dst)
{
  if (!vrrp_vr_is_ipv6 (vr))	/* IPv4 */
    {
      ip4_header_t *ip4 = vlib_buffer_get_current (b);

      clib_memset (ip4, 0, sizeof (*ip4));
      ip4->ip_version_and_header_length = 0x45;
      ip4->ttl = 255;
      ip4->protocol = IP_PROTOCOL_VRRP;
      clib_memcpy (&ip4->dst_address, &dst->ip4, sizeof (dst->ip4));
      fib_sas4_get (vr->config.sw_if_index, NULL, &ip4->src_address);
      ip4->length = clib_host_to_net_u16 (sizeof (*ip4) +
					  vrrp_adv_payload_len (vr));
      ip4->checksum = ip4_header_checksum (ip4);

      vlib_buffer_chain_increase_length (b, b, sizeof (*ip4));
      vlib_buffer_advance (b, sizeof (*ip4));

      return sizeof (*ip4);
    }
  else
    {
      ip6_header_t *ip6 = vlib_buffer_get_current (b);

      clib_memset (ip6, 0, sizeof (*ip6));
      ip6->ip_version_traffic_class_and_flow_label = 0x00000060;
      ip6->hop_limit = 255;
      ip6->protocol = IP_PROTOCOL_VRRP;
      clib_memcpy (&ip6->dst_address, &dst->ip6, sizeof (dst->ip6));
      ip6_address_copy (&ip6->src_address,
			ip6_get_link_local_address (vr->config.sw_if_index));
      ip6->payload_length = clib_host_to_net_u16 (vrrp_adv_payload_len (vr));

      vlib_buffer_chain_increase_length (b, b, sizeof (*ip6));
      vlib_buffer_advance (b, sizeof (*ip6));

      return sizeof (*ip6);
    }
}


u16
vrrp_adv_csum (void *l3_hdr, void *payload, u8 is_ipv6, u16 len)
{
  ip_csum_t csum = 0;
  u8 proto = IP_PROTOCOL_VRRP;
  int addr_len;
  int word_size = sizeof (uword);
  void *src_addr;
  int i;

  if (is_ipv6)
    {
      addr_len = 16;
      src_addr = &(((ip6_header_t *) l3_hdr)->src_address);
    }
  else
    {
      addr_len = 4;
      src_addr = &(((ip4_header_t *) l3_hdr)->src_address);
    }

  for (i = 0; i < (2 * addr_len); i += word_size)
    {
      if (word_size == sizeof (u64))
	csum =
	  ip_csum_with_carry (csum, clib_mem_unaligned (src_addr + i, u64));
      else
	csum =
	  ip_csum_with_carry (csum, clib_mem_unaligned (src_addr + i, u32));
    }

  csum = ip_csum_with_carry (csum,
			     clib_host_to_net_u32 (len + (proto << 16)));

  /* now do the payload */
  csum = ip_incremental_checksum (csum, payload, len);

  csum = ~ip_csum_fold (csum);

  return (u16) csum;
}

static int
vrrp_adv_payload_build (vrrp_vr_t * vr, vlib_buffer_t * b, int shutdown)
{
  vrrp_header_t *vrrp = vlib_buffer_get_current (b);
  void *l3_hdr;
  ip46_address_t *vr_addr;
  void *hdr_addr;
  u8 is_ipv6;
  u8 n_addrs;
  int len;

  n_addrs = vec_len (vr->config.vr_addrs);
  is_ipv6 = vrrp_vr_is_ipv6 (vr);

  if (is_ipv6)
    {
      ip6_header_t *ip6;

      len = sizeof (*vrrp) + n_addrs * sizeof (ip6_address_t);;
      l3_hdr = vlib_buffer_get_current (b) - sizeof (ip6_header_t);
      ip6 = l3_hdr;
      ip6->payload_length = clib_host_to_net_u16 (len);
    }
  else
    {
      len = sizeof (*vrrp) + n_addrs * sizeof (ip4_address_t);
      l3_hdr = vlib_buffer_get_current (b) - sizeof (ip4_header_t);
    }

  vrrp->vrrp_version_and_type = 0x31;
  vrrp->vr_id = vr->config.vr_id;
  vrrp->priority = (shutdown) ? 0 : vrrp_vr_priority (vr);
  vrrp->n_addrs = vec_len (vr->config.vr_addrs);
  vrrp->rsvd_and_max_adv_int = clib_host_to_net_u16 (vr->config.adv_interval);
  vrrp->checksum = 0;

  hdr_addr = (void *) (vrrp + 1);

  vec_foreach (vr_addr, vr->config.vr_addrs)
  {
    if (is_ipv6)
      {
	clib_memcpy (hdr_addr, &vr_addr->ip6, 16);
	hdr_addr += 16;
      }
    else
      {
	clib_memcpy (hdr_addr, &vr_addr->ip4, 4);
	hdr_addr += 4;
      }
  }

  vlib_buffer_chain_increase_length (b, b, vrrp_adv_payload_len (vr));

  vrrp->checksum =
    vrrp_adv_csum (l3_hdr, vrrp, is_ipv6, vrrp_adv_payload_len (vr));

  return len;
}

static_always_inline u32
vrrp_adv_next_node (vrrp_vr_t * vr)
{
  if (vrrp_vr_is_unicast (vr))
    {
      if (vrrp_vr_is_ipv6 (vr))
	return ip6_lookup_node.index;
      else
	return ip4_lookup_node.index;
    }
  else
    {
      vrrp_main_t *vmp = &vrrp_main;

      return vmp->intf_output_node_idx;
    }
}

static_always_inline const ip46_address_t *
vrrp_adv_mcast_addr (vrrp_vr_t * vr)
{
  if (vrrp_vr_is_ipv6 (vr))
    return &vrrp6_mcast_addr;

  return &vrrp4_mcast_addr;
}

int
vrrp_adv_send (vrrp_vr_t * vr, int shutdown)
{
  vlib_main_t *vm = vlib_get_main ();
  vlib_frame_t *to_frame;
  int i, n_buffers = 1;
  u32 node_index, *to_next, *bi = 0;
  u8 is_unicast = vrrp_vr_is_unicast (vr);

  node_index = vrrp_adv_next_node (vr);

  if (is_unicast)
    n_buffers = vec_len (vr->config.peer_addrs);

  if (n_buffers < 1)
    {
      /* A unicast VR will not start without peers added so this should
       * not happen. Just avoiding a crash if it happened somehow.
       */
      clib_warning ("Unicast VR configuration corrupted for %U",
		    format_vrrp_vr_key, vr);
      return -1;
    }

  vec_validate (bi, n_buffers - 1);
  if (vlib_buffer_alloc (vm, bi, n_buffers) != n_buffers)
    {
      clib_warning ("Buffer allocation failed for %U", format_vrrp_vr_key,
		    vr);
      vec_free (bi);
      return -1;
    }

  to_frame = vlib_get_frame_to_node (vm, node_index);
  to_next = vlib_frame_vector_args (to_frame);

  for (i = 0; i < n_buffers; i++)
    {
      vlib_buffer_t *b;
      u32 bi0;
      const ip46_address_t *dst = vrrp_adv_mcast_addr (vr);

      bi0 = vec_elt (bi, i);
      b = vlib_get_buffer (vm, bi0);

      b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
      vnet_buffer (b)->sw_if_index[VLIB_RX] = 0;
      vnet_buffer (b)->sw_if_index[VLIB_TX] = vr->config.sw_if_index;

      if (is_unicast)
	{
	  dst = vec_elt_at_index (vr->config.peer_addrs, i);
	  vnet_buffer (b)->sw_if_index[VLIB_TX] = ~0;
	}
      else
	vrrp_adv_l2_build_multicast (vr, b);

      vrrp_adv_l3_build (vr, b, dst);
      vrrp_adv_payload_build (vr, b, shutdown);

      vlib_buffer_reset (b);

      to_next[i] = bi0;
    }

  to_frame->n_vectors = n_buffers;

  vlib_put_frame_to_node (vm, node_index, to_frame);

  vec_free (bi);

  return 0;
}

static void
vrrp6_na_pkt_build (vrrp_vr_t * vr, vlib_buffer_t * b, ip6_address_t * addr6)
{
  vnet_main_t *vnm = vnet_get_main ();
  vlib_main_t *vm = vlib_get_main ();
  ethernet_header_t *eth;
  ip6_header_t *ip6;
  icmp6_neighbor_solicitation_or_advertisement_header_t *na;
  icmp6_neighbor_discovery_ethernet_link_layer_address_option_t *ll_opt;
  int payload_length, bogus_length;
  int rewrite_bytes = 0;
  u8 *rewrite;
  u8 dst_mac[6];

  /* L2 headers */
  eth = vlib_buffer_get_current (b);

  ip6_multicast_ethernet_address (dst_mac, IP6_MULTICAST_GROUP_ID_all_hosts);
  rewrite =
    ethernet_build_rewrite (vnm, vr->config.sw_if_index, VNET_LINK_IP6,
			    dst_mac);
  rewrite_bytes += vec_len (rewrite);
  clib_memcpy (eth, rewrite, vec_len (rewrite));
  vec_free (rewrite);

  b->current_length += rewrite_bytes;
  vlib_buffer_advance (b, rewrite_bytes);

  /* IPv6 */
  ip6 = vlib_buffer_get_current (b);

  b->current_length += sizeof (*ip6);
  clib_memset (ip6, 0, sizeof (*ip6));

  ip6->ip_version_traffic_class_and_flow_label = 0x00000060;
  ip6->protocol = IP_PROTOCOL_ICMP6;
  ip6->hop_limit = 255;
  ip6_set_reserved_multicast_address (&ip6->dst_address,
				      IP6_MULTICAST_SCOPE_link_local,
				      IP6_MULTICAST_GROUP_ID_all_hosts);
  ip6_address_copy (&ip6->src_address,
		    ip6_get_link_local_address (vr->config.sw_if_index));


  /* ICMPv6 */
  na = (icmp6_neighbor_solicitation_or_advertisement_header_t *) (ip6 + 1);
  ll_opt =
    (icmp6_neighbor_discovery_ethernet_link_layer_address_option_t *) (na +
								       1);

  payload_length = sizeof (*na) + sizeof (*ll_opt);
  b->current_length += payload_length;
  clib_memset (na, 0, payload_length);

  na->icmp.type = ICMP6_neighbor_advertisement;	/* icmp code, csum are 0 */
  na->target_address = *addr6;
  na->advertisement_flags = clib_host_to_net_u32
    (ICMP6_NEIGHBOR_ADVERTISEMENT_FLAG_OVERRIDE
     | ICMP6_NEIGHBOR_ADVERTISEMENT_FLAG_ROUTER);

  ll_opt->header.type =
    ICMP6_NEIGHBOR_DISCOVERY_OPTION_target_link_layer_address;
  ll_opt->header.n_data_u64s = 1;
  clib_memcpy (ll_opt->ethernet_address, vr->runtime.mac.bytes,
	       sizeof (vr->runtime.mac));

  ip6->payload_length = clib_host_to_net_u16 (payload_length);
  na->icmp.checksum =
    ip6_tcp_udp_icmp_compute_checksum (vm, b, ip6, &bogus_length);
}

const mac_address_t broadcast_mac = {
  .bytes = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff,},
};

static void
vrrp4_garp_pkt_build (vrrp_vr_t * vr, vlib_buffer_t * b, ip4_address_t * ip4)
{
  vnet_main_t *vnm = vnet_get_main ();
  ethernet_header_t *eth;
  ethernet_arp_header_t *arp;
  int rewrite_bytes;
  u8 *rewrite;

  eth = vlib_buffer_get_current (b);

  rewrite =
    ethernet_build_rewrite (vnm, vr->config.sw_if_index, VNET_LINK_ARP,
			    broadcast_mac.bytes);
  rewrite_bytes = vec_len (rewrite);
  clib_memcpy (eth, rewrite, rewrite_bytes);
  vec_free (rewrite);

  b->current_length += rewrite_bytes;
  vlib_buffer_advance (b, rewrite_bytes);

  arp = vlib_buffer_get_current (b);
  b->current_length += sizeof (*arp);

  clib_memset (arp, 0, sizeof (*arp));

  arp->l2_type = clib_host_to_net_u16 (ETHERNET_ARP_HARDWARE_TYPE_ethernet);
  arp->l3_type = clib_host_to_net_u16 (ETHERNET_TYPE_IP4);
  arp->n_l2_address_bytes = 6;
  arp->n_l3_address_bytes = 4;
  arp->opcode = clib_host_to_net_u16 (ETHERNET_ARP_OPCODE_request);
  arp->ip4_over_ethernet[0].mac = vr->runtime.mac;
  arp->ip4_over_ethernet[0].ip4 = *ip4;
  arp->ip4_over_ethernet[1].mac = broadcast_mac;
  arp->ip4_over_ethernet[1].ip4 = *ip4;
}

int
vrrp_garp_or_na_send (vrrp_vr_t * vr)
{
  vlib_main_t *vm = vlib_get_main ();
  vrrp_main_t *vmp = &vrrp_main;
  vlib_frame_t *to_frame;
  u32 *bi = 0;
  u32 n_buffers;
  u32 *to_next;
  int i;

  if (vec_len (vr->config.peer_addrs))
    return 0;			/* unicast is used in routed environments - don't garp */

  n_buffers = vec_len (vr->config.vr_addrs);
  if (!n_buffers)
    {
      clib_warning ("Unable to send gratuitous ARP for VR %U - no addresses",
		    format_vrrp_vr_key, vr);
      return -1;
    }

  /* need to send a packet for each VR address */
  vec_validate (bi, n_buffers - 1);

  if (vlib_buffer_alloc (vm, bi, n_buffers) != n_buffers)
    {
      clib_warning ("Buffer allocation failed for %U", format_vrrp_vr_key,
		    vr);
      vec_free (bi);
      return -1;
    }

  to_frame = vlib_get_frame_to_node (vm, vmp->intf_output_node_idx);
  to_frame->n_vectors = 0;
  to_next = vlib_frame_vector_args (to_frame);

  for (i = 0; i < n_buffers; i++)
    {
      vlib_buffer_t *b;
      ip46_address_t *addr;

      addr = vec_elt_at_index (vr->config.vr_addrs, i);
      b = vlib_get_buffer (vm, bi[i]);

      b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
      vnet_buffer (b)->sw_if_index[VLIB_RX] = 0;
      vnet_buffer (b)->sw_if_index[VLIB_TX] = vr->config.sw_if_index;

      if (vrrp_vr_is_ipv6 (vr))
	vrrp6_na_pkt_build (vr, b, &addr->ip6);
      else
	vrrp4_garp_pkt_build (vr, b, &addr->ip4);

      vlib_buffer_reset (b);

      to_next[i] = bi[i];
      to_frame->n_vectors++;
    }

  vlib_put_frame_to_node (vm, vmp->intf_output_node_idx, to_frame);

  return 0;
}

#define IGMP4_MCAST_ADDR_AS_U8 { 224, 0, 0, 22 }

static const ip4_header_t igmp_ip4_mcast = {
  .ip_version_and_header_length = 0x46,	/* there's options! */
  .ttl = 1,
  .protocol = IP_PROTOCOL_IGMP,
  .tos = 0xc0,
  .dst_address = {.as_u8 = IGMP4_MCAST_ADDR_AS_U8,},
};

static void
vrrp_igmp_pkt_build (vrrp_vr_t * vr, vlib_buffer_t * b)
{
  ip4_header_t *ip4;
  u8 *ip4_options;
  igmp_membership_report_v3_t *report;
  igmp_membership_group_v3_t *group;

  ip4 = vlib_buffer_get_current (b);
  clib_memcpy (ip4, &igmp_ip4_mcast, sizeof (*ip4));
  fib_sas4_get (vr->config.sw_if_index, NULL, &ip4->src_address);

  vlib_buffer_chain_increase_length (b, b, sizeof (*ip4));
  vlib_buffer_advance (b, sizeof (*ip4));

  ip4_options = (u8 *) (ip4 + 1);
  ip4_options[0] = 0x94;	/* 10010100 == the router alert option */
  ip4_options[1] = 0x04;	/* length == 4 bytes */
  ip4_options[2] = 0x0;		/* value == Router shall examine packet */
  ip4_options[3] = 0x0;		/* reserved */

  vlib_buffer_chain_increase_length (b, b, 4);
  vlib_buffer_advance (b, 4);

  report = vlib_buffer_get_current (b);

  report->header.type = IGMP_TYPE_membership_report_v3;
  report->header.code = 0;
  report->header.checksum = 0;
  report->unused = 0;
  report->n_groups = clib_host_to_net_u16 (1);

  vlib_buffer_chain_increase_length (b, b, sizeof (*report));
  vlib_buffer_advance (b, sizeof (*report));

  group = vlib_buffer_get_current (b);
  group->type = IGMP_MEMBERSHIP_GROUP_change_to_exclude;
  group->n_aux_u32s = 0;
  group->n_src_addresses = 0;
  group->group_address.as_u32 = clib_host_to_net_u32 (0xe0000012);

  vlib_buffer_chain_increase_length (b, b, sizeof (*group));
  vlib_buffer_advance (b, sizeof (*group));

  ip4->length = clib_host_to_net_u16 (b->current_data);
  ip4->checksum = ip4_header_checksum (ip4);

  int payload_len = vlib_buffer_get_current (b) - ((void *) report);
  report->header.checksum =
    ~ip_csum_fold (ip_incremental_checksum (0, report, payload_len));

  vlib_buffer_reset (b);
}

/* multicast listener report packet format for ethernet. */
typedef CLIB_PACKED (struct
		     {
		     ip6_hop_by_hop_ext_t ext_hdr;
		     ip6_router_alert_option_t alert;
		     ip6_padN_option_t pad;
		     icmp46_header_t icmp;
		     u16 rsvd;
		     u16 num_addr_records;
		     icmp6_multicast_address_record_t records[0];
		     }) icmp6_multicast_listener_report_header_t;

static void
vrrp_icmp6_mlr_pkt_build (vrrp_vr_t * vr, vlib_buffer_t * b)
{
  vlib_main_t *vm = vlib_get_main ();
  ip6_header_t *ip6;
  icmp6_multicast_listener_report_header_t *rh;
  icmp6_multicast_address_record_t *rr;
  ip46_address_t *vr_addr;
  int bogus_length, n_addrs;
  u16 payload_length;

  n_addrs = vec_len (vr->config.vr_addrs) + 1;
  payload_length = sizeof (*rh) + (n_addrs * sizeof (*rr));
  b->current_length = sizeof (*ip6) + payload_length;
  b->error = ICMP6_ERROR_NONE;

  ip6 = vlib_buffer_get_current (b);
  rh = (icmp6_multicast_listener_report_header_t *) (ip6 + 1);
  rr = (icmp6_multicast_address_record_t *) (rh + 1);

  /* IP header */
  clib_memset (ip6, 0, b->current_length);
  ip6->ip_version_traffic_class_and_flow_label =
    clib_host_to_net_u32 (0x60000000);
  ip6->hop_limit = 1;
  ip6->protocol = IP_PROTOCOL_IP6_HOP_BY_HOP_OPTIONS;
  ip6_set_reserved_multicast_address (&ip6->dst_address,
				      IP6_MULTICAST_SCOPE_link_local,
				      IP6_MULTICAST_GROUP_ID_mldv2_routers);
  ip6_address_copy (&ip6->src_address,
		    ip6_get_link_local_address (vr->config.sw_if_index));

  clib_memset (rh, 0, sizeof (*rh));

  /* v6 hop by hop extension header */
  rh->ext_hdr.next_hdr = IP_PROTOCOL_ICMP6;
  rh->ext_hdr.n_data_u64s = 0;

  rh->alert.type = IP6_MLDP_ALERT_TYPE;
  rh->alert.len = 2;
  rh->alert.value = 0;

  rh->pad.type = 1;
  rh->pad.len = 0;

  /* icmp6 header */
  rh->icmp.type = ICMP6_multicast_listener_report_v2;
  rh->icmp.checksum = 0;

  rh->rsvd = 0;
  rh->num_addr_records = clib_host_to_net_u16 (n_addrs);

  /* group addresses */

  /* All VRRP routers group */
  rr->type = 4;
  rr->aux_data_len_u32s = 0;
  rr->num_sources = 0;
  clib_memcpy
    (&rr->mcast_addr, &vrrp6_mcast_addr.ip6, sizeof (ip6_address_t));

  /* solicited node multicast addresses for VR addrs */
  vec_foreach (vr_addr, vr->config.vr_addrs)
  {
    u32 id;

    rr++;
    rr->type = 4;
    rr->aux_data_len_u32s = 0;
    rr->num_sources = 0;

    id = clib_net_to_host_u32 (vr_addr->ip6.as_u32[3]) & 0x00ffffff;
    ip6_set_solicited_node_multicast_address (&rr->mcast_addr, id);
  }

  ip6->payload_length = clib_host_to_net_u16 (payload_length);
  rh->icmp.checksum = ip6_tcp_udp_icmp_compute_checksum (vm, b, ip6,
							 &bogus_length);
}

int
vrrp_vr_multicast_group_join (vrrp_vr_t * vr)
{
  vlib_main_t *vm = vlib_get_main ();
  vlib_buffer_t *b;
  vlib_frame_t *f;
  vnet_main_t *vnm = vnet_get_main ();
  vrrp_intf_t *intf;
  u32 bi = 0, *to_next;
  int n_buffers = 1;
  u8 is_ipv6;
  u32 node_index;

  if (!vnet_sw_interface_is_up (vnm, vr->config.sw_if_index))
    return 0;

  is_ipv6 = vrrp_vr_is_ipv6 (vr);

  if (is_ipv6 && ip6_link_is_enabled (vr->config.sw_if_index) == 0)
    return 0;

  if (vlib_buffer_alloc (vm, &bi, n_buffers) != n_buffers)
    {
      clib_warning ("Buffer allocation failed for %U", format_vrrp_vr_key,
		    vr);
      return -1;
    }

  b = vlib_get_buffer (vm, bi);

  b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;

  vnet_buffer (b)->sw_if_index[VLIB_RX] = 0;
  vnet_buffer (b)->sw_if_index[VLIB_TX] = vr->config.sw_if_index;

  intf = vrrp_intf_get (vr->config.sw_if_index);
  vnet_buffer (b)->ip.adj_index[VLIB_TX] = intf->mcast_adj_index[is_ipv6];

  if (is_ipv6)
    {
      vrrp_icmp6_mlr_pkt_build (vr, b);
      node_index = ip6_rewrite_mcast_node.index;
    }
  else
    {
      vrrp_igmp_pkt_build (vr, b);
      node_index = ip4_rewrite_mcast_node.index;
    }

  f = vlib_get_frame_to_node (vm, node_index);
  to_next = vlib_frame_vector_args (f);
  to_next[0] = bi;
  f->n_vectors = 1;

  vlib_put_frame_to_node (vm, node_index, f);

  return f->n_vectors;
}


/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */