[PATCH] Force BSS expiration
by Yasser
We were having a problem with our wifi scanning, where the list of
wifi available would become empty and would not be repopulated until
after a long delay. Researching the problem it seemed that it was
related to BSS expiration age. There were already some people who had
faced the same issue, so inspired by this we developed the following
patch which allows us to set the BSS expiration age to match ConnMan
long scanning interval to avoid the loss of networks during a long
interval between two scans.
diff --git a/gsupplicant/gsupplicant.h b/gsupplicant/gsupplicant.h
index bfb52db..08d6b9e 100644
--- a/gsupplicant/gsupplicant.h
+++ b/gsupplicant/gsupplicant.h
@@ -267,7 +267,8 @@ int
g_supplicant_interface_connect(GSupplicantInterface *interface,
int g_supplicant_interface_disconnect(GSupplicantInterface *interface,
GSupplicantInterfaceCallback callback,
void *user_data);
-
+int g_supplicant_interface_set_bss_expiration_age(GSupplicantInterface
*interface,
+ unsigned int
bss_expiration_age);
int g_supplicant_interface_set_apscan(GSupplicantInterface *interface,
unsigned int ap_scan);
diff --git a/gsupplicant/supplicant.c b/gsupplicant/supplicant.c
index 6052f7b..fe6ad48 100644
--- a/gsupplicant/supplicant.c
+++ b/gsupplicant/supplicant.c
@@ -981,6 +981,46 @@ static void interface_capability(const char *key,
DBusMessageIter *iter,
key, dbus_message_iter_get_arg_type(iter));
}
+struct g_supplicant_bss_expiration_age
+{
+ GSupplicantInterface *interface;
+ unsigned int bss_expiration_age;
+};
+
+static void set_bss_expiration_age(DBusMessageIter *iter, void *user_data)
+{
+ struct g_supplicant_bss_expiration_age *data = user_data;
+ unsigned int bss_expiration_age = data->bss_expiration_age;
+
+ dbus_free(data);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32,
&bss_expiration_age);
+}
+
+int g_supplicant_interface_set_bss_expiration_age(GSupplicantInterface
*interface,
+ unsigned int
bss_expiration_age)
+{
+ struct g_supplicant_bss_expiration_age *data;
+ int ret;
+
+ data = dbus_malloc0(sizeof(*data));
+
+ if (!data)
+ return -ENOMEM;
+
+ data->bss_expiration_age = bss_expiration_age;
+ data->interface = interface;
+
+ ret = supplicant_dbus_property_set(interface->path,
+ SUPPLICANT_INTERFACE ".Interface",
+ "BSSExpireAge", DBUS_TYPE_UINT32_AS_STRING,
+ set_bss_expiration_age, NULL, data, NULL);
+ if (ret < 0)
+ dbus_free(data);
+
+ return ret;
+}
+
+
struct set_apscan_data
{
unsigned int ap_scan;
diff --git a/plugins/wifi.c b/plugins/wifi.c
index 910b739..57b63e2 100644
--- a/plugins/wifi.c
+++ b/plugins/wifi.c
@@ -1522,6 +1522,7 @@ static void interface_create_callback(int result,
void *user_data)
{
struct wifi_data *wifi = user_data;
+ char * bgscan_range_max;
DBG("result %d ifname %s, wifi %p", result,
g_supplicant_interface_get_ifname(interface),
@@ -1537,6 +1538,13 @@ static void interface_create_callback(int result,
wifi->interface_ready = true;
finalize_interface_creation(wifi);
}
+ /* Force the BSS expiration age to match ConnMan long scanning
interval to avoid the loss of networks during a long interval between
two scannings. */
+ if ((bgscan_range_max = strrchr(BGSCAN_DEFAULT,':')) != NULL &&
+
g_supplicant_interface_set_bss_expiration_age(interface,
strtol(bgscan_range_max + 1, (char**)NULL, 10) + 10) >= 0) {
+ DBG("bss expiration age successfully updated");
+ } else {
+ DBG("bss expiration age update has failed");
+ }
}
static int wifi_enable(struct connman_device *device)
1 year, 4 months
connman wifi disconnect problem
by KeithG
I have been trying to diagnose a problem I am having with my RPis. I am
using iwd and connman to manage my ethernet connectivity. What I have
noticed is that after a while, sometimes, connectivity stops and I cannot
reconnect except after a reboot. I have seen this on RPi3's more so than
with RPi1/2. I have only noticed it with wlan0 and not with ethernet.
From the journal, it looks like the network goes down (wlan0), connman
quickly notes it is down and pulls the ip address and sets the interface
down and then avahi identifies that it is down. It is interesting that
sometime before this, the ipv4 address was withdrawn. I am running an
openWRT router (other side of the DHCP equation) and this RPI MAC has a
reserved ipv4 address on the router.
If I restart connman on the RPi, it will not regain connectivity on ipv4 or
ipv6 on wlan0. If I reboot the router and then restart connman on the RPi,
it will not regain connectivity. I have to power cycle the RPI to get it to
connect again. If I plug in the ethernet cable, it grabs an address
immediately and everything is back, but it will still not reconnect over
wifi. I must reboot the RPi to get it to reconnect. I will see if I can
figure out how to have journal log more info on connman...
Any idea what this could be? I will flash the router with ddwrt and see if
it is the router.
In my /etc/connman/main.conf, I have this:
[General]
FallbackNameservers = 8.8.8.8,8.8.4.4
DefaultAutoConnectTechnologies = ethernet,wifi
PreferredTechnologies = ethernet,wifi
AllowHostnameUpdates = false
SingleConnectedTechnology = true
AlwaysConnectedTechnologies = ethernet,wifi
AutoConnectRoamingServices = true
The journal shows this:
Jun 20 18:02:19 rune64 kernel: nfs: server 192.168.2.198 not responding,
timed out
Jun 20 18:02:20 rune64 systemd[1]: dbus-org.freedesktop.timedate1.service:
Succeeded.
Jun 20 18:02:35 rune64 kernel: nfs: server 192.168.2.198 not responding,
timed out
Jun 20 18:02:39 rune64 kernel: nfs: server 192.168.2.198 not responding,
timed out
... lots of these 'not responding'...
Jun 20 18:07:54 rune64 kernel: nfs: server 192.168.2.198 not responding,
timed out
Jun 20 18:07:55 rune64 avahi-daemon[6529]: Withdrawing address record for
2601:241:4200:255:ba27:ebff:fe52:ccd0 on wlan0.
Jun 20 18:07:55 rune64 connmand[259]: wlan0 {del} address
2601:241:4200:255:ba27:ebff:fe52:ccd0/64 label (null)
Jun 20 18:07:55 rune64 avahi-daemon[6529]: Leaving mDNS multicast group on
interface wlan0.IPv6 with address 2601:241:4200:255:ba27:ebff:fe52:ccd0.
Jun 20 18:07:55 rune64 avahi-daemon[6529]: Joining mDNS multicast group on
interface wlan0.IPv6 with address fdcb:9d61:70f3:0:ba27:ebff:fe52:ccd0.
Jun 20 18:07:57 rune64 kernel: nfs: server 192.168.2.198 not responding,
timed out
Jun 20 18:08:10 rune64 kernel: nfs: server 192.168.2.198 not responding,
timed out
Jun 20 18:08:13 rune64 kernel: nfs: server 192.168.2.198 not responding,
timed out
Jun 20 18:08:14 rune64 connmand[259]: wlan0 {del} route 2601:241:4200:255::
gw :: scope 0 <UNIVERSE>
1 year, 7 months
ipv6 test web page down
by KeithG
DOn't know if this is the right place or not, but since moving to connman,
I have noticed that the ipv6 test page is down. the ipv4 one works fine,
but I get a mention in the log:
"connmand[254]: Failed to find URL:
http://ipv6.connman.net/online/status.html"
If I try to get to it from a browser, I get a '502 bad gateway error'
If I use curl on the cli, I get this:
# curl ipv4.connman.net/online/status.html
<html>
<head>
</head>
<body>
</body>
</html>
# curl ipv6.connman.net/online/status.html
<html>
<head><title>502 Bad Gateway</title></head>
<body bgcolor="white">
<center><h1>502 Bad Gateway</h1></center>
<hr><center>nginx</center>
</body>
</html>
I think something is not set right on the server...
Keith
1 year, 7 months
Re: service config using network interface name
by Daniel Wagner
Hi,
On Mon, Jun 22, 2020 at 02:44:10PM +0000, Pieter Cardoen wrote:
> Dear
>
> We are developing embedded systems with a multitude of wired network connections. I recently started using connmand to manage our network interfaces. I am however not able to define a service config file using the interface name (like eth0, eth1, ...).
>
> The documentation states:
> MAC= address
> MAC address of the interface to be used. If not specified, the first
> found interface is used. Must be in format ab:cd:ef:01:23:45.
>
> Is it possible to use network interface name instead of MAC address for defining a service config?
Yes, it's possible with DeviceName which exists in version 1.38 of ConnMan.
Thanks,
Daniel
1 year, 10 months
wireguard plugin doesn't load on musl (alpine linux)
by Sean Behan
I'm using connman on Alpine Linux and the wireguard plugin is failing to
load with the error below. If anyone knows the reason why this might be
happening or has any advice it would be greatly appreciated. Thanks!
I also opened an issue on the alpine linux tracker here:
https://gitlab.alpinelinux.org/alpine/aports/-/issues/11654
Jun 15 19:23:50 komodo daemon.err connman-vpnd[32560]: Can't load /usr/lib/connman/plugins-vpn/wireguard.so: Error relocating /usr/lib/connman/plugins-vpn/wireguard.so: __vpn_ipconfig_foreach: symbol not found
1 year, 10 months
service config using network interface name
by Pieter Cardoen
Dear
We are developing embedded systems with a multitude of wired network connections. I recently started using connmand to manage our network interfaces. I am however not able to define a service config file using the interface name (like eth0, eth1, ...).
The documentation states:
MAC= address
MAC address of the interface to be used. If not specified, the first
found interface is used. Must be in format ab:cd:ef:01:23:45.
Is it possible to use network interface name instead of MAC address for defining a service config?
Thanks
Pieter Cardoen
1 year, 10 months
Race condition in iwd plugin between addition of Network and Device
objects
by Alvin Šipraga
Hello,
I am using connman 0.38 and iwd 1.7. This problem is in relation to the
iwd plugin of connman.
I have observed a race condition in the initialization of the connman
iwd plugin, where the D-Bus proxy handler object_add() gets invoked on
the net.connman.iwd.Network interface before a relevant device object
has been created through an invocation on the net.connman.iwd.Device
interface.
This is problematic because in the create_network function, it is
possible that the iwd_network object is created and added to the
networks list, but whose network member is NULL. To see how this can
happen, observe that create_network() creates an iwd_network object and
adds it to the list, but relies on add_network() to populate the
object's network member.
This is dangerous for two reasons. Firstly, I cannot see how this
network member of iwd_network can get populated later on. But secondly,
it can cause a crash as indicated in the stack trace at the end of this
mail.
I looked briefly into fixing this, but I am not familiar with the
codebase or the g_dbus API. Specifically, I could not see how I could
prioritize invocation of object_add() on Device interfaces before
handling the Network interfaces. Other quick fixes seem to be "hacks" at
best, which might lead to information loss and other weird bugs.
I am therefore posting here to ask for some guidance on how I can fix
this issue, or provide assistance if anybody has the time to fix it
themselves.
Thanks in advance.
Kind regards,
Alvin
(gdb) bt
#0 connman_network_set_connected (network=0x0, connected=false) at
../connman-1.38/src/network.c:1682
#1 0x0000aaaadb288f80 in network_property_change (proxy=<optimized out>,
name=0xaaab143bcb3c "Connected", iter=0xfffffe3d8d90,
user_data=<optimized out>)
at ../connman-1.38/plugins/iwd.c:944
#2 0x0000aaaadb2eae80 in add_property (proxy=proxy@entry=0xaaab143b9500,
name=0xaaab143bcb3c "Connected", iter=iter@entry=0xfffffe3d8e70,
send_changed=send_changed@entry=0)
at ../connman-1.38/gdbus/client.c:243
#3 0x0000aaaadb2eb09c in update_properties (send_changed=0,
iter=<optimized out>,
proxy=0xaaab143b9500) at ../connman-1.38/gdbus/client.c:275
#4 update_properties (proxy=0xaaab143b9500, iter=<optimized out>,
send_changed=0)
at ../connman-1.38/gdbus/client.c:253
#5 0x0000aaaadb2eb40c in parse_properties (iter=0xfffffe3d8f70,
interface=0xaaab143bcafc "net.connman.iwd.Network",
path=0xaaab143bcac4 "/net/connman/iwd/0/3/42454f4755455354_open",
client=0xaaab143b21d0)
at ../connman-1.38/gdbus/client.c:944
#6 parse_interfaces (client=client@entry=0xaaab143b21d0,
path=path@entry=0xaaab143bcac4
"/net/connman/iwd/0/3/42454f4755455354_open",
iter=iter@entry=0xfffffe3d90d0) at ../connman-1.38/gdbus/client.c:982
#7 0x0000aaaadb2ec954 in parse_interfaces (iter=0xfffffe3d90d0,
path=0xaaab143bcac4 "/net/connman/iwd/0/3/42454f4755455354_open",
client=0xaaab143b21d0)
at ../connman-1.38/gdbus/client.c:965
#8 parse_managed_objects (msg=0xaaab143b56d0, client=0xaaab143b21d0)
at ../connman-1.38/gdbus/client.c:1074
#9 get_managed_objects_reply (call=<optimized out>,
user_data=0xaaab143b21d0)
at ../connman-1.38/gdbus/client.c:1095
#10 0x0000ffff9dc59034 in complete_pending_call_and_unlock (
connection=connection@entry=0xaaab143a9820, pending=0xaaab143ad0f0,
message=message@entry=0xaaab143b56d0) at
../../dbus-1.12.16/dbus/dbus-connection.c:2332
#11 0x0000ffff9dc5ce4c in dbus_connection_dispatch
(connection=0xaaab143a9820)
at ../../dbus-1.12.16/dbus/dbus-connection.c:4653
#12 dbus_connection_dispatch (connection=connection@entry=0xaaab143a9820)
at ../../dbus-1.12.16/dbus/dbus-connection.c:4576
#13 0x0000aaaadb2e5f78 in message_dispatch (data=0xaaab143a9820) at
../connman-1.38/gdbus/mainloop.c:72
#14 0x0000ffff9dcf9b64 in g_main_dispatch (context=0xaaab143a8970) at
../glib-2.60.7/glib/gmain.c:3189
#15 g_main_context_dispatch (context=context@entry=0xaaab143a8970) at
../glib-2.60.7/glib/gmain.c:3854
#16 0x0000ffff9dcf9f20 in g_main_context_iterate
(context=0xaaab143a8970, block=block@entry=1,
dispatch=dispatch@entry=1, self=<optimized out>) at
../glib-2.60.7/glib/gmain.c:3927
#17 0x0000ffff9dcfa2d8 in g_main_loop_run (loop=0xaaab143a82e0) at
../glib-2.60.7/glib/gmain.c:4123
#18 0x0000aaaadb278c4c in main (argc=<optimized out>, argv=<optimized out>)
at ../connman-1.38/src/main.c:863
1 year, 11 months
[PATCH] IP Accounting for WiFi Clients
by Aravind Gunasekaran
diff --git a/Makefile.am b/Makefile.am
old mode 100644
new mode 100755
index 5971ca9..44148df
--- a/Makefile.am
+++ b/Makefile.am
@@ -12,7 +12,7 @@ include_HEADERS = include/log.h include/plugin.h \
include/storage.h include/provision.h \
include/session.h include/ipaddress.h include/agent.h \
include/inotify.h include/peer.h include/machine.h \
- include/acd.h include/tethering.h
+ include/acd.h include/tethering.h include/accounting-iptables.h
nodist_include_HEADERS = include/version.h
@@ -152,7 +152,7 @@ src_connmand_wait_online_LDADD = gdbus/
libgdbus-internal.la \
@GLIB_LIBS@ @DBUS_LIBS@
if XTABLES
-src_connmand_SOURCES += src/iptables.c src/firewall-iptables.c
+src_connmand_SOURCES += src/iptables.c src/firewall-iptables.c
src/accounting-iptables.c
src_connmand_LDADD += @XTABLES_LIBS@
endif
diff --git a/include/accounting-iptables.h b/include/accounting-iptables.h
new file mode 100755
index 0000000..3a9cc30
--- /dev/null
+++ b/include/accounting-iptables.h
@@ -0,0 +1,49 @@
+/*
+ *
+ * Connection Manager
+ *
+ * Copyright (C) 2007-2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * 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
+ *
+ */
+
+#ifndef __CONNMAN_ACCOUNTING_IPTABLES_H
+#define __CONNMAN_ACCOUNTING_IPTABLES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct counters {
+ __u64 pcnt, bcnt; /* Packet and byte counters */
+};
+
+struct ip_accounting_info {
+ char *ipv4_address; /* IP address of the client */
+ struct counters egress; /* Transmitted bytes information of the client */
+ struct counters ingress; /* Received bytes information of the client */
+};
+
+int __connman_get_ip_acc_info(struct ip_accounting_info *ip_acc);
+int __connman_add_ip_acc_rules(char *ip_addrs);
+int __connman_delete_ip_acc_rules(char *ip_addrs);
+int __connman_create_ip_acc_chain(char *ifname);
+int __connman_destroy_ip_acc_chain(char *ifname);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CONNMAN_ACCOUNTING_IPTABLES_H */
diff --git a/src/accounting-iptables.c b/src/accounting-iptables.c
new file mode 100755
index 0000000..e5b45d9
--- /dev/null
+++ b/src/accounting-iptables.c
@@ -0,0 +1,367 @@
+/*
+ *
+ * Connection Manager
+ *
+ * Copyright (C) 2007-2013 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * 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
+ *
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.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"
+
+#define CHAIN_PREFIX "connman-"
+#define INGRESS_IP_ACC (CHAIN_PREFIX"INGRESS_IP_ACC")
+#define EGRESS_IP_ACC (CHAIN_PREFIX"EGRESS_IP_ACC")
+
+struct iptables_entry {
+ int type;
+ unsigned int offset;
+ int builtin;
+ int counter_idx;
+ struct ipt_entry *entry;
+ struct ip6t_entry *entry6;
+};
+
+static int get_ingress_ip_acc_info(int type, const char *table_name, const
char *chain, struct ip_accounting_info *ip_acc)
+{
+ GList *chain_head, *chain_tail, *list, *next;
+ char src_ip_addrs[INET_ADDRSTRLEN], dst_ip_addrs[INET_ADDRSTRLEN];
+ struct iptables_entry *entry;
+ int err = 0;
+
+ DBG("%d -t %s -L %s", type, table_name, chain);
+
+ err = __connman_iptables_get_chain(type, table_name, chain, &chain_head,
&chain_tail);
+
+ if (err < 0) {
+ return err;
+ }
+
+ list = chain_head;
+
+ // if no entry
+ if (list == chain_tail->prev) {
+ DBG("No entries found for this %s chain", chain);
+ return 0;
+ }
+
+ // traversing through the entries
+ while (list != chain_tail->prev) {
+ entry = list->data;
+ next = g_list_next(list);
+ // check for IPv4 Entries
+ if (entry->type == AF_INET) {
+ if (inet_ntop(entry->type, &entry->entry->ip.src, &src_ip_addrs,
INET_ADDRSTRLEN) == NULL) {
+ DBG("Invalid ipv4 source address");
+ return -EINVAL;
+ }
+ if (inet_ntop(entry->type, &entry->entry->ip.dst, &dst_ip_addrs,
INET_ADDRSTRLEN) == NULL) {
+ DBG("Invalid ipv4 destination address");
+ return -EINVAL;
+ }
+ DBG("src %s dst %s packets %"PRIu64" ""bytes %"PRIu64, src_ip_addrs,
dst_ip_addrs
+ , (uint64_t)entry->entry->counters.pcnt,
(uint64_t)entry->entry->counters.bcnt);
+
+ if (!strcmp(src_ip_addrs, ip_acc->ipv4_address)) {
+ ip_acc->ingress.pcnt = entry->entry->counters.pcnt;
+ ip_acc->ingress.bcnt = entry->entry->counters.bcnt;
+ break;
+ }
+
+ }
+ list = next;
+ }
+ return 0;
+}
+
+static int get_egress_ip_acc_info(int type, const char *table_name, const
char *chain, struct ip_accounting_info *ip_acc)
+{
+ GList *chain_head, *chain_tail, *list, *next;
+ char src_ip_addrs[INET_ADDRSTRLEN], dst_ip_addrs[INET_ADDRSTRLEN];
+ struct iptables_entry *entry;
+ int err = 0;
+
+ DBG("%d -t %s -L %s", type, table_name, chain);
+
+ err = __connman_iptables_get_chain(type, table_name, chain, &chain_head,
&chain_tail);
+
+ if (err < 0) {
+ return err;
+ }
+
+ list = chain_head;
+
+ // if no entry
+ if (list == chain_tail->prev) {
+ DBG("No entries found for this %s chain", chain);
+ return 0;
+ }
+
+ // traversing through the entries
+ while (list != chain_tail->prev) {
+ entry = list->data;
+ next = g_list_next(list);
+ // check for IPv4 Entries
+ if (entry->type == AF_INET) {
+ if (inet_ntop(entry->type, &entry->entry->ip.src, &src_ip_addrs,
INET_ADDRSTRLEN) == NULL) {
+ DBG("Invalid ipv4 source address");
+ return -EINVAL;
+ }
+ if (inet_ntop(entry->type, &entry->entry->ip.dst, &dst_ip_addrs,
INET_ADDRSTRLEN) == NULL) {
+ DBG("Invalid ipv4 destination address");
+ return -EINVAL;
+ }
+ DBG("src %s dst %s packets %"PRIu64" ""bytes %"PRIu64, src_ip_addrs,
dst_ip_addrs
+ , (uint64_t)entry->entry->counters.pcnt,
(uint64_t)entry->entry->counters.bcnt);
+
+ if (!strcmp(dst_ip_addrs, ip_acc->ipv4_address)) {
+ ip_acc->egress.pcnt = entry->entry->counters.pcnt;
+ ip_acc->egress.bcnt = entry->entry->counters.bcnt;
+ break;
+ }
+
+ }
+ list = next;
+ }
+
+ return 0;
+}
+
+int __connman_get_ip_acc_info(struct ip_accounting_info *ip_acc)
+{
+ char *user_defined_chain;
+ int err = 0;
+
+ // get ip accounting for ingress packets
+ DBG("get ip accounting ingress info for ipv4 address (%s)",
ip_acc->ipv4_address);
+ user_defined_chain = g_strdup_printf("%s", INGRESS_IP_ACC);
+ err = get_ingress_ip_acc_info(2, "filter", user_defined_chain, ip_acc);
+ if (err < 0) {
+ DBG("failed to get ip accounting from ingress chain %s, error:(%d/%s)",
user_defined_chain, -err, strerror(-err));
+ }
+ g_free(user_defined_chain);
+
+ // get ip accounting for egress packets
+ DBG("get ip accounting egress info for ipv4 address (%s)",
ip_acc->ipv4_address);
+ user_defined_chain = g_strdup_printf("%s", EGRESS_IP_ACC);
+ err = get_egress_ip_acc_info(2, "filter", user_defined_chain, ip_acc);
+ if (err < 0) {
+ DBG("failed to get ip accounting from egress chain %s, error:(%d/%s)",
user_defined_chain, -err, strerror(-err));
+ }
+ g_free(user_defined_chain);
+
+ return err;
+}
+
+int __connman_add_ip_acc_rules(char *ip_addrs)
+{
+ char *user_defined_chain = NULL;
+ char *user_defined_rule = NULL;
+ int err = 0;
+
+ // Adding the ingress ip accounting rules
+ DBG("adding ip accounting ingress rules for ipv4 address (%s)", ip_addrs);
+ user_defined_chain = g_strdup_printf("%s", INGRESS_IP_ACC);
+ user_defined_rule = g_strdup_printf("-s %s", ip_addrs);
+ err = __connman_iptables_append(AF_INET, "filter", user_defined_chain,
user_defined_rule);
+ if (err < 0) {
+ DBG("failed to add %s rule in chain %s, error:(%d/%s)",
user_defined_rule, user_defined_chain, -err, strerror(-err));
+ }
+ g_free(user_defined_chain);
+ g_free(user_defined_rule);
+
+ // Adding the egress ip accounting rules
+ DBG("adding ip accounting egress rules for ipv4 address (%s)", ip_addrs);
+ user_defined_chain = g_strdup_printf("%s", EGRESS_IP_ACC);
+ user_defined_rule = g_strdup_printf("-d %s", ip_addrs);
+ err = __connman_iptables_append(AF_INET, "filter", user_defined_chain,
user_defined_rule);
+ if (err < 0) {
+ DBG("failed to add %s rule in chain %s, error:(%d/%s)",
user_defined_rule, user_defined_chain, -err, strerror(-err));
+ }
+ g_free(user_defined_chain);
+ g_free(user_defined_rule);
+
+ //committing the rules to kernel
+ DBG("commit ip accounting changes");
+ err = __connman_iptables_commit(AF_INET, "filter");
+ if (err < 0) {
+ DBG("failed to commit filter rule in chain, error:(%d/%s)", -err,
strerror(-err));
+ }
+
+ return err;
+}
+
+int __connman_delete_ip_acc_rules(char *ip_addrs)
+{
+ char *user_defined_chain = NULL;
+ char *user_defined_rule = NULL;
+ int err = 0;
+
+ // Deleting the ingress ip accounting rules
+ DBG("deleting ip accounting ingress rules for ipv4 address (%s)",
ip_addrs);
+ user_defined_chain = g_strdup_printf("%s", INGRESS_IP_ACC);
+ user_defined_rule = g_strdup_printf("-s %s/32 -j ACCEPT", ip_addrs);
+ err = __connman_iptables_delete(AF_INET, "filter", user_defined_chain,
user_defined_rule);
+ if (err < 0) {
+ DBG("failed to delete %s rule in chain %s, error:(%d/%s)",
user_defined_rule, user_defined_chain, -err, strerror(-err));
+ }
+ g_free(user_defined_chain);
+ g_free(user_defined_rule);
+
+ //Deleting the egress ip accounting rules
+ DBG("deleting ip accounting egress rules for ipv4 address (%s)",
ip_addrs);
+ user_defined_chain = g_strdup_printf("%s", EGRESS_IP_ACC);
+ user_defined_rule = g_strdup_printf("-d %s/32 -j ACCEPT", ip_addrs);
+ err = __connman_iptables_delete(AF_INET, "filter", user_defined_chain,
user_defined_rule);
+ if (err < 0) {
+ DBG("failed to delete %s rule in chain %s, error:(%d/%s)",
user_defined_rule, user_defined_chain, -err, strerror(-err));
+ }
+ g_free(user_defined_chain);
+ g_free(user_defined_rule);
+
+ //committing the rules to kernel
+ DBG("commit ip accounting changes");
+ err = __connman_iptables_commit(AF_INET, "filter");
+ if (err < 0) {
+ DBG("failed to commit filter rule in chain, error:(%d/%s)", -err,
strerror(-err));
+ }
+
+ return err;
+}
+
+int __connman_create_ip_acc_chain(char *ifname)
+{
+ char *user_defined_chain = NULL;
+ char *user_defined_entry = NULL;
+ int err = 0;
+
+ // creating user defined chain for ingress ip accounting
+ DBG("creating ip accounting chain for ingress");
+ user_defined_chain = g_strdup_printf("%s", INGRESS_IP_ACC);
+ err = __connman_iptables_new_chain(AF_INET, "filter", user_defined_chain);
+ if (err < 0) {
+ DBG("failed to create new chain -t filter %s, error:(%d/%s)",
user_defined_chain, -err, strerror(-err));
+ }
+ user_defined_entry = g_strdup_printf("-i %s -j %s", ifname,
user_defined_chain);
+ err = __connman_iptables_insert(AF_INET, "filter", "FORWARD",
user_defined_entry);
+ if (err < 0) {
+ DBG("failed to insert entry -t filter %s, error:(%d/%s)",
user_defined_entry, -err, strerror(-err));
+ }
+ g_free(user_defined_chain);
+ g_free(user_defined_entry);
+
+ // creating user defined chain for egress ip accounting
+ DBG("creating ip accounting chain for egress");
+ user_defined_chain = g_strdup_printf("%s", EGRESS_IP_ACC);
+ err = __connman_iptables_new_chain(AF_INET, "filter", user_defined_chain);
+ if (err < 0) {
+ DBG("failed to create new chain -t filter %s, error:(%d/%s)",
user_defined_chain, -err, strerror(-err));
+ }
+ user_defined_entry = g_strdup_printf("-o %s -j %s", ifname,
user_defined_chain);
+ err = __connman_iptables_insert(AF_INET, "filter", "FORWARD",
user_defined_entry);
+ if (err < 0) {
+ DBG("failed to insert entry -t filter %s, error:(%d/%s)",
user_defined_entry, -err, strerror(-err));
+ }
+ g_free(user_defined_chain);
+ g_free(user_defined_entry);
+
+ //committing the ip accounting chain to kernel
+ DBG("commit ip accounting changes");
+ err = __connman_iptables_commit(AF_INET, "filter");
+ if (err < 0) {
+ DBG("failed to commit filter rule in chain, error:(%d/%s)", -err,
strerror(-err));
+ }
+ return err;
+}
+
+int __connman_destroy_ip_acc_chain(char *ifname)
+{
+ char *user_defined_chain = NULL;
+ char *user_defined_entry = NULL;
+ int err = 0;
+
+ // Deleting user defined chain for ingress ip accounting
+ DBG("deleting ip accounting rules for ingress");
+ user_defined_chain = g_strdup_printf("%s", INGRESS_IP_ACC);
+ user_defined_entry = g_strdup_printf("-i %s -j %s", ifname,
user_defined_chain);
+ // Delete rule, if any
+ err = __connman_iptables_delete(AF_INET, "filter", "FORWARD",
user_defined_entry);
+ if (err < 0) {
+ DBG("failed to delete -D FORWARD %s, error:(%d/%s)", user_defined_entry,
-err, strerror(-err));
+ }
+ // Flush rules, if any
+ DBG("flush ip accounting chain for ingress");
+ err = __connman_iptables_flush_chain(AF_INET, "filter",
user_defined_chain);
+ if (err < 0) {
+ DBG("failed to flush chain -F %s, error:(%d/%s)", user_defined_chain,
-err, strerror(-err));
+ }
+ // Delete the chain
+ DBG("deleting ip accounting chain for ingress");
+ err = __connman_iptables_delete_chain(AF_INET, "filter",
user_defined_chain);
+ if (err < 0) {
+ DBG("failed to delete chain -X %s, error:(%d/%s)", user_defined_chain,
-err, strerror(-err));
+ }
+ g_free(user_defined_chain);
+ g_free(user_defined_entry);
+
+ // Deleting user defined chain for egress ip accounting
+ DBG("deleting ip accounting rules for egress");
+ user_defined_chain = g_strdup_printf("%s", EGRESS_IP_ACC);
+ user_defined_entry = g_strdup_printf("-o %s -j %s", ifname,
user_defined_chain);
+ // Delete rule, if any
+ err = __connman_iptables_delete(AF_INET, "filter", "FORWARD",
user_defined_entry);
+ if (err < 0) {
+ DBG("failed to delete -D FORWARD %s, error:(%d/%s)", user_defined_entry,
-err, strerror(-err));
+ }
+ // Flush rules, if any
+ DBG("flush ip accounting chain for egress");
+ err = __connman_iptables_flush_chain(AF_INET, "filter",
user_defined_chain);
+ if (err < 0) {
+ DBG("failed to flush chain -F %s, error:(%d/%s)", user_defined_chain,
-err, strerror(-err));
+ }
+ // Delete the chain
+ DBG("deleting ip accounting chain for egress");
+ err = __connman_iptables_delete_chain(AF_INET, "filter",
user_defined_chain);
+ if (err < 0) {
+ DBG("Failed to delete chain -X %s: %s", user_defined_chain,
strerror(-err));
+ }
+ g_free(user_defined_chain);
+ g_free(user_defined_entry);
+
+ //committing the rules to kernel
+ DBG("commit ip accounting changes");
+ err = __connman_iptables_commit(AF_INET, "filter");
+ if (err < 0) {
+ DBG("failed to commit filter rule in chain, error:(%d/%s)", -err,
strerror(-err));
+ }
+
+ return err;
+}
diff --git a/src/connman.h b/src/connman.h
old mode 100644
new mode 100755
index 3bdc0dc..3530937
--- a/src/connman.h
+++ b/src/connman.h
@@ -951,6 +951,11 @@ int __connman_iptables_delete(int type,
const char *table_name,
const char *chain,
const char *rule_spec);
+int __connman_iptables_get_chain(int type,
+ const char *table_name,
+ const char *chain,
+ GList **chain_head,
+ GList **chain_tail);
typedef void (*connman_iptables_iterate_chains_cb_t) (const char
*chain_name,
void *user_data);
@@ -1015,6 +1020,8 @@ int __connman_nat_enable(const char *name, const char
*address,
unsigned char prefixlen);
void __connman_nat_disable(const char *name);
+#include <connman/accounting-iptables.h>
+
struct firewall_context;
struct firewall_context *__connman_firewall_create(void);
diff --git a/src/iptables.c b/src/iptables.c
old mode 100644
new mode 100755
index 47ea1c2..f4e0e74
--- a/src/iptables.c
+++ b/src/iptables.c
@@ -3646,6 +3646,48 @@ int __connman_iptables_dump(int type, const char
*table_name)
return 0;
}
+int __connman_iptables_get_chain(int type,
+ const char *table_name,
+ const char *chain,
+ GList **chain_head,
+ GList **chain_tail)
+{
+ GList *list, *next;
+ struct connman_iptables_entry *entry;
+ struct connman_iptables *table = NULL;
+ int builtin;
+
+ table = iptables_init(type, table_name);
+
+ if (!table)
+ return -EINVAL;
+
+ DBG("table %s chain %s", table->name, chain);
+
+ // finding the chain head
+ *chain_head = find_chain_head(table, chain);
+ if (!*chain_head)
+ return -EINVAL;
+
+ // finding the chain tail
+ *chain_tail = find_chain_tail(table, chain);
+ if (!*chain_tail)
+ return -EINVAL;
+
+ entry = (*chain_head)->data;
+ builtin = entry->builtin;
+
+ //if it builtin chain then interate starts from head
+ if (builtin >= 0)
+ list = *chain_head;
+ else
+ list = (*chain_head)->next;
+
+ *chain_head = list;
+
+ return 0;
+}
+
int __connman_iptables_new_chain(int type,
const char *table_name,
const char *chain)
diff --git a/src/manager.c b/src/manager.c
old mode 100644
new mode 100755
index 3bf8f4e..0e1bc7e
--- a/src/manager.c
+++ b/src/manager.c
@@ -227,8 +227,13 @@ static DBusMessage
*get_tethering_clients(DBusConnection *conn,
dbus_message_iter_init_append(reply, &iter);
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
- DBUS_TYPE_STRING_AS_STRING, &array);
-
+ DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+ DBUS_STRUCT_END_CHAR_AS_STRING, &array);
__connman_tethering_list_clients(&array);
dbus_message_iter_close_container(&iter, &array);
diff --git a/src/tethering.c b/src/tethering.c
old mode 100644
new mode 100755
index e2687b6..e80d4c3
--- a/src/tethering.c
+++ b/src/tethering.c
@@ -50,6 +50,8 @@
#define BRIDGE_NAME "tether"
+#define IP_NOT_OFFERED ("not offered")
+
#define DEFAULT_MTU 1500
static char *private_network_primary_dns = NULL;
@@ -61,7 +63,7 @@ static struct connman_ippool *dhcp_ippool = NULL;
static DBusConnection *connection;
static GHashTable *pn_hash;
-static GHashTable *clients_table;
+static GHashTable *added_clients_table, *removed_clients_table;
struct _clients_notify {
int id;
@@ -139,6 +141,64 @@ static void dhcp_server_error(GDHCPServerError error)
}
}
+struct ip_accounting_info *tether_client_create(void)
+{
+ struct ip_accounting_info *tether_client;
+
+ tether_client = g_try_new0(struct ip_accounting_info, 1);
+ if (!tether_client)
+ return NULL;
+
+ tether_client->ipv4_address = g_strdup(IP_NOT_OFFERED);
+ tether_client->ingress.bcnt = 0;
+ tether_client->ingress.bcnt = 0;
+ tether_client->egress.bcnt = 0;
+ tether_client->egress.bcnt = 0;
+ return tether_client;
+}
+
+static void lease_added(unsigned char *mac, uint32_t ipv4)
+{
+ char mac_addr[18];
+ struct in_addr addr;
+ struct ip_accounting_info *teth_cli = NULL;
+
+ snprintf(mac_addr, 18,
+ "%02x:%02x:%02x:%02x:%02x:%02x",
+ mac[0], mac[1], mac[2],
+ mac[3], mac[4], mac[5]);
+
+ addr.s_addr = ipv4;
+ char *ip_addr = inet_ntoa(addr);
+
+ DBG("ipv4 address(%s) leased for client mac(%s)", ip_addr, mac_addr);
+
+ if (true == g_hash_table_lookup_extended(added_clients_table,
+ mac_addr,
+ NULL, &teth_cli)) {
+ if (!strcmp(teth_cli->ipv4_address, g_strdup(IP_NOT_OFFERED))) {
+ // ipv4 address is not offered, it new client
+ DBG("new ip address is offered to new client");
+ g_free(teth_cli->ipv4_address);
+ teth_cli->ipv4_address = g_strdup(ip_addr);
+ __connman_add_ip_acc_rules(teth_cli->ipv4_address);
+ } else if (strcmp(teth_cli->ipv4_address, ip_addr)) {
+ // new ipv4 address is assigned, may be lease expired
+ DBG("new ip address is offered to existing client, may be lease expired");
+ __connman_delete_ip_acc_rules(teth_cli->ipv4_address);
+ g_free(teth_cli->ipv4_address);
+ teth_cli->ipv4_address = g_strdup(ip_addr);
+ __connman_add_ip_acc_rules(teth_cli->ipv4_address);
+ } else {
+ // sometime lease_added is called multiple time and ipv4 address is same
+ DBG("already ip address is offered to this client");
+ }
+ } else {
+ // possibly this situation will not happen
+ DBG("client is not authorised/registered, fatal error!");
+ }
+}
+
static GDHCPServer *dhcp_server_start(const char *bridge,
const char *router, const char *subnet,
const char *start_ip, const char *end_ip,
@@ -167,6 +227,7 @@ static GDHCPServer *dhcp_server_start(const char
*bridge,
g_dhcp_server_set_option(dhcp_server, G_DHCP_ROUTER, router);
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_lease_added_cb(dhcp_server, lease_added);
g_dhcp_server_start(dhcp_server);
@@ -195,9 +256,42 @@ static void unregister_client(gpointer key,
__connman_tethering_client_unregister(addr);
}
+static void tether_client_copy(gpointer key,
+ gpointer value, gpointer user_data)
+{
+ GHashTable *tether_client_table = user_data;
+ struct ip_accounting_info *copy_teth_client;
+ struct ip_accounting_info *tether_client = value;
+
+ copy_teth_client = tether_client_create();
+ if (tether_client) {
+ copy_teth_client->ipv4_address = g_strdup(tether_client->ipv4_address);
+ g_hash_table_insert(tether_client_table, g_strdup(key), copy_teth_client);
+ } else {
+ DBG("could not create tether client entry");
+ }
+}
+
+static void tether_client_destroy(gpointer data)
+{
+ struct ip_accounting_info *tether_client = data;
+
+ g_free(tether_client->ipv4_address);
+ g_free(tether_client);
+}
+
static void unregister_all_clients(void)
{
- g_hash_table_foreach(clients_table, unregister_client, NULL);
+ GHashTable *tether_client_table;
+
+ tether_client_table = g_hash_table_new_full(g_str_hash,
+ g_str_equal,
+ g_free,
+ tether_client_destroy);
+ g_hash_table_foreach(added_clients_table, tether_client_copy,
+ tether_client_table);
+ g_hash_table_foreach(tether_client_table, unregister_client, NULL);
+ g_hash_table_destroy(tether_client_table);
}
int __connman_tethering_set_enabled(void)
@@ -306,6 +400,8 @@ int __connman_tethering_set_enabled(void)
DBG("Cannot setup IPv6 prefix delegation %d/%s", err,
strerror(-err));
+ __connman_create_ip_acc_chain(BRIDGE_NAME);
+
DBG("tethering started");
return 0;
@@ -345,9 +441,60 @@ void __connman_tethering_set_disabled(void)
g_free(private_network_secondary_dns);
private_network_secondary_dns = NULL;
+ __connman_destroy_ip_acc_chain(BRIDGE_NAME);
+
DBG("tethering stopped");
}
+static void append_dict_properties(DBusMessageIter *dict,
+ struct ip_accounting_info *tether_client,
+ char *addr)
+{
+ struct ip_accounting_info *removed_tether_client = NULL;
+
+ connman_dbus_dict_append_basic(dict, "mac", DBUS_TYPE_STRING, &addr);
+
+ connman_dbus_dict_append_basic(dict, "ipv4", DBUS_TYPE_STRING,
+ &tether_client->ipv4_address);
+
+ __connman_get_ip_acc_info(tether_client);
+
+ connman_dbus_dict_append_basic(dict, "tx-bytes", DBUS_TYPE_UINT64,
+ &tether_client->ingress.bcnt);
+
+ connman_dbus_dict_append_basic(dict, "rx-bytes", DBUS_TYPE_UINT64,
+ &tether_client->egress.bcnt);
+
+ removed_tether_client = g_hash_table_lookup(removed_clients_table, addr);
+ if (removed_tether_client) {
+ connman_dbus_dict_append_basic(dict, "previous tx-bytes",
+ DBUS_TYPE_UINT64,
+ &removed_tether_client->ingress.bcnt);
+ connman_dbus_dict_append_basic(dict, "previous rx-bytes",
+ DBUS_TYPE_UINT64,
+ &removed_tether_client->egress.bcnt);
+ } else {
+ DBG("no history found for this client");
+ }
+}
+
+static void append_struct_ip_acc_info(char *addr,
+ struct ip_accounting_info *tether_client,
+ DBusMessageIter *iter)
+{
+ DBusMessageIter entry, dict;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT, NULL, &entry);
+
+ connman_dbus_dict_open(&entry, &dict);
+
+ append_dict_properties(&dict, tether_client, addr);
+
+ connman_dbus_dict_close(&entry, &dict);
+
+ dbus_message_iter_close_container(iter, &entry);
+}
+
static void append_client(gpointer key, gpointer value,
gpointer user_data)
{
@@ -360,7 +507,11 @@ static void append_client(gpointer key, gpointer value,
void __connman_tethering_list_clients(DBusMessageIter *array)
{
- g_hash_table_foreach(clients_table, append_client, array);
+ DBG("found %d clients in hash table",
+ g_hash_table_size(added_clients_table));
+
+ g_hash_table_foreach(added_clients_table, append_struct_ip_acc_info,
+ array);
}
static void setup_tun_interface(unsigned int flags, unsigned change,
@@ -494,7 +645,7 @@ static gboolean client_send_changed(gpointer data)
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
DBUS_TYPE_STRING_AS_STRING, &array);
- g_hash_table_foreach(clients_table, append_client, &array);
+ g_hash_table_foreach(added_clients_table, append_client, &array);
dbus_message_iter_close_container(&iter, &array);
@@ -631,14 +782,68 @@ int __connman_private_network_release(const char
*path)
void __connman_tethering_client_register(const char *addr)
{
- g_hash_table_insert(clients_table, g_strdup(addr), NULL);
+ struct ip_accounting_info *tether_client;
+
+ DBG("adding %s mac address in hash table", addr);
+
+ tether_client = tether_client_create();
+
+ if (tether_client)
+ g_hash_table_insert(added_clients_table, g_strdup(addr), tether_client);
+ else
+ DBG("could not create tether client entry");
+
+ DBG("hash table size %d", g_hash_table_size(added_clients_table));
+
client_added(addr);
}
void __connman_tethering_client_unregister(const char *addr)
{
- client_removed(addr);
- g_hash_table_remove(clients_table, addr);
+ struct ip_accounting_info *existing_tether_client;
+ struct ip_accounting_info *old_tether_client;
+ struct ip_accounting_info *tether_client;
+
+ DBG("removing %s mac address from hash table", addr);
+
+ existing_tether_client = g_hash_table_lookup(added_clients_table, addr);
+ if (existing_tether_client) {
+ if (strcmp(existing_tether_client->ipv4_address,
+ g_strdup(IP_NOT_OFFERED))) {
+ __connman_get_ip_acc_info(existing_tether_client);
+ __connman_delete_ip_acc_rules(existing_tether_client->ipv4_address);
+ old_tether_client = g_hash_table_lookup(removed_clients_table, addr);
+ if (old_tether_client) {
+ DBG("replacing existing client in hash table");
+ tether_client = tether_client_create();
+ if (tether_client) {
+ tether_client->ingress.bcnt = old_tether_client->ingress.bcnt
+ + existing_tether_client->ingress.bcnt;
+ tether_client->egress.bcnt = old_tether_client->egress.bcnt
+ + existing_tether_client->egress.bcnt;
+ g_hash_table_replace(removed_clients_table,
+ g_strdup(addr),
+ tether_client);
+ } else
+ DBG("could not create tether client entry");
+ } else {
+ DBG("adding client in hash table");
+ tether_client = tether_client_create();
+ if (tether_client) {
+ tether_client->ingress.bcnt = existing_tether_client->ingress.bcnt;
+ tether_client->egress.bcnt = existing_tether_client->egress.bcnt;
+ g_hash_table_insert(removed_clients_table,
+ g_strdup(addr),
+ tether_client);
+ } else
+ DBG("could not create tether client entry");
+ }
+ } else
+ DBG("ip address not offered for %s ", addr);
+ g_hash_table_remove(added_clients_table, addr);
+ client_removed(addr);
+ } else
+ DBG("could not remove client from hash table");
}
int __connman_tethering_init(void)
@@ -654,8 +859,11 @@ int __connman_tethering_init(void)
pn_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
NULL, remove_private_network);
- clients_table = g_hash_table_new_full(g_str_hash, g_str_equal,
- g_free, NULL);
+ added_clients_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, tether_client_destroy);
+
+ removed_clients_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+ g_free, tether_client_destroy);
clients_notify = g_new0(struct _clients_notify, 1);
clients_notify->remove = g_hash_table_new_full(g_str_hash, g_str_equal,
@@ -685,8 +893,11 @@ void __connman_tethering_cleanup(void)
g_free(clients_notify);
clients_notify = NULL;
- g_hash_table_destroy(clients_table);
- clients_table = NULL;
+ g_hash_table_destroy(added_clients_table);
+ added_clients_table = NULL;
+
+ g_hash_table_destroy(removed_clients_table);
+ removed_clients_table = NULL;
dbus_connection_unref(connection);
}
--
Thanks,
Aravind
1 year, 11 months