diff options
author | Pablo Neira Ayuso <pablo@netfilter.org> | 2009-02-17 20:40:43 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-02-20 04:01:08 -0500 |
commit | be0c22a46cfb79ab2342bb28fde99afa94ef868e (patch) | |
tree | da112ef3066d75a7e5adcfe3355ad2b60ef1929b | |
parent | 2d96cf8cdfd625da51e5d237d564cd75d3147547 (diff) |
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>
-rw-r--r-- | include/linux/netlink.h | 1 | ||||
-rw-r--r-- | net/netlink/af_netlink.c | 25 |
2 files changed, 24 insertions, 2 deletions
diff --git a/include/linux/netlink.h b/include/linux/netlink.h index 51b09a1f46c3..1e6bf995435c 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h | |||
@@ -103,6 +103,7 @@ struct nlmsgerr | |||
103 | #define NETLINK_ADD_MEMBERSHIP 1 | 103 | #define NETLINK_ADD_MEMBERSHIP 1 |
104 | #define NETLINK_DROP_MEMBERSHIP 2 | 104 | #define NETLINK_DROP_MEMBERSHIP 2 |
105 | #define NETLINK_PKTINFO 3 | 105 | #define NETLINK_PKTINFO 3 |
106 | #define NETLINK_BROADCAST_ERROR 4 | ||
106 | 107 | ||
107 | struct nl_pktinfo | 108 | struct nl_pktinfo |
108 | { | 109 | { |
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 6ee69c27f806..ed587be1e1c2 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c | |||
@@ -85,6 +85,7 @@ struct netlink_sock { | |||
85 | 85 | ||
86 | #define NETLINK_KERNEL_SOCKET 0x1 | 86 | #define NETLINK_KERNEL_SOCKET 0x1 |
87 | #define NETLINK_RECV_PKTINFO 0x2 | 87 | #define NETLINK_RECV_PKTINFO 0x2 |
88 | #define NETLINK_BROADCAST_SEND_ERROR 0x4 | ||
88 | 89 | ||
89 | static inline struct netlink_sock *nlk_sk(struct sock *sk) | 90 | static inline struct netlink_sock *nlk_sk(struct sock *sk) |
90 | { | 91 | { |
@@ -995,12 +996,15 @@ static inline int do_one_broadcast(struct sock *sk, | |||
995 | netlink_overrun(sk); | 996 | netlink_overrun(sk); |
996 | /* Clone failed. Notify ALL listeners. */ | 997 | /* Clone failed. Notify ALL listeners. */ |
997 | p->failure = 1; | 998 | p->failure = 1; |
999 | if (nlk->flags & NETLINK_BROADCAST_SEND_ERROR) | ||
1000 | p->delivery_failure = 1; | ||
998 | } else if (sk_filter(sk, p->skb2)) { | 1001 | } else if (sk_filter(sk, p->skb2)) { |
999 | kfree_skb(p->skb2); | 1002 | kfree_skb(p->skb2); |
1000 | p->skb2 = NULL; | 1003 | p->skb2 = NULL; |
1001 | } else if ((val = netlink_broadcast_deliver(sk, p->skb2)) < 0) { | 1004 | } else if ((val = netlink_broadcast_deliver(sk, p->skb2)) < 0) { |
1002 | netlink_overrun(sk); | 1005 | netlink_overrun(sk); |
1003 | p->delivery_failure = 1; | 1006 | if (nlk->flags & NETLINK_BROADCAST_SEND_ERROR) |
1007 | p->delivery_failure = 1; | ||
1004 | } else { | 1008 | } else { |
1005 | p->congested |= val; | 1009 | p->congested |= val; |
1006 | p->delivered = 1; | 1010 | p->delivered = 1; |
@@ -1048,7 +1052,7 @@ int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid, | |||
1048 | if (info.skb2) | 1052 | if (info.skb2) |
1049 | kfree_skb(info.skb2); | 1053 | kfree_skb(info.skb2); |
1050 | 1054 | ||
1051 | if (info.delivery_failure || info.failure) | 1055 | if (info.delivery_failure) |
1052 | return -ENOBUFS; | 1056 | return -ENOBUFS; |
1053 | 1057 | ||
1054 | if (info.delivered) { | 1058 | if (info.delivered) { |
@@ -1163,6 +1167,13 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname, | |||
1163 | err = 0; | 1167 | err = 0; |
1164 | break; | 1168 | break; |
1165 | } | 1169 | } |
1170 | case NETLINK_BROADCAST_ERROR: | ||
1171 | if (val) | ||
1172 | nlk->flags |= NETLINK_BROADCAST_SEND_ERROR; | ||
1173 | else | ||
1174 | nlk->flags &= ~NETLINK_BROADCAST_SEND_ERROR; | ||
1175 | err = 0; | ||
1176 | break; | ||
1166 | default: | 1177 | default: |
1167 | err = -ENOPROTOOPT; | 1178 | err = -ENOPROTOOPT; |
1168 | } | 1179 | } |
@@ -1195,6 +1206,16 @@ static int netlink_getsockopt(struct socket *sock, int level, int optname, | |||
1195 | return -EFAULT; | 1206 | return -EFAULT; |
1196 | err = 0; | 1207 | err = 0; |
1197 | break; | 1208 | break; |
1209 | case NETLINK_BROADCAST_ERROR: | ||
1210 | if (len < sizeof(int)) | ||
1211 | return -EINVAL; | ||
1212 | len = sizeof(int); | ||
1213 | val = nlk->flags & NETLINK_BROADCAST_SEND_ERROR ? 1 : 0; | ||
1214 | if (put_user(len, optlen) || | ||
1215 | put_user(val, optval)) | ||
1216 | return -EFAULT; | ||
1217 | err = 0; | ||
1218 | break; | ||
1198 | default: | 1219 | default: |
1199 | err = -ENOPROTOOPT; | 1220 | err = -ENOPROTOOPT; |
1200 | } | 1221 | } |