gbp: refactor policy nodes 21/20521/4
authorBenoît Ganne <bganne@cisco.com>
Wed, 3 Jul 2019 15:34:21 +0000 (17:34 +0200)
committerNeale Ranns <nranns@cisco.com>
Mon, 8 Jul 2019 07:46:49 +0000 (07:46 +0000)
Refactor both policy and policy-dpo nodes so they share the same code
for contract & acl lookup and for tracing.
This should help to implement new policy schemes.

Type: refactor

Change-Id: If5704bda708838eb01516dd39473d9bf248cfdf6
Signed-off-by: Benoît Ganne <bganne@cisco.com>
src/plugins/gbp/gbp.h
src/plugins/gbp/gbp_api.c
src/plugins/gbp/gbp_contract.c
src/plugins/gbp/gbp_contract.h
src/plugins/gbp/gbp_policy.c
src/plugins/gbp/gbp_policy.h [new file with mode: 0644]
src/plugins/gbp/gbp_policy_dpo.c
src/plugins/gbp/gbp_policy_node.c

index e194a9d..50039b3 100644 (file)
@@ -36,7 +36,6 @@
 #include <plugins/gbp/gbp_types.h>
 #include <plugins/gbp/gbp_endpoint.h>
 #include <plugins/gbp/gbp_endpoint_group.h>
-#include <plugins/gbp/gbp_contract.h>
 #include <plugins/gbp/gbp_subnet.h>
 #include <plugins/gbp/gbp_recirc.h>
 
index fca85a7..f487e16 100644 (file)
@@ -31,6 +31,7 @@
 #include <gbp/gbp_bridge_domain.h>
 #include <gbp/gbp_route_domain.h>
 #include <gbp/gbp_ext_itf.h>
+#include <gbp/gbp_contract.h>
 
 #include <vlibapi/api.h>
 #include <vlibmemory/api.h>
index a24edb3..c7c2378 100644 (file)
 #include <plugins/gbp/gbp_bridge_domain.h>
 #include <plugins/gbp/gbp_route_domain.h>
 #include <plugins/gbp/gbp_policy_dpo.h>
+#include <plugins/gbp/gbp_contract.h>
 
 #include <vnet/dpo/load_balance.h>
 #include <vnet/dpo/drop_dpo.h>
 
+char *gbp_contract_error_strings[] = {
+#define _(sym,string) string,
+  foreach_gbp_contract_error
+#undef _
+};
+
 /**
  * Single contract DB instance
  */
index 8e621a4..c87af52 100644 (file)
 #ifndef __GBP_CONTRACT_H__
 #define __GBP_CONTRACT_H__
 
+#include <plugins/gbp/gbp.h>
 #include <plugins/gbp/gbp_types.h>
 
-#define foreach_gbp_policy_error                           \
+#define foreach_gbp_contract_error                         \
   _(ALLOW_NO_SCLASS,    "allow-no-sclass")                 \
   _(ALLOW_INTRA,        "allow-intra-sclass")              \
   _(ALLOW_A_BIT,        "allow-a-bit-set")                 \
   _(DROP_NO_CONTRACT,   "drop-no-contract")                \
   _(DROP_NO_DCLASS,     "drop-no-dclass")
 
+typedef enum
+{
+#define _(sym,str) GBP_CONTRACT_ERROR_##sym,
+  foreach_gbp_contract_error
+#undef _
+    GBP_CONTRACT_N_ERROR,
+#define GBP_CONTRACT_N_ERROR GBP_CONTRACT_N_ERROR
+} gbp_contract_error_t;
+
+extern char *gbp_contract_error_strings[GBP_CONTRACT_N_ERROR];
+
 /**
  * The key for an Contract
  */
@@ -207,8 +219,129 @@ gbp_rule_get (index_t gui)
 extern vlib_combined_counter_main_t gbp_contract_permit_counters;
 extern vlib_combined_counter_main_t gbp_contract_drop_counters;
 
-#endif
+typedef enum
+{
+  GBP_CONTRACT_APPLY_L2,
+  GBP_CONTRACT_APPLY_IP4,
+  GBP_CONTRACT_APPLY_IP6,
+} gbp_contract_apply_type_t;
+
+static_always_inline gbp_rule_action_t
+gbp_contract_apply (vlib_main_t * vm, gbp_main_t * gm,
+                   gbp_contract_key_t * key, vlib_buffer_t * b,
+                   gbp_rule_t ** rule, u32 * intra, u32 * sclass1,
+                   gbp_contract_error_t * err,
+                   gbp_contract_apply_type_t type)
+{
+  fa_5tuple_opaque_t fa_5tuple;
+  const gbp_contract_t *contract;
+  index_t contract_index;
+  u32 acl_pos, acl_match, rule_match, trace_bitmap;
+  u16 etype;
+  u8 ip6, action;
+
+  *rule = 0;
+
+  if (key->gck_src == key->gck_dst)
+    {
+      /* intra-epg allowed */
+      (*intra)++;
+      *err = GBP_CONTRACT_ERROR_ALLOW_INTRA;
+      return GBP_RULE_PERMIT;
+    }
+
+  if (1 == key->gck_src || 1 == key->gck_dst)
+    {
+      /* sclass 1 allowed */
+      (*sclass1)++;
+      *err = GBP_CONTRACT_ERROR_ALLOW_SCLASS_1;
+      return GBP_RULE_PERMIT;
+    }
+
+  /* look for contract */
+  contract_index = gbp_contract_find (key);
+  if (INDEX_INVALID == contract_index)
+    {
+      *err = GBP_CONTRACT_ERROR_DROP_NO_CONTRACT;
+      return GBP_RULE_DENY;
+    }
+
+  contract = gbp_contract_get (contract_index);
+
+  *err = GBP_CONTRACT_ERROR_DROP_CONTRACT;
+
+  switch (type)
+    {
+    case GBP_CONTRACT_APPLY_IP4:
+      ip6 = 0;
+      break;
+    case GBP_CONTRACT_APPLY_IP6:
+      ip6 = 1;
+      break;
+    case GBP_CONTRACT_APPLY_L2:
+      {
+       /* check ethertype */
+       etype =
+         ((u16 *) (vlib_buffer_get_current (b) +
+                   vnet_buffer (b)->l2.l2_len))[-1];
+
+       if (~0 == vec_search (contract->gc_allowed_ethertypes, etype))
+         {
+           *err = GBP_CONTRACT_ERROR_DROP_ETHER_TYPE;
+           goto contract_deny;
+         }
+
+       switch (clib_net_to_host_u16 (etype))
+         {
+         case ETHERNET_TYPE_IP4:
+           ip6 = 0;
+           break;
+         case ETHERNET_TYPE_IP6:
+           ip6 = 1;
+           break;
+         default:
+           goto contract_deny;
+         }
+      }
+      break;
+    }
+
+  /* check ACL */
+  action = 0;
+  acl_plugin_fill_5tuple_inline (gm->acl_plugin.p_acl_main,
+                                contract->gc_lc_index, b, ip6,
+                                0 /* is_input */ ,
+                                GBP_CONTRACT_APPLY_L2 ==
+                                type /* is_l2_path */ , &fa_5tuple);
+  acl_plugin_match_5tuple_inline (gm->acl_plugin.p_acl_main,
+                                 contract->gc_lc_index, &fa_5tuple, ip6,
+                                 &action, &acl_pos, &acl_match, &rule_match,
+                                 &trace_bitmap);
+  if (action <= 0)
+    goto contract_deny;
+
+  *rule = gbp_rule_get (contract->gc_rules[rule_match]);
+  switch ((*rule)->gu_action)
+    {
+    case GBP_RULE_PERMIT:
+    case GBP_RULE_REDIRECT:
+      *err = GBP_CONTRACT_ERROR_ALLOW_CONTRACT;
+      vlib_increment_combined_counter (&gbp_contract_permit_counters,
+                                      vm->thread_index, contract_index, 1,
+                                      vlib_buffer_length_in_chain (vm, b));
+      return (*rule)->gu_action;
+    case GBP_RULE_DENY:
+      break;
+    }
+
+contract_deny:
+  vlib_increment_combined_counter (&gbp_contract_drop_counters,
+                                  vm->thread_index, contract_index, 1,
+                                  vlib_buffer_length_in_chain (vm, b));
+  return GBP_RULE_DENY;
+}
 
+#endif /* __GBP_CONTRACT_H__ */
 /*
  * fd.io coding-style-patch-verification: ON
  *
index 0f26701..21e4bdb 100644 (file)
  */
 
 #include <plugins/gbp/gbp.h>
-#include <plugins/gbp/gbp_policy_dpo.h>
+#include <plugins/gbp/gbp_policy.h>
+#include <vnet/vxlan-gbp/vxlan_gbp_packet.h>
 
 gbp_policy_main_t gbp_policy_main;
 
+/* packet trace format function */
+u8 *
+format_gbp_policy_trace (u8 * s, va_list * args)
+{
+  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+  gbp_policy_trace_t *t = va_arg (*args, gbp_policy_trace_t *);
+
+  s =
+    format (s, "scope:%d sclass:%d, dclass:%d, allowed:%d flags:%U", t->scope,
+           t->sclass, t->dclass, t->allowed, format_vxlan_gbp_header_gpflags,
+           t->flags);
+
+  return s;
+}
+
 static clib_error_t *
 gbp_policy_init (vlib_main_t * vm)
 {
diff --git a/src/plugins/gbp/gbp_policy.h b/src/plugins/gbp/gbp_policy.h
new file mode 100644 (file)
index 0000000..36bb493
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __GBP_POLICY_H__
+#define __GBP_POLICY_H__
+
+#include <plugins/gbp/gbp_contract.h>
+
+/**
+ * per-packet trace data
+ */
+typedef struct gbp_policy_trace_t_
+{
+  /* per-pkt trace data */
+  gbp_scope_t scope;
+  sclass_t sclass;
+  sclass_t dclass;
+  u32 allowed;
+  u32 flags;
+} gbp_policy_trace_t;
+
+/* packet trace format function */
+u8 * format_gbp_policy_trace (u8 * s, va_list * args);
+
+static_always_inline void
+gbp_policy_trace(vlib_main_t * vm, vlib_node_runtime_t * node, vlib_buffer_t *b, const gbp_contract_key_t *key, u8 allowed)
+{
+  gbp_policy_trace_t *t;
+
+  if (PREDICT_TRUE (!(b->flags & VLIB_BUFFER_IS_TRACED)))
+    return;
+
+  t = vlib_add_trace (vm, node, b, sizeof (*t));
+  t->sclass = key->gck_src;
+  t->dclass = key->gck_dst;
+  t->scope = key->gck_scope;
+  t->allowed = allowed;
+  t->flags = vnet_buffer2 (b)->gbp.flags;
+}
+
+#endif /* __GBP_POLICY_H__ */
index 8152315..dec30e4 100644 (file)
 #include <vnet/vxlan-gbp/vxlan_gbp.h>
 
 #include <plugins/gbp/gbp.h>
+#include <plugins/gbp/gbp_policy.h>
 #include <plugins/gbp/gbp_policy_dpo.h>
 #include <plugins/gbp/gbp_recirc.h>
+#include <plugins/gbp/gbp_contract.h>
 
 #ifndef CLIB_MARCH_VARIANT
 /**
@@ -220,31 +222,6 @@ gbp_policy_dpo_module_init (vlib_main_t * vm)
 VLIB_INIT_FUNCTION (gbp_policy_dpo_module_init);
 #endif /* CLIB_MARCH_VARIANT */
 
-typedef enum
-{
-#define _(sym,str) GBP_POLICY_DPO_ERROR_##sym,
-  foreach_gbp_policy_error
-#undef _
-    GBP_POLICY_N_ERROR,
-} gbp_policy_dpo_error_t;
-
-static char *gbp_policy_dpo_error_strings[] = {
-#define _(sym,string) string,
-  foreach_gbp_policy_error
-#undef _
-};
-
-typedef struct gbp_policy_dpo_trace_t_
-{
-  gbp_scope_t scope;
-  sclass_t sclass;
-  sclass_t dclass;
-  u32 acl_index;
-  u32 flags;
-  u32 action;
-  u32 gci;
-} gbp_policy_dpo_trace_t;
-
 typedef enum
 {
   GBP_POLICY_DROP,
@@ -274,14 +251,12 @@ gbp_policy_dpo_inline (vlib_main_t * vm,
                       vlib_frame_t * from_frame, u8 is_ip6)
 {
   gbp_main_t *gm = &gbp_main;
-  u32 n_left_from, next_index, *from, *to_next, thread_index;
+  u32 n_left_from, next_index, *from, *to_next;
   u32 n_allow_intra, n_allow_a_bit, n_allow_sclass_1;
-  gbp_rule_t *gu;
 
   from = vlib_frame_vector_args (from_frame);
   n_left_from = from_frame->n_vectors;
   n_allow_intra = n_allow_a_bit = n_allow_sclass_1 = 0;
-  thread_index = vm->thread_index;
 
   next_index = node->cached_next_index;
 
@@ -294,14 +269,13 @@ gbp_policy_dpo_inline (vlib_main_t * vm,
       while (n_left_from > 0 && n_left_to_next > 0)
        {
          const gbp_policy_dpo_t *gpd0;
+         gbp_rule_action_t action0;
+         gbp_contract_error_t err0;
          u32 bi0, next0;
          gbp_contract_key_t key0;
-         gbp_contract_t *gc0;
          vlib_buffer_t *b0;
-         index_t gci0;
-         u8 action0;
+         gbp_rule_t *rule0;
 
-         action0 = 0;
          bi0 = from[0];
          to_next[0] = bi0;
          from += 1;
@@ -312,7 +286,6 @@ gbp_policy_dpo_inline (vlib_main_t * vm,
 
          b0 = vlib_get_buffer (vm, bi0);
 
-         gc0 = NULL;
          gpd0 = gbp_policy_dpo_get (vnet_buffer (b0)->ip.adj_index[VLIB_TX]);
          vnet_buffer (b0)->ip.adj_index[VLIB_TX] = gpd0->gpd_dpo.dpoi_index;
 
@@ -333,130 +306,47 @@ gbp_policy_dpo_inline (vlib_main_t * vm,
              goto trace;
            }
 
+         /* zero out the key to ensure the pad space is clear */
          key0.as_u64 = 0;
-         key0.gck_scope = gpd0->gpd_scope;
          key0.gck_src = vnet_buffer2 (b0)->gbp.sclass;
-         key0.gck_dst = gpd0->gpd_sclass;
 
-         if (SCLASS_INVALID != key0.gck_src)
-           {
-             if (PREDICT_FALSE (key0.gck_src == key0.gck_dst))
-               {
-                 /*
-                  * intra-epg allowed
-                  */
-                 next0 = gpd0->gpd_dpo.dpoi_next_node;
-                 vnet_buffer2 (b0)->gbp.flags |= VXLAN_GBP_GPFLAGS_A;
-                 n_allow_intra++;
-                 action0 = 0;
-               }
-             else if (PREDICT_FALSE (key0.gck_src == 1 || key0.gck_dst == 1))
-               {
-                 /*
-                  * sclass or dclass 1 allowed
-                  */
-                 next0 = gpd0->gpd_dpo.dpoi_next_node;
-                 vnet_buffer2 (b0)->gbp.flags |= VXLAN_GBP_GPFLAGS_A;
-                 n_allow_sclass_1++;
-                 action0 = 0;
-               }
-             else
-               {
-                 gci0 = gbp_contract_find (&key0);
-
-                 if (INDEX_INVALID != gci0)
-                   {
-                     fa_5tuple_opaque_t pkt_5tuple0;
-                     u32 acl_pos_p0, acl_match_p0;
-                     u32 rule_match_p0, trace_bitmap0;
-                     /*
-                      * tests against the ACL
-                      */
-                     gc0 = gbp_contract_get (gci0);
-                     acl_plugin_fill_5tuple_inline (gm->
-                                                    acl_plugin.p_acl_main,
-                                                    gc0->gc_lc_index, b0,
-                                                    is_ip6,
-                                                    /* is_input */ 1,
-                                                    /* is_l2_path */ 0,
-                                                    &pkt_5tuple0);
-                     acl_plugin_match_5tuple_inline (gm->
-                                                     acl_plugin.p_acl_main,
-                                                     gc0->gc_lc_index,
-                                                     &pkt_5tuple0, is_ip6,
-                                                     &action0, &acl_pos_p0,
-                                                     &acl_match_p0,
-                                                     &rule_match_p0,
-                                                     &trace_bitmap0);
-
-                     if (action0 > 0)
-                       {
-                         vnet_buffer2 (b0)->gbp.flags |= VXLAN_GBP_GPFLAGS_A;
-                         gu = gbp_rule_get (gc0->gc_rules[rule_match_p0]);
-                         action0 = gu->gu_action;
-
-                         switch (gu->gu_action)
-                           {
-                           case GBP_RULE_PERMIT:
-                             next0 = gpd0->gpd_dpo.dpoi_next_node;
-                             break;
-                           case GBP_RULE_DENY:
-                             next0 = GBP_POLICY_DROP;
-                             break;
-                           case GBP_RULE_REDIRECT:
-                             next0 = gbp_rule_l3_redirect (gu, b0, is_ip6);
-                             break;
-                           }
-                       }
-                     if (next0 == GBP_POLICY_DROP)
-                       {
-                         vlib_increment_combined_counter
-                           (&gbp_contract_drop_counters,
-                            thread_index,
-                            gci0, 1, vlib_buffer_length_in_chain (vm, b0));
-                         b0->error =
-                           node->errors[GBP_POLICY_DPO_ERROR_DROP_CONTRACT];
-                       }
-                     else
-                       {
-                         vlib_increment_combined_counter
-                           (&gbp_contract_permit_counters,
-                            thread_index,
-                            gci0, 1, vlib_buffer_length_in_chain (vm, b0));
-                       }
-
-                   }
-                 else
-                   {
-                     b0->error =
-                       node->errors[GBP_POLICY_DPO_ERROR_DROP_NO_CONTRACT];
-                   }
-               }
-           }
-         else
+         if (SCLASS_INVALID == key0.gck_src)
            {
              /*
               * the src EPG is not set when the packet arrives on an EPG
               * uplink interface and we do not need to apply policy
               */
              next0 = gpd0->gpd_dpo.dpoi_next_node;
+             goto trace;
            }
-       trace:
-         if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
-           {
-             gbp_policy_dpo_trace_t *tr;
 
-             tr = vlib_add_trace (vm, node, b0, sizeof (*tr));
-             tr->scope = key0.gck_scope;
-             tr->sclass = key0.gck_src;
-             tr->dclass = key0.gck_dst;
-             tr->acl_index = (gc0 ? gc0->gc_acl_index : ~0);
-             tr->flags = vnet_buffer2 (b0)->gbp.flags;
-             tr->action = action0;
-             tr->gci = (gc0 ? gc0 - gbp_contract_pool : INDEX_INVALID);
+         key0.gck_scope = gpd0->gpd_scope;
+         key0.gck_dst = gpd0->gpd_sclass;
 
+         action0 =
+           gbp_contract_apply (vm, gm, &key0, b0, &rule0, &n_allow_intra,
+                               &n_allow_sclass_1, &err0,
+                               is_ip6 ? GBP_CONTRACT_APPLY_IP6 :
+                               GBP_CONTRACT_APPLY_IP4);
+         switch (action0)
+           {
+           case GBP_RULE_PERMIT:
+             next0 = gpd0->gpd_dpo.dpoi_next_node;
+             vnet_buffer2 (b0)->gbp.flags |= VXLAN_GBP_GPFLAGS_A;
+             break;
+           case GBP_RULE_REDIRECT:
+             next0 = gbp_rule_l3_redirect (rule0, b0, is_ip6);
+             vnet_buffer2 (b0)->gbp.flags |= VXLAN_GBP_GPFLAGS_A;
+             break;
+           case GBP_RULE_DENY:
+             next0 = GBP_POLICY_DROP;
+             b0->error = node->errors[err0];
+             break;
            }
 
+       trace:
+         gbp_policy_trace (vm, node, b0, &key0, (next0 != GBP_POLICY_DROP));
+
          vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
                                           n_left_to_next, bi0, next0);
        }
@@ -464,33 +354,15 @@ gbp_policy_dpo_inline (vlib_main_t * vm,
     }
 
   vlib_node_increment_counter (vm, node->node_index,
-                              GBP_POLICY_DPO_ERROR_ALLOW_INTRA,
-                              n_allow_intra);
+                              GBP_CONTRACT_ERROR_ALLOW_INTRA, n_allow_intra);
   vlib_node_increment_counter (vm, node->node_index,
-                              GBP_POLICY_DPO_ERROR_ALLOW_A_BIT,
-                              n_allow_a_bit);
+                              GBP_CONTRACT_ERROR_ALLOW_A_BIT, n_allow_a_bit);
   vlib_node_increment_counter (vm, node->node_index,
-                              GBP_POLICY_DPO_ERROR_ALLOW_SCLASS_1,
+                              GBP_CONTRACT_ERROR_ALLOW_SCLASS_1,
                               n_allow_sclass_1);
   return from_frame->n_vectors;
 }
 
-static u8 *
-format_gbp_policy_dpo_trace (u8 * s, va_list * args)
-{
-  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
-  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
-  gbp_policy_dpo_trace_t *t = va_arg (*args, gbp_policy_dpo_trace_t *);
-
-  s =
-    format (s,
-           "scope:%d sclass:%d dclass:%d gci:%d acl-index:%d flags:%U action:%d",
-           t->scope, t->sclass, t->dclass, t->gci, t->acl_index,
-           format_vxlan_gbp_header_gpflags, t->flags, t->action);
-
-  return s;
-}
-
 VLIB_NODE_FN (ip4_gbp_policy_dpo_node) (vlib_main_t * vm,
                                        vlib_node_runtime_t * node,
                                        vlib_frame_t * from_frame)
@@ -509,10 +381,10 @@ VLIB_NODE_FN (ip6_gbp_policy_dpo_node) (vlib_main_t * vm,
 VLIB_REGISTER_NODE (ip4_gbp_policy_dpo_node) = {
     .name = "ip4-gbp-policy-dpo",
     .vector_size = sizeof (u32),
-    .format_trace = format_gbp_policy_dpo_trace,
+    .format_trace = format_gbp_policy_trace,
 
-    .n_errors = ARRAY_LEN(gbp_policy_dpo_error_strings),
-    .error_strings = gbp_policy_dpo_error_strings,
+    .n_errors = ARRAY_LEN(gbp_contract_error_strings),
+    .error_strings = gbp_contract_error_strings,
 
     .n_next_nodes = GBP_POLICY_N_NEXT,
     .next_nodes =
@@ -523,10 +395,10 @@ VLIB_REGISTER_NODE (ip4_gbp_policy_dpo_node) = {
 VLIB_REGISTER_NODE (ip6_gbp_policy_dpo_node) = {
     .name = "ip6-gbp-policy-dpo",
     .vector_size = sizeof (u32),
-    .format_trace = format_gbp_policy_dpo_trace,
+    .format_trace = format_gbp_policy_trace,
 
-    .n_errors = ARRAY_LEN(gbp_policy_dpo_error_strings),
-    .error_strings = gbp_policy_dpo_error_strings,
+    .n_errors = ARRAY_LEN(gbp_contract_error_strings),
+    .error_strings = gbp_contract_error_strings,
 
     .n_next_nodes = GBP_POLICY_N_NEXT,
     .next_nodes =
index 10f5956..fd2e29c 100644 (file)
 
 #include <plugins/gbp/gbp.h>
 #include <plugins/gbp/gbp_classify.h>
+#include <plugins/gbp/gbp_policy.h>
 #include <plugins/gbp/gbp_policy_dpo.h>
 #include <plugins/gbp/gbp_bridge_domain.h>
 #include <plugins/gbp/gbp_ext_itf.h>
+#include <plugins/gbp/gbp_contract.h>
 
 #include <vnet/vxlan-gbp/vxlan_gbp_packet.h>
 #include <vnet/vxlan-gbp/vxlan_gbp.h>
 
-#define foreach_gbp_policy                      \
-  _(DENY,    "deny")                            \
-  _(REFLECTION, "reflection")
-
-typedef enum
-{
-#define _(sym,str) GBP_POLICY_ERROR_##sym,
-  foreach_gbp_policy_error
-#undef _
-    GBP_POLICY_N_ERROR,
-} gbp_policy_error_t;
-
-static char *gbp_policy_error_strings[] = {
-#define _(sym,string) string,
-  foreach_gbp_policy_error
-#undef _
-};
-
 typedef enum
 {
   GBP_POLICY_NEXT_DROP,
   GBP_POLICY_N_NEXT,
 } gbp_policy_next_t;
 
-/**
- * per-packet trace data
- */
-typedef struct gbp_policy_trace_t_
-{
-  /* per-pkt trace data */
-  gbp_scope_t scope;
-  sclass_t sclass;
-  sclass_t dclass;
-  u32 acl_index;
-  u32 allowed;
-  u32 flags;
-} gbp_policy_trace_t;
-
 always_inline dpo_proto_t
 ethertype_to_dpo_proto (u16 etype)
 {
@@ -98,19 +68,6 @@ gbp_rule_l2_redirect (const gbp_rule_t * gu, vlib_buffer_t * b0)
   return (dpo->dpoi_next_node);
 }
 
-always_inline u8
-gbp_policy_is_ethertype_allowed (const gbp_contract_t * gc0, u16 ethertype)
-{
-  u16 *et;
-
-  vec_foreach (et, gc0->gc_allowed_ethertypes)
-  {
-    if (*et == ethertype)
-      return (1);
-  }
-  return (0);
-}
-
 static_always_inline gbp_policy_next_t
 gbp_policy_l2_feature_next (gbp_policy_main_t * gpm, vlib_buffer_t * b,
                            const gbp_policy_type_t type)
@@ -143,13 +100,12 @@ gbp_policy_inline (vlib_main_t * vm,
   gbp_main_t *gm = &gbp_main;
   gbp_policy_main_t *gpm = &gbp_policy_main;
   u32 n_left_from, *from, *to_next;
-  u32 next_index, thread_index;
+  u32 next_index;
   u32 n_allow_intra, n_allow_a_bit, n_allow_sclass_1;
 
   next_index = 0;
   n_left_from = frame->n_vectors;
   from = vlib_frame_vector_args (frame);
-  thread_index = vm->thread_index;
   n_allow_intra = n_allow_a_bit = n_allow_sclass_1 = 0;
 
   while (n_left_from > 0)
@@ -162,14 +118,14 @@ gbp_policy_inline (vlib_main_t * vm,
        {
          const ethernet_header_t *h0;
          const gbp_endpoint_t *ge0;
-         const gbp_contract_t *gc0;
+         gbp_rule_action_t action0;
+         gbp_contract_error_t err0;
          gbp_policy_next_t next0;
          gbp_contract_key_t key0;
          u32 bi0, sw_if_index0;
          vlib_buffer_t *b0;
-         index_t gci0;
+         gbp_rule_t *rule0;
 
-         gc0 = NULL;
          next0 = GBP_POLICY_NEXT_DROP;
          bi0 = from[0];
          to_next[0] = bi0;
@@ -182,9 +138,6 @@ gbp_policy_inline (vlib_main_t * vm,
          h0 = vlib_buffer_get_current (b0);
          sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_TX];
 
-         /* zero out the key to ensure the pad space is clear */
-         key0.as_u64 = 0;
-
          /*
           * Reflection check; in and out on an ivxlan tunnel
           */
@@ -210,6 +163,8 @@ gbp_policy_inline (vlib_main_t * vm,
           * determine the src and dst EPG
           */
 
+         /* zero out the key to ensure the pad space is clear */
+         key0.as_u64 = 0;
          key0.gck_dst = SCLASS_INVALID;
 
          if (GBP_POLICY_LPM == type)
@@ -244,174 +199,48 @@ gbp_policy_inline (vlib_main_t * vm,
          if (SCLASS_INVALID == key0.gck_dst)
            {
              /* If you cannot determine the destination EP then drop */
-             b0->error = node->errors[GBP_POLICY_ERROR_DROP_NO_DCLASS];
+             b0->error = node->errors[GBP_CONTRACT_ERROR_DROP_NO_DCLASS];
              goto trace;
            }
-         key0.gck_src = vnet_buffer2 (b0)->gbp.sclass;
 
-         if (SCLASS_INVALID != key0.gck_src)
-           {
-             if (PREDICT_FALSE (key0.gck_src == key0.gck_dst))
-               {
-                 /*
-                  * intra-epg allowed
-                  */
-                 next0 = gbp_policy_l2_feature_next (gpm, b0, type);
-                 vnet_buffer2 (b0)->gbp.flags |= VXLAN_GBP_GPFLAGS_A;
-                 n_allow_intra++;
-               }
-             else if (PREDICT_FALSE (key0.gck_src == 1 || key0.gck_dst == 1))
-               {
-                 /*
-                  * sclass or dclass 1 allowed
-                  */
-                 next0 = gbp_policy_l2_feature_next (gpm, b0, type);
-                 vnet_buffer2 (b0)->gbp.flags |= VXLAN_GBP_GPFLAGS_A;
-                 n_allow_sclass_1++;
-               }
-             else
-               {
-                 key0.gck_scope =
-                   gbp_bridge_domain_get_scope (vnet_buffer (b0)->
-                                                l2.bd_index);
-                 gci0 = gbp_contract_find (&key0);
-
-                 if (INDEX_INVALID != gci0)
-                   {
-                     u32 rule_match_p0, trace_bitmap0;
-                     fa_5tuple_opaque_t pkt_5tuple0;
-                     u32 acl_pos_p0, acl_match_p0;
-                     u8 is_ip60, l2_len0, action0;
-                     const gbp_rule_t *gu;
-                     u16 ether_type0;
-                     const u8 *h0;
-
-                     vlib_prefetch_combined_counter
-                       (&gbp_contract_drop_counters, thread_index, gci0);
-                     vlib_prefetch_combined_counter
-                       (&gbp_contract_permit_counters, thread_index, gci0);
-
-                     action0 = 0;
-                     gc0 = gbp_contract_get (gci0);
-                     l2_len0 = vnet_buffer (b0)->l2.l2_len;
-                     h0 = vlib_buffer_get_current (b0);
-
-                     ether_type0 = *(u16 *) (h0 + l2_len0 - 2);
-
-                     if (!gbp_policy_is_ethertype_allowed (gc0, ether_type0))
-                       {
-                         /*
-                          * black list model so drop
-                          */
-                         b0->error =
-                           node->errors[GBP_POLICY_ERROR_DROP_ETHER_TYPE];
-
-                         vlib_increment_combined_counter
-                           (&gbp_contract_drop_counters,
-                            thread_index,
-                            gci0, 1, vlib_buffer_length_in_chain (vm, b0));
-
-                         goto trace;
-                       }
-
-                     if ((ether_type0 ==
-                          clib_net_to_host_u16 (ETHERNET_TYPE_IP6))
-                         || (ether_type0 ==
-                             clib_net_to_host_u16 (ETHERNET_TYPE_IP4)))
-                       {
-                         is_ip60 =
-                           (ether_type0 ==
-                            clib_net_to_host_u16 (ETHERNET_TYPE_IP6)) ? 1 :
-                           0;
-                         /*
-                          * tests against the ACL
-                          */
-                         acl_plugin_fill_5tuple_inline (gm->
-                                                        acl_plugin.p_acl_main,
-                                                        gc0->gc_lc_index, b0,
-                                                        is_ip60,
-                                                        /* is_input */ 0,
-                                                        /* is_l2_path */ 1,
-                                                        &pkt_5tuple0);
-                         acl_plugin_match_5tuple_inline (gm->
-                                                         acl_plugin.p_acl_main,
-                                                         gc0->gc_lc_index,
-                                                         &pkt_5tuple0,
-                                                         is_ip60, &action0,
-                                                         &acl_pos_p0,
-                                                         &acl_match_p0,
-                                                         &rule_match_p0,
-                                                         &trace_bitmap0);
-
-                         if (action0 > 0)
-                           {
-                             vnet_buffer2 (b0)->gbp.flags |=
-                               VXLAN_GBP_GPFLAGS_A;
-                             gu =
-                               gbp_rule_get (gc0->gc_rules[rule_match_p0]);
-
-                             switch (gu->gu_action)
-                               {
-                               case GBP_RULE_PERMIT:
-                                 next0 =
-                                   gbp_policy_l2_feature_next (gpm, b0,
-                                                               type);
-                                 break;
-                               case GBP_RULE_DENY:
-                                 next0 = GBP_POLICY_NEXT_DROP;
-                                 break;
-                               case GBP_RULE_REDIRECT:
-                                 next0 = gbp_rule_l2_redirect (gu, b0);
-                                 break;
-                               }
-                           }
-                       }
-                     if (next0 == GBP_POLICY_NEXT_DROP)
-                       {
-                         vlib_increment_combined_counter
-                           (&gbp_contract_drop_counters,
-                            thread_index,
-                            gci0, 1, vlib_buffer_length_in_chain (vm, b0));
-                         b0->error =
-                           node->errors[GBP_POLICY_ERROR_DROP_CONTRACT];
-                       }
-                     else
-                       {
-                         vlib_increment_combined_counter
-                           (&gbp_contract_permit_counters,
-                            thread_index,
-                            gci0, 1, vlib_buffer_length_in_chain (vm, b0));
-                       }
-                   }
-                 else
-                   {
-                     b0->error =
-                       node->errors[GBP_POLICY_ERROR_DROP_NO_CONTRACT];
-                   }
-               }
-           }
-         else
+         key0.gck_src = vnet_buffer2 (b0)->gbp.sclass;
+         if (SCLASS_INVALID == key0.gck_src)
            {
              /*
               * the src EPG is not set when the packet arrives on an EPG
               * uplink interface and we do not need to apply policy
               */
              next0 = gbp_policy_l2_feature_next (gpm, b0, type);
+             goto trace;
            }
 
-       trace:
-         if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
+         key0.gck_scope =
+           gbp_bridge_domain_get_scope (vnet_buffer (b0)->l2.bd_index);
+
+         action0 =
+           gbp_contract_apply (vm, gm, &key0, b0, &rule0, &n_allow_intra,
+                               &n_allow_sclass_1, &err0,
+                               GBP_CONTRACT_APPLY_L2);
+         switch (action0)
            {
-             gbp_policy_trace_t *t =
-               vlib_add_trace (vm, node, b0, sizeof (*t));
-             t->sclass = key0.gck_src;
-             t->dclass = key0.gck_dst;
-             t->scope = key0.gck_scope;
-             t->acl_index = (gc0 ? gc0->gc_acl_index : ~0);
-             t->allowed = (next0 != GBP_POLICY_NEXT_DROP);
-             t->flags = vnet_buffer2 (b0)->gbp.flags;
+           case GBP_RULE_PERMIT:
+             next0 = gbp_policy_l2_feature_next (gpm, b0, type);
+             vnet_buffer2 (b0)->gbp.flags |= VXLAN_GBP_GPFLAGS_A;
+             break;
+           case GBP_RULE_REDIRECT:
+             next0 = gbp_rule_l2_redirect (rule0, b0);
+             vnet_buffer2 (b0)->gbp.flags |= VXLAN_GBP_GPFLAGS_A;
+             break;
+           case GBP_RULE_DENY:
+             next0 = GBP_POLICY_NEXT_DROP;
+             b0->error = node->errors[err0];
+             break;
            }
 
+       trace:
+         gbp_policy_trace (vm, node, b0, &key0,
+                           (next0 != GBP_POLICY_NEXT_DROP));
+
          /* verify speculative enqueue, maybe switch current next frame */
          vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
                                           to_next, n_left_to_next,
@@ -422,11 +251,11 @@ gbp_policy_inline (vlib_main_t * vm,
     }
 
   vlib_node_increment_counter (vm, node->node_index,
-                              GBP_POLICY_ERROR_ALLOW_INTRA, n_allow_intra);
+                              GBP_CONTRACT_ERROR_ALLOW_INTRA, n_allow_intra);
   vlib_node_increment_counter (vm, node->node_index,
-                              GBP_POLICY_ERROR_ALLOW_A_BIT, n_allow_a_bit);
+                              GBP_CONTRACT_ERROR_ALLOW_A_BIT, n_allow_a_bit);
   vlib_node_increment_counter (vm, node->node_index,
-                              GBP_POLICY_ERROR_ALLOW_SCLASS_1,
+                              GBP_CONTRACT_ERROR_ALLOW_SCLASS_1,
                               n_allow_sclass_1);
 
   return frame->n_vectors;
@@ -453,22 +282,6 @@ VLIB_NODE_FN (gbp_policy_lpm_node) (vlib_main_t * vm,
   return (gbp_policy_inline (vm, node, frame, GBP_POLICY_LPM));
 }
 
-/* packet trace format function */
-static u8 *
-format_gbp_policy_trace (u8 * s, va_list * args)
-{
-  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
-  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
-  gbp_policy_trace_t *t = va_arg (*args, gbp_policy_trace_t *);
-
-  s =
-    format (s, "scope:%d sclass:%d, dclass:%d, acl:%d allowed:%d flags:%U",
-           t->scope, t->sclass, t->dclass, t->acl_index, t->allowed,
-           format_vxlan_gbp_header_gpflags, t->flags);
-
-  return s;
-}
-
 /* *INDENT-OFF* */
 VLIB_REGISTER_NODE (gbp_policy_port_node) = {
   .name = "gbp-policy-port",
@@ -476,8 +289,8 @@ VLIB_REGISTER_NODE (gbp_policy_port_node) = {
   .format_trace = format_gbp_policy_trace,
   .type = VLIB_NODE_TYPE_INTERNAL,
 
-  .n_errors = ARRAY_LEN(gbp_policy_error_strings),
-  .error_strings = gbp_policy_error_strings,
+  .n_errors = ARRAY_LEN(gbp_contract_error_strings),
+  .error_strings = gbp_contract_error_strings,
 
   .n_next_nodes = GBP_POLICY_N_NEXT,
   .next_nodes = {
@@ -491,8 +304,8 @@ VLIB_REGISTER_NODE (gbp_policy_mac_node) = {
   .format_trace = format_gbp_policy_trace,
   .type = VLIB_NODE_TYPE_INTERNAL,
 
-  .n_errors = ARRAY_LEN(gbp_policy_error_strings),
-  .error_strings = gbp_policy_error_strings,
+  .n_errors = ARRAY_LEN(gbp_contract_error_strings),
+  .error_strings = gbp_contract_error_strings,
 
   .n_next_nodes = GBP_POLICY_N_NEXT,
   .next_nodes = {
@@ -506,8 +319,8 @@ VLIB_REGISTER_NODE (gbp_policy_lpm_node) = {
   .format_trace = format_gbp_policy_trace,
   .type = VLIB_NODE_TYPE_INTERNAL,
 
-  .n_errors = ARRAY_LEN(gbp_policy_error_strings),
-  .error_strings = gbp_policy_error_strings,
+  .n_errors = ARRAY_LEN(gbp_contract_error_strings),
+  .error_strings = gbp_contract_error_strings,
 
   .n_next_nodes = GBP_POLICY_N_NEXT,
   .next_nodes = {