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