diff options
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; |