vpn: Add support to allow ppp tunnelling
authorMohamed Abbas <mabbas@linux.intel.com>
Tue, 15 Nov 2011 11:06:13 +0000 (13:06 +0200)
committerSamuel Ortiz <sameo@linux.intel.com>
Tue, 15 Nov 2011 12:41:44 +0000 (13:41 +0100)
pptp and l2tp does not use tun/tab approach so change the vpn.c to
allow vpn daemon to create the interface. Now vpn plugin tell
vpn to create the device or leave to vpn client.
On connection vpn will get the interface name set the index
for the provider.

This patch is prepared by Jukka Rissanen and it contains
Mohamed Abbas original patch + fixes suggested by Daniel Wagner.
Jukka fixed memory leak in vpn_set_ifname().

plugins/vpn.c
plugins/vpn.h

index 79ec305..7d55d81 100644 (file)
@@ -66,27 +66,40 @@ struct vpn_driver_data {
 
 GHashTable *driver_hash = NULL;
 
-static int kill_tun(char *tun_name)
+static int stop_vpn(struct connman_provider *provider)
 {
+       struct vpn_data *data = connman_provider_get_data(provider);
+       struct vpn_driver_data *vpn_driver_data;
+       const char *name;
        struct ifreq ifr;
        int fd, err;
 
+       if (data == NULL)
+               return -EINVAL;
+
+       name = connman_provider_get_driver_name(provider);
+       vpn_driver_data = g_hash_table_lookup(driver_hash, name);
+
+       if (vpn_driver_data != NULL && vpn_driver_data->vpn_driver != NULL &&
+                       vpn_driver_data->vpn_driver->flags == VPN_FLAG_NO_TUN)
+               return 0;
+
        memset(&ifr, 0, sizeof(ifr));
        ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
-       sprintf(ifr.ifr_name, "%s", tun_name);
+       sprintf(ifr.ifr_name, "%s", data->if_name);
 
        fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
        if (fd < 0) {
                err = -errno;
                connman_error("Failed to open /dev/net/tun to device %s: %s",
-                             tun_name, strerror(errno));
+                             data->if_name, strerror(errno));
                return err;
        }
 
        if (ioctl(fd, TUNSETIFF, (void *)&ifr)) {
                err = -errno;
                connman_error("Failed to TUNSETIFF for device %s to it: %s",
-                             tun_name, strerror(errno));
+                             data->if_name, strerror(errno));
                close(fd);
                return err;
        }
@@ -94,12 +107,12 @@ static int kill_tun(char *tun_name)
        if (ioctl(fd, TUNSETPERSIST, 0)) {
                err = -errno;
                connman_error("Failed to set tun device %s nonpersistent: %s",
-                             tun_name, strerror(errno));
+                             data->if_name, strerror(errno));
                close(fd);
                return err;
        }
        close(fd);
-       DBG("Killed tun device %s", tun_name);
+       DBG("Killed tun device %s", data->if_name);
        return 0;
 }
 
@@ -117,7 +130,7 @@ void vpn_died(struct connman_task *task, int exit_code, void *user_data)
 
        state = data->state;
 
-       kill_tun(data->if_name);
+       stop_vpn(provider);
        connman_provider_set_data(provider, NULL);
        connman_rtnl_remove_watch(data->watch);
 
@@ -141,11 +154,34 @@ vpn_exit:
 
        connman_provider_set_index(provider, -1);
        connman_provider_unref(data->provider);
+
+       g_free(data->if_name);
        g_free(data);
 
        connman_task_destroy(task);
 }
 
+int vpn_set_ifname(struct connman_provider *provider, const char *ifname)
+{
+       struct vpn_data *data = connman_provider_get_data(provider);
+       int index;
+
+       if (ifname == NULL || data == NULL)
+               return  -EIO;
+
+       index = connman_inet_ifindex(ifname);
+       if (index < 0)
+               return  -EIO;
+
+       if (data->if_name != NULL)
+               g_free(data->if_name);
+
+       data->if_name = (char *)g_strdup(ifname);
+       connman_provider_set_index(provider, index);
+
+       return 0;
+}
+
 static void vpn_newlink(unsigned flags, unsigned change, void *user_data)
 {
        struct connman_provider *provider = user_data;
@@ -202,32 +238,15 @@ static void vpn_notify(struct connman_task *task,
        }
 }
 
-static int vpn_connect(struct connman_provider *provider)
+static int vpn_create_tun(struct connman_provider *provider)
 {
        struct vpn_data *data = connman_provider_get_data(provider);
-       struct vpn_driver_data *vpn_driver_data;
        struct ifreq ifr;
-       const char *name;
        int i, fd, index;
        int ret = 0;
 
-       if (data != NULL)
-               return -EISCONN;
-
-       data = g_try_new0(struct vpn_data, 1);
        if (data == NULL)
-               return -ENOMEM;
-
-       data->provider = connman_provider_ref(provider);
-       data->watch = 0;
-       data->flags = 0;
-       data->task = NULL;
-       data->state = VPN_STATE_IDLE;
-
-       connman_provider_set_data(provider, data);
-
-       name = connman_provider_get_driver_name(provider);
-       vpn_driver_data = g_hash_table_lookup(driver_hash, name);
+               return -EISCONN;
 
        fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
        if (fd < 0) {
@@ -277,24 +296,63 @@ static int vpn_connect(struct connman_provider *provider)
        index = connman_inet_ifindex(data->if_name);
        if (index < 0) {
                connman_error("Failed to get tun ifindex");
-               kill_tun(data->if_name);
+               stop_vpn(provider);
                ret = -EIO;
                goto exist_err;
        }
        connman_provider_set_index(provider, index);
 
+       return 0;
+
+exist_err:
+       return ret;
+}
+
+static int vpn_connect(struct connman_provider *provider)
+{
+       struct vpn_data *data = connman_provider_get_data(provider);
+       struct vpn_driver_data *vpn_driver_data;
+       const char *name;
+       int ret = 0;
+
+       if (data != NULL)
+               return -EISCONN;
+
+       data = g_try_new0(struct vpn_data, 1);
+       if (data == NULL)
+               return -ENOMEM;
+
+       data->provider = connman_provider_ref(provider);
+       data->watch = 0;
+       data->flags = 0;
+       data->task = NULL;
+       data->state = VPN_STATE_IDLE;
+
+       connman_provider_set_data(provider, data);
+
+       name = connman_provider_get_driver_name(provider);
+       vpn_driver_data = g_hash_table_lookup(driver_hash, name);
+
+       if (vpn_driver_data != NULL && vpn_driver_data->vpn_driver != NULL &&
+               vpn_driver_data->vpn_driver->flags != VPN_FLAG_NO_TUN) {
+
+               ret = vpn_create_tun(provider);
+               if (ret < 0)
+                       goto exist_err;
+       }
+
        data->task = connman_task_create(vpn_driver_data->program);
 
        if (data->task == NULL) {
                ret = -ENOMEM;
-               kill_tun(data->if_name);
+               stop_vpn(provider);
                goto exist_err;
        }
 
        if (connman_task_set_notify(data->task, "notify",
                                        vpn_notify, provider)) {
                ret = -ENOMEM;
-               kill_tun(data->if_name);
+               stop_vpn(provider);
                connman_task_destroy(data->task);
                data->task = NULL;
                goto exist_err;
@@ -303,7 +361,7 @@ static int vpn_connect(struct connman_provider *provider)
        ret = vpn_driver_data->vpn_driver->connect(provider, data->task,
                                                        data->if_name);
        if (ret < 0) {
-               kill_tun(data->if_name);
+               stop_vpn(provider);
                connman_task_destroy(data->task);
                data->task = NULL;
                goto exist_err;
@@ -320,6 +378,7 @@ exist_err:
        connman_provider_set_index(provider, -1);
        connman_provider_set_data(provider, NULL);
        connman_provider_unref(data->provider);
+       g_free(data->if_name);
        g_free(data);
 
        return ret;
@@ -361,7 +420,6 @@ static int vpn_remove(struct connman_provider *provider)
        struct vpn_data *data;
 
        data = connman_provider_get_data(provider);
-       connman_provider_set_data(provider, NULL);
        if (data == NULL)
                return 0;
 
@@ -371,7 +429,7 @@ static int vpn_remove(struct connman_provider *provider)
        connman_task_stop(data->task);
 
        g_usleep(G_USEC_PER_SEC);
-       kill_tun(data->if_name);
+       stop_vpn(provider);
        return 0;
 }
 
index 9542ebf..1ecfba2 100644 (file)
@@ -19,6 +19,8 @@
  *
  */
 
+#define VPN_FLAG_NO_TUN        1
+
 enum vpn_state {
        VPN_STATE_UNKNOWN       = 0,
        VPN_STATE_IDLE          = 1,
@@ -30,6 +32,7 @@ enum vpn_state {
 };
 
 struct vpn_driver {
+       int flags;
        int (*notify) (DBusMessage *msg, struct connman_provider *provider);
        int (*connect) (struct connman_provider *provider,
                        struct connman_task *task, const char *if_name);
@@ -42,3 +45,4 @@ int vpn_register(const char *name, struct vpn_driver *driver,
                        const char *program);
 void vpn_unregister(const char *provider_name);
 void vpn_died(struct connman_task *task, int exit_code, void *user_data);
+int vpn_set_ifname(struct connman_provider *provider, const char *ifname);