iproute2/ip/link_gre.c
<<
>>
Prefs
   1/*
   2 * link_gre.c   gre driver module
   3 *
   4 *              This program is free software; you can redistribute it and/or
   5 *              modify it under the terms of the GNU General Public License
   6 *              as published by the Free Software Foundation; either version
   7 *              2 of the License, or (at your option) any later version.
   8 *
   9 * Authors:     Herbert Xu <herbert@gondor.apana.org.au>
  10 *
  11 */
  12
  13#include <string.h>
  14#include <net/if.h>
  15#include <sys/types.h>
  16#include <sys/socket.h>
  17#include <arpa/inet.h>
  18
  19#include <linux/ip.h>
  20#include <linux/if_tunnel.h>
  21#include "rt_names.h"
  22#include "utils.h"
  23#include "ip_common.h"
  24#include "tunnel.h"
  25
  26static bool gre_is_erspan(struct link_util *lu)
  27{
  28        return !strcmp(lu->id, "erspan");
  29}
  30
  31static void gre_print_help(struct link_util *lu, int argc, char **argv, FILE *f)
  32{
  33        bool is_erspan = gre_is_erspan(lu);
  34
  35        fprintf(f,
  36                "Usage: ... %-9s        [ remote ADDR ]\n"
  37                "                       [ local ADDR ]\n"
  38                "                       [ [no][i|o]seq ]\n"
  39                "                       [ [i|o]key KEY | no[i|o]key ]\n"
  40                "                       [ [no][i|o]csum ]\n"
  41                "                       [ ttl TTL ]\n"
  42                "                       [ tos TOS ]\n"
  43                "                       [ [no]pmtudisc ]\n"
  44                "                       [ [no]ignore-df ]\n"
  45                "                       [ dev PHYS_DEV ]\n"
  46                "                       [ fwmark MARK ]\n"
  47                "                       [ external ]\n"
  48                "                       [ noencap ]\n"
  49                "                       [ encap { fou | gue | none } ]\n"
  50                "                       [ encap-sport PORT ]\n"
  51                "                       [ encap-dport PORT ]\n"
  52                "                       [ [no]encap-csum ]\n"
  53                "                       [ [no]encap-csum6 ]\n"
  54                "                       [ [no]encap-remcsum ]\n", lu->id);
  55        if (is_erspan)
  56                fprintf(f,
  57                        "                       [ erspan_ver version ]\n"
  58                        "                       [ erspan IDX ]\n"
  59                        "                       [ erspan_dir { ingress | egress } ]\n"
  60                        "                       [ erspan_hwid hwid ]\n");
  61        fprintf(f,
  62                "\n"
  63                "Where: ADDR := { IP_ADDRESS | any }\n"
  64                "       TOS  := { NUMBER | inherit }\n"
  65                "       TTL  := { 1..255 | inherit }\n"
  66                "       KEY  := { DOTTED_QUAD | NUMBER }\n"
  67                "       MARK := { 0x0..0xffffffff }\n");
  68}
  69
  70static int gre_parse_opt(struct link_util *lu, int argc, char **argv,
  71                         struct nlmsghdr *n)
  72{
  73        struct ifinfomsg *ifi = NLMSG_DATA(n);
  74        struct {
  75                struct nlmsghdr n;
  76                struct ifinfomsg i;
  77        } req = {
  78                .n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)),
  79                .n.nlmsg_flags = NLM_F_REQUEST,
  80                .n.nlmsg_type = RTM_GETLINK,
  81                .i.ifi_family = preferred_family,
  82                .i.ifi_index = ifi->ifi_index,
  83        };
  84        struct nlmsghdr *answer;
  85        struct rtattr *tb[IFLA_MAX + 1];
  86        struct rtattr *linkinfo[IFLA_INFO_MAX+1];
  87        struct rtattr *greinfo[IFLA_GRE_MAX + 1];
  88        int len;
  89        __u16 iflags = 0;
  90        __u16 oflags = 0;
  91        __be32 ikey = 0;
  92        __be32 okey = 0;
  93        inet_prefix saddr, daddr;
  94        __u8 pmtudisc = 1;
  95        __u8 ignore_df = 0;
  96        __u8 tos = 0;
  97        __u8 ttl = 0;
  98        __u32 link = 0;
  99        __u16 encaptype = 0;
 100        __u16 encapflags = 0;
 101        __u16 encapsport = 0;
 102        __u16 encapdport = 0;
 103        __u8 metadata = 0;
 104        __u32 fwmark = 0;
 105        bool is_erspan = gre_is_erspan(lu);
 106        __u32 erspan_idx = 0;
 107        __u8 erspan_ver = 1;
 108        __u8 erspan_dir = 0;
 109        __u16 erspan_hwid = 0;
 110
 111        inet_prefix_reset(&saddr);
 112        inet_prefix_reset(&daddr);
 113
 114        if (!(n->nlmsg_flags & NLM_F_CREATE)) {
 115                const struct rtattr *rta;
 116
 117                if (rtnl_talk(&rth, &req.n, &answer) < 0) {
 118get_failed:
 119                        fprintf(stderr,
 120                                "Failed to get existing tunnel info.\n");
 121                        return -1;
 122                }
 123
 124                len = answer->nlmsg_len;
 125                len -= NLMSG_LENGTH(sizeof(*ifi));
 126                if (len < 0)
 127                        goto get_failed;
 128
 129                parse_rtattr(tb, IFLA_MAX, IFLA_RTA(NLMSG_DATA(answer)), len);
 130
 131                if (!tb[IFLA_LINKINFO])
 132                        goto get_failed;
 133
 134                parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
 135
 136                if (!linkinfo[IFLA_INFO_DATA])
 137                        goto get_failed;
 138
 139                parse_rtattr_nested(greinfo, IFLA_GRE_MAX,
 140                                    linkinfo[IFLA_INFO_DATA]);
 141
 142                rta = greinfo[IFLA_GRE_LOCAL];
 143                if (rta && get_addr_rta(&saddr, rta, AF_INET))
 144                        goto get_failed;
 145
 146                rta = greinfo[IFLA_GRE_REMOTE];
 147                if (rta && get_addr_rta(&daddr, rta, AF_INET))
 148                        goto get_failed;
 149
 150                if (greinfo[IFLA_GRE_IKEY])
 151                        ikey = rta_getattr_u32(greinfo[IFLA_GRE_IKEY]);
 152
 153                if (greinfo[IFLA_GRE_OKEY])
 154                        okey = rta_getattr_u32(greinfo[IFLA_GRE_OKEY]);
 155
 156                if (greinfo[IFLA_GRE_IFLAGS])
 157                        iflags = rta_getattr_u16(greinfo[IFLA_GRE_IFLAGS]);
 158
 159                if (greinfo[IFLA_GRE_OFLAGS])
 160                        oflags = rta_getattr_u16(greinfo[IFLA_GRE_OFLAGS]);
 161
 162                if (greinfo[IFLA_GRE_PMTUDISC])
 163                        pmtudisc = rta_getattr_u8(
 164                                greinfo[IFLA_GRE_PMTUDISC]);
 165
 166                if (greinfo[IFLA_GRE_IGNORE_DF])
 167                        ignore_df =
 168                                !!rta_getattr_u8(greinfo[IFLA_GRE_IGNORE_DF]);
 169
 170                if (greinfo[IFLA_GRE_TOS])
 171                        tos = rta_getattr_u8(greinfo[IFLA_GRE_TOS]);
 172
 173                if (greinfo[IFLA_GRE_TTL])
 174                        ttl = rta_getattr_u8(greinfo[IFLA_GRE_TTL]);
 175
 176                if (greinfo[IFLA_GRE_LINK])
 177                        link = rta_getattr_u32(greinfo[IFLA_GRE_LINK]);
 178
 179                if (greinfo[IFLA_GRE_ENCAP_TYPE])
 180                        encaptype = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_TYPE]);
 181
 182                if (greinfo[IFLA_GRE_ENCAP_FLAGS])
 183                        encapflags = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_FLAGS]);
 184
 185                if (greinfo[IFLA_GRE_ENCAP_SPORT])
 186                        encapsport = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_SPORT]);
 187
 188                if (greinfo[IFLA_GRE_ENCAP_DPORT])
 189                        encapdport = rta_getattr_u16(greinfo[IFLA_GRE_ENCAP_DPORT]);
 190
 191                if (greinfo[IFLA_GRE_COLLECT_METADATA])
 192                        metadata = 1;
 193
 194                if (greinfo[IFLA_GRE_FWMARK])
 195                        fwmark = rta_getattr_u32(greinfo[IFLA_GRE_FWMARK]);
 196
 197                if (greinfo[IFLA_GRE_ERSPAN_INDEX])
 198                        erspan_idx = rta_getattr_u32(greinfo[IFLA_GRE_ERSPAN_INDEX]);
 199
 200                if (greinfo[IFLA_GRE_ERSPAN_VER])
 201                        erspan_ver = rta_getattr_u8(greinfo[IFLA_GRE_ERSPAN_VER]);
 202
 203                if (greinfo[IFLA_GRE_ERSPAN_DIR])
 204                        erspan_dir = rta_getattr_u8(greinfo[IFLA_GRE_ERSPAN_DIR]);
 205
 206                if (greinfo[IFLA_GRE_ERSPAN_HWID])
 207                        erspan_hwid = rta_getattr_u16(greinfo[IFLA_GRE_ERSPAN_HWID]);
 208
 209                free(answer);
 210        }
 211
 212        while (argc > 0) {
 213                if (!matches(*argv, "key")) {
 214                        NEXT_ARG();
 215                        iflags |= GRE_KEY;
 216                        oflags |= GRE_KEY;
 217                        ikey = okey = tnl_parse_key("key", *argv);
 218                } else if (!matches(*argv, "nokey")) {
 219                        iflags &= ~GRE_KEY;
 220                        oflags &= ~GRE_KEY;
 221                        ikey = okey = 0;
 222                } else if (!matches(*argv, "ikey")) {
 223                        NEXT_ARG();
 224                        iflags |= GRE_KEY;
 225                        ikey = tnl_parse_key("ikey", *argv);
 226                } else if (!matches(*argv, "noikey")) {
 227                        iflags &= ~GRE_KEY;
 228                        ikey = 0;
 229                } else if (!matches(*argv, "okey")) {
 230                        NEXT_ARG();
 231                        oflags |= GRE_KEY;
 232                        okey = tnl_parse_key("okey", *argv);
 233                } else if (!matches(*argv, "nookey")) {
 234                        oflags &= ~GRE_KEY;
 235                        okey = 0;
 236                } else if (!matches(*argv, "seq")) {
 237                        iflags |= GRE_SEQ;
 238                        oflags |= GRE_SEQ;
 239                } else if (!matches(*argv, "noseq")) {
 240                        iflags &= ~GRE_SEQ;
 241                        oflags &= ~GRE_SEQ;
 242                } else if (!matches(*argv, "iseq")) {
 243                        iflags |= GRE_SEQ;
 244                } else if (!matches(*argv, "noiseq")) {
 245                        iflags &= ~GRE_SEQ;
 246                } else if (!matches(*argv, "oseq")) {
 247                        oflags |= GRE_SEQ;
 248                } else if (!matches(*argv, "nooseq")) {
 249                        oflags &= ~GRE_SEQ;
 250                } else if (!matches(*argv, "csum")) {
 251                        iflags |= GRE_CSUM;
 252                        oflags |= GRE_CSUM;
 253                } else if (!matches(*argv, "nocsum")) {
 254                        iflags &= ~GRE_CSUM;
 255                        oflags &= ~GRE_CSUM;
 256                } else if (!matches(*argv, "icsum")) {
 257                        iflags |= GRE_CSUM;
 258                } else if (!matches(*argv, "noicsum")) {
 259                        iflags &= ~GRE_CSUM;
 260                } else if (!matches(*argv, "ocsum")) {
 261                        oflags |= GRE_CSUM;
 262                } else if (!matches(*argv, "noocsum")) {
 263                        oflags &= ~GRE_CSUM;
 264                } else if (!matches(*argv, "nopmtudisc")) {
 265                        pmtudisc = 0;
 266                } else if (!matches(*argv, "pmtudisc")) {
 267                        pmtudisc = 1;
 268                } else if (!matches(*argv, "remote")) {
 269                        NEXT_ARG();
 270                        get_addr(&daddr, *argv, AF_INET);
 271                } else if (!matches(*argv, "local")) {
 272                        NEXT_ARG();
 273                        get_addr(&saddr, *argv, AF_INET);
 274                } else if (!matches(*argv, "dev")) {
 275                        NEXT_ARG();
 276                        link = ll_name_to_index(*argv);
 277                        if (!link)
 278                                exit(nodev(*argv));
 279                } else if (!matches(*argv, "ttl") ||
 280                           !matches(*argv, "hoplimit") ||
 281                           !matches(*argv, "hlim")) {
 282                        NEXT_ARG();
 283                        if (strcmp(*argv, "inherit") != 0) {
 284                                if (get_u8(&ttl, *argv, 0))
 285                                        invarg("invalid TTL\n", *argv);
 286                        } else
 287                                ttl = 0;
 288                } else if (!matches(*argv, "tos") ||
 289                           !matches(*argv, "tclass") ||
 290                           !matches(*argv, "dsfield")) {
 291                        __u32 uval;
 292
 293                        NEXT_ARG();
 294                        if (strcmp(*argv, "inherit") != 0) {
 295                                if (rtnl_dsfield_a2n(&uval, *argv))
 296                                        invarg("bad TOS value", *argv);
 297                                tos = uval;
 298                        } else
 299                                tos = 1;
 300                } else if (strcmp(*argv, "noencap") == 0) {
 301                        encaptype = TUNNEL_ENCAP_NONE;
 302                } else if (strcmp(*argv, "encap") == 0) {
 303                        NEXT_ARG();
 304                        if (strcmp(*argv, "fou") == 0)
 305                                encaptype = TUNNEL_ENCAP_FOU;
 306                        else if (strcmp(*argv, "gue") == 0)
 307                                encaptype = TUNNEL_ENCAP_GUE;
 308                        else if (strcmp(*argv, "none") == 0)
 309                                encaptype = TUNNEL_ENCAP_NONE;
 310                        else
 311                                invarg("Invalid encap type.", *argv);
 312                } else if (strcmp(*argv, "encap-sport") == 0) {
 313                        NEXT_ARG();
 314                        if (strcmp(*argv, "auto") == 0)
 315                                encapsport = 0;
 316                        else if (get_u16(&encapsport, *argv, 0))
 317                                invarg("Invalid source port.", *argv);
 318                } else if (strcmp(*argv, "encap-dport") == 0) {
 319                        NEXT_ARG();
 320                        if (get_u16(&encapdport, *argv, 0))
 321                                invarg("Invalid destination port.", *argv);
 322                } else if (strcmp(*argv, "encap-csum") == 0) {
 323                        encapflags |= TUNNEL_ENCAP_FLAG_CSUM;
 324                } else if (strcmp(*argv, "noencap-csum") == 0) {
 325                        encapflags &= ~TUNNEL_ENCAP_FLAG_CSUM;
 326                } else if (strcmp(*argv, "encap-udp6-csum") == 0) {
 327                        encapflags |= TUNNEL_ENCAP_FLAG_CSUM6;
 328                } else if (strcmp(*argv, "noencap-udp6-csum") == 0) {
 329                        encapflags &= ~TUNNEL_ENCAP_FLAG_CSUM6;
 330                } else if (strcmp(*argv, "encap-remcsum") == 0) {
 331                        encapflags |= TUNNEL_ENCAP_FLAG_REMCSUM;
 332                } else if (strcmp(*argv, "noencap-remcsum") == 0) {
 333                        encapflags &= ~TUNNEL_ENCAP_FLAG_REMCSUM;
 334                } else if (strcmp(*argv, "external") == 0) {
 335                        metadata = 1;
 336                } else if (strcmp(*argv, "ignore-df") == 0) {
 337                        ignore_df = 1;
 338                } else if (strcmp(*argv, "noignore-df") == 0) {
 339                        /*
 340                         *only the lsb is significant, use 2 for presence
 341                         */
 342                        ignore_df = 2;
 343                } else if (strcmp(*argv, "fwmark") == 0) {
 344                        NEXT_ARG();
 345                        if (get_u32(&fwmark, *argv, 0))
 346                                invarg("invalid fwmark\n", *argv);
 347                } else if (is_erspan && strcmp(*argv, "erspan") == 0) {
 348                        NEXT_ARG();
 349                        if (get_u32(&erspan_idx, *argv, 0))
 350                                invarg("invalid erspan index\n", *argv);
 351                        if (erspan_idx & ~((1<<20) - 1) || erspan_idx == 0)
 352                                invarg("erspan index must be > 0 and <= 20-bit\n", *argv);
 353                } else if (is_erspan && strcmp(*argv, "erspan_ver") == 0) {
 354                        NEXT_ARG();
 355                        if (get_u8(&erspan_ver, *argv, 0))
 356                                invarg("invalid erspan version\n", *argv);
 357                        if (erspan_ver > 2)
 358                                invarg("erspan version must be 0/1/2\n", *argv);
 359                } else if (is_erspan && strcmp(*argv, "erspan_dir") == 0) {
 360                        NEXT_ARG();
 361                        if (matches(*argv, "ingress") == 0)
 362                                erspan_dir = 0;
 363                        else if (matches(*argv, "egress") == 0)
 364                                erspan_dir = 1;
 365                        else
 366                                invarg("Invalid erspan direction.", *argv);
 367                } else if (is_erspan && strcmp(*argv, "erspan_hwid") == 0) {
 368                        NEXT_ARG();
 369                        if (get_u16(&erspan_hwid, *argv, 0))
 370                                invarg("invalid erspan hwid\n", *argv);
 371                } else {
 372                        gre_print_help(lu, argc, argv, stderr);
 373                        return -1;
 374                }
 375                argc--; argv++;
 376        }
 377
 378        if (is_addrtype_inet_multi(&daddr)) {
 379                if (!ikey) {
 380                        ikey = daddr.data[0];
 381                        iflags |= GRE_KEY;
 382                }
 383                if (!okey) {
 384                        okey = daddr.data[0];
 385                        oflags |= GRE_KEY;
 386                }
 387                if (!is_addrtype_inet_not_unspec(&saddr)) {
 388                        fprintf(stderr,
 389                                "A broadcast tunnel requires a source address.\n");
 390                        return -1;
 391                }
 392        }
 393
 394        if (metadata) {
 395                addattr_l(n, 1024, IFLA_GRE_COLLECT_METADATA, NULL, 0);
 396                return 0;
 397        }
 398
 399        addattr32(n, 1024, IFLA_GRE_IKEY, ikey);
 400        addattr32(n, 1024, IFLA_GRE_OKEY, okey);
 401        addattr_l(n, 1024, IFLA_GRE_IFLAGS, &iflags, 2);
 402        addattr_l(n, 1024, IFLA_GRE_OFLAGS, &oflags, 2);
 403        if (is_addrtype_inet_not_unspec(&saddr))
 404                addattr_l(n, 1024, IFLA_GRE_LOCAL, saddr.data, saddr.bytelen);
 405        if (is_addrtype_inet_not_unspec(&daddr))
 406                addattr_l(n, 1024, IFLA_GRE_REMOTE, daddr.data, daddr.bytelen);
 407        addattr_l(n, 1024, IFLA_GRE_PMTUDISC, &pmtudisc, 1);
 408        if (ignore_df)
 409                addattr8(n, 1024, IFLA_GRE_IGNORE_DF, ignore_df & 1);
 410        addattr_l(n, 1024, IFLA_GRE_TOS, &tos, 1);
 411        if (link)
 412                addattr32(n, 1024, IFLA_GRE_LINK, link);
 413        addattr_l(n, 1024, IFLA_GRE_TTL, &ttl, 1);
 414        addattr32(n, 1024, IFLA_GRE_FWMARK, fwmark);
 415        if (is_erspan) {
 416                addattr8(n, 1024, IFLA_GRE_ERSPAN_VER, erspan_ver);
 417                if (erspan_ver == 1 && erspan_idx != 0) {
 418                        addattr32(n, 1024, IFLA_GRE_ERSPAN_INDEX, erspan_idx);
 419                } else if (erspan_ver == 2) {
 420                        addattr8(n, 1024, IFLA_GRE_ERSPAN_DIR, erspan_dir);
 421                        addattr16(n, 1024, IFLA_GRE_ERSPAN_HWID, erspan_hwid);
 422                }
 423        }
 424        addattr16(n, 1024, IFLA_GRE_ENCAP_TYPE, encaptype);
 425        addattr16(n, 1024, IFLA_GRE_ENCAP_FLAGS, encapflags);
 426        addattr16(n, 1024, IFLA_GRE_ENCAP_SPORT, htons(encapsport));
 427        addattr16(n, 1024, IFLA_GRE_ENCAP_DPORT, htons(encapdport));
 428
 429        return 0;
 430}
 431
 432static void gre_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 433{
 434        char s2[64];
 435        __u16 iflags = 0;
 436        __u16 oflags = 0;
 437        __u8 ttl = 0;
 438        __u8 tos = 0;
 439
 440        if (!tb)
 441                return;
 442
 443        if (tb[IFLA_GRE_COLLECT_METADATA]) {
 444                print_bool(PRINT_ANY, "external", "external ", true);
 445                return;
 446        }
 447
 448        tnl_print_endpoint("remote", tb[IFLA_GRE_REMOTE], AF_INET);
 449        tnl_print_endpoint("local", tb[IFLA_GRE_LOCAL], AF_INET);
 450
 451        if (tb[IFLA_GRE_LINK]) {
 452                __u32 link = rta_getattr_u32(tb[IFLA_GRE_LINK]);
 453
 454                if (link) {
 455                        print_string(PRINT_ANY, "link", "dev %s ",
 456                                     ll_index_to_name(link));
 457                }
 458        }
 459
 460        if (tb[IFLA_GRE_TTL])
 461                ttl = rta_getattr_u8(tb[IFLA_GRE_TTL]);
 462        if (is_json_context() || ttl)
 463                print_uint(PRINT_ANY, "ttl", "ttl %u ", ttl);
 464        else
 465                print_string(PRINT_FP, NULL, "ttl %s ", "inherit");
 466
 467        if (tb[IFLA_GRE_TOS])
 468                tos = rta_getattr_u8(tb[IFLA_GRE_TOS]);
 469        if (tos) {
 470                if (is_json_context() || tos != 1)
 471                        print_0xhex(PRINT_ANY, "tos", "tos %#llx ", tos);
 472                else
 473                        print_string(PRINT_FP, NULL, "tos %s ", "inherit");
 474        }
 475
 476        if (tb[IFLA_GRE_PMTUDISC]) {
 477                if (!rta_getattr_u8(tb[IFLA_GRE_PMTUDISC]))
 478                        print_bool(PRINT_ANY, "pmtudisc", "nopmtudisc ", false);
 479                else
 480                        print_bool(PRINT_JSON, "pmtudisc", NULL, true);
 481        }
 482
 483        if (tb[IFLA_GRE_IGNORE_DF] && rta_getattr_u8(tb[IFLA_GRE_IGNORE_DF]))
 484                print_bool(PRINT_ANY, "ignore_df", "ignore-df ", true);
 485
 486        if (tb[IFLA_GRE_IFLAGS])
 487                iflags = rta_getattr_u16(tb[IFLA_GRE_IFLAGS]);
 488
 489        if (tb[IFLA_GRE_OFLAGS])
 490                oflags = rta_getattr_u16(tb[IFLA_GRE_OFLAGS]);
 491
 492        if ((iflags & GRE_KEY) && tb[IFLA_GRE_IKEY]) {
 493                inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_IKEY]), s2, sizeof(s2));
 494                print_string(PRINT_ANY, "ikey", "ikey %s ", s2);
 495        }
 496
 497        if ((oflags & GRE_KEY) && tb[IFLA_GRE_OKEY]) {
 498                inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_OKEY]), s2, sizeof(s2));
 499                print_string(PRINT_ANY, "okey", "okey %s ", s2);
 500        }
 501
 502        if (iflags & GRE_SEQ)
 503                print_bool(PRINT_ANY, "iseq", "iseq ", true);
 504        if (oflags & GRE_SEQ)
 505                print_bool(PRINT_ANY, "oseq", "oseq ", true);
 506        if (iflags & GRE_CSUM)
 507                print_bool(PRINT_ANY, "icsum", "icsum ", true);
 508        if (oflags & GRE_CSUM)
 509                print_bool(PRINT_ANY, "ocsum", "ocsum ", true);
 510
 511        if (tb[IFLA_GRE_FWMARK]) {
 512                __u32 fwmark = rta_getattr_u32(tb[IFLA_GRE_FWMARK]);
 513
 514                if (fwmark) {
 515                        print_0xhex(PRINT_ANY,
 516                                    "fwmark", "fwmark %#llx ", fwmark);
 517                }
 518        }
 519
 520        if (tb[IFLA_GRE_ERSPAN_INDEX]) {
 521                __u32 erspan_idx = rta_getattr_u32(tb[IFLA_GRE_ERSPAN_INDEX]);
 522
 523                print_uint(PRINT_ANY,
 524                           "erspan_index", "erspan_index %u ", erspan_idx);
 525        }
 526
 527        if (tb[IFLA_GRE_ERSPAN_VER]) {
 528                __u8 erspan_ver = rta_getattr_u8(tb[IFLA_GRE_ERSPAN_VER]);
 529
 530                print_uint(PRINT_ANY,
 531                           "erspan_ver", "erspan_ver %u ", erspan_ver);
 532        }
 533
 534        if (tb[IFLA_GRE_ERSPAN_DIR]) {
 535                __u8 erspan_dir = rta_getattr_u8(tb[IFLA_GRE_ERSPAN_DIR]);
 536
 537                if (erspan_dir == 0)
 538                        print_string(PRINT_ANY, "erspan_dir",
 539                                     "erspan_dir %s ", "ingress");
 540                else
 541                        print_string(PRINT_ANY, "erspan_dir",
 542                                     "erspan_dir %s ", "egress");
 543        }
 544
 545        if (tb[IFLA_GRE_ERSPAN_HWID]) {
 546                __u16 erspan_hwid = rta_getattr_u16(tb[IFLA_GRE_ERSPAN_HWID]);
 547
 548                print_0xhex(PRINT_ANY,
 549                            "erspan_hwid", "erspan_hwid %#llx ", erspan_hwid);
 550        }
 551
 552        tnl_print_encap(tb,
 553                        IFLA_GRE_ENCAP_TYPE,
 554                        IFLA_GRE_ENCAP_FLAGS,
 555                        IFLA_GRE_ENCAP_SPORT,
 556                        IFLA_GRE_ENCAP_DPORT);
 557}
 558
 559struct link_util gre_link_util = {
 560        .id = "gre",
 561        .maxattr = IFLA_GRE_MAX,
 562        .parse_opt = gre_parse_opt,
 563        .print_opt = gre_print_opt,
 564        .print_help = gre_print_help,
 565};
 566
 567struct link_util gretap_link_util = {
 568        .id = "gretap",
 569        .maxattr = IFLA_GRE_MAX,
 570        .parse_opt = gre_parse_opt,
 571        .print_opt = gre_print_opt,
 572        .print_help = gre_print_help,
 573};
 574
 575struct link_util erspan_link_util = {
 576        .id = "erspan",
 577        .maxattr = IFLA_GRE_MAX,
 578        .parse_opt = gre_parse_opt,
 579        .print_opt = gre_print_opt,
 580        .print_help = gre_print_help,
 581};
 582