Dear Developers,
I developed a patch for implementing 1:1 NAT (something similar to the iptables
NETMAP target).
This is useful in situations when you have the same (private) network address
behind clients.
For example, consider the following scenario:
-lan1--192.168.0.0/24-- -lan2--192.168.0.0/24--
| |
gw1 192.168.0.1 gw2 192.168.0.1
| |
[tunnel]-----OpenVPN server---[tunnel]
|
[tunnel]
|
clients…
The clients have to access to both the machines in lan1 and lan2,
This patch allow to map all the address of a network, e.g.
[to g1] push "netmap 172.16.1.0/24 192.168.0.0/24"
[to g2] push "netmap 172.16.2.0/24 192.168.0.0/24"
The clients can access to, e.g. 192.168.0.79 on lan1 using the IP 172.16.1.79.
Regards,
Andrea
:: e n d i a n
:: security with passion
:: andrea bonomi
:: http://www.endian.com :: [email protected]
Signed-off-by: Andrea Bonomi <[email protected]>
---
src/openvpn/Makefile.am | 1 +
src/openvpn/forward.c | 10 +-
src/openvpn/netmap.c | 238 +++++++++++++++++++++++++++++++++++
src/openvpn/netmap.h | 53 ++++++++
src/openvpn/openvpn.vcxproj | 1 +
src/openvpn/openvpn.vcxproj.filters | 3 +
src/openvpn/options.c | 19 +++
src/openvpn/options.h | 10 ++
8 files changed, 333 insertions(+), 2 deletions(-)
create mode 100644 src/openvpn/netmap.c
create mode 100644 src/openvpn/netmap.h
diff --git a/src/openvpn/Makefile.am b/src/openvpn/Makefile.am
index 5d38628..eee5117 100644
--- a/src/openvpn/Makefile.am
+++ b/src/openvpn/Makefile.am
@@ -73,6 +73,7 @@ openvpn_SOURCES = \
mtu.c mtu.h \
mudp.c mudp.h \
multi.c multi.h \
+ netmap.c netmap.h \
ntlm.c ntlm.h \
occ.c occ.h occ-inline.h \
pkcs11.c pkcs11.h pkcs11_backend.h \
diff --git a/src/openvpn/forward.c b/src/openvpn/forward.c
index 57c7846..fc52cae 100644
--- a/src/openvpn/forward.c
+++ b/src/openvpn/forward.c
@@ -1026,15 +1026,21 @@ process_ipv4_header (struct context *c, unsigned int
flags, struct buffer *buf)
* The --passtos and --mssfix options require
* us to examine the IPv4 header.
*/
+
#if PASSTOS_CAPABILITY
- if (flags & (PIPV4_PASSTOS|PIPV4_MSSFIX))
+ if (flags & (PIPV4_PASSTOS|PIPV4_MSSFIX) || c->options.netmap_enabled)
#else
- if (flags & PIPV4_MSSFIX)
+ if (flags & PIPV4_MSSFIX || c->options.netmap_enabled)
#endif
{
struct buffer ipbuf = *buf;
if (is_ipv4 (TUNNEL_TYPE (c->c1.tuntap), &ipbuf))
{
+ if (c->options.netmap_enabled)
+ {
+ do_netmap(&c->options.netmaps, flags, &ipbuf);
+ }
+
#if PASSTOS_CAPABILITY
/* extract TOS from IP header */
if (flags & PIPV4_PASSTOS)
diff --git a/src/openvpn/netmap.c b/src/openvpn/netmap.c
new file mode 100644
index 0000000..52fe25b
--- /dev/null
+++ b/src/openvpn/netmap.c
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2012 Endian
+ * Endian GmbH/Srl
+ * Bergweg 41 Via Monte
+ * 39057 Eppan/Appiano
+ * ITALIEN/ITALIA
+ * [email protected]
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Andrea Bonomi <[email protected]>
+ */
+
+#include "syshead.h"
+#include "common.h"
+#include "error.h"
+#include "netmap.h"
+#include "socket.h"
+#include "proto.h"
+#include "forward.h"
+
+
+/**
+ * Parse a network address in CIDR notation or an IP address
+ */
+static bool
+parse_cidr (const char *cidr, int *addr, int *mask, bool *has_mask)
+{
+ unsigned int a, b, c, d, netmask, l;
+ *addr = 0;
+ *mask = 0;
+ *has_mask = false;
+ l = sscanf (cidr, "%u.%u.%u.%u/%u", &a, &b, &c, &d, &netmask);
+ if (l == 4 || l == 5) {
+ if (l == 4) {
+ netmask = 0;
+ }
+ if (a < 256 && b < 256 && c < 256 && d < 256 && netmask <= 32) {
+ uint32_t uaddr = a<<24 | b<<16 | c<<8 | d;
+ if (l == 4) { /* the netmask was not specified */
+ *addr = htonl (uaddr);
+ } else {
+ *mask = htonl((0xffffffff >> (32 - netmask )) << (32 - netmask));
+ *addr = htonl (uaddr) & *mask;
+ *has_mask = true;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+/*
+ * Calculate the IP checksum and update the IP header
+ */
+
+static void
+update_ip_checksum(struct openvpn_iphdr *iph) {
+ const char *t = (const char *)iph;
+ const char *end = t + OPENVPN_IPH_GET_LEN (iph->version_len) - 1;
+ uint32_t sum = 0;
+ iph->check = 0;
+
+ while (t < end) {
+ sum += (((char)*t << 8) & 0xff00);
+ t++;
+ sum += (char)*t & 0xff;
+ t++;
+ if (sum > 0xffff)
+ sum = (sum & 0xffff) + (sum >> 16);
+ }
+
+ iph->check = htons((uint16_t) ~sum);
+}
+
+/*
+ * Check if the source/destination address have to be remapped.
+ * If the package direction is outgoing change the source address,
+ * otherwise change the destination address.
+ */
+
+void
+do_netmap (struct netmap_option_list *netmaps, unsigned int flags, struct
buffer *ipbuf) {
+ struct openvpn_iphdr *iph = (struct openvpn_iphdr *) BPTR (ipbuf);
+ struct netmap_option *n;
+ bool package_updated = false;
+ int i;
+
+#ifdef NETMAP_DEBUG
+ struct in_addr s;
+ struct in_addr d;
+ s.s_addr = iph->saddr;
+ d.s_addr = iph->daddr;
+ msg (M_INFO, "outgoing: %c", flags & PIPV4_OUTGOING ? 'y' : 'n');
+ msg (M_INFO, "source: %s 0x%08x", inet_ntoa(s), iph->saddr);
+ msg (M_INFO, "destination: %s 0x%08x", inet_ntoa(d), iph->daddr);
+ msg (M_INFO, "netmaps size: %d", netmaps ? netmaps->size : -1);
+#endif
+
+ if (!netmaps) {
+ return;
+ }
+
+ if (flags & PIPV4_OUTGOING) { /* outgoing (received package) */
+ for (i = 0, n = netmaps->netmaps; i < netmaps->size; i++, n++) {
+
+#ifdef NETMAP_DEBUG
+ msg (M_INFO, "netmap %d#: destination: 0x%08x source: 0x%08x netmap:
0x%08x", i, n->destination, n->source, n->netmap_mask);
+#endif
+
+ if (n->destination == (iph->saddr & n->netmap_mask)) {
+ iph->saddr = n->source | (iph->saddr & ~n->netmap_mask);
+ package_updated = true;
+#ifdef NETMAP_DEBUG
+ s.s_addr = iph->saddr;
+ msg (M_INFO, "netmap %d#: source changed to: %s 0x%08x", i,
inet_ntoa(s), iph->saddr);
+#endif
+ }
+
+ if (n->destination == (iph->daddr & n->netmap_mask)) {
+ iph->daddr = n->source | (iph->daddr & ~n->netmap_mask);
+ package_updated = true;
+#ifdef NETMAP_DEBUG
+ d.s_addr = iph->daddr;
+ msg (M_INFO, "netmap %d#: destination changed to: %s 0x%08x", i,
inet_ntoa(d), iph->daddr);
+#endif
+ }
+
+ }
+ } else { /* incoming (package to be sent) */
+
+ for (i = 0, n = netmaps->netmaps; i < netmaps->size; i++, n++) {
+
+#ifdef NETMAP_DEBUG
+ msg (M_INFO, "netmap %d#: destination: 0x%08x source: 0x%08x netmap:
0x%08x", i, n->destination, n->source, n->netmap_mask);
+#endif
+
+ if (n->source == (iph->saddr & n->netmap_mask)) {
+ iph->saddr = n->destination | (iph->saddr & ~n->netmap_mask);
+ package_updated = true;
+#ifdef NETMAP_DEBUG
+ s.s_addr = iph->saddr;
+ msg (M_INFO, "netmap %d#: source changed to: %s 0x%08x", i,
inet_ntoa(s), iph->saddr);
+#endif
+ }
+
+ if (n->source == (iph->daddr & n->netmap_mask)) {
+ iph->daddr = n->destination | (iph->daddr & ~n->netmap_mask);
+ package_updated = true;
+#ifdef NETMAP_DEBUG
+ d.s_addr = iph->daddr;
+ msg (M_INFO, "netmap %d#: destination changed to: %s 0x%08x", i,
inet_ntoa(d), iph->daddr);
+#endif
+ }
+
+ }
+
+ }
+
+ if (package_updated) {
+ update_ip_checksum(iph);
+ }
+
+}
+
+/*
+ * Add a new netmap option
+ */
+bool
+add_netmap_to_option_list (struct netmap_option_list *netmaps, const char
*source,
+ const char *destination, const char *netmask, int
msglevel)
+{
+ struct netmap_option *no;
+ int netmask1;
+ bool has_netmask1;
+ int netmask2;
+ bool has_netmask2;
+ if (netmaps->size >= MAX_NETMAPS_DEFAULT) {
+ msg (M_FATAL, PACKAGE_NAME " NETMAP: cannot add more than %d netmaps",
MAX_NETMAPS_DEFAULT);
+ }
+ no = &netmaps->netmaps[netmaps->size];
+
+ if (!source || !parse_cidr(source, &no->source, &netmask1, &has_netmask1)) {
+ msg (msglevel, " NETMAP: Cannot parse source IP: %s", source);
+ return false;
+ }
+
+ if (!destination || !parse_cidr(destination, &no->destination, &netmask2,
&has_netmask2)) {
+ msg (msglevel, " NETMAP: Cannot parse destination IP: %s", destination);
+ return false;
+ }
+
+ if (netmask1 != netmask2) {
+ msg (msglevel, " NETMAP: Source and destination netmasks must be the
same");
+ return false;
+ }
+
+ if (!netmask) {
+ /* netmap not specified, use the netmask from the cidr or the default
netmask */
+ if (has_netmask1) {
+ no->netmap_mask = netmask1;
+ } else {
+ /* default netmask 255.255.255.255 */
+ no->netmap_mask = 0xffffffff;
+ }
+
+ } else {
+ struct in_addr addr;
+ if (openvpn_inet_aton(netmask, &addr) != OIA_IP) {
+ msg (msglevel, " NETMAP: Cannot parse netmask: %s", netmask);
+ return false;
+ }
+
+ if (netmask1 != -1 && htonl((0xffffffff >> (32 - netmask1 )) << (32 -
netmask1) != addr.s_addr)) {
+ msg (msglevel, " NETMAP: You canot use both CIDR and dot-decimal
netmasks");
+ return false;
+ }
+
+ no->netmap_mask = addr.s_addr;
+ no->source = no->source & no->netmap_mask;
+ no->destination = no->destination & no->netmap_mask;
+ }
+
+ netmaps->size++;
+ return true;
+}
diff --git a/src/openvpn/netmap.h b/src/openvpn/netmap.h
new file mode 100644
index 0000000..24f9a2b
--- /dev/null
+++ b/src/openvpn/netmap.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2012 Endian
+ * Endian GmbH/Srl
+ * Bergweg 41 Via Monte
+ * 39057 Eppan/Appiano
+ * ITALIEN/ITALIA
+ * [email protected]
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Andrea Bonomi <[email protected]>
+ */
+
+/*
+ * Support routines for adding/deleting network maps.
+ */
+
+#ifndef NETMAP_H
+#define NETMAP_H
+
+#include "buffer.h"
+
+/* #define NETMAP_DEBUG */
+
+#define MAX_NETMAPS_DEFAULT 32
+
+struct netmap_option {
+ uint32_t source;
+ uint32_t destination;
+ uint32_t netmap_mask;
+};
+
+struct netmap_option_list {
+ int size;
+ struct netmap_option netmaps[MAX_NETMAPS_DEFAULT];
+};
+
+void do_netmap (struct netmap_option_list *netmaps, unsigned int flags, struct
buffer *ipbuf);
+bool add_netmap_to_option_list (struct netmap_option_list *netmaps, const char
*source, const char *destination, const char *netmask, int msglevel);
+
+#endif
diff --git a/src/openvpn/openvpn.vcxproj b/src/openvpn/openvpn.vcxproj
index 3b2340e..633b881 100755
--- a/src/openvpn/openvpn.vcxproj
+++ b/src/openvpn/openvpn.vcxproj
@@ -128,6 +128,7 @@
<ClCompile Include="mtu.c" />
<ClCompile Include="mudp.c" />
<ClCompile Include="multi.c" />
+ <ClCompile Include="netmap.c" />
<ClCompile Include="ntlm.c" />
<ClCompile Include="occ.c" />
<ClCompile Include="openvpn.c" />
diff --git a/src/openvpn/openvpn.vcxproj.filters
b/src/openvpn/openvpn.vcxproj.filters
index 40336ba..4b26efb 100644
--- a/src/openvpn/openvpn.vcxproj.filters
+++ b/src/openvpn/openvpn.vcxproj.filters
@@ -108,6 +108,9 @@
<ClCompile Include="multi.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="netmap.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="ntlm.c">
<Filter>Source Files</Filter>
</ClCompile>
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index d25bbea..7e5684e 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -239,6 +239,9 @@ static const char usage_message[] =
" Add 'bypass-dns' flag to similarly bypass tunnel for
DNS.\n"
"--redirect-private [flags]: Like --redirect-gateway, but omit actually
changing\n"
" the default gateway. Useful when pushing private
subnets.\n"
+ "--netmap source dest [netmask]: Add a 1:1 mapping of network address\n"
+ " 'source' is the local network to be remapped into
'dest'\n"
+ " on the other side of the tunnel\n"
#ifdef ENABLE_CLIENT_NAT
"--client-nat snat|dnat network netmask alias : on client add 1-to-1 NAT
rule.\n"
#endif
@@ -870,6 +873,7 @@ init_options (struct options *o, const bool init_gc)
}
#endif /* WIN32 */
#endif /* P2MP_SERVER */
+ o->netmaps.size = 0;
}
void
@@ -2759,6 +2763,9 @@ pre_pull_save (struct options *o)
o->pre_pull->tuntap_options = o->tuntap_options;
o->pre_pull->tuntap_options_defined = true;
o->pre_pull->foreign_option_index = o->foreign_option_index;
+ o->pre_pull->netmaps = o->netmaps;
+ o->pre_pull->netmaps_defined = true;
+
if (o->routes)
{
o->pre_pull->routes = clone_route_option_list(o->routes, &o->gc);
@@ -2789,6 +2796,10 @@ pre_pull_restore (struct options *o)
if (pp->tuntap_options_defined)
o->tuntap_options = pp->tuntap_options;
+ CLEAR (o->netmaps);
+ if (pp->netmaps_defined)
+ o->netmaps = pp->netmaps;
+
if (pp->routes_defined)
{
rol_check_alloc (o);
@@ -5115,6 +5126,14 @@ add_option (struct options *options,
}
options->max_routes = max_routes;
}
+ else if (streq (p[0], "netmap") && p[1])
+ {
+ VERIFY_PERMISSION (OPT_P_ROUTE);
+ if (!add_netmap_to_option_list(&options->netmaps, p[1], p[2], p[3],
msglevel)) {
+ goto err;
+ }
+ options->netmap_enabled = true;
+ }
else if (streq (p[0], "route-gateway") && p[1])
{
VERIFY_PERMISSION (OPT_P_ROUTE_EXTRAS);
diff --git a/src/openvpn/options.h b/src/openvpn/options.h
index 306520b..7b697d5 100644
--- a/src/openvpn/options.h
+++ b/src/openvpn/options.h
@@ -34,6 +34,7 @@
#include "common.h"
#include "mtu.h"
#include "route.h"
+#include "netmap.h"
#include "tun.h"
#include "socket.h"
#include "plugin.h"
@@ -76,6 +77,9 @@ struct options_pre_pull
struct client_nat_option_list *client_nat;
#endif
+ bool netmaps_defined;
+ struct netmap_option_list netmaps;
+
int foreign_option_index;
};
@@ -347,6 +351,11 @@ struct options
struct client_nat_option_list *client_nat;
#endif
+ /** Network map */
+ int max_netmaps;
+ struct netmap_option_list netmaps;
+ bool netmap_enabled;
+
#ifdef ENABLE_OCC
/* Enable options consistency check between peers */
bool occ;
@@ -624,6 +633,7 @@ struct options
#define OPT_P_SOCKBUF (1<<25)
#define OPT_P_SOCKFLAGS (1<<26)
#define OPT_P_CONNECTION (1<<27)
+#define OPT_P_NETMAP (1<<28)
#define OPT_P_DEFAULT (~(OPT_P_INSTANCE|OPT_P_PULL_MODE))
--
1.7.9.5