|
@@ -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)
|
|
|
{
|
|
@@ -610,11 +611,11 @@ static void netlink_increment_head(struct netlink_ring *ring)
|
|
|
|
|
|
static void netlink_forward_ring(struct netlink_ring *ring)
|
|
|
{
|
|
|
- unsigned int head = ring->head, pos = head;
|
|
|
+ unsigned int head = ring->head;
|
|
|
const struct nl_mmap_hdr *hdr;
|
|
|
|
|
|
do {
|
|
|
- hdr = __netlink_lookup_frame(ring, pos);
|
|
|
+ hdr = __netlink_lookup_frame(ring, ring->head);
|
|
|
if (hdr->nm_status == NL_MMAP_STATUS_UNUSED)
|
|
|
break;
|
|
|
if (hdr->nm_status != NL_MMAP_STATUS_SKIP)
|
|
@@ -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;
|
|
|
}
|
|
@@ -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;
|
|
|
}
|
|
@@ -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),
|
|
@@ -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);
|