1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * X.25 Packet Layer release 002 |
4 | * |
5 | * This is ALPHA test software. This code may break your machine, |
6 | * randomly fail to work with new releases, misbehave and/or generally |
7 | * screw up. It might even work. |
8 | * |
9 | * This code REQUIRES 2.1.15 or higher |
10 | * |
11 | * History |
12 | * X.25 001 Jonathan Naylor Started coding. |
13 | */ |
14 | |
15 | #include <linux/if_arp.h> |
16 | #include <linux/init.h> |
17 | #include <linux/slab.h> |
18 | #include <net/x25.h> |
19 | |
20 | LIST_HEAD(x25_route_list); |
21 | DEFINE_RWLOCK(x25_route_list_lock); |
22 | |
23 | /* |
24 | * Add a new route. |
25 | */ |
26 | static int x25_add_route(struct x25_address *address, unsigned int sigdigits, |
27 | struct net_device *dev) |
28 | { |
29 | struct x25_route *rt; |
30 | int rc = -EINVAL; |
31 | |
32 | write_lock_bh(&x25_route_list_lock); |
33 | |
34 | list_for_each_entry(rt, &x25_route_list, node) { |
35 | if (!memcmp(p: &rt->address, q: address, size: sigdigits) && |
36 | rt->sigdigits == sigdigits) |
37 | goto out; |
38 | } |
39 | |
40 | rt = kmalloc(size: sizeof(*rt), GFP_ATOMIC); |
41 | rc = -ENOMEM; |
42 | if (!rt) |
43 | goto out; |
44 | |
45 | strcpy(p: rt->address.x25_addr, q: "000000000000000" ); |
46 | memcpy(rt->address.x25_addr, address->x25_addr, sigdigits); |
47 | |
48 | rt->sigdigits = sigdigits; |
49 | rt->dev = dev; |
50 | refcount_set(r: &rt->refcnt, n: 1); |
51 | |
52 | list_add(new: &rt->node, head: &x25_route_list); |
53 | rc = 0; |
54 | out: |
55 | write_unlock_bh(&x25_route_list_lock); |
56 | return rc; |
57 | } |
58 | |
59 | /** |
60 | * __x25_remove_route - remove route from x25_route_list |
61 | * @rt: route to remove |
62 | * |
63 | * Remove route from x25_route_list. If it was there. |
64 | * Caller must hold x25_route_list_lock. |
65 | */ |
66 | static void __x25_remove_route(struct x25_route *rt) |
67 | { |
68 | if (rt->node.next) { |
69 | list_del(entry: &rt->node); |
70 | x25_route_put(rt); |
71 | } |
72 | } |
73 | |
74 | static int x25_del_route(struct x25_address *address, unsigned int sigdigits, |
75 | struct net_device *dev) |
76 | { |
77 | struct x25_route *rt; |
78 | int rc = -EINVAL; |
79 | |
80 | write_lock_bh(&x25_route_list_lock); |
81 | |
82 | list_for_each_entry(rt, &x25_route_list, node) { |
83 | if (!memcmp(p: &rt->address, q: address, size: sigdigits) && |
84 | rt->sigdigits == sigdigits && rt->dev == dev) { |
85 | __x25_remove_route(rt); |
86 | rc = 0; |
87 | break; |
88 | } |
89 | } |
90 | |
91 | write_unlock_bh(&x25_route_list_lock); |
92 | return rc; |
93 | } |
94 | |
95 | /* |
96 | * A device has been removed, remove its routes. |
97 | */ |
98 | void x25_route_device_down(struct net_device *dev) |
99 | { |
100 | struct x25_route *rt; |
101 | struct list_head *entry, *tmp; |
102 | |
103 | write_lock_bh(&x25_route_list_lock); |
104 | |
105 | list_for_each_safe(entry, tmp, &x25_route_list) { |
106 | rt = list_entry(entry, struct x25_route, node); |
107 | |
108 | if (rt->dev == dev) |
109 | __x25_remove_route(rt); |
110 | } |
111 | write_unlock_bh(&x25_route_list_lock); |
112 | } |
113 | |
114 | /* |
115 | * Check that the device given is a valid X.25 interface that is "up". |
116 | */ |
117 | struct net_device *x25_dev_get(char *devname) |
118 | { |
119 | struct net_device *dev = dev_get_by_name(net: &init_net, name: devname); |
120 | |
121 | if (dev && (!(dev->flags & IFF_UP) || dev->type != ARPHRD_X25)) { |
122 | dev_put(dev); |
123 | dev = NULL; |
124 | } |
125 | |
126 | return dev; |
127 | } |
128 | |
129 | /** |
130 | * x25_get_route - Find a route given an X.25 address. |
131 | * @addr: - address to find a route for |
132 | * |
133 | * Find a route given an X.25 address. |
134 | */ |
135 | struct x25_route *x25_get_route(struct x25_address *addr) |
136 | { |
137 | struct x25_route *rt, *use = NULL; |
138 | |
139 | read_lock_bh(&x25_route_list_lock); |
140 | |
141 | list_for_each_entry(rt, &x25_route_list, node) { |
142 | if (!memcmp(p: &rt->address, q: addr, size: rt->sigdigits)) { |
143 | if (!use) |
144 | use = rt; |
145 | else if (rt->sigdigits > use->sigdigits) |
146 | use = rt; |
147 | } |
148 | } |
149 | |
150 | if (use) |
151 | x25_route_hold(rt: use); |
152 | |
153 | read_unlock_bh(&x25_route_list_lock); |
154 | return use; |
155 | } |
156 | |
157 | /* |
158 | * Handle the ioctls that control the routing functions. |
159 | */ |
160 | int x25_route_ioctl(unsigned int cmd, void __user *arg) |
161 | { |
162 | struct x25_route_struct rt; |
163 | struct net_device *dev; |
164 | int rc = -EINVAL; |
165 | |
166 | if (cmd != SIOCADDRT && cmd != SIOCDELRT) |
167 | goto out; |
168 | |
169 | rc = -EFAULT; |
170 | if (copy_from_user(to: &rt, from: arg, n: sizeof(rt))) |
171 | goto out; |
172 | |
173 | rc = -EINVAL; |
174 | if (rt.sigdigits > 15) |
175 | goto out; |
176 | |
177 | dev = x25_dev_get(devname: rt.device); |
178 | if (!dev) |
179 | goto out; |
180 | |
181 | if (cmd == SIOCADDRT) |
182 | rc = x25_add_route(address: &rt.address, sigdigits: rt.sigdigits, dev); |
183 | else |
184 | rc = x25_del_route(address: &rt.address, sigdigits: rt.sigdigits, dev); |
185 | dev_put(dev); |
186 | out: |
187 | return rc; |
188 | } |
189 | |
190 | /* |
191 | * Release all memory associated with X.25 routing structures. |
192 | */ |
193 | void __exit x25_route_free(void) |
194 | { |
195 | struct x25_route *rt; |
196 | struct list_head *entry, *tmp; |
197 | |
198 | write_lock_bh(&x25_route_list_lock); |
199 | list_for_each_safe(entry, tmp, &x25_route_list) { |
200 | rt = list_entry(entry, struct x25_route, node); |
201 | __x25_remove_route(rt); |
202 | } |
203 | write_unlock_bh(&x25_route_list_lock); |
204 | } |
205 | |