712 lines · 19.6 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> *                         Patrick Schaaf <bof@bof.de> *                         Martin Josefsson <gandalf@wlug.westbo.se> * Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@netfilter.org> */ /* Kernel module which implements the set match and SET target * for netfilter/iptables. */ #include <linux/module.h>#include <linux/skbuff.h> #include <linux/netfilter/x_tables.h>#include <linux/netfilter/ipset/ip_set.h>#include <uapi/linux/netfilter/xt_set.h> MODULE_LICENSE("GPL");MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@netfilter.org>");MODULE_DESCRIPTION("Xtables: IP set match and target module");MODULE_ALIAS("xt_SET");MODULE_ALIAS("ipt_set");MODULE_ALIAS("ip6t_set");MODULE_ALIAS("ipt_SET");MODULE_ALIAS("ip6t_SET"); static inline intmatch_set(ip_set_id_t index, const struct sk_buff *skb,	  const struct xt_action_param *par,	  struct ip_set_adt_opt *opt, int inv){	if (ip_set_test(index, skb, par, opt))		inv = !inv;	return inv;} #define ADT_OPT(n, f, d, fs, cfs, t, p, b, po, bo)	\struct ip_set_adt_opt n = {				\	.family	= f,					\	.dim = d,					\	.flags = fs,					\	.cmdflags = cfs,				\	.ext.timeout = t,				\	.ext.packets = p,				\	.ext.bytes = b,					\	.ext.packets_op = po,				\	.ext.bytes_op = bo,				\} /* Revision 0 interface: backward compatible with netfilter/iptables */ static boolset_match_v0(const struct sk_buff *skb, struct xt_action_param *par){	const struct xt_set_info_match_v0 *info = par->matchinfo; 	ADT_OPT(opt, xt_family(par), info->match_set.u.compat.dim,		info->match_set.u.compat.flags, 0, UINT_MAX,		0, 0, 0, 0); 	return match_set(info->match_set.index, skb, par, &opt,			 info->match_set.u.compat.flags & IPSET_INV_MATCH);} static voidcompat_flags(struct xt_set_info_v0 *info){	u_int8_t i; 	/* Fill out compatibility data according to enum ip_set_kopt */	info->u.compat.dim = IPSET_DIM_ZERO;	if (info->u.flags[0] & IPSET_MATCH_INV)		info->u.compat.flags |= IPSET_INV_MATCH;	for (i = 0; i < IPSET_DIM_MAX - 1 && info->u.flags[i]; i++) {		info->u.compat.dim++;		if (info->u.flags[i] & IPSET_SRC)			info->u.compat.flags |= (1 << info->u.compat.dim);	}} static intset_match_v0_checkentry(const struct xt_mtchk_param *par){	struct xt_set_info_match_v0 *info = par->matchinfo;	ip_set_id_t index; 	index = ip_set_nfnl_get_byindex(par->net, info->match_set.index); 	if (index == IPSET_INVALID_ID) {		pr_info_ratelimited("Cannot find set identified by id %u to match\n",				    info->match_set.index);		return -ENOENT;	}	if (info->match_set.u.flags[IPSET_DIM_MAX - 1] != 0) {		pr_info_ratelimited("set match dimension is over the limit!\n");		ip_set_nfnl_put(par->net, info->match_set.index);		return -ERANGE;	} 	/* Fill out compatibility data */	compat_flags(&info->match_set); 	return 0;} static voidset_match_v0_destroy(const struct xt_mtdtor_param *par){	struct xt_set_info_match_v0 *info = par->matchinfo; 	ip_set_nfnl_put(par->net, info->match_set.index);} /* Revision 1 match */ static boolset_match_v1(const struct sk_buff *skb, struct xt_action_param *par){	const struct xt_set_info_match_v1 *info = par->matchinfo; 	ADT_OPT(opt, xt_family(par), info->match_set.dim,		info->match_set.flags, 0, UINT_MAX,		0, 0, 0, 0); 	if (opt.flags & IPSET_RETURN_NOMATCH)		opt.cmdflags |= IPSET_FLAG_RETURN_NOMATCH; 	return match_set(info->match_set.index, skb, par, &opt,			 info->match_set.flags & IPSET_INV_MATCH);} static intset_match_v1_checkentry(const struct xt_mtchk_param *par){	struct xt_set_info_match_v1 *info = par->matchinfo;	ip_set_id_t index; 	index = ip_set_nfnl_get_byindex(par->net, info->match_set.index); 	if (index == IPSET_INVALID_ID) {		pr_info_ratelimited("Cannot find set identified by id %u to match\n",				    info->match_set.index);		return -ENOENT;	}	if (info->match_set.dim > IPSET_DIM_MAX) {		pr_info_ratelimited("set match dimension is over the limit!\n");		ip_set_nfnl_put(par->net, info->match_set.index);		return -ERANGE;	} 	return 0;} static voidset_match_v1_destroy(const struct xt_mtdtor_param *par){	struct xt_set_info_match_v1 *info = par->matchinfo; 	ip_set_nfnl_put(par->net, info->match_set.index);} /* Revision 3 match */ static boolset_match_v3(const struct sk_buff *skb, struct xt_action_param *par){	const struct xt_set_info_match_v3 *info = par->matchinfo; 	ADT_OPT(opt, xt_family(par), info->match_set.dim,		info->match_set.flags, info->flags, UINT_MAX,		info->packets.value, info->bytes.value,		info->packets.op, info->bytes.op); 	if (info->packets.op != IPSET_COUNTER_NONE ||	    info->bytes.op != IPSET_COUNTER_NONE)		opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS; 	return match_set(info->match_set.index, skb, par, &opt,			 info->match_set.flags & IPSET_INV_MATCH);} #define set_match_v3_checkentry	set_match_v1_checkentry#define set_match_v3_destroy	set_match_v1_destroy /* Revision 4 match */ static boolset_match_v4(const struct sk_buff *skb, struct xt_action_param *par){	const struct xt_set_info_match_v4 *info = par->matchinfo; 	ADT_OPT(opt, xt_family(par), info->match_set.dim,		info->match_set.flags, info->flags, UINT_MAX,		info->packets.value, info->bytes.value,		info->packets.op, info->bytes.op); 	if (info->packets.op != IPSET_COUNTER_NONE ||	    info->bytes.op != IPSET_COUNTER_NONE)		opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS; 	return match_set(info->match_set.index, skb, par, &opt,			 info->match_set.flags & IPSET_INV_MATCH);} #define set_match_v4_checkentry	set_match_v1_checkentry#define set_match_v4_destroy	set_match_v1_destroy /* Revision 0 interface: backward compatible with netfilter/iptables */ static unsigned intset_target_v0(struct sk_buff *skb, const struct xt_action_param *par){	const struct xt_set_info_target_v0 *info = par->targinfo; 	ADT_OPT(add_opt, xt_family(par), info->add_set.u.compat.dim,		info->add_set.u.compat.flags, 0, UINT_MAX,		0, 0, 0, 0);	ADT_OPT(del_opt, xt_family(par), info->del_set.u.compat.dim,		info->del_set.u.compat.flags, 0, UINT_MAX,		0, 0, 0, 0); 	if (info->add_set.index != IPSET_INVALID_ID)		ip_set_add(info->add_set.index, skb, par, &add_opt);	if (info->del_set.index != IPSET_INVALID_ID)		ip_set_del(info->del_set.index, skb, par, &del_opt); 	return XT_CONTINUE;} static intset_target_v0_checkentry(const struct xt_tgchk_param *par){	struct xt_set_info_target_v0 *info = par->targinfo;	ip_set_id_t index; 	if (info->add_set.index != IPSET_INVALID_ID) {		index = ip_set_nfnl_get_byindex(par->net, info->add_set.index);		if (index == IPSET_INVALID_ID) {			pr_info_ratelimited("Cannot find add_set index %u as target\n",					    info->add_set.index);			return -ENOENT;		}	} 	if (info->del_set.index != IPSET_INVALID_ID) {		index = ip_set_nfnl_get_byindex(par->net, info->del_set.index);		if (index == IPSET_INVALID_ID) {			pr_info_ratelimited("Cannot find del_set index %u as target\n",					    info->del_set.index);			if (info->add_set.index != IPSET_INVALID_ID)				ip_set_nfnl_put(par->net, info->add_set.index);			return -ENOENT;		}	}	if (info->add_set.u.flags[IPSET_DIM_MAX - 1] != 0 ||	    info->del_set.u.flags[IPSET_DIM_MAX - 1] != 0) {		pr_info_ratelimited("SET target dimension over the limit!\n");		if (info->add_set.index != IPSET_INVALID_ID)			ip_set_nfnl_put(par->net, info->add_set.index);		if (info->del_set.index != IPSET_INVALID_ID)			ip_set_nfnl_put(par->net, info->del_set.index);		return -ERANGE;	} 	/* Fill out compatibility data */	compat_flags(&info->add_set);	compat_flags(&info->del_set); 	return 0;} static voidset_target_v0_destroy(const struct xt_tgdtor_param *par){	const struct xt_set_info_target_v0 *info = par->targinfo; 	if (info->add_set.index != IPSET_INVALID_ID)		ip_set_nfnl_put(par->net, info->add_set.index);	if (info->del_set.index != IPSET_INVALID_ID)		ip_set_nfnl_put(par->net, info->del_set.index);} /* Revision 1 target */ static unsigned intset_target_v1(struct sk_buff *skb, const struct xt_action_param *par){	const struct xt_set_info_target_v1 *info = par->targinfo; 	ADT_OPT(add_opt, xt_family(par), info->add_set.dim,		info->add_set.flags, 0, UINT_MAX,		0, 0, 0, 0);	ADT_OPT(del_opt, xt_family(par), info->del_set.dim,		info->del_set.flags, 0, UINT_MAX,		0, 0, 0, 0); 	if (info->add_set.index != IPSET_INVALID_ID)		ip_set_add(info->add_set.index, skb, par, &add_opt);	if (info->del_set.index != IPSET_INVALID_ID)		ip_set_del(info->del_set.index, skb, par, &del_opt); 	return XT_CONTINUE;} static intset_target_v1_checkentry(const struct xt_tgchk_param *par){	const struct xt_set_info_target_v1 *info = par->targinfo;	ip_set_id_t index; 	if (info->add_set.index != IPSET_INVALID_ID) {		index = ip_set_nfnl_get_byindex(par->net, info->add_set.index);		if (index == IPSET_INVALID_ID) {			pr_info_ratelimited("Cannot find add_set index %u as target\n",					    info->add_set.index);			return -ENOENT;		}	} 	if (info->del_set.index != IPSET_INVALID_ID) {		index = ip_set_nfnl_get_byindex(par->net, info->del_set.index);		if (index == IPSET_INVALID_ID) {			pr_info_ratelimited("Cannot find del_set index %u as target\n",					    info->del_set.index);			if (info->add_set.index != IPSET_INVALID_ID)				ip_set_nfnl_put(par->net, info->add_set.index);			return -ENOENT;		}	}	if (info->add_set.dim > IPSET_DIM_MAX ||	    info->del_set.dim > IPSET_DIM_MAX) {		pr_info_ratelimited("SET target dimension over the limit!\n");		if (info->add_set.index != IPSET_INVALID_ID)			ip_set_nfnl_put(par->net, info->add_set.index);		if (info->del_set.index != IPSET_INVALID_ID)			ip_set_nfnl_put(par->net, info->del_set.index);		return -ERANGE;	} 	return 0;} static voidset_target_v1_destroy(const struct xt_tgdtor_param *par){	const struct xt_set_info_target_v1 *info = par->targinfo; 	if (info->add_set.index != IPSET_INVALID_ID)		ip_set_nfnl_put(par->net, info->add_set.index);	if (info->del_set.index != IPSET_INVALID_ID)		ip_set_nfnl_put(par->net, info->del_set.index);} /* Revision 2 target */ static unsigned intset_target_v2(struct sk_buff *skb, const struct xt_action_param *par){	const struct xt_set_info_target_v2 *info = par->targinfo; 	ADT_OPT(add_opt, xt_family(par), info->add_set.dim,		info->add_set.flags, info->flags, info->timeout,		0, 0, 0, 0);	ADT_OPT(del_opt, xt_family(par), info->del_set.dim,		info->del_set.flags, 0, UINT_MAX,		0, 0, 0, 0); 	/* Normalize to fit into jiffies */	if (add_opt.ext.timeout != IPSET_NO_TIMEOUT &&	    add_opt.ext.timeout > IPSET_MAX_TIMEOUT)		add_opt.ext.timeout = IPSET_MAX_TIMEOUT;	if (info->add_set.index != IPSET_INVALID_ID)		ip_set_add(info->add_set.index, skb, par, &add_opt);	if (info->del_set.index != IPSET_INVALID_ID)		ip_set_del(info->del_set.index, skb, par, &del_opt); 	return XT_CONTINUE;} #define set_target_v2_checkentry	set_target_v1_checkentry#define set_target_v2_destroy		set_target_v1_destroy /* Revision 3 target */ #define MOPT(opt, member)	((opt).ext.skbinfo.member) static unsigned intset_target_v3(struct sk_buff *skb, const struct xt_action_param *par){	const struct xt_set_info_target_v3 *info = par->targinfo;	int ret; 	ADT_OPT(add_opt, xt_family(par), info->add_set.dim,		info->add_set.flags, info->flags, info->timeout,		0, 0, 0, 0);	ADT_OPT(del_opt, xt_family(par), info->del_set.dim,		info->del_set.flags, 0, UINT_MAX,		0, 0, 0, 0);	ADT_OPT(map_opt, xt_family(par), info->map_set.dim,		info->map_set.flags, 0, UINT_MAX,		0, 0, 0, 0); 	/* Normalize to fit into jiffies */	if (add_opt.ext.timeout != IPSET_NO_TIMEOUT &&	    add_opt.ext.timeout > IPSET_MAX_TIMEOUT)		add_opt.ext.timeout = IPSET_MAX_TIMEOUT;	if (info->add_set.index != IPSET_INVALID_ID)		ip_set_add(info->add_set.index, skb, par, &add_opt);	if (info->del_set.index != IPSET_INVALID_ID)		ip_set_del(info->del_set.index, skb, par, &del_opt);	if (info->map_set.index != IPSET_INVALID_ID) {		map_opt.cmdflags |= info->flags & (IPSET_FLAG_MAP_SKBMARK |						   IPSET_FLAG_MAP_SKBPRIO |						   IPSET_FLAG_MAP_SKBQUEUE);		ret = match_set(info->map_set.index, skb, par, &map_opt,				info->map_set.flags & IPSET_INV_MATCH);		if (!ret)			return XT_CONTINUE;		if (map_opt.cmdflags & IPSET_FLAG_MAP_SKBMARK)			skb->mark = (skb->mark & ~MOPT(map_opt,skbmarkmask))				    ^ MOPT(map_opt, skbmark);		if (map_opt.cmdflags & IPSET_FLAG_MAP_SKBPRIO)			skb->priority = MOPT(map_opt, skbprio);		if ((map_opt.cmdflags & IPSET_FLAG_MAP_SKBQUEUE) &&		    skb->dev &&		    skb->dev->real_num_tx_queues > MOPT(map_opt, skbqueue))			skb_set_queue_mapping(skb, MOPT(map_opt, skbqueue));	}	return XT_CONTINUE;} static intset_target_v3_checkentry(const struct xt_tgchk_param *par){	const struct xt_set_info_target_v3 *info = par->targinfo;	ip_set_id_t index;	int ret = 0; 	if (info->add_set.index != IPSET_INVALID_ID) {		index = ip_set_nfnl_get_byindex(par->net,						info->add_set.index);		if (index == IPSET_INVALID_ID) {			pr_info_ratelimited("Cannot find add_set index %u as target\n",					    info->add_set.index);			return -ENOENT;		}	} 	if (info->del_set.index != IPSET_INVALID_ID) {		index = ip_set_nfnl_get_byindex(par->net,						info->del_set.index);		if (index == IPSET_INVALID_ID) {			pr_info_ratelimited("Cannot find del_set index %u as target\n",					    info->del_set.index);			ret = -ENOENT;			goto cleanup_add;		}	} 	if (info->map_set.index != IPSET_INVALID_ID) {		if (strncmp(par->table, "mangle", 7)) {			pr_info_ratelimited("--map-set only usable from mangle table\n");			ret = -EINVAL;			goto cleanup_del;		}		if (((info->flags & IPSET_FLAG_MAP_SKBPRIO) |		     (info->flags & IPSET_FLAG_MAP_SKBQUEUE)) &&		     (par->hook_mask & ~(1 << NF_INET_FORWARD |					 1 << NF_INET_LOCAL_OUT |					 1 << NF_INET_POST_ROUTING))) {			pr_info_ratelimited("mapping of prio or/and queue is allowed only from OUTPUT/FORWARD/POSTROUTING chains\n");			ret = -EINVAL;			goto cleanup_del;		}		index = ip_set_nfnl_get_byindex(par->net,						info->map_set.index);		if (index == IPSET_INVALID_ID) {			pr_info_ratelimited("Cannot find map_set index %u as target\n",					    info->map_set.index);			ret = -ENOENT;			goto cleanup_del;		}	} 	if (info->add_set.dim > IPSET_DIM_MAX ||	    info->del_set.dim > IPSET_DIM_MAX ||	    info->map_set.dim > IPSET_DIM_MAX) {		pr_info_ratelimited("SET target dimension over the limit!\n");		ret = -ERANGE;		goto cleanup_mark;	} 	return 0;cleanup_mark:	if (info->map_set.index != IPSET_INVALID_ID)		ip_set_nfnl_put(par->net, info->map_set.index);cleanup_del:	if (info->del_set.index != IPSET_INVALID_ID)		ip_set_nfnl_put(par->net, info->del_set.index);

Lines 1–500 of 712.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t