flow dpdk: introduce IP in IP support for flow 62/38362/5
authorXinyao Cai <xinyao.cai@intel.com>
Tue, 28 Feb 2023 06:44:58 +0000 (14:44 +0800)
committerDamjan Marion <dmarion@0xa5.net>
Mon, 6 Mar 2023 16:46:17 +0000 (16:46 +0000)
This patch introduces IP in IP packet support for flow cli and dpdk plugin.
Specifically, the following IP in IP packet types are supported:
MAC-IPv4-IPv4-TCP/UDP/None,
MAC-IPv4-IPv6-TCP/UDP/None,
MAC-IPv6-IPv4-TCP/UDP/None,
MAC-IPv6-IPv6-TCP/UDP/None,

IP in IP flow rules can be created by using the following new keywords in vppctl:
in-src-ip, in-dst-ip        : to provide information for inner IPv4 header
in-ip6-src-ip, in-ip6-dst-ip: to provide information for inner IPv6 header
in-proto                    : to specify inner transport layer protocol type (TCP or UDP)
in-src-port, in-dst-port    : to provide information for inner TCP/UDP header

An example to create flow rule for MAC-IPv6-IPv6-TCP:
test flow add index 0 ip6-src-ip any ip6-dst-ip any in-ip6-src-ip any in-ip6-dst-ip any in-proto tcp in-src-port 1234 in-dst-port any rss function default

Another example to create flow rule for MAC-IPv6-IPv6:
test flow add index 0 ip6-src-ip any in-ip6-src-ip any rss function default

Type: feature

Signed-off-by: Xinyao Cai <xinyao.cai@intel.com>
Change-Id: I6a1ca36d47eb65b9cb5a4b8d874b2a7f017c35cd

src/plugins/dpdk/device/flow.c
src/vnet/flow/flow.h
src/vnet/flow/flow_cli.c

index be453bb..635f6f3 100644 (file)
 #define FLOW_IS_ETHERNET_CLASS(f) \
   (f->type == VNET_FLOW_TYPE_ETHERNET)
 
-#define FLOW_IS_IPV4_CLASS(f) \
-  ((f->type == VNET_FLOW_TYPE_IP4) || \
-    (f->type == VNET_FLOW_TYPE_IP4_N_TUPLE) || \
-    (f->type == VNET_FLOW_TYPE_IP4_N_TUPLE_TAGGED) || \
-    (f->type == VNET_FLOW_TYPE_IP4_VXLAN) || \
-    (f->type == VNET_FLOW_TYPE_IP4_GTPC) || \
-    (f->type == VNET_FLOW_TYPE_IP4_GTPU) || \
-    (f->type == VNET_FLOW_TYPE_IP4_L2TPV3OIP) || \
-    (f->type == VNET_FLOW_TYPE_IP4_IPSEC_ESP) || \
-    (f->type == VNET_FLOW_TYPE_IP4_IPSEC_AH))
-
-#define FLOW_IS_IPV6_CLASS(f) \
-  ((f->type == VNET_FLOW_TYPE_IP6) || \
-    (f->type == VNET_FLOW_TYPE_IP6_N_TUPLE) || \
-    (f->type == VNET_FLOW_TYPE_IP6_N_TUPLE_TAGGED) || \
-    (f->type == VNET_FLOW_TYPE_IP6_VXLAN))
+#define FLOW_IS_IPV4_CLASS(f)                                                 \
+  ((f->type == VNET_FLOW_TYPE_IP4) ||                                         \
+   (f->type == VNET_FLOW_TYPE_IP4_N_TUPLE) ||                                 \
+   (f->type == VNET_FLOW_TYPE_IP4_N_TUPLE_TAGGED) ||                          \
+   (f->type == VNET_FLOW_TYPE_IP4_VXLAN) ||                                   \
+   (f->type == VNET_FLOW_TYPE_IP4_GTPC) ||                                    \
+   (f->type == VNET_FLOW_TYPE_IP4_GTPU) ||                                    \
+   (f->type == VNET_FLOW_TYPE_IP4_L2TPV3OIP) ||                               \
+   (f->type == VNET_FLOW_TYPE_IP4_IPSEC_ESP) ||                               \
+   (f->type == VNET_FLOW_TYPE_IP4_IPSEC_AH) ||                                \
+   (f->type == VNET_FLOW_TYPE_IP4_IP4) ||                                     \
+   (f->type == VNET_FLOW_TYPE_IP4_IP6) ||                                     \
+   (f->type == VNET_FLOW_TYPE_IP4_IP4_N_TUPLE) ||                             \
+   (f->type == VNET_FLOW_TYPE_IP4_IP6_N_TUPLE))
+
+#define FLOW_IS_IPV6_CLASS(f)                                                 \
+  ((f->type == VNET_FLOW_TYPE_IP6) ||                                         \
+   (f->type == VNET_FLOW_TYPE_IP6_N_TUPLE) ||                                 \
+   (f->type == VNET_FLOW_TYPE_IP6_N_TUPLE_TAGGED) ||                          \
+   (f->type == VNET_FLOW_TYPE_IP6_VXLAN) ||                                   \
+   (f->type == VNET_FLOW_TYPE_IP6_IP4) ||                                     \
+   (f->type == VNET_FLOW_TYPE_IP6_IP6) ||                                     \
+   (f->type == VNET_FLOW_TYPE_IP6_IP4_N_TUPLE) ||                             \
+   (f->type == VNET_FLOW_TYPE_IP6_IP6_N_TUPLE))
 
 /* check if flow is VLAN sensitive */
 #define FLOW_HAS_VLAN_TAG(f) \
     (f->type == VNET_FLOW_TYPE_IP4_GTPC) || \
     (f->type == VNET_FLOW_TYPE_IP4_GTPU))
 
+/* check if flow has a inner TCP/UDP header */
+#define FLOW_HAS_INNER_N_TUPLE(f)                                             \
+  ((f->type == VNET_FLOW_TYPE_IP4_IP4_N_TUPLE) ||                             \
+   (f->type == VNET_FLOW_TYPE_IP4_IP6_N_TUPLE) ||                             \
+   (f->type == VNET_FLOW_TYPE_IP6_IP4_N_TUPLE) ||                             \
+   (f->type == VNET_FLOW_TYPE_IP6_IP6_N_TUPLE))
+
 /* constant structs */
 static const struct rte_flow_attr ingress = {.ingress = 1 };
 
@@ -153,10 +168,10 @@ static int
 dpdk_flow_add (dpdk_device_t * xd, vnet_flow_t * f, dpdk_flow_entry_t * fe)
 {
   struct rte_flow_item_eth eth[2] = { };
-  struct rte_flow_item_ipv4 ip4[2] = { };
-  struct rte_flow_item_ipv6 ip6[2] = { };
-  struct rte_flow_item_udp udp[2] = { };
-  struct rte_flow_item_tcp tcp[2] = { };
+  struct rte_flow_item_ipv4 ip4[2] = {}, in_ip4[2] = {};
+  struct rte_flow_item_ipv6 ip6[2] = {}, in_ip6[2] = {};
+  struct rte_flow_item_udp udp[2] = {}, in_UDP[2] = {};
+  struct rte_flow_item_tcp tcp[2] = {}, in_TCP[2] = {};
   struct rte_flow_item_gtp gtp[2] = { };
   struct rte_flow_item_l2tpv3oip l2tp[2] = { };
   struct rte_flow_item_esp esp[2] = { };
@@ -472,12 +487,118 @@ dpdk_flow_add (dpdk_device_t * xd, vnet_flow_t * f, dpdk_flow_entry_t * fe)
          item->mask = raw + 1;
        }
       break;
+    case IP_PROTOCOL_IPV6:
+      item->type = RTE_FLOW_ITEM_TYPE_IPV6;
+
+#define fill_inner_ip6_with_outer_ipv(OUTER_IP_VER)                           \
+  if (f->type == VNET_FLOW_TYPE_IP##OUTER_IP_VER##_IP6 ||                     \
+      f->type == VNET_FLOW_TYPE_IP##OUTER_IP_VER##_IP6_N_TUPLE)               \
+    {                                                                         \
+      vnet_flow_ip##OUTER_IP_VER##_ip6_t *ptr = &f->ip##OUTER_IP_VER##_ip6;   \
+      if ((ptr->in_src_addr.mask.as_u64[0] == 0) &&                           \
+         (ptr->in_src_addr.mask.as_u64[1] == 0) &&                           \
+         (ptr->in_dst_addr.mask.as_u64[0] == 0) &&                           \
+         (ptr->in_dst_addr.mask.as_u64[1] == 0) && (!ptr->in_protocol.mask)) \
+       {                                                                     \
+         item->spec = NULL;                                                  \
+         item->mask = NULL;                                                  \
+       }                                                                     \
+      else                                                                    \
+       {                                                                     \
+         clib_memcpy (in_ip6[0].hdr.src_addr, &ptr->in_src_addr.addr,        \
+                      ARRAY_LEN (ptr->in_src_addr.addr.as_u8));              \
+         clib_memcpy (in_ip6[1].hdr.src_addr, &ptr->in_src_addr.mask,        \
+                      ARRAY_LEN (ptr->in_src_addr.mask.as_u8));              \
+         clib_memcpy (in_ip6[0].hdr.dst_addr, &ptr->in_dst_addr.addr,        \
+                      ARRAY_LEN (ptr->in_dst_addr.addr.as_u8));              \
+         clib_memcpy (in_ip6[1].hdr.dst_addr, &ptr->in_dst_addr.mask,        \
+                      ARRAY_LEN (ptr->in_dst_addr.mask.as_u8));              \
+         item->spec = in_ip6;                                                \
+         item->mask = in_ip6 + 1;                                            \
+       }                                                                     \
+    }
+      fill_inner_ip6_with_outer_ipv (6) fill_inner_ip6_with_outer_ipv (4)
+#undef fill_inner_ip6_with_outer_ipv
+       break;
+    case IP_PROTOCOL_IP_IN_IP:
+      item->type = RTE_FLOW_ITEM_TYPE_IPV4;
 
+#define fill_inner_ip4_with_outer_ipv(OUTER_IP_VER)                           \
+  if (f->type == VNET_FLOW_TYPE_IP##OUTER_IP_VER##_IP4 ||                     \
+      f->type == VNET_FLOW_TYPE_IP##OUTER_IP_VER##_IP4_N_TUPLE)               \
+    {                                                                         \
+      vnet_flow_ip##OUTER_IP_VER##_ip4_t *ptr = &f->ip##OUTER_IP_VER##_ip4;   \
+      if ((!ptr->in_src_addr.mask.as_u32) &&                                  \
+         (!ptr->in_dst_addr.mask.as_u32) && (!ptr->in_protocol.mask))        \
+       {                                                                     \
+         item->spec = NULL;                                                  \
+         item->mask = NULL;                                                  \
+       }                                                                     \
+      else                                                                    \
+       {                                                                     \
+         in_ip4[0].hdr.src_addr = ptr->in_src_addr.addr.as_u32;              \
+         in_ip4[1].hdr.src_addr = ptr->in_src_addr.mask.as_u32;              \
+         in_ip4[0].hdr.dst_addr = ptr->in_dst_addr.addr.as_u32;              \
+         in_ip4[1].hdr.dst_addr = ptr->in_dst_addr.mask.as_u32;              \
+         item->spec = in_ip4;                                                \
+         item->mask = in_ip4 + 1;                                            \
+       }                                                                     \
+    }
+      fill_inner_ip4_with_outer_ipv (6) fill_inner_ip4_with_outer_ipv (4)
+#undef fill_inner_ip4_with_outer_ipv
+       break;
     default:
       rv = VNET_FLOW_ERROR_NOT_SUPPORTED;
       goto done;
     }
 
+  if (FLOW_HAS_INNER_N_TUPLE (f))
+    {
+      vec_add2 (items, item, 1);
+
+#define fill_inner_n_tuple_of(proto)                                          \
+  item->type = RTE_FLOW_ITEM_TYPE_##proto;                                    \
+  if ((ptr->in_src_port.mask == 0) && (ptr->in_dst_port.mask == 0))           \
+    {                                                                         \
+      item->spec = NULL;                                                      \
+      item->mask = NULL;                                                      \
+    }                                                                         \
+  else                                                                        \
+    {                                                                         \
+      in_##proto[0].hdr.src_port =                                            \
+       clib_host_to_net_u16 (ptr->in_src_port.port);                         \
+      in_##proto[1].hdr.src_port =                                            \
+       clib_host_to_net_u16 (ptr->in_src_port.mask);                         \
+      in_##proto[0].hdr.dst_port =                                            \
+       clib_host_to_net_u16 (ptr->in_dst_port.port);                         \
+      in_##proto[1].hdr.dst_port =                                            \
+       clib_host_to_net_u16 (ptr->in_dst_port.mask);                         \
+      item->spec = in_##proto;                                                \
+      item->mask = in_##proto + 1;                                            \
+    }
+
+#define fill_inner_n_tuple(OUTER_IP_VER, INNER_IP_VER)                        \
+  if (f->type ==                                                              \
+      VNET_FLOW_TYPE_IP##OUTER_IP_VER##_IP##INNER_IP_VER##_N_TUPLE)           \
+    {                                                                         \
+      vnet_flow_ip##OUTER_IP_VER##_ip##INNER_IP_VER##_n_tuple_t *ptr =        \
+       &f->ip##OUTER_IP_VER##_ip##INNER_IP_VER##_n_tuple;                    \
+      switch (ptr->in_protocol.prot)                                          \
+       {                                                                     \
+       case IP_PROTOCOL_UDP:                                                 \
+         fill_inner_n_tuple_of (UDP) break;                                  \
+       case IP_PROTOCOL_TCP:                                                 \
+         fill_inner_n_tuple_of (TCP) break;                                  \
+       default:                                                              \
+         break;                                                              \
+       }                                                                     \
+    }
+      fill_inner_n_tuple (6, 4) fill_inner_n_tuple (4, 4)
+       fill_inner_n_tuple (6, 6) fill_inner_n_tuple (4, 6)
+#undef fill_inner_n_tuple
+#undef fill_inner_n_tuple_of
+    }
+
 pattern_end:
   if ((f->actions & VNET_FLOW_ACTION_RSS) &&
       (f->rss_types & (1ULL << VNET_FLOW_RSS_TYPES_ESP)))
@@ -692,6 +813,14 @@ dpdk_flow_ops_fn (vnet_main_t * vnm, vnet_flow_dev_op_t op, u32 dev_instance,
     case VNET_FLOW_TYPE_IP4_L2TPV3OIP:
     case VNET_FLOW_TYPE_IP4_IPSEC_ESP:
     case VNET_FLOW_TYPE_IP4_IPSEC_AH:
+    case VNET_FLOW_TYPE_IP4_IP4:
+    case VNET_FLOW_TYPE_IP4_IP4_N_TUPLE:
+    case VNET_FLOW_TYPE_IP4_IP6:
+    case VNET_FLOW_TYPE_IP4_IP6_N_TUPLE:
+    case VNET_FLOW_TYPE_IP6_IP4:
+    case VNET_FLOW_TYPE_IP6_IP4_N_TUPLE:
+    case VNET_FLOW_TYPE_IP6_IP6:
+    case VNET_FLOW_TYPE_IP6_IP6_N_TUPLE:
     case VNET_FLOW_TYPE_GENERIC:
       if ((rv = dpdk_flow_add (xd, flow, fe)))
        goto done;
index 194579b..9069bd1 100644 (file)
   _ (IP4_GTPC, ip4_gtpc, "ipv4-gtpc")                                         \
   _ (IP4_GTPU, ip4_gtpu, "ipv4-gtpu")                                         \
   /* generic flow */                                                          \
-  _ (GENERIC, generic, "generic")
+  _ (GENERIC, generic, "generic")                                             \
+  /* IP in IP */                                                              \
+  _ (IP6_IP6, ip6_ip6, "ipv6-ipv6")                                           \
+  _ (IP6_IP4, ip6_ip4, "ipv6-ipv4")                                           \
+  _ (IP4_IP6, ip4_ip6, "ipv4-ipv6")                                           \
+  _ (IP4_IP4, ip4_ip4, "ipv4-ipv4")                                           \
+  _ (IP6_IP6_N_TUPLE, ip6_ip6_n_tuple, "ipv6-ipv6-n-tuple")                   \
+  _ (IP6_IP4_N_TUPLE, ip6_ip4_n_tuple, "ipv6-ipv4-n-tuple")                   \
+  _ (IP4_IP6_N_TUPLE, ip4_ip6_n_tuple, "ipv4-ipv6-n-tuple")                   \
+  _ (IP4_IP4_N_TUPLE, ip4_ip4_n_tuple, "ipv4-ipv4-n-tuple")
 
 #define foreach_flow_entry_ethernet \
   _fe(ethernet_header_t, eth_hdr)
   foreach_flow_entry_ip4_n_tuple \
   _fe(u32, teid)
 
+#define foreach_flow_entry_ip6_ip6                                            \
+  foreach_flow_entry_ip6 _fe (ip6_address_and_mask_t, in_src_addr)            \
+    _fe (ip6_address_and_mask_t, in_dst_addr)                                 \
+      _fe (ip_prot_and_mask_t, in_protocol)
+
+#define foreach_flow_entry_ip6_ip6_n_tuple                                    \
+  foreach_flow_entry_ip6_ip6 _fe (ip_port_and_mask_t, in_src_port)            \
+    _fe (ip_port_and_mask_t, in_dst_port)
+
+#define foreach_flow_entry_ip6_ip4                                            \
+  foreach_flow_entry_ip6 _fe (ip4_address_and_mask_t, in_src_addr)            \
+    _fe (ip4_address_and_mask_t, in_dst_addr)                                 \
+      _fe (ip_prot_and_mask_t, in_protocol)
+
+#define foreach_flow_entry_ip6_ip4_n_tuple                                    \
+  foreach_flow_entry_ip6_ip4 _fe (ip_port_and_mask_t, in_src_port)            \
+    _fe (ip_port_and_mask_t, in_dst_port)
+
+#define foreach_flow_entry_ip4_ip6                                            \
+  foreach_flow_entry_ip4 _fe (ip6_address_and_mask_t, in_src_addr)            \
+    _fe (ip6_address_and_mask_t, in_dst_addr)                                 \
+      _fe (ip_prot_and_mask_t, in_protocol)
+
+#define foreach_flow_entry_ip4_ip6_n_tuple                                    \
+  foreach_flow_entry_ip4_ip6 _fe (ip_port_and_mask_t, in_src_port)            \
+    _fe (ip_port_and_mask_t, in_dst_port)
+
+#define foreach_flow_entry_ip4_ip4                                            \
+  foreach_flow_entry_ip4 _fe (ip4_address_and_mask_t, in_src_addr)            \
+    _fe (ip4_address_and_mask_t, in_dst_addr)                                 \
+      _fe (ip_prot_and_mask_t, in_protocol)
+
+#define foreach_flow_entry_ip4_ip4_n_tuple                                    \
+  foreach_flow_entry_ip4_ip4 _fe (ip_port_and_mask_t, in_src_port)            \
+    _fe (ip_port_and_mask_t, in_dst_port)
+
 #define foreach_flow_entry_generic _fe (generic_pattern_t, pattern)
 
 #define foreach_flow_action \
index 5f44a09..bf343b0 100644 (file)
@@ -366,15 +366,16 @@ test_flow (vlib_main_t * vm, unformat_input_t * input,
   u32 vni = 0;
   u32 queue_start = 0, queue_end = 0;
   vnet_flow_type_t type = VNET_FLOW_TYPE_UNKNOWN;
-  ip4_address_and_mask_t ip4s = { };
-  ip4_address_and_mask_t ip4d = { };
-  ip6_address_and_mask_t ip6s = { };
-  ip6_address_and_mask_t ip6d = { };
-  ip_port_and_mask_t sport = { };
-  ip_port_and_mask_t dport = { };
-  ip_prot_and_mask_t protocol = { };
+  ip4_address_and_mask_t ip4s = {}, in_ip4s = {};
+  ip4_address_and_mask_t ip4d = {}, in_ip4d = {};
+  ip6_address_and_mask_t ip6s = {}, in_ip6s = {};
+  ip6_address_and_mask_t ip6d = {}, in_ip6d = {};
+  ip_port_and_mask_t sport = {}, in_sport = {};
+  ip_port_and_mask_t dport = {}, in_dport = {};
+  ip_prot_and_mask_t protocol = {}, in_proto = {};
   u16 eth_type;
-  bool tcp_udp_port_set = false;
+  bool inner_ip4_set = false, inner_ip6_set = false;
+  bool tcp_udp_port_set = false, inner_port_set = false;
   bool gtpc_set = false;
   bool gtpu_set = false;
   bool vni_set = false;
@@ -415,12 +416,24 @@ test_flow (vlib_main_t * vm, unformat_input_t * input,
       else if (unformat (line_input, "dst-ip %U",
                         unformat_ip4_address_and_mask, &ip4d))
        flow_class = FLOW_IPV4_CLASS;
+      else if (unformat (line_input, "in-src-ip %U",
+                        unformat_ip4_address_and_mask, &in_ip4s))
+       inner_ip4_set = true;
+      else if (unformat (line_input, "in-dst-ip %U",
+                        unformat_ip4_address_and_mask, &in_ip4d))
+       inner_ip4_set = true;
       else if (unformat (line_input, "ip6-src-ip %U",
                         unformat_ip6_address_and_mask, &ip6s))
        flow_class = FLOW_IPV6_CLASS;
       else if (unformat (line_input, "ip6-dst-ip %U",
                         unformat_ip6_address_and_mask, &ip6d))
        flow_class = FLOW_IPV6_CLASS;
+      else if (unformat (line_input, "in-ip6-src-ip %U",
+                        unformat_ip6_address_and_mask, &in_ip6s))
+       inner_ip6_set = true;
+      else if (unformat (line_input, "in-ip6-dst-ip %U",
+                        unformat_ip6_address_and_mask, &in_ip6d))
+       inner_ip6_set = true;
       else if (unformat (line_input, "src-port %U", unformat_ip_port_and_mask,
                         &sport))
        tcp_udp_port_set = true;
@@ -432,6 +445,15 @@ test_flow (vlib_main_t * vm, unformat_input_t * input,
            (line_input, "proto %U", unformat_ip_protocol_and_mask,
             &protocol))
        ;
+      else if (unformat (line_input, "in-src-port %U",
+                        unformat_ip_port_and_mask, &in_sport))
+       inner_port_set = true;
+      else if (unformat (line_input, "in-dst-port %U",
+                        unformat_ip_port_and_mask, &in_dport))
+       inner_port_set = true;
+      else if (unformat (line_input, "in-proto %U",
+                        unformat_ip_protocol_and_mask, &in_proto))
+       ;
       else if (unformat (line_input, "gtpc teid %u", &teid))
        gtpc_set = true;
       else if (unformat (line_input, "gtpu teid %u", &teid))
@@ -592,6 +614,22 @@ test_flow (vlib_main_t * vm, unformat_input_t * input,
            type = VNET_FLOW_TYPE_IP4_IPSEC_AH;
          else if (tcp_udp_port_set)
            type = VNET_FLOW_TYPE_IP4_N_TUPLE;
+         else if (inner_ip4_set)
+           {
+             if (inner_port_set)
+               type = VNET_FLOW_TYPE_IP4_IP4_N_TUPLE;
+             else
+               type = VNET_FLOW_TYPE_IP4_IP4;
+             protocol.prot = IP_PROTOCOL_IP_IN_IP;
+           }
+         else if (inner_ip6_set)
+           {
+             if (inner_port_set)
+               type = VNET_FLOW_TYPE_IP4_IP6_N_TUPLE;
+             else
+               type = VNET_FLOW_TYPE_IP4_IP6;
+             protocol.prot = IP_PROTOCOL_IPV6;
+           }
          else
            type = VNET_FLOW_TYPE_IP4;
          break;
@@ -600,6 +638,22 @@ test_flow (vlib_main_t * vm, unformat_input_t * input,
            type = VNET_FLOW_TYPE_IP6_N_TUPLE;
          else if (vni_set)
            type = VNET_FLOW_TYPE_IP6_VXLAN;
+         else if (inner_ip4_set)
+           {
+             if (inner_port_set)
+               type = VNET_FLOW_TYPE_IP6_IP4_N_TUPLE;
+             else
+               type = VNET_FLOW_TYPE_IP6_IP4;
+             protocol.prot = IP_PROTOCOL_IP_IN_IP;
+           }
+         else if (inner_ip6_set)
+           {
+             if (inner_port_set)
+               type = VNET_FLOW_TYPE_IP6_IP6_N_TUPLE;
+             else
+               type = VNET_FLOW_TYPE_IP6_IP6;
+             protocol.prot = IP_PROTOCOL_IPV6;
+           }
          else
            type = VNET_FLOW_TYPE_IP6;
          break;
@@ -660,6 +714,30 @@ test_flow (vlib_main_t * vm, unformat_input_t * input,
            case IP_PROTOCOL_IPSEC_AH:
              flow.ip4_ipsec_esp.spi = spi;
              break;
+           case IP_PROTOCOL_IP_IN_IP:
+             clib_memcpy (&flow.ip4_ip4.in_src_addr, &in_ip4s,
+                          sizeof (ip4_address_and_mask_t));
+             clib_memcpy (&flow.ip4_ip4.in_dst_addr, &in_ip4d,
+                          sizeof (ip4_address_and_mask_t));
+             if (type == VNET_FLOW_TYPE_IP4_IP4_N_TUPLE)
+               {
+                 flow.ip4_ip4.in_protocol.prot = in_proto.prot;
+                 flow.ip4_ip4_n_tuple.in_src_port = in_sport;
+                 flow.ip4_ip4_n_tuple.in_dst_port = in_dport;
+               }
+             break;
+           case IP_PROTOCOL_IPV6:
+             clib_memcpy (&flow.ip4_ip6.in_src_addr, &in_ip6s,
+                          sizeof (ip6_address_and_mask_t));
+             clib_memcpy (&flow.ip4_ip6.in_dst_addr, &in_ip6d,
+                          sizeof (ip6_address_and_mask_t));
+             if (type == VNET_FLOW_TYPE_IP4_IP6_N_TUPLE)
+               {
+                 flow.ip4_ip6.in_protocol.prot = in_proto.prot;
+                 flow.ip4_ip6_n_tuple.in_src_port = in_sport;
+                 flow.ip4_ip6_n_tuple.in_dst_port = in_dport;
+               }
+             break;
            default:
              break;
            }
@@ -693,6 +771,30 @@ test_flow (vlib_main_t * vm, unformat_input_t * input,
              if (type == VNET_FLOW_TYPE_IP6_VXLAN)
                flow.ip6_vxlan.vni = vni;
              break;
+           case IP_PROTOCOL_IP_IN_IP:
+             clib_memcpy (&flow.ip6_ip4.in_src_addr, &in_ip4s,
+                          sizeof (ip4_address_and_mask_t));
+             clib_memcpy (&flow.ip6_ip4.in_dst_addr, &in_ip4d,
+                          sizeof (ip4_address_and_mask_t));
+             if (type == VNET_FLOW_TYPE_IP6_IP4_N_TUPLE)
+               {
+                 flow.ip6_ip4.in_protocol.prot = in_proto.prot;
+                 flow.ip6_ip4_n_tuple.in_src_port = in_sport;
+                 flow.ip6_ip4_n_tuple.in_dst_port = in_dport;
+               }
+             break;
+           case IP_PROTOCOL_IPV6:
+             clib_memcpy (&flow.ip6_ip6.in_src_addr, &in_ip6s,
+                          sizeof (ip6_address_and_mask_t));
+             clib_memcpy (&flow.ip6_ip6.in_dst_addr, &in_ip6d,
+                          sizeof (ip6_address_and_mask_t));
+             if (type == VNET_FLOW_TYPE_IP6_IP6_N_TUPLE)
+               {
+                 flow.ip6_ip6.in_protocol.prot = in_proto.prot;
+                 flow.ip6_ip6_n_tuple.in_src_port = in_sport;
+                 flow.ip6_ip6_n_tuple.in_dst_port = in_dport;
+               }
+             break;
            default:
              break;
            }