Blame view
net/netfilter/xt_TCPMSS.c
8.04 KB
cdd289a2f [NETFILTER]: add ... |
1 2 3 4 5 6 7 8 9 |
/* * This is a module which is used for setting the MSS option in TCP packets. * * Copyright (C) 2000 Marc Boucher <marc@mbsi.ca> * * 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. */ |
8bee4bad0 netfilter: xt ext... |
10 |
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
cdd289a2f [NETFILTER]: add ... |
11 12 13 |
#include <linux/module.h> #include <linux/skbuff.h> #include <linux/ip.h> |
5a0e3ad6a include cleanup: ... |
14 |
#include <linux/gfp.h> |
cdd289a2f [NETFILTER]: add ... |
15 16 |
#include <linux/ipv6.h> #include <linux/tcp.h> |
37c08387f [NETFILTER]: xt_T... |
17 18 |
#include <net/dst.h> #include <net/flow.h> |
cdd289a2f [NETFILTER]: add ... |
19 |
#include <net/ipv6.h> |
37c08387f [NETFILTER]: xt_T... |
20 |
#include <net/route.h> |
cdd289a2f [NETFILTER]: add ... |
21 22 23 24 25 26 27 28 29 30 |
#include <net/tcp.h> #include <linux/netfilter_ipv4/ip_tables.h> #include <linux/netfilter_ipv6/ip6_tables.h> #include <linux/netfilter/x_tables.h> #include <linux/netfilter/xt_tcpudp.h> #include <linux/netfilter/xt_TCPMSS.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>"); |
2ae15b64e [NETFILTER]: Upda... |
31 |
MODULE_DESCRIPTION("Xtables: TCP Maximum Segment Size (MSS) adjustment"); |
cdd289a2f [NETFILTER]: add ... |
32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
MODULE_ALIAS("ipt_TCPMSS"); MODULE_ALIAS("ip6t_TCPMSS"); static inline unsigned int optlen(const u_int8_t *opt, unsigned int offset) { /* Beware zero-length options: make finite progress */ if (opt[offset] <= TCPOPT_NOP || opt[offset+1] == 0) return 1; else return opt[offset+1]; } static int |
3db05fea5 [NETFILTER]: Repl... |
46 |
tcpmss_mangle_packet(struct sk_buff *skb, |
cdd289a2f [NETFILTER]: add ... |
47 |
const struct xt_tcpmss_info *info, |
37c08387f [NETFILTER]: xt_T... |
48 |
unsigned int in_mtu, |
cdd289a2f [NETFILTER]: add ... |
49 50 51 52 53 54 55 56 |
unsigned int tcphoff, unsigned int minlen) { struct tcphdr *tcph; unsigned int tcplen, i; __be16 oldval; u16 newmss; u8 *opt; |
3db05fea5 [NETFILTER]: Repl... |
57 |
if (!skb_make_writable(skb, skb->len)) |
cdd289a2f [NETFILTER]: add ... |
58 |
return -1; |
3db05fea5 [NETFILTER]: Repl... |
59 60 |
tcplen = skb->len - tcphoff; tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff); |
cdd289a2f [NETFILTER]: add ... |
61 |
|
10a199394 netfilter: xt_TCP... |
62 63 |
/* Header cannot be larger than the packet */ if (tcplen < tcph->doff*4) |
cdd289a2f [NETFILTER]: add ... |
64 |
return -1; |
cdd289a2f [NETFILTER]: add ... |
65 66 |
if (info->mss == XT_TCPMSS_CLAMP_PMTU) { |
adf30907d net: skb->dst acc... |
67 |
if (dst_mtu(skb_dst(skb)) <= minlen) { |
cdd289a2f [NETFILTER]: add ... |
68 |
if (net_ratelimit()) |
ff67e4e42 netfilter: xt ext... |
69 70 |
pr_err("unknown or invalid path-MTU (%u) ", |
adf30907d net: skb->dst acc... |
71 |
dst_mtu(skb_dst(skb))); |
cdd289a2f [NETFILTER]: add ... |
72 73 |
return -1; } |
37c08387f [NETFILTER]: xt_T... |
74 75 |
if (in_mtu <= minlen) { if (net_ratelimit()) |
ff67e4e42 netfilter: xt ext... |
76 77 78 |
pr_err("unknown or invalid path-MTU (%u) ", in_mtu); |
37c08387f [NETFILTER]: xt_T... |
79 80 |
return -1; } |
adf30907d net: skb->dst acc... |
81 |
newmss = min(dst_mtu(skb_dst(skb)), in_mtu) - minlen; |
cdd289a2f [NETFILTER]: add ... |
82 83 84 85 86 87 88 89 90 91 |
} else newmss = info->mss; opt = (u_int8_t *)tcph; for (i = sizeof(struct tcphdr); i < tcph->doff*4; i += optlen(opt, i)) { if (opt[i] == TCPOPT_MSS && tcph->doff*4 - i >= TCPOLEN_MSS && opt[i+1] == TCPOLEN_MSS) { u_int16_t oldmss; oldmss = (opt[i+2] << 8) | opt[i+3]; |
170080645 [NETFILTER]: xt_T... |
92 93 94 95 96 |
/* Never increase MSS, even when setting it, as * doing so results in problems for hosts that rely * on MSS being set correctly. */ if (oldmss <= newmss) |
cdd289a2f [NETFILTER]: add ... |
97 98 99 |
return 0; opt[i+2] = (newmss & 0xff00) >> 8; |
7c4e36bc1 [NETFILTER]: Remo... |
100 |
opt[i+3] = newmss & 0x00ff; |
cdd289a2f [NETFILTER]: add ... |
101 |
|
be0ea7d5d [NETFILTER]: Conv... |
102 103 104 |
inet_proto_csum_replace2(&tcph->check, skb, htons(oldmss), htons(newmss), 0); |
cdd289a2f [NETFILTER]: add ... |
105 106 107 |
return 0; } } |
10a199394 netfilter: xt_TCP... |
108 109 110 111 112 |
/* There is data after the header so the option can't be added without moving it, and doing so may make the SYN packet itself too large. Accept the packet unmodified instead. */ if (tcplen > tcph->doff*4) return 0; |
cdd289a2f [NETFILTER]: add ... |
113 114 115 |
/* * MSS Option not found ?! add it.. */ |
3db05fea5 [NETFILTER]: Repl... |
116 117 118 |
if (skb_tailroom(skb) < TCPOLEN_MSS) { if (pskb_expand_head(skb, 0, TCPOLEN_MSS - skb_tailroom(skb), |
2ca7b0ac0 [NETFILTER]: Avoi... |
119 |
GFP_ATOMIC)) |
cdd289a2f [NETFILTER]: add ... |
120 |
return -1; |
3db05fea5 [NETFILTER]: Repl... |
121 |
tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff); |
cdd289a2f [NETFILTER]: add ... |
122 |
} |
3db05fea5 [NETFILTER]: Repl... |
123 |
skb_put(skb, TCPOLEN_MSS); |
cdd289a2f [NETFILTER]: add ... |
124 125 126 |
opt = (u_int8_t *)tcph + sizeof(struct tcphdr); memmove(opt + TCPOLEN_MSS, opt, tcplen - sizeof(struct tcphdr)); |
be0ea7d5d [NETFILTER]: Conv... |
127 128 |
inet_proto_csum_replace2(&tcph->check, skb, htons(tcplen), htons(tcplen + TCPOLEN_MSS), 1); |
cdd289a2f [NETFILTER]: add ... |
129 130 131 |
opt[0] = TCPOPT_MSS; opt[1] = TCPOLEN_MSS; opt[2] = (newmss & 0xff00) >> 8; |
7c4e36bc1 [NETFILTER]: Remo... |
132 |
opt[3] = newmss & 0x00ff; |
cdd289a2f [NETFILTER]: add ... |
133 |
|
be0ea7d5d [NETFILTER]: Conv... |
134 |
inet_proto_csum_replace4(&tcph->check, skb, 0, *((__be32 *)opt), 0); |
cdd289a2f [NETFILTER]: add ... |
135 136 137 |
oldval = ((__be16 *)tcph)[6]; tcph->doff += TCPOLEN_MSS/4; |
be0ea7d5d [NETFILTER]: Conv... |
138 139 |
inet_proto_csum_replace2(&tcph->check, skb, oldval, ((__be16 *)tcph)[6], 0); |
cdd289a2f [NETFILTER]: add ... |
140 141 |
return TCPOLEN_MSS; } |
db1a75bdc netfilter: xt_TCP... |
142 143 |
static u_int32_t tcpmss_reverse_mtu(const struct sk_buff *skb, unsigned int family) |
37c08387f [NETFILTER]: xt_T... |
144 |
{ |
a1bbb0e69 netfilter: Use fl... |
145 |
struct flowi fl; |
37c08387f [NETFILTER]: xt_T... |
146 147 148 |
const struct nf_afinfo *ai; struct rtable *rt = NULL; u_int32_t mtu = ~0U; |
a1bbb0e69 netfilter: Use fl... |
149 150 151 152 153 154 |
if (family == PF_INET) { struct flowi4 *fl4 = &fl.u.ip4; memset(fl4, 0, sizeof(*fl4)); fl4->daddr = ip_hdr(skb)->saddr; } else { struct flowi6 *fl6 = &fl.u.ip6; |
db1a75bdc netfilter: xt_TCP... |
155 |
|
a1bbb0e69 netfilter: Use fl... |
156 |
memset(fl6, 0, sizeof(*fl6)); |
4e3fd7a06 net: remove ipv6_... |
157 |
fl6->daddr = ipv6_hdr(skb)->saddr; |
a1bbb0e69 netfilter: Use fl... |
158 |
} |
37c08387f [NETFILTER]: xt_T... |
159 |
rcu_read_lock(); |
db1a75bdc netfilter: xt_TCP... |
160 |
ai = nf_get_afinfo(family); |
37c08387f [NETFILTER]: xt_T... |
161 |
if (ai != NULL) |
0fae2e774 netfilter: af_inf... |
162 |
ai->route(&init_net, (struct dst_entry **)&rt, &fl, false); |
37c08387f [NETFILTER]: xt_T... |
163 164 165 |
rcu_read_unlock(); if (rt != NULL) { |
d8d1f30b9 net-next: remove ... |
166 167 |
mtu = dst_mtu(&rt->dst); dst_release(&rt->dst); |
37c08387f [NETFILTER]: xt_T... |
168 169 170 |
} return mtu; } |
cdd289a2f [NETFILTER]: add ... |
171 |
static unsigned int |
4b560b447 netfilter: xtable... |
172 |
tcpmss_tg4(struct sk_buff *skb, const struct xt_action_param *par) |
cdd289a2f [NETFILTER]: add ... |
173 |
{ |
3db05fea5 [NETFILTER]: Repl... |
174 |
struct iphdr *iph = ip_hdr(skb); |
cdd289a2f [NETFILTER]: add ... |
175 176 |
__be16 newlen; int ret; |
7eb355865 netfilter: xtable... |
177 |
ret = tcpmss_mangle_packet(skb, par->targinfo, |
db1a75bdc netfilter: xt_TCP... |
178 |
tcpmss_reverse_mtu(skb, PF_INET), |
37c08387f [NETFILTER]: xt_T... |
179 |
iph->ihl * 4, |
cdd289a2f [NETFILTER]: add ... |
180 181 182 183 |
sizeof(*iph) + sizeof(struct tcphdr)); if (ret < 0) return NF_DROP; if (ret > 0) { |
3db05fea5 [NETFILTER]: Repl... |
184 |
iph = ip_hdr(skb); |
cdd289a2f [NETFILTER]: add ... |
185 |
newlen = htons(ntohs(iph->tot_len) + ret); |
be0ea7d5d [NETFILTER]: Conv... |
186 |
csum_replace2(&iph->check, iph->tot_len, newlen); |
cdd289a2f [NETFILTER]: add ... |
187 188 189 190 |
iph->tot_len = newlen; } return XT_CONTINUE; } |
c0cd11566 net:netfilter: us... |
191 |
#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) |
cdd289a2f [NETFILTER]: add ... |
192 |
static unsigned int |
4b560b447 netfilter: xtable... |
193 |
tcpmss_tg6(struct sk_buff *skb, const struct xt_action_param *par) |
cdd289a2f [NETFILTER]: add ... |
194 |
{ |
3db05fea5 [NETFILTER]: Repl... |
195 |
struct ipv6hdr *ipv6h = ipv6_hdr(skb); |
cdd289a2f [NETFILTER]: add ... |
196 |
u8 nexthdr; |
75f2811c6 ipv6: Add fragmen... |
197 |
__be16 frag_off; |
cdd289a2f [NETFILTER]: add ... |
198 199 200 201 |
int tcphoff; int ret; nexthdr = ipv6h->nexthdr; |
75f2811c6 ipv6: Add fragmen... |
202 |
tcphoff = ipv6_skip_exthdr(skb, sizeof(*ipv6h), &nexthdr, &frag_off); |
9dc0564e8 [NETFILTER]: xt_T... |
203 |
if (tcphoff < 0) |
cdd289a2f [NETFILTER]: add ... |
204 |
return NF_DROP; |
7eb355865 netfilter: xtable... |
205 |
ret = tcpmss_mangle_packet(skb, par->targinfo, |
db1a75bdc netfilter: xt_TCP... |
206 |
tcpmss_reverse_mtu(skb, PF_INET6), |
37c08387f [NETFILTER]: xt_T... |
207 |
tcphoff, |
cdd289a2f [NETFILTER]: add ... |
208 209 210 211 |
sizeof(*ipv6h) + sizeof(struct tcphdr)); if (ret < 0) return NF_DROP; if (ret > 0) { |
3db05fea5 [NETFILTER]: Repl... |
212 |
ipv6h = ipv6_hdr(skb); |
cdd289a2f [NETFILTER]: add ... |
213 214 215 216 217 |
ipv6h->payload_len = htons(ntohs(ipv6h->payload_len) + ret); } return XT_CONTINUE; } #endif |
cdd289a2f [NETFILTER]: add ... |
218 |
/* Must specify -p tcp --syn */ |
e1931b784 [NETFILTER]: x_ta... |
219 |
static inline bool find_syn_match(const struct xt_entry_match *m) |
cdd289a2f [NETFILTER]: add ... |
220 221 222 223 |
{ const struct xt_tcp *tcpinfo = (const struct xt_tcp *)m->data; if (strcmp(m->u.kernel.match->name, "tcp") == 0 && |
a3433f35a tcp: unify tcp fl... |
224 |
tcpinfo->flg_cmp & TCPHDR_SYN && |
cdd289a2f [NETFILTER]: add ... |
225 |
!(tcpinfo->invflags & XT_TCP_INV_FLAGS)) |
e1931b784 [NETFILTER]: x_ta... |
226 |
return true; |
cdd289a2f [NETFILTER]: add ... |
227 |
|
e1931b784 [NETFILTER]: x_ta... |
228 |
return false; |
cdd289a2f [NETFILTER]: add ... |
229 |
} |
135367b8f netfilter: xtable... |
230 |
static int tcpmss_tg4_check(const struct xt_tgchk_param *par) |
cdd289a2f [NETFILTER]: add ... |
231 |
{ |
af5d6dc20 netfilter: xtable... |
232 233 |
const struct xt_tcpmss_info *info = par->targinfo; const struct ipt_entry *e = par->entryinfo; |
dcea992ac netfilter: xtable... |
234 |
const struct xt_entry_match *ematch; |
cdd289a2f [NETFILTER]: add ... |
235 236 |
if (info->mss == XT_TCPMSS_CLAMP_PMTU && |
af5d6dc20 netfilter: xtable... |
237 |
(par->hook_mask & ~((1 << NF_INET_FORWARD) | |
6e23ae2a4 [NETFILTER]: Intr... |
238 239 |
(1 << NF_INET_LOCAL_OUT) | (1 << NF_INET_POST_ROUTING))) != 0) { |
8bee4bad0 netfilter: xt ext... |
240 241 242 |
pr_info("path-MTU clamping only supported in " "FORWARD, OUTPUT and POSTROUTING hooks "); |
d6b00a534 netfilter: xtable... |
243 |
return -EINVAL; |
cdd289a2f [NETFILTER]: add ... |
244 |
} |
dcea992ac netfilter: xtable... |
245 246 |
xt_ematch_foreach(ematch, e) if (find_syn_match(ematch)) |
d6b00a534 netfilter: xtable... |
247 |
return 0; |
8bee4bad0 netfilter: xt ext... |
248 249 |
pr_info("Only works on TCP SYN packets "); |
d6b00a534 netfilter: xtable... |
250 |
return -EINVAL; |
cdd289a2f [NETFILTER]: add ... |
251 |
} |
c0cd11566 net:netfilter: us... |
252 |
#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) |
135367b8f netfilter: xtable... |
253 |
static int tcpmss_tg6_check(const struct xt_tgchk_param *par) |
cdd289a2f [NETFILTER]: add ... |
254 |
{ |
af5d6dc20 netfilter: xtable... |
255 256 |
const struct xt_tcpmss_info *info = par->targinfo; const struct ip6t_entry *e = par->entryinfo; |
dcea992ac netfilter: xtable... |
257 |
const struct xt_entry_match *ematch; |
cdd289a2f [NETFILTER]: add ... |
258 259 |
if (info->mss == XT_TCPMSS_CLAMP_PMTU && |
af5d6dc20 netfilter: xtable... |
260 |
(par->hook_mask & ~((1 << NF_INET_FORWARD) | |
6e23ae2a4 [NETFILTER]: Intr... |
261 262 |
(1 << NF_INET_LOCAL_OUT) | (1 << NF_INET_POST_ROUTING))) != 0) { |
8bee4bad0 netfilter: xt ext... |
263 264 265 |
pr_info("path-MTU clamping only supported in " "FORWARD, OUTPUT and POSTROUTING hooks "); |
d6b00a534 netfilter: xtable... |
266 |
return -EINVAL; |
cdd289a2f [NETFILTER]: add ... |
267 |
} |
dcea992ac netfilter: xtable... |
268 269 |
xt_ematch_foreach(ematch, e) if (find_syn_match(ematch)) |
d6b00a534 netfilter: xtable... |
270 |
return 0; |
8bee4bad0 netfilter: xt ext... |
271 272 |
pr_info("Only works on TCP SYN packets "); |
d6b00a534 netfilter: xtable... |
273 |
return -EINVAL; |
cdd289a2f [NETFILTER]: add ... |
274 275 |
} #endif |
d3c5ee6d5 [NETFILTER]: x_ta... |
276 |
static struct xt_target tcpmss_tg_reg[] __read_mostly = { |
cdd289a2f [NETFILTER]: add ... |
277 |
{ |
ee999d8b9 netfilter: x_tabl... |
278 |
.family = NFPROTO_IPV4, |
cdd289a2f [NETFILTER]: add ... |
279 |
.name = "TCPMSS", |
d3c5ee6d5 [NETFILTER]: x_ta... |
280 281 |
.checkentry = tcpmss_tg4_check, .target = tcpmss_tg4, |
cdd289a2f [NETFILTER]: add ... |
282 283 284 285 |
.targetsize = sizeof(struct xt_tcpmss_info), .proto = IPPROTO_TCP, .me = THIS_MODULE, }, |
c0cd11566 net:netfilter: us... |
286 |
#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) |
cdd289a2f [NETFILTER]: add ... |
287 |
{ |
ee999d8b9 netfilter: x_tabl... |
288 |
.family = NFPROTO_IPV6, |
cdd289a2f [NETFILTER]: add ... |
289 |
.name = "TCPMSS", |
d3c5ee6d5 [NETFILTER]: x_ta... |
290 291 |
.checkentry = tcpmss_tg6_check, .target = tcpmss_tg6, |
cdd289a2f [NETFILTER]: add ... |
292 293 294 295 296 297 |
.targetsize = sizeof(struct xt_tcpmss_info), .proto = IPPROTO_TCP, .me = THIS_MODULE, }, #endif }; |
d3c5ee6d5 [NETFILTER]: x_ta... |
298 |
static int __init tcpmss_tg_init(void) |
cdd289a2f [NETFILTER]: add ... |
299 |
{ |
d3c5ee6d5 [NETFILTER]: x_ta... |
300 |
return xt_register_targets(tcpmss_tg_reg, ARRAY_SIZE(tcpmss_tg_reg)); |
cdd289a2f [NETFILTER]: add ... |
301 |
} |
d3c5ee6d5 [NETFILTER]: x_ta... |
302 |
static void __exit tcpmss_tg_exit(void) |
cdd289a2f [NETFILTER]: add ... |
303 |
{ |
d3c5ee6d5 [NETFILTER]: x_ta... |
304 |
xt_unregister_targets(tcpmss_tg_reg, ARRAY_SIZE(tcpmss_tg_reg)); |
cdd289a2f [NETFILTER]: add ... |
305 |
} |
d3c5ee6d5 [NETFILTER]: x_ta... |
306 307 |
module_init(tcpmss_tg_init); module_exit(tcpmss_tg_exit); |