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) Darryl Miles G7LED (dlm@g7led.demon.co.uk)
762306a36Sopenharmony_ci * Copyright (C) Steven Whitehouse GW7RRM (stevew@acm.org)
862306a36Sopenharmony_ci * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de)
962306a36Sopenharmony_ci * Copyright (C) Hans-Joachim Hetscher DD8NE (dd8ne@bnv-bamberg.de)
1062306a36Sopenharmony_ci * Copyright (C) Hans Alblas PE1AYX (hans@esrac.ele.tue.nl)
1162306a36Sopenharmony_ci * Copyright (C) Frederic Rible F1OAT (frible@teaser.fr)
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci#include <linux/capability.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/errno.h>
1662306a36Sopenharmony_ci#include <linux/types.h>
1762306a36Sopenharmony_ci#include <linux/socket.h>
1862306a36Sopenharmony_ci#include <linux/in.h>
1962306a36Sopenharmony_ci#include <linux/kernel.h>
2062306a36Sopenharmony_ci#include <linux/sched/signal.h>
2162306a36Sopenharmony_ci#include <linux/timer.h>
2262306a36Sopenharmony_ci#include <linux/string.h>
2362306a36Sopenharmony_ci#include <linux/sockios.h>
2462306a36Sopenharmony_ci#include <linux/net.h>
2562306a36Sopenharmony_ci#include <linux/slab.h>
2662306a36Sopenharmony_ci#include <net/ax25.h>
2762306a36Sopenharmony_ci#include <linux/inet.h>
2862306a36Sopenharmony_ci#include <linux/netdevice.h>
2962306a36Sopenharmony_ci#include <linux/if_arp.h>
3062306a36Sopenharmony_ci#include <linux/skbuff.h>
3162306a36Sopenharmony_ci#include <net/sock.h>
3262306a36Sopenharmony_ci#include <linux/uaccess.h>
3362306a36Sopenharmony_ci#include <linux/fcntl.h>
3462306a36Sopenharmony_ci#include <linux/termios.h>	/* For TIOCINQ/OUTQ */
3562306a36Sopenharmony_ci#include <linux/mm.h>
3662306a36Sopenharmony_ci#include <linux/interrupt.h>
3762306a36Sopenharmony_ci#include <linux/notifier.h>
3862306a36Sopenharmony_ci#include <linux/proc_fs.h>
3962306a36Sopenharmony_ci#include <linux/stat.h>
4062306a36Sopenharmony_ci#include <linux/sysctl.h>
4162306a36Sopenharmony_ci#include <linux/init.h>
4262306a36Sopenharmony_ci#include <linux/spinlock.h>
4362306a36Sopenharmony_ci#include <net/net_namespace.h>
4462306a36Sopenharmony_ci#include <net/tcp_states.h>
4562306a36Sopenharmony_ci#include <net/ip.h>
4662306a36Sopenharmony_ci#include <net/arp.h>
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ciHLIST_HEAD(ax25_list);
5162306a36Sopenharmony_ciDEFINE_SPINLOCK(ax25_list_lock);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic const struct proto_ops ax25_proto_ops;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic void ax25_free_sock(struct sock *sk)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	ax25_cb_put(sk_to_ax25(sk));
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci/*
6162306a36Sopenharmony_ci *	Socket removal during an interrupt is now safe.
6262306a36Sopenharmony_ci */
6362306a36Sopenharmony_cistatic void ax25_cb_del(ax25_cb *ax25)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	spin_lock_bh(&ax25_list_lock);
6662306a36Sopenharmony_ci	if (!hlist_unhashed(&ax25->ax25_node)) {
6762306a36Sopenharmony_ci		hlist_del_init(&ax25->ax25_node);
6862306a36Sopenharmony_ci		ax25_cb_put(ax25);
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci	spin_unlock_bh(&ax25_list_lock);
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci/*
7462306a36Sopenharmony_ci *	Kill all bound sockets on a dropped device.
7562306a36Sopenharmony_ci */
7662306a36Sopenharmony_cistatic void ax25_kill_by_device(struct net_device *dev)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	ax25_dev *ax25_dev;
7962306a36Sopenharmony_ci	ax25_cb *s;
8062306a36Sopenharmony_ci	struct sock *sk;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
8362306a36Sopenharmony_ci		return;
8462306a36Sopenharmony_ci	ax25_dev->device_up = false;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	spin_lock_bh(&ax25_list_lock);
8762306a36Sopenharmony_ciagain:
8862306a36Sopenharmony_ci	ax25_for_each(s, &ax25_list) {
8962306a36Sopenharmony_ci		if (s->ax25_dev == ax25_dev) {
9062306a36Sopenharmony_ci			sk = s->sk;
9162306a36Sopenharmony_ci			if (!sk) {
9262306a36Sopenharmony_ci				spin_unlock_bh(&ax25_list_lock);
9362306a36Sopenharmony_ci				ax25_disconnect(s, ENETUNREACH);
9462306a36Sopenharmony_ci				s->ax25_dev = NULL;
9562306a36Sopenharmony_ci				ax25_cb_del(s);
9662306a36Sopenharmony_ci				spin_lock_bh(&ax25_list_lock);
9762306a36Sopenharmony_ci				goto again;
9862306a36Sopenharmony_ci			}
9962306a36Sopenharmony_ci			sock_hold(sk);
10062306a36Sopenharmony_ci			spin_unlock_bh(&ax25_list_lock);
10162306a36Sopenharmony_ci			lock_sock(sk);
10262306a36Sopenharmony_ci			ax25_disconnect(s, ENETUNREACH);
10362306a36Sopenharmony_ci			s->ax25_dev = NULL;
10462306a36Sopenharmony_ci			if (sk->sk_socket) {
10562306a36Sopenharmony_ci				netdev_put(ax25_dev->dev,
10662306a36Sopenharmony_ci					   &ax25_dev->dev_tracker);
10762306a36Sopenharmony_ci				ax25_dev_put(ax25_dev);
10862306a36Sopenharmony_ci			}
10962306a36Sopenharmony_ci			ax25_cb_del(s);
11062306a36Sopenharmony_ci			release_sock(sk);
11162306a36Sopenharmony_ci			spin_lock_bh(&ax25_list_lock);
11262306a36Sopenharmony_ci			sock_put(sk);
11362306a36Sopenharmony_ci			/* The entry could have been deleted from the
11462306a36Sopenharmony_ci			 * list meanwhile and thus the next pointer is
11562306a36Sopenharmony_ci			 * no longer valid.  Play it safe and restart
11662306a36Sopenharmony_ci			 * the scan.  Forward progress is ensured
11762306a36Sopenharmony_ci			 * because we set s->ax25_dev to NULL and we
11862306a36Sopenharmony_ci			 * are never passed a NULL 'dev' argument.
11962306a36Sopenharmony_ci			 */
12062306a36Sopenharmony_ci			goto again;
12162306a36Sopenharmony_ci		}
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci	spin_unlock_bh(&ax25_list_lock);
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci/*
12762306a36Sopenharmony_ci *	Handle device status changes.
12862306a36Sopenharmony_ci */
12962306a36Sopenharmony_cistatic int ax25_device_event(struct notifier_block *this, unsigned long event,
13062306a36Sopenharmony_ci			     void *ptr)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	if (!net_eq(dev_net(dev), &init_net))
13562306a36Sopenharmony_ci		return NOTIFY_DONE;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	/* Reject non AX.25 devices */
13862306a36Sopenharmony_ci	if (dev->type != ARPHRD_AX25)
13962306a36Sopenharmony_ci		return NOTIFY_DONE;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	switch (event) {
14262306a36Sopenharmony_ci	case NETDEV_UP:
14362306a36Sopenharmony_ci		ax25_dev_device_up(dev);
14462306a36Sopenharmony_ci		break;
14562306a36Sopenharmony_ci	case NETDEV_DOWN:
14662306a36Sopenharmony_ci		ax25_kill_by_device(dev);
14762306a36Sopenharmony_ci		ax25_rt_device_down(dev);
14862306a36Sopenharmony_ci		ax25_dev_device_down(dev);
14962306a36Sopenharmony_ci		break;
15062306a36Sopenharmony_ci	default:
15162306a36Sopenharmony_ci		break;
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	return NOTIFY_DONE;
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci/*
15862306a36Sopenharmony_ci *	Add a socket to the bound sockets list.
15962306a36Sopenharmony_ci */
16062306a36Sopenharmony_civoid ax25_cb_add(ax25_cb *ax25)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	spin_lock_bh(&ax25_list_lock);
16362306a36Sopenharmony_ci	ax25_cb_hold(ax25);
16462306a36Sopenharmony_ci	hlist_add_head(&ax25->ax25_node, &ax25_list);
16562306a36Sopenharmony_ci	spin_unlock_bh(&ax25_list_lock);
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci/*
16962306a36Sopenharmony_ci *	Find a socket that wants to accept the SABM we have just
17062306a36Sopenharmony_ci *	received.
17162306a36Sopenharmony_ci */
17262306a36Sopenharmony_cistruct sock *ax25_find_listener(ax25_address *addr, int digi,
17362306a36Sopenharmony_ci	struct net_device *dev, int type)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	ax25_cb *s;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	spin_lock(&ax25_list_lock);
17862306a36Sopenharmony_ci	ax25_for_each(s, &ax25_list) {
17962306a36Sopenharmony_ci		if ((s->iamdigi && !digi) || (!s->iamdigi && digi))
18062306a36Sopenharmony_ci			continue;
18162306a36Sopenharmony_ci		if (s->sk && !ax25cmp(&s->source_addr, addr) &&
18262306a36Sopenharmony_ci		    s->sk->sk_type == type && s->sk->sk_state == TCP_LISTEN) {
18362306a36Sopenharmony_ci			/* If device is null we match any device */
18462306a36Sopenharmony_ci			if (s->ax25_dev == NULL || s->ax25_dev->dev == dev) {
18562306a36Sopenharmony_ci				sock_hold(s->sk);
18662306a36Sopenharmony_ci				spin_unlock(&ax25_list_lock);
18762306a36Sopenharmony_ci				return s->sk;
18862306a36Sopenharmony_ci			}
18962306a36Sopenharmony_ci		}
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ci	spin_unlock(&ax25_list_lock);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	return NULL;
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci/*
19762306a36Sopenharmony_ci *	Find an AX.25 socket given both ends.
19862306a36Sopenharmony_ci */
19962306a36Sopenharmony_cistruct sock *ax25_get_socket(ax25_address *my_addr, ax25_address *dest_addr,
20062306a36Sopenharmony_ci	int type)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	struct sock *sk = NULL;
20362306a36Sopenharmony_ci	ax25_cb *s;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	spin_lock(&ax25_list_lock);
20662306a36Sopenharmony_ci	ax25_for_each(s, &ax25_list) {
20762306a36Sopenharmony_ci		if (s->sk && !ax25cmp(&s->source_addr, my_addr) &&
20862306a36Sopenharmony_ci		    !ax25cmp(&s->dest_addr, dest_addr) &&
20962306a36Sopenharmony_ci		    s->sk->sk_type == type) {
21062306a36Sopenharmony_ci			sk = s->sk;
21162306a36Sopenharmony_ci			sock_hold(sk);
21262306a36Sopenharmony_ci			break;
21362306a36Sopenharmony_ci		}
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	spin_unlock(&ax25_list_lock);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	return sk;
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci/*
22262306a36Sopenharmony_ci *	Find an AX.25 control block given both ends. It will only pick up
22362306a36Sopenharmony_ci *	floating AX.25 control blocks or non Raw socket bound control blocks.
22462306a36Sopenharmony_ci */
22562306a36Sopenharmony_ciax25_cb *ax25_find_cb(const ax25_address *src_addr, ax25_address *dest_addr,
22662306a36Sopenharmony_ci	ax25_digi *digi, struct net_device *dev)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	ax25_cb *s;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	spin_lock_bh(&ax25_list_lock);
23162306a36Sopenharmony_ci	ax25_for_each(s, &ax25_list) {
23262306a36Sopenharmony_ci		if (s->sk && s->sk->sk_type != SOCK_SEQPACKET)
23362306a36Sopenharmony_ci			continue;
23462306a36Sopenharmony_ci		if (s->ax25_dev == NULL)
23562306a36Sopenharmony_ci			continue;
23662306a36Sopenharmony_ci		if (ax25cmp(&s->source_addr, src_addr) == 0 && ax25cmp(&s->dest_addr, dest_addr) == 0 && s->ax25_dev->dev == dev) {
23762306a36Sopenharmony_ci			if (digi != NULL && digi->ndigi != 0) {
23862306a36Sopenharmony_ci				if (s->digipeat == NULL)
23962306a36Sopenharmony_ci					continue;
24062306a36Sopenharmony_ci				if (ax25digicmp(s->digipeat, digi) != 0)
24162306a36Sopenharmony_ci					continue;
24262306a36Sopenharmony_ci			} else {
24362306a36Sopenharmony_ci				if (s->digipeat != NULL && s->digipeat->ndigi != 0)
24462306a36Sopenharmony_ci					continue;
24562306a36Sopenharmony_ci			}
24662306a36Sopenharmony_ci			ax25_cb_hold(s);
24762306a36Sopenharmony_ci			spin_unlock_bh(&ax25_list_lock);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci			return s;
25062306a36Sopenharmony_ci		}
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci	spin_unlock_bh(&ax25_list_lock);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	return NULL;
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ciEXPORT_SYMBOL(ax25_find_cb);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_civoid ax25_send_to_raw(ax25_address *addr, struct sk_buff *skb, int proto)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	ax25_cb *s;
26262306a36Sopenharmony_ci	struct sk_buff *copy;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	spin_lock(&ax25_list_lock);
26562306a36Sopenharmony_ci	ax25_for_each(s, &ax25_list) {
26662306a36Sopenharmony_ci		if (s->sk != NULL && ax25cmp(&s->source_addr, addr) == 0 &&
26762306a36Sopenharmony_ci		    s->sk->sk_type == SOCK_RAW &&
26862306a36Sopenharmony_ci		    s->sk->sk_protocol == proto &&
26962306a36Sopenharmony_ci		    s->ax25_dev->dev == skb->dev &&
27062306a36Sopenharmony_ci		    atomic_read(&s->sk->sk_rmem_alloc) <= s->sk->sk_rcvbuf) {
27162306a36Sopenharmony_ci			if ((copy = skb_clone(skb, GFP_ATOMIC)) == NULL)
27262306a36Sopenharmony_ci				continue;
27362306a36Sopenharmony_ci			if (sock_queue_rcv_skb(s->sk, copy) != 0)
27462306a36Sopenharmony_ci				kfree_skb(copy);
27562306a36Sopenharmony_ci		}
27662306a36Sopenharmony_ci	}
27762306a36Sopenharmony_ci	spin_unlock(&ax25_list_lock);
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci/*
28162306a36Sopenharmony_ci *	Deferred destroy.
28262306a36Sopenharmony_ci */
28362306a36Sopenharmony_civoid ax25_destroy_socket(ax25_cb *);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci/*
28662306a36Sopenharmony_ci *	Handler for deferred kills.
28762306a36Sopenharmony_ci */
28862306a36Sopenharmony_cistatic void ax25_destroy_timer(struct timer_list *t)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	ax25_cb *ax25 = from_timer(ax25, t, dtimer);
29162306a36Sopenharmony_ci	struct sock *sk;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	sk=ax25->sk;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	bh_lock_sock(sk);
29662306a36Sopenharmony_ci	sock_hold(sk);
29762306a36Sopenharmony_ci	ax25_destroy_socket(ax25);
29862306a36Sopenharmony_ci	bh_unlock_sock(sk);
29962306a36Sopenharmony_ci	sock_put(sk);
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci/*
30362306a36Sopenharmony_ci *	This is called from user mode and the timers. Thus it protects itself
30462306a36Sopenharmony_ci *	against interrupt users but doesn't worry about being called during
30562306a36Sopenharmony_ci *	work. Once it is removed from the queue no interrupt or bottom half
30662306a36Sopenharmony_ci *	will touch it and we are (fairly 8-) ) safe.
30762306a36Sopenharmony_ci */
30862306a36Sopenharmony_civoid ax25_destroy_socket(ax25_cb *ax25)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	struct sk_buff *skb;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	ax25_cb_del(ax25);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	ax25_stop_heartbeat(ax25);
31562306a36Sopenharmony_ci	ax25_stop_t1timer(ax25);
31662306a36Sopenharmony_ci	ax25_stop_t2timer(ax25);
31762306a36Sopenharmony_ci	ax25_stop_t3timer(ax25);
31862306a36Sopenharmony_ci	ax25_stop_idletimer(ax25);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	ax25_clear_queues(ax25);	/* Flush the queues */
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	if (ax25->sk != NULL) {
32362306a36Sopenharmony_ci		while ((skb = skb_dequeue(&ax25->sk->sk_receive_queue)) != NULL) {
32462306a36Sopenharmony_ci			if (skb->sk != ax25->sk) {
32562306a36Sopenharmony_ci				/* A pending connection */
32662306a36Sopenharmony_ci				ax25_cb *sax25 = sk_to_ax25(skb->sk);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci				/* Queue the unaccepted socket for death */
32962306a36Sopenharmony_ci				sock_orphan(skb->sk);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci				/* 9A4GL: hack to release unaccepted sockets */
33262306a36Sopenharmony_ci				skb->sk->sk_state = TCP_LISTEN;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci				ax25_start_heartbeat(sax25);
33562306a36Sopenharmony_ci				sax25->state = AX25_STATE_0;
33662306a36Sopenharmony_ci			}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci			kfree_skb(skb);
33962306a36Sopenharmony_ci		}
34062306a36Sopenharmony_ci		skb_queue_purge(&ax25->sk->sk_write_queue);
34162306a36Sopenharmony_ci	}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	if (ax25->sk != NULL) {
34462306a36Sopenharmony_ci		if (sk_has_allocations(ax25->sk)) {
34562306a36Sopenharmony_ci			/* Defer: outstanding buffers */
34662306a36Sopenharmony_ci			timer_setup(&ax25->dtimer, ax25_destroy_timer, 0);
34762306a36Sopenharmony_ci			ax25->dtimer.expires  = jiffies + 2 * HZ;
34862306a36Sopenharmony_ci			add_timer(&ax25->dtimer);
34962306a36Sopenharmony_ci		} else {
35062306a36Sopenharmony_ci			struct sock *sk=ax25->sk;
35162306a36Sopenharmony_ci			ax25->sk=NULL;
35262306a36Sopenharmony_ci			sock_put(sk);
35362306a36Sopenharmony_ci		}
35462306a36Sopenharmony_ci	} else {
35562306a36Sopenharmony_ci		ax25_cb_put(ax25);
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci/*
36062306a36Sopenharmony_ci * dl1bke 960311: set parameters for existing AX.25 connections,
36162306a36Sopenharmony_ci *		  includes a KILL command to abort any connection.
36262306a36Sopenharmony_ci *		  VERY useful for debugging ;-)
36362306a36Sopenharmony_ci */
36462306a36Sopenharmony_cistatic int ax25_ctl_ioctl(const unsigned int cmd, void __user *arg)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	struct ax25_ctl_struct ax25_ctl;
36762306a36Sopenharmony_ci	ax25_digi digi;
36862306a36Sopenharmony_ci	ax25_dev *ax25_dev;
36962306a36Sopenharmony_ci	ax25_cb *ax25;
37062306a36Sopenharmony_ci	unsigned int k;
37162306a36Sopenharmony_ci	int ret = 0;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	if (copy_from_user(&ax25_ctl, arg, sizeof(ax25_ctl)))
37462306a36Sopenharmony_ci		return -EFAULT;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	if (ax25_ctl.digi_count > AX25_MAX_DIGIS)
37762306a36Sopenharmony_ci		return -EINVAL;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	if (ax25_ctl.arg > ULONG_MAX / HZ && ax25_ctl.cmd != AX25_KILL)
38062306a36Sopenharmony_ci		return -EINVAL;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	ax25_dev = ax25_addr_ax25dev(&ax25_ctl.port_addr);
38362306a36Sopenharmony_ci	if (!ax25_dev)
38462306a36Sopenharmony_ci		return -ENODEV;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	digi.ndigi = ax25_ctl.digi_count;
38762306a36Sopenharmony_ci	for (k = 0; k < digi.ndigi; k++)
38862306a36Sopenharmony_ci		digi.calls[k] = ax25_ctl.digi_addr[k];
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	ax25 = ax25_find_cb(&ax25_ctl.source_addr, &ax25_ctl.dest_addr, &digi, ax25_dev->dev);
39162306a36Sopenharmony_ci	if (!ax25) {
39262306a36Sopenharmony_ci		ax25_dev_put(ax25_dev);
39362306a36Sopenharmony_ci		return -ENOTCONN;
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	switch (ax25_ctl.cmd) {
39762306a36Sopenharmony_ci	case AX25_KILL:
39862306a36Sopenharmony_ci		ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
39962306a36Sopenharmony_ci#ifdef CONFIG_AX25_DAMA_SLAVE
40062306a36Sopenharmony_ci		if (ax25_dev->dama.slave && ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_DAMA_SLAVE)
40162306a36Sopenharmony_ci			ax25_dama_off(ax25);
40262306a36Sopenharmony_ci#endif
40362306a36Sopenharmony_ci		ax25_disconnect(ax25, ENETRESET);
40462306a36Sopenharmony_ci		break;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	case AX25_WINDOW:
40762306a36Sopenharmony_ci		if (ax25->modulus == AX25_MODULUS) {
40862306a36Sopenharmony_ci			if (ax25_ctl.arg < 1 || ax25_ctl.arg > 7)
40962306a36Sopenharmony_ci				goto einval_put;
41062306a36Sopenharmony_ci		} else {
41162306a36Sopenharmony_ci			if (ax25_ctl.arg < 1 || ax25_ctl.arg > 63)
41262306a36Sopenharmony_ci				goto einval_put;
41362306a36Sopenharmony_ci		}
41462306a36Sopenharmony_ci		ax25->window = ax25_ctl.arg;
41562306a36Sopenharmony_ci		break;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	case AX25_T1:
41862306a36Sopenharmony_ci		if (ax25_ctl.arg < 1 || ax25_ctl.arg > ULONG_MAX / HZ)
41962306a36Sopenharmony_ci			goto einval_put;
42062306a36Sopenharmony_ci		ax25->rtt = (ax25_ctl.arg * HZ) / 2;
42162306a36Sopenharmony_ci		ax25->t1  = ax25_ctl.arg * HZ;
42262306a36Sopenharmony_ci		break;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	case AX25_T2:
42562306a36Sopenharmony_ci		if (ax25_ctl.arg < 1 || ax25_ctl.arg > ULONG_MAX / HZ)
42662306a36Sopenharmony_ci			goto einval_put;
42762306a36Sopenharmony_ci		ax25->t2 = ax25_ctl.arg * HZ;
42862306a36Sopenharmony_ci		break;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	case AX25_N2:
43162306a36Sopenharmony_ci		if (ax25_ctl.arg < 1 || ax25_ctl.arg > 31)
43262306a36Sopenharmony_ci			goto einval_put;
43362306a36Sopenharmony_ci		ax25->n2count = 0;
43462306a36Sopenharmony_ci		ax25->n2 = ax25_ctl.arg;
43562306a36Sopenharmony_ci		break;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	case AX25_T3:
43862306a36Sopenharmony_ci		if (ax25_ctl.arg > ULONG_MAX / HZ)
43962306a36Sopenharmony_ci			goto einval_put;
44062306a36Sopenharmony_ci		ax25->t3 = ax25_ctl.arg * HZ;
44162306a36Sopenharmony_ci		break;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	case AX25_IDLE:
44462306a36Sopenharmony_ci		if (ax25_ctl.arg > ULONG_MAX / (60 * HZ))
44562306a36Sopenharmony_ci			goto einval_put;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci		ax25->idle = ax25_ctl.arg * 60 * HZ;
44862306a36Sopenharmony_ci		break;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	case AX25_PACLEN:
45162306a36Sopenharmony_ci		if (ax25_ctl.arg < 16 || ax25_ctl.arg > 65535)
45262306a36Sopenharmony_ci			goto einval_put;
45362306a36Sopenharmony_ci		ax25->paclen = ax25_ctl.arg;
45462306a36Sopenharmony_ci		break;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	default:
45762306a36Sopenharmony_ci		goto einval_put;
45862306a36Sopenharmony_ci	  }
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ciout_put:
46162306a36Sopenharmony_ci	ax25_dev_put(ax25_dev);
46262306a36Sopenharmony_ci	ax25_cb_put(ax25);
46362306a36Sopenharmony_ci	return ret;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_cieinval_put:
46662306a36Sopenharmony_ci	ret = -EINVAL;
46762306a36Sopenharmony_ci	goto out_put;
46862306a36Sopenharmony_ci}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_cistatic void ax25_fillin_cb_from_dev(ax25_cb *ax25, ax25_dev *ax25_dev)
47162306a36Sopenharmony_ci{
47262306a36Sopenharmony_ci	ax25->rtt     = msecs_to_jiffies(ax25_dev->values[AX25_VALUES_T1]) / 2;
47362306a36Sopenharmony_ci	ax25->t1      = msecs_to_jiffies(ax25_dev->values[AX25_VALUES_T1]);
47462306a36Sopenharmony_ci	ax25->t2      = msecs_to_jiffies(ax25_dev->values[AX25_VALUES_T2]);
47562306a36Sopenharmony_ci	ax25->t3      = msecs_to_jiffies(ax25_dev->values[AX25_VALUES_T3]);
47662306a36Sopenharmony_ci	ax25->n2      = ax25_dev->values[AX25_VALUES_N2];
47762306a36Sopenharmony_ci	ax25->paclen  = ax25_dev->values[AX25_VALUES_PACLEN];
47862306a36Sopenharmony_ci	ax25->idle    = msecs_to_jiffies(ax25_dev->values[AX25_VALUES_IDLE]);
47962306a36Sopenharmony_ci	ax25->backoff = ax25_dev->values[AX25_VALUES_BACKOFF];
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	if (ax25_dev->values[AX25_VALUES_AXDEFMODE]) {
48262306a36Sopenharmony_ci		ax25->modulus = AX25_EMODULUS;
48362306a36Sopenharmony_ci		ax25->window  = ax25_dev->values[AX25_VALUES_EWINDOW];
48462306a36Sopenharmony_ci	} else {
48562306a36Sopenharmony_ci		ax25->modulus = AX25_MODULUS;
48662306a36Sopenharmony_ci		ax25->window  = ax25_dev->values[AX25_VALUES_WINDOW];
48762306a36Sopenharmony_ci	}
48862306a36Sopenharmony_ci}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci/*
49162306a36Sopenharmony_ci *	Fill in a created AX.25 created control block with the default
49262306a36Sopenharmony_ci *	values for a particular device.
49362306a36Sopenharmony_ci */
49462306a36Sopenharmony_civoid ax25_fillin_cb(ax25_cb *ax25, ax25_dev *ax25_dev)
49562306a36Sopenharmony_ci{
49662306a36Sopenharmony_ci	ax25->ax25_dev = ax25_dev;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	if (ax25->ax25_dev != NULL) {
49962306a36Sopenharmony_ci		ax25_fillin_cb_from_dev(ax25, ax25_dev);
50062306a36Sopenharmony_ci		return;
50162306a36Sopenharmony_ci	}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	/*
50462306a36Sopenharmony_ci	 * No device, use kernel / AX.25 spec default values
50562306a36Sopenharmony_ci	 */
50662306a36Sopenharmony_ci	ax25->rtt     = msecs_to_jiffies(AX25_DEF_T1) / 2;
50762306a36Sopenharmony_ci	ax25->t1      = msecs_to_jiffies(AX25_DEF_T1);
50862306a36Sopenharmony_ci	ax25->t2      = msecs_to_jiffies(AX25_DEF_T2);
50962306a36Sopenharmony_ci	ax25->t3      = msecs_to_jiffies(AX25_DEF_T3);
51062306a36Sopenharmony_ci	ax25->n2      = AX25_DEF_N2;
51162306a36Sopenharmony_ci	ax25->paclen  = AX25_DEF_PACLEN;
51262306a36Sopenharmony_ci	ax25->idle    = msecs_to_jiffies(AX25_DEF_IDLE);
51362306a36Sopenharmony_ci	ax25->backoff = AX25_DEF_BACKOFF;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	if (AX25_DEF_AXDEFMODE) {
51662306a36Sopenharmony_ci		ax25->modulus = AX25_EMODULUS;
51762306a36Sopenharmony_ci		ax25->window  = AX25_DEF_EWINDOW;
51862306a36Sopenharmony_ci	} else {
51962306a36Sopenharmony_ci		ax25->modulus = AX25_MODULUS;
52062306a36Sopenharmony_ci		ax25->window  = AX25_DEF_WINDOW;
52162306a36Sopenharmony_ci	}
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci/*
52562306a36Sopenharmony_ci * Create an empty AX.25 control block.
52662306a36Sopenharmony_ci */
52762306a36Sopenharmony_ciax25_cb *ax25_create_cb(void)
52862306a36Sopenharmony_ci{
52962306a36Sopenharmony_ci	ax25_cb *ax25;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	if ((ax25 = kzalloc(sizeof(*ax25), GFP_ATOMIC)) == NULL)
53262306a36Sopenharmony_ci		return NULL;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	refcount_set(&ax25->refcount, 1);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	skb_queue_head_init(&ax25->write_queue);
53762306a36Sopenharmony_ci	skb_queue_head_init(&ax25->frag_queue);
53862306a36Sopenharmony_ci	skb_queue_head_init(&ax25->ack_queue);
53962306a36Sopenharmony_ci	skb_queue_head_init(&ax25->reseq_queue);
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	ax25_setup_timers(ax25);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	ax25_fillin_cb(ax25, NULL);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	ax25->state = AX25_STATE_0;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	return ax25;
54862306a36Sopenharmony_ci}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci/*
55162306a36Sopenharmony_ci *	Handling for system calls applied via the various interfaces to an
55262306a36Sopenharmony_ci *	AX25 socket object
55362306a36Sopenharmony_ci */
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_cistatic int ax25_setsockopt(struct socket *sock, int level, int optname,
55662306a36Sopenharmony_ci		sockptr_t optval, unsigned int optlen)
55762306a36Sopenharmony_ci{
55862306a36Sopenharmony_ci	struct sock *sk = sock->sk;
55962306a36Sopenharmony_ci	ax25_cb *ax25;
56062306a36Sopenharmony_ci	struct net_device *dev;
56162306a36Sopenharmony_ci	char devname[IFNAMSIZ];
56262306a36Sopenharmony_ci	unsigned int opt;
56362306a36Sopenharmony_ci	int res = 0;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	if (level != SOL_AX25)
56662306a36Sopenharmony_ci		return -ENOPROTOOPT;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	if (optlen < sizeof(unsigned int))
56962306a36Sopenharmony_ci		return -EINVAL;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	if (copy_from_sockptr(&opt, optval, sizeof(unsigned int)))
57262306a36Sopenharmony_ci		return -EFAULT;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	lock_sock(sk);
57562306a36Sopenharmony_ci	ax25 = sk_to_ax25(sk);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	switch (optname) {
57862306a36Sopenharmony_ci	case AX25_WINDOW:
57962306a36Sopenharmony_ci		if (ax25->modulus == AX25_MODULUS) {
58062306a36Sopenharmony_ci			if (opt < 1 || opt > 7) {
58162306a36Sopenharmony_ci				res = -EINVAL;
58262306a36Sopenharmony_ci				break;
58362306a36Sopenharmony_ci			}
58462306a36Sopenharmony_ci		} else {
58562306a36Sopenharmony_ci			if (opt < 1 || opt > 63) {
58662306a36Sopenharmony_ci				res = -EINVAL;
58762306a36Sopenharmony_ci				break;
58862306a36Sopenharmony_ci			}
58962306a36Sopenharmony_ci		}
59062306a36Sopenharmony_ci		ax25->window = opt;
59162306a36Sopenharmony_ci		break;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	case AX25_T1:
59462306a36Sopenharmony_ci		if (opt < 1 || opt > UINT_MAX / HZ) {
59562306a36Sopenharmony_ci			res = -EINVAL;
59662306a36Sopenharmony_ci			break;
59762306a36Sopenharmony_ci		}
59862306a36Sopenharmony_ci		ax25->rtt = (opt * HZ) >> 1;
59962306a36Sopenharmony_ci		ax25->t1  = opt * HZ;
60062306a36Sopenharmony_ci		break;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	case AX25_T2:
60362306a36Sopenharmony_ci		if (opt < 1 || opt > UINT_MAX / HZ) {
60462306a36Sopenharmony_ci			res = -EINVAL;
60562306a36Sopenharmony_ci			break;
60662306a36Sopenharmony_ci		}
60762306a36Sopenharmony_ci		ax25->t2 = opt * HZ;
60862306a36Sopenharmony_ci		break;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	case AX25_N2:
61162306a36Sopenharmony_ci		if (opt < 1 || opt > 31) {
61262306a36Sopenharmony_ci			res = -EINVAL;
61362306a36Sopenharmony_ci			break;
61462306a36Sopenharmony_ci		}
61562306a36Sopenharmony_ci		ax25->n2 = opt;
61662306a36Sopenharmony_ci		break;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	case AX25_T3:
61962306a36Sopenharmony_ci		if (opt < 1 || opt > UINT_MAX / HZ) {
62062306a36Sopenharmony_ci			res = -EINVAL;
62162306a36Sopenharmony_ci			break;
62262306a36Sopenharmony_ci		}
62362306a36Sopenharmony_ci		ax25->t3 = opt * HZ;
62462306a36Sopenharmony_ci		break;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	case AX25_IDLE:
62762306a36Sopenharmony_ci		if (opt > UINT_MAX / (60 * HZ)) {
62862306a36Sopenharmony_ci			res = -EINVAL;
62962306a36Sopenharmony_ci			break;
63062306a36Sopenharmony_ci		}
63162306a36Sopenharmony_ci		ax25->idle = opt * 60 * HZ;
63262306a36Sopenharmony_ci		break;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	case AX25_BACKOFF:
63562306a36Sopenharmony_ci		if (opt > 2) {
63662306a36Sopenharmony_ci			res = -EINVAL;
63762306a36Sopenharmony_ci			break;
63862306a36Sopenharmony_ci		}
63962306a36Sopenharmony_ci		ax25->backoff = opt;
64062306a36Sopenharmony_ci		break;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	case AX25_EXTSEQ:
64362306a36Sopenharmony_ci		ax25->modulus = opt ? AX25_EMODULUS : AX25_MODULUS;
64462306a36Sopenharmony_ci		break;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	case AX25_PIDINCL:
64762306a36Sopenharmony_ci		ax25->pidincl = opt ? 1 : 0;
64862306a36Sopenharmony_ci		break;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	case AX25_IAMDIGI:
65162306a36Sopenharmony_ci		ax25->iamdigi = opt ? 1 : 0;
65262306a36Sopenharmony_ci		break;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	case AX25_PACLEN:
65562306a36Sopenharmony_ci		if (opt < 16 || opt > 65535) {
65662306a36Sopenharmony_ci			res = -EINVAL;
65762306a36Sopenharmony_ci			break;
65862306a36Sopenharmony_ci		}
65962306a36Sopenharmony_ci		ax25->paclen = opt;
66062306a36Sopenharmony_ci		break;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	case SO_BINDTODEVICE:
66362306a36Sopenharmony_ci		if (optlen > IFNAMSIZ - 1)
66462306a36Sopenharmony_ci			optlen = IFNAMSIZ - 1;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci		memset(devname, 0, sizeof(devname));
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci		if (copy_from_sockptr(devname, optval, optlen)) {
66962306a36Sopenharmony_ci			res = -EFAULT;
67062306a36Sopenharmony_ci			break;
67162306a36Sopenharmony_ci		}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci		if (sk->sk_type == SOCK_SEQPACKET &&
67462306a36Sopenharmony_ci		   (sock->state != SS_UNCONNECTED ||
67562306a36Sopenharmony_ci		    sk->sk_state == TCP_LISTEN)) {
67662306a36Sopenharmony_ci			res = -EADDRNOTAVAIL;
67762306a36Sopenharmony_ci			break;
67862306a36Sopenharmony_ci		}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci		rtnl_lock();
68162306a36Sopenharmony_ci		dev = __dev_get_by_name(&init_net, devname);
68262306a36Sopenharmony_ci		if (!dev) {
68362306a36Sopenharmony_ci			rtnl_unlock();
68462306a36Sopenharmony_ci			res = -ENODEV;
68562306a36Sopenharmony_ci			break;
68662306a36Sopenharmony_ci		}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci		ax25->ax25_dev = ax25_dev_ax25dev(dev);
68962306a36Sopenharmony_ci		if (!ax25->ax25_dev) {
69062306a36Sopenharmony_ci			rtnl_unlock();
69162306a36Sopenharmony_ci			res = -ENODEV;
69262306a36Sopenharmony_ci			break;
69362306a36Sopenharmony_ci		}
69462306a36Sopenharmony_ci		ax25_fillin_cb(ax25, ax25->ax25_dev);
69562306a36Sopenharmony_ci		rtnl_unlock();
69662306a36Sopenharmony_ci		break;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	default:
69962306a36Sopenharmony_ci		res = -ENOPROTOOPT;
70062306a36Sopenharmony_ci	}
70162306a36Sopenharmony_ci	release_sock(sk);
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	return res;
70462306a36Sopenharmony_ci}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_cistatic int ax25_getsockopt(struct socket *sock, int level, int optname,
70762306a36Sopenharmony_ci	char __user *optval, int __user *optlen)
70862306a36Sopenharmony_ci{
70962306a36Sopenharmony_ci	struct sock *sk = sock->sk;
71062306a36Sopenharmony_ci	ax25_cb *ax25;
71162306a36Sopenharmony_ci	struct ax25_dev *ax25_dev;
71262306a36Sopenharmony_ci	char devname[IFNAMSIZ];
71362306a36Sopenharmony_ci	void *valptr;
71462306a36Sopenharmony_ci	int val = 0;
71562306a36Sopenharmony_ci	int maxlen, length;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	if (level != SOL_AX25)
71862306a36Sopenharmony_ci		return -ENOPROTOOPT;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	if (get_user(maxlen, optlen))
72162306a36Sopenharmony_ci		return -EFAULT;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	if (maxlen < 1)
72462306a36Sopenharmony_ci		return -EFAULT;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	valptr = &val;
72762306a36Sopenharmony_ci	length = min_t(unsigned int, maxlen, sizeof(int));
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	lock_sock(sk);
73062306a36Sopenharmony_ci	ax25 = sk_to_ax25(sk);
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	switch (optname) {
73362306a36Sopenharmony_ci	case AX25_WINDOW:
73462306a36Sopenharmony_ci		val = ax25->window;
73562306a36Sopenharmony_ci		break;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	case AX25_T1:
73862306a36Sopenharmony_ci		val = ax25->t1 / HZ;
73962306a36Sopenharmony_ci		break;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	case AX25_T2:
74262306a36Sopenharmony_ci		val = ax25->t2 / HZ;
74362306a36Sopenharmony_ci		break;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	case AX25_N2:
74662306a36Sopenharmony_ci		val = ax25->n2;
74762306a36Sopenharmony_ci		break;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	case AX25_T3:
75062306a36Sopenharmony_ci		val = ax25->t3 / HZ;
75162306a36Sopenharmony_ci		break;
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	case AX25_IDLE:
75462306a36Sopenharmony_ci		val = ax25->idle / (60 * HZ);
75562306a36Sopenharmony_ci		break;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	case AX25_BACKOFF:
75862306a36Sopenharmony_ci		val = ax25->backoff;
75962306a36Sopenharmony_ci		break;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	case AX25_EXTSEQ:
76262306a36Sopenharmony_ci		val = (ax25->modulus == AX25_EMODULUS);
76362306a36Sopenharmony_ci		break;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	case AX25_PIDINCL:
76662306a36Sopenharmony_ci		val = ax25->pidincl;
76762306a36Sopenharmony_ci		break;
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	case AX25_IAMDIGI:
77062306a36Sopenharmony_ci		val = ax25->iamdigi;
77162306a36Sopenharmony_ci		break;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	case AX25_PACLEN:
77462306a36Sopenharmony_ci		val = ax25->paclen;
77562306a36Sopenharmony_ci		break;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	case SO_BINDTODEVICE:
77862306a36Sopenharmony_ci		ax25_dev = ax25->ax25_dev;
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci		if (ax25_dev != NULL && ax25_dev->dev != NULL) {
78162306a36Sopenharmony_ci			strscpy(devname, ax25_dev->dev->name, sizeof(devname));
78262306a36Sopenharmony_ci			length = strlen(devname) + 1;
78362306a36Sopenharmony_ci		} else {
78462306a36Sopenharmony_ci			*devname = '\0';
78562306a36Sopenharmony_ci			length = 1;
78662306a36Sopenharmony_ci		}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci		valptr = devname;
78962306a36Sopenharmony_ci		break;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	default:
79262306a36Sopenharmony_ci		release_sock(sk);
79362306a36Sopenharmony_ci		return -ENOPROTOOPT;
79462306a36Sopenharmony_ci	}
79562306a36Sopenharmony_ci	release_sock(sk);
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	if (put_user(length, optlen))
79862306a36Sopenharmony_ci		return -EFAULT;
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	return copy_to_user(optval, valptr, length) ? -EFAULT : 0;
80162306a36Sopenharmony_ci}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_cistatic int ax25_listen(struct socket *sock, int backlog)
80462306a36Sopenharmony_ci{
80562306a36Sopenharmony_ci	struct sock *sk = sock->sk;
80662306a36Sopenharmony_ci	int res = 0;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	lock_sock(sk);
80962306a36Sopenharmony_ci	if (sk->sk_type == SOCK_SEQPACKET && sk->sk_state != TCP_LISTEN) {
81062306a36Sopenharmony_ci		sk->sk_max_ack_backlog = backlog;
81162306a36Sopenharmony_ci		sk->sk_state           = TCP_LISTEN;
81262306a36Sopenharmony_ci		goto out;
81362306a36Sopenharmony_ci	}
81462306a36Sopenharmony_ci	res = -EOPNOTSUPP;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ciout:
81762306a36Sopenharmony_ci	release_sock(sk);
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	return res;
82062306a36Sopenharmony_ci}
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci/*
82362306a36Sopenharmony_ci * XXX: when creating ax25_sock we should update the .obj_size setting
82462306a36Sopenharmony_ci * below.
82562306a36Sopenharmony_ci */
82662306a36Sopenharmony_cistatic struct proto ax25_proto = {
82762306a36Sopenharmony_ci	.name	  = "AX25",
82862306a36Sopenharmony_ci	.owner	  = THIS_MODULE,
82962306a36Sopenharmony_ci	.obj_size = sizeof(struct ax25_sock),
83062306a36Sopenharmony_ci};
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_cistatic int ax25_create(struct net *net, struct socket *sock, int protocol,
83362306a36Sopenharmony_ci		       int kern)
83462306a36Sopenharmony_ci{
83562306a36Sopenharmony_ci	struct sock *sk;
83662306a36Sopenharmony_ci	ax25_cb *ax25;
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	if (protocol < 0 || protocol > U8_MAX)
83962306a36Sopenharmony_ci		return -EINVAL;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	if (!net_eq(net, &init_net))
84262306a36Sopenharmony_ci		return -EAFNOSUPPORT;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	switch (sock->type) {
84562306a36Sopenharmony_ci	case SOCK_DGRAM:
84662306a36Sopenharmony_ci		if (protocol == 0 || protocol == PF_AX25)
84762306a36Sopenharmony_ci			protocol = AX25_P_TEXT;
84862306a36Sopenharmony_ci		break;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	case SOCK_SEQPACKET:
85162306a36Sopenharmony_ci		switch (protocol) {
85262306a36Sopenharmony_ci		case 0:
85362306a36Sopenharmony_ci		case PF_AX25:	/* For CLX */
85462306a36Sopenharmony_ci			protocol = AX25_P_TEXT;
85562306a36Sopenharmony_ci			break;
85662306a36Sopenharmony_ci		case AX25_P_SEGMENT:
85762306a36Sopenharmony_ci#ifdef CONFIG_INET
85862306a36Sopenharmony_ci		case AX25_P_ARP:
85962306a36Sopenharmony_ci		case AX25_P_IP:
86062306a36Sopenharmony_ci#endif
86162306a36Sopenharmony_ci#ifdef CONFIG_NETROM
86262306a36Sopenharmony_ci		case AX25_P_NETROM:
86362306a36Sopenharmony_ci#endif
86462306a36Sopenharmony_ci#ifdef CONFIG_ROSE
86562306a36Sopenharmony_ci		case AX25_P_ROSE:
86662306a36Sopenharmony_ci#endif
86762306a36Sopenharmony_ci			return -ESOCKTNOSUPPORT;
86862306a36Sopenharmony_ci#ifdef CONFIG_NETROM_MODULE
86962306a36Sopenharmony_ci		case AX25_P_NETROM:
87062306a36Sopenharmony_ci			if (ax25_protocol_is_registered(AX25_P_NETROM))
87162306a36Sopenharmony_ci				return -ESOCKTNOSUPPORT;
87262306a36Sopenharmony_ci			break;
87362306a36Sopenharmony_ci#endif
87462306a36Sopenharmony_ci#ifdef CONFIG_ROSE_MODULE
87562306a36Sopenharmony_ci		case AX25_P_ROSE:
87662306a36Sopenharmony_ci			if (ax25_protocol_is_registered(AX25_P_ROSE))
87762306a36Sopenharmony_ci				return -ESOCKTNOSUPPORT;
87862306a36Sopenharmony_ci			break;
87962306a36Sopenharmony_ci#endif
88062306a36Sopenharmony_ci		default:
88162306a36Sopenharmony_ci			break;
88262306a36Sopenharmony_ci		}
88362306a36Sopenharmony_ci		break;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	case SOCK_RAW:
88662306a36Sopenharmony_ci		if (!capable(CAP_NET_RAW))
88762306a36Sopenharmony_ci			return -EPERM;
88862306a36Sopenharmony_ci		break;
88962306a36Sopenharmony_ci	default:
89062306a36Sopenharmony_ci		return -ESOCKTNOSUPPORT;
89162306a36Sopenharmony_ci	}
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	sk = sk_alloc(net, PF_AX25, GFP_ATOMIC, &ax25_proto, kern);
89462306a36Sopenharmony_ci	if (sk == NULL)
89562306a36Sopenharmony_ci		return -ENOMEM;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	ax25 = ax25_sk(sk)->cb = ax25_create_cb();
89862306a36Sopenharmony_ci	if (!ax25) {
89962306a36Sopenharmony_ci		sk_free(sk);
90062306a36Sopenharmony_ci		return -ENOMEM;
90162306a36Sopenharmony_ci	}
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	sock_init_data(sock, sk);
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	sk->sk_destruct = ax25_free_sock;
90662306a36Sopenharmony_ci	sock->ops    = &ax25_proto_ops;
90762306a36Sopenharmony_ci	sk->sk_protocol = protocol;
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	ax25->sk    = sk;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	return 0;
91262306a36Sopenharmony_ci}
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_cistruct sock *ax25_make_new(struct sock *osk, struct ax25_dev *ax25_dev)
91562306a36Sopenharmony_ci{
91662306a36Sopenharmony_ci	struct sock *sk;
91762306a36Sopenharmony_ci	ax25_cb *ax25, *oax25;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	sk = sk_alloc(sock_net(osk), PF_AX25, GFP_ATOMIC, osk->sk_prot, 0);
92062306a36Sopenharmony_ci	if (sk == NULL)
92162306a36Sopenharmony_ci		return NULL;
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	if ((ax25 = ax25_create_cb()) == NULL) {
92462306a36Sopenharmony_ci		sk_free(sk);
92562306a36Sopenharmony_ci		return NULL;
92662306a36Sopenharmony_ci	}
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	switch (osk->sk_type) {
92962306a36Sopenharmony_ci	case SOCK_DGRAM:
93062306a36Sopenharmony_ci		break;
93162306a36Sopenharmony_ci	case SOCK_SEQPACKET:
93262306a36Sopenharmony_ci		break;
93362306a36Sopenharmony_ci	default:
93462306a36Sopenharmony_ci		sk_free(sk);
93562306a36Sopenharmony_ci		ax25_cb_put(ax25);
93662306a36Sopenharmony_ci		return NULL;
93762306a36Sopenharmony_ci	}
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	sock_init_data(NULL, sk);
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	sk->sk_type     = osk->sk_type;
94262306a36Sopenharmony_ci	sk->sk_priority = osk->sk_priority;
94362306a36Sopenharmony_ci	sk->sk_protocol = osk->sk_protocol;
94462306a36Sopenharmony_ci	sk->sk_rcvbuf   = osk->sk_rcvbuf;
94562306a36Sopenharmony_ci	sk->sk_sndbuf   = osk->sk_sndbuf;
94662306a36Sopenharmony_ci	sk->sk_state    = TCP_ESTABLISHED;
94762306a36Sopenharmony_ci	sock_copy_flags(sk, osk);
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	oax25 = sk_to_ax25(osk);
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	ax25->modulus = oax25->modulus;
95262306a36Sopenharmony_ci	ax25->backoff = oax25->backoff;
95362306a36Sopenharmony_ci	ax25->pidincl = oax25->pidincl;
95462306a36Sopenharmony_ci	ax25->iamdigi = oax25->iamdigi;
95562306a36Sopenharmony_ci	ax25->rtt     = oax25->rtt;
95662306a36Sopenharmony_ci	ax25->t1      = oax25->t1;
95762306a36Sopenharmony_ci	ax25->t2      = oax25->t2;
95862306a36Sopenharmony_ci	ax25->t3      = oax25->t3;
95962306a36Sopenharmony_ci	ax25->n2      = oax25->n2;
96062306a36Sopenharmony_ci	ax25->idle    = oax25->idle;
96162306a36Sopenharmony_ci	ax25->paclen  = oax25->paclen;
96262306a36Sopenharmony_ci	ax25->window  = oax25->window;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	ax25->ax25_dev    = ax25_dev;
96562306a36Sopenharmony_ci	ax25->source_addr = oax25->source_addr;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	if (oax25->digipeat != NULL) {
96862306a36Sopenharmony_ci		ax25->digipeat = kmemdup(oax25->digipeat, sizeof(ax25_digi),
96962306a36Sopenharmony_ci					 GFP_ATOMIC);
97062306a36Sopenharmony_ci		if (ax25->digipeat == NULL) {
97162306a36Sopenharmony_ci			sk_free(sk);
97262306a36Sopenharmony_ci			ax25_cb_put(ax25);
97362306a36Sopenharmony_ci			return NULL;
97462306a36Sopenharmony_ci		}
97562306a36Sopenharmony_ci	}
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	ax25_sk(sk)->cb = ax25;
97862306a36Sopenharmony_ci	sk->sk_destruct = ax25_free_sock;
97962306a36Sopenharmony_ci	ax25->sk    = sk;
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	return sk;
98262306a36Sopenharmony_ci}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_cistatic int ax25_release(struct socket *sock)
98562306a36Sopenharmony_ci{
98662306a36Sopenharmony_ci	struct sock *sk = sock->sk;
98762306a36Sopenharmony_ci	ax25_cb *ax25;
98862306a36Sopenharmony_ci	ax25_dev *ax25_dev;
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	if (sk == NULL)
99162306a36Sopenharmony_ci		return 0;
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	sock_hold(sk);
99462306a36Sopenharmony_ci	lock_sock(sk);
99562306a36Sopenharmony_ci	sock_orphan(sk);
99662306a36Sopenharmony_ci	ax25 = sk_to_ax25(sk);
99762306a36Sopenharmony_ci	ax25_dev = ax25->ax25_dev;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	if (sk->sk_type == SOCK_SEQPACKET) {
100062306a36Sopenharmony_ci		switch (ax25->state) {
100162306a36Sopenharmony_ci		case AX25_STATE_0:
100262306a36Sopenharmony_ci			if (!sock_flag(ax25->sk, SOCK_DEAD)) {
100362306a36Sopenharmony_ci				release_sock(sk);
100462306a36Sopenharmony_ci				ax25_disconnect(ax25, 0);
100562306a36Sopenharmony_ci				lock_sock(sk);
100662306a36Sopenharmony_ci			}
100762306a36Sopenharmony_ci			ax25_destroy_socket(ax25);
100862306a36Sopenharmony_ci			break;
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci		case AX25_STATE_1:
101162306a36Sopenharmony_ci		case AX25_STATE_2:
101262306a36Sopenharmony_ci			ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
101362306a36Sopenharmony_ci			release_sock(sk);
101462306a36Sopenharmony_ci			ax25_disconnect(ax25, 0);
101562306a36Sopenharmony_ci			lock_sock(sk);
101662306a36Sopenharmony_ci			if (!sock_flag(ax25->sk, SOCK_DESTROY))
101762306a36Sopenharmony_ci				ax25_destroy_socket(ax25);
101862306a36Sopenharmony_ci			break;
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci		case AX25_STATE_3:
102162306a36Sopenharmony_ci		case AX25_STATE_4:
102262306a36Sopenharmony_ci			ax25_clear_queues(ax25);
102362306a36Sopenharmony_ci			ax25->n2count = 0;
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci			switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
102662306a36Sopenharmony_ci			case AX25_PROTO_STD_SIMPLEX:
102762306a36Sopenharmony_ci			case AX25_PROTO_STD_DUPLEX:
102862306a36Sopenharmony_ci				ax25_send_control(ax25,
102962306a36Sopenharmony_ci						  AX25_DISC,
103062306a36Sopenharmony_ci						  AX25_POLLON,
103162306a36Sopenharmony_ci						  AX25_COMMAND);
103262306a36Sopenharmony_ci				ax25_stop_t2timer(ax25);
103362306a36Sopenharmony_ci				ax25_stop_t3timer(ax25);
103462306a36Sopenharmony_ci				ax25_stop_idletimer(ax25);
103562306a36Sopenharmony_ci				break;
103662306a36Sopenharmony_ci#ifdef CONFIG_AX25_DAMA_SLAVE
103762306a36Sopenharmony_ci			case AX25_PROTO_DAMA_SLAVE:
103862306a36Sopenharmony_ci				ax25_stop_t3timer(ax25);
103962306a36Sopenharmony_ci				ax25_stop_idletimer(ax25);
104062306a36Sopenharmony_ci				break;
104162306a36Sopenharmony_ci#endif
104262306a36Sopenharmony_ci			}
104362306a36Sopenharmony_ci			ax25_calculate_t1(ax25);
104462306a36Sopenharmony_ci			ax25_start_t1timer(ax25);
104562306a36Sopenharmony_ci			ax25->state = AX25_STATE_2;
104662306a36Sopenharmony_ci			sk->sk_state                = TCP_CLOSE;
104762306a36Sopenharmony_ci			sk->sk_shutdown            |= SEND_SHUTDOWN;
104862306a36Sopenharmony_ci			sk->sk_state_change(sk);
104962306a36Sopenharmony_ci			sock_set_flag(sk, SOCK_DESTROY);
105062306a36Sopenharmony_ci			break;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci		default:
105362306a36Sopenharmony_ci			break;
105462306a36Sopenharmony_ci		}
105562306a36Sopenharmony_ci	} else {
105662306a36Sopenharmony_ci		sk->sk_state     = TCP_CLOSE;
105762306a36Sopenharmony_ci		sk->sk_shutdown |= SEND_SHUTDOWN;
105862306a36Sopenharmony_ci		sk->sk_state_change(sk);
105962306a36Sopenharmony_ci		ax25_destroy_socket(ax25);
106062306a36Sopenharmony_ci	}
106162306a36Sopenharmony_ci	if (ax25_dev) {
106262306a36Sopenharmony_ci		if (!ax25_dev->device_up) {
106362306a36Sopenharmony_ci			del_timer_sync(&ax25->timer);
106462306a36Sopenharmony_ci			del_timer_sync(&ax25->t1timer);
106562306a36Sopenharmony_ci			del_timer_sync(&ax25->t2timer);
106662306a36Sopenharmony_ci			del_timer_sync(&ax25->t3timer);
106762306a36Sopenharmony_ci			del_timer_sync(&ax25->idletimer);
106862306a36Sopenharmony_ci		}
106962306a36Sopenharmony_ci		netdev_put(ax25_dev->dev, &ax25->dev_tracker);
107062306a36Sopenharmony_ci		ax25_dev_put(ax25_dev);
107162306a36Sopenharmony_ci	}
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	sock->sk   = NULL;
107462306a36Sopenharmony_ci	release_sock(sk);
107562306a36Sopenharmony_ci	sock_put(sk);
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	return 0;
107862306a36Sopenharmony_ci}
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci/*
108162306a36Sopenharmony_ci *	We support a funny extension here so you can (as root) give any callsign
108262306a36Sopenharmony_ci *	digipeated via a local address as source. This hack is obsolete now
108362306a36Sopenharmony_ci *	that we've implemented support for SO_BINDTODEVICE. It is however small
108462306a36Sopenharmony_ci *	and trivially backward compatible.
108562306a36Sopenharmony_ci */
108662306a36Sopenharmony_cistatic int ax25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
108762306a36Sopenharmony_ci{
108862306a36Sopenharmony_ci	struct sock *sk = sock->sk;
108962306a36Sopenharmony_ci	struct full_sockaddr_ax25 *addr = (struct full_sockaddr_ax25 *)uaddr;
109062306a36Sopenharmony_ci	ax25_dev *ax25_dev = NULL;
109162306a36Sopenharmony_ci	ax25_uid_assoc *user;
109262306a36Sopenharmony_ci	ax25_address call;
109362306a36Sopenharmony_ci	ax25_cb *ax25;
109462306a36Sopenharmony_ci	int err = 0;
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	if (addr_len != sizeof(struct sockaddr_ax25) &&
109762306a36Sopenharmony_ci	    addr_len != sizeof(struct full_sockaddr_ax25))
109862306a36Sopenharmony_ci		/* support for old structure may go away some time
109962306a36Sopenharmony_ci		 * ax25_bind(): uses old (6 digipeater) socket structure.
110062306a36Sopenharmony_ci		 */
110162306a36Sopenharmony_ci		if ((addr_len < sizeof(struct sockaddr_ax25) + sizeof(ax25_address) * 6) ||
110262306a36Sopenharmony_ci		    (addr_len > sizeof(struct full_sockaddr_ax25)))
110362306a36Sopenharmony_ci			return -EINVAL;
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	if (addr->fsa_ax25.sax25_family != AF_AX25)
110662306a36Sopenharmony_ci		return -EINVAL;
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	user = ax25_findbyuid(current_euid());
110962306a36Sopenharmony_ci	if (user) {
111062306a36Sopenharmony_ci		call = user->call;
111162306a36Sopenharmony_ci		ax25_uid_put(user);
111262306a36Sopenharmony_ci	} else {
111362306a36Sopenharmony_ci		if (ax25_uid_policy && !capable(CAP_NET_ADMIN))
111462306a36Sopenharmony_ci			return -EACCES;
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci		call = addr->fsa_ax25.sax25_call;
111762306a36Sopenharmony_ci	}
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	lock_sock(sk);
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	ax25 = sk_to_ax25(sk);
112262306a36Sopenharmony_ci	if (!sock_flag(sk, SOCK_ZAPPED)) {
112362306a36Sopenharmony_ci		err = -EINVAL;
112462306a36Sopenharmony_ci		goto out;
112562306a36Sopenharmony_ci	}
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	ax25->source_addr = call;
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	/*
113062306a36Sopenharmony_ci	 * User already set interface with SO_BINDTODEVICE
113162306a36Sopenharmony_ci	 */
113262306a36Sopenharmony_ci	if (ax25->ax25_dev != NULL)
113362306a36Sopenharmony_ci		goto done;
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	if (addr_len > sizeof(struct sockaddr_ax25) && addr->fsa_ax25.sax25_ndigis == 1) {
113662306a36Sopenharmony_ci		if (ax25cmp(&addr->fsa_digipeater[0], &null_ax25_address) != 0 &&
113762306a36Sopenharmony_ci		    (ax25_dev = ax25_addr_ax25dev(&addr->fsa_digipeater[0])) == NULL) {
113862306a36Sopenharmony_ci			err = -EADDRNOTAVAIL;
113962306a36Sopenharmony_ci			goto out;
114062306a36Sopenharmony_ci		}
114162306a36Sopenharmony_ci	} else {
114262306a36Sopenharmony_ci		if ((ax25_dev = ax25_addr_ax25dev(&addr->fsa_ax25.sax25_call)) == NULL) {
114362306a36Sopenharmony_ci			err = -EADDRNOTAVAIL;
114462306a36Sopenharmony_ci			goto out;
114562306a36Sopenharmony_ci		}
114662306a36Sopenharmony_ci	}
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	if (ax25_dev) {
114962306a36Sopenharmony_ci		ax25_fillin_cb(ax25, ax25_dev);
115062306a36Sopenharmony_ci		netdev_hold(ax25_dev->dev, &ax25->dev_tracker, GFP_ATOMIC);
115162306a36Sopenharmony_ci	}
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_cidone:
115462306a36Sopenharmony_ci	ax25_cb_add(ax25);
115562306a36Sopenharmony_ci	sock_reset_flag(sk, SOCK_ZAPPED);
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ciout:
115862306a36Sopenharmony_ci	release_sock(sk);
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	return err;
116162306a36Sopenharmony_ci}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci/*
116462306a36Sopenharmony_ci *	FIXME: nonblock behaviour looks like it may have a bug.
116562306a36Sopenharmony_ci */
116662306a36Sopenharmony_cistatic int __must_check ax25_connect(struct socket *sock,
116762306a36Sopenharmony_ci	struct sockaddr *uaddr, int addr_len, int flags)
116862306a36Sopenharmony_ci{
116962306a36Sopenharmony_ci	struct sock *sk = sock->sk;
117062306a36Sopenharmony_ci	ax25_cb *ax25 = sk_to_ax25(sk), *ax25t;
117162306a36Sopenharmony_ci	struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)uaddr;
117262306a36Sopenharmony_ci	ax25_digi *digi = NULL;
117362306a36Sopenharmony_ci	int ct = 0, err = 0;
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	/*
117662306a36Sopenharmony_ci	 * some sanity checks. code further down depends on this
117762306a36Sopenharmony_ci	 */
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	if (addr_len == sizeof(struct sockaddr_ax25))
118062306a36Sopenharmony_ci		/* support for this will go away in early 2.5.x
118162306a36Sopenharmony_ci		 * ax25_connect(): uses obsolete socket structure
118262306a36Sopenharmony_ci		 */
118362306a36Sopenharmony_ci		;
118462306a36Sopenharmony_ci	else if (addr_len != sizeof(struct full_sockaddr_ax25))
118562306a36Sopenharmony_ci		/* support for old structure may go away some time
118662306a36Sopenharmony_ci		 * ax25_connect(): uses old (6 digipeater) socket structure.
118762306a36Sopenharmony_ci		 */
118862306a36Sopenharmony_ci		if ((addr_len < sizeof(struct sockaddr_ax25) + sizeof(ax25_address) * 6) ||
118962306a36Sopenharmony_ci		    (addr_len > sizeof(struct full_sockaddr_ax25)))
119062306a36Sopenharmony_ci			return -EINVAL;
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	if (fsa->fsa_ax25.sax25_family != AF_AX25)
119462306a36Sopenharmony_ci		return -EINVAL;
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	lock_sock(sk);
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	/* deal with restarts */
119962306a36Sopenharmony_ci	if (sock->state == SS_CONNECTING) {
120062306a36Sopenharmony_ci		switch (sk->sk_state) {
120162306a36Sopenharmony_ci		case TCP_SYN_SENT: /* still trying */
120262306a36Sopenharmony_ci			err = -EINPROGRESS;
120362306a36Sopenharmony_ci			goto out_release;
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci		case TCP_ESTABLISHED: /* connection established */
120662306a36Sopenharmony_ci			sock->state = SS_CONNECTED;
120762306a36Sopenharmony_ci			goto out_release;
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci		case TCP_CLOSE: /* connection refused */
121062306a36Sopenharmony_ci			sock->state = SS_UNCONNECTED;
121162306a36Sopenharmony_ci			err = -ECONNREFUSED;
121262306a36Sopenharmony_ci			goto out_release;
121362306a36Sopenharmony_ci		}
121462306a36Sopenharmony_ci	}
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	if (sk->sk_state == TCP_ESTABLISHED && sk->sk_type == SOCK_SEQPACKET) {
121762306a36Sopenharmony_ci		err = -EISCONN;	/* No reconnect on a seqpacket socket */
121862306a36Sopenharmony_ci		goto out_release;
121962306a36Sopenharmony_ci	}
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	sk->sk_state   = TCP_CLOSE;
122262306a36Sopenharmony_ci	sock->state = SS_UNCONNECTED;
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	kfree(ax25->digipeat);
122562306a36Sopenharmony_ci	ax25->digipeat = NULL;
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	/*
122862306a36Sopenharmony_ci	 *	Handle digi-peaters to be used.
122962306a36Sopenharmony_ci	 */
123062306a36Sopenharmony_ci	if (addr_len > sizeof(struct sockaddr_ax25) &&
123162306a36Sopenharmony_ci	    fsa->fsa_ax25.sax25_ndigis != 0) {
123262306a36Sopenharmony_ci		/* Valid number of digipeaters ? */
123362306a36Sopenharmony_ci		if (fsa->fsa_ax25.sax25_ndigis < 1 ||
123462306a36Sopenharmony_ci		    fsa->fsa_ax25.sax25_ndigis > AX25_MAX_DIGIS ||
123562306a36Sopenharmony_ci		    addr_len < sizeof(struct sockaddr_ax25) +
123662306a36Sopenharmony_ci		    sizeof(ax25_address) * fsa->fsa_ax25.sax25_ndigis) {
123762306a36Sopenharmony_ci			err = -EINVAL;
123862306a36Sopenharmony_ci			goto out_release;
123962306a36Sopenharmony_ci		}
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci		if ((digi = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) {
124262306a36Sopenharmony_ci			err = -ENOBUFS;
124362306a36Sopenharmony_ci			goto out_release;
124462306a36Sopenharmony_ci		}
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci		digi->ndigi      = fsa->fsa_ax25.sax25_ndigis;
124762306a36Sopenharmony_ci		digi->lastrepeat = -1;
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_ci		while (ct < fsa->fsa_ax25.sax25_ndigis) {
125062306a36Sopenharmony_ci			if ((fsa->fsa_digipeater[ct].ax25_call[6] &
125162306a36Sopenharmony_ci			     AX25_HBIT) && ax25->iamdigi) {
125262306a36Sopenharmony_ci				digi->repeated[ct] = 1;
125362306a36Sopenharmony_ci				digi->lastrepeat   = ct;
125462306a36Sopenharmony_ci			} else {
125562306a36Sopenharmony_ci				digi->repeated[ct] = 0;
125662306a36Sopenharmony_ci			}
125762306a36Sopenharmony_ci			digi->calls[ct] = fsa->fsa_digipeater[ct];
125862306a36Sopenharmony_ci			ct++;
125962306a36Sopenharmony_ci		}
126062306a36Sopenharmony_ci	}
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	/*
126362306a36Sopenharmony_ci	 *	Must bind first - autobinding in this may or may not work. If
126462306a36Sopenharmony_ci	 *	the socket is already bound, check to see if the device has
126562306a36Sopenharmony_ci	 *	been filled in, error if it hasn't.
126662306a36Sopenharmony_ci	 */
126762306a36Sopenharmony_ci	if (sock_flag(sk, SOCK_ZAPPED)) {
126862306a36Sopenharmony_ci		/* check if we can remove this feature. It is broken. */
126962306a36Sopenharmony_ci		printk(KERN_WARNING "ax25_connect(): %s uses autobind, please contact jreuter@yaina.de\n",
127062306a36Sopenharmony_ci			current->comm);
127162306a36Sopenharmony_ci		if ((err = ax25_rt_autobind(ax25, &fsa->fsa_ax25.sax25_call)) < 0) {
127262306a36Sopenharmony_ci			kfree(digi);
127362306a36Sopenharmony_ci			goto out_release;
127462306a36Sopenharmony_ci		}
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci		ax25_fillin_cb(ax25, ax25->ax25_dev);
127762306a36Sopenharmony_ci		ax25_cb_add(ax25);
127862306a36Sopenharmony_ci	} else {
127962306a36Sopenharmony_ci		if (ax25->ax25_dev == NULL) {
128062306a36Sopenharmony_ci			kfree(digi);
128162306a36Sopenharmony_ci			err = -EHOSTUNREACH;
128262306a36Sopenharmony_ci			goto out_release;
128362306a36Sopenharmony_ci		}
128462306a36Sopenharmony_ci	}
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	if (sk->sk_type == SOCK_SEQPACKET &&
128762306a36Sopenharmony_ci	    (ax25t=ax25_find_cb(&ax25->source_addr, &fsa->fsa_ax25.sax25_call, digi,
128862306a36Sopenharmony_ci			 ax25->ax25_dev->dev))) {
128962306a36Sopenharmony_ci		kfree(digi);
129062306a36Sopenharmony_ci		err = -EADDRINUSE;		/* Already such a connection */
129162306a36Sopenharmony_ci		ax25_cb_put(ax25t);
129262306a36Sopenharmony_ci		goto out_release;
129362306a36Sopenharmony_ci	}
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	ax25->dest_addr = fsa->fsa_ax25.sax25_call;
129662306a36Sopenharmony_ci	ax25->digipeat  = digi;
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	/* First the easy one */
129962306a36Sopenharmony_ci	if (sk->sk_type != SOCK_SEQPACKET) {
130062306a36Sopenharmony_ci		sock->state = SS_CONNECTED;
130162306a36Sopenharmony_ci		sk->sk_state   = TCP_ESTABLISHED;
130262306a36Sopenharmony_ci		goto out_release;
130362306a36Sopenharmony_ci	}
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci	/* Move to connecting socket, ax.25 lapb WAIT_UA.. */
130662306a36Sopenharmony_ci	sock->state        = SS_CONNECTING;
130762306a36Sopenharmony_ci	sk->sk_state          = TCP_SYN_SENT;
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
131062306a36Sopenharmony_ci	case AX25_PROTO_STD_SIMPLEX:
131162306a36Sopenharmony_ci	case AX25_PROTO_STD_DUPLEX:
131262306a36Sopenharmony_ci		ax25_std_establish_data_link(ax25);
131362306a36Sopenharmony_ci		break;
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci#ifdef CONFIG_AX25_DAMA_SLAVE
131662306a36Sopenharmony_ci	case AX25_PROTO_DAMA_SLAVE:
131762306a36Sopenharmony_ci		ax25->modulus = AX25_MODULUS;
131862306a36Sopenharmony_ci		ax25->window  = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
131962306a36Sopenharmony_ci		if (ax25->ax25_dev->dama.slave)
132062306a36Sopenharmony_ci			ax25_ds_establish_data_link(ax25);
132162306a36Sopenharmony_ci		else
132262306a36Sopenharmony_ci			ax25_std_establish_data_link(ax25);
132362306a36Sopenharmony_ci		break;
132462306a36Sopenharmony_ci#endif
132562306a36Sopenharmony_ci	}
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	ax25->state = AX25_STATE_1;
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	ax25_start_heartbeat(ax25);
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	/* Now the loop */
133262306a36Sopenharmony_ci	if (sk->sk_state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) {
133362306a36Sopenharmony_ci		err = -EINPROGRESS;
133462306a36Sopenharmony_ci		goto out_release;
133562306a36Sopenharmony_ci	}
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	if (sk->sk_state == TCP_SYN_SENT) {
133862306a36Sopenharmony_ci		DEFINE_WAIT(wait);
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci		for (;;) {
134162306a36Sopenharmony_ci			prepare_to_wait(sk_sleep(sk), &wait,
134262306a36Sopenharmony_ci					TASK_INTERRUPTIBLE);
134362306a36Sopenharmony_ci			if (sk->sk_state != TCP_SYN_SENT)
134462306a36Sopenharmony_ci				break;
134562306a36Sopenharmony_ci			if (!signal_pending(current)) {
134662306a36Sopenharmony_ci				release_sock(sk);
134762306a36Sopenharmony_ci				schedule();
134862306a36Sopenharmony_ci				lock_sock(sk);
134962306a36Sopenharmony_ci				continue;
135062306a36Sopenharmony_ci			}
135162306a36Sopenharmony_ci			err = -ERESTARTSYS;
135262306a36Sopenharmony_ci			break;
135362306a36Sopenharmony_ci		}
135462306a36Sopenharmony_ci		finish_wait(sk_sleep(sk), &wait);
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci		if (err)
135762306a36Sopenharmony_ci			goto out_release;
135862306a36Sopenharmony_ci	}
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	if (sk->sk_state != TCP_ESTABLISHED) {
136162306a36Sopenharmony_ci		/* Not in ABM, not in WAIT_UA -> failed */
136262306a36Sopenharmony_ci		sock->state = SS_UNCONNECTED;
136362306a36Sopenharmony_ci		err = sock_error(sk);	/* Always set at this point */
136462306a36Sopenharmony_ci		goto out_release;
136562306a36Sopenharmony_ci	}
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci	sock->state = SS_CONNECTED;
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	err = 0;
137062306a36Sopenharmony_ciout_release:
137162306a36Sopenharmony_ci	release_sock(sk);
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	return err;
137462306a36Sopenharmony_ci}
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_cistatic int ax25_accept(struct socket *sock, struct socket *newsock, int flags,
137762306a36Sopenharmony_ci		       bool kern)
137862306a36Sopenharmony_ci{
137962306a36Sopenharmony_ci	struct sk_buff *skb;
138062306a36Sopenharmony_ci	struct sock *newsk;
138162306a36Sopenharmony_ci	DEFINE_WAIT(wait);
138262306a36Sopenharmony_ci	struct sock *sk;
138362306a36Sopenharmony_ci	int err = 0;
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ci	if (sock->state != SS_UNCONNECTED)
138662306a36Sopenharmony_ci		return -EINVAL;
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_ci	if ((sk = sock->sk) == NULL)
138962306a36Sopenharmony_ci		return -EINVAL;
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci	lock_sock(sk);
139262306a36Sopenharmony_ci	if (sk->sk_type != SOCK_SEQPACKET) {
139362306a36Sopenharmony_ci		err = -EOPNOTSUPP;
139462306a36Sopenharmony_ci		goto out;
139562306a36Sopenharmony_ci	}
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	if (sk->sk_state != TCP_LISTEN) {
139862306a36Sopenharmony_ci		err = -EINVAL;
139962306a36Sopenharmony_ci		goto out;
140062306a36Sopenharmony_ci	}
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_ci	/*
140362306a36Sopenharmony_ci	 *	The read queue this time is holding sockets ready to use
140462306a36Sopenharmony_ci	 *	hooked into the SABM we saved
140562306a36Sopenharmony_ci	 */
140662306a36Sopenharmony_ci	for (;;) {
140762306a36Sopenharmony_ci		prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
140862306a36Sopenharmony_ci		skb = skb_dequeue(&sk->sk_receive_queue);
140962306a36Sopenharmony_ci		if (skb)
141062306a36Sopenharmony_ci			break;
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci		if (flags & O_NONBLOCK) {
141362306a36Sopenharmony_ci			err = -EWOULDBLOCK;
141462306a36Sopenharmony_ci			break;
141562306a36Sopenharmony_ci		}
141662306a36Sopenharmony_ci		if (!signal_pending(current)) {
141762306a36Sopenharmony_ci			release_sock(sk);
141862306a36Sopenharmony_ci			schedule();
141962306a36Sopenharmony_ci			lock_sock(sk);
142062306a36Sopenharmony_ci			continue;
142162306a36Sopenharmony_ci		}
142262306a36Sopenharmony_ci		err = -ERESTARTSYS;
142362306a36Sopenharmony_ci		break;
142462306a36Sopenharmony_ci	}
142562306a36Sopenharmony_ci	finish_wait(sk_sleep(sk), &wait);
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	if (err)
142862306a36Sopenharmony_ci		goto out;
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci	newsk		 = skb->sk;
143162306a36Sopenharmony_ci	sock_graft(newsk, newsock);
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci	/* Now attach up the new socket */
143462306a36Sopenharmony_ci	kfree_skb(skb);
143562306a36Sopenharmony_ci	sk_acceptq_removed(sk);
143662306a36Sopenharmony_ci	newsock->state = SS_CONNECTED;
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ciout:
143962306a36Sopenharmony_ci	release_sock(sk);
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci	return err;
144262306a36Sopenharmony_ci}
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_cistatic int ax25_getname(struct socket *sock, struct sockaddr *uaddr,
144562306a36Sopenharmony_ci	int peer)
144662306a36Sopenharmony_ci{
144762306a36Sopenharmony_ci	struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)uaddr;
144862306a36Sopenharmony_ci	struct sock *sk = sock->sk;
144962306a36Sopenharmony_ci	unsigned char ndigi, i;
145062306a36Sopenharmony_ci	ax25_cb *ax25;
145162306a36Sopenharmony_ci	int err = 0;
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	memset(fsa, 0, sizeof(*fsa));
145462306a36Sopenharmony_ci	lock_sock(sk);
145562306a36Sopenharmony_ci	ax25 = sk_to_ax25(sk);
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	if (peer != 0) {
145862306a36Sopenharmony_ci		if (sk->sk_state != TCP_ESTABLISHED) {
145962306a36Sopenharmony_ci			err = -ENOTCONN;
146062306a36Sopenharmony_ci			goto out;
146162306a36Sopenharmony_ci		}
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci		fsa->fsa_ax25.sax25_family = AF_AX25;
146462306a36Sopenharmony_ci		fsa->fsa_ax25.sax25_call   = ax25->dest_addr;
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci		if (ax25->digipeat != NULL) {
146762306a36Sopenharmony_ci			ndigi = ax25->digipeat->ndigi;
146862306a36Sopenharmony_ci			fsa->fsa_ax25.sax25_ndigis = ndigi;
146962306a36Sopenharmony_ci			for (i = 0; i < ndigi; i++)
147062306a36Sopenharmony_ci				fsa->fsa_digipeater[i] =
147162306a36Sopenharmony_ci						ax25->digipeat->calls[i];
147262306a36Sopenharmony_ci		}
147362306a36Sopenharmony_ci	} else {
147462306a36Sopenharmony_ci		fsa->fsa_ax25.sax25_family = AF_AX25;
147562306a36Sopenharmony_ci		fsa->fsa_ax25.sax25_call   = ax25->source_addr;
147662306a36Sopenharmony_ci		fsa->fsa_ax25.sax25_ndigis = 1;
147762306a36Sopenharmony_ci		if (ax25->ax25_dev != NULL) {
147862306a36Sopenharmony_ci			memcpy(&fsa->fsa_digipeater[0],
147962306a36Sopenharmony_ci			       ax25->ax25_dev->dev->dev_addr, AX25_ADDR_LEN);
148062306a36Sopenharmony_ci		} else {
148162306a36Sopenharmony_ci			fsa->fsa_digipeater[0] = null_ax25_address;
148262306a36Sopenharmony_ci		}
148362306a36Sopenharmony_ci	}
148462306a36Sopenharmony_ci	err = sizeof (struct full_sockaddr_ax25);
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ciout:
148762306a36Sopenharmony_ci	release_sock(sk);
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci	return err;
149062306a36Sopenharmony_ci}
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_cistatic int ax25_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
149362306a36Sopenharmony_ci{
149462306a36Sopenharmony_ci	DECLARE_SOCKADDR(struct sockaddr_ax25 *, usax, msg->msg_name);
149562306a36Sopenharmony_ci	struct sock *sk = sock->sk;
149662306a36Sopenharmony_ci	struct sockaddr_ax25 sax;
149762306a36Sopenharmony_ci	struct sk_buff *skb;
149862306a36Sopenharmony_ci	ax25_digi dtmp, *dp;
149962306a36Sopenharmony_ci	ax25_cb *ax25;
150062306a36Sopenharmony_ci	size_t size;
150162306a36Sopenharmony_ci	int lv, err, addr_len = msg->msg_namelen;
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci	if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_EOR|MSG_CMSG_COMPAT))
150462306a36Sopenharmony_ci		return -EINVAL;
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	lock_sock(sk);
150762306a36Sopenharmony_ci	ax25 = sk_to_ax25(sk);
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci	if (sock_flag(sk, SOCK_ZAPPED)) {
151062306a36Sopenharmony_ci		err = -EADDRNOTAVAIL;
151162306a36Sopenharmony_ci		goto out;
151262306a36Sopenharmony_ci	}
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci	if (sk->sk_shutdown & SEND_SHUTDOWN) {
151562306a36Sopenharmony_ci		send_sig(SIGPIPE, current, 0);
151662306a36Sopenharmony_ci		err = -EPIPE;
151762306a36Sopenharmony_ci		goto out;
151862306a36Sopenharmony_ci	}
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci	if (ax25->ax25_dev == NULL) {
152162306a36Sopenharmony_ci		err = -ENETUNREACH;
152262306a36Sopenharmony_ci		goto out;
152362306a36Sopenharmony_ci	}
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci	if (len > ax25->ax25_dev->dev->mtu) {
152662306a36Sopenharmony_ci		err = -EMSGSIZE;
152762306a36Sopenharmony_ci		goto out;
152862306a36Sopenharmony_ci	}
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci	if (usax != NULL) {
153162306a36Sopenharmony_ci		if (usax->sax25_family != AF_AX25) {
153262306a36Sopenharmony_ci			err = -EINVAL;
153362306a36Sopenharmony_ci			goto out;
153462306a36Sopenharmony_ci		}
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci		if (addr_len == sizeof(struct sockaddr_ax25))
153762306a36Sopenharmony_ci			/* ax25_sendmsg(): uses obsolete socket structure */
153862306a36Sopenharmony_ci			;
153962306a36Sopenharmony_ci		else if (addr_len != sizeof(struct full_sockaddr_ax25))
154062306a36Sopenharmony_ci			/* support for old structure may go away some time
154162306a36Sopenharmony_ci			 * ax25_sendmsg(): uses old (6 digipeater)
154262306a36Sopenharmony_ci			 * socket structure.
154362306a36Sopenharmony_ci			 */
154462306a36Sopenharmony_ci			if ((addr_len < sizeof(struct sockaddr_ax25) + sizeof(ax25_address) * 6) ||
154562306a36Sopenharmony_ci			    (addr_len > sizeof(struct full_sockaddr_ax25))) {
154662306a36Sopenharmony_ci				err = -EINVAL;
154762306a36Sopenharmony_ci				goto out;
154862306a36Sopenharmony_ci			}
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci		if (addr_len > sizeof(struct sockaddr_ax25) && usax->sax25_ndigis != 0) {
155262306a36Sopenharmony_ci			int ct           = 0;
155362306a36Sopenharmony_ci			struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)usax;
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci			/* Valid number of digipeaters ? */
155662306a36Sopenharmony_ci			if (usax->sax25_ndigis < 1 ||
155762306a36Sopenharmony_ci			    usax->sax25_ndigis > AX25_MAX_DIGIS ||
155862306a36Sopenharmony_ci			    addr_len < sizeof(struct sockaddr_ax25) +
155962306a36Sopenharmony_ci			    sizeof(ax25_address) * usax->sax25_ndigis) {
156062306a36Sopenharmony_ci				err = -EINVAL;
156162306a36Sopenharmony_ci				goto out;
156262306a36Sopenharmony_ci			}
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci			dtmp.ndigi      = usax->sax25_ndigis;
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci			while (ct < usax->sax25_ndigis) {
156762306a36Sopenharmony_ci				dtmp.repeated[ct] = 0;
156862306a36Sopenharmony_ci				dtmp.calls[ct]    = fsa->fsa_digipeater[ct];
156962306a36Sopenharmony_ci				ct++;
157062306a36Sopenharmony_ci			}
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci			dtmp.lastrepeat = 0;
157362306a36Sopenharmony_ci		}
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ci		sax = *usax;
157662306a36Sopenharmony_ci		if (sk->sk_type == SOCK_SEQPACKET &&
157762306a36Sopenharmony_ci		    ax25cmp(&ax25->dest_addr, &sax.sax25_call)) {
157862306a36Sopenharmony_ci			err = -EISCONN;
157962306a36Sopenharmony_ci			goto out;
158062306a36Sopenharmony_ci		}
158162306a36Sopenharmony_ci		if (usax->sax25_ndigis == 0)
158262306a36Sopenharmony_ci			dp = NULL;
158362306a36Sopenharmony_ci		else
158462306a36Sopenharmony_ci			dp = &dtmp;
158562306a36Sopenharmony_ci	} else {
158662306a36Sopenharmony_ci		/*
158762306a36Sopenharmony_ci		 *	FIXME: 1003.1g - if the socket is like this because
158862306a36Sopenharmony_ci		 *	it has become closed (not started closed) and is VC
158962306a36Sopenharmony_ci		 *	we ought to SIGPIPE, EPIPE
159062306a36Sopenharmony_ci		 */
159162306a36Sopenharmony_ci		if (sk->sk_state != TCP_ESTABLISHED) {
159262306a36Sopenharmony_ci			err = -ENOTCONN;
159362306a36Sopenharmony_ci			goto out;
159462306a36Sopenharmony_ci		}
159562306a36Sopenharmony_ci		sax.sax25_family = AF_AX25;
159662306a36Sopenharmony_ci		sax.sax25_call   = ax25->dest_addr;
159762306a36Sopenharmony_ci		dp = ax25->digipeat;
159862306a36Sopenharmony_ci	}
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_ci	/* Build a packet */
160162306a36Sopenharmony_ci	/* Assume the worst case */
160262306a36Sopenharmony_ci	size = len + ax25->ax25_dev->dev->hard_header_len;
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci	skb = sock_alloc_send_skb(sk, size, msg->msg_flags&MSG_DONTWAIT, &err);
160562306a36Sopenharmony_ci	if (skb == NULL)
160662306a36Sopenharmony_ci		goto out;
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ci	skb_reserve(skb, size - len);
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci	/* User data follows immediately after the AX.25 data */
161162306a36Sopenharmony_ci	if (memcpy_from_msg(skb_put(skb, len), msg, len)) {
161262306a36Sopenharmony_ci		err = -EFAULT;
161362306a36Sopenharmony_ci		kfree_skb(skb);
161462306a36Sopenharmony_ci		goto out;
161562306a36Sopenharmony_ci	}
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_ci	skb_reset_network_header(skb);
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_ci	/* Add the PID if one is not supplied by the user in the skb */
162062306a36Sopenharmony_ci	if (!ax25->pidincl)
162162306a36Sopenharmony_ci		*(u8 *)skb_push(skb, 1) = sk->sk_protocol;
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci	if (sk->sk_type == SOCK_SEQPACKET) {
162462306a36Sopenharmony_ci		/* Connected mode sockets go via the LAPB machine */
162562306a36Sopenharmony_ci		if (sk->sk_state != TCP_ESTABLISHED) {
162662306a36Sopenharmony_ci			kfree_skb(skb);
162762306a36Sopenharmony_ci			err = -ENOTCONN;
162862306a36Sopenharmony_ci			goto out;
162962306a36Sopenharmony_ci		}
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci		/* Shove it onto the queue and kick */
163262306a36Sopenharmony_ci		ax25_output(ax25, ax25->paclen, skb);
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci		err = len;
163562306a36Sopenharmony_ci		goto out;
163662306a36Sopenharmony_ci	}
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci	skb_push(skb, 1 + ax25_addr_size(dp));
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ci	/* Building AX.25 Header */
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci	/* Build an AX.25 header */
164362306a36Sopenharmony_ci	lv = ax25_addr_build(skb->data, &ax25->source_addr, &sax.sax25_call,
164462306a36Sopenharmony_ci			     dp, AX25_COMMAND, AX25_MODULUS);
164562306a36Sopenharmony_ci
164662306a36Sopenharmony_ci	skb_set_transport_header(skb, lv);
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci	*skb_transport_header(skb) = AX25_UI;
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci	/* Datagram frames go straight out of the door as UI */
165162306a36Sopenharmony_ci	ax25_queue_xmit(skb, ax25->ax25_dev->dev);
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ci	err = len;
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ciout:
165662306a36Sopenharmony_ci	release_sock(sk);
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ci	return err;
165962306a36Sopenharmony_ci}
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_cistatic int ax25_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,
166262306a36Sopenharmony_ci			int flags)
166362306a36Sopenharmony_ci{
166462306a36Sopenharmony_ci	struct sock *sk = sock->sk;
166562306a36Sopenharmony_ci	struct sk_buff *skb, *last;
166662306a36Sopenharmony_ci	struct sk_buff_head *sk_queue;
166762306a36Sopenharmony_ci	int copied;
166862306a36Sopenharmony_ci	int err = 0;
166962306a36Sopenharmony_ci	int off = 0;
167062306a36Sopenharmony_ci	long timeo;
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	lock_sock(sk);
167362306a36Sopenharmony_ci	/*
167462306a36Sopenharmony_ci	 * 	This works for seqpacket too. The receiver has ordered the
167562306a36Sopenharmony_ci	 *	queue for us! We do one quick check first though
167662306a36Sopenharmony_ci	 */
167762306a36Sopenharmony_ci	if (sk->sk_type == SOCK_SEQPACKET && sk->sk_state != TCP_ESTABLISHED) {
167862306a36Sopenharmony_ci		err =  -ENOTCONN;
167962306a36Sopenharmony_ci		goto out;
168062306a36Sopenharmony_ci	}
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci	/*  We need support for non-blocking reads. */
168362306a36Sopenharmony_ci	sk_queue = &sk->sk_receive_queue;
168462306a36Sopenharmony_ci	skb = __skb_try_recv_datagram(sk, sk_queue, flags, &off, &err, &last);
168562306a36Sopenharmony_ci	/* If no packet is available, release_sock(sk) and try again. */
168662306a36Sopenharmony_ci	if (!skb) {
168762306a36Sopenharmony_ci		if (err != -EAGAIN)
168862306a36Sopenharmony_ci			goto out;
168962306a36Sopenharmony_ci		release_sock(sk);
169062306a36Sopenharmony_ci		timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
169162306a36Sopenharmony_ci		while (timeo && !__skb_wait_for_more_packets(sk, sk_queue, &err,
169262306a36Sopenharmony_ci							     &timeo, last)) {
169362306a36Sopenharmony_ci			skb = __skb_try_recv_datagram(sk, sk_queue, flags, &off,
169462306a36Sopenharmony_ci						      &err, &last);
169562306a36Sopenharmony_ci			if (skb)
169662306a36Sopenharmony_ci				break;
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_ci			if (err != -EAGAIN)
169962306a36Sopenharmony_ci				goto done;
170062306a36Sopenharmony_ci		}
170162306a36Sopenharmony_ci		if (!skb)
170262306a36Sopenharmony_ci			goto done;
170362306a36Sopenharmony_ci		lock_sock(sk);
170462306a36Sopenharmony_ci	}
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_ci	if (!sk_to_ax25(sk)->pidincl)
170762306a36Sopenharmony_ci		skb_pull(skb, 1);		/* Remove PID */
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci	skb_reset_transport_header(skb);
171062306a36Sopenharmony_ci	copied = skb->len;
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	if (copied > size) {
171362306a36Sopenharmony_ci		copied = size;
171462306a36Sopenharmony_ci		msg->msg_flags |= MSG_TRUNC;
171562306a36Sopenharmony_ci	}
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_ci	skb_copy_datagram_msg(skb, 0, msg, copied);
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_ci	if (msg->msg_name) {
172062306a36Sopenharmony_ci		ax25_digi digi;
172162306a36Sopenharmony_ci		ax25_address src;
172262306a36Sopenharmony_ci		const unsigned char *mac = skb_mac_header(skb);
172362306a36Sopenharmony_ci		DECLARE_SOCKADDR(struct sockaddr_ax25 *, sax, msg->msg_name);
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_ci		memset(sax, 0, sizeof(struct full_sockaddr_ax25));
172662306a36Sopenharmony_ci		ax25_addr_parse(mac + 1, skb->data - mac - 1, &src, NULL,
172762306a36Sopenharmony_ci				&digi, NULL, NULL);
172862306a36Sopenharmony_ci		sax->sax25_family = AF_AX25;
172962306a36Sopenharmony_ci		/* We set this correctly, even though we may not let the
173062306a36Sopenharmony_ci		   application know the digi calls further down (because it
173162306a36Sopenharmony_ci		   did NOT ask to know them).  This could get political... **/
173262306a36Sopenharmony_ci		sax->sax25_ndigis = digi.ndigi;
173362306a36Sopenharmony_ci		sax->sax25_call   = src;
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ci		if (sax->sax25_ndigis != 0) {
173662306a36Sopenharmony_ci			int ct;
173762306a36Sopenharmony_ci			struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)sax;
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_ci			for (ct = 0; ct < digi.ndigi; ct++)
174062306a36Sopenharmony_ci				fsa->fsa_digipeater[ct] = digi.calls[ct];
174162306a36Sopenharmony_ci		}
174262306a36Sopenharmony_ci		msg->msg_namelen = sizeof(struct full_sockaddr_ax25);
174362306a36Sopenharmony_ci	}
174462306a36Sopenharmony_ci
174562306a36Sopenharmony_ci	skb_free_datagram(sk, skb);
174662306a36Sopenharmony_ci	err = copied;
174762306a36Sopenharmony_ci
174862306a36Sopenharmony_ciout:
174962306a36Sopenharmony_ci	release_sock(sk);
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_cidone:
175262306a36Sopenharmony_ci	return err;
175362306a36Sopenharmony_ci}
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_cistatic int ax25_shutdown(struct socket *sk, int how)
175662306a36Sopenharmony_ci{
175762306a36Sopenharmony_ci	/* FIXME - generate DM and RNR states */
175862306a36Sopenharmony_ci	return -EOPNOTSUPP;
175962306a36Sopenharmony_ci}
176062306a36Sopenharmony_ci
176162306a36Sopenharmony_cistatic int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
176262306a36Sopenharmony_ci{
176362306a36Sopenharmony_ci	struct sock *sk = sock->sk;
176462306a36Sopenharmony_ci	void __user *argp = (void __user *)arg;
176562306a36Sopenharmony_ci	int res = 0;
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ci	lock_sock(sk);
176862306a36Sopenharmony_ci	switch (cmd) {
176962306a36Sopenharmony_ci	case TIOCOUTQ: {
177062306a36Sopenharmony_ci		long amount;
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_ci		amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk);
177362306a36Sopenharmony_ci		if (amount < 0)
177462306a36Sopenharmony_ci			amount = 0;
177562306a36Sopenharmony_ci		res = put_user(amount, (int __user *)argp);
177662306a36Sopenharmony_ci		break;
177762306a36Sopenharmony_ci	}
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_ci	case TIOCINQ: {
178062306a36Sopenharmony_ci		struct sk_buff *skb;
178162306a36Sopenharmony_ci		long amount = 0L;
178262306a36Sopenharmony_ci		/* These two are safe on a single CPU system as only user tasks fiddle here */
178362306a36Sopenharmony_ci		if ((skb = skb_peek(&sk->sk_receive_queue)) != NULL)
178462306a36Sopenharmony_ci			amount = skb->len;
178562306a36Sopenharmony_ci		res = put_user(amount, (int __user *) argp);
178662306a36Sopenharmony_ci		break;
178762306a36Sopenharmony_ci	}
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_ci	case SIOCAX25ADDUID:	/* Add a uid to the uid/call map table */
179062306a36Sopenharmony_ci	case SIOCAX25DELUID:	/* Delete a uid from the uid/call map table */
179162306a36Sopenharmony_ci	case SIOCAX25GETUID: {
179262306a36Sopenharmony_ci		struct sockaddr_ax25 sax25;
179362306a36Sopenharmony_ci		if (copy_from_user(&sax25, argp, sizeof(sax25))) {
179462306a36Sopenharmony_ci			res = -EFAULT;
179562306a36Sopenharmony_ci			break;
179662306a36Sopenharmony_ci		}
179762306a36Sopenharmony_ci		res = ax25_uid_ioctl(cmd, &sax25);
179862306a36Sopenharmony_ci		break;
179962306a36Sopenharmony_ci	}
180062306a36Sopenharmony_ci
180162306a36Sopenharmony_ci	case SIOCAX25NOUID: {	/* Set the default policy (default/bar) */
180262306a36Sopenharmony_ci		long amount;
180362306a36Sopenharmony_ci		if (!capable(CAP_NET_ADMIN)) {
180462306a36Sopenharmony_ci			res = -EPERM;
180562306a36Sopenharmony_ci			break;
180662306a36Sopenharmony_ci		}
180762306a36Sopenharmony_ci		if (get_user(amount, (long __user *)argp)) {
180862306a36Sopenharmony_ci			res = -EFAULT;
180962306a36Sopenharmony_ci			break;
181062306a36Sopenharmony_ci		}
181162306a36Sopenharmony_ci		if (amount < 0 || amount > AX25_NOUID_BLOCK) {
181262306a36Sopenharmony_ci			res = -EINVAL;
181362306a36Sopenharmony_ci			break;
181462306a36Sopenharmony_ci		}
181562306a36Sopenharmony_ci		ax25_uid_policy = amount;
181662306a36Sopenharmony_ci		res = 0;
181762306a36Sopenharmony_ci		break;
181862306a36Sopenharmony_ci	}
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_ci	case SIOCADDRT:
182162306a36Sopenharmony_ci	case SIOCDELRT:
182262306a36Sopenharmony_ci	case SIOCAX25OPTRT:
182362306a36Sopenharmony_ci		if (!capable(CAP_NET_ADMIN)) {
182462306a36Sopenharmony_ci			res = -EPERM;
182562306a36Sopenharmony_ci			break;
182662306a36Sopenharmony_ci		}
182762306a36Sopenharmony_ci		res = ax25_rt_ioctl(cmd, argp);
182862306a36Sopenharmony_ci		break;
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_ci	case SIOCAX25CTLCON:
183162306a36Sopenharmony_ci		if (!capable(CAP_NET_ADMIN)) {
183262306a36Sopenharmony_ci			res = -EPERM;
183362306a36Sopenharmony_ci			break;
183462306a36Sopenharmony_ci		}
183562306a36Sopenharmony_ci		res = ax25_ctl_ioctl(cmd, argp);
183662306a36Sopenharmony_ci		break;
183762306a36Sopenharmony_ci
183862306a36Sopenharmony_ci	case SIOCAX25GETINFO:
183962306a36Sopenharmony_ci	case SIOCAX25GETINFOOLD: {
184062306a36Sopenharmony_ci		ax25_cb *ax25 = sk_to_ax25(sk);
184162306a36Sopenharmony_ci		struct ax25_info_struct ax25_info;
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_ci		ax25_info.t1        = ax25->t1   / HZ;
184462306a36Sopenharmony_ci		ax25_info.t2        = ax25->t2   / HZ;
184562306a36Sopenharmony_ci		ax25_info.t3        = ax25->t3   / HZ;
184662306a36Sopenharmony_ci		ax25_info.idle      = ax25->idle / (60 * HZ);
184762306a36Sopenharmony_ci		ax25_info.n2        = ax25->n2;
184862306a36Sopenharmony_ci		ax25_info.t1timer   = ax25_display_timer(&ax25->t1timer)   / HZ;
184962306a36Sopenharmony_ci		ax25_info.t2timer   = ax25_display_timer(&ax25->t2timer)   / HZ;
185062306a36Sopenharmony_ci		ax25_info.t3timer   = ax25_display_timer(&ax25->t3timer)   / HZ;
185162306a36Sopenharmony_ci		ax25_info.idletimer = ax25_display_timer(&ax25->idletimer) / (60 * HZ);
185262306a36Sopenharmony_ci		ax25_info.n2count   = ax25->n2count;
185362306a36Sopenharmony_ci		ax25_info.state     = ax25->state;
185462306a36Sopenharmony_ci		ax25_info.rcv_q     = sk_rmem_alloc_get(sk);
185562306a36Sopenharmony_ci		ax25_info.snd_q     = sk_wmem_alloc_get(sk);
185662306a36Sopenharmony_ci		ax25_info.vs        = ax25->vs;
185762306a36Sopenharmony_ci		ax25_info.vr        = ax25->vr;
185862306a36Sopenharmony_ci		ax25_info.va        = ax25->va;
185962306a36Sopenharmony_ci		ax25_info.vs_max    = ax25->vs; /* reserved */
186062306a36Sopenharmony_ci		ax25_info.paclen    = ax25->paclen;
186162306a36Sopenharmony_ci		ax25_info.window    = ax25->window;
186262306a36Sopenharmony_ci
186362306a36Sopenharmony_ci		/* old structure? */
186462306a36Sopenharmony_ci		if (cmd == SIOCAX25GETINFOOLD) {
186562306a36Sopenharmony_ci			static int warned = 0;
186662306a36Sopenharmony_ci			if (!warned) {
186762306a36Sopenharmony_ci				printk(KERN_INFO "%s uses old SIOCAX25GETINFO\n",
186862306a36Sopenharmony_ci					current->comm);
186962306a36Sopenharmony_ci				warned=1;
187062306a36Sopenharmony_ci			}
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci			if (copy_to_user(argp, &ax25_info, sizeof(struct ax25_info_struct_deprecated))) {
187362306a36Sopenharmony_ci				res = -EFAULT;
187462306a36Sopenharmony_ci				break;
187562306a36Sopenharmony_ci			}
187662306a36Sopenharmony_ci		} else {
187762306a36Sopenharmony_ci			if (copy_to_user(argp, &ax25_info, sizeof(struct ax25_info_struct))) {
187862306a36Sopenharmony_ci				res = -EINVAL;
187962306a36Sopenharmony_ci				break;
188062306a36Sopenharmony_ci			}
188162306a36Sopenharmony_ci		}
188262306a36Sopenharmony_ci		res = 0;
188362306a36Sopenharmony_ci		break;
188462306a36Sopenharmony_ci	}
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_ci	case SIOCAX25ADDFWD:
188762306a36Sopenharmony_ci	case SIOCAX25DELFWD: {
188862306a36Sopenharmony_ci		struct ax25_fwd_struct ax25_fwd;
188962306a36Sopenharmony_ci		if (!capable(CAP_NET_ADMIN)) {
189062306a36Sopenharmony_ci			res = -EPERM;
189162306a36Sopenharmony_ci			break;
189262306a36Sopenharmony_ci		}
189362306a36Sopenharmony_ci		if (copy_from_user(&ax25_fwd, argp, sizeof(ax25_fwd))) {
189462306a36Sopenharmony_ci			res = -EFAULT;
189562306a36Sopenharmony_ci			break;
189662306a36Sopenharmony_ci		}
189762306a36Sopenharmony_ci		res = ax25_fwd_ioctl(cmd, &ax25_fwd);
189862306a36Sopenharmony_ci		break;
189962306a36Sopenharmony_ci	}
190062306a36Sopenharmony_ci
190162306a36Sopenharmony_ci	case SIOCGIFADDR:
190262306a36Sopenharmony_ci	case SIOCSIFADDR:
190362306a36Sopenharmony_ci	case SIOCGIFDSTADDR:
190462306a36Sopenharmony_ci	case SIOCSIFDSTADDR:
190562306a36Sopenharmony_ci	case SIOCGIFBRDADDR:
190662306a36Sopenharmony_ci	case SIOCSIFBRDADDR:
190762306a36Sopenharmony_ci	case SIOCGIFNETMASK:
190862306a36Sopenharmony_ci	case SIOCSIFNETMASK:
190962306a36Sopenharmony_ci	case SIOCGIFMETRIC:
191062306a36Sopenharmony_ci	case SIOCSIFMETRIC:
191162306a36Sopenharmony_ci		res = -EINVAL;
191262306a36Sopenharmony_ci		break;
191362306a36Sopenharmony_ci
191462306a36Sopenharmony_ci	default:
191562306a36Sopenharmony_ci		res = -ENOIOCTLCMD;
191662306a36Sopenharmony_ci		break;
191762306a36Sopenharmony_ci	}
191862306a36Sopenharmony_ci	release_sock(sk);
191962306a36Sopenharmony_ci
192062306a36Sopenharmony_ci	return res;
192162306a36Sopenharmony_ci}
192262306a36Sopenharmony_ci
192362306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS
192462306a36Sopenharmony_ci
192562306a36Sopenharmony_cistatic void *ax25_info_start(struct seq_file *seq, loff_t *pos)
192662306a36Sopenharmony_ci	__acquires(ax25_list_lock)
192762306a36Sopenharmony_ci{
192862306a36Sopenharmony_ci	spin_lock_bh(&ax25_list_lock);
192962306a36Sopenharmony_ci	return seq_hlist_start(&ax25_list, *pos);
193062306a36Sopenharmony_ci}
193162306a36Sopenharmony_ci
193262306a36Sopenharmony_cistatic void *ax25_info_next(struct seq_file *seq, void *v, loff_t *pos)
193362306a36Sopenharmony_ci{
193462306a36Sopenharmony_ci	return seq_hlist_next(v, &ax25_list, pos);
193562306a36Sopenharmony_ci}
193662306a36Sopenharmony_ci
193762306a36Sopenharmony_cistatic void ax25_info_stop(struct seq_file *seq, void *v)
193862306a36Sopenharmony_ci	__releases(ax25_list_lock)
193962306a36Sopenharmony_ci{
194062306a36Sopenharmony_ci	spin_unlock_bh(&ax25_list_lock);
194162306a36Sopenharmony_ci}
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_cistatic int ax25_info_show(struct seq_file *seq, void *v)
194462306a36Sopenharmony_ci{
194562306a36Sopenharmony_ci	ax25_cb *ax25 = hlist_entry(v, struct ax25_cb, ax25_node);
194662306a36Sopenharmony_ci	char buf[11];
194762306a36Sopenharmony_ci	int k;
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_ci
195062306a36Sopenharmony_ci	/*
195162306a36Sopenharmony_ci	 * New format:
195262306a36Sopenharmony_ci	 * magic dev src_addr dest_addr,digi1,digi2,.. st vs vr va t1 t1 t2 t2 t3 t3 idle idle n2 n2 rtt window paclen Snd-Q Rcv-Q inode
195362306a36Sopenharmony_ci	 */
195462306a36Sopenharmony_ci
195562306a36Sopenharmony_ci	seq_printf(seq, "%p %s %s%s ",
195662306a36Sopenharmony_ci		   ax25,
195762306a36Sopenharmony_ci		   ax25->ax25_dev == NULL? "???" : ax25->ax25_dev->dev->name,
195862306a36Sopenharmony_ci		   ax2asc(buf, &ax25->source_addr),
195962306a36Sopenharmony_ci		   ax25->iamdigi? "*":"");
196062306a36Sopenharmony_ci	seq_printf(seq, "%s", ax2asc(buf, &ax25->dest_addr));
196162306a36Sopenharmony_ci
196262306a36Sopenharmony_ci	for (k=0; (ax25->digipeat != NULL) && (k < ax25->digipeat->ndigi); k++) {
196362306a36Sopenharmony_ci		seq_printf(seq, ",%s%s",
196462306a36Sopenharmony_ci			   ax2asc(buf, &ax25->digipeat->calls[k]),
196562306a36Sopenharmony_ci			   ax25->digipeat->repeated[k]? "*":"");
196662306a36Sopenharmony_ci	}
196762306a36Sopenharmony_ci
196862306a36Sopenharmony_ci	seq_printf(seq, " %d %d %d %d %lu %lu %lu %lu %lu %lu %lu %lu %d %d %lu %d %d",
196962306a36Sopenharmony_ci		   ax25->state,
197062306a36Sopenharmony_ci		   ax25->vs, ax25->vr, ax25->va,
197162306a36Sopenharmony_ci		   ax25_display_timer(&ax25->t1timer) / HZ, ax25->t1 / HZ,
197262306a36Sopenharmony_ci		   ax25_display_timer(&ax25->t2timer) / HZ, ax25->t2 / HZ,
197362306a36Sopenharmony_ci		   ax25_display_timer(&ax25->t3timer) / HZ, ax25->t3 / HZ,
197462306a36Sopenharmony_ci		   ax25_display_timer(&ax25->idletimer) / (60 * HZ),
197562306a36Sopenharmony_ci		   ax25->idle / (60 * HZ),
197662306a36Sopenharmony_ci		   ax25->n2count, ax25->n2,
197762306a36Sopenharmony_ci		   ax25->rtt / HZ,
197862306a36Sopenharmony_ci		   ax25->window,
197962306a36Sopenharmony_ci		   ax25->paclen);
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci	if (ax25->sk != NULL) {
198262306a36Sopenharmony_ci		seq_printf(seq, " %d %d %lu\n",
198362306a36Sopenharmony_ci			   sk_wmem_alloc_get(ax25->sk),
198462306a36Sopenharmony_ci			   sk_rmem_alloc_get(ax25->sk),
198562306a36Sopenharmony_ci			   sock_i_ino(ax25->sk));
198662306a36Sopenharmony_ci	} else {
198762306a36Sopenharmony_ci		seq_puts(seq, " * * *\n");
198862306a36Sopenharmony_ci	}
198962306a36Sopenharmony_ci	return 0;
199062306a36Sopenharmony_ci}
199162306a36Sopenharmony_ci
199262306a36Sopenharmony_cistatic const struct seq_operations ax25_info_seqops = {
199362306a36Sopenharmony_ci	.start = ax25_info_start,
199462306a36Sopenharmony_ci	.next = ax25_info_next,
199562306a36Sopenharmony_ci	.stop = ax25_info_stop,
199662306a36Sopenharmony_ci	.show = ax25_info_show,
199762306a36Sopenharmony_ci};
199862306a36Sopenharmony_ci#endif
199962306a36Sopenharmony_ci
200062306a36Sopenharmony_cistatic const struct net_proto_family ax25_family_ops = {
200162306a36Sopenharmony_ci	.family =	PF_AX25,
200262306a36Sopenharmony_ci	.create =	ax25_create,
200362306a36Sopenharmony_ci	.owner	=	THIS_MODULE,
200462306a36Sopenharmony_ci};
200562306a36Sopenharmony_ci
200662306a36Sopenharmony_cistatic const struct proto_ops ax25_proto_ops = {
200762306a36Sopenharmony_ci	.family		= PF_AX25,
200862306a36Sopenharmony_ci	.owner		= THIS_MODULE,
200962306a36Sopenharmony_ci	.release	= ax25_release,
201062306a36Sopenharmony_ci	.bind		= ax25_bind,
201162306a36Sopenharmony_ci	.connect	= ax25_connect,
201262306a36Sopenharmony_ci	.socketpair	= sock_no_socketpair,
201362306a36Sopenharmony_ci	.accept		= ax25_accept,
201462306a36Sopenharmony_ci	.getname	= ax25_getname,
201562306a36Sopenharmony_ci	.poll		= datagram_poll,
201662306a36Sopenharmony_ci	.ioctl		= ax25_ioctl,
201762306a36Sopenharmony_ci	.gettstamp	= sock_gettstamp,
201862306a36Sopenharmony_ci	.listen		= ax25_listen,
201962306a36Sopenharmony_ci	.shutdown	= ax25_shutdown,
202062306a36Sopenharmony_ci	.setsockopt	= ax25_setsockopt,
202162306a36Sopenharmony_ci	.getsockopt	= ax25_getsockopt,
202262306a36Sopenharmony_ci	.sendmsg	= ax25_sendmsg,
202362306a36Sopenharmony_ci	.recvmsg	= ax25_recvmsg,
202462306a36Sopenharmony_ci	.mmap		= sock_no_mmap,
202562306a36Sopenharmony_ci};
202662306a36Sopenharmony_ci
202762306a36Sopenharmony_ci/*
202862306a36Sopenharmony_ci *	Called by socket.c on kernel start up
202962306a36Sopenharmony_ci */
203062306a36Sopenharmony_cistatic struct packet_type ax25_packet_type __read_mostly = {
203162306a36Sopenharmony_ci	.type	=	cpu_to_be16(ETH_P_AX25),
203262306a36Sopenharmony_ci	.func	=	ax25_kiss_rcv,
203362306a36Sopenharmony_ci};
203462306a36Sopenharmony_ci
203562306a36Sopenharmony_cistatic struct notifier_block ax25_dev_notifier = {
203662306a36Sopenharmony_ci	.notifier_call = ax25_device_event,
203762306a36Sopenharmony_ci};
203862306a36Sopenharmony_ci
203962306a36Sopenharmony_cistatic int __init ax25_init(void)
204062306a36Sopenharmony_ci{
204162306a36Sopenharmony_ci	int rc = proto_register(&ax25_proto, 0);
204262306a36Sopenharmony_ci
204362306a36Sopenharmony_ci	if (rc != 0)
204462306a36Sopenharmony_ci		goto out;
204562306a36Sopenharmony_ci
204662306a36Sopenharmony_ci	sock_register(&ax25_family_ops);
204762306a36Sopenharmony_ci	dev_add_pack(&ax25_packet_type);
204862306a36Sopenharmony_ci	register_netdevice_notifier(&ax25_dev_notifier);
204962306a36Sopenharmony_ci
205062306a36Sopenharmony_ci	proc_create_seq("ax25_route", 0444, init_net.proc_net, &ax25_rt_seqops);
205162306a36Sopenharmony_ci	proc_create_seq("ax25", 0444, init_net.proc_net, &ax25_info_seqops);
205262306a36Sopenharmony_ci	proc_create_seq("ax25_calls", 0444, init_net.proc_net,
205362306a36Sopenharmony_ci			&ax25_uid_seqops);
205462306a36Sopenharmony_ciout:
205562306a36Sopenharmony_ci	return rc;
205662306a36Sopenharmony_ci}
205762306a36Sopenharmony_cimodule_init(ax25_init);
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ci
206062306a36Sopenharmony_ciMODULE_AUTHOR("Jonathan Naylor G4KLX <g4klx@g4klx.demon.co.uk>");
206162306a36Sopenharmony_ciMODULE_DESCRIPTION("The amateur radio AX.25 link layer protocol");
206262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
206362306a36Sopenharmony_ciMODULE_ALIAS_NETPROTO(PF_AX25);
206462306a36Sopenharmony_ci
206562306a36Sopenharmony_cistatic void __exit ax25_exit(void)
206662306a36Sopenharmony_ci{
206762306a36Sopenharmony_ci	remove_proc_entry("ax25_route", init_net.proc_net);
206862306a36Sopenharmony_ci	remove_proc_entry("ax25", init_net.proc_net);
206962306a36Sopenharmony_ci	remove_proc_entry("ax25_calls", init_net.proc_net);
207062306a36Sopenharmony_ci
207162306a36Sopenharmony_ci	unregister_netdevice_notifier(&ax25_dev_notifier);
207262306a36Sopenharmony_ci
207362306a36Sopenharmony_ci	dev_remove_pack(&ax25_packet_type);
207462306a36Sopenharmony_ci
207562306a36Sopenharmony_ci	sock_unregister(PF_AX25);
207662306a36Sopenharmony_ci	proto_unregister(&ax25_proto);
207762306a36Sopenharmony_ci
207862306a36Sopenharmony_ci	ax25_rt_free();
207962306a36Sopenharmony_ci	ax25_uid_free();
208062306a36Sopenharmony_ci	ax25_dev_free();
208162306a36Sopenharmony_ci}
208262306a36Sopenharmony_cimodule_exit(ax25_exit);
2083