📄 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. *//****************************************************************************** (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/config.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 <linux/netdevice.h>#include <linux/inet.h>#include <linux/route.h>#include <linux/netfilter.h>#include <net/sock.h>#include <asm/segment.h>#include <asm/system.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/neighbour.h>#include <net/dst.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>#define MAX(a,b) ((a)>(b)?(a):(b))static void dn_keepalive(struct sock *sk);/* * decnet_address is kept in network order, decnet_ether_address is kept * as a string of bytes. */dn_address decnet_address = 0;unsigned char decnet_ether_address[ETH_ALEN] = { 0xAA, 0x00, 0x04, 0x00, 0x00, 0x00 };static struct proto_ops dn_proto_ops;rwlock_t dn_hash_lock = RW_LOCK_UNLOCKED;static struct sock *dn_sklist = NULL;static struct sock *dn_wild_sk = NULL;static int __dn_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen, int flags);static int __dn_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen, int flags);static struct sock **dn_find_list(struct sock *sk){ struct dn_scp *scp = &sk->protinfo.dn; if (scp->addr.sdn_flags & SDF_WILD) return dn_wild_sk ? NULL : &dn_wild_sk; return &dn_sklist;}static unsigned short port_alloc(struct sock *sk){ struct dn_scp *scp = &sk->protinfo.dn;static unsigned short port = 0x2000; if (port == 0) port++; scp->addrloc = 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 = &sk->protinfo.dn; struct sock **skp; int rv = -EUSERS; write_lock_bh(&dn_hash_lock); if (!scp->addrloc && !port_alloc(sk)) goto out; rv = -EADDRINUSE; if ((skp = dn_find_list(sk)) == NULL) goto out; sk->next = *skp; sk->pprev = skp; *skp = sk; rv = 0;out: write_unlock_bh(&dn_hash_lock); return rv;}static void dn_unhash_sock(struct sock *sk){ struct sock **skp = sk->pprev; if (skp == NULL) return; write_lock(&dn_hash_lock); while(*skp != sk) skp = &((*skp)->next); *skp = sk->next; write_unlock(&dn_hash_lock); sk->next = NULL; sk->pprev = NULL;}static void dn_unhash_sock_bh(struct sock *sk){ struct sock **skp = sk->pprev; if (skp == NULL) return; write_lock_bh(&dn_hash_lock); while(*skp != sk) skp = &((*skp)->next); *skp = sk->next; write_unlock_bh(&dn_hash_lock); sk->next = NULL; sk->pprev = NULL;}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 sock *sk; read_lock(&dn_hash_lock); for(sk = dn_sklist; sk != NULL; sk = sk->next) { struct dn_scp *scp = &sk->protinfo.dn; if (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; } if (dn_wild_sk && (dn_wild_sk->state == TCP_LISTEN)) sock_hold((sk = dn_wild_sk)); read_unlock(&dn_hash_lock); return sk;}struct sock *dn_find_by_skb(struct sk_buff *skb){ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb; struct sock *sk; struct dn_scp *scp; read_lock(&dn_hash_lock); for(sk = dn_sklist; sk != NULL; sk = sk->next) { scp = &sk->protinfo.dn; if (cb->src != dn_saddr2dn(&scp->peer)) continue; if (cb->dst_port != scp->addrloc) continue; if (scp->addrrem && (cb->src_port != scp->addrrem)) continue; break; } if (sk) sock_hold(sk); read_unlock(&dn_hash_lock); return sk;}static void dn_destruct(struct sock *sk){ struct dn_scp *scp = &sk->protinfo.dn; 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->dst_cache, NULL)); MOD_DEC_USE_COUNT;}struct sock *dn_alloc_sock(struct socket *sock, int flags){ struct sock *sk; struct dn_scp *scp; if ((sk = sk_alloc(PF_DECnet, flags, 1)) == NULL) goto no_sock; if (sock) { sock->ops = &dn_proto_ops; } sock_init_data(sock,sk); scp = &sk->protinfo.dn; sk->backlog_rcv = dn_nsp_backlog_rcv; sk->destruct = dn_destruct; sk->no_check = 1; sk->family = PF_DECnet; sk->protocol = 0; /* Initialization of DECnet Session Control Port */ 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->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->mss = 1460; 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); MOD_INC_USE_COUNT; return sk;no_sock: return NULL;}/* * Keepalive timer. * FIXME: Should respond to SO_KEEPALIVE etc. */static void dn_keepalive(struct sock *sk){ struct dn_scp *scp = &sk->protinfo.dn; /* * 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_len(&scp->other_xmit_queue) == 0) dn_nsp_send_lnk(sk, DN_NOCHANGE);}/* * 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 = &sk->protinfo.dn; 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->socket) return 0; dn_stop_fast_timer(sk); /* unlikely, but possible that this is runninng */ if ((jiffies - scp->stamp) >= (HZ * decnet_time_wait)) { dn_unhash_sock(sk); sock_put(sk); return 1; } return 0;}static void dn_destroy_sock(struct sock *sk){ struct dn_scp *scp = &sk->protinfo.dn; scp->nsp_rxtshift = 0; /* reset back off */ if (sk->socket) { if (sk->socket->state != SS_UNCONNECTED) sk->socket->state = SS_DISCONNECTING; } sk->state = TCP_CLOSE;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -