diff options
Diffstat (limited to 'net/ax25/ax25_route.c')
-rw-r--r-- | net/ax25/ax25_route.c | 594 |
1 files changed, 279 insertions, 315 deletions
diff --git a/net/ax25/ax25_route.c b/net/ax25/ax25_route.c index 254ff36fb..cf468c65f 100644 --- a/net/ax25/ax25_route.c +++ b/net/ax25/ax25_route.c @@ -1,278 +1,246 @@ /* - * AX.25 release 037 + * ax25_route.c: Routing table management for NEW-AX.25 * - * This code REQUIRES 2.1.15 or higher/ NET3.038 + * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF), Jonathan (G4K + * Alan Cox (GW4PTS), Joerg (DL1BKE), et al * - * This module: - * This module is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. + * Comment: * - * Other kernels modules in this kit are generally BSD derived. See the copyright headers. + * Changelog: * - * - * History - * AX.25 020 Jonathan(G4KLX) First go. - * AX.25 022 Jonathan(G4KLX) Added the actual meat to this - we now have a nice heard list. - * AX.25 025 Alan(GW4PTS) First cut at autobinding by route scan. - * AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from the - * sock structure. Device removal now - * removes the heard structure. - * AX.25 029 Steven(GW7RRM) Added /proc information for uid/callsign mapping. - * Jonathan(G4KLX) Handling of IP mode in the routing list and /proc entry. - * AX.25 030 Jonathan(G4KLX) Added digi-peaters to routing table, and - * ioctls to manipulate them. Added port - * configuration. - * AX.25 031 Jonathan(G4KLX) Added concept of default route. - * Joerg(DL1BKE) ax25_rt_build_path() find digipeater list and device by - * destination call. Needed for IP routing via digipeater - * Jonathan(G4KLX) Added routing for IP datagram packets. - * Joerg(DL1BKE) Changed routing for IP datagram and VC to use a default - * route if available. Does not overwrite default routes - * on route-table overflow anymore. - * Joerg(DL1BKE) Fixed AX.25 routing of IP datagram and VC, new ioctl() - * "SIOCAX25OPTRT" to set IP mode and a 'permanent' flag - * on routes. - * AX.25 033 Jonathan(G4KLX) Remove auto-router. - * Joerg(DL1BKE) Moved BPQ Ethernet driver to separate device. - * AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating. - * Jonathan(G4KLX) Support for packet forwarding. - * Arnaldo C. Melo s/suser/capable/ + * License: This module is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. */ -#include <linux/errno.h> +#include <linux/config.h> #include <linux/types.h> #include <linux/socket.h> -#include <linux/in.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/string.h> -#include <linux/sockios.h> -#include <linux/net.h> -#include <net/ax25.h> -#include <linux/inet.h> -#include <linux/netdevice.h> -#include <linux/if_arp.h> -#include <linux/skbuff.h> -#include <net/sock.h> +#include <linux/spinlock.h> #include <asm/uaccess.h> -#include <asm/system.h> -#include <linux/fcntl.h> -#include <linux/mm.h> -#include <linux/interrupt.h> -#include <linux/init.h> +#include <net/sock.h> +#include <net/ax25_uid.h> -static ax25_route *ax25_route_list; +#include "af_ax25.h" +#include "ax25_core.h" +#include "ax25_route.h" -static ax25_route *ax25_find_route(ax25_address *, struct net_device *); +static struct ax25_route *ax25_route = NULL; +rwlock_t ax25_rt_lock = RW_LOCK_UNLOCKED; /* - * small macro to drop non-digipeated digipeaters and reverse path + * delete all routes on a given device */ -static inline void ax25_route_invert(ax25_digi *in, ax25_digi *out) +void ax25_rt_device_down(struct net_device *dev) { - int k; + struct ax25_route *this, *last; - for (k = 0; k < in->ndigi; k++) - if (!in->repeated[k]) - break; + write_lock(&ax25_rt_lock); + + this = ax25_route; + last = NULL; - in->ndigi = k; + while (this) { + if (this->dev != dev) { + last = this; + this = this->next; + continue; + } + if (!last) { + ax25_route = this->next; + kfree(this); + this = ax25_route; + continue; + } + last->next = this->next; + kfree(this); + this = last->next; + } - ax25_digi_invert(in, out); + write_unlock(&ax25_rt_lock); } -void ax25_rt_device_down(struct net_device *dev) +int ax25_add_route(ax25_path_t *path, struct net_device *dev) { - ax25_route *s, *t, *ax25_rt = ax25_route_list; - - while (ax25_rt != NULL) { - s = ax25_rt; - ax25_rt = ax25_rt->next; + struct ax25_route *this; + + write_lock(&ax25_rt_lock); + + for (this = ax25_route; this; this = this->next) { + if (ax25cmp(&this->path.addr, &path->addr) == 0) { + this->path = *path; + this->dev = dev; + return 0; + } + } + + if ((this = (struct ax25_route *)kmalloc(sizeof(struct ax25_route), GFP_KERNEL)) == NULL) + return -ENOMEM; + this->path = *path; + this->dev = dev; + this->ip_mode = ' '; + this->next = ax25_route; + ax25_route = this; + + write_unlock(&ax25_rt_lock); + + return 0; +} + +int ax25_del_route(ax25_address *addr) +{ + struct ax25_route *this, *last; + + write_lock(&ax25_rt_lock); + + this = ax25_route; + last = NULL; + while (this != NULL) { + if (ax25cmp(&this->path.addr, addr) != 0) { + last = this; + this = this->next; + continue; + } + if (!last) { + ax25_route = this->next; + kfree(this); + return 0; + } + last->next = this->next; + kfree(this); + return 0; + } + + write_unlock(&ax25_rt_lock); + + return -EINVAL; +} + +int ax25_ipopt_route(ax25_address *addr, unsigned char opt) +{ + struct ax25_route *this; + int err = -EINVAL; + + write_lock(&ax25_rt_lock); - if (s->dev == dev) { - if (ax25_route_list == s) { - ax25_route_list = s->next; - if (s->digipeat != NULL) - kfree(s->digipeat); - kfree(s); - } else { - for (t = ax25_route_list; t != NULL; t = t->next) { - if (t->next == s) { - t->next = s->next; - if (s->digipeat != NULL) - kfree(s->digipeat); - kfree(s); - break; - } - } + for (this = ax25_route; this; this = this->next) { + if (ax25cmp(&this->path.addr, addr) == 0) { + switch(opt) { + case ' ': + case 'D': + case 'V': + case 'C': + this->ip_mode = opt; + err = 0; + break; } } } + + write_unlock(&ax25_rt_lock); + + return err; } int ax25_rt_ioctl(unsigned int cmd, void *arg) { - unsigned long flags; - ax25_route *s, *t, *ax25_rt; struct ax25_routes_struct route; struct ax25_route_opt_struct rt_option; - ax25_dev *ax25_dev; + struct net_device *dev; + ax25_path_t ax25_path; + int err = -EINVAL; int i; switch (cmd) { - case SIOCADDRT: - if (copy_from_user(&route, arg, sizeof(route))) - return -EFAULT; - if ((ax25_dev = ax25_addr_ax25dev(&route.port_addr)) == NULL) - return -EINVAL; - if (route.digi_count > AX25_MAX_DIGIS) - return -EINVAL; - for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) { - if (ax25cmp(&ax25_rt->callsign, &route.dest_addr) == 0 && ax25_rt->dev == ax25_dev->dev) { - if (ax25_rt->digipeat != NULL) { - kfree(ax25_rt->digipeat); - ax25_rt->digipeat = NULL; - } - if (route.digi_count != 0) { - if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) - return -ENOMEM; - ax25_rt->digipeat->lastrepeat = -1; - ax25_rt->digipeat->ndigi = route.digi_count; - for (i = 0; i < route.digi_count; i++) { - ax25_rt->digipeat->repeated[i] = 0; - ax25_rt->digipeat->calls[i] = route.digi_addr[i]; - } - } - return 0; - } - } - if ((ax25_rt = kmalloc(sizeof(ax25_route), GFP_ATOMIC)) == NULL) - return -ENOMEM; - ax25_rt->callsign = route.dest_addr; - ax25_rt->dev = ax25_dev->dev; - ax25_rt->digipeat = NULL; - ax25_rt->ip_mode = ' '; - if (route.digi_count != 0) { - if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) { - kfree(ax25_rt); - return -ENOMEM; - } - ax25_rt->digipeat->lastrepeat = -1; - ax25_rt->digipeat->ndigi = route.digi_count; - for (i = 0; i < route.digi_count; i++) { - ax25_rt->digipeat->repeated[i] = 0; - ax25_rt->digipeat->calls[i] = route.digi_addr[i]; - } - } - save_flags(flags); cli(); - ax25_rt->next = ax25_route_list; - ax25_route_list = ax25_rt; - restore_flags(flags); + case SIOCADDRT: + /* do some sanity checks */ + if (copy_from_user(&route, arg, sizeof(route))) { + err = -EFAULT; + break; + } + if ((dev = ax25rtr_get_dev(&route.port_addr)) == NULL) + break; + if (route.digi_count > AX25_MAX_DIGIS) break; - case SIOCDELRT: - if (copy_from_user(&route, arg, sizeof(route))) - return -EFAULT; - if ((ax25_dev = ax25_addr_ax25dev(&route.port_addr)) == NULL) - return -EINVAL; - ax25_rt = ax25_route_list; - while (ax25_rt != NULL) { - s = ax25_rt; - ax25_rt = ax25_rt->next; - if (s->dev == ax25_dev->dev && ax25cmp(&route.dest_addr, &s->callsign) == 0) { - if (ax25_route_list == s) { - ax25_route_list = s->next; - if (s->digipeat != NULL) - kfree(s->digipeat); - kfree(s); - } else { - for (t = ax25_route_list; t != NULL; t = t->next) { - if (t->next == s) { - t->next = s->next; - if (s->digipeat != NULL) - kfree(s->digipeat); - kfree(s); - break; - } - } - } - } - } + ax25_path.addr = route.dest_addr; + for (i = 0; i < route.digi_count; i++) + ax25_path.digipeater[i] = route.digi_addr[i]; + ax25_path.dcount = route.digi_count; + err = ax25_add_route(&ax25_path, dev); + break; + + case SIOCDELRT: + /* sanity checks */ + if (copy_from_user(&route, arg, sizeof(route))) { + err = -EFAULT; + break; + } + if ((dev = ax25rtr_get_dev(&route.port_addr)) == NULL) break; - case SIOCAX25OPTRT: - if (copy_from_user(&rt_option, arg, sizeof(rt_option))) - return -EFAULT; - if ((ax25_dev = ax25_addr_ax25dev(&rt_option.port_addr)) == NULL) - return -EINVAL; - for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) { - if (ax25_rt->dev == ax25_dev->dev && ax25cmp(&rt_option.dest_addr, &ax25_rt->callsign) == 0) { - switch (rt_option.cmd) { - case AX25_SET_RT_IPMODE: - switch (rt_option.arg) { - case ' ': - case 'D': - case 'V': - ax25_rt->ip_mode = rt_option.arg; - break; - default: - return -EINVAL; - } - break; - default: - return -EINVAL; - } - } - } + err = ax25_del_route(&route.dest_addr); + break; + + case SIOCAX25OPTRT: + /* sanity checks */ + if (copy_from_user(&rt_option, arg, sizeof(rt_option))) { + err = -EFAULT; + break; + } + if ((dev = ax25rtr_get_dev(&rt_option.port_addr)) == NULL) break; - default: - return -EINVAL; + switch (rt_option.cmd) { + case AX25_SET_RT_IPMODE: + err = ax25_ipopt_route(&rt_option.dest_addr, rt_option.arg); + break; + } } - return 0; + return err; } int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length) { - ax25_route *ax25_rt; + struct ax25_route *ax25_rt; int len = 0; off_t pos = 0; off_t begin = 0; char *callsign; int i; - cli(); + read_lock(&ax25_rt_lock); len += sprintf(buffer, "callsign dev mode digipeaters\n"); - for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) { - if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0) + for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) { + if (ax25cmp(&ax25_rt->path.addr, &null_ax25_address) == 0) callsign = "default"; else - callsign = ax2asc(&ax25_rt->callsign); + callsign = ax2asc(&ax25_rt->path.addr); len += sprintf(buffer + len, "%-9s %-4s", - callsign, - ax25_rt->dev ? ax25_rt->dev->name : "???"); + callsign, + ax25_rt->dev ? ax25_rt->dev->name : "???"); switch (ax25_rt->ip_mode) { - case 'V': - len += sprintf(buffer + len, " vc"); - break; - case 'D': - len += sprintf(buffer + len, " dg"); - break; - default: - len += sprintf(buffer + len, " *"); - break; + case 'V': + len += sprintf(buffer + len, " vc"); + break; + case 'D': + len += sprintf(buffer + len, " dg"); + break; + case 'C': + len += sprintf(buffer + len, " vj"); + break; + default: + len += sprintf(buffer + len, " *"); + break; } - if (ax25_rt->digipeat != NULL) - for (i = 0; i < ax25_rt->digipeat->ndigi; i++) - len += sprintf(buffer + len, " %s", ax2asc(&ax25_rt->digipeat->calls[i])); + for (i = 0; i < ax25_rt->path.dcount; i++) + len += sprintf(buffer + len, " %s", ax2asc(&ax25_rt->path.digipeater[i])); len += sprintf(buffer + len, "\n"); @@ -287,7 +255,7 @@ int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length) break; } - sti(); + read_unlock(&ax25_rt_lock); *start = buffer + (offset - begin); len -= (offset - begin); @@ -300,153 +268,149 @@ int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length) /* * Find AX.25 route */ -static ax25_route *ax25_find_route(ax25_address *addr, struct net_device *dev) +struct ax25_route *ax25_find_route(ax25_address *addr) { - ax25_route *ax25_spe_rt = NULL; - ax25_route *ax25_def_rt = NULL; - ax25_route *ax25_rt; + struct ax25_route *ax25_spe_rt = NULL; + struct ax25_route *ax25_def_rt = NULL; + struct ax25_route *ax25_rt; - /* - * Bind to the physical interface we heard them on, or the default - * route if none is found; - */ - for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) { - if (dev == NULL) { - if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev != NULL) - ax25_spe_rt = ax25_rt; - if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev != NULL) - ax25_def_rt = ax25_rt; - } else { - if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev == dev) - ax25_spe_rt = ax25_rt; - if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev == dev) - ax25_def_rt = ax25_rt; - } + read_lock(&ax25_rt_lock); + + for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) { + if (ax25cmp(&ax25_rt->path.addr, addr) == 0) + ax25_spe_rt = ax25_rt; + if (ax25cmp(&ax25_rt->path.addr, &null_ax25_address) == 0) + ax25_def_rt = ax25_rt; } if (ax25_spe_rt != NULL) return ax25_spe_rt; + read_unlock(&ax25_rt_lock); + return ax25_def_rt; } /* - * Adjust path: If you specify a default route and want to connect - * a target on the digipeater path but w/o having a special route - * set before, the path has to be truncated from your target on. + * MW: This is the core of the digipeating stuff. For a given + * src/dest it finds the appropriate device and digipeaterpath + * to route to. */ -static inline void ax25_adjust_path(ax25_address *addr, ax25_digi *digipeat) +struct net_device *ax25_rt_set_addr(ax25_addr_t *out, ax25_addr_t *in, struct net_device *dev, struct net_device *out_dev) { - int k; + struct ax25_route *ax25rt; + ax25_address *next_dest; + ax25_address *dptr; + int more_digis; - for (k = 0; k < digipeat->ndigi; k++) { - if (ax25cmp(addr, &digipeat->calls[k]) == 0) - break; - } + /* + * find out where to go next. we route the packet either + * to the next digi behind us or to the destination. We + * NEVER route to the destination if there are digipeaters + * left. + */ + more_digis = in->dcount - (in->lastrepeat+2); + if (more_digis > 0) + next_dest = &in->digipeater[in->lastrepeat+2]; + else + next_dest = &in->dest; - digipeat->ndigi = k; -} - + /* + * check for a route. + */ + if ((ax25rt = ax25_find_route(next_dest)) != NULL) + out_dev = ax25rt->dev; -/* - * Find which interface to use. - */ -int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr) -{ - ax25_route *ax25_rt; - ax25_address *call; + /* + * set up the digipeater path. + * for now we just copy the path of the incoming SABM + * up to the digipeater before us, if any. + */ + out->dest = in->dest; + out->src = in->src; - if ((ax25_rt = ax25_find_route(addr, NULL)) == NULL) - return -EHOSTUNREACH; + if (in->lastrepeat >= 0) + memcpy(out->digipeater, in->digipeater, sizeof(ax25_address) * (in->lastrepeat+1)); - if ((ax25->ax25_dev = ax25_dev_ax25dev(ax25_rt->dev)) == NULL) - return -EHOSTUNREACH; + /* + * then we fill in the callsign of the device the frame + * came in. + */ + out->lastrepeat = in->lastrepeat+1; + out->dcount = out->lastrepeat+1; + dptr = &out->digipeater[(int) out->lastrepeat]; + *dptr++ = *((ax25_address *)dev->dev_addr); - if ((call = ax25_findbyuid(current->euid)) == NULL) { - if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE)) - return -EPERM; - call = (ax25_address *)ax25->ax25_dev->dev->dev_addr; + /* + * insert the route to next_dest, if any. + */ + if (ax25rt != NULL && ax25rt->path.dcount != 0) { + memcpy(dptr, ax25rt->path.digipeater, sizeof(ax25_address) * ax25rt->path.dcount); + out->dcount += ax25rt->path.dcount; + dptr += ax25rt->path.dcount; } - ax25->source_addr = *call; - - if (ax25_rt->digipeat != NULL) { - if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) - return -ENOMEM; - memcpy(ax25->digipeat, ax25_rt->digipeat, sizeof(ax25_digi)); - ax25_adjust_path(addr, ax25->digipeat); + /* + * append next_dest if we route to a waypoint + */ + while (more_digis-- > 0 && out->dcount <= AX25_MAX_DIGIS) { + *dptr++ = *next_dest++; + out->dcount++; } - - if (ax25->sk != NULL) - ax25->sk->zapped = 0; - - return 0; + return out_dev; } /* - * dl1bke 960117: build digipeater path - * dl1bke 960301: use the default route if it exists + * Find the device to use */ -ax25_route *ax25_rt_find_route(ax25_address *addr, struct net_device *dev) +int ax25_rt_fillin_dev(ax25_cb *ax25, ax25_address *addr) { - static ax25_route route; - ax25_route *ax25_rt; - - if ((ax25_rt = ax25_find_route(addr, dev)) == NULL) { - route.next = NULL; - route.callsign = *addr; - route.dev = dev; - route.digipeat = NULL; - route.ip_mode = ' '; - return &route; - } + struct ax25_route *ax25_rt; - return ax25_rt; -} + if ((ax25_rt = ax25_find_route(addr)) == NULL) + return -EHOSTUNREACH; -struct sk_buff *ax25_rt_build_path(struct sk_buff *skb, ax25_address *src, ax25_address *dest, ax25_digi *digi) -{ - struct sk_buff *skbn; - unsigned char *bp; - int len; +/* ax25_remove_cb(ax25); */ + ax25_fillin_cb(ax25, ax25_rt->dev); +/* ax25_insert_cb(ax25); */ - len = digi->ndigi * AX25_ADDR_LEN; + return 0; +} - if (skb_headroom(skb) < len) { - if ((skbn = skb_realloc_headroom(skb, len)) == NULL) { - printk(KERN_CRIT "AX.25: ax25_dg_build_path - out of memory\n"); - return NULL; - } +/* + * Return the IP mode of a given callsign/device pair. + */ +char ax25_rt_mode_get(ax25_address *callsign) +{ + struct ax25_route *ax25_rt; - if (skb->sk != NULL) - skb_set_owner_w(skbn, skb->sk); + read_lock(&ax25_rt_lock); - kfree_skb(skb); + for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) + if (ax25cmp(&ax25_rt->path.addr, callsign) == 0) + return ax25_rt->ip_mode; - skb = skbn; - } + read_unlock(&ax25_rt_lock); - bp = skb_push(skb, len); + return ' '; +} - ax25_addr_build(bp, src, dest, digi, AX25_COMMAND, AX25_MODULUS); - return skb; -} /* - * Free all memory associated with routing structures. + * Free all memory associated with routing and device structures. */ -void __exit ax25_rt_free(void) +void ax25_rt_free(void) { - ax25_route *s, *ax25_rt = ax25_route_list; + struct ax25_route *s, *ax25_rt = ax25_route; + + write_lock(&ax25_rt_lock); while (ax25_rt != NULL) { s = ax25_rt; ax25_rt = ax25_rt->next; - - if (s->digipeat != NULL) - kfree(s->digipeat); - kfree(s); } + + write_unlock(&ax25_rt_lock); } |