forked from mirrors/linux
		
	net: fib_rules: support for match on ip_proto, sport and dport
uapi for ip_proto, sport and dport range match in fib rules. Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com> Acked-by: Nikolay Aleksandrov <nikolay@cumulusnetworks.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									2927499157
								
							
						
					
					
						commit
						bfff486265
					
				
					 3 changed files with 133 additions and 3 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 *); | ||||||
|  |  | ||||||
|  | @ -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) { | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Roopa Prabhu
						Roopa Prabhu