581bef87 |
/*
* OpenVPN -- An application to securely tunnel IP networks
* over a single TCP/UDP port, with support for SSL/TLS-based
* session authentication and key exchange,
* packet encryption, packet authentication, and
* packet compression.
* |
58716979 |
* Copyright (C) 2002-2017 OpenVPN Technologies, Inc. <sales@openvpn.net> |
581bef87 |
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* |
caa54ac3 |
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
581bef87 |
*/
|
c110b289 |
#ifdef HAVE_CONFIG_H
#include "config.h"
#elif defined(_MSC_VER)
#include "config-msvc.h"
#endif
|
581bef87 |
#include "syshead.h"
#include "clinat.h"
#include "proto.h"
#include "socket.h"
#include "memdbg.h"
static bool
add_entry(struct client_nat_option_list *dest, |
81d882d5 |
const struct client_nat_entry *e) |
581bef87 |
{ |
81d882d5 |
if (dest->n >= MAX_CLIENT_NAT) |
581bef87 |
{ |
81d882d5 |
msg(M_WARN, "WARNING: client-nat table overflow (max %d entries)", MAX_CLIENT_NAT);
return false; |
581bef87 |
} |
81d882d5 |
else |
581bef87 |
{ |
81d882d5 |
dest->entries[dest->n++] = *e;
return true; |
581bef87 |
}
}
void
print_client_nat_list(const struct client_nat_option_list *list, int msglevel)
{ |
81d882d5 |
struct gc_arena gc = gc_new();
int i; |
581bef87 |
|
81d882d5 |
msg(msglevel, "*** CNAT list");
if (list) |
581bef87 |
{ |
81d882d5 |
for (i = 0; i < list->n; ++i)
{
const struct client_nat_entry *e = &list->entries[i];
msg(msglevel, " CNAT[%d] t=%d %s/%s/%s",
i,
e->type,
print_in_addr_t(e->network, IA_NET_ORDER, &gc),
print_in_addr_t(e->netmask, IA_NET_ORDER, &gc),
print_in_addr_t(e->foreign_network, IA_NET_ORDER, &gc));
} |
581bef87 |
} |
81d882d5 |
gc_free(&gc); |
581bef87 |
}
struct client_nat_option_list * |
81d882d5 |
new_client_nat_list(struct gc_arena *gc) |
581bef87 |
{ |
81d882d5 |
struct client_nat_option_list *ret;
ALLOC_OBJ_CLEAR_GC(ret, struct client_nat_option_list, gc);
return ret; |
581bef87 |
}
struct client_nat_option_list * |
81d882d5 |
clone_client_nat_option_list(const struct client_nat_option_list *src, struct gc_arena *gc) |
581bef87 |
{ |
81d882d5 |
struct client_nat_option_list *ret;
ALLOC_OBJ_GC(ret, struct client_nat_option_list, gc);
*ret = *src;
return ret; |
581bef87 |
}
void |
81d882d5 |
copy_client_nat_option_list(struct client_nat_option_list *dest,
const struct client_nat_option_list *src) |
581bef87 |
{ |
81d882d5 |
int i;
for (i = 0; i < src->n; ++i) |
581bef87 |
{ |
81d882d5 |
if (!add_entry(dest, &src->entries[i]))
{
break;
} |
581bef87 |
}
}
void |
81d882d5 |
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) |
581bef87 |
{ |
81d882d5 |
struct client_nat_entry e;
bool ok; |
581bef87 |
|
81d882d5 |
if (!strcmp(type, "snat")) |
581bef87 |
{ |
81d882d5 |
e.type = CN_SNAT;
}
else if (!strcmp(type, "dnat"))
{
e.type = CN_DNAT;
}
else
{
msg(msglevel, "client-nat: type must be 'snat' or 'dnat'");
return; |
581bef87 |
}
|
81d882d5 |
e.network = getaddr(0, network, 0, &ok, NULL);
if (!ok) |
581bef87 |
{ |
81d882d5 |
msg(msglevel, "client-nat: bad network: %s", network);
return; |
581bef87 |
} |
81d882d5 |
e.netmask = getaddr(0, netmask, 0, &ok, NULL);
if (!ok) |
581bef87 |
{ |
81d882d5 |
msg(msglevel, "client-nat: bad netmask: %s", netmask);
return; |
581bef87 |
} |
81d882d5 |
e.foreign_network = getaddr(0, foreign_network, 0, &ok, NULL);
if (!ok) |
581bef87 |
{ |
81d882d5 |
msg(msglevel, "client-nat: bad foreign network: %s", foreign_network);
return; |
581bef87 |
}
|
81d882d5 |
add_entry(dest, &e); |
581bef87 |
}
#if 0
static void |
81d882d5 |
print_checksum(struct openvpn_iphdr *iph, const char *prefix) |
581bef87 |
{ |
81d882d5 |
uint16_t *sptr;
unsigned int sum = 0;
int i = 0;
for (sptr = (uint16_t *)iph; (uint8_t *)sptr < (uint8_t *)iph + sizeof(struct openvpn_iphdr); sptr++) |
581bef87 |
{ |
81d882d5 |
i += 1;
sum += *sptr; |
581bef87 |
} |
81d882d5 |
msg(M_INFO, "** CKSUM[%d] %s %08x", i, prefix, sum); |
581bef87 |
}
#endif
static void |
81d882d5 |
print_pkt(struct openvpn_iphdr *iph, const char *prefix, const int direction, const int msglevel) |
581bef87 |
{ |
81d882d5 |
struct gc_arena gc = gc_new(); |
581bef87 |
|
81d882d5 |
char *dirstr = "???";
if (direction == CN_OUTGOING)
{
dirstr = "OUT";
}
else if (direction == CN_INCOMING)
{
dirstr = "IN";
}
msg(msglevel, "** CNAT %s %s %s -> %s",
dirstr,
prefix,
print_in_addr_t(iph->saddr, IA_NET_ORDER, &gc),
print_in_addr_t(iph->daddr, IA_NET_ORDER, &gc)); |
581bef87 |
|
81d882d5 |
gc_free(&gc); |
581bef87 |
}
void |
81d882d5 |
client_nat_transform(const struct client_nat_option_list *list,
struct buffer *ipbuf,
const int direction) |
581bef87 |
{ |
81d882d5 |
struct ip_tcp_udp_hdr *h = (struct ip_tcp_udp_hdr *) BPTR(ipbuf);
int i;
uint32_t addr, *addr_ptr;
const uint32_t *from, *to;
int accumulate = 0;
unsigned int amask;
unsigned int alog = 0; |
581bef87 |
|
81d882d5 |
if (check_debug_level(D_CLIENT_NAT))
{
print_pkt(&h->ip, "BEFORE", direction, D_CLIENT_NAT);
} |
581bef87 |
|
81d882d5 |
for (i = 0; i < list->n; ++i) |
581bef87 |
{ |
81d882d5 |
const struct client_nat_entry *e = &list->entries[i]; /* current NAT rule */
if (e->type ^ direction)
{
addr = *(addr_ptr = &h->ip.daddr);
amask = 2;
}
else
{
addr = *(addr_ptr = &h->ip.saddr);
amask = 1;
}
if (direction)
{
from = &e->foreign_network;
to = &e->network;
}
else
{
from = &e->network;
to = &e->foreign_network;
} |
581bef87 |
|
81d882d5 |
if (((addr & e->netmask) == *from) && !(amask & alog))
{
/* pre-adjust IP checksum */
ADD_CHECKSUM_32(accumulate, addr); |
581bef87 |
|
81d882d5 |
/* do NAT transform */
addr = (addr & ~e->netmask) | *to; |
581bef87 |
|
81d882d5 |
/* post-adjust IP checksum */
SUB_CHECKSUM_32(accumulate, addr); |
581bef87 |
|
81d882d5 |
/* write the modified address to packet */
*addr_ptr = addr; |
581bef87 |
|
81d882d5 |
/* mark as modified */
alog |= amask;
} |
581bef87 |
} |
81d882d5 |
if (alog) |
581bef87 |
{ |
81d882d5 |
if (check_debug_level(D_CLIENT_NAT))
{
print_pkt(&h->ip, "AFTER", direction, D_CLIENT_NAT);
} |
581bef87 |
|
81d882d5 |
ADJUST_CHECKSUM(accumulate, h->ip.check); |
581bef87 |
|
81d882d5 |
if (h->ip.protocol == OPENVPN_IPPROTO_TCP)
{
if (BLEN(ipbuf) >= sizeof(struct openvpn_iphdr) + sizeof(struct openvpn_tcphdr))
{
ADJUST_CHECKSUM(accumulate, h->u.tcp.check);
}
}
else if (h->ip.protocol == OPENVPN_IPPROTO_UDP)
{
if (BLEN(ipbuf) >= sizeof(struct openvpn_iphdr) + sizeof(struct openvpn_udphdr))
{
ADJUST_CHECKSUM(accumulate, h->u.udp.check);
}
} |
581bef87 |
}
} |