/* * A module for stripping a specific TCP option from TCP packets. * * Copyright (C) 2007 Sven Schnelle * Copyright © CC Computer Consultants GmbH, 2007 * Contact: Jan Engelhardt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include static inline unsigned int optlen(const u_int8_t *opt, unsigned int offset) { /* Beware zero-length options: make finite progress */ if (opt[offset] <= TCPOPT_NOP || opt[offset+1] == 0) return 1; else return opt[offset+1]; } static u_int16_t cheat_check(u_int32_t oldvalinv, u_int32_t newval, u_int16_t oldcheck) { u_int32_t diffs[] = { oldvalinv, newval }; return csum_fold(csum_partial((char *)diffs, sizeof(diffs), oldcheck^0xFFFF)); } static unsigned int tcpoptstrip_mangle_packet(struct sk_buff **pskb, const struct xt_tcpoptstrip_target_info *info, unsigned int tcphoff, unsigned int minlen) { unsigned int optl, i, j; struct tcphdr *tcph; u_int16_t n, o; u_int8_t *opt; if (!skb_make_writable(pskb, (*pskb)->len)) return NF_DROP; tcph = (struct tcphdr *)(skb_network_header(*pskb) + tcphoff); opt = (u_int8_t *)tcph; /* * Walk through all TCP options - if we find some option to remove, * set all octets to %TCPOPT_NOP and adjust checksum. */ for (i = sizeof(struct tcphdr); i < tcp_hdrlen(*pskb); i += optl) { optl = optlen(opt, i); if (i + optl > tcp_hdrlen(*pskb)) break; if (!tcpoptstrip_test_bit(info->strip_bmap, opt[i])) continue; for (j = 0; j < optl; ++j) { o = opt[i+j]; n = TCPOPT_NOP; if ((i + j) % 2 == 0) { o <<= 8; n <<= 8; } tcph->check = cheat_check(htons(o)^0xFFFF, htons(n), tcph->check); } memset(opt + i, TCPOPT_NOP, optl); } return XT_CONTINUE; } static unsigned int tcpoptstrip_tg4(struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, unsigned int hooknum, const struct xt_target *target, const void *targinfo, void *userinfo) { return tcpoptstrip_mangle_packet(pskb, targinfo, ip_hdrlen(*pskb), sizeof(struct iphdr) + sizeof(struct tcphdr)); } #if defined(CONFIG_IP6_NF_MANGLE) || defined(CONFIG_IP6_NF_MANGLE_MODULE) static unsigned int tcpoptstrip_tg6(struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, unsigned int hooknum, const struct xt_target *target, const void *targinfo, void *userinfo) { struct ipv6hdr *ipv6h = ipv6_hdr(*pskb); unsigned int tcphoff; u_int8_t nexthdr; nexthdr = ipv6h->nexthdr; tcphoff = ipv6_skip_exthdr(*pskb, sizeof(*ipv6h), &nexthdr); if (tcphoff < 0) return NF_DROP; return tcpoptstrip_mangle_packet(pskb, targinfo, tcphoff, sizeof(*ipv6h) + sizeof(struct tcphdr)); } #endif static struct xt_target tcpoptstrip_tg4_reg = { .name = "TCPOPTSTRIP", .family = AF_INET, .table = "mangle", .proto = IPPROTO_TCP, .target = tcpoptstrip_tg4, .targetsize = sizeof(struct xt_tcpoptstrip_target_info), .me = THIS_MODULE, }; #if defined(CONFIG_IP6_NF_MANGLE) || defined(CONFIG_IP6_NF_MANGLE_MODULE) static struct xt_target tcpoptstrip_tg6_reg = { .name = "TCPOPTSTRIP", .family = AF_INET6, .table = "mangle", .proto = IPPROTO_TCP, .target = tcpoptstrip_tg6, .targetsize = sizeof(struct xt_tcpoptstrip_target_info), .me = THIS_MODULE, }; #endif static int __init tcpoptstrip_tg_init(void) { int ret; ret = xt_register_target(&tcpoptstrip_tg4_reg); #if defined(CONFIG_IP6_NF_MANGLE) || defined(CONFIG_IP6_NF_MANGLE_MODULE) if (ret) { xt_unregister_target(&tcpoptstrip_tg4_reg); return ret; } ret = xt_register_target(&tcpoptstrip_tg6_reg); #endif return ret; } static void __exit tcpoptstrip_tg_exit(void) { xt_unregister_target(&tcpoptstrip_tg4_reg); #if defined(CONFIG_IP6_NF_MANGLE) || defined(CONFIG_IP6_NF_MANGLE_MODULE) xt_unregister_target(&tcpoptstrip_tg6_reg); #endif } module_init(tcpoptstrip_tg_init); module_exit(tcpoptstrip_tg_exit); MODULE_AUTHOR("Sven Schnelle , Jan Engelhardt "); MODULE_DESCRIPTION("Xtables: TCP option stripping"); MODULE_LICENSE("GPL"); MODULE_ALIAS("ipt_TCPOPTSTRIP"); MODULE_ALIAS("ip6t_TCPOPTSTRIP");