18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *	X.25 Packet Layer release 002
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *	This is ALPHA test software. This code may break your machine,
68c2ecf20Sopenharmony_ci *	randomly fail to work with new releases, misbehave and/or generally
78c2ecf20Sopenharmony_ci *	screw up. It might even work.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci *	This code REQUIRES 2.1.15 or higher
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci *	History
128c2ecf20Sopenharmony_ci *	X.25 001	Jonathan Naylor	Started coding.
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
168c2ecf20Sopenharmony_ci#include <linux/init.h>
178c2ecf20Sopenharmony_ci#include <linux/slab.h>
188c2ecf20Sopenharmony_ci#include <net/x25.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ciLIST_HEAD(x25_route_list);
218c2ecf20Sopenharmony_ciDEFINE_RWLOCK(x25_route_list_lock);
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/*
248c2ecf20Sopenharmony_ci *	Add a new route.
258c2ecf20Sopenharmony_ci */
268c2ecf20Sopenharmony_cistatic int x25_add_route(struct x25_address *address, unsigned int sigdigits,
278c2ecf20Sopenharmony_ci			 struct net_device *dev)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	struct x25_route *rt;
308c2ecf20Sopenharmony_ci	struct list_head *entry;
318c2ecf20Sopenharmony_ci	int rc = -EINVAL;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	write_lock_bh(&x25_route_list_lock);
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	list_for_each(entry, &x25_route_list) {
368c2ecf20Sopenharmony_ci		rt = list_entry(entry, struct x25_route, node);
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci		if (!memcmp(&rt->address, address, sigdigits) &&
398c2ecf20Sopenharmony_ci		    rt->sigdigits == sigdigits)
408c2ecf20Sopenharmony_ci			goto out;
418c2ecf20Sopenharmony_ci	}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	rt = kmalloc(sizeof(*rt), GFP_ATOMIC);
448c2ecf20Sopenharmony_ci	rc = -ENOMEM;
458c2ecf20Sopenharmony_ci	if (!rt)
468c2ecf20Sopenharmony_ci		goto out;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	strcpy(rt->address.x25_addr, "000000000000000");
498c2ecf20Sopenharmony_ci	memcpy(rt->address.x25_addr, address->x25_addr, sigdigits);
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	rt->sigdigits = sigdigits;
528c2ecf20Sopenharmony_ci	rt->dev       = dev;
538c2ecf20Sopenharmony_ci	refcount_set(&rt->refcnt, 1);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	list_add(&rt->node, &x25_route_list);
568c2ecf20Sopenharmony_ci	rc = 0;
578c2ecf20Sopenharmony_ciout:
588c2ecf20Sopenharmony_ci	write_unlock_bh(&x25_route_list_lock);
598c2ecf20Sopenharmony_ci	return rc;
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/**
638c2ecf20Sopenharmony_ci * __x25_remove_route - remove route from x25_route_list
648c2ecf20Sopenharmony_ci * @rt: route to remove
658c2ecf20Sopenharmony_ci *
668c2ecf20Sopenharmony_ci * Remove route from x25_route_list. If it was there.
678c2ecf20Sopenharmony_ci * Caller must hold x25_route_list_lock.
688c2ecf20Sopenharmony_ci */
698c2ecf20Sopenharmony_cistatic void __x25_remove_route(struct x25_route *rt)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	if (rt->node.next) {
728c2ecf20Sopenharmony_ci		list_del(&rt->node);
738c2ecf20Sopenharmony_ci		x25_route_put(rt);
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic int x25_del_route(struct x25_address *address, unsigned int sigdigits,
788c2ecf20Sopenharmony_ci			 struct net_device *dev)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	struct x25_route *rt;
818c2ecf20Sopenharmony_ci	struct list_head *entry;
828c2ecf20Sopenharmony_ci	int rc = -EINVAL;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	write_lock_bh(&x25_route_list_lock);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	list_for_each(entry, &x25_route_list) {
878c2ecf20Sopenharmony_ci		rt = list_entry(entry, struct x25_route, node);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci		if (!memcmp(&rt->address, address, sigdigits) &&
908c2ecf20Sopenharmony_ci		    rt->sigdigits == sigdigits && rt->dev == dev) {
918c2ecf20Sopenharmony_ci			__x25_remove_route(rt);
928c2ecf20Sopenharmony_ci			rc = 0;
938c2ecf20Sopenharmony_ci			break;
948c2ecf20Sopenharmony_ci		}
958c2ecf20Sopenharmony_ci	}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	write_unlock_bh(&x25_route_list_lock);
988c2ecf20Sopenharmony_ci	return rc;
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci/*
1028c2ecf20Sopenharmony_ci *	A device has been removed, remove its routes.
1038c2ecf20Sopenharmony_ci */
1048c2ecf20Sopenharmony_civoid x25_route_device_down(struct net_device *dev)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	struct x25_route *rt;
1078c2ecf20Sopenharmony_ci	struct list_head *entry, *tmp;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	write_lock_bh(&x25_route_list_lock);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	list_for_each_safe(entry, tmp, &x25_route_list) {
1128c2ecf20Sopenharmony_ci		rt = list_entry(entry, struct x25_route, node);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci		if (rt->dev == dev)
1158c2ecf20Sopenharmony_ci			__x25_remove_route(rt);
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci	write_unlock_bh(&x25_route_list_lock);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	/* Remove any related forwarding */
1208c2ecf20Sopenharmony_ci	x25_clear_forward_by_dev(dev);
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci/*
1248c2ecf20Sopenharmony_ci *	Check that the device given is a valid X.25 interface that is "up".
1258c2ecf20Sopenharmony_ci */
1268c2ecf20Sopenharmony_cistruct net_device *x25_dev_get(char *devname)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	struct net_device *dev = dev_get_by_name(&init_net, devname);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	if (dev &&
1318c2ecf20Sopenharmony_ci	    (!(dev->flags & IFF_UP) || (dev->type != ARPHRD_X25
1328c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_LLC)
1338c2ecf20Sopenharmony_ci					&& dev->type != ARPHRD_ETHER
1348c2ecf20Sopenharmony_ci#endif
1358c2ecf20Sopenharmony_ci					))){
1368c2ecf20Sopenharmony_ci		dev_put(dev);
1378c2ecf20Sopenharmony_ci		dev = NULL;
1388c2ecf20Sopenharmony_ci	}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	return dev;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci/**
1448c2ecf20Sopenharmony_ci * 	x25_get_route -	Find a route given an X.25 address.
1458c2ecf20Sopenharmony_ci *	@addr: - address to find a route for
1468c2ecf20Sopenharmony_ci *
1478c2ecf20Sopenharmony_ci * 	Find a route given an X.25 address.
1488c2ecf20Sopenharmony_ci */
1498c2ecf20Sopenharmony_cistruct x25_route *x25_get_route(struct x25_address *addr)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	struct x25_route *rt, *use = NULL;
1528c2ecf20Sopenharmony_ci	struct list_head *entry;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	read_lock_bh(&x25_route_list_lock);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	list_for_each(entry, &x25_route_list) {
1578c2ecf20Sopenharmony_ci		rt = list_entry(entry, struct x25_route, node);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci		if (!memcmp(&rt->address, addr, rt->sigdigits)) {
1608c2ecf20Sopenharmony_ci			if (!use)
1618c2ecf20Sopenharmony_ci				use = rt;
1628c2ecf20Sopenharmony_ci			else if (rt->sigdigits > use->sigdigits)
1638c2ecf20Sopenharmony_ci				use = rt;
1648c2ecf20Sopenharmony_ci		}
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	if (use)
1688c2ecf20Sopenharmony_ci		x25_route_hold(use);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	read_unlock_bh(&x25_route_list_lock);
1718c2ecf20Sopenharmony_ci	return use;
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci/*
1758c2ecf20Sopenharmony_ci *	Handle the ioctls that control the routing functions.
1768c2ecf20Sopenharmony_ci */
1778c2ecf20Sopenharmony_ciint x25_route_ioctl(unsigned int cmd, void __user *arg)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	struct x25_route_struct rt;
1808c2ecf20Sopenharmony_ci	struct net_device *dev;
1818c2ecf20Sopenharmony_ci	int rc = -EINVAL;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	if (cmd != SIOCADDRT && cmd != SIOCDELRT)
1848c2ecf20Sopenharmony_ci		goto out;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	rc = -EFAULT;
1878c2ecf20Sopenharmony_ci	if (copy_from_user(&rt, arg, sizeof(rt)))
1888c2ecf20Sopenharmony_ci		goto out;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	rc = -EINVAL;
1918c2ecf20Sopenharmony_ci	if (rt.sigdigits > 15)
1928c2ecf20Sopenharmony_ci		goto out;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	dev = x25_dev_get(rt.device);
1958c2ecf20Sopenharmony_ci	if (!dev)
1968c2ecf20Sopenharmony_ci		goto out;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	if (cmd == SIOCADDRT)
1998c2ecf20Sopenharmony_ci		rc = x25_add_route(&rt.address, rt.sigdigits, dev);
2008c2ecf20Sopenharmony_ci	else
2018c2ecf20Sopenharmony_ci		rc = x25_del_route(&rt.address, rt.sigdigits, dev);
2028c2ecf20Sopenharmony_ci	dev_put(dev);
2038c2ecf20Sopenharmony_ciout:
2048c2ecf20Sopenharmony_ci	return rc;
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci/*
2088c2ecf20Sopenharmony_ci *	Release all memory associated with X.25 routing structures.
2098c2ecf20Sopenharmony_ci */
2108c2ecf20Sopenharmony_civoid __exit x25_route_free(void)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	struct x25_route *rt;
2138c2ecf20Sopenharmony_ci	struct list_head *entry, *tmp;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	write_lock_bh(&x25_route_list_lock);
2168c2ecf20Sopenharmony_ci	list_for_each_safe(entry, tmp, &x25_route_list) {
2178c2ecf20Sopenharmony_ci		rt = list_entry(entry, struct x25_route, node);
2188c2ecf20Sopenharmony_ci		__x25_remove_route(rt);
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci	write_unlock_bh(&x25_route_list_lock);
2218c2ecf20Sopenharmony_ci}
222