aboutsummaryrefslogtreecommitdiffstats
path: root/src/vnet/policer/node_funcs.c
blob: 7e7e1312a46becb72cc15c9ae459ef70ef360ed7 (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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152

@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 */
}
/*
 * 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.
 */
#ifndef __included_dslite_h__
#define __included_dslite_h__

#include <vppinfra/bihash_8_8.h>
#include <vppinfra/bihash_16_8.h>
#include <vppinfra/bihash_24_8.h>
#include <nat/nat.h>

typedef struct
{
  union
  {
    struct
    {
      ip6_address_t softwire_id;
      ip4_address_t addr;
      u16 port;
      u8 proto;
      u8 pad;
    };
    u64 as_u64[3];
  };
} dslite_session_key_t;

/* *INDENT-OFF* */
typedef CLIB_PACKED (struct
{
  snat_session_key_t out2in;
  dslite_session_key_t in2out;
  u32 per_b4_index;
  u32 per_b4_list_head_index;
  f64 last_heard;
  u64 total_bytes;
  u32 total_pkts;
  u32 outside_address_index;
}) dslite_session_t;
/* *INDENT-ON* */

typedef struct
{
  ip6_address_t addr;
  u32 sessions_per_b4_list_head_index;
  u32 nsessions;
} dslite_b4_t;

typedef struct
{
  /* Main lookup tables */
  clib_bihash_8_8_t out2in;
  clib_bihash_24_8_t in2out;

  /* Find a B4 */
  clib_bihash_16_8_t b4_hash;

  /* B4 pool */
  dslite_b4_t *b4s;

  /* Session pool */
  dslite_session_t *sessions;

  /* Pool of doubly-linked list elements */
  dlist_elt_t *list_pool;
} dslite_per_thread_data_t;

typedef struct
{
  ip6_address_t aftr_ip6_addr;
  ip4_address_t aftr_ip4_addr;
  ip6_address_t b4_ip6_addr;
  ip4_address_t b4_ip4_addr;
  dslite_per_thread_data_t *per_thread_data;
  snat_address_t *addr_pool;
  u32 num_workers;
  u32 first_worker_index;
  u16 port_per_thread;

  /* If set then the DSLite component behaves as CPE/B4
   * otherwise it behaves as AFTR */
  u8 is_ce;
} dslite_main_t;

typedef struct
{
  u32 next_index;
  u32 session_index;
} dslite_trace_t;

typedef struct
{
  u32 next_index;
} dslite_ce_trace_t;

#define foreach_dslite_error                    \
_(IN2OUT, "valid in2out DS-Lite packets")       \
_(OUT2IN, "valid out2in DS-Lite packets")       \
_(CE_ENCAP, "valid CE encap DS-Lite packets")   \
_(CE_DECAP, "valid CE decap DS-Lite packets")   \
_(NO_TRANSLATION, "no translation")             \
_(BAD_IP6_PROTOCOL, "bad ip6 protocol")         \
_(OUT_OF_PORTS, "out of ports")                 \
_(UNSUPPORTED_PROTOCOL, "unsupported protocol") \
_(BAD_ICMP_TYPE, "unsupported icmp type")       \
_(UNKNOWN, "unknown")

typedef enum
{
#define _(sym,str) DSLITE_ERROR_##sym,
  foreach_dslite_error
#undef _
    DSLITE_N_ERROR,
} dslite_error_t;

extern dslite_main_t dslite_main;
extern vlib_node_registration_t dslite_in2out_node;
extern vlib_node_registration_t dslite_in2out_slowpath_node;
extern vlib_node_registration_t dslite_out2in_node;
extern vlib_node_registration_t dslite_ce_encap_node;
extern vlib_node_registration_t dslite_ce_decap_node;

void dslite_init (vlib_main_t * vm);
void dslite_set_ce (dslite_main_t * dm, u8 set);
int dslite_set_aftr_ip6_addr (dslite_main_t * dm, ip6_address_t * addr);
int dslite_set_b4_ip6_addr (dslite_main_t * dm, ip6_address_t * addr);
int dslite_set_aftr_ip4_addr (dslite_main_t * dm, ip4_address_t * addr);
int dslite_set_b4_ip4_addr (dslite_main_t * dm, ip4_address_t * addr);
int dslite_add_del_pool_addr (dslite_main_t * dm, ip4_address_t * addr,
			      u8 is_add);
u8 *format_dslite_trace (u8 * s, va_list * args);
u8 *format_dslite_ce_trace (u8 * s, va_list * args);

#endif /* __included_dslite_h__ */

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */
804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885
/*
 * Copyright (c) 2015 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 <stdint.h>

#include <vlib/vlib.h>
#include <vnet/vnet.h>
#include <vnet/policer/policer.h>
#include <vnet/policer/police_inlines.h>
#include <vnet/ip/ip.h>
#include <vnet/classify/policer_classify.h>
#include <vnet/classify/vnet_classify.h>
#include <vnet/l2/feat_bitmap.h>
#include <vnet/l2/l2_input.h>


/* Dispatch functions meant to be instantiated elsewhere */

typedef struct
{
  u32 next_index;
  u32 sw_if_index;
  u32 policer_index;
} vnet_policer_trace_t;

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

  s = format (s, "VNET_POLICER: sw_if_index %d policer_index %d next %d",
	      t->sw_if_index, t->policer_index, t->next_index);
  return s;
}

#define foreach_vnet_policer_error              \
_(TRANSMIT, "Packets Transmitted")              \
_(DROP, "Packets Dropped")

typedef enum
{
#define _(sym,str) VNET_POLICER_ERROR_##sym,
  foreach_vnet_policer_error
#undef _
    VNET_POLICER_N_ERROR,
} vnet_policer_error_t;

static char *vnet_policer_error_strings[] = {
#define _(sym,string) string,
  foreach_vnet_policer_error
#undef _
};

static inline uword
vnet_policer_inline (vlib_main_t * vm,
		     vlib_node_runtime_t * node,
		     vlib_frame_t * frame, vnet_policer_index_t which)
{
  u32 n_left_from, *from, *to_next;
  vnet_policer_next_t next_index;
  vnet_policer_main_t *pm = &vnet_policer_main;
  u64 time_in_policer_periods;
  u32 transmitted = 0;

  time_in_policer_periods =
    clib_cpu_time_now () >> POLICER_TICKS_PER_PERIOD_SHIFT;

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

  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 >= 4 && n_left_to_next >= 2)
	{
	  u32 bi0, bi1;
	  vlib_buffer_t *b0, *b1;
	  u32 next0, next1;
	  u32 sw_if_index0, sw_if_index1;
	  u32 pi0 = 0, pi1 = 0;
	  u8 act0, act1;

	  /* Prefetch next iteration. */
	  {
	    vlib_buffer_t *b2, *b3;

	    b2 = vlib_get_buffer (vm, from[2]);
	    b3 = vlib_get_buffer (vm, from[3]);

	    vlib_prefetch_buffer_header (b2, LOAD);
	    vlib_prefetch_buffer_header (b3, LOAD);
	  }

	  /* speculatively enqueue b0 and b1 to the current next frame */
	  to_next[0] = bi0 = from[0];
	  to_next[1] = bi1 = from[1];
	  from += 2;
	  to_next += 2;
	  n_left_from -= 2;
	  n_left_to_next -= 2;

	  b0 = vlib_get_buffer (vm, bi0);
	  b1 = vlib_get_buffer (vm, bi1);

	  sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
	  next0 = VNET_POLICER_NEXT_TRANSMIT;

	  sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
	  next1 = VNET_POLICER_NEXT_TRANSMIT;


	  if (which == VNET_POLICER_INDEX_BY_SW_IF_INDEX)
	    {
	      pi0 = pm->policer_index_by_sw_if_index[sw_if_index0];
	      pi1 = pm->policer_index_by_sw_if_index[sw_if_index1];
	    }

	  if (which == VNET_POLICER_INDEX_BY_OPAQUE)
	    {
	      pi0 = vnet_buffer (b0)->policer.index;
	      pi1 = vnet_buffer (b1)->policer.index;
	    }

	  if (which == VNET_POLICER_INDEX_BY_EITHER)
	    {
	      pi0 = vnet_buffer (b0)->policer.index;
	      pi0 = (pi0 != ~0) ? pi0 :
		pm->policer_index_by_sw_if_index[sw_if_index0];
	      pi1 = vnet_buffer (b1)->policer.index;
	      pi1 = (pi1 != ~0) ? pi1 :
		pm->policer_index_by_sw_if_index[sw_if_index1];
	    }

	  act0 = vnet_policer_police (vm, b0, pi0, time_in_policer_periods,
				      POLICE_CONFORM /* no chaining */ );

	  act1 = vnet_policer_police (vm, b1, pi1, time_in_policer_periods,
				      POLICE_CONFORM /* no chaining */ );

	  if (PREDICT_FALSE (act0 == SSE2_QOS_ACTION_DROP))	/* drop action */
	    {
	      next0 = VNET_POLICER_NEXT_DROP;
	      b0->error = node->errors[VNET_POLICER_ERROR_DROP];
	    }
	  else			/* transmit or mark-and-transmit action */
	    {
	      transmitted++;
	    }

	  if (PREDICT_FALSE (act1 == SSE2_QOS_ACTION_DROP))	/* drop action */
	    {
	      next1 = VNET_POLICER_NEXT_DROP;
	      b1->error = node->errors[VNET_POLICER_ERROR_DROP];
	    }
	  else			/* transmit or mark-and-transmit action */
	    {
	      transmitted++;
	    }


	  if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
	    {
	      if (b0->flags & VLIB_BUFFER_IS_TRACED)
		{
		  vnet_policer_trace_t *t =
		    vlib_add_trace (vm, node, b0, sizeof (*t));
		  t->sw_if_index = sw_if_index0;
		  t->next_index = next0;
		}
	      if (b1->flags & VLIB_BUFFER_IS_TRACED)
		{
		  vnet_policer_trace_t *t =
		    vlib_add_trace (vm, node, b1, sizeof (*t));
		  t->sw_if_index = sw_if_index1;
		  t->next_index = next1;
		}
	    }

	  /* verify speculative enqueues, maybe switch current next frame */
	  vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
					   to_next, n_left_to_next,
					   bi0, bi1, next0, next1);
	}

      while (n_left_from > 0 && n_left_to_next > 0)
	{
	  u32 bi0;
	  vlib_buffer_t *b0;
	  u32 next0;
	  u32 sw_if_index0;
	  u32 pi0 = 0;
	  u8 act0;

	  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];
	  next0 = VNET_POLICER_NEXT_TRANSMIT;

	  if (which == VNET_POLICER_INDEX_BY_SW_IF_INDEX)
	    pi0 = pm->policer_index_by_sw_if_index[sw_if_index0];

	  if (which == VNET_POLICER_INDEX_BY_OPAQUE)
	    pi0 = vnet_buffer (b0)->policer.index;

	  if (which == VNET_POLICER_INDEX_BY_EITHER)
	    {
	      pi0 = vnet_buffer (b0)->policer.index;
	      pi0 = (pi0 != ~0) ? pi0 :
		pm->policer_index_by_sw_if_index[sw_if_index0];
	    }

	  act0 = vnet_policer_police (vm, b0, pi0, time_in_policer_periods,
				      POLICE_CONFORM /* no chaining */ );

	  if (PREDICT_FALSE (act0 == SSE2_QOS_ACTION_DROP))	/* drop action */
	    {
	      next0 = VNET_POLICER_NEXT_DROP;
	      b0->error = node->errors[VNET_POLICER_ERROR_DROP];
	    }
	  else			/* transmit or mark-and-transmit action */
	    {
	      transmitted++;
	    }

	  if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
			     && (b0->flags & VLIB_BUFFER_IS_TRACED)))
	    {
	      vnet_policer_trace_t *t =
		vlib_add_trace (vm, node, b0, sizeof (*t));
	      t->sw_if_index = sw_if_index0;
	      t->next_index = next0;
	      t->policer_index = pi0;
	    }

	  /* verify speculative enqueue, maybe switch current next frame */
	  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);
    }

  vlib_node_increment_counter (vm, node->node_index,
			       VNET_POLICER_ERROR_TRANSMIT, transmitted);
  return frame->n_vectors;
}

uword
vnet_policer_by_sw_if_index (vlib_main_t * vm,
			     vlib_node_runtime_t * node, vlib_frame_t * frame)
{
  return vnet_policer_inline (vm, node, frame,
			      VNET_POLICER_INDEX_BY_SW_IF_INDEX);
}

uword
vnet_policer_by_opaque (vlib_main_t * vm,
			vlib_node_runtime_t * node, vlib_frame_t * frame)
{
  return vnet_policer_inline (vm, node, frame, VNET_POLICER_INDEX_BY_OPAQUE);
}

uword
vnet_policer_by_either (vlib_main_t * vm,
			vlib_node_runtime_t * node, vlib_frame_t * frame)
{
  return vnet_policer_inline (vm, node, frame, VNET_POLICER_INDEX_BY_EITHER);
}

void
vnet_policer_node_funcs_reference (void)
{
}


#define TEST_CODE 1

#ifdef TEST_CODE

/* *INDENT-OFF* */
VLIB_REGISTER_NODE (policer_by_sw_if_index_node, static) = {
  .function = vnet_policer_by_sw_if_index,
  .name = "policer-by-sw-if-index",
  .vector_size = sizeof (u32),
  .format_trace = format_policer_trace,
  .type = VLIB_NODE_TYPE_INTERNAL,

  .n_errors = ARRAY_LEN(vnet_policer_error_strings),
  .error_strings = vnet_policer_error_strings,

  .n_next_nodes = VNET_POLICER_N_NEXT,

  /* edit / add dispositions here */
  .next_nodes = {
    [VNET_POLICER_NEXT_TRANSMIT] = "ethernet-input",
    [VNET_POLICER_NEXT_DROP] = "error-drop",
  },
};

VLIB_NODE_FUNCTION_MULTIARCH (policer_by_sw_if_index_node,
			      vnet_policer_by_sw_if_index);
/* *INDENT-ON* */


int
test_policer_add_del (u32 rx_sw_if_index, u8 * config_name, int is_add)
{
  vnet_policer_main_t *pm = &vnet_policer_main;
  policer_read_response_type_st *template;
  policer_read_response_type_st *policer;
  vnet_hw_interface_t *rxhi;
  uword *p;

  rxhi = vnet_get_sup_hw_interface (pm->vnet_main, rx_sw_if_index);

  /* Make sure caller didn't pass a vlan subif, etc. */
  if (rxhi->sw_if_index != rx_sw_if_index)
    return VNET_API_ERROR_INVALID_SW_IF_INDEX;

  if (is_add)
    {

      p = hash_get_mem (pm->policer_config_by_name, config_name);

      if (p == 0)
	return -2;

      template = pool_elt_at_index (pm->policer_templates, p[0]);

      vnet_hw_interface_rx_redirect_to_node
	(pm->vnet_main, rxhi->hw_if_index, policer_by_sw_if_index_node.index);

      pool_get_aligned (pm->policers, policer, CLIB_CACHE_LINE_BYTES);

      policer[0] = template[0];

      vec_validate (pm->policer_index_by_sw_if_index, rx_sw_if_index);
      pm->policer_index_by_sw_if_index[rx_sw_if_index]
	= policer - pm->policers;
    }
  else
    {
      u32 pi;
      vnet_hw_interface_rx_redirect_to_node (pm->vnet_main,
					     rxhi->hw_if_index,
					     ~0 /* disable */ );

      pi = pm->policer_index_by_sw_if_index[rx_sw_if_index];
      pm->policer_index_by_sw_if_index[rx_sw_if_index] = ~0;
      pool_put_index (pm->policers, pi);
    }

  return 0;
}

static clib_error_t *
test_policer_command_fn (vlib_main_t * vm,
			 unformat_input_t * input, vlib_cli_command_t * cmd)
{
  vnet_policer_main_t *pm = &vnet_policer_main;
  unformat_input_t _line_input, *line_input = &_line_input;
  u32 rx_sw_if_index;
  int rv;
  u8 *config_name = 0;
  int rx_set = 0;
  int is_add = 1;
  int is_show = 0;
  clib_error_t *error = NULL;

  /* Get a line of input. */
  if (!unformat_user (input, unformat_line_input, line_input))
    return 0;

  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (line_input, "intfc %U", unformat_vnet_sw_interface,
		    pm->vnet_main, &rx_sw_if_index))
	rx_set = 1;
      else if (unformat (line_input, "show"))
	is_show = 1;
      else if (unformat (line_input, "policer %s", &config_name))
	;
      else if (unformat (line_input, "del"))
	is_add = 0;
      else
	break;
    }

  if (rx_set == 0)
    {
      error = clib_error_return (0, "interface not set");
      goto done;
    }

  if (is_show)
    {
      u32 pi = pm->policer_index_by_sw_if_index[rx_sw_if_index];
      policer_read_response_type_st *policer;
      policer = pool_elt_at_index (pm->policers, pi);

      vlib_cli_output (vm, "%U", format_policer_instance, policer);
      goto done;
    }

  if (is_add && config_name == 0)
    {
      error = clib_error_return (0, "policer config name required");
      goto done;
    }

  rv = test_policer_add_del (rx_sw_if_index, config_name, is_add);

  switch (rv)
    {
    case 0:
      break;

    default:
      error = clib_error_return
	(0, "WARNING: vnet_vnet_policer_add_del returned %d", rv);
      goto done;
    }

done:
  unformat_free (line_input);

  return error;
}

/* *INDENT-OFF* */
VLIB_CLI_COMMAND (test_patch_command, static) = {
    .path = "test policer",
    .short_help =
    "intfc <intfc> policer <policer-config-name> [del]",
    .function = test_policer_command_fn,
};
/* *INDENT-ON* */

#endif /* TEST_CODE */


typedef struct
{
  u32 sw_if_index;
  u32 next_index;
  u32 table_index;
  u32 offset;
  u32 policer_index;
} policer_classify_trace_t;

static u8 *
format_policer_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 *);
  policer_classify_trace_t *t = va_arg (*args, policer_classify_trace_t *);

  s = format (s, "POLICER_CLASSIFY: sw_if_index %d next %d table %d offset %d"
	      " policer_index %d",
	      t->sw_if_index, t->next_index, t->table_index, t->offset,
	      t->policer_index);
  return s;
}

#define foreach_policer_classify_error                 \
_(MISS, "Policer classify misses")                     \
_(HIT, "Policer classify hits")                        \
_(CHAIN_HIT, "Policer classify hits after chain walk") \
_(DROP, "Policer classify action drop")

typedef enum
{
#define _(sym,str) POLICER_CLASSIFY_ERROR_##sym,
  foreach_policer_classify_error
#undef _
    POLICER_CLASSIFY_N_ERROR,
} policer_classify_error_t;

static char *policer_classify_error_strings[] = {
#define _(sym,string) string,
  foreach_policer_classify_error
#undef _
};

static inline uword
policer_classify_inline (vlib_main_t * vm,
			 vlib_node_runtime_t * node,
			 vlib_frame_t * frame,
			 policer_classify_table_id_t tid)
{
  u32 n_left_from, *from, *to_next;
  policer_classify_next_index_t next_index;
  policer_classify_main_t *pcm = &policer_classify_main;
  vnet_classify_main_t *vcm = pcm->vnet_classify_main;
  f64 now = vlib_time_now (vm);
  u32 hits = 0;
  u32 misses = 0;
  u32 chain_hits = 0;
  u32 n_next_nodes;
  u64 time_in_policer_periods;

  time_in_policer_periods =
    clib_cpu_time_now () >> POLICER_TICKS_PER_PERIOD_SHIFT;

  n_next_nodes = node->n_next_nodes;

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

  /* First pass: compute hashes */
  while (n_left_from > 2)
    {
      vlib_buffer_t *b0, *b1;
      u32 bi0, bi1;
      u8 *h0, *h1;
      u32 sw_if_index0, sw_if_index1;
      u32 table_index0, table_index1;
      vnet_classify_table_t *t0, *t1;

      /* Prefetch next iteration */
      {
	vlib_buffer_t *p1, *p2;

	p1 = vlib_get_buffer (vm, from[1]);
	p2 = vlib_get_buffer (vm, from[2]);

	vlib_prefetch_buffer_header (p1, STORE);
	CLIB_PREFETCH (p1->data, CLIB_CACHE_LINE_BYTES, STORE);
	vlib_prefetch_buffer_header (p2, STORE);
	CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
      }

      bi0 = from[0];
      b0 = vlib_get_buffer (vm, bi0);
      h0 = b0->data;

      bi1 = from[1];
      b1 = vlib_get_buffer (vm, bi1);
      h1 = b1->data;

      sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
      table_index0 =
	pcm->classify_table_index_by_sw_if_index[tid][sw_if_index0];

      sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
      table_index1 =
	pcm->classify_table_index_by_sw_if_index[tid][sw_if_index1];

      t0 = pool_elt_at_index (vcm->tables, table_index0);

      t1 = pool_elt_at_index (vcm->tables, table_index1);

      vnet_buffer (b0)->l2_classify.hash =
	vnet_classify_hash_packet (t0, (u8 *) h0);

      vnet_classify_prefetch_bucket (t0, vnet_buffer (b0)->l2_classify.hash);

      vnet_buffer (b1)->l2_classify.hash =
	vnet_classify_hash_packet (t1, (u8 *) h1);

      vnet_classify_prefetch_bucket (t1, vnet_buffer (b1)->l2_classify.hash);

      vnet_buffer (b0)->l2_classify.table_index = table_index0;

      vnet_buffer (b1)->l2_classify.table_index = table_index1;

      from += 2;
      n_left_from -= 2;
    }

  while (n_left_from > 0)
    {
      vlib_buffer_t *b0;
      u32 bi0;
      u8 *h0;
      u32 sw_if_index0;
      u32 table_index0;
      vnet_classify_table_t *t0;

      bi0 = from[0];
      b0 = vlib_get_buffer (vm, bi0);
      h0 = b0->data;

      sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
      table_index0 =
	pcm->classify_table_index_by_sw_if_index[tid][sw_if_index0];

      t0 = pool_elt_at_index (vcm->tables, table_index0);
      vnet_buffer (b0)->l2_classify.hash =
	vnet_classify_hash_packet (t0, (u8 *) h0);

      vnet_buffer (b0)->l2_classify.table_index = table_index0;
      vnet_classify_prefetch_bucket (t0, vnet_buffer (b0)->l2_classify.hash);

      from++;
      n_left_from--;
    }

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

  while (n_left_from > 0)
    {
      u32 n_left_to_next;

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

      /* Not enough load/store slots to dual loop... */
      while (n_left_from > 0 && n_left_to_next > 0)
	{
	  u32 bi0;
	  vlib_buffer_t *b0;
	  u32 next0 = POLICER_CLASSIFY_NEXT_INDEX_DROP;
	  u32 table_index0;
	  vnet_classify_table_t *t0;
	  vnet_classify_entry_t *e0;
	  u64 hash0;
	  u8 *h0;
	  u8 act0;

	  /* Stride 3 seems to work best */
	  if (PREDICT_TRUE (n_left_from > 3))
	    {
	      vlib_buffer_t *p1 = vlib_get_buffer (vm, from[3]);
	      vnet_classify_table_t *tp1;
	      u32 table_index1;
	      u64 phash1;

	      table_index1 = vnet_buffer (p1)->l2_classify.table_index;

	      if (PREDICT_TRUE (table_index1 != ~0))
		{
		  tp1 = pool_elt_at_index (vcm->tables, table_index1);
		  phash1 = vnet_buffer (p1)->l2_classify.hash;
		  vnet_classify_prefetch_entry (tp1, phash1);
		}
	    }

	  /* Speculatively enqueue b0 to the current next frame */
	  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);
	  h0 = b0->data;
	  table_index0 = vnet_buffer (b0)->l2_classify.table_index;
	  e0 = 0;
	  t0 = 0;

	  if (tid == POLICER_CLASSIFY_TABLE_L2)
	    {
	      /* Feature bitmap update and determine the next node */
	      next0 = vnet_l2_feature_next (b0, pcm->feat_next_node_index,
					    L2INPUT_FEAT_POLICER_CLAS);
	    }
	  else
	    vnet_get_config_data (pcm->vnet_config_main[tid],
				  &b0->current_config_index, &next0,
				  /* # bytes of config data */ 0);

	  vnet_buffer (b0)->l2_classify.opaque_index = ~0;

	  if (PREDICT_TRUE (table_index0 != ~0))
	    {
	      hash0 = vnet_buffer (b0)->l2_classify.hash;
	      t0 = pool_elt_at_index (vcm->tables, table_index0);
	      e0 = vnet_classify_find_entry (t0, (u8 *) h0, hash0, now);

	      if (e0)
		{
		  act0 = vnet_policer_police (vm,
					      b0,
					      e0->next_index,
					      time_in_policer_periods,
					      e0->opaque_index);
		  if (PREDICT_FALSE (act0 == SSE2_QOS_ACTION_DROP))
		    {
		      next0 = POLICER_CLASSIFY_NEXT_INDEX_DROP;
		      b0->error = node->errors[POLICER_CLASSIFY_ERROR_DROP];
		    }
		  hits++;
		}
	      else
		{
		  while (1)
		    {
		      if (PREDICT_TRUE (t0->next_table_index != ~0))
			{
			  t0 = pool_elt_at_index (vcm->tables,
						  t0->next_table_index);
			}
		      else
			{
			  next0 = (t0->miss_next_index < n_next_nodes) ?
			    t0->miss_next_index : next0;
			  misses++;
			  break;
			}

		      hash0 = vnet_classify_hash_packet (t0, (u8 *) h0);
		      e0 =
			vnet_classify_find_entry (t0, (u8 *) h0, hash0, now);
		      if (e0)
			{
			  act0 = vnet_policer_police (vm,
						      b0,
						      e0->next_index,
						      time_in_policer_periods,
						      e0->opaque_index);
			  if (PREDICT_FALSE (act0 == SSE2_QOS_ACTION_DROP))
			    {
			      next0 = POLICER_CLASSIFY_NEXT_INDEX_DROP;
			      b0->error =
				node->errors[POLICER_CLASSIFY_ERROR_DROP];
			    }
			  hits++;
			  chain_hits++;
			  break;
			}
		    }
		}
	    }
	  if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
			     && (b0->flags & VLIB_BUFFER_IS_TRACED)))
	    {
	      policer_classify_trace_t *t =
		vlib_add_trace (vm, node, b0, sizeof (*t));
	      t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
	      t->next_index = next0;
	      t->table_index = t0 ? t0 - vcm->tables : ~0;
	      t->offset = (e0 && t0) ? vnet_classify_get_offset (t0, e0) : ~0;
	      t->policer_index = e0 ? e0->next_index : ~0;
	    }

	  /* Verify speculative enqueue, maybe switch current next frame */
	  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);
    }

  vlib_node_increment_counter (vm, node->node_index,
			       POLICER_CLASSIFY_ERROR_MISS, misses);
  vlib_node_increment_counter (vm, node->node_index,
			       POLICER_CLASSIFY_ERROR_HIT, hits);
  vlib_node_increment_counter (vm, node->node_index,
			       POLICER_CLASSIFY_ERROR_CHAIN_HIT, chain_hits);

  return frame->n_vectors;
}

static uword
ip4_policer_classify (vlib_main_t * vm,
		      vlib_node_runtime_t * node, vlib_frame_t * frame)
{
  return policer_classify_inline (vm, node, frame,
				  POLICER_CLASSIFY_TABLE_IP4);
}

/* *INDENT-OFF* */
VLIB_REGISTER_NODE (ip4_policer_classify_node) = {
  .function = ip4_policer_classify,
  .name = "ip4-policer-classify",
  .vector_size = sizeof (u32),
  .format_trace = format_policer_classify_trace,
  .n_errors = ARRAY_LEN(policer_classify_error_strings),
  .error_strings = policer_classify_error_strings,
  .n_next_nodes = POLICER_CLASSIFY_NEXT_INDEX_N_NEXT,
  .next_nodes = {
    [POLICER_CLASSIFY_NEXT_INDEX_DROP] = "error-drop",
  },
};

VLIB_NODE_FUNCTION_MULTIARCH (ip4_policer_classify_node, ip4_policer_classify);
/* *INDENT-ON* */

static uword
ip6_policer_classify (vlib_main_t * vm,
		      vlib_node_runtime_t * node, vlib_frame_t * frame)
{
  return policer_classify_inline (vm, node, frame,
				  POLICER_CLASSIFY_TABLE_IP6);
}

/* *INDENT-OFF* */
VLIB_REGISTER_NODE (ip6_policer_classify_node) = {
  .function = ip6_policer_classify,
  .name = "ip6-policer-classify",
  .vector_size = sizeof (u32),
  .format_trace = format_policer_classify_trace,
  .n_errors = ARRAY_LEN(policer_classify_error_strings),
  .error_strings = policer_classify_error_strings,
  .n_next_nodes = POLICER_CLASSIFY_NEXT_INDEX_N_NEXT,
  .next_nodes = {
    [POLICER_CLASSIFY_NEXT_INDEX_DROP] = "error-drop",
  },
};

VLIB_NODE_FUNCTION_MULTIARCH (ip6_policer_classify_node, ip6_policer_classify);
/* *INDENT-ON* */

static uword
l2_policer_classify (vlib_main_t * vm,
		     vlib_node_runtime_t * node, vlib_frame_t * frame)
{
  return policer_classify_inline (vm, node, frame, POLICER_CLASSIFY_TABLE_L2);
}

/* *INDENT-OFF* */
VLIB_REGISTER_NODE (l2_policer_classify_node) = {
  .function = l2_policer_classify,
  .name = "l2-policer-classify",
  .vector_size = sizeof (u32),
  .format_trace = format_policer_classify_trace,
  .n_errors = ARRAY_LEN (policer_classify_error_strings),
  .error_strings = policer_classify_error_strings,
  .n_next_nodes = POLICER_CLASSIFY_NEXT_INDEX_N_NEXT,
  .next_nodes = {
    [POLICER_CLASSIFY_NEXT_INDEX_DROP] = "error-drop",
  },
};

VLIB_NODE_FUNCTION_MULTIARCH (l2_policer_classify_node, l2_policer_classify);
/* *INDENT-ON* */

static clib_error_t *
policer_classify_init (vlib_main_t * vm)
{
  policer_classify_main_t *pcm = &policer_classify_main;

  pcm->vlib_main = vm;
  pcm->vnet_main = vnet_get_main ();
  pcm->vnet_classify_main = &vnet_classify_main;

  /* Initialize L2 feature next-node indexes */
  feat_bitmap_init_next_nodes (vm,
			       l2_policer_classify_node.index,
			       L2INPUT_N_FEAT,
			       l2input_get_feat_names (),
			       pcm->feat_next_node_index);

  return 0;
}

VLIB_INIT_FUNCTION (policer_classify_init);

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