mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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			l3mdev;
 | 
			
		||||
	u8                      proto;
 | 
			
		||||
	/* 1 byte hole, try to use */
 | 
			
		||||
	u8			ip_proto;
 | 
			
		||||
	u32			target;
 | 
			
		||||
	__be64			tun_id;
 | 
			
		||||
	struct fib_rule __rcu	*ctarget;
 | 
			
		||||
| 
						 | 
				
			
			@ -40,6 +40,8 @@ struct fib_rule {
 | 
			
		|||
	char			iifname[IFNAMSIZ];
 | 
			
		||||
	char			oifname[IFNAMSIZ];
 | 
			
		||||
	struct fib_kuid_range	uid_range;
 | 
			
		||||
	struct fib_rule_port_range	sport_range;
 | 
			
		||||
	struct fib_rule_port_range	dport_range;
 | 
			
		||||
	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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 net *);
 | 
			
		||||
void fib_rules_unregister(struct fib_rules_ops *);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,6 +35,11 @@ struct fib_rule_uid_range {
 | 
			
		|||
	__u32		end;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct fib_rule_port_range {
 | 
			
		||||
	__u16		start;
 | 
			
		||||
	__u16		end;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
	FRA_UNSPEC,
 | 
			
		||||
	FRA_DST,	/* destination address */
 | 
			
		||||
| 
						 | 
				
			
			@ -59,6 +64,9 @@ enum {
 | 
			
		|||
	FRA_L3MDEV,	/* iif or oif is l3mdev goto its table */
 | 
			
		||||
	FRA_UID_RANGE,	/* UID range */
 | 
			
		||||
	FRA_PROTOCOL,   /* Originator of the rule */
 | 
			
		||||
	FRA_IP_PROTO,	/* ip proto */
 | 
			
		||||
	FRA_SPORT_RANGE, /* sport */
 | 
			
		||||
	FRA_DPORT_RANGE, /* dport */
 | 
			
		||||
	__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) ||
 | 
			
		||||
	    !uid_eq(rule->uid_range.end, fib_kuid_range_unset.end))
 | 
			
		||||
		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;
 | 
			
		||||
}
 | 
			
		||||
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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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,
 | 
			
		||||
			  struct flowi *fl, int flags,
 | 
			
		||||
			  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))
 | 
			
		||||
			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))
 | 
			
		||||
			continue;
 | 
			
		||||
		return 1;
 | 
			
		||||
| 
						 | 
				
			
			@ -569,6 +604,23 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh,
 | 
			
		|||
		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) &&
 | 
			
		||||
	    rule_exists(ops, frh, tb, rule)) {
 | 
			
		||||
		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 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_rule *rule, *r;
 | 
			
		||||
	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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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) {
 | 
			
		||||
		if (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)))
 | 
			
		||||
			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))
 | 
			
		||||
			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_64bit(8) /* FRA_TUN_ID */
 | 
			
		||||
			 + 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)
 | 
			
		||||
		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 &&
 | 
			
		||||
	     nla_put_u8(skb, FRA_L3MDEV, rule->l3mdev)) ||
 | 
			
		||||
	    (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;
 | 
			
		||||
 | 
			
		||||
	if (rule->suppress_ifgroup != -1) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue