#include <config.h>
#endif
+#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <gdbus.h>
+#ifndef DBUS_TYPE_UNIX_FD
+#define DBUS_TYPE_UNIX_FD -1
+#endif
+
#define BRIDGE_PROC_DIR "/proc/sys/net/bridge"
#define BRIDGE_NAME "tether"
#define PRIVATE_NETWORK_SECONDARY_DNS "8.8.4.4"
static char *default_interface = NULL;
-static volatile gint tethering_enabled;
+static volatile int tethering_enabled;
static GDHCPServer *tethering_dhcp_server = NULL;
static DBusConnection *connection;
static GHashTable *pn_hash;
struct connman_private_network {
char *owner;
+ char *path;
guint watch;
DBusMessage *msg;
DBusMessage *reply;
DBG("name %s", name);
- sk = socket(AF_INET, SOCK_STREAM, 0);
+ sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (sk < 0)
return -EOPNOTSUPP;
- err = ioctl(sk, SIOCBRADDBR, name);
-
- if (err < 0)
- return -EOPNOTSUPP;
+ if (ioctl(sk, SIOCBRADDBR, name) == -1) {
+ err = -errno;
+ if (err != -EEXIST)
+ return -EOPNOTSUPP;
+ }
err = set_forward_delay(name, 0);
DBG("name %s", name);
- sk = socket(AF_INET, SOCK_STREAM, 0);
+ sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (sk < 0)
return -EOPNOTSUPP;
void __connman_tethering_set_enabled(void)
{
int err;
+ const char *dns;
DBG("enabled %d", tethering_enabled + 1);
- if (g_atomic_int_exchange_and_add(&tethering_enabled, 1) == 0) {
- err = create_bridge(BRIDGE_NAME);
- if (err < 0)
- return;
+ if (__sync_fetch_and_add(&tethering_enabled, 1) != 0)
+ return;
+
+ err = create_bridge(BRIDGE_NAME);
+ if (err < 0)
+ return;
- err = enable_bridge(BRIDGE_NAME);
- if (err < 0) {
- remove_bridge(BRIDGE_NAME);
- return;
- }
+ err = enable_bridge(BRIDGE_NAME);
+ if (err < 0 && err != -EALREADY) {
+ remove_bridge(BRIDGE_NAME);
+ return;
+ }
- if (__connman_dnsproxy_add_listener(BRIDGE_NAME) < 0)
- connman_error("Can't add listener %s to DNS proxy",
+ dns = BRIDGE_IP;
+ if (__connman_dnsproxy_add_listener(BRIDGE_NAME) < 0) {
+ connman_error("Can't add listener %s to DNS proxy",
BRIDGE_NAME);
+ dns = BRIDGE_DNS;
+ }
- tethering_dhcp_server =
- dhcp_server_start(BRIDGE_NAME,
- BRIDGE_IP, BRIDGE_SUBNET,
- BRIDGE_IP_START, BRIDGE_IP_END,
- 24 * 3600, BRIDGE_IP);
- if (tethering_dhcp_server == NULL) {
- disable_bridge(BRIDGE_NAME);
- remove_bridge(BRIDGE_NAME);
- return;
- }
+ tethering_dhcp_server =
+ dhcp_server_start(BRIDGE_NAME,
+ BRIDGE_IP, BRIDGE_SUBNET,
+ BRIDGE_IP_START, BRIDGE_IP_END,
+ 24 * 3600, dns);
+ if (tethering_dhcp_server == NULL) {
+ disable_bridge(BRIDGE_NAME);
+ remove_bridge(BRIDGE_NAME);
+ return;
+ }
- enable_nat(default_interface);
+ enable_nat(default_interface);
- DBG("tethering started");
- }
+ DBG("tethering started");
}
void __connman_tethering_set_disabled(void)
__connman_dnsproxy_remove_listener(BRIDGE_NAME);
- if (g_atomic_int_dec_and_test(&tethering_enabled) == TRUE) {
- disable_nat(default_interface);
+ if (__sync_fetch_and_sub(&tethering_enabled, 1) != 1)
+ return;
- dhcp_server_stop(tethering_dhcp_server);
+ disable_nat(default_interface);
- disable_bridge(BRIDGE_NAME);
+ dhcp_server_stop(tethering_dhcp_server);
- remove_bridge(BRIDGE_NAME);
+ tethering_dhcp_server = NULL;
- DBG("tethering stopped");
- }
+ disable_bridge(BRIDGE_NAME);
+
+ remove_bridge(BRIDGE_NAME);
+
+ DBG("tethering stopped");
}
void __connman_tethering_update_interface(const char *interface)
default_interface = g_strdup(interface);
- if (!g_atomic_int_get(&tethering_enabled))
+ __sync_synchronize();
+ if (tethering_enabled == 0)
return;
enable_nat(interface);
{
struct connman_private_network *pn = data;
unsigned char prefixlen;
- DBusMessage *reply;
DBusMessageIter array, dict;
int err;
goto error;
}
- reply = dbus_message_new_method_return(pn->msg);
-
- if (reply == NULL)
- goto error;
-
- dbus_message_iter_init_append(reply, &array);
+ dbus_message_iter_init_append(pn->reply, &array);
- dbus_message_iter_append_basic(&array, DBUS_TYPE_UNIX_FD, &pn->fd);
+ dbus_message_iter_append_basic(&array, DBUS_TYPE_OBJECT_PATH,
+ &pn->path);
connman_dbus_dict_open(&array, &dict);
connman_dbus_dict_append_basic(&dict, "ServerIPv4",
- DBUS_TYPE_STRING, &pn->server_ip);
+ DBUS_TYPE_STRING, &pn->server_ip);
connman_dbus_dict_append_basic(&dict, "PeerIPv4",
- DBUS_TYPE_STRING, &pn->peer_ip);
+ DBUS_TYPE_STRING, &pn->peer_ip);
connman_dbus_dict_append_basic(&dict, "PrimaryDNS",
- DBUS_TYPE_STRING, &pn->primary_dns);
+ DBUS_TYPE_STRING, &pn->primary_dns);
connman_dbus_dict_append_basic(&dict, "SecondaryDNS",
- DBUS_TYPE_STRING, &pn->secondary_dns);
+ DBUS_TYPE_STRING, &pn->secondary_dns);
connman_dbus_dict_close(&array, &dict);
- g_dbus_send_message(connection, reply);
+ dbus_message_iter_append_basic(&array, DBUS_TYPE_UNIX_FD, &pn->fd);
+
+ g_dbus_send_message(connection, pn->reply);
return;
error:
- reply = __connman_error_failed(pn->msg, -err);
- g_dbus_send_message(connection, reply);
+ pn->reply = __connman_error_failed(pn->msg, -err);
+ g_dbus_send_message(connection, pn->reply);
- g_hash_table_remove(pn_hash, pn->owner);
+ g_hash_table_remove(pn_hash, pn->path);
}
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);
-
disable_nat(default_interface);
+ connman_rtnl_remove_watch(pn->iface_watch);
if (pn->watch > 0) {
g_dbus_remove_watch(connection, pn->watch);
pn->watch = 0;
}
+ close(pn->fd);
+
g_free(pn->interface);
g_free(pn->owner);
+ g_free(pn->path);
g_free(pn);
}
pn->watch = 0;
- g_hash_table_remove(pn_hash, pn->owner);
+ g_hash_table_remove(pn_hash, pn->path);
}
int __connman_private_network_request(DBusMessage *msg, const char *owner)
{
struct connman_private_network *pn;
char *iface = NULL;
+ char *path = NULL;
int index, fd, err;
- pn = g_hash_table_lookup(pn_hash, owner);
- if (pn != NULL)
- return -EEXIST;
+ if (DBUS_TYPE_UNIX_FD < 0)
+ return -EINVAL;
fd = connman_inet_create_tunnel(&iface);
if (fd < 0)
return fd;
+ path = g_strdup_printf("/tethering/%s", iface);
+
+ pn = g_hash_table_lookup(pn_hash, path);
+ if (pn) {
+ g_free(path);
+ g_free(iface);
+ close(fd);
+ return -EEXIST;
+ }
+
index = connman_inet_ifindex(iface);
if (index < 0) {
err = -ENODEV;
goto error;
}
- DBG("inteface %s", iface);
+ DBG("interface %s", iface);
err = connman_inet_set_mtu(index, DEFAULT_MTU);
}
pn->owner = g_strdup(owner);
+ pn->path = path;
pn->watch = g_dbus_add_disconnect_watch(connection, pn->owner,
owner_disconnect, pn, NULL);
pn->msg = msg;
+ pn->reply = dbus_message_new_method_return(pn->msg);
+ if (pn->reply == NULL)
+ goto error;
+
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);
+ g_hash_table_insert(pn_hash, pn->path, pn);
return 0;
error:
close(fd);
g_free(iface);
+ g_free(path);
+ g_free(pn);
return err;
}
-int __connman_private_network_release(const char *owner)
+int __connman_private_network_release(const char *path)
{
struct connman_private_network *pn;
- pn = g_hash_table_lookup(pn_hash, owner);
+ pn = g_hash_table_lookup(pn_hash, path);
if (pn == NULL)
return -EACCES;
- g_hash_table_remove(pn_hash, owner);
+ g_hash_table_remove(pn_hash, path);
return 0;
}
{
DBG("");
- if (g_atomic_int_get(&tethering_enabled)) {
+ __sync_synchronize();
+ if (tethering_enabled == 0) {
if (tethering_dhcp_server)
dhcp_server_stop(tethering_dhcp_server);
disable_bridge(BRIDGE_NAME);