summaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorJozsef Kadlecsik <kadlec@blackhole.kfki.hu>2011-02-02 20:43:18 +0100
committerJozsef Kadlecsik <kadlec@blackhole.kfki.hu>2011-02-02 20:43:18 +0100
commita7586eb0a0eed3554a104ace4fda912c3ab087ab (patch)
tree286c152f326f0ea59c0307e00b425173e8b2f1d5 /kernel
parent85cee26417bb1456575028fdfe343b3db777a5c9 (diff)
netfilter: ipset: send error message manually
When a message carries multiple commands and one of them triggers an error, we have to report to the userspace which one was that. The line number of the command plays this role and there's an attribute reserved in the header part of the message to be filled out with the error line number. In order not to modify the original message received from the userspace, we construct a new, complete netlink error message and modifies the attribute there, then send it. Netlink is notified not to send its ACK/error message.
Diffstat (limited to 'kernel')
-rw-r--r--kernel/ip_set_core.c33
1 files changed, 26 insertions, 7 deletions
diff --git a/kernel/ip_set_core.c b/kernel/ip_set_core.c
index 19158bf..2f7df3a 100644
--- a/kernel/ip_set_core.c
+++ b/kernel/ip_set_core.c
@@ -1103,7 +1103,7 @@ static const struct nla_policy ip_set_adt_policy[IPSET_ATTR_CMD_MAX + 1] = {
};
static int
-call_ad(struct sk_buff *skb, struct ip_set *set,
+call_ad(struct sock *ctnl, struct sk_buff *skb, struct ip_set *set,
struct nlattr *tb[], enum ipset_adt adt,
u32 flags, bool use_lineno)
{
@@ -1123,12 +1123,25 @@ call_ad(struct sk_buff *skb, struct ip_set *set,
return 0;
if (lineno && use_lineno) {
/* Error in restore/batch mode: send back lineno */
- struct nlmsghdr *nlh = nlmsg_hdr(skb);
+ struct nlmsghdr *rep, *nlh = nlmsg_hdr(skb);
+ struct sk_buff *skb2;
+ struct nlmsgerr *errmsg;
+ size_t payload = sizeof(*errmsg) + nlmsg_len(nlh);
int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg));
struct nlattr *cda[IPSET_ATTR_CMD_MAX+1];
- struct nlattr *cmdattr = (void *)nlh + min_len;
+ struct nlattr *cmdattr;
u32 *errline;
+ skb2 = nlmsg_new(payload, GFP_KERNEL);
+ if (skb2 == NULL)
+ return -ENOMEM;
+ rep = __nlmsg_put(skb2, NETLINK_CB(skb).pid,
+ nlh->nlmsg_seq, NLMSG_ERROR, payload, 0);
+ errmsg = nlmsg_data(rep);
+ errmsg->error = ret;
+ memcpy(&errmsg->msg, nlh, nlh->nlmsg_len);
+ cmdattr = (void *)&errmsg->msg + min_len;
+
nla_parse(cda, IPSET_ATTR_CMD_MAX,
cmdattr, nlh->nlmsg_len - min_len,
ip_set_adt_policy);
@@ -1136,6 +1149,10 @@ call_ad(struct sk_buff *skb, struct ip_set *set,
errline = nla_data(cda[IPSET_ATTR_LINENO]);
*errline = lineno;
+
+ netlink_unicast(ctnl, skb2, NETLINK_CB(skb).pid, MSG_DONTWAIT);
+ /* Signal netlink not to send its ACK/errmsg. */
+ return -EINTR;
}
return ret;
@@ -1174,7 +1191,8 @@ ip_set_uadd(struct sock *ctnl, struct sk_buff *skb,
attr[IPSET_ATTR_DATA],
set->type->adt_policy))
return -IPSET_ERR_PROTOCOL;
- ret = call_ad(skb, set, tb, IPSET_ADD, flags, use_lineno);
+ ret = call_ad(ctnl, skb, set, tb, IPSET_ADD, flags,
+ use_lineno);
} else {
int nla_rem;
@@ -1185,7 +1203,7 @@ ip_set_uadd(struct sock *ctnl, struct sk_buff *skb,
nla_parse_nested(tb, IPSET_ATTR_ADT_MAX, nla,
set->type->adt_policy))
return -IPSET_ERR_PROTOCOL;
- ret = call_ad(skb, set, tb, IPSET_ADD,
+ ret = call_ad(ctnl, skb, set, tb, IPSET_ADD,
flags, use_lineno);
if (ret < 0)
return ret;
@@ -1227,7 +1245,8 @@ ip_set_udel(struct sock *ctnl, struct sk_buff *skb,
attr[IPSET_ATTR_DATA],
set->type->adt_policy))
return -IPSET_ERR_PROTOCOL;
- ret = call_ad(skb, set, tb, IPSET_DEL, flags, use_lineno);
+ ret = call_ad(ctnl, skb, set, tb, IPSET_DEL, flags,
+ use_lineno);
} else {
int nla_rem;
@@ -1238,7 +1257,7 @@ ip_set_udel(struct sock *ctnl, struct sk_buff *skb,
nla_parse_nested(tb, IPSET_ATTR_ADT_MAX, nla,
set->type->adt_policy))
return -IPSET_ERR_PROTOCOL;
- ret = call_ad(skb, set, tb, IPSET_DEL,
+ ret = call_ad(ctnl, skb, set, tb, IPSET_DEL,
flags, use_lineno);
if (ret < 0)
return ret;