Commit ff491a73 authored by Pablo Neira Ayuso's avatar Pablo Neira Ayuso Committed by David S. Miller

netlink: change return-value logic of netlink_broadcast()

Currently, netlink_broadcast() reports errors to the caller if no
messages at all were delivered:

1) If, at least, one message has been delivered correctly, returns 0.
2) Otherwise, if no messages at all were delivered due to skb_clone()
   failure, return -ENOBUFS.
3) Otherwise, if there are no listeners, return -ESRCH.

With this patch, the caller knows if the delivery of any of the
messages to the listeners have failed:

1) If it fails to deliver any message (for whatever reason), return
   -ENOBUFS.
2) Otherwise, if all messages were delivered OK, returns 0.
3) Otherwise, if no listeners, return -ESRCH.

In the current ctnetlink code and in Netfilter in general, we can add
reliable logging and connection tracking event delivery by dropping the
packets whose events were not successfully delivered over Netlink. Of
course, this option would be settable via /proc as this approach reduces
performance (in terms of filtered connections per seconds by a stateful
firewall) but providing reliable logging and event delivery (for
conntrackd) in return.

This patch also changes some clients of netlink_broadcast() that
may report ENOBUFS errors via printk. This error handling is not
of any help. Instead, the userspace daemons that are listening to
those netlink messages should resync themselves with the kernel-side
if they hit ENOBUFS.

BTW, netlink_broadcast() clients include those that call
cn_netlink_send(), nlmsg_multicast() and genlmsg_multicast() since they
internally call netlink_broadcast() and return its error value.
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 612e244c
...@@ -235,11 +235,7 @@ int acpi_bus_generate_netlink_event(const char *device_class, ...@@ -235,11 +235,7 @@ int acpi_bus_generate_netlink_event(const char *device_class,
return result; return result;
} }
result = genlmsg_multicast(skb, 0, acpi_event_mcgrp.id, GFP_ATOMIC);
genlmsg_multicast(skb, 0, acpi_event_mcgrp.id, GFP_ATOMIC);
if (result)
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Failed to send a Genetlink message!\n"));
return 0; return 0;
} }
......
...@@ -533,12 +533,8 @@ fc_host_post_event(struct Scsi_Host *shost, u32 event_number, ...@@ -533,12 +533,8 @@ fc_host_post_event(struct Scsi_Host *shost, u32 event_number,
event->event_code = event_code; event->event_code = event_code;
event->event_data = event_data; event->event_data = event_data;
err = nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_FC_EVENTS, nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_FC_EVENTS,
GFP_KERNEL); GFP_KERNEL);
if (err && (err != -ESRCH)) /* filter no recipient errors */
/* nlmsg_multicast already kfree_skb'd */
goto send_fail;
return; return;
send_fail_skb: send_fail_skb:
...@@ -607,12 +603,8 @@ fc_host_post_vendor_event(struct Scsi_Host *shost, u32 event_number, ...@@ -607,12 +603,8 @@ fc_host_post_vendor_event(struct Scsi_Host *shost, u32 event_number,
event->event_code = FCH_EVT_VENDOR_UNIQUE; event->event_code = FCH_EVT_VENDOR_UNIQUE;
memcpy(&event->event_data, data_buf, data_len); memcpy(&event->event_data, data_buf, data_len);
err = nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_FC_EVENTS, nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_FC_EVENTS,
GFP_KERNEL); GFP_KERNEL);
if (err && (err != -ESRCH)) /* filter no recipient errors */
/* nlmsg_multicast already kfree_skb'd */
goto send_vendor_fail;
return; return;
send_vendor_fail_skb: send_vendor_fail_skb:
......
...@@ -966,15 +966,7 @@ iscsi_if_transport_lookup(struct iscsi_transport *tt) ...@@ -966,15 +966,7 @@ iscsi_if_transport_lookup(struct iscsi_transport *tt)
static int static int
iscsi_broadcast_skb(struct sk_buff *skb, gfp_t gfp) iscsi_broadcast_skb(struct sk_buff *skb, gfp_t gfp)
{ {
int rc; return netlink_broadcast(nls, skb, 0, 1, gfp);
rc = netlink_broadcast(nls, skb, 0, 1, gfp);
if (rc < 0) {
printk(KERN_ERR "iscsi: can not broadcast skb (%d)\n", rc);
return rc;
}
return 0;
} }
static int static int
...@@ -1207,7 +1199,7 @@ int iscsi_session_event(struct iscsi_cls_session *session, ...@@ -1207,7 +1199,7 @@ int iscsi_session_event(struct iscsi_cls_session *session,
* the user and when the daemon is restarted it will handle it * the user and when the daemon is restarted it will handle it
*/ */
rc = iscsi_broadcast_skb(skb, GFP_KERNEL); rc = iscsi_broadcast_skb(skb, GFP_KERNEL);
if (rc < 0) if (rc == -ESRCH)
iscsi_cls_session_printk(KERN_ERR, session, iscsi_cls_session_printk(KERN_ERR, session,
"Cannot notify userspace of session " "Cannot notify userspace of session "
"event %u. Check iscsi daemon\n", "event %u. Check iscsi daemon\n",
......
...@@ -204,8 +204,11 @@ static int uvesafb_exec(struct uvesafb_ktask *task) ...@@ -204,8 +204,11 @@ static int uvesafb_exec(struct uvesafb_ktask *task)
} else { } else {
v86d_started = 1; v86d_started = 1;
err = cn_netlink_send(m, 0, gfp_any()); err = cn_netlink_send(m, 0, gfp_any());
if (err == -ENOBUFS)
err = 0;
} }
} } else if (err == -ENOBUFS)
err = 0;
if (!err && !(task->t.flags & TF_EXIT)) if (!err && !(task->t.flags & TF_EXIT))
err = !wait_for_completion_timeout(task->done, err = !wait_for_completion_timeout(task->done,
......
...@@ -1057,10 +1057,7 @@ static void send_warning(const struct dquot *dquot, const char warntype) ...@@ -1057,10 +1057,7 @@ static void send_warning(const struct dquot *dquot, const char warntype)
goto attr_err_out; goto attr_err_out;
genlmsg_end(skb, msg_head); genlmsg_end(skb, msg_head);
ret = genlmsg_multicast(skb, 0, quota_genl_family.id, GFP_NOFS); genlmsg_multicast(skb, 0, quota_genl_family.id, GFP_NOFS);
if (ret < 0 && ret != -ESRCH)
printk(KERN_ERR
"VFS: Failed to send notification message: %d\n", ret);
return; return;
attr_err_out: attr_err_out:
printk(KERN_ERR "VFS: Not enough space to compose quota message!\n"); printk(KERN_ERR "VFS: Not enough space to compose quota message!\n");
......
...@@ -227,6 +227,9 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, ...@@ -227,6 +227,9 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
NETLINK_CB(skb).dst_group = 1; NETLINK_CB(skb).dst_group = 1;
retval = netlink_broadcast(uevent_sock, skb, 0, 1, retval = netlink_broadcast(uevent_sock, skb, 0, 1,
GFP_KERNEL); GFP_KERNEL);
/* ENOBUFS should be handled in userspace */
if (retval == -ENOBUFS)
retval = 0;
} else } else
retval = -ENOMEM; retval = -ENOMEM;
} }
......
...@@ -950,6 +950,7 @@ struct netlink_broadcast_data { ...@@ -950,6 +950,7 @@ struct netlink_broadcast_data {
u32 pid; u32 pid;
u32 group; u32 group;
int failure; int failure;
int delivery_failure;
int congested; int congested;
int delivered; int delivered;
gfp_t allocation; gfp_t allocation;
...@@ -999,6 +1000,7 @@ static inline int do_one_broadcast(struct sock *sk, ...@@ -999,6 +1000,7 @@ static inline int do_one_broadcast(struct sock *sk,
p->skb2 = NULL; p->skb2 = NULL;
} else if ((val = netlink_broadcast_deliver(sk, p->skb2)) < 0) { } else if ((val = netlink_broadcast_deliver(sk, p->skb2)) < 0) {
netlink_overrun(sk); netlink_overrun(sk);
p->delivery_failure = 1;
} else { } else {
p->congested |= val; p->congested |= val;
p->delivered = 1; p->delivered = 1;
...@@ -1025,6 +1027,7 @@ int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid, ...@@ -1025,6 +1027,7 @@ int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid,
info.pid = pid; info.pid = pid;
info.group = group; info.group = group;
info.failure = 0; info.failure = 0;
info.delivery_failure = 0;
info.congested = 0; info.congested = 0;
info.delivered = 0; info.delivered = 0;
info.allocation = allocation; info.allocation = allocation;
...@@ -1045,13 +1048,14 @@ int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid, ...@@ -1045,13 +1048,14 @@ int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid,
if (info.skb2) if (info.skb2)
kfree_skb(info.skb2); kfree_skb(info.skb2);
if (info.delivery_failure || info.failure)
return -ENOBUFS;
if (info.delivered) { if (info.delivered) {
if (info.congested && (allocation & __GFP_WAIT)) if (info.congested && (allocation & __GFP_WAIT))
yield(); yield();
return 0; return 0;
} }
if (info.failure)
return -ENOBUFS;
return -ESRCH; return -ESRCH;
} }
EXPORT_SYMBOL(netlink_broadcast); EXPORT_SYMBOL(netlink_broadcast);
......
...@@ -258,7 +258,6 @@ EXPORT_SYMBOL_GPL(wimax_msg_len); ...@@ -258,7 +258,6 @@ EXPORT_SYMBOL_GPL(wimax_msg_len);
*/ */
int wimax_msg_send(struct wimax_dev *wimax_dev, struct sk_buff *skb) int wimax_msg_send(struct wimax_dev *wimax_dev, struct sk_buff *skb)
{ {
int result;
struct device *dev = wimax_dev->net_dev->dev.parent; struct device *dev = wimax_dev->net_dev->dev.parent;
void *msg = skb->data; void *msg = skb->data;
size_t size = skb->len; size_t size = skb->len;
...@@ -266,11 +265,9 @@ int wimax_msg_send(struct wimax_dev *wimax_dev, struct sk_buff *skb) ...@@ -266,11 +265,9 @@ int wimax_msg_send(struct wimax_dev *wimax_dev, struct sk_buff *skb)
d_printf(1, dev, "CTX: wimax msg, %zu bytes\n", size); d_printf(1, dev, "CTX: wimax msg, %zu bytes\n", size);
d_dump(2, dev, msg, size); d_dump(2, dev, msg, size);
result = genlmsg_multicast(skb, 0, wimax_gnl_mcg.id, GFP_KERNEL); genlmsg_multicast(skb, 0, wimax_gnl_mcg.id, GFP_KERNEL);
d_printf(1, dev, "CTX: genl multicast result %d\n", result); d_printf(1, dev, "CTX: genl multicast done\n");
if (result == -ESRCH) /* Nobody connected, ignore it */ return 0;
result = 0; /* btw, the skb is freed already */
return result;
} }
EXPORT_SYMBOL_GPL(wimax_msg_send); EXPORT_SYMBOL_GPL(wimax_msg_send);
......
...@@ -163,16 +163,12 @@ int wimax_gnl_re_state_change_send( ...@@ -163,16 +163,12 @@ int wimax_gnl_re_state_change_send(
struct device *dev = wimax_dev_to_dev(wimax_dev); struct device *dev = wimax_dev_to_dev(wimax_dev);
d_fnstart(3, dev, "(wimax_dev %p report_skb %p)\n", d_fnstart(3, dev, "(wimax_dev %p report_skb %p)\n",
wimax_dev, report_skb); wimax_dev, report_skb);
if (report_skb == NULL) if (report_skb == NULL) {
result = -ENOMEM;
goto out; goto out;
genlmsg_end(report_skb, header);
result = genlmsg_multicast(report_skb, 0, wimax_gnl_mcg.id, GFP_KERNEL);
if (result == -ESRCH) /* Nobody connected, ignore it */
result = 0; /* btw, the skb is freed already */
if (result < 0) {
dev_err(dev, "RE_STCH: Error sending: %d\n", result);
nlmsg_free(report_skb);
} }
genlmsg_end(report_skb, header);
genlmsg_multicast(report_skb, 0, wimax_gnl_mcg.id, GFP_KERNEL);
out: out:
d_fnend(3, dev, "(wimax_dev %p report_skb %p) = %d\n", d_fnend(3, dev, "(wimax_dev %p report_skb %p) = %d\n",
wimax_dev, report_skb, result); wimax_dev, report_skb, result);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment