tc: m_vlan: Add vlan modify action

The 'vlan modify' action allows to replace an existing 802.1q tag
according to user provided settings.
It accepts same arguments as the 'vlan push' action.

For example, this replaces vid 6 with vid 5:

 # tc filter add dev veth0 parent ffff: pref 1 protocol 802.1q \
      basic match 'meta(vlan mask 0xfff eq 6)' \
      action vlan modify id 5 continue

Signed-off-by: Shmulik Ladkani <shmulik.ladkani@gmail.com>
This commit is contained in:
Shmulik Ladkani 2016-09-22 21:01:05 +03:00 committed by Stephen Hemminger
parent 590bf22a34
commit 4654173e90
2 changed files with 50 additions and 15 deletions

View File

@ -6,7 +6,7 @@ vlan - vlan manipulation module
.in +8 .in +8
.ti -8 .ti -8
.BR tc " ... " "action vlan" " { " pop " |" .BR tc " ... " "action vlan" " { " pop " |"
.IR PUSH " } [ " CONTROL " ]" .IR PUSH " | " MODIFY " } [ " CONTROL " ]"
.ti -8 .ti -8
.IR PUSH " := " .IR PUSH " := "
@ -16,6 +16,14 @@ vlan - vlan manipulation module
.IR VLANPRIO " ] " .IR VLANPRIO " ] "
.BI id " VLANID" .BI id " VLANID"
.ti -8
.IR MODIFY " := "
.BR modify " [ " protocol
.IR VLANPROTO " ]"
.BR " [ " priority
.IR VLANPRIO " ] "
.BI id " VLANID"
.ti -8 .ti -8
.IR CONTROL " := { " .IR CONTROL " := { "
.BR reclassify " | " pipe " | " drop " | " continue " | " pass " }" .BR reclassify " | " pipe " | " drop " | " continue " | " pass " }"
@ -23,16 +31,16 @@ vlan - vlan manipulation module
The The
.B vlan .B vlan
action allows to perform 802.1Q en- or decapsulation on a packet, reflected by action allows to perform 802.1Q en- or decapsulation on a packet, reflected by
the two operation modes the operation modes
.IR POP " and " PUSH . .IR POP ", " PUSH " and " MODIFY .
The The
.I POP .I POP
mode is simple, as no further information is required to just drop the mode is simple, as no further information is required to just drop the
outer-most VLAN encapsulation. The outer-most VLAN encapsulation. The
.I PUSH .IR PUSH " and " MODIFY
mode on the other hand requires at least a modes require at least a
.I VLANID .I VLANID
and allows to optionally choose the and allow to optionally choose the
.I VLANPROTO .I VLANPROTO
to use. to use.
.SH OPTIONS .SH OPTIONS
@ -45,6 +53,11 @@ Encapsulation mode. Requires at least
.B id .B id
option. option.
.TP .TP
.B modify
Replace mode. Existing 802.1Q tag is replaced. Requires at least
.B id
option.
.TP
.BI id " VLANID" .BI id " VLANID"
Specify the VLAN ID to encapsulate into. Specify the VLAN ID to encapsulate into.
.I VLANID .I VLANID

View File

@ -19,10 +19,17 @@
#include "tc_util.h" #include "tc_util.h"
#include <linux/tc_act/tc_vlan.h> #include <linux/tc_act/tc_vlan.h>
static const char * const action_names[] = {
[TCA_VLAN_ACT_POP] = "pop",
[TCA_VLAN_ACT_PUSH] = "push",
[TCA_VLAN_ACT_MODIFY] = "modify",
};
static void explain(void) static void explain(void)
{ {
fprintf(stderr, "Usage: vlan pop\n"); fprintf(stderr, "Usage: vlan pop\n");
fprintf(stderr, " vlan push [ protocol VLANPROTO ] id VLANID [ priority VLANPRIO ] [CONTROL]\n"); fprintf(stderr, " vlan push [ protocol VLANPROTO ] id VLANID [ priority VLANPRIO ] [CONTROL]\n");
fprintf(stderr, " vlan modify [ protocol VLANPROTO ] id VLANID [ priority VLANPRIO ] [CONTROL]\n");
fprintf(stderr, " VLANPROTO is one of 802.1Q or 802.1AD\n"); fprintf(stderr, " VLANPROTO is one of 802.1Q or 802.1AD\n");
fprintf(stderr, " with default: 802.1Q\n"); fprintf(stderr, " with default: 802.1Q\n");
fprintf(stderr, " CONTROL := reclassify | pipe | drop | continue | pass\n"); fprintf(stderr, " CONTROL := reclassify | pipe | drop | continue | pass\n");
@ -34,6 +41,11 @@ static void usage(void)
exit(-1); exit(-1);
} }
static bool has_push_attribs(int action)
{
return action == TCA_VLAN_ACT_PUSH || action == TCA_VLAN_ACT_MODIFY;
}
static int parse_vlan(struct action_util *a, int *argc_p, char ***argv_p, static int parse_vlan(struct action_util *a, int *argc_p, char ***argv_p,
int tca_id, struct nlmsghdr *n) int tca_id, struct nlmsghdr *n)
{ {
@ -71,9 +83,17 @@ static int parse_vlan(struct action_util *a, int *argc_p, char ***argv_p,
return -1; return -1;
} }
action = TCA_VLAN_ACT_PUSH; action = TCA_VLAN_ACT_PUSH;
} else if (matches(*argv, "modify") == 0) {
if (action) {
fprintf(stderr, "unexpected \"%s\" - action already specified\n",
*argv);
explain();
return -1;
}
action = TCA_VLAN_ACT_MODIFY;
} else if (matches(*argv, "id") == 0) { } else if (matches(*argv, "id") == 0) {
if (action != TCA_VLAN_ACT_PUSH) { if (!has_push_attribs(action)) {
fprintf(stderr, "\"%s\" is only valid for push\n", fprintf(stderr, "\"%s\" is only valid for push/modify\n",
*argv); *argv);
explain(); explain();
return -1; return -1;
@ -83,8 +103,8 @@ static int parse_vlan(struct action_util *a, int *argc_p, char ***argv_p,
invarg("id is invalid", *argv); invarg("id is invalid", *argv);
id_set = 1; id_set = 1;
} else if (matches(*argv, "protocol") == 0) { } else if (matches(*argv, "protocol") == 0) {
if (action != TCA_VLAN_ACT_PUSH) { if (!has_push_attribs(action)) {
fprintf(stderr, "\"%s\" is only valid for push\n", fprintf(stderr, "\"%s\" is only valid for push/modify\n",
*argv); *argv);
explain(); explain();
return -1; return -1;
@ -94,8 +114,8 @@ static int parse_vlan(struct action_util *a, int *argc_p, char ***argv_p,
invarg("protocol is invalid", *argv); invarg("protocol is invalid", *argv);
proto_set = 1; proto_set = 1;
} else if (matches(*argv, "priority") == 0) { } else if (matches(*argv, "priority") == 0) {
if (action != TCA_VLAN_ACT_PUSH) { if (!has_push_attribs(action)) {
fprintf(stderr, "\"%s\" is only valid for push\n", fprintf(stderr, "\"%s\" is only valid for push/modify\n",
*argv); *argv);
explain(); explain();
return -1; return -1;
@ -129,8 +149,9 @@ static int parse_vlan(struct action_util *a, int *argc_p, char ***argv_p,
} }
} }
if (action == TCA_VLAN_ACT_PUSH && !id_set) { if (has_push_attribs(action) && !id_set) {
fprintf(stderr, "id needs to be set for push\n"); fprintf(stderr, "id needs to be set for %s\n",
action_names[action]);
explain(); explain();
return -1; return -1;
} }
@ -186,7 +207,8 @@ static int print_vlan(struct action_util *au, FILE *f, struct rtattr *arg)
fprintf(f, " pop"); fprintf(f, " pop");
break; break;
case TCA_VLAN_ACT_PUSH: case TCA_VLAN_ACT_PUSH:
fprintf(f, " push"); case TCA_VLAN_ACT_MODIFY:
fprintf(f, " %s", action_names[parm->v_action]);
if (tb[TCA_VLAN_PUSH_VLAN_ID]) { if (tb[TCA_VLAN_PUSH_VLAN_ID]) {
val = rta_getattr_u16(tb[TCA_VLAN_PUSH_VLAN_ID]); val = rta_getattr_u16(tb[TCA_VLAN_PUSH_VLAN_ID]);
fprintf(f, " id %u", val); fprintf(f, " id %u", val);