Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / net / ipv6 / udp.c
index ff00dc4..3d54f24 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>
@@ -36,6 +35,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 +51,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 +59,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 +99,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 +107,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 {
@@ -160,7 +163,7 @@ static struct sock *udp_v6_lookup(struct in6_addr *saddr, u16 sport,
 
        read_lock(&udp_hash_lock);
        sk_for_each(sk, node, &udp_hash[hnum & (UDP_HTABLE_SIZE - 1)]) {
-               struct inet_opt *inet = inet_sk(sk);
+               struct inet_sock *inet = inet_sk(sk);
 
                if (inet->num == hnum && sk->sk_family == PF_INET6) {
                        struct ipv6_pinfo *np = inet6_sk(sk);
@@ -171,12 +174,12 @@ static struct sock *udp_v6_lookup(struct in6_addr *saddr, u16 sport,
                                score++;
                        }
                        if (!ipv6_addr_any(&np->rcv_saddr)) {
-                               if (ipv6_addr_cmp(&np->rcv_saddr, daddr))
+                               if (!ipv6_addr_equal(&np->rcv_saddr, daddr))
                                        continue;
                                score++;
                        }
                        if (!ipv6_addr_any(&np->daddr)) {
-                               if (ipv6_addr_cmp(&np->daddr, saddr))
+                               if (!ipv6_addr_equal(&np->daddr, saddr))
                                        continue;
                                score++;
                        }
@@ -219,6 +222,7 @@ static int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk,
                  int noblock, int flags, int *addr_len)
 {
        struct ipv6_pinfo *np = inet6_sk(sk);
+       struct inet_sock *inet = inet_sk(sk);
        struct sk_buff *skb;
        size_t copied;
        int err;
@@ -244,7 +248,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);
@@ -268,21 +272,22 @@ try_again:
                sin6->sin6_flowinfo = 0;
                sin6->sin6_scope_id = 0;
 
-               if (skb->protocol == htons(ETH_P_IP)) {
-                       struct inet_opt *inet = inet_sk(sk);
-
+               if (skb->protocol == htons(ETH_P_IP))
                        ipv6_addr_set(&sin6->sin6_addr, 0, 0,
                                      htonl(0xffff), skb->nh.iph->saddr);
-                       if (inet->cmsg_flags)
-                               ip_cmsg_recv(msg, skb);
-               } else {
+               else {
                        ipv6_addr_copy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr);
-
-                       if (np->rxopt.all)
-                               datagram_recv_ctl(sk, msg, skb);
                        if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL)
                                sin6->sin6_scope_id = IP6CB(skb)->iif;
                }
+
+       }
+       if (skb->protocol == htons(ETH_P_IP)) {
+               if (inet->cmsg_flags)
+                       ip_cmsg_recv(msg, skb);
+       } else {
+               if (np->rxopt.all)
+                       datagram_recv_ctl(sk, msg, skb);
        }
 
        err = copied;
@@ -295,20 +300,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);
@@ -358,13 +350,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) {
@@ -386,7 +375,7 @@ static struct sock *udp_v6_mcast_next(struct sock *sk,
        unsigned short num = ntohs(loc_port);
 
        sk_for_each_from(s, node) {
-               struct inet_opt *inet = inet_sk(s);
+               struct inet_sock *inet = inet_sk(s);
 
                if (inet->num == num && s->sk_family == PF_INET6) {
                        struct ipv6_pinfo *np = inet6_sk(s);
@@ -395,16 +384,15 @@ static struct sock *udp_v6_mcast_next(struct sock *sk,
                                        continue;
                        }
                        if (!ipv6_addr_any(&np->daddr) &&
-                           ipv6_addr_cmp(&np->daddr, rmt_addr))
+                           !ipv6_addr_equal(&np->daddr, rmt_addr))
                                continue;
 
                        if (s->sk_bound_dev_if && s->sk_bound_dev_if != dif)
                                continue;
 
                        if (!ipv6_addr_any(&np->rcv_saddr)) {
-                               if (!ipv6_addr_cmp(&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;
@@ -446,7 +434,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;
@@ -475,26 +463,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);
 
@@ -518,8 +502,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);
 
@@ -549,7 +532,7 @@ discard:
  */
 static void udp_v6_flush_pending_frames(struct sock *sk)
 {
-       struct udp_opt *up = udp_sk(sk);
+       struct udp_sock *up = udp_sk(sk);
 
        if (up->pending) {
                up->len = 0;
@@ -562,11 +545,11 @@ static void udp_v6_flush_pending_frames(struct sock *sk)
  *     Sending
  */
 
-static int udp_v6_push_pending_frames(struct sock *sk, struct udp_opt *up)
+static int udp_v6_push_pending_frames(struct sock *sk, struct udp_sock *up)
 {
        struct sk_buff *skb;
        struct udphdr *uh;
-       struct inet_opt *inet = inet_sk(sk);
+       struct inet_sock *inet = inet_sk(sk);
        struct flowi *fl = &inet->cork.fl;
        int err = 0;
 
@@ -623,8 +606,8 @@ static int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk,
                  struct msghdr *msg, size_t len)
 {
        struct ipv6_txoptions opt_space;
-       struct udp_opt *up = udp_sk(sk);
-       struct inet_opt *inet = inet_sk(sk);
+       struct udp_sock *up = udp_sk(sk);
+       struct inet_sock *inet = inet_sk(sk);
        struct ipv6_pinfo *np = inet6_sk(sk);
        struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) msg->msg_name;
        struct in6_addr *daddr, *final_p = NULL, final;
@@ -635,8 +618,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) {
@@ -699,7 +684,7 @@ do_udp_sendmsg:
                if (likely(up->pending)) {
                        if (unlikely(up->pending != AF_INET6)) {
                                release_sock(sk);
-                               return -EINVAL;
+                               return -EAFNOSUPPORT;
                        }
                        dst = NULL;
                        goto do_append_data;
@@ -732,7 +717,7 @@ do_udp_sendmsg:
                 * sk->sk_dst_cache.
                 */
                if (sk->sk_state == TCP_ESTABLISHED &&
-                   !ipv6_addr_cmp(daddr, &np->daddr))
+                   ipv6_addr_equal(daddr, &np->daddr))
                        daddr = &np->daddr;
 
                if (addr_len >= sizeof(struct sockaddr_in6) &&
@@ -746,6 +731,7 @@ do_udp_sendmsg:
                fl->fl_ip_dport = inet->dport;
                daddr = &np->daddr;
                fl->fl6_flowlabel = np->flow_label;
+               connected = 1;
        }
 
        if (!fl->oif)
@@ -756,7 +742,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;
@@ -768,11 +754,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);
@@ -786,21 +774,22 @@ 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);
+       err = ip6_sk_dst_lookup(sk, &dst, fl);
        if (err)
                goto out;
        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))
@@ -809,6 +798,14 @@ do_udp_sendmsg:
                        hlimit = np->hop_limit;
                if (hlimit < 0)
                        hlimit = dst_metric(dst, RTAX_HOPLIMIT);
+               if (hlimit < 0)
+                       hlimit = ipv6_get_hoplimit(dst->dev);
+       }
+
+       if (tclass < 0) {
+               tclass = np->tclass;
+               if (tclass < 0)
+                       tclass = 0;
        }
 
        if (msg->msg_flags&MSG_CONFIRM)
@@ -821,7 +818,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;
        }
@@ -830,18 +827,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_cmp(&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);
@@ -875,16 +879,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_opt *up = udp_sk(sk);
+       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;
 
@@ -922,14 +923,30 @@ static int udpv6_setsockopt(struct sock *sk, int level, int optname,
        return err;
 }
 
-static int udpv6_getsockopt(struct sock *sk, int level, int optname, 
-                         char __user *optval, int __user *optlen)
+static int udpv6_setsockopt(struct sock *sk, int level, int optname,
+                         char __user *optval, int optlen)
 {
-       struct udp_opt *up = udp_sk(sk);
-       int val, len;
+       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 ipv6_getsockopt(sk, level, optname, optval, optlen);
+               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(get_user(len,optlen))
                return -EFAULT;
@@ -959,6 +976,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,
@@ -970,7 +1006,7 @@ static struct inet6_protocol udpv6_protocol = {
 
 static void udp6_sock_seq_show(struct seq_file *seq, struct sock *sp, int bucket)
 {
-       struct inet_opt *inet = inet_sk(sp);
+       struct inet_sock *inet = inet_sk(sp);
        struct ipv6_pinfo *np = inet6_sk(sp);
        struct in6_addr *dest, *src;
        __u16 destp, srcp;
@@ -1032,25 +1068,28 @@ void udp6_proc_exit(void) {
 /* ------------------------------------------------------------------------ */
 
 struct proto udpv6_prot = {
-       .name =         "UDP",
-       .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,
-       .slab_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,