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