From 86f7f6daaa4d05428e73ba5417bd0a5b4970ed32 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Wed, 20 Apr 2011 14:41:20 +0200 Subject: tethering: Add support to create private networks TUN interface --- src/connman.h | 2 +- src/inet.c | 36 ++++++++++++++++++++++++++++++++++ src/manager.c | 8 ++++---- src/tethering.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 99 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/connman.h b/src/connman.h index ab2b52e6..b79726fd 100644 --- a/src/connman.h +++ b/src/connman.h @@ -456,7 +456,7 @@ void __connman_tethering_update_interface(const char *interface); void __connman_tethering_set_enabled(void); void __connman_tethering_set_disabled(void); -int __connman_private_network_request(const char *owner); +int __connman_private_network_request(DBusMessage *msg, const char *owner); int __connman_private_network_release(const char *owner); #include diff --git a/src/inet.c b/src/inet.c index 5e5d9cc8..ec2dc520 100644 --- a/src/inet.c +++ b/src/inet.c @@ -40,6 +40,8 @@ #include #include #include +#include +#include #include "connman.h" @@ -1265,6 +1267,40 @@ done: return err; } +int connman_inet_create_tunnel(char **iface) +{ + struct ifreq ifr; + int i, fd; + + fd = open("/dev/net/tun", O_RDWR); + if (fd < 0) { + i = -errno; + connman_error("Failed to open /dev/net/tun: %s", + strerror(errno)); + return i; + } + + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_TUN | IFF_NO_PI; + + for (i = 0; i < 256; i++) { + sprintf(ifr.ifr_name, "tun%d", i); + + if (!ioctl(fd, TUNSETIFF, (void *)&ifr)) + break; + } + + if (i == 256) { + connman_error("Failed to find available tun device"); + close(fd); + return -ENODEV; + } + + *iface = g_strdup(ifr.ifr_name); + + return fd; +} + struct rs_cb_data { GIOChannel *channel; __connman_inet_rs_cb_t callback; diff --git a/src/manager.c b/src/manager.c index 05146555..f73330c2 100644 --- a/src/manager.c +++ b/src/manager.c @@ -601,11 +601,11 @@ static DBusMessage *request_private_network(DBusConnection *conn, sender = dbus_message_get_sender(msg); - err = __connman_private_network_request(sender); + err = __connman_private_network_request(msg, sender); if (err < 0) return __connman_error_failed(msg, -err); - return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); + return NULL; } static DBusMessage *release_private_network(DBusConnection *conn, @@ -649,8 +649,8 @@ static GDBusMethodTable manager_methods[] = { { "UnregisterCounter", "o", "", unregister_counter }, { "CreateSession", "a{sv}o", "o", create_session }, { "DestroySession", "o", "", destroy_session }, - { "RequestPrivateNetwork", "", "", - request_private_network }, + { "RequestPrivateNetwork", "", "h", request_private_network, + G_DBUS_METHOD_FLAG_ASYNC }, { "ReleasePrivateNetwork", "", "", release_private_network }, { }, diff --git a/src/tethering.c b/src/tethering.c index 00fa3dee..c8ec981a 100644 --- a/src/tethering.c +++ b/src/tethering.c @@ -30,6 +30,9 @@ #include #include #include +#include +#include +#include #include "connman.h" @@ -47,6 +50,8 @@ #define BRIDGE_IP_END "192.168.218.200" #define BRIDGE_DNS "8.8.8.8" +#define DEFAULT_MTU 1500 + static char *default_interface = NULL; static volatile gint tethering_enabled; static GDHCPServer *tethering_dhcp_server = NULL; @@ -56,6 +61,11 @@ static GHashTable *pn_hash; struct connman_private_network { char *owner; guint watch; + DBusMessage *msg; + int fd; + char *interface; + int index; + guint iface_watch; }; const char *__connman_tethering_get_bridge(void) @@ -377,15 +387,31 @@ void __connman_tethering_update_interface(const char *interface) enable_nat(interface); } +static void setup_tun_interface(unsigned int flags, unsigned change, + void *data) +{ + struct connman_private_network *pn = data; + + DBG("index %d flags %d change %d", pn->index, flags, change); + + g_dbus_send_reply(connection, pn->msg, DBUS_TYPE_UNIX_FD, &pn->fd, + DBUS_TYPE_INVALID); +} + static void remove_private_network(gpointer user_data) { struct connman_private_network *pn = user_data; + close(pn->fd); + + connman_rtnl_remove_watch(pn->iface_watch); + if (pn->watch > 0) { g_dbus_remove_watch(connection, pn->watch); pn->watch = 0; } + g_free(pn->interface); g_free(pn->owner); g_free(pn); } @@ -401,25 +427,54 @@ static void owner_disconnect(DBusConnection *connection, void *user_data) g_hash_table_remove(pn_hash, pn->owner); } -int __connman_private_network_request(const char *owner) +int __connman_private_network_request(DBusMessage *msg, const char *owner) { struct connman_private_network *pn; + char *iface = NULL; + int index, fd, err; pn = g_hash_table_lookup(pn_hash, owner); if (pn != NULL) return -EEXIST; + fd = connman_inet_create_tunnel(&iface); + if (fd < 0) + return fd; + + index = connman_inet_ifindex(iface); + if (index < 0) { + err = -ENODEV; + goto error; + } + DBG("inteface %s", iface); + + err = connman_inet_set_mtu(index, DEFAULT_MTU); + pn = g_try_new0(struct connman_private_network, 1); - if (pn == NULL) - return -ENOMEM; + if (pn == NULL) { + err = -ENOMEM; + goto error; + } pn->owner = g_strdup(owner); pn->watch = g_dbus_add_disconnect_watch(connection, pn->owner, owner_disconnect, pn, NULL); + pn->msg = msg; + pn->fd = fd; + pn->interface = iface; + pn->index = index; + + pn->iface_watch = connman_rtnl_add_newlink_watch(index, + setup_tun_interface, pn); g_hash_table_insert(pn_hash, pn->owner, pn); return 0; + +error: + close(fd); + g_free(iface); + return err; } int __connman_private_network_release(const char *owner) -- cgit v1.2.3