mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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>
This commit is contained in:
		
							parent
							
								
									67800f9b1f
								
							
						
					
					
						commit
						42240901f7
					
				
					 6 changed files with 70 additions and 25 deletions
				
			
		| 
						 | 
					@ -1215,14 +1215,20 @@ flowlabel_consistency - BOOLEAN
 | 
				
			||||||
	FALSE: disabled
 | 
						FALSE: disabled
 | 
				
			||||||
	Default: TRUE
 | 
						Default: TRUE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
auto_flowlabels - BOOLEAN
 | 
					auto_flowlabels - INTEGER
 | 
				
			||||||
	Automatically generate flow labels based based on a flow hash
 | 
						Automatically generate flow labels based on a flow hash of the
 | 
				
			||||||
	of the packet. This allows intermediate devices, such as routers,
 | 
						packet. This allows intermediate devices, such as routers, to
 | 
				
			||||||
	to idenfify packet flows for mechanisms like Equal Cost Multipath
 | 
						identify packet flows for mechanisms like Equal Cost Multipath
 | 
				
			||||||
	Routing (see RFC 6438).
 | 
						Routing (see RFC 6438).
 | 
				
			||||||
	TRUE: enabled
 | 
						0: automatic flow labels are completely disabled
 | 
				
			||||||
	FALSE: disabled
 | 
						1: automatic flow labels are enabled by default, they can be
 | 
				
			||||||
	Default: false
 | 
						   disabled on a per socket basis using the IPV6_AUTOFLOWLABEL
 | 
				
			||||||
 | 
						   socket option
 | 
				
			||||||
 | 
						2: automatic flow labels are allowed, they may be enabled on a
 | 
				
			||||||
 | 
						   per socket basis using the IPV6_AUTOFLOWLABEL socket option
 | 
				
			||||||
 | 
						3: automatic flow labels are enabled and enforced, they cannot
 | 
				
			||||||
 | 
						   be disabled by the socket option
 | 
				
			||||||
 | 
						Default: 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
flowlabel_state_ranges - BOOLEAN
 | 
					flowlabel_state_ranges - BOOLEAN
 | 
				
			||||||
	Split the flow label number space into two ranges. 0-0x7FFFF is
 | 
						Split the flow label number space into two ranges. 0-0x7FFFF is
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -707,36 +707,69 @@ static inline void iph_to_flow_copy_v6addrs(struct flow_keys *flow,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if IS_ENABLED(CONFIG_IPV6)
 | 
					#if IS_ENABLED(CONFIG_IPV6)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Sysctl settings for net ipv6.auto_flowlabels */
 | 
				
			||||||
 | 
					#define IP6_AUTO_FLOW_LABEL_OFF		0
 | 
				
			||||||
 | 
					#define IP6_AUTO_FLOW_LABEL_OPTOUT	1
 | 
				
			||||||
 | 
					#define IP6_AUTO_FLOW_LABEL_OPTIN	2
 | 
				
			||||||
 | 
					#define IP6_AUTO_FLOW_LABEL_FORCED	3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define IP6_AUTO_FLOW_LABEL_MAX		IP6_AUTO_FLOW_LABEL_FORCED
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define IP6_DEFAULT_AUTO_FLOW_LABELS	IP6_AUTO_FLOW_LABEL_OFF
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline __be32 ip6_make_flowlabel(struct net *net, struct sk_buff *skb,
 | 
					static inline __be32 ip6_make_flowlabel(struct net *net, struct sk_buff *skb,
 | 
				
			||||||
					__be32 flowlabel, bool autolabel,
 | 
										__be32 flowlabel, bool autolabel,
 | 
				
			||||||
					struct flowi6 *fl6)
 | 
										struct flowi6 *fl6)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (!flowlabel && (autolabel || net->ipv6.sysctl.auto_flowlabels)) {
 | 
						u32 hash;
 | 
				
			||||||
		u32 hash;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		hash = skb_get_hash_flowi6(skb, fl6);
 | 
						if (flowlabel ||
 | 
				
			||||||
 | 
						    net->ipv6.sysctl.auto_flowlabels == IP6_AUTO_FLOW_LABEL_OFF ||
 | 
				
			||||||
 | 
						    (!autolabel &&
 | 
				
			||||||
 | 
						     net->ipv6.sysctl.auto_flowlabels != IP6_AUTO_FLOW_LABEL_FORCED))
 | 
				
			||||||
 | 
							return flowlabel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* Since this is being sent on the wire obfuscate hash a bit
 | 
						hash = skb_get_hash_flowi6(skb, fl6);
 | 
				
			||||||
		 * to minimize possbility that any useful information to an
 | 
					 | 
				
			||||||
		 * attacker is leaked. Only lower 20 bits are relevant.
 | 
					 | 
				
			||||||
		 */
 | 
					 | 
				
			||||||
		hash ^= hash >> 12;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		flowlabel = (__force __be32)hash & IPV6_FLOWLABEL_MASK;
 | 
						/* Since this is being sent on the wire obfuscate hash a bit
 | 
				
			||||||
 | 
						 * to minimize possbility that any useful information to an
 | 
				
			||||||
 | 
						 * attacker is leaked. Only lower 20 bits are relevant.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						rol32(hash, 16);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (net->ipv6.sysctl.flowlabel_state_ranges)
 | 
						flowlabel = (__force __be32)hash & IPV6_FLOWLABEL_MASK;
 | 
				
			||||||
			flowlabel |= IPV6_FLOWLABEL_STATELESS_FLAG;
 | 
					
 | 
				
			||||||
	}
 | 
						if (net->ipv6.sysctl.flowlabel_state_ranges)
 | 
				
			||||||
 | 
							flowlabel |= IPV6_FLOWLABEL_STATELESS_FLAG;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return flowlabel;
 | 
						return flowlabel;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline int ip6_default_np_autolabel(struct net *net)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						switch (net->ipv6.sysctl.auto_flowlabels) {
 | 
				
			||||||
 | 
						case IP6_AUTO_FLOW_LABEL_OFF:
 | 
				
			||||||
 | 
						case IP6_AUTO_FLOW_LABEL_OPTIN:
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						case IP6_AUTO_FLOW_LABEL_OPTOUT:
 | 
				
			||||||
 | 
						case IP6_AUTO_FLOW_LABEL_FORCED:
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
static inline void ip6_set_txhash(struct sock *sk) { }
 | 
					static inline void ip6_set_txhash(struct sock *sk) { }
 | 
				
			||||||
static inline __be32 ip6_make_flowlabel(struct net *net, struct sk_buff *skb,
 | 
					static inline __be32 ip6_make_flowlabel(struct net *net, struct sk_buff *skb,
 | 
				
			||||||
					__be32 flowlabel, bool autolabel)
 | 
										__be32 flowlabel, bool autolabel,
 | 
				
			||||||
 | 
										struct flowi6 *fl6)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return flowlabel;
 | 
						return flowlabel;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					static inline int ip6_default_np_autolabel(struct net *net)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -197,6 +197,7 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol,
 | 
				
			||||||
	np->mcast_hops	= IPV6_DEFAULT_MCASTHOPS;
 | 
						np->mcast_hops	= IPV6_DEFAULT_MCASTHOPS;
 | 
				
			||||||
	np->mc_loop	= 1;
 | 
						np->mc_loop	= 1;
 | 
				
			||||||
	np->pmtudisc	= IPV6_PMTUDISC_WANT;
 | 
						np->pmtudisc	= IPV6_PMTUDISC_WANT;
 | 
				
			||||||
 | 
						np->autoflowlabel = ip6_default_np_autolabel(sock_net(sk));
 | 
				
			||||||
	sk->sk_ipv6only	= net->ipv6.sysctl.bindv6only;
 | 
						sk->sk_ipv6only	= net->ipv6.sysctl.bindv6only;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Init the ipv4 part of the socket since we can have sockets
 | 
						/* Init the ipv4 part of the socket since we can have sockets
 | 
				
			||||||
| 
						 | 
					@ -767,7 +768,7 @@ static int __net_init inet6_net_init(struct net *net)
 | 
				
			||||||
	net->ipv6.sysctl.bindv6only = 0;
 | 
						net->ipv6.sysctl.bindv6only = 0;
 | 
				
			||||||
	net->ipv6.sysctl.icmpv6_time = 1*HZ;
 | 
						net->ipv6.sysctl.icmpv6_time = 1*HZ;
 | 
				
			||||||
	net->ipv6.sysctl.flowlabel_consistency = 1;
 | 
						net->ipv6.sysctl.flowlabel_consistency = 1;
 | 
				
			||||||
	net->ipv6.sysctl.auto_flowlabels = 0;
 | 
						net->ipv6.sysctl.auto_flowlabels = IP6_DEFAULT_AUTO_FLOW_LABELS;
 | 
				
			||||||
	net->ipv6.sysctl.idgen_retries = 3;
 | 
						net->ipv6.sysctl.idgen_retries = 3;
 | 
				
			||||||
	net->ipv6.sysctl.idgen_delay = 1 * HZ;
 | 
						net->ipv6.sysctl.idgen_delay = 1 * HZ;
 | 
				
			||||||
	net->ipv6.sysctl.flowlabel_state_ranges = 1;
 | 
						net->ipv6.sysctl.flowlabel_state_ranges = 1;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -728,7 +728,7 @@ static netdev_tx_t ip6gre_xmit2(struct sk_buff *skb,
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	ipv6h = ipv6_hdr(skb);
 | 
						ipv6h = ipv6_hdr(skb);
 | 
				
			||||||
	ip6_flow_hdr(ipv6h, INET_ECN_encapsulate(0, dsfield),
 | 
						ip6_flow_hdr(ipv6h, INET_ECN_encapsulate(0, dsfield),
 | 
				
			||||||
		     ip6_make_flowlabel(net, skb, fl6->flowlabel, false, fl6));
 | 
							     ip6_make_flowlabel(net, skb, fl6->flowlabel, true, fl6));
 | 
				
			||||||
	ipv6h->hop_limit = tunnel->parms.hop_limit;
 | 
						ipv6h->hop_limit = tunnel->parms.hop_limit;
 | 
				
			||||||
	ipv6h->nexthdr = proto;
 | 
						ipv6h->nexthdr = proto;
 | 
				
			||||||
	ipv6h->saddr = fl6->saddr;
 | 
						ipv6h->saddr = fl6->saddr;
 | 
				
			||||||
| 
						 | 
					@ -1182,7 +1182,7 @@ static int ip6gre_header(struct sk_buff *skb, struct net_device *dev,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ip6_flow_hdr(ipv6h, 0,
 | 
						ip6_flow_hdr(ipv6h, 0,
 | 
				
			||||||
		     ip6_make_flowlabel(dev_net(dev), skb,
 | 
							     ip6_make_flowlabel(dev_net(dev), skb,
 | 
				
			||||||
					t->fl.u.ip6.flowlabel, false,
 | 
										t->fl.u.ip6.flowlabel, true,
 | 
				
			||||||
					&t->fl.u.ip6));
 | 
										&t->fl.u.ip6));
 | 
				
			||||||
	ipv6h->hop_limit = t->parms.hop_limit;
 | 
						ipv6h->hop_limit = t->parms.hop_limit;
 | 
				
			||||||
	ipv6h->nexthdr = NEXTHDR_GRE;
 | 
						ipv6h->nexthdr = NEXTHDR_GRE;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1095,7 +1095,7 @@ static int ip6_tnl_xmit2(struct sk_buff *skb,
 | 
				
			||||||
	skb_reset_network_header(skb);
 | 
						skb_reset_network_header(skb);
 | 
				
			||||||
	ipv6h = ipv6_hdr(skb);
 | 
						ipv6h = ipv6_hdr(skb);
 | 
				
			||||||
	ip6_flow_hdr(ipv6h, INET_ECN_encapsulate(0, dsfield),
 | 
						ip6_flow_hdr(ipv6h, INET_ECN_encapsulate(0, dsfield),
 | 
				
			||||||
		     ip6_make_flowlabel(net, skb, fl6->flowlabel, false, fl6));
 | 
							     ip6_make_flowlabel(net, skb, fl6->flowlabel, true, fl6));
 | 
				
			||||||
	ipv6h->hop_limit = t->parms.hop_limit;
 | 
						ipv6h->hop_limit = t->parms.hop_limit;
 | 
				
			||||||
	ipv6h->nexthdr = proto;
 | 
						ipv6h->nexthdr = proto;
 | 
				
			||||||
	ipv6h->saddr = fl6->saddr;
 | 
						ipv6h->saddr = fl6->saddr;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,6 +17,9 @@
 | 
				
			||||||
#include <net/inet_frag.h>
 | 
					#include <net/inet_frag.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int one = 1;
 | 
					static int one = 1;
 | 
				
			||||||
 | 
					static int auto_flowlabels_min;
 | 
				
			||||||
 | 
					static int auto_flowlabels_max = IP6_AUTO_FLOW_LABEL_MAX;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct ctl_table ipv6_table_template[] = {
 | 
					static struct ctl_table ipv6_table_template[] = {
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
| 
						 | 
					@ -45,7 +48,9 @@ static struct ctl_table ipv6_table_template[] = {
 | 
				
			||||||
		.data		= &init_net.ipv6.sysctl.auto_flowlabels,
 | 
							.data		= &init_net.ipv6.sysctl.auto_flowlabels,
 | 
				
			||||||
		.maxlen		= sizeof(int),
 | 
							.maxlen		= sizeof(int),
 | 
				
			||||||
		.mode		= 0644,
 | 
							.mode		= 0644,
 | 
				
			||||||
		.proc_handler	= proc_dointvec
 | 
							.proc_handler	= proc_dointvec_minmax,
 | 
				
			||||||
 | 
							.extra1		= &auto_flowlabels_min,
 | 
				
			||||||
 | 
							.extra2		= &auto_flowlabels_max
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		.procname	= "fwmark_reflect",
 | 
							.procname	= "fwmark_reflect",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue