tethering: Add support to create private networks TUN interface
authorGustavo F. Padovan <padovan@profusion.mobi>
Wed, 20 Apr 2011 12:41:20 +0000 (14:41 +0200)
committerSamuel Ortiz <sameo@linux.intel.com>
Wed, 20 Apr 2011 13:18:57 +0000 (15:18 +0200)
include/inet.h
src/connman.h
src/inet.c
src/manager.c
src/tethering.c

index 9a9411d..19f4455 100644 (file)
@@ -75,6 +75,7 @@ int connman_inet_remove_from_bridge(int index, const char *bridge);
 
 int connman_inet_set_mtu(int index, int mtu);
 int connman_inet_setup_tunnel(char *tunnel, int mtu);
+int connman_inet_create_tunnel(char **iface);
 
 #ifdef __cplusplus
 }
index ab2b52e..b79726f 100644 (file)
@@ -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 <connman/provider.h>
index 5e5d9cc..ec2dc52 100644 (file)
@@ -40,6 +40,8 @@
 #include <net/if.h>
 #include <net/if_arp.h>
 #include <netinet/icmp6.h>
+#include <fcntl.h>
+#include <linux/if_tun.h>
 
 #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;
index 0514655..f73330c 100644 (file)
@@ -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 },
        { },
index 00fa3de..c8ec981 100644 (file)
@@ -30,6 +30,9 @@
 #include <sys/ioctl.h>
 #include <net/if.h>
 #include <linux/sockios.h>
+#include <string.h>
+#include <fcntl.h>
+#include <linux/if_tun.h>
 
 #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)