xref: /openbmc/linux/net/netfilter/nf_nat_helper.c (revision 8032bf12)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2c7232c99SPatrick McHardy /* nf_nat_helper.c - generic support functions for NAT helpers
3c7232c99SPatrick McHardy  *
4c7232c99SPatrick McHardy  * (C) 2000-2002 Harald Welte <laforge@netfilter.org>
5c7232c99SPatrick McHardy  * (C) 2003-2006 Netfilter Core Team <coreteam@netfilter.org>
6f229f6ceSPatrick McHardy  * (C) 2007-2012 Patrick McHardy <kaber@trash.net>
7c7232c99SPatrick McHardy  */
8c7232c99SPatrick McHardy #include <linux/module.h>
9c7232c99SPatrick McHardy #include <linux/gfp.h>
10c7232c99SPatrick McHardy #include <linux/types.h>
11c7232c99SPatrick McHardy #include <linux/skbuff.h>
12c7232c99SPatrick McHardy #include <linux/tcp.h>
13c7232c99SPatrick McHardy #include <linux/udp.h>
14c7232c99SPatrick McHardy #include <net/tcp.h>
15c7232c99SPatrick McHardy 
16c7232c99SPatrick McHardy #include <net/netfilter/nf_conntrack.h>
17c7232c99SPatrick McHardy #include <net/netfilter/nf_conntrack_helper.h>
18c7232c99SPatrick McHardy #include <net/netfilter/nf_conntrack_ecache.h>
19c7232c99SPatrick McHardy #include <net/netfilter/nf_conntrack_expect.h>
2041d73ec0SPatrick McHardy #include <net/netfilter/nf_conntrack_seqadj.h>
21c7232c99SPatrick McHardy #include <net/netfilter/nf_nat.h>
22c7232c99SPatrick McHardy #include <net/netfilter/nf_nat_helper.h>
23c7232c99SPatrick McHardy 
24c7232c99SPatrick McHardy /* Frobs data inside this packet, which is linear. */
mangle_contents(struct sk_buff * skb,unsigned int dataoff,unsigned int match_offset,unsigned int match_len,const char * rep_buffer,unsigned int rep_len)25c7232c99SPatrick McHardy static void mangle_contents(struct sk_buff *skb,
26c7232c99SPatrick McHardy 			    unsigned int dataoff,
27c7232c99SPatrick McHardy 			    unsigned int match_offset,
28c7232c99SPatrick McHardy 			    unsigned int match_len,
29c7232c99SPatrick McHardy 			    const char *rep_buffer,
30c7232c99SPatrick McHardy 			    unsigned int rep_len)
31c7232c99SPatrick McHardy {
32c7232c99SPatrick McHardy 	unsigned char *data;
33c7232c99SPatrick McHardy 
34f8b0a3abSTaehee Yoo 	SKB_LINEAR_ASSERT(skb);
35c7232c99SPatrick McHardy 	data = skb_network_header(skb) + dataoff;
36c7232c99SPatrick McHardy 
37c7232c99SPatrick McHardy 	/* move post-replacement */
38c7232c99SPatrick McHardy 	memmove(data + match_offset + rep_len,
39c7232c99SPatrick McHardy 		data + match_offset + match_len,
40938177e9SSimon Horman 		skb_tail_pointer(skb) - (skb_network_header(skb) + dataoff +
41c7232c99SPatrick McHardy 			     match_offset + match_len));
42c7232c99SPatrick McHardy 
43c7232c99SPatrick McHardy 	/* insert data from buffer */
44c7232c99SPatrick McHardy 	memcpy(data + match_offset, rep_buffer, rep_len);
45c7232c99SPatrick McHardy 
46c7232c99SPatrick McHardy 	/* update skb info */
47c7232c99SPatrick McHardy 	if (rep_len > match_len) {
48c7232c99SPatrick McHardy 		pr_debug("nf_nat_mangle_packet: Extending packet by "
49c7232c99SPatrick McHardy 			 "%u from %u bytes\n", rep_len - match_len, skb->len);
50c7232c99SPatrick McHardy 		skb_put(skb, rep_len - match_len);
51c7232c99SPatrick McHardy 	} else {
52c7232c99SPatrick McHardy 		pr_debug("nf_nat_mangle_packet: Shrinking packet from "
53c7232c99SPatrick McHardy 			 "%u from %u bytes\n", match_len - rep_len, skb->len);
54c7232c99SPatrick McHardy 		__skb_trim(skb, skb->len + rep_len - match_len);
55c7232c99SPatrick McHardy 	}
56c7232c99SPatrick McHardy 
57cb9c6836SFlorian Westphal 	if (nf_ct_l3num((struct nf_conn *)skb_nfct(skb)) == NFPROTO_IPV4) {
58c7232c99SPatrick McHardy 		/* fix IP hdr checksum information */
59c7232c99SPatrick McHardy 		ip_hdr(skb)->tot_len = htons(skb->len);
60c7232c99SPatrick McHardy 		ip_send_check(ip_hdr(skb));
61c7232c99SPatrick McHardy 	} else
62c7232c99SPatrick McHardy 		ipv6_hdr(skb)->payload_len =
63c7232c99SPatrick McHardy 			htons(skb->len - sizeof(struct ipv6hdr));
64c7232c99SPatrick McHardy }
65c7232c99SPatrick McHardy 
66c7232c99SPatrick McHardy /* Unusual, but possible case. */
enlarge_skb(struct sk_buff * skb,unsigned int extra)67cba81cc4SGao Feng static bool enlarge_skb(struct sk_buff *skb, unsigned int extra)
68c7232c99SPatrick McHardy {
69c7232c99SPatrick McHardy 	if (skb->len + extra > 65535)
70cba81cc4SGao Feng 		return false;
71c7232c99SPatrick McHardy 
72c7232c99SPatrick McHardy 	if (pskb_expand_head(skb, 0, extra - skb_tailroom(skb), GFP_ATOMIC))
73cba81cc4SGao Feng 		return false;
74c7232c99SPatrick McHardy 
75cba81cc4SGao Feng 	return true;
76c7232c99SPatrick McHardy }
77c7232c99SPatrick McHardy 
78c7232c99SPatrick McHardy /* Generic function for mangling variable-length address changes inside
79c7232c99SPatrick McHardy  * NATed TCP connections (like the PORT XXX,XXX,XXX,XXX,XXX,XXX
80c7232c99SPatrick McHardy  * command in FTP).
81c7232c99SPatrick McHardy  *
82c7232c99SPatrick McHardy  * Takes care about all the nasty sequence number changes, checksumming,
83c7232c99SPatrick McHardy  * skb enlargement, ...
84c7232c99SPatrick McHardy  *
85c7232c99SPatrick McHardy  * */
__nf_nat_mangle_tcp_packet(struct sk_buff * skb,struct nf_conn * ct,enum ip_conntrack_info ctinfo,unsigned int protoff,unsigned int match_offset,unsigned int match_len,const char * rep_buffer,unsigned int rep_len,bool adjust)86cba81cc4SGao Feng bool __nf_nat_mangle_tcp_packet(struct sk_buff *skb,
87c7232c99SPatrick McHardy 				struct nf_conn *ct,
88c7232c99SPatrick McHardy 				enum ip_conntrack_info ctinfo,
89c7232c99SPatrick McHardy 				unsigned int protoff,
90c7232c99SPatrick McHardy 				unsigned int match_offset,
91c7232c99SPatrick McHardy 				unsigned int match_len,
92c7232c99SPatrick McHardy 				const char *rep_buffer,
93c7232c99SPatrick McHardy 				unsigned int rep_len, bool adjust)
94c7232c99SPatrick McHardy {
95c7232c99SPatrick McHardy 	struct tcphdr *tcph;
96c7232c99SPatrick McHardy 	int oldlen, datalen;
97c7232c99SPatrick McHardy 
9886f04538SFlorian Westphal 	if (skb_ensure_writable(skb, skb->len))
99cba81cc4SGao Feng 		return false;
100c7232c99SPatrick McHardy 
101c7232c99SPatrick McHardy 	if (rep_len > match_len &&
102c7232c99SPatrick McHardy 	    rep_len - match_len > skb_tailroom(skb) &&
103c7232c99SPatrick McHardy 	    !enlarge_skb(skb, rep_len - match_len))
104cba81cc4SGao Feng 		return false;
105c7232c99SPatrick McHardy 
106c7232c99SPatrick McHardy 	tcph = (void *)skb->data + protoff;
107c7232c99SPatrick McHardy 
108c7232c99SPatrick McHardy 	oldlen = skb->len - protoff;
109c7232c99SPatrick McHardy 	mangle_contents(skb, protoff + tcph->doff*4,
110c7232c99SPatrick McHardy 			match_offset, match_len, rep_buffer, rep_len);
111c7232c99SPatrick McHardy 
112c7232c99SPatrick McHardy 	datalen = skb->len - protoff;
113c7232c99SPatrick McHardy 
114dac3fe72SFlorian Westphal 	nf_nat_csum_recalc(skb, nf_ct_l3num(ct), IPPROTO_TCP,
115dac3fe72SFlorian Westphal 			   tcph, &tcph->check, datalen, oldlen);
116c7232c99SPatrick McHardy 
117c7232c99SPatrick McHardy 	if (adjust && rep_len != match_len)
11841d73ec0SPatrick McHardy 		nf_ct_seqadj_set(ct, ctinfo, tcph->seq,
119c7232c99SPatrick McHardy 				 (int)rep_len - (int)match_len);
120c7232c99SPatrick McHardy 
121cba81cc4SGao Feng 	return true;
122c7232c99SPatrick McHardy }
123c7232c99SPatrick McHardy EXPORT_SYMBOL(__nf_nat_mangle_tcp_packet);
124c7232c99SPatrick McHardy 
125c7232c99SPatrick McHardy /* Generic function for mangling variable-length address changes inside
126c7232c99SPatrick McHardy  * NATed UDP connections (like the CONNECT DATA XXXXX MESG XXXXX INDEX XXXXX
127c7232c99SPatrick McHardy  * command in the Amanda protocol)
128c7232c99SPatrick McHardy  *
129c7232c99SPatrick McHardy  * Takes care about all the nasty sequence number changes, checksumming,
130c7232c99SPatrick McHardy  * skb enlargement, ...
131c7232c99SPatrick McHardy  *
132c7232c99SPatrick McHardy  * XXX - This function could be merged with nf_nat_mangle_tcp_packet which
133c7232c99SPatrick McHardy  *       should be fairly easy to do.
134c7232c99SPatrick McHardy  */
135cba81cc4SGao Feng bool
nf_nat_mangle_udp_packet(struct sk_buff * skb,struct nf_conn * ct,enum ip_conntrack_info ctinfo,unsigned int protoff,unsigned int match_offset,unsigned int match_len,const char * rep_buffer,unsigned int rep_len)136c7232c99SPatrick McHardy nf_nat_mangle_udp_packet(struct sk_buff *skb,
137c7232c99SPatrick McHardy 			 struct nf_conn *ct,
138c7232c99SPatrick McHardy 			 enum ip_conntrack_info ctinfo,
139c7232c99SPatrick McHardy 			 unsigned int protoff,
140c7232c99SPatrick McHardy 			 unsigned int match_offset,
141c7232c99SPatrick McHardy 			 unsigned int match_len,
142c7232c99SPatrick McHardy 			 const char *rep_buffer,
143c7232c99SPatrick McHardy 			 unsigned int rep_len)
144c7232c99SPatrick McHardy {
145c7232c99SPatrick McHardy 	struct udphdr *udph;
146c7232c99SPatrick McHardy 	int datalen, oldlen;
147c7232c99SPatrick McHardy 
14886f04538SFlorian Westphal 	if (skb_ensure_writable(skb, skb->len))
149cba81cc4SGao Feng 		return false;
150c7232c99SPatrick McHardy 
151c7232c99SPatrick McHardy 	if (rep_len > match_len &&
152c7232c99SPatrick McHardy 	    rep_len - match_len > skb_tailroom(skb) &&
153c7232c99SPatrick McHardy 	    !enlarge_skb(skb, rep_len - match_len))
154cba81cc4SGao Feng 		return false;
155c7232c99SPatrick McHardy 
156c7232c99SPatrick McHardy 	udph = (void *)skb->data + protoff;
157c7232c99SPatrick McHardy 
158c7232c99SPatrick McHardy 	oldlen = skb->len - protoff;
159c7232c99SPatrick McHardy 	mangle_contents(skb, protoff + sizeof(*udph),
160c7232c99SPatrick McHardy 			match_offset, match_len, rep_buffer, rep_len);
161c7232c99SPatrick McHardy 
162c7232c99SPatrick McHardy 	/* update the length of the UDP packet */
163c7232c99SPatrick McHardy 	datalen = skb->len - protoff;
164c7232c99SPatrick McHardy 	udph->len = htons(datalen);
165c7232c99SPatrick McHardy 
166c7232c99SPatrick McHardy 	/* fix udp checksum if udp checksum was previously calculated */
167c7232c99SPatrick McHardy 	if (!udph->check && skb->ip_summed != CHECKSUM_PARTIAL)
168cba81cc4SGao Feng 		return true;
169c7232c99SPatrick McHardy 
1706bac76dbSFlorian Westphal 	nf_nat_csum_recalc(skb, nf_ct_l3num(ct), IPPROTO_UDP,
171dac3fe72SFlorian Westphal 			   udph, &udph->check, datalen, oldlen);
172c7232c99SPatrick McHardy 
173cba81cc4SGao Feng 	return true;
174c7232c99SPatrick McHardy }
175c7232c99SPatrick McHardy EXPORT_SYMBOL(nf_nat_mangle_udp_packet);
176c7232c99SPatrick McHardy 
177c7232c99SPatrick McHardy /* Setup NAT on this expected conntrack so it follows master. */
178c7232c99SPatrick McHardy /* If we fail to get a free NAT slot, we'll get dropped on confirm */
nf_nat_follow_master(struct nf_conn * ct,struct nf_conntrack_expect * exp)179c7232c99SPatrick McHardy void nf_nat_follow_master(struct nf_conn *ct,
180c7232c99SPatrick McHardy 			  struct nf_conntrack_expect *exp)
181c7232c99SPatrick McHardy {
1822eb0f624SThierry Du Tre 	struct nf_nat_range2 range;
183c7232c99SPatrick McHardy 
184c7232c99SPatrick McHardy 	/* This must be a fresh one. */
185c7232c99SPatrick McHardy 	BUG_ON(ct->status & IPS_NAT_DONE_MASK);
186c7232c99SPatrick McHardy 
187c7232c99SPatrick McHardy 	/* Change src to where master sends to */
188c7232c99SPatrick McHardy 	range.flags = NF_NAT_RANGE_MAP_IPS;
189c7232c99SPatrick McHardy 	range.min_addr = range.max_addr
190c7232c99SPatrick McHardy 		= ct->master->tuplehash[!exp->dir].tuple.dst.u3;
191c7232c99SPatrick McHardy 	nf_nat_setup_info(ct, &range, NF_NAT_MANIP_SRC);
192c7232c99SPatrick McHardy 
193c7232c99SPatrick McHardy 	/* For DST manip, map port here to where it's expected. */
194c7232c99SPatrick McHardy 	range.flags = (NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED);
195c7232c99SPatrick McHardy 	range.min_proto = range.max_proto = exp->saved_proto;
196c7232c99SPatrick McHardy 	range.min_addr = range.max_addr
197c7232c99SPatrick McHardy 		= ct->master->tuplehash[!exp->dir].tuple.src.u3;
198c7232c99SPatrick McHardy 	nf_nat_setup_info(ct, &range, NF_NAT_MANIP_DST);
199c7232c99SPatrick McHardy }
200c7232c99SPatrick McHardy EXPORT_SYMBOL(nf_nat_follow_master);
201c92c2717SFlorian Westphal 
nf_nat_exp_find_port(struct nf_conntrack_expect * exp,u16 port)202c92c2717SFlorian Westphal u16 nf_nat_exp_find_port(struct nf_conntrack_expect *exp, u16 port)
203c92c2717SFlorian Westphal {
204adda60ccSFlorian Westphal 	static const unsigned int max_attempts = 128;
205adda60ccSFlorian Westphal 	int range, attempts_left;
206adda60ccSFlorian Westphal 	u16 min = port;
207adda60ccSFlorian Westphal 
208adda60ccSFlorian Westphal 	range = USHRT_MAX - port;
209adda60ccSFlorian Westphal 	attempts_left = range;
210adda60ccSFlorian Westphal 
211adda60ccSFlorian Westphal 	if (attempts_left > max_attempts)
212adda60ccSFlorian Westphal 		attempts_left = max_attempts;
213adda60ccSFlorian Westphal 
214c92c2717SFlorian Westphal 	/* Try to get same port: if not, try to change it. */
215adda60ccSFlorian Westphal 	for (;;) {
216c92c2717SFlorian Westphal 		int res;
217c92c2717SFlorian Westphal 
218c92c2717SFlorian Westphal 		exp->tuple.dst.u.tcp.port = htons(port);
219c92c2717SFlorian Westphal 		res = nf_ct_expect_related(exp, 0);
220c92c2717SFlorian Westphal 		if (res == 0)
221c92c2717SFlorian Westphal 			return port;
222c92c2717SFlorian Westphal 
223adda60ccSFlorian Westphal 		if (res != -EBUSY || (--attempts_left < 0))
224c92c2717SFlorian Westphal 			break;
225adda60ccSFlorian Westphal 
226*8032bf12SJason A. Donenfeld 		port = min + get_random_u32_below(range);
227c92c2717SFlorian Westphal 	}
228c92c2717SFlorian Westphal 
229c92c2717SFlorian Westphal 	return 0;
230c92c2717SFlorian Westphal }
231c92c2717SFlorian Westphal EXPORT_SYMBOL_GPL(nf_nat_exp_find_port);
232