netlink: add NETLINK_BROADCAST_ERROR socket option
This patch adds NETLINK_BROADCAST_ERROR which is a netlink socket option that the listener can set to make netlink_broadcast() return errors in the delivery to the caller. This option is useful if the caller of netlink_broadcast() do something with the result of the message delivery, like in ctnetlink where it drops a network packet if the event delivery failed, this is used to enable reliable logging and state-synchronization. If this socket option is not set, netlink_broadcast() only reports ESRCH errors and silently ignore ENOBUFS errors, which is what most netlink_broadcast() callers should do. This socket option is based on a suggestion from Patrick McHardy. Patrick McHardy can exchange this patch for a beer from me ;). Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> Acked-by: Patrick McHardy <kaber@trash.net> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Родитель
2d96cf8cdf
Коммит
be0c22a46c
|
@ -103,6 +103,7 @@ struct nlmsgerr
|
|||
#define NETLINK_ADD_MEMBERSHIP 1
|
||||
#define NETLINK_DROP_MEMBERSHIP 2
|
||||
#define NETLINK_PKTINFO 3
|
||||
#define NETLINK_BROADCAST_ERROR 4
|
||||
|
||||
struct nl_pktinfo
|
||||
{
|
||||
|
|
|
@ -85,6 +85,7 @@ struct netlink_sock {
|
|||
|
||||
#define NETLINK_KERNEL_SOCKET 0x1
|
||||
#define NETLINK_RECV_PKTINFO 0x2
|
||||
#define NETLINK_BROADCAST_SEND_ERROR 0x4
|
||||
|
||||
static inline struct netlink_sock *nlk_sk(struct sock *sk)
|
||||
{
|
||||
|
@ -995,11 +996,14 @@ static inline int do_one_broadcast(struct sock *sk,
|
|||
netlink_overrun(sk);
|
||||
/* Clone failed. Notify ALL listeners. */
|
||||
p->failure = 1;
|
||||
if (nlk->flags & NETLINK_BROADCAST_SEND_ERROR)
|
||||
p->delivery_failure = 1;
|
||||
} else if (sk_filter(sk, p->skb2)) {
|
||||
kfree_skb(p->skb2);
|
||||
p->skb2 = NULL;
|
||||
} else if ((val = netlink_broadcast_deliver(sk, p->skb2)) < 0) {
|
||||
netlink_overrun(sk);
|
||||
if (nlk->flags & NETLINK_BROADCAST_SEND_ERROR)
|
||||
p->delivery_failure = 1;
|
||||
} else {
|
||||
p->congested |= val;
|
||||
|
@ -1048,7 +1052,7 @@ int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid,
|
|||
if (info.skb2)
|
||||
kfree_skb(info.skb2);
|
||||
|
||||
if (info.delivery_failure || info.failure)
|
||||
if (info.delivery_failure)
|
||||
return -ENOBUFS;
|
||||
|
||||
if (info.delivered) {
|
||||
|
@ -1163,6 +1167,13 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname,
|
|||
err = 0;
|
||||
break;
|
||||
}
|
||||
case NETLINK_BROADCAST_ERROR:
|
||||
if (val)
|
||||
nlk->flags |= NETLINK_BROADCAST_SEND_ERROR;
|
||||
else
|
||||
nlk->flags &= ~NETLINK_BROADCAST_SEND_ERROR;
|
||||
err = 0;
|
||||
break;
|
||||
default:
|
||||
err = -ENOPROTOOPT;
|
||||
}
|
||||
|
@ -1195,6 +1206,16 @@ static int netlink_getsockopt(struct socket *sock, int level, int optname,
|
|||
return -EFAULT;
|
||||
err = 0;
|
||||
break;
|
||||
case NETLINK_BROADCAST_ERROR:
|
||||
if (len < sizeof(int))
|
||||
return -EINVAL;
|
||||
len = sizeof(int);
|
||||
val = nlk->flags & NETLINK_BROADCAST_SEND_ERROR ? 1 : 0;
|
||||
if (put_user(len, optlen) ||
|
||||
put_user(val, optval))
|
||||
return -EFAULT;
|
||||
err = 0;
|
||||
break;
|
||||
default:
|
||||
err = -ENOPROTOOPT;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче