FD.io VPP  v16.06
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. Also insures file is not eliminated by linker.
35 {
36  return 0;
37 }
38 
40 
41 
42 // Configure vtag tag rewrite on the given interface.
43 // Return 1 if there is an error, 0 if ok
46  u32 sw_if_index,
47  u32 vtr_op,
48  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  error = VNET_API_ERROR_INVALID_INTERFACE; // non-ethernet interface
66  goto done;
67  }
68 
69  // Init the config for this interface
70  vec_validate (l2output_main.configs, sw_if_index);
71  in_config = &(vec_elt_at_index(l2output_main.configs, sw_if_index)->input_vtr);
72  out_config = &(vec_elt_at_index(l2output_main.configs, sw_if_index)->output_vtr);
73  in_config->raw_tags = 0;
74  out_config->raw_tags = 0;
75 
76  // Get the configured tags for the interface
77  si = vnet_get_sw_interface (vnet_main, sw_if_index);
78  hw_no_tags = (si->type == VNET_SW_INTERFACE_TYPE_HARDWARE);
79 
80  // Construct the input tag-rewrite config
81 
82  push_outer_et = clib_net_to_host_u16 (push_dot1q ? ETHERNET_TYPE_VLAN : ETHERNET_TYPE_DOT1AD);
83  push_inner_et = clib_net_to_host_u16 (ETHERNET_TYPE_VLAN);
84  vtr_tag1 = clib_net_to_host_u16 (vtr_tag1);
85  vtr_tag2 = clib_net_to_host_u16 (vtr_tag2);
86 
87  // Determine number of vlan tags with explictly configured values
88  cfg_tags = 0;
89  if (hw_no_tags || si->sub.eth.flags.no_tags) {
90  cfg_tags = 0;
91  } else if (si->sub.eth.flags.one_tag) {
92  cfg_tags = 1;
93  if (si->sub.eth.flags.outer_vlan_id_any) {
94  cfg_tags = 0;
95  }
96  } else if (si->sub.eth.flags.two_tags) {
97  cfg_tags = 2;
98  if (si->sub.eth.flags.inner_vlan_id_any) {
99  cfg_tags = 1;
100  }
101  if (si->sub.eth.flags.outer_vlan_id_any) {
102  cfg_tags = 0;
103  }
104  }
105 
106  switch (vtr_op) {
107  case L2_VTR_DISABLED:
108  in_config->push_and_pop_bytes = 0;
109  break;
110 
111  case L2_VTR_POP_1:
112  if (cfg_tags < 1) {
113  error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; // Need one or two tags
114  goto done;
115  }
116  in_config->pop_bytes = 4;
117  in_config->push_bytes = 0;
118  break;
119 
120  case L2_VTR_POP_2:
121  if (cfg_tags < 2) {
122  error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; // Need two tags
123  goto done;
124  }
125  in_config->pop_bytes = 8;
126  in_config->push_bytes = 0;
127 
128  out_config->push_bytes = in_config->pop_bytes;
129  out_config->pop_bytes = in_config->push_bytes;
130  break;
131 
132  case L2_VTR_PUSH_1:
133  in_config->pop_bytes = 0;
134  in_config->push_bytes = 4;
135  in_config->tags[1].priority_cfi_and_id = vtr_tag1;
136  in_config->tags[1].type = push_outer_et;
137  break;
138 
139  case L2_VTR_PUSH_2:
140  in_config->pop_bytes = 0;
141  in_config->push_bytes = 8;
142  in_config->tags[0].priority_cfi_and_id = vtr_tag1;
143  in_config->tags[0].type = push_outer_et;
144  in_config->tags[1].priority_cfi_and_id = vtr_tag2;
145  in_config->tags[1].type = push_inner_et;
146  break;
147 
149  if (cfg_tags < 1) {
150  error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; // Need one or two tags
151  goto done;
152  }
153  in_config->pop_bytes = 4;
154  in_config->push_bytes = 4;
155  in_config->tags[1].priority_cfi_and_id = vtr_tag1;
156  in_config->tags[1].type = push_outer_et;
157  break;
158 
160  if (cfg_tags < 1) {
161  error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; // Need one or two tags
162  goto done;
163  }
164  in_config->pop_bytes = 4;
165  in_config->push_bytes = 8;
166  in_config->tags[0].priority_cfi_and_id = vtr_tag1;
167  in_config->tags[0].type = push_outer_et;
168  in_config->tags[1].priority_cfi_and_id = vtr_tag2;
169  in_config->tags[1].type = push_inner_et;
170  break;
171 
173  if (cfg_tags < 2) {
174  error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; // Need two tags
175  goto done;
176  }
177  in_config->pop_bytes = 8;
178  in_config->push_bytes = 4;
179  in_config->tags[1].priority_cfi_and_id = vtr_tag1;
180  in_config->tags[1].type = push_outer_et;
181  break;
182 
184  if (cfg_tags < 2) {
185  error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; // Need two tags
186  goto done;
187  }
188  in_config->pop_bytes = 8;
189  in_config->push_bytes = 8;
190  in_config->tags[0].priority_cfi_and_id = vtr_tag1;
191  in_config->tags[0].type = push_outer_et;
192  in_config->tags[1].priority_cfi_and_id = vtr_tag2;
193  in_config->tags[1].type = push_inner_et;
194  break;
195  }
196 
197  // Construct the output tag-rewrite config
198 
199  // The push/pop values are always reversed
200  out_config->push_bytes = in_config->pop_bytes;
201  out_config->pop_bytes = in_config->push_bytes;
202 
203  // Any pushed tags are derived from the subinterface config
204  push_outer_et = clib_net_to_host_u16 (si->sub.eth.flags.dot1ad ? ETHERNET_TYPE_DOT1AD : ETHERNET_TYPE_VLAN);
205  push_inner_et = clib_net_to_host_u16 (ETHERNET_TYPE_VLAN);
206  vtr_tag1 = clib_net_to_host_u16 (si->sub.eth.outer_vlan_id);
207  vtr_tag2 = clib_net_to_host_u16 (si->sub.eth.inner_vlan_id);
208 
209  if (out_config->push_bytes == 4) {
210  out_config->tags[1].priority_cfi_and_id = vtr_tag1;
211  out_config->tags[1].type = push_outer_et;
212  } else if (out_config->push_bytes == 8) {
213  out_config->tags[0].priority_cfi_and_id = vtr_tag1;
214  out_config->tags[0].type = push_outer_et;
215  out_config->tags[1].priority_cfi_and_id = vtr_tag2;
216  out_config->tags[1].type = push_inner_et;
217  }
218 
219  // set the interface enable flags
220  enable = (vtr_op != L2_VTR_DISABLED);
221  l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_VTR, enable);
222  // output vtr enable is checked explicitly in l2_output
223 
224  done:
225  return error;
226 }
227 
228 // Get vtag tag rewrite on the given interface.
229 // Return 1 if there is an error, 0 if ok
232  u32 sw_if_index,
233  u32 *vtr_op,
234  u32 *push_dot1q, // ethertype of first pushed tag is dot1q/dot1ad
235  u32 *vtr_tag1, // first pushed tag
236  u32 *vtr_tag2) // second pushed tag
237 {
239  u32 error = 0;
240  vtr_config_t * in_config;
241 
242  if (!vtr_op || !push_dot1q || !vtr_tag1 || !vtr_tag2) {
243  clib_warning ("invalid arguments");
244  error = VNET_API_ERROR_INVALID_ARGUMENT;
245  goto done;
246  }
247 
248  *vtr_op = L2_VTR_DISABLED;
249  *vtr_tag1 = 0;
250  *vtr_tag2 = 0;
251  *push_dot1q = 0;
252 
253  hi = vnet_get_sup_hw_interface (vnet_main, sw_if_index);
254  if (!hi || (hi->hw_class_index != ethernet_hw_interface_class.index)) {
255  // non-ethernet interface
256  goto done;
257  }
258 
259  if (sw_if_index >= vec_len(l2output_main.configs)) {
260  // no specific config (return disabled)
261  goto done;
262  }
263 
264  // Get the config for this interface
265  in_config = &(vec_elt_at_index(l2output_main.configs, sw_if_index)->input_vtr);
266 
267  // DISABLED
268  if (in_config->push_and_pop_bytes == 0) {
269  goto done;
270  }
271 
272  // find out vtr_op
273  switch (in_config->pop_bytes) {
274  case 0:
275  switch (in_config->push_bytes) {
276  case 0:
277  // DISABLED
278  goto done;
279  case 4:
280  *vtr_op = L2_VTR_PUSH_1;
281  *vtr_tag1 = clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
282  *push_dot1q = (ETHERNET_TYPE_VLAN == clib_host_to_net_u16 (in_config->tags[1].type));
283  break;
284  case 8:
285  *vtr_op = L2_VTR_PUSH_2;
286  *vtr_tag1 = clib_host_to_net_u16 (in_config->tags[0].priority_cfi_and_id);
287  *vtr_tag2 = clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
288  *push_dot1q = (ETHERNET_TYPE_VLAN == clib_host_to_net_u16 (in_config->tags[0].type));
289  break;
290  default:
291  clib_warning ("invalid push_bytes count: %d", in_config->push_bytes);
292  error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
293  goto done;
294  }
295  break;
296 
297  case 4:
298  switch (in_config->push_bytes) {
299  case 0:
300  *vtr_op = L2_VTR_POP_1;
301  break;
302  case 4:
303  *vtr_op = L2_VTR_TRANSLATE_1_1;
304  *vtr_tag1 = clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
305  *push_dot1q = (ETHERNET_TYPE_VLAN == clib_host_to_net_u16 (in_config->tags[1].type));
306  break;
307  case 8:
308  *vtr_op = L2_VTR_TRANSLATE_1_2;
309  *vtr_tag1 = clib_host_to_net_u16 (in_config->tags[0].priority_cfi_and_id);
310  *vtr_tag2 = clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
311  *push_dot1q = (ETHERNET_TYPE_VLAN == clib_host_to_net_u16 (in_config->tags[0].type));
312  break;
313  default:
314  clib_warning ("invalid push_bytes count: %d", in_config->push_bytes);
315  error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
316  goto done;
317  }
318  break;
319 
320  case 8:
321  switch (in_config->push_bytes) {
322  case 0:
323  *vtr_op = L2_VTR_POP_2;
324  break;
325  case 4:
326  *vtr_op = L2_VTR_TRANSLATE_2_1;
327  *vtr_tag1 = clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
328  *push_dot1q = (ETHERNET_TYPE_VLAN == clib_host_to_net_u16 (in_config->tags[1].type));
329  break;
330  case 8:
331  *vtr_op = L2_VTR_TRANSLATE_2_2;
332  *vtr_tag1 = clib_host_to_net_u16 (in_config->tags[0].priority_cfi_and_id);
333  *vtr_tag2 = clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
334  *push_dot1q = (ETHERNET_TYPE_VLAN == clib_host_to_net_u16 (in_config->tags[0].type));
335  break;
336  default:
337  clib_warning ("invalid push_bytes count: %d", in_config->push_bytes);
338  error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
339  goto done;
340  }
341  break;
342 
343  default:
344  clib_warning ("invalid pop_bytes count: %d", in_config->pop_bytes);
345  error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
346  goto done;
347  }
348 
349  done:
350  return error;
351 }
352 
353 // set subinterface vtr enable/disable
354 // The CLI format is:
355 // set interface l2 tag-rewrite <interface> [disable | pop 1 | pop 2 | push {dot1q|dot1ad} <tag> [<tag>]]
356 // "push" can also be replaced by "translate-{1|2}-{1|2}"
357 static clib_error_t *
359  unformat_input_t * input,
360  vlib_cli_command_t * cmd)
361 {
362  vnet_main_t * vnm = vnet_get_main();
363  clib_error_t * error = 0;
364  u32 sw_if_index;
365  u32 vtr_op;
366  u32 push_dot1q = 0;
367  u32 tag1 = 0, tag2 = 0;
368 
369  if (! unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
370  {
371  error = clib_error_return (0, "unknown interface `%U'",
372  format_unformat_error, input);
373  goto done;
374  }
375 
376  vtr_op = L2_VTR_DISABLED;
377 
378  if (unformat (input, "disable")) {
379  vtr_op = L2_VTR_DISABLED;
380  } else if (unformat (input, "pop 1")) {
381  vtr_op = L2_VTR_POP_1;
382  } else if (unformat (input, "pop 2")) {
383  vtr_op = L2_VTR_POP_2;
384 
385  } else if (unformat (input, "push dot1q %d %d", &tag1, &tag2)) {
386  vtr_op = L2_VTR_PUSH_2;
387  push_dot1q = 1;
388  } else if (unformat (input, "push dot1ad %d %d", &tag1, &tag2)) {
389  vtr_op = L2_VTR_PUSH_2;
390 
391  } else if (unformat (input, "push dot1q %d", &tag1)) {
392  vtr_op = L2_VTR_PUSH_1;
393  push_dot1q = 1;
394  } else if (unformat (input, "push dot1ad %d", &tag1)) {
395  vtr_op = L2_VTR_PUSH_1;
396 
397  } else if (unformat (input, "translate 1-1 dot1q %d", &tag1)) {
398  vtr_op = L2_VTR_TRANSLATE_1_1;
399  push_dot1q = 1;
400  } else if (unformat (input, "translate 1-1 dot1ad %d", &tag1)) {
401  vtr_op = L2_VTR_TRANSLATE_1_1;
402 
403  } else if (unformat (input, "translate 2-1 dot1q %d", &tag1)) {
404  vtr_op = L2_VTR_TRANSLATE_2_1;
405  push_dot1q = 1;
406  } else if (unformat (input, "translate 2-1 dot1ad %d", &tag1)) {
407  vtr_op = L2_VTR_TRANSLATE_2_1;
408 
409  } else if (unformat (input, "translate 2-2 dot1q %d %d", &tag1, &tag2)) {
410  vtr_op = L2_VTR_TRANSLATE_2_2;
411  push_dot1q = 1;
412  } else if (unformat (input, "translate 2-2 dot1ad %d %d", &tag1, &tag2)) {
413  vtr_op = L2_VTR_TRANSLATE_2_2;
414 
415  } else if (unformat (input, "translate 1-2 dot1q %d %d", &tag1, &tag2)) {
416  vtr_op = L2_VTR_TRANSLATE_1_2;
417  push_dot1q = 1;
418  } else if (unformat (input, "translate 1-2 dot1ad %d %d", &tag1, &tag2)) {
419  vtr_op = L2_VTR_TRANSLATE_1_2;
420 
421  } else {
422  error = clib_error_return (0, "expecting [disable | pop 1 | pop 2 | push {dot1q|dot1ah} <tag> [<tag>]\n"
423  " | translate {1|2}-{1|2} {dot1q|dot1ah} <tag> [<tag>]] but got `%U'",
424  format_unformat_error, input);
425  goto done;
426  }
427 
428  if (l2vtr_configure (vm,
429  vnm,
430  sw_if_index,
431  vtr_op,
432  push_dot1q,
433  tag1,
434  tag2)) {
435  error = clib_error_return (0, "vlan tag rewrite is not compatible with interface");
436  goto done;
437  }
438 
439  done:
440  return error;
441 }
442 
443 VLIB_CLI_COMMAND (int_l2_vtr_cli, static) = {
444  .path = "set interface l2 tag-rewrite",
445  .short_help = "set interface l2 tag-rewrite <interface> [disable | pop {1|2} | push {dot1q|dot1ad} <tag> <tag>]",
446  .function = int_l2_vtr,
447 };
448 
struct vnet_sub_interface_t::@91 eth
#define vec_validate(V, I)
Make sure vector is long enough for given index (no header, unspecified alignment) ...
Definition: vec.h:394
vmrglw vmrglh hi
uword unformat(unformat_input_t *i, char *fmt,...)
Definition: unformat.c:942
u8 push_bytes
Definition: l2_vtr.h:52
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)
Definition: l2_vtr.c:44
clib_error_t * l2_vtr_init(vlib_main_t *vm)
Definition: l2_vtr.c:34
u8 pop_bytes
Definition: l2_vtr.h:53
unformat_function_t unformat_vnet_sw_interface
u16 push_and_pop_bytes
Definition: l2_vtr.h:55
vnet_main_t * vnet_get_main(void)
Definition: misc.c:45
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:109
#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:1538
uword unformat_user(unformat_input_t *input, unformat_function_t *func,...)
Definition: unformat.c:953
vnet_sub_interface_t sub
Definition: interface.h:404
vnet_main_t vnet_main
Definition: misc.c:42
l2output_main_t l2output_main
Definition: l2_output.c:41
vnet_hw_interface_class_t ethernet_hw_interface_class
always_inline vnet_hw_interface_t * vnet_get_sup_hw_interface(vnet_main_t *vnm, u32 sw_if_index)
#define VLIB_CLI_COMMAND(x,...)
Definition: cli.h:150
unsigned int u32
Definition: types.h:88
u8 * format_unformat_error(u8 *s, va_list *va)
Definition: unformat.c:87
u64 raw_tags
Definition: l2_vtr.h:47
static clib_error_t * int_l2_vtr(vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd)
Definition: l2_vtr.c:358
u32 l2input_intf_bitmap_enable(u32 sw_if_index, u32 feature_bitmap, u32 enable)
Definition: l2_input.c:473
ethernet_vlan_header_tv_t tags[2]
Definition: l2_vtr.h:46
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
always_inline vnet_sw_interface_t * vnet_get_sw_interface(vnet_main_t *vnm, u32 sw_if_index)
struct vnet_sub_interface_t::@91::@92::@94 flags
l2_output_config_t * configs
Definition: l2_output.h:68
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)
Definition: l2_vtr.c:230
vnet_sw_interface_type_t type
Definition: interface.h:368
#define clib_error_return(e, args...)
Definition: error.h:112
struct _unformat_input_t unformat_input_t