diff options
author | Nishant Chaprana <n.chaprana@samsung.com> | 2019-09-17 19:00:55 +0530 |
---|---|---|
committer | Nishant Chaprana <n.chaprana@samsung.com> | 2019-09-18 19:23:41 +0530 |
commit | 26cc90dfaf2ad149b702626f9552c81abbb26862 (patch) | |
tree | 2524c8994cf58358350fde67dfba5c3b8cb58f7d /src | |
parent | 9e3beb21876b6e63bd8acf53e751480d7a1cc16f (diff) | |
parent | 6b2381a2adabea7d8309ff158ef675ff88184305 (diff) | |
download | connman-26cc90dfaf2ad149b702626f9552c81abbb26862.tar.gz connman-26cc90dfaf2ad149b702626f9552c81abbb26862.tar.bz2 connman-26cc90dfaf2ad149b702626f9552c81abbb26862.zip |
Imported Upstream version 1.37submit/tizen/20190920.082459
Change-Id: Idb47c1ddbedc9f97181b8e9a5eeac04ddd832a2c
Signed-off-by: Nishant Chaprana <n.chaprana@samsung.com>
Diffstat (limited to 'src')
52 files changed, 5542 insertions, 1986 deletions
diff --git a/src/acd.c b/src/acd.c new file mode 100644 index 00000000..7ace1554 --- /dev/null +++ b/src/acd.c @@ -0,0 +1,596 @@ +/* + * + * Connection Manager + * + * Copyright (C) 2018 Commend International GmbH. 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 + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* + * Address Conflict Detection (RFC 5227) + * + * based on DHCP client library with GLib integration, + * Copyright (C) 2009-2014 Intel Corporation. All rights reserved. + * + */ + +#include <netinet/if_ether.h> +#include <net/if_arp.h> +#include <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> + +#include "connman.h" +#include <connman/acd.h> +#include <connman/log.h> +#include <connman/inet.h> +#include <connman/dbus.h> +#include <glib.h> +#include "src/shared/arp.h" + +enum acd_state { + ACD_STATE_PROBE, + ACD_STATE_ANNOUNCE, + ACD_STATE_MONITOR, + ACD_STATE_DEFEND, +}; + +static const char* acd_state_texts[] = { + "PROBE", + "ANNOUNCE", + "MONITOR", + "DEFEND" +}; + +struct acd_host { + enum acd_state state; + int ifindex; + char *interface; + uint8_t mac_address[6]; + uint32_t requested_ip; /* host byte order */ + + /* address conflict fields */ + uint32_t ac_ip; /* host byte order */ + uint8_t ac_mac[6]; + gint64 ac_timestamp; + bool ac_resolved; + const char *path; + + bool listen_on; + int listener_sockfd; + unsigned int retry_times; + unsigned int conflicts; + guint timeout; + guint listener_watch; + + acd_host_cb_t ipv4_available_cb; + gpointer ipv4_available_data; + acd_host_cb_t ipv4_lost_cb; + gpointer ipv4_lost_data; + acd_host_cb_t ipv4_conflict_cb; + gpointer ipv4_conflict_data; + acd_host_cb_t ipv4_max_conflicts_cb; + gpointer ipv4_max_conflicts_data; +}; + +static int start_listening(struct acd_host *acd); +static void stop_listening(struct acd_host *acd); +static gboolean acd_listener_event(GIOChannel *channel, GIOCondition condition, + gpointer acd_data); +static int acd_recv_arp_packet(struct acd_host *acd); +static void send_probe_packet(gpointer acd_data); +static gboolean acd_probe_timeout(gpointer acd_data); +static gboolean send_announce_packet(gpointer acd_data); +static gboolean acd_announce_timeout(gpointer acd_data); +static gboolean acd_defend_timeout(gpointer acd_data); + +/* for D-Bus property */ +static void report_conflict(struct acd_host *acd, const struct ether_arp* arp); + +static void debug(struct acd_host *acd, const char *format, ...) +{ + char str[256]; + va_list ap; + + va_start(ap, format); + + if (vsnprintf(str, sizeof(str), format, ap) > 0) + connman_info("ACD index %d: %s", acd->ifindex, str); + + va_end(ap); +} + +void acd_host_free(struct acd_host *acd) +{ + if (!acd) + return; + + g_free(acd->interface); + g_free(acd); +} + +struct acd_host *acd_host_new(int ifindex, const char *path) +{ + struct acd_host *acd; + + if (ifindex < 0) { + connman_error("Invalid interface index %d", ifindex); + return NULL; + } + + acd = g_try_new0(struct acd_host, 1); + if (!acd) { + connman_error("Could not allocate ACD data structure"); + return NULL; + } + + acd->interface = connman_inet_ifname(ifindex); + if (!acd->interface) { + connman_error("Interface with index %d is not available", ifindex); + goto error; + } + + if (!connman_inet_is_ifup(ifindex)) { + connman_error("Interface with index %d and name %s is down", ifindex, + acd->interface); + goto error; + } + + __connman_inet_get_interface_mac_address(ifindex, acd->mac_address); + + acd->listener_sockfd = -1; + acd->listen_on = false; + acd->ifindex = ifindex; + acd->listener_watch = 0; + acd->retry_times = 0; + + acd->ipv4_available_cb = NULL; + acd->ipv4_lost_cb = NULL; + acd->ipv4_conflict_cb = NULL; + acd->ipv4_max_conflicts_cb = NULL; + + acd->ac_ip = 0; + memset(acd->ac_mac, 0, sizeof(acd->ac_mac)); + acd->ac_timestamp = 0; + acd->ac_resolved = true; + acd->path = path; + + return acd; + +error: + acd_host_free(acd); + return NULL; +} + +static void remove_timeout(struct acd_host *acd) +{ + if (acd->timeout > 0) + g_source_remove(acd->timeout); + + acd->timeout = 0; +} + +static int start_listening(struct acd_host *acd) +{ + GIOChannel *listener_channel; + int listener_sockfd; + + if (acd->listen_on) + return 0; + + debug(acd, "start listening"); + + listener_sockfd = arp_socket(acd->ifindex); + if (listener_sockfd < 0) + return -EIO; + + listener_channel = g_io_channel_unix_new(listener_sockfd); + if (!listener_channel) { + /* Failed to create listener channel */ + close(listener_sockfd); + return -EIO; + } + + acd->listen_on = true; + acd->listener_sockfd = listener_sockfd; + + g_io_channel_set_close_on_unref(listener_channel, TRUE); + acd->listener_watch = + g_io_add_watch_full(listener_channel, G_PRIORITY_HIGH, + G_IO_IN | G_IO_NVAL | G_IO_ERR | G_IO_HUP, + acd_listener_event, acd, + NULL); + g_io_channel_unref(listener_channel); + + return 0; +} + +static void stop_listening(struct acd_host *acd) +{ + if (!acd->listen_on) + return; + + if (acd->listener_watch > 0) + g_source_remove(acd->listener_watch); + acd->listen_on = FALSE; + acd->listener_sockfd = -1; + acd->listener_watch = 0; +} + +static gboolean acd_listener_event(GIOChannel *channel, GIOCondition condition, + gpointer acd_data) +{ + struct acd_host *acd = acd_data; + + if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { + acd->listener_watch = 0; + return FALSE; + } + + if (!acd->listen_on) + return FALSE; + + acd_recv_arp_packet(acd); + + return TRUE; +} + +static bool is_link_local(uint32_t ip) +{ + return (ip & LINKLOCAL_ADDR) == LINKLOCAL_ADDR; +} + +static int acd_recv_arp_packet(struct acd_host *acd) +{ + ssize_t cnt; + struct ether_arp arp; + uint32_t ip_n; /* network byte order */ + struct in_addr addr; + int source_conflict; + int target_conflict; + bool probe; + char* confltxt; + uint8_t* mac; + uint8_t* omac; + + memset(&arp, 0, sizeof(arp)); + cnt = read(acd->listener_sockfd, &arp, sizeof(arp)); + if (cnt != sizeof(arp)) + return -EINVAL; + + if (arp.arp_op != htons(ARPOP_REPLY) && + arp.arp_op != htons(ARPOP_REQUEST)) + return -EINVAL; + + if (memcmp(arp.arp_sha, acd->mac_address, ETH_ALEN) == 0) + return 0; + + ip_n = htonl(acd->requested_ip); + source_conflict = !memcmp(arp.arp_spa, &ip_n, sizeof(uint32_t)); + probe = !memcmp(arp.arp_spa, "\0\0\0\0", sizeof(uint32_t)); + target_conflict = probe && + !memcmp(arp.arp_tpa, &ip_n, sizeof(uint32_t)); + + if (!source_conflict && !target_conflict) + return 0; + + acd->conflicts++; + + confltxt = target_conflict ? "target" : "source"; + + addr.s_addr = ip_n; + debug(acd, "IPv4 %d %s conflicts detected for address %s. " + "State=%s", acd->conflicts, confltxt, inet_ntoa(addr), + acd_state_texts[acd->state]); + mac = acd->mac_address; + omac = arp.arp_sha; + debug(acd, "Our MAC: %02x:%02x:%02x:%02x:%02x:%02x" + " other MAC: %02x:%02x:%02x:%02x:%02x:%02x", + mac[0], mac[1], mac[2],mac[3], mac[4], mac[5], + omac[0], omac[1], omac[2],omac[3], omac[4], omac[5]); + + if (acd->state == ACD_STATE_MONITOR) { + if (!source_conflict) + return 0; + + acd->state = ACD_STATE_DEFEND; + debug(acd, "DEFEND mode conflicts: %d", acd->conflicts); + /* Try to defend with a single announce. */ + send_announce_packet(acd); + return 0; + } else if (acd->state == ACD_STATE_DEFEND) { + if (!source_conflict) + return 0; + + debug(acd, "LOST IPv4 address %s", inet_ntoa(addr)); + if (!is_link_local(acd->requested_ip)) + report_conflict(acd, &arp); + + if (acd->ipv4_lost_cb) + acd->ipv4_lost_cb(acd, acd->ipv4_lost_data); + return 0; + } + + if (acd->conflicts < MAX_CONFLICTS) { + if (!is_link_local(acd->requested_ip)) + report_conflict(acd, &arp); + + acd_host_stop(acd); + + /* we need a new request_ip */ + if (acd->ipv4_conflict_cb) + acd->ipv4_conflict_cb(acd, acd->ipv4_conflict_data); + } else { + acd_host_stop(acd); + + /* + * Here we got a lot of conflicts, RFC3927 and RFC5227 state that we + * have to wait RATE_LIMIT_INTERVAL before retrying. + */ + if (acd->ipv4_max_conflicts_cb) + acd->ipv4_max_conflicts_cb(acd, acd->ipv4_max_conflicts_data); + } + + return 0; +} + +int acd_host_start(struct acd_host *acd, uint32_t ip) +{ + guint timeout; + int err; + + remove_timeout(acd); + + err = start_listening(acd); + if (err) + return err; + + acd->retry_times = 0; + acd->requested_ip = ip; + + /* First wait a random delay to avoid storm of ARP requests on boot */ + timeout = __connman_util_random_delay_ms(PROBE_WAIT); + acd->state = ACD_STATE_PROBE; + + acd->timeout = g_timeout_add_full(G_PRIORITY_HIGH, + timeout, + acd_probe_timeout, + acd, + NULL); + + return 0; +} + +void acd_host_stop(struct acd_host *acd) +{ + stop_listening(acd); + + remove_timeout(acd); + + if (acd->listener_watch > 0) { + g_source_remove(acd->listener_watch); + acd->listener_watch = 0; + } + + acd->state = ACD_STATE_PROBE; + acd->retry_times = 0; + acd->requested_ip = 0; +} + +static void send_probe_packet(gpointer acd_data) +{ + guint timeout; + struct acd_host *acd = acd_data; + + debug(acd, "sending ARP probe request"); + remove_timeout(acd); + if (acd->retry_times == 1) { + acd->state = ACD_STATE_PROBE; + start_listening(acd); + } + arp_send_packet(acd->mac_address, 0, + acd->requested_ip, acd->ifindex); + + if (acd->retry_times < PROBE_NUM) { + /* Add a random timeout in range of PROBE_MIN to PROBE_MAX. */ + timeout = __connman_util_random_delay_ms(PROBE_MAX-PROBE_MIN); + timeout += PROBE_MIN * 1000; + } else + timeout = ANNOUNCE_WAIT * 1000; + + acd->timeout = g_timeout_add_full(G_PRIORITY_HIGH, + timeout, + acd_probe_timeout, + acd, + NULL); +} + +static gboolean acd_probe_timeout(gpointer acd_data) +{ + struct acd_host *acd = acd_data; + + acd->timeout = 0; + + debug(acd, "acd probe timeout (retries %d)", acd->retry_times); + if (acd->retry_times == PROBE_NUM) { + acd->state = ACD_STATE_ANNOUNCE; + acd->retry_times = 1; + + send_announce_packet(acd); + return FALSE; + } + + acd->retry_times++; + send_probe_packet(acd); + + return FALSE; +} + +static gboolean send_announce_packet(gpointer acd_data) +{ + struct acd_host *acd = acd_data; + + debug(acd, "sending ACD announce request"); + + arp_send_packet(acd->mac_address, + acd->requested_ip, + acd->requested_ip, + acd->ifindex); + + remove_timeout(acd); + + if (acd->state == ACD_STATE_DEFEND) + acd->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH, + DEFEND_INTERVAL, + acd_defend_timeout, + acd, + NULL); + else + acd->timeout = g_timeout_add_seconds_full(G_PRIORITY_HIGH, + ANNOUNCE_INTERVAL, + acd_announce_timeout, + acd, + NULL); + return TRUE; +} + +static gboolean acd_announce_timeout(gpointer acd_data) +{ + struct acd_host *acd = acd_data; + + acd->timeout = 0; + + debug(acd, "acd announce timeout (retries %d)", acd->retry_times); + if (acd->retry_times != ANNOUNCE_NUM) { + acd->retry_times++; + send_announce_packet(acd); + return FALSE; + } + + debug(acd, "switching to monitor mode"); + acd->state = ACD_STATE_MONITOR; + + if (!acd->ac_resolved && !is_link_local(acd->requested_ip)) + report_conflict(acd, NULL); + + if (acd->ipv4_available_cb) + acd->ipv4_available_cb(acd, + acd->ipv4_available_data); + acd->conflicts = 0; + + return FALSE; +} + +static gboolean acd_defend_timeout(gpointer acd_data) +{ + struct acd_host *acd = acd_data; + + debug(acd, "back to MONITOR mode"); + acd->timeout = 0; + acd->conflicts = 0; + acd->state = ACD_STATE_MONITOR; + + return FALSE; +} + +void acd_host_register_event(struct acd_host *acd, + enum acd_host_event event, + acd_host_cb_t func, + gpointer user_data) +{ + switch (event) { + case ACD_HOST_EVENT_IPV4_AVAILABLE: + acd->ipv4_available_cb = func; + acd->ipv4_available_data = user_data; + break; + case ACD_HOST_EVENT_IPV4_LOST: + acd->ipv4_lost_cb = func; + acd->ipv4_lost_data = user_data; + break; + case ACD_HOST_EVENT_IPV4_CONFLICT: + acd->ipv4_conflict_cb = func; + acd->ipv4_conflict_data = user_data; + break; + case ACD_HOST_EVENT_IPV4_MAXCONFLICT: + acd->ipv4_max_conflicts_cb = func; + acd->ipv4_max_conflicts_data = user_data; + break; + default: + connman_warn("%s unknown event %d.", __FUNCTION__, event); + break; + } +} + +static void append_ac_mac(DBusMessageIter *iter, void *user_data) +{ + struct acd_host *acd = user_data; + char mac[32]; + uint8_t *m = acd->ac_mac; + const char *str = mac; + + snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x", + m[0], m[1], m[2], m[3], m[4], m[5]); + connman_dbus_dict_append_basic(iter, "Address", DBUS_TYPE_STRING, &str); +} + +static void append_ac_ipv4(DBusMessageIter *iter, void *user_data) +{ + struct acd_host *acd = user_data; + struct in_addr addr; + char *a; + + addr.s_addr = htonl(acd->ac_ip); + a = inet_ntoa(addr); + if (!a) + a = ""; + connman_dbus_dict_append_basic(iter, "Address", DBUS_TYPE_STRING, &a); +} + +static void append_ac_property(DBusMessageIter *iter, void *user_data) +{ + struct acd_host *acd = user_data; + + connman_dbus_dict_append_dict(iter, "IPv4", append_ac_ipv4, acd); + connman_dbus_dict_append_dict(iter, "Ethernet", append_ac_mac, acd); + connman_dbus_dict_append_basic(iter, "Timestamp", DBUS_TYPE_INT64, + &acd->ac_timestamp); + connman_dbus_dict_append_basic(iter, "Resolved", DBUS_TYPE_BOOLEAN, + &acd->ac_resolved); +} + +void acd_host_append_dbus_property(struct acd_host *acd, DBusMessageIter *dict) +{ + connman_dbus_dict_append_dict(dict, "LastAddressConflict", + append_ac_property, acd); +} + +static void report_conflict(struct acd_host *acd, const struct ether_arp* arp) +{ + if (arp) { + acd->ac_ip = acd->requested_ip; + memcpy(acd->ac_mac, arp->arp_sha, sizeof(acd->ac_mac)); + acd->ac_timestamp = g_get_real_time(); + acd->ac_resolved = false; + } else { + acd->ac_resolved = true; + } + + connman_dbus_property_changed_dict(acd->path, CONNMAN_SERVICE_INTERFACE, + "LastAddressConflict", append_ac_property, acd); +} + +unsigned int acd_host_get_conflicts_count(struct acd_host *acd) +{ + return acd->conflicts; +} diff --git a/src/backtrace.c b/src/backtrace.c new file mode 100644 index 00000000..bede6698 --- /dev/null +++ b/src/backtrace.c @@ -0,0 +1,147 @@ +/* + * + * Connection Manager + * + * Copyright (C) 2007-2013 Intel Corporation. All rights reserved. + * Copyright (C) 2016 Yann E. MORIN <yann.morin.1998@free.fr>. 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 + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <execinfo.h> +#include <dlfcn.h> + +#include "connman.h" + +void print_backtrace(const char* program_path, const char* program_exec, + unsigned int offset) +{ + void *frames[99]; + size_t n_ptrs; + unsigned int i; + int outfd[2], infd[2]; + int pathlen; + pid_t pid; + + if (!program_exec) + return; + + pathlen = strlen(program_path); + + n_ptrs = backtrace(frames, G_N_ELEMENTS(frames)); + if (n_ptrs < offset) + return; + + if (pipe(outfd) < 0) + return; + + if (pipe(infd) < 0) { + close(outfd[0]); + close(outfd[1]); + return; + } + + pid = fork(); + if (pid < 0) { + close(outfd[0]); + close(outfd[1]); + close(infd[0]); + close(infd[1]); + return; + } + + if (pid == 0) { + close(outfd[1]); + close(infd[0]); + + dup2(outfd[0], STDIN_FILENO); + dup2(infd[1], STDOUT_FILENO); + + execlp("addr2line", "-C", "-f", "-e", program_exec, NULL); + + exit(EXIT_FAILURE); + } + + close(outfd[0]); + close(infd[1]); + + connman_error("++++++++ backtrace ++++++++"); + + for (i = offset; i < n_ptrs - 1; i++) { + Dl_info info; + char addr[20], buf[PATH_MAX * 2]; + int len, written; + char *ptr, *pos; + + dladdr(frames[i], &info); + + len = snprintf(addr, sizeof(addr), "%p\n", frames[i]); + if (len < 0) + break; + + written = write(outfd[1], addr, len); + if (written < 0) + break; + + len = read(infd[0], buf, sizeof(buf) - 1); + if (len < 0) + break; + + buf[len] = '\0'; + + pos = strchr(buf, '\n'); + if (!pos) { + connman_error("Error in backtrace format"); + break; + } + + *pos++ = '\0'; + + if (strcmp(buf, "??") == 0) { + connman_error("#%-2u %p in %s", i - offset, + frames[i], info.dli_fname); + continue; + } + + ptr = strchr(pos, '\n'); + if (!ptr) { + connman_error("Error in backtrace format"); + break; + } + + *ptr++ = '\0'; + + if (strncmp(pos, program_path, pathlen) == 0) + pos += pathlen + 1; + + connman_error("#%-2u %p in %s() at %s", i - offset, + frames[i], buf, pos); + } + + connman_error("+++++++++++++++++++++++++++"); + + kill(pid, SIGTERM); + + close(outfd[1]); + close(infd[0]); +} diff --git a/src/config.c b/src/config.c index d4ba0b37..ca5957d6 100644 --- a/src/config.c +++ b/src/config.c @@ -72,6 +72,7 @@ struct connman_config_service { char *ipv6_gateway; char *ipv6_privacy; char *mac; + bool mdns; char **nameservers; char **search_domains; char **timeservers; @@ -112,6 +113,7 @@ static bool cleanup = false; #define SERVICE_KEY_PASSPHRASE "Passphrase" #define SERVICE_KEY_SECURITY "Security" #define SERVICE_KEY_HIDDEN "Hidden" +#define SERVICE_KEY_MDNS "mDNS" #define SERVICE_KEY_IPv4 "IPv4" #define SERVICE_KEY_IPv6 "IPv6" @@ -152,6 +154,7 @@ static const char *service_possible_keys[] = { SERVICE_KEY_IPv6, SERVICE_KEY_IPv6_PRIVACY, SERVICE_KEY_MAC, + SERVICE_KEY_MDNS, SERVICE_KEY_NAMESERVERS, SERVICE_KEY_SEARCH_DOMAINS, SERVICE_KEY_TIMESERVERS, @@ -193,7 +196,7 @@ static void unregister_service(gpointer data) list = list->next) { service_id = list->data; - service = __connman_service_lookup_from_ident(service_id); + service = connman_service_lookup_from_identifier(service_id); if (service) { __connman_service_set_immutable(service, false); __connman_service_set_config(service, NULL, NULL); @@ -514,6 +517,9 @@ static bool load_service_generic(GKeyFile *keyfile, g_strfreev(strlist); } + service->mdns = __connman_config_get_bool(keyfile, group, + SERVICE_KEY_MDNS, NULL); + return true; err: @@ -564,8 +570,8 @@ static bool load_service(GKeyFile *keyfile, const char *group, g_free(service->type); service->type = str; } else { - DBG("Type of the configured service is missing for group %s", - group); + connman_warn("Type of the configured service is missing " + "for group %s", group); goto err; } @@ -759,14 +765,14 @@ static bool load_service(GKeyFile *keyfile, const char *group, service->security = CONNMAN_SERVICE_SECURITY_PSK; } else if (str) { - if (security != CONNMAN_SERVICE_SECURITY_NONE) { + if (security != CONNMAN_SERVICE_SECURITY_NONE) connman_info("Mismatch no security and " "setting %s = %s", SERVICE_KEY_SECURITY, str); - } - service->security = CONNMAN_SERVICE_SECURITY_NONE; + + service->security = CONNMAN_SERVICE_SECURITY_NONE; } else - service->security = CONNMAN_SERVICE_SECURITY_NONE; + service->security = CONNMAN_SERVICE_SECURITY_NONE; g_free(str); @@ -806,8 +812,11 @@ static bool load_service_from_keyfile(GKeyFile *keyfile, groups = g_key_file_get_groups(keyfile, NULL); for (i = 0; groups[i]; i++) { - if (!g_str_has_prefix(groups[i], "service_")) + if (!g_str_has_prefix(groups[i], "service_")) { + connman_warn("Ignore group named '%s' because prefix " + "is not 'service_'", groups[i]); continue; + } if (load_service(keyfile, groups[i], config)) found = true; } @@ -1211,6 +1220,7 @@ static int try_provision_service(struct connman_config_service *config, enum connman_service_type type; const void *ssid; unsigned int ssid_len; + const char *str; network = __connman_service_get_network(service); if (!network) { @@ -1230,10 +1240,8 @@ static int try_provision_service(struct connman_config_service *config, ssid = connman_network_get_blob(network, "WiFi.SSID", &ssid_len); - if (!ssid) { - connman_error("Network SSID not set"); - return -EINVAL; - } + if (!ssid) + return -ENOENT; if (!config->ssid || ssid_len != config->ssid_len) return -ENOENT; @@ -1241,6 +1249,10 @@ static int try_provision_service(struct connman_config_service *config, if (memcmp(config->ssid, ssid, ssid_len)) return -ENOENT; + str = connman_network_get_string(network, "WiFi.Security"); + if (config->security != __connman_service_string2security(str)) + return -ENOENT; + break; case CONNMAN_SERVICE_TYPE_ETHERNET: @@ -1266,7 +1278,7 @@ static int try_provision_service(struct connman_config_service *config, } DBG("service %p ident %s", service, - __connman_service_get_ident(service)); + connman_service_get_identifier(service)); if (config->mac) { struct connman_device *device; @@ -1387,7 +1399,7 @@ ipv4_out: #endif __connman_service_disconnect(service); - service_id = __connman_service_get_ident(service); + service_id = connman_service_get_identifier(service); config->service_identifiers = g_slist_prepend(config->service_identifiers, g_strdup(service_id)); @@ -1426,6 +1438,8 @@ ipv4_out: __connman_service_set_search_domains(service, config->search_domains); + __connman_service_set_mdns(service, config->mdns); + if (config->timeservers) __connman_service_set_timeservers(service, config->timeservers); @@ -1752,7 +1766,6 @@ void connman_config_free_entries(struct connman_config_entry **entries) } g_free(entries); - return; } bool __connman_config_address_provisioned(const char *address, diff --git a/src/connection.c b/src/connection.c index 64d48b7d..f8194a64 100755 --- a/src/connection.c +++ b/src/connection.c @@ -460,6 +460,7 @@ static void set_default_gateway(struct gateway_data *data, "0.0.0.0") == 0) { if (connman_inet_set_gateway_interface(index) < 0) return; + data->ipv4_gateway->active = true; goto done; } @@ -468,6 +469,7 @@ static void set_default_gateway(struct gateway_data *data, "::") == 0) { if (connman_inet_set_ipv6_gateway_interface(index) < 0) return; + data->ipv6_gateway->active = true; goto done; } @@ -534,6 +536,7 @@ static void unset_default_gateway(struct gateway_data *data, g_strcmp0(data->ipv4_gateway->gateway, "0.0.0.0") == 0) { connman_inet_clear_gateway_interface(index); + data->ipv4_gateway->active = false; return; } @@ -541,6 +544,7 @@ static void unset_default_gateway(struct gateway_data *data, g_strcmp0(data->ipv6_gateway->gateway, "::") == 0) { connman_inet_clear_ipv6_gateway_interface(index); + data->ipv6_gateway->active = false; return; } @@ -557,7 +561,7 @@ static struct gateway_data *find_default_gateway(void) { struct connman_service *service; - service = __connman_service_get_default(); + service = connman_service_get_default(); if (!service) return NULL; @@ -1078,12 +1082,18 @@ bool __connman_connection_update_gateway(void) old_default = default_gateway; } #endif - if (updated && default_gateway) { - if (default_gateway->ipv4_gateway) + /* + * Set default gateway if it has been updated or if it has not been + * set as active yet. + */ + if (default_gateway) { + if (default_gateway->ipv4_gateway && + (updated || !default_gateway->ipv4_gateway->active)) set_default_gateway(default_gateway, CONNMAN_IPCONFIG_TYPE_IPV4); - if (default_gateway->ipv6_gateway) + if (default_gateway->ipv6_gateway && + (updated || !default_gateway->ipv6_gateway->active)) set_default_gateway(default_gateway, CONNMAN_IPCONFIG_TYPE_IPV6); } diff --git a/src/connman-wait-online.service.in b/src/connman-wait-online.service.in new file mode 100644 index 00000000..c2ad5cc9 --- /dev/null +++ b/src/connman-wait-online.service.in @@ -0,0 +1,15 @@ +[Unit] +Description=Wait for network to be configured by ConnMan +Requisite=connman.service +After=connman.service +Before=network-online.target +DefaultDependencies=no +Conflicts=shutdown.target + +[Service] +Type=oneshot +ExecStart=@sbindir@/connmand-wait-online +RemainAfterExit=yes + +[Install] +WantedBy=network-online.target diff --git a/src/connman.h b/src/connman.h index bb4c0e59..57c30508 100644 --- a/src/connman.h +++ b/src/connman.h @@ -133,6 +133,7 @@ int __connman_agent_request_peer_authorization(struct connman_peer *peer, bool wps_requested, const char *dbus_sender, void *user_data); + #include <connman/log.h> int __connman_log_init(const char *program, const char *debug, @@ -142,6 +143,8 @@ void __connman_log_cleanup(gboolean backtrace); void __connman_log_enable(struct connman_debug_desc *start, struct connman_debug_desc *stop); +#include <connman/backtrace.h> + #include <connman/option.h> #include <connman/setting.h> @@ -166,6 +169,9 @@ int __connman_inet_modify_address(int cmd, int flags, int index, int family, const char *broadcast); int __connman_inet_get_interface_address(int index, int family, void *address); int __connman_inet_get_interface_ll_address(int index, int family, void *address); +int __connman_inet_get_interface_mac_address(int index, uint8_t *mac_address); + +bool __connman_inet_is_any_addr(const char *address, int family); #include <netinet/ip6.h> #include <netinet/icmp6.h> @@ -248,7 +254,11 @@ int __connman_inet_rtnl_addattr32(struct nlmsghdr *n, size_t maxlen, int __connman_inet_add_fwmark_rule(uint32_t table_id, int family, uint32_t fwmark); int __connman_inet_del_fwmark_rule(uint32_t table_id, int family, uint32_t fwmark); int __connman_inet_add_default_to_table(uint32_t table_id, int ifindex, const char *gateway); +int __connman_inet_add_subnet_to_table(uint32_t table_id, int ifindex, + const char *gateway, unsigned char prefixlen); int __connman_inet_del_default_from_table(uint32_t table_id, int ifindex, const char *gateway); +int __connman_inet_del_subnet_from_table(uint32_t table_id, int ifindex, + const char *gateway, unsigned char prefixlen); int __connman_inet_get_address_netmask(int ifindex, struct sockaddr_in *address, struct sockaddr_in *netmask); @@ -263,6 +273,7 @@ void __connman_resolver_append_fallback_nameservers(void); int __connman_resolvfile_append(int index, const char *domain, const char *server); int __connman_resolvfile_remove(int index, const char *domain, const char *server); int __connman_resolver_redo_servers(int index); +int __connman_resolver_set_mdns(int index, bool enabled); GKeyFile *__connman_storage_open_global(void); GKeyFile *__connman_storage_load_global(void); @@ -464,7 +475,6 @@ GSList *__connman_timeserver_add_list(GSList *server_list, const char *timeserver); GSList *__connman_timeserver_get_all(struct connman_service *service); int __connman_timeserver_sync(struct connman_service *service); -void __connman_timeserver_sync_next(); enum __connman_dhcpv6_status { CONNMAN_DHCPV6_STATUS_FAIL = 0, @@ -488,6 +498,7 @@ int __connman_dhcp_start(struct connman_ipconfig *ipconfig, struct connman_network *network, dhcp_cb callback, gpointer user_data); void __connman_dhcp_stop(struct connman_ipconfig *ipconfig); +void __connman_dhcp_decline(struct connman_ipconfig *ipconfig); int __connman_dhcp_init(void); void __connman_dhcp_cleanup(void); int __connman_dhcpv6_init(void); @@ -524,7 +535,9 @@ int __connman_connection_get_vpn_index(int phy_index); bool __connman_connection_update_gateway(void); -int __connman_ntp_start(char *server); +typedef void (*__connman_ntp_cb_t) (bool success, void *user_data); +int __connman_ntp_start(char *server, __connman_ntp_cb_t callback, + void *user_data); void __connman_ntp_stop(); int __connman_wpad_init(void); @@ -593,10 +606,12 @@ void __connman_device_list(DBusMessageIter *iter, void *user_data); enum connman_service_type __connman_device_get_service_type(struct connman_device *device); struct connman_device *__connman_device_find_device(enum connman_service_type type); int __connman_device_request_scan(enum connman_service_type type); +int __connman_device_request_scan_full(enum connman_service_type type); int __connman_device_request_hidden_scan(struct connman_device *device, const char *ssid, unsigned int ssid_len, const char *identity, const char *passphrase, const char *security, void *user_data); +void __connman_device_stop_scan(enum connman_service_type type); #if defined TIZEN_EXT int __connman_device_request_specific_scan(enum connman_service_type type, int scan_type, GSList *specific_scan_list); @@ -665,12 +680,15 @@ bool __connman_config_get_bool(GKeyFile *key_file, bool __connman_config_address_provisioned(const char *address, const char *netmask); +#include <connman/tethering.h> + int __connman_tethering_init(void); void __connman_tethering_cleanup(void); const char *__connman_tethering_get_bridge(void); int __connman_tethering_set_enabled(void); void __connman_tethering_set_disabled(void); +void __connman_tethering_list_clients(DBusMessageIter *array); int __connman_private_network_request(DBusMessage *msg, const char *owner); int __connman_private_network_release(const char *path); @@ -719,11 +737,9 @@ int __connman_service_compare(const struct connman_service *a, const struct connman_service *b); struct connman_service *__connman_service_lookup_from_index(int index); -struct connman_service *__connman_service_lookup_from_ident(const char *identifier); struct connman_service *__connman_service_create_from_network(struct connman_network *network); struct connman_service *__connman_service_create_from_provider(struct connman_provider *provider); bool __connman_service_index_is_default(int index); -struct connman_service *__connman_service_get_default(void); #if defined TIZEN_EXT void __connman_service_notify_strength_changed(struct connman_network *network); #endif @@ -740,13 +756,12 @@ struct connman_ipconfig *__connman_service_get_ipconfig( struct connman_service *service, int family); void __connman_service_notify_ipv4_configuration( struct connman_service *service); +void __connman_service_wispr_start(struct connman_service *service, + enum connman_ipconfig_type type); bool __connman_service_is_connected_state(struct connman_service *service, enum connman_ipconfig_type type); -const char *__connman_service_get_ident(struct connman_service *service); const char *__connman_service_get_path(struct connman_service *service); const char *__connman_service_get_name(struct connman_service *service); -unsigned int __connman_service_get_order(struct connman_service *service); -enum connman_service_state __connman_service_get_state(struct connman_service *service); struct connman_network *__connman_service_get_network(struct connman_service *service); enum connman_service_security __connman_service_get_security(struct connman_service *service); const char *__connman_service_get_phase2(struct connman_service *service); @@ -766,6 +781,8 @@ int __connman_service_set_ignore(struct connman_service *service, bool ignore); void __connman_service_set_search_domains(struct connman_service *service, char **domains); +int __connman_service_set_mdns(struct connman_service *service, + bool enabled); void __connman_service_set_string(struct connman_service *service, const char *key, const char *value); @@ -898,9 +915,6 @@ void __connman_service_notify(struct connman_service *service, unsigned int rx_error, unsigned int tx_error, unsigned int rx_dropped, unsigned int tx_dropped); -bool __connman_service_is_user_allowed(enum connman_service_type type, - uid_t uid); - int __connman_service_counter_register(const char *counter); void __connman_service_counter_unregister(const char *counter); @@ -911,6 +925,7 @@ void __connman_peer_cleanup(void); void __connman_peer_list_struct(DBusMessageIter *array); const char *__connman_peer_get_path(struct connman_peer *peer); +void __connman_peer_disconnect_all(void); int __connman_peer_service_init(void); void __connman_peer_service_cleanup(void); @@ -954,11 +969,6 @@ void __connman_mesh_auto_connect(void); #include <connman/session.h> -typedef void (* service_iterate_cb) (struct connman_service *service, - void *user_data); - -int __connman_service_iterate_services(service_iterate_cb cb, void *user_data); - void __connman_service_mark_dirty(); void __connman_service_save(struct connman_service *service); @@ -1040,35 +1050,47 @@ int __connman_stats_get(struct connman_service *service, bool roaming, struct connman_stats_data *data); -int __connman_iptables_dump(const char *table_name); -int __connman_iptables_new_chain(const char *table_name, - const char *chain); -int __connman_iptables_delete_chain(const char *table_name, - const char *chain); -int __connman_iptables_flush_chain(const char *table_name, - const char *chain); -int __connman_iptables_change_policy(const char *table_name, - const char *chain, - const char *policy); -int __connman_iptables_append(const char *table_name, - const char *chain, - const char *rule_spec); -int __connman_iptables_insert(const char *table_name, - const char *chain, - const char *rule_spec); -int __connman_iptables_delete(const char *table_name, - const char *chain, - const char *rule_spec); +int __connman_iptables_dump(int type, + const char *table_name); +int __connman_iptables_new_chain(int type, + const char *table_name, + const char *chain); +int __connman_iptables_delete_chain(int type, + const char *table_name, + const char *chain); +int __connman_iptables_flush_chain(int type, + const char *table_name, + const char *chain); +int __connman_iptables_find_chain(int type, + const char *table_name, + const char *chain); +int __connman_iptables_change_policy(int type, + const char *table_name, + const char *chain, + const char *policy); +int __connman_iptables_append(int type, + const char *table_name, + const char *chain, + const char *rule_spec); +int __connman_iptables_insert(int type, + const char *table_name, + const char *chain, + const char *rule_spec); +int __connman_iptables_delete(int type, + const char *table_name, + const char *chain, + const char *rule_spec); typedef void (*connman_iptables_iterate_chains_cb_t) (const char *chain_name, void *user_data); -int __connman_iptables_iterate_chains(const char *table_name, +int __connman_iptables_iterate_chains(int type, + const char *table_name, connman_iptables_iterate_chains_cb_t cb, void *user_data); int __connman_iptables_init(void); void __connman_iptables_cleanup(void); -int __connman_iptables_commit(const char *table_name); +int __connman_iptables_commit(int type, const char *table_name); int __connman_dnsproxy_init(void); void __connman_dnsproxy_cleanup(void); @@ -1076,6 +1098,7 @@ int __connman_dnsproxy_add_listener(int index); void __connman_dnsproxy_remove_listener(int index); int __connman_dnsproxy_append(int index, const char *domain, const char *server); int __connman_dnsproxy_remove(int index, const char *domain, const char *server); +int __connman_dnsproxy_set_mdns(int index, bool enabled); int __connman_6to4_probe(struct connman_service *service); void __connman_6to4_remove(struct connman_ipconfig *ipconfig); @@ -1089,15 +1112,7 @@ typedef void (*ippool_collision_cb_t) (struct connman_ippool *pool, int __connman_ippool_init(void); void __connman_ippool_cleanup(void); -#define __connman_ippool_ref(ipconfig) \ - __connman_ippool_ref_debug(ipconfig, __FILE__, __LINE__, __func__) -#define __connman_ippool_unref(ipconfig) \ - __connman_ippool_unref_debug(ipconfig, __FILE__, __LINE__, __func__) - -struct connman_ippool *__connman_ippool_ref_debug(struct connman_ippool *pool, - const char *file, int line, const char *caller); -void __connman_ippool_unref_debug(struct connman_ippool *pool, - const char *file, int line, const char *caller); +void __connman_ippool_free(struct connman_ippool *pool); struct connman_ippool *__connman_ippool_create(int index, unsigned int start, @@ -1188,6 +1203,7 @@ int __connman_machine_init(void); void __connman_machine_cleanup(void); int __connman_util_get_random(uint64_t *val); +unsigned int __connman_util_random_delay_ms(unsigned int secs); int __connman_util_init(void); void __connman_util_cleanup(void); diff --git a/src/connman.service.in b/src/connman.service.in index 31febbde..a1ddddc7 100755 --- a/src/connman.service.in +++ b/src/connman.service.in @@ -1,6 +1,11 @@ [Unit] Description=Connection service -After=net-config.service +DefaultDependencies=false +Conflicts=shutdown.target +RequiresMountsFor=@localstatedir@/lib/connman +After=dbus.service network-pre.target systemd-sysusers.service net-config.service +Before=network.target multi-user.target shutdown.target +Wants=network.target [Service] Type=dbus diff --git a/src/connman.socket b/src/connman.socket index bcf8638e..3e1e64d5 100644 --- a/src/connman.socket +++ b/src/connman.socket @@ -1,5 +1,6 @@ [Unit] Description=DNS Proxy Socket +Before=connman.service [Socket] ListenStream=127.0.0.1:53 diff --git a/src/connman_tv.service.in b/src/connman_tv.service.in index e5faac27..9eb75b24 100644 --- a/src/connman_tv.service.in +++ b/src/connman_tv.service.in @@ -1,6 +1,7 @@ [Unit] Description=Connection service After=net-config.service +DefaultDependencies=no [Service] Type=dbus @@ -524,35 +524,6 @@ err: return err; } -int connman_dbus_get_connection_unix_user_sync(DBusConnection *connection, - const char *bus_name, - unsigned int *user_id) -{ -#if defined TIZEN_EXT - *user_id = 0; -#else - unsigned long uid; - DBusError err; - - dbus_error_init(&err); - - uid = dbus_bus_get_unix_user(connection, bus_name, &err); - - if (uid == (unsigned long)-1) { - DBG("Can not get unix user ID!"); - if (dbus_error_is_set(&err)) { - DBG("%s", err.message); - dbus_error_free(&err); - } - return -1; - } - - *user_id = (unsigned int)uid; -#endif - - return 0; -} - static unsigned char *parse_context(DBusMessage *msg) { DBusMessageIter iter, array; diff --git a/src/device.c b/src/device.c index 8b77021a..df7b2bbc 100644 --- a/src/device.c +++ b/src/device.c @@ -52,7 +52,7 @@ struct connman_device { * request */ bool powered; - bool scanning; + bool scanning[MAX_CONNMAN_SERVICE_TYPES]; char *name; char *node; char *address; @@ -152,6 +152,33 @@ enum connman_service_type __connman_device_get_service_type( return CONNMAN_SERVICE_TYPE_UNKNOWN; } +static bool device_has_service_type(struct connman_device *device, + enum connman_service_type service_type) +{ + enum connman_service_type device_service_type = + __connman_device_get_service_type(device); + + /* + * For devices whose device_service_type is unknown we should + * allow to decide whether they support specific service_type + * by themself. + */ + if (device_service_type == CONNMAN_SERVICE_TYPE_UNKNOWN) + return true; + +#if defined TIZEN_EXT_WIFI_MESH + if (device_service_type == CONNMAN_SERVICE_TYPE_MESH) + return service_type != CONNMAN_SERVICE_TYPE_MESH; +#endif + + if (device_service_type == CONNMAN_SERVICE_TYPE_WIFI) { + return service_type == CONNMAN_SERVICE_TYPE_WIFI || + service_type == CONNMAN_SERVICE_TYPE_P2P; + } + + return service_type == device_service_type; +} + static gboolean device_pending_reset(gpointer user_data) { struct connman_device *device = user_data; @@ -179,7 +206,7 @@ int __connman_device_enable(struct connman_device *device) return -EBUSY; if (device->powered_pending == PENDING_ENABLE) - return -EALREADY; + return -EINPROGRESS; if (device->powered_pending == PENDING_NONE && device->powered) return -EALREADY; @@ -230,7 +257,7 @@ int __connman_device_disable(struct connman_device *device) return -EBUSY; if (device->powered_pending == PENDING_DISABLE) - return -EALREADY; + return -EINPROGRESS; if (device->powered_pending == PENDING_NONE && !device->powered) return -EALREADY; @@ -565,7 +592,9 @@ const char *connman_device_get_ident(struct connman_device *device) int connman_device_set_powered(struct connman_device *device, bool powered) { + struct connman_device_scan_params params; enum connman_service_type type; + int i; DBG("device %p powered %d", device, powered); @@ -587,11 +616,15 @@ int connman_device_set_powered(struct connman_device *device, __connman_technology_enabled(type); - device->scanning = false; + for (i = 0; i < MAX_CONNMAN_SERVICE_TYPES; i++) + device->scanning[i] = false; + + if (device->driver && device->driver->scan) { + memset(¶ms, 0, sizeof(params)); + params.type = CONNMAN_SERVICE_TYPE_UNKNOWN; - if (device->driver && device->driver->scan) - device->driver->scan(CONNMAN_SERVICE_TYPE_UNKNOWN, device, - NULL, 0, NULL, NULL, NULL, NULL); + device->driver->scan(device, ¶ms); + } return 0; } @@ -602,16 +635,22 @@ bool connman_device_get_powered(struct connman_device *device) } static int device_scan(enum connman_service_type type, - struct connman_device *device) + struct connman_device *device, + bool force_full_scan) { + struct connman_device_scan_params params; + if (!device->driver || !device->driver->scan) return -EOPNOTSUPP; if (!device->powered) return -ENOLINK; - return device->driver->scan(type, device, NULL, 0, - NULL, NULL, NULL, NULL); + memset(¶ms, 0, sizeof(params)); + params.type = type; + params.force_full_scan = force_full_scan; + + return device->driver->scan(device, ¶ms); } int __connman_device_disconnect(struct connman_device *device) @@ -682,7 +721,8 @@ static gboolean remove_unavailable_network(gpointer key, gpointer value, { struct connman_network *network = value; - if (connman_network_get_connected(network)) + if (connman_network_get_connected(network) || + connman_network_get_connecting(network)) return FALSE; if (connman_network_get_available(network)) @@ -697,9 +737,19 @@ void __connman_device_cleanup_networks(struct connman_device *device) remove_unavailable_network, NULL); } -bool connman_device_get_scanning(struct connman_device *device) +bool connman_device_get_scanning(struct connman_device *device, + enum connman_service_type type) { - return device->scanning; + int i; + + if (type != CONNMAN_SERVICE_TYPE_UNKNOWN) + return device->scanning[type]; + + for (i = 0; i < MAX_CONNMAN_SERVICE_TYPES; i++) + if (device->scanning[i]) + return true; + + return false; } void connman_device_reset_scanning(struct connman_device *device) @@ -723,10 +773,13 @@ int connman_device_set_scanning(struct connman_device *device, if (!device->driver || !device->driver->scan) return -EINVAL; - if (device->scanning == scanning) + if (type == CONNMAN_SERVICE_TYPE_UNKNOWN) + return -EINVAL; + + if (device->scanning[type] == scanning) return -EALREADY; - device->scanning = scanning; + device->scanning[type] = scanning; if (scanning) { __connman_technology_scan_started(device); @@ -1211,7 +1264,8 @@ int __connman_device_request_mesh_specific_scan(enum connman_service_type type, #endif /* TIZEN_EXT_WIFI_MESH */ #endif -int __connman_device_request_scan(enum connman_service_type type) +static int connman_device_request_scan(enum connman_service_type type, + bool force_full_scan) { bool success = false; int last_err = -ENOSYS; @@ -1238,23 +1292,11 @@ int __connman_device_request_scan(enum connman_service_type type) for (list = device_list; list; list = list->next) { struct connman_device *device = list->data; - enum connman_service_type service_type = - __connman_device_get_service_type(device); - if (service_type != CONNMAN_SERVICE_TYPE_UNKNOWN) { - if (type == CONNMAN_SERVICE_TYPE_P2P) { - if (service_type != CONNMAN_SERVICE_TYPE_WIFI) - continue; -#if defined TIZEN_EXT_WIFI_MESH - } else if (type == CONNMAN_SERVICE_TYPE_MESH) { - if (service_type != CONNMAN_SERVICE_TYPE_WIFI) - continue; -#endif - } else if (service_type != type) - continue; - } + if (!device_has_service_type(device, type)) + continue; - err = device_scan(type, device); + err = device_scan(type, device, force_full_scan); #if defined TIZEN_EXT /* When Scan is already in progress then return Error so that * wifi-manager can block the scan-done signal to be sent to @@ -1277,20 +1319,53 @@ int __connman_device_request_scan(enum connman_service_type type) return last_err; } +int __connman_device_request_scan(enum connman_service_type type) +{ + return connman_device_request_scan(type, false); +} + +int __connman_device_request_scan_full(enum connman_service_type type) +{ + return connman_device_request_scan(type, true); +} + int __connman_device_request_hidden_scan(struct connman_device *device, const char *ssid, unsigned int ssid_len, const char *identity, const char *passphrase, const char *security, void *user_data) { + struct connman_device_scan_params params; + DBG("device %p", device); if (!device || !device->driver || !device->driver->scan) return -EINVAL; - return device->driver->scan(CONNMAN_SERVICE_TYPE_UNKNOWN, - device, ssid, ssid_len, identity, - passphrase, security, user_data); + params.type = CONNMAN_SERVICE_TYPE_UNKNOWN; + params.ssid = ssid; + params.ssid_len = ssid_len; + params.identity = identity; + params.passphrase = passphrase; + params.security = security; + params.user_data = user_data; + + return device->driver->scan(device, ¶ms); +} + +void __connman_device_stop_scan(enum connman_service_type type) +{ + GSList *list; + + for (list = device_list; list; list = list->next) { + struct connman_device *device = list->data; + + if (!device_has_service_type(device, type)) + continue; + + if (device->driver && device->driver->stop_scan) + device->driver->stop_scan(type, device); + } } #if defined TIZEN_EXT @@ -134,7 +134,7 @@ static bool apply_dhcp_invalidate_on_network(struct connman_dhcp *dhcp) CONNMAN_IPCONFIG_TYPE_IPV4); #else __connman_service_nameserver_remove(service, - dhcp->nameservers[i], false); + dhcp->nameservers[i], false); #endif } g_strfreev(dhcp->nameservers); @@ -265,6 +265,7 @@ static gboolean dhcp_retry_cb(gpointer user_data) struct connman_dhcp *dhcp = user_data; dhcp->timeout = 0; + #if defined TIZEN_EXT DBG("dhcp %p", dhcp); DBG("dhcp->timeout %d", dhcp->timeout); @@ -849,6 +850,30 @@ void __connman_dhcp_stop(struct connman_ipconfig *ipconfig) } } +void __connman_dhcp_decline(struct connman_ipconfig *ipconfig) +{ + struct connman_dhcp *dhcp; + const char *address; + struct in_addr addr; + + DBG("ipconfig_table %p ipconfig %p", ipconfig_table, ipconfig); + + if (!ipconfig_table) + return; + + dhcp = g_hash_table_lookup(ipconfig_table, ipconfig); + if (dhcp) { + address = __connman_ipconfig_get_local(ipconfig); + if (!address) + return; + + if (inet_pton(AF_INET, address, &addr) != 1) + connman_error("Could not convert address %s", address); + + g_dhcp_client_decline(dhcp->dhcp_client, htonl(addr.s_addr)); + } +} + int __connman_dhcp_init(void) { DBG(""); @@ -865,6 +890,4 @@ void __connman_dhcp_cleanup(void) g_hash_table_destroy(ipconfig_table); ipconfig_table = NULL; - - dhcp_cleanup_random(); } diff --git a/src/dhcpv6.c b/src/dhcpv6.c index c624cb00..4c07c769 100755 --- a/src/dhcpv6.c +++ b/src/dhcpv6.c @@ -196,7 +196,7 @@ static int set_duid(struct connman_service *service, unsigned char *duid; int duid_len; - ident = __connman_service_get_ident(service); + ident = connman_service_get_identifier(service); #if defined TIZEN_EXT if(ident != NULL) DBG("ident : %s", ident); @@ -342,8 +342,8 @@ static void info_req_cb(GDHCPClient *dhcp_client, gpointer user_data) CONNMAN_IPCONFIG_TYPE_IPV6); #else __connman_service_nameserver_remove(service, - dhcp->nameservers[i], - false); + dhcp->nameservers[i], + false); #endif #if defined TIZEN_EXT } @@ -579,8 +579,8 @@ static int set_other_addresses(GDHCPClient *dhcp_client, false, CONNMAN_IPCONFIG_TYPE_IPV6); #else __connman_service_nameserver_append(service, - dhcp->nameservers[i], - false); + dhcp->nameservers[i], + false); #endif #if defined TIZEN_EXT } diff --git a/src/dns-systemd-resolved.c b/src/dns-systemd-resolved.c new file mode 100644 index 00000000..5fe306c3 --- /dev/null +++ b/src/dns-systemd-resolved.c @@ -0,0 +1,490 @@ +/* + * + * Connection Manager + * + * Copyright (C) 2007-2017 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 + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <gdbus.h> +#include <glib.h> +#include <connman/dbus.h> + +#include "connman.h" + +#define SYSTEMD_RESOLVED_SERVICE "org.freedesktop.resolve1" +#define SYSTEMD_RESOLVED_PATH "/org/freedesktop/resolve1" + +struct mdns_data { + int index; + bool enabled; +}; + +static GHashTable *interface_hash; +static DBusConnection *connection; +static GDBusClient *client; +static GDBusProxy *resolved_proxy; + +/* update after a full set of instructions has been received */ +static guint update_interfaces_source; + +struct dns_interface { + GList *domains; + GList *servers; + int index; + bool needs_domain_update; + bool needs_server_update; +}; + +static gboolean compare_index(gconstpointer a, gconstpointer b) +{ + gint ai = GPOINTER_TO_UINT(a); + gint bi = GPOINTER_TO_UINT(b); + + return ai == bi; +} + +static void free_dns_interface(gpointer data) +{ + struct dns_interface *iface = data; + + if (!iface) + return; + + g_list_free_full(iface->domains, g_free); + g_list_free_full(iface->servers, g_free); + + g_free(iface); +} + +static void setlinkdns_append(DBusMessageIter *iter, void *user_data) +{ + struct dns_interface *iface = user_data; + int result; + unsigned int i; + int type; + char ipv4_bytes[4]; + char ipv6_bytes[16]; + GList *list; + DBusMessageIter address_array, struct_array, byte_array; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_INT32, &iface->index); + + dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "(iay)", + &address_array); + + for (list = iface->servers; list; list = g_list_next(list)) { + char *server = list->data; + + DBG("index: %d, server: %s", iface->index, server); + + dbus_message_iter_open_container(&address_array, + DBUS_TYPE_STRUCT, NULL, &struct_array); + + type = connman_inet_check_ipaddress(server); + + if (type == AF_INET) { + result = inet_pton(type, server, ipv4_bytes); + if (!result) { + DBG("Failed to parse IPv4 address: %s", + server); + return; + } + + dbus_message_iter_append_basic(&struct_array, + DBUS_TYPE_INT32, &type); + + dbus_message_iter_open_container(&struct_array, + DBUS_TYPE_ARRAY, "y", &byte_array); + + for (i = 0; i < sizeof(ipv4_bytes); i++) { + dbus_message_iter_append_basic(&byte_array, + DBUS_TYPE_BYTE, + &(ipv4_bytes[i])); + } + + dbus_message_iter_close_container(&struct_array, + &byte_array); + } else if (type == AF_INET6) { + result = inet_pton(type, server, ipv6_bytes); + if (!result) { + DBG("Failed to parse IPv6 address: %s", server); + return; + } + + dbus_message_iter_append_basic(&struct_array, + DBUS_TYPE_INT32, &type); + + dbus_message_iter_open_container(&struct_array, + DBUS_TYPE_ARRAY, "y", &byte_array); + + for (i = 0; i < sizeof(ipv6_bytes); i++) { + dbus_message_iter_append_basic(&byte_array, + DBUS_TYPE_BYTE, + &(ipv6_bytes[i])); + } + + dbus_message_iter_close_container(&struct_array, + &byte_array); + } + + dbus_message_iter_close_container(&address_array, + &struct_array); + } + + dbus_message_iter_close_container(iter, &address_array); +} + +static void setlinkdomains_append(DBusMessageIter *iter, void *user_data) +{ + struct dns_interface *iface = user_data; + GList *list; + DBusMessageIter domain_array, struct_array; + gboolean only_routing = FALSE; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_INT32, &iface->index); + + dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "(sb)", + &domain_array); + + for (list = iface->domains; list; list = g_list_next(list)) { + char *domain = list->data; + + DBG("index: %d, domain: %s", iface->index, domain); + + dbus_message_iter_open_container(&domain_array, + DBUS_TYPE_STRUCT, NULL, &struct_array); + + dbus_message_iter_append_basic(&struct_array, DBUS_TYPE_STRING, + &domain); + + dbus_message_iter_append_basic(&struct_array, DBUS_TYPE_BOOLEAN, + &only_routing); + + dbus_message_iter_close_container(&domain_array, &struct_array); + } + + dbus_message_iter_close_container(iter, &domain_array); +} + +static int set_systemd_resolved_values(struct dns_interface *iface) +{ + if (!resolved_proxy || !iface) + return -ENOENT; + + /* No async error processing -- just fire and forget */ + + if (iface->needs_server_update) { + if (!g_dbus_proxy_method_call(resolved_proxy, "SetLinkDNS", + setlinkdns_append, NULL, iface, NULL)) + return -EINVAL; + + iface->needs_server_update = FALSE; + } + + if (iface->needs_domain_update) { + if (!g_dbus_proxy_method_call(resolved_proxy, "SetLinkDomains", + setlinkdomains_append, NULL, iface, NULL)) + return -EINVAL; + + iface->needs_domain_update = FALSE; + } + + return 0; +} + +static bool is_empty(struct dns_interface *iface) +{ + if (!iface) + return FALSE; + + return (!iface->domains && !iface->servers); +} + +static void update_interface(gpointer key, gpointer value, gpointer data) +{ + struct dns_interface *iface = value; + GList **removed_items = data; + + set_systemd_resolved_values(iface); + + if (is_empty(iface)) + *removed_items = g_list_prepend(*removed_items, iface); +} + +static int update_systemd_resolved(gpointer data) +{ + GList *removed_items = NULL, *list; + + if (!interface_hash) { + DBG("no interface hash when updating"); + + return G_SOURCE_REMOVE; + } + + g_hash_table_foreach(interface_hash, update_interface, &removed_items); + + for (list = removed_items; list; list = g_list_next(list)) { + struct dns_interface *iface = list->data; + + g_hash_table_remove(interface_hash, + GUINT_TO_POINTER(iface->index)); + } + + g_list_free(removed_items); + + update_interfaces_source = 0; + + return G_SOURCE_REMOVE; +} + +static GList *remove_string(GList *str_list, const char *str) +{ + GList *match = NULL; + + match = g_list_find_custom(str_list, str, + (GCompareFunc) g_strcmp0); + if (match) { + g_free(match->data); + return g_list_delete_link(str_list, match); + } + + return str_list; +} + +static void remove_values(struct dns_interface *iface, const char *domain, + const char *server) +{ + if (!iface) + return; + + if (domain) { + iface->domains = remove_string(iface->domains, domain); + iface->needs_domain_update = TRUE; + } + + if (server) { + iface->servers = remove_string(iface->servers, server); + iface->needs_server_update = TRUE; + } +} + +int __connman_dnsproxy_remove(int index, const char *domain, + const char *server) +{ + struct dns_interface *iface; + + DBG("%d, %s, %s", index, domain ? domain : "no domain", + server ? server : "no server"); + + if (!interface_hash || index < 0) + return -EINVAL; + + iface = g_hash_table_lookup(interface_hash, GUINT_TO_POINTER(index)); + + if (!iface) + return -EINVAL; + + remove_values(iface, domain, server); + + if (!update_interfaces_source) + update_interfaces_source = g_idle_add(update_systemd_resolved, + NULL); + + return 0; +} + +static GList *replace_to_end(GList *str_list, const char *str) +{ + GList *list; + + for (list = str_list; list; list = g_list_next(list)) { + char *orig = list->data; + + if (g_strcmp0(orig, str) == 0) { + str_list = g_list_remove(str_list, orig); + g_free(orig); + break; + } + } + + return g_list_append(str_list, g_strdup(str)); +} + +int __connman_dnsproxy_append(int index, const char *domain, + const char *server) +{ + struct dns_interface *iface; + + DBG("%d, %s, %s", index, domain ? domain : "no domain", + server ? server : "no server"); + + if (!interface_hash || index < 0) + return -EINVAL; + + iface = g_hash_table_lookup(interface_hash, GUINT_TO_POINTER(index)); + + if (!iface) { + iface = g_new0(struct dns_interface, 1); + if (!iface) + return -ENOMEM; + + iface->index = index; + g_hash_table_replace(interface_hash, GUINT_TO_POINTER(index), iface); + } + + if (domain) { + iface->domains = replace_to_end(iface->domains, domain); + iface->needs_domain_update = TRUE; + } + + if (server) { + iface->servers = replace_to_end(iface->servers, server); + iface->needs_server_update = TRUE; + } + + if (!update_interfaces_source) + update_interfaces_source = g_idle_add(update_systemd_resolved, + NULL); + + return 0; +} + +int __connman_dnsproxy_add_listener(int index) +{ + DBG(""); + + return -ENXIO; +} + +void __connman_dnsproxy_remove_listener(int index) +{ + DBG(""); +} + +static int setup_resolved(void) +{ + connection = connman_dbus_get_connection(); + if (!connection) + return -ENXIO; + + client = g_dbus_client_new(connection, SYSTEMD_RESOLVED_SERVICE, + SYSTEMD_RESOLVED_PATH); + + if (!client) + return -EINVAL; + + resolved_proxy = g_dbus_proxy_new(client, SYSTEMD_RESOLVED_PATH, + "org.freedesktop.resolve1.Manager"); + + if (!resolved_proxy) + return -EINVAL; + + return 0; +} + +static void setlinkmulticastdns_append(DBusMessageIter *iter, void *user_data) { + struct mdns_data *data = user_data; + char *val = "no"; + + if (data->enabled) + val = "yes"; + + DBG("SetLinkMulticastDNS: %d/%s", data->index, val); + + dbus_message_iter_append_basic(iter, DBUS_TYPE_INT32, &data->index); + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &val); +} + +int __connman_dnsproxy_set_mdns(int index, bool enabled) +{ + struct mdns_data data = { .index = index, .enabled = enabled }; + + if (!resolved_proxy) + return -ENOENT; + + if (index < 0) + return -EINVAL; + + if (!g_dbus_proxy_method_call(resolved_proxy, "SetLinkMulticastDNS", + setlinkmulticastdns_append, NULL, &data, NULL)) + return -EINVAL; + + return 0; +} + +int __connman_dnsproxy_init(void) +{ + int ret; + + DBG(""); + + ret = setup_resolved(); + if (ret) + return ret; + + interface_hash = g_hash_table_new_full(g_direct_hash, + compare_index, + NULL, + free_dns_interface); + if (!interface_hash) + return -ENOMEM; + + return 0; +} + +void __connman_dnsproxy_cleanup(void) +{ + DBG(""); + + if (update_interfaces_source) { + /* + * It might be that we don't get to an idle loop anymore, so + * run the update function once more to clean up. + */ + g_source_remove(update_interfaces_source); + update_systemd_resolved(NULL); + update_interfaces_source = 0; + } + + if (interface_hash) { + g_hash_table_destroy(interface_hash); + interface_hash = NULL; + } + + if (resolved_proxy) { + g_dbus_proxy_unref(resolved_proxy); + resolved_proxy = NULL; + } + + if (client) { + g_dbus_client_unref(client); + client = NULL; + } + + if (connection) { + dbus_connection_unref(connection); + connection = NULL; + } +} diff --git a/src/dnsproxy.c b/src/dnsproxy.c index 72c0d8f1..cb583251 100755 --- a/src/dnsproxy.c +++ b/src/dnsproxy.c @@ -84,6 +84,11 @@ struct domain_hdr { #error "Unknown byte order" #endif +struct qtype_qclass { + uint16_t qtype; + uint16_t qclass; +} __attribute__ ((packed)); + struct partial_reply { uint16_t len; uint16_t received; @@ -489,7 +494,7 @@ static void send_cached_response(int sk, unsigned char *buf, int len, err, len, dns_len); } -static void send_response(int sk, unsigned char *buf, int len, +static void send_response(int sk, unsigned char *buf, size_t len, const struct sockaddr *to, socklen_t tolen, int protocol) { @@ -501,21 +506,26 @@ static void send_response(int sk, unsigned char *buf, int len, if (offset < 0) return; - if (len < 12) + if (len < sizeof(*hdr) + offset) return; hdr = (void *) (buf + offset); + if (offset) { + buf[0] = 0; + buf[1] = sizeof(*hdr); + } debug("id 0x%04x qr %d opcode %d", hdr->id, hdr->qr, hdr->opcode); hdr->qr = 1; hdr->rcode = ns_r_servfail; + hdr->qdcount = 0; hdr->ancount = 0; hdr->nscount = 0; hdr->arcount = 0; - err = sendto(sk, buf, len, MSG_NOSIGNAL, to, tolen); + err = sendto(sk, buf, sizeof(*hdr) + offset, MSG_NOSIGNAL, to, tolen); if (err < 0) { connman_error("Failed to send DNS response to %d: %s", sk, strerror(errno)); @@ -2265,7 +2275,7 @@ static gboolean udp_server_event(GIOChannel *channel, GIOCondition condition, gpointer user_data) { unsigned char buf[4096]; - int sk, err, len; + int sk, len; struct server_data *data = user_data; if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { @@ -2277,12 +2287,9 @@ static gboolean udp_server_event(GIOChannel *channel, GIOCondition condition, sk = g_io_channel_unix_get_fd(channel); len = recv(sk, buf, sizeof(buf), 0); - if (len < 12) - return TRUE; - err = forward_dns_reply(buf, len, IPPROTO_UDP, data); - if (err < 0) - return TRUE; + if (len >= 12) + forward_dns_reply(buf, len, IPPROTO_UDP, data); #if defined TIZEN_EXT GSList *list; @@ -3137,34 +3144,41 @@ static void dnsproxy_default_changed(struct connman_service *service) cache_refresh(); } -static struct connman_notifier dnsproxy_notifier = { +static const struct connman_notifier dnsproxy_notifier = { .name = "dnsproxy", .default_changed = dnsproxy_default_changed, .offline_mode = dnsproxy_offline_mode, }; -static unsigned char opt_edns0_type[2] = { 0x00, 0x29 }; +static const unsigned char opt_edns0_type[2] = { 0x00, 0x29 }; -static int parse_request(unsigned char *buf, int len, +static int parse_request(unsigned char *buf, size_t len, char *name, unsigned int size) { struct domain_hdr *hdr = (void *) buf; uint16_t qdcount = ntohs(hdr->qdcount); + uint16_t ancount = ntohs(hdr->ancount); + uint16_t nscount = ntohs(hdr->nscount); uint16_t arcount = ntohs(hdr->arcount); unsigned char *ptr; - char *last_label = NULL; unsigned int remain, used = 0; - if (len < 12) + if (len < sizeof(*hdr) + sizeof(struct qtype_qclass) || + hdr->qr || qdcount != 1 || ancount || nscount) { + DBG("Dropped DNS request qr %d with len %zd qdcount %d " + "ancount %d nscount %d", hdr->qr, len, qdcount, ancount, + nscount); + + return -EINVAL; + } + + if (!name || !size) return -EINVAL; debug("id 0x%04x qr %d opcode %d qdcount %d arcount %d", hdr->id, hdr->qr, hdr->opcode, qdcount, arcount); - if (hdr->qr != 0 || qdcount != 1) - return -EINVAL; - name[0] = '\0'; ptr = buf + sizeof(struct domain_hdr); @@ -3174,7 +3188,23 @@ static int parse_request(unsigned char *buf, int len, uint8_t label_len = *ptr; if (label_len == 0x00) { - last_label = (char *) (ptr + 1); + uint8_t class; + struct qtype_qclass *q = + (struct qtype_qclass *)(ptr + 1); + + if (remain < sizeof(*q)) { + DBG("Dropped malformed DNS query"); + return -EINVAL; + } + + class = ntohs(q->qclass); + if (class != 1 && class != 255) { + DBG("Dropped non-IN DNS class %d", class); + return -EINVAL; + } + + ptr += sizeof(*q) + 1; + remain -= (sizeof(*q) + 1); break; } @@ -3190,26 +3220,13 @@ static int parse_request(unsigned char *buf, int len, remain -= label_len + 1; } - if (last_label && arcount && remain >= 9 && last_label[4] == 0 && - !memcmp(last_label + 5, opt_edns0_type, 2)) { - uint16_t edns0_bufsize; - - edns0_bufsize = last_label[7] << 8 | last_label[8]; + if (arcount && remain >= sizeof(struct domain_rr) + 1 && !ptr[0] && + ptr[1] == opt_edns0_type[0] && ptr[2] == opt_edns0_type[1]) { + struct domain_rr *edns0 = (struct domain_rr *)(ptr + 1); - debug("EDNS0 buffer size %u", edns0_bufsize); - - /* This is an evil hack until full TCP support has been - * implemented. - * - * Somtimes the EDNS0 request gets send with a too-small - * buffer size. Since glibc doesn't seem to crash when it - * gets a response biffer then it requested, just bump - * the buffer size up to 4KiB. - */ - if (edns0_bufsize < 0x1000) { - last_label[7] = 0x10; - last_label[8] = 0x00; - } + DBG("EDNS0 buffer size %u", ntohs(edns0->class)); + } else if (!arcount && remain) { + DBG("DNS request with %d garbage bytes", remain); } debug("query %s", name); @@ -3902,9 +3919,6 @@ static GIOChannel *get_listener(int family, int protocol, int index) return NULL; } - /* ConnMan listens DNS from multiple interfaces - * E.g. various technology based and tethering interfaces - */ interface = connman_inet_ifname(index); if (!interface || setsockopt(sk, SOL_SOCKET, SO_BINDTODEVICE, interface, @@ -3941,6 +3955,7 @@ static GIOChannel *get_listener(int family, int protocol, int index) s.sin.sin_family = AF_INET; s.sin.sin_port = htons(53); slen = sizeof(s.sin); + if (__connman_inet_get_interface_address(index, AF_INET, &s.sin.sin_addr) < 0) { @@ -3952,6 +3967,7 @@ static GIOChannel *get_listener(int family, int protocol, int index) return NULL; } #endif + #if defined TIZEN_EXT /* When ConnMan crashed, * probably DNS listener cannot bind existing address */ @@ -3971,6 +3987,7 @@ static GIOChannel *get_listener(int family, int protocol, int index) #endif if (protocol == IPPROTO_TCP) { + #if !defined TIZEN_EXT if (listen(sk, 10) < 0) { connman_error("Failed to listen on TCP socket %d/%s", @@ -3978,6 +3995,7 @@ static GIOChannel *get_listener(int family, int protocol, int index) close(sk); return NULL; } + #endif fcntl(sk, F_SETFL, O_NONBLOCK); } @@ -4275,6 +4293,11 @@ destroy: return err; } +int __connman_dnsproxy_set_mdns(int index, bool enabled) +{ + return -ENOTSUP; +} + void __connman_dnsproxy_cleanup(void) { DBG(""); @@ -4296,4 +4319,9 @@ void __connman_dnsproxy_cleanup(void) g_hash_table_destroy(listener_table); g_hash_table_destroy(partial_tcp_req_table); + + if (ipv4_resolve) + g_resolv_unref(ipv4_resolve); + if (ipv6_resolve) + g_resolv_unref(ipv6_resolve); } diff --git a/src/firewall-iptables.c b/src/firewall-iptables.c index 45943a82..1b04648b 100644 --- a/src/firewall-iptables.c +++ b/src/firewall-iptables.c @@ -92,15 +92,17 @@ static int insert_managed_chain(const char *table_name, int id) managed_chain = g_strdup_printf("%s%s", CHAIN_PREFIX, builtin_chains[id]); - err = __connman_iptables_new_chain(table_name, managed_chain); + err = __connman_iptables_new_chain(AF_INET, table_name, managed_chain); if (err < 0) goto out; rule = g_strdup_printf("-j %s", managed_chain); - err = __connman_iptables_insert(table_name, builtin_chains[id], rule); + err = __connman_iptables_insert(AF_INET, table_name, builtin_chains[id], + rule); g_free(rule); if (err < 0) { - __connman_iptables_delete_chain(table_name, managed_chain); + __connman_iptables_delete_chain(AF_INET, table_name, + managed_chain); goto out; } @@ -119,13 +121,15 @@ static int delete_managed_chain(const char *table_name, int id) builtin_chains[id]); rule = g_strdup_printf("-j %s", managed_chain); - err = __connman_iptables_delete(table_name, builtin_chains[id], rule); + err = __connman_iptables_delete(AF_INET, table_name, builtin_chains[id], + rule); g_free(rule); if (err < 0) goto out; - err = __connman_iptables_delete_chain(table_name, managed_chain); + err = __connman_iptables_delete_chain(AF_INET, table_name, + managed_chain); out: g_free(managed_chain); @@ -178,7 +182,7 @@ static int insert_managed_rule(const char *table_name, chain = g_strdup_printf("%s%s", CHAIN_PREFIX, chain_name); out: - err = __connman_iptables_append(table_name, chain, rule_spec); + err = __connman_iptables_append(AF_INET, table_name, chain, rule_spec); g_free(chain); @@ -197,14 +201,14 @@ static int delete_managed_rule(const char *table_name, id = chain_to_index(chain_name); if (id < 0) { /* This chain is not managed */ - return __connman_iptables_delete(table_name, chain_name, - rule_spec); + return __connman_iptables_delete(AF_INET, table_name, + chain_name, rule_spec); } managed_chain = g_strdup_printf("%s%s", CHAIN_PREFIX, chain_name); - err = __connman_iptables_delete(table_name, managed_chain, - rule_spec); + err = __connman_iptables_delete(AF_INET, table_name, managed_chain, + rule_spec); for (list = managed_tables; list; list = list->next) { mtable = list->data; @@ -281,7 +285,7 @@ static int enable_rule(struct fw_rule *rule) if (err < 0) return err; - err = __connman_iptables_commit(rule->table); + err = __connman_iptables_commit(AF_INET, rule->table); if (err < 0) return err; @@ -304,7 +308,7 @@ static int disable_rule(struct fw_rule *rule) return err; } - err = __connman_iptables_commit(rule->table); + err = __connman_iptables_commit(AF_INET, rule->table); if (err < 0) { connman_error("Cannot remove previously installed " "iptables rules: %s", strerror(-err)); @@ -343,16 +347,8 @@ static void firewall_add_rule(struct firewall_context *ctx, static void firewall_remove_rules(struct firewall_context *ctx) { - struct fw_rule *rule; - GList *list; - - for (list = g_list_last(ctx->rules); list; - list = g_list_previous(list)) { - rule = list->data; - - ctx->rules = g_list_remove(ctx->rules, rule); - cleanup_fw_rule(rule); - } + g_list_free_full(ctx->rules, cleanup_fw_rule); + ctx->rules = NULL; } static int firewall_enable_rules(struct firewall_context *ctx) @@ -399,14 +395,12 @@ int __connman_firewall_enable_nat(struct firewall_context *ctx, char *address, unsigned char prefixlen, char *interface) { - char *cmd; int err; - cmd = g_strdup_printf("-s %s/%d -o %s -j MASQUERADE", - address, prefixlen, interface); + firewall_add_rule(ctx, "nat", "POSTROUTING", + "-s %s/%d -o %s -j MASQUERADE", + address, prefixlen, interface); - firewall_add_rule(ctx, "nat", "POSTROUTING", cmd); - g_free(cmd); err = firewall_enable_rules(ctx); if (err) firewall_remove_rules(ctx); @@ -555,8 +549,8 @@ static void flush_table(const char *table_name) char *rule, *managed_chain; int id, err; - __connman_iptables_iterate_chains(table_name, iterate_chains_cb, - &chains); + __connman_iptables_iterate_chains(AF_INET, table_name, + iterate_chains_cb, &chains); for (list = chains; list; list = list->next) { id = GPOINTER_TO_INT(list->data); @@ -565,7 +559,7 @@ static void flush_table(const char *table_name) builtin_chains[id]); rule = g_strdup_printf("-j %s", managed_chain); - err = __connman_iptables_delete(table_name, + err = __connman_iptables_delete(AF_INET, table_name, builtin_chains[id], rule); if (err < 0) { connman_warn("Failed to delete jump rule '%s': %s", @@ -573,12 +567,14 @@ static void flush_table(const char *table_name) } g_free(rule); - err = __connman_iptables_flush_chain(table_name, managed_chain); + err = __connman_iptables_flush_chain(AF_INET, table_name, + managed_chain); if (err < 0) { connman_warn("Failed to flush chain '%s': %s", managed_chain, strerror(-err)); } - err = __connman_iptables_delete_chain(table_name, managed_chain); + err = __connman_iptables_delete_chain(AF_INET, table_name, + managed_chain); if (err < 0) { connman_warn("Failed to delete chain '%s': %s", managed_chain, strerror(-err)); @@ -587,7 +583,7 @@ static void flush_table(const char *table_name) g_free(managed_chain); } - err = __connman_iptables_commit(table_name); + err = __connman_iptables_commit(AF_INET, table_name); if (err < 0) { connman_warn("Failed to flush table '%s': %s", table_name, strerror(-err)); diff --git a/src/firewall-nftables.c b/src/firewall-nftables.c index 1febce44..262b2a90 100644 --- a/src/firewall-nftables.c +++ b/src/firewall-nftables.c @@ -67,7 +67,7 @@ #define CONNMAN_CHAIN_NAT_POST "nat-postrouting" #define CONNMAN_CHAIN_ROUTE_OUTPUT "route-output" -static bool debug_enabled = true; +static bool debug_enabled = false; struct firewall_handle { uint64_t handle; @@ -402,6 +402,8 @@ static int table_cmd(struct mnl_socket *nl, struct nftnl_table *t, uint32_t seq = 0; int err; + bzero(buf, sizeof(buf)); + batch = mnl_nlmsg_batch_start(buf, sizeof(buf)); nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); mnl_nlmsg_batch_next(batch); @@ -433,6 +435,8 @@ static int chain_cmd(struct mnl_socket *nl, struct nftnl_chain *chain, uint32_t seq = 0; int err; + bzero(buf, sizeof(buf)); + batch = mnl_nlmsg_batch_start(buf, sizeof(buf)); nftnl_batch_begin(mnl_nlmsg_batch_current(batch), seq++); mnl_nlmsg_batch_next(batch); @@ -465,6 +469,8 @@ static int rule_cmd(struct mnl_socket *nl, struct nftnl_rule *rule, uint32_t seq = 0; int err; + bzero(buf, sizeof(buf)); + debug_netlink_dump_rule(rule); batch = mnl_nlmsg_batch_start(buf, sizeof(buf)); @@ -670,7 +676,7 @@ static int build_rule_snat(int index, const char *address, nftnl_rule_set(rule, NFTNL_RULE_TABLE, CONNMAN_TABLE); nftnl_rule_set(rule, NFTNL_RULE_CHAIN, CONNMAN_CHAIN_NAT_POST); - /* IOF */ + /* OIF */ expr = nftnl_expr_alloc("meta"); if (!expr) goto err; @@ -1003,7 +1009,7 @@ static int create_table_and_chains(struct nftables_info *nft_info) /* * # nft add chain connman nat-prerouting \ - * { type nat hook prerouting priortiy 0 ; } + * { type nat hook prerouting priority 0 ; } */ chain = build_chain(CONNMAN_CHAIN_NAT_PRE, CONNMAN_TABLE, "nat", NF_INET_PRE_ROUTING, 0); @@ -1020,7 +1026,7 @@ static int create_table_and_chains(struct nftables_info *nft_info) /* * # nft add chain connman nat-postrouting \ - * { type nat hook postrouting priortiy 0 ; } + * { type nat hook postrouting priority 0 ; } */ chain = build_chain(CONNMAN_CHAIN_NAT_POST, CONNMAN_TABLE, "nat", NF_INET_POST_ROUTING, 0); @@ -25,7 +25,6 @@ #include <config.h> #endif -#define _GNU_SOURCE #include <stdio.h> #include <errno.h> #include <unistd.h> @@ -262,6 +261,40 @@ char *connman_inet_ifname2addr(const char *name) } #endif +bool __connman_inet_is_any_addr(const char *address, int family) +{ + bool ret = false; + struct addrinfo hints; + struct addrinfo *result = NULL; + struct sockaddr_in6 *in6 = NULL; + struct sockaddr_in *in4 = NULL; + + if (!address || !*address) + goto out; + + memset(&hints, 0, sizeof(struct addrinfo)); + + hints.ai_family = family; + + if (getaddrinfo(address, NULL, &hints, &result)) + goto out; + + if (result) { + if (result->ai_family == AF_INET6) { + in6 = (struct sockaddr_in6*)result->ai_addr; + ret = IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr); + } else if (result->ai_family == AF_INET) { + in4 = (struct sockaddr_in*)result->ai_addr; + ret = in4->sin_addr.s_addr == INADDR_ANY; + } + + freeaddrinfo(result); + } + +out: + return ret; +} + int connman_inet_ifindex(const char *name) { struct ifreq ifr; @@ -442,6 +475,40 @@ void connman_inet_update_device_ident(struct connman_device *device) } #endif +bool connman_inet_is_ifup(int index) +{ + int sk; + struct ifreq ifr; + bool ret = false; + + sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (sk < 0) { + connman_warn("Failed to open socket"); + return false; + } + + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_ifindex = index; + + if (ioctl(sk, SIOCGIFNAME, &ifr) < 0) { + connman_warn("Failed to get interface name for interface %d", index); + goto done; + } + + if (ioctl(sk, SIOCGIFFLAGS, &ifr) < 0) { + connman_warn("Failed to get interface flags for index %d", index); + goto done; + } + + if (ifr.ifr_flags & IFF_UP) + ret = true; + +done: + close(sk); + + return ret; +} + struct in6_ifreq { struct in6_addr ifr6_addr; __u32 ifr6_prefixlen; @@ -590,7 +657,17 @@ int connman_inet_add_network_route(int index, const char *host, memset(&rt, 0, sizeof(rt)); rt.rt_flags = RTF_UP; - if (gateway) + + /* + * Set RTF_GATEWAY only when gateway is set and the gateway IP address + * is not IPv4 any address (0.0.0.0). If the given gateway IP address is + * any address adding of route will fail when RTF_GATEWAY set. Passing + * gateway as NULL or INADDR_ANY should have the same effect. Setting + * the gateway address later to the struct is not affected by this, + * since given IPv4 any address (0.0.0.0) equals the value set with + * INADDR_ANY. + */ + if (gateway && !__connman_inet_is_any_addr(gateway, AF_INET)) rt.rt_flags |= RTF_GATEWAY; if (!netmask) rt.rt_flags |= RTF_HOST; @@ -753,10 +830,17 @@ int connman_inet_add_ipv6_network_route(int index, const char *host, rt.rtmsg_flags = RTF_UP | RTF_HOST; - if (gateway) { + /* + * Set RTF_GATEWAY only when gateway is set, the gateway IP address is + * not IPv6 any address (e.g., ::) and the address is valid (conversion + * succeeds). If the given gateway IP address is any address then + * adding of route will fail when RTF_GATEWAY set. Passing gateway as + * NULL or IPv6 any address should have the same effect. + */ + + if (gateway && !__connman_inet_is_any_addr(gateway, AF_INET6) && + inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway) > 0) rt.rtmsg_flags |= RTF_GATEWAY; - inet_pton(AF_INET6, gateway, &rt.rtmsg_gateway); - } rt.rtmsg_metric = 1; rt.rtmsg_ifindex = index; @@ -2433,6 +2517,7 @@ static gboolean inet_rtnl_event(GIOChannel *chan, GIOCondition cond, return TRUE; cleanup: + rtnl_data->callback(NULL, rtnl_data->user_data); inet_rtnl_cleanup(rtnl_data); return TRUE; } @@ -2585,8 +2670,6 @@ out: data->callback(addr, index, data->user_data); g_free(data); - - return; } /* @@ -2678,9 +2761,10 @@ int connman_inet_check_ipaddress(const char *host) addr = NULL; result = getaddrinfo(host, NULL, &hints, &addr); - if (result == 0) + if (result == 0) { result = addr->ai_family; - freeaddrinfo(addr); + freeaddrinfo(addr); + } return result; } @@ -2799,8 +2883,7 @@ char **__connman_inet_get_running_interfaces(void) g_free(ifr); - if (count < numif) - { + if (count < numif) { char **prev_result = result; result = g_try_realloc(result, (count + 1) * sizeof(char *)); if (!result) { @@ -2883,6 +2966,41 @@ out: return err; } +int __connman_inet_get_interface_mac_address(int index, uint8_t *mac_address) +{ + struct ifreq ifr; + int sk, err; + int ret = -EINVAL; + + sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (sk < 0) { + DBG("Open socket error"); + return ret; + } + + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_ifindex = index; + + err = ioctl(sk, SIOCGIFNAME, &ifr); + if (err < 0) { + DBG("Get interface name error"); + goto done; + } + + err = ioctl(sk, SIOCGIFHWADDR, &ifr); + if (err < 0) { + DBG("Get MAC address error"); + goto done; + } + + memcpy(mac_address, ifr.ifr_hwaddr.sa_data, ETH_ALEN); + ret = 0; + +done: + close(sk); + return ret; +} + static int iprule_modify(int cmd, int family, uint32_t table_id, uint32_t fwmark) { @@ -2945,12 +3063,15 @@ int __connman_inet_del_fwmark_rule(uint32_t table_id, int family, uint32_t fwmar } static int iproute_default_modify(int cmd, uint32_t table_id, int ifindex, - const char *gateway) + const char *gateway, unsigned char prefixlen) { struct __connman_inet_rtnl_handle rth; unsigned char buf[sizeof(struct in6_addr)]; int ret, len; int family = connman_inet_check_ipaddress(gateway); + char *dst = NULL; + + DBG("gateway %s/%u table %u", gateway, prefixlen, table_id); switch (family) { case AF_INET: @@ -2963,7 +3084,19 @@ static int iproute_default_modify(int cmd, uint32_t table_id, int ifindex, return -EINVAL; } - ret = inet_pton(family, gateway, buf); + if (prefixlen) { + struct in_addr ipv4_subnet_addr, ipv4_mask; + + memset(&ipv4_subnet_addr, 0, sizeof(ipv4_subnet_addr)); + ipv4_mask.s_addr = htonl((0xffffffff << (32 - prefixlen)) & 0xffffffff); + ipv4_subnet_addr.s_addr = inet_addr(gateway); + ipv4_subnet_addr.s_addr &= ipv4_mask.s_addr; + + dst = g_strdup(inet_ntoa(ipv4_subnet_addr)); + } + + ret = inet_pton(family, dst ? dst : gateway, buf); + g_free(dst); if (ret <= 0) return -EINVAL; @@ -2978,9 +3111,11 @@ static int iproute_default_modify(int cmd, uint32_t table_id, int ifindex, rth.req.u.r.rt.rtm_protocol = RTPROT_BOOT; rth.req.u.r.rt.rtm_scope = RT_SCOPE_UNIVERSE; rth.req.u.r.rt.rtm_type = RTN_UNICAST; + rth.req.u.r.rt.rtm_dst_len = prefixlen; + + __connman_inet_rtnl_addattr_l(&rth.req.n, sizeof(rth.req), + prefixlen > 0 ? RTA_DST : RTA_GATEWAY, buf, len); - __connman_inet_rtnl_addattr_l(&rth.req.n, sizeof(rth.req), RTA_GATEWAY, - buf, len); if (table_id < 256) { rth.req.u.r.rt.rtm_table = table_id; } else { @@ -3009,7 +3144,14 @@ int __connman_inet_add_default_to_table(uint32_t table_id, int ifindex, { /* ip route add default via 1.2.3.4 dev wlan0 table 1234 */ - return iproute_default_modify(RTM_NEWROUTE, table_id, ifindex, gateway); + return iproute_default_modify(RTM_NEWROUTE, table_id, ifindex, gateway, 0); +} + +int __connman_inet_add_subnet_to_table(uint32_t table_id, int ifindex, + const char *gateway, unsigned char prefixlen) +{ + /* ip route add 1.2.3.4/24 dev eth0 table 1234 */ + return iproute_default_modify(RTM_NEWROUTE, table_id, ifindex, gateway, prefixlen); } int __connman_inet_del_default_from_table(uint32_t table_id, int ifindex, @@ -3017,7 +3159,14 @@ int __connman_inet_del_default_from_table(uint32_t table_id, int ifindex, { /* ip route del default via 1.2.3.4 dev wlan0 table 1234 */ - return iproute_default_modify(RTM_DELROUTE, table_id, ifindex, gateway); + return iproute_default_modify(RTM_DELROUTE, table_id, ifindex, gateway, 0); +} + +int __connman_inet_del_subnet_from_table(uint32_t table_id, int ifindex, + const char *gateway, unsigned char prefixlen) +{ + /* ip route del 1.2.3.4/24 dev eth0 table 1234 */ + return iproute_default_modify(RTM_DELROUTE, table_id, ifindex, gateway, prefixlen); } int __connman_inet_get_interface_ll_address(int index, int family, @@ -3152,10 +3301,8 @@ static int get_nfs_server_ip(const char *cmdline_file, const char *pnp_file, pnp_file, error->message); goto out; } - } else { - connman_error("%s: File %s doesn't exist\n", __func__, pnp_file); + } else goto out; - } len = strlen(cmdline); if (len <= 1) { @@ -3351,6 +3498,9 @@ char **__connman_inet_get_pnp_nameservers(const char *pnp_file) if (!pnp_file) pnp_file = "/proc/net/pnp"; + if (!g_file_test(pnp_file, G_FILE_TEST_EXISTS)) + goto out; + if (!g_file_get_contents(pnp_file, &pnp, NULL, &error)) { connman_error("%s: Cannot read %s %s\n", __func__, pnp_file, error->message); diff --git a/src/ipconfig.c b/src/ipconfig.c index d94b8734..fb39f64d 100755 --- a/src/ipconfig.c +++ b/src/ipconfig.c @@ -1157,6 +1157,7 @@ static struct connman_ipconfig *create_ipv6config(int index) #else ipv6config->ipv6_privacy_config = ipdevice->ipv6_privacy = 2; #endif + ipv6config->address = connman_ipaddress_alloc(AF_INET6); if (!ipv6config->address) { g_free(ipv6config); @@ -2104,6 +2105,7 @@ int __connman_ipconfig_set_config(struct connman_ipconfig *ipconfig, if (ipconfig->type == CONNMAN_IPCONFIG_TYPE_IPV6) disable_ipv6(ipconfig); #endif + break; case CONNMAN_IPCONFIG_METHOD_AUTO: @@ -2116,6 +2118,7 @@ int __connman_ipconfig_set_config(struct connman_ipconfig *ipconfig, #if defined TIZEN_EXT enable_ipv6(ipconfig); #endif + break; case CONNMAN_IPCONFIG_METHOD_MANUAL: diff --git a/src/ippool.c b/src/ippool.c index cea1dccd..f2e9b000 100755 --- a/src/ippool.c +++ b/src/ippool.c @@ -28,7 +28,6 @@ #include <stdio.h> #include <string.h> #include <unistd.h> -#include <sys/errno.h> #include <sys/socket.h> #include "connman.h" @@ -43,8 +42,6 @@ struct address_info { }; struct connman_ippool { - unsigned int refcount; - struct address_info *info; char *gateway; @@ -65,30 +62,11 @@ static uint32_t block_20_bits; static uint32_t block_24_bits; static uint32_t subnet_mask_24; -struct connman_ippool * -__connman_ippool_ref_debug(struct connman_ippool *pool, - const char *file, int line, const char *caller) -{ - DBG("%p ref %d by %s:%d:%s()", pool, pool->refcount + 1, - file, line, caller); - - __sync_fetch_and_add(&pool->refcount, 1); - - return pool; -} - -void __connman_ippool_unref_debug(struct connman_ippool *pool, - const char *file, int line, const char *caller) +void __connman_ippool_free(struct connman_ippool *pool) { if (!pool) return; - DBG("%p ref %d by %s:%d:%s()", pool, pool->refcount - 1, - file, line, caller); - - if (__sync_fetch_and_sub(&pool->refcount, 1) != 1) - return; - if (pool->info) { allocated_blocks = g_slist_remove(allocated_blocks, pool->info); g_free(pool->info); @@ -386,7 +364,6 @@ struct connman_ippool *__connman_ippool_create(int index, info->start = block; info->end = block + range; - pool->refcount = 1; pool->info = info; pool->collision_cb = collision_cb; pool->user_data = user_data; diff --git a/src/iptables.c b/src/iptables.c index aaddf9d6..9cfd80f8 100755 --- a/src/iptables.c +++ b/src/iptables.c @@ -28,12 +28,14 @@ #include <stdio.h> #include <string.h> #include <unistd.h> -#include <sys/errno.h> +#include <errno.h> #include <sys/socket.h> #include <xtables.h> #include <inttypes.h> +#include <setjmp.h> #include <linux/netfilter_ipv4/ip_tables.h> +#include <linux/netfilter_ipv6/ip6_tables.h> #include "connman.h" #include "src/shared/util.h" @@ -128,6 +130,10 @@ * End of CHAIN */ +/* + * Values for the index values used here are defined as equal for both IPv4 + * and IPv6 (NF_IP_* and NF_IP6_*) in Netfilter headers. + */ static const char *hooknames[] = { [NF_IP_PRE_ROUTING] = "PREROUTING", [NF_IP_LOCAL_IN] = "INPUT", @@ -143,29 +149,25 @@ static const char *hooknames[] = { #define XT_OPTION_OFFSET_SCALE 256 -#define MIN_ALIGN (__alignof__(struct ipt_entry)) - -#define ALIGN(s) (((s) + ((MIN_ALIGN)-1)) & ~((MIN_ALIGN)-1)) - -struct error_target { - struct xt_entry_target t; - char error[IPT_TABLE_MAXNAMELEN]; -}; - struct connman_iptables_entry { - int offset; + int type; + unsigned int offset; int builtin; int counter_idx; struct ipt_entry *entry; + struct ip6t_entry *entry6; }; struct connman_iptables { + int type; char *name; int ipt_sock; struct ipt_getinfo *info; struct ipt_get_entries *blob_entries; + struct ip6t_getinfo *info6; + struct ip6t_get_entries *blob_entries6; unsigned int num_entries; unsigned int old_entries; @@ -178,11 +180,307 @@ struct connman_iptables { }; static GHashTable *table_hash = NULL; +static GHashTable *table_hash_ipv6 = NULL; static bool debug_enabled = false; -typedef int (*iterate_entries_cb_t)(struct ipt_entry *entry, int builtin, - unsigned int hook, size_t size, - unsigned int offset, void *user_data); +struct iptables_ip { + int type; + struct ipt_ip *ip; + struct ip6t_ip6 *ip6; +}; + +struct iptables_replace { + int type; + struct ipt_replace *r; + struct ip6t_replace *r6; +}; + +static jmp_buf env_state; +static bool jmp_set = false; + +static void enable_jmp() +{ + jmp_set = true; +} + +static void disable_jmp() +{ + jmp_set = false; +} + +static bool can_jmp() +{ + DBG("%s", jmp_set ? "true" : "false"); + return jmp_set; +} + +typedef int (*iterate_entries_cb_t)(struct connman_iptables_entry *entry, + int builtin, unsigned int hook, + size_t size, unsigned int offset, + void *user_data); + +static u_int16_t iptables_entry_get_next_offset( + struct connman_iptables_entry *entry) +{ + if (!entry) + return 0; + + switch (entry->type) { + case AF_INET: + return entry->entry ? entry->entry->next_offset : 0; + case AF_INET6: + return entry->entry6 ? entry->entry6->next_offset : 0; + } + + return 0; +} + +static u_int16_t iptables_entry_get_target_offset( + struct connman_iptables_entry *entry) +{ + if (!entry) + return 0; + + switch (entry->type) { + case AF_INET: + return entry->entry ? entry->entry->target_offset : 0; + case AF_INET6: + return entry->entry6 ? entry->entry6->target_offset : 0; + } + + return 0; +} + +static unsigned char *iptables_entry_get_elems( + struct connman_iptables_entry *entry) +{ + if (!entry) + return NULL; + + switch (entry->type) { + case AF_INET: + return entry->entry ? entry->entry->elems : NULL; + case AF_INET6: + return entry->entry6 ? entry->entry6->elems : NULL; + } + + return NULL; +} + +static struct xt_entry_target *iptables_entry_get_target( + struct connman_iptables_entry *entry) +{ + if (!entry) + return NULL; + + switch (entry->type) { + case AF_INET: + return entry->entry ? ipt_get_target(entry->entry) : NULL; + case AF_INET6: + return entry->entry6 ? ip6t_get_target(entry->entry6) : NULL; + } + + return NULL; +} + +static struct xt_counters *iptables_entry_get_counters( + struct connman_iptables_entry *entry) +{ + if (!entry) + return NULL; + + switch (entry->type) { + case AF_INET: + return entry->entry ? &entry->entry->counters : NULL; + case AF_INET6: + return entry->entry6 ? &entry->entry6->counters : NULL; + } + + return NULL; +} + +static void iptables_entry_free(struct connman_iptables_entry *entry) +{ + if (!entry) + return; + + g_free(entry->entry); + g_free(entry->entry6); + g_free(entry); +} + +static const char *iptables_table_get_info_name(struct connman_iptables* table) +{ + if (!table) + return NULL; + + switch (table->type) { + case AF_INET: + return table->info->name; + case AF_INET6: + return table->info6->name; + } + + return NULL; +} + +static unsigned int iptables_table_get_info_num_entries( + struct connman_iptables* table) +{ + if (!table) + return 0; + + switch (table->type) { + case AF_INET: + return table->info->num_entries; + case AF_INET6: + return table->info6->num_entries; + } + + return 0; +} + +static unsigned int iptables_table_get_info_size(struct connman_iptables* table) +{ + if (!table) + return 0; + + switch (table->type) { + case AF_INET: + return table->info->size; + case AF_INET6: + return table->info6->size; + } + + return 0; +} + +static unsigned int iptables_table_get_info_valid_hooks( + struct connman_iptables* table) +{ + if (!table) + return 0; + + switch (table->type) { + case AF_INET: + return table->info->valid_hooks; + case AF_INET6: + return table->info6->valid_hooks; + } + + return 0; +} + +static unsigned int *iptables_table_get_info_hook_entry( + struct connman_iptables* table) +{ + if (!table) + return NULL; + + switch (table->type) { + case AF_INET: + return table->info->hook_entry; + case AF_INET6: + return table->info6->hook_entry; + } + + return NULL; +} + +static unsigned int *iptables_table_get_info_underflow( + struct connman_iptables* table) +{ + if (!table) + return NULL; + + switch (table->type) { + case AF_INET: + return table->info->underflow; + case AF_INET6: + return table->info6->underflow; + } + + return NULL; +} + +static unsigned int iptables_table_get_entries_size( + struct connman_iptables* table) +{ + if (!table) + return 0; + + switch (table->type) { + case AF_INET: + return table->blob_entries->size; + case AF_INET6: + return table->blob_entries6->size; + } + + return 0; +} + +static const char *get_error_target(int type) +{ + switch (type) { + case AF_INET: + return IPT_ERROR_TARGET; + case AF_INET6: + return IP6T_ERROR_TARGET; + default: + return XT_ERROR_TARGET; + } +} + +static const char *get_standard_target(int type) +{ + switch (type) { + case AF_INET: + return IPT_STANDARD_TARGET; + case AF_INET6: + return IP6T_STANDARD_TARGET; + default: + return XT_STANDARD_TARGET; + } +} + +static struct connman_iptables *hash_table_lookup(int type, + const char *table_name) { + + switch (type) { + case AF_INET: + return g_hash_table_lookup(table_hash, table_name); + case AF_INET6: + return g_hash_table_lookup(table_hash_ipv6, table_name); + } + + return NULL; +} + +static bool hash_table_replace(int type, + char *table_name, + struct connman_iptables *table) { + + switch (type) { + case AF_INET: + return g_hash_table_replace(table_hash, table_name, table); + case AF_INET6: + return g_hash_table_replace(table_hash_ipv6, table_name, table); + } + + return false; +} + +static bool hash_table_remove(int type, const char *table_name) +{ + switch (type) { + case AF_INET: + return g_hash_table_remove(table_hash, table_name); + case AF_INET6: + return g_hash_table_remove(table_hash_ipv6, table_name); + } + + return false; +} static unsigned int next_hook_entry_index(unsigned int *valid_hooks) { @@ -197,7 +495,7 @@ static unsigned int next_hook_entry_index(unsigned int *valid_hooks) return h; } -static int iterate_entries(struct ipt_entry *entries, +static int iterate_entries(struct connman_iptables_entry *entries, unsigned int valid_hooks, unsigned int *hook_entry, unsigned int *underflow, @@ -206,15 +504,45 @@ static int iterate_entries(struct ipt_entry *entries, { unsigned int offset, h, hook; int builtin, err; - struct ipt_entry *entry; + struct connman_iptables_entry entry; + + if (!entries) + return -EINVAL; + + switch (entries->type) { + case AF_INET: + if (!entries->entry) + return -EINVAL; + + break; + case AF_INET6: + if (!entries->entry6) + return -EINVAL; + + break; + default: + return -EINVAL; + } h = next_hook_entry_index(&valid_hooks); hook = h; - for (offset = 0, entry = entries; offset < size; - offset += entry->next_offset) { + entry.type = entries->type; + entry.entry = entries->entry; + entry.entry6 = entries->entry6; + + for (offset = 0; offset < size; + offset += iptables_entry_get_next_offset(&entry)) { builtin = -1; - entry = (void *)entries + offset; + + switch (entries->type) { + case AF_INET: + entry.entry = (void* )entries->entry + offset; + break; + case AF_INET6: + entry.entry6 = (void* )entries->entry6 + offset; + break; + } /* * Updating builtin, hook and h is very tricky. @@ -239,7 +567,7 @@ static int iterate_entries(struct ipt_entry *entries, if (h < NF_INET_NUMHOOKS && underflow[h] <= offset) h = next_hook_entry_index(&valid_hooks); - err = cb(entry, builtin, hook, size, offset, user_data); + err = cb(&entry, builtin, hook, size, offset, user_data); if (err < 0) return err; } @@ -247,15 +575,20 @@ static int iterate_entries(struct ipt_entry *entries, return 0; } -static int print_entry(struct ipt_entry *entry, int builtin, unsigned int hook, - size_t size, unsigned int offset, - void *user_data) +static int print_entry(struct connman_iptables_entry *entry, int builtin, + unsigned int hook, size_t size, + unsigned int offset, void *user_data) { - iterate_entries_cb_t cb = user_data; + iterate_entries_cb_t cb; + struct xt_counters *counters; - DBG("entry %p hook %u offset %u size %u packets %"PRIu64" bytes %"PRIu64, - entry, hook, offset, (unsigned int) entry->next_offset, - (uint64_t) entry->counters.pcnt, (uint64_t) entry->counters.bcnt); + cb = user_data; + counters = iptables_entry_get_counters(entry); + + DBG("entry %p hook %u offset %u size %u packets %"PRIu64" " + "bytes %"PRIu64, entry, hook, offset, + iptables_entry_get_next_offset(entry), + (uint64_t) counters->pcnt, (uint64_t) counters->bcnt); return cb(entry, builtin, hook, size, offset, NULL); } @@ -292,9 +625,12 @@ static bool is_jump(struct connman_iptables_entry *e) { struct xt_entry_target *target; - target = ipt_get_target(e->entry); + target = iptables_entry_get_target(e); + + if (!target) + return false; - if (!g_strcmp0(target->u.user.name, IPT_STANDARD_TARGET)) { + if (!g_strcmp0(target->u.user.name, get_standard_target(e->type))) { struct xt_standard_target *t; t = (struct xt_standard_target *)target; @@ -319,8 +655,12 @@ static bool is_fallthrough(struct connman_iptables_entry *e) { struct xt_entry_target *target; - target = ipt_get_target(e->entry); - if (!g_strcmp0(target->u.user.name, IPT_STANDARD_TARGET)) { + target = iptables_entry_get_target(e); + + if (!target) + return false; + + if (!g_strcmp0(target->u.user.name, get_standard_target(e->type))) { struct xt_standard_target *t; t = (struct xt_standard_target *)target; @@ -333,15 +673,20 @@ static bool is_fallthrough(struct connman_iptables_entry *e) static bool is_chain(struct connman_iptables *table, struct connman_iptables_entry *e) { - struct ipt_entry *entry; struct xt_entry_target *target; - entry = e->entry; + if (!e) + return false; + if (e->builtin >= 0) return true; - target = ipt_get_target(entry); - if (!g_strcmp0(target->u.user.name, IPT_ERROR_TARGET)) + target = iptables_entry_get_target(e); + + if (!target) + return false; + + if (!g_strcmp0(target->u.user.name, get_error_target(e->type))) return true; return false; @@ -352,23 +697,35 @@ static GList *find_chain_head(struct connman_iptables *table, { GList *list; struct connman_iptables_entry *head; - struct ipt_entry *entry; struct xt_entry_target *target; int builtin; + + switch (table->type) { + case AF_INET: + case AF_INET6: + break; + default: + return NULL; + } for (list = table->entries; list; list = list->next) { head = list->data; - entry = head->entry; /* Buit-in chain */ builtin = head->builtin; + if (builtin >= 0 && !g_strcmp0(hooknames[builtin], chain_name)) break; /* User defined chain */ - target = ipt_get_target(entry); - if (!g_strcmp0(target->u.user.name, IPT_ERROR_TARGET) && - !g_strcmp0((char *)target->data, chain_name)) + target = iptables_entry_get_target(head); + + if (!target) + continue; + + if (!g_strcmp0(target->u.user.name, + get_error_target(table->type)) && + !g_strcmp0((char *)target->data, chain_name)) break; } @@ -397,7 +754,6 @@ static GList *find_chain_tail(struct connman_iptables *table, return g_list_last(table->entries); } - static void update_offsets(struct connman_iptables *table) { GList *list, *prev; @@ -416,7 +772,8 @@ static void update_offsets(struct connman_iptables *table) prev_entry = prev->data; entry->offset = prev_entry->offset + - prev_entry->entry->next_offset; + iptables_entry_get_next_offset( + prev_entry); } } @@ -428,9 +785,9 @@ static void update_targets_reference(struct connman_iptables *table, struct connman_iptables_entry *tmp; struct xt_standard_target *t; GList *list; - int offset; + unsigned int offset; - offset = modified_entry->entry->next_offset; + offset = iptables_entry_get_next_offset(modified_entry); for (list = table->entries; list; list = list->next) { tmp = list->data; @@ -438,7 +795,11 @@ static void update_targets_reference(struct connman_iptables *table, if (!is_jump(tmp)) continue; - t = (struct xt_standard_target *)ipt_get_target(tmp->entry); + t = (struct xt_standard_target *) + iptables_entry_get_target(tmp); + + if (!t) + continue; if (is_removing) { if (t->verdict >= entry_before->offset) @@ -451,40 +812,56 @@ static void update_targets_reference(struct connman_iptables *table, if (is_fallthrough(modified_entry)) { t = (struct xt_standard_target *) - ipt_get_target(modified_entry->entry); + iptables_entry_get_target(modified_entry); + + if (!t) + return; t->verdict = entry_before->offset + - modified_entry->entry->target_offset + - ALIGN(sizeof(struct xt_standard_target)); + iptables_entry_get_target_offset(modified_entry) + + XT_ALIGN(sizeof(struct xt_standard_target)); t->target.u.target_size = - ALIGN(sizeof(struct xt_standard_target)); + XT_ALIGN(sizeof(struct xt_standard_target)); } } static int iptables_add_entry(struct connman_iptables *table, - struct ipt_entry *entry, GList *before, - int builtin, int counter_idx) + struct connman_iptables_entry *entry, + GList *before, int builtin, int counter_idx) { struct connman_iptables_entry *e, *entry_before; - if (!table) - return -1; + if (!table) { + return -EINVAL; + } e = g_try_malloc0(sizeof(struct connman_iptables_entry)); if (!e) - return -1; + return -ENOMEM; + + switch (table->type) { + case AF_INET: + e->entry = entry->entry; + break; + case AF_INET6: + e->entry6 = entry->entry6; + break; + default: + g_free(e); + return -EINVAL; + } - e->entry = entry; + e->type = entry->type; e->builtin = builtin; e->counter_idx = counter_idx; table->entries = g_list_insert_before(table->entries, before, e); table->num_entries++; - table->size += entry->next_offset; + table->size += iptables_entry_get_next_offset(e); if (!before) { - e->offset = table->size - entry->next_offset; - + e->offset = table->size - + iptables_entry_get_next_offset(e); return 0; } @@ -505,15 +882,17 @@ static int remove_table_entry(struct connman_iptables *table, struct connman_iptables_entry *entry) { int removed = 0; + u_int16_t next_offset; + next_offset = iptables_entry_get_next_offset(entry); table->num_entries--; - table->size -= entry->entry->next_offset; - removed = entry->entry->next_offset; + + table->size -= next_offset; + removed = next_offset; table->entries = g_list_remove(table->entries, entry); - g_free(entry->entry); - g_free(entry); + iptables_entry_free(entry); return removed; } @@ -590,16 +969,30 @@ static int iptables_add_chain(struct connman_iptables *table, const char *name) { GList *last; - struct ipt_entry *entry_head; - struct ipt_entry *entry_return; - struct error_target *error; - struct ipt_standard_target *standard; + struct ipt_entry *entry_head = NULL; + struct ipt_entry *entry_return = NULL; + struct ip6t_entry *entry6_head = NULL; + struct ip6t_entry *entry6_return = NULL; + struct connman_iptables_entry entry = { 0 }; + struct xt_error_target *error = NULL; + struct ipt_standard_target *standard = NULL; u_int16_t entry_head_size, entry_return_size; + size_t entry_struct_size = 0; + size_t xt_error_target_size = 0; + size_t standard_target_size = 0; DBG("table %s chain %s", table->name, name); + entry.type = table->type; + + /* Do not allow to add duplicate chains */ + if (find_chain_head(table, name)) + return -EEXIST; + last = g_list_last(table->entries); + xt_error_target_size = XT_ALIGN(sizeof(struct xt_error_target)); + /* * An empty chain is composed of: * - A head entry, with no match and an error target. @@ -610,47 +1003,93 @@ static int iptables_add_chain(struct connman_iptables *table, */ /* head entry */ - entry_head_size = ALIGN(sizeof(struct ipt_entry)) + - ALIGN(sizeof(struct error_target)); - entry_head = g_try_malloc0(entry_head_size); - if (!entry_head) - goto err_head; + switch (entry.type) { + case AF_INET: + entry_struct_size = XT_ALIGN(sizeof(struct ipt_entry)); + entry_head_size = entry_struct_size + xt_error_target_size; + + entry_head = g_try_malloc0(entry_head_size); + if (!entry_head) + goto err_head; + + entry_head->target_offset = entry_struct_size; + entry_head->next_offset = entry_head_size; + + error = (struct xt_error_target *) entry_head->elems; + + entry.entry = entry_head; + break; + case AF_INET6: + entry_struct_size = XT_ALIGN(sizeof(struct ip6t_entry)); + entry_head_size = entry_struct_size + xt_error_target_size; - entry_head->target_offset = ALIGN(sizeof(struct ipt_entry)); - entry_head->next_offset = entry_head_size; + entry6_head = g_try_malloc0(entry_head_size); + if (!entry6_head) + goto err_head; - error = (struct error_target *) entry_head->elems; - g_stpcpy(error->t.u.user.name, IPT_ERROR_TARGET); - error->t.u.user.target_size = ALIGN(sizeof(struct error_target)); - g_stpcpy(error->error, name); + entry6_head->target_offset = entry_struct_size; + entry6_head->next_offset = entry_head_size; - if (iptables_add_entry(table, entry_head, last, -1, -1) < 0) + error = (struct xt_error_target *) entry6_head->elems; + + entry.entry6 = entry6_head; + break; + default: + return -EINVAL; + } + + g_stpcpy(error->target.u.user.name, get_error_target(entry.type)); + error->target.u.user.target_size = xt_error_target_size; + g_stpcpy(error->errorname, name); + + if (iptables_add_entry(table, &entry, last, -1, -1) < 0) goto err_head; + standard_target_size = XT_ALIGN(sizeof(struct ipt_standard_target)); + entry_return_size = entry_struct_size + standard_target_size; + /* tail entry */ - entry_return_size = ALIGN(sizeof(struct ipt_entry))+ - ALIGN(sizeof(struct ipt_standard_target)); - entry_return = g_try_malloc0(entry_return_size); - if (!entry_return) - goto err; + switch (entry.type) { + case AF_INET: + entry_return = g_try_malloc0(entry_return_size); + if (!entry_return) + goto err; + + entry_return->target_offset = entry_struct_size; + entry_return->next_offset = entry_return_size; + + standard = (struct ipt_standard_target *) entry_return->elems; + + entry.entry = entry_return; + break; + case AF_INET6: + entry6_return = g_try_malloc0(entry_return_size); + if (!entry6_return) + goto err; - entry_return->target_offset = ALIGN(sizeof(struct ipt_entry)); - entry_return->next_offset = entry_return_size; + entry6_return->target_offset = entry_struct_size; + entry6_return->next_offset = entry_return_size; - standard = (struct ipt_standard_target *) entry_return->elems; - standard->target.u.user.target_size = - ALIGN(sizeof(struct ipt_standard_target)); + standard = (struct ipt_standard_target *) entry6_return->elems; + + entry.entry6 = entry6_return; + break; + } + + standard->target.u.user.target_size = standard_target_size; standard->verdict = XT_RETURN; - if (iptables_add_entry(table, entry_return, last, -1, -1) < 0) + if (iptables_add_entry(table, &entry, last, -1, -1) < 0) goto err; return 0; err: g_free(entry_return); + g_free(entry6_return); err_head: g_free(entry_head); + g_free(entry6_head); return -ENOMEM; } @@ -691,14 +1130,21 @@ static int iptables_delete_chain(struct connman_iptables *table, return 0; } -static struct ipt_entry *new_rule(struct ipt_ip *ip, +static struct connman_iptables_entry *new_rule(struct iptables_ip *ip, const char *target_name, struct xtables_target *xt_t, struct xtables_rule_match *xt_rm) { struct xtables_rule_match *tmp_xt_rm; - struct ipt_entry *new_entry; + struct connman_iptables_entry *new_entry; size_t match_size, target_size; + new_entry = g_try_malloc0(sizeof(struct connman_iptables_entry)); + + if (!new_entry) + return NULL; + + new_entry->type = ip->type; + match_size = 0; for (tmp_xt_rm = xt_rm; tmp_xt_rm; tmp_xt_rm = tmp_xt_rm->next) match_size += tmp_xt_rm->match->m->u.match_size; @@ -706,46 +1152,89 @@ static struct ipt_entry *new_rule(struct ipt_ip *ip, if (xt_t) target_size = xt_t->t->u.target_size; else - target_size = ALIGN(sizeof(struct xt_standard_target)); - - new_entry = g_try_malloc0(ALIGN(sizeof(struct ipt_entry)) + - target_size + match_size); - if (!new_entry) - return NULL; - - memcpy(&new_entry->ip, ip, sizeof(struct ipt_ip)); - - new_entry->target_offset = ALIGN(sizeof(struct ipt_entry)) + - match_size; - new_entry->next_offset = ALIGN(sizeof(struct ipt_entry)) + + target_size = XT_ALIGN(sizeof(struct xt_standard_target)); + + switch (ip->type) { + case AF_INET: + new_entry->entry = g_try_malloc0( + XT_ALIGN(sizeof(struct ipt_entry)) + + target_size + match_size); + if (!new_entry->entry) + goto err; + + memcpy(&new_entry->entry->ip, ip->ip, sizeof(struct ipt_ip)); + + new_entry->entry->target_offset = + XT_ALIGN(sizeof(struct ipt_entry)) + + match_size; + new_entry->entry->next_offset = + XT_ALIGN(sizeof(struct ipt_entry)) + + target_size + match_size; + break; + case AF_INET6: + new_entry->entry6 = g_try_malloc0( + XT_ALIGN(sizeof(struct ip6t_entry)) + + target_size + match_size); + if (!new_entry->entry6) + goto err; + + memcpy(&new_entry->entry6->ipv6, ip->ip6, + sizeof(struct ip6t_ip6)); + + new_entry->entry6->target_offset = + XT_ALIGN(sizeof(struct ip6t_entry)) + + match_size; + new_entry->entry6->next_offset = + XT_ALIGN(sizeof(struct ip6t_entry)) + target_size + match_size; + break; + default: + goto err; + } match_size = 0; for (tmp_xt_rm = xt_rm; tmp_xt_rm; tmp_xt_rm = tmp_xt_rm->next) { - memcpy(new_entry->elems + match_size, tmp_xt_rm->match->m, + + switch (new_entry->type) { + case AF_INET: + memcpy(new_entry->entry->elems + match_size, + tmp_xt_rm->match->m, + tmp_xt_rm->match->m->u.match_size); + break; + case AF_INET6: + memcpy(new_entry->entry6->elems + match_size, + tmp_xt_rm->match->m, tmp_xt_rm->match->m->u.match_size); + break; + } match_size += tmp_xt_rm->match->m->u.match_size; } if (xt_t) { struct xt_entry_target *entry_target; - entry_target = ipt_get_target(new_entry); + entry_target = iptables_entry_get_target(new_entry); memcpy(entry_target, xt_t->t, target_size); } return new_entry; + +err: + g_free(new_entry); + + return NULL; } static void update_hooks(struct connman_iptables *table, GList *chain_head, - struct ipt_entry *entry) + struct connman_iptables_entry *entry) { GList *list; struct connman_iptables_entry *head, *e; int builtin; + u_int16_t next_offset; - if (!chain_head) + if (!table || !chain_head) return; head = chain_head->data; @@ -754,7 +1243,9 @@ static void update_hooks(struct connman_iptables *table, GList *chain_head, if (builtin < 0) return; - table->underflow[builtin] += entry->next_offset; + next_offset = iptables_entry_get_next_offset(entry); + + table->underflow[builtin] += next_offset; for (list = chain_head->next; list; list = list->next) { e = list->data; @@ -763,21 +1254,24 @@ static void update_hooks(struct connman_iptables *table, GList *chain_head, if (builtin < 0) continue; - table->hook_entry[builtin] += entry->next_offset; - table->underflow[builtin] += entry->next_offset; + table->hook_entry[builtin] += next_offset; + table->underflow[builtin] += next_offset; } } -static struct ipt_entry *prepare_rule_inclusion(struct connman_iptables *table, - struct ipt_ip *ip, const char *chain_name, +static struct connman_iptables_entry *prepare_rule_inclusion( + struct connman_iptables *table, + struct iptables_ip *ip, + const char *chain_name, const char *target_name, struct xtables_target *xt_t, - int *builtin, struct xtables_rule_match *xt_rm, + int *builtin, + struct xtables_rule_match *xt_rm, bool insert) { GList *chain_tail, *chain_head; - struct ipt_entry *new_entry; struct connman_iptables_entry *head; + struct connman_iptables_entry *new_entry; chain_head = find_chain_head(table, chain_name); if (!chain_head) @@ -788,8 +1282,17 @@ static struct ipt_entry *prepare_rule_inclusion(struct connman_iptables *table, return NULL; new_entry = new_rule(ip, target_name, xt_t, xt_rm); - if (!new_entry) - return NULL; + + switch (new_entry->type) { + case AF_INET: + if (new_entry->entry) + break; + case AF_INET6: + if (new_entry->entry6) + break; + default: + goto err; + } update_hooks(table, chain_head, new_entry); @@ -807,15 +1310,21 @@ static struct ipt_entry *prepare_rule_inclusion(struct connman_iptables *table, } return new_entry; + +err: + g_free(new_entry); + + return NULL; } static int iptables_append_rule(struct connman_iptables *table, - struct ipt_ip *ip, const char *chain_name, + struct iptables_ip *ip, + const char *chain_name, const char *target_name, struct xtables_target *xt_t, struct xtables_rule_match *xt_rm) { - struct ipt_entry *new_entry; + struct connman_iptables_entry *new_entry; int builtin = -1, ret; GList *chain_tail; @@ -825,25 +1334,51 @@ static int iptables_append_rule(struct connman_iptables *table, if (!chain_tail) return -EINVAL; - new_entry = prepare_rule_inclusion(table, ip, chain_name, - target_name, xt_t, &builtin, xt_rm, false); + new_entry = prepare_rule_inclusion(table, ip, chain_name, target_name, + xt_t, &builtin, xt_rm, false); + if (!new_entry) return -EINVAL; - ret = iptables_add_entry(table, new_entry, chain_tail->prev, builtin, -1); + switch (new_entry->type) { + case AF_INET: + if (new_entry->entry) + break; + case AF_INET6: + if (new_entry->entry6) + break; + default: + ret = -EINVAL; + goto err; + } + + ret = iptables_add_entry(table, new_entry, chain_tail->prev, + builtin, -1); if (ret < 0) - g_free(new_entry); + goto err; + + /* + * Free only the container, not the content iptables_add_entry() + * allocates new containers for entries. + */ + g_free(new_entry); + + return ret; + +err: + iptables_entry_free(new_entry); return ret; } static int iptables_insert_rule(struct connman_iptables *table, - struct ipt_ip *ip, const char *chain_name, + struct iptables_ip *ip, + const char *chain_name, const char *target_name, struct xtables_target *xt_t, struct xtables_rule_match *xt_rm) { - struct ipt_entry *new_entry; + struct connman_iptables_entry *new_entry; int builtin = -1, ret; GList *chain_head; @@ -853,17 +1388,41 @@ static int iptables_insert_rule(struct connman_iptables *table, if (!chain_head) return -EINVAL; - new_entry = prepare_rule_inclusion(table, ip, chain_name, - target_name, xt_t, &builtin, xt_rm, true); + new_entry = prepare_rule_inclusion(table, ip, chain_name, target_name, + xt_t, &builtin, xt_rm, true); + if (!new_entry) return -EINVAL; + switch (new_entry->type) { + case AF_INET: + if (new_entry->entry) + break; + case AF_INET6: + if (new_entry->entry6) + break; + default: + ret = -EINVAL; + goto err; + } + if (builtin == -1) chain_head = chain_head->next; ret = iptables_add_entry(table, new_entry, chain_head, builtin, -1); if (ret < 0) - g_free(new_entry); + goto err; + + /* + * Free only the container, not the content iptables_add_entry() + * allocates new containers for entries. + */ + g_free(new_entry); + + return ret; + +err: + iptables_entry_free(new_entry); return ret; } @@ -883,6 +1442,38 @@ static bool is_same_ipt_entry(struct ipt_entry *i_e1, return true; } +/* A copy of is_same_ipt_entry with IPv6 structures */ +static bool is_same_ip6t_entry(struct ip6t_entry *i_e1, + struct ip6t_entry *i_e2) +{ + if (memcmp(&i_e1->ipv6, &i_e2->ipv6, sizeof(struct ip6t_ip6)) != 0) + return false; + + if (i_e1->target_offset != i_e2->target_offset) + return false; + + if (i_e1->next_offset != i_e2->next_offset) + return false; + + return true; +} + +static bool is_same_iptables_entry(struct connman_iptables_entry *e1, + struct connman_iptables_entry *e2) +{ + if (e1->type != e2->type) + return false; + + switch (e1->type) { + case AF_INET: + return is_same_ipt_entry(e1->entry, e2->entry); + case AF_INET6: + return is_same_ip6t_entry(e1->entry6, e2->entry6); + } + + return false; +} + static bool is_same_target(struct xt_entry_target *xt_e_t1, struct xt_entry_target *xt_e_t2) { @@ -895,7 +1486,12 @@ static bool is_same_target(struct xt_entry_target *xt_e_t1, g_strcmp0(xt_e_t2->u.user.name, "") == 0) { /* fallthrough */ return true; - } else if (g_strcmp0(xt_e_t1->u.user.name, IPT_STANDARD_TARGET) == 0) { + + /* + * IPT_STANDARD_TARGET and IP6T_STANDARD_TARGET are defined by + * XT_STANDARD_TARGET + */ + } else if (g_strcmp0(xt_e_t1->u.user.name, XT_STANDARD_TARGET) == 0) { struct xt_standard_target *xt_s_t1; struct xt_standard_target *xt_s_t2; @@ -948,7 +1544,8 @@ static bool is_same_match(struct xt_entry_match *xt_e_m1, } static GList *find_existing_rule(struct connman_iptables *table, - struct ipt_ip *ip, const char *chain_name, + struct iptables_ip *ip, + const char *chain_name, const char *target_name, struct xtables_target *xt_t, GList *matches, @@ -958,7 +1555,7 @@ static GList *find_existing_rule(struct connman_iptables *table, struct xt_entry_target *xt_e_t = NULL; struct xt_entry_match *xt_e_m = NULL; struct connman_iptables_entry *entry; - struct ipt_entry *entry_test; + struct connman_iptables_entry *entry_test; int builtin; chain_head = find_chain_head(table, chain_name); @@ -973,13 +1570,25 @@ static GList *find_existing_rule(struct connman_iptables *table, return NULL; entry_test = new_rule(ip, target_name, xt_t, xt_rm); - if (!entry_test) + + switch (entry_test->type) { + case AF_INET: + if (!entry_test->entry) + return NULL; + break; + case AF_INET6: + if (!entry_test->entry6) + return NULL; + break; + default: return NULL; + } if (xt_t) - xt_e_t = ipt_get_target(entry_test); + xt_e_t = iptables_entry_get_target(entry_test); if (matches) - xt_e_m = (struct xt_entry_match *)entry_test->elems; + xt_e_m = (struct xt_entry_match *) + iptables_entry_get_elems(entry_test); entry = chain_head->data; builtin = entry->builtin; @@ -991,18 +1600,16 @@ static GList *find_existing_rule(struct connman_iptables *table, for (; list != chain_tail->prev; list = list->next) { struct connman_iptables_entry *tmp; - struct ipt_entry *tmp_e; tmp = list->data; - tmp_e = tmp->entry; - if (!is_same_ipt_entry(entry_test, tmp_e)) + if (!is_same_iptables_entry(entry_test, tmp)) continue; if (xt_t) { - struct xt_entry_target *tmp_xt_e_t; + struct xt_entry_target *tmp_xt_e_t = NULL; - tmp_xt_e_t = ipt_get_target(tmp_e); + tmp_xt_e_t = iptables_entry_get_target(tmp); if (!is_same_target(tmp_xt_e_t, xt_e_t)) continue; @@ -1011,7 +1618,8 @@ static GList *find_existing_rule(struct connman_iptables *table, if (matches) { struct xt_entry_match *tmp_xt_e_m; - tmp_xt_e_m = (struct xt_entry_match *)tmp_e->elems; + tmp_xt_e_m = (struct xt_entry_match *) + iptables_entry_get_elems(tmp); if (!is_same_match(tmp_xt_e_m, xt_e_m)) continue; @@ -1020,7 +1628,7 @@ static GList *find_existing_rule(struct connman_iptables *table, break; } - g_free(entry_test); + iptables_entry_free(entry_test); if (list != chain_tail->prev) return list; @@ -1029,7 +1637,8 @@ static GList *find_existing_rule(struct connman_iptables *table, } static int iptables_delete_rule(struct connman_iptables *table, - struct ipt_ip *ip, const char *chain_name, + struct iptables_ip *ip, + const char *chain_name, const char *target_name, struct xtables_target *xt_t, GList *matches, @@ -1039,7 +1648,6 @@ static int iptables_delete_rule(struct connman_iptables *table, GList *chain_head, *chain_tail, *list; int builtin, removed; - DBG("table %s chain %s", table->name, chain_name); removed = 0; @@ -1053,7 +1661,8 @@ static int iptables_delete_rule(struct connman_iptables *table, return -EINVAL; list = find_existing_rule(table, ip, chain_name, target_name, - xt_t, matches, xt_rm); + xt_t, matches, xt_rm); + if (!list) return -EINVAL; @@ -1129,7 +1738,11 @@ static int iptables_change_policy(struct connman_iptables *table, return -EINVAL; entry = chain_tail->prev->data; - target = ipt_get_target(entry->entry); + + target = iptables_entry_get_target(entry); + + if (!target) + return -EINVAL; t = (struct xt_standard_target *)target; if (t->verdict != verdict) @@ -1180,38 +1793,114 @@ static struct ipt_replace *iptables_blob(struct connman_iptables *table) return r; } -static void dump_ip(struct ipt_entry *entry) +/* A copy of iptables_blob() with IPv6 structures */ +static struct ip6t_replace *ip6tables_blob(struct connman_iptables *table) { - struct ipt_ip *ip = &entry->ip; + struct ip6t_replace *r; + GList *list; + struct connman_iptables_entry *e; + unsigned char *entry_index; + + r = g_try_malloc0(sizeof(struct ip6t_replace) + table->size); + if (!r) + return NULL; + + memset(r, 0, sizeof(*r) + table->size); + + r->counters = g_try_malloc0(sizeof(struct xt_counters) + * table->old_entries); + if (!r->counters) { + g_free(r); + return NULL; + } + + g_stpcpy(r->name, table->info6->name); + r->num_entries = table->num_entries; + r->size = table->size; + + r->num_counters = table->old_entries; + r->valid_hooks = table->info6->valid_hooks; + + memcpy(r->hook_entry, table->hook_entry, sizeof(table->hook_entry)); + memcpy(r->underflow, table->underflow, sizeof(table->underflow)); + + entry_index = (unsigned char *)r->entries; + for (list = table->entries; list; list = list->next) { + e = list->data; + + memcpy(entry_index, e->entry6, e->entry6->next_offset); + entry_index += e->entry6->next_offset; + } + + return r; +} + +static void dump_ip(struct connman_iptables_entry *entry) +{ + char *iniface, *outiface; char ip_string[INET6_ADDRSTRLEN]; char ip_mask[INET6_ADDRSTRLEN]; - if (strlen(ip->iniface)) - DBG("\tin %s", ip->iniface); + switch (entry->type) { + case AF_INET: + iniface = entry->entry->ip.iniface; + outiface = entry->entry->ip.outiface; + break; + case AF_INET6: + iniface = entry->entry6->ipv6.iniface; + outiface = entry->entry6->ipv6.outiface; + break; + default: + return; + } + + if (strlen(iniface)) + DBG("\tin %s", iniface); - if (strlen(ip->outiface)) - DBG("\tout %s", ip->outiface); + if (strlen(outiface)) + DBG("\tout %s", outiface); - if (inet_ntop(AF_INET, &ip->src, ip_string, INET6_ADDRSTRLEN) && - inet_ntop(AF_INET, &ip->smsk, ip_mask, + if (entry->type == AF_INET) { + if (inet_ntop(entry->type, &entry->entry->ip.src, ip_string, + INET6_ADDRSTRLEN) && inet_ntop(entry->type, + &entry->entry->ip.smsk, ip_mask, INET6_ADDRSTRLEN)) - DBG("\tsrc %s/%s", ip_string, ip_mask); + DBG("\tsrc %s/%s", ip_string, ip_mask); - if (inet_ntop(AF_INET, &ip->dst, ip_string, INET6_ADDRSTRLEN) && - inet_ntop(AF_INET, &ip->dmsk, ip_mask, + if (inet_ntop(entry->type, &entry->entry->ip.dst, ip_string, + INET6_ADDRSTRLEN) && inet_ntop(entry->type, + &entry->entry->ip.dmsk, ip_mask, INET6_ADDRSTRLEN)) - DBG("\tdst %s/%s", ip_string, ip_mask); -} + DBG("\tdst %s/%s", ip_string, ip_mask); + } -static void dump_target(struct ipt_entry *entry) + if (entry->type == AF_INET6) { + if (inet_ntop(entry->type, &entry->entry6->ipv6.src, ip_string, + INET6_ADDRSTRLEN) && inet_ntop(entry->type, + &entry->entry6->ipv6.smsk, ip_mask, + INET6_ADDRSTRLEN)) + DBG("\tsrc %s/%s", ip_string, ip_mask); + if (inet_ntop(entry->type, &entry->entry6->ipv6.dst, ip_string, + INET6_ADDRSTRLEN) && inet_ntop(entry->type, + &entry->entry6->ipv6.dmsk, ip_mask, + INET6_ADDRSTRLEN)) + DBG("\tdst %s/%s", ip_string, ip_mask); + } +} + +static void dump_target(struct connman_iptables_entry *entry) { struct xtables_target *xt_t; struct xt_entry_target *target; + int err; - target = ipt_get_target(entry); + target = iptables_entry_get_target(entry); - if (!g_strcmp0(target->u.user.name, IPT_STANDARD_TARGET)) { + if (!target) + return; + + if (!g_strcmp0(target->u.user.name, get_standard_target(entry->type))) { struct xt_standard_target *t; t = (struct xt_standard_target *)target; @@ -1242,13 +1931,34 @@ static void dump_target(struct ipt_entry *entry) break; } - xt_t = xtables_find_target(IPT_STANDARD_TARGET, + enable_jmp(); + + if ((err = setjmp(env_state)) != 0) { + DBG("setjmp() called by longjmp() with value %d", err); + disable_jmp(); + return; + } + + xt_t = xtables_find_target(get_standard_target(entry->type), XTF_LOAD_MUST_SUCCEED); + disable_jmp(); + if (xt_t->print) xt_t->print(NULL, target, 1); } else { + enable_jmp(); + + if ((err = setjmp(env_state)) != 0) { + DBG("setjmp() called by longjmp() with value %d", err); + disable_jmp(); + return; + } + xt_t = xtables_find_target(target->u.user.name, XTF_TRY_LOAD); + + disable_jmp(); + if (!xt_t) { DBG("\ttarget %s", target->u.user.name); return; @@ -1264,20 +1974,47 @@ static void dump_target(struct ipt_entry *entry) free(xt_t); } -static void dump_match(struct ipt_entry *entry) +static void dump_match(struct connman_iptables_entry *entry) { struct xtables_match *xt_m; struct xt_entry_match *match; + u_int16_t target_offset; + int err; + + target_offset = iptables_entry_get_target_offset(entry); - if (entry->elems == (unsigned char *)entry + entry->target_offset) + switch (entry->type) { + case AF_INET: + if (entry->entry->elems == (unsigned char *)entry->entry + + target_offset) + return; + break; + case AF_INET6: + if (entry->entry6->elems == (unsigned char *)entry->entry6 + + target_offset) + return; + break; + default: return; + } - match = (struct xt_entry_match *) entry->elems; + match = (struct xt_entry_match *) iptables_entry_get_elems(entry); if (!strlen(match->u.user.name)) return; + enable_jmp(); + + if ((err = setjmp(env_state)) != 0) { + DBG("setjmp() called by longjmp() with value %d", err); + disable_jmp(); + return; + } + xt_m = xtables_find_match(match->u.user.name, XTF_TRY_LOAD, NULL); + + disable_jmp(); + if (!xt_m) goto out; @@ -1295,33 +2032,48 @@ out: } -static int dump_entry(struct ipt_entry *entry, int builtin, +static int dump_entry(struct connman_iptables_entry *entry, int builtin, unsigned int hook, size_t size, unsigned int offset, void *user_data) { struct xt_entry_target *target; + char *char_entry; + + target = iptables_entry_get_target(entry); - target = ipt_get_target(entry); + if (!target) + return -EINVAL; - if (offset + entry->next_offset == size) { + if (offset + iptables_entry_get_next_offset(entry) == size) { DBG("\tEnd of CHAIN"); return 0; } + switch (entry->type) { + case AF_INET: + char_entry = (char *)entry->entry; + break; + case AF_INET6: + char_entry = (char *)entry->entry6; + break; + default: + return 0; + } + if (!g_strcmp0(target->u.user.name, IPT_ERROR_TARGET)) { DBG("\tUSER CHAIN (%s) match %p target %p", - target->data, entry->elems, - (char *)entry + entry->target_offset); + target->data, iptables_entry_get_elems(entry), + char_entry + iptables_entry_get_target_offset(entry)); return 0; } else if (builtin >= 0) { DBG("\tCHAIN (%s) match %p target %p", - hooknames[builtin], entry->elems, - (char *)entry + entry->target_offset); + hooknames[builtin], iptables_entry_get_elems(entry), + char_entry + iptables_entry_get_target_offset(entry)); } else { DBG("\tRULE match %p target %p", - entry->elems, - (char *)entry + entry->target_offset); + iptables_entry_get_elems(entry), + char_entry + iptables_entry_get_target_offset(entry)); } dump_match(entry); @@ -1333,54 +2085,192 @@ static int dump_entry(struct ipt_entry *entry, int builtin, static void dump_table(struct connman_iptables *table) { + struct connman_iptables_entry entry = { 0 }; + unsigned int *hook_entry; + unsigned int *underflow; + unsigned int valid_hooks; + unsigned int size; + + hook_entry = iptables_table_get_info_hook_entry(table); + underflow = iptables_table_get_info_underflow(table); + valid_hooks = iptables_table_get_info_valid_hooks(table); + size = iptables_table_get_info_size(table); + DBG("%s valid_hooks=0x%08x, num_entries=%u, size=%u", - table->info->name, - table->info->valid_hooks, table->info->num_entries, - table->info->size); + iptables_table_get_info_name(table), + valid_hooks, + iptables_table_get_info_num_entries(table), + size); DBG("entry hook: pre/in/fwd/out/post %d/%d/%d/%d/%d", - table->info->hook_entry[NF_IP_PRE_ROUTING], - table->info->hook_entry[NF_IP_LOCAL_IN], - table->info->hook_entry[NF_IP_FORWARD], - table->info->hook_entry[NF_IP_LOCAL_OUT], - table->info->hook_entry[NF_IP_POST_ROUTING]); + hook_entry[NF_IP_PRE_ROUTING], + hook_entry[NF_IP_LOCAL_IN], + hook_entry[NF_IP_FORWARD], + hook_entry[NF_IP_LOCAL_OUT], + hook_entry[NF_IP_POST_ROUTING]); DBG("underflow: pre/in/fwd/out/post %d/%d/%d/%d/%d", - table->info->underflow[NF_IP_PRE_ROUTING], - table->info->underflow[NF_IP_LOCAL_IN], - table->info->underflow[NF_IP_FORWARD], - table->info->underflow[NF_IP_LOCAL_OUT], - table->info->underflow[NF_IP_POST_ROUTING]); + underflow[NF_IP_PRE_ROUTING], + underflow[NF_IP_LOCAL_IN], + underflow[NF_IP_FORWARD], + underflow[NF_IP_LOCAL_OUT], + underflow[NF_IP_POST_ROUTING]); + + entry.type = table->type; + + switch (table->type) { + case AF_INET: + entry.entry = table->blob_entries->entrytable; + break; + case AF_INET6: + entry.entry6 = table->blob_entries6->entrytable; + } + + iterate_entries(&entry, + valid_hooks, + hook_entry, + underflow, + size, + print_entry, dump_entry); +} + +static const char *iptables_replace_get_name(struct iptables_replace *replace) +{ + if (!replace) + return NULL; + + switch (replace->type) { + case AF_INET: + return replace->r->name; + case AF_INET6: + return replace->r6->name; + } + + return NULL; +} + +static unsigned int iptables_replace_get_valid_hooks( + struct iptables_replace *replace) +{ + if (!replace) + return 0; + + switch (replace->type) { + case AF_INET: + return replace->r->valid_hooks; + case AF_INET6: + return replace->r6->valid_hooks; + } + + return 0; +} + +static unsigned int iptables_replace_get_num_entries( + struct iptables_replace *replace) +{ + if (!replace) + return 0; + + switch (replace->type) { + case AF_INET: + return replace->r->num_entries; + case AF_INET6: + return replace->r6->num_entries; + } + + return 0; +} + +static unsigned int *iptables_replace_get_hook_entry( + struct iptables_replace *replace) +{ + if (!replace) + return NULL; + + switch (replace->type) { + case AF_INET: + return replace->r->hook_entry; + case AF_INET6: + return replace->r6->hook_entry; + } + + return NULL; +} + +static unsigned int *iptables_replace_get_underflow( + struct iptables_replace *replace) +{ + if (!replace) + return NULL; - iterate_entries(table->blob_entries->entrytable, - table->info->valid_hooks, - table->info->hook_entry, - table->info->underflow, - table->blob_entries->size, - print_entry, dump_entry); + switch (replace->type) { + case AF_INET: + return replace->r->underflow; + case AF_INET6: + return replace->r6->underflow; + } + + return NULL; } -static void dump_ipt_replace(struct ipt_replace *repl) +static unsigned int iptables_replace_get_size(struct iptables_replace *replace) { + if (!replace) + return 0; + + switch (replace->type) { + case AF_INET: + return replace->r->size; + case AF_INET6: + return replace->r6->size; + } + + return 0; +} + +static void dump_replace(struct iptables_replace *repl) +{ + struct connman_iptables_entry entry = { 0 }; + unsigned int *hook_entry; + unsigned int *underflow; + unsigned int valid_hooks; + unsigned int size; + + hook_entry = iptables_replace_get_hook_entry(repl); + underflow = iptables_replace_get_underflow(repl); + valid_hooks = iptables_replace_get_valid_hooks(repl); + size = iptables_replace_get_size(repl); + + switch (repl->type) { + case AF_INET: + entry.entry = repl->r->entries; + break; + case AF_INET6: + entry.entry6 = repl->r6->entries; + break; + default: + return; + } + DBG("%s valid_hooks 0x%08x num_entries %u size %u", - repl->name, repl->valid_hooks, repl->num_entries, - repl->size); + iptables_replace_get_name(repl), + valid_hooks, + iptables_replace_get_num_entries(repl), size); DBG("entry hook: pre/in/fwd/out/post %d/%d/%d/%d/%d", - repl->hook_entry[NF_IP_PRE_ROUTING], - repl->hook_entry[NF_IP_LOCAL_IN], - repl->hook_entry[NF_IP_FORWARD], - repl->hook_entry[NF_IP_LOCAL_OUT], - repl->hook_entry[NF_IP_POST_ROUTING]); + hook_entry[NF_IP_PRE_ROUTING], + hook_entry[NF_IP_LOCAL_IN], + hook_entry[NF_IP_FORWARD], + hook_entry[NF_IP_LOCAL_OUT], + hook_entry[NF_IP_POST_ROUTING]); DBG("underflow: pre/in/fwd/out/post %d/%d/%d/%d/%d", - repl->underflow[NF_IP_PRE_ROUTING], - repl->underflow[NF_IP_LOCAL_IN], - repl->underflow[NF_IP_FORWARD], - repl->underflow[NF_IP_LOCAL_OUT], - repl->underflow[NF_IP_POST_ROUTING]); + underflow[NF_IP_PRE_ROUTING], + underflow[NF_IP_LOCAL_IN], + underflow[NF_IP_FORWARD], + underflow[NF_IP_LOCAL_OUT], + underflow[NF_IP_POST_ROUTING]); - iterate_entries(repl->entries, repl->valid_hooks, - repl->hook_entry, repl->underflow, - repl->size, print_entry, dump_entry); + iterate_entries(&entry, valid_hooks, hook_entry, underflow, + size, print_entry, dump_entry); } static int iptables_get_entries(struct connman_iptables *table) @@ -1388,10 +2278,26 @@ static int iptables_get_entries(struct connman_iptables *table) socklen_t entry_size; int err; - entry_size = sizeof(struct ipt_get_entries) + table->info->size; + switch (table->type) { + case AF_INET: + entry_size = sizeof(struct ipt_get_entries) + table->info->size; + + err = getsockopt(table->ipt_sock, IPPROTO_IP, + IPT_SO_GET_ENTRIES, table->blob_entries, + &entry_size); + break; + case AF_INET6: + entry_size = sizeof(struct ip6t_get_entries) + + table->info6->size; + + err = getsockopt(table->ipt_sock, IPPROTO_IPV6, + IP6T_SO_GET_ENTRIES, table->blob_entries6, + &entry_size); + break; + default: + return -EINVAL; + } - err = getsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_GET_ENTRIES, - table->blob_entries, &entry_size); if (err < 0) return -errno; @@ -1399,12 +2305,31 @@ static int iptables_get_entries(struct connman_iptables *table) } static int iptables_replace(struct connman_iptables *table, - struct ipt_replace *r) + struct iptables_replace *r) { int err; - err = setsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_SET_REPLACE, r, - sizeof(*r) + r->size); + switch (r->type) { + case AF_INET: + if (!r->r) + return -EINVAL; + + err = setsockopt(table->ipt_sock, IPPROTO_IP, + IPT_SO_SET_REPLACE, r->r, + sizeof(*r->r) + r->r->size); + break; + case AF_INET6: + if (!r->r6) + return -EINVAL; + + err = setsockopt(table->ipt_sock, IPPROTO_IPV6, + IP6T_SO_SET_REPLACE, r->r6, + sizeof(*r->r6) + r->r6->size); + break; + default: + return -EINVAL; + } + if (err < 0) return -errno; @@ -1415,29 +2340,63 @@ static int iptables_add_counters(struct connman_iptables *table, struct xt_counters_info *c) { int err; + int level; + int optname; + + switch (table->type) { + case AF_INET: + level = IPPROTO_IP; + optname = IPT_SO_SET_ADD_COUNTERS; + break; + case AF_INET6: + level = IPPROTO_IPV6; + optname = IP6T_SO_SET_ADD_COUNTERS; + break; + default: + return -EINVAL; + } + + err = setsockopt(table->ipt_sock, level, optname, c, + sizeof(*c) + sizeof(struct xt_counters) * c->num_counters); - err = setsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_SET_ADD_COUNTERS, c, - sizeof(*c) + sizeof(struct xt_counters) * c->num_counters); if (err < 0) return -errno; return 0; } -static int add_entry(struct ipt_entry *entry, int builtin, unsigned int hook, - size_t size, unsigned offset, void *user_data) +static int add_entry(struct connman_iptables_entry *entry, int builtin, + unsigned int hook, size_t size, unsigned offset, + void *user_data) { struct connman_iptables *table = user_data; - struct ipt_entry *new_entry; - - new_entry = g_try_malloc0(entry->next_offset); - if (!new_entry) - return -ENOMEM; - - memcpy(new_entry, entry, entry->next_offset); + struct connman_iptables_entry new_entry = { 0 }; + u_int16_t next_offset; + + new_entry.type = entry->type; + next_offset = iptables_entry_get_next_offset(entry); + + switch (entry->type) { + case AF_INET: + new_entry.entry = g_try_malloc0(next_offset); + if (!new_entry.entry) + return -ENOMEM; + + memcpy(new_entry.entry, entry->entry, next_offset); + break; + case AF_INET6: + new_entry.entry6 = g_try_malloc0(next_offset); + if (!new_entry.entry6) + return -ENOMEM; - return iptables_add_entry(table, new_entry, NULL, builtin, - table->num_entries); + memcpy(new_entry.entry6, entry->entry6, next_offset); + break; + default: + return -EINVAL; + } + + return iptables_add_entry(table, &new_entry, NULL, builtin, + table->num_entries); } static void table_cleanup(struct connman_iptables *table) @@ -1454,32 +2413,60 @@ static void table_cleanup(struct connman_iptables *table) for (list = table->entries; list; list = list->next) { entry = list->data; - g_free(entry->entry); - g_free(entry); + iptables_entry_free(entry); } g_list_free(table->entries); g_free(table->name); - g_free(table->info); - g_free(table->blob_entries); + + if (table->type == AF_INET) { + g_free(table->info); + g_free(table->blob_entries); + } + + if (table->type == AF_INET6) { + g_free(table->info6); + g_free(table->blob_entries6); + } + g_free(table); } -static struct connman_iptables *iptables_init(const char *table_name) +static int setup_xtables(int type); +static void reset_xtables(); + +static struct connman_iptables *iptables_init(int type, const char *table_name) { struct connman_iptables *table = NULL; + struct connman_iptables_entry entry = { 0 }; + char *iptables_mod = NULL; char *module = NULL; socklen_t s; - DBG("%s", table_name); + switch(type) { + case AF_INET: + iptables_mod = g_strdup("ip_tables"); + module = g_strconcat("iptable_", table_name, NULL); + break; + case AF_INET6: + iptables_mod = g_strdup("ip6_tables"); + module = g_strconcat("ip6table_", table_name, NULL); + break; + default: + return NULL; + } - if (xtables_insmod("ip_tables", NULL, TRUE) != 0) - DBG("ip_tables module loading gives error but trying anyway"); + DBG("%d %s", type, table_name); - module = g_strconcat("iptable_", table_name, NULL); - if (!module) + if (setup_xtables(type)) return NULL; + if (xtables_insmod(iptables_mod, NULL, TRUE) != 0) + DBG("%s module loading gives error but trying anyway", + iptables_mod); + + g_free(iptables_mod); + if (xtables_insmod(module, NULL, TRUE) != 0) DBG("%s module loading gives error but trying anyway", module); @@ -1489,55 +2476,112 @@ static struct connman_iptables *iptables_init(const char *table_name) if (!table) return NULL; - table->info = g_try_new0(struct ipt_getinfo, 1); - if (!table->info) - goto err; + table->type = entry.type = type; - table->ipt_sock = socket(AF_INET, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW); + table->ipt_sock = socket(type, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW); if (table->ipt_sock < 0) goto err; - s = sizeof(*table->info); - g_stpcpy(table->info->name, table_name); - if (getsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_GET_INFO, - table->info, &s) < 0) { - connman_error("iptables support missing error %d (%s)", errno, - strerror(errno)); - goto err; - } + switch (type) { + case AF_INET: + table->info = g_try_new0(struct ipt_getinfo, 1); + if (!table->info) + goto err; - table->blob_entries = g_try_malloc0(sizeof(struct ipt_get_entries) + - table->info->size); - if (!table->blob_entries) - goto err; + s = sizeof(*table->info); + g_stpcpy(table->info->name, table_name); + + if (getsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_GET_INFO, + table->info, &s) < 0) { + connman_error("iptables support missing error %d (%s)", + errno, strerror(errno)); + goto err; + } + + table->blob_entries = g_try_malloc0( + sizeof(struct ipt_get_entries) + + table->info->size); + if (!table->blob_entries) + goto err; - g_stpcpy(table->blob_entries->name, table_name); - table->blob_entries->size = table->info->size; + g_stpcpy(table->blob_entries->name, table_name); + table->blob_entries->size = table->info->size; + + break; + case AF_INET6: + table->info6 = g_try_new0(struct ip6t_getinfo, 1); + if (!table->info6) + goto err; + + s = sizeof(*table->info6); + g_stpcpy(table->info6->name, table_name); + + if (getsockopt(table->ipt_sock, IPPROTO_IPV6, IP6T_SO_GET_INFO, + table->info6, &s) < 0) { + connman_error("ip6tables support missing error %d (%s)", + errno, strerror(errno)); + goto err; + } + + table->blob_entries6 = g_try_malloc0( + sizeof(struct ip6t_get_entries) + + table->info6->size); + if (!table->blob_entries6) + goto err; + + g_stpcpy(table->blob_entries6->name, table_name); + table->blob_entries6->size = table->info6->size; + + break; + } if (iptables_get_entries(table) < 0) goto err; table->num_entries = 0; - table->old_entries = table->info->num_entries; table->size = 0; - memcpy(table->underflow, table->info->underflow, - sizeof(table->info->underflow)); - memcpy(table->hook_entry, table->info->hook_entry, - sizeof(table->info->hook_entry)); + switch (type) { + case AF_INET: + table->old_entries = table->info->num_entries; + + memcpy(table->underflow, table->info->underflow, + sizeof(table->info->underflow)); + memcpy(table->hook_entry, table->info->hook_entry, + sizeof(table->info->hook_entry)); - iterate_entries(table->blob_entries->entrytable, - table->info->valid_hooks, table->info->hook_entry, - table->info->underflow, table->blob_entries->size, - add_entry, table); + entry.entry = table->blob_entries->entrytable; + break; + case AF_INET6: + table->old_entries = table->info6->num_entries; + + memcpy(table->underflow, table->info6->underflow, + sizeof(table->info6->underflow)); + memcpy(table->hook_entry, table->info6->hook_entry, + sizeof(table->info6->hook_entry)); + + entry.entry6 = table->blob_entries6->entrytable; + break; + } + + iterate_entries(&entry, + iptables_table_get_info_valid_hooks(table), + iptables_table_get_info_hook_entry(table), + iptables_table_get_info_underflow(table), + iptables_table_get_entries_size(table), + add_entry, + table); if (debug_enabled) dump_table(table); + reset_xtables(); + return table; err: table_cleanup(table); + reset_xtables(); return NULL; } @@ -1559,13 +2603,77 @@ static struct option iptables_opts[] = { {.name = "out-interface", .has_arg = 1, .val = 'o'}, {.name = "source", .has_arg = 1, .val = 's'}, {.name = "table", .has_arg = 1, .val = 't'}, + {.name = "protocol", .has_arg = 1, .val = 'p'}, {NULL}, }; +void iptables_exit(enum xtables_exittype status, const char *msg, ...) + __attribute__((noreturn, format(printf,2,3))); + +void iptables_exit(enum xtables_exittype status, const char *msg, ...) +{ + va_list args; + gchar str[256] = { 0 }; + + switch (status) { + case OTHER_PROBLEM: + DBG("OTHER_PROBLEM"); + break; + case PARAMETER_PROBLEM: + DBG("PARAMETER_PROBLEM"); + break; + case VERSION_PROBLEM: + DBG("VERSION_PROBLEM"); + break; + case RESOURCE_PROBLEM: + DBG("RESOURCE_PROBLEM"); + break; + case XTF_ONLY_ONCE: + DBG("XTF_ONLY_ONCE"); + break; + case XTF_NO_INVERT: + DBG("XTF_NO_INVERT"); + break; + case XTF_BAD_VALUE: + DBG("XTF_BAD_VALUE"); + break; + case XTF_ONE_ACTION: + DBG("XTF_ONE_ACTION"); + break; + } + + va_start(args, msg); + vsnprintf(str, 256, msg, args); + va_end(args); + + connman_error("iptables rule error: %s", str); + + if (can_jmp()) { + DBG("calling longjmp()"); + /* enum xtables_exittype begins from 1 */ + longjmp(env_state, status); + } + + connman_error("exit because of iptables error"); + + exit(status); +} + struct xtables_globals iptables_globals = { .option_offset = 0, .opts = iptables_opts, .orig_opts = iptables_opts, + .exit_err = iptables_exit, +#if XTABLES_VERSION_CODE > 10 + .compat_rev = xtables_compatible_revision, +#endif +}; + +struct xtables_globals ip6tables_globals = { + .option_offset = 0, + .opts = iptables_opts, + .orig_opts = iptables_opts, + .exit_err = iptables_exit, #if XTABLES_VERSION_CODE > 10 .compat_rev = xtables_compatible_revision, #endif @@ -1578,9 +2686,15 @@ static struct xtables_target *prepare_target(struct connman_iptables *table, bool is_builtin, is_user_defined; GList *chain_head = NULL; size_t target_size; + int err; is_builtin = false; is_user_defined = false; + + DBG("target %s", target_name); + + if (!table) + return NULL; if (is_builtin_target(target_name)) is_builtin = true; @@ -1590,16 +2704,37 @@ static struct xtables_target *prepare_target(struct connman_iptables *table, is_user_defined = true; } + enable_jmp(); + + if ((err = setjmp(env_state)) != 0) { + DBG("setjmp() called by longjmp() with value %d", err); + disable_jmp(); + return NULL; + } + if (is_builtin || is_user_defined) - xt_t = xtables_find_target(IPT_STANDARD_TARGET, + xt_t = xtables_find_target(get_standard_target(table->type), XTF_LOAD_MUST_SUCCEED); - else + else xt_t = xtables_find_target(target_name, XTF_TRY_LOAD); + disable_jmp(); + if (!xt_t) return NULL; - target_size = ALIGN(sizeof(struct ipt_entry_target)) + xt_t->size; + switch (table->type) { + case AF_INET: + target_size = XT_ALIGN(sizeof(struct ipt_entry_target)) + + xt_t->size; + break; + case AF_INET6: + target_size = XT_ALIGN(sizeof(struct ip6t_entry_target)) + + xt_t->size; + break; + default: + return NULL; + } xt_t->t = g_try_malloc0(target_size); if (!xt_t->t) @@ -1611,7 +2746,8 @@ static struct xtables_target *prepare_target(struct connman_iptables *table, struct xt_standard_target *target; target = (struct xt_standard_target *)(xt_t->t); - g_stpcpy(target->target.u.user.name, IPT_STANDARD_TARGET); + g_stpcpy(target->target.u.user.name, + get_standard_target(table->type)); if (is_builtin) target->verdict = target_to_verdict(target_name); @@ -1628,24 +2764,51 @@ static struct xtables_target *prepare_target(struct connman_iptables *table, xt_t->init(xt_t->t); } - if (xt_t->x6_options) - iptables_globals.opts = - xtables_options_xfrm( - iptables_globals.orig_opts, - iptables_globals.opts, - xt_t->x6_options, - &xt_t->option_offset); - else - iptables_globals.opts = - xtables_merge_options( - iptables_globals.orig_opts, - iptables_globals.opts, - xt_t->extra_opts, - &xt_t->option_offset); + switch (table->type) { + case AF_INET: + if (xt_t->x6_options) + iptables_globals.opts = + xtables_options_xfrm( + iptables_globals.orig_opts, + iptables_globals.opts, + xt_t->x6_options, + &xt_t->option_offset); + else + iptables_globals.opts = + xtables_merge_options( + iptables_globals.orig_opts, + iptables_globals.opts, + xt_t->extra_opts, + &xt_t->option_offset); + + if (!iptables_globals.opts) { + g_free(xt_t->t); + xt_t = NULL; + } + + break; + case AF_INET6: + if (xt_t->x6_options) + ip6tables_globals.opts = + xtables_options_xfrm( + ip6tables_globals.orig_opts, + ip6tables_globals.opts, + xt_t->x6_options, + &xt_t->option_offset); + else + ip6tables_globals.opts = + xtables_merge_options( + ip6tables_globals.orig_opts, + ip6tables_globals.opts, + xt_t->extra_opts, + &xt_t->option_offset); + + if (!ip6tables_globals.opts) { + g_free(xt_t->t); + xt_t = NULL; + } - if (!iptables_globals.opts) { - g_free(xt_t->t); - xt_t = NULL; + break; } return xt_t; @@ -1657,12 +2820,35 @@ static struct xtables_match *prepare_matches(struct connman_iptables *table, { struct xtables_match *xt_m; size_t match_size; + int err; + + if (!table || !match_name) + return NULL; + + enable_jmp(); - if (!match_name) + if ((err = setjmp(env_state)) != 0) { + DBG("setjmp() called by longjmp() with value %d", err); + disable_jmp(); return NULL; + } xt_m = xtables_find_match(match_name, XTF_LOAD_MUST_SUCCEED, xt_rm); - match_size = ALIGN(sizeof(struct ipt_entry_match)) + xt_m->size; + + disable_jmp(); + + switch (table->type) { + case AF_INET: + match_size = XT_ALIGN(sizeof(struct ipt_entry_match)) + + xt_m->size; + break; + case AF_INET6: + match_size = XT_ALIGN(sizeof(struct ip6t_entry_match)) + + xt_m->size; + break; + default: + return NULL; + } xt_m->m = g_try_malloc0(match_size); if (!xt_m->m) @@ -1675,28 +2861,59 @@ static struct xtables_match *prepare_matches(struct connman_iptables *table, if (xt_m->init) xt_m->init(xt_m->m); - if (xt_m->x6_options) - iptables_globals.opts = - xtables_options_xfrm( - iptables_globals.orig_opts, - iptables_globals.opts, - xt_m->x6_options, - &xt_m->option_offset); - else + switch (table->type) { + case AF_INET: + if (xt_m->x6_options) + iptables_globals.opts = + xtables_options_xfrm( + iptables_globals.orig_opts, + iptables_globals.opts, + xt_m->x6_options, + &xt_m->option_offset); + else iptables_globals.opts = - xtables_merge_options( - iptables_globals.orig_opts, - iptables_globals.opts, - xt_m->extra_opts, - &xt_m->option_offset); + xtables_merge_options( + iptables_globals.orig_opts, + iptables_globals.opts, + xt_m->extra_opts, + &xt_m->option_offset); - if (!iptables_globals.opts) { - g_free(xt_m->m); + if (!iptables_globals.opts) { + g_free(xt_m->m); + + if (xt_m == xt_m->next) + free(xt_m); + + xt_m = NULL; + } - if (xt_m == xt_m->next) - free(xt_m); + break; + case AF_INET6: + if (xt_m->x6_options) + ip6tables_globals.opts = + xtables_options_xfrm( + ip6tables_globals.orig_opts, + ip6tables_globals.opts, + xt_m->x6_options, + &xt_m->option_offset); + else + ip6tables_globals.opts = + xtables_merge_options( + ip6tables_globals.orig_opts, + ip6tables_globals.opts, + xt_m->extra_opts, + &xt_m->option_offset); + + if (!ip6tables_globals.opts) { + g_free(xt_m->m); + + if (xt_m == xt_m->next) + free(xt_m); + + xt_m = NULL; + } - xt_m = NULL; + break; } return xt_m; @@ -1721,12 +2938,14 @@ static int parse_ip_and_mask(const char *str, struct in_addr *ip, if (tokens[1]) { prefixlength = strtol(tokens[1], NULL, 10); - if (prefixlength > 31) { + if (prefixlength > 32) { err = -1; goto out; + } else if (prefixlength == 32) { + tmp = 0xffffffff; + } else { + tmp = ~(0xffffffff >> prefixlength); } - - tmp = ~(0xffffffff >> prefixlength); } else { tmp = 0xffffffff; } @@ -1740,35 +2959,96 @@ out: return err; } -static struct connman_iptables *get_table(const char *table_name) +static int parse_ipv6_and_mask(const char *str, struct in6_addr *ip, + struct in6_addr *mask) { - struct connman_iptables *table; + char **tokens; + uint32_t prefixlength; + struct in6_addr in6; + int i, j; + int err; + + tokens = g_strsplit(str, "/", 2); + if (!tokens) + return -1; + + if (!inet_pton(AF_INET6, tokens[0], ip)) { + err = -1; + goto out; + } + + if (tokens[1]) { + prefixlength = strtol(tokens[1], NULL, 10); + if (prefixlength > 128) { + err = -1; + goto out; + } + } else { + prefixlength = 128; + } + + /* + * This part was adapted from (no need to re-invent the wheel): + * https://gitlab.com/ipcalc/ipcalc/blob/master/ipcalc.c#L733 + */ + memset(&in6, 0, sizeof(struct in6_addr)); + + for (i = prefixlength, j = 0; i > 0; i -= 8, j++) { + if (i >= 8) + in6.s6_addr[j] = 0xff; + else + in6.s6_addr[j] = (unsigned long)(0xffU << (8 - i)); + } + + memcpy(mask, &in6, sizeof(struct in6_addr)); + + for (i = 0; i < 16 ; i++) + ip->s6_addr[i] = ip->s6_addr[i] & mask->s6_addr[i]; + + err = 0; +out: + g_strfreev(tokens); + + return err; +} + +static struct connman_iptables *get_table(int type, const char *table_name) +{ + struct connman_iptables *table = NULL; if (!table_name) table_name = "filter"; - table = g_hash_table_lookup(table_hash, table_name); + table = hash_table_lookup(type, table_name); + if (table) return table; - table = iptables_init(table_name); + table = iptables_init(type, table_name); + if (!table) return NULL; + if (table->name) + g_free(table->name); + table->name = g_strdup(table_name); - g_hash_table_replace(table_hash, table->name, table); + + hash_table_replace(type, table->name, table); return table; } struct parse_context { + int type; int argc; char **argv; struct ipt_ip *ip; + struct ip6t_ip6 *ipv6; struct xtables_target *xt_t; GList *xt_m; struct xtables_rule_match *xt_rm; - int proto; + uint16_t proto; }; static int prepare_getopt_args(const char *str, struct parse_context *ctx) @@ -1809,13 +3089,34 @@ static int parse_xt_modules(int c, bool invert, struct xtables_match *m; struct xtables_rule_match *rm; struct ipt_entry fw; + struct ip6t_entry fw6; + int err; + + switch (ctx->type) { + case AF_INET: + memset(&fw, 0, sizeof(fw)); + + /* The SNAT parser wants to know the protocol. */ + if (ctx->proto == 0) + ctx->proto = IPPROTO_IP; + + fw.ip.proto = ctx->proto; + break; + case AF_INET6: + memset(&fw6, 0, sizeof(fw6)); + + if (ctx->proto == 0) + ctx->proto = IPPROTO_IPV6; + + fw6.ipv6.proto = ctx->proto; - memset(&fw, 0, sizeof(fw)); + /* Flags must be set for IPv6 if protocol is set. */ + fw6.ipv6.flags |= IP6T_F_PROTO; - /* The SNAT parser wants to know the protocol. */ - if (ctx->proto == 0) - ctx->proto = IPPROTO_IP; - fw.ip.proto = ctx->proto; + break; + default: + return 0; + } for (rm = ctx->xt_rm; rm; rm = rm->next) { if (rm->completed != 0) @@ -1831,7 +3132,24 @@ static int parse_xt_modules(int c, bool invert, + XT_OPTION_OFFSET_SCALE) continue; - xtables_option_mpcall(c, ctx->argv, invert, m, &fw); + enable_jmp(); + + if ((err = setjmp(env_state)) != 0) { + DBG("setjmp() called by longjmp() with value %d", err); + disable_jmp(); + return -EINVAL; + } + + switch (ctx->type) { + case AF_INET: + xtables_option_mpcall(c, ctx->argv, invert, m, &fw); + break; + case AF_INET6: + xtables_option_mpcall(c, ctx->argv, invert, m, &fw6); + break; + } + + disable_jmp(); } if (!ctx->xt_t) @@ -1845,7 +3163,24 @@ static int parse_xt_modules(int c, bool invert, + XT_OPTION_OFFSET_SCALE) return 0; - xtables_option_tpcall(c, ctx->argv, invert, ctx->xt_t, &fw); + enable_jmp(); + + if ((err = setjmp(env_state)) != 0) { + DBG("setjmp() called by longjmp() with value %d", err); + disable_jmp(); + return -EINVAL; + } + + switch (ctx->type) { + case AF_INET: + xtables_option_tpcall(c, ctx->argv, invert, ctx->xt_t, &fw); + break; + case AF_INET6: + xtables_option_tpcall(c, ctx->argv, invert, ctx->xt_t, &fw6); + break; + } + + disable_jmp(); return 0; } @@ -1853,13 +3188,35 @@ static int parse_xt_modules(int c, bool invert, static int final_check_xt_modules(struct parse_context *ctx) { struct xtables_rule_match *rm; + int err; + + for (rm = ctx->xt_rm; rm; rm = rm->next) { + enable_jmp(); + + if ((err = setjmp(env_state)) != 0) { + DBG("setjmp() called by longjmp() with value %d", err); + disable_jmp(); + return -EINVAL; + } - for (rm = ctx->xt_rm; rm; rm = rm->next) xtables_option_mfcall(rm->match); + disable_jmp(); + } + + enable_jmp(); + + if ((err = setjmp(env_state)) != 0) { + DBG("setjmp() called by longjmp() with value %d", err); + disable_jmp(); + return -EINVAL; + } + if (ctx->xt_t) xtables_option_tfcall(ctx->xt_t); + disable_jmp(); + return 0; } @@ -1946,9 +3303,28 @@ static int parse_rule_spec(struct connman_iptables *table, bool invert = false; int len, c, err; - ctx->ip = g_try_new0(struct ipt_ip, 1); - if (!ctx->ip) - return -ENOMEM; + if (ctx->type != table->type) { + DBG("ctx->type %d does not match table->type %d", ctx->type, + table->type); + return -EINVAL; + } + + switch (ctx->type) { + case AF_INET: + ctx->ip = g_try_new0(struct ipt_ip, 1); + if (!ctx->ip) + return -ENOMEM; + + break; + case AF_INET6: + ctx->ipv6 = g_try_new0(struct ip6t_ip6, 1); + if (!ctx->ipv6) + return -ENOMEM; + + break; + default: + return -EINVAL; + } /* * Tell getopt_long not to generate error messages for unknown @@ -1958,29 +3334,58 @@ static int parse_rule_spec(struct connman_iptables *table, optind = 0; while ((c = getopt_long(ctx->argc, ctx->argv, - "-:d:i:o:s:m:j:", - iptables_globals.opts, NULL)) != -1) { + "-:d:i:o:s:m:j:p:", + ctx->type == AF_INET ? + iptables_globals.opts : + ip6tables_globals.opts, + NULL)) != -1) { switch (c) { case 's': - /* Source specification */ - if (!parse_ip_and_mask(optarg, - &ctx->ip->src, - &ctx->ip->smsk)) - break; + if (ctx->type == AF_INET) { + /* Source specification */ + if (!parse_ip_and_mask(optarg, + &ctx->ip->src, + &ctx->ip->smsk)) + break; + + if (invert) + ctx->ip->invflags |= IPT_INV_SRCIP; + } + + if (ctx->type == AF_INET6) { + if (!parse_ipv6_and_mask(optarg, + &ctx->ipv6->src, + &ctx->ipv6->smsk)) + break; - if (invert) - ctx->ip->invflags |= IPT_INV_SRCIP; + if (invert) + ctx->ipv6->invflags |= IP6T_INV_SRCIP; + } break; case 'd': - /* Destination specification */ - if (!parse_ip_and_mask(optarg, - &ctx->ip->dst, - &ctx->ip->dmsk)) - break; + if (ctx->type == AF_INET) { + /* Destination specification */ + if (!parse_ip_and_mask(optarg, + &ctx->ip->dst, + &ctx->ip->dmsk)) + break; + + if (invert) + ctx->ip->invflags |= IPT_INV_DSTIP; + } + + if (ctx->type == AF_INET6) { + /* Destination specification */ + if (!parse_ipv6_and_mask(optarg, + &ctx->ipv6->dst, + &ctx->ipv6->dmsk)) + break; - if (invert) - ctx->ip->invflags |= IPT_INV_DSTIP; + if (invert) + ctx->ip->invflags |= IP6T_INV_DSTIP; + } + break; case 'i': /* In interface specification */ @@ -1989,11 +3394,21 @@ static int parse_rule_spec(struct connman_iptables *table, if (len + 1 > IFNAMSIZ) break; - g_stpcpy(ctx->ip->iniface, optarg); - memset(ctx->ip->iniface_mask, 0xff, len + 1); + if (ctx->type == AF_INET) { + g_stpcpy(ctx->ip->iniface, optarg); + memset(ctx->ip->iniface_mask, 0xff, len + 1); + + if (invert) + ctx->ip->invflags |= IPT_INV_VIA_IN; + } + + if (ctx->type == AF_INET6) { + g_stpcpy(ctx->ipv6->iniface, optarg); + memset(ctx->ipv6->iniface_mask, 0xff, len + 1); - if (invert) - ctx->ip->invflags |= IPT_INV_VIA_IN; + if (invert) + ctx->ipv6->invflags |= IP6T_INV_VIA_IN; + } break; case 'o': @@ -2003,11 +3418,21 @@ static int parse_rule_spec(struct connman_iptables *table, if (len + 1 > IFNAMSIZ) break; - g_stpcpy(ctx->ip->outiface, optarg); - memset(ctx->ip->outiface_mask, 0xff, len + 1); + if (ctx->type == AF_INET) { + g_stpcpy(ctx->ip->outiface, optarg); + memset(ctx->ip->outiface_mask, 0xff, len + 1); + + if (invert) + ctx->ip->invflags |= IPT_INV_VIA_OUT; + } - if (invert) - ctx->ip->invflags |= IPT_INV_VIA_OUT; + if (ctx->type == AF_INET6) { + g_stpcpy(ctx->ipv6->outiface, optarg); + memset(ctx->ipv6->outiface_mask, 0xff, len + 1); + + if (invert) + ctx->ipv6->invflags |= IP6T_INV_VIA_OUT; + } break; case 'm': @@ -2021,7 +3446,41 @@ static int parse_rule_spec(struct connman_iptables *table, break; case 'p': + enable_jmp(); + + if ((err = setjmp(env_state)) != 0) { + DBG("setjmp() called by longjmp() with value " + "%d", err); + disable_jmp(); + + /* Errors from parse_rule_spec are negative */ + err = -EINVAL; + goto out; + } + ctx->proto = xtables_parse_protocol(optarg); + + disable_jmp(); + + /* + * If protocol was set add it to ipt_ip. + * xtables_parse_protocol() returns 0 or + * UINT16_MAX (-1) on error + */ + if (ctx->proto > 0 && ctx->proto < UINT16_MAX) { + if (ctx->type == AF_INET) + ctx->ip->proto = ctx->proto; + + if (ctx->type == AF_INET6) { + ctx->ipv6->proto = ctx->proto; + + /* + * Flags must be set for IPv6 if + * protocol is set. + */ + ctx->ipv6->flags |= IP6T_F_PROTO; + } + } break; case 'j': /* Target */ @@ -2051,6 +3510,8 @@ static int parse_rule_spec(struct connman_iptables *table, err = parse_xt_modules(c, invert, ctx); if (err == 1) continue; + else if (err == -EINVAL) + goto out; break; } @@ -2064,6 +3525,42 @@ out: return err; } +static int current_type = -1; + +static int setup_xtables(int type) +{ + int err; + + DBG("%d", type); + + if (type == current_type) + return 0; + + if (current_type != -1) + reset_xtables(); + + switch (type) { + case AF_INET: + err = xtables_init_all(&iptables_globals, NFPROTO_IPV4); + break; + case AF_INET6: + err = xtables_init_all(&ip6tables_globals, NFPROTO_IPV6); + break; + default: + return -1; + } + + if (!err) { + current_type = type; + } else { + connman_error("error initializing xtables"); + current_type = -1; + reset_xtables(); + } + + return err; +} + static void reset_xtables(void) { struct xtables_match *xt_m; @@ -2103,7 +3600,10 @@ static void cleanup_parse_context(struct parse_context *ctx) GList *list; g_strfreev(ctx->argv); + g_free(ctx->ip); + g_free(ctx->ipv6); + if (ctx->xt_t) { g_free(ctx->xt_t->t); ctx->xt_t->t = NULL; @@ -2131,13 +3631,13 @@ static void cleanup_parse_context(struct parse_context *ctx) g_free(ctx); } -int __connman_iptables_dump(const char *table_name) +int __connman_iptables_dump(int type, const char *table_name) { struct connman_iptables *table; - DBG("-t %s -L", table_name); + DBG("%d -t %s -L", type, table_name); - table = get_table(table_name); + table = get_table(type, table_name); if (!table) return -EINVAL; @@ -2146,83 +3646,131 @@ int __connman_iptables_dump(const char *table_name) return 0; } -int __connman_iptables_new_chain(const char *table_name, +int __connman_iptables_new_chain(int type, + const char *table_name, const char *chain) { struct connman_iptables *table; - DBG("-t %s -N %s", table_name, chain); + DBG("%d -t %s -N %s", type, table_name, chain); - table = get_table(table_name); - if (!table) + table = get_table(type, table_name); + if (!table) { return -EINVAL; + } - return iptables_add_chain(table, chain); + switch (type) { + case AF_INET: + case AF_INET6: + return iptables_add_chain(table, chain); + } + + return -EINVAL; } -int __connman_iptables_delete_chain(const char *table_name, +int __connman_iptables_delete_chain(int type, + const char *table_name, const char *chain) { struct connman_iptables *table; - DBG("-t %s -X %s", table_name, chain); + DBG("%d -t %s -X %s", type, table_name, chain); - table = get_table(table_name); + table = get_table(type, table_name); if (!table) return -EINVAL; return iptables_delete_chain(table, chain); } -int __connman_iptables_flush_chain(const char *table_name, +int __connman_iptables_flush_chain(int type, + const char *table_name, const char *chain) { struct connman_iptables *table; - DBG("-t %s -F %s", table_name, chain); + DBG("%d -t %s -F %s", type, table_name, chain); - table = get_table(table_name); + table = get_table(type, table_name); if (!table) return -EINVAL; return iptables_flush_chain(table, chain); } -int __connman_iptables_change_policy(const char *table_name, +int __connman_iptables_find_chain(int type, + const char *table_name, + const char *chain) +{ + struct connman_iptables *table; + + DBG("%d -t %s -F %s", type, table_name, chain); + + table = get_table(type, table_name); + if (!table) + return -EINVAL; + + if(!find_chain_head(table, chain)) + return -ENOENT; // Not Found + + return 0; // Found +} + +int __connman_iptables_change_policy(int type, + const char *table_name, const char *chain, const char *policy) { struct connman_iptables *table; - DBG("-t %s -F %s", table_name, chain); + DBG("%d -t %s -F %s", type, table_name, chain); - table = get_table(table_name); + table = get_table(type, table_name); if (!table) return -EINVAL; return iptables_change_policy(table, chain, policy); } -int __connman_iptables_append(const char *table_name, +static void iptables_ip_setup(struct iptables_ip *ip, struct parse_context *ctx) +{ + if (!ip || !ctx) + return; + + ip->type = ctx->type; + ip->ip = ctx->ip; + ip->ip6 = ctx->ipv6; +} + +int __connman_iptables_append(int type, + const char *table_name, const char *chain, const char *rule_spec) { struct connman_iptables *table; struct parse_context *ctx; + struct iptables_ip ip = { 0 }; const char *target_name; int err; + err = setup_xtables(type); + + if (err < 0) + return err; + ctx = g_try_new0(struct parse_context, 1); if (!ctx) return -ENOMEM; - DBG("-t %s -A %s %s", table_name, chain, rule_spec); + ctx->type = type; + + DBG("%d -t %s -A %s %s", type, table_name, chain, rule_spec); err = prepare_getopt_args(rule_spec, ctx); if (err < 0) goto out; - table = get_table(table_name); + table = get_table(type, table_name); if (!table) { err = -EINVAL; goto out; @@ -2237,8 +3785,10 @@ int __connman_iptables_append(const char *table_name, else target_name = ctx->xt_t->name; - err = iptables_append_rule(table, ctx->ip, chain, - target_name, ctx->xt_t, ctx->xt_rm); + iptables_ip_setup(&ip, ctx); + + err = iptables_append_rule(table, &ip, chain, target_name, ctx->xt_t, + ctx->xt_rm); out: cleanup_parse_context(ctx); reset_xtables(); @@ -2246,26 +3796,35 @@ out: return err; } -int __connman_iptables_insert(const char *table_name, +int __connman_iptables_insert(int type, + const char *table_name, const char *chain, const char *rule_spec) { struct connman_iptables *table; struct parse_context *ctx; + struct iptables_ip ip = { 0 }; const char *target_name; int err; + err = setup_xtables(type); + + if (err < 0) + return err; + ctx = g_try_new0(struct parse_context, 1); if (!ctx) return -ENOMEM; + + ctx->type = type; - DBG("-t %s -I %s %s", table_name, chain, rule_spec); + DBG("%d -t %s -I %s %s", type, table_name, chain, rule_spec); err = prepare_getopt_args(rule_spec, ctx); if (err < 0) goto out; - table = get_table(table_name); + table = get_table(type, table_name); if (!table) { err = -EINVAL; goto out; @@ -2280,8 +3839,10 @@ int __connman_iptables_insert(const char *table_name, else target_name = ctx->xt_t->name; - err = iptables_insert_rule(table, ctx->ip, chain, - target_name, ctx->xt_t, ctx->xt_rm); + iptables_ip_setup(&ip, ctx); + + err = iptables_insert_rule(table, &ip, chain, target_name, ctx->xt_t, + ctx->xt_rm); out: cleanup_parse_context(ctx); reset_xtables(); @@ -2289,26 +3850,35 @@ out: return err; } -int __connman_iptables_delete(const char *table_name, +int __connman_iptables_delete(int type, + const char *table_name, const char *chain, const char *rule_spec) { struct connman_iptables *table; struct parse_context *ctx; + struct iptables_ip ip = { 0 }; const char *target_name; int err; + err = setup_xtables(type); + + if (err < 0) + return err; + ctx = g_try_new0(struct parse_context, 1); if (!ctx) return -ENOMEM; + + ctx->type = type; - DBG("-t %s -D %s %s", table_name, chain, rule_spec); + DBG("%d -t %s -D %s %s", type, table_name, chain, rule_spec); err = prepare_getopt_args(rule_spec, ctx); if (err < 0) goto out; - table = get_table(table_name); + table = get_table(type, table_name); if (!table) { err = -EINVAL; goto out; @@ -2323,9 +3893,10 @@ int __connman_iptables_delete(const char *table_name, else target_name = ctx->xt_t->name; - err = iptables_delete_rule(table, ctx->ip, chain, - target_name, ctx->xt_t, ctx->xt_m, - ctx->xt_rm); + iptables_ip_setup(&ip, ctx); + + err = iptables_delete_rule(table, &ip, chain, target_name, ctx->xt_t, + ctx->xt_m, ctx->xt_rm); out: cleanup_parse_context(ctx); reset_xtables(); @@ -2333,30 +3904,46 @@ out: return err; } -int __connman_iptables_commit(const char *table_name) +int __connman_iptables_commit(int type, const char *table_name) { struct connman_iptables *table; - struct ipt_replace *repl; + struct iptables_replace repl = { 0 }; int err; struct xt_counters_info *counters; struct connman_iptables_entry *e; GList *list; unsigned int cnt; - DBG("%s", table_name); + err = setup_xtables(type); + + if (err < 0) + return err; + + DBG("%d %s", type, table_name); - table = g_hash_table_lookup(table_hash, table_name); + repl.type = type; + + table = hash_table_lookup(type, table_name); if (!table) return -EINVAL; - repl = iptables_blob(table); - if(!repl) - return -ENOMEM; + switch (type) { + case AF_INET: + repl.r = iptables_blob(table); + if (!repl.r) + return -ENOMEM; + + break; + case AF_INET6: + repl.r6 = ip6tables_blob(table); + if (!repl.r6) + return -ENOMEM; + } if (debug_enabled) - dump_ipt_replace(repl); + dump_replace(&repl); - err = iptables_replace(table, repl); + err = iptables_replace(table, &repl); if (err < 0) goto out_free; @@ -2367,12 +3954,23 @@ int __connman_iptables_commit(const char *table_name) err = -ENOMEM; goto out_hash_remove; } - g_stpcpy(counters->name, table->info->name); + g_stpcpy(counters->name, iptables_table_get_info_name(table)); counters->num_counters = table->num_entries; for (list = table->entries, cnt = 0; list; list = list->next, cnt++) { e = list->data; - if (e->counter_idx >= 0) - counters->counters[cnt] = repl->counters[e->counter_idx]; + if (e->counter_idx >= 0) { + + switch (type) { + case AF_INET: + counters->counters[cnt] = + repl.r->counters[e->counter_idx]; + break; + case AF_INET6: + counters->counters[cnt] = + repl.r6->counters[e->counter_idx]; + break; + } + } } err = iptables_add_counters(table, counters); g_free(counters); @@ -2383,10 +3981,19 @@ int __connman_iptables_commit(const char *table_name) err = 0; out_hash_remove: - g_hash_table_remove(table_hash, table_name); + hash_table_remove(type, table_name); out_free: - g_free(repl->counters); - g_free(repl); + if (type == AF_INET && repl.r) + g_free(repl.r->counters); + + if (type == AF_INET6 && repl.r6) + g_free(repl.r6->counters); + + g_free(repl.r); + g_free(repl.r6); + + reset_xtables(); + return err; } @@ -2397,7 +4004,7 @@ static void remove_table(gpointer user_data) table_cleanup(table); } -static int iterate_chains_cb(struct ipt_entry *entry, int builtin, +static int iterate_chains_cb(struct connman_iptables_entry *entry, int builtin, unsigned int hook, size_t size, unsigned int offset, void *user_data) { @@ -2405,40 +4012,58 @@ static int iterate_chains_cb(struct ipt_entry *entry, int builtin, connman_iptables_iterate_chains_cb_t cb = cbd->cb; struct xt_entry_target *target; - if (offset + entry->next_offset == size) + if (offset + iptables_entry_get_next_offset(entry) == size) return 0; - target = ipt_get_target(entry); + target = iptables_entry_get_target(entry); - if (!g_strcmp0(target->u.user.name, IPT_ERROR_TARGET)) + if (!g_strcmp0(target->u.user.name, get_error_target(entry->type))) { (*cb)((const char *)target->data, cbd->user_data); - else if (builtin >= 0) + } else if (builtin >= 0) { (*cb)(hooknames[builtin], cbd->user_data); + } return 0; } -int __connman_iptables_iterate_chains(const char *table_name, +int __connman_iptables_iterate_chains(int type, const char *table_name, connman_iptables_iterate_chains_cb_t cb, void *user_data) { struct cb_data *cbd = cb_data_new(cb, user_data); struct connman_iptables *table; + struct connman_iptables_entry entry = { 0 }; + int err; + + err = setup_xtables(type); + + if (err < 0) + return err; - table = get_table(table_name); + table = get_table(type, table_name); if (!table) { g_free(cbd); return -EINVAL; } - iterate_entries(table->blob_entries->entrytable, - table->info->valid_hooks, - table->info->hook_entry, - table->info->underflow, - table->blob_entries->size, + entry.type = type; + + if (type == AF_INET) + entry.entry = table->blob_entries->entrytable; + + if (type == AF_INET6) + entry.entry6 = table->blob_entries6->entrytable; + + iterate_entries(&entry, + iptables_table_get_info_valid_hooks(table), + iptables_table_get_info_hook_entry(table), + iptables_table_get_info_underflow(table), + iptables_table_get_entries_size(table), iterate_chains_cb, cbd); g_free(cbd); + + reset_xtables(); return 0; } @@ -2453,7 +4078,8 @@ int __connman_iptables_init(void) table_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, remove_table); - xtables_init_all(&iptables_globals, NFPROTO_IPV4); + table_hash_ipv6 = g_hash_table_new_full(g_str_hash, g_str_equal, + NULL, remove_table); return 0; } @@ -2463,4 +4089,6 @@ void __connman_iptables_cleanup(void) DBG(""); g_hash_table_destroy(table_hash); + g_hash_table_destroy(table_hash_ipv6); } + diff --git a/src/ipv6pd.c b/src/ipv6pd.c index 0d221f07..8cd2a33e 100755 --- a/src/ipv6pd.c +++ b/src/ipv6pd.c @@ -41,7 +41,6 @@ static guint timer_uplink; static guint timer_ra; static char *default_interface; static GSList *prefixes; -static GHashTable *timer_hash; static void *rs_context; static int setup_prefix_delegation(struct connman_service *service); @@ -139,7 +138,7 @@ static gboolean do_setup(gpointer data) if (!default_interface) DBG("No uplink connection, retrying prefix delegation"); - ret = setup_prefix_delegation(__connman_service_get_default()); + ret = setup_prefix_delegation(connman_service_get_default()); if (ret < 0 && ret != -EINPROGRESS) return TRUE; /* delegation error, try again */ @@ -155,7 +154,7 @@ static void dhcpv6_renew_callback(struct connman_network *network, if (status == CONNMAN_DHCPV6_STATUS_SUCCEED) dhcpv6_callback(network, status, data); else - setup_prefix_delegation(__connman_service_get_default()); + setup_prefix_delegation(connman_service_get_default()); } static void cleanup(void) @@ -165,11 +164,6 @@ static void cleanup(void) timer_uplink = 0; } - if (timer_hash) { - g_hash_table_destroy(timer_hash); - timer_hash = NULL; - } - if (prefixes) { g_slist_free_full(prefixes, g_free); prefixes = NULL; @@ -260,13 +254,6 @@ static int setup_prefix_delegation(struct connman_service *service) return err; } -static void cleanup_timer(gpointer user_data) -{ - guint timer = GPOINTER_TO_UINT(user_data); - - g_source_remove(timer); -} - static void update_default_interface(struct connman_service *service) { setup_prefix_delegation(service); @@ -275,7 +262,7 @@ static void update_default_interface(struct connman_service *service) static void update_ipconfig(struct connman_service *service, struct connman_ipconfig *ipconfig) { - if (!service || service != __connman_service_get_default()) + if (!service || service != connman_service_get_default()) return; if (ipconfig != __connman_service_get_ip6config(service)) @@ -308,7 +295,7 @@ static void update_ipconfig(struct connman_service *service, } } -static struct connman_notifier pd_notifier = { +static const struct connman_notifier pd_notifier = { .name = "IPv6 prefix delegation", .default_changed = update_default_interface, .ipconfig_changed = update_ipconfig, @@ -333,16 +320,13 @@ int __connman_ipv6pd_setup(const char *bridge) bridge_index = connman_inet_ifindex(bridge); - timer_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, - NULL, cleanup_timer); - err = __connman_inet_ipv6_start_recv_rs(bridge_index, rs_received, NULL, &rs_context); if (err < 0) DBG("Cannot receive router solicitation %d/%s", err, strerror(-err)); - service = __connman_service_get_default(); + service = connman_service_get_default(); if (service) { /* * We have an uplink connection already, try to use it. @@ -23,15 +23,14 @@ #include <config.h> #endif -#define _GNU_SOURCE #include <stdio.h> #include <unistd.h> #include <stdarg.h> #include <stdlib.h> #include <string.h> #include <syslog.h> -#include <execinfo.h> #include <dlfcn.h> +#include <signal.h> #include "connman.h" @@ -175,6 +174,9 @@ void __connman_log_s(int log_priority, const char *format, ...) } #endif +/* This makes sure we always have a __debug section. */ +CONNMAN_DEBUG_DEFINE(dummy); + /** * connman_info: * @format: format string @@ -227,7 +229,6 @@ void connman_error(const char *format, ...) vsyslog(LOG_ERR, format, ap); va_end(ap); - fflush(log_file); } /** @@ -246,122 +247,13 @@ void connman_debug(const char *format, ...) vsyslog(LOG_DEBUG, format, ap); va_end(ap); - fflush(log_file); -} - -static void print_backtrace(unsigned int offset) -{ - void *frames[99]; - size_t n_ptrs; - unsigned int i; - int outfd[2], infd[2]; - int pathlen; - pid_t pid; - - if (!program_exec) - return; - - pathlen = strlen(program_path); - - n_ptrs = backtrace(frames, G_N_ELEMENTS(frames)); - if (n_ptrs < offset) - return; - - if (pipe(outfd) < 0) - return; - - if (pipe(infd) < 0) { - close(outfd[0]); - close(outfd[1]); - return; - } - - pid = fork(); - if (pid < 0) { - close(outfd[0]); - close(outfd[1]); - close(infd[0]); - close(infd[1]); - return; - } - - if (pid == 0) { - close(outfd[1]); - close(infd[0]); - - dup2(outfd[0], STDIN_FILENO); - dup2(infd[1], STDOUT_FILENO); - - execlp("addr2line", "-C", "-f", "-e", program_exec, NULL); - - exit(EXIT_FAILURE); - } - - close(outfd[0]); - close(infd[1]); - - connman_error("++++++++ backtrace ++++++++"); - - for (i = offset; i < n_ptrs - 1; i++) { - Dl_info info; - char addr[20], buf[PATH_MAX * 2]; - int len, written; - char *ptr, *pos; - - dladdr(frames[i], &info); - - len = snprintf(addr, sizeof(addr), "%p\n", frames[i]); - if (len < 0) - break; - - written = write(outfd[1], addr, len); - if (written < 0) - break; - - len = read(infd[0], buf, sizeof(buf) - 1); - if (len < 0) - break; - - buf[len] = '\0'; - - pos = strchr(buf, '\n'); -#if defined TIZEN_EXT - if (pos) { -#endif - *pos++ = '\0'; - - if (strcmp(buf, "??") == 0) { - connman_error("#%-2u %p in %s", i - offset, - frames[i], info.dli_fname); - continue; - } - - ptr = strchr(pos, '\n'); - *ptr++ = '\0'; - - if (strncmp(pos, program_path, pathlen) == 0) - pos += pathlen + 1; - - connman_error("#%-2u %p in %s() at %s", i - offset, - frames[i], buf, pos); -#if defined TIZEN_EXT - } -#endif - } - - connman_error("+++++++++++++++++++++++++++"); - - kill(pid, SIGTERM); - - close(outfd[1]); - close(infd[0]); } static void signal_handler(int signo) { connman_error("Aborting (signal %d) [%s]", signo, program_exec); - print_backtrace(2); + print_backtrace(program_path, program_exec, 2); exit(EXIT_FAILURE); } @@ -30,17 +30,19 @@ #include <string.h> #include <signal.h> #include <sys/signalfd.h> -#include <sys/types.h> -#include <sys/resource.h> #include <getopt.h> #include <sys/stat.h> #include <net/if.h> #include <netdb.h> +#include <sys/time.h> +#include <sys/resource.h> #include <gdbus.h> #include "connman.h" +#define CONF_ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]) - 1) + #define DEFAULT_INPUT_REQUEST_TIMEOUT (120 * 1000) #define DEFAULT_BROWSER_LAUNCH_TIMEOUT (300 * 1000) @@ -54,6 +56,11 @@ static char *default_auto_connect[] = { NULL }; +static char *default_favorite_techs[] = { + "ethernet", + NULL +}; + static char *default_blacklist[] = { "vmnet", "vboxnet", @@ -68,6 +75,7 @@ static struct { bool bg_scan; char **pref_timeservers; unsigned int *auto_connect; + unsigned int *favorite_techs; unsigned int *preferred_techs; unsigned int *always_connected_techs; char **fallback_nameservers; @@ -75,21 +83,25 @@ static struct { unsigned int timeout_browserlaunch; char **blacklisted_interfaces; bool allow_hostname_updates; + bool allow_domainname_updates; bool single_tech; char **tethering_technologies; bool persistent_tethering_mode; bool enable_6to4; char *vendor_class_id; bool enable_online_check; + bool auto_connect_roaming_services; + bool acd; + bool use_gateways_as_timeservers; #if defined TIZEN_EXT char **cellular_interfaces; bool tizen_tv_extension; - bool use_gateway_timeserver; #endif } connman_settings = { .bg_scan = true, .pref_timeservers = NULL, .auto_connect = NULL, + .favorite_techs = NULL, .preferred_techs = NULL, .always_connected_techs = NULL, .fallback_nameservers = NULL, @@ -97,22 +109,26 @@ static struct { .timeout_browserlaunch = DEFAULT_BROWSER_LAUNCH_TIMEOUT, .blacklisted_interfaces = NULL, .allow_hostname_updates = true, + .allow_domainname_updates = true, .single_tech = false, .tethering_technologies = NULL, .persistent_tethering_mode = false, .enable_6to4 = false, .vendor_class_id = NULL, .enable_online_check = true, + .auto_connect_roaming_services = false, + .acd = false, + .use_gateways_as_timeservers = false, #if defined TIZEN_EXT .cellular_interfaces = NULL, .tizen_tv_extension = false, - .use_gateway_timeserver = false, #endif }; #define CONF_BG_SCAN "BackgroundScanning" #define CONF_PREF_TIMESERVERS "FallbackTimeservers" -#define CONF_AUTO_CONNECT "DefaultAutoConnectTechnologies" +#define CONF_AUTO_CONNECT_TECHS "DefaultAutoConnectTechnologies" +#define CONF_FAVORITE_TECHS "DefaultFavoriteTechnologies" #define CONF_ALWAYS_CONNECTED_TECHS "AlwaysConnectedTechnologies" #define CONF_PREFERRED_TECHS "PreferredTechnologies" #define CONF_FALLBACK_NAMESERVERS "FallbackNameservers" @@ -120,22 +136,25 @@ static struct { #define CONF_TIMEOUT_BROWSERLAUNCH "BrowserLaunchTimeout" #define CONF_BLACKLISTED_INTERFACES "NetworkInterfaceBlacklist" #define CONF_ALLOW_HOSTNAME_UPDATES "AllowHostnameUpdates" +#define CONF_ALLOW_DOMAINNAME_UPDATES "AllowDomainnameUpdates" #define CONF_SINGLE_TECH "SingleConnectedTechnology" #define CONF_TETHERING_TECHNOLOGIES "TetheringTechnologies" #define CONF_PERSISTENT_TETHERING_MODE "PersistentTetheringMode" #define CONF_ENABLE_6TO4 "Enable6to4" #define CONF_VENDOR_CLASS_ID "VendorClassID" #define CONF_ENABLE_ONLINE_CHECK "EnableOnlineCheck" +#define CONF_AUTO_CONNECT_ROAMING_SERVICES "AutoConnectRoamingServices" +#define CONF_ACD "AddressConflictDetection" +#define CONF_USE_GATEWAYS_AS_TIMESERVERS "UseGatewaysAsTimeservers" #if defined TIZEN_EXT #define CONF_CELLULAR_INTERFACE "NetworkCellularInterfaceList" #define CONF_TIZEN_TV_EXT "TizenTVExtension" -#define CONF_USE_GATEWAY_TIMESERVER "UseGatewayTimeserver" #endif static const char *supported_options[] = { CONF_BG_SCAN, CONF_PREF_TIMESERVERS, - CONF_AUTO_CONNECT, + CONF_AUTO_CONNECT_TECHS, CONF_ALWAYS_CONNECTED_TECHS, CONF_PREFERRED_TECHS, CONF_FALLBACK_NAMESERVERS, @@ -143,15 +162,19 @@ static const char *supported_options[] = { CONF_TIMEOUT_BROWSERLAUNCH, CONF_BLACKLISTED_INTERFACES, CONF_ALLOW_HOSTNAME_UPDATES, + CONF_ALLOW_DOMAINNAME_UPDATES, CONF_SINGLE_TECH, CONF_TETHERING_TECHNOLOGIES, CONF_PERSISTENT_TETHERING_MODE, CONF_ENABLE_6TO4, + CONF_VENDOR_CLASS_ID, CONF_ENABLE_ONLINE_CHECK, + CONF_AUTO_CONNECT_ROAMING_SERVICES, + CONF_ACD, + CONF_USE_GATEWAYS_AS_TIMESERVERS, #if defined TIZEN_EXT CONF_CELLULAR_INTERFACE, CONF_TIZEN_TV_EXT, - CONF_USE_GATEWAY_TIMESERVER, #endif NULL }; @@ -289,13 +312,6 @@ static void check_Tizen_configuration(GKeyFile *config) connman_settings.tizen_tv_extension = boolean; g_clear_error(&error); - - boolean = __connman_config_get_bool(config, "General", - CONF_USE_GATEWAY_TIMESERVER, &error); - if (!error) - connman_settings.use_gateway_timeserver = boolean; - - g_clear_error(&error); } static void set_nofile_inc(void) @@ -328,7 +344,9 @@ static void parse_config(GKeyFile *config) if (!config) { connman_settings.auto_connect = - parse_service_types(default_auto_connect, 3); + parse_service_types(default_auto_connect, CONF_ARRAY_SIZE(default_auto_connect)); + connman_settings.favorite_techs = + parse_service_types(default_favorite_techs, CONF_ARRAY_SIZE(default_favorite_techs)); connman_settings.blacklisted_interfaces = g_strdupv(default_blacklist); return; @@ -351,14 +369,26 @@ static void parse_config(GKeyFile *config) g_clear_error(&error); str_list = __connman_config_get_string_list(config, "General", - CONF_AUTO_CONNECT, &len, &error); + CONF_AUTO_CONNECT_TECHS, &len, &error); if (!error) connman_settings.auto_connect = parse_service_types(str_list, len); else connman_settings.auto_connect = - parse_service_types(default_auto_connect, 3); + parse_service_types(default_auto_connect, CONF_ARRAY_SIZE(default_auto_connect)); + + g_clear_error(&error); + + str_list = __connman_config_get_string_list(config, "General", + CONF_FAVORITE_TECHS, &len, &error); + + if (!error) + connman_settings.favorite_techs = + parse_service_types(str_list, len); + else + connman_settings.favorite_techs = + parse_service_types(default_favorite_techs, CONF_ARRAY_SIZE(default_favorite_techs)); g_strfreev(str_list); @@ -431,6 +461,14 @@ static void parse_config(GKeyFile *config) g_clear_error(&error); boolean = __connman_config_get_bool(config, "General", + CONF_ALLOW_DOMAINNAME_UPDATES, + &error); + if (!error) + connman_settings.allow_domainname_updates = boolean; + + g_clear_error(&error); + + boolean = __connman_config_get_bool(config, "General", CONF_SINGLE_TECH, &error); if (!error) connman_settings.single_tech = boolean; @@ -477,6 +515,26 @@ static void parse_config(GKeyFile *config) g_clear_error(&error); + boolean = __connman_config_get_bool(config, "General", + CONF_AUTO_CONNECT_ROAMING_SERVICES, &error); + if (!error) + connman_settings.auto_connect_roaming_services = boolean; + + g_clear_error(&error); + + boolean = __connman_config_get_bool(config, "General", CONF_ACD, &error); + if (!error) + connman_settings.acd = boolean; + + g_clear_error(&error); + + boolean = __connman_config_get_bool(config, "General", + CONF_USE_GATEWAYS_AS_TIMESERVERS, &error); + if (!error) + connman_settings.use_gateways_as_timeservers = boolean; + + g_clear_error(&error); + #if defined TIZEN_EXT check_Tizen_configuration(config); #endif @@ -632,9 +690,9 @@ static GOptionEntry options[] = { G_OPTION_ARG_CALLBACK, parse_debug, "Specify debug options to enable", "DEBUG" }, { "device", 'i', 0, G_OPTION_ARG_STRING, &option_device, - "Specify networking device or interface", "DEV" }, + "Specify networking devices or interfaces", "DEV,..." }, { "nodevice", 'I', 0, G_OPTION_ARG_STRING, &option_nodevice, - "Specify networking interface to ignore", "DEV" }, + "Specify networking interfaces to ignore", "DEV,..." }, { "plugin", 'p', 0, G_OPTION_ARG_STRING, &option_plugin, "Specify plugins to load", "NAME,..." }, { "noplugin", 'P', 0, G_OPTION_ARG_CALLBACK, &parse_noplugin, @@ -646,7 +704,7 @@ static GOptionEntry options[] = { "Don't fork daemon to background" }, { "nodnsproxy", 'r', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &option_dnsproxy, - "Don't enable DNS Proxy" }, + "Don't support DNS resolving" }, { "nobacktrace", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &option_backtrace, "Don't print out backtrace information" }, @@ -678,6 +736,9 @@ bool connman_setting_get_bool(const char *key) if (g_str_equal(key, CONF_ALLOW_HOSTNAME_UPDATES)) return connman_settings.allow_hostname_updates; + if (g_str_equal(key, CONF_ALLOW_DOMAINNAME_UPDATES)) + return connman_settings.allow_domainname_updates; + if (g_str_equal(key, CONF_SINGLE_TECH)) return connman_settings.single_tech; @@ -690,10 +751,14 @@ bool connman_setting_get_bool(const char *key) if (g_str_equal(key, CONF_ENABLE_ONLINE_CHECK)) return connman_settings.enable_online_check; -#if defined TIZEN_EXT - if (g_str_equal(key, CONF_USE_GATEWAY_TIMESERVER)) - return connman_settings.use_gateway_timeserver; -#endif + if (g_str_equal(key, CONF_AUTO_CONNECT_ROAMING_SERVICES)) + return connman_settings.auto_connect_roaming_services; + + if (g_str_equal(key, CONF_ACD)) + return connman_settings.acd; + + if (g_str_equal(key, CONF_USE_GATEWAYS_AS_TIMESERVERS)) + return connman_settings.use_gateways_as_timeservers; return false; } @@ -722,9 +787,12 @@ char **connman_setting_get_string_list(const char *key) unsigned int *connman_setting_get_uint_list(const char *key) { - if (g_str_equal(key, CONF_AUTO_CONNECT)) + if (g_str_equal(key, CONF_AUTO_CONNECT_TECHS)) return connman_settings.auto_connect; + if (g_str_equal(key, CONF_FAVORITE_TECHS)) + return connman_settings.favorite_techs; + if (g_str_equal(key, CONF_PREFERRED_TECHS)) return connman_settings.preferred_techs; @@ -926,6 +994,7 @@ int main(int argc, char *argv[]) g_strfreev(connman_settings.pref_timeservers); g_free(connman_settings.auto_connect); + g_free(connman_settings.favorite_techs); g_free(connman_settings.preferred_techs); g_strfreev(connman_settings.fallback_nameservers); g_strfreev(connman_settings.blacklisted_interfaces); diff --git a/src/main.conf b/src/main.conf index 80724d52..c3190ca3 100755 --- a/src/main.conf +++ b/src/main.conf @@ -14,18 +14,27 @@ # user interface designs. # BrowserLaunchTimeout = 300 -# Enable background scanning. Default is true. -# Background scanning will start every 5 minutes unless -# the scan list is empty. In that case, a simple backoff -# mechanism starting from 10s up to 5 minutes will run. +# If wifi is disconnected, the background scanning will follow a simple +# backoff mechanism from 3s up to 5 minutes. Then, it will stay in 5 +# minutes unless user specifically asks for scanning through a D-Bus +# call. If so, the mechanism will start again from 3s. This feature +# activates also the background scanning while being connected, which +# is required for roaming on wifi. +# When BackgroundScanning is false, ConnMan will not perform any scan +# regardless of wifi is connected or not, unless it is requested by +# the user through a D-Bus call. # BackgroundScanning = true BackgroundScanning = false +# Assume that service gateways also function as timeservers. +# UseGatewaysAsTimeservers = false + # List of Fallback timeservers separated by ",". # These timeservers are used for NTP sync when there are -# no timeserver set by the user or by the service. -# These can contain mixed combination of fully qualified -# domain names, IPv4 and IPv6 addresses. +# no timeservers set by the user or by the service, and +# when UseGatewaysAsTimeservers = false. These can contain +# mixed combination of fully qualified domain names, IPv4 +# and IPv6 addresses. # FallbackTimeservers = #FallbackTimeservers = pool.ntp.org @@ -58,17 +67,22 @@ PreferredTechnologies = wifi, ethernet # List of blacklisted network interfaces separated by ",". # Found interfaces will be compared to the list and will -# not be handled by connman, if their first characters +# not be handled by ConnMan, if their first characters # match any of the list entries. Default value is # vmnet,vboxnet,virbr,ifb,ve-,vb-. # NetworkInterfaceBlacklist = vmnet,vboxnet,virbr,ifb,ve-,vb- -NetworkInterfaceBlacklist = veth, vmnet,vboxnet,virbr,usb,rndis,rmnet,rev_rmnet,dummy,seth_td,seth_w +NetworkInterfaceBlacklist = veth,vmnet,vboxnet,virbr,usb,rndis,rmnet,rev_rmnet,dummy,seth_td,seth_w -# Allow connman to change the system hostname. This can +# Allow ConnMan to change the system hostname. This can # happen for example if we receive DHCP hostname option. # Default value is true. # AllowHostnameUpdates = true +# Allow ConnMan to change the system domainname. This can +# happen for example if we receive DHCP domainname option. +# Default value is true. +# AllowDomainnameUpdates = true + # Keep only a single connected technology at any time. When a new # service is connected by the user or a better one is found according # to PreferredTechnologies, the new service is kept connected and all @@ -123,8 +137,20 @@ SingleConnectedTechnology = true # This setting has no effect if SingleConnectedTechnologies is enabled. # AlwaysConnectedTechnologies = -NetworkCellularInterfaceList = pdp,rmnet,seth_td,seth_w - # Allow connman to add service gateway to the time server list. # Default value is false. -# UseGatewayTimeserver = false +# UseGatewaysAsTimeservers = false + +# Enable auto connection of services in roaming. +# If this setting is false, roaming services are not auto-connected by ConnMan. +# Default value is false. +# AutoConnectRoamingServices = false + +# Enable address conflict detection +# If this setting is true, ConnMan will send probe ARP packets to see +# if an IPv4 address is already in use before assigning the address +# to an interface (in accordance with RFC 5227). +# Default value is false. +# AddressConflictDetection = false + +NetworkCellularInterfaceList = pdp,rmnet,seth_td,seth_w diff --git a/src/main_disable_eth.conf b/src/main_disable_eth.conf index ea4a9ddb..4cc22f1d 100755 --- a/src/main_disable_eth.conf +++ b/src/main_disable_eth.conf @@ -111,4 +111,4 @@ NetworkCellularInterfaceList = pdp,rmnet,seth_td,seth_w # Allow connman to add service gateway to the time server list. # Default value is false. -# UseGatewayTimeserver = false +# UseGatewaysAsTimeservers = false diff --git a/src/main_ivi.conf b/src/main_ivi.conf index c7e65c48..8ac0d589 100755 --- a/src/main_ivi.conf +++ b/src/main_ivi.conf @@ -111,4 +111,4 @@ NetworkCellularInterfaceList = pdp,rmnet,seth_td,seth_w # Allow connman to add service gateway to the time server list. # Default value is false. -# UseGatewayTimeserver = false +# UseGatewaysAsTimeservers = false diff --git a/src/main_tv.conf b/src/main_tv.conf index 44d1b025..7a72dbe1 100755 --- a/src/main_tv.conf +++ b/src/main_tv.conf @@ -111,7 +111,7 @@ NetworkCellularInterfaceList = pdp,rmnet,seth_td,seth_w # Allow connman to add service gateway to the time server list. # Default value is false. -# UseGatewayTimeserver = false +# UseGatewaysAsTimeservers = false # Enable Tizen TV Profile Features TizenTVExtension = true diff --git a/src/manager.c b/src/manager.c index bd44fea4..583b2ad1 100644 --- a/src/manager.c +++ b/src/manager.c @@ -111,20 +111,6 @@ static DBusMessage *set_property(DBusConnection *conn, dbus_message_iter_get_basic(&value, &offlinemode); - if (offlinemode) { - uid_t uid; - if (connman_dbus_get_connection_unix_user_sync(conn, - dbus_message_get_sender(msg), - &uid) < 0) { - DBG("Can not get unix user id!"); - return __connman_error_permission_denied(msg); - } - - if (!__connman_service_is_user_allowed(CONNMAN_SERVICE_TYPE_WIFI, uid)) { - DBG("Not allow this user to turn on offlinemode now!"); - return __connman_error_permission_denied(msg); - } - } __connman_technology_set_offlinemode(offlinemode); } else if (g_str_equal(name, "SessionMode")) { @@ -132,9 +118,8 @@ static DBusMessage *set_property(DBusConnection *conn, return __connman_error_invalid_arguments(msg); dbus_message_iter_get_basic(&value, &sessionmode); - } #if defined TIZEN_EXT - else if (g_str_equal(name, "AutoConnectMode") == TRUE) { + } else if (g_str_equal(name, "AutoConnectMode") == TRUE) { bool automode; if (type != DBUS_TYPE_BOOLEAN) @@ -143,9 +128,8 @@ static DBusMessage *set_property(DBusConnection *conn, dbus_message_iter_get_basic(&value, &automode); __connman_service_set_auto_connect_mode(automode); - } #endif - else + } else return __connman_error_invalid_property(msg); return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); @@ -206,7 +190,7 @@ static void idle_state(bool idle) return; } -static struct connman_notifier technology_notifier = { +static const struct connman_notifier technology_notifier = { .name = "manager", .priority = CONNMAN_NOTIFIER_PRIORITY_HIGH, .idle_state = idle_state, @@ -251,6 +235,27 @@ static DBusMessage *get_peers(DBusConnection *conn, return reply; } +static DBusMessage *get_tethering_clients(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + DBusMessage *reply; + DBusMessageIter iter, array; + + reply = dbus_message_new_method_return(msg); + if (!reply) + return NULL; + + dbus_message_iter_init_append(reply, &iter); + + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_STRING_AS_STRING, &array); + + __connman_tethering_list_clients(&array); + + dbus_message_iter_close_container(&iter, &array); + return reply; +} + static DBusMessage *connect_provider(DBusConnection *conn, DBusMessage *msg, void *data) { @@ -664,6 +669,9 @@ static const GDBusMethodTable manager_methods[] = { { GDBUS_METHOD("GetPeers", NULL, GDBUS_ARGS({ "peers", "a(oa{sv})" }), get_peers) }, + { GDBUS_METHOD("GetTetheringClients", + NULL, GDBUS_ARGS({ "tethering_clients", "as" }), + get_tethering_clients) }, { GDBUS_DEPRECATED_ASYNC_METHOD("ConnectProvider", GDBUS_ARGS({ "provider", "a{sv}" }), GDBUS_ARGS({ "path", "o" }), @@ -55,8 +55,10 @@ static int enable_ip_forward(bool enable) if (read(f, &value, sizeof(value)) < 0) value = 0; - if (lseek(f, 0, SEEK_SET) < 0) + if (lseek(f, 0, SEEK_SET) < 0) { + close(f); return -errno; + } } if (enable) { @@ -201,7 +203,7 @@ static void cleanup_nat(gpointer data) g_free(nat); } -static struct connman_notifier nat_notifier = { +static const struct connman_notifier nat_notifier = { .name = "nat", .default_changed = update_default_interface, }; diff --git a/src/network.c b/src/network.c index fc57dcc8..a6d635ec 100755 --- a/src/network.c +++ b/src/network.c @@ -27,6 +27,8 @@ #include <string.h> #include "connman.h" +#include <connman/acd.h> +#include "src/shared/arp.h" /* * How many times to send RS with the purpose of @@ -48,6 +50,8 @@ */ #define RTR_SOLICITATION_INTERVAL 4 +#define DHCP_RETRY_TIMEOUT 10 + static GSList *network_list = NULL; static GSList *driver_list = NULL; @@ -67,6 +71,9 @@ struct connman_network { int index; int router_solicit_count; int router_solicit_refresh_count; + struct acd_host *acd_host; + guint ipv4ll_timeout; + guint dhcp_timeout; struct connman_network_driver *driver; void *driver_data; @@ -97,6 +104,7 @@ struct connman_network { char *private_key_passphrase; char *phase2_auth; bool wps; + bool wps_advertizing; bool use_wps; char *pin_wps; #if defined TIZEN_EXT @@ -179,6 +187,264 @@ static void set_configuration(struct connman_network *network, type); } +void connman_network_append_acddbus(DBusMessageIter *dict, + struct connman_network *network) +{ + if (!network->acd_host) + return; + + acd_host_append_dbus_property(network->acd_host, dict); +} + +static int start_acd(struct connman_network *network); + +static void remove_ipv4ll_timeout(struct connman_network *network) +{ + if (network->ipv4ll_timeout > 0) { + g_source_remove(network->ipv4ll_timeout); + network->ipv4ll_timeout = 0; + } +} + +static void acd_host_ipv4_available(struct acd_host *acd, gpointer user_data) +{ + struct connman_network *network = user_data; + struct connman_service *service; + struct connman_ipconfig *ipconfig_ipv4; + int err; + + if (!network) + return; + + service = connman_service_lookup_from_network(network); + if (!service) + return; + + ipconfig_ipv4 = __connman_service_get_ip4config(service); + if (!ipconfig_ipv4) { + connman_error("Service has no IPv4 configuration"); + return; + } + + err = __connman_ipconfig_address_add(ipconfig_ipv4); + if (err < 0) + goto err; + +#if defined TIZEN_EXT + err = __connman_ipconfig_gateway_add(ipconfig_ipv4, service); +#else + err = __connman_ipconfig_gateway_add(ipconfig_ipv4); +#endif + if (err < 0) + goto err; + + __connman_service_save(service); + + return; + +err: + connman_network_set_error(__connman_service_get_network(service), + CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL); +} + +static int start_ipv4ll(struct connman_network *network) +{ + struct connman_service *service; + struct connman_ipconfig *ipconfig_ipv4; + struct in_addr addr; + char *address; + + service = connman_service_lookup_from_network(network); + if (!service) + return -EINVAL; + + ipconfig_ipv4 = __connman_service_get_ip4config(service); + if (!ipconfig_ipv4) { + connman_error("Service has no IPv4 configuration"); + return -EINVAL; + } + + /* Apply random IPv4 address. */ + addr.s_addr = htonl(arp_random_ip()); + address = inet_ntoa(addr); + if (!address) { + connman_error("Could not convert IPv4LL random address %u", + addr.s_addr); + return -EINVAL; + } + __connman_ipconfig_set_local(ipconfig_ipv4, address); + + connman_info("Probing IPv4LL address %s", address); + return start_acd(network); +} + +static gboolean start_ipv4ll_ontimeout(gpointer data) +{ + struct connman_network *network = data; + + if (!network) + return FALSE; + + /* Start IPv4LL ACD. */ + start_ipv4ll(network); + + return FALSE; +} + +static void acd_host_ipv4_lost(struct acd_host *acd, gpointer user_data) +{ + struct connman_network *network = user_data; + struct connman_service *service; + struct connman_ipconfig *ipconfig_ipv4; + enum connman_ipconfig_type type; + enum connman_ipconfig_method method; + + if (!network) + return; + + service = connman_service_lookup_from_network(network); + if (!service) + return; + + ipconfig_ipv4 = __connman_service_get_ip4config(service); + if (!ipconfig_ipv4) { + connman_error("Service has no IPv4 configuration"); + return; + } + + type = __connman_ipconfig_get_config_type(ipconfig_ipv4); + if (type != CONNMAN_IPCONFIG_TYPE_IPV4) + return; + + __connman_ipconfig_address_remove(ipconfig_ipv4); + + method = __connman_ipconfig_get_method(ipconfig_ipv4); + if (method == CONNMAN_IPCONFIG_METHOD_DHCP) { + /* + * We have one more chance for DHCP. If this fails + * acd_host_ipv4_conflict will be called. + */ + network = __connman_service_get_network(service); + if (network) + __connman_network_enable_ipconfig(network, ipconfig_ipv4); + } else { + /* Start IPv4LL ACD. */ + start_ipv4ll(network); + } +} + +static void acd_host_ipv4_conflict(struct acd_host *acd, gpointer user_data) +{ + struct connman_network *network = user_data; + struct connman_service *service; + struct connman_ipconfig *ipconfig_ipv4; + enum connman_ipconfig_method method; + + service = connman_service_lookup_from_network(network); + if (!service) + return; + + ipconfig_ipv4 = __connman_service_get_ip4config(service); + if (!ipconfig_ipv4) { + connman_error("Service has no IPv4 configuration"); + return; + } + + method = __connman_ipconfig_get_method(ipconfig_ipv4); + connman_info("%s conflict counts=%u", __FUNCTION__, + acd_host_get_conflicts_count(acd)); + + if (method == CONNMAN_IPCONFIG_METHOD_DHCP && + acd_host_get_conflicts_count(acd) < 2) { + connman_info("%s Sending DHCP decline", __FUNCTION__); + __connman_dhcp_decline(ipconfig_ipv4); + + connman_network_set_connected_dhcp_later(network, DHCP_RETRY_TIMEOUT); + __connman_ipconfig_set_local(ipconfig_ipv4, NULL); + } else { + if (method == CONNMAN_IPCONFIG_METHOD_DHCP) { + __connman_ipconfig_set_method(ipconfig_ipv4, + CONNMAN_IPCONFIG_METHOD_AUTO); + __connman_dhcp_decline(ipconfig_ipv4); + } + /* Start IPv4LL ACD. */ + start_ipv4ll(network); + } +} + +static void acd_host_ipv4_maxconflict(struct acd_host *acd, gpointer user_data) +{ + struct connman_network *network = user_data; + + remove_ipv4ll_timeout(network); + connman_info("Had maximum number of conflicts. Next IPv4LL address will be " + "tried in %d seconds", RATE_LIMIT_INTERVAL); + /* Wait, then start IPv4LL ACD. */ + network->ipv4ll_timeout = + g_timeout_add_seconds_full(G_PRIORITY_HIGH, + RATE_LIMIT_INTERVAL, + start_ipv4ll_ontimeout, + network, + NULL); +} + +static int start_acd(struct connman_network *network) +{ + struct connman_service *service; + struct connman_ipconfig *ipconfig_ipv4; + const char* address; + struct in_addr addr; + + remove_ipv4ll_timeout(network); + + service = connman_service_lookup_from_network(network); + if (!service) + return -EINVAL; + + ipconfig_ipv4 = __connman_service_get_ip4config(service); + if (!ipconfig_ipv4) { + connman_error("Service has no IPv4 configuration"); + return -EINVAL; + } + + if (!network->acd_host) { + int index; + + index = __connman_ipconfig_get_index(ipconfig_ipv4); + network->acd_host = acd_host_new(index, + connman_service_get_dbuspath(service)); + if (!network->acd_host) { + connman_error("Could not create ACD data structure"); + return -EINVAL; + } + + acd_host_register_event(network->acd_host, + ACD_HOST_EVENT_IPV4_AVAILABLE, + acd_host_ipv4_available, network); + acd_host_register_event(network->acd_host, + ACD_HOST_EVENT_IPV4_LOST, + acd_host_ipv4_lost, network); + acd_host_register_event(network->acd_host, + ACD_HOST_EVENT_IPV4_CONFLICT, + acd_host_ipv4_conflict, network); + acd_host_register_event(network->acd_host, + ACD_HOST_EVENT_IPV4_MAXCONFLICT, + acd_host_ipv4_maxconflict, network); + } + + address = __connman_ipconfig_get_local(ipconfig_ipv4); + if (!address) + return -EINVAL; + + connman_info("Starting ACD for address %s", address); + if (inet_pton(AF_INET, address, &addr) != 1) + connman_error("Could not convert address %s", address); + + acd_host_start(network->acd_host, htonl(addr.s_addr)); + + return 0; +} + static void dhcp_success(struct connman_network *network) { struct connman_service *service; @@ -196,6 +462,14 @@ static void dhcp_success(struct connman_network *network) if (!ipconfig_ipv4) return; + if (connman_setting_get_bool("AddressConflictDetection")) { + err = start_acd(network); + if (!err) + return; + + /* On error proceed without ACD. */ + } + err = __connman_ipconfig_address_add(ipconfig_ipv4); if (err < 0) goto err; @@ -266,6 +540,14 @@ static int set_connected_manual(struct connman_network *network) if (!__connman_ipconfig_get_local(ipconfig)) __connman_service_read_ip4config(service); + if (connman_setting_get_bool("AddressConflictDetection")) { + err = start_acd(network); + if (!err) + return 0; + + /* On error proceed without ACD. */ + } + err = __connman_ipconfig_address_add(ipconfig); if (err < 0) goto err; @@ -282,6 +564,14 @@ err: return err; } +static void remove_dhcp_timeout(struct connman_network *network) +{ + if (network->dhcp_timeout > 0) { + g_source_remove(network->dhcp_timeout); + network->dhcp_timeout = 0; + } +} + static int set_connected_dhcp(struct connman_network *network) { struct connman_service *service; @@ -289,6 +579,7 @@ static int set_connected_dhcp(struct connman_network *network) int err; DBG("network %p", network); + remove_dhcp_timeout(network); service = connman_service_lookup_from_network(network); ipconfig_ipv4 = __connman_service_get_ip4config(service); @@ -304,6 +595,44 @@ static int set_connected_dhcp(struct connman_network *network) return 0; } +static gboolean set_connected_dhcp_timout(gpointer data) +{ + struct connman_network *network = data; + struct connman_service *service; + struct connman_ipconfig *ipconfig; + enum connman_ipconfig_method method; + + network->dhcp_timeout = 0; + + service = connman_service_lookup_from_network(network); + if (!service) + return FALSE; + + ipconfig = __connman_service_get_ip4config(service); + if (!ipconfig) + return FALSE; + + /* Method is still DHCP? */ + method = __connman_ipconfig_get_method(ipconfig); + if (method == CONNMAN_IPCONFIG_METHOD_DHCP) + set_connected_dhcp(network); + + return FALSE; +} + +void connman_network_set_connected_dhcp_later(struct connman_network *network, + uint32_t sec) +{ + remove_dhcp_timeout(network); + + network->dhcp_timeout = + g_timeout_add_seconds_full(G_PRIORITY_HIGH, + sec, + set_connected_dhcp_timout, + network, + NULL); +} + static int manual_ipv6_set(struct connman_network *network, struct connman_ipconfig *ipconfig_ipv6) { @@ -573,7 +902,6 @@ static void receive_refresh_rs_reply(struct nd_router_advert *reply, network->router_solicit_refresh_count = 0; connman_network_unref(network); - return; } int __connman_network_refresh_rs_ipv6(struct connman_network *network, @@ -730,6 +1058,7 @@ static void set_disconnected(struct connman_network *network) __connman_service_notify_ipv4_configuration(service); /* fall through */ case CONNMAN_IPCONFIG_METHOD_DHCP: + remove_dhcp_timeout(network); __connman_dhcp_stop(ipconfig_ipv4); break; } @@ -770,6 +1099,7 @@ static void set_disconnected(struct connman_network *network) __connman_ipconfig_address_unset(ipconfig_ipv4); __connman_ipconfig_address_unset(ipconfig_ipv6); + #if defined TIZEN_EXT } #endif @@ -982,6 +1312,7 @@ static void network_destruct(struct connman_network *network) g_free(network->wifi.private_key_passphrase); g_free(network->wifi.phase2_auth); g_free(network->wifi.pin_wps); + #if defined TIZEN_EXT g_slist_free_full(network->wifi.vsie_list, g_free); g_slist_free_full(network->wifi.bssid_list, g_free); @@ -991,6 +1322,7 @@ static void network_destruct(struct connman_network *network) g_free(network->node); g_free(network->name); g_free(network->identifier); + acd_host_free(network->acd_host); network->device = NULL; @@ -1026,9 +1358,13 @@ struct connman_network *connman_network_create(const char *identifier, network->type = type; network->identifier = ident; + network->acd_host = NULL; + network->ipv4ll_timeout = 0; network_list = g_slist_prepend(network_list, network); + network->dhcp_timeout = 0; + DBG("network %p identifier %s type %s", network, identifier, type2string(type)); return network; @@ -1559,7 +1895,6 @@ void connman_network_set_error(struct connman_network *network, case CONNMAN_NETWORK_ERROR_BLOCKED: set_blocked_error(network); break; - } __connman_network_disconnect(network); @@ -1697,17 +2032,17 @@ int __connman_network_connect(struct connman_network *network) if (!network->device) return -ENODEV; - network->connecting = true; - #if defined TIZEN_EXT if (network->type != CONNMAN_NETWORK_TYPE_CELLULAR) #endif __connman_device_disconnect(network->device); + + network->connecting = true; + #if defined TIZEN_EXT DBG("ConnMan, Connect Request [%s]", network->name); - struct connman_service *service = connman_service_lookup_from_network(network); - connman_service_set_disconnection_requested(service, false); #endif + err = network->driver->connect(network); if (err < 0) { if (err == -EINPROGRESS) { @@ -1738,6 +2073,10 @@ int __connman_network_disconnect(struct connman_network *network) DBG("network %p", network); + remove_ipv4ll_timeout(network); + if (network->acd_host) + acd_host_stop(network->acd_host); + if (!network->connected && !network->connecting && !network->associating) return -ENOTCONN; @@ -1746,6 +2085,7 @@ int __connman_network_disconnect(struct connman_network *network) return -EUNATCH; network->connecting = false; + #if defined TIZEN_EXT DBG("ConnMan, Disconnect request"); struct connman_service *service = connman_service_lookup_from_network(network); @@ -1781,13 +2121,16 @@ int __connman_network_clear_ipconfig(struct connman_network *network, case CONNMAN_IPCONFIG_METHOD_OFF: case CONNMAN_IPCONFIG_METHOD_FIXED: return -EINVAL; - case CONNMAN_IPCONFIG_METHOD_AUTO: - release_dhcpv6(network); - break; case CONNMAN_IPCONFIG_METHOD_MANUAL: __connman_ipconfig_address_remove(ipconfig); break; + case CONNMAN_IPCONFIG_METHOD_AUTO: + release_dhcpv6(network); + if (type == CONNMAN_IPCONFIG_TYPE_IPV6) + break; + /* fall through */ case CONNMAN_IPCONFIG_METHOD_DHCP: + remove_dhcp_timeout(network); __connman_dhcp_stop(ipconfig_ipv4); break; } @@ -2443,6 +2786,8 @@ int connman_network_set_bool(struct connman_network *network, network->roaming = value; else if (g_strcmp0(key, "WiFi.WPS") == 0) network->wifi.wps = value; + else if (g_strcmp0(key, "WiFi.WPSAdvertising") == 0) + network->wifi.wps_advertizing = value; else if (g_strcmp0(key, "WiFi.UseWPS") == 0) network->wifi.use_wps = value; #if defined TIZEN_EXT @@ -2469,6 +2814,8 @@ bool connman_network_get_bool(struct connman_network *network, return network->roaming; else if (g_str_equal(key, "WiFi.WPS")) return network->wifi.wps; + else if (g_str_equal(key, "WiFi.WPSAdvertising")) + return network->wifi.wps_advertizing; else if (g_str_equal(key, "WiFi.UseWPS")) return network->wifi.use_wps; #if defined TIZEN_EXT diff --git a/src/nostats.c b/src/nostats.c new file mode 100644 index 00000000..aedc2c06 --- /dev/null +++ b/src/nostats.c @@ -0,0 +1,60 @@ +/* + * + * Connection Manager + * + * Copyright (C) 2018 Chris Novakovic + * + * 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 + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> + +#include "connman.h" + +int __connman_stats_service_register(struct connman_service *service) +{ + return -ENOTSUP; +} + +void __connman_stats_service_unregister(struct connman_service *service) +{ +} + +int __connman_stats_update(struct connman_service *service, + bool roaming, + struct connman_stats_data *data) +{ + return 0; +} + +int __connman_stats_get(struct connman_service *service, + bool roaming, + struct connman_stats_data *data) +{ + return 0; +} + +int __connman_stats_init(void) +{ + return 0; +} + +void __connman_stats_cleanup(void) +{ +} diff --git a/src/notifier.c b/src/notifier.c index 7c3d0311..d1be47f6 100644 --- a/src/notifier.c +++ b/src/notifier.c @@ -50,11 +50,11 @@ static gint compare_priority(gconstpointer a, gconstpointer b) * * Returns: %0 on success */ -int connman_notifier_register(struct connman_notifier *notifier) +int connman_notifier_register(const struct connman_notifier *notifier) { DBG("notifier %p name %s", notifier, notifier->name); - notifier_list = g_slist_insert_sorted(notifier_list, notifier, + notifier_list = g_slist_insert_sorted(notifier_list, (void*)notifier, compare_priority); return 0; @@ -66,7 +66,7 @@ int connman_notifier_register(struct connman_notifier *notifier) * * Remove a previously registered notifier module */ -void connman_notifier_unregister(struct connman_notifier *notifier) +void connman_notifier_unregister(const struct connman_notifier *notifier) { DBG("notifier %p name %s", notifier, notifier->name); @@ -221,7 +221,7 @@ void __connman_notifier_default_changed(struct connman_service *service) GSList *list; for (list = notifier_list; list; list = list->next) { - struct connman_notifier *notifier = list->data; + const struct connman_notifier *notifier = list->data; if (notifier->default_changed) notifier->default_changed(service); @@ -234,7 +234,7 @@ void __connman_notifier_service_add(struct connman_service *service, GSList *list; for (list = notifier_list; list; list = list->next) { - struct connman_notifier *notifier = list->data; + const struct connman_notifier *notifier = list->data; if (notifier->service_add) notifier->service_add(service, name); @@ -257,7 +257,7 @@ void __connman_notifier_service_remove(struct connman_service *service) } for (list = notifier_list; list; list = list->next) { - struct connman_notifier *notifier = list->data; + const struct connman_notifier *notifier = list->data; if (notifier->service_remove) notifier->service_remove(service); @@ -269,7 +269,7 @@ void __connman_notifier_proxy_changed(struct connman_service *service) GSList *list; for (list = notifier_list; list; list = list->next) { - struct connman_notifier *notifier = list->data; + const struct connman_notifier *notifier = list->data; if (notifier->proxy_changed) notifier->proxy_changed(service); @@ -295,7 +295,7 @@ void __connman_notifier_offlinemode(bool enabled) state_changed(); for (list = notifier_list; list; list = list->next) { - struct connman_notifier *notifier = list->data; + const struct connman_notifier *notifier = list->data; if (notifier->offline_mode) notifier->offline_mode(enabled); @@ -309,7 +309,7 @@ static void notify_idle_state(bool idle) DBG("idle %d", idle); for (list = notifier_list; list; list = list->next) { - struct connman_notifier *notifier = list->data; + const struct connman_notifier *notifier = list->data; if (notifier->idle_state) notifier->idle_state(idle); @@ -324,7 +324,7 @@ void __connman_notifier_service_state_changed(struct connman_service *service, bool found; for (list = notifier_list; list; list = list->next) { - struct connman_notifier *notifier = list->data; + const struct connman_notifier *notifier = list->data; if (notifier->service_state_changed) notifier->service_state_changed(service, state); @@ -367,7 +367,7 @@ void __connman_notifier_ipconfig_changed(struct connman_service *service, GSList *list; for (list = notifier_list; list; list = list->next) { - struct connman_notifier *notifier = list->data; + const struct connman_notifier *notifier = list->data; if (notifier->ipconfig_changed) notifier->ipconfig_changed(service, ipconfig); @@ -23,7 +23,6 @@ #include <config.h> #endif -#define _GNU_SOURCE #include <errno.h> #include <fcntl.h> #include <unistd.h> @@ -118,43 +117,55 @@ struct ntp_msg { #define NTP_PRECISION_US -19 #define NTP_PRECISION_NS -29 -static guint channel_watch = 0; -static struct timespec mtx_time; -static int transmit_fd = 0; - -static char *timeserver = NULL; -static struct sockaddr_in6 timeserver_addr; -static gint poll_id = 0; -static gint timeout_id = 0; -static guint retries = 0; - -static void send_packet(int fd, struct sockaddr *server, uint32_t timeout); - -static void next_server(void) +struct ntp_data { + char *timeserver; + struct sockaddr_in6 timeserver_addr; + struct timespec mtx_time; + int transmit_fd; + gint timeout_id; + guint retries; + guint channel_watch; + gint poll_id; + uint32_t timeout; + __connman_ntp_cb_t cb; + void *user_data; +}; + +static struct ntp_data *ntp_data; + +static void free_ntp_data(struct ntp_data *nd) { - if (timeserver) { - g_free(timeserver); - timeserver = NULL; - } - - __connman_timeserver_sync_next(); + if (nd->poll_id) + g_source_remove(nd->poll_id); + if (nd->timeout_id) + g_source_remove(nd->timeout_id); + if (nd->channel_watch) + g_source_remove(nd->channel_watch); + if (nd->timeserver) + g_free(nd->timeserver); + g_free(nd); } +static void send_packet(struct ntp_data *nd, struct sockaddr *server, + uint32_t timeout); + static gboolean send_timeout(gpointer user_data) { - uint32_t timeout = GPOINTER_TO_UINT(user_data); + struct ntp_data *nd = user_data; - DBG("send timeout %u (retries %d)", timeout, retries); + DBG("send timeout %u (retries %d)", nd->timeout, nd->retries); - if (retries++ == NTP_SEND_RETRIES) - next_server(); + if (nd->retries++ == NTP_SEND_RETRIES) + nd->cb(false, nd->user_data); else - send_packet(transmit_fd, (struct sockaddr *)×erver_addr, timeout << 1); + send_packet(nd, (struct sockaddr *)&nd->timeserver_addr, + nd->timeout << 1); return FALSE; } -static void send_packet(int fd, struct sockaddr *server, uint32_t timeout) +static void send_packet(struct ntp_data *nd, struct sockaddr *server, + uint32_t timeout) { struct ntp_msg msg; struct timeval transmit_timeval; @@ -177,38 +188,28 @@ static void send_packet(int fd, struct sockaddr *server, uint32_t timeout) if (server->sa_family == AF_INET) { size = sizeof(struct sockaddr_in); - addr = (void *)&(((struct sockaddr_in *)×erver_addr)->sin_addr); + addr = (void *)&(((struct sockaddr_in *)&nd->timeserver_addr)->sin_addr); } else if (server->sa_family == AF_INET6) { size = sizeof(struct sockaddr_in6); - addr = (void *)×erver_addr.sin6_addr; + addr = (void *)&nd->timeserver_addr.sin6_addr; } else { - connman_error("Family is neither ipv4 nor ipv6"); + DBG("Family is neither ipv4 nor ipv6"); + nd->cb(false, nd->user_data); return; } gettimeofday(&transmit_timeval, NULL); - clock_gettime(CLOCK_MONOTONIC, &mtx_time); + clock_gettime(CLOCK_MONOTONIC, &nd->mtx_time); msg.xmttime.seconds = htonl(transmit_timeval.tv_sec + OFFSET_1900_1970); msg.xmttime.fraction = htonl(transmit_timeval.tv_usec * 1000); - len = sendto(fd, &msg, sizeof(msg), MSG_DONTWAIT, + len = sendto(nd->transmit_fd, &msg, sizeof(msg), MSG_DONTWAIT, server, size); - - if (len < 0) { - connman_error("Time request for server %s failed (%d/%s)", - inet_ntop(server->sa_family, addr, ipaddrstring, sizeof(ipaddrstring)), - errno, strerror(errno)); - - if (errno == ENETUNREACH) - __connman_timeserver_sync_next(); - - return; - } - - if (len != sizeof(msg)) { - connman_error("Broken time request for server %s", + if (len < 0 || len != sizeof(msg)) { + DBG("Time request for server %s failed", inet_ntop(server->sa_family, addr, ipaddrstring, sizeof(ipaddrstring))); + nd->cb(false, nd->user_data); return; } @@ -218,34 +219,35 @@ static void send_packet(int fd, struct sockaddr *server, uint32_t timeout) * trying another server. */ - timeout_id = g_timeout_add_seconds(timeout, send_timeout, - GUINT_TO_POINTER(timeout)); + nd->timeout = timeout; + nd->timeout_id = g_timeout_add_seconds(timeout, send_timeout, nd); } static gboolean next_poll(gpointer user_data) { - poll_id = 0; + struct ntp_data *nd = user_data; + nd->poll_id = 0; - if (!timeserver || transmit_fd == 0) + if (!nd->timeserver || nd->transmit_fd == 0) return FALSE; - send_packet(transmit_fd, (struct sockaddr *)×erver_addr, NTP_SEND_TIMEOUT); + send_packet(nd, (struct sockaddr *)&nd->timeserver_addr, NTP_SEND_TIMEOUT); return FALSE; } -static void reset_timeout(void) +static void reset_timeout(struct ntp_data *nd) { - if (timeout_id > 0) { - g_source_remove(timeout_id); - timeout_id = 0; + if (nd->timeout_id > 0) { + g_source_remove(nd->timeout_id); + nd->timeout_id = 0; } - retries = 0; + nd->retries = 0; } -static void decode_msg(void *base, size_t len, struct timeval *tv, - struct timespec *mrx_time) +static void decode_msg(struct ntp_data *nd, void *base, size_t len, + struct timeval *tv, struct timespec *mrx_time) { struct ntp_msg *msg = base; double m_delta, org, rec, xmt, dst; @@ -254,6 +256,7 @@ static void decode_msg(void *base, size_t len, struct timeval *tv, #if !defined TIZEN_EXT struct timex tmx = {}; #endif + if (len < sizeof(*msg)) { connman_error("Invalid response from time server"); return; @@ -281,9 +284,9 @@ static void decode_msg(void *base, size_t len, struct timeval *tv, uint32_t code = ntohl(msg->refid); connman_info("Skipping server %s KoD code %c%c%c%c", - timeserver, code >> 24, code >> 16 & 0xff, + nd->timeserver, code >> 24, code >> 16 & 0xff, code >> 8 & 0xff, code & 0xff); - next_server(); + nd->cb(false, nd->user_data); return; } @@ -291,6 +294,7 @@ static void decode_msg(void *base, size_t len, struct timeval *tv, if (NTP_FLAGS_LI_DECODE(msg->flags) == NTP_FLAG_LI_NOTINSYNC) { DBG("ignoring unsynchronized peer"); + nd->cb(false, nd->user_data); return; } @@ -301,17 +305,19 @@ static void decode_msg(void *base, size_t len, struct timeval *tv, NTP_FLAG_VN_VER4, NTP_FLAGS_VN_DECODE(msg->flags)); } else { DBG("unsupported version %d", NTP_FLAGS_VN_DECODE(msg->flags)); + nd->cb(false, nd->user_data); return; } } if (NTP_FLAGS_MD_DECODE(msg->flags) != NTP_FLAG_MD_SERVER) { DBG("unsupported mode %d", NTP_FLAGS_MD_DECODE(msg->flags)); + nd->cb(false, nd->user_data); return; } - m_delta = mrx_time->tv_sec - mtx_time.tv_sec + - 1.0e-9 * (mrx_time->tv_nsec - mtx_time.tv_nsec); + m_delta = mrx_time->tv_sec - nd->mtx_time.tv_sec + + 1.0e-9 * (mrx_time->tv_nsec - nd->mtx_time.tv_nsec); org = tv->tv_sec + (1.0e-6 * tv->tv_usec) - m_delta + OFFSET_1900_1970; rec = ntohl(msg->rectime.seconds) + @@ -329,18 +335,19 @@ static void decode_msg(void *base, size_t len, struct timeval *tv, /* Remove the timeout, as timeserver has responded */ - reset_timeout(); + reset_timeout(nd); /* * Now poll the server every transmit_delay seconds * for time correction. */ - if (poll_id > 0) - g_source_remove(poll_id); + if (nd->poll_id > 0) + g_source_remove(nd->poll_id); - DBG("Timeserver %s, next sync in %d seconds", timeserver, transmit_delay); + DBG("Timeserver %s, next sync in %d seconds", nd->timeserver, + transmit_delay); - poll_id = g_timeout_add_seconds(transmit_delay, next_poll, NULL); + nd->poll_id = g_timeout_add_seconds(transmit_delay, next_poll, nd); #if defined TIZEN_EXT //send the dbus message to alram-manager @@ -435,18 +442,22 @@ static void decode_msg(void *base, size_t len, struct timeval *tv, tmx.status |= STA_DEL; if (adjtimex(&tmx) < 0) { - connman_error("Failed to adjust time"); + connman_error("Failed to adjust time: %s (%d)", strerror(errno), errno); + nd->cb(false, nd->user_data); return; } DBG("interval/delta/delay/drift %fs/%+.3fs/%.3fs/%+ldppm", - LOGTOD(msg->poll), offset, delay, tmx.freq / 65536); + LOGTOD(msg->poll), offset, delay, tmx.freq / 65536); + + nd->cb(true, nd->user_data); #endif } static gboolean received_data(GIOChannel *channel, GIOCondition condition, gpointer user_data) { + struct ntp_data *nd = user_data; unsigned char buf[128]; struct sockaddr_in6 sender_addr; struct msghdr msg; @@ -463,7 +474,7 @@ static gboolean received_data(GIOChannel *channel, GIOCondition condition, if (condition & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) { connman_error("Problem with timer server channel"); - channel_watch = 0; + nd->channel_watch = 0; return FALSE; } @@ -486,11 +497,11 @@ static gboolean received_data(GIOChannel *channel, GIOCondition condition, if (sender_addr.sin6_family == AF_INET) { size = 4; - addr_ptr = &((struct sockaddr_in *)×erver_addr)->sin_addr; + addr_ptr = &((struct sockaddr_in *)&nd->timeserver_addr)->sin_addr; src_ptr = &((struct sockaddr_in *)&sender_addr)->sin_addr; } else if (sender_addr.sin6_family == AF_INET6) { size = 16; - addr_ptr = &((struct sockaddr_in6 *)×erver_addr)->sin6_addr; + addr_ptr = &((struct sockaddr_in6 *)&nd->timeserver_addr)->sin6_addr; src_ptr = &((struct sockaddr_in6 *)&sender_addr)->sin6_addr; } else { connman_error("Not a valid family type"); @@ -514,12 +525,12 @@ static gboolean received_data(GIOChannel *channel, GIOCondition condition, } } - decode_msg(iov.iov_base, iov.iov_len, tv, &mrx_time); + decode_msg(nd, iov.iov_base, iov.iov_len, tv, &mrx_time); return TRUE; } -static void start_ntp(char *server) +static void start_ntp(struct ntp_data *nd) { GIOChannel *channel; struct addrinfo hint; @@ -532,14 +543,11 @@ static void start_ntp(char *server) int tos = IPTOS_LOWDELAY, timestamp = 1; int ret; - if (!server) - return; - memset(&hint, 0, sizeof(hint)); hint.ai_family = AF_UNSPEC; hint.ai_socktype = SOCK_DGRAM; hint.ai_flags = AI_NUMERICHOST | AI_PASSIVE; - ret = getaddrinfo(server, NULL, &hint, &info); + ret = getaddrinfo(nd->timeserver, NULL, &hint, &info); if (ret) { connman_error("cannot get server info"); @@ -548,18 +556,18 @@ static void start_ntp(char *server) family = info->ai_family; - memcpy(×erver_addr, info->ai_addr, info->ai_addrlen); + memcpy(&ntp_data->timeserver_addr, info->ai_addr, info->ai_addrlen); freeaddrinfo(info); memset(&in6addr, 0, sizeof(in6addr)); if (family == AF_INET) { - ((struct sockaddr_in *)×erver_addr)->sin_port = htons(123); + ((struct sockaddr_in *)&ntp_data->timeserver_addr)->sin_port = htons(123); in4addr = (struct sockaddr_in *)&in6addr; in4addr->sin_family = family; addr = (struct sockaddr *)in4addr; size = sizeof(struct sockaddr_in); } else if (family == AF_INET6) { - timeserver_addr.sin6_port = htons(123); + ntp_data->timeserver_addr.sin6_port = htons(123); in6addr.sin6_family = family; addr = (struct sockaddr *)&in6addr; size = sizeof(in6addr); @@ -568,96 +576,90 @@ static void start_ntp(char *server) return; } - DBG("server %s family %d", server, family); + DBG("server %s family %d", nd->timeserver, family); - if (channel_watch > 0) + if (nd->channel_watch > 0) goto send; - transmit_fd = socket(family, SOCK_DGRAM | SOCK_CLOEXEC, 0); + nd->transmit_fd = socket(family, SOCK_DGRAM | SOCK_CLOEXEC, 0); - if (transmit_fd <= 0) { - connman_error("Failed to open time server socket"); - return; + if (nd->transmit_fd <= 0) { + if (errno != EAFNOSUPPORT) + connman_error("Failed to open time server socket"); } - if (bind(transmit_fd, (struct sockaddr *) addr, size) < 0) { + if (bind(nd->transmit_fd, (struct sockaddr *) addr, size) < 0) { connman_error("Failed to bind time server socket"); - close(transmit_fd); - return; + goto err; } if (family == AF_INET) { - if (setsockopt(transmit_fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) { + if (setsockopt(nd->transmit_fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) { connman_error("Failed to set type of service option"); - close(transmit_fd); - return; + goto err; } } - if (setsockopt(transmit_fd, SOL_SOCKET, SO_TIMESTAMP, ×tamp, + if (setsockopt(nd->transmit_fd, SOL_SOCKET, SO_TIMESTAMP, ×tamp, sizeof(timestamp)) < 0) { connman_error("Failed to enable timestamp support"); - close(transmit_fd); - return; + goto err; } - channel = g_io_channel_unix_new(transmit_fd); - if (!channel) { - close(transmit_fd); - return; - } + channel = g_io_channel_unix_new(nd->transmit_fd); + if (!channel) + goto err; g_io_channel_set_encoding(channel, NULL, NULL); g_io_channel_set_buffered(channel, FALSE); g_io_channel_set_close_on_unref(channel, TRUE); - channel_watch = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, + nd->channel_watch = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - received_data, NULL, NULL); + received_data, nd, NULL); g_io_channel_unref(channel); send: - send_packet(transmit_fd, (struct sockaddr*)×erver_addr, NTP_SEND_TIMEOUT); + send_packet(nd, (struct sockaddr*)&ntp_data->timeserver_addr, + NTP_SEND_TIMEOUT); + return; + +err: + if (nd->transmit_fd > 0) + close(nd->transmit_fd); + + nd->cb(false, nd->user_data); } -int __connman_ntp_start(char *server) +int __connman_ntp_start(char *server, __connman_ntp_cb_t callback, + void *user_data) { - DBG("%s", server); - if (!server) return -EINVAL; - if (timeserver) - g_free(timeserver); + if (ntp_data) { + connman_warn("ntp_data is not NULL (timerserver %s)", + ntp_data->timeserver); + free_ntp_data(ntp_data); + } + + ntp_data = g_new0(struct ntp_data, 1); - timeserver = g_strdup(server); + ntp_data->timeserver = g_strdup(server); + ntp_data->cb = callback; + ntp_data->user_data = user_data; - start_ntp(timeserver); + start_ntp(ntp_data); return 0; } void __connman_ntp_stop() { - DBG(""); - - if (poll_id > 0) { - g_source_remove(poll_id); - poll_id = 0; - } - - reset_timeout(); - - if (channel_watch > 0) { - g_source_remove(channel_watch); - channel_watch = 0; - transmit_fd = 0; - } - - if (timeserver) { - g_free(timeserver); - timeserver = NULL; + if (ntp_data) { + free_ntp_data(ntp_data); + ntp_data = NULL; } } @@ -82,7 +82,7 @@ static void stop_dhcp_server(struct connman_peer *peer) peer->dhcp_server = NULL; if (peer->ip_pool) - __connman_ippool_unref(peer->ip_pool); + __connman_ippool_free(peer->ip_pool); peer->ip_pool = NULL; peer->lease_ip = 0; } @@ -606,6 +606,9 @@ static int peer_connect(struct connman_peer *peer) { int err = -ENOTSUP; + if (is_connected(peer)) + return -EISCONN; + if (peer_driver->connect) err = peer_driver->connect(peer, CONNMAN_PEER_WPS_UNKNOWN, NULL); @@ -1177,6 +1180,18 @@ const char *__connman_peer_get_path(struct connman_peer *peer) return peer->path; } +static void disconnect_peer_hash_table(gpointer key, + gpointer value, gpointer user_data) +{ + struct connman_peer *peer = value; + peer_disconnect(peer); +} + +void __connman_peer_disconnect_all(void) +{ + g_hash_table_foreach(peers_table, disconnect_peer_hash_table, NULL); +} + int __connman_peer_init(void) { DBG(""); diff --git a/src/provider.c b/src/provider.c index c0d69e49..f1e4a067 100755 --- a/src/provider.c +++ b/src/provider.c @@ -742,7 +742,7 @@ static void provider_service_changed(struct connman_service *service, vpn_index = __connman_connection_get_vpn_index(service_index); DBG("service %p %s state %d index %d/%d", service, - __connman_service_get_ident(service), + connman_service_get_identifier(service), state, service_index, vpn_index); if (vpn_index < 0) @@ -755,11 +755,9 @@ static void provider_service_changed(struct connman_service *service, DBG("disconnect %p index %d", provider, vpn_index); connman_provider_disconnect(provider); - - return; } -static struct connman_notifier provider_notifier = { +static const struct connman_notifier provider_notifier = { .name = "provider", .offline_mode = provider_offline_mode, .service_state_changed = provider_service_changed, diff --git a/src/resolver.c b/src/resolver.c index d6c20cdd..7ec2150b 100755 --- a/src/resolver.c +++ b/src/resolver.c @@ -23,7 +23,6 @@ #include <config.h> #endif -#define _GNU_SOURCE #include <stdio.h> #include <errno.h> #include <fcntl.h> @@ -35,6 +34,19 @@ #include "connman.h" +/* + * Just to avoid build failure due to missing STATEDIR + */ +#if defined TIZEN_EXT +#ifdef STATEDIR +#undef STATEDIR +#endif +#define STATEDIR "/etc" +#endif + +#define RESOLV_CONF_STATEDIR STATEDIR"/resolv.conf" +#define RESOLV_CONF_ETC "/etc/resolv.conf" + #define RESOLVER_FLAG_PUBLIC (1 << 0) /* @@ -130,11 +142,19 @@ static int resolvfile_export(void) old_umask = umask(022); - fd = open("/etc/resolv.conf", O_RDWR | O_CREAT | O_CLOEXEC, + fd = open(RESOLV_CONF_STATEDIR, O_RDWR | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (fd < 0) { - err = -errno; - goto done; + connman_warn_once("Cannot create "RESOLV_CONF_STATEDIR" " + "falling back to "RESOLV_CONF_ETC); + + fd = open(RESOLV_CONF_ETC, O_RDWR | O_CREAT | O_CLOEXEC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + if (fd < 0) { + err = -errno; + goto done; + } } if (ftruncate(fd, 0) < 0) { @@ -658,6 +678,14 @@ static void free_resolvfile(gpointer data) g_free(entry); } +int __connman_resolver_set_mdns(int index, bool enabled) +{ + if (!dnsproxy_enabled) + return -ENOTSUP; + + return __connman_dnsproxy_set_mdns(index, enabled); +} + int __connman_resolver_init(gboolean dnsproxy) { int i; diff --git a/src/rfkill.c b/src/rfkill.c index fce9d720..99b337d2 100755 --- a/src/rfkill.c +++ b/src/rfkill.c @@ -23,7 +23,6 @@ #include <config.h> #endif -#define _GNU_SOURCE #include <stdio.h> #include <errno.h> #include <fcntl.h> @@ -167,6 +167,7 @@ static void read_uevent(struct interface_data *interface) if (ether_blacklisted(name)) { interface->service_type = CONNMAN_SERVICE_TYPE_UNKNOWN; interface->device_type = CONNMAN_DEVICE_TYPE_UNKNOWN; + goto out; } else { interface->service_type = CONNMAN_SERVICE_TYPE_ETHERNET; interface->device_type = CONNMAN_DEVICE_TYPE_ETHERNET; @@ -1381,10 +1382,10 @@ static void rtnl_newnduseropt(struct nlmsghdr *hdr) DBG("service: %p\n",service); #endif servers = rtnl_nd_opt_rdnss(opt, &lifetime, - &nr_servers); + &nr_servers); for (i = 0; i < nr_servers; i++) { if (!inet_ntop(AF_INET6, servers + i, buf, - sizeof(buf))) + sizeof(buf))) continue; #if defined TIZEN_EXT @@ -1396,7 +1397,7 @@ static void rtnl_newnduseropt(struct nlmsghdr *hdr) CONNMAN_IPCONFIG_TYPE_IPV6); #endif connman_resolver_append_lifetime(index, - NULL, buf, lifetime); + NULL, buf, lifetime); } } else if (opt->nd_opt_type == 31) { /* ND_OPT_DNSSL */ @@ -1531,7 +1532,7 @@ static void rtnl_message(void *buf, size_t len) if (!NLMSG_OK(hdr, len)) break; - DBG("%s len %d type %d flags 0x%04x seq %d pid %d", + DBG("%s len %u type %u flags 0x%04x seq %u pid %u", type2string(hdr->nlmsg_type), hdr->nlmsg_len, hdr->nlmsg_type, hdr->nlmsg_flags, hdr->nlmsg_seq, diff --git a/src/service.c b/src/service.c index 47c828d8..0c6e6235 100644 --- a/src/service.c +++ b/src/service.c @@ -30,8 +30,6 @@ #include <gdbus.h> #include <ctype.h> #include <stdint.h> -#include <pwd.h> -#include <utmpx.h> #include <connman/storage.h> #include <connman/setting.h> @@ -41,9 +39,6 @@ #define CONNECT_TIMEOUT 120 -#define USER_ROOT 0 -#define USER_NONE (uid_t)-1 - #if defined TIZEN_EXT #define WIFI_BSSID_STR_LEN 18 #endif @@ -53,8 +48,8 @@ static DBusConnection *connection = NULL; static GList *service_list = NULL; static GHashTable *service_hash = NULL; static GSList *counter_list = NULL; -static unsigned int autoconnect_timeout = 0; -static unsigned int vpn_autoconnect_timeout = 0; +static unsigned int autoconnect_id = 0; +static unsigned int vpn_autoconnect_id = 0; static struct connman_service *current_default = NULL; static bool services_dirty = false; @@ -76,11 +71,6 @@ struct connman_stats_counter { struct connman_stats stats_roaming; }; -struct connman_service_user { - uid_t favorite_user; - uid_t current_user; -}; - struct connman_service { int refcount; char *identifier; @@ -103,8 +93,6 @@ struct connman_service { char *name; char *passphrase; bool roaming; - bool request_passphrase_input; - struct connman_service_user user; struct connman_ipconfig *ipconfig_ipv4; struct connman_ipconfig *ipconfig_ipv6; struct connman_network *network; @@ -114,6 +102,8 @@ struct connman_service { char **nameservers_auto; int nameservers_timeout; char **domains; + bool mdns; + bool mdns_config; char *hostname; char *domainname; char **timeservers; @@ -144,7 +134,10 @@ struct connman_service { char **excludes; char *pac; bool wps; - int online_check_count; + bool wps_advertizing; + guint online_timeout; + int online_check_interval_ipv4; + int online_check_interval_ipv6; bool do_split_routing; bool new_service; bool hidden_service; @@ -497,89 +490,6 @@ static enum connman_dnsconfig_method __connman_dnsconfig_string2method( } #endif -static bool -connman_service_is_user_allowed(struct connman_service *service, uid_t uid) -{ - uid_t favorite_user = service->user.favorite_user; - uid_t current_user = uid; - - DBG("Service favorite UID: %d, current UID: %d", favorite_user, current_user); - if (favorite_user == USER_NONE || current_user == USER_ROOT) - return true; - - if (favorite_user != current_user || current_user == USER_NONE) { - DBG("Current user is not a favorite user to this service!"); - return false; - } - - return true; -} - -#if !defined TIZEN_EXT -static GList *connman_service_get_login_users() -{ - struct utmpx *utmp; - struct passwd *pwd; - GList *user_list = NULL; - - setutxent(); - - while ((utmp = getutxent()) != NULL) { - DBG("User Name: %s", utmp->ut_user); - - pwd = getpwnam(utmp->ut_user); - if (pwd) { - if (!g_list_find(user_list, GUINT_TO_POINTER(pwd->pw_uid))) - user_list = g_list_append(user_list, - GUINT_TO_POINTER(pwd->pw_uid)); - - DBG("User Name: %s, UID: %d", utmp->ut_user, pwd->pw_uid); - } - } - - endutxent(); - - return user_list; -} -#endif - -static bool is_service_owner_user_login(struct connman_service *service) -{ -#if defined TIZEN_EXT - return true; -#else - GList *list, *user_list; - bool ret = false; - - /* Here we only care about wifi service */ - if (service->type != CONNMAN_SERVICE_TYPE_WIFI) - return true; - - DBG("service favorite user id is: %d", service->user.favorite_user); - - user_list = connman_service_get_login_users(); - if (user_list == NULL) { - DBG("Can not get any logged in user info."); - return true; - } - - for (list = user_list; list; list = list->next) { - uid_t uid = GPOINTER_TO_UINT(list->data); - - DBG("login user id is %d", uid); - - if (service->user.favorite_user == uid) { - ret = true; - break; - } - } - - g_list_free(user_list); - - return ret; -#endif -} - static void set_split_routing(struct connman_service *service, bool value) { if (service->type != CONNMAN_SERVICE_TYPE_VPN) @@ -646,25 +556,6 @@ int __connman_service_load_modifiable(struct connman_service *service) return 0; } -static int service_load_passphrase(struct connman_service *service) -{ - GKeyFile *keyfile; - gchar *str; - - keyfile = connman_storage_load_service(service->identifier); - if (!keyfile) - return -EIO; - - str = g_key_file_get_string(keyfile, - service->identifier, "Passphrase", NULL); - if (str) - service->passphrase = str; - - g_key_file_free(keyfile); - - return 0; -} - static int service_load(struct connman_service *service) { GKeyFile *keyfile; @@ -862,6 +753,9 @@ static int service_load(struct connman_service *service) service->pac = str; } + service->mdns_config = g_key_file_get_boolean(keyfile, + service->identifier, "mDNS", NULL); + service->hidden_service = g_key_file_get_boolean(keyfile, service->identifier, "Hidden", NULL); @@ -919,9 +813,6 @@ static int service_load(struct connman_service *service) } #endif - if (g_key_file_has_key(keyfile, service->identifier, "UID", NULL)) - service->user.favorite_user = g_key_file_get_integer(keyfile, - service->identifier, "UID", NULL); done: g_key_file_free(keyfile); @@ -970,13 +861,6 @@ static int service_save(struct connman_service *service) const unsigned char *ssid; unsigned int ssid_len = 0; - if (service->user.favorite_user == USER_NONE) - g_key_file_remove_key(keyfile, service->identifier, - "UID", NULL); - else - g_key_file_set_integer(keyfile, service->identifier, - "UID", service->user.favorite_user); - ssid = connman_network_get_blob(service->network, "WiFi.SSID", &ssid_len); @@ -1032,14 +916,12 @@ static int service_save(struct connman_service *service) g_free(str); } - if (service->user.current_user == service->user.favorite_user) { - if (service->passphrase && strlen(service->passphrase) > 0) - g_key_file_set_string(keyfile, service->identifier, + if (service->passphrase && strlen(service->passphrase) > 0) + g_key_file_set_string(keyfile, service->identifier, "Passphrase", service->passphrase); - else - g_key_file_remove_key(keyfile, service->identifier, - "Passphrase", NULL); - } + else + g_key_file_remove_key(keyfile, service->identifier, + "Passphrase", NULL); if (service->ipconfig_ipv4) __connman_ipconfig_save(service->ipconfig_ipv4, keyfile, @@ -1133,6 +1015,13 @@ static int service_save(struct connman_service *service) g_key_file_remove_key(keyfile, service->identifier, "Proxy.URL", NULL); + if (service->mdns_config) + g_key_file_set_boolean(keyfile, service->identifier, + "mDNS", TRUE); + else + g_key_file_remove_key(keyfile, service->identifier, + "mDNS", NULL); + if (service->hidden_service) g_key_file_set_boolean(keyfile, service->identifier, "Hidden", TRUE); @@ -1646,6 +1535,7 @@ static int nameserver_remove_all(struct connman_service *service, return -ENXIO; while (service->nameservers_config && service->nameservers_config[i]) { + #if defined TIZEN_EXT DBG("type %d Remove service->nameservers_config[%d]: %s", type, i, service->nameservers_config[i]); @@ -1789,7 +1679,7 @@ int __connman_service_nameserver_append(struct connman_service *service, char **nameservers; int len, i; - DBG("service %p nameserver %s auto %d", service, nameserver, is_auto); + DBG("service %p nameserver %s auto %d", service, nameserver, is_auto); if (!nameserver) return -EINVAL; @@ -2050,7 +1940,7 @@ static void address_updated(struct connman_service *service, enum connman_ipconfig_type type) { if (is_connected(service->state) && - service == __connman_service_get_default()) { + service == connman_service_get_default()) { nameserver_remove_all(service, type); nameserver_add_all(service, type); @@ -2160,19 +2050,6 @@ static gboolean __connman_service_is_internet_profile( return FALSE; } -static gboolean __connman_service_is_tethering_profile( - struct connman_service *cellular) -{ - const char tethering_suffix[] = "_5"; - - DBG("Service path: %s", cellular->path); - - if (g_str_has_suffix(cellular->path, tethering_suffix) == TRUE) - return TRUE; - - return FALSE; -} - struct connman_service *connman_service_get_default_connection(void) { GList *list; @@ -2216,7 +2093,7 @@ struct connman_service *connman_service_get_default_connection(void) } #endif -struct connman_service *__connman_service_get_default(void) +struct connman_service *connman_service_get_default(void) { #if defined TIZEN_MAINTAIN_ONLINE return connman_service_get_default_connection(); @@ -2242,14 +2119,14 @@ bool __connman_service_index_is_default(int index) if (index < 0) return false; - service = __connman_service_get_default(); + service = connman_service_get_default(); return __connman_service_get_index(service) == index; } static void default_changed(void) { - struct connman_service *service = __connman_service_get_default(); + struct connman_service *service = connman_service_get_default(); if (service == current_default) return; @@ -2273,7 +2150,8 @@ static void default_changed(void) connman_setting_get_bool("AllowHostnameUpdates")) __connman_utsname_set_hostname(service->hostname); - if (service->domainname) + if (service->domainname && + connman_setting_get_bool("AllowDomainnameUpdates")) __connman_utsname_set_domainname(service->domainname); } @@ -2293,6 +2171,7 @@ static void state_changed(struct connman_service *service) #if !defined TIZEN_EXT if (!allow_property_changed(service)) return; + #endif #if defined TIZEN_EXT DBG(" %s, %s", str, service->path); @@ -2451,15 +2330,37 @@ static void append_security(DBusMessageIter *iter, void *user_data) dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str); break; +#if defined TIZEN_EXT + case CONNMAN_SERVICE_SECURITY_OWE: +#endif case CONNMAN_SERVICE_SECURITY_UNKNOWN: case CONNMAN_SERVICE_SECURITY_NONE: case CONNMAN_SERVICE_SECURITY_WEP: case CONNMAN_SERVICE_SECURITY_8021X: break; } + + if (service->wps_advertizing) { + str = "wps_advertising"; + dbus_message_iter_append_basic(iter, + DBUS_TYPE_STRING, &str); + } } } +static void security_changed(struct connman_service *service) +{ + if (!service->path) + return; + + if (!allow_property_changed(service)) + return; + + connman_dbus_property_changed_array(service->path, + CONNMAN_SERVICE_INTERFACE, "Security", + DBUS_TYPE_STRING, append_security, service); +} + static void append_ethernet(DBusMessageIter *iter, void *user_data) { struct connman_service *service = user_data; @@ -3039,6 +2940,48 @@ static void proxy_configuration_changed(struct connman_service *service) proxy_changed(service); } +static void mdns_changed(struct connman_service *service) +{ + dbus_bool_t mdns = service->mdns; + + if (!allow_property_changed(service)) + return; + + connman_dbus_property_changed_basic(service->path, + CONNMAN_SERVICE_INTERFACE, "mDNS", DBUS_TYPE_BOOLEAN, + &mdns); +} + +static void mdns_configuration_changed(struct connman_service *service) +{ + dbus_bool_t mdns_config = service->mdns_config; + + if (!allow_property_changed(service)) + return; + + connman_dbus_property_changed_basic(service->path, + CONNMAN_SERVICE_INTERFACE, "mDNS.Configuration", + DBUS_TYPE_BOOLEAN, &mdns_config); +} + +static int set_mdns(struct connman_service *service, + bool enabled) +{ + int result; + + result = __connman_resolver_set_mdns( + __connman_service_get_index(service), enabled); + + if (result == 0) { + if (service->mdns != enabled) { + service->mdns = enabled; + mdns_changed(service); + } + } + + return result; +} + static void timeservers_configuration_changed(struct connman_service *service) { if (!allow_property_changed(service)) @@ -3289,17 +3232,16 @@ void __connman_service_counter_unregister(const char *counter) counter_list = g_slist_remove(counter_list, counter); } -int __connman_service_iterate_services(service_iterate_cb cb, void *user_data) +int connman_service_iterate_services(connman_service_iterate_cb cb, + void *user_data) { GList *list; + int ret = 0; - for (list = service_list; list; list = list->next) { - struct connman_service *service = list->data; + for (list = service_list; list && ret == 0; list = list->next) + ret = cb((struct connman_service *)list->data, user_data); - cb(service, user_data); - } - - return 0; + return ret; } #if defined TIZEN_EXT @@ -3602,8 +3544,19 @@ static void append_properties(DBusMessageIter *dict, dbus_bool_t limited, connman_dbus_dict_append_dict(dict, "Proxy.Configuration", append_proxyconfig, service); + val = service->mdns; + connman_dbus_dict_append_basic(dict, "mDNS", DBUS_TYPE_BOOLEAN, + &val); + + val = service->mdns_config; + connman_dbus_dict_append_basic(dict, "mDNS.Configuration", + DBUS_TYPE_BOOLEAN, &val); + connman_dbus_dict_append_dict(dict, "Provider", append_provider, service); + + if (service->network) + connman_network_append_acddbus(dict, service->network); } static void append_struct_service(DBusMessageIter *iter, @@ -3701,7 +3654,10 @@ void __connman_service_set_hostname(struct connman_service *service, return; g_free(service->hostname); - service->hostname = g_strdup(hostname); + service->hostname = NULL; + + if (hostname && g_str_is_ascii(hostname)) + service->hostname = g_strdup(hostname); } const char *__connman_service_get_hostname(struct connman_service *service) @@ -3719,7 +3675,10 @@ void __connman_service_set_domainname(struct connman_service *service, return; g_free(service->domainname); - service->domainname = g_strdup(domainname); + service->domainname = NULL; + + if (domainname && g_str_is_ascii(domainname)) + service->domainname = g_strdup(domainname); domain_changed(service); } @@ -3735,6 +3694,14 @@ const char *connman_service_get_domainname(struct connman_service *service) return service->domainname; } +const char *connman_service_get_dbuspath(struct connman_service *service) +{ + if (!service) + return NULL; + + return service->path; +} + char **connman_service_get_nameservers(struct connman_service *service) { if (!service) @@ -4235,6 +4202,9 @@ int __connman_service_check_passphrase(enum connman_service_security security, break; case CONNMAN_SERVICE_SECURITY_8021X: +#if defined TIZEN_EXT + case CONNMAN_SERVICE_SECURITY_OWE: +#endif break; } @@ -4252,6 +4222,7 @@ int __connman_service_set_passphrase(struct connman_service *service, if (service->immutable && service->security != CONNMAN_SERVICE_SECURITY_8021X) return -EINVAL; + #if defined TIZEN_EXT /* The encrypted passphrase is used here * and validation is done by net-config before being encrypted. @@ -4561,7 +4532,7 @@ int __connman_service_reset_ipconfig(struct connman_service *service, *new_state = service->state_ipv6; settings_changed(service, new_ipconfig); - address_updated(service, new_method); + address_updated(service, type); __connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_AUTO); } @@ -4573,6 +4544,29 @@ int __connman_service_reset_ipconfig(struct connman_service *service, return err; } +/* + * We set the timeout to 1 sec so that we have a chance to get + * necessary IPv6 router advertisement messages that might have + * DNS data etc. + */ +#define ONLINE_CHECK_INITIAL_INTERVAL 1 +#define ONLINE_CHECK_MAX_INTERVAL 12 + +void __connman_service_wispr_start(struct connman_service *service, + enum connman_ipconfig_type type) +{ + DBG("service %p type %s", service, __connman_ipconfig_type2string(type)); + + if (type == CONNMAN_IPCONFIG_TYPE_IPV4) + service->online_check_interval_ipv4 = + ONLINE_CHECK_INITIAL_INTERVAL; + else + service->online_check_interval_ipv6 = + ONLINE_CHECK_INITIAL_INTERVAL; + + __connman_wispr_start(service, type); +} + static DBusMessage *set_property(DBusConnection *conn, DBusMessage *msg, void *user_data) { @@ -4589,21 +4583,6 @@ static DBusMessage *set_property(DBusConnection *conn, if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) return __connman_error_invalid_arguments(msg); - if (service->type == CONNMAN_SERVICE_TYPE_WIFI && is_connected(service->state)) { - uid_t uid; - if (connman_dbus_get_connection_unix_user_sync(conn, - dbus_message_get_sender(msg), - &uid) < 0) { - DBG("Can not get unix user id!"); - return __connman_error_permission_denied(msg); - } - - if (!connman_service_is_user_allowed(service, uid)) { - DBG("Not allow this user to operate this wifi service now!"); - return __connman_error_permission_denied(msg); - } - } - dbus_message_iter_get_basic(&iter, &name); dbus_message_iter_next(&iter); @@ -4665,6 +4644,7 @@ static DBusMessage *set_property(DBusConnection *conn, if (gw && strlen(gw)) __connman_service_nameserver_del_routes(service, CONNMAN_IPCONFIG_TYPE_ALL); + #endif dbus_message_iter_recurse(&value, &entry); @@ -4775,13 +4755,11 @@ static DBusMessage *set_property(DBusConnection *conn, if (__connman_service_is_connected_state(service, CONNMAN_IPCONFIG_TYPE_IPV4)) - __connman_wispr_start(service, - CONNMAN_IPCONFIG_TYPE_IPV4); + __connman_service_wispr_start(service, CONNMAN_IPCONFIG_TYPE_IPV4); if (__connman_service_is_connected_state(service, CONNMAN_IPCONFIG_TYPE_IPV6)) - __connman_wispr_start(service, - CONNMAN_IPCONFIG_TYPE_IPV6); + __connman_service_wispr_start(service, CONNMAN_IPCONFIG_TYPE_IPV6); service_save(service); } else if (g_str_equal(name, "Timeservers.Configuration")) { @@ -4821,15 +4799,14 @@ static DBusMessage *set_property(DBusConnection *conn, char **timeservers = g_strsplit_set(str->str, " ", 0); timeservers = remove_empty_strings(timeservers); service->timeservers_config = timeservers; - } else - service->timeservers = NULL; + } g_string_free(str, TRUE); service_save(service); timeservers_configuration_changed(service); - if (service == __connman_service_get_default()) + if (service == connman_service_get_default()) __connman_timeserver_sync(service); } else if (g_str_equal(name, "Domains.Configuration")) { @@ -4898,6 +4875,23 @@ static DBusMessage *set_property(DBusConnection *conn, __connman_notifier_proxy_changed(service); service_save(service); + } else if (g_str_equal(name, "mDNS.Configuration")) { + dbus_bool_t val; + + if (service->immutable) + return __connman_error_not_supported(msg); + + if (type != DBUS_TYPE_BOOLEAN) + return __connman_error_invalid_arguments(msg); + + dbus_message_iter_get_basic(&value, &val); + service->mdns_config = val; + + mdns_configuration_changed(service); + + set_mdns(service, service->mdns_config); + + service_save(service); } else if (g_str_equal(name, "IPv4.Configuration") || g_str_equal(name, "IPv6.Configuration")) { @@ -5036,14 +5030,6 @@ static void service_complete(struct connman_service *service) service_save(service); } -static void set_idle(struct connman_service *service) -{ - service->state = service->state_ipv4 = service->state_ipv6 = - CONNMAN_SERVICE_STATE_IDLE; - set_error(service, CONNMAN_SERVICE_ERROR_UNKNOWN); - state_changed(service); -} - static DBusMessage *clear_property(DBusConnection *conn, DBusMessage *msg, void *user_data) { @@ -5080,7 +5066,8 @@ static bool is_ignore(struct connman_service *service) if (!service->autoconnect) return true; - if (service->roaming) + if (service->roaming && + !connman_setting_get_bool("AutoConnectRoamingServices")) return true; if (service->ignore) @@ -5358,11 +5345,6 @@ static bool auto_connect_service(GList *services, continue; } - if (!is_service_owner_user_login(service)) { - DBG("favorite user not login, wifi auto connect denied"); - continue; - } - DBG("service %p %s %s", service, service->name, (preferred) ? "preferred" : reason2string(reason)); @@ -5383,7 +5365,7 @@ static gboolean run_auto_connect(gpointer data) bool autoconnecting = false; GList *preferred_tech; - autoconnect_timeout = 0; + autoconnect_id = 0; DBG(""); @@ -5419,7 +5401,7 @@ void __connman_service_auto_connect(enum connman_service_connect_reason reason) { DBG(""); - if (autoconnect_timeout != 0) + if (autoconnect_id != 0) return; #if defined TIZEN_EXT @@ -5445,9 +5427,9 @@ void __connman_service_auto_connect(enum connman_service_connect_reason reason) * FAILURE state due to this when connection with AP2 is cancelled * then autoconnect with AP1 doesn't works because its autoconnection * is ignored as its last state was FAILURE rather than IDLE */ - autoconnect_timeout = g_timeout_add(500, run_auto_connect, + autoconnect_id = g_timeout_add(500, run_auto_connect, #else - autoconnect_timeout = g_idle_add(run_auto_connect, + autoconnect_id = g_idle_add(run_auto_connect, #endif GUINT_TO_POINTER(reason)); } @@ -5456,7 +5438,7 @@ static gboolean run_vpn_auto_connect(gpointer data) { GList *list; bool need_split = false; - vpn_autoconnect_timeout = 0; + vpn_autoconnect_id = 0; for (list = service_list; list; list = list->next) { struct connman_service *service = list->data; @@ -5498,10 +5480,10 @@ static gboolean run_vpn_auto_connect(gpointer data) { static void vpn_auto_connect(void) { - if (vpn_autoconnect_timeout) + if (vpn_autoconnect_id) return; - vpn_autoconnect_timeout = + vpn_autoconnect_id = g_idle_add(run_vpn_auto_connect, NULL); } @@ -5527,7 +5509,6 @@ void __connman_service_set_provider_pending(struct connman_service *service, } service->provider_pending = msg; - return; } static void check_pending_msg(struct connman_service *service) @@ -5641,31 +5622,6 @@ static DBusMessage *connect_service(DBusConnection *conn, if (service->pending) return __connman_error_in_progress(msg); - if (service->type == CONNMAN_SERVICE_TYPE_WIFI) { - uid_t uid; - if (connman_dbus_get_connection_unix_user_sync(conn, - dbus_message_get_sender(msg), - &uid) < 0) { - DBG("Can not get unix user id!"); - return __connman_error_permission_denied(msg); - } - - if (!__connman_service_is_user_allowed(CONNMAN_SERVICE_TYPE_WIFI, uid)) { - DBG("Not allow this user to connect this wifi service now!"); - return __connman_error_permission_denied(msg); - } - - if (uid != USER_ROOT && uid != service->user.favorite_user) - service->request_passphrase_input = true; - - service->user.current_user = uid; - - if (!service->passphrase && uid == service->user.favorite_user) { - DBG("Now load this favorite user's passphrase."); - service_load_passphrase(service); - } - } - #if !defined TIZEN_EXT index = __connman_service_get_index(service); @@ -5701,18 +5657,10 @@ static DBusMessage *connect_service(DBusConnection *conn, err = __connman_service_connect(service, CONNMAN_SERVICE_CONNECT_REASON_USER); - if (err == -EINPROGRESS) - return NULL; - - if (service->pending) { - dbus_message_unref(service->pending); - service->pending = NULL; - } - - if (err < 0) - return __connman_error_failed(msg, -err); + if (err != -EINPROGRESS) + reply_pending(service, -err); - return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); + return NULL; } static DBusMessage *disconnect_service(DBusConnection *conn, @@ -5737,21 +5685,6 @@ static DBusMessage *disconnect_service(DBusConnection *conn, } #endif - if (service->type == CONNMAN_SERVICE_TYPE_WIFI) { - uid_t uid; - if (connman_dbus_get_connection_unix_user_sync(conn, - dbus_message_get_sender(msg), - &uid) < 0) { - DBG("Can not get unix user id!"); - return __connman_error_permission_denied(msg); - } - - if (!connman_service_is_user_allowed(service, uid)) { - DBG("Not allow this user to disconnect this wifi service now!"); - return __connman_error_permission_denied(msg); - } - } - service->ignore = true; err = __connman_service_disconnect(service); @@ -5853,15 +5786,8 @@ bool __connman_service_remove(struct connman_service *service) #endif -#if defined TIZEN_EXT - if (service->security != CONNMAN_SERVICE_SECURITY_8021X) -#endif - set_idle(service); - service->error = CONNMAN_SERVICE_ERROR_UNKNOWN; - service->user.favorite_user = USER_NONE; - __connman_service_set_favorite(service, false); __connman_ipconfig_ipv6_reset_privacy(service->ipconfig_ipv6); @@ -5891,23 +5817,6 @@ static DBusMessage *remove_service(DBusConnection *conn, DBG("service %p", service); - if (service->type == CONNMAN_SERVICE_TYPE_WIFI) { - uid_t uid; - if (connman_dbus_get_connection_unix_user_sync(conn, - dbus_message_get_sender(msg), - &uid) < 0) { - DBG("Can not get unix user id!"); - return __connman_error_permission_denied(msg); - } - -#if !defined TIZEN_EXT - if (!connman_service_is_user_allowed(service, uid)) { - DBG("Not allow this user to remove this wifi service now!"); - return __connman_error_permission_denied(msg); - } -#endif - } - if (!__connman_service_remove(service)) return __connman_error_not_supported(msg); @@ -5953,7 +5862,7 @@ static void apply_relevant_default_downgrade(struct connman_service *service) { struct connman_service *def_service; - def_service = __connman_service_get_default(); + def_service = connman_service_get_default(); if (!def_service) return; @@ -5986,6 +5895,89 @@ static void switch_default_service(struct connman_service *default_service, downgrade_state(downgrade_service); } +static struct _services_notify { + int id; + GHashTable *add; + GHashTable *remove; +} *services_notify; + + +static void service_append_added_foreach(gpointer data, gpointer user_data) +{ + struct connman_service *service = data; + DBusMessageIter *iter = user_data; + + if (!service || !service->path) { + DBG("service %p or path is NULL", service); + return; + } + + if (g_hash_table_lookup(services_notify->add, service->path)) { + DBG("new %s", service->path); + + append_struct(service, iter); + g_hash_table_remove(services_notify->add, service->path); + } else { + DBG("changed %s", service->path); + + append_struct_service(iter, NULL, service); + } +} + +static void service_append_ordered(DBusMessageIter *iter, void *user_data) +{ + g_list_foreach(service_list, service_append_added_foreach, iter); +} + +static void append_removed(gpointer key, gpointer value, gpointer user_data) +{ + char *objpath = key; + DBusMessageIter *iter = user_data; + + DBG("removed %s", objpath); + dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &objpath); +} + +static void service_append_removed(DBusMessageIter *iter, void *user_data) +{ + g_hash_table_foreach(services_notify->remove, append_removed, iter); +} + +static gboolean service_send_changed(gpointer data) +{ + DBusMessage *signal; + + DBG(""); + + services_notify->id = 0; + + signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH, + CONNMAN_MANAGER_INTERFACE, "ServicesChanged"); + if (!signal) + return FALSE; + + __connman_dbus_append_objpath_dict_array(signal, + service_append_ordered, NULL); + __connman_dbus_append_objpath_array(signal, + service_append_removed, NULL); + + dbus_connection_send(connection, signal, NULL); + dbus_message_unref(signal); + + g_hash_table_remove_all(services_notify->remove); + g_hash_table_remove_all(services_notify->add); + + return FALSE; +} + +static void service_schedule_changed(void) +{ + if (services_notify->id != 0) + return; + + services_notify->id = g_timeout_add(100, service_send_changed, NULL); +} + static DBusMessage *move_service(DBusConnection *conn, DBusMessage *msg, void *user_data, bool before) @@ -6092,6 +6084,8 @@ static DBusMessage *move_service(DBusConnection *conn, __connman_connection_update_gateway(); + service_schedule_changed(); + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } @@ -6117,30 +6111,6 @@ static DBusMessage *reset_counters(DBusConnection *conn, return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } -static DBusMessage *get_user_favorite(DBusConnection *conn, - DBusMessage *msg, void *user_data) -{ - DBusMessage *reply; - uid_t uid = USER_NONE; - dbus_bool_t user_favorite = false; - struct connman_service *service = user_data; - - connman_dbus_get_connection_unix_user_sync(conn, - dbus_message_get_sender(msg), - &uid); - if (uid == USER_ROOT) - user_favorite = service->favorite; - else if (uid != USER_NONE && uid == service->user.favorite_user) { - DBG("The service is favorite to this user!"); - user_favorite = true; - } - - reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID); - dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, - &user_favorite, DBUS_TYPE_INVALID); - return reply; -} - #if defined TIZEN_MAINTAIN_ONLINE static DBusMessage *downgrade_service(DBusConnection *conn, DBusMessage *msg, void *user_data) @@ -6154,94 +6124,6 @@ static DBusMessage *downgrade_service(DBusConnection *conn, } #endif -static struct _services_notify { - int id; - GHashTable *add; - GHashTable *remove; -} *services_notify; - -static void service_append_added_foreach(gpointer data, gpointer user_data) -{ - struct connman_service *service = data; - DBusMessageIter *iter = user_data; - - if (!service || !service->path) { -#if !defined TIZEN_EXT - DBG("service %p or path is NULL", service); -#endif - return; - } - - if (g_hash_table_lookup(services_notify->add, service->path)) { -#if !defined TIZEN_EXT - DBG("new %s", service->path); -#endif - - append_struct(service, iter); - g_hash_table_remove(services_notify->add, service->path); - } else { -#if !defined TIZEN_EXT - DBG("changed %s", service->path); -#endif - - append_struct_service(iter, NULL, service); - } -} - -static void service_append_ordered(DBusMessageIter *iter, void *user_data) -{ - g_list_foreach(service_list, service_append_added_foreach, iter); -} - -static void append_removed(gpointer key, gpointer value, gpointer user_data) -{ - char *objpath = key; - DBusMessageIter *iter = user_data; - - DBG("removed %s", objpath); - dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &objpath); -} - -static void service_append_removed(DBusMessageIter *iter, void *user_data) -{ - g_hash_table_foreach(services_notify->remove, append_removed, iter); -} - -static gboolean service_send_changed(gpointer data) -{ - DBusMessage *signal; - - DBG(""); - - services_notify->id = 0; - - signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH, - CONNMAN_MANAGER_INTERFACE, "ServicesChanged"); - if (!signal) - return FALSE; - - __connman_dbus_append_objpath_dict_array(signal, - service_append_ordered, NULL); - __connman_dbus_append_objpath_array(signal, - service_append_removed, NULL); - - dbus_connection_send(connection, signal, NULL); - dbus_message_unref(signal); - - g_hash_table_remove_all(services_notify->remove); - g_hash_table_remove_all(services_notify->add); - - return FALSE; -} - -static void service_schedule_changed(void) -{ - if (services_notify->id != 0) - return; - - services_notify->id = g_timeout_add(100, service_send_changed, NULL); -} - static void service_schedule_added(struct connman_service *service) { DBG("service %p", service); @@ -6303,9 +6185,6 @@ static const GDBusMethodTable service_methods[] = { GDBUS_ARGS({ "service", "o" }), NULL, move_after) }, { GDBUS_METHOD("ResetCounters", NULL, NULL, reset_counters) }, - { GDBUS_METHOD("GetUserFavorite", - NULL, GDBUS_ARGS({ "value", "v" }), - get_user_favorite) }, #if defined TIZEN_MAINTAIN_ONLINE { GDBUS_METHOD("Downgrade", NULL, NULL, downgrade_service) }, #endif @@ -6449,11 +6328,6 @@ static void service_initialize(struct connman_service *service) service->ignore = false; - service->user.favorite_user = USER_NONE; - service->user.current_user = USER_NONE; - - service->request_passphrase_input = false; - service->connect_reason = CONNMAN_SERVICE_CONNECT_REASON_NONE; service->order = 0; @@ -6463,6 +6337,7 @@ static void service_initialize(struct connman_service *service) service->provider = NULL; service->wps = false; + service->wps_advertizing = false; #if defined TIZEN_EXT service->disconnection_requested = false; service->storage_reload = false; @@ -6709,40 +6584,6 @@ char *connman_service_get_interface(struct connman_service *service) } /** - * __connman_service_is_user_allowed: - * @type: service type - * @uid: user id - * - * Check a user is allowed to operate a type of service - */ -bool __connman_service_is_user_allowed(enum connman_service_type type, - uid_t uid) -{ - GList *list; - uid_t owner_user = USER_NONE; - - for (list = service_list; list; list = list->next) { - struct connman_service *service = list->data; - - if (service->type != type) - continue; - - if (is_connected(service->state)) { - owner_user = service->user.favorite_user; - break; - } - } - - if (uid == USER_NONE || - (uid != USER_ROOT && - owner_user != USER_NONE && - owner_user != uid)) - return false; - - return true; -} - -/** * connman_service_get_network: * @service: service structure * @@ -6908,9 +6749,6 @@ int __connman_service_set_favorite_delayed(struct connman_service *service, service->favorite = favorite; - if (!delay_ordering) - __connman_service_get_order(service); - favorite_changed(service); if (!delay_ordering) { @@ -7032,6 +6870,14 @@ void __connman_service_set_search_domains(struct connman_service *service, searchdomain_add_all(service); } +int __connman_service_set_mdns(struct connman_service *service, + bool enabled) +{ + service->mdns_config = enabled; + + return set_mdns(service, enabled); +} + static void report_error_cb(void *user_context, bool retry, void *user_data) { @@ -7107,7 +6953,7 @@ static void request_input_cb(struct connman_service *service, if (g_strcmp0(error, "net.connman.Agent.Error.Canceled") == 0) { - err = -EINVAL; + err = -ECONNABORTED; if (service->hidden) __connman_service_return_error(service, @@ -7389,6 +7235,7 @@ static void single_connected_tech(struct connman_service *allowed) if (service == allowed) continue; #endif + services = g_slist_prepend(services, service); } @@ -7465,7 +7312,7 @@ static int service_indicate_state(struct connman_service *service) if (old_state == new_state) return -EALREADY; - def_service = __connman_service_get_default(); + def_service = connman_service_get_default(); if (new_state == CONNMAN_SERVICE_STATE_ONLINE) { result = service_update_preferred_order(def_service, @@ -7536,7 +7383,7 @@ static int service_indicate_state(struct connman_service *service) default_changed(); - def_service = __connman_service_get_default(); + def_service = connman_service_get_default(); service_update_preferred_order(def_service, service, new_state); @@ -7607,15 +7454,6 @@ static int service_indicate_state(struct connman_service *service) reply_pending(service, ECONNABORTED); - def_service = __connman_service_get_default(); - service->disconnect_reason = connman_network_get_disconnect_reason(service->network); - service->assoc_status_code = connman_network_get_assoc_status_code(service->network); - - if (!__connman_notifier_is_connected() && - def_service && - def_service->provider) - connman_provider_disconnect(def_service->provider); - default_changed(); __connman_wispr_stop(service); @@ -7624,9 +7462,9 @@ static int service_indicate_state(struct connman_service *service) #if defined TIZEN_EXT /** - * Skip the functions if there is any connected profiles - * that use same interface - */ + * Skip the functions if there is any connected profiles + * that use same interface + */ if (service->type != CONNMAN_SERVICE_TYPE_CELLULAR || __connman_service_get_connected_count_of_iface( service) <= 0) { @@ -7653,15 +7491,14 @@ static int service_indicate_state(struct connman_service *service) service->order = 5; __connman_service_auto_connect(CONNMAN_SERVICE_CONNECT_REASON_AUTO); #endif - if (service->connect_reason == CONNMAN_SERVICE_CONNECT_REASON_USER && + if (service->connect_reason == CONNMAN_SERVICE_CONNECT_REASON_USER) { connman_agent_report_error(service, service->path, - error2string(service->error), - report_error_cb, - get_dbus_sender(service), - NULL) == -EINPROGRESS) - return 0; + error2string(service->error), + report_error_cb, + get_dbus_sender(service), + NULL); + } service_complete(service); - break; } @@ -7689,20 +7526,6 @@ static int service_indicate_state(struct connman_service *service) default_changed(); } - if (service->type == CONNMAN_SERVICE_TYPE_WIFI && - service->connect_reason == CONNMAN_SERVICE_CONNECT_REASON_USER && - (new_state == CONNMAN_SERVICE_STATE_READY || - new_state == CONNMAN_SERVICE_STATE_ONLINE)) { - if (service->user.favorite_user != service->user.current_user) { - DBG("Now set service favorite user id from %d to %d", - service->user.favorite_user, service->user.current_user); - - service->user.favorite_user = service->user.current_user; - - service_save(service); - } - } - return 0; } @@ -7835,7 +7658,7 @@ static void check_proxy_setup(struct connman_service *service) return; done: - __connman_wispr_start(service, CONNMAN_IPCONFIG_TYPE_IPV4); + __connman_service_wispr_start(service, CONNMAN_IPCONFIG_TYPE_IPV4); } #if defined TIZEN_EXT @@ -7900,18 +7723,33 @@ static void service_rp_filter(struct connman_service *service, connected_networks_count, original_rp_filter); } -static gboolean redo_wispr(gpointer user_data) +static void redo_wispr(struct connman_service *service, + enum connman_ipconfig_type type) +{ + service->online_timeout = 0; + connman_service_unref(service); + + DBG("Retrying %s WISPr for %p %s", + __connman_ipconfig_type2string(type), + service, service->name); + + __connman_wispr_start(service, type); +} + +static gboolean redo_wispr_ipv4(gpointer user_data) { struct connman_service *service = user_data; - int refcount = service->refcount - 1; - connman_service_unref(service); - if (refcount == 0) { - DBG("Service %p already removed", service); - return FALSE; - } + redo_wispr(service, CONNMAN_IPCONFIG_TYPE_IPV4); - __connman_wispr_start(service, CONNMAN_IPCONFIG_TYPE_IPV6); + return FALSE; +} + +static gboolean redo_wispr_ipv6(gpointer user_data) +{ + struct connman_service *service = user_data; + + redo_wispr(service, CONNMAN_IPCONFIG_TYPE_IPV6); return FALSE; } @@ -7932,40 +7770,42 @@ static gboolean redo_wispr_ipv4(gpointer user_data) int __connman_service_online_check_failed(struct connman_service *service, enum connman_ipconfig_type type) { - DBG("service %p type %d count %d", service, type, - service->online_check_count); + GSourceFunc redo_func; + int *interval; -#if defined TIZEN_MAINTAIN_ONLINE - /* Retry IPv4 stuff also */ if (type == CONNMAN_IPCONFIG_TYPE_IPV4) { - connman_warn("Online check failed for %p %s", service, - service->name); - - g_timeout_add_seconds(1, redo_wispr_ipv4, service); - return 0; - } -#else - /* currently we only retry IPv6 stuff */ - if (type == CONNMAN_IPCONFIG_TYPE_IPV4 || - service->online_check_count != 1) { - connman_warn("Online check failed for %p %s", service, - service->name); - return 0; + interval = &service->online_check_interval_ipv4; + redo_func = redo_wispr_ipv4; + } else { + interval = &service->online_check_interval_ipv6; + redo_func = redo_wispr_ipv6; } -#endif - service->online_check_count = 0; + DBG("service %p type %s interval %d", service, + __connman_ipconfig_type2string(type), *interval); - /* - * We set the timeout to 1 sec so that we have a chance to get - * necessary IPv6 router advertisement messages that might have - * DNS data etc. + service->online_timeout = g_timeout_add_seconds(*interval * *interval, + redo_func, connman_service_ref(service)); + + /* Increment the interval for the next time, set a maximum timeout of + * ONLINE_CHECK_MAX_INTERVAL * ONLINE_CHECK_MAX_INTERVAL seconds. */ - g_timeout_add_seconds(1, redo_wispr, connman_service_ref(service)); + if (*interval < ONLINE_CHECK_MAX_INTERVAL) + (*interval)++; return EAGAIN; } +static void cancel_online_check(struct connman_service *service) +{ + if (service->online_timeout == 0) + return; + + g_source_remove(service->online_timeout); + service->online_timeout = 0; + connman_service_unref(service); +} + int __connman_service_ipconfig_indicate_state(struct connman_service *service, enum connman_service_state new_state, enum connman_ipconfig_type type) @@ -8060,24 +7900,24 @@ int __connman_service_ipconfig_indicate_state(struct connman_service *service, break; } #endif - if (connman_setting_get_bool("EnableOnlineCheck")) { + if (connman_setting_get_bool("EnableOnlineCheck")) if (type == CONNMAN_IPCONFIG_TYPE_IPV4) { #if !defined TIZEN_EXT check_proxy_setup(service); #endif #if defined TIZEN_MAINTAIN_ONLINE -/* if (old_state == CONNMAN_SERVICE_STATE_ONLINE) */ - check_proxy_setup(service); +/* if (old_state == CONNMAN_SERVICE_STATE_ONLINE) */ + check_proxy_setup(service); #endif } else { - service->online_check_count = 1; - __connman_wispr_start(service, type); + __connman_service_wispr_start(service, type); } - } else + else connman_info("Online check disabled. " "Default service remains in READY state."); if (type == CONNMAN_IPCONFIG_TYPE_IPV4) service_rp_filter(service, true); + set_mdns(service, service->mdns_config); break; case CONNMAN_SERVICE_STATE_ONLINE: break; @@ -8097,8 +7937,10 @@ int __connman_service_ipconfig_indicate_state(struct connman_service *service, break; } - if (is_connected(old_state) && !is_connected(new_state)) + if (is_connected(old_state) && !is_connected(new_state)) { nameserver_remove_all(service, type); + cancel_online_check(service); + } if (type == CONNMAN_IPCONFIG_TYPE_IPV4) service->state_ipv4 = new_state; @@ -8290,11 +8132,7 @@ static int service_connect(struct connman_service *service) if (service->error == CONNMAN_SERVICE_ERROR_INVALID_KEY) return -ENOKEY; - if (service->request_passphrase_input) { - DBG("Now try to connect other user's favorite service"); - service->request_passphrase_input = false; - return -ENOKEY; - } else if (!service->passphrase) { + if (!service->passphrase) { if (!service->network) return -EOPNOTSUPP; @@ -8305,8 +8143,15 @@ static int service_connect(struct connman_service *service) break; case CONNMAN_SERVICE_SECURITY_8021X: - if (!service->eap) + if (!service->eap) { + connman_warn("EAP type has not been found. " + "Most likely ConnMan is not able to " + "find a configuration for given " + "8021X network. " + "Check SSID or Name match with the " + "network name."); return -EINVAL; + } #if defined TIZEN_EXT /* @@ -8329,6 +8174,7 @@ static int service_connect(struct connman_service *service) */ if (g_str_equal(service->eap, "tls")) break; + #endif /* * Return -ENOKEY if either identity or passphrase is @@ -8445,6 +8291,7 @@ int __connman_service_connect(struct connman_service *service, #if defined TIZEN_EXT connect_reason_changed(service); #endif + if (err >= 0) return 0; @@ -8487,7 +8334,6 @@ int __connman_service_connect(struct connman_service *service, return err; } - reply_pending(service, -err); } return err; @@ -8504,7 +8350,7 @@ int __connman_service_disconnect(struct connman_service *service) connman_agent_cancel(service); - reply_pending(service, ECONNABORTED); + __connman_stats_service_unregister(service); if (service->network) { err = __connman_network_disconnect(service->network); @@ -8546,8 +8392,6 @@ int __connman_service_disconnect(struct connman_service *service) } #endif - __connman_stats_service_unregister(service); - return err; } @@ -8594,7 +8438,7 @@ static struct connman_service *lookup_by_identifier(const char *identifier) struct connman_service *connman_service_lookup_from_identifier(const char* identifier) { - return lookup_by_identifier(identifier); + return identifier ? lookup_by_identifier(identifier) : NULL; } struct provision_user_data { @@ -8993,14 +8837,9 @@ struct connman_service *__connman_service_lookup_from_index(int index) return NULL; } -struct connman_service *__connman_service_lookup_from_ident(const char *identifier) +const char *connman_service_get_identifier(struct connman_service *service) { - return lookup_by_identifier(identifier); -} - -const char *__connman_service_get_ident(struct connman_service *service) -{ - return service->identifier; + return service ? service->identifier : NULL; } const char *__connman_service_get_path(struct connman_service *service) @@ -9013,65 +8852,9 @@ const char *__connman_service_get_name(struct connman_service *service) return service->name; } -enum connman_service_state __connman_service_get_state(struct connman_service *service) -{ - return service->state; -} - -unsigned int __connman_service_get_order(struct connman_service *service) +enum connman_service_state connman_service_get_state(struct connman_service *service) { - unsigned int order = 0; - - if (!service) - return 0; - - service->order = 0; - - if (!service->favorite) - return 0; - -#if defined TIZEN_EXT - if (service->type == CONNMAN_SERVICE_TYPE_VPN && - service->do_split_routing == FALSE) - order = 10; - else if (service->type == CONNMAN_SERVICE_TYPE_WIFI) { -#if defined TIZEN_MAINTAIN_ONLINE - if (service->state != CONNMAN_SERVICE_STATE_ONLINE) - service->order = 0; - else if (service->order < 5) - service->order = 5; -#else - if (service->order < 5) - order = 5; -#endif - } else if (service->type == CONNMAN_SERVICE_TYPE_ETHERNET) - order = 4; - else if (service->type == CONNMAN_SERVICE_TYPE_BLUETOOTH) - order = 3; - else if (service->type == CONNMAN_SERVICE_TYPE_CELLULAR && - __connman_service_is_internet_profile(service) == TRUE) - order = 1; - else if (service->type == CONNMAN_SERVICE_TYPE_CELLULAR && - __connman_service_is_tethering_profile(service) == TRUE) - order = 0; - else if (service->type == CONNMAN_SERVICE_TYPE_CELLULAR) - order = 0; - else - order = 2; -#else - if (service == service_list->data) - order = 1; - - if (service->type == CONNMAN_SERVICE_TYPE_VPN && - !service->do_split_routing) { - service->order = 10; - order = 10; - } -#endif - DBG("service %p name %s order %d split %d", service, service->name, - order, service->do_split_routing); - - return order; + return service ? service->state : CONNMAN_SERVICE_STATE_UNKNOWN; } static enum connman_service_type convert_network_type(struct connman_network *network) @@ -9142,6 +8925,21 @@ int check_passphrase_ext(struct connman_network *network, } #endif +static void update_wps_values(struct connman_service *service, + struct connman_network *network) +{ + bool wps = connman_network_get_bool(network, "WiFi.WPS"); + bool wps_advertising = connman_network_get_bool(network, + "WiFi.WPSAdvertising"); + + if (service->wps != wps || + service->wps_advertizing != wps_advertising) { + service->wps = wps; + service->wps_advertizing = wps_advertising; + security_changed(service); + } +} + static void update_from_network(struct connman_service *service, struct connman_network *network) { @@ -9182,7 +8980,7 @@ static void update_from_network(struct connman_service *service, service->security = convert_wifi_security(str); if (service->type == CONNMAN_SERVICE_TYPE_WIFI) - service->wps = connman_network_get_bool(network, "WiFi.WPS"); + update_wps_values(service, network); if (service->strength > strength && service->network) { connman_network_unref(service->network); @@ -9209,7 +9007,7 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne struct connman_device *device; const char *ident, *group; char *name; - unsigned int *auto_connect_types; + unsigned int *auto_connect_types, *favorite_types; int i, index; DBG("network %p", network); @@ -9254,23 +9052,13 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne } } - switch (service->type) { - case CONNMAN_SERVICE_TYPE_UNKNOWN: - case CONNMAN_SERVICE_TYPE_SYSTEM: - case CONNMAN_SERVICE_TYPE_BLUETOOTH: - case CONNMAN_SERVICE_TYPE_GPS: - case CONNMAN_SERVICE_TYPE_VPN: - case CONNMAN_SERVICE_TYPE_GADGET: - case CONNMAN_SERVICE_TYPE_WIFI: - case CONNMAN_SERVICE_TYPE_CELLULAR: - case CONNMAN_SERVICE_TYPE_P2P: -#if defined TIZEN_EXT_WIFI_MESH - case CONNMAN_SERVICE_TYPE_MESH: -#endif - break; - case CONNMAN_SERVICE_TYPE_ETHERNET: - service->favorite = true; - break; + favorite_types = connman_setting_get_uint_list("DefaultFavoriteTechnologies"); + service->favorite = false; + for (i = 0; favorite_types && favorite_types[i] != 0; i++) { + if (service->type == favorite_types[i]) { + service->favorite = true; + break; + } } service->state_ipv4 = service->state_ipv6 = CONNMAN_SERVICE_STATE_IDLE; @@ -9292,7 +9080,8 @@ struct connman_service * __connman_service_create_from_network(struct connman_ne if (service->favorite) { device = connman_network_get_device(service->network); - if (device && !connman_device_get_scanning(device)) { + if (device && !connman_device_get_scanning(device, + CONNMAN_SERVICE_TYPE_UNKNOWN)) { switch (service->type) { case CONNMAN_SERVICE_TYPE_UNKNOWN: @@ -9400,7 +9189,7 @@ void __connman_service_update_from_network(struct connman_network *network) } if (service->type == CONNMAN_SERVICE_TYPE_WIFI) - service->wps = connman_network_get_bool(network, "WiFi.WPS"); + update_wps_values(service, network); strength = connman_network_get_strength(service->network); if (strength == service->strength) @@ -9481,6 +9270,7 @@ __connman_service_create_from_provider(struct connman_provider *provider) return NULL; service->type = CONNMAN_SERVICE_TYPE_VPN; + service->order = service->do_split_routing ? 0 : 10; service->provider = connman_provider_ref(provider); service->autoconnect = false; service->favorite = true; @@ -9644,14 +9434,14 @@ void __connman_service_cleanup(void) { DBG(""); - if (vpn_autoconnect_timeout) { - g_source_remove(vpn_autoconnect_timeout); - vpn_autoconnect_timeout = 0; + if (vpn_autoconnect_id) { + g_source_remove(vpn_autoconnect_id); + vpn_autoconnect_id = 0; } - if (autoconnect_timeout != 0) { - g_source_remove(autoconnect_timeout); - autoconnect_timeout = 0; + if (autoconnect_id != 0) { + g_source_remove(autoconnect_id); + autoconnect_id = 0; } connman_agent_driver_unregister(&agent_driver); diff --git a/src/session.c b/src/session.c index 26cbf878..808931a8 100644 --- a/src/session.c +++ b/src/session.c @@ -28,8 +28,6 @@ #include <gdbus.h> -#include <connman/session.h> - #include "connman.h" static DBusConnection *connection; @@ -65,7 +63,9 @@ struct connman_session { struct firewall_context *fw; uint32_t mark; int index; + char *addr; char *gateway; + unsigned char prefixlen; bool policy_routing; bool snat_enabled; }; @@ -79,6 +79,7 @@ struct fw_snat { GSList *sessions; int id; int index; + char *addr; struct firewall_context *fw; }; @@ -203,7 +204,7 @@ static char *service2bearer(enum connman_service_type type) return ""; } -static struct fw_snat *fw_snat_lookup(int index) +static struct fw_snat *fw_snat_lookup(int index, const char *addr) { struct fw_snat *fw_snat; GSList *list; @@ -211,8 +212,11 @@ static struct fw_snat *fw_snat_lookup(int index) for (list = fw_snat_list; list; list = list->next) { fw_snat = list->data; - if (fw_snat->index == index) + if (fw_snat->index == index) { + if (g_strcmp0(addr, fw_snat->addr) != 0) + continue; return fw_snat; + } } return NULL; } @@ -227,6 +231,7 @@ static int fw_snat_create(struct connman_session *session, fw_snat->fw = __connman_firewall_create(); fw_snat->index = index; + fw_snat->addr = g_strdup(addr); fw_snat->id = __connman_firewall_enable_snat(fw_snat->fw, index, ifname, addr); @@ -241,6 +246,7 @@ static int fw_snat_create(struct connman_session *session, return 0; err: __connman_firewall_destroy(fw_snat->fw); + g_free(fw_snat->addr); g_free(fw_snat); return err; } @@ -353,13 +359,17 @@ static void del_default_route(struct connman_session *session) if (!session->gateway) return; - DBG("index %d routing table %d default gateway %s", - session->index, session->mark, session->gateway); + DBG("index %d routing table %d default gateway %s/%u", + session->index, session->mark, session->gateway, session->prefixlen); + + __connman_inet_del_subnet_from_table(session->mark, + session->index, session->gateway, session->prefixlen); __connman_inet_del_default_from_table(session->mark, session->index, session->gateway); g_free(session->gateway); session->gateway = NULL; + session->prefixlen = 0; session->index = -1; } @@ -379,13 +389,20 @@ static void add_default_route(struct connman_session *session) if (!session->gateway) session->gateway = g_strdup(inet_ntoa(addr)); - DBG("index %d routing table %d default gateway %s", - session->index, session->mark, session->gateway); + session->prefixlen = __connman_ipconfig_get_prefixlen(ipconfig); + + DBG("index %d routing table %d default gateway %s/%u", + session->index, session->mark, session->gateway, session->prefixlen); err = __connman_inet_add_default_to_table(session->mark, session->index, session->gateway); if (err < 0) DBG("session %p %s", session, strerror(-err)); + + err = __connman_inet_add_subnet_to_table(session->mark, + session->index, session->gateway, session->prefixlen); + if (err < 0) + DBG("session add subnet route %p %s", session, strerror(-err)); } static void del_nat_rules(struct connman_session *session) @@ -396,7 +413,7 @@ static void del_nat_rules(struct connman_session *session) return; session->snat_enabled = false; - fw_snat = fw_snat_lookup(session->index); + fw_snat = fw_snat_lookup(session->index, session->addr); if (!fw_snat) return; @@ -423,8 +440,11 @@ static void add_nat_rules(struct connman_session *session) if (!addr) return; + g_free(session->addr); + session->addr = g_strdup(addr); + session->snat_enabled = true; - fw_snat = fw_snat_lookup(index); + fw_snat = fw_snat_lookup(index, session->addr); if (fw_snat) { fw_snat_ref(session, fw_snat); return; @@ -496,6 +516,9 @@ static void free_session(struct connman_session *session) if (session->notify_watch > 0) g_dbus_remove_watch(connection, session->notify_watch); + g_dbus_unregister_interface(connection, session->session_path, + CONNMAN_SESSION_INTERFACE); + destroy_policy_config(session); g_slist_free(session->info->config.allowed_bearers); g_free(session->info->config.allowed_interface); @@ -505,6 +528,7 @@ static void free_session(struct connman_session *session) g_free(session->info); g_free(session->info_last); g_free(session->gateway); + g_free(session->addr); g_free(session); } @@ -551,6 +575,7 @@ struct creation_data { GSList *allowed_bearers; char *allowed_interface; bool source_ip_rule; + char *context_identifier; }; static void cleanup_creation_data(struct creation_data *creation_data) @@ -560,6 +585,8 @@ static void cleanup_creation_data(struct creation_data *creation_data) if (creation_data->pending) dbus_message_unref(creation_data->pending); + if (creation_data->context_identifier) + g_free(creation_data->context_identifier); g_slist_free(creation_data->allowed_bearers); g_free(creation_data->allowed_interface); @@ -930,6 +957,17 @@ static void append_notify(DBusMessageIter *dict, } if (session->append_all || + info->config.context_identifier != info_last->config.context_identifier) { + char *ifname = info->config.context_identifier; + if (!ifname) + ifname = ""; + connman_dbus_dict_append_basic(dict, "ContextIdentifier", + DBUS_TYPE_STRING, + &ifname); + info_last->config.context_identifier = info->config.context_identifier; + } + + if (session->append_all || info->config.source_ip_rule != info_last->config.source_ip_rule) { dbus_bool_t source_ip_rule = FALSE; if (info->config.source_ip_rule) @@ -1375,7 +1413,7 @@ static int session_policy_config_cb(struct connman_session *session, connman_error("Failed to register %s", session->session_path); g_hash_table_remove(session_hash, session->session_path); err = -EINVAL; - goto err; + goto err_notify; } reply = g_dbus_create_reply(creation_data->pending, @@ -1400,11 +1438,13 @@ static int session_policy_config_cb(struct connman_session *session, return 0; err: + cleanup_session(session); + +err_notify: reply = __connman_error_failed(creation_data->pending, -err); g_dbus_send_message(connection, reply); creation_data->pending = NULL; - cleanup_session(session); cleanup_creation_data(creation_data); return err; @@ -1477,6 +1517,9 @@ int __connman_session_create(DBusMessage *msg) connman_session_parse_connection_type(val); user_connection_type = true; + } else if (g_str_equal(key, "ContextIdentifier")) { + dbus_message_iter_get_basic(&value, &val); + creation_data->context_identifier = g_strdup(val); } else if (g_str_equal(key, "AllowedInterface")) { dbus_message_iter_get_basic(&value, &val); creation_data->allowed_interface = g_strdup(val); @@ -1675,7 +1718,7 @@ static void update_session_state(struct connman_session *session) enum connman_session_state state = CONNMAN_SESSION_STATE_DISCONNECTED; if (session->service) { - service_state = __connman_service_get_state(session->service); + service_state = connman_service_get_state(session->service); state = service_to_session_state(service_state); session->info->state = state; } @@ -1770,7 +1813,7 @@ static void session_activate(struct connman_session *session) while (g_hash_table_iter_next(&iter, &key, &value)) { struct connman_service_info *info = value; - state = __connman_service_get_state(info->service); + state = connman_service_get_state(info->service); if (is_session_connected(session, state)) service_list = g_slist_prepend(service_list, @@ -1799,7 +1842,7 @@ static void session_activate(struct connman_session *session) struct connman_service_info *info = value; enum connman_service_state state; - state = __connman_service_get_state(info->service); + state = connman_service_get_state(info->service); if (is_session_connected(session, state) && session_match_service(session, info->service)) { @@ -1961,7 +2004,7 @@ static void ipconfig_changed(struct connman_service *service, } } -static struct connman_notifier session_notifier = { +static const struct connman_notifier session_notifier = { .name = "session", .service_state_changed = service_state_changed, .ipconfig_changed = ipconfig_changed, diff --git a/src/shared/arp.c b/src/shared/arp.c new file mode 100644 index 00000000..6cd611f2 --- /dev/null +++ b/src/shared/arp.c @@ -0,0 +1,126 @@ +/* + * + * Connection Manager + * + * based on IPv4 Local Link library with GLib integration, + * Copyright (C) 2009-2010 Aldebaran Robotics. All rights reserved. + * + * Copyright (C) 2018 Commend International. 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 + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> + +#include <sys/time.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <netpacket/packet.h> +#include <net/ethernet.h> +#include <netinet/if_ether.h> +#include <net/if_arp.h> + +#include <arpa/inet.h> + +#include "src/shared/arp.h" +#include "src/connman.h" + +int arp_send_packet(uint8_t* source_eth, uint32_t source_ip, + uint32_t target_ip, int ifindex) +{ + struct sockaddr_ll dest; + struct ether_arp p; + uint32_t ip_source; + uint32_t ip_target; + int fd, n; + + fd = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (fd < 0) + return -errno; + + memset(&dest, 0, sizeof(dest)); + memset(&p, 0, sizeof(p)); + + dest.sll_family = AF_PACKET; + dest.sll_protocol = htons(ETH_P_ARP); + dest.sll_ifindex = ifindex; + dest.sll_halen = ETH_ALEN; + memset(dest.sll_addr, 0xFF, ETH_ALEN); + if (bind(fd, (struct sockaddr *)&dest, sizeof(dest)) < 0) { + int err = errno; + close(fd); + return -err; + } + + ip_source = htonl(source_ip); + ip_target = htonl(target_ip); + p.arp_hrd = htons(ARPHRD_ETHER); + p.arp_pro = htons(ETHERTYPE_IP); + p.arp_hln = ETH_ALEN; + p.arp_pln = 4; + p.arp_op = htons(ARPOP_REQUEST); + + memcpy(&p.arp_sha, source_eth, ETH_ALEN); + memcpy(&p.arp_spa, &ip_source, sizeof(p.arp_spa)); + memcpy(&p.arp_tpa, &ip_target, sizeof(p.arp_tpa)); + + n = sendto(fd, &p, sizeof(p), 0, + (struct sockaddr*) &dest, sizeof(dest)); + if (n < 0) + n = -errno; + + close(fd); + + return n; +} + +int arp_socket(int ifindex) +{ + int fd; + struct sockaddr_ll sock; + + fd = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, 0); + if (fd < 0) + return fd; + + memset(&sock, 0, sizeof(sock)); + + sock.sll_family = AF_PACKET; + sock.sll_protocol = htons(ETH_P_ARP); + sock.sll_ifindex = ifindex; + + if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) != 0) { + int err = errno; + close(fd); + return -err; + } + + return fd; +} + +/** + * Return a random link local IP (in host byte order) + */ +uint32_t arp_random_ip(void) +{ + unsigned tmp; + + do { + uint64_t rand; + __connman_util_get_random(&rand); + tmp = rand; + tmp = tmp & IN_CLASSB_HOST; + } while (tmp > (IN_CLASSB_HOST - 0x0200)); + + return (LINKLOCAL_ADDR + 0x0100) + tmp; +} diff --git a/src/shared/arp.h b/src/shared/arp.h new file mode 100755 index 00000000..03e2168c --- /dev/null +++ b/src/shared/arp.h @@ -0,0 +1,47 @@ +/* + * + * Connection Manager + * + * based on IPv4 Local Link library with GLib integration, + * Copyright (C) 2009-2010 Aldebaran Robotics. All rights reserved. + * + * Copyright (C) 2018 Commend International. 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 + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef SHARED_ARP_H +#define SHARED_ARP_H + +#include <stdint.h> + +/* IPv4 Link-Local (RFC 3927), IPv4 Address Conflict Detection (RFC 5227) */ +#define PROBE_WAIT 1 +#define PROBE_NUM 3 +#define PROBE_MIN 1 +#define PROBE_MAX 2 +#define ANNOUNCE_WAIT 2 +#define ANNOUNCE_NUM 2 +#define ANNOUNCE_INTERVAL 2 +#define MAX_CONFLICTS 10 +#define RATE_LIMIT_INTERVAL 60 +#define DEFEND_INTERVAL 10 + +/* 169.254.0.0 */ +#define LINKLOCAL_ADDR 0xa9fe0000 + +int arp_send_packet(uint8_t* source_eth, uint32_t source_ip, + uint32_t target_ip, int ifindex); +int arp_socket(int ifindex); + +uint32_t arp_random_ip(void); + +#endif diff --git a/src/stats.c b/src/stats.c index 663bc382..6f7ce208 100755 --- a/src/stats.c +++ b/src/stats.c @@ -23,7 +23,6 @@ #include <config.h> #endif -#define _GNU_SOURCE #include <errno.h> #include <sys/mman.h> #include <sys/types.h> @@ -678,7 +677,7 @@ int __connman_stats_service_register(struct connman_service *service) DBG("service %p", service); dir = g_strdup_printf("%s/%s", STORAGEDIR, - __connman_service_get_ident(service)); + connman_service_get_identifier(service)); /* If the dir doesn't exist, create it */ if (!g_file_test(dir, G_FILE_TEST_IS_DIR)) { @@ -707,9 +706,9 @@ int __connman_stats_service_register(struct connman_service *service) } name = g_strdup_printf("%s/%s/data", STORAGEDIR, - __connman_service_get_ident(service)); + connman_service_get_identifier(service)); file->history_name = g_strdup_printf("%s/%s/history", STORAGEDIR, - __connman_service_get_ident(service)); + connman_service_get_identifier(service)); /* TODO: Use a global config file instead of hard coded value. */ file->account_period_offset = 1; diff --git a/src/storage.c b/src/storage.c index 50c8e955..6ca600b2 100755 --- a/src/storage.c +++ b/src/storage.c @@ -23,11 +23,11 @@ #include <config.h> #endif -#include <stdio.h> #include <errno.h> #include <unistd.h> #include <sys/stat.h> #include <dirent.h> +#include <stdio.h> #include <connman/storage.h> diff --git a/src/technology.c b/src/technology.c index 2f14d57d..782ce15c 100644 --- a/src/technology.c +++ b/src/technology.c @@ -80,7 +80,6 @@ struct connman_technology { */ char *tethering_ident; char *tethering_passphrase; - bool tethering_hidden; bool enable_persistent; /* Save the tech state */ @@ -122,7 +121,7 @@ static void rfkill_check(gpointer key, gpointer value, gpointer user_data) struct connman_rfkill *rfkill = value; enum connman_service_type type = GPOINTER_TO_INT(user_data); - /* Calling _technology_rfkill_add will update the tech. */ + /* Calling _technology_add_rfkill will update the tech. */ if (rfkill->type == type) __connman_technology_add_rfkill(rfkill->index, type, rfkill->softblock, rfkill->hardblock); @@ -206,9 +205,6 @@ static void technology_save(struct connman_technology *technology) g_key_file_set_boolean(keyfile, identifier, "Tethering", technology->tethering_persistent); - g_key_file_set_boolean(keyfile, identifier, "Hidden", - technology->tethering_hidden); - if (technology->tethering_ident) g_key_file_set_string(keyfile, identifier, "Tethering.Identifier", @@ -225,8 +221,6 @@ done: __connman_storage_save_global(keyfile); g_key_file_free(keyfile); - - return; } static void tethering_changed(struct connman_technology *technology) @@ -282,7 +276,8 @@ static int set_tethering(struct connman_technology *technology, if (!bridge) return -EOPNOTSUPP; - if (technology->type == CONNMAN_SERVICE_TYPE_WIFI && (!ident)) + if (technology->type == CONNMAN_SERVICE_TYPE_WIFI && + (!ident || !passphrase)) return -EINVAL; for (tech_drivers = technology->driver_list; tech_drivers; @@ -375,6 +370,15 @@ static struct connman_technology *technology_find(enum connman_service_type type return NULL; } +enum connman_service_type connman_technology_get_type + (struct connman_technology *technology) +{ + if (!technology) + return CONNMAN_SERVICE_TYPE_UNKNOWN; + + return technology->type; +} + bool connman_technology_get_wifi_tethering(const char **ssid, const char **psk) { @@ -473,8 +477,6 @@ done: g_free(identifier); g_key_file_free(keyfile); - - return; } bool __connman_technology_get_offlinemode(void) @@ -512,8 +514,6 @@ static void connman_technology_save_offlinemode(void) } g_key_file_free(keyfile); - - return; } static bool connman_technology_load_offlinemode(void) @@ -584,11 +584,6 @@ static void append_properties(DBusMessageIter *iter, DBUS_TYPE_STRING, &technology->tethering_passphrase); - val = technology->tethering_hidden; - connman_dbus_dict_append_basic(&dict, "Hidden", - DBUS_TYPE_BOOLEAN, - &val); - connman_dbus_dict_close(iter, &dict); } @@ -663,7 +658,7 @@ static gboolean technology_pending_reply(gpointer user_data) struct connman_technology *technology = user_data; DBusMessage *reply; - /* Power request timedout, send ETIMEDOUT. */ + /* Power request timed out, send ETIMEDOUT. */ if (technology->pending_reply) { reply = __connman_error_failed(technology->pending_reply, ETIMEDOUT); if (reply) @@ -839,6 +834,8 @@ static int technology_disable(struct connman_technology *technology) if (technology->type == CONNMAN_SERVICE_TYPE_P2P) { technology->enable_persistent = false; + __connman_device_stop_scan(CONNMAN_SERVICE_TYPE_P2P); + __connman_peer_disconnect_all(); return technology_disabled(technology); } else if (technology->type == CONNMAN_SERVICE_TYPE_WIFI) { struct connman_technology *p2p; @@ -977,21 +974,6 @@ static DBusMessage *set_property(DBusConnection *conn, DBG("property %s", name); - if (technology->type == CONNMAN_SERVICE_TYPE_WIFI && technology->connected) { - uid_t uid; - if (connman_dbus_get_connection_unix_user_sync(conn, - dbus_message_get_sender(msg), - &uid) < 0) { - DBG("Can not get unix user id!"); - return __connman_error_permission_denied(msg); - } - - if (!__connman_service_is_user_allowed(CONNMAN_SERVICE_TYPE_WIFI, uid)) { - DBG("Not allow this user to operate wifi technology now!"); - return __connman_error_permission_denied(msg); - } - } - if (g_str_equal(name, "Tethering")) { dbus_bool_t tethering; int err; @@ -1068,25 +1050,6 @@ static DBusMessage *set_property(DBusConnection *conn, DBUS_TYPE_STRING, &technology->tethering_passphrase); } - } else if (g_str_equal(name, "Hidden")) { - dbus_bool_t hidden; - - if (type != DBUS_TYPE_BOOLEAN) - return __connman_error_invalid_arguments(msg); - - dbus_message_iter_get_basic(&value, &hidden); - - if (technology->type != CONNMAN_SERVICE_TYPE_WIFI) - return __connman_error_not_supported(msg); - - technology->tethering_hidden = hidden; - technology_save(technology); - - connman_dbus_property_changed_basic(technology->path, - CONNMAN_TECHNOLOGY_INTERFACE, - "Hidden", - DBUS_TYPE_BOOLEAN, - &hidden); } else if (g_str_equal(name, "Powered")) { dbus_bool_t enable; @@ -1191,10 +1154,7 @@ void __connman_technology_scan_stopped(struct connman_device *device, if (device == other_device) continue; - if (__connman_device_get_service_type(other_device) != type) - continue; - - if (connman_device_get_scanning(other_device)) + if (connman_device_get_scanning(other_device, type)) count += 1; } @@ -1280,12 +1240,11 @@ static DBusMessage *scan(DBusConnection *conn, DBusMessage *msg, void *data) g_slist_prepend(technology->scan_pending, msg); #endif - err = __connman_device_request_scan(technology->type); -#if defined TIZEN_EXT + err = __connman_device_request_scan_full(technology->type); if (err < 0) +#if defined TIZEN_EXT return __connman_error_failed(msg, -err); #else - if (err < 0) reply_scan_pending(technology, err); #endif @@ -1463,7 +1422,8 @@ static DBusMessage *get_scan_state(DBusConnection *conn, DBusMessage *msg, void for (list = technology->device_list; list; list = list->next) { struct connman_device *device = list->data; - scanning = connman_device_get_scanning(device); + scanning = connman_device_get_scanning(device, + connman_device_get_type(device)); if(scanning) break; } @@ -2012,16 +1972,6 @@ static const GDBusMethodTable technology_methods[] = { static const GDBusSignalTable technology_signals[] = { { GDBUS_SIGNAL("PropertyChanged", GDBUS_ARGS({ "name", "s" }, { "value", "v" })) }, - { GDBUS_SIGNAL("DhcpConnected", - GDBUS_ARGS({ "aptype", "s" }, - { "ipaddr", "s" }, - { "macaddr", "s" }, - { "hostname", "s" })) }, - { GDBUS_SIGNAL("DhcpLeaseDeleted", - GDBUS_ARGS({ "aptype", "s" }, - { "ipaddr", "s" }, - { "macaddr", "s" }, - { "hostname", "s" })) }, { }, }; @@ -2148,7 +2098,6 @@ static struct connman_technology *technology_get(enum connman_service_type type) technology->refcount = 1; technology->type = type; - technology->tethering_hidden = FALSE; technology->path = g_strdup_printf("%s/technology/%s", CONNMAN_PATH, str); @@ -2428,7 +2377,7 @@ int __connman_technology_add_device(struct connman_device *device) int err = __connman_device_enable(device); /* * connman_technology_add_device() calls __connman_device_enable() - * but since the device is already enabled, the calls does not + * but since the device is already enabled, the call does not * propagate through to connman_technology_enabled via * connman_device_set_powered. */ @@ -2484,6 +2433,7 @@ int __connman_technology_enabled(enum connman_service_type type) DBG("technology %p type %s rfkill %d enabled %d", technology, get_name(type), technology->rfkill_driven, technology->enabled); + #if !defined TIZEN_EXT if (technology->rfkill_driven) { if (technology->tethering_persistent) @@ -2503,9 +2453,11 @@ int __connman_technology_disabled(enum connman_service_type type) technology = technology_find(type); if (!technology) return -ENXIO; + #if !defined TIZEN_EXT if (technology->rfkill_driven) return 0; + #endif for (list = technology->device_list; list; list = list->next) { struct connman_device *device = list->data; @@ -2533,7 +2485,7 @@ int __connman_technology_set_offlinemode(bool offlinemode) * resuming offlinemode from last saved profile. We need that * information in rfkill_update, otherwise it falls back on the * technology's persistent state. Hence we set the offline mode here - * but save it & call the notifier only if its successful. + * but save it & call the notifier only if it is successful. */ global_offlinemode = offlinemode; @@ -2716,6 +2668,7 @@ done: softblock, hardblock, true)) return 0; #endif + if (global_offlinemode) return 0; diff --git a/src/tethering.c b/src/tethering.c index 891ee51f..e04756ff 100755 --- a/src/tethering.c +++ b/src/tethering.c @@ -31,11 +31,11 @@ #include <stdio.h> #include <sys/ioctl.h> #include <net/if.h> -#include <linux/sockios.h> #include <string.h> #include <fcntl.h> -#include <linux/if_tun.h> #include <netinet/in.h> +#include <linux/sockios.h> +#include <linux/if_tun.h> #include <linux/if_bridge.h> #include "connman.h" @@ -52,9 +52,6 @@ #define DEFAULT_MTU 1500 -#define CONNMAN_STATION_STR_INFO_LEN 64 -#define CONNMAN_STATION_MAC_INFO_LEN 32 - static char *private_network_primary_dns = NULL; static char *private_network_secondary_dns = NULL; @@ -63,7 +60,13 @@ static GDHCPServer *tethering_dhcp_server = NULL; static struct connman_ippool *dhcp_ippool = NULL; static DBusConnection *connection; static GHashTable *pn_hash; -static GHashTable *sta_hash; + +static GHashTable *clients_table; + +struct _clients_notify { + int id; + GHashTable *remove; +} *clients_notify; struct connman_private_network { char *owner; @@ -80,164 +83,6 @@ struct connman_private_network { char *secondary_dns; }; -struct connman_station_info { - bool is_connected; - char *path; - char *type; - char ip[CONNMAN_STATION_STR_INFO_LEN]; - char mac[CONNMAN_STATION_MAC_INFO_LEN]; - char hostname[CONNMAN_STATION_STR_INFO_LEN]; -}; - -static void emit_station_signal(char *action_str, - const struct connman_station_info *station_info) -{ - char *ip, *mac, *hostname; - - if (station_info->path == NULL || station_info->type == NULL - || station_info->ip == NULL || station_info->mac == NULL - || station_info->hostname == NULL) - return; - - ip = g_strdup(station_info->ip); - mac = g_strdup(station_info->mac); - hostname = g_strdup(station_info->hostname); - - g_dbus_emit_signal(connection, station_info->path, - CONNMAN_TECHNOLOGY_INTERFACE, action_str, - DBUS_TYPE_STRING, &station_info->type, - DBUS_TYPE_STRING, &ip, - DBUS_TYPE_STRING, &mac, - DBUS_TYPE_STRING, &hostname, - DBUS_TYPE_INVALID); - - g_free(ip); - g_free(mac); - g_free(hostname); -} -static void destroy_station(gpointer key, gpointer value, gpointer user_data) -{ - struct connman_station_info *station_info; - - __sync_synchronize(); - - station_info = value; - - if (station_info->is_connected) { - station_info->is_connected = FALSE; - emit_station_signal("DhcpLeaseDeleted", station_info); - } - - g_free(station_info->path); - g_free(station_info->type); - g_free(station_info); -} - -static void save_dhcp_ack_lease_info(char *hostname, - unsigned char *mac, unsigned int nip) -{ - char *lower_mac; - const char *ip; - char sta_mac[CONNMAN_STATION_MAC_INFO_LEN]; - struct connman_station_info *info_found; - struct in_addr addr; - int str_len; - - __sync_synchronize(); - - snprintf(sta_mac, CONNMAN_STATION_MAC_INFO_LEN, - "%02x:%02x:%02x:%02x:%02x:%02x", - mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - lower_mac = g_ascii_strdown(sta_mac, -1); - - info_found = g_hash_table_lookup(sta_hash, lower_mac); - if (info_found == NULL) { - g_free(lower_mac); - return; - } - - /* get the ip */ - addr.s_addr = nip; - ip = inet_ntoa(addr); - str_len = strlen(ip) + 1; - if (str_len > CONNMAN_STATION_STR_INFO_LEN) - str_len = CONNMAN_STATION_STR_INFO_LEN - 1; - memcpy(info_found->ip, ip, str_len); - - /* get hostname */ - str_len = strlen(hostname) + 1; - if (str_len > CONNMAN_STATION_STR_INFO_LEN) - str_len = CONNMAN_STATION_STR_INFO_LEN - 1; - memcpy(info_found->hostname, hostname, str_len); - - /* emit a signal */ - info_found->is_connected = TRUE; - emit_station_signal("DhcpConnected", info_found); - g_free(lower_mac); -} - -int connman_technology_tethering_add_station(enum connman_service_type type, - const char *mac) -{ - const char *str_type; - char *lower_mac; - char *path; - struct connman_station_info *station_info; - - __sync_synchronize(); - - DBG("type %d", type); - - str_type = __connman_service_type2string(type); - if (str_type == NULL) - return 0; - - path = g_strdup_printf("%s/technology/%s", CONNMAN_PATH, str_type); - - station_info = g_try_new0(struct connman_station_info, 1); - if (station_info == NULL) - return -ENOMEM; - - lower_mac = g_ascii_strdown(mac, -1); - - memcpy(station_info->mac, lower_mac, strlen(lower_mac) + 1); - station_info->path = path; - station_info->type = g_strdup(str_type); - - g_hash_table_insert(sta_hash, station_info->mac, station_info); - - g_free(lower_mac); - return 0; -} - -int connman_technology_tethering_remove_station(const char *mac) -{ - char *lower_mac; - struct connman_station_info *info_found; - - __sync_synchronize(); - - lower_mac = g_ascii_strdown(mac, -1); - - info_found = g_hash_table_lookup(sta_hash, lower_mac); - if (info_found == NULL) { - g_free(lower_mac); - return -EACCES; - } - - if (info_found->is_connected) { - info_found->is_connected = FALSE; - emit_station_signal("DhcpLeaseDeleted", info_found); - } - g_free(lower_mac); - g_hash_table_remove(sta_hash, info_found->mac); - g_free(info_found->path); - g_free(info_found->type); - g_free(info_found); - - return 0; -} - const char *__connman_tethering_get_bridge(void) { int sk, err; @@ -323,9 +168,6 @@ static GDHCPServer *dhcp_server_start(const char *bridge, g_dhcp_server_set_option(dhcp_server, G_DHCP_DNS_SERVER, dns); g_dhcp_server_set_ip_range(dhcp_server, start_ip, end_ip); - g_dhcp_server_set_save_ack_lease(dhcp_server, - save_dhcp_ack_lease_info, NULL); - g_dhcp_server_start(dhcp_server); return dhcp_server; @@ -346,6 +188,18 @@ static void tethering_restart(struct connman_ippool *pool, void *user_data) __connman_tethering_set_enabled(); } +static void unregister_client(gpointer key, + gpointer value, gpointer user_data) +{ + const char *addr = key; + __connman_tethering_client_unregister(addr); +} + +static void unregister_all_clients(void) +{ + g_hash_table_foreach(clients_table, unregister_client, NULL); +} + int __connman_tethering_set_enabled(void) { int index; @@ -390,7 +244,8 @@ int __connman_tethering_set_enabled(void) connman_ipaddress_calc_netmask_len(subnet_mask), broadcast); if (err < 0 && err != -EALREADY) { - __connman_ippool_unref(dhcp_ippool); + __connman_ippool_free(dhcp_ippool); + dhcp_ippool = NULL; __connman_bridge_remove(BRIDGE_NAME); __sync_fetch_and_sub(&tethering_enabled, 1); return -EADDRNOTAVAIL; @@ -426,7 +281,8 @@ int __connman_tethering_set_enabled(void) 24 * 3600, dns); if (!tethering_dhcp_server) { __connman_bridge_disable(BRIDGE_NAME); - __connman_ippool_unref(dhcp_ippool); + __connman_ippool_free(dhcp_ippool); + dhcp_ippool = NULL; __connman_bridge_remove(BRIDGE_NAME); __sync_fetch_and_sub(&tethering_enabled, 1); return -EOPNOTSUPP; @@ -438,7 +294,8 @@ int __connman_tethering_set_enabled(void) connman_error("Cannot enable NAT %d/%s", err, strerror(-err)); dhcp_server_stop(tethering_dhcp_server); __connman_bridge_disable(BRIDGE_NAME); - __connman_ippool_unref(dhcp_ippool); + __connman_ippool_free(dhcp_ippool); + dhcp_ippool = NULL; __connman_bridge_remove(BRIDGE_NAME); __sync_fetch_and_sub(&tethering_enabled, 1); return -EOPNOTSUPP; @@ -463,6 +320,8 @@ void __connman_tethering_set_disabled(void) if (__sync_fetch_and_sub(&tethering_enabled, 1) != 1) return; + unregister_all_clients(); + __connman_ipv6pd_cleanup(); index = connman_inet_ifindex(BRIDGE_NAME); @@ -476,7 +335,8 @@ void __connman_tethering_set_disabled(void) __connman_bridge_disable(BRIDGE_NAME); - __connman_ippool_unref(dhcp_ippool); + __connman_ippool_free(dhcp_ippool); + dhcp_ippool = NULL; __connman_bridge_remove(BRIDGE_NAME); @@ -488,6 +348,21 @@ void __connman_tethering_set_disabled(void) DBG("tethering stopped"); } +static void append_client(gpointer key, gpointer value, + gpointer user_data) +{ + const char *addr = key; + DBusMessageIter *array = user_data; + + dbus_message_iter_append_basic(array, DBUS_TYPE_STRING, + &addr); +} + +void __connman_tethering_list_clients(DBusMessageIter *array) +{ + g_hash_table_foreach(clients_table, append_client, array); +} + static void setup_tun_interface(unsigned int flags, unsigned change, void *data) { @@ -564,7 +439,7 @@ static void remove_private_network(gpointer user_data) __connman_nat_disable(BRIDGE_NAME); connman_rtnl_remove_watch(pn->iface_watch); - __connman_ippool_unref(pn->pool); + __connman_ippool_free(pn->pool); if (pn->watch > 0) { g_dbus_remove_watch(connection, pn->watch); @@ -601,6 +476,70 @@ static void ippool_disconnect(struct connman_ippool *pool, void *user_data) g_hash_table_remove(pn_hash, pn->path); } +static gboolean client_send_changed(gpointer data) +{ + DBusMessage *signal; + DBusMessageIter iter, array; + + DBG(""); + + clients_notify->id = 0; + + signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH, + CONNMAN_MANAGER_INTERFACE, "TetheringClientsChanged"); + if (!signal) + return FALSE; + + dbus_message_iter_init_append(signal, &iter); + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_STRING_AS_STRING, &array); + + g_hash_table_foreach(clients_table, append_client, &array); + + dbus_message_iter_close_container(&iter, &array); + + dbus_message_iter_init_append(signal, &iter); + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_STRING_AS_STRING, &array); + + g_hash_table_foreach(clients_notify->remove, append_client, &array); + + dbus_message_iter_close_container(&iter, &array); + + dbus_connection_send(connection, signal, NULL); + dbus_message_unref(signal); + + g_hash_table_remove_all(clients_notify->remove); + + return FALSE; +} + +static void client_schedule_changed(void) +{ + if (clients_notify->id != 0) + return; + + clients_notify->id = g_timeout_add(100, client_send_changed, NULL); +} + +static void client_added(const char *addr) +{ + DBG("client %s", addr); + + g_hash_table_remove(clients_notify->remove, addr); + + client_schedule_changed(); +} + +static void client_removed(const char *addr) +{ + DBG("client %s", addr); + + g_hash_table_replace(clients_notify->remove, g_strdup(addr), NULL); + + client_schedule_changed(); +} + int __connman_private_network_request(DBusMessage *msg, const char *owner) { struct connman_private_network *pn; @@ -690,6 +629,18 @@ int __connman_private_network_release(const char *path) return 0; } +void __connman_tethering_client_register(const char *addr) +{ + g_hash_table_insert(clients_table, g_strdup(addr), NULL); + client_added(addr); +} + +void __connman_tethering_client_unregister(const char *addr) +{ + g_hash_table_remove(clients_table, addr); + client_removed(addr); +} + int __connman_tethering_init(void) { DBG(""); @@ -703,9 +654,12 @@ int __connman_tethering_init(void) pn_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, remove_private_network); - sta_hash = g_hash_table_new_full(g_str_hash, - g_str_equal, NULL, NULL); + clients_table = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, NULL); + clients_notify = g_new0(struct _clients_notify, 1); + clients_notify->remove = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, NULL); return 0; } @@ -726,7 +680,13 @@ void __connman_tethering_cleanup(void) return; g_hash_table_destroy(pn_hash); - g_hash_table_foreach(sta_hash, destroy_station, NULL); - g_hash_table_destroy(sta_hash); + + g_hash_table_destroy(clients_notify->remove); + g_free(clients_notify); + clients_notify = NULL; + + g_hash_table_destroy(clients_table); + clients_table = NULL; + dbus_connection_unref(connection); } diff --git a/src/timeserver.c b/src/timeserver.c index e4cfc062..c6103466 100755 --- a/src/timeserver.c +++ b/src/timeserver.c @@ -34,17 +34,31 @@ #define TS_RECHECK_INTERVAL 7200 +static struct connman_service *ts_service; +static GSList *timeservers_list = NULL; static GSList *ts_list = NULL; static char *ts_current = NULL; static int ts_recheck_id = 0; +static int ts_backoff_id = 0; static GResolv *resolv = NULL; static int resolv_id = 0; +static void sync_next(void); + static void resolv_debug(const char *str, void *data) { connman_info("%s: %s\n", (const char *) data, str); } + +static void ntp_callback(bool success, void *user_data) +{ + DBG("success %d", success); + + if (!success) + sync_next(); +} + static void save_timeservers(char **servers) { GKeyFile *keyfile; @@ -62,8 +76,6 @@ static void save_timeservers(char **servers) __connman_storage_save_global(keyfile); g_key_file_free(keyfile); - - return; } static char **load_timeservers(void) @@ -101,35 +113,60 @@ static void resolv_result(GResolvResultStatus status, char **results, if (status == G_RESOLV_RESULT_STATUS_SUCCESS) { if (results) { - for (i = 0; results[i]; i++) { + /* prepend the results in reverse order */ + + for (i = 0; results[i]; i++) + /* count */; + i--; + + for (; i >= 0; i--) { DBG("result[%d]: %s", i, results[i]); - if (i == 0) - continue; ts_list = __connman_timeserver_add_list( - ts_list, results[i]); + ts_list, results[i]); } + } + } - DBG("Using timeserver %s", results[0]); + sync_next(); +} - __connman_ntp_start(results[0]); +/* + * Once the timeserver list (timeserver_list) is created, we start + * querying the servers one by one. If resolving fails on one of them, + * we move to the next one. The user can enter either an IP address or + * a URL for the timeserver. We only resolve the URLs. Once we have an + * IP for the NTP server, we start querying it for time corrections. + */ +static void timeserver_sync_start(void) +{ + GSList *list; - return; - } + for (list = timeservers_list; list; list = list->next) { + char *timeserver = list->data; + + ts_list = g_slist_prepend(ts_list, g_strdup(timeserver)); } + ts_list = g_slist_reverse(ts_list); + + sync_next(); +} + +static gboolean timeserver_sync_restart(gpointer user_data) +{ + timeserver_sync_start(); + ts_backoff_id = 0; - /* If resolving fails, move to the next server */ - __connman_timeserver_sync_next(); + return FALSE; } /* - * Once the timeserver list (ts_list) is created, we start querying the - * servers one by one. If resolving fails on one of them, we move to the - * next one. The user can enter either an IP address or a URL for the - * timeserver. We only resolve the URLs. Once we have an IP for the NTP - * server, we start querying it for time corrections. + * Select the next time server from the working list (ts_list) because + * for some reason the first time server in the list didn't work. If + * none of the server did work we start over with the first server + * with a backoff. */ -void __connman_timeserver_sync_next() +static void sync_next(void) { if (ts_current) { g_free(ts_current); @@ -138,34 +175,30 @@ void __connman_timeserver_sync_next() __connman_ntp_stop(); - /* Get the 1st server in the list */ - if (!ts_list) - return; - - ts_current = ts_list->data; - - ts_list = g_slist_delete_link(ts_list, ts_list); - - /* if it's an IP, directly query it. */ - if (connman_inet_check_ipaddress(ts_current) > 0) { - DBG("Using timeserver %s", ts_current); + while (ts_list) { + ts_current = ts_list->data; + ts_list = g_slist_delete_link(ts_list, ts_list); - __connman_ntp_start(ts_current); - - return; - } - - DBG("Resolving timeserver %s", ts_current); + /* if it's an IP, directly query it. */ + if (connman_inet_check_ipaddress(ts_current) > 0) { + DBG("Using timeserver %s", ts_current); + __connman_ntp_start(ts_current, ntp_callback, NULL); + return; + } + DBG("Resolving timeserver %s", ts_current); #if defined TIZEN_EXT - resolv_id = g_resolv_lookup_hostname(resolv, ts_current, + resolv_id = g_resolv_lookup_hostname(resolv, ts_current, resolv_result, ts_current); #else - resolv_id = g_resolv_lookup_hostname(resolv, ts_current, + resolv_id = g_resolv_lookup_hostname(resolv, ts_current, resolv_result, NULL); #endif + return; + } - return; + DBG("No timeserver could be used, restart probing in 5 seconds"); + ts_backoff_id = g_timeout_add_seconds(5, timeserver_sync_restart, NULL); } GSList *__connman_timeserver_add_list(GSList *server_list, @@ -218,22 +251,22 @@ GSList *__connman_timeserver_get_all(struct connman_service *service) for (i = 0; service_ts && service_ts[i]; i++) list = __connman_timeserver_add_list(list, service_ts[i]); -#if defined TIZEN_EXT - if (connman_setting_get_bool("UseGatewayTimeserver")) { -#endif - network = __connman_service_get_network(service); - if (network) { - index = connman_network_get_index(network); - service_gw = __connman_ipconfig_get_gateway_from_index(index, - CONNMAN_IPCONFIG_TYPE_ALL); - - /* Then add Service Gateway to the list */ - if (service_gw) - list = __connman_timeserver_add_list(list, service_gw); - } -#if defined TIZEN_EXT + /* + * Then add Service Gateway to the list, if UseGatewaysAsTimeservers + * configuration option is set to true. + */ + if (connman_setting_get_bool("UseGatewaysAsTimeservers")) { + network = __connman_service_get_network(service); + if (network) { + index = connman_network_get_index(network); + service_gw = __connman_ipconfig_get_gateway_from_index(index, + CONNMAN_IPCONFIG_TYPE_ALL); + + if (service_gw) + list = __connman_timeserver_add_list(list, service_gw); + } } -#endif + /* Then add Global Timeservers to the list */ timeservers = load_timeservers(); @@ -255,7 +288,7 @@ static gboolean ts_recheck(gpointer user_data) { GSList *ts; - ts = __connman_timeserver_get_all(__connman_service_get_default()); + ts = __connman_timeserver_get_all(connman_service_get_default()); if (!ts) { DBG("timeservers disabled"); @@ -288,6 +321,11 @@ static void ts_recheck_disable(void) g_source_remove(ts_recheck_id); ts_recheck_id = 0; + if (ts_backoff_id) { + g_source_remove(ts_backoff_id); + ts_backoff_id = 0; + } + if (ts_current) { g_free(ts_current); ts_current = NULL; @@ -316,11 +354,14 @@ int __connman_timeserver_sync(struct connman_service *default_service) if (default_service) service = default_service; else - service = __connman_service_get_default(); + service = connman_service_get_default(); if (!service) return -EINVAL; + if (service == ts_service) + return -EALREADY; + if (!resolv) return 0; /* @@ -346,20 +387,21 @@ int __connman_timeserver_sync(struct connman_service *default_service) g_strfreev(nameservers); - g_slist_free_full(ts_list, g_free); + g_slist_free_full(timeservers_list, g_free); - ts_list = __connman_timeserver_get_all(service); + timeservers_list = __connman_timeserver_get_all(service); - __connman_service_timeserver_changed(service, ts_list); + __connman_service_timeserver_changed(service, timeservers_list); - if (!ts_list) { + if (!timeservers_list) { DBG("No timeservers set."); return 0; } ts_recheck_enable(); - __connman_timeserver_sync_next(); + ts_service = service; + timeserver_sync_start(); return 0; } @@ -376,8 +418,6 @@ static int timeserver_start(struct connman_service *service) return -EINVAL; nameservers = connman_service_get_nameservers(service); - if (!nameservers) - return -EINVAL; /* Stop an already ongoing resolution, if there is one */ if (resolv && resolv_id > 0) @@ -398,10 +438,12 @@ static int timeserver_start(struct connman_service *service) if (getenv("CONNMAN_RESOLV_DEBUG")) g_resolv_set_debug(resolv, resolv_debug, "RESOLV"); - for (i = 0; nameservers[i]; i++) - g_resolv_add_nameserver(resolv, nameservers[i], 53, 0); + if (nameservers) { + for (i = 0; nameservers[i]; i++) + g_resolv_add_nameserver(resolv, nameservers[i], 53, 0); - g_strfreev(nameservers); + g_strfreev(nameservers); + } return __connman_timeserver_sync(service); } @@ -410,13 +452,17 @@ static void timeserver_stop(void) { DBG(" "); + ts_service = NULL; + if (resolv) { g_resolv_unref(resolv); resolv = NULL; } - g_slist_free_full(ts_list, g_free); + g_slist_free_full(timeservers_list, g_free); + timeservers_list = NULL; + g_slist_free_full(ts_list, g_free); ts_list = NULL; __connman_ntp_stop(); @@ -449,7 +495,7 @@ static void default_changed(struct connman_service *default_service) timeserver_stop(); } -static struct connman_notifier timeserver_notifier = { +static const struct connman_notifier timeserver_notifier = { .name = "timeserver", .default_changed = default_changed, }; diff --git a/src/timezone.c b/src/timezone.c index e346b11a..8e912670 100755 --- a/src/timezone.c +++ b/src/timezone.c @@ -23,7 +23,6 @@ #include <config.h> #endif -#define _GNU_SOURCE #include <errno.h> #include <stdio.h> #include <fcntl.h> @@ -91,3 +91,14 @@ void __connman_util_cleanup(void) f = -1; } + +/** + * Return a random delay in range of zero to secs*1000 milli seconds. + */ +unsigned int __connman_util_random_delay_ms(unsigned int secs) +{ + uint64_t rand; + + __connman_util_get_random(&rand); + return rand % (secs * 1000); +} diff --git a/src/wispr.c b/src/wispr.c index f5fe36b4..57abdf1e 100644 --- a/src/wispr.c +++ b/src/wispr.c @@ -568,7 +568,7 @@ static void wispr_portal_browser_reply_cb(struct connman_service *service, } /* Restarting the test */ - __connman_wispr_start(service, wp_context->type); + __connman_service_wispr_start(service, wp_context->type); } static void wispr_portal_request_wispr_login(struct connman_service *service, @@ -87,7 +87,7 @@ static void wpad_result(GResolvResultStatus status, g_free(url); - __connman_wispr_start(wpad->service, + __connman_service_wispr_start(wpad->service, CONNMAN_IPCONFIG_TYPE_IPV4); return; @@ -119,7 +119,7 @@ failed: connman_service_set_proxy_method(wpad->service, CONNMAN_SERVICE_PROXY_METHOD_DIRECT); - __connman_wispr_start(wpad->service, + __connman_service_wispr_start(wpad->service, CONNMAN_IPCONFIG_TYPE_IPV4); } |