patch-2.3.15 linux/net/decnet/af_decnet.c

Next file: linux/net/decnet/dn_dev.c
Previous file: linux/net/decnet/TODO
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.14/linux/net/decnet/af_decnet.c linux/net/decnet/af_decnet.c
@@ -30,6 +30,8 @@
  *        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
  */
 
 
@@ -136,11 +138,99 @@
 int decnet_node_type = DN_RT_INFO_ENDN;
 
 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 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)
 {
@@ -154,16 +244,16 @@
 			break;
 		case 1:
 			*buf++ = 0;
-			*buf++ = sdn->sdn_objnamel;
-			memcpy(buf, sdn->sdn_objname, sdn->sdn_objnamel);
-			len = 3 + sdn->sdn_objnamel;
+			*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++ = sdn->sdn_objnamel;
-			memcpy(buf, sdn->sdn_objname, sdn->sdn_objnamel);
-			len = 7 + sdn->sdn_objnamel;
+			*buf++ = dn_ntohs(sdn->sdn_objnamel);
+			memcpy(buf, sdn->sdn_objname, dn_ntohs(sdn->sdn_objnamel));
+			len = 7 + dn_ntohs(sdn->sdn_objnamel);
 			break;
 	}
 
@@ -184,7 +274,7 @@
 	int namel = 12;
 
 	sdn->sdn_objnum = 0;
-	sdn->sdn_objnamel = 0;
+	sdn->sdn_objnamel = dn_htons(0);
 	memset(sdn->sdn_objname, 0, DN_MAXOBJL);
 
 	if (len < 2)
@@ -196,7 +286,7 @@
 
 	switch(*fmt) {
 		case 0:
-			sdn->sdn_objnum = type;
+			sdn->sdn_objnum = dn_htons(type);
 			return 2;
 		case 1:
 			namel = 16;
@@ -218,13 +308,13 @@
 	if (len < 0)
 		return -1;
 
-	sdn->sdn_objnamel = *data++;
-	len -= sdn->sdn_objnamel;
+	sdn->sdn_objnamel = dn_htons(*data++);
+	len -= dn_ntohs(sdn->sdn_objnamel);
 
-	if ((len < 0) || (sdn->sdn_objnamel > namel))
+	if ((len < 0) || (dn_ntohs(sdn->sdn_objnamel) > namel))
 		return -1;
 
-	memcpy(sdn->sdn_objname, data, sdn->sdn_objnamel);
+	memcpy(sdn->sdn_objname, data, dn_ntohs(sdn->sdn_objnamel));
 
 	return size - len;
 }
@@ -233,6 +323,7 @@
 {
 	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)
@@ -245,64 +336,28 @@
 				continue;
 			if (scp->addr.sdn_objnamel != addr->sdn_objnamel)
 				continue;
-			if (memcmp(scp->addr.sdn_objname, addr->sdn_objname, addr->sdn_objnamel) != 0)
+			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;
 	}
 
-	return (dn_wild_sk && (dn_wild_sk->state == TCP_LISTEN)) ? dn_wild_sk : NULL;
-}
-
-struct sock *dn_sklist_find(unsigned short port)
-{
-        struct sock *s;
-
-        for (s = dn_sklist; s != NULL; s = s->next) {
-                if (s->protinfo.dn.addrloc == port) {
-                        return s;
-                }
-        }
-
-        return NULL;
-}
-
-static struct sock *dn_sklist_find_by_objnum(unsigned char objnum)
-{
-        struct sock *s;
-
-        for (s = dn_sklist; s != NULL; s = s->next) {
-                if ((s->protinfo.dn.addr.sdn_objnum == objnum) &&
-                     (s->state == TCP_LISTEN)) {
-                        return s;
-                }
-        }
-        return NULL;
-}
+	if (dn_wild_sk && (dn_wild_sk->state == TCP_LISTEN))
+		sock_hold((sk = dn_wild_sk));
 
-static struct sock *dn_sklist_find_by_name(char *name)
-{
-       struct sock *s;
-
-       for (s = dn_sklist; s != NULL; s = s->next) {
-		if (s->protinfo.dn.addr.sdn_objnum)
-			continue;
-               if ((memcmp(s->protinfo.dn.addr.sdn_objname,name,
-                           s->protinfo.dn.addr.sdn_objnamel) == 0) 
-                     && (s->state == TCP_LISTEN)) {
-                       return s;
-               }
-       }
-       return NULL;
+	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))
@@ -314,27 +369,14 @@
 		break;
 	}
 
-	return sk;
-}
-
-
-unsigned short dn_alloc_port(void)
-{
-	struct sock *sk;
-	static unsigned short dn_port = 0x2000;
-	short port;
+	if (sk)
+		sock_hold(sk);
 
-	start_bh_atomic();
+	read_unlock(&dn_hash_lock);
 
-	do {
-		port = dn_port++;
-		sk = dn_sklist_find(port);
-	} while((sk != NULL) || (port == 0));
-
-	end_bh_atomic();
+	return sk;
+}
 
-        return dn_htons(port);
-};
 
 
 static void dn_destruct(struct sock *sk)
@@ -453,14 +495,12 @@
 
 	switch(scp->state) {
 		case DN_DI:
-			/* printk(KERN_DEBUG "dn_destroy_timer: DI\n"); */
 			dn_send_disc(sk, NSP_DISCINIT, 0);
 			if (scp->nsp_rxtshift >= decnet_di_count)
 				scp->state = DN_CN;
 			return 0;
 
 		case DN_DR:
-			/* printk(KERN_DEBUG "dn_destroy_timer: DR\n"); */
 			dn_send_disc(sk, NSP_DISCINIT, 0);
 			if (scp->nsp_rxtshift >= decnet_dr_count)
 				scp->state = DN_DRC;
@@ -476,19 +516,16 @@
 
 	scp->persist = (HZ * decnet_time_wait);
 
-/*	printk(KERN_DEBUG "dn_destroy_timer: testing dead\n"); */
-	
 	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)) {
-		sklist_destroy_socket(&dn_sklist, sk);
+		dn_unhash_sock(sk);
+		sock_put(sk);
 		return 1;
 	}
 
-	/*printk(KERN_DEBUG "dn_destroy_timer: dead 'n' waiting...\n"); */
-
 	return 0;
 }
 
@@ -538,18 +575,12 @@
 		default:
 			printk(KERN_DEBUG "DECnet: dn_destroy_sock passed socket in invalid state\n");
 		case DN_O:
-			start_bh_atomic();
 			dn_stop_fast_timer(sk);
 			dn_stop_slow_timer(sk);
 
-	                if (sk == dn_wild_sk) {
-        	                dn_wild_sk = NULL;
-                	        sklist_destroy_socket(NULL, sk);
-			} else {
-                        	sklist_destroy_socket(&dn_sklist, sk);
-                	}
+			dn_unhash_sock_bh(sk);
+			sock_put(sk);
 
-			end_bh_atomic();
 			break;
 	}
 }
@@ -606,52 +637,6 @@
 	return "????";
 }
 
-static int dn_get_info(char *buffer, char **start, off_t offset,
-			    int length, int dummy)
-{
-	struct	sock	*sk;
-	int		len = 0;
-	off_t		pos = 0;
-	off_t		begin = 0;
-	char buf[DN_ASCBUF_LEN];
-
-	len += sprintf(buffer+len,"%-8s%-7s%-7s%-7s%-5s%-13s%-13s\n",
-			"Remote","Source","Remote","Object","Link ",
-			"   Data  Packets ","Link  Packets");
-	len += sprintf(buffer+len,"%-8s%-7s%-7s%-7s%-5s%-13s%-13s\n\n",
-			"Node  ","Port  ","Port  ","Number","State",
-			"   Out     In ","   Out     In");
-	start_bh_atomic();
-	for (sk = dn_sklist; sk != NULL; sk = sk->next) {
-		len += sprintf(buffer+len,
-		      "%6s  %04X   %04X   %6d %4s %6d %6d %6d %6d\n",
-					   
-		       dn_addr2asc(dn_ntohs(dn_saddr2dn(&sk->protinfo.dn.peer)), buf),
-		       sk->protinfo.dn.addrloc,sk->protinfo.dn.addrrem,
-		       sk->protinfo.dn.addr.sdn_objnum,
-		       dn_state2asc(sk->protinfo.dn.state),
-		       sk->protinfo.dn.numdat, sk->protinfo.dn.numdat_rcv,
-		       sk->protinfo.dn.numoth, sk->protinfo.dn.numoth_rcv);
-
-		pos = begin + len;
-		if (pos < offset) {
-                        len = 0;
-			begin = pos;
-		}
-		if (pos > offset+length) 
-			break;
-	}
-	end_bh_atomic();
-
-	*start = buffer + (offset - begin);
-	len -= (offset - begin);
-
-	if (len > length)
-		len = length;
-
-	return len;
-}
-
 static int dn_create(struct socket *sock, int protocol)
 {
 	struct	sock	*sk;
@@ -688,7 +673,7 @@
 
 
 static int
-dn_release(struct socket *sock, struct socket *peer)
+dn_release(struct socket *sock)
 {
 	struct sock *sk = sock->sk;
 
@@ -705,8 +690,11 @@
 
 static int dn_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 {
-	struct	sock	*sk = sock->sk;
+	struct sock *sk = sock->sk;
+	struct dn_scp *scp = &sk->protinfo.dn;
 	struct sockaddr_dn *saddr = (struct sockaddr_dn *)uaddr;
+	struct net_device *dev;
+	int rv;
 
 	if (sk->zapped == 0)
 		return -EINVAL;
@@ -717,50 +705,47 @@
 	if (saddr->sdn_family != AF_DECnet)
 		return -EINVAL;
 
+	if (dn_ntohs(saddr->sdn_nodeaddrl) && (dn_ntohs(saddr->sdn_nodeaddrl) != 2))
+		return -EINVAL;
+
 	if (saddr->sdn_objnum && !suser())
 		return -EPERM;
 
-	if (!saddr->sdn_objname && (saddr->sdn_objnamel > DN_MAXOBJL))
+	if (dn_ntohs(saddr->sdn_objnamel) > DN_MAXOBJL)
 		return -EINVAL;
 
 	if (saddr->sdn_flags & ~SDF_WILD)
 		return -EINVAL;
 
-	if ((saddr->sdn_flags & SDF_WILD) && !suser())
-		return -EPERM;
-
-	start_bh_atomic();
-
 	if (saddr->sdn_flags & SDF_WILD) {
-		if (dn_wild_sk) {
-			end_bh_atomic();
-			return -EADDRINUSE;
-		}
-		dn_wild_sk = sk;
-		sk->zapped = 0;
-		memcpy(&sk->protinfo.dn.addr, saddr, addr_len);
-		end_bh_atomic();
-		return 0;
-	}
-
-	if (saddr->sdn_objnum && dn_sklist_find_by_objnum(saddr->sdn_objnum)) {
-		end_bh_atomic();
-		return -EADDRINUSE;
-	}
-
-	if (!saddr->sdn_objnum) {
-		if (dn_sklist_find_by_name(saddr->sdn_objname)) {
-			end_bh_atomic();
-			return -EADDRINUSE;
+		if (!suser())
+			return -EPERM;
+	} else {
+		if (dn_ntohs(saddr->sdn_nodeaddrl)) {
+			read_lock(&dev_base_lock);
+			for(dev = dev_base; dev; dev = dev->next) {
+				if (!dev->dn_ptr)
+					continue;
+				if (dn_dev_islocal(dev, dn_saddr2dn(saddr)))
+					break;
+			}
+			read_unlock(&dev_base_lock);
+			if (dev == NULL)
+				return -EADDRNOTAVAIL;
 		}
 	}
 
-	memcpy(&sk->protinfo.dn.addr, saddr, addr_len);
+
+	memcpy(&scp->addr, saddr, addr_len);
 	sk->zapped = 0;
-	sklist_insert_socket(&dn_sklist, sk);
-	end_bh_atomic();
 
-        return 0;
+	if ((rv = dn_hash_sock(sk)) == 0)
+		goto out;
+
+	sk->zapped = 1;
+out:
+
+        return rv;
 }
 
 
@@ -781,17 +766,17 @@
 	if ((scp->accessdata.acc_accl != 0) &&
 		(scp->accessdata.acc_accl <= 12)) {
 	
-		scp->addr.sdn_objnamel = scp->accessdata.acc_accl;
-		memcpy(scp->addr.sdn_objname, scp->accessdata.acc_acc, scp->addr.sdn_objnamel);
+		scp->addr.sdn_objnamel = dn_htons(scp->accessdata.acc_accl);
+		memcpy(scp->addr.sdn_objname, scp->accessdata.acc_acc, dn_ntohs(scp->addr.sdn_objnamel));
 
 		scp->accessdata.acc_accl = 0;
 		memset(scp->accessdata.acc_acc, 0, 40);
 	}
 
-	scp->addr.sdn_add.a_len = 2;
+	scp->addr.sdn_add.a_len = dn_htons(2);
 	*(dn_address *)scp->addr.sdn_add.a_addr = decnet_address;
 
-	sklist_insert_socket(&dn_sklist, sk);
+	dn_hash_sock(sk);
 
 	return 0;
 }
@@ -801,6 +786,7 @@
 {
 	struct sockaddr_dn *addr = (struct sockaddr_dn *)uaddr;
 	struct sock *sk = sock->sk;
+	struct dn_scp *scp = &sk->protinfo.dn;
 	int err = -EISCONN;
 
 	lock_sock(sk);
@@ -835,10 +821,10 @@
 	if (sk->zapped && (err = dn_auto_bind(sock)))
 		goto out;
 
-	memcpy(&sk->protinfo.dn.peer, addr, addr_len);
+	memcpy(&scp->peer, addr, addr_len);
 
 	err = -EHOSTUNREACH;
-	if (dn_route_output(sk) < 0)
+	if (dn_route_output(&sk->dst_cache, dn_saddr2dn(&scp->peer), dn_saddr2dn(&scp->addr), 0) < 0)
 		goto out;
 
 	sk->state   = TCP_SYN_SENT;
@@ -925,8 +911,6 @@
 {
         struct sock *sk = sock->sk;
 
-	/* printk(KERN_DEBUG "dn_wait_accept: in\n"); */
-
         while(sk->state == TCP_LISTEN) {
                 if (flags & O_NONBLOCK) {
                         return -EAGAIN;
@@ -939,10 +923,8 @@
 
 		SOCK_SLEEP_POST(sk)
 
-                if (signal_pending(current)) {
-			/* printk(KERN_DEBUG "dn_wait_accept: signal\n"); */
+                if (signal_pending(current))
                         return -ERESTARTSYS; /* But of course you don't! */
-                }
         }
 
         if ((sk->protinfo.dn.state != DN_RUN) && (sk->protinfo.dn.state != DN_DRC)) {
@@ -951,16 +933,15 @@
         }
 
 	sock->state = SS_CONNECTED;
-	/* printk(KERN_DEBUG "dn_wait_accept: out\n"); */
 
         return 0;
 }
 
 
-static int dn_accept(struct socket *sock, struct socket *newsock,int flags)
+static int dn_accept(struct socket *sock, struct socket *newsock, int flags)
 {
-	struct	sock	*sk=sock->sk, *newsk;
-	struct	sk_buff	*skb = NULL;
+	struct sock *sk = sock->sk, *newsk;
+	struct sk_buff *skb = NULL;
 	struct dn_skb_cb *cb;
 	unsigned char menuver;
 	int err = 0;
@@ -978,15 +959,8 @@
 		return -EINVAL;
 	}
 
-	if (newsock->sk != NULL) {
-		newsock->sk->socket = NULL;
-		dn_destroy_sock(newsock->sk);
-		newsock->sk = NULL;
-	}
-
         do
         {
-		/* printk(KERN_DEBUG "dn_accept: loop top\n"); */
                 if ((skb = skb_dequeue(&sk->receive_queue)) == NULL)
                 {
                         if (flags & O_NONBLOCK)
@@ -1023,9 +997,7 @@
 	dst_release(xchg(&newsk->dst_cache, skb->dst));
 	skb->dst = NULL;
 
-
         newsk->protinfo.dn.state      = DN_CR;
-        newsk->protinfo.dn.addrloc    = dn_alloc_port();
 	newsk->protinfo.dn.addrrem    = cb->src_port;
 	newsk->protinfo.dn.mss        = cb->segsize;
 	newsk->protinfo.dn.accept_mode = sk->protinfo.dn.accept_mode;
@@ -1041,6 +1013,7 @@
 	skb_pull(skb, dn_username2sockaddr(skb->data, skb->len, &newsk->protinfo.dn.addr, &type));
 	skb_pull(skb, dn_username2sockaddr(skb->data, skb->len, &newsk->protinfo.dn.peer, &type));
 	*(dn_address *)newsk->protinfo.dn.peer.sdn_add.a_addr = cb->src;
+	*(dn_address *)newsk->protinfo.dn.addr.sdn_add.a_addr = cb->dst;
 
 	menuver = *skb->data;
 	skb_pull(skb, 1);
@@ -1065,7 +1038,7 @@
 		sizeof(struct optdata_dn));
 
 	lock_sock(newsk);
-        sklist_insert_socket(&dn_sklist, newsk);
+	dn_hash_sock(newsk);
 
 	dn_send_conn_ack(newsk);
 
@@ -1091,7 +1064,7 @@
 	lock_sock(sk);
 
 	if (peer) {
-		if (sock->state != SS_CONNECTED && sk->protinfo.dn.accept_mode == ACC_IMMED)
+		if (sock->state != SS_CONNECTED && scp->accept_mode == ACC_IMMED)
 			return -ENOTCONN;
 
 		memcpy(sa, &scp->peer, sizeof(struct sockaddr_dn));
@@ -1259,12 +1232,15 @@
 static int dn_listen(struct socket *sock, int backlog)
 {
 	struct sock *sk = sock->sk;
+	int err = -EINVAL;
+
+	lock_sock(sk);
 
 	if (sk->zapped)
-		return -EINVAL;
+		goto out;
 
 	if ((sk->protinfo.dn.state != DN_O) || (sk->state == TCP_LISTEN))
-		return -EINVAL;
+		goto out;
 
 	if (backlog > SOMAXCONN)
 		backlog = SOMAXCONN;
@@ -1272,8 +1248,12 @@
 	sk->max_ack_backlog = backlog;
 	sk->ack_backlog     = 0;
 	sk->state           = TCP_LISTEN;
+	err                 = 0;
 
-        return 0;
+out:
+	release_sock(sk);
+
+        return err;
 }
 
 
@@ -1312,10 +1292,17 @@
 
 static int dn_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
 {
-	return _dn_setsockopt(sock, level, optname, optval, optlen, 0);
+	struct sock *sk = sock->sk;
+	int err;
+
+	lock_sock(sk);
+	err = __dn_setsockopt(sock, level, optname, optval, optlen, 0);
+	release_sock(sk);
+
+	return err;
 }
 
-static int _dn_setsockopt(struct socket *sock, int level,int optname, char *optval, int optlen, int flags) 
+static int __dn_setsockopt(struct socket *sock, int level,int optname, char *optval, int optlen, int flags) 
 {
 	struct	sock *sk = sock->sk;
 	struct dn_scp *scp = &sk->protinfo.dn;
@@ -1406,7 +1393,6 @@
 			break;
 
 		case DSO_CONACCEPT:
-			lock_sock(sk);
 
 			if (scp->state != DN_CR)
 				return -EINVAL;
@@ -1414,11 +1400,9 @@
 			scp->state = DN_CC;
 			dn_send_conn_conf(sk);
 			err = dn_wait_accept(sock, sock->file->f_flags);
-			release_sock(sk);
 			return err;
 
 		case DSO_CONREJECT:
-			lock_sock(sk);
 
 			if (scp->state != DN_CR)
 				return -EINVAL;
@@ -1426,11 +1410,9 @@
 			scp->state = DN_DR;
 			sk->shutdown = SHUTDOWN_MASK;
 			dn_send_disc(sk, 0x38, 0);
-			release_sock(sk);
 			break;
 
 #ifdef CONFIG_DECNET_FW
-		case DN_FW_MASQ_TIMEOUTS:
                 case DN_FW_APPEND:
                 case DN_FW_REPLACE:
                 case DN_FW_DELETE:
@@ -1465,11 +1447,17 @@
 
 static int dn_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
 {
-	return _dn_getsockopt(sock, level, optname, optval, optlen, 0);
+	struct sock *sk = sock->sk;
+	int err;
+
+	lock_sock(sk);
+	err = __dn_getsockopt(sock, level, optname, optval, optlen, 0);
+	release_sock(sk);
+
+	return err;
 }
 
-static int
-_dn_getsockopt(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)
 {
 	struct	sock *sk = sock->sk;
 	struct dn_scp *scp = &sk->protinfo.dn;
@@ -1552,8 +1540,6 @@
 	struct dn_scp *scp = &sk->protinfo.dn;
 	int err = 0;
 
-	/* printk(KERN_DEBUG "dn_wait_run %d\n", scp->state); */
-
 	switch(scp->state) {
 		case DN_RUN:
 			return 0;
@@ -1567,7 +1553,6 @@
 			break;
 		default:
 			return -ENOTCONN;
-			goto out;
 	}
 
 	if (flags & MSG_DONTWAIT)
@@ -1575,11 +1560,11 @@
 
 	do {
 		if ((err = sock_error(sk)) != 0)
-			goto out;
+			break;
 
 		if (signal_pending(current)) {
 			err = -ERESTARTSYS;
-			goto out;
+			break;
 		}
 
 		SOCK_SLEEP_PRE(sk)
@@ -1591,8 +1576,6 @@
 
 	} while(scp->state != DN_RUN);
 
-out:
-
 	return 0;
 }
 
@@ -1621,6 +1604,8 @@
 		/* minimum data length for read exceeded */
 		if (len >= target)
 			return 1;
+
+		skb = skb->next;
 	}
 
 	return 0;
@@ -1636,8 +1621,9 @@
 	int target = size > 1 ? 1 : 0;
 	int copied = 0;
 	int rv = 0;
-	struct sk_buff *skb = NULL, **pskb;
+	struct sk_buff *skb, *nskb;
 	struct dn_skb_cb *cb = NULL;
+	unsigned char eor = 0;
 
 	lock_sock(sk);
 
@@ -1710,8 +1696,7 @@
 		sock->flags &= ~SO_WAITDATA;
 	}
 
-	pskb = &((struct sk_buff *)queue)->next;
-	while((skb = *pskb) != (struct sk_buff *)queue) {
+	for(skb = queue->next; skb != (struct sk_buff *)queue; skb = nskb) {
 		int chunk = skb->len;
 		cb = (struct dn_skb_cb *)skb->cb;
 
@@ -1727,27 +1712,29 @@
 		if (!(flags & MSG_PEEK))
 			skb->len -= chunk;
 
+		eor = cb->nsp_flags & 0x40;
+		nskb = skb->next;
+
 		if (skb->len == 0) {
 			skb_unlink(skb);
 			kfree_skb(skb);
+			/* 
+			 * N.B. Don't refer to skb or cb after this point
+			 * in loop.
+			 */
 			if ((scp->flowloc_sw == DN_DONTSEND) && !dn_congested(sk)) {
 				scp->flowloc_sw = DN_SEND;
 				dn_nsp_send_lnk(sk, DN_SEND);
 			}
 		}
 
-		pskb = &skb->next;
-
-		if (cb->nsp_flags & 0x40) {
-                        if (sk->type == SOCK_SEQPACKET) {
-                                msg->msg_flags |= MSG_EOR;
+		if (eor) { 
+			if (sk->type == SOCK_SEQPACKET)
+				break;
+			if (!(flags & MSG_WAITALL))
 				break;
-			}
 		}
 
-		if (!(flags & MSG_WAITALL))
-			break;
-
 		if (flags & MSG_OOB)
 			break;
 
@@ -1756,10 +1743,19 @@
 	}
 
 	rv = copied;
+
+	if (eor && (sk->type == SOCK_SEQPACKET))
+		msg->msg_flags |= MSG_EOR;
+
 out:
 	if (rv == 0)
 		rv = (flags & MSG_PEEK) ? -sk->err : sock_error(sk);
 
+	if ((rv >= 0) && msg->msg_name) {
+		memcpy(msg->msg_name, &scp->peer, sizeof(struct sockaddr_dn));
+		msg->msg_namelen = sizeof(struct sockaddr_dn);
+	}
+
 	release_sock(sk);
 
 	return rv;
@@ -1787,7 +1783,7 @@
 	unsigned short ack;
 	int len;
 
-	if (flags & ~(MSG_TRYHARD|MSG_OOB|MSG_DONTWAIT))
+	if (flags & ~(MSG_TRYHARD|MSG_OOB|MSG_DONTWAIT|MSG_EOR))
 		return -EOPNOTSUPP;
 
 	if (addr_len && (addr_len != sizeof(struct sockaddr_dn)))
@@ -1913,14 +1909,18 @@
 		} else {
 			cb->segnum = scp->numdat++;
 			scp->numdat &= 0x0fff;
+			msgflg = 0x00;
 			if (sock->type == SOCK_STREAM)
 				msgflg = 0x60;
-			else
-				msgflg = 0x00;
-			if (numseg == 0)
+			if (scp->seg_size == 0)
 				msgflg |= 0x20;
-			if ((sent + len) == size)
+
+			scp->seg_size += len;
+		
+			if (((sent + len) == size) && (flags & MSG_EOR)) {
 				msgflg |= 0x40;
+				scp->seg_size = 0;
+			}
 			ack = scp->ackxmt_dat | 0x8000;
 		}
 
@@ -1987,6 +1987,61 @@
 };
 
 #ifdef CONFIG_PROC_FS
+
+static int dn_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	struct sock *sk;
+	struct dn_scp *scp;
+	int len = 0;
+	off_t pos = 0;
+	off_t begin = 0;
+	char buf1[DN_ASCBUF_LEN];
+	char buf2[DN_ASCBUF_LEN];
+
+	len += sprintf(buffer + len, "Local                             Remote\n");
+
+	read_lock(&dn_hash_lock);
+	for(sk = dn_sklist; sk != NULL; sk = sk->next) {
+		scp = &sk->protinfo.dn;
+
+		len += sprintf(buffer + len,
+				"%6s/%04X %04d:%04d %04d:%04d %01d %6s/%04X %04d:%04d %04d:%04d %01d %4s %s\n",
+				dn_addr2asc(dn_ntohs(dn_saddr2dn(&scp->addr)), buf1),
+				scp->addrloc,
+				scp->numdat,
+				scp->numoth,
+				scp->ackxmt_dat,
+				scp->ackxmt_oth,
+				scp->flowloc_sw,
+				dn_addr2asc(dn_ntohs(dn_saddr2dn(&scp->peer)), buf2),
+				scp->addrrem,
+				scp->numdat_rcv,
+				scp->numoth_rcv,
+				scp->ackrcv_dat,
+				scp->ackrcv_oth,
+				scp->flowrem_sw,
+				dn_state2asc(scp->state),
+				((scp->accept_mode == ACC_IMMED) ? "IMMED" : "DEFER"));
+
+		pos = begin + len;
+		if (pos < offset) {
+			len = 0;
+			begin = pos;
+		}
+		if (pos > (offset + length))
+			break;
+	}
+	read_unlock(&dn_hash_lock);
+
+	*start = buffer + (offset - begin);
+	len -= (offset - begin);
+
+	if (len > length)
+		len = length;
+
+	return len;
+}
+
 struct proc_dir_entry decnet_linkinfo = {
 	PROC_NET_DN_SKT, 6, "decnet", S_IFREG | S_IRUGO,
 	1, 0, 0, 0, &proc_net_inode_operations, dn_get_info
@@ -2011,7 +2066,6 @@
 static struct proto_ops dn_proto_ops = {
         AF_DECnet,
 
-	sock_no_dup,
 	dn_release,
 	dn_bind,
 	dn_connect,
@@ -2026,7 +2080,8 @@
 	dn_getsockopt,
 	sock_no_fcntl,
         dn_sendmsg,
-	dn_recvmsg
+	dn_recvmsg,
+	sock_no_mmap
 };
 
 #ifdef CONFIG_SYSCTL
@@ -2061,46 +2116,41 @@
 #ifdef CONFIG_SYSCTL
 	dn_register_sysctl();
 #endif /* CONFIG_SYSCTL */
-        printk(KERN_INFO "DECnet for Linux: V.2.2.5s (C) 1995-1999 Linux DECnet Project Team\n");
+        printk(KERN_INFO "NET4: DECnet for Linux: V.2.3.13s (C) 1995-1999 Linux DECnet Project Team\n");
 
 }
 
-void __init decnet_setup(char *str, int *ints)
+#ifndef MODULE
+static int __init decnet_setup(char *str)
 {
+	unsigned short area = simple_strtoul(str, &str, 0);
+	unsigned short node = simple_strtoul(*str > 0 ? ++str : str, &str, 0);
+	unsigned short type = simple_strtoul(*str > 0 ? ++str : str, &str, 0);
 
-	if ((ints[0] == 2) || (ints[0] == 3)) {
-
-		if (ints[1] < 0)
-			ints[1] = 0;
-		if (ints[1] > 63)
-			ints[1] = 63;
-
-		if (ints[2] < 0)
-			ints[2] = 0;
-		if (ints[2] > 1023)
-			ints[2] = 1023;
+	decnet_address = dn_htons(area << 10 | node);
+	dn_dn2eth(decnet_ether_address, dn_ntohs(decnet_address));
 
-		decnet_address = dn_htons(ints[1] << 10 | ints[2]);
-		dn_dn2eth(decnet_ether_address, dn_ntohs(decnet_address));
-
-		if (ints[0] == 3) {
-			switch(ints[3]) {
+	switch(type) {
+		default:
+			printk(KERN_INFO "Invalid DECnet node type, switching to EndNode\n");
+		case 0:
+			decnet_node_type = DN_RT_INFO_ENDN;
+			break;
 #ifdef CONFIG_DECNET_ROUTER
-				case 1:
-					decnet_node_type = DN_RT_INFO_L1RT;
-					break;
-				case 2:
-					decnet_node_type = DN_RT_INFO_L2RT;
-					break;
+		case 1:
+			decnet_node_type = DN_RT_INFO_L1RT;
+			break;
+		case 2:
+			decnet_node_type = DN_RT_INFO_L2RT;
+			break;
 #endif /* CONFIG_DECNET_ROUTER */
-				default:
-					decnet_node_type = DN_RT_INFO_ENDN;
-			}
-		}
-	} else {
-		printk(KERN_ERR "DECnet: Invalid command line options\n");
 	}
+
+	return 0;
 }
+
+__setup("decnet=", decnet_setup);
+#endif
 
 #ifdef MODULE
 EXPORT_NO_SYMBOLS;

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)