Commit 5f52bc3c authored by Jozsef Kadlecsik's avatar Jozsef Kadlecsik Committed by Patrick McHardy

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.

Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu
Signed-off-by: default avatarPatrick McHardy <kaber@trash.net>
parent 724bab47
...@@ -1098,7 +1098,7 @@ static const struct nla_policy ip_set_adt_policy[IPSET_ATTR_CMD_MAX + 1] = { ...@@ -1098,7 +1098,7 @@ static const struct nla_policy ip_set_adt_policy[IPSET_ATTR_CMD_MAX + 1] = {
}; };
static int 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, struct nlattr *tb[], enum ipset_adt adt,
u32 flags, bool use_lineno) u32 flags, bool use_lineno)
{ {
...@@ -1118,12 +1118,25 @@ call_ad(struct sk_buff *skb, struct ip_set *set, ...@@ -1118,12 +1118,25 @@ call_ad(struct sk_buff *skb, struct ip_set *set,
return 0; return 0;
if (lineno && use_lineno) { if (lineno && use_lineno) {
/* Error in restore/batch mode: send back 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)); int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg));
struct nlattr *cda[IPSET_ATTR_CMD_MAX+1]; struct nlattr *cda[IPSET_ATTR_CMD_MAX+1];
struct nlattr *cmdattr = (void *)nlh + min_len; struct nlattr *cmdattr;
u32 *errline; 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, nla_parse(cda, IPSET_ATTR_CMD_MAX,
cmdattr, nlh->nlmsg_len - min_len, cmdattr, nlh->nlmsg_len - min_len,
ip_set_adt_policy); ip_set_adt_policy);
...@@ -1131,6 +1144,10 @@ call_ad(struct sk_buff *skb, struct ip_set *set, ...@@ -1131,6 +1144,10 @@ call_ad(struct sk_buff *skb, struct ip_set *set,
errline = nla_data(cda[IPSET_ATTR_LINENO]); errline = nla_data(cda[IPSET_ATTR_LINENO]);
*errline = 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; return ret;
...@@ -1169,7 +1186,8 @@ ip_set_uadd(struct sock *ctnl, struct sk_buff *skb, ...@@ -1169,7 +1186,8 @@ ip_set_uadd(struct sock *ctnl, struct sk_buff *skb,
attr[IPSET_ATTR_DATA], attr[IPSET_ATTR_DATA],
set->type->adt_policy)) set->type->adt_policy))
return -IPSET_ERR_PROTOCOL; 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 { } else {
int nla_rem; int nla_rem;
...@@ -1180,7 +1198,7 @@ ip_set_uadd(struct sock *ctnl, struct sk_buff *skb, ...@@ -1180,7 +1198,7 @@ ip_set_uadd(struct sock *ctnl, struct sk_buff *skb,
nla_parse_nested(tb, IPSET_ATTR_ADT_MAX, nla, nla_parse_nested(tb, IPSET_ATTR_ADT_MAX, nla,
set->type->adt_policy)) set->type->adt_policy))
return -IPSET_ERR_PROTOCOL; return -IPSET_ERR_PROTOCOL;
ret = call_ad(skb, set, tb, IPSET_ADD, ret = call_ad(ctnl, skb, set, tb, IPSET_ADD,
flags, use_lineno); flags, use_lineno);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -1222,7 +1240,8 @@ ip_set_udel(struct sock *ctnl, struct sk_buff *skb, ...@@ -1222,7 +1240,8 @@ ip_set_udel(struct sock *ctnl, struct sk_buff *skb,
attr[IPSET_ATTR_DATA], attr[IPSET_ATTR_DATA],
set->type->adt_policy)) set->type->adt_policy))
return -IPSET_ERR_PROTOCOL; 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 { } else {
int nla_rem; int nla_rem;
...@@ -1233,7 +1252,7 @@ ip_set_udel(struct sock *ctnl, struct sk_buff *skb, ...@@ -1233,7 +1252,7 @@ ip_set_udel(struct sock *ctnl, struct sk_buff *skb,
nla_parse_nested(tb, IPSET_ATTR_ADT_MAX, nla, nla_parse_nested(tb, IPSET_ATTR_ADT_MAX, nla,
set->type->adt_policy)) set->type->adt_policy))
return -IPSET_ERR_PROTOCOL; return -IPSET_ERR_PROTOCOL;
ret = call_ad(skb, set, tb, IPSET_DEL, ret = call_ad(ctnl, skb, set, tb, IPSET_DEL,
flags, use_lineno); flags, use_lineno);
if (ret < 0) if (ret < 0)
return ret; return ret;
......
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