ipip: Tunnel flags controlling copying data to/from payload/encap
[vpp.git] / src / vnet / ipip / ipip.c
index 82c961c..15f453a 100644 (file)
@@ -20,6 +20,7 @@
 #include <vnet/ipip/ipip.h>
 #include <vnet/vnet.h>
 #include <vnet/adj/adj_nbr.h>
+#include <vnet/adj/adj_midchain.h>
 #include <vnet/fib/ip4_fib.h>
 #include <vnet/fib/ip6_fib.h>
 #include <vnet/ip/format.h>
@@ -74,6 +75,10 @@ ipip_build_rewrite (vnet_main_t * vnm, u32 sw_if_index,
       ip4->src_address.as_u32 = t->tunnel_src.ip4.as_u32;
       ip4->dst_address.as_u32 = t->tunnel_dst.ip4.as_u32;
       ip4->checksum = ip4_header_checksum (ip4);
+      if (!(t->flags & IPIP_TUNNEL_FLAG_ENCAP_COPY_DSCP))
+       ip4_header_set_dscp (ip4, t->dscp);
+      if (t->flags & IPIP_TUNNEL_FLAG_ENCAP_SET_DF)
+       ip4_header_set_df (ip4);
       break;
 
     case IPIP_TRANSPORT_IP6:
@@ -87,7 +92,10 @@ ipip_build_rewrite (vnet_main_t * vnm, u32 sw_if_index,
       ip6->src_address.as_u64[1] = t->tunnel_src.ip6.as_u64[1];
       ip6->dst_address.as_u64[0] = t->tunnel_dst.ip6.as_u64[0];
       ip6->dst_address.as_u64[1] = t->tunnel_dst.ip6.as_u64[1];
+      if (!(t->flags & IPIP_TUNNEL_FLAG_ENCAP_COPY_DSCP))
+       ip6_set_dscp_network_order (ip6, t->dscp);
       break;
+
     default:
       /* pass through */
       ;
@@ -100,11 +108,39 @@ ipip4_fixup (vlib_main_t * vm, ip_adjacency_t * adj, vlib_buffer_t * b,
             const void *data)
 {
   ip4_header_t *ip4;
+  const ipip_tunnel_t *t = data;
 
   ip4 = vlib_buffer_get_current (b);
   ip4->length = clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b));
-  ip4->protocol =
-    adj->ia_link == VNET_LINK_IP6 ? IP_PROTOCOL_IPV6 : IP_PROTOCOL_IP_IN_IP;
+  switch (adj->ia_link)
+    {
+    case VNET_LINK_IP6:
+      ip4->protocol = IP_PROTOCOL_IPV6;
+      if (t->flags & IPIP_TUNNEL_FLAG_ENCAP_COPY_DSCP)
+       ip4_header_set_dscp (ip4,
+                            ip6_dscp_network_order ((ip6_header_t *) (ip4 +
+                                                                      1)));
+      if (t->flags & IPIP_TUNNEL_FLAG_ENCAP_COPY_ECN)
+       ip4_header_set_ecn (ip4,
+                           ip6_ecn_network_order ((ip6_header_t *) (ip4 +
+                                                                    1)));
+      break;
+
+    case VNET_LINK_IP4:
+      ip4->protocol = IP_PROTOCOL_IP_IN_IP;
+      if (t->flags & IPIP_TUNNEL_FLAG_ENCAP_COPY_DSCP)
+       ip4_header_set_dscp (ip4, ip4_header_get_dscp (ip4 + 1));
+      if (t->flags & IPIP_TUNNEL_FLAG_ENCAP_COPY_ECN)
+       ip4_header_set_ecn (ip4, ip4_header_get_ecn (ip4 + 1));
+      if ((t->flags & IPIP_TUNNEL_FLAG_ENCAP_COPY_DF) &&
+         ip4_header_get_df (ip4 + 1))
+       ip4_header_set_df (ip4);
+      break;
+
+    default:
+      break;
+    }
+
   ip4->checksum = ip4_header_checksum (ip4);
 }
 
@@ -113,13 +149,39 @@ ipip6_fixup (vlib_main_t * vm, ip_adjacency_t * adj, vlib_buffer_t * b,
             const void *data)
 {
   ip6_header_t *ip6;
+  const ipip_tunnel_t *t = data;
+
+  /* Must set locally originated otherwise we're not allowed to
+     fragment the packet later */
+  b->flags |= VNET_BUFFER_F_LOCALLY_ORIGINATED;
 
   ip6 = vlib_buffer_get_current (b);
   ip6->payload_length =
     clib_host_to_net_u16 (vlib_buffer_length_in_chain (vm, b) -
                          sizeof (*ip6));
-  ip6->protocol =
-    adj->ia_link == VNET_LINK_IP6 ? IP_PROTOCOL_IPV6 : IP_PROTOCOL_IP_IN_IP;
+  switch (adj->ia_link)
+    {
+    case VNET_LINK_IP6:
+      ip6->protocol = IP_PROTOCOL_IPV6;
+      if (t->flags & IPIP_TUNNEL_FLAG_ENCAP_COPY_DSCP)
+       ip6_set_dscp_network_order (ip6, ip6_dscp_network_order (ip6 + 1));
+      if (t->flags & IPIP_TUNNEL_FLAG_ENCAP_COPY_ECN)
+       ip6_set_ecn_network_order (ip6, ip6_ecn_network_order (ip6 + 1));
+      break;
+
+    case VNET_LINK_IP4:
+      ip6->protocol = IP_PROTOCOL_IP_IN_IP;
+      if (t->flags & IPIP_TUNNEL_FLAG_ENCAP_COPY_DSCP)
+       ip6_set_dscp_network_order
+         (ip6, ip4_header_get_dscp ((ip4_header_t *) (ip6 + 1)));
+      if (t->flags & IPIP_TUNNEL_FLAG_ENCAP_COPY_ECN)
+       ip6_set_ecn_network_order
+         (ip6, ip4_header_get_ecn ((ip4_header_t *) (ip6 + 1)));
+      break;
+
+    default:
+      break;
+    }
 }
 
 static void
@@ -139,47 +201,22 @@ ipip_tunnel_stack (adj_index_t ai)
   if ((vnet_hw_interface_get_flags (vnet_get_main (), t->hw_if_index) &
        VNET_HW_INTERFACE_FLAG_LINK_UP) == 0)
     {
-      adj_nbr_midchain_unstack (ai);
-      return;
+      adj_midchain_delegate_unstack (ai);
     }
-
-  dpo_id_t tmp = DPO_INVALID;
-  fib_forward_chain_type_t fib_fwd =
-    t->transport ==
-    IPIP_TRANSPORT_IP6 ? FIB_FORW_CHAIN_TYPE_UNICAST_IP6 :
-    FIB_FORW_CHAIN_TYPE_UNICAST_IP4;
-
-  fib_entry_contribute_forwarding (t->p2p.fib_entry_index, fib_fwd, &tmp);
-  if (DPO_LOAD_BALANCE == tmp.dpoi_type)
+  else
     {
-      /*
-       * post IPIP rewrite we will load-balance. However, the IPIP encap
-       * is always the same for this adjacency/tunnel and hence the IP/IPIP
-       * src,dst hash is always the same result too. So we do that hash now and
-       * stack on the choice.
-       * If the choice is an incomplete adj then we will need a poke when
-       * it becomes complete. This happens since the adj update walk propagates
-       * as far a recursive paths.
-       */
-      const dpo_id_t *choice;
-      load_balance_t *lb;
-      int hash;
-
-      lb = load_balance_get (tmp.dpoi_index);
-
-      if (fib_fwd == FIB_FORW_CHAIN_TYPE_UNICAST_IP4)
-       hash = ip4_compute_flow_hash ((ip4_header_t *) adj_get_rewrite (ai),
-                                     lb->lb_hash_config);
-      else
-       hash = ip6_compute_flow_hash ((ip6_header_t *) adj_get_rewrite (ai),
-                                     lb->lb_hash_config);
-      choice =
-       load_balance_get_bucket_i (lb, hash & lb->lb_n_buckets_minus_1);
-      dpo_copy (&tmp, choice);
+      /* *INDENT-OFF* */
+      fib_prefix_t dst = {
+        .fp_len = t->transport == IPIP_TRANSPORT_IP6 ? 128 : 32,
+        .fp_proto = (t->transport == IPIP_TRANSPORT_IP6 ?
+                     FIB_PROTOCOL_IP6 :
+                     FIB_PROTOCOL_IP4),
+        .fp_addr = t->tunnel_dst
+      };
+      /* *INDENT-ON* */
+
+      adj_midchain_delegate_stack (ai, t->fib_index, &dst);
     }
-
-  adj_nbr_midchain_stack (ai, &tmp);
-  dpo_reset (&tmp);
 }
 
 static adj_walk_rc_t
@@ -207,27 +244,41 @@ ipip_tunnel_restack (ipip_tunnel_t * gt)
 void
 ipip_update_adj (vnet_main_t * vnm, u32 sw_if_index, adj_index_t ai)
 {
-  ipip_tunnel_t *t;
   adj_midchain_fixup_t f;
+  ipip_tunnel_t *t;
+  adj_flags_t af;
 
   t = ipip_tunnel_db_find_by_sw_if_index (sw_if_index);
   if (!t)
     return;
 
   f = t->transport == IPIP_TRANSPORT_IP6 ? ipip6_fixup : ipip4_fixup;
-
-  adj_nbr_midchain_update_rewrite (ai, f, NULL,
-                                  (VNET_LINK_ETHERNET ==
-                                   adj_get_link_type (ai) ?
-                                   ADJ_FLAG_MIDCHAIN_NO_COUNT :
-                                   ADJ_FLAG_NONE), ipip_build_rewrite (vnm,
-                                                                       sw_if_index,
-                                                                       adj_get_link_type
-                                                                       (ai),
-                                                                       NULL));
+  af = ADJ_FLAG_MIDCHAIN_IP_STACK;
+  if (VNET_LINK_ETHERNET == adj_get_link_type (ai))
+    af |= ADJ_FLAG_MIDCHAIN_NO_COUNT;
+
+  adj_nbr_midchain_update_rewrite (ai, f, t, af,
+                                  ipip_build_rewrite (vnm,
+                                                      sw_if_index,
+                                                      adj_get_link_type
+                                                      (ai), NULL));
   ipip_tunnel_stack (ai);
 }
 
+u8 *
+format_ipip_tunnel_flags (u8 * s, va_list * args)
+{
+  ipip_tunnel_flags_t f = va_arg (*args, int);
+
+  if (f == IPIP_TUNNEL_FLAG_NONE)
+    return (format (s, "none"));
+
+#define _(a,b,c) if (f & IPIP_TUNNEL_FLAG_##a) s = format(s, "%s ", b);
+  forech_ipip_tunnel_flag
+#undef _
+    return (s);
+}
+
 static u8 *
 format_ipip_tunnel_name (u8 * s, va_list * args)
 {
@@ -275,6 +326,23 @@ ipip_interface_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
   return /* no error */ 0;
 }
 
+static int
+ipip_tunnel_desc (u32 sw_if_index,
+                 ip46_address_t * src, ip46_address_t * dst, u8 * is_l2)
+{
+  ipip_tunnel_t *t;
+
+  t = ipip_tunnel_db_find_by_sw_if_index (sw_if_index);
+  if (!t)
+    return -1;
+
+  *src = t->tunnel_src;
+  *dst = t->tunnel_dst;
+  *is_l2 = 0;
+
+  return (0);
+}
+
 /* *INDENT-OFF* */
 VNET_DEVICE_CLASS(ipip_device_class) = {
     .name = "IPIP tunnel device",
@@ -282,6 +350,7 @@ VNET_DEVICE_CLASS(ipip_device_class) = {
     .format_device = format_ipip_device,
     .format_tx_trace = format_ipip_tx_trace,
     .admin_up_down_function = ipip_interface_admin_up_down,
+    .ip_tun_desc = ipip_tunnel_desc,
 #ifdef SOON
     .clear counter = 0;
 #endif
@@ -313,7 +382,7 @@ ipip_tunnel_t *
 ipip_tunnel_db_find_by_sw_if_index (u32 sw_if_index)
 {
   ipip_main_t *gm = &ipip_main;
-  if (vec_len (gm->tunnel_index_by_sw_if_index) < sw_if_index)
+  if (vec_len (gm->tunnel_index_by_sw_if_index) <= sw_if_index)
     return NULL;
   u32 ti = gm->tunnel_index_by_sw_if_index[sw_if_index];
   if (ti == ~0)
@@ -341,86 +410,11 @@ ipip_tunnel_db_remove (ipip_tunnel_t * t)
   t->key = NULL;
 }
 
-static ipip_tunnel_t *
-ipip_tunnel_from_fib_node (fib_node_t * node)
-{
-  ipip_main_t *gm = &ipip_main;
-  ASSERT (gm->fib_node_type == node->fn_type);
-  return ((ipip_tunnel_t *) (((char *) node) -
-                            offsetof (ipip_tunnel_t, p2p.node)));
-}
-
-static fib_node_back_walk_rc_t
-ipip_tunnel_back_walk (fib_node_t * node, fib_node_back_walk_ctx_t * ctx)
-{
-  ipip_tunnel_restack (ipip_tunnel_from_fib_node (node));
-
-  return (FIB_NODE_BACK_WALK_CONTINUE);
-}
-
-static fib_node_t *
-ipip_tunnel_fib_node_get (fib_node_index_t index)
-{
-  ipip_tunnel_t *gt;
-  ipip_main_t *gm;
-
-  gm = &ipip_main;
-  gt = pool_elt_at_index (gm->tunnels, index);
-
-  return (&gt->p2p.node);
-}
-
-static void
-ipip_tunnel_last_lock_gone (fib_node_t * node)
-{
-  /*
-   * The MPLS IPIP tunnel is a root of the graph. As such
-   * it never has children and thus is never locked.
-   */
-  ASSERT (0);
-}
-
-/*
- * Virtual function table registered by IPIP tunnels
- * for participation in the FIB object graph.
- */
-const static fib_node_vft_t ipip_vft = {
-  .fnv_get = ipip_tunnel_fib_node_get,
-  .fnv_last_lock = ipip_tunnel_last_lock_gone,
-  .fnv_back_walk = ipip_tunnel_back_walk,
-};
-
-static void
-ipip_fib_add (ipip_tunnel_t * t)
-{
-  ipip_main_t *gm = &ipip_main;
-  fib_prefix_t dst = {.fp_len = t->transport == IPIP_TRANSPORT_IP6 ? 128 : 32,
-    .fp_proto =
-      t->transport ==
-      IPIP_TRANSPORT_IP6 ? FIB_PROTOCOL_IP6 : FIB_PROTOCOL_IP4,
-    .fp_addr = t->tunnel_dst
-  };
-
-  t->p2p.fib_entry_index =
-    fib_table_entry_special_add (t->fib_index, &dst, FIB_SOURCE_RR,
-                                FIB_ENTRY_FLAG_NONE);
-  t->p2p.sibling_index =
-    fib_entry_child_add (t->p2p.fib_entry_index, gm->fib_node_type,
-                        t->dev_instance);
-}
-
-static void
-ipip_fib_delete (ipip_tunnel_t * t)
-{
-  fib_entry_child_remove (t->p2p.fib_entry_index, t->p2p.sibling_index);
-  fib_table_entry_delete_index (t->p2p.fib_entry_index, FIB_SOURCE_RR);
-  fib_node_deinit (&t->p2p.node);
-}
-
 int
 ipip_add_tunnel (ipip_transport_t transport,
                 u32 instance, ip46_address_t * src, ip46_address_t * dst,
-                u32 fib_index, u32 * sw_if_indexp)
+                u32 fib_index, ipip_tunnel_flags_t flags,
+                ip_dscp_t dscp, u32 * sw_if_indexp)
 {
   ipip_main_t *gm = &ipip_main;
   vnet_main_t *vnm = gm->vnet_main;
@@ -439,7 +433,7 @@ ipip_add_tunnel (ipip_transport_t transport,
     return VNET_API_ERROR_IF_ALREADY_EXISTS;
 
   pool_get_aligned (gm->tunnels, t, CLIB_CACHE_LINE_BYTES);
-  memset (t, 0, sizeof (*t));
+  clib_memset (t, 0, sizeof (*t));
 
   /* Reconcile the real dev_instance and a possible requested instance */
   u32 t_idx = t - gm->tunnels; /* tunnel index (or instance) */
@@ -455,7 +449,6 @@ ipip_add_tunnel (ipip_transport_t transport,
 
   t->dev_instance = t_idx;     /* actual */
   t->user_instance = u_idx;    /* name */
-  fib_node_init (&t->p2p.node, gm->fib_node_type);
 
   hw_if_index = vnet_register_interface (vnm, ipip_device_class.index, t_idx,
                                         ipip_hw_interface_class.index,
@@ -467,8 +460,10 @@ ipip_add_tunnel (ipip_transport_t transport,
   t->hw_if_index = hw_if_index;
   t->fib_index = fib_index;
   t->sw_if_index = sw_if_index;
-
+  t->dscp = dscp;
+  t->flags = flags;
   t->transport = transport;
+
   vec_validate_init_empty (gm->tunnel_index_by_sw_if_index, sw_if_index, ~0);
   gm->tunnel_index_by_sw_if_index[sw_if_index] = t_idx;
 
@@ -483,22 +478,14 @@ ipip_add_tunnel (ipip_transport_t transport,
       hi->min_packet_bytes = 64 + sizeof (ip6_header_t);
     }
 
-  hi->per_packet_overhead_bytes = /* preamble */ 8 + /* inter frame gap */ 12;
-
   /* Standard default ipip MTU. */
-  hi->max_l3_packet_bytes[VLIB_RX] = hi->max_l3_packet_bytes[VLIB_TX] = 9000;
+  vnet_sw_interface_set_mtu (vnm, sw_if_index, 9000);
 
   t->tunnel_src = *src;
   t->tunnel_dst = *dst;
 
   ipip_tunnel_db_add (t, &key);
 
-  /*
-   * 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
-   */
-  ipip_fib_add (t);
   if (sw_if_indexp)
     *sw_if_indexp = sw_if_index;
 
@@ -532,7 +519,6 @@ ipip_del_tunnel (u32 sw_if_index)
   vnet_sw_interface_set_flags (vnm, sw_if_index, 0 /* down */ );
   gm->tunnel_index_by_sw_if_index[sw_if_index] = ~0;
   vnet_delete_hw_interface (vnm, t->hw_if_index);
-  ipip_fib_delete (t);
   hash_unset (gm->instance_used, t->user_instance);
   ipip_tunnel_db_remove (t);
   pool_put (gm->tunnels, t);
@@ -545,12 +531,11 @@ ipip_init (vlib_main_t * vm)
 {
   ipip_main_t *gm = &ipip_main;
 
-  memset (gm, 0, sizeof (gm[0]));
+  clib_memset (gm, 0, sizeof (gm[0]));
   gm->vlib_main = vm;
   gm->vnet_main = vnet_get_main ();
   gm->tunnel_by_key =
     hash_create_mem (0, sizeof (ipip_tunnel_key_t), sizeof (uword));
-  gm->fib_node_type = fib_node_register_new_type (&ipip_vft);
 
   return 0;
 }