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

📄 af_decnet.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * DECnet       An implementation of the DECnet protocol suite for the LINUX *              operating system.  DECnet is implemented using the  BSD Socket *              interface as the means of communication with the user level. * *              DECnet Socket Layer Interface * * Authors:     Eduardo Marcelo Serrat <emserrat@geocities.com> *              Patrick Caulfield <patrick@pandh.demon.co.uk> * * Changes: *        Steve Whitehouse: Copied from Eduardo Serrat and Patrick Caulfield's *                          version of the code. Original copyright preserved *                          below. *        Steve Whitehouse: Some bug fixes, cleaning up some code to make it *                          compatible with my routing layer. *        Steve Whitehouse: Merging changes from Eduardo Serrat and Patrick *                          Caulfield. *        Steve Whitehouse: Further bug fixes, checking module code still works *                          with new routing layer. *        Steve Whitehouse: Additional set/get_sockopt() calls. *        Steve Whitehouse: Fixed TIOCINQ ioctl to be same as Eduardo's new *                          code. *        Steve Whitehouse: recvmsg() changed to try and behave in a POSIX like *                          way. Didn't manage it entirely, but its better. *        Steve Whitehouse: ditto for sendmsg(). *        Steve Whitehouse: A selection of bug fixes to various things. *        Steve Whitehouse: Added TIOCOUTQ ioctl. *        Steve Whitehouse: Fixes to username2sockaddr & sockaddr2username. *        Steve Whitehouse: Fixes to connect() error returns. *       Patrick Caulfield: Fixes to delayed acceptance logic. *         David S. Miller: New socket locking *        Steve Whitehouse: Socket list hashing/locking *         Arnaldo C. Melo: use capable, not suser *        Steve Whitehouse: Removed unused code. Fix to use sk->allocation *                          when required. *       Patrick Caulfield: /proc/net/decnet now has object name/number *        Steve Whitehouse: Fixed local port allocation, hashed sk list *          Matthew Wilcox: Fixes for dn_ioctl() *        Steve Whitehouse: New connect/accept logic to allow timeouts and *                          prepare for sendpage etc. *//******************************************************************************    (c) 1995-1998 E.M. Serrat		emserrat@geocities.com    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    any later version.    This program is distributed in the hope that it will be useful,    but WITHOUT ANY WARRANTY; without even the implied warranty of    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    GNU General Public License for more details.HISTORY:Version           Kernel     Date       Author/Comments-------           ------     ----       ---------------Version 0.0.1     2.0.30    01-dic-97	Eduardo Marcelo Serrat					(emserrat@geocities.com)					First Development of DECnet Socket La-					yer for Linux. Only supports outgoing					connections.Version 0.0.2	  2.1.105   20-jun-98   Patrick J. Caulfield					(patrick@pandh.demon.co.uk)					Port to new kernel development version.Version 0.0.3     2.1.106   25-jun-98   Eduardo Marcelo Serrat					(emserrat@geocities.com)					_					Added support for incoming connections					so we can start developing server apps					on Linux.					-					Module SupportVersion 0.0.4     2.1.109   21-jul-98   Eduardo Marcelo Serrat				       (emserrat@geocities.com)				       _					Added support for X11R6.4. Now we can					use DECnet transport for X on Linux!!!				       -Version 0.0.5    2.1.110   01-aug-98   Eduardo Marcelo Serrat				       (emserrat@geocities.com)				       Removed bugs on flow control				       Removed bugs on incoming accessdata				       order				       -Version 0.0.6    2.1.110   07-aug-98   Eduardo Marcelo Serrat				       dn_recvmsg fixes					Patrick J. Caulfield				       dn_bind fixes*******************************************************************************/#include <linux/module.h>#include <linux/errno.h>#include <linux/types.h>#include <linux/slab.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 <linux/netdevice.h>#include <linux/inet.h>#include <linux/route.h>#include <linux/netfilter.h>#include <linux/seq_file.h>#include <net/sock.h>#include <net/tcp_states.h>#include <net/flow.h>#include <asm/system.h>#include <asm/ioctls.h>#include <linux/capability.h>#include <linux/mm.h>#include <linux/interrupt.h>#include <linux/proc_fs.h>#include <linux/stat.h>#include <linux/init.h>#include <linux/poll.h>#include <net/net_namespace.h>#include <net/neighbour.h>#include <net/dst.h>#include <net/fib_rules.h>#include <net/dn.h>#include <net/dn_nsp.h>#include <net/dn_dev.h>#include <net/dn_route.h>#include <net/dn_fib.h>#include <net/dn_neigh.h>struct dn_sock {	struct sock sk;	struct dn_scp scp;};static void dn_keepalive(struct sock *sk);#define DN_SK_HASH_SHIFT 8#define DN_SK_HASH_SIZE (1 << DN_SK_HASH_SHIFT)#define DN_SK_HASH_MASK (DN_SK_HASH_SIZE - 1)static const struct proto_ops dn_proto_ops;static DEFINE_RWLOCK(dn_hash_lock);static struct hlist_head dn_sk_hash[DN_SK_HASH_SIZE];static struct hlist_head dn_wild_sk;static atomic_t decnet_memory_allocated;static int __dn_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen, int flags);static int __dn_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen, int flags);static struct hlist_head *dn_find_list(struct sock *sk){	struct dn_scp *scp = DN_SK(sk);	if (scp->addr.sdn_flags & SDF_WILD)		return hlist_empty(&dn_wild_sk) ? &dn_wild_sk : NULL;	return &dn_sk_hash[dn_ntohs(scp->addrloc) & DN_SK_HASH_MASK];}/* * Valid ports are those greater than zero and not already in use. */static int check_port(__le16 port){	struct sock *sk;	struct hlist_node *node;	if (port == 0)		return -1;	sk_for_each(sk, node, &dn_sk_hash[dn_ntohs(port) & DN_SK_HASH_MASK]) {		struct dn_scp *scp = DN_SK(sk);		if (scp->addrloc == port)			return -1;	}	return 0;}static unsigned short port_alloc(struct sock *sk){	struct dn_scp *scp = DN_SK(sk);static unsigned short port = 0x2000;	unsigned short i_port = port;	while(check_port(dn_htons(++port)) != 0) {		if (port == i_port)			return 0;	}	scp->addrloc = dn_htons(port);	return 1;}/* * Since this is only ever called from user * level, we don't need a write_lock() version * of this. */static int dn_hash_sock(struct sock *sk){	struct dn_scp *scp = DN_SK(sk);	struct hlist_head *list;	int rv = -EUSERS;	BUG_ON(sk_hashed(sk));	write_lock_bh(&dn_hash_lock);	if (!scp->addrloc && !port_alloc(sk))		goto out;	rv = -EADDRINUSE;	if ((list = dn_find_list(sk)) == NULL)		goto out;	sk_add_node(sk, list);	rv = 0;out:	write_unlock_bh(&dn_hash_lock);	return rv;}static void dn_unhash_sock(struct sock *sk){	write_lock(&dn_hash_lock);	sk_del_node_init(sk);	write_unlock(&dn_hash_lock);}static void dn_unhash_sock_bh(struct sock *sk){	write_lock_bh(&dn_hash_lock);	sk_del_node_init(sk);	write_unlock_bh(&dn_hash_lock);}static struct hlist_head *listen_hash(struct sockaddr_dn *addr){	int i;	unsigned hash = addr->sdn_objnum;	if (hash == 0) {		hash = addr->sdn_objnamel;		for(i = 0; i < dn_ntohs(addr->sdn_objnamel); i++) {			hash ^= addr->sdn_objname[i];			hash ^= (hash << 3);		}	}	return &dn_sk_hash[hash & DN_SK_HASH_MASK];}/* * Called to transform a socket from bound (i.e. with a local address) * into a listening socket (doesn't need a local port number) and rehashes * based upon the object name/number. */static void dn_rehash_sock(struct sock *sk){	struct hlist_head *list;	struct dn_scp *scp = DN_SK(sk);	if (scp->addr.sdn_flags & SDF_WILD)		return;	write_lock_bh(&dn_hash_lock);	sk_del_node_init(sk);	DN_SK(sk)->addrloc = 0;	list = listen_hash(&DN_SK(sk)->addr);	sk_add_node(sk, list);	write_unlock_bh(&dn_hash_lock);}int dn_sockaddr2username(struct sockaddr_dn *sdn, unsigned char *buf, unsigned char type){	int len = 2;	*buf++ = type;	switch(type) {		case 0:			*buf++ = sdn->sdn_objnum;			break;		case 1:			*buf++ = 0;			*buf++ = dn_ntohs(sdn->sdn_objnamel);			memcpy(buf, sdn->sdn_objname, dn_ntohs(sdn->sdn_objnamel));			len = 3 + dn_ntohs(sdn->sdn_objnamel);			break;		case 2:			memset(buf, 0, 5);			buf += 5;			*buf++ = dn_ntohs(sdn->sdn_objnamel);			memcpy(buf, sdn->sdn_objname, dn_ntohs(sdn->sdn_objnamel));			len = 7 + dn_ntohs(sdn->sdn_objnamel);			break;	}	return len;}/* * On reception of usernames, we handle types 1 and 0 for destination * addresses only. Types 2 and 4 are used for source addresses, but the * UIC, GIC are ignored and they are both treated the same way. Type 3 * is never used as I've no idea what its purpose might be or what its * format is. */int dn_username2sockaddr(unsigned char *data, int len, struct sockaddr_dn *sdn, unsigned char *fmt){	unsigned char type;	int size = len;	int namel = 12;	sdn->sdn_objnum = 0;	sdn->sdn_objnamel = dn_htons(0);	memset(sdn->sdn_objname, 0, DN_MAXOBJL);	if (len < 2)		return -1;	len -= 2;	*fmt = *data++;	type = *data++;	switch(*fmt) {		case 0:			sdn->sdn_objnum = type;			return 2;		case 1:			namel = 16;			break;		case 2:			len  -= 4;			data += 4;			break;		case 4:			len  -= 8;			data += 8;			break;		default:			return -1;	}	len -= 1;	if (len < 0)		return -1;	sdn->sdn_objnamel = dn_htons(*data++);	len -= dn_ntohs(sdn->sdn_objnamel);	if ((len < 0) || (dn_ntohs(sdn->sdn_objnamel) > namel))		return -1;	memcpy(sdn->sdn_objname, data, dn_ntohs(sdn->sdn_objnamel));	return size - len;}struct sock *dn_sklist_find_listener(struct sockaddr_dn *addr){	struct hlist_head *list = listen_hash(addr);	struct hlist_node *node;	struct sock *sk;	read_lock(&dn_hash_lock);	sk_for_each(sk, node, list) {		struct dn_scp *scp = DN_SK(sk);		if (sk->sk_state != TCP_LISTEN)			continue;		if (scp->addr.sdn_objnum) {			if (scp->addr.sdn_objnum != addr->sdn_objnum)				continue;		} else {			if (addr->sdn_objnum)				continue;			if (scp->addr.sdn_objnamel != addr->sdn_objnamel)				continue;			if (memcmp(scp->addr.sdn_objname, addr->sdn_objname, dn_ntohs(addr->sdn_objnamel)) != 0)				continue;		}		sock_hold(sk);		read_unlock(&dn_hash_lock);		return sk;	}	sk = sk_head(&dn_wild_sk);	if (sk) {		if (sk->sk_state == TCP_LISTEN)			sock_hold(sk);		else			sk = NULL;	}	read_unlock(&dn_hash_lock);	return sk;}struct sock *dn_find_by_skb(struct sk_buff *skb){	struct dn_skb_cb *cb = DN_SKB_CB(skb);	struct sock *sk;	struct hlist_node *node;	struct dn_scp *scp;	read_lock(&dn_hash_lock);	sk_for_each(sk, node, &dn_sk_hash[dn_ntohs(cb->dst_port) & DN_SK_HASH_MASK]) {		scp = DN_SK(sk);		if (cb->src != dn_saddr2dn(&scp->peer))			continue;		if (cb->dst_port != scp->addrloc)			continue;		if (scp->addrrem && (cb->src_port != scp->addrrem))			continue;		sock_hold(sk);		goto found;	}	sk = NULL;found:	read_unlock(&dn_hash_lock);	return sk;}static void dn_destruct(struct sock *sk){	struct dn_scp *scp = DN_SK(sk);	skb_queue_purge(&scp->data_xmit_queue);	skb_queue_purge(&scp->other_xmit_queue);	skb_queue_purge(&scp->other_receive_queue);	dst_release(xchg(&sk->sk_dst_cache, NULL));}static int dn_memory_pressure;static void dn_enter_memory_pressure(void){	if (!dn_memory_pressure) {		dn_memory_pressure = 1;	}}static struct proto dn_proto = {	.name			= "NSP",	.owner			= THIS_MODULE,	.enter_memory_pressure	= dn_enter_memory_pressure,	.memory_pressure	= &dn_memory_pressure,	.memory_allocated	= &decnet_memory_allocated,	.sysctl_mem		= sysctl_decnet_mem,	.sysctl_wmem		= sysctl_decnet_wmem,	.sysctl_rmem		= sysctl_decnet_rmem,	.max_header		= DN_MAX_NSP_DATA_HEADER + 64,	.obj_size		= sizeof(struct dn_sock),};static struct sock *dn_alloc_sock(struct net *net, struct socket *sock, gfp_t gfp){	struct dn_scp *scp;	struct sock *sk = sk_alloc(net, PF_DECnet, gfp, &dn_proto);	if  (!sk)		goto out;	if (sock)		sock->ops = &dn_proto_ops;	sock_init_data(sock, sk);	sk->sk_backlog_rcv = dn_nsp_backlog_rcv;	sk->sk_destruct    = dn_destruct;	sk->sk_no_check    = 1;	sk->sk_family      = PF_DECnet;	sk->sk_protocol    = 0;	sk->sk_allocation  = gfp;	sk->sk_sndbuf	   = sysctl_decnet_wmem[1];	sk->sk_rcvbuf	   = sysctl_decnet_rmem[1];	/* Initialization of DECnet Session Control Port		*/	scp = DN_SK(sk);	scp->state	= DN_O;		/* Open			*/	scp->numdat	= 1;		/* Next data seg to tx	*/	scp->numoth	= 1;		/* Next oth data to tx  */	scp->ackxmt_dat = 0;		/* Last data seg ack'ed */	scp->ackxmt_oth = 0;		/* Last oth data ack'ed */	scp->ackrcv_dat = 0;		/* Highest data ack recv*/	scp->ackrcv_oth = 0;		/* Last oth data ack rec*/	scp->flowrem_sw = DN_SEND;	scp->flowloc_sw = DN_SEND;	scp->flowrem_dat = 0;	scp->flowrem_oth = 1;	scp->flowloc_dat = 0;	scp->flowloc_oth = 1;	scp->services_rem = 0;	scp->services_loc = 1 | NSP_FC_NONE;	scp->info_rem = 0;	scp->info_loc = 0x03; /* NSP version 4.1 */	scp->segsize_rem = 230 - DN_MAX_NSP_DATA_HEADER; /* Default: Updated by remote segsize */	scp->nonagle = 0;	scp->multi_ireq = 1;	scp->accept_mode = ACC_IMMED;	scp->addr.sdn_family    = AF_DECnet;	scp->peer.sdn_family    = AF_DECnet;	scp->accessdata.acc_accl = 5;	memcpy(scp->accessdata.acc_acc, "LINUX", 5);	scp->max_window   = NSP_MAX_WINDOW;	scp->snd_window   = NSP_MIN_WINDOW;	scp->nsp_srtt     = NSP_INITIAL_SRTT;	scp->nsp_rttvar   = NSP_INITIAL_RTTVAR;	scp->nsp_rxtshift = 0;	skb_queue_head_init(&scp->data_xmit_queue);	skb_queue_head_init(&scp->other_xmit_queue);	skb_queue_head_init(&scp->other_receive_queue);	scp->persist = 0;	scp->persist_fxn = NULL;	scp->keepalive = 10 * HZ;	scp->keepalive_fxn = dn_keepalive;	init_timer(&scp->delack_timer);	scp->delack_pending = 0;	scp->delack_fxn = dn_nsp_delayed_ack;	dn_start_slow_timer(sk);out:	return sk;}/* * Keepalive timer. * FIXME: Should respond to SO_KEEPALIVE etc. */static void dn_keepalive(struct sock *sk){	struct dn_scp *scp = DN_SK(sk);	/*	 * By checking the other_data transmit queue is empty	 * we are double checking that we are not sending too	 * many of these keepalive frames.	 */	if (skb_queue_empty(&scp->other_xmit_queue))		dn_nsp_send_link(sk, DN_NOCHANGE, 0);}/* * Timer for shutdown/destroyed sockets. * When socket is dead & no packets have been sent for a * certain amount of time, they are removed by this * routine. Also takes care of sending out DI & DC * frames at correct times. */int dn_destroy_timer(struct sock *sk){	struct dn_scp *scp = DN_SK(sk);	scp->persist = dn_nsp_persist(sk);	switch(scp->state) {		case DN_DI:			dn_nsp_send_disc(sk, NSP_DISCINIT, 0, GFP_ATOMIC);			if (scp->nsp_rxtshift >= decnet_di_count)				scp->state = DN_CN;			return 0;		case DN_DR:			dn_nsp_send_disc(sk, NSP_DISCINIT, 0, GFP_ATOMIC);			if (scp->nsp_rxtshift >= decnet_dr_count)				scp->state = DN_DRC;			return 0;		case DN_DN:			if (scp->nsp_rxtshift < decnet_dn_count) {				/* printk(KERN_DEBUG "dn_destroy_timer: DN\n"); */				dn_nsp_send_disc(sk, NSP_DISCCONF, NSP_REASON_DC, GFP_ATOMIC);				return 0;			}	}	scp->persist = (HZ * decnet_time_wait);	if (sk->sk_socket)		return 0;	if ((jiffies - scp->stamp) >= (HZ * decnet_time_wait)) {		dn_unhash_sock(sk);		sock_put(sk);		return 1;	}	return 0;}

⌨️ 快捷键说明

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