xt_tcpudp.c 6.31 KB
Newer Older
1
2
3
#include <linux/types.h>
#include <linux/module.h>
#include <net/ip.h>
4
#include <linux/ipv6.h>
5
6
7
8
9
10
11
12
#include <net/ipv6.h>
#include <net/tcp.h>
#include <net/udp.h>
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter/xt_tcpudp.h>
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter_ipv6/ip6_tables.h>

13
MODULE_DESCRIPTION("x_tables match for TCP and UDP(-Lite), supports IPv4 and IPv6");
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
MODULE_LICENSE("GPL");
MODULE_ALIAS("xt_tcp");
MODULE_ALIAS("xt_udp");
MODULE_ALIAS("ipt_udp");
MODULE_ALIAS("ipt_tcp");
MODULE_ALIAS("ip6t_udp");
MODULE_ALIAS("ip6t_tcp");

#ifdef DEBUG_IP_FIREWALL_USER
#define duprintf(format, args...) printk(format , ## args)
#else
#define duprintf(format, args...)
#endif


/* Returns 1 if the port is matched by the range, 0 otherwise */
30
31
static inline bool
port_match(u_int16_t min, u_int16_t max, u_int16_t port, bool invert)
32
{
33
	return (port >= min && port <= max) ^ invert;
34
35
}

36
static bool
37
38
39
40
tcp_find_option(u_int8_t option,
		const struct sk_buff *skb,
		unsigned int protoff,
		unsigned int optlen,
41
		bool invert,
42
		bool *hotdrop)
43
44
45
46
47
48
49
50
51
52
53
54
55
56
{
	/* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */
	u_int8_t _opt[60 - sizeof(struct tcphdr)], *op;
	unsigned int i;

	duprintf("tcp_match: finding option\n");

	if (!optlen)
		return invert;

	/* If we don't have the whole header, drop packet. */
	op = skb_header_pointer(skb, protoff + sizeof(struct tcphdr),
				optlen, _opt);
	if (op == NULL) {
57
		*hotdrop = true;
58
		return false;
59
60
61
62
63
64
65
66
67
68
69
	}

	for (i = 0; i < optlen; ) {
		if (op[i] == option) return !invert;
		if (op[i] < 2) i++;
		else i += op[i+1]?:1;
	}

	return invert;
}

70
static bool
71
72
73
tcp_match(const struct sk_buff *skb,
	  const struct net_device *in,
	  const struct net_device *out,
74
	  const struct xt_match *match,
75
76
77
	  const void *matchinfo,
	  int offset,
	  unsigned int protoff,
78
	  bool *hotdrop)
79
80
81
82
83
84
85
86
87
88
89
90
91
{
	struct tcphdr _tcph, *th;
	const struct xt_tcp *tcpinfo = matchinfo;

	if (offset) {
		/* To quote Alan:

		   Don't allow a fragment of TCP 8 bytes in. Nobody normal
		   causes this. Its a cracker trying to break in by doing a
		   flag overwrite to pass the direction checks.
		*/
		if (offset == 1) {
			duprintf("Dropping evil TCP offset=1 frag.\n");
92
			*hotdrop = true;
93
94
		}
		/* Must not be a fragment. */
95
		return false;
96
97
	}

98
#define FWINVTCP(bool, invflg) ((bool) ^ !!(tcpinfo->invflags & (invflg)))
99
100
101
102
103
104

	th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph);
	if (th == NULL) {
		/* We've been asked to examine this packet, and we
		   can't.  Hence, no choice but to drop. */
		duprintf("Dropping evil TCP offset=0 tinygram.\n");
105
		*hotdrop = true;
106
		return false;
107
108
109
110
111
	}

	if (!port_match(tcpinfo->spts[0], tcpinfo->spts[1],
			ntohs(th->source),
			!!(tcpinfo->invflags & XT_TCP_INV_SRCPT)))
112
		return false;
113
114
115
	if (!port_match(tcpinfo->dpts[0], tcpinfo->dpts[1],
			ntohs(th->dest),
			!!(tcpinfo->invflags & XT_TCP_INV_DSTPT)))
116
		return false;
117
118
119
	if (!FWINVTCP((((unsigned char *)th)[13] & tcpinfo->flg_mask)
		      == tcpinfo->flg_cmp,
		      XT_TCP_INV_FLAGS))
120
		return false;
121
122
	if (tcpinfo->option) {
		if (th->doff * 4 < sizeof(_tcph)) {
123
			*hotdrop = true;
124
			return false;
125
126
127
128
129
		}
		if (!tcp_find_option(tcpinfo->option, skb, protoff,
				     th->doff*4 - sizeof(_tcph),
				     tcpinfo->invflags & XT_TCP_INV_OPTION,
				     hotdrop))
130
			return false;
131
	}
132
	return true;
133
134
135
}

/* Called when user tries to insert an entry of this type. */
136
static bool
137
138
tcp_checkentry(const char *tablename,
	       const void *info,
139
	       const struct xt_match *match,
140
141
142
143
144
	       void *matchinfo,
	       unsigned int hook_mask)
{
	const struct xt_tcp *tcpinfo = matchinfo;

145
146
	/* Must specify no unknown invflags */
	return !(tcpinfo->invflags & ~XT_TCP_INV_MASK);
147
148
}

149
static bool
150
151
152
udp_match(const struct sk_buff *skb,
	  const struct net_device *in,
	  const struct net_device *out,
153
	  const struct xt_match *match,
154
155
156
	  const void *matchinfo,
	  int offset,
	  unsigned int protoff,
157
	  bool *hotdrop)
158
159
160
161
162
163
{
	struct udphdr _udph, *uh;
	const struct xt_udp *udpinfo = matchinfo;

	/* Must not be a fragment. */
	if (offset)
164
		return false;
165
166
167
168
169
170

	uh = skb_header_pointer(skb, protoff, sizeof(_udph), &_udph);
	if (uh == NULL) {
		/* We've been asked to examine this packet, and we
		   can't.  Hence, no choice but to drop. */
		duprintf("Dropping evil UDP tinygram.\n");
171
		*hotdrop = true;
172
		return false;
173
174
175
176
177
178
179
180
181
182
183
	}

	return port_match(udpinfo->spts[0], udpinfo->spts[1],
			  ntohs(uh->source),
			  !!(udpinfo->invflags & XT_UDP_INV_SRCPT))
		&& port_match(udpinfo->dpts[0], udpinfo->dpts[1],
			      ntohs(uh->dest),
			      !!(udpinfo->invflags & XT_UDP_INV_DSTPT));
}

/* Called when user tries to insert an entry of this type. */
184
static bool
185
186
udp_checkentry(const char *tablename,
	       const void *info,
187
	       const struct xt_match *match,
188
189
190
	       void *matchinfo,
	       unsigned int hook_mask)
{
191
	const struct xt_udp *udpinfo = matchinfo;
192

193
194
	/* Must specify no unknown invflags */
	return !(udpinfo->invflags & ~XT_UDP_INV_MASK);
195
196
}

197
static struct xt_match xt_tcpudp_match[] __read_mostly = {
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
	{
		.name		= "tcp",
		.family		= AF_INET,
		.checkentry	= tcp_checkentry,
		.match		= tcp_match,
		.matchsize	= sizeof(struct xt_tcp),
		.proto		= IPPROTO_TCP,
		.me		= THIS_MODULE,
	},
	{
		.name		= "tcp",
		.family		= AF_INET6,
		.checkentry	= tcp_checkentry,
		.match		= tcp_match,
		.matchsize	= sizeof(struct xt_tcp),
		.proto		= IPPROTO_TCP,
		.me		= THIS_MODULE,
	},
	{
		.name		= "udp",
		.family		= AF_INET,
		.checkentry	= udp_checkentry,
		.match		= udp_match,
		.matchsize	= sizeof(struct xt_udp),
		.proto		= IPPROTO_UDP,
		.me		= THIS_MODULE,
	},
	{
		.name		= "udp",
		.family		= AF_INET6,
		.checkentry	= udp_checkentry,
		.match		= udp_match,
		.matchsize	= sizeof(struct xt_udp),
		.proto		= IPPROTO_UDP,
		.me		= THIS_MODULE,
	},
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
	{
		.name		= "udplite",
		.family		= AF_INET,
		.checkentry	= udp_checkentry,
		.match		= udp_match,
		.matchsize	= sizeof(struct xt_udp),
		.proto		= IPPROTO_UDPLITE,
		.me		= THIS_MODULE,
	},
	{
		.name		= "udplite",
		.family		= AF_INET6,
		.checkentry	= udp_checkentry,
		.match		= udp_match,
		.matchsize	= sizeof(struct xt_udp),
		.proto		= IPPROTO_UDPLITE,
		.me		= THIS_MODULE,
	},
252
253
};

254
static int __init xt_tcpudp_init(void)
255
{
256
257
	return xt_register_matches(xt_tcpudp_match,
				   ARRAY_SIZE(xt_tcpudp_match));
258
259
}

260
static void __exit xt_tcpudp_fini(void)
261
{
262
	xt_unregister_matches(xt_tcpudp_match, ARRAY_SIZE(xt_tcpudp_match));
263
264
}

265
266
module_init(xt_tcpudp_init);
module_exit(xt_tcpudp_fini);