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.ladk...@gmail.com>
---
 v2: Coding. No need to encapsule action_names[] access into a function

 include/linux/tc_act/tc_vlan.h |  1 +
 man/man8/tc-vlan.8             | 25 +++++++++++++++++++------
 tc/m_vlan.c                    | 40 +++++++++++++++++++++++++++++++---------
 3 files changed, 51 insertions(+), 15 deletions(-)

diff --git a/include/linux/tc_act/tc_vlan.h b/include/linux/tc_act/tc_vlan.h
index be72b6e384..bddb272b84 100644
--- a/include/linux/tc_act/tc_vlan.h
+++ b/include/linux/tc_act/tc_vlan.h
@@ -16,6 +16,7 @@
 
 #define TCA_VLAN_ACT_POP       1
 #define TCA_VLAN_ACT_PUSH      2
+#define TCA_VLAN_ACT_MODIFY    3
 
 struct tc_vlan {
        tc_gen;
diff --git a/man/man8/tc-vlan.8 b/man/man8/tc-vlan.8
index 4d0c5c8a15..af3de1c54e 100644
--- a/man/man8/tc-vlan.8
+++ b/man/man8/tc-vlan.8
@@ -6,7 +6,7 @@ vlan - vlan manipulation module
 .in +8
 .ti -8
 .BR tc " ... " "action vlan" " { " pop " |"
-.IR PUSH " } [ " CONTROL " ]"
+.IR PUSH " | " MODIFY " } [ " CONTROL " ]"
 
 .ti -8
 .IR PUSH " := "
@@ -17,22 +17,30 @@ vlan - vlan manipulation module
 .BI id " VLANID"
 
 .ti -8
+.IR MODIFY " := "
+.BR modify " [ " protocol
+.IR VLANPROTO " ]"
+.BR " [ " priority
+.IR VLANPRIO " ] "
+.BI id " VLANID"
+
+.ti -8
 .IR CONTROL " := { "
 .BR reclassify " | " pipe " | " drop " | " continue " | " pass " }"
 .SH DESCRIPTION
 The
 .B vlan
 action allows to perform 802.1Q en- or decapsulation on a packet, reflected by
-the two operation modes
-.IR POP " and " PUSH .
+the operation modes
+.IR POP ", " PUSH " and " MODIFY .
 The
 .I POP
 mode is simple, as no further information is required to just drop the
 outer-most VLAN encapsulation. The
-.I PUSH
-mode on the other hand requires at least a
+.IR PUSH " and " MODIFY
+modes require at least a
 .I VLANID
-and allows to optionally choose the
+and allow to optionally choose the
 .I VLANPROTO
 to use.
 .SH OPTIONS
@@ -45,6 +53,11 @@ Encapsulation mode. Requires at least
 .B id
 option.
 .TP
+.B modify
+Replace mode. Existing 802.1Q tag is replaced. Requires at least
+.B id
+option.
+.TP
 .BI id " VLANID"
 Specify the VLAN ID to encapsulate into.
 .I VLANID
diff --git a/tc/m_vlan.c b/tc/m_vlan.c
index 05a63b48f1..b32f746015 100644
--- a/tc/m_vlan.c
+++ b/tc/m_vlan.c
@@ -19,10 +19,17 @@
 #include "tc_util.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)
 {
        fprintf(stderr, "Usage: vlan pop\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, "            with default: 802.1Q\n");
        fprintf(stderr, "       CONTROL := reclassify | pipe | drop | continue 
| pass\n");
@@ -34,6 +41,11 @@ static void usage(void)
        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,
                      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;
                        }
                        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) {
-                       if (action != TCA_VLAN_ACT_PUSH) {
-                               fprintf(stderr, "\"%s\" is only valid for 
push\n",
+                       if (!has_push_attribs(action)) {
+                               fprintf(stderr, "\"%s\" is only valid for 
push/modify\n",
                                        *argv);
                                explain();
                                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);
                        id_set = 1;
                } else if (matches(*argv, "protocol") == 0) {
-                       if (action != TCA_VLAN_ACT_PUSH) {
-                               fprintf(stderr, "\"%s\" is only valid for 
push\n",
+                       if (!has_push_attribs(action)) {
+                               fprintf(stderr, "\"%s\" is only valid for 
push/modify\n",
                                        *argv);
                                explain();
                                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);
                        proto_set = 1;
                } else if (matches(*argv, "priority") == 0) {
-                       if (action != TCA_VLAN_ACT_PUSH) {
-                               fprintf(stderr, "\"%s\" is only valid for 
push\n",
+                       if (!has_push_attribs(action)) {
+                               fprintf(stderr, "\"%s\" is only valid for 
push/modify\n",
                                        *argv);
                                explain();
                                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) {
-               fprintf(stderr, "id needs to be set for push\n");
+       if (has_push_attribs(action) && !id_set) {
+               fprintf(stderr, "id needs to be set for %s\n",
+                       action_names[action]);
                explain();
                return -1;
        }
@@ -186,7 +207,8 @@ static int print_vlan(struct action_util *au, FILE *f, 
struct rtattr *arg)
                fprintf(f, " pop");
                break;
        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]) {
                        val = rta_getattr_u16(tb[TCA_VLAN_PUSH_VLAN_ID]);
                        fprintf(f, " id %u", val);
-- 
2.7.4

Reply via email to