xref: /openbmc/linux/net/netfilter/xt_tcpudp.c (revision 36ce9982)
1 // SPDX-License-Identifier: GPL-2.0-only
2 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
3 #include <linux/types.h>
4 #include <linux/module.h>
5 #include <net/ip.h>
6 #include <linux/ipv6.h>
7 #include <linux/icmp.h>
8 #include <net/ipv6.h>
9 #include <net/tcp.h>
10 #include <net/udp.h>
11 #include <linux/netfilter/x_tables.h>
12 #include <linux/netfilter/xt_tcpudp.h>
13 #include <linux/netfilter_ipv4/ip_tables.h>
14 #include <linux/netfilter_ipv6/ip6_tables.h>
15 
16 MODULE_DESCRIPTION("Xtables: TCP, UDP and UDP-Lite match");
17 MODULE_LICENSE("GPL");
18 MODULE_ALIAS("xt_tcp");
19 MODULE_ALIAS("xt_udp");
20 MODULE_ALIAS("ipt_udp");
21 MODULE_ALIAS("ipt_tcp");
22 MODULE_ALIAS("ip6t_udp");
23 MODULE_ALIAS("ip6t_tcp");
24 MODULE_ALIAS("ipt_icmp");
25 MODULE_ALIAS("ip6t_icmp6");
26 
27 /* Returns 1 if the port is matched by the range, 0 otherwise */
28 static inline bool
port_match(u_int16_t min,u_int16_t max,u_int16_t port,bool invert)29 port_match(u_int16_t min, u_int16_t max, u_int16_t port, bool invert)
30 {
31 	return (port >= min && port <= max) ^ invert;
32 }
33 
34 static bool
tcp_find_option(u_int8_t option,const struct sk_buff * skb,unsigned int protoff,unsigned int optlen,bool invert,bool * hotdrop)35 tcp_find_option(u_int8_t option,
36 		const struct sk_buff *skb,
37 		unsigned int protoff,
38 		unsigned int optlen,
39 		bool invert,
40 		bool *hotdrop)
41 {
42 	/* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */
43 	const u_int8_t *op;
44 	u_int8_t _opt[60 - sizeof(struct tcphdr)];
45 	unsigned int i;
46 
47 	pr_debug("finding option\n");
48 
49 	if (!optlen)
50 		return invert;
51 
52 	/* If we don't have the whole header, drop packet. */
53 	op = skb_header_pointer(skb, protoff + sizeof(struct tcphdr),
54 				optlen, _opt);
55 	if (op == NULL) {
56 		*hotdrop = true;
57 		return false;
58 	}
59 
60 	for (i = 0; i < optlen; ) {
61 		if (op[i] == option) return !invert;
62 		if (op[i] < 2) i++;
63 		else i += op[i+1]?:1;
64 	}
65 
66 	return invert;
67 }
68 
tcp_mt(const struct sk_buff * skb,struct xt_action_param * par)69 static bool tcp_mt(const struct sk_buff *skb, struct xt_action_param *par)
70 {
71 	const struct tcphdr *th;
72 	struct tcphdr _tcph;
73 	const struct xt_tcp *tcpinfo = par->matchinfo;
74 
75 	if (par->fragoff != 0) {
76 		/* To quote Alan:
77 
78 		   Don't allow a fragment of TCP 8 bytes in. Nobody normal
79 		   causes this. Its a cracker trying to break in by doing a
80 		   flag overwrite to pass the direction checks.
81 		*/
82 		if (par->fragoff == 1) {
83 			pr_debug("Dropping evil TCP offset=1 frag.\n");
84 			par->hotdrop = true;
85 		}
86 		/* Must not be a fragment. */
87 		return false;
88 	}
89 
90 	th = skb_header_pointer(skb, par->thoff, sizeof(_tcph), &_tcph);
91 	if (th == NULL) {
92 		/* We've been asked to examine this packet, and we
93 		   can't.  Hence, no choice but to drop. */
94 		pr_debug("Dropping evil TCP offset=0 tinygram.\n");
95 		par->hotdrop = true;
96 		return false;
97 	}
98 
99 	if (!port_match(tcpinfo->spts[0], tcpinfo->spts[1],
100 			ntohs(th->source),
101 			!!(tcpinfo->invflags & XT_TCP_INV_SRCPT)))
102 		return false;
103 	if (!port_match(tcpinfo->dpts[0], tcpinfo->dpts[1],
104 			ntohs(th->dest),
105 			!!(tcpinfo->invflags & XT_TCP_INV_DSTPT)))
106 		return false;
107 	if (!NF_INVF(tcpinfo, XT_TCP_INV_FLAGS,
108 		     (((unsigned char *)th)[13] & tcpinfo->flg_mask) == tcpinfo->flg_cmp))
109 		return false;
110 	if (tcpinfo->option) {
111 		if (th->doff * 4 < sizeof(_tcph)) {
112 			par->hotdrop = true;
113 			return false;
114 		}
115 		if (!tcp_find_option(tcpinfo->option, skb, par->thoff,
116 				     th->doff*4 - sizeof(_tcph),
117 				     tcpinfo->invflags & XT_TCP_INV_OPTION,
118 				     &par->hotdrop))
119 			return false;
120 	}
121 	return true;
122 }
123 
tcp_mt_check(const struct xt_mtchk_param * par)124 static int tcp_mt_check(const struct xt_mtchk_param *par)
125 {
126 	const struct xt_tcp *tcpinfo = par->matchinfo;
127 
128 	/* Must specify no unknown invflags */
129 	return (tcpinfo->invflags & ~XT_TCP_INV_MASK) ? -EINVAL : 0;
130 }
131 
udp_mt(const struct sk_buff * skb,struct xt_action_param * par)132 static bool udp_mt(const struct sk_buff *skb, struct xt_action_param *par)
133 {
134 	const struct udphdr *uh;
135 	struct udphdr _udph;
136 	const struct xt_udp *udpinfo = par->matchinfo;
137 
138 	/* Must not be a fragment. */
139 	if (par->fragoff != 0)
140 		return false;
141 
142 	uh = skb_header_pointer(skb, par->thoff, sizeof(_udph), &_udph);
143 	if (uh == NULL) {
144 		/* We've been asked to examine this packet, and we
145 		   can't.  Hence, no choice but to drop. */
146 		pr_debug("Dropping evil UDP tinygram.\n");
147 		par->hotdrop = true;
148 		return false;
149 	}
150 
151 	return port_match(udpinfo->spts[0], udpinfo->spts[1],
152 			  ntohs(uh->source),
153 			  !!(udpinfo->invflags & XT_UDP_INV_SRCPT))
154 		&& port_match(udpinfo->dpts[0], udpinfo->dpts[1],
155 			      ntohs(uh->dest),
156 			      !!(udpinfo->invflags & XT_UDP_INV_DSTPT));
157 }
158 
udp_mt_check(const struct xt_mtchk_param * par)159 static int udp_mt_check(const struct xt_mtchk_param *par)
160 {
161 	const struct xt_udp *udpinfo = par->matchinfo;
162 
163 	/* Must specify no unknown invflags */
164 	return (udpinfo->invflags & ~XT_UDP_INV_MASK) ? -EINVAL : 0;
165 }
166 
167 /* Returns 1 if the type and code is matched by the range, 0 otherwise */
type_code_in_range(u8 test_type,u8 min_code,u8 max_code,u8 type,u8 code)168 static bool type_code_in_range(u8 test_type, u8 min_code, u8 max_code,
169 			       u8 type, u8 code)
170 {
171 	return type == test_type && code >= min_code && code <= max_code;
172 }
173 
icmp_type_code_match(u8 test_type,u8 min_code,u8 max_code,u8 type,u8 code,bool invert)174 static bool icmp_type_code_match(u8 test_type, u8 min_code, u8 max_code,
175 				 u8 type, u8 code, bool invert)
176 {
177 	return (test_type == 0xFF ||
178 		type_code_in_range(test_type, min_code, max_code, type, code))
179 		^ invert;
180 }
181 
icmp6_type_code_match(u8 test_type,u8 min_code,u8 max_code,u8 type,u8 code,bool invert)182 static bool icmp6_type_code_match(u8 test_type, u8 min_code, u8 max_code,
183 				  u8 type, u8 code, bool invert)
184 {
185 	return type_code_in_range(test_type, min_code, max_code, type, code) ^ invert;
186 }
187 
188 static bool
icmp_match(const struct sk_buff * skb,struct xt_action_param * par)189 icmp_match(const struct sk_buff *skb, struct xt_action_param *par)
190 {
191 	const struct icmphdr *ic;
192 	struct icmphdr _icmph;
193 	const struct ipt_icmp *icmpinfo = par->matchinfo;
194 
195 	/* Must not be a fragment. */
196 	if (par->fragoff != 0)
197 		return false;
198 
199 	ic = skb_header_pointer(skb, par->thoff, sizeof(_icmph), &_icmph);
200 	if (!ic) {
201 		/* We've been asked to examine this packet, and we
202 		 * can't.  Hence, no choice but to drop.
203 		 */
204 		par->hotdrop = true;
205 		return false;
206 	}
207 
208 	return icmp_type_code_match(icmpinfo->type,
209 				    icmpinfo->code[0],
210 				    icmpinfo->code[1],
211 				    ic->type, ic->code,
212 				    !!(icmpinfo->invflags & IPT_ICMP_INV));
213 }
214 
215 static bool
icmp6_match(const struct sk_buff * skb,struct xt_action_param * par)216 icmp6_match(const struct sk_buff *skb, struct xt_action_param *par)
217 {
218 	const struct icmp6hdr *ic;
219 	struct icmp6hdr _icmph;
220 	const struct ip6t_icmp *icmpinfo = par->matchinfo;
221 
222 	/* Must not be a fragment. */
223 	if (par->fragoff != 0)
224 		return false;
225 
226 	ic = skb_header_pointer(skb, par->thoff, sizeof(_icmph), &_icmph);
227 	if (!ic) {
228 		/* We've been asked to examine this packet, and we
229 		 * can't.  Hence, no choice but to drop.
230 		 */
231 		par->hotdrop = true;
232 		return false;
233 	}
234 
235 	return icmp6_type_code_match(icmpinfo->type,
236 				     icmpinfo->code[0],
237 				     icmpinfo->code[1],
238 				     ic->icmp6_type, ic->icmp6_code,
239 				     !!(icmpinfo->invflags & IP6T_ICMP_INV));
240 }
241 
icmp_checkentry(const struct xt_mtchk_param * par)242 static int icmp_checkentry(const struct xt_mtchk_param *par)
243 {
244 	const struct ipt_icmp *icmpinfo = par->matchinfo;
245 
246 	return (icmpinfo->invflags & ~IPT_ICMP_INV) ? -EINVAL : 0;
247 }
248 
icmp6_checkentry(const struct xt_mtchk_param * par)249 static int icmp6_checkentry(const struct xt_mtchk_param *par)
250 {
251 	const struct ip6t_icmp *icmpinfo = par->matchinfo;
252 
253 	return (icmpinfo->invflags & ~IP6T_ICMP_INV) ? -EINVAL : 0;
254 }
255 
256 static struct xt_match tcpudp_mt_reg[] __read_mostly = {
257 	{
258 		.name		= "tcp",
259 		.family		= NFPROTO_IPV4,
260 		.checkentry	= tcp_mt_check,
261 		.match		= tcp_mt,
262 		.matchsize	= sizeof(struct xt_tcp),
263 		.proto		= IPPROTO_TCP,
264 		.me		= THIS_MODULE,
265 	},
266 	{
267 		.name		= "tcp",
268 		.family		= NFPROTO_IPV6,
269 		.checkentry	= tcp_mt_check,
270 		.match		= tcp_mt,
271 		.matchsize	= sizeof(struct xt_tcp),
272 		.proto		= IPPROTO_TCP,
273 		.me		= THIS_MODULE,
274 	},
275 	{
276 		.name		= "udp",
277 		.family		= NFPROTO_IPV4,
278 		.checkentry	= udp_mt_check,
279 		.match		= udp_mt,
280 		.matchsize	= sizeof(struct xt_udp),
281 		.proto		= IPPROTO_UDP,
282 		.me		= THIS_MODULE,
283 	},
284 	{
285 		.name		= "udp",
286 		.family		= NFPROTO_IPV6,
287 		.checkentry	= udp_mt_check,
288 		.match		= udp_mt,
289 		.matchsize	= sizeof(struct xt_udp),
290 		.proto		= IPPROTO_UDP,
291 		.me		= THIS_MODULE,
292 	},
293 	{
294 		.name		= "udplite",
295 		.family		= NFPROTO_IPV4,
296 		.checkentry	= udp_mt_check,
297 		.match		= udp_mt,
298 		.matchsize	= sizeof(struct xt_udp),
299 		.proto		= IPPROTO_UDPLITE,
300 		.me		= THIS_MODULE,
301 	},
302 	{
303 		.name		= "udplite",
304 		.family		= NFPROTO_IPV6,
305 		.checkentry	= udp_mt_check,
306 		.match		= udp_mt,
307 		.matchsize	= sizeof(struct xt_udp),
308 		.proto		= IPPROTO_UDPLITE,
309 		.me		= THIS_MODULE,
310 	},
311 	{
312 		.name       = "icmp",
313 		.match      = icmp_match,
314 		.matchsize  = sizeof(struct ipt_icmp),
315 		.checkentry = icmp_checkentry,
316 		.proto      = IPPROTO_ICMP,
317 		.family     = NFPROTO_IPV4,
318 		.me         = THIS_MODULE,
319 	},
320 	{
321 		.name       = "icmp6",
322 		.match      = icmp6_match,
323 		.matchsize  = sizeof(struct ip6t_icmp),
324 		.checkentry = icmp6_checkentry,
325 		.proto      = IPPROTO_ICMPV6,
326 		.family     = NFPROTO_IPV6,
327 		.me	    = THIS_MODULE,
328 	},
329 };
330 
tcpudp_mt_init(void)331 static int __init tcpudp_mt_init(void)
332 {
333 	return xt_register_matches(tcpudp_mt_reg, ARRAY_SIZE(tcpudp_mt_reg));
334 }
335 
tcpudp_mt_exit(void)336 static void __exit tcpudp_mt_exit(void)
337 {
338 	xt_unregister_matches(tcpudp_mt_reg, ARRAY_SIZE(tcpudp_mt_reg));
339 }
340 
341 module_init(tcpudp_mt_init);
342 module_exit(tcpudp_mt_exit);
343