diff options
author | Mohamed Abbas <mabbas@linux.intel.com> | 2011-11-15 13:06:13 +0200 |
---|---|---|
committer | Samuel Ortiz <sameo@linux.intel.com> | 2011-11-15 13:41:44 +0100 |
commit | 43365bffe9cd2f4b0364c3df808a49ffcfc31090 (patch) | |
tree | da3ae3a680ec75c6985dd678a0b8cfe8a17deffb /plugins/vpn.c | |
parent | 43a29da43f9d9f518c939d0251bc0f266dff7887 (diff) | |
download | connman-43365bffe9cd2f4b0364c3df808a49ffcfc31090.tar.gz connman-43365bffe9cd2f4b0364c3df808a49ffcfc31090.tar.bz2 connman-43365bffe9cd2f4b0364c3df808a49ffcfc31090.zip |
vpn: Add support to allow ppp tunnelling
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().
Diffstat (limited to 'plugins/vpn.c')
-rw-r--r-- | plugins/vpn.c | 122 |
1 files changed, 90 insertions, 32 deletions
diff --git a/plugins/vpn.c b/plugins/vpn.c index 79ec305f..7d55d812 100644 --- a/plugins/vpn.c +++ b/plugins/vpn.c @@ -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; } |