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