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-2025 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
50print_client_nat_list(const struct client_nat_option_list *list, int msglevel)
51{
52 struct gc_arena gc = gc_new();
53 int i;
54
55 msg(msglevel, "*** CNAT list");
56 if (list)
57 {
58 for (i = 0; i < list->n; ++i)
59 {
60 const struct client_nat_entry *e = &list->entries[i];
61 msg(msglevel, " CNAT[%d] t=%d %s/%s/%s", i, e->type,
65 }
66 }
67 gc_free(&gc);
68}
69
72{
73 struct client_nat_option_list *ret;
75 return ret;
76}
77
80{
81 struct client_nat_option_list *ret;
83 *ret = *src;
84 return ret;
85}
86
87void
89 const struct client_nat_option_list *src)
90{
91 int i;
92 for (i = 0; i < src->n; ++i)
93 {
94 if (!add_entry(dest, &src->entries[i]))
95 {
96 break;
97 }
98 }
99}
100
101void
103 const char *network, const char *netmask, const char *foreign_network,
104 int msglevel)
105{
106 struct client_nat_entry e;
107 bool ok;
108
109 if (!strcmp(type, "snat"))
110 {
111 e.type = CN_SNAT;
112 }
113 else if (!strcmp(type, "dnat"))
114 {
115 e.type = CN_DNAT;
116 }
117 else
118 {
119 msg(msglevel, "client-nat: type must be 'snat' or 'dnat'");
120 return;
121 }
122
123 e.network = getaddr(0, network, 0, &ok, NULL);
124 if (!ok)
125 {
126 msg(msglevel, "client-nat: bad network: %s", network);
127 return;
128 }
129 e.netmask = getaddr(0, netmask, 0, &ok, NULL);
130 if (!ok)
131 {
132 msg(msglevel, "client-nat: bad netmask: %s", netmask);
133 return;
134 }
135 e.foreign_network = getaddr(0, foreign_network, 0, &ok, NULL);
136 if (!ok)
137 {
138 msg(msglevel, "client-nat: bad foreign network: %s", foreign_network);
139 return;
140 }
141
142 add_entry(dest, &e);
143}
144
145#if 0
146static void
147print_checksum(struct openvpn_iphdr *iph, const char *prefix)
148{
149 uint16_t *sptr;
150 unsigned int sum = 0;
151 int i = 0;
152 for (sptr = (uint16_t *)iph; (uint8_t *)sptr < (uint8_t *)iph + sizeof(struct openvpn_iphdr); sptr++)
153 {
154 i += 1;
155 sum += *sptr;
156 }
157 msg(M_INFO, "** CKSUM[%d] %s %08x", i, prefix, sum);
158}
159#endif
160
161static void
162print_pkt(struct openvpn_iphdr *iph, const char *prefix, const int direction, const int msglevel)
163{
164 struct gc_arena gc = gc_new();
165
166 char *dirstr = "???";
167 if (direction == CN_OUTGOING)
168 {
169 dirstr = "OUT";
170 }
171 else if (direction == CN_INCOMING)
172 {
173 dirstr = "IN";
174 }
175
176 msg(msglevel, "** CNAT %s %s %s -> %s", dirstr, prefix,
179
180 gc_free(&gc);
181}
182
183void
185 const int direction)
186{
187 struct ip_tcp_udp_hdr *h = (struct ip_tcp_udp_hdr *)BPTR(ipbuf);
188 int i;
189 uint32_t addr, *addr_ptr;
190 const uint32_t *from, *to;
191 int accumulate = 0;
192 unsigned int amask;
193 unsigned int alog = 0;
194
196 {
197 print_pkt(&h->ip, "BEFORE", direction, D_CLIENT_NAT);
198 }
199
200 for (i = 0; i < list->n; ++i)
201 {
202 const struct client_nat_entry *e = &list->entries[i]; /* current NAT rule */
203 if (e->type ^ direction)
204 {
205 addr = *(addr_ptr = &h->ip.daddr);
206 amask = 2;
207 }
208 else
209 {
210 addr = *(addr_ptr = &h->ip.saddr);
211 amask = 1;
212 }
213 if (direction)
214 {
215 from = &e->foreign_network;
216 to = &e->network;
217 }
218 else
219 {
220 from = &e->network;
221 to = &e->foreign_network;
222 }
223
224 if (((addr & e->netmask) == *from) && !(amask & alog))
225 {
226 /* pre-adjust IP checksum */
227 ADD_CHECKSUM_32(accumulate, addr);
228
229 /* do NAT transform */
230 addr = (addr & ~e->netmask) | *to;
231
232 /* post-adjust IP checksum */
233 SUB_CHECKSUM_32(accumulate, addr);
234
235 /* write the modified address to packet */
236 *addr_ptr = addr;
237
238 /* mark as modified */
239 alog |= amask;
240 }
241 }
242 if (alog)
243 {
245 {
246 print_pkt(&h->ip, "AFTER", direction, D_CLIENT_NAT);
247 }
248
249 ADJUST_CHECKSUM(accumulate, h->ip.check);
250
252 {
253 if (BLEN(ipbuf) >= sizeof(struct openvpn_iphdr) + sizeof(struct openvpn_tcphdr))
254 {
255 ADJUST_CHECKSUM(accumulate, h->u.tcp.check);
256 }
257 }
258 else if (h->ip.protocol == OPENVPN_IPPROTO_UDP)
259 {
260 if (BLEN(ipbuf) >= sizeof(struct openvpn_iphdr) + sizeof(struct openvpn_udphdr))
261 {
262 ADJUST_CHECKSUM(accumulate, h->u.udp.check);
263 }
264 }
265 }
266}
#define BPTR(buf)
Definition buffer.h:123
#define ALLOC_OBJ_CLEAR_GC(dptr, type, gc)
Definition buffer.h:1079
#define BLEN(buf)
Definition buffer.h:126
#define ALLOC_OBJ_GC(dptr, type, gc)
Definition buffer.h:1074
static void gc_free(struct gc_arena *a)
Definition buffer.h:1015
static struct gc_arena gc_new(void)
Definition buffer.h:1007
void print_client_nat_list(const struct client_nat_option_list *list, int msglevel)
Definition clinat.c:50
void client_nat_transform(const struct client_nat_option_list *list, struct buffer *ipbuf, const int direction)
Definition clinat.c:184
static void print_pkt(struct openvpn_iphdr *iph, const char *prefix, const int direction, const int msglevel)
Definition clinat.c:162
static bool add_entry(struct client_nat_option_list *dest, const struct client_nat_entry *e)
Definition clinat.c:35
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, int msglevel)
Definition clinat.c:102
struct client_nat_option_list * new_client_nat_list(struct gc_arena *gc)
Definition clinat.c:71
struct client_nat_option_list * clone_client_nat_option_list(const struct client_nat_option_list *src, struct gc_arena *gc)
Definition clinat.c:79
void copy_client_nat_option_list(struct client_nat_option_list *dest, const struct client_nat_option_list *src)
Definition clinat.c:88
#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(unsigned int level)
Definition error.h:257
#define msg(flags,...)
Definition error.h:150
#define M_WARN
Definition error.h:90
#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:187
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:60
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:116
struct gc_entry * list
First element of the linked list of gc_entry structures.
Definition buffer.h:117
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:154