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