Skip to content

Commit

Permalink
netlink: extended ACK reporting
Browse files Browse the repository at this point in the history
Add the base infrastructure and UAPI for netlink extended ACK
reporting. All "manual" calls to netlink_ack() pass NULL for now and
thus don't get extended ACK reporting.

Big thanks goes to Pablo Neira Ayuso for not only bringing up the
whole topic at netconf (again) but also coming up with the nlattr
passing trick and various other ideas.

Signed-off-by: Johannes Berg <[email protected]>
Reviewed-by: David Ahern <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
jmberg-intel authored and davem330 committed Apr 13, 2017
1 parent fb9eb89 commit 2d4bc93
Show file tree
Hide file tree
Showing 17 changed files with 153 additions and 34 deletions.
3 changes: 2 additions & 1 deletion crypto/crypto_user.c
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,8 @@ static const struct crypto_link {
[CRYPTO_MSG_DELRNG - CRYPTO_MSG_BASE] = { .doit = crypto_del_rng },
};

static int crypto_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
static int crypto_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
struct netlink_ext_ack *extack)
{
struct nlattr *attrs[CRYPTOCFGA_MAX+1];
const struct crypto_link *link;
Expand Down
5 changes: 3 additions & 2 deletions drivers/infiniband/core/netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,8 @@ int ibnl_put_attr(struct sk_buff *skb, struct nlmsghdr *nlh,
}
EXPORT_SYMBOL(ibnl_put_attr);

static int ibnl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
static int ibnl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
struct netlink_ext_ack *extack)
{
struct ibnl_client *client;
int type = nlh->nlmsg_type;
Expand Down Expand Up @@ -209,7 +210,7 @@ static void ibnl_rcv_reply_skb(struct sk_buff *skb)
if (nlh->nlmsg_flags & NLM_F_REQUEST)
return;

ibnl_rcv_msg(skb, nlh);
ibnl_rcv_msg(skb, nlh, NULL);

msglen = NLMSG_ALIGN(nlh->nlmsg_len);
if (msglen > skb->len)
Expand Down
2 changes: 1 addition & 1 deletion drivers/scsi/scsi_netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ scsi_nl_rcv_msg(struct sk_buff *skb)

next_msg:
if ((err) || (nlh->nlmsg_flags & NLM_F_ACK))
netlink_ack(skb, nlh, err);
netlink_ack(skb, nlh, err, NULL);

skb_pull(skb, rlen);
}
Expand Down
26 changes: 25 additions & 1 deletion include/linux/netlink.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,35 @@ netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg)
return __netlink_kernel_create(net, unit, THIS_MODULE, cfg);
}

/**
* struct netlink_ext_ack - netlink extended ACK report struct
* @_msg: message string to report - don't access directly, use
* %NL_SET_ERR_MSG
* @bad_attr: attribute with error
*/
struct netlink_ext_ack {
const char *_msg;
const struct nlattr *bad_attr;
};

/* Always use this macro, this allows later putting the
* message into a separate section or such for things
* like translation or listing all possible messages.
* Currently string formatting is not supported (due
* to the lack of an output buffer.)
*/
#define NL_SET_ERR_MSG(extack, msg) do { \
static const char _msg[] = (msg); \
\
(extack)->_msg = _msg; \
} while (0)

extern void netlink_kernel_release(struct sock *sk);
extern int __netlink_change_ngroups(struct sock *sk, unsigned int groups);
extern int netlink_change_ngroups(struct sock *sk, unsigned int groups);
extern void __netlink_clear_multicast_users(struct sock *sk, unsigned int group);
extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err);
extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err,
const struct netlink_ext_ack *extack);
extern int netlink_has_listeners(struct sock *sk, unsigned int group);

extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 portid, int nonblock);
Expand Down
3 changes: 2 additions & 1 deletion include/net/netlink.h
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,8 @@ struct nl_info {
};

int netlink_rcv_skb(struct sk_buff *skb,
int (*cb)(struct sk_buff *, struct nlmsghdr *));
int (*cb)(struct sk_buff *, struct nlmsghdr *,
struct netlink_ext_ack *));
int nlmsg_notify(struct sock *sk, struct sk_buff *skb, u32 portid,
unsigned int group, int report, gfp_t flags);

Expand Down
32 changes: 32 additions & 0 deletions include/uapi/linux/netlink.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ struct nlmsghdr {
#define NLM_F_CREATE 0x400 /* Create, if it does not exist */
#define NLM_F_APPEND 0x800 /* Add to end of list */

/* Flags for ACK message */
#define NLM_F_CAPPED 0x100 /* request was capped */
#define NLM_F_ACK_TLVS 0x200 /* extended ACK TVLs were included */

/*
4.4BSD ADD NLM_F_CREATE|NLM_F_EXCL
4.4BSD CHANGE NLM_F_REPLACE
Expand Down Expand Up @@ -101,6 +105,33 @@ struct nlmsghdr {
struct nlmsgerr {
int error;
struct nlmsghdr msg;
/*
* followed by the message contents unless NETLINK_CAP_ACK was set
* or the ACK indicates success (error == 0)
* message length is aligned with NLMSG_ALIGN()
*/
/*
* followed by TLVs defined in enum nlmsgerr_attrs
* if NETLINK_EXT_ACK was set
*/
};

/**
* enum nlmsgerr_attrs - nlmsgerr attributes
* @NLMSGERR_ATTR_UNUSED: unused
* @NLMSGERR_ATTR_MSG: error message string (string)
* @NLMSGERR_ATTR_OFFS: offset of the invalid attribute in the original
* message, counting from the beginning of the header (u32)
* @__NLMSGERR_ATTR_MAX: number of attributes
* @NLMSGERR_ATTR_MAX: highest attribute number
*/
enum nlmsgerr_attrs {
NLMSGERR_ATTR_UNUSED,
NLMSGERR_ATTR_MSG,
NLMSGERR_ATTR_OFFS,

__NLMSGERR_ATTR_MAX,
NLMSGERR_ATTR_MAX = __NLMSGERR_ATTR_MAX - 1
};

#define NETLINK_ADD_MEMBERSHIP 1
Expand All @@ -115,6 +146,7 @@ struct nlmsgerr {
#define NETLINK_LISTEN_ALL_NSID 8
#define NETLINK_LIST_MEMBERSHIPS 9
#define NETLINK_CAP_ACK 10
#define NETLINK_EXT_ACK 11

struct nl_pktinfo {
__u32 group;
Expand Down
2 changes: 1 addition & 1 deletion kernel/audit.c
Original file line number Diff line number Diff line change
Expand Up @@ -1402,7 +1402,7 @@ static void audit_receive_skb(struct sk_buff *skb)
err = audit_receive_msg(skb, nlh);
/* if err or if this message says it wants a response */
if (err || (nlh->nlmsg_flags & NLM_F_ACK))
netlink_ack(skb, nlh, err);
netlink_ack(skb, nlh, err, NULL);

nlh = nlmsg_next(nlh, &len);
}
Expand Down
3 changes: 2 additions & 1 deletion net/core/rtnetlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -4046,7 +4046,8 @@ static int rtnl_stats_dump(struct sk_buff *skb, struct netlink_callback *cb)

/* Process one rtnetlink message. */

static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
struct netlink_ext_ack *extack)
{
struct net *net = sock_net(skb->sk);
rtnl_doit_func doit;
Expand Down
3 changes: 2 additions & 1 deletion net/core/sock_diag.c
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,8 @@ static int __sock_diag_cmd(struct sk_buff *skb, struct nlmsghdr *nlh)
return err;
}

static int sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
static int sock_diag_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
struct netlink_ext_ack *extack)
{
int ret;

Expand Down
2 changes: 1 addition & 1 deletion net/decnet/netfilter/dn_rtmsg.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ static unsigned int dnrmg_hook(void *priv,
}


#define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0)
#define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err), NULL); return; } while (0)

static inline void dnrmg_receive_user_skb(struct sk_buff *skb)
{
Expand Down
4 changes: 2 additions & 2 deletions net/hsr/hsr_netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ static int hsr_get_node_status(struct sk_buff *skb_in, struct genl_info *info)
return 0;

invalid:
netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL);
netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL, NULL);
return 0;

nla_put_failure:
Expand Down Expand Up @@ -432,7 +432,7 @@ static int hsr_get_node_list(struct sk_buff *skb_in, struct genl_info *info)
return 0;

invalid:
netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL);
netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL, NULL);
return 0;

nla_put_failure:
Expand Down
2 changes: 1 addition & 1 deletion net/netfilter/ipset/ip_set_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -1305,7 +1305,7 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb)
* manually :-(
*/
if (nlh->nlmsg_flags & NLM_F_ACK)
netlink_ack(cb->skb, nlh, ret);
netlink_ack(cb->skb, nlh, ret, NULL);
return ret;
}
}
Expand Down
22 changes: 12 additions & 10 deletions net/netfilter/nfnetlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,8 @@ int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u32 portid,
EXPORT_SYMBOL_GPL(nfnetlink_unicast);

/* Process one complete nfnetlink message. */
static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
struct netlink_ext_ack *extack)
{
struct net *net = sock_net(skb->sk);
const struct nfnl_callback *nc;
Expand Down Expand Up @@ -261,7 +262,7 @@ static void nfnl_err_deliver(struct list_head *err_list, struct sk_buff *skb)
struct nfnl_err *nfnl_err, *next;

list_for_each_entry_safe(nfnl_err, next, err_list, head) {
netlink_ack(skb, nfnl_err->nlh, nfnl_err->err);
netlink_ack(skb, nfnl_err->nlh, nfnl_err->err, NULL);
nfnl_err_del(nfnl_err);
}
}
Expand All @@ -284,13 +285,13 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
int err;

if (subsys_id >= NFNL_SUBSYS_COUNT)
return netlink_ack(skb, nlh, -EINVAL);
return netlink_ack(skb, nlh, -EINVAL, NULL);
replay:
status = 0;

skb = netlink_skb_clone(oskb, GFP_KERNEL);
if (!skb)
return netlink_ack(oskb, nlh, -ENOMEM);
return netlink_ack(oskb, nlh, -ENOMEM, NULL);

nfnl_lock(subsys_id);
ss = nfnl_dereference_protected(subsys_id);
Expand All @@ -304,20 +305,20 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
#endif
{
nfnl_unlock(subsys_id);
netlink_ack(oskb, nlh, -EOPNOTSUPP);
netlink_ack(oskb, nlh, -EOPNOTSUPP, NULL);
return kfree_skb(skb);
}
}

if (!ss->commit || !ss->abort) {
nfnl_unlock(subsys_id);
netlink_ack(oskb, nlh, -EOPNOTSUPP);
netlink_ack(oskb, nlh, -EOPNOTSUPP, NULL);
return kfree_skb(skb);
}

if (genid && ss->valid_genid && !ss->valid_genid(net, genid)) {
nfnl_unlock(subsys_id);
netlink_ack(oskb, nlh, -ERESTART);
netlink_ack(oskb, nlh, -ERESTART, NULL);
return kfree_skb(skb);
}

Expand Down Expand Up @@ -407,7 +408,8 @@ static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh,
* pointing to the batch header.
*/
nfnl_err_reset(&err_list);
netlink_ack(oskb, nlmsg_hdr(oskb), -ENOMEM);
netlink_ack(oskb, nlmsg_hdr(oskb), -ENOMEM,
NULL);
status |= NFNL_BATCH_FAILURE;
goto done;
}
Expand Down Expand Up @@ -467,7 +469,7 @@ static void nfnetlink_rcv_skb_batch(struct sk_buff *skb, struct nlmsghdr *nlh)

err = nla_parse(cda, NFNL_BATCH_MAX, attr, attrlen, nfnl_batch_policy);
if (err < 0) {
netlink_ack(skb, nlh, err);
netlink_ack(skb, nlh, err, NULL);
return;
}
if (cda[NFNL_BATCH_GENID])
Expand All @@ -493,7 +495,7 @@ static void nfnetlink_rcv(struct sk_buff *skb)
return;

if (!netlink_net_capable(skb, CAP_NET_ADMIN)) {
netlink_ack(skb, nlh, -EPERM);
netlink_ack(skb, nlh, -EPERM, NULL);
return;
}

Expand Down
Loading

0 comments on commit 2d4bc93

Please sign in to comment.