cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

xt_set.c (20111B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
      3 *                         Patrick Schaaf <bof@bof.de>
      4 *                         Martin Josefsson <gandalf@wlug.westbo.se>
      5 * Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@netfilter.org>
      6 */
      7
      8/* Kernel module which implements the set match and SET target
      9 * for netfilter/iptables.
     10 */
     11
     12#include <linux/module.h>
     13#include <linux/skbuff.h>
     14
     15#include <linux/netfilter/x_tables.h>
     16#include <linux/netfilter/ipset/ip_set.h>
     17#include <uapi/linux/netfilter/xt_set.h>
     18
     19MODULE_LICENSE("GPL");
     20MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@netfilter.org>");
     21MODULE_DESCRIPTION("Xtables: IP set match and target module");
     22MODULE_ALIAS("xt_SET");
     23MODULE_ALIAS("ipt_set");
     24MODULE_ALIAS("ip6t_set");
     25MODULE_ALIAS("ipt_SET");
     26MODULE_ALIAS("ip6t_SET");
     27
     28static inline int
     29match_set(ip_set_id_t index, const struct sk_buff *skb,
     30	  const struct xt_action_param *par,
     31	  struct ip_set_adt_opt *opt, int inv)
     32{
     33	if (ip_set_test(index, skb, par, opt))
     34		inv = !inv;
     35	return inv;
     36}
     37
     38#define ADT_OPT(n, f, d, fs, cfs, t, p, b, po, bo)	\
     39struct ip_set_adt_opt n = {				\
     40	.family	= f,					\
     41	.dim = d,					\
     42	.flags = fs,					\
     43	.cmdflags = cfs,				\
     44	.ext.timeout = t,				\
     45	.ext.packets = p,				\
     46	.ext.bytes = b,					\
     47	.ext.packets_op = po,				\
     48	.ext.bytes_op = bo,				\
     49}
     50
     51/* Revision 0 interface: backward compatible with netfilter/iptables */
     52
     53static bool
     54set_match_v0(const struct sk_buff *skb, struct xt_action_param *par)
     55{
     56	const struct xt_set_info_match_v0 *info = par->matchinfo;
     57
     58	ADT_OPT(opt, xt_family(par), info->match_set.u.compat.dim,
     59		info->match_set.u.compat.flags, 0, UINT_MAX,
     60		0, 0, 0, 0);
     61
     62	return match_set(info->match_set.index, skb, par, &opt,
     63			 info->match_set.u.compat.flags & IPSET_INV_MATCH);
     64}
     65
     66static void
     67compat_flags(struct xt_set_info_v0 *info)
     68{
     69	u_int8_t i;
     70
     71	/* Fill out compatibility data according to enum ip_set_kopt */
     72	info->u.compat.dim = IPSET_DIM_ZERO;
     73	if (info->u.flags[0] & IPSET_MATCH_INV)
     74		info->u.compat.flags |= IPSET_INV_MATCH;
     75	for (i = 0; i < IPSET_DIM_MAX - 1 && info->u.flags[i]; i++) {
     76		info->u.compat.dim++;
     77		if (info->u.flags[i] & IPSET_SRC)
     78			info->u.compat.flags |= (1 << info->u.compat.dim);
     79	}
     80}
     81
     82static int
     83set_match_v0_checkentry(const struct xt_mtchk_param *par)
     84{
     85	struct xt_set_info_match_v0 *info = par->matchinfo;
     86	ip_set_id_t index;
     87
     88	index = ip_set_nfnl_get_byindex(par->net, info->match_set.index);
     89
     90	if (index == IPSET_INVALID_ID) {
     91		pr_info_ratelimited("Cannot find set identified by id %u to match\n",
     92				    info->match_set.index);
     93		return -ENOENT;
     94	}
     95	if (info->match_set.u.flags[IPSET_DIM_MAX - 1] != 0) {
     96		pr_info_ratelimited("set match dimension is over the limit!\n");
     97		ip_set_nfnl_put(par->net, info->match_set.index);
     98		return -ERANGE;
     99	}
    100
    101	/* Fill out compatibility data */
    102	compat_flags(&info->match_set);
    103
    104	return 0;
    105}
    106
    107static void
    108set_match_v0_destroy(const struct xt_mtdtor_param *par)
    109{
    110	struct xt_set_info_match_v0 *info = par->matchinfo;
    111
    112	ip_set_nfnl_put(par->net, info->match_set.index);
    113}
    114
    115/* Revision 1 match */
    116
    117static bool
    118set_match_v1(const struct sk_buff *skb, struct xt_action_param *par)
    119{
    120	const struct xt_set_info_match_v1 *info = par->matchinfo;
    121
    122	ADT_OPT(opt, xt_family(par), info->match_set.dim,
    123		info->match_set.flags, 0, UINT_MAX,
    124		0, 0, 0, 0);
    125
    126	if (opt.flags & IPSET_RETURN_NOMATCH)
    127		opt.cmdflags |= IPSET_FLAG_RETURN_NOMATCH;
    128
    129	return match_set(info->match_set.index, skb, par, &opt,
    130			 info->match_set.flags & IPSET_INV_MATCH);
    131}
    132
    133static int
    134set_match_v1_checkentry(const struct xt_mtchk_param *par)
    135{
    136	struct xt_set_info_match_v1 *info = par->matchinfo;
    137	ip_set_id_t index;
    138
    139	index = ip_set_nfnl_get_byindex(par->net, info->match_set.index);
    140
    141	if (index == IPSET_INVALID_ID) {
    142		pr_info_ratelimited("Cannot find set identified by id %u to match\n",
    143				    info->match_set.index);
    144		return -ENOENT;
    145	}
    146	if (info->match_set.dim > IPSET_DIM_MAX) {
    147		pr_info_ratelimited("set match dimension is over the limit!\n");
    148		ip_set_nfnl_put(par->net, info->match_set.index);
    149		return -ERANGE;
    150	}
    151
    152	return 0;
    153}
    154
    155static void
    156set_match_v1_destroy(const struct xt_mtdtor_param *par)
    157{
    158	struct xt_set_info_match_v1 *info = par->matchinfo;
    159
    160	ip_set_nfnl_put(par->net, info->match_set.index);
    161}
    162
    163/* Revision 3 match */
    164
    165static bool
    166set_match_v3(const struct sk_buff *skb, struct xt_action_param *par)
    167{
    168	const struct xt_set_info_match_v3 *info = par->matchinfo;
    169
    170	ADT_OPT(opt, xt_family(par), info->match_set.dim,
    171		info->match_set.flags, info->flags, UINT_MAX,
    172		info->packets.value, info->bytes.value,
    173		info->packets.op, info->bytes.op);
    174
    175	if (info->packets.op != IPSET_COUNTER_NONE ||
    176	    info->bytes.op != IPSET_COUNTER_NONE)
    177		opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS;
    178
    179	return match_set(info->match_set.index, skb, par, &opt,
    180			 info->match_set.flags & IPSET_INV_MATCH);
    181}
    182
    183#define set_match_v3_checkentry	set_match_v1_checkentry
    184#define set_match_v3_destroy	set_match_v1_destroy
    185
    186/* Revision 4 match */
    187
    188static bool
    189set_match_v4(const struct sk_buff *skb, struct xt_action_param *par)
    190{
    191	const struct xt_set_info_match_v4 *info = par->matchinfo;
    192
    193	ADT_OPT(opt, xt_family(par), info->match_set.dim,
    194		info->match_set.flags, info->flags, UINT_MAX,
    195		info->packets.value, info->bytes.value,
    196		info->packets.op, info->bytes.op);
    197
    198	if (info->packets.op != IPSET_COUNTER_NONE ||
    199	    info->bytes.op != IPSET_COUNTER_NONE)
    200		opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS;
    201
    202	return match_set(info->match_set.index, skb, par, &opt,
    203			 info->match_set.flags & IPSET_INV_MATCH);
    204}
    205
    206#define set_match_v4_checkentry	set_match_v1_checkentry
    207#define set_match_v4_destroy	set_match_v1_destroy
    208
    209/* Revision 0 interface: backward compatible with netfilter/iptables */
    210
    211static unsigned int
    212set_target_v0(struct sk_buff *skb, const struct xt_action_param *par)
    213{
    214	const struct xt_set_info_target_v0 *info = par->targinfo;
    215
    216	ADT_OPT(add_opt, xt_family(par), info->add_set.u.compat.dim,
    217		info->add_set.u.compat.flags, 0, UINT_MAX,
    218		0, 0, 0, 0);
    219	ADT_OPT(del_opt, xt_family(par), info->del_set.u.compat.dim,
    220		info->del_set.u.compat.flags, 0, UINT_MAX,
    221		0, 0, 0, 0);
    222
    223	if (info->add_set.index != IPSET_INVALID_ID)
    224		ip_set_add(info->add_set.index, skb, par, &add_opt);
    225	if (info->del_set.index != IPSET_INVALID_ID)
    226		ip_set_del(info->del_set.index, skb, par, &del_opt);
    227
    228	return XT_CONTINUE;
    229}
    230
    231static int
    232set_target_v0_checkentry(const struct xt_tgchk_param *par)
    233{
    234	struct xt_set_info_target_v0 *info = par->targinfo;
    235	ip_set_id_t index;
    236
    237	if (info->add_set.index != IPSET_INVALID_ID) {
    238		index = ip_set_nfnl_get_byindex(par->net, info->add_set.index);
    239		if (index == IPSET_INVALID_ID) {
    240			pr_info_ratelimited("Cannot find add_set index %u as target\n",
    241					    info->add_set.index);
    242			return -ENOENT;
    243		}
    244	}
    245
    246	if (info->del_set.index != IPSET_INVALID_ID) {
    247		index = ip_set_nfnl_get_byindex(par->net, info->del_set.index);
    248		if (index == IPSET_INVALID_ID) {
    249			pr_info_ratelimited("Cannot find del_set index %u as target\n",
    250					    info->del_set.index);
    251			if (info->add_set.index != IPSET_INVALID_ID)
    252				ip_set_nfnl_put(par->net, info->add_set.index);
    253			return -ENOENT;
    254		}
    255	}
    256	if (info->add_set.u.flags[IPSET_DIM_MAX - 1] != 0 ||
    257	    info->del_set.u.flags[IPSET_DIM_MAX - 1] != 0) {
    258		pr_info_ratelimited("SET target dimension over the limit!\n");
    259		if (info->add_set.index != IPSET_INVALID_ID)
    260			ip_set_nfnl_put(par->net, info->add_set.index);
    261		if (info->del_set.index != IPSET_INVALID_ID)
    262			ip_set_nfnl_put(par->net, info->del_set.index);
    263		return -ERANGE;
    264	}
    265
    266	/* Fill out compatibility data */
    267	compat_flags(&info->add_set);
    268	compat_flags(&info->del_set);
    269
    270	return 0;
    271}
    272
    273static void
    274set_target_v0_destroy(const struct xt_tgdtor_param *par)
    275{
    276	const struct xt_set_info_target_v0 *info = par->targinfo;
    277
    278	if (info->add_set.index != IPSET_INVALID_ID)
    279		ip_set_nfnl_put(par->net, info->add_set.index);
    280	if (info->del_set.index != IPSET_INVALID_ID)
    281		ip_set_nfnl_put(par->net, info->del_set.index);
    282}
    283
    284/* Revision 1 target */
    285
    286static unsigned int
    287set_target_v1(struct sk_buff *skb, const struct xt_action_param *par)
    288{
    289	const struct xt_set_info_target_v1 *info = par->targinfo;
    290
    291	ADT_OPT(add_opt, xt_family(par), info->add_set.dim,
    292		info->add_set.flags, 0, UINT_MAX,
    293		0, 0, 0, 0);
    294	ADT_OPT(del_opt, xt_family(par), info->del_set.dim,
    295		info->del_set.flags, 0, UINT_MAX,
    296		0, 0, 0, 0);
    297
    298	if (info->add_set.index != IPSET_INVALID_ID)
    299		ip_set_add(info->add_set.index, skb, par, &add_opt);
    300	if (info->del_set.index != IPSET_INVALID_ID)
    301		ip_set_del(info->del_set.index, skb, par, &del_opt);
    302
    303	return XT_CONTINUE;
    304}
    305
    306static int
    307set_target_v1_checkentry(const struct xt_tgchk_param *par)
    308{
    309	const struct xt_set_info_target_v1 *info = par->targinfo;
    310	ip_set_id_t index;
    311
    312	if (info->add_set.index != IPSET_INVALID_ID) {
    313		index = ip_set_nfnl_get_byindex(par->net, info->add_set.index);
    314		if (index == IPSET_INVALID_ID) {
    315			pr_info_ratelimited("Cannot find add_set index %u as target\n",
    316					    info->add_set.index);
    317			return -ENOENT;
    318		}
    319	}
    320
    321	if (info->del_set.index != IPSET_INVALID_ID) {
    322		index = ip_set_nfnl_get_byindex(par->net, info->del_set.index);
    323		if (index == IPSET_INVALID_ID) {
    324			pr_info_ratelimited("Cannot find del_set index %u as target\n",
    325					    info->del_set.index);
    326			if (info->add_set.index != IPSET_INVALID_ID)
    327				ip_set_nfnl_put(par->net, info->add_set.index);
    328			return -ENOENT;
    329		}
    330	}
    331	if (info->add_set.dim > IPSET_DIM_MAX ||
    332	    info->del_set.dim > IPSET_DIM_MAX) {
    333		pr_info_ratelimited("SET target dimension over the limit!\n");
    334		if (info->add_set.index != IPSET_INVALID_ID)
    335			ip_set_nfnl_put(par->net, info->add_set.index);
    336		if (info->del_set.index != IPSET_INVALID_ID)
    337			ip_set_nfnl_put(par->net, info->del_set.index);
    338		return -ERANGE;
    339	}
    340
    341	return 0;
    342}
    343
    344static void
    345set_target_v1_destroy(const struct xt_tgdtor_param *par)
    346{
    347	const struct xt_set_info_target_v1 *info = par->targinfo;
    348
    349	if (info->add_set.index != IPSET_INVALID_ID)
    350		ip_set_nfnl_put(par->net, info->add_set.index);
    351	if (info->del_set.index != IPSET_INVALID_ID)
    352		ip_set_nfnl_put(par->net, info->del_set.index);
    353}
    354
    355/* Revision 2 target */
    356
    357static unsigned int
    358set_target_v2(struct sk_buff *skb, const struct xt_action_param *par)
    359{
    360	const struct xt_set_info_target_v2 *info = par->targinfo;
    361
    362	ADT_OPT(add_opt, xt_family(par), info->add_set.dim,
    363		info->add_set.flags, info->flags, info->timeout,
    364		0, 0, 0, 0);
    365	ADT_OPT(del_opt, xt_family(par), info->del_set.dim,
    366		info->del_set.flags, 0, UINT_MAX,
    367		0, 0, 0, 0);
    368
    369	/* Normalize to fit into jiffies */
    370	if (add_opt.ext.timeout != IPSET_NO_TIMEOUT &&
    371	    add_opt.ext.timeout > IPSET_MAX_TIMEOUT)
    372		add_opt.ext.timeout = IPSET_MAX_TIMEOUT;
    373	if (info->add_set.index != IPSET_INVALID_ID)
    374		ip_set_add(info->add_set.index, skb, par, &add_opt);
    375	if (info->del_set.index != IPSET_INVALID_ID)
    376		ip_set_del(info->del_set.index, skb, par, &del_opt);
    377
    378	return XT_CONTINUE;
    379}
    380
    381#define set_target_v2_checkentry	set_target_v1_checkentry
    382#define set_target_v2_destroy		set_target_v1_destroy
    383
    384/* Revision 3 target */
    385
    386#define MOPT(opt, member)	((opt).ext.skbinfo.member)
    387
    388static unsigned int
    389set_target_v3(struct sk_buff *skb, const struct xt_action_param *par)
    390{
    391	const struct xt_set_info_target_v3 *info = par->targinfo;
    392	int ret;
    393
    394	ADT_OPT(add_opt, xt_family(par), info->add_set.dim,
    395		info->add_set.flags, info->flags, info->timeout,
    396		0, 0, 0, 0);
    397	ADT_OPT(del_opt, xt_family(par), info->del_set.dim,
    398		info->del_set.flags, 0, UINT_MAX,
    399		0, 0, 0, 0);
    400	ADT_OPT(map_opt, xt_family(par), info->map_set.dim,
    401		info->map_set.flags, 0, UINT_MAX,
    402		0, 0, 0, 0);
    403
    404	/* Normalize to fit into jiffies */
    405	if (add_opt.ext.timeout != IPSET_NO_TIMEOUT &&
    406	    add_opt.ext.timeout > IPSET_MAX_TIMEOUT)
    407		add_opt.ext.timeout = IPSET_MAX_TIMEOUT;
    408	if (info->add_set.index != IPSET_INVALID_ID)
    409		ip_set_add(info->add_set.index, skb, par, &add_opt);
    410	if (info->del_set.index != IPSET_INVALID_ID)
    411		ip_set_del(info->del_set.index, skb, par, &del_opt);
    412	if (info->map_set.index != IPSET_INVALID_ID) {
    413		map_opt.cmdflags |= info->flags & (IPSET_FLAG_MAP_SKBMARK |
    414						   IPSET_FLAG_MAP_SKBPRIO |
    415						   IPSET_FLAG_MAP_SKBQUEUE);
    416		ret = match_set(info->map_set.index, skb, par, &map_opt,
    417				info->map_set.flags & IPSET_INV_MATCH);
    418		if (!ret)
    419			return XT_CONTINUE;
    420		if (map_opt.cmdflags & IPSET_FLAG_MAP_SKBMARK)
    421			skb->mark = (skb->mark & ~MOPT(map_opt,skbmarkmask))
    422				    ^ MOPT(map_opt, skbmark);
    423		if (map_opt.cmdflags & IPSET_FLAG_MAP_SKBPRIO)
    424			skb->priority = MOPT(map_opt, skbprio);
    425		if ((map_opt.cmdflags & IPSET_FLAG_MAP_SKBQUEUE) &&
    426		    skb->dev &&
    427		    skb->dev->real_num_tx_queues > MOPT(map_opt, skbqueue))
    428			skb_set_queue_mapping(skb, MOPT(map_opt, skbqueue));
    429	}
    430	return XT_CONTINUE;
    431}
    432
    433static int
    434set_target_v3_checkentry(const struct xt_tgchk_param *par)
    435{
    436	const struct xt_set_info_target_v3 *info = par->targinfo;
    437	ip_set_id_t index;
    438	int ret = 0;
    439
    440	if (info->add_set.index != IPSET_INVALID_ID) {
    441		index = ip_set_nfnl_get_byindex(par->net,
    442						info->add_set.index);
    443		if (index == IPSET_INVALID_ID) {
    444			pr_info_ratelimited("Cannot find add_set index %u as target\n",
    445					    info->add_set.index);
    446			return -ENOENT;
    447		}
    448	}
    449
    450	if (info->del_set.index != IPSET_INVALID_ID) {
    451		index = ip_set_nfnl_get_byindex(par->net,
    452						info->del_set.index);
    453		if (index == IPSET_INVALID_ID) {
    454			pr_info_ratelimited("Cannot find del_set index %u as target\n",
    455					    info->del_set.index);
    456			ret = -ENOENT;
    457			goto cleanup_add;
    458		}
    459	}
    460
    461	if (info->map_set.index != IPSET_INVALID_ID) {
    462		if (strncmp(par->table, "mangle", 7)) {
    463			pr_info_ratelimited("--map-set only usable from mangle table\n");
    464			ret = -EINVAL;
    465			goto cleanup_del;
    466		}
    467		if (((info->flags & IPSET_FLAG_MAP_SKBPRIO) |
    468		     (info->flags & IPSET_FLAG_MAP_SKBQUEUE)) &&
    469		     (par->hook_mask & ~(1 << NF_INET_FORWARD |
    470					 1 << NF_INET_LOCAL_OUT |
    471					 1 << NF_INET_POST_ROUTING))) {
    472			pr_info_ratelimited("mapping of prio or/and queue is allowed only from OUTPUT/FORWARD/POSTROUTING chains\n");
    473			ret = -EINVAL;
    474			goto cleanup_del;
    475		}
    476		index = ip_set_nfnl_get_byindex(par->net,
    477						info->map_set.index);
    478		if (index == IPSET_INVALID_ID) {
    479			pr_info_ratelimited("Cannot find map_set index %u as target\n",
    480					    info->map_set.index);
    481			ret = -ENOENT;
    482			goto cleanup_del;
    483		}
    484	}
    485
    486	if (info->add_set.dim > IPSET_DIM_MAX ||
    487	    info->del_set.dim > IPSET_DIM_MAX ||
    488	    info->map_set.dim > IPSET_DIM_MAX) {
    489		pr_info_ratelimited("SET target dimension over the limit!\n");
    490		ret = -ERANGE;
    491		goto cleanup_mark;
    492	}
    493
    494	return 0;
    495cleanup_mark:
    496	if (info->map_set.index != IPSET_INVALID_ID)
    497		ip_set_nfnl_put(par->net, info->map_set.index);
    498cleanup_del:
    499	if (info->del_set.index != IPSET_INVALID_ID)
    500		ip_set_nfnl_put(par->net, info->del_set.index);
    501cleanup_add:
    502	if (info->add_set.index != IPSET_INVALID_ID)
    503		ip_set_nfnl_put(par->net, info->add_set.index);
    504	return ret;
    505}
    506
    507static void
    508set_target_v3_destroy(const struct xt_tgdtor_param *par)
    509{
    510	const struct xt_set_info_target_v3 *info = par->targinfo;
    511
    512	if (info->add_set.index != IPSET_INVALID_ID)
    513		ip_set_nfnl_put(par->net, info->add_set.index);
    514	if (info->del_set.index != IPSET_INVALID_ID)
    515		ip_set_nfnl_put(par->net, info->del_set.index);
    516	if (info->map_set.index != IPSET_INVALID_ID)
    517		ip_set_nfnl_put(par->net, info->map_set.index);
    518}
    519
    520static struct xt_match set_matches[] __read_mostly = {
    521	{
    522		.name		= "set",
    523		.family		= NFPROTO_IPV4,
    524		.revision	= 0,
    525		.match		= set_match_v0,
    526		.matchsize	= sizeof(struct xt_set_info_match_v0),
    527		.checkentry	= set_match_v0_checkentry,
    528		.destroy	= set_match_v0_destroy,
    529		.me		= THIS_MODULE
    530	},
    531	{
    532		.name		= "set",
    533		.family		= NFPROTO_IPV4,
    534		.revision	= 1,
    535		.match		= set_match_v1,
    536		.matchsize	= sizeof(struct xt_set_info_match_v1),
    537		.checkentry	= set_match_v1_checkentry,
    538		.destroy	= set_match_v1_destroy,
    539		.me		= THIS_MODULE
    540	},
    541	{
    542		.name		= "set",
    543		.family		= NFPROTO_IPV6,
    544		.revision	= 1,
    545		.match		= set_match_v1,
    546		.matchsize	= sizeof(struct xt_set_info_match_v1),
    547		.checkentry	= set_match_v1_checkentry,
    548		.destroy	= set_match_v1_destroy,
    549		.me		= THIS_MODULE
    550	},
    551	/* --return-nomatch flag support */
    552	{
    553		.name		= "set",
    554		.family		= NFPROTO_IPV4,
    555		.revision	= 2,
    556		.match		= set_match_v1,
    557		.matchsize	= sizeof(struct xt_set_info_match_v1),
    558		.checkentry	= set_match_v1_checkentry,
    559		.destroy	= set_match_v1_destroy,
    560		.me		= THIS_MODULE
    561	},
    562	{
    563		.name		= "set",
    564		.family		= NFPROTO_IPV6,
    565		.revision	= 2,
    566		.match		= set_match_v1,
    567		.matchsize	= sizeof(struct xt_set_info_match_v1),
    568		.checkentry	= set_match_v1_checkentry,
    569		.destroy	= set_match_v1_destroy,
    570		.me		= THIS_MODULE
    571	},
    572	/* counters support: update, match */
    573	{
    574		.name		= "set",
    575		.family		= NFPROTO_IPV4,
    576		.revision	= 3,
    577		.match		= set_match_v3,
    578		.matchsize	= sizeof(struct xt_set_info_match_v3),
    579		.checkentry	= set_match_v3_checkentry,
    580		.destroy	= set_match_v3_destroy,
    581		.me		= THIS_MODULE
    582	},
    583	{
    584		.name		= "set",
    585		.family		= NFPROTO_IPV6,
    586		.revision	= 3,
    587		.match		= set_match_v3,
    588		.matchsize	= sizeof(struct xt_set_info_match_v3),
    589		.checkentry	= set_match_v3_checkentry,
    590		.destroy	= set_match_v3_destroy,
    591		.me		= THIS_MODULE
    592	},
    593	/* new revision for counters support: update, match */
    594	{
    595		.name		= "set",
    596		.family		= NFPROTO_IPV4,
    597		.revision	= 4,
    598		.match		= set_match_v4,
    599		.matchsize	= sizeof(struct xt_set_info_match_v4),
    600		.checkentry	= set_match_v4_checkentry,
    601		.destroy	= set_match_v4_destroy,
    602		.me		= THIS_MODULE
    603	},
    604	{
    605		.name		= "set",
    606		.family		= NFPROTO_IPV6,
    607		.revision	= 4,
    608		.match		= set_match_v4,
    609		.matchsize	= sizeof(struct xt_set_info_match_v4),
    610		.checkentry	= set_match_v4_checkentry,
    611		.destroy	= set_match_v4_destroy,
    612		.me		= THIS_MODULE
    613	},
    614};
    615
    616static struct xt_target set_targets[] __read_mostly = {
    617	{
    618		.name		= "SET",
    619		.revision	= 0,
    620		.family		= NFPROTO_IPV4,
    621		.target		= set_target_v0,
    622		.targetsize	= sizeof(struct xt_set_info_target_v0),
    623		.checkentry	= set_target_v0_checkentry,
    624		.destroy	= set_target_v0_destroy,
    625		.me		= THIS_MODULE
    626	},
    627	{
    628		.name		= "SET",
    629		.revision	= 1,
    630		.family		= NFPROTO_IPV4,
    631		.target		= set_target_v1,
    632		.targetsize	= sizeof(struct xt_set_info_target_v1),
    633		.checkentry	= set_target_v1_checkentry,
    634		.destroy	= set_target_v1_destroy,
    635		.me		= THIS_MODULE
    636	},
    637	{
    638		.name		= "SET",
    639		.revision	= 1,
    640		.family		= NFPROTO_IPV6,
    641		.target		= set_target_v1,
    642		.targetsize	= sizeof(struct xt_set_info_target_v1),
    643		.checkentry	= set_target_v1_checkentry,
    644		.destroy	= set_target_v1_destroy,
    645		.me		= THIS_MODULE
    646	},
    647	/* --timeout and --exist flags support */
    648	{
    649		.name		= "SET",
    650		.revision	= 2,
    651		.family		= NFPROTO_IPV4,
    652		.target		= set_target_v2,
    653		.targetsize	= sizeof(struct xt_set_info_target_v2),
    654		.checkentry	= set_target_v2_checkentry,
    655		.destroy	= set_target_v2_destroy,
    656		.me		= THIS_MODULE
    657	},
    658	{
    659		.name		= "SET",
    660		.revision	= 2,
    661		.family		= NFPROTO_IPV6,
    662		.target		= set_target_v2,
    663		.targetsize	= sizeof(struct xt_set_info_target_v2),
    664		.checkentry	= set_target_v2_checkentry,
    665		.destroy	= set_target_v2_destroy,
    666		.me		= THIS_MODULE
    667	},
    668	/* --map-set support */
    669	{
    670		.name		= "SET",
    671		.revision	= 3,
    672		.family		= NFPROTO_IPV4,
    673		.target		= set_target_v3,
    674		.targetsize	= sizeof(struct xt_set_info_target_v3),
    675		.checkentry	= set_target_v3_checkentry,
    676		.destroy	= set_target_v3_destroy,
    677		.me		= THIS_MODULE
    678	},
    679	{
    680		.name		= "SET",
    681		.revision	= 3,
    682		.family		= NFPROTO_IPV6,
    683		.target		= set_target_v3,
    684		.targetsize	= sizeof(struct xt_set_info_target_v3),
    685		.checkentry	= set_target_v3_checkentry,
    686		.destroy	= set_target_v3_destroy,
    687		.me		= THIS_MODULE
    688	},
    689};
    690
    691static int __init xt_set_init(void)
    692{
    693	int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches));
    694
    695	if (!ret) {
    696		ret = xt_register_targets(set_targets,
    697					  ARRAY_SIZE(set_targets));
    698		if (ret)
    699			xt_unregister_matches(set_matches,
    700					      ARRAY_SIZE(set_matches));
    701	}
    702	return ret;
    703}
    704
    705static void __exit xt_set_fini(void)
    706{
    707	xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches));
    708	xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets));
    709}
    710
    711module_init(xt_set_init);
    712module_exit(xt_set_fini);