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