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