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-2023 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, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #elif defined(_MSC_VER)
27 #include "config-msvc.h"
28 #endif
29 
30 #include "syshead.h"
31 
32 #include "clinat.h"
33 #include "proto.h"
34 #include "socket.h"
35 #include "memdbg.h"
36 
37 static bool
39  const struct client_nat_entry *e)
40 {
41  if (dest->n >= MAX_CLIENT_NAT)
42  {
43  msg(M_WARN, "WARNING: client-nat table overflow (max %d entries)", MAX_CLIENT_NAT);
44  return false;
45  }
46  else
47  {
48  dest->entries[dest->n++] = *e;
49  return true;
50  }
51 }
52 
53 void
55 {
56  struct gc_arena gc = gc_new();
57  int i;
58 
59  msg(msglevel, "*** CNAT list");
60  if (list)
61  {
62  for (i = 0; i < list->n; ++i)
63  {
64  const struct client_nat_entry *e = &list->entries[i];
65  msg(msglevel, " CNAT[%d] t=%d %s/%s/%s",
66  i,
67  e->type,
71  }
72  }
73  gc_free(&gc);
74 }
75 
78 {
79  struct client_nat_option_list *ret;
81  return ret;
82 }
83 
86 {
87  struct client_nat_option_list *ret;
88  ALLOC_OBJ_GC(ret, struct client_nat_option_list, gc);
89  *ret = *src;
90  return ret;
91 }
92 
93 void
95  const struct client_nat_option_list *src)
96 {
97  int i;
98  for (i = 0; i < src->n; ++i)
99  {
100  if (!add_entry(dest, &src->entries[i]))
101  {
102  break;
103  }
104  }
105 }
106 
107 void
109  const char *type,
110  const char *network,
111  const char *netmask,
112  const char *foreign_network,
113  int msglevel)
114 {
115  struct client_nat_entry e;
116  bool ok;
117 
118  if (!strcmp(type, "snat"))
119  {
120  e.type = CN_SNAT;
121  }
122  else if (!strcmp(type, "dnat"))
123  {
124  e.type = CN_DNAT;
125  }
126  else
127  {
128  msg(msglevel, "client-nat: type must be 'snat' or 'dnat'");
129  return;
130  }
131 
132  e.network = getaddr(0, network, 0, &ok, NULL);
133  if (!ok)
134  {
135  msg(msglevel, "client-nat: bad network: %s", network);
136  return;
137  }
138  e.netmask = getaddr(0, netmask, 0, &ok, NULL);
139  if (!ok)
140  {
141  msg(msglevel, "client-nat: bad netmask: %s", netmask);
142  return;
143  }
144  e.foreign_network = getaddr(0, foreign_network, 0, &ok, NULL);
145  if (!ok)
146  {
147  msg(msglevel, "client-nat: bad foreign network: %s", foreign_network);
148  return;
149  }
150 
151  add_entry(dest, &e);
152 }
153 
154 #if 0
155 static void
156 print_checksum(struct openvpn_iphdr *iph, const char *prefix)
157 {
158  uint16_t *sptr;
159  unsigned int sum = 0;
160  int i = 0;
161  for (sptr = (uint16_t *)iph; (uint8_t *)sptr < (uint8_t *)iph + sizeof(struct openvpn_iphdr); sptr++)
162  {
163  i += 1;
164  sum += *sptr;
165  }
166  msg(M_INFO, "** CKSUM[%d] %s %08x", i, prefix, sum);
167 }
168 #endif
169 
170 static void
171 print_pkt(struct openvpn_iphdr *iph, const char *prefix, const int direction, const int msglevel)
172 {
173  struct gc_arena gc = gc_new();
174 
175  char *dirstr = "???";
176  if (direction == CN_OUTGOING)
177  {
178  dirstr = "OUT";
179  }
180  else if (direction == CN_INCOMING)
181  {
182  dirstr = "IN";
183  }
184 
185  msg(msglevel, "** CNAT %s %s %s -> %s",
186  dirstr,
187  prefix,
188  print_in_addr_t(iph->saddr, IA_NET_ORDER, &gc),
189  print_in_addr_t(iph->daddr, IA_NET_ORDER, &gc));
190 
191  gc_free(&gc);
192 }
193 
194 void
196  struct buffer *ipbuf,
197  const int direction)
198 {
199  struct ip_tcp_udp_hdr *h = (struct ip_tcp_udp_hdr *) BPTR(ipbuf);
200  int i;
201  uint32_t addr, *addr_ptr;
202  const uint32_t *from, *to;
203  int accumulate = 0;
204  unsigned int amask;
205  unsigned int alog = 0;
206 
208  {
209  print_pkt(&h->ip, "BEFORE", direction, D_CLIENT_NAT);
210  }
211 
212  for (i = 0; i < list->n; ++i)
213  {
214  const struct client_nat_entry *e = &list->entries[i]; /* current NAT rule */
215  if (e->type ^ direction)
216  {
217  addr = *(addr_ptr = &h->ip.daddr);
218  amask = 2;
219  }
220  else
221  {
222  addr = *(addr_ptr = &h->ip.saddr);
223  amask = 1;
224  }
225  if (direction)
226  {
227  from = &e->foreign_network;
228  to = &e->network;
229  }
230  else
231  {
232  from = &e->network;
233  to = &e->foreign_network;
234  }
235 
236  if (((addr & e->netmask) == *from) && !(amask & alog))
237  {
238  /* pre-adjust IP checksum */
239  ADD_CHECKSUM_32(accumulate, addr);
240 
241  /* do NAT transform */
242  addr = (addr & ~e->netmask) | *to;
243 
244  /* post-adjust IP checksum */
245  SUB_CHECKSUM_32(accumulate, addr);
246 
247  /* write the modified address to packet */
248  *addr_ptr = addr;
249 
250  /* mark as modified */
251  alog |= amask;
252  }
253  }
254  if (alog)
255  {
257  {
258  print_pkt(&h->ip, "AFTER", direction, D_CLIENT_NAT);
259  }
260 
261  ADJUST_CHECKSUM(accumulate, h->ip.check);
262 
263  if (h->ip.protocol == OPENVPN_IPPROTO_TCP)
264  {
265  if (BLEN(ipbuf) >= sizeof(struct openvpn_iphdr) + sizeof(struct openvpn_tcphdr))
266  {
267  ADJUST_CHECKSUM(accumulate, h->u.tcp.check);
268  }
269  }
270  else if (h->ip.protocol == OPENVPN_IPPROTO_UDP)
271  {
272  if (BLEN(ipbuf) >= sizeof(struct openvpn_iphdr) + sizeof(struct openvpn_udphdr))
273  {
274  ADJUST_CHECKSUM(accumulate, h->u.udp.check);
275  }
276  }
277  }
278 }
gc_arena::list
struct gc_entry * list
First element of the linked list of gc_entry structures.
Definition: buffer.h:118
OPENVPN_IPPROTO_TCP
#define OPENVPN_IPPROTO_TCP
Definition: proto.h:121
M_INFO
#define M_INFO
Definition: errlevel.h:55
gc_new
static struct gc_arena gc_new(void)
Definition: buffer.h:1011
SUB_CHECKSUM_32
#define SUB_CHECKSUM_32(acc, u32)
Definition: proto.h:245
client_nat_option_list::n
int n
Definition: clinat.h:44
client_nat_option_list
Definition: clinat.h:43
ip_tcp_udp_hdr::ip
struct openvpn_iphdr ip
Definition: proto.h:208
OPENVPN_IPPROTO_UDP
#define OPENVPN_IPPROTO_UDP
Definition: proto.h:122
config-msvc.h
ADD_CHECKSUM_32
#define ADD_CHECKSUM_32(acc, u32)
Definition: proto.h:240
clinat.h
IA_NET_ORDER
#define IA_NET_ORDER
Definition: socket.h:389
CN_DNAT
#define CN_DNAT
Definition: clinat.h:36
clone_client_nat_option_list
struct client_nat_option_list * clone_client_nat_option_list(const struct client_nat_option_list *src, struct gc_arena *gc)
Definition: clinat.c:85
print_client_nat_list
void print_client_nat_list(const struct client_nat_option_list *list, int msglevel)
Definition: clinat.c:54
ip_tcp_udp_hdr::udp
struct openvpn_udphdr udp
Definition: proto.h:211
openvpn_udphdr
Definition: proto.h:168
openvpn_iphdr::daddr
uint32_t daddr
Definition: proto.h:128
CN_INCOMING
#define CN_INCOMING
Definition: clinat.h:32
client_nat_option_list::entries
struct client_nat_entry entries[MAX_CLIENT_NAT]
Definition: clinat.h:45
ADJUST_CHECKSUM
#define ADJUST_CHECKSUM(acc, cksum)
Definition: proto.h:225
getaddr
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:182
client_nat_entry::netmask
in_addr_t netmask
Definition: clinat.h:39
openvpn_iphdr::protocol
uint8_t protocol
Definition: proto.h:124
openvpn_iphdr::saddr
uint32_t saddr
Definition: proto.h:127
BLEN
#define BLEN(buf)
Definition: buffer.h:127
proto.h
openvpn_tcphdr
Definition: proto.h:178
M_WARN
#define M_WARN
Definition: error.h:97
openvpn_iphdr::check
uint16_t check
Definition: proto.h:126
ALLOC_OBJ_CLEAR_GC
#define ALLOC_OBJ_CLEAR_GC(dptr, type, gc)
Definition: buffer.h:1083
client_nat_entry::type
int type
Definition: clinat.h:37
openvpn_udphdr::check
uint16_t check
Definition: proto.h:172
copy_client_nat_option_list
void copy_client_nat_option_list(struct client_nat_option_list *dest, const struct client_nat_option_list *src)
Definition: clinat.c:94
buffer
Wrapper structure for dynamically allocated memory.
Definition: buffer.h:60
client_nat_entry::foreign_network
in_addr_t foreign_network
Definition: clinat.h:40
print_in_addr_t
const char * print_in_addr_t(in_addr_t addr, unsigned int flags, struct gc_arena *gc)
Definition: socket.c:2905
syshead.h
BPTR
#define BPTR(buf)
Definition: buffer.h:124
ip_tcp_udp_hdr::tcp
struct openvpn_tcphdr tcp
Definition: proto.h:210
gc_arena
Garbage collection arena used to keep track of dynamically allocated memory.
Definition: buffer.h:116
add_client_nat_to_option_list
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:108
new_client_nat_list
struct client_nat_option_list * new_client_nat_list(struct gc_arena *gc)
Definition: clinat.c:77
client_nat_transform
void client_nat_transform(const struct client_nat_option_list *list, struct buffer *ipbuf, const int direction)
Definition: clinat.c:195
check_debug_level
static bool check_debug_level(unsigned int level)
Definition: error.h:226
ip_tcp_udp_hdr::u
union ip_tcp_udp_hdr::@12 u
gc_free
static void gc_free(struct gc_arena *a)
Definition: buffer.h:1019
client_nat_entry::network
in_addr_t network
Definition: clinat.h:38
openvpn_tcphdr::check
uint16_t check
Definition: proto.h:198
socket.h
config.h
print_pkt
static void print_pkt(struct openvpn_iphdr *iph, const char *prefix, const int direction, const int msglevel)
Definition: clinat.c:171
client_nat_entry
Definition: clinat.h:34
MAX_CLIENT_NAT
#define MAX_CLIENT_NAT
Definition: clinat.h:29
openvpn_iphdr
Definition: proto.h:106
ip_tcp_udp_hdr
Definition: proto.h:207
memdbg.h
CN_OUTGOING
#define CN_OUTGOING
Definition: clinat.h:31
msg
#define msg(flags,...)
Definition: error.h:150
D_CLIENT_NAT
#define D_CLIENT_NAT
Definition: errlevel.h:116
CN_SNAT
#define CN_SNAT
Definition: clinat.h:35
add_entry
static bool add_entry(struct client_nat_option_list *dest, const struct client_nat_entry *e)
Definition: clinat.c:38
ALLOC_OBJ_GC
#define ALLOC_OBJ_GC(dptr, type, gc)
Definition: buffer.h:1078