/*
 * Host AP (software wireless LAN access point) user space daemon for
 * Host AP kernel driver
 * Copyright (c) 2002, Jouni Malinen <jkmaline@cc.hut.fi>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation. See README and COPYING for
 * more details.
 */

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <arpa/inet.h>
#include <time.h>

#include <sys/socket.h>
#include <asm/types.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>   /* The L2 protocols */
#include <linux/if_arp.h>
#include <linux/wireless.h>

#if WIRELESS_EXT < 9
#error "Linux Wireless Extensions version 9 or newer required"
#endif

#include "eloop.h"
#include "hostapd.h"
#include "ieee802_1x.h"


struct ieee80211_mgmt {
	u16 frame_control;
	u16 duration;
	u8 da[6];
	u8 sa[6];
	u8 bssid[6];
	u16 seq_ctrl;
	union {
		struct {
			u16 auth_alg;
			u16 auth_transaction;
			u16 status_code;
			/* possibly followed by Challenge text */
			u8 variable[0];
		} __attribute__ ((packed)) auth;
		struct {
			u16 reason_code;
		} __attribute__ ((packed)) deauth;
		struct {
			u16 capab_info;
			u16 listen_interval;
			/* followed by SSID and Supported rates */
			u8 variable[0];
		} __attribute__ ((packed)) assoc_req;
		struct {
			u16 capab_info;
			u16 status_code;
			u16 aid;
			/* followed by Supported rates */
			u8 variable[0];
		} __attribute__ ((packed)) assoc_resp, reassoc_resp;
		struct {
			u16 capab_info;
			u16 listen_interval;
			u8 current_ap[6];
			/* followed by SSID and Supported rates */
			u8 variable[0];
		} __attribute__ ((packed)) reassoc_req;
		struct {
			u16 reason_code;
		} __attribute__ ((packed)) disassoc;
		struct {
			u8 timestamp[8];
			u16 beacon_int;
			u16 capab_info;
			/* followed by some of SSID, Supported rates,
			 * FH Params, DS Params, CF Params, IBSS Params, TIM */
			u8 variable[0];
		} __attribute__ ((packed)) beacon;
	} u;
} __attribute__ ((packed));


unsigned char rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };


static void ap_handle_timer(void *eloop_ctx, void *timeout_ctx);


struct sta_info* ap_get_sta(hostapd *hapd, u8 *sta)
{
	struct sta_info *s;

	s = hapd->sta_hash[STA_HASH(sta)];
	while (s != NULL && memcmp(s->addr, sta, 6) != 0)
		s = s->hnext;
	return s;
}


struct sta_info* ap_get_sta_radius_identifier(hostapd *hapd,
					      u8 radius_identifier)
{
	struct sta_info *s;

	s = hapd->sta_list;

	while (s) {
		if (s->radius_identifier >= 0 &&
		    s->radius_identifier == radius_identifier)
			return s;
		s = s->next;
	}

	return NULL;
}


static void ap_sta_list_del(hostapd *hapd, struct sta_info *sta)
{
	struct sta_info *tmp;

	if (hapd->sta_list == sta) {
		hapd->sta_list = sta->next;
		return;
	}

	tmp = hapd->sta_list;
	while (tmp != NULL && tmp->next != sta)
		tmp = tmp->next;
	if (tmp == NULL) {
		printf("Could not remove STA " MACSTR " from list.\n",
		       MAC2STR(sta->addr));
	} else
		tmp->next = sta->next;
}


static void ap_sta_hash_add(hostapd *hapd, struct sta_info *sta)
{
	sta->hnext = hapd->sta_hash[STA_HASH(sta->addr)];
	hapd->sta_hash[STA_HASH(sta->addr)] = sta;
}

static void ap_sta_hash_del(hostapd *hapd, struct sta_info *sta)
{
	struct sta_info *s;

	s = hapd->sta_hash[STA_HASH(sta->addr)];
	if (s == NULL) return;
	if (memcmp(s->addr, sta->addr, 6) == 0) {
		hapd->sta_hash[STA_HASH(sta->addr)] = s->hnext;
		return;
	}

	while (s->hnext != NULL && memcmp(s->hnext->addr, sta->addr, 6) != 0)
		s = s->hnext;
	if (s->hnext != NULL)
		s->hnext = s->hnext->hnext;
	else
		printf("AP: could not remove STA " MACSTR " from hash table\n",
		       MAC2STR(sta->addr));
}


static void ap_free_sta(hostapd *hapd, struct sta_info *sta)
{
	struct prism2_hostapd_param param;

	memset(&param, 0, sizeof(param));
	param.cmd = PRISM2_HOSTAPD_REMOVE_STA;
	memcpy(param.sta_addr, sta->addr, ETH_ALEN);
	if (hostapd_ioctl(hapd, &param)) {
		printf("Could not remove station from kernel driver.\n");
	}

	ap_sta_hash_del(hapd, sta);
	ap_sta_list_del(hapd, sta);

	if (sta->aid > 0)
		hapd->sta_aid[sta->aid - 1] = NULL;

	hapd->num_sta--;
	eloop_cancel_timeout(ap_handle_timer, sta);

	ieee802_1x_free_station(sta);

	free(sta);
}


static int hostapd_set_iface_flags(hostapd *hapd, int dev_up)
{
	struct ifreq ifr;

	if (hapd->ioctl_sock < 0)
		return -1;

	memset(&ifr, 0, sizeof(ifr));
	snprintf(ifr.ifr_name, IFNAMSIZ, "%sap", hapd->iface);

	if (ioctl(hapd->ioctl_sock, SIOCGIFFLAGS, &ifr) != 0) {
		perror("ioctl[SIOCGIFFLAGS]");
		return -1;
	}

	if (dev_up)
		ifr.ifr_flags |= IFF_UP;
	else
		ifr.ifr_flags &= ~IFF_UP;

	if (ioctl(hapd->ioctl_sock, SIOCSIFFLAGS, &ifr) != 0) {
		perror("ioctl[SIOCSIFFLAGS]");
		return -1;
	}

	if (dev_up) {
		memset(&ifr, 0, sizeof(ifr));
		snprintf(ifr.ifr_name, IFNAMSIZ, "%sap", hapd->iface);
		ifr.ifr_mtu = HOSTAPD_MTU;
		if (ioctl(hapd->ioctl_sock, SIOCSIFMTU, &ifr) != 0) {
			perror("ioctl[SIOCSIFMTU]");
			printf("Setting MTU failed - trying to survive with "
			       "current value\n");
		}
	}

	return 0;
}


int hostapd_ioctl(hostapd *hapd, struct prism2_hostapd_param *param)
{
	struct iwreq iwr;

	memset(&iwr, 0, sizeof(iwr));
	strncpy(iwr.ifr_name, hapd->iface, IFNAMSIZ);
	iwr.u.data.pointer = (caddr_t) param;
	iwr.u.data.length = sizeof(*param);

	if (ioctl(hapd->ioctl_sock, PRISM2_IOCTL_HOSTAPD, &iwr) < 0) {
		perror("ioctl[PRISM2_IOCTL_HOSTAPD]");
		return -1;
	}

	return 0;
}


int hostapd_set_encryption(hostapd *hapd, const char *alg, u8 *addr,
			   int idx, u8 *key, size_t key_len)
{
	struct iwreq iwr;
	struct prism2_hostapd_param *param;
	u8 *buf;
	size_t blen;

	blen = sizeof(*param) + key_len;
	buf = malloc(blen);
	if (buf == NULL)
		return -1;
	memset(buf, 0, blen);

	memset(&iwr, 0, sizeof(iwr));
	strncpy(iwr.ifr_name, hapd->iface, IFNAMSIZ);
	iwr.u.data.pointer = (caddr_t) buf;
	iwr.u.data.length = blen;

	param = (struct prism2_hostapd_param *) buf;
	param->cmd = PRISM2_SET_ENCRYPTION;
	if (addr == NULL)
		memset(param->sta_addr, 0xff, ETH_ALEN);
	else
		memcpy(param->sta_addr, addr, ETH_ALEN);
	strncpy(param->u.crypt.alg, alg, HOSTAP_CRYPT_ALG_NAME_LEN);
	param->u.crypt.flags = HOSTAP_CRYPT_FLAG_SET_TX_KEY;
	param->u.crypt.idx = idx;
	param->u.crypt.key_len = key_len;
	memcpy((u8 *) (param + 1), key, key_len);

	if (ioctl(hapd->ioctl_sock, PRISM2_IOCTL_HOSTAPD, &iwr) < 0) {
		perror("ioctl[PRISM2_IOCTL_HOSTAPD]");
		return -1;
	}

	return 0;
}


int hostap_ioctl_prism2param(hostapd *hapd, int param, int value)
{
	struct iwreq iwr;
	int *i;

	memset(&iwr, 0, sizeof(iwr));
	strncpy(iwr.ifr_name, hapd->iface, IFNAMSIZ);
	i = (int *) iwr.u.name;
	*i++ = param;
	*i++ = value;

	if (ioctl(hapd->ioctl_sock, PRISM2_IOCTL_PRISM2_PARAM, &iwr) < 0) {
		perror("ioctl[PRISM2_IOCTL_PRISM2_PARAM]");
		return -1;
	}

	return 0;
}


int hostap_ioctl_setiwessid(hostapd *hapd, char *buf, int len)
{
	struct iwreq iwr;

	memset(&iwr, 0, sizeof(iwr));
	strncpy(iwr.ifr_name, hapd->iface, IFNAMSIZ);
	iwr.u.essid.flags = 1; /* SSID active */
	iwr.u.essid.pointer = (caddr_t) buf;
	iwr.u.essid.length = len;

	if (ioctl(hapd->ioctl_sock, SIOCSIWESSID, &iwr) < 0) {
		perror("ioctl[SIOCSIWESSID]");
		printf("len=%d\n", len);
		return -1;
	}

	return 0;
}


static int hostapd_flush(hostapd *hapd)
{
	struct prism2_hostapd_param param;

	memset(&param, 0, sizeof(param));
	param.cmd = PRISM2_HOSTAPD_FLUSH;
	return hostapd_ioctl(hapd, &param);
}


static void hostapd_deauth_all_stas(hostapd *hapd)
{
	struct ieee80211_mgmt mgmt;

	memset(&mgmt, 0, sizeof(mgmt));
	mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
					  WLAN_FC_STYPE_DEAUTH);
	memset(mgmt.da, 0xff, ETH_ALEN);
	memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
	memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
	mgmt.u.deauth.reason_code =
		host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
	if (send(hapd->sock, &mgmt, IEEE80211_HDRLEN + sizeof(mgmt.u.deauth),
		 0) < 0)
		perror("hostapd_deauth_all_stas: send");
}


static int allowed_address(hostapd *hapd, u8 *addr)
{
	/* TODO: add support for filtering unwanted client addresses */
	return 1;
}


static void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
{
	hostapd *hapd = eloop_ctx;
	struct sta_info *sta = timeout_ctx;
	unsigned long next_time = 0;
	struct prism2_hostapd_param param;

	if (hapd->debug >= HOSTAPD_DEBUG_MINIMAL)
		printf("Checking STA " MACSTR " inactivity:\n",
		       MAC2STR(sta->addr));
	memset(&param, 0, sizeof(param));
	param.cmd = PRISM2_HOSTAPD_GET_INFO_STA;
	memcpy(param.sta_addr, sta->addr, ETH_ALEN);
	if (hostapd_ioctl(hapd, &param)) {
		printf("  Could not get station info from kernel driver.\n");
		/* assume the station has expired */
		param.u.get_info_sta.inactive_sec = AP_MAX_INACTIVITY + 1;
		param.u.get_info_sta.txexc = 1;
	}

	if (param.u.get_info_sta.inactive_sec < AP_MAX_INACTIVITY) {
		/* station activity detected; reset timeout state */
		if (hapd->debug >= HOSTAPD_DEBUG_MINIMAL)
			printf("  Station has been active\n");
		sta->timeout_next = STA_NULLFUNC;
		next_time = AP_MAX_INACTIVITY -
			param.u.get_info_sta.inactive_sec;
	} else if (sta->timeout_next == STA_DISASSOC &&
		   param.u.get_info_sta.txexc == 0) {
		if (hapd->debug >= HOSTAPD_DEBUG_MINIMAL)
			printf("  Station has ACKed data poll\n");
		/* data nullfunc frame poll did not produce TX errors; assume
		 * station ACKed it */
		sta->timeout_next = STA_NULLFUNC;
		next_time = AP_MAX_INACTIVITY;
	}

	if (next_time) {
		eloop_register_timeout(next_time, 0, ap_handle_timer, sta);
		return;
	}

	if (sta->timeout_next == STA_NULLFUNC) {
		/* send data frame to poll STA and check whether this frame
		 * is ACKed */
		struct ieee80211_hdr hdr;

		if (hapd->debug >= HOSTAPD_DEBUG_MINIMAL)
			printf("  Polling STA with data frame\n");
		memset(&param, 0, sizeof(param));
		param.cmd = PRISM2_HOSTAPD_RESET_TXEXC_STA;
		memcpy(param.sta_addr, sta->addr, ETH_ALEN);
		if (hostapd_ioctl(hapd, &param)) {
			printf("  Could not reset station TX Exc counter\n");
		}

		/* FIX: WLAN_FC_STYPE_NULLFUNC would be more appropriate, but
		 * it is apparently not retried so TX Exc events are not
		 * received for it */
		memset(&hdr, 0, sizeof(hdr));
		hdr.frame_control =
			IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA);
		hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS);
		memcpy(hdr.IEEE80211_DA_FROMDS, sta->addr, ETH_ALEN);
		memcpy(hdr.IEEE80211_BSSID_FROMDS, hapd->own_addr, ETH_ALEN);
		memcpy(hdr.IEEE80211_SA_FROMDS, hapd->own_addr, ETH_ALEN);

		if (send(hapd->sock, &hdr, sizeof(hdr), 0) < 0)
			perror("ap_handle_timer: send");
	} else {
		int deauth = sta->timeout_next == STA_DEAUTH;
		struct ieee80211_mgmt mgmt;
		int slen;

		printf("  Sending %s info to STA " MACSTR "\n",
		       deauth ? "deauthentication" : "disassociation",
		       MAC2STR(sta->addr));

		memset(&mgmt, 0, sizeof(mgmt));

		memcpy(mgmt.da, sta->addr, ETH_ALEN);
		memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
		memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);

		if (deauth) {
			mgmt.frame_control = IEEE80211_FC(
				WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DEAUTH);
			mgmt.u.deauth.reason_code =
				host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
			slen = IEEE80211_HDRLEN + sizeof(mgmt.u.deauth);
		} else {
			mgmt.frame_control = IEEE80211_FC(
				WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DISASSOC);
			mgmt.u.disassoc.reason_code = host_to_le16(
				WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY);
			slen = IEEE80211_HDRLEN + sizeof(mgmt.u.disassoc);
		}
		if (send(hapd->sock, &mgmt, slen, 0) < 0)
			perror("ap_handle_timer: send");
	}

	switch (sta->timeout_next) {
	case STA_NULLFUNC:
		sta->timeout_next = STA_DISASSOC;
		eloop_register_timeout(AP_DISASSOC_DELAY, 0, ap_handle_timer,
				       sta);
		break;
	case STA_DISASSOC:
		sta->flags &= ~WLAN_STA_ASSOC;
		ieee802_1x_set_port_enabled(hapd, sta, 0);
		sta->timeout_next = STA_DEAUTH;
		eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer,
				       sta);
		break;
	case STA_DEAUTH:
		ap_free_sta(hapd, sta);
		break;
	}
}


static void handle_auth(hostapd *hapd, struct ieee80211_mgmt *mgmt, size_t len)
{
	u16 auth_alg, auth_transaction, status_code;
	u16 resp = WLAN_STATUS_SUCCESS;
	struct sta_info *sta;

	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
		printf("handle_auth - too short payload (len=%d)\n", len);
		return;
	}

	auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
	auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
	status_code = le_to_host16(mgmt->u.auth.status_code);

	if (hapd->debug >= HOSTAPD_DEBUG_MINIMAL)
		printf("authentication: STA=" MACSTR " auth_alg=%d "
		       "auth_transaction=%d status_code=%d\n",
		       MAC2STR(mgmt->sa), auth_alg, auth_transaction,
		       status_code);

	/* FIX: add support for WLAN_SHARED_KEY authentication */
	if (auth_alg != WLAN_AUTH_OPEN) {
		printf("Unknown authentication algorithm (%d)\n", auth_alg);
		resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
		goto fail;
	}

	if (auth_transaction != 1) {
		printf("Unknown authentication transaction number (%d)\n",
		       auth_transaction);
		resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
		goto fail;
	}

	if (!allowed_address(hapd, mgmt->sa)) {
		printf("Station " MACSTR " not allowed to authenticate.\n",
		       MAC2STR(mgmt->sa));
		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
		goto fail;
	}

	sta = ap_get_sta(hapd, mgmt->sa);

	if (sta == NULL) {
		if (hapd->debug >= HOSTAPD_DEBUG_MINIMAL)
			printf("  New STA\n");
		if (hapd->num_sta >= MAX_STA_COUNT) {
			/* FIX: might try to remove some old STAs first? */
			printf("  no more room for new STAs (%d/%d)\n",
			       hapd->num_sta, MAX_STA_COUNT);
			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
			goto fail;
		}

		sta = (struct sta_info *) malloc(sizeof(struct sta_info));
		if (sta == NULL) {
			printf("  kmalloc failed\n");
			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
			goto fail;
		}
		memset(sta, 0, sizeof(struct sta_info));
		sta->radius_identifier = -1;

		/* initialize STA info data */
		eloop_register_timeout(AP_MAX_INACTIVITY, 0, ap_handle_timer,
				       sta);
		memcpy(sta->addr, mgmt->sa, 6);
		sta->next = hapd->sta_list;
		hapd->sta_list = sta;
		hapd->num_sta++;
		ap_sta_hash_add(hapd, sta);
	}

	switch (auth_alg) {
	case WLAN_AUTH_OPEN:
		printf("Station " MACSTR " authenticated (open system)\n",
		       MAC2STR(sta->addr));
		sta->flags |= WLAN_STA_AUTH;
		break;
	}

 fail:

	/* use the queued buffer for transmission because it is large enough
	 * and not needed anymore */
	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
					   WLAN_FC_STYPE_AUTH);
	memcpy(mgmt->da, mgmt->sa, ETH_ALEN);
	memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
	/* Addr3 = BSSID - already set */

	mgmt->u.auth.auth_alg = host_to_le16(WLAN_AUTH_OPEN);
	mgmt->u.auth.auth_transaction = host_to_le16(2);
	mgmt->u.auth.status_code = host_to_le16(resp);

	if (send(hapd->sock, mgmt, IEEE80211_HDRLEN + sizeof(mgmt->u.auth), 0)
	    < 0)
		perror("handle_auth: send");
}


static void handle_assoc(hostapd *hapd, struct ieee80211_mgmt *mgmt,
			 size_t len, int reassoc)
{
	u16 capab_info, listen_interval;
	u16 resp = WLAN_STATUS_SUCCESS;
	u8 *pos;
	int send_deauth = 0, send_len, left;
	struct prism2_hostapd_param param;
	struct sta_info *sta;

	if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
				      sizeof(mgmt->u.assoc_req))) {
		printf("handle_assoc(reassoc=%d) - too short payload (len=%d)"
		       "\n", reassoc, len);
		return;
	}

	if (reassoc) {
		capab_info = le_to_host16(mgmt->u.reassoc_req.capab_info);
		listen_interval = le_to_host16(
			mgmt->u.reassoc_req.listen_interval);
		if (hapd->debug >= HOSTAPD_DEBUG_MINIMAL)
			printf("reassociation request: STA=" MACSTR
			       " capab_info=0x%02x "
			       "listen_interval=%d current_ap=" MACSTR "\n",
			       MAC2STR(mgmt->sa), capab_info, listen_interval,
			       MAC2STR(mgmt->u.reassoc_req.current_ap));
		left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req));
		pos = mgmt->u.reassoc_req.variable;
	} else {
		capab_info = le_to_host16(mgmt->u.assoc_req.capab_info);
		listen_interval = le_to_host16(
			mgmt->u.assoc_req.listen_interval);
		if (hapd->debug >= HOSTAPD_DEBUG_MINIMAL)
			printf("association request: STA=" MACSTR
			       " capab_info=0x%02x listen_interval=%d\n",
			       MAC2STR(mgmt->sa), capab_info, listen_interval);
		left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req));
		pos = mgmt->u.assoc_req.variable;
	}

	sta = ap_get_sta(hapd, mgmt->sa);
	if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) {
		printf("STA " MACSTR " trying to associate before "
		       "authentication\n", MAC2STR(mgmt->sa));
		if (sta) {
			printf("  sta: addr=" MACSTR " aid=%d flags=0x%04x\n",
			       MAC2STR(sta->addr), sta->aid, sta->flags);
		}
		send_deauth = 1;
		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
		goto fail;
	}

	/* followed by SSID and Supported rates */
	if (left < 2) {
		printf("STA " MACSTR " trying to associate without SSID "
		       "info\n", MAC2STR(sta->addr));
		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
		goto fail;
	}

	if (left >= 2 && *pos == WLAN_EID_SSID) {
		int elen;
		pos++;
		elen = *pos++;
		left -= 2;

		if (elen > left) {
			printf("SSID element underflow (elen=%d left=%d)\n",
			       elen, left);
			return;
		}

		if (elen != hapd->ssid_len - 1 ||
		    memcmp(pos, hapd->ssid, elen) != 0) {
			printf("Station " MACSTR " tried to associate with "
			       "unknown SSID\n", MAC2STR(sta->addr));
			return;
		}

		pos += elen;
		left -= elen;
	}

	if (left >= 2 && *pos == WLAN_EID_SUPP_RATES) {
		int elen, i;
		pos++;
		elen = *pos++;
		left -= 2;

		if (elen > left || elen > sizeof(sta->supported_rates)) {
			printf("Invalid supported rates element length "
			       "(elen=%d left=%d)\n", elen, left);
			return;
		}

		memset(sta->supported_rates, 0, sizeof(sta->supported_rates));
		memcpy(sta->supported_rates, pos, elen);

		sta->tx_supp_rates = 0;
		for (i = 0; i < elen; i++) {
			if ((sta->supported_rates[i] & 0x7f) == 2)
				sta->tx_supp_rates |= WLAN_RATE_1M;
			if ((sta->supported_rates[i] & 0x7f) == 4)
				sta->tx_supp_rates |= WLAN_RATE_2M;
			if ((sta->supported_rates[i] & 0x7f) == 11)
				sta->tx_supp_rates |= WLAN_RATE_5M5;
			if ((sta->supported_rates[i] & 0x7f) == 22)
				sta->tx_supp_rates |= WLAN_RATE_11M;
		}

		pos += elen;
		left -= elen;
	} else
		sta->tx_supp_rates = 0xff;

	if (left > 0) {
		printf("Associated request included %d bytes extra data\n",
		       left);
	}

	/* get a unique AID */
	if (sta->aid > 0) {
		if (hapd->debug >= HOSTAPD_DEBUG_MINIMAL)
			printf("  old AID %d\n", sta->aid);
	} else {
		for (sta->aid = 1; sta->aid <= MAX_AID_TABLE_SIZE; sta->aid++)
			if (hapd->sta_aid[sta->aid - 1] == NULL)
				break;
		if (sta->aid > MAX_AID_TABLE_SIZE) {
			sta->aid = 0;
			resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
			printf("  no room for more AIDs\n");
			goto fail;
		} else {
			hapd->sta_aid[sta->aid - 1] = sta;
			if (hapd->debug >= HOSTAPD_DEBUG_MINIMAL)
				printf("  new AID %d\n", sta->aid);
		}
	}

	printf("Station " MACSTR " associated (aid %d)\n",
	       MAC2STR(sta->addr), sta->aid);
	sta->flags |= WLAN_STA_ASSOC;

	memset(&param, 0, sizeof(param));
	param.cmd = PRISM2_HOSTAPD_ADD_STA;
	memcpy(param.sta_addr, mgmt->sa, ETH_ALEN);
	param.u.add_sta.aid = sta->aid;
	param.u.add_sta.capability = capab_info;
	param.u.add_sta.tx_supp_rates = sta->tx_supp_rates;
	if (hostapd_ioctl(hapd, &param)) {
		printf("Could not add station to kernel driver.\n");
	}


 fail:

	/* use the queued buffer for transmission because it is large enough
	 * and not needed anymore */
	mgmt->frame_control =
		IEEE80211_FC(WLAN_FC_TYPE_MGMT,
			     (send_deauth ? WLAN_FC_STYPE_DEAUTH :
			      (reassoc ? WLAN_FC_STYPE_REASSOC_RESP :
			       WLAN_FC_STYPE_ASSOC_RESP)));
	memcpy(mgmt->da, mgmt->sa, ETH_ALEN);
	memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
	/* Addr3 = BSSID - already set */

	send_len = IEEE80211_HDRLEN;
	if (send_deauth) {
		send_len += sizeof(mgmt->u.deauth);
		mgmt->u.deauth.reason_code = host_to_le16(resp);
	} else {
		char *p;
		send_len += sizeof(mgmt->u.assoc_resp);
		mgmt->u.assoc_resp.capab_info =
			host_to_le16(WLAN_CAPABILITY_ESS);
		mgmt->u.assoc_resp.status_code = host_to_le16(resp);
		mgmt->u.assoc_resp.aid = host_to_le16((sta ? sta->aid : 0)
						      | BIT(14) | BIT(15));
		/* Supported rates */
		p = (char *) mgmt->u.assoc_resp.variable;
		*p++ = WLAN_EID_SUPP_RATES;
		*p++ = 4; /* len */
		*p++ = 0x82; /* 1 Mbps, base set */
		*p++ = 0x84; /* 2 Mbps, base set */
		*p++ = 0x0b; /* 5.5 Mbps */
		*p++ = 0x16; /* 11 Mbps */
	}

	if (send(hapd->sock, mgmt, send_len, 0) < 0)
		perror("handle_assoc: send");

	if (hapd->ieee802_1x && resp == WLAN_STATUS_SUCCESS)
		ieee802_1x_new_station(hapd, sta);
}


static void handle_disassoc(hostapd *hapd, struct ieee80211_mgmt *mgmt,
			    size_t len)
{
	struct sta_info *sta;

	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.disassoc)) {
		printf("handle_disassoc - too short payload (len=%d)\n", len);
		return;
	}

	if (hapd->debug >= HOSTAPD_DEBUG_MINIMAL)
		printf("disassocation: STA=" MACSTR " reason_code=%d\n",
		       MAC2STR(mgmt->sa),
		       le_to_host16(mgmt->u.disassoc.reason_code));

	sta = ap_get_sta(hapd, mgmt->sa);
	if (sta == NULL) {
		printf("Station " MACSTR " trying to disassociate, but it "
		       "is not associated.\n", MAC2STR(mgmt->sa));
		return;
	}

	sta->flags &= ~WLAN_STA_ASSOC;
	printf("Station " MACSTR " disassociated\n", MAC2STR(sta->addr));
	ieee802_1x_set_port_enabled(hapd, sta, 0);
}


static void handle_deauth(hostapd *hapd, struct ieee80211_mgmt *mgmt,
			  size_t len)
{
	struct sta_info *sta;

	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.deauth)) {
		printf("handle_deauth - too short payload (len=%d)\n", len);
		return;
	}

	if (hapd->debug >= HOSTAPD_DEBUG_MINIMAL)
		printf("deauthentication: STA=" MACSTR " reason_code=%d\n",
		       MAC2STR(mgmt->sa),
		       le_to_host16(mgmt->u.deauth.reason_code));

	sta = ap_get_sta(hapd, mgmt->sa);
	if (sta == NULL) {
		printf("Station " MACSTR " trying to deauthenticate, but it "
		       "is not authenticated.\n", MAC2STR(mgmt->sa));
		return;
	}

	sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
	printf("Station " MACSTR " deauthenticated\n", MAC2STR(sta->addr));
	ieee802_1x_set_port_enabled(hapd, sta, 0);
}


static void handle_mgmt(hostapd *hapd, char *buf, size_t len, u16 stype)
{
	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) buf;

	if (stype == WLAN_FC_STYPE_BEACON) {
		if (hapd->debug >= HOSTAPD_DEBUG_MINIMAL)
			printf("mgmt::beacon\n");
		/* FIX: implement handle_beacon() */
		return;
	}

	if (memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) {
		printf("MGMT: BSSID=" MACSTR " not our address\n",
		       MAC2STR(mgmt->bssid));
		return;
	}


	if (stype == WLAN_FC_STYPE_PROBE_REQ) {
		printf("mgmt::probe_req\n");
		return;
	}

	if (memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) {
		printf("MGMT: DA=" MACSTR " not our address\n",
		       MAC2STR(mgmt->da));
		return;
	}

	switch (stype) {
	case WLAN_FC_STYPE_AUTH:
		if (hapd->debug >= HOSTAPD_DEBUG_MINIMAL)
			printf("mgmt::auth\n");
		handle_auth(hapd, mgmt, len);
		break;
	case WLAN_FC_STYPE_ASSOC_REQ:
		if (hapd->debug >= HOSTAPD_DEBUG_MINIMAL)
			printf("mgmt::assoc_req\n");
		handle_assoc(hapd, mgmt, len, 0);
		break;
	case WLAN_FC_STYPE_REASSOC_REQ:
		if (hapd->debug >= HOSTAPD_DEBUG_MINIMAL)
			printf("mgmt::reassoc_req\n");
		handle_assoc(hapd, mgmt, len, 1);
		break;
	case WLAN_FC_STYPE_DISASSOC:
		if (hapd->debug >= HOSTAPD_DEBUG_MINIMAL)
			printf("mgmt::disassoc\n");
		handle_disassoc(hapd, mgmt, len);
		break;
	case WLAN_FC_STYPE_DEAUTH:
		if (hapd->debug >= HOSTAPD_DEBUG_MINIMAL)
			printf("mgmt::deauth\n");
		handle_deauth(hapd, mgmt, len);
		break;
	default:
		printf("unknown mgmt frame subtype %d\n", stype);
		break;
	}
}


static void handle_data(hostapd *hapd, char *buf, size_t len, u16 stype)
{
	struct ieee80211_hdr *hdr;
	u16 fc, ethertype;
	u8 *pos, *sa;
	size_t left;

	if (len < sizeof(struct ieee80211_hdr))
		return;

	hdr = (struct ieee80211_hdr *) buf;
	fc = le_to_host16(hdr->frame_control);

	if ((fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) != WLAN_FC_TODS) {
		printf("Not ToDS data frame (fc=0x%04x)\n", fc);
		return;
	}

	sa = hdr->addr2;

	pos = (u8 *) (hdr + 1);
	left = len - sizeof(*hdr);

	if (left < sizeof(rfc1042_header)) {
		printf("Too short data frame\n");
		return;
	}

	if (memcmp(pos, rfc1042_header, sizeof(rfc1042_header)) != 0) {
		printf("Data frame with no RFC1042 header\n");
		return;
	}
	pos += sizeof(rfc1042_header);
	left -= sizeof(rfc1042_header);

	if (left < 2) {
		printf("No ethertype in data frame\n");
		return;
	}

	ethertype = *pos++ << 8;
	ethertype |= *pos++;
	left -= 2;
	switch (ethertype) {
	case ETH_P_PAE:
		if (hapd->ieee802_1x)
			ieee802_1x_receive(hapd, sa, pos, left);
		else
			printf("IEEE 802.1X is not configured to be used - "
			       "dropping packet\n");
		break;

	default:
		printf("Unknown ethertype 0x%04x in data frame\n", ethertype);
		break;
	}
}


static void handle_frame(hostapd *hapd, char *buf, size_t len)
{
	struct ieee80211_hdr *hdr;
	u16 fc, extra_len, type, stype;
	unsigned char *extra = NULL;
	size_t data_len = len;
	int ver;

	/* PSPOLL is only 16 bytes, but driver does not (at least yet) pass
	 * these to user space */
	if (len < 24) {
		printf("handle_frame: too short (%d)\n", len);
		return;
	}

	hdr = (struct ieee80211_hdr *) buf;
	fc = le_to_host16(hdr->frame_control);

	ver = fc & WLAN_FC_PVER;

	/* protocol version 3 is reserved for indicating extra data after the
	 * payload */
	if (ver == 3) {
		u16 *elen;
		elen = (u16 *) (buf + len - 1);
		extra_len = le_to_host16(*elen);
		printf("extra data in frame (elen=%d)\n", extra_len);
		if (extra_len + 2 > len) {
			printf("  extra data overflow\n");
			return;
		}
		len -= extra_len + 2;
		extra = buf + len;
	} else if (ver != 0) {
		printf("unknown protocol version %d\n", ver);
		return;
	}

	type = WLAN_FC_GET_TYPE(fc);
	stype = WLAN_FC_GET_STYPE(fc);

	switch (type) {
	case WLAN_FC_TYPE_MGMT:
		if (hapd->debug >= HOSTAPD_DEBUG_MINIMAL)
			printf("MGMT\n");
		handle_mgmt(hapd, buf, data_len, stype);
		break;
	case WLAN_FC_TYPE_CTRL:
		if (hapd->debug >= HOSTAPD_DEBUG_MINIMAL)
			printf("CTRL\n");
		break;
	case WLAN_FC_TYPE_DATA:
		if (hapd->debug >= HOSTAPD_DEBUG_MINIMAL)
			printf("DATA\n");
		handle_data(hapd, buf, data_len, stype);
		break;
	default:
		printf("unknown frame type %d\n", type);
		break;
	}
}


static void handle_term(int sig, void *eloop_ctx, void *signal_ctx)
{
	printf("Signal %d received - terminating\n", sig);
	eloop_terminate();
}


static void handle_dump_state(int sig, void *eloop_ctx, void *signal_ctx)
{
#ifdef HOSTAPD_DUMP_STATE
	hostapd *hapd = (hostapd *) eloop_ctx;
	FILE *f;
	time_t now;
	struct sta_info *sta;
	int i;

	if (!hapd->dump_log_name) {
		printf("Dump file not defined - ignoring dump request\n");
		return;
	}

	printf("Dumping hostapd state to '%s'\n", hapd->dump_log_name);
	f = fopen(hapd->dump_log_name, "w");
	if (f == NULL) {
		printf("Could not open dump file '%s' for writing.\n",
		       hapd->dump_log_name);
		return;
	}

	time(&now);
	fprintf(f, "hostapd state dump - %s", ctime(&now));

	for (sta = hapd->sta_list; sta != NULL; sta = sta->next) {
		fprintf(f, "\nSTA=" MACSTR "\n", MAC2STR(sta->addr));

		fprintf(f,
			"  AID=%d flags=0x%x %s%s%s%s%s%s\n"
			"  capability=0x%x listen_interval=%d\n",
			sta->aid,
			sta->flags,
			(sta->flags & WLAN_STA_AUTH ? "[AUTH]" : ""),
			(sta->flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""),
			(sta->flags & WLAN_STA_PS ? "[PS]" : ""),
			(sta->flags & WLAN_STA_TIM ? "[TIM]" : ""),
			(sta->flags & WLAN_STA_PERM ? "[PERM]" : ""),
			(sta->flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" :
			 ""),
			sta->capability,
			sta->listen_interval);

		fprintf(f, "  supported_rates=");
		for (i = 0; i < sizeof(sta->supported_rates); i++)
			if (sta->supported_rates[i] != 0)
				fprintf(f, "%02x ", sta->supported_rates[i]);
		fprintf(f, "%s%s%s%s\n",
			(sta->tx_supp_rates & WLAN_RATE_1M ? "[1M]" : ""),
			(sta->tx_supp_rates & WLAN_RATE_2M ? "[2M]" : ""),
			(sta->tx_supp_rates & WLAN_RATE_5M5 ? "[5.5M]" : ""),
			(sta->tx_supp_rates & WLAN_RATE_11M ? "[11M]" : ""));

		fprintf(f,
			"  timeout_next=%s\n",
			(sta->timeout_next == STA_NULLFUNC ? "NULLFUNC POLL" :
			 (sta->timeout_next == STA_DISASSOC ? "DISASSOC" :
			  "DEAUTH")));

		ieee802_1x_dump_state(f, "  ", sta);
	}

	fclose(f);
#endif /* HOSTAPD_DUMP_STATE */
}


static void handle_read(int sock, void *eloop_ctx, void *sock_ctx)
{
	hostapd *hapd = (hostapd *) eloop_ctx;
	int len;
	unsigned char buf[3000];

	len = recv(sock, buf, sizeof(buf), 0);
	if (len < 0) {
		perror("recv");
		return;
	}
	if (hapd->debug >= HOSTAPD_DEBUG_MINIMAL)
		printf("Received %d bytes management frame\n", len);
	if (hapd->debug >= HOSTAPD_DEBUG_MSGDUMPS) {
		int i;
		printf("  dump:");
		for (i = 0; i < len; i++)
			printf(" %02x", buf[i]);
		printf("\n");
	}

	handle_frame(hapd, buf, len);
}


static void handle_auth_serv_read(int sock, void *eloop_ctx, void *sock_ctx)
{
	hostapd *hapd = (hostapd *) eloop_ctx;
	int len;
	unsigned char buf[3000];

	len = recv(sock, buf, sizeof(buf), 0);
	if (len < 0) {
		perror("recv");
		return;
	}
	if (hapd->debug >= HOSTAPD_DEBUG_MINIMAL)
		printf("Received %d bytes from authentication server\n", len);
	if (len == sizeof(buf)) {
		printf("Possibly too long UDP frame for our buffer - "
		       "dropping it\n");
		return;
	}
	ieee802_1x_receive_auth(hapd, buf, len);
}


static void hostapd_set_broadcast_wep(hostapd *hapd, int keylen)
{
	if (keylen < 0 || keylen > 13) {
		printf("Invalid WEP key len\n");
		exit(1);
	}

	hapd->default_wep_key_len = keylen;
	hapd->default_wep_key = malloc(keylen);
	if (hapd->default_wep_key == NULL ||
	    hostapd_get_rand(hapd->default_wep_key, keylen)) {
		printf("Could not generate random WEP key.\n");
		free(hapd->default_wep_key);
		exit(1);
	}
}



static void usage(void)
{
	fprintf(stderr,
		"Host AP user space daemon for management functionality of "
		"Host AP kernel driver\n"
		"Copyright (c) 2002, Jouni Malinen <jkmaline@cc.hut.fi>\n"
		"\n"
		"usage: hostapd [-hdxm] [-o<addr>] [-a<addr>] [-p<port>] "
		"[-s<secret>] [-M<text>]\n"
		"\t[-b<len>] <wlan#>\n"
		"\n"
		"options:\n"
		"   -h   show this usage\n"
		"   -d   show more debug messages (-dd for even more)\n"
		"   -x   require IEEE 802.1X authorization\n"
		"   -m   minimal EAP authentication for testing IEEE 802.1X\n"
		"        Note! This authorizes _ALL_ stations that support\n"
		"        IEEE 802.1X, it does not need any keys.\n"
		"   -S<SSID> use given SSID (default 'test')\n"
		"   -D<dump file> dump state to the given file on SIGUSR1\n"
		"   -M<msg> optional displayable message sent with EAP "
		"Request-Identity\n"
		"   -b<len> set default/broadcast random WEP key when using "
		"EAP/TLS\n"
		"   -i<len> set individual unicast random WEP key when using "
		"EAP/TLS\n"
		"           (len=5 for 40-bit WEP and 13 for 104-bit WEP)\n"
		"\n"
		"   (following are required for IEEE 802.1X authorization when"
		" using RADIUS\n"
		"   encapsulation, i.e., no -m):\n"
		"\n"
		"   -o<addr> own IP address used as NAS-IP-Address\n"
		"   -a<addr> authentication server IP address (RADIUS)\n"
		"   -p<port> authentication server UDP port (default 1812)\n"
		"   -s<secret> shared secret with authentication server\n");

	exit(1);
}


int main(int argc, char *argv[])
{
	hostapd hapd;
	int ret = 0, c, i;
        struct ifreq ifr;
	struct sockaddr_ll addr;
	char *iface = NULL;

	memset(&hapd, 0, sizeof(hapd));

	hapd.auth_serv_sock = hapd.sock = hapd.ioctl_sock = -1;
	hapd.auth_server_port = 1812;
	hapd.ssid_len = 5;
	strncpy(hapd.ssid, "test", sizeof(hapd.ssid) - 1);

	for (;;) {
		c = getopt(argc, argv, "dhmxa:b:D:i:M:o:p:S:s:");
		if (c < 0)
			break;
		switch (c) {
		case 'h':
			usage();
			break;
		case 'd':
			hapd.debug++;
			break;
		case 'x':
			hapd.ieee802_1x = 1;
			break;
		case 'm':
			hapd.minimal_eap = 1;
			break;
		case 'o':
			if (!inet_aton(optarg, &hapd.own_ip_addr)) {
				printf("Invalid '-o' option '%s'\n\n", optarg);
				usage();
			}
			break;
		case 'a':
			if (!inet_aton(optarg, &hapd.auth_server_addr)) {
				printf("Invalid '-a' option '%s'\n\n", optarg);
				usage();
			}
			break;

		case 'b':
			i = atoi(optarg);
			if (i < 1 || i > 13)
				usage();
			hostapd_set_broadcast_wep(&hapd, i);
			break;

		case 'D':
			hapd.dump_log_name = strdup(optarg);
			break;

		case 'i':
			hapd.individual_wep_key_len = atoi(optarg);
			if (hapd.individual_wep_key_len != 5 &&
			    hapd.individual_wep_key_len != 13)
				usage();
			/* use key0 in individual key and key1 in broadcast key
			 */
			hapd.default_wep_key_idx = 1;
			break;

		case 'M':
			hapd.eap_req_id_text = strdup(optarg);
			break;

		case 'p':
			hapd.auth_server_port = atoi(optarg);
			break;

		case 's':
			hapd.auth_server_shared_secret = strdup(optarg);
			hapd.auth_server_shared_secret_len = strlen(optarg);
			if (hapd.auth_server_shared_secret_len == 0) {
				/* RFC 2865, Ch. 3 */
				printf("Empty shared secret ('-s') is not "
				       "allowed.\n\n");
				usage();
			}
			break;

		case 'S':
			hapd.ssid_len = strlen(optarg) + 1;
			if (hapd.ssid_len + 1 > sizeof(hapd.ssid) ||
			    hapd.ssid_len < 1) {
				printf("Invalid SSID '%s'\n", optarg);
				usage();
			}
			memcpy(hapd.ssid, optarg, hapd.ssid_len);
			break;

		default:
			usage();
			break;
		}
	}

	if (optind + 1 != argc)
		usage();
	iface = argv[optind];

	if (hapd.ieee802_1x && !hapd.minimal_eap) {
		int failed = 0;

		if (hapd.own_ip_addr.s_addr == 0) {
			printf("Own IP address required (-o)\n");
			failed = 1;
		}

		if (hapd.auth_server_addr.s_addr == 0) {
			printf("Authentication server IP address required "
			       "(-a)\n");
			failed = 1;
		}

		if (hapd.auth_server_shared_secret == NULL) {
			printf("Authentication server shared secret required "
			       "(-s)\n");
			failed = 1;
		}

		if (failed) {
			printf("\n");
			usage();
		}
	}

	snprintf(hapd.iface, sizeof(hapd.iface), "%s", iface);
	hapd.sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
	if (hapd.sock < 0) {
		perror("socket[PF_PACKET,SOCK_RAW]");
		ret = 1;
		goto out;
	}

	hapd.ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
	if (hapd.ioctl_sock < 0) {
		perror("socket[PF_INET,SOCK_DGRAM]");
		ret = 1;
		goto out;
	}

        memset(&ifr, 0, sizeof(ifr));
        snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%sap", iface);
        if (ioctl(hapd.sock, SIOCGIFINDEX, &ifr) != 0) {
		perror("ioctl(SIOCGIFINDEX)");
		ret = 1;
		goto out;
        }

	if (hostapd_set_iface_flags(&hapd, 1)) {
		ret = 1;
		goto out;
	}

	memset(&addr, 0, sizeof(addr));
	addr.sll_family = AF_PACKET;
	addr.sll_ifindex = ifr.ifr_ifindex;
	if (hapd.debug >= HOSTAPD_DEBUG_MINIMAL)
		printf("Opening raw packet socket for ifindex %d\n",
		       addr.sll_ifindex);

	if (bind(hapd.sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
		perror("bind");
		ret = 1;
		goto out;
	}

        memset(&ifr, 0, sizeof(ifr));
        snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", iface);
        if (ioctl(hapd.sock, SIOCGIFHWADDR, &ifr) != 0) {
		perror("ioctl(SIOCGIFHWADDR)");
		ret = 1;
		goto out;
        }

	if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
		printf("Invalid HW-addr family 0x%04x\n",
		       ifr.ifr_hwaddr.sa_family);
		ret = 1;
		goto out;
	}
	memcpy(hapd.own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);

	printf("Using interface %sap with hwaddr " MACSTR " and ssid '%s'\n",
	       iface, MAC2STR(hapd.own_addr), hapd.ssid);

	/* Set SSID for the kernel driver (to be used in beacon and probe
	 * response frames) */
	if (hostap_ioctl_setiwessid(&hapd, hapd.ssid, hapd.ssid_len)) {
		printf("Could not set SSID for kernel driver\n");
		ret = 1;
		goto out;
	}

	if (hapd.ieee802_1x) {
		struct sockaddr_in serv;

		/* enable kernel driver support for IEEE 802.1X */
		if (hostap_ioctl_prism2param(&hapd, PRISM2_PARAM_IEEE_802_1X,
					     1)) {
			printf("Could not setup IEEE 802.1X support in kernel "
			       "driver.\n");
			ret = 1;
			goto out;
		}

		/* use host driver implementation of encryption to allow
		 * individual keys and passing plaintext EAPOL frames */
		if (hostap_ioctl_prism2param(&hapd, PRISM2_PARAM_HOST_DECRYPT,
					     1) ||
		    hostap_ioctl_prism2param(&hapd, PRISM2_PARAM_HOST_ENCRYPT,
					     1)) {
			printf("Could not setup host-based encryption in "
			       "kernel driver.\n");
			ret = 1;
			goto out;
		}

		hapd.auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
		if (hapd.auth_serv_sock < 0) {
			perror("socket[PF_INET,SOCK_DGRAM]");
			ret = 1;
			goto out;
		}

		memset(&serv, 0, sizeof(serv));
		serv.sin_family = AF_INET;
		serv.sin_addr.s_addr = hapd.auth_server_addr.s_addr;
		serv.sin_port = htons(hapd.auth_server_port);

		if (connect(hapd.auth_serv_sock, (struct sockaddr *) &serv,
			    sizeof(serv)) < 0) {
			perror("connect[auth_serv_sock]");
			ret = 1;
			goto out;
		}
	}

	if (hapd.default_wep_key) {
		if (hapd.debug >= HOSTAPD_DEBUG_MINIMAL)
			hostapd_hexdump("Default WEP key",
					hapd.default_wep_key,
					hapd.default_wep_key_len);

		hostapd_set_encryption(&hapd, "none", NULL, 0, NULL, 0);

		if (hostapd_set_encryption(&hapd, "WEP", NULL,
					   hapd.default_wep_key_idx,
					   hapd.default_wep_key,
					   hapd.default_wep_key_len)) {
			printf("Could not set WEP encryption.\n");
			ret = 1;
			goto out;
		}
	}

	printf("Flushing old station entries\n");
	if (hostapd_flush(&hapd)) {
		printf("Could not connect to kernel driver.\n");
		ret = 1;
		goto out;
	}

	hostapd_deauth_all_stas(&hapd);

	eloop_init(&hapd);
	/* TODO: reload config etc. on SIGHUP (?) */
	eloop_register_signal(SIGHUP, handle_term, NULL);
	eloop_register_signal(SIGINT, handle_term, NULL);
	eloop_register_signal(SIGTERM, handle_term, NULL);
	eloop_register_signal(SIGUSR1, handle_dump_state, NULL);
	if (eloop_register_read_sock(hapd.sock, handle_read, NULL)) {
		printf("Could not register read socket\n");
		ret = 1;
		goto out;
	}
	if (hapd.auth_serv_sock >= 0 &&
	    eloop_register_read_sock(hapd.auth_serv_sock,
				     handle_auth_serv_read, NULL)) {
		printf("Could not register read socket for authentication "
		       "server\n");
		ret = 1;
		goto out;
	}
	eloop_run();
	eloop_destroy();

	printf("Flushing old station entries\n");
	if (hostapd_flush(&hapd))
		printf("Could not connect to kernel driver.\n");
	printf("Deauthenticate all stations\n");
	hostapd_deauth_all_stas(&hapd);

 out:
	(void) hostapd_set_iface_flags(&hapd, 0);

	if (hapd.sock >= 0)
		close(hapd.sock);
	if (hapd.ioctl_sock >= 0)
		close(hapd.ioctl_sock);
	if (hapd.auth_serv_sock >= 0)
		close(hapd.auth_serv_sock);
	if (hapd.auth_server_shared_secret)
		free(hapd.auth_server_shared_secret);
	if (hapd.dump_log_name)
		free(hapd.dump_log_name);
	free(hapd.default_wep_key);

	return ret;
}
