2013-08-23 18:57:40 +04:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <linux/if.h>
|
|
|
|
#include <linux/if_tun.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
|
2015-01-12 14:37:11 +03:00
|
|
|
// MAO required on Centos 6 (linux-3.18.1 kernel)
|
|
|
|
#include <linux/filter.h>
|
|
|
|
|
2013-11-06 17:21:11 +04:00
|
|
|
#include "cr_options.h"
|
2014-09-29 12:47:37 +04:00
|
|
|
#include "imgset.h"
|
2013-08-23 18:57:40 +04:00
|
|
|
#include "protobuf.h"
|
|
|
|
#include "cr-show.h"
|
2013-08-28 19:06:51 +04:00
|
|
|
#include "string.h"
|
2013-08-23 18:57:40 +04:00
|
|
|
#include "files.h"
|
|
|
|
#include "files-reg.h"
|
|
|
|
#include "tun.h"
|
|
|
|
#include "net.h"
|
2013-08-28 19:39:56 +04:00
|
|
|
#include "namespaces.h"
|
2013-08-23 18:57:40 +04:00
|
|
|
|
|
|
|
#include "protobuf/tun.pb-c.h"
|
|
|
|
|
|
|
|
#ifndef IFF_PERSIST
|
|
|
|
#define IFF_PERSIST 0x0800
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef IFF_NOFILTER
|
|
|
|
#define IFF_NOFILTER 0x1000
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef TUNSETQUEUE
|
|
|
|
#define TUNSETQUEUE _IOW('T', 217, int)
|
|
|
|
#define IFF_ATTACH_QUEUE 0x0200
|
|
|
|
#define IFF_DETACH_QUEUE 0x0400
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Absense of the 1st ioctl means we cannot restore tun link. But
|
|
|
|
* since the 2nd one appeared at the same time, we'll "check" this
|
|
|
|
* by trying to dump filter and abort dump if it's not there.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef TUNSETIFINDEX
|
|
|
|
#define TUNSETIFINDEX _IOW('T', 218, unsigned int)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef TUNGETFILTER
|
|
|
|
#define TUNGETFILTER _IOR('T', 219, struct sock_fprog)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define TUN_DEV_GEN_PATH "/dev/net/tun"
|
|
|
|
|
2015-01-22 18:55:34 +03:00
|
|
|
int check_tun_cr(int no_tun_err)
|
2013-08-23 19:18:47 +04:00
|
|
|
{
|
|
|
|
int fd, idx = 13, ret;
|
|
|
|
|
|
|
|
if (opts.check_ms_kernel) {
|
|
|
|
pr_warn("Skipping tun support check\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
fd = open(TUN_DEV_GEN_PATH, O_RDWR);
|
|
|
|
if (fd < 0) {
|
|
|
|
pr_perror("Can't check tun support");
|
2015-01-22 18:55:34 +03:00
|
|
|
return no_tun_err;
|
2013-08-23 19:18:47 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
ret = ioctl(fd, TUNSETIFINDEX, &idx);
|
|
|
|
if (ret < 0)
|
|
|
|
pr_perror("No proper support for tun dump/restore");
|
|
|
|
|
|
|
|
close(fd);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-08-23 19:02:55 +04:00
|
|
|
static LIST_HEAD(tun_links);
|
|
|
|
|
|
|
|
struct tun_link {
|
|
|
|
char name[IFNAMSIZ];
|
|
|
|
struct list_head l;
|
|
|
|
union {
|
2013-08-23 19:10:15 +04:00
|
|
|
struct {
|
|
|
|
unsigned flags;
|
|
|
|
} rst;
|
|
|
|
|
2013-08-23 19:02:55 +04:00
|
|
|
struct {
|
|
|
|
unsigned sndbuf;
|
|
|
|
unsigned vnethdr;
|
|
|
|
} dmp;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2013-08-23 19:10:15 +04:00
|
|
|
static int list_tun_link(NetDeviceEntry *nde)
|
|
|
|
{
|
|
|
|
struct tun_link *tl;
|
|
|
|
|
|
|
|
tl = xmalloc(sizeof(*tl));
|
|
|
|
if (!tl)
|
|
|
|
return -1;
|
|
|
|
|
2013-08-28 19:06:51 +04:00
|
|
|
strlcpy(tl->name, nde->name, sizeof(tl->name));
|
2013-08-23 19:10:15 +04:00
|
|
|
/*
|
|
|
|
* Keep tun-flags not only for persistency fixup (see
|
|
|
|
* commend below), but also for TUNSETIFF -- we must
|
|
|
|
* open the device with the same flags it should live
|
|
|
|
* with (i.e. -- with which it was created.
|
|
|
|
*/
|
|
|
|
tl->rst.flags = nde->tun->flags;
|
|
|
|
list_add_tail(&tl->l, &tun_links);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-08-23 19:02:55 +04:00
|
|
|
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));
|
2013-08-28 17:20:44 +04:00
|
|
|
if (!tl)
|
|
|
|
goto err;
|
2013-08-28 19:06:51 +04:00
|
|
|
strlcpy(tl->name, name, sizeof(tl->name));
|
2013-08-23 19:02:55 +04:00
|
|
|
|
|
|
|
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));
|
2013-08-28 19:06:51 +04:00
|
|
|
strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
|
2013-08-23 19:02:55 +04:00
|
|
|
ifr.ifr_flags = flags;
|
|
|
|
|
|
|
|
if (ioctl(fd, TUNSETIFF, &ifr)) {
|
2013-09-02 16:19:12 +04:00
|
|
|
pr_perror("Can't create tun device");
|
2013-08-23 19:02:55 +04:00
|
|
|
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)
|
|
|
|
{
|
2014-09-29 12:48:53 +04:00
|
|
|
int ret;
|
|
|
|
struct cr_img *img;
|
2013-08-23 19:02:55 +04:00
|
|
|
TunfileEntry tfe = TUNFILE_ENTRY__INIT;
|
|
|
|
struct ifreq ifr;
|
|
|
|
|
2014-04-21 18:23:22 +04:00
|
|
|
if (!(root_ns_mask & CLONE_NEWNET)) {
|
2013-08-28 19:39:56 +04:00
|
|
|
pr_err("Net namespace is required to dump tun link\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-08-23 19:02:55 +04:00
|
|
|
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 {
|
2013-09-10 12:01:28 +04:00
|
|
|
tfe.netdev = ifr.ifr_name;
|
2013-08-23 19:02:55 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-09-29 12:48:53 +04:00
|
|
|
img = img_from_set(glob_imgset, CR_FD_TUNFILE);
|
2013-08-23 19:02:55 +04:00
|
|
|
return pb_write_one(img, &tfe, PB_TUNFILE);
|
|
|
|
}
|
|
|
|
|
|
|
|
const struct fdtype_ops tunfile_dump_ops = {
|
2014-10-08 14:35:00 +04:00
|
|
|
.type = FD_TYPES__TUNF,
|
2013-08-23 19:02:55 +04:00
|
|
|
.dump = dump_tunfile,
|
|
|
|
};
|
|
|
|
|
2013-08-23 19:10:15 +04:00
|
|
|
struct tunfile_info {
|
|
|
|
struct file_desc d;
|
|
|
|
TunfileEntry *tfe;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int tunfile_open(struct file_desc *d)
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
struct tunfile_info *ti;
|
|
|
|
struct ifreq ifr;
|
|
|
|
struct tun_link *tl;
|
|
|
|
|
|
|
|
ti = container_of(d, struct tunfile_info, d);
|
|
|
|
fd = open_reg_by_id(ti->tfe->id);
|
|
|
|
if (fd < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (!ti->tfe->netdev)
|
|
|
|
/* just-opened tun file */
|
|
|
|
return fd;
|
|
|
|
|
|
|
|
tl = find_tun_link(ti->tfe->netdev);
|
|
|
|
if (!tl) {
|
|
|
|
pr_err("No tun device for file %s\n", ti->tfe->netdev);
|
2015-05-05 15:31:11 -07:00
|
|
|
goto err;
|
2013-08-23 19:10:15 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
memset(&ifr, 0, sizeof(ifr));
|
2013-08-28 19:06:51 +04:00
|
|
|
strlcpy(ifr.ifr_name, tl->name, sizeof(ifr.ifr_name));
|
2013-08-23 19:10:15 +04:00
|
|
|
ifr.ifr_flags = tl->rst.flags;
|
|
|
|
|
|
|
|
if (ioctl(fd, TUNSETIFF, &ifr) < 0) {
|
|
|
|
pr_perror("Can't attach tunfile to device");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ti->tfe->has_detached && ti->tfe->detached) {
|
|
|
|
pr_info("Detaching from %s queue\n", ti->tfe->netdev);
|
|
|
|
ifr.ifr_flags = IFF_DETACH_QUEUE;
|
|
|
|
if (ioctl(fd, TUNSETQUEUE, &ifr) < 0) {
|
|
|
|
pr_perror("Can't detach queue");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(tl->rst.flags & IFF_PERSIST)) {
|
|
|
|
pr_info("Dropping persistency for %s\n", tl->name);
|
|
|
|
if (ioctl(fd, TUNSETPERSIST, 0) < 0) {
|
|
|
|
pr_perror("Error dropping persistency");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return fd;
|
|
|
|
|
|
|
|
err:
|
|
|
|
close(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct file_desc_ops tunfile_desc_ops = {
|
2014-10-08 14:35:00 +04:00
|
|
|
.type = FD_TYPES__TUNF,
|
2013-08-23 19:10:15 +04:00
|
|
|
.open = tunfile_open,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int collect_one_tunfile(void *o, ProtobufCMessage *base)
|
|
|
|
{
|
|
|
|
struct tunfile_info *ti = o;
|
|
|
|
|
|
|
|
ti->tfe = pb_msg(base, TunfileEntry);
|
|
|
|
file_desc_add(&ti->d, ti->tfe->id, &tunfile_desc_ops);
|
|
|
|
|
|
|
|
pr_info("Collected %s tunfile\n", ti->tfe->netdev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct collect_image_info tunfile_cinfo = {
|
|
|
|
.fd_type = CR_FD_TUNFILE,
|
|
|
|
.pb_type = PB_TUNFILE,
|
|
|
|
.priv_size = sizeof(struct tunfile_info),
|
|
|
|
.collect = collect_one_tunfile,
|
|
|
|
};
|
|
|
|
|
2014-09-29 12:47:37 +04:00
|
|
|
int dump_tun_link(NetDeviceEntry *nde, struct cr_imgset *fds)
|
2013-08-23 19:02:55 +04:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
2013-08-23 19:10:15 +04:00
|
|
|
|
|
|
|
int restore_one_tun(NetDeviceEntry *nde, int nlsk)
|
|
|
|
{
|
|
|
|
int fd, ret = -1, aux;
|
|
|
|
|
|
|
|
if (!nde->tun) {
|
|
|
|
pr_err("Corrupted TUN link entry %x\n", nde->ifindex);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
pr_info("Restoring tun device %s\n", nde->name);
|
|
|
|
|
|
|
|
fd = open_tun_dev(nde->name, nde->ifindex, nde->tun->flags);
|
|
|
|
if (fd < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
aux = nde->tun->owner;
|
|
|
|
if ((aux != -1) && ioctl(fd, TUNSETOWNER, aux) < 0) {
|
2013-09-02 16:19:12 +04:00
|
|
|
pr_perror("Can't set owner");
|
2013-08-23 19:10:15 +04:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
aux = nde->tun->group;
|
|
|
|
if ((aux != -1) && ioctl(fd, TUNSETGROUP, aux) < 0) {
|
2013-09-02 16:19:12 +04:00
|
|
|
pr_perror("Can't set group");
|
2013-08-23 19:10:15 +04:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
aux = nde->tun->sndbuf;
|
|
|
|
if (ioctl(fd, TUNSETSNDBUF, &aux) < 0) {
|
|
|
|
pr_perror("Can't set sndbuf");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
aux = nde->tun->vnethdr;
|
|
|
|
if (ioctl(fd, TUNSETVNETHDRSZ, &aux) < 0) {
|
|
|
|
pr_perror("Can't set vnethdr");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set this device persistent anyway and schedule
|
|
|
|
* the persistence drop if it should not be such.
|
|
|
|
* The first _real_ opener will do it.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (ioctl(fd, TUNSETPERSIST, 1)) {
|
2013-09-02 16:19:12 +04:00
|
|
|
pr_perror("Can't make tun device persistent");
|
2013-08-23 19:10:15 +04:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (restore_link_parms(nde, nlsk)) {
|
|
|
|
pr_err("Error restoring %s link params\n", nde->name);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = list_tun_link(nde);
|
|
|
|
out:
|
|
|
|
close(fd);
|
|
|
|
return ret;
|
|
|
|
}
|