iproute2/ip/iptunnel.c
<<
>>
Prefs
   1/*
   2 * iptunnel.c          "ip tunnel"
   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:     Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  10 *
  11 */
  12
  13#include <stdio.h>
  14#include <stdlib.h>
  15#include <string.h>
  16#include <unistd.h>
  17#include <sys/types.h>
  18#include <sys/socket.h>
  19#include <arpa/inet.h>
  20#include <sys/ioctl.h>
  21#include <net/if.h>
  22#include <net/if_arp.h>
  23#include <linux/ip.h>
  24#include <linux/if_tunnel.h>
  25
  26#include "rt_names.h"
  27#include "utils.h"
  28#include "ip_common.h"
  29#include "tunnel.h"
  30
  31static void usage(void) __attribute__((noreturn));
  32
  33static void usage(void)
  34{
  35        fprintf(stderr,
  36                "Usage: ip tunnel { add | change | del | show | prl | 6rd } [ NAME ]\n"
  37                "        [ mode { gre | ipip | isatap | sit | vti } ]\n"
  38                "        [ remote ADDR ] [ local ADDR ]\n"
  39                "        [ [i|o]seq ] [ [i|o]key KEY ] [ [i|o]csum ]\n"
  40                "        [ prl-default ADDR ] [ prl-nodefault ADDR ] [ prl-delete ADDR ]\n"
  41                "        [ 6rd-prefix ADDR ] [ 6rd-relay_prefix ADDR ] [ 6rd-reset ]\n"
  42                "        [ ttl TTL ] [ tos TOS ] [ [no]pmtudisc ] [ dev PHYS_DEV ]\n"
  43                "\n"
  44                "Where: NAME := STRING\n"
  45                "       ADDR := { IP_ADDRESS | any }\n"
  46                "       TOS  := { STRING | 00..ff | inherit | inherit/STRING | inherit/00..ff }\n"
  47                "       TTL  := { 1..255 | inherit }\n"
  48                "       KEY  := { DOTTED_QUAD | NUMBER }\n");
  49        exit(-1);
  50}
  51
  52static void set_tunnel_proto(struct ip_tunnel_parm *p, int proto)
  53{
  54        if (p->iph.protocol && p->iph.protocol != proto) {
  55                fprintf(stderr,
  56                        "You managed to ask for more than one tunnel mode.\n");
  57                exit(-1);
  58        }
  59        p->iph.protocol = proto;
  60}
  61
  62static int parse_args(int argc, char **argv, int cmd, struct ip_tunnel_parm *p)
  63{
  64        int count = 0;
  65        const char *medium = NULL;
  66        int isatap = 0;
  67
  68        memset(p, 0, sizeof(*p));
  69        p->iph.version = 4;
  70        p->iph.ihl = 5;
  71#ifndef IP_DF
  72#define IP_DF           0x4000          /* Flag: "Don't Fragment"       */
  73#endif
  74        p->iph.frag_off = htons(IP_DF);
  75
  76        while (argc > 0) {
  77                if (strcmp(*argv, "mode") == 0) {
  78                        NEXT_ARG();
  79                        if (strcmp(*argv, "ipip") == 0 ||
  80                            strcmp(*argv, "ip/ip") == 0) {
  81                                set_tunnel_proto(p, IPPROTO_IPIP);
  82                        } else if (strcmp(*argv, "gre") == 0 ||
  83                                   strcmp(*argv, "gre/ip") == 0) {
  84                                set_tunnel_proto(p, IPPROTO_GRE);
  85                        } else if (strcmp(*argv, "sit") == 0 ||
  86                                   strcmp(*argv, "ipv6/ip") == 0) {
  87                                set_tunnel_proto(p, IPPROTO_IPV6);
  88                        } else if (strcmp(*argv, "isatap") == 0) {
  89                                set_tunnel_proto(p, IPPROTO_IPV6);
  90                                isatap++;
  91                        } else if (strcmp(*argv, "vti") == 0) {
  92                                set_tunnel_proto(p, IPPROTO_IPIP);
  93                                p->i_flags |= VTI_ISVTI;
  94                        } else {
  95                                fprintf(stderr,
  96                                        "Unknown tunnel mode \"%s\"\n", *argv);
  97                                exit(-1);
  98                        }
  99                } else if (strcmp(*argv, "key") == 0) {
 100                        NEXT_ARG();
 101                        p->i_flags |= GRE_KEY;
 102                        p->o_flags |= GRE_KEY;
 103                        p->i_key = p->o_key = tnl_parse_key("key", *argv);
 104                } else if (strcmp(*argv, "ikey") == 0) {
 105                        NEXT_ARG();
 106                        p->i_flags |= GRE_KEY;
 107                        p->i_key = tnl_parse_key("ikey", *argv);
 108                } else if (strcmp(*argv, "okey") == 0) {
 109                        NEXT_ARG();
 110                        p->o_flags |= GRE_KEY;
 111                        p->o_key = tnl_parse_key("okey", *argv);
 112                } else if (strcmp(*argv, "seq") == 0) {
 113                        p->i_flags |= GRE_SEQ;
 114                        p->o_flags |= GRE_SEQ;
 115                } else if (strcmp(*argv, "iseq") == 0) {
 116                        p->i_flags |= GRE_SEQ;
 117                } else if (strcmp(*argv, "oseq") == 0) {
 118                        p->o_flags |= GRE_SEQ;
 119                } else if (strcmp(*argv, "csum") == 0) {
 120                        p->i_flags |= GRE_CSUM;
 121                        p->o_flags |= GRE_CSUM;
 122                } else if (strcmp(*argv, "icsum") == 0) {
 123                        p->i_flags |= GRE_CSUM;
 124                } else if (strcmp(*argv, "ocsum") == 0) {
 125                        p->o_flags |= GRE_CSUM;
 126                } else if (strcmp(*argv, "nopmtudisc") == 0) {
 127                        p->iph.frag_off = 0;
 128                } else if (strcmp(*argv, "pmtudisc") == 0) {
 129                        p->iph.frag_off = htons(IP_DF);
 130                } else if (strcmp(*argv, "remote") == 0) {
 131                        NEXT_ARG();
 132                        p->iph.daddr = get_addr32(*argv);
 133                } else if (strcmp(*argv, "local") == 0) {
 134                        NEXT_ARG();
 135                        p->iph.saddr = get_addr32(*argv);
 136                } else if (strcmp(*argv, "dev") == 0) {
 137                        NEXT_ARG();
 138                        medium = *argv;
 139                } else if (strcmp(*argv, "ttl") == 0 ||
 140                           strcmp(*argv, "hoplimit") == 0 ||
 141                           strcmp(*argv, "hlim") == 0) {
 142                        __u8 uval;
 143
 144                        NEXT_ARG();
 145                        if (strcmp(*argv, "inherit") != 0) {
 146                                if (get_u8(&uval, *argv, 0))
 147                                        invarg("invalid TTL\n", *argv);
 148                                p->iph.ttl = uval;
 149                        }
 150                } else if (strcmp(*argv, "tos") == 0 ||
 151                           strcmp(*argv, "tclass") == 0 ||
 152                           matches(*argv, "dsfield") == 0) {
 153                        char *dsfield;
 154                        __u32 uval;
 155
 156                        NEXT_ARG();
 157                        dsfield = *argv;
 158                        strsep(&dsfield, "/");
 159                        if (strcmp(*argv, "inherit") != 0) {
 160                                dsfield = *argv;
 161                                p->iph.tos = 0;
 162                        } else
 163                                p->iph.tos = 1;
 164                        if (dsfield) {
 165                                if (rtnl_dsfield_a2n(&uval, dsfield))
 166                                        invarg("bad TOS value", *argv);
 167                                p->iph.tos |= uval;
 168                        }
 169                } else {
 170                        if (strcmp(*argv, "name") == 0)
 171                                NEXT_ARG();
 172                        else if (matches(*argv, "help") == 0)
 173                                usage();
 174
 175                        if (p->name[0])
 176                                duparg2("name", *argv);
 177                        if (get_ifname(p->name, *argv))
 178                                invarg("\"name\" not a valid ifname", *argv);
 179                        if (cmd == SIOCCHGTUNNEL && count == 0) {
 180                                struct ip_tunnel_parm old_p = {};
 181
 182                                if (tnl_get_ioctl(*argv, &old_p))
 183                                        return -1;
 184                                *p = old_p;
 185                        }
 186                }
 187                count++;
 188                argc--; argv++;
 189        }
 190
 191
 192        if (p->iph.protocol == 0) {
 193                if (memcmp(p->name, "gre", 3) == 0)
 194                        p->iph.protocol = IPPROTO_GRE;
 195                else if (memcmp(p->name, "ipip", 4) == 0)
 196                        p->iph.protocol = IPPROTO_IPIP;
 197                else if (memcmp(p->name, "sit", 3) == 0)
 198                        p->iph.protocol = IPPROTO_IPV6;
 199                else if (memcmp(p->name, "isatap", 6) == 0) {
 200                        p->iph.protocol = IPPROTO_IPV6;
 201                        isatap++;
 202                } else if (memcmp(p->name, "vti", 3) == 0) {
 203                        p->iph.protocol = IPPROTO_IPIP;
 204                        p->i_flags |= VTI_ISVTI;
 205                }
 206        }
 207
 208        if ((p->i_flags & GRE_KEY) || (p->o_flags & GRE_KEY)) {
 209                if (!(p->i_flags & VTI_ISVTI) &&
 210                    (p->iph.protocol != IPPROTO_GRE)) {
 211                        fprintf(stderr, "Keys are not allowed with ipip and sit tunnels\n");
 212                        return -1;
 213                }
 214        }
 215
 216        if (medium) {
 217                p->link = ll_name_to_index(medium);
 218                if (!p->link)
 219                        return nodev(medium);
 220        }
 221
 222        if (p->i_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) {
 223                p->i_key = p->iph.daddr;
 224                p->i_flags |= GRE_KEY;
 225        }
 226        if (p->o_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) {
 227                p->o_key = p->iph.daddr;
 228                p->o_flags |= GRE_KEY;
 229        }
 230        if (IN_MULTICAST(ntohl(p->iph.daddr)) && !p->iph.saddr) {
 231                fprintf(stderr, "A broadcast tunnel requires a source address\n");
 232                return -1;
 233        }
 234        if (isatap)
 235                p->i_flags |= SIT_ISATAP;
 236
 237        return 0;
 238}
 239
 240static const char *tnl_defname(const struct ip_tunnel_parm *p)
 241{
 242        switch (p->iph.protocol) {
 243        case IPPROTO_IPIP:
 244                if (p->i_flags & VTI_ISVTI)
 245                        return "ip_vti0";
 246                else
 247                        return "tunl0";
 248        case IPPROTO_GRE:
 249                return "gre0";
 250        case IPPROTO_IPV6:
 251                return "sit0";
 252        }
 253        return NULL;
 254}
 255
 256static int do_add(int cmd, int argc, char **argv)
 257{
 258        struct ip_tunnel_parm p;
 259        const char *basedev;
 260
 261        if (parse_args(argc, argv, cmd, &p) < 0)
 262                return -1;
 263
 264        if (p.iph.ttl && p.iph.frag_off == 0) {
 265                fprintf(stderr, "ttl != 0 and nopmtudisc are incompatible\n");
 266                return -1;
 267        }
 268
 269        basedev = tnl_defname(&p);
 270        if (!basedev) {
 271                fprintf(stderr,
 272                        "cannot determine tunnel mode (ipip, gre, vti or sit)\n");
 273                return -1;
 274        }
 275
 276        return tnl_add_ioctl(cmd, basedev, p.name, &p);
 277}
 278
 279static int do_del(int argc, char **argv)
 280{
 281        struct ip_tunnel_parm p;
 282
 283        if (parse_args(argc, argv, SIOCDELTUNNEL, &p) < 0)
 284                return -1;
 285
 286        return tnl_del_ioctl(tnl_defname(&p) ? : p.name, p.name, &p);
 287}
 288
 289static void print_tunnel(const void *t)
 290{
 291        const struct ip_tunnel_parm *p = t;
 292        struct ip_tunnel_6rd ip6rd = {};
 293        SPRINT_BUF(b1);
 294
 295        /* Do not use format_host() for local addr,
 296         * symbolic name will not be useful.
 297         */
 298        open_json_object(NULL);
 299        print_color_string(PRINT_ANY, COLOR_IFNAME, "ifname", "%s: ", p->name);
 300        snprintf(b1, sizeof(b1), "%s/ip", tnl_strproto(p->iph.protocol));
 301        print_string(PRINT_ANY, "mode", "%s ", b1);
 302        print_null(PRINT_FP, NULL, "remote ", NULL);
 303        print_color_string(PRINT_ANY, COLOR_INET, "remote", "%s ",
 304                           p->iph.daddr || is_json_context()
 305                                ? format_host_r(AF_INET, 4, &p->iph.daddr, b1, sizeof(b1))
 306                                : "any");
 307        print_null(PRINT_FP, NULL, "local ", NULL);
 308        print_color_string(PRINT_ANY, COLOR_INET, "local", "%s",
 309                           p->iph.saddr || is_json_context()
 310                                ? rt_addr_n2a_r(AF_INET, 4, &p->iph.saddr, b1, sizeof(b1))
 311                                : "any");
 312
 313        if (p->iph.protocol == IPPROTO_IPV6 && (p->i_flags & SIT_ISATAP)) {
 314                struct ip_tunnel_prl prl[16] = {};
 315                int i;
 316
 317                prl[0].datalen = sizeof(prl) - sizeof(prl[0]);
 318                prl[0].addr = htonl(INADDR_ANY);
 319
 320                if (!tnl_prl_ioctl(SIOCGETPRL, p->name, prl)) {
 321                        for (i = 1; i < ARRAY_SIZE(prl); i++) {
 322                                if (prl[i].addr == htonl(INADDR_ANY))
 323                                        continue;
 324                                if (prl[i].flags & PRL_DEFAULT)
 325                                        print_string(PRINT_ANY, "pdr",
 326                                                     " pdr %s",
 327                                                     format_host(AF_INET, 4, &prl[i].addr));
 328                                else
 329                                        print_string(PRINT_ANY, "pr", " pr %s",
 330                                                     format_host(AF_INET, 4, &prl[i].addr));
 331                        }
 332                }
 333        }
 334
 335        if (p->link) {
 336                const char *n = ll_index_to_name(p->link);
 337
 338                if (n)
 339                        print_string(PRINT_ANY, "dev", " dev %s", n);
 340        }
 341
 342        if (p->iph.ttl)
 343                print_uint(PRINT_ANY, "ttl", " ttl %u", p->iph.ttl);
 344        else
 345                print_string(PRINT_FP, "ttl", " ttl %s", "inherit");
 346
 347        if (p->iph.tos) {
 348                SPRINT_BUF(b2);
 349
 350                if (p->iph.tos != 1) {
 351                        if (!is_json_context() && p->iph.tos & 1)
 352                                snprintf(b2, sizeof(b2), "%s%s",
 353                                         p->iph.tos & 1 ? "inherit/" : "",
 354                                         rtnl_dsfield_n2a(p->iph.tos & ~1, b1, sizeof(b1)));
 355                        else
 356                                snprintf(b2, sizeof(b2), "%s",
 357                                         rtnl_dsfield_n2a(p->iph.tos, b1, sizeof(b1)));
 358                        print_string(PRINT_ANY, "tos", " tos %s", b2);
 359                } else {
 360                        print_string(PRINT_FP, NULL, " tos %s", "inherit");
 361                }
 362        }
 363
 364        if (!(p->iph.frag_off & htons(IP_DF)))
 365                print_null(PRINT_ANY, "nopmtudisc", " nopmtudisc", NULL);
 366
 367        if (p->iph.protocol == IPPROTO_IPV6 && !tnl_ioctl_get_6rd(p->name, &ip6rd) && ip6rd.prefixlen) {
 368                print_string(PRINT_ANY, "6rd-prefix", " 6rd-prefix %s",
 369                             inet_ntop(AF_INET6, &ip6rd.prefix, b1, sizeof(b1)));
 370                print_uint(PRINT_ANY, "6rd-prefixlen", "/%u", ip6rd.prefixlen);
 371                if (ip6rd.relay_prefix) {
 372                        print_string(PRINT_ANY, "6rd-relay_prefix",
 373                                     " 6rd-relay_prefix %s",
 374                                     format_host(AF_INET, 4, &ip6rd.relay_prefix));
 375                        print_uint(PRINT_ANY, "6rd-relay_prefixlen", "/%u",
 376                                   ip6rd.relay_prefixlen);
 377                }
 378        }
 379
 380        tnl_print_gre_flags(p->iph.protocol, p->i_flags, p->o_flags,
 381                            p->i_key, p->o_key);
 382
 383        close_json_object();
 384}
 385
 386
 387static void ip_tunnel_parm_initialize(const struct tnl_print_nlmsg_info *info)
 388{
 389        struct ip_tunnel_parm *p2 = info->p2;
 390
 391        memset(p2, 0, sizeof(*p2));
 392}
 393
 394static bool ip_tunnel_parm_match(const struct tnl_print_nlmsg_info *info)
 395{
 396        const struct ip_tunnel_parm *p1 = info->p1;
 397        const struct ip_tunnel_parm *p2 = info->p2;
 398
 399        return ((!p1->link || p1->link == p2->link) &&
 400                (!p1->name[0] || strcmp(p1->name, p2->name) == 0) &&
 401                (!p1->iph.daddr || p1->iph.daddr == p2->iph.daddr) &&
 402                (!p1->iph.saddr || p1->iph.saddr == p2->iph.saddr) &&
 403                (!p1->i_key || p1->i_key == p2->i_key));
 404}
 405
 406static int do_show(int argc, char **argv)
 407{
 408        struct ip_tunnel_parm p, p1;
 409        const char *basedev;
 410
 411        if (parse_args(argc, argv, SIOCGETTUNNEL, &p) < 0)
 412                return -1;
 413
 414        basedev = tnl_defname(&p);
 415        if (!basedev) {
 416                struct tnl_print_nlmsg_info info = {
 417                        .p1    = &p,
 418                        .p2    = &p1,
 419                        .init  = ip_tunnel_parm_initialize,
 420                        .match = ip_tunnel_parm_match,
 421                        .print = print_tunnel,
 422                };
 423
 424                return do_tunnels_list(&info);
 425        }
 426
 427        if (tnl_get_ioctl(p.name[0] ? p.name : basedev, &p))
 428                return -1;
 429
 430        print_tunnel(&p);
 431        fputc('\n', stdout);
 432        return 0;
 433}
 434
 435static int do_prl(int argc, char **argv)
 436{
 437        struct ip_tunnel_prl p = {};
 438        int count = 0;
 439        int cmd = 0;
 440        const char *medium = NULL;
 441
 442        while (argc > 0) {
 443                if (strcmp(*argv, "prl-default") == 0) {
 444                        NEXT_ARG();
 445                        cmd = SIOCADDPRL;
 446                        p.addr = get_addr32(*argv);
 447                        p.flags |= PRL_DEFAULT;
 448                        count++;
 449                } else if (strcmp(*argv, "prl-nodefault") == 0) {
 450                        NEXT_ARG();
 451                        cmd = SIOCADDPRL;
 452                        p.addr = get_addr32(*argv);
 453                        count++;
 454                } else if (strcmp(*argv, "prl-delete") == 0) {
 455                        NEXT_ARG();
 456                        cmd = SIOCDELPRL;
 457                        p.addr = get_addr32(*argv);
 458                        count++;
 459                } else if (strcmp(*argv, "dev") == 0) {
 460                        NEXT_ARG();
 461                        if (check_ifname(*argv))
 462                                invarg("\"dev\" not a valid ifname", *argv);
 463                        medium = *argv;
 464                } else {
 465                        fprintf(stderr,
 466                                "Invalid PRL parameter \"%s\"\n", *argv);
 467                        exit(-1);
 468                }
 469                if (count > 1) {
 470                        fprintf(stderr,
 471                                "One PRL entry at a time\n");
 472                        exit(-1);
 473                }
 474                argc--; argv++;
 475        }
 476        if (!medium) {
 477                fprintf(stderr, "Must specify device\n");
 478                exit(-1);
 479        }
 480
 481        return tnl_prl_ioctl(cmd, medium, &p);
 482}
 483
 484static int do_6rd(int argc, char **argv)
 485{
 486        struct ip_tunnel_6rd ip6rd = {};
 487        int cmd = 0;
 488        const char *medium = NULL;
 489        inet_prefix prefix;
 490
 491        while (argc > 0) {
 492                if (strcmp(*argv, "6rd-prefix") == 0) {
 493                        NEXT_ARG();
 494                        if (get_prefix(&prefix, *argv, AF_INET6))
 495                                invarg("invalid 6rd_prefix\n", *argv);
 496                        cmd = SIOCADD6RD;
 497                        memcpy(&ip6rd.prefix, prefix.data, 16);
 498                        ip6rd.prefixlen = prefix.bitlen;
 499                } else if (strcmp(*argv, "6rd-relay_prefix") == 0) {
 500                        NEXT_ARG();
 501                        if (get_prefix(&prefix, *argv, AF_INET))
 502                                invarg("invalid 6rd-relay_prefix\n", *argv);
 503                        cmd = SIOCADD6RD;
 504                        memcpy(&ip6rd.relay_prefix, prefix.data, 4);
 505                        ip6rd.relay_prefixlen = prefix.bitlen;
 506                } else if (strcmp(*argv, "6rd-reset") == 0) {
 507                        cmd = SIOCDEL6RD;
 508                } else if (strcmp(*argv, "dev") == 0) {
 509                        NEXT_ARG();
 510                        if (check_ifname(*argv))
 511                                invarg("\"dev\" not a valid ifname", *argv);
 512                        medium = *argv;
 513                } else {
 514                        fprintf(stderr,
 515                                "Invalid 6RD parameter \"%s\"\n", *argv);
 516                        exit(-1);
 517                }
 518                argc--; argv++;
 519        }
 520        if (!medium) {
 521                fprintf(stderr, "Must specify device\n");
 522                exit(-1);
 523        }
 524
 525        return tnl_6rd_ioctl(cmd, medium, &ip6rd);
 526}
 527
 528static int tunnel_mode_is_ipv6(char *tunnel_mode)
 529{
 530        static const char * const ipv6_modes[] = {
 531                "ipv6/ipv6", "ip6ip6",
 532                "vti6",
 533                "ip/ipv6", "ipv4/ipv6", "ipip6", "ip4ip6",
 534                "ip6gre", "gre/ipv6",
 535                "any/ipv6", "any"
 536        };
 537        int i;
 538
 539        for (i = 0; i < ARRAY_SIZE(ipv6_modes); i++) {
 540                if (strcmp(ipv6_modes[i], tunnel_mode) == 0)
 541                        return 1;
 542        }
 543        return 0;
 544}
 545
 546int do_iptunnel(int argc, char **argv)
 547{
 548        int i;
 549
 550        for (i = 0; i < argc - 1; i++) {
 551                if (strcmp(argv[i], "mode") == 0) {
 552                        if (tunnel_mode_is_ipv6(argv[i + 1]))
 553                                preferred_family = AF_INET6;
 554                        break;
 555                }
 556        }
 557        switch (preferred_family) {
 558        case AF_UNSPEC:
 559                preferred_family = AF_INET;
 560                break;
 561        case AF_INET:
 562                break;
 563        /*
 564         * This is silly enough but we have no easy way to make it
 565         * protocol-independent because of unarranged structure between
 566         * IPv4 and IPv6.
 567         */
 568        case AF_INET6:
 569                return do_ip6tunnel(argc, argv);
 570        default:
 571                fprintf(stderr, "Unsupported protocol family: %d\n", preferred_family);
 572                exit(-1);
 573        }
 574
 575        if (argc > 0) {
 576                if (matches(*argv, "add") == 0)
 577                        return do_add(SIOCADDTUNNEL, argc - 1, argv + 1);
 578                if (matches(*argv, "change") == 0)
 579                        return do_add(SIOCCHGTUNNEL, argc - 1, argv + 1);
 580                if (matches(*argv, "delete") == 0)
 581                        return do_del(argc - 1, argv + 1);
 582                if (matches(*argv, "show") == 0 ||
 583                    matches(*argv, "lst") == 0 ||
 584                    matches(*argv, "list") == 0)
 585                        return do_show(argc - 1, argv + 1);
 586                if (matches(*argv, "prl") == 0)
 587                        return do_prl(argc - 1, argv + 1);
 588                if (matches(*argv, "6rd") == 0)
 589                        return do_6rd(argc - 1, argv + 1);
 590                if (matches(*argv, "help") == 0)
 591                        usage();
 592        } else
 593                return do_show(0, NULL);
 594
 595        fprintf(stderr, "Command \"%s\" is unknown, try \"ip tunnel help\"\n", *argv);
 596        exit(-1);
 597}
 598