1/* 2 * A module for stripping a specific TCP option from TCP packets. 3 * 4 * Copyright (C) 2007 Sven Schnelle <svens@bitebene.org> 5 * Copyright �� CC Computer Consultants GmbH, 2007 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 12#include <linux/module.h> 13#include <linux/skbuff.h> 14#include <linux/ip.h> 15#include <linux/ipv6.h> 16#include <linux/tcp.h> 17#include <net/ipv6.h> 18#include <net/tcp.h> 19#include <linux/netfilter/x_tables.h> 20#include <linux/netfilter/xt_TCPOPTSTRIP.h> 21 22static inline unsigned int optlen(const u_int8_t *opt, unsigned int offset) 23{ 24 /* Beware zero-length options: make finite progress */ 25 if (opt[offset] <= TCPOPT_NOP || opt[offset+1] == 0) 26 return 1; 27 else 28 return opt[offset+1]; 29} 30 31static unsigned int 32tcpoptstrip_mangle_packet(struct sk_buff *skb, 33 const struct xt_tcpoptstrip_target_info *info, 34 unsigned int tcphoff, unsigned int minlen) 35{ 36 unsigned int optl, i, j; 37 struct tcphdr *tcph; 38 u_int16_t n, o; 39 u_int8_t *opt; 40 41 if (!skb_make_writable(skb, skb->len)) 42 return NF_DROP; 43 44 tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff); 45 opt = (u_int8_t *)tcph; 46 47 /* 48 * Walk through all TCP options - if we find some option to remove, 49 * set all octets to %TCPOPT_NOP and adjust checksum. 50 */ 51 for (i = sizeof(struct tcphdr); i < tcp_hdrlen(skb); i += optl) { 52 optl = optlen(opt, i); 53 54 if (i + optl > tcp_hdrlen(skb)) 55 break; 56 57 if (!tcpoptstrip_test_bit(info->strip_bmap, opt[i])) 58 continue; 59 60 for (j = 0; j < optl; ++j) { 61 o = opt[i+j]; 62 n = TCPOPT_NOP; 63 if ((i + j) % 2 == 0) { 64 o <<= 8; 65 n <<= 8; 66 } 67 inet_proto_csum_replace2(&tcph->check, skb, htons(o), 68 htons(n), 0); 69 } 70 memset(opt + i, TCPOPT_NOP, optl); 71 } 72 73 return XT_CONTINUE; 74} 75 76static unsigned int 77tcpoptstrip_tg4(struct sk_buff *skb, const struct xt_action_param *par) 78{ 79 return tcpoptstrip_mangle_packet(skb, par->targinfo, ip_hdrlen(skb), 80 sizeof(struct iphdr) + sizeof(struct tcphdr)); 81} 82 83#if defined(CONFIG_IP6_NF_MANGLE) || defined(CONFIG_IP6_NF_MANGLE_MODULE) 84static unsigned int 85tcpoptstrip_tg6(struct sk_buff *skb, const struct xt_action_param *par) 86{ 87 struct ipv6hdr *ipv6h = ipv6_hdr(skb); 88 int tcphoff; 89 u_int8_t nexthdr; 90 91 nexthdr = ipv6h->nexthdr; 92 tcphoff = ipv6_skip_exthdr(skb, sizeof(*ipv6h), &nexthdr); 93 if (tcphoff < 0) 94 return NF_DROP; 95 96 return tcpoptstrip_mangle_packet(skb, par->targinfo, tcphoff, 97 sizeof(*ipv6h) + sizeof(struct tcphdr)); 98} 99#endif 100 101static struct xt_target tcpoptstrip_tg_reg[] __read_mostly = { 102 { 103 .name = "TCPOPTSTRIP", 104 .family = NFPROTO_IPV4, 105 .table = "mangle", 106 .proto = IPPROTO_TCP, 107 .target = tcpoptstrip_tg4, 108 .targetsize = sizeof(struct xt_tcpoptstrip_target_info), 109 .me = THIS_MODULE, 110 }, 111#if defined(CONFIG_IP6_NF_MANGLE) || defined(CONFIG_IP6_NF_MANGLE_MODULE) 112 { 113 .name = "TCPOPTSTRIP", 114 .family = NFPROTO_IPV6, 115 .table = "mangle", 116 .proto = IPPROTO_TCP, 117 .target = tcpoptstrip_tg6, 118 .targetsize = sizeof(struct xt_tcpoptstrip_target_info), 119 .me = THIS_MODULE, 120 }, 121#endif 122}; 123 124static int __init tcpoptstrip_tg_init(void) 125{ 126 return xt_register_targets(tcpoptstrip_tg_reg, 127 ARRAY_SIZE(tcpoptstrip_tg_reg)); 128} 129 130static void __exit tcpoptstrip_tg_exit(void) 131{ 132 xt_unregister_targets(tcpoptstrip_tg_reg, 133 ARRAY_SIZE(tcpoptstrip_tg_reg)); 134} 135 136module_init(tcpoptstrip_tg_init); 137module_exit(tcpoptstrip_tg_exit); 138MODULE_AUTHOR("Sven Schnelle <svens@bitebene.org>, Jan Engelhardt <jengelh@medozas.de>"); 139MODULE_DESCRIPTION("Xtables: TCP option stripping"); 140MODULE_LICENSE("GPL"); 141MODULE_ALIAS("ipt_TCPOPTSTRIP"); 142MODULE_ALIAS("ip6t_TCPOPTSTRIP"); 143