mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	net: fib_rules: add extack support
Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									f9d4b0c1e9
								
							
						
					
					
						commit
						b16fb418b1
					
				
					 7 changed files with 63 additions and 22 deletions
				
			
		| 
						 | 
					@ -75,7 +75,8 @@ struct fib_rules_ops {
 | 
				
			||||||
	int			(*configure)(struct fib_rule *,
 | 
						int			(*configure)(struct fib_rule *,
 | 
				
			||||||
					     struct sk_buff *,
 | 
										     struct sk_buff *,
 | 
				
			||||||
					     struct fib_rule_hdr *,
 | 
										     struct fib_rule_hdr *,
 | 
				
			||||||
					     struct nlattr **);
 | 
										     struct nlattr **,
 | 
				
			||||||
 | 
										     struct netlink_ext_ack *);
 | 
				
			||||||
	int			(*delete)(struct fib_rule *);
 | 
						int			(*delete)(struct fib_rule *);
 | 
				
			||||||
	int			(*compare)(struct fib_rule *,
 | 
						int			(*compare)(struct fib_rule *,
 | 
				
			||||||
					   struct fib_rule_hdr *,
 | 
										   struct fib_rule_hdr *,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -469,14 +469,18 @@ static int fib_nl2rule(struct sk_buff *skb, struct nlmsghdr *nlh,
 | 
				
			||||||
	if (frh->src_len)
 | 
						if (frh->src_len)
 | 
				
			||||||
		if (!tb[FRA_SRC] ||
 | 
							if (!tb[FRA_SRC] ||
 | 
				
			||||||
		    frh->src_len > (ops->addr_size * 8) ||
 | 
							    frh->src_len > (ops->addr_size * 8) ||
 | 
				
			||||||
		    nla_len(tb[FRA_SRC]) != ops->addr_size)
 | 
							    nla_len(tb[FRA_SRC]) != ops->addr_size) {
 | 
				
			||||||
 | 
								NL_SET_ERR_MSG(extack, "Invalid source address");
 | 
				
			||||||
			goto errout;
 | 
								goto errout;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (frh->dst_len)
 | 
						if (frh->dst_len)
 | 
				
			||||||
		if (!tb[FRA_DST] ||
 | 
							if (!tb[FRA_DST] ||
 | 
				
			||||||
		    frh->dst_len > (ops->addr_size * 8) ||
 | 
							    frh->dst_len > (ops->addr_size * 8) ||
 | 
				
			||||||
		    nla_len(tb[FRA_DST]) != ops->addr_size)
 | 
							    nla_len(tb[FRA_DST]) != ops->addr_size) {
 | 
				
			||||||
 | 
								NL_SET_ERR_MSG(extack, "Invalid dst address");
 | 
				
			||||||
			goto errout;
 | 
								goto errout;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	nlrule = kzalloc(ops->rule_size, GFP_KERNEL);
 | 
						nlrule = kzalloc(ops->rule_size, GFP_KERNEL);
 | 
				
			||||||
	if (!nlrule) {
 | 
						if (!nlrule) {
 | 
				
			||||||
| 
						 | 
					@ -537,6 +541,7 @@ static int fib_nl2rule(struct sk_buff *skb, struct nlmsghdr *nlh,
 | 
				
			||||||
		nlrule->l3mdev = nla_get_u8(tb[FRA_L3MDEV]);
 | 
							nlrule->l3mdev = nla_get_u8(tb[FRA_L3MDEV]);
 | 
				
			||||||
		if (nlrule->l3mdev != 1)
 | 
							if (nlrule->l3mdev != 1)
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
								NL_SET_ERR_MSG(extack, "Invalid l3mdev");
 | 
				
			||||||
			goto errout_free;
 | 
								goto errout_free;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -554,31 +559,41 @@ static int fib_nl2rule(struct sk_buff *skb, struct nlmsghdr *nlh,
 | 
				
			||||||
		nlrule->suppress_ifgroup = -1;
 | 
							nlrule->suppress_ifgroup = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (tb[FRA_GOTO]) {
 | 
						if (tb[FRA_GOTO]) {
 | 
				
			||||||
		if (nlrule->action != FR_ACT_GOTO)
 | 
							if (nlrule->action != FR_ACT_GOTO) {
 | 
				
			||||||
			goto errout_free;
 | 
								NL_SET_ERR_MSG(extack, "Unexpected goto");
 | 
				
			||||||
 | 
					 | 
				
			||||||
		nlrule->target = nla_get_u32(tb[FRA_GOTO]);
 | 
					 | 
				
			||||||
		/* Backward jumps are prohibited to avoid endless loops */
 | 
					 | 
				
			||||||
		if (nlrule->target <= nlrule->pref)
 | 
					 | 
				
			||||||
			goto errout_free;
 | 
					 | 
				
			||||||
	} else if (nlrule->action == FR_ACT_GOTO) {
 | 
					 | 
				
			||||||
			goto errout_free;
 | 
								goto errout_free;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (nlrule->l3mdev && nlrule->table)
 | 
							nlrule->target = nla_get_u32(tb[FRA_GOTO]);
 | 
				
			||||||
 | 
							/* Backward jumps are prohibited to avoid endless loops */
 | 
				
			||||||
 | 
							if (nlrule->target <= nlrule->pref) {
 | 
				
			||||||
 | 
								NL_SET_ERR_MSG(extack, "Backward goto not supported");
 | 
				
			||||||
			goto errout_free;
 | 
								goto errout_free;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else if (nlrule->action == FR_ACT_GOTO) {
 | 
				
			||||||
 | 
							NL_SET_ERR_MSG(extack, "Missing goto target for action goto");
 | 
				
			||||||
 | 
							goto errout_free;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (nlrule->l3mdev && nlrule->table) {
 | 
				
			||||||
 | 
							NL_SET_ERR_MSG(extack, "l3mdev and table are mutually exclusive");
 | 
				
			||||||
 | 
							goto errout_free;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (tb[FRA_UID_RANGE]) {
 | 
						if (tb[FRA_UID_RANGE]) {
 | 
				
			||||||
		if (current_user_ns() != net->user_ns) {
 | 
							if (current_user_ns() != net->user_ns) {
 | 
				
			||||||
			err = -EPERM;
 | 
								err = -EPERM;
 | 
				
			||||||
 | 
								NL_SET_ERR_MSG(extack, "No permission to set uid");
 | 
				
			||||||
			goto errout_free;
 | 
								goto errout_free;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		nlrule->uid_range = nla_get_kuid_range(tb);
 | 
							nlrule->uid_range = nla_get_kuid_range(tb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!uid_range_set(&nlrule->uid_range) ||
 | 
							if (!uid_range_set(&nlrule->uid_range) ||
 | 
				
			||||||
		    !uid_lte(nlrule->uid_range.start, nlrule->uid_range.end))
 | 
							    !uid_lte(nlrule->uid_range.start, nlrule->uid_range.end)) {
 | 
				
			||||||
 | 
								NL_SET_ERR_MSG(extack, "Invalid uid range");
 | 
				
			||||||
			goto errout_free;
 | 
								goto errout_free;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		nlrule->uid_range = fib_kuid_range_unset;
 | 
							nlrule->uid_range = fib_kuid_range_unset;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -589,16 +604,20 @@ static int fib_nl2rule(struct sk_buff *skb, struct nlmsghdr *nlh,
 | 
				
			||||||
	if (tb[FRA_SPORT_RANGE]) {
 | 
						if (tb[FRA_SPORT_RANGE]) {
 | 
				
			||||||
		err = nla_get_port_range(tb[FRA_SPORT_RANGE],
 | 
							err = nla_get_port_range(tb[FRA_SPORT_RANGE],
 | 
				
			||||||
					 &nlrule->sport_range);
 | 
										 &nlrule->sport_range);
 | 
				
			||||||
		if (err)
 | 
							if (err) {
 | 
				
			||||||
 | 
								NL_SET_ERR_MSG(extack, "Invalid sport range");
 | 
				
			||||||
			goto errout_free;
 | 
								goto errout_free;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (tb[FRA_DPORT_RANGE]) {
 | 
						if (tb[FRA_DPORT_RANGE]) {
 | 
				
			||||||
		err = nla_get_port_range(tb[FRA_DPORT_RANGE],
 | 
							err = nla_get_port_range(tb[FRA_DPORT_RANGE],
 | 
				
			||||||
					 &nlrule->dport_range);
 | 
										 &nlrule->dport_range);
 | 
				
			||||||
		if (err)
 | 
							if (err) {
 | 
				
			||||||
 | 
								NL_SET_ERR_MSG(extack, "Invalid dport range");
 | 
				
			||||||
			goto errout_free;
 | 
								goto errout_free;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	*rule = nlrule;
 | 
						*rule = nlrule;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -621,18 +640,23 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh,
 | 
				
			||||||
	int err = -EINVAL, unresolved = 0;
 | 
						int err = -EINVAL, unresolved = 0;
 | 
				
			||||||
	bool user_priority = false;
 | 
						bool user_priority = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh)))
 | 
						if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh))) {
 | 
				
			||||||
 | 
							NL_SET_ERR_MSG(extack, "Invalid msg length");
 | 
				
			||||||
		goto errout;
 | 
							goto errout;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ops = lookup_rules_ops(net, frh->family);
 | 
						ops = lookup_rules_ops(net, frh->family);
 | 
				
			||||||
	if (!ops) {
 | 
						if (!ops) {
 | 
				
			||||||
		err = -EAFNOSUPPORT;
 | 
							err = -EAFNOSUPPORT;
 | 
				
			||||||
 | 
							NL_SET_ERR_MSG(extack, "Rule family not supported");
 | 
				
			||||||
		goto errout;
 | 
							goto errout;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = nlmsg_parse(nlh, sizeof(*frh), tb, FRA_MAX, ops->policy, extack);
 | 
						err = nlmsg_parse(nlh, sizeof(*frh), tb, FRA_MAX, ops->policy, extack);
 | 
				
			||||||
	if (err < 0)
 | 
						if (err < 0) {
 | 
				
			||||||
 | 
							NL_SET_ERR_MSG(extack, "Error parsing msg");
 | 
				
			||||||
		goto errout;
 | 
							goto errout;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = fib_nl2rule(skb, nlh, extack, ops, tb, &rule, &user_priority);
 | 
						err = fib_nl2rule(skb, nlh, extack, ops, tb, &rule, &user_priority);
 | 
				
			||||||
	if (err)
 | 
						if (err)
 | 
				
			||||||
| 
						 | 
					@ -644,7 +668,7 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh,
 | 
				
			||||||
		goto errout_free;
 | 
							goto errout_free;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = ops->configure(rule, skb, frh, tb);
 | 
						err = ops->configure(rule, skb, frh, tb, extack);
 | 
				
			||||||
	if (err < 0)
 | 
						if (err < 0)
 | 
				
			||||||
		goto errout_free;
 | 
							goto errout_free;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -723,18 +747,23 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh,
 | 
				
			||||||
	int err = -EINVAL;
 | 
						int err = -EINVAL;
 | 
				
			||||||
	bool user_priority = false;
 | 
						bool user_priority = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh)))
 | 
						if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh))) {
 | 
				
			||||||
 | 
							NL_SET_ERR_MSG(extack, "Invalid msg length");
 | 
				
			||||||
		goto errout;
 | 
							goto errout;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ops = lookup_rules_ops(net, frh->family);
 | 
						ops = lookup_rules_ops(net, frh->family);
 | 
				
			||||||
	if (ops == NULL) {
 | 
						if (ops == NULL) {
 | 
				
			||||||
		err = -EAFNOSUPPORT;
 | 
							err = -EAFNOSUPPORT;
 | 
				
			||||||
 | 
							NL_SET_ERR_MSG(extack, "Rule family not supported");
 | 
				
			||||||
		goto errout;
 | 
							goto errout;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = nlmsg_parse(nlh, sizeof(*frh), tb, FRA_MAX, ops->policy, extack);
 | 
						err = nlmsg_parse(nlh, sizeof(*frh), tb, FRA_MAX, ops->policy, extack);
 | 
				
			||||||
	if (err < 0)
 | 
						if (err < 0) {
 | 
				
			||||||
 | 
							NL_SET_ERR_MSG(extack, "Error parsing msg");
 | 
				
			||||||
		goto errout;
 | 
							goto errout;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = fib_nl2rule(skb, nlh, extack, ops, tb, &nlrule, &user_priority);
 | 
						err = fib_nl2rule(skb, nlh, extack, ops, tb, &nlrule, &user_priority);
 | 
				
			||||||
	if (err)
 | 
						if (err)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -121,13 +121,16 @@ static int dn_fib_rule_match(struct fib_rule *rule, struct flowi *fl, int flags)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int dn_fib_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
 | 
					static int dn_fib_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
 | 
				
			||||||
				 struct fib_rule_hdr *frh,
 | 
									 struct fib_rule_hdr *frh,
 | 
				
			||||||
				 struct nlattr **tb)
 | 
									 struct nlattr **tb,
 | 
				
			||||||
 | 
									 struct netlink_ext_ack *extack)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int err = -EINVAL;
 | 
						int err = -EINVAL;
 | 
				
			||||||
	struct dn_fib_rule *r = (struct dn_fib_rule *)rule;
 | 
						struct dn_fib_rule *r = (struct dn_fib_rule *)rule;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (frh->tos)
 | 
						if (frh->tos) {
 | 
				
			||||||
 | 
							NL_SET_ERR_MSG(extack, "Invalid tos value");
 | 
				
			||||||
		goto  errout;
 | 
							goto  errout;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (rule->table == RT_TABLE_UNSPEC) {
 | 
						if (rule->table == RT_TABLE_UNSPEC) {
 | 
				
			||||||
		if (rule->action == FR_ACT_TO_TBL) {
 | 
							if (rule->action == FR_ACT_TO_TBL) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -213,14 +213,17 @@ static const struct nla_policy fib4_rule_policy[FRA_MAX+1] = {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int fib4_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
 | 
					static int fib4_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
 | 
				
			||||||
			       struct fib_rule_hdr *frh,
 | 
								       struct fib_rule_hdr *frh,
 | 
				
			||||||
			       struct nlattr **tb)
 | 
								       struct nlattr **tb,
 | 
				
			||||||
 | 
								       struct netlink_ext_ack *extack)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct net *net = sock_net(skb->sk);
 | 
						struct net *net = sock_net(skb->sk);
 | 
				
			||||||
	int err = -EINVAL;
 | 
						int err = -EINVAL;
 | 
				
			||||||
	struct fib4_rule *rule4 = (struct fib4_rule *) rule;
 | 
						struct fib4_rule *rule4 = (struct fib4_rule *) rule;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (frh->tos & ~IPTOS_TOS_MASK)
 | 
						if (frh->tos & ~IPTOS_TOS_MASK) {
 | 
				
			||||||
 | 
							NL_SET_ERR_MSG(extack, "Invalid tos");
 | 
				
			||||||
		goto errout;
 | 
							goto errout;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* split local/main if they are not already split */
 | 
						/* split local/main if they are not already split */
 | 
				
			||||||
	err = fib_unmerge(net);
 | 
						err = fib_unmerge(net);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -201,7 +201,8 @@ static const struct nla_policy ipmr_rule_policy[FRA_MAX + 1] = {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int ipmr_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
 | 
					static int ipmr_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
 | 
				
			||||||
			       struct fib_rule_hdr *frh, struct nlattr **tb)
 | 
								       struct fib_rule_hdr *frh, struct nlattr **tb,
 | 
				
			||||||
 | 
								       struct netlink_ext_ack *extack)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -245,15 +245,18 @@ static const struct nla_policy fib6_rule_policy[FRA_MAX+1] = {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int fib6_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
 | 
					static int fib6_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
 | 
				
			||||||
			       struct fib_rule_hdr *frh,
 | 
								       struct fib_rule_hdr *frh,
 | 
				
			||||||
			       struct nlattr **tb)
 | 
								       struct nlattr **tb,
 | 
				
			||||||
 | 
								       struct netlink_ext_ack *extack)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int err = -EINVAL;
 | 
						int err = -EINVAL;
 | 
				
			||||||
	struct net *net = sock_net(skb->sk);
 | 
						struct net *net = sock_net(skb->sk);
 | 
				
			||||||
	struct fib6_rule *rule6 = (struct fib6_rule *) rule;
 | 
						struct fib6_rule *rule6 = (struct fib6_rule *) rule;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (rule->action == FR_ACT_TO_TBL && !rule->l3mdev) {
 | 
						if (rule->action == FR_ACT_TO_TBL && !rule->l3mdev) {
 | 
				
			||||||
		if (rule->table == RT6_TABLE_UNSPEC)
 | 
							if (rule->table == RT6_TABLE_UNSPEC) {
 | 
				
			||||||
 | 
								NL_SET_ERR_MSG(extack, "Invalid table");
 | 
				
			||||||
			goto errout;
 | 
								goto errout;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (fib6_new_table(net, rule->table) == NULL) {
 | 
							if (fib6_new_table(net, rule->table) == NULL) {
 | 
				
			||||||
			err = -ENOBUFS;
 | 
								err = -ENOBUFS;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -180,7 +180,8 @@ static const struct nla_policy ip6mr_rule_policy[FRA_MAX + 1] = {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int ip6mr_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
 | 
					static int ip6mr_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
 | 
				
			||||||
				struct fib_rule_hdr *frh, struct nlattr **tb)
 | 
									struct fib_rule_hdr *frh, struct nlattr **tb,
 | 
				
			||||||
 | 
									struct netlink_ext_ack *extack)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue