Skip to content

Commit

Permalink
netlink: add NETLINK_CAP_ACK socket option
Browse files Browse the repository at this point in the history
Since commit c05cdb1 ("netlink: allow large data transfers from
user-space"), the kernel may fail to allocate the necessary room for the
acknowledgment message back to userspace. This patch introduces a new
socket option that trims off the payload of the original netlink message.

The netlink message header is still included, so the user can guess from
the sequence number what is the message that has triggered the
acknowledgment.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Christophe Ricard <christophe-h.ricard@st.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Christophe Ricard authored and David S. Miller committed Aug 29, 2015
1 parent 4941b8f commit 0a6a3a2
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 3 deletions.
1 change: 1 addition & 0 deletions include/uapi/linux/netlink.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ struct nlmsgerr {
#define NETLINK_TX_RING 7
#define NETLINK_LISTEN_ALL_NSID 8
#define NETLINK_LIST_MEMBERSHIPS 9
#define NETLINK_CAP_ACK 10

struct nl_pktinfo {
__u32 group;
Expand Down
27 changes: 24 additions & 3 deletions net/netlink/af_netlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ struct listeners {
#define NETLINK_F_BROADCAST_SEND_ERROR 0x4
#define NETLINK_F_RECV_NO_ENOBUFS 0x8
#define NETLINK_F_LISTEN_ALL_NSID 0x10
#define NETLINK_F_CAP_ACK 0x20

static inline int netlink_is_kernel(struct sock *sk)
{
Expand Down Expand Up @@ -2258,6 +2259,13 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname,
nlk->flags &= ~NETLINK_F_LISTEN_ALL_NSID;
err = 0;
break;
case NETLINK_CAP_ACK:
if (val)
nlk->flags |= NETLINK_F_CAP_ACK;
else
nlk->flags &= ~NETLINK_F_CAP_ACK;
err = 0;
break;
default:
err = -ENOPROTOOPT;
}
Expand Down Expand Up @@ -2332,6 +2340,16 @@ static int netlink_getsockopt(struct socket *sock, int level, int optname,
netlink_table_ungrab();
break;
}
case NETLINK_CAP_ACK:
if (len < sizeof(int))
return -EINVAL;
len = sizeof(int);
val = nlk->flags & NETLINK_F_CAP_ACK ? 1 : 0;
if (put_user(len, optlen) ||
put_user(val, optval))
return -EFAULT;
err = 0;
break;
default:
err = -ENOPROTOOPT;
}
Expand Down Expand Up @@ -2873,9 +2891,12 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err)
struct nlmsghdr *rep;
struct nlmsgerr *errmsg;
size_t payload = sizeof(*errmsg);
struct netlink_sock *nlk = nlk_sk(NETLINK_CB(in_skb).sk);

/* error messages get the original request appened */
if (err)
/* Error messages get the original request appened, unless the user
* requests to cap the error message.
*/
if (!(nlk->flags & NETLINK_F_CAP_ACK) && err)
payload += nlmsg_len(nlh);

skb = netlink_alloc_skb(in_skb->sk, nlmsg_total_size(payload),
Expand All @@ -2898,7 +2919,7 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err)
NLMSG_ERROR, payload, 0);
errmsg = nlmsg_data(rep);
errmsg->error = err;
memcpy(&errmsg->msg, nlh, err ? nlh->nlmsg_len : sizeof(*nlh));
memcpy(&errmsg->msg, nlh, payload > sizeof(*errmsg) ? nlh->nlmsg_len : sizeof(*nlh));
netlink_unicast(in_skb->sk, skb, NETLINK_CB(in_skb).portid, MSG_DONTWAIT);
}
EXPORT_SYMBOL(netlink_ack);
Expand Down

0 comments on commit 0a6a3a2

Please sign in to comment.