mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-28 18:51:56 -04:00
netlink: extended ACK reporting
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]>
This commit is contained in:
parent
fb9eb899a6
commit
2d4bc93368
17 changed files with 153 additions and 34 deletions
|
|
@ -1652,6 +1652,13 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname,
|
|||
nlk->flags &= ~NETLINK_F_CAP_ACK;
|
||||
err = 0;
|
||||
break;
|
||||
case NETLINK_EXT_ACK:
|
||||
if (val)
|
||||
nlk->flags |= NETLINK_F_EXT_ACK;
|
||||
else
|
||||
nlk->flags &= ~NETLINK_F_EXT_ACK;
|
||||
err = 0;
|
||||
break;
|
||||
default:
|
||||
err = -ENOPROTOOPT;
|
||||
}
|
||||
|
|
@ -1736,6 +1743,15 @@ static int netlink_getsockopt(struct socket *sock, int level, int optname,
|
|||
return -EFAULT;
|
||||
err = 0;
|
||||
break;
|
||||
case NETLINK_EXT_ACK:
|
||||
if (len < sizeof(int))
|
||||
return -EINVAL;
|
||||
len = sizeof(int);
|
||||
val = nlk->flags & NETLINK_F_EXT_ACK ? 1 : 0;
|
||||
if (put_user(len, optlen) || put_user(val, optval))
|
||||
return -EFAULT;
|
||||
err = 0;
|
||||
break;
|
||||
default:
|
||||
err = -ENOPROTOOPT;
|
||||
}
|
||||
|
|
@ -2267,21 +2283,40 @@ error_free:
|
|||
}
|
||||
EXPORT_SYMBOL(__netlink_dump_start);
|
||||
|
||||
void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err)
|
||||
void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err,
|
||||
const struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct nlmsghdr *rep;
|
||||
struct nlmsgerr *errmsg;
|
||||
size_t payload = sizeof(*errmsg);
|
||||
size_t tlvlen = 0;
|
||||
struct netlink_sock *nlk = nlk_sk(NETLINK_CB(in_skb).sk);
|
||||
unsigned int flags = 0;
|
||||
|
||||
/* Error messages get the original request appened, unless the user
|
||||
* requests to cap the error message.
|
||||
* requests to cap the error message, and get extra error data if
|
||||
* requested.
|
||||
*/
|
||||
if (!(nlk->flags & NETLINK_F_CAP_ACK) && err)
|
||||
payload += nlmsg_len(nlh);
|
||||
if (err) {
|
||||
if (!(nlk->flags & NETLINK_F_CAP_ACK))
|
||||
payload += nlmsg_len(nlh);
|
||||
else
|
||||
flags |= NLM_F_CAPPED;
|
||||
if (nlk->flags & NETLINK_F_EXT_ACK && extack) {
|
||||
if (extack->_msg)
|
||||
tlvlen += nla_total_size(strlen(extack->_msg) + 1);
|
||||
if (extack->bad_attr)
|
||||
tlvlen += nla_total_size(sizeof(u32));
|
||||
}
|
||||
} else {
|
||||
flags |= NLM_F_CAPPED;
|
||||
}
|
||||
|
||||
skb = nlmsg_new(payload, GFP_KERNEL);
|
||||
if (tlvlen)
|
||||
flags |= NLM_F_ACK_TLVS;
|
||||
|
||||
skb = nlmsg_new(payload + tlvlen, GFP_KERNEL);
|
||||
if (!skb) {
|
||||
struct sock *sk;
|
||||
|
||||
|
|
@ -2297,17 +2332,35 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err)
|
|||
}
|
||||
|
||||
rep = __nlmsg_put(skb, NETLINK_CB(in_skb).portid, nlh->nlmsg_seq,
|
||||
NLMSG_ERROR, payload, 0);
|
||||
NLMSG_ERROR, payload, flags);
|
||||
errmsg = nlmsg_data(rep);
|
||||
errmsg->error = err;
|
||||
memcpy(&errmsg->msg, nlh, payload > sizeof(*errmsg) ? nlh->nlmsg_len : sizeof(*nlh));
|
||||
|
||||
if (err && nlk->flags & NETLINK_F_EXT_ACK && extack) {
|
||||
if (extack->_msg)
|
||||
WARN_ON(nla_put_string(skb, NLMSGERR_ATTR_MSG,
|
||||
extack->_msg));
|
||||
if (extack->bad_attr &&
|
||||
!WARN_ON((u8 *)extack->bad_attr < in_skb->data ||
|
||||
(u8 *)extack->bad_attr >= in_skb->data +
|
||||
in_skb->len))
|
||||
WARN_ON(nla_put_u32(skb, NLMSGERR_ATTR_OFFS,
|
||||
(u8 *)extack->bad_attr -
|
||||
in_skb->data));
|
||||
}
|
||||
|
||||
nlmsg_end(skb, rep);
|
||||
|
||||
netlink_unicast(in_skb->sk, skb, NETLINK_CB(in_skb).portid, MSG_DONTWAIT);
|
||||
}
|
||||
EXPORT_SYMBOL(netlink_ack);
|
||||
|
||||
int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *,
|
||||
struct nlmsghdr *))
|
||||
struct nlmsghdr *,
|
||||
struct netlink_ext_ack *))
|
||||
{
|
||||
struct netlink_ext_ack extack = {};
|
||||
struct nlmsghdr *nlh;
|
||||
int err;
|
||||
|
||||
|
|
@ -2328,13 +2381,13 @@ int netlink_rcv_skb(struct sk_buff *skb, int (*cb)(struct sk_buff *,
|
|||
if (nlh->nlmsg_type < NLMSG_MIN_TYPE)
|
||||
goto ack;
|
||||
|
||||
err = cb(skb, nlh);
|
||||
err = cb(skb, nlh, &extack);
|
||||
if (err == -EINTR)
|
||||
goto skip;
|
||||
|
||||
ack:
|
||||
if (nlh->nlmsg_flags & NLM_F_ACK || err)
|
||||
netlink_ack(skb, nlh, err);
|
||||
netlink_ack(skb, nlh, err, &extack);
|
||||
|
||||
skip:
|
||||
msglen = NLMSG_ALIGN(nlh->nlmsg_len);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#define NETLINK_F_RECV_NO_ENOBUFS 0x8
|
||||
#define NETLINK_F_LISTEN_ALL_NSID 0x10
|
||||
#define NETLINK_F_CAP_ACK 0x20
|
||||
#define NETLINK_F_EXT_ACK 0x40
|
||||
|
||||
#define NLGRPSZ(x) (ALIGN(x, sizeof(unsigned long) * 8) / 8)
|
||||
#define NLGRPLONGS(x) (NLGRPSZ(x)/sizeof(unsigned long))
|
||||
|
|
|
|||
|
|
@ -605,7 +605,8 @@ out:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
|
||||
static int genl_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
const struct genl_family *family;
|
||||
int err;
|
||||
|
|
|
|||
Loading…
Reference in a new issue