busybox/networking/libiproute/iptunnel.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
   4 *
   5 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
   6 *
   7 * Changes:
   8 *
   9 * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
  10 * Rani Assaf <rani@magic.metawire.com> 980930: do not allow key for ipip/sit
  11 * Phil Karn <karn@ka9q.ampr.org>       990408: "pmtudisc" flag
  12 */
  13#include <netinet/ip.h>
  14#include <net/if.h>
  15#include <net/if_arp.h>
  16#include <asm/types.h>
  17
  18#ifndef __constant_htons
  19#define __constant_htons htons
  20#endif
  21
  22// FYI: #define SIOCDEVPRIVATE 0x89F0
  23
  24/* From linux/if_tunnel.h. #including it proved troublesome
  25 * (redefiniton errors due to name collisions in linux/ and net[inet]/) */
  26#define SIOCGETTUNNEL   (SIOCDEVPRIVATE + 0)
  27#define SIOCADDTUNNEL   (SIOCDEVPRIVATE + 1)
  28#define SIOCDELTUNNEL   (SIOCDEVPRIVATE + 2)
  29#define SIOCCHGTUNNEL   (SIOCDEVPRIVATE + 3)
  30//#define SIOCGETPRL      (SIOCDEVPRIVATE + 4)
  31//#define SIOCADDPRL      (SIOCDEVPRIVATE + 5)
  32//#define SIOCDELPRL      (SIOCDEVPRIVATE + 6)
  33//#define SIOCCHGPRL      (SIOCDEVPRIVATE + 7)
  34#define GRE_CSUM        __constant_htons(0x8000)
  35//#define GRE_ROUTING     __constant_htons(0x4000)
  36#define GRE_KEY         __constant_htons(0x2000)
  37#define GRE_SEQ         __constant_htons(0x1000)
  38//#define GRE_STRICT      __constant_htons(0x0800)
  39//#define GRE_REC         __constant_htons(0x0700)
  40//#define GRE_FLAGS       __constant_htons(0x00F8)
  41//#define GRE_VERSION     __constant_htons(0x0007)
  42struct ip_tunnel_parm {
  43        char            name[IFNAMSIZ];
  44        int             link;
  45        uint16_t        i_flags;
  46        uint16_t        o_flags;
  47        uint32_t        i_key;
  48        uint32_t        o_key;
  49        struct iphdr    iph;
  50};
  51/* SIT-mode i_flags */
  52//#define SIT_ISATAP 0x0001
  53//struct ip_tunnel_prl {
  54//      uint32_t          addr;
  55//      uint16_t          flags;
  56//      uint16_t          __reserved;
  57//      uint32_t          datalen;
  58//      uint32_t          __reserved2;
  59//      /* data follows */
  60//};
  61///* PRL flags */
  62//#define PRL_DEFAULT 0x0001
  63
  64#include "ip_common.h"  /* #include "libbb.h" is inside */
  65#include "rt_names.h"
  66#include "utils.h"
  67
  68
  69/* Dies on error */
  70static int do_ioctl_get_ifindex(char *dev)
  71{
  72        struct ifreq ifr;
  73        int fd;
  74
  75        strncpy_IFNAMSIZ(ifr.ifr_name, dev);
  76        fd = xsocket(AF_INET, SOCK_DGRAM, 0);
  77        xioctl(fd, SIOCGIFINDEX, &ifr);
  78        close(fd);
  79        return ifr.ifr_ifindex;
  80}
  81
  82static int do_ioctl_get_iftype(char *dev)
  83{
  84        struct ifreq ifr;
  85        int fd;
  86        int err;
  87
  88        strncpy_IFNAMSIZ(ifr.ifr_name, dev);
  89        fd = xsocket(AF_INET, SOCK_DGRAM, 0);
  90        err = ioctl_or_warn(fd, SIOCGIFHWADDR, &ifr);
  91        close(fd);
  92        return err ? -1 : ifr.ifr_addr.sa_family;
  93}
  94
  95static char *do_ioctl_get_ifname(int idx)
  96{
  97        struct ifreq ifr;
  98        int fd;
  99        int err;
 100
 101        ifr.ifr_ifindex = idx;
 102        fd = xsocket(AF_INET, SOCK_DGRAM, 0);
 103        err = ioctl_or_warn(fd, SIOCGIFNAME, &ifr);
 104        close(fd);
 105        return err ? NULL : xstrndup(ifr.ifr_name, sizeof(ifr.ifr_name));
 106}
 107
 108static int do_get_ioctl(const char *basedev, struct ip_tunnel_parm *p)
 109{
 110        struct ifreq ifr;
 111        int fd;
 112        int err;
 113
 114        strncpy_IFNAMSIZ(ifr.ifr_name, basedev);
 115        ifr.ifr_ifru.ifru_data = (void*)p;
 116        fd = xsocket(AF_INET, SOCK_DGRAM, 0);
 117        err = ioctl_or_warn(fd, SIOCGETTUNNEL, &ifr);
 118        close(fd);
 119        return err;
 120}
 121
 122/* Dies on error, otherwise returns 0 */
 123static int do_add_ioctl(int cmd, const char *basedev, struct ip_tunnel_parm *p)
 124{
 125        struct ifreq ifr;
 126        int fd;
 127
 128        if (cmd == SIOCCHGTUNNEL && p->name[0]) {
 129                strncpy_IFNAMSIZ(ifr.ifr_name, p->name);
 130        } else {
 131                strncpy_IFNAMSIZ(ifr.ifr_name, basedev);
 132        }
 133        ifr.ifr_ifru.ifru_data = (void*)p;
 134        fd = xsocket(AF_INET, SOCK_DGRAM, 0);
 135#if ENABLE_IOCTL_HEX2STR_ERROR
 136        /* #define magic will turn ioctl# into string */
 137        if (cmd == SIOCCHGTUNNEL)
 138                xioctl(fd, SIOCCHGTUNNEL, &ifr);
 139        else
 140                xioctl(fd, SIOCADDTUNNEL, &ifr);
 141#else
 142        xioctl(fd, cmd, &ifr);
 143#endif
 144        close(fd);
 145        return 0;
 146}
 147
 148/* Dies on error, otherwise returns 0 */
 149static int do_del_ioctl(const char *basedev, struct ip_tunnel_parm *p)
 150{
 151        struct ifreq ifr;
 152        int fd;
 153
 154        if (p->name[0]) {
 155                strncpy_IFNAMSIZ(ifr.ifr_name, p->name);
 156        } else {
 157                strncpy_IFNAMSIZ(ifr.ifr_name, basedev);
 158        }
 159        ifr.ifr_ifru.ifru_data = (void*)p;
 160        fd = xsocket(AF_INET, SOCK_DGRAM, 0);
 161        xioctl(fd, SIOCDELTUNNEL, &ifr);
 162        close(fd);
 163        return 0;
 164}
 165
 166/* Dies on error */
 167static void parse_args(char **argv, int cmd, struct ip_tunnel_parm *p)
 168{
 169        static const char keywords[] ALIGN1 =
 170                "mode\0""ipip\0""ip/ip\0""gre\0""gre/ip\0""sit\0""ipv6/ip\0"
 171                "key\0""ikey\0""okey\0""seq\0""iseq\0""oseq\0"
 172                "csum\0""icsum\0""ocsum\0""nopmtudisc\0""pmtudisc\0"
 173                "remote\0""any\0""local\0""dev\0"
 174                "ttl\0""inherit\0""tos\0""dsfield\0"
 175                "name\0";
 176        enum {
 177                ARG_mode, ARG_ipip, ARG_ip_ip, ARG_gre, ARG_gre_ip, ARG_sit, ARG_ip6_ip,
 178                ARG_key, ARG_ikey, ARG_okey, ARG_seq, ARG_iseq, ARG_oseq,
 179                ARG_csum, ARG_icsum, ARG_ocsum, ARG_nopmtudisc, ARG_pmtudisc,
 180                ARG_remote, ARG_any, ARG_local, ARG_dev,
 181                ARG_ttl, ARG_inherit, ARG_tos, ARG_dsfield,
 182                ARG_name
 183        };
 184        int count = 0;
 185        char medium[IFNAMSIZ];
 186        int key;
 187
 188        memset(p, 0, sizeof(*p));
 189        medium[0] = '\0';
 190
 191        p->iph.version = 4;
 192        p->iph.ihl = 5;
 193#ifndef IP_DF
 194#define IP_DF 0x4000  /* Flag: "Don't Fragment" */
 195#endif
 196        p->iph.frag_off = htons(IP_DF);
 197
 198        while (*argv) {
 199                key = index_in_strings(keywords, *argv);
 200                if (key == ARG_mode) {
 201                        NEXT_ARG();
 202                        key = index_in_strings(keywords, *argv);
 203                        if (key == ARG_ipip ||
 204                            key == ARG_ip_ip
 205                        ) {
 206                                if (p->iph.protocol && p->iph.protocol != IPPROTO_IPIP) {
 207                                        bb_error_msg_and_die("%s tunnel mode", "you managed to ask for more than one");
 208                                }
 209                                p->iph.protocol = IPPROTO_IPIP;
 210                        } else if (key == ARG_gre ||
 211                                   key == ARG_gre_ip
 212                        ) {
 213                                if (p->iph.protocol && p->iph.protocol != IPPROTO_GRE) {
 214                                        bb_error_msg_and_die("%s tunnel mode", "you managed to ask for more than one");
 215                                }
 216                                p->iph.protocol = IPPROTO_GRE;
 217                        } else if (key == ARG_sit ||
 218                                   key == ARG_ip6_ip
 219                        ) {
 220                                if (p->iph.protocol && p->iph.protocol != IPPROTO_IPV6) {
 221                                        bb_error_msg_and_die("%s tunnel mode", "you managed to ask for more than one");
 222                                }
 223                                p->iph.protocol = IPPROTO_IPV6;
 224                        } else {
 225                                bb_error_msg_and_die("%s tunnel mode", "can't guess");
 226                        }
 227                } else if (key == ARG_key) {
 228                        unsigned uval;
 229                        NEXT_ARG();
 230                        p->i_flags |= GRE_KEY;
 231                        p->o_flags |= GRE_KEY;
 232                        if (strchr(*argv, '.'))
 233                                p->i_key = p->o_key = get_addr32(*argv);
 234                        else {
 235                                uval = get_unsigned(*argv, "key");
 236                                p->i_key = p->o_key = htonl(uval);
 237                        }
 238                } else if (key == ARG_ikey) {
 239                        unsigned uval;
 240                        NEXT_ARG();
 241                        p->i_flags |= GRE_KEY;
 242                        if (strchr(*argv, '.'))
 243                                p->o_key = get_addr32(*argv);
 244                        else {
 245                                uval = get_unsigned(*argv, "ikey");
 246                                p->i_key = htonl(uval);
 247                        }
 248                } else if (key == ARG_okey) {
 249                        unsigned uval;
 250                        NEXT_ARG();
 251                        p->o_flags |= GRE_KEY;
 252                        if (strchr(*argv, '.'))
 253                                p->o_key = get_addr32(*argv);
 254                        else {
 255                                uval = get_unsigned(*argv, "okey");
 256                                p->o_key = htonl(uval);
 257                        }
 258                } else if (key == ARG_seq) {
 259                        p->i_flags |= GRE_SEQ;
 260                        p->o_flags |= GRE_SEQ;
 261                } else if (key == ARG_iseq) {
 262                        p->i_flags |= GRE_SEQ;
 263                } else if (key == ARG_oseq) {
 264                        p->o_flags |= GRE_SEQ;
 265                } else if (key == ARG_csum) {
 266                        p->i_flags |= GRE_CSUM;
 267                        p->o_flags |= GRE_CSUM;
 268                } else if (key == ARG_icsum) {
 269                        p->i_flags |= GRE_CSUM;
 270                } else if (key == ARG_ocsum) {
 271                        p->o_flags |= GRE_CSUM;
 272                } else if (key == ARG_nopmtudisc) {
 273                        p->iph.frag_off = 0;
 274                } else if (key == ARG_pmtudisc) {
 275                        p->iph.frag_off = htons(IP_DF);
 276                } else if (key == ARG_remote) {
 277                        NEXT_ARG();
 278                        key = index_in_strings(keywords, *argv);
 279                        if (key != ARG_any)
 280                                p->iph.daddr = get_addr32(*argv);
 281                } else if (key == ARG_local) {
 282                        NEXT_ARG();
 283                        key = index_in_strings(keywords, *argv);
 284                        if (key != ARG_any)
 285                                p->iph.saddr = get_addr32(*argv);
 286                } else if (key == ARG_dev) {
 287                        NEXT_ARG();
 288                        strncpy_IFNAMSIZ(medium, *argv);
 289                } else if (key == ARG_ttl) {
 290                        unsigned uval;
 291                        NEXT_ARG();
 292                        key = index_in_strings(keywords, *argv);
 293                        if (key != ARG_inherit) {
 294                                uval = get_unsigned(*argv, "TTL");
 295                                if (uval > 255)
 296                                        invarg_1_to_2(*argv, "TTL");
 297                                p->iph.ttl = uval;
 298                        }
 299                } else if (key == ARG_tos ||
 300                           key == ARG_dsfield
 301                ) {
 302                        uint32_t uval;
 303                        NEXT_ARG();
 304                        key = index_in_strings(keywords, *argv);
 305                        if (key != ARG_inherit) {
 306                                if (rtnl_dsfield_a2n(&uval, *argv))
 307                                        invarg_1_to_2(*argv, "TOS");
 308                                p->iph.tos = uval;
 309                        } else
 310                                p->iph.tos = 1;
 311                } else {
 312                        if (key == ARG_name) {
 313                                NEXT_ARG();
 314                        }
 315                        if (p->name[0])
 316                                duparg2("name", *argv);
 317                        strncpy_IFNAMSIZ(p->name, *argv);
 318                        if (cmd == SIOCCHGTUNNEL && count == 0) {
 319                                struct ip_tunnel_parm old_p;
 320                                memset(&old_p, 0, sizeof(old_p));
 321                                if (do_get_ioctl(*argv, &old_p))
 322                                        exit(EXIT_FAILURE);
 323                                *p = old_p;
 324                        }
 325                }
 326                count++;
 327                argv++;
 328        }
 329
 330        if (p->iph.protocol == 0) {
 331                if (memcmp(p->name, "gre", 3) == 0)
 332                        p->iph.protocol = IPPROTO_GRE;
 333                else if (memcmp(p->name, "ipip", 4) == 0)
 334                        p->iph.protocol = IPPROTO_IPIP;
 335                else if (memcmp(p->name, "sit", 3) == 0)
 336                        p->iph.protocol = IPPROTO_IPV6;
 337        }
 338
 339        if (p->iph.protocol == IPPROTO_IPIP || p->iph.protocol == IPPROTO_IPV6) {
 340                if ((p->i_flags & GRE_KEY) || (p->o_flags & GRE_KEY)) {
 341                        bb_error_msg_and_die("keys are not allowed with ipip and sit");
 342                }
 343        }
 344
 345        if (medium[0]) {
 346                p->link = do_ioctl_get_ifindex(medium);
 347        }
 348
 349        if (p->i_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) {
 350                p->i_key = p->iph.daddr;
 351                p->i_flags |= GRE_KEY;
 352        }
 353        if (p->o_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) {
 354                p->o_key = p->iph.daddr;
 355                p->o_flags |= GRE_KEY;
 356        }
 357        if (IN_MULTICAST(ntohl(p->iph.daddr)) && !p->iph.saddr) {
 358                bb_error_msg_and_die("broadcast tunnel requires a source address");
 359        }
 360}
 361
 362/* Return value becomes exitcode. It's okay to not return at all */
 363static int do_add(int cmd, char **argv)
 364{
 365        struct ip_tunnel_parm p;
 366
 367        parse_args(argv, cmd, &p);
 368
 369        if (p.iph.ttl && p.iph.frag_off == 0) {
 370                bb_error_msg_and_die("ttl != 0 and noptmudisc are incompatible");
 371        }
 372
 373        switch (p.iph.protocol) {
 374        case IPPROTO_IPIP:
 375                return do_add_ioctl(cmd, "tunl0", &p);
 376        case IPPROTO_GRE:
 377                return do_add_ioctl(cmd, "gre0", &p);
 378        case IPPROTO_IPV6:
 379                return do_add_ioctl(cmd, "sit0", &p);
 380        default:
 381                bb_error_msg_and_die("can't determine tunnel mode (ipip, gre or sit)");
 382        }
 383}
 384
 385/* Return value becomes exitcode. It's okay to not return at all */
 386static int do_del(char **argv)
 387{
 388        struct ip_tunnel_parm p;
 389
 390        parse_args(argv, SIOCDELTUNNEL, &p);
 391
 392        switch (p.iph.protocol) {
 393        case IPPROTO_IPIP:
 394                return do_del_ioctl("tunl0", &p);
 395        case IPPROTO_GRE:
 396                return do_del_ioctl("gre0", &p);
 397        case IPPROTO_IPV6:
 398                return do_del_ioctl("sit0", &p);
 399        default:
 400                return do_del_ioctl(p.name, &p);
 401        }
 402}
 403
 404static void print_tunnel(struct ip_tunnel_parm *p)
 405{
 406        char s3[INET_ADDRSTRLEN];
 407        char s4[INET_ADDRSTRLEN];
 408
 409        printf("%s: %s/ip  remote %s  local %s ",
 410                p->name,
 411                p->iph.protocol == IPPROTO_IPIP ? "ip" :
 412                        p->iph.protocol == IPPROTO_GRE ? "gre" :
 413                        p->iph.protocol == IPPROTO_IPV6 ? "ipv6" :
 414                        "unknown",
 415                p->iph.daddr ? format_host(AF_INET, 4, &p->iph.daddr) : "any",
 416                p->iph.saddr ? format_host(AF_INET, 4, &p->iph.saddr) : "any"
 417        );
 418        if (p->link) {
 419                char *n = do_ioctl_get_ifname(p->link);
 420                if (n) {
 421                        printf(" dev %s ", n);
 422                        free(n);
 423                }
 424        }
 425        if (p->iph.ttl)
 426                printf(" ttl %d ", p->iph.ttl);
 427        else
 428                printf(" ttl inherit ");
 429        if (p->iph.tos) {
 430                printf(" tos");
 431                if (p->iph.tos & 1)
 432                        printf(" inherit");
 433                if (p->iph.tos & ~1)
 434                        printf("%c%s ", p->iph.tos & 1 ? '/' : ' ',
 435                                rtnl_dsfield_n2a(p->iph.tos & ~1));
 436        }
 437        if (!(p->iph.frag_off & htons(IP_DF)))
 438                printf(" nopmtudisc");
 439
 440        inet_ntop(AF_INET, &p->i_key, s3, sizeof(s3));
 441        inet_ntop(AF_INET, &p->o_key, s4, sizeof(s4));
 442        if ((p->i_flags & GRE_KEY) && (p->o_flags & GRE_KEY) && p->o_key == p->i_key)
 443                printf(" key %s", s3);
 444        else {
 445                if (p->i_flags & GRE_KEY)
 446                        printf(" ikey %s ", s3);
 447                if (p->o_flags & GRE_KEY)
 448                        printf(" okey %s ", s4);
 449        }
 450
 451        if (p->i_flags & GRE_SEQ)
 452                printf("%c  Drop packets out of sequence.\n", _SL_);
 453        if (p->i_flags & GRE_CSUM)
 454                printf("%c  Checksum in received packet is required.", _SL_);
 455        if (p->o_flags & GRE_SEQ)
 456                printf("%c  Sequence packets on output.", _SL_);
 457        if (p->o_flags & GRE_CSUM)
 458                printf("%c  Checksum output packets.", _SL_);
 459}
 460
 461static void do_tunnels_list(struct ip_tunnel_parm *p)
 462{
 463        char name[IFNAMSIZ];
 464        unsigned long rx_bytes, rx_packets, rx_errs, rx_drops,
 465                rx_fifo, rx_frame,
 466                tx_bytes, tx_packets, tx_errs, tx_drops,
 467                tx_fifo, tx_colls, tx_carrier, rx_multi;
 468        int type;
 469        struct ip_tunnel_parm p1;
 470        char buf[512];
 471        FILE *fp = fopen_or_warn("/proc/net/dev", "r");
 472
 473        if (fp == NULL) {
 474                return;
 475        }
 476        /* skip headers */
 477        fgets(buf, sizeof(buf), fp);
 478        fgets(buf, sizeof(buf), fp);
 479
 480        while (fgets(buf, sizeof(buf), fp) != NULL) {
 481                char *ptr;
 482
 483                /*buf[sizeof(buf) - 1] = 0; - fgets is safe anyway */
 484                ptr = strchr(buf, ':');
 485                if (ptr == NULL ||
 486                    (*ptr++ = 0, sscanf(buf, "%s", name) != 1)
 487                ) {
 488                        bb_error_msg("wrong format of /proc/net/dev");
 489                        return;
 490                }
 491                if (sscanf(ptr, "%lu%lu%lu%lu%lu%lu%lu%*d%lu%lu%lu%lu%lu%lu%lu",
 492                           &rx_bytes, &rx_packets, &rx_errs, &rx_drops,
 493                           &rx_fifo, &rx_frame, &rx_multi,
 494                           &tx_bytes, &tx_packets, &tx_errs, &tx_drops,
 495                           &tx_fifo, &tx_colls, &tx_carrier) != 14)
 496                        continue;
 497                if (p->name[0] && strcmp(p->name, name))
 498                        continue;
 499                type = do_ioctl_get_iftype(name);
 500                if (type == -1) {
 501                        bb_error_msg("can't get type of [%s]", name);
 502                        continue;
 503                }
 504                if (type != ARPHRD_TUNNEL && type != ARPHRD_IPGRE && type != ARPHRD_SIT)
 505                        continue;
 506                memset(&p1, 0, sizeof(p1));
 507                if (do_get_ioctl(name, &p1))
 508                        continue;
 509                if ((p->link && p1.link != p->link) ||
 510                    (p->name[0] && strcmp(p1.name, p->name)) ||
 511                    (p->iph.daddr && p1.iph.daddr != p->iph.daddr) ||
 512                    (p->iph.saddr && p1.iph.saddr != p->iph.saddr) ||
 513                    (p->i_key && p1.i_key != p->i_key)
 514                ) {
 515                        continue;
 516                }
 517                print_tunnel(&p1);
 518                bb_putchar('\n');
 519        }
 520}
 521
 522/* Return value becomes exitcode. It's okay to not return at all */
 523static int do_show(char **argv)
 524{
 525        int err;
 526        struct ip_tunnel_parm p;
 527
 528        parse_args(argv, SIOCGETTUNNEL, &p);
 529
 530        switch (p.iph.protocol) {
 531        case IPPROTO_IPIP:
 532                err = do_get_ioctl(p.name[0] ? p.name : "tunl0", &p);
 533                break;
 534        case IPPROTO_GRE:
 535                err = do_get_ioctl(p.name[0] ? p.name : "gre0", &p);
 536                break;
 537        case IPPROTO_IPV6:
 538                err = do_get_ioctl(p.name[0] ? p.name : "sit0", &p);
 539                break;
 540        default:
 541                do_tunnels_list(&p);
 542                return 0;
 543        }
 544        if (err)
 545                return -1;
 546
 547        print_tunnel(&p);
 548        bb_putchar('\n');
 549        return 0;
 550}
 551
 552/* Return value becomes exitcode. It's okay to not return at all */
 553int FAST_FUNC do_iptunnel(char **argv)
 554{
 555        static const char keywords[] ALIGN1 =
 556                "add\0""change\0""delete\0""show\0""list\0""lst\0";
 557        enum { ARG_add = 0, ARG_change, ARG_del, ARG_show, ARG_list, ARG_lst };
 558
 559        if (*argv) {
 560                int key = index_in_substrings(keywords, *argv);
 561                if (key < 0)
 562                        invarg_1_to_2(*argv, applet_name);
 563                argv++;
 564                if (key == ARG_add)
 565                        return do_add(SIOCADDTUNNEL, argv);
 566                if (key == ARG_change)
 567                        return do_add(SIOCCHGTUNNEL, argv);
 568                if (key == ARG_del)
 569                        return do_del(argv);
 570        }
 571        return do_show(argv);
 572}
 573