File: /usr/src/linux/drivers/net/appletalk/ipddp.c

1     /*
2      *	ipddp.c: IP to Appletalk-IP Encapsulation driver for Linux
3      *		 Appletalk-IP to IP Decapsulation driver for Linux
4      *
5      *	Authors:
6      *      - DDP-IP Encap by: Bradford W. Johnson <johns393@maroon.tc.umn.edu>
7      *	- DDP-IP Decap by: Jay Schulist <jschlst@samba.org>
8      *
9      *	Derived from:
10      *	- Almost all code already existed in net/appletalk/ddp.c I just
11      *	  moved/reorginized it into a driver file. Original IP-over-DDP code
12      *	  was done by Bradford W. Johnson <johns393@maroon.tc.umn.edu>
13      *      - skeleton.c: A network driver outline for linux.
14      *        Written 1993-94 by Donald Becker.
15      *	- dummy.c: A dummy net driver. By Nick Holloway.
16      *	- MacGate: A user space Daemon for Appletalk-IP Decap for
17      *	  Linux by Jay Schulist <jschlst@samba.org>
18      *
19      *      Copyright 1993 United States Government as represented by the
20      *      Director, National Security Agency.
21      *
22      *      This software may be used and distributed according to the terms
23      *      of the GNU General Public License, incorporated herein by reference.
24      */
25     
26     #include <linux/config.h>
27     #include <linux/module.h>
28     #include <linux/kernel.h>
29     #include <linux/init.h>
30     #include <linux/netdevice.h>
31     #include <linux/ip.h>
32     #include <linux/atalk.h>
33     #include <linux/if_arp.h>
34     #include <net/route.h>
35     #include <asm/uaccess.h>
36     
37     #include "ipddp.h"		/* Our stuff */
38     
39     static const char version[] = KERN_INFO "ipddp.c:v0.01 8/28/97 Bradford W. Johnson <johns393@maroon.tc.umn.edu>\n";
40     
41     static struct ipddp_route *ipddp_route_list;
42     
43     #ifdef CONFIG_IPDDP_ENCAP
44     static int ipddp_mode = IPDDP_ENCAP;
45     #else
46     static int ipddp_mode = IPDDP_DECAP;
47     #endif
48     
49     /* Index to functions, as function prototypes. */
50     static int ipddp_xmit(struct sk_buff *skb, struct net_device *dev);
51     static struct net_device_stats *ipddp_get_stats(struct net_device *dev);
52     static int ipddp_create(struct ipddp_route *new_rt);
53     static int ipddp_delete(struct ipddp_route *rt);
54     static struct ipddp_route* ipddp_find_route(struct ipddp_route *rt);
55     static int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
56     
57     
58     static int __init ipddp_init(struct net_device *dev)
59     {
60     	static unsigned version_printed;
61     
62     	SET_MODULE_OWNER(dev);
63     
64     	if (version_printed++ == 0)
65                     printk(version);
66     
67     	/* Let the user now what mode we are in */
68     	if(ipddp_mode == IPDDP_ENCAP)
69     		printk("%s: Appletalk-IP Encap. mode by Bradford W. Johnson <johns393@maroon.tc.umn.edu>\n", 
70     			dev->name);
71     	if(ipddp_mode == IPDDP_DECAP)
72     		printk("%s: Appletalk-IP Decap. mode by Jay Schulist <jschlst@samba.org>\n", 
73     			dev->name);
74     
75     	/* Fill in the device structure with ethernet-generic values. */
76             ether_setup(dev);
77     
78     	/* Initalize the device structure. */
79             dev->hard_start_xmit = ipddp_xmit;
80     
81             dev->priv = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL);
82             if(!dev->priv)
83                     return -ENOMEM;
84             memset(dev->priv,0,sizeof(struct net_device_stats));
85     
86             dev->get_stats      = ipddp_get_stats;
87             dev->do_ioctl       = ipddp_ioctl;
88     
89             dev->type = ARPHRD_IPDDP;       	/* IP over DDP tunnel */
90             dev->mtu = 585;
91             dev->flags |= IFF_NOARP;
92     
93             /*
94              *      The worst case header we will need is currently a
95              *      ethernet header (14 bytes) and a ddp header (sizeof ddpehdr+1)
96              *      We send over SNAP so that takes another 8 bytes.
97              */
98             dev->hard_header_len = 14+8+sizeof(struct ddpehdr)+1;
99     
100             return 0;
101     }
102     
103     /*
104      * Get the current statistics. This may be called with the card open or closed.
105      */
106     static struct net_device_stats *ipddp_get_stats(struct net_device *dev)
107     {
108             return dev->priv;
109     }
110     
111     /*
112      * Transmit LLAP/ELAP frame using aarp_send_ddp.
113      */
114     static int ipddp_xmit(struct sk_buff *skb, struct net_device *dev)
115     {
116     	u32 paddr = ((struct rtable*)skb->dst)->rt_gateway;
117             struct ddpehdr *ddp;
118             struct ipddp_route *rt;
119             struct at_addr *our_addr;
120     
121     	/*
122              * Find appropriate route to use, based only on IP number.
123              */
124             for(rt = ipddp_route_list; rt != NULL; rt = rt->next)
125             {
126                     if(rt->ip == paddr)
127                             break;
128             }
129             if(rt == NULL)
130                     return 0;
131     
132             our_addr = atalk_find_dev_addr(rt->dev);
133     
134     	if(ipddp_mode == IPDDP_DECAP)
135     		/* 
136     		 * Pull off the excess room that should not be there.
137     		 * This is due to a hard-header problem. This is the
138     		 * quick fix for now though, till it breaks.
139     		 */
140     		skb_pull(skb, 35-(sizeof(struct ddpehdr)+1));
141     
142     	/* Create the Extended DDP header */
143     	ddp = (struct ddpehdr *)skb->data;
144             ddp->deh_len = skb->len;
145             ddp->deh_hops = 1;
146             ddp->deh_pad = 0;
147             ddp->deh_sum = 0;
148     
149     	/*
150              * For Localtalk we need aarp_send_ddp to strip the
151              * long DDP header and place a shot DDP header on it.
152              */
153             if(rt->dev->type == ARPHRD_LOCALTLK)
154             {
155                     ddp->deh_dnet  = 0;   /* FIXME more hops?? */
156                     ddp->deh_snet  = 0;
157             }
158             else
159             {
160                     ddp->deh_dnet  = rt->at.s_net;   /* FIXME more hops?? */
161                     ddp->deh_snet  = our_addr->s_net;
162             }
163             ddp->deh_dnode = rt->at.s_node;
164             ddp->deh_snode = our_addr->s_node;
165             ddp->deh_dport = 72;
166             ddp->deh_sport = 72;
167     
168             *((__u8 *)(ddp+1)) = 22;        	/* ddp type = IP */
169             *((__u16 *)ddp)=ntohs(*((__u16 *)ddp));	/* fix up length field */
170     
171             skb->protocol = htons(ETH_P_ATALK);     /* Protocol has changed */
172     
173     	((struct net_device_stats *) dev->priv)->tx_packets++;
174             ((struct net_device_stats *) dev->priv)->tx_bytes+=skb->len;
175     
176             if(aarp_send_ddp(rt->dev, skb, &rt->at, NULL) < 0)
177                     dev_kfree_skb(skb);
178     
179             return 0;
180     }
181     
182     /*
183      * Create a routing entry. We first verify that the
184      * record does not already exist. If it does we return -EEXIST
185      */
186     static int ipddp_create(struct ipddp_route *new_rt)
187     {
188             struct ipddp_route *rt =(struct ipddp_route*) kmalloc(sizeof(*rt), GFP_KERNEL);
189     
190             if (rt == NULL)
191                     return -ENOMEM;
192     
193             rt->ip = new_rt->ip;
194             rt->at = new_rt->at;
195             rt->next = NULL;
196             if ((rt->dev = atrtr_get_dev(&rt->at)) == NULL) {
197     		kfree(rt);
198                     return -ENETUNREACH;
199             }
200     
201     	if (ipddp_find_route(rt)) {
202     		kfree(rt);
203     		return -EEXIST;
204     	}
205     
206             rt->next = ipddp_route_list;
207             ipddp_route_list = rt;
208     
209             return 0;
210     }
211     
212     /*
213      * Delete a route, we only delete a FULL match.
214      * If route does not exist we return -ENOENT.
215      */
216     static int ipddp_delete(struct ipddp_route *rt)
217     {
218             struct ipddp_route **r = &ipddp_route_list;
219             struct ipddp_route *tmp;
220     
221             while((tmp = *r) != NULL)
222             {
223                     if(tmp->ip == rt->ip
224                             && tmp->at.s_net == rt->at.s_net
225                             && tmp->at.s_node == rt->at.s_node)
226                     {
227                             *r = tmp->next;
228                             kfree(tmp);
229                             return 0;
230                     }
231                     r = &tmp->next;
232             }
233     
234             return (-ENOENT);
235     }
236     
237     /*
238      * Find a routing entry, we only return a FULL match
239      */
240     static struct ipddp_route* ipddp_find_route(struct ipddp_route *rt)
241     {
242             struct ipddp_route *f;
243     
244             for(f = ipddp_route_list; f != NULL; f = f->next)
245             {
246                     if(f->ip == rt->ip
247                             && f->at.s_net == rt->at.s_net
248                             && f->at.s_node == rt->at.s_node)
249                             return (f);
250             }
251     
252             return (NULL);
253     }
254     
255     static int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
256     {
257             struct ipddp_route *rt = (struct ipddp_route *)ifr->ifr_data;
258             struct ipddp_route rcp;
259     
260             if(!capable(CAP_NET_ADMIN))
261                     return -EPERM;
262     
263     	if(copy_from_user(&rcp, rt, sizeof(rcp)))
264     		return -EFAULT;
265     
266             switch(cmd)
267             {
268     		case SIOCADDIPDDPRT:
269                             return (ipddp_create(&rcp));
270     
271                     case SIOCFINDIPDDPRT:
272                             if(copy_to_user(rt, ipddp_find_route(&rcp), sizeof(struct ipddp_route)))
273                                     return -EFAULT;
274                             return 0;
275     
276                     case SIOCDELIPDDPRT:
277                             return (ipddp_delete(&rcp));
278     
279                     default:
280                             return -EINVAL;
281             }
282     }
283     
284     static struct net_device dev_ipddp;
285     
286     MODULE_LICENSE("GPL");
287     MODULE_PARM(ipddp_mode, "i");
288     
289     static int __init ipddp_init_module(void)
290     {
291     	int err;
292     
293     	dev_ipddp.init = ipddp_init;
294     	err=dev_alloc_name(&dev_ipddp, "ipddp%d");
295             if(err < 0)
296                     return err;
297     
298     	if(register_netdev(&dev_ipddp) != 0)
299                     return -EIO;
300     
301     	return 0;
302     }
303     
304     static void __exit ipddp_cleanup_module(void)
305     {
306             struct ipddp_route *p;
307     
308     	unregister_netdev(&dev_ipddp);
309             kfree(dev_ipddp.priv);
310     
311             while (ipddp_route_list) {
312                     p = ipddp_route_list->next;
313                     kfree(ipddp_route_list);
314                     ipddp_route_list = p;
315             }
316     }
317     
318     module_init(ipddp_init_module);
319     module_exit(ipddp_cleanup_module);
320