📄 af_decnet.c
字号:
/* * 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 + -