162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) 562306a36Sopenharmony_ci * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) 662306a36Sopenharmony_ci * Copyright (C) Steven Whitehouse GW7RRM (stevew@acm.org) 762306a36Sopenharmony_ci * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) 862306a36Sopenharmony_ci * Copyright (C) Hans-Joachim Hetscher DD8NE (dd8ne@bnv-bamberg.de) 962306a36Sopenharmony_ci * Copyright (C) Frederic Rible F1OAT (frible@teaser.fr) 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/capability.h> 1362306a36Sopenharmony_ci#include <linux/errno.h> 1462306a36Sopenharmony_ci#include <linux/types.h> 1562306a36Sopenharmony_ci#include <linux/socket.h> 1662306a36Sopenharmony_ci#include <linux/timer.h> 1762306a36Sopenharmony_ci#include <linux/in.h> 1862306a36Sopenharmony_ci#include <linux/kernel.h> 1962306a36Sopenharmony_ci#include <linux/sched.h> 2062306a36Sopenharmony_ci#include <linux/string.h> 2162306a36Sopenharmony_ci#include <linux/sockios.h> 2262306a36Sopenharmony_ci#include <linux/net.h> 2362306a36Sopenharmony_ci#include <linux/slab.h> 2462306a36Sopenharmony_ci#include <net/ax25.h> 2562306a36Sopenharmony_ci#include <linux/inet.h> 2662306a36Sopenharmony_ci#include <linux/netdevice.h> 2762306a36Sopenharmony_ci#include <linux/if_arp.h> 2862306a36Sopenharmony_ci#include <linux/skbuff.h> 2962306a36Sopenharmony_ci#include <linux/spinlock.h> 3062306a36Sopenharmony_ci#include <net/sock.h> 3162306a36Sopenharmony_ci#include <linux/uaccess.h> 3262306a36Sopenharmony_ci#include <linux/fcntl.h> 3362306a36Sopenharmony_ci#include <linux/mm.h> 3462306a36Sopenharmony_ci#include <linux/interrupt.h> 3562306a36Sopenharmony_ci#include <linux/init.h> 3662306a36Sopenharmony_ci#include <linux/seq_file.h> 3762306a36Sopenharmony_ci#include <linux/export.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic ax25_route *ax25_route_list; 4062306a36Sopenharmony_ciDEFINE_RWLOCK(ax25_route_lock); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_civoid ax25_rt_device_down(struct net_device *dev) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci ax25_route *s, *t, *ax25_rt; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci write_lock_bh(&ax25_route_lock); 4762306a36Sopenharmony_ci ax25_rt = ax25_route_list; 4862306a36Sopenharmony_ci while (ax25_rt != NULL) { 4962306a36Sopenharmony_ci s = ax25_rt; 5062306a36Sopenharmony_ci ax25_rt = ax25_rt->next; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci if (s->dev == dev) { 5362306a36Sopenharmony_ci if (ax25_route_list == s) { 5462306a36Sopenharmony_ci ax25_route_list = s->next; 5562306a36Sopenharmony_ci kfree(s->digipeat); 5662306a36Sopenharmony_ci kfree(s); 5762306a36Sopenharmony_ci } else { 5862306a36Sopenharmony_ci for (t = ax25_route_list; t != NULL; t = t->next) { 5962306a36Sopenharmony_ci if (t->next == s) { 6062306a36Sopenharmony_ci t->next = s->next; 6162306a36Sopenharmony_ci kfree(s->digipeat); 6262306a36Sopenharmony_ci kfree(s); 6362306a36Sopenharmony_ci break; 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci write_unlock_bh(&ax25_route_lock); 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic int __must_check ax25_rt_add(struct ax25_routes_struct *route) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci ax25_route *ax25_rt; 7562306a36Sopenharmony_ci ax25_dev *ax25_dev; 7662306a36Sopenharmony_ci int i; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (route->digi_count > AX25_MAX_DIGIS) 7962306a36Sopenharmony_ci return -EINVAL; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci ax25_dev = ax25_addr_ax25dev(&route->port_addr); 8262306a36Sopenharmony_ci if (!ax25_dev) 8362306a36Sopenharmony_ci return -EINVAL; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci write_lock_bh(&ax25_route_lock); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci ax25_rt = ax25_route_list; 8862306a36Sopenharmony_ci while (ax25_rt != NULL) { 8962306a36Sopenharmony_ci if (ax25cmp(&ax25_rt->callsign, &route->dest_addr) == 0 && 9062306a36Sopenharmony_ci ax25_rt->dev == ax25_dev->dev) { 9162306a36Sopenharmony_ci kfree(ax25_rt->digipeat); 9262306a36Sopenharmony_ci ax25_rt->digipeat = NULL; 9362306a36Sopenharmony_ci if (route->digi_count != 0) { 9462306a36Sopenharmony_ci if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) { 9562306a36Sopenharmony_ci write_unlock_bh(&ax25_route_lock); 9662306a36Sopenharmony_ci ax25_dev_put(ax25_dev); 9762306a36Sopenharmony_ci return -ENOMEM; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci ax25_rt->digipeat->lastrepeat = -1; 10062306a36Sopenharmony_ci ax25_rt->digipeat->ndigi = route->digi_count; 10162306a36Sopenharmony_ci for (i = 0; i < route->digi_count; i++) { 10262306a36Sopenharmony_ci ax25_rt->digipeat->repeated[i] = 0; 10362306a36Sopenharmony_ci ax25_rt->digipeat->calls[i] = route->digi_addr[i]; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci write_unlock_bh(&ax25_route_lock); 10762306a36Sopenharmony_ci ax25_dev_put(ax25_dev); 10862306a36Sopenharmony_ci return 0; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci ax25_rt = ax25_rt->next; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if ((ax25_rt = kmalloc(sizeof(ax25_route), GFP_ATOMIC)) == NULL) { 11462306a36Sopenharmony_ci write_unlock_bh(&ax25_route_lock); 11562306a36Sopenharmony_ci ax25_dev_put(ax25_dev); 11662306a36Sopenharmony_ci return -ENOMEM; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci ax25_rt->callsign = route->dest_addr; 12062306a36Sopenharmony_ci ax25_rt->dev = ax25_dev->dev; 12162306a36Sopenharmony_ci ax25_rt->digipeat = NULL; 12262306a36Sopenharmony_ci ax25_rt->ip_mode = ' '; 12362306a36Sopenharmony_ci if (route->digi_count != 0) { 12462306a36Sopenharmony_ci if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) { 12562306a36Sopenharmony_ci write_unlock_bh(&ax25_route_lock); 12662306a36Sopenharmony_ci kfree(ax25_rt); 12762306a36Sopenharmony_ci ax25_dev_put(ax25_dev); 12862306a36Sopenharmony_ci return -ENOMEM; 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci ax25_rt->digipeat->lastrepeat = -1; 13162306a36Sopenharmony_ci ax25_rt->digipeat->ndigi = route->digi_count; 13262306a36Sopenharmony_ci for (i = 0; i < route->digi_count; i++) { 13362306a36Sopenharmony_ci ax25_rt->digipeat->repeated[i] = 0; 13462306a36Sopenharmony_ci ax25_rt->digipeat->calls[i] = route->digi_addr[i]; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci ax25_rt->next = ax25_route_list; 13862306a36Sopenharmony_ci ax25_route_list = ax25_rt; 13962306a36Sopenharmony_ci write_unlock_bh(&ax25_route_lock); 14062306a36Sopenharmony_ci ax25_dev_put(ax25_dev); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci return 0; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_civoid __ax25_put_route(ax25_route *ax25_rt) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci kfree(ax25_rt->digipeat); 14862306a36Sopenharmony_ci kfree(ax25_rt); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic int ax25_rt_del(struct ax25_routes_struct *route) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci ax25_route *s, *t, *ax25_rt; 15462306a36Sopenharmony_ci ax25_dev *ax25_dev; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if ((ax25_dev = ax25_addr_ax25dev(&route->port_addr)) == NULL) 15762306a36Sopenharmony_ci return -EINVAL; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci write_lock_bh(&ax25_route_lock); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci ax25_rt = ax25_route_list; 16262306a36Sopenharmony_ci while (ax25_rt != NULL) { 16362306a36Sopenharmony_ci s = ax25_rt; 16462306a36Sopenharmony_ci ax25_rt = ax25_rt->next; 16562306a36Sopenharmony_ci if (s->dev == ax25_dev->dev && 16662306a36Sopenharmony_ci ax25cmp(&route->dest_addr, &s->callsign) == 0) { 16762306a36Sopenharmony_ci if (ax25_route_list == s) { 16862306a36Sopenharmony_ci ax25_route_list = s->next; 16962306a36Sopenharmony_ci __ax25_put_route(s); 17062306a36Sopenharmony_ci } else { 17162306a36Sopenharmony_ci for (t = ax25_route_list; t != NULL; t = t->next) { 17262306a36Sopenharmony_ci if (t->next == s) { 17362306a36Sopenharmony_ci t->next = s->next; 17462306a36Sopenharmony_ci __ax25_put_route(s); 17562306a36Sopenharmony_ci break; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci write_unlock_bh(&ax25_route_lock); 18262306a36Sopenharmony_ci ax25_dev_put(ax25_dev); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci return 0; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic int ax25_rt_opt(struct ax25_route_opt_struct *rt_option) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci ax25_route *ax25_rt; 19062306a36Sopenharmony_ci ax25_dev *ax25_dev; 19162306a36Sopenharmony_ci int err = 0; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if ((ax25_dev = ax25_addr_ax25dev(&rt_option->port_addr)) == NULL) 19462306a36Sopenharmony_ci return -EINVAL; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci write_lock_bh(&ax25_route_lock); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci ax25_rt = ax25_route_list; 19962306a36Sopenharmony_ci while (ax25_rt != NULL) { 20062306a36Sopenharmony_ci if (ax25_rt->dev == ax25_dev->dev && 20162306a36Sopenharmony_ci ax25cmp(&rt_option->dest_addr, &ax25_rt->callsign) == 0) { 20262306a36Sopenharmony_ci switch (rt_option->cmd) { 20362306a36Sopenharmony_ci case AX25_SET_RT_IPMODE: 20462306a36Sopenharmony_ci switch (rt_option->arg) { 20562306a36Sopenharmony_ci case ' ': 20662306a36Sopenharmony_ci case 'D': 20762306a36Sopenharmony_ci case 'V': 20862306a36Sopenharmony_ci ax25_rt->ip_mode = rt_option->arg; 20962306a36Sopenharmony_ci break; 21062306a36Sopenharmony_ci default: 21162306a36Sopenharmony_ci err = -EINVAL; 21262306a36Sopenharmony_ci goto out; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci break; 21562306a36Sopenharmony_ci default: 21662306a36Sopenharmony_ci err = -EINVAL; 21762306a36Sopenharmony_ci goto out; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci ax25_rt = ax25_rt->next; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ciout: 22462306a36Sopenharmony_ci write_unlock_bh(&ax25_route_lock); 22562306a36Sopenharmony_ci ax25_dev_put(ax25_dev); 22662306a36Sopenharmony_ci return err; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ciint ax25_rt_ioctl(unsigned int cmd, void __user *arg) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct ax25_route_opt_struct rt_option; 23262306a36Sopenharmony_ci struct ax25_routes_struct route; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci switch (cmd) { 23562306a36Sopenharmony_ci case SIOCADDRT: 23662306a36Sopenharmony_ci if (copy_from_user(&route, arg, sizeof(route))) 23762306a36Sopenharmony_ci return -EFAULT; 23862306a36Sopenharmony_ci return ax25_rt_add(&route); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci case SIOCDELRT: 24162306a36Sopenharmony_ci if (copy_from_user(&route, arg, sizeof(route))) 24262306a36Sopenharmony_ci return -EFAULT; 24362306a36Sopenharmony_ci return ax25_rt_del(&route); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci case SIOCAX25OPTRT: 24662306a36Sopenharmony_ci if (copy_from_user(&rt_option, arg, sizeof(rt_option))) 24762306a36Sopenharmony_ci return -EFAULT; 24862306a36Sopenharmony_ci return ax25_rt_opt(&rt_option); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci default: 25162306a36Sopenharmony_ci return -EINVAL; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic void *ax25_rt_seq_start(struct seq_file *seq, loff_t *pos) 25862306a36Sopenharmony_ci __acquires(ax25_route_lock) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci struct ax25_route *ax25_rt; 26162306a36Sopenharmony_ci int i = 1; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci read_lock(&ax25_route_lock); 26462306a36Sopenharmony_ci if (*pos == 0) 26562306a36Sopenharmony_ci return SEQ_START_TOKEN; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) { 26862306a36Sopenharmony_ci if (i == *pos) 26962306a36Sopenharmony_ci return ax25_rt; 27062306a36Sopenharmony_ci ++i; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci return NULL; 27462306a36Sopenharmony_ci} 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic void *ax25_rt_seq_next(struct seq_file *seq, void *v, loff_t *pos) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci ++*pos; 27962306a36Sopenharmony_ci return (v == SEQ_START_TOKEN) ? ax25_route_list : 28062306a36Sopenharmony_ci ((struct ax25_route *) v)->next; 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic void ax25_rt_seq_stop(struct seq_file *seq, void *v) 28462306a36Sopenharmony_ci __releases(ax25_route_lock) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci read_unlock(&ax25_route_lock); 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic int ax25_rt_seq_show(struct seq_file *seq, void *v) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci char buf[11]; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (v == SEQ_START_TOKEN) 29462306a36Sopenharmony_ci seq_puts(seq, "callsign dev mode digipeaters\n"); 29562306a36Sopenharmony_ci else { 29662306a36Sopenharmony_ci struct ax25_route *ax25_rt = v; 29762306a36Sopenharmony_ci const char *callsign; 29862306a36Sopenharmony_ci int i; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0) 30162306a36Sopenharmony_ci callsign = "default"; 30262306a36Sopenharmony_ci else 30362306a36Sopenharmony_ci callsign = ax2asc(buf, &ax25_rt->callsign); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci seq_printf(seq, "%-9s %-4s", 30662306a36Sopenharmony_ci callsign, 30762306a36Sopenharmony_ci ax25_rt->dev ? ax25_rt->dev->name : "???"); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci switch (ax25_rt->ip_mode) { 31062306a36Sopenharmony_ci case 'V': 31162306a36Sopenharmony_ci seq_puts(seq, " vc"); 31262306a36Sopenharmony_ci break; 31362306a36Sopenharmony_ci case 'D': 31462306a36Sopenharmony_ci seq_puts(seq, " dg"); 31562306a36Sopenharmony_ci break; 31662306a36Sopenharmony_ci default: 31762306a36Sopenharmony_ci seq_puts(seq, " *"); 31862306a36Sopenharmony_ci break; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (ax25_rt->digipeat != NULL) 32262306a36Sopenharmony_ci for (i = 0; i < ax25_rt->digipeat->ndigi; i++) 32362306a36Sopenharmony_ci seq_printf(seq, " %s", 32462306a36Sopenharmony_ci ax2asc(buf, &ax25_rt->digipeat->calls[i])); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci seq_puts(seq, "\n"); 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci return 0; 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ciconst struct seq_operations ax25_rt_seqops = { 33262306a36Sopenharmony_ci .start = ax25_rt_seq_start, 33362306a36Sopenharmony_ci .next = ax25_rt_seq_next, 33462306a36Sopenharmony_ci .stop = ax25_rt_seq_stop, 33562306a36Sopenharmony_ci .show = ax25_rt_seq_show, 33662306a36Sopenharmony_ci}; 33762306a36Sopenharmony_ci#endif 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci/* 34062306a36Sopenharmony_ci * Find AX.25 route 34162306a36Sopenharmony_ci * 34262306a36Sopenharmony_ci * Only routes with a reference count of zero can be destroyed. 34362306a36Sopenharmony_ci * Must be called with ax25_route_lock read locked. 34462306a36Sopenharmony_ci */ 34562306a36Sopenharmony_ciax25_route *ax25_get_route(ax25_address *addr, struct net_device *dev) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci ax25_route *ax25_spe_rt = NULL; 34862306a36Sopenharmony_ci ax25_route *ax25_def_rt = NULL; 34962306a36Sopenharmony_ci ax25_route *ax25_rt; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci /* 35262306a36Sopenharmony_ci * Bind to the physical interface we heard them on, or the default 35362306a36Sopenharmony_ci * route if none is found; 35462306a36Sopenharmony_ci */ 35562306a36Sopenharmony_ci for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) { 35662306a36Sopenharmony_ci if (dev == NULL) { 35762306a36Sopenharmony_ci if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev != NULL) 35862306a36Sopenharmony_ci ax25_spe_rt = ax25_rt; 35962306a36Sopenharmony_ci if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev != NULL) 36062306a36Sopenharmony_ci ax25_def_rt = ax25_rt; 36162306a36Sopenharmony_ci } else { 36262306a36Sopenharmony_ci if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev == dev) 36362306a36Sopenharmony_ci ax25_spe_rt = ax25_rt; 36462306a36Sopenharmony_ci if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev == dev) 36562306a36Sopenharmony_ci ax25_def_rt = ax25_rt; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci ax25_rt = ax25_def_rt; 37062306a36Sopenharmony_ci if (ax25_spe_rt != NULL) 37162306a36Sopenharmony_ci ax25_rt = ax25_spe_rt; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci return ax25_rt; 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci/* 37762306a36Sopenharmony_ci * Adjust path: If you specify a default route and want to connect 37862306a36Sopenharmony_ci * a target on the digipeater path but w/o having a special route 37962306a36Sopenharmony_ci * set before, the path has to be truncated from your target on. 38062306a36Sopenharmony_ci */ 38162306a36Sopenharmony_cistatic inline void ax25_adjust_path(ax25_address *addr, ax25_digi *digipeat) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci int k; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci for (k = 0; k < digipeat->ndigi; k++) { 38662306a36Sopenharmony_ci if (ax25cmp(addr, &digipeat->calls[k]) == 0) 38762306a36Sopenharmony_ci break; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci digipeat->ndigi = k; 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci/* 39562306a36Sopenharmony_ci * Find which interface to use. 39662306a36Sopenharmony_ci */ 39762306a36Sopenharmony_ciint ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci ax25_uid_assoc *user; 40062306a36Sopenharmony_ci ax25_route *ax25_rt; 40162306a36Sopenharmony_ci int err = 0; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci ax25_route_lock_use(); 40462306a36Sopenharmony_ci ax25_rt = ax25_get_route(addr, NULL); 40562306a36Sopenharmony_ci if (!ax25_rt) { 40662306a36Sopenharmony_ci ax25_route_lock_unuse(); 40762306a36Sopenharmony_ci return -EHOSTUNREACH; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci if ((ax25->ax25_dev = ax25_dev_ax25dev(ax25_rt->dev)) == NULL) { 41062306a36Sopenharmony_ci err = -EHOSTUNREACH; 41162306a36Sopenharmony_ci goto put; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci user = ax25_findbyuid(current_euid()); 41562306a36Sopenharmony_ci if (user) { 41662306a36Sopenharmony_ci ax25->source_addr = user->call; 41762306a36Sopenharmony_ci ax25_uid_put(user); 41862306a36Sopenharmony_ci } else { 41962306a36Sopenharmony_ci if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE)) { 42062306a36Sopenharmony_ci err = -EPERM; 42162306a36Sopenharmony_ci goto put; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci ax25->source_addr = *(ax25_address *)ax25->ax25_dev->dev->dev_addr; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci if (ax25_rt->digipeat != NULL) { 42762306a36Sopenharmony_ci ax25->digipeat = kmemdup(ax25_rt->digipeat, sizeof(ax25_digi), 42862306a36Sopenharmony_ci GFP_ATOMIC); 42962306a36Sopenharmony_ci if (ax25->digipeat == NULL) { 43062306a36Sopenharmony_ci err = -ENOMEM; 43162306a36Sopenharmony_ci goto put; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci ax25_adjust_path(addr, ax25->digipeat); 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (ax25->sk != NULL) { 43762306a36Sopenharmony_ci local_bh_disable(); 43862306a36Sopenharmony_ci bh_lock_sock(ax25->sk); 43962306a36Sopenharmony_ci sock_reset_flag(ax25->sk, SOCK_ZAPPED); 44062306a36Sopenharmony_ci bh_unlock_sock(ax25->sk); 44162306a36Sopenharmony_ci local_bh_enable(); 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ciput: 44562306a36Sopenharmony_ci ax25_route_lock_unuse(); 44662306a36Sopenharmony_ci return err; 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistruct sk_buff *ax25_rt_build_path(struct sk_buff *skb, ax25_address *src, 45062306a36Sopenharmony_ci ax25_address *dest, ax25_digi *digi) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci unsigned char *bp; 45362306a36Sopenharmony_ci int len; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci len = digi->ndigi * AX25_ADDR_LEN; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (unlikely(skb_headroom(skb) < len)) { 45862306a36Sopenharmony_ci skb = skb_expand_head(skb, len); 45962306a36Sopenharmony_ci if (!skb) { 46062306a36Sopenharmony_ci printk(KERN_CRIT "AX.25: ax25_dg_build_path - out of memory\n"); 46162306a36Sopenharmony_ci return NULL; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci bp = skb_push(skb, len); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci ax25_addr_build(bp, src, dest, digi, AX25_COMMAND, AX25_MODULUS); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci return skb; 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci/* 47362306a36Sopenharmony_ci * Free all memory associated with routing structures. 47462306a36Sopenharmony_ci */ 47562306a36Sopenharmony_civoid __exit ax25_rt_free(void) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci ax25_route *s, *ax25_rt = ax25_route_list; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci write_lock_bh(&ax25_route_lock); 48062306a36Sopenharmony_ci while (ax25_rt != NULL) { 48162306a36Sopenharmony_ci s = ax25_rt; 48262306a36Sopenharmony_ci ax25_rt = ax25_rt->next; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci kfree(s->digipeat); 48562306a36Sopenharmony_ci kfree(s); 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci write_unlock_bh(&ax25_route_lock); 48862306a36Sopenharmony_ci} 489