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 if (!optlen)
48 return invert;
49
50 /* If we don't have the whole header, drop packet. */
51 op = skb_header_pointer(skb, protoff + sizeof(struct tcphdr),
52 optlen, _opt);
53 if (op == NULL) {
54 *hotdrop = true;
55 return false;
56 }
57
58 for (i = 0; i < optlen; ) {
59 if (op[i] == option) return !invert;
60 if (op[i] < 2 || i == optlen - 1)
61 i++;
62 else
63 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 par->hotdrop = true;
84 /* Must not be a fragment. */
85 return false;
86 }
87
88 th = skb_header_pointer(skb, par->thoff, sizeof(_tcph), &_tcph);
89 if (th == NULL) {
90 /* We've been asked to examine this packet, and we
91 can't. Hence, no choice but to drop. */
92 par->hotdrop = true;
93 return false;
94 }
95
96 if (!port_match(tcpinfo->spts[0], tcpinfo->spts[1],
97 ntohs(th->source),
98 !!(tcpinfo->invflags & XT_TCP_INV_SRCPT)))
99 return false;
100 if (!port_match(tcpinfo->dpts[0], tcpinfo->dpts[1],
101 ntohs(th->dest),
102 !!(tcpinfo->invflags & XT_TCP_INV_DSTPT)))
103 return false;
104 if (!NF_INVF(tcpinfo, XT_TCP_INV_FLAGS,
105 (((unsigned char *)th)[13] & tcpinfo->flg_mask) == tcpinfo->flg_cmp))
106 return false;
107 if (tcpinfo->option) {
108 if (th->doff * 4 < sizeof(_tcph)) {
109 par->hotdrop = true;
110 return false;
111 }
112 if (!tcp_find_option(tcpinfo->option, skb, par->thoff,
113 th->doff*4 - sizeof(_tcph),
114 tcpinfo->invflags & XT_TCP_INV_OPTION,
115 &par->hotdrop))
116 return false;
117 }
118 return true;
119 }
120
tcp_mt_check(const struct xt_mtchk_param * par)121 static int tcp_mt_check(const struct xt_mtchk_param *par)
122 {
123 const struct xt_tcp *tcpinfo = par->matchinfo;
124
125 /* Must specify no unknown invflags */
126 return (tcpinfo->invflags & ~XT_TCP_INV_MASK) ? -EINVAL : 0;
127 }
128
udp_mt(const struct sk_buff * skb,struct xt_action_param * par)129 static bool udp_mt(const struct sk_buff *skb, struct xt_action_param *par)
130 {
131 const struct udphdr *uh;
132 struct udphdr _udph;
133 const struct xt_udp *udpinfo = par->matchinfo;
134
135 /* Must not be a fragment. */
136 if (par->fragoff != 0)
137 return false;
138
139 uh = skb_header_pointer(skb, par->thoff, sizeof(_udph), &_udph);
140 if (uh == NULL) {
141 /* We've been asked to examine this packet, and we
142 can't. Hence, no choice but to drop. */
143 par->hotdrop = true;
144 return false;
145 }
146
147 return port_match(udpinfo->spts[0], udpinfo->spts[1],
148 ntohs(uh->source),
149 !!(udpinfo->invflags & XT_UDP_INV_SRCPT))
150 && port_match(udpinfo->dpts[0], udpinfo->dpts[1],
151 ntohs(uh->dest),
152 !!(udpinfo->invflags & XT_UDP_INV_DSTPT));
153 }
154
udp_mt_check(const struct xt_mtchk_param * par)155 static int udp_mt_check(const struct xt_mtchk_param *par)
156 {
157 const struct xt_udp *udpinfo = par->matchinfo;
158
159 /* Must specify no unknown invflags */
160 return (udpinfo->invflags & ~XT_UDP_INV_MASK) ? -EINVAL : 0;
161 }
162
163 /* 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)164 static bool type_code_in_range(u8 test_type, u8 min_code, u8 max_code,
165 u8 type, u8 code)
166 {
167 return type == test_type && code >= min_code && code <= max_code;
168 }
169
icmp_type_code_match(u8 test_type,u8 min_code,u8 max_code,u8 type,u8 code,bool invert)170 static bool icmp_type_code_match(u8 test_type, u8 min_code, u8 max_code,
171 u8 type, u8 code, bool invert)
172 {
173 return (test_type == 0xFF ||
174 type_code_in_range(test_type, min_code, max_code, type, code))
175 ^ invert;
176 }
177
icmp6_type_code_match(u8 test_type,u8 min_code,u8 max_code,u8 type,u8 code,bool invert)178 static bool icmp6_type_code_match(u8 test_type, u8 min_code, u8 max_code,
179 u8 type, u8 code, bool invert)
180 {
181 return type_code_in_range(test_type, min_code, max_code, type, code) ^ invert;
182 }
183
184 static bool
icmp_match(const struct sk_buff * skb,struct xt_action_param * par)185 icmp_match(const struct sk_buff *skb, struct xt_action_param *par)
186 {
187 const struct icmphdr *ic;
188 struct icmphdr _icmph;
189 const struct ipt_icmp *icmpinfo = par->matchinfo;
190
191 /* Must not be a fragment. */
192 if (par->fragoff != 0)
193 return false;
194
195 ic = skb_header_pointer(skb, par->thoff, sizeof(_icmph), &_icmph);
196 if (!ic) {
197 /* We've been asked to examine this packet, and we
198 * can't. Hence, no choice but to drop.
199 */
200 par->hotdrop = true;
201 return false;
202 }
203
204 return icmp_type_code_match(icmpinfo->type,
205 icmpinfo->code[0],
206 icmpinfo->code[1],
207 ic->type, ic->code,
208 !!(icmpinfo->invflags & IPT_ICMP_INV));
209 }
210
211 static bool
icmp6_match(const struct sk_buff * skb,struct xt_action_param * par)212 icmp6_match(const struct sk_buff *skb, struct xt_action_param *par)
213 {
214 const struct icmp6hdr *ic;
215 struct icmp6hdr _icmph;
216 const struct ip6t_icmp *icmpinfo = par->matchinfo;
217
218 /* Must not be a fragment. */
219 if (par->fragoff != 0)
220 return false;
221
222 ic = skb_header_pointer(skb, par->thoff, sizeof(_icmph), &_icmph);
223 if (!ic) {
224 /* We've been asked to examine this packet, and we
225 * can't. Hence, no choice but to drop.
226 */
227 par->hotdrop = true;
228 return false;
229 }
230
231 return icmp6_type_code_match(icmpinfo->type,
232 icmpinfo->code[0],
233 icmpinfo->code[1],
234 ic->icmp6_type, ic->icmp6_code,
235 !!(icmpinfo->invflags & IP6T_ICMP_INV));
236 }
237
icmp_checkentry(const struct xt_mtchk_param * par)238 static int icmp_checkentry(const struct xt_mtchk_param *par)
239 {
240 const struct ipt_icmp *icmpinfo = par->matchinfo;
241
242 return (icmpinfo->invflags & ~IPT_ICMP_INV) ? -EINVAL : 0;
243 }
244
icmp6_checkentry(const struct xt_mtchk_param * par)245 static int icmp6_checkentry(const struct xt_mtchk_param *par)
246 {
247 const struct ip6t_icmp *icmpinfo = par->matchinfo;
248
249 return (icmpinfo->invflags & ~IP6T_ICMP_INV) ? -EINVAL : 0;
250 }
251
252 static struct xt_match tcpudp_mt_reg[] __read_mostly = {
253 {
254 .name = "tcp",
255 .family = NFPROTO_IPV4,
256 .checkentry = tcp_mt_check,
257 .match = tcp_mt,
258 .matchsize = sizeof(struct xt_tcp),
259 .proto = IPPROTO_TCP,
260 .me = THIS_MODULE,
261 },
262 {
263 .name = "tcp",
264 .family = NFPROTO_IPV6,
265 .checkentry = tcp_mt_check,
266 .match = tcp_mt,
267 .matchsize = sizeof(struct xt_tcp),
268 .proto = IPPROTO_TCP,
269 .me = THIS_MODULE,
270 },
271 {
272 .name = "udp",
273 .family = NFPROTO_IPV4,
274 .checkentry = udp_mt_check,
275 .match = udp_mt,
276 .matchsize = sizeof(struct xt_udp),
277 .proto = IPPROTO_UDP,
278 .me = THIS_MODULE,
279 },
280 {
281 .name = "udp",
282 .family = NFPROTO_IPV6,
283 .checkentry = udp_mt_check,
284 .match = udp_mt,
285 .matchsize = sizeof(struct xt_udp),
286 .proto = IPPROTO_UDP,
287 .me = THIS_MODULE,
288 },
289 {
290 .name = "udplite",
291 .family = NFPROTO_IPV4,
292 .checkentry = udp_mt_check,
293 .match = udp_mt,
294 .matchsize = sizeof(struct xt_udp),
295 .proto = IPPROTO_UDPLITE,
296 .me = THIS_MODULE,
297 },
298 {
299 .name = "udplite",
300 .family = NFPROTO_IPV6,
301 .checkentry = udp_mt_check,
302 .match = udp_mt,
303 .matchsize = sizeof(struct xt_udp),
304 .proto = IPPROTO_UDPLITE,
305 .me = THIS_MODULE,
306 },
307 {
308 .name = "icmp",
309 .match = icmp_match,
310 .matchsize = sizeof(struct ipt_icmp),
311 .checkentry = icmp_checkentry,
312 .proto = IPPROTO_ICMP,
313 .family = NFPROTO_IPV4,
314 .me = THIS_MODULE,
315 },
316 {
317 .name = "icmp6",
318 .match = icmp6_match,
319 .matchsize = sizeof(struct ip6t_icmp),
320 .checkentry = icmp6_checkentry,
321 .proto = IPPROTO_ICMPV6,
322 .family = NFPROTO_IPV6,
323 .me = THIS_MODULE,
324 },
325 };
326
tcpudp_mt_init(void)327 static int __init tcpudp_mt_init(void)
328 {
329 return xt_register_matches(tcpudp_mt_reg, ARRAY_SIZE(tcpudp_mt_reg));
330 }
331
tcpudp_mt_exit(void)332 static void __exit tcpudp_mt_exit(void)
333 {
334 xt_unregister_matches(tcpudp_mt_reg, ARRAY_SIZE(tcpudp_mt_reg));
335 }
336
337 module_init(tcpudp_mt_init);
338 module_exit(tcpudp_mt_exit);
339