Skip to contentSkip to conversation

Virtual ethernet device (v.4)

PATCH In review Pavel Emelyanov 2 participants 8 messages Jul 19, 2007 – Aug 9, 2007

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 Virtual ethernet device (v.4)

Veth stands for Virtual ETHernet. It is a simple tunnel driver that works at the link layer and looks like a pair of ethernet devices interconnected with each other.

Mainly it allows to communicate between network namespaces but it can be used as is as well. E.g. one may join to bridged networking segments together.

Eric recently sent a similar driver called etun with the sysfs
interface. This implementation uses another interface - the RTM_NRELINK message introduced by Patric.

The newlink callback is organized that way to make it easy to create the peer device in the separate namespace when we have them in kernel.

Many thanks to Patrick for reviewing the patches and his advises on how to make driver cleaner.

Changes from v.3:
* Reserved place for struct ifinfomsg in IFLA_INFO_DATA part of the packet. This is not used yet, but may be in the future.

Changes from v.2.1:
* Made the generic routine for link creation to be used by veth driver, any other tunnel driver that needs to create several devices at once and rtnl_newlink() code.

Changes from v.2:
* Rebase over latest netdev tree. No actual changes;
* Small code rework.

Changes from v.1:
* Per-cpu statistics;
* Standard convention for nla policy names;
* Module alias added;
* Xmit function fixes noticed by Patric;
* Code cleanup.

The patch for an ip utility is also provided.

#1 lore raw

PATCH 1/2 Introduce the generic rtnl_create_link()

This routine gets the parsed rtnl attributes and creates a new link with generic info (IFLA_LINKINFO policy). Its intention
is to help the drivers, that need to create several links at once (like VETH).

This is nothing but a copy-paste-ed part of rtnl_newlink() function that is responsible for creation of new device.

---

include/net/rtnetlink.h | 4 ++
net/core/rtnetlink.c | 83 ++++++++++++++++++++++++++++++------------------

2 files changed, 57 insertions(+), 30 deletions(-)
include/net/rtnetlink.h +0 -0
index 3861c05..8218288 100644
@@ -78,6 +78,10 @@ extern void	__rtnl_link_unregister(struc

extern int rtnl_link_register(struct rtnl_link_ops *ops);
extern void rtnl_link_unregister(struct rtnl_link_ops *ops);

+extern struct net_device *rtnl_create_link(char *ifname,
+ const struct rtnl_link_ops *ops, struct nlattr *tb[]);
+extern const struct nla_policy ifla_policy[IFLA_MAX+1]; +
#define MODULE_ALIAS_RTNL_LINK(kind) MODULE_ALIAS("rtnl-link-" kind)

#endif

net/core/rtnetlink.c +0 -0
index 864cbdf..02e00e8 100644
@@ -714,7 +714,7 @@ cont:
	return skb->len;
}

-static const struct nla_policy ifla_policy[IFLA_MAX+1] = {

+const struct nla_policy ifla_policy[IFLA_MAX+1] = {
	[IFLA_IFNAME]		= { .type = NLA_STRING, .len = IFNAMSIZ-1 },
	[IFLA_ADDRESS]		= { .type = NLA_BINARY, .len = MAX_ADDR_LEN },
	[IFLA_BROADCAST]	= { .type = NLA_BINARY, .len = MAX_ADDR_LEN },
@@ -941,6 +941,50 @@ static int rtnl_dellink(struct sk_buff *
	return 0;
}

+struct net_device *rtnl_create_link(char *ifname,
+ const struct rtnl_link_ops *ops, struct nlattr *tb[])
+{
+ int err;
+ struct net_device *dev;
+
+ err = -ENOMEM;
+ dev = alloc_netdev(ops->priv_size, ifname, ops->setup);
+ if (!dev)
+ goto err;
+
+ if (strchr(dev->name, '%')) {
+ err = dev_alloc_name(dev, dev->name);
+ if (err < 0)
+ goto err_free;
+ }
+
+ dev->rtnl_link_ops = ops;
+
+ if (tb[IFLA_MTU])
+ dev->mtu = nla_get_u32(tb[IFLA_MTU]);
+ if (tb[IFLA_ADDRESS])
+ memcpy(dev->dev_addr, nla_data(tb[IFLA_ADDRESS]),
+ nla_len(tb[IFLA_ADDRESS]));
+ if (tb[IFLA_BROADCAST])
+ memcpy(dev->broadcast, nla_data(tb[IFLA_BROADCAST]),
+ nla_len(tb[IFLA_BROADCAST]));
+ if (tb[IFLA_TXQLEN])
+ dev->tx_queue_len = nla_get_u32(tb[IFLA_TXQLEN]);
+ if (tb[IFLA_WEIGHT])
+ dev->weight = nla_get_u32(tb[IFLA_WEIGHT]);
+ if (tb[IFLA_OPERSTATE])
+ set_operstate(dev, nla_get_u8(tb[IFLA_OPERSTATE]));
+ if (tb[IFLA_LINKMODE])
+ dev->link_mode = nla_get_u8(tb[IFLA_LINKMODE]);
+
+ return dev;
+
+err_free:
+ free_netdev(dev);
+err:
+ return ERR_PTR(err);
+}
+
static int rtnl_newlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)

{
	const struct rtnl_link_ops *ops;

@@ -1051,40 +1095,17 @@ replay:

		if (!ifname[0])
			snprintf(ifname, IFNAMSIZ, "%s%%d", ops->kind);

- dev = alloc_netdev(ops->priv_size, ifname, ops->setup);
- if (!dev)
- return -ENOMEM;
-
- if (strchr(dev->name, '%')) {
- err = dev_alloc_name(dev, dev->name);
- if (err < 0)
- goto err_free;
- }
- dev->rtnl_link_ops = ops;

- if (tb[IFLA_MTU])
- dev->mtu = nla_get_u32(tb[IFLA_MTU]);
- if (tb[IFLA_ADDRESS])
- memcpy(dev->dev_addr, nla_data(tb[IFLA_ADDRESS]),
- nla_len(tb[IFLA_ADDRESS]));
- if (tb[IFLA_BROADCAST])
- memcpy(dev->broadcast, nla_data(tb[IFLA_BROADCAST]),
- nla_len(tb[IFLA_BROADCAST]));
- if (tb[IFLA_TXQLEN])
- dev->tx_queue_len = nla_get_u32(tb[IFLA_TXQLEN]);
- if (tb[IFLA_WEIGHT])
- dev->weight = nla_get_u32(tb[IFLA_WEIGHT]);
- if (tb[IFLA_OPERSTATE])
- set_operstate(dev, nla_get_u8(tb[IFLA_OPERSTATE]));
- if (tb[IFLA_LINKMODE])
- dev->link_mode = nla_get_u8(tb[IFLA_LINKMODE]);
+ dev = rtnl_create_link(ifname, ops, tb);

- if (ops->newlink)
+ if (IS_ERR(dev))
+ err = PTR_ERR(dev);
+ else if (ops->newlink)

			err = ops->newlink(dev, tb, data);
		else
			err = register_netdevice(dev);

-err_free:
- if (err < 0)
+
+ if (err < 0 && !IS_ERR(dev))

			free_netdev(dev);
		return err;
	}

@@ -1333,3 +1354,5 @@ EXPORT_SYMBOL(rtnl_unlock);
EXPORT_SYMBOL(rtnl_unicast);
EXPORT_SYMBOL(rtnl_notify);
EXPORT_SYMBOL(rtnl_set_sk_err);
+EXPORT_SYMBOL(rtnl_create_link);
+EXPORT_SYMBOL(ifla_policy);

#2 lore raw

PATCH 2/2 Virtual ethernet device driver

In reply to Pavel Emelyanov

Veth stands for Virtual ETHernet. It is a simple tunnel driver that works at the link layer and looks like a pair of ethernet devices interconnected with each other.

Mainly it allows to communicate between network namespaces but it can be used as is as well.

The newlink callback is organized that way to make it easy to create the peer device in the separate namespace when we have them in kernel.

This implementation uses another interface - the RTM_NRELINK message introduced by Patric.

---

drivers/net/Kconfig | 6 drivers/net/Makefile | 1 drivers/net/veth.c | 475 +++++++++++++++++++++++++++++++++++++++++++++++++++
include/net/veth.h | 12 +

4 files changed, 494 insertions(+)
drivers/net/Kconfig +0 -0
index 43d0317..ccfc002 100644
@@ -134,6 +134,12 @@ config TUN
 

If you don't know what to use this for, you don't need it.

+config VETH
+ tristate "Virtual ethernet device"
+ ---help---
+ The device is an ethernet tunnel. Devices are created in pairs. When
+ one end receives the packet it appears on its pair and vice versa.
+
config NET_SB1000
tristate "General Instruments Surfboard 1000" depends on PNP

drivers/net/Makefile +0 -0
index eb41676..32db559 100644
@@ -189,6 +189,7 @@ obj-$(CONFIG_MACSONIC) += macsonic.o

obj-$(CONFIG_MACMACE) += macmace.o
obj-$(CONFIG_MAC89x0) += mac89x0.o
obj-$(CONFIG_TUN) += tun.o
+obj-$(CONFIG_VETH) += veth.o
obj-$(CONFIG_NET_NETX) += netx-eth.o
obj-$(CONFIG_DL2K) += dl2k.o
obj-$(CONFIG_R8169) += r8169.o

drivers/net/veth.c new file +475 -0

475 lines not on this page · raw message

include/net/veth.h new file +12 -0
new file mode 100644
index 0000000..3354c1e
@@ -0,0 +1,12 @@
+#ifndef __NET_VETH_H_
+#define __NET_VETH_H_
+
+enum {
+	VETH_INFO_UNSPEC,
+	VETH_INFO_PEER,
+
+	__VETH_INFO_MAX
+#define VETH_INFO_MAX	(__VETH_INFO_MAX - 1)
+};
+
+#endif
#4 lore raw

PATCH 1/2 Introduce iplink_parse() routine

This routine parses CLI attributes, describing generic link parameters such as name, address, etc.

This is mostly copy-pasted from iplink_modify().

---

include/utils.h | 3 +
ip/iplink.c | 127 ++++++++++++++++++++++++++++++++------------------------

2 files changed, 76 insertions(+), 54 deletions(-)
include/utils.h +0 -0
index a3fd335..3fd851d 100644
@@ -146,4 +146,7 @@ extern int cmdlineno;

extern size_t getcmdline(char **line, size_t *len, FILE *in); extern int makeargs(char *line, char *argv[], int maxargs);

+struct iplink_req;
+int iplink_parse(int argc, char **argv, struct iplink_req *req,
+ char **name, char **type, char **link, char **dev);
#endif /* __UTILS_H__ */

ip/iplink.c +0 -0
index cfacdab..e0e0d85 100644
@@ -142,140 +142,159 @@ static int iplink_have_newlink(void)

}
#endif /* ! IPLINK_IOCTL_COMPAT */

-static int iplink_modify(int cmd, unsigned int flags, int argc, char **argv) +struct iplink_req {
+ struct nlmsghdr n;
+ struct ifinfomsg i;
+ char buf[1024];
+};
+
+int iplink_parse(int argc, char **argv, struct iplink_req *req,
+ char **name, char **type, char **link, char **dev)
{
+ int ret, len;

+	char abuf[32];
	int qlen = -1;
	int mtu = -1;

- int len;
- char abuf[32];
- char *dev = NULL;
- char *name = NULL;
- char *link = NULL;
- char *type = NULL;
- struct link_util *lu = NULL;
- struct {
- struct nlmsghdr n;
- struct ifinfomsg i;
- char buf[1024];
- } req;

- memset(&req, 0, sizeof(req));
-
- req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
- req.n.nlmsg_flags = NLM_F_REQUEST|flags;
- req.n.nlmsg_type = cmd;
- req.i.ifi_family = preferred_family;
+ ret = argc;

	while (argc > 0) {
		if (strcmp(*argv, "up") == 0) {
-			req.i.ifi_change |= IFF_UP;
-			req.i.ifi_flags |= IFF_UP;
+			req->i.ifi_change |= IFF_UP;
+			req->i.ifi_flags |= IFF_UP;
		} else if (strcmp(*argv, "down") == 0) {
-			req.i.ifi_change |= IFF_UP;
-			req.i.ifi_flags &= ~IFF_UP;
+			req->i.ifi_change |= IFF_UP;
+			req->i.ifi_flags &= ~IFF_UP;
		} else if (strcmp(*argv, "name") == 0) {
			NEXT_ARG();
-			name = *argv;
+			*name = *argv;
		} else if (matches(*argv, "link") == 0) {
			NEXT_ARG();
-			link = *argv;
+			*link = *argv;
		} else if (matches(*argv, "address") == 0) {
			NEXT_ARG();
			len = ll_addr_a2n(abuf, sizeof(abuf), *argv);
-			addattr_l(&req.n, sizeof(req), IFLA_ADDRESS, abuf, len);
+			addattr_l(&req->n, sizeof(*req), IFLA_ADDRESS, abuf, len);
		} else if (matches(*argv, "broadcast") == 0 ||
-			   strcmp(*argv, "brd") == 0) {
+				strcmp(*argv, "brd") == 0) {
			NEXT_ARG();
			len = ll_addr_a2n(abuf, sizeof(abuf), *argv);
-			addattr_l(&req.n, sizeof(req), IFLA_BROADCAST, abuf, len);
+			addattr_l(&req->n, sizeof(*req), IFLA_BROADCAST, abuf, len);
		} else if (matches(*argv, "txqueuelen") == 0 ||
-			   strcmp(*argv, "qlen") == 0 ||
-			   matches(*argv, "txqlen") == 0) {
+				strcmp(*argv, "qlen") == 0 ||
+				matches(*argv, "txqlen") == 0) {
			NEXT_ARG();
			if (qlen != -1)
				duparg("txqueuelen", *argv);
			if (get_integer(&qlen,  *argv, 0))
				invarg("Invalid \"txqueuelen\" value\n", *argv);
-			addattr_l(&req.n, sizeof(req), IFLA_TXQLEN, &qlen, 4);
+			addattr_l(&req->n, sizeof(*req), IFLA_TXQLEN, &qlen, 4);
		} else if (strcmp(*argv, "mtu") == 0) {
			NEXT_ARG();
			if (mtu != -1)
				duparg("mtu", *argv);
			if (get_integer(&mtu, *argv, 0))
				invarg("Invalid \"mtu\" value\n", *argv);
-			addattr_l(&req.n, sizeof(req), IFLA_MTU, &mtu, 4);
+			addattr_l(&req->n, sizeof(*req), IFLA_MTU, &mtu, 4);
		} else if (strcmp(*argv, "multicast") == 0) {
			NEXT_ARG();
-			req.i.ifi_change |= IFF_MULTICAST;
+			req->i.ifi_change |= IFF_MULTICAST;
			if (strcmp(*argv, "on") == 0) {
-				req.i.ifi_flags |= IFF_MULTICAST;
+				req->i.ifi_flags |= IFF_MULTICAST;
			} else if (strcmp(*argv, "off") == 0) {
-				req.i.ifi_flags &= ~IFF_MULTICAST;
+				req->i.ifi_flags &= ~IFF_MULTICAST;
			} else
				return on_off("multicast");
		} else if (strcmp(*argv, "allmulticast") == 0) {
			NEXT_ARG();
-			req.i.ifi_change |= IFF_ALLMULTI;
+			req->i.ifi_change |= IFF_ALLMULTI;
			if (strcmp(*argv, "on") == 0) {
-				req.i.ifi_flags |= IFF_ALLMULTI;
+				req->i.ifi_flags |= IFF_ALLMULTI;
			} else if (strcmp(*argv, "off") == 0) {
-				req.i.ifi_flags &= ~IFF_ALLMULTI;
+				req->i.ifi_flags &= ~IFF_ALLMULTI;
			} else
				return on_off("allmulticast");
		} else if (strcmp(*argv, "promisc") == 0) {
			NEXT_ARG();
-			req.i.ifi_change |= IFF_PROMISC;
+			req->i.ifi_change |= IFF_PROMISC;
			if (strcmp(*argv, "on") == 0) {
-				req.i.ifi_flags |= IFF_PROMISC;
+				req->i.ifi_flags |= IFF_PROMISC;
			} else if (strcmp(*argv, "off") == 0) {
-				req.i.ifi_flags &= ~IFF_PROMISC;
+				req->i.ifi_flags &= ~IFF_PROMISC;
			} else
				return on_off("promisc");
		} else if (strcmp(*argv, "trailers") == 0) {
			NEXT_ARG();
-			req.i.ifi_change |= IFF_NOTRAILERS;
+			req->i.ifi_change |= IFF_NOTRAILERS;
			if (strcmp(*argv, "off") == 0) {
-				req.i.ifi_flags |= IFF_NOTRAILERS;
+				req->i.ifi_flags |= IFF_NOTRAILERS;
			} else if (strcmp(*argv, "on") == 0) {
-				req.i.ifi_flags &= ~IFF_NOTRAILERS;
+				req->i.ifi_flags &= ~IFF_NOTRAILERS;
			} else
				return on_off("trailers");
		} else if (strcmp(*argv, "arp") == 0) {
			NEXT_ARG();
-			req.i.ifi_change |= IFF_NOARP;
+			req->i.ifi_change |= IFF_NOARP;
			if (strcmp(*argv, "on") == 0) {
-				req.i.ifi_flags &= ~IFF_NOARP;
+				req->i.ifi_flags &= ~IFF_NOARP;
			} else if (strcmp(*argv, "off") == 0) {
-				req.i.ifi_flags |= IFF_NOARP;
+				req->i.ifi_flags |= IFF_NOARP;
			} else
				return on_off("noarp");
#ifdef IFF_DYNAMIC
		} else if (matches(*argv, "dynamic") == 0) {
			NEXT_ARG();
-			req.i.ifi_change |= IFF_DYNAMIC;
+			req->i.ifi_change |= IFF_DYNAMIC;
			if (strcmp(*argv, "on") == 0) {
-				req.i.ifi_flags |= IFF_DYNAMIC;
+				req->i.ifi_flags |= IFF_DYNAMIC;
			} else if (strcmp(*argv, "off") == 0) {
-				req.i.ifi_flags &= ~IFF_DYNAMIC;
+				req->i.ifi_flags &= ~IFF_DYNAMIC;
			} else
				return on_off("dynamic");
#endif
		} else if (matches(*argv, "type") == 0) {
			NEXT_ARG();
-			type = *argv;
+			*type = *argv;
			argc--; argv++;
			break;
		} else {
-                        if (strcmp(*argv, "dev") == 0) {
+			if (strcmp(*argv, "dev") == 0) {
				NEXT_ARG();
			}
-			if (dev)
+			if (*dev)
				duparg2("dev", *argv);
-			dev = *argv;
+			*dev = *argv;
		}
		argc--; argv++;
	}

+ return ret - argc;
+}
+
+static int iplink_modify(int cmd, unsigned int flags, int argc, char **argv) +{
+ int len;
+ char *dev = NULL;
+ char *name = NULL;
+ char *link = NULL;
+ char *type = NULL;
+ struct link_util *lu = NULL;
+ struct iplink_req req;
+ int ret;
+
+ memset(&req, 0, sizeof(req));
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST|flags;
+ req.n.nlmsg_type = cmd;
+ req.i.ifi_family = preferred_family;
+
+ ret = iplink_parse(argc, argv, &req, &name, &type, &link, &dev);
+ if (ret < 0)
+ return ret;
+
+ argc -= ret;

+	argv += ret;
	ll_init_map(&rth);

	if (type) {
#5 lore raw

PATCH 2/2 Module for ip utility to support veth device

In reply to Pavel Emelyanov

The link_veth.so itself.

---

Makefile    |    6 ++++-
link_veth.c |   63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
veth.h      |   12 +++++++++++
3 files changed, 80 insertions(+), 1 deletion(-)
ip/Makefile +0 -0
index 9a5bfe3..b46bce3 100644
@@ -8,8 +8,9 @@ RTMONOBJ=rtmon.o

ALLOBJ=$(IPOBJ) $(RTMONOBJ)
SCRIPTS=ifcfg rtpr routel routef
TARGETS=ip rtmon
+LIBS=link_veth.so

-all: $(TARGETS) $(SCRIPTS)
+all: $(TARGETS) $(SCRIPTS) $(LIBS)

ip: $(IPOBJ) $(LIBNETLINK) $(LIBUTIL)

@@ -24,3 +25,6 @@ clean:

LDLIBS += -ldl
LDFLAGS += -Wl,-export-dynamic
+
+%.so: %.c
+ $(CC) $(CFLAGS) -shared $< -o $@

ip/link_veth.c new file +63 -0

63 lines not on this page · raw message

ip/veth.h new file +12 -0
new file mode 100644
index 0000000..aa2e6f9
@@ -0,0 +1,12 @@
+#ifndef __NET_VETH_H__
+#define __NET_VETH_H__
+
+enum {
+	VETH_INFO_UNSPEC,
+	VETH_INFO_PEER,
+
+	__VETH_INFO_MAX
+#define VETH_INFO_MAX	(__VETH_INFO_MAX - 1)
+};
+
+#endif
#6 lore raw
In reply to Pavel Emelyanov

Hi, David.

What are your plans about this driver?

Thanks,
Pavel

Quoted 42 lines

Veth stands for Virtual ETHernet. It is a simple tunnel driver that works at the link layer and looks like a pair of ethernet devices interconnected with each other.

Mainly it allows to communicate between network namespaces but it can be used as is as well. E.g. one may join to bridged networking segments together.

Eric recently sent a similar driver called etun with the sysfs
interface. This implementation uses another interface - the RTM_NRELINK message introduced by Patric.

The newlink callback is organized that way to make it easy to create the peer device in the separate namespace when we have them in kernel.

Many thanks to Patrick for reviewing the patches and his advises on how to make driver cleaner.

Changes from v.3:
* Reserved place for struct ifinfomsg in IFLA_INFO_DATA part of the packet. This is not used yet, but may be in the future.

Changes from v.2.1:
* Made the generic routine for link creation to be used by veth driver, any other tunnel driver that needs to create several devices at once and rtnl_newlink() code.

Changes from v.2:
* Rebase over latest netdev tree. No actual changes;
* Small code rework.

Changes from v.1:
* Per-cpu statistics;
* Standard convention for nla policy names;
* Module alias added;
* Xmit function fixes noticed by Patric;
* Code cleanup.

The patch for an ip utility is also provided.

maintainer
#7 lore raw

From: Pavel Emelyanov <[email protected]>
Date: Wed, 08 Aug 2007 14:42:50 +0400

Quoted 1 line

What are your plans about this driver?

I've added it to my net-2.6.24 which I'm building to night.