patch-2.1.75 linux/drivers/net/ipddp.c

Next file: linux/drivers/net/ipddp.h
Previous file: linux/drivers/net/hamradio/soundmodem/sm.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.74/linux/drivers/net/ipddp.c linux/drivers/net/ipddp.c
@@ -1,9 +1,10 @@
 /*
- *	ipddp.c: IP-over-DDP driver for Linux
+ *	ipddp.c: IP to Appletalk-IP Encapsulation driver for Linux
+ *		 Appletalk-IP to IP Decapsulation driver for Linux
  *
  *	Authors:
- *      - Original code by: Bradford W. Johnson <johns393@maroon.tc.umn.edu>
- *	- Moved to driver by: Jay Schulist <Jay.Schulist@spacs.k12.wi.us>
+ *      - DDP-IP Encap by: Bradford W. Johnson <johns393@maroon.tc.umn.edu>
+ *	- DDP-IP Decap by: Jay Schulist <Jay.Schulist@spacs.k12.wi.us>
  *
  *	Derived from:
  *	- Almost all code already existed in net/appletalk/ddp.c I just
@@ -12,6 +13,8 @@
  *      - skeleton.c: A network driver outline for linux.
  *        Written 1993-94 by Donald Becker.
  *	- dummy.c: A dummy net driver. By Nick Holloway.
+ *	- MacGate: A user space Daemon for Appletalk-IP Decap for
+ *	  Linux by Jay Schulist <Jay.Schulist@spacs.k12.wi.us>
  *
  *      Copyright 1993 United States Government as represented by the
  *      Director, National Security Agency.
@@ -21,7 +24,7 @@
  */
 
 static const char *version = 
-"ipddp.c:v0.01 8/28/97 Bradford W. Johnson <johns393@maroon.tc.umn.edu>\n";
+	"ipddp.c:v0.01 8/28/97 Bradford W. Johnson <johns393@maroon.tc.umn.edu>\n";
 
 #include <linux/config.h>
 #ifdef MODULE
@@ -53,16 +56,24 @@
 #include <linux/atalk.h>
 #include <linux/ip.h>
 #include <net/route.h>
+#include <linux/inet.h>
 
 #include "ipddp.h"		/* Our stuff */
 
+static struct ipddp_route *ipddp_route_list = NULL;
+
 /*
  *      The name of the card. Is used for messages and in the requests for
  *      io regions, irqs and dma channels
  */
-
 static const char *cardname = "ipddp";
 
+#ifdef CONFIG_IPDDP_ENCAP
+static int ipddp_mode = IPDDP_ENCAP;
+#else
+static int ipddp_mode = IPDDP_DECAP;
+#endif
+
 /* Use 0 for production, 1 for verification, 2 for debug, 3 for verbose debug */
 #ifndef IPDDP_DEBUG
 #define IPDDP_DEBUG 1
@@ -73,8 +84,11 @@
 static int ipddp_xmit(struct sk_buff *skb, struct device *dev);
 static struct net_device_stats *ipddp_get_stats(struct device *dev);
 static int ipddp_rebuild_header(struct sk_buff *skb);
-static int ipddp_header(struct sk_buff *skb, struct device *dev,
+static int ipddp_hard_header(struct sk_buff *skb, struct device *dev,
                 unsigned short type, void *daddr, void *saddr, unsigned len);
+static int ipddp_create(struct ipddp_route *new_rt);
+static int ipddp_delete(struct ipddp_route *rt);
+static struct ipddp_route* ipddp_find_route(struct ipddp_route *rt);
 static int ipddp_ioctl(struct device *dev, struct ifreq *ifr, int cmd);
 
 
@@ -103,6 +117,17 @@
 	if (ipddp_debug && version_printed++ == 0)
                 printk("%s", version);
 
+	/* Let the user now what mode we are in */
+	if(ipddp_mode == IPDDP_ENCAP)
+		printk("%s: Appletalk-IP Encapsulation mode by Bradford W. Johnson <johns393@maroon.tc.umn.edu>\n", 
+			dev->name);
+	if(ipddp_mode == IPDDP_DECAP)
+		printk("%s: Appletalk-IP Decapsulation mode by Jay Schulist <Jay.Schulist@spacs.k12.wi.us>\n", 
+			dev->name);
+
+	/* Fill in the device structure with ethernet-generic values. */
+        ether_setup(dev);
+
 	/* Initalize the device structure. */
         dev->hard_start_xmit = ipddp_xmit;
 
@@ -115,7 +140,7 @@
         dev->stop 	    = ipddp_close;
         dev->get_stats      = ipddp_get_stats;
         dev->do_ioctl       = ipddp_ioctl;
-	dev->hard_header    = ipddp_header;        /* see ip_output.c */
+	dev->hard_header    = ipddp_hard_header;        /* see ip_output.c */
 	dev->rebuild_header = ipddp_rebuild_header;
 
         dev->type = ARPHRD_IPDDP;       	/* IP over DDP tunnel */
@@ -129,9 +154,6 @@
          */
         dev->hard_header_len = 14+8+sizeof(struct ddpehdr)+1;
 
-	/* Fill in the device structure with ethernet-generic values. */
-	ether_setup(dev);
-
         return 0;
 }
 
@@ -141,16 +163,13 @@
 static int ipddp_xmit(struct sk_buff *skb, struct device *dev)
 {
         /* Retrieve the saved address hint */
-        struct at_addr *a=(struct at_addr *)skb->data;
+        struct at_addr *at = (struct at_addr *)skb->data;
         skb_pull(skb,4);
 
         ((struct net_device_stats *) dev->priv)->tx_packets++;
         ((struct net_device_stats *) dev->priv)->tx_bytes+=skb->len;
 
-	if(ipddp_debug>1)
-        	printk("ipddp_xmit: Headroom %d\n",skb_headroom(skb));
-
-        if(aarp_send_ddp(skb->dev,skb,a,NULL) < 0)
+        if(aarp_send_ddp(skb->dev, skb, at, NULL) < 0)
                 dev_kfree_skb(skb,FREE_WRITE);
 
         return 0;
@@ -165,7 +184,8 @@
 }
 
 /*
- * Now the packet really wants to go out.
+ * Now the packet really wants to go out. On entry skb->data points to the
+ * ddpehdr we reserved earlier. skb->h.raw will be the higher level header.
  */
 static int ipddp_rebuild_header(struct sk_buff *skb)
 {
@@ -175,106 +195,171 @@
         struct ipddp_route *rt;
         struct at_addr *our_addr;
 
-        /*
-         * On entry skb->data points to the ddpehdr we reserved earlier.
-         * skb->h.raw will be the higher level header.
-         */
-
-        /*
-         * We created this earlier.
+	/*
+         * Find appropriate route to use, based only on IP number.
          */
-
-        ddp = (struct ddpehdr *) (skb->data+4);
-
-        /* find appropriate route */
-
-        for(rt=ipddp_route_head;rt;rt=rt->next)
+        for(rt = ipddp_route_list; rt != NULL; rt = rt->next)
         {
                 if(rt->ip == paddr)
                         break;
         }
 
-        if(!rt) {
-                printk("ipddp unreachable dst %08lx\n",ntohl(paddr));
+        if(rt == NULL)
+        {
+                printk("%s: unreachable dst %s\n", cardname, in_ntoa(paddr));
                 return -ENETUNREACH;
         }
 
         our_addr = atalk_find_dev_addr(rt->dev);
 
-        /* fill in ddpehdr */
+	if(ipddp_mode == IPDDP_DECAP)
+		/* 
+		 * Pull off the excess room that should not be there.
+        	 * This is the case for Localtalk, this may not hold
+        	 * true for Ethertalk, etc. 
+		 */
+		skb_pull(skb, 31-(sizeof(struct ddpehdr)+1));
+
+	/* Create the Extended DDP header */
+	ddp = (struct ddpehdr *) (skb->data+4);
         ddp->deh_len = skb->len;
         ddp->deh_hops = 1;
         ddp->deh_pad = 0;
         ddp->deh_sum = 0;
-        ddp->deh_dnet = rt->at.s_net;   /* FIXME more hops?? */
-        ddp->deh_snet = our_addr->s_net;
+
+	/*
+         * For Localtalk we need aarp_send_ddp to strip the
+         * Ext DDP header and place a Shrt DDP header on it.
+         */
+        if(rt->dev->type == ARPHRD_LOCALTLK)
+        {
+                ddp->deh_dnet  = 0;   /* FIXME more hops?? */
+                ddp->deh_snet  = 0;
+        }
+        else
+        {
+                ddp->deh_dnet  = rt->at.s_net;   /* FIXME more hops?? */
+                ddp->deh_snet  = our_addr->s_net;
+        }
         ddp->deh_dnode = rt->at.s_node;
         ddp->deh_snode = our_addr->s_node;
         ddp->deh_dport = 72;
         ddp->deh_sport = 72;
 
-        *((__u8 *)(ddp+1)) = 22;        /* ddp type = IP */
+        *((__u8 *)(ddp+1)) = 22;        		/* ddp type = IP */
+        *((__u16 *)ddp)=ntohs(*((__u16 *)ddp));		/* fix up length field */
 
-        /* fix up length field */
-        *((__u16 *)ddp)=ntohs(*((__u16 *)ddp));
-
-        /* set skb->dev to appropriate device */
-        skb->dev = rt->dev;
-
-        /* skb->raddr = (unsigned long) at */
+	/* Hide it at the start of the buffer, we pull it out in ipddp_xmit */
         at = rt->at;
-        /* Hide it at the start of the buffer */
         memcpy(skb->data,(void *)&at,sizeof(at));
-        skb->arp = 1;   /* so the actual device doesn't try to arp it... */
+
+	skb->dev = rt->dev;	/* set skb->dev to appropriate device */
+        skb->arp = 1;   	/* so the actual device doesn't try to arp it... */
         skb->protocol = htons(ETH_P_ATALK);     /* Protocol has changed */
 
         return 0;
 }
 
-static int ipddp_header(struct sk_buff *skb, struct device *dev, 
+static int ipddp_hard_header(struct sk_buff *skb, struct device *dev, 
 		unsigned short type, void *daddr, void *saddr, unsigned len)
 {
-	if(ipddp_debug>=2)
-        	printk("%s: ipddp_header\n", cardname);
-
         /* Push down the header space and the type byte */
         skb_push(skb, sizeof(struct ddpehdr)+1+4);
 
         return 0;
 }
 
+/*
+ * Create a routing entry. We first verify that the
+ * record does not already exist. If it does we return -EEXIST
+ */
+static int ipddp_create(struct ipddp_route *new_rt)
+{
+        struct ipddp_route *rt =(struct ipddp_route*) kmalloc(sizeof(*rt), GFP_KERNEL);
+	struct ipddp_route *test;
+
+        if(rt == NULL)
+                return -ENOMEM;
+
+        rt->ip = new_rt->ip;
+        rt->at = new_rt->at;
+        rt->next = NULL;
+        rt->dev = atrtr_get_dev(&rt->at);
+        if(rt->dev == NULL)
+                return (-ENETUNREACH);
+
+	test = ipddp_find_route(rt);
+	if(test != NULL)
+		return (-EEXIST);
+	
+        rt->next = ipddp_route_list;
+        ipddp_route_list = rt;
+
+        return 0;
+}
+
+/*
+ * Delete a route, we only delete a FULL match.
+ * If route does not exist we return -ENOENT.
+ */
+static int ipddp_delete(struct ipddp_route *rt)
+{
+        struct ipddp_route **r = &ipddp_route_list;
+        struct ipddp_route *tmp;
+
+        while((tmp = *r) != NULL)
+        {
+                if(tmp->ip == rt->ip
+                        && tmp->at.s_net == rt->at.s_net
+                        && tmp->at.s_node == rt->at.s_node)
+                {
+                        *r = tmp->next;
+                        kfree_s(tmp, sizeof(struct ipddp_route));
+                        return 0;
+                }
+                r = &tmp->next;
+        }
+
+        return (-ENOENT);
+}
+
+/*
+ * Find a routing entry, we only return a FULL match
+ */
+static struct ipddp_route* ipddp_find_route(struct ipddp_route *rt)
+{
+        struct ipddp_route *f;
+
+        for(f = ipddp_route_list; f != NULL; f = f->next)
+        {
+                if(f->ip == rt->ip
+                        && f->at.s_net == rt->at.s_net
+                        && f->at.s_node == rt->at.s_node)
+                        return (f);
+        }
+
+        return (NULL);
+}
+
 static int ipddp_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
 {
-        struct ipddp_route *urt = (struct ipddp_route *)ifr->ifr_data;
+        struct ipddp_route *rt = (struct ipddp_route *)ifr->ifr_data;
 
         if(!suser())
                 return -EPERM;
 
-        /* for now we only have one route at a time */
-
         switch(cmd)
         {
-                case SIOCADDIPDDPRT:
-                        if(copy_from_user(&ipddp_route_test,urt,sizeof(struct ipddp_route)))
-                                return -EFAULT;
-                        ipddp_route_test.dev = atrtr_get_dev(&ipddp_route_test.at);
-                        if (dev==NULL)
-                                return -ENETUNREACH;
-                        ipddp_route_test.next = NULL;
-                        printk("%s: Added route through %s\n",
-				ipddp_route_test.dev->name, cardname);
-                        ipddp_route_head = &ipddp_route_test;
-                        return 0;
+		case SIOCADDIPDDPRT:
+                        return (ipddp_create(rt));
 
                 case SIOCFINDIPDDPRT:
-                        if(copy_to_user(urt,&ipddp_route_test,sizeof(struct ipddp_route)))
+                        if(copy_to_user(rt, ipddp_find_route(rt), sizeof(struct ipddp_route)))
                                 return -EFAULT;
                         return 0;
 
                 case SIOCDELIPDDPRT:
-                        ipddp_route_test.dev = NULL;
-                        ipddp_route_head = NULL;
-                        return 0;
+                        return (ipddp_delete(rt));
 
                 default:
                         return -EINVAL;
@@ -291,9 +376,17 @@
                 0, 0, 0, NULL, ipddp_init
 };
 
+MODULE_PARM(ipddp_mode, "i");
+
 int init_module(void)
 {
-	if (register_netdev(&dev_ipddp) != 0)
+	int err;
+
+	err=dev_alloc_name(&dev_ipddp, "ipddp%d");
+        if(err < 0)
+                return err;
+
+	if(register_netdev(&dev_ipddp) != 0)
                 return -EIO;
 
 	return 0;

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov