⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 af_ax25.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Copyright (C) Alan Cox GW4PTS (alan@lxorguk.ukuu.org.uk) * Copyright (C) Jonathan Naylor G4KLX (g4klx@g4klx.demon.co.uk) * Copyright (C) Darryl Miles G7LED (dlm@g7led.demon.co.uk) * Copyright (C) Steven Whitehouse GW7RRM (stevew@acm.org) * Copyright (C) Joerg Reuter DL1BKE (jreuter@yaina.de) * Copyright (C) Hans-Joachim Hetscher DD8NE (dd8ne@bnv-bamberg.de) * Copyright (C) Hans Alblas PE1AYX (hans@esrac.ele.tue.nl) * Copyright (C) Frederic Rible F1OAT (frible@teaser.fr) */#include <linux/capability.h>#include <linux/module.h>#include <linux/errno.h>#include <linux/types.h>#include <linux/socket.h>#include <linux/in.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/timer.h>#include <linux/string.h>#include <linux/sockios.h>#include <linux/net.h>#include <net/ax25.h>#include <linux/inet.h>#include <linux/netdevice.h>#include <linux/if_arp.h>#include <linux/skbuff.h>#include <net/sock.h>#include <asm/uaccess.h>#include <asm/system.h>#include <linux/fcntl.h>#include <linux/termios.h>	/* For TIOCINQ/OUTQ */#include <linux/mm.h>#include <linux/interrupt.h>#include <linux/notifier.h>#include <linux/proc_fs.h>#include <linux/stat.h>#include <linux/netfilter.h>#include <linux/sysctl.h>#include <linux/init.h>#include <linux/spinlock.h>#include <net/net_namespace.h>#include <net/tcp_states.h>#include <net/ip.h>#include <net/arp.h>HLIST_HEAD(ax25_list);DEFINE_SPINLOCK(ax25_list_lock);static const struct proto_ops ax25_proto_ops;static void ax25_free_sock(struct sock *sk){	ax25_cb_put(ax25_sk(sk));}/* *	Socket removal during an interrupt is now safe. */static void ax25_cb_del(ax25_cb *ax25){	if (!hlist_unhashed(&ax25->ax25_node)) {		spin_lock_bh(&ax25_list_lock);		hlist_del_init(&ax25->ax25_node);		spin_unlock_bh(&ax25_list_lock);		ax25_cb_put(ax25);	}}/* *	Kill all bound sockets on a dropped device. */static void ax25_kill_by_device(struct net_device *dev){	ax25_dev *ax25_dev;	ax25_cb *s;	struct hlist_node *node;	if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)		return;	spin_lock_bh(&ax25_list_lock);again:	ax25_for_each(s, node, &ax25_list) {		if (s->ax25_dev == ax25_dev) {			s->ax25_dev = NULL;			spin_unlock_bh(&ax25_list_lock);			ax25_disconnect(s, ENETUNREACH);			spin_lock_bh(&ax25_list_lock);			/* The entry could have been deleted from the			 * list meanwhile and thus the next pointer is			 * no longer valid.  Play it safe and restart			 * the scan.  Forward progress is ensured			 * because we set s->ax25_dev to NULL and we			 * are never passed a NULL 'dev' argument.			 */			goto again;		}	}	spin_unlock_bh(&ax25_list_lock);}/* *	Handle device status changes. */static int ax25_device_event(struct notifier_block *this, unsigned long event,	void *ptr){	struct net_device *dev = (struct net_device *)ptr;	if (dev->nd_net != &init_net)		return NOTIFY_DONE;	/* Reject non AX.25 devices */	if (dev->type != ARPHRD_AX25)		return NOTIFY_DONE;	switch (event) {	case NETDEV_UP:		ax25_dev_device_up(dev);		break;	case NETDEV_DOWN:		ax25_kill_by_device(dev);		ax25_rt_device_down(dev);		ax25_dev_device_down(dev);		break;	default:		break;	}	return NOTIFY_DONE;}/* *	Add a socket to the bound sockets list. */void ax25_cb_add(ax25_cb *ax25){	spin_lock_bh(&ax25_list_lock);	ax25_cb_hold(ax25);	hlist_add_head(&ax25->ax25_node, &ax25_list);	spin_unlock_bh(&ax25_list_lock);}/* *	Find a socket that wants to accept the SABM we have just *	received. */struct sock *ax25_find_listener(ax25_address *addr, int digi,	struct net_device *dev, int type){	ax25_cb *s;	struct hlist_node *node;	spin_lock(&ax25_list_lock);	ax25_for_each(s, node, &ax25_list) {		if ((s->iamdigi && !digi) || (!s->iamdigi && digi))			continue;		if (s->sk && !ax25cmp(&s->source_addr, addr) &&		    s->sk->sk_type == type && s->sk->sk_state == TCP_LISTEN) {			/* If device is null we match any device */			if (s->ax25_dev == NULL || s->ax25_dev->dev == dev) {				sock_hold(s->sk);				spin_unlock(&ax25_list_lock);				return s->sk;			}		}	}	spin_unlock(&ax25_list_lock);	return NULL;}/* *	Find an AX.25 socket given both ends. */struct sock *ax25_get_socket(ax25_address *my_addr, ax25_address *dest_addr,	int type){	struct sock *sk = NULL;	ax25_cb *s;	struct hlist_node *node;	spin_lock(&ax25_list_lock);	ax25_for_each(s, node, &ax25_list) {		if (s->sk && !ax25cmp(&s->source_addr, my_addr) &&		    !ax25cmp(&s->dest_addr, dest_addr) &&		    s->sk->sk_type == type) {			sk = s->sk;			sock_hold(sk);			break;		}	}	spin_unlock(&ax25_list_lock);	return sk;}/* *	Find an AX.25 control block given both ends. It will only pick up *	floating AX.25 control blocks or non Raw socket bound control blocks. */ax25_cb *ax25_find_cb(ax25_address *src_addr, ax25_address *dest_addr,	ax25_digi *digi, struct net_device *dev){	ax25_cb *s;	struct hlist_node *node;	spin_lock_bh(&ax25_list_lock);	ax25_for_each(s, node, &ax25_list) {		if (s->sk && s->sk->sk_type != SOCK_SEQPACKET)			continue;		if (s->ax25_dev == NULL)			continue;		if (ax25cmp(&s->source_addr, src_addr) == 0 && ax25cmp(&s->dest_addr, dest_addr) == 0 && s->ax25_dev->dev == dev) {			if (digi != NULL && digi->ndigi != 0) {				if (s->digipeat == NULL)					continue;				if (ax25digicmp(s->digipeat, digi) != 0)					continue;			} else {				if (s->digipeat != NULL && s->digipeat->ndigi != 0)					continue;			}			ax25_cb_hold(s);			spin_unlock_bh(&ax25_list_lock);			return s;		}	}	spin_unlock_bh(&ax25_list_lock);	return NULL;}EXPORT_SYMBOL(ax25_find_cb);void ax25_send_to_raw(ax25_address *addr, struct sk_buff *skb, int proto){	ax25_cb *s;	struct sk_buff *copy;	struct hlist_node *node;	spin_lock(&ax25_list_lock);	ax25_for_each(s, node, &ax25_list) {		if (s->sk != NULL && ax25cmp(&s->source_addr, addr) == 0 &&		    s->sk->sk_type == SOCK_RAW &&		    s->sk->sk_protocol == proto &&		    s->ax25_dev->dev == skb->dev &&		    atomic_read(&s->sk->sk_rmem_alloc) <= s->sk->sk_rcvbuf) {			if ((copy = skb_clone(skb, GFP_ATOMIC)) == NULL)				continue;			if (sock_queue_rcv_skb(s->sk, copy) != 0)				kfree_skb(copy);		}	}	spin_unlock(&ax25_list_lock);}/* *	Deferred destroy. */void ax25_destroy_socket(ax25_cb *);/* *	Handler for deferred kills. */static void ax25_destroy_timer(unsigned long data){	ax25_cb *ax25=(ax25_cb *)data;	struct sock *sk;	sk=ax25->sk;	bh_lock_sock(sk);	sock_hold(sk);	ax25_destroy_socket(ax25);	bh_unlock_sock(sk);	sock_put(sk);}/* *	This is called from user mode and the timers. Thus it protects itself *	against interrupt users but doesn't worry about being called during *	work. Once it is removed from the queue no interrupt or bottom half *	will touch it and we are (fairly 8-) ) safe. */void ax25_destroy_socket(ax25_cb *ax25){	struct sk_buff *skb;	ax25_cb_del(ax25);	ax25_stop_heartbeat(ax25);	ax25_stop_t1timer(ax25);	ax25_stop_t2timer(ax25);	ax25_stop_t3timer(ax25);	ax25_stop_idletimer(ax25);	ax25_clear_queues(ax25);	/* Flush the queues */	if (ax25->sk != NULL) {		while ((skb = skb_dequeue(&ax25->sk->sk_receive_queue)) != NULL) {			if (skb->sk != ax25->sk) {				/* A pending connection */				ax25_cb *sax25 = ax25_sk(skb->sk);				/* Queue the unaccepted socket for death */				sock_orphan(skb->sk);				ax25_start_heartbeat(sax25);				sax25->state = AX25_STATE_0;			}			kfree_skb(skb);		}		skb_queue_purge(&ax25->sk->sk_write_queue);	}	if (ax25->sk != NULL) {		if (atomic_read(&ax25->sk->sk_wmem_alloc) ||		    atomic_read(&ax25->sk->sk_rmem_alloc)) {			/* Defer: outstanding buffers */			init_timer(&ax25->dtimer);			ax25->dtimer.expires  = jiffies + 2 * HZ;			ax25->dtimer.function = ax25_destroy_timer;			ax25->dtimer.data     = (unsigned long)ax25;			add_timer(&ax25->dtimer);		} else {			struct sock *sk=ax25->sk;			ax25->sk=NULL;			sock_put(sk);		}	} else {		ax25_cb_put(ax25);	}}/* * dl1bke 960311: set parameters for existing AX.25 connections, *		  includes a KILL command to abort any connection. *		  VERY useful for debugging ;-) */static int ax25_ctl_ioctl(const unsigned int cmd, void __user *arg){	struct ax25_ctl_struct ax25_ctl;	ax25_digi digi;	ax25_dev *ax25_dev;	ax25_cb *ax25;	unsigned int k;	if (copy_from_user(&ax25_ctl, arg, sizeof(ax25_ctl)))		return -EFAULT;	if ((ax25_dev = ax25_addr_ax25dev(&ax25_ctl.port_addr)) == NULL)		return -ENODEV;	if (ax25_ctl.digi_count > AX25_MAX_DIGIS)		return -EINVAL;	digi.ndigi = ax25_ctl.digi_count;	for (k = 0; k < digi.ndigi; k++)		digi.calls[k] = ax25_ctl.digi_addr[k];	if ((ax25 = ax25_find_cb(&ax25_ctl.source_addr, &ax25_ctl.dest_addr, &digi, ax25_dev->dev)) == NULL)		return -ENOTCONN;	switch (ax25_ctl.cmd) {	case AX25_KILL:		ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);#ifdef CONFIG_AX25_DAMA_SLAVE		if (ax25_dev->dama.slave && ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_DAMA_SLAVE)			ax25_dama_off(ax25);#endif		ax25_disconnect(ax25, ENETRESET);		break;	case AX25_WINDOW:		if (ax25->modulus == AX25_MODULUS) {			if (ax25_ctl.arg < 1 || ax25_ctl.arg > 7)				return -EINVAL;		} else {			if (ax25_ctl.arg < 1 || ax25_ctl.arg > 63)				return -EINVAL;		}		ax25->window = ax25_ctl.arg;		break;	case AX25_T1:		if (ax25_ctl.arg < 1)			return -EINVAL;		ax25->rtt = (ax25_ctl.arg * HZ) / 2;		ax25->t1  = ax25_ctl.arg * HZ;		break;	case AX25_T2:		if (ax25_ctl.arg < 1)			return -EINVAL;		ax25->t2 = ax25_ctl.arg * HZ;		break;	case AX25_N2:		if (ax25_ctl.arg < 1 || ax25_ctl.arg > 31)			return -EINVAL;		ax25->n2count = 0;		ax25->n2 = ax25_ctl.arg;		break;	case AX25_T3:		if (ax25_ctl.arg < 0)			return -EINVAL;		ax25->t3 = ax25_ctl.arg * HZ;		break;	case AX25_IDLE:		if (ax25_ctl.arg < 0)			return -EINVAL;		ax25->idle = ax25_ctl.arg * 60 * HZ;		break;	case AX25_PACLEN:		if (ax25_ctl.arg < 16 || ax25_ctl.arg > 65535)			return -EINVAL;		ax25->paclen = ax25_ctl.arg;		break;	default:		return -EINVAL;	  }	return 0;}static void ax25_fillin_cb_from_dev(ax25_cb *ax25, ax25_dev *ax25_dev){	ax25->rtt     = msecs_to_jiffies(ax25_dev->values[AX25_VALUES_T1]) / 2;	ax25->t1      = msecs_to_jiffies(ax25_dev->values[AX25_VALUES_T1]);	ax25->t2      = msecs_to_jiffies(ax25_dev->values[AX25_VALUES_T2]);	ax25->t3      = msecs_to_jiffies(ax25_dev->values[AX25_VALUES_T3]);	ax25->n2      = ax25_dev->values[AX25_VALUES_N2];	ax25->paclen  = ax25_dev->values[AX25_VALUES_PACLEN];	ax25->idle    = msecs_to_jiffies(ax25_dev->values[AX25_VALUES_IDLE]);	ax25->backoff = ax25_dev->values[AX25_VALUES_BACKOFF];	if (ax25_dev->values[AX25_VALUES_AXDEFMODE]) {		ax25->modulus = AX25_EMODULUS;		ax25->window  = ax25_dev->values[AX25_VALUES_EWINDOW];	} else {		ax25->modulus = AX25_MODULUS;		ax25->window  = ax25_dev->values[AX25_VALUES_WINDOW];	}}/* *	Fill in a created AX.25 created control block with the default *	values for a particular device. */void ax25_fillin_cb(ax25_cb *ax25, ax25_dev *ax25_dev){	ax25->ax25_dev = ax25_dev;	if (ax25->ax25_dev != NULL) {		ax25_fillin_cb_from_dev(ax25, ax25_dev);		return;	}	/*	 * No device, use kernel / AX.25 spec default values	 */	ax25->rtt     = msecs_to_jiffies(AX25_DEF_T1) / 2;	ax25->t1      = msecs_to_jiffies(AX25_DEF_T1);	ax25->t2      = msecs_to_jiffies(AX25_DEF_T2);	ax25->t3      = msecs_to_jiffies(AX25_DEF_T3);	ax25->n2      = AX25_DEF_N2;	ax25->paclen  = AX25_DEF_PACLEN;	ax25->idle    = msecs_to_jiffies(AX25_DEF_IDLE);	ax25->backoff = AX25_DEF_BACKOFF;	if (AX25_DEF_AXDEFMODE) {		ax25->modulus = AX25_EMODULUS;		ax25->window  = AX25_DEF_EWINDOW;	} else {		ax25->modulus = AX25_MODULUS;		ax25->window  = AX25_DEF_WINDOW;	}}/* * Create an empty AX.25 control block. */ax25_cb *ax25_create_cb(void){	ax25_cb *ax25;	if ((ax25 = kzalloc(sizeof(*ax25), GFP_ATOMIC)) == NULL)		return NULL;	atomic_set(&ax25->refcount, 1);	skb_queue_head_init(&ax25->write_queue);	skb_queue_head_init(&ax25->frag_queue);	skb_queue_head_init(&ax25->ack_queue);	skb_queue_head_init(&ax25->reseq_queue);	init_timer(&ax25->timer);	init_timer(&ax25->t1timer);	init_timer(&ax25->t2timer);	init_timer(&ax25->t3timer);	init_timer(&ax25->idletimer);	ax25_fillin_cb(ax25, NULL);	ax25->state = AX25_STATE_0;	return ax25;}/* *	Handling for system calls applied via the various interfaces to an *	AX25 socket object */static int ax25_setsockopt(struct socket *sock, int level, int optname,	char __user *optval, int optlen){	struct sock *sk = sock->sk;	ax25_cb *ax25;	struct net_device *dev;	char devname[IFNAMSIZ];	int opt, res = 0;	if (level != SOL_AX25)		return -ENOPROTOOPT;	if (optlen < sizeof(int))		return -EINVAL;	if (get_user(opt, (int __user *)optval))		return -EFAULT;	lock_sock(sk);	ax25 = ax25_sk(sk);	switch (optname) {	case AX25_WINDOW:		if (ax25->modulus == AX25_MODULUS) {			if (opt < 1 || opt > 7) {				res = -EINVAL;				break;			}		} else {			if (opt < 1 || opt > 63) {				res = -EINVAL;				break;			}		}		ax25->window = opt;		break;	case AX25_T1:		if (opt < 1) {			res = -EINVAL;			break;		}		ax25->rtt = (opt * HZ) / 2;		ax25->t1  = opt * HZ;		break;	case AX25_T2:		if (opt < 1) {			res = -EINVAL;			break;		}		ax25->t2 = opt * HZ;		break;	case AX25_N2:		if (opt < 1 || opt > 31) {			res = -EINVAL;			break;		}		ax25->n2 = opt;		break;	case AX25_T3:		if (opt < 1) {			res = -EINVAL;			break;		}		ax25->t3 = opt * HZ;		break;	case AX25_IDLE:		if (opt < 0) {			res = -EINVAL;			break;		}		ax25->idle = opt * 60 * HZ;		break;	case AX25_BACKOFF:		if (opt < 0 || opt > 2) {			res = -EINVAL;			break;		}		ax25->backoff = opt;		break;	case AX25_EXTSEQ:		ax25->modulus = opt ? AX25_EMODULUS : AX25_MODULUS;		break;	case AX25_PIDINCL:		ax25->pidincl = opt ? 1 : 0;		break;	case AX25_IAMDIGI:		ax25->iamdigi = opt ? 1 : 0;		break;	case AX25_PACLEN:		if (opt < 16 || opt > 65535) {			res = -EINVAL;			break;		}		ax25->paclen = opt;		break;	case SO_BINDTODEVICE:		if (optlen > IFNAMSIZ)			optlen=IFNAMSIZ;		if (copy_from_user(devname, optval, optlen)) {		res = -EFAULT;			break;		}		dev = dev_get_by_name(&init_net, devname);		if (dev == NULL) {			res = -ENODEV;			break;		}		if (sk->sk_type == SOCK_SEQPACKET &&		   (sock->state != SS_UNCONNECTED ||		    sk->sk_state == TCP_LISTEN)) {			res = -EADDRNOTAVAIL;			dev_put(dev);			break;		}		ax25->ax25_dev = ax25_dev_ax25dev(dev);		ax25_fillin_cb(ax25, ax25->ax25_dev);		break;	default:		res = -ENOPROTOOPT;	}	release_sock(sk);	return res;}static int ax25_getsockopt(struct socket *sock, int level, int optname,	char __user *optval, int __user *optlen){	struct sock *sk = sock->sk;	ax25_cb *ax25;	struct ax25_dev *ax25_dev;	char devname[IFNAMSIZ];	void *valptr;	int val = 0;	int maxlen, length;	if (level != SOL_AX25)		return -ENOPROTOOPT;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -