aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/gbp/gbp_classify_node.c
blob: a2058a212848d2d0d512714a0e50d5b55b6e5ea2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/*
 * Copyright (c) 2017 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.
 */

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/vfio.h>
#include <sys/ioctl.h>

#include <vnet/vnet.h>
#include <vnet/plugin/plugin.h>
#include <dpdk/device/dpdk.h>
#include <vpp/app/version.h>

/*
 * Called by the dpdk driver's rte_delay_us() function.
 * Return 0 to have the dpdk do a regular delay loop.
 * Return 1 if to skip the delay loop because we are suspending
 * the calling vlib process instead.
 */
static int
rte_delay_us_override (unsigned us)
{
  vlib_main_t *vm;

  /* Don't bother intercepting for short delays */
  if (us < 10)
    return 0;

  /*
   * Only intercept if we are in a vlib process.
   * If we are called from a vlib worker thread or the vlib main
   * thread then do not intercept. (Must not be called from an
   * independent pthread).
   */
  if (vlib_get_thread_index () == 0)
    {
      /*
       * We're in the vlib main thread or a vlib process. Make sure
       * the process is running and we're not still initializing.
       */
      vm = vlib_get_main ();
      if (vlib_in_process_context (vm))
	{
	  /* Only suspend for the admin_down_process */
	  vlib_process_t *proc = vlib_get_current_process (vm);
	  if (!(proc->flags & VLIB_PROCESS_IS_RUNNING) ||
	      (proc->node_runtime.function != admin_up_down_process))
	    return 0;

	  f64 delay = 1e-6 * us;
	  vlib_process_suspend (vm, delay);
	  return 1;
	}
    }
  return 0;			// no override
}

static void
rte_delay_us_override_cb (unsigned us)
{
  if (rte_delay_us_override (us) == 0)
    rte_delay_us_block (us);
}

static clib_error_t * dpdk_main_init (vlib_main_t * vm)
{
  dpdk_main_t * dm = &dpdk_main;
  clib_error_t * error = 0;

  dm->vlib_main = vm;
  dm->vnet_main = vnet_get_main ();

  if ((error = vlib_call_init_function (vm, dpdk_init)))
    return error;

  /* register custom delay function */
  
@media only all and (prefers-color-scheme: dark) {
.highlight .hll { background-color: #49483e }
.highlight .c { color: #75715e } /* Comment */
.highlight .err { color: #960050; background-color: #1e0010 } /* Error */
.highlight .k { color: #66d9ef } /* Keyword */
.highlight .l { color: #ae81ff } /* Literal */
.highlight .n { color: #f8f8f2 } /* Name */
.highlight .o { color: #f92672 } /* Operator */
.highlight .p { color: #f8f8f2 } /* Punctuation */
.highlight .ch { color: #75715e } /* Comment.Hashbang */
.highlight .cm { color: #75715e } /* Comment.Multiline */
.highlight .cp { color: #75715e } /* Comment.Preproc */
.highlight .cpf { color: #75715e } /* Comment.PreprocFile */
.highlight .c1 { color: #75715e } /* Comment.Single */
.highlight .cs { color: #75715e } /* Comment.Special */
.highlight .gd { color: #f92672 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gi { color: #a6e22e } /* Generic.Inserted */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #75715e } /* Generic.Subheading */
.highlight .kc { color: #66d9ef } /* Keyword.Constant */
.highlight .kd { color: #66d9ef } /* Keyword.Declaration */
.highlight .kn { color: #f92672 } /* Keyword.Namespace */
.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
.highlight .kr { color: #66d9ef } /* Keyword.Reserved */
.highlight .kt { color: #66d9ef } /* Keyword.Type */
.highlight .ld { color: #e6db74 } /* Literal.Date */
.highlight .m { color: #ae81ff } /* Literal.Number */
.highlight .s { color: #e6db74 } /* Literal.String */
.highlight .na { color: #a6e22e } /* Name.Attribute */
.highlight .nb { color: #f8f8f2 } /* Name.Builtin */
.highlight .nc { color: #a6e22e } /* Name.Class */
.highlight .no { color: #66d9ef } /* Name.Constant */
.highlight .nd { color: #a6e22e } /* Name.Decorator */
.highlight .ni { color: #f8f8f2 } /* Name.Entity */
.highlight .ne { color: #a6e22e } /* Name.Exception */
.highlight .nf { color: #a6e22e } /* Name.Function */
.highlight .nl { color: #f8f8f2 } /* Name.Label */
.highlight .nn { color: #f8f8f2 } /* Name.Namespace */
.highlight .nx { color: #a6e22e } /* Name.Other */
.highlight .py { color: #f8f8f2 } /* Name.Property */
.highlight .nt { color: #f92672 } /* Name.Tag */
.highlight .nv { color: #f8f8f2 } /* Name.Variable */
.highlight .ow { color: #f92672 } /* Operator.Word */
.highlight .w { color: #f8f8f2 } /* Text.Whitespace */
.highlight .mb { color: #ae81ff } /* Literal.Number.Bin */
.highlight .mf { color: #ae81ff } /* Literal.Number.Float */
.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
.highlight .sa { color: #e6db74 } /* Literal.String.Affix */
.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
.highlight .sc { color: #e6db74 } /* Literal.String.Char */
.highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */
.highlight .sd { color: #e6db74 } /* Literal.String.Doc */
.highlight .s2 { color: #e6db74 } /* Literal.String.Double */
.highlight .se { color: #ae81ff } /* Literal.String.Escape */
.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
.highlight .si { color: #e6db74 } /* Literal.String.Interpol */
.highlight .sx { color: #e6db74 } /* Literal.String.Other */
.highlight .sr { color: #e6db74 } /* Literal.String.Regex */
.highlight .s1 { color: #e6db74 } /* Literal.String.Single */
.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #a6e22e } /* Name.Function.Magic */
.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
.highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */
.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
}
@media (prefers-color-scheme: light) {
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
}
/*
 * gbp.h : Group Based Policy
 *
 * 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.
 */

#include <plugins/gbp/gbp.h>
#include <plugins/gbp/gbp_classify.h>
#include <plugins/gbp/gbp_policy_dpo.h>
#include <plugins/gbp/gbp_ext_itf.h>
#include <vnet/fib/ip4_fib.h>
#include <vnet/fib/ip6_fib.h>
#include <vnet/dpo/load_balance.h>
#include <vnet/l2/l2_input.h>
#include <vnet/l2/feat_bitmap.h>
#include <vnet/fib/fib_table.h>
#include <vnet/vxlan-gbp/vxlan_gbp_packet.h>
#include <vnet/ethernet/arp_packet.h>

/**
 * per-packet trace data
 */
typedef struct gbp_classify_trace_t_
{
  /* per-pkt trace data */
  sclass_t sclass;
} gbp_classify_trace_t;

/*
 * determine the SRC EPG form the input port
 */
always_inline uword
gbp_classify_inline (vlib_main_t * vm,
		     vlib_node_runtime_t * node,
		     vlib_frame_t * frame,
		     gbp_src_classify_type_t type, dpo_proto_t dproto)
{
  gbp_src_classify_main_t *gscm = &gbp_src_classify_main;
  u32 n_left_from, *from, *to_next;
  u32 next_index;

  next_index = 0;
  n_left_from = frame->n_vectors;
  from = vlib_frame_vector_args (frame);

  while (n_left_from > 0)
    {
      u32 n_left_to_next;

      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);

      while (n_left_from > 0 && n_left_to_next > 0)
	{
	  u32 next0, bi0, sw_if_index0;
	  const gbp_endpoint_t *ge0;
	  vlib_buffer_t *b0;
	  sclass_t sclass0;

	  bi0 = from[0];
	  to_next[0] = bi0;
	  from += 1;
	  to_next += 1;
	  n_left_from -= 1;
	  n_left_to_next -= 1;

	  b0 = vlib_get_buffer (vm, bi0);

	  sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
	  vnet_buffer2 (b0)->gbp.flags = VXLAN_GBP_GPFLAGS_NONE;

	  if (GBP_SRC_CLASSIFY_NULL == type)
	    {
	      sclass0 = SCLASS_INVALID;
	      next0 =
		vnet_l2_feature_next (b0, gscm->l2_input_feat_next[type],
				      L2INPUT_FEAT_GBP_NULL_CLASSIFY);
	    }
	  else
	    {
	      if (DPO_PROTO_ETHERNET == dproto)
		{
		  const ethernet_header_t *h0;

		  h0 = vlib_buffer_get_current (b0);
		  next0 =
		    vnet_l2_feature_next (b0, gscm->l2_input_feat_next[type],
					  L2INPUT_FEAT_GBP_SRC_CLASSIFY);
		  ge0 = gbp_endpoint_find_mac (h0->src_address,
					       vnet_buffer (b0)->l2.bd_index);
		}
	      else if (DPO_PROTO_IP4 == dproto)
		{
		  const ip4_header_t *h0;

		  h0 = vlib_buffer_get_current (b0);

		  ge0 = gbp_endpoint_find_ip4
		    (&h0->src_address,
		     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4,
							  sw_if_index0));


		  /*
		   * Go straight to looukp, do not pass go, do not collect $200
		   */
		  next0 = 0;
		}
	      else if (DPO_PROTO_IP6 == dproto)
		{
		  const ip6_header_t *h0;

		  h0 = vlib_buffer_get_current (b0);

		  ge0 = gbp_endpoint_find_ip6
		    (&h0->src_address,
		     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP6,
							  sw_if_index0));


		  /*
		   * Go straight to lookup, do not pass go, do not collect $200
		   */
		  next0 = 0;
		}
	      else
		{
		  ge0 = NULL;
		  next0 = 0;
		  ASSERT (0);
		}

	      if (PREDICT_TRUE (NULL != ge0))
		sclass0 = ge0->ge_fwd.gef_sclass;
	      else
		sclass0 = SCLASS_INVALID;
	    }

	  vnet_buffer2 (b0)->gbp.sclass = sclass0;

	  if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
	    {
	      gbp_classify_trace_t *t =
		vlib_add_trace (vm, node, b0, sizeof (*t));
	      t->sclass = sclass0;
	    }

	  vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
					   to_next, n_left_to_next,
					   bi0, next0);
	}

      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
    }

  return frame->n_vectors;
}

VLIB_NODE_FN (gbp_src_classify_node) (vlib_main_t * vm,
				      vlib_node_runtime_t * node,
				      vlib_frame_t * frame)
{
  return (gbp_classify_inline (vm, node, frame,
			       GBP_SRC_CLASSIFY_PORT, DPO_PROTO_ETHERNET));
}

VLIB_NODE_FN (gbp_null_classify_node) (vlib_main_t * vm,
				       vlib_node_runtime_t * node,
				       vlib_frame_t * frame)
{
  return (gbp_classify_inline (vm, node, frame,
			       GBP_SRC_CLASSIFY_NULL, DPO_PROTO_ETHERNET));
}

VLIB_NODE_FN (gbp_ip4_src_classify_node) (vlib_main_t * vm,
					  vlib_node_runtime_t * node,
					  vlib_frame_t * frame)
{
  return (gbp_classify_inline (vm, node, frame,
			       GBP_SRC_CLASSIFY_PORT, DPO_PROTO_IP4));
}

VLIB_NODE_FN (gbp_ip6_src_classify_node) (vlib_main_t * vm,
					  vlib_node_runtime_t * node,
					  vlib_frame_t * frame)
{
  return (gbp_classify_inline (vm, node, frame,
			       GBP_SRC_CLASSIFY_PORT, DPO_PROTO_IP6));
}


/* packet trace format function */
static u8 *
format_gbp_classify_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_classify_trace_t *t = va_arg (*args, gbp_classify_trace_t *);

  s = format (s, "sclass:%d", t->sclass);

  return s;
}

/* *INDENT-OFF* */
VLIB_REGISTER_NODE (gbp_null_classify_node) = {
  .name = "gbp-null-classify",
  .vector_size = sizeof (u32),
  .format_trace = format_gbp_classify_trace,
  .type = VLIB_NODE_TYPE_INTERNAL,

  .n_errors = 0,
  .n_next_nodes = 0,
};

VLIB_REGISTER_NODE (gbp_src_classify_node) = {
  .name = "gbp-src-classify",
  .vector_size = sizeof (u32),
  .format_trace = format_gbp_classify_trace,
  .type = VLIB_NODE_TYPE_INTERNAL,

  .n_errors = 0,
  .n_next_nodes = 0,
};

VLIB_REGISTER_NODE (gbp_ip4_src_classify_node) = {
  .name = "ip4-gbp-src-classify",
  .vector_size = sizeof (u32),
  .format_trace = format_gbp_classify_trace,
  .type = VLIB_NODE_TYPE_INTERNAL,

  .n_errors = 0,
  .n_next_nodes = 1,
  .next_nodes = {
    [0] = "ip4-lookup"
  },
};

VLIB_REGISTER_NODE (gbp_ip6_src_classify_node) = {
  .name = "ip6-gbp-src-classify",
  .vector_size = sizeof (u32),
  .format_trace = format_gbp_classify_trace,
  .type = VLIB_NODE_TYPE_INTERNAL,

  .n_errors = 0,
  .n_next_nodes = 1,
  .next_nodes = {
    [0] = "ip6-lookup"
  },
};

VNET_FEATURE_INIT (gbp_ip4_src_classify_feat_node, static) =
{
  .arc_name = "ip4-unicast",
  .node_name = "ip4-gbp-src-classify",
  .runs_before = VNET_FEATURES ("nat44-out2in"),
};
VNET_FEATURE_INIT (gbp_ip6_src_classify_feat_node, static) =
{
  .arc_name = "ip6-unicast",
  .node_name = "ip6-gbp-src-classify",
  .runs_before = VNET_FEATURES ("nat66-out2in"),
};

/* *INDENT-ON* */

typedef enum gbp_lpm_classify_next_t_
{
  GPB_LPM_CLASSIFY_DROP,
} gbp_lpm_classify_next_t;

/**
 * per-packet trace data
 */
typedef struct gbp_lpm_classify_trace_t_
{
  sclass_t sclass;
  index_t lbi;
  ip46_address_t src;
} gbp_lpm_classify_trace_t;

/* packet trace format function */
static u8 *
format_gbp_lpm_classify_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_lpm_classify_trace_t *t = va_arg (*args, gbp_lpm_classify_trace_t *);

  s = format (s, "sclass:%d lb:%d src:%U",
	      t->sclass, t->lbi, format_ip46_address, &t->src, IP46_TYPE_ANY);

  return s;
}

enum gbp_lpm_type
{
  GBP_LPM_RECIRC,
  GBP_LPM_EPG,
  GBP_LPM_ANON
};

/*
 * Determine the SRC EPG from a LPM
 */
always_inline uword
gbp_lpm_classify_inline (vlib_main_t * vm,
			 vlib_node_runtime_t * node,
			 vlib_frame_t * frame,
			 const dpo_proto_t dproto,
			 const enum gbp_lpm_type type)
{
  gbp_src_classify_main_t *gscm = &gbp_src_classify_main;
  u32 n_left_from, *from, *to_next;
  u32 next_index;

  next_index = 0;
  n_left_from = frame->n_vectors;
  from = vlib_frame_vector_args (frame);

  while (n_left_from > 0)
    {
      u32 n_left_to_next;

      vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);

      while (n_left_from > 0 && n_left_to_next > 0)
	{
	  u32 bi0, sw_if_index0, fib_index0, lbi0;
	  const gbp_endpoint_t *ge0, *ge_lpm0;
	  gbp_lpm_classify_next_t next0;
	  const ethernet_header_t *eh0;
	  const gbp_policy_dpo_t *gpd0;
	  const ip4_address_t *ip4_0;
	  const ip6_address_t *ip6_0;
	  const gbp_recirc_t *gr0;
	  vlib_buffer_t *b0;
	  sclass_t sclass0;

	  bi0 = from[0];
	  to_next[0] = bi0;
	  from += 1;
	  to_next += 1;
	  n_left_from -= 1;
	  n_left_to_next -= 1;
	  ip4_0 = NULL;
	  ip6_0 = NULL;
	  next0 = GPB_LPM_CLASSIFY_DROP;

	  lbi0 = ~0;
	  eh0 = NULL;
	  b0 = vlib_get_buffer (vm, bi0);

	  sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
	  vnet_buffer2 (b0)->gbp.flags = VXLAN_GBP_GPFLAGS_NONE;

	  if (DPO_PROTO_IP4 == dproto)
	    ip4_0 =
	      &((ip4_header_t *) vlib_buffer_get_current (b0))->src_address;
	  else if (DPO_PROTO_IP6 == dproto)
	    ip6_0 =
	      &((ip6_header_t *) vlib_buffer_get_current (b0))->src_address;
	  else if (DPO_PROTO_ETHERNET == dproto)
	    {
	      eh0 = vlib_buffer_get_current (b0);
	      gbp_classify_get_ip_address (eh0, &ip4_0, &ip6_0,
					   GBP_CLASSIFY_GET_IP_SRC);
	    }

	  if (GBP_LPM_RECIRC == type)
	    {
	      gr0 = gbp_recirc_get (sw_if_index0);
	      fib_index0 = gr0->gr_fib_index[dproto];
	      ge0 = NULL;

	      vnet_feature_next (&next0, b0);
	    }
	  else
	    {
	      if (NULL == eh0)
		{
		  /* packet should be l2 */
		  sclass0 = SCLASS_INVALID;
		  goto trace;
		}

	      if (GBP_LPM_ANON == type)
		{
		  /*
		   * anonymous LPM classification: only honour LPM as no EP
		   * were programmed
		   */
		  gbp_ext_itf_t *gei = gbp_ext_itf_get (sw_if_index0);
		  if (ip4_0)
		    fib_index0 = gei->gx_fib_index[DPO_PROTO_IP4];
		  else if (ip6_0)
		    fib_index0 = gei->gx_fib_index[DPO_PROTO_IP6];
		  else
		    {
		      /* not IP so no LPM classify possible */
		      sclass0 = SCLASS_INVALID;
		      next0 = GPB_LPM_CLASSIFY_DROP;
		      goto trace;
		    }
		  next0 = vnet_l2_feature_next
		    (b0, gscm->l2_input_feat_next[GBP_SRC_CLASSIFY_LPM_ANON],
		     L2INPUT_FEAT_GBP_LPM_ANON_CLASSIFY);
		}
	      else
		{
		  /*
		   * not an anonymous LPM classification: check it comes from
		   * an EP, and use EP RD info
		   */
		  ge0 = gbp_endpoint_find_mac (eh0->src_address,
					       vnet_buffer (b0)->l2.bd_index);

		  if (NULL == ge0)
		    {
		      /* packet must have come from an EP's mac */
		      sclass0 = SCLASS_INVALID;
		      goto trace;
		    }

		  fib_index0 = ge0->ge_fwd.gef_fib_index;

		  if (~0 == fib_index0)
		    {
		      sclass0 = SCLASS_INVALID;
		      goto trace;
		    }

		  if (ip4_0)
		    {
		      ge_lpm0 = gbp_endpoint_find_ip4 (ip4_0, fib_index0);
		    }
		  else if (ip6_0)
		    {
		      ge_lpm0 = gbp_endpoint_find_ip6 (ip6_0, fib_index0);
		    }
		  else
		    {
		      ge_lpm0 = NULL;
		    }

		  next0 = vnet_l2_feature_next
		    (b0, gscm->l2_input_feat_next[GBP_SRC_CLASSIFY_LPM],
		     L2INPUT_FEAT_GBP_LPM_CLASSIFY);

		  /*
		   * if we found the EP by IP lookup, it must be from the EP
		   * not a network behind it
		   */
		  if (NULL != ge_lpm0)
		    {
		      if (PREDICT_FALSE (ge0 != ge_lpm0))
			{
			  /* an EP spoofing another EP */
			  sclass0 = SCLASS_INVALID;
			  next0 = GPB_LPM_CLASSIFY_DROP;
			}
		      else
			{
			  sclass0 = ge0->ge_fwd.gef_sclass;
			}
		      goto trace;
		    }
		}
	    }

	  gpd0 = gbp_classify_get_gpd (ip4_0, ip6_0, fib_index0);
	  if (0 == gpd0)
	    {
	      /* could not classify => drop */
	      sclass0 = SCLASS_INVALID;
	      next0 = GPB_LPM_CLASSIFY_DROP;
	      goto trace;
	    }

	  sclass0 = gpd0->gpd_sclass;

	  /* all packets from an external network should not be learned by the
	   * reciever. so set the Do-not-learn bit here */
	  vnet_buffer2 (b0)->gbp.flags = VXLAN_GBP_GPFLAGS_D;

	trace:
	  vnet_buffer2 (b0)->gbp.sclass = sclass0;

	  if (PREDICT_FALSE ((b0->flags & VLIB_BUFFER_IS_TRACED)))
	    {
	      gbp_lpm_classify_trace_t *t =
		vlib_add_trace (vm, node, b0, sizeof (*t));
	      t->sclass = sclass0;
	      t->lbi = lbi0;
	      if (ip4_0)
		t->src.ip4 = *ip4_0;
	      if (ip6_0)
		t->src.ip6 = *ip6_0;
	    }

	  vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
					   to_next, n_left_to_next,
					   bi0, next0);
	}

      vlib_put_next_frame (vm, node, next_index, n_left_to_next);
    }

  return frame->n_vectors;
}

VLIB_NODE_FN (gbp_ip4_lpm_classify_node) (vlib_main_t * vm,
					  vlib_node_runtime_t * node,
					  vlib_frame_t * frame)
{
  return (gbp_lpm_classify_inline
	  (vm, node, frame, DPO_PROTO_IP4, GBP_LPM_RECIRC));
}

VLIB_NODE_FN (gbp_ip6_lpm_classify_node) (vlib_main_t * vm,
					  vlib_node_runtime_t * node,
					  vlib_frame_t * frame)
{
  return (gbp_lpm_classify_inline
	  (vm, node, frame, DPO_PROTO_IP6, GBP_LPM_RECIRC));
}

VLIB_NODE_FN (gbp_l2_lpm_classify_node) (vlib_main_t * vm,
					 vlib_node_runtime_t * node,
					 vlib_frame_t * frame)
{
  return (gbp_lpm_classify_inline
	  (vm, node, frame, DPO_PROTO_ETHERNET, GBP_LPM_EPG));
}

VLIB_NODE_FN (gbp_l2_lpm_anon_classify_node) (vlib_main_t * vm,
					      vlib_node_runtime_t * node,
					      vlib_frame_t * frame)
{
  return (gbp_lpm_classify_inline
	  (vm, node, frame, DPO_PROTO_ETHERNET, GBP_LPM_ANON));
}

/* *INDENT-OFF* */
VLIB_REGISTER_NODE (gbp_ip4_lpm_classify_node) = {
  .name = "ip4-gbp-lpm-classify",
  .vector_size = sizeof (u32),
  .format_trace = format_gbp_lpm_classify_trace,
  .type = VLIB_NODE_TYPE_INTERNAL,

  .n_errors = 0,
  .n_next_nodes = 1,
  .next_nodes = {
    [GPB_LPM_CLASSIFY_DROP] = "ip4-drop"
  },
};

VLIB_REGISTER_NODE (gbp_ip6_lpm_classify_node) = {
  .name = "ip6-gbp-lpm-classify",
  .vector_size = sizeof (u32),
  .format_trace = format_gbp_lpm_classify_trace,
  .type = VLIB_NODE_TYPE_INTERNAL,

  .n_errors = 0,
  .n_next_nodes = 1,
  .next_nodes = {
    [GPB_LPM_CLASSIFY_DROP] = "ip6-drop"
  },
};

VLIB_REGISTER_NODE (gbp_l2_lpm_classify_node) = {
  .name = "l2-gbp-lpm-classify",
  .vector_size = sizeof (u32),
  .format_trace = format_gbp_lpm_classify_trace,
  .type = VLIB_NODE_TYPE_INTERNAL,

  .n_errors = 0,
  .n_next_nodes = 1,
  .next_nodes = {
    [GPB_LPM_CLASSIFY_DROP] = "error-drop"
  },
};

VLIB_REGISTER_NODE (gbp_l2_lpm_anon_classify_node) = {
  .name = "l2-gbp-lpm-anon-classify",
  .vector_size = sizeof (u32),
  .format_trace = format_gbp_lpm_classify_trace,
  .type = VLIB_NODE_TYPE_INTERNAL,

  .n_errors = 0,
  .n_next_nodes = 1,
  .next_nodes = {
    [GPB_LPM_CLASSIFY_DROP] = "error-drop"
  },
};

VNET_FEATURE_INIT (gbp_ip4_lpm_classify_feat_node, static) =
{
  .arc_name = "ip4-unicast",
  .node_name = "ip4-gbp-lpm-classify",
  .runs_before = VNET_FEATURES ("nat44-out2in"),
};
VNET_FEATURE_INIT (gbp_ip6_lpm_classify_feat_node, static) =
{
  .arc_name = "ip6-unicast",
  .node_name = "ip6-gbp-lpm-classify",
  .runs_before = VNET_FEATURES ("nat66-out2in"),
};

/* *INDENT-ON* */

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */