diff options
author | 2016-11-24 19:42:43 +0200 | |
---|---|---|
committer | 2016-11-30 16:31:32 +0000 | |
commit | c5b136004543b9861a203af335d1ce61a976382d (patch) | |
tree | 5a4ad365f4086103afc65892a603a78be2ffbef1 /vnet | |
parent | aff7077d2031545a15efd12e7f65eac723799491 (diff) |
VXLAN multicast dst (remote) address support
Added support for multicast vxlan tunnels which are used for bridge domain flooding instead
of flooding the all unicast tunnels in the bridge domain.
features added:
* conditional flooding to some of the BD members - based on existance of multicast tunnel member
* added local multicast adjacency - multicast packets are handled as the same as unicast - based on src (unicast) address
* refactored some of vxlan tunnel creation code - to unify ip4/6 handling
Change-Id: I60cca4124265a8dd4f6b2d6ea8701e52e7c1baa4
Signed-off-by: Eyal Bari <ebari@cisco.com>
Diffstat (limited to 'vnet')
-rw-r--r-- | vnet/vnet/fib/fib_internal.h | 3 | ||||
-rw-r--r-- | vnet/vnet/fib/fib_path_ext.h | 2 | ||||
-rw-r--r-- | vnet/vnet/fib/fib_types.h | 7 | ||||
-rw-r--r-- | vnet/vnet/interface.c | 10 | ||||
-rw-r--r-- | vnet/vnet/interface.h | 11 | ||||
-rw-r--r-- | vnet/vnet/interface_cli.c | 1 | ||||
-rw-r--r-- | vnet/vnet/ip/ip4_packet.h | 13 | ||||
-rw-r--r-- | vnet/vnet/ip/ip6_packet.h | 17 | ||||
-rw-r--r-- | vnet/vnet/l2/l2_bd.c | 82 | ||||
-rw-r--r-- | vnet/vnet/l2/l2_bd.h | 11 | ||||
-rw-r--r-- | vnet/vnet/l2/l2_flood.c | 21 | ||||
-rw-r--r-- | vnet/vnet/l2/l2_input.c | 4 | ||||
-rw-r--r-- | vnet/vnet/lisp-gpe/lisp_gpe_sub_interface.c | 1 | ||||
-rw-r--r-- | vnet/vnet/vxlan/vxlan.c | 219 | ||||
-rw-r--r-- | vnet/vnet/vxlan/vxlan.h | 10 |
15 files changed, 260 insertions, 152 deletions
diff --git a/vnet/vnet/fib/fib_internal.h b/vnet/vnet/fib/fib_internal.h index 26b349ee..a0238ac3 100644 --- a/vnet/vnet/fib/fib_internal.h +++ b/vnet/vnet/fib/fib_internal.h @@ -24,9 +24,6 @@ */ #undef FIB_DEBUG -extern void fib_prefix_from_ip46_addr (const ip46_address_t *addr, - fib_prefix_t *prf); - extern int fib_route_path_cmp(const fib_route_path_t *rpath1, const fib_route_path_t *rpath2); diff --git a/vnet/vnet/fib/fib_path_ext.h b/vnet/vnet/fib/fib_path_ext.h index 949b1e2b..6cb7f507 100644 --- a/vnet/vnet/fib/fib_path_ext.h +++ b/vnet/vnet/fib/fib_path_ext.h @@ -20,7 +20,7 @@ #include <vnet/fib/fib_types.h> /** - * A path extension is a per-entry addition to the forwarigind information + * A path extension is a per-entry addition to the forwarding information * when packets are sent for that entry over that path. * * For example: diff --git a/vnet/vnet/fib/fib_types.h b/vnet/vnet/fib/fib_types.h index 83123a51..92371e6b 100644 --- a/vnet/vnet/fib/fib_types.h +++ b/vnet/vnet/fib/fib_types.h @@ -206,6 +206,13 @@ extern int fib_prefix_is_cover(const fib_prefix_t *p1, */ extern int fib_prefix_is_host(const fib_prefix_t *p); + +/** + * \brief Host prefix from ip + */ +extern void fib_prefix_from_ip46_addr (const ip46_address_t *addr, + fib_prefix_t *pfx); + extern u8 * format_fib_prefix(u8 * s, va_list * args); extern u8 * format_fib_forw_chain_type(u8 * s, va_list * args); diff --git a/vnet/vnet/interface.c b/vnet/vnet/interface.c index 33827e2b..e552733e 100644 --- a/vnet/vnet/interface.c +++ b/vnet/vnet/interface.c @@ -712,11 +712,11 @@ vnet_register_interface (vnet_main_t * vnm, /* Make hardware interface point to software interface. */ { - vnet_sw_interface_t sw; - - memset (&sw, 0, sizeof (sw)); - sw.type = VNET_SW_INTERFACE_TYPE_HARDWARE; - sw.hw_if_index = hw_index; + vnet_sw_interface_t sw = { + .type = VNET_SW_INTERFACE_TYPE_HARDWARE, + .flood_class = VNET_FLOOD_CLASS_NORMAL, + .hw_if_index = hw_index + }; hw->sw_if_index = vnet_create_sw_interface_no_callbacks (vnm, &sw); } diff --git a/vnet/vnet/interface.h b/vnet/vnet/interface.h index 5ac7fbd5..412574d1 100644 --- a/vnet/vnet/interface.h +++ b/vnet/vnet/interface.h @@ -503,6 +503,15 @@ typedef struct } eth; } vnet_sub_interface_t; +typedef enum +{ + /* Always flood */ + VNET_FLOOD_CLASS_NORMAL, + VNET_FLOOD_CLASS_TUNNEL_MASTER, + /* Does not flood when tunnel master is in the same L2 BD */ + VNET_FLOOD_CLASS_TUNNEL_NORMAL +} vnet_flood_class_t; + /* Software-interface. This corresponds to a Ethernet VLAN, ATM vc, a tunnel, etc. Configuration (e.g. IP address) gets attached to software interface. */ @@ -545,6 +554,8 @@ typedef struct /* VNET_SW_INTERFACE_TYPE_SUB. */ vnet_sub_interface_t sub; }; + + vnet_flood_class_t flood_class; } vnet_sw_interface_t; typedef enum diff --git a/vnet/vnet/interface_cli.c b/vnet/vnet/interface_cli.c index cd7a620b..1c15eb18 100644 --- a/vnet/vnet/interface_cli.c +++ b/vnet/vnet/interface_cli.c @@ -697,6 +697,7 @@ create_sub_interfaces (vlib_main_t * vm, *kp = sup_and_sub_key; template.type = VNET_SW_INTERFACE_TYPE_SUB; + template.flood_class = VNET_FLOOD_CLASS_NORMAL; template.sup_sw_if_index = hi->sw_if_index; template.sub.id = id; if (id_min < id_max) diff --git a/vnet/vnet/ip/ip4_packet.h b/vnet/vnet/ip/ip4_packet.h index 78db01a1..277b968f 100644 --- a/vnet/vnet/ip/ip4_packet.h +++ b/vnet/vnet/ip/ip4_packet.h @@ -271,6 +271,19 @@ ip4_multicast_address_set_for_group (ip4_address_t * a, ip_multicast_group_t g) } always_inline void +ip4_multicast_ethernet_address (u8 * ethernet_address, ip4_address_t * a) +{ + u8 *d = a->as_u8; + + ethernet_address[0] = 0x01; + ethernet_address[1] = 0x00; + ethernet_address[2] = 0x5e; + ethernet_address[3] = d[1] & 0x7f; + ethernet_address[4] = d[2]; + ethernet_address[5] = d[3]; +} + +always_inline void ip4_tcp_reply_x1 (ip4_header_t * ip0, tcp_header_t * tcp0) { u32 src0, dst0; diff --git a/vnet/vnet/ip/ip6_packet.h b/vnet/vnet/ip/ip6_packet.h index 29fa4a4e..456c011a 100644 --- a/vnet/vnet/ip/ip6_packet.h +++ b/vnet/vnet/ip/ip6_packet.h @@ -64,6 +64,7 @@ typedef CLIB_PACKED (union { ip4_address_t ip4; }; ip6_address_t ip6; + u8 as_u8[16]; u64 as_u64[2]; }) ip46_address_t; #define ip46_address_is_ip4(ip46) (((ip46)->pad[0] | (ip46)->pad[1] | (ip46)->pad[2]) == 0) @@ -74,6 +75,15 @@ typedef CLIB_PACKED (union { #define ip46_address_is_zero(ip46) (((ip46)->as_u64[0] == 0) && ((ip46)->as_u64[1] == 0)) always_inline void +ip46_from_addr_buf(u32 is_ipv6, u8 *buf, ip46_address_t *ip) +{ + if (is_ipv6) + ip->ip6 = *((ip6_address_t *) buf); + else + ip46_address_set_ip4(ip, (ip4_address_t *) buf); +} + +always_inline void ip6_addr_fib_init (ip6_address_fib_t * addr_fib, ip6_address_t * address, u32 fib_index) { @@ -123,6 +133,13 @@ always_inline uword ip6_address_is_multicast (ip6_address_t * a) { return a->as_u8[0] == 0xff; } +always_inline uword +ip46_address_is_multicast (ip46_address_t * a) +{ + return ip46_address_is_ip4(a) ? ip4_address_is_multicast(&a->ip4) : + ip6_address_is_multicast(&a->ip6); +} + always_inline void ip6_set_reserved_multicast_address (ip6_address_t * a, ip6_multicast_address_scope_t scope, diff --git a/vnet/vnet/l2/l2_bd.c b/vnet/vnet/l2/l2_bd.c index 0b5656e6..e2ef6797 100644 --- a/vnet/vnet/l2/l2_bd.c +++ b/vnet/vnet/l2/l2_bd.c @@ -54,6 +54,9 @@ bd_validate (l2_bridge_domain_t * bd_config) bd_config->feature_bitmap = ~L2INPUT_FEAT_ARP_TERM; bd_config->bvi_sw_if_index = ~0; bd_config->members = 0; + bd_config->flood_count = 0; + bd_config->tun_master_count = 0; + bd_config->tun_normal_count = 0; bd_config->mac_by_ip4 = 0; bd_config->mac_by_ip6 = hash_create_mem (0, sizeof (ip6_address_t), sizeof (uword)); @@ -114,32 +117,49 @@ bd_delete_bd_index (bd_main_t * bdm, u32 bd_id) return 0; } +static void +update_flood_count (l2_bridge_domain_t * bd_config) +{ + bd_config->flood_count = vec_len (bd_config->members) - + (bd_config->tun_master_count ? bd_config->tun_normal_count : 0); +} + void bd_add_member (l2_bridge_domain_t * bd_config, l2_flood_member_t * member) { + u32 ix; + vnet_sw_interface_t *sw_if = vnet_get_sw_interface + (vnet_get_main (), member->sw_if_index); + /* * Add one element to the vector - * + * vector is ordered [ bvi, normal/tun_masters..., tun_normals... ] * When flooding, the bvi interface (if present) must be the last member * processed due to how BVI processing can change the packet. To enable * this order, we make the bvi interface the first in the vector and * flooding walks the vector in reverse. */ - if ((member->flags == L2_FLOOD_MEMBER_NORMAL) || - (vec_len (bd_config->members) == 0)) + switch (sw_if->flood_class) { - vec_add1 (bd_config->members, *member); - - } - else - { - /* Move 0th element to the end */ - vec_add1 (bd_config->members, bd_config->members[0]); - bd_config->members[0] = *member; - } + case VNET_FLOOD_CLASS_TUNNEL_MASTER: + bd_config->tun_master_count++; + /* Fall through */ + default: + /* Fall through */ + case VNET_FLOOD_CLASS_NORMAL: + ix = (member->flags & L2_FLOOD_MEMBER_BVI) ? 0 : + vec_len (bd_config->members) - bd_config->tun_normal_count; + break; + case VNET_FLOOD_CLASS_TUNNEL_NORMAL: + ix = vec_len (bd_config->members); + bd_config->tun_normal_count++; + break; + } + + vec_insert_elts (bd_config->members, member, 1, ix); + update_flood_count (bd_config); } - #define BD_REMOVE_ERROR_OK 0 #define BD_REMOVE_ERROR_NOT_FOUND 1 @@ -151,9 +171,22 @@ bd_remove_member (l2_bridge_domain_t * bd_config, u32 sw_if_index) /* Find and delete the member */ vec_foreach_index (ix, bd_config->members) { - if (vec_elt (bd_config->members, ix).sw_if_index == sw_if_index) + l2_flood_member_t *m = vec_elt_at_index (bd_config->members, ix); + if (m->sw_if_index == sw_if_index) { + vnet_sw_interface_t *sw_if = vnet_get_sw_interface + (vnet_get_main (), sw_if_index); + + if (sw_if->flood_class != VNET_FLOOD_CLASS_NORMAL) + { + if (sw_if->flood_class == VNET_FLOOD_CLASS_TUNNEL_MASTER) + bd_config->tun_master_count--; + else if (sw_if->flood_class == VNET_FLOOD_CLASS_TUNNEL_NORMAL) + bd_config->tun_normal_count--; + } vec_del1 (bd_config->members, ix); + update_flood_count (bd_config); + return BD_REMOVE_ERROR_OK; } } @@ -854,28 +887,27 @@ bd_show (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) if (detail || intf) { /* Show all member interfaces */ - - l2_flood_member_t *member; - u32 header = 0; - - vec_foreach (member, bd_config->members) + int i; + vec_foreach_index (i, bd_config->members) { + l2_flood_member_t *member = + vec_elt_at_index (bd_config->members, i); u32 vtr_opr, dot1q, tag1, tag2; - if (!header) + if (i == 0) { - header = 1; - vlib_cli_output (vm, "\n%=30s%=7s%=5s%=5s%=30s", + vlib_cli_output (vm, "\n%=30s%=7s%=5s%=5s%=9s%=30s", "Interface", "Index", "SHG", "BVI", - "VLAN-Tag-Rewrite"); + "TxFlood", "VLAN-Tag-Rewrite"); } l2vtr_get (vm, vnm, member->sw_if_index, &vtr_opr, &dot1q, &tag1, &tag2); - vlib_cli_output (vm, "%=30U%=7d%=5d%=5s%=30U", + vlib_cli_output (vm, "%=30U%=7d%=5d%=5s%=9s%=30U", format_vnet_sw_if_index_name, vnm, member->sw_if_index, member->sw_if_index, member->shg, member->flags & L2_FLOOD_MEMBER_BVI ? "*" : - "-", format_vtr, vtr_opr, dot1q, tag1, tag2); + "-", i < bd_config->flood_count ? "*" : "-", + format_vtr, vtr_opr, dot1q, tag1, tag2); } } diff --git a/vnet/vnet/l2/l2_bd.h b/vnet/vnet/l2/l2_bd.h index 2d7853eb..b9ee8236 100644 --- a/vnet/vnet/l2/l2_bd.h +++ b/vnet/vnet/l2/l2_bd.h @@ -67,9 +67,18 @@ typedef struct /* bridge domain id, not to be confused with bd_index */ u32 bd_id; - /* Vector of members in the replication group */ + /* Vector of member ports */ l2_flood_member_t *members; + /* First flood_count member ports are flooded */ + u32 flood_count; + + /* Tunnel Master (Multicast vxlan) are always flooded */ + u32 tun_master_count; + + /* Tunnels (Unicast vxlan) are flooded if there are no masters */ + u32 tun_normal_count; + /* hash ip4/ip6 -> mac for arp/nd termination */ uword *mac_by_ip4; uword *mac_by_ip6; diff --git a/vnet/vnet/l2/l2_flood.c b/vnet/vnet/l2/l2_flood.c index 5e9881bf..ed9e5ac2 100644 --- a/vnet/vnet/l2/l2_flood.c +++ b/vnet/vnet/l2/l2_flood.c @@ -160,7 +160,7 @@ l2flood_process (vlib_main_t * vm, members = bd_config->members; /* Find first member that passes the reflection and SHG checks */ - current_member = vec_len (members) - 1; + current_member = bd_config->flood_count - 1; while ((current_member >= 0) && ((members[current_member].sw_if_index == *sw_if_index0) || (in_shg && members[current_member].shg == in_shg))) @@ -247,16 +247,7 @@ l2flood_process (vlib_main_t * vm, } /* Forward packet to the current member */ - - if (PREDICT_TRUE (members[current_member].flags == L2_FLOOD_MEMBER_NORMAL)) - { - /* Do normal L2 forwarding */ - vnet_buffer (b0)->sw_if_index[VLIB_TX] = - members[current_member].sw_if_index; - *next0 = L2FLOOD_NEXT_L2_OUTPUT; - - } - else + if (PREDICT_FALSE (members[current_member].flags & L2_FLOOD_MEMBER_BVI)) { /* Do BVI processing */ u32 rc; @@ -280,6 +271,14 @@ l2flood_process (vlib_main_t * vm, } } } + else + { + /* Do normal L2 forwarding */ + vnet_buffer (b0)->sw_if_index[VLIB_TX] = + members[current_member].sw_if_index; + *next0 = L2FLOOD_NEXT_L2_OUTPUT; + + } } diff --git a/vnet/vnet/l2/l2_input.c b/vnet/vnet/l2/l2_input.c index 5d4a3761..babca1b2 100644 --- a/vnet/vnet/l2/l2_input.c +++ b/vnet/vnet/l2/l2_input.c @@ -550,9 +550,7 @@ set_int_l2_mode (vlib_main_t * vm, vnet_main_t * vnet_main, u32 mode, u32 sw_if_ u32 slot; hi = vnet_get_sup_hw_interface (vnet_main, sw_if_index); - - vec_validate (mp->configs, sw_if_index); - config = vec_elt_at_index (mp->configs, sw_if_index); + config = l2input_intf_config (sw_if_index); if (config->bridge) { diff --git a/vnet/vnet/lisp-gpe/lisp_gpe_sub_interface.c b/vnet/vnet/lisp-gpe/lisp_gpe_sub_interface.c index b7802ff2..5b69bd15 100644 --- a/vnet/vnet/lisp-gpe/lisp_gpe_sub_interface.c +++ b/vnet/vnet/lisp-gpe/lisp_gpe_sub_interface.c @@ -133,6 +133,7 @@ lisp_gpe_sub_interface_find_or_create_and_lock (const ip_address_t * lrloc, vnet_sw_interface_t sub_itf_template = { .type = VNET_SW_INTERFACE_TYPE_SUB, + .flood_class = VNET_FLOOD_CLASS_NORMAL, .sup_sw_if_index = main_sw_if_index, .sub.id = lisp_gpe_sub_interface_id++, }; diff --git a/vnet/vnet/vxlan/vxlan.c b/vnet/vnet/vxlan/vxlan.c index d37e9d68..3c6d7bd9 100644 --- a/vnet/vnet/vxlan/vxlan.c +++ b/vnet/vnet/vxlan/vxlan.c @@ -16,6 +16,7 @@ #include <vnet/ip/format.h> #include <vnet/fib/fib_entry.h> #include <vnet/fib/fib_table.h> +#include <vnet/dpo/receive_dpo.h> /** * @file @@ -97,6 +98,19 @@ VNET_HW_INTERFACE_CLASS (vxlan_hw_class) = { .build_rewrite = default_build_rewrite, }; +static void +vxlan_tunnel_restack_dpo(vxlan_tunnel_t * t) +{ + dpo_id_t dpo = DPO_INVALID; + u32 encap_index = ip46_address_is_ip4(&t->dst) ? + vxlan4_encap_node.index : vxlan6_encap_node.index; + fib_forward_chain_type_t forw_type = ip46_address_is_ip4(&t->dst) ? + FIB_FORW_CHAIN_TYPE_UNICAST_IP4 : FIB_FORW_CHAIN_TYPE_UNICAST_IP6; + + fib_entry_contribute_forwarding (t->fib_entry_index, forw_type, &dpo); + dpo_stack_from_node (encap_index, &t->next_dpo, &dpo); + dpo_reset(&dpo); +} static vxlan_tunnel_t * vxlan_tunnel_from_fib_node (fib_node_t *node) @@ -116,22 +130,7 @@ static fib_node_back_walk_rc_t vxlan_tunnel_back_walk (fib_node_t *node, fib_node_back_walk_ctx_t *ctx) { - vxlan_tunnel_t *t = vxlan_tunnel_from_fib_node(node); - dpo_id_t dpo = DPO_INVALID; - - if (ip46_address_is_ip4(&t->dst)) { - fib_entry_contribute_forwarding - (t->fib_entry_index, FIB_FORW_CHAIN_TYPE_UNICAST_IP4, &dpo); - dpo_stack_from_node - (vxlan4_encap_node.index, &t->next_dpo, &dpo); - } else { - fib_entry_contribute_forwarding - (t->fib_entry_index, FIB_FORW_CHAIN_TYPE_UNICAST_IP6, &dpo); - dpo_stack_from_node - (vxlan6_encap_node.index, &t->next_dpo, &dpo); - } - dpo_reset(&dpo); - + vxlan_tunnel_restack_dpo(vxlan_tunnel_from_fib_node(node)); return (FIB_NODE_BACK_WALK_CONTINUE); } @@ -175,19 +174,10 @@ const static fib_node_vft_t vxlan_vft = { #define foreach_copy_field \ _(vni) \ -_(encap_fib_index) - -#define foreach_copy_ipv4 { \ - _(src.ip4.as_u32) \ - _(dst.ip4.as_u32) \ -} - -#define foreach_copy_ipv6 { \ - _(src.ip6.as_u64[0]) \ - _(src.ip6.as_u64[1]) \ - _(dst.ip6.as_u64[0]) \ - _(dst.ip6.as_u64[1]) \ -} +_(mcast_sw_if_index) \ +_(encap_fib_index) \ +_(src) \ +_(dst) static int vxlan4_rewrite (vxlan_tunnel_t * t) { @@ -297,8 +287,6 @@ int vnet_vxlan_add_del_tunnel /* copy from arg structure */ #define _(x) t->x = a->x; foreach_copy_field; - if (!is_ip6) foreach_copy_ipv4 - else foreach_copy_ipv6 #undef _ /* copy the key */ @@ -372,66 +360,59 @@ int vnet_vxlan_add_del_tunnel vnet_sw_interface_set_flags (vnm, sw_if_index, VNET_SW_INTERFACE_FLAG_ADMIN_UP); - /* - * source the FIB entry for the tunnel's destination - * and become a child thereof. The tunnel will then get poked - * when the forwarding for the entry updates, and the tunnel can - * re-stack accordingly - */ fib_node_init(&t->node, FIB_NODE_TYPE_VXLAN_TUNNEL); - if (!is_ip6) - { - dpo_id_t dpo = DPO_INVALID; - const fib_prefix_t tun_dst_pfx = - { - .fp_len = 32, - .fp_proto = FIB_PROTOCOL_IP4, - .fp_addr = - { - .ip4 = t->dst.ip4, - } - }; - - t->fib_entry_index = fib_table_entry_special_add - (t->encap_fib_index, &tun_dst_pfx, - FIB_SOURCE_RR, FIB_ENTRY_FLAG_NONE, ADJ_INDEX_INVALID); - t->sibling_index = fib_entry_child_add - (t->fib_entry_index, FIB_NODE_TYPE_VXLAN_TUNNEL, t - vxm->tunnels); - fib_entry_contribute_forwarding - (t->fib_entry_index, FIB_FORW_CHAIN_TYPE_UNICAST_IP4, &dpo); - dpo_stack_from_node (vxlan4_encap_node.index, &t->next_dpo, &dpo); - dpo_reset(&dpo); - - /* Set vxlan tunnel output node to ip4 version */ - hi->output_node_index = vxlan4_encap_node.index; - } - else - { - dpo_id_t dpo = DPO_INVALID; - const fib_prefix_t tun_dst_pfx = - { - .fp_len = 128, - .fp_proto = FIB_PROTOCOL_IP6, - .fp_addr = - { - .ip6 = t->dst.ip6, - } - }; - - t->fib_entry_index = fib_table_entry_special_add - (t->encap_fib_index, &tun_dst_pfx, - FIB_SOURCE_RR, FIB_ENTRY_FLAG_NONE, ADJ_INDEX_INVALID); - t->sibling_index = fib_entry_child_add - (t->fib_entry_index, FIB_NODE_TYPE_VXLAN_TUNNEL, t - vxm->tunnels); - fib_entry_contribute_forwarding - (t->fib_entry_index, FIB_FORW_CHAIN_TYPE_UNICAST_IP6, &dpo); - dpo_stack_from_node - (vxlan6_encap_node.index, &t->next_dpo, &dpo); - dpo_reset(&dpo); - - /* Set vxlan tunnel output node to ip6 version */ - hi->output_node_index = vxlan6_encap_node.index; - } + fib_prefix_t tun_dst_pfx; + u32 encap_index = !is_ip6 ? + vxlan4_encap_node.index : vxlan6_encap_node.index; + vnet_flood_class_t flood_class = VNET_FLOOD_CLASS_TUNNEL_NORMAL; + + fib_prefix_from_ip46_addr(&t->dst, &tun_dst_pfx); + if (ip46_address_is_multicast(&t->dst)) + { + fib_protocol_t fp; + u8 mcast_mac[6]; + if (!is_ip6) { + ip4_multicast_ethernet_address(mcast_mac, &t->dst.ip4); + fp = FIB_PROTOCOL_IP4; + } else { + ip6_multicast_ethernet_address(mcast_mac, t->dst.ip6.as_u32[0]); + fp = FIB_PROTOCOL_IP6; + } + t->mcast_adj_index = adj_rewrite_add_and_lock + (fp, fib_proto_to_link(fp), t->mcast_sw_if_index, mcast_mac); + + flood_class = VNET_FLOOD_CLASS_TUNNEL_MASTER; + + /* Stack mcast dst mac addr rewrite on encap */ + dpo_proto_t dproto = fib_proto_to_dpo(fp); + dpo_id_t dpo = DPO_INVALID; + + dpo_set (&dpo, DPO_ADJACENCY, dproto, t->mcast_adj_index); + dpo_stack_from_node (encap_index, &t->next_dpo, &dpo); + dpo_reset(&dpo); + + /* Add local mcast adj. */ + receive_dpo_add_or_lock(dproto, ~0, NULL, &dpo); + t->fib_entry_index = fib_table_entry_special_dpo_add + (t->encap_fib_index, &tun_dst_pfx, FIB_SOURCE_SPECIAL, FIB_ENTRY_FLAG_NONE, &dpo); + dpo_reset(&dpo); + } else { + /* + * source the FIB entry for the tunnel's destination + * and become a child thereof. The tunnel will then get poked + * when the forwarding for the entry updates, and the tunnel can + * re-stack accordingly + */ + t->fib_entry_index = fib_table_entry_special_add + (t->encap_fib_index, &tun_dst_pfx, FIB_SOURCE_RR, FIB_ENTRY_FLAG_NONE, ADJ_INDEX_INVALID); + t->sibling_index = fib_entry_child_add + (t->fib_entry_index, FIB_NODE_TYPE_VXLAN_TUNNEL, t - vxm->tunnels); + vxlan_tunnel_restack_dpo(t); + } + /* Set vxlan tunnel output node */ + hi->output_node_index = encap_index; + + vnet_get_sw_interface (vnet_get_main(), sw_if_index)->flood_class = flood_class; } else { @@ -448,8 +429,16 @@ int vnet_vxlan_add_del_tunnel vxm->tunnel_index_by_sw_if_index[t->sw_if_index] = ~0; - fib_entry_child_remove(t->fib_entry_index, t->sibling_index); - fib_table_entry_delete_index(t->fib_entry_index, FIB_SOURCE_RR); + if (ip46_address_is_multicast(&t->dst)) + { + adj_unlock(t->mcast_adj_index); + fib_table_entry_delete_index(t->fib_entry_index, FIB_SOURCE_SPECIAL); + } + else + { + fib_entry_child_remove(t->fib_entry_index, t->sibling_index); + fib_table_entry_delete_index(t->fib_entry_index, FIB_SOURCE_RR); + } fib_node_deinit(&t->node); if (!is_ip6) @@ -515,20 +504,26 @@ vxlan_add_del_tunnel_command_fn (vlib_main_t * vm, vlib_cli_command_t * cmd) { unformat_input_t _line_input, * line_input = &_line_input; - ip46_address_t src, dst; + ip46_address_t src , dst; u8 is_add = 1; u8 src_set = 0; u8 dst_set = 0; + u8 grp_set = 0; u8 ipv4_set = 0; u8 ipv6_set = 0; u32 encap_fib_index = 0; + u32 mcast_sw_if_index = ~0; u32 decap_next_index = ~0; u32 vni = 0; u32 tmp; int rv; vnet_vxlan_add_del_tunnel_args_t _a, * a = &_a; - u32 sw_if_index; - + u32 tunnel_sw_if_index; + + /* Cant "universally zero init" (={0}) due to GCC bug 53119 */ + memset(&src, 0, sizeof src); + memset(&dst, 0, sizeof dst); + /* Get a line of input. */ if (! unformat_user (input, unformat_line_input, line_input)) return 0; @@ -562,6 +557,22 @@ vxlan_add_del_tunnel_command_fn (vlib_main_t * vm, dst_set = 1; ipv6_set = 1; } + else if (unformat (line_input, "group %U %U", + unformat_ip4_address, &dst.ip4, + unformat_vnet_sw_interface, + vnet_get_main(), &mcast_sw_if_index)) + { + grp_set = dst_set = 1; + ipv4_set = 1; + } + else if (unformat (line_input, "group %U %U", + unformat_ip6_address, &dst.ip6, + unformat_vnet_sw_interface, + vnet_get_main(), &mcast_sw_if_index)) + { + grp_set = dst_set = 1; + ipv6_set = 1; + } else if (unformat (line_input, "encap-vrf-id %d", &tmp)) { if (ipv6_set) @@ -592,11 +603,16 @@ vxlan_add_del_tunnel_command_fn (vlib_main_t * vm, if (dst_set == 0) return clib_error_return (0, "tunnel dst address not specified"); + if (grp_set && !ip46_address_is_multicast(&dst)) + return clib_error_return (0, "tunnel group address not multicast"); + + if (grp_set && mcast_sw_if_index == ~0) + return clib_error_return (0, "tunnel nonexistent multicast device"); + if (ipv4_set && ipv6_set) return clib_error_return (0, "both IPv4 and IPv6 addresses specified"); - if ((ipv4_set && memcmp(&src.ip4, &dst.ip4, sizeof(src.ip4)) == 0) || - (ipv6_set && memcmp(&src.ip6, &dst.ip6, sizeof(src.ip6)) == 0)) + if (ip46_address_cmp(&src, &dst) == 0) return clib_error_return (0, "src and dst addresses are identical"); if (vni == 0) @@ -609,18 +625,16 @@ vxlan_add_del_tunnel_command_fn (vlib_main_t * vm, #define _(x) a->x = x; foreach_copy_field; - if (ipv4_set) foreach_copy_ipv4 - else foreach_copy_ipv6 #undef _ - rv = vnet_vxlan_add_del_tunnel (a, &sw_if_index); + rv = vnet_vxlan_add_del_tunnel (a, &tunnel_sw_if_index); switch(rv) { case 0: if (is_add) vlib_cli_output(vm, "%U\n", format_vnet_sw_if_index_name, - vnet_get_main(), sw_if_index); + vnet_get_main(), tunnel_sw_if_index); break; case VNET_API_ERROR_TUNNEL_EXIST: @@ -661,7 +675,8 @@ vxlan_add_del_tunnel_command_fn (vlib_main_t * vm, VLIB_CLI_COMMAND (create_vxlan_tunnel_command, static) = { .path = "create vxlan tunnel", .short_help = - "create vxlan tunnel src <local-vtep-addr> dst <remote-vtep-addr> vni <nn>" + "create vxlan tunnel src <local-vtep-addr>" + " {dst <remote-vtep-addr>|group <mcast-vtep-addr> <intf-name>} vni <nn>" " [encap-vrf-id <nn>]", .function = vxlan_add_del_tunnel_command_fn, }; diff --git a/vnet/vnet/vxlan/vxlan.h b/vnet/vnet/vxlan/vxlan.h index e37f09aa..28ce2a93 100644 --- a/vnet/vnet/vxlan/vxlan.h +++ b/vnet/vnet/vxlan/vxlan.h @@ -28,6 +28,7 @@ #include <vnet/ip/ip6_packet.h> #include <vnet/ip/udp.h> #include <vnet/dpo/dpo.h> +#include <vnet/adj/adj_types.h> typedef CLIB_PACKED (struct { ip4_header_t ip4; /* 20 bytes */ @@ -84,6 +85,8 @@ typedef struct { ip46_address_t src; ip46_address_t dst; + u32 mcast_sw_if_index; + /* The FIB index for src/dst addresses */ u32 encap_fib_index; @@ -96,8 +99,12 @@ typedef struct { */ fib_node_t node; - /* The FIB entry sourced by the tunnel for its destination prefix */ + /* + * The FIB entry for (depending on VXLAN tunnel is unicast or mcast) + * sending unicast VXLAN encap packets or receiving mcast VXLAN packets + */ fib_node_index_t fib_entry_index; + adj_index_t mcast_adj_index; /** * The tunnel is a child of the FIB entry for its desintion. This is @@ -161,6 +168,7 @@ typedef struct { * structure, this seems less of abreaking change */ u8 is_ip6; ip46_address_t src, dst; + u32 mcast_sw_if_index; u32 encap_fib_index; u32 decap_next_index; u32 vni; |