Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* Kernel module to match TCP MSS values. */ |
2 | ||
3 | /* Copyright (C) 2000 Marc Boucher <marc@mbsi.ca> | |
2e4e6a17 | 4 | * Portions (C) 2005 by Harald Welte <laforge@netfilter.org> |
1da177e4 LT |
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 | ||
11 | #include <linux/module.h> | |
12 | #include <linux/skbuff.h> | |
13 | #include <net/tcp.h> | |
14 | ||
2e4e6a17 HW |
15 | #include <linux/netfilter/xt_tcpmss.h> |
16 | #include <linux/netfilter/x_tables.h> | |
17 | ||
1da177e4 | 18 | #include <linux/netfilter_ipv4/ip_tables.h> |
2e4e6a17 | 19 | #include <linux/netfilter_ipv6/ip6_tables.h> |
1da177e4 LT |
20 | |
21 | #define TH_SYN 0x02 | |
22 | ||
23 | MODULE_LICENSE("GPL"); | |
24 | MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>"); | |
25 | MODULE_DESCRIPTION("iptables TCP MSS match module"); | |
2e4e6a17 | 26 | MODULE_ALIAS("ipt_tcpmss"); |
1da177e4 LT |
27 | |
28 | /* Returns 1 if the mss option is set and matched by the range, 0 otherwise */ | |
29 | static inline int | |
30 | mssoption_match(u_int16_t min, u_int16_t max, | |
31 | const struct sk_buff *skb, | |
2e4e6a17 | 32 | unsigned int protoff, |
1da177e4 LT |
33 | int invert, |
34 | int *hotdrop) | |
35 | { | |
36 | struct tcphdr _tcph, *th; | |
37 | /* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */ | |
38 | u8 _opt[15 * 4 - sizeof(_tcph)], *op; | |
39 | unsigned int i, optlen; | |
40 | ||
41 | /* If we don't have the whole header, drop packet. */ | |
2e4e6a17 | 42 | th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph); |
1da177e4 LT |
43 | if (th == NULL) |
44 | goto dropit; | |
45 | ||
46 | /* Malformed. */ | |
47 | if (th->doff*4 < sizeof(*th)) | |
48 | goto dropit; | |
49 | ||
50 | optlen = th->doff*4 - sizeof(*th); | |
51 | if (!optlen) | |
52 | goto out; | |
53 | ||
54 | /* Truncated options. */ | |
2e4e6a17 | 55 | op = skb_header_pointer(skb, protoff + sizeof(*th), optlen, _opt); |
1da177e4 LT |
56 | if (op == NULL) |
57 | goto dropit; | |
58 | ||
59 | for (i = 0; i < optlen; ) { | |
60 | if (op[i] == TCPOPT_MSS | |
61 | && (optlen - i) >= TCPOLEN_MSS | |
62 | && op[i+1] == TCPOLEN_MSS) { | |
63 | u_int16_t mssval; | |
64 | ||
65 | mssval = (op[i+2] << 8) | op[i+3]; | |
66 | ||
67 | return (mssval >= min && mssval <= max) ^ invert; | |
68 | } | |
69 | if (op[i] < 2) i++; | |
70 | else i += op[i+1]?:1; | |
71 | } | |
72 | out: | |
73 | return invert; | |
74 | ||
75 | dropit: | |
76 | *hotdrop = 1; | |
77 | return 0; | |
78 | } | |
79 | ||
80 | static int | |
81 | match(const struct sk_buff *skb, | |
82 | const struct net_device *in, | |
83 | const struct net_device *out, | |
c4986734 | 84 | const struct xt_match *match, |
1da177e4 LT |
85 | const void *matchinfo, |
86 | int offset, | |
2e4e6a17 | 87 | unsigned int protoff, |
1da177e4 LT |
88 | int *hotdrop) |
89 | { | |
2e4e6a17 | 90 | const struct xt_tcpmss_match_info *info = matchinfo; |
1da177e4 | 91 | |
2e4e6a17 | 92 | return mssoption_match(info->mss_min, info->mss_max, skb, protoff, |
1da177e4 LT |
93 | info->invert, hotdrop); |
94 | } | |
95 | ||
2e4e6a17 | 96 | static struct xt_match tcpmss_match = { |
1da177e4 | 97 | .name = "tcpmss", |
5d04bff0 PM |
98 | .match = match, |
99 | .matchsize = sizeof(struct xt_tcpmss_match_info), | |
100 | .proto = IPPROTO_TCP, | |
a45049c5 | 101 | .family = AF_INET, |
1da177e4 LT |
102 | .me = THIS_MODULE, |
103 | }; | |
104 | ||
2e4e6a17 HW |
105 | static struct xt_match tcpmss6_match = { |
106 | .name = "tcpmss", | |
5d04bff0 PM |
107 | .match = match, |
108 | .matchsize = sizeof(struct xt_tcpmss_match_info), | |
109 | .proto = IPPROTO_TCP, | |
a45049c5 | 110 | .family = AF_INET6, |
2e4e6a17 HW |
111 | .me = THIS_MODULE, |
112 | }; | |
113 | ||
114 | ||
1da177e4 LT |
115 | static int __init init(void) |
116 | { | |
2e4e6a17 | 117 | int ret; |
a45049c5 | 118 | ret = xt_register_match(&tcpmss_match); |
2e4e6a17 HW |
119 | if (ret) |
120 | return ret; | |
121 | ||
a45049c5 | 122 | ret = xt_register_match(&tcpmss6_match); |
2e4e6a17 | 123 | if (ret) |
a45049c5 | 124 | xt_unregister_match(&tcpmss_match); |
2e4e6a17 HW |
125 | |
126 | return ret; | |
1da177e4 LT |
127 | } |
128 | ||
129 | static void __exit fini(void) | |
130 | { | |
a45049c5 PNA |
131 | xt_unregister_match(&tcpmss6_match); |
132 | xt_unregister_match(&tcpmss_match); | |
1da177e4 LT |
133 | } |
134 | ||
135 | module_init(init); | |
136 | module_exit(fini); |