162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk)
562306a36Sopenharmony_ci * Copyright Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk)
662306a36Sopenharmony_ci * Copyright Tomi Manninen OH2BNS (oh2bns@sral.fi)
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#include <linux/errno.h>
962306a36Sopenharmony_ci#include <linux/types.h>
1062306a36Sopenharmony_ci#include <linux/socket.h>
1162306a36Sopenharmony_ci#include <linux/in.h>
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/timer.h>
1462306a36Sopenharmony_ci#include <linux/string.h>
1562306a36Sopenharmony_ci#include <linux/sockios.h>
1662306a36Sopenharmony_ci#include <linux/net.h>
1762306a36Sopenharmony_ci#include <linux/slab.h>
1862306a36Sopenharmony_ci#include <net/ax25.h>
1962306a36Sopenharmony_ci#include <linux/inet.h>
2062306a36Sopenharmony_ci#include <linux/netdevice.h>
2162306a36Sopenharmony_ci#include <net/arp.h>
2262306a36Sopenharmony_ci#include <linux/if_arp.h>
2362306a36Sopenharmony_ci#include <linux/skbuff.h>
2462306a36Sopenharmony_ci#include <net/sock.h>
2562306a36Sopenharmony_ci#include <linux/uaccess.h>
2662306a36Sopenharmony_ci#include <linux/fcntl.h>
2762306a36Sopenharmony_ci#include <linux/termios.h>	/* For TIOCINQ/OUTQ */
2862306a36Sopenharmony_ci#include <linux/mm.h>
2962306a36Sopenharmony_ci#include <linux/interrupt.h>
3062306a36Sopenharmony_ci#include <linux/notifier.h>
3162306a36Sopenharmony_ci#include <linux/init.h>
3262306a36Sopenharmony_ci#include <linux/spinlock.h>
3362306a36Sopenharmony_ci#include <net/netrom.h>
3462306a36Sopenharmony_ci#include <linux/seq_file.h>
3562306a36Sopenharmony_ci#include <linux/export.h>
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic unsigned int nr_neigh_no = 1;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic HLIST_HEAD(nr_node_list);
4062306a36Sopenharmony_cistatic DEFINE_SPINLOCK(nr_node_list_lock);
4162306a36Sopenharmony_cistatic HLIST_HEAD(nr_neigh_list);
4262306a36Sopenharmony_cistatic DEFINE_SPINLOCK(nr_neigh_list_lock);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic struct nr_node *nr_node_get(ax25_address *callsign)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	struct nr_node *found = NULL;
4762306a36Sopenharmony_ci	struct nr_node *nr_node;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	spin_lock_bh(&nr_node_list_lock);
5062306a36Sopenharmony_ci	nr_node_for_each(nr_node, &nr_node_list)
5162306a36Sopenharmony_ci		if (ax25cmp(callsign, &nr_node->callsign) == 0) {
5262306a36Sopenharmony_ci			nr_node_hold(nr_node);
5362306a36Sopenharmony_ci			found = nr_node;
5462306a36Sopenharmony_ci			break;
5562306a36Sopenharmony_ci		}
5662306a36Sopenharmony_ci	spin_unlock_bh(&nr_node_list_lock);
5762306a36Sopenharmony_ci	return found;
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic struct nr_neigh *nr_neigh_get_dev(ax25_address *callsign,
6162306a36Sopenharmony_ci					 struct net_device *dev)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	struct nr_neigh *found = NULL;
6462306a36Sopenharmony_ci	struct nr_neigh *nr_neigh;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	spin_lock_bh(&nr_neigh_list_lock);
6762306a36Sopenharmony_ci	nr_neigh_for_each(nr_neigh, &nr_neigh_list)
6862306a36Sopenharmony_ci		if (ax25cmp(callsign, &nr_neigh->callsign) == 0 &&
6962306a36Sopenharmony_ci		    nr_neigh->dev == dev) {
7062306a36Sopenharmony_ci			nr_neigh_hold(nr_neigh);
7162306a36Sopenharmony_ci			found = nr_neigh;
7262306a36Sopenharmony_ci			break;
7362306a36Sopenharmony_ci		}
7462306a36Sopenharmony_ci	spin_unlock_bh(&nr_neigh_list_lock);
7562306a36Sopenharmony_ci	return found;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic void nr_remove_neigh(struct nr_neigh *);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci/*      re-sort the routes in quality order.    */
8162306a36Sopenharmony_cistatic void re_sort_routes(struct nr_node *nr_node, int x, int y)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	if (nr_node->routes[y].quality > nr_node->routes[x].quality) {
8462306a36Sopenharmony_ci		if (nr_node->which == x)
8562306a36Sopenharmony_ci			nr_node->which = y;
8662306a36Sopenharmony_ci		else if (nr_node->which == y)
8762306a36Sopenharmony_ci			nr_node->which = x;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci		swap(nr_node->routes[x], nr_node->routes[y]);
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci/*
9462306a36Sopenharmony_ci *	Add a new route to a node, and in the process add the node and the
9562306a36Sopenharmony_ci *	neighbour if it is new.
9662306a36Sopenharmony_ci */
9762306a36Sopenharmony_cistatic int __must_check nr_add_node(ax25_address *nr, const char *mnemonic,
9862306a36Sopenharmony_ci	ax25_address *ax25, ax25_digi *ax25_digi, struct net_device *dev,
9962306a36Sopenharmony_ci	int quality, int obs_count)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	struct nr_node  *nr_node;
10262306a36Sopenharmony_ci	struct nr_neigh *nr_neigh;
10362306a36Sopenharmony_ci	int i, found;
10462306a36Sopenharmony_ci	struct net_device *odev;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	if ((odev=nr_dev_get(nr)) != NULL) {	/* Can't add routes to ourself */
10762306a36Sopenharmony_ci		dev_put(odev);
10862306a36Sopenharmony_ci		return -EINVAL;
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	nr_node = nr_node_get(nr);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	nr_neigh = nr_neigh_get_dev(ax25, dev);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	/*
11662306a36Sopenharmony_ci	 * The L2 link to a neighbour has failed in the past
11762306a36Sopenharmony_ci	 * and now a frame comes from this neighbour. We assume
11862306a36Sopenharmony_ci	 * it was a temporary trouble with the link and reset the
11962306a36Sopenharmony_ci	 * routes now (and not wait for a node broadcast).
12062306a36Sopenharmony_ci	 */
12162306a36Sopenharmony_ci	if (nr_neigh != NULL && nr_neigh->failed != 0 && quality == 0) {
12262306a36Sopenharmony_ci		struct nr_node *nr_nodet;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci		spin_lock_bh(&nr_node_list_lock);
12562306a36Sopenharmony_ci		nr_node_for_each(nr_nodet, &nr_node_list) {
12662306a36Sopenharmony_ci			nr_node_lock(nr_nodet);
12762306a36Sopenharmony_ci			for (i = 0; i < nr_nodet->count; i++)
12862306a36Sopenharmony_ci				if (nr_nodet->routes[i].neighbour == nr_neigh)
12962306a36Sopenharmony_ci					if (i < nr_nodet->which)
13062306a36Sopenharmony_ci						nr_nodet->which = i;
13162306a36Sopenharmony_ci			nr_node_unlock(nr_nodet);
13262306a36Sopenharmony_ci		}
13362306a36Sopenharmony_ci		spin_unlock_bh(&nr_node_list_lock);
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	if (nr_neigh != NULL)
13762306a36Sopenharmony_ci		nr_neigh->failed = 0;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	if (quality == 0 && nr_neigh != NULL && nr_node != NULL) {
14062306a36Sopenharmony_ci		nr_neigh_put(nr_neigh);
14162306a36Sopenharmony_ci		nr_node_put(nr_node);
14262306a36Sopenharmony_ci		return 0;
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	if (nr_neigh == NULL) {
14662306a36Sopenharmony_ci		if ((nr_neigh = kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL) {
14762306a36Sopenharmony_ci			if (nr_node)
14862306a36Sopenharmony_ci				nr_node_put(nr_node);
14962306a36Sopenharmony_ci			return -ENOMEM;
15062306a36Sopenharmony_ci		}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci		nr_neigh->callsign = *ax25;
15362306a36Sopenharmony_ci		nr_neigh->digipeat = NULL;
15462306a36Sopenharmony_ci		nr_neigh->ax25     = NULL;
15562306a36Sopenharmony_ci		nr_neigh->dev      = dev;
15662306a36Sopenharmony_ci		nr_neigh->quality  = READ_ONCE(sysctl_netrom_default_path_quality);
15762306a36Sopenharmony_ci		nr_neigh->locked   = 0;
15862306a36Sopenharmony_ci		nr_neigh->count    = 0;
15962306a36Sopenharmony_ci		nr_neigh->number   = nr_neigh_no++;
16062306a36Sopenharmony_ci		nr_neigh->failed   = 0;
16162306a36Sopenharmony_ci		refcount_set(&nr_neigh->refcount, 1);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci		if (ax25_digi != NULL && ax25_digi->ndigi > 0) {
16462306a36Sopenharmony_ci			nr_neigh->digipeat = kmemdup(ax25_digi,
16562306a36Sopenharmony_ci						     sizeof(*ax25_digi),
16662306a36Sopenharmony_ci						     GFP_KERNEL);
16762306a36Sopenharmony_ci			if (nr_neigh->digipeat == NULL) {
16862306a36Sopenharmony_ci				kfree(nr_neigh);
16962306a36Sopenharmony_ci				if (nr_node)
17062306a36Sopenharmony_ci					nr_node_put(nr_node);
17162306a36Sopenharmony_ci				return -ENOMEM;
17262306a36Sopenharmony_ci			}
17362306a36Sopenharmony_ci		}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci		spin_lock_bh(&nr_neigh_list_lock);
17662306a36Sopenharmony_ci		hlist_add_head(&nr_neigh->neigh_node, &nr_neigh_list);
17762306a36Sopenharmony_ci		nr_neigh_hold(nr_neigh);
17862306a36Sopenharmony_ci		spin_unlock_bh(&nr_neigh_list_lock);
17962306a36Sopenharmony_ci	}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	if (quality != 0 && ax25cmp(nr, ax25) == 0 && !nr_neigh->locked)
18262306a36Sopenharmony_ci		nr_neigh->quality = quality;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if (nr_node == NULL) {
18562306a36Sopenharmony_ci		if ((nr_node = kmalloc(sizeof(*nr_node), GFP_ATOMIC)) == NULL) {
18662306a36Sopenharmony_ci			if (nr_neigh)
18762306a36Sopenharmony_ci				nr_neigh_put(nr_neigh);
18862306a36Sopenharmony_ci			return -ENOMEM;
18962306a36Sopenharmony_ci		}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci		nr_node->callsign = *nr;
19262306a36Sopenharmony_ci		strcpy(nr_node->mnemonic, mnemonic);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci		nr_node->which = 0;
19562306a36Sopenharmony_ci		nr_node->count = 1;
19662306a36Sopenharmony_ci		refcount_set(&nr_node->refcount, 1);
19762306a36Sopenharmony_ci		spin_lock_init(&nr_node->node_lock);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci		nr_node->routes[0].quality   = quality;
20062306a36Sopenharmony_ci		nr_node->routes[0].obs_count = obs_count;
20162306a36Sopenharmony_ci		nr_node->routes[0].neighbour = nr_neigh;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci		nr_neigh_hold(nr_neigh);
20462306a36Sopenharmony_ci		nr_neigh->count++;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci		spin_lock_bh(&nr_node_list_lock);
20762306a36Sopenharmony_ci		hlist_add_head(&nr_node->node_node, &nr_node_list);
20862306a36Sopenharmony_ci		/* refcount initialized at 1 */
20962306a36Sopenharmony_ci		spin_unlock_bh(&nr_node_list_lock);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci		nr_neigh_put(nr_neigh);
21262306a36Sopenharmony_ci		return 0;
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci	nr_node_lock(nr_node);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	if (quality != 0)
21762306a36Sopenharmony_ci		strcpy(nr_node->mnemonic, mnemonic);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	for (found = 0, i = 0; i < nr_node->count; i++) {
22062306a36Sopenharmony_ci		if (nr_node->routes[i].neighbour == nr_neigh) {
22162306a36Sopenharmony_ci			nr_node->routes[i].quality   = quality;
22262306a36Sopenharmony_ci			nr_node->routes[i].obs_count = obs_count;
22362306a36Sopenharmony_ci			found = 1;
22462306a36Sopenharmony_ci			break;
22562306a36Sopenharmony_ci		}
22662306a36Sopenharmony_ci	}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	if (!found) {
22962306a36Sopenharmony_ci		/* We have space at the bottom, slot it in */
23062306a36Sopenharmony_ci		if (nr_node->count < 3) {
23162306a36Sopenharmony_ci			nr_node->routes[2] = nr_node->routes[1];
23262306a36Sopenharmony_ci			nr_node->routes[1] = nr_node->routes[0];
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci			nr_node->routes[0].quality   = quality;
23562306a36Sopenharmony_ci			nr_node->routes[0].obs_count = obs_count;
23662306a36Sopenharmony_ci			nr_node->routes[0].neighbour = nr_neigh;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci			nr_node->which++;
23962306a36Sopenharmony_ci			nr_node->count++;
24062306a36Sopenharmony_ci			nr_neigh_hold(nr_neigh);
24162306a36Sopenharmony_ci			nr_neigh->count++;
24262306a36Sopenharmony_ci		} else {
24362306a36Sopenharmony_ci			/* It must be better than the worst */
24462306a36Sopenharmony_ci			if (quality > nr_node->routes[2].quality) {
24562306a36Sopenharmony_ci				nr_node->routes[2].neighbour->count--;
24662306a36Sopenharmony_ci				nr_neigh_put(nr_node->routes[2].neighbour);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci				if (nr_node->routes[2].neighbour->count == 0 && !nr_node->routes[2].neighbour->locked)
24962306a36Sopenharmony_ci					nr_remove_neigh(nr_node->routes[2].neighbour);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci				nr_node->routes[2].quality   = quality;
25262306a36Sopenharmony_ci				nr_node->routes[2].obs_count = obs_count;
25362306a36Sopenharmony_ci				nr_node->routes[2].neighbour = nr_neigh;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci				nr_neigh_hold(nr_neigh);
25662306a36Sopenharmony_ci				nr_neigh->count++;
25762306a36Sopenharmony_ci			}
25862306a36Sopenharmony_ci		}
25962306a36Sopenharmony_ci	}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	/* Now re-sort the routes in quality order */
26262306a36Sopenharmony_ci	switch (nr_node->count) {
26362306a36Sopenharmony_ci	case 3:
26462306a36Sopenharmony_ci		re_sort_routes(nr_node, 0, 1);
26562306a36Sopenharmony_ci		re_sort_routes(nr_node, 1, 2);
26662306a36Sopenharmony_ci		fallthrough;
26762306a36Sopenharmony_ci	case 2:
26862306a36Sopenharmony_ci		re_sort_routes(nr_node, 0, 1);
26962306a36Sopenharmony_ci		break;
27062306a36Sopenharmony_ci	case 1:
27162306a36Sopenharmony_ci		break;
27262306a36Sopenharmony_ci	}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	for (i = 0; i < nr_node->count; i++) {
27562306a36Sopenharmony_ci		if (nr_node->routes[i].neighbour == nr_neigh) {
27662306a36Sopenharmony_ci			if (i < nr_node->which)
27762306a36Sopenharmony_ci				nr_node->which = i;
27862306a36Sopenharmony_ci			break;
27962306a36Sopenharmony_ci		}
28062306a36Sopenharmony_ci	}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	nr_neigh_put(nr_neigh);
28362306a36Sopenharmony_ci	nr_node_unlock(nr_node);
28462306a36Sopenharmony_ci	nr_node_put(nr_node);
28562306a36Sopenharmony_ci	return 0;
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic inline void __nr_remove_node(struct nr_node *nr_node)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	hlist_del_init(&nr_node->node_node);
29162306a36Sopenharmony_ci	nr_node_put(nr_node);
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci#define nr_remove_node_locked(__node) \
29562306a36Sopenharmony_ci	__nr_remove_node(__node)
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistatic void nr_remove_node(struct nr_node *nr_node)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	spin_lock_bh(&nr_node_list_lock);
30062306a36Sopenharmony_ci	__nr_remove_node(nr_node);
30162306a36Sopenharmony_ci	spin_unlock_bh(&nr_node_list_lock);
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistatic inline void __nr_remove_neigh(struct nr_neigh *nr_neigh)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	hlist_del_init(&nr_neigh->neigh_node);
30762306a36Sopenharmony_ci	nr_neigh_put(nr_neigh);
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci#define nr_remove_neigh_locked(__neigh) \
31162306a36Sopenharmony_ci	__nr_remove_neigh(__neigh)
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistatic void nr_remove_neigh(struct nr_neigh *nr_neigh)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	spin_lock_bh(&nr_neigh_list_lock);
31662306a36Sopenharmony_ci	__nr_remove_neigh(nr_neigh);
31762306a36Sopenharmony_ci	spin_unlock_bh(&nr_neigh_list_lock);
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci/*
32162306a36Sopenharmony_ci *	"Delete" a node. Strictly speaking remove a route to a node. The node
32262306a36Sopenharmony_ci *	is only deleted if no routes are left to it.
32362306a36Sopenharmony_ci */
32462306a36Sopenharmony_cistatic int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct net_device *dev)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	struct nr_node  *nr_node;
32762306a36Sopenharmony_ci	struct nr_neigh *nr_neigh;
32862306a36Sopenharmony_ci	int i;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	nr_node = nr_node_get(callsign);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	if (nr_node == NULL)
33362306a36Sopenharmony_ci		return -EINVAL;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	nr_neigh = nr_neigh_get_dev(neighbour, dev);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	if (nr_neigh == NULL) {
33862306a36Sopenharmony_ci		nr_node_put(nr_node);
33962306a36Sopenharmony_ci		return -EINVAL;
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	nr_node_lock(nr_node);
34362306a36Sopenharmony_ci	for (i = 0; i < nr_node->count; i++) {
34462306a36Sopenharmony_ci		if (nr_node->routes[i].neighbour == nr_neigh) {
34562306a36Sopenharmony_ci			nr_neigh->count--;
34662306a36Sopenharmony_ci			nr_neigh_put(nr_neigh);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci			if (nr_neigh->count == 0 && !nr_neigh->locked)
34962306a36Sopenharmony_ci				nr_remove_neigh(nr_neigh);
35062306a36Sopenharmony_ci			nr_neigh_put(nr_neigh);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci			nr_node->count--;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci			if (nr_node->count == 0) {
35562306a36Sopenharmony_ci				nr_remove_node(nr_node);
35662306a36Sopenharmony_ci			} else {
35762306a36Sopenharmony_ci				switch (i) {
35862306a36Sopenharmony_ci				case 0:
35962306a36Sopenharmony_ci					nr_node->routes[0] = nr_node->routes[1];
36062306a36Sopenharmony_ci					fallthrough;
36162306a36Sopenharmony_ci				case 1:
36262306a36Sopenharmony_ci					nr_node->routes[1] = nr_node->routes[2];
36362306a36Sopenharmony_ci					fallthrough;
36462306a36Sopenharmony_ci				case 2:
36562306a36Sopenharmony_ci					break;
36662306a36Sopenharmony_ci				}
36762306a36Sopenharmony_ci				nr_node_put(nr_node);
36862306a36Sopenharmony_ci			}
36962306a36Sopenharmony_ci			nr_node_unlock(nr_node);
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci			return 0;
37262306a36Sopenharmony_ci		}
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci	nr_neigh_put(nr_neigh);
37562306a36Sopenharmony_ci	nr_node_unlock(nr_node);
37662306a36Sopenharmony_ci	nr_node_put(nr_node);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	return -EINVAL;
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci/*
38262306a36Sopenharmony_ci *	Lock a neighbour with a quality.
38362306a36Sopenharmony_ci */
38462306a36Sopenharmony_cistatic int __must_check nr_add_neigh(ax25_address *callsign,
38562306a36Sopenharmony_ci	ax25_digi *ax25_digi, struct net_device *dev, unsigned int quality)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	struct nr_neigh *nr_neigh;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	nr_neigh = nr_neigh_get_dev(callsign, dev);
39062306a36Sopenharmony_ci	if (nr_neigh) {
39162306a36Sopenharmony_ci		nr_neigh->quality = quality;
39262306a36Sopenharmony_ci		nr_neigh->locked  = 1;
39362306a36Sopenharmony_ci		nr_neigh_put(nr_neigh);
39462306a36Sopenharmony_ci		return 0;
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	if ((nr_neigh = kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL)
39862306a36Sopenharmony_ci		return -ENOMEM;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	nr_neigh->callsign = *callsign;
40162306a36Sopenharmony_ci	nr_neigh->digipeat = NULL;
40262306a36Sopenharmony_ci	nr_neigh->ax25     = NULL;
40362306a36Sopenharmony_ci	nr_neigh->dev      = dev;
40462306a36Sopenharmony_ci	nr_neigh->quality  = quality;
40562306a36Sopenharmony_ci	nr_neigh->locked   = 1;
40662306a36Sopenharmony_ci	nr_neigh->count    = 0;
40762306a36Sopenharmony_ci	nr_neigh->number   = nr_neigh_no++;
40862306a36Sopenharmony_ci	nr_neigh->failed   = 0;
40962306a36Sopenharmony_ci	refcount_set(&nr_neigh->refcount, 1);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	if (ax25_digi != NULL && ax25_digi->ndigi > 0) {
41262306a36Sopenharmony_ci		nr_neigh->digipeat = kmemdup(ax25_digi, sizeof(*ax25_digi),
41362306a36Sopenharmony_ci					     GFP_KERNEL);
41462306a36Sopenharmony_ci		if (nr_neigh->digipeat == NULL) {
41562306a36Sopenharmony_ci			kfree(nr_neigh);
41662306a36Sopenharmony_ci			return -ENOMEM;
41762306a36Sopenharmony_ci		}
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	spin_lock_bh(&nr_neigh_list_lock);
42162306a36Sopenharmony_ci	hlist_add_head(&nr_neigh->neigh_node, &nr_neigh_list);
42262306a36Sopenharmony_ci	/* refcount is initialized at 1 */
42362306a36Sopenharmony_ci	spin_unlock_bh(&nr_neigh_list_lock);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	return 0;
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci/*
42962306a36Sopenharmony_ci *	"Delete" a neighbour. The neighbour is only removed if the number
43062306a36Sopenharmony_ci *	of nodes that may use it is zero.
43162306a36Sopenharmony_ci */
43262306a36Sopenharmony_cistatic int nr_del_neigh(ax25_address *callsign, struct net_device *dev, unsigned int quality)
43362306a36Sopenharmony_ci{
43462306a36Sopenharmony_ci	struct nr_neigh *nr_neigh;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	nr_neigh = nr_neigh_get_dev(callsign, dev);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	if (nr_neigh == NULL) return -EINVAL;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	nr_neigh->quality = quality;
44162306a36Sopenharmony_ci	nr_neigh->locked  = 0;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	if (nr_neigh->count == 0)
44462306a36Sopenharmony_ci		nr_remove_neigh(nr_neigh);
44562306a36Sopenharmony_ci	nr_neigh_put(nr_neigh);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	return 0;
44862306a36Sopenharmony_ci}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci/*
45162306a36Sopenharmony_ci *	Decrement the obsolescence count by one. If a route is reduced to a
45262306a36Sopenharmony_ci *	count of zero, remove it. Also remove any unlocked neighbours with
45362306a36Sopenharmony_ci *	zero nodes routing via it.
45462306a36Sopenharmony_ci */
45562306a36Sopenharmony_cistatic int nr_dec_obs(void)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	struct nr_neigh *nr_neigh;
45862306a36Sopenharmony_ci	struct nr_node  *s;
45962306a36Sopenharmony_ci	struct hlist_node *nodet;
46062306a36Sopenharmony_ci	int i;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	spin_lock_bh(&nr_node_list_lock);
46362306a36Sopenharmony_ci	nr_node_for_each_safe(s, nodet, &nr_node_list) {
46462306a36Sopenharmony_ci		nr_node_lock(s);
46562306a36Sopenharmony_ci		for (i = 0; i < s->count; i++) {
46662306a36Sopenharmony_ci			switch (s->routes[i].obs_count) {
46762306a36Sopenharmony_ci			case 0:		/* A locked entry */
46862306a36Sopenharmony_ci				break;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci			case 1:		/* From 1 -> 0 */
47162306a36Sopenharmony_ci				nr_neigh = s->routes[i].neighbour;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci				nr_neigh->count--;
47462306a36Sopenharmony_ci				nr_neigh_put(nr_neigh);
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci				if (nr_neigh->count == 0 && !nr_neigh->locked)
47762306a36Sopenharmony_ci					nr_remove_neigh(nr_neigh);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci				s->count--;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci				switch (i) {
48262306a36Sopenharmony_ci				case 0:
48362306a36Sopenharmony_ci					s->routes[0] = s->routes[1];
48462306a36Sopenharmony_ci					fallthrough;
48562306a36Sopenharmony_ci				case 1:
48662306a36Sopenharmony_ci					s->routes[1] = s->routes[2];
48762306a36Sopenharmony_ci					break;
48862306a36Sopenharmony_ci				case 2:
48962306a36Sopenharmony_ci					break;
49062306a36Sopenharmony_ci				}
49162306a36Sopenharmony_ci				break;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci			default:
49462306a36Sopenharmony_ci				s->routes[i].obs_count--;
49562306a36Sopenharmony_ci				break;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci			}
49862306a36Sopenharmony_ci		}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci		if (s->count <= 0)
50162306a36Sopenharmony_ci			nr_remove_node_locked(s);
50262306a36Sopenharmony_ci		nr_node_unlock(s);
50362306a36Sopenharmony_ci	}
50462306a36Sopenharmony_ci	spin_unlock_bh(&nr_node_list_lock);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	return 0;
50762306a36Sopenharmony_ci}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci/*
51062306a36Sopenharmony_ci *	A device has been removed. Remove its routes and neighbours.
51162306a36Sopenharmony_ci */
51262306a36Sopenharmony_civoid nr_rt_device_down(struct net_device *dev)
51362306a36Sopenharmony_ci{
51462306a36Sopenharmony_ci	struct nr_neigh *s;
51562306a36Sopenharmony_ci	struct hlist_node *nodet, *node2t;
51662306a36Sopenharmony_ci	struct nr_node  *t;
51762306a36Sopenharmony_ci	int i;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	spin_lock_bh(&nr_neigh_list_lock);
52062306a36Sopenharmony_ci	nr_neigh_for_each_safe(s, nodet, &nr_neigh_list) {
52162306a36Sopenharmony_ci		if (s->dev == dev) {
52262306a36Sopenharmony_ci			spin_lock_bh(&nr_node_list_lock);
52362306a36Sopenharmony_ci			nr_node_for_each_safe(t, node2t, &nr_node_list) {
52462306a36Sopenharmony_ci				nr_node_lock(t);
52562306a36Sopenharmony_ci				for (i = 0; i < t->count; i++) {
52662306a36Sopenharmony_ci					if (t->routes[i].neighbour == s) {
52762306a36Sopenharmony_ci						t->count--;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci						switch (i) {
53062306a36Sopenharmony_ci						case 0:
53162306a36Sopenharmony_ci							t->routes[0] = t->routes[1];
53262306a36Sopenharmony_ci							fallthrough;
53362306a36Sopenharmony_ci						case 1:
53462306a36Sopenharmony_ci							t->routes[1] = t->routes[2];
53562306a36Sopenharmony_ci							break;
53662306a36Sopenharmony_ci						case 2:
53762306a36Sopenharmony_ci							break;
53862306a36Sopenharmony_ci						}
53962306a36Sopenharmony_ci					}
54062306a36Sopenharmony_ci				}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci				if (t->count <= 0)
54362306a36Sopenharmony_ci					nr_remove_node_locked(t);
54462306a36Sopenharmony_ci				nr_node_unlock(t);
54562306a36Sopenharmony_ci			}
54662306a36Sopenharmony_ci			spin_unlock_bh(&nr_node_list_lock);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci			nr_remove_neigh_locked(s);
54962306a36Sopenharmony_ci		}
55062306a36Sopenharmony_ci	}
55162306a36Sopenharmony_ci	spin_unlock_bh(&nr_neigh_list_lock);
55262306a36Sopenharmony_ci}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci/*
55562306a36Sopenharmony_ci *	Check that the device given is a valid AX.25 interface that is "up".
55662306a36Sopenharmony_ci *	Or a valid ethernet interface with an AX.25 callsign binding.
55762306a36Sopenharmony_ci */
55862306a36Sopenharmony_cistatic struct net_device *nr_ax25_dev_get(char *devname)
55962306a36Sopenharmony_ci{
56062306a36Sopenharmony_ci	struct net_device *dev;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	if ((dev = dev_get_by_name(&init_net, devname)) == NULL)
56362306a36Sopenharmony_ci		return NULL;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	if ((dev->flags & IFF_UP) && dev->type == ARPHRD_AX25)
56662306a36Sopenharmony_ci		return dev;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	dev_put(dev);
56962306a36Sopenharmony_ci	return NULL;
57062306a36Sopenharmony_ci}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci/*
57362306a36Sopenharmony_ci *	Find the first active NET/ROM device, usually "nr0".
57462306a36Sopenharmony_ci */
57562306a36Sopenharmony_cistruct net_device *nr_dev_first(void)
57662306a36Sopenharmony_ci{
57762306a36Sopenharmony_ci	struct net_device *dev, *first = NULL;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	rcu_read_lock();
58062306a36Sopenharmony_ci	for_each_netdev_rcu(&init_net, dev) {
58162306a36Sopenharmony_ci		if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM)
58262306a36Sopenharmony_ci			if (first == NULL || strncmp(dev->name, first->name, 3) < 0)
58362306a36Sopenharmony_ci				first = dev;
58462306a36Sopenharmony_ci	}
58562306a36Sopenharmony_ci	dev_hold(first);
58662306a36Sopenharmony_ci	rcu_read_unlock();
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	return first;
58962306a36Sopenharmony_ci}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci/*
59262306a36Sopenharmony_ci *	Find the NET/ROM device for the given callsign.
59362306a36Sopenharmony_ci */
59462306a36Sopenharmony_cistruct net_device *nr_dev_get(ax25_address *addr)
59562306a36Sopenharmony_ci{
59662306a36Sopenharmony_ci	struct net_device *dev;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	rcu_read_lock();
59962306a36Sopenharmony_ci	for_each_netdev_rcu(&init_net, dev) {
60062306a36Sopenharmony_ci		if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM &&
60162306a36Sopenharmony_ci		    ax25cmp(addr, (const ax25_address *)dev->dev_addr) == 0) {
60262306a36Sopenharmony_ci			dev_hold(dev);
60362306a36Sopenharmony_ci			goto out;
60462306a36Sopenharmony_ci		}
60562306a36Sopenharmony_ci	}
60662306a36Sopenharmony_ci	dev = NULL;
60762306a36Sopenharmony_ciout:
60862306a36Sopenharmony_ci	rcu_read_unlock();
60962306a36Sopenharmony_ci	return dev;
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_cistatic ax25_digi *nr_call_to_digi(ax25_digi *digi, int ndigis,
61362306a36Sopenharmony_ci	ax25_address *digipeaters)
61462306a36Sopenharmony_ci{
61562306a36Sopenharmony_ci	int i;
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	if (ndigis == 0)
61862306a36Sopenharmony_ci		return NULL;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	for (i = 0; i < ndigis; i++) {
62162306a36Sopenharmony_ci		digi->calls[i]    = digipeaters[i];
62262306a36Sopenharmony_ci		digi->repeated[i] = 0;
62362306a36Sopenharmony_ci	}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	digi->ndigi      = ndigis;
62662306a36Sopenharmony_ci	digi->lastrepeat = -1;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	return digi;
62962306a36Sopenharmony_ci}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci/*
63262306a36Sopenharmony_ci *	Handle the ioctls that control the routing functions.
63362306a36Sopenharmony_ci */
63462306a36Sopenharmony_ciint nr_rt_ioctl(unsigned int cmd, void __user *arg)
63562306a36Sopenharmony_ci{
63662306a36Sopenharmony_ci	struct nr_route_struct nr_route;
63762306a36Sopenharmony_ci	struct net_device *dev;
63862306a36Sopenharmony_ci	ax25_digi digi;
63962306a36Sopenharmony_ci	int ret;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	switch (cmd) {
64262306a36Sopenharmony_ci	case SIOCADDRT:
64362306a36Sopenharmony_ci		if (copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct)))
64462306a36Sopenharmony_ci			return -EFAULT;
64562306a36Sopenharmony_ci		if (nr_route.ndigis > AX25_MAX_DIGIS)
64662306a36Sopenharmony_ci			return -EINVAL;
64762306a36Sopenharmony_ci		if ((dev = nr_ax25_dev_get(nr_route.device)) == NULL)
64862306a36Sopenharmony_ci			return -EINVAL;
64962306a36Sopenharmony_ci		switch (nr_route.type) {
65062306a36Sopenharmony_ci		case NETROM_NODE:
65162306a36Sopenharmony_ci			if (strnlen(nr_route.mnemonic, 7) == 7) {
65262306a36Sopenharmony_ci				ret = -EINVAL;
65362306a36Sopenharmony_ci				break;
65462306a36Sopenharmony_ci			}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci			ret = nr_add_node(&nr_route.callsign,
65762306a36Sopenharmony_ci				nr_route.mnemonic,
65862306a36Sopenharmony_ci				&nr_route.neighbour,
65962306a36Sopenharmony_ci				nr_call_to_digi(&digi, nr_route.ndigis,
66062306a36Sopenharmony_ci						nr_route.digipeaters),
66162306a36Sopenharmony_ci				dev, nr_route.quality,
66262306a36Sopenharmony_ci				nr_route.obs_count);
66362306a36Sopenharmony_ci			break;
66462306a36Sopenharmony_ci		case NETROM_NEIGH:
66562306a36Sopenharmony_ci			ret = nr_add_neigh(&nr_route.callsign,
66662306a36Sopenharmony_ci				nr_call_to_digi(&digi, nr_route.ndigis,
66762306a36Sopenharmony_ci						nr_route.digipeaters),
66862306a36Sopenharmony_ci				dev, nr_route.quality);
66962306a36Sopenharmony_ci			break;
67062306a36Sopenharmony_ci		default:
67162306a36Sopenharmony_ci			ret = -EINVAL;
67262306a36Sopenharmony_ci		}
67362306a36Sopenharmony_ci		dev_put(dev);
67462306a36Sopenharmony_ci		return ret;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	case SIOCDELRT:
67762306a36Sopenharmony_ci		if (copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct)))
67862306a36Sopenharmony_ci			return -EFAULT;
67962306a36Sopenharmony_ci		if ((dev = nr_ax25_dev_get(nr_route.device)) == NULL)
68062306a36Sopenharmony_ci			return -EINVAL;
68162306a36Sopenharmony_ci		switch (nr_route.type) {
68262306a36Sopenharmony_ci		case NETROM_NODE:
68362306a36Sopenharmony_ci			ret = nr_del_node(&nr_route.callsign,
68462306a36Sopenharmony_ci				&nr_route.neighbour, dev);
68562306a36Sopenharmony_ci			break;
68662306a36Sopenharmony_ci		case NETROM_NEIGH:
68762306a36Sopenharmony_ci			ret = nr_del_neigh(&nr_route.callsign,
68862306a36Sopenharmony_ci				dev, nr_route.quality);
68962306a36Sopenharmony_ci			break;
69062306a36Sopenharmony_ci		default:
69162306a36Sopenharmony_ci			ret = -EINVAL;
69262306a36Sopenharmony_ci		}
69362306a36Sopenharmony_ci		dev_put(dev);
69462306a36Sopenharmony_ci		return ret;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	case SIOCNRDECOBS:
69762306a36Sopenharmony_ci		return nr_dec_obs();
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	default:
70062306a36Sopenharmony_ci		return -EINVAL;
70162306a36Sopenharmony_ci	}
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	return 0;
70462306a36Sopenharmony_ci}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci/*
70762306a36Sopenharmony_ci * 	A level 2 link has timed out, therefore it appears to be a poor link,
70862306a36Sopenharmony_ci *	then don't use that neighbour until it is reset.
70962306a36Sopenharmony_ci */
71062306a36Sopenharmony_civoid nr_link_failed(ax25_cb *ax25, int reason)
71162306a36Sopenharmony_ci{
71262306a36Sopenharmony_ci	struct nr_neigh *s, *nr_neigh = NULL;
71362306a36Sopenharmony_ci	struct nr_node  *nr_node = NULL;
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	spin_lock_bh(&nr_neigh_list_lock);
71662306a36Sopenharmony_ci	nr_neigh_for_each(s, &nr_neigh_list) {
71762306a36Sopenharmony_ci		if (s->ax25 == ax25) {
71862306a36Sopenharmony_ci			nr_neigh_hold(s);
71962306a36Sopenharmony_ci			nr_neigh = s;
72062306a36Sopenharmony_ci			break;
72162306a36Sopenharmony_ci		}
72262306a36Sopenharmony_ci	}
72362306a36Sopenharmony_ci	spin_unlock_bh(&nr_neigh_list_lock);
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	if (nr_neigh == NULL)
72662306a36Sopenharmony_ci		return;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	nr_neigh->ax25 = NULL;
72962306a36Sopenharmony_ci	ax25_cb_put(ax25);
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	if (++nr_neigh->failed < READ_ONCE(sysctl_netrom_link_fails_count)) {
73262306a36Sopenharmony_ci		nr_neigh_put(nr_neigh);
73362306a36Sopenharmony_ci		return;
73462306a36Sopenharmony_ci	}
73562306a36Sopenharmony_ci	spin_lock_bh(&nr_node_list_lock);
73662306a36Sopenharmony_ci	nr_node_for_each(nr_node, &nr_node_list) {
73762306a36Sopenharmony_ci		nr_node_lock(nr_node);
73862306a36Sopenharmony_ci		if (nr_node->which < nr_node->count &&
73962306a36Sopenharmony_ci		    nr_node->routes[nr_node->which].neighbour == nr_neigh)
74062306a36Sopenharmony_ci			nr_node->which++;
74162306a36Sopenharmony_ci		nr_node_unlock(nr_node);
74262306a36Sopenharmony_ci	}
74362306a36Sopenharmony_ci	spin_unlock_bh(&nr_node_list_lock);
74462306a36Sopenharmony_ci	nr_neigh_put(nr_neigh);
74562306a36Sopenharmony_ci}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci/*
74862306a36Sopenharmony_ci *	Route a frame to an appropriate AX.25 connection. A NULL ax25_cb
74962306a36Sopenharmony_ci *	indicates an internally generated frame.
75062306a36Sopenharmony_ci */
75162306a36Sopenharmony_ciint nr_route_frame(struct sk_buff *skb, ax25_cb *ax25)
75262306a36Sopenharmony_ci{
75362306a36Sopenharmony_ci	ax25_address *nr_src, *nr_dest;
75462306a36Sopenharmony_ci	struct nr_neigh *nr_neigh;
75562306a36Sopenharmony_ci	struct nr_node  *nr_node;
75662306a36Sopenharmony_ci	struct net_device *dev;
75762306a36Sopenharmony_ci	unsigned char *dptr;
75862306a36Sopenharmony_ci	ax25_cb *ax25s;
75962306a36Sopenharmony_ci	int ret;
76062306a36Sopenharmony_ci	struct sk_buff *skbn;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	nr_src  = (ax25_address *)(skb->data + 0);
76462306a36Sopenharmony_ci	nr_dest = (ax25_address *)(skb->data + 7);
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	if (ax25 != NULL) {
76762306a36Sopenharmony_ci		ret = nr_add_node(nr_src, "", &ax25->dest_addr, ax25->digipeat,
76862306a36Sopenharmony_ci				  ax25->ax25_dev->dev, 0,
76962306a36Sopenharmony_ci				  READ_ONCE(sysctl_netrom_obsolescence_count_initialiser));
77062306a36Sopenharmony_ci		if (ret)
77162306a36Sopenharmony_ci			return ret;
77262306a36Sopenharmony_ci	}
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	if ((dev = nr_dev_get(nr_dest)) != NULL) {	/* Its for me */
77562306a36Sopenharmony_ci		if (ax25 == NULL)			/* Its from me */
77662306a36Sopenharmony_ci			ret = nr_loopback_queue(skb);
77762306a36Sopenharmony_ci		else
77862306a36Sopenharmony_ci			ret = nr_rx_frame(skb, dev);
77962306a36Sopenharmony_ci		dev_put(dev);
78062306a36Sopenharmony_ci		return ret;
78162306a36Sopenharmony_ci	}
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	if (!READ_ONCE(sysctl_netrom_routing_control) && ax25 != NULL)
78462306a36Sopenharmony_ci		return 0;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	/* Its Time-To-Live has expired */
78762306a36Sopenharmony_ci	if (skb->data[14] == 1) {
78862306a36Sopenharmony_ci		return 0;
78962306a36Sopenharmony_ci	}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	nr_node = nr_node_get(nr_dest);
79262306a36Sopenharmony_ci	if (nr_node == NULL)
79362306a36Sopenharmony_ci		return 0;
79462306a36Sopenharmony_ci	nr_node_lock(nr_node);
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	if (nr_node->which >= nr_node->count) {
79762306a36Sopenharmony_ci		nr_node_unlock(nr_node);
79862306a36Sopenharmony_ci		nr_node_put(nr_node);
79962306a36Sopenharmony_ci		return 0;
80062306a36Sopenharmony_ci	}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	nr_neigh = nr_node->routes[nr_node->which].neighbour;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	if ((dev = nr_dev_first()) == NULL) {
80562306a36Sopenharmony_ci		nr_node_unlock(nr_node);
80662306a36Sopenharmony_ci		nr_node_put(nr_node);
80762306a36Sopenharmony_ci		return 0;
80862306a36Sopenharmony_ci	}
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	/* We are going to change the netrom headers so we should get our
81162306a36Sopenharmony_ci	   own skb, we also did not know until now how much header space
81262306a36Sopenharmony_ci	   we had to reserve... - RXQ */
81362306a36Sopenharmony_ci	if ((skbn=skb_copy_expand(skb, dev->hard_header_len, 0, GFP_ATOMIC)) == NULL) {
81462306a36Sopenharmony_ci		nr_node_unlock(nr_node);
81562306a36Sopenharmony_ci		nr_node_put(nr_node);
81662306a36Sopenharmony_ci		dev_put(dev);
81762306a36Sopenharmony_ci		return 0;
81862306a36Sopenharmony_ci	}
81962306a36Sopenharmony_ci	kfree_skb(skb);
82062306a36Sopenharmony_ci	skb=skbn;
82162306a36Sopenharmony_ci	skb->data[14]--;
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	dptr  = skb_push(skb, 1);
82462306a36Sopenharmony_ci	*dptr = AX25_P_NETROM;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	ax25s = nr_neigh->ax25;
82762306a36Sopenharmony_ci	nr_neigh->ax25 = ax25_send_frame(skb, 256,
82862306a36Sopenharmony_ci					 (const ax25_address *)dev->dev_addr,
82962306a36Sopenharmony_ci					 &nr_neigh->callsign,
83062306a36Sopenharmony_ci					 nr_neigh->digipeat, nr_neigh->dev);
83162306a36Sopenharmony_ci	if (ax25s)
83262306a36Sopenharmony_ci		ax25_cb_put(ax25s);
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	dev_put(dev);
83562306a36Sopenharmony_ci	ret = (nr_neigh->ax25 != NULL);
83662306a36Sopenharmony_ci	nr_node_unlock(nr_node);
83762306a36Sopenharmony_ci	nr_node_put(nr_node);
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	return ret;
84062306a36Sopenharmony_ci}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_cistatic void *nr_node_start(struct seq_file *seq, loff_t *pos)
84562306a36Sopenharmony_ci	__acquires(&nr_node_list_lock)
84662306a36Sopenharmony_ci{
84762306a36Sopenharmony_ci	spin_lock_bh(&nr_node_list_lock);
84862306a36Sopenharmony_ci	return seq_hlist_start_head(&nr_node_list, *pos);
84962306a36Sopenharmony_ci}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_cistatic void *nr_node_next(struct seq_file *seq, void *v, loff_t *pos)
85262306a36Sopenharmony_ci{
85362306a36Sopenharmony_ci	return seq_hlist_next(v, &nr_node_list, pos);
85462306a36Sopenharmony_ci}
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_cistatic void nr_node_stop(struct seq_file *seq, void *v)
85762306a36Sopenharmony_ci	__releases(&nr_node_list_lock)
85862306a36Sopenharmony_ci{
85962306a36Sopenharmony_ci	spin_unlock_bh(&nr_node_list_lock);
86062306a36Sopenharmony_ci}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_cistatic int nr_node_show(struct seq_file *seq, void *v)
86362306a36Sopenharmony_ci{
86462306a36Sopenharmony_ci	char buf[11];
86562306a36Sopenharmony_ci	int i;
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	if (v == SEQ_START_TOKEN)
86862306a36Sopenharmony_ci		seq_puts(seq,
86962306a36Sopenharmony_ci			 "callsign  mnemonic w n qual obs neigh qual obs neigh qual obs neigh\n");
87062306a36Sopenharmony_ci	else {
87162306a36Sopenharmony_ci		struct nr_node *nr_node = hlist_entry(v, struct nr_node,
87262306a36Sopenharmony_ci						      node_node);
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci		nr_node_lock(nr_node);
87562306a36Sopenharmony_ci		seq_printf(seq, "%-9s %-7s  %d %d",
87662306a36Sopenharmony_ci			ax2asc(buf, &nr_node->callsign),
87762306a36Sopenharmony_ci			(nr_node->mnemonic[0] == '\0') ? "*" : nr_node->mnemonic,
87862306a36Sopenharmony_ci			nr_node->which + 1,
87962306a36Sopenharmony_ci			nr_node->count);
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci		for (i = 0; i < nr_node->count; i++) {
88262306a36Sopenharmony_ci			seq_printf(seq, "  %3d   %d %05d",
88362306a36Sopenharmony_ci				nr_node->routes[i].quality,
88462306a36Sopenharmony_ci				nr_node->routes[i].obs_count,
88562306a36Sopenharmony_ci				nr_node->routes[i].neighbour->number);
88662306a36Sopenharmony_ci		}
88762306a36Sopenharmony_ci		nr_node_unlock(nr_node);
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci		seq_puts(seq, "\n");
89062306a36Sopenharmony_ci	}
89162306a36Sopenharmony_ci	return 0;
89262306a36Sopenharmony_ci}
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ciconst struct seq_operations nr_node_seqops = {
89562306a36Sopenharmony_ci	.start = nr_node_start,
89662306a36Sopenharmony_ci	.next = nr_node_next,
89762306a36Sopenharmony_ci	.stop = nr_node_stop,
89862306a36Sopenharmony_ci	.show = nr_node_show,
89962306a36Sopenharmony_ci};
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_cistatic void *nr_neigh_start(struct seq_file *seq, loff_t *pos)
90262306a36Sopenharmony_ci	__acquires(&nr_neigh_list_lock)
90362306a36Sopenharmony_ci{
90462306a36Sopenharmony_ci	spin_lock_bh(&nr_neigh_list_lock);
90562306a36Sopenharmony_ci	return seq_hlist_start_head(&nr_neigh_list, *pos);
90662306a36Sopenharmony_ci}
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_cistatic void *nr_neigh_next(struct seq_file *seq, void *v, loff_t *pos)
90962306a36Sopenharmony_ci{
91062306a36Sopenharmony_ci	return seq_hlist_next(v, &nr_neigh_list, pos);
91162306a36Sopenharmony_ci}
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_cistatic void nr_neigh_stop(struct seq_file *seq, void *v)
91462306a36Sopenharmony_ci	__releases(&nr_neigh_list_lock)
91562306a36Sopenharmony_ci{
91662306a36Sopenharmony_ci	spin_unlock_bh(&nr_neigh_list_lock);
91762306a36Sopenharmony_ci}
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_cistatic int nr_neigh_show(struct seq_file *seq, void *v)
92062306a36Sopenharmony_ci{
92162306a36Sopenharmony_ci	char buf[11];
92262306a36Sopenharmony_ci	int i;
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	if (v == SEQ_START_TOKEN)
92562306a36Sopenharmony_ci		seq_puts(seq, "addr  callsign  dev  qual lock count failed digipeaters\n");
92662306a36Sopenharmony_ci	else {
92762306a36Sopenharmony_ci		struct nr_neigh *nr_neigh;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci		nr_neigh = hlist_entry(v, struct nr_neigh, neigh_node);
93062306a36Sopenharmony_ci		seq_printf(seq, "%05d %-9s %-4s  %3d    %d   %3d    %3d",
93162306a36Sopenharmony_ci			nr_neigh->number,
93262306a36Sopenharmony_ci			ax2asc(buf, &nr_neigh->callsign),
93362306a36Sopenharmony_ci			nr_neigh->dev ? nr_neigh->dev->name : "???",
93462306a36Sopenharmony_ci			nr_neigh->quality,
93562306a36Sopenharmony_ci			nr_neigh->locked,
93662306a36Sopenharmony_ci			nr_neigh->count,
93762306a36Sopenharmony_ci			nr_neigh->failed);
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci		if (nr_neigh->digipeat != NULL) {
94062306a36Sopenharmony_ci			for (i = 0; i < nr_neigh->digipeat->ndigi; i++)
94162306a36Sopenharmony_ci				seq_printf(seq, " %s",
94262306a36Sopenharmony_ci					   ax2asc(buf, &nr_neigh->digipeat->calls[i]));
94362306a36Sopenharmony_ci		}
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci		seq_puts(seq, "\n");
94662306a36Sopenharmony_ci	}
94762306a36Sopenharmony_ci	return 0;
94862306a36Sopenharmony_ci}
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ciconst struct seq_operations nr_neigh_seqops = {
95162306a36Sopenharmony_ci	.start = nr_neigh_start,
95262306a36Sopenharmony_ci	.next = nr_neigh_next,
95362306a36Sopenharmony_ci	.stop = nr_neigh_stop,
95462306a36Sopenharmony_ci	.show = nr_neigh_show,
95562306a36Sopenharmony_ci};
95662306a36Sopenharmony_ci#endif
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci/*
95962306a36Sopenharmony_ci *	Free all memory associated with the nodes and routes lists.
96062306a36Sopenharmony_ci */
96162306a36Sopenharmony_civoid nr_rt_free(void)
96262306a36Sopenharmony_ci{
96362306a36Sopenharmony_ci	struct nr_neigh *s = NULL;
96462306a36Sopenharmony_ci	struct nr_node  *t = NULL;
96562306a36Sopenharmony_ci	struct hlist_node *nodet;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	spin_lock_bh(&nr_neigh_list_lock);
96862306a36Sopenharmony_ci	spin_lock_bh(&nr_node_list_lock);
96962306a36Sopenharmony_ci	nr_node_for_each_safe(t, nodet, &nr_node_list) {
97062306a36Sopenharmony_ci		nr_node_lock(t);
97162306a36Sopenharmony_ci		nr_remove_node_locked(t);
97262306a36Sopenharmony_ci		nr_node_unlock(t);
97362306a36Sopenharmony_ci	}
97462306a36Sopenharmony_ci	nr_neigh_for_each_safe(s, nodet, &nr_neigh_list) {
97562306a36Sopenharmony_ci		while(s->count) {
97662306a36Sopenharmony_ci			s->count--;
97762306a36Sopenharmony_ci			nr_neigh_put(s);
97862306a36Sopenharmony_ci		}
97962306a36Sopenharmony_ci		nr_remove_neigh_locked(s);
98062306a36Sopenharmony_ci	}
98162306a36Sopenharmony_ci	spin_unlock_bh(&nr_node_list_lock);
98262306a36Sopenharmony_ci	spin_unlock_bh(&nr_neigh_list_lock);
98362306a36Sopenharmony_ci}
984