Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0a6a3a23 authored by Christophe Ricard's avatar Christophe Ricard Committed by David S. Miller
Browse files

netlink: add NETLINK_CAP_ACK socket option



Since commit c05cdb1b ("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: default avatarPablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: default avatarChristophe Ricard <christophe-h.ricard@st.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 4941b8f0
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -110,6 +110,7 @@ struct nlmsgerr {
#define NETLINK_TX_RING			7
#define NETLINK_TX_RING			7
#define NETLINK_LISTEN_ALL_NSID		8
#define NETLINK_LISTEN_ALL_NSID		8
#define NETLINK_LIST_MEMBERSHIPS	9
#define NETLINK_LIST_MEMBERSHIPS	9
#define NETLINK_CAP_ACK			10


struct nl_pktinfo {
struct nl_pktinfo {
	__u32	group;
	__u32	group;
+24 −3
Original line number Original line Diff line number Diff line
@@ -84,6 +84,7 @@ struct listeners {
#define NETLINK_F_BROADCAST_SEND_ERROR	0x4
#define NETLINK_F_BROADCAST_SEND_ERROR	0x4
#define NETLINK_F_RECV_NO_ENOBUFS	0x8
#define NETLINK_F_RECV_NO_ENOBUFS	0x8
#define NETLINK_F_LISTEN_ALL_NSID	0x10
#define NETLINK_F_LISTEN_ALL_NSID	0x10
#define NETLINK_F_CAP_ACK		0x20


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


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


	skb = netlink_alloc_skb(in_skb->sk, nlmsg_total_size(payload),
	skb = netlink_alloc_skb(in_skb->sk, nlmsg_total_size(payload),
@@ -2898,7 +2919,7 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err)
			  NLMSG_ERROR, payload, 0);
			  NLMSG_ERROR, payload, 0);
	errmsg = nlmsg_data(rep);
	errmsg = nlmsg_data(rep);
	errmsg->error = err;
	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);
	netlink_unicast(in_skb->sk, skb, NETLINK_CB(in_skb).portid, MSG_DONTWAIT);
}
}
EXPORT_SYMBOL(netlink_ack);
EXPORT_SYMBOL(netlink_ack);