summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/net80211/ieee80211_input.c608
-rw-r--r--sys/net80211/ieee80211_output.c439
-rw-r--r--sys/net80211/ieee80211_proto.c5
-rw-r--r--sys/net80211/ieee80211_proto.h18
4 files changed, 1062 insertions, 8 deletions
diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c
index f5c94ef6b32..8538f6ea940 100644
--- a/sys/net80211/ieee80211_input.c
+++ b/sys/net80211/ieee80211_input.c
@@ -1,5 +1,5 @@
/* $NetBSD: ieee80211_input.c,v 1.24 2004/05/31 11:12:24 dyoung Exp $ */
-/* $OpenBSD: ieee80211_input.c,v 1.44 2007/07/14 19:58:05 damien Exp $ */
+/* $OpenBSD: ieee80211_input.c,v 1.45 2007/07/28 11:24:06 damien Exp $ */
/*-
* Copyright (c) 2001 Atsushi Onoe
* Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
@@ -98,6 +98,20 @@ void ieee80211_recv_deauth(struct ieee80211com *, struct mbuf *,
struct ieee80211_node *, int, u_int32_t);
void ieee80211_recv_disassoc(struct ieee80211com *, struct mbuf *,
struct ieee80211_node *, int, u_int32_t);
+void ieee80211_recv_4way_msg1(struct ieee80211com *,
+ const struct ieee80211_eapol_key *, struct ieee80211_node *);
+void ieee80211_recv_4way_msg2(struct ieee80211com *,
+ const struct ieee80211_eapol_key *, struct ieee80211_node *);
+void ieee80211_recv_4way_msg3(struct ieee80211com *,
+ const struct ieee80211_eapol_key *, struct ieee80211_node *);
+void ieee80211_recv_4way_msg4(struct ieee80211com *,
+ const struct ieee80211_eapol_key *, struct ieee80211_node *);
+void ieee80211_recv_group_msg1(struct ieee80211com *,
+ const struct ieee80211_eapol_key *, struct ieee80211_node *);
+void ieee80211_recv_group_msg2(struct ieee80211com *,
+ const struct ieee80211_eapol_key *, struct ieee80211_node *);
+void ieee80211_recv_eapol_key_req(struct ieee80211com *,
+ const struct ieee80211_eapol_key *, struct ieee80211_node *);
/*
* Process a received frame. The node associated with the sender
@@ -353,13 +367,22 @@ ieee80211_input(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node *ni,
ic->ic_stats.is_rx_decap++;
goto err;
}
+ eh = mtod(m, struct ether_header *);
+#if 0
+ if (!ni->ni_port_valid &&
+ eh->ether_type != htons(ETHERTYPE_PAE)) {
+ IEEE80211_DPRINTF(("%s: port not valid: %s\n",
+ __func__, ether_sprintf(wh->i_addr2)));
+ ic->ic_stats.is_rx_unauth++;
+ goto err;
+ }
+#endif
ifp->if_ipackets++;
/* perform as a bridge within the AP */
m1 = NULL;
if (ic->ic_opmode == IEEE80211_M_HOSTAP &&
(ic->ic_flags & IEEE80211_F_NOBRIDGE) == 0) {
- eh = mtod(m, struct ether_header *);
if (ETHER_IS_MULTICAST(eh->ether_dhost)) {
m1 = m_copym(m, 0, M_COPYALL, M_DONTWAIT);
if (m1 == NULL)
@@ -1836,6 +1859,587 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0,
#undef IEEE80211_VERIFY_ELEMENT
#undef IEEE80211_VERIFY_SSID
+/* unaligned big endian access */
+#define BE_READ_2(p) \
+ ((u_int16_t)(p)[0] << 8 | (u_int16_t)(p)[1])
+
+#define BE_READ_8(p) \
+ ((u_int64_t)(p)[0] << 56 | (u_int64_t)(p)[1] << 48 | \
+ (u_int64_t)(p)[2] << 40 | (u_int64_t)(p)[3] << 32 | \
+ (u_int64_t)(p)[4] << 24 | (u_int64_t)(p)[5] << 16 | \
+ (u_int64_t)(p)[6] << 8 | (u_int64_t)(p)[7])
+
+#define BE_WRITE_2(p, v) do { \
+ (p)[0] = (v) >> 8; \
+ (p)[1] = (v) & 0xff; \
+} while (0)
+
+/* unaligned little endian access */
+#define LE_READ_8(p) \
+ ((u_int64_t)(p)[7] << 56 | (u_int64_t)(p)[6] << 48 | \
+ (u_int64_t)(p)[5] << 40 | (u_int64_t)(p)[4] << 32 | \
+ (u_int64_t)(p)[3] << 24 | (u_int64_t)(p)[2] << 16 | \
+ (u_int64_t)(p)[1] << 8 | (u_int64_t)(p)[0])
+
+/*
+ * 4-Way Handshake Message 1 is sent by the authenticator to the supplicant
+ * (see 8.5.3.1).
+ */
+void
+ieee80211_recv_4way_msg1(struct ieee80211com *ic,
+ const struct ieee80211_eapol_key *key, struct ieee80211_node *ni)
+{
+ u_int8_t snonce[EAPOL_KEY_NONCE_LEN];
+ const u_int8_t *frm, *efrm;
+ const u_int8_t *pmkid;
+ const u_int8_t *pmk;
+ size_t pmk_len;
+
+ if (ic->ic_opmode != IEEE80211_M_STA &&
+ ic->ic_opmode != IEEE80211_M_IBSS)
+ return;
+
+ if (BE_READ_8(key->replaycnt) <= ni->ni_replaycnt)
+ return;
+
+ /* save authenticator's nonce (ANonce) */
+ memcpy(ni->ni_nonce, key->nonce, EAPOL_KEY_NONCE_LEN);
+
+ /* parse key data field (shall contain an encapsulated PMKID) */
+ frm = (const u_int8_t *)&key[1];
+ efrm = frm + BE_READ_2(key->paylen);
+
+ pmkid = NULL;
+ while (frm + 2 <= efrm) {
+ if (frm + 2 + frm[1] > efrm)
+ break;
+ switch (frm[0]) {
+ case IEEE80211_ELEMID_VENDOR:
+ if (frm[1] < 4)
+ break;
+ if (memcmp(&frm[2], IEEE80211_OUI, 3) == 0) {
+ switch (frm[5]) {
+ case IEEE80211_KDE_PMKID:
+ pmkid = frm;
+ break;
+ }
+ }
+ break;
+ }
+ frm += 2 + frm[1];
+ }
+ /* check that we have a valid PMKID KDE */
+ if (pmkid == NULL || pmkid[1] - 4 < 16)
+ return;
+
+ /* do not update replaycnt since the frame contains no MIC */
+
+ /* generate a new nonce (SNonce) */
+ get_random_bytes(snonce, EAPOL_KEY_NONCE_LEN);
+
+ if (ni->ni_akm == IEEE80211_AKM_IEEE8021X) {
+ /* XXX find the PMK in the PMKSA cache using the PMKID */
+ } else {
+ /* XXX the PMK is the PSK */
+ }
+
+ /* derive PTK from PMK */
+ ieee80211_derive_ptk(pmk, pmk_len, ni->ni_macaddr, ic->ic_myaddr,
+ ni->ni_nonce, snonce, (u_int8_t *)&ni->ni_ptk, sizeof(ni->ni_ptk));
+ ni->ni_ptk_ok = 1;
+
+ if (ic->ic_if.if_flags & IFF_DEBUG)
+ printf("%s: received msg %d/%d of the %s handshake from %s\n",
+ ic->ic_if.if_xname, 1, 4, "4-way",
+ ether_sprintf(ni->ni_macaddr));
+
+ /* send message 2 to authenticator */
+ ieee80211_send_4way_msg2(ic, ni);
+}
+
+/*
+ * 4-Way Handshake Message 2 is sent by the supplicant to the authenticator
+ * (see 8.5.3.2).
+ */
+void
+ieee80211_recv_4way_msg2(struct ieee80211com *ic,
+ const struct ieee80211_eapol_key *key, struct ieee80211_node *ni)
+{
+ const u_int8_t *frm, *efrm;
+ const u_int8_t *rsn;
+ const u_int8_t *pmk;
+ size_t pmk_len;
+
+ if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
+ ic->ic_opmode != IEEE80211_M_IBSS)
+ return;
+
+ if (BE_READ_8(key->replaycnt) != ni->ni_replaycnt)
+ return;
+
+ /* derive PTK from PMK */
+ ieee80211_derive_ptk(pmk, pmk_len, ic->ic_myaddr, ni->ni_macaddr,
+ ni->ni_nonce, key->nonce, (u_int8_t *)&ni->ni_ptk,
+ sizeof(ni->ni_ptk));
+ ni->ni_ptk_ok = 1;
+
+ /* parse key data field (shall contain an RSN IE) */
+ frm = (const u_int8_t *)&key[1];
+ efrm = frm + BE_READ_2(key->paylen);
+
+ rsn = NULL;
+ while (frm + 2 <= efrm) {
+ if (frm + 2 + frm[1] > efrm)
+ break;
+ switch (frm[0]) {
+ case IEEE80211_ELEMID_RSN:
+ rsn = frm;
+ break;
+ }
+ frm += 2 + frm[1];
+ }
+ if (rsn == NULL)
+ return;
+
+ /*
+ * The RSN IE must match bit-wise with what the STA included in its
+ * (Re)Association Request.
+ */
+ if (rsn[1] != ni->ni_rsnie[1] ||
+ memcmp(rsn, ni->ni_rsnie, 2 + rsn[1]) != 0)
+ return;
+
+ if (ic->ic_if.if_flags & IFF_DEBUG)
+ printf("%s: received msg %d/%d of the %s handshake from %s\n",
+ ic->ic_if.if_xname, 2, 4, "4-way",
+ ether_sprintf(ni->ni_macaddr));
+
+ /* send message 3 to supplicant */
+ ieee80211_send_4way_msg3(ic, ni);
+}
+
+/*
+ * 4-Way Handshake Message 3 is sent by the authenticator to the supplicant
+ * (see 8.5.3.3).
+ */
+void
+ieee80211_recv_4way_msg3(struct ieee80211com *ic,
+ const struct ieee80211_eapol_key *key, struct ieee80211_node *ni)
+{
+ struct ieee80211_key k;
+ const u_int8_t *frm, *efrm;
+ const u_int8_t *rsn1, *rsn2, *gtk;
+
+ if (ic->ic_opmode != IEEE80211_M_STA &&
+ ic->ic_opmode != IEEE80211_M_IBSS)
+ return;
+
+ if (BE_READ_8(key->replaycnt) <= ni->ni_replaycnt)
+ return;
+
+ /* check that ANonce matches the one received in message 1 */
+ if (memcmp(key->nonce, ni->ni_nonce, EAPOL_KEY_NONCE_LEN) != 0)
+ return;
+
+ /* parse key data field */
+ frm = (const u_int8_t *)&key[1];
+ efrm = frm + BE_READ_2(key->paylen);
+
+ rsn1 = rsn2 = gtk = NULL;
+ while (frm + 2 <= efrm) {
+ if (frm + 2 + frm[1] > efrm)
+ break;
+ switch (frm[0]) {
+ case IEEE80211_ELEMID_RSN:
+ if (rsn1 == NULL)
+ rsn1 = frm;
+ else if (rsn2 == NULL)
+ rsn2 = frm;
+ /* ignore if more than two RSN IEs */
+ break;
+ case IEEE80211_ELEMID_VENDOR:
+ if (frm[1] < 4)
+ break;
+ if (memcmp(&frm[2], IEEE80211_OUI, 3) == 0) {
+ switch (frm[5]) {
+ case IEEE80211_KDE_GTK:
+ gtk = frm;
+ break;
+ }
+ }
+ break;
+ }
+ frm += 2 + frm[1];
+ }
+ /* first RSN IE is mandatory */
+ if (rsn1 == NULL)
+ return;
+ /* key data must be encrypted if GTK is included */
+ if (gtk != NULL && !(BE_READ_2(key->info) & EAPOL_KEY_ENCRYPTED))
+ return;
+
+ /*
+ * Check that first RSN IE is identical to the one received in
+ * the beacon or probe response frame.
+ */
+ if (rsn1[1] != ni->ni_rsnie[1] ||
+ memcmp(rsn1, ni->ni_rsnie, 2 + rsn1[1]) != 0)
+ return;
+
+ /* update the last seen value of the key replay counter field */
+ ni->ni_replaycnt = BE_READ_8(key->replaycnt);
+
+ /*
+ * If a second RSN information element is present, use its pairwise
+ * cipher suite or deauthenticate.
+ */
+ if (rsn2 != NULL) {
+ /* XXX ieee80211_parse_rsn(rsn2); */
+ }
+
+ /* install the PTK */
+ memset(&k, 0, sizeof k);
+ k.k_cipher = ni->ni_pairwise_cipher;
+ k.k_flags = IEEE80211_KEY_TX;
+ k.k_len = ieee80211_cipher_keylen(k.k_cipher);
+ memcpy(k.k_key, ni->ni_ptk.tk, k.k_len);
+ if ((*ic->ic_set_key)(ic, ni, &k) != 0)
+ return;
+
+ if (gtk != NULL) {
+ /* check that the GTK KDE is valid */
+ if (gtk[1] - 4 < 2)
+ return;
+ /* install the GTK */
+ memset(&k, 0, sizeof k);
+ k.k_id = gtk[6] & 3;
+ k.k_cipher = ni->ni_group_cipher;
+ k.k_flags = IEEE80211_KEY_GROUP;
+ if (gtk[6] & (1 << 2)) /* Tx bit */
+ k.k_flags |= IEEE80211_KEY_TX;
+ k.k_len = gtk[1] - 6;
+ /* check that key length matches group cipher */
+ if (k.k_len != ieee80211_cipher_keylen(k.k_cipher))
+ return; /* XXX PTK already installed! */
+ memcpy(k.k_key, &gtk[7], k.k_len);
+ k.k_rsc = LE_READ_8(key->rsc);
+ if ((*ic->ic_set_key)(ic, ni, &k) != 0)
+ return;
+ }
+
+ /* mark the PAE port as valid */
+ ni->ni_port_valid = 1;
+
+ if (ic->ic_if.if_flags & IFF_DEBUG)
+ printf("%s: received msg %d/%d of the %s handshake from %s\n",
+ ic->ic_if.if_xname, 3, 4, "4-way",
+ ether_sprintf(ni->ni_macaddr));
+
+ /* send message 4 to authenticator */
+ ieee80211_send_4way_msg4(ic, ni);
+}
+
+/*
+ * 4-Way Handshake Message 4 is sent by the supplicant to the authenticator
+ * (see 8.5.3.4).
+ */
+void
+ieee80211_recv_4way_msg4(struct ieee80211com *ic,
+ const struct ieee80211_eapol_key *key, struct ieee80211_node *ni)
+{
+ struct ieee80211_key k;
+
+ if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
+ ic->ic_opmode != IEEE80211_M_IBSS)
+ return;
+
+ if (BE_READ_8(key->replaycnt) != ni->ni_replaycnt)
+ return;
+
+ /* empty key data field */
+
+ /* install the PTK */
+ memset(&k, 0, sizeof k);
+ k.k_cipher = ni->ni_pairwise_cipher;
+ k.k_flags = IEEE80211_KEY_TX;
+ k.k_len = ieee80211_cipher_keylen(k.k_cipher);
+ memcpy(k.k_key, ni->ni_ptk.tk, k.k_len);
+ if ((*ic->ic_set_key)(ic, ni, &k) != 0)
+ return;
+
+ if (ic->ic_opmode == IEEE80211_M_IBSS) {
+ if (++ni->ni_key_count == 2)
+ ni->ni_port_valid = 1;
+ } else
+ ni->ni_port_valid = 1;
+
+ /* increment the 64-bit Key Replay Counter */
+ ni->ni_replaycnt++;
+
+ if (ic->ic_if.if_flags & IFF_DEBUG)
+ printf("%s: received msg %d/%d of the %s handshake from %s\n",
+ ic->ic_if.if_xname, 4, 4, "4-way",
+ ether_sprintf(ni->ni_macaddr));
+}
+
+/*
+ * Group Key Handshake Message 1 is sent by the authenticator to the
+ * supplicant (see 8.5.4.1).
+ */
+void
+ieee80211_recv_group_msg1(struct ieee80211com *ic,
+ const struct ieee80211_eapol_key *key, struct ieee80211_node *ni)
+{
+ struct ieee80211_key k;
+ const u_int8_t *frm, *efrm;
+ const u_int8_t *gtk;
+
+ if (ic->ic_opmode != IEEE80211_M_STA &&
+ ic->ic_opmode != IEEE80211_M_IBSS)
+ return;
+
+ if (BE_READ_8(key->replaycnt) <= ni->ni_replaycnt)
+ return;
+
+ /* parse key data field (shall contain a GTK KDE) */
+ frm = (const u_int8_t *)&key[1];
+ efrm = frm + BE_READ_2(key->paylen);
+
+ gtk = NULL;
+ while (frm + 2 <= efrm) {
+ if (frm + 2 + frm[1] > efrm)
+ break;
+ switch (frm[0]) {
+ case IEEE80211_ELEMID_VENDOR:
+ if (frm[1] < 4)
+ break;
+ if (memcmp(&frm[2], IEEE80211_OUI, 3) == 0) {
+ switch (frm[5]) {
+ case IEEE80211_KDE_GTK:
+ gtk = frm;
+ break;
+ }
+ }
+ break;
+ }
+ frm += 2 + frm[1];
+ }
+ if (gtk == NULL || !(BE_READ_2(key->info) & EAPOL_KEY_ENCRYPTED))
+ return;
+
+ /* check that the GTK KDE is valid */
+ if (gtk[1] - 4 < 2)
+ return;
+ /* install the GTK */
+ memset(&k, 0, sizeof k);
+ k.k_id = gtk[6] & 3;
+ k.k_cipher = ni->ni_group_cipher;
+ k.k_flags = IEEE80211_KEY_GROUP;
+ if (gtk[6] & (1 << 2)) /* Tx bit */
+ k.k_flags |= IEEE80211_KEY_TX;
+ k.k_len = gtk[1] - 6;
+ /* check that key length matches group cipher */
+ if (k.k_len != ieee80211_cipher_keylen(k.k_cipher))
+ return;
+ memcpy(k.k_key, &gtk[7], k.k_len);
+ k.k_rsc = LE_READ_8(key->rsc);
+ if ((*ic->ic_set_key)(ic, ni, &k) != 0)
+ return;
+
+ /* update the last seen value of the key replay counter field */
+ ni->ni_replaycnt = BE_READ_8(key->replaycnt);
+
+ if (ic->ic_if.if_flags & IFF_DEBUG)
+ printf("%s: received msg %d/%d of the %s handshake from %s\n",
+ ic->ic_if.if_xname, 1, 2, "group key",
+ ether_sprintf(ni->ni_macaddr));
+
+ /* send message 2 to authenticator */
+ ieee80211_send_group_msg2(ic, ni);
+}
+
+/*
+ * Group Key Handshake Message 2 is sent by the supplicant to the
+ * authenticator (see 8.5.4.2).
+ */
+void
+ieee80211_recv_group_msg2(struct ieee80211com *ic,
+ const struct ieee80211_eapol_key *key, struct ieee80211_node *ni)
+{
+ if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
+ ic->ic_opmode != IEEE80211_M_IBSS)
+ return;
+
+ if (BE_READ_8(key->replaycnt) != ni->ni_replaycnt)
+ return;
+
+ /* empty key data field */
+
+#ifdef notyet
+ if (--ic->ic_keydone_sta == 0) {
+ /* install GTK */
+ }
+#endif
+ if (ic->ic_if.if_flags & IFF_DEBUG)
+ printf("%s: received msg %d/%d of the %s handshake from %s\n",
+ ic->ic_if.if_xname, 2, 2, "group key",
+ ether_sprintf(ni->ni_macaddr));
+}
+
+/*
+ * EAPOL-Key Request frames are sent by the supplicant to request that the
+ * authenticator initiate either a 4-Way Handshake or Group Key Handshake
+ * and to report a MIC failure in a TKIP MSDU.
+ */
+void
+ieee80211_recv_eapol_key_req(struct ieee80211com *ic,
+ const struct ieee80211_eapol_key *key, struct ieee80211_node *ni)
+{
+ u_int16_t info;
+
+ if (ic->ic_opmode != IEEE80211_M_HOSTAP &&
+ ic->ic_opmode != IEEE80211_M_IBSS)
+ return;
+
+ info = BE_READ_2(key->info);
+
+ if (info & EAPOL_KEY_ERROR) {
+ /* TKIP MIC failure */
+
+ } else if (info & EAPOL_KEY_PAIRWISE) {
+ /* initiate the 4-Way Handshake */
+
+ } else {
+ /*
+ * Should change the GTK, initiate the 4-Way Handshake and
+ * then execute a Group Key Handshake with all supplicants.
+ */
+ }
+}
+
+#ifdef IEEE80211_DEBUG
+static void
+ieee80211_print_eapol_key(struct ieee80211com *ic,
+ const struct ieee80211_eapol_key *key, const struct ieee80211_node *ni)
+{
+ int i;
+ printf("%s: received EAPOL-Key frame from %s\n",
+ ic->ic_if.if_xname, ether_sprintf((u_int8_t *)ni->ni_macaddr));
+ printf("version=0x%02x type=0x%02x desc=0x%02x body length=%d "
+ "data length=%d\n", key->version, key->type, key->desc,
+ BE_READ_2(key->len), BE_READ_2(key->paylen));
+ printf("info=%b\n", BE_READ_2(key->info),
+ "\20\x03PAIRWISE\x06INSTALL\x07KEYACK\x08KEYMIC\x09SECURE"
+ "\x0aERROR\x0bREQUEST\x0cENCRYPTED\x0dSMK");
+ printf("Key Replay Counter=0x");
+ for (i = 0; i < 8; i++)
+ printf("%02x", key->replaycnt[i]);
+ printf("\n");
+ printf("Key Nonce=0x");
+ for (i = 0; i < EAPOL_KEY_NONCE_LEN; i++)
+ printf("%02x", key->nonce[i]);
+ printf("\n");
+ printf("EAPOL-Key IV=0x");
+ for (i = 0; i < EAPOL_KEY_IV_LEN; i++)
+ printf("%02x", key->iv[i]);
+ printf("\n");
+ printf("Key RSC=0x");
+ for (i = 0; i < 8; i++)
+ printf("%02x", key->replaycnt[i]);
+ printf("\n");
+ printf("Key MIC=0x");
+ for (i = 0; i < EAPOL_KEY_MIC_LEN; i++)
+ printf("%02x", key->mic[i]);
+ printf("\n");
+}
+#endif
+
+/*
+ * Process an incoming EAPOL frame. Notice that we are only interested in
+ * EAPOL-Key frames with an IEEE 802.11 descriptor type.
+ */
+void
+ieee80211_recv_eapol(struct ieee80211com *ic, struct mbuf *m0,
+ struct ieee80211_node *ni)
+{
+ struct ieee80211_eapol_key *key;
+ u_int16_t info;
+
+ if (m0->m_len < sizeof(*key))
+ goto out;
+
+ key = mtod(m0, struct ieee80211_eapol_key *);
+
+ if (key->version < EAPOL_VERSION)
+ goto out;
+
+ if (key->type != EAPOL_KEY ||
+ (key->desc != EAPOL_KEY_DESC_IEEE80211 &&
+ key->desc != EAPOL_KEY_DESC_WPA1))
+ goto out;
+
+ /* check packet body length */
+ if (m0->m_len < 4 + BE_READ_2(key->len))
+ goto out;
+
+ /* check key data length */
+ if (m0->m_len < sizeof(*key) + BE_READ_2(key->paylen))
+ goto out;
+
+#ifdef IEEE80211_DEBUG
+ if (ieee80211_debug > 0)
+ ieee80211_print_eapol_key(ic, key, ni);
+#endif
+ info = BE_READ_2(key->info);
+
+ if (info & (EAPOL_KEY_KEYMIC | EAPOL_KEY_ENCRYPTED)) {
+ /* check that we have a valid PTK or TPTK */
+ if (!ni->ni_ptk_ok)
+ goto out;
+
+ if (ni->ni_pairwise_cipher == IEEE80211_CIPHER_CCMP &&
+ (info & EAPOL_KEY_VERSION_MASK) != EAPOL_KEY_DESC_V2)
+ goto out;
+
+ /* check Key MIC field using KCK */
+ if ((info & EAPOL_KEY_KEYMIC) &&
+ ieee80211_eapol_key_check_mic(key, ni->ni_ptk.kck) != 0)
+ goto out;
+
+ /* decrypt Key Data field using KEK */
+ if ((info & EAPOL_KEY_ENCRYPTED) &&
+ ieee80211_eapol_key_decrypt(key, ni->ni_ptk.kek) != 0)
+ goto out;
+ }
+
+ /* determine message type (see 8.5.3.7) */
+ if (info & EAPOL_KEY_REQUEST) {
+ /* EAPOL-Key Request */
+ ieee80211_recv_eapol_key_req(ic, key, ni);
+
+ } else if (info & EAPOL_KEY_PAIRWISE) {
+ /* 4-Way Handshake */
+ if (info & EAPOL_KEY_KEYMIC) {
+ if (info & EAPOL_KEY_KEYACK) {
+ if (info & EAPOL_KEY_SECURE)
+ ieee80211_recv_4way_msg4(ic, key, ni);
+ else
+ ieee80211_recv_4way_msg2(ic, key, ni);
+ } else
+ ieee80211_recv_4way_msg3(ic, key, ni);
+ } else
+ ieee80211_recv_4way_msg1(ic, key, ni);
+ } else {
+ /* Group Key Handshake */
+ if (info & EAPOL_KEY_KEYACK)
+ ieee80211_recv_group_msg1(ic, key, ni);
+ else
+ ieee80211_recv_group_msg2(ic, key, ni);
+ }
+ out:
+ m_freem(m0);
+}
+
void
ieee80211_recv_pspoll(struct ieee80211com *ic, struct mbuf *m0, int rssi,
u_int32_t rstamp)
diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c
index 5ef66b93889..fe9f184b7f6 100644
--- a/sys/net80211/ieee80211_output.c
+++ b/sys/net80211/ieee80211_output.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_output.c,v 1.47 2007/07/14 19:58:05 damien Exp $ */
+/* $OpenBSD: ieee80211_output.c,v 1.48 2007/07/28 11:24:06 damien Exp $ */
/* $NetBSD: ieee80211_output.c,v 1.13 2004/05/31 11:02:55 dyoung Exp $ */
/*-
@@ -65,6 +65,8 @@
#include <net80211/ieee80211_var.h>
+#include <dev/rndvar.h>
+
enum ieee80211_edca_ac ieee80211_up_to_ac(struct ieee80211com *, int);
int ieee80211_classify(struct ieee80211com *, struct mbuf *);
int ieee80211_mgmt_output(struct ifnet *, struct ieee80211_node *,
@@ -86,6 +88,11 @@ struct mbuf *ieee80211_get_assoc_resp(struct ieee80211com *,
struct ieee80211_node *, u_int16_t);
struct mbuf *ieee80211_get_disassoc(struct ieee80211com *,
struct ieee80211_node *, u_int16_t);
+int ieee80211_send_eapol_key(struct ieee80211com *, struct mbuf *,
+ struct ieee80211_node *);
+u_int8_t *ieee80211_add_gtk_kde(u_int8_t *, const struct ieee80211_key *);
+u_int8_t *ieee80211_add_pmkid_kde(u_int8_t *, const u_int8_t *);
+struct mbuf *ieee80211_get_eapol_key(int, int, u_int);
/*
@@ -500,10 +507,20 @@ ieee80211_encap(struct ifnet *ifp, struct mbuf *m, struct ieee80211_node **pni)
ic->ic_stats.is_tx_nonode++;
goto bad;
}
+#if 0
+ if (!ni->ni_port_valid && eh.ether_type != htons(ETHERTYPE_PAE)) {
+ IEEE80211_DPRINTF(("%s: port not valid: %s\n",
+ __func__, ether_sprintf(eh.ether_dhost)));
+ ic->ic_stats.is_tx_noauth++;
+ goto bad;
+ }
+#endif
ni->ni_inact = 0;
if ((ic->ic_flags & IEEE80211_F_QOS) &&
- (ni->ni_flags & IEEE80211_NODE_QOS)) {
+ (ni->ni_flags & IEEE80211_NODE_QOS) &&
+ /* do not QoS-encapsulate EAPOL frames */
+ eh.ether_type != htons(ETHERTYPE_PAE)) {
tid = ieee80211_classify(ic, m);
hdrlen = sizeof(struct ieee80211_qosframe);
addqos = 1;
@@ -1331,7 +1348,7 @@ bad:
}
/*
- * Build a RTS (Request To Send) control frame.
+ * Build a RTS (Request To Send) control frame (see 7.2.1.1).
*/
struct mbuf *
ieee80211_get_rts(struct ieee80211com *ic, const struct ieee80211_frame *wh,
@@ -1358,7 +1375,7 @@ ieee80211_get_rts(struct ieee80211com *ic, const struct ieee80211_frame *wh,
}
/*
- * Build a CTS-to-self (Clear To Send) control frame.
+ * Build a CTS-to-self (Clear To Send) control frame (see 7.2.1.2).
*/
struct mbuf *
ieee80211_get_cts_to_self(struct ieee80211com *ic, u_int16_t dur)
@@ -1465,6 +1482,420 @@ ieee80211_beacon_alloc(struct ieee80211com *ic, struct ieee80211_node *ni)
return m;
}
+/* unaligned big endian access */
+#define BE_READ_2(p) \
+ ((u_int16_t)(p)[0] << 8 | (u_int16_t)(p)[1])
+
+#define BE_WRITE_2(p, v) do { \
+ (p)[0] = (v) >> 8; (p)[1] = (v); \
+} while (0)
+
+#define BE_WRITE_8(p, v) do { \
+ (p)[0] = (v) >> 56; (p)[1] = (v) >> 48; \
+ (p)[2] = (v) >> 40; (p)[3] = (v) >> 32; \
+ (p)[4] = (v) >> 24; (p)[5] = (v) >> 16; \
+ (p)[6] = (v) >> 8; (p)[7] = (v); \
+} while (0)
+
+/* unaligned little endian access */
+#define LE_WRITE_8(p, v) do { \
+ (p)[7] = (v) >> 56; (p)[6] = (v) >> 48; \
+ (p)[5] = (v) >> 40; (p)[4] = (v) >> 32; \
+ (p)[3] = (v) >> 24; (p)[2] = (v) >> 16; \
+ (p)[1] = (v) >> 8; (p)[0] = (v); \
+} while (0)
+
+int
+ieee80211_send_eapol_key(struct ieee80211com *ic, struct mbuf *m,
+ struct ieee80211_node *ni)
+{
+ struct ifnet *ifp = &ic->ic_if;
+ struct ether_header *eh;
+ struct ieee80211_eapol_key *key;
+ u_int16_t len, info;
+ int s, error;
+
+ M_PREPEND(m, sizeof(struct ether_header), M_DONTWAIT);
+ if (m == NULL)
+ return ENOMEM;
+ eh = mtod(m, struct ether_header *);
+ eh->ether_type = ETHERTYPE_PAE;
+ IEEE80211_ADDR_COPY(eh->ether_shost, ic->ic_myaddr);
+ IEEE80211_ADDR_COPY(eh->ether_dhost, ni->ni_macaddr);
+
+ key = (struct ieee80211_eapol_key *)&eh[1];
+ key->version = EAPOL_VERSION;
+ key->type = EAPOL_KEY;
+ key->desc = EAPOL_KEY_DESC_IEEE80211; /* XXX WPA1 */
+
+ info = BE_READ_2(key->info);
+ /* use V2 descriptor only when pairwise cipher is CCMP */
+ info |= (ni->ni_pairwise_cipher != IEEE80211_CIPHER_CCMP) ?
+ EAPOL_KEY_DESC_V1 : EAPOL_KEY_DESC_V2;
+ BE_WRITE_2(key->info, info);
+ BE_WRITE_2(key->paylen, m->m_len - sizeof(*key));
+
+ KASSERT((info & (EAPOL_KEY_ENCRYPTED | EAPOL_KEY_KEYMIC)) == 0 ||
+ ni->ni_ptk_ok);
+
+ if (info & EAPOL_KEY_ENCRYPTED) {
+ info &= ~EAPOL_KEY_ENCRYPTED;
+ BE_WRITE_2(key->info, info);
+ ieee80211_eapol_key_encrypt(ic, key, ni->ni_ptk.kek);
+ }
+
+ if (info & EAPOL_KEY_KEYMIC) {
+ info &= ~EAPOL_KEY_KEYMIC;
+ BE_WRITE_2(key->info, info);
+ ieee80211_eapol_key_mic(key, ni->ni_ptk.kck);
+ }
+
+ /* write packet body length field */
+ len = BE_READ_2(key->paylen);
+ BE_WRITE_2(key->len, sizeof(*key) + len);
+
+ s = splnet();
+ IFQ_ENQUEUE(&ifp->if_snd, m, NULL, error);
+ if (error) {
+ splx(s);
+ return error;
+ }
+ ifp->if_obytes += m->m_pkthdr.len;
+ if ((ifp->if_flags & IFF_OACTIVE) == 0)
+ (*ifp->if_start)(ifp);
+ splx(s);
+
+ return 0;
+}
+
+/*
+ * Add a GTK KDE to an EAPOL-Key frame (see Figure 144).
+ */
+u_int8_t *
+ieee80211_add_gtk_kde(u_int8_t *frm, const struct ieee80211_key *k)
+{
+ KASSERT(k->k_flags & IEEE80211_KEY_GROUP);
+
+ *frm++ = IEEE80211_ELEMID_VENDOR;
+ *frm++ = 6 + k->k_len;
+ memcpy(frm, IEEE80211_OUI, 3); frm += 3;
+ *frm++ = IEEE80211_KDE_GTK;
+ *frm = k->k_id & 3;
+ if (k->k_flags & IEEE80211_KEY_TX)
+ *frm |= 1 << 2; /* set the Tx bit */
+ frm++;
+ *frm++ = 0; /* reserved */
+ memcpy(frm, k->k_key, k->k_len);
+ return frm + k->k_len;
+}
+
+/*
+ * Add a PMKID KDE to an EAPOL-Key frame (see Figure 146).
+ */
+u_int8_t *
+ieee80211_add_pmkid_kde(u_int8_t *frm, const u_int8_t *pmkid)
+{
+ *frm++ = IEEE80211_ELEMID_VENDOR;
+ *frm++ = 20;
+ memcpy(frm, IEEE80211_OUI, 3); frm += 3;
+ *frm++ = IEEE80211_KDE_PMKID;
+ memcpy(frm, pmkid, IEEE80211_PMKID_LEN);
+ return frm + IEEE80211_PMKID_LEN;
+}
+
+struct mbuf *
+ieee80211_get_eapol_key(int flags, int type, u_int pktlen)
+{
+ struct mbuf *m;
+
+ pktlen += sizeof(struct ether_header) +
+ sizeof(struct ieee80211_eapol_key);
+
+ if (pktlen > MCLBYTES)
+ panic("EAPOL-Key frame too large: %u", pktlen);
+ MGETHDR(m, flags, type);
+ if (m != NULL && pktlen > MHLEN) {
+ MCLGET(m, flags);
+ if (!(m->m_flags & M_EXT))
+ m = m_free(m);
+ }
+ m->m_data += sizeof(struct ether_header);
+ return m;
+}
+
+/*
+ * 4-Way Handshake Message 1 is sent by the authenticator to the supplicant
+ * (see 8.5.3.1).
+ */
+int
+ieee80211_send_4way_msg1(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+ struct ieee80211_eapol_key *key;
+ struct mbuf *m;
+ u_int16_t info;
+ u_int8_t *pmkid;
+ u_int8_t *frm;
+
+ m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA,
+ 2 + 20);
+ if (m == NULL)
+ return ENOMEM;
+ key = mtod(m, struct ieee80211_eapol_key *);
+ memset(key, 0, sizeof(*key));
+
+ info = EAPOL_KEY_PAIRWISE | EAPOL_KEY_KEYACK;
+ BE_WRITE_2(key->info, info);
+
+ /* generate a new nonce ANonce */
+ get_random_bytes(ni->ni_nonce, EAPOL_KEY_NONCE_LEN);
+ memcpy(key->nonce, ni->ni_nonce, EAPOL_KEY_NONCE_LEN);
+
+ /* XXX retrieve PMKID from the PMKSA cache */
+
+ frm = (u_int8_t *)&key[1];
+ frm = ieee80211_add_pmkid_kde(frm, pmkid);
+
+ m->m_pkthdr.len = m->m_len = frm - (u_int8_t *)key;
+
+ if (ic->ic_if.if_flags & IFF_DEBUG)
+ printf("%s: sending msg %d/%d of the %s handshake to %s\n",
+ ic->ic_if.if_xname, 1, 4, "4-way",
+ ether_sprintf(ni->ni_macaddr));
+
+ return ieee80211_send_eapol_key(ic, m, ni);
+}
+
+/*
+ * 4-Way Handshake Message 2 is sent by the supplicant to the authenticator
+ * (see 8.5.3.2).
+ */
+int
+ieee80211_send_4way_msg2(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+ struct ieee80211_eapol_key *key;
+ struct mbuf *m;
+ u_int16_t info;
+ u_int8_t *frm;
+
+ m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA,
+ 2 + 44);
+ if (m == NULL)
+ return ENOMEM;
+ key = mtod(m, struct ieee80211_eapol_key *);
+ memset(key, 0, sizeof(*key));
+
+ info = EAPOL_KEY_PAIRWISE | EAPOL_KEY_KEYMIC;
+ BE_WRITE_2(key->info, info);
+
+ /* copy key replay counter from authenticator */
+ BE_WRITE_8(key->replaycnt, ni->ni_replaycnt);
+
+ /* XXX memcpy(key->nonce, snonce, EAPOL_KEY_NONCE_LEN); */
+
+ frm = (u_int8_t *)&key[1];
+ /* add the RSN IE used in the (Re)Association Request */
+ frm = ieee80211_add_rsn(frm, ic, ni);
+
+ m->m_pkthdr.len = m->m_len = frm - (u_int8_t *)key;
+
+ if (ic->ic_if.if_flags & IFF_DEBUG)
+ printf("%s: sending msg %d/%d of the %s handshake to %s\n",
+ ic->ic_if.if_xname, 2, 4, "4-way",
+ ether_sprintf(ni->ni_macaddr));
+
+ return ieee80211_send_eapol_key(ic, m, ni);
+}
+
+/*
+ * 4-Way Handshake Message 3 is sent by the authenticator to the supplicant
+ * (see 8.5.3.3).
+ */
+int
+ieee80211_send_4way_msg3(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+ struct ieee80211_eapol_key *key;
+ struct ieee80211_key *gtk;
+ struct mbuf *m;
+ u_int16_t info;
+ u_int8_t *frm;
+
+ m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA,
+ 2 + 44 +
+ 2 + 6 + gtk->k_len +
+ 8);
+ if (m == NULL)
+ return ENOMEM;
+ key = mtod(m, struct ieee80211_eapol_key *);
+ memset(key, 0, sizeof(*key));
+
+ /* XXX no need to encrypt if GTK is not included */
+ info = EAPOL_KEY_PAIRWISE | EAPOL_KEY_INSTALL | EAPOL_KEY_KEYACK |
+ EAPOL_KEY_KEYMIC | EAPOL_KEY_SECURE | EAPOL_KEY_ENCRYPTED;
+ BE_WRITE_2(key->info, info);
+
+ BE_WRITE_8(key->replaycnt, ni->ni_replaycnt);
+ /* use same Nonce as Message 1 */
+ memcpy(key->nonce, ni->ni_nonce, EAPOL_KEY_NONCE_LEN);
+
+ frm = (u_int8_t *)&key[1];
+ /* add the RSN IE included in Beacon/Probe Response */
+ frm = ieee80211_add_rsn(frm, ic, ni);
+ /* XXX always include GTK? */
+ frm = ieee80211_add_gtk_kde(frm, gtk);
+ LE_WRITE_8(key->rsc, gtk->k_rsc);
+
+ m->m_pkthdr.len = m->m_len = frm - (u_int8_t *)key;
+
+ if (ic->ic_if.if_flags & IFF_DEBUG)
+ printf("%s: sending msg %d/%d of the %s handshake to %s\n",
+ ic->ic_if.if_xname, 3, 4, "4-way",
+ ether_sprintf(ni->ni_macaddr));
+
+ return ieee80211_send_eapol_key(ic, m, ni);
+}
+
+/*
+ * 4-Way Handshake Message 4 is sent by the supplicant to the authenticator
+ * (see 8.5.3.4).
+ */
+int
+ieee80211_send_4way_msg4(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+ struct ieee80211_eapol_key *key;
+ struct mbuf *m;
+ u_int16_t info;
+
+ m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA, 8);
+ if (m == NULL)
+ return ENOMEM;
+ key = mtod(m, struct ieee80211_eapol_key *);
+ memset(key, 0, sizeof(*key));
+
+ info = EAPOL_KEY_PAIRWISE | EAPOL_KEY_KEYMIC | EAPOL_KEY_SECURE;
+ BE_WRITE_2(key->info, info);
+
+ /* copy key replay counter from authenticator */
+ BE_WRITE_8(key->replaycnt, ni->ni_replaycnt);
+
+ /* empty key data field */
+ m->m_pkthdr.len = m->m_len = sizeof(*key);
+
+ if (ic->ic_if.if_flags & IFF_DEBUG)
+ printf("%s: sending msg %d/%d of the %s handshake to %s\n",
+ ic->ic_if.if_xname, 4, 4, "4-way",
+ ether_sprintf(ni->ni_macaddr));
+
+ return ieee80211_send_eapol_key(ic, m, ni);
+}
+
+/*
+ * Group Key Handshake Message 1 is sent by the authenticator to the
+ * supplicant (see 8.5.4.1).
+ */
+int
+ieee80211_send_group_msg1(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+ struct ieee80211_eapol_key *key;
+ struct ieee80211_key *gtk;
+ struct mbuf *m;
+ u_int16_t info;
+ u_int8_t *frm;
+
+ m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA,
+ 2 + 6 + gtk->k_len +
+ 8);
+ if (m == NULL)
+ return ENOMEM;
+ key = mtod(m, struct ieee80211_eapol_key *);
+ memset(key, 0, sizeof(*key));
+
+ info = EAPOL_KEY_KEYACK | EAPOL_KEY_KEYMIC | EAPOL_KEY_SECURE |
+ EAPOL_KEY_ENCRYPTED;
+ BE_WRITE_2(key->info, info);
+
+ BE_WRITE_8(key->replaycnt, ni->ni_replaycnt);
+
+ frm = (u_int8_t *)&key[1];
+ frm = ieee80211_add_gtk_kde(frm, gtk);
+ LE_WRITE_8(key->rsc, gtk->k_rsc);
+
+ m->m_pkthdr.len = m->m_len = frm - (u_int8_t *)key;
+
+ if (ic->ic_if.if_flags & IFF_DEBUG)
+ printf("%s: sending msg %d/%d of the %s handshake to %s\n",
+ ic->ic_if.if_xname, 1, 2, "group key",
+ ether_sprintf(ni->ni_macaddr));
+
+ return ieee80211_send_eapol_key(ic, m, ni);
+}
+
+/*
+ * Group Key Handshake Message 2 is sent by the supplicant to the
+ * authenticator (see 8.5.4.2).
+ */
+int
+ieee80211_send_group_msg2(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+ struct ieee80211_eapol_key *key;
+ u_int16_t info;
+ struct mbuf *m;
+
+ m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA, 0);
+ if (m == NULL)
+ return ENOMEM;
+ key = mtod(m, struct ieee80211_eapol_key *);
+ memset(key, 0, sizeof(*key));
+
+ info = EAPOL_KEY_KEYMIC | EAPOL_KEY_SECURE;
+ BE_WRITE_2(key->info, info);
+
+ /* copy key replay counter from authenticator */
+ BE_WRITE_8(key->replaycnt, ni->ni_replaycnt);
+
+ /* empty key data field */
+ m->m_pkthdr.len = m->m_len = sizeof(*key);
+
+ if (ic->ic_if.if_flags & IFF_DEBUG)
+ printf("%s: sending msg %d/%d of the %s handshake to %s\n",
+ ic->ic_if.if_xname, 2, 2, "group key",
+ ether_sprintf(ni->ni_macaddr));
+
+ return ieee80211_send_eapol_key(ic, m, ni);
+}
+
+/*
+ * EAPOL-Key Request frames are sent by the supplicant to request that the
+ * authenticator initiate either a 4-Way Handshake or Group Key Handshake
+ * and to report a MIC failure in a TKIP MSDU.
+ */
+int
+ieee80211_send_eapol_key_req(struct ieee80211com *ic,
+ struct ieee80211_node *ni, int is4way)
+{
+ struct ieee80211_eapol_key *key;
+ struct mbuf *m;
+ u_int16_t info;
+
+ m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA, 0);
+ if (m == NULL)
+ return ENOMEM;
+ key = mtod(m, struct ieee80211_eapol_key *);
+ memset(key, 0, sizeof(*key));
+
+ info = is4way ? EAPOL_KEY_PAIRWISE : 0;
+ BE_WRITE_2(key->info, info);
+
+ /* use our separate key replay counter for key requests */
+ BE_WRITE_8(key->replaycnt, ic->ic_keyreplaycnt);
+ ic->ic_keyreplaycnt++;
+
+ if (ic->ic_if.if_flags & IFF_DEBUG)
+ printf("%s: sending EAPOL-Key request to %s\n",
+ ic->ic_if.if_xname, ether_sprintf(ni->ni_macaddr));
+
+ return ieee80211_send_eapol_key(ic, m, ni);
+}
+
void
ieee80211_pwrsave(struct ieee80211com *ic, struct ieee80211_node *ni,
struct mbuf *m)
diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c
index d1e3d858e49..cbf3b449f52 100644
--- a/sys/net80211/ieee80211_proto.c
+++ b/sys/net80211/ieee80211_proto.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_proto.c,v 1.19 2007/07/04 20:32:43 damien Exp $ */
+/* $OpenBSD: ieee80211_proto.c,v 1.20 2007/07/28 11:24:06 damien Exp $ */
/* $NetBSD: ieee80211_proto.c,v 1.8 2004/04/30 23:58:20 dyoung Exp $ */
/*-
@@ -109,6 +109,9 @@ ieee80211_proto_attach(struct ifnet *ifp)
/* initialize management frame handlers */
ic->ic_recv_mgmt = ieee80211_recv_mgmt;
ic->ic_send_mgmt = ieee80211_send_mgmt;
+
+ /* initialize EAPOL frame handler */
+ ic->ic_recv_eapol = ieee80211_recv_eapol;
}
void
diff --git a/sys/net80211/ieee80211_proto.h b/sys/net80211/ieee80211_proto.h
index f8cb2c63bd9..9042976fadb 100644
--- a/sys/net80211/ieee80211_proto.h
+++ b/sys/net80211/ieee80211_proto.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ieee80211_proto.h,v 1.23 2007/07/24 20:34:16 damien Exp $ */
+/* $OpenBSD: ieee80211_proto.h,v 1.24 2007/07/28 11:24:06 damien Exp $ */
/* $NetBSD: ieee80211_proto.h,v 1.3 2003/10/13 04:23:56 dyoung Exp $ */
/*-
@@ -65,6 +65,8 @@ extern void ieee80211_recv_mgmt(struct ieee80211com *, struct mbuf *,
struct ieee80211_node *, int, int, u_int32_t);
extern int ieee80211_send_mgmt(struct ieee80211com *, struct ieee80211_node *,
int, int);
+extern void ieee80211_recv_eapol(struct ieee80211com *, struct mbuf *,
+ struct ieee80211_node *);
extern struct mbuf *ieee80211_encap(struct ifnet *, struct mbuf *,
struct ieee80211_node **);
extern struct mbuf *ieee80211_get_rts(struct ieee80211com *,
@@ -73,6 +75,20 @@ extern struct mbuf *ieee80211_get_cts_to_self(struct ieee80211com *,
u_int16_t);
extern struct mbuf *ieee80211_beacon_alloc(struct ieee80211com *,
struct ieee80211_node *);
+extern int ieee80211_send_4way_msg1(struct ieee80211com *,
+ struct ieee80211_node *);
+extern int ieee80211_send_4way_msg2(struct ieee80211com *,
+ struct ieee80211_node *);
+extern int ieee80211_send_4way_msg3(struct ieee80211com *,
+ struct ieee80211_node *);
+extern int ieee80211_send_4way_msg4(struct ieee80211com *,
+ struct ieee80211_node *);
+extern int ieee80211_send_group_msg1(struct ieee80211com *,
+ struct ieee80211_node *);
+extern int ieee80211_send_group_msg2(struct ieee80211com *,
+ struct ieee80211_node *);
+extern int ieee80211_send_eapol_key_req(struct ieee80211com *,
+ struct ieee80211_node *, int);
extern void ieee80211_pwrsave(struct ieee80211com *, struct ieee80211_node *,
struct mbuf *);
extern struct mbuf *ieee80211_decap(struct ifnet *, struct mbuf *, int);