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