]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - net/ipv6/tcp_ipv6.c
[NET]: {get|set}sockopt compatibility layer
[net-next-2.6.git] / net / ipv6 / tcp_ipv6.c
index e5c8a669e84ecfc79665381d8c16a316e04df15c..2f8975e0150a6c675de7e422a14ef5d13976b3f5 100644 (file)
 #include <net/addrconf.h>
 #include <net/snmp.h>
 #include <net/dsfield.h>
+#include <net/timewait_sock.h>
 
 #include <asm/uaccess.h>
 
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 
+/* Socket used for sending RSTs and ACKs */
+static struct socket *tcp6_socket;
+
 static void    tcp_v6_send_reset(struct sk_buff *skb);
 static void    tcp_v6_reqsk_send_ack(struct sk_buff *skb, struct request_sock *req);
 static void    tcp_v6_send_check(struct sock *sk, int len, 
@@ -118,195 +122,12 @@ static __u32 tcp_v6_init_sequence(struct sock *sk, struct sk_buff *skb)
        }
 }
 
-static int __tcp_v6_check_established(struct sock *sk, const __u16 lport,
-                                     struct inet_timewait_sock **twp)
-{
-       struct inet_sock *inet = inet_sk(sk);
-       const struct ipv6_pinfo *np = inet6_sk(sk);
-       const struct in6_addr *daddr = &np->rcv_saddr;
-       const struct in6_addr *saddr = &np->daddr;
-       const int dif = sk->sk_bound_dev_if;
-       const u32 ports = INET_COMBINED_PORTS(inet->dport, lport);
-       unsigned int hash = inet6_ehashfn(daddr, inet->num, saddr, inet->dport);
-       struct inet_ehash_bucket *head = inet_ehash_bucket(&tcp_hashinfo, hash);
-       struct sock *sk2;
-       const struct hlist_node *node;
-       struct inet_timewait_sock *tw;
-
-       prefetch(head->chain.first);
-       write_lock(&head->lock);
-
-       /* Check TIME-WAIT sockets first. */
-       sk_for_each(sk2, node, &(head + tcp_hashinfo.ehash_size)->chain) {
-               const struct inet6_timewait_sock *tw6 = inet6_twsk(sk2);
-
-               tw = inet_twsk(sk2);
-
-               if(*((__u32 *)&(tw->tw_dport))  == ports        &&
-                  sk2->sk_family               == PF_INET6     &&
-                  ipv6_addr_equal(&tw6->tw_v6_daddr, saddr)    &&
-                  ipv6_addr_equal(&tw6->tw_v6_rcv_saddr, daddr)        &&
-                  sk2->sk_bound_dev_if == sk->sk_bound_dev_if) {
-                       const struct tcp_timewait_sock *tcptw = tcp_twsk(sk2);
-                       struct tcp_sock *tp = tcp_sk(sk);
-
-                       if (tcptw->tw_ts_recent_stamp &&
-                           (!twp ||
-                            (sysctl_tcp_tw_reuse &&
-                             xtime.tv_sec - tcptw->tw_ts_recent_stamp > 1))) {
-                               /* See comment in tcp_ipv4.c */
-                               tp->write_seq = tcptw->tw_snd_nxt + 65535 + 2;
-                               if (!tp->write_seq)
-                                       tp->write_seq = 1;
-                               tp->rx_opt.ts_recent       = tcptw->tw_ts_recent;
-                               tp->rx_opt.ts_recent_stamp = tcptw->tw_ts_recent_stamp;
-                               sock_hold(sk2);
-                               goto unique;
-                       } else
-                               goto not_unique;
-               }
-       }
-       tw = NULL;
-
-       /* And established part... */
-       sk_for_each(sk2, node, &head->chain) {
-               if (INET6_MATCH(sk2, hash, saddr, daddr, ports, dif))
-                       goto not_unique;
-       }
-
-unique:
-       BUG_TRAP(sk_unhashed(sk));
-       __sk_add_node(sk, &head->chain);
-       sk->sk_hash = hash;
-       sock_prot_inc_use(sk->sk_prot);
-       write_unlock(&head->lock);
-
-       if (twp) {
-               *twp = tw;
-               NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
-       } else if (tw) {
-               /* Silly. Should hash-dance instead... */
-               inet_twsk_deschedule(tw, &tcp_death_row);
-               NET_INC_STATS_BH(LINUX_MIB_TIMEWAITRECYCLED);
-
-               inet_twsk_put(tw);
-       }
-       return 0;
-
-not_unique:
-       write_unlock(&head->lock);
-       return -EADDRNOTAVAIL;
-}
-
-static inline u32 tcpv6_port_offset(const struct sock *sk)
-{
-       const struct inet_sock *inet = inet_sk(sk);
-       const struct ipv6_pinfo *np = inet6_sk(sk);
-
-       return secure_tcpv6_port_ephemeral(np->rcv_saddr.s6_addr32,
-                                          np->daddr.s6_addr32,
-                                          inet->dport);
-}
-
-static int tcp_v6_hash_connect(struct sock *sk)
-{
-       unsigned short snum = inet_sk(sk)->num;
-       struct inet_bind_hashbucket *head;
-       struct inet_bind_bucket *tb;
-       int ret;
-
-       if (!snum) {
-               int low = sysctl_local_port_range[0];
-               int high = sysctl_local_port_range[1];
-               int range = high - low;
-               int i;
-               int port;
-               static u32 hint;
-               u32 offset = hint + tcpv6_port_offset(sk);
-               struct hlist_node *node;
-               struct inet_timewait_sock *tw = NULL;
-
-               local_bh_disable();
-               for (i = 1; i <= range; i++) {
-                       port = low + (i + offset) % range;
-                       head = &tcp_hashinfo.bhash[inet_bhashfn(port, tcp_hashinfo.bhash_size)];
-                       spin_lock(&head->lock);
-
-                       /* Does not bother with rcv_saddr checks,
-                        * because the established check is already
-                        * unique enough.
-                        */
-                       inet_bind_bucket_for_each(tb, node, &head->chain) {
-                               if (tb->port == port) {
-                                       BUG_TRAP(!hlist_empty(&tb->owners));
-                                       if (tb->fastreuse >= 0)
-                                               goto next_port;
-                                       if (!__tcp_v6_check_established(sk,
-                                                                       port,
-                                                                       &tw))
-                                               goto ok;
-                                       goto next_port;
-                               }
-                       }
-
-                       tb = inet_bind_bucket_create(tcp_hashinfo.bind_bucket_cachep, head, port);
-                       if (!tb) {
-                               spin_unlock(&head->lock);
-                               break;
-                       }
-                       tb->fastreuse = -1;
-                       goto ok;
-
-               next_port:
-                       spin_unlock(&head->lock);
-               }
-               local_bh_enable();
-
-               return -EADDRNOTAVAIL;
-
-ok:
-               hint += i;
-
-               /* Head lock still held and bh's disabled */
-               inet_bind_hash(sk, tb, port);
-               if (sk_unhashed(sk)) {
-                       inet_sk(sk)->sport = htons(port);
-                       __inet6_hash(&tcp_hashinfo, sk);
-               }
-               spin_unlock(&head->lock);
-
-               if (tw) {
-                       inet_twsk_deschedule(tw, &tcp_death_row);
-                       inet_twsk_put(tw);
-               }
-
-               ret = 0;
-               goto out;
-       }
-
-       head = &tcp_hashinfo.bhash[inet_bhashfn(snum, tcp_hashinfo.bhash_size)];
-       tb   = inet_csk(sk)->icsk_bind_hash;
-       spin_lock_bh(&head->lock);
-
-       if (sk_head(&tb->owners) == sk && !sk->sk_bind_node.next) {
-               __inet6_hash(&tcp_hashinfo, sk);
-               spin_unlock_bh(&head->lock);
-               return 0;
-       } else {
-               spin_unlock(&head->lock);
-               /* No definite answer... Walk to established hash table */
-               ret = __tcp_v6_check_established(sk, snum, NULL);
-out:
-               local_bh_enable();
-               return ret;
-       }
-}
-
 static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, 
                          int addr_len)
 {
        struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr;
-       struct inet_sock *inet = inet_sk(sk);
+       struct inet_sock *inet = inet_sk(sk);
+       struct inet_connection_sock *icsk = inet_csk(sk);
        struct ipv6_pinfo *np = inet6_sk(sk);
        struct tcp_sock *tp = tcp_sk(sk);
        struct in6_addr *saddr = NULL, *final_p = NULL, final;
@@ -381,7 +202,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
         */
 
        if (addr_type == IPV6_ADDR_MAPPED) {
-               u32 exthdrlen = tp->ext_header_len;
+               u32 exthdrlen = icsk->icsk_ext_hdr_len;
                struct sockaddr_in sin;
 
                SOCK_DEBUG(sk, "connect: ipv4 mapped\n");
@@ -393,14 +214,14 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
                sin.sin_port = usin->sin6_port;
                sin.sin_addr.s_addr = usin->sin6_addr.s6_addr32[3];
 
-               inet_csk(sk)->icsk_af_ops = &ipv6_mapped;
+               icsk->icsk_af_ops = &ipv6_mapped;
                sk->sk_backlog_rcv = tcp_v4_do_rcv;
 
                err = tcp_v4_connect(sk, (struct sockaddr *)&sin, sizeof(sin));
 
                if (err) {
-                       tp->ext_header_len = exthdrlen;
-                       inet_csk(sk)->icsk_af_ops = &ipv6_specific;
+                       icsk->icsk_ext_hdr_len = exthdrlen;
+                       icsk->icsk_af_ops = &ipv6_specific;
                        sk->sk_backlog_rcv = tcp_v6_do_rcv;
                        goto failure;
                } else {
@@ -453,16 +274,17 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
        sk->sk_route_caps = dst->dev->features &
                ~(NETIF_F_IP_CSUM | NETIF_F_TSO);
 
-       tp->ext_header_len = 0;
+       icsk->icsk_ext_hdr_len = 0;
        if (np->opt)
-               tp->ext_header_len = np->opt->opt_flen + np->opt->opt_nflen;
+               icsk->icsk_ext_hdr_len = (np->opt->opt_flen +
+                                         np->opt->opt_nflen);
 
        tp->rx_opt.mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr);
 
        inet->dport = usin->sin6_port;
 
        tcp_set_state(sk, TCP_SYN_SENT);
-       err = tcp_v6_hash_connect(sk);
+       err = inet6_hash_connect(&tcp_death_row, sk);
        if (err)
                goto late_failure;
 
@@ -568,7 +390,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
                } else
                        dst_hold(dst);
 
-               if (tp->pmtu_cookie > dst_mtu(dst)) {
+               if (inet_csk(sk)->icsk_pmtu_cookie > dst_mtu(dst)) {
                        tcp_sync_mss(sk, dst_mtu(dst));
                        tcp_simple_retransmit(sk);
                } /* else let the usual retransmit timer handle it */
@@ -693,6 +515,7 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req,
 done:
         if (opt && opt != np->opt)
                sock_kfree_s(sk, opt, opt->tot_len);
+       dst_release(dst);
        return err;
 }
 
@@ -711,6 +534,11 @@ static struct request_sock_ops tcp6_request_sock_ops = {
        .send_reset     =       tcp_v6_send_reset
 };
 
+static struct timewait_sock_ops tcp6_timewait_sock_ops = {
+       .twsk_obj_size  = sizeof(struct tcp6_timewait_sock),
+       .twsk_unique    = tcp_twsk_unique,
+};
+
 static void tcp_v6_send_check(struct sock *sk, int len, struct sk_buff *skb)
 {
        struct ipv6_pinfo *np = inet6_sk(sk);
@@ -787,7 +615,7 @@ static void tcp_v6_send_reset(struct sk_buff *skb)
        if (!ip6_dst_lookup(NULL, &buff->dst, &fl)) {
 
                if (xfrm_lookup(&buff->dst, &fl, NULL, 0) >= 0) {
-                       ip6_xmit(NULL, buff, &fl, NULL, 0);
+                       ip6_xmit(tcp6_socket->sk, buff, &fl, NULL, 0);
                        TCP_INC_STATS_BH(TCP_MIB_OUTSEGS);
                        TCP_INC_STATS_BH(TCP_MIB_OUTRSTS);
                        return;
@@ -851,7 +679,7 @@ static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack, u32 win, u32
 
        if (!ip6_dst_lookup(NULL, &buff->dst, &fl)) {
                if (xfrm_lookup(&buff->dst, &fl, NULL, 0) >= 0) {
-                       ip6_xmit(NULL, buff, &fl, NULL, 0);
+                       ip6_xmit(tcp6_socket->sk, buff, &fl, NULL, 0);
                        TCP_INC_STATS_BH(TCP_MIB_OUTSEGS);
                        return;
                }
@@ -1047,7 +875,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
                   worked with IPv6 icsk.icsk_af_ops.
                   Sync it now.
                 */
-               tcp_sync_mss(newsk, newtp->pmtu_cookie);
+               tcp_sync_mss(newsk, inet_csk(newsk)->icsk_pmtu_cookie);
 
                return newsk;
        }
@@ -1154,11 +982,12 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
                        sock_kfree_s(sk, opt, opt->tot_len);
        }
 
-       newtp->ext_header_len = 0;
+       inet_csk(newsk)->icsk_ext_hdr_len = 0;
        if (newnp->opt)
-               newtp->ext_header_len = newnp->opt->opt_nflen +
-                                       newnp->opt->opt_flen;
+               inet_csk(newsk)->icsk_ext_hdr_len = (newnp->opt->opt_nflen +
+                                                    newnp->opt->opt_flen);
 
+       tcp_mtup_init(newsk);
        tcp_sync_mss(newsk, dst_mtu(dst));
        newtp->advmss = dst_metric(dst, RTAX_ADVMSS);
        tcp_initialize_rcv_mss(newsk);
@@ -1329,7 +1158,7 @@ ipv6_pktoptions:
        return 0;
 }
 
-static int tcp_v6_rcv(struct sk_buff **pskb, unsigned int *nhoffp)
+static int tcp_v6_rcv(struct sk_buff **pskb)
 {
        struct sk_buff *skb = *pskb;
        struct tcphdr *th;      
@@ -1479,6 +1308,10 @@ static struct inet_connection_sock_af_ops ipv6_specific = {
 
        .setsockopt     =       ipv6_setsockopt,
        .getsockopt     =       ipv6_getsockopt,
+#ifdef CONFIG_COMPAT
+       .compat_setsockopt      = compat_ipv6_setsockopt,
+       .compat_getsockopt      = compat_ipv6_getsockopt,
+#endif
        .addr2sockaddr  =       inet6_csk_addr2sockaddr,
        .sockaddr_len   =       sizeof(struct sockaddr_in6)
 };
@@ -1498,6 +1331,10 @@ static struct inet_connection_sock_af_ops ipv6_mapped = {
 
        .setsockopt     =       ipv6_setsockopt,
        .getsockopt     =       ipv6_getsockopt,
+#ifdef CONFIG_COMPAT
+       .compat_setsockopt      = compat_ipv6_setsockopt,
+       .compat_getsockopt      = compat_ipv6_getsockopt,
+#endif
        .addr2sockaddr  =       inet6_csk_addr2sockaddr,
        .sockaddr_len   =       sizeof(struct sockaddr_in6)
 };
@@ -1539,6 +1376,7 @@ static int tcp_v6_init_sock(struct sock *sk)
 
        icsk->icsk_af_ops = &ipv6_specific;
        icsk->icsk_ca_ops = &tcp_init_congestion_ops;
+       icsk->icsk_sync_mss = tcp_sync_mss;
        sk->sk_write_space = sk_stream_write_space;
        sock_set_flag(sk, SOCK_USE_WRITE_QUEUE);
 
@@ -1736,6 +1574,10 @@ struct proto tcpv6_prot = {
        .shutdown               = tcp_shutdown,
        .setsockopt             = tcp_setsockopt,
        .getsockopt             = tcp_getsockopt,
+#ifdef CONFIG_COMPAT
+       .compat_setsockopt      = compat_tcp_setsockopt,
+       .compat_getsockopt      = compat_tcp_getsockopt,
+#endif
        .sendmsg                = tcp_sendmsg,
        .recvmsg                = tcp_recvmsg,
        .backlog_rcv            = tcp_v6_do_rcv,
@@ -1752,7 +1594,7 @@ struct proto tcpv6_prot = {
        .sysctl_rmem            = sysctl_tcp_rmem,
        .max_header             = MAX_TCP_HEADER,
        .obj_size               = sizeof(struct tcp6_sock),
-       .twsk_obj_size          = sizeof(struct tcp6_timewait_sock),
+       .twsk_prot              = &tcp6_timewait_sock_ops,
        .rsk_prot               = &tcp6_request_sock_ops,
 };
 
@@ -1769,7 +1611,8 @@ static struct inet_protosw tcpv6_protosw = {
        .ops            =       &inet6_stream_ops,
        .capability     =       -1,
        .no_check       =       0,
-       .flags          =       INET_PROTOSW_PERMANENT,
+       .flags          =       INET_PROTOSW_PERMANENT |
+                               INET_PROTOSW_ICSK,
 };
 
 void __init tcpv6_init(void)
@@ -1778,4 +1621,8 @@ void __init tcpv6_init(void)
        if (inet6_add_protocol(&tcpv6_protocol, IPPROTO_TCP) < 0)
                printk(KERN_ERR "tcpv6_init: Could not register protocol\n");
        inet6_register_protosw(&tcpv6_protosw);
+
+       if (inet_csk_ctl_sock_create(&tcp6_socket, PF_INET6, SOCK_RAW,
+                                    IPPROTO_TCP) < 0)
+               panic("Failed to create the TCPv6 control socket.\n");
 }