Imported Upstream connman version 1.38
[platform/upstream/connman.git] / vpn / plugins / vpn.c
old mode 100644 (file)
new mode 100755 (executable)
index df030b0..c0b2977
@@ -2,7 +2,7 @@
  *
  *  ConnMan VPN daemon
  *
- *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2007-2013  Intel Corporation. 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
@@ -23,7 +23,6 @@
 #include <config.h>
 #endif
 
-#define _GNU_SOURCE
 #include <string.h>
 #include <fcntl.h>
 #include <unistd.h>
@@ -34,6 +33,9 @@
 #include <sys/types.h>
 #include <linux/if_tun.h>
 #include <net/if.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
 
 #include <dbus/dbus.h>
 
 #include "../vpn-provider.h"
 
 #include "vpn.h"
+#include "../vpn.h"
 
 struct vpn_data {
        struct vpn_provider *provider;
        char *if_name;
        unsigned flags;
        unsigned int watch;
-       unsigned int state;
+       enum vpn_state state;
        struct connman_task *task;
+       int tun_flags;
 };
 
 struct vpn_driver_data {
@@ -75,21 +79,23 @@ static int stop_vpn(struct vpn_provider *provider)
        struct ifreq ifr;
        int fd, err;
 
-       if (data == NULL)
+       if (!data)
                return -EINVAL;
 
        name = vpn_provider_get_driver_name(provider);
-       if (name == NULL)
+       if (!name)
                return -EINVAL;
 
        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)
+       if (vpn_driver_data && vpn_driver_data->vpn_driver &&
+                       vpn_driver_data->vpn_driver->flags & VPN_FLAG_NO_TUN) {
+               vpn_driver_data->vpn_driver->disconnect(data->provider);
                return 0;
+       }
 
        memset(&ifr, 0, sizeof(ifr));
-       ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
+       ifr.ifr_flags = data->tun_flags | IFF_NO_PI;
        sprintf(ifr.ifr_name, "%s", data->if_name);
 
        fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
@@ -129,18 +135,22 @@ void vpn_died(struct connman_task *task, int exit_code, void *user_data)
 
        DBG("provider %p data %p", provider, data);
 
-       if (data == NULL)
+       if (!data)
                goto vpn_exit;
 
+       /* The task may die after we have already started the new one */
+       if (data->task != task)
+               goto done;
+
        state = data->state;
 
        stop_vpn(provider);
        vpn_provider_set_data(provider, NULL);
 
        if (data->watch != 0) {
-               vpn_provider_unref(provider);
                vpn_rtnl_remove_watch(data->watch);
                data->watch = 0;
+               vpn_provider_unref(provider);
        }
 
 vpn_exit:
@@ -149,29 +159,29 @@ vpn_exit:
                struct vpn_driver_data *vpn_data = NULL;
 
                name = vpn_provider_get_driver_name(provider);
-               if (name != NULL)
+               if (name)
                        vpn_data = g_hash_table_lookup(driver_hash, name);
 
-               if (vpn_data != NULL &&
-                               vpn_data->vpn_driver->error_code != NULL)
-                       ret = vpn_data->vpn_driver->error_code(exit_code);
+               if (vpn_data &&
+                               vpn_data->vpn_driver->error_code)
+                       ret = vpn_data->vpn_driver->error_code(provider,
+                                       exit_code);
                else
                        ret = VPN_PROVIDER_ERROR_UNKNOWN;
 
                vpn_provider_indicate_error(provider, ret);
-
-               vpn_provider_set_state(provider, VPN_PROVIDER_STATE_FAILURE);
        } else
                vpn_provider_set_state(provider, VPN_PROVIDER_STATE_IDLE);
 
        vpn_provider_set_index(provider, -1);
 
-       if (data != NULL) {
+       if (data) {
                vpn_provider_unref(data->provider);
                g_free(data->if_name);
                g_free(data);
        }
 
+done:
        connman_task_destroy(task);
 }
 
@@ -180,14 +190,14 @@ int vpn_set_ifname(struct vpn_provider *provider, const char *ifname)
        struct vpn_data *data = vpn_provider_get_data(provider);
        int index;
 
-       if (ifname == NULL || data == NULL)
+       if (!ifname || !data)
                return  -EIO;
 
        index = connman_inet_ifindex(ifname);
        if (index < 0)
                return  -EIO;
 
-       if (data->if_name != NULL)
+       if (data->if_name)
                g_free(data->if_name);
 
        data->if_name = (char *)g_strdup(ifname);
@@ -196,6 +206,34 @@ int vpn_set_ifname(struct vpn_provider *provider, const char *ifname)
        return 0;
 }
 
+static int vpn_set_state(struct vpn_provider *provider,
+                                               enum vpn_provider_state state)
+{
+       struct vpn_data *data = vpn_provider_get_data(provider);
+       if (!data)
+               return -EINVAL;
+
+       switch (state) {
+       case VPN_PROVIDER_STATE_UNKNOWN:
+               return -EINVAL;
+       case VPN_PROVIDER_STATE_IDLE:
+               data->state = VPN_STATE_IDLE;
+               break;
+       case VPN_PROVIDER_STATE_CONNECT:
+       case VPN_PROVIDER_STATE_READY:
+               data->state = VPN_STATE_CONNECT;
+               break;
+       case VPN_PROVIDER_STATE_DISCONNECT:
+               data->state = VPN_STATE_DISCONNECT;
+               break;
+       case VPN_PROVIDER_STATE_FAILURE:
+               data->state = VPN_STATE_FAILURE;
+               break;
+       }
+
+       return 0;
+}
+
 static void vpn_newlink(unsigned flags, unsigned change, void *user_data)
 {
        struct vpn_provider *provider = user_data;
@@ -223,36 +261,74 @@ static DBusMessage *vpn_notify(struct connman_task *task,
        data = vpn_provider_get_data(provider);
 
        name = vpn_provider_get_driver_name(provider);
-       if (name == NULL)
+
+       if (!name) {
+               DBG("Cannot find VPN driver for provider %p", provider);
+               vpn_provider_set_state(provider, VPN_PROVIDER_STATE_FAILURE);
                return NULL;
+       }
 
        vpn_driver_data = g_hash_table_lookup(driver_hash, name);
-       if (vpn_driver_data == NULL)
+       if (!vpn_driver_data) {
+               DBG("Cannot find VPN driver data for name %s", name);
+               vpn_provider_set_state(provider, VPN_PROVIDER_STATE_FAILURE);
                return NULL;
+       }
 
        state = vpn_driver_data->vpn_driver->notify(msg, provider);
+
+       DBG("provider %p driver %s state %d", provider, name, state);
+
        switch (state) {
        case VPN_STATE_CONNECT:
        case VPN_STATE_READY:
+               if (data->state == VPN_STATE_READY) {
+                       /*
+                        * This is the restart case, in which case we must
+                        * just set the IP address.
+                        *
+                        * We need to remove first the old address, just
+                        * replacing the old address will not work as expected
+                        * because the old address will linger in the interface
+                        * and not disappear so the clearing is needed here.
+                        *
+                        * Also the state must change, otherwise the routes
+                        * will not be set properly.
+                        */
+                       vpn_provider_set_state(provider,
+                                               VPN_PROVIDER_STATE_CONNECT);
+
+                       vpn_provider_clear_address(provider, AF_INET);
+                       vpn_provider_clear_address(provider, AF_INET6);
+
+                       vpn_provider_change_address(provider);
+                       vpn_provider_set_state(provider,
+                                               VPN_PROVIDER_STATE_READY);
+                       break;
+               }
+
                index = vpn_provider_get_index(provider);
                vpn_provider_ref(provider);
                data->watch = vpn_rtnl_add_newlink_watch(index,
                                                     vpn_newlink, provider);
                err = connman_inet_ifup(index);
                if (err < 0) {
-                       if (err == -EALREADY)
+                       if (err == -EALREADY) {
                                /*
                                 * So the interface is up already, that is just
                                 * great. Unfortunately in this case the
                                 * newlink watch might not have been called at
                                 * all. We must manually call it here so that
                                 * the provider can go to ready state and the
-                                * routes are setup properly.
+                                * routes are setup properly. Also reset flags
+                                * so vpn_newlink() can handle the change.
                                 */
+                               data->flags = 0;
                                vpn_newlink(IFF_UP, 0, provider);
-                       else
+                       } else {
                                DBG("Cannot take interface %d up err %d/%s",
                                        index, -err, strerror(-err));
+                       }
                }
                break;
 
@@ -273,14 +349,64 @@ static DBusMessage *vpn_notify(struct connman_task *task,
        return NULL;
 }
 
-static int vpn_create_tun(struct vpn_provider *provider)
+#if defined TIZEN_EXT
+static void vpn_event(struct vpn_provider *provider, int state)
+{
+       struct vpn_driver_data *vpn_driver_data;
+       const char *name;
+
+       name = vpn_provider_get_driver_name(provider);
+       if (!name) {
+               DBG("Cannot find VPN driver for provider %p", provider);
+               vpn_provider_set_state(provider, VPN_PROVIDER_STATE_FAILURE);
+               return;
+       }
+
+       vpn_driver_data = g_hash_table_lookup(driver_hash, name);
+       if (!vpn_driver_data) {
+               DBG("Cannot find VPN driver data for name %s", name);
+               vpn_provider_set_state(provider, VPN_PROVIDER_STATE_FAILURE);
+               return;
+       }
+
+       DBG("provider %p driver %s state %d", provider, name, state);
+
+       switch (state) {
+       case VPN_STATE_CONNECT:
+               vpn_provider_set_state(provider,
+                               VPN_PROVIDER_STATE_CONNECT);
+               break;
+       case VPN_STATE_READY:
+               vpn_provider_set_state(provider,
+                               VPN_PROVIDER_STATE_READY);
+               break;
+
+       case VPN_STATE_UNKNOWN:
+       case VPN_STATE_IDLE:
+       case VPN_STATE_DISCONNECT:
+       case VPN_STATE_FAILURE:
+               vpn_provider_set_state(provider,
+                                       VPN_PROVIDER_STATE_DISCONNECT);
+               break;
+
+       case VPN_STATE_AUTH_FAILURE:
+               vpn_provider_indicate_error(provider,
+                                       VPN_PROVIDER_ERROR_AUTH_FAILED);
+               break;
+       }
+
+       return;
+}
+#endif
+
+static int vpn_create_tun(struct vpn_provider *provider, int flags)
 {
        struct vpn_data *data = vpn_provider_get_data(provider);
        struct ifreq ifr;
        int i, fd, index;
        int ret = 0;
 
-       if (data == NULL)
+       if (!data)
                return -EISCONN;
 
        fd = open("/dev/net/tun", O_RDWR | O_CLOEXEC);
@@ -293,7 +419,7 @@ static int vpn_create_tun(struct vpn_provider *provider)
        }
 
        memset(&ifr, 0, sizeof(ifr));
-       ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
+       ifr.ifr_flags = flags | IFF_NO_PI;
 
        for (i = 0; i < 256; i++) {
                sprintf(ifr.ifr_name, "vpn%d", i);
@@ -309,8 +435,10 @@ static int vpn_create_tun(struct vpn_provider *provider)
                goto exist_err;
        }
 
+       data->tun_flags = flags;
+       g_free(data->if_name);
        data->if_name = (char *)g_strdup(ifr.ifr_name);
-       if (data->if_name == NULL) {
+       if (!data->if_name) {
                connman_error("Failed to allocate memory");
                close(fd);
                ret = -ENOMEM;
@@ -343,48 +471,238 @@ exist_err:
        return ret;
 }
 
-static int vpn_connect(struct vpn_provider *provider)
+static gboolean is_numeric(const char *str)
+{
+       gint i;
+
+       if(!str || !(*str))
+               return false;
+
+       for(i = 0; str[i] ; i++) {
+               if(!g_ascii_isdigit(str[i]))
+                       return false;
+       }
+
+       return true;
+}
+
+static gint get_gid(const char *group_name)
+{
+       gint gid = -1;
+       struct group *grp;
+
+       if(!group_name || !(*group_name))
+               return gid;
+
+       if (is_numeric(group_name)) {
+               gid_t group_id = (gid_t)g_ascii_strtoull(group_name, NULL, 10);
+               grp = getgrgid(group_id);
+       } else {
+               grp = getgrnam(group_name);
+       }
+
+       if (grp)
+               gid = grp->gr_gid;
+
+       return gid;
+}
+
+static gint get_uid(const char *user_name)
+{
+       gint uid = -1;
+       struct passwd *pw;
+
+       if(!user_name || !(*user_name))
+               return uid;
+
+       if (is_numeric(user_name)) {
+               uid_t user_id = (uid_t)g_ascii_strtoull(user_name, NULL, 10);
+               pw = getpwuid(user_id);
+       } else {
+               pw = getpwnam(user_name);
+       }
+
+       if (pw)
+               uid = pw->pw_uid;
+
+       return uid;
+}
+
+static gint get_supplementary_gids(gchar **groups, gid_t **gid_list)
+{
+       gint group_count = 0;
+       gid_t *list = NULL;
+       int i;
+
+       if (groups) {
+               for(i = 0; groups[i]; i++) {
+                       group_count++;
+
+                       list = (gid_t*)g_try_realloc(list,
+                                               sizeof(gid_t) * group_count);
+
+                       if (!list) {
+                               DBG("cannot allocate supplementary group list");
+                               break;
+                       }
+
+                       list[i] = get_gid(groups[i]);
+               }
+       }
+
+       *gid_list = list;
+
+       return group_count;
+}
+
+static void vpn_task_setup(gpointer user_data)
+{
+       struct vpn_plugin_data *data;
+       gint uid;
+       gint gid;
+       gid_t *gid_list = NULL;
+       size_t gid_list_size;
+       const gchar *user;
+       const gchar *group;
+       gchar **suppl_groups;
+
+       data = user_data;
+       user = vpn_settings_get_binary_user(data);
+       group = vpn_settings_get_binary_group(data);
+       suppl_groups = vpn_settings_get_binary_supplementary_groups(data);
+
+       uid = get_uid(user);
+       gid = get_gid(group);
+       gid_list_size = get_supplementary_gids(suppl_groups, &gid_list);
+
+       DBG("vpn_task_setup uid:%d gid:%d supplementary group list size:%zu",
+                                       uid, gid, gid_list_size);
+
+
+       /* Change group if proper group name was set, requires CAP_SETGID.*/
+       if (gid > 0 && setgid(gid))
+               connman_error("error setting gid %d %s", gid, strerror(errno));
+
+       /* Set the supplementary groups if list exists, requires CAP_SETGID. */
+       if (gid_list_size && gid_list && setgroups(gid_list_size, gid_list))
+               connman_error("error setting gid list %s", strerror(errno));
+
+       /* Change user for the task if set, requires CAP_SETUID */
+       if (uid > 0 && setuid(uid))
+               connman_error("error setting uid %d %s", uid, strerror(errno));
+}
+
+
+static gboolean update_provider_state(gpointer data)
+{
+       struct vpn_provider *provider = data;
+       struct vpn_data *vpn_data;
+       int index;
+
+       DBG("");
+
+       vpn_data = vpn_provider_get_data(provider);
+
+       index = vpn_provider_get_index(provider);
+       DBG("index to watch %d", index);
+       vpn_provider_ref(provider);
+       vpn_data->watch = vpn_rtnl_add_newlink_watch(index,
+                                               vpn_newlink, provider);
+       connman_inet_ifup(index);
+
+       return FALSE;
+}
+
+static int vpn_connect(struct vpn_provider *provider,
+                       vpn_provider_connect_cb_t cb,
+                       const char *dbus_sender, void *user_data)
 {
        struct vpn_data *data = vpn_provider_get_data(provider);
        struct vpn_driver_data *vpn_driver_data;
+       struct vpn_plugin_data *vpn_plugin_data;
        const char *name;
-       int ret = 0;
+       int ret = 0, tun_flags = IFF_TUN;
+       enum vpn_state state = VPN_STATE_UNKNOWN;
 
-       if (data != NULL)
-               return -EISCONN;
+       if (data)
+               state = data->state;
 
-       data = g_try_new0(struct vpn_data, 1);
-       if (data == NULL)
-               return -ENOMEM;
+       DBG("data %p state %d", data, state);
 
-       data->provider = vpn_provider_ref(provider);
-       data->watch = 0;
-       data->flags = 0;
-       data->task = NULL;
-       data->state = VPN_STATE_IDLE;
+       switch (state) {
+       case VPN_STATE_UNKNOWN:
+               data = g_try_new0(struct vpn_data, 1);
+               if (!data)
+                       return -ENOMEM;
+
+               data->provider = vpn_provider_ref(provider);
+               data->watch = 0;
+               data->flags = 0;
+               data->task = NULL;
+
+               vpn_provider_set_data(provider, data);
+               /* fall through */
 
-       vpn_provider_set_data(provider, data);
+       case VPN_STATE_DISCONNECT:
+       case VPN_STATE_IDLE:
+       case VPN_STATE_FAILURE:
+       case VPN_STATE_AUTH_FAILURE:
+               data->state = VPN_STATE_IDLE;
+               break;
+
+       case VPN_STATE_CONNECT:
+               return -EINPROGRESS;
+
+       case VPN_STATE_READY:
+               return -EISCONN;
+       }
 
        name = vpn_provider_get_driver_name(provider);
-       if (name == NULL)
+       if (!name)
                return -EINVAL;
 
        vpn_driver_data = g_hash_table_lookup(driver_hash, name);
 
-       if (vpn_driver_data == NULL || vpn_driver_data->vpn_driver == NULL) {
+       if (!vpn_driver_data || !vpn_driver_data->vpn_driver) {
                ret = -EINVAL;
                goto exist_err;
        }
 
-       if (vpn_driver_data->vpn_driver->flags != VPN_FLAG_NO_TUN) {
-               ret = vpn_create_tun(provider);
+       if (!(vpn_driver_data->vpn_driver->flags & VPN_FLAG_NO_TUN)) {
+               if (vpn_driver_data->vpn_driver->device_flags) {
+                       tun_flags = vpn_driver_data->vpn_driver->device_flags(provider);
+               }
+               ret = vpn_create_tun(provider, tun_flags);
                if (ret < 0)
                        goto exist_err;
        }
 
-       data->task = connman_task_create(vpn_driver_data->program);
 
-       if (data->task == NULL) {
+       if (vpn_driver_data && vpn_driver_data->vpn_driver &&
+                       vpn_driver_data->vpn_driver->flags & VPN_FLAG_NO_DAEMON) {
+
+               ret = vpn_driver_data->vpn_driver->connect(provider,
+                                               NULL, NULL, NULL, NULL, NULL);
+               if (ret) {
+                       stop_vpn(provider);
+                       goto exist_err;
+               }
+
+               DBG("%s started with dev %s",
+                       vpn_driver_data->provider_driver.name, data->if_name);
+
+               data->state = VPN_STATE_CONNECT;
+
+               g_timeout_add(1, update_provider_state, provider);
+               return -EINPROGRESS;
+       }
+
+       vpn_plugin_data =
+               vpn_settings_get_vpn_plugin_config(vpn_driver_data->name);
+       data->task = connman_task_create(vpn_driver_data->program,
+                                       vpn_task_setup, vpn_plugin_data);
+
+       if (!data->task) {
                ret = -ENOMEM;
                stop_vpn(provider);
                goto exist_err;
@@ -399,9 +717,16 @@ static int vpn_connect(struct vpn_provider *provider)
                goto exist_err;
        }
 
+
+#if defined TIZEN_EXT
+       if(vpn_driver_data->vpn_driver->set_event_cb)
+               vpn_driver_data->vpn_driver->set_event_cb(vpn_event, provider);
+#endif
+
        ret = vpn_driver_data->vpn_driver->connect(provider, data->task,
-                                                       data->if_name);
-       if (ret < 0) {
+                                               data->if_name, cb, dbus_sender,
+                                               user_data);
+       if (ret < 0 && ret != -EINPROGRESS) {
                stop_vpn(provider);
                connman_task_destroy(data->task);
                data->task = NULL;
@@ -438,16 +763,16 @@ static int vpn_disconnect(struct vpn_provider *provider)
 
        DBG("disconnect provider %p:", provider);
 
-       if (data == NULL)
+       if (!data)
                return 0;
 
        name = vpn_provider_get_driver_name(provider);
-       if (name == NULL)
+       if (!name)
                return 0;
 
        vpn_driver_data = g_hash_table_lookup(driver_hash, name);
        if (vpn_driver_data->vpn_driver->disconnect)
-               vpn_driver_data->vpn_driver->disconnect();
+               vpn_driver_data->vpn_driver->disconnect(provider);
 
        if (data->watch != 0) {
                vpn_provider_unref(provider);
@@ -456,7 +781,15 @@ static int vpn_disconnect(struct vpn_provider *provider)
        }
 
        data->state = VPN_STATE_DISCONNECT;
-       connman_task_stop(data->task);
+
+       if (!vpn_driver_data->vpn_driver->disconnect) {
+               DBG("Driver has no disconnect() implementation, set provider "
+                                       "state to disconnect.");
+               vpn_provider_set_state(provider, VPN_PROVIDER_STATE_DISCONNECT);
+       }
+
+       if (data->task)
+               connman_task_stop(data->task);
 
        return 0;
 }
@@ -464,10 +797,15 @@ static int vpn_disconnect(struct vpn_provider *provider)
 static int vpn_remove(struct vpn_provider *provider)
 {
        struct vpn_data *data;
+       struct vpn_driver_data *driver_data;
+       const char *name;
+       int err = 0;
 
        data = vpn_provider_get_data(provider);
-       if (data == NULL)
-               return 0;
+       name = vpn_provider_get_driver_name(provider);
+
+       if (!data)
+               goto call_remove;
 
        if (data->watch != 0) {
                vpn_provider_unref(provider);
@@ -475,11 +813,25 @@ static int vpn_remove(struct vpn_provider *provider)
                data->watch = 0;
        }
 
-       connman_task_stop(data->task);
+       if (data->task)
+               connman_task_stop(data->task);
 
        g_usleep(G_USEC_PER_SEC);
        stop_vpn(provider);
-       return 0;
+
+call_remove:
+       if (!name)
+               return 0;
+
+       driver_data = g_hash_table_lookup(driver_hash, name);
+
+       if (driver_data && driver_data->vpn_driver->remove)
+               err = driver_data->vpn_driver->remove(provider);
+
+       if (err)
+               DBG("%p vpn_driver->remove() returned %d", provider, err);
+
+       return err;
 }
 
 static int vpn_save(struct vpn_provider *provider, GKeyFile *keyfile)
@@ -489,25 +841,48 @@ static int vpn_save(struct vpn_provider *provider, GKeyFile *keyfile)
 
        name = vpn_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->save != NULL)
+       if (vpn_driver_data &&
+                       vpn_driver_data->vpn_driver->save)
                return vpn_driver_data->vpn_driver->save(provider, keyfile);
 
        return 0;
 }
 
+static int vpn_route_env_parse(struct vpn_provider *provider, const char *key,
+                       int *family, unsigned long *idx,
+                       enum vpn_provider_route_type *type)
+{
+       struct vpn_driver_data *vpn_driver_data = NULL;
+       const char *name = NULL;
+
+       if (!provider)
+               return -EINVAL;
+
+       name = vpn_provider_get_driver_name(provider);
+       vpn_driver_data = g_hash_table_lookup(driver_hash, name);
+
+       if (vpn_driver_data && vpn_driver_data->vpn_driver->route_env_parse)
+               return vpn_driver_data->vpn_driver->route_env_parse(provider, key,
+                       family, idx, type);
+
+       return 0;
+}
+
 int vpn_register(const char *name, struct vpn_driver *vpn_driver,
                        const char *program)
 {
        struct vpn_driver_data *data;
 
        data = g_try_new0(struct vpn_driver_data, 1);
-       if (data == NULL)
+       if (!data)
                return -ENOMEM;
 
        data->name = name;
        data->program = program;
 
+       if (vpn_settings_parse_vpn_plugin_config(data->name) != 0)
+               DBG("No configuration provided for VPN plugin %s", data->name);
+
        data->vpn_driver = vpn_driver;
 
        data->provider_driver.name = name;
@@ -516,13 +891,15 @@ int vpn_register(const char *name, struct vpn_driver *vpn_driver,
        data->provider_driver.probe = vpn_probe;
        data->provider_driver.remove = vpn_remove;
        data->provider_driver.save = vpn_save;
+       data->provider_driver.set_state = vpn_set_state;
+       data->provider_driver.route_env_parse = vpn_route_env_parse;
 
-       if (driver_hash == NULL)
+       if (!driver_hash)
                driver_hash = g_hash_table_new_full(g_str_hash,
                                                        g_str_equal,
                                                        NULL, g_free);
 
-       if (driver_hash == NULL) {
+       if (!driver_hash) {
                connman_error("driver_hash not initialized for %s", name);
                g_free(data);
                return -ENOMEM;
@@ -540,10 +917,11 @@ void vpn_unregister(const char *name)
        struct vpn_driver_data *data;
 
        data = g_hash_table_lookup(driver_hash, name);
-       if (data == NULL)
+       if (!data)
                return;
 
        vpn_provider_driver_unregister(&data->provider_driver);
+       vpn_settings_delete_vpn_plugin_config(name);
 
        g_hash_table_remove(driver_hash, name);