Blame view
net/netfilter/xt_tcpudp.c
5.65 KB
09c434b8a treewide: Add SPD... |
1 |
// SPDX-License-Identifier: GPL-2.0-only |
be91fd5e3 netfilter: xtable... |
2 |
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
2e4e6a17a [NETFILTER] x_tab... |
3 4 5 |
#include <linux/types.h> #include <linux/module.h> #include <net/ip.h> |
37d8dc82e [NETFILTER] x-tab... |
6 |
#include <linux/ipv6.h> |
2e4e6a17a [NETFILTER] x_tab... |
7 8 9 10 11 12 13 |
#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> |
2ae15b64e [NETFILTER]: Upda... |
14 |
MODULE_DESCRIPTION("Xtables: TCP, UDP and UDP-Lite match"); |
2e4e6a17a [NETFILTER] x_tab... |
15 16 17 18 19 20 21 |
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"); |
2e4e6a17a [NETFILTER] x_tab... |
22 |
/* Returns 1 if the port is matched by the range, 0 otherwise */ |
1d93a9cba [NETFILTER]: x_ta... |
23 24 |
static inline bool port_match(u_int16_t min, u_int16_t max, u_int16_t port, bool invert) |
2e4e6a17a [NETFILTER] x_tab... |
25 |
{ |
1d93a9cba [NETFILTER]: x_ta... |
26 |
return (port >= min && port <= max) ^ invert; |
2e4e6a17a [NETFILTER] x_tab... |
27 |
} |
1d93a9cba [NETFILTER]: x_ta... |
28 |
static bool |
2e4e6a17a [NETFILTER] x_tab... |
29 30 31 32 |
tcp_find_option(u_int8_t option, const struct sk_buff *skb, unsigned int protoff, unsigned int optlen, |
1d93a9cba [NETFILTER]: x_ta... |
33 |
bool invert, |
cff533ac1 [NETFILTER]: x_ta... |
34 |
bool *hotdrop) |
2e4e6a17a [NETFILTER] x_tab... |
35 36 |
{ /* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */ |
3cf93c96a [NETFILTER]: anno... |
37 38 |
const u_int8_t *op; u_int8_t _opt[60 - sizeof(struct tcphdr)]; |
2e4e6a17a [NETFILTER] x_tab... |
39 |
unsigned int i; |
be91fd5e3 netfilter: xtable... |
40 41 |
pr_debug("finding option "); |
2e4e6a17a [NETFILTER] x_tab... |
42 43 44 45 46 47 48 49 |
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) { |
cff533ac1 [NETFILTER]: x_ta... |
50 |
*hotdrop = true; |
1d93a9cba [NETFILTER]: x_ta... |
51 |
return false; |
2e4e6a17a [NETFILTER] x_tab... |
52 53 54 55 56 57 58 59 60 61 |
} for (i = 0; i < optlen; ) { if (op[i] == option) return !invert; if (op[i] < 2) i++; else i += op[i+1]?:1; } return invert; } |
62fc80510 netfilter: xtable... |
62 |
static bool tcp_mt(const struct sk_buff *skb, struct xt_action_param *par) |
2e4e6a17a [NETFILTER] x_tab... |
63 |
{ |
3cf93c96a [NETFILTER]: anno... |
64 65 |
const struct tcphdr *th; struct tcphdr _tcph; |
f7108a20d netfilter: xtable... |
66 |
const struct xt_tcp *tcpinfo = par->matchinfo; |
2e4e6a17a [NETFILTER] x_tab... |
67 |
|
f7108a20d netfilter: xtable... |
68 |
if (par->fragoff != 0) { |
2e4e6a17a [NETFILTER] x_tab... |
69 70 71 72 73 74 |
/* 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. */ |
f7108a20d netfilter: xtable... |
75 |
if (par->fragoff == 1) { |
be91fd5e3 netfilter: xtable... |
76 77 |
pr_debug("Dropping evil TCP offset=1 frag. "); |
b4ba26119 netfilter: xtable... |
78 |
par->hotdrop = true; |
2e4e6a17a [NETFILTER] x_tab... |
79 80 |
} /* Must not be a fragment. */ |
1d93a9cba [NETFILTER]: x_ta... |
81 |
return false; |
2e4e6a17a [NETFILTER] x_tab... |
82 |
} |
f7108a20d netfilter: xtable... |
83 |
th = skb_header_pointer(skb, par->thoff, sizeof(_tcph), &_tcph); |
2e4e6a17a [NETFILTER] x_tab... |
84 85 86 |
if (th == NULL) { /* We've been asked to examine this packet, and we can't. Hence, no choice but to drop. */ |
be91fd5e3 netfilter: xtable... |
87 88 |
pr_debug("Dropping evil TCP offset=0 tinygram. "); |
b4ba26119 netfilter: xtable... |
89 |
par->hotdrop = true; |
1d93a9cba [NETFILTER]: x_ta... |
90 |
return false; |
2e4e6a17a [NETFILTER] x_tab... |
91 92 93 94 95 |
} if (!port_match(tcpinfo->spts[0], tcpinfo->spts[1], ntohs(th->source), !!(tcpinfo->invflags & XT_TCP_INV_SRCPT))) |
1d93a9cba [NETFILTER]: x_ta... |
96 |
return false; |
2e4e6a17a [NETFILTER] x_tab... |
97 98 99 |
if (!port_match(tcpinfo->dpts[0], tcpinfo->dpts[1], ntohs(th->dest), !!(tcpinfo->invflags & XT_TCP_INV_DSTPT))) |
1d93a9cba [NETFILTER]: x_ta... |
100 |
return false; |
c37a2dfa6 netfilter: Conver... |
101 102 |
if (!NF_INVF(tcpinfo, XT_TCP_INV_FLAGS, (((unsigned char *)th)[13] & tcpinfo->flg_mask) == tcpinfo->flg_cmp)) |
1d93a9cba [NETFILTER]: x_ta... |
103 |
return false; |
2e4e6a17a [NETFILTER] x_tab... |
104 105 |
if (tcpinfo->option) { if (th->doff * 4 < sizeof(_tcph)) { |
b4ba26119 netfilter: xtable... |
106 |
par->hotdrop = true; |
1d93a9cba [NETFILTER]: x_ta... |
107 |
return false; |
2e4e6a17a [NETFILTER] x_tab... |
108 |
} |
f7108a20d netfilter: xtable... |
109 |
if (!tcp_find_option(tcpinfo->option, skb, par->thoff, |
2e4e6a17a [NETFILTER] x_tab... |
110 111 |
th->doff*4 - sizeof(_tcph), tcpinfo->invflags & XT_TCP_INV_OPTION, |
b4ba26119 netfilter: xtable... |
112 |
&par->hotdrop)) |
1d93a9cba [NETFILTER]: x_ta... |
113 |
return false; |
2e4e6a17a [NETFILTER] x_tab... |
114 |
} |
1d93a9cba [NETFILTER]: x_ta... |
115 |
return true; |
2e4e6a17a [NETFILTER] x_tab... |
116 |
} |
b0f38452f netfilter: xtable... |
117 |
static int tcp_mt_check(const struct xt_mtchk_param *par) |
2e4e6a17a [NETFILTER] x_tab... |
118 |
{ |
9b4fce7a3 netfilter: xtable... |
119 |
const struct xt_tcp *tcpinfo = par->matchinfo; |
2e4e6a17a [NETFILTER] x_tab... |
120 |
|
5d04bff09 [NETFILTER]: Conv... |
121 |
/* Must specify no unknown invflags */ |
bd414ee60 netfilter: xtable... |
122 |
return (tcpinfo->invflags & ~XT_TCP_INV_MASK) ? -EINVAL : 0; |
2e4e6a17a [NETFILTER] x_tab... |
123 |
} |
62fc80510 netfilter: xtable... |
124 |
static bool udp_mt(const struct sk_buff *skb, struct xt_action_param *par) |
2e4e6a17a [NETFILTER] x_tab... |
125 |
{ |
3cf93c96a [NETFILTER]: anno... |
126 127 |
const struct udphdr *uh; struct udphdr _udph; |
f7108a20d netfilter: xtable... |
128 |
const struct xt_udp *udpinfo = par->matchinfo; |
2e4e6a17a [NETFILTER] x_tab... |
129 130 |
/* Must not be a fragment. */ |
f7108a20d netfilter: xtable... |
131 |
if (par->fragoff != 0) |
1d93a9cba [NETFILTER]: x_ta... |
132 |
return false; |
2e4e6a17a [NETFILTER] x_tab... |
133 |
|
f7108a20d netfilter: xtable... |
134 |
uh = skb_header_pointer(skb, par->thoff, sizeof(_udph), &_udph); |
2e4e6a17a [NETFILTER] x_tab... |
135 136 137 |
if (uh == NULL) { /* We've been asked to examine this packet, and we can't. Hence, no choice but to drop. */ |
be91fd5e3 netfilter: xtable... |
138 139 |
pr_debug("Dropping evil UDP tinygram. "); |
b4ba26119 netfilter: xtable... |
140 |
par->hotdrop = true; |
1d93a9cba [NETFILTER]: x_ta... |
141 |
return false; |
2e4e6a17a [NETFILTER] x_tab... |
142 143 144 145 146 147 148 149 150 |
} 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)); } |
b0f38452f netfilter: xtable... |
151 |
static int udp_mt_check(const struct xt_mtchk_param *par) |
2e4e6a17a [NETFILTER] x_tab... |
152 |
{ |
9b4fce7a3 netfilter: xtable... |
153 |
const struct xt_udp *udpinfo = par->matchinfo; |
2e4e6a17a [NETFILTER] x_tab... |
154 |
|
5d04bff09 [NETFILTER]: Conv... |
155 |
/* Must specify no unknown invflags */ |
bd414ee60 netfilter: xtable... |
156 |
return (udpinfo->invflags & ~XT_UDP_INV_MASK) ? -EINVAL : 0; |
2e4e6a17a [NETFILTER] x_tab... |
157 |
} |
d3c5ee6d5 [NETFILTER]: x_ta... |
158 |
static struct xt_match tcpudp_mt_reg[] __read_mostly = { |
4470bbc74 [NETFILTER]: x_ta... |
159 160 |
{ .name = "tcp", |
ee999d8b9 netfilter: x_tabl... |
161 |
.family = NFPROTO_IPV4, |
d3c5ee6d5 [NETFILTER]: x_ta... |
162 163 |
.checkentry = tcp_mt_check, .match = tcp_mt, |
4470bbc74 [NETFILTER]: x_ta... |
164 165 166 167 168 169 |
.matchsize = sizeof(struct xt_tcp), .proto = IPPROTO_TCP, .me = THIS_MODULE, }, { .name = "tcp", |
ee999d8b9 netfilter: x_tabl... |
170 |
.family = NFPROTO_IPV6, |
d3c5ee6d5 [NETFILTER]: x_ta... |
171 172 |
.checkentry = tcp_mt_check, .match = tcp_mt, |
4470bbc74 [NETFILTER]: x_ta... |
173 174 175 176 177 178 |
.matchsize = sizeof(struct xt_tcp), .proto = IPPROTO_TCP, .me = THIS_MODULE, }, { .name = "udp", |
ee999d8b9 netfilter: x_tabl... |
179 |
.family = NFPROTO_IPV4, |
d3c5ee6d5 [NETFILTER]: x_ta... |
180 181 |
.checkentry = udp_mt_check, .match = udp_mt, |
4470bbc74 [NETFILTER]: x_ta... |
182 183 184 185 186 187 |
.matchsize = sizeof(struct xt_udp), .proto = IPPROTO_UDP, .me = THIS_MODULE, }, { .name = "udp", |
ee999d8b9 netfilter: x_tabl... |
188 |
.family = NFPROTO_IPV6, |
d3c5ee6d5 [NETFILTER]: x_ta... |
189 190 |
.checkentry = udp_mt_check, .match = udp_mt, |
4470bbc74 [NETFILTER]: x_ta... |
191 192 193 194 |
.matchsize = sizeof(struct xt_udp), .proto = IPPROTO_UDP, .me = THIS_MODULE, }, |
ba4e58eca [NET]: Supporting... |
195 196 |
{ .name = "udplite", |
ee999d8b9 netfilter: x_tabl... |
197 |
.family = NFPROTO_IPV4, |
d3c5ee6d5 [NETFILTER]: x_ta... |
198 199 |
.checkentry = udp_mt_check, .match = udp_mt, |
ba4e58eca [NET]: Supporting... |
200 201 202 203 204 205 |
.matchsize = sizeof(struct xt_udp), .proto = IPPROTO_UDPLITE, .me = THIS_MODULE, }, { .name = "udplite", |
ee999d8b9 netfilter: x_tabl... |
206 |
.family = NFPROTO_IPV6, |
d3c5ee6d5 [NETFILTER]: x_ta... |
207 208 |
.checkentry = udp_mt_check, .match = udp_mt, |
ba4e58eca [NET]: Supporting... |
209 210 211 212 |
.matchsize = sizeof(struct xt_udp), .proto = IPPROTO_UDPLITE, .me = THIS_MODULE, }, |
2e4e6a17a [NETFILTER] x_tab... |
213 |
}; |
d3c5ee6d5 [NETFILTER]: x_ta... |
214 |
static int __init tcpudp_mt_init(void) |
2e4e6a17a [NETFILTER] x_tab... |
215 |
{ |
d3c5ee6d5 [NETFILTER]: x_ta... |
216 |
return xt_register_matches(tcpudp_mt_reg, ARRAY_SIZE(tcpudp_mt_reg)); |
2e4e6a17a [NETFILTER] x_tab... |
217 |
} |
d3c5ee6d5 [NETFILTER]: x_ta... |
218 |
static void __exit tcpudp_mt_exit(void) |
2e4e6a17a [NETFILTER] x_tab... |
219 |
{ |
d3c5ee6d5 [NETFILTER]: x_ta... |
220 |
xt_unregister_matches(tcpudp_mt_reg, ARRAY_SIZE(tcpudp_mt_reg)); |
2e4e6a17a [NETFILTER] x_tab... |
221 |
} |
d3c5ee6d5 [NETFILTER]: x_ta... |
222 223 |
module_init(tcpudp_mt_init); module_exit(tcpudp_mt_exit); |