History

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

Tom Herbert committed 12 years ago

Commit cb1ce2e1 parent 19469a811 files · +62 −6Browse files

Unified · Split

11 files
  1. Documentation/networking/ip-sysctl.txt +9 −0
  2. include/linux/ipv6.h +2 −1
  3. include/net/ipv6.h +20 −0
  4. include/net/netns/ipv6.h +1 −0
  5. include/uapi/linux/in6.h +1 −0
  6. net/ipv6/af_inet6.c +1 −0
  7. net/ipv6/ip6_gre.c +5 −2
  8. net/ipv6/ip6_output.c +5 −2
  9. net/ipv6/ip6_tunnel.c +2 −1
  10. net/ipv6/ipv6_sockglue.c +8 −0
  11. net/ipv6/sysctl_net_ipv6.c +8 −0
Documentation/networking/ip-sysctl.txt+9−0
@@ -1132,6 +1132,15 @@ flowlabel_consistency - BOOLEAN · 1131 unmodified lines
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+2−1
@@ -199,7 +199,8 @@ struct ipv6_pinfo { · 198 unmodified lines
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+20−0
@@ -699,6 +699,26 @@ static inline void ip6_set_txhash(struct sock *sk) · 698 unmodified lines
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+1−0
@@ -28,6 +28,7 @@ struct netns_sysctl_ipv6 { · 27 unmodified lines
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+1−0
@@ -233,6 +233,7 @@ struct in6_flowlabel_req { · 232 unmodified lines
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+1−0
@@ -765,6 +765,7 @@ static int __net_init inet6_net_init(struct net *net) · 764 unmodified lines
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+5−2
@@ -723,7 +723,8 @@ static netdev_tx_t ip6gre_xmit2(struct sk_buff *skb, · 722 unmodified lines
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, · 444 unmodified lines
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+5−2
@@ -205,7 +205,8 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6, · 204 unmodified lines
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) · 1357 unmodified lines
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+2−1
@@ -1046,7 +1046,8 @@ static int ip6_tnl_xmit2(struct sk_buff *skb, · 1045 unmodified lines
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+8−0
@@ -834,6 +834,10 @@ pref_skip_coa: · 833 unmodified lines
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, · 433 unmodified lines
12731277 val = np->dontfrag;
12741278 break;
12751279
1280+ case IPV6_AUTOFLOWLABEL:
1281+ val = np->autoflowlabel;
1282+ break;
1283+
12761284 default:
12771285 return -ENOPROTOOPT;
12781286 }
net/ipv6/sysctl_net_ipv6.c+8−0
@@ -38,6 +38,13 @@ static struct ctl_table ipv6_table_template[] = { · 37 unmodified lines
3838 .mode = 0644,
3939 .proc_handler = proc_dointvec
4040 },
41+ {
42+ .procname = "auto_flowlabels",
43+ .data = &init_net.ipv6.sysctl.auto_flowlabels,
44+ .maxlen = sizeof(int),
45+ .mode = 0644,
46+ .proc_handler = proc_dointvec
47+ },
4148 {
4249 .procname = "fwmark_reflect",
4350 .data = &init_net.ipv6.sysctl.fwmark_reflect,
@@ -74,6 +81,7 @@ static int __net_init ipv6_sysctl_net_init(struct net *net) · 30 unmodified lines
7481 ipv6_table[0].data = &net->ipv6.sysctl.bindv6only;
7582 ipv6_table[1].data = &net->ipv6.sysctl.anycast_src_echo_reply;
7683 ipv6_table[2].data = &net->ipv6.sysctl.flowlabel_consistency;
84+ ipv6_table[3].data = &net->ipv6.sysctl.auto_flowlabels;
7785
7886 ipv6_route_table = ipv6_route_sysctl_init(net);
7987 if (!ipv6_route_table)