FD.io VPP  v16.06
Vector Packet Processing
l2_patch.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include <vlib/vlib.h>
16 #include <vnet/vnet.h>
17 #include <vnet/pg/pg.h>
18 #include <vnet/ethernet/ethernet.h>
19 #include <vppinfra/error.h>
20 
21 typedef struct {
24 
25  /* vector of dispositions, indexed by rx_sw_if_index */
28 
29  /* convenience variables */
33 
34 typedef struct {
38 
39 /* packet trace format function */
40 static u8 * format_l2_patch_trace (u8 * s, va_list * args)
41 {
42  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
43  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
44  l2_patch_trace_t * t = va_arg (*args, l2_patch_trace_t *);
45 
46  s = format (s, "L2_PATCH: rx %d tx %d", t->rx_sw_if_index,
47  t->tx_sw_if_index);
48  return s;
49 }
50 
52 
54 
55 #define foreach_l2_patch_error \
56 _(PATCHED, "L2 patch packets") \
57 _(DROPPED, "L2 patch misconfigured drops")
58 
59 typedef enum {
60 #define _(sym,str) L2_PATCH_ERROR_##sym,
62 #undef _
65 
66 static char * l2_patch_error_strings[] = {
67 #define _(sym,string) string,
69 #undef _
70 };
71 
72 typedef enum {
76 
77 static uword
79  vlib_node_runtime_t * node,
80  vlib_frame_t * frame)
81 {
82  u32 n_left_from, * from, * to_next;
83  l2_patch_next_t next_index;
85  vlib_node_t *n = vlib_get_node (vm, l2_patch_node.index);
86  u32 node_counter_base_index = n->error_heap_index;
87  vlib_error_main_t * em = &vm->error_main;
88 
89  from = vlib_frame_vector_args (frame);
90  n_left_from = frame->n_vectors;
91  next_index = node->cached_next_index;
92 
93  while (n_left_from > 0)
94  {
95  u32 n_left_to_next;
96 
97  vlib_get_next_frame (vm, node, next_index,
98  to_next, n_left_to_next);
99 
100  while (n_left_from >= 4 && n_left_to_next >= 2)
101  {
102  u32 bi0, bi1;
103  vlib_buffer_t * b0, * b1;
104  u32 next0, next1;
105  u32 sw_if_index0, sw_if_index1;
106 
107  /* Prefetch next iteration. */
108  {
109  vlib_buffer_t * p2, * p3;
110 
111  p2 = vlib_get_buffer (vm, from[2]);
112  p3 = vlib_get_buffer (vm, from[3]);
113 
114  vlib_prefetch_buffer_header (p2, LOAD);
115  vlib_prefetch_buffer_header (p3, LOAD);
116 
117  /* So stupid / simple, we don't need to prefetch data */
118  }
119 
120  /* speculatively enqueue b0 and b1 to the current next frame */
121  to_next[0] = bi0 = from[0];
122  to_next[1] = bi1 = from[1];
123  from += 2;
124  to_next += 2;
125  n_left_from -= 2;
126  n_left_to_next -= 2;
127 
128  b0 = vlib_get_buffer (vm, bi0);
129  b1 = vlib_get_buffer (vm, bi1);
130 
131  sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
132  sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX];
133 
134  ASSERT(l2pm->tx_next_by_rx_sw_if_index[sw_if_index0] != ~0);
135  ASSERT(l2pm->tx_sw_if_index_by_rx_sw_if_index[sw_if_index0] != ~0);
136  ASSERT(l2pm->tx_next_by_rx_sw_if_index[sw_if_index1] != ~0);
137  ASSERT(l2pm->tx_sw_if_index_by_rx_sw_if_index[sw_if_index1] != ~0);
138 
139  if (PREDICT_TRUE (sw_if_index0 == l2pm->cached_rx_sw_if_index))
140  next0 = l2pm->cached_next_index;
141  else
142  {
143  next0 = l2pm->tx_next_by_rx_sw_if_index[sw_if_index0];
144  l2pm->cached_rx_sw_if_index = sw_if_index0;
145  l2pm->cached_next_index = next0;
146  }
147 
148  if (PREDICT_TRUE (sw_if_index1 == l2pm->cached_rx_sw_if_index))
149  next1 = l2pm->cached_next_index;
150  else
151  next1 = l2pm->tx_next_by_rx_sw_if_index [sw_if_index1];
152 
154  {
155  if (b0->flags & VLIB_BUFFER_IS_TRACED)
156  {
157  l2_patch_trace_t *t =
158  vlib_add_trace (vm, node, b0, sizeof (*t));
159  t->rx_sw_if_index = sw_if_index0;
160  t->tx_sw_if_index =
161  l2pm->tx_sw_if_index_by_rx_sw_if_index [sw_if_index0];
162  }
163  if (b1->flags & VLIB_BUFFER_IS_TRACED)
164  {
165  l2_patch_trace_t *t =
166  vlib_add_trace (vm, node, b1, sizeof (*t));
167  t->rx_sw_if_index = sw_if_index1;
168  t->tx_sw_if_index =
169  l2pm->tx_sw_if_index_by_rx_sw_if_index [sw_if_index1];
170  }
171  }
172 
173  /* verify speculative enqueues, maybe switch current next frame */
174  vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
175  to_next, n_left_to_next,
176  bi0, bi1, next0, next1);
177  }
178 
179  while (n_left_from > 0 && n_left_to_next > 0)
180  {
181  u32 bi0;
182  vlib_buffer_t * b0;
183  u32 next0;
184  u32 sw_if_index0;
185 
186  /* speculatively enqueue b0 to the current next frame */
187  bi0 = from[0];
188  to_next[0] = bi0;
189  from += 1;
190  to_next += 1;
191  n_left_from -= 1;
192  n_left_to_next -= 1;
193 
194  b0 = vlib_get_buffer (vm, bi0);
195 
196  sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
197 
198  ASSERT(l2pm->tx_next_by_rx_sw_if_index[sw_if_index0] != ~0);
199  ASSERT(l2pm->tx_sw_if_index_by_rx_sw_if_index[sw_if_index0] != ~0);
200 
201  if (PREDICT_TRUE (sw_if_index0 == l2pm->cached_rx_sw_if_index))
202  next0 = l2pm->cached_next_index;
203  else
204  {
205  next0 = l2pm->tx_next_by_rx_sw_if_index [sw_if_index0];
206  l2pm->cached_rx_sw_if_index = sw_if_index0;
207  l2pm->cached_next_index = next0;
208  }
209 
211  {
212  if (b0->flags & VLIB_BUFFER_IS_TRACED)
213  {
214  l2_patch_trace_t *t =
215  vlib_add_trace (vm, node, b0, sizeof (*t));
216  t->rx_sw_if_index = sw_if_index0;
217  t->tx_sw_if_index =
218  l2pm->tx_sw_if_index_by_rx_sw_if_index [sw_if_index0];
219  }
220  }
221 
222  /* verify speculative enqueue, maybe switch current next frame */
223  vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
224  to_next, n_left_to_next,
225  bi0, next0);
226  }
227 
228  vlib_put_next_frame (vm, node, next_index, n_left_to_next);
229  }
230 
231  em->counters[node_counter_base_index + L2_PATCH_ERROR_PATCHED] +=
232  frame->n_vectors;
233 
234  return frame->n_vectors;
235 }
236 
237 VLIB_REGISTER_NODE (l2_patch_node, static) = {
238  .function = l2_patch_node_fn,
239  .name = "l2_patch",
240  .vector_size = sizeof (u32),
241  .format_trace = format_l2_patch_trace,
243 
244  .n_errors = ARRAY_LEN(l2_patch_error_strings),
245  .error_strings = l2_patch_error_strings,
246 
247  .n_next_nodes = L2_PATCH_N_NEXT,
248 
249  /* edit / add dispositions here */
250  .next_nodes = {
251  [L2_PATCH_NEXT_DROP] = "error-drop",
252  },
253 };
254 
255 int vnet_l2_patch_add_del (u32 rx_sw_if_index, u32 tx_sw_if_index, int is_add)
256 {
257  l2_patch_main_t * l2pm = &l2_patch_main;
258  vnet_hw_interface_t * rxhi, *txhi;
259  u32 tx_next_index;
260 
261  /*
262  * We assume that the API msg handler has used 2x VALIDATE_SW_IF_INDEX
263  * macros...
264  */
265 
266  rxhi = vnet_get_sup_hw_interface (l2pm->vnet_main, rx_sw_if_index);
267 
268  /* Make sure caller didn't pass a vlan subif, etc. */
269  if (rxhi->sw_if_index != rx_sw_if_index)
270  return VNET_API_ERROR_INVALID_SW_IF_INDEX;
271 
272  txhi = vnet_get_sup_hw_interface (l2pm->vnet_main, tx_sw_if_index);
273  if (txhi->sw_if_index != tx_sw_if_index)
274  return VNET_API_ERROR_INVALID_SW_IF_INDEX_2;
275 
276  if (is_add)
277  {
278  tx_next_index = vlib_node_add_next (l2pm->vlib_main,
279  l2_patch_node.index,
280  txhi->output_node_index);
281 
283  rx_sw_if_index, ~0);
284 
285  l2pm->tx_next_by_rx_sw_if_index[rx_sw_if_index] = tx_next_index;
287  rx_sw_if_index, ~0);
288  l2pm->tx_sw_if_index_by_rx_sw_if_index[rx_sw_if_index]
289  = txhi->sw_if_index;
290 
293 
295  rxhi->hw_if_index,
296  l2_patch_node.index);
297  }
298  else
299  {
301  0 /* disable promiscuous mode */);
302 
304  rxhi->hw_if_index,
305  ~0 /* disable */);
306  if (vec_len (l2pm->tx_next_by_rx_sw_if_index) > rx_sw_if_index)
307  {
308  l2pm->tx_next_by_rx_sw_if_index[rx_sw_if_index] = ~0;
309  l2pm->tx_sw_if_index_by_rx_sw_if_index[rx_sw_if_index] = ~0;
310  }
311  }
312 
313  return 0;
314 }
315 
316 static clib_error_t *
318  unformat_input_t * input,
319  vlib_cli_command_t * cmd)
320 {
321  l2_patch_main_t * l2pm = &l2_patch_main;
322  unformat_input_t _line_input, * line_input = &_line_input;
323  u32 rx_sw_if_index, tx_sw_if_index;
324  int rv;
325  int rx_set = 0;
326  int tx_set = 0;
327  int is_add = 1;
328 
329  /* Get a line of input. */
330  if (! unformat_user (input, unformat_line_input, line_input))
331  return 0;
332 
333  while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
334  {
335  if (unformat (line_input, "rx %U", unformat_vnet_sw_interface,
336  l2pm->vnet_main, &rx_sw_if_index))
337  rx_set = 1;
338  else if (unformat (line_input, "tx %U", unformat_vnet_sw_interface,
339  l2pm->vnet_main, &tx_sw_if_index))
340  tx_set = 1;
341  else if (unformat (line_input, "del"))
342  is_add = 0;
343  else break;
344  }
345 
346  if (rx_set == 0)
347  return clib_error_return (0, "rx interface not set");
348 
349  if (tx_set == 0)
350  return clib_error_return (0, "tx interface not set");
351 
352  rv = vnet_l2_patch_add_del (rx_sw_if_index, tx_sw_if_index, is_add);
353 
354  switch (rv)
355  {
356  case 0:
357  break;
358 
359  case VNET_API_ERROR_INVALID_SW_IF_INDEX:
360  return clib_error_return (0, "rx interface not a physical port");
361 
362  case VNET_API_ERROR_INVALID_SW_IF_INDEX_2:
363  return clib_error_return (0, "tx interface not a physical port");
364 
365  default:
366  return clib_error_return
367  (0, "WARNING: vnet_l2_patch_add_del returned %d", rv);
368  }
369 
370  return 0;
371 }
372 
373 VLIB_CLI_COMMAND (test_patch_command, static) = {
374  .path = "test l2patch",
375  .short_help =
376  "rx <intfc> tx <intfc> [del]",
377  .function = test_patch_command_fn,
378 };
379 
380 // Display the contents of the l2patch table.
381 static clib_error_t *
383  unformat_input_t * input,
384  vlib_cli_command_t * cmd)
385 {
386  l2_patch_main_t * l2pm = &l2_patch_main;
387  u32 rx_sw_if_index;
388  u32 no_entries = 1;
389 
392 
393  for (rx_sw_if_index = 0;
394  rx_sw_if_index < vec_len (l2pm->tx_sw_if_index_by_rx_sw_if_index);
395  rx_sw_if_index++)
396  {
397  u32 tx_sw_if_index =
398  l2pm->tx_sw_if_index_by_rx_sw_if_index[rx_sw_if_index];
399  if (tx_sw_if_index != ~0)
400  {
401  no_entries = 0;
402  vlib_cli_output (vm, "%26U -> %U",
404  l2pm->vnet_main, rx_sw_if_index,
406  l2pm->vnet_main,tx_sw_if_index);
407  }
408  }
409 
410  if (no_entries)
411  vlib_cli_output (vm, "no l2patch entries");
412 
413  return 0;
414 }
415 
416 VLIB_CLI_COMMAND (show_l2patch_cli, static) = {
417  .path = "show l2patch",
418  .short_help = "Show l2 interface cross-connect entries",
419  .function = show_l2patch,
420 };
421 
423 {
425 
426  mp->vlib_main = vm;
427  mp->vnet_main = vnet_get_main();
428 
429  return 0;
430 }
431 
void vlib_put_next_frame(vlib_main_t *vm, vlib_node_runtime_t *r, u32 next_index, u32 n_vectors_left)
Definition: main.c:459
vnet_main_t * vnet_main
Definition: l2_patch.c:31
static clib_error_t * show_l2patch(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
Definition: l2_patch.c:382
u32 error_heap_index
Definition: node.h:244
#define CLIB_UNUSED(x)
Definition: clib.h:79
uword unformat(unformat_input_t *i, char *fmt,...)
Definition: unformat.c:942
always_inline vlib_node_t * vlib_get_node(vlib_main_t *vm, u32 i)
Definition: node_funcs.h:46
bad routing header type(not 4)") sr_error (NO_MORE_SEGMENTS
#define PREDICT_TRUE(x)
Definition: clib.h:98
int vnet_hw_interface_rx_redirect_to_node(vnet_main_t *vnm, u32 hw_if_index, u32 node_index)
Definition: interface.c:901
#define UNFORMAT_END_OF_INPUT
Definition: format.h:142
struct _vlib_node_registration vlib_node_registration_t
u32 cached_next_index
Definition: l2_patch.c:22
unformat_function_t unformat_vnet_sw_interface
format_function_t format_vnet_sw_if_index_name
always_inline uword unformat_check_input(unformat_input_t *i)
Definition: format.h:168
vnet_main_t * vnet_get_main(void)
Definition: misc.c:45
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:109
uword unformat_user(unformat_input_t *input, unformat_function_t *func,...)
Definition: unformat.c:953
always_inline void * vlib_frame_vector_args(vlib_frame_t *f)
Definition: node_funcs.h:202
vlib_error_main_t error_main
Definition: main.h:124
static u8 * format_l2_patch_trace(u8 *s, va_list *args)
Definition: l2_patch.c:40
static char * l2_patch_error_strings[]
Definition: l2_patch.c:66
#define PREDICT_FALSE(x)
Definition: clib.h:97
#define vlib_validate_buffer_enqueue_x2(vm, node, next_index, to_next, n_left_to_next, bi0, bi1, next0, next1)
Definition: buffer_node.h:43
#define vlib_validate_buffer_enqueue_x1(vm, node, next_index, to_next, n_left_to_next, bi0, next0)
Definition: buffer_node.h:83
#define vlib_get_next_frame(vm, node, next_index, vectors, n_vectors_left)
Definition: node_funcs.h:265
void vlib_cli_output(vlib_main_t *vm, char *fmt,...)
Definition: cli.c:538
u64 * counters
Definition: error.h:73
u16 n_vectors
Definition: node.h:307
l2_patch_main_t l2_patch_main
Definition: l2_patch.c:51
u32 tx_sw_if_index
Definition: l2_patch.c:36
u32 * tx_next_by_rx_sw_if_index
Definition: l2_patch.c:26
always_inline vnet_hw_interface_t * vnet_get_sup_hw_interface(vnet_main_t *vnm, u32 sw_if_index)
#define ETHERNET_INTERFACE_FLAG_ACCEPT_ALL
Definition: ethernet.h:81
#define ARRAY_LEN(x)
Definition: clib.h:59
l2_patch_error_t
Definition: l2_patch.c:59
static vlib_node_registration_t l2_patch_node
(constructor) VLIB_REGISTER_NODE (l2_patch_node)
Definition: l2_patch.c:53
#define foreach_l2_patch_error
Definition: l2_patch.c:55
#define VLIB_CLI_COMMAND(x,...)
Definition: cli.h:150
u16 cached_next_index
Definition: node.h:422
#define ASSERT(truth)
unsigned int u32
Definition: types.h:88
vlib_main_t * vlib_main
Definition: l2_patch.c:30
#define vnet_buffer(b)
Definition: buffer.h:300
u8 * format(u8 *s, char *fmt,...)
Definition: format.c:405
static uword l2_patch_node_fn(vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
Definition: l2_patch.c:78
u32 * tx_sw_if_index_by_rx_sw_if_index
Definition: l2_patch.c:27
#define VLIB_NODE_FLAG_TRACE
Definition: node.h:225
static clib_error_t * test_patch_command_fn(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
Definition: l2_patch.c:317
always_inline uword vlib_node_add_next(vlib_main_t *vm, uword node, uword next_node)
Definition: node_funcs.h:919
#define VLIB_BUFFER_IS_TRACED
Definition: buffer.h:91
l2_patch_next_t
Definition: l2_patch.c:72
u64 uword
Definition: types.h:112
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
unsigned char u8
Definition: types.h:56
int vnet_l2_patch_add_del(u32 rx_sw_if_index, u32 tx_sw_if_index, int is_add)
Definition: l2_patch.c:255
always_inline void * vlib_add_trace(vlib_main_t *vm, vlib_node_runtime_t *r, vlib_buffer_t *b, u32 n_data_bytes)
Definition: trace_funcs.h:55
#define vlib_prefetch_buffer_header(b, type)
Prefetch buffer metadata.
Definition: buffer.h:162
u32 rx_sw_if_index
Definition: l2_patch.c:35
#define VLIB_REGISTER_NODE(x,...)
Definition: node.h:140
#define clib_error_return(e, args...)
Definition: error.h:112
struct _unformat_input_t unformat_input_t
#define vec_validate_init_empty(V, I, INIT)
Make sure vector is long enough for given index and initialize empty space (no header, unspecified alignment)
Definition: vec.h:443
u32 flags
buffer flags: VLIB_BUFFER_IS_TRACED: trace this buffer.
Definition: buffer.h:84
unformat_function_t unformat_line_input
Definition: format.h:279
always_inline vlib_buffer_t * vlib_get_buffer(vlib_main_t *vm, u32 buffer_index)
Translate buffer index into buffer pointer.
Definition: buffer_funcs.h:69
u32 cached_rx_sw_if_index
Definition: l2_patch.c:23
Definition: defs.h:45
clib_error_t * l2_patch_init(vlib_main_t *vm)
Definition: l2_patch.c:422
u32 ethernet_set_flags(vnet_main_t *vnm, u32 hw_if_index, u32 flags)
Definition: interface.c:243