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