Subject: Re: [PATCHv2 net-next 3/8] net: sched: act: handle generic action errors

From: Davide Caratti <[email-masked]>
Date: Thu, 15 Feb 2018 12:14:30 +0100

On Wed, 2018-02-14 at 17:13 -0500, Alexander Aring wrote:
> This patch adds extack support for generic act handling. The extack
> will be set deeper to each called function which is not part of netdev
> core api.
> 
> Based on work by David Ahern <[email-masked]>

hello Alexander,

after looking at the code more closely, I think there is margin for some
more improvement here (i.e make the message contents easier to grep from a
log). Please let me know if the comments below make sense for you.

thank you in advance!

-- 
davide

> Cc: David Ahern <[email-masked]>
> Signed-off-by: Alexander Aring <[email-masked]>
> ---
>  net/sched/act_api.c | 93 +++++++++++++++++++++++++++++++++++------------------
>  1 file changed, 61 insertions(+), 32 deletions(-)
> 
> diff --git a/net/sched/act_api.c b/net/sched/act_api.c
> index 8d89b026414f..a5138ae026a1 100644
> --- a/net/sched/act_api.c
> +++ b/net/sched/act_api.c
> @@ -617,31 +617,40 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
>  	int err;
>  
>  	if (name == NULL) {
> -		err = nla_parse_nested(tb, TCA_ACT_MAX, nla, NULL, NULL);
> +		err = nla_parse_nested(tb, TCA_ACT_MAX, nla, NULL, extack);
>  		if (err < 0)
>  			goto err_out;
>  		err = -EINVAL;
>  		kind = tb[TCA_ACT_KIND];
> -		if (!kind)
> +		if (!kind) {
> +			NL_SET_ERR_MSG(extack, "TC action kind must be specified");
>  			goto err_out;
> -		if (nla_strlcpy(act_name, kind, IFNAMSIZ) >= IFNAMSIZ)
> +		}
> +		if (nla_strlcpy(act_name, kind, IFNAMSIZ) >= IFNAMSIZ) {
> +			NL_SET_ERR_MSG(extack, "TC action name too long");
>  			goto err_out;
> +		}
>  		if (tb[TCA_ACT_COOKIE]) {
>  			int cklen = nla_len(tb[TCA_ACT_COOKIE]);
>  
> -			if (cklen > TC_COOKIE_MAX_SIZE)
> +			if (cklen > TC_COOKIE_MAX_SIZE) {
> +				NL_SET_ERR_MSG(extack, "TC cookie size above the maximum");
>  				goto err_out;
> +			}
>  
>  			cookie = nla_memdup_cookie(tb);
>  			if (!cookie) {
> +				NL_SET_ERR_MSG(extack, "No memory to generate TC cookie");
>  				err = -ENOMEM;
>  				goto err_out;
>  			}
>  		}
>  	} else {
> -		err = -EINVAL;
> -		if (strlcpy(act_name, name, IFNAMSIZ) >= IFNAMSIZ)
> +		if (strlcpy(act_name, name, IFNAMSIZ) >= IFNAMSIZ) {
> +			NL_SET_ERR_MSG(extack, "TC action name too long");
> +			err = -EINVAL;
>  			goto err_out;
> +		}
>  	}
>  
>  	a_o = tc_lookup_action_n(act_name);
> @@ -664,6 +673,7 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
>  			goto err_mod;
>  		}
>  #endif
> +		NL_SET_ERR_MSG(extack, "Failed to load TC action module");
>  		err = -ENOENT;
>  		goto err_out;
>  	}
> @@ -698,6 +708,7 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
>  
>  			list_add_tail(&a->list, &actions);
>  			tcf_action_destroy(&actions, bind);
> +			NL_SET_ERR_MSG(extack, "Failed to init action chain");

most of the times, the word 'action' is prepended by the word 'TC'.
Proposal:

"Failed to init TC action chain" 

>  			return ERR_PTR(err);
>  		}
>  	}
> @@ -734,7 +745,7 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
>  	int err;
>  	int i;
>  
> -	err = nla_parse_nested(tb, TCA_ACT_MAX_PRIO, nla, NULL, NULL);
> +	err = nla_parse_nested(tb, TCA_ACT_MAX_PRIO, nla, NULL, extack);
>  	if (err < 0)
>  		return err;
>  
> @@ -842,7 +853,8 @@ static int tca_get_fill(struct sk_buff *skb, struct list_head *actions,
>  
>  static int
>  tcf_get_notify(struct net *net, u32 portid, struct nlmsghdr *n,
> -	       struct list_head *actions, int event)
> +	       struct list_head *actions, int event,
> +	       struct netlink_ext_ack *extack)
>  {
>  	struct sk_buff *skb;
>  
> @@ -851,6 +863,7 @@ tcf_get_notify(struct net *net, u32 portid, struct nlmsghdr *n,
>  		return -ENOBUFS;
>  	if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, event,
>  			 0, 0) <= 0) {
> +		NL_SET_ERR_MSG(extack, "Failed to fill netlink tc action attributes");

see also @@ -975,6 +1000,7 @@ and @@ -975,6 +1000,7 @@:

this is the same message you get in case tcf_add_notify() fails, and it's
very similar to what you get when tcf_del_notify() fails: the only
difference is uppercase/lowercase 'tc' word.

Proposal:

"Failed to fill netlink attributes while getting TC action"

>  		kfree_skb(skb);
>  		return -EINVAL;
>  	}
> @@ -859,7 +872,8 @@ tcf_get_notify(struct net *net, u32 portid, struct nlmsghdr *n,
>  }
>  
>  static struct tc_action *tcf_action_get_1(struct net *net, struct nlattr *nla,
> -					  struct nlmsghdr *n, u32 portid)
> +					  struct nlmsghdr *n, u32 portid,
> +					  struct netlink_ext_ack *extack)
>  {
>  	struct nlattr *tb[TCA_ACT_MAX + 1];
>  	const struct tc_action_ops *ops;
> @@ -867,20 +881,24 @@ static struct tc_action *tcf_action_get_1(struct net *net, struct nlattr *nla,
>  	int index;
>  	int err;
>  
> -	err = nla_parse_nested(tb, TCA_ACT_MAX, nla, NULL, NULL);
> +	err = nla_parse_nested(tb, TCA_ACT_MAX, nla, NULL, extack);
>  	if (err < 0)
>  		goto err_out;
>  
>  	err = -EINVAL;
>  	if (tb[TCA_ACT_INDEX] == NULL ||
> -	    nla_len(tb[TCA_ACT_INDEX]) < sizeof(index))
> +	    nla_len(tb[TCA_ACT_INDEX]) < sizeof(index)) {
> +		NL_SET_ERR_MSG(extack, "Invalid TC action index value");
>  		goto err_out;
> +	}
>  	index = nla_get_u32(tb[TCA_ACT_INDEX]);
>  
>  	err = -EINVAL;
>  	ops = tc_lookup_action(tb[TCA_ACT_KIND]);
> -	if (!ops) /* could happen in batch of actions */
> +	if (!ops) { /* could happen in batch of actions */
> +		NL_SET_ERR_MSG(extack, "Specified TC action not found");
>  		goto err_out;
> +	}
>  	err = -ENOENT;
>  	if (ops->lookup(net, &a, index) == 0)
>  		goto err_mod;
> @@ -895,7 +913,8 @@ static struct tc_action *tcf_action_get_1(struct net *net, struct nlattr *nla,
>  }
>  
>  static int tca_action_flush(struct net *net, struct nlattr *nla,
> -			    struct nlmsghdr *n, u32 portid)
> +			    struct nlmsghdr *n, u32 portid,
> +			    struct netlink_ext_ack *extack)
>  {
>  	struct sk_buff *skb;
>  	unsigned char *b;
> @@ -909,35 +928,39 @@ static int tca_action_flush(struct net *net, struct nlattr *nla,
>  	int err = -ENOMEM;
>  
>  	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
> -	if (!skb) {
> -		pr_debug("tca_action_flush: failed skb alloc\n");
> +	if (!skb)
>  		return err;
> -	}
>  
>  	b = skb_tail_pointer(skb);
>  
> -	err = nla_parse_nested(tb, TCA_ACT_MAX, nla, NULL, NULL);
> +	err = nla_parse_nested(tb, TCA_ACT_MAX, nla, NULL, extack);
>  	if (err < 0)
>  		goto err_out;
>  
>  	err = -EINVAL;
>  	kind = tb[TCA_ACT_KIND];
>  	ops = tc_lookup_action(kind);
> -	if (!ops) /*some idjot trying to flush unknown action */
> +	if (!ops) { /*some idjot trying to flush unknown action */

the above comment was already there before your patch. I think that it can
be removed, now that we have an error message for this situation; it's
also funny (and harmless) to leave it - but then maybe it becomes more
clear if the word 'idiot' is spelled correctly :)

> +		NL_SET_ERR_MSG(extack, "Aborted Flush. Cannot flush unknown TC action");

Also the tests below result in an aborted flush when they fail. So,
'Aborted Flush' can probably be omitted. Proposal:

"Cannot flush unknown TC action"

>  		goto err_out;
> +	}
>  
>  	nlh = nlmsg_put(skb, portid, n->nlmsg_seq, RTM_DELACTION,
>  			sizeof(*t), 0);
> -	if (!nlh)
> +	if (!nlh) {
> +		NL_SET_ERR_MSG(extack, "Aborted Flush. Failed to create flush netlink notification");
Proposal:

"Failed to create netlink notification while flushing TC action". But I'm
not sure that these kind of errors deserve an extended ACK (see below).

>  		goto out_module_put;
> +	}
>  	t = nlmsg_data(nlh);
>  	t->tca_family = AF_UNSPEC;
>  	t->tca__pad1 = 0;
>  	t->tca__pad2 = 0;
>  
>  	nest = nla_nest_start(skb, TCA_ACT_TAB);
> -	if (!nest)
> +	if (!nest) {
> +		NL_SET_ERR_MSG(extack, "Failed to add new netlink message");

"Failed to set nested attribute while flushing TC action". But I'm not
sure that this kind of errors deserve an extended ACK. Probaly a good
compromise is to do a single message in the error path of the function
(i.e. below the "out_module_put" label).

>  		goto out_module_put;
> +	}
>  
>  	err = ops->walk(net, skb, &dcb, RTM_DELACTION, ops);
>  	if (err <= 0)

here the action flush is aborted because walk() failed. Is it worth adding
a message here? (I'm also ok if there is a single error below
"out_module_put" label).

(and, for the record, I think we miss a nla_nest_cancel() here. I will
post a patch targeting 'net' today.)

> @@ -952,6 +975,8 @@ static int tca_action_flush(struct net *net, struct nlattr *nla,
>  			     n->nlmsg_flags & NLM_F_ECHO);
>  	if (err > 0)
>  		return 0;
> +	if (err < 0)
> +		NL_SET_ERR_MSG(extack, "Failed to send flush notification");

Proposal:

"Failed to send TC action flush notification"

>  
>  	return err;
>  
> @@ -964,7 +989,7 @@ static int tca_action_flush(struct net *net, struct nlattr *nla,
>  
>  static int
>  tcf_del_notify(struct net *net, struct nlmsghdr *n, struct list_head *actions,
> -	       u32 portid)
> +	       u32 portid, struct netlink_ext_ack *extack)
>  {
>  	int ret;
>  	struct sk_buff *skb;
> @@ -975,6 +1000,7 @@ tcf_del_notify(struct net *net, struct nlmsghdr *n, struct list_head *actions,
>  
>  	if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, RTM_DELACTION,
>  			 0, 1) <= 0) {
> +		NL_SET_ERR_MSG(extack, "Failed to fill netlink tc action attributes");

see also @@ -1039,7 +1067,7 @@:

this error message is almost the same as the one we get in case of failing
tca_get_fill(..., RTM_NEWACTION, ...) in tcf_add_notify(). The only
difference is the word 'tc' lowercase here, and uppercase word 'TC' in
tcf_add_notify(). So, if these message must be different, it's better to
specify better what went wrong _ something like:

"Failed to fill netlink attributes while deleting TC action"

>  		kfree_skb(skb);
>  		return -EINVAL;
>  	}
> @@ -982,6 +1008,7 @@ tcf_del_notify(struct net *net, struct nlmsghdr *n, struct list_head *actions,
>  	/* now do the delete */
>  	ret = tcf_action_destroy(actions, 0);
>  	if (ret < 0) {
> +		NL_SET_ERR_MSG(extack, "Failed to delete TC action");
>  		kfree_skb(skb);
>  		return ret;
>  	}
> @@ -995,26 +1022,27 @@ tcf_del_notify(struct net *net, struct nlmsghdr *n, struct list_head *actions,
>  
>  static int
>  tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n,
> -	      u32 portid, int event)
> +	      u32 portid, int event, struct netlink_ext_ack *extack)
>  {
>  	int i, ret;
>  	struct nlattr *tb[TCA_ACT_MAX_PRIO + 1];
>  	struct tc_action *act;
>  	LIST_HEAD(actions);
>  
> -	ret = nla_parse_nested(tb, TCA_ACT_MAX_PRIO, nla, NULL, NULL);
> +	ret = nla_parse_nested(tb, TCA_ACT_MAX_PRIO, nla, NULL, extack);
>  	if (ret < 0)
>  		return ret;
>  
>  	if (event == RTM_DELACTION && n->nlmsg_flags & NLM_F_ROOT) {
>  		if (tb[1])
> -			return tca_action_flush(net, tb[1], n, portid);
> +			return tca_action_flush(net, tb[1], n, portid, extack);
>  
> +		NL_SET_ERR_MSG(extack, "Invalid netlink attributes to flush actions");

"Invalid netlink attributes while flushing TC action"
>  		return -EINVAL;
>  	}
>  
>  	for (i = 1; i <= TCA_ACT_MAX_PRIO && tb[i]; i++) {
> -		act = tcf_action_get_1(net, tb[i], n, portid);
> +		act = tcf_action_get_1(net, tb[i], n, portid, extack);
>  		if (IS_ERR(act)) {
>  			ret = PTR_ERR(act);
>  			goto err;
> @@ -1024,9 +1052,9 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n,
>  	}
>  
>  	if (event == RTM_GETACTION)
> -		ret = tcf_get_notify(net, portid, n, &actions, event);
> +		ret = tcf_get_notify(net, portid, n, &actions, event, extack);
>  	else { /* delete */
> -		ret = tcf_del_notify(net, n, &actions, portid);
> +		ret = tcf_del_notify(net, n, &actions, portid, extack);
>  		if (ret)
>  			goto err;
>  		return ret;
> @@ -1039,7 +1067,7 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n,
>  
>  static int
>  tcf_add_notify(struct net *net, struct nlmsghdr *n, struct list_head *actions,
> -	       u32 portid)
> +	       u32 portid, struct netlink_ext_ack *extack)
>  {
>  	struct sk_buff *skb;
>  	int err = 0;
> @@ -1050,6 +1078,7 @@ tcf_add_notify(struct net *net, struct nlmsghdr *n, struct list_head *actions,
>  
>  	if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, n->nlmsg_flags,
>  			 RTM_NEWACTION, 0, 0) <= 0) {
> +		NL_SET_ERR_MSG(extack, "Failed to fill netlink tc action attributes");

see comment at @@ -975,6 +1000,7 @@

"Failed to fill netlink attributes while adding TC action"

>  		kfree_skb(skb);
>  		return -EINVAL;
>  	}
> @@ -1073,7 +1102,7 @@ static int tcf_action_add(struct net *net, struct nlattr *nla,
>  	if (ret)
>  		return ret;
>  
> -	return tcf_add_notify(net, n, &actions, portid);
> +	return tcf_add_notify(net, n, &actions, portid, extack);
>  }
>  
>  static u32 tcaa_root_flags_allowed = TCA_FLAG_LARGE_DUMP_ON;
> @@ -1101,7 +1130,7 @@ static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n,
>  		return ret;
>  
>  	if (tca[TCA_ACT_TAB] == NULL) {
> -		pr_notice("tc_ctl_action: received NO action attribs\n");
> +		NL_SET_ERR_MSG(extack, "Netlink Action attributes missing");

"action" is lowercase in almost all messages, and tipically prepended by
'TC' keyword:

"Missing netlink attributes for TC action"

>  		return -EINVAL;
>  	}
>  
> @@ -1124,11 +1153,11 @@ static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n,
>  		break;
>  	case RTM_DELACTION:
>  		ret = tca_action_gd(net, tca[TCA_ACT_TAB], n,
> -				    portid, RTM_DELACTION);
> +				    portid, RTM_DELACTION, extack);
>  		break;
>  	case RTM_GETACTION:
>  		ret = tca_action_gd(net, tca[TCA_ACT_TAB], n,
> -				    portid, RTM_GETACTION);
> +				    portid, RTM_GETACTION, extack);
>  		break;
>  	default:
>  		BUG();