Loading...
1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * This is a module which is used for setting the MSS option in TCP packets.
4 *
5 * Copyright (C) 2000 Marc Boucher <marc@mbsi.ca>
6 * Copyright (C) 2007 Patrick McHardy <kaber@trash.net>
7 */
8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
9#include <linux/module.h>
10#include <linux/skbuff.h>
11#include <linux/ip.h>
12#include <linux/gfp.h>
13#include <linux/ipv6.h>
14#include <linux/tcp.h>
15#include <net/dst.h>
16#include <net/flow.h>
17#include <net/ipv6.h>
18#include <net/route.h>
19#include <net/tcp.h>
20
21#include <linux/netfilter_ipv4/ip_tables.h>
22#include <linux/netfilter_ipv6/ip6_tables.h>
23#include <linux/netfilter/x_tables.h>
24#include <linux/netfilter/xt_tcpudp.h>
25#include <linux/netfilter/xt_TCPMSS.h>
26
27MODULE_LICENSE("GPL");
28MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>");
29MODULE_DESCRIPTION("Xtables: TCP Maximum Segment Size (MSS) adjustment");
30MODULE_ALIAS("ipt_TCPMSS");
31MODULE_ALIAS("ip6t_TCPMSS");
32
33static inline unsigned int
34optlen(const u_int8_t *opt, unsigned int offset)
35{
36 /* Beware zero-length options: make finite progress */
37 if (opt[offset] <= TCPOPT_NOP || opt[offset+1] == 0)
38 return 1;
39 else
40 return opt[offset+1];
41}
42
43static u_int32_t tcpmss_reverse_mtu(struct net *net,
44 const struct sk_buff *skb,
45 unsigned int family)
46{
47 struct flowi fl;
48 struct rtable *rt = NULL;
49 u_int32_t mtu = ~0U;
50
51 if (family == PF_INET) {
52 struct flowi4 *fl4 = &fl.u.ip4;
53 memset(fl4, 0, sizeof(*fl4));
54 fl4->daddr = ip_hdr(skb)->saddr;
55 } else {
56 struct flowi6 *fl6 = &fl.u.ip6;
57
58 memset(fl6, 0, sizeof(*fl6));
59 fl6->daddr = ipv6_hdr(skb)->saddr;
60 }
61
62 nf_route(net, (struct dst_entry **)&rt, &fl, false, family);
63 if (rt != NULL) {
64 mtu = dst_mtu(&rt->dst);
65 dst_release(&rt->dst);
66 }
67 return mtu;
68}
69
70static int
71tcpmss_mangle_packet(struct sk_buff *skb,
72 const struct xt_action_param *par,
73 unsigned int family,
74 unsigned int tcphoff,
75 unsigned int minlen)
76{
77 const struct xt_tcpmss_info *info = par->targinfo;
78 struct tcphdr *tcph;
79 int len, tcp_hdrlen;
80 unsigned int i;
81 __be16 oldval;
82 u16 newmss;
83 u8 *opt;
84
85 /* This is a fragment, no TCP header is available */
86 if (par->fragoff != 0)
87 return 0;
88
89 if (skb_ensure_writable(skb, skb->len))
90 return -1;
91
92 len = skb->len - tcphoff;
93 if (len < (int)sizeof(struct tcphdr))
94 return -1;
95
96 tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff);
97 tcp_hdrlen = tcph->doff * 4;
98
99 if (len < tcp_hdrlen || tcp_hdrlen < sizeof(struct tcphdr))
100 return -1;
101
102 if (info->mss == XT_TCPMSS_CLAMP_PMTU) {
103 struct net *net = xt_net(par);
104 unsigned int in_mtu = tcpmss_reverse_mtu(net, skb, family);
105 unsigned int min_mtu = min(dst_mtu(skb_dst(skb)), in_mtu);
106
107 if (min_mtu <= minlen) {
108 net_err_ratelimited("unknown or invalid path-MTU (%u)\n",
109 min_mtu);
110 return -1;
111 }
112 newmss = min_mtu - minlen;
113 } else
114 newmss = info->mss;
115
116 opt = (u_int8_t *)tcph;
117 for (i = sizeof(struct tcphdr); i <= tcp_hdrlen - TCPOLEN_MSS; i += optlen(opt, i)) {
118 if (opt[i] == TCPOPT_MSS && opt[i+1] == TCPOLEN_MSS) {
119 u_int16_t oldmss;
120
121 oldmss = (opt[i+2] << 8) | opt[i+3];
122
123 /* Never increase MSS, even when setting it, as
124 * doing so results in problems for hosts that rely
125 * on MSS being set correctly.
126 */
127 if (oldmss <= newmss)
128 return 0;
129
130 opt[i+2] = (newmss & 0xff00) >> 8;
131 opt[i+3] = newmss & 0x00ff;
132
133 inet_proto_csum_replace2(&tcph->check, skb,
134 htons(oldmss), htons(newmss),
135 false);
136 return 0;
137 }
138 }
139
140 /* There is data after the header so the option can't be added
141 * without moving it, and doing so may make the SYN packet
142 * itself too large. Accept the packet unmodified instead.
143 */
144 if (len > tcp_hdrlen)
145 return 0;
146
147 /* tcph->doff has 4 bits, do not wrap it to 0 */
148 if (tcp_hdrlen >= 15 * 4)
149 return 0;
150
151 /*
152 * MSS Option not found ?! add it..
153 */
154 if (skb_tailroom(skb) < TCPOLEN_MSS) {
155 if (pskb_expand_head(skb, 0,
156 TCPOLEN_MSS - skb_tailroom(skb),
157 GFP_ATOMIC))
158 return -1;
159 tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff);
160 }
161
162 skb_put(skb, TCPOLEN_MSS);
163
164 /*
165 * IPv4: RFC 1122 states "If an MSS option is not received at
166 * connection setup, TCP MUST assume a default send MSS of 536".
167 * IPv6: RFC 2460 states IPv6 has a minimum MTU of 1280 and a minimum
168 * length IPv6 header of 60, ergo the default MSS value is 1220
169 * Since no MSS was provided, we must use the default values
170 */
171 if (xt_family(par) == NFPROTO_IPV4)
172 newmss = min(newmss, (u16)536);
173 else
174 newmss = min(newmss, (u16)1220);
175
176 opt = (u_int8_t *)tcph + sizeof(struct tcphdr);
177 memmove(opt + TCPOLEN_MSS, opt, len - sizeof(struct tcphdr));
178
179 inet_proto_csum_replace2(&tcph->check, skb,
180 htons(len), htons(len + TCPOLEN_MSS), true);
181 opt[0] = TCPOPT_MSS;
182 opt[1] = TCPOLEN_MSS;
183 opt[2] = (newmss & 0xff00) >> 8;
184 opt[3] = newmss & 0x00ff;
185
186 inet_proto_csum_replace4(&tcph->check, skb, 0, *((__be32 *)opt), false);
187
188 oldval = ((__be16 *)tcph)[6];
189 tcph->doff += TCPOLEN_MSS/4;
190 inet_proto_csum_replace2(&tcph->check, skb,
191 oldval, ((__be16 *)tcph)[6], false);
192 return TCPOLEN_MSS;
193}
194
195static unsigned int
196tcpmss_tg4(struct sk_buff *skb, const struct xt_action_param *par)
197{
198 struct iphdr *iph = ip_hdr(skb);
199 __be16 newlen;
200 int ret;
201
202 ret = tcpmss_mangle_packet(skb, par,
203 PF_INET,
204 iph->ihl * 4,
205 sizeof(*iph) + sizeof(struct tcphdr));
206 if (ret < 0)
207 return NF_DROP;
208 if (ret > 0) {
209 iph = ip_hdr(skb);
210 newlen = htons(ntohs(iph->tot_len) + ret);
211 csum_replace2(&iph->check, iph->tot_len, newlen);
212 iph->tot_len = newlen;
213 }
214 return XT_CONTINUE;
215}
216
217#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
218static unsigned int
219tcpmss_tg6(struct sk_buff *skb, const struct xt_action_param *par)
220{
221 struct ipv6hdr *ipv6h = ipv6_hdr(skb);
222 u8 nexthdr;
223 __be16 frag_off, oldlen, newlen;
224 int tcphoff;
225 int ret;
226
227 nexthdr = ipv6h->nexthdr;
228 tcphoff = ipv6_skip_exthdr(skb, sizeof(*ipv6h), &nexthdr, &frag_off);
229 if (tcphoff < 0)
230 return NF_DROP;
231 ret = tcpmss_mangle_packet(skb, par,
232 PF_INET6,
233 tcphoff,
234 sizeof(*ipv6h) + sizeof(struct tcphdr));
235 if (ret < 0)
236 return NF_DROP;
237 if (ret > 0) {
238 ipv6h = ipv6_hdr(skb);
239 oldlen = ipv6h->payload_len;
240 newlen = htons(ntohs(oldlen) + ret);
241 if (skb->ip_summed == CHECKSUM_COMPLETE)
242 skb->csum = csum_add(csum_sub(skb->csum, oldlen),
243 newlen);
244 ipv6h->payload_len = newlen;
245 }
246 return XT_CONTINUE;
247}
248#endif
249
250/* Must specify -p tcp --syn */
251static inline bool find_syn_match(const struct xt_entry_match *m)
252{
253 const struct xt_tcp *tcpinfo = (const struct xt_tcp *)m->data;
254
255 if (strcmp(m->u.kernel.match->name, "tcp") == 0 &&
256 tcpinfo->flg_cmp & TCPHDR_SYN &&
257 !(tcpinfo->invflags & XT_TCP_INV_FLAGS))
258 return true;
259
260 return false;
261}
262
263static int tcpmss_tg4_check(const struct xt_tgchk_param *par)
264{
265 const struct xt_tcpmss_info *info = par->targinfo;
266 const struct ipt_entry *e = par->entryinfo;
267 const struct xt_entry_match *ematch;
268
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_ratelimited("path-MTU clamping only supported in FORWARD, OUTPUT and POSTROUTING hooks\n");
274 return -EINVAL;
275 }
276 if (par->nft_compat)
277 return 0;
278
279 xt_ematch_foreach(ematch, e)
280 if (find_syn_match(ematch))
281 return 0;
282 pr_info_ratelimited("Only works on TCP SYN packets\n");
283 return -EINVAL;
284}
285
286#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
287static int tcpmss_tg6_check(const struct xt_tgchk_param *par)
288{
289 const struct xt_tcpmss_info *info = par->targinfo;
290 const struct ip6t_entry *e = par->entryinfo;
291 const struct xt_entry_match *ematch;
292
293 if (info->mss == XT_TCPMSS_CLAMP_PMTU &&
294 (par->hook_mask & ~((1 << NF_INET_FORWARD) |
295 (1 << NF_INET_LOCAL_OUT) |
296 (1 << NF_INET_POST_ROUTING))) != 0) {
297 pr_info_ratelimited("path-MTU clamping only supported in FORWARD, OUTPUT and POSTROUTING hooks\n");
298 return -EINVAL;
299 }
300 if (par->nft_compat)
301 return 0;
302
303 xt_ematch_foreach(ematch, e)
304 if (find_syn_match(ematch))
305 return 0;
306 pr_info_ratelimited("Only works on TCP SYN packets\n");
307 return -EINVAL;
308}
309#endif
310
311static struct xt_target tcpmss_tg_reg[] __read_mostly = {
312 {
313 .family = NFPROTO_IPV4,
314 .name = "TCPMSS",
315 .checkentry = tcpmss_tg4_check,
316 .target = tcpmss_tg4,
317 .targetsize = sizeof(struct xt_tcpmss_info),
318 .proto = IPPROTO_TCP,
319 .me = THIS_MODULE,
320 },
321#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
322 {
323 .family = NFPROTO_IPV6,
324 .name = "TCPMSS",
325 .checkentry = tcpmss_tg6_check,
326 .target = tcpmss_tg6,
327 .targetsize = sizeof(struct xt_tcpmss_info),
328 .proto = IPPROTO_TCP,
329 .me = THIS_MODULE,
330 },
331#endif
332};
333
334static int __init tcpmss_tg_init(void)
335{
336 return xt_register_targets(tcpmss_tg_reg, ARRAY_SIZE(tcpmss_tg_reg));
337}
338
339static void __exit tcpmss_tg_exit(void)
340{
341 xt_unregister_targets(tcpmss_tg_reg, ARRAY_SIZE(tcpmss_tg_reg));
342}
343
344module_init(tcpmss_tg_init);
345module_exit(tcpmss_tg_exit);
1/*
2 * This is a module which is used for setting the MSS option in TCP packets.
3 *
4 * Copyright (C) 2000 Marc Boucher <marc@mbsi.ca>
5 *
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.
9 */
10#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
11#include <linux/module.h>
12#include <linux/skbuff.h>
13#include <linux/ip.h>
14#include <linux/gfp.h>
15#include <linux/ipv6.h>
16#include <linux/tcp.h>
17#include <net/dst.h>
18#include <net/flow.h>
19#include <net/ipv6.h>
20#include <net/route.h>
21#include <net/tcp.h>
22
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>
28
29MODULE_LICENSE("GPL");
30MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>");
31MODULE_DESCRIPTION("Xtables: TCP Maximum Segment Size (MSS) adjustment");
32MODULE_ALIAS("ipt_TCPMSS");
33MODULE_ALIAS("ip6t_TCPMSS");
34
35static inline unsigned int
36optlen(const u_int8_t *opt, unsigned int offset)
37{
38 /* Beware zero-length options: make finite progress */
39 if (opt[offset] <= TCPOPT_NOP || opt[offset+1] == 0)
40 return 1;
41 else
42 return opt[offset+1];
43}
44
45static int
46tcpmss_mangle_packet(struct sk_buff *skb,
47 const struct xt_tcpmss_info *info,
48 unsigned int in_mtu,
49 unsigned int tcphoff,
50 unsigned int minlen)
51{
52 struct tcphdr *tcph;
53 unsigned int tcplen, i;
54 __be16 oldval;
55 u16 newmss;
56 u8 *opt;
57
58 if (!skb_make_writable(skb, skb->len))
59 return -1;
60
61 tcplen = skb->len - tcphoff;
62 tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff);
63
64 /* Header cannot be larger than the packet */
65 if (tcplen < tcph->doff*4)
66 return -1;
67
68 if (info->mss == XT_TCPMSS_CLAMP_PMTU) {
69 if (dst_mtu(skb_dst(skb)) <= minlen) {
70 if (net_ratelimit())
71 pr_err("unknown or invalid path-MTU (%u)\n",
72 dst_mtu(skb_dst(skb)));
73 return -1;
74 }
75 if (in_mtu <= minlen) {
76 if (net_ratelimit())
77 pr_err("unknown or invalid path-MTU (%u)\n",
78 in_mtu);
79 return -1;
80 }
81 newmss = min(dst_mtu(skb_dst(skb)), in_mtu) - minlen;
82 } else
83 newmss = info->mss;
84
85 opt = (u_int8_t *)tcph;
86 for (i = sizeof(struct tcphdr); i < tcph->doff*4; i += optlen(opt, i)) {
87 if (opt[i] == TCPOPT_MSS && tcph->doff*4 - i >= TCPOLEN_MSS &&
88 opt[i+1] == TCPOLEN_MSS) {
89 u_int16_t oldmss;
90
91 oldmss = (opt[i+2] << 8) | opt[i+3];
92
93 /* Never increase MSS, even when setting it, as
94 * doing so results in problems for hosts that rely
95 * on MSS being set correctly.
96 */
97 if (oldmss <= newmss)
98 return 0;
99
100 opt[i+2] = (newmss & 0xff00) >> 8;
101 opt[i+3] = newmss & 0x00ff;
102
103 inet_proto_csum_replace2(&tcph->check, skb,
104 htons(oldmss), htons(newmss),
105 0);
106 return 0;
107 }
108 }
109
110 /* There is data after the header so the option can't be added
111 without moving it, and doing so may make the SYN packet
112 itself too large. Accept the packet unmodified instead. */
113 if (tcplen > tcph->doff*4)
114 return 0;
115
116 /*
117 * MSS Option not found ?! add it..
118 */
119 if (skb_tailroom(skb) < TCPOLEN_MSS) {
120 if (pskb_expand_head(skb, 0,
121 TCPOLEN_MSS - skb_tailroom(skb),
122 GFP_ATOMIC))
123 return -1;
124 tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff);
125 }
126
127 skb_put(skb, TCPOLEN_MSS);
128
129 opt = (u_int8_t *)tcph + sizeof(struct tcphdr);
130 memmove(opt + TCPOLEN_MSS, opt, tcplen - sizeof(struct tcphdr));
131
132 inet_proto_csum_replace2(&tcph->check, skb,
133 htons(tcplen), htons(tcplen + TCPOLEN_MSS), 1);
134 opt[0] = TCPOPT_MSS;
135 opt[1] = TCPOLEN_MSS;
136 opt[2] = (newmss & 0xff00) >> 8;
137 opt[3] = newmss & 0x00ff;
138
139 inet_proto_csum_replace4(&tcph->check, skb, 0, *((__be32 *)opt), 0);
140
141 oldval = ((__be16 *)tcph)[6];
142 tcph->doff += TCPOLEN_MSS/4;
143 inet_proto_csum_replace2(&tcph->check, skb,
144 oldval, ((__be16 *)tcph)[6], 0);
145 return TCPOLEN_MSS;
146}
147
148static u_int32_t tcpmss_reverse_mtu(const struct sk_buff *skb,
149 unsigned int family)
150{
151 struct flowi fl;
152 const struct nf_afinfo *ai;
153 struct rtable *rt = NULL;
154 u_int32_t mtu = ~0U;
155
156 if (family == PF_INET) {
157 struct flowi4 *fl4 = &fl.u.ip4;
158 memset(fl4, 0, sizeof(*fl4));
159 fl4->daddr = ip_hdr(skb)->saddr;
160 } else {
161 struct flowi6 *fl6 = &fl.u.ip6;
162
163 memset(fl6, 0, sizeof(*fl6));
164 ipv6_addr_copy(&fl6->daddr, &ipv6_hdr(skb)->saddr);
165 }
166 rcu_read_lock();
167 ai = nf_get_afinfo(family);
168 if (ai != NULL)
169 ai->route(&init_net, (struct dst_entry **)&rt, &fl, false);
170 rcu_read_unlock();
171
172 if (rt != NULL) {
173 mtu = dst_mtu(&rt->dst);
174 dst_release(&rt->dst);
175 }
176 return mtu;
177}
178
179static unsigned int
180tcpmss_tg4(struct sk_buff *skb, const struct xt_action_param *par)
181{
182 struct iphdr *iph = ip_hdr(skb);
183 __be16 newlen;
184 int ret;
185
186 ret = tcpmss_mangle_packet(skb, par->targinfo,
187 tcpmss_reverse_mtu(skb, PF_INET),
188 iph->ihl * 4,
189 sizeof(*iph) + sizeof(struct tcphdr));
190 if (ret < 0)
191 return NF_DROP;
192 if (ret > 0) {
193 iph = ip_hdr(skb);
194 newlen = htons(ntohs(iph->tot_len) + ret);
195 csum_replace2(&iph->check, iph->tot_len, newlen);
196 iph->tot_len = newlen;
197 }
198 return XT_CONTINUE;
199}
200
201#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
202static unsigned int
203tcpmss_tg6(struct sk_buff *skb, const struct xt_action_param *par)
204{
205 struct ipv6hdr *ipv6h = ipv6_hdr(skb);
206 u8 nexthdr;
207 int tcphoff;
208 int ret;
209
210 nexthdr = ipv6h->nexthdr;
211 tcphoff = ipv6_skip_exthdr(skb, sizeof(*ipv6h), &nexthdr);
212 if (tcphoff < 0)
213 return NF_DROP;
214 ret = tcpmss_mangle_packet(skb, par->targinfo,
215 tcpmss_reverse_mtu(skb, PF_INET6),
216 tcphoff,
217 sizeof(*ipv6h) + sizeof(struct tcphdr));
218 if (ret < 0)
219 return NF_DROP;
220 if (ret > 0) {
221 ipv6h = ipv6_hdr(skb);
222 ipv6h->payload_len = htons(ntohs(ipv6h->payload_len) + ret);
223 }
224 return XT_CONTINUE;
225}
226#endif
227
228/* Must specify -p tcp --syn */
229static inline bool find_syn_match(const struct xt_entry_match *m)
230{
231 const struct xt_tcp *tcpinfo = (const struct xt_tcp *)m->data;
232
233 if (strcmp(m->u.kernel.match->name, "tcp") == 0 &&
234 tcpinfo->flg_cmp & TCPHDR_SYN &&
235 !(tcpinfo->invflags & XT_TCP_INV_FLAGS))
236 return true;
237
238 return false;
239}
240
241static int tcpmss_tg4_check(const struct xt_tgchk_param *par)
242{
243 const struct xt_tcpmss_info *info = par->targinfo;
244 const struct ipt_entry *e = par->entryinfo;
245 const struct xt_entry_match *ematch;
246
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");
253 return -EINVAL;
254 }
255 xt_ematch_foreach(ematch, e)
256 if (find_syn_match(ematch))
257 return 0;
258 pr_info("Only works on TCP SYN packets\n");
259 return -EINVAL;
260}
261
262#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
263static int tcpmss_tg6_check(const struct xt_tgchk_param *par)
264{
265 const struct xt_tcpmss_info *info = par->targinfo;
266 const struct ip6t_entry *e = par->entryinfo;
267 const struct xt_entry_match *ematch;
268
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");
275 return -EINVAL;
276 }
277 xt_ematch_foreach(ematch, e)
278 if (find_syn_match(ematch))
279 return 0;
280 pr_info("Only works on TCP SYN packets\n");
281 return -EINVAL;
282}
283#endif
284
285static struct xt_target tcpmss_tg_reg[] __read_mostly = {
286 {
287 .family = NFPROTO_IPV4,
288 .name = "TCPMSS",
289 .checkentry = tcpmss_tg4_check,
290 .target = tcpmss_tg4,
291 .targetsize = sizeof(struct xt_tcpmss_info),
292 .proto = IPPROTO_TCP,
293 .me = THIS_MODULE,
294 },
295#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE)
296 {
297 .family = NFPROTO_IPV6,
298 .name = "TCPMSS",
299 .checkentry = tcpmss_tg6_check,
300 .target = tcpmss_tg6,
301 .targetsize = sizeof(struct xt_tcpmss_info),
302 .proto = IPPROTO_TCP,
303 .me = THIS_MODULE,
304 },
305#endif
306};
307
308static int __init tcpmss_tg_init(void)
309{
310 return xt_register_targets(tcpmss_tg_reg, ARRAY_SIZE(tcpmss_tg_reg));
311}
312
313static void __exit tcpmss_tg_exit(void)
314{
315 xt_unregister_targets(tcpmss_tg_reg, ARRAY_SIZE(tcpmss_tg_reg));
316}
317
318module_init(tcpmss_tg_init);
319module_exit(tcpmss_tg_exit);