ipset; update to ipset-6.3 (genl)

* Handle EAGAIN from autoloading code.
* Turn one nfgenmsg site into genlmsg to avoid protocol mismatch
This commit is contained in:
Jan Engelhardt
2011-04-11 04:03:30 +02:00
parent 499c6db75e
commit 2dc79fe008
8 changed files with 73 additions and 36 deletions

View File

@@ -21,6 +21,10 @@ Enhancements:
* revision reporting fixes * revision reporting fixes
- update to ipset 6.2 - update to ipset 6.2
* list:set timeout variant fixes * list:set timeout variant fixes
- update to ipset 6.3
* bitmap:ip,mac type requires "src" for MAC, enforce it
- ipset-genl: handle EAGAIN return value emitted from autoloader
- ipset-genl: resolve nfgenmsg remains and fix spurious protocol abort
v1.33 (2011-02-02) v1.33 (2011-02-02)

View File

@@ -12,7 +12,7 @@
*/ */
/* The protocol version */ /* The protocol version */
#define IPSET_PROTOCOL 60 #define IPSET_PROTOCOL 6
/* The max length of strings including NUL: set and type identifiers */ /* The max length of strings including NUL: set and type identifiers */
#define IPSET_MAXNAMELEN 32 #define IPSET_MAXNAMELEN 32

View File

@@ -14,7 +14,7 @@
#include <linux/netlink.h> #include <linux/netlink.h>
/* The protocol version */ /* The protocol version */
#define IPSET_PROTOCOL 60 #define IPSET_PROTOCOL 6
/* The max length of strings including NUL: set and type identifiers */ /* The max length of strings including NUL: set and type identifiers */
#define IPSET_MAXNAMELEN 32 #define IPSET_MAXNAMELEN 32

View File

@@ -344,6 +344,10 @@ bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb,
ipset_adtfn adtfn = set->variant->adt[adt]; ipset_adtfn adtfn = set->variant->adt[adt];
struct ipmac data; struct ipmac data;
/* MAC can be src only */
if (!(flags & IPSET_DIM_TWO_SRC))
return 0;
data.id = ntohl(ip4addr(skb, flags & IPSET_DIM_ONE_SRC)); data.id = ntohl(ip4addr(skb, flags & IPSET_DIM_ONE_SRC));
if (data.id < map->first_ip || data.id > map->last_ip) if (data.id < map->first_ip || data.id > map->last_ip)
return -IPSET_ERR_BITMAP_RANGE; return -IPSET_ERR_BITMAP_RANGE;

View File

@@ -26,6 +26,8 @@
#include <net/genetlink.h> #include <net/genetlink.h>
#define GENLMSG_DEFAULT_SIZE (NLMSG_DEFAULT_SIZE - GENL_HDRLEN) #define GENLMSG_DEFAULT_SIZE (NLMSG_DEFAULT_SIZE - GENL_HDRLEN)
struct genlmsg_buf;
static LIST_HEAD(ip_set_type_list); /* all registered set types */ static LIST_HEAD(ip_set_type_list); /* all registered set types */
static DEFINE_MUTEX(ip_set_type_mutex); /* protects ip_set_type_list */ static DEFINE_MUTEX(ip_set_type_mutex); /* protects ip_set_type_list */
static DEFINE_RWLOCK(ip_set_ref_lock); /* protects the set refs */ static DEFINE_RWLOCK(ip_set_ref_lock); /* protects the set refs */
@@ -82,14 +84,14 @@ find_set_type(const char *name, u8 family, u8 revision)
static int static int
try_to_load_type(const char *name) try_to_load_type(const char *name)
{ {
nfnl_unlock(); genl_unlock();
pr_debug("try to load ip_set_%s\n", name); pr_debug("try to load ip_set_%s\n", name);
if (request_module("ip_set_%s", name) < 0) { if (request_module("ip_set_%s", name) < 0) {
pr_warning("Can't find ip_set type %s\n", name); pr_warning("Can't find ip_set type %s\n", name);
nfnl_lock(); genl_lock();
return -IPSET_ERR_FIND_TYPE; return -IPSET_ERR_FIND_TYPE;
} }
nfnl_lock(); genl_lock();
return -EAGAIN; return -EAGAIN;
} }
@@ -99,8 +101,10 @@ find_set_type_get(const char *name, u8 family, u8 revision,
struct ip_set_type **found) struct ip_set_type **found)
{ {
struct ip_set_type *type; struct ip_set_type *type;
unsigned int retry = 0;
int err; int err;
retry:
rcu_read_lock(); rcu_read_lock();
*found = find_set_type(name, family, revision); *found = find_set_type(name, family, revision);
if (*found) { if (*found) {
@@ -115,7 +119,10 @@ find_set_type_get(const char *name, u8 family, u8 revision,
} }
rcu_read_unlock(); rcu_read_unlock();
return try_to_load_type(name); err = try_to_load_type(name);
if (err == -EAGAIN && retry++ == 0)
goto retry;
return err;
unlock: unlock:
rcu_read_unlock(); rcu_read_unlock();
@@ -131,7 +138,10 @@ find_set_type_minmax(const char *name, u8 family, u8 *min, u8 *max)
{ {
struct ip_set_type *type; struct ip_set_type *type;
bool found = false; bool found = false;
unsigned int retry = 0;
int err;
retry:
*min = 255; *max = 0; *min = 255; *max = 0;
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(type, &ip_set_type_list, list) list_for_each_entry_rcu(type, &ip_set_type_list, list)
@@ -147,7 +157,10 @@ find_set_type_minmax(const char *name, u8 family, u8 *min, u8 *max)
if (found) if (found)
return 0; return 0;
return try_to_load_type(name); err = try_to_load_type(name);
if (err == -EAGAIN && retry++ == 0)
goto retry;
return err;
} }
#define family_name(f) ((f) == AF_INET ? "inet" : \ #define family_name(f) ((f) == AF_INET ? "inet" : \
@@ -482,9 +495,9 @@ ip_set_nfnl_get(const char *name)
struct ip_set *s; struct ip_set *s;
ip_set_id_t index; ip_set_id_t index;
nfnl_lock(); genl_lock();
index = ip_set_get_byname(name, &s); index = ip_set_get_byname(name, &s);
nfnl_unlock(); genl_unlock();
return index; return index;
} }
@@ -502,12 +515,12 @@ ip_set_nfnl_get_byindex(ip_set_id_t index)
if (index > ip_set_max) if (index > ip_set_max)
return IPSET_INVALID_ID; return IPSET_INVALID_ID;
nfnl_lock(); genl_lock();
if (ip_set_list[index]) if (ip_set_list[index])
__ip_set_get(index); __ip_set_get(index);
else else
index = IPSET_INVALID_ID; index = IPSET_INVALID_ID;
nfnl_unlock(); genl_unlock();
return index; return index;
} }
@@ -523,9 +536,9 @@ EXPORT_SYMBOL_GPL(ip_set_nfnl_get_byindex);
void void
ip_set_nfnl_put(ip_set_id_t index) ip_set_nfnl_put(ip_set_id_t index)
{ {
nfnl_lock(); genl_lock();
ip_set_put_byindex(index); ip_set_put_byindex(index);
nfnl_unlock(); genl_unlock();
} }
EXPORT_SYMBOL_GPL(ip_set_nfnl_put); EXPORT_SYMBOL_GPL(ip_set_nfnl_put);
@@ -548,11 +561,11 @@ flag_exist(const struct genlmsghdr *ghdr)
return ghdr->reserved & NLM_F_EXCL ? 0 : IPSET_FLAG_EXIST; return ghdr->reserved & NLM_F_EXCL ? 0 : IPSET_FLAG_EXIST;
} }
static struct nlmsghdr * static struct genlmsg_buf *
start_msg(struct sk_buff *skb, u32 pid, u32 seq, unsigned int flags, start_msg(struct sk_buff *skb, u32 pid, u32 seq, unsigned int flags,
enum ipset_cmd cmd) enum ipset_cmd cmd)
{ {
void *nlh; struct genlmsg_buf *nlh;
nlh = genlmsg_put(skb, pid, seq, &ip_set_netlink_subsys, flags, cmd); nlh = genlmsg_put(skb, pid, seq, &ip_set_netlink_subsys, flags, cmd);
if (nlh == NULL) if (nlh == NULL)
@@ -950,10 +963,11 @@ ip_set_dump_done(struct netlink_callback *cb)
} }
static inline void static inline void
dump_attrs(void *phdr) dump_attrs(struct genlmsg_buf *phdr)
{ {
const struct nlattr *attr; const struct nlattr *attr;
const struct nlmsghdr *nlh = phdr - GENL_HDRLEN - NLMSG_HDRLEN; const struct nlmsghdr *nlh =
(const void *)phdr - GENL_HDRLEN - NLMSG_HDRLEN;
int rem; int rem;
pr_debug("dump nlmsg\n"); pr_debug("dump nlmsg\n");
@@ -999,14 +1013,14 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb)
{ {
ip_set_id_t index = IPSET_INVALID_ID, max; ip_set_id_t index = IPSET_INVALID_ID, max;
struct ip_set *set = NULL; struct ip_set *set = NULL;
struct nlmsghdr *nlh = NULL; struct genlmsg_buf *nlh = NULL;
unsigned int flags = NETLINK_CB(cb->skb).pid ? NLM_F_MULTI : 0; unsigned int flags = NETLINK_CB(cb->skb).pid ? NLM_F_MULTI : 0;
int ret = 0; int ret = 0;
if (cb->args[0] == DUMP_INIT) { if (cb->args[0] == DUMP_INIT) {
ret = dump_init(cb); ret = dump_init(cb);
if (ret < 0) { if (ret < 0) {
nlh = nlmsg_hdr(cb->skb); struct nlmsghdr *nlh = nlmsg_hdr(cb->skb);
/* We have to create and send the error message /* We have to create and send the error message
* manually :-( */ * manually :-( */
if (nlh->nlmsg_flags & NLM_F_ACK) if (nlh->nlmsg_flags & NLM_F_ACK)
@@ -1159,7 +1173,7 @@ call_ad(struct sock *ctnl, struct sk_buff *skb, struct ip_set *set,
struct sk_buff *skb2; struct sk_buff *skb2;
struct nlmsgerr *errmsg; struct nlmsgerr *errmsg;
size_t payload = sizeof(*errmsg) + nlmsg_len(nlh); size_t payload = sizeof(*errmsg) + nlmsg_len(nlh);
int min_len = NLMSG_SPACE(sizeof(struct nfgenmsg)); int min_len = NLMSG_SPACE(sizeof(struct genlmsghdr));
struct nlattr *cda[IPSET_ATTR_CMD_MAX+1]; struct nlattr *cda[IPSET_ATTR_CMD_MAX+1];
struct nlattr *cmdattr; struct nlattr *cmdattr;
u32 *errline; u32 *errline;
@@ -1342,7 +1356,7 @@ ip_set_header(struct sk_buff *skb, struct genl_info *info)
struct nlattr *const *attr = info->attrs; struct nlattr *const *attr = info->attrs;
const struct nlmsghdr *nlh = info->nlhdr; const struct nlmsghdr *nlh = info->nlhdr;
struct sk_buff *skb2; struct sk_buff *skb2;
struct nlmsghdr *nlh2; struct genlmsg_buf *nlh2;
ip_set_id_t index; ip_set_id_t index;
int ret = 0; int ret = 0;
@@ -1355,7 +1369,7 @@ ip_set_header(struct sk_buff *skb, struct genl_info *info)
return -ENOENT; return -ENOENT;
set = ip_set_list[index]; set = ip_set_list[index];
skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); skb2 = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (skb2 == NULL) if (skb2 == NULL)
return -ENOMEM; return -ENOMEM;
@@ -1377,7 +1391,7 @@ ip_set_header(struct sk_buff *skb, struct genl_info *info)
return 0; return 0;
nla_put_failure: nla_put_failure:
nlmsg_cancel(skb2, nlh2); genlmsg_cancel(skb2, nlh2);
nlmsg_failure: nlmsg_failure:
kfree_skb(skb2); kfree_skb(skb2);
return -EMSGSIZE; return -EMSGSIZE;
@@ -1399,7 +1413,7 @@ ip_set_type(struct sk_buff *skb, struct genl_info *info)
const struct nlmsghdr *nlh = info->nlhdr; const struct nlmsghdr *nlh = info->nlhdr;
struct sk_buff *skb2; struct sk_buff *skb2;
void *nlh2; struct genlmsg_buf *nlh2;
u8 family, min, max; u8 family, min, max;
const char *typename; const char *typename;
int ret = 0; int ret = 0;
@@ -1458,7 +1472,7 @@ ip_set_protocol(struct sk_buff *skb, struct genl_info *info)
const struct nlmsghdr *nlh = info->nlhdr; const struct nlmsghdr *nlh = info->nlhdr;
struct sk_buff *skb2; struct sk_buff *skb2;
void *nlh2; struct genlmsg_buf *nlh2;
int ret = 0; int ret = 0;
if (unlikely(attr[IPSET_ATTR_PROTOCOL] == NULL)) if (unlikely(attr[IPSET_ATTR_PROTOCOL] == NULL))
@@ -1633,9 +1647,9 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len)
goto done; goto done;
} }
req_get->set.name[IPSET_MAXNAMELEN - 1] = '\0'; req_get->set.name[IPSET_MAXNAMELEN - 1] = '\0';
nfnl_lock(); genl_lock();
req_get->set.index = find_set_id(req_get->set.name); req_get->set.index = find_set_id(req_get->set.name);
nfnl_unlock(); genl_unlock();
goto copy; goto copy;
} }
case IP_SET_OP_GET_BYINDEX: { case IP_SET_OP_GET_BYINDEX: {
@@ -1646,12 +1660,12 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len)
ret = -EINVAL; ret = -EINVAL;
goto done; goto done;
} }
nfnl_lock(); genl_lock();
strncpy(req_get->set.name, strncpy(req_get->set.name,
ip_set_list[req_get->set.index] ip_set_list[req_get->set.index]
? ip_set_list[req_get->set.index]->name : "", ? ip_set_list[req_get->set.index]->name : "",
IPSET_MAXNAMELEN); IPSET_MAXNAMELEN);
nfnl_unlock(); genl_unlock();
goto copy; goto copy;
} }
default: default:

View File

@@ -310,8 +310,8 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[],
!id_eq(map, i + 1, refid)) || !id_eq(map, i + 1, refid)) ||
(before < 0 && (before < 0 &&
(i == 0 || !id_eq(map, i - 1, refid)))) { (i == 0 || !id_eq(map, i - 1, refid)))) {
ret = -IPSET_ERR_EXIST; ret = -IPSET_ERR_EXIST;
goto finish; goto finish;
} }
e->timeout = ip_set_timeout_set(timeout); e->timeout = ip_set_timeout_set(timeout);
ip_set_put_byindex(id); ip_set_put_byindex(id);

View File

@@ -302,9 +302,10 @@ matched by the kernel, it will automatically fill out the missing MAC address wi
source MAC address from the packet. If the entry was specified with a timeout value, source MAC address from the packet. If the entry was specified with a timeout value,
the timer starts off when the IP and MAC address pair is complete. the timer starts off when the IP and MAC address pair is complete.
.PP .PP
Please note, the \fBset\fR match and \fBSET\fR target netfilter kernel modules The \fBbitmap:ip,mac\fR type of sets require two \fBsrc/dst\fR parameters of
\fBalways\fR use the source MAC address from the packet to match, add or delete the \fBset\fR match and \fBSET\fR target netfilter kernel modules and the second
entries from a \fBbitmap:ip,mac\fR type of set. one must be \fBsrc\fR to match, add or delete entries because the \fBset\fR match
and \fBSET\fR target have access to the source MAC address only.
.PP .PP
Examples: Examples:
.IP .IP

View File

@@ -115,6 +115,7 @@ set_match_v0_checkentry(const struct xt_mtchk_param *par)
if (info->match_set.u.flags[IPSET_DIM_MAX-1] != 0) { if (info->match_set.u.flags[IPSET_DIM_MAX-1] != 0) {
pr_warning("Protocol error: set match dimension " pr_warning("Protocol error: set match dimension "
"is over the limit!\n"); "is over the limit!\n");
ip_set_nfnl_put(info->match_set.index);
return CHECK_FAIL(-ERANGE); /* error */ return CHECK_FAIL(-ERANGE); /* error */
} }
@@ -179,6 +180,8 @@ set_target_v0_checkentry(const struct xt_tgchk_param *par)
if (index == IPSET_INVALID_ID) { if (index == IPSET_INVALID_ID) {
pr_warning("Cannot find del_set index %u as target\n", pr_warning("Cannot find del_set index %u as target\n",
info->del_set.index); info->del_set.index);
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(info->add_set.index);
return CHECK_FAIL(-ENOENT); /* error */ return CHECK_FAIL(-ENOENT); /* error */
} }
} }
@@ -186,6 +189,10 @@ set_target_v0_checkentry(const struct xt_tgchk_param *par)
info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) { info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) {
pr_warning("Protocol error: SET target dimension " pr_warning("Protocol error: SET target dimension "
"is over the limit!\n"); "is over the limit!\n");
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(info->add_set.index);
if (info->del_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(info->del_set.index);
return CHECK_FAIL(-ERANGE); /* error */ return CHECK_FAIL(-ERANGE); /* error */
} }
@@ -246,6 +253,7 @@ set_match_checkentry(const struct xt_mtchk_param *par)
if (info->match_set.dim > IPSET_DIM_MAX) { if (info->match_set.dim > IPSET_DIM_MAX) {
pr_warning("Protocol error: set match dimension " pr_warning("Protocol error: set match dimension "
"is over the limit!\n"); "is over the limit!\n");
ip_set_nfnl_put(info->match_set.index);
return CHECK_FAIL(-ERANGE); /* error */ return CHECK_FAIL(-ERANGE); /* error */
} }
@@ -278,7 +286,7 @@ set_target(struct sk_buff *skb, const struct xt_action_param *par)
if (info->del_set.index != IPSET_INVALID_ID) if (info->del_set.index != IPSET_INVALID_ID)
ip_set_del(info->del_set.index, ip_set_del(info->del_set.index,
skb, par->family, skb, par->family,
info->add_set.dim, info->del_set.dim,
info->del_set.flags); info->del_set.flags);
return XT_CONTINUE; return XT_CONTINUE;
@@ -309,13 +317,19 @@ set_target_checkentry(const struct xt_tgchk_param *par)
if (index == IPSET_INVALID_ID) { if (index == IPSET_INVALID_ID) {
pr_warning("Cannot find del_set index %u as target\n", pr_warning("Cannot find del_set index %u as target\n",
info->del_set.index); info->del_set.index);
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(info->add_set.index);
return CHECK_FAIL(-ENOENT); /* error */ return CHECK_FAIL(-ENOENT); /* error */
} }
} }
if (info->add_set.dim > IPSET_DIM_MAX || if (info->add_set.dim > IPSET_DIM_MAX ||
info->del_set.flags > IPSET_DIM_MAX) { info->del_set.dim > IPSET_DIM_MAX) {
pr_warning("Protocol error: SET target dimension " pr_warning("Protocol error: SET target dimension "
"is over the limit!\n"); "is over the limit!\n");
if (info->add_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(info->add_set.index);
if (info->del_set.index != IPSET_INVALID_ID)
ip_set_nfnl_put(info->del_set.index);
return CHECK_FAIL(-ERANGE); /* error */ return CHECK_FAIL(-ERANGE); /* error */
} }