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_action_param *par, 34 unsigned int tcphoff, unsigned int minlen) 35{ 36 const struct xt_tcpoptstrip_target_info *info = par->targinfo; 37 unsigned int optl, i, j; 38 struct tcphdr *tcph; 39 u_int16_t n, o; 40 u_int8_t *opt; 41 int len, tcp_hdrlen; 42 43 /* This is a fragment, no TCP header is available */ 44 if (par->fragoff != 0) 45 return XT_CONTINUE; 46 47 if (!skb_make_writable(skb, skb->len)) 48 return NF_DROP; 49 50 len = skb->len - tcphoff; 51 if (len < (int)sizeof(struct tcphdr)) 52 return NF_DROP; 53 54 tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff); 55 tcp_hdrlen = tcph->doff * 4; 56 57 if (len < tcp_hdrlen) 58 return NF_DROP; 59 60 opt = (u_int8_t *)tcph; 61 62 /* 63 * Walk through all TCP options - if we find some option to remove, 64 * set all octets to %TCPOPT_NOP and adjust checksum. 65 */ 66 for (i = sizeof(struct tcphdr); i < tcp_hdrlen - 1; i += optl) { 67 optl = optlen(opt, i); 68 69 if (i + optl > tcp_hdrlen) 70 break; 71 72 if (!tcpoptstrip_test_bit(info->strip_bmap, opt[i])) 73 continue; 74 75 for (j = 0; j < optl; ++j) { 76 o = opt[i+j]; 77 n = TCPOPT_NOP; 78 if ((i + j) % 2 == 0) { 79 o <<= 8; 80 n <<= 8; 81 } 82 inet_proto_csum_replace2(&tcph->check, skb, htons(o), 83 htons(n), 0); 84 } 85 memset(opt + i, TCPOPT_NOP, optl); 86 } 87 88 return XT_CONTINUE; 89} 90 91static unsigned int 92tcpoptstrip_tg4(struct sk_buff *skb, const struct xt_action_param *par) 93{ 94 return tcpoptstrip_mangle_packet(skb, par, ip_hdrlen(skb), 95 sizeof(struct iphdr) + sizeof(struct tcphdr)); 96} 97 98#if IS_ENABLED(CONFIG_IP6_NF_MANGLE) 99static unsigned int 100tcpoptstrip_tg6(struct sk_buff *skb, const struct xt_action_param *par) 101{ 102 struct ipv6hdr *ipv6h = ipv6_hdr(skb); 103 int tcphoff; 104 u_int8_t nexthdr; 105 __be16 frag_off; 106 107 nexthdr = ipv6h->nexthdr; 108 tcphoff = ipv6_skip_exthdr(skb, sizeof(*ipv6h), &nexthdr, &frag_off); 109 if (tcphoff < 0) 110 return NF_DROP; 111 112 return tcpoptstrip_mangle_packet(skb, par, tcphoff, 113 sizeof(*ipv6h) + sizeof(struct tcphdr)); 114} 115#endif 116 117static struct xt_target tcpoptstrip_tg_reg[] __read_mostly = { 118 { 119 .name = "TCPOPTSTRIP", 120 .family = NFPROTO_IPV4, 121 .table = "mangle", 122 .proto = IPPROTO_TCP, 123 .target = tcpoptstrip_tg4, 124 .targetsize = sizeof(struct xt_tcpoptstrip_target_info), 125 .me = THIS_MODULE, 126 }, 127#if IS_ENABLED(CONFIG_IP6_NF_MANGLE) 128 { 129 .name = "TCPOPTSTRIP", 130 .family = NFPROTO_IPV6, 131 .table = "mangle", 132 .proto = IPPROTO_TCP, 133 .target = tcpoptstrip_tg6, 134 .targetsize = sizeof(struct xt_tcpoptstrip_target_info), 135 .me = THIS_MODULE, 136 }, 137#endif 138}; 139 140static int __init tcpoptstrip_tg_init(void) 141{ 142 return xt_register_targets(tcpoptstrip_tg_reg, 143 ARRAY_SIZE(tcpoptstrip_tg_reg)); 144} 145 146static void __exit tcpoptstrip_tg_exit(void) 147{ 148 xt_unregister_targets(tcpoptstrip_tg_reg, 149 ARRAY_SIZE(tcpoptstrip_tg_reg)); 150} 151 152module_init(tcpoptstrip_tg_init); 153module_exit(tcpoptstrip_tg_exit); 154MODULE_AUTHOR("Sven Schnelle <svens@bitebene.org>, Jan Engelhardt <jengelh@medozas.de>"); 155MODULE_DESCRIPTION("Xtables: TCP option stripping"); 156MODULE_LICENSE("GPL"); 157MODULE_ALIAS("ipt_TCPOPTSTRIP"); 158MODULE_ALIAS("ip6t_TCPOPTSTRIP"); 159