2009-09-20 00:48:43 +08:00
|
|
|
/*
|
|
|
|
* iptunnel.c "ip tuntap"
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version
|
|
|
|
* 2 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* Authors: David Woodhouse <David.Woodhouse@intel.com>
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <linux/if.h>
|
|
|
|
#include <linux/if_tun.h>
|
2018-02-07 14:30:56 +08:00
|
|
|
#include <linux/if_arp.h>
|
2009-09-20 00:48:43 +08:00
|
|
|
#include <pwd.h>
|
|
|
|
#include <grp.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <dirent.h>
|
|
|
|
#include <errno.h>
|
2016-08-25 02:08:25 +08:00
|
|
|
#include <glob.h>
|
2009-09-20 00:48:43 +08:00
|
|
|
|
|
|
|
#include "rt_names.h"
|
|
|
|
#include "utils.h"
|
|
|
|
#include "ip_common.h"
|
|
|
|
|
2018-02-07 14:30:56 +08:00
|
|
|
static const char drv_name[] = "tun";
|
|
|
|
|
2009-09-20 00:48:43 +08:00
|
|
|
#define TUNDEV "/dev/net/tun"
|
|
|
|
|
|
|
|
static void usage(void) __attribute__((noreturn));
|
|
|
|
|
|
|
|
static void usage(void)
|
|
|
|
{
|
treewide: refactor help messages
Every tool in the iproute2 package have one or more function to show
an help message to the user. Some of these functions print the help
line by line with a series of printf call, e.g. ip/xfrm_state.c does
60 fprintf calls.
If we group all the calls to a single one and just concatenate strings,
we save a lot of libc calls and thus object size. The size difference
of the compiled binaries calculated with bloat-o-meter is:
ip/ip:
add/remove: 0/0 grow/shrink: 5/15 up/down: 103/-4796 (-4693)
Total: Before=672591, After=667898, chg -0.70%
ip/rtmon:
add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-54 (-54)
Total: Before=48879, After=48825, chg -0.11%
tc/tc:
add/remove: 0/2 grow/shrink: 31/10 up/down: 882/-6133 (-5251)
Total: Before=351912, After=346661, chg -1.49%
bridge/bridge:
add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-459 (-459)
Total: Before=70502, After=70043, chg -0.65%
misc/lnstat:
add/remove: 0/1 grow/shrink: 1/0 up/down: 48/-486 (-438)
Total: Before=9960, After=9522, chg -4.40%
tipc/tipc:
add/remove: 0/0 grow/shrink: 1/1 up/down: 18/-62 (-44)
Total: Before=79182, After=79138, chg -0.06%
While at it, indent some strings which were starting at column 0,
and use tabs where possible, to have a consistent style across helps.
Signed-off-by: Matteo Croce <mcroce@redhat.com>
Signed-off-by: David Ahern <dsahern@gmail.com>
2019-05-17 21:38:28 +08:00
|
|
|
fprintf(stderr,
|
|
|
|
"Usage: ip tuntap { add | del | show | list | lst | help } [ dev PHYS_DEV ]\n"
|
2021-10-05 21:19:39 +08:00
|
|
|
" [ mode { tun | tap } ] [ user USER ] [ group GROUP ]\n"
|
|
|
|
" [ one_queue ] [ pi ] [ vnet_hdr ] [ multi_queue ] [ name NAME ]\n"
|
treewide: refactor help messages
Every tool in the iproute2 package have one or more function to show
an help message to the user. Some of these functions print the help
line by line with a series of printf call, e.g. ip/xfrm_state.c does
60 fprintf calls.
If we group all the calls to a single one and just concatenate strings,
we save a lot of libc calls and thus object size. The size difference
of the compiled binaries calculated with bloat-o-meter is:
ip/ip:
add/remove: 0/0 grow/shrink: 5/15 up/down: 103/-4796 (-4693)
Total: Before=672591, After=667898, chg -0.70%
ip/rtmon:
add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-54 (-54)
Total: Before=48879, After=48825, chg -0.11%
tc/tc:
add/remove: 0/2 grow/shrink: 31/10 up/down: 882/-6133 (-5251)
Total: Before=351912, After=346661, chg -1.49%
bridge/bridge:
add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-459 (-459)
Total: Before=70502, After=70043, chg -0.65%
misc/lnstat:
add/remove: 0/1 grow/shrink: 1/0 up/down: 48/-486 (-438)
Total: Before=9960, After=9522, chg -4.40%
tipc/tipc:
add/remove: 0/0 grow/shrink: 1/1 up/down: 18/-62 (-44)
Total: Before=79182, After=79138, chg -0.06%
While at it, indent some strings which were starting at column 0,
and use tabs where possible, to have a consistent style across helps.
Signed-off-by: Matteo Croce <mcroce@redhat.com>
Signed-off-by: David Ahern <dsahern@gmail.com>
2019-05-17 21:38:28 +08:00
|
|
|
"\n"
|
2021-10-05 21:19:39 +08:00
|
|
|
"Where: USER := { STRING | NUMBER }\n"
|
|
|
|
" GROUP := { STRING | NUMBER }\n");
|
2009-09-20 00:48:43 +08:00
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int tap_add_ioctl(struct ifreq *ifr, uid_t uid, gid_t gid)
|
|
|
|
{
|
2011-09-01 03:14:51 +08:00
|
|
|
int fd;
|
2009-09-20 00:48:43 +08:00
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
#ifdef IFF_TUN_EXCL
|
|
|
|
ifr->ifr_flags |= IFF_TUN_EXCL;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
fd = open(TUNDEV, O_RDWR);
|
|
|
|
if (fd < 0) {
|
|
|
|
perror("open");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (ioctl(fd, TUNSETIFF, ifr)) {
|
|
|
|
perror("ioctl(TUNSETIFF)");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (uid != -1 && ioctl(fd, TUNSETOWNER, uid)) {
|
|
|
|
perror("ioctl(TUNSETOWNER)");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (gid != -1 && ioctl(fd, TUNSETGROUP, gid)) {
|
|
|
|
perror("ioctl(TUNSETGROUP)");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (ioctl(fd, TUNSETPERSIST, 1)) {
|
|
|
|
perror("ioctl(TUNSETPERSIST)");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
out:
|
|
|
|
close(fd);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int tap_del_ioctl(struct ifreq *ifr)
|
|
|
|
{
|
|
|
|
int fd = open(TUNDEV, O_RDWR);
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (fd < 0) {
|
|
|
|
perror("open");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (ioctl(fd, TUNSETIFF, ifr)) {
|
|
|
|
perror("ioctl(TUNSETIFF)");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (ioctl(fd, TUNSETPERSIST, 0)) {
|
|
|
|
perror("ioctl(TUNSETPERSIST)");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
out:
|
|
|
|
close(fd);
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
}
|
2016-09-02 00:03:40 +08:00
|
|
|
static int parse_args(int argc, char **argv,
|
|
|
|
struct ifreq *ifr, uid_t *uid, gid_t *gid)
|
2009-09-20 00:48:43 +08:00
|
|
|
{
|
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
memset(ifr, 0, sizeof(*ifr));
|
|
|
|
|
|
|
|
ifr->ifr_flags |= IFF_NO_PI;
|
|
|
|
|
|
|
|
while (argc > 0) {
|
|
|
|
if (matches(*argv, "mode") == 0) {
|
|
|
|
NEXT_ARG();
|
|
|
|
if (matches(*argv, "tun") == 0) {
|
|
|
|
if (ifr->ifr_flags & IFF_TAP) {
|
2016-03-22 02:52:19 +08:00
|
|
|
fprintf(stderr, "You managed to ask for more than one tunnel mode.\n");
|
2009-09-20 00:48:43 +08:00
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
ifr->ifr_flags |= IFF_TUN;
|
|
|
|
} else if (matches(*argv, "tap") == 0) {
|
|
|
|
if (ifr->ifr_flags & IFF_TUN) {
|
2016-03-22 02:52:19 +08:00
|
|
|
fprintf(stderr, "You managed to ask for more than one tunnel mode.\n");
|
2009-09-20 00:48:43 +08:00
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
ifr->ifr_flags |= IFF_TAP;
|
|
|
|
} else {
|
2016-03-22 02:52:19 +08:00
|
|
|
fprintf(stderr, "Unknown tunnel mode \"%s\"\n", *argv);
|
2009-09-20 00:48:43 +08:00
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
} else if (uid && matches(*argv, "user") == 0) {
|
|
|
|
char *end;
|
|
|
|
unsigned long user;
|
|
|
|
|
|
|
|
NEXT_ARG();
|
|
|
|
if (**argv && ((user = strtol(*argv, &end, 10)), !*end))
|
|
|
|
*uid = user;
|
|
|
|
else {
|
|
|
|
struct passwd *pw = getpwnam(*argv);
|
2016-03-22 02:52:19 +08:00
|
|
|
|
2009-09-20 00:48:43 +08:00
|
|
|
if (!pw) {
|
|
|
|
fprintf(stderr, "invalid user \"%s\"\n", *argv);
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
*uid = pw->pw_uid;
|
|
|
|
}
|
|
|
|
} else if (gid && matches(*argv, "group") == 0) {
|
|
|
|
char *end;
|
|
|
|
unsigned long group;
|
|
|
|
|
|
|
|
NEXT_ARG();
|
|
|
|
|
|
|
|
if (**argv && ((group = strtol(*argv, &end, 10)), !*end))
|
|
|
|
*gid = group;
|
|
|
|
else {
|
|
|
|
struct group *gr = getgrnam(*argv);
|
2016-03-22 02:52:19 +08:00
|
|
|
|
2009-09-20 00:48:43 +08:00
|
|
|
if (!gr) {
|
|
|
|
fprintf(stderr, "invalid group \"%s\"\n", *argv);
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
*gid = gr->gr_gid;
|
|
|
|
}
|
|
|
|
} else if (matches(*argv, "pi") == 0) {
|
|
|
|
ifr->ifr_flags &= ~IFF_NO_PI;
|
|
|
|
} else if (matches(*argv, "one_queue") == 0) {
|
|
|
|
ifr->ifr_flags |= IFF_ONE_QUEUE;
|
|
|
|
} else if (matches(*argv, "vnet_hdr") == 0) {
|
|
|
|
ifr->ifr_flags |= IFF_VNET_HDR;
|
2013-05-23 20:36:29 +08:00
|
|
|
} else if (matches(*argv, "multi_queue") == 0) {
|
|
|
|
ifr->ifr_flags |= IFF_MULTI_QUEUE;
|
2009-09-20 00:48:43 +08:00
|
|
|
} else if (matches(*argv, "dev") == 0) {
|
|
|
|
NEXT_ARG();
|
2017-10-02 19:46:37 +08:00
|
|
|
if (get_ifname(ifr->ifr_name, *argv))
|
|
|
|
invarg("\"dev\" not a valid ifname", *argv);
|
2009-09-20 00:48:43 +08:00
|
|
|
} else {
|
|
|
|
if (matches(*argv, "name") == 0) {
|
|
|
|
NEXT_ARG();
|
|
|
|
} else if (matches(*argv, "help") == 0)
|
|
|
|
usage();
|
|
|
|
if (ifr->ifr_name[0])
|
|
|
|
duparg2("name", *argv);
|
2017-10-02 19:46:37 +08:00
|
|
|
if (get_ifname(ifr->ifr_name, *argv))
|
|
|
|
invarg("\"name\" not a valid ifname", *argv);
|
2009-09-20 00:48:43 +08:00
|
|
|
}
|
|
|
|
count++;
|
|
|
|
argc--; argv++;
|
|
|
|
}
|
|
|
|
|
2014-09-17 04:54:34 +08:00
|
|
|
if (!(ifr->ifr_flags & TUN_TYPE_MASK)) {
|
|
|
|
fprintf(stderr, "You failed to specify a tunnel mode\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-09-20 00:48:43 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int do_add(int argc, char **argv)
|
|
|
|
{
|
|
|
|
struct ifreq ifr;
|
|
|
|
uid_t uid = -1;
|
|
|
|
gid_t gid = -1;
|
|
|
|
|
|
|
|
if (parse_args(argc, argv, &ifr, &uid, &gid) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return tap_add_ioctl(&ifr, uid, gid);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int do_del(int argc, char **argv)
|
|
|
|
{
|
|
|
|
struct ifreq ifr;
|
|
|
|
|
|
|
|
if (parse_args(argc, argv, &ifr, NULL, NULL) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return tap_del_ioctl(&ifr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void print_flags(long flags)
|
|
|
|
{
|
2018-03-07 05:07:18 +08:00
|
|
|
open_json_array(PRINT_JSON, "flags");
|
|
|
|
|
2009-09-20 00:48:43 +08:00
|
|
|
if (flags & IFF_TUN)
|
2018-03-07 05:07:18 +08:00
|
|
|
print_string(PRINT_ANY, NULL, " %s", "tun");
|
2009-09-20 00:48:43 +08:00
|
|
|
|
|
|
|
if (flags & IFF_TAP)
|
2018-03-07 05:07:18 +08:00
|
|
|
print_string(PRINT_ANY, NULL, " %s", "tap");
|
2009-09-20 00:48:43 +08:00
|
|
|
|
|
|
|
if (!(flags & IFF_NO_PI))
|
2018-03-07 05:07:18 +08:00
|
|
|
print_string(PRINT_ANY, NULL, " %s", "pi");
|
2009-09-20 00:48:43 +08:00
|
|
|
|
|
|
|
if (flags & IFF_ONE_QUEUE)
|
2018-03-07 05:07:18 +08:00
|
|
|
print_string(PRINT_ANY, NULL, " %s", "one_queue");
|
2009-09-20 00:48:43 +08:00
|
|
|
|
2021-09-01 23:48:26 +08:00
|
|
|
if (flags & IFF_MULTI_QUEUE)
|
|
|
|
print_string(PRINT_ANY, NULL, " %s", "multi_queue");
|
|
|
|
|
2009-09-20 00:48:43 +08:00
|
|
|
if (flags & IFF_VNET_HDR)
|
2018-03-07 05:07:18 +08:00
|
|
|
print_string(PRINT_ANY, NULL, " %s", "vnet_hdr");
|
|
|
|
|
|
|
|
if (flags & IFF_PERSIST)
|
|
|
|
print_string(PRINT_ANY, NULL, " %s", "persist");
|
|
|
|
|
|
|
|
if (!(flags & IFF_NOFILTER))
|
|
|
|
print_string(PRINT_ANY, NULL, " %s", "filter");
|
2009-09-20 00:48:43 +08:00
|
|
|
|
2018-03-07 05:07:18 +08:00
|
|
|
flags &= ~(IFF_TUN | IFF_TAP | IFF_NO_PI | IFF_ONE_QUEUE |
|
2021-09-01 23:48:26 +08:00
|
|
|
IFF_MULTI_QUEUE | IFF_VNET_HDR | IFF_PERSIST |
|
|
|
|
IFF_NOFILTER);
|
2009-09-20 00:48:43 +08:00
|
|
|
if (flags)
|
2021-09-01 23:48:26 +08:00
|
|
|
print_0xhex(PRINT_ANY, NULL, " %#llx", flags);
|
2018-03-07 05:07:18 +08:00
|
|
|
|
|
|
|
close_json_array(PRINT_JSON, NULL);
|
2009-09-20 00:48:43 +08:00
|
|
|
}
|
|
|
|
|
2016-08-25 02:08:25 +08:00
|
|
|
static void show_processes(const char *name)
|
|
|
|
{
|
|
|
|
glob_t globbuf = { };
|
|
|
|
char **fd_path;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = glob("/proc/[0-9]*/fd/[0-9]*", GLOB_NOSORT,
|
|
|
|
NULL, &globbuf);
|
|
|
|
if (err)
|
|
|
|
return;
|
|
|
|
|
2018-03-07 05:07:18 +08:00
|
|
|
open_json_array(PRINT_JSON, "processes");
|
|
|
|
|
2016-08-25 02:08:25 +08:00
|
|
|
fd_path = globbuf.gl_pathv;
|
|
|
|
while (*fd_path) {
|
|
|
|
const char *dev_net_tun = "/dev/net/tun";
|
|
|
|
const size_t linkbuf_len = strlen(dev_net_tun) + 2;
|
|
|
|
char linkbuf[linkbuf_len], *fdinfo;
|
|
|
|
int pid, fd;
|
|
|
|
FILE *f;
|
|
|
|
|
|
|
|
if (sscanf(*fd_path, "/proc/%d/fd/%d", &pid, &fd) != 2)
|
|
|
|
goto next;
|
|
|
|
|
|
|
|
if (pid == getpid())
|
|
|
|
goto next;
|
|
|
|
|
|
|
|
err = readlink(*fd_path, linkbuf, linkbuf_len - 1);
|
|
|
|
if (err < 0) {
|
|
|
|
perror("readlink");
|
|
|
|
goto next;
|
|
|
|
}
|
|
|
|
linkbuf[err] = '\0';
|
|
|
|
if (strcmp(dev_net_tun, linkbuf))
|
|
|
|
goto next;
|
|
|
|
|
|
|
|
if (asprintf(&fdinfo, "/proc/%d/fdinfo/%d", pid, fd) < 0)
|
|
|
|
goto next;
|
|
|
|
|
|
|
|
f = fopen(fdinfo, "r");
|
|
|
|
free(fdinfo);
|
|
|
|
if (!f) {
|
|
|
|
perror("fopen");
|
|
|
|
goto next;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (!feof(f)) {
|
|
|
|
char *key = NULL, *value = NULL;
|
|
|
|
|
|
|
|
err = fscanf(f, "%m[^:]: %ms\n", &key, &value);
|
|
|
|
if (err == EOF) {
|
|
|
|
if (ferror(f))
|
|
|
|
perror("fscanf");
|
|
|
|
break;
|
|
|
|
} else if (err == 2 &&
|
2016-09-02 00:03:40 +08:00
|
|
|
!strcmp("iff", key) &&
|
|
|
|
!strcmp(name, value)) {
|
2021-04-19 21:34:58 +08:00
|
|
|
char *pname = get_task_name(pid);
|
2016-09-02 00:03:40 +08:00
|
|
|
|
2018-03-07 05:07:18 +08:00
|
|
|
print_string(PRINT_ANY, "name",
|
|
|
|
"%s", pname ? : "<NULL>");
|
|
|
|
|
|
|
|
print_uint(PRINT_ANY, "pid",
|
|
|
|
"(%d)", pid);
|
2016-08-25 02:08:25 +08:00
|
|
|
free(pname);
|
|
|
|
}
|
|
|
|
|
|
|
|
free(key);
|
|
|
|
free(value);
|
|
|
|
}
|
|
|
|
if (fclose(f))
|
|
|
|
perror("fclose");
|
|
|
|
|
|
|
|
next:
|
|
|
|
++fd_path;
|
|
|
|
}
|
2018-03-07 05:07:18 +08:00
|
|
|
close_json_array(PRINT_JSON, NULL);
|
2016-08-25 02:08:25 +08:00
|
|
|
|
|
|
|
globfree(&globbuf);
|
|
|
|
}
|
|
|
|
|
2018-02-07 14:30:56 +08:00
|
|
|
static int tuntap_filter_req(struct nlmsghdr *nlh, int reqlen)
|
|
|
|
{
|
|
|
|
struct rtattr *linkinfo;
|
|
|
|
int err;
|
2016-08-25 02:08:25 +08:00
|
|
|
|
2018-02-07 14:30:56 +08:00
|
|
|
linkinfo = addattr_nest(nlh, reqlen, IFLA_LINKINFO);
|
|
|
|
|
|
|
|
err = addattr_l(nlh, reqlen, IFLA_INFO_KIND,
|
|
|
|
drv_name, sizeof(drv_name) - 1);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
addattr_nest_end(nlh, linkinfo);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-10-20 04:42:36 +08:00
|
|
|
static int print_tuntap(struct nlmsghdr *n, void *arg)
|
2009-09-20 00:48:43 +08:00
|
|
|
{
|
2018-02-07 14:30:56 +08:00
|
|
|
struct ifinfomsg *ifi = NLMSG_DATA(n);
|
|
|
|
struct rtattr *tb[IFLA_MAX+1];
|
|
|
|
struct rtattr *linkinfo[IFLA_INFO_MAX+1];
|
|
|
|
const char *name, *kind;
|
2009-09-20 00:48:43 +08:00
|
|
|
long flags, owner = -1, group = -1;
|
|
|
|
|
2018-02-07 14:30:56 +08:00
|
|
|
if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (n->nlmsg_len < NLMSG_LENGTH(sizeof(*ifi)))
|
2009-09-20 00:48:43 +08:00
|
|
|
return -1;
|
2018-02-07 14:30:56 +08:00
|
|
|
|
|
|
|
switch (ifi->ifi_type) {
|
|
|
|
case ARPHRD_NONE:
|
|
|
|
case ARPHRD_ETHER:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return 0;
|
2009-09-20 00:48:43 +08:00
|
|
|
}
|
2018-02-07 14:30:56 +08:00
|
|
|
|
|
|
|
parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n));
|
|
|
|
|
|
|
|
if (!tb[IFLA_IFNAME])
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!tb[IFLA_LINKINFO])
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
|
|
|
|
|
|
|
|
if (!linkinfo[IFLA_INFO_KIND])
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
kind = rta_getattr_str(linkinfo[IFLA_INFO_KIND]);
|
|
|
|
if (strcmp(kind, drv_name))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
name = rta_getattr_str(tb[IFLA_IFNAME]);
|
|
|
|
|
|
|
|
if (read_prop(name, "tun_flags", &flags))
|
|
|
|
return 0;
|
|
|
|
if (read_prop(name, "owner", &owner))
|
|
|
|
return 0;
|
|
|
|
if (read_prop(name, "group", &group))
|
|
|
|
return 0;
|
|
|
|
|
2018-03-07 05:07:18 +08:00
|
|
|
open_json_object(NULL);
|
|
|
|
print_color_string(PRINT_ANY, COLOR_IFNAME,
|
|
|
|
"ifname", "%s:", name);
|
2018-02-07 14:30:56 +08:00
|
|
|
print_flags(flags);
|
|
|
|
if (owner != -1)
|
2018-04-25 23:28:57 +08:00
|
|
|
print_u64(PRINT_ANY, "user",
|
2018-03-07 05:07:18 +08:00
|
|
|
" user %ld", owner);
|
2018-02-07 14:30:56 +08:00
|
|
|
if (group != -1)
|
2018-04-25 23:28:57 +08:00
|
|
|
print_u64(PRINT_ANY, "group",
|
2018-03-07 05:07:18 +08:00
|
|
|
" group %ld", group);
|
|
|
|
|
2018-02-07 14:30:56 +08:00
|
|
|
if (show_details) {
|
2018-03-07 05:07:18 +08:00
|
|
|
print_string(PRINT_FP, NULL,
|
|
|
|
"%s\tAttached to processes:", _SL_);
|
2018-02-07 14:30:56 +08:00
|
|
|
show_processes(name);
|
2009-09-20 00:48:43 +08:00
|
|
|
}
|
2018-03-07 05:07:18 +08:00
|
|
|
close_json_object();
|
|
|
|
print_string(PRINT_FP, NULL, "%s", "\n");
|
2018-02-07 14:30:56 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int do_show(int argc, char **argv)
|
|
|
|
{
|
2018-09-30 00:56:57 +08:00
|
|
|
if (rtnl_linkdump_req_filter_fn(&rth, AF_UNSPEC,
|
2018-02-07 14:30:56 +08:00
|
|
|
tuntap_filter_req) < 0) {
|
|
|
|
perror("Cannot send dump request\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2018-03-07 05:07:18 +08:00
|
|
|
new_json_obj(json);
|
|
|
|
|
2018-02-07 14:30:56 +08:00
|
|
|
if (rtnl_dump_filter(&rth, print_tuntap, NULL) < 0) {
|
|
|
|
fprintf(stderr, "Dump terminated\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2018-03-07 05:07:18 +08:00
|
|
|
delete_json_obj();
|
|
|
|
fflush(stdout);
|
|
|
|
|
2009-09-20 00:48:43 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int do_iptuntap(int argc, char **argv)
|
|
|
|
{
|
|
|
|
if (argc > 0) {
|
|
|
|
if (matches(*argv, "add") == 0)
|
|
|
|
return do_add(argc-1, argv+1);
|
2012-05-19 22:08:21 +08:00
|
|
|
if (matches(*argv, "delete") == 0)
|
2009-09-20 00:48:43 +08:00
|
|
|
return do_del(argc-1, argv+1);
|
|
|
|
if (matches(*argv, "show") == 0 ||
|
2016-03-22 02:52:19 +08:00
|
|
|
matches(*argv, "lst") == 0 ||
|
|
|
|
matches(*argv, "list") == 0)
|
|
|
|
return do_show(argc-1, argv+1);
|
2009-09-20 00:48:43 +08:00
|
|
|
if (matches(*argv, "help") == 0)
|
|
|
|
usage();
|
|
|
|
} else
|
|
|
|
return do_show(0, NULL);
|
|
|
|
|
|
|
|
fprintf(stderr, "Command \"%s\" is unknown, try \"ip tuntap help\".\n",
|
|
|
|
*argv);
|
|
|
|
exit(-1);
|
|
|
|
}
|
2018-02-20 20:43:22 +08:00
|
|
|
|
|
|
|
static void print_owner(FILE *f, uid_t uid)
|
|
|
|
{
|
|
|
|
struct passwd *pw = getpwuid(uid);
|
|
|
|
|
|
|
|
if (pw)
|
2018-02-26 18:36:15 +08:00
|
|
|
print_string(PRINT_ANY, "user", "user %s ", pw->pw_name);
|
2018-02-20 20:43:22 +08:00
|
|
|
else
|
2018-02-26 18:36:15 +08:00
|
|
|
print_uint(PRINT_ANY, "user", "user %u ", uid);
|
2018-02-20 20:43:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void print_group(FILE *f, gid_t gid)
|
|
|
|
{
|
|
|
|
struct group *group = getgrgid(gid);
|
|
|
|
|
|
|
|
if (group)
|
2018-02-26 18:36:15 +08:00
|
|
|
print_string(PRINT_ANY, "group", "group %s ", group->gr_name);
|
2018-02-20 20:43:22 +08:00
|
|
|
else
|
2018-02-26 18:36:15 +08:00
|
|
|
print_uint(PRINT_ANY, "group", "group %u ", gid);
|
2018-02-20 20:43:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void print_mq(FILE *f, struct rtattr *tb[])
|
|
|
|
{
|
|
|
|
if (!tb[IFLA_TUN_MULTI_QUEUE] ||
|
2018-02-26 18:36:15 +08:00
|
|
|
!rta_getattr_u8(tb[IFLA_TUN_MULTI_QUEUE])) {
|
|
|
|
if (is_json_context())
|
|
|
|
print_bool(PRINT_JSON, "multi_queue", NULL, false);
|
2018-02-20 20:43:22 +08:00
|
|
|
return;
|
2018-02-26 18:36:15 +08:00
|
|
|
}
|
2018-02-20 20:43:22 +08:00
|
|
|
|
2018-02-26 18:36:15 +08:00
|
|
|
print_bool(PRINT_ANY, "multi_queue", "multi_queue ", true);
|
2018-02-20 20:43:22 +08:00
|
|
|
|
|
|
|
if (tb[IFLA_TUN_NUM_QUEUES]) {
|
2018-02-26 18:36:15 +08:00
|
|
|
print_uint(PRINT_ANY, "numqueues", "numqueues %u ",
|
|
|
|
rta_getattr_u32(tb[IFLA_TUN_NUM_QUEUES]));
|
2018-02-20 20:43:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (tb[IFLA_TUN_NUM_DISABLED_QUEUES]) {
|
2018-02-26 18:36:15 +08:00
|
|
|
print_uint(PRINT_ANY, "numdisabled", "numdisabled %u ",
|
|
|
|
rta_getattr_u32(tb[IFLA_TUN_NUM_DISABLED_QUEUES]));
|
2018-02-20 20:43:22 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-26 18:36:15 +08:00
|
|
|
static void print_type(FILE *f, __u8 type)
|
|
|
|
{
|
|
|
|
SPRINT_BUF(buf);
|
|
|
|
const char *str = buf;
|
|
|
|
|
|
|
|
if (type == IFF_TUN)
|
|
|
|
str = "tun";
|
|
|
|
else if (type == IFF_TAP)
|
|
|
|
str = "tap";
|
|
|
|
else
|
|
|
|
snprintf(buf, sizeof(buf), "UNKNOWN:%hhu", type);
|
|
|
|
|
|
|
|
print_string(PRINT_ANY, "type", "type %s ", str);
|
2018-02-20 20:43:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void tun_print_opt(struct link_util *lu, FILE *f, struct rtattr *tb[])
|
|
|
|
{
|
|
|
|
if (!tb)
|
|
|
|
return;
|
|
|
|
|
2018-02-26 18:36:15 +08:00
|
|
|
if (tb[IFLA_TUN_TYPE])
|
|
|
|
print_type(f, rta_getattr_u8(tb[IFLA_TUN_TYPE]));
|
2018-02-20 20:43:22 +08:00
|
|
|
|
|
|
|
if (tb[IFLA_TUN_PI])
|
2020-11-15 06:54:01 +08:00
|
|
|
print_on_off(PRINT_ANY, "pi", "pi %s ",
|
|
|
|
rta_getattr_u8(tb[IFLA_TUN_PI]));
|
2018-02-20 20:43:22 +08:00
|
|
|
|
|
|
|
if (tb[IFLA_TUN_VNET_HDR]) {
|
2020-11-15 06:54:01 +08:00
|
|
|
print_on_off(PRINT_ANY, "vnet_hdr", "vnet_hdr %s ",
|
|
|
|
rta_getattr_u8(tb[IFLA_TUN_VNET_HDR]));
|
2018-02-20 20:43:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
print_mq(f, tb);
|
|
|
|
|
|
|
|
if (tb[IFLA_TUN_PERSIST])
|
2020-11-15 06:54:01 +08:00
|
|
|
print_on_off(PRINT_ANY, "persist", "persist %s ",
|
|
|
|
rta_getattr_u8(tb[IFLA_TUN_PERSIST]));
|
2018-02-20 20:43:22 +08:00
|
|
|
|
|
|
|
if (tb[IFLA_TUN_OWNER])
|
|
|
|
print_owner(f, rta_getattr_u32(tb[IFLA_TUN_OWNER]));
|
|
|
|
|
|
|
|
if (tb[IFLA_TUN_GROUP])
|
|
|
|
print_group(f, rta_getattr_u32(tb[IFLA_TUN_GROUP]));
|
|
|
|
}
|
|
|
|
|
|
|
|
struct link_util tun_link_util = {
|
|
|
|
.id = "tun",
|
|
|
|
.maxattr = IFLA_TUN_MAX,
|
|
|
|
.print_opt = tun_print_opt,
|
|
|
|
};
|