Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.25-rc9 340 lines 8.9 kB view raw
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 11#include <linux/module.h> 12#include <linux/skbuff.h> 13#include <linux/ip.h> 14#include <linux/ipv6.h> 15#include <linux/tcp.h> 16#include <net/dst.h> 17#include <net/flow.h> 18#include <net/ipv6.h> 19#include <net/route.h> 20#include <net/tcp.h> 21 22#include <linux/netfilter_ipv4/ip_tables.h> 23#include <linux/netfilter_ipv6/ip6_tables.h> 24#include <linux/netfilter/x_tables.h> 25#include <linux/netfilter/xt_tcpudp.h> 26#include <linux/netfilter/xt_TCPMSS.h> 27 28MODULE_LICENSE("GPL"); 29MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>"); 30MODULE_DESCRIPTION("Xtables: TCP Maximum Segment Size (MSS) adjustment"); 31MODULE_ALIAS("ipt_TCPMSS"); 32MODULE_ALIAS("ip6t_TCPMSS"); 33 34static inline unsigned int 35optlen(const u_int8_t *opt, unsigned int offset) 36{ 37 /* Beware zero-length options: make finite progress */ 38 if (opt[offset] <= TCPOPT_NOP || opt[offset+1] == 0) 39 return 1; 40 else 41 return opt[offset+1]; 42} 43 44static int 45tcpmss_mangle_packet(struct sk_buff *skb, 46 const struct xt_tcpmss_info *info, 47 unsigned int in_mtu, 48 unsigned int tcphoff, 49 unsigned int minlen) 50{ 51 struct tcphdr *tcph; 52 unsigned int tcplen, i; 53 __be16 oldval; 54 u16 newmss; 55 u8 *opt; 56 57 if (!skb_make_writable(skb, skb->len)) 58 return -1; 59 60 tcplen = skb->len - tcphoff; 61 tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff); 62 63 /* Since it passed flags test in tcp match, we know it is is 64 not a fragment, and has data >= tcp header length. SYN 65 packets should not contain data: if they did, then we risk 66 running over MTU, sending Frag Needed and breaking things 67 badly. --RR */ 68 if (tcplen != tcph->doff*4) { 69 if (net_ratelimit()) 70 printk(KERN_ERR "xt_TCPMSS: bad length (%u bytes)\n", 71 skb->len); 72 return -1; 73 } 74 75 if (info->mss == XT_TCPMSS_CLAMP_PMTU) { 76 if (dst_mtu(skb->dst) <= minlen) { 77 if (net_ratelimit()) 78 printk(KERN_ERR "xt_TCPMSS: " 79 "unknown or invalid path-MTU (%u)\n", 80 dst_mtu(skb->dst)); 81 return -1; 82 } 83 if (in_mtu <= minlen) { 84 if (net_ratelimit()) 85 printk(KERN_ERR "xt_TCPMSS: unknown or " 86 "invalid path-MTU (%u)\n", in_mtu); 87 return -1; 88 } 89 newmss = min(dst_mtu(skb->dst), in_mtu) - minlen; 90 } else 91 newmss = info->mss; 92 93 opt = (u_int8_t *)tcph; 94 for (i = sizeof(struct tcphdr); i < tcph->doff*4; i += optlen(opt, i)) { 95 if (opt[i] == TCPOPT_MSS && tcph->doff*4 - i >= TCPOLEN_MSS && 96 opt[i+1] == TCPOLEN_MSS) { 97 u_int16_t oldmss; 98 99 oldmss = (opt[i+2] << 8) | opt[i+3]; 100 101 /* Never increase MSS, even when setting it, as 102 * doing so results in problems for hosts that rely 103 * on MSS being set correctly. 104 */ 105 if (oldmss <= newmss) 106 return 0; 107 108 opt[i+2] = (newmss & 0xff00) >> 8; 109 opt[i+3] = newmss & 0x00ff; 110 111 inet_proto_csum_replace2(&tcph->check, skb, 112 htons(oldmss), htons(newmss), 113 0); 114 return 0; 115 } 116 } 117 118 /* 119 * MSS Option not found ?! add it.. 120 */ 121 if (skb_tailroom(skb) < TCPOLEN_MSS) { 122 if (pskb_expand_head(skb, 0, 123 TCPOLEN_MSS - skb_tailroom(skb), 124 GFP_ATOMIC)) 125 return -1; 126 tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff); 127 } 128 129 skb_put(skb, TCPOLEN_MSS); 130 131 opt = (u_int8_t *)tcph + sizeof(struct tcphdr); 132 memmove(opt + TCPOLEN_MSS, opt, tcplen - sizeof(struct tcphdr)); 133 134 inet_proto_csum_replace2(&tcph->check, skb, 135 htons(tcplen), htons(tcplen + TCPOLEN_MSS), 1); 136 opt[0] = TCPOPT_MSS; 137 opt[1] = TCPOLEN_MSS; 138 opt[2] = (newmss & 0xff00) >> 8; 139 opt[3] = newmss & 0x00ff; 140 141 inet_proto_csum_replace4(&tcph->check, skb, 0, *((__be32 *)opt), 0); 142 143 oldval = ((__be16 *)tcph)[6]; 144 tcph->doff += TCPOLEN_MSS/4; 145 inet_proto_csum_replace2(&tcph->check, skb, 146 oldval, ((__be16 *)tcph)[6], 0); 147 return TCPOLEN_MSS; 148} 149 150static u_int32_t tcpmss_reverse_mtu4(const struct iphdr *iph) 151{ 152 struct flowi fl = { 153 .fl4_dst = iph->saddr, 154 }; 155 const struct nf_afinfo *ai; 156 struct rtable *rt = NULL; 157 u_int32_t mtu = ~0U; 158 159 rcu_read_lock(); 160 ai = nf_get_afinfo(AF_INET); 161 if (ai != NULL) 162 ai->route((struct dst_entry **)&rt, &fl); 163 rcu_read_unlock(); 164 165 if (rt != NULL) { 166 mtu = dst_mtu(&rt->u.dst); 167 dst_release(&rt->u.dst); 168 } 169 return mtu; 170} 171 172static unsigned int 173tcpmss_tg4(struct sk_buff *skb, const struct net_device *in, 174 const struct net_device *out, unsigned int hooknum, 175 const struct xt_target *target, const void *targinfo) 176{ 177 struct iphdr *iph = ip_hdr(skb); 178 __be16 newlen; 179 int ret; 180 181 ret = tcpmss_mangle_packet(skb, targinfo, tcpmss_reverse_mtu4(iph), 182 iph->ihl * 4, 183 sizeof(*iph) + sizeof(struct tcphdr)); 184 if (ret < 0) 185 return NF_DROP; 186 if (ret > 0) { 187 iph = ip_hdr(skb); 188 newlen = htons(ntohs(iph->tot_len) + ret); 189 csum_replace2(&iph->check, iph->tot_len, newlen); 190 iph->tot_len = newlen; 191 } 192 return XT_CONTINUE; 193} 194 195#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) 196static u_int32_t tcpmss_reverse_mtu6(const struct ipv6hdr *iph) 197{ 198 struct flowi fl = { 199 .fl6_dst = iph->saddr, 200 }; 201 const struct nf_afinfo *ai; 202 struct rtable *rt = NULL; 203 u_int32_t mtu = ~0U; 204 205 rcu_read_lock(); 206 ai = nf_get_afinfo(AF_INET6); 207 if (ai != NULL) 208 ai->route((struct dst_entry **)&rt, &fl); 209 rcu_read_unlock(); 210 211 if (rt != NULL) { 212 mtu = dst_mtu(&rt->u.dst); 213 dst_release(&rt->u.dst); 214 } 215 return mtu; 216} 217 218static unsigned int 219tcpmss_tg6(struct sk_buff *skb, const struct net_device *in, 220 const struct net_device *out, unsigned int hooknum, 221 const struct xt_target *target, const void *targinfo) 222{ 223 struct ipv6hdr *ipv6h = ipv6_hdr(skb); 224 u8 nexthdr; 225 int tcphoff; 226 int ret; 227 228 nexthdr = ipv6h->nexthdr; 229 tcphoff = ipv6_skip_exthdr(skb, sizeof(*ipv6h), &nexthdr); 230 if (tcphoff < 0) 231 return NF_DROP; 232 ret = tcpmss_mangle_packet(skb, targinfo, tcpmss_reverse_mtu6(ipv6h), 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 ipv6h->payload_len = htons(ntohs(ipv6h->payload_len) + ret); 240 } 241 return XT_CONTINUE; 242} 243#endif 244 245#define TH_SYN 0x02 246 247/* Must specify -p tcp --syn */ 248static inline bool find_syn_match(const struct xt_entry_match *m) 249{ 250 const struct xt_tcp *tcpinfo = (const struct xt_tcp *)m->data; 251 252 if (strcmp(m->u.kernel.match->name, "tcp") == 0 && 253 tcpinfo->flg_cmp & TH_SYN && 254 !(tcpinfo->invflags & XT_TCP_INV_FLAGS)) 255 return true; 256 257 return false; 258} 259 260static bool 261tcpmss_tg4_check(const char *tablename, const void *entry, 262 const struct xt_target *target, void *targinfo, 263 unsigned int hook_mask) 264{ 265 const struct xt_tcpmss_info *info = targinfo; 266 const struct ipt_entry *e = entry; 267 268 if (info->mss == XT_TCPMSS_CLAMP_PMTU && 269 (hook_mask & ~((1 << NF_INET_FORWARD) | 270 (1 << NF_INET_LOCAL_OUT) | 271 (1 << NF_INET_POST_ROUTING))) != 0) { 272 printk("xt_TCPMSS: path-MTU clamping only supported in " 273 "FORWARD, OUTPUT and POSTROUTING hooks\n"); 274 return false; 275 } 276 if (IPT_MATCH_ITERATE(e, find_syn_match)) 277 return true; 278 printk("xt_TCPMSS: Only works on TCP SYN packets\n"); 279 return false; 280} 281 282#if defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) 283static bool 284tcpmss_tg6_check(const char *tablename, const void *entry, 285 const struct xt_target *target, void *targinfo, 286 unsigned int hook_mask) 287{ 288 const struct xt_tcpmss_info *info = targinfo; 289 const struct ip6t_entry *e = entry; 290 291 if (info->mss == XT_TCPMSS_CLAMP_PMTU && 292 (hook_mask & ~((1 << NF_INET_FORWARD) | 293 (1 << NF_INET_LOCAL_OUT) | 294 (1 << NF_INET_POST_ROUTING))) != 0) { 295 printk("xt_TCPMSS: path-MTU clamping only supported in " 296 "FORWARD, OUTPUT and POSTROUTING hooks\n"); 297 return false; 298 } 299 if (IP6T_MATCH_ITERATE(e, find_syn_match)) 300 return true; 301 printk("xt_TCPMSS: Only works on TCP SYN packets\n"); 302 return false; 303} 304#endif 305 306static struct xt_target tcpmss_tg_reg[] __read_mostly = { 307 { 308 .family = AF_INET, 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 defined(CONFIG_IP6_NF_IPTABLES) || defined(CONFIG_IP6_NF_IPTABLES_MODULE) 317 { 318 .family = AF_INET6, 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 329static int __init tcpmss_tg_init(void) 330{ 331 return xt_register_targets(tcpmss_tg_reg, ARRAY_SIZE(tcpmss_tg_reg)); 332} 333 334static void __exit tcpmss_tg_exit(void) 335{ 336 xt_unregister_targets(tcpmss_tg_reg, ARRAY_SIZE(tcpmss_tg_reg)); 337} 338 339module_init(tcpmss_tg_init); 340module_exit(tcpmss_tg_exit);