11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	ipddp.c: IP to Appletalk-IP Encapsulation driver for Linux
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *		 Appletalk-IP to IP Decapsulation driver for Linux
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Authors:
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *      - DDP-IP Encap by: Bradford W. Johnson <johns393@maroon.tc.umn.edu>
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	- DDP-IP Decap by: Jay Schulist <jschlst@samba.org>
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Derived from:
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	- Almost all code already existed in net/appletalk/ddp.c I just
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	  moved/reorginized it into a driver file. Original IP-over-DDP code
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	  was done by Bradford W. Johnson <johns393@maroon.tc.umn.edu>
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *      - skeleton.c: A network driver outline for linux.
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *        Written 1993-94 by Donald Becker.
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	- dummy.c: A dummy net driver. By Nick Holloway.
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	- MacGate: A user space Daemon for Appletalk-IP Decap for
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	  Linux by Jay Schulist <jschlst@samba.org>
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *      Copyright 1993 United States Government as represented by the
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *      Director, National Security Agency.
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *      This software may be used and distributed according to the terms
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *      of the GNU General Public License, incorporated herein by reference.
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/netdevice.h>
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/etherdevice.h>
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/ip.h>
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/atalk.h>
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/if_arp.h>
345a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <net/route.h>
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/uaccess.h>
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "ipddp.h"		/* Our stuff */
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const char version[] = KERN_INFO "ipddp.c:v0.01 8/28/97 Bradford W. Johnson <johns393@maroon.tc.umn.edu>\n";
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct ipddp_route *ipddp_route_list;
435615968a70845157adaffc11062c997d045339eeDavid S. Millerstatic DEFINE_SPINLOCK(ipddp_route_lock);
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_IPDDP_ENCAP
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ipddp_mode = IPDDP_ENCAP;
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ipddp_mode = IPDDP_DECAP;
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Index to functions, as function prototypes. */
520fc480987e69f22b9212f087545b4d1ca6950807Stephen Hemmingerstatic netdev_tx_t ipddp_xmit(struct sk_buff *skb,
530fc480987e69f22b9212f087545b4d1ca6950807Stephen Hemminger				    struct net_device *dev);
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ipddp_create(struct ipddp_route *new_rt);
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ipddp_delete(struct ipddp_route *rt);
565615968a70845157adaffc11062c997d045339eeDavid S. Millerstatic struct ipddp_route* __ipddp_find_route(struct ipddp_route *rt);
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5943a67304a3e882ec297e08159f8698be59a235feStephen Hemmingerstatic const struct net_device_ops ipddp_netdev_ops = {
6043a67304a3e882ec297e08159f8698be59a235feStephen Hemminger	.ndo_start_xmit		= ipddp_xmit,
6143a67304a3e882ec297e08159f8698be59a235feStephen Hemminger	.ndo_do_ioctl   	= ipddp_ioctl,
6243a67304a3e882ec297e08159f8698be59a235feStephen Hemminger	.ndo_change_mtu		= eth_change_mtu,
6343a67304a3e882ec297e08159f8698be59a235feStephen Hemminger	.ndo_set_mac_address 	= eth_mac_addr,
6443a67304a3e882ec297e08159f8698be59a235feStephen Hemminger	.ndo_validate_addr	= eth_validate_addr,
6543a67304a3e882ec297e08159f8698be59a235feStephen Hemminger};
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct net_device * __init ipddp_init(void)
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	static unsigned version_printed;
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct net_device *dev;
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int err;
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7343a67304a3e882ec297e08159f8698be59a235feStephen Hemminger	dev = alloc_etherdev(0);
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dev)
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ERR_PTR(-ENOMEM);
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7793f154b594fe47e4a7e5358b309add449a046cd3Eric Dumazet	dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	strcpy(dev->name, "ipddp%d");
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (version_printed++ == 0)
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                printk(version);
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
83421f91d21ad6f799dc7b489bb33cc560ccc56f98Uwe Kleine-König	/* Initialize the device structure. */
8443a67304a3e882ec297e08159f8698be59a235feStephen Hemminger	dev->netdev_ops = &ipddp_netdev_ops;
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        dev->type = ARPHRD_IPDDP;       	/* IP over DDP tunnel */
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        dev->mtu = 585;
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        dev->flags |= IFF_NOARP;
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        /*
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds         *      The worst case header we will need is currently a
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds         *      ethernet header (14 bytes) and a ddp header (sizeof ddpehdr+1)
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds         *      We send over SNAP so that takes another 8 bytes.
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds         */
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        dev->hard_header_len = 14+8+sizeof(struct ddpehdr)+1;
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = register_netdev(dev);
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (err) {
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		free_netdev(dev);
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ERR_PTR(err);
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Let the user now what mode we are in */
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if(ipddp_mode == IPDDP_ENCAP)
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk("%s: Appletalk-IP Encap. mode by Bradford W. Johnson <johns393@maroon.tc.umn.edu>\n",
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dev->name);
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if(ipddp_mode == IPDDP_DECAP)
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk("%s: Appletalk-IP Decap. mode by Jay Schulist <jschlst@samba.org>\n",
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dev->name);
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        return dev;
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Transmit LLAP/ELAP frame using aarp_send_ddp.
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1180fc480987e69f22b9212f087545b4d1ca6950807Stephen Hemmingerstatic netdev_tx_t ipddp_xmit(struct sk_buff *skb, struct net_device *dev)
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
120511c3f92ad5b6d9f8f6464be1b4f85f0422be91aEric Dumazet	__be32 paddr = skb_rtable(skb)->rt_gateway;
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        struct ddpehdr *ddp;
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        struct ipddp_route *rt;
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        struct atalk_addr *our_addr;
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1255615968a70845157adaffc11062c997d045339eeDavid S. Miller	spin_lock(&ipddp_route_lock);
1265615968a70845157adaffc11062c997d045339eeDavid S. Miller
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds         * Find appropriate route to use, based only on IP number.
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds         */
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        for(rt = ipddp_route_list; rt != NULL; rt = rt->next)
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        {
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                if(rt->ip == paddr)
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                        break;
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        }
1355615968a70845157adaffc11062c997d045339eeDavid S. Miller        if(rt == NULL) {
1365615968a70845157adaffc11062c997d045339eeDavid S. Miller		spin_unlock(&ipddp_route_lock);
1376ed106549d17474ca17a16057f4c0ed4eba5a7caPatrick McHardy                return NETDEV_TX_OK;
1385615968a70845157adaffc11062c997d045339eeDavid S. Miller	}
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        our_addr = atalk_find_dev_addr(rt->dev);
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if(ipddp_mode == IPDDP_DECAP)
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * Pull off the excess room that should not be there.
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * This is due to a hard-header problem. This is the
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * quick fix for now though, till it breaks.
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		skb_pull(skb, 35-(sizeof(struct ddpehdr)+1));
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Create the Extended DDP header */
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ddp = (struct ddpehdr *)skb->data;
1522a50f28c326d20ab4556be1b867ecddf6aefbb88Al Viro        ddp->deh_len_hops = htons(skb->len + (1<<10));
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        ddp->deh_sum = 0;
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds         * For Localtalk we need aarp_send_ddp to strip the
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds         * long DDP header and place a shot DDP header on it.
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds         */
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        if(rt->dev->type == ARPHRD_LOCALTLK)
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        {
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                ddp->deh_dnet  = 0;   /* FIXME more hops?? */
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                ddp->deh_snet  = 0;
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        }
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        else
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        {
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                ddp->deh_dnet  = rt->at.s_net;   /* FIXME more hops?? */
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                ddp->deh_snet  = our_addr->s_net;
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        }
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        ddp->deh_dnode = rt->at.s_node;
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        ddp->deh_snode = our_addr->s_node;
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        ddp->deh_dport = 72;
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        ddp->deh_sport = 72;
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        *((__u8 *)(ddp+1)) = 22;        	/* ddp type = IP */
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        skb->protocol = htons(ETH_P_ATALK);     /* Protocol has changed */
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17843a67304a3e882ec297e08159f8698be59a235feStephen Hemminger	dev->stats.tx_packets++;
17943a67304a3e882ec297e08159f8698be59a235feStephen Hemminger	dev->stats.tx_bytes += skb->len;
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
181ffcfb8db540ff879c2a85bf7e404954281443414Arnaldo Carvalho de Melo	aarp_send_ddp(rt->dev, skb, &rt->at, NULL);
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1835615968a70845157adaffc11062c997d045339eeDavid S. Miller	spin_unlock(&ipddp_route_lock);
1845615968a70845157adaffc11062c997d045339eeDavid S. Miller
1856ed106549d17474ca17a16057f4c0ed4eba5a7caPatrick McHardy        return NETDEV_TX_OK;
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Create a routing entry. We first verify that the
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * record does not already exist. If it does we return -EEXIST
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ipddp_create(struct ipddp_route *new_rt)
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1945cbded585d129d0226cb48ac4202b253c781be26Robert P. J. Day        struct ipddp_route *rt = kmalloc(sizeof(*rt), GFP_KERNEL);
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        if (rt == NULL)
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                return -ENOMEM;
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        rt->ip = new_rt->ip;
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        rt->at = new_rt->at;
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        rt->next = NULL;
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        if ((rt->dev = atrtr_get_dev(&rt->at)) == NULL) {
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kfree(rt);
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                return -ENETUNREACH;
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        }
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2075615968a70845157adaffc11062c997d045339eeDavid S. Miller	spin_lock_bh(&ipddp_route_lock);
2085615968a70845157adaffc11062c997d045339eeDavid S. Miller	if (__ipddp_find_route(rt)) {
2095615968a70845157adaffc11062c997d045339eeDavid S. Miller		spin_unlock_bh(&ipddp_route_lock);
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kfree(rt);
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EEXIST;
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        rt->next = ipddp_route_list;
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        ipddp_route_list = rt;
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2175615968a70845157adaffc11062c997d045339eeDavid S. Miller	spin_unlock_bh(&ipddp_route_lock);
2185615968a70845157adaffc11062c997d045339eeDavid S. Miller
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        return 0;
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Delete a route, we only delete a FULL match.
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * If route does not exist we return -ENOENT.
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ipddp_delete(struct ipddp_route *rt)
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        struct ipddp_route **r = &ipddp_route_list;
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        struct ipddp_route *tmp;
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2315615968a70845157adaffc11062c997d045339eeDavid S. Miller	spin_lock_bh(&ipddp_route_lock);
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        while((tmp = *r) != NULL)
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        {
2348e95a2026f3b43f7c3d676adaccd2de9532e8dccJoe Perches                if(tmp->ip == rt->ip &&
2358e95a2026f3b43f7c3d676adaccd2de9532e8dccJoe Perches		   tmp->at.s_net == rt->at.s_net &&
2368e95a2026f3b43f7c3d676adaccd2de9532e8dccJoe Perches		   tmp->at.s_node == rt->at.s_node)
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                {
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                        *r = tmp->next;
2395615968a70845157adaffc11062c997d045339eeDavid S. Miller			spin_unlock_bh(&ipddp_route_lock);
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                        kfree(tmp);
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                        return 0;
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                }
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                r = &tmp->next;
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        }
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2465615968a70845157adaffc11062c997d045339eeDavid S. Miller	spin_unlock_bh(&ipddp_route_lock);
247807540baae406c84dcb9c1c8ef07a56d2d2ae84aEric Dumazet        return -ENOENT;
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Find a routing entry, we only return a FULL match
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2535615968a70845157adaffc11062c997d045339eeDavid S. Millerstatic struct ipddp_route* __ipddp_find_route(struct ipddp_route *rt)
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        struct ipddp_route *f;
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        for(f = ipddp_route_list; f != NULL; f = f->next)
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        {
2598e95a2026f3b43f7c3d676adaccd2de9532e8dccJoe Perches                if(f->ip == rt->ip &&
2608e95a2026f3b43f7c3d676adaccd2de9532e8dccJoe Perches		   f->at.s_net == rt->at.s_net &&
2618e95a2026f3b43f7c3d676adaccd2de9532e8dccJoe Perches		   f->at.s_node == rt->at.s_node)
262807540baae406c84dcb9c1c8ef07a56d2d2ae84aEric Dumazet                        return f;
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        }
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
265807540baae406c84dcb9c1c8ef07a56d2d2ae84aEric Dumazet        return NULL;
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        struct ipddp_route __user *rt = ifr->ifr_data;
2715615968a70845157adaffc11062c997d045339eeDavid S. Miller        struct ipddp_route rcp, rcp2, *rp;
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        if(!capable(CAP_NET_ADMIN))
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                return -EPERM;
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if(copy_from_user(&rcp, rt, sizeof(rcp)))
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EFAULT;
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        switch(cmd)
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        {
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case SIOCADDIPDDPRT:
282807540baae406c84dcb9c1c8ef07a56d2d2ae84aEric Dumazet                        return ipddp_create(&rcp);
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                case SIOCFINDIPDDPRT:
2855615968a70845157adaffc11062c997d045339eeDavid S. Miller			spin_lock_bh(&ipddp_route_lock);
2865615968a70845157adaffc11062c997d045339eeDavid S. Miller			rp = __ipddp_find_route(&rcp);
2875615968a70845157adaffc11062c997d045339eeDavid S. Miller			if (rp)
2885615968a70845157adaffc11062c997d045339eeDavid S. Miller				memcpy(&rcp2, rp, sizeof(rcp2));
2895615968a70845157adaffc11062c997d045339eeDavid S. Miller			spin_unlock_bh(&ipddp_route_lock);
2905615968a70845157adaffc11062c997d045339eeDavid S. Miller
2915615968a70845157adaffc11062c997d045339eeDavid S. Miller			if (rp) {
2925615968a70845157adaffc11062c997d045339eeDavid S. Miller				if (copy_to_user(rt, &rcp2,
2935615968a70845157adaffc11062c997d045339eeDavid S. Miller						 sizeof(struct ipddp_route)))
2945615968a70845157adaffc11062c997d045339eeDavid S. Miller					return -EFAULT;
2955615968a70845157adaffc11062c997d045339eeDavid S. Miller				return 0;
2965615968a70845157adaffc11062c997d045339eeDavid S. Miller			} else
2975615968a70845157adaffc11062c997d045339eeDavid S. Miller				return -ENOENT;
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                case SIOCDELIPDDPRT:
300807540baae406c84dcb9c1c8ef07a56d2d2ae84aEric Dumazet                        return ipddp_delete(&rcp);
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                default:
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                        return -EINVAL;
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        }
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct net_device *dev_ipddp;
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(ipddp_mode, int, 0);
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init ipddp_init_module(void)
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev_ipddp = ipddp_init();
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        if (IS_ERR(dev_ipddp))
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                return PTR_ERR(dev_ipddp);
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit ipddp_cleanup_module(void)
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        struct ipddp_route *p;
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unregister_netdev(dev_ipddp);
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        free_netdev(dev_ipddp);
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        while (ipddp_route_list) {
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                p = ipddp_route_list->next;
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                kfree(ipddp_route_list);
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds                ipddp_route_list = p;
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        }
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(ipddp_init_module);
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(ipddp_cleanup_module);
336