Ability to change geneve device attributes was added to kernel through
commit 5b861f6baa3a ("geneve: add rtnl changelink support"), however one
cannot do the same through ip-link(8) command.  Changing the allowed
geneve device attributes using 'ip link set <geneve_name> type geneve id
<geneve_id> <allowed_attributes>' currently fails with 'operation not
supported' error.  This patch adds support for it.

Signed-off-by: Girish Moodalbail <girish.moodalb...@oracle.com>
---
 ip/iplink_geneve.c | 82 +++++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 60 insertions(+), 22 deletions(-)

diff --git a/ip/iplink_geneve.c b/ip/iplink_geneve.c
index 2c510fc..594a3e5 100644
--- a/ip/iplink_geneve.c
+++ b/ip/iplink_geneve.c
@@ -15,6 +15,8 @@
 #include "utils.h"
 #include "ip_common.h"
 
+#define GENEVE_ATTRSET(attrs, type) (((attrs) & (1L << (type))) != 0)
+
 static void print_explain(FILE *f)
 {
        fprintf(f,
@@ -42,11 +44,20 @@ static void explain(void)
        print_explain(stderr);
 }
 
+static void check_duparg(__u64 *attrs, int type, const char *key,
+                        const char *argv)
+{
+       if (!GENEVE_ATTRSET(*attrs, type)) {
+               *attrs |= (1L << type);
+               return;
+       }
+       duparg2(key, argv);
+}
+
 static int geneve_parse_opt(struct link_util *lu, int argc, char **argv,
                          struct nlmsghdr *n)
 {
        __u32 vni = 0;
-       int vni_set = 0;
        __u32 daddr = 0;
        struct in6_addr daddr6 = IN6ADDR_ANY_INIT;
        __u32 label = 0;
@@ -55,22 +66,24 @@ static int geneve_parse_opt(struct link_util *lu, int argc, 
char **argv,
        __u16 dstport = 0;
        bool metadata = 0;
        __u8 udpcsum = 0;
-       bool udpcsum_set = false;
        __u8 udp6zerocsumtx = 0;
-       bool udp6zerocsumtx_set = false;
        __u8 udp6zerocsumrx = 0;
-       bool udp6zerocsumrx_set = false;
+       __u64 attrs = 0;
+       bool set_op = (n->nlmsg_type == RTM_NEWLINK &&
+                      !(n->nlmsg_flags & NLM_F_CREATE));
 
        while (argc > 0) {
                if (!matches(*argv, "id") ||
                    !matches(*argv, "vni")) {
                        NEXT_ARG();
+                       check_duparg(&attrs, IFLA_GENEVE_ID, "id", *argv);
                        if (get_u32(&vni, *argv, 0) ||
                            vni >= 1u << 24)
                                invarg("invalid id", *argv);
-                       vni_set = 1;
                } else if (!matches(*argv, "remote")) {
                        NEXT_ARG();
+                       check_duparg(&attrs, IFLA_GENEVE_REMOTE, "remote",
+                                    *argv);
                        if (!inet_get_addr(*argv, &daddr, &daddr6)) {
                                fprintf(stderr, "Invalid address \"%s\"\n", 
*argv);
                                return -1;
@@ -82,6 +95,7 @@ static int geneve_parse_opt(struct link_util *lu, int argc, 
char **argv,
                        unsigned int uval;
 
                        NEXT_ARG();
+                       check_duparg(&attrs, IFLA_GENEVE_TTL, "ttl", *argv);
                        if (strcmp(*argv, "inherit") != 0) {
                                if (get_unsigned(&uval, *argv, 0))
                                        invarg("invalid TTL", *argv);
@@ -94,6 +108,7 @@ static int geneve_parse_opt(struct link_util *lu, int argc, 
char **argv,
                        __u32 uval;
 
                        NEXT_ARG();
+                       check_duparg(&attrs, IFLA_GENEVE_TOS, "tos", *argv);
                        if (strcmp(*argv, "inherit") != 0) {
                                if (rtnl_dsfield_a2n(&uval, *argv))
                                        invarg("bad TOS value", *argv);
@@ -105,36 +120,50 @@ static int geneve_parse_opt(struct link_util *lu, int 
argc, char **argv,
                        __u32 uval;
 
                        NEXT_ARG();
+                       check_duparg(&attrs, IFLA_GENEVE_LABEL, "flowlabel",
+                                    *argv);
                        if (get_u32(&uval, *argv, 0) ||
                            (uval & ~LABEL_MAX_MASK))
                                invarg("invalid flowlabel", *argv);
                        label = htonl(uval);
                } else if (!matches(*argv, "dstport")) {
                        NEXT_ARG();
+                       check_duparg(&attrs, IFLA_GENEVE_PORT, "dstport",
+                                    *argv);
                        if (get_u16(&dstport, *argv, 0))
                                invarg("dstport", *argv);
                } else if (!matches(*argv, "external")) {
+                       check_duparg(&attrs, IFLA_GENEVE_COLLECT_METADATA,
+                                    *argv, *argv);
                        metadata = true;
                } else if (!matches(*argv, "noexternal")) {
+                       check_duparg(&attrs, IFLA_GENEVE_COLLECT_METADATA,
+                                    *argv, *argv);
                        metadata = false;
                } else if (!matches(*argv, "udpcsum")) {
+                       check_duparg(&attrs, IFLA_GENEVE_UDP_CSUM, *argv,
+                                    *argv);
                        udpcsum = 1;
-                       udpcsum_set = true;
                } else if (!matches(*argv, "noudpcsum")) {
+                       check_duparg(&attrs, IFLA_GENEVE_UDP_CSUM, *argv,
+                                    *argv);
                        udpcsum = 0;
-                       udpcsum_set = true;
                } else if (!matches(*argv, "udp6zerocsumtx")) {
+                       check_duparg(&attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_TX,
+                                    *argv, *argv);
                        udp6zerocsumtx = 1;
-                       udp6zerocsumtx_set = true;
                } else if (!matches(*argv, "noudp6zerocsumtx")) {
+                       check_duparg(&attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_TX,
+                                    *argv, *argv);
                        udp6zerocsumtx = 0;
-                       udp6zerocsumtx_set = true;
                } else if (!matches(*argv, "udp6zerocsumrx")) {
+                       check_duparg(&attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_RX,
+                                    *argv, *argv);
                        udp6zerocsumrx = 1;
-                       udp6zerocsumrx_set = true;
                } else if (!matches(*argv, "noudp6zerocsumrx")) {
+                       check_duparg(&attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_RX,
+                                    *argv, *argv);
                        udp6zerocsumrx = 0;
-                       udp6zerocsumrx_set = true;
                } else if (matches(*argv, "help") == 0) {
                        explain();
                        return -1;
@@ -146,19 +175,23 @@ static int geneve_parse_opt(struct link_util *lu, int 
argc, char **argv,
                argc--, argv++;
        }
 
-       if (metadata && vni_set) {
+       if (metadata && GENEVE_ATTRSET(attrs, IFLA_GENEVE_ID)) {
                fprintf(stderr, "geneve: both 'external' and vni cannot be 
specified\n");
                return -1;
        }
 
        if (!metadata) {
                /* parameter checking make sense only for full geneve tunnels */
-               if (!vni_set) {
+               if (!GENEVE_ATTRSET(attrs, IFLA_GENEVE_ID)) {
                        fprintf(stderr, "geneve: missing virtual network 
identifier\n");
                        return -1;
                }
 
-               if (!daddr && IN6_IS_ADDR_UNSPECIFIED(&daddr6)) {
+               /* If we are modifying the geneve device, then we only need the
+                * ID (VNI) to identify the geneve device, and we do not need
+                * the remote IP.
+                */
+               if (!set_op && !daddr && IN6_IS_ADDR_UNSPECIFIED(&daddr6)) {
                        fprintf(stderr, "geneve: remote link partner not 
specified\n");
                        return -1;
                }
@@ -167,20 +200,25 @@ static int geneve_parse_opt(struct link_util *lu, int 
argc, char **argv,
        addattr32(n, 1024, IFLA_GENEVE_ID, vni);
        if (daddr)
                addattr_l(n, 1024, IFLA_GENEVE_REMOTE, &daddr, 4);
-       if (!IN6_IS_ADDR_UNSPECIFIED(&daddr6))
-               addattr_l(n, 1024, IFLA_GENEVE_REMOTE6, &daddr6, sizeof(struct 
in6_addr));
-       addattr32(n, 1024, IFLA_GENEVE_LABEL, label);
-       addattr8(n, 1024, IFLA_GENEVE_TTL, ttl);
-       addattr8(n, 1024, IFLA_GENEVE_TOS, tos);
+       if (!IN6_IS_ADDR_UNSPECIFIED(&daddr6)) {
+               addattr_l(n, 1024, IFLA_GENEVE_REMOTE6, &daddr6,
+                         sizeof(struct in6_addr));
+       }
+       if (!set_op || GENEVE_ATTRSET(attrs, IFLA_GENEVE_LABEL))
+               addattr32(n, 1024, IFLA_GENEVE_LABEL, label);
+       if (!set_op || GENEVE_ATTRSET(attrs, IFLA_GENEVE_TTL))
+               addattr8(n, 1024, IFLA_GENEVE_TTL, ttl);
+       if (!set_op || GENEVE_ATTRSET(attrs, IFLA_GENEVE_TOS))
+               addattr8(n, 1024, IFLA_GENEVE_TOS, tos);
        if (dstport)
                addattr16(n, 1024, IFLA_GENEVE_PORT, htons(dstport));
        if (metadata)
                addattr(n, 1024, IFLA_GENEVE_COLLECT_METADATA);
-       if (udpcsum_set)
+       if (GENEVE_ATTRSET(attrs, IFLA_GENEVE_UDP_CSUM))
                addattr8(n, 1024, IFLA_GENEVE_UDP_CSUM, udpcsum);
-       if (udp6zerocsumtx_set)
+       if (GENEVE_ATTRSET(attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_TX))
                addattr8(n, 1024, IFLA_GENEVE_UDP_ZERO_CSUM6_TX, 
udp6zerocsumtx);
-       if (udp6zerocsumrx_set)
+       if (GENEVE_ATTRSET(attrs, IFLA_GENEVE_UDP_ZERO_CSUM6_RX))
                addattr8(n, 1024, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, 
udp6zerocsumrx);
 
        return 0;
-- 
1.8.3.1

Reply via email to