From 56912c896ae803fad01af390ade921be68ea5ba2 Mon Sep 17 00:00:00 2001 From: John Lo Date: Thu, 8 Dec 2016 16:10:02 -0500 Subject: [PATCH] Add extra validation for VXLAN packets and tunnels - On VXLAN packet decap, validate its DIP against VXLAN tunnel. - Add extra logic to validate and handle creation of multicast VXLAN tunnels. Change-Id: I6abdddd7be4cd9f1bcfc88d9970ba681fdd72f7c Signed-off-by: John Lo --- vnet/vnet/vxlan/decap.c | 191 ++++++++++++++++++++++++++++++++++-------- vnet/vnet/vxlan/vxlan.c | 141 +++++++++++++++++++------------ vnet/vnet/vxlan/vxlan.h | 2 + vpp-api-test/vat/api_format.c | 5 ++ vpp/vpp-api/api.c | 8 ++ 5 files changed, 257 insertions(+), 90 deletions(-) diff --git a/vnet/vnet/vxlan/decap.c b/vnet/vnet/vxlan/decap.c index 22f2f85b2bc..73e50ffe31f 100644 --- a/vnet/vnet/vxlan/decap.c +++ b/vnet/vnet/vxlan/decap.c @@ -93,7 +93,7 @@ vxlan_input (vlib_main_t * vm, vxlan_header_t * vxlan0, * vxlan1; uword * p0, * p1; u32 tunnel_index0, tunnel_index1; - vxlan_tunnel_t * t0, * t1; + vxlan_tunnel_t * t0, * t1, * mt0 = NULL, * mt1 = NULL; vxlan4_tunnel_key_t key4_0, key4_1; vxlan6_tunnel_key_t key6_0, key6_1; u32 error0, error1; @@ -128,9 +128,6 @@ vxlan_input (vlib_main_t * vm, /* udp leaves current_data pointing at the vxlan header */ vxlan0 = vlib_buffer_get_current (b0); vxlan1 = vlib_buffer_get_current (b1); - - next0 = next1 = VXLAN_INPUT_NEXT_L2_INPUT; - if (is_ip4) { vlib_buffer_advance (b0, -(word)(sizeof(udp_header_t)+sizeof(ip4_header_t))); @@ -177,46 +174,86 @@ vxlan_input (vlib_main_t * vm, key4_0.src = ip4_0->src_address.as_u32; key4_0.vni = vxlan0->vni_reserved; - if (PREDICT_FALSE (key4_0.as_u64 != last_key4.as_u64)) + /* Make sure VXLAN tunnel exist according to packet SIP and VNI */ + if (PREDICT_FALSE (key4_0.as_u64 != last_key4.as_u64)) { p0 = hash_get (vxm->vxlan4_tunnel_by_key, key4_0.as_u64); - - if (p0 == 0) + if (PREDICT_FALSE (p0 == NULL)) { error0 = VXLAN_ERROR_NO_SUCH_TUNNEL; next0 = VXLAN_INPUT_NEXT_DROP; goto trace0; } - last_key4.as_u64 = key4_0.as_u64; tunnel_index0 = last_tunnel_index = p0[0]; } else tunnel_index0 = last_tunnel_index; - } else /* !is_ip4 */ { + t0 = pool_elt_at_index (vxm->tunnels, tunnel_index0); + + /* Validate VXLAN tunnel SIP against packet DIP */ + if (PREDICT_TRUE (ip4_0->dst_address.as_u32 == t0->src.ip4.as_u32)) + goto next0; /* valid packet */ + if (PREDICT_FALSE (ip4_address_is_multicast (&ip4_0->dst_address))) + { + key4_0.src = ip4_0->dst_address.as_u32; + key4_0.vni = vxlan0->vni_reserved; + /* Make sure mcast VXLAN tunnel exist by packet DIP and VNI */ + p0 = hash_get (vxm->vxlan4_tunnel_by_key, key4_0.as_u64); + if (PREDICT_TRUE (p0 != NULL)) + { + mt0 = pool_elt_at_index (vxm->tunnels, p0[0]); + goto next0; /* valid packet */ + } + } + error0 = VXLAN_ERROR_NO_SUCH_TUNNEL; + next0 = VXLAN_INPUT_NEXT_DROP; + goto trace0; + + } else /* !is_ip4 */ { key6_0.src.as_u64[0] = ip6_0->src_address.as_u64[0]; key6_0.src.as_u64[1] = ip6_0->src_address.as_u64[1]; key6_0.vni = vxlan0->vni_reserved; - if (PREDICT_FALSE (memcmp(&key6_0, &last_key6, sizeof(last_key6)) != 0)) + /* Make sure VXLAN tunnel exist according to packet SIP and VNI */ + if (PREDICT_FALSE (memcmp(&key6_0, &last_key6, sizeof(last_key6)) != 0)) { p0 = hash_get_mem (vxm->vxlan6_tunnel_by_key, &key6_0); - - if (p0 == 0) + if (PREDICT_FALSE (p0 == NULL)) { error0 = VXLAN_ERROR_NO_SUCH_TUNNEL; next0 = VXLAN_INPUT_NEXT_DROP; goto trace0; } - clib_memcpy (&last_key6, &key6_0, sizeof(key6_0)); tunnel_index0 = last_tunnel_index = p0[0]; } else tunnel_index0 = last_tunnel_index; + t0 = pool_elt_at_index (vxm->tunnels, tunnel_index0); + + /* Validate VXLAN tunnel SIP against packet DIP */ + if (PREDICT_TRUE (ip6_address_is_equal (&ip6_0->dst_address, + &t0->src.ip6))) + goto next0; /* valid packet */ + if (PREDICT_FALSE (ip6_address_is_multicast (&ip6_0->dst_address))) + { + key6_0.src.as_u64[0] = ip6_0->dst_address.as_u64[0]; + key6_0.src.as_u64[1] = ip6_0->dst_address.as_u64[1]; + key6_0.vni = vxlan0->vni_reserved; + p0 = hash_get (vxm->vxlan6_tunnel_by_key, &key6_0); + if (PREDICT_TRUE (p0 != NULL)) + { + mt0 = pool_elt_at_index (vxm->tunnels, p0[0]); + goto next0; /* valid packet */ + } + } + error0 = VXLAN_ERROR_NO_SUCH_TUNNEL; + next0 = VXLAN_INPUT_NEXT_DROP; + goto trace0; } - t0 = pool_elt_at_index (vxm->tunnels, tunnel_index0); + next0: next0 = t0->decap_next_index; sw_if_index0 = t0->sw_if_index; len0 = vlib_buffer_length_in_chain (vm, b0); @@ -225,8 +262,9 @@ vxlan_input (vlib_main_t * vm, if (PREDICT_TRUE(next0 == VXLAN_INPUT_NEXT_L2_INPUT)) vnet_update_l2_len (b0); - /* Set input sw_if_index to VXLAN tunnel for learning */ + /* Set packet input sw_if_index to unicast VXLAN tunnel for learning */ vnet_buffer(b0)->sw_if_index[VLIB_RX] = sw_if_index0; + sw_if_index0 = (mt0) ? mt0->sw_if_index : sw_if_index0; pkts_decapsulated ++; stats_n_packets += 1; @@ -272,32 +310,53 @@ vxlan_input (vlib_main_t * vm, key4_1.src = ip4_1->src_address.as_u32; key4_1.vni = vxlan1->vni_reserved; - if (PREDICT_FALSE (key4_1.as_u64 != last_key4.as_u64)) + /* Make sure unicast VXLAN tunnel exist by packet SIP and VNI */ + if (PREDICT_FALSE (key4_1.as_u64 != last_key4.as_u64)) { p1 = hash_get (vxm->vxlan4_tunnel_by_key, key4_1.as_u64); - - if (p1 == 0) + if (PREDICT_FALSE (p1 == NULL)) { error1 = VXLAN_ERROR_NO_SUCH_TUNNEL; next1 = VXLAN_INPUT_NEXT_DROP; goto trace1; } - last_key4.as_u64 = key4_1.as_u64; tunnel_index1 = last_tunnel_index = p1[0]; } else tunnel_index1 = last_tunnel_index; - } else /* !is_ip4 */ { + t1 = pool_elt_at_index (vxm->tunnels, tunnel_index1); + + /* Validate VXLAN tunnel SIP against packet DIP */ + if (PREDICT_TRUE (ip4_1->dst_address.as_u32 == t1->src.ip4.as_u32)) + goto next1; /* valid packet */ + if (PREDICT_FALSE (ip4_address_is_multicast (&ip4_1->dst_address))) + { + key4_1.src = ip4_1->dst_address.as_u32; + key4_1.vni = vxlan1->vni_reserved; + /* Make sure mcast VXLAN tunnel exist by packet DIP and VNI */ + p1 = hash_get (vxm->vxlan4_tunnel_by_key, key4_1.as_u64); + if (PREDICT_TRUE (p1 != NULL)) + { + mt1 = pool_elt_at_index (vxm->tunnels, p1[0]); + goto next1; /* valid packet */ + } + } + error1 = VXLAN_ERROR_NO_SUCH_TUNNEL; + next1 = VXLAN_INPUT_NEXT_DROP; + goto trace1; + + } else /* !is_ip4 */ { key6_1.src.as_u64[0] = ip6_1->src_address.as_u64[0]; key6_1.src.as_u64[1] = ip6_1->src_address.as_u64[1]; key6_1.vni = vxlan1->vni_reserved; + /* Make sure VXLAN tunnel exist according to packet SIP and VNI */ if (PREDICT_FALSE (memcmp(&key6_1, &last_key6, sizeof(last_key6)) != 0)) { p1 = hash_get_mem (vxm->vxlan6_tunnel_by_key, &key6_1); - if (p1 == 0) + if (PREDICT_FALSE (p1 == NULL)) { error1 = VXLAN_ERROR_NO_SUCH_TUNNEL; next1 = VXLAN_INPUT_NEXT_DROP; @@ -309,9 +368,30 @@ vxlan_input (vlib_main_t * vm, } else tunnel_index1 = last_tunnel_index; - } + t1 = pool_elt_at_index (vxm->tunnels, tunnel_index1); + + /* Validate VXLAN tunnel SIP against packet DIP */ + if (PREDICT_TRUE (ip6_address_is_equal (&ip6_1->dst_address, + &t1->src.ip6))) + goto next1; /* valid packet */ + if (PREDICT_FALSE (ip6_address_is_multicast (&ip6_1->dst_address))) + { + key6_1.src.as_u64[0] = ip6_1->dst_address.as_u64[0]; + key6_1.src.as_u64[1] = ip6_1->dst_address.as_u64[1]; + key6_1.vni = vxlan1->vni_reserved; + p1 = hash_get (vxm->vxlan6_tunnel_by_key, &key6_1); + if (PREDICT_TRUE (p1 != NULL)) + { + mt1 = pool_elt_at_index (vxm->tunnels, p1[0]); + goto next1; /* valid packet */ + } + } + error1 = VXLAN_ERROR_NO_SUCH_TUNNEL; + next1 = VXLAN_INPUT_NEXT_DROP; + goto trace1; + } - t1 = pool_elt_at_index (vxm->tunnels, tunnel_index1); + next1: next1 = t1->decap_next_index; sw_if_index1 = t1->sw_if_index; len1 = vlib_buffer_length_in_chain (vm, b1); @@ -320,8 +400,9 @@ vxlan_input (vlib_main_t * vm, if (PREDICT_TRUE(next1 == VXLAN_INPUT_NEXT_L2_INPUT)) vnet_update_l2_len (b1); - /* Set input sw_if_index to VXLAN tunnel for learning */ + /* Set packet input sw_if_index to unicast VXLAN tunnel for learning */ vnet_buffer(b1)->sw_if_index[VLIB_RX] = sw_if_index1; + sw_if_index1 = (mt1) ? mt1->sw_if_index : sw_if_index1; pkts_decapsulated ++; stats_n_packets += 1; @@ -371,7 +452,7 @@ vxlan_input (vlib_main_t * vm, vxlan_header_t * vxlan0; uword * p0; u32 tunnel_index0; - vxlan_tunnel_t * t0; + vxlan_tunnel_t * t0, * mt0 = NULL; vxlan4_tunnel_key_t key4_0; vxlan6_tunnel_key_t key6_0; u32 error0; @@ -388,9 +469,6 @@ vxlan_input (vlib_main_t * vm, /* udp leaves current_data pointing at the vxlan header */ vxlan0 = vlib_buffer_get_current (b0); - - next0 = VXLAN_INPUT_NEXT_L2_INPUT; - if (is_ip4) { vlib_buffer_advance (b0, -(word)(sizeof(udp_header_t)+sizeof(ip4_header_t))); @@ -424,46 +502,86 @@ vxlan_input (vlib_main_t * vm, key4_0.src = ip4_0->src_address.as_u32; key4_0.vni = vxlan0->vni_reserved; + /* Make sure unicast VXLAN tunnel exist by packet SIP and VNI */ if (PREDICT_FALSE (key4_0.as_u64 != last_key4.as_u64)) { p0 = hash_get (vxm->vxlan4_tunnel_by_key, key4_0.as_u64); - - if (p0 == 0) + if (PREDICT_FALSE (p0 == NULL)) { error0 = VXLAN_ERROR_NO_SUCH_TUNNEL; next0 = VXLAN_INPUT_NEXT_DROP; goto trace00; } - last_key4.as_u64 = key4_0.as_u64; tunnel_index0 = last_tunnel_index = p0[0]; } else tunnel_index0 = last_tunnel_index; + t0 = pool_elt_at_index (vxm->tunnels, tunnel_index0); + + /* Validate VXLAN tunnel SIP against packet DIP */ + if (PREDICT_TRUE (ip4_0->dst_address.as_u32 == t0->src.ip4.as_u32)) + goto next00; /* valid packet */ + if (PREDICT_FALSE (ip4_address_is_multicast (&ip4_0->dst_address))) + { + key4_0.src = ip4_0->dst_address.as_u32; + key4_0.vni = vxlan0->vni_reserved; + /* Make sure mcast VXLAN tunnel exist by packet DIP and VNI */ + p0 = hash_get (vxm->vxlan4_tunnel_by_key, key4_0.as_u64); + if (PREDICT_TRUE (p0 != NULL)) + { + mt0 = pool_elt_at_index (vxm->tunnels, p0[0]); + goto next00; /* valid packet */ + } + } + error0 = VXLAN_ERROR_NO_SUCH_TUNNEL; + next0 = VXLAN_INPUT_NEXT_DROP; + goto trace00; + } else /* !is_ip4 */ { key6_0.src.as_u64[0] = ip6_0->src_address.as_u64[0]; key6_0.src.as_u64[1] = ip6_0->src_address.as_u64[1]; key6_0.vni = vxlan0->vni_reserved; + /* Make sure VXLAN tunnel exist according to packet SIP and VNI */ if (PREDICT_FALSE (memcmp(&key6_0, &last_key6, sizeof(last_key6)) != 0)) { p0 = hash_get_mem (vxm->vxlan6_tunnel_by_key, &key6_0); - - if (p0 == 0) + if (PREDICT_FALSE (p0 == NULL)) { error0 = VXLAN_ERROR_NO_SUCH_TUNNEL; next0 = VXLAN_INPUT_NEXT_DROP; goto trace00; } - clib_memcpy (&last_key6, &key6_0, sizeof(key6_0)); tunnel_index0 = last_tunnel_index = p0[0]; } else tunnel_index0 = last_tunnel_index; + t0 = pool_elt_at_index (vxm->tunnels, tunnel_index0); + + /* Validate VXLAN tunnel SIP against packet DIP */ + if (PREDICT_TRUE (ip6_address_is_equal (&ip6_0->dst_address, + &t0->src.ip6))) + goto next00; /* valid packet */ + if (PREDICT_FALSE (ip6_address_is_multicast (&ip6_0->dst_address))) + { + key6_0.src.as_u64[0] = ip6_0->dst_address.as_u64[0]; + key6_0.src.as_u64[1] = ip6_0->dst_address.as_u64[1]; + key6_0.vni = vxlan0->vni_reserved; + p0 = hash_get (vxm->vxlan6_tunnel_by_key, &key6_0); + if (PREDICT_TRUE (p0 != NULL)) + { + mt0 = pool_elt_at_index (vxm->tunnels, p0[0]); + goto next00; /* valid packet */ + } + } + error0 = VXLAN_ERROR_NO_SUCH_TUNNEL; + next0 = VXLAN_INPUT_NEXT_DROP; + goto trace00; } - t0 = pool_elt_at_index (vxm->tunnels, tunnel_index0); + next00: next0 = t0->decap_next_index; sw_if_index0 = t0->sw_if_index; len0 = vlib_buffer_length_in_chain (vm, b0); @@ -472,8 +590,9 @@ vxlan_input (vlib_main_t * vm, if (PREDICT_TRUE(next0 == VXLAN_INPUT_NEXT_L2_INPUT)) vnet_update_l2_len (b0); - /* Set input sw_if_index to VXLAN tunnel for learning */ + /* Set packet input sw_if_index to unicast VXLAN tunnel for learning */ vnet_buffer(b0)->sw_if_index[VLIB_RX] = sw_if_index0; + sw_if_index0 = (mt0) ? mt0->sw_if_index : sw_if_index0; pkts_decapsulated ++; stats_n_packets += 1; diff --git a/vnet/vnet/vxlan/vxlan.c b/vnet/vnet/vxlan/vxlan.c index a2b89208116..894cc3dcbf2 100644 --- a/vnet/vnet/vxlan/vxlan.c +++ b/vnet/vnet/vxlan/vxlan.c @@ -49,7 +49,7 @@ static u8 * format_decap_next (u8 * s, va_list * args) case VXLAN_INPUT_NEXT_L2_INPUT: return format (s, "l2"); default: - return format (s, "next-index %d", next_index); + return format (s, "index %d", next_index); } return s; } @@ -59,14 +59,18 @@ u8 * format_vxlan_tunnel (u8 * s, va_list * args) vxlan_tunnel_t * t = va_arg (*args, vxlan_tunnel_t *); vxlan_main_t * ngm = &vxlan_main; - s = format (s, - "[%d] src %U dst %U vni %d encap_fib_index %d sw_if_index %d " - "fib_entry_index %d", + s = format (s, "[%d] src %U dst %U vni %d sw_if_index %d ", t - ngm->tunnels, format_ip46_address, &t->src, IP46_TYPE_ANY, format_ip46_address, &t->dst, IP46_TYPE_ANY, - t->vni, t->encap_fib_index, t->sw_if_index, t->fib_entry_index); - s = format (s, " decap_next %U\n", format_decap_next, t->decap_next_index); + t->vni, t->sw_if_index); + + if (ip46_address_is_multicast (&t->dst)) + s = format (s, "mcast_sw_if_index %d ", t->mcast_sw_if_index); + + s = format (s, "encap_fib_index %d fib_entry_index %d decap_next %U\n", + t->encap_fib_index, t->fib_entry_index, + format_decap_next, t->decap_next_index); return s; } @@ -300,20 +304,24 @@ int vnet_vxlan_add_del_tunnel vxlan6_tunnel_key_t key6; u32 is_ip6 = a->is_ip6; - if (!is_ip6) { - key4.src = a->dst.ip4.as_u32; /* decap src in key is encap dst in config */ - key4.vni = clib_host_to_net_u32 (a->vni << 8); - - p = hash_get (vxm->vxlan4_tunnel_by_key, key4.as_u64); - pvtep = hash_get (vxm->vtep4, a->src.ip4.as_u32); - } else { - key6.src.as_u64[0] = a->dst.ip6.as_u64[0]; - key6.src.as_u64[1] = a->dst.ip6.as_u64[1]; - key6.vni = clib_host_to_net_u32 (a->vni << 8); - - p = hash_get_mem (vxm->vxlan6_tunnel_by_key, &key6); - pvtep = NULL; /* ip6 vxlan-bypass not yet implemented */ - } + if (!is_ip6) + { + key4.src = a->dst.ip4.as_u32; /* decap src in key is encap dst in config */ + key4.vni = clib_host_to_net_u32 (a->vni << 8); + p = hash_get (vxm->vxlan4_tunnel_by_key, key4.as_u64); + if (ip4_address_is_multicast (&a->dst.ip4)) + pvtep = hash_get (vxm->vtep4, a->dst.ip4.as_u32); + else + pvtep = hash_get (vxm->vtep4, a->src.ip4.as_u32); + } + else + { + key6.src.as_u64[0] = a->dst.ip6.as_u64[0]; + key6.src.as_u64[1] = a->dst.ip6.as_u64[1]; + key6.vni = clib_host_to_net_u32 (a->vni << 8); + p = hash_get_mem (vxm->vxlan6_tunnel_by_key, &key6); + pvtep = NULL; /* ip6 vxlan-bypass not yet implemented */ + } if (a->is_add) { @@ -348,11 +356,10 @@ int vnet_vxlan_add_del_tunnel t->key4 = 0; /* not yet used */ } - if (!is_ip6) { + if (!is_ip6) rv = vxlan4_rewrite (t); - } else { - rv = vxlan6_rewrite (t); - } + else + rv = vxlan6_rewrite (t); if (rv) { @@ -363,8 +370,15 @@ int vnet_vxlan_add_del_tunnel if (!is_ip6) { hash_set (vxm->vxlan4_tunnel_by_key, key4.as_u64, t - vxm->tunnels); - if (pvtep) pvtep[0]++; - else hash_set (vxm->vtep4, a->src.ip4.as_u32, 1); + if (pvtep) + pvtep[0]++; + else + { + if (ip4_address_is_multicast (&a->dst.ip4)) + hash_set (vxm->vtep4, a->dst.ip4.as_u32, 1); + else + hash_set (vxm->vtep4, a->src.ip4.as_u32, 1); + } } else hash_set_mem (vxm->vxlan6_tunnel_by_key, t->key6, t - vxm->tunnels); @@ -419,8 +433,28 @@ int vnet_vxlan_add_del_tunnel 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)) - { + if (!ip46_address_is_multicast(&t->dst)) + { + /* Unicast tunnel - + * 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); + } + else if (pvtep == NULL) + { + /* Multicast tunnel - + * as the same mcast group can be used for mutiple mcast tunnels + * with different VNIs, create the output fib adjecency only if + * it does not already exist + */ fib_protocol_t fp; u8 mcast_mac[6]; if (!is_ip6) { @@ -446,21 +480,11 @@ int vnet_vxlan_add_del_tunnel /* 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); + (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; @@ -481,17 +505,18 @@ int vnet_vxlan_add_del_tunnel vxm->tunnel_index_by_sw_if_index[t->sw_if_index] = ~0; - 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 (!ip46_address_is_multicast(&t->dst)) + { + 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); + } + else if (pvtep == NULL || pvtep[0] == 1) + { + adj_unlock(t->mcast_adj_index); + fib_table_entry_delete_index(t->fib_entry_index, FIB_SOURCE_SPECIAL); + fib_node_deinit(&t->node); + } if (!is_ip6) { @@ -500,7 +525,12 @@ int vnet_vxlan_add_del_tunnel { pvtep[0]--; if (pvtep[0] == 0) - hash_unset (vxm->vtep4, a->src.ip4.as_u32); + { + if (ip4_address_is_multicast (&a->dst.ip4)) + hash_unset (vxm->vtep4, a->dst.ip4.as_u32); + else + hash_unset (vxm->vtep4, a->src.ip4.as_u32); + } } } else @@ -688,6 +718,9 @@ vxlan_add_del_tunnel_command_fn (vlib_main_t * vm, if (grp_set && !ip46_address_is_multicast(&dst)) return clib_error_return (0, "tunnel group address not multicast"); + if (grp_set == 0 && ip46_address_is_multicast(&dst)) + return clib_error_return (0, "dst address must be unicast"); + if (grp_set && mcast_sw_if_index == ~0) return clib_error_return (0, "tunnel nonexistent multicast device"); diff --git a/vnet/vnet/vxlan/vxlan.h b/vnet/vnet/vxlan/vxlan.h index fe31ce160b8..f475bbacf9d 100644 --- a/vnet/vnet/vxlan/vxlan.h +++ b/vnet/vnet/vxlan/vxlan.h @@ -85,7 +85,9 @@ typedef struct { ip46_address_t src; ip46_address_t dst; + /* mcast packet output intfc index (used only if dst is mcast) */ u32 mcast_sw_if_index; + /* decap next index */ u32 decap_next_index; diff --git a/vpp-api-test/vat/api_format.c b/vpp-api-test/vat/api_format.c index 85821ee287b..258c67f8e0c 100644 --- a/vpp-api-test/vat/api_format.c +++ b/vpp-api-test/vat/api_format.c @@ -10372,6 +10372,11 @@ api_vxlan_add_del_tunnel (vat_main_t * vam) errmsg ("tunnel nonexistent multicast device\n"); return -99; } + if (grp_set == 0 && ip46_address_is_multicast (&dst)) + { + errmsg ("tunnel dst address must be unicast\n"); + return -99; + } if (ipv4_set && ipv6_set) diff --git a/vpp/vpp-api/api.c b/vpp/vpp-api/api.c index 3868af9c377..a5f50ff1eef 100644 --- a/vpp/vpp-api/api.c +++ b/vpp/vpp-api/api.c @@ -3104,6 +3104,7 @@ static void vl_api_vxlan_add_del_tunnel_t_handler u32 encap_fib_index; uword *p; ip4_main_t *im = &ip4_main; + vnet_main_t *vnm = vnet_get_main (); u32 sw_if_index = ~0; p = hash_get (im->fib_index_by_table_id, ntohl (mp->encap_vrf_id)); @@ -3129,6 +3130,13 @@ static void vl_api_vxlan_add_del_tunnel_t_handler goto out; } a->mcast_sw_if_index = ntohl (mp->mcast_sw_if_index); + if (ip46_address_is_multicast (&a->dst) && + pool_is_free_index (vnm->interface_main.sw_interfaces, + a->mcast_sw_if_index)) + { + rv = VNET_API_ERROR_INVALID_SW_IF_INDEX; + goto out; + } a->encap_fib_index = encap_fib_index; a->decap_next_index = ntohl (mp->decap_next_index); a->vni = ntohl (mp->vni); -- 2.16.6