netlink: add NETLINK_BROADCAST_ERROR socket option
authorPablo Neira Ayuso <pablo@netfilter.org>
Wed, 18 Feb 2009 01:40:43 +0000 (01:40 +0000)
committerDavid S. Miller <davem@davemloft.net>
Fri, 20 Feb 2009 09:01:08 +0000 (01:01 -0800)
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>
include/linux/netlink.h
net/netlink/af_netlink.c

index 51b09a1..1e6bf99 100644 (file)
@@ -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
 {
index 6ee69c2..ed587be 100644 (file)
@@ -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,12 +996,15 @@ 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);
-               p->delivery_failure = 1;
+               if (nlk->flags & NETLINK_BROADCAST_SEND_ERROR)
+                       p->delivery_failure = 1;
        } else {
                p->congested |= val;
                p->delivered = 1;
@@ -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;
        }