import of upstream 2.4.34.4 from kernel.org
[linux-2.4.git] / net / ax25 / ax25_route.c
1 /*
2  *      AX.25 release 037
3  *
4  *      This code REQUIRES 2.1.15 or higher/ NET3.038
5  *
6  *      This module:
7  *              This module is free software; you can redistribute it and/or
8  *              modify it under the terms of the GNU General Public License
9  *              as published by the Free Software Foundation; either version
10  *              2 of the License, or (at your option) any later version.
11  *
12  *      Other kernels modules in this kit are generally BSD derived. See the copyright headers.
13  *
14  *
15  *      History
16  *      AX.25 020       Jonathan(G4KLX) First go.
17  *      AX.25 022       Jonathan(G4KLX) Added the actual meat to this - we now have a nice heard list.
18  *      AX.25 025       Alan(GW4PTS)    First cut at autobinding by route scan.
19  *      AX.25 028b      Jonathan(G4KLX) Extracted AX25 control block from the
20  *                                      sock structure. Device removal now
21  *                                      removes the heard structure.
22  *      AX.25 029       Steven(GW7RRM)  Added /proc information for uid/callsign mapping.
23  *                      Jonathan(G4KLX) Handling of IP mode in the routing list and /proc entry.
24  *      AX.25 030       Jonathan(G4KLX) Added digi-peaters to routing table, and
25  *                                      ioctls to manipulate them. Added port
26  *                                      configuration.
27  *      AX.25 031       Jonathan(G4KLX) Added concept of default route.
28  *                      Joerg(DL1BKE)   ax25_rt_build_path() find digipeater list and device by 
29  *                                      destination call. Needed for IP routing via digipeater
30  *                      Jonathan(G4KLX) Added routing for IP datagram packets.
31  *                      Joerg(DL1BKE)   Changed routing for IP datagram and VC to use a default
32  *                                      route if available. Does not overwrite default routes
33  *                                      on route-table overflow anymore.
34  *                      Joerg(DL1BKE)   Fixed AX.25 routing of IP datagram and VC, new ioctl()
35  *                                      "SIOCAX25OPTRT" to set IP mode and a 'permanent' flag
36  *                                      on routes.
37  *      AX.25 033       Jonathan(G4KLX) Remove auto-router.
38  *                      Joerg(DL1BKE)   Moved BPQ Ethernet driver to separate device.
39  *      AX.25 035       Frederic(F1OAT) Support for pseudo-digipeating.
40  *                      Jonathan(G4KLX) Support for packet forwarding.
41  *                      Arnaldo C. Melo s/suser/capable/
42  */
43
44 #include <linux/errno.h>
45 #include <linux/types.h>
46 #include <linux/socket.h>
47 #include <linux/in.h>
48 #include <linux/kernel.h>
49 #include <linux/sched.h>
50 #include <linux/timer.h>
51 #include <linux/string.h>
52 #include <linux/sockios.h>
53 #include <linux/net.h>
54 #include <net/ax25.h>
55 #include <linux/inet.h>
56 #include <linux/netdevice.h>
57 #include <linux/if_arp.h>
58 #include <linux/skbuff.h>
59 #include <net/sock.h>
60 #include <asm/uaccess.h>
61 #include <asm/system.h>
62 #include <linux/fcntl.h>
63 #include <linux/mm.h>
64 #include <linux/interrupt.h>
65 #include <linux/init.h>
66
67 static ax25_route *ax25_route_list;
68
69 static ax25_route *ax25_find_route(ax25_address *, struct net_device *);
70
71 /*
72  * small macro to drop non-digipeated digipeaters and reverse path
73  */
74 static inline void ax25_route_invert(ax25_digi *in, ax25_digi *out)
75 {
76         int k;
77
78         for (k = 0; k < in->ndigi; k++)
79                 if (!in->repeated[k])
80                         break;
81
82         in->ndigi = k;
83
84         ax25_digi_invert(in, out);
85 }
86
87 void ax25_rt_device_down(struct net_device *dev)
88 {
89         ax25_route *s, *t, *ax25_rt = ax25_route_list;
90         
91         while (ax25_rt != NULL) {
92                 s       = ax25_rt;
93                 ax25_rt = ax25_rt->next;
94
95                 if (s->dev == dev) {
96                         if (ax25_route_list == s) {
97                                 ax25_route_list = s->next;
98                                 if (s->digipeat != NULL)
99                                         kfree(s->digipeat);
100                                 kfree(s);
101                         } else {
102                                 for (t = ax25_route_list; t != NULL; t = t->next) {
103                                         if (t->next == s) {
104                                                 t->next = s->next;
105                                                 if (s->digipeat != NULL)
106                                                         kfree(s->digipeat);
107                                                 kfree(s);
108                                                 break;
109                                         }
110                                 }
111                         }
112                 }
113         }
114 }
115
116 int ax25_rt_ioctl(unsigned int cmd, void *arg)
117 {
118         unsigned long flags;
119         ax25_route *s, *t, *ax25_rt;
120         struct ax25_routes_struct route;
121         struct ax25_route_opt_struct rt_option;
122         ax25_dev *ax25_dev;
123         int i;
124
125         switch (cmd) {
126                 case SIOCADDRT:
127                         if (copy_from_user(&route, arg, sizeof(route)))
128                                 return -EFAULT;
129                         if ((ax25_dev = ax25_addr_ax25dev(&route.port_addr)) == NULL)
130                                 return -EINVAL;
131                         if (route.digi_count > AX25_MAX_DIGIS)
132                                 return -EINVAL;
133                         for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
134                                 if (ax25cmp(&ax25_rt->callsign, &route.dest_addr) == 0 && ax25_rt->dev == ax25_dev->dev) {
135                                         if (ax25_rt->digipeat != NULL) {
136                                                 kfree(ax25_rt->digipeat);
137                                                 ax25_rt->digipeat = NULL;
138                                         }
139                                         if (route.digi_count != 0) {
140                                                 if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL)
141                                                         return -ENOMEM;
142                                                 ax25_rt->digipeat->lastrepeat = -1;
143                                                 ax25_rt->digipeat->ndigi      = route.digi_count;
144                                                 for (i = 0; i < route.digi_count; i++) {
145                                                         ax25_rt->digipeat->repeated[i] = 0;
146                                                         ax25_rt->digipeat->calls[i]    = route.digi_addr[i];
147                                                 }
148                                         }
149                                         return 0;
150                                 }
151                         }
152                         if ((ax25_rt = kmalloc(sizeof(ax25_route), GFP_ATOMIC)) == NULL)
153                                 return -ENOMEM;
154                         ax25_rt->callsign     = route.dest_addr;
155                         ax25_rt->dev          = ax25_dev->dev;
156                         ax25_rt->digipeat     = NULL;
157                         ax25_rt->ip_mode      = ' ';
158                         if (route.digi_count != 0) {
159                                 if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
160                                         kfree(ax25_rt);
161                                         return -ENOMEM;
162                                 }
163                                 ax25_rt->digipeat->lastrepeat = -1;
164                                 ax25_rt->digipeat->ndigi      = route.digi_count;
165                                 for (i = 0; i < route.digi_count; i++) {
166                                         ax25_rt->digipeat->repeated[i] = 0;
167                                         ax25_rt->digipeat->calls[i]    = route.digi_addr[i];
168                                 }
169                         }
170                         save_flags(flags); cli();
171                         ax25_rt->next   = ax25_route_list;
172                         ax25_route_list = ax25_rt;
173                         restore_flags(flags);
174                         break;
175
176                 case SIOCDELRT:
177                         if (copy_from_user(&route, arg, sizeof(route)))
178                                 return -EFAULT;
179                         if ((ax25_dev = ax25_addr_ax25dev(&route.port_addr)) == NULL)
180                                 return -EINVAL;
181                         ax25_rt = ax25_route_list;
182                         while (ax25_rt != NULL) {
183                                 s       = ax25_rt;
184                                 ax25_rt = ax25_rt->next;
185                                 if (s->dev == ax25_dev->dev && ax25cmp(&route.dest_addr, &s->callsign) == 0) {
186                                         if (ax25_route_list == s) {
187                                                 ax25_route_list = s->next;
188                                                 if (s->digipeat != NULL)
189                                                         kfree(s->digipeat);
190                                                 kfree(s);
191                                         } else {
192                                                 for (t = ax25_route_list; t != NULL; t = t->next) {
193                                                         if (t->next == s) {
194                                                                 t->next = s->next;
195                                                                 if (s->digipeat != NULL)
196                                                                         kfree(s->digipeat);
197                                                                 kfree(s);
198                                                                 break;
199                                                         }
200                                                 }                               
201                                         }
202                                 }
203                         }
204                         break;
205
206                 case SIOCAX25OPTRT:
207                         if (copy_from_user(&rt_option, arg, sizeof(rt_option)))
208                                 return -EFAULT;
209                         if ((ax25_dev = ax25_addr_ax25dev(&rt_option.port_addr)) == NULL)
210                                 return -EINVAL;
211                         for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
212                                 if (ax25_rt->dev == ax25_dev->dev && ax25cmp(&rt_option.dest_addr, &ax25_rt->callsign) == 0) {
213                                         switch (rt_option.cmd) {
214                                                 case AX25_SET_RT_IPMODE:
215                                                         switch (rt_option.arg) {
216                                                                 case ' ':
217                                                                 case 'D':
218                                                                 case 'V':
219                                                                         ax25_rt->ip_mode = rt_option.arg;
220                                                                         break;
221                                                                 default:
222                                                                         return -EINVAL;
223                                                         }
224                                                         break;
225                                                 default:
226                                                         return -EINVAL;
227                                         }
228                                 }
229                         }
230                         break;
231
232                 default:
233                         return -EINVAL;
234         }
235
236         return 0;
237 }
238
239 int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length)
240 {
241         ax25_route *ax25_rt;
242         int len     = 0;
243         off_t pos   = 0;
244         off_t begin = 0;
245         char *callsign;
246         int i;
247   
248         cli();
249
250         len += sprintf(buffer, "callsign  dev  mode digipeaters\n");
251
252         for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
253                 if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0)
254                         callsign = "default";
255                 else
256                         callsign = ax2asc(&ax25_rt->callsign);
257                 len += sprintf(buffer + len, "%-9s %-4s",
258                         callsign,
259                         ax25_rt->dev ? ax25_rt->dev->name : "???");
260
261                 switch (ax25_rt->ip_mode) {
262                         case 'V':
263                                 len += sprintf(buffer + len, "   vc");
264                                 break;
265                         case 'D':
266                                 len += sprintf(buffer + len, "   dg");
267                                 break;
268                         default:
269                                 len += sprintf(buffer + len, "    *");
270                                 break;
271                 }
272
273                 if (ax25_rt->digipeat != NULL)
274                         for (i = 0; i < ax25_rt->digipeat->ndigi; i++)
275                                 len += sprintf(buffer + len, " %s", ax2asc(&ax25_rt->digipeat->calls[i]));
276
277                 len += sprintf(buffer + len, "\n");
278
279                 pos = begin + len;
280
281                 if (pos < offset) {
282                         len   = 0;
283                         begin = pos;
284                 }
285
286                 if (pos > offset + length)
287                         break;
288         }
289
290         sti();
291
292         *start = buffer + (offset - begin);
293         len   -= (offset - begin);
294
295         if (len > length) len = length;
296
297         return len;
298
299
300 /*
301  *      Find AX.25 route
302  */
303 static ax25_route *ax25_find_route(ax25_address *addr, struct net_device *dev)
304 {
305         ax25_route *ax25_spe_rt = NULL;
306         ax25_route *ax25_def_rt = NULL;
307         ax25_route *ax25_rt;
308
309         /*
310          *      Bind to the physical interface we heard them on, or the default
311          *      route if none is found;
312          */
313         for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
314                 if (dev == NULL) {
315                         if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev != NULL)
316                                 ax25_spe_rt = ax25_rt;
317                         if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev != NULL)
318                                 ax25_def_rt = ax25_rt;
319                 } else {
320                         if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev == dev)
321                                 ax25_spe_rt = ax25_rt;
322                         if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev == dev)
323                                 ax25_def_rt = ax25_rt;
324                 }
325         }
326
327         if (ax25_spe_rt != NULL)
328                 return ax25_spe_rt;
329
330         return ax25_def_rt;
331 }
332
333 /*
334  *      Adjust path: If you specify a default route and want to connect
335  *      a target on the digipeater path but w/o having a special route
336  *      set before, the path has to be truncated from your target on.
337  */
338 static inline void ax25_adjust_path(ax25_address *addr, ax25_digi *digipeat)
339 {
340         int k;
341
342         for (k = 0; k < digipeat->ndigi; k++) {
343                 if (ax25cmp(addr, &digipeat->calls[k]) == 0)
344                         break;
345         }
346
347         digipeat->ndigi = k;
348 }
349  
350
351 /*
352  *      Find which interface to use.
353  */
354 int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr)
355 {
356         ax25_route *ax25_rt;
357         ax25_address *call;
358
359         if ((ax25_rt = ax25_find_route(addr, NULL)) == NULL)
360                 return -EHOSTUNREACH;
361
362         if ((ax25->ax25_dev = ax25_dev_ax25dev(ax25_rt->dev)) == NULL)
363                 return -EHOSTUNREACH;
364
365         if ((call = ax25_findbyuid(current->euid)) == NULL) {
366                 if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE))
367                         return -EPERM;
368                 call = (ax25_address *)ax25->ax25_dev->dev->dev_addr;
369         }
370
371         ax25->source_addr = *call;
372
373         if (ax25_rt->digipeat != NULL) {
374                 if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL)
375                         return -ENOMEM;
376                 memcpy(ax25->digipeat, ax25_rt->digipeat, sizeof(ax25_digi));
377                 ax25_adjust_path(addr, ax25->digipeat);
378         }
379
380         if (ax25->sk != NULL)
381                 ax25->sk->zapped = 0;
382
383         return 0;
384 }
385
386 /*
387  *      dl1bke 960117: build digipeater path
388  *      dl1bke 960301: use the default route if it exists
389  */
390 ax25_route *ax25_rt_find_route(ax25_address *addr, struct net_device *dev)
391 {
392         static ax25_route route;
393         ax25_route *ax25_rt;
394
395         if ((ax25_rt = ax25_find_route(addr, dev)) == NULL) {
396                 route.next     = NULL;
397                 route.callsign = *addr;
398                 route.dev      = dev;
399                 route.digipeat = NULL;
400                 route.ip_mode  = ' ';
401                 return &route;
402         }
403
404         return ax25_rt;
405 }
406
407 struct sk_buff *ax25_rt_build_path(struct sk_buff *skb, ax25_address *src, ax25_address *dest, ax25_digi *digi)
408 {
409         struct sk_buff *skbn;
410         unsigned char *bp;
411         int len;
412
413         len = digi->ndigi * AX25_ADDR_LEN;
414
415         if (skb_headroom(skb) < len) {
416                 if ((skbn = skb_realloc_headroom(skb, len)) == NULL) {
417                         printk(KERN_CRIT "AX.25: ax25_dg_build_path - out of memory\n");
418                         return NULL;
419                 }
420
421                 if (skb->sk != NULL)
422                         skb_set_owner_w(skbn, skb->sk);
423
424                 kfree_skb(skb);
425
426                 skb = skbn;
427         }
428
429         bp = skb_push(skb, len);
430
431         ax25_addr_build(bp, src, dest, digi, AX25_COMMAND, AX25_MODULUS);
432
433         return skb;
434 }
435
436 /*
437  *      Free all memory associated with routing structures.
438  */
439 void __exit ax25_rt_free(void)
440 {
441         ax25_route *s, *ax25_rt = ax25_route_list;
442
443         while (ax25_rt != NULL) {
444                 s       = ax25_rt;
445                 ax25_rt = ax25_rt->next;
446
447                 if (s->digipeat != NULL)
448                         kfree(s->digipeat);
449
450                 kfree(s);
451         }
452 }