ipv6: tcp: enable flowlabel reflection in some RST packets
authorEric Dumazet <edumazet@google.com>
Wed, 5 Jun 2019 14:55:09 +0000 (07:55 -0700)
committerDavid S. Miller <davem@davemloft.net>
Thu, 6 Jun 2019 02:05:01 +0000 (19:05 -0700)
When RST packets are sent because no socket could be found,
it makes sense to use flowlabel_reflect sysctl to decide
if a reflection of the flowlabel is requested.

This extends commit 22b6722bfa59 ("ipv6: Add sysctl for per
namespace flow label reflection"), for some TCP RST packets.

In order to provide full control of this new feature,
flowlabel_reflect becomes a bitmask.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Documentation/networking/ip-sysctl.txt
net/ipv6/af_inet6.c
net/ipv6/sysctl_net_ipv6.c
net/ipv6/tcp_ipv6.c

index a73b3a02e49aa4e2a072e366845d36e30ffa44ea..f4b1043e92edc78e93a64eaec467ea615ee21eab 100644 (file)
@@ -1429,14 +1429,24 @@ flowlabel_state_ranges - BOOLEAN
        FALSE: disabled
        Default: true
 
-flowlabel_reflect - BOOLEAN
-       Automatically reflect the flow label. Needed for Path MTU
+flowlabel_reflect - INTEGER
+       Control flow label reflection. Needed for Path MTU
        Discovery to work with Equal Cost Multipath Routing in anycast
        environments. See RFC 7690 and:
        https://tools.ietf.org/html/draft-wang-6man-flow-label-reflection-01
-       TRUE: enabled
-       FALSE: disabled
-       Default: FALSE
+
+       This is a mask of two bits.
+       1: enabled for established flows
+
+       Note that this prevents automatic flowlabel changes, as done
+       in "tcp: change IPv6 flow-label upon receiving spurious retransmission"
+       and "tcp: Change txhash on every SYN and RTO retransmit"
+
+       2: enabled for TCP RESET packets (no active listener)
+       If set, a RST packet sent in response to a SYN packet on a closed
+       port will reflect the incoming flow label.
+
+       Default: 0
 
 fib_multipath_hash_policy - INTEGER
        Controls which hash policy to use for multipath routes.
index cc6f8d0c625afea5b9b76014396fd8f4370ecf20..ceab2fe2833b9f571cd90725902671ef58a04726 100644 (file)
@@ -212,7 +212,7 @@ lookup_protocol:
        np->mc_loop     = 1;
        np->mc_all      = 1;
        np->pmtudisc    = IPV6_PMTUDISC_WANT;
-       np->repflow     = net->ipv6.sysctl.flowlabel_reflect;
+       np->repflow     = net->ipv6.sysctl.flowlabel_reflect & 1;
        sk->sk_ipv6only = net->ipv6.sysctl.bindv6only;
 
        /* Init the ipv4 part of the socket since we can have sockets
index e15cd37024fd9786bc675754514f03f5a8c919c2..6d86fac472e7298cbd8df7aa0b190cf0087675e2 100644 (file)
@@ -23,6 +23,7 @@
 
 static int zero;
 static int one = 1;
+static int three = 3;
 static int auto_flowlabels_min;
 static int auto_flowlabels_max = IP6_AUTO_FLOW_LABEL_MAX;
 
@@ -114,6 +115,8 @@ static struct ctl_table ipv6_table_template[] = {
                .maxlen         = sizeof(int),
                .mode           = 0644,
                .proc_handler   = proc_dointvec,
+               .extra1         = &zero,
+               .extra2         = &three,
        },
        {
                .procname       = "max_dst_opts_number",
index beaf284563015ef0677c39fc056e6ecde3518920..4ccb06ea8ce32d614fc0848e1c4e74b441fa1f2c 100644 (file)
@@ -916,15 +916,17 @@ static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32
 static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
 {
        const struct tcphdr *th = tcp_hdr(skb);
+       struct ipv6hdr *ipv6h = ipv6_hdr(skb);
        u32 seq = 0, ack_seq = 0;
        struct tcp_md5sig_key *key = NULL;
 #ifdef CONFIG_TCP_MD5SIG
        const __u8 *hash_location = NULL;
-       struct ipv6hdr *ipv6h = ipv6_hdr(skb);
        unsigned char newhash[16];
        int genhash;
        struct sock *sk1 = NULL;
 #endif
+       __be32 label = 0;
+       struct net *net;
        int oif = 0;
 
        if (th->rst)
@@ -936,6 +938,7 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
        if (!sk && !ipv6_unicast_destination(skb))
                return;
 
+       net = dev_net(skb_dst(skb)->dev);
 #ifdef CONFIG_TCP_MD5SIG
        rcu_read_lock();
        hash_location = tcp_parse_md5sig_option(th);
@@ -949,7 +952,7 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
                 * Incoming packet is checked with md5 hash with finding key,
                 * no RST generated if md5 hash doesn't match.
                 */
-               sk1 = inet6_lookup_listener(dev_net(skb_dst(skb)->dev),
+               sk1 = inet6_lookup_listener(net,
                                           &tcp_hashinfo, NULL, 0,
                                           &ipv6h->saddr,
                                           th->source, &ipv6h->daddr,
@@ -979,9 +982,13 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)
                oif = sk->sk_bound_dev_if;
                if (sk_fullsock(sk))
                        trace_tcp_send_reset(sk, skb);
+       } else {
+               if (net->ipv6.sysctl.flowlabel_reflect & 2)
+                       label = ip6_flowlabel(ipv6h);
        }
 
-       tcp_v6_send_response(sk, skb, seq, ack_seq, 0, 0, 0, oif, key, 1, 0, 0);
+       tcp_v6_send_response(sk, skb, seq, ack_seq, 0, 0, 0, oif, key, 1, 0,
+                            label);
 
 #ifdef CONFIG_TCP_MD5SIG
 out: