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