@@ -18,12 +18,6 @@ static int compare_ap_interfaces(const void *a, const void *b)
1818 return strcmp (name_a , name_b );
1919}
2020
21- /* Comparison function for sorting AP interfaces by name (descending) */
22- static int compare_ap_interfaces_reverse (const void * a , const void * b )
23- {
24- return - compare_ap_interfaces (a , b );
25- }
26-
2721struct lyd_node * wifi_ap_get_radio (struct lyd_node * cif ) {
2822 struct lyd_node * wifi = lydx_get_child (cif , "wifi" );
2923 if (wifi ) {
@@ -221,9 +215,7 @@ int wifi_ap_del_iface(struct lyd_node *dif, struct lyd_node *cif, struct dagger
221215{
222216 const char * ifname , * radio ;
223217 struct lyd_node * wifi ;
224- struct ly_set * ap_interfaces = NULL ;
225218 FILE * iw ;
226- int rc ;
227219 bool is_last_ap = false;
228220
229221 ifname = lydx_get_cattr (dif , "name" );
@@ -234,41 +226,81 @@ int wifi_ap_del_iface(struct lyd_node *dif, struct lyd_node *cif, struct dagger
234226 radio = lydx_get_cattr (wifi , "radio" );
235227 ERROR ("Found radio: %s for interface %s" , radio , ifname );
236228 if (radio ) {
237- /* Find all wifi-ap interfaces from diff config to determine position */
238- rc = lyd_find_xpath (dif , "../interface[derived-from-or-self(type, 'infix-if-type:wifi-ap') and wifi/radio = current()/wifi/radio]" , & ap_interfaces );
239- ERROR ("XPath result: rc=%d, count=%u" , rc , ap_interfaces ? ap_interfaces -> count : 0 );
240- if (rc == LY_SUCCESS && ap_interfaces && ap_interfaces -> count > 0 ) {
241- /* Sort interfaces by name in REVERSE order for deletion (ap2, ap1, ap0) */
242- qsort (ap_interfaces -> dnodes , ap_interfaces -> count , sizeof (struct lyd_node * ), compare_ap_interfaces_reverse );
243- /* Find our position - last one (ap0) restores radio, others delete virtual */
244- ERROR ("Searching for %s in %d AP interfaces" , ifname , ap_interfaces -> count );
245- for (uint32_t i = 0 ; i < ap_interfaces -> count ; i ++ ) {
246- const char * ap_ifname = lydx_get_cattr (ap_interfaces -> dnodes [i ], "name" );
247- ERROR (" AP[%d]: %s (comparing with %s)" , i , ap_ifname , ifname );
229+ struct lyd_node * iface , * sibling ;
230+ struct lyd_node * * matching ;
231+ uint32_t match_count = 0 , alloc_count = 8 ;
232+
233+ /* Iterate through sibling interfaces, filter for
234+ * non-created wifi-ap interfaces with matching radio.
235+ */
236+ matching = calloc (alloc_count , sizeof (struct lyd_node * ));
237+ if (!matching )
238+ goto skip_position_check ;
239+
240+ sibling = lyd_first_sibling (dif );
241+ LYX_LIST_FOR_EACH (sibling , iface , "interface" ) {
242+ struct lyd_node * iface_radio_node ;
243+ const char * iface_radio ;
244+
245+ /* Skip created interfaces - only consider deleted/modified */
246+ if (lydx_get_op (iface ) == LYDX_OP_CREATE )
247+ continue ;
248+
249+ /* Check wifi/radio to identify wifi-ap interfaces */
250+ iface_radio_node = lydx_get_descendant (lyd_child (iface ), "wifi" , "radio" , NULL );
251+ if (!iface_radio_node )
252+ continue ;
253+
254+ iface_radio = lyd_get_value (iface_radio_node );
255+ if (!iface_radio || strcmp (iface_radio , radio ))
256+ continue ;
257+
258+ if (match_count >= alloc_count ) {
259+ alloc_count *= 2 ;
260+ matching = realloc (matching , alloc_count * sizeof (struct lyd_node * ));
261+ }
262+ matching [match_count ++ ] = iface ;
263+ }
264+
265+ ERROR ("Found %u non-created interfaces matching radio %s" , match_count , radio );
266+ if (match_count > 0 ) {
267+ /* Sort in normal order to find the last AP (renamed radio) */
268+ qsort (matching , match_count , sizeof (struct lyd_node * ), compare_ap_interfaces );
269+
270+ for (uint32_t i = 0 ; i < match_count ; i ++ ) {
271+ const char * ap_ifname = lydx_get_cattr (matching [i ], "name" );
272+ ERROR (" AP[%d]: %s" , i , ap_ifname );
248273 if (!strcmp (ap_ifname , ifname )) {
249- is_last_ap = (i == ap_interfaces -> count - 1 );
250- ERROR (" MATCH! Interface %s is at position %d of %d, is_last_ap=%d" , ifname , i , ap_interfaces -> count , is_last_ap );
274+ is_last_ap = (i == match_count - 1 );
275+ ERROR (" MATCH! is_last_ap=%d" , is_last_ap );
251276 break ;
252277 }
253278 }
254- ERROR ("After loop: is_last_ap=%d" , is_last_ap );
255- ly_set_free (ap_interfaces , NULL );
256279 }
280+
281+ free (matching );
257282 }
258283 }
284+ skip_position_check :
259285
260286 iw = dagger_fopen_net_exit (net , ifname , NETDAG_EXIT_POST , "exit-iw.sh" );
261287
262288 if (is_last_ap ) {
263- /* Last AP in deletion order (originally first: wifi0-ap0) - restore original radio name */
264- fprintf (iw , "# Last AP to delete (wifi0-ap0) , restore original radio name\n" );
289+ /* Last AP (e.g., wifi0-ap2) is the renamed radio - restore original radio name and MAC */
290+ fprintf (iw , "# Last AP is renamed radio , restore original radio name and MAC \n" );
265291 fprintf (iw , "logger -t confd -p daemon.info \"Restoring radio name from %s to %s\"\n" , ifname , radio );
266292 fprintf (iw , "ip link set dev %s down\n" , ifname );
267293 fprintf (iw , "ip link property del dev %s altname %s 2>/dev/null || true\n" , ifname , radio );
268294 fprintf (iw , "ip link set dev %s name %s\n" , ifname , radio );
295+ /* Restore original MAC address from permaddr */
296+ fprintf (iw , "permaddr=$(ip -d -j link show dev %s | jq -rM '.[].permaddr // empty')\n" , radio );
297+ fprintf (iw , "if [ -n \"$permaddr\" ]; then\n" );
298+ fprintf (iw , " logger -t confd -p daemon.info \"Restoring original MAC $permaddr on %s\"\n" , radio );
299+ fprintf (iw , " ip link set dev %s address $permaddr\n" , radio );
300+ fprintf (iw , "fi\n" );
269301 } else {
270- /* Subsequent AP interface - delete virtual interface */
271- fprintf (iw , "# Additional AP interface, delete virtual interface \n" );
302+ /* Not last AP - delete virtual interface */
303+ fprintf (iw , "# Virtual AP interface, delete it \n" );
272304 fprintf (iw , "logger -t confd -p daemon.info \"Deleting virtual AP interface %s\"\n" , ifname );
273305 fprintf (iw , "iw dev %s del\n" , ifname );
274306 }
@@ -313,11 +345,11 @@ int wifi_ap_add_iface(struct lyd_node *cif,struct dagger *net)
313345 qsort (ap_interfaces -> dnodes , ap_interfaces -> count , sizeof (struct lyd_node * ), compare_ap_interfaces );
314346
315347 /* Find our position in the AP list */
316- bool is_first_ap = false;
317- const char * first_ap_name = lydx_get_cattr ( ap_interfaces -> dnodes [ 0 ], "name" ) ;
348+ bool is_last_ap = false;
349+ uint32_t last_idx = ap_interfaces -> count - 1 ;
318350 for (uint32_t i = 0 ; i < ap_interfaces -> count ; i ++ ) {
319351 if (ap_interfaces -> dnodes [i ] == cif ) {
320- is_first_ap = (i == 0 );
352+ is_last_ap = (i == last_idx );
321353 /* If not first, add dependency to previous AP for sequential creation */
322354 if (i > 0 ) {
323355 const char * prev_ap_name = lydx_get_cattr (ap_interfaces -> dnodes [i - 1 ], "name" );
@@ -331,18 +363,18 @@ int wifi_ap_add_iface(struct lyd_node *cif,struct dagger *net)
331363
332364 iw = dagger_fopen_net_init (net , ifname , NETDAG_INIT_PRE , "init-iw.sh" );
333365
334- if (is_first_ap ) {
335- /* First AP interface - rename radio to AP name and preserve radio name as altname */
336- fprintf (iw , "# First AP interface, rename radio to AP name\n" );
366+ if (is_last_ap ) {
367+ /* Last AP interface - rename radio to AP name and preserve radio name as altname */
368+ fprintf (iw , "# Last AP interface, rename radio to AP name\n" );
337369 fprintf (iw , "logger -t confd -p daemon.info \"Renaming radio %s to AP interface %s\"\n" , radio , ifname );
338370 fprintf (iw , "ip link set dev %s down\n" , radio );
339371 fprintf (iw , "ip link set dev %s name %s\n" , radio , ifname );
340372 fprintf (iw , "ip link property add dev %s altname %s\n" , ifname , radio );
341373 } else {
342- /* Subsequent AP interface - create virtual interface on the renamed radio */
343- fprintf (iw , "# Additional AP interface, create virtual interface \n" );
344- fprintf (iw , "logger -t confd -p daemon.info \"Creating virtual AP interface %s on %s (radio %s) \"\n" , ifname , first_ap_name , radio );
345- fprintf (iw , "iw dev %s interface add %s type __ap\n" , first_ap_name , ifname );
374+ /* Not last AP - create virtual interface on the radio */
375+ fprintf (iw , "# Virtual AP interface, create on radio \n" );
376+ fprintf (iw , "logger -t confd -p daemon.info \"Creating virtual AP interface %s on %s\"\n" , ifname , radio );
377+ fprintf (iw , "iw dev %s interface add %s type __ap\n" , radio , ifname );
346378 }
347379
348380 fclose (iw );
@@ -398,16 +430,12 @@ int wifi_ap_gen(struct lyd_node *cif, struct dagger *net)
398430 /* Sort interfaces by name to ensure consistent ordering */
399431 qsort (ap_interfaces -> dnodes , ap_interfaces -> count , sizeof (struct lyd_node * ), compare_ap_interfaces );
400432
401- /* The first AP interface becomes the main interface (radio gets renamed to this) */
402- ap_interface = ap_interfaces -> dnodes [0 ];
433+ /* The last AP interface becomes the main interface (radio gets renamed to this) */
434+ ap_interface = ap_interfaces -> dnodes [ap_interfaces -> count - 1 ];
403435 main_interface_name = lydx_get_cattr (ap_interface , "name" );
404436
405- /* Radio depends on first AP interface since radio will be renamed to it */
406- dagger_add_dep (& confd .netdag , ifname , main_interface_name );
407- ERROR ("Adding dependency: radio %s depends on first AP %s" , ifname , main_interface_name );
408-
409437 ERROR ("Generating hostapd config for radio %s, main interface %s with %d total APs" ,
410- ifname , ifname , ap_interfaces -> count );
438+ ifname , main_interface_name , ap_interfaces -> count );
411439
412440 /* Clean up any existing AP configuration */
413441 erasef (HOSTAPD_SUPPLICANT_CONF , ifname );
@@ -443,7 +471,7 @@ int wifi_ap_gen(struct lyd_node *cif, struct dagger *net)
443471 else
444472 fprintf (hostapd_conf , "ieee80211ac=1\n" );
445473
446- /* Configure first AP interface as main SSID */
474+ /* Configure last AP interface as main SSID (it's the renamed radio) */
447475 struct lyd_node * main_wifi = lydx_get_child (ap_interface , "wifi" );
448476 if (main_wifi ) {
449477 const char * ssid = lydx_get_cattr (main_wifi , "ssid" );
@@ -472,8 +500,8 @@ int wifi_ap_gen(struct lyd_node *cif, struct dagger *net)
472500 fputs ("ignore_broadcast_ssid=0\n" , hostapd_conf );
473501 }
474502
475- /* Add additional AP interfaces as BSS entries */
476- for (uint32_t i = 1 ; i < ap_interfaces -> count ; i ++ ) {
503+ /* Add other AP interfaces as BSS entries (all except the last one) */
504+ for (uint32_t i = 0 ; i < ap_interfaces -> count - 1 ; i ++ ) {
477505 ap_interface = ap_interfaces -> dnodes [i ];
478506 const char * ap_ifname = lydx_get_cattr (ap_interface , "name" );
479507 struct lyd_node * ap_wifi = lydx_get_child (ap_interface , "wifi" );
0 commit comments