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