]>
git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blob - net/netfilter/xt_TCPMSS.c
2 * This is a module which is used for setting the MSS option in TCP packets.
4 * Copyright (C) 2000 Marc Boucher <marc@mbsi.ca>
5 * Copyright (C) 2007 Patrick McHardy <kaber@trash.net>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
11 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
12 #include <linux/module.h>
13 #include <linux/skbuff.h>
15 #include <linux/gfp.h>
16 #include <linux/ipv6.h>
17 #include <linux/tcp.h>
21 #include <net/route.h>
24 #include <linux/netfilter_ipv4/ip_tables.h>
25 #include <linux/netfilter_ipv6/ip6_tables.h>
26 #include <linux/netfilter/x_tables.h>
27 #include <linux/netfilter/xt_tcpudp.h>
28 #include <linux/netfilter/xt_TCPMSS.h>
30 MODULE_LICENSE("GPL");
31 MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>");
32 MODULE_DESCRIPTION("Xtables: TCP Maximum Segment Size (MSS) adjustment");
33 MODULE_ALIAS("ipt_TCPMSS");
34 MODULE_ALIAS("ip6t_TCPMSS");
36 static inline unsigned int
37 optlen(const u_int8_t
*opt
, unsigned int offset
)
39 /* Beware zero-length options: make finite progress */
40 if (opt
[offset
] <= TCPOPT_NOP
|| opt
[offset
+1] == 0)
47 tcpmss_mangle_packet(struct sk_buff
*skb
,
48 const struct xt_tcpmss_info
*info
,
54 unsigned int tcplen
, i
;
59 if (!skb_make_writable(skb
, skb
->len
))
62 tcplen
= skb
->len
- tcphoff
;
63 tcph
= (struct tcphdr
*)(skb_network_header(skb
) + tcphoff
);
65 /* Header cannot be larger than the packet */
66 if (tcplen
< tcph
->doff
*4)
69 if (info
->mss
== XT_TCPMSS_CLAMP_PMTU
) {
70 if (dst_mtu(skb_dst(skb
)) <= minlen
) {
71 net_err_ratelimited("unknown or invalid path-MTU (%u)\n",
72 dst_mtu(skb_dst(skb
)));
75 if (in_mtu
<= minlen
) {
76 net_err_ratelimited("unknown or invalid path-MTU (%u)\n",
80 newmss
= min(dst_mtu(skb_dst(skb
)), in_mtu
) - minlen
;
84 opt
= (u_int8_t
*)tcph
;
85 for (i
= sizeof(struct tcphdr
); i
< tcph
->doff
*4; i
+= optlen(opt
, i
)) {
86 if (opt
[i
] == TCPOPT_MSS
&& tcph
->doff
*4 - i
>= TCPOLEN_MSS
&&
87 opt
[i
+1] == TCPOLEN_MSS
) {
90 oldmss
= (opt
[i
+2] << 8) | opt
[i
+3];
92 /* Never increase MSS, even when setting it, as
93 * doing so results in problems for hosts that rely
94 * on MSS being set correctly.
99 opt
[i
+2] = (newmss
& 0xff00) >> 8;
100 opt
[i
+3] = newmss
& 0x00ff;
102 inet_proto_csum_replace2(&tcph
->check
, skb
,
103 htons(oldmss
), htons(newmss
),
109 /* There is data after the header so the option can't be added
110 without moving it, and doing so may make the SYN packet
111 itself too large. Accept the packet unmodified instead. */
112 if (tcplen
> tcph
->doff
*4)
116 * MSS Option not found ?! add it..
118 if (skb_tailroom(skb
) < TCPOLEN_MSS
) {
119 if (pskb_expand_head(skb
, 0,
120 TCPOLEN_MSS
- skb_tailroom(skb
),
123 tcph
= (struct tcphdr
*)(skb_network_header(skb
) + tcphoff
);
126 skb_put(skb
, TCPOLEN_MSS
);
128 opt
= (u_int8_t
*)tcph
+ sizeof(struct tcphdr
);
129 memmove(opt
+ TCPOLEN_MSS
, opt
, tcplen
- sizeof(struct tcphdr
));
131 inet_proto_csum_replace2(&tcph
->check
, skb
,
132 htons(tcplen
), htons(tcplen
+ TCPOLEN_MSS
), 1);
134 opt
[1] = TCPOLEN_MSS
;
135 opt
[2] = (newmss
& 0xff00) >> 8;
136 opt
[3] = newmss
& 0x00ff;
138 inet_proto_csum_replace4(&tcph
->check
, skb
, 0, *((__be32
*)opt
), 0);
140 oldval
= ((__be16
*)tcph
)[6];
141 tcph
->doff
+= TCPOLEN_MSS
/4;
142 inet_proto_csum_replace2(&tcph
->check
, skb
,
143 oldval
, ((__be16
*)tcph
)[6], 0);
147 static u_int32_t
tcpmss_reverse_mtu(const struct sk_buff
*skb
,
151 const struct nf_afinfo
*ai
;
152 struct rtable
*rt
= NULL
;
155 if (family
== PF_INET
) {
156 struct flowi4
*fl4
= &fl
.u
.ip4
;
157 memset(fl4
, 0, sizeof(*fl4
));
158 fl4
->daddr
= ip_hdr(skb
)->saddr
;
160 struct flowi6
*fl6
= &fl
.u
.ip6
;
162 memset(fl6
, 0, sizeof(*fl6
));
163 fl6
->daddr
= ipv6_hdr(skb
)->saddr
;
166 ai
= nf_get_afinfo(family
);
168 ai
->route(&init_net
, (struct dst_entry
**)&rt
, &fl
, false);
172 mtu
= dst_mtu(&rt
->dst
);
173 dst_release(&rt
->dst
);
179 tcpmss_tg4(struct sk_buff
*skb
, const struct xt_action_param
*par
)
181 struct iphdr
*iph
= ip_hdr(skb
);
185 ret
= tcpmss_mangle_packet(skb
, par
->targinfo
,
186 tcpmss_reverse_mtu(skb
, PF_INET
),
188 sizeof(*iph
) + sizeof(struct tcphdr
));
193 newlen
= htons(ntohs(iph
->tot_len
) + ret
);
194 csum_replace2(&iph
->check
, iph
->tot_len
, newlen
);
195 iph
->tot_len
= newlen
;
200 #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
202 tcpmss_tg6(struct sk_buff
*skb
, const struct xt_action_param
*par
)
204 struct ipv6hdr
*ipv6h
= ipv6_hdr(skb
);
210 nexthdr
= ipv6h
->nexthdr
;
211 tcphoff
= ipv6_skip_exthdr(skb
, sizeof(*ipv6h
), &nexthdr
, &frag_off
);
214 ret
= tcpmss_mangle_packet(skb
, par
->targinfo
,
215 tcpmss_reverse_mtu(skb
, PF_INET6
),
217 sizeof(*ipv6h
) + sizeof(struct tcphdr
));
221 ipv6h
= ipv6_hdr(skb
);
222 ipv6h
->payload_len
= htons(ntohs(ipv6h
->payload_len
) + ret
);
228 /* Must specify -p tcp --syn */
229 static inline bool find_syn_match(const struct xt_entry_match
*m
)
231 const struct xt_tcp
*tcpinfo
= (const struct xt_tcp
*)m
->data
;
233 if (strcmp(m
->u
.kernel
.match
->name
, "tcp") == 0 &&
234 tcpinfo
->flg_cmp
& TCPHDR_SYN
&&
235 !(tcpinfo
->invflags
& XT_TCP_INV_FLAGS
))
241 static int tcpmss_tg4_check(const struct xt_tgchk_param
*par
)
243 const struct xt_tcpmss_info
*info
= par
->targinfo
;
244 const struct ipt_entry
*e
= par
->entryinfo
;
245 const struct xt_entry_match
*ematch
;
247 if (info
->mss
== XT_TCPMSS_CLAMP_PMTU
&&
248 (par
->hook_mask
& ~((1 << NF_INET_FORWARD
) |
249 (1 << NF_INET_LOCAL_OUT
) |
250 (1 << NF_INET_POST_ROUTING
))) != 0) {
251 pr_info("path-MTU clamping only supported in "
252 "FORWARD, OUTPUT and POSTROUTING hooks\n");
255 xt_ematch_foreach(ematch
, e
)
256 if (find_syn_match(ematch
))
258 pr_info("Only works on TCP SYN packets\n");
262 #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
263 static int tcpmss_tg6_check(const struct xt_tgchk_param
*par
)
265 const struct xt_tcpmss_info
*info
= par
->targinfo
;
266 const struct ip6t_entry
*e
= par
->entryinfo
;
267 const struct xt_entry_match
*ematch
;
269 if (info
->mss
== XT_TCPMSS_CLAMP_PMTU
&&
270 (par
->hook_mask
& ~((1 << NF_INET_FORWARD
) |
271 (1 << NF_INET_LOCAL_OUT
) |
272 (1 << NF_INET_POST_ROUTING
))) != 0) {
273 pr_info("path-MTU clamping only supported in "
274 "FORWARD, OUTPUT and POSTROUTING hooks\n");
277 xt_ematch_foreach(ematch
, e
)
278 if (find_syn_match(ematch
))
280 pr_info("Only works on TCP SYN packets\n");
285 static struct xt_target tcpmss_tg_reg
[] __read_mostly
= {
287 .family
= NFPROTO_IPV4
,
289 .checkentry
= tcpmss_tg4_check
,
290 .target
= tcpmss_tg4
,
291 .targetsize
= sizeof(struct xt_tcpmss_info
),
292 .proto
= IPPROTO_TCP
,
295 #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
297 .family
= NFPROTO_IPV6
,
299 .checkentry
= tcpmss_tg6_check
,
300 .target
= tcpmss_tg6
,
301 .targetsize
= sizeof(struct xt_tcpmss_info
),
302 .proto
= IPPROTO_TCP
,
308 static int __init
tcpmss_tg_init(void)
310 return xt_register_targets(tcpmss_tg_reg
, ARRAY_SIZE(tcpmss_tg_reg
));
313 static void __exit
tcpmss_tg_exit(void)
315 xt_unregister_targets(tcpmss_tg_reg
, ARRAY_SIZE(tcpmss_tg_reg
));
318 module_init(tcpmss_tg_init
);
319 module_exit(tcpmss_tg_exit
);