mac80211: fix modprobe deadlock by not calling wep_init under rtnl_lock
[safe/jmp/linux-2.6] / net / ipv4 / udp.c
index ced8203..7a1d1ce 100644 (file)
@@ -120,24 +120,32 @@ EXPORT_SYMBOL(sysctl_udp_wmem_min);
 atomic_t udp_memory_allocated;
 EXPORT_SYMBOL(udp_memory_allocated);
 
+#define PORTS_PER_CHAIN (65536 / UDP_HTABLE_SIZE)
+
 static int udp_lib_lport_inuse(struct net *net, __u16 num,
                               const struct udp_hslot *hslot,
+                              unsigned long *bitmap,
                               struct sock *sk,
                               int (*saddr_comp)(const struct sock *sk1,
                                                 const struct sock *sk2))
 {
        struct sock *sk2;
-       struct hlist_node *node;
+       struct hlist_nulls_node *node;
 
-       sk_for_each(sk2, node, &hslot->head)
+       sk_nulls_for_each(sk2, node, &hslot->head)
                if (net_eq(sock_net(sk2), net)                  &&
                    sk2 != sk                                   &&
-                   sk2->sk_hash == num                         &&
+                   (bitmap || sk2->sk_hash == num)             &&
                    (!sk2->sk_reuse || !sk->sk_reuse)           &&
                    (!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if
                        || sk2->sk_bound_dev_if == sk->sk_bound_dev_if) &&
-                   (*saddr_comp)(sk, sk2))
-                       return 1;
+                   (*saddr_comp)(sk, sk2)) {
+                       if (bitmap)
+                               __set_bit(sk2->sk_hash / UDP_HTABLE_SIZE,
+                                         bitmap);
+                       else
+                               return 1;
+               }
        return 0;
 }
 
@@ -160,36 +168,51 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum,
        if (!snum) {
                int low, high, remaining;
                unsigned rand;
-               unsigned short first;
+               unsigned short first, last;
+               DECLARE_BITMAP(bitmap, PORTS_PER_CHAIN);
 
                inet_get_local_port_range(&low, &high);
                remaining = (high - low) + 1;
 
                rand = net_random();
-               snum = first = rand % remaining + low;
-               rand |= 1;
-               for (;;) {
-                       hslot = &udptable->hash[udp_hashfn(net, snum)];
+               first = (((u64)rand * remaining) >> 32) + low;
+               /*
+                * force rand to be an odd multiple of UDP_HTABLE_SIZE
+                */
+               rand = (rand | 1) * UDP_HTABLE_SIZE;
+               for (last = first + UDP_HTABLE_SIZE; first != last; first++) {
+                       hslot = &udptable->hash[udp_hashfn(net, first)];
+                       bitmap_zero(bitmap, PORTS_PER_CHAIN);
                        spin_lock_bh(&hslot->lock);
-                       if (!udp_lib_lport_inuse(net, snum, hslot, sk, saddr_comp))
-                               break;
-                       spin_unlock_bh(&hslot->lock);
+                       udp_lib_lport_inuse(net, snum, hslot, bitmap, sk,
+                                           saddr_comp);
+
+                       snum = first;
+                       /*
+                        * Iterate on all possible values of snum for this hash.
+                        * Using steps of an odd multiple of UDP_HTABLE_SIZE
+                        * give us randomization and full range coverage.
+                        */
                        do {
-                               snum = snum + rand;
-                       } while (snum < low || snum > high);
-                       if (snum == first)
-                               goto fail;
+                               if (low <= snum && snum <= high &&
+                                   !test_bit(snum / UDP_HTABLE_SIZE, bitmap))
+                                       goto found;
+                               snum += rand;
+                       } while (snum != first);
+                       spin_unlock_bh(&hslot->lock);
                }
+               goto fail;
        } else {
                hslot = &udptable->hash[udp_hashfn(net, snum)];
                spin_lock_bh(&hslot->lock);
-               if (udp_lib_lport_inuse(net, snum, hslot, sk, saddr_comp))
+               if (udp_lib_lport_inuse(net, snum, hslot, NULL, sk, saddr_comp))
                        goto fail_unlock;
        }
+found:
        inet_sk(sk)->num = snum;
        sk->sk_hash = snum;
        if (sk_unhashed(sk)) {
-               sk_add_node_rcu(sk, &hslot->head);
+               sk_nulls_add_node_rcu(sk, &hslot->head);
                sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
        }
        error = 0;
@@ -256,7 +279,7 @@ static struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr,
                int dif, struct udp_table *udptable)
 {
        struct sock *sk, *result;
-       struct hlist_node *node;
+       struct hlist_nulls_node *node;
        unsigned short hnum = ntohs(dport);
        unsigned int hash = udp_hashfn(net, hnum);
        struct udp_hslot *hslot = &udptable->hash[hash];
@@ -266,13 +289,7 @@ static struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr,
 begin:
        result = NULL;
        badness = -1;
-       sk_for_each_rcu(sk, node, &hslot->head) {
-               /*
-                * lockless reader, and SLAB_DESTROY_BY_RCU items:
-                * We must check this item was not moved to another chain
-                */
-               if (udp_hashfn(net, sk->sk_hash) != hash)
-                       goto begin;
+       sk_nulls_for_each_rcu(sk, node, &hslot->head) {
                score = compute_score(sk, net, saddr, hnum, sport,
                                      daddr, dport, dif);
                if (score > badness) {
@@ -280,6 +297,14 @@ begin:
                        badness = score;
                }
        }
+       /*
+        * if the nulls value we got at the end of this lookup is
+        * not the expected one, we must restart lookup.
+        * We probably met an item that was moved to another chain.
+        */
+       if (get_nulls_value(node) != hash)
+               goto begin;
+
        if (result) {
                if (unlikely(!atomic_inc_not_zero(&result->sk_refcnt)))
                        result = NULL;
@@ -315,19 +340,20 @@ struct sock *udp4_lib_lookup(struct net *net, __be32 saddr, __be16 sport,
 }
 EXPORT_SYMBOL_GPL(udp4_lib_lookup);
 
-static inline struct sock *udp_v4_mcast_next(struct sock *sk,
+static inline struct sock *udp_v4_mcast_next(struct net *net, struct sock *sk,
                                             __be16 loc_port, __be32 loc_addr,
                                             __be16 rmt_port, __be32 rmt_addr,
                                             int dif)
 {
-       struct hlist_node *node;
+       struct hlist_nulls_node *node;
        struct sock *s = sk;
        unsigned short hnum = ntohs(loc_port);
 
-       sk_for_each_from(s, node) {
+       sk_nulls_for_each_from(s, node) {
                struct inet_sock *inet = inet_sk(s);
 
-               if (s->sk_hash != hnum                                  ||
+               if (!net_eq(sock_net(s), net)                           ||
+                   s->sk_hash != hnum                                  ||
                    (inet->daddr && inet->daddr != rmt_addr)            ||
                    (inet->dport != rmt_port && inet->dport)            ||
                    (inet->rcv_saddr && inet->rcv_saddr != loc_addr)    ||
@@ -570,6 +596,7 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
                return -EOPNOTSUPP;
 
        ipc.opt = NULL;
+       ipc.shtx.flags = 0;
 
        if (up->pending) {
                /*
@@ -617,6 +644,9 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
        ipc.addr = inet->saddr;
 
        ipc.oif = sk->sk_bound_dev_if;
+       err = sock_tx_timestamp(msg, sk, &ipc.shtx);
+       if (err)
+               return err;
        if (msg->msg_controllen) {
                err = ip_cmsg_send(sock_net(sk), msg, &ipc);
                if (err)
@@ -663,6 +693,7 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
                                                .saddr = saddr,
                                                .tos = tos } },
                                    .proto = sk->sk_protocol,
+                                   .flags = inet_sk_flowi_flags(sk),
                                    .uli_u = { .ports =
                                               { .sport = inet->sport,
                                                 .dport = dport } } };
@@ -715,7 +746,7 @@ do_append_data:
        up->len += ulen;
        getfrag  =  is_udplite ?  udplite_getfrag : ip_generic_getfrag;
        err = ip_append_data(sk, getfrag, msg->msg_iov, ulen,
-                       sizeof(struct udphdr), &ipc, rt,
+                       sizeof(struct udphdr), &ipc, &rt,
                        corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags);
        if (err)
                udp_flush_pending_frames(sk);
@@ -966,16 +997,18 @@ int udp_disconnect(struct sock *sk, int flags)
 
 void udp_lib_unhash(struct sock *sk)
 {
-       struct udp_table *udptable = sk->sk_prot->h.udp_table;
-       unsigned int hash = udp_hashfn(sock_net(sk), sk->sk_hash);
-       struct udp_hslot *hslot = &udptable->hash[hash];
+       if (sk_hashed(sk)) {
+               struct udp_table *udptable = sk->sk_prot->h.udp_table;
+               unsigned int hash = udp_hashfn(sock_net(sk), sk->sk_hash);
+               struct udp_hslot *hslot = &udptable->hash[hash];
 
-       spin_lock(&hslot->lock);
-       if (sk_del_node_init_rcu(sk)) {
-               inet_sk(sk)->num = 0;
-               sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
+               spin_lock_bh(&hslot->lock);
+               if (sk_nulls_del_node_init_rcu(sk)) {
+                       inet_sk(sk)->num = 0;
+                       sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
+               }
+               spin_unlock_bh(&hslot->lock);
        }
-       spin_unlock(&hslot->lock);
 }
 EXPORT_SYMBOL(udp_lib_unhash);
 
@@ -986,9 +1019,11 @@ static int __udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
 
        if ((rc = sock_queue_rcv_skb(sk, skb)) < 0) {
                /* Note that an ENOMEM error is charged twice */
-               if (rc == -ENOMEM)
+               if (rc == -ENOMEM) {
                        UDP_INC_STATS_BH(sock_net(sk), UDP_MIB_RCVBUFERRORS,
                                         is_udplite);
+                       atomic_inc(&sk->sk_drops);
+               }
                goto drop;
        }
 
@@ -1124,17 +1159,18 @@ static int __udp4_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
        int dif;
 
        spin_lock(&hslot->lock);
-       sk = sk_head(&hslot->head);
+       sk = sk_nulls_head(&hslot->head);
        dif = skb->dev->ifindex;
-       sk = udp_v4_mcast_next(sk, uh->dest, daddr, uh->source, saddr, dif);
+       sk = udp_v4_mcast_next(net, sk, uh->dest, daddr, uh->source, saddr, dif);
        if (sk) {
                struct sock *sknext = NULL;
 
                do {
                        struct sk_buff *skb1 = skb;
 
-                       sknext = udp_v4_mcast_next(sk_next(sk), uh->dest, daddr,
-                                                  uh->source, saddr, dif);
+                       sknext = udp_v4_mcast_next(net, sk_nulls_next(sk), uh->dest,
+                                                  daddr, uh->source, saddr,
+                                                  dif);
                        if (sknext)
                                skb1 = skb_clone(skb, GFP_ATOMIC);
 
@@ -1148,7 +1184,7 @@ static int __udp4_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
                        sk = sknext;
                } while (sknext);
        } else
-               kfree_skb(skb);
+               consume_skb(skb);
        spin_unlock(&hslot->lock);
        return 0;
 }
@@ -1199,11 +1235,10 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
                   int proto)
 {
        struct sock *sk;
-       struct udphdr *uh = udp_hdr(skb);
+       struct udphdr *uh;
        unsigned short ulen;
        struct rtable *rt = (struct rtable*)skb->dst;
-       __be32 saddr = ip_hdr(skb)->saddr;
-       __be32 daddr = ip_hdr(skb)->daddr;
+       __be32 saddr, daddr;
        struct net *net = dev_net(skb->dev);
 
        /*
@@ -1212,6 +1247,7 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
        if (!pskb_may_pull(skb, sizeof(struct udphdr)))
                goto drop;              /* No space for header. */
 
+       uh   = udp_hdr(skb);
        ulen = ntohs(uh->len);
        if (ulen > skb->len)
                goto short_packet;
@@ -1226,6 +1262,9 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
        if (udp4_csum_init(skb, uh, proto))
                goto csum_error;
 
+       saddr = ip_hdr(skb)->saddr;
+       daddr = ip_hdr(skb)->daddr;
+
        if (rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST))
                return __udp4_lib_mcast_deliver(net, skb, uh,
                                saddr, daddr, udptable);
@@ -1263,13 +1302,13 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
        return 0;
 
 short_packet:
-       LIMIT_NETDEBUG(KERN_DEBUG "UDP%s: short packet: From " NIPQUAD_FMT ":%u %d/%d to " NIPQUAD_FMT ":%u\n",
+       LIMIT_NETDEBUG(KERN_DEBUG "UDP%s: short packet: From %pI4:%u %d/%d to %pI4:%u\n",
                       proto == IPPROTO_UDPLITE ? "-Lite" : "",
-                      NIPQUAD(saddr),
+                      &saddr,
                       ntohs(uh->source),
                       ulen,
                       skb->len,
-                      NIPQUAD(daddr),
+                      &daddr,
                       ntohs(uh->dest));
        goto drop;
 
@@ -1278,11 +1317,11 @@ csum_error:
         * RFC1122: OK.  Discards the bad packet silently (as far as
         * the network is concerned, anyway) as per 4.1.3.4 (MUST).
         */
-       LIMIT_NETDEBUG(KERN_DEBUG "UDP%s: bad checksum. From " NIPQUAD_FMT ":%u to " NIPQUAD_FMT ":%u ulen %d\n",
+       LIMIT_NETDEBUG(KERN_DEBUG "UDP%s: bad checksum. From %pI4:%u to %pI4:%u ulen %d\n",
                       proto == IPPROTO_UDPLITE ? "-Lite" : "",
-                      NIPQUAD(saddr),
+                      &saddr,
                       ntohs(uh->source),
-                      NIPQUAD(daddr),
+                      &daddr,
                       ntohs(uh->dest),
                       ulen);
 drop:
@@ -1553,10 +1592,10 @@ static struct sock *udp_get_first(struct seq_file *seq, int start)
        struct net *net = seq_file_net(seq);
 
        for (state->bucket = start; state->bucket < UDP_HTABLE_SIZE; ++state->bucket) {
-               struct hlist_node *node;
+               struct hlist_nulls_node *node;
                struct udp_hslot *hslot = &state->udp_table->hash[state->bucket];
                spin_lock_bh(&hslot->lock);
-               sk_for_each(sk, node, &hslot->head) {
+               sk_nulls_for_each(sk, node, &hslot->head) {
                        if (!net_eq(sock_net(sk), net))
                                continue;
                        if (sk->sk_family == state->family)
@@ -1575,11 +1614,12 @@ static struct sock *udp_get_next(struct seq_file *seq, struct sock *sk)
        struct net *net = seq_file_net(seq);
 
        do {
-               sk = sk_next(sk);
+               sk = sk_nulls_next(sk);
        } while (sk && (!net_eq(sock_net(sk), net) || sk->sk_family != state->family));
 
        if (!sk) {
-               spin_unlock_bh(&state->udp_table->hash[state->bucket].lock);
+               if (state->bucket < UDP_HTABLE_SIZE)
+                       spin_unlock_bh(&state->udp_table->hash[state->bucket].lock);
                return udp_get_first(seq, state->bucket + 1);
        }
        return sk;
@@ -1597,6 +1637,9 @@ static struct sock *udp_get_idx(struct seq_file *seq, loff_t pos)
 
 static void *udp_seq_start(struct seq_file *seq, loff_t *pos)
 {
+       struct udp_iter_state *state = seq->private;
+       state->bucket = UDP_HTABLE_SIZE;
+
        return *pos ? udp_get_idx(seq, *pos-1) : SEQ_START_TOKEN;
 }
 
@@ -1746,7 +1789,7 @@ void __init udp_table_init(struct udp_table *table)
        int i;
 
        for (i = 0; i < UDP_HTABLE_SIZE; i++) {
-               INIT_HLIST_HEAD(&table->hash[i].head);
+               INIT_HLIST_NULLS_HEAD(&table->hash[i].head, i);
                spin_lock_init(&table->hash[i].lock);
        }
 }