acl-plugin: add whitelisted ethertype mode (VPP-1163) 34/10434/4
authorAndrew Yourtchenko <ayourtch@gmail.com>
Tue, 6 Feb 2018 16:42:32 +0000 (17:42 +0100)
committerNeale Ranns <nranns@cisco.com>
Thu, 8 Feb 2018 15:51:46 +0000 (15:51 +0000)
Currently, ACL plugin largely does not care about the
ethertypes other than 0x0800 (IPv4) and 0x86dd (IPv6),
the only exception being 0x0806 (ARP), which is
dealt with by the MACIP ACLs.

The other ethertypes in L2 mode are just let through.

This adds a new API message acl_interface_set_etype_whitelist,
which allows to flip the mode of a given interface
into "ethertype whitelist mode": the caller of this message
must supply the two lists (inbound and outbound) of the ethertypes
that are to be permitted, the rest of the ethertypes are
dropped.

The whitelisting for a given interface and direction takes
effect only when a policy ACL is also applied.

This operates on the same classifier node as the one used for
dispatching the policy ACL, thus, if one wishes for most of the
reasonable IPv4 deployments to continue to operate within
the whitelist mode, they must permit ARP ethertype (0x0806)

The empty list for a given direction resets the processing
to allow the unknown ethertypes. So, if one wants to just
permit the IPv4 and IPv6 and nothing else, one can add
their ethertypes to the whitelist.

Add the "show acl-plugin interface" corresponding outputs
about the whitelists, vat command, and unittests.

Change-Id: I4659978c801f36d554b6615e56e424b77876662c
Signed-off-by: Andrew Yourtchenko <ayourtch@gmail.com>
src/plugins/acl/acl.api
src/plugins/acl/acl.c
src/plugins/acl/acl.h
src/plugins/acl/acl_test.c
src/plugins/acl/manual_fns.h
test/test_acl_plugin.py
test/vpp_papi_provider.py

index e3166d6..047fc68 100644 (file)
@@ -476,3 +476,23 @@ define macip_acl_interface_list_details
   u8 count;
   u32 acls[count];
 };
+
+/** \brief Set the ethertype whitelists on an interface. Takes effect when applying ACLs on the interface, so must be given prior.
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param sw_if_index - the interface to alter the list of ACLs on
+    @param count - total number of whitelisted ethertypes in the vector
+    @param n_input - this many first elements correspond to input whitelisted ethertypes, the rest - output
+    @param whitelist - vector of whitelisted ethertypes
+*/
+
+autoreply manual_print define acl_interface_set_etype_whitelist
+{
+  u32 client_index;
+  u32 context;
+  u32 sw_if_index;
+  u8 count; /* Total number of ethertypes in the whitelist */
+  u8 n_input; /* first n_input ethertypes are input, the rest - output */
+  u16 whitelist[count];
+};
+
index 82e1ab0..82a5670 100644 (file)
@@ -75,7 +75,8 @@ _(MACIP_ACL_DEL, macip_acl_del) \
 _(MACIP_ACL_INTERFACE_ADD_DEL, macip_acl_interface_add_del) \
 _(MACIP_ACL_DUMP, macip_acl_dump) \
 _(MACIP_ACL_INTERFACE_GET, macip_acl_interface_get) \
-_(MACIP_ACL_INTERFACE_LIST_DUMP, macip_acl_interface_list_dump)
+_(MACIP_ACL_INTERFACE_LIST_DUMP, macip_acl_interface_list_dump) \
+_(ACL_INTERFACE_SET_ETYPE_WHITELIST, acl_interface_set_etype_whitelist)
 
 
 /* *INDENT-OFF* */
@@ -85,6 +86,24 @@ VLIB_PLUGIN_REGISTER () = {
 };
 /* *INDENT-ON* */
 
+/* Format vec16. */
+u8 *
+format_vec16 (u8 * s, va_list * va)
+{
+  u16 *v = va_arg (*va, u16 *);
+  char *fmt = va_arg (*va, char *);
+  uword i;
+  for (i = 0; i < vec_len (v); i++)
+    {
+      if (i > 0)
+       s = format (s, ", ");
+      s = format (s, fmt, v[i]);
+    }
+  return s;
+}
+
+
+
 
 static void *
 acl_set_heap (acl_main_t * am)
@@ -402,6 +421,10 @@ u8 ip4_5tuple_mask[] =
    _(padpad) __ __ __ __
    _(padeth) __ __;
 
+ u8 ethertype_mask[] =
+   _("             dmac               smac          etype ")
+   _(ether) __ __ __ __ __ __ v __ __ __ __ __ __ v XX XX __ __;
+
 /* *INDENT-ON* */
 #undef XX
 #undef __
@@ -486,6 +509,7 @@ acl_unhook_l2_input_classify (acl_main_t * am, u32 sw_if_index)
   u32 ip6_table_index = ~0;
   u32 dot1q_table_index = ~0;
   u32 dot1ad_table_index = ~0;
+  u32 etype_table_index = ~0;
   void *oldheap = acl_set_heap (am);
 
   vec_validate_init_empty (am->acl_ip4_input_classify_table_by_sw_if_index,
@@ -496,6 +520,8 @@ acl_unhook_l2_input_classify (acl_main_t * am, u32 sw_if_index)
                           sw_if_index, ~0);
   vec_validate_init_empty (am->acl_dot1ad_input_classify_table_by_sw_if_index,
                           sw_if_index, ~0);
+  vec_validate_init_empty (am->acl_etype_input_classify_table_by_sw_if_index,
+                          sw_if_index, ~0);
 
   /* switch to global heap while calling vnet_* functions */
   clib_mem_set_heap (cm->vlib_main->heap_base);
@@ -539,6 +565,15 @@ acl_unhook_l2_input_classify (acl_main_t * am, u32 sw_if_index)
                                       sizeof (dot1ad_5tuple_mask) - 1, ~0,
                                       ~0, &dot1ad_table_index, 0);
     }
+  if (am->acl_etype_input_classify_table_by_sw_if_index[sw_if_index] != ~0)
+    {
+      etype_table_index =
+       am->acl_etype_input_classify_table_by_sw_if_index[sw_if_index];
+      am->acl_etype_input_classify_table_by_sw_if_index[sw_if_index] = ~0;
+      acl_classify_add_del_table_tiny (cm, ethertype_mask,
+                                      sizeof (ethertype_mask) - 1, ~0,
+                                      ~0, &etype_table_index, 0);
+    }
   clib_mem_set_heap (oldheap);
   return 0;
 }
@@ -551,6 +586,7 @@ acl_unhook_l2_output_classify (acl_main_t * am, u32 sw_if_index)
   u32 ip6_table_index = ~0;
   u32 dot1q_table_index = ~0;
   u32 dot1ad_table_index = ~0;
+  u32 etype_table_index = ~0;
   void *oldheap = acl_set_heap (am);
 
   vec_validate_init_empty (am->acl_ip4_output_classify_table_by_sw_if_index,
@@ -561,6 +597,8 @@ acl_unhook_l2_output_classify (acl_main_t * am, u32 sw_if_index)
                           sw_if_index, ~0);
   vec_validate_init_empty
     (am->acl_dot1ad_output_classify_table_by_sw_if_index, sw_if_index, ~0);
+  vec_validate_init_empty (am->acl_etype_output_classify_table_by_sw_if_index,
+                          sw_if_index, ~0);
 
   /* switch to global heap while calling vnet_* functions */
   clib_mem_set_heap (cm->vlib_main->heap_base);
@@ -605,6 +643,15 @@ acl_unhook_l2_output_classify (acl_main_t * am, u32 sw_if_index)
                                       sizeof (dot1ad_5tuple_mask) - 1, ~0,
                                       ~0, &dot1ad_table_index, 0);
     }
+  if (am->acl_etype_output_classify_table_by_sw_if_index[sw_if_index] != ~0)
+    {
+      etype_table_index =
+       am->acl_etype_output_classify_table_by_sw_if_index[sw_if_index];
+      am->acl_etype_output_classify_table_by_sw_if_index[sw_if_index] = ~0;
+      acl_classify_add_del_table_tiny (cm, ethertype_mask,
+                                      sizeof (ethertype_mask) - 1, ~0,
+                                      ~0, &etype_table_index, 0);
+    }
   clib_mem_set_heap (oldheap);
   return 0;
 }
@@ -676,6 +723,45 @@ acl_add_vlan_session (acl_main_t * am, u32 table_index, u8 is_output,
     }
 }
 
+static int
+intf_has_etype_whitelist (acl_main_t * am, u32 sw_if_index, int is_input)
+{
+  u16 **v = is_input
+    ? am->input_etype_whitelist_by_sw_if_index
+    : am->output_etype_whitelist_by_sw_if_index;
+  return ((vec_len (v) > sw_if_index) && vec_elt (v, sw_if_index));
+}
+
+static int
+etype_whitelist_add_sessions (acl_main_t * am, u32 sw_if_index, int is_input,
+                             u32 etype_table_index)
+{
+  vnet_classify_main_t *cm = &vnet_classify_main;
+  u16 **v = is_input
+    ? am->input_etype_whitelist_by_sw_if_index
+    : am->output_etype_whitelist_by_sw_if_index;
+  u8 *match = ethertype_mask;
+
+  int i;
+  int rv = 0;
+  u16 *whitelist = vec_elt (v, sw_if_index);
+  u32 next = ~0;               /* permit */
+  for (i = 0; i < vec_len (whitelist); i++)
+    {
+      /* big-endian */
+      match[12] = (whitelist[i] >> 8) & 0xff;
+      match[13] = whitelist[i] & 0xff;
+      rv = rv
+       || vnet_classify_add_del_session (cm, etype_table_index, match, next,
+                                         whitelist[i], 0, 0, 0, 1);
+    }
+
+  /* restore the mask */
+  match[12] = 0xff;
+  match[13] = 0xff;
+  return rv;
+}
+
 static int
 acl_hook_l2_input_classify (acl_main_t * am, u32 sw_if_index)
 {
@@ -684,6 +770,7 @@ acl_hook_l2_input_classify (acl_main_t * am, u32 sw_if_index)
   u32 ip6_table_index = ~0;
   u32 dot1q_table_index = ~0;
   u32 dot1ad_table_index = ~0;
+  u32 etype_table_index = ~0;
   int rv;
 
   void *prevheap = clib_mem_set_heap (cm->vlib_main->heap_base);
@@ -712,10 +799,18 @@ acl_hook_l2_input_classify (acl_main_t * am, u32 sw_if_index)
       goto done;
     }
 
+  if (intf_has_etype_whitelist (am, sw_if_index, 1))
+    {
+      acl_classify_add_del_table_tiny (cm, ethertype_mask, sizeof (ethertype_mask) - 1, ~0, 0, /* drop if no match */
+                                      &etype_table_index, 1);
+      etype_whitelist_add_sessions (am, sw_if_index, 1, etype_table_index);
+    }
+
   rv =
     acl_classify_add_del_table_tiny (cm, dot1ad_5tuple_mask,
-                                    sizeof (dot1ad_5tuple_mask) - 1, ~0,
-                                    ~0, &dot1ad_table_index, 1);
+                                    sizeof (dot1ad_5tuple_mask) - 1,
+                                    etype_table_index, ~0,
+                                    &dot1ad_table_index, 1);
   rv =
     acl_classify_add_del_table_tiny (cm, dot1q_5tuple_mask,
                                     sizeof (dot1q_5tuple_mask) - 1,
@@ -774,6 +869,10 @@ acl_hook_l2_input_classify (acl_main_t * am, u32 sw_if_index)
     dot1q_table_index;
   am->acl_dot1ad_input_classify_table_by_sw_if_index[sw_if_index] =
     dot1ad_table_index;
+  am->acl_dot1ad_input_classify_table_by_sw_if_index[sw_if_index] =
+    dot1ad_table_index;
+  am->acl_etype_input_classify_table_by_sw_if_index[sw_if_index] =
+    etype_table_index;
 
   vnet_l2_input_classify_enable_disable (sw_if_index, 1);
 done:
@@ -789,6 +888,7 @@ acl_hook_l2_output_classify (acl_main_t * am, u32 sw_if_index)
   u32 ip6_table_index = ~0;
   u32 dot1q_table_index = ~0;
   u32 dot1ad_table_index = ~0;
+  u32 etype_table_index = ~0;
   int rv;
 
   void *prevheap = clib_mem_set_heap (cm->vlib_main->heap_base);
@@ -816,10 +916,19 @@ acl_hook_l2_output_classify (acl_main_t * am, u32 sw_if_index)
       goto done;
     }
 
+  if (intf_has_etype_whitelist (am, sw_if_index, 0))
+    {
+      acl_classify_add_del_table_tiny (cm, ethertype_mask, sizeof (ethertype_mask) - 1, ~0, 0, /* drop if no match */
+                                      &etype_table_index, 1);
+      etype_whitelist_add_sessions (am, sw_if_index, 0, etype_table_index);
+    }
+
+
   rv =
     acl_classify_add_del_table_tiny (cm, dot1ad_5tuple_mask,
-                                    sizeof (dot1ad_5tuple_mask) - 1, ~0,
-                                    ~0, &dot1ad_table_index, 1);
+                                    sizeof (dot1ad_5tuple_mask) - 1,
+                                    etype_table_index, ~0,
+                                    &dot1ad_table_index, 1);
   rv =
     acl_classify_add_del_table_tiny (cm, dot1q_5tuple_mask,
                                     sizeof (dot1q_5tuple_mask) - 1,
@@ -881,6 +990,8 @@ acl_hook_l2_output_classify (acl_main_t * am, u32 sw_if_index)
     dot1q_table_index;
   am->acl_dot1ad_output_classify_table_by_sw_if_index[sw_if_index] =
     dot1ad_table_index;
+  am->acl_etype_output_classify_table_by_sw_if_index[sw_if_index] =
+    etype_table_index;
 
   vnet_l2_output_classify_enable_disable (sw_if_index, 1);
 done:
@@ -1187,6 +1298,43 @@ acl_interface_add_del_inout_acl (u32 sw_if_index, u8 is_add, u8 is_input,
   return rv;
 }
 
+static int
+acl_set_etype_whitelists (acl_main_t * am, u32 sw_if_index, u16 * vec_in,
+                         u16 * vec_out)
+{
+  vec_validate (am->input_etype_whitelist_by_sw_if_index, sw_if_index);
+  vec_validate (am->output_etype_whitelist_by_sw_if_index, sw_if_index);
+
+  vec_free (am->input_etype_whitelist_by_sw_if_index[sw_if_index]);
+  vec_free (am->output_etype_whitelist_by_sw_if_index[sw_if_index]);
+
+  am->input_etype_whitelist_by_sw_if_index[sw_if_index] = vec_in;
+  am->output_etype_whitelist_by_sw_if_index[sw_if_index] = vec_out;
+
+  /*
+   * if there are already inbound/outbound ACLs applied, toggle the
+   * enable/disable - this will recreate the necessary tables.
+   */
+
+  if (vec_len (am->input_acl_vec_by_sw_if_index) > sw_if_index)
+    {
+      if (vec_len (am->input_acl_vec_by_sw_if_index[sw_if_index]) > 0)
+       {
+         acl_interface_in_enable_disable (am, sw_if_index, 0);
+         acl_interface_in_enable_disable (am, sw_if_index, 1);
+       }
+    }
+  if (vec_len (am->output_acl_vec_by_sw_if_index) > sw_if_index)
+    {
+      if (vec_len (am->output_acl_vec_by_sw_if_index[sw_if_index]) > 0)
+       {
+         acl_interface_out_enable_disable (am, sw_if_index, 0);
+         acl_interface_out_enable_disable (am, sw_if_index, 1);
+       }
+    }
+  return 0;
+}
+
 
 typedef struct
 {
@@ -2499,6 +2647,38 @@ static void
     }
 }
 
+static void
+  vl_api_acl_interface_set_etype_whitelist_t_handler
+  (vl_api_acl_interface_set_etype_whitelist_t * mp)
+{
+  acl_main_t *am = &acl_main;
+  vl_api_acl_interface_set_etype_whitelist_reply_t *rmp;
+  int rv = 0;
+  int i;
+  vnet_interface_main_t *im = &am->vnet_main->interface_main;
+  u32 sw_if_index = ntohl (mp->sw_if_index);
+  u16 *vec_in = 0, *vec_out = 0;
+  void *oldheap = acl_set_heap (am);
+
+  if (pool_is_free_index (im->sw_interfaces, sw_if_index))
+    rv = VNET_API_ERROR_INVALID_SW_IF_INDEX;
+  else
+    {
+      for (i = 0; i < mp->count; i++)
+       {
+         if (i < mp->n_input)
+           vec_add1 (vec_in, ntohs (mp->whitelist[i]));
+         else
+           vec_add1 (vec_out, ntohs (mp->whitelist[i]));
+       }
+      rv = acl_set_etype_whitelists (am, sw_if_index, vec_in, vec_out);
+    }
+
+  clib_mem_set_heap (oldheap);
+  REPLY_MACRO (VL_API_ACL_INTERFACE_SET_ETYPE_WHITELIST_REPLY);
+}
+
+
 /* Set up the API message handling tables */
 static clib_error_t *
 acl_plugin_api_hookup (vlib_main_t * vm)
@@ -3013,6 +3193,19 @@ acl_plugin_show_interface (acl_main_t * am, u32 sw_if_index, int show_acl)
 
       vlib_cli_output (vm, "sw_if_index %d:\n", swi);
 
+      if (intf_has_etype_whitelist (am, swi, 1))
+       {
+         vlib_cli_output (vm, "  input etype whitelist: %U", format_vec16,
+                          am->input_etype_whitelist_by_sw_if_index[swi],
+                          "%04x");
+       }
+      if (intf_has_etype_whitelist (am, swi, 0))
+       {
+         vlib_cli_output (vm, " output etype whitelist: %U", format_vec16,
+                          am->output_etype_whitelist_by_sw_if_index[swi],
+                          "%04x");
+       }
+
       if ((swi < vec_len (am->input_acl_vec_by_sw_if_index)) &&
          (vec_len (am->input_acl_vec_by_sw_if_index[swi]) > 0))
        {
index 263867b..07ed868 100644 (file)
@@ -188,6 +188,12 @@ typedef struct {
   u32 *acl_dot1q_output_classify_table_by_sw_if_index;
   u32 *acl_dot1ad_output_classify_table_by_sw_if_index;
 
+  u32 *acl_etype_input_classify_table_by_sw_if_index;
+  u32 *acl_etype_output_classify_table_by_sw_if_index;
+
+  u16 **input_etype_whitelist_by_sw_if_index;
+  u16 **output_etype_whitelist_by_sw_if_index;
+
   /* MACIP (input) ACLs associated with the interfaces */
   u32 *macip_acl_by_sw_if_index;
 
index 90fd499..c264034 100644 (file)
@@ -67,6 +67,7 @@ _(acl_del_reply) \
 _(acl_interface_add_del_reply) \
 _(macip_acl_interface_add_del_reply) \
 _(acl_interface_set_acl_list_reply) \
+_(acl_interface_set_etype_whitelist_reply) \
 _(macip_acl_del_reply)
 
 #define foreach_reply_retval_aclindex_handler  \
@@ -269,6 +270,7 @@ _(ACL_ADD_REPLACE_REPLY, acl_add_replace_reply) \
 _(ACL_DEL_REPLY, acl_del_reply) \
 _(ACL_INTERFACE_ADD_DEL_REPLY, acl_interface_add_del_reply)  \
 _(ACL_INTERFACE_SET_ACL_LIST_REPLY, acl_interface_set_acl_list_reply) \
+_(ACL_INTERFACE_SET_ETYPE_WHITELIST_REPLY, acl_interface_set_etype_whitelist_reply) \
 _(ACL_INTERFACE_LIST_DETAILS, acl_interface_list_details)  \
 _(ACL_DETAILS, acl_details)  \
 _(MACIP_ACL_ADD_REPLY, macip_acl_add_reply) \
@@ -746,6 +748,63 @@ static int api_acl_interface_set_acl_list (vat_main_t * vam)
     return ret;
 }
 
+static int api_acl_interface_set_etype_whitelist (vat_main_t * vam)
+{
+    unformat_input_t * i = vam->input;
+    vl_api_acl_interface_set_etype_whitelist_t * mp;
+    u32 sw_if_index = ~0;
+    u32 ethertype = ~0;
+    u16 *etypes_in = 0;
+    u16 *etypes_out = 0;
+    u8 is_input = 1;
+    int ret;
+
+//  acl_interface_set_etype_whitelist <intfc> | sw_if_index <if-idx> input [ethertype list] output [ethertype list]
+
+    /* Parse args required to build the message */
+    while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) {
+        if (unformat (i, "%U", unformat_sw_if_index, vam, &sw_if_index))
+            ;
+        else if (unformat (i, "sw_if_index %d", &sw_if_index))
+            ;
+        else if (unformat (i, "%x", &ethertype))
+          {
+            ethertype = ethertype & 0xffff;
+            if(is_input)
+              vec_add1(etypes_in, htons(ethertype));
+            else
+              vec_add1(etypes_out, htons(ethertype));
+          }
+        else if (unformat (i, "input"))
+            is_input = 1;
+        else if (unformat (i, "output"))
+            is_input = 0;
+        else
+            break;
+    }
+
+    if (sw_if_index == ~0) {
+        errmsg ("missing interface name / explicit sw_if_index number \n");
+        return -99;
+    }
+
+    /* Construct the API message */
+    M2(ACL_INTERFACE_SET_ETYPE_WHITELIST, mp, sizeof(u32) * (vec_len(etypes_in) + vec_len(etypes_out)));
+    mp->sw_if_index = ntohl(sw_if_index);
+    mp->n_input = vec_len(etypes_in);
+    mp->count = vec_len(etypes_in) + vec_len(etypes_out);
+    vec_append(etypes_in, etypes_out);
+    if (vec_len(etypes_in) > 0)
+      clib_memcpy(mp->whitelist, etypes_in, vec_len(etypes_in)*sizeof(etypes_in[0]));
+
+    /* send it... */
+    S(mp);
+
+    /* Wait for a reply... */
+    W (ret);
+    return ret;
+}
+
 static void
 api_acl_send_control_ping(vat_main_t *vam)
 {
@@ -1163,6 +1222,7 @@ _(acl_del, "<acl-idx>") \
 _(acl_dump, "[<acl-idx>]") \
 _(acl_interface_add_del, "<intfc> | sw_if_index <if-idx> [add|del] [input|output] acl <acl-idx>") \
 _(acl_interface_set_acl_list, "<intfc> | sw_if_index <if-idx> input [acl-idx list] output [acl-idx list]") \
+_(acl_interface_set_etype_whitelist, "<intfc> | sw_if_index <if-idx> input [ethertype list] output [ethertype list]") \
 _(acl_interface_list_dump, "[<intfc> | sw_if_index <if-idx>]") \
 _(macip_acl_add, "...") \
 _(macip_acl_add_replace, "<acl-idx> [<ipv4|ipv6> <permit|deny|action N> [count <count>] [src] ip <ipaddress/[plen]> mac <mac> mask <mac_mask>, ... , ...") \
index e00f1ab..6ab014c 100644 (file)
@@ -359,6 +359,30 @@ vl_api_acl_interface_set_acl_list_t_print (vl_api_acl_interface_set_acl_list_t
   return handle;
 }
 
+static inline void *
+vl_api_acl_interface_set_etype_whitelist_t_print (vl_api_acl_interface_set_etype_whitelist_t
+                                          * a, void *handle)
+{
+  u8 *s;
+  int i;
+
+  s = format
+    (0, "SCRIPT: acl_interface_set_etype_whitelist sw_if_index %d count %d\n",
+     clib_net_to_host_u32 (a->sw_if_index), (u32) a->count);
+
+  s = format (s, "    input ");
+
+  for (i = 0; i < a->count; i++)
+    {
+      if (i == a->n_input)
+        s = format (s, "output ");
+      s = format (s, "%x ", clib_net_to_host_u16 (a->whitelist[i]));
+    }
+
+  PRINT_S;
+  return handle;
+}
+
 static inline void *
 vl_api_acl_interface_add_del_t_print (vl_api_acl_interface_add_del_t * a,
                                      void *handle)
index 361ced1..5fcf09c 100644 (file)
@@ -247,6 +247,15 @@ class TestACLplugin(VppTestCase):
                                                  acls=[reply.acl_index])
         return
 
+    def etype_whitelist(self, whitelist, n_input):
+        # Apply whitelists on all the interfaces
+        for i in self.pg_interfaces:
+            # checkstyle can't read long names. Help them.
+            fun = self.vapi.acl_interface_set_etype_whitelist
+            fun(sw_if_index=i.sw_if_index, n_input=n_input,
+                whitelist=whitelist)
+        return
+
     def create_upper_layer(self, packet_index, proto, ports=0):
         p = self.proto_map[proto]
         if p == 'UDP':
@@ -268,7 +277,8 @@ class TestACLplugin(VppTestCase):
         return ''
 
     def create_stream(self, src_if, packet_sizes, traffic_type=0, ipv6=0,
-                      proto=-1, ports=0, fragments=False, pkt_raw=True):
+                      proto=-1, ports=0, fragments=False,
+                      pkt_raw=True, etype=-1):
         """
         Create input packet stream for defined interface using hosts or
         deleted_hosts list.
@@ -300,6 +310,10 @@ class TestACLplugin(VppTestCase):
                         pkt_info.proto = proto
                     payload = self.info_to_payload(pkt_info)
                     p = Ether(dst=dst_host.mac, src=src_host.mac)
+                    if etype > 0:
+                        p = Ether(dst=dst_host.mac,
+                                  src=src_host.mac,
+                                  type=etype)
                     if pkt_info.ip:
                         p /= IPv6(dst=dst_host.ip6, src=src_host.ip6)
                         if fragments:
@@ -328,7 +342,8 @@ class TestACLplugin(VppTestCase):
                     pkts.append(p)
         return pkts
 
-    def verify_capture(self, pg_if, capture, traffic_type=0, ip_type=0):
+    def verify_capture(self, pg_if, capture,
+                       traffic_type=0, ip_type=0, etype=-1):
         """
         Verify captured input packet stream for defined interface.
 
@@ -341,6 +356,12 @@ class TestACLplugin(VppTestCase):
             last_info[i.sw_if_index] = None
         dst_sw_if_index = pg_if.sw_if_index
         for packet in capture:
+            if etype > 0:
+                if packet[Ether].type != etype:
+                    self.logger.error(ppp("Unexpected ethertype in packet:",
+                                          packet))
+                else:
+                    continue
             try:
                 # Raw data for ICMPv6 are stored in ICMPv6EchoRequest.data
                 if traffic_type == self.ICMP and ip_type == self.IPV6:
@@ -429,7 +450,7 @@ class TestACLplugin(VppTestCase):
         self.pg_start()
 
     def run_verify_test(self, traffic_type=0, ip_type=0, proto=-1, ports=0,
-                        frags=False, pkt_raw=True):
+                        frags=False, pkt_raw=True, etype=-1):
         # Test
         # Create incoming packet streams for packet-generator interfaces
         pkts_cnt = 0
@@ -437,7 +458,7 @@ class TestACLplugin(VppTestCase):
             if self.flows.__contains__(i):
                 pkts = self.create_stream(i, self.pg_if_packet_sizes,
                                           traffic_type, ip_type, proto, ports,
-                                          frags, pkt_raw)
+                                          frags, pkt_raw, etype)
                 if len(pkts) > 0:
                     i.add_stream(pkts)
                     pkts_cnt += len(pkts)
@@ -454,17 +475,18 @@ class TestACLplugin(VppTestCase):
                     capture = dst_if.get_capture(pkts_cnt)
                     self.logger.info("Verifying capture on interface %s" %
                                      dst_if.name)
-                    self.verify_capture(dst_if, capture, traffic_type, ip_type)
+                    self.verify_capture(dst_if, capture,
+                                        traffic_type, ip_type, etype)
 
     def run_verify_negat_test(self, traffic_type=0, ip_type=0, proto=-1,
-                              ports=0, frags=False):
+                              ports=0, frags=False, etype=-1):
         # Test
         self.reset_packet_infos()
         for i in self.pg_interfaces:
             if self.flows.__contains__(i):
                 pkts = self.create_stream(i, self.pg_if_packet_sizes,
                                           traffic_type, ip_type, proto, ports,
-                                          frags)
+                                          frags, True, etype)
                 if len(pkts) > 0:
                     i.add_stream(pkts)
 
@@ -1306,6 +1328,70 @@ class TestACLplugin(VppTestCase):
 
         self.logger.info("ACLP_TEST_FINISH_0113")
 
+    def test_0300_tcp_permit_v4_etype_aaaa(self):
+        """ permit TCPv4, send 0xAAAA etype
+        """
+        self.logger.info("ACLP_TEST_START_0300")
+
+        # Add an ACL
+        rules = []
+        rules.append(self.create_rule(self.IPV4, self.DENY, self.PORTS_RANGE_2,
+                     self.proto[self.IP][self.TCP]))
+        rules.append(self.create_rule(self.IPV4, self.PERMIT, self.PORTS_RANGE,
+                     self.proto[self.IP][self.TCP]))
+        # deny ip any any in the end
+        rules.append(self.create_rule(self.IPV4, self.DENY, self.PORTS_ALL, 0))
+
+        # Apply rules
+        self.apply_rules(rules, "permit ipv4 tcp")
+
+        # Traffic should still pass
+        self.run_verify_test(self.IP, self.IPV4, self.proto[self.IP][self.TCP])
+
+        # Traffic should still pass also for an odd ethertype
+        self.run_verify_test(self.IP, self.IPV4, self.proto[self.IP][self.TCP],
+                             0, False, True, 0xaaaa)
+
+        self.logger.info("ACLP_TEST_FINISH_0300")
+
+    def test_0305_tcp_permit_v4_etype_blacklist_aaaa(self):
+        """ permit TCPv4, whitelist 0x0BBB ethertype, send 0xAAAA, 0x0BBB
+        """
+        self.logger.info("ACLP_TEST_START_0305")
+
+        # Add an ACL
+        rules = []
+        rules.append(self.create_rule(self.IPV4, self.DENY, self.PORTS_RANGE_2,
+                     self.proto[self.IP][self.TCP]))
+        rules.append(self.create_rule(self.IPV4, self.PERMIT, self.PORTS_RANGE,
+                     self.proto[self.IP][self.TCP]))
+        # deny ip any any in the end
+        rules.append(self.create_rule(self.IPV4, self.DENY, self.PORTS_ALL, 0))
+
+        # Apply rules
+        self.apply_rules(rules, "permit ipv4 tcp")
+
+        # whitelist the 0xbbbb etype - so the 0xaaaa should be blocked
+        self.etype_whitelist([0xbbb], 1)
+
+        # The IPv4 traffic should still pass
+        self.run_verify_test(self.IP, self.IPV4, self.proto[self.IP][self.TCP])
+
+        # The oddball ethertype should be blocked
+        self.run_verify_negat_test(self.IP, self.IPV4,
+                                   self.proto[self.IP][self.TCP],
+                                   0, False, 0xaaaa)
+
+        # The whitelisted traffic, on the other hand, should pass
+        self.run_verify_test(self.IP, self.IPV4, self.proto[self.IP][self.TCP],
+                             0, False, True, 0x0bbb)
+
+        # remove the whitelist, the previously blocked 0xAAAA should pass now
+        self.etype_whitelist([], 0)
+        self.run_verify_test(self.IP, self.IPV4, self.proto[self.IP][self.TCP],
+                             0, False, True, 0xaaaa)
+
+        self.logger.info("ACLP_TEST_FINISH_0305")
 
 if __name__ == '__main__':
     unittest.main(testRunner=VppTestRunner)
index 0e56af1..a10b777 100644 (file)
@@ -2680,6 +2680,16 @@ class VppPapiProvider(object):
                          'acls': acls},
                         expected_retval=expected_retval)
 
+    def acl_interface_set_etype_whitelist(self, sw_if_index,
+                                          n_input, whitelist,
+                                          expected_retval=0):
+        return self.api(self.papi.acl_interface_set_etype_whitelist,
+                        {'sw_if_index': sw_if_index,
+                         'count': len(whitelist),
+                         'n_input': n_input,
+                         'whitelist': whitelist},
+                        expected_retval=expected_retval)
+
     def acl_interface_add_del(self,
                               sw_if_index,
                               acl_index,