diff options
author | Daniel Wagner <daniel.wagner@bmw-carit.de> | 2012-02-01 18:51:46 +0100 |
---|---|---|
committer | Samuel Ortiz <sameo@linux.intel.com> | 2012-02-13 11:33:17 +0100 |
commit | 37cbd6d1e4f65892bab87194479a3cf86fa181aa (patch) | |
tree | b94b0033f0055021a7e66b3e3b65972932b91d95 /src/nat.c | |
parent | d9a94c8478156c97f9cca85d824964994a9b54bf (diff) | |
download | connman-37cbd6d1e4f65892bab87194479a3cf86fa181aa.tar.gz connman-37cbd6d1e4f65892bab87194479a3cf86fa181aa.tar.bz2 connman-37cbd6d1e4f65892bab87194479a3cf86fa181aa.zip |
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
Diffstat (limited to 'src/nat.c')
-rw-r--r-- | src/nat.c | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/src/nat.c b/src/nat.c new file mode 100644 index 00000000..ba52790d --- /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); +} |