Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / net / ipv6 / udp.c
index e251d0b..8d3432a 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/ipv6.h>
 #include <linux/icmpv6.h>
 #include <linux/init.h>
+#include <linux/skbuff.h>
 #include <asm/uaccess.h>
 
 #include <net/sock.h>
@@ -51,6 +52,7 @@
 #include <net/udp.h>
 #include <net/raw.h>
 #include <net/inet_common.h>
+#include <net/tcp_states.h>
 
 #include <net/ip6_checksum.h>
 #include <net/xfrm.h>
@@ -58,7 +60,7 @@
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 
-DEFINE_SNMP_STAT(struct udp_mib, udp_stats_in6);
+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.
@@ -98,7 +100,7 @@ static int udp_v6_get_port(struct sock *sk, unsigned short snum)
                next:;
                }
                result = best;
-               for(;; result += UDP_HTABLE_SIZE) {
+               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]) &
@@ -106,6 +108,8 @@ static int udp_v6_get_port(struct sock *sk, unsigned short snum)
                        if (!udp_lport_inuse(result))
                                break;
                }
+               if (i >= (1 << 16) / UDP_HTABLE_SIZE)
+                       goto fail;
 gotit:
                udp_port_rover = snum = result;
        } else {
@@ -245,7 +249,7 @@ try_again:
                err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,
                                              copied);
        } else if (msg->msg_flags&MSG_TRUNC) {
-               if ((unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum)))
+               if (__skb_checksum_complete(skb))
                        goto csum_copy_err;
                err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,
                                              copied);
@@ -297,20 +301,7 @@ out:
        return err;
 
 csum_copy_err:
-       /* Clear queue. */
-       if (flags&MSG_PEEK) {
-               int clear = 0;
-               spin_lock_irq(&sk->sk_receive_queue.lock);
-               if (skb == skb_peek(&sk->sk_receive_queue)) {
-                       __skb_unlink(skb, &sk->sk_receive_queue);
-                       clear = 1;
-               }
-               spin_unlock_irq(&sk->sk_receive_queue.lock);
-               if (clear)
-                       kfree_skb(skb);
-       }
-
-       skb_free_datagram(sk, skb);
+       skb_kill_datagram(sk, skb, flags);
 
        if (flags & MSG_DONTWAIT) {
                UDP6_INC_STATS_USER(UDP_MIB_INERRORS);
@@ -360,13 +351,10 @@ static inline int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
                return -1;
        }
 
-       if (skb->ip_summed != CHECKSUM_UNNECESSARY) {
-               if ((unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum))) {
-                       UDP6_INC_STATS_BH(UDP_MIB_INERRORS);
-                       kfree_skb(skb);
-                       return 0;
-               }
-               skb->ip_summed = CHECKSUM_UNNECESSARY;
+       if (skb_checksum_complete(skb)) {
+               UDP6_INC_STATS_BH(UDP_MIB_INERRORS);
+               kfree_skb(skb);
+               return 0;
        }
 
        if (sock_queue_rcv_skb(sk,skb)<0) {
@@ -404,9 +392,8 @@ static struct sock *udp_v6_mcast_next(struct sock *sk,
                                continue;
 
                        if (!ipv6_addr_any(&np->rcv_saddr)) {
-                               if (ipv6_addr_equal(&np->rcv_saddr, loc_addr))
-                                       return s;
-                               continue;
+                               if (!ipv6_addr_equal(&np->rcv_saddr, loc_addr))
+                                       continue;
                        }
                        if(!inet6_mc_check(s, loc_addr, rmt_addr))
                                continue;
@@ -448,7 +435,7 @@ out:
        read_unlock(&udp_hash_lock);
 }
 
-static int udpv6_rcv(struct sk_buff **pskb, unsigned int *nhoffp)
+static int udpv6_rcv(struct sk_buff **pskb)
 {
        struct sk_buff *skb = *pskb;
        struct sock *sk;
@@ -477,26 +464,22 @@ static int udpv6_rcv(struct sk_buff **pskb, unsigned int *nhoffp)
                /* RFC 2460 section 8.1 says that we SHOULD log
                   this error. Well, it is reasonable.
                 */
-               LIMIT_NETDEBUG(
-                       printk(KERN_INFO "IPv6: udp checksum is 0\n"));
+               LIMIT_NETDEBUG(KERN_INFO "IPv6: udp checksum is 0\n");
                goto discard;
        }
 
        if (ulen < skb->len) {
-               if (__pskb_trim(skb, ulen))
+               if (pskb_trim_rcsum(skb, ulen))
                        goto discard;
                saddr = &skb->nh.ipv6h->saddr;
                daddr = &skb->nh.ipv6h->daddr;
                uh = skb->h.uh;
        }
 
-       if (skb->ip_summed==CHECKSUM_HW) {
+       if (skb->ip_summed == CHECKSUM_HW &&
+           !csum_ipv6_magic(saddr, daddr, ulen, IPPROTO_UDP, skb->csum))
                skb->ip_summed = CHECKSUM_UNNECESSARY;
-               if (csum_ipv6_magic(saddr, daddr, ulen, IPPROTO_UDP, skb->csum)) {
-                       LIMIT_NETDEBUG(printk(KERN_DEBUG "udp v6 hw csum failure.\n"));
-                       skb->ip_summed = CHECKSUM_NONE;
-               }
-       }
+
        if (skb->ip_summed != CHECKSUM_UNNECESSARY)
                skb->csum = ~csum_ipv6_magic(saddr, daddr, ulen, IPPROTO_UDP, 0);
 
@@ -520,8 +503,7 @@ static int udpv6_rcv(struct sk_buff **pskb, unsigned int *nhoffp)
                if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb))
                        goto discard;
 
-               if (skb->ip_summed != CHECKSUM_UNNECESSARY &&
-                   (unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum)))
+               if (skb_checksum_complete(skb))
                        goto discard;
                UDP6_INC_STATS_BH(UDP_MIB_NOPORTS);
 
@@ -637,8 +619,10 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk,
        int addr_len = msg->msg_namelen;
        int ulen = len;
        int hlimit = -1;
+       int tclass = -1;
        int corkreq = up->corkflag || msg->msg_flags&MSG_MORE;
        int err;
+       int connected = 0;
 
        /* destination address check */
        if (sin6) {
@@ -748,6 +732,7 @@ do_udp_sendmsg:
                fl->fl_ip_dport = inet->dport;
                daddr = &np->daddr;
                fl->fl6_flowlabel = np->flow_label;
+               connected = 1;
        }
 
        if (!fl->oif)
@@ -758,7 +743,7 @@ do_udp_sendmsg:
                memset(opt, 0, sizeof(struct ipv6_txoptions));
                opt->tot_len = sizeof(*opt);
 
-               err = datagram_send_ctl(msg, fl, opt, &hlimit);
+               err = datagram_send_ctl(msg, fl, opt, &hlimit, &tclass);
                if (err < 0) {
                        fl6_sock_release(flowlabel);
                        return err;
@@ -770,11 +755,13 @@ do_udp_sendmsg:
                }
                if (!(opt->opt_nflen|opt->opt_flen))
                        opt = NULL;
+               connected = 0;
        }
        if (opt == NULL)
                opt = np->opt;
        if (flowlabel)
                opt = fl6_merge_options(&opt_space, flowlabel, opt);
+       opt = ipv6_fixup_options(&opt_space, opt);
 
        fl->proto = IPPROTO_UDP;
        ipv6_addr_copy(&fl->fl6_dst, daddr);
@@ -788,10 +775,13 @@ do_udp_sendmsg:
                ipv6_addr_copy(&final, &fl->fl6_dst);
                ipv6_addr_copy(&fl->fl6_dst, rt0->addr);
                final_p = &final;
+               connected = 0;
        }
 
-       if (!fl->oif && ipv6_addr_is_multicast(&fl->fl6_dst))
+       if (!fl->oif && ipv6_addr_is_multicast(&fl->fl6_dst)) {
                fl->oif = np->mcast_oif;
+               connected = 0;
+       }
 
        err = ip6_dst_lookup(sk, &dst, fl);
        if (err)
@@ -799,10 +789,8 @@ do_udp_sendmsg:
        if (final_p)
                ipv6_addr_copy(&fl->fl6_dst, final_p);
 
-       if ((err = xfrm_lookup(&dst, fl, sk, 0)) < 0) {
-               dst_release(dst);
+       if ((err = xfrm_lookup(&dst, fl, sk, 0)) < 0)
                goto out;
-       }
 
        if (hlimit < 0) {
                if (ipv6_addr_is_multicast(&fl->fl6_dst))
@@ -815,6 +803,12 @@ do_udp_sendmsg:
                        hlimit = ipv6_get_hoplimit(dst->dev);
        }
 
+       if (tclass < 0) {
+               tclass = np->tclass;
+               if (tclass < 0)
+                       tclass = 0;
+       }
+
        if (msg->msg_flags&MSG_CONFIRM)
                goto do_confirm;
 back_from_confirm:
@@ -825,7 +819,7 @@ back_from_confirm:
                /* ... which is an evident application bug. --ANK */
                release_sock(sk);
 
-               LIMIT_NETDEBUG(printk(KERN_DEBUG "udp cork app bug 2\n"));
+               LIMIT_NETDEBUG(KERN_DEBUG "udp cork app bug 2\n");
                err = -EINVAL;
                goto out;
        }
@@ -834,18 +828,25 @@ back_from_confirm:
 
 do_append_data:
        up->len += ulen;
-       err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov, ulen, sizeof(struct udphdr),
-                             hlimit, opt, fl, (struct rt6_info*)dst,
-                             corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags);
+       err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov, ulen,
+               sizeof(struct udphdr), hlimit, tclass, opt, fl,
+               (struct rt6_info*)dst,
+               corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags);
        if (err)
                udp_v6_flush_pending_frames(sk);
        else if (!corkreq)
                err = udp_v6_push_pending_frames(sk, up);
 
-       if (dst)
-               ip6_dst_store(sk, dst,
-                             ipv6_addr_equal(&fl->fl6_dst, &np->daddr) ?
-                             &np->daddr : NULL);
+       if (dst) {
+               if (connected) {
+                       ip6_dst_store(sk, dst,
+                                     ipv6_addr_equal(&fl->fl6_dst, &np->daddr) ?
+                                     &np->daddr : NULL);
+               } else {
+                       dst_release(dst);
+               }
+       }
+
        if (err > 0)
                err = np->recverr ? net_xmit_errno(err) : 0;
        release_sock(sk);
@@ -879,16 +880,13 @@ static int udpv6_destroy_sock(struct sock *sk)
 /*
  *     Socket option code for UDP
  */
-static int udpv6_setsockopt(struct sock *sk, int level, int optname, 
+static int do_udpv6_setsockopt(struct sock *sk, int level, int optname,
                          char __user *optval, int optlen)
 {
        struct udp_sock *up = udp_sk(sk);
        int val;
        int err = 0;
 
-       if (level != SOL_UDP)
-               return ipv6_setsockopt(sk, level, optname, optval, optlen);
-
        if(optlen<sizeof(int))
                return -EINVAL;
 
@@ -926,15 +924,31 @@ static int udpv6_setsockopt(struct sock *sk, int level, int optname,
        return err;
 }
 
-static int udpv6_getsockopt(struct sock *sk, int level, int optname, 
+static int udpv6_setsockopt(struct sock *sk, int level, int optname,
+                         char __user *optval, int optlen)
+{
+       if (level != SOL_UDP)
+               return ipv6_setsockopt(sk, level, optname, optval, optlen);
+       return do_udpv6_setsockopt(sk, level, optname, optval, optlen);
+}
+
+#ifdef CONFIG_COMPAT
+static int compat_udpv6_setsockopt(struct sock *sk, int level, int optname,
+                                  char __user *optval, int optlen)
+{
+       if (level != SOL_UDP)
+               return compat_ipv6_setsockopt(sk, level, optname,
+                                             optval, optlen);
+       return do_udpv6_setsockopt(sk, level, optname, optval, optlen);
+}
+#endif
+
+static int do_udpv6_getsockopt(struct sock *sk, int level, int optname,
                          char __user *optval, int __user *optlen)
 {
        struct udp_sock *up = udp_sk(sk);
        int val, len;
 
-       if (level != SOL_UDP)
-               return ipv6_getsockopt(sk, level, optname, optval, optlen);
-
        if(get_user(len,optlen))
                return -EFAULT;
 
@@ -963,6 +977,25 @@ static int udpv6_getsockopt(struct sock *sk, int level, int optname,
        return 0;
 }
 
+static int udpv6_getsockopt(struct sock *sk, int level, int optname,
+                         char __user *optval, int __user *optlen)
+{
+       if (level != SOL_UDP)
+               return ipv6_getsockopt(sk, level, optname, optval, optlen);
+       return do_udpv6_getsockopt(sk, level, optname, optval, optlen);
+}
+
+#ifdef CONFIG_COMPAT
+static int compat_udpv6_getsockopt(struct sock *sk, int level, int optname,
+                                  char __user *optval, int __user *optlen)
+{
+       if (level != SOL_UDP)
+               return compat_ipv6_getsockopt(sk, level, optname,
+                                             optval, optlen);
+       return do_udpv6_getsockopt(sk, level, optname, optval, optlen);
+}
+#endif
+
 static struct inet6_protocol udpv6_protocol = {
        .handler        =       udpv6_rcv,
        .err_handler    =       udpv6_err,
@@ -1036,26 +1069,28 @@ void udp6_proc_exit(void) {
 /* ------------------------------------------------------------------------ */
 
 struct proto udpv6_prot = {
-       .name =         "UDPv6",
-       .owner =        THIS_MODULE,
-       .close =        udpv6_close,
-       .connect =      ip6_datagram_connect,
-       .disconnect =   udp_disconnect,
-       .ioctl =        udp_ioctl,
-       .destroy =      udpv6_destroy_sock,
-       .setsockopt =   udpv6_setsockopt,
-       .getsockopt =   udpv6_getsockopt,
-       .sendmsg =      udpv6_sendmsg,
-       .recvmsg =      udpv6_recvmsg,
-       .backlog_rcv =  udpv6_queue_rcv_skb,
-       .hash =         udp_v6_hash,
-       .unhash =       udp_v6_unhash,
-       .get_port =     udp_v6_get_port,
-       .obj_size =     sizeof(struct udp6_sock),
+       .name              = "UDPv6",
+       .owner             = THIS_MODULE,
+       .close             = udpv6_close,
+       .connect           = ip6_datagram_connect,
+       .disconnect        = udp_disconnect,
+       .ioctl             = udp_ioctl,
+       .destroy           = udpv6_destroy_sock,
+       .setsockopt        = udpv6_setsockopt,
+       .getsockopt        = udpv6_getsockopt,
+       .sendmsg           = udpv6_sendmsg,
+       .recvmsg           = udpv6_recvmsg,
+       .backlog_rcv       = udpv6_queue_rcv_skb,
+       .hash              = udp_v6_hash,
+       .unhash            = udp_v6_unhash,
+       .get_port          = udp_v6_get_port,
+       .obj_size          = sizeof(struct udp6_sock),
+#ifdef CONFIG_COMPAT
+       .compat_setsockopt = compat_udpv6_setsockopt,
+       .compat_getsockopt = compat_udpv6_getsockopt,
+#endif
 };
 
-extern struct proto_ops inet6_dgram_ops;
-
 static struct inet_protosw udpv6_protosw = {
        .type =      SOCK_DGRAM,
        .protocol =  IPPROTO_UDP,