From 37cbd6d1e4f65892bab87194479a3cf86fa181aa Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Wed, 1 Feb 2012 18:51:46 +0100 Subject: [PATCH] nat: Move nat code into a seperate file 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 | 2 +- src/connman.h | 7 ++ src/main.c | 2 + src/nat.c | 235 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tethering.c | 119 +++------------------------- 5 files changed, 254 insertions(+), 111 deletions(-) create mode 100644 src/nat.c diff --git a/Makefile.am b/Makefile.am index 5a70f0c..79b4f9f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 diff --git a/src/connman.h b/src/connman.h index 0a16fb4..60bb9c1 100644 --- a/src/connman.h +++ b/src/connman.h @@ -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); diff --git a/src/main.c b/src/main.c index 28337af..ded3bb1 100644 --- a/src/main.c +++ b/src/main.c @@ -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 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 +#endif + +#include +#include + +#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); +} diff --git a/src/tethering.c b/src/tethering.c index 70c9177..4bf16d7 100644 --- a/src/tethering.c +++ b/src/tethering.c @@ -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; -- 2.7.4