* [RFC PATCH] EtherIP tunnel driver (RFC 3387)
@ 2006-09-01 15:13 Joerg Roedel
2006-09-01 16:24 ` Patrick McHardy
0 siblings, 1 reply; 4+ messages in thread
From: Joerg Roedel @ 2006-09-01 15:13 UTC (permalink / raw)
To: netdev
[-- Attachment #1: Type: text/plain, Size: 1357 bytes --]
Greetings,
Upon request of a BSD user I wrote an EtherIP tunnel driver for the
Linux Kernel. With this driver it is possible to tunnel Ethernet packets
over IPv4 networks using the EtherIP protocol defined in RFC 3378.
This RFC defines the EtherIP header to be a 16 bit field with the first
4 bits set to 0011. This is a bit unclear and left room for
interpretation. I interpreted that as 0x30 0x00 as the header value for
the protocol. But since NetBSD and OpenBSD use 0x03 0x00 there, I
implemented it this way to keep interoperability. But due the unclear
RFC this driver does not check the EtherIP header of the incoming
packets as required by the RFC.
The driver was written with an eye on the IP tunnel driver and follows
its behavior were this makes sense.
This protocol is also implemented by different BSD versions (I know of
OpenBSD, NetBSD and FreeBSD) and is in use there. Actually, this driver is
tested against itself and the EtherIP implementation of NetBSD (both
on i386 hardware). It was also tested on a 32 bit HP-PA machine.
Currently the tunnels must be configured using a seperate tool. This can
be found online at
http://www-user.tu-chemnitz.de/~roej/projects/etherip.tar.gz .
TODO:
- make it work with multicast destinations
- integration into iproute
Regards,
Joerg Roedel
Signed-off-by: Joerg Roedel <joro-lkml@zlug.org>
[-- Attachment #2: patch_etherip --]
[-- Type: text/plain, Size: 15475 bytes --]
diff -uprN linux-2.6.17.11-vanilla/include/linux/in.h linux-2.6.17.11/include/linux/in.h
--- linux-2.6.17.11-vanilla/include/linux/in.h 2006-08-23 23:16:33.000000000 +0200
+++ linux-2.6.17.11/include/linux/in.h 2006-09-01 15:12:46.000000000 +0200
@@ -40,6 +40,7 @@ enum {
IPPROTO_ESP = 50, /* Encapsulation Security Payload protocol */
IPPROTO_AH = 51, /* Authentication Header protocol */
+ IPPROTO_ETHERIP = 97, /* Ethernet over IPv4 protocol */
IPPROTO_PIM = 103, /* Protocol Independent Multicast */
IPPROTO_COMP = 108, /* Compression Header protocol */
diff -uprN linux-2.6.17.11-vanilla/net/ipv4/etherip.c linux-2.6.17.11/net/ipv4/etherip.c
--- linux-2.6.17.11-vanilla/net/ipv4/etherip.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.17.11/net/ipv4/etherip.c 2006-09-01 16:22:54.000000000 +0200
@@ -0,0 +1,538 @@
+/*
+ * etherip.c: Ethernet over IPv4 tunnel driver (according to RFC3378)
+ *
+ * This driver could be used to tunnel Ethernet packets through IPv4
+ * networks. This is especially usefull together with the bridging
+ * code in Linux.
+ *
+ * This code was written with an eye on the IPIP driver in linux from
+ * Sam Lantinga. Thanks for the great work.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 (no later version) as published by the
+ * Free Software Foundation.
+ *
+ */
+
+#include <linux/capability.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <linux/if_tunnel.h>
+#include <linux/list.h>
+#include <linux/string.h>
+#include <linux/netfilter_ipv4.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/route.h>
+#include <net/ipip.h>
+#include <net/xfrm.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Joerg Roedel <joerg@zlug.org>");
+MODULE_DESCRIPTION("Ethernet over IPv4 tunnel driver");
+
+/*
+ * These 2 defines are taken from ipip.c - if it's good enough for them
+ * it's good enough for me.
+ */
+#define HASH_SIZE 16
+#define HASH(addr) ((addr^(addr>>4))&0xF)
+
+#define ETHERIP_HEADER ((u16)0x0300)
+#define ETHERIP_HLEN 2
+
+#define BANNER1 "etherip: Ethernet over IPv4 tunneling driver\n"
+#define BANNER2 "etherip: (C) 2006 by Joerg Roedel <joerg@zlug.org>\n"
+
+struct etherip_tunnel {
+ struct list_head list;
+ struct net_device *dev;
+ struct net_device_stats stats;
+ struct ip_tunnel_parm parms;
+ unsigned int recursion;
+};
+
+static struct net_device *etherip_tunnel_dev;
+static struct list_head tunnels[HASH_SIZE];
+
+static DEFINE_RWLOCK(etherip_lock);
+
+static void etherip_tunnel_setup(struct net_device *dev);
+
+/* add a tunnel to the hash */
+static void etherip_tunnel_add(struct etherip_tunnel *tun)
+{
+ unsigned h = HASH(tun->parms.iph.daddr);
+ list_add_tail(&tun->list, &tunnels[h]);
+}
+
+/* delete a tunnel from the hash*/
+static void etherip_tunnel_del(struct etherip_tunnel *tun)
+{
+ list_del(&tun->list);
+}
+
+/* find a tunnel in the hash by parameters from userspace */
+static struct etherip_tunnel* etherip_tunnel_find(struct ip_tunnel_parm *p)
+{
+ struct list_head *ptr;
+ struct etherip_tunnel *ret;
+ unsigned h = HASH(p->iph.daddr);
+
+ list_for_each(ptr, &tunnels[h]) {
+ ret = list_entry(ptr, struct etherip_tunnel, list);
+ if (ret->parms.iph.daddr == p->iph.daddr) {
+ return ret;
+ }
+ }
+
+ return NULL;
+}
+
+/* find a tunnel by its destination address */
+static struct etherip_tunnel* etherip_tunnel_locate(u32 remote)
+{
+ struct list_head *ptr;
+ struct etherip_tunnel *ret;
+ unsigned h = HASH(remote);
+
+ list_for_each(ptr, &tunnels[h]) {
+ ret = list_entry(ptr, struct etherip_tunnel, list);
+ if (ret->parms.iph.daddr == remote) {
+ return ret;
+ }
+ }
+
+ return NULL;
+}
+
+/* netdevice start function */
+static int etherip_tunnel_open(struct net_device *dev)
+{
+ netif_start_queue(dev);
+ return 0;
+}
+
+/* netdevice stop function */
+static int etherip_tunnel_stop(struct net_device *dev)
+{
+ netif_stop_queue(dev);
+ return 0;
+}
+
+/* netdevice hard_start_xmit function
+ * it gets an Ethernet packet in skb and encapsulates it in another IP
+ * packet */
+static int etherip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct etherip_tunnel *tunnel = netdev_priv(dev);
+ struct rtable *rt;
+ struct iphdr *iph;
+ struct flowi fl;
+ struct net_device *tdev;
+ int max_headroom;
+ struct net_device_stats *stats = &tunnel->stats;
+
+ if (tunnel->recursion++) {
+ tunnel->stats.collisions++;
+ goto tx_error;
+ }
+
+ fl.oif = 0;
+ fl.nl_u.ip4_u.daddr = tunnel->parms.iph.daddr;
+ fl.nl_u.ip4_u.saddr = tunnel->parms.iph.saddr;
+ fl.nl_u.ip4_u.saddr = 0;
+ fl.proto = IPPROTO_ETHERIP;
+
+ if (ip_route_output_key(&rt, &fl)) {
+ tunnel->stats.tx_carrier_errors++;
+ goto tx_error_icmp;
+ }
+
+ tdev = rt->u.dst.dev;
+ if (tdev == dev) {
+ ip_rt_put(rt);
+ tunnel->stats.collisions++;
+ goto tx_error;
+ }
+
+ max_headroom = (LL_RESERVED_SPACE(tdev)+sizeof(struct iphdr)
+ + ETHERIP_HLEN);
+
+ if (skb_headroom(skb) < max_headroom || skb_cloned(skb)
+ || skb_shared(skb)) {
+ struct sk_buff *n_skb = skb_realloc_headroom(skb,max_headroom);
+ if (!n_skb) {
+ ip_rt_put(rt);
+ dev_kfree_skb(skb);
+ tunnel->stats.tx_dropped++;
+ return 0;
+ }
+ if (skb->sk)
+ skb_set_owner_w(n_skb, skb->sk);
+ dev_kfree_skb(skb);
+ skb = n_skb;
+ }
+
+ skb->h.raw = skb->nh.raw;
+ skb->nh.raw = skb_push(skb, sizeof(struct iphdr)+ETHERIP_HLEN);
+ memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
+ IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
+ IPSKB_REROUTED);
+ dst_release(skb->dst);
+ skb->dst = &rt->u.dst;
+
+ iph = skb->nh.iph;
+ iph->version = 4;
+ iph->ihl = sizeof(struct iphdr)>>2;
+ iph->frag_off = 0;
+ iph->protocol = IPPROTO_ETHERIP;
+ iph->tos = 0;
+ iph->daddr = rt->rt_dst;
+ iph->saddr = rt->rt_src;
+ iph->ttl = tunnel->parms.iph.ttl;
+ if (iph->ttl == 0)
+ iph->ttl = dst_metric(&rt->u.dst, RTAX_HOPLIMIT);
+
+ /* add the 16bit etherip header after the ip header */
+ *((u16*)(skb->nh.raw + sizeof(struct iphdr))) = ntohs(ETHERIP_HEADER);
+ nf_reset(skb);
+ IPTUNNEL_XMIT();
+ tunnel->dev->trans_start = jiffies;
+ tunnel->recursion--;
+
+ return 0;
+
+tx_error_icmp:
+ dst_link_failure(skb);
+
+tx_error:
+ tunnel->stats.tx_errors++;
+ dev_kfree_skb(skb);
+ tunnel->recursion--;
+ return 0;
+}
+
+/* get statistics callback */
+static struct net_device_stats *etherip_tunnel_stats(struct net_device *dev)
+{
+ struct etherip_tunnel *ethip = netdev_priv(dev);
+ return ðip->stats;
+}
+
+/* checks parameters the driver gets from userspace */
+static int etherip_param_check(struct ip_tunnel_parm *p)
+{
+ if ((p->iph.version != 4)
+ || (p->iph.protocol != IPPROTO_ETHERIP)
+ || (p->iph.ihl != 5)
+ || (p->iph.daddr == INADDR_ANY)
+ || MULTICAST(p->iph.daddr))
+ return -EINVAL;
+
+ return 0;
+}
+
+/* central ioctl function for all netdevices this driver manages
+ * it allows to create, delete, modify a tunnel and fetch tunnel
+ * information */
+static int etherip_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr,
+ int cmd)
+{
+ int err = 0;
+ struct ip_tunnel_parm p;
+ struct net_device *new_dev;
+ char *dev_name;
+ struct etherip_tunnel *t;
+
+
+ switch (cmd) {
+ case SIOCGETTUNNEL:
+ err = -EINVAL;
+ if (dev == etherip_tunnel_dev)
+ goto out;
+ t = netdev_priv(dev);
+ if (copy_to_user(ifr->ifr_ifru.ifru_data, &t->parms,
+ sizeof(t->parms)))
+ err = -EFAULT;
+ err = 0;
+ break;
+ case SIOCADDTUNNEL:
+ err = -EINVAL;
+ if (dev != etherip_tunnel_dev)
+ goto out;
+
+ case SIOCCHGTUNNEL:
+ err = -EPERM;
+ if (!capable(CAP_NET_ADMIN))
+ goto out;
+
+ err = -EFAULT;
+ if (copy_from_user(&p, ifr->ifr_ifru.ifru_data,
+ sizeof(p)))
+ goto out;
+
+ if ((err = etherip_param_check(&p)) < 0)
+ goto out;
+
+ t = etherip_tunnel_find(&p);
+
+ err = -EEXIST;
+ if ((t != NULL) && (t->dev != dev))
+ goto out;
+
+ if (cmd == SIOCADDTUNNEL) {
+
+ p.name[IFNAMSIZ-1] = 0;
+ dev_name = p.name;
+ if (dev_name[0] == 0)
+ dev_name = "ethip%d";
+
+ err = -ENOMEM;
+ new_dev = alloc_netdev(
+ sizeof(struct etherip_tunnel),
+ dev_name,
+ etherip_tunnel_setup);
+
+ if (new_dev == NULL)
+ goto out;
+
+ if (strchr(new_dev->name, '%')) {
+ err = dev_alloc_name( new_dev, new_dev->name);
+ if (err < 0)
+ goto add_err1;
+ }
+
+ t = netdev_priv(new_dev);
+ t->dev = new_dev;
+ strncpy(p.name, new_dev->name, IFNAMSIZ);
+ memcpy(&(t->parms), &p, sizeof(p));
+
+ write_lock(ðerip_lock);
+ etherip_tunnel_add(t);
+ write_unlock(ðerip_lock);
+
+ err = register_netdevice(new_dev);
+ if (err < 0)
+ goto add_err2;
+ if (copy_to_user(ifr->ifr_ifru.ifru_data, &p,
+ sizeof(p)))
+ err = -EFAULT;
+
+ } else {
+ err = -EINVAL;
+ if ((t = netdev_priv(dev)) == NULL)
+ goto out;
+ if (dev == etherip_tunnel_dev)
+ goto out;
+ write_lock(ðerip_lock);
+ memcpy(&(t->parms), &p, sizeof(p));
+ write_unlock(ðerip_lock);
+ }
+
+ err = 0;
+ break;
+add_err2:
+ write_lock(ðerip_lock);
+ etherip_tunnel_del(t);
+ write_unlock(ðerip_lock);
+add_err1:
+ free_netdev(new_dev);
+ goto out;
+
+ case SIOCDELTUNNEL:
+ err = -EPERM;
+ if (!capable(CAP_NET_ADMIN))
+ goto out;
+
+ err = -EINVAL;
+ if (dev == etherip_tunnel_dev)
+ goto out;
+
+ t = netdev_priv(dev);
+
+ write_lock(ðerip_lock);
+ etherip_tunnel_del(t);
+ write_unlock(ðerip_lock);
+
+ unregister_netdevice(t->dev);
+ err = 0;
+
+ break;
+ default:
+ err = -EINVAL;
+ }
+
+out:
+ return err;
+}
+
+/* device init function - called via register_netdevice
+ * The tunnel is registered as an Ethernet device. This allows
+ * the tunnel to be added to a bridge */
+static void etherip_tunnel_setup(struct net_device *dev)
+{
+ SET_MODULE_OWNER(dev);
+ dev->open = etherip_tunnel_open;
+ dev->hard_start_xmit = etherip_tunnel_xmit;
+ dev->stop = etherip_tunnel_stop;
+ dev->get_stats = etherip_tunnel_stats;
+ dev->do_ioctl = etherip_tunnel_ioctl;
+ dev->destructor = free_netdev;
+
+ ether_setup(dev);
+ random_ether_addr(dev->dev_addr);
+}
+
+/* receive function for EtherIP packets
+ * Does some basic checks on the MAC addresses and
+ * interface modes */
+static int etherip_rcv(struct sk_buff *skb)
+{
+ struct iphdr *iph;
+ struct ethhdr *ehdr;
+ struct etherip_tunnel *tunnel;
+ struct net_device *dev;
+
+ iph = skb->nh.iph;
+
+ read_lock(ðerip_lock);
+ tunnel = etherip_tunnel_locate(iph->saddr);
+ if (tunnel == NULL)
+ goto drop;
+
+ dev = tunnel->dev;
+ secpath_reset(skb);
+ memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
+ skb_pull(skb, (skb->nh.raw - skb->data)
+ + sizeof(struct iphdr) + ETHERIP_HLEN);
+ ehdr = (struct ethhdr*)skb->data;
+ skb->dev = dev;
+ skb->pkt_type = PACKET_HOST;
+ skb->protocol = eth_type_trans(skb, tunnel->dev);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ dst_release(skb->dst);
+ skb->dst = NULL;
+
+
+ /* do some checks */
+ if ((skb->pkt_type == PACKET_HOST)
+ || (skb->pkt_type == PACKET_BROADCAST))
+ goto accept;
+
+ if ((skb->pkt_type == PACKET_MULTICAST) && ((dev->mc_count > 0)
+ || (dev->flags & IFF_ALLMULTI)))
+ goto accept;
+
+ if ((skb->pkt_type == PACKET_OTHERHOST) && (dev->flags & IFF_PROMISC))
+ goto accept;
+
+ goto drop;
+
+accept:
+ tunnel->dev->last_rx = jiffies;
+ tunnel->stats.rx_packets++;
+ tunnel->stats.rx_bytes += skb->len;
+ dst_release(skb->dst);
+ skb->dst = NULL;
+ netif_rx(skb);
+ read_unlock(ðerip_lock);
+ return 0;
+
+drop:
+ read_unlock(ðerip_lock);
+ kfree_skb(skb);
+ return 0;
+}
+
+static void etherip_err(struct sk_buff *skb, u32 info)
+{
+}
+
+static struct net_protocol etherip_protocol = {
+ .handler = etherip_rcv,
+ .err_handler = 0,//etherip_err,
+ .no_policy = 1,
+};
+
+/* module init function
+ * initializes the EtherIP protocol (97) and registers the initial
+ * device */
+static int __init etherip_init(void)
+{
+ int err, i;
+ struct etherip_tunnel *p;
+
+ printk(KERN_INFO BANNER1);
+ printk(KERN_INFO BANNER2);
+
+ if (inet_add_protocol(ðerip_protocol, IPPROTO_ETHERIP)) {
+ printk(KERN_ERR "etherip: can't add protocol\n");
+ return -EAGAIN;
+ }
+
+ etherip_tunnel_dev = alloc_netdev(sizeof(struct etherip_tunnel),
+ "ethip0",
+ etherip_tunnel_setup);
+
+ if (!etherip_tunnel_dev) {
+ err = -ENOMEM;
+ goto err2;
+ }
+
+ p = netdev_priv(etherip_tunnel_dev);
+ p->dev = etherip_tunnel_dev;
+
+ if ((err = register_netdev(etherip_tunnel_dev)))
+ goto err1;
+
+ for (i=0;i<HASH_SIZE;++i)
+ INIT_LIST_HEAD(&tunnels[i]);
+
+out:
+ return err;
+err1:
+ free_netdev(etherip_tunnel_dev);
+err2:
+ inet_del_protocol(ðerip_protocol, IPPROTO_ETHERIP);
+ goto out;
+}
+
+/* destroy all tunnels */
+static void __exit etherip_destroy_tunnels(void)
+{
+ int i;
+ struct list_head *ptr;
+ struct etherip_tunnel *ret;
+
+ for (i=0;i<HASH_SIZE;++i) {
+ ptr = tunnels[i].next;
+ while (ptr != &(tunnels[i])) {
+ ret = list_entry(ptr, struct etherip_tunnel, list);
+ ptr = ptr->next;
+ unregister_netdevice(ret->dev);
+ }
+ }
+}
+
+/* module cleanup function */
+static void __exit etherip_exit(void)
+{
+ rtnl_lock();
+ etherip_destroy_tunnels();
+ unregister_netdevice(etherip_tunnel_dev);
+ rtnl_unlock();
+ if (inet_del_protocol(ðerip_protocol, IPPROTO_ETHERIP))
+ printk(KERN_ERR "etherip: can't remove protocol\n");
+}
+
+module_init(etherip_init);
+module_exit(etherip_exit);
diff -uprN linux-2.6.17.11-vanilla/net/ipv4/Kconfig linux-2.6.17.11/net/ipv4/Kconfig
--- linux-2.6.17.11-vanilla/net/ipv4/Kconfig 2006-08-23 23:16:33.000000000 +0200
+++ linux-2.6.17.11/net/ipv4/Kconfig 2006-09-01 16:27:34.000000000 +0200
@@ -272,6 +272,20 @@ config NET_IPGRE_BROADCAST
Network), but can be distributed all over the Internet. If you want
to do that, say Y here and to "IP multicast routing" below.
+config NET_ETHERIP
+ tristate "IP: Ethernet over IP tunneling (experimental)"
+ help
+ Tunneling means encapsulating data of one protocol type within
+ another protocol and sending it over a channel that understands the
+ encapsulating protocol. This tunnel driver implements the
+ encapsulation of Ethernet frames into IP packets using the EtherIP
+ protocol defined in RFC 3378. This is especially usefull together with
+ the bridging code to build distributed OSI Layer 2 networks.
+
+ To configure tunnels an extra tool is required. You can download
+ it from http://www-user.tu-chemnitz.de/~roej/projects/ under the
+ EtherIP section. If unsure, say N.
+
config IP_MROUTE
bool "IP: multicast routing"
depends on IP_MULTICAST
diff -uprN linux-2.6.17.11-vanilla/net/ipv4/Makefile linux-2.6.17.11/net/ipv4/Makefile
--- linux-2.6.17.11-vanilla/net/ipv4/Makefile 2006-08-23 23:16:33.000000000 +0200
+++ linux-2.6.17.11/net/ipv4/Makefile 2006-09-01 15:09:37.000000000 +0200
@@ -17,6 +17,7 @@ obj-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_IP_MULTIPLE_TABLES) += fib_rules.o
obj-$(CONFIG_IP_MROUTE) += ipmr.o
obj-$(CONFIG_NET_IPIP) += ipip.o
+obj-$(CONFIG_NET_ETHERIP) += etherip.o
obj-$(CONFIG_NET_IPGRE) += ip_gre.o
obj-$(CONFIG_SYN_COOKIES) += syncookies.o
obj-$(CONFIG_INET_AH) += ah4.o
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [RFC PATCH] EtherIP tunnel driver (RFC 3387)
2006-09-01 15:13 [RFC PATCH] EtherIP tunnel driver (RFC 3387) Joerg Roedel
@ 2006-09-01 16:24 ` Patrick McHardy
2006-09-03 19:10 ` Joerg Roedel
0 siblings, 1 reply; 4+ messages in thread
From: Patrick McHardy @ 2006-09-01 16:24 UTC (permalink / raw)
To: Joerg Roedel; +Cc: netdev
Joerg Roedel wrote:
> diff -uprN linux-2.6.17.11-vanilla/net/ipv4/etherip.c linux-2.6.17.11/net/ipv4/etherip.c
> --- linux-2.6.17.11-vanilla/net/ipv4/etherip.c 1970-01-01 01:00:00.000000000 +0100
> +++ linux-2.6.17.11/net/ipv4/etherip.c 2006-09-01 16:22:54.000000000 +0200
> +/* netdevice hard_start_xmit function
> + * it gets an Ethernet packet in skb and encapsulates it in another IP
> + * packet */
> +static int etherip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
> +{
> + struct etherip_tunnel *tunnel = netdev_priv(dev);
> + struct rtable *rt;
> + struct iphdr *iph;
> + struct flowi fl;
> + struct net_device *tdev;
> + int max_headroom;
> + struct net_device_stats *stats = &tunnel->stats;
> +
> + if (tunnel->recursion++) {
> + tunnel->stats.collisions++;
> + goto tx_error;
> + }
> +
> + fl.oif = 0;
> + fl.nl_u.ip4_u.daddr = tunnel->parms.iph.daddr;
> + fl.nl_u.ip4_u.saddr = tunnel->parms.iph.saddr;
> + fl.nl_u.ip4_u.saddr = 0;
> + fl.proto = IPPROTO_ETHERIP;
This leaves fields like nfmark or iif uninitialized.
> +
> + if (ip_route_output_key(&rt, &fl)) {
> + tunnel->stats.tx_carrier_errors++;
> + goto tx_error_icmp;
> + }
> +
> + tdev = rt->u.dst.dev;
> + if (tdev == dev) {
> + ip_rt_put(rt);
> + tunnel->stats.collisions++;
> + goto tx_error;
> + }
> +
> + max_headroom = (LL_RESERVED_SPACE(tdev)+sizeof(struct iphdr)
> + + ETHERIP_HLEN);
> +
> + if (skb_headroom(skb) < max_headroom || skb_cloned(skb)
> + || skb_shared(skb)) {
> + struct sk_buff *n_skb = skb_realloc_headroom(skb,max_headroom);
> + if (!n_skb) {
> + ip_rt_put(rt);
> + dev_kfree_skb(skb);
> + tunnel->stats.tx_dropped++;
> + return 0;
> + }
> + if (skb->sk)
> + skb_set_owner_w(n_skb, skb->sk);
> + dev_kfree_skb(skb);
> + skb = n_skb;
> + }
> +
> + skb->h.raw = skb->nh.raw;
> + skb->nh.raw = skb_push(skb, sizeof(struct iphdr)+ETHERIP_HLEN);
> + memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
> + IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
> + IPSKB_REROUTED);
> + dst_release(skb->dst);
> + skb->dst = &rt->u.dst;
The ipip driver updates the dst_entry's pmtu value, looks like a good
idea for this driver too.
> +
> + iph = skb->nh.iph;
> + iph->version = 4;
> + iph->ihl = sizeof(struct iphdr)>>2;
> + iph->frag_off = 0;
> + iph->protocol = IPPROTO_ETHERIP;
> + iph->tos = 0;
> + iph->daddr = rt->rt_dst;
> + iph->saddr = rt->rt_src;
> + iph->ttl = tunnel->parms.iph.ttl;
> + if (iph->ttl == 0)
> + iph->ttl = dst_metric(&rt->u.dst, RTAX_HOPLIMIT);
> +
> + /* add the 16bit etherip header after the ip header */
> + *((u16*)(skb->nh.raw + sizeof(struct iphdr))) = ntohs(ETHERIP_HEADER);
> + nf_reset(skb);
> + IPTUNNEL_XMIT();
> + tunnel->dev->trans_start = jiffies;
> + tunnel->recursion--;
> +
> + return 0;
> +
> +tx_error_icmp:
> + dst_link_failure(skb);
> +
> +tx_error:
> + tunnel->stats.tx_errors++;
> + dev_kfree_skb(skb);
> + tunnel->recursion--;
> + return 0;
> +}
> +
> +/* get statistics callback */
> +static struct net_device_stats *etherip_tunnel_stats(struct net_device *dev)
> +{
> + struct etherip_tunnel *ethip = netdev_priv(dev);
> + return ðip->stats;
> +}
> +
> +/* checks parameters the driver gets from userspace */
> +static int etherip_param_check(struct ip_tunnel_parm *p)
> +{
> + if ((p->iph.version != 4)
> + || (p->iph.protocol != IPPROTO_ETHERIP)
> + || (p->iph.ihl != 5)
> + || (p->iph.daddr == INADDR_ANY)
> + || MULTICAST(p->iph.daddr))
This looks a bit strangely aligned.
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +/* central ioctl function for all netdevices this driver manages
> + * it allows to create, delete, modify a tunnel and fetch tunnel
> + * information */
> +static int etherip_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr,
> + int cmd)
> +{
> + int err = 0;
> + struct ip_tunnel_parm p;
> + struct net_device *new_dev;
> + char *dev_name;
> + struct etherip_tunnel *t;
> +
> +
> + switch (cmd) {
> + case SIOCGETTUNNEL:
> + err = -EINVAL;
> + if (dev == etherip_tunnel_dev)
> + goto out;
> + t = netdev_priv(dev);
> + if (copy_to_user(ifr->ifr_ifru.ifru_data, &t->parms,
> + sizeof(t->parms)))
> + err = -EFAULT;
> + err = 0;
> + break;
> + case SIOCADDTUNNEL:
> + err = -EINVAL;
> + if (dev != etherip_tunnel_dev)
> + goto out;
> +
> + case SIOCCHGTUNNEL:
> + err = -EPERM;
> + if (!capable(CAP_NET_ADMIN))
> + goto out;
> +
> + err = -EFAULT;
> + if (copy_from_user(&p, ifr->ifr_ifru.ifru_data,
> + sizeof(p)))
> + goto out;
> +
> + if ((err = etherip_param_check(&p)) < 0)
> + goto out;
> +
> + t = etherip_tunnel_find(&p);
> +
> + err = -EEXIST;
> + if ((t != NULL) && (t->dev != dev))
> + goto out;
> +
> + if (cmd == SIOCADDTUNNEL) {
> +
> + p.name[IFNAMSIZ-1] = 0;
> + dev_name = p.name;
> + if (dev_name[0] == 0)
> + dev_name = "ethip%d";
> +
> + err = -ENOMEM;
> + new_dev = alloc_netdev(
> + sizeof(struct etherip_tunnel),
> + dev_name,
> + etherip_tunnel_setup);
> +
> + if (new_dev == NULL)
> + goto out;
> +
> + if (strchr(new_dev->name, '%')) {
> + err = dev_alloc_name( new_dev, new_dev->name);
> + if (err < 0)
> + goto add_err1;
> + }
> +
> + t = netdev_priv(new_dev);
> + t->dev = new_dev;
> + strncpy(p.name, new_dev->name, IFNAMSIZ);
> + memcpy(&(t->parms), &p, sizeof(p));
> +
> + write_lock(ðerip_lock);
> + etherip_tunnel_add(t);
> + write_unlock(ðerip_lock);
> +
> + err = register_netdevice(new_dev);
> + if (err < 0)
> + goto add_err2;
> + if (copy_to_user(ifr->ifr_ifru.ifru_data, &p,
> + sizeof(p)))
> + err = -EFAULT;
> +
> + } else {
> + err = -EINVAL;
> + if ((t = netdev_priv(dev)) == NULL)
> + goto out;
> + if (dev == etherip_tunnel_dev)
> + goto out;
> + write_lock(ðerip_lock);
> + memcpy(&(t->parms), &p, sizeof(p));
> + write_unlock(ðerip_lock);
> + }
> +
> + err = 0;
> + break;
> +add_err2:
> + write_lock(ðerip_lock);
> + etherip_tunnel_del(t);
> + write_unlock(ðerip_lock);
It there a reason for adding the tunnel before register_netdevice
succeeds? It seems like the tunnel can be found on the list before
it is fully initialized.
> +add_err1:
> + free_netdev(new_dev);
> + goto out;
> +
> + case SIOCDELTUNNEL:
> + err = -EPERM;
> + if (!capable(CAP_NET_ADMIN))
> + goto out;
> +
> + err = -EINVAL;
> + if (dev == etherip_tunnel_dev)
> + goto out;
> +
> + t = netdev_priv(dev);
> +
> + write_lock(ðerip_lock);
> + etherip_tunnel_del(t);
> + write_unlock(ðerip_lock);
> +
> + unregister_netdevice(t->dev);
> + err = 0;
> +
> + break;
> + default:
> + err = -EINVAL;
> + }
> +
> +out:
> + return err;
> +}
> +
> +/* device init function - called via register_netdevice
> + * The tunnel is registered as an Ethernet device. This allows
> + * the tunnel to be added to a bridge */
> +static void etherip_tunnel_setup(struct net_device *dev)
> +{
> + SET_MODULE_OWNER(dev);
> + dev->open = etherip_tunnel_open;
> + dev->hard_start_xmit = etherip_tunnel_xmit;
> + dev->stop = etherip_tunnel_stop;
> + dev->get_stats = etherip_tunnel_stats;
> + dev->do_ioctl = etherip_tunnel_ioctl;
> + dev->destructor = free_netdev;
> +
> + ether_setup(dev);
Maybe you should set tx_queue_len to zero after this, I guess
you don't want a queue len of 1000 for a software device.
> + random_ether_addr(dev->dev_addr);
> +}
> +
> +/* receive function for EtherIP packets
> + * Does some basic checks on the MAC addresses and
> + * interface modes */
> +static int etherip_rcv(struct sk_buff *skb)
> +{
> + struct iphdr *iph;
> + struct ethhdr *ehdr;
> + struct etherip_tunnel *tunnel;
> + struct net_device *dev;
> +
> + iph = skb->nh.iph;
> +
> + read_lock(ðerip_lock);
> + tunnel = etherip_tunnel_locate(iph->saddr);
> + if (tunnel == NULL)
> + goto drop;
> +
> + dev = tunnel->dev;
> + secpath_reset(skb);
> + memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
> + skb_pull(skb, (skb->nh.raw - skb->data)
> + + sizeof(struct iphdr) + ETHERIP_HLEN);
> + ehdr = (struct ethhdr*)skb->data;
> + skb->dev = dev;
> + skb->pkt_type = PACKET_HOST;
> + skb->protocol = eth_type_trans(skb, tunnel->dev);
> + skb->ip_summed = CHECKSUM_UNNECESSARY;
> + dst_release(skb->dst);
> + skb->dst = NULL;
> +
> +
> + /* do some checks */
> + if ((skb->pkt_type == PACKET_HOST)
> + || (skb->pkt_type == PACKET_BROADCAST))
> + goto accept;
> +
> + if ((skb->pkt_type == PACKET_MULTICAST) && ((dev->mc_count > 0)
> + || (dev->flags & IFF_ALLMULTI)))
Strangely aligned and unnecessary parens around comparisons.
> + goto accept;
> +
> + if ((skb->pkt_type == PACKET_OTHERHOST) && (dev->flags & IFF_PROMISC))
> + goto accept;
Why would you want to receive packets for other hosts picked up in
promiscous mode?
> +
> + goto drop;
> +
> +accept:
> + tunnel->dev->last_rx = jiffies;
> + tunnel->stats.rx_packets++;
> + tunnel->stats.rx_bytes += skb->len;
> + dst_release(skb->dst);
> + skb->dst = NULL;
Already done above, nf_reset is missing.
> + netif_rx(skb);
> + read_unlock(ðerip_lock);
> + return 0;
> +
> +drop:
> + read_unlock(ðerip_lock);
> + kfree_skb(skb);
> + return 0;
> +}
> +
> +static void etherip_err(struct sk_buff *skb, u32 info)
> +{
You could propagte errors from destination unreachable messages
similar to what the IPIP driver does.
> +}
> +
> +static struct net_protocol etherip_protocol = {
> + .handler = etherip_rcv,
> + .err_handler = 0,//etherip_err,
> + .no_policy = 1,
> +};
> +
> +/* module init function
> + * initializes the EtherIP protocol (97) and registers the initial
> + * device */
> +static int __init etherip_init(void)
> +{
> + int err, i;
> + struct etherip_tunnel *p;
> +
> + printk(KERN_INFO BANNER1);
> + printk(KERN_INFO BANNER2);
> +
> + if (inet_add_protocol(ðerip_protocol, IPPROTO_ETHERIP)) {
> + printk(KERN_ERR "etherip: can't add protocol\n");
> + return -EAGAIN;
> + }
> +
> + etherip_tunnel_dev = alloc_netdev(sizeof(struct etherip_tunnel),
> + "ethip0",
> + etherip_tunnel_setup);
> +
> + if (!etherip_tunnel_dev) {
> + err = -ENOMEM;
> + goto err2;
> + }
> +
> + p = netdev_priv(etherip_tunnel_dev);
> + p->dev = etherip_tunnel_dev;
> +
> + if ((err = register_netdev(etherip_tunnel_dev)))
> + goto err1;
> +
> + for (i=0;i<HASH_SIZE;++i)
> + INIT_LIST_HEAD(&tunnels[i]);
This needs to be done before register_netdev since as soon as it
returns the ioctl handler can be called.
> +
> +out:
> + return err;
> +err1:
> + free_netdev(etherip_tunnel_dev);
> +err2:
> + inet_del_protocol(ðerip_protocol, IPPROTO_ETHERIP);
> + goto out;
> +}
> +
> +/* destroy all tunnels */
> +static void __exit etherip_destroy_tunnels(void)
> +{
> + int i;
> + struct list_head *ptr;
> + struct etherip_tunnel *ret;
> +
> + for (i=0;i<HASH_SIZE;++i) {
> + ptr = tunnels[i].next;
> + while (ptr != &(tunnels[i])) {
> + ret = list_entry(ptr, struct etherip_tunnel, list);
list_for_each_entry
> + ptr = ptr->next;
> + unregister_netdevice(ret->dev);
> + }
> + }
> +}
> +
> +/* module cleanup function */
> +static void __exit etherip_exit(void)
> +{
> + rtnl_lock();
> + etherip_destroy_tunnels();
> + unregister_netdevice(etherip_tunnel_dev);
> + rtnl_unlock();
> + if (inet_del_protocol(ðerip_protocol, IPPROTO_ETHERIP))
> + printk(KERN_ERR "etherip: can't remove protocol\n");
> +}
> +
> +module_init(etherip_init);
> +module_exit(etherip_exit);
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [RFC PATCH] EtherIP tunnel driver (RFC 3387)
2006-09-01 16:24 ` Patrick McHardy
@ 2006-09-03 19:10 ` Joerg Roedel
2006-09-03 22:22 ` Patrick McHardy
0 siblings, 1 reply; 4+ messages in thread
From: Joerg Roedel @ 2006-09-03 19:10 UTC (permalink / raw)
To: Patrick McHardy; +Cc: netdev
On Fri, Sep 01, 2006 at 06:24:56PM +0200, Patrick McHardy wrote:
Hello Patrick,
Thank you very much for reviewing my driver. I will fix the issues you
mentioned. But I have some explanations and questions left.
> Joerg Roedel wrote:
> > + fl.oif = 0;
> > + fl.nl_u.ip4_u.daddr = tunnel->parms.iph.daddr;
> > + fl.nl_u.ip4_u.saddr = tunnel->parms.iph.saddr;
> > + fl.nl_u.ip4_u.saddr = 0;
> > + fl.proto = IPPROTO_ETHERIP;
>
> This leaves fields like nfmark or iif uninitialized.
I changed the code to initialize iif to 0, but I am not sure how to
initialize the fwmark field. Neigther the ipip driver nor the sit driver
initialize this field. Is it right to initialize it to 0?
> > + goto accept;
> > +
> > + if ((skb->pkt_type == PACKET_OTHERHOST) && (dev->flags & IFF_PROMISC))
> > + goto accept;
>
>
> Why would you want to receive packets for other hosts picked up in
> promiscous mode?
This is because the device should be usable in a bridge to build a
distributed layer 2 network. Furthermore, this could be usefull when I
extend the driver to support multicast destinations.
> > +static void etherip_err(struct sk_buff *skb, u32 info)
> > +{
>
>
> You could propagte errors from destination unreachable messages
> similar to what the IPIP driver does.
I am not sure that this makes sense here. The IPIP driver handles
ICMP_FRAG_NEEDED messages. But I think those messages will never be
received by the EtherIP driver because it never sets the DF flag in the
outgoing packets (to ensure the Ethernet MTU for protocols besides IP
and IPv6). I added a comment about this issue to the etherip_tunnel_xmit
function.
This were my questions and explanations. Thanks again for reviewing, I
will submit a new patch as soon as the questions have been discussed and
the changes are tested.
Greets,
Joerg Roedel
--
VGER BF report: U 0.5001
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [RFC PATCH] EtherIP tunnel driver (RFC 3387)
2006-09-03 19:10 ` Joerg Roedel
@ 2006-09-03 22:22 ` Patrick McHardy
0 siblings, 0 replies; 4+ messages in thread
From: Patrick McHardy @ 2006-09-03 22:22 UTC (permalink / raw)
To: Joerg Roedel; +Cc: netdev
Joerg Roedel wrote:
> On Fri, Sep 01, 2006 at 06:24:56PM +0200, Patrick McHardy wrote:
>>
>>>+ fl.oif = 0;
>>>+ fl.nl_u.ip4_u.daddr = tunnel->parms.iph.daddr;
>>>+ fl.nl_u.ip4_u.saddr = tunnel->parms.iph.saddr;
>>>+ fl.nl_u.ip4_u.saddr = 0;
>>>+ fl.proto = IPPROTO_ETHERIP;
>>
>>This leaves fields like nfmark or iif uninitialized.
>
>
> I changed the code to initialize iif to 0, but I am not sure how to
> initialize the fwmark field. Neigther the ipip driver nor the sit driver
> initialize this field. Is it right to initialize it to 0?
They both initialize the entire structure at once, which sets members
not explicitly initialized to zero.
>>>+ goto accept;
>>>+
>>>+ if ((skb->pkt_type == PACKET_OTHERHOST) && (dev->flags & IFF_PROMISC))
>>>+ goto accept;
>>
>>
>>Why would you want to receive packets for other hosts picked up in
>>promiscous mode?
>
>
> This is because the device should be usable in a bridge to build a
> distributed layer 2 network. Furthermore, this could be usefull when I
> extend the driver to support multicast destinations.
Yes, I totally misunderstood the intention.
>>>+static void etherip_err(struct sk_buff *skb, u32 info)
>>>+{
>>
>>
>>You could propagte errors from destination unreachable messages
>>similar to what the IPIP driver does.
>
>
> I am not sure that this makes sense here. The IPIP driver handles
> ICMP_FRAG_NEEDED messages. But I think those messages will never be
> received by the EtherIP driver because it never sets the DF flag in the
> outgoing packets (to ensure the Ethernet MTU for protocols besides IP
> and IPv6). I added a comment about this issue to the etherip_tunnel_xmit
> function.
Thanks for the explanation, I didn't notice that it doesn't set DF.
--
VGER BF report: U 0.505164
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2006-09-03 22:25 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-09-01 15:13 [RFC PATCH] EtherIP tunnel driver (RFC 3387) Joerg Roedel
2006-09-01 16:24 ` Patrick McHardy
2006-09-03 19:10 ` Joerg Roedel
2006-09-03 22:22 ` Patrick McHardy
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).