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-2018 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
add_entry(struct client_nat_option_list * dest,const struct client_nat_entry * e)38 add_entry(struct client_nat_option_list *dest,
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
print_client_nat_list(const struct client_nat_option_list * list,int msglevel)54 print_client_nat_list(const struct client_nat_option_list *list, int msglevel)
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,
68 print_in_addr_t(e->network, IA_NET_ORDER, &gc),
69 print_in_addr_t(e->netmask, IA_NET_ORDER, &gc),
70 print_in_addr_t(e->foreign_network, IA_NET_ORDER, &gc));
71 }
72 }
73 gc_free(&gc);
74 }
75
76 struct client_nat_option_list *
new_client_nat_list(struct gc_arena * gc)77 new_client_nat_list(struct gc_arena *gc)
78 {
79 struct client_nat_option_list *ret;
80 ALLOC_OBJ_CLEAR_GC(ret, struct client_nat_option_list, gc);
81 return ret;
82 }
83
84 struct client_nat_option_list *
clone_client_nat_option_list(const struct client_nat_option_list * src,struct gc_arena * gc)85 clone_client_nat_option_list(const struct client_nat_option_list *src, struct gc_arena *gc)
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
copy_client_nat_option_list(struct client_nat_option_list * dest,const struct client_nat_option_list * src)94 copy_client_nat_option_list(struct client_nat_option_list *dest,
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
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)108 add_client_nat_to_option_list(struct client_nat_option_list *dest,
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
print_pkt(struct openvpn_iphdr * iph,const char * prefix,const int direction,const int msglevel)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
client_nat_transform(const struct client_nat_option_list * list,struct buffer * ipbuf,const int direction)195 client_nat_transform(const struct client_nat_option_list *list,
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
207 if (check_debug_level(D_CLIENT_NAT))
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 {
256 if (check_debug_level(D_CLIENT_NAT))
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 }
279