patch-2.2.11 linux/net/ipv4/udp.c
Next file: linux/net/ipv6/addrconf.c
Previous file: linux/net/ipv4/tcp_timer.c
Back to the patch index
Back to the overall index
- Lines: 332
- Date:
Mon Aug 9 12:05:10 1999
- Orig file:
v2.2.10/linux/net/ipv4/udp.c
- Orig date:
Mon May 10 09:55:25 1999
diff -u --recursive --new-file v2.2.10/linux/net/ipv4/udp.c linux/net/ipv4/udp.c
@@ -5,7 +5,7 @@
*
* The User Datagram Protocol (UDP).
*
- * Version: $Id: udp.c,v 1.66 1999/05/08 20:00:25 davem Exp $
+ * Version: $Id: udp.c,v 1.66.2.3 1999/08/07 10:56:36 davem Exp $
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -123,108 +123,75 @@
struct sock *udp_hash[UDP_HTABLE_SIZE];
-static int udp_v4_verify_bind(struct sock *sk, unsigned short snum)
-{
- struct sock *sk2;
- int retval = 0, sk_reuse = sk->reuse;
+/* Shared by v4/v6 udp. */
+int udp_port_rover = 0;
+static int udp_v4_get_port(struct sock *sk, unsigned short snum)
+{
SOCKHASH_LOCK();
- 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 (snum == 0) {
+ int best_size_so_far, best, result, i;
- if(sk2->bound_dev_if != sk->bound_dev_if)
- continue;
-
- if(!sk2->rcv_saddr || !sk->rcv_saddr) {
- if((!sk2_reuse) ||
- (!sk_reuse) ||
- (state == TCP_LISTEN)) {
- retval = 1;
- break;
- }
- } else if(sk2->rcv_saddr == sk->rcv_saddr) {
- if((!sk_reuse) ||
- (!sk2_reuse) ||
- (state == TCP_LISTEN)) {
- retval = 1;
- break;
- }
+ 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:
}
- }
- SOCKHASH_UNLOCK();
- return retval;
-}
-
-static inline int udp_lport_inuse(u16 num)
-{
- struct sock *sk = udp_hash[num & (UDP_HTABLE_SIZE - 1)];
+ 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;
- for(; sk != NULL; sk = sk->next) {
- if(sk->num == num)
- return 1;
+ 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 ||
+ !sk->rcv_saddr ||
+ sk2->rcv_saddr == sk->rcv_saddr) &&
+ (!sk2->reuse || !sk->reuse))
+ goto fail;
+ }
}
+ sk->num = snum;
+ SOCKHASH_UNLOCK();
return 0;
-}
-
-/* Shared by v4/v6 tcp. */
-unsigned short udp_good_socknum(void)
-{
- int result;
- static int start = 0;
- int i, best, best_size_so_far;
-
- SOCKHASH_LOCK();
- if (start > sysctl_local_port_range[1] || start < sysctl_local_port_range[0])
- start = sysctl_local_port_range[0];
- best_size_so_far = 32767; /* "big" num */
- best = result = start;
-
- 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 out;
- }
-
- /* Is this one better than our best so far? */
- 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) {
- /* Get into range (but preserve hash bin)... */
- 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;
- }
-out:
- start = result;
+fail:
SOCKHASH_UNLOCK();
- return result;
+ return 1;
}
/* Last hit UDP socket cache, this is ipv4 specific so make it static. */
@@ -234,62 +201,27 @@
static void udp_v4_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();
- sk->next = *skp;
+ if ((sk->next = *skp) != NULL)
+ (*skp)->pprev = &sk->next;
*skp = sk;
- sk->hashent = num;
+ sk->pprev = skp;
SOCKHASH_UNLOCK();
}
static void udp_v4_unhash(struct sock *sk)
{
- struct sock **skp;
- int num = sk->num;
-
- num &= (UDP_HTABLE_SIZE - 1);
- skp = &udp_hash[num];
-
SOCKHASH_LOCK();
- while(*skp != NULL) {
- if(*skp == sk) {
- *skp = sk->next;
- break;
- }
- skp = &((*skp)->next);
- }
- if(uh_cache_sk == sk)
- uh_cache_sk = NULL;
- SOCKHASH_UNLOCK();
-}
-
-static void udp_v4_rehash(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();
- 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;
+ if(uh_cache_sk == sk)
+ uh_cache_sk = NULL;
}
- sk->next = udp_hash[num];
- udp_hash[num] = sk;
- sk->hashent = num;
- if(uh_cache_sk == sk)
- uh_cache_sk = NULL;
SOCKHASH_UNLOCK();
}
@@ -755,8 +687,15 @@
connected = 0;
}
- if (connected)
- rt = (struct rtable*)dst_clone(sk->dst_cache);
+ if (connected && sk->dst_cache) {
+ rt = (struct rtable*)sk->dst_cache;
+ if (rt->u.dst.obsolete) {
+ sk->dst_cache = NULL;
+ dst_release(&rt->u.dst);
+ rt = NULL;
+ } else
+ dst_clone(&rt->u.dst);
+ }
if (rt == NULL) {
err = ip_route_output(&rt, daddr, ufh.saddr,
@@ -770,6 +709,8 @@
err = -EACCES;
if (rt->rt_flags&RTCF_BROADCAST && !sk->broadcast)
goto out;
+ if (connected && sk->dst_cache == NULL)
+ sk->dst_cache = dst_clone(&rt->u.dst);
}
ufh.saddr = rt->rt_src;
@@ -810,7 +751,6 @@
{
unsigned long amount;
- if (sk->state == TCP_LISTEN) return(-EINVAL);
amount = sock_wspace(sk);
return put_user(amount, (int *)arg);
}
@@ -820,8 +760,6 @@
struct sk_buff *skb;
unsigned long amount;
- if (sk->state == TCP_LISTEN)
- return(-EINVAL);
amount = 0;
/* N.B. Is this interrupt safe??
-> Yes. Interrupts do not remove skbs. --ANK (980725)
@@ -860,12 +798,6 @@
struct sk_buff *skb;
int copied, err;
- /*
- * Check any passed addresses
- */
- if (addr_len)
- *addr_len=sizeof(*sin);
-
if (flags & MSG_ERRQUEUE)
return ip_recv_error(sk, msg, len);
@@ -916,6 +848,12 @@
/* Copy the address. */
if (sin)
{
+ /*
+ * Check any passed addresses
+ */
+ if (addr_len)
+ *addr_len=sizeof(*sin);
+
sin->sin_family = AF_INET;
sin->sin_port = skb->h.uh->source;
sin->sin_addr.s_addr = skb->nh.iph->saddr;
@@ -1244,9 +1182,7 @@
udp_queue_rcv_skb, /* backlog_rcv */
udp_v4_hash, /* hash */
udp_v4_unhash, /* unhash */
- udp_v4_rehash, /* rehash */
- udp_good_socknum, /* good_socknum */
- udp_v4_verify_bind, /* verify_bind */
+ udp_v4_get_port, /* good_socknum */
128, /* max_header */
0, /* retransmits */
"UDP", /* name */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)