mirror of
https://github.com/torvalds/linux.git
synced 2024-11-15 08:31:55 +00:00
netfilter pull request 23-12-22
-----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEN9lkrMBJgcdVAPub1V2XiooUIOQFAmWFd9oACgkQ1V2XiooU IOTBog//auEj3LR5v2b5et6CLsuMESsCFkVko1AFdzzpu94sJ8TTkqlrW7SPSfPW CMNYz1EYFaRGK1GjfH0a49EUofeU/kPD/TmffmiYwuJlGyEIXp17ZIUQnGDrM6Mz UBHX8VrW73McJYuZJbxM4HX6Q3aFyshKy8iOPpffdFAQcCD5XtkYbW24Mzw6V4sI 8fBz6C/iT+5Jq1wGtvM2xmGkNYuMLJxbf9qOpinRZCXQV/z5IWda/bFnv3kpJ9mR x2ZYXHnzFEr2gVFIhZ7G6FvMgQAkkKGVQE+n9zVZ9V78czqBk9depdeLaq/RYGyx 8VYqJyaXHKQm7FqsBwSSxU964aQhi/zNrjF9SeJSfcRNhTmXgzdERndVWVjUPe9O rFLyQVoQ6JyKy5++1lL3+63cd6ZeGw8gUw8MCw9DX8rwamnNFunUsixKv4SLzQHA jfFgmAUq+HVWHXI4xmj+SDO8g+s7O3BpbZM09E3si6lwy4/LiQqHhV4sNgEVN/It hf76t/U6GRYZ0Hwy2dwQl8SZg1BxMSGFmzB7vyEZyGXaqxRZb1Jym4h9slOMKXWl gS5y4y0ZVTmzRCqJR+jUrMAKPCw/R964LrslDYwQpsQeP7nuUPeU5jH1VWD6kAbS Vc36uDRnfojoNiRcmAfuhvZIseM1+hI6UXCIsqYR6cekCFlBad8= =+Uvv -----END PGP SIGNATURE----- Merge tag 'nf-next-23-12-22' of git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf-next Pablo Neira Ayuso says: ==================== netfilter pull request 23-12-22 The following patchset contains Netfilter updates for net-next: 1) Add locking for NFT_MSG_GETSETELEM_RESET requests, to address a race scenario with two concurrent processes running a dump-and-reset which exposes negative counters to userspace, from Phil Sutter. 2) Use GFP_KERNEL in pipapo GC, from Florian Westphal. 3) Reorder nf_flowtable struct members, place the read-mostly parts accessed by the datapath first. From Florian Westphal. 4) Set on dead flag for NFT_MSG_NEWSET in abort path, from Florian Westphal. 5) Support filtering zone in ctnetlink, from Felix Huettner. 6) Bail out if user tries to redefine an existing chain with different type in nf_tables. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
109bf4cfe1
@ -74,12 +74,13 @@ enum nf_flowtable_flags {
|
||||
};
|
||||
|
||||
struct nf_flowtable {
|
||||
struct list_head list;
|
||||
struct rhashtable rhashtable;
|
||||
int priority;
|
||||
unsigned int flags; /* readonly in datapath */
|
||||
int priority; /* control path (padding hole) */
|
||||
struct rhashtable rhashtable; /* datapath, read-mostly members come first */
|
||||
|
||||
struct list_head list; /* slowpath parts */
|
||||
const struct nf_flowtable_type *type;
|
||||
struct delayed_work gc_work;
|
||||
unsigned int flags;
|
||||
struct flow_block flow_block;
|
||||
struct rw_semaphore flow_block_lock; /* Guards flow_block */
|
||||
possible_net_t net;
|
||||
|
@ -992,13 +992,13 @@ ctnetlink_alloc_filter(const struct nlattr * const cda[], u8 family)
|
||||
if (err)
|
||||
goto err_filter;
|
||||
|
||||
if (!cda[CTA_FILTER])
|
||||
return filter;
|
||||
|
||||
err = ctnetlink_parse_zone(cda[CTA_ZONE], &filter->zone);
|
||||
if (err < 0)
|
||||
goto err_filter;
|
||||
|
||||
if (!cda[CTA_FILTER])
|
||||
return filter;
|
||||
|
||||
err = ctnetlink_parse_filter(cda[CTA_FILTER], filter);
|
||||
if (err < 0)
|
||||
goto err_filter;
|
||||
@ -1043,7 +1043,7 @@ err_filter:
|
||||
|
||||
static bool ctnetlink_needs_filter(u8 family, const struct nlattr * const *cda)
|
||||
{
|
||||
return family || cda[CTA_MARK] || cda[CTA_FILTER] || cda[CTA_STATUS];
|
||||
return family || cda[CTA_MARK] || cda[CTA_FILTER] || cda[CTA_STATUS] || cda[CTA_ZONE];
|
||||
}
|
||||
|
||||
static int ctnetlink_start(struct netlink_callback *cb)
|
||||
@ -1148,6 +1148,10 @@ static int ctnetlink_filter_match(struct nf_conn *ct, void *data)
|
||||
if (filter->family && nf_ct_l3num(ct) != filter->family)
|
||||
goto ignore_entry;
|
||||
|
||||
if (filter->zone.id != NF_CT_DEFAULT_ZONE_ID &&
|
||||
!nf_ct_zone_equal_any(ct, &filter->zone))
|
||||
goto ignore_entry;
|
||||
|
||||
if (filter->orig_flags) {
|
||||
tuple = nf_ct_tuple(ct, IP_CT_DIR_ORIGINAL);
|
||||
if (!ctnetlink_filter_match_tuple(&filter->orig, tuple,
|
||||
|
@ -2261,7 +2261,16 @@ static int nft_chain_parse_hook(struct net *net,
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
type = basechain->type;
|
||||
if (nla[NFTA_CHAIN_TYPE]) {
|
||||
type = __nf_tables_chain_type_lookup(nla[NFTA_CHAIN_TYPE],
|
||||
family);
|
||||
if (!type) {
|
||||
NL_SET_BAD_ATTR(extack, nla[NFTA_CHAIN_TYPE]);
|
||||
return -ENOENT;
|
||||
}
|
||||
} else {
|
||||
type = basechain->type;
|
||||
}
|
||||
}
|
||||
|
||||
if (!try_module_get(type->owner)) {
|
||||
@ -5817,10 +5826,6 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
nla_nest_end(skb, nest);
|
||||
nlmsg_end(skb, nlh);
|
||||
|
||||
if (dump_ctx->reset && args.iter.count > args.iter.skip)
|
||||
audit_log_nft_set_reset(table, cb->seq,
|
||||
args.iter.count - args.iter.skip);
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
if (args.iter.err && args.iter.err != -EMSGSIZE)
|
||||
@ -5836,6 +5841,26 @@ nla_put_failure:
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
static int nf_tables_dumpreset_set(struct sk_buff *skb,
|
||||
struct netlink_callback *cb)
|
||||
{
|
||||
struct nftables_pernet *nft_net = nft_pernet(sock_net(skb->sk));
|
||||
struct nft_set_dump_ctx *dump_ctx = cb->data;
|
||||
int ret, skip = cb->args[0];
|
||||
|
||||
mutex_lock(&nft_net->commit_mutex);
|
||||
|
||||
ret = nf_tables_dump_set(skb, cb);
|
||||
|
||||
if (cb->args[0] > skip)
|
||||
audit_log_nft_set_reset(dump_ctx->ctx.table, cb->seq,
|
||||
cb->args[0] - skip);
|
||||
|
||||
mutex_unlock(&nft_net->commit_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nf_tables_dump_set_start(struct netlink_callback *cb)
|
||||
{
|
||||
struct nft_set_dump_ctx *dump_ctx = cb->data;
|
||||
@ -5910,7 +5935,7 @@ static int nft_setelem_parse_flags(const struct nft_set *set,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nft_setelem_parse_key(struct nft_ctx *ctx, struct nft_set *set,
|
||||
static int nft_setelem_parse_key(struct nft_ctx *ctx, const struct nft_set *set,
|
||||
struct nft_data *key, struct nlattr *attr)
|
||||
{
|
||||
struct nft_data_desc desc = {
|
||||
@ -5963,7 +5988,7 @@ static void *nft_setelem_catchall_get(const struct net *net,
|
||||
return priv;
|
||||
}
|
||||
|
||||
static int nft_setelem_get(struct nft_ctx *ctx, struct nft_set *set,
|
||||
static int nft_setelem_get(struct nft_ctx *ctx, const struct nft_set *set,
|
||||
struct nft_set_elem *elem, u32 flags)
|
||||
{
|
||||
void *priv;
|
||||
@ -5982,7 +6007,7 @@ static int nft_setelem_get(struct nft_ctx *ctx, struct nft_set *set,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nft_get_set_elem(struct nft_ctx *ctx, struct nft_set *set,
|
||||
static int nft_get_set_elem(struct nft_ctx *ctx, const struct nft_set *set,
|
||||
const struct nlattr *attr, bool reset)
|
||||
{
|
||||
struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
|
||||
@ -6039,21 +6064,18 @@ err_fill_setelem:
|
||||
return err;
|
||||
}
|
||||
|
||||
/* called with rcu_read_lock held */
|
||||
static int nf_tables_getsetelem(struct sk_buff *skb,
|
||||
const struct nfnl_info *info,
|
||||
const struct nlattr * const nla[])
|
||||
static int nft_set_dump_ctx_init(struct nft_set_dump_ctx *dump_ctx,
|
||||
const struct sk_buff *skb,
|
||||
const struct nfnl_info *info,
|
||||
const struct nlattr * const nla[],
|
||||
bool reset)
|
||||
{
|
||||
struct netlink_ext_ack *extack = info->extack;
|
||||
u8 genmask = nft_genmask_cur(info->net);
|
||||
u8 family = info->nfmsg->nfgen_family;
|
||||
int rem, err = 0, nelems = 0;
|
||||
struct net *net = info->net;
|
||||
struct nft_table *table;
|
||||
struct nft_set *set;
|
||||
struct nlattr *attr;
|
||||
struct nft_ctx ctx;
|
||||
bool reset = false;
|
||||
|
||||
table = nft_table_lookup(net, nla[NFTA_SET_ELEM_LIST_TABLE], family,
|
||||
genmask, 0);
|
||||
@ -6068,10 +6090,22 @@ static int nf_tables_getsetelem(struct sk_buff *skb,
|
||||
return PTR_ERR(set);
|
||||
}
|
||||
|
||||
nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla);
|
||||
nft_ctx_init(&dump_ctx->ctx, net, skb,
|
||||
info->nlh, family, table, NULL, nla);
|
||||
dump_ctx->set = set;
|
||||
dump_ctx->reset = reset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (NFNL_MSG_TYPE(info->nlh->nlmsg_type) == NFT_MSG_GETSETELEM_RESET)
|
||||
reset = true;
|
||||
/* called with rcu_read_lock held */
|
||||
static int nf_tables_getsetelem(struct sk_buff *skb,
|
||||
const struct nfnl_info *info,
|
||||
const struct nlattr * const nla[])
|
||||
{
|
||||
struct netlink_ext_ack *extack = info->extack;
|
||||
struct nft_set_dump_ctx dump_ctx;
|
||||
struct nlattr *attr;
|
||||
int rem, err = 0;
|
||||
|
||||
if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
|
||||
struct netlink_dump_control c = {
|
||||
@ -6080,11 +6114,10 @@ static int nf_tables_getsetelem(struct sk_buff *skb,
|
||||
.done = nf_tables_dump_set_done,
|
||||
.module = THIS_MODULE,
|
||||
};
|
||||
struct nft_set_dump_ctx dump_ctx = {
|
||||
.set = set,
|
||||
.ctx = ctx,
|
||||
.reset = reset,
|
||||
};
|
||||
|
||||
err = nft_set_dump_ctx_init(&dump_ctx, skb, info, nla, false);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
c.data = &dump_ctx;
|
||||
return nft_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c);
|
||||
@ -6093,18 +6126,75 @@ static int nf_tables_getsetelem(struct sk_buff *skb,
|
||||
if (!nla[NFTA_SET_ELEM_LIST_ELEMENTS])
|
||||
return -EINVAL;
|
||||
|
||||
err = nft_set_dump_ctx_init(&dump_ctx, skb, info, nla, false);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
|
||||
err = nft_get_set_elem(&ctx, set, attr, reset);
|
||||
err = nft_get_set_elem(&dump_ctx.ctx, dump_ctx.set, attr, false);
|
||||
if (err < 0) {
|
||||
NL_SET_BAD_ATTR(extack, attr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int nf_tables_getsetelem_reset(struct sk_buff *skb,
|
||||
const struct nfnl_info *info,
|
||||
const struct nlattr * const nla[])
|
||||
{
|
||||
struct nftables_pernet *nft_net = nft_pernet(info->net);
|
||||
struct netlink_ext_ack *extack = info->extack;
|
||||
struct nft_set_dump_ctx dump_ctx;
|
||||
int rem, err = 0, nelems = 0;
|
||||
struct nlattr *attr;
|
||||
|
||||
if (info->nlh->nlmsg_flags & NLM_F_DUMP) {
|
||||
struct netlink_dump_control c = {
|
||||
.start = nf_tables_dump_set_start,
|
||||
.dump = nf_tables_dumpreset_set,
|
||||
.done = nf_tables_dump_set_done,
|
||||
.module = THIS_MODULE,
|
||||
};
|
||||
|
||||
err = nft_set_dump_ctx_init(&dump_ctx, skb, info, nla, true);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
c.data = &dump_ctx;
|
||||
return nft_netlink_dump_start_rcu(info->sk, skb, info->nlh, &c);
|
||||
}
|
||||
|
||||
if (!nla[NFTA_SET_ELEM_LIST_ELEMENTS])
|
||||
return -EINVAL;
|
||||
|
||||
if (!try_module_get(THIS_MODULE))
|
||||
return -EINVAL;
|
||||
rcu_read_unlock();
|
||||
mutex_lock(&nft_net->commit_mutex);
|
||||
rcu_read_lock();
|
||||
|
||||
err = nft_set_dump_ctx_init(&dump_ctx, skb, info, nla, true);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
|
||||
nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
|
||||
err = nft_get_set_elem(&dump_ctx.ctx, dump_ctx.set, attr, true);
|
||||
if (err < 0) {
|
||||
NL_SET_BAD_ATTR(extack, attr);
|
||||
break;
|
||||
}
|
||||
nelems++;
|
||||
}
|
||||
audit_log_nft_set_reset(dump_ctx.ctx.table, nft_net->base_seq, nelems);
|
||||
|
||||
if (reset)
|
||||
audit_log_nft_set_reset(table, nft_pernet(net)->base_seq,
|
||||
nelems);
|
||||
out_unlock:
|
||||
rcu_read_unlock();
|
||||
mutex_unlock(&nft_net->commit_mutex);
|
||||
rcu_read_lock();
|
||||
module_put(THIS_MODULE);
|
||||
|
||||
return err;
|
||||
}
|
||||
@ -9078,7 +9168,7 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
|
||||
.policy = nft_set_elem_list_policy,
|
||||
},
|
||||
[NFT_MSG_GETSETELEM_RESET] = {
|
||||
.call = nf_tables_getsetelem,
|
||||
.call = nf_tables_getsetelem_reset,
|
||||
.type = NFNL_CB_RCU,
|
||||
.attr_count = NFTA_SET_ELEM_LIST_MAX,
|
||||
.policy = nft_set_elem_list_policy,
|
||||
@ -10383,6 +10473,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
|
||||
nft_trans_destroy(trans);
|
||||
break;
|
||||
}
|
||||
nft_trans_set(trans)->dead = 1;
|
||||
list_del_rcu(&nft_trans_set(trans)->list);
|
||||
break;
|
||||
case NFT_MSG_DELSET:
|
||||
|
@ -1597,7 +1597,7 @@ static void pipapo_gc(struct nft_set *set, struct nft_pipapo_match *m)
|
||||
if (nft_set_elem_expired(&e->ext)) {
|
||||
priv->dirty = true;
|
||||
|
||||
gc = nft_trans_gc_queue_sync(gc, GFP_ATOMIC);
|
||||
gc = nft_trans_gc_queue_sync(gc, GFP_KERNEL);
|
||||
if (!gc)
|
||||
return;
|
||||
|
||||
|
2
tools/testing/selftests/netfilter/.gitignore
vendored
2
tools/testing/selftests/netfilter/.gitignore
vendored
@ -2,3 +2,5 @@
|
||||
nf-queue
|
||||
connect_close
|
||||
audit_logread
|
||||
conntrack_dump_flush
|
||||
sctp_collision
|
||||
|
@ -14,6 +14,7 @@ HOSTPKG_CONFIG := pkg-config
|
||||
CFLAGS += $(shell $(HOSTPKG_CONFIG) --cflags libmnl 2>/dev/null)
|
||||
LDLIBS += $(shell $(HOSTPKG_CONFIG) --libs libmnl 2>/dev/null || echo -lmnl)
|
||||
|
||||
TEST_GEN_FILES = nf-queue connect_close audit_logread sctp_collision
|
||||
TEST_GEN_FILES = nf-queue connect_close audit_logread sctp_collision \
|
||||
conntrack_dump_flush
|
||||
|
||||
include ../lib.mk
|
||||
|
430
tools/testing/selftests/netfilter/conntrack_dump_flush.c
Normal file
430
tools/testing/selftests/netfilter/conntrack_dump_flush.c
Normal file
@ -0,0 +1,430 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <time.h>
|
||||
#include <libmnl/libmnl.h>
|
||||
#include <netinet/ip.h>
|
||||
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/netfilter/nfnetlink.h>
|
||||
#include <linux/netfilter/nfnetlink_conntrack.h>
|
||||
#include <linux/netfilter/nf_conntrack_tcp.h>
|
||||
#include "../kselftest_harness.h"
|
||||
|
||||
#define TEST_ZONE_ID 123
|
||||
#define CTA_FILTER_F_CTA_TUPLE_ZONE (1 << 2)
|
||||
|
||||
static int reply_counter;
|
||||
|
||||
static int build_cta_tuple_v4(struct nlmsghdr *nlh, int type,
|
||||
uint32_t src_ip, uint32_t dst_ip,
|
||||
uint16_t src_port, uint16_t dst_port)
|
||||
{
|
||||
struct nlattr *nest, *nest_ip, *nest_proto;
|
||||
|
||||
nest = mnl_attr_nest_start(nlh, type);
|
||||
if (!nest)
|
||||
return -1;
|
||||
|
||||
nest_ip = mnl_attr_nest_start(nlh, CTA_TUPLE_IP);
|
||||
if (!nest_ip)
|
||||
return -1;
|
||||
mnl_attr_put_u32(nlh, CTA_IP_V4_SRC, src_ip);
|
||||
mnl_attr_put_u32(nlh, CTA_IP_V4_DST, dst_ip);
|
||||
mnl_attr_nest_end(nlh, nest_ip);
|
||||
|
||||
nest_proto = mnl_attr_nest_start(nlh, CTA_TUPLE_PROTO);
|
||||
if (!nest_proto)
|
||||
return -1;
|
||||
mnl_attr_put_u8(nlh, CTA_PROTO_NUM, 6);
|
||||
mnl_attr_put_u16(nlh, CTA_PROTO_SRC_PORT, htons(src_port));
|
||||
mnl_attr_put_u16(nlh, CTA_PROTO_DST_PORT, htons(dst_port));
|
||||
mnl_attr_nest_end(nlh, nest_proto);
|
||||
|
||||
mnl_attr_nest_end(nlh, nest);
|
||||
}
|
||||
|
||||
static int build_cta_tuple_v6(struct nlmsghdr *nlh, int type,
|
||||
struct in6_addr src_ip, struct in6_addr dst_ip,
|
||||
uint16_t src_port, uint16_t dst_port)
|
||||
{
|
||||
struct nlattr *nest, *nest_ip, *nest_proto;
|
||||
|
||||
nest = mnl_attr_nest_start(nlh, type);
|
||||
if (!nest)
|
||||
return -1;
|
||||
|
||||
nest_ip = mnl_attr_nest_start(nlh, CTA_TUPLE_IP);
|
||||
if (!nest_ip)
|
||||
return -1;
|
||||
mnl_attr_put(nlh, CTA_IP_V6_SRC, sizeof(struct in6_addr), &src_ip);
|
||||
mnl_attr_put(nlh, CTA_IP_V6_DST, sizeof(struct in6_addr), &dst_ip);
|
||||
mnl_attr_nest_end(nlh, nest_ip);
|
||||
|
||||
nest_proto = mnl_attr_nest_start(nlh, CTA_TUPLE_PROTO);
|
||||
if (!nest_proto)
|
||||
return -1;
|
||||
mnl_attr_put_u8(nlh, CTA_PROTO_NUM, 6);
|
||||
mnl_attr_put_u16(nlh, CTA_PROTO_SRC_PORT, htons(src_port));
|
||||
mnl_attr_put_u16(nlh, CTA_PROTO_DST_PORT, htons(dst_port));
|
||||
mnl_attr_nest_end(nlh, nest_proto);
|
||||
|
||||
mnl_attr_nest_end(nlh, nest);
|
||||
}
|
||||
|
||||
static int build_cta_proto(struct nlmsghdr *nlh)
|
||||
{
|
||||
struct nlattr *nest, *nest_proto;
|
||||
|
||||
nest = mnl_attr_nest_start(nlh, CTA_PROTOINFO);
|
||||
if (!nest)
|
||||
return -1;
|
||||
|
||||
nest_proto = mnl_attr_nest_start(nlh, CTA_PROTOINFO_TCP);
|
||||
if (!nest_proto)
|
||||
return -1;
|
||||
mnl_attr_put_u8(nlh, CTA_PROTOINFO_TCP_STATE, TCP_CONNTRACK_ESTABLISHED);
|
||||
mnl_attr_put_u16(nlh, CTA_PROTOINFO_TCP_FLAGS_ORIGINAL, 0x0a0a);
|
||||
mnl_attr_put_u16(nlh, CTA_PROTOINFO_TCP_FLAGS_REPLY, 0x0a0a);
|
||||
mnl_attr_nest_end(nlh, nest_proto);
|
||||
|
||||
mnl_attr_nest_end(nlh, nest);
|
||||
}
|
||||
|
||||
static int conntrack_data_insert(struct mnl_socket *sock, struct nlmsghdr *nlh,
|
||||
uint16_t zone)
|
||||
{
|
||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
||||
struct nlmsghdr *rplnlh;
|
||||
unsigned int portid;
|
||||
int err, ret;
|
||||
|
||||
portid = mnl_socket_get_portid(sock);
|
||||
|
||||
ret = build_cta_proto(nlh);
|
||||
if (ret < 0) {
|
||||
perror("build_cta_proto");
|
||||
return -1;
|
||||
}
|
||||
mnl_attr_put_u32(nlh, CTA_TIMEOUT, htonl(20000));
|
||||
mnl_attr_put_u16(nlh, CTA_ZONE, htons(zone));
|
||||
|
||||
if (mnl_socket_sendto(sock, nlh, nlh->nlmsg_len) < 0) {
|
||||
perror("mnl_socket_sendto");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = mnl_socket_recvfrom(sock, buf, MNL_SOCKET_BUFFER_SIZE);
|
||||
if (ret < 0) {
|
||||
perror("mnl_socket_recvfrom");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mnl_cb_run(buf, ret, nlh->nlmsg_seq, portid, NULL, NULL);
|
||||
if (ret < 0) {
|
||||
if (errno == EEXIST) {
|
||||
/* The entries are probably still there from a previous
|
||||
* run. So we are good
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
perror("mnl_cb_run");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int conntrack_data_generate_v4(struct mnl_socket *sock, uint32_t src_ip,
|
||||
uint32_t dst_ip, uint16_t zone)
|
||||
{
|
||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
||||
struct nlmsghdr *nlh;
|
||||
struct nfgenmsg *nfh;
|
||||
int ret;
|
||||
|
||||
nlh = mnl_nlmsg_put_header(buf);
|
||||
nlh->nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_NEW;
|
||||
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE |
|
||||
NLM_F_ACK | NLM_F_EXCL;
|
||||
nlh->nlmsg_seq = time(NULL);
|
||||
|
||||
nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
|
||||
nfh->nfgen_family = AF_INET;
|
||||
nfh->version = NFNETLINK_V0;
|
||||
nfh->res_id = 0;
|
||||
|
||||
ret = build_cta_tuple_v4(nlh, CTA_TUPLE_ORIG, src_ip, dst_ip, 12345, 443);
|
||||
if (ret < 0) {
|
||||
perror("build_cta_tuple_v4");
|
||||
return ret;
|
||||
}
|
||||
ret = build_cta_tuple_v4(nlh, CTA_TUPLE_REPLY, dst_ip, src_ip, 443, 12345);
|
||||
if (ret < 0) {
|
||||
perror("build_cta_tuple_v4");
|
||||
return ret;
|
||||
}
|
||||
return conntrack_data_insert(sock, nlh, zone);
|
||||
}
|
||||
|
||||
static int conntrack_data_generate_v6(struct mnl_socket *sock,
|
||||
struct in6_addr src_ip,
|
||||
struct in6_addr dst_ip,
|
||||
uint16_t zone)
|
||||
{
|
||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
||||
struct nlmsghdr *nlh;
|
||||
struct nfgenmsg *nfh;
|
||||
int ret;
|
||||
|
||||
nlh = mnl_nlmsg_put_header(buf);
|
||||
nlh->nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_NEW;
|
||||
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE |
|
||||
NLM_F_ACK | NLM_F_EXCL;
|
||||
nlh->nlmsg_seq = time(NULL);
|
||||
|
||||
nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
|
||||
nfh->nfgen_family = AF_INET6;
|
||||
nfh->version = NFNETLINK_V0;
|
||||
nfh->res_id = 0;
|
||||
|
||||
ret = build_cta_tuple_v6(nlh, CTA_TUPLE_ORIG, src_ip, dst_ip,
|
||||
12345, 443);
|
||||
if (ret < 0) {
|
||||
perror("build_cta_tuple_v6");
|
||||
return ret;
|
||||
}
|
||||
ret = build_cta_tuple_v6(nlh, CTA_TUPLE_REPLY, dst_ip, src_ip,
|
||||
12345, 443);
|
||||
if (ret < 0) {
|
||||
perror("build_cta_tuple_v6");
|
||||
return ret;
|
||||
}
|
||||
return conntrack_data_insert(sock, nlh, zone);
|
||||
}
|
||||
|
||||
static int count_entries(const struct nlmsghdr *nlh, void *data)
|
||||
{
|
||||
reply_counter++;
|
||||
}
|
||||
|
||||
static int conntracK_count_zone(struct mnl_socket *sock, uint16_t zone)
|
||||
{
|
||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
||||
struct nlmsghdr *nlh, *rplnlh;
|
||||
struct nfgenmsg *nfh;
|
||||
struct nlattr *nest;
|
||||
unsigned int portid;
|
||||
int err, ret;
|
||||
|
||||
portid = mnl_socket_get_portid(sock);
|
||||
|
||||
nlh = mnl_nlmsg_put_header(buf);
|
||||
nlh->nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_GET;
|
||||
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
|
||||
nlh->nlmsg_seq = time(NULL);
|
||||
|
||||
nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
|
||||
nfh->nfgen_family = AF_UNSPEC;
|
||||
nfh->version = NFNETLINK_V0;
|
||||
nfh->res_id = 0;
|
||||
|
||||
mnl_attr_put_u16(nlh, CTA_ZONE, htons(zone));
|
||||
|
||||
ret = mnl_socket_sendto(sock, nlh, nlh->nlmsg_len);
|
||||
if (ret < 0) {
|
||||
perror("mnl_socket_sendto");
|
||||
return ret;
|
||||
}
|
||||
|
||||
reply_counter = 0;
|
||||
ret = mnl_socket_recvfrom(sock, buf, MNL_SOCKET_BUFFER_SIZE);
|
||||
while (ret > 0) {
|
||||
ret = mnl_cb_run(buf, ret, nlh->nlmsg_seq, portid,
|
||||
count_entries, NULL);
|
||||
if (ret <= MNL_CB_STOP)
|
||||
break;
|
||||
|
||||
ret = mnl_socket_recvfrom(sock, buf, MNL_SOCKET_BUFFER_SIZE);
|
||||
}
|
||||
if (ret < 0) {
|
||||
perror("mnl_socket_recvfrom");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return reply_counter;
|
||||
}
|
||||
|
||||
static int conntrack_flush_zone(struct mnl_socket *sock, uint16_t zone)
|
||||
{
|
||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
||||
struct nlmsghdr *nlh, *rplnlh;
|
||||
struct nfgenmsg *nfh;
|
||||
struct nlattr *nest;
|
||||
unsigned int portid;
|
||||
int err, ret;
|
||||
|
||||
portid = mnl_socket_get_portid(sock);
|
||||
|
||||
nlh = mnl_nlmsg_put_header(buf);
|
||||
nlh->nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_DELETE;
|
||||
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
|
||||
nlh->nlmsg_seq = time(NULL);
|
||||
|
||||
nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
|
||||
nfh->nfgen_family = AF_UNSPEC;
|
||||
nfh->version = NFNETLINK_V0;
|
||||
nfh->res_id = 0;
|
||||
|
||||
mnl_attr_put_u16(nlh, CTA_ZONE, htons(zone));
|
||||
|
||||
ret = mnl_socket_sendto(sock, nlh, nlh->nlmsg_len);
|
||||
if (ret < 0) {
|
||||
perror("mnl_socket_sendto");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mnl_socket_recvfrom(sock, buf, MNL_SOCKET_BUFFER_SIZE);
|
||||
if (ret < 0) {
|
||||
perror("mnl_socket_recvfrom");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mnl_cb_run(buf, ret, nlh->nlmsg_seq, portid, NULL, NULL);
|
||||
if (ret < 0) {
|
||||
perror("mnl_cb_run");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
FIXTURE(conntrack_dump_flush)
|
||||
{
|
||||
struct mnl_socket *sock;
|
||||
};
|
||||
|
||||
FIXTURE_SETUP(conntrack_dump_flush)
|
||||
{
|
||||
struct in6_addr src, dst;
|
||||
int ret;
|
||||
|
||||
self->sock = mnl_socket_open(NETLINK_NETFILTER);
|
||||
if (!self->sock) {
|
||||
perror("mnl_socket_open");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (mnl_socket_bind(self->sock, 0, MNL_SOCKET_AUTOPID) < 0) {
|
||||
perror("mnl_socket_bind");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
ret = conntracK_count_zone(self->sock, TEST_ZONE_ID);
|
||||
if (ret < 0 && errno == EPERM)
|
||||
SKIP(return, "Needs to be run as root");
|
||||
else if (ret < 0 && errno == EOPNOTSUPP)
|
||||
SKIP(return, "Kernel does not seem to support conntrack zones");
|
||||
|
||||
ret = conntrack_data_generate_v4(self->sock, 0xf0f0f0f0, 0xf1f1f1f1,
|
||||
TEST_ZONE_ID);
|
||||
EXPECT_EQ(ret, 0);
|
||||
ret = conntrack_data_generate_v4(self->sock, 0xf2f2f2f2, 0xf3f3f3f3,
|
||||
TEST_ZONE_ID + 1);
|
||||
EXPECT_EQ(ret, 0);
|
||||
ret = conntrack_data_generate_v4(self->sock, 0xf4f4f4f4, 0xf5f5f5f5,
|
||||
TEST_ZONE_ID + 2);
|
||||
EXPECT_EQ(ret, 0);
|
||||
|
||||
src = (struct in6_addr) {{
|
||||
.__u6_addr32 = {
|
||||
0xb80d0120,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x01000000
|
||||
}
|
||||
}};
|
||||
dst = (struct in6_addr) {{
|
||||
.__u6_addr32 = {
|
||||
0xb80d0120,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x02000000
|
||||
}
|
||||
}};
|
||||
ret = conntrack_data_generate_v6(self->sock, src, dst,
|
||||
TEST_ZONE_ID);
|
||||
EXPECT_EQ(ret, 0);
|
||||
src = (struct in6_addr) {{
|
||||
.__u6_addr32 = {
|
||||
0xb80d0120,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x03000000
|
||||
}
|
||||
}};
|
||||
dst = (struct in6_addr) {{
|
||||
.__u6_addr32 = {
|
||||
0xb80d0120,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x04000000
|
||||
}
|
||||
}};
|
||||
ret = conntrack_data_generate_v6(self->sock, src, dst,
|
||||
TEST_ZONE_ID + 1);
|
||||
EXPECT_EQ(ret, 0);
|
||||
src = (struct in6_addr) {{
|
||||
.__u6_addr32 = {
|
||||
0xb80d0120,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x05000000
|
||||
}
|
||||
}};
|
||||
dst = (struct in6_addr) {{
|
||||
.__u6_addr32 = {
|
||||
0xb80d0120,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x06000000
|
||||
}
|
||||
}};
|
||||
ret = conntrack_data_generate_v6(self->sock, src, dst,
|
||||
TEST_ZONE_ID + 2);
|
||||
EXPECT_EQ(ret, 0);
|
||||
|
||||
ret = conntracK_count_zone(self->sock, TEST_ZONE_ID);
|
||||
EXPECT_GE(ret, 2);
|
||||
if (ret > 2)
|
||||
SKIP(return, "kernel does not support filtering by zone");
|
||||
}
|
||||
|
||||
FIXTURE_TEARDOWN(conntrack_dump_flush)
|
||||
{
|
||||
}
|
||||
|
||||
TEST_F(conntrack_dump_flush, test_dump_by_zone)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = conntracK_count_zone(self->sock, TEST_ZONE_ID);
|
||||
EXPECT_EQ(ret, 2);
|
||||
}
|
||||
|
||||
TEST_F(conntrack_dump_flush, test_flush_by_zone)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = conntrack_flush_zone(self->sock, TEST_ZONE_ID);
|
||||
EXPECT_EQ(ret, 0);
|
||||
ret = conntracK_count_zone(self->sock, TEST_ZONE_ID);
|
||||
EXPECT_EQ(ret, 0);
|
||||
ret = conntracK_count_zone(self->sock, TEST_ZONE_ID + 1);
|
||||
EXPECT_EQ(ret, 2);
|
||||
ret = conntracK_count_zone(self->sock, TEST_ZONE_ID + 2);
|
||||
EXPECT_EQ(ret, 2);
|
||||
}
|
||||
|
||||
TEST_HARNESS_MAIN
|
Loading…
Reference in New Issue
Block a user