]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - net/ipv6/seg6_iptunnel.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm...
[linux.git] / net / ipv6 / seg6_iptunnel.c
index bf4763fd68c22388f59cb68755ec799545e7fb29..a8854dd3e9c5ef64a7a480bb6ff891fac0e6d1ea 100644 (file)
@@ -91,6 +91,24 @@ static void set_tun_src(struct net *net, struct net_device *dev,
        rcu_read_unlock();
 }
 
+/* Compute flowlabel for outer IPv6 header */
+static __be32 seg6_make_flowlabel(struct net *net, struct sk_buff *skb,
+                                 struct ipv6hdr *inner_hdr)
+{
+       int do_flowlabel = net->ipv6.sysctl.seg6_flowlabel;
+       __be32 flowlabel = 0;
+       u32 hash;
+
+       if (do_flowlabel > 0) {
+               hash = skb_get_hash(skb);
+               hash = rol32(hash, 16);
+               flowlabel = (__force __be32)hash & IPV6_FLOWLABEL_MASK;
+       } else if (!do_flowlabel && skb->protocol == htons(ETH_P_IPV6)) {
+               flowlabel = ip6_flowlabel(inner_hdr);
+       }
+       return flowlabel;
+}
+
 /* encapsulate an IPv6 packet within an outer IPv6 header with a given SRH */
 int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh, int proto)
 {
@@ -99,6 +117,7 @@ int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh, int proto)
        struct ipv6hdr *hdr, *inner_hdr;
        struct ipv6_sr_hdr *isrh;
        int hdrlen, tot_len, err;
+       __be32 flowlabel;
 
        hdrlen = (osrh->hdrlen + 1) << 3;
        tot_len = hdrlen + sizeof(*hdr);
@@ -108,6 +127,7 @@ int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh, int proto)
                return err;
 
        inner_hdr = ipv6_hdr(skb);
+       flowlabel = seg6_make_flowlabel(net, skb, inner_hdr);
 
        skb_push(skb, tot_len);
        skb_reset_network_header(skb);
@@ -121,10 +141,10 @@ int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh, int proto)
 
        if (skb->protocol == htons(ETH_P_IPV6)) {
                ip6_flow_hdr(hdr, ip6_tclass(ip6_flowinfo(inner_hdr)),
-                            ip6_flowlabel(inner_hdr));
+                            flowlabel);
                hdr->hop_limit = inner_hdr->hop_limit;
        } else {
-               ip6_flow_hdr(hdr, 0, 0);
+               ip6_flow_hdr(hdr, 0, flowlabel);
                hdr->hop_limit = ip6_dst_hoplimit(skb_dst(skb));
        }