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