Line data Source code
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 : * @file
34 : * @brief Ethernet VLAN Tag Rewrite.
35 : *
36 : * VLAN tag rewrite provides the ability to change the VLAN tags on a packet.
37 : * Existing tags can be popped, new tags can be pushed, and existing tags can
38 : * be swapped with new tags. The rewrite feature is attached to a subinterface
39 : * as input and output operations. The input operation is explicitly configured.
40 : * The output operation is the symmetric opposite and is automatically derived
41 : * from the input operation.
42 : */
43 :
44 : /** Just a placeholder; ensures file is not eliminated by linker. */
45 : clib_error_t *
46 575 : l2_vtr_init (vlib_main_t * vm)
47 : {
48 575 : return 0;
49 : }
50 :
51 24767 : VLIB_INIT_FUNCTION (l2_vtr_init);
52 :
53 : u32
54 0 : l2pbb_configure (vlib_main_t * vlib_main,
55 : vnet_main_t * vnet_main, u32 sw_if_index, u32 vtr_op,
56 : u8 * b_dmac, u8 * b_smac,
57 : u16 b_vlanid, u32 i_sid, u16 vlan_outer_tag)
58 : {
59 0 : u32 error = 0;
60 0 : u32 enable = 0;
61 :
62 0 : l2_output_config_t *config = 0;
63 : vnet_hw_interface_t *hi;
64 0 : hi = vnet_get_sup_hw_interface_api_visible_or_null (vnet_main, sw_if_index);
65 :
66 0 : if (!hi)
67 : {
68 0 : error = VNET_API_ERROR_INVALID_INTERFACE;
69 0 : goto done;
70 : }
71 :
72 : // Config for this interface should be already initialized
73 : ptr_config_t *in_config;
74 : ptr_config_t *out_config;
75 0 : config = vec_elt_at_index (l2output_main.configs, sw_if_index);
76 0 : in_config = &(config->input_pbb_vtr);
77 0 : out_config = &(config->output_pbb_vtr);
78 :
79 0 : in_config->pop_bytes = 0;
80 0 : in_config->push_bytes = 0;
81 0 : out_config->pop_bytes = 0;
82 0 : out_config->push_bytes = 0;
83 0 : enable = (vtr_op != L2_VTR_DISABLED);
84 :
85 0 : if (!enable)
86 0 : goto done;
87 :
88 0 : if (vtr_op == L2_VTR_POP_2)
89 : {
90 0 : in_config->pop_bytes = sizeof (ethernet_pbb_header_packed_t);
91 : }
92 0 : else if (vtr_op == L2_VTR_PUSH_2)
93 : {
94 0 : clib_memcpy_fast (in_config->macs_tags.b_dst_address, b_dmac,
95 : sizeof (in_config->macs_tags.b_dst_address));
96 0 : clib_memcpy_fast (in_config->macs_tags.b_src_address, b_smac,
97 : sizeof (in_config->macs_tags.b_src_address));
98 0 : in_config->macs_tags.b_type =
99 0 : clib_net_to_host_u16 (ETHERNET_TYPE_DOT1AD);
100 0 : in_config->macs_tags.priority_dei_id =
101 0 : clib_net_to_host_u16 (b_vlanid & 0xFFF);
102 0 : in_config->macs_tags.i_type =
103 0 : clib_net_to_host_u16 (ETHERNET_TYPE_DOT1AH);
104 0 : in_config->macs_tags.priority_dei_uca_res_sid =
105 0 : clib_net_to_host_u32 (i_sid & 0xFFFFF);
106 0 : in_config->push_bytes = sizeof (ethernet_pbb_header_packed_t);
107 : }
108 : else if (vtr_op == L2_VTR_TRANSLATE_2_2)
109 : {
110 : /* TODO after PoC */
111 : }
112 :
113 : /*
114 : * Construct the output tag-rewrite config
115 : *
116 : * The push/pop values are always reversed
117 : */
118 0 : out_config->raw_data = in_config->raw_data;
119 0 : out_config->pop_bytes = in_config->push_bytes;
120 0 : out_config->push_bytes = in_config->pop_bytes;
121 :
122 0 : done:
123 0 : l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_VTR, enable);
124 0 : if (config)
125 0 : config->out_vtr_flag = (u8) enable;
126 :
127 : /* output vtr enable is checked explicitly in l2_output */
128 0 : return error;
129 : }
130 :
131 : /**
132 : * Configure vtag tag rewrite on the given interface.
133 : * Return 1 if there is an error, 0 if ok
134 : */
135 : u32
136 175 : 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 */
137 : u32 vtr_tag1, /* first pushed tag */
138 : u32 vtr_tag2) /* second pushed tag */
139 : {
140 : vnet_hw_interface_t *hi;
141 : vnet_sw_interface_t *si;
142 : u32 hw_no_tags;
143 175 : u32 error = 0;
144 : l2_output_config_t *config;
145 : vtr_config_t *in_config;
146 : vtr_config_t *out_config;
147 : u32 enable;
148 : u32 push_inner_et;
149 : u32 push_outer_et;
150 : u32 cfg_tags;
151 :
152 175 : hi = vnet_get_sup_hw_interface_api_visible_or_null (vnet_main, sw_if_index);
153 175 : if (!hi || (hi->hw_class_index != ethernet_hw_interface_class.index))
154 : {
155 0 : error = VNET_API_ERROR_INVALID_INTERFACE; /* non-ethernet interface */
156 0 : goto done;
157 : }
158 :
159 : /* Init the config for this interface */
160 175 : vec_validate (l2output_main.configs, sw_if_index);
161 175 : config = vec_elt_at_index (l2output_main.configs, sw_if_index);
162 175 : in_config = &(config->input_vtr);
163 175 : out_config = &(config->output_vtr);
164 175 : in_config->raw_tags = 0;
165 175 : out_config->raw_tags = 0;
166 :
167 : /* Get the configured tags for the interface */
168 175 : si = vnet_get_sw_interface (vnet_main, sw_if_index);
169 175 : hw_no_tags = (si->type == VNET_SW_INTERFACE_TYPE_HARDWARE);
170 :
171 : /* Construct the input tag-rewrite config */
172 :
173 175 : push_outer_et =
174 175 : clib_net_to_host_u16 (push_dot1q ? ETHERNET_TYPE_VLAN :
175 : ETHERNET_TYPE_DOT1AD);
176 175 : push_inner_et = clib_net_to_host_u16 (ETHERNET_TYPE_VLAN);
177 175 : vtr_tag1 = clib_net_to_host_u16 (vtr_tag1);
178 175 : vtr_tag2 = clib_net_to_host_u16 (vtr_tag2);
179 :
180 : /* Determine number of vlan tags with explicitly configured values */
181 175 : cfg_tags = 0;
182 175 : if (hw_no_tags || si->sub.eth.flags.no_tags)
183 : {
184 4 : cfg_tags = 0;
185 : }
186 171 : else if (si->sub.eth.flags.one_tag)
187 : {
188 100 : cfg_tags = 1;
189 100 : if (si->sub.eth.flags.outer_vlan_id_any)
190 : {
191 0 : cfg_tags = 0;
192 : }
193 : }
194 71 : else if (si->sub.eth.flags.two_tags)
195 : {
196 48 : cfg_tags = 2;
197 48 : if (si->sub.eth.flags.inner_vlan_id_any)
198 : {
199 0 : cfg_tags = 1;
200 : }
201 48 : if (si->sub.eth.flags.outer_vlan_id_any)
202 : {
203 0 : cfg_tags = 0;
204 : }
205 : }
206 :
207 175 : switch (vtr_op)
208 : {
209 115 : case L2_VTR_DISABLED:
210 115 : in_config->push_and_pop_bytes = 0;
211 115 : break;
212 :
213 25 : case L2_VTR_POP_1:
214 25 : if (cfg_tags < 1)
215 : {
216 : /* Need one or two tags */
217 0 : error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT;
218 0 : goto done;
219 : }
220 25 : in_config->pop_bytes = 4;
221 25 : in_config->push_bytes = 0;
222 25 : break;
223 :
224 16 : case L2_VTR_POP_2:
225 16 : if (cfg_tags < 2)
226 : {
227 0 : error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; /* Need two tags */
228 0 : goto done;
229 : }
230 16 : in_config->pop_bytes = 8;
231 16 : in_config->push_bytes = 0;
232 16 : break;
233 :
234 4 : case L2_VTR_PUSH_1:
235 4 : in_config->pop_bytes = 0;
236 4 : in_config->push_bytes = 4;
237 4 : in_config->tags[1].priority_cfi_and_id = vtr_tag1;
238 4 : in_config->tags[1].type = push_outer_et;
239 4 : break;
240 :
241 5 : case L2_VTR_PUSH_2:
242 5 : in_config->pop_bytes = 0;
243 5 : in_config->push_bytes = 8;
244 5 : in_config->tags[0].priority_cfi_and_id = vtr_tag1;
245 5 : in_config->tags[0].type = push_outer_et;
246 5 : in_config->tags[1].priority_cfi_and_id = vtr_tag2;
247 5 : in_config->tags[1].type = push_inner_et;
248 5 : break;
249 :
250 3 : case L2_VTR_TRANSLATE_1_1:
251 3 : if (cfg_tags < 1)
252 : {
253 0 : error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; /* Need one or two tags */
254 0 : goto done;
255 : }
256 3 : in_config->pop_bytes = 4;
257 3 : in_config->push_bytes = 4;
258 3 : in_config->tags[1].priority_cfi_and_id = vtr_tag1;
259 3 : in_config->tags[1].type = push_outer_et;
260 3 : break;
261 :
262 3 : case L2_VTR_TRANSLATE_1_2:
263 3 : if (cfg_tags < 1)
264 : {
265 0 : error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; /* Need one or two tags */
266 0 : goto done;
267 : }
268 3 : in_config->pop_bytes = 4;
269 3 : in_config->push_bytes = 8;
270 3 : in_config->tags[0].priority_cfi_and_id = vtr_tag1;
271 3 : in_config->tags[0].type = push_outer_et;
272 3 : in_config->tags[1].priority_cfi_and_id = vtr_tag2;
273 3 : in_config->tags[1].type = push_inner_et;
274 3 : break;
275 :
276 2 : case L2_VTR_TRANSLATE_2_1:
277 2 : if (cfg_tags < 2)
278 : {
279 0 : error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; /* Need two tags */
280 0 : goto done;
281 : }
282 2 : in_config->pop_bytes = 8;
283 2 : in_config->push_bytes = 4;
284 2 : in_config->tags[1].priority_cfi_and_id = vtr_tag1;
285 2 : in_config->tags[1].type = push_outer_et;
286 2 : break;
287 :
288 2 : case L2_VTR_TRANSLATE_2_2:
289 2 : if (cfg_tags < 2)
290 : {
291 0 : error = VNET_API_ERROR_INVALID_VLAN_TAG_COUNT; /* Need two tags */
292 0 : goto done;
293 : }
294 2 : in_config->pop_bytes = 8;
295 2 : in_config->push_bytes = 8;
296 2 : in_config->tags[0].priority_cfi_and_id = vtr_tag1;
297 2 : in_config->tags[0].type = push_outer_et;
298 2 : in_config->tags[1].priority_cfi_and_id = vtr_tag2;
299 2 : in_config->tags[1].type = push_inner_et;
300 2 : break;
301 : }
302 :
303 : /*
304 : * Construct the output tag-rewrite config
305 : *
306 : * The push/pop values are always reversed
307 : */
308 175 : out_config->push_bytes = in_config->pop_bytes;
309 175 : out_config->pop_bytes = in_config->push_bytes;
310 :
311 : /* Any pushed tags are derived from the subinterface config */
312 175 : push_outer_et =
313 175 : clib_net_to_host_u16 (si->sub.eth.flags.dot1ad ? ETHERNET_TYPE_DOT1AD :
314 : ETHERNET_TYPE_VLAN);
315 175 : push_inner_et = clib_net_to_host_u16 (ETHERNET_TYPE_VLAN);
316 175 : vtr_tag1 = clib_net_to_host_u16 (si->sub.eth.outer_vlan_id);
317 175 : vtr_tag2 = clib_net_to_host_u16 (si->sub.eth.inner_vlan_id);
318 :
319 175 : if (out_config->push_bytes == 4)
320 : {
321 31 : out_config->tags[1].priority_cfi_and_id = vtr_tag1;
322 31 : out_config->tags[1].type = push_outer_et;
323 : }
324 144 : else if (out_config->push_bytes == 8)
325 : {
326 20 : out_config->tags[0].priority_cfi_and_id = vtr_tag1;
327 20 : out_config->tags[0].type = push_outer_et;
328 20 : out_config->tags[1].priority_cfi_and_id = vtr_tag2;
329 20 : out_config->tags[1].type = push_inner_et;
330 : }
331 :
332 : /* set the interface enable flags */
333 175 : enable = (vtr_op != L2_VTR_DISABLED);
334 175 : config->out_vtr_flag = (u8) enable;
335 175 : l2input_intf_bitmap_enable (sw_if_index, L2INPUT_FEAT_VTR, enable);
336 : /* output vtr enable is checked explicitly in l2_output */
337 :
338 175 : done:
339 175 : return error;
340 : }
341 :
342 : /**
343 : * Get vtag tag rewrite on the given interface.
344 : * Return 1 if there is an error, 0 if ok
345 : */
346 : u32
347 8161 : 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 */
348 : u32 * vtr_tag1, /* first pushed tag */
349 : u32 * vtr_tag2) /* second pushed tag */
350 : {
351 : vnet_hw_interface_t *hi;
352 8161 : u32 error = 0;
353 : vtr_config_t *in_config;
354 :
355 8161 : if (!vtr_op || !push_dot1q || !vtr_tag1 || !vtr_tag2)
356 : {
357 0 : clib_warning ("invalid arguments");
358 0 : error = VNET_API_ERROR_INVALID_ARGUMENT;
359 0 : goto done;
360 : }
361 :
362 8161 : *vtr_op = L2_VTR_DISABLED;
363 8161 : *vtr_tag1 = 0;
364 8161 : *vtr_tag2 = 0;
365 8161 : *push_dot1q = 0;
366 :
367 8161 : hi = vnet_get_sup_hw_interface_api_visible_or_null (vnet_main, sw_if_index);
368 8161 : if (!hi || (hi->hw_class_index != ethernet_hw_interface_class.index))
369 : {
370 : /* non-ethernet interface */
371 811 : goto done;
372 : }
373 :
374 7350 : if (sw_if_index >= vec_len (l2output_main.configs))
375 : {
376 : /* no specific config (return disabled) */
377 137 : goto done;
378 : }
379 :
380 : /* Get the config for this interface */
381 7213 : in_config =
382 7213 : &(vec_elt_at_index (l2output_main.configs, sw_if_index)->input_vtr);
383 :
384 : /* DISABLED */
385 7213 : if (in_config->push_and_pop_bytes == 0)
386 : {
387 7178 : goto done;
388 : }
389 :
390 : /* find out vtr_op */
391 35 : switch (in_config->pop_bytes)
392 : {
393 11 : case 0:
394 11 : switch (in_config->push_bytes)
395 : {
396 0 : case 0:
397 : /* DISABLED */
398 0 : goto done;
399 5 : case 4:
400 5 : *vtr_op = L2_VTR_PUSH_1;
401 5 : *vtr_tag1 =
402 5 : clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
403 5 : *push_dot1q =
404 5 : (ETHERNET_TYPE_VLAN ==
405 5 : clib_host_to_net_u16 (in_config->tags[1].type));
406 5 : break;
407 6 : case 8:
408 6 : *vtr_op = L2_VTR_PUSH_2;
409 6 : *vtr_tag1 =
410 6 : clib_host_to_net_u16 (in_config->tags[0].priority_cfi_and_id);
411 6 : *vtr_tag2 =
412 6 : clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
413 6 : *push_dot1q =
414 6 : (ETHERNET_TYPE_VLAN ==
415 6 : clib_host_to_net_u16 (in_config->tags[0].type));
416 6 : break;
417 0 : default:
418 0 : clib_warning ("invalid push_bytes count: %d",
419 : in_config->push_bytes);
420 0 : error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
421 0 : goto done;
422 : }
423 11 : break;
424 :
425 11 : case 4:
426 11 : switch (in_config->push_bytes)
427 : {
428 2 : case 0:
429 2 : *vtr_op = L2_VTR_POP_1;
430 2 : break;
431 3 : case 4:
432 3 : *vtr_op = L2_VTR_TRANSLATE_1_1;
433 3 : *vtr_tag1 =
434 3 : clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
435 3 : *push_dot1q =
436 3 : (ETHERNET_TYPE_VLAN ==
437 3 : clib_host_to_net_u16 (in_config->tags[1].type));
438 3 : break;
439 6 : case 8:
440 6 : *vtr_op = L2_VTR_TRANSLATE_1_2;
441 6 : *vtr_tag1 =
442 6 : clib_host_to_net_u16 (in_config->tags[0].priority_cfi_and_id);
443 6 : *vtr_tag2 =
444 6 : clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
445 6 : *push_dot1q =
446 6 : (ETHERNET_TYPE_VLAN ==
447 6 : clib_host_to_net_u16 (in_config->tags[0].type));
448 6 : break;
449 0 : default:
450 0 : clib_warning ("invalid push_bytes count: %d",
451 : in_config->push_bytes);
452 0 : error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
453 0 : goto done;
454 : }
455 11 : break;
456 :
457 13 : case 8:
458 13 : switch (in_config->push_bytes)
459 : {
460 1 : case 0:
461 1 : *vtr_op = L2_VTR_POP_2;
462 1 : break;
463 2 : case 4:
464 2 : *vtr_op = L2_VTR_TRANSLATE_2_1;
465 2 : *vtr_tag1 =
466 2 : clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
467 2 : *push_dot1q =
468 2 : (ETHERNET_TYPE_VLAN ==
469 2 : clib_host_to_net_u16 (in_config->tags[1].type));
470 2 : break;
471 10 : case 8:
472 10 : *vtr_op = L2_VTR_TRANSLATE_2_2;
473 10 : *vtr_tag1 =
474 10 : clib_host_to_net_u16 (in_config->tags[0].priority_cfi_and_id);
475 10 : *vtr_tag2 =
476 10 : clib_host_to_net_u16 (in_config->tags[1].priority_cfi_and_id);
477 10 : *push_dot1q =
478 10 : (ETHERNET_TYPE_VLAN ==
479 10 : clib_host_to_net_u16 (in_config->tags[0].type));
480 10 : break;
481 0 : default:
482 0 : clib_warning ("invalid push_bytes count: %d",
483 : in_config->push_bytes);
484 0 : error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
485 0 : goto done;
486 : }
487 13 : break;
488 :
489 0 : default:
490 0 : clib_warning ("invalid pop_bytes count: %d", in_config->pop_bytes);
491 0 : error = VNET_API_ERROR_UNEXPECTED_INTF_STATE;
492 0 : goto done;
493 : }
494 :
495 8161 : done:
496 8161 : return error;
497 : }
498 :
499 : /**
500 : * Set subinterface vtr enable/disable.
501 : * The CLI format is:
502 : * set interface l2 tag-rewrite <interface> [disable | pop 1 | pop 2 | push {dot1q|dot1ad} <tag> [<tag>]]
503 : *
504 : * "push" can also be replaced by "translate-{1|2}-{1|2}"
505 : */
506 : static clib_error_t *
507 0 : int_l2_vtr (vlib_main_t * vm,
508 : unformat_input_t * input, vlib_cli_command_t * cmd)
509 : {
510 0 : vnet_main_t *vnm = vnet_get_main ();
511 0 : clib_error_t *error = 0;
512 : u32 sw_if_index;
513 : u32 vtr_op;
514 0 : u32 push_dot1q = 0;
515 0 : u32 tag1 = 0, tag2 = 0;
516 :
517 0 : if (!unformat_user (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
518 : {
519 0 : error = clib_error_return (0, "unknown interface `%U'",
520 : format_unformat_error, input);
521 0 : goto done;
522 : }
523 :
524 0 : vtr_op = L2_VTR_DISABLED;
525 :
526 0 : if (unformat (input, "disable"))
527 : {
528 0 : vtr_op = L2_VTR_DISABLED;
529 : }
530 0 : else if (unformat (input, "pop 1"))
531 : {
532 0 : vtr_op = L2_VTR_POP_1;
533 : }
534 0 : else if (unformat (input, "pop 2"))
535 : {
536 0 : vtr_op = L2_VTR_POP_2;
537 :
538 : }
539 0 : else if (unformat (input, "push dot1q %d %d", &tag1, &tag2))
540 : {
541 0 : vtr_op = L2_VTR_PUSH_2;
542 0 : push_dot1q = 1;
543 : }
544 0 : else if (unformat (input, "push dot1ad %d %d", &tag1, &tag2))
545 : {
546 0 : vtr_op = L2_VTR_PUSH_2;
547 :
548 : }
549 0 : else if (unformat (input, "push dot1q %d", &tag1))
550 : {
551 0 : vtr_op = L2_VTR_PUSH_1;
552 0 : push_dot1q = 1;
553 : }
554 0 : else if (unformat (input, "push dot1ad %d", &tag1))
555 : {
556 0 : vtr_op = L2_VTR_PUSH_1;
557 :
558 : }
559 0 : else if (unformat (input, "translate 1-1 dot1q %d", &tag1))
560 : {
561 0 : vtr_op = L2_VTR_TRANSLATE_1_1;
562 0 : push_dot1q = 1;
563 : }
564 0 : else if (unformat (input, "translate 1-1 dot1ad %d", &tag1))
565 : {
566 0 : vtr_op = L2_VTR_TRANSLATE_1_1;
567 :
568 : }
569 0 : else if (unformat (input, "translate 2-1 dot1q %d", &tag1))
570 : {
571 0 : vtr_op = L2_VTR_TRANSLATE_2_1;
572 0 : push_dot1q = 1;
573 : }
574 0 : else if (unformat (input, "translate 2-1 dot1ad %d", &tag1))
575 : {
576 0 : vtr_op = L2_VTR_TRANSLATE_2_1;
577 :
578 : }
579 0 : else if (unformat (input, "translate 2-2 dot1q %d %d", &tag1, &tag2))
580 : {
581 0 : vtr_op = L2_VTR_TRANSLATE_2_2;
582 0 : push_dot1q = 1;
583 : }
584 0 : else if (unformat (input, "translate 2-2 dot1ad %d %d", &tag1, &tag2))
585 : {
586 0 : vtr_op = L2_VTR_TRANSLATE_2_2;
587 :
588 : }
589 0 : else if (unformat (input, "translate 1-2 dot1q %d %d", &tag1, &tag2))
590 : {
591 0 : vtr_op = L2_VTR_TRANSLATE_1_2;
592 0 : push_dot1q = 1;
593 : }
594 0 : else if (unformat (input, "translate 1-2 dot1ad %d %d", &tag1, &tag2))
595 : {
596 0 : vtr_op = L2_VTR_TRANSLATE_1_2;
597 :
598 : }
599 : else
600 : {
601 : error =
602 0 : clib_error_return (0,
603 : "expecting [disable | pop 1 | pop 2 | push {dot1q|dot1ah} <tag> [<tag>]\n"
604 : " | translate {1|2}-{1|2} {dot1q|dot1ah} <tag> [<tag>]] but got `%U'",
605 : format_unformat_error, input);
606 0 : goto done;
607 : }
608 :
609 0 : if (l2vtr_configure (vm, vnm, sw_if_index, vtr_op, push_dot1q, tag1, tag2))
610 : {
611 : error =
612 0 : clib_error_return (0,
613 : "vlan tag rewrite is not compatible with interface");
614 0 : goto done;
615 : }
616 :
617 0 : done:
618 0 : return error;
619 : }
620 :
621 : /*?
622 : * VLAN tag rewrite provides the ability to change the VLAN tags on a packet.
623 : * Existing tags can be popped, new tags can be pushed, and existing tags can
624 : * be swapped with new tags. The rewrite feature is attached to a subinterface
625 : * as input and output operations. The input operation is explicitly configured.
626 : * The output operation is the symmetric opposite and is automatically derived
627 : * from the input operation.
628 : *
629 : * <b>POP:</b> For pop operations, the subinterface encapsulation (the vlan
630 : * tags specified when it was created) must have at least the number of popped
631 : * tags. e.g. the \"pop 2\" operation would be rejected on a single-vlan interface.
632 : * The output tag-rewrite operation for pops is to push the specified number of
633 : * vlan tags onto the packet. The pushed tag values are the ones in the
634 : * subinterface encapsulation.
635 : *
636 : * <b>PUSH:</b> For push operations, the ethertype is also specified. The
637 : * output tag-rewrite operation for pushes is to pop the same number of tags
638 : * off the packet. If the packet doesn't have enough tags it is dropped.
639 : *
640 : *
641 : * @cliexpar
642 : * @parblock
643 : * By default a subinterface has no tag-rewrite. To return a subinterface to
644 : * this state use:
645 : * @cliexcmd{set interface l2 tag-rewrite GigabitEthernet0/8/0.200 disable}
646 : *
647 : * To pop vlan tags off packets received from a subinterface, use:
648 : * @cliexcmd{set interface l2 tag-rewrite GigabitEthernet0/8/0.200 pop 1}
649 : * @cliexcmd{set interface l2 tag-rewrite GigabitEthernet0/8/0.200 pop 2}
650 : *
651 : * To push one or two vlan tags onto packets received from an interface, use:
652 : * @cliexcmd{set interface l2 tag-rewrite GigabitEthernet0/8/0.200 push dot1q 100}
653 : * @cliexcmd{set interface l2 tag-rewrite GigabitEthernet0/8/0.200 push dot1ad 100 150}
654 : *
655 : * Tags can also be translated, which is basically a combination of a pop and push.
656 : * @cliexcmd{set interface l2 tag-rewrite GigabitEthernet0/8/0.200 translate 1-1 dot1ad 100}
657 : * @cliexcmd{set interface l2 tag-rewrite GigabitEthernet0/8/0.200 translate 2-2 dot1ad 100 150}
658 : * @cliexcmd{set interface l2 tag-rewrite GigabitEthernet0/8/0.200 translate 1-2 dot1q 100}
659 : * @cliexcmd{set interface l2 tag-rewrite GigabitEthernet0/8/0.200 translate 2-1 dot1q 100 150}
660 : *
661 : * To display the VLAN Tag settings, show the associate bridge-domain:
662 : * @cliexstart{show bridge-domain 200 detail}
663 : * ID Index Learning U-Forwrd UU-Flood Flooding ARP-Term BVI-Intf
664 : * 200 1 on on on on off N/A
665 : *
666 : * Interface Index SHG BVI VLAN-Tag-Rewrite
667 : * GigabitEthernet0/8/0.200 5 0 - trans-1-1 dot1ad 100
668 : * GigabitEthernet0/9/0.200 4 0 - none
669 : * GigabitEthernet0/a/0.200 6 0 - none
670 : * @cliexend
671 : * @endparblock
672 : ?*/
673 : /* *INDENT-OFF* */
674 285289 : VLIB_CLI_COMMAND (int_l2_vtr_cli, static) = {
675 : .path = "set interface l2 tag-rewrite",
676 : .short_help = "set interface l2 tag-rewrite <interface> [disable | pop {1|2} | push {dot1q|dot1ad} <tag> <tag>]",
677 : .function = int_l2_vtr,
678 : };
679 : /* *INDENT-ON* */
680 :
681 : /**
682 : * Get pbb tag rewrite on the given interface.
683 : * Return 1 if there is an error, 0 if ok
684 : */
685 : u32
686 7007 : l2pbb_get (vlib_main_t * vlib_main, vnet_main_t * vnet_main, u32 sw_if_index,
687 : u32 * vtr_op, u16 * outer_tag, ethernet_header_t * eth_hdr,
688 : u16 * b_vlanid, u32 * i_sid)
689 : {
690 7007 : u32 error = 1;
691 : ptr_config_t *in_config;
692 :
693 7007 : if (!vtr_op || !outer_tag || !b_vlanid || !i_sid)
694 : {
695 0 : clib_warning ("invalid arguments");
696 0 : error = VNET_API_ERROR_INVALID_ARGUMENT;
697 0 : goto done;
698 : }
699 :
700 7007 : *vtr_op = L2_VTR_DISABLED;
701 7007 : *outer_tag = 0;
702 7007 : *b_vlanid = 0;
703 7007 : *i_sid = 0;
704 :
705 7007 : if (sw_if_index >= vec_len (l2output_main.configs))
706 : {
707 : /* no specific config (return disabled) */
708 241 : goto done;
709 : }
710 :
711 : /* Get the config for this interface */
712 6766 : in_config =
713 6766 : &(vec_elt_at_index (l2output_main.configs, sw_if_index)->input_pbb_vtr);
714 :
715 6766 : if (in_config->push_and_pop_bytes == 0)
716 : {
717 : /* DISABLED */
718 6766 : goto done;
719 : }
720 : else
721 : {
722 0 : if (in_config->pop_bytes && in_config->push_bytes)
723 0 : *vtr_op = L2_VTR_TRANSLATE_2_1;
724 0 : else if (in_config->pop_bytes)
725 0 : *vtr_op = L2_VTR_POP_2;
726 0 : else if (in_config->push_bytes)
727 0 : *vtr_op = L2_VTR_PUSH_2;
728 :
729 0 : clib_memcpy_fast (ð_hdr->dst_address,
730 0 : in_config->macs_tags.b_dst_address,
731 : sizeof (eth_hdr->dst_address));
732 0 : clib_memcpy_fast (ð_hdr->src_address,
733 0 : in_config->macs_tags.b_src_address,
734 : sizeof (eth_hdr->src_address));
735 :
736 0 : *b_vlanid =
737 0 : clib_host_to_net_u16 (in_config->macs_tags.priority_dei_id) & 0xFFF;
738 0 : *i_sid =
739 0 : clib_host_to_net_u32 (in_config->macs_tags.
740 0 : priority_dei_uca_res_sid) & 0xFFFFF;
741 0 : error = 0;
742 : }
743 7007 : done:
744 7007 : return error;
745 : }
746 :
747 : /**
748 : * Set subinterface pbb vtr enable/disable.
749 : * The CLI format is:
750 : * set interface l2 pbb-tag-rewrite <interface> [disable | pop | push | translate_pbb_stag <outer_tag> dmac <address> smac <address> s_id <nn> [b_vlanid <nn>]]
751 : */
752 : static clib_error_t *
753 0 : int_l2_pbb_vtr (vlib_main_t * vm,
754 : unformat_input_t * input, vlib_cli_command_t * cmd)
755 : {
756 0 : vnet_main_t *vnm = vnet_get_main ();
757 0 : clib_error_t *error = 0;
758 : u32 sw_if_index, tmp;
759 0 : u32 vtr_op = L2_VTR_DISABLED;
760 0 : u32 outer_tag = 0;
761 : u8 dmac[6];
762 : u8 smac[6];
763 0 : u8 dmac_set = 0, smac_set = 0;
764 0 : u16 b_vlanid = 0;
765 0 : u32 s_id = ~0;
766 :
767 0 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
768 : {
769 0 : if (unformat_user
770 : (input, unformat_vnet_sw_interface, vnm, &sw_if_index))
771 : ;
772 0 : else if (unformat (input, "disable"))
773 0 : vtr_op = L2_VTR_DISABLED;
774 0 : else if (vtr_op == L2_VTR_DISABLED && unformat (input, "pop"))
775 0 : vtr_op = L2_VTR_POP_2;
776 0 : else if (vtr_op == L2_VTR_DISABLED && unformat (input, "push"))
777 0 : vtr_op = L2_VTR_PUSH_2;
778 0 : else if (vtr_op == L2_VTR_DISABLED
779 0 : && unformat (input, "translate_pbb_stag %d", &outer_tag))
780 0 : vtr_op = L2_VTR_TRANSLATE_2_1;
781 0 : else if (unformat (input, "dmac %U", unformat_ethernet_address, dmac))
782 0 : dmac_set = 1;
783 0 : else if (unformat (input, "smac %U", unformat_ethernet_address, smac))
784 0 : smac_set = 1;
785 0 : else if (unformat (input, "b_vlanid %d", &tmp))
786 0 : b_vlanid = tmp;
787 0 : else if (unformat (input, "s_id %d", &s_id))
788 : ;
789 : else
790 : {
791 0 : error = clib_error_return (0,
792 : "expecting [disable | pop | push | translate_pbb_stag <outer_tag>\n"
793 : "dmac <address> smac <address> s_id <nn> [b_vlanid <nn>]]");
794 0 : goto done;
795 : }
796 : }
797 :
798 0 : if ((vtr_op == L2_VTR_PUSH_2 || vtr_op == L2_VTR_TRANSLATE_2_1)
799 0 : && (!dmac_set || !smac_set || s_id == ~0))
800 : {
801 0 : error = clib_error_return (0,
802 : "expecting dmac <address> smac <address> s_id <nn> [b_vlanid <nn>]");
803 0 : goto done;
804 : }
805 :
806 0 : if (l2pbb_configure
807 : (vm, vnm, sw_if_index, vtr_op, dmac, smac, b_vlanid, s_id, outer_tag))
808 : {
809 : error =
810 0 : clib_error_return (0,
811 : "pbb tag rewrite is not compatible with interface");
812 0 : goto done;
813 : }
814 :
815 0 : done:
816 0 : return error;
817 : }
818 :
819 : /* *INDENT-OFF* */
820 285289 : VLIB_CLI_COMMAND (int_l2_pbb_vtr_cli, static) = {
821 : .path = "set interface l2 pbb-tag-rewrite",
822 : .short_help = "set interface l2 pbb-tag-rewrite <interface> [disable | pop | push | translate_pbb_stag <outer_tag> dmac <address> smac <address> s_id <nn> [b_vlanid <nn>]]",
823 : .function = int_l2_pbb_vtr,
824 : };
825 : /* *INDENT-ON* */
826 :
827 : /*
828 : * fd.io coding-style-patch-verification: ON
829 : *
830 : * Local Variables:
831 : * eval: (c-set-style "gnu")
832 : * End:
833 : */
|