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