Skip to content

Commit

Permalink
Merge branch 'master' of git://blackhole.kfki.hu/nf-next
Browse files Browse the repository at this point in the history
Jozsef Kadlecsik says:

====================
- Introduction of new commands and thus protocol version 7. The
  new commands makes possible to eliminate the getsockopt interface
  of ipset and use solely netlink to communicate with the kernel.
  Due to the strict attribute checking both in user/kernel space,
  a new protocol number was introduced. Both the kernel/userspace is
  fully backward compatible.
- Make invalid MAC address checks consisten, from Stefano Brivio.
  The patch depends on the next one.
- Allow matching on destination MAC address for mac and ipmac sets,
  also from Stefano Brivio.
====================

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
  • Loading branch information
Pablo Neira Ayuso committed Nov 12, 2018
2 parents 30beabb + 23c42a4 commit 1226cfe
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 48 deletions.
2 changes: 1 addition & 1 deletion include/linux/netfilter/ipset/ip_set.h
Original file line number Diff line number Diff line change
Expand Up @@ -303,11 +303,11 @@ ip_set_put_flags(struct sk_buff *skb, struct ip_set *set)
/* Netlink CB args */
enum {
IPSET_CB_NET = 0, /* net namespace */
IPSET_CB_PROTO, /* ipset protocol */
IPSET_CB_DUMP, /* dump single set/all sets */
IPSET_CB_INDEX, /* set index */
IPSET_CB_PRIVATE, /* set private data */
IPSET_CB_ARG0, /* type specific */
IPSET_CB_ARG1,
};

/* register and unregister set references */
Expand Down
19 changes: 12 additions & 7 deletions include/uapi/linux/netfilter/ipset/ip_set.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@

#include <linux/types.h>

/* The protocol version */
#define IPSET_PROTOCOL 6
/* The protocol versions */
#define IPSET_PROTOCOL 7
#define IPSET_PROTOCOL_MIN 6

/* The max length of strings including NUL: set and type identifiers */
#define IPSET_MAXNAMELEN 32
Expand All @@ -38,17 +39,19 @@ enum ipset_cmd {
IPSET_CMD_TEST, /* 11: Test an element in a set */
IPSET_CMD_HEADER, /* 12: Get set header data only */
IPSET_CMD_TYPE, /* 13: Get set type */
IPSET_CMD_GET_BYNAME, /* 14: Get set index by name */
IPSET_CMD_GET_BYINDEX, /* 15: Get set name by index */
IPSET_MSG_MAX, /* Netlink message commands */

/* Commands in userspace: */
IPSET_CMD_RESTORE = IPSET_MSG_MAX, /* 14: Enter restore mode */
IPSET_CMD_HELP, /* 15: Get help */
IPSET_CMD_VERSION, /* 16: Get program version */
IPSET_CMD_QUIT, /* 17: Quit from interactive mode */
IPSET_CMD_RESTORE = IPSET_MSG_MAX, /* 16: Enter restore mode */
IPSET_CMD_HELP, /* 17: Get help */
IPSET_CMD_VERSION, /* 18: Get program version */
IPSET_CMD_QUIT, /* 19: Quit from interactive mode */

IPSET_CMD_MAX,

IPSET_CMD_COMMIT = IPSET_CMD_MAX, /* 18: Commit buffered commands */
IPSET_CMD_COMMIT = IPSET_CMD_MAX, /* 20: Commit buffered commands */
};

/* Attributes at command level */
Expand All @@ -66,6 +69,7 @@ enum {
IPSET_ATTR_LINENO, /* 9: Restore lineno */
IPSET_ATTR_PROTOCOL_MIN, /* 10: Minimal supported version number */
IPSET_ATTR_REVISION_MIN = IPSET_ATTR_PROTOCOL_MIN, /* type rev min */
IPSET_ATTR_INDEX, /* 11: Kernel index of set */
__IPSET_ATTR_CMD_MAX,
};
#define IPSET_ATTR_CMD_MAX (__IPSET_ATTR_CMD_MAX - 1)
Expand Down Expand Up @@ -223,6 +227,7 @@ enum ipset_adt {

/* Sets are identified by an index in kernel space. Tweak with ip_set_id_t
* and IPSET_INVALID_ID if you want to increase the max number of sets.
* Also, IPSET_ATTR_INDEX must be changed.
*/
typedef __u16 ip_set_id_t;

Expand Down
13 changes: 8 additions & 5 deletions net/netfilter/ipset/ip_set_bitmap_ipmac.c
Original file line number Diff line number Diff line change
Expand Up @@ -219,10 +219,6 @@ bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb,
struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
u32 ip;

/* MAC can be src only */
if (!(opt->flags & IPSET_DIM_TWO_SRC))
return 0;

ip = ntohl(ip4addr(skb, opt->flags & IPSET_DIM_ONE_SRC));
if (ip < map->first_ip || ip > map->last_ip)
return -IPSET_ERR_BITMAP_RANGE;
Expand All @@ -233,7 +229,14 @@ bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb,
return -EINVAL;

e.id = ip_to_id(map, ip);
memcpy(e.ether, eth_hdr(skb)->h_source, ETH_ALEN);

if (opt->flags & IPSET_DIM_ONE_SRC)
ether_addr_copy(e.ether, eth_hdr(skb)->h_source);
else
ether_addr_copy(e.ether, eth_hdr(skb)->h_dest);

if (is_zero_ether_addr(e.ether))
return -EINVAL;

return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
}
Expand Down
164 changes: 147 additions & 17 deletions net/netfilter/ipset/ip_set_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -771,11 +771,21 @@ EXPORT_SYMBOL_GPL(ip_set_nfnl_put);
* The commands are serialized by the nfnl mutex.
*/

static inline u8 protocol(const struct nlattr * const tb[])
{
return nla_get_u8(tb[IPSET_ATTR_PROTOCOL]);
}

static inline bool
protocol_failed(const struct nlattr * const tb[])
{
return !tb[IPSET_ATTR_PROTOCOL] ||
nla_get_u8(tb[IPSET_ATTR_PROTOCOL]) != IPSET_PROTOCOL;
return !tb[IPSET_ATTR_PROTOCOL] || protocol(tb) != IPSET_PROTOCOL;
}

static inline bool
protocol_min_failed(const struct nlattr * const tb[])
{
return !tb[IPSET_ATTR_PROTOCOL] || protocol(tb) < IPSET_PROTOCOL_MIN;
}

static inline u32
Expand Down Expand Up @@ -889,7 +899,7 @@ static int ip_set_create(struct net *net, struct sock *ctnl,
u32 flags = flag_exist(nlh);
int ret = 0;

if (unlikely(protocol_failed(attr) ||
if (unlikely(protocol_min_failed(attr) ||
!attr[IPSET_ATTR_SETNAME] ||
!attr[IPSET_ATTR_TYPENAME] ||
!attr[IPSET_ATTR_REVISION] ||
Expand Down Expand Up @@ -1027,7 +1037,7 @@ static int ip_set_destroy(struct net *net, struct sock *ctnl,
ip_set_id_t i;
int ret = 0;

if (unlikely(protocol_failed(attr)))
if (unlikely(protocol_min_failed(attr)))
return -IPSET_ERR_PROTOCOL;

/* Must wait for flush to be really finished in list:set */
Expand Down Expand Up @@ -1105,7 +1115,7 @@ static int ip_set_flush(struct net *net, struct sock *ctnl, struct sk_buff *skb,
struct ip_set *s;
ip_set_id_t i;

if (unlikely(protocol_failed(attr)))
if (unlikely(protocol_min_failed(attr)))
return -IPSET_ERR_PROTOCOL;

if (!attr[IPSET_ATTR_SETNAME]) {
Expand Down Expand Up @@ -1147,7 +1157,7 @@ static int ip_set_rename(struct net *net, struct sock *ctnl,
ip_set_id_t i;
int ret = 0;

if (unlikely(protocol_failed(attr) ||
if (unlikely(protocol_min_failed(attr) ||
!attr[IPSET_ATTR_SETNAME] ||
!attr[IPSET_ATTR_SETNAME2]))
return -IPSET_ERR_PROTOCOL;
Expand Down Expand Up @@ -1196,7 +1206,7 @@ static int ip_set_swap(struct net *net, struct sock *ctnl, struct sk_buff *skb,
ip_set_id_t from_id, to_id;
char from_name[IPSET_MAXNAMELEN];

if (unlikely(protocol_failed(attr) ||
if (unlikely(protocol_min_failed(attr) ||
!attr[IPSET_ATTR_SETNAME] ||
!attr[IPSET_ATTR_SETNAME2]))
return -IPSET_ERR_PROTOCOL;
Expand Down Expand Up @@ -1291,6 +1301,7 @@ dump_init(struct netlink_callback *cb, struct ip_set_net *inst)
nla_parse(cda, IPSET_ATTR_CMD_MAX, attr, nlh->nlmsg_len - min_len,
ip_set_setname_policy, NULL);

cb->args[IPSET_CB_PROTO] = nla_get_u8(cda[IPSET_ATTR_PROTOCOL]);
if (cda[IPSET_ATTR_SETNAME]) {
struct ip_set *set;

Expand Down Expand Up @@ -1392,7 +1403,8 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb)
ret = -EMSGSIZE;
goto release_refcount;
}
if (nla_put_u8(skb, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL) ||
if (nla_put_u8(skb, IPSET_ATTR_PROTOCOL,
cb->args[IPSET_CB_PROTO]) ||
nla_put_string(skb, IPSET_ATTR_SETNAME, set->name))
goto nla_put_failure;
if (dump_flags & IPSET_FLAG_LIST_SETNAME)
Expand All @@ -1407,6 +1419,9 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb)
nla_put_u8(skb, IPSET_ATTR_REVISION,
set->revision))
goto nla_put_failure;
if (cb->args[IPSET_CB_PROTO] > IPSET_PROTOCOL_MIN &&
nla_put_net16(skb, IPSET_ATTR_INDEX, htons(index)))
goto nla_put_failure;
ret = set->variant->head(set, skb);
if (ret < 0)
goto release_refcount;
Expand Down Expand Up @@ -1466,7 +1481,7 @@ static int ip_set_dump(struct net *net, struct sock *ctnl, struct sk_buff *skb,
const struct nlattr * const attr[],
struct netlink_ext_ack *extack)
{
if (unlikely(protocol_failed(attr)))
if (unlikely(protocol_min_failed(attr)))
return -IPSET_ERR_PROTOCOL;

{
Expand Down Expand Up @@ -1560,7 +1575,7 @@ static int ip_set_uadd(struct net *net, struct sock *ctnl, struct sk_buff *skb,
bool use_lineno;
int ret = 0;

if (unlikely(protocol_failed(attr) ||
if (unlikely(protocol_min_failed(attr) ||
!attr[IPSET_ATTR_SETNAME] ||
!((attr[IPSET_ATTR_DATA] != NULL) ^
(attr[IPSET_ATTR_ADT] != NULL)) ||
Expand Down Expand Up @@ -1615,7 +1630,7 @@ static int ip_set_udel(struct net *net, struct sock *ctnl, struct sk_buff *skb,
bool use_lineno;
int ret = 0;

if (unlikely(protocol_failed(attr) ||
if (unlikely(protocol_min_failed(attr) ||
!attr[IPSET_ATTR_SETNAME] ||
!((attr[IPSET_ATTR_DATA] != NULL) ^
(attr[IPSET_ATTR_ADT] != NULL)) ||
Expand Down Expand Up @@ -1667,7 +1682,7 @@ static int ip_set_utest(struct net *net, struct sock *ctnl, struct sk_buff *skb,
struct nlattr *tb[IPSET_ATTR_ADT_MAX + 1] = {};
int ret = 0;

if (unlikely(protocol_failed(attr) ||
if (unlikely(protocol_min_failed(attr) ||
!attr[IPSET_ATTR_SETNAME] ||
!attr[IPSET_ATTR_DATA] ||
!flag_nested(attr[IPSET_ATTR_DATA])))
Expand Down Expand Up @@ -1704,7 +1719,7 @@ static int ip_set_header(struct net *net, struct sock *ctnl,
struct nlmsghdr *nlh2;
int ret = 0;

if (unlikely(protocol_failed(attr) ||
if (unlikely(protocol_min_failed(attr) ||
!attr[IPSET_ATTR_SETNAME]))
return -IPSET_ERR_PROTOCOL;

Expand All @@ -1720,7 +1735,7 @@ static int ip_set_header(struct net *net, struct sock *ctnl,
IPSET_CMD_HEADER);
if (!nlh2)
goto nlmsg_failure;
if (nla_put_u8(skb2, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL) ||
if (nla_put_u8(skb2, IPSET_ATTR_PROTOCOL, protocol(attr)) ||
nla_put_string(skb2, IPSET_ATTR_SETNAME, set->name) ||
nla_put_string(skb2, IPSET_ATTR_TYPENAME, set->type->name) ||
nla_put_u8(skb2, IPSET_ATTR_FAMILY, set->family) ||
Expand Down Expand Up @@ -1761,7 +1776,7 @@ static int ip_set_type(struct net *net, struct sock *ctnl, struct sk_buff *skb,
const char *typename;
int ret = 0;

if (unlikely(protocol_failed(attr) ||
if (unlikely(protocol_min_failed(attr) ||
!attr[IPSET_ATTR_TYPENAME] ||
!attr[IPSET_ATTR_FAMILY]))
return -IPSET_ERR_PROTOCOL;
Expand All @@ -1780,7 +1795,7 @@ static int ip_set_type(struct net *net, struct sock *ctnl, struct sk_buff *skb,
IPSET_CMD_TYPE);
if (!nlh2)
goto nlmsg_failure;
if (nla_put_u8(skb2, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL) ||
if (nla_put_u8(skb2, IPSET_ATTR_PROTOCOL, protocol(attr)) ||
nla_put_string(skb2, IPSET_ATTR_TYPENAME, typename) ||
nla_put_u8(skb2, IPSET_ATTR_FAMILY, family) ||
nla_put_u8(skb2, IPSET_ATTR_REVISION, max) ||
Expand Down Expand Up @@ -1831,6 +1846,111 @@ static int ip_set_protocol(struct net *net, struct sock *ctnl,
goto nlmsg_failure;
if (nla_put_u8(skb2, IPSET_ATTR_PROTOCOL, IPSET_PROTOCOL))
goto nla_put_failure;
if (nla_put_u8(skb2, IPSET_ATTR_PROTOCOL_MIN, IPSET_PROTOCOL_MIN))
goto nla_put_failure;
nlmsg_end(skb2, nlh2);

ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).portid, MSG_DONTWAIT);
if (ret < 0)
return ret;

return 0;

nla_put_failure:
nlmsg_cancel(skb2, nlh2);
nlmsg_failure:
kfree_skb(skb2);
return -EMSGSIZE;
}

/* Get set by name or index, from userspace */

static int ip_set_byname(struct net *net, struct sock *ctnl,
struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const attr[],
struct netlink_ext_ack *extack)
{
struct ip_set_net *inst = ip_set_pernet(net);
struct sk_buff *skb2;
struct nlmsghdr *nlh2;
ip_set_id_t id = IPSET_INVALID_ID;
const struct ip_set *set;
int ret = 0;

if (unlikely(protocol_failed(attr) ||
!attr[IPSET_ATTR_SETNAME]))
return -IPSET_ERR_PROTOCOL;

set = find_set_and_id(inst, nla_data(attr[IPSET_ATTR_SETNAME]), &id);
if (id == IPSET_INVALID_ID)
return -ENOENT;

skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!skb2)
return -ENOMEM;

nlh2 = start_msg(skb2, NETLINK_CB(skb).portid, nlh->nlmsg_seq, 0,
IPSET_CMD_GET_BYNAME);
if (!nlh2)
goto nlmsg_failure;
if (nla_put_u8(skb2, IPSET_ATTR_PROTOCOL, protocol(attr)) ||
nla_put_u8(skb2, IPSET_ATTR_FAMILY, set->family) ||
nla_put_net16(skb2, IPSET_ATTR_INDEX, htons(id)))
goto nla_put_failure;
nlmsg_end(skb2, nlh2);

ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).portid, MSG_DONTWAIT);
if (ret < 0)
return ret;

return 0;

nla_put_failure:
nlmsg_cancel(skb2, nlh2);
nlmsg_failure:
kfree_skb(skb2);
return -EMSGSIZE;
}

static const struct nla_policy ip_set_index_policy[IPSET_ATTR_CMD_MAX + 1] = {
[IPSET_ATTR_PROTOCOL] = { .type = NLA_U8 },
[IPSET_ATTR_INDEX] = { .type = NLA_U16 },
};

static int ip_set_byindex(struct net *net, struct sock *ctnl,
struct sk_buff *skb, const struct nlmsghdr *nlh,
const struct nlattr * const attr[],
struct netlink_ext_ack *extack)
{
struct ip_set_net *inst = ip_set_pernet(net);
struct sk_buff *skb2;
struct nlmsghdr *nlh2;
ip_set_id_t id = IPSET_INVALID_ID;
const struct ip_set *set;
int ret = 0;

if (unlikely(protocol_failed(attr) ||
!attr[IPSET_ATTR_INDEX]))
return -IPSET_ERR_PROTOCOL;

id = ip_set_get_h16(attr[IPSET_ATTR_INDEX]);
if (id >= inst->ip_set_max)
return -ENOENT;
set = ip_set(inst, id);
if (set == NULL)
return -ENOENT;

skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!skb2)
return -ENOMEM;

nlh2 = start_msg(skb2, NETLINK_CB(skb).portid, nlh->nlmsg_seq, 0,
IPSET_CMD_GET_BYINDEX);
if (!nlh2)
goto nlmsg_failure;
if (nla_put_u8(skb2, IPSET_ATTR_PROTOCOL, protocol(attr)) ||
nla_put_string(skb, IPSET_ATTR_SETNAME, set->name))
goto nla_put_failure;
nlmsg_end(skb2, nlh2);

ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).portid, MSG_DONTWAIT);
Expand Down Expand Up @@ -1916,6 +2036,16 @@ static const struct nfnl_callback ip_set_netlink_subsys_cb[IPSET_MSG_MAX] = {
.attr_count = IPSET_ATTR_CMD_MAX,
.policy = ip_set_protocol_policy,
},
[IPSET_CMD_GET_BYNAME] = {
.call = ip_set_byname,
.attr_count = IPSET_ATTR_CMD_MAX,
.policy = ip_set_setname_policy,
},
[IPSET_CMD_GET_BYINDEX] = {
.call = ip_set_byindex,
.attr_count = IPSET_ATTR_CMD_MAX,
.policy = ip_set_index_policy,
},
};

static struct nfnetlink_subsystem ip_set_netlink_subsys __read_mostly = {
Expand Down Expand Up @@ -1961,7 +2091,7 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len)
goto done;
}

if (req_version->version != IPSET_PROTOCOL) {
if (req_version->version < IPSET_PROTOCOL_MIN) {
ret = -EPROTO;
goto done;
}
Expand Down
Loading

0 comments on commit 1226cfe

Please sign in to comment.