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