iproute2/ip/iptuntap.c
<<
>>
Prefs
   1/*
   2 * iptunnel.c          "ip tuntap"
   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:     David Woodhouse <David.Woodhouse@intel.com>
  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 <linux/if.h>
  22#include <linux/if_tun.h>
  23#include <linux/if_arp.h>
  24#include <pwd.h>
  25#include <grp.h>
  26#include <fcntl.h>
  27#include <dirent.h>
  28#include <errno.h>
  29#include <glob.h>
  30
  31#include "rt_names.h"
  32#include "utils.h"
  33#include "ip_common.h"
  34
  35static const char drv_name[] = "tun";
  36
  37#define TUNDEV "/dev/net/tun"
  38
  39static void usage(void) __attribute__((noreturn));
  40
  41static void usage(void)
  42{
  43        fprintf(stderr,
  44                "Usage: ip tuntap { add | del | show | list | lst | help } [ dev PHYS_DEV ]\n"
  45                "       [ mode { tun | tap } ] [ user USER ] [ group GROUP ]\n"
  46                "       [ one_queue ] [ pi ] [ vnet_hdr ] [ multi_queue ] [ name NAME ]\n"
  47                "\n"
  48                "Where: USER  := { STRING | NUMBER }\n"
  49                "       GROUP := { STRING | NUMBER }\n");
  50        exit(-1);
  51}
  52
  53static int tap_add_ioctl(struct ifreq *ifr, uid_t uid, gid_t gid)
  54{
  55        int fd;
  56        int ret = -1;
  57
  58#ifdef IFF_TUN_EXCL
  59        ifr->ifr_flags |= IFF_TUN_EXCL;
  60#endif
  61
  62        fd = open(TUNDEV, O_RDWR);
  63        if (fd < 0) {
  64                perror("open");
  65                return -1;
  66        }
  67        if (ioctl(fd, TUNSETIFF, ifr)) {
  68                perror("ioctl(TUNSETIFF)");
  69                goto out;
  70        }
  71        if (uid != -1 && ioctl(fd, TUNSETOWNER, uid)) {
  72                perror("ioctl(TUNSETOWNER)");
  73                goto out;
  74        }
  75        if (gid != -1 && ioctl(fd, TUNSETGROUP, gid)) {
  76                perror("ioctl(TUNSETGROUP)");
  77                goto out;
  78        }
  79        if (ioctl(fd, TUNSETPERSIST, 1)) {
  80                perror("ioctl(TUNSETPERSIST)");
  81                goto out;
  82        }
  83        ret = 0;
  84 out:
  85        close(fd);
  86        return ret;
  87}
  88
  89static int tap_del_ioctl(struct ifreq *ifr)
  90{
  91        int fd = open(TUNDEV, O_RDWR);
  92        int ret = -1;
  93
  94        if (fd < 0) {
  95                perror("open");
  96                return -1;
  97        }
  98        if (ioctl(fd, TUNSETIFF, ifr)) {
  99                perror("ioctl(TUNSETIFF)");
 100                goto out;
 101        }
 102        if (ioctl(fd, TUNSETPERSIST, 0)) {
 103                perror("ioctl(TUNSETPERSIST)");
 104                goto out;
 105        }
 106        ret = 0;
 107 out:
 108        close(fd);
 109        return ret;
 110
 111}
 112static int parse_args(int argc, char **argv,
 113                      struct ifreq *ifr, uid_t *uid, gid_t *gid)
 114{
 115        int count = 0;
 116
 117        memset(ifr, 0, sizeof(*ifr));
 118
 119        ifr->ifr_flags |= IFF_NO_PI;
 120
 121        while (argc > 0) {
 122                if (matches(*argv, "mode") == 0) {
 123                        NEXT_ARG();
 124                        if (matches(*argv, "tun") == 0) {
 125                                if (ifr->ifr_flags & IFF_TAP) {
 126                                        fprintf(stderr, "You managed to ask for more than one tunnel mode.\n");
 127                                        exit(-1);
 128                                }
 129                                ifr->ifr_flags |= IFF_TUN;
 130                        } else if (matches(*argv, "tap") == 0) {
 131                                if (ifr->ifr_flags & IFF_TUN) {
 132                                        fprintf(stderr, "You managed to ask for more than one tunnel mode.\n");
 133                                        exit(-1);
 134                                }
 135                                ifr->ifr_flags |= IFF_TAP;
 136                        } else {
 137                                fprintf(stderr, "Unknown tunnel mode \"%s\"\n", *argv);
 138                                exit(-1);
 139                        }
 140                } else if (uid && matches(*argv, "user") == 0) {
 141                        char *end;
 142                        unsigned long user;
 143
 144                        NEXT_ARG();
 145                        if (**argv && ((user = strtol(*argv, &end, 10)), !*end))
 146                                *uid = user;
 147                        else {
 148                                struct passwd *pw = getpwnam(*argv);
 149
 150                                if (!pw) {
 151                                        fprintf(stderr, "invalid user \"%s\"\n", *argv);
 152                                        exit(-1);
 153                                }
 154                                *uid = pw->pw_uid;
 155                        }
 156                } else if (gid && matches(*argv, "group") == 0) {
 157                        char *end;
 158                        unsigned long group;
 159
 160                        NEXT_ARG();
 161
 162                        if (**argv && ((group = strtol(*argv, &end, 10)), !*end))
 163                                *gid = group;
 164                        else {
 165                                struct group *gr = getgrnam(*argv);
 166
 167                                if (!gr) {
 168                                        fprintf(stderr, "invalid group \"%s\"\n", *argv);
 169                                        exit(-1);
 170                                }
 171                                *gid = gr->gr_gid;
 172                        }
 173                } else if (matches(*argv, "pi") == 0) {
 174                        ifr->ifr_flags &= ~IFF_NO_PI;
 175                } else if (matches(*argv, "one_queue") == 0) {
 176                        ifr->ifr_flags |= IFF_ONE_QUEUE;
 177                } else if (matches(*argv, "vnet_hdr") == 0) {
 178                        ifr->ifr_flags |= IFF_VNET_HDR;
 179                } else if (matches(*argv, "multi_queue") == 0) {
 180                        ifr->ifr_flags |= IFF_MULTI_QUEUE;
 181                } else if (matches(*argv, "dev") == 0) {
 182                        NEXT_ARG();
 183                        if (get_ifname(ifr->ifr_name, *argv))
 184                                invarg("\"dev\" not a valid ifname", *argv);
 185                } else {
 186                        if (matches(*argv, "name") == 0) {
 187                                NEXT_ARG();
 188                        } else if (matches(*argv, "help") == 0)
 189                                usage();
 190                        if (ifr->ifr_name[0])
 191                                duparg2("name", *argv);
 192                        if (get_ifname(ifr->ifr_name, *argv))
 193                                invarg("\"name\" not a valid ifname", *argv);
 194                }
 195                count++;
 196                argc--; argv++;
 197        }
 198
 199        if (!(ifr->ifr_flags & TUN_TYPE_MASK)) {
 200                fprintf(stderr, "You failed to specify a tunnel mode\n");
 201                return -1;
 202        }
 203
 204        return 0;
 205}
 206
 207
 208static int do_add(int argc, char **argv)
 209{
 210        struct ifreq ifr;
 211        uid_t uid = -1;
 212        gid_t gid = -1;
 213
 214        if (parse_args(argc, argv, &ifr, &uid, &gid) < 0)
 215                return -1;
 216
 217        return tap_add_ioctl(&ifr, uid, gid);
 218}
 219
 220static int do_del(int argc, char **argv)
 221{
 222        struct ifreq ifr;
 223
 224        if (parse_args(argc, argv, &ifr, NULL, NULL) < 0)
 225                return -1;
 226
 227        return tap_del_ioctl(&ifr);
 228}
 229
 230static void print_flags(long flags)
 231{
 232        open_json_array(PRINT_JSON, "flags");
 233
 234        if (flags & IFF_TUN)
 235                print_string(PRINT_ANY, NULL, " %s", "tun");
 236
 237        if (flags & IFF_TAP)
 238                print_string(PRINT_ANY, NULL, " %s", "tap");
 239
 240        if (!(flags & IFF_NO_PI))
 241                print_string(PRINT_ANY, NULL, " %s", "pi");
 242
 243        if (flags & IFF_ONE_QUEUE)
 244                print_string(PRINT_ANY, NULL, " %s", "one_queue");
 245
 246        if (flags & IFF_MULTI_QUEUE)
 247                print_string(PRINT_ANY, NULL, " %s", "multi_queue");
 248
 249        if (flags & IFF_VNET_HDR)
 250                print_string(PRINT_ANY, NULL, " %s", "vnet_hdr");
 251
 252        if (flags & IFF_PERSIST)
 253                print_string(PRINT_ANY, NULL, " %s", "persist");
 254
 255        if (!(flags & IFF_NOFILTER))
 256                print_string(PRINT_ANY, NULL, " %s", "filter");
 257
 258        flags &= ~(IFF_TUN | IFF_TAP | IFF_NO_PI | IFF_ONE_QUEUE |
 259                   IFF_MULTI_QUEUE | IFF_VNET_HDR | IFF_PERSIST |
 260                   IFF_NOFILTER);
 261        if (flags)
 262                print_0xhex(PRINT_ANY, NULL, " %#llx", flags);
 263
 264        close_json_array(PRINT_JSON, NULL);
 265}
 266
 267static void show_processes(const char *name)
 268{
 269        glob_t globbuf = { };
 270        char **fd_path;
 271        int err;
 272
 273        err = glob("/proc/[0-9]*/fd/[0-9]*", GLOB_NOSORT,
 274                   NULL, &globbuf);
 275        if (err)
 276                return;
 277
 278        open_json_array(PRINT_JSON, "processes");
 279
 280        fd_path = globbuf.gl_pathv;
 281        while (*fd_path) {
 282                const char *dev_net_tun = "/dev/net/tun";
 283                const size_t linkbuf_len = strlen(dev_net_tun) + 2;
 284                char linkbuf[linkbuf_len], *fdinfo;
 285                int pid, fd;
 286                FILE *f;
 287
 288                if (sscanf(*fd_path, "/proc/%d/fd/%d", &pid, &fd) != 2)
 289                        goto next;
 290
 291                if (pid == getpid())
 292                        goto next;
 293
 294                err = readlink(*fd_path, linkbuf, linkbuf_len - 1);
 295                if (err < 0) {
 296                        perror("readlink");
 297                        goto next;
 298                }
 299                linkbuf[err] = '\0';
 300                if (strcmp(dev_net_tun, linkbuf))
 301                        goto next;
 302
 303                if (asprintf(&fdinfo, "/proc/%d/fdinfo/%d", pid, fd) < 0)
 304                        goto next;
 305
 306                f = fopen(fdinfo, "r");
 307                free(fdinfo);
 308                if (!f) {
 309                        perror("fopen");
 310                        goto next;
 311                }
 312
 313                while (!feof(f)) {
 314                        char *key = NULL, *value = NULL;
 315
 316                        err = fscanf(f, "%m[^:]: %ms\n", &key, &value);
 317                        if (err == EOF) {
 318                                if (ferror(f))
 319                                        perror("fscanf");
 320                                break;
 321                        } else if (err == 2 &&
 322                                   !strcmp("iff", key) &&
 323                                   !strcmp(name, value)) {
 324                                char *pname = get_task_name(pid);
 325
 326                                print_string(PRINT_ANY, "name",
 327                                             "%s", pname ? : "<NULL>");
 328
 329                                print_uint(PRINT_ANY, "pid",
 330                                           "(%d)", pid);
 331                                free(pname);
 332                        }
 333
 334                        free(key);
 335                        free(value);
 336                }
 337                if (fclose(f))
 338                        perror("fclose");
 339
 340next:
 341                ++fd_path;
 342        }
 343        close_json_array(PRINT_JSON, NULL);
 344
 345        globfree(&globbuf);
 346}
 347
 348static int tuntap_filter_req(struct nlmsghdr *nlh, int reqlen)
 349{
 350        struct rtattr *linkinfo;
 351        int err;
 352
 353        linkinfo = addattr_nest(nlh, reqlen, IFLA_LINKINFO);
 354
 355        err = addattr_l(nlh, reqlen, IFLA_INFO_KIND,
 356                        drv_name, sizeof(drv_name) - 1);
 357        if (err)
 358                return err;
 359
 360        addattr_nest_end(nlh, linkinfo);
 361
 362        return 0;
 363}
 364
 365static int print_tuntap(struct nlmsghdr *n, void *arg)
 366{
 367        struct ifinfomsg *ifi = NLMSG_DATA(n);
 368        struct rtattr *tb[IFLA_MAX+1];
 369        struct rtattr *linkinfo[IFLA_INFO_MAX+1];
 370        const char *name, *kind;
 371        long flags, owner = -1, group = -1;
 372
 373        if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK)
 374                return 0;
 375
 376        if (n->nlmsg_len < NLMSG_LENGTH(sizeof(*ifi)))
 377                return -1;
 378
 379        switch (ifi->ifi_type) {
 380        case ARPHRD_NONE:
 381        case ARPHRD_ETHER:
 382                break;
 383        default:
 384                return 0;
 385        }
 386
 387        parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n));
 388
 389        if (!tb[IFLA_IFNAME])
 390                return 0;
 391
 392        if (!tb[IFLA_LINKINFO])
 393                return 0;
 394
 395        parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
 396
 397        if (!linkinfo[IFLA_INFO_KIND])
 398                return 0;
 399
 400        kind = rta_getattr_str(linkinfo[IFLA_INFO_KIND]);
 401        if (strcmp(kind, drv_name))
 402                return 0;
 403
 404        name = rta_getattr_str(tb[IFLA_IFNAME]);
 405
 406        if (read_prop(name, "tun_flags", &flags))
 407                return 0;
 408        if (read_prop(name, "owner", &owner))
 409                return 0;
 410        if (read_prop(name, "group", &group))
 411                return 0;
 412
 413        open_json_object(NULL);
 414        print_color_string(PRINT_ANY, COLOR_IFNAME,
 415                           "ifname", "%s:", name);
 416        print_flags(flags);
 417        if (owner != -1)
 418                print_u64(PRINT_ANY, "user",
 419                           " user %ld", owner);
 420        if (group != -1)
 421                print_u64(PRINT_ANY, "group",
 422                           " group %ld", group);
 423
 424        if (show_details) {
 425                print_string(PRINT_FP, NULL,
 426                             "%s\tAttached to processes:", _SL_);
 427                show_processes(name);
 428        }
 429        close_json_object();
 430        print_string(PRINT_FP, NULL, "%s", "\n");
 431
 432        return 0;
 433}
 434
 435static int do_show(int argc, char **argv)
 436{
 437        if (rtnl_linkdump_req_filter_fn(&rth, AF_UNSPEC,
 438                                        tuntap_filter_req) < 0) {
 439                perror("Cannot send dump request\n");
 440                return -1;
 441        }
 442
 443        new_json_obj(json);
 444
 445        if (rtnl_dump_filter(&rth, print_tuntap, NULL) < 0) {
 446                fprintf(stderr, "Dump terminated\n");
 447                return -1;
 448        }
 449
 450        delete_json_obj();
 451        fflush(stdout);
 452
 453        return 0;
 454}
 455
 456int do_iptuntap(int argc, char **argv)
 457{
 458        if (argc > 0) {
 459                if (matches(*argv, "add") == 0)
 460                        return do_add(argc-1, argv+1);
 461                if (matches(*argv, "delete") == 0)
 462                        return do_del(argc-1, argv+1);
 463                if (matches(*argv, "show") == 0 ||
 464                    matches(*argv, "lst") == 0 ||
 465                    matches(*argv, "list") == 0)
 466                        return do_show(argc-1, argv+1);
 467                if (matches(*argv, "help") == 0)
 468                        usage();
 469        } else
 470                return do_show(0, NULL);
 471
 472        fprintf(stderr, "Command \"%s\" is unknown, try \"ip tuntap help\".\n",
 473                *argv);
 474        exit(-1);
 475}
 476
 477static void print_owner(FILE *f, uid_t uid)
 478{
 479        struct passwd *pw = getpwuid(uid);
 480
 481        if (pw)
 482                print_string(PRINT_ANY, "user", "user %s ", pw->pw_name);
 483        else
 484                print_uint(PRINT_ANY, "user", "user %u ", uid);
 485}
 486
 487static void print_group(FILE *f, gid_t gid)
 488{
 489        struct group *group = getgrgid(gid);
 490
 491        if (group)
 492                print_string(PRINT_ANY, "group", "group %s ", group->gr_name);
 493        else
 494                print_uint(PRINT_ANY, "group", "group %u ", gid);
 495}
 496
 497static void print_mq(FILE *f, struct rtattr *tb[])
 498{
 499        if (!tb[IFLA_TUN_MULTI_QUEUE] ||
 500            !rta_getattr_u8(tb[IFLA_TUN_MULTI_QUEUE])) {
 501                if (is_json_context())
 502                        print_bool(PRINT_JSON, "multi_queue", NULL, false);
 503                return;
 504        }
 505
 506        print_bool(PRINT_ANY, "multi_queue", "multi_queue ", true);
 507
 508        if (tb[IFLA_TUN_NUM_QUEUES]) {
 509                print_uint(PRINT_ANY, "numqueues", "numqueues %u ",
 510                           rta_getattr_u32(tb[IFLA_TUN_NUM_QUEUES]));
 511        }
 512
 513        if (tb[IFLA_TUN_NUM_DISABLED_QUEUES]) {
 514                print_uint(PRINT_ANY, "numdisabled", "numdisabled %u ",
 515                           rta_getattr_u32(tb[IFLA_TUN_NUM_DISABLED_QUEUES]));
 516        }
 517}
 518
 519static void print_type(FILE *f, __u8 type)
 520{
 521        SPRINT_BUF(buf);
 522        const char *str = buf;
 523
 524        if (type == IFF_TUN)
 525                str = "tun";
 526        else if (type == IFF_TAP)
 527                str = "tap";
 528        else
 529                snprintf(buf, sizeof(buf), "UNKNOWN:%hhu", type);
 530
 531        print_string(PRINT_ANY, "type", "type %s ", str);
 532}
 533
 534static void tun_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
 535{
 536        if (!tb)
 537                return;
 538
 539        if (tb[IFLA_TUN_TYPE])
 540                print_type(f, rta_getattr_u8(tb[IFLA_TUN_TYPE]));
 541
 542        if (tb[IFLA_TUN_PI])
 543                print_on_off(PRINT_ANY, "pi", "pi %s ",
 544                             rta_getattr_u8(tb[IFLA_TUN_PI]));
 545
 546        if (tb[IFLA_TUN_VNET_HDR]) {
 547                print_on_off(PRINT_ANY, "vnet_hdr", "vnet_hdr %s ",
 548                             rta_getattr_u8(tb[IFLA_TUN_VNET_HDR]));
 549        }
 550
 551        print_mq(f, tb);
 552
 553        if (tb[IFLA_TUN_PERSIST])
 554                print_on_off(PRINT_ANY, "persist", "persist %s ",
 555                             rta_getattr_u8(tb[IFLA_TUN_PERSIST]));
 556
 557        if (tb[IFLA_TUN_OWNER])
 558                print_owner(f, rta_getattr_u32(tb[IFLA_TUN_OWNER]));
 559
 560        if (tb[IFLA_TUN_GROUP])
 561                print_group(f, rta_getattr_u32(tb[IFLA_TUN_GROUP]));
 562}
 563
 564struct link_util tun_link_util = {
 565        .id = "tun",
 566        .maxattr = IFLA_TUN_MAX,
 567        .print_opt = tun_print_opt,
 568};
 569