[Linux-decnet-user] DECnet patch to fix local port alloc bug
Brought to you by:
chrissie_c,
ph3-der-loewe
|
From: Steven W. <st...@gw...> - 2001-11-20 10:27:44
|
Hi,
Here is a patch which fixes a bug with local port allocation. Its been tested
and appears to work ok. I'm quite keen to get this patch in as the bug is
a fairly important one to get fixed. Eventually there will be a part two of
this patch which will fix dn_accept() (see the FIXME I added in this patch).
For the time being though I'd rather get the original bug fixed and I prefer to
do the fixes in small chuncks rather than all at once.
Also included are a few doc updates.
Please queue for the next 2.4 kernel patch,
Thanks,
Steve.
------------------------------------------------------------------------------
diff -Nru linux-2.4.15-pre5/Documentation/networking/decnet.txt linux/Documentation/networking/decnet.txt
--- linux-2.4.15-pre5/Documentation/networking/decnet.txt Sun Mar 11 21:33:27 2001
+++ linux/Documentation/networking/decnet.txt Tue Nov 20 11:45:25 2001
@@ -4,20 +4,9 @@
1) Other documentation....
o Project Home Pages
- http://www.sucs.swan.ac.uk/~rohan/DECnet/index.html - Kernel info
+ http://www.chygwyn.com/DECnet/ - Kernel info
http://linux-decnet.sourceforge.net/ - Userland tools
-
- o FTP sites
- ftp://ftp.sucs.swan.ac.uk/pub/Linux/DECnet/
- - Swansea University Computer Society DECnet Archive
- (contains kernel patches and info)
- - Mirror of userland tools on ftp.dreamtime.org
- - Mirror of Alexey Kuznetsov's iproute2 package and
- other utilities
-
- ftp://linux-decnet.sourceforge.net/pub/linux-decnet/
- - Patrick Caulfield's archive of userland tools and
- Eduardo Serrat's kernel patches
+ http://www.sourceforge.net/projects/linux-decnet/ - Status page
2) Configuring the kernel
@@ -34,6 +23,12 @@
CONFIG_NETLINK (to allow rtnetlink)
CONFIG_RTNETLINK (for communication with the kernel routing layer)
CONFIG_NETFILTER (will be required for the DECnet routing daemon)
+
+ CONFIG_DECNET_ROUTE_FWMARK is optional
+
+Don't turn on SIOCGIFCONF support for DECnet unless you are really sure
+that you need it, in general you won't and it can cause ifconfig to
+malfunction.
3) Command line options
diff -Nru linux-2.4.15-pre5/net/decnet/README linux/net/decnet/README
--- linux-2.4.15-pre5/net/decnet/README Wed May 26 17:36:36 1999
+++ linux/net/decnet/README Sat Nov 17 20:07:21 2001
@@ -3,6 +3,6 @@
The documentation for this kernel subsystem is available in the
Documentation/networking subdirctory of this distribution and also
-on line at http://www.sucs.swan.ac.uk/~rohan/DECnet/index.html.
+on line at http://www.chygwyn.com/DECnet/
Steve Whitehouse <St...@AC...>
diff -Nru linux-2.4.15-pre5/net/decnet/af_decnet.c linux/net/decnet/af_decnet.c
--- linux-2.4.15-pre5/net/decnet/af_decnet.c Sat Nov 17 18:03:34 2001
+++ linux/net/decnet/af_decnet.c Mon Nov 19 19:01:16 2001
@@ -36,6 +36,7 @@
* 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
*/
@@ -139,9 +140,13 @@
dn_address decnet_address = 0;
unsigned char decnet_ether_address[ETH_ALEN] = { 0xAA, 0x00, 0x04, 0x00, 0x00, 0x00 };
+#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 struct proto_ops dn_proto_ops;
rwlock_t dn_hash_lock = RW_LOCK_UNLOCKED;
-static struct sock *dn_sklist;
+static struct sock *dn_sk_hash[DN_SK_HASH_SIZE];
static struct sock *dn_wild_sk;
static int __dn_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen, int flags);
@@ -154,18 +159,38 @@
if (scp->addr.sdn_flags & SDF_WILD)
return dn_wild_sk ? NULL : &dn_wild_sk;
- return &dn_sklist;
+ return &dn_sk_hash[scp->addrloc & DN_SK_HASH_MASK];
+}
+
+/*
+ * Valid ports are those greater than zero and not already in use.
+ */
+static int check_port(unsigned short port)
+{
+ struct sock *sk = dn_sk_hash[port & DN_SK_HASH_MASK];
+ if (port == 0)
+ return -1;
+ while(sk) {
+ struct dn_scp *scp = DN_SK(sk);
+ if (scp->addrloc == port)
+ return -1;
+ sk = sk->next;
+ }
+ 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;
- if (port == 0)
- port++;
+ while(check_port(++port) != 0) {
+ if (port == i_port)
+ return 0;
+ }
- scp->addrloc = port++;
+ scp->addrloc = port;
return 1;
}
@@ -238,6 +263,48 @@
sk->pprev = NULL;
}
+struct sock **listen_hash(struct sockaddr_dn *addr)
+{
+ int i;
+ unsigned hash = addr->sdn_objnum;
+
+ if (hash == 0) {
+ hash = addr->sdn_objnamel;
+ for(i = 0; i < 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 sock **skp = sk->pprev;
+ struct dn_scp *scp = DN_SK(sk);
+
+ if (scp->addr.sdn_flags & SDF_WILD)
+ return;
+
+ write_lock_bh(&dn_hash_lock);
+ while(*skp != sk)
+ skp = &((*skp)->next);
+ *skp = sk->next;
+
+ DN_SK(sk)->addrloc = 0;
+ skp = listen_hash(&DN_SK(sk)->addr);
+
+ sk->next = *skp;
+ sk->pprev = skp;
+ *skp = sk;
+ write_unlock_bh(&dn_hash_lock);
+}
int dn_sockaddr2username(struct sockaddr_dn *sdn, unsigned char *buf, unsigned char type)
{
@@ -328,10 +395,11 @@
struct sock *dn_sklist_find_listener(struct sockaddr_dn *addr)
{
+ struct sock **skp = listen_hash(addr);
struct sock *sk;
read_lock(&dn_hash_lock);
- for(sk = dn_sklist; sk != NULL; sk = sk->next) {
+ for(sk = *skp; sk != NULL; sk = sk->next) {
struct dn_scp *scp = DN_SK(sk);
if (sk->state != TCP_LISTEN)
continue;
@@ -365,7 +433,8 @@
struct dn_scp *scp;
read_lock(&dn_hash_lock);
- for(sk = dn_sklist; sk != NULL; sk = sk->next) {
+ sk = dn_sk_hash[cb->dst_port & DN_SK_HASH_MASK];
+ for (; sk != NULL; sk = sk->next) {
scp = DN_SK(sk);
if (cb->src != dn_saddr2dn(&scp->peer))
continue;
@@ -1045,6 +1114,9 @@
sizeof(struct optdata_dn));
lock_sock(newsk);
+ /*
+ * FIXME: This can fail if we've run out of local ports....
+ */
dn_hash_sock(newsk);
dn_send_conn_ack(newsk);
@@ -1200,6 +1272,7 @@
sk->ack_backlog = 0;
sk->state = TCP_LISTEN;
err = 0;
+ dn_rehash_sock(sk);
out:
release_sock(sk);
@@ -2063,44 +2136,47 @@
char buf2[DN_ASCBUF_LEN];
char local_object[DN_MAXOBJL+3];
char remote_object[DN_MAXOBJL+3];
+ int i;
len += sprintf(buffer + len, "Local Remote\n");
read_lock(&dn_hash_lock);
- for(sk = dn_sklist; sk != NULL; sk = sk->next) {
- scp = DN_SK(sk);
-
- dn_printable_object(&scp->addr, local_object);
- dn_printable_object(&scp->peer, remote_object);
-
- len += sprintf(buffer + len,
- "%6s/%04X %04d:%04d %04d:%04d %01d %-16s %6s/%04X %04d:%04d %04d:%04d %01d %-16s %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,
- local_object,
- 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,
- remote_object,
- dn_state2asc(scp->state),
- ((scp->accept_mode == ACC_IMMED) ? "IMMED" : "DEFER"));
-
- pos = begin + len;
- if (pos < offset) {
- len = 0;
- begin = pos;
+ for(i = 0; i < DN_SK_HASH_SIZE; i++) {
+ for(sk = dn_sk_hash[i]; sk != NULL; sk = sk->next) {
+ scp = DN_SK(sk);
+
+ dn_printable_object(&scp->addr, local_object);
+ dn_printable_object(&scp->peer, remote_object);
+
+ len += sprintf(buffer + len,
+ "%6s/%04X %04d:%04d %04d:%04d %01d %-16s %6s/%04X %04d:%04d %04d:%04d %01d %-16s %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,
+ local_object,
+ 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,
+ remote_object,
+ 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;
}
- if (pos > (offset + length))
- break;
}
read_unlock(&dn_hash_lock);
@@ -2158,7 +2234,7 @@
MODULE_PARM_DESC(addr, "The DECnet address of this machine: area,node");
#endif
-static char banner[] __initdata = KERN_INFO "NET4: DECnet for Linux: V.2.4.9s (C) 1995-2001 Linux DECnet Project Team\n";
+static char banner[] __initdata = KERN_INFO "NET4: DECnet for Linux: V.2.4.15-pre5s (C) 1995-2001 Linux DECnet Project Team\n";
static int __init decnet_init(void)
{
|