| Index: plugins/vpn.c
|
| diff --git a/plugins/vpn.c b/plugins/vpn.c
|
| index af0e082c7276f9424023b3ad2274df21e5867fe2..fd96dddcfae840d585ee26cbaa41e261998f4bcc 100644
|
| --- a/plugins/vpn.c
|
| +++ b/plugins/vpn.c
|
| @@ -25,7 +25,6 @@
|
| * vpn plugin is responsible for launching the external process and
|
| * handling notification callbacks to clock the provider state machine.
|
| *
|
| - * TODO(sleffler) currently assumes extern vpn service uses tun but not all do
|
| * TODO(sleffler) seems to make more sense in src than plugins
|
| */
|
|
|
| @@ -77,27 +76,40 @@ struct vpn_driver_data {
|
|
|
| static GHashTable *driver_hash = NULL;
|
|
|
| -static int kill_tun(char *tun_name)
|
| +static int kill_tun(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 -1;
|
| +
|
| + 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;
|
| - strncpy(ifr.ifr_name, tun_name, sizeof(ifr.ifr_name));
|
| + strncpy(ifr.ifr_name, data->if_name, sizeof(ifr.ifr_name));
|
|
|
| fd = open("/dev/net/tun", O_RDWR);
|
| 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;
|
| }
|
| @@ -105,12 +117,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_VPN("Killed tun device %s", tun_name);
|
| + _DBG_VPN("Killed tun device %s", data->if_name);
|
| return 0;
|
| }
|
|
|
| @@ -125,7 +137,7 @@ void vpn_died(struct connman_task *task, void *user_data)
|
| if (!data)
|
| goto vpn_exit;
|
|
|
| - kill_tun(data->if_name);
|
| + kill_tun(provider);
|
| connman_provider_set_data(provider, NULL);
|
| connman_rtnl_remove_watch(data->watch);
|
|
|
| @@ -144,6 +156,36 @@ vpn_exit:
|
| 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 (data == NULL) {
|
| + _DBG_VPN("%s: provider data not accessible", __func__);
|
| + return -EIO;
|
| + }
|
| +
|
| + if (ifname == NULL) {
|
| + _DBG_VPN("%s: ifname not provided", __func__);
|
| + return -EIO;
|
| + }
|
| +
|
| + index = connman_inet_ifindex(ifname);
|
| + if (index < 0) {
|
| + _DBG_VPN("%s: could not get ifindex from %s", __func__, ifname);
|
| + return -EIO;
|
| + }
|
| +
|
| + data->if_name = (char *)g_strdup(ifname);
|
| + connman_provider_set_index(provider, index);
|
| +
|
| + /* Set connect state to retry creating ipconfig with index above. */
|
| + connman_provider_set_state(provider, CONNMAN_PROVIDER_STATE_CONNECT);
|
| +
|
| + return 0;
|
| +}
|
| +
|
| static void vpn_newlink(const char *ifname, unsigned flags, unsigned change, void *user_data)
|
| {
|
| struct connman_provider *provider = user_data;
|
| @@ -159,7 +201,7 @@ static void vpn_newlink(const char *ifname, unsigned flags, unsigned change,
|
| data->flags = flags;
|
| }
|
|
|
| -static void vpn_notify(struct connman_task *task,
|
| +static DBusMessage *vpn_notify(struct connman_task *task,
|
| DBusMessage *msg, void *user_data)
|
| {
|
| struct connman_provider *provider = user_data;
|
| @@ -173,7 +215,7 @@ static void vpn_notify(struct connman_task *task,
|
| name = connman_provider_get_driver_name(provider);
|
| vpn_driver_data = g_hash_table_lookup(driver_hash, name);
|
| if (vpn_driver_data == NULL)
|
| - return;
|
| + return NULL;
|
|
|
| state = vpn_driver_data->vpn_driver->notify(msg, provider);
|
| switch (state) {
|
| @@ -193,34 +235,19 @@ static void vpn_notify(struct connman_task *task,
|
| CONNMAN_PROVIDER_STATE_DISCONNECT);
|
| break;
|
| }
|
| +
|
| + return NULL;
|
| }
|
|
|
| -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);
|
| if (fd < 0) {
|
| @@ -269,24 +296,65 @@ static int vpn_connect(struct connman_provider *provider)
|
| index = connman_inet_ifindex(data->if_name);
|
| if (index < 0) {
|
| connman_error("%s: failed to get tun ifindex", __func__);
|
| - kill_tun(data->if_name);
|
| + kill_tun(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) {
|
| + _DBG_VPN("%s: unable to access provider data", __func__);
|
| + 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);
|
| + kill_tun(provider);
|
| goto exist_err;
|
| }
|
|
|
| if (connman_task_set_notify(data->task, "notify",
|
| vpn_notify, provider)) {
|
| ret = -ENOMEM;
|
| - kill_tun(data->if_name);
|
| + kill_tun(provider);
|
| connman_task_destroy(data->task);
|
| data->task = NULL;
|
| goto exist_err;
|
| @@ -295,7 +363,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);
|
| + kill_tun(provider);
|
| connman_task_destroy(data->task);
|
| data->task = NULL;
|
| goto exist_err;
|
| @@ -355,7 +423,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;
|
|
|
| @@ -365,7 +432,8 @@ static int vpn_remove(struct connman_provider *provider)
|
| connman_task_stop(data->task);
|
|
|
| g_usleep(G_USEC_PER_SEC);
|
| - kill_tun(data->if_name);
|
| + kill_tun(provider);
|
| + connman_provider_set_data(provider, NULL);
|
| return 0;
|
| }
|
|
|
|
|