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

Shailesh Rathod/LGSI Connectivity Team <[email protected]> Fri, 11 Jul 2025 11:13:36 +0000
Newsgroups dev.linux.lists.connman
Message-ID <SE1P216MB2647C232928EFD6B3B54BCA8F04BA@SE1P216MB2647.KORP216.PROD.OUTLOOK.COM>
Hello Michael,

Sorry for the delayed response.

I dont think roman patch handles the channel settings and other ip checks (ex.
__connman_tethering_set_enabled_with_ip  function impl)

Let me know your thoughts on our implementation.

Please find the corrected patch.

From 84d64870ddecede4fe687eb1cafcbb4fac87a990 Mon Sep 17 00:00:00 2001
From: "jisoo.cha" <[email protected]>
Date: Mon, 21 Apr 2025 18:16:09 +0900
Subject: [PATCH] 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.
---
 src/connman.h    |  13 +++++
 src/technology.c | 109 +++++++++++++++++++++++++++++++++++++-
 src/tethering.c  | 135 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 256 insertions(+), 1 deletion(-)

diff --git a/src/connman.h b/src/connman.h
index 7ebda7a..85f221e 100644
--- a/src/connman.h
+++ b/src/connman.h
@@ -677,9 +677,22 @@ 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..51f3cf0 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,14 +237,21 @@ 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;
 
+   ip = technology->tethering_ipaddress;
+
    if (enabled) {
-       err = __connman_tethering_set_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
@@ -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,54 @@ 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 +1350,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 +2029,16 @@ 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..2e1e6a4 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
 
@@ -363,6 +364,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)
 {