/kern_oII/net/netfilter/xt_tcpmss.c

http://omnia2droid.googlecode.com/ · C · 314 lines · 251 code · 40 blank · 23 comment · 45 complexity · 3f4558120621777d4cef10b6fa170173 MD5 · raw file

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