0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015 #include <linux/if_arp.h>
0016 #include <linux/init.h>
0017 #include <linux/slab.h>
0018 #include <net/x25.h>
0019
0020 LIST_HEAD(x25_route_list);
0021 DEFINE_RWLOCK(x25_route_list_lock);
0022
0023
0024
0025
0026 static int x25_add_route(struct x25_address *address, unsigned int sigdigits,
0027 struct net_device *dev)
0028 {
0029 struct x25_route *rt;
0030 int rc = -EINVAL;
0031
0032 write_lock_bh(&x25_route_list_lock);
0033
0034 list_for_each_entry(rt, &x25_route_list, node) {
0035 if (!memcmp(&rt->address, address, sigdigits) &&
0036 rt->sigdigits == sigdigits)
0037 goto out;
0038 }
0039
0040 rt = kmalloc(sizeof(*rt), GFP_ATOMIC);
0041 rc = -ENOMEM;
0042 if (!rt)
0043 goto out;
0044
0045 strcpy(rt->address.x25_addr, "000000000000000");
0046 memcpy(rt->address.x25_addr, address->x25_addr, sigdigits);
0047
0048 rt->sigdigits = sigdigits;
0049 rt->dev = dev;
0050 refcount_set(&rt->refcnt, 1);
0051
0052 list_add(&rt->node, &x25_route_list);
0053 rc = 0;
0054 out:
0055 write_unlock_bh(&x25_route_list_lock);
0056 return rc;
0057 }
0058
0059
0060
0061
0062
0063
0064
0065
0066 static void __x25_remove_route(struct x25_route *rt)
0067 {
0068 if (rt->node.next) {
0069 list_del(&rt->node);
0070 x25_route_put(rt);
0071 }
0072 }
0073
0074 static int x25_del_route(struct x25_address *address, unsigned int sigdigits,
0075 struct net_device *dev)
0076 {
0077 struct x25_route *rt;
0078 int rc = -EINVAL;
0079
0080 write_lock_bh(&x25_route_list_lock);
0081
0082 list_for_each_entry(rt, &x25_route_list, node) {
0083 if (!memcmp(&rt->address, address, sigdigits) &&
0084 rt->sigdigits == sigdigits && rt->dev == dev) {
0085 __x25_remove_route(rt);
0086 rc = 0;
0087 break;
0088 }
0089 }
0090
0091 write_unlock_bh(&x25_route_list_lock);
0092 return rc;
0093 }
0094
0095
0096
0097
0098 void x25_route_device_down(struct net_device *dev)
0099 {
0100 struct x25_route *rt;
0101 struct list_head *entry, *tmp;
0102
0103 write_lock_bh(&x25_route_list_lock);
0104
0105 list_for_each_safe(entry, tmp, &x25_route_list) {
0106 rt = list_entry(entry, struct x25_route, node);
0107
0108 if (rt->dev == dev)
0109 __x25_remove_route(rt);
0110 }
0111 write_unlock_bh(&x25_route_list_lock);
0112 }
0113
0114
0115
0116
0117 struct net_device *x25_dev_get(char *devname)
0118 {
0119 struct net_device *dev = dev_get_by_name(&init_net, devname);
0120
0121 if (dev && (!(dev->flags & IFF_UP) || dev->type != ARPHRD_X25)) {
0122 dev_put(dev);
0123 dev = NULL;
0124 }
0125
0126 return dev;
0127 }
0128
0129
0130
0131
0132
0133
0134
0135 struct x25_route *x25_get_route(struct x25_address *addr)
0136 {
0137 struct x25_route *rt, *use = NULL;
0138
0139 read_lock_bh(&x25_route_list_lock);
0140
0141 list_for_each_entry(rt, &x25_route_list, node) {
0142 if (!memcmp(&rt->address, addr, rt->sigdigits)) {
0143 if (!use)
0144 use = rt;
0145 else if (rt->sigdigits > use->sigdigits)
0146 use = rt;
0147 }
0148 }
0149
0150 if (use)
0151 x25_route_hold(use);
0152
0153 read_unlock_bh(&x25_route_list_lock);
0154 return use;
0155 }
0156
0157
0158
0159
0160 int x25_route_ioctl(unsigned int cmd, void __user *arg)
0161 {
0162 struct x25_route_struct rt;
0163 struct net_device *dev;
0164 int rc = -EINVAL;
0165
0166 if (cmd != SIOCADDRT && cmd != SIOCDELRT)
0167 goto out;
0168
0169 rc = -EFAULT;
0170 if (copy_from_user(&rt, arg, sizeof(rt)))
0171 goto out;
0172
0173 rc = -EINVAL;
0174 if (rt.sigdigits > 15)
0175 goto out;
0176
0177 dev = x25_dev_get(rt.device);
0178 if (!dev)
0179 goto out;
0180
0181 if (cmd == SIOCADDRT)
0182 rc = x25_add_route(&rt.address, rt.sigdigits, dev);
0183 else
0184 rc = x25_del_route(&rt.address, rt.sigdigits, dev);
0185 dev_put(dev);
0186 out:
0187 return rc;
0188 }
0189
0190
0191
0192
0193 void __exit x25_route_free(void)
0194 {
0195 struct x25_route *rt;
0196 struct list_head *entry, *tmp;
0197
0198 write_lock_bh(&x25_route_list_lock);
0199 list_for_each_safe(entry, tmp, &x25_route_list) {
0200 rt = list_entry(entry, struct x25_route, node);
0201 __x25_remove_route(rt);
0202 }
0203 write_unlock_bh(&x25_route_list_lock);
0204 }