xt_iprange.c 5.17 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
/*
2
 *	xt_iprange - Netfilter module to match IP address ranges
Linus Torvalds's avatar
Linus Torvalds committed
3
 *
4
 *	(C) 2003 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
5
 *	(C) CC Computer Consultants GmbH, 2008
Linus Torvalds's avatar
Linus Torvalds committed
6
 *
7
8
9
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License version 2 as
 *	published by the Free Software Foundation.
Linus Torvalds's avatar
Linus Torvalds committed
10
11
12
13
 */
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
14
#include <linux/ipv6.h>
15
#include <linux/netfilter/x_tables.h>
16
#include <linux/netfilter/xt_iprange.h>
Linus Torvalds's avatar
Linus Torvalds committed
17
18
#include <linux/netfilter_ipv4/ipt_iprange.h>

19
static bool
20
21
22
23
iprange_mt_v0(const struct sk_buff *skb, const struct net_device *in,
              const struct net_device *out, const struct xt_match *match,
              const void *matchinfo, int offset, unsigned int protoff,
              bool *hotdrop)
Linus Torvalds's avatar
Linus Torvalds committed
24
25
{
	const struct ipt_iprange_info *info = matchinfo;
26
	const struct iphdr *iph = ip_hdr(skb);
Linus Torvalds's avatar
Linus Torvalds committed
27
28

	if (info->flags & IPRANGE_SRC) {
29
30
		if ((ntohl(iph->saddr) < ntohl(info->src.min_ip)
			  || ntohl(iph->saddr) > ntohl(info->src.max_ip))
Linus Torvalds's avatar
Linus Torvalds committed
31
			 ^ !!(info->flags & IPRANGE_SRC_INV)) {
32
33
34
35
36
37
			pr_debug("src IP %u.%u.%u.%u NOT in range %s"
				 "%u.%u.%u.%u-%u.%u.%u.%u\n",
				 NIPQUAD(iph->saddr),
				 info->flags & IPRANGE_SRC_INV ? "(INV) " : "",
				 NIPQUAD(info->src.min_ip),
				 NIPQUAD(info->src.max_ip));
38
			return false;
Linus Torvalds's avatar
Linus Torvalds committed
39
40
41
		}
	}
	if (info->flags & IPRANGE_DST) {
42
43
		if ((ntohl(iph->daddr) < ntohl(info->dst.min_ip)
			  || ntohl(iph->daddr) > ntohl(info->dst.max_ip))
Linus Torvalds's avatar
Linus Torvalds committed
44
			 ^ !!(info->flags & IPRANGE_DST_INV)) {
45
46
47
48
49
50
			pr_debug("dst IP %u.%u.%u.%u NOT in range %s"
				 "%u.%u.%u.%u-%u.%u.%u.%u\n",
				 NIPQUAD(iph->daddr),
				 info->flags & IPRANGE_DST_INV ? "(INV) " : "",
				 NIPQUAD(info->dst.min_ip),
				 NIPQUAD(info->dst.max_ip));
51
			return false;
Linus Torvalds's avatar
Linus Torvalds committed
52
53
		}
	}
54
	return true;
Linus Torvalds's avatar
Linus Torvalds committed
55
56
}

57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
static bool
iprange_mt4(const struct sk_buff *skb, const struct net_device *in,
            const struct net_device *out, const struct xt_match *match,
            const void *matchinfo, int offset, unsigned int protoff,
            bool *hotdrop)
{
	const struct xt_iprange_mtinfo *info = matchinfo;
	const struct iphdr *iph = ip_hdr(skb);
	bool m;

	if (info->flags & IPRANGE_SRC) {
		m  = ntohl(iph->saddr) < ntohl(info->src_min.ip);
		m |= ntohl(iph->saddr) > ntohl(info->src_max.ip);
		m ^= info->flags & IPRANGE_SRC_INV;
		if (m) {
			pr_debug("src IP " NIPQUAD_FMT " NOT in range %s"
			         NIPQUAD_FMT "-" NIPQUAD_FMT "\n",
			         NIPQUAD(iph->saddr),
			         (info->flags & IPRANGE_SRC_INV) ? "(INV) " : "",
			         NIPQUAD(info->src_max.ip),
			         NIPQUAD(info->src_max.ip));
			return false;
		}
	}
	if (info->flags & IPRANGE_DST) {
		m  = ntohl(iph->daddr) < ntohl(info->dst_min.ip);
		m |= ntohl(iph->daddr) > ntohl(info->dst_max.ip);
		m ^= info->flags & IPRANGE_DST_INV;
		if (m) {
			pr_debug("dst IP " NIPQUAD_FMT " NOT in range %s"
			         NIPQUAD_FMT "-" NIPQUAD_FMT "\n",
			         NIPQUAD(iph->daddr),
			         (info->flags & IPRANGE_DST_INV) ? "(INV) " : "",
			         NIPQUAD(info->dst_min.ip),
			         NIPQUAD(info->dst_max.ip));
			return false;
		}
	}
	return true;
}

static inline int
iprange_ipv6_sub(const struct in6_addr *a, const struct in6_addr *b)
{
	unsigned int i;
	int r;

	for (i = 0; i < 4; ++i) {
105
		r = ntohl(a->s6_addr32[i]) - ntohl(b->s6_addr32[i]);
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
		if (r != 0)
			return r;
	}

	return 0;
}

static bool
iprange_mt6(const struct sk_buff *skb, const struct net_device *in,
            const struct net_device *out, const struct xt_match *match,
            const void *matchinfo, int offset, unsigned int protoff,
            bool *hotdrop)
{
	const struct xt_iprange_mtinfo *info = matchinfo;
	const struct ipv6hdr *iph = ipv6_hdr(skb);
	bool m;

	if (info->flags & IPRANGE_SRC) {
		m  = iprange_ipv6_sub(&iph->saddr, &info->src_min.in6) < 0;
		m |= iprange_ipv6_sub(&iph->saddr, &info->src_max.in6) > 0;
		m ^= info->flags & IPRANGE_SRC_INV;
		if (m)
			return false;
	}
	if (info->flags & IPRANGE_DST) {
		m  = iprange_ipv6_sub(&iph->daddr, &info->dst_min.in6) < 0;
		m |= iprange_ipv6_sub(&iph->daddr, &info->dst_max.in6) > 0;
		m ^= info->flags & IPRANGE_DST_INV;
		if (m)
			return false;
	}
	return true;
}

static struct xt_match iprange_mt_reg[] __read_mostly = {
	{
		.name      = "iprange",
		.revision  = 0,
		.family    = AF_INET,
		.match     = iprange_mt_v0,
		.matchsize = sizeof(struct ipt_iprange_info),
		.me        = THIS_MODULE,
	},
	{
		.name      = "iprange",
		.revision  = 1,
152
		.family    = AF_INET,
153
154
155
156
157
158
159
160
161
162
163
164
		.match     = iprange_mt4,
		.matchsize = sizeof(struct xt_iprange_mtinfo),
		.me        = THIS_MODULE,
	},
	{
		.name      = "iprange",
		.revision  = 1,
		.family    = AF_INET6,
		.match     = iprange_mt6,
		.matchsize = sizeof(struct xt_iprange_mtinfo),
		.me        = THIS_MODULE,
	},
Linus Torvalds's avatar
Linus Torvalds committed
165
166
};

167
static int __init iprange_mt_init(void)
Linus Torvalds's avatar
Linus Torvalds committed
168
{
169
	return xt_register_matches(iprange_mt_reg, ARRAY_SIZE(iprange_mt_reg));
Linus Torvalds's avatar
Linus Torvalds committed
170
171
}

172
static void __exit iprange_mt_exit(void)
Linus Torvalds's avatar
Linus Torvalds committed
173
{
174
	xt_unregister_matches(iprange_mt_reg, ARRAY_SIZE(iprange_mt_reg));
Linus Torvalds's avatar
Linus Torvalds committed
175
176
}

177
178
module_init(iprange_mt_init);
module_exit(iprange_mt_exit);
179
MODULE_LICENSE("GPL");
180
MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>, Jan Engelhardt <jengelh@computergmbh.de>");
181
MODULE_DESCRIPTION("Xtables: arbitrary IPv4 range matching");