]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - net/ipv6/tcp_ipv6.c
ipv6: Refactor update of IPv6 flowi destination address for srcrt (RH) option
[mirror_ubuntu-artful-kernel.git] / net / ipv6 / tcp_ipv6.c
index 075f540ec1975cdce6148e75661219cfe5decb6e..e487080d02dbe96ac2355bb1a8a42a3eb1b423f4 100644 (file)
@@ -75,6 +75,9 @@ static void   tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
                                      struct request_sock *req);
 
 static int     tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb);
+static void    __tcp_v6_send_check(struct sk_buff *skb,
+                                   struct in6_addr *saddr,
+                                   struct in6_addr *daddr);
 
 static const struct inet_connection_sock_af_ops ipv6_mapped;
 static const struct inet_connection_sock_af_ops ipv6_specific;
@@ -126,7 +129,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
        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;
+       struct in6_addr *saddr = NULL, *final_p, final;
        struct flowi fl;
        struct dst_entry *dst;
        int addr_type;
@@ -247,12 +250,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
        fl.fl_ip_dport = usin->sin6_port;
        fl.fl_ip_sport = inet->inet_sport;
 
-       if (np->opt && np->opt->srcrt) {
-               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;
-       }
+       final_p = fl6_update_dst(&fl, np->opt, &final);
 
        security_sk_classify_flow(sk, &fl);
 
@@ -350,6 +348,11 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
        if (sk->sk_state == TCP_CLOSE)
                goto out;
 
+       if (ipv6_hdr(skb)->hop_limit < inet6_sk(sk)->min_hopcount) {
+               NET_INC_STATS_BH(net, LINUX_MIB_TCPMINTTLDROP);
+               goto out;
+       }
+
        tp = tcp_sk(sk);
        seq = ntohl(th->seq);
        if (sk->sk_state != TCP_LISTEN &&
@@ -469,7 +472,7 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *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;
        struct dst_entry *dst;
        int err = -1;
@@ -486,12 +489,7 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req,
        security_req_classify_flow(req, &fl);
 
        opt = np->opt;
-       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;
-       }
+       final_p = fl6_update_dst(&fl, opt, &final);
 
        err = ip6_dst_lookup(sk, &dst, &fl);
        if (err)
@@ -503,14 +501,10 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req,
 
        skb = tcp_make_synack(sk, dst, req, rvp);
        if (skb) {
-               struct tcphdr *th = tcp_hdr(skb);
-
-               th->check = tcp_v6_check(skb->len,
-                                        &treq->loc_addr, &treq->rmt_addr,
-                                        csum_partial(th, skb->len, skb->csum));
+               __tcp_v6_send_check(skb, &treq->loc_addr, &treq->rmt_addr);
 
                ipv6_addr_copy(&fl.fl6_dst, &treq->rmt_addr);
-               err = ip6_xmit(sk, skb, &fl, opt, 0);
+               err = ip6_xmit(sk, skb, &fl, opt);
                err = net_xmit_eval(err);
        }
 
@@ -600,7 +594,7 @@ static int tcp_v6_md5_do_add(struct sock *sk, struct in6_addr *peer,
                                kfree(newkey);
                                return -ENOMEM;
                        }
-                       sk->sk_route_caps &= ~NETIF_F_GSO_MASK;
+                       sk_nocaps_add(sk, NETIF_F_GSO_MASK);
                }
                if (tcp_alloc_md5sig_pool(sk) == NULL) {
                        kfree(newkey);
@@ -737,7 +731,7 @@ static int tcp_v6_parse_md5_keys (struct sock *sk, char __user *optval,
                        return -ENOMEM;
 
                tp->md5sig_info = p;
-               sk->sk_route_caps &= ~NETIF_F_GSO_MASK;
+               sk_nocaps_add(sk, NETIF_F_GSO_MASK);
        }
 
        newkey = kmemdup(cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL);
@@ -918,22 +912,29 @@ static struct timewait_sock_ops tcp6_timewait_sock_ops = {
        .twsk_destructor= tcp_twsk_destructor,
 };
 
-static void tcp_v6_send_check(struct sock *sk, int len, struct sk_buff *skb)
+static void __tcp_v6_send_check(struct sk_buff *skb,
+                               struct in6_addr *saddr, struct in6_addr *daddr)
 {
-       struct ipv6_pinfo *np = inet6_sk(sk);
        struct tcphdr *th = tcp_hdr(skb);
 
        if (skb->ip_summed == CHECKSUM_PARTIAL) {
-               th->check = ~csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP,  0);
+               th->check = ~tcp_v6_check(skb->len, saddr, daddr, 0);
                skb->csum_start = skb_transport_header(skb) - skb->head;
                skb->csum_offset = offsetof(struct tcphdr, check);
        } else {
-               th->check = csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP,
-                                           csum_partial(th, th->doff<<2,
-                                                        skb->csum));
+               th->check = tcp_v6_check(skb->len, saddr, daddr,
+                                        csum_partial(th, th->doff << 2,
+                                                     skb->csum));
        }
 }
 
+static void tcp_v6_send_check(struct sock *sk, struct sk_buff *skb)
+{
+       struct ipv6_pinfo *np = inet6_sk(sk);
+
+       __tcp_v6_send_check(skb, &np->saddr, &np->daddr);
+}
+
 static int tcp_v6_gso_send_check(struct sk_buff *skb)
 {
        struct ipv6hdr *ipv6h;
@@ -946,11 +947,8 @@ static int tcp_v6_gso_send_check(struct sk_buff *skb)
        th = tcp_hdr(skb);
 
        th->check = 0;
-       th->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, skb->len,
-                                    IPPROTO_TCP, 0);
-       skb->csum_start = skb_transport_header(skb) - skb->head;
-       skb->csum_offset = offsetof(struct tcphdr, check);
        skb->ip_summed = CHECKSUM_PARTIAL;
+       __tcp_v6_send_check(skb, &ipv6h->saddr, &ipv6h->daddr);
        return 0;
 }
 
@@ -1047,15 +1045,14 @@ static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win,
        }
 #endif
 
-       buff->csum = csum_partial(t1, tot_len, 0);
-
        memset(&fl, 0, sizeof(fl));
        ipv6_addr_copy(&fl.fl6_dst, &ipv6_hdr(skb)->saddr);
        ipv6_addr_copy(&fl.fl6_src, &ipv6_hdr(skb)->daddr);
 
-       t1->check = csum_ipv6_magic(&fl.fl6_src, &fl.fl6_dst,
-                                   tot_len, IPPROTO_TCP,
-                                   buff->csum);
+       buff->ip_summed = CHECKSUM_PARTIAL;
+       buff->csum = 0;
+
+       __tcp_v6_send_check(buff, &fl.fl6_src, &fl.fl6_dst);
 
        fl.proto = IPPROTO_TCP;
        fl.oif = inet6_iif(skb);
@@ -1070,7 +1067,7 @@ static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win,
        if (!ip6_dst_lookup(ctl_sk, &dst, &fl)) {
                if (xfrm_lookup(net, &dst, &fl, NULL, 0) >= 0) {
                        skb_dst_set(buff, dst);
-                       ip6_xmit(ctl_sk, buff, &fl, NULL, 0);
+                       ip6_xmit(ctl_sk, buff, &fl, NULL);
                        TCP_INC_STATS_BH(net, TCP_MIB_OUTSEGS);
                        if (rst)
                                TCP_INC_STATS_BH(net, TCP_MIB_OUTRSTS);
@@ -1233,12 +1230,12 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
                        goto drop_and_free;
 
                /* Secret recipe starts with IP addresses */
-               d = &ipv6_hdr(skb)->daddr.s6_addr32[0];
+               d = (__force u32 *)&ipv6_hdr(skb)->daddr.s6_addr32[0];
                *mess++ ^= *d++;
                *mess++ ^= *d++;
                *mess++ ^= *d++;
                *mess++ ^= *d++;
-               d = &ipv6_hdr(skb)->saddr.s6_addr32[0];
+               d = (__force u32 *)&ipv6_hdr(skb)->saddr.s6_addr32[0];
                *mess++ ^= *d++;
                *mess++ ^= *d++;
                *mess++ ^= *d++;
@@ -1385,18 +1382,13 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
                goto out_overflow;
 
        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_TCP;
                ipv6_addr_copy(&fl.fl6_dst, &treq->rmt_addr);
-               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;
-               }
+               final_p = fl6_update_dst(&fl, opt, &final);
                ipv6_addr_copy(&fl.fl6_src, &treq->loc_addr);
                fl.oif = sk->sk_bound_dev_if;
                fl.mark = sk->sk_mark;
@@ -1676,6 +1668,7 @@ ipv6_pktoptions:
 static int tcp_v6_rcv(struct sk_buff *skb)
 {
        struct tcphdr *th;
+       struct ipv6hdr *hdr;
        struct sock *sk;
        int ret;
        struct net *net = dev_net(skb->dev);
@@ -1702,12 +1695,13 @@ static int tcp_v6_rcv(struct sk_buff *skb)
                goto bad_packet;
 
        th = tcp_hdr(skb);
+       hdr = ipv6_hdr(skb);
        TCP_SKB_CB(skb)->seq = ntohl(th->seq);
        TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin +
                                    skb->len - th->doff*4);
        TCP_SKB_CB(skb)->ack_seq = ntohl(th->ack_seq);
        TCP_SKB_CB(skb)->when = 0;
-       TCP_SKB_CB(skb)->flags = ipv6_get_dsfield(ipv6_hdr(skb));
+       TCP_SKB_CB(skb)->flags = ipv6_get_dsfield(hdr);
        TCP_SKB_CB(skb)->sacked = 0;
 
        sk = __inet6_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest);
@@ -1718,6 +1712,11 @@ process:
        if (sk->sk_state == TCP_TIME_WAIT)
                goto do_time_wait;
 
+       if (hdr->hop_limit < inet6_sk(sk)->min_hopcount) {
+               NET_INC_STATS_BH(net, LINUX_MIB_TCPMINTTLDROP);
+               goto discard_and_relse;
+       }
+
        if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
                goto discard_and_relse;