• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/drivers/net/appletalk/
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/module.h>
27#include <linux/kernel.h>
28#include <linux/init.h>
29#include <linux/netdevice.h>
30#include <linux/etherdevice.h>
31#include <linux/ip.h>
32#include <linux/atalk.h>
33#include <linux/if_arp.h>
34#include <linux/slab.h>
35#include <net/route.h>
36#include <asm/uaccess.h>
37
38#include "ipddp.h"		/* Our stuff */
39
40static const char version[] = KERN_INFO "ipddp.c:v0.01 8/28/97 Bradford W. Johnson <johns393@maroon.tc.umn.edu>\n";
41
42static struct ipddp_route *ipddp_route_list;
43static DEFINE_SPINLOCK(ipddp_route_lock);
44
45#ifdef CONFIG_IPDDP_ENCAP
46static int ipddp_mode = IPDDP_ENCAP;
47#else
48static int ipddp_mode = IPDDP_DECAP;
49#endif
50
51/* Index to functions, as function prototypes. */
52static netdev_tx_t ipddp_xmit(struct sk_buff *skb,
53				    struct net_device *dev);
54static int ipddp_create(struct ipddp_route *new_rt);
55static int ipddp_delete(struct ipddp_route *rt);
56static struct ipddp_route* __ipddp_find_route(struct ipddp_route *rt);
57static int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
58
59static const struct net_device_ops ipddp_netdev_ops = {
60	.ndo_start_xmit		= ipddp_xmit,
61	.ndo_do_ioctl   	= ipddp_ioctl,
62	.ndo_change_mtu		= eth_change_mtu,
63	.ndo_set_mac_address 	= eth_mac_addr,
64	.ndo_validate_addr	= eth_validate_addr,
65};
66
67static struct net_device * __init ipddp_init(void)
68{
69	static unsigned version_printed;
70	struct net_device *dev;
71	int err;
72
73	dev = alloc_etherdev(0);
74	if (!dev)
75		return ERR_PTR(-ENOMEM);
76
77	dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
78	strcpy(dev->name, "ipddp%d");
79
80	if (version_printed++ == 0)
81                printk(version);
82
83	/* Initialize the device structure. */
84	dev->netdev_ops = &ipddp_netdev_ops;
85
86        dev->type = ARPHRD_IPDDP;       	/* IP over DDP tunnel */
87        dev->mtu = 585;
88        dev->flags |= IFF_NOARP;
89
90        /*
91         *      The worst case header we will need is currently a
92         *      ethernet header (14 bytes) and a ddp header (sizeof ddpehdr+1)
93         *      We send over SNAP so that takes another 8 bytes.
94         */
95        dev->hard_header_len = 14+8+sizeof(struct ddpehdr)+1;
96
97	err = register_netdev(dev);
98	if (err) {
99		free_netdev(dev);
100		return ERR_PTR(err);
101	}
102
103	/* Let the user now what mode we are in */
104	if(ipddp_mode == IPDDP_ENCAP)
105		printk("%s: Appletalk-IP Encap. mode by Bradford W. Johnson <johns393@maroon.tc.umn.edu>\n",
106			dev->name);
107	if(ipddp_mode == IPDDP_DECAP)
108		printk("%s: Appletalk-IP Decap. mode by Jay Schulist <jschlst@samba.org>\n",
109			dev->name);
110
111        return dev;
112}
113
114
115/*
116 * Transmit LLAP/ELAP frame using aarp_send_ddp.
117 */
118static netdev_tx_t ipddp_xmit(struct sk_buff *skb, struct net_device *dev)
119{
120	__be32 paddr = skb_rtable(skb)->rt_gateway;
121        struct ddpehdr *ddp;
122        struct ipddp_route *rt;
123        struct atalk_addr *our_addr;
124
125	spin_lock(&ipddp_route_lock);
126
127	/*
128         * Find appropriate route to use, based only on IP number.
129         */
130        for(rt = ipddp_route_list; rt != NULL; rt = rt->next)
131        {
132                if(rt->ip == paddr)
133                        break;
134        }
135        if(rt == NULL) {
136		spin_unlock(&ipddp_route_lock);
137                return NETDEV_TX_OK;
138	}
139
140        our_addr = atalk_find_dev_addr(rt->dev);
141
142	if(ipddp_mode == IPDDP_DECAP)
143		/*
144		 * Pull off the excess room that should not be there.
145		 * This is due to a hard-header problem. This is the
146		 * quick fix for now though, till it breaks.
147		 */
148		skb_pull(skb, 35-(sizeof(struct ddpehdr)+1));
149
150	/* Create the Extended DDP header */
151	ddp = (struct ddpehdr *)skb->data;
152        ddp->deh_len_hops = htons(skb->len + (1<<10));
153        ddp->deh_sum = 0;
154
155	/*
156         * For Localtalk we need aarp_send_ddp to strip the
157         * long DDP header and place a shot DDP header on it.
158         */
159        if(rt->dev->type == ARPHRD_LOCALTLK)
160        {
161                ddp->deh_dnet  = 0;
162                ddp->deh_snet  = 0;
163        }
164        else
165        {
166                ddp->deh_dnet  = rt->at.s_net;
167                ddp->deh_snet  = our_addr->s_net;
168        }
169        ddp->deh_dnode = rt->at.s_node;
170        ddp->deh_snode = our_addr->s_node;
171        ddp->deh_dport = 72;
172        ddp->deh_sport = 72;
173
174        *((__u8 *)(ddp+1)) = 22;        	/* ddp type = IP */
175
176        skb->protocol = htons(ETH_P_ATALK);     /* Protocol has changed */
177
178	dev->stats.tx_packets++;
179	dev->stats.tx_bytes += skb->len;
180
181	aarp_send_ddp(rt->dev, skb, &rt->at, NULL);
182
183	spin_unlock(&ipddp_route_lock);
184
185        return NETDEV_TX_OK;
186}
187
188/*
189 * Create a routing entry. We first verify that the
190 * record does not already exist. If it does we return -EEXIST
191 */
192static int ipddp_create(struct ipddp_route *new_rt)
193{
194        struct ipddp_route *rt = kmalloc(sizeof(*rt), GFP_KERNEL);
195
196        if (rt == NULL)
197                return -ENOMEM;
198
199        rt->ip = new_rt->ip;
200        rt->at = new_rt->at;
201        rt->next = NULL;
202        if ((rt->dev = atrtr_get_dev(&rt->at)) == NULL) {
203		kfree(rt);
204                return -ENETUNREACH;
205        }
206
207	spin_lock_bh(&ipddp_route_lock);
208	if (__ipddp_find_route(rt)) {
209		spin_unlock_bh(&ipddp_route_lock);
210		kfree(rt);
211		return -EEXIST;
212	}
213
214        rt->next = ipddp_route_list;
215        ipddp_route_list = rt;
216
217	spin_unlock_bh(&ipddp_route_lock);
218
219        return 0;
220}
221
222/*
223 * Delete a route, we only delete a FULL match.
224 * If route does not exist we return -ENOENT.
225 */
226static int ipddp_delete(struct ipddp_route *rt)
227{
228        struct ipddp_route **r = &ipddp_route_list;
229        struct ipddp_route *tmp;
230
231	spin_lock_bh(&ipddp_route_lock);
232        while((tmp = *r) != NULL)
233        {
234                if(tmp->ip == rt->ip &&
235		   tmp->at.s_net == rt->at.s_net &&
236		   tmp->at.s_node == rt->at.s_node)
237                {
238                        *r = tmp->next;
239			spin_unlock_bh(&ipddp_route_lock);
240                        kfree(tmp);
241                        return 0;
242                }
243                r = &tmp->next;
244        }
245
246	spin_unlock_bh(&ipddp_route_lock);
247        return (-ENOENT);
248}
249
250/*
251 * Find a routing entry, we only return a FULL match
252 */
253static struct ipddp_route* __ipddp_find_route(struct ipddp_route *rt)
254{
255        struct ipddp_route *f;
256
257        for(f = ipddp_route_list; f != NULL; f = f->next)
258        {
259                if(f->ip == rt->ip &&
260		   f->at.s_net == rt->at.s_net &&
261		   f->at.s_node == rt->at.s_node)
262                        return (f);
263        }
264
265        return (NULL);
266}
267
268static int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
269{
270        struct ipddp_route __user *rt = ifr->ifr_data;
271        struct ipddp_route rcp, rcp2, *rp;
272
273        if(!capable(CAP_NET_ADMIN))
274                return -EPERM;
275
276	if(copy_from_user(&rcp, rt, sizeof(rcp)))
277		return -EFAULT;
278
279        switch(cmd)
280        {
281		case SIOCADDIPDDPRT:
282                        return (ipddp_create(&rcp));
283
284                case SIOCFINDIPDDPRT:
285			spin_lock_bh(&ipddp_route_lock);
286			rp = __ipddp_find_route(&rcp);
287			if (rp)
288				memcpy(&rcp2, rp, sizeof(rcp2));
289			spin_unlock_bh(&ipddp_route_lock);
290
291			if (rp) {
292				if (copy_to_user(rt, &rcp2,
293						 sizeof(struct ipddp_route)))
294					return -EFAULT;
295				return 0;
296			} else
297				return -ENOENT;
298
299                case SIOCDELIPDDPRT:
300                        return (ipddp_delete(&rcp));
301
302                default:
303                        return -EINVAL;
304        }
305}
306
307static struct net_device *dev_ipddp;
308
309MODULE_LICENSE("GPL");
310module_param(ipddp_mode, int, 0);
311
312static int __init ipddp_init_module(void)
313{
314	dev_ipddp = ipddp_init();
315        if (IS_ERR(dev_ipddp))
316                return PTR_ERR(dev_ipddp);
317	return 0;
318}
319
320static void __exit ipddp_cleanup_module(void)
321{
322        struct ipddp_route *p;
323
324	unregister_netdev(dev_ipddp);
325        free_netdev(dev_ipddp);
326
327        while (ipddp_route_list) {
328                p = ipddp_route_list->next;
329                kfree(ipddp_route_list);
330                ipddp_route_list = p;
331        }
332}
333
334module_init(ipddp_init_module);
335module_exit(ipddp_cleanup_module);
336