[PATCH] connman : Introduce softap with ip feature to connman

Shailesh Rathod/LGSI GPOS Dev <[email protected]> Mon, 2 Jun 2025 06:33:02 +0000
Newsgroups dev.linux.lists.connman
Message-ID <SE1P216MB2647222FA0265D346F4C13C1F062A@SE1P216MB2647.KORP216.PROD.OUTLOOK.COM>
From: Shailesh Rathod<[email protected]>
Date: Mon, 21 Apr 2025 18:16:09 +0900

Introduce softap with ip feature to connman

By default, ConnMan supports tethering with a passphrase.
However, it does not support configuring tethering with a
specific IP address and channel.For certain applications
 it is essential to set a specific IP address for the SoftAP.

To enable support for specifying the IP address
and channel, new properties TetheringIPAddress and
TetheringChannel should be added.

Signed-off-by: Shailesh Rathod<[email protected]>
---
 src/connman.h    |  15 ++++++
 src/technology.c | 122 +++++++++++++++++++++++++++++++++++++++---
 src/tethering.c  | 136 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 267 insertions(+), 6 deletions(-)

diff --git a/src/connman.h b/src/connman.h
index 32ba559..4241a58 100644
--- a/src/connman.h
+++ b/src/connman.h
@@ -676,9 +676,24 @@ bool __connman_config_address_provisioned(const char *address,

 #include <connman/tethering.h>

+struct connman_station_info {
+        bool is_connected;
+        char *path;
+        char *type;
+        char ip[64];
+        char mac[32];
+        char hostname[64];
+};
+
+
 int __connman_tethering_init(void);
 void __connman_tethering_cleanup(void);

+GHashTable *__connman_tethering_get_sta_hash();
+
+void __connman_tethering_list_clients(DBusMessageIter *array);
+int __connman_tethering_set_enabled_with_ip(const char *ip);
+
 const char *__connman_tethering_get_bridge(void);
 int __connman_tethering_set_enabled(void);
 void __connman_tethering_set_disabled(void);
diff --git a/src/technology.c b/src/technology.c
index 270d83d..719c680 100644
--- a/src/technology.c
+++ b/src/technology.c
@@ -69,6 +69,8 @@ struct connman_technology {
 	char *tethering_ident;
 	char *tethering_passphrase;
 	int tethering_freq;
+	char *tethering_ipaddress;
+    unsigned int tethering_channel;

 	bool enable_persistent; /* Save the tech state */

@@ -195,6 +197,16 @@ static void technology_save(struct connman_technology *technology)
 		g_free(enc);
 	}

+	if (technology->tethering_channel)
+                g_key_file_set_integer(keyfile, identifier,
+                                    "Tethering.Channel",
+                                    technology->tethering_channel);
+
+    if (technology->tethering_ipaddress)
+                g_key_file_set_string(keyfile, identifier,
+                                    "Tethering.IP",
+                                     technology->tethering_ipaddress);
+
 	if (technology->tethering_freq == 0)
 		technology->tethering_freq = 2412;

@@ -225,18 +237,25 @@ int connman_technology_tethering_notify(struct connman_technology *technology,
 							bool enabled)
 {
 	int err;
+	const char *ip;

 	DBG("technology %p enabled %u", technology, enabled);

 	if (technology->tethering == enabled)
 		return -EALREADY;

-	if (enabled) {
-		err = __connman_tethering_set_enabled();
-		if (err < 0)
-			return err;
-	} else
-		__connman_tethering_set_disabled();
+	ip = technology->tethering_ipaddress;
+
+    if (enabled) {
+        if (!ip || strlen(ip) == 0)
+            err = __connman_tethering_set_enabled();
+        else
+            err = __connman_tethering_set_enabled_with_ip(ip);
+
+        if (err < 0)
+            return err;
+    } else
+        __connman_tethering_set_disabled();

 	technology->tethering = enabled;
 	tethering_changed(technology);
@@ -417,6 +436,17 @@ bool connman_technology_get_wifi_tethering(const struct connman_technology *tech
 	return true;
 }

+unsigned int connman_technology_get_wifi_tethering_channel(void)
+{
+        struct connman_technology *technology;
+
+        technology = technology_find(CONNMAN_SERVICE_TYPE_WIFI);
+        if (!technology)
+                return 0;
+
+        return technology->tethering_channel;
+}
+
 static void free_rfkill(gpointer data)
 {
 	struct connman_rfkill *rfkill = data;
@@ -479,6 +509,12 @@ static void technology_load(struct connman_technology *technology)

 	enc = g_key_file_get_string(keyfile,
 				identifier, "Tethering.Passphrase", NULL);
+
+	technology->tethering_channel = g_key_file_get_integer(keyfile,
+                identifier, "Tethering.Channel", NULL);
+
+    technology->tethering_ipaddress = g_key_file_get_string(keyfile,
+                identifier, "Tethering.IP", NULL);
 	if (enc) {
 		technology->tethering_passphrase = g_strcompress(enc);
 		g_free(enc);
@@ -597,6 +633,15 @@ static void append_properties(DBusMessageIter *iter,
 		connman_dbus_dict_append_basic(&dict, "TetheringPassphrase",
 					DBUS_TYPE_STRING,
 					&technology->tethering_passphrase);
+	if (technology->tethering_ipaddress)
+        connman_dbus_dict_append_basic(&dict, "TetheringIPAddress",
+                    DBUS_TYPE_STRING,
+                    &technology->tethering_ipaddress);
+
+    if (technology->tethering_channel)
+        connman_dbus_dict_append_basic(&dict, "TetheringChannel",
+                    DBUS_TYPE_UINT32,
+                    &technology->tethering_channel);

 	connman_dbus_dict_append_basic(&dict, "TetheringFreq",
 				DBUS_TYPE_INT32,
@@ -1016,6 +1061,55 @@ static DBusMessage *set_property(DBusConnection *conn,
 					DBUS_TYPE_STRING,
 					&technology->tethering_passphrase);
 		}
+	} else if (g_str_equal(name, "TetheringChannel")) {
+                dbus_uint32_t channel;
+
+                if (type != DBUS_TYPE_UINT32)
+                        return __connman_error_invalid_arguments(msg);
+
+                dbus_message_iter_get_basic(&value, &channel);
+
+                if (technology->type != CONNMAN_SERVICE_TYPE_WIFI)
+                        return __connman_error_not_supported(msg);
+
+                if (channel == 0 || channel > 13)
+                        return __connman_error_invalid_arguments(msg);
+
+                if (technology->tethering_channel != channel) {
+
+                        technology->tethering_channel = channel;
+                        technology_save(technology);
+
+                        connman_dbus_property_changed_basic(technology->path,
+                                        CONNMAN_TECHNOLOGY_INTERFACE,
+                                        "TetheringChannel",
+                                        DBUS_TYPE_UINT32,
+                                        &technology->tethering_channel);
+                }
+        } else if (g_str_equal(name, "TetheringIPAddress")) {
+                const char *str = NULL;
+
+                dbus_message_iter_get_basic(&value, &str);
+
+                if (technology->type != CONNMAN_SERVICE_TYPE_WIFI)
+                        return __connman_error_not_supported(msg);
+
+                if (!str || strlen(str) == 0) {
+                        g_free(technology->tethering_ipaddress);
+                        technology->tethering_ipaddress = NULL;
+                        return __connman_error_invalid_arguments(msg);
+                }
+                if (g_strcmp0(technology->tethering_ipaddress, str) != 0) {
+                        g_free(technology->tethering_ipaddress);
+                        technology->tethering_ipaddress = g_strdup(str);
+                        technology_save(technology);
+
+                        connman_dbus_property_changed_basic(technology->path,
+                                        CONNMAN_TECHNOLOGY_INTERFACE,
+                                        "TetheringIPAddress",
+                                        DBUS_TYPE_STRING,
+                                        &technology->tethering_ipaddress);
+                }
 	} else if (g_str_equal(name, "TetheringFreq")) {
 		dbus_int32_t freq;

@@ -1257,6 +1351,7 @@ static void technology_put(struct connman_technology *technology)
 	g_free(technology->regdom);
 	g_free(technology->tethering_ident);
 	g_free(technology->tethering_passphrase);
+	g_free(technology->tethering_ipaddress);
 	g_free(technology);
 }

@@ -1935,3 +2030,18 @@ void __connman_technology_cleanup(void)

 	g_free(global_regdom);
 }
+
+void __connman_technology_sta_count_changed(enum connman_service_type type, int stacount)
+{
+        struct connman_technology *technology;
+
+        technology = technology_find(type);
+        if (technology == NULL)
+                return;
+
+        connman_dbus_property_changed_basic(technology->path,
+                                        CONNMAN_TECHNOLOGY_INTERFACE, "StaCount",
+                                        DBUS_TYPE_INT32, &stacount);
+
+
+}
\ No newline at end of file
diff --git a/src/tethering.c b/src/tethering.c
index f930a26..7039037 100644
--- a/src/tethering.c
+++ b/src/tethering.c
@@ -49,6 +49,7 @@
 #endif

 #define BRIDGE_NAME "tether"
+#define SUBNET_MASK_24 "255.255.255.0"

 #define DEFAULT_MTU	1500

@@ -200,6 +201,7 @@ static void unregister_all_clients(void)
 	g_hash_table_foreach(clients_table, unregister_client, NULL);
 }

+
 int __connman_tethering_set_enabled(void)
 {
 	int index;
@@ -363,6 +365,140 @@ void __connman_tethering_list_clients(DBusMessageIter *array)
 	g_hash_table_foreach(clients_table, append_client, array);
 }

+static char *get_ip(uint32_t ip)
+{
+        struct in_addr addr;
+
+        addr.s_addr = htonl(ip);
+
+        return g_strdup(inet_ntoa(addr));
+}
+
+int __connman_tethering_set_enabled_with_ip(const char *ip)
+{
+        int index;
+        int err;
+        const char *gateway;
+        const char *broadcast;
+        const char *subnet_mask;
+        const char *start_ip;
+        const char *end_ip;
+        const char *dns;
+        unsigned char prefixlen;
+        char **ns;
+
+        DBG("enabled %d", tethering_enabled + 1);
+
+        if (__sync_fetch_and_add(&tethering_enabled, 1) != 0)
+                return 0;
+
+        err = __connman_bridge_create(BRIDGE_NAME);
+        if (err < 0) {
+                __sync_fetch_and_sub(&tethering_enabled, 1);
+                return 0;
+        }
+
+        index = connman_inet_ifindex(BRIDGE_NAME);
+
+        __connman_ippool_newaddr(index, ip, 24);
+
+        struct in_addr inp;
+        uint32_t start, mask;
+
+        if (inet_aton(ip, &inp) == 0)
+                return 0;
+
+        start = ntohl(inp.s_addr);
+        mask = ~(0xffffffff >> 24);
+
+        start = start & mask;
+
+        gateway = ip;
+        broadcast = get_ip(start + 255);
+        subnet_mask = SUBNET_MASK_24;
+        start_ip = get_ip(start + 2);
+        end_ip = get_ip(start + 254);
+
+        err = __connman_bridge_enable(BRIDGE_NAME, gateway,
+                        connman_ipaddress_calc_netmask_len(subnet_mask),
+                        broadcast);
+        if (err < 0 && err != -EALREADY) {
+                __connman_ippool_free(dhcp_ippool);
+                __connman_ippool_deladdr(index, ip, 24);
+                __connman_bridge_remove(BRIDGE_NAME);
+                __sync_fetch_and_sub(&tethering_enabled, 1);
+                err = -EADDRNOTAVAIL;
+                goto clean;
+        }
+
+        ns = connman_setting_get_string_list("FallbackNameservers");
+        if (ns) {
+                if (ns[0]) {
+                        g_free(private_network_primary_dns);
+                        private_network_primary_dns = g_strdup(ns[0]);
+                }
+                if (ns[1]) {
+                        g_free(private_network_secondary_dns);
+                        private_network_secondary_dns = g_strdup(ns[1]);
+                }
+
+                DBG("Fallback ns primary %s secondary %s",
+                        private_network_primary_dns,
+                        private_network_secondary_dns);
+        }
+
+        dns = gateway;
+        if (__connman_dnsproxy_add_listener(index) < 0) {
+                connman_error("Can't add listener %s to DNS proxy",
+                                                                BRIDGE_NAME);
+                dns = private_network_primary_dns;
+                DBG("Serving %s nameserver to clients", dns);
+        }
+
+        tethering_dhcp_server = dhcp_server_start(BRIDGE_NAME,
+                                                gateway, subnet_mask,
+                                                start_ip, end_ip,
+                                                24 * 3600, dns);
+        if (!tethering_dhcp_server) {
+                __connman_bridge_disable(BRIDGE_NAME);
+                __connman_ippool_free(dhcp_ippool);
+                __connman_ippool_deladdr(index, ip, 24);
+                __connman_bridge_remove(BRIDGE_NAME);
+                __sync_fetch_and_sub(&tethering_enabled, 1);
+                err = -EOPNOTSUPP;
+                goto clean;
+        }
+
+        prefixlen = connman_ipaddress_calc_netmask_len(subnet_mask);
+        err = __connman_nat_enable(BRIDGE_NAME, start_ip, prefixlen);
+        if (err < 0) {
+                connman_error("Cannot enable NAT %d/%s", err, strerror(-err));
+                dhcp_server_stop(tethering_dhcp_server);
+                __connman_bridge_disable(BRIDGE_NAME);
+                __connman_ippool_free(dhcp_ippool);
+                __connman_ippool_deladdr(index, ip, 24);
+                __connman_bridge_remove(BRIDGE_NAME);
+                __sync_fetch_and_sub(&tethering_enabled, 1);
+                err = -EOPNOTSUPP;
+                goto clean;
+        }
+
+        err = __connman_ipv6pd_setup(BRIDGE_NAME);
+        if (err < 0 && err != -EINPROGRESS)
+                DBG("Cannot setup IPv6 prefix delegation %d/%s", err,
+                        strerror(-err));
+clean:
+        g_free(broadcast);
+        g_free(start_ip);
+        g_free(end_ip);
+        if ((err == -EOPNOTSUPP) || (err == -EADDRNOTAVAIL)){
+                return err;
+        } else {
+                DBG("tethering started");
+                return 0;
+        }
+}
+
 static void setup_tun_interface(unsigned int flags, unsigned change,
 		void *data)
 {