diff options
Diffstat (limited to 'net/ipv4/fib_rules.c')
| -rw-r--r-- | net/ipv4/fib_rules.c | 106 | 
1 files changed, 76 insertions, 30 deletions
diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index 7981a24f5c7..f2e15738534 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -26,6 +26,7 @@  #include <linux/init.h>  #include <linux/list.h>  #include <linux/rcupdate.h> +#include <linux/export.h>  #include <net/ip.h>  #include <net/route.h>  #include <net/tcp.h> @@ -41,19 +42,12 @@ struct fib4_rule {  	__be32			srcmask;  	__be32			dst;  	__be32			dstmask; -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID  	u32			tclassid;  #endif  }; -#ifdef CONFIG_NET_CLS_ROUTE -u32 fib_rules_tclass(struct fib_result *res) -{ -	return res->r ? ((struct fib4_rule *) res->r)->tclassid : 0; -} -#endif - -int fib_lookup(struct net *net, struct flowi *flp, struct fib_result *res) +int __fib_lookup(struct net *net, struct flowi4 *flp, struct fib_result *res)  {  	struct fib_lookup_arg arg = {  		.result = res, @@ -61,11 +55,16 @@ int fib_lookup(struct net *net, struct flowi *flp, struct fib_result *res)  	};  	int err; -	err = fib_rules_lookup(net->ipv4.rules_ops, flp, 0, &arg); -	res->r = arg.rule; - +	err = fib_rules_lookup(net->ipv4.rules_ops, flowi4_to_flowi(flp), 0, &arg); +#ifdef CONFIG_IP_ROUTE_CLASSID +	if (arg.rule) +		res->tclassid = ((struct fib4_rule *)arg.rule)->tclassid; +	else +		res->tclassid = 0; +#endif  	return err;  } +EXPORT_SYMBOL_GPL(__fib_lookup);  static int fib4_rule_action(struct fib_rule *rule, struct flowi *flp,  			    int flags, struct fib_lookup_arg *arg) @@ -95,25 +94,53 @@ static int fib4_rule_action(struct fib_rule *rule, struct flowi *flp,  	if (!tbl)  		goto errout; -	err = fib_table_lookup(tbl, flp, (struct fib_result *) arg->result, arg->flags); +	err = fib_table_lookup(tbl, &flp->u.ip4, (struct fib_result *) arg->result, arg->flags);  	if (err > 0)  		err = -EAGAIN;  errout:  	return err;  } +static bool fib4_rule_suppress(struct fib_rule *rule, struct fib_lookup_arg *arg) +{ +	struct fib_result *result = (struct fib_result *) arg->result; +	struct net_device *dev = NULL; + +	if (result->fi) +		dev = result->fi->fib_dev; + +	/* do not accept result if the route does +	 * not meet the required prefix length +	 */ +	if (result->prefixlen <= rule->suppress_prefixlen) +		goto suppress_route; + +	/* do not accept result if the route uses a device +	 * belonging to a forbidden interface group +	 */ +	if (rule->suppress_ifgroup != -1 && dev && dev->group == rule->suppress_ifgroup) +		goto suppress_route; + +	return false; + +suppress_route: +	if (!(arg->flags & FIB_LOOKUP_NOREF)) +		fib_info_put(result->fi); +	return true; +}  static int fib4_rule_match(struct fib_rule *rule, struct flowi *fl, int flags)  {  	struct fib4_rule *r = (struct fib4_rule *) rule; -	__be32 daddr = fl->fl4_dst; -	__be32 saddr = fl->fl4_src; +	struct flowi4 *fl4 = &fl->u.ip4; +	__be32 daddr = fl4->daddr; +	__be32 saddr = fl4->saddr;  	if (((saddr ^ r->src) & r->srcmask) ||  	    ((daddr ^ r->dst) & r->dstmask))  		return 0; -	if (r->tos && (r->tos != fl->fl4_tos)) +	if (r->tos && (r->tos != fl4->flowi4_tos))  		return 0;  	return 1; @@ -165,9 +192,12 @@ static int fib4_rule_configure(struct fib_rule *rule, struct sk_buff *skb,  	if (frh->dst_len)  		rule4->dst = nla_get_be32(tb[FRA_DST]); -#ifdef CONFIG_NET_CLS_ROUTE -	if (tb[FRA_FLOW]) +#ifdef CONFIG_IP_ROUTE_CLASSID +	if (tb[FRA_FLOW]) {  		rule4->tclassid = nla_get_u32(tb[FRA_FLOW]); +		if (rule4->tclassid) +			net->ipv4.fib_num_tclassid_users++; +	}  #endif  	rule4->src_len = frh->src_len; @@ -176,11 +206,24 @@ static int fib4_rule_configure(struct fib_rule *rule, struct sk_buff *skb,  	rule4->dstmask = inet_make_mask(rule4->dst_len);  	rule4->tos = frh->tos; +	net->ipv4.fib_has_custom_rules = true;  	err = 0;  errout:  	return err;  } +static void fib4_rule_delete(struct fib_rule *rule) +{ +	struct net *net = rule->fr_net; +#ifdef CONFIG_IP_ROUTE_CLASSID +	struct fib4_rule *rule4 = (struct fib4_rule *) rule; + +	if (rule4->tclassid) +		net->ipv4.fib_num_tclassid_users--; +#endif +	net->ipv4.fib_has_custom_rules = true; +} +  static int fib4_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh,  			     struct nlattr **tb)  { @@ -195,7 +238,7 @@ static int fib4_rule_compare(struct fib_rule *rule, struct fib_rule_hdr *frh,  	if (frh->tos && (rule4->tos != frh->tos))  		return 0; -#ifdef CONFIG_NET_CLS_ROUTE +#ifdef CONFIG_IP_ROUTE_CLASSID  	if (tb[FRA_FLOW] && (rule4->tclassid != nla_get_u32(tb[FRA_FLOW])))  		return 0;  #endif @@ -218,15 +261,15 @@ static int fib4_rule_fill(struct fib_rule *rule, struct sk_buff *skb,  	frh->src_len = rule4->src_len;  	frh->tos = rule4->tos; -	if (rule4->dst_len) -		NLA_PUT_BE32(skb, FRA_DST, rule4->dst); - -	if (rule4->src_len) -		NLA_PUT_BE32(skb, FRA_SRC, rule4->src); - -#ifdef CONFIG_NET_CLS_ROUTE -	if (rule4->tclassid) -		NLA_PUT_U32(skb, FRA_FLOW, rule4->tclassid); +	if ((rule4->dst_len && +	     nla_put_be32(skb, FRA_DST, rule4->dst)) || +	    (rule4->src_len && +	     nla_put_be32(skb, FRA_SRC, rule4->src))) +		goto nla_put_failure; +#ifdef CONFIG_IP_ROUTE_CLASSID +	if (rule4->tclassid && +	    nla_put_u32(skb, FRA_FLOW, rule4->tclassid)) +		goto nla_put_failure;  #endif  	return 0; @@ -243,16 +286,18 @@ static size_t fib4_rule_nlmsg_payload(struct fib_rule *rule)  static void fib4_rule_flush_cache(struct fib_rules_ops *ops)  { -	rt_cache_flush(ops->fro_net, -1); +	rt_cache_flush(ops->fro_net);  } -static const struct fib_rules_ops __net_initdata fib4_rules_ops_template = { +static const struct fib_rules_ops __net_initconst fib4_rules_ops_template = {  	.family		= AF_INET,  	.rule_size	= sizeof(struct fib4_rule),  	.addr_size	= sizeof(u32),  	.action		= fib4_rule_action, +	.suppress	= fib4_rule_suppress,  	.match		= fib4_rule_match,  	.configure	= fib4_rule_configure, +	.delete		= fib4_rule_delete,  	.compare	= fib4_rule_compare,  	.fill		= fib4_rule_fill,  	.default_pref	= fib_default_rule_pref, @@ -292,6 +337,7 @@ int __net_init fib4_rules_init(struct net *net)  	if (err < 0)  		goto fail;  	net->ipv4.rules_ops = ops; +	net->ipv4.fib_has_custom_rules = false;  	return 0;  fail:  | 
