VPP-521: Classify API enhancement to redirect traffic to pre-defined VRF
[vpp.git] / vnet / vnet / classify / vnet_classify.c
index 2a77701..d7a0d81 100644 (file)
@@ -16,7 +16,8 @@
 #include <vnet/classify/input_acl.h>
 #include <vnet/ip/ip.h>
 #include <vnet/api_errno.h>     /* for API error numbers */
-#include <vnet/l2/l2_classify.h> /* for L2_CLASSIFY_NEXT_xxx */
+#include <vnet/l2/l2_classify.h> /* for L2_INPUT_CLASSIFY_NEXT_xxx */
+#include <vnet/fib/fib_table.h>
 
 vnet_classify_main_t vnet_classify_main;
 
@@ -88,6 +89,14 @@ vnet_classify_register_unformat_acl_next_index_fn (unformat_function_t * fn)
   vec_add1 (cm->unformat_acl_next_index_fns, fn);
 }
 
+void
+vnet_classify_register_unformat_policer_next_index_fn (unformat_function_t * fn)
+{
+  vnet_classify_main_t * cm = &vnet_classify_main;
+
+  vec_add1 (cm->unformat_policer_next_index_fns, fn);
+}
+
 void vnet_classify_register_unformat_opaque_index_fn (unformat_function_t * fn)
 {
   vnet_classify_main_t * cm = &vnet_classify_main;
@@ -110,7 +119,7 @@ vnet_classify_new_table (vnet_classify_main_t *cm,
   memset(t, 0, sizeof (*t));
   
   vec_validate_aligned (t->mask, match_n_vectors - 1, sizeof(u32x4));
-  memcpy (t->mask, mask, match_n_vectors * sizeof (u32x4));
+  clib_memcpy (t->mask, mask, match_n_vectors * sizeof (u32x4));
 
   t->next_table_index = ~0;
   t->nbuckets = nbuckets;
@@ -292,7 +301,7 @@ static inline void make_working_copy
     {
 #define _(size)                                         \
     case size:                                          \
-      memcpy (working_copy, v,                          \
+      clib_memcpy (working_copy, v,                          \
               sizeof (vnet_classify_entry_##size##_t)   \
               * (1<<b->log2_pages)                      \
               * (t->entries_per_page));                 \
@@ -348,7 +357,7 @@ split_and_rehash (vnet_classify_table_t * t,
                   
                   if (vnet_classify_entry_is_free (new_v))
                     {
-                      memcpy (new_v, v, sizeof (vnet_classify_entry_t) 
+                      clib_memcpy (new_v, v, sizeof (vnet_classify_entry_t) 
                               + (t->match_n_vectors * sizeof (u32x4)));
                       new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
                       goto doublebreak;
@@ -405,7 +414,7 @@ int vnet_classify_add_del (vnet_classify_table_t * t,
         }
 
       v = vnet_classify_entry_alloc (t, 0 /* new_log2_pages */);
-      memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
+      clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
               t->match_n_vectors * sizeof (u32x4));
       v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
 
@@ -436,7 +445,7 @@ int vnet_classify_add_del (vnet_classify_table_t * t,
 
           if (!memcmp (v->key, add_v->key, t->match_n_vectors * sizeof (u32x4)))
             {
-              memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
+              clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
                       t->match_n_vectors * sizeof(u32x4));
               v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
 
@@ -452,7 +461,7 @@ int vnet_classify_add_del (vnet_classify_table_t * t,
 
           if (vnet_classify_entry_is_free (v))
             {
-              memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
+              clib_memcpy (v, add_v, sizeof (vnet_classify_entry_t) +
                       t->match_n_vectors * sizeof(u32x4));
               v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
               CLIB_MEMORY_BARRIER();
@@ -513,7 +522,7 @@ int vnet_classify_add_del (vnet_classify_table_t * t,
 
       if (vnet_classify_entry_is_free (new_v))
         {
-          memcpy (new_v, add_v, sizeof (vnet_classify_entry_t) +
+          clib_memcpy (new_v, add_v, sizeof (vnet_classify_entry_t) +
                   t->match_n_vectors * sizeof(u32x4));
           new_v->flags &= ~(VNET_CLASSIFY_ENTRY_FREE);
           goto expand_ok;
@@ -563,9 +572,9 @@ static u8 * format_classify_entry (u8 * s, va_list * args)
   vnet_classify_entry_t * e = va_arg (*args, vnet_classify_entry_t *);
 
   s = format
-    (s, "[%u]: next_index %d advance %d opaque %d\n",
+    (s, "[%u]: next_index %d advance %d opaque %d action %d metadata %d\n",
      vnet_classify_get_offset (t, e), e->next_index, e->advance, 
-     e->opaque_index);
+     e->opaque_index, e->action, e->metadata);
 
 
   s = format (s, "        k: %U\n", format_hex_bytes, e->key,
@@ -645,24 +654,37 @@ int vnet_classify_add_del_table (vnet_classify_main_t * cm,
                                  u32 next_table_index,
                                  u32 miss_next_index,
                                  u32 * table_index,
+                                 u8 current_data_flag,
+                                 i16 current_data_offset,
                                  int is_add)
 {
   vnet_classify_table_t * t;
 
   if (is_add)
     {
-      *table_index = ~0;
-      if (memory_size == 0)
-        return VNET_API_ERROR_INVALID_MEMORY_SIZE;
-
-      if (nbuckets == 0)
-        return VNET_API_ERROR_INVALID_VALUE;
-
-      t = vnet_classify_new_table (cm, mask, nbuckets, memory_size, 
-        skip, match);
-      t->next_table_index = next_table_index;
-      t->miss_next_index = miss_next_index;
-      *table_index = t - cm->tables;
+      if (*table_index == ~0) /* add */
+        {
+          if (memory_size == 0)
+            return VNET_API_ERROR_INVALID_MEMORY_SIZE;
+
+          if (nbuckets == 0)
+            return VNET_API_ERROR_INVALID_VALUE;
+
+          t = vnet_classify_new_table (cm, mask, nbuckets, memory_size,
+            skip, match);
+          t->next_table_index = next_table_index;
+          t->miss_next_index = miss_next_index;
+          t->current_data_flag = current_data_flag;
+          t->current_data_offset = current_data_offset;
+          *table_index = t - cm->tables;
+        }
+      else /* update */
+        {
+          vnet_classify_main_t *cm = &vnet_classify_main;
+          t = pool_elt_at_index (cm->tables, *table_index);
+
+          t->next_table_index = next_table_index;
+        }
       return 0;
     }
   
@@ -670,6 +692,14 @@ int vnet_classify_add_del_table (vnet_classify_main_t * cm,
   return 0;
 }
 
+#define foreach_tcp_proto_field                 \
+_(src_port)                                     \
+_(dst_port)
+
+#define foreach_udp_proto_field                 \
+_(src_port)                                     \
+_(dst_port)
+
 #define foreach_ip4_proto_field                 \
 _(src_address)                                  \
 _(dst_address)                                  \
@@ -680,6 +710,125 @@ _(ttl)                                          \
 _(protocol)                                     \
 _(checksum)
 
+uword unformat_tcp_mask (unformat_input_t * input, va_list * args)
+{
+  u8 ** maskp = va_arg (*args, u8 **);
+  u8 * mask = 0;
+  u8 found_something = 0;
+  tcp_header_t * tcp;
+
+#define _(a) u8 a=0;
+  foreach_tcp_proto_field;
+#undef _
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (0) ;
+#define _(a) else if (unformat (input, #a)) a=1;
+      foreach_tcp_proto_field
+#undef _
+      else
+        break;
+    }
+
+#define _(a) found_something += a;
+  foreach_tcp_proto_field;
+#undef _
+
+  if (found_something == 0)
+    return 0;
+
+  vec_validate (mask, sizeof (*tcp) - 1);
+
+  tcp = (tcp_header_t *) mask;
+
+#define _(a) if (a) memset (&tcp->a, 0xff, sizeof (tcp->a));
+  foreach_tcp_proto_field;
+#undef _
+
+  *maskp = mask;
+  return 1;
+}
+
+uword unformat_udp_mask (unformat_input_t * input, va_list * args)
+{
+  u8 ** maskp = va_arg (*args, u8 **);
+  u8 * mask = 0;
+  u8 found_something = 0;
+  udp_header_t * udp;
+
+#define _(a) u8 a=0;
+  foreach_udp_proto_field;
+#undef _
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (0) ;
+#define _(a) else if (unformat (input, #a)) a=1;
+      foreach_udp_proto_field
+#undef _
+      else
+        break;
+    }
+
+#define _(a) found_something += a;
+  foreach_udp_proto_field;
+#undef _
+
+  if (found_something == 0)
+    return 0;
+
+  vec_validate (mask, sizeof (*udp) - 1);
+
+  udp = (udp_header_t *) mask;
+
+#define _(a) if (a) memset (&udp->a, 0xff, sizeof (udp->a));
+  foreach_udp_proto_field;
+#undef _
+
+  *maskp = mask;
+  return 1;
+}
+
+typedef struct {
+  u16 src_port, dst_port;
+} tcpudp_header_t;
+
+uword unformat_l4_mask (unformat_input_t * input, va_list * args)
+{
+  u8 ** maskp = va_arg (*args, u8 **);
+  u16 src_port = 0, dst_port = 0;
+  tcpudp_header_t * tcpudp;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "tcp %U", unformat_tcp_mask, maskp))
+        return 1;
+      else if (unformat (input, "udp %U", unformat_udp_mask, maskp))
+        return 1;
+      else if (unformat (input, "src_port"))
+        src_port = 0xFFFF;
+      else if (unformat (input, "dst_port"))
+        dst_port = 0xFFFF;
+      else
+        return 0;
+    }
+
+  if (!src_port && !dst_port)
+    return 0;
+
+  u8 * mask = 0;
+  vec_validate (mask, sizeof (tcpudp_header_t) - 1);
+
+  tcpudp = (tcpudp_header_t *) mask;
+  tcpudp->src_port = src_port;
+  tcpudp->dst_port = dst_port;
+
+  *maskp = mask;
+
+  return 1;
+}
+
 uword unformat_ip4_mask (unformat_input_t * input, va_list * args)
 {
   u8 ** maskp = va_arg (*args, u8 **);
@@ -951,6 +1100,7 @@ uword unformat_classify_mask (unformat_input_t * input, va_list * args)
   u8 * mask = 0;
   u8 * l2 = 0;
   u8 * l3 = 0;
+  u8 * l4 = 0;
   int i;
   
   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
@@ -960,20 +1110,37 @@ uword unformat_classify_mask (unformat_input_t * input, va_list * args)
       ;
     else if (unformat (input, "l3 %U", unformat_l3_mask, &l3))
       ;
+    else if (unformat (input, "l4 %U", unformat_l4_mask, &l4))
+      ;
     else
       break;
   }
 
-  if (mask || l2 || l3)
+  if (l4 && !l3) {
+    vec_free (mask);
+    vec_free (l2);
+    vec_free (l4);
+    return 0;
+  }
+
+  if (mask || l2 || l3 || l4)
     {
-      if (l2 || l3)
+      if (l2 || l3 || l4)
         {
           /* "With a free Ethernet header in every package" */
           if (l2 == 0)
             vec_validate (l2, 13);
           mask = l2;
-          vec_append (mask, l3);
-          vec_free (l3);
+          if (l3)
+            {
+              vec_append (mask, l3);
+              vec_free (l3);
+            }
+          if (l4)
+            {
+              vec_append (mask, l4);
+              vec_free (l4);
+            }
         }
 
       /* Scan forward looking for the first significant mask octet */
@@ -1012,14 +1179,14 @@ uword unformat_classify_mask (unformat_input_t * input, va_list * args)
   return 0;
 }
 
-#define foreach_l2_next                         \
+#define foreach_l2_input_next                   \
 _(drop, DROP)                                   \
 _(ethernet, ETHERNET_INPUT)                     \
 _(ip4, IP4_INPUT)                               \
 _(ip6, IP6_INPUT)                              \
 _(li, LI)
 
-uword unformat_l2_next_index (unformat_input_t * input, va_list * args)
+uword unformat_l2_input_next_index (unformat_input_t * input, va_list * args)
 {
   vnet_classify_main_t * cm = &vnet_classify_main;
   u32 * miss_next_indexp = va_arg (*args, u32 *);
@@ -1038,8 +1205,47 @@ uword unformat_l2_next_index (unformat_input_t * input, va_list * args)
     }
 
 #define _(n,N) \
-  if (unformat (input, #n)) { next_index = L2_CLASSIFY_NEXT_##N; goto out;}
-  foreach_l2_next;
+  if (unformat (input, #n)) { next_index = L2_INPUT_CLASSIFY_NEXT_##N; goto out;}
+  foreach_l2_input_next;
+#undef _
+  
+  if (unformat (input, "%d", &tmp))
+    { 
+      next_index = tmp; 
+      goto out; 
+    }
+  
+  return 0;
+
+ out:
+  *miss_next_indexp = next_index;
+  return 1;
+}
+
+#define foreach_l2_output_next                   \
+_(drop, DROP)
+
+uword unformat_l2_output_next_index (unformat_input_t * input, va_list * args)
+{
+  vnet_classify_main_t * cm = &vnet_classify_main;
+  u32 * miss_next_indexp = va_arg (*args, u32 *);
+  u32 next_index = 0;
+  u32 tmp;
+  int i;
+  
+  /* First try registered unformat fns, allowing override... */
+  for (i = 0; i < vec_len (cm->unformat_l2_next_index_fns); i++)
+    {
+      if (unformat (input, "%U", cm->unformat_l2_next_index_fns[i], &tmp))
+        {
+          next_index = tmp;
+          goto out;
+        }
+    }
+
+#define _(n,N) \
+  if (unformat (input, #n)) { next_index = L2_OUTPUT_CLASSIFY_NEXT_##N; goto out;}
+  foreach_l2_output_next;
 #undef _
   
   if (unformat (input, "%d", &tmp))
@@ -1056,9 +1262,7 @@ uword unformat_l2_next_index (unformat_input_t * input, va_list * args)
 }
 
 #define foreach_ip_next                         \
-_(miss, MISS)                                   \
 _(drop, DROP)                                   \
-_(local, LOCAL)                                 \
 _(rewrite, REWRITE)
 
 uword unformat_ip_next_index (unformat_input_t * input, va_list * args)
@@ -1141,6 +1345,37 @@ uword unformat_acl_next_index (unformat_input_t * input, va_list * args)
   return 1;
 }
 
+uword unformat_policer_next_index (unformat_input_t * input, va_list * args)
+{
+  u32 * next_indexp = va_arg (*args, u32 *);
+  vnet_classify_main_t * cm = &vnet_classify_main;
+  u32 next_index = 0;
+  u32 tmp;
+  int i;
+
+  /* First try registered unformat fns, allowing override... */
+  for (i = 0; i < vec_len (cm->unformat_policer_next_index_fns); i++)
+    {
+      if (unformat (input, "%U", cm->unformat_policer_next_index_fns[i], &tmp))
+        {
+          next_index = tmp;
+          goto out;
+        }
+    }
+
+  if (unformat (input, "%d", &tmp))
+    {
+      next_index = tmp;
+      goto out;
+    }
+
+  return 0;
+
+ out:
+  *next_indexp = next_index;
+  return 1;
+}
+
 static clib_error_t *
 classify_table_command_fn (vlib_main_t * vm,
                            unformat_input_t * input,
@@ -1155,6 +1390,8 @@ classify_table_command_fn (vlib_main_t * vm,
   u32 miss_next_index = ~0;
   u32 memory_size = 2<<20;
   u32 tmp;
+  u32 current_data_flag = 0;
+  int current_data_offset = 0;
 
   u8 * mask = 0;
   vnet_classify_main_t * cm = &vnet_classify_main;
@@ -1183,24 +1420,31 @@ classify_table_command_fn (vlib_main_t * vm,
     else if (unformat (input, "miss-next %U", unformat_ip_next_index,
                        &miss_next_index))
       ;
-    else if (unformat (input, "l2-miss-next %U", unformat_l2_next_index,
+    else if (unformat (input, "l2-input-miss-next %U", unformat_l2_input_next_index,
+                       &miss_next_index))
+        ;
+    else if (unformat (input, "l2-output-miss-next %U", unformat_l2_output_next_index,
                        &miss_next_index))
       ;
     else if (unformat (input, "acl-miss-next %U", unformat_acl_next_index,
                        &miss_next_index))
       ;
-                       
+    else if (unformat (input, "current-data-flag %d", &current_data_flag))
+      ;
+    else if (unformat (input, "current-data-offset %d", &current_data_offset))
+      ;
+
     else
       break;
   }
   
-  if (is_add && mask == 0)
+  if (is_add && mask == 0 && table_index == ~0)
     return clib_error_return (0, "Mask required");
 
-  if (is_add && skip == ~0)
+  if (is_add && skip == ~0 && table_index == ~0)
     return clib_error_return (0, "skip count required");
 
-  if (is_add && match == ~0)
+  if (is_add && match == ~0 && table_index == ~0)
     return clib_error_return (0, "match count required");
       
   if (!is_add && table_index == ~0)
@@ -1208,7 +1452,7 @@ classify_table_command_fn (vlib_main_t * vm,
 
   rv = vnet_classify_add_del_table (cm, mask, nbuckets, memory_size,
         skip, match, next_table_index, miss_next_index,
-        &table_index, is_add);
+        &table_index, current_data_flag, current_data_offset, is_add);
   switch (rv)
     {
     case 0:
@@ -1225,7 +1469,8 @@ VLIB_CLI_COMMAND (classify_table, static) = {
   .path = "classify table",
   .short_help = 
   "classify table [miss-next|l2-miss_next|acl-miss-next <next_index>]"
-  "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>] [del]",
+  "\n mask <mask-value> buckets <nn> [skip <n>] [match <n>]"
+  "\n [current-data-flag <n>] [current-data-offset <n>] [table <n>] [del]",
   .function = classify_table_command_fn,
 };
 
@@ -1249,8 +1494,9 @@ static u8 * format_vnet_classify_table (u8 * s, va_list * args)
 
   s = format (s, "\n  Heap: %U", format_mheap, t->mheap, 0 /*verbose*/); 
 
-  s = format (s, "\n  nbuckets %d, skip %d match %d", 
-              t->nbuckets, t->skip_n_vectors, t->match_n_vectors);
+  s = format (s, "\n  nbuckets %d, skip %d match %d flag %d offset %d",
+              t->nbuckets, t->skip_n_vectors, t->match_n_vectors,
+              t->current_data_flag, t->current_data_offset);
   s = format (s, "\n  mask %U", format_hex_bytes, t->mask, 
               t->match_n_vectors * sizeof (u32x4));
 
@@ -1314,6 +1560,36 @@ VLIB_CLI_COMMAND (show_classify_table_command, static) = {
   .function = show_classify_tables_command_fn,
 };
 
+uword unformat_l4_match (unformat_input_t * input, va_list * args)
+{
+  u8 ** matchp = va_arg (*args, u8 **);
+
+  u8 * proto_header = 0;
+  int src_port = 0;
+  int dst_port = 0;
+
+  tcpudp_header_t h;
+
+  while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (input, "src_port %d", &src_port))
+        ;
+      else if (unformat (input, "dst_port %d", &dst_port))
+        ;
+      else
+        return 0;
+    }
+
+  h.src_port = clib_host_to_net_u16(src_port);
+  h.dst_port = clib_host_to_net_u16(dst_port);
+  vec_validate(proto_header, sizeof(h)-1);
+  memcpy(proto_header, &h, sizeof(h));
+
+  *matchp = proto_header;
+
+  return 1;
+}
+
 uword unformat_ip4_match (unformat_input_t * input, va_list * args)
 {
   u8 ** matchp = va_arg (*args, u8 **);
@@ -1397,13 +1673,13 @@ uword unformat_ip4_match (unformat_input_t * input, va_list * args)
     ip->tos = tos_val;
   
   if (length)
-    ip->length = length_val;
+    ip->length = clib_host_to_net_u16 (length_val);
   
   if (ttl)
     ip->ttl = ttl_val;
 
   if (checksum)
-    ip->checksum = checksum_val;
+    ip->checksum = clib_host_to_net_u16 (checksum_val);
 
   *matchp = match;
   return 1;
@@ -1464,10 +1740,10 @@ uword unformat_ip6_match (unformat_input_t * input, va_list * args)
   ip = (ip6_header_t *) match;
   
   if (src)
-    memcpy (&ip->src_address, &src_val, sizeof (ip->src_address));
+    clib_memcpy (&ip->src_address, &src_val, sizeof (ip->src_address));
 
   if (dst)
-    memcpy (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
+    clib_memcpy (&ip->dst_address, &dst_val, sizeof (ip->dst_address));
   
   if (proto)
     ip->protocol = proto_val;
@@ -1584,10 +1860,10 @@ uword unformat_l2_match (unformat_input_t * input, va_list * args)
   vec_validate_aligned (match, len-1, sizeof(u32x4));
 
   if (dst)
-    memcpy (match, dst_val, 6);
+    clib_memcpy (match, dst_val, 6);
 
   if (src)
-    memcpy (match + 6, src_val, 6);
+    clib_memcpy (match + 6, src_val, 6);
   
   if (tag2)
     {
@@ -1651,6 +1927,7 @@ uword unformat_classify_match (unformat_input_t * input, va_list * args)
   u8 * match = 0;
   u8 * l2 = 0;
   u8 * l3 = 0;
+  u8 * l4 = 0;
 
   if (pool_is_free_index (cm->tables, table_index))
     return 0;
@@ -1664,20 +1941,37 @@ uword unformat_classify_match (unformat_input_t * input, va_list * args)
       ;
     else if (unformat (input, "l3 %U", unformat_l3_match, &l3))
       ;
+    else if (unformat (input, "l4 %U", unformat_l4_match, &l4))
+      ;
     else
       break;
   }
 
-  if (match || l2 || l3)
+  if (l4 && !l3) {
+    vec_free (match);
+    vec_free (l2);
+    vec_free (l4);
+    return 0;
+  }
+
+  if (match || l2 || l3 || l4)
     {
-      if (l2 || l3)
+      if (l2 || l3 || l4)
         {
           /* "Win a free Ethernet header in every packet" */
           if (l2 == 0)
             vec_validate_aligned (l2, 13, sizeof(u32x4));
           match = l2;
-          vec_append_aligned (match, l3, sizeof(u32x4));
-          vec_free (l3);
+          if (l3)
+            {
+              vec_append_aligned (match, l3, sizeof(u32x4));
+              vec_free (l3);
+            }
+          if (l4)
+            {
+              vec_append_aligned (match, l4, sizeof(u32x4));
+              vec_free (l4);
+            }
         }
 
       /* Make sure the vector is big enough even if key is all 0's */
@@ -1702,6 +1996,8 @@ int vnet_classify_add_del_session (vnet_classify_main_t * cm,
                                    u32 hit_next_index,
                                    u32 opaque_index, 
                                    i32 advance,
+                                   u8 action,
+                                   u32 metadata,
                                    int is_add)
 {
   vnet_classify_table_t * t;
@@ -1721,9 +2017,14 @@ int vnet_classify_add_del_session (vnet_classify_main_t * cm,
   e->hits = 0;
   e->last_heard = 0;
   e->flags = 0;
+  e->action = action;
+  if (e->action == CLASSIFY_ACTION_SET_IP4_FIB_INDEX)
+    e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4, metadata);
+  else if (e->action == CLASSIFY_ACTION_SET_IP6_FIB_INDEX)
+    e->metadata = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, metadata);
 
   /* Copy key data, honoring skip_n_vectors */
-  memcpy (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
+  clib_memcpy (&e->key, match + t->skip_n_vectors * sizeof (u32x4),
           t->match_n_vectors * sizeof (u32x4));
 
   /* Clear don't-care bits; likely when dynamically creating sessions */
@@ -1748,6 +2049,8 @@ classify_session_command_fn (vlib_main_t * vm,
   u64 opaque_index = ~0;
   u8 * match = 0;
   i32 advance = 0;
+  u32 action = 0;
+  u32 metadata = 0;
   int i, rv;
 
   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) 
@@ -1757,12 +2060,18 @@ classify_session_command_fn (vlib_main_t * vm,
       else if (unformat (input, "hit-next %U", unformat_ip_next_index,
                          &hit_next_index))
         ;
-      else if (unformat (input, "l2-hit-next %U", unformat_l2_next_index,
+      else if (unformat (input, "l2-input-hit-next %U", unformat_l2_input_next_index,
+                         &hit_next_index))
+        ;
+      else if (unformat (input, "l2-output-hit-next %U", unformat_l2_output_next_index,
                          &hit_next_index))
         ;
       else if (unformat (input, "acl-hit-next %U", unformat_acl_next_index,
                          &hit_next_index))
         ;
+      else if (unformat (input, "policer-hit-next %U",
+                         unformat_policer_next_index, &hit_next_index))
+        ;
       else if (unformat (input, "opaque-index %lld", &opaque_index))
         ;
       else if (unformat (input, "match %U", unformat_classify_match,
@@ -1772,6 +2081,10 @@ classify_session_command_fn (vlib_main_t * vm,
         ;
       else if (unformat (input, "table-index %d", &table_index))
         ;
+      else if (unformat (input, "action set-ip4-fib-id %d", &metadata))
+        action = 1;
+      else if (unformat (input, "action set-ip6-fib-id %d", &metadata))
+        action = 2;
       else
         {
           /* Try registered opaque-index unformat fns */
@@ -1795,7 +2108,8 @@ classify_session_command_fn (vlib_main_t * vm,
 
   rv = vnet_classify_add_del_session (cm, table_index, match, 
                                       hit_next_index,
-                                      opaque_index, advance, is_add);
+                                      opaque_index, advance,
+                                      action, metadata, is_add);
 
   switch(rv)
     {
@@ -1812,9 +2126,11 @@ classify_session_command_fn (vlib_main_t * vm,
 
 VLIB_CLI_COMMAND (classify_session_command, static) = {
     .path = "classify session",
-    .short_help = 
-    "classify session [hit-next|l2-hit-next|acl-hit-next <next_index>]"
-    "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]",
+    .short_help =
+    "classify session [hit-next|l2-hit-next|"
+    "acl-hit-next <next_index>|policer-hit-next <policer_name>]"
+    "\n table-index <nn> match [hex] [l2] [l3 ip4] [opaque-index <index>]"
+    "\n [action set-ip4-fib-id <n>] [action set-ip6-fib-id <n>] [del]",
     .function = classify_session_command_fn,
 };
 
@@ -1833,27 +2149,31 @@ unformat_opaque_sw_if_index (unformat_input_t * input, va_list * args)
   return 0;
 }
 
-static uword 
+static uword
 unformat_ip_next_node (unformat_input_t * input, va_list * args)
 {
   vnet_classify_main_t * cm = &vnet_classify_main;
   u32 * next_indexp = va_arg (*args, u32 *);
   u32 node_index;
-  u32 next_index, rv;
+  u32 next_index = ~0;
 
-  if (unformat (input, "node %U", unformat_vlib_node,
+  if (unformat (input, "ip6-node %U", unformat_vlib_node,
                 cm->vlib_main, &node_index))
     {
-      rv = next_index = vlib_node_add_next 
-        (cm->vlib_main, ip4_classify_node.index, node_index);
-      next_index = vlib_node_add_next 
-        (cm->vlib_main, ip6_classify_node.index, node_index);
-      ASSERT(rv == next_index);
-
-      *next_indexp = next_index;
-      return 1;
+      next_index = vlib_node_add_next (cm->vlib_main,
+                                      ip6_classify_node.index, node_index);
     }
-  return 0;
+  else if (unformat (input, "ip4-node %U", unformat_vlib_node,
+                    cm->vlib_main, &node_index))
+    {
+      next_index = vlib_node_add_next (cm->vlib_main,
+                                      ip4_classify_node.index, node_index);
+    }
+  else
+    return 0;
+
+  *next_indexp = next_index;
+  return 1;
 }
 
 static uword 
@@ -1862,16 +2182,40 @@ unformat_acl_next_node (unformat_input_t * input, va_list * args)
   vnet_classify_main_t * cm = &vnet_classify_main;
   u32 * next_indexp = va_arg (*args, u32 *);
   u32 node_index;
-  u32 next_index, rv;
+  u32 next_index;
+
+  if (unformat (input, "ip6-node %U", unformat_vlib_node,
+                cm->vlib_main, &node_index))
+    {
+      next_index = vlib_node_add_next (cm->vlib_main,
+                                      ip6_inacl_node.index, node_index);
+    }
+  else if (unformat (input, "ip4-node %U", unformat_vlib_node,
+                    cm->vlib_main, &node_index))
+    {
+      next_index = vlib_node_add_next (cm->vlib_main,
+                                      ip4_inacl_node.index, node_index);
+    }
+  else
+    return 0;
+
+  *next_indexp = next_index;
+  return 1;
+}
+
+static uword 
+unformat_l2_input_next_node (unformat_input_t * input, va_list * args)
+{
+  vnet_classify_main_t * cm = &vnet_classify_main;
+  u32 * next_indexp = va_arg (*args, u32 *);
+  u32 node_index;
+  u32 next_index;
 
-  if (unformat (input, "node %U", unformat_vlib_node,
+  if (unformat (input, "input-node %U", unformat_vlib_node,
                 cm->vlib_main, &node_index))
     {
-      rv = next_index = vlib_node_add_next 
-        (cm->vlib_main, ip4_inacl_node.index, node_index);
       next_index = vlib_node_add_next 
-        (cm->vlib_main, ip6_inacl_node.index, node_index);
-      ASSERT(rv == next_index);
+        (cm->vlib_main, l2_input_classify_node.index, node_index);
 
       *next_indexp = next_index;
       return 1;
@@ -1880,18 +2224,18 @@ unformat_acl_next_node (unformat_input_t * input, va_list * args)
 }
 
 static uword 
-unformat_l2_next_node (unformat_input_t * input, va_list * args)
+unformat_l2_output_next_node (unformat_input_t * input, va_list * args)
 {
   vnet_classify_main_t * cm = &vnet_classify_main;
   u32 * next_indexp = va_arg (*args, u32 *);
   u32 node_index;
   u32 next_index;
 
-  if (unformat (input, "node %U", unformat_vlib_node,
+  if (unformat (input, "output-node %U", unformat_vlib_node,
                 cm->vlib_main, &node_index))
     {
       next_index = vlib_node_add_next 
-        (cm->vlib_main, l2_classify_node.index, node_index);
+        (cm->vlib_main, l2_output_classify_node.index, node_index);
 
       *next_indexp = next_index;
       return 1;
@@ -1899,7 +2243,6 @@ unformat_l2_next_node (unformat_input_t * input, va_list * args)
   return 0;
 }
 
-
 static clib_error_t * 
 vnet_classify_init (vlib_main_t * vm)
 {
@@ -1915,7 +2258,10 @@ vnet_classify_init (vlib_main_t * vm)
     (unformat_ip_next_node);
 
   vnet_classify_register_unformat_l2_next_index_fn
-    (unformat_l2_next_node);
+    (unformat_l2_input_next_node);
+
+  vnet_classify_register_unformat_l2_next_index_fn
+    (unformat_l2_output_next_node);
 
   vnet_classify_register_unformat_acl_next_index_fn
     (unformat_acl_next_node);
@@ -2002,7 +2348,7 @@ test_classify_command_fn (vlib_main_t * vm,
                                        memory_size,
                                        0 /* skip */,
                                        3 /* vectors to match */);
-          t->miss_next_index = IP_LOOKUP_NEXT_LOCAL;
+          t->miss_next_index = IP_LOOKUP_NEXT_DROP;
           vlib_cli_output (vm, "Create table %d", t - cm->tables);
         }
       
@@ -2014,7 +2360,7 @@ test_classify_command_fn (vlib_main_t * vm,
           rv = vnet_classify_add_del_session (cm, t - cm->tables, (u8 *) data,
                                               IP_LOOKUP_NEXT_DROP,
                                               i+100 /* opaque_index */, 
-                                              0 /* advance */, 
+                                              0 /* advance */, 0, 0,
                                               1 /* is_add */);
 
           if (rv != 0)
@@ -2053,7 +2399,7 @@ test_classify_command_fn (vlib_main_t * vm,
       rv = vnet_classify_add_del_session (cm, t - cm->tables, key_minus_skip,
                                           IP_LOOKUP_NEXT_DROP,
                                           i+100 /* opaque_index */, 
-                                          0 /* advance */, 
+                                          0 /* advance */, 0, 0,
                                           0 /* is_add */);
       if (rv != 0)
         clib_warning ("del: returned %d", rv);