nat: Move nat code into a seperate file
authorDaniel Wagner <daniel.wagner@bmw-carit.de>
Wed, 1 Feb 2012 17:51:46 +0000 (18:51 +0100)
committerSamuel Ortiz <sameo@linux.intel.com>
Mon, 13 Feb 2012 10:33:17 +0000 (11:33 +0100)
Instead of natting all traffic, nat.c will restrict the masquerading
on given network/mask.

The nat handling code will be reused by session.c

Makefile.am
src/connman.h
src/main.c
src/nat.c [new file with mode: 0644]
src/tethering.c

index 5a70f0c..79b4f9f 100644 (file)
@@ -80,7 +80,7 @@ src_connmand_SOURCES = $(gdbus_sources) $(gdhcp_sources) \
                        src/technology.c src/counter.c src/ntp.c \
                        src/session.c src/tethering.c src/wpad.c src/wispr.c \
                        src/stats.c src/iptables.c src/dnsproxy.c src/6to4.c \
-                       src/ippool.c src/bridge.c
+                       src/ippool.c src/bridge.c src/nat.c
 
 src_connmand_LDADD = $(builtin_libadd) @GLIB_LIBS@ @DBUS_LIBS@ \
                                @CAPNG_LIBS@ @XTABLES_LIBS@ -lresolv -ldl
index 0a16fb4..60bb9c1 100644 (file)
@@ -749,3 +749,10 @@ int __connman_bridge_remove(const char *name);
 int __connman_bridge_enable(const char *name, const char *broadcast,
                                const char *gateway);
 int __connman_bridge_disable(const char *name);
+
+int __connman_nat_init(void);
+void __connman_nat_cleanup(void);
+
+int __connman_nat_enable(const char *name, const char *address,
+                               unsigned char prefixlen);
+void __connman_nat_disable(const char *name);
index 28337af..ded3bb1 100644 (file)
@@ -339,6 +339,7 @@ int main(int argc, char *argv[])
        __connman_agent_init();
        __connman_ippool_init();
        __connman_iptables_init();
+       __connman_nat_init();
        __connman_tethering_init();
        __connman_counter_init();
        __connman_manager_init();
@@ -397,6 +398,7 @@ int main(int argc, char *argv[])
        __connman_counter_cleanup();
        __connman_agent_cleanup();
        __connman_tethering_cleanup();
+       __connman_nat_cleanup();
        __connman_iptables_cleanup();
        __connman_ippool_cleanup();
        __connman_device_cleanup();
diff --git a/src/nat.c b/src/nat.c
new file mode 100644 (file)
index 0000000..ba52790
--- /dev/null
+++ b/src/nat.c
@@ -0,0 +1,235 @@
+/*
+ *
+ *  Connection Manager
+ *
+ *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2012  BMW Car IT GmbH. All rights reserved.
+ *
+ *  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; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+
+#include "connman.h"
+
+static char *default_interface;
+static GHashTable *nat_hash;
+
+struct connman_nat {
+       char *address;
+       unsigned char prefixlen;
+
+       char *interface;
+};
+
+static int enable_ip_forward(connman_bool_t enable)
+{
+       FILE *f;
+
+       f = fopen("/proc/sys/net/ipv4/ip_forward", "r+");
+       if (f == NULL)
+               return -errno;
+
+       if (enable == TRUE)
+               fprintf(f, "1");
+       else
+               fprintf(f, "0");
+
+       fclose(f);
+
+       return 0;
+}
+
+static void flush_nat(void)
+{
+       int err;
+
+       err = __connman_iptables_command("-t nat -F POSTROUTING");
+       if (err < 0) {
+               DBG("Flushing the nat table failed");
+
+               return;
+       }
+
+       __connman_iptables_commit("nat");
+}
+
+static int enable_nat(struct connman_nat *nat)
+{
+       int err;
+
+       g_free(nat->interface);
+       nat->interface = g_strdup(default_interface);
+
+       if (nat->interface == NULL)
+               return 0;
+
+       /* Enable masquerading */
+       err = __connman_iptables_command("-t nat -A POSTROUTING "
+                                       "-s %s/%d -o %s -j MASQUERADE",
+                                       nat->address,
+                                       nat->prefixlen,
+                                       nat->interface);
+       if (err < 0)
+               return err;
+
+       return __connman_iptables_commit("nat");
+}
+
+static void disable_nat(struct connman_nat *nat)
+{
+       int err;
+
+       if (nat->interface == NULL)
+               return;
+
+       /* Disable masquerading */
+       err = __connman_iptables_command("-t nat -D POSTROUTING "
+                                       "-s %s/%d -o %s, -j MASKQUERADE",
+                                       nat->address,
+                                       nat->prefixlen,
+                                       nat->interface);
+       if (err < 0)
+               return;
+
+       __connman_iptables_commit("nat");
+}
+
+int __connman_nat_enable(const char *name, const char *address,
+                               unsigned char prefixlen)
+{
+       struct connman_nat *nat;
+       int err;
+
+       if (g_hash_table_size(nat_hash) == 0) {
+               err = enable_ip_forward(TRUE);
+               if (err < 0)
+                       return err;
+       }
+
+       nat = g_try_new0(struct connman_nat, 1);
+       if (nat == NULL) {
+               if (g_hash_table_size(nat_hash) == 0)
+                       enable_ip_forward(FALSE);
+
+               return -ENOMEM;
+       }
+
+       nat->address = g_strdup(address);
+       nat->prefixlen = prefixlen;
+
+       g_hash_table_insert(nat_hash, g_strdup(name), nat);
+
+       return enable_nat(nat);
+}
+
+void __connman_nat_disable(const char *name)
+{
+       struct connman_nat *nat;
+
+       nat = g_hash_table_lookup(nat_hash, name);
+       if (nat == NULL)
+               return;
+
+       disable_nat(nat);
+
+       g_hash_table_remove(nat_hash, name);
+
+       if (g_hash_table_size(nat_hash) == 0)
+               enable_ip_forward(FALSE);
+}
+
+static void update_default_interface(struct connman_service *service)
+{
+       GHashTableIter iter;
+       gpointer key, value;
+       char *interface;
+       int err;
+
+       interface = connman_service_get_interface(service);
+
+       DBG("interface %s", interface);
+
+       g_free(default_interface);
+       default_interface = interface;
+
+       g_hash_table_iter_init(&iter, nat_hash);
+
+       while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
+               const char *name = key;
+               struct connman_nat *nat = value;
+
+               disable_nat(nat);
+               err = enable_nat(nat);
+               if (err < 0)
+                       DBG("Failed to enable nat for %s", name);
+       }
+}
+
+static void shutdown_nat(gpointer key, gpointer value, gpointer user_data)
+{
+       const char *name = key;
+
+       __connman_nat_disable(name);
+}
+
+static void cleanup_nat(gpointer data)
+{
+       struct connman_nat *nat = data;
+
+       g_free(nat->address);
+       g_free(nat->interface);
+}
+
+static struct connman_notifier nat_notifier = {
+       .name                   = "nat",
+       .default_changed        = update_default_interface,
+};
+
+int __connman_nat_init(void)
+{
+       int err;
+
+       DBG("");
+
+       err = connman_notifier_register(&nat_notifier);
+       if (err < 0)
+               return err;
+
+       nat_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                               g_free, cleanup_nat);
+
+       flush_nat();
+
+       return 0;
+}
+
+void __connman_nat_cleanup(void)
+{
+       DBG("");
+
+       g_hash_table_foreach(nat_hash, shutdown_nat, NULL);
+       g_hash_table_destroy(nat_hash);
+       nat_hash = NULL;
+
+       flush_nat();
+
+       connman_notifier_unregister(&nat_notifier);
+}
index 70c9177..4bf16d7 100644 (file)
@@ -56,7 +56,6 @@
 #define PRIVATE_NETWORK_PRIMARY_DNS BRIDGE_DNS
 #define PRIVATE_NETWORK_SECONDARY_DNS "8.8.4.4"
 
-static char *default_interface = NULL;
 static volatile int tethering_enabled;
 static GDHCPServer *tethering_dhcp_server = NULL;
 static struct connman_ippool *dhcp_ippool = NULL;
@@ -167,66 +166,6 @@ static void dhcp_server_stop(GDHCPServer *server)
        g_dhcp_server_unref(server);
 }
 
-static int enable_ip_forward(connman_bool_t enable)
-{
-
-       FILE *f;
-
-       f = fopen("/proc/sys/net/ipv4/ip_forward", "r+");
-       if (f == NULL)
-               return -errno;
-
-       if (enable == TRUE)
-               fprintf(f, "1");
-       else
-               fprintf(f, "0");
-
-       fclose(f);
-
-       return 0;
-}
-
-static int enable_nat(const char *interface)
-{
-       int err;
-
-       if (interface == NULL)
-               return 0;
-
-       /* Enable IPv4 forwarding */
-       err = enable_ip_forward(TRUE);
-       if (err < 0)
-               return err;
-
-       /* POSTROUTING flush */
-       err = __connman_iptables_command("-t nat -F POSTROUTING");
-       if (err < 0)
-               return err;
-
-       /* Enable masquerading */
-       err = __connman_iptables_command("-t nat -A POSTROUTING "
-                                       "-o %s -j MASQUERADE", interface);
-       if (err < 0)
-               return err;
-
-       return __connman_iptables_commit("nat");
-}
-
-static void disable_nat(const char *interface)
-{
-       int err;
-
-       /* Disable IPv4 forwarding */
-       enable_ip_forward(FALSE);
-
-       /* POSTROUTING flush */
-       err = __connman_iptables_command("-t nat -F POSTROUTING");
-       if (err < 0)
-               return;
-
-       __connman_iptables_commit("nat");
-}
-
 static void tethering_restart(struct connman_ippool *pool, void *user_data)
 {
        __connman_tethering_set_disabled();
@@ -243,6 +182,7 @@ void __connman_tethering_set_enabled(void)
        const char *start_ip;
        const char *end_ip;
        const char *dns;
+       unsigned char prefixlen;
 
        DBG("enabled %d", tethering_enabled + 1);
 
@@ -290,7 +230,9 @@ void __connman_tethering_set_enabled(void)
                return;
        }
 
-       enable_nat(default_interface);
+       prefixlen =
+               __connman_ipconfig_netmask_prefix_len(subnet_mask);
+       __connman_nat_enable(BRIDGE_NAME, start_ip, prefixlen);
 
        DBG("tethering started");
 }
@@ -304,7 +246,7 @@ void __connman_tethering_set_disabled(void)
        if (__sync_fetch_and_sub(&tethering_enabled, 1) != 1)
                return;
 
-       disable_nat(default_interface);
+       __connman_nat_disable(BRIDGE_NAME);
 
        dhcp_server_stop(tethering_dhcp_server);
 
@@ -319,35 +261,6 @@ void __connman_tethering_set_disabled(void)
        DBG("tethering stopped");
 }
 
-static void update_tethering_interface(struct connman_service *service)
-{
-       char *interface;
-
-       interface = connman_service_get_interface(service);
-
-       DBG("interface %s", interface);
-
-       g_free(default_interface);
-
-       if (interface == NULL) {
-               disable_nat(interface);
-               default_interface = NULL;
-
-               goto out;
-       }
-
-       default_interface = g_strdup(interface);
-
-       __sync_synchronize();
-       if (tethering_enabled == 0)
-               goto out;
-
-       enable_nat(interface);
-
-out:
-       g_free(interface);
-}
-
 static void setup_tun_interface(unsigned int flags, unsigned change,
                void *data)
 {
@@ -379,9 +292,9 @@ static void setup_tun_interface(unsigned int flags, unsigned change,
 
        connman_inet_ifup(pn->index);
 
-       err = enable_nat(default_interface);
+       err = __connman_nat_enable(BRIDGE_NAME, server_ip, prefixlen);
        if (err < 0) {
-               connman_error("failed to enable NAT on %s", default_interface);
+               connman_error("failed to enable NAT");
                goto error;
        }
 
@@ -420,7 +333,7 @@ static void remove_private_network(gpointer user_data)
 {
        struct connman_private_network *pn = user_data;
 
-       disable_nat(default_interface);
+       __connman_nat_disable(BRIDGE_NAME);
        connman_rtnl_remove_watch(pn->iface_watch);
        __connman_ippool_unref(pn->pool);
 
@@ -544,15 +457,8 @@ int __connman_private_network_release(const char *path)
        return 0;
 }
 
-static struct connman_notifier tethering_notifier = {
-       .name                   = "tethering",
-       .default_changed        = update_tethering_interface,
-};
-
 int __connman_tethering_init(void)
 {
-       int err;
-
        DBG("");
 
        tethering_enabled = 0;
@@ -561,12 +467,6 @@ int __connman_tethering_init(void)
        if (connection == NULL)
                return -EFAULT;
 
-       err = connman_notifier_register(&tethering_notifier);
-       if (err < 0) {
-               dbus_connection_unref(connection);
-               return err;
-       }
-
        pn_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
                                                NULL, remove_private_network);
 
@@ -583,10 +483,9 @@ void __connman_tethering_cleanup(void)
                        dhcp_server_stop(tethering_dhcp_server);
                __connman_bridge_disable(BRIDGE_NAME);
                __connman_bridge_remove(BRIDGE_NAME);
+               __connman_nat_disable(BRIDGE_NAME);
        }
 
-       connman_notifier_unregister(&tethering_notifier);
-
        if (connection == NULL)
                return;