]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/ipv6/udp.c
[ARM] 3856/1: Add clocksource for Intel IXP4xx platforms
[linux-2.6-omap-h63xx.git] / net / ipv6 / udp.c
index 8d3432a70f3a6e25d1a32cb5793e91f8fa380bca..9662561701d1c8463b53f0f1fe73dd89a2ba44d7 100644 (file)
@@ -23,7 +23,6 @@
  *      2 of the License, or (at your option) any later version.
  */
 
-#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/socket.h>
 
 DEFINE_SNMP_STAT(struct udp_mib, udp_stats_in6) __read_mostly;
 
-/* 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_get_port(struct sock *sk, unsigned short snum)
+static inline int udp_v6_get_port(struct sock *sk, unsigned short snum)
 {
-       struct sock *sk2;
-       struct hlist_node *node;
-
-       write_lock_bh(&udp_hash_lock);
-       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++) {
-                       int size;
-                       struct hlist_head *list;
-
-                       list = &udp_hash[result & (UDP_HTABLE_SIZE - 1)];
-                       if (hlist_empty(list)) {
-                               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;
-                       sk_for_each(sk2, node, list)
-                               if (++size >= best_size_so_far)
-                                       goto next;
-                       best_size_so_far = size;
-                       best = result;
-               next:;
-               }
-               result = best;
-               for(i = 0; i < (1 << 16) / UDP_HTABLE_SIZE; i++, 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;
-               }
-               if (i >= (1 << 16) / UDP_HTABLE_SIZE)
-                       goto fail;
-gotit:
-               udp_port_rover = snum = result;
-       } else {
-               sk_for_each(sk2, node,
-                           &udp_hash[snum & (UDP_HTABLE_SIZE - 1)]) {
-                       if (inet_sk(sk2)->num == snum &&
-                           sk2 != sk &&
-                           (!sk2->sk_bound_dev_if ||
-                            !sk->sk_bound_dev_if ||
-                            sk2->sk_bound_dev_if == sk->sk_bound_dev_if) &&
-                           (!sk2->sk_reuse || !sk->sk_reuse) &&
-                           ipv6_rcv_saddr_equal(sk, sk2))
-                               goto fail;
-               }
-       }
-
-       inet_sk(sk)->num = snum;
-       if (sk_unhashed(sk)) {
-               sk_add_node(sk, &udp_hash[snum & (UDP_HTABLE_SIZE - 1)]);
-               sock_prot_inc_use(sk->sk_prot);
-       }
-       write_unlock_bh(&udp_hash_lock);
-       return 0;
-
-fail:
-       write_unlock_bh(&udp_hash_lock);
-       return 1;
+       return udp_get_port(sk, snum, ipv6_rcv_saddr_equal);
 }
 
 static void udp_v6_hash(struct sock *sk)
@@ -346,6 +273,8 @@ out:
 
 static inline int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
 {
+       int rc;
+
        if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) {
                kfree_skb(skb);
                return -1;
@@ -357,7 +286,10 @@ static inline int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
                return 0;
        }
 
-       if (sock_queue_rcv_skb(sk,skb)<0) {
+       if ((rc = sock_queue_rcv_skb(sk,skb)) < 0) {
+               /* Note that an ENOMEM error is charged twice */
+               if (rc == -ENOMEM)
+                       UDP6_INC_STATS_BH(UDP_MIB_RCVBUFERRORS);
                UDP6_INC_STATS_BH(UDP_MIB_INERRORS);
                kfree_skb(skb);
                return 0;
@@ -476,7 +408,7 @@ static int udpv6_rcv(struct sk_buff **pskb)
                uh = skb->h.uh;
        }
 
-       if (skb->ip_summed == CHECKSUM_HW &&
+       if (skb->ip_summed == CHECKSUM_COMPLETE &&
            !csum_ipv6_magic(saddr, daddr, ulen, IPPROTO_UDP, skb->csum))
                skb->ip_summed = CHECKSUM_UNNECESSARY;
 
@@ -783,7 +715,9 @@ do_udp_sendmsg:
                connected = 0;
        }
 
-       err = ip6_dst_lookup(sk, &dst, fl);
+       security_sk_classify_flow(sk, fl);
+
+       err = ip6_sk_dst_lookup(sk, &dst, fl);
        if (err)
                goto out;
        if (final_p)
@@ -841,7 +775,12 @@ do_append_data:
                if (connected) {
                        ip6_dst_store(sk, dst,
                                      ipv6_addr_equal(&fl->fl6_dst, &np->daddr) ?
-                                     &np->daddr : NULL);
+                                     &np->daddr : NULL,
+#ifdef CONFIG_IPV6_SUBTREES
+                                     ipv6_addr_equal(&fl->fl6_src, &np->saddr) ?
+                                     &np->saddr :
+#endif
+                                     NULL);
                } else {
                        dst_release(dst);
                }
@@ -856,6 +795,16 @@ out:
                UDP6_INC_STATS_USER(UDP_MIB_OUTDATAGRAMS);
                return len;
        }
+       /*
+        * ENOBUFS = no kernel mem, SOCK_NOSPACE = no sndbuf space.  Reporting
+        * ENOBUFS might not be good (it's not tunable per se), but otherwise
+        * we don't have a good statistic (IpOutDiscards but it can be too many
+        * things).  We could add another new stat but at least for now that
+        * seems like overkill.
+        */
+       if (err == -ENOBUFS || test_bit(SOCK_NOSPACE, &sk->sk_socket->flags)) {
+               UDP6_INC_STATS_USER(UDP_MIB_SNDBUFERRORS);
+       }
        return err;
 
 do_confirm: