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 0dc7f0e..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>
 #include <net/addrconf.h>
 #include <net/ip.h>
 #include <net/udp.h>
+#include <net/raw.h>
 #include <net/inet_common.h>
+#include <net/tcp_states.h>
 
-#include <net/checksum.h>
+#include <net/ip6_checksum.h>
 #include <net/xfrm.h>
 
 #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.
@@ -97,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]) &
@@ -105,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 {
@@ -159,7 +164,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);
@@ -170,12 +175,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++;
                        }
@@ -203,162 +208,9 @@ static struct sock *udp_v6_lookup(struct in6_addr *saddr, u16 sport,
  *
  */
 
-int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
-{
-       struct sockaddr_in6     *usin = (struct sockaddr_in6 *) uaddr;
-       struct inet_opt         *inet = inet_sk(sk);
-       struct ipv6_pinfo       *np = inet6_sk(sk);
-       struct in6_addr         *daddr;
-       struct dst_entry        *dst;
-       struct flowi            fl;
-       struct ip6_flowlabel    *flowlabel = NULL;
-       int                     addr_type;
-       int                     err;
-
-       if (usin->sin6_family == AF_INET) {
-               if (__ipv6_only_sock(sk))
-                       return -EAFNOSUPPORT;
-               err = udp_connect(sk, uaddr, addr_len);
-               goto ipv4_connected;
-       }
-
-       if (addr_len < SIN6_LEN_RFC2133)
-               return -EINVAL;
-
-       if (usin->sin6_family != AF_INET6) 
-               return -EAFNOSUPPORT;
-
-       memset(&fl, 0, sizeof(fl));
-       if (np->sndflow) {
-               fl.fl6_flowlabel = usin->sin6_flowinfo&IPV6_FLOWINFO_MASK;
-               if (fl.fl6_flowlabel&IPV6_FLOWLABEL_MASK) {
-                       flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel);
-                       if (flowlabel == NULL)
-                               return -EINVAL;
-                       ipv6_addr_copy(&usin->sin6_addr, &flowlabel->dst);
-               }
-       }
-
-       addr_type = ipv6_addr_type(&usin->sin6_addr);
-
-       if (addr_type == IPV6_ADDR_ANY) {
-               /*
-                *      connect to self
-                */
-               usin->sin6_addr.s6_addr[15] = 0x01;
-       }
-
-       daddr = &usin->sin6_addr;
-
-       if (addr_type == IPV6_ADDR_MAPPED) {
-               struct sockaddr_in sin;
-
-               if (__ipv6_only_sock(sk)) {
-                       err = -ENETUNREACH;
-                       goto out;
-               }
-               sin.sin_family = AF_INET;
-               sin.sin_addr.s_addr = daddr->s6_addr32[3];
-               sin.sin_port = usin->sin6_port;
-
-               err = udp_connect(sk, (struct sockaddr*) &sin, sizeof(sin));
-
-ipv4_connected:
-               if (err)
-                       goto out;
-               
-               ipv6_addr_set(&np->daddr, 0, 0, htonl(0x0000ffff), inet->daddr);
-
-               if (ipv6_addr_any(&np->saddr)) {
-                       ipv6_addr_set(&np->saddr, 0, 0, htonl(0x0000ffff),
-                                     inet->saddr);
-               }
-
-               if (ipv6_addr_any(&np->rcv_saddr)) {
-                       ipv6_addr_set(&np->rcv_saddr, 0, 0, htonl(0x0000ffff),
-                                     inet->rcv_saddr);
-               }
-               goto out;
-       }
-
-       if (addr_type&IPV6_ADDR_LINKLOCAL) {
-               if (addr_len >= sizeof(struct sockaddr_in6) &&
-                   usin->sin6_scope_id) {
-                       if (sk->sk_bound_dev_if &&
-                           sk->sk_bound_dev_if != usin->sin6_scope_id) {
-                               err = -EINVAL;
-                               goto out;
-                       }
-                       sk->sk_bound_dev_if = usin->sin6_scope_id;
-                       if (!sk->sk_bound_dev_if &&
-                           (addr_type & IPV6_ADDR_MULTICAST))
-                               fl.oif = np->mcast_oif;
-               }
-
-               /* Connect to link-local address requires an interface */
-               if (!sk->sk_bound_dev_if) {
-                       err = -EINVAL;
-                       goto out;
-               }
-       }
-
-       ipv6_addr_copy(&np->daddr, daddr);
-       np->flow_label = fl.fl6_flowlabel;
-
-       inet->dport = usin->sin6_port;
-
-       /*
-        *      Check for a route to destination an obtain the
-        *      destination cache for it.
-        */
-
-       fl.proto = IPPROTO_UDP;
-       ipv6_addr_copy(&fl.fl6_dst, &np->daddr);
-       ipv6_addr_copy(&fl.fl6_src, &np->saddr);
-       fl.oif = sk->sk_bound_dev_if;
-       fl.fl_ip_dport = inet->dport;
-       fl.fl_ip_sport = inet->sport;
-
-       if (!fl.oif && (addr_type&IPV6_ADDR_MULTICAST))
-               fl.oif = np->mcast_oif;
-
-       if (flowlabel) {
-               if (flowlabel->opt && flowlabel->opt->srcrt) {
-                       struct rt0_hdr *rt0 = (struct rt0_hdr *) flowlabel->opt->srcrt;
-                       ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
-               }
-       } else if (np->opt && np->opt->srcrt) {
-               struct rt0_hdr *rt0 = (struct rt0_hdr *)np->opt->srcrt;
-               ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
-       }
-
-       err = ip6_dst_lookup(sk, &dst, &fl);
-       if (err)
-               goto out;
-
-       /* source address lookup done in ip6_dst_lookup */
-
-       if (ipv6_addr_any(&np->saddr))
-               ipv6_addr_copy(&np->saddr, &fl.fl6_src);
-
-       if (ipv6_addr_any(&np->rcv_saddr)) {
-               ipv6_addr_copy(&np->rcv_saddr, &fl.fl6_src);
-               inet->rcv_saddr = LOOPBACK4_IPV6;
-       }
-
-       ip6_dst_store(sk, dst,
-                     !ipv6_addr_cmp(&fl.fl6_dst, &np->daddr) ?
-                     &np->daddr : NULL);
-
-       sk->sk_state = TCP_ESTABLISHED;
-out:
-       fl6_sock_release(flowlabel);
-       return err;
-}
-
 static void udpv6_close(struct sock *sk, long timeout)
 {
-       inet_sock_release(sk);
+       sk_common_release(sk);
 }
 
 /*
@@ -371,6 +223,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;
@@ -396,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);
@@ -420,23 +273,27 @@ 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;
+       if (flags & MSG_TRUNC)
+               err = skb->len - sizeof(struct udphdr);
 
 out_free:
        skb_free_datagram(sk, skb);
@@ -444,23 +301,10 @@ 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(UdpInErrors);
+               UDP6_INC_STATS_USER(UDP_MIB_INERRORS);
                return -EAGAIN;
        }
        goto try_again;
@@ -507,21 +351,18 @@ static inline int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
                return -1;
        }
 
-       if (sk->sk_filter && skb->ip_summed != CHECKSUM_UNNECESSARY) {
-               if ((unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum))) {
-                       UDP6_INC_STATS_BH(UdpInErrors);
-                       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) {
-               UDP6_INC_STATS_BH(UdpInErrors);
+               UDP6_INC_STATS_BH(UDP_MIB_INERRORS);
                kfree_skb(skb);
                return 0;
        }
-       UDP6_INC_STATS_BH(UdpInDatagrams);
+       UDP6_INC_STATS_BH(UDP_MIB_INDATAGRAMS);
        return 0;
 }
 
@@ -535,7 +376,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);
@@ -544,16 +385,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;
@@ -595,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;
@@ -624,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);
 
@@ -667,10 +503,9 @@ 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(UdpNoPorts);
+               UDP6_INC_STATS_BH(UDP_MIB_NOPORTS);
 
                icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0, dev);
 
@@ -689,7 +524,7 @@ short_packet:
                printk(KERN_DEBUG "UDP: short packet: %d/%u\n", ulen, skb->len);
 
 discard:
-       UDP6_INC_STATS_BH(UdpInErrors);
+       UDP6_INC_STATS_BH(UDP_MIB_INERRORS);
        kfree_skb(skb);
        return(0);      
 }
@@ -698,7 +533,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;
@@ -711,11 +546,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;
 
@@ -772,11 +607,11 @@ 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;
+       struct in6_addr *daddr, *final_p = NULL, final;
        struct ipv6_txoptions *opt = NULL;
        struct ip6_flowlabel *flowlabel = NULL;
        struct flowi *fl = &inet->cork.fl;
@@ -784,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) {
@@ -848,7 +685,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;
@@ -881,7 +718,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) &&
@@ -895,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)
@@ -905,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;
@@ -917,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);
@@ -932,15 +772,25 @@ do_udp_sendmsg:
        /* merge ip6_build_xmit from ip6_output */
        if (opt && opt->srcrt) {
                struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt;
+               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)
                goto out;
+       if (final_p)
+               ipv6_addr_copy(&fl->fl6_dst, final_p);
+
+       if ((err = xfrm_lookup(&dst, fl, sk, 0)) < 0)
+               goto out;
 
        if (hlimit < 0) {
                if (ipv6_addr_is_multicast(&fl->fl6_dst))
@@ -949,6 +799,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)
@@ -961,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;
        }
@@ -970,25 +828,32 @@ 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);
 out:
        fl6_sock_release(flowlabel);
        if (!err) {
-               UDP6_INC_STATS_USER(UdpOutDatagrams);
+               UDP6_INC_STATS_USER(UDP_MIB_OUTDATAGRAMS);
                return len;
        }
        return err;
@@ -1015,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_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;
 
@@ -1044,7 +906,14 @@ static int udpv6_setsockopt(struct sock *sk, int level, int optname,
                break;
                
        case UDP_ENCAP:
-               up->encap_type = val;
+               switch (val) {
+               case 0:
+                       up->encap_type = val;
+                       break;
+               default:
+                       err = -ENOPROTOOPT;
+                       break;
+               }
                break;
 
        default:
@@ -1055,14 +924,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;
@@ -1092,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,
@@ -1103,7 +1007,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;
@@ -1165,24 +1069,28 @@ void udp6_proc_exit(void) {
 /* ------------------------------------------------------------------------ */
 
 struct proto udpv6_prot = {
-       .name =         "UDP",
-       .close =        udpv6_close,
-       .connect =      udpv6_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,
+       .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,