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>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
11 #include <linux/module.h>
12 #include <linux/skbuff.h>
14 #include <linux/gfp.h>
15 #include <linux/ipv6.h>
16 #include <linux/tcp.h>
20 #include <net/route.h>
23 #include <linux/netfilter_ipv4/ip_tables.h>
24 #include <linux/netfilter_ipv6/ip6_tables.h>
25 #include <linux/netfilter/x_tables.h>
26 #include <linux/netfilter/xt_tcpudp.h>
27 #include <linux/netfilter/xt_TCPMSS.h>
29 MODULE_LICENSE("GPL");
30 MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>");
31 MODULE_DESCRIPTION("Xtables: TCP Maximum Segment Size (MSS) adjustment");
32 MODULE_ALIAS("ipt_TCPMSS");
33 MODULE_ALIAS("ip6t_TCPMSS");
35 static inline unsigned int
36 optlen(const u_int8_t
*opt
, unsigned int offset
)
38 /* Beware zero-length options: make finite progress */
39 if (opt
[offset
] <= TCPOPT_NOP
|| opt
[offset
+1] == 0)
46 tcpmss_mangle_packet(struct sk_buff
*skb
,
47 const struct xt_tcpmss_info
*info
,
53 unsigned int tcplen
, i
;
58 if (!skb_make_writable(skb
, skb
->len
))
61 tcplen
= skb
->len
- tcphoff
;
62 tcph
= (struct tcphdr
*)(skb_network_header(skb
) + tcphoff
);
64 /* Header cannot be larger than the packet */
65 if (tcplen
< tcph
->doff
*4)
68 if (info
->mss
== XT_TCPMSS_CLAMP_PMTU
) {
69 if (dst_mtu(skb_dst(skb
)) <= minlen
) {
71 printk(KERN_ERR
"xt_TCPMSS: "
72 "unknown or invalid path-MTU (%u)\n",
73 dst_mtu(skb_dst(skb
)));
76 if (in_mtu
<= minlen
) {
78 printk(KERN_ERR
"xt_TCPMSS: unknown or "
79 "invalid path-MTU (%u)\n", in_mtu
);
82 newmss
= min(dst_mtu(skb_dst(skb
)), in_mtu
) - minlen
;
86 opt
= (u_int8_t
*)tcph
;
87 for (i
= sizeof(struct tcphdr
); i
< tcph
->doff
*4; i
+= optlen(opt
, i
)) {
88 if (opt
[i
] == TCPOPT_MSS
&& tcph
->doff
*4 - i
>= TCPOLEN_MSS
&&
89 opt
[i
+1] == TCPOLEN_MSS
) {
92 oldmss
= (opt
[i
+2] << 8) | opt
[i
+3];
94 /* Never increase MSS, even when setting it, as
95 * doing so results in problems for hosts that rely
96 * on MSS being set correctly.
101 opt
[i
+2] = (newmss
& 0xff00) >> 8;
102 opt
[i
+3] = newmss
& 0x00ff;
104 inet_proto_csum_replace2(&tcph
->check
, skb
,
105 htons(oldmss
), htons(newmss
),
111 /* There is data after the header so the option can't be added
112 without moving it, and doing so may make the SYN packet
113 itself too large. Accept the packet unmodified instead. */
114 if (tcplen
> tcph
->doff
*4)
118 * MSS Option not found ?! add it..
120 if (skb_tailroom(skb
) < TCPOLEN_MSS
) {
121 if (pskb_expand_head(skb
, 0,
122 TCPOLEN_MSS
- skb_tailroom(skb
),
125 tcph
= (struct tcphdr
*)(skb_network_header(skb
) + tcphoff
);
128 skb_put(skb
, TCPOLEN_MSS
);
130 opt
= (u_int8_t
*)tcph
+ sizeof(struct tcphdr
);
131 memmove(opt
+ TCPOLEN_MSS
, opt
, tcplen
- sizeof(struct tcphdr
));
133 inet_proto_csum_replace2(&tcph
->check
, skb
,
134 htons(tcplen
), htons(tcplen
+ TCPOLEN_MSS
), 1);
136 opt
[1] = TCPOLEN_MSS
;
137 opt
[2] = (newmss
& 0xff00) >> 8;
138 opt
[3] = newmss
& 0x00ff;
140 inet_proto_csum_replace4(&tcph
->check
, skb
, 0, *((__be32
*)opt
), 0);
142 oldval
= ((__be16
*)tcph
)[6];
143 tcph
->doff
+= TCPOLEN_MSS
/4;
144 inet_proto_csum_replace2(&tcph
->check
, skb
,
145 oldval
, ((__be16
*)tcph
)[6], 0);
149 static u_int32_t
tcpmss_reverse_mtu(const struct sk_buff
*skb
,
152 struct flowi fl
= {};
153 const struct nf_afinfo
*ai
;
154 struct rtable
*rt
= NULL
;
157 if (family
== PF_INET
)
158 fl
.fl4_dst
= ip_hdr(skb
)->saddr
;
160 fl
.fl6_dst
= ipv6_hdr(skb
)->saddr
;
163 ai
= nf_get_afinfo(family
);
165 ai
->route((struct dst_entry
**)&rt
, &fl
);
169 mtu
= dst_mtu(&rt
->u
.dst
);
170 dst_release(&rt
->u
.dst
);
176 tcpmss_tg4(struct sk_buff
*skb
, const struct xt_target_param
*par
)
178 struct iphdr
*iph
= ip_hdr(skb
);
182 ret
= tcpmss_mangle_packet(skb
, par
->targinfo
,
183 tcpmss_reverse_mtu(skb
, PF_INET
),
185 sizeof(*iph
) + sizeof(struct tcphdr
));
190 newlen
= htons(ntohs(iph
->tot_len
) + ret
);
191 csum_replace2(&iph
->check
, iph
->tot_len
, newlen
);
192 iph
->tot_len
= newlen
;
197 #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
199 tcpmss_tg6(struct sk_buff
*skb
, const struct xt_target_param
*par
)
201 struct ipv6hdr
*ipv6h
= ipv6_hdr(skb
);
206 nexthdr
= ipv6h
->nexthdr
;
207 tcphoff
= ipv6_skip_exthdr(skb
, sizeof(*ipv6h
), &nexthdr
);
210 ret
= tcpmss_mangle_packet(skb
, par
->targinfo
,
211 tcpmss_reverse_mtu(skb
, PF_INET6
),
213 sizeof(*ipv6h
) + sizeof(struct tcphdr
));
217 ipv6h
= ipv6_hdr(skb
);
218 ipv6h
->payload_len
= htons(ntohs(ipv6h
->payload_len
) + ret
);
226 /* Must specify -p tcp --syn */
227 static inline bool find_syn_match(const struct xt_entry_match
*m
)
229 const struct xt_tcp
*tcpinfo
= (const struct xt_tcp
*)m
->data
;
231 if (strcmp(m
->u
.kernel
.match
->name
, "tcp") == 0 &&
232 tcpinfo
->flg_cmp
& TH_SYN
&&
233 !(tcpinfo
->invflags
& XT_TCP_INV_FLAGS
))
239 static bool tcpmss_tg4_check(const struct xt_tgchk_param
*par
)
241 const struct xt_tcpmss_info
*info
= par
->targinfo
;
242 const struct ipt_entry
*e
= par
->entryinfo
;
243 const struct xt_entry_match
*ematch
;
245 if (info
->mss
== XT_TCPMSS_CLAMP_PMTU
&&
246 (par
->hook_mask
& ~((1 << NF_INET_FORWARD
) |
247 (1 << NF_INET_LOCAL_OUT
) |
248 (1 << NF_INET_POST_ROUTING
))) != 0) {
249 printk("xt_TCPMSS: path-MTU clamping only supported in "
250 "FORWARD, OUTPUT and POSTROUTING hooks\n");
253 xt_ematch_foreach(ematch
, e
)
254 if (find_syn_match(ematch
))
256 printk("xt_TCPMSS: Only works on TCP SYN packets\n");
260 #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
261 static bool tcpmss_tg6_check(const struct xt_tgchk_param
*par
)
263 const struct xt_tcpmss_info
*info
= par
->targinfo
;
264 const struct ip6t_entry
*e
= par
->entryinfo
;
265 const struct xt_entry_match
*ematch
;
267 if (info
->mss
== XT_TCPMSS_CLAMP_PMTU
&&
268 (par
->hook_mask
& ~((1 << NF_INET_FORWARD
) |
269 (1 << NF_INET_LOCAL_OUT
) |
270 (1 << NF_INET_POST_ROUTING
))) != 0) {
271 printk("xt_TCPMSS: path-MTU clamping only supported in "
272 "FORWARD, OUTPUT and POSTROUTING hooks\n");
275 xt_ematch_foreach(ematch
, e
)
276 if (find_syn_match(ematch
))
278 printk("xt_TCPMSS: Only works on TCP SYN packets\n");
283 static struct xt_target tcpmss_tg_reg
[] __read_mostly
= {
285 .family
= NFPROTO_IPV4
,
287 .checkentry
= tcpmss_tg4_check
,
288 .target
= tcpmss_tg4
,
289 .targetsize
= sizeof(struct xt_tcpmss_info
),
290 .proto
= IPPROTO_TCP
,
293 #if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
295 .family
= NFPROTO_IPV6
,
297 .checkentry
= tcpmss_tg6_check
,
298 .target
= tcpmss_tg6
,
299 .targetsize
= sizeof(struct xt_tcpmss_info
),
300 .proto
= IPPROTO_TCP
,
306 static int __init
tcpmss_tg_init(void)
308 return xt_register_targets(tcpmss_tg_reg
, ARRAY_SIZE(tcpmss_tg_reg
));
311 static void __exit
tcpmss_tg_exit(void)
313 xt_unregister_targets(tcpmss_tg_reg
, ARRAY_SIZE(tcpmss_tg_reg
));
316 module_init(tcpmss_tg_init
);
317 module_exit(tcpmss_tg_exit
);