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