summaryrefslogtreecommitdiffstats
path: root/net/ipv6/udp.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1999-09-28 22:25:29 +0000
committerRalf Baechle <ralf@linux-mips.org>1999-09-28 22:25:29 +0000
commit0ae8dceaebe3659ee0c3352c08125f403e77ebca (patch)
tree5085c389f09da78182b899d19fe1068b619a69dd /net/ipv6/udp.c
parent273767781288c35c9d679e908672b9996cda4c34 (diff)
Merge with 2.3.10.
Diffstat (limited to 'net/ipv6/udp.c')
-rw-r--r--net/ipv6/udp.c249
1 files changed, 159 insertions, 90 deletions
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index da020d8fb..b3045c694 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -7,7 +7,7 @@
*
* Based on linux/ipv4/udp.c
*
- * $Id: udp.c,v 1.42 1999/06/09 10:11:24 davem Exp $
+ * $Id: udp.c,v 1.43 1999/07/02 11:26:44 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -49,101 +49,102 @@ struct udp_mib udp_stats_in6;
/* Grrr, addr_type already calculated by caller, but I don't want
* to add some silly "cookie" argument to this method just for that.
*/
-static int udp_v6_verify_bind(struct sock *sk, unsigned short snum)
+static int udp_v6_get_port(struct sock *sk, unsigned short snum)
{
- struct sock *sk2;
- int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr);
- int retval = 0, sk_reuse = sk->reuse;
-
- SOCKHASH_LOCK_READ();
- for(sk2 = udp_hash[snum & (UDP_HTABLE_SIZE - 1)]; sk2 != NULL; sk2 = sk2->next) {
- if((sk2->num == snum) && (sk2 != sk)) {
- unsigned char state = sk2->state;
- int sk2_reuse = sk2->reuse;
-
- /* Two sockets can be bound to the same port if they're
- * bound to different interfaces.
- */
-
- if(sk2->bound_dev_if != sk->bound_dev_if)
- continue;
-
- if(addr_type == IPV6_ADDR_ANY || (!sk2->rcv_saddr)) {
- if((!sk2_reuse) ||
- (!sk_reuse) ||
- (state == TCP_LISTEN)) {
- retval = 1;
- break;
- }
- } else if(!ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
- &sk2->net_pinfo.af_inet6.rcv_saddr)) {
- if((!sk_reuse) ||
- (!sk2_reuse) ||
- (state == TCP_LISTEN)) {
- retval = 1;
- break;
- }
+ SOCKHASH_LOCK_WRITE();
+ if (snum == 0) {
+ int best_size_so_far, best, result, i;
+
+ if (udp_port_rover > sysctl_local_port_range[1] ||
+ udp_port_rover < sysctl_local_port_range[0])
+ udp_port_rover = sysctl_local_port_range[0];
+ best_size_so_far = 32767;
+ best = result = udp_port_rover;
+ for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) {
+ struct sock *sk;
+ int size;
+
+ sk = udp_hash[result & (UDP_HTABLE_SIZE - 1)];
+ if (!sk) {
+ if (result > sysctl_local_port_range[1])
+ result = sysctl_local_port_range[0] +
+ ((result - sysctl_local_port_range[0]) &
+ (UDP_HTABLE_SIZE - 1));
+ goto gotit;
}
+ size = 0;
+ do {
+ if (++size >= best_size_so_far)
+ goto next;
+ } while ((sk = sk->next) != NULL);
+ best_size_so_far = size;
+ best = result;
+ next:
+ }
+ result = best;
+ for(;; result += UDP_HTABLE_SIZE) {
+ if (result > sysctl_local_port_range[1])
+ result = sysctl_local_port_range[0]
+ + ((result - sysctl_local_port_range[0]) &
+ (UDP_HTABLE_SIZE - 1));
+ if (!udp_lport_inuse(result))
+ break;
+ }
+gotit:
+ udp_port_rover = snum = result;
+ } else {
+ struct sock *sk2;
+ int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr);
+
+ for (sk2 = udp_hash[snum & (UDP_HTABLE_SIZE - 1)];
+ sk2 != NULL;
+ sk2 = sk2->next) {
+ if (sk2->num == snum &&
+ sk2 != sk &&
+ sk2->bound_dev_if == sk->bound_dev_if &&
+ (!sk2->rcv_saddr ||
+ addr_type == IPV6_ADDR_ANY ||
+ !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
+ &sk2->net_pinfo.af_inet6.rcv_saddr)) &&
+ (!sk2->reuse || !sk->reuse))
+ goto fail;
}
}
- SOCKHASH_UNLOCK_READ();
- return retval;
-}
-
-static void udp_v6_hash(struct sock *sk)
-{
- struct sock **skp;
- int num = sk->num;
- num &= (UDP_HTABLE_SIZE - 1);
- skp = &udp_hash[num];
+ sk->num = snum;
+ SOCKHASH_UNLOCK_WRITE();
+ return 0;
- SOCKHASH_LOCK_WRITE();
- sk->next = *skp;
- *skp = sk;
- sk->hashent = num;
+fail:
SOCKHASH_UNLOCK_WRITE();
+ return 1;
}
-static void udp_v6_unhash(struct sock *sk)
+static void udp_v6_hash(struct sock *sk)
{
- struct sock **skp;
- int num = sk->num;
-
- num &= (UDP_HTABLE_SIZE - 1);
- skp = &udp_hash[num];
+ struct sock **skp = &udp_hash[sk->num & (UDP_HTABLE_SIZE - 1)];
SOCKHASH_LOCK_WRITE();
- while(*skp != NULL) {
- if(*skp == sk) {
- *skp = sk->next;
- break;
- }
- skp = &((*skp)->next);
- }
+ if ((sk->next = *skp) != NULL)
+ (*skp)->pprev = &sk->next;
+ *skp = sk;
+ sk->pprev = skp;
+ sk->prot->inuse++;
+ if(sk->prot->highestinuse < sk->prot->inuse)
+ sk->prot->highestinuse = sk->prot->inuse;
SOCKHASH_UNLOCK_WRITE();
}
-static void udp_v6_rehash(struct sock *sk)
+static void udp_v6_unhash(struct sock *sk)
{
- struct sock **skp;
- int num = sk->num;
- int oldnum = sk->hashent;
-
- num &= (UDP_HTABLE_SIZE - 1);
- skp = &udp_hash[oldnum];
-
SOCKHASH_LOCK_WRITE();
- while(*skp != NULL) {
- if(*skp == sk) {
- *skp = sk->next;
- break;
- }
- skp = &((*skp)->next);
+ if (sk->pprev) {
+ if (sk->next)
+ sk->next->pprev = sk->pprev;
+ *sk->pprev = sk->next;
+ sk->pprev = NULL;
+ sk->prot->inuse--;
}
- sk->next = udp_hash[num];
- udp_hash[num] = sk;
- sk->hashent = num;
SOCKHASH_UNLOCK_WRITE();
}
@@ -216,10 +217,10 @@ int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
}
if (addr_len < sizeof(*usin))
- return(-EINVAL);
+ return -EINVAL;
if (usin->sin6_family && usin->sin6_family != AF_INET6)
- return(-EAFNOSUPPORT);
+ return -EAFNOSUPPORT;
fl.fl6_flowlabel = 0;
if (np->sndflow) {
@@ -764,7 +765,7 @@ static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen)
return -EMSGSIZE;
if (msg->msg_flags & ~(MSG_DONTROUTE|MSG_DONTWAIT))
- return(-EINVAL);
+ return -EINVAL;
fl.fl6_flowlabel = 0;
@@ -773,13 +774,13 @@ static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen)
return udp_sendmsg(sk, msg, ulen);
if (addr_len < sizeof(*sin6))
- return(-EINVAL);
+ return -EINVAL;
if (sin6->sin6_family && sin6->sin6_family != AF_INET6)
- return(-EINVAL);
+ return -EINVAL;
if (sin6->sin6_port == 0)
- return(-EINVAL);
+ return -EINVAL;
udh.uh.dest = sin6->sin6_port;
daddr = &sin6->sin6_addr;
@@ -800,7 +801,7 @@ static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen)
daddr = &sk->net_pinfo.af_inet6.daddr;
} else {
if (sk->state != TCP_ESTABLISHED)
- return(-ENOTCONN);
+ return -ENOTCONN;
udh.uh.dest = sk->dport;
daddr = &sk->net_pinfo.af_inet6.daddr;
@@ -885,10 +886,80 @@ static struct inet6_protocol udpv6_protocol =
"UDPv6" /* name */
};
+static void get_udp6_sock(struct sock *sp, char *tmpbuf, int i)
+{
+ struct in6_addr *dest, *src;
+ __u16 destp, srcp;
+ int timer_active;
+ unsigned long timer_expires;
+
+ dest = &sp->net_pinfo.af_inet6.daddr;
+ src = &sp->net_pinfo.af_inet6.rcv_saddr;
+ destp = ntohs(sp->dport);
+ srcp = ntohs(sp->sport);
+ timer_active = (sp->timer.prev != NULL) ? 2 : 0;
+ timer_expires = (timer_active == 2 ? sp->timer.expires : jiffies);
+ sprintf(tmpbuf,
+ "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
+ "%02X %08X:%08X %02X:%08lX %08X %5d %8d %ld",
+ i,
+ src->s6_addr32[0], src->s6_addr32[1],
+ src->s6_addr32[2], src->s6_addr32[3], srcp,
+ dest->s6_addr32[0], dest->s6_addr32[1],
+ dest->s6_addr32[2], dest->s6_addr32[3], destp,
+ sp->state,
+ atomic_read(&sp->wmem_alloc), atomic_read(&sp->rmem_alloc),
+ timer_active, timer_expires-jiffies, 0,
+ sp->socket->inode->i_uid, timer_active ? sp->timeout : 0,
+ sp->socket ? sp->socket->inode->i_ino : 0);
+}
+
+int udp6_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ int len = 0, num = 0, i;
+ off_t pos = 0;
+ off_t begin;
+ char tmpbuf[150];
+
+ if (offset < 149)
+ len += sprintf(buffer, "%-148s\n",
+ " sl " /* 6 */
+ "local_address " /* 38 */
+ "remote_address " /* 38 */
+ "st tx_queue rx_queue tr tm->when retrnsmt" /* 41 */
+ " uid timeout inode"); /* 21 */
+ /*----*/
+ /*144 */
+ pos = 149;
+ SOCKHASH_LOCK_READ();
+ for (i = 0; i < UDP_HTABLE_SIZE; i++) {
+ struct sock *sk;
+
+ for (sk = udp_hash[i]; sk; sk = sk->next, num++) {
+ if (sk->family != PF_INET6)
+ continue;
+ pos += 149;
+ if (pos < offset)
+ continue;
+ get_udp6_sock(sk, tmpbuf, i);
+ len += sprintf(buffer+len, "%-148s\n", tmpbuf);
+ if(len >= length)
+ goto out;
+ }
+ }
+out:
+ SOCKHASH_UNLOCK_READ();
+ begin = len - (pos - offset);
+ *start = buffer + begin;
+ len -= begin;
+ if(len > length)
+ len = length;
+ if (len < 0)
+ len = 0;
+ return len;
+}
struct proto udpv6_prot = {
- (struct sock *)&udpv6_prot, /* sklist_next */
- (struct sock *)&udpv6_prot, /* sklist_prev */
udpv6_close, /* close */
udpv6_connect, /* connect */
NULL, /* accept */
@@ -908,9 +979,7 @@ struct proto udpv6_prot = {
udpv6_queue_rcv_skb, /* backlog_rcv */
udp_v6_hash, /* hash */
udp_v6_unhash, /* unhash */
- udp_v6_rehash, /* rehash */
- udp_good_socknum, /* good_socknum */
- udp_v6_verify_bind, /* verify_bind */
+ udp_v6_get_port, /* get_port */
128, /* max_header */
0, /* retransmits */
"UDP", /* name */