VPP-1283: IPv4 PMTU missing MTU value in ICMP4 message.
[vpp.git] / src / vnet / ip / ip4_forward.c
index 3dce590..40fe9dc 100644 (file)
@@ -545,7 +545,7 @@ ip4_add_del_interface_address_internal (vlib_main_t * vm,
                     vec_elt (im->fib_index_by_sw_if_index, sw_if_index));
   vec_add1 (addr_fib, ip4_af);
 
-  /* FIXME-LATER
+  /*
    * there is no support for adj-fib handling in the presence of overlapping
    * subnets on interfaces. Easy fix - disallow overlapping subnets, like
    * most routers do.
@@ -554,31 +554,44 @@ ip4_add_del_interface_address_internal (vlib_main_t * vm,
   if (!is_del)
     {
       /* When adding an address check that it does not conflict
-         with an existing address. */
+         with an existing address on any interface in this table. */
       ip_interface_address_t *ia;
-      foreach_ip_interface_address
-        (&im->lookup_main, ia, sw_if_index,
-         0 /* honor unnumbered */ ,
-         ({
-           ip4_address_t * x =
-             ip_interface_address_get_address
-             (&im->lookup_main, ia);
-           if (ip4_destination_matches_route
-               (im, address, x, ia->address_length) ||
-               ip4_destination_matches_route (im,
-                                              x,
-                                              address,
-                                              address_length))
-             return
-               clib_error_create
-               ("failed to add %U which conflicts with %U for interface %U",
-                format_ip4_address_and_length, address,
-                address_length,
-                format_ip4_address_and_length, x,
-                ia->address_length,
-                format_vnet_sw_if_index_name, vnm,
-                sw_if_index);
-         }));
+      vnet_sw_interface_t *sif;
+
+      pool_foreach(sif, vnm->interface_main.sw_interfaces,
+      ({
+          if (im->fib_index_by_sw_if_index[sw_if_index] ==
+              im->fib_index_by_sw_if_index[sif->sw_if_index])
+            {
+              foreach_ip_interface_address
+                (&im->lookup_main, ia, sif->sw_if_index,
+                 0 /* honor unnumbered */ ,
+                 ({
+                   ip4_address_t * x =
+                     ip_interface_address_get_address
+                     (&im->lookup_main, ia);
+                   if (ip4_destination_matches_route
+                       (im, address, x, ia->address_length) ||
+                       ip4_destination_matches_route (im,
+                                                      x,
+                                                      address,
+                                                      address_length))
+                     {
+                       vnm->api_errno = VNET_API_ERROR_DUPLICATE_IF_ADDRESS;
+
+                       return
+                         clib_error_create
+                         ("failed to add %U which conflicts with %U for interface %U",
+                          format_ip4_address_and_length, address,
+                          address_length,
+                          format_ip4_address_and_length, x,
+                          ia->address_length,
+                          format_vnet_sw_if_index_name, vnm,
+                          sif->sw_if_index);
+                     }
+                 }));
+            }
+      }));
     }
   /* *INDENT-ON* */
 
@@ -1940,11 +1953,21 @@ typedef enum
   IP4_REWRITE_NEXT_ICMP_ERROR,
 } ip4_rewrite_next_t;
 
+/**
+ * This bits of an IPv4 address to mask to construct a multicast
+ * MAC address
+ */
+#if CLIB_ARCH_IS_BIG_ENDIAN
+#define IP4_MCAST_ADDR_MASK 0x007fffff
+#else
+#define IP4_MCAST_ADDR_MASK 0xffff7f00
+#endif
+
 always_inline void
-ip4_mtu_check (vlib_buffer_t * b, u16 buffer_packet_bytes,
+ip4_mtu_check (vlib_buffer_t * b, u16 packet_len,
               u16 adj_packet_bytes, bool df, u32 * next, u32 * error)
 {
-  if (buffer_packet_bytes > adj_packet_bytes)
+  if (packet_len > adj_packet_bytes)
     {
       *error = IP4_ERROR_MTU_EXCEEDED;
       if (df)
@@ -2123,21 +2146,17 @@ ip4_rewrite_inline (vlib_main_t * vm,
          vnet_buffer (p1)->ip.save_rewrite_length = rw_len1;
 
          /* Check MTU of outgoing interface. */
-         ip4_mtu_check (p0, vlib_buffer_length_in_chain (vm, p0),
+         ip4_mtu_check (p0, clib_net_to_host_u16 (ip0->length),
                         adj0[0].rewrite_header.max_l3_packet_bytes,
                         ip0->flags_and_fragment_offset &
                         clib_host_to_net_u16 (IP4_HEADER_FLAG_DONT_FRAGMENT),
                         &next0, &error0);
-         ip4_mtu_check (p1, vlib_buffer_length_in_chain (vm, p1),
+         ip4_mtu_check (p1, clib_net_to_host_u16 (ip1->length),
                         adj1[0].rewrite_header.max_l3_packet_bytes,
                         ip1->flags_and_fragment_offset &
                         clib_host_to_net_u16 (IP4_HEADER_FLAG_DONT_FRAGMENT),
                         &next1, &error1);
 
-         /* Guess we are only writing on simple Ethernet header. */
-         vnet_rewrite_two_headers (adj0[0], adj1[0],
-                                   ip0, ip1, sizeof (ethernet_header_t));
-
          if (is_mcast)
            {
              error0 = ((adj0[0].rewrite_header.sw_if_index ==
@@ -2160,17 +2179,10 @@ ip4_rewrite_inline (vlib_main_t * vm,
              tx_sw_if_index0 = adj0[0].rewrite_header.sw_if_index;
              vnet_buffer (p0)->sw_if_index[VLIB_TX] = tx_sw_if_index0;
 
-             if (is_midchain)
-               {
-                 adj0->sub_type.midchain.fixup_func
-                   (vm, adj0, p0, adj0->sub_type.midchain.fixup_data);
-               }
-
              if (PREDICT_FALSE
                  (adj0[0].rewrite_header.flags & VNET_REWRITE_HAS_FEATURES))
                vnet_feature_arc_start (lm->output_feature_arc_index,
                                        tx_sw_if_index0, &next0, p0);
-
            }
          if (PREDICT_TRUE (error1 == IP4_ERROR_NONE))
            {
@@ -2181,18 +2193,16 @@ ip4_rewrite_inline (vlib_main_t * vm,
              tx_sw_if_index1 = adj1[0].rewrite_header.sw_if_index;
              vnet_buffer (p1)->sw_if_index[VLIB_TX] = tx_sw_if_index1;
 
-             if (is_midchain)
-               {
-                 adj1->sub_type.midchain.fixup_func
-                   (vm, adj1, p1, adj0->sub_type.midchain.fixup_data);
-               }
-
              if (PREDICT_FALSE
                  (adj1[0].rewrite_header.flags & VNET_REWRITE_HAS_FEATURES))
                vnet_feature_arc_start (lm->output_feature_arc_index,
                                        tx_sw_if_index1, &next1, p1);
            }
 
+         /* Guess we are only writing on simple Ethernet header. */
+         vnet_rewrite_two_headers (adj0[0], adj1[0],
+                                   ip0, ip1, sizeof (ethernet_header_t));
+
          /*
           * Bump the per-adjacency counters
           */
@@ -2211,13 +2221,28 @@ ip4_rewrite_inline (vlib_main_t * vm,
                 vlib_buffer_length_in_chain (vm, p1) + rw_len1);
            }
 
+         if (is_midchain)
+           {
+             adj0->sub_type.midchain.fixup_func
+               (vm, adj0, p0, adj0->sub_type.midchain.fixup_data);
+             adj1->sub_type.midchain.fixup_func
+               (vm, adj1, p1, adj0->sub_type.midchain.fixup_data);
+           }
          if (is_mcast)
            {
              /*
               * copy bytes from the IP address into the MAC rewrite
               */
-             vnet_fixup_one_header (adj0[0], &ip0->dst_address, ip0);
-             vnet_fixup_one_header (adj1[0], &ip1->dst_address, ip1);
+             vnet_ip_mcast_fixup_header (IP4_MCAST_ADDR_MASK,
+                                         adj0->
+                                         rewrite_header.dst_mcast_offset,
+                                         &ip0->dst_address.as_u32,
+                                         (u8 *) ip0);
+             vnet_ip_mcast_fixup_header (IP4_MCAST_ADDR_MASK,
+                                         adj0->
+                                         rewrite_header.dst_mcast_offset,
+                                         &ip1->dst_address.as_u32,
+                                         (u8 *) ip1);
            }
 
          vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
@@ -2291,13 +2316,16 @@ ip4_rewrite_inline (vlib_main_t * vm,
 
          /* Guess we are only writing on simple Ethernet header. */
          vnet_rewrite_one_header (adj0[0], ip0, sizeof (ethernet_header_t));
-
          if (is_mcast)
            {
              /*
               * copy bytes from the IP address into the MAC rewrite
               */
-             vnet_fixup_one_header (adj0[0], &ip0->dst_address, ip0);
+             vnet_ip_mcast_fixup_header (IP4_MCAST_ADDR_MASK,
+                                         adj0->
+                                         rewrite_header.dst_mcast_offset,
+                                         &ip0->dst_address.as_u32,
+                                         (u8 *) ip0);
            }
 
          /* Update packet buffer attributes/set output interface. */
@@ -2311,7 +2339,7 @@ ip4_rewrite_inline (vlib_main_t * vm,
               vlib_buffer_length_in_chain (vm, p0) + rw_len0);
 
          /* Check MTU of outgoing interface. */
-         ip4_mtu_check (p0, vlib_buffer_length_in_chain (vm, p0),
+         ip4_mtu_check (p0, clib_net_to_host_u16 (ip0->length),
                         adj0[0].rewrite_header.max_l3_packet_bytes,
                         ip0->flags_and_fragment_offset &
                         clib_host_to_net_u16 (IP4_HEADER_FLAG_DONT_FRAGMENT),