summaryrefslogtreecommitdiffstats
path: root/src/VBox/NetworkServices/NAT/pxremap.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/NetworkServices/NAT/pxremap.c')
-rw-r--r--src/VBox/NetworkServices/NAT/pxremap.c333
1 files changed, 333 insertions, 0 deletions
diff --git a/src/VBox/NetworkServices/NAT/pxremap.c b/src/VBox/NetworkServices/NAT/pxremap.c
new file mode 100644
index 00000000..625cefc8
--- /dev/null
+++ b/src/VBox/NetworkServices/NAT/pxremap.c
@@ -0,0 +1,333 @@
+/* $Id: pxremap.c $ */
+/** @file
+ * NAT Network - Loopback remapping.
+ */
+
+/*
+ * Copyright (C) 2013-2022 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * 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; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This file contains functions pertinent to magic address remapping.
+ *
+ * We want to expose host's loopback interfaces to the guest by
+ * mapping them to the addresses from the same prefix/subnet, so if,
+ * for example proxy interface is 10.0.2.1, we redirect traffic to
+ * 10.0.2.2 to host's 127.0.0.1 loopback. If need be, we may extend
+ * this to provide additional mappings, e.g. 127.0.1.1 loopback
+ * address is used on Ubuntu 12.10+ for NetworkManager's dnsmasq.
+ *
+ * Ditto for IPv6, except that IPv6 only has one loopback address.
+ */
+#define LOG_GROUP LOG_GROUP_NAT_SERVICE
+
+#include "winutils.h"
+#include "pxremap.h"
+#include "proxy.h"
+
+#include "lwip/netif.h"
+#include "netif/etharp.h" /* proxy arp hook */
+
+#include "lwip/ip4.h" /* IPv4 divert hook */
+#include "lwip/ip6.h" /* IPv6 divert hook */
+
+#include <string.h>
+
+
+/**
+ * Check if "dst" is an IPv4 address that proxy remaps to host's
+ * loopback.
+ */
+static int
+proxy_ip4_is_mapped_loopback(struct netif *netif, const ip_addr_t *dst, ip_addr_t *lo)
+{
+ u32_t off;
+ const struct ip4_lomap *lomap;
+ size_t i;
+
+ LWIP_ASSERT1(dst != NULL);
+
+ if (g_proxy_options->lomap_desc == NULL) {
+ return 0;
+ }
+
+ if (!ip_addr_netcmp(dst, &netif->ip_addr, &netif->netmask)) {
+ return 0;
+ }
+
+ /* XXX: TODO: check netif is a proxying netif! */
+
+ off = ntohl(ip4_addr_get_u32(dst) & ~ip4_addr_get_u32(&netif->netmask));
+ lomap = g_proxy_options->lomap_desc->lomap;
+ for (i = 0; i < g_proxy_options->lomap_desc->num_lomap; ++i) {
+ if (off == lomap[i].off) {
+ if (lo != NULL) {
+ ip_addr_copy(*lo, lomap[i].loaddr);
+ }
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+#if ARP_PROXY
+/**
+ * Hook function for etharp_arp_input() - returns true to cause proxy
+ * ARP reply to be generated for "dst".
+ */
+int
+pxremap_proxy_arp(struct netif *netif, ip_addr_t *dst)
+{
+ return proxy_ip4_is_mapped_loopback(netif, dst, NULL);
+}
+#endif /* ARP_PROXY */
+
+
+/**
+ * Hook function for ip_forward() - returns true to divert packets to
+ * "dst" to proxy (instead of forwarding them via "netif" or dropping).
+ */
+int
+pxremap_ip4_divert(struct netif *netif, ip_addr_t *dst)
+{
+ return proxy_ip4_is_mapped_loopback(netif, dst, NULL);
+}
+
+
+/**
+ * Mapping from local network to loopback for outbound connections.
+ *
+ * Copy "src" to "dst" with ip_addr_set(dst, src), but if "src" is a
+ * local network address that maps host's loopback address, copy
+ * loopback address to "dst".
+ */
+int
+pxremap_outbound_ip4(ip_addr_t *dst, ip_addr_t *src)
+{
+ struct netif *netif;
+
+ LWIP_ASSERT1(dst != NULL);
+ LWIP_ASSERT1(src != NULL);
+
+ for (netif = netif_list; netif != NULL; netif = netif->next) {
+ if (netif_is_up(netif) /* && this is a proxy netif */) {
+ if (proxy_ip4_is_mapped_loopback(netif, src, dst)) {
+ return PXREMAP_MAPPED;
+ }
+ }
+ }
+
+ /* not remapped, just copy src */
+ ip_addr_set(dst, src);
+ return PXREMAP_ASIS;
+}
+
+
+/**
+ * Mapping from loopback to local network for inbound (port-forwarded)
+ * connections.
+ *
+ * Copy "src" to "dst" with ip_addr_set(dst, src), but if "src" is a
+ * host's loopback address, copy local network address that maps it to
+ * "dst".
+ */
+int
+pxremap_inbound_ip4(ip_addr_t *dst, ip_addr_t *src)
+{
+ struct netif *netif;
+ const struct ip4_lomap *lomap;
+ unsigned int i;
+
+ if (ip4_addr1(src) != IP_LOOPBACKNET) {
+ ip_addr_set(dst, src);
+ return PXREMAP_ASIS;
+ }
+
+ if (g_proxy_options->lomap_desc == NULL) {
+ return PXREMAP_FAILED;
+ }
+
+#if 0 /* ?TODO: with multiple interfaces we need to consider fwspec::dst */
+ netif = ip_route(target);
+ if (netif == NULL) {
+ return PXREMAP_FAILED;
+ }
+#else
+ netif = netif_list;
+ LWIP_ASSERT1(netif != NULL);
+ LWIP_ASSERT1(netif->next == NULL);
+#endif
+
+ lomap = g_proxy_options->lomap_desc->lomap;
+ for (i = 0; i < g_proxy_options->lomap_desc->num_lomap; ++i) {
+ if (ip_addr_cmp(src, &lomap[i].loaddr)) {
+ ip_addr_t net;
+
+ ip_addr_get_network(&net, &netif->ip_addr, &netif->netmask);
+ ip4_addr_set_u32(dst,
+ htonl(ntohl(ip4_addr_get_u32(&net))
+ + lomap[i].off));
+ return PXREMAP_MAPPED;
+ }
+ }
+
+ return PXREMAP_FAILED;
+}
+
+
+static int
+proxy_ip6_is_mapped_loopback(struct netif *netif, ip6_addr_t *dst)
+{
+ int i;
+
+ /* XXX: TODO: check netif is a proxying netif! */
+
+ LWIP_ASSERT1(dst != NULL);
+
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) {
+ if (ip6_addr_ispreferred(netif_ip6_addr_state(netif, i))
+ && ip6_addr_isuniquelocal(netif_ip6_addr(netif, i)))
+ {
+ ip6_addr_t *ifaddr = netif_ip6_addr(netif, i);
+ if (memcmp(dst, ifaddr, sizeof(ip6_addr_t) - 1) == 0
+ && ((IP6_ADDR_BLOCK8(dst) & 0xff)
+ == (IP6_ADDR_BLOCK8(ifaddr) & 0xff) + 1))
+ {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ * Hook function for nd6_input() - returns true to cause proxy NA
+ * reply to be generated for "dst".
+ */
+int
+pxremap_proxy_na(struct netif *netif, ip6_addr_t *dst)
+{
+ return proxy_ip6_is_mapped_loopback(netif, dst);
+}
+
+
+/**
+ * Hook function for ip6_forward() - returns true to divert packets to
+ * "dst" to proxy (instead of forwarding them via "netif" or dropping).
+ */
+int
+pxremap_ip6_divert(struct netif *netif, ip6_addr_t *dst)
+{
+ return proxy_ip6_is_mapped_loopback(netif, dst);
+}
+
+
+/**
+ * Mapping from local network to loopback for outbound connections.
+ *
+ * Copy "src" to "dst" with ip6_addr_set(dst, src), but if "src" is a
+ * local network address that maps host's loopback address, copy IPv6
+ * loopback address to "dst".
+ */
+int
+pxremap_outbound_ip6(ip6_addr_t *dst, ip6_addr_t *src)
+{
+ struct netif *netif;
+ int i;
+
+ LWIP_ASSERT1(dst != NULL);
+ LWIP_ASSERT1(src != NULL);
+
+ for (netif = netif_list; netif != NULL; netif = netif->next) {
+ if (!netif_is_up(netif) /* || this is not a proxy netif */) {
+ continue;
+ }
+
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) {
+ if (ip6_addr_ispreferred(netif_ip6_addr_state(netif, i))
+ && ip6_addr_isuniquelocal(netif_ip6_addr(netif, i)))
+ {
+ ip6_addr_t *ifaddr = netif_ip6_addr(netif, i);
+ if (memcmp(src, ifaddr, sizeof(ip6_addr_t) - 1) == 0
+ && ((IP6_ADDR_BLOCK8(src) & 0xff)
+ == (IP6_ADDR_BLOCK8(ifaddr) & 0xff) + 1))
+ {
+ ip6_addr_set_loopback(dst);
+ return PXREMAP_MAPPED;
+ }
+ }
+ }
+ }
+
+ /* not remapped, just copy src */
+ ip6_addr_set(dst, src);
+ return PXREMAP_ASIS;
+}
+
+
+/**
+ * Mapping from loopback to local network for inbound (port-forwarded)
+ * connections.
+ *
+ * Copy "src" to "dst" with ip6_addr_set(dst, src), but if "src" is a
+ * host's loopback address, copy local network address that maps it to
+ * "dst".
+ */
+int
+pxremap_inbound_ip6(ip6_addr_t *dst, ip6_addr_t *src)
+{
+ ip6_addr_t loopback;
+ struct netif *netif;
+ int i;
+
+ ip6_addr_set_loopback(&loopback);
+ if (!ip6_addr_cmp(src, &loopback)) {
+ ip6_addr_set(dst, src);
+ return PXREMAP_ASIS;
+ }
+
+#if 0 /* ?TODO: with multiple interfaces we need to consider fwspec::dst */
+ netif = ip6_route_fwd(target);
+ if (netif == NULL) {
+ return PXREMAP_FAILED;
+ }
+#else
+ netif = netif_list;
+ LWIP_ASSERT1(netif != NULL);
+ LWIP_ASSERT1(netif->next == NULL);
+#endif
+
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) {
+ ip6_addr_t *ifaddr = netif_ip6_addr(netif, i);
+ if (ip6_addr_ispreferred(netif_ip6_addr_state(netif, i))
+ && ip6_addr_isuniquelocal(ifaddr))
+ {
+ ip6_addr_set(dst, ifaddr);
+ ++((u8_t *)&dst->addr[3])[3];
+ return PXREMAP_MAPPED;
+ }
+ }
+
+ return PXREMAP_FAILED;
+}