drivers: power: report battery voltage in AOSP compatible format
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / net / netfilter / xt_TCPMSS.c
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
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");
35
36 static inline unsigned int
37 optlen(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
46 static int
47 tcpmss_mangle_packet(struct sk_buff *skb,
48 const struct xt_action_param *par,
49 unsigned int in_mtu,
50 unsigned int tcphoff,
51 unsigned int minlen)
52 {
53 const struct xt_tcpmss_info *info = par->targinfo;
54 struct tcphdr *tcph;
55 unsigned int tcplen, i;
56 __be16 oldval;
57 u16 newmss;
58 u8 *opt;
59
60 /* This is a fragment, no TCP header is available */
61 if (par->fragoff != 0)
62 return XT_CONTINUE;
63
64 if (!skb_make_writable(skb, skb->len))
65 return -1;
66
67 tcplen = skb->len - tcphoff;
68 tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff);
69
70 /* Header cannot be larger than the packet */
71 if (tcplen < tcph->doff*4 || tcph->doff*4 < sizeof(struct tcphdr))
72 return -1;
73
74 if (info->mss == XT_TCPMSS_CLAMP_PMTU) {
75 if (dst_mtu(skb_dst(skb)) <= minlen) {
76 net_err_ratelimited("unknown or invalid path-MTU (%u)\n",
77 dst_mtu(skb_dst(skb)));
78 return -1;
79 }
80 if (in_mtu <= minlen) {
81 net_err_ratelimited("unknown or invalid path-MTU (%u)\n",
82 in_mtu);
83 return -1;
84 }
85 newmss = min(dst_mtu(skb_dst(skb)), in_mtu) - minlen;
86 } else
87 newmss = info->mss;
88
89 opt = (u_int8_t *)tcph;
90 for (i = sizeof(struct tcphdr); i < tcph->doff*4; i += optlen(opt, i)) {
91 if (opt[i] == TCPOPT_MSS && tcph->doff*4 - i >= TCPOLEN_MSS &&
92 opt[i+1] == TCPOLEN_MSS) {
93 u_int16_t oldmss;
94
95 oldmss = (opt[i+2] << 8) | opt[i+3];
96
97 /* Never increase MSS, even when setting it, as
98 * doing so results in problems for hosts that rely
99 * on MSS being set correctly.
100 */
101 if (oldmss <= newmss)
102 return 0;
103
104 opt[i+2] = (newmss & 0xff00) >> 8;
105 opt[i+3] = newmss & 0x00ff;
106
107 inet_proto_csum_replace2(&tcph->check, skb,
108 htons(oldmss), htons(newmss),
109 0);
110 return 0;
111 }
112 }
113
114 /* There is data after the header so the option can't be added
115 without moving it, and doing so may make the SYN packet
116 itself too large. Accept the packet unmodified instead. */
117 if (tcplen > tcph->doff*4)
118 return 0;
119
120 /* tcph->doff has 4 bits, do not wrap it to 0 */
121 if (tcph->doff >= 15)
122 return 0;
123
124 /*
125 * MSS Option not found ?! add it..
126 */
127 if (skb_tailroom(skb) < TCPOLEN_MSS) {
128 if (pskb_expand_head(skb, 0,
129 TCPOLEN_MSS - skb_tailroom(skb),
130 GFP_ATOMIC))
131 return -1;
132 tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff);
133 }
134
135 skb_put(skb, TCPOLEN_MSS);
136
137 /*
138 * IPv4: RFC 1122 states "If an MSS option is not received at
139 * connection setup, TCP MUST assume a default send MSS of 536".
140 * IPv6: RFC 2460 states IPv6 has a minimum MTU of 1280 and a minimum
141 * length IPv6 header of 60, ergo the default MSS value is 1220
142 * Since no MSS was provided, we must use the default values
143 */
144 if (par->family == NFPROTO_IPV4)
145 newmss = min(newmss, (u16)536);
146 else
147 newmss = min(newmss, (u16)1220);
148
149 opt = (u_int8_t *)tcph + sizeof(struct tcphdr);
150 memmove(opt + TCPOLEN_MSS, opt, tcplen - sizeof(struct tcphdr));
151
152 inet_proto_csum_replace2(&tcph->check, skb,
153 htons(tcplen), htons(tcplen + TCPOLEN_MSS), 1);
154 opt[0] = TCPOPT_MSS;
155 opt[1] = TCPOLEN_MSS;
156 opt[2] = (newmss & 0xff00) >> 8;
157 opt[3] = newmss & 0x00ff;
158
159 inet_proto_csum_replace4(&tcph->check, skb, 0, *((__be32 *)opt), 0);
160
161 oldval = ((__be16 *)tcph)[6];
162 tcph->doff += TCPOLEN_MSS/4;
163 inet_proto_csum_replace2(&tcph->check, skb,
164 oldval, ((__be16 *)tcph)[6], 0);
165 return TCPOLEN_MSS;
166 }
167
168 static u_int32_t tcpmss_reverse_mtu(const struct sk_buff *skb,
169 unsigned int family)
170 {
171 struct flowi fl;
172 const struct nf_afinfo *ai;
173 struct rtable *rt = NULL;
174 u_int32_t mtu = ~0U;
175
176 if (family == PF_INET) {
177 struct flowi4 *fl4 = &fl.u.ip4;
178 memset(fl4, 0, sizeof(*fl4));
179 fl4->daddr = ip_hdr(skb)->saddr;
180 } else {
181 struct flowi6 *fl6 = &fl.u.ip6;
182
183 memset(fl6, 0, sizeof(*fl6));
184 fl6->daddr = ipv6_hdr(skb)->saddr;
185 }
186 rcu_read_lock();
187 ai = nf_get_afinfo(family);
188 if (ai != NULL)
189 ai->route(&init_net, (struct dst_entry **)&rt, &fl, false);
190 rcu_read_unlock();
191
192 if (rt != NULL) {
193 mtu = dst_mtu(&rt->dst);
194 dst_release(&rt->dst);
195 }
196 return mtu;
197 }
198
199 static unsigned int
200 tcpmss_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 tcpmss_reverse_mtu(skb, 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)
222 static unsigned int
223 tcpmss_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;
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 tcpmss_reverse_mtu(skb, 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 ipv6h->payload_len = htons(ntohs(ipv6h->payload_len) + ret);
244 }
245 return XT_CONTINUE;
246 }
247 #endif
248
249 /* Must specify -p tcp --syn */
250 static inline bool find_syn_match(const struct xt_entry_match *m)
251 {
252 const struct xt_tcp *tcpinfo = (const struct xt_tcp *)m->data;
253
254 if (strcmp(m->u.kernel.match->name, "tcp") == 0 &&
255 tcpinfo->flg_cmp & TCPHDR_SYN &&
256 !(tcpinfo->invflags & XT_TCP_INV_FLAGS))
257 return true;
258
259 return false;
260 }
261
262 static int tcpmss_tg4_check(const struct xt_tgchk_param *par)
263 {
264 const struct xt_tcpmss_info *info = par->targinfo;
265 const struct ipt_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
283 #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
284 static int tcpmss_tg6_check(const struct xt_tgchk_param *par)
285 {
286 const struct xt_tcpmss_info *info = par->targinfo;
287 const struct ip6t_entry *e = par->entryinfo;
288 const struct xt_entry_match *ematch;
289
290 if (info->mss == XT_TCPMSS_CLAMP_PMTU &&
291 (par->hook_mask & ~((1 << NF_INET_FORWARD) |
292 (1 << NF_INET_LOCAL_OUT) |
293 (1 << NF_INET_POST_ROUTING))) != 0) {
294 pr_info("path-MTU clamping only supported in "
295 "FORWARD, OUTPUT and POSTROUTING hooks\n");
296 return -EINVAL;
297 }
298 xt_ematch_foreach(ematch, e)
299 if (find_syn_match(ematch))
300 return 0;
301 pr_info("Only works on TCP SYN packets\n");
302 return -EINVAL;
303 }
304 #endif
305
306 static struct xt_target tcpmss_tg_reg[] __read_mostly = {
307 {
308 .family = NFPROTO_IPV4,
309 .name = "TCPMSS",
310 .checkentry = tcpmss_tg4_check,
311 .target = tcpmss_tg4,
312 .targetsize = sizeof(struct xt_tcpmss_info),
313 .proto = IPPROTO_TCP,
314 .me = THIS_MODULE,
315 },
316 #if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
317 {
318 .family = NFPROTO_IPV6,
319 .name = "TCPMSS",
320 .checkentry = tcpmss_tg6_check,
321 .target = tcpmss_tg6,
322 .targetsize = sizeof(struct xt_tcpmss_info),
323 .proto = IPPROTO_TCP,
324 .me = THIS_MODULE,
325 },
326 #endif
327 };
328
329 static int __init tcpmss_tg_init(void)
330 {
331 return xt_register_targets(tcpmss_tg_reg, ARRAY_SIZE(tcpmss_tg_reg));
332 }
333
334 static void __exit tcpmss_tg_exit(void)
335 {
336 xt_unregister_targets(tcpmss_tg_reg, ARRAY_SIZE(tcpmss_tg_reg));
337 }
338
339 module_init(tcpmss_tg_init);
340 module_exit(tcpmss_tg_exit);