iproute2/ip/link_gre6.c
<<
>>
Prefs
   1/*
   2 * link_gre6.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:     Dmitry Kozlov <xeb@mail.ru>
  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 <linux/ip6_tunnel.h>
  22
  23#include "rt_names.h"
  24#include "utils.h"
  25#include "ip_common.h"
  26#include "tunnel.h"
  27
  28#define IP6_FLOWINFO_TCLASS     htonl(0x0FF00000)
  29#define IP6_FLOWINFO_FLOWLABEL  htonl(0x000FFFFF)
  30
  31#define DEFAULT_TNL_HOP_LIMIT   (64)
  32
  33static void gre_print_help(struct link_util *lu, int argc, char **argv, FILE *f)
  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                "                       [ hoplimit TTL ]\n"
  42                "                       [ encaplimit ELIM ]\n"
  43                "                       [ tclass TCLASS ]\n"
  44                "                       [ flowlabel FLOWLABEL ]\n"
  45                "                       [ dscp inherit ]\n"
  46                "                       [ dev PHYS_DEV ]\n"
  47                "                       [ fwmark MARK ]\n"
  48                "                       [ [no]allow-localremote ]\n"
  49                "                       [ external ]\n"
  50                "                       [ noencap ]\n"
  51                "                       [ encap { fou | gue | none } ]\n"
  52                "                       [ encap-sport PORT ]\n"
  53                "                       [ encap-dport PORT ]\n"
  54                "                       [ [no]encap-csum ]\n"
  55                "                       [ [no]encap-csum6 ]\n"
  56                "                       [ [no]encap-remcsum ]\n"
  57                "                       [ erspan_ver version ]\n"
  58                "                       [ erspan IDX ]\n"
  59                "                       [ erspan_dir { ingress | egress } ]\n"
  60                "                       [ erspan_hwid hwid ]\n"
  61                "\n"
  62                "Where: ADDR      := IPV6_ADDRESS\n"
  63                "       TTL       := { 0..255 } (default=%d)\n"
  64                "       KEY       := { DOTTED_QUAD | NUMBER }\n"
  65                "       ELIM      := { none | 0..255 }(default=%d)\n"
  66                "       TCLASS    := { 0x0..0xff | inherit }\n"
  67                "       FLOWLABEL := { 0x0..0xfffff | inherit }\n"
  68                "       MARK      := { 0x0..0xffffffff | inherit }\n",
  69                lu->id,
  70                DEFAULT_TNL_HOP_LIMIT, IPV6_DEFAULT_TNL_ENCAP_LIMIT);
  71}
  72
  73static int gre_parse_opt(struct link_util *lu, int argc, char **argv,
  74                         struct nlmsghdr *n)
  75{
  76        struct ifinfomsg *ifi = NLMSG_DATA(n);
  77        struct {
  78                struct nlmsghdr n;
  79                struct ifinfomsg i;
  80        } req = {
  81                .n.nlmsg_len = NLMSG_LENGTH(sizeof(*ifi)),
  82                .n.nlmsg_flags = NLM_F_REQUEST,
  83                .n.nlmsg_type = RTM_GETLINK,
  84                .i.ifi_family = preferred_family,
  85                .i.ifi_index = ifi->ifi_index,
  86        };
  87        struct nlmsghdr *answer;
  88        struct rtattr *tb[IFLA_MAX + 1];
  89        struct rtattr *linkinfo[IFLA_INFO_MAX+1];
  90        struct rtattr *greinfo[IFLA_GRE_MAX + 1];
  91        int len;
  92        __u16 iflags = 0;
  93        __u16 oflags = 0;
  94        __be32 ikey = 0;
  95        __be32 okey = 0;
  96        inet_prefix saddr, daddr;
  97        __u8 hop_limit = DEFAULT_TNL_HOP_LIMIT;
  98        __u8 encap_limit = IPV6_DEFAULT_TNL_ENCAP_LIMIT;
  99        __u32 flowinfo = 0;
 100        __u32 flags = 0;
 101        __u32 link = 0;
 102        __u16 encaptype = 0;
 103        __u16 encapflags = TUNNEL_ENCAP_FLAG_CSUM6;
 104        __u16 encapsport = 0;
 105        __u16 encapdport = 0;
 106        __u8 metadata = 0;
 107        __u32 fwmark = 0;
 108        __u32 erspan_idx = 0;
 109        __u8 erspan_ver = 1;
 110        __u8 erspan_dir = 0;
 111        __u16 erspan_hwid = 0;
 112
 113        inet_prefix_reset(&saddr);
 114        inet_prefix_reset(&daddr);
 115
 116        if (!(n->nlmsg_flags & NLM_F_CREATE)) {
 117                const struct rtattr *rta;
 118
 119                if (rtnl_talk(&rth, &req.n, &answer) < 0) {
 120get_failed:
 121                        fprintf(stderr,
 122                                "Failed to get existing tunnel info.\n");
 123                        return -1;
 124                }
 125
 126                len = answer->nlmsg_len;
 127                len -= NLMSG_LENGTH(sizeof(*ifi));
 128                if (len < 0)
 129                        goto get_failed;
 130
 131                parse_rtattr(tb, IFLA_MAX, IFLA_RTA(NLMSG_DATA(answer)), len);
 132
 133                if (!tb[IFLA_LINKINFO])
 134                        goto get_failed;
 135
 136                parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
 137
 138                if (!linkinfo[IFLA_INFO_DATA])
 139                        goto get_failed;
 140
 141                parse_rtattr_nested(greinfo, IFLA_GRE_MAX,
 142                                    linkinfo[IFLA_INFO_DATA]);
 143
 144                rta = greinfo[IFLA_GRE_LOCAL];
 145                if (rta && get_addr_rta(&saddr, rta, AF_INET6))
 146                        goto get_failed;
 147
 148                rta = greinfo[IFLA_GRE_REMOTE];
 149                if (rta && get_addr_rta(&daddr, rta, AF_INET6))
 150                        goto get_failed;
 151
 152                if (greinfo[IFLA_GRE_IKEY])
 153                        ikey = rta_getattr_u32(greinfo[IFLA_GRE_IKEY]);
 154
 155                if (greinfo[IFLA_GRE_OKEY])
 156                        okey = rta_getattr_u32(greinfo[IFLA_GRE_OKEY]);
 157
 158                if (greinfo[IFLA_GRE_IFLAGS])
 159                        iflags = rta_getattr_u16(greinfo[IFLA_GRE_IFLAGS]);
 160
 161                if (greinfo[IFLA_GRE_OFLAGS])
 162                        oflags = rta_getattr_u16(greinfo[IFLA_GRE_OFLAGS]);
 163
 164                if (greinfo[IFLA_GRE_TTL])
 165                        hop_limit = rta_getattr_u8(greinfo[IFLA_GRE_TTL]);
 166
 167                if (greinfo[IFLA_GRE_LINK])
 168                        link = rta_getattr_u32(greinfo[IFLA_GRE_LINK]);
 169
 170                if (greinfo[IFLA_GRE_ENCAP_LIMIT])
 171                        encap_limit = rta_getattr_u8(greinfo[IFLA_GRE_ENCAP_LIMIT]);
 172
 173                if (greinfo[IFLA_GRE_FLOWINFO])
 174                        flowinfo = rta_getattr_u32(greinfo[IFLA_GRE_FLOWINFO]);
 175
 176                if (greinfo[IFLA_GRE_FLAGS])
 177                        flags = rta_getattr_u32(greinfo[IFLA_GRE_FLAGS]);
 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, "remote")) {
 265                        NEXT_ARG();
 266                        get_addr(&daddr, *argv, AF_INET6);
 267                } else if (!matches(*argv, "local")) {
 268                        NEXT_ARG();
 269                        get_addr(&saddr, *argv, AF_INET6);
 270                } else if (!matches(*argv, "dev")) {
 271                        NEXT_ARG();
 272                        link = ll_name_to_index(*argv);
 273                        if (!link)
 274                                exit(nodev(*argv));
 275                } else if (!matches(*argv, "ttl") ||
 276                           !matches(*argv, "hoplimit") ||
 277                           !matches(*argv, "hlim")) {
 278                        NEXT_ARG();
 279                        if (strcmp(*argv, "inherit") != 0) {
 280                                if (get_u8(&hop_limit, *argv, 0))
 281                                        invarg("invalid HLIM\n", *argv);
 282                        } else
 283                                hop_limit = 0;
 284                } else if (!matches(*argv, "tos") ||
 285                           !matches(*argv, "tclass") ||
 286                           !matches(*argv, "dsfield")) {
 287                        __u8 uval;
 288
 289                        NEXT_ARG();
 290                        flowinfo &= ~IP6_FLOWINFO_TCLASS;
 291                        if (strcmp(*argv, "inherit") == 0)
 292                                flags |= IP6_TNL_F_USE_ORIG_TCLASS;
 293                        else {
 294                                if (get_u8(&uval, *argv, 16))
 295                                        invarg("invalid TClass", *argv);
 296                                flowinfo |= htonl((__u32)uval << 20) & IP6_FLOWINFO_TCLASS;
 297                                flags &= ~IP6_TNL_F_USE_ORIG_TCLASS;
 298                        }
 299                } else if (strcmp(*argv, "flowlabel") == 0 ||
 300                           strcmp(*argv, "fl") == 0) {
 301                        __u32 uval;
 302
 303                        NEXT_ARG();
 304                        flowinfo &= ~IP6_FLOWINFO_FLOWLABEL;
 305                        if (strcmp(*argv, "inherit") == 0)
 306                                flags |= IP6_TNL_F_USE_ORIG_FLOWLABEL;
 307                        else {
 308                                if (get_u32(&uval, *argv, 16))
 309                                        invarg("invalid Flowlabel", *argv);
 310                                if (uval > 0xFFFFF)
 311                                        invarg("invalid Flowlabel", *argv);
 312                                flowinfo |= htonl(uval) & IP6_FLOWINFO_FLOWLABEL;
 313                                flags &= ~IP6_TNL_F_USE_ORIG_FLOWLABEL;
 314                        }
 315                } else if (strcmp(*argv, "dscp") == 0) {
 316                        NEXT_ARG();
 317                        if (strcmp(*argv, "inherit") != 0)
 318                                invarg("not inherit", *argv);
 319                        flags |= IP6_TNL_F_RCV_DSCP_COPY;
 320                } else if (strcmp(*argv, "noencap") == 0) {
 321                        encaptype = TUNNEL_ENCAP_NONE;
 322                } else if (strcmp(*argv, "encap") == 0) {
 323                        NEXT_ARG();
 324                        if (strcmp(*argv, "fou") == 0)
 325                                encaptype = TUNNEL_ENCAP_FOU;
 326                        else if (strcmp(*argv, "gue") == 0)
 327                                encaptype = TUNNEL_ENCAP_GUE;
 328                        else if (strcmp(*argv, "none") == 0)
 329                                encaptype = TUNNEL_ENCAP_NONE;
 330                        else
 331                                invarg("Invalid encap type.", *argv);
 332                } else if (strcmp(*argv, "encap-sport") == 0) {
 333                        NEXT_ARG();
 334                        if (strcmp(*argv, "auto") == 0)
 335                                encapsport = 0;
 336                        else if (get_u16(&encapsport, *argv, 0))
 337                                invarg("Invalid source port.", *argv);
 338                } else if (strcmp(*argv, "encap-dport") == 0) {
 339                        NEXT_ARG();
 340                        if (get_u16(&encapdport, *argv, 0))
 341                                invarg("Invalid destination port.", *argv);
 342                } else if (strcmp(*argv, "encap-csum") == 0) {
 343                        encapflags |= TUNNEL_ENCAP_FLAG_CSUM;
 344                } else if (strcmp(*argv, "noencap-csum") == 0) {
 345                        encapflags &= ~TUNNEL_ENCAP_FLAG_CSUM;
 346                } else if (strcmp(*argv, "encap-udp6-csum") == 0) {
 347                        encapflags |= TUNNEL_ENCAP_FLAG_CSUM6;
 348                } else if (strcmp(*argv, "noencap-udp6-csum") == 0) {
 349                        encapflags &= ~TUNNEL_ENCAP_FLAG_CSUM6;
 350                } else if (strcmp(*argv, "encap-remcsum") == 0) {
 351                        encapflags |= TUNNEL_ENCAP_FLAG_REMCSUM;
 352                } else if (strcmp(*argv, "noencap-remcsum") == 0) {
 353                        encapflags &= ~TUNNEL_ENCAP_FLAG_REMCSUM;
 354                } else if (strcmp(*argv, "external") == 0) {
 355                        metadata = 1;
 356                } else if (strcmp(*argv, "fwmark") == 0) {
 357                        NEXT_ARG();
 358                        if (strcmp(*argv, "inherit") == 0) {
 359                                flags |= IP6_TNL_F_USE_ORIG_FWMARK;
 360                                fwmark = 0;
 361                        } else {
 362                                if (get_u32(&fwmark, *argv, 0))
 363                                        invarg("invalid fwmark\n", *argv);
 364                                flags &= ~IP6_TNL_F_USE_ORIG_FWMARK;
 365                        }
 366                } else if (strcmp(*argv, "allow-localremote") == 0) {
 367                        flags |= IP6_TNL_F_ALLOW_LOCAL_REMOTE;
 368                } else if (strcmp(*argv, "noallow-localremote") == 0) {
 369                        flags &= ~IP6_TNL_F_ALLOW_LOCAL_REMOTE;
 370                } else if (strcmp(*argv, "encaplimit") == 0) {
 371                        NEXT_ARG();
 372                        if (strcmp(*argv, "none") == 0) {
 373                                flags |= IP6_TNL_F_IGN_ENCAP_LIMIT;
 374                        } else {
 375                                __u8 uval;
 376
 377                                if (get_u8(&uval, *argv, 0))
 378                                        invarg("invalid ELIM", *argv);
 379                                encap_limit = uval;
 380                                flags &= ~IP6_TNL_F_IGN_ENCAP_LIMIT;
 381                        }
 382                } else if (strcmp(*argv, "erspan") == 0) {
 383                        NEXT_ARG();
 384                        if (get_u32(&erspan_idx, *argv, 0))
 385                                invarg("invalid erspan index\n", *argv);
 386                        if (erspan_idx & ~((1<<20) - 1) || erspan_idx == 0)
 387                                invarg("erspan index must be > 0 and <= 20-bit\n", *argv);
 388                } else if (strcmp(*argv, "erspan_ver") == 0) {
 389                        NEXT_ARG();
 390                        if (get_u8(&erspan_ver, *argv, 0))
 391                                invarg("invalid erspan version\n", *argv);
 392                        if (erspan_ver > 2)
 393                                invarg("erspan version must be 0/1/2\n", *argv);
 394                } else if (strcmp(*argv, "erspan_dir") == 0) {
 395                        NEXT_ARG();
 396                        if (matches(*argv, "ingress") == 0)
 397                                erspan_dir = 0;
 398                        else if (matches(*argv, "egress") == 0)
 399                                erspan_dir = 1;
 400                        else
 401                                invarg("Invalid erspan direction.", *argv);
 402                } else if (strcmp(*argv, "erspan_hwid") == 0) {
 403                        NEXT_ARG();
 404                        if (get_u16(&erspan_hwid, *argv, 0))
 405                                invarg("invalid erspan hwid\n", *argv);
 406                } else {
 407                        gre_print_help(lu, argc, argv, stderr);
 408                        return -1;
 409                }
 410                argc--; argv++;
 411        }
 412
 413        if (metadata) {
 414                addattr_l(n, 1024, IFLA_GRE_COLLECT_METADATA, NULL, 0);
 415                return 0;
 416        }
 417
 418        addattr32(n, 1024, IFLA_GRE_IKEY, ikey);
 419        addattr32(n, 1024, IFLA_GRE_OKEY, okey);
 420        addattr_l(n, 1024, IFLA_GRE_IFLAGS, &iflags, 2);
 421        addattr_l(n, 1024, IFLA_GRE_OFLAGS, &oflags, 2);
 422        if (is_addrtype_inet_not_unspec(&saddr))
 423                addattr_l(n, 1024, IFLA_GRE_LOCAL, saddr.data, saddr.bytelen);
 424        if (is_addrtype_inet_not_unspec(&daddr))
 425                addattr_l(n, 1024, IFLA_GRE_REMOTE, daddr.data, daddr.bytelen);
 426        if (link)
 427                addattr32(n, 1024, IFLA_GRE_LINK, link);
 428        addattr_l(n, 1024, IFLA_GRE_TTL, &hop_limit, 1);
 429        addattr_l(n, 1024, IFLA_GRE_ENCAP_LIMIT, &encap_limit, 1);
 430        addattr_l(n, 1024, IFLA_GRE_FLOWINFO, &flowinfo, 4);
 431        addattr32(n, 1024, IFLA_GRE_FLAGS, flags);
 432        addattr32(n, 1024, IFLA_GRE_FWMARK, fwmark);
 433        if (erspan_ver <= 2) {
 434                addattr8(n, 1024, IFLA_GRE_ERSPAN_VER, erspan_ver);
 435                if (erspan_ver == 1 && erspan_idx != 0) {
 436                        addattr32(n, 1024, IFLA_GRE_ERSPAN_INDEX, erspan_idx);
 437                } else if (erspan_ver == 2) {
 438                        addattr8(n, 1024, IFLA_GRE_ERSPAN_DIR, erspan_dir);
 439                        addattr16(n, 1024, IFLA_GRE_ERSPAN_HWID, erspan_hwid);
 440                }
 441        }
 442        addattr16(n, 1024, IFLA_GRE_ENCAP_TYPE, encaptype);
 443        addattr16(n, 1024, IFLA_GRE_ENCAP_FLAGS, encapflags);
 444        addattr16(n, 1024, IFLA_GRE_ENCAP_SPORT, htons(encapsport));
 445        addattr16(n, 1024, IFLA_GRE_ENCAP_DPORT, htons(encapdport));
 446
 447        return 0;
 448}
 449
 450static void gre_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 451{
 452        char s2[64];
 453        __u16 iflags = 0;
 454        __u16 oflags = 0;
 455        __u32 flags = 0;
 456        __u32 flowinfo = 0;
 457        __u8 ttl = 0;
 458
 459        if (!tb)
 460                return;
 461
 462        if (tb[IFLA_GRE_COLLECT_METADATA]) {
 463                print_bool(PRINT_ANY, "external", "external ", true);
 464                return;
 465        }
 466
 467        if (tb[IFLA_GRE_FLAGS])
 468                flags = rta_getattr_u32(tb[IFLA_GRE_FLAGS]);
 469
 470        if (tb[IFLA_GRE_FLOWINFO])
 471                flowinfo = rta_getattr_u32(tb[IFLA_GRE_FLOWINFO]);
 472
 473        tnl_print_endpoint("remote", tb[IFLA_GRE_REMOTE], AF_INET6);
 474        tnl_print_endpoint("local", tb[IFLA_GRE_LOCAL], AF_INET6);
 475
 476        if (tb[IFLA_GRE_LINK]) {
 477                __u32 link = rta_getattr_u32(tb[IFLA_GRE_LINK]);
 478
 479                if (link) {
 480                        print_string(PRINT_ANY, "link", "dev %s ",
 481                                     ll_index_to_name(link));
 482                }
 483        }
 484
 485        if (tb[IFLA_GRE_TTL])
 486                ttl = rta_getattr_u8(tb[IFLA_GRE_TTL]);
 487        if (is_json_context() || ttl)
 488                print_uint(PRINT_ANY, "ttl", "hoplimit %u ", ttl);
 489        else
 490                print_string(PRINT_FP, NULL, "hoplimit %s ", "inherit");
 491
 492        if (flags & IP6_TNL_F_IGN_ENCAP_LIMIT) {
 493                print_bool(PRINT_ANY,
 494                           "ip6_tnl_f_ign_encap_limit",
 495                           "encaplimit none ",
 496                           true);
 497        } else if (tb[IFLA_GRE_ENCAP_LIMIT]) {
 498                __u8 val = rta_getattr_u8(tb[IFLA_GRE_ENCAP_LIMIT]);
 499
 500                print_uint(PRINT_ANY, "encap_limit", "encaplimit %u ", val);
 501        }
 502
 503        if (flags & IP6_TNL_F_USE_ORIG_TCLASS) {
 504                print_bool(PRINT_ANY,
 505                           "ip6_tnl_f_use_orig_tclass",
 506                           "tclass inherit ",
 507                           true);
 508        } else if (tb[IFLA_GRE_FLOWINFO]) {
 509                __u32 val = ntohl(flowinfo & IP6_FLOWINFO_TCLASS) >> 20;
 510
 511                snprintf(s2, sizeof(s2), "0x%02x", val);
 512                print_string(PRINT_ANY, "tclass", "tclass %s ", s2);
 513        }
 514
 515        if (flags & IP6_TNL_F_USE_ORIG_FLOWLABEL) {
 516                print_bool(PRINT_ANY,
 517                           "ip6_tnl_f_use_orig_flowlabel",
 518                           "flowlabel inherit ",
 519                           true);
 520        } else if (tb[IFLA_GRE_FLOWINFO]) {
 521                __u32 val = ntohl(flowinfo & IP6_FLOWINFO_FLOWLABEL);
 522
 523                snprintf(s2, sizeof(s2), "0x%05x", val);
 524                print_string(PRINT_ANY, "flowlabel", "flowlabel %s ", s2);
 525        }
 526
 527        if (flags & IP6_TNL_F_RCV_DSCP_COPY)
 528                print_bool(PRINT_ANY,
 529                           "ip6_tnl_f_rcv_dscp_copy",
 530                           "dscp inherit ",
 531                           true);
 532
 533        if (tb[IFLA_GRE_IFLAGS])
 534                iflags = rta_getattr_u16(tb[IFLA_GRE_IFLAGS]);
 535
 536        if (tb[IFLA_GRE_OFLAGS])
 537                oflags = rta_getattr_u16(tb[IFLA_GRE_OFLAGS]);
 538
 539        if ((iflags & GRE_KEY) && tb[IFLA_GRE_IKEY]) {
 540                inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_IKEY]), s2, sizeof(s2));
 541                print_string(PRINT_ANY, "ikey", "ikey %s ", s2);
 542        }
 543
 544        if ((oflags & GRE_KEY) && tb[IFLA_GRE_OKEY]) {
 545                inet_ntop(AF_INET, RTA_DATA(tb[IFLA_GRE_OKEY]), s2, sizeof(s2));
 546                print_string(PRINT_ANY, "okey", "okey %s ", s2);
 547        }
 548
 549        if (iflags & GRE_SEQ)
 550                print_bool(PRINT_ANY, "iseq", "iseq ", true);
 551        if (oflags & GRE_SEQ)
 552                print_bool(PRINT_ANY, "oseq", "oseq ", true);
 553        if (iflags & GRE_CSUM)
 554                print_bool(PRINT_ANY, "icsum", "icsum ", true);
 555        if (oflags & GRE_CSUM)
 556                print_bool(PRINT_ANY, "ocsum", "ocsum ", true);
 557
 558        if (flags & IP6_TNL_F_ALLOW_LOCAL_REMOTE)
 559                print_bool(PRINT_ANY,
 560                           "ip6_tnl_f_allow_local_remote",
 561                           "allow-localremote ",
 562                           true);
 563
 564        if (flags & IP6_TNL_F_USE_ORIG_FWMARK) {
 565                print_bool(PRINT_ANY,
 566                           "ip6_tnl_f_use_orig_fwmark",
 567                           "fwmark inherit ",
 568                           true);
 569        } else if (tb[IFLA_GRE_FWMARK]) {
 570                __u32 fwmark = rta_getattr_u32(tb[IFLA_GRE_FWMARK]);
 571
 572                if (fwmark) {
 573                        print_0xhex(PRINT_ANY,
 574                                    "fwmark", "fwmark %#llx ", fwmark);
 575                }
 576        }
 577
 578        if (tb[IFLA_GRE_ERSPAN_INDEX]) {
 579                __u32 erspan_idx = rta_getattr_u32(tb[IFLA_GRE_ERSPAN_INDEX]);
 580
 581                print_uint(PRINT_ANY,
 582                           "erspan_index", "erspan_index %u ", erspan_idx);
 583        }
 584
 585        if (tb[IFLA_GRE_ERSPAN_VER]) {
 586                __u8 erspan_ver = rta_getattr_u8(tb[IFLA_GRE_ERSPAN_VER]);
 587
 588                print_uint(PRINT_ANY,
 589                           "erspan_ver", "erspan_ver %u ", erspan_ver);
 590        }
 591
 592        if (tb[IFLA_GRE_ERSPAN_DIR]) {
 593                __u8 erspan_dir = rta_getattr_u8(tb[IFLA_GRE_ERSPAN_DIR]);
 594
 595                if (erspan_dir == 0)
 596                        print_string(PRINT_ANY, "erspan_dir",
 597                                     "erspan_dir %s ", "ingress");
 598                else
 599                        print_string(PRINT_ANY, "erspan_dir",
 600                                     "erspan_dir %s ", "egress");
 601        }
 602
 603        if (tb[IFLA_GRE_ERSPAN_HWID]) {
 604                __u16 erspan_hwid = rta_getattr_u16(tb[IFLA_GRE_ERSPAN_HWID]);
 605
 606                print_0xhex(PRINT_ANY,
 607                            "erspan_hwid", "erspan_hwid %#llx ", erspan_hwid);
 608        }
 609
 610        tnl_print_encap(tb,
 611                        IFLA_GRE_ENCAP_TYPE,
 612                        IFLA_GRE_ENCAP_FLAGS,
 613                        IFLA_GRE_ENCAP_SPORT,
 614                        IFLA_GRE_ENCAP_DPORT);
 615}
 616
 617struct link_util ip6gre_link_util = {
 618        .id = "ip6gre",
 619        .maxattr = IFLA_GRE_MAX,
 620        .parse_opt = gre_parse_opt,
 621        .print_opt = gre_print_opt,
 622        .print_help = gre_print_help,
 623};
 624
 625struct link_util ip6gretap_link_util = {
 626        .id = "ip6gretap",
 627        .maxattr = IFLA_GRE_MAX,
 628        .parse_opt = gre_parse_opt,
 629        .print_opt = gre_print_opt,
 630        .print_help = gre_print_help,
 631};
 632
 633struct link_util ip6erspan_link_util = {
 634        .id = "ip6erspan",
 635        .maxattr = IFLA_GRE_MAX,
 636        .parse_opt = gre_parse_opt,
 637        .print_opt = gre_print_opt,
 638        .print_help = gre_print_help,
 639};
 640