m_vlan: add pop_eth and push_eth actions

Add support for the new TCA_VLAN_ACT_POP_ETH and TCA_VLAN_ACT_PUSH_ETH
actions (kernel commit 19fbcb36a39e ("net/sched: act_vlan:
Add {POP,PUSH}_ETH actions"). These action let TC remove or add the
Ethernet at the head of a frame.

Drop an Ethernet header:
 # tc filter add dev ethX matchall action vlan pop_eth

Push an Ethernet header (the original frame must have no MAC header):
 # tc filter add dev ethX matchall action vlan \
       push_eth dst_mac 0a:00:00:00:00:02 src_mac 0a:00:00:00:00:01

Also add a test suite for m_vlan, which covers these new actions and
the pre-existing ones.

Signed-off-by: Guillaume Nault <gnault@redhat.com>
Signed-off-by: David Ahern <dsahern@gmail.com>
This commit is contained in:
Guillaume Nault 2020-10-19 17:23:01 +02:00 committed by David Ahern
parent 3342688a66
commit d61167dd88
3 changed files with 192 additions and 2 deletions

View File

@ -5,8 +5,8 @@ vlan - vlan manipulation module
.SH SYNOPSIS .SH SYNOPSIS
.in +8 .in +8
.ti -8 .ti -8
.BR tc " ... " "action vlan" " { " pop " |" .BR tc " ... " "action vlan" " { " pop " | " pop_eth " |"
.IR PUSH " | " MODIFY " } [ " CONTROL " ]" .IR PUSH " | " MODIFY " | " PUSH_ETH " } [ " CONTROL " ]"
.ti -8 .ti -8
.IR PUSH " := " .IR PUSH " := "
@ -24,6 +24,11 @@ vlan - vlan manipulation module
.IR VLANPRIO " ] " .IR VLANPRIO " ] "
.BI id " VLANID" .BI id " VLANID"
.ti -8
.IR PUSH_ETH " := "
.B push_eth
.BI dst_mac " LLADDR " src_mac " LLADDR "
.ti -8 .ti -8
.IR CONTROL " := { " .IR CONTROL " := { "
.BR reclassify " | " pipe " | " drop " | " continue " | " pass " | " goto " " chain " " CHAIN_INDEX " }" .BR reclassify " | " pipe " | " drop " | " continue " | " pass " | " goto " " chain " " CHAIN_INDEX " }"
@ -43,6 +48,20 @@ modes require at least a
and allow to optionally choose the and allow to optionally choose the
.I VLANPROTO .I VLANPROTO
to use. to use.
The
.B vlan
action can also be used to add or remove the base Ethernet header. The
.B pop_eth
mode, which takes no argument, is used to remove the base Ethernet header. All
existing VLANs must have been previously dropped. The opposite operation,
adding a base Ethernet header, is done with the
.B push_eth
mode. In that case, the packet must have no MAC header (stacking MAC headers is
not permitted). This mode is mostly useful when a previous action has
encapsulated the whole original frame behind a network header and one needs
to prepend an Ethernet header before forwarding the resulting packet.
.SH OPTIONS .SH OPTIONS
.TP .TP
.B pop .B pop
@ -58,6 +77,16 @@ Replace mode. Existing 802.1Q tag is replaced. Requires at least
.B id .B id
option. option.
.TP .TP
.B pop_eth
Ethernet header decapsulation mode. Only works on a plain Ethernet header:
VLANs, if any, must be removed first.
.TP
.B push_eth
Ethernet header encapsulation mode. The Ethertype is automatically set
using the network header type. Chaining Ethernet headers is not allowed: the
packet must have no MAC header when using this mode. Requires the
.BR "dst_mac " and " src_mac " options.
.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
@ -73,6 +102,12 @@ Choose the VLAN protocol to use. At the time of writing, the kernel accepts only
.BI priority " VLANPRIO" .BI priority " VLANPRIO"
Choose the VLAN priority to use. Decimal number in range of 0-7. Choose the VLAN priority to use. Decimal number in range of 0-7.
.TP .TP
.BI dst_mac " LLADDR"
Choose the destination MAC address to use.
.TP
.BI src_mac " LLADDR"
Choose the source MAC address to use.
.TP
.I CONTROL .I CONTROL
How to continue after executing this action. How to continue after executing this action.
.RS .RS

View File

@ -23,6 +23,8 @@ static const char * const action_names[] = {
[TCA_VLAN_ACT_POP] = "pop", [TCA_VLAN_ACT_POP] = "pop",
[TCA_VLAN_ACT_PUSH] = "push", [TCA_VLAN_ACT_PUSH] = "push",
[TCA_VLAN_ACT_MODIFY] = "modify", [TCA_VLAN_ACT_MODIFY] = "modify",
[TCA_VLAN_ACT_POP_ETH] = "pop_eth",
[TCA_VLAN_ACT_PUSH_ETH] = "push_eth",
}; };
static void explain(void) static void explain(void)
@ -31,6 +33,8 @@ static void explain(void)
"Usage: vlan pop\n" "Usage: vlan pop\n"
" vlan push [ protocol VLANPROTO ] id VLANID [ priority VLANPRIO ] [CONTROL]\n" " vlan push [ protocol VLANPROTO ] id VLANID [ priority VLANPRIO ] [CONTROL]\n"
" vlan modify [ protocol VLANPROTO ] id VLANID [ priority VLANPRIO ] [CONTROL]\n" " vlan modify [ protocol VLANPROTO ] id VLANID [ priority VLANPRIO ] [CONTROL]\n"
" vlan pop_eth [CONTROL]\n"
" vlan push_eth dst_mac LLADDR src_mac LLADDR [CONTROL]\n"
" VLANPROTO is one of 802.1Q or 802.1AD\n" " VLANPROTO is one of 802.1Q or 802.1AD\n"
" with default: 802.1Q\n" " with default: 802.1Q\n"
" CONTROL := reclassify | pipe | drop | continue | pass |\n" " CONTROL := reclassify | pipe | drop | continue | pass |\n"
@ -63,6 +67,10 @@ static int parse_vlan(struct action_util *a, int *argc_p, char ***argv_p,
char **argv = *argv_p; char **argv = *argv_p;
struct rtattr *tail; struct rtattr *tail;
int action = 0; int action = 0;
char dst_mac[ETH_ALEN] = {};
int dst_mac_set = 0;
char src_mac[ETH_ALEN] = {};
int src_mac_set = 0;
__u16 id; __u16 id;
int id_set = 0; int id_set = 0;
__u16 proto; __u16 proto;
@ -95,6 +103,18 @@ static int parse_vlan(struct action_util *a, int *argc_p, char ***argv_p,
return -1; return -1;
} }
action = TCA_VLAN_ACT_MODIFY; action = TCA_VLAN_ACT_MODIFY;
} else if (matches(*argv, "pop_eth") == 0) {
if (action) {
unexpected(*argv);
return -1;
}
action = TCA_VLAN_ACT_POP_ETH;
} else if (matches(*argv, "push_eth") == 0) {
if (action) {
unexpected(*argv);
return -1;
}
action = TCA_VLAN_ACT_PUSH_ETH;
} else if (matches(*argv, "id") == 0) { } else if (matches(*argv, "id") == 0) {
if (!has_push_attribs(action)) if (!has_push_attribs(action))
invarg("only valid for push/modify", *argv); invarg("only valid for push/modify", *argv);
@ -119,6 +139,22 @@ static int parse_vlan(struct action_util *a, int *argc_p, char ***argv_p,
if (get_u8(&prio, *argv, 0) || (prio & ~0x7)) if (get_u8(&prio, *argv, 0) || (prio & ~0x7))
invarg("prio is invalid", *argv); invarg("prio is invalid", *argv);
prio_set = 1; prio_set = 1;
} else if (matches(*argv, "dst_mac") == 0) {
if (action != TCA_VLAN_ACT_PUSH_ETH)
invarg("only valid for push_eth", *argv);
NEXT_ARG();
if (ll_addr_a2n(dst_mac, sizeof(dst_mac), *argv) < 0)
invarg("dst_mac is invalid", *argv);
dst_mac_set = 1;
} else if (matches(*argv, "src_mac") == 0) {
if (action != TCA_VLAN_ACT_PUSH_ETH)
invarg("only valid for push_eth", *argv);
NEXT_ARG();
if (ll_addr_a2n(src_mac, sizeof(src_mac), *argv) < 0)
invarg("src_mac is invalid", *argv);
src_mac_set = 1;
} else if (matches(*argv, "help") == 0) { } else if (matches(*argv, "help") == 0) {
usage(); usage();
} else { } else {
@ -150,6 +186,20 @@ static int parse_vlan(struct action_util *a, int *argc_p, char ***argv_p,
return -1; return -1;
} }
if (action == TCA_VLAN_ACT_PUSH_ETH) {
if (!dst_mac_set) {
fprintf(stderr, "dst_mac needs to be set for %s\n",
action_names[action]);
explain();
return -1;
} else if (!src_mac_set) {
fprintf(stderr, "src_mac needs to be set for %s\n",
action_names[action]);
explain();
return -1;
}
}
parm.v_action = action; parm.v_action = action;
tail = addattr_nest(n, MAX_MSG, tca_id); tail = addattr_nest(n, MAX_MSG, tca_id);
addattr_l(n, MAX_MSG, TCA_VLAN_PARMS, &parm, sizeof(parm)); addattr_l(n, MAX_MSG, TCA_VLAN_PARMS, &parm, sizeof(parm));
@ -167,6 +217,12 @@ static int parse_vlan(struct action_util *a, int *argc_p, char ***argv_p,
} }
if (prio_set) if (prio_set)
addattr8(n, MAX_MSG, TCA_VLAN_PUSH_VLAN_PRIORITY, prio); addattr8(n, MAX_MSG, TCA_VLAN_PUSH_VLAN_PRIORITY, prio);
if (dst_mac_set)
addattr_l(n, MAX_MSG, TCA_VLAN_PUSH_ETH_DST, dst_mac,
sizeof(dst_mac));
if (src_mac_set)
addattr_l(n, MAX_MSG, TCA_VLAN_PUSH_ETH_SRC, src_mac,
sizeof(src_mac));
addattr_nest_end(n, tail); addattr_nest_end(n, tail);
@ -216,6 +272,19 @@ static int print_vlan(struct action_util *au, FILE *f, struct rtattr *arg)
print_uint(PRINT_ANY, "priority", " priority %u", val); print_uint(PRINT_ANY, "priority", " priority %u", val);
} }
break; break;
case TCA_VLAN_ACT_PUSH_ETH:
if (tb[TCA_VLAN_PUSH_ETH_DST] &&
RTA_PAYLOAD(tb[TCA_VLAN_PUSH_ETH_DST]) == ETH_ALEN) {
ll_addr_n2a(RTA_DATA(tb[TCA_VLAN_PUSH_ETH_DST]),
ETH_ALEN, 0, b1, sizeof(b1));
print_string(PRINT_ANY, "dst_mac", " dst_mac %s", b1);
}
if (tb[TCA_VLAN_PUSH_ETH_SRC &&
RTA_PAYLOAD(tb[TCA_VLAN_PUSH_ETH_SRC]) == ETH_ALEN]) {
ll_addr_n2a(RTA_DATA(tb[TCA_VLAN_PUSH_ETH_SRC]),
ETH_ALEN, 0, b1, sizeof(b1));
print_string(PRINT_ANY, "src_mac", " src_mac %s", b1);
}
} }
print_action_control(f, " ", parm->action, ""); print_action_control(f, " ", parm->action, "");

86
testsuite/tests/tc/vlan.t Executable file
View File

@ -0,0 +1,86 @@
#!/bin/sh
. lib/generic.sh
DEV="$(rand_dev)"
ts_ip "$0" "Add $DEV dummy interface" link add dev $DEV up type dummy
ts_tc "$0" "Add ingress qdisc" qdisc add dev $DEV ingress
reset_qdisc()
{
ts_tc "$0" "Remove ingress qdisc" qdisc del dev $DEV ingress
ts_tc "$0" "Add ingress qdisc" qdisc add dev $DEV ingress
}
ts_tc "$0" "Add vlan action pop" \
filter add dev $DEV ingress matchall action vlan pop
ts_tc "$0" "Show ingress filters" filter show dev $DEV ingress
test_on "vlan"
test_on "pop"
test_on "pipe"
reset_qdisc
ts_tc "$0" "Add vlan action push (default parameters)" \
filter add dev $DEV ingress matchall action vlan push id 5
ts_tc "$0" "Show ingress filters" filter show dev $DEV ingress
test_on "vlan"
test_on "push"
test_on "id 5"
test_on "protocol 802.1Q"
test_on "priority 0"
test_on "pipe"
reset_qdisc
ts_tc "$0" "Add vlan action push (explicit parameters)" \
filter add dev $DEV ingress matchall \
action vlan push id 5 protocol 802.1ad priority 2
ts_tc "$0" "Show ingress filters" filter show dev $DEV ingress
test_on "vlan"
test_on "push"
test_on "id 5"
test_on "protocol 802.1ad"
test_on "priority 2"
test_on "pipe"
reset_qdisc
ts_tc "$0" "Add vlan action modify (default parameters)" \
filter add dev $DEV ingress matchall action vlan modify id 5
ts_tc "$0" "Show ingress filters" filter show dev $DEV ingress
test_on "vlan"
test_on "modify"
test_on "id 5"
test_on "protocol 802.1Q"
test_on "priority 0"
test_on "pipe"
reset_qdisc
ts_tc "$0" "Add vlan action modify (explicit parameters)" \
filter add dev $DEV ingress matchall \
action vlan modify id 5 protocol 802.1ad priority 2
ts_tc "$0" "Show ingress filters" filter show dev $DEV ingress
test_on "vlan"
test_on "modify"
test_on "id 5"
test_on "protocol 802.1ad"
test_on "priority 2"
test_on "pipe"
reset_qdisc
ts_tc "$0" "Add vlan action pop_eth" \
filter add dev $DEV ingress matchall action vlan pop_eth
ts_tc "$0" "Show ingress filters" filter show dev $DEV ingress
test_on "vlan"
test_on "pop_eth"
test_on "pipe"
reset_qdisc
ts_tc "$0" "Add vlan action push_eth" \
filter add dev $DEV ingress matchall \
action vlan push_eth dst_mac 02:00:00:00:00:02 \
src_mac 02:00:00:00:00:01
ts_tc "$0" "Show ingress filters" filter show dev $DEV ingress
test_on "vlan"
test_on "push_eth"
test_on "dst_mac 02:00:00:00:00:02"
test_on "src_mac 02:00:00:00:00:01"
test_on "pipe"