2 * hostapd / Configuration reloading
3 * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
4 * Copyright (c) 2002-2004, Instant802 Networks, Inc.
5 * Copyright (c) 2005-2006, Devicescape Software, Inc.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
11 * Alternatively, this software may be distributed under the terms of BSD
14 * See README and COPYING for more details.
21 #include "hw_features.h"
24 #include "radius/radius_client.h"
25 #include "ieee802_11.h"
29 #include "vlan_init.h"
30 #include "ieee802_11_auth.h"
31 #include "ieee802_1x.h"
32 #include "accounting.h"
37 * struct hostapd_config_change - Configuration change information
38 * This is for two purposes:
39 * - Storing configuration information in the hostapd_iface during
40 * the asynchronous parts of reconfiguration.
41 * - Passing configuration information for per-station reconfiguration.
43 struct hostapd_config_change
{
44 struct hostapd_data
*hapd
;
45 struct hostapd_config
*newconf
, *oldconf
;
46 struct hostapd_bss_config
*newbss
, *oldbss
;
48 int num_sta_remove
; /* number of STAs that need to be removed */
50 struct hostapd_iface
*hapd_iface
;
51 struct hostapd_data
**new_hapd
, **old_hapd
;
56 static int hostapd_config_reload_sta(struct hostapd_data
*hapd
,
57 struct sta_info
*sta
, void *data
)
59 struct hostapd_config_change
*change
= data
;
60 struct hostapd_bss_config
*newbss
, *oldbss
;
62 u8 reason
= WLAN_REASON_PREV_AUTH_NOT_VALID
;
64 newbss
= change
->newbss
;
65 oldbss
= change
->oldbss
;
68 if (sta
->ssid
== &oldbss
->ssid
) {
69 sta
->ssid
= &newbss
->ssid
;
71 if (newbss
->ssid
.ssid_len
!= oldbss
->ssid
.ssid_len
||
72 os_memcmp(newbss
->ssid
.ssid
, oldbss
->ssid
.ssid
,
73 newbss
->ssid
.ssid_len
) != 0) {
74 /* main SSID was changed - kick STA out */
78 sta
->ssid_probe
= sta
->ssid
;
81 * If MAC ACL configuration has changed, deauthenticate stations that
82 * have been removed from accepted list or have been added to denied
83 * list. If external RADIUS server is used for ACL, all stations are
84 * deauthenticated and they will need to authenticate again. This
85 * limits sudden load on the RADIUS server since the verification will
86 * be done over the time needed for the STAs to reauthenticate
89 if (change
->mac_acl_changed
&&
90 (newbss
->macaddr_acl
== USE_EXTERNAL_RADIUS_AUTH
||
91 !hostapd_allowed_address(hapd
, sta
->addr
, NULL
, 0, NULL
, NULL
,
95 if (newbss
->ieee802_1x
!= oldbss
->ieee802_1x
&&
96 sta
->ssid
== &hapd
->conf
->ssid
)
99 if (newbss
->wpa
!= oldbss
->wpa
)
102 if (!newbss
->wme_enabled
&& (sta
->flags
& WLAN_STA_WME
))
105 if (newbss
->auth_algs
!= oldbss
->auth_algs
&&
106 ((sta
->auth_alg
== WLAN_AUTH_OPEN
&&
107 !(newbss
->auth_algs
& WPA_AUTH_ALG_OPEN
)) ||
108 (sta
->auth_alg
== WLAN_AUTH_SHARED_KEY
&&
109 !(newbss
->auth_algs
& WPA_AUTH_ALG_SHARED
))))
112 if (change
->num_sta_remove
> 0) {
114 reason
= WLAN_REASON_DISASSOC_AP_BUSY
;
118 wpa_printf(MSG_DEBUG
, "STA " MACSTR
" deauthenticated during "
119 "config reloading (reason=%d)",
120 MAC2STR(sta
->addr
), reason
);
121 ieee802_11_send_deauth(hapd
, sta
->addr
, reason
);
122 ap_sta_deauthenticate(hapd
, sta
, reason
);
123 change
->num_sta_remove
--;
130 static void hostapd_reconfig_tx_queue_params(struct hostapd_data
*hapd
,
131 struct hostapd_config
*newconf
,
132 struct hostapd_config
*oldconf
)
135 struct hostapd_tx_queue_params
*o
, *n
;
137 for (i
= 0; i
< NUM_TX_QUEUES
; i
++) {
138 o
= &oldconf
->tx_queue
[i
];
139 n
= &newconf
->tx_queue
[i
];
144 if ((n
->aifs
!= o
->aifs
|| n
->cwmin
!= o
->cwmin
||
145 n
->cwmax
!= o
->cwmax
|| n
->burst
!= o
->burst
) &&
146 hostapd_set_tx_queue_params(hapd
, i
, n
->aifs
, n
->cwmin
,
148 printf("Failed to set TX queue parameters for queue %d"
154 static int hostapd_reconfig_wme(struct hostapd_data
*hapd
,
155 struct hostapd_config
*newconf
,
156 struct hostapd_config
*oldconf
)
158 int beacon_changed
= 0;
160 struct hostapd_wme_ac_params
*o
, *n
;
162 for (i
= 0; i
< sizeof(newconf
->wme_ac_params
) /
163 sizeof(newconf
->wme_ac_params
[0]); i
++) {
164 o
= &oldconf
->wme_ac_params
[i
];
165 n
= &newconf
->wme_ac_params
[i
];
166 if (n
->cwmin
!= o
->cwmin
||
167 n
->cwmax
!= o
->cwmax
||
168 n
->aifs
!= o
->aifs
||
169 n
->txopLimit
!= o
->txopLimit
||
170 n
->admission_control_mandatory
!=
171 o
->admission_control_mandatory
) {
173 hapd
->parameter_set_count
++;
177 return beacon_changed
;
181 static int rate_array_diff(int *a1
, int *a2
)
185 if (a1
== NULL
&& a2
== NULL
)
187 if (a1
== NULL
|| a2
== NULL
)
203 static int hostapd_acl_diff(struct hostapd_bss_config
*a
,
204 struct hostapd_bss_config
*b
)
208 if (a
->macaddr_acl
!= b
->macaddr_acl
||
209 a
->num_accept_mac
!= b
->num_accept_mac
||
210 a
->num_deny_mac
!= b
->num_deny_mac
)
213 for (i
= 0; i
< a
->num_accept_mac
; i
++) {
214 if (os_memcmp(a
->accept_mac
[i
], b
->accept_mac
[i
], ETH_ALEN
) !=
219 for (i
= 0; i
< a
->num_deny_mac
; i
++) {
220 if (os_memcmp(a
->deny_mac
[i
], b
->deny_mac
[i
], ETH_ALEN
) != 0)
229 * reload_iface2 - Part 2 of reload_iface
230 * @hapd_iface: Pointer to hostapd interface data.
232 static void reload_iface2(struct hostapd_iface
*hapd_iface
)
234 struct hostapd_data
*hapd
= hapd_iface
->bss
[0];
235 struct hostapd_config
*newconf
= hapd_iface
->change
->newconf
;
236 struct hostapd_config
*oldconf
= hapd_iface
->change
->oldconf
;
237 int beacon_changed
= hapd_iface
->change
->beacon_changed
;
238 hostapd_iface_cb cb
= hapd_iface
->reload_iface_cb
;
240 if (newconf
->preamble
!= oldconf
->preamble
) {
241 if (hostapd_set_preamble(hapd
, hapd
->iconf
->preamble
))
242 printf("Could not set preamble for kernel driver\n");
246 if (newconf
->beacon_int
!= oldconf
->beacon_int
) {
247 /* Need to change beacon interval if it has changed or if
248 * auto channel selection was used. */
249 if (hostapd_set_beacon_int(hapd
, newconf
->beacon_int
))
250 printf("Could not set beacon interval for kernel "
252 if (newconf
->beacon_int
!= oldconf
->beacon_int
)
256 if (newconf
->cts_protection_type
!= oldconf
->cts_protection_type
)
259 if (newconf
->rts_threshold
> -1 &&
260 newconf
->rts_threshold
!= oldconf
->rts_threshold
&&
261 hostapd_set_rts(hapd
, newconf
->rts_threshold
))
262 printf("Could not set RTS threshold for kernel driver\n");
264 if (newconf
->fragm_threshold
> -1 &&
265 newconf
->fragm_threshold
!= oldconf
->fragm_threshold
&&
266 hostapd_set_frag(hapd
, newconf
->fragm_threshold
))
267 printf("Could not set fragmentation threshold for kernel "
270 hostapd_reconfig_tx_queue_params(hapd
, newconf
, oldconf
);
272 if (hostapd_reconfig_wme(hapd
, newconf
, oldconf
) > 0)
275 ap_list_reconfig(hapd_iface
, oldconf
);
277 hapd_iface
->change
->beacon_changed
= beacon_changed
;
279 hapd_iface
->reload_iface_cb
= NULL
;
285 * reload_iface2_handler - Handler that calls reload_face2
286 * @eloop_data: Stores the struct hostapd_iface for the interface.
289 static void reload_iface2_handler(void *eloop_data
, void *user_ctx
)
291 struct hostapd_iface
*hapd_iface
= eloop_data
;
293 reload_iface2(hapd_iface
);
298 * reload_hw_mode_done - Callback for after the HW mode is setup
299 * @hapd_iface: Pointer to interface data.
300 * @status: Status of the HW mode setup.
302 static void reload_hw_mode_done(struct hostapd_iface
*hapd_iface
, int status
)
304 struct hostapd_data
*hapd
= hapd_iface
->bss
[0];
305 struct hostapd_config_change
*change
= hapd_iface
->change
;
306 struct hostapd_config
*newconf
= change
->newconf
;
311 printf("Failed to select hw_mode.\n");
313 cb
= hapd_iface
->reload_iface_cb
;
314 hapd_iface
->reload_iface_cb
= NULL
;
320 freq
= hostapd_hw_get_freq(hapd
, newconf
->channel
);
321 wpa_printf(MSG_DEBUG
, "Mode: %s Channel: %d Frequency: %d MHz",
322 hostapd_hw_mode_txt(newconf
->hw_mode
),
323 newconf
->channel
, freq
);
325 if (hostapd_set_freq(hapd
, newconf
->hw_mode
, freq
)) {
326 printf("Could not set channel %d (%d MHz) for kernel "
327 "driver\n", newconf
->channel
, freq
);
330 change
->beacon_changed
++;
332 reload_iface2(hapd_iface
);
337 * hostapd_config_reload_iface_start - Start interface reload
338 * @hapd_iface: Pointer to interface data.
339 * @cb: The function to callback when done.
340 * Returns: 0 if it starts successfully; cb will be called when done.
341 * -1 on failure; cb will not be called.
343 static int hostapd_config_reload_iface_start(struct hostapd_iface
*hapd_iface
,
346 struct hostapd_config_change
*change
= hapd_iface
->change
;
347 struct hostapd_config
*newconf
= change
->newconf
;
348 struct hostapd_config
*oldconf
= change
->oldconf
;
349 struct hostapd_data
*hapd
= hapd_iface
->bss
[0];
351 if (hapd_iface
->reload_iface_cb
) {
352 wpa_printf(MSG_DEBUG
,
353 "%s: Interface reload already in progress.",
354 hapd_iface
->bss
[0]->conf
->iface
);
358 hapd_iface
->reload_iface_cb
= cb
;
360 if (newconf
->bridge_packets
!= oldconf
->bridge_packets
&&
361 hapd
->iconf
->bridge_packets
!= INTERNAL_BRIDGE_DO_NOT_CONTROL
&&
362 hostapd_set_internal_bridge(hapd
, hapd
->iconf
->bridge_packets
))
363 printf("Failed to set bridge_packets for kernel driver\n");
365 if (newconf
->channel
!= oldconf
->channel
||
366 newconf
->hw_mode
!= oldconf
->hw_mode
||
367 rate_array_diff(newconf
->supported_rates
,
368 oldconf
->supported_rates
) ||
369 rate_array_diff(newconf
->basic_rates
, oldconf
->basic_rates
)) {
370 hostapd_free_stas(hapd
);
372 if (hostapd_get_hw_features(hapd_iface
)) {
373 printf("Could not read HW feature info from the kernel"
375 hapd_iface
->reload_iface_cb
= NULL
;
379 if (hostapd_select_hw_mode_start(hapd_iface
,
380 reload_hw_mode_done
)) {
381 printf("Failed to start select hw_mode.\n");
382 hapd_iface
->reload_iface_cb
= NULL
;
389 eloop_register_timeout(0, 0, reload_iface2_handler
, hapd_iface
, NULL
);
394 static void hostapd_reconfig_bss(struct hostapd_data
*hapd
,
395 struct hostapd_bss_config
*newbss
,
396 struct hostapd_bss_config
*oldbss
,
397 struct hostapd_config
*oldconf
,
400 struct hostapd_config_change change
;
401 int encr_changed
= 0;
402 struct radius_client_data
*old_radius
;
404 radius_client_flush(hapd
->radius
, 0);
406 if (hostapd_set_dtim_period(hapd
, newbss
->dtim_period
))
407 printf("Could not set DTIM period for kernel driver\n");
409 if (newbss
->ssid
.ssid_len
!= oldbss
->ssid
.ssid_len
||
410 os_memcmp(newbss
->ssid
.ssid
, oldbss
->ssid
.ssid
,
411 newbss
->ssid
.ssid_len
) != 0) {
412 if (hostapd_set_ssid(hapd
, (u8
*) newbss
->ssid
.ssid
,
413 newbss
->ssid
.ssid_len
))
414 printf("Could not set SSID for kernel driver\n");
418 if (newbss
->ignore_broadcast_ssid
!= oldbss
->ignore_broadcast_ssid
)
421 if (hostapd_wep_key_cmp(&newbss
->ssid
.wep
, &oldbss
->ssid
.wep
)) {
426 vlan_reconfig(hapd
, oldconf
, oldbss
);
428 if (beacon_changed
) {
429 wpa_printf(MSG_DEBUG
, "Updating beacon frame information");
430 ieee802_11_set_beacon(hapd
);
434 change
.oldconf
= oldconf
;
435 change
.newconf
= hapd
->iconf
;
436 change
.oldbss
= oldbss
;
437 change
.newbss
= newbss
;
438 change
.mac_acl_changed
= hostapd_acl_diff(newbss
, oldbss
);
439 if (newbss
->max_num_sta
!= oldbss
->max_num_sta
&&
440 newbss
->max_num_sta
< hapd
->num_sta
) {
441 change
.num_sta_remove
= hapd
->num_sta
- newbss
->max_num_sta
;
443 change
.num_sta_remove
= 0;
444 ap_for_each_sta(hapd
, hostapd_config_reload_sta
, &change
);
446 old_radius
= hapd
->radius
;
447 hapd
->radius
= radius_client_reconfig(hapd
->radius
, hapd
,
448 oldbss
->radius
, newbss
->radius
);
449 hapd
->radius_client_reconfigured
= old_radius
!= hapd
->radius
||
450 hostapd_ip_diff(&newbss
->own_ip_addr
, &oldbss
->own_ip_addr
);
452 ieee802_1x_reconfig(hapd
, oldconf
, oldbss
);
453 iapp_reconfig(hapd
, oldconf
, oldbss
);
455 hostapd_acl_reconfig(hapd
, oldconf
);
456 accounting_reconfig(hapd
, oldconf
);
461 * config_reload2 - Part 2 of configuration reloading
464 static void config_reload2(struct hostapd_iface
*hapd_iface
, int status
)
466 struct hostapd_config_change
*change
= hapd_iface
->change
;
467 struct hostapd_data
*hapd
= change
->hapd
;
468 struct hostapd_config
*newconf
= change
->newconf
;
469 struct hostapd_config
*oldconf
= change
->oldconf
;
470 int beacon_changed
= change
->beacon_changed
;
471 struct hostapd_data
**new_hapd
= change
->new_hapd
;
472 struct hostapd_data
**old_hapd
= change
->old_hapd
;
473 int num_old_hapd
= change
->num_old_hapd
;
474 size_t i
, j
, max_bss
, same_bssid
;
475 struct hostapd_bss_config
*newbss
, *oldbss
;
480 hapd_iface
->change
= NULL
;
483 printf("Failed to setup new interface config\n");
485 cb
= hapd_iface
->config_reload_cb
;
486 hapd_iface
->config_reload_cb
= NULL
;
488 /* Invalid configuration - cleanup and terminate hostapd */
489 hapd_iface
->bss
= old_hapd
;
490 hapd_iface
->num_bss
= num_old_hapd
;
491 hapd_iface
->conf
= hapd
->iconf
= oldconf
;
492 hapd
->conf
= &oldconf
->bss
[0];
493 hostapd_config_free(newconf
);
502 * If any BSSes have been removed, added, or had their BSSIDs changed,
503 * completely remove and reinitialize such BSSes and all the BSSes
504 * following them since their BSSID might have changed.
506 max_bss
= oldconf
->num_bss
;
507 if (max_bss
> newconf
->num_bss
)
508 max_bss
= newconf
->num_bss
;
510 for (i
= 0; i
< max_bss
; i
++) {
511 if (os_strcmp(oldconf
->bss
[i
].iface
, newconf
->bss
[i
].iface
) !=
512 0 || hostapd_mac_comp(oldconf
->bss
[i
].bssid
,
513 newconf
->bss
[i
].bssid
) != 0)
518 for (i
= 0; i
< oldconf
->num_bss
; i
++) {
519 oldbss
= &oldconf
->bss
[i
];
521 for (j
= 0; j
< newconf
->num_bss
; j
++) {
522 if (os_strcmp(oldbss
->iface
, newconf
->bss
[j
].iface
) ==
524 newbss
= &newconf
->bss
[j
];
529 if (newbss
&& i
< same_bssid
) {
530 hapd
= hapd_iface
->bss
[j
] = old_hapd
[i
];
531 hapd
->iconf
= newconf
;
533 hostapd_reconfig_bss(hapd
, newbss
, oldbss
, oldconf
,
537 wpa_printf(MSG_DEBUG
, "Removing BSS (ifname %s)",
539 hostapd_free_stas(hapd
);
540 /* Send broadcast deauthentication for this BSS, but do
541 * not clear all STAs from the driver since other BSSes
542 * may have STA entries. The driver will remove all STA
543 * entries for this BSS anyway when the interface is
546 hostapd_deauth_all_stas(hapd
);
547 hostapd_cleanup(hapd
);
555 prev_addr
= hapd_iface
->bss
[0]->own_addr
;
556 hapd
= hapd_iface
->bss
[0];
557 for (j
= 0; j
< newconf
->num_bss
; j
++) {
558 if (hapd_iface
->bss
[j
] != NULL
) {
559 prev_addr
= hapd_iface
->bss
[j
]->own_addr
;
563 newbss
= &newconf
->bss
[j
];
565 wpa_printf(MSG_DEBUG
, "Reconfiguration: adding new BSS "
566 "(ifname=%s)", newbss
->iface
);
569 hapd
= hapd_iface
->bss
[j
] =
570 hostapd_alloc_bss_data(hapd_iface
, newconf
, newbss
);
572 printf("Failed to initialize new BSS\n");
573 /* FIX: This one is somewhat hard to recover
574 * from.. Would need to remove this BSS from
575 * conf and BSS list. */
579 hapd
->driver
= hapd_iface
->bss
[0]->driver
;
580 hapd
->iface
= hapd_iface
;
581 hapd
->iconf
= newconf
;
584 os_memcpy(hapd
->own_addr
, prev_addr
, ETH_ALEN
);
585 if (hostapd_mac_comp_empty(hapd
->conf
->bssid
) == 0)
586 prev_addr
= hapd
->own_addr
;
589 if (hostapd_setup_bss(hapd
, j
== 0)) {
590 printf("Failed to setup new BSS\n");
599 hostapd_config_free(oldconf
);
601 cb
= hapd_iface
->config_reload_cb
;
602 hapd_iface
->config_reload_cb
= NULL
;
609 * hostapd_config_reload_start - Start reconfiguration of an interface
610 * @hapd_iface: Pointer to hostapd interface data
611 * @cb: Function to be called back when done.
612 * The status indicates:
613 * 0 = success, new configuration in use;
614 * -1 = failed to update configuraiton, old configuration in use;
615 * -2 = failed to update configuration and failed to recover; caller
616 * should cleanup and terminate hostapd
618 * 0 = reconfiguration started;
619 * -1 = failed to update configuration, old configuration in use;
620 * -2 = failed to update configuration and failed to recover; caller
621 * should cleanup and terminate hostapd
623 int hostapd_config_reload_start(struct hostapd_iface
*hapd_iface
,
626 struct hostapd_config
*newconf
, *oldconf
;
627 struct hostapd_config_change
*change
;
628 struct hostapd_data
*hapd
= NULL
;
629 struct hostapd_data
**old_hapd
, **new_hapd
;
632 if (hapd_iface
->config_reload_cb
) {
633 wpa_printf(MSG_DEBUG
, "%s: Config reload already in progress.",
634 hapd_iface
->bss
[0]->conf
->iface
);
638 newconf
= hostapd_config_read(hapd_iface
->config_fname
);
639 if (newconf
== NULL
) {
640 printf("Failed to read new configuration file - continuing "
645 if (os_strcmp(newconf
->bss
[0].iface
, hapd_iface
->conf
->bss
[0].iface
) !=
647 printf("Interface name changing is not allowed in "
648 "configuration reloading (%s -> %s).\n",
649 hapd_iface
->conf
->bss
[0].iface
, newconf
->bss
[0].iface
);
650 hostapd_config_free(newconf
);
654 new_hapd
= os_zalloc(newconf
->num_bss
*
655 sizeof(struct hostapd_data
*));
656 if (new_hapd
== NULL
) {
657 hostapd_config_free(newconf
);
660 old_hapd
= hapd_iface
->bss
;
661 num_old_hapd
= hapd_iface
->num_bss
;
663 hapd_iface
->bss
= new_hapd
;
664 hapd_iface
->num_bss
= newconf
->num_bss
;
666 * First BSS remains the same since interface name changing was
667 * prohibited above. Now, this is only used in
668 * hostapd_config_reload_iface() and following loop will anyway set
671 hapd
= hapd_iface
->bss
[0] = old_hapd
[0];
673 oldconf
= hapd_iface
->conf
;
674 hapd
->iconf
= hapd_iface
->conf
= newconf
;
675 hapd
->conf
= &newconf
->bss
[0];
677 change
= os_zalloc(sizeof(struct hostapd_config_change
));
678 if (change
== NULL
) {
679 hostapd_config_free(newconf
);
684 change
->newconf
= newconf
;
685 change
->oldconf
= oldconf
;
686 change
->beacon_changed
= 0;
687 change
->hapd_iface
= hapd_iface
;
688 change
->new_hapd
= new_hapd
;
689 change
->old_hapd
= old_hapd
;
690 change
->num_old_hapd
= num_old_hapd
;
692 hapd_iface
->config_reload_cb
= cb
;
693 hapd_iface
->change
= change
;
694 if (hostapd_config_reload_iface_start(hapd_iface
, config_reload2
)) {
695 printf("Failed to start setup of new interface config\n");
697 hapd_iface
->config_reload_cb
= NULL
;
699 hapd_iface
->change
= NULL
;
701 /* Invalid configuration - cleanup and terminate hostapd */
702 hapd_iface
->bss
= old_hapd
;
703 hapd_iface
->num_bss
= num_old_hapd
;
704 hapd_iface
->conf
= hapd
->iconf
= oldconf
;
705 hapd
->conf
= &oldconf
->bss
[0];
706 hostapd_config_free(newconf
);