Add extra validation for VXLAN packets and tunnels 85/4185/3
authorJohn Lo <loj@cisco.com>
Thu, 8 Dec 2016 21:10:02 +0000 (16:10 -0500)
committerDamjan Marion <dmarion.lists@gmail.com>
Fri, 9 Dec 2016 09:00:11 +0000 (09:00 +0000)
- 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 <loj@cisco.com>
vnet/vnet/vxlan/decap.c
vnet/vnet/vxlan/vxlan.c
vnet/vnet/vxlan/vxlan.h
vpp-api-test/vat/api_format.c
vpp/vpp-api/api.c

index 22f2f85..73e50ff 100644 (file)
@@ -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;
index a2b8920..894cc3d 100644 (file)
@@ -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");
 
index fe31ce1..f475bba 100644 (file)
@@ -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;
 
index 85821ee..258c67f 100644 (file)
@@ -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)
index 3868af9..a5f50ff 100644 (file)
@@ -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);