FD.io VPP  v16.09
Vector Packet Processing
l2_vtr.c
Go to the documentation of this file.
1 /*
2  * l2_vtr.c : layer 2 vlan tag rewrite configuration
3  *
4  * Copyright (c) 2013 Cisco and/or its affiliates.
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 #include <vlib/vlib.h>
19 #include <vnet/vnet.h>
20 #include <vnet/ethernet/ethernet.h>
21 #include <vnet/ethernet/packet.h>
22 #include <vnet/l2/l2_input.h>
23 #include <vnet/l2/l2_output.h>
24 #include <vnet/l2/feat_bitmap.h>
25 #include <vnet/l2/l2_vtr.h>
26 #include <vnet/l2/l2_input_vtr.h>
27 #include <vnet/l2/l2_output.h>
28 
29 #include <vppinfra/error.h>
30 #include <vlib/cli.h>
31 
32 
33 /** Just a placeholder; ensures file is not eliminated by linker. */
36 {
37  return 0;
38 }
39 
41 
42 
43 /**
44  * Configure vtag tag rewrite on the given interface.
45  * Return 1 if there is an error, 0 if ok
46  */
47 u32
48 l2vtr_configure (vlib_main_t * vlib_main, vnet_main_t * vnet_main, u32 sw_if_index, u32 vtr_op, u32 push_dot1q, /* ethertype of first pushed tag is dot1q/dot1ad */
49  u32 vtr_tag1, /* first pushed tag */
50  u32 vtr_tag2) /* second pushed tag */
51 {
54  u32 hw_no_tags;
55  u32 error = 0;
56  vtr_config_t *in_config;
57  vtr_config_t *out_config;
58  u32 enable;
59  u32 push_inner_et;
60  u32 push_outer_et;
61  u32 cfg_tags;
62 
63  hi = vnet_get_sup_hw_interface (vnet_main, sw_if_index);
64  if (!hi || (hi->hw_class_index != ethernet_hw_interface_class.index))
65  {
66  error = VNET_API_ERROR_INVALID_INTERFACE; /* non-ethernet interface */
67  goto done;
68  }
69 
70  /* Init the config for this interface */
71  vec_validate (l2output_main.configs, sw_if_index);
72  in_config =
73  &(vec_elt_at_index (l2output_main.configs, sw_if_index)->input_vtr);
74  out_config =
75  &(vec_elt_at_index (l2output_main.configs, sw_if_index)->output_vtr);
76  in_config->raw_tags = 0;
77  out_config->raw_tags = 0;
78 
79  /* Get the configured tags for the interface */
80  si = vnet_get_sw_interface (vnet_main, sw_if_index);
81  hw_no_tags = (si->type == VNET_SW_INTERFACE_TYPE_HARDWARE);
82 
83  /* Construct the input tag-rewrite config */
84 
85  push_outer_et =
86  clib_net_to_host_u16 (push_dot1q ? ETHERNET_TYPE_VLAN :
87  ETHERNET_TYPE_DOT1AD);
88  push_inner_et = clib_net_to_host_u16 (ETHERNET_TYPE_VLAN);
89  vtr_tag1 = clib_net_to_host_u16 (vtr_tag1);
90  vtr_tag2 = clib_net_to_host_u16 (vtr_tag2);
91 
92  /* Determine number of vlan tags with explictly configured values */
93  cfg_tags = 0;
94  if (hw_no_tags || si->sub.eth.flags.no_tags)
95  {
96  cfg_tags = 0;
97  }
98  else if (si->sub.eth.flags.one_tag)
99  {
100  cfg_tags = 1;
101  if (si->sub.eth.flags.outer_vlan_id_any)
102  {
103  cfg_tags = 0;
104  }
105  }
106  else if (si->sub.eth.flags.two_tags)
107  {
108  cfg_tags = 2;
109  if (si->sub.eth.flags.inner_vlan_id_any)
110  {
111  cfg_tags = 1;
112  }
113  if (si->sub.eth.flags.outer_vlan_id_any)
114  {
115  cfg_tags = 0;
116  }
117  }
118 
119  switch (vtr_op)
120  {
121  case L2_VTR_DISABLED:
122  in_config->push_and_pop_bytes = 0;
123  break;
124 
125  case L2_VTR_POP_1:
126  if (cfg_tags < 1)
127  {
128  /* Need one or two tags */
129  error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT;
130  goto done;
131  }
132  in_config->pop_bytes = 4;
133  in_config->push_bytes = 0;
134  break;
135 
136  case L2_VTR_POP_2:
137  if (cfg_tags < 2)
138  {
139  error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; /* Need two tags */
140  goto done;
141  }
142  in_config->pop_bytes = 8;
143  in_config->push_bytes = 0;
144 
145  out_config->push_bytes = in_config->pop_bytes;
146  out_config->pop_bytes = in_config->push_bytes;
147  break;
148 
149  case L2_VTR_PUSH_1:
150  in_config->pop_bytes = 0;
151  in_config->push_bytes = 4;
152  in_config->tags[1].priority_cfi_and_id = vtr_tag1;
153  in_config->tags[1].type = push_outer_et;
154  break;
155 
156  case L2_VTR_PUSH_2:
157  in_config->pop_bytes = 0;
158  in_config->push_bytes = 8;
159  in_config->tags[0].priority_cfi_and_id = vtr_tag1;
160  in_config->tags[0].type = push_outer_et;
161  in_config->tags[1].priority_cfi_and_id = vtr_tag2;
162  in_config->tags[1].type = push_inner_et;
163  break;
164 
166  if (cfg_tags < 1)
167  {
168  error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; /* Need one or two tags */
169  goto done;
170  }
171  in_config->pop_bytes = 4;
172  in_config->push_bytes = 4;
173  in_config->tags[1].priority_cfi_and_id = vtr_tag1;
174  in_config->tags[1].type = push_outer_et;
175  break;
176 
178  if (cfg_tags < 1)
179  {
180  error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; /* Need one or two tags */
181  goto done;
182  }
183  in_config->pop_bytes = 4;
184  in_config->push_bytes = 8;
185  in_config->tags[0].priority_cfi_and_id = vtr_tag1;
186  in_config->tags[0].type = push_outer_et;
187  in_config->tags[1].priority_cfi_and_id = vtr_tag2;
188  in_config->tags[1].type = push_inner_et;
189  break;
190 
192  if (cfg_tags < 2)
193  {
194  error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; /* Need two tags */
195  goto done;
196  }
197  in_config->pop_bytes = 8;
198  in_config->push_bytes = 4;
199  in_config->tags[1].priority_cfi_and_id = vtr_tag1;
200  in_config->tags[1].type = push_outer_et;
201  break;
202 
204  if (cfg_tags < 2)
205  {
206  error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; /* Need two tags */
207  goto done;
208  }
209  in_config->pop_bytes = 8;
210  in_config->push_bytes = 8;
211  in_config->tags[0].priority_cfi_and_id = vtr_tag1;
212  in_config->tags[0].type = push_outer_et;
213  in_config->tags[1].priority_cfi_and_id = vtr_tag2;
214  in_config->tags[1].type = push_inner_et;
215  break;
216  }
217 
218  /*
219  * Construct the output tag-rewrite config
220  *
221  * The push/pop values are always reversed
222  */
223  out_config->push_bytes = in_config->pop_bytes;
224  out_config->pop_bytes = in_config->push_bytes;
225 
226  /* Any pushed tags are derived from the subinterface config */
227  push_outer_et =
228  clib_net_to_host_u16 (si->sub.eth.flags.dot1ad ? ETHERNET_TYPE_DOT1AD :
229  ETHERNET_TYPE_VLAN);
230  push_inner_et = clib_net_to_host_u16 (ETHERNET_TYPE_VLAN);
231  vtr_tag1 = clib_net_to_host_u16 (si->sub.eth.outer_vlan_id);
232  vtr_tag2 = clib_net_to_host_u16 (si->sub.eth.inner_vlan_id);
233 
234  if (out_config->push_bytes == 4)
235  {
236  out_config->tags[1].priority_cfi_and_id = vtr_tag1;
237  out_config->tags[1].type = push_outer_et;
238  }
239  else if (out_config->push_bytes == 8)
240  {
241  out_config->tags[0].priority_cfi_and_id = vtr_tag1;
242  out_config->tags[0].type = push_outer_et;
243  out_config->tags[1].priority_cfi_and_id = vtr_tag2;
244  out_config->tags[1].type = push_inner_et;
245  }
246 
247  /* set the interface enable flags */
248  enable = (vtr_op != L2_VTR_DISABLED);
249  l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_VTR, enable);
250  /* output vtr enable is checked explicitly in l2_output */
251 
252 done:
253  return error;
254 }
255 
256 /**
257  * Get vtag tag rewrite on the given interface.
258  * Return 1 if there is an error, 0 if ok
259  */
260 u32
261 l2vtr_get (vlib_main_t * vlib_main, vnet_main_t * vnet_main, u32 sw_if_index, u32 * vtr_op, u32 * push_dot1q, /* ethertype of first pushed tag is dot1q/dot1ad */
262  u32 * vtr_tag1, /* first pushed tag */
263  u32 * vtr_tag2) /* second pushed tag */
264 {
266  u32 error = 0;
267  vtr_config_t *in_config;
268 
269  if (!vtr_op || !push_dot1q || !vtr_tag1 || !vtr_tag2)
270  {
271  clib_warning ("invalid arguments");
272  error = VNET_API_ERROR_INVALID_ARGUMENT;
273  goto done;
274  }
275 
276  *vtr_op = L2_VTR_DISABLED;
277  *vtr_tag1 = 0;
278  *vtr_tag2 = 0;
279  *push_dot1q = 0;
280 
281  hi = vnet_get_sup_hw_interface (vnet_main, sw_if_index);
282  if (!hi || (hi->hw_class_index != ethernet_hw_interface_class.index))
283  {
284  /* non-ethernet interface */
285  goto done;
286  }
287 
288  if (sw_if_index >= vec_len (l2output_main.configs))
289  {
290  /* no specific config (return disabled) */
291  goto done;
292  }
293 
294  /* Get the config for this interface */
295  in_config =
296  &(vec_elt_at_index (l2output_main.configs, sw_if_index)->input_vtr);
297 
298  /* DISABLED */
299  if (in_config->push_and_pop_bytes == 0)
300  {
301  goto done;
302  }
303 
304  /* find out vtr_op */
305  switch (in_config->pop_bytes)
306  {
307  case 0:
308  switch (in_config->push_bytes)
309  {
310  case 0:
311  /* DISABLED */
312  goto done;
313  case 4:
314  *vtr_op = L2_VTR_PUSH_1;
315  *vtr_tag1 =
316  clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
317  *push_dot1q =
318  (ETHERNET_TYPE_VLAN ==
319  clib_host_to_net_u16 (in_config->tags[1].type));
320  break;
321  case 8:
322  *vtr_op = L2_VTR_PUSH_2;
323  *vtr_tag1 =
324  clib_host_to_net_u16 (in_config->tags[0].priority_cfi_and_id);
325  *vtr_tag2 =
326  clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
327  *push_dot1q =
328  (ETHERNET_TYPE_VLAN ==
329  clib_host_to_net_u16 (in_config->tags[0].type));
330  break;
331  default:
332  clib_warning ("invalid push_bytes count: %d",
333  in_config->push_bytes);
334  error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
335  goto done;
336  }
337  break;
338 
339  case 4:
340  switch (in_config->push_bytes)
341  {
342  case 0:
343  *vtr_op = L2_VTR_POP_1;
344  break;
345  case 4:
346  *vtr_op = L2_VTR_TRANSLATE_1_1;
347  *vtr_tag1 =
348  clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
349  *push_dot1q =
350  (ETHERNET_TYPE_VLAN ==
351  clib_host_to_net_u16 (in_config->tags[1].type));
352  break;
353  case 8:
354  *vtr_op = L2_VTR_TRANSLATE_1_2;
355  *vtr_tag1 =
356  clib_host_to_net_u16 (in_config->tags[0].priority_cfi_and_id);
357  *vtr_tag2 =
358  clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
359  *push_dot1q =
360  (ETHERNET_TYPE_VLAN ==
361  clib_host_to_net_u16 (in_config->tags[0].type));
362  break;
363  default:
364  clib_warning ("invalid push_bytes count: %d",
365  in_config->push_bytes);
366  error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
367  goto done;
368  }
369  break;
370 
371  case 8:
372  switch (in_config->push_bytes)
373  {
374  case 0:
375  *vtr_op = L2_VTR_POP_2;
376  break;
377  case 4:
378  *vtr_op = L2_VTR_TRANSLATE_2_1;
379  *vtr_tag1 =
380  clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
381  *push_dot1q =
382  (ETHERNET_TYPE_VLAN ==
383  clib_host_to_net_u16 (in_config->tags[1].type));
384  break;
385  case 8:
386  *vtr_op = L2_VTR_TRANSLATE_2_2;
387  *vtr_tag1 =
388  clib_host_to_net_u16 (in_config->tags[0].priority_cfi_and_id);
389  *vtr_tag2 =
390  clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
391  *push_dot1q =
392  (ETHERNET_TYPE_VLAN ==
393  clib_host_to_net_u16 (in_config->tags[0].type));
394  break;
395  default:
396  clib_warning ("invalid push_bytes count: %d",
397  in_config->push_bytes);
398  error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
399  goto done;
400  }
401  break;
402 
403  default:
404  clib_warning ("invalid pop_bytes count: %d", in_config->pop_bytes);
405  error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
406  goto done;
407  }
408 
409 done:
410  return error;
411 }
412 
413 /**
414  * Set subinterface vtr enable/disable.
415  * The CLI format is:
416  * set interface l2 tag-rewrite <interface> [disable | pop 1 | pop 2 | push {dot1q|dot1ad} <tag> [<tag>]]
417  *
418  * "push" can also be replaced by "translate-{1|2}-{1|2}"
419  */
420 static clib_error_t *
422  unformat_input_t * input, vlib_cli_command_t * cmd)
423 {
424  vnet_main_t *vnm = vnet_get_main ();
425  clib_error_t *error = 0;
426  u32 sw_if_index;
427  u32 vtr_op;
428  u32 push_dot1q = 0;
429  u32 tag1 = 0, tag2 = 0;
430 
431  if (!unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
432  {
433  error = clib_error_return (0, "unknown interface `%U'",
434  format_unformat_error, input);
435  goto done;
436  }
437 
438  vtr_op = L2_VTR_DISABLED;
439 
440  if (unformat (input, "disable"))
441  {
442  vtr_op = L2_VTR_DISABLED;
443  }
444  else if (unformat (input, "pop 1"))
445  {
446  vtr_op = L2_VTR_POP_1;
447  }
448  else if (unformat (input, "pop 2"))
449  {
450  vtr_op = L2_VTR_POP_2;
451 
452  }
453  else if (unformat (input, "push dot1q %d %d", &tag1, &tag2))
454  {
455  vtr_op = L2_VTR_PUSH_2;
456  push_dot1q = 1;
457  }
458  else if (unformat (input, "push dot1ad %d %d", &tag1, &tag2))
459  {
460  vtr_op = L2_VTR_PUSH_2;
461 
462  }
463  else if (unformat (input, "push dot1q %d", &tag1))
464  {
465  vtr_op = L2_VTR_PUSH_1;
466  push_dot1q = 1;
467  }
468  else if (unformat (input, "push dot1ad %d", &tag1))
469  {
470  vtr_op = L2_VTR_PUSH_1;
471 
472  }
473  else if (unformat (input, "translate 1-1 dot1q %d", &tag1))
474  {
475  vtr_op = L2_VTR_TRANSLATE_1_1;
476  push_dot1q = 1;
477  }
478  else if (unformat (input, "translate 1-1 dot1ad %d", &tag1))
479  {
480  vtr_op = L2_VTR_TRANSLATE_1_1;
481 
482  }
483  else if (unformat (input, "translate 2-1 dot1q %d", &tag1))
484  {
485  vtr_op = L2_VTR_TRANSLATE_2_1;
486  push_dot1q = 1;
487  }
488  else if (unformat (input, "translate 2-1 dot1ad %d", &tag1))
489  {
490  vtr_op = L2_VTR_TRANSLATE_2_1;
491 
492  }
493  else if (unformat (input, "translate 2-2 dot1q %d %d", &tag1, &tag2))
494  {
495  vtr_op = L2_VTR_TRANSLATE_2_2;
496  push_dot1q = 1;
497  }
498  else if (unformat (input, "translate 2-2 dot1ad %d %d", &tag1, &tag2))
499  {
500  vtr_op = L2_VTR_TRANSLATE_2_2;
501 
502  }
503  else if (unformat (input, "translate 1-2 dot1q %d %d", &tag1, &tag2))
504  {
505  vtr_op = L2_VTR_TRANSLATE_1_2;
506  push_dot1q = 1;
507  }
508  else if (unformat (input, "translate 1-2 dot1ad %d %d", &tag1, &tag2))
509  {
510  vtr_op = L2_VTR_TRANSLATE_1_2;
511 
512  }
513  else
514  {
515  error =
517  "expecting [disable | pop 1 | pop 2 | push {dot1q|dot1ah} <tag> [<tag>]\n"
518  " | translate {1|2}-{1|2} {dot1q|dot1ah} <tag> [<tag>]] but got `%U'",
519  format_unformat_error, input);
520  goto done;
521  }
522 
523  if (l2vtr_configure (vm, vnm, sw_if_index, vtr_op, push_dot1q, tag1, tag2))
524  {
525  error =
527  "vlan tag rewrite is not compatible with interface");
528  goto done;
529  }
530 
531 done:
532  return error;
533 }
534 
535 /* *INDENT-OFF* */
536 VLIB_CLI_COMMAND (int_l2_vtr_cli, static) = {
537  .path = "set interface l2 tag-rewrite",
538  .short_help = "set interface l2 tag-rewrite <interface> [disable | pop {1|2} | push {dot1q|dot1ad} <tag> <tag>]",
539  .function = int_l2_vtr,
540 };
541 /* *INDENT-ON* */
542 
543 
544 /*
545  * fd.io coding-style-patch-verification: ON
546  *
547  * Local Variables:
548  * eval: (c-set-style "gnu")
549  * End:
550  */
#define vec_validate(V, I)
Make sure vector is long enough for given index (no header, unspecified alignment) ...
Definition: vec.h:396
vmrglw vmrglh hi
uword unformat(unformat_input_t *i, char *fmt,...)
Definition: unformat.c:966
u8 push_bytes
Definition: l2_vtr.h:61
u32 l2vtr_configure(vlib_main_t *vlib_main, vnet_main_t *vnet_main, u32 sw_if_index, u32 vtr_op, u32 push_dot1q, u32 vtr_tag1, u32 vtr_tag2)
Configure vtag tag rewrite on the given interface.
Definition: l2_vtr.c:48
clib_error_t * l2_vtr_init(vlib_main_t *vm)
Just a placeholder; ensures file is not eliminated by linker.
Definition: l2_vtr.c:35
static vnet_hw_interface_t * vnet_get_sup_hw_interface(vnet_main_t *vnm, u32 sw_if_index)
u8 pop_bytes
Definition: l2_vtr.h:62
struct vnet_sub_interface_t::@120::@121::@123 flags
static vnet_sw_interface_t * vnet_get_sw_interface(vnet_main_t *vnm, u32 sw_if_index)
unformat_function_t unformat_vnet_sw_interface
u16 push_and_pop_bytes
Definition: l2_vtr.h:64
Per-interface vlan tag rewrite configuration There will be one instance of this struct for each sw_if...
Definition: l2_vtr.h:45
vnet_main_t * vnet_get_main(void)
Definition: misc.c:45
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:111
#define vec_elt_at_index(v, i)
Get vector value at index i checking that i is in bounds.
#define clib_warning(format, args...)
Definition: error.h:59
int vlib_main(vlib_main_t *vm, unformat_input_t *input)
Definition: main.c:1572
uword unformat_user(unformat_input_t *input, unformat_function_t *func,...)
Definition: unformat.c:977
vnet_sub_interface_t sub
Definition: interface.h:447
vnet_main_t vnet_main
Definition: misc.c:42
l2output_main_t l2output_main
Definition: l2_output.c:43
vnet_hw_interface_class_t ethernet_hw_interface_class
struct vnet_sub_interface_t::@120 eth
unsigned int u32
Definition: types.h:88
u8 * format_unformat_error(u8 *s, va_list *va)
Definition: unformat.c:91
u64 raw_tags
Definition: l2_vtr.h:54
static clib_error_t * int_l2_vtr(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
Set subinterface vtr enable/disable.
Definition: l2_vtr.c:421
u32 l2input_intf_bitmap_enable(u32 sw_if_index, u32 feature_bitmap, u32 enable)
Enable (or disable) the feature in the bitmap for the given interface.
Definition: l2_input.c:501
VLIB_CLI_COMMAND(set_interface_ip_source_and_port_range_check_command, static)
ethernet_vlan_header_tv_t tags[2]
Definition: l2_vtr.h:53
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
l2_output_config_t * configs
Definition: l2_output.h:79
u32 l2vtr_get(vlib_main_t *vlib_main, vnet_main_t *vnet_main, u32 sw_if_index, u32 *vtr_op, u32 *push_dot1q, u32 *vtr_tag1, u32 *vtr_tag2)
Get vtag tag rewrite on the given interface.
Definition: l2_vtr.c:261
vnet_sw_interface_type_t type
Definition: interface.h:410
#define clib_error_return(e, args...)
Definition: error.h:111
struct _unformat_input_t unformat_input_t