/* * ax25_route.c: Routing table management for NEW-AX.25 * * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF), Jonathan (G4K * Alan Cox (GW4PTS), Joerg (DL1BKE), et al * * Comment: * * Changelog: * * 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 #include #include #include #include #include #include #include "af_ax25.h" #include "ax25_core.h" #include "ax25_route.h" static struct ax25_route *ax25_route = NULL; rwlock_t ax25_rt_lock = RW_LOCK_UNLOCKED; /* * delete all routes on a given device */ void ax25_rt_device_down(struct net_device *dev) { struct ax25_route *this, *last; write_lock(&ax25_rt_lock); this = ax25_route; last = NULL; 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; } write_unlock(&ax25_rt_lock); } int ax25_add_route(ax25_path_t *path, struct net_device *dev) { 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); 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) { struct ax25_routes_struct route; struct ax25_route_opt_struct rt_option; struct net_device *dev; ax25_path_t ax25_path; int err = -EINVAL; int i; switch (cmd) { 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; 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; 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; switch (rt_option.cmd) { case AX25_SET_RT_IPMODE: err = ax25_ipopt_route(&rt_option.dest_addr, rt_option.arg); break; } } return err; } int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length) { struct ax25_route *ax25_rt; int len = 0; off_t pos = 0; off_t begin = 0; char *callsign; int i; read_lock(&ax25_rt_lock); len += sprintf(buffer, "callsign dev mode digipeaters\n"); 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->path.addr); len += sprintf(buffer + len, "%-9s %-4s", 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; case 'C': len += sprintf(buffer + len, " vj"); break; default: len += sprintf(buffer + len, " *"); break; } 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"); pos = begin + len; if (pos < offset) { len = 0; begin = pos; } if (pos > offset + length) break; } read_unlock(&ax25_rt_lock); *start = buffer + (offset - begin); len -= (offset - begin); if (len > length) len = length; return len; } /* * Find AX.25 route */ struct ax25_route *ax25_find_route(ax25_address *addr) { struct ax25_route *ax25_spe_rt = NULL; struct ax25_route *ax25_def_rt = NULL; struct ax25_route *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; } /* * MW: This is the core of the digipeating stuff. For a given * src/dest it finds the appropriate device and digipeaterpath * to route to. */ struct net_device *ax25_rt_set_addr(ax25_addr_t *out, ax25_addr_t *in, struct net_device *dev, struct net_device *out_dev) { struct ax25_route *ax25rt; ax25_address *next_dest; ax25_address *dptr; int more_digis; /* * 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; /* * check for a route. */ if ((ax25rt = ax25_find_route(next_dest)) != NULL) out_dev = ax25rt->dev; /* * 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 (in->lastrepeat >= 0) memcpy(out->digipeater, in->digipeater, sizeof(ax25_address) * (in->lastrepeat+1)); /* * 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); /* * 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; } /* * append next_dest if we route to a waypoint */ while (more_digis-- > 0 && out->dcount <= AX25_MAX_DIGIS) { *dptr++ = *next_dest++; out->dcount++; } return out_dev; } /* * Find the device to use */ int ax25_rt_fillin_dev(ax25_cb *ax25, ax25_address *addr) { struct ax25_route *ax25_rt; if ((ax25_rt = ax25_find_route(addr)) == NULL) return -EHOSTUNREACH; /* ax25_remove_cb(ax25); */ ax25_fillin_cb(ax25, ax25_rt->dev); /* ax25_insert_cb(ax25); */ return 0; } /* * Return the IP mode of a given callsign/device pair. */ char ax25_rt_mode_get(ax25_address *callsign) { struct ax25_route *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, callsign) == 0) return ax25_rt->ip_mode; read_unlock(&ax25_rt_lock); return ' '; } /* * Free all memory associated with routing and device structures. */ void ax25_rt_free(void) { 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; kfree(s); } write_unlock(&ax25_rt_lock); }