diff mbox

[iproute2] : Add 'ip tuntap' facility for managing tun/tap devices

Message ID 1241433778.6126.90.camel@macbook.infradead.org
State Not Applicable, archived
Delegated to: David Miller
Headers show

Commit Message

David Woodhouse May 4, 2009, 10:42 a.m. UTC
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.

Comments

stephen hemminger May 4, 2009, 2:38 p.m. UTC | #1
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
David Woodhouse May 4, 2009, 2:49 p.m. UTC | #2
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?
David Woodhouse May 27, 2009, 4:32 p.m. UTC | #3
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?
stephen hemminger May 27, 2009, 4:38 p.m. UTC | #4
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.
David Woodhouse May 27, 2009, 4:49 p.m. UTC | #5
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?
stephen hemminger May 27, 2009, 5:06 p.m. UTC | #6
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?
David Miller May 27, 2009, 8:43 p.m. UTC | #7
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
David Miller May 27, 2009, 8:44 p.m. UTC | #8
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
stephen hemminger May 28, 2009, 4:58 a.m. UTC | #9
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 mbox

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;
+}
+
+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);
+}