Adapt to OpenPAM Hydrangea.
[dragonfly.git] / contrib / hostapd-0.5.8 / reconfig.c
bloba0d61569812bcf5a6a6157c879fae3498d7d890e
1 /*
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
12 * license.
14 * See README and COPYING for more details.
17 #include "includes.h"
19 #include "hostapd.h"
20 #include "beacon.h"
21 #include "hw_features.h"
22 #include "driver.h"
23 #include "sta_info.h"
24 #include "radius_client.h"
25 #include "ieee802_11.h"
26 #include "iapp.h"
27 #include "ap_list.h"
28 #include "wpa.h"
29 #include "vlan_init.h"
30 #include "ieee802_11_auth.h"
31 #include "ieee802_1x.h"
32 #include "accounting.h"
33 #include "eloop.h"
36 /**
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;
47 int mac_acl_changed;
48 int num_sta_remove; /* number of STAs that need to be removed */
49 int beacon_changed;
50 struct hostapd_iface *hapd_iface;
51 struct hostapd_data **new_hapd, **old_hapd;
52 int num_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;
61 int deauth = 0;
62 u8 reason = WLAN_REASON_PREV_AUTH_NOT_VALID;
64 newbss = change->newbss;
65 oldbss = change->oldbss;
66 hapd = change->hapd;
68 if (sta->ssid == &oldbss->ssid) {
69 sta->ssid = &newbss->ssid;
71 if (newbss->ssid.ssid_len != oldbss->ssid.ssid_len ||
72 memcmp(newbss->ssid.ssid, oldbss->ssid.ssid,
73 newbss->ssid.ssid_len) != 0) {
74 /* main SSID was changed - kick STA out */
75 deauth++;
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
87 * themselves.
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,
92 NULL)))
93 deauth++;
95 if (newbss->ieee802_1x != oldbss->ieee802_1x &&
96 sta->ssid == &hapd->conf->ssid)
97 deauth++;
99 if (newbss->wpa != oldbss->wpa)
100 deauth++;
102 if (!newbss->wme_enabled && (sta->flags & WLAN_STA_WME))
103 deauth++;
105 if (newbss->auth_algs != oldbss->auth_algs &&
106 ((sta->auth_alg == WLAN_AUTH_OPEN &&
107 !(newbss->auth_algs & HOSTAPD_AUTH_OPEN)) ||
108 (sta->auth_alg == WLAN_AUTH_SHARED_KEY &&
109 !(newbss->auth_algs & HOSTAPD_AUTH_SHARED_KEY))))
110 deauth++;
112 if (change->num_sta_remove > 0) {
113 deauth++;
114 reason = WLAN_REASON_DISASSOC_AP_BUSY;
117 if (deauth) {
118 HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "STA " MACSTR
119 " deauthenticated during config reloading "
120 "(reason=%d)\n", 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--;
126 return 0;
130 static void hostapd_reconfig_tx_queue_params(struct hostapd_data *hapd,
131 struct hostapd_config *newconf,
132 struct hostapd_config *oldconf)
134 int i;
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];
141 if (!n->configured)
142 continue;
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,
147 n->cwmax, n->burst))
148 printf("Failed to set TX queue parameters for queue %d"
149 ".\n", i);
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;
159 size_t i;
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) {
172 beacon_changed++;
173 hapd->parameter_set_count++;
177 return beacon_changed;
181 static int rate_array_diff(int *a1, int *a2)
183 int i;
185 if (a1 == NULL && a2 == NULL)
186 return 0;
187 if (a1 == NULL || a2 == NULL)
188 return 1;
190 i = 0;
191 for (;;) {
192 if (a1[i] != a2[i])
193 return 1;
194 if (a1[i] == -1)
195 break;
196 i++;
199 return 0;
203 static int hostapd_acl_diff(struct hostapd_bss_config *a,
204 struct hostapd_bss_config *b)
206 int i;
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)
211 return 1;
213 for (i = 0; i < a->num_accept_mac; i++) {
214 if (memcmp(a->accept_mac[i], b->accept_mac[i], ETH_ALEN) != 0)
215 return 1;
218 for (i = 0; i < a->num_deny_mac; i++) {
219 if (memcmp(a->deny_mac[i], b->deny_mac[i], ETH_ALEN) != 0)
220 return 1;
223 return 0;
228 * reload_iface2 - Part 2 of reload_iface
229 * @hapd_iface: Pointer to hostapd interface data.
231 static void reload_iface2(struct hostapd_iface *hapd_iface)
233 struct hostapd_data *hapd = hapd_iface->bss[0];
234 struct hostapd_config *newconf = hapd_iface->change->newconf;
235 struct hostapd_config *oldconf = hapd_iface->change->oldconf;
236 int beacon_changed = hapd_iface->change->beacon_changed;
237 hostapd_iface_cb cb = hapd_iface->reload_iface_cb;
239 if (newconf->preamble != oldconf->preamble) {
240 if (hostapd_set_preamble(hapd, hapd->iconf->preamble))
241 printf("Could not set preamble for kernel driver\n");
242 beacon_changed++;
245 if (newconf->beacon_int != oldconf->beacon_int) {
246 /* Need to change beacon interval if it has changed or if
247 * auto channel selection was used. */
248 if (hostapd_set_beacon_int(hapd, newconf->beacon_int))
249 printf("Could not set beacon interval for kernel "
250 "driver\n");
251 if (newconf->beacon_int != oldconf->beacon_int)
252 beacon_changed++;
255 if (newconf->cts_protection_type != oldconf->cts_protection_type)
256 beacon_changed++;
258 if (newconf->rts_threshold > -1 &&
259 newconf->rts_threshold != oldconf->rts_threshold &&
260 hostapd_set_rts(hapd, newconf->rts_threshold))
261 printf("Could not set RTS threshold for kernel driver\n");
263 if (newconf->fragm_threshold > -1 &&
264 newconf->fragm_threshold != oldconf->fragm_threshold &&
265 hostapd_set_frag(hapd, newconf->fragm_threshold))
266 printf("Could not set fragmentation threshold for kernel "
267 "driver\n");
269 hostapd_reconfig_tx_queue_params(hapd, newconf, oldconf);
271 if (hostapd_reconfig_wme(hapd, newconf, oldconf) > 0)
272 beacon_changed++;
274 ap_list_reconfig(hapd_iface, oldconf);
276 hapd_iface->change->beacon_changed = beacon_changed;
278 hapd_iface->reload_iface_cb = NULL;
279 cb(hapd_iface, 0);
284 * reload_iface2_handler - Handler that calls reload_face2
285 * @eloop_data: Stores the struct hostapd_iface for the interface.
286 * @user_ctx: Unused.
288 static void reload_iface2_handler(void *eloop_data, void *user_ctx)
290 struct hostapd_iface *hapd_iface = eloop_data;
292 reload_iface2(hapd_iface);
297 * reload_hw_mode_done - Callback for after the HW mode is setup
298 * @hapd_iface: Pointer to interface data.
299 * @status: Status of the HW mode setup.
301 static void reload_hw_mode_done(struct hostapd_iface *hapd_iface, int status)
303 struct hostapd_data *hapd = hapd_iface->bss[0];
304 struct hostapd_config_change *change = hapd_iface->change;
305 struct hostapd_config *newconf = change->newconf;
306 hostapd_iface_cb cb;
307 int freq;
309 if (status) {
310 printf("Failed to select hw_mode.\n");
312 cb = hapd_iface->reload_iface_cb;
313 hapd_iface->reload_iface_cb = NULL;
314 cb(hapd_iface, -1);
316 return;
319 freq = hostapd_hw_get_freq(hapd, newconf->channel);
320 HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
321 "Mode: %s Channel: %d Frequency: %d MHz\n",
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,
344 hostapd_iface_cb cb)
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);
355 return -1;
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"
374 " driver.\n");
375 hapd_iface->reload_iface_cb = NULL;
376 return -1;
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;
383 return -1;
386 return 0;
389 eloop_register_timeout(0, 0, reload_iface2_handler, hapd_iface, NULL);
390 return 0;
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,
398 int beacon_changed)
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 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");
415 beacon_changed++;
418 if (newbss->ignore_broadcast_ssid != oldbss->ignore_broadcast_ssid)
419 beacon_changed++;
421 if (hostapd_wep_key_cmp(&newbss->ssid.wep, &oldbss->ssid.wep)) {
422 encr_changed++;
423 beacon_changed++;
426 vlan_reconfig(hapd, oldconf, oldbss);
428 if (beacon_changed) {
429 HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Updating beacon frame "
430 "information\n");
431 ieee802_11_set_beacon(hapd);
434 change.hapd = hapd;
435 change.oldconf = oldconf;
436 change.newconf = hapd->iconf;
437 change.oldbss = oldbss;
438 change.newbss = newbss;
439 change.mac_acl_changed = hostapd_acl_diff(newbss, oldbss);
440 if (newbss->max_num_sta != oldbss->max_num_sta &&
441 newbss->max_num_sta < hapd->num_sta) {
442 change.num_sta_remove = hapd->num_sta - newbss->max_num_sta;
443 } else
444 change.num_sta_remove = 0;
445 ap_for_each_sta(hapd, hostapd_config_reload_sta, &change);
447 old_radius = hapd->radius;
448 hapd->radius = radius_client_reconfig(hapd->radius, hapd,
449 oldbss->radius, newbss->radius);
450 hapd->radius_client_reconfigured = old_radius != hapd->radius ||
451 hostapd_ip_diff(&newbss->own_ip_addr, &oldbss->own_ip_addr);
453 ieee802_1x_reconfig(hapd, oldconf, oldbss);
454 iapp_reconfig(hapd, oldconf, oldbss);
456 hostapd_acl_reconfig(hapd, oldconf);
457 accounting_reconfig(hapd, oldconf);
462 * config_reload2 - Part 2 of configuration reloading
463 * @hapd_iface:
465 static void config_reload2(struct hostapd_iface *hapd_iface, int status)
467 struct hostapd_config_change *change = hapd_iface->change;
468 struct hostapd_data *hapd = change->hapd;
469 struct hostapd_config *newconf = change->newconf;
470 struct hostapd_config *oldconf = change->oldconf;
471 int beacon_changed = change->beacon_changed;
472 struct hostapd_data **new_hapd = change->new_hapd;
473 struct hostapd_data **old_hapd = change->old_hapd;
474 int num_old_hapd = change->num_old_hapd;
475 size_t i, j, max_bss, same_bssid;
476 struct hostapd_bss_config *newbss, *oldbss;
477 u8 *prev_addr;
478 hostapd_iface_cb cb;
480 free(change);
481 hapd_iface->change = NULL;
483 if (status) {
484 printf("Failed to setup new interface config\n");
486 cb = hapd_iface->config_reload_cb;
487 hapd_iface->config_reload_cb = NULL;
489 /* Invalid configuration - cleanup and terminate hostapd */
490 hapd_iface->bss = old_hapd;
491 hapd_iface->num_bss = num_old_hapd;
492 hapd_iface->conf = hapd->iconf = oldconf;
493 hapd->conf = &oldconf->bss[0];
494 hostapd_config_free(newconf);
495 free(new_hapd);
497 cb(hapd_iface, -2);
499 return;
503 * If any BSSes have been removed, added, or had their BSSIDs changed,
504 * completely remove and reinitialize such BSSes and all the BSSes
505 * following them since their BSSID might have changed.
507 max_bss = oldconf->num_bss;
508 if (max_bss > newconf->num_bss)
509 max_bss = newconf->num_bss;
511 for (i = 0; i < max_bss; i++) {
512 if (strcmp(oldconf->bss[i].iface, newconf->bss[i].iface) != 0
513 || hostapd_mac_comp(oldconf->bss[i].bssid,
514 newconf->bss[i].bssid) != 0)
515 break;
517 same_bssid = i;
519 for (i = 0; i < oldconf->num_bss; i++) {
520 oldbss = &oldconf->bss[i];
521 newbss = NULL;
522 for (j = 0; j < newconf->num_bss; j++) {
523 if (strcmp(oldbss->iface, newconf->bss[j].iface) == 0)
525 newbss = &newconf->bss[j];
526 break;
530 if (newbss && i < same_bssid) {
531 hapd = hapd_iface->bss[j] = old_hapd[i];
532 hapd->iconf = newconf;
533 hapd->conf = newbss;
534 hostapd_reconfig_bss(hapd, newbss, oldbss, oldconf,
535 beacon_changed);
536 } else {
537 hapd = old_hapd[i];
538 HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
539 "Removing BSS (ifname %s)\n",
540 hapd->conf->iface);
541 hostapd_free_stas(hapd);
542 /* Send broadcast deauthentication for this BSS, but do
543 * not clear all STAs from the driver since other BSSes
544 * may have STA entries. The driver will remove all STA
545 * entries for this BSS anyway when the interface is
546 * being removed. */
547 #if 0
548 hostapd_deauth_all_stas(hapd);
549 hostapd_cleanup(hapd);
550 #endif
552 free(hapd);
557 prev_addr = hapd_iface->bss[0]->own_addr;
558 hapd = hapd_iface->bss[0];
559 for (j = 0; j < newconf->num_bss; j++) {
560 if (hapd_iface->bss[j] != NULL) {
561 prev_addr = hapd_iface->bss[j]->own_addr;
562 continue;
565 newbss = &newconf->bss[j];
567 HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Reconfiguration: adding "
568 "new BSS (ifname=%s)\n", newbss->iface);
570 #if 0
571 hapd = hapd_iface->bss[j] =
572 hostapd_alloc_bss_data(hapd_iface, newconf, newbss);
573 #endif
574 if (hapd == NULL) {
575 printf("Failed to initialize new BSS\n");
576 /* FIX: This one is somewhat hard to recover
577 * from.. Would need to remove this BSS from
578 * conf and BSS list. */
579 exit(1);
581 hapd->driver = hapd_iface->bss[0]->driver;
582 hapd->iface = hapd_iface;
583 hapd->iconf = newconf;
584 hapd->conf = newbss;
586 memcpy(hapd->own_addr, prev_addr, ETH_ALEN);
587 if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0)
588 prev_addr = hapd->own_addr;
590 #if 0
591 if (hostapd_setup_bss(hapd, j == 0)) {
592 printf("Failed to setup new BSS\n");
593 /* FIX */
594 exit(1);
596 #endif
600 free(old_hapd);
601 hostapd_config_free(oldconf);
603 cb = hapd_iface->config_reload_cb;
604 hapd_iface->config_reload_cb = NULL;
606 cb(hapd_iface, 0);
611 * hostapd_config_reload_start - Start reconfiguration of an interface
612 * @hapd_iface: Pointer to hostapd interface data
613 * @cb: Function to be called back when done.
614 * The status indicates:
615 * 0 = success, new configuration in use;
616 * -1 = failed to update configuraiton, old configuration in use;
617 * -2 = failed to update configuration and failed to recover; caller
618 * should cleanup and terminate hostapd
619 * Returns:
620 * 0 = reconfiguration started;
621 * -1 = failed to update configuration, old configuration in use;
622 * -2 = failed to update configuration and failed to recover; caller
623 * should cleanup and terminate hostapd
625 int hostapd_config_reload_start(struct hostapd_iface *hapd_iface,
626 hostapd_iface_cb cb)
628 struct hostapd_config *newconf, *oldconf;
629 struct hostapd_config_change *change;
630 struct hostapd_data *hapd = NULL;
631 struct hostapd_data **old_hapd, **new_hapd;
632 int num_old_hapd;
634 if (hapd_iface->config_reload_cb) {
635 wpa_printf(MSG_DEBUG, "%s: Config reload already in progress.",
636 hapd_iface->bss[0]->conf->iface);
637 return -1;
640 newconf = hostapd_config_read(hapd_iface->config_fname);
641 if (newconf == NULL) {
642 printf("Failed to read new configuration file - continuing "
643 "with old.\n");
644 return -1;
647 if (strcmp(newconf->bss[0].iface, hapd_iface->conf->bss[0].iface) !=
648 0) {
649 printf("Interface name changing is not allowed in "
650 "configuration reloading (%s -> %s).\n",
651 hapd_iface->conf->bss[0].iface, newconf->bss[0].iface);
652 hostapd_config_free(newconf);
653 return -1;
656 new_hapd = wpa_zalloc(newconf->num_bss *
657 sizeof(struct hostapd_data *));
658 if (new_hapd == NULL) {
659 hostapd_config_free(newconf);
660 return -1;
662 old_hapd = hapd_iface->bss;
663 num_old_hapd = hapd_iface->num_bss;
665 hapd_iface->bss = new_hapd;
666 hapd_iface->num_bss = newconf->num_bss;
668 * First BSS remains the same since interface name changing was
669 * prohibited above. Now, this is only used in
670 * hostapd_config_reload_iface() and following loop will anyway set
671 * this again.
673 hapd = hapd_iface->bss[0] = old_hapd[0];
675 oldconf = hapd_iface->conf;
676 hapd->iconf = hapd_iface->conf = newconf;
677 hapd->conf = &newconf->bss[0];
679 change = wpa_zalloc(sizeof(struct hostapd_config_change));
680 if (change == NULL) {
681 hostapd_config_free(newconf);
682 return -1;
685 change->hapd = hapd;
686 change->newconf = newconf;
687 change->oldconf = oldconf;
688 change->beacon_changed = 0;
689 change->hapd_iface = hapd_iface;
690 change->new_hapd = new_hapd;
691 change->old_hapd = old_hapd;
692 change->num_old_hapd = num_old_hapd;
694 hapd_iface->config_reload_cb = cb;
695 hapd_iface->change = change;
696 if (hostapd_config_reload_iface_start(hapd_iface, config_reload2)) {
697 printf("Failed to start setup of new interface config\n");
699 hapd_iface->config_reload_cb = NULL;
700 free(change);
701 hapd_iface->change = NULL;
703 /* Invalid configuration - cleanup and terminate hostapd */
704 hapd_iface->bss = old_hapd;
705 hapd_iface->num_bss = num_old_hapd;
706 hapd_iface->conf = hapd->iconf = oldconf;
707 hapd->conf = &oldconf->bss[0];
708 hostapd_config_free(newconf);
709 free(new_hapd);
710 return -2;
713 return 0;