forked from mirrors/linux
		
	Merge branch 'fib_rules-support-sport-dport-and-proto-match'
Roopa Prabhu says: ==================== fib_rules: support sport, dport and proto match This series extends fib rule match support to include sport, dport and ip proto match (to complete the 5-tuple match support). Common use-cases of Policy based routing in the data center require 5-tuple match. The last 2 patches in the series add a call to flow dissect in the fwd path if required by the installed fib rules (controlled by a flag). v1: - Fix errors reported by kbuild and feedback on RFC - extend port match uapi to accomodate port ranges v2: - address comments from Nikolay, David Ahern and Paolo (Thanks!) Pending things I will submit separate patches for: - extack for fib rules - fib rules test (as requested by david ahern) ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
						commit
						a25724b05a
					
				
					 14 changed files with 292 additions and 31 deletions
				
			
		| 
						 | 
					@ -27,7 +27,7 @@ struct fib_rule {
 | 
				
			||||||
	u8			action;
 | 
						u8			action;
 | 
				
			||||||
	u8			l3mdev;
 | 
						u8			l3mdev;
 | 
				
			||||||
	u8                      proto;
 | 
						u8                      proto;
 | 
				
			||||||
	/* 1 byte hole, try to use */
 | 
						u8			ip_proto;
 | 
				
			||||||
	u32			target;
 | 
						u32			target;
 | 
				
			||||||
	__be64			tun_id;
 | 
						__be64			tun_id;
 | 
				
			||||||
	struct fib_rule __rcu	*ctarget;
 | 
						struct fib_rule __rcu	*ctarget;
 | 
				
			||||||
| 
						 | 
					@ -40,6 +40,8 @@ struct fib_rule {
 | 
				
			||||||
	char			iifname[IFNAMSIZ];
 | 
						char			iifname[IFNAMSIZ];
 | 
				
			||||||
	char			oifname[IFNAMSIZ];
 | 
						char			oifname[IFNAMSIZ];
 | 
				
			||||||
	struct fib_kuid_range	uid_range;
 | 
						struct fib_kuid_range	uid_range;
 | 
				
			||||||
 | 
						struct fib_rule_port_range	sport_range;
 | 
				
			||||||
 | 
						struct fib_rule_port_range	dport_range;
 | 
				
			||||||
	struct rcu_head		rcu;
 | 
						struct rcu_head		rcu;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -144,6 +146,38 @@ static inline u32 frh_get_table(struct fib_rule_hdr *frh, struct nlattr **nla)
 | 
				
			||||||
	return frh->table;
 | 
						return frh->table;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline bool fib_rule_port_range_set(const struct fib_rule_port_range *range)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return range->start != 0 && range->end != 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline bool fib_rule_port_inrange(const struct fib_rule_port_range *a,
 | 
				
			||||||
 | 
										 __be16 port)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return ntohs(port) >= a->start &&
 | 
				
			||||||
 | 
							ntohs(port) <= a->end;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline bool fib_rule_port_range_valid(const struct fib_rule_port_range *a)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return a->start != 0 && a->end != 0 && a->end < 0xffff &&
 | 
				
			||||||
 | 
							a->start <= a->end;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline bool fib_rule_port_range_compare(struct fib_rule_port_range *a,
 | 
				
			||||||
 | 
										       struct fib_rule_port_range *b)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return a->start == b->start &&
 | 
				
			||||||
 | 
							a->end == b->end;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline bool fib_rule_requires_fldissect(struct fib_rule *rule)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return rule->ip_proto ||
 | 
				
			||||||
 | 
							fib_rule_port_range_set(&rule->sport_range) ||
 | 
				
			||||||
 | 
							fib_rule_port_range_set(&rule->dport_range);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct fib_rules_ops *fib_rules_register(const struct fib_rules_ops *,
 | 
					struct fib_rules_ops *fib_rules_register(const struct fib_rules_ops *,
 | 
				
			||||||
					 struct net *);
 | 
										 struct net *);
 | 
				
			||||||
void fib_rules_unregister(struct fib_rules_ops *);
 | 
					void fib_rules_unregister(struct fib_rules_ops *);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -415,6 +415,24 @@ void fib6_rules_cleanup(void);
 | 
				
			||||||
bool fib6_rule_default(const struct fib_rule *rule);
 | 
					bool fib6_rule_default(const struct fib_rule *rule);
 | 
				
			||||||
int fib6_rules_dump(struct net *net, struct notifier_block *nb);
 | 
					int fib6_rules_dump(struct net *net, struct notifier_block *nb);
 | 
				
			||||||
unsigned int fib6_rules_seq_read(struct net *net);
 | 
					unsigned int fib6_rules_seq_read(struct net *net);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline bool fib6_rules_early_flow_dissect(struct net *net,
 | 
				
			||||||
 | 
											 struct sk_buff *skb,
 | 
				
			||||||
 | 
											 struct flowi6 *fl6,
 | 
				
			||||||
 | 
											 struct flow_keys *flkeys)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned int flag = FLOW_DISSECTOR_F_STOP_AT_ENCAP;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!net->ipv6.fib6_rules_require_fldissect)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						skb_flow_dissect_flow_keys(skb, flkeys, flag);
 | 
				
			||||||
 | 
						fl6->fl6_sport = flkeys->ports.src;
 | 
				
			||||||
 | 
						fl6->fl6_dport = flkeys->ports.dst;
 | 
				
			||||||
 | 
						fl6->flowi6_proto = flkeys->basic.ip_proto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
static inline int               fib6_rules_init(void)
 | 
					static inline int               fib6_rules_init(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -436,5 +454,12 @@ static inline unsigned int fib6_rules_seq_read(struct net *net)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					static inline bool fib6_rules_early_flow_dissect(struct net *net,
 | 
				
			||||||
 | 
											 struct sk_buff *skb,
 | 
				
			||||||
 | 
											 struct flowi6 *fl6,
 | 
				
			||||||
 | 
											 struct flow_keys *flkeys)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -127,7 +127,8 @@ static inline int ip6_route_get_saddr(struct net *net, struct rt6_info *rt,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
 | 
					struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr,
 | 
				
			||||||
			    const struct in6_addr *saddr, int oif, int flags);
 | 
								    const struct in6_addr *saddr, int oif, int flags);
 | 
				
			||||||
u32 rt6_multipath_hash(const struct flowi6 *fl6, const struct sk_buff *skb);
 | 
					u32 rt6_multipath_hash(const struct flowi6 *fl6, const struct sk_buff *skb,
 | 
				
			||||||
 | 
							       struct flow_keys *hkeys);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct dst_entry *icmp6_dst_alloc(struct net_device *dev, struct flowi6 *fl6);
 | 
					struct dst_entry *icmp6_dst_alloc(struct net_device *dev, struct flowi6 *fl6);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -266,4 +267,5 @@ static inline bool rt6_duplicate_nexthop(struct rt6_info *a, struct rt6_info *b)
 | 
				
			||||||
	       ipv6_addr_equal(&a->rt6i_gateway, &b->rt6i_gateway) &&
 | 
						       ipv6_addr_equal(&a->rt6i_gateway, &b->rt6i_gateway) &&
 | 
				
			||||||
	       !lwtunnel_cmp_encap(a->dst.lwtstate, b->dst.lwtstate);
 | 
						       !lwtunnel_cmp_encap(a->dst.lwtstate, b->dst.lwtstate);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -293,6 +293,13 @@ static inline unsigned int fib4_rules_seq_read(struct net *net)
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline bool fib4_rules_early_flow_dissect(struct net *net,
 | 
				
			||||||
 | 
											 struct sk_buff *skb,
 | 
				
			||||||
 | 
											 struct flowi4 *fl4,
 | 
				
			||||||
 | 
											 struct flow_keys *flkeys)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
#else /* CONFIG_IP_MULTIPLE_TABLES */
 | 
					#else /* CONFIG_IP_MULTIPLE_TABLES */
 | 
				
			||||||
int __net_init fib4_rules_init(struct net *net);
 | 
					int __net_init fib4_rules_init(struct net *net);
 | 
				
			||||||
void __net_exit fib4_rules_exit(struct net *net);
 | 
					void __net_exit fib4_rules_exit(struct net *net);
 | 
				
			||||||
| 
						 | 
					@ -341,6 +348,24 @@ bool fib4_rule_default(const struct fib_rule *rule);
 | 
				
			||||||
int fib4_rules_dump(struct net *net, struct notifier_block *nb);
 | 
					int fib4_rules_dump(struct net *net, struct notifier_block *nb);
 | 
				
			||||||
unsigned int fib4_rules_seq_read(struct net *net);
 | 
					unsigned int fib4_rules_seq_read(struct net *net);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline bool fib4_rules_early_flow_dissect(struct net *net,
 | 
				
			||||||
 | 
											 struct sk_buff *skb,
 | 
				
			||||||
 | 
											 struct flowi4 *fl4,
 | 
				
			||||||
 | 
											 struct flow_keys *flkeys)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned int flag = FLOW_DISSECTOR_F_STOP_AT_ENCAP;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!net->ipv4.fib_rules_require_fldissect)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						skb_flow_dissect_flow_keys(skb, flkeys, flag);
 | 
				
			||||||
 | 
						fl4->fl4_sport = flkeys->ports.src;
 | 
				
			||||||
 | 
						fl4->fl4_dport = flkeys->ports.dst;
 | 
				
			||||||
 | 
						fl4->flowi4_proto = flkeys->basic.ip_proto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif /* CONFIG_IP_MULTIPLE_TABLES */
 | 
					#endif /* CONFIG_IP_MULTIPLE_TABLES */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Exported by fib_frontend.c */
 | 
					/* Exported by fib_frontend.c */
 | 
				
			||||||
| 
						 | 
					@ -371,7 +396,7 @@ int fib_sync_up(struct net_device *dev, unsigned int nh_flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_IP_ROUTE_MULTIPATH
 | 
					#ifdef CONFIG_IP_ROUTE_MULTIPATH
 | 
				
			||||||
int fib_multipath_hash(const struct fib_info *fi, const struct flowi4 *fl4,
 | 
					int fib_multipath_hash(const struct fib_info *fi, const struct flowi4 *fl4,
 | 
				
			||||||
		       const struct sk_buff *skb);
 | 
							       const struct sk_buff *skb, struct flow_keys *flkeys);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
void fib_select_multipath(struct fib_result *res, int hash);
 | 
					void fib_select_multipath(struct fib_result *res, int hash);
 | 
				
			||||||
void fib_select_path(struct net *net, struct fib_result *res,
 | 
					void fib_select_path(struct net *net, struct fib_result *res,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -52,6 +52,7 @@ struct netns_ipv4 {
 | 
				
			||||||
#ifdef CONFIG_IP_MULTIPLE_TABLES
 | 
					#ifdef CONFIG_IP_MULTIPLE_TABLES
 | 
				
			||||||
	struct fib_rules_ops	*rules_ops;
 | 
						struct fib_rules_ops	*rules_ops;
 | 
				
			||||||
	bool			fib_has_custom_rules;
 | 
						bool			fib_has_custom_rules;
 | 
				
			||||||
 | 
						unsigned int		fib_rules_require_fldissect;
 | 
				
			||||||
	struct fib_table __rcu	*fib_main;
 | 
						struct fib_table __rcu	*fib_main;
 | 
				
			||||||
	struct fib_table __rcu	*fib_default;
 | 
						struct fib_table __rcu	*fib_default;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -71,6 +71,7 @@ struct netns_ipv6 {
 | 
				
			||||||
	unsigned int		 ip6_rt_gc_expire;
 | 
						unsigned int		 ip6_rt_gc_expire;
 | 
				
			||||||
	unsigned long		 ip6_rt_last_gc;
 | 
						unsigned long		 ip6_rt_last_gc;
 | 
				
			||||||
#ifdef CONFIG_IPV6_MULTIPLE_TABLES
 | 
					#ifdef CONFIG_IPV6_MULTIPLE_TABLES
 | 
				
			||||||
 | 
						unsigned int		fib6_rules_require_fldissect;
 | 
				
			||||||
	bool			fib6_has_custom_rules;
 | 
						bool			fib6_has_custom_rules;
 | 
				
			||||||
	struct rt6_info         *ip6_prohibit_entry;
 | 
						struct rt6_info         *ip6_prohibit_entry;
 | 
				
			||||||
	struct rt6_info         *ip6_blk_hole_entry;
 | 
						struct rt6_info         *ip6_blk_hole_entry;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -35,6 +35,11 @@ struct fib_rule_uid_range {
 | 
				
			||||||
	__u32		end;
 | 
						__u32		end;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct fib_rule_port_range {
 | 
				
			||||||
 | 
						__u16		start;
 | 
				
			||||||
 | 
						__u16		end;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum {
 | 
					enum {
 | 
				
			||||||
	FRA_UNSPEC,
 | 
						FRA_UNSPEC,
 | 
				
			||||||
	FRA_DST,	/* destination address */
 | 
						FRA_DST,	/* destination address */
 | 
				
			||||||
| 
						 | 
					@ -59,6 +64,9 @@ enum {
 | 
				
			||||||
	FRA_L3MDEV,	/* iif or oif is l3mdev goto its table */
 | 
						FRA_L3MDEV,	/* iif or oif is l3mdev goto its table */
 | 
				
			||||||
	FRA_UID_RANGE,	/* UID range */
 | 
						FRA_UID_RANGE,	/* UID range */
 | 
				
			||||||
	FRA_PROTOCOL,   /* Originator of the rule */
 | 
						FRA_PROTOCOL,   /* Originator of the rule */
 | 
				
			||||||
 | 
						FRA_IP_PROTO,	/* ip proto */
 | 
				
			||||||
 | 
						FRA_SPORT_RANGE, /* sport */
 | 
				
			||||||
 | 
						FRA_DPORT_RANGE, /* dport */
 | 
				
			||||||
	__FRA_MAX
 | 
						__FRA_MAX
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,6 +33,10 @@ bool fib_rule_matchall(const struct fib_rule *rule)
 | 
				
			||||||
	if (!uid_eq(rule->uid_range.start, fib_kuid_range_unset.start) ||
 | 
						if (!uid_eq(rule->uid_range.start, fib_kuid_range_unset.start) ||
 | 
				
			||||||
	    !uid_eq(rule->uid_range.end, fib_kuid_range_unset.end))
 | 
						    !uid_eq(rule->uid_range.end, fib_kuid_range_unset.end))
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
 | 
						if (fib_rule_port_range_set(&rule->sport_range))
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						if (fib_rule_port_range_set(&rule->dport_range))
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL_GPL(fib_rule_matchall);
 | 
					EXPORT_SYMBOL_GPL(fib_rule_matchall);
 | 
				
			||||||
| 
						 | 
					@ -221,6 +225,26 @@ static int nla_put_uid_range(struct sk_buff *skb, struct fib_kuid_range *range)
 | 
				
			||||||
	return nla_put(skb, FRA_UID_RANGE, sizeof(out), &out);
 | 
						return nla_put(skb, FRA_UID_RANGE, sizeof(out), &out);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int nla_get_port_range(struct nlattr *pattr,
 | 
				
			||||||
 | 
								      struct fib_rule_port_range *port_range)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const struct fib_rule_port_range *pr = nla_data(pattr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!fib_rule_port_range_valid(pr))
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						port_range->start = pr->start;
 | 
				
			||||||
 | 
						port_range->end = pr->end;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int nla_put_port_range(struct sk_buff *skb, int attrtype,
 | 
				
			||||||
 | 
								      struct fib_rule_port_range *range)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return nla_put(skb, attrtype, sizeof(*range), range);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops,
 | 
					static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops,
 | 
				
			||||||
			  struct flowi *fl, int flags,
 | 
								  struct flowi *fl, int flags,
 | 
				
			||||||
			  struct fib_lookup_arg *arg)
 | 
								  struct fib_lookup_arg *arg)
 | 
				
			||||||
| 
						 | 
					@ -425,6 +449,17 @@ static int rule_exists(struct fib_rules_ops *ops, struct fib_rule_hdr *frh,
 | 
				
			||||||
		    !uid_eq(r->uid_range.end, rule->uid_range.end))
 | 
							    !uid_eq(r->uid_range.end, rule->uid_range.end))
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (r->ip_proto != rule->ip_proto)
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!fib_rule_port_range_compare(&r->sport_range,
 | 
				
			||||||
 | 
											 &rule->sport_range))
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!fib_rule_port_range_compare(&r->dport_range,
 | 
				
			||||||
 | 
											 &rule->dport_range))
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!ops->compare(r, frh, tb))
 | 
							if (!ops->compare(r, frh, tb))
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
		return 1;
 | 
							return 1;
 | 
				
			||||||
| 
						 | 
					@ -569,6 +604,23 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh,
 | 
				
			||||||
		rule->uid_range = fib_kuid_range_unset;
 | 
							rule->uid_range = fib_kuid_range_unset;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (tb[FRA_IP_PROTO])
 | 
				
			||||||
 | 
							rule->ip_proto = nla_get_u8(tb[FRA_IP_PROTO]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (tb[FRA_SPORT_RANGE]) {
 | 
				
			||||||
 | 
							err = nla_get_port_range(tb[FRA_SPORT_RANGE],
 | 
				
			||||||
 | 
										 &rule->sport_range);
 | 
				
			||||||
 | 
							if (err)
 | 
				
			||||||
 | 
								goto errout_free;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (tb[FRA_DPORT_RANGE]) {
 | 
				
			||||||
 | 
							err = nla_get_port_range(tb[FRA_DPORT_RANGE],
 | 
				
			||||||
 | 
										 &rule->dport_range);
 | 
				
			||||||
 | 
							if (err)
 | 
				
			||||||
 | 
								goto errout_free;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ((nlh->nlmsg_flags & NLM_F_EXCL) &&
 | 
						if ((nlh->nlmsg_flags & NLM_F_EXCL) &&
 | 
				
			||||||
	    rule_exists(ops, frh, tb, rule)) {
 | 
						    rule_exists(ops, frh, tb, rule)) {
 | 
				
			||||||
		err = -EEXIST;
 | 
							err = -EEXIST;
 | 
				
			||||||
| 
						 | 
					@ -634,6 +686,8 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh,
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct net *net = sock_net(skb->sk);
 | 
						struct net *net = sock_net(skb->sk);
 | 
				
			||||||
	struct fib_rule_hdr *frh = nlmsg_data(nlh);
 | 
						struct fib_rule_hdr *frh = nlmsg_data(nlh);
 | 
				
			||||||
 | 
						struct fib_rule_port_range sprange = {0, 0};
 | 
				
			||||||
 | 
						struct fib_rule_port_range dprange = {0, 0};
 | 
				
			||||||
	struct fib_rules_ops *ops = NULL;
 | 
						struct fib_rules_ops *ops = NULL;
 | 
				
			||||||
	struct fib_rule *rule, *r;
 | 
						struct fib_rule *rule, *r;
 | 
				
			||||||
	struct nlattr *tb[FRA_MAX+1];
 | 
						struct nlattr *tb[FRA_MAX+1];
 | 
				
			||||||
| 
						 | 
					@ -667,6 +721,20 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh,
 | 
				
			||||||
		range = fib_kuid_range_unset;
 | 
							range = fib_kuid_range_unset;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (tb[FRA_SPORT_RANGE]) {
 | 
				
			||||||
 | 
							err = nla_get_port_range(tb[FRA_SPORT_RANGE],
 | 
				
			||||||
 | 
										 &sprange);
 | 
				
			||||||
 | 
							if (err)
 | 
				
			||||||
 | 
								goto errout;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (tb[FRA_DPORT_RANGE]) {
 | 
				
			||||||
 | 
							err = nla_get_port_range(tb[FRA_DPORT_RANGE],
 | 
				
			||||||
 | 
										 &dprange);
 | 
				
			||||||
 | 
							if (err)
 | 
				
			||||||
 | 
								goto errout;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	list_for_each_entry(rule, &ops->rules_list, list) {
 | 
						list_for_each_entry(rule, &ops->rules_list, list) {
 | 
				
			||||||
		if (tb[FRA_PROTOCOL] &&
 | 
							if (tb[FRA_PROTOCOL] &&
 | 
				
			||||||
		    (rule->proto != nla_get_u8(tb[FRA_PROTOCOL])))
 | 
							    (rule->proto != nla_get_u8(tb[FRA_PROTOCOL])))
 | 
				
			||||||
| 
						 | 
					@ -712,6 +780,18 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh,
 | 
				
			||||||
		     !uid_eq(rule->uid_range.end, range.end)))
 | 
							     !uid_eq(rule->uid_range.end, range.end)))
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (tb[FRA_IP_PROTO] &&
 | 
				
			||||||
 | 
							    (rule->ip_proto != nla_get_u8(tb[FRA_IP_PROTO])))
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (fib_rule_port_range_set(&sprange) &&
 | 
				
			||||||
 | 
							    !fib_rule_port_range_compare(&rule->sport_range, &sprange))
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (fib_rule_port_range_set(&dprange) &&
 | 
				
			||||||
 | 
							    !fib_rule_port_range_compare(&rule->dport_range, &dprange))
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!ops->compare(rule, frh, tb))
 | 
							if (!ops->compare(rule, frh, tb))
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -790,7 +870,10 @@ static inline size_t fib_rule_nlmsg_size(struct fib_rules_ops *ops,
 | 
				
			||||||
			 + nla_total_size(4) /* FRA_FWMASK */
 | 
								 + nla_total_size(4) /* FRA_FWMASK */
 | 
				
			||||||
			 + nla_total_size_64bit(8) /* FRA_TUN_ID */
 | 
								 + nla_total_size_64bit(8) /* FRA_TUN_ID */
 | 
				
			||||||
			 + nla_total_size(sizeof(struct fib_kuid_range))
 | 
								 + nla_total_size(sizeof(struct fib_kuid_range))
 | 
				
			||||||
			 + nla_total_size(1); /* FRA_PROTOCOL */
 | 
								 + nla_total_size(1) /* FRA_PROTOCOL */
 | 
				
			||||||
 | 
								 + nla_total_size(1) /* FRA_IP_PROTO */
 | 
				
			||||||
 | 
								 + nla_total_size(sizeof(struct fib_rule_port_range)) /* FRA_SPORT_RANGE */
 | 
				
			||||||
 | 
								 + nla_total_size(sizeof(struct fib_rule_port_range)); /* FRA_DPORT_RANGE */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (ops->nlmsg_payload)
 | 
						if (ops->nlmsg_payload)
 | 
				
			||||||
		payload += ops->nlmsg_payload(rule);
 | 
							payload += ops->nlmsg_payload(rule);
 | 
				
			||||||
| 
						 | 
					@ -855,7 +938,12 @@ static int fib_nl_fill_rule(struct sk_buff *skb, struct fib_rule *rule,
 | 
				
			||||||
	    (rule->l3mdev &&
 | 
						    (rule->l3mdev &&
 | 
				
			||||||
	     nla_put_u8(skb, FRA_L3MDEV, rule->l3mdev)) ||
 | 
						     nla_put_u8(skb, FRA_L3MDEV, rule->l3mdev)) ||
 | 
				
			||||||
	    (uid_range_set(&rule->uid_range) &&
 | 
						    (uid_range_set(&rule->uid_range) &&
 | 
				
			||||||
	     nla_put_uid_range(skb, &rule->uid_range)))
 | 
						     nla_put_uid_range(skb, &rule->uid_range)) ||
 | 
				
			||||||
 | 
						    (fib_rule_port_range_set(&rule->sport_range) &&
 | 
				
			||||||
 | 
						     nla_put_port_range(skb, FRA_SPORT_RANGE, &rule->sport_range)) ||
 | 
				
			||||||
 | 
						    (fib_rule_port_range_set(&rule->dport_range) &&
 | 
				
			||||||
 | 
						     nla_put_port_range(skb, FRA_DPORT_RANGE, &rule->dport_range)) ||
 | 
				
			||||||
 | 
						    (rule->ip_proto && nla_put_u8(skb, FRA_IP_PROTO, rule->ip_proto)))
 | 
				
			||||||
		goto nla_put_failure;
 | 
							goto nla_put_failure;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (rule->suppress_ifgroup != -1) {
 | 
						if (rule->suppress_ifgroup != -1) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -182,6 +182,17 @@ static int fib4_rule_match(struct fib_rule *rule, struct flowi *fl, int flags)
 | 
				
			||||||
	if (r->tos && (r->tos != fl4->flowi4_tos))
 | 
						if (r->tos && (r->tos != fl4->flowi4_tos))
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (rule->ip_proto && (rule->ip_proto != fl4->flowi4_proto))
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (fib_rule_port_range_set(&rule->sport_range) &&
 | 
				
			||||||
 | 
						    !fib_rule_port_inrange(&rule->sport_range, fl4->fl4_sport))
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (fib_rule_port_range_set(&rule->dport_range) &&
 | 
				
			||||||
 | 
						    !fib_rule_port_inrange(&rule->dport_range, fl4->fl4_dport))
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 1;
 | 
						return 1;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -244,6 +255,9 @@ static int fib4_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (fib_rule_requires_fldissect(rule))
 | 
				
			||||||
 | 
							net->ipv4.fib_rules_require_fldissect++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rule4->src_len = frh->src_len;
 | 
						rule4->src_len = frh->src_len;
 | 
				
			||||||
	rule4->srcmask = inet_make_mask(rule4->src_len);
 | 
						rule4->srcmask = inet_make_mask(rule4->src_len);
 | 
				
			||||||
	rule4->dst_len = frh->dst_len;
 | 
						rule4->dst_len = frh->dst_len;
 | 
				
			||||||
| 
						 | 
					@ -272,6 +286,10 @@ static int fib4_rule_delete(struct fib_rule *rule)
 | 
				
			||||||
		net->ipv4.fib_num_tclassid_users--;
 | 
							net->ipv4.fib_num_tclassid_users--;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
	net->ipv4.fib_has_custom_rules = true;
 | 
						net->ipv4.fib_has_custom_rules = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (net->ipv4.fib_rules_require_fldissect &&
 | 
				
			||||||
 | 
						    fib_rule_requires_fldissect(rule))
 | 
				
			||||||
 | 
							net->ipv4.fib_rules_require_fldissect--;
 | 
				
			||||||
errout:
 | 
					errout:
 | 
				
			||||||
	return err;
 | 
						return err;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -389,6 +407,7 @@ int __net_init fib4_rules_init(struct net *net)
 | 
				
			||||||
		goto fail;
 | 
							goto fail;
 | 
				
			||||||
	net->ipv4.rules_ops = ops;
 | 
						net->ipv4.rules_ops = ops;
 | 
				
			||||||
	net->ipv4.fib_has_custom_rules = false;
 | 
						net->ipv4.fib_has_custom_rules = false;
 | 
				
			||||||
 | 
						net->ipv4.fib_rules_require_fldissect = 0;
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fail:
 | 
					fail:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1770,7 +1770,7 @@ void fib_select_path(struct net *net, struct fib_result *res,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_IP_ROUTE_MULTIPATH
 | 
					#ifdef CONFIG_IP_ROUTE_MULTIPATH
 | 
				
			||||||
	if (res->fi->fib_nhs > 1) {
 | 
						if (res->fi->fib_nhs > 1) {
 | 
				
			||||||
		int h = fib_multipath_hash(res->fi, fl4, skb);
 | 
							int h = fib_multipath_hash(res->fi, fl4, skb, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		fib_select_multipath(res, h);
 | 
							fib_select_multipath(res, h);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1783,7 +1783,7 @@ static void ip_multipath_l3_keys(const struct sk_buff *skb,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* if skb is set it will be used and fl4 can be NULL */
 | 
					/* if skb is set it will be used and fl4 can be NULL */
 | 
				
			||||||
int fib_multipath_hash(const struct fib_info *fi, const struct flowi4 *fl4,
 | 
					int fib_multipath_hash(const struct fib_info *fi, const struct flowi4 *fl4,
 | 
				
			||||||
		       const struct sk_buff *skb)
 | 
							       const struct sk_buff *skb, struct flow_keys *flkeys)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct net *net = fi->fib_net;
 | 
						struct net *net = fi->fib_net;
 | 
				
			||||||
	struct flow_keys hash_keys;
 | 
						struct flow_keys hash_keys;
 | 
				
			||||||
| 
						 | 
					@ -1810,14 +1810,23 @@ int fib_multipath_hash(const struct fib_info *fi, const struct flowi4 *fl4,
 | 
				
			||||||
			if (skb->l4_hash)
 | 
								if (skb->l4_hash)
 | 
				
			||||||
				return skb_get_hash_raw(skb) >> 1;
 | 
									return skb_get_hash_raw(skb) >> 1;
 | 
				
			||||||
			memset(&hash_keys, 0, sizeof(hash_keys));
 | 
								memset(&hash_keys, 0, sizeof(hash_keys));
 | 
				
			||||||
			skb_flow_dissect_flow_keys(skb, &keys, flag);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (flkeys) {
 | 
				
			||||||
 | 
									hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
 | 
				
			||||||
 | 
									hash_keys.addrs.v4addrs.src = flkeys->addrs.v4addrs.src;
 | 
				
			||||||
 | 
									hash_keys.addrs.v4addrs.dst = flkeys->addrs.v4addrs.dst;
 | 
				
			||||||
 | 
									hash_keys.ports.src = flkeys->ports.src;
 | 
				
			||||||
 | 
									hash_keys.ports.dst = flkeys->ports.dst;
 | 
				
			||||||
 | 
									hash_keys.basic.ip_proto = flkeys->basic.ip_proto;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									skb_flow_dissect_flow_keys(skb, &keys, flag);
 | 
				
			||||||
				hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
 | 
									hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
 | 
				
			||||||
				hash_keys.addrs.v4addrs.src = keys.addrs.v4addrs.src;
 | 
									hash_keys.addrs.v4addrs.src = keys.addrs.v4addrs.src;
 | 
				
			||||||
				hash_keys.addrs.v4addrs.dst = keys.addrs.v4addrs.dst;
 | 
									hash_keys.addrs.v4addrs.dst = keys.addrs.v4addrs.dst;
 | 
				
			||||||
				hash_keys.ports.src = keys.ports.src;
 | 
									hash_keys.ports.src = keys.ports.src;
 | 
				
			||||||
				hash_keys.ports.dst = keys.ports.dst;
 | 
									hash_keys.ports.dst = keys.ports.dst;
 | 
				
			||||||
				hash_keys.basic.ip_proto = keys.basic.ip_proto;
 | 
									hash_keys.basic.ip_proto = keys.basic.ip_proto;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			memset(&hash_keys, 0, sizeof(hash_keys));
 | 
								memset(&hash_keys, 0, sizeof(hash_keys));
 | 
				
			||||||
			hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
 | 
								hash_keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
 | 
				
			||||||
| 
						 | 
					@ -1838,11 +1847,12 @@ int fib_multipath_hash(const struct fib_info *fi, const struct flowi4 *fl4,
 | 
				
			||||||
static int ip_mkroute_input(struct sk_buff *skb,
 | 
					static int ip_mkroute_input(struct sk_buff *skb,
 | 
				
			||||||
			    struct fib_result *res,
 | 
								    struct fib_result *res,
 | 
				
			||||||
			    struct in_device *in_dev,
 | 
								    struct in_device *in_dev,
 | 
				
			||||||
			    __be32 daddr, __be32 saddr, u32 tos)
 | 
								    __be32 daddr, __be32 saddr, u32 tos,
 | 
				
			||||||
 | 
								    struct flow_keys *hkeys)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
#ifdef CONFIG_IP_ROUTE_MULTIPATH
 | 
					#ifdef CONFIG_IP_ROUTE_MULTIPATH
 | 
				
			||||||
	if (res->fi && res->fi->fib_nhs > 1) {
 | 
						if (res->fi && res->fi->fib_nhs > 1) {
 | 
				
			||||||
		int h = fib_multipath_hash(res->fi, NULL, skb);
 | 
							int h = fib_multipath_hash(res->fi, NULL, skb, hkeys);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		fib_select_multipath(res, h);
 | 
							fib_select_multipath(res, h);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -1868,13 +1878,14 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
 | 
				
			||||||
			       struct fib_result *res)
 | 
								       struct fib_result *res)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct in_device *in_dev = __in_dev_get_rcu(dev);
 | 
						struct in_device *in_dev = __in_dev_get_rcu(dev);
 | 
				
			||||||
 | 
						struct flow_keys *flkeys = NULL, _flkeys;
 | 
				
			||||||
 | 
						struct net    *net = dev_net(dev);
 | 
				
			||||||
	struct ip_tunnel_info *tun_info;
 | 
						struct ip_tunnel_info *tun_info;
 | 
				
			||||||
	struct flowi4	fl4;
 | 
						int		err = -EINVAL;
 | 
				
			||||||
	unsigned int	flags = 0;
 | 
						unsigned int	flags = 0;
 | 
				
			||||||
	u32		itag = 0;
 | 
						u32		itag = 0;
 | 
				
			||||||
	struct rtable	*rth;
 | 
						struct rtable	*rth;
 | 
				
			||||||
	int		err = -EINVAL;
 | 
						struct flowi4	fl4;
 | 
				
			||||||
	struct net    *net = dev_net(dev);
 | 
					 | 
				
			||||||
	bool do_cache;
 | 
						bool do_cache;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* IP on this device is disabled. */
 | 
						/* IP on this device is disabled. */
 | 
				
			||||||
| 
						 | 
					@ -1933,6 +1944,10 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
 | 
				
			||||||
	fl4.daddr = daddr;
 | 
						fl4.daddr = daddr;
 | 
				
			||||||
	fl4.saddr = saddr;
 | 
						fl4.saddr = saddr;
 | 
				
			||||||
	fl4.flowi4_uid = sock_net_uid(net, NULL);
 | 
						fl4.flowi4_uid = sock_net_uid(net, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (fib4_rules_early_flow_dissect(net, skb, &fl4, &_flkeys))
 | 
				
			||||||
 | 
							flkeys = &_flkeys;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = fib_lookup(net, &fl4, res, 0);
 | 
						err = fib_lookup(net, &fl4, res, 0);
 | 
				
			||||||
	if (err != 0) {
 | 
						if (err != 0) {
 | 
				
			||||||
		if (!IN_DEV_FORWARD(in_dev))
 | 
							if (!IN_DEV_FORWARD(in_dev))
 | 
				
			||||||
| 
						 | 
					@ -1958,7 +1973,7 @@ static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
 | 
				
			||||||
	if (res->type != RTN_UNICAST)
 | 
						if (res->type != RTN_UNICAST)
 | 
				
			||||||
		goto martian_destination;
 | 
							goto martian_destination;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = ip_mkroute_input(skb, res, in_dev, daddr, saddr, tos);
 | 
						err = ip_mkroute_input(skb, res, in_dev, daddr, saddr, tos, flkeys);
 | 
				
			||||||
out:	return err;
 | 
					out:	return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
brd_input:
 | 
					brd_input:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -223,6 +223,17 @@ static int fib6_rule_match(struct fib_rule *rule, struct flowi *fl, int flags)
 | 
				
			||||||
	if (r->tclass && r->tclass != ip6_tclass(fl6->flowlabel))
 | 
						if (r->tclass && r->tclass != ip6_tclass(fl6->flowlabel))
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (rule->ip_proto && (rule->ip_proto != fl6->flowi6_proto))
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (fib_rule_port_range_set(&rule->sport_range) &&
 | 
				
			||||||
 | 
						    !fib_rule_port_inrange(&rule->sport_range, fl6->fl6_sport))
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (fib_rule_port_range_set(&rule->dport_range) &&
 | 
				
			||||||
 | 
						    !fib_rule_port_inrange(&rule->dport_range, fl6->fl6_dport))
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 1;
 | 
						return 1;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -258,12 +269,26 @@ static int fib6_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
 | 
				
			||||||
	rule6->dst.plen = frh->dst_len;
 | 
						rule6->dst.plen = frh->dst_len;
 | 
				
			||||||
	rule6->tclass = frh->tos;
 | 
						rule6->tclass = frh->tos;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (fib_rule_requires_fldissect(rule))
 | 
				
			||||||
 | 
							net->ipv6.fib6_rules_require_fldissect++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	net->ipv6.fib6_has_custom_rules = true;
 | 
						net->ipv6.fib6_has_custom_rules = true;
 | 
				
			||||||
	err = 0;
 | 
						err = 0;
 | 
				
			||||||
errout:
 | 
					errout:
 | 
				
			||||||
	return err;
 | 
						return err;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int fib6_rule_delete(struct fib_rule *rule)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct net *net = rule->fr_net;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (net->ipv6.fib6_rules_require_fldissect &&
 | 
				
			||||||
 | 
						    fib_rule_requires_fldissect(rule))
 | 
				
			||||||
 | 
							net->ipv6.fib6_rules_require_fldissect--;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int fib6_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh,
 | 
					static int fib6_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh,
 | 
				
			||||||
			     struct nlattr **tb)
 | 
								     struct nlattr **tb)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -323,6 +348,7 @@ static const struct fib_rules_ops __net_initconst fib6_rules_ops_template = {
 | 
				
			||||||
	.match			= fib6_rule_match,
 | 
						.match			= fib6_rule_match,
 | 
				
			||||||
	.suppress		= fib6_rule_suppress,
 | 
						.suppress		= fib6_rule_suppress,
 | 
				
			||||||
	.configure		= fib6_rule_configure,
 | 
						.configure		= fib6_rule_configure,
 | 
				
			||||||
 | 
						.delete			= fib6_rule_delete,
 | 
				
			||||||
	.compare		= fib6_rule_compare,
 | 
						.compare		= fib6_rule_compare,
 | 
				
			||||||
	.fill			= fib6_rule_fill,
 | 
						.fill			= fib6_rule_fill,
 | 
				
			||||||
	.nlmsg_payload		= fib6_rule_nlmsg_payload,
 | 
						.nlmsg_payload		= fib6_rule_nlmsg_payload,
 | 
				
			||||||
| 
						 | 
					@ -350,6 +376,7 @@ static int __net_init fib6_rules_net_init(struct net *net)
 | 
				
			||||||
		goto out_fib6_rules_ops;
 | 
							goto out_fib6_rules_ops;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	net->ipv6.fib6_rules_ops = ops;
 | 
						net->ipv6.fib6_rules_ops = ops;
 | 
				
			||||||
 | 
						net->ipv6.fib6_rules_require_fldissect = 0;
 | 
				
			||||||
out:
 | 
					out:
 | 
				
			||||||
	return err;
 | 
						return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -522,7 +522,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
 | 
				
			||||||
	fl6.fl6_icmp_type = type;
 | 
						fl6.fl6_icmp_type = type;
 | 
				
			||||||
	fl6.fl6_icmp_code = code;
 | 
						fl6.fl6_icmp_code = code;
 | 
				
			||||||
	fl6.flowi6_uid = sock_net_uid(net, NULL);
 | 
						fl6.flowi6_uid = sock_net_uid(net, NULL);
 | 
				
			||||||
	fl6.mp_hash = rt6_multipath_hash(&fl6, skb);
 | 
						fl6.mp_hash = rt6_multipath_hash(&fl6, skb, NULL);
 | 
				
			||||||
	security_skb_classify_flow(skb, flowi6_to_flowi(&fl6));
 | 
						security_skb_classify_flow(skb, flowi6_to_flowi(&fl6));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sk = icmpv6_xmit_lock(net);
 | 
						sk = icmpv6_xmit_lock(net);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -460,7 +460,7 @@ static struct rt6_info *rt6_multipath_select(struct rt6_info *match,
 | 
				
			||||||
	 * case it will always be non-zero. Otherwise now is the time to do it.
 | 
						 * case it will always be non-zero. Otherwise now is the time to do it.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (!fl6->mp_hash)
 | 
						if (!fl6->mp_hash)
 | 
				
			||||||
		fl6->mp_hash = rt6_multipath_hash(fl6, NULL);
 | 
							fl6->mp_hash = rt6_multipath_hash(fl6, NULL, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (fl6->mp_hash <= atomic_read(&match->rt6i_nh_upper_bound))
 | 
						if (fl6->mp_hash <= atomic_read(&match->rt6i_nh_upper_bound))
 | 
				
			||||||
		return match;
 | 
							return match;
 | 
				
			||||||
| 
						 | 
					@ -1786,10 +1786,12 @@ struct dst_entry *ip6_route_input_lookup(struct net *net,
 | 
				
			||||||
EXPORT_SYMBOL_GPL(ip6_route_input_lookup);
 | 
					EXPORT_SYMBOL_GPL(ip6_route_input_lookup);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void ip6_multipath_l3_keys(const struct sk_buff *skb,
 | 
					static void ip6_multipath_l3_keys(const struct sk_buff *skb,
 | 
				
			||||||
				  struct flow_keys *keys)
 | 
									  struct flow_keys *keys,
 | 
				
			||||||
 | 
									  struct flow_keys *flkeys)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	const struct ipv6hdr *outer_iph = ipv6_hdr(skb);
 | 
						const struct ipv6hdr *outer_iph = ipv6_hdr(skb);
 | 
				
			||||||
	const struct ipv6hdr *key_iph = outer_iph;
 | 
						const struct ipv6hdr *key_iph = outer_iph;
 | 
				
			||||||
 | 
						struct flow_keys *_flkeys = flkeys;
 | 
				
			||||||
	const struct ipv6hdr *inner_iph;
 | 
						const struct ipv6hdr *inner_iph;
 | 
				
			||||||
	const struct icmp6hdr *icmph;
 | 
						const struct icmp6hdr *icmph;
 | 
				
			||||||
	struct ipv6hdr _inner_iph;
 | 
						struct ipv6hdr _inner_iph;
 | 
				
			||||||
| 
						 | 
					@ -1811,22 +1813,31 @@ static void ip6_multipath_l3_keys(const struct sk_buff *skb,
 | 
				
			||||||
		goto out;
 | 
							goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	key_iph = inner_iph;
 | 
						key_iph = inner_iph;
 | 
				
			||||||
 | 
						_flkeys = NULL;
 | 
				
			||||||
out:
 | 
					out:
 | 
				
			||||||
	memset(keys, 0, sizeof(*keys));
 | 
						memset(keys, 0, sizeof(*keys));
 | 
				
			||||||
	keys->control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
 | 
						keys->control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
 | 
				
			||||||
 | 
						if (_flkeys) {
 | 
				
			||||||
 | 
							keys->addrs.v6addrs.src = _flkeys->addrs.v6addrs.src;
 | 
				
			||||||
 | 
							keys->addrs.v6addrs.dst = _flkeys->addrs.v6addrs.dst;
 | 
				
			||||||
 | 
							keys->tags.flow_label = _flkeys->tags.flow_label;
 | 
				
			||||||
 | 
							keys->basic.ip_proto = _flkeys->basic.ip_proto;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
		keys->addrs.v6addrs.src = key_iph->saddr;
 | 
							keys->addrs.v6addrs.src = key_iph->saddr;
 | 
				
			||||||
		keys->addrs.v6addrs.dst = key_iph->daddr;
 | 
							keys->addrs.v6addrs.dst = key_iph->daddr;
 | 
				
			||||||
		keys->tags.flow_label = ip6_flowinfo(key_iph);
 | 
							keys->tags.flow_label = ip6_flowinfo(key_iph);
 | 
				
			||||||
		keys->basic.ip_proto = key_iph->nexthdr;
 | 
							keys->basic.ip_proto = key_iph->nexthdr;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* if skb is set it will be used and fl6 can be NULL */
 | 
					/* if skb is set it will be used and fl6 can be NULL */
 | 
				
			||||||
u32 rt6_multipath_hash(const struct flowi6 *fl6, const struct sk_buff *skb)
 | 
					u32 rt6_multipath_hash(const struct flowi6 *fl6, const struct sk_buff *skb,
 | 
				
			||||||
 | 
							       struct flow_keys *flkeys)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct flow_keys hash_keys;
 | 
						struct flow_keys hash_keys;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (skb) {
 | 
						if (skb) {
 | 
				
			||||||
		ip6_multipath_l3_keys(skb, &hash_keys);
 | 
							ip6_multipath_l3_keys(skb, &hash_keys, flkeys);
 | 
				
			||||||
		return flow_hash_from_keys(&hash_keys) >> 1;
 | 
							return flow_hash_from_keys(&hash_keys) >> 1;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1847,12 +1858,17 @@ void ip6_route_input(struct sk_buff *skb)
 | 
				
			||||||
		.flowi6_mark = skb->mark,
 | 
							.flowi6_mark = skb->mark,
 | 
				
			||||||
		.flowi6_proto = iph->nexthdr,
 | 
							.flowi6_proto = iph->nexthdr,
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
						struct flow_keys *flkeys = NULL, _flkeys;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tun_info = skb_tunnel_info(skb);
 | 
						tun_info = skb_tunnel_info(skb);
 | 
				
			||||||
	if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX))
 | 
						if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX))
 | 
				
			||||||
		fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id;
 | 
							fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (fib6_rules_early_flow_dissect(net, skb, &fl6, &_flkeys))
 | 
				
			||||||
 | 
							flkeys = &_flkeys;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (unlikely(fl6.flowi6_proto == IPPROTO_ICMPV6))
 | 
						if (unlikely(fl6.flowi6_proto == IPPROTO_ICMPV6))
 | 
				
			||||||
		fl6.mp_hash = rt6_multipath_hash(&fl6, skb);
 | 
							fl6.mp_hash = rt6_multipath_hash(&fl6, skb, flkeys);
 | 
				
			||||||
	skb_dst_drop(skb);
 | 
						skb_dst_drop(skb);
 | 
				
			||||||
	skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
 | 
						skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue