[PATCH 2/2] hostapd: Add DFS CAC and radar detection

Janusz Dziedzic janusz.dziedzic
Sun Sep 15 22:40:07 PDT 2013


Add DFS CAC handling, radar detection.
By default after radar detected or
channel became unavailable random channel
will be choose.

Signed-hostap: Simon Wunderlich <siwu at hrz.tu-chemnitz.de>
Signed-hostap: Janusz Dziedzic <janusz.dziedzic at tieto.com>
---
 src/ap/ap_drv_ops.c    |    2 +-
 src/ap/drv_callbacks.c |   21 ++++++++++
 src/ap/hostapd.c       |  100 ++++++++++++++++++++++++++++++++++++++++++++++--
 src/ap/hostapd.h       |   14 +------
 src/ap/ieee802_11.c    |   73 +++++++++++++++++++++++++++++++++++
 src/ap/ieee802_11.h    |    4 +-
 6 files changed, 197 insertions(+), 17 deletions(-)

diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index d9df9cc..cedfe8e 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -712,7 +712,7 @@ int hostapd_start_dfs_cac(struct hostapd_data *hapd, int freq, int flags)
 		return -1;
 	}
 
-	if (!(hapd->iface->dfs_state & DFS_ENABLED)) {
+	if (!hapd->iface->conf->ieee80211h) {
 		wpa_printf(MSG_ERROR, "Can't start DFS CAC, DFS functionality "
 			   "is not enabled");
 		return -1;
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index a54ab0c..68c12d2 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -788,8 +788,26 @@ static void hostapd_event_get_survey(struct hostapd_data *hapd,
 static void hostapd_event_dfs_radar_detected(struct hostapd_data *hapd,
 					     struct dfs_event *radar)
 {
+	int res;
 	wpa_printf(MSG_DEBUG, "DFS radar detected on %d MHz", radar->freq);
 
+	if (!hapd->iconf->ieee80211h)
+		return;
+
+	/* mark radar frequency as invalid */
+	res = ieee802_11_set_dfs_state(hapd, radar->freq,
+				       HOSTAPD_CHAN_DFS_UNAVAILABLE);
+
+	/* other frequency, just mark it and return. */
+	if (hapd->iface->freq != radar->freq)
+		return;
+
+	/* we are working on non-DFS channel - skip event */
+	if (res == 0)
+		return;
+
+	/* radar detected while operating, switch the channel. */
+	ieee802_11_start_channel_switch(hapd);
 }
 
 
@@ -797,6 +815,7 @@ static void hostapd_event_dfs_cac_finished(struct hostapd_data *hapd,
 					   struct dfs_event *radar)
 {
 	wpa_printf(MSG_DEBUG, "DFS CAC finished on %d MHz", radar->freq);
+	ieee802_11_complete_cac(hapd, 1, radar->freq);
 }
 
 
@@ -804,6 +823,7 @@ static void hostapd_event_dfs_cac_aborted(struct hostapd_data *hapd,
 					  struct dfs_event *radar)
 {
 	wpa_printf(MSG_DEBUG, "DFS CAC aborted on %d MHz", radar->freq);
+	ieee802_11_complete_cac(hapd, 0, radar->freq);
 }
 
 
@@ -811,6 +831,7 @@ static void hostapd_event_dfs_nop_finished(struct hostapd_data *hapd,
 					   struct dfs_event *radar)
 {
 	wpa_printf(MSG_DEBUG, "DFS NOP finished on %d MHz", radar->freq);
+	ieee802_11_set_dfs_state(hapd, radar->freq, HOSTAPD_CHAN_DFS_USABLE);
 }
 
 
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 55e6c95..4af7845 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -932,10 +932,8 @@ static int setup_interface(struct hostapd_iface *iface)
 			return 0;
 		}
 
-		if (iface->conf->ieee80211h) {
+		if (iface->conf->ieee80211h)
 			wpa_printf(MSG_DEBUG, "DFS support is enabled");
-			iface->dfs_state |= DFS_ENABLED;
-		}
 	}
 	return hostapd_setup_interface_complete(iface, 0);
 }
@@ -946,6 +944,9 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
 	struct hostapd_data *hapd = iface->bss[0];
 	size_t j;
 	u8 *prev_addr;
+	int flags;
+	struct hostapd_channel_data *channel;
+
 
 	if (err) {
 		wpa_printf(MSG_ERROR, "Interface initialization failed");
@@ -961,6 +962,41 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
 			   hostapd_hw_mode_txt(hapd->iconf->hw_mode),
 			   hapd->iconf->channel, iface->freq);
 
+		/* Handle DFS channel */
+check_dfs_chan_again:
+		flags = hostapd_hw_get_channel_flag(hapd, hapd->iconf->channel);
+		if (flags & HOSTAPD_CHAN_RADAR) {
+			switch(flags & HOSTAPD_CHAN_DFS_MASK) {
+			case HOSTAPD_CHAN_DFS_USABLE:
+				wpa_printf(MSG_DEBUG, "DFS start CAC on %dMHz", iface->freq);
+				if (hostapd_start_dfs_cac(hapd, hapd->iface->freq, flags)) {
+					wpa_printf(MSG_DEBUG, "DFS start_dfs_cac() failed\n");
+					return -1;
+				}
+				/* Continue initialisation after CAC */
+				return 0;
+				break;
+			case HOSTAPD_CHAN_DFS_UNAVAILABLE:
+				wpa_printf(MSG_DEBUG, "HOSTAPD_CHAN_DFS_UNAVAILABLE, get new chan");
+				/* find other channel here */
+				channel = hostapd_dfs_get_valid_channel(hapd);
+				if (!channel) {
+					wpa_printf(MSG_ERROR, "could not get valid channel");
+					return -1;
+				}
+				hapd->iconf->channel = channel->chan;
+				hapd->iface->freq = channel->freq;
+				goto check_dfs_chan_again;
+				break;
+			case HOSTAPD_CHAN_DFS_AVAILABLE:
+				/* We don't need CAC here */
+				wpa_printf(MSG_DEBUG, "HOSTAPD_CHAN_DFS_AVAILABLE, skip CAC");
+				break;
+			default:
+				break;
+			}
+		}
+
 		if (hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
 				     hapd->iconf->channel,
 				     hapd->iconf->ieee80211n,
@@ -1477,3 +1513,61 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
 	eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
 			       ap_handle_timer, hapd, sta);
 }
+
+static int hostapd_dfs_find_channel(struct hostapd_data *hapd,
+				    struct hostapd_channel_data **ret_chan,
+				    int idx)
+{
+	struct hostapd_hw_modes *mode;
+	struct hostapd_channel_data *chan;
+	int i, channel_idx = 0;
+
+	mode = hapd->iface->current_mode;
+
+	for (i = 0; i < mode->num_channels; i++) {
+		chan = &mode->channels[i];
+
+		if (chan->flag & HOSTAPD_CHAN_DISABLED)
+			continue;
+
+		if (chan->flag & HOSTAPD_CHAN_RADAR &&
+		    chan->flag & HOSTAPD_CHAN_DFS_UNAVAILABLE)
+			continue;
+
+		if (ret_chan && idx == channel_idx) {
+			wpa_printf(MSG_DEBUG, "Selected ch. #%d", chan->chan);
+			*ret_chan = chan;
+			return idx;
+		}
+		channel_idx++;
+	}
+	return channel_idx;
+}
+
+struct hostapd_channel_data * hostapd_dfs_get_valid_channel(
+					struct hostapd_data *hapd)
+{
+	struct hostapd_hw_modes *mode;
+	struct hostapd_channel_data *chan = NULL;
+	int channel_idx, new_channel_idx;
+	u32 _rand;
+
+	wpa_printf(MSG_DEBUG, "DFS: Selecting random channel");
+
+	if (hapd->iface->current_mode == NULL)
+		return NULL;
+
+	mode = hapd->iface->current_mode;
+	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+		return NULL;
+
+	/* get random available channel */
+	channel_idx = hostapd_dfs_find_channel(hapd, NULL, 0);
+	if (channel_idx > 0) {
+		os_get_random((u8 *) &_rand, sizeof(_rand));
+		new_channel_idx = _rand % channel_idx;
+		hostapd_dfs_find_channel(hapd, &chan, new_channel_idx);
+	};
+
+	return chan;
+}
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index b5595e7..379dd8d 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -315,18 +315,6 @@ struct hostapd_iface {
 	/* lowest observed noise floor in dBm */
 	s8 lowest_nf;
 
-
-       /* DFS functionality is enabled. */
-#define DFS_ENABLED BIT(0)
-
-       /* DFS channel availability check during initialization. */
-#define DFS_CAC_ACTIVE BIT(1)
-
-       /* DFS channel availability check completed successfully. */
-#define DFS_CAC_SUCCESS BIT(2)
-
-       u8 dfs_state; /* DFS_* bitfield */
-
 #ifdef CONFIG_ACS
 	unsigned int acs_num_completed_scans;
 #endif /* CONFIG_ACS */
@@ -392,4 +380,6 @@ const struct hostapd_eap_user *
 hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
 		     size_t identity_len, int phase2);
 
+struct hostapd_channel_data * hostapd_dfs_get_valid_channel(
+						struct hostapd_data *hapd);
 #endif /* HOSTAPD_H */
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 781f826..52bb5d0 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -2239,5 +2239,78 @@ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
 			WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
 }
 
+int ieee802_11_set_dfs_state(struct hostapd_data *hapd, int freq, u32 state)
+{
+	struct hostapd_hw_modes *mode;
+	struct hostapd_channel_data *chan = NULL;
+	int i;
+
+	mode = hapd->iface->current_mode;
+	if (mode == NULL)
+		return 0;
+
+	if (mode->mode != HOSTAPD_MODE_IEEE80211A) {
+		wpa_printf(MSG_WARNING, "current_mode != IEEE80211A");
+		return 0;
+	}
+
+	for (i = 0; i < hapd->iface->current_mode->num_channels; i++) {
+		chan = &hapd->iface->current_mode->channels[i];
+		if (chan->freq == freq) {
+			if (chan->flag & HOSTAPD_CHAN_RADAR) {
+				chan->flag &= ~HOSTAPD_CHAN_DFS_MASK;
+				chan->flag |= state;
+				return 1; /* Channel found */
+			}
+		}
+	}
+	wpa_printf(MSG_WARNING, "Can't set DFS state for freq %d MHz", freq);
+	return 0;
+}
+
+int ieee802_11_complete_cac(struct hostapd_data *hapd, int success, int freq)
+{
+	struct hostapd_channel_data *channel;
+	int err = 1;
+
+	if (success) {
+		/* Complete iface/ap configuration */
+		ieee802_11_set_dfs_state(hapd, freq, HOSTAPD_CHAN_DFS_AVAILABLE);
+		hostapd_setup_interface_complete(hapd->iface, 0);
+	} else {
+		/* Switch to new channel */
+		ieee802_11_set_dfs_state(hapd, freq, HOSTAPD_CHAN_DFS_UNAVAILABLE);
+		channel = hostapd_dfs_get_valid_channel(hapd);
+		if (channel) {
+			hapd->iconf->channel = channel->chan;
+			hapd->iface->freq = channel->freq;
+			err = 0;
+		} else
+			wpa_printf(MSG_ERROR, "No valid channel available");
+
+		hostapd_setup_interface_complete(hapd->iface, err);
+	}
+
+	return 0;
+}
+
+int ieee802_11_start_channel_switch(struct hostapd_data *hapd)
+{
+	struct hostapd_channel_data *channel;
+	wpa_printf(MSG_DEBUG, "%s called\n", __func__);
+	int err = 1;
+
+	channel = hostapd_dfs_get_valid_channel(hapd);
+	if (channel) {
+			hapd->iconf->channel = channel->chan;
+			hapd->iface->freq = channel->freq;
+			err = 0;
+	}
+
+	hapd->driver->stop_ap(hapd->drv_priv);
+
+	hostapd_setup_interface_complete(hapd->iface, err);
+	return 0;
+}
 
 #endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 2aab56d..358edfd 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -80,5 +80,7 @@ u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid);
 int hostapd_update_time_adv(struct hostapd_data *hapd);
 void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr);
 u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid);
-
+int ieee802_11_complete_cac(struct hostapd_data *hapd, int success, int freq);
+int ieee802_11_set_dfs_state(struct hostapd_data *hapd, int freq, u32 state);
+int ieee802_11_start_channel_switch(struct hostapd_data *hapd);
 #endif /* IEEE802_11_H */
-- 
1.7.9.5




More information about the Hostap mailing list