Skip to content

Commit cb1ce2e

Browse files
Tom Herbertdavem330
authored andcommitted
ipv6: Implement automatic flow label generation on transmit
Automatically generate flow labels for IPv6 packets on transmit. The flow label is computed based on skb_get_hash. The flow label will only automatically be set when it is zero otherwise (i.e. flow label manager hasn't set one). This supports the transmit side functionality of RFC 6438. Added an IPv6 sysctl auto_flowlabels to enable/disable this behavior system wide, and added IPV6_AUTOFLOWLABEL socket option to enable this functionality per socket. By default, auto flowlabels are disabled to avoid possible conflicts with flow label manager, however if this feature proves useful we may want to enable it by default. It should also be noted that FreeBSD has already implemented automatic flow labels (including the sysctl and socket option). In FreeBSD, automatic flow labels default to enabled. Performance impact: Running super_netperf with 200 flows for TCP_RR and UDP_RR for IPv6. Note that in UDP case, __skb_get_hash will be called for every packet with explains slight regression. In the TCP case the hash is saved in the socket so there is no regression. Automatic flow labels disabled: TCP_RR: 86.53% CPU utilization 127/195/322 90/95/99% latencies 1.40498e+06 tps UDP_RR: 90.70% CPU utilization 118/168/243 90/95/99% latencies 1.50309e+06 tps Automatic flow labels enabled: TCP_RR: 85.90% CPU utilization 128/199/337 90/95/99% latencies 1.40051e+06 UDP_RR 92.61% CPU utilization 115/164/236 90/95/99% latencies 1.4687e+06 Signed-off-by: Tom Herbert <therbert@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 19469a8 commit cb1ce2e

File tree

11 files changed

+62
-6
lines changed

11 files changed

+62
-6
lines changed

Documentation/networking/ip-sysctl.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,6 +1132,15 @@ flowlabel_consistency - BOOLEAN
11321132
FALSE: disabled
11331133
Default: TRUE
11341134

1135+
auto_flowlabels - BOOLEAN
1136+
Automatically generate flow labels based based on a flow hash
1137+
of the packet. This allows intermediate devices, such as routers,
1138+
to idenfify packet flows for mechanisms like Equal Cost Multipath
1139+
Routing (see RFC 6438).
1140+
TRUE: enabled
1141+
FALSE: disabled
1142+
Default: false
1143+
11351144
anycast_src_echo_reply - BOOLEAN
11361145
Controls the use of anycast addresses as source addresses for ICMPv6
11371146
echo reply

include/linux/ipv6.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,8 @@ struct ipv6_pinfo {
199199
* 010: prefer public address
200200
* 100: prefer care-of address
201201
*/
202-
dontfrag:1;
202+
dontfrag:1,
203+
autoflowlabel:1;
203204
__u8 min_hopcount;
204205
__u8 tclass;
205206
__be32 rcv_flowinfo;

include/net/ipv6.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,26 @@ static inline void ip6_set_txhash(struct sock *sk)
699699
sk->sk_txhash = flow_hash_from_keys(&keys);
700700
}
701701

702+
static inline __be32 ip6_make_flowlabel(struct net *net, struct sk_buff *skb,
703+
__be32 flowlabel, bool autolabel)
704+
{
705+
if (!flowlabel && (autolabel || net->ipv6.sysctl.auto_flowlabels)) {
706+
__be32 hash;
707+
708+
hash = skb_get_hash(skb);
709+
710+
/* Since this is being sent on the wire obfuscate hash a bit
711+
* to minimize possbility that any useful information to an
712+
* attacker is leaked. Only lower 20 bits are relevant.
713+
*/
714+
hash ^= hash >> 12;
715+
716+
flowlabel = hash & IPV6_FLOWLABEL_MASK;
717+
}
718+
719+
return flowlabel;
720+
}
721+
702722
/*
703723
* Header manipulation
704724
*/

include/net/netns/ipv6.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ struct netns_sysctl_ipv6 {
2828
int ip6_rt_mtu_expires;
2929
int ip6_rt_min_advmss;
3030
int flowlabel_consistency;
31+
int auto_flowlabels;
3132
int icmpv6_time;
3233
int anycast_src_echo_reply;
3334
int fwmark_reflect;

include/uapi/linux/in6.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ struct in6_flowlabel_req {
233233
#if 0 /* not yet */
234234
#define IPV6_USE_MIN_MTU 63
235235
#endif
236+
#define IPV6_AUTOFLOWLABEL 64
236237

237238
/*
238239
* Netfilter (1)

net/ipv6/af_inet6.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,7 @@ static int __net_init inet6_net_init(struct net *net)
765765
net->ipv6.sysctl.bindv6only = 0;
766766
net->ipv6.sysctl.icmpv6_time = 1*HZ;
767767
net->ipv6.sysctl.flowlabel_consistency = 1;
768+
net->ipv6.sysctl.auto_flowlabels = 0;
768769
atomic_set(&net->ipv6.rt_genid, 0);
769770

770771
err = ipv6_init_mibs(net);

net/ipv6/ip6_gre.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -723,7 +723,8 @@ static netdev_tx_t ip6gre_xmit2(struct sk_buff *skb,
723723
* Push down and install the IP header.
724724
*/
725725
ipv6h = ipv6_hdr(skb);
726-
ip6_flow_hdr(ipv6h, INET_ECN_encapsulate(0, dsfield), fl6->flowlabel);
726+
ip6_flow_hdr(ipv6h, INET_ECN_encapsulate(0, dsfield),
727+
ip6_make_flowlabel(net, skb, fl6->flowlabel, false));
727728
ipv6h->hop_limit = tunnel->parms.hop_limit;
728729
ipv6h->nexthdr = proto;
729730
ipv6h->saddr = fl6->saddr;
@@ -1174,7 +1175,9 @@ static int ip6gre_header(struct sk_buff *skb, struct net_device *dev,
11741175
struct ipv6hdr *ipv6h = (struct ipv6hdr *)skb_push(skb, t->hlen);
11751176
__be16 *p = (__be16 *)(ipv6h+1);
11761177

1177-
ip6_flow_hdr(ipv6h, 0, t->fl.u.ip6.flowlabel);
1178+
ip6_flow_hdr(ipv6h, 0,
1179+
ip6_make_flowlabel(dev_net(dev), skb,
1180+
t->fl.u.ip6.flowlabel, false));
11781181
ipv6h->hop_limit = t->parms.hop_limit;
11791182
ipv6h->nexthdr = NEXTHDR_GRE;
11801183
ipv6h->saddr = t->parms.laddr;

net/ipv6/ip6_output.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,8 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6,
205205
if (hlimit < 0)
206206
hlimit = ip6_dst_hoplimit(dst);
207207

208-
ip6_flow_hdr(hdr, tclass, fl6->flowlabel);
208+
ip6_flow_hdr(hdr, tclass, ip6_make_flowlabel(net, skb, fl6->flowlabel,
209+
np->autoflowlabel));
209210

210211
hdr->payload_len = htons(seg_len);
211212
hdr->nexthdr = proto;
@@ -1569,7 +1570,9 @@ int ip6_push_pending_frames(struct sock *sk)
15691570
skb_reset_network_header(skb);
15701571
hdr = ipv6_hdr(skb);
15711572

1572-
ip6_flow_hdr(hdr, np->cork.tclass, fl6->flowlabel);
1573+
ip6_flow_hdr(hdr, np->cork.tclass,
1574+
ip6_make_flowlabel(net, skb, fl6->flowlabel,
1575+
np->autoflowlabel));
15731576
hdr->hop_limit = np->cork.hop_limit;
15741577
hdr->nexthdr = proto;
15751578
hdr->saddr = fl6->saddr;

net/ipv6/ip6_tunnel.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1046,7 +1046,8 @@ static int ip6_tnl_xmit2(struct sk_buff *skb,
10461046
skb_push(skb, sizeof(struct ipv6hdr));
10471047
skb_reset_network_header(skb);
10481048
ipv6h = ipv6_hdr(skb);
1049-
ip6_flow_hdr(ipv6h, INET_ECN_encapsulate(0, dsfield), fl6->flowlabel);
1049+
ip6_flow_hdr(ipv6h, INET_ECN_encapsulate(0, dsfield),
1050+
ip6_make_flowlabel(net, skb, fl6->flowlabel, false));
10501051
ipv6h->hop_limit = t->parms.hop_limit;
10511052
ipv6h->nexthdr = proto;
10521053
ipv6h->saddr = fl6->saddr;

net/ipv6/ipv6_sockglue.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,10 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
834834
np->dontfrag = valbool;
835835
retv = 0;
836836
break;
837+
case IPV6_AUTOFLOWLABEL:
838+
np->autoflowlabel = valbool;
839+
retv = 0;
840+
break;
837841
}
838842

839843
release_sock(sk);
@@ -1273,6 +1277,10 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
12731277
val = np->dontfrag;
12741278
break;
12751279

1280+
case IPV6_AUTOFLOWLABEL:
1281+
val = np->autoflowlabel;
1282+
break;
1283+
12761284
default:
12771285
return -ENOPROTOOPT;
12781286
}

0 commit comments

Comments
 (0)