Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ipv6: Implement different admin modes for automatic flow labels

Change the meaning of net.ipv6.auto_flowlabels to provide a mode for
automatic flow labels generation. There are four modes:

0: flow labels are disabled
1: flow labels are enabled, sockets can opt-out
2: flow labels are allowed, sockets can opt-in
3: flow labels are enabled and enforced, no opt-out for sockets

np->autoflowlabel is initialized according to the sysctl value.

Signed-off-by: Tom Herbert <tom@herbertland.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Tom Herbert and committed by
David S. Miller
42240901 67800f9b

+70 -25
+13 -7
Documentation/networking/ip-sysctl.txt
··· 1215 1215 FALSE: disabled 1216 1216 Default: TRUE 1217 1217 1218 - auto_flowlabels - BOOLEAN 1219 - Automatically generate flow labels based based on a flow hash 1220 - of the packet. This allows intermediate devices, such as routers, 1221 - to idenfify packet flows for mechanisms like Equal Cost Multipath 1218 + auto_flowlabels - INTEGER 1219 + Automatically generate flow labels based on a flow hash of the 1220 + packet. This allows intermediate devices, such as routers, to 1221 + identify packet flows for mechanisms like Equal Cost Multipath 1222 1222 Routing (see RFC 6438). 1223 - TRUE: enabled 1224 - FALSE: disabled 1225 - Default: false 1223 + 0: automatic flow labels are completely disabled 1224 + 1: automatic flow labels are enabled by default, they can be 1225 + disabled on a per socket basis using the IPV6_AUTOFLOWLABEL 1226 + socket option 1227 + 2: automatic flow labels are allowed, they may be enabled on a 1228 + per socket basis using the IPV6_AUTOFLOWLABEL socket option 1229 + 3: automatic flow labels are enabled and enforced, they cannot 1230 + be disabled by the socket option 1231 + Default: 0 1226 1232 1227 1233 flowlabel_state_ranges - BOOLEAN 1228 1234 Split the flow label number space into two ranges. 0-0x7FFFF is
+46 -13
include/net/ipv6.h
··· 707 707 } 708 708 709 709 #if IS_ENABLED(CONFIG_IPV6) 710 + 711 + /* Sysctl settings for net ipv6.auto_flowlabels */ 712 + #define IP6_AUTO_FLOW_LABEL_OFF 0 713 + #define IP6_AUTO_FLOW_LABEL_OPTOUT 1 714 + #define IP6_AUTO_FLOW_LABEL_OPTIN 2 715 + #define IP6_AUTO_FLOW_LABEL_FORCED 3 716 + 717 + #define IP6_AUTO_FLOW_LABEL_MAX IP6_AUTO_FLOW_LABEL_FORCED 718 + 719 + #define IP6_DEFAULT_AUTO_FLOW_LABELS IP6_AUTO_FLOW_LABEL_OFF 720 + 710 721 static inline __be32 ip6_make_flowlabel(struct net *net, struct sk_buff *skb, 711 722 __be32 flowlabel, bool autolabel, 712 723 struct flowi6 *fl6) 713 724 { 714 - if (!flowlabel && (autolabel || net->ipv6.sysctl.auto_flowlabels)) { 715 - u32 hash; 725 + u32 hash; 716 726 717 - hash = skb_get_hash_flowi6(skb, fl6); 727 + if (flowlabel || 728 + net->ipv6.sysctl.auto_flowlabels == IP6_AUTO_FLOW_LABEL_OFF || 729 + (!autolabel && 730 + net->ipv6.sysctl.auto_flowlabels != IP6_AUTO_FLOW_LABEL_FORCED)) 731 + return flowlabel; 718 732 719 - /* Since this is being sent on the wire obfuscate hash a bit 720 - * to minimize possbility that any useful information to an 721 - * attacker is leaked. Only lower 20 bits are relevant. 722 - */ 723 - hash ^= hash >> 12; 733 + hash = skb_get_hash_flowi6(skb, fl6); 724 734 725 - flowlabel = (__force __be32)hash & IPV6_FLOWLABEL_MASK; 735 + /* Since this is being sent on the wire obfuscate hash a bit 736 + * to minimize possbility that any useful information to an 737 + * attacker is leaked. Only lower 20 bits are relevant. 738 + */ 739 + rol32(hash, 16); 726 740 727 - if (net->ipv6.sysctl.flowlabel_state_ranges) 728 - flowlabel |= IPV6_FLOWLABEL_STATELESS_FLAG; 729 - } 741 + flowlabel = (__force __be32)hash & IPV6_FLOWLABEL_MASK; 742 + 743 + if (net->ipv6.sysctl.flowlabel_state_ranges) 744 + flowlabel |= IPV6_FLOWLABEL_STATELESS_FLAG; 730 745 731 746 return flowlabel; 747 + } 748 + 749 + static inline int ip6_default_np_autolabel(struct net *net) 750 + { 751 + switch (net->ipv6.sysctl.auto_flowlabels) { 752 + case IP6_AUTO_FLOW_LABEL_OFF: 753 + case IP6_AUTO_FLOW_LABEL_OPTIN: 754 + default: 755 + return 0; 756 + case IP6_AUTO_FLOW_LABEL_OPTOUT: 757 + case IP6_AUTO_FLOW_LABEL_FORCED: 758 + return 1; 759 + } 732 760 } 733 761 #else 734 762 static inline void ip6_set_txhash(struct sock *sk) { } 735 763 static inline __be32 ip6_make_flowlabel(struct net *net, struct sk_buff *skb, 736 - __be32 flowlabel, bool autolabel) 764 + __be32 flowlabel, bool autolabel, 765 + struct flowi6 *fl6) 737 766 { 738 767 return flowlabel; 768 + } 769 + static inline int ip6_default_np_autolabel(struct net *net) 770 + { 771 + return 0; 739 772 } 740 773 #endif 741 774
+2 -1
net/ipv6/af_inet6.c
··· 197 197 np->mcast_hops = IPV6_DEFAULT_MCASTHOPS; 198 198 np->mc_loop = 1; 199 199 np->pmtudisc = IPV6_PMTUDISC_WANT; 200 + np->autoflowlabel = ip6_default_np_autolabel(sock_net(sk)); 200 201 sk->sk_ipv6only = net->ipv6.sysctl.bindv6only; 201 202 202 203 /* Init the ipv4 part of the socket since we can have sockets ··· 768 767 net->ipv6.sysctl.bindv6only = 0; 769 768 net->ipv6.sysctl.icmpv6_time = 1*HZ; 770 769 net->ipv6.sysctl.flowlabel_consistency = 1; 771 - net->ipv6.sysctl.auto_flowlabels = 0; 770 + net->ipv6.sysctl.auto_flowlabels = IP6_DEFAULT_AUTO_FLOW_LABELS; 772 771 net->ipv6.sysctl.idgen_retries = 3; 773 772 net->ipv6.sysctl.idgen_delay = 1 * HZ; 774 773 net->ipv6.sysctl.flowlabel_state_ranges = 1;
+2 -2
net/ipv6/ip6_gre.c
··· 728 728 */ 729 729 ipv6h = ipv6_hdr(skb); 730 730 ip6_flow_hdr(ipv6h, INET_ECN_encapsulate(0, dsfield), 731 - ip6_make_flowlabel(net, skb, fl6->flowlabel, false, fl6)); 731 + ip6_make_flowlabel(net, skb, fl6->flowlabel, true, fl6)); 732 732 ipv6h->hop_limit = tunnel->parms.hop_limit; 733 733 ipv6h->nexthdr = proto; 734 734 ipv6h->saddr = fl6->saddr; ··· 1182 1182 1183 1183 ip6_flow_hdr(ipv6h, 0, 1184 1184 ip6_make_flowlabel(dev_net(dev), skb, 1185 - t->fl.u.ip6.flowlabel, false, 1185 + t->fl.u.ip6.flowlabel, true, 1186 1186 &t->fl.u.ip6)); 1187 1187 ipv6h->hop_limit = t->parms.hop_limit; 1188 1188 ipv6h->nexthdr = NEXTHDR_GRE;
+1 -1
net/ipv6/ip6_tunnel.c
··· 1095 1095 skb_reset_network_header(skb); 1096 1096 ipv6h = ipv6_hdr(skb); 1097 1097 ip6_flow_hdr(ipv6h, INET_ECN_encapsulate(0, dsfield), 1098 - ip6_make_flowlabel(net, skb, fl6->flowlabel, false, fl6)); 1098 + ip6_make_flowlabel(net, skb, fl6->flowlabel, true, fl6)); 1099 1099 ipv6h->hop_limit = t->parms.hop_limit; 1100 1100 ipv6h->nexthdr = proto; 1101 1101 ipv6h->saddr = fl6->saddr;
+6 -1
net/ipv6/sysctl_net_ipv6.c
··· 17 17 #include <net/inet_frag.h> 18 18 19 19 static int one = 1; 20 + static int auto_flowlabels_min; 21 + static int auto_flowlabels_max = IP6_AUTO_FLOW_LABEL_MAX; 22 + 20 23 21 24 static struct ctl_table ipv6_table_template[] = { 22 25 { ··· 48 45 .data = &init_net.ipv6.sysctl.auto_flowlabels, 49 46 .maxlen = sizeof(int), 50 47 .mode = 0644, 51 - .proc_handler = proc_dointvec 48 + .proc_handler = proc_dointvec_minmax, 49 + .extra1 = &auto_flowlabels_min, 50 + .extra2 = &auto_flowlabels_max 52 51 }, 53 52 { 54 53 .procname = "fwmark_reflect",