Message ID | 1241433778.6126.90.camel@macbook.infradead.org |
---|---|
State | Not Applicable, archived |
Delegated to: | David Miller |
Headers | show |
On Mon, 04 May 2009 11:42:58 +0100 David Woodhouse <dwmw2@infradead.org> wrote: > This patch provides support for 'ip tuntap', allowing creation and > deletion of persistent tun/tap devices. > > The support for _listing_ devices relies on the patch I just sent to > netdev (<1241433136.6126.70.camel@macbook.infradead.org>), although > creation and deletion work without that patch. > > diff --git a/ip/Makefile b/ip/Makefile > index 98ba876..e868f3e 100644 > --- a/ip/Makefile > +++ b/ip/Makefile > @@ -1,6 +1,6 @@ > IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o \ > rtm_map.o iptunnel.o ip6tunnel.o tunnel.o ipneigh.o ipntable.o iplink.o \ > - ipmaddr.o ipmonitor.o ipmroute.o ipprefix.o \ > + ipmaddr.o ipmonitor.o ipmroute.o ipprefix.o iptuntap.o \ > ipxfrm.o xfrm_state.o xfrm_policy.o xfrm_monitor.o \ > iplink_vlan.o link_veth.o link_gre.o > > diff --git a/ip/ip.c b/ip/ip.c > index 2bd54b2..d846a76 100644 > --- a/ip/ip.c > +++ b/ip/ip.c > @@ -47,7 +47,7 @@ static void usage(void) > "Usage: ip [ OPTIONS ] OBJECT { COMMAND | help }\n" > " ip [ -force ] -batch filename\n" > "where OBJECT := { link | addr | addrlabel | route | rule | neigh | ntable |\n" > -" tunnel | maddr | mroute | monitor | xfrm }\n" > +" tunnel | tuntap | maddr | mroute | monitor | xfrm }\n" > " OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |\n" > " -f[amily] { inet | inet6 | ipx | dnet | link } |\n" > " -o[neline] | -t[imestamp] | -b[atch] [filename] }\n"); > @@ -75,6 +75,8 @@ static const struct cmd { > { "link", do_iplink }, > { "tunnel", do_iptunnel }, > { "tunl", do_iptunnel }, > + { "tuntap", do_iptuntap }, > + { "tap", do_iptuntap }, > { "monitor", do_ipmonitor }, > { "xfrm", do_xfrm }, > { "mroute", do_multiroute }, > diff --git a/ip/ip_common.h b/ip/ip_common.h > index 273065f..c857667 100644 > --- a/ip/ip_common.h > +++ b/ip/ip_common.h > @@ -32,6 +32,7 @@ extern int do_ipneigh(int argc, char **argv); > extern int do_ipntable(int argc, char **argv); > extern int do_iptunnel(int argc, char **argv); > extern int do_ip6tunnel(int argc, char **argv); > +extern int do_iptuntap(int argc, char **argv); > extern int do_iplink(int argc, char **argv); > extern int do_ipmonitor(int argc, char **argv); > extern int do_multiaddr(int argc, char **argv); > diff --git a/ip/iptuntap.c b/ip/iptuntap.c > new file mode 100644 > index 0000000..b480296 > --- /dev/null > +++ b/ip/iptuntap.c > @@ -0,0 +1,322 @@ > +/* > + * 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> > +#include <pwd.h> > +#include <grp.h> > +#include <fcntl.h> > +#include <dirent.h> > +#include <errno.h> > + > +#include "rt_names.h" > +#include "utils.h" > +#include "ip_common.h" > + > +#define TUNDEV "/dev/net/tun" > + > +static void usage(void) __attribute__((noreturn)); > + > +static void usage(void) > +{ > + fprintf(stderr, "Usage: ip tuntap { add | del } [ dev PHYS_DEV ] \n"); > + fprintf(stderr, " [ mode { tun | tap } ] [ user USER ] [ group GROUP ]\n"); > + fprintf(stderr, " [ one_queue ] [ no_pi ] [ vnet_hdr ]\n"); > + fprintf(stderr, "\n"); > + fprintf(stderr, "Where: USER := { STRING | NUMBER }\n"); > + fprintf(stderr, " GROUP := { STRING | NUMBER }\n"); > + exit(-1); > +} > + > +static int tap_add_ioctl(struct ifreq *ifr, uid_t uid, gid_t gid) > +{ > + int fd = open(TUNDEV, O_RDWR); > + int ret = -1; > + > +#ifndef IFF_TUN_EXCL > +#define IFF_TUN_EXCL 0x8000 > +#endif > + ifr->ifr_flags |= IFF_TUN_EXCL; > + > + 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; > +} > + I would rather provide a netlink for managing TUNTAP interfaces and reorganize under ip link?? -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Mon, 2009-05-04 at 07:38 -0700, Stephen Hemminger wrote: > I would rather provide a netlink for managing TUNTAP interfaces I'm not sure that makes a lot of sense. We'd be adding a new, duplicate user API solely for the benefit of iproute2; it's not as if we'd ever be able to get rid of the existing interface that everyone uses. Unless you want to ditch the /dev/net/tun chardev completely and do _everything_ over netlink, maybe... but that doesn't seem particularly worthwhile either. > and reorganize under ip link?? It seemed more intuitive to model it after 'ip tunnel'. How would you want it to look?
On Mon, 2009-05-04 at 15:49 +0100, David Woodhouse wrote: > On Mon, 2009-05-04 at 07:38 -0700, Stephen Hemminger wrote: > > I would rather provide a netlink for managing TUNTAP interfaces > > I'm not sure that makes a lot of sense. We'd be adding a new, duplicate > user API solely for the benefit of iproute2; it's not as if we'd ever be > able to get rid of the existing interface that everyone uses. > > Unless you want to ditch the /dev/net/tun chardev completely and do > _everything_ over netlink, maybe... but that doesn't seem particularly > worthwhile either. > > > and reorganize under ip link?? > > It seemed more intuitive to model it after 'ip tunnel'. How would you > want it to look? Ping?
On Wed, 27 May 2009 17:32:17 +0100 David Woodhouse <dwmw2@infradead.org> wrote: > On Mon, 2009-05-04 at 15:49 +0100, David Woodhouse wrote: > > On Mon, 2009-05-04 at 07:38 -0700, Stephen Hemminger wrote: > > > I would rather provide a netlink for managing TUNTAP interfaces > > > > I'm not sure that makes a lot of sense. We'd be adding a new, duplicate > > user API solely for the benefit of iproute2; it's not as if we'd ever be > > able to get rid of the existing interface that everyone uses. > > > > Unless you want to ditch the /dev/net/tun chardev completely and do > > _everything_ over netlink, maybe... but that doesn't seem particularly > > worthwhile either. > > > > > and reorganize under ip link?? > > > > It seemed more intuitive to model it after 'ip tunnel'. How would you > > want it to look? > > Ping? > Almost all of iproute2 is based on netlink, I don't want to add non netlink interfaces.
On Wed, 2009-05-27 at 09:38 -0700, Stephen Hemminger wrote: > On Wed, 27 May 2009 17:32:17 +0100 > David Woodhouse <dwmw2@infradead.org> wrote: > > > On Mon, 2009-05-04 at 15:49 +0100, David Woodhouse wrote: > > > On Mon, 2009-05-04 at 07:38 -0700, Stephen Hemminger wrote: > > > > I would rather provide a netlink for managing TUNTAP interfaces > > > > > > I'm not sure that makes a lot of sense. We'd be adding a new, duplicate > > > user API solely for the benefit of iproute2; it's not as if we'd ever be > > > able to get rid of the existing interface that everyone uses. > > > > > > Unless you want to ditch the /dev/net/tun chardev completely and do > > > _everything_ over netlink, maybe... but that doesn't seem particularly > > > worthwhile either. > > > > > > > and reorganize under ip link?? > > > > > > It seemed more intuitive to model it after 'ip tunnel'. How would you > > > want it to look? > > > > Ping? > > > > Almost all of iproute2 is based on netlink, I don't want to add non netlink > interfaces. So you want to add a new interface to the kernel which duplicates the one we've had for years, then make userspace which will only work with newer kernels?
On Wed, 27 May 2009 17:49:32 +0100 David Woodhouse <dwmw2@infradead.org> wrote: > On Wed, 2009-05-27 at 09:38 -0700, Stephen Hemminger wrote: > > On Wed, 27 May 2009 17:32:17 +0100 > > David Woodhouse <dwmw2@infradead.org> wrote: > > > > > On Mon, 2009-05-04 at 15:49 +0100, David Woodhouse wrote: > > > > On Mon, 2009-05-04 at 07:38 -0700, Stephen Hemminger wrote: > > > > > I would rather provide a netlink for managing TUNTAP interfaces > > > > > > > > I'm not sure that makes a lot of sense. We'd be adding a new, duplicate > > > > user API solely for the benefit of iproute2; it's not as if we'd ever be > > > > able to get rid of the existing interface that everyone uses. > > > > > > > > Unless you want to ditch the /dev/net/tun chardev completely and do > > > > _everything_ over netlink, maybe... but that doesn't seem particularly > > > > worthwhile either. > > > > > > > > > and reorganize under ip link?? > > > > > > > > It seemed more intuitive to model it after 'ip tunnel'. How would you > > > > want it to look? > > > > > > Ping? > > > > > > > Almost all of iproute2 is based on netlink, I don't want to add non netlink > > interfaces. > > So you want to add a new interface to the kernel which duplicates the > one we've had for years, then make userspace which will only work with > newer kernels? Well if the kernel interface existed for years, there were obviously other tools using it. Why do you have to cram that into iproute2?
From: David Woodhouse <dwmw2@infradead.org> Date: Wed, 27 May 2009 17:49:32 +0100 > On Wed, 2009-05-27 at 09:38 -0700, Stephen Hemminger wrote: >> Almost all of iproute2 is based on netlink, I don't want to add non netlink >> interfaces. > > So you want to add a new interface to the kernel which duplicates the > one we've had for years, then make userspace which will only work with > newer kernels? I agree, Stephen just put in the ioctl() or whatever based support for TUN devices. We've done stuff like this before in iproute2 so your arguments against this are hogwash. -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
From: Stephen Hemminger <shemminger@vyatta.com> Date: Wed, 27 May 2009 10:06:47 -0700 > Well if the kernel interface existed for years, there were obviously other > tools using it. Why do you have to cram that into iproute2? Because iproute2 is what we want everyone to use perhaps?!?!?!? Stephen, STOP THIS PASSIVE AGGRESSIVE CRAP. It's not becoming, and it's just delaying making iproute2 more useful by supporting tuntap devices properly. -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Mon, 04 May 2009 11:42:58 +0100 David Woodhouse <dwmw2@infradead.org> wrote: > This patch provides support for 'ip tuntap', allowing creation and > deletion of persistent tun/tap devices. > > The support for _listing_ devices relies on the patch I just sent to > netdev (<1241433136.6126.70.camel@macbook.infradead.org>), although > creation and deletion work without that patch. > > diff --git a/ip/Makefile b/ip/Makefile > index 98ba876..e868f3e 100644 > --- a/ip/Makefile > +++ b/ip/Makefile > @@ -1,6 +1,6 @@ > IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o \ > rtm_map.o iptunnel.o ip6tunnel.o tunnel.o ipneigh.o ipntable.o iplink.o \ > - ipmaddr.o ipmonitor.o ipmroute.o ipprefix.o \ > + ipmaddr.o ipmonitor.o ipmroute.o ipprefix.o iptuntap.o \ > ipxfrm.o xfrm_state.o xfrm_policy.o xfrm_monitor.o \ > iplink_vlan.o link_veth.o link_gre.o > > diff --git a/ip/ip.c b/ip/ip.c > index 2bd54b2..d846a76 100644 > --- a/ip/ip.c > +++ b/ip/ip.c > @@ -47,7 +47,7 @@ static void usage(void) > "Usage: ip [ OPTIONS ] OBJECT { COMMAND | help }\n" > " ip [ -force ] -batch filename\n" > "where OBJECT := { link | addr | addrlabel | route | rule | neigh | ntable |\n" > -" tunnel | maddr | mroute | monitor | xfrm }\n" > +" tunnel | tuntap | maddr | mroute | monitor | xfrm }\n" > " OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |\n" > " -f[amily] { inet | inet6 | ipx | dnet | link } |\n" > " -o[neline] | -t[imestamp] | -b[atch] [filename] }\n"); > @@ -75,6 +75,8 @@ static const struct cmd { > { "link", do_iplink }, > { "tunnel", do_iptunnel }, > { "tunl", do_iptunnel }, > + { "tuntap", do_iptuntap }, > + { "tap", do_iptuntap }, > { "monitor", do_ipmonitor }, > { "xfrm", do_xfrm }, > { "mroute", do_multiroute }, > diff --git a/ip/ip_common.h b/ip/ip_common.h > index 273065f..c857667 100644 > --- a/ip/ip_common.h > +++ b/ip/ip_common.h > @@ -32,6 +32,7 @@ extern int do_ipneigh(int argc, char **argv); > extern int do_ipntable(int argc, char **argv); > extern int do_iptunnel(int argc, char **argv); > extern int do_ip6tunnel(int argc, char **argv); > +extern int do_iptuntap(int argc, char **argv); > extern int do_iplink(int argc, char **argv); > extern int do_ipmonitor(int argc, char **argv); > extern int do_multiaddr(int argc, char **argv); > diff --git a/ip/iptuntap.c b/ip/iptuntap.c > new file mode 100644 > index 0000000..b480296 > --- /dev/null > +++ b/ip/iptuntap.c > @@ -0,0 +1,322 @@ > +/* > + * 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> > +#include <pwd.h> > +#include <grp.h> > +#include <fcntl.h> > +#include <dirent.h> > +#include <errno.h> > + > +#include "rt_names.h" > +#include "utils.h" > +#include "ip_common.h" > + > +#define TUNDEV "/dev/net/tun" I know this is historical legacy, but how does this play with network namespaces? > + > +static void usage(void) __attribute__((noreturn)); > + > +static void usage(void) > +{ > + fprintf(stderr, "Usage: ip tuntap { add | del } [ dev PHYS_DEV ] \n"); > + fprintf(stderr, " [ mode { tun | tap } ] [ user USER ] [ group GROUP ]\n"); > + fprintf(stderr, " [ one_queue ] [ no_pi ] [ vnet_hdr ]\n"); > + fprintf(stderr, "\n"); > + fprintf(stderr, "Where: USER := { STRING | NUMBER }\n"); > + fprintf(stderr, " GROUP := { STRING | NUMBER }\n"); > + exit(-1); > +} > + > +static int tap_add_ioctl(struct ifreq *ifr, uid_t uid, gid_t gid) > +{ > + int fd = open(TUNDEV, O_RDWR); > + int ret = -1; > + > +#ifndef IFF_TUN_EXCL > +#define IFF_TUN_EXCL 0x8000 > +#endif This shouldn't be here. It should be defined in include/linux/if_tun.h in kernel source and then a kernel sanitized version of if_tun.h should be put in iproute2 source for backwards compatibility. But why bother it doesn't seem to be defined or used by current kernel?? > + ifr->ifr_flags |= IFF_TUN_EXCL; Would prefer open() next to the test. > + 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; > + > +} > +static int parse_args(int argc, char **argv, struct ifreq *ifr, uid_t *uid, gid_t *gid) > +{ > + int count = 0; > + > + memset(ifr, 0, sizeof(*ifr)); > + > + ifr->ifr_flags |= IFF_NO_PI; > + > + while (argc > 0) { > + if (strcmp(*argv, "mode") == 0) { The argument parsing in ip commands uses matches() rather than strcmp to allow for partial completion. > + NEXT_ARG(); > + if (strcmp(*argv, "tun") == 0) { > + if (ifr->ifr_flags & IFF_TAP) { > + fprintf(stderr,"You managed to ask for more than one tunnel mode.\n"); > + exit(-1); > + } > + ifr->ifr_flags |= IFF_TUN; > + } else if (strcmp(*argv, "tap") == 0) { > + if (ifr->ifr_flags & IFF_TUN) { > + fprintf(stderr,"You managed to ask for more than one tunnel mode.\n"); > + exit(-1); > + } > + ifr->ifr_flags |= IFF_TAP; > + } else { > + fprintf(stderr,"Cannot guess tunnel mode.\n"); > + exit(-1); > + } > + } else if (uid && strcmp(*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); > + if (!pw) { > + fprintf(stderr, "invalid user \"%s\"\n", *argv); > + exit(-1); > + } > + *uid = pw->pw_uid; > + } > + } else if (gid && strcmp(*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); > + if (!gr) { > + fprintf(stderr, "invalid group \"%s\"\n", *argv); > + exit(-1); > + } > + *gid = gr->gr_gid; > + } > + } else if (strcmp(*argv, "pi") == 0) { > + ifr->ifr_flags &= ~IFF_NO_PI; > + } else if (strcmp(*argv, "one_queue") == 0) { > + ifr->ifr_flags |= IFF_ONE_QUEUE; > + } else if (strcmp(*argv, "vnet_hdr") == 0) { > + ifr->ifr_flags |= IFF_VNET_HDR; > + } else if (strcmp(*argv, "dev") == 0) { > + NEXT_ARG(); > + strncpy(ifr->ifr_name, *argv, IFNAMSIZ-1); > + } else { > + if (strcmp(*argv, "name") == 0) { > + NEXT_ARG(); > + } else if (matches(*argv, "help") == 0) > + usage(); > + if (ifr->ifr_name[0]) > + duparg2("name", *argv); > + strncpy(ifr->ifr_name, *argv, IFNAMSIZ); > + } > + count++; > + argc--; argv++; > + } > + > + 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; > + > + if (!(ifr.ifr_flags & TUN_TYPE_MASK)) { > + fprintf(stderr, "You failed to specify a tunnel mode\n"); > + 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 int read_prop(char *dev, char *prop, long *value) > +{ > + char fname[IFNAMSIZ+25], buf[80], *endp; > + ssize_t len; > + int fd; > + long result; > + > + sprintf(fname, "/sys/class/net/%s/%s", dev, prop); > + fd = open(fname, O_RDONLY); > + if (fd < 0) { > + if (strcmp(prop, "tun_flags")) > + fprintf(stderr, "open %s: %s\n", fname, strerror(errno)); > + return -1; > + } > + len = read(fd, buf, sizeof(buf)-1); > + close(fd); > + if (len < 0) { > + fprintf(stderr, "read %s: %s", fname, strerror(errno)); > + return -1; > + } > + > + buf[len] = 0; > + result = strtol(buf, &endp, 0); > + if (*endp != '\n') { > + fprintf(stderr, "Failed to parse %s\n", fname); > + return -1; > + } > + *value = result; > + return 0; > +} > + > +static void print_flags(long flags) > +{ > + if (flags & IFF_TUN) > + printf(" tun"); > + > + if (flags & IFF_TAP) > + printf(" tap"); > + > + if (!(flags & IFF_NO_PI)) > + printf(" pi"); > + > + if (flags & IFF_ONE_QUEUE) > + printf(" one_queue"); > + > + if (flags & IFF_VNET_HDR) > + printf(" vnet_hdr"); > + > + flags &= ~(IFF_TUN|IFF_TAP|IFF_NO_PI|IFF_ONE_QUEUE|IFF_VNET_HDR); > + if (flags) > + printf(" UNKNOWN_FLAGS:%x", flags); > +} > + > +static int do_show(int argc, char **argv) > +{ > + DIR *dir; > + struct dirent *d; > + long flags, owner = -1, group = -1; > + int i; > + > + dir = opendir("/sys/class/net"); > + if (!dir) { > + perror("opendir"); > + return -1; > + } > + while ((d = readdir(dir))) { > + if (d->d_name[0] == '.' && > + (d->d_name[1] == 0 || d->d_name[1] == '.')) > + continue; > + > + if (read_prop(d->d_name, "tun_flags", &flags)) > + continue; > + > + read_prop(d->d_name, "owner", &owner); > + read_prop(d->d_name, "group", &group); > + > + printf("%s:", d->d_name); > + print_flags(flags); > + if (owner != -1) > + printf(" user %ld", owner); > + if (group != -1) > + printf(" group %ld", group); > + printf("\n"); > + } > + return 0; > +} > + > +int do_iptuntap(int argc, char **argv) > +{ > + if (argc > 0) { > + if (matches(*argv, "add") == 0) > + return do_add(argc-1, argv+1); > + if (matches(*argv, "del") == 0) > + return do_del(argc-1, argv+1); > + if (matches(*argv, "show") == 0 || > + matches(*argv, "lst") == 0 || > + matches(*argv, "list") == 0) > + return do_show(argc-1, argv+1); > + 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); > +} >
diff --git a/ip/Makefile b/ip/Makefile index 98ba876..e868f3e 100644 --- a/ip/Makefile +++ b/ip/Makefile @@ -1,6 +1,6 @@ IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o \ rtm_map.o iptunnel.o ip6tunnel.o tunnel.o ipneigh.o ipntable.o iplink.o \ - ipmaddr.o ipmonitor.o ipmroute.o ipprefix.o \ + ipmaddr.o ipmonitor.o ipmroute.o ipprefix.o iptuntap.o \ ipxfrm.o xfrm_state.o xfrm_policy.o xfrm_monitor.o \ iplink_vlan.o link_veth.o link_gre.o diff --git a/ip/ip.c b/ip/ip.c index 2bd54b2..d846a76 100644 --- a/ip/ip.c +++ b/ip/ip.c @@ -47,7 +47,7 @@ static void usage(void) "Usage: ip [ OPTIONS ] OBJECT { COMMAND | help }\n" " ip [ -force ] -batch filename\n" "where OBJECT := { link | addr | addrlabel | route | rule | neigh | ntable |\n" -" tunnel | maddr | mroute | monitor | xfrm }\n" +" tunnel | tuntap | maddr | mroute | monitor | xfrm }\n" " OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |\n" " -f[amily] { inet | inet6 | ipx | dnet | link } |\n" " -o[neline] | -t[imestamp] | -b[atch] [filename] }\n"); @@ -75,6 +75,8 @@ static const struct cmd { { "link", do_iplink }, { "tunnel", do_iptunnel }, { "tunl", do_iptunnel }, + { "tuntap", do_iptuntap }, + { "tap", do_iptuntap }, { "monitor", do_ipmonitor }, { "xfrm", do_xfrm }, { "mroute", do_multiroute }, diff --git a/ip/ip_common.h b/ip/ip_common.h index 273065f..c857667 100644 --- a/ip/ip_common.h +++ b/ip/ip_common.h @@ -32,6 +32,7 @@ extern int do_ipneigh(int argc, char **argv); extern int do_ipntable(int argc, char **argv); extern int do_iptunnel(int argc, char **argv); extern int do_ip6tunnel(int argc, char **argv); +extern int do_iptuntap(int argc, char **argv); extern int do_iplink(int argc, char **argv); extern int do_ipmonitor(int argc, char **argv); extern int do_multiaddr(int argc, char **argv); diff --git a/ip/iptuntap.c b/ip/iptuntap.c new file mode 100644 index 0000000..b480296 --- /dev/null +++ b/ip/iptuntap.c @@ -0,0 +1,322 @@ +/* + * 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> +#include <pwd.h> +#include <grp.h> +#include <fcntl.h> +#include <dirent.h> +#include <errno.h> + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" + +#define TUNDEV "/dev/net/tun" + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, "Usage: ip tuntap { add | del } [ dev PHYS_DEV ] \n"); + fprintf(stderr, " [ mode { tun | tap } ] [ user USER ] [ group GROUP ]\n"); + fprintf(stderr, " [ one_queue ] [ no_pi ] [ vnet_hdr ]\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Where: USER := { STRING | NUMBER }\n"); + fprintf(stderr, " GROUP := { STRING | NUMBER }\n"); + exit(-1); +} + +static int tap_add_ioctl(struct ifreq *ifr, uid_t uid, gid_t gid) +{ + int fd = open(TUNDEV, O_RDWR); + int ret = -1; + +#ifndef IFF_TUN_EXCL +#define IFF_TUN_EXCL 0x8000 +#endif + ifr->ifr_flags |= IFF_TUN_EXCL; + + 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; + +} +static int parse_args(int argc, char **argv, struct ifreq *ifr, uid_t *uid, gid_t *gid) +{ + int count = 0; + + memset(ifr, 0, sizeof(*ifr)); + + ifr->ifr_flags |= IFF_NO_PI; + + while (argc > 0) { + if (strcmp(*argv, "mode") == 0) { + NEXT_ARG(); + if (strcmp(*argv, "tun") == 0) { + if (ifr->ifr_flags & IFF_TAP) { + fprintf(stderr,"You managed to ask for more than one tunnel mode.\n"); + exit(-1); + } + ifr->ifr_flags |= IFF_TUN; + } else if (strcmp(*argv, "tap") == 0) { + if (ifr->ifr_flags & IFF_TUN) { + fprintf(stderr,"You managed to ask for more than one tunnel mode.\n"); + exit(-1); + } + ifr->ifr_flags |= IFF_TAP; + } else { + fprintf(stderr,"Cannot guess tunnel mode.\n"); + exit(-1); + } + } else if (uid && strcmp(*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); + if (!pw) { + fprintf(stderr, "invalid user \"%s\"\n", *argv); + exit(-1); + } + *uid = pw->pw_uid; + } + } else if (gid && strcmp(*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); + if (!gr) { + fprintf(stderr, "invalid group \"%s\"\n", *argv); + exit(-1); + } + *gid = gr->gr_gid; + } + } else if (strcmp(*argv, "pi") == 0) { + ifr->ifr_flags &= ~IFF_NO_PI; + } else if (strcmp(*argv, "one_queue") == 0) { + ifr->ifr_flags |= IFF_ONE_QUEUE; + } else if (strcmp(*argv, "vnet_hdr") == 0) { + ifr->ifr_flags |= IFF_VNET_HDR; + } else if (strcmp(*argv, "dev") == 0) { + NEXT_ARG(); + strncpy(ifr->ifr_name, *argv, IFNAMSIZ-1); + } else { + if (strcmp(*argv, "name") == 0) { + NEXT_ARG(); + } else if (matches(*argv, "help") == 0) + usage(); + if (ifr->ifr_name[0]) + duparg2("name", *argv); + strncpy(ifr->ifr_name, *argv, IFNAMSIZ); + } + count++; + argc--; argv++; + } + + 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; + + if (!(ifr.ifr_flags & TUN_TYPE_MASK)) { + fprintf(stderr, "You failed to specify a tunnel mode\n"); + 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 int read_prop(char *dev, char *prop, long *value) +{ + char fname[IFNAMSIZ+25], buf[80], *endp; + ssize_t len; + int fd; + long result; + + sprintf(fname, "/sys/class/net/%s/%s", dev, prop); + fd = open(fname, O_RDONLY); + if (fd < 0) { + if (strcmp(prop, "tun_flags")) + fprintf(stderr, "open %s: %s\n", fname, strerror(errno)); + return -1; + } + len = read(fd, buf, sizeof(buf)-1); + close(fd); + if (len < 0) { + fprintf(stderr, "read %s: %s", fname, strerror(errno)); + return -1; + } + + buf[len] = 0; + result = strtol(buf, &endp, 0); + if (*endp != '\n') { + fprintf(stderr, "Failed to parse %s\n", fname); + return -1; + } + *value = result; + return 0; +} + +static void print_flags(long flags) +{ + if (flags & IFF_TUN) + printf(" tun"); + + if (flags & IFF_TAP) + printf(" tap"); + + if (!(flags & IFF_NO_PI)) + printf(" pi"); + + if (flags & IFF_ONE_QUEUE) + printf(" one_queue"); + + if (flags & IFF_VNET_HDR) + printf(" vnet_hdr"); + + flags &= ~(IFF_TUN|IFF_TAP|IFF_NO_PI|IFF_ONE_QUEUE|IFF_VNET_HDR); + if (flags) + printf(" UNKNOWN_FLAGS:%x", flags); +} + +static int do_show(int argc, char **argv) +{ + DIR *dir; + struct dirent *d; + long flags, owner = -1, group = -1; + int i; + + dir = opendir("/sys/class/net"); + if (!dir) { + perror("opendir"); + return -1; + } + while ((d = readdir(dir))) { + if (d->d_name[0] == '.' && + (d->d_name[1] == 0 || d->d_name[1] == '.')) + continue; + + if (read_prop(d->d_name, "tun_flags", &flags)) + continue; + + read_prop(d->d_name, "owner", &owner); + read_prop(d->d_name, "group", &group); + + printf("%s:", d->d_name); + print_flags(flags); + if (owner != -1) + printf(" user %ld", owner); + if (group != -1) + printf(" group %ld", group); + printf("\n"); + } + return 0; +} + +int do_iptuntap(int argc, char **argv) +{ + if (argc > 0) { + if (matches(*argv, "add") == 0) + return do_add(argc-1, argv+1); + if (matches(*argv, "del") == 0) + return do_del(argc-1, argv+1); + if (matches(*argv, "show") == 0 || + matches(*argv, "lst") == 0 || + matches(*argv, "list") == 0) + return do_show(argc-1, argv+1); + 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); +}