1338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle/*
2338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle * A module for stripping a specific TCP option from TCP packets.
3338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle *
4338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle * Copyright (C) 2007 Sven Schnelle <svens@bitebene.org>
5338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle * Copyright © CC Computer Consultants GmbH, 2007
6338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle *
7338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle * This program is free software; you can redistribute it and/or modify
8338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle * it under the terms of the GNU General Public License version 2 as
9338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle * published by the Free Software Foundation.
10338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle */
11338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle
12338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle#include <linux/module.h>
13338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle#include <linux/skbuff.h>
14338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle#include <linux/ip.h>
15338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle#include <linux/ipv6.h>
16338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle#include <linux/tcp.h>
17338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle#include <net/ipv6.h>
18338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle#include <net/tcp.h>
19338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle#include <linux/netfilter/x_tables.h>
20338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle#include <linux/netfilter/xt_TCPOPTSTRIP.h>
21338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle
22338e8a79262c3709e314fbcc7ca193323e534934Sven Schnellestatic inline unsigned int optlen(const u_int8_t *opt, unsigned int offset)
23338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle{
24338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle	/* Beware zero-length options: make finite progress */
25338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle	if (opt[offset] <= TCPOPT_NOP || opt[offset+1] == 0)
26338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle		return 1;
27338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle	else
28338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle		return opt[offset+1];
29338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle}
30338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle
31338e8a79262c3709e314fbcc7ca193323e534934Sven Schnellestatic unsigned int
32338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelletcpoptstrip_mangle_packet(struct sk_buff *skb,
33bc6bcb59dd7c184d229f9e86d08aa56059938a4cPablo Neira Ayuso			  const struct xt_action_param *par,
34338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle			  unsigned int tcphoff, unsigned int minlen)
35338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle{
36bc6bcb59dd7c184d229f9e86d08aa56059938a4cPablo Neira Ayuso	const struct xt_tcpoptstrip_target_info *info = par->targinfo;
37338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle	unsigned int optl, i, j;
38338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle	struct tcphdr *tcph;
39338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle	u_int16_t n, o;
40338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle	u_int8_t *opt;
41bc6bcb59dd7c184d229f9e86d08aa56059938a4cPablo Neira Ayuso	int len;
42bc6bcb59dd7c184d229f9e86d08aa56059938a4cPablo Neira Ayuso
43bc6bcb59dd7c184d229f9e86d08aa56059938a4cPablo Neira Ayuso	/* This is a fragment, no TCP header is available */
44bc6bcb59dd7c184d229f9e86d08aa56059938a4cPablo Neira Ayuso	if (par->fragoff != 0)
45bc6bcb59dd7c184d229f9e86d08aa56059938a4cPablo Neira Ayuso		return XT_CONTINUE;
46338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle
47338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle	if (!skb_make_writable(skb, skb->len))
48338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle		return NF_DROP;
49338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle
50bc6bcb59dd7c184d229f9e86d08aa56059938a4cPablo Neira Ayuso	len = skb->len - tcphoff;
51ed82c437320c48a4032492f4a55a7e2c934158b6Pablo Neira Ayuso	if (len < (int)sizeof(struct tcphdr))
52bc6bcb59dd7c184d229f9e86d08aa56059938a4cPablo Neira Ayuso		return NF_DROP;
53bc6bcb59dd7c184d229f9e86d08aa56059938a4cPablo Neira Ayuso
54338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle	tcph = (struct tcphdr *)(skb_network_header(skb) + tcphoff);
55ed82c437320c48a4032492f4a55a7e2c934158b6Pablo Neira Ayuso	if (tcph->doff * 4 > len)
56ed82c437320c48a4032492f4a55a7e2c934158b6Pablo Neira Ayuso		return NF_DROP;
57ed82c437320c48a4032492f4a55a7e2c934158b6Pablo Neira Ayuso
58338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle	opt  = (u_int8_t *)tcph;
59338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle
60338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle	/*
61338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle	 * Walk through all TCP options - if we find some option to remove,
62338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle	 * set all octets to %TCPOPT_NOP and adjust checksum.
63338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle	 */
64338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle	for (i = sizeof(struct tcphdr); i < tcp_hdrlen(skb); i += optl) {
65338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle		optl = optlen(opt, i);
66338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle
67338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle		if (i + optl > tcp_hdrlen(skb))
68338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle			break;
69338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle
70338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle		if (!tcpoptstrip_test_bit(info->strip_bmap, opt[i]))
71338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle			continue;
72338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle
73338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle		for (j = 0; j < optl; ++j) {
74338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle			o = opt[i+j];
75338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle			n = TCPOPT_NOP;
76338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle			if ((i + j) % 2 == 0) {
77338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle				o <<= 8;
78338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle				n <<= 8;
79338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle			}
80338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle			inet_proto_csum_replace2(&tcph->check, skb, htons(o),
81338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle						 htons(n), 0);
82338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle		}
83338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle		memset(opt + i, TCPOPT_NOP, optl);
84338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle	}
85338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle
86338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle	return XT_CONTINUE;
87338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle}
88338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle
89338e8a79262c3709e314fbcc7ca193323e534934Sven Schnellestatic unsigned int
904b560b447df83368df44bd3712c0c39b1d79ba04Jan Engelhardttcpoptstrip_tg4(struct sk_buff *skb, const struct xt_action_param *par)
91338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle{
92bc6bcb59dd7c184d229f9e86d08aa56059938a4cPablo Neira Ayuso	return tcpoptstrip_mangle_packet(skb, par, ip_hdrlen(skb),
93338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle	       sizeof(struct iphdr) + sizeof(struct tcphdr));
94338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle}
95338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle
96c0cd115667bcd23c2a31fe2114beaab3608de68cIgor Maravić#if IS_ENABLED(CONFIG_IP6_NF_MANGLE)
97338e8a79262c3709e314fbcc7ca193323e534934Sven Schnellestatic unsigned int
984b560b447df83368df44bd3712c0c39b1d79ba04Jan Engelhardttcpoptstrip_tg6(struct sk_buff *skb, const struct xt_action_param *par)
99338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle{
100338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle	struct ipv6hdr *ipv6h = ipv6_hdr(skb);
101be8d0d7903af85d396449b34366e7f5b0c9cc58bRoel Kluin	int tcphoff;
102338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle	u_int8_t nexthdr;
10375f2811c6460ccc59d83c66059943ce9c9f81a18Jesse Gross	__be16 frag_off;
104338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle
105338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle	nexthdr = ipv6h->nexthdr;
10675f2811c6460ccc59d83c66059943ce9c9f81a18Jesse Gross	tcphoff = ipv6_skip_exthdr(skb, sizeof(*ipv6h), &nexthdr, &frag_off);
107338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle	if (tcphoff < 0)
108338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle		return NF_DROP;
109338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle
110bc6bcb59dd7c184d229f9e86d08aa56059938a4cPablo Neira Ayuso	return tcpoptstrip_mangle_packet(skb, par, tcphoff,
111338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle	       sizeof(*ipv6h) + sizeof(struct tcphdr));
112338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle}
113338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle#endif
114338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle
115338e8a79262c3709e314fbcc7ca193323e534934Sven Schnellestatic struct xt_target tcpoptstrip_tg_reg[] __read_mostly = {
116338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle	{
117338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle		.name       = "TCPOPTSTRIP",
118ee999d8b9573df1b547aacdc6d79f86eb79c25cdJan Engelhardt		.family     = NFPROTO_IPV4,
119338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle		.table      = "mangle",
120338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle		.proto      = IPPROTO_TCP,
121338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle		.target     = tcpoptstrip_tg4,
122338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle		.targetsize = sizeof(struct xt_tcpoptstrip_target_info),
123338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle		.me         = THIS_MODULE,
124338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle	},
125c0cd115667bcd23c2a31fe2114beaab3608de68cIgor Maravić#if IS_ENABLED(CONFIG_IP6_NF_MANGLE)
126338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle	{
127338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle		.name       = "TCPOPTSTRIP",
128ee999d8b9573df1b547aacdc6d79f86eb79c25cdJan Engelhardt		.family     = NFPROTO_IPV6,
129338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle		.table      = "mangle",
130338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle		.proto      = IPPROTO_TCP,
131338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle		.target     = tcpoptstrip_tg6,
132338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle		.targetsize = sizeof(struct xt_tcpoptstrip_target_info),
133338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle		.me         = THIS_MODULE,
134338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle	},
135338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle#endif
136338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle};
137338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle
138338e8a79262c3709e314fbcc7ca193323e534934Sven Schnellestatic int __init tcpoptstrip_tg_init(void)
139338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle{
140338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle	return xt_register_targets(tcpoptstrip_tg_reg,
141338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle				   ARRAY_SIZE(tcpoptstrip_tg_reg));
142338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle}
143338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle
144338e8a79262c3709e314fbcc7ca193323e534934Sven Schnellestatic void __exit tcpoptstrip_tg_exit(void)
145338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle{
146338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle	xt_unregister_targets(tcpoptstrip_tg_reg,
147338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle			      ARRAY_SIZE(tcpoptstrip_tg_reg));
148338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle}
149338e8a79262c3709e314fbcc7ca193323e534934Sven Schnelle
150338e8a79262c3709e314fbcc7ca193323e534934Sven Schnellemodule_init(tcpoptstrip_tg_init);
151338e8a79262c3709e314fbcc7ca193323e534934Sven Schnellemodule_exit(tcpoptstrip_tg_exit);
152408ffaa4a11ddd6f730be520479fd5cd890c57d3Jan EngelhardtMODULE_AUTHOR("Sven Schnelle <svens@bitebene.org>, Jan Engelhardt <jengelh@medozas.de>");
1532ae15b64e6a1608c840c60df38e8e5eef7b2b8c3Jan EngelhardtMODULE_DESCRIPTION("Xtables: TCP option stripping");
154338e8a79262c3709e314fbcc7ca193323e534934Sven SchnelleMODULE_LICENSE("GPL");
155338e8a79262c3709e314fbcc7ca193323e534934Sven SchnelleMODULE_ALIAS("ipt_TCPOPTSTRIP");
156338e8a79262c3709e314fbcc7ca193323e534934Sven SchnelleMODULE_ALIAS("ip6t_TCPOPTSTRIP");
157