Commit a3e53658 authored by Pavel Emelyanov's avatar Pavel Emelyanov

tun: Dump tun files and tun links

The major issue with dump is -- some info id get via netlink,
some via sysfs and some (!) via opened and attached tun file.
But the latter cannot be created, if there's another one attached
(or the mq device is full with threads).

Thus we have to dump this info via existing tun file and keep one
in memory till the link dump code takes place.

Opposite situation is also possible -- we can have a persistent
unattached device. In this case we have to attach to it, dump
things and detach back.
Signed-off-by: 's avatarPavel Emelyanov <xemul@parallels.com>
parent 9615c3b9
......@@ -33,6 +33,7 @@
#include "fsnotify.h"
#include "signalfd.h"
#include "namespaces.h"
#include "tun.h"
#include "parasite.h"
#include "parasite-syscall.h"
......@@ -206,6 +207,16 @@ static int dump_unsupp_fd(const struct fd_parms *p)
return -1;
}
static const struct fdtype_ops *get_misc_dev_ops(int minor)
{
switch (minor) {
case TUN_MINOR:
return &tunfile_dump_ops;
};
return NULL;
}
static int dump_chrdev(struct fd_parms *p, int lfd, const int fdinfo)
{
int maj = major(p->stat.st_rdev);
......@@ -220,6 +231,11 @@ static int dump_chrdev(struct fd_parms *p, int lfd, const int fdinfo)
case UNIX98_PTY_SLAVE_MAJOR:
ops = &tty_dump_ops;
break;
case MISC_MAJOR:
ops = get_misc_dev_ops(minor(p->stat.st_rdev));
if (ops)
break;
/* fallthrough */
default:
return dump_unsupp_fd(p);
}
......
......@@ -3,4 +3,9 @@
#ifndef TUN_MINOR
#define TUN_MINOR 200
#endif
#include "protobuf/netdev.pb-c.h"
extern const struct fdtype_ops tunfile_dump_ops;
int dump_tun_link(NetDeviceEntry *nde, struct cr_fdset *fds);
#endif
......@@ -14,6 +14,7 @@
#include "libnetlink.h"
#include "crtools.h"
#include "sk-inet.h"
#include "tun.h"
#include "util-pie.h"
#include "protobuf.h"
......@@ -113,11 +114,30 @@ static int dump_one_ethernet(struct ifinfomsg *ifi,
* connection to the outer world and just dump this end :(
*/
return dump_one_netdev(ND_TYPE__VETH, ifi, tb, fds, NULL);
if (!strcmp(kind, "tun"))
return dump_one_netdev(ND_TYPE__TUN, ifi, tb, fds, dump_tun_link);
unk:
pr_err("Unknown eth kind %s link %d\n", kind, ifi->ifi_index);
return -1;
}
static int dump_one_gendev(struct ifinfomsg *ifi,
struct rtattr **tb, struct cr_fdset *fds)
{
char *kind;
kind = link_kind(ifi, tb);
if (!kind)
goto unk;
if (!strcmp(kind, "tun"))
return dump_one_netdev(ND_TYPE__TUN, ifi, tb, fds, dump_tun_link);
unk:
pr_err("Unknown ARPHRD_NONE kind %s link %d\n", kind, ifi->ifi_index);
return -1;
}
static int dump_one_link(struct nlmsghdr *hdr, void *arg)
{
struct cr_fdset *fds = arg;
......@@ -143,6 +163,9 @@ static int dump_one_link(struct nlmsghdr *hdr, void *arg)
case ARPHRD_ETHER:
ret = dump_one_ethernet(ifi, tb, fds);
break;
case ARPHRD_NONE:
ret = dump_one_gendev(ifi, tb, fds);
break;
default:
pr_err("Unsupported link type %d, kind %s\n",
ifi->ifi_type, link_kind(ifi, tb));
......
......@@ -44,7 +44,253 @@
#define TUN_DEV_GEN_PATH "/dev/net/tun"
static LIST_HEAD(tun_links);
struct tun_link {
char name[IFNAMSIZ];
struct list_head l;
union {
struct {
unsigned sndbuf;
unsigned vnethdr;
} dmp;
};
};
static struct tun_link *find_tun_link(char *name)
{
struct tun_link *tl;
list_for_each_entry(tl, &tun_links, l)
if (!strcmp(tl->name, name))
return tl;
return NULL;
}
static struct tun_link *__dump_tun_link_fd(int fd, char *name, unsigned flags)
{
struct tun_link *tl;
struct sock_fprog flt;
tl = xmalloc(sizeof(*tl));
strcpy(tl->name, name);
if (ioctl(fd, TUNGETVNETHDRSZ, &tl->dmp.vnethdr) < 0) {
pr_perror("Can't dump vnethdr size for %s", name);
goto err;
}
if (ioctl(fd, TUNGETSNDBUF, &tl->dmp.sndbuf) < 0) {
pr_perror("Can't dump sndbuf for %s", name);
goto err;
}
if (flags & IFF_TAP) {
pr_debug("Checking filter for tap %s\n", name);
if (ioctl(fd, TUNGETFILTER, &flt) < 0) {
pr_perror("Can't get tun filter for %s", name);
goto err;
}
/*
* TUN filters are tricky -- the program itself is 'somewhere'
* in the task's memory, so we can't get one for unattached
* persistent device. The only way for doing it is opening the
* device with IFF_NOFILTER and attaching some fake one :(
*/
if (flt.len != 0) {
pr_err("Can't dump %s with filter on-board\n", name);
goto err;
}
} else if (!(flags & IFF_NOFILTER)) {
pr_err("No info about %s filter, kernel is too old\n", name);
goto err;
}
return tl;
err:
xfree(tl);
return NULL;
}
static struct tun_link *dump_tun_link_fd(int fd, char *name, unsigned flags)
{
struct tun_link *tl;
tl = find_tun_link(name);
if (tl)
return tl;
tl = __dump_tun_link_fd(fd, name, flags);
if (tl)
/*
* Keep this in list till links dumping code starts.
* We can't let it dump all this stuff itself, since
* multiple attaches to one tun device is limited and
* we may not be able to it that late.
*
* For persistent detached devices the get_tun_link_fd
* will attach to the device and get the needed stuff.
*/
list_add(&tl->l, &tun_links);
return tl;
}
static int open_tun_dev(char *name, unsigned int idx, unsigned flags)
{
int fd;
struct ifreq ifr;
fd = open(TUN_DEV_GEN_PATH, O_RDWR);
if (fd < 0) {
pr_perror("Can't open tun device");
return -1;
}
if (idx) {
pr_debug(" restoring %u for %s tun\n", idx, name);
if (ioctl(fd, TUNSETIFINDEX, &idx) < 0) {
pr_perror("Can't restore tun's index");
goto err;
}
}
memset(&ifr, 0, sizeof(ifr));
strcpy(ifr.ifr_name, name);
ifr.ifr_flags = flags;
if (ioctl(fd, TUNSETIFF, &ifr)) {
pr_perror("Can't create tun device\n");
goto err;
}
return fd;
err:
close(fd);
return -1;
}
static struct tun_link *get_tun_link_fd(char *name, unsigned flags)
{
struct tun_link *tl;
int fd;
tl = find_tun_link(name);
if (tl)
return tl;
/*
* If we haven't found this thing, then the
* device we see via netlink exists w/o any fds
* attached, i.e. -- it's persistent
*/
if (!(flags & IFF_PERSIST)) {
pr_err("No fd infor for non persistent tun device %s\n", name);
return NULL;
}
/*
* Kernel will try to attach filter (if it exists) to our memory,
* avoid this.
*/
flags |= IFF_NOFILTER;
fd = open_tun_dev(name, 0, flags);
if (fd < 0)
return NULL;
tl = __dump_tun_link_fd(fd, name, flags);
close(fd);
return tl;
}
static int dump_tunfile(int lfd, u32 id, const struct fd_parms *p)
{
int ret, img = fdset_fd(glob_fdset, CR_FD_TUNFILE);
TunfileEntry tfe = TUNFILE_ENTRY__INIT;
struct ifreq ifr;
if (dump_one_reg_file(lfd, id, p))
return -1;
pr_info("Dumping tun-file %d with id %#x\n", lfd, id);
tfe.id = id;
ret = ioctl(lfd, TUNGETIFF, &ifr);
if (ret < 0) {
if (errno != EBADFD) {
pr_perror("Can't dump tun-file device");
return -1;
}
/*
* Otherwise this is just opened file with not yet attached
* tun device. Go agead an write the respective entry.
*/
} else {
tfe.netdev = ifr.ifr_ifrn.ifrn_name;
pr_info("`- attached to device %s (flags %x)\n", tfe.netdev, ifr.ifr_flags);
if (ifr.ifr_flags & IFF_DETACH_QUEUE) {
tfe.has_detached = true;
tfe.detached = true;
}
if (dump_tun_link_fd(lfd, tfe.netdev, ifr.ifr_flags) == NULL)
return -1;
}
return pb_write_one(img, &tfe, PB_TUNFILE);
}
const struct fdtype_ops tunfile_dump_ops = {
.type = FD_TYPES__TUN,
.dump = dump_tunfile,
};
void show_tunfile(int fd)
{
pb_show_plain(fd, PB_TUNFILE);
}
int dump_tun_link(NetDeviceEntry *nde, struct cr_fdset *fds)
{
TunLinkEntry tle = TUN_LINK_ENTRY__INIT;
char spath[64];
char buf[64];
int ret = 0;
struct tun_link *tl;
sprintf(spath, "class/net/%s/tun_flags", nde->name);
ret |= read_ns_sys_file(spath, buf, sizeof(buf));
tle.flags = strtol(buf, NULL, 0);
sprintf(spath, "class/net/%s/owner", nde->name);
ret |= read_ns_sys_file(spath, buf, sizeof(buf));
tle.owner = strtol(buf, NULL, 10);
sprintf(spath, "class/net/%s/group", nde->name);
ret |= read_ns_sys_file(spath, buf, sizeof(buf));
tle.group = strtol(buf, NULL, 10);
if (ret < 0)
return ret;
tl = get_tun_link_fd(nde->name, tle.flags);
if (!tl)
return ret;
tle.vnethdr = tl->dmp.vnethdr;
tle.sndbuf = tl->dmp.sndbuf;
nde->tun = &tle;
return write_netdev_img(nde, fds);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment