18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) 58c2ecf20Sopenharmony_ci * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) 68c2ecf20Sopenharmony_ci * Copyright (C) Steven Whitehouse GW7RRM (stevew@acm.org) 78c2ecf20Sopenharmony_ci * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) 88c2ecf20Sopenharmony_ci * Copyright (C) Hans-Joachim Hetscher DD8NE (dd8ne@bnv-bamberg.de) 98c2ecf20Sopenharmony_ci * Copyright (C) Frederic Rible F1OAT (frible@teaser.fr) 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/capability.h> 138c2ecf20Sopenharmony_ci#include <linux/errno.h> 148c2ecf20Sopenharmony_ci#include <linux/types.h> 158c2ecf20Sopenharmony_ci#include <linux/socket.h> 168c2ecf20Sopenharmony_ci#include <linux/timer.h> 178c2ecf20Sopenharmony_ci#include <linux/in.h> 188c2ecf20Sopenharmony_ci#include <linux/kernel.h> 198c2ecf20Sopenharmony_ci#include <linux/sched.h> 208c2ecf20Sopenharmony_ci#include <linux/string.h> 218c2ecf20Sopenharmony_ci#include <linux/sockios.h> 228c2ecf20Sopenharmony_ci#include <linux/net.h> 238c2ecf20Sopenharmony_ci#include <linux/slab.h> 248c2ecf20Sopenharmony_ci#include <net/ax25.h> 258c2ecf20Sopenharmony_ci#include <linux/inet.h> 268c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 278c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 288c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 298c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 308c2ecf20Sopenharmony_ci#include <net/sock.h> 318c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 328c2ecf20Sopenharmony_ci#include <linux/fcntl.h> 338c2ecf20Sopenharmony_ci#include <linux/mm.h> 348c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 358c2ecf20Sopenharmony_ci#include <linux/init.h> 368c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 378c2ecf20Sopenharmony_ci#include <linux/export.h> 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic ax25_route *ax25_route_list; 408c2ecf20Sopenharmony_ciDEFINE_RWLOCK(ax25_route_lock); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_civoid ax25_rt_device_down(struct net_device *dev) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci ax25_route *s, *t, *ax25_rt; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci write_lock_bh(&ax25_route_lock); 478c2ecf20Sopenharmony_ci ax25_rt = ax25_route_list; 488c2ecf20Sopenharmony_ci while (ax25_rt != NULL) { 498c2ecf20Sopenharmony_ci s = ax25_rt; 508c2ecf20Sopenharmony_ci ax25_rt = ax25_rt->next; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (s->dev == dev) { 538c2ecf20Sopenharmony_ci if (ax25_route_list == s) { 548c2ecf20Sopenharmony_ci ax25_route_list = s->next; 558c2ecf20Sopenharmony_ci kfree(s->digipeat); 568c2ecf20Sopenharmony_ci kfree(s); 578c2ecf20Sopenharmony_ci } else { 588c2ecf20Sopenharmony_ci for (t = ax25_route_list; t != NULL; t = t->next) { 598c2ecf20Sopenharmony_ci if (t->next == s) { 608c2ecf20Sopenharmony_ci t->next = s->next; 618c2ecf20Sopenharmony_ci kfree(s->digipeat); 628c2ecf20Sopenharmony_ci kfree(s); 638c2ecf20Sopenharmony_ci break; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci write_unlock_bh(&ax25_route_lock); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic int __must_check ax25_rt_add(struct ax25_routes_struct *route) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci ax25_route *ax25_rt; 758c2ecf20Sopenharmony_ci ax25_dev *ax25_dev; 768c2ecf20Sopenharmony_ci int i; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci if (route->digi_count > AX25_MAX_DIGIS) 798c2ecf20Sopenharmony_ci return -EINVAL; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci ax25_dev = ax25_addr_ax25dev(&route->port_addr); 828c2ecf20Sopenharmony_ci if (!ax25_dev) 838c2ecf20Sopenharmony_ci return -EINVAL; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci write_lock_bh(&ax25_route_lock); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci ax25_rt = ax25_route_list; 888c2ecf20Sopenharmony_ci while (ax25_rt != NULL) { 898c2ecf20Sopenharmony_ci if (ax25cmp(&ax25_rt->callsign, &route->dest_addr) == 0 && 908c2ecf20Sopenharmony_ci ax25_rt->dev == ax25_dev->dev) { 918c2ecf20Sopenharmony_ci kfree(ax25_rt->digipeat); 928c2ecf20Sopenharmony_ci ax25_rt->digipeat = NULL; 938c2ecf20Sopenharmony_ci if (route->digi_count != 0) { 948c2ecf20Sopenharmony_ci if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) { 958c2ecf20Sopenharmony_ci write_unlock_bh(&ax25_route_lock); 968c2ecf20Sopenharmony_ci ax25_dev_put(ax25_dev); 978c2ecf20Sopenharmony_ci return -ENOMEM; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci ax25_rt->digipeat->lastrepeat = -1; 1008c2ecf20Sopenharmony_ci ax25_rt->digipeat->ndigi = route->digi_count; 1018c2ecf20Sopenharmony_ci for (i = 0; i < route->digi_count; i++) { 1028c2ecf20Sopenharmony_ci ax25_rt->digipeat->repeated[i] = 0; 1038c2ecf20Sopenharmony_ci ax25_rt->digipeat->calls[i] = route->digi_addr[i]; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci write_unlock_bh(&ax25_route_lock); 1078c2ecf20Sopenharmony_ci ax25_dev_put(ax25_dev); 1088c2ecf20Sopenharmony_ci return 0; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci ax25_rt = ax25_rt->next; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if ((ax25_rt = kmalloc(sizeof(ax25_route), GFP_ATOMIC)) == NULL) { 1148c2ecf20Sopenharmony_ci write_unlock_bh(&ax25_route_lock); 1158c2ecf20Sopenharmony_ci ax25_dev_put(ax25_dev); 1168c2ecf20Sopenharmony_ci return -ENOMEM; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci refcount_set(&ax25_rt->refcount, 1); 1208c2ecf20Sopenharmony_ci ax25_rt->callsign = route->dest_addr; 1218c2ecf20Sopenharmony_ci ax25_rt->dev = ax25_dev->dev; 1228c2ecf20Sopenharmony_ci ax25_rt->digipeat = NULL; 1238c2ecf20Sopenharmony_ci ax25_rt->ip_mode = ' '; 1248c2ecf20Sopenharmony_ci if (route->digi_count != 0) { 1258c2ecf20Sopenharmony_ci if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) { 1268c2ecf20Sopenharmony_ci write_unlock_bh(&ax25_route_lock); 1278c2ecf20Sopenharmony_ci kfree(ax25_rt); 1288c2ecf20Sopenharmony_ci ax25_dev_put(ax25_dev); 1298c2ecf20Sopenharmony_ci return -ENOMEM; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci ax25_rt->digipeat->lastrepeat = -1; 1328c2ecf20Sopenharmony_ci ax25_rt->digipeat->ndigi = route->digi_count; 1338c2ecf20Sopenharmony_ci for (i = 0; i < route->digi_count; i++) { 1348c2ecf20Sopenharmony_ci ax25_rt->digipeat->repeated[i] = 0; 1358c2ecf20Sopenharmony_ci ax25_rt->digipeat->calls[i] = route->digi_addr[i]; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci ax25_rt->next = ax25_route_list; 1398c2ecf20Sopenharmony_ci ax25_route_list = ax25_rt; 1408c2ecf20Sopenharmony_ci write_unlock_bh(&ax25_route_lock); 1418c2ecf20Sopenharmony_ci ax25_dev_put(ax25_dev); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci return 0; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_civoid __ax25_put_route(ax25_route *ax25_rt) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci kfree(ax25_rt->digipeat); 1498c2ecf20Sopenharmony_ci kfree(ax25_rt); 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic int ax25_rt_del(struct ax25_routes_struct *route) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci ax25_route *s, *t, *ax25_rt; 1558c2ecf20Sopenharmony_ci ax25_dev *ax25_dev; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if ((ax25_dev = ax25_addr_ax25dev(&route->port_addr)) == NULL) 1588c2ecf20Sopenharmony_ci return -EINVAL; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci write_lock_bh(&ax25_route_lock); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci ax25_rt = ax25_route_list; 1638c2ecf20Sopenharmony_ci while (ax25_rt != NULL) { 1648c2ecf20Sopenharmony_ci s = ax25_rt; 1658c2ecf20Sopenharmony_ci ax25_rt = ax25_rt->next; 1668c2ecf20Sopenharmony_ci if (s->dev == ax25_dev->dev && 1678c2ecf20Sopenharmony_ci ax25cmp(&route->dest_addr, &s->callsign) == 0) { 1688c2ecf20Sopenharmony_ci if (ax25_route_list == s) { 1698c2ecf20Sopenharmony_ci ax25_route_list = s->next; 1708c2ecf20Sopenharmony_ci ax25_put_route(s); 1718c2ecf20Sopenharmony_ci } else { 1728c2ecf20Sopenharmony_ci for (t = ax25_route_list; t != NULL; t = t->next) { 1738c2ecf20Sopenharmony_ci if (t->next == s) { 1748c2ecf20Sopenharmony_ci t->next = s->next; 1758c2ecf20Sopenharmony_ci ax25_put_route(s); 1768c2ecf20Sopenharmony_ci break; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci write_unlock_bh(&ax25_route_lock); 1838c2ecf20Sopenharmony_ci ax25_dev_put(ax25_dev); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci return 0; 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic int ax25_rt_opt(struct ax25_route_opt_struct *rt_option) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci ax25_route *ax25_rt; 1918c2ecf20Sopenharmony_ci ax25_dev *ax25_dev; 1928c2ecf20Sopenharmony_ci int err = 0; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if ((ax25_dev = ax25_addr_ax25dev(&rt_option->port_addr)) == NULL) 1958c2ecf20Sopenharmony_ci return -EINVAL; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci write_lock_bh(&ax25_route_lock); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci ax25_rt = ax25_route_list; 2008c2ecf20Sopenharmony_ci while (ax25_rt != NULL) { 2018c2ecf20Sopenharmony_ci if (ax25_rt->dev == ax25_dev->dev && 2028c2ecf20Sopenharmony_ci ax25cmp(&rt_option->dest_addr, &ax25_rt->callsign) == 0) { 2038c2ecf20Sopenharmony_ci switch (rt_option->cmd) { 2048c2ecf20Sopenharmony_ci case AX25_SET_RT_IPMODE: 2058c2ecf20Sopenharmony_ci switch (rt_option->arg) { 2068c2ecf20Sopenharmony_ci case ' ': 2078c2ecf20Sopenharmony_ci case 'D': 2088c2ecf20Sopenharmony_ci case 'V': 2098c2ecf20Sopenharmony_ci ax25_rt->ip_mode = rt_option->arg; 2108c2ecf20Sopenharmony_ci break; 2118c2ecf20Sopenharmony_ci default: 2128c2ecf20Sopenharmony_ci err = -EINVAL; 2138c2ecf20Sopenharmony_ci goto out; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci break; 2168c2ecf20Sopenharmony_ci default: 2178c2ecf20Sopenharmony_ci err = -EINVAL; 2188c2ecf20Sopenharmony_ci goto out; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci ax25_rt = ax25_rt->next; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ciout: 2258c2ecf20Sopenharmony_ci write_unlock_bh(&ax25_route_lock); 2268c2ecf20Sopenharmony_ci ax25_dev_put(ax25_dev); 2278c2ecf20Sopenharmony_ci return err; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ciint ax25_rt_ioctl(unsigned int cmd, void __user *arg) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci struct ax25_route_opt_struct rt_option; 2338c2ecf20Sopenharmony_ci struct ax25_routes_struct route; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci switch (cmd) { 2368c2ecf20Sopenharmony_ci case SIOCADDRT: 2378c2ecf20Sopenharmony_ci if (copy_from_user(&route, arg, sizeof(route))) 2388c2ecf20Sopenharmony_ci return -EFAULT; 2398c2ecf20Sopenharmony_ci return ax25_rt_add(&route); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci case SIOCDELRT: 2428c2ecf20Sopenharmony_ci if (copy_from_user(&route, arg, sizeof(route))) 2438c2ecf20Sopenharmony_ci return -EFAULT; 2448c2ecf20Sopenharmony_ci return ax25_rt_del(&route); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci case SIOCAX25OPTRT: 2478c2ecf20Sopenharmony_ci if (copy_from_user(&rt_option, arg, sizeof(rt_option))) 2488c2ecf20Sopenharmony_ci return -EFAULT; 2498c2ecf20Sopenharmony_ci return ax25_rt_opt(&rt_option); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci default: 2528c2ecf20Sopenharmony_ci return -EINVAL; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic void *ax25_rt_seq_start(struct seq_file *seq, loff_t *pos) 2598c2ecf20Sopenharmony_ci __acquires(ax25_route_lock) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct ax25_route *ax25_rt; 2628c2ecf20Sopenharmony_ci int i = 1; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci read_lock(&ax25_route_lock); 2658c2ecf20Sopenharmony_ci if (*pos == 0) 2668c2ecf20Sopenharmony_ci return SEQ_START_TOKEN; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) { 2698c2ecf20Sopenharmony_ci if (i == *pos) 2708c2ecf20Sopenharmony_ci return ax25_rt; 2718c2ecf20Sopenharmony_ci ++i; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci return NULL; 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic void *ax25_rt_seq_next(struct seq_file *seq, void *v, loff_t *pos) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci ++*pos; 2808c2ecf20Sopenharmony_ci return (v == SEQ_START_TOKEN) ? ax25_route_list : 2818c2ecf20Sopenharmony_ci ((struct ax25_route *) v)->next; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic void ax25_rt_seq_stop(struct seq_file *seq, void *v) 2858c2ecf20Sopenharmony_ci __releases(ax25_route_lock) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci read_unlock(&ax25_route_lock); 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic int ax25_rt_seq_show(struct seq_file *seq, void *v) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci char buf[11]; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci if (v == SEQ_START_TOKEN) 2958c2ecf20Sopenharmony_ci seq_puts(seq, "callsign dev mode digipeaters\n"); 2968c2ecf20Sopenharmony_ci else { 2978c2ecf20Sopenharmony_ci struct ax25_route *ax25_rt = v; 2988c2ecf20Sopenharmony_ci const char *callsign; 2998c2ecf20Sopenharmony_ci int i; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0) 3028c2ecf20Sopenharmony_ci callsign = "default"; 3038c2ecf20Sopenharmony_ci else 3048c2ecf20Sopenharmony_ci callsign = ax2asc(buf, &ax25_rt->callsign); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci seq_printf(seq, "%-9s %-4s", 3078c2ecf20Sopenharmony_ci callsign, 3088c2ecf20Sopenharmony_ci ax25_rt->dev ? ax25_rt->dev->name : "???"); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci switch (ax25_rt->ip_mode) { 3118c2ecf20Sopenharmony_ci case 'V': 3128c2ecf20Sopenharmony_ci seq_puts(seq, " vc"); 3138c2ecf20Sopenharmony_ci break; 3148c2ecf20Sopenharmony_ci case 'D': 3158c2ecf20Sopenharmony_ci seq_puts(seq, " dg"); 3168c2ecf20Sopenharmony_ci break; 3178c2ecf20Sopenharmony_ci default: 3188c2ecf20Sopenharmony_ci seq_puts(seq, " *"); 3198c2ecf20Sopenharmony_ci break; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (ax25_rt->digipeat != NULL) 3238c2ecf20Sopenharmony_ci for (i = 0; i < ax25_rt->digipeat->ndigi; i++) 3248c2ecf20Sopenharmony_ci seq_printf(seq, " %s", 3258c2ecf20Sopenharmony_ci ax2asc(buf, &ax25_rt->digipeat->calls[i])); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci seq_puts(seq, "\n"); 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci return 0; 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ciconst struct seq_operations ax25_rt_seqops = { 3338c2ecf20Sopenharmony_ci .start = ax25_rt_seq_start, 3348c2ecf20Sopenharmony_ci .next = ax25_rt_seq_next, 3358c2ecf20Sopenharmony_ci .stop = ax25_rt_seq_stop, 3368c2ecf20Sopenharmony_ci .show = ax25_rt_seq_show, 3378c2ecf20Sopenharmony_ci}; 3388c2ecf20Sopenharmony_ci#endif 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci/* 3418c2ecf20Sopenharmony_ci * Find AX.25 route 3428c2ecf20Sopenharmony_ci * 3438c2ecf20Sopenharmony_ci * Only routes with a reference count of zero can be destroyed. 3448c2ecf20Sopenharmony_ci * Must be called with ax25_route_lock read locked. 3458c2ecf20Sopenharmony_ci */ 3468c2ecf20Sopenharmony_ciax25_route *ax25_get_route(ax25_address *addr, struct net_device *dev) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci ax25_route *ax25_spe_rt = NULL; 3498c2ecf20Sopenharmony_ci ax25_route *ax25_def_rt = NULL; 3508c2ecf20Sopenharmony_ci ax25_route *ax25_rt; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci /* 3538c2ecf20Sopenharmony_ci * Bind to the physical interface we heard them on, or the default 3548c2ecf20Sopenharmony_ci * route if none is found; 3558c2ecf20Sopenharmony_ci */ 3568c2ecf20Sopenharmony_ci for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) { 3578c2ecf20Sopenharmony_ci if (dev == NULL) { 3588c2ecf20Sopenharmony_ci if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev != NULL) 3598c2ecf20Sopenharmony_ci ax25_spe_rt = ax25_rt; 3608c2ecf20Sopenharmony_ci if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev != NULL) 3618c2ecf20Sopenharmony_ci ax25_def_rt = ax25_rt; 3628c2ecf20Sopenharmony_ci } else { 3638c2ecf20Sopenharmony_ci if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev == dev) 3648c2ecf20Sopenharmony_ci ax25_spe_rt = ax25_rt; 3658c2ecf20Sopenharmony_ci if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev == dev) 3668c2ecf20Sopenharmony_ci ax25_def_rt = ax25_rt; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci ax25_rt = ax25_def_rt; 3718c2ecf20Sopenharmony_ci if (ax25_spe_rt != NULL) 3728c2ecf20Sopenharmony_ci ax25_rt = ax25_spe_rt; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci return ax25_rt; 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci/* 3788c2ecf20Sopenharmony_ci * Adjust path: If you specify a default route and want to connect 3798c2ecf20Sopenharmony_ci * a target on the digipeater path but w/o having a special route 3808c2ecf20Sopenharmony_ci * set before, the path has to be truncated from your target on. 3818c2ecf20Sopenharmony_ci */ 3828c2ecf20Sopenharmony_cistatic inline void ax25_adjust_path(ax25_address *addr, ax25_digi *digipeat) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci int k; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci for (k = 0; k < digipeat->ndigi; k++) { 3878c2ecf20Sopenharmony_ci if (ax25cmp(addr, &digipeat->calls[k]) == 0) 3888c2ecf20Sopenharmony_ci break; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci digipeat->ndigi = k; 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci/* 3968c2ecf20Sopenharmony_ci * Find which interface to use. 3978c2ecf20Sopenharmony_ci */ 3988c2ecf20Sopenharmony_ciint ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci ax25_uid_assoc *user; 4018c2ecf20Sopenharmony_ci ax25_route *ax25_rt; 4028c2ecf20Sopenharmony_ci int err = 0; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci ax25_route_lock_use(); 4058c2ecf20Sopenharmony_ci ax25_rt = ax25_get_route(addr, NULL); 4068c2ecf20Sopenharmony_ci if (!ax25_rt) { 4078c2ecf20Sopenharmony_ci ax25_route_lock_unuse(); 4088c2ecf20Sopenharmony_ci return -EHOSTUNREACH; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci if ((ax25->ax25_dev = ax25_dev_ax25dev(ax25_rt->dev)) == NULL) { 4118c2ecf20Sopenharmony_ci err = -EHOSTUNREACH; 4128c2ecf20Sopenharmony_ci goto put; 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci user = ax25_findbyuid(current_euid()); 4168c2ecf20Sopenharmony_ci if (user) { 4178c2ecf20Sopenharmony_ci ax25->source_addr = user->call; 4188c2ecf20Sopenharmony_ci ax25_uid_put(user); 4198c2ecf20Sopenharmony_ci } else { 4208c2ecf20Sopenharmony_ci if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE)) { 4218c2ecf20Sopenharmony_ci err = -EPERM; 4228c2ecf20Sopenharmony_ci goto put; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci ax25->source_addr = *(ax25_address *)ax25->ax25_dev->dev->dev_addr; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci if (ax25_rt->digipeat != NULL) { 4288c2ecf20Sopenharmony_ci ax25->digipeat = kmemdup(ax25_rt->digipeat, sizeof(ax25_digi), 4298c2ecf20Sopenharmony_ci GFP_ATOMIC); 4308c2ecf20Sopenharmony_ci if (ax25->digipeat == NULL) { 4318c2ecf20Sopenharmony_ci err = -ENOMEM; 4328c2ecf20Sopenharmony_ci goto put; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci ax25_adjust_path(addr, ax25->digipeat); 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (ax25->sk != NULL) { 4388c2ecf20Sopenharmony_ci local_bh_disable(); 4398c2ecf20Sopenharmony_ci bh_lock_sock(ax25->sk); 4408c2ecf20Sopenharmony_ci sock_reset_flag(ax25->sk, SOCK_ZAPPED); 4418c2ecf20Sopenharmony_ci bh_unlock_sock(ax25->sk); 4428c2ecf20Sopenharmony_ci local_bh_enable(); 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ciput: 4468c2ecf20Sopenharmony_ci ax25_route_lock_unuse(); 4478c2ecf20Sopenharmony_ci return err; 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistruct sk_buff *ax25_rt_build_path(struct sk_buff *skb, ax25_address *src, 4518c2ecf20Sopenharmony_ci ax25_address *dest, ax25_digi *digi) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci struct sk_buff *skbn; 4548c2ecf20Sopenharmony_ci unsigned char *bp; 4558c2ecf20Sopenharmony_ci int len; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci len = digi->ndigi * AX25_ADDR_LEN; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci if (skb_headroom(skb) < len) { 4608c2ecf20Sopenharmony_ci if ((skbn = skb_realloc_headroom(skb, len)) == NULL) { 4618c2ecf20Sopenharmony_ci printk(KERN_CRIT "AX.25: ax25_dg_build_path - out of memory\n"); 4628c2ecf20Sopenharmony_ci return NULL; 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci if (skb->sk != NULL) 4668c2ecf20Sopenharmony_ci skb_set_owner_w(skbn, skb->sk); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci consume_skb(skb); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci skb = skbn; 4718c2ecf20Sopenharmony_ci } 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci bp = skb_push(skb, len); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci ax25_addr_build(bp, src, dest, digi, AX25_COMMAND, AX25_MODULUS); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci return skb; 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci/* 4818c2ecf20Sopenharmony_ci * Free all memory associated with routing structures. 4828c2ecf20Sopenharmony_ci */ 4838c2ecf20Sopenharmony_civoid __exit ax25_rt_free(void) 4848c2ecf20Sopenharmony_ci{ 4858c2ecf20Sopenharmony_ci ax25_route *s, *ax25_rt = ax25_route_list; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci write_lock_bh(&ax25_route_lock); 4888c2ecf20Sopenharmony_ci while (ax25_rt != NULL) { 4898c2ecf20Sopenharmony_ci s = ax25_rt; 4908c2ecf20Sopenharmony_ci ax25_rt = ax25_rt->next; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci kfree(s->digipeat); 4938c2ecf20Sopenharmony_ci kfree(s); 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci write_unlock_bh(&ax25_route_lock); 4968c2ecf20Sopenharmony_ci} 497