diff options
Diffstat (limited to 'net/x25/x25_route.c')
-rw-r--r-- | net/x25/x25_route.c | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/net/x25/x25_route.c b/net/x25/x25_route.c new file mode 100644 index 0000000..2c999cc --- /dev/null +++ b/net/x25/x25_route.c @@ -0,0 +1,223 @@ +/* + * X.25 Packet Layer release 002 + * + * This is ALPHA test software. This code may break your machine, + * randomly fail to work with new releases, misbehave and/or generally + * screw up. It might even work. + * + * This code REQUIRES 2.1.15 or higher + * + * 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. + * + * History + * X.25 001 Jonathan Naylor Started coding. + */ + +#include <linux/if_arp.h> +#include <linux/init.h> +#include <net/x25.h> + +LIST_HEAD(x25_route_list); +DEFINE_RWLOCK(x25_route_list_lock); + +/* + * Add a new route. + */ +static int x25_add_route(struct x25_address *address, unsigned int sigdigits, + struct net_device *dev) +{ + struct x25_route *rt; + struct list_head *entry; + int rc = -EINVAL; + + write_lock_bh(&x25_route_list_lock); + + list_for_each(entry, &x25_route_list) { + rt = list_entry(entry, struct x25_route, node); + + if (!memcmp(&rt->address, address, sigdigits) && + rt->sigdigits == sigdigits) + goto out; + } + + rt = kmalloc(sizeof(*rt), GFP_ATOMIC); + rc = -ENOMEM; + if (!rt) + goto out; + + strcpy(rt->address.x25_addr, "000000000000000"); + memcpy(rt->address.x25_addr, address->x25_addr, sigdigits); + + rt->sigdigits = sigdigits; + rt->dev = dev; + atomic_set(&rt->refcnt, 1); + + list_add(&rt->node, &x25_route_list); + rc = 0; +out: + write_unlock_bh(&x25_route_list_lock); + return rc; +} + +/** + * __x25_remove_route - remove route from x25_route_list + * @rt - route to remove + * + * Remove route from x25_route_list. If it was there. + * Caller must hold x25_route_list_lock. + */ +static void __x25_remove_route(struct x25_route *rt) +{ + if (rt->node.next) { + list_del(&rt->node); + x25_route_put(rt); + } +} + +static int x25_del_route(struct x25_address *address, unsigned int sigdigits, + struct net_device *dev) +{ + struct x25_route *rt; + struct list_head *entry; + int rc = -EINVAL; + + write_lock_bh(&x25_route_list_lock); + + list_for_each(entry, &x25_route_list) { + rt = list_entry(entry, struct x25_route, node); + + if (!memcmp(&rt->address, address, sigdigits) && + rt->sigdigits == sigdigits && rt->dev == dev) { + __x25_remove_route(rt); + rc = 0; + break; + } + } + + write_unlock_bh(&x25_route_list_lock); + return rc; +} + +/* + * A device has been removed, remove its routes. + */ +void x25_route_device_down(struct net_device *dev) +{ + struct x25_route *rt; + struct list_head *entry, *tmp; + + write_lock_bh(&x25_route_list_lock); + + list_for_each_safe(entry, tmp, &x25_route_list) { + rt = list_entry(entry, struct x25_route, node); + + if (rt->dev == dev) + __x25_remove_route(rt); + } + write_unlock_bh(&x25_route_list_lock); + + /* Remove any related forwarding */ + x25_clear_forward_by_dev(dev); +} + +/* + * Check that the device given is a valid X.25 interface that is "up". + */ +struct net_device *x25_dev_get(char *devname) +{ + struct net_device *dev = dev_get_by_name(&init_net, devname); + + if (dev && + (!(dev->flags & IFF_UP) || (dev->type != ARPHRD_X25 +#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE) + && dev->type != ARPHRD_ETHER +#endif + ))) + dev_put(dev); + + return dev; +} + +/** + * x25_get_route - Find a route given an X.25 address. + * @addr - address to find a route for + * + * Find a route given an X.25 address. + */ +struct x25_route *x25_get_route(struct x25_address *addr) +{ + struct x25_route *rt, *use = NULL; + struct list_head *entry; + + read_lock_bh(&x25_route_list_lock); + + list_for_each(entry, &x25_route_list) { + rt = list_entry(entry, struct x25_route, node); + + if (!memcmp(&rt->address, addr, rt->sigdigits)) { + if (!use) + use = rt; + else if (rt->sigdigits > use->sigdigits) + use = rt; + } + } + + if (use) + x25_route_hold(use); + + read_unlock_bh(&x25_route_list_lock); + return use; +} + +/* + * Handle the ioctls that control the routing functions. + */ +int x25_route_ioctl(unsigned int cmd, void __user *arg) +{ + struct x25_route_struct rt; + struct net_device *dev; + int rc = -EINVAL; + + if (cmd != SIOCADDRT && cmd != SIOCDELRT) + goto out; + + rc = -EFAULT; + if (copy_from_user(&rt, arg, sizeof(rt))) + goto out; + + rc = -EINVAL; + if (rt.sigdigits < 0 || rt.sigdigits > 15) + goto out; + + dev = x25_dev_get(rt.device); + if (!dev) + goto out; + + if (cmd == SIOCADDRT) + rc = x25_add_route(&rt.address, rt.sigdigits, dev); + else + rc = x25_del_route(&rt.address, rt.sigdigits, dev); + dev_put(dev); +out: + return rc; +} + +/* + * Release all memory associated with X.25 routing structures. + */ +void __exit x25_route_free(void) +{ + struct x25_route *rt; + struct list_head *entry, *tmp; + + write_lock_bh(&x25_route_list_lock); + list_for_each_safe(entry, tmp, &x25_route_list) { + rt = list_entry(entry, struct x25_route, node); + __x25_remove_route(rt); + } + write_unlock_bh(&x25_route_list_lock); +} |