1 // SPDX-License-Identifier: GPL-2.0-only
2 /* nf_nat_helper.c - generic support functions for NAT helpers
4 * (C) 2000-2002 Harald Welte <laforge@netfilter.org>
5 * (C) 2003-2006 Netfilter Core Team <coreteam@netfilter.org>
6 * (C) 2007-2012 Patrick McHardy <kaber@trash.net>
8 #include <linux/module.h>
10 #include <linux/types.h>
11 #include <linux/skbuff.h>
12 #include <linux/tcp.h>
13 #include <linux/udp.h>
16 #include <net/netfilter/nf_conntrack.h>
17 #include <net/netfilter/nf_conntrack_helper.h>
18 #include <net/netfilter/nf_conntrack_ecache.h>
19 #include <net/netfilter/nf_conntrack_expect.h>
20 #include <net/netfilter/nf_conntrack_seqadj.h>
21 #include <net/netfilter/nf_nat.h>
22 #include <net/netfilter/nf_nat_helper.h>
24 /* Frobs data inside this packet, which is linear. */
25 static void mangle_contents(struct sk_buff
*skb
,
27 unsigned int match_offset
,
28 unsigned int match_len
,
29 const char *rep_buffer
,
34 SKB_LINEAR_ASSERT(skb
);
35 data
= skb_network_header(skb
) + dataoff
;
37 /* move post-replacement */
38 memmove(data
+ match_offset
+ rep_len
,
39 data
+ match_offset
+ match_len
,
40 skb_tail_pointer(skb
) - (skb_network_header(skb
) + dataoff
+
41 match_offset
+ match_len
));
43 /* insert data from buffer */
44 memcpy(data
+ match_offset
, rep_buffer
, rep_len
);
47 if (rep_len
> match_len
) {
48 pr_debug("nf_nat_mangle_packet: Extending packet by "
49 "%u from %u bytes\n", rep_len
- match_len
, skb
->len
);
50 skb_put(skb
, rep_len
- match_len
);
52 pr_debug("nf_nat_mangle_packet: Shrinking packet from "
53 "%u from %u bytes\n", match_len
- rep_len
, skb
->len
);
54 __skb_trim(skb
, skb
->len
+ rep_len
- match_len
);
57 if (nf_ct_l3num((struct nf_conn
*)skb_nfct(skb
)) == NFPROTO_IPV4
) {
58 /* fix IP hdr checksum information */
59 ip_hdr(skb
)->tot_len
= htons(skb
->len
);
60 ip_send_check(ip_hdr(skb
));
62 ipv6_hdr(skb
)->payload_len
=
63 htons(skb
->len
- sizeof(struct ipv6hdr
));
66 /* Unusual, but possible case. */
67 static bool enlarge_skb(struct sk_buff
*skb
, unsigned int extra
)
69 if (skb
->len
+ extra
> 65535)
72 if (pskb_expand_head(skb
, 0, extra
- skb_tailroom(skb
), GFP_ATOMIC
))
78 /* Generic function for mangling variable-length address changes inside
79 * NATed TCP connections (like the PORT XXX,XXX,XXX,XXX,XXX,XXX
82 * Takes care about all the nasty sequence number changes, checksumming,
83 * skb enlargement, ...
86 bool __nf_nat_mangle_tcp_packet(struct sk_buff
*skb
,
88 enum ip_conntrack_info ctinfo
,
90 unsigned int match_offset
,
91 unsigned int match_len
,
92 const char *rep_buffer
,
93 unsigned int rep_len
, bool adjust
)
98 if (skb_ensure_writable(skb
, skb
->len
))
101 if (rep_len
> match_len
&&
102 rep_len
- match_len
> skb_tailroom(skb
) &&
103 !enlarge_skb(skb
, rep_len
- match_len
))
106 tcph
= (void *)skb
->data
+ protoff
;
108 oldlen
= skb
->len
- protoff
;
109 mangle_contents(skb
, protoff
+ tcph
->doff
*4,
110 match_offset
, match_len
, rep_buffer
, rep_len
);
112 datalen
= skb
->len
- protoff
;
114 nf_nat_csum_recalc(skb
, nf_ct_l3num(ct
), IPPROTO_TCP
,
115 tcph
, &tcph
->check
, datalen
, oldlen
);
117 if (adjust
&& rep_len
!= match_len
)
118 nf_ct_seqadj_set(ct
, ctinfo
, tcph
->seq
,
119 (int)rep_len
- (int)match_len
);
123 EXPORT_SYMBOL(__nf_nat_mangle_tcp_packet
);
125 /* Generic function for mangling variable-length address changes inside
126 * NATed UDP connections (like the CONNECT DATA XXXXX MESG XXXXX INDEX XXXXX
127 * command in the Amanda protocol)
129 * Takes care about all the nasty sequence number changes, checksumming,
130 * skb enlargement, ...
132 * XXX - This function could be merged with nf_nat_mangle_tcp_packet which
133 * should be fairly easy to do.
136 nf_nat_mangle_udp_packet(struct sk_buff
*skb
,
138 enum ip_conntrack_info ctinfo
,
139 unsigned int protoff
,
140 unsigned int match_offset
,
141 unsigned int match_len
,
142 const char *rep_buffer
,
143 unsigned int rep_len
)
148 if (skb_ensure_writable(skb
, skb
->len
))
151 if (rep_len
> match_len
&&
152 rep_len
- match_len
> skb_tailroom(skb
) &&
153 !enlarge_skb(skb
, rep_len
- match_len
))
156 udph
= (void *)skb
->data
+ protoff
;
158 oldlen
= skb
->len
- protoff
;
159 mangle_contents(skb
, protoff
+ sizeof(*udph
),
160 match_offset
, match_len
, rep_buffer
, rep_len
);
162 /* update the length of the UDP packet */
163 datalen
= skb
->len
- protoff
;
164 udph
->len
= htons(datalen
);
166 /* fix udp checksum if udp checksum was previously calculated */
167 if (!udph
->check
&& skb
->ip_summed
!= CHECKSUM_PARTIAL
)
170 nf_nat_csum_recalc(skb
, nf_ct_l3num(ct
), IPPROTO_UDP
,
171 udph
, &udph
->check
, datalen
, oldlen
);
175 EXPORT_SYMBOL(nf_nat_mangle_udp_packet
);
177 /* Setup NAT on this expected conntrack so it follows master. */
178 /* If we fail to get a free NAT slot, we'll get dropped on confirm */
179 void nf_nat_follow_master(struct nf_conn
*ct
,
180 struct nf_conntrack_expect
*exp
)
182 struct nf_nat_range2 range
;
184 /* This must be a fresh one. */
185 BUG_ON(ct
->status
& IPS_NAT_DONE_MASK
);
187 /* Change src to where master sends to */
188 range
.flags
= NF_NAT_RANGE_MAP_IPS
;
189 range
.min_addr
= range
.max_addr
190 = ct
->master
->tuplehash
[!exp
->dir
].tuple
.dst
.u3
;
191 nf_nat_setup_info(ct
, &range
, NF_NAT_MANIP_SRC
);
193 /* For DST manip, map port here to where it's expected. */
194 range
.flags
= (NF_NAT_RANGE_MAP_IPS
| NF_NAT_RANGE_PROTO_SPECIFIED
);
195 range
.min_proto
= range
.max_proto
= exp
->saved_proto
;
196 range
.min_addr
= range
.max_addr
197 = ct
->master
->tuplehash
[!exp
->dir
].tuple
.src
.u3
;
198 nf_nat_setup_info(ct
, &range
, NF_NAT_MANIP_DST
);
200 EXPORT_SYMBOL(nf_nat_follow_master
);
202 u16
nf_nat_exp_find_port(struct nf_conntrack_expect
*exp
, u16 port
)
204 static const unsigned int max_attempts
= 128;
205 int range
, attempts_left
;
208 range
= USHRT_MAX
- port
;
209 attempts_left
= range
;
211 if (attempts_left
> max_attempts
)
212 attempts_left
= max_attempts
;
214 /* Try to get same port: if not, try to change it. */
218 exp
->tuple
.dst
.u
.tcp
.port
= htons(port
);
219 res
= nf_ct_expect_related(exp
, 0);
223 if (res
!= -EBUSY
|| (--attempts_left
< 0))
226 port
= min
+ get_random_u32_below(range
);
231 EXPORT_SYMBOL_GPL(nf_nat_exp_find_port
);