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