net: Compute protocol sequence numbers and fragment IDs using MD5, CVE-2011-3188
[linux-flexiantxendom0-natty.git] / net / dccp / ipv6.c
index dee0853..3631a6c 100644 (file)
@@ -14,6 +14,7 @@
 
 #include <linux/module.h>
 #include <linux/random.h>
+#include <linux/slab.h>
 #include <linux/xfrm.h>
 
 #include <net/addrconf.h>
 #include <net/transp_v6.h>
 #include <net/ip6_checksum.h>
 #include <net/xfrm.h>
+#include <net/secure_seq.h>
 
 #include "dccp.h"
 #include "ipv6.h"
 #include "feat.h"
 
-/* Socket used for sending RSTs and ACKs */
-static struct socket *dccp_v6_ctl_socket;
+/* The per-net dccp.v6_ctl_sk is used for sending RSTs and ACKs */
 
-static void dccp_v6_ctl_send_reset(struct sk_buff *skb);
-static void dccp_v6_reqsk_send_ack(struct sk_buff *skb,
-                                  struct request_sock *req);
-static void dccp_v6_send_check(struct sock *sk, int len, struct sk_buff *skb);
-
-static int dccp_v6_do_rcv(struct sock *sk, struct sk_buff *skb);
-
-static struct inet_connection_sock_af_ops dccp_ipv6_mapped;
-static struct inet_connection_sock_af_ops dccp_ipv6_af_ops;
-
-static int dccp_v6_get_port(struct sock *sk, unsigned short snum)
-{
-       return inet_csk_get_port(&dccp_hashinfo, sk, snum,
-                                inet6_csk_bind_conflict);
-}
+static const struct inet_connection_sock_af_ops dccp_ipv6_mapped;
+static const struct inet_connection_sock_af_ops dccp_ipv6_af_ops;
 
 static void dccp_v6_hash(struct sock *sk)
 {
        if (sk->sk_state != DCCP_CLOSED) {
                if (inet_csk(sk)->icsk_af_ops == &dccp_ipv6_mapped) {
-                       dccp_hash(sk);
+                       inet_hash(sk);
                        return;
                }
                local_bh_disable();
-               __inet6_hash(&dccp_hashinfo, sk);
+               __inet6_hash(sk, NULL);
                local_bh_enable();
        }
 }
 
-static inline u16 dccp_v6_check(struct dccp_hdr *dh, int len,
-                               struct in6_addr *saddr,
-                               struct in6_addr *daddr,
-                               unsigned long base)
+/* add pseudo-header to DCCP checksum stored in skb->csum */
+static inline __sum16 dccp_v6_csum_finish(struct sk_buff *skb,
+                                     struct in6_addr *saddr,
+                                     struct in6_addr *daddr)
 {
-       return csum_ipv6_magic(saddr, daddr, len, IPPROTO_DCCP, base);
+       return csum_ipv6_magic(saddr, daddr, skb->len, IPPROTO_DCCP, skb->csum);
 }
 
-static __u32 dccp_v6_init_sequence(struct sock *sk, struct sk_buff *skb)
+static inline void dccp_v6_send_check(struct sock *sk, struct sk_buff *skb)
 {
-       const struct dccp_hdr *dh = dccp_hdr(skb);
-
-       if (skb->protocol == htons(ETH_P_IPV6))
-               return secure_tcpv6_sequence_number(skb->nh.ipv6h->daddr.s6_addr32,
-                                                   skb->nh.ipv6h->saddr.s6_addr32,
-                                                   dh->dccph_dport,
-                                                   dh->dccph_sport);
+       struct ipv6_pinfo *np = inet6_sk(sk);
+       struct dccp_hdr *dh = dccp_hdr(skb);
 
-       return secure_dccp_sequence_number(skb->nh.iph->daddr,
-                                          skb->nh.iph->saddr,
-                                          dh->dccph_dport,
-                                          dh->dccph_sport);
+       dccp_csum_outgoing(skb);
+       dh->dccph_checksum = dccp_v6_csum_finish(skb, &np->saddr, &np->daddr);
 }
 
-static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
-                          int addr_len)
+static inline __u64 dccp_v6_init_sequence(struct sk_buff *skb)
 {
-       struct sockaddr_in6 *usin = (struct sockaddr_in6 *)uaddr;
-       struct inet_connection_sock *icsk = inet_csk(sk);
-       struct inet_sock *inet = inet_sk(sk);
-       struct ipv6_pinfo *np = inet6_sk(sk);
-       struct dccp_sock *dp = dccp_sk(sk);
-       struct in6_addr *saddr = NULL, *final_p = NULL, final;
-       struct flowi fl;
-       struct dst_entry *dst;
-       int addr_type;
-       int err;
-
-       dp->dccps_role = DCCP_ROLE_CLIENT;
-
-       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;
-               IP6_ECN_flow_init(fl.fl6_flowlabel);
-               if (fl.fl6_flowlabel & IPV6_FLOWLABEL_MASK) {
-                       struct ip6_flowlabel *flowlabel;
-                       flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel);
-                       if (flowlabel == NULL)
-                               return -EINVAL;
-                       ipv6_addr_copy(&usin->sin6_addr, &flowlabel->dst);
-                       fl6_sock_release(flowlabel);
-               }
-       }
-       /*
-        * connect() to INADDR_ANY means loopback (BSD'ism).
-        */
-       if (ipv6_addr_any(&usin->sin6_addr))
-               usin->sin6_addr.s6_addr[15] = 1;
-
-       addr_type = ipv6_addr_type(&usin->sin6_addr);
-
-       if (addr_type & IPV6_ADDR_MULTICAST)
-               return -ENETUNREACH;
-
-       if (addr_type & IPV6_ADDR_LINKLOCAL) {
-               if (addr_len >= sizeof(struct sockaddr_in6) &&
-                   usin->sin6_scope_id) {
-                       /* If interface is set while binding, indices
-                        * must coincide.
-                        */
-                       if (sk->sk_bound_dev_if &&
-                           sk->sk_bound_dev_if != usin->sin6_scope_id)
-                               return -EINVAL;
-
-                       sk->sk_bound_dev_if = usin->sin6_scope_id;
-               }
-
-               /* Connect to link-local address requires an interface */
-               if (!sk->sk_bound_dev_if)
-                       return -EINVAL;
-       }
-
-       ipv6_addr_copy(&np->daddr, &usin->sin6_addr);
-       np->flow_label = fl.fl6_flowlabel;
-
-       /*
-        * DCCP over IPv4
-        */
-       if (addr_type == IPV6_ADDR_MAPPED) {
-               u32 exthdrlen = icsk->icsk_ext_hdr_len;
-               struct sockaddr_in sin;
-
-               SOCK_DEBUG(sk, "connect: ipv4 mapped\n");
-
-               if (__ipv6_only_sock(sk))
-                       return -ENETUNREACH;
-
-               sin.sin_family = AF_INET;
-               sin.sin_port = usin->sin6_port;
-               sin.sin_addr.s_addr = usin->sin6_addr.s6_addr32[3];
-
-               icsk->icsk_af_ops = &dccp_ipv6_mapped;
-               sk->sk_backlog_rcv = dccp_v4_do_rcv;
-
-               err = dccp_v4_connect(sk, (struct sockaddr *)&sin, sizeof(sin));
-               if (err) {
-                       icsk->icsk_ext_hdr_len = exthdrlen;
-                       icsk->icsk_af_ops = &dccp_ipv6_af_ops;
-                       sk->sk_backlog_rcv = dccp_v6_do_rcv;
-                       goto failure;
-               } else {
-                       ipv6_addr_set(&np->saddr, 0, 0, htonl(0x0000FFFF),
-                                     inet->saddr);
-                       ipv6_addr_set(&np->rcv_saddr, 0, 0, htonl(0x0000FFFF),
-                                     inet->rcv_saddr);
-               }
-
-               return err;
-       }
-
-       if (!ipv6_addr_any(&np->rcv_saddr))
-               saddr = &np->rcv_saddr;
-
-       fl.proto = IPPROTO_DCCP;
-       ipv6_addr_copy(&fl.fl6_dst, &np->daddr);
-       ipv6_addr_copy(&fl.fl6_src, saddr ? saddr : &np->saddr);
-       fl.oif = sk->sk_bound_dev_if;
-       fl.fl_ip_dport = usin->sin6_port;
-       fl.fl_ip_sport = inet->sport;
-       security_sk_classify_flow(sk, &fl);
-
-       if (np->opt != NULL && np->opt->srcrt != NULL) {
-               const struct rt0_hdr *rt0 = (struct rt0_hdr *)np->opt->srcrt;
-
-               ipv6_addr_copy(&final, &fl.fl6_dst);
-               ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
-               final_p = &final;
-       }
-
-       err = ip6_dst_lookup(sk, &dst, &fl);
-       if (err)
-               goto failure;
-
-       if (final_p)
-               ipv6_addr_copy(&fl.fl6_dst, final_p);
-
-       err = xfrm_lookup(&dst, &fl, sk, 0);
-       if (err < 0)
-               goto failure;
-
-       if (saddr == NULL) {
-               saddr = &fl.fl6_src;
-               ipv6_addr_copy(&np->rcv_saddr, saddr);
-       }
-
-       /* set the source address */
-       ipv6_addr_copy(&np->saddr, saddr);
-       inet->rcv_saddr = LOOPBACK4_IPV6;
-
-       __ip6_dst_store(sk, dst, NULL, NULL);
+       return secure_dccpv6_sequence_number(ipv6_hdr(skb)->daddr.s6_addr32,
+                                            ipv6_hdr(skb)->saddr.s6_addr32,
+                                            dccp_hdr(skb)->dccph_dport,
+                                            dccp_hdr(skb)->dccph_sport     );
 
-       icsk->icsk_ext_hdr_len = 0;
-       if (np->opt != NULL)
-               icsk->icsk_ext_hdr_len = (np->opt->opt_flen +
-                                         np->opt->opt_nflen);
-
-       inet->dport = usin->sin6_port;
-
-       dccp_set_state(sk, DCCP_REQUESTING);
-       err = inet6_hash_connect(&dccp_death_row, sk);
-       if (err)
-               goto late_failure;
-       /* FIXME */
-#if 0
-       dp->dccps_gar = secure_dccp_v6_sequence_number(np->saddr.s6_addr32,
-                                                      np->daddr.s6_addr32,
-                                                      inet->sport,
-                                                      inet->dport);
-#endif
-       err = dccp_connect(sk);
-       if (err)
-               goto late_failure;
-
-       return 0;
-
-late_failure:
-       dccp_set_state(sk, DCCP_CLOSED);
-       __sk_dst_reset(sk);
-failure:
-       inet->dport = 0;
-       sk->sk_route_caps = 0;
-       return err;
 }
 
 static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
-                       int type, int code, int offset, __be32 info)
+                       u8 type, u8 code, int offset, __be32 info)
 {
        struct ipv6hdr *hdr = (struct ipv6hdr *)skb->data;
        const struct dccp_hdr *dh = (struct dccp_hdr *)(skb->data + offset);
+       struct dccp_sock *dp;
        struct ipv6_pinfo *np;
        struct sock *sk;
        int err;
        __u64 seq;
+       struct net *net = dev_net(skb->dev);
+
+       if (skb->len < offset + sizeof(*dh) ||
+           skb->len < offset + __dccp_basic_hdr_len(dh)) {
+               ICMP6_INC_STATS_BH(net, __in6_dev_get(skb->dev),
+                                  ICMP6_MIB_INERRORS);
+               return;
+       }
 
-       sk = inet6_lookup(&dccp_hashinfo, &hdr->daddr, dh->dccph_dport,
-                         &hdr->saddr, dh->dccph_sport, inet6_iif(skb));
+       sk = inet6_lookup(net, &dccp_hashinfo,
+                       &hdr->daddr, dh->dccph_dport,
+                       &hdr->saddr, dh->dccph_sport, inet6_iif(skb));
 
        if (sk == NULL) {
-               ICMP6_INC_STATS_BH(__in6_dev_get(skb->dev), ICMP6_MIB_INERRORS);
+               ICMP6_INC_STATS_BH(net, __in6_dev_get(skb->dev),
+                                  ICMP6_MIB_INERRORS);
                return;
        }
 
@@ -291,11 +115,19 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
 
        bh_lock_sock(sk);
        if (sock_owned_by_user(sk))
-               NET_INC_STATS_BH(LINUX_MIB_LOCKDROPPEDICMPS);
+               NET_INC_STATS_BH(net, LINUX_MIB_LOCKDROPPEDICMPS);
 
        if (sk->sk_state == DCCP_CLOSED)
                goto out;
 
+       dp = dccp_sk(sk);
+       seq = dccp_hdr_seq(dh);
+       if ((1 << sk->sk_state) & ~(DCCPF_REQUESTING | DCCPF_LISTEN) &&
+           !between48(seq, dp->dccps_awl, dp->dccps_awh)) {
+               NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
+               goto out;
+       }
+
        np = inet6_sk(sk);
 
        if (type == ICMPV6_PKT_TOOBIG) {
@@ -321,8 +153,8 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                        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;
+                       fl.fl_ip_dport = inet->inet_dport;
+                       fl.fl_ip_sport = inet->inet_sport;
                        security_sk_classify_flow(sk, &fl);
 
                        err = ip6_dst_lookup(sk, &dst, &fl);
@@ -331,7 +163,7 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                                goto out;
                        }
 
-                       err = xfrm_lookup(&dst, &fl, sk, 0);
+                       err = xfrm_lookup(net, &dst, &fl, sk, 0);
                        if (err < 0) {
                                sk->sk_err_soft = -err;
                                goto out;
@@ -348,7 +180,6 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
 
        icmpv6_err_convert(type, code, &err);
 
-       seq = DCCP_SKB_CB(skb)->dccpd_seq;
        /* Might be for an request_sock */
        switch (sk->sk_state) {
                struct request_sock *req, **prev;
@@ -366,10 +197,10 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                 * ICMPs are not backlogged, hence we cannot get an established
                 * socket here.
                 */
-               BUG_TRAP(req->sk == NULL);
+               WARN_ON(req->sk != NULL);
 
                if (seq != dccp_rsk(req)->dreq_iss) {
-                       NET_INC_STATS_BH(LINUX_MIB_OUTOFWINDOWICMPS);
+                       NET_INC_STATS_BH(net, LINUX_MIB_OUTOFWINDOWICMPS);
                        goto out;
                }
 
@@ -406,15 +237,16 @@ out:
 
 
 static int dccp_v6_send_response(struct sock *sk, struct request_sock *req,
-                                struct dst_entry *dst)
+                                struct request_values *rv_unused)
 {
        struct inet6_request_sock *ireq6 = inet6_rsk(req);
        struct ipv6_pinfo *np = inet6_sk(sk);
        struct sk_buff *skb;
        struct ipv6_txoptions *opt = NULL;
-       struct in6_addr *final_p = NULL, final;
+       struct in6_addr *final_p, final;
        struct flowi fl;
        int err = -1;
+       struct dst_entry *dst;
 
        memset(&fl, 0, sizeof(fl));
        fl.proto = IPPROTO_DCCP;
@@ -423,57 +255,34 @@ static int dccp_v6_send_response(struct sock *sk, struct request_sock *req,
        fl.fl6_flowlabel = 0;
        fl.oif = ireq6->iif;
        fl.fl_ip_dport = inet_rsk(req)->rmt_port;
-       fl.fl_ip_sport = inet_sk(sk)->sport;
+       fl.fl_ip_sport = inet_rsk(req)->loc_port;
        security_req_classify_flow(req, &fl);
 
-       if (dst == NULL) {
-               opt = np->opt;
-               if (opt == NULL &&
-                   np->rxopt.bits.osrcrt == 2 &&
-                   ireq6->pktopts) {
-                       struct sk_buff *pktopts = ireq6->pktopts;
-                       struct inet6_skb_parm *rxopt = IP6CB(pktopts);
-
-                       if (rxopt->srcrt)
-                               opt = ipv6_invert_rthdr(sk,
-                                       (struct ipv6_rt_hdr *)(pktopts->nh.raw +
-                                                              rxopt->srcrt));
-               }
-
-               if (opt != NULL && opt->srcrt != NULL) {
-                       const struct rt0_hdr *rt0 = (struct rt0_hdr *)opt->srcrt;
+       opt = np->opt;
 
-                       ipv6_addr_copy(&final, &fl.fl6_dst);
-                       ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
-                       final_p = &final;
-               }
+       final_p = fl6_update_dst(&fl, opt, &final);
 
-               err = ip6_dst_lookup(sk, &dst, &fl);
-               if (err)
-                       goto done;
+       err = ip6_dst_lookup(sk, &dst, &fl);
+       if (err)
+               goto done;
 
-               if (final_p)
-                       ipv6_addr_copy(&fl.fl6_dst, final_p);
+       if (final_p)
+               ipv6_addr_copy(&fl.fl6_dst, final_p);
 
-               err = xfrm_lookup(&dst, &fl, sk, 0);
-               if (err < 0)
-                       goto done;
-       }
+       err = xfrm_lookup(sock_net(sk), &dst, &fl, sk, 0);
+       if (err < 0)
+               goto done;
 
        skb = dccp_make_response(sk, dst, req);
        if (skb != NULL) {
                struct dccp_hdr *dh = dccp_hdr(skb);
 
-               dh->dccph_checksum = dccp_v6_check(dh, skb->len,
-                                                  &ireq6->loc_addr,
-                                                  &ireq6->rmt_addr,
-                                                  csum_partial((char *)dh,
-                                                               skb->len,
-                                                               skb->csum));
+               dh->dccph_checksum = dccp_v6_csum_finish(skb,
+                                                        &ireq6->loc_addr,
+                                                        &ireq6->rmt_addr);
                ipv6_addr_copy(&fl.fl6_dst, &ireq6->rmt_addr);
-               err = ip6_xmit(sk, skb, &fl, opt, 0);
-               if (err == NET_XMIT_CN)
-                       err = 0;
+               err = ip6_xmit(sk, skb, &fl, opt);
+               err = net_xmit_eval(err);
        }
 
 done:
@@ -485,94 +294,49 @@ done:
 
 static void dccp_v6_reqsk_destructor(struct request_sock *req)
 {
+       dccp_feat_list_purge(&dccp_rsk(req)->dreq_featneg);
        if (inet6_rsk(req)->pktopts != NULL)
                kfree_skb(inet6_rsk(req)->pktopts);
 }
 
-static struct request_sock_ops dccp6_request_sock_ops = {
-       .family         = AF_INET6,
-       .obj_size       = sizeof(struct dccp6_request_sock),
-       .rtx_syn_ack    = dccp_v6_send_response,
-       .send_ack       = dccp_v6_reqsk_send_ack,
-       .destructor     = dccp_v6_reqsk_destructor,
-       .send_reset     = dccp_v6_ctl_send_reset,
-};
-
-static struct timewait_sock_ops dccp6_timewait_sock_ops = {
-       .twsk_obj_size  = sizeof(struct dccp6_timewait_sock),
-};
-
-static void dccp_v6_send_check(struct sock *sk, int len, struct sk_buff *skb)
-{
-       struct ipv6_pinfo *np = inet6_sk(sk);
-       struct dccp_hdr *dh = dccp_hdr(skb);
-
-       dh->dccph_checksum = csum_ipv6_magic(&np->saddr, &np->daddr,
-                                            len, IPPROTO_DCCP,
-                                            csum_partial((char *)dh,
-                                                         dh->dccph_doff << 2,
-                                                         skb->csum));
-}
-
-static void dccp_v6_ctl_send_reset(struct sk_buff *rxskb)
+static void dccp_v6_ctl_send_reset(struct sock *sk, struct sk_buff *rxskb)
 {
-       struct dccp_hdr *rxdh = dccp_hdr(rxskb), *dh;
-       const u32 dccp_hdr_reset_len = sizeof(struct dccp_hdr) +
-                                      sizeof(struct dccp_hdr_ext) +
-                                      sizeof(struct dccp_hdr_reset);
+       struct ipv6hdr *rxip6h;
        struct sk_buff *skb;
        struct flowi fl;
-       u64 seqno;
+       struct net *net = dev_net(skb_dst(rxskb)->dev);
+       struct sock *ctl_sk = net->dccp.v6_ctl_sk;
+       struct dst_entry *dst;
 
-       if (rxdh->dccph_type == DCCP_PKT_RESET)
+       if (dccp_hdr(rxskb)->dccph_type == DCCP_PKT_RESET)
                return;
 
        if (!ipv6_unicast_destination(rxskb))
                return;
 
-       skb = alloc_skb(dccp_v6_ctl_socket->sk->sk_prot->max_header,
-                       GFP_ATOMIC);
+       skb = dccp_ctl_make_reset(ctl_sk, rxskb);
        if (skb == NULL)
-               return;
-
-       skb_reserve(skb, dccp_v6_ctl_socket->sk->sk_prot->max_header);
-
-       dh = dccp_zeroed_hdr(skb, dccp_hdr_reset_len);
-
-       /* Swap the send and the receive. */
-       dh->dccph_type  = DCCP_PKT_RESET;
-       dh->dccph_sport = rxdh->dccph_dport;
-       dh->dccph_dport = rxdh->dccph_sport;
-       dh->dccph_doff  = dccp_hdr_reset_len / 4;
-       dh->dccph_x     = 1;
-       dccp_hdr_reset(skb)->dccph_reset_code =
-                               DCCP_SKB_CB(rxskb)->dccpd_reset_code;
-
-       /* See "8.3.1. Abnormal Termination" in RFC 4340 */
-       seqno = 0;
-       if (DCCP_SKB_CB(rxskb)->dccpd_ack_seq != DCCP_PKT_WITHOUT_ACK_SEQ)
-               dccp_set_seqno(&seqno, DCCP_SKB_CB(rxskb)->dccpd_ack_seq + 1);
+               return;
 
-       dccp_hdr_set_seq(dh, seqno);
-       dccp_hdr_set_ack(dccp_hdr_ack_bits(skb),
-                        DCCP_SKB_CB(rxskb)->dccpd_seq);
+       rxip6h = ipv6_hdr(rxskb);
+       dccp_hdr(skb)->dccph_checksum = dccp_v6_csum_finish(skb, &rxip6h->saddr,
+                                                           &rxip6h->daddr);
 
        memset(&fl, 0, sizeof(fl));
-       ipv6_addr_copy(&fl.fl6_dst, &rxskb->nh.ipv6h->saddr);
-       ipv6_addr_copy(&fl.fl6_src, &rxskb->nh.ipv6h->daddr);
-       dh->dccph_checksum = csum_ipv6_magic(&fl.fl6_src, &fl.fl6_dst,
-                                            sizeof(*dh), IPPROTO_DCCP,
-                                            skb->csum);
+       ipv6_addr_copy(&fl.fl6_dst, &rxip6h->saddr);
+       ipv6_addr_copy(&fl.fl6_src, &rxip6h->daddr);
+
        fl.proto = IPPROTO_DCCP;
        fl.oif = inet6_iif(rxskb);
-       fl.fl_ip_dport = dh->dccph_dport;
-       fl.fl_ip_sport = dh->dccph_sport;
+       fl.fl_ip_dport = dccp_hdr(skb)->dccph_dport;
+       fl.fl_ip_sport = dccp_hdr(skb)->dccph_sport;
        security_skb_classify_flow(rxskb, &fl);
 
        /* sk = NULL, but it is safe for now. RST socket required. */
-       if (!ip6_dst_lookup(NULL, &skb->dst, &fl)) {
-               if (xfrm_lookup(&skb->dst, &fl, NULL, 0) >= 0) {
-                       ip6_xmit(dccp_v6_ctl_socket->sk, skb, &fl, NULL, 0);
+       if (!ip6_dst_lookup(ctl_sk, &dst, &fl)) {
+               if (xfrm_lookup(net, &dst, &fl, NULL, 0) >= 0) {
+                       skb_dst_set(skb, dst);
+                       ip6_xmit(ctl_sk, skb, &fl, NULL);
                        DCCP_INC_STATS_BH(DCCP_MIB_OUTSEGS);
                        DCCP_INC_STATS_BH(DCCP_MIB_OUTRSTS);
                        return;
@@ -582,63 +346,19 @@ static void dccp_v6_ctl_send_reset(struct sk_buff *rxskb)
        kfree_skb(skb);
 }
 
-static void dccp_v6_reqsk_send_ack(struct sk_buff *rxskb,
-                                  struct request_sock *req)
-{
-       struct flowi fl;
-       struct dccp_hdr *rxdh = dccp_hdr(rxskb), *dh;
-       const u32 dccp_hdr_ack_len = sizeof(struct dccp_hdr) +
-                                    sizeof(struct dccp_hdr_ext) +
-                                    sizeof(struct dccp_hdr_ack_bits);
-       struct sk_buff *skb;
-
-       skb = alloc_skb(dccp_v6_ctl_socket->sk->sk_prot->max_header,
-                       GFP_ATOMIC);
-       if (skb == NULL)
-               return;
-
-       skb_reserve(skb, dccp_v6_ctl_socket->sk->sk_prot->max_header);
-
-       dh = dccp_zeroed_hdr(skb, dccp_hdr_ack_len);
-
-       /* Build DCCP header and checksum it. */
-       dh->dccph_type  = DCCP_PKT_ACK;
-       dh->dccph_sport = rxdh->dccph_dport;
-       dh->dccph_dport = rxdh->dccph_sport;
-       dh->dccph_doff  = dccp_hdr_ack_len / 4;
-       dh->dccph_x     = 1;
-
-       dccp_hdr_set_seq(dh, DCCP_SKB_CB(rxskb)->dccpd_ack_seq);
-       dccp_hdr_set_ack(dccp_hdr_ack_bits(skb),
-                        DCCP_SKB_CB(rxskb)->dccpd_seq);
-
-       memset(&fl, 0, sizeof(fl));
-       ipv6_addr_copy(&fl.fl6_dst, &rxskb->nh.ipv6h->saddr);
-       ipv6_addr_copy(&fl.fl6_src, &rxskb->nh.ipv6h->daddr);
-
-       /* FIXME: calculate checksum, IPv4 also should... */
-
-       fl.proto = IPPROTO_DCCP;
-       fl.oif = inet6_iif(rxskb);
-       fl.fl_ip_dport = dh->dccph_dport;
-       fl.fl_ip_sport = dh->dccph_sport;
-       security_req_classify_flow(req, &fl);
-
-       if (!ip6_dst_lookup(NULL, &skb->dst, &fl)) {
-               if (xfrm_lookup(&skb->dst, &fl, NULL, 0) >= 0) {
-                       ip6_xmit(dccp_v6_ctl_socket->sk, skb, &fl, NULL, 0);
-                       DCCP_INC_STATS_BH(DCCP_MIB_OUTSEGS);
-                       return;
-               }
-       }
-
-       kfree_skb(skb);
-}
+static struct request_sock_ops dccp6_request_sock_ops = {
+       .family         = AF_INET6,
+       .obj_size       = sizeof(struct dccp6_request_sock),
+       .rtx_syn_ack    = dccp_v6_send_response,
+       .send_ack       = dccp_reqsk_send_ack,
+       .destructor     = dccp_v6_reqsk_destructor,
+       .send_reset     = dccp_v6_ctl_send_reset,
+};
 
 static struct sock *dccp_v6_hnd_req(struct sock *sk,struct sk_buff *skb)
 {
        const struct dccp_hdr *dh = dccp_hdr(skb);
-       const struct ipv6hdr *iph = skb->nh.ipv6h;
+       const struct ipv6hdr *iph = ipv6_hdr(skb);
        struct sock *nsk;
        struct request_sock **prev;
        /* Find possible connection requests. */
@@ -650,7 +370,7 @@ static struct sock *dccp_v6_hnd_req(struct sock *sk,struct sk_buff *skb)
        if (req != NULL)
                return dccp_check_req(sk, skb, req, prev);
 
-       nsk = __inet6_lookup_established(&dccp_hashinfo,
+       nsk = __inet6_lookup_established(sock_net(sk), &dccp_hashinfo,
                                         &iph->saddr, dh->dccph_sport,
                                         &iph->daddr, ntohs(dh->dccph_dport),
                                         inet6_iif(skb));
@@ -668,28 +388,27 @@ static struct sock *dccp_v6_hnd_req(struct sock *sk,struct sk_buff *skb)
 
 static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
 {
-       struct dccp_sock dp;
        struct request_sock *req;
        struct dccp_request_sock *dreq;
        struct inet6_request_sock *ireq6;
        struct ipv6_pinfo *np = inet6_sk(sk);
-       const __be32 service = dccp_hdr_request(skb)->dccph_req_service;
+       const __be32 service = dccp_hdr_request(skb)->dccph_req_service;
        struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb);
-       __u8 reset_code = DCCP_RESET_CODE_TOO_BUSY;
 
        if (skb->protocol == htons(ETH_P_IP))
                return dccp_v4_conn_request(sk, skb);
 
        if (!ipv6_unicast_destination(skb))
-               goto drop;
+               return 0;       /* discard, don't send a reset here */
 
        if (dccp_bad_service_code(sk, service)) {
-               reset_code = DCCP_RESET_CODE_BAD_SERVICE_CODE;
+               dcb->dccpd_reset_code = DCCP_RESET_CODE_BAD_SERVICE_CODE;
                goto drop;
-       }
+       }
        /*
         * There are no SYN attacks on IPv6, yet...
         */
+       dcb->dccpd_reset_code = DCCP_RESET_CODE_TOO_BUSY;
        if (inet_csk_reqsk_queue_is_full(sk))
                goto drop;
 
@@ -700,18 +419,19 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
        if (req == NULL)
                goto drop;
 
-       /* FIXME: process options */
+       if (dccp_reqsk_init(req, dccp_sk(sk), skb))
+               goto drop_and_free;
 
-       dccp_openreq_init(req, &dp, skb);
+       dreq = dccp_rsk(req);
+       if (dccp_parse_options(sk, dreq, skb))
+               goto drop_and_free;
 
        if (security_inet_conn_request(sk, skb, req))
                goto drop_and_free;
 
        ireq6 = inet6_rsk(req);
-       ipv6_addr_copy(&ireq6->rmt_addr, &skb->nh.ipv6h->saddr);
-       ipv6_addr_copy(&ireq6->loc_addr, &skb->nh.ipv6h->daddr);
-       req->rcv_wnd    = dccp_feat_default_sequence_window;
-       ireq6->pktopts  = NULL;
+       ipv6_addr_copy(&ireq6->rmt_addr, &ipv6_hdr(skb)->saddr);
+       ipv6_addr_copy(&ireq6->loc_addr, &ipv6_hdr(skb)->daddr);
 
        if (ipv6_opt_accepted(sk, skb) ||
            np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo ||
@@ -729,14 +449,13 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
        /*
         * Step 3: Process LISTEN state
         *
-        * Set S.ISR, S.GSR, S.SWL, S.SWH from packet or Init Cookie
+        *   Set S.ISR, S.GSR, S.SWL, S.SWH from packet or Init Cookie
         *
-        * In fact we defer setting S.GSR, S.SWL, S.SWH to
-        * dccp_create_openreq_child.
+        *   In fact we defer setting S.GSR, S.SWL, S.SWH to
+        *   dccp_create_openreq_child.
         */
-       dreq = dccp_rsk(req);
        dreq->dreq_isr     = dcb->dccpd_seq;
-       dreq->dreq_iss     = dccp_v6_init_sequence(sk, skb);
+       dreq->dreq_iss     = dccp_v6_init_sequence(skb);
        dreq->dreq_service = service;
 
        if (dccp_v6_send_response(sk, req, NULL))
@@ -749,7 +468,6 @@ drop_and_free:
        reqsk_free(req);
 drop:
        DCCP_INC_STATS_BH(DCCP_MIB_ATTEMPTFAILS);
-       dcb->dccpd_reset_code = reset_code;
        return -1;
 }
 
@@ -782,11 +500,9 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
 
                memcpy(newnp, np, sizeof(struct ipv6_pinfo));
 
-               ipv6_addr_set(&newnp->daddr, 0, 0, htonl(0x0000FFFF),
-                             newinet->daddr);
+               ipv6_addr_set_v4mapped(newinet->inet_daddr, &newnp->daddr);
 
-               ipv6_addr_set(&newnp->saddr, 0, 0, htonl(0x0000FFFF),
-                             newinet->saddr);
+               ipv6_addr_set_v4mapped(newinet->inet_saddr, &newnp->saddr);
 
                ipv6_addr_copy(&newnp->rcv_saddr, &newnp->saddr);
 
@@ -795,7 +511,7 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
                newnp->pktoptions  = NULL;
                newnp->opt         = NULL;
                newnp->mcast_oif   = inet6_iif(skb);
-               newnp->mcast_hops  = skb->nh.ipv6h->hop_limit;
+               newnp->mcast_hops  = ipv6_hdr(skb)->hop_limit;
 
                /*
                 * No need to charge this sock to the relevant IPv6 refcnt debug socks count
@@ -817,33 +533,18 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
        if (sk_acceptq_is_full(sk))
                goto out_overflow;
 
-       if (np->rxopt.bits.osrcrt == 2 && opt == NULL && ireq6->pktopts) {
-               const struct inet6_skb_parm *rxopt = IP6CB(ireq6->pktopts);
-
-               if (rxopt->srcrt)
-                       opt = ipv6_invert_rthdr(sk,
-                               (struct ipv6_rt_hdr *)(ireq6->pktopts->nh.raw +
-                                                      rxopt->srcrt));
-       }
-
        if (dst == NULL) {
-               struct in6_addr *final_p = NULL, final;
+               struct in6_addr *final_p, final;
                struct flowi fl;
 
                memset(&fl, 0, sizeof(fl));
                fl.proto = IPPROTO_DCCP;
                ipv6_addr_copy(&fl.fl6_dst, &ireq6->rmt_addr);
-               if (opt != NULL && opt->srcrt != NULL) {
-                       const 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;
-               }
+               final_p = fl6_update_dst(&fl, opt, &final);
                ipv6_addr_copy(&fl.fl6_src, &ireq6->loc_addr);
                fl.oif = sk->sk_bound_dev_if;
                fl.fl_ip_dport = inet_rsk(req)->rmt_port;
-               fl.fl_ip_sport = inet_sk(sk)->sport;
+               fl.fl_ip_sport = inet_rsk(req)->loc_port;
                security_sk_classify_flow(sk, &fl);
 
                if (ip6_dst_lookup(sk, &dst, &fl))
@@ -852,13 +553,13 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
                if (final_p)
                        ipv6_addr_copy(&fl.fl6_dst, final_p);
 
-               if ((xfrm_lookup(&dst, &fl, sk, 0)) < 0)
+               if ((xfrm_lookup(sock_net(sk), &dst, &fl, sk, 0)) < 0)
                        goto out;
        }
 
        newsk = dccp_create_openreq_child(sk, req, skb);
        if (newsk == NULL)
-               goto out;
+               goto out_nonewsk;
 
        /*
         * No need to charge this sock to the relevant IPv6 refcnt debug socks
@@ -902,7 +603,7 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
        }
        newnp->opt        = NULL;
        newnp->mcast_oif  = inet6_iif(skb);
-       newnp->mcast_hops = skb->nh.ipv6h->hop_limit;
+       newnp->mcast_hops = ipv6_hdr(skb)->hop_limit;
 
        /*
         * Clone native IPv6 options from listening socket (if any)
@@ -923,20 +624,25 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
 
        dccp_sync_mss(newsk, dst_mtu(dst));
 
-       newinet->daddr = newinet->saddr = newinet->rcv_saddr = LOOPBACK4_IPV6;
+       newinet->inet_daddr = newinet->inet_saddr = LOOPBACK4_IPV6;
+       newinet->inet_rcv_saddr = LOOPBACK4_IPV6;
 
-       __inet6_hash(&dccp_hashinfo, newsk);
-       inet_inherit_port(&dccp_hashinfo, sk, newsk);
+       if (__inet_inherit_port(sk, newsk) < 0) {
+               sock_put(newsk);
+               goto out;
+       }
+       __inet6_hash(newsk, NULL);
 
        return newsk;
 
 out_overflow:
-       NET_INC_STATS_BH(LINUX_MIB_LISTENOVERFLOWS);
+       NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
+out_nonewsk:
+       dst_release(dst);
 out:
-       NET_INC_STATS_BH(LINUX_MIB_LISTENDROPS);
+       NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS);
        if (opt != NULL && opt != np->opt)
                sock_kfree_s(sk, opt, opt->tot_len);
-       dst_release(dst);
        return NULL;
 }
 
@@ -983,7 +689,7 @@ static int dccp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
           looks not very well thought. For now we latch
           options, received in the last packet, enqueued
           by tcp. Feel free to propose better solution.
-                                              --ANK (980728)
+                                              --ANK (980728)
         */
        if (np->rxopt.all)
        /*
@@ -1002,6 +708,30 @@ static int dccp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
                return 0;
        }
 
+       /*
+        *  Step 3: Process LISTEN state
+        *     If S.state == LISTEN,
+        *       If P.type == Request or P contains a valid Init Cookie option,
+        *            (* Must scan the packet's options to check for Init
+        *               Cookies.  Only Init Cookies are processed here,
+        *               however; other options are processed in Step 8.  This
+        *               scan need only be performed if the endpoint uses Init
+        *               Cookies *)
+        *            (* Generate a new socket and switch to that socket *)
+        *            Set S := new socket for this port pair
+        *            S.state = RESPOND
+        *            Choose S.ISS (initial seqno) or set from Init Cookies
+        *            Initialize S.GAR := S.ISS
+        *            Set S.ISR, S.GSR, S.SWL, S.SWH from packet or Init Cookies
+        *            Continue with S.state == RESPOND
+        *            (* A Response packet will be generated in Step 11 *)
+        *       Otherwise,
+        *            Generate Reset(No Connection) unless P.type == Reset
+        *            Drop packet and return
+        *
+        * NOTE: the check for the packet types is done in
+        *       dccp_rcv_state_process
+        */
        if (sk->sk_state == DCCP_LISTEN) {
                struct sock *nsk = dccp_v6_hnd_req(sk, skb);
 
@@ -1012,7 +742,7 @@ static int dccp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
                 * otherwise we just shortcircuit this and continue with
                 * the new socket..
                 */
-               if (nsk != sk) {
+               if (nsk != sk) {
                        if (dccp_child_process(sk, nsk, skb))
                                goto reset;
                        if (opt_skb != NULL)
@@ -1030,7 +760,7 @@ static int dccp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
        return 0;
 
 reset:
-       dccp_v6_ctl_send_reset(skb);
+       dccp_v6_ctl_send_reset(sk, skb);
 discard:
        if (opt_skb != NULL)
                __kfree_skb(opt_skb);
@@ -1038,20 +768,27 @@ discard:
        return 0;
 }
 
-static int dccp_v6_rcv(struct sk_buff **pskb)
+static int dccp_v6_rcv(struct sk_buff *skb)
 {
        const struct dccp_hdr *dh;
-       struct sk_buff *skb = *pskb;
        struct sock *sk;
+       int min_cov;
 
-       /* Step 1: Check header basics: */
+       /* Step 1: Check header basics */
 
        if (dccp_invalid_packet(skb))
                goto discard_it;
 
+       /* Step 1: If header checksum is incorrect, drop packet and return. */
+       if (dccp_v6_csum_finish(skb, &ipv6_hdr(skb)->saddr,
+                                    &ipv6_hdr(skb)->daddr)) {
+               DCCP_WARN("dropped packet with invalid checksum\n");
+               goto discard_it;
+       }
+
        dh = dccp_hdr(skb);
 
-       DCCP_SKB_CB(skb)->dccpd_seq  = dccp_hdr_seq(skb);
+       DCCP_SKB_CB(skb)->dccpd_seq  = dccp_hdr_seq(dh);
        DCCP_SKB_CB(skb)->dccpd_type = dh->dccph_type;
 
        if (dccp_packet_without_ack(skb))
@@ -1060,16 +797,12 @@ static int dccp_v6_rcv(struct sk_buff **pskb)
                DCCP_SKB_CB(skb)->dccpd_ack_seq = dccp_hdr_ack_seq(skb);
 
        /* Step 2:
-        *      Look up flow ID in table and get corresponding socket */
-       sk = __inet6_lookup(&dccp_hashinfo, &skb->nh.ipv6h->saddr,
-                           dh->dccph_sport,
-                           &skb->nh.ipv6h->daddr, ntohs(dh->dccph_dport),
-                           inet6_iif(skb));
+        *      Look up flow ID in table and get corresponding socket */
+       sk = __inet6_lookup_skb(&dccp_hashinfo, skb,
+                               dh->dccph_sport, dh->dccph_dport);
        /*
         * Step 2:
-        *      If no socket ...
-        *              Generate Reset(No Connection) unless P.type == Reset
-        *              Drop packet and return
+        *      If no socket ...
         */
        if (sk == NULL) {
                dccp_pr_debug("failed to look up flow ID in table and "
@@ -1079,7 +812,7 @@ static int dccp_v6_rcv(struct sk_buff **pskb)
 
        /*
         * Step 2:
-        *      ... or S.state == TIMEWAIT,
+        *      ... or S.state == TIMEWAIT,
         *              Generate Reset(No Connection) unless P.type == Reset
         *              Drop packet and return
         */
@@ -1089,23 +822,37 @@ static int dccp_v6_rcv(struct sk_buff **pskb)
                goto no_dccp_socket;
        }
 
+       /*
+        * RFC 4340, sec. 9.2.1: Minimum Checksum Coverage
+        *      o if MinCsCov = 0, only packets with CsCov = 0 are accepted
+        *      o if MinCsCov > 0, also accept packets with CsCov >= MinCsCov
+        */
+       min_cov = dccp_sk(sk)->dccps_pcrlen;
+       if (dh->dccph_cscov  &&  (min_cov == 0 || dh->dccph_cscov < min_cov))  {
+               dccp_pr_debug("Packet CsCov %d does not satisfy MinCsCov %d\n",
+                             dh->dccph_cscov, min_cov);
+               /* FIXME: send Data Dropped option (see also dccp_v4_rcv) */
+               goto discard_and_relse;
+       }
+
        if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
                goto discard_and_relse;
 
-       return sk_receive_skb(sk, skb) ? -1 : 0;
+       return sk_receive_skb(sk, skb, 1) ? -1 : 0;
 
 no_dccp_socket:
        if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb))
                goto discard_it;
        /*
         * Step 2:
+        *      If no socket ...
         *              Generate Reset(No Connection) unless P.type == Reset
         *              Drop packet and return
         */
        if (dh->dccph_type != DCCP_PKT_RESET) {
                DCCP_SKB_CB(skb)->dccpd_reset_code =
                                        DCCP_RESET_CODE_NO_CONNECTION;
-               dccp_v6_ctl_send_reset(skb);
+               dccp_v6_ctl_send_reset(sk, skb);
        }
 
 discard_it:
@@ -1117,7 +864,177 @@ discard_and_relse:
        goto discard_it;
 }
 
-static struct inet_connection_sock_af_ops dccp_ipv6_af_ops = {
+static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
+                          int addr_len)
+{
+       struct sockaddr_in6 *usin = (struct sockaddr_in6 *)uaddr;
+       struct inet_connection_sock *icsk = inet_csk(sk);
+       struct inet_sock *inet = inet_sk(sk);
+       struct ipv6_pinfo *np = inet6_sk(sk);
+       struct dccp_sock *dp = dccp_sk(sk);
+       struct in6_addr *saddr = NULL, *final_p, final;
+       struct flowi fl;
+       struct dst_entry *dst;
+       int addr_type;
+       int err;
+
+       dp->dccps_role = DCCP_ROLE_CLIENT;
+
+       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;
+               IP6_ECN_flow_init(fl.fl6_flowlabel);
+               if (fl.fl6_flowlabel & IPV6_FLOWLABEL_MASK) {
+                       struct ip6_flowlabel *flowlabel;
+                       flowlabel = fl6_sock_lookup(sk, fl.fl6_flowlabel);
+                       if (flowlabel == NULL)
+                               return -EINVAL;
+                       ipv6_addr_copy(&usin->sin6_addr, &flowlabel->dst);
+                       fl6_sock_release(flowlabel);
+               }
+       }
+       /*
+        * connect() to INADDR_ANY means loopback (BSD'ism).
+        */
+       if (ipv6_addr_any(&usin->sin6_addr))
+               usin->sin6_addr.s6_addr[15] = 1;
+
+       addr_type = ipv6_addr_type(&usin->sin6_addr);
+
+       if (addr_type & IPV6_ADDR_MULTICAST)
+               return -ENETUNREACH;
+
+       if (addr_type & IPV6_ADDR_LINKLOCAL) {
+               if (addr_len >= sizeof(struct sockaddr_in6) &&
+                   usin->sin6_scope_id) {
+                       /* If interface is set while binding, indices
+                        * must coincide.
+                        */
+                       if (sk->sk_bound_dev_if &&
+                           sk->sk_bound_dev_if != usin->sin6_scope_id)
+                               return -EINVAL;
+
+                       sk->sk_bound_dev_if = usin->sin6_scope_id;
+               }
+
+               /* Connect to link-local address requires an interface */
+               if (!sk->sk_bound_dev_if)
+                       return -EINVAL;
+       }
+
+       ipv6_addr_copy(&np->daddr, &usin->sin6_addr);
+       np->flow_label = fl.fl6_flowlabel;
+
+       /*
+        * DCCP over IPv4
+        */
+       if (addr_type == IPV6_ADDR_MAPPED) {
+               u32 exthdrlen = icsk->icsk_ext_hdr_len;
+               struct sockaddr_in sin;
+
+               SOCK_DEBUG(sk, "connect: ipv4 mapped\n");
+
+               if (__ipv6_only_sock(sk))
+                       return -ENETUNREACH;
+
+               sin.sin_family = AF_INET;
+               sin.sin_port = usin->sin6_port;
+               sin.sin_addr.s_addr = usin->sin6_addr.s6_addr32[3];
+
+               icsk->icsk_af_ops = &dccp_ipv6_mapped;
+               sk->sk_backlog_rcv = dccp_v4_do_rcv;
+
+               err = dccp_v4_connect(sk, (struct sockaddr *)&sin, sizeof(sin));
+               if (err) {
+                       icsk->icsk_ext_hdr_len = exthdrlen;
+                       icsk->icsk_af_ops = &dccp_ipv6_af_ops;
+                       sk->sk_backlog_rcv = dccp_v6_do_rcv;
+                       goto failure;
+               }
+               ipv6_addr_set_v4mapped(inet->inet_saddr, &np->saddr);
+               ipv6_addr_set_v4mapped(inet->inet_rcv_saddr, &np->rcv_saddr);
+
+               return err;
+       }
+
+       if (!ipv6_addr_any(&np->rcv_saddr))
+               saddr = &np->rcv_saddr;
+
+       fl.proto = IPPROTO_DCCP;
+       ipv6_addr_copy(&fl.fl6_dst, &np->daddr);
+       ipv6_addr_copy(&fl.fl6_src, saddr ? saddr : &np->saddr);
+       fl.oif = sk->sk_bound_dev_if;
+       fl.fl_ip_dport = usin->sin6_port;
+       fl.fl_ip_sport = inet->inet_sport;
+       security_sk_classify_flow(sk, &fl);
+
+       final_p = fl6_update_dst(&fl, np->opt, &final);
+
+       err = ip6_dst_lookup(sk, &dst, &fl);
+       if (err)
+               goto failure;
+
+       if (final_p)
+               ipv6_addr_copy(&fl.fl6_dst, final_p);
+
+       err = __xfrm_lookup(sock_net(sk), &dst, &fl, sk, XFRM_LOOKUP_WAIT);
+       if (err < 0) {
+               if (err == -EREMOTE)
+                       err = ip6_dst_blackhole(sk, &dst, &fl);
+               if (err < 0)
+                       goto failure;
+       }
+
+       if (saddr == NULL) {
+               saddr = &fl.fl6_src;
+               ipv6_addr_copy(&np->rcv_saddr, saddr);
+       }
+
+       /* set the source address */
+       ipv6_addr_copy(&np->saddr, saddr);
+       inet->inet_rcv_saddr = LOOPBACK4_IPV6;
+
+       __ip6_dst_store(sk, dst, NULL, NULL);
+
+       icsk->icsk_ext_hdr_len = 0;
+       if (np->opt != NULL)
+               icsk->icsk_ext_hdr_len = (np->opt->opt_flen +
+                                         np->opt->opt_nflen);
+
+       inet->inet_dport = usin->sin6_port;
+
+       dccp_set_state(sk, DCCP_REQUESTING);
+       err = inet6_hash_connect(&dccp_death_row, sk);
+       if (err)
+               goto late_failure;
+
+       dp->dccps_iss = secure_dccpv6_sequence_number(np->saddr.s6_addr32,
+                                                     np->daddr.s6_addr32,
+                                                     inet->inet_sport,
+                                                     inet->inet_dport);
+       err = dccp_connect(sk);
+       if (err)
+               goto late_failure;
+
+       return 0;
+
+late_failure:
+       dccp_set_state(sk, DCCP_CLOSED);
+       __sk_dst_reset(sk);
+failure:
+       inet->inet_dport = 0;
+       sk->sk_route_caps = 0;
+       return err;
+}
+
+static const struct inet_connection_sock_af_ops dccp_ipv6_af_ops = {
        .queue_xmit        = inet6_csk_xmit,
        .send_check        = dccp_v6_send_check,
        .rebuild_header    = inet6_sk_rebuild_header,
@@ -1128,6 +1045,7 @@ static struct inet_connection_sock_af_ops dccp_ipv6_af_ops = {
        .getsockopt        = ipv6_getsockopt,
        .addr2sockaddr     = inet6_csk_addr2sockaddr,
        .sockaddr_len      = sizeof(struct sockaddr_in6),
+       .bind_conflict     = inet6_csk_bind_conflict,
 #ifdef CONFIG_COMPAT
        .compat_setsockopt = compat_ipv6_setsockopt,
        .compat_getsockopt = compat_ipv6_getsockopt,
@@ -1137,7 +1055,7 @@ static struct inet_connection_sock_af_ops dccp_ipv6_af_ops = {
 /*
  *     DCCP over IPv4 via INET6 API
  */
-static struct inet_connection_sock_af_ops dccp_ipv6_mapped = {
+static const struct inet_connection_sock_af_ops dccp_ipv6_mapped = {
        .queue_xmit        = ip_queue_xmit,
        .send_check        = dccp_v4_send_check,
        .rebuild_header    = inet_sk_rebuild_header,
@@ -1171,12 +1089,16 @@ static int dccp_v6_init_sock(struct sock *sk)
        return err;
 }
 
-static int dccp_v6_destroy_sock(struct sock *sk)
+static void dccp_v6_destroy_sock(struct sock *sk)
 {
        dccp_destroy_sock(sk);
-       return inet6_destroy_sock(sk);
+       inet6_destroy_sock(sk);
 }
 
+static struct timewait_sock_ops dccp6_timewait_sock_ops = {
+       .twsk_obj_size  = sizeof(struct dccp6_timewait_sock),
+};
+
 static struct proto dccp_v6_prot = {
        .name              = "DCCPv6",
        .owner             = THIS_MODULE,
@@ -1191,29 +1113,31 @@ static struct proto dccp_v6_prot = {
        .recvmsg           = dccp_recvmsg,
        .backlog_rcv       = dccp_v6_do_rcv,
        .hash              = dccp_v6_hash,
-       .unhash            = dccp_unhash,
+       .unhash            = inet_unhash,
        .accept            = inet_csk_accept,
-       .get_port          = dccp_v6_get_port,
+       .get_port          = inet_csk_get_port,
        .shutdown          = dccp_shutdown,
        .destroy           = dccp_v6_destroy_sock,
        .orphan_count      = &dccp_orphan_count,
        .max_header        = MAX_DCCP_HEADER,
        .obj_size          = sizeof(struct dccp6_sock),
+       .slab_flags        = SLAB_DESTROY_BY_RCU,
        .rsk_prot          = &dccp6_request_sock_ops,
        .twsk_prot         = &dccp6_timewait_sock_ops,
+       .h.hashinfo        = &dccp_hashinfo,
 #ifdef CONFIG_COMPAT
        .compat_setsockopt = compat_dccp_setsockopt,
        .compat_getsockopt = compat_dccp_getsockopt,
 #endif
 };
 
-static struct inet6_protocol dccp_v6_protocol = {
+static const struct inet6_protocol dccp_v6_protocol = {
        .handler        = dccp_v6_rcv,
        .err_handler    = dccp_v6_err,
        .flags          = INET6_PROTO_NOPOLICY | INET6_PROTO_FINAL,
 };
 
-static struct proto_ops inet6_dccp_ops = {
+static const struct proto_ops inet6_dccp_ops = {
        .family            = PF_INET6,
        .owner             = THIS_MODULE,
        .release           = inet6_release,
@@ -1243,10 +1167,28 @@ static struct inet_protosw dccp_v6_protosw = {
        .protocol       = IPPROTO_DCCP,
        .prot           = &dccp_v6_prot,
        .ops            = &inet6_dccp_ops,
-       .capability     = -1,
        .flags          = INET_PROTOSW_ICSK,
 };
 
+static int __net_init dccp_v6_init_net(struct net *net)
+{
+       if (dccp_hashinfo.bhash == NULL)
+               return -ESOCKTNOSUPPORT;
+
+       return inet_ctl_sock_create(&net->dccp.v6_ctl_sk, PF_INET6,
+                                   SOCK_DCCP, IPPROTO_DCCP, net);
+}
+
+static void __net_exit dccp_v6_exit_net(struct net *net)
+{
+       inet_ctl_sock_destroy(net->dccp.v6_ctl_sk);
+}
+
+static struct pernet_operations dccp_v6_ops = {
+       .init   = dccp_v6_init_net,
+       .exit   = dccp_v6_exit_net,
+};
+
 static int __init dccp_v6_init(void)
 {
        int err = proto_register(&dccp_v6_prot, 1);
@@ -1260,13 +1202,13 @@ static int __init dccp_v6_init(void)
 
        inet6_register_protosw(&dccp_v6_protosw);
 
-       err = inet_csk_ctl_sock_create(&dccp_v6_ctl_socket, PF_INET6,
-                                      SOCK_DCCP, IPPROTO_DCCP);
+       err = register_pernet_subsys(&dccp_v6_ops);
        if (err != 0)
-               goto out_unregister_protosw;
+               goto out_destroy_ctl_sock;
 out:
        return err;
-out_unregister_protosw:
+
+out_destroy_ctl_sock:
        inet6_del_protocol(&dccp_v6_protocol, IPPROTO_DCCP);
        inet6_unregister_protosw(&dccp_v6_protosw);
 out_unregister_proto:
@@ -1276,6 +1218,7 @@ out_unregister_proto:
 
 static void __exit dccp_v6_exit(void)
 {
+       unregister_pernet_subsys(&dccp_v6_ops);
        inet6_del_protocol(&dccp_v6_protocol, IPPROTO_DCCP);
        inet6_unregister_protosw(&dccp_v6_protosw);
        proto_unregister(&dccp_v6_prot);
@@ -1289,8 +1232,8 @@ module_exit(dccp_v6_exit);
  * values directly, Also cover the case where the protocol is not specified,
  * i.e. net-pf-PF_INET6-proto-0-type-SOCK_DCCP
  */
-MODULE_ALIAS("net-pf-" __stringify(PF_INET6) "-proto-33-type-6");
-MODULE_ALIAS("net-pf-" __stringify(PF_INET6) "-proto-0-type-6");
+MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_INET6, 33, 6);
+MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_INET6, 0, 6);
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@mandriva.com>");
 MODULE_DESCRIPTION("DCCPv6 - Datagram Congestion Controlled Protocol");