OpenVPN
clinat.c
Go to the documentation of this file.
1/*
2 * OpenVPN -- An application to securely tunnel IP networks
3 * over a single TCP/UDP port, with support for SSL/TLS-based
4 * session authentication and key exchange,
5 * packet encryption, packet authentication, and
6 * packet compression.
7 *
8 * Copyright (C) 2002-2026 OpenVPN Inc <sales@openvpn.net>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, see <https://www.gnu.org/licenses/>.
21 */
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#include "syshead.h"
28
29#include "clinat.h"
30#include "proto.h"
31#include "socket_util.h"
32#include "memdbg.h"
33
34static bool
36{
37 if (dest->n >= MAX_CLIENT_NAT)
38 {
39 msg(M_WARN, "WARNING: client-nat table overflow (max %d entries)", MAX_CLIENT_NAT);
40 return false;
41 }
42 else
43 {
44 dest->entries[dest->n++] = *e;
45 return true;
46 }
47}
48
49void
51{
52 struct gc_arena gc = gc_new();
53
54 msg(msglevel, "*** CNAT list");
55 if (list)
56 {
57 for (int i = 0; i < list->n; ++i)
58 {
59 const struct client_nat_entry *e = &list->entries[i];
60 msg(msglevel, " CNAT[%d] t=%d %s/%s/%s", i, e->type,
64 }
65 }
66 gc_free(&gc);
67}
68
71{
72 struct client_nat_option_list *ret;
74 return ret;
75}
76
79{
80 struct client_nat_option_list *ret;
82 *ret = *src;
83 return ret;
84}
85
86void
88 const struct client_nat_option_list *src)
89{
90 int i;
91 for (i = 0; i < src->n; ++i)
92 {
93 if (!add_entry(dest, &src->entries[i]))
94 {
95 break;
96 }
97 }
98}
99
100void
102 const char *network, const char *netmask, const char *foreign_network,
103 msglvl_t msglevel)
104{
105 struct client_nat_entry e;
106 bool ok;
107
108 if (!strcmp(type, "snat"))
109 {
110 e.type = CN_SNAT;
111 }
112 else if (!strcmp(type, "dnat"))
113 {
114 e.type = CN_DNAT;
115 }
116 else
117 {
118 msg(msglevel, "client-nat: type must be 'snat' or 'dnat'");
119 return;
120 }
121
122 e.network = getaddr(0, network, 0, &ok, NULL);
123 if (!ok)
124 {
125 msg(msglevel, "client-nat: bad network: %s", network);
126 return;
127 }
128 e.netmask = getaddr(0, netmask, 0, &ok, NULL);
129 if (!ok)
130 {
131 msg(msglevel, "client-nat: bad netmask: %s", netmask);
132 return;
133 }
134 e.foreign_network = getaddr(0, foreign_network, 0, &ok, NULL);
135 if (!ok)
136 {
137 msg(msglevel, "client-nat: bad foreign network: %s", foreign_network);
138 return;
139 }
140
141 add_entry(dest, &e);
142}
143
144#if 0
145static void
146print_checksum(struct openvpn_iphdr *iph, const char *prefix)
147{
148 uint16_t *sptr;
149 unsigned int sum = 0;
150 int i = 0;
151 for (sptr = (uint16_t *)iph; (uint8_t *)sptr < (uint8_t *)iph + sizeof(struct openvpn_iphdr); sptr++)
152 {
153 i += 1;
154 sum += *sptr;
155 }
156 msg(M_INFO, "** CKSUM[%d] %s %08x", i, prefix, sum);
157}
158#endif
159
160static void
161print_pkt(struct openvpn_iphdr *iph, const char *prefix, const int direction, const msglvl_t msglevel)
162{
163 struct gc_arena gc = gc_new();
164
165 char *dirstr = "???";
166 if (direction == CN_OUTGOING)
167 {
168 dirstr = "OUT";
169 }
170 else if (direction == CN_INCOMING)
171 {
172 dirstr = "IN";
173 }
174
175 msg(msglevel, "** CNAT %s %s %s -> %s", dirstr, prefix,
178
179 gc_free(&gc);
180}
181
182void
184 const int direction)
185{
186 struct ip_tcp_udp_hdr *h = (struct ip_tcp_udp_hdr *)BPTR(ipbuf);
187 int32_t accumulate = 0;
188 unsigned int alog = 0;
189
191 {
192 print_pkt(&h->ip, "BEFORE", direction, D_CLIENT_NAT);
193 }
194
195 for (int i = 0; i < list->n; ++i)
196 {
197 uint32_t addr, *addr_ptr;
198 const uint32_t *from, *to;
199 unsigned int amask;
200 const struct client_nat_entry *e = &list->entries[i]; /* current NAT rule */
201 if (e->type ^ direction)
202 {
203 addr = *(addr_ptr = &h->ip.daddr);
204 amask = 2;
205 }
206 else
207 {
208 addr = *(addr_ptr = &h->ip.saddr);
209 amask = 1;
210 }
211 if (direction)
212 {
213 from = &e->foreign_network;
214 to = &e->network;
215 }
216 else
217 {
218 from = &e->network;
219 to = &e->foreign_network;
220 }
221
222 if (((addr & e->netmask) == *from) && !(amask & alog))
223 {
224 /* pre-adjust IP checksum */
225 ADD_CHECKSUM_32(accumulate, addr);
226
227 /* do NAT transform */
228 addr = (addr & ~e->netmask) | *to;
229
230 /* post-adjust IP checksum */
231 SUB_CHECKSUM_32(accumulate, addr);
232
233 /* write the modified address to packet */
234 *addr_ptr = addr;
235
236 /* mark as modified */
237 alog |= amask;
238 }
239 }
240 if (alog)
241 {
243 {
244 print_pkt(&h->ip, "AFTER", direction, D_CLIENT_NAT);
245 }
246
247 ADJUST_CHECKSUM(accumulate, h->ip.check);
248
250 {
251 if (BLENZ(ipbuf) >= sizeof(struct openvpn_iphdr) + sizeof(struct openvpn_tcphdr))
252 {
253 ADJUST_CHECKSUM(accumulate, h->u.tcp.check);
254 }
255 }
256 else if (h->ip.protocol == OPENVPN_IPPROTO_UDP)
257 {
258 if (BLENZ(ipbuf) >= sizeof(struct openvpn_iphdr) + sizeof(struct openvpn_udphdr))
259 {
260 /* RFC 768: a UDP checksum of 0 means "no checksum computed".
261 * Do not run the incremental adjustment over a non-checksum,
262 * or we will write a bogus non-zero value into the field.
263 */
264 if (h->u.udp.check)
265 {
266 ADJUST_CHECKSUM(accumulate, h->u.udp.check);
267
268 if (!h->u.udp.check)
269 {
270 /* RFC 768: a computed checksum of 0 must be transmitted
271 * as 0xFFFF (one-complement), because 0 is reserved for
272 * "no checksum computed"
273 */
274 h->u.udp.check = 0xFFFF;
275 }
276 }
277 }
278 }
279 }
280}
#define BPTR(buf)
Return a pointer to the start of the buffer content.
Definition buffer.h:139
#define ALLOC_OBJ_CLEAR_GC(dptr, type, gc)
Allocate and zero-initialise a garbage-collected object of the given type.
Definition buffer.h:2134
#define ALLOC_OBJ_GC(dptr, type, gc)
Allocate a garbage-collected object of the given type (uninitialised).
Definition buffer.h:2122
#define BLENZ(buf)
Return the length of the buffer content as a size_t.
Definition buffer.h:153
static void gc_free(struct gc_arena *a)
Free all allocations in a garbage collection arena.
Definition buffer.h:1976
static struct gc_arena gc_new(void)
Allocate and return a new, empty garbage collection arena.
Definition buffer.h:1960
void add_client_nat_to_option_list(struct client_nat_option_list *dest, const char *type, const char *network, const char *netmask, const char *foreign_network, msglvl_t msglevel)
Definition clinat.c:101
void client_nat_transform(const struct client_nat_option_list *list, struct buffer *ipbuf, const int direction)
Definition clinat.c:183
void print_client_nat_list(const struct client_nat_option_list *list, msglvl_t msglevel)
Definition clinat.c:50
static bool add_entry(struct client_nat_option_list *dest, const struct client_nat_entry *e)
Definition clinat.c:35
struct client_nat_option_list * new_client_nat_list(struct gc_arena *gc)
Definition clinat.c:70
struct client_nat_option_list * clone_client_nat_option_list(const struct client_nat_option_list *src, struct gc_arena *gc)
Definition clinat.c:78
static void print_pkt(struct openvpn_iphdr *iph, const char *prefix, const int direction, const msglvl_t msglevel)
Definition clinat.c:161
void copy_client_nat_option_list(struct client_nat_option_list *dest, const struct client_nat_option_list *src)
Definition clinat.c:87
#define CN_INCOMING
Definition clinat.h:31
#define CN_SNAT
Definition clinat.h:35
#define MAX_CLIENT_NAT
Definition clinat.h:28
#define CN_OUTGOING
Definition clinat.h:30
#define CN_DNAT
Definition clinat.h:36
#define M_INFO
Definition errlevel.h:54
#define D_CLIENT_NAT
Definition errlevel.h:115
static bool check_debug_level(msglvl_t level)
Definition error.h:253
#define msg(flags,...)
Definition error.h:152
unsigned int msglvl_t
Definition error.h:77
#define M_WARN
Definition error.h:92
#define SUB_CHECKSUM_32(acc, u32)
Definition proto.h:240
#define ADJUST_CHECKSUM(acc, cksum)
Definition proto.h:215
#define OPENVPN_IPPROTO_UDP
Definition proto.h:106
#define OPENVPN_IPPROTO_TCP
Definition proto.h:105
#define ADD_CHECKSUM_32(acc, u32)
Definition proto.h:234
in_addr_t getaddr(unsigned int flags, const char *hostname, int resolve_retry_seconds, bool *succeeded, struct signal_info *sig_info)
Translate an IPv4 addr or hostname from string form to in_addr_t.
Definition socket.c:195
const char * print_in_addr_t(in_addr_t addr, unsigned int flags, struct gc_arena *gc)
#define IA_NET_ORDER
Definition socket_util.h:90
Wrapper structure for dynamically allocated memory.
Definition buffer.h:71
Definition clinat.h:34
int type
Definition clinat.h:37
in_addr_t network
Definition clinat.h:38
in_addr_t foreign_network
Definition clinat.h:40
in_addr_t netmask
Definition clinat.h:39
struct client_nat_entry entries[MAX_CLIENT_NAT]
Definition clinat.h:46
Garbage collection arena used to keep track of dynamically allocated memory.
Definition buffer.h:127
struct gc_entry * list
First element of the linked list of gc_entry structures.
Definition buffer.h:128
union ip_tcp_udp_hdr::@23 u
struct openvpn_iphdr ip
Definition proto.h:197
struct openvpn_udphdr udp
Definition proto.h:201
struct openvpn_tcphdr tcp
Definition proto.h:200
uint32_t saddr
Definition proto.h:111
uint32_t daddr
Definition proto.h:112
uint8_t protocol
Definition proto.h:108
uint16_t check
Definition proto.h:110
uint16_t check
Definition proto.h:186
uint16_t check
Definition proto.h:159
struct gc_arena gc
Definition test_ssl.c:122