]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
FILS: DHCP relay for HLP requests
authorJouni Malinen <jouni@qca.qualcomm.com>
Tue, 31 Jan 2017 12:38:44 +0000 (14:38 +0200)
committerJouni Malinen <j@w1.fi>
Wed, 1 Feb 2017 16:17:39 +0000 (18:17 +0200)
The new dhcp_server configuration parameter can now be used to configure
hostapd to act as a DHCP relay for DHCPDISCOVER messages received as
FILS HLP requests. The dhcp_rapid_commit_proxy=1 parameter can be used
to configure hostapd to convert 4 message DHCP exchange into a 2 message
exchange in case the DHCP server does not support DHCP rapid commit
option.

The fils_hlp_wait_time parameter can be used to set the time hostapd
waits for an HLP response. This matches the dot11HLPWaitTime in IEEE Std
802.11ai-2016.

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
14 files changed:
hostapd/config_file.c
hostapd/hostapd.conf
src/ap/ap_config.c
src/ap/ap_config.h
src/ap/fils_hlp.c
src/ap/fils_hlp.h
src/ap/hostapd.c
src/ap/hostapd.h
src/ap/ieee802_11.c
src/ap/ieee802_11.h
src/ap/sta_info.c
src/ap/sta_info.h
src/ap/wpa_auth.c
src/ap/wpa_auth.h

index b3970345d29796b238c04f6868361d8da8890616..8cfa198c347be5c6f3432e1e5d04b930cbcf4db4 100644 (file)
@@ -3615,6 +3615,21 @@ static int hostapd_config_fill(struct hostapd_config *conf,
        } else if (os_strcmp(buf, "fils_realm") == 0) {
                if (parse_fils_realm(bss, pos) < 0)
                        return 1;
+       } else if (os_strcmp(buf, "dhcp_server") == 0) {
+               if (hostapd_parse_ip_addr(pos, &bss->dhcp_server)) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: invalid IP address '%s'",
+                                  line, pos);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "dhcp_rapid_commit_proxy") == 0) {
+               bss->dhcp_rapid_commit_proxy = atoi(pos);
+       } else if (os_strcmp(buf, "fils_hlp_wait_time") == 0) {
+               bss->fils_hlp_wait_time = atoi(pos);
+       } else if (os_strcmp(buf, "dhcp_server_port") == 0) {
+               bss->dhcp_server_port = atoi(pos);
+       } else if (os_strcmp(buf, "dhcp_relay_port") == 0) {
+               bss->dhcp_relay_port = atoi(pos);
 #endif /* CONFIG_FILS */
        } else if (os_strcmp(buf, "multicast_to_unicast") == 0) {
                bss->multicast_to_unicast = atoi(pos);
index c9b105aa956cce8df5f3bdb51c9be5f8e0ea07ea..314f3842b9e5a83c7dd40e708afa565f4e714226 100644 (file)
@@ -1316,6 +1316,34 @@ own_ip_addr=127.0.0.1
 #fils_realm=example.com
 #fils_realm=example.org
 
+# DHCP server for FILS HLP
+# If configured, hostapd will act as a DHCP relay for all FILS HLP requests
+# that include a DHCPDISCOVER message and send them to the specific DHCP
+# server for processing. hostapd will then wait for a response from that server
+# before replying with (Re)Association Response frame that encapsulates this
+# DHCP response. own_ip_addr is used as the local address for the communication
+# with the DHCP server.
+#dhcp_server=127.0.0.1
+
+# DHCP server UDP port
+# Default: 67
+#dhcp_server_port=67
+
+# DHCP relay UDP port on the local device
+# Default: 67; 0 means not to bind any specific port
+#dhcp_relay_port=67
+
+# DHCP rapid commit proxy
+# If set to 1, this enables hostapd to act as a DHCP rapid commit proxy to
+# allow the rapid commit options (two message DHCP exchange) to be used with a
+# server that supports only the four message DHCP exchange. This is disabled by
+# default (= 0) and can be enabled by setting this to 1.
+#dhcp_rapid_commit_proxy=0
+
+# Wait time for FILS HLP (dot11HLPWaitTime) in TUs
+# default: 30 TUs (= 30.72 milliseconds)
+#fils_hlp_wait_time=30
+
 ##### IEEE 802.11r configuration ##############################################
 
 # Mobility Domain identifier (dot11FTMobilityDomainID, MDID)
index e417a1247a2550ccfeb19f99ac1876ef1dab5713..c2b80ad97ee029ff5e3f29302fd4101c16ecbcee 100644 (file)
@@ -13,6 +13,7 @@
 #include "radius/radius_client.h"
 #include "common/ieee802_11_defs.h"
 #include "common/eapol_common.h"
+#include "common/dhcp.h"
 #include "eap_common/eap_wsc_common.h"
 #include "eap_server/eap.h"
 #include "wpa_auth.h"
@@ -100,6 +101,9 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
 
 #ifdef CONFIG_FILS
        dl_list_init(&bss->fils_realms);
+       bss->fils_hlp_wait_time = 30;
+       bss->dhcp_server_port = DHCP_SERVER_PORT;
+       bss->dhcp_relay_port = DHCP_SERVER_PORT;
 #endif /* CONFIG_FILS */
 }
 
index 075261c74e7d559a4870051e3234f7e905abfc93..31b1e7762b57e67716fdb1388013002730f53962 100644 (file)
@@ -607,6 +607,11 @@ struct hostapd_bss_config {
        u8 fils_cache_id[FILS_CACHE_ID_LEN];
        int fils_cache_id_set;
        struct dl_list fils_realms; /* list of struct fils_realm */
+       struct hostapd_ip_addr dhcp_server;
+       int dhcp_rapid_commit_proxy;
+       unsigned int fils_hlp_wait_time;
+       u16 dhcp_server_port;
+       u16 dhcp_relay_port;
 #endif /* CONFIG_FILS */
 
        int multicast_to_unicast;
index 3fde71efc3e695965b5adefcc416b1402912cf68..c66c1f15ff9efe6315081cc5ee06ac9e5605bc5e 100644 (file)
 #include "utils/includes.h"
 
 #include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/dhcp.h"
+#include "hostapd.h"
 #include "sta_info.h"
+#include "ieee802_11.h"
 #include "fils_hlp.h"
 
 
-static void fils_process_hlp_req(struct hostapd_data *hapd,
+static be16 ip_checksum(const void *buf, size_t len)
+{
+       u32 sum = 0;
+       const u16 *pos;
+
+       for (pos = buf; len >= 2; len -= 2)
+               sum += ntohs(*pos++);
+       if (len)
+               sum += ntohs(*pos << 8);
+
+       sum = (sum >> 16) + (sum & 0xffff);
+       sum += sum >> 16;
+       return htons(~sum);
+}
+
+
+static int fils_dhcp_request(struct hostapd_data *hapd, struct sta_info *sta,
+                            struct dhcp_data *dhcpoffer, u8 *dhcpofferend)
+{
+       u8 *pos, *end;
+       struct dhcp_data *dhcp;
+       struct sockaddr_in addr;
+       ssize_t res;
+       const u8 *server_id = NULL;
+
+       if (!sta->hlp_dhcp_discover) {
+               wpa_printf(MSG_DEBUG,
+                          "FILS: No pending HLP DHCPDISCOVER available");
+               return -1;
+       }
+
+       /* Convert to DHCPREQUEST, remove rapid commit option, replace requested
+        * IP address option with yiaddr. */
+       pos = wpabuf_mhead(sta->hlp_dhcp_discover);
+       end = pos + wpabuf_len(sta->hlp_dhcp_discover);
+       dhcp = (struct dhcp_data *) pos;
+       pos = (u8 *) (dhcp + 1);
+       pos += 4; /* skip magic */
+       while (pos < end && *pos != DHCP_OPT_END) {
+               u8 opt, olen;
+
+               opt = *pos++;
+               if (opt == DHCP_OPT_PAD)
+                       continue;
+               if (pos >= end)
+                       break;
+               olen = *pos++;
+               if (olen > end - pos)
+                       break;
+
+               switch (opt) {
+               case DHCP_OPT_MSG_TYPE:
+                       if (olen > 0)
+                               *pos = DHCPREQUEST;
+                       break;
+               case DHCP_OPT_RAPID_COMMIT:
+               case DHCP_OPT_REQUESTED_IP_ADDRESS:
+               case DHCP_OPT_SERVER_ID:
+                       /* Remove option */
+                       pos -= 2;
+                       os_memmove(pos, pos + 2 + olen, end - pos - 2 - olen);
+                       end -= 2 + olen;
+                       olen = 0;
+                       break;
+               }
+               pos += olen;
+       }
+       if (pos >= end || *pos != DHCP_OPT_END) {
+               wpa_printf(MSG_DEBUG, "FILS: Could not update DHCPDISCOVER");
+               return -1;
+       }
+       sta->hlp_dhcp_discover->used = pos - (u8 *) dhcp;
+
+       /* Copy Server ID option from DHCPOFFER to DHCPREQUEST */
+       pos = (u8 *) (dhcpoffer + 1);
+       end = dhcpofferend;
+       pos += 4; /* skip magic */
+       while (pos < end && *pos != DHCP_OPT_END) {
+               u8 opt, olen;
+
+               opt = *pos++;
+               if (opt == DHCP_OPT_PAD)
+                       continue;
+               if (pos >= end)
+                       break;
+               olen = *pos++;
+               if (olen > end - pos)
+                       break;
+
+               switch (opt) {
+               case DHCP_OPT_SERVER_ID:
+                       server_id = pos - 2;
+                       break;
+               }
+               pos += olen;
+       }
+
+       if (wpabuf_resize(&sta->hlp_dhcp_discover,
+                         6 + 1 + (server_id ? 2 + server_id[1] : 0)))
+               return -1;
+       if (server_id)
+               wpabuf_put_data(sta->hlp_dhcp_discover, server_id,
+                               2 + server_id[1]);
+       wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_REQUESTED_IP_ADDRESS);
+       wpabuf_put_u8(sta->hlp_dhcp_discover, 4);
+       wpabuf_put_data(sta->hlp_dhcp_discover, &dhcpoffer->your_ip, 4);
+       wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_END);
+
+       os_memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr;
+       addr.sin_port = htons(hapd->conf->dhcp_server_port);
+       res = sendto(hapd->dhcp_sock, wpabuf_head(sta->hlp_dhcp_discover),
+                    wpabuf_len(sta->hlp_dhcp_discover), 0,
+                    (const struct sockaddr *) &addr, sizeof(addr));
+       if (res < 0) {
+               wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s",
+                          strerror(errno));
+               return -1;
+       }
+       wpa_printf(MSG_DEBUG,
+                  "FILS: Acting as DHCP rapid commit proxy for %s:%d",
+                  inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+       wpabuf_free(sta->hlp_dhcp_discover);
+       sta->hlp_dhcp_discover = NULL;
+       sta->fils_dhcp_rapid_commit_proxy = 1;
+       return 0;
+}
+
+
+static void fils_dhcp_handler(int sd, void *eloop_ctx, void *sock_ctx)
+{
+       struct hostapd_data *hapd = sock_ctx;
+       struct sta_info *sta;
+       u8 buf[1500], *pos, *end, *end_opt = NULL;
+       struct dhcp_data *dhcp;
+       struct sockaddr_in addr;
+       socklen_t addr_len;
+       ssize_t res;
+       u8 msgtype = 0;
+       int rapid_commit = 0;
+       struct iphdr *iph;
+       struct udphdr *udph;
+       struct wpabuf *resp;
+       const u8 *rpos;
+       size_t left, len;
+
+       addr_len = sizeof(addr);
+       res = recvfrom(sd, buf, sizeof(buf), 0,
+                      (struct sockaddr *) &addr, &addr_len);
+       if (res < 0) {
+               wpa_printf(MSG_DEBUG, "FILS: DHCP read failed: %s",
+                          strerror(errno));
+               return;
+       }
+       wpa_printf(MSG_DEBUG, "FILS: DHCP response from server %s:%d (len=%d)",
+                  inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), (int) res);
+       wpa_hexdump(MSG_MSGDUMP, "FILS: HLP - DHCP server response", buf, res);
+       if ((size_t) res < sizeof(*dhcp))
+               return;
+       dhcp = (struct dhcp_data *) buf;
+       if (dhcp->op != 2)
+               return; /* Not a BOOTREPLY */
+       if (dhcp->relay_ip != hapd->conf->own_ip_addr.u.v4.s_addr) {
+               wpa_printf(MSG_DEBUG,
+                          "FILS: HLP - DHCP response to unknown relay address 0x%x",
+                          dhcp->relay_ip);
+               return;
+       }
+       dhcp->relay_ip = 0;
+       pos = (u8 *) (dhcp + 1);
+       end = &buf[res];
+
+       if (end - pos < 4 || WPA_GET_BE32(pos) != DHCP_MAGIC) {
+               wpa_printf(MSG_DEBUG, "FILS: HLP - no DHCP magic in response");
+               return;
+       }
+       pos += 4;
+
+       wpa_hexdump(MSG_DEBUG, "FILS: HLP - DHCP options in response",
+                   pos, end - pos);
+       while (pos < end && *pos != DHCP_OPT_END) {
+               u8 opt, olen;
+
+               opt = *pos++;
+               if (opt == DHCP_OPT_PAD)
+                       continue;
+               if (pos >= end)
+                       break;
+               olen = *pos++;
+               if (olen > end - pos)
+                       break;
+
+               switch (opt) {
+               case DHCP_OPT_MSG_TYPE:
+                       if (olen > 0)
+                               msgtype = pos[0];
+                       break;
+               case DHCP_OPT_RAPID_COMMIT:
+                       rapid_commit = 1;
+                       break;
+               }
+               pos += olen;
+       }
+       if (pos < end && *pos == DHCP_OPT_END)
+               end_opt = pos;
+
+       wpa_printf(MSG_DEBUG,
+                  "FILS: HLP - DHCP message type %u (rapid_commit=%d hw_addr="
+                  MACSTR ")",
+                  msgtype, rapid_commit, MAC2STR(dhcp->hw_addr));
+
+       sta = ap_get_sta(hapd, dhcp->hw_addr);
+       if (!sta || !sta->fils_pending_assoc_req) {
+               wpa_printf(MSG_DEBUG,
+                          "FILS: No pending HLP DHCP exchange with hw_addr"
+                          MACSTR, MAC2STR(dhcp->hw_addr));
+               return;
+       }
+
+       if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPOFFER &&
+           !rapid_commit) {
+               /* Use hostapd to take care of 4-message exchange and convert
+                * the final DHCPACK to rapid commit version. */
+               if (fils_dhcp_request(hapd, sta, dhcp, end) == 0)
+                       return;
+               /* failed, so send the server response as-is */
+       } else if (msgtype != DHCPACK) {
+               wpa_printf(MSG_DEBUG,
+                          "FILS: No DHCPACK available from the server and cannot do rapid commit proxying");
+       }
+
+       pos = buf;
+       resp = wpabuf_alloc(2 * ETH_ALEN + 6 + 2 +
+                           sizeof(*iph) + sizeof(*udph) + (end - pos) + 2);
+       if (!resp)
+               return;
+       wpabuf_put_data(resp, sta->addr, ETH_ALEN);
+       wpabuf_put_data(resp, hapd->own_addr, ETH_ALEN);
+       wpabuf_put_data(resp, "\xaa\xaa\x03\x00\x00\x00", 6);
+       wpabuf_put_be16(resp, ETH_P_IP);
+       iph = wpabuf_put(resp, sizeof(*iph));
+       iph->version = 4;
+       iph->ihl = sizeof(*iph) / 4;
+       iph->tot_len = htons(sizeof(*iph) + sizeof(*udph) + (end - pos));
+       iph->ttl = 1;
+       iph->saddr = hapd->conf->dhcp_server.u.v4.s_addr;
+       iph->daddr = dhcp->client_ip;
+       iph->check = ip_checksum(iph, sizeof(*iph));
+       udph = wpabuf_put(resp, sizeof(*udph));
+       udph->uh_sport = htons(DHCP_SERVER_PORT);
+       udph->uh_dport = htons(DHCP_CLIENT_PORT);
+       udph->len = htons(sizeof(*udph) + (end - pos));
+       udph->check = htons(0x0000); /* TODO: calculate checksum */
+       if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPACK &&
+           !rapid_commit && sta->fils_dhcp_rapid_commit_proxy && end_opt) {
+               /* Add rapid commit option */
+               wpabuf_put_data(resp, pos, end_opt - pos);
+               wpabuf_put_u8(resp, DHCP_OPT_RAPID_COMMIT);
+               wpabuf_put_u8(resp, 0);
+               wpabuf_put_data(resp, end_opt, end - end_opt);
+       } else {
+               wpabuf_put_data(resp, pos, end - pos);
+       }
+       if (!wpabuf_resize(&sta->fils_hlp_resp, wpabuf_len(resp) +
+                          2 * wpabuf_len(resp) / 255 + 100) == 0) {
+               wpabuf_free(resp);
+               return;
+       }
+
+       rpos = wpabuf_head(resp);
+       left = wpabuf_len(resp);
+
+       wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_EXTENSION); /* Element ID */
+       if (left <= 254)
+               len = 1 + left;
+       else
+               len = 255;
+       wpabuf_put_u8(sta->fils_hlp_resp, len); /* Length */
+       /* Element ID Extension */
+       wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_EXT_FILS_HLP_CONTAINER);
+       /* Destination MAC Address, Source MAC Address, HLP Packet.
+        * HLP Packet is in MSDU format (i.e., including the LLC/SNAP header
+        * when LPD is used). */
+       wpabuf_put_data(sta->fils_hlp_resp, rpos, len - 1);
+       rpos += len - 1;
+       left -= len - 1;
+       while (left) {
+               wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_FRAGMENT);
+               len = left > 255 ? 255 : left;
+               wpabuf_put_u8(sta->fils_hlp_resp, len);
+               wpabuf_put_data(sta->fils_hlp_resp, rpos, len);
+               rpos += len;
+               left -= len;
+       }
+       wpabuf_free(resp);
+       fils_hlp_finish_assoc(hapd, sta);
+}
+
+
+static int fils_process_hlp_dhcp(struct hostapd_data *hapd,
                                 struct sta_info *sta,
-                                const u8 *pos, size_t len)
+                                const u8 *msg, size_t len)
+{
+       const struct dhcp_data *dhcp;
+       struct wpabuf *dhcp_buf;
+       struct dhcp_data *dhcp_msg;
+       u8 msgtype = 0;
+       int rapid_commit = 0;
+       const u8 *pos = msg, *end;
+       struct sockaddr_in addr;
+       ssize_t res;
+
+       if (len < sizeof(*dhcp))
+               return 0;
+       dhcp = (const struct dhcp_data *) pos;
+       end = pos + len;
+       wpa_printf(MSG_DEBUG,
+                  "FILS: HLP request DHCP: op=%u htype=%u hlen=%u hops=%u xid=0x%x",
+                  dhcp->op, dhcp->htype, dhcp->hlen, dhcp->hops,
+                  ntohl(dhcp->xid));
+       pos += sizeof(*dhcp);
+       if (dhcp->op != 1)
+               return 0; /* Not a BOOTREQUEST */
+
+       if (end - pos < 4)
+               return 0;
+       if (WPA_GET_BE32(pos) != DHCP_MAGIC) {
+               wpa_printf(MSG_DEBUG, "FILS: HLP - no DHCP magic");
+               return 0;
+       }
+       pos += 4;
+
+       wpa_hexdump(MSG_DEBUG, "FILS: HLP - DHCP options", pos, end - pos);
+       while (pos < end && *pos != DHCP_OPT_END) {
+               u8 opt, olen;
+
+               opt = *pos++;
+               if (opt == DHCP_OPT_PAD)
+                       continue;
+               if (pos >= end)
+                       break;
+               olen = *pos++;
+               if (olen > end - pos)
+                       break;
+
+               switch (opt) {
+               case DHCP_OPT_MSG_TYPE:
+                       if (olen > 0)
+                               msgtype = pos[0];
+                       break;
+               case DHCP_OPT_RAPID_COMMIT:
+                       rapid_commit = 1;
+                       break;
+               }
+               pos += olen;
+       }
+
+       wpa_printf(MSG_DEBUG, "FILS: HLP - DHCP message type %u", msgtype);
+       if (msgtype != DHCPDISCOVER)
+               return 0;
+
+       if (hapd->conf->dhcp_server.af != AF_INET ||
+           hapd->conf->dhcp_server.u.v4.s_addr == 0) {
+               wpa_printf(MSG_DEBUG,
+                          "FILS: HLP - no DHCPv4 server configured - drop request");
+               return 0;
+       }
+
+       if (hapd->conf->own_ip_addr.af != AF_INET ||
+           hapd->conf->own_ip_addr.u.v4.s_addr == 0) {
+               wpa_printf(MSG_DEBUG,
+                          "FILS: HLP - no IPv4 own_ip_addr configured - drop request");
+               return 0;
+       }
+
+       if (hapd->dhcp_sock < 0) {
+               int s;
+
+               s = socket(AF_INET, SOCK_DGRAM, 0);
+               if (s < 0) {
+                       wpa_printf(MSG_ERROR,
+                                  "FILS: Failed to open DHCP socket: %s",
+                                  strerror(errno));
+                       return 0;
+               }
+
+               if (hapd->conf->dhcp_relay_port) {
+                       os_memset(&addr, 0, sizeof(addr));
+                       addr.sin_family = AF_INET;
+                       addr.sin_addr.s_addr =
+                               hapd->conf->own_ip_addr.u.v4.s_addr;
+                       addr.sin_port = htons(hapd->conf->dhcp_relay_port);
+                       if (bind(s, (struct sockaddr *) &addr, sizeof(addr))) {
+                               wpa_printf(MSG_ERROR,
+                                          "FILS: Failed to bind DHCP socket: %s",
+                                          strerror(errno));
+                               close(s);
+                               return 0;
+                       }
+               }
+               if (eloop_register_sock(s, EVENT_TYPE_READ,
+                                       fils_dhcp_handler, NULL, hapd)) {
+                       close(s);
+                       return 0;
+               }
+
+               hapd->dhcp_sock = s;
+       }
+
+       dhcp_buf = wpabuf_alloc(len);
+       if (!dhcp_buf)
+               return 0;
+       dhcp_msg = wpabuf_put(dhcp_buf, len);
+       os_memcpy(dhcp_msg, msg, len);
+       dhcp_msg->relay_ip = hapd->conf->own_ip_addr.u.v4.s_addr;
+       os_memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr;
+       addr.sin_port = htons(hapd->conf->dhcp_server_port);
+       res = sendto(hapd->dhcp_sock, dhcp_msg, len, 0,
+                    (const struct sockaddr *) &addr, sizeof(addr));
+       if (res < 0) {
+               wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s",
+                          strerror(errno));
+               wpabuf_free(dhcp_buf);
+               /* Close the socket to try to recover from error */
+               eloop_unregister_read_sock(hapd->dhcp_sock);
+               close(hapd->dhcp_sock);
+               hapd->dhcp_sock = -1;
+               return 0;
+       }
+
+       wpa_printf(MSG_DEBUG,
+                  "FILS: HLP relayed DHCP request to server %s:%d (rapid_commit=%d)",
+                  inet_ntoa(addr.sin_addr), ntohs(addr.sin_port),
+                  rapid_commit);
+       if (hapd->conf->dhcp_rapid_commit_proxy && rapid_commit) {
+               /* Store a copy of the DHCPDISCOVER for rapid commit proxying
+                * purposes if the server does not support the rapid commit
+                * option. */
+               wpa_printf(MSG_DEBUG,
+                          "FILS: Store DHCPDISCOVER for rapid commit proxy");
+               wpabuf_free(sta->hlp_dhcp_discover);
+               sta->hlp_dhcp_discover = dhcp_buf;
+       } else {
+               wpabuf_free(dhcp_buf);
+       }
+
+       return 1;
+}
+
+
+static int fils_process_hlp_udp(struct hostapd_data *hapd,
+                               struct sta_info *sta, const u8 *dst,
+                               const u8 *pos, size_t len)
+{
+       const struct iphdr *iph;
+       const struct udphdr *udph;
+       u16 sport, dport, ulen;
+
+       if (len < sizeof(*iph) + sizeof(*udph))
+               return 0;
+       iph = (const struct iphdr *) pos;
+       udph = (const struct udphdr *) (iph + 1);
+       sport = ntohs(udph->uh_sport);
+       dport = ntohs(udph->uh_dport);
+       ulen = ntohs(udph->uh_ulen);
+       wpa_printf(MSG_DEBUG,
+                  "FILS: HLP request UDP: sport=%u dport=%u ulen=%u sum=0x%x",
+                  sport, dport, ulen, ntohs(udph->uh_sum));
+       /* TODO: Check UDP checksum */
+       if (ulen < sizeof(*udph) || ulen > len - sizeof(*iph))
+               return 0;
+
+       if (dport == DHCP_SERVER_PORT && sport == DHCP_CLIENT_PORT) {
+               return fils_process_hlp_dhcp(hapd, sta, (const u8 *) (udph + 1),
+                                            ulen - sizeof(*udph));
+       }
+
+       return 0;
+}
+
+
+static int fils_process_hlp_ip(struct hostapd_data *hapd,
+                              struct sta_info *sta, const u8 *dst,
+                              const u8 *pos, size_t len)
+{
+       const struct iphdr *iph;
+       u16 tot_len;
+
+       if (len < sizeof(*iph))
+               return 0;
+       iph = (const struct iphdr *) pos;
+       if (ip_checksum(iph, sizeof(*iph)) != 0) {
+               wpa_printf(MSG_DEBUG,
+                          "FILS: HLP request IPv4 packet had invalid header checksum - dropped");
+               return 0;
+       }
+       tot_len = ntohs(iph->tot_len);
+       if (tot_len > len)
+               return 0;
+       wpa_printf(MSG_DEBUG,
+                  "FILS: HLP request IPv4: saddr=%08x daddr=%08x protocol=%u",
+                  iph->saddr, iph->daddr, iph->protocol);
+       switch (iph->protocol) {
+       case 17:
+               return fils_process_hlp_udp(hapd, sta, dst, pos, len);
+       }
+
+       return 0;
+}
+
+
+static int fils_process_hlp_req(struct hostapd_data *hapd,
+                               struct sta_info *sta,
+                               const u8 *pos, size_t len)
 {
        const u8 *pkt, *end;
 
@@ -27,7 +545,7 @@ static void fils_process_hlp_req(struct hostapd_data *hapd,
                wpa_printf(MSG_DEBUG,
                           "FILS: Ignore HLP request with unexpected source address"
                           MACSTR, MAC2STR(pos + ETH_ALEN));
-               return;
+               return 0;
        }
 
        end = pos + len;
@@ -36,19 +554,36 @@ static void fils_process_hlp_req(struct hostapd_data *hapd,
            os_memcmp(pkt, "\xaa\xaa\x03\x00\x00\x00", 6) == 0)
                pkt += 6; /* Remove SNAP/LLC header */
        wpa_hexdump(MSG_MSGDUMP, "FILS: HLP request packet", pkt, end - pkt);
+
+       if (end - pkt < 2)
+               return 0;
+
+       switch (WPA_GET_BE16(pkt)) {
+       case ETH_P_IP:
+               return fils_process_hlp_ip(hapd, sta, pos, pkt + 2,
+                                          end - pkt - 2);
+       }
+
+       return 0;
 }
 
 
-void fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta,
-                     const u8 *pos, int left)
+int fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta,
+                    const u8 *pos, int left)
 {
        const u8 *end = pos + left;
        u8 *tmp, *tmp_pos;
+       int ret = 0;
+
+       /* Old DHCPDISCOVER is not needed anymore, if it was still pending */
+       wpabuf_free(sta->hlp_dhcp_discover);
+       sta->hlp_dhcp_discover = NULL;
+       sta->fils_dhcp_rapid_commit_proxy = 0;
 
        /* Check if there are any FILS HLP Container elements */
        while (end - pos >= 2) {
                if (2 + pos[1] > end - pos)
-                       return;
+                       return 0;
                if (pos[0] == WLAN_EID_EXTENSION &&
                    pos[1] >= 1 + 2 * ETH_ALEN &&
                    pos[2] == WLAN_EID_EXT_FILS_HLP_CONTAINER)
@@ -56,11 +591,11 @@ void fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta,
                pos += 2 + pos[1];
        }
        if (end - pos < 2)
-               return; /* No FILS HLP Container elements */
+               return 0; /* No FILS HLP Container elements */
 
        tmp = os_malloc(end - pos);
        if (!tmp)
-               return;
+               return 0;
 
        while (end - pos >= 2) {
                if (2 + pos[1] > end - pos ||
@@ -81,8 +616,21 @@ void fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta,
                        pos += 2 + pos[1];
                }
 
-               fils_process_hlp_req(hapd, sta, tmp, tmp_pos - tmp);
+               if (fils_process_hlp_req(hapd, sta, tmp, tmp_pos - tmp) > 0)
+                       ret = 1;
        }
 
        os_free(tmp);
+
+       return ret;
+}
+
+
+void fils_hlp_deinit(struct hostapd_data *hapd)
+{
+       if (hapd->dhcp_sock >= 0) {
+               eloop_unregister_read_sock(hapd->dhcp_sock);
+               close(hapd->dhcp_sock);
+               hapd->dhcp_sock = -1;
+       }
 }
index 98cc3195b54fe603edf8766c81f6fd92204f5483..e14a6bf65e04b0c1917b768a5580375ac15b73b7 100644 (file)
@@ -9,7 +9,19 @@
 #ifndef FILS_HLP_H
 #define FILS_HLP_H
 
-void fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta,
-                     const u8 *pos, int left);
+int fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta,
+                    const u8 *pos, int left);
+
+#ifdef CONFIG_FILS
+
+void fils_hlp_deinit(struct hostapd_data *hapd);
+
+#else /* CONFIG_FILS */
+
+static inline void fils_hlp_deinit(struct hostapd_data *hapd)
+{
+}
+
+#endif /* CONFIG_FILS */
 
 #endif /* FILS_HLP_H */
index 26ebbb666cd12d4ae240c36a1f90116b7364f941..47fa144c4e9bddbbd098b2b4f24b478ee36ca29c 100644 (file)
@@ -45,6 +45,7 @@
 #include "ndisc_snoop.h"
 #include "neighbor_db.h"
 #include "rrm.h"
+#include "fils_hlp.h"
 
 
 static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
@@ -344,6 +345,7 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd)
 #endif /* CONFIG_MESH */
 
        hostapd_clean_rrm(hapd);
+       fils_hlp_deinit(hapd);
 }
 
 
@@ -2006,6 +2008,7 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
        hapd->ctrl_sock = -1;
        dl_list_init(&hapd->ctrl_dst);
        dl_list_init(&hapd->nr_db);
+       hapd->dhcp_sock = -1;
 
        return hapd;
 }
index bc0ac23b0ac3489975bd62ef3d3b25bc2fa46857..5ab623de1549787638f11bf0de9d933f30ebf224 100644 (file)
@@ -303,6 +303,8 @@ struct hostapd_data {
        u8 range_req_token;
        unsigned int lci_req_active:1;
        unsigned int range_req_active:1;
+
+       int dhcp_sock; /* UDP socket used with the DHCP server */
 };
 
 
index 129cc158d73abe949fcbae93f039fc5178472694..d9bc976e371651ef08501e7840256e342323a882 100644 (file)
@@ -2266,11 +2266,22 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
                           const u8 *ies, size_t ies_len)
 {
        int send_len;
-       u8 buf[sizeof(struct ieee80211_mgmt) + 1024];
+       u8 *buf;
+       size_t buflen;
        struct ieee80211_mgmt *reply;
        u8 *p;
+       u16 res = WLAN_STATUS_SUCCESS;
 
-       os_memset(buf, 0, sizeof(buf));
+       buflen = sizeof(struct ieee80211_mgmt) + 1024;
+#ifdef CONFIG_FILS
+       if (sta->fils_hlp_resp)
+               buflen += wpabuf_len(sta->fils_hlp_resp);
+#endif /* CONFIG_FILS */
+       buf = os_zalloc(buflen);
+       if (!buf) {
+               res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+               goto done;
+       }
        reply = (struct ieee80211_mgmt *) buf;
        reply->frame_control =
                IEEE80211_FC(WLAN_FC_TYPE_MGMT,
@@ -2298,7 +2309,7 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
                /* IEEE 802.11r: Mobility Domain Information, Fast BSS
                 * Transition Information, RSN, [RIC Response] */
                p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, p,
-                                               buf + sizeof(buf) - p,
+                                               buf + buflen - p,
                                                sta->auth_alg, ies, ies_len);
        }
 #endif /* CONFIG_IEEE80211R_AP */
@@ -2400,10 +2411,10 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
                p = hostapd_eid_p2p_manage(hapd, p);
 #endif /* CONFIG_P2P_MANAGER */
 
-       p = hostapd_eid_mbo(hapd, p, buf + sizeof(buf) - p);
+       p = hostapd_eid_mbo(hapd, p, buf + buflen - p);
 
        if (hapd->conf->assocresp_elements &&
-           (size_t) (buf + sizeof(buf) - p) >=
+           (size_t) (buf + buflen - p) >=
            wpabuf_len(hapd->conf->assocresp_elements)) {
                os_memcpy(p, wpabuf_head(hapd->conf->assocresp_elements),
                          wpabuf_len(hapd->conf->assocresp_elements));
@@ -2421,8 +2432,10 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
                struct ieee802_11_elems elems;
 
                if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) ==
-                   ParseFailed || !elems.fils_session)
-                       return WLAN_STATUS_UNSPECIFIED_FAILURE;
+                   ParseFailed || !elems.fils_session) {
+                       res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                       goto done;
+               }
 
                /* FILS Session */
                *p++ = WLAN_EID_EXTENSION; /* Element ID */
@@ -2432,22 +2445,75 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
                send_len += 2 + 1 + FILS_SESSION_LEN;
 
                send_len = fils_encrypt_assoc(sta->wpa_sm, buf, send_len,
-                                             sizeof(buf));
-               if (send_len < 0)
-                       return WLAN_STATUS_UNSPECIFIED_FAILURE;
+                                             buflen, sta->fils_hlp_resp);
+               if (send_len < 0) {
+                       res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                       goto done;
+               }
        }
 #endif /* CONFIG_FILS */
 
        if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0) {
                wpa_printf(MSG_INFO, "Failed to send assoc resp: %s",
                           strerror(errno));
-               return WLAN_STATUS_UNSPECIFIED_FAILURE;
+               res = WLAN_STATUS_UNSPECIFIED_FAILURE;
        }
 
-       return WLAN_STATUS_SUCCESS;
+done:
+       os_free(buf);
+       return res;
 }
 
 
+#ifdef CONFIG_FILS
+
+void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta)
+{
+       u16 reply_res;
+
+       wpa_printf(MSG_DEBUG, "FILS: Finish association with " MACSTR,
+                  MAC2STR(sta->addr));
+       eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+       if (!sta->fils_pending_assoc_req)
+               return;
+       reply_res = send_assoc_resp(hapd, sta, sta->addr, WLAN_STATUS_SUCCESS,
+                                   sta->fils_pending_assoc_is_reassoc,
+                                   sta->fils_pending_assoc_req,
+                                   sta->fils_pending_assoc_req_len);
+       os_free(sta->fils_pending_assoc_req);
+       sta->fils_pending_assoc_req = NULL;
+       sta->fils_pending_assoc_req_len = 0;
+       wpabuf_free(sta->fils_hlp_resp);
+       sta->fils_hlp_resp = NULL;
+       wpabuf_free(sta->hlp_dhcp_discover);
+       sta->hlp_dhcp_discover = NULL;
+
+       /*
+        * Remove the station in case tranmission of a success response fails
+        * (the STA was added associated to the driver) or if the station was
+        * previously added unassociated.
+        */
+       if (reply_res != WLAN_STATUS_SUCCESS || sta->added_unassoc) {
+               hostapd_drv_sta_remove(hapd, sta->addr);
+               sta->added_unassoc = 0;
+       }
+}
+
+
+void fils_hlp_timeout(void *eloop_ctx, void *eloop_data)
+{
+       struct hostapd_data *hapd = eloop_ctx;
+       struct sta_info *sta = eloop_data;
+
+       wpa_printf(MSG_DEBUG,
+                  "FILS: HLP response timeout - continue with association response for "
+                  MACSTR, MAC2STR(sta->addr));
+       fils_hlp_finish_assoc(hapd, sta);
+}
+
+#endif /* CONFIG_FILS */
+
+
 static void handle_assoc(struct hostapd_data *hapd,
                         const struct ieee80211_mgmt *mgmt, size_t len,
                         int reassoc)
@@ -2461,6 +2527,9 @@ static void handle_assoc(struct hostapd_data *hapd,
        struct hostapd_sta_wpa_psk_short *psk = NULL;
        char *identity = NULL;
        char *radius_cui = NULL;
+#ifdef CONFIG_FILS
+       int delay_assoc = 0;
+#endif /* CONFIG_FILS */
 
        if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
                                      sizeof(mgmt->u.assoc_req))) {
@@ -2749,8 +2818,10 @@ static void handle_assoc(struct hostapd_data *hapd,
 #ifdef CONFIG_FILS
        if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
            sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
-           sta->auth_alg == WLAN_AUTH_FILS_PK)
-               fils_process_hlp(hapd, sta, pos, left);
+           sta->auth_alg == WLAN_AUTH_FILS_PK) {
+               if (fils_process_hlp(hapd, sta, pos, left) > 0)
+                       delay_assoc = 1;
+       }
 #endif /* CONFIG_FILS */
 
  fail:
@@ -2779,6 +2850,29 @@ static void handle_assoc(struct hostapd_data *hapd,
        if (resp == WLAN_STATUS_SUCCESS && sta && add_associated_sta(hapd, sta))
                resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
 
+#ifdef CONFIG_FILS
+       if (sta) {
+               eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+               os_free(sta->fils_pending_assoc_req);
+               sta->fils_pending_assoc_req = NULL;
+               sta->fils_pending_assoc_req_len = 0;
+               wpabuf_free(sta->fils_hlp_resp);
+               sta->fils_hlp_resp = NULL;
+       }
+       if (sta && delay_assoc && resp == WLAN_STATUS_SUCCESS) {
+               sta->fils_pending_assoc_req = tmp;
+               sta->fils_pending_assoc_req_len = left;
+               sta->fils_pending_assoc_is_reassoc = reassoc;
+               wpa_printf(MSG_DEBUG,
+                          "FILS: Waiting for HLP processing before sending (Re)Association Response frame to "
+                          MACSTR, MAC2STR(sta->addr));
+               eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+               eloop_register_timeout(0, hapd->conf->fils_hlp_wait_time * 1024,
+                                      fils_hlp_timeout, hapd, sta);
+               return;
+       }
+#endif /* CONFIG_FILS */
+
        reply_res = send_assoc_resp(hapd, sta, mgmt->sa, resp, reassoc, pos,
                                    left);
        os_free(tmp);
index 46c92b7858dd38120ed2f10b3ccf9fc032b97d13..74ed69013a24fa89d2990426f146083509b84593 100644 (file)
@@ -140,5 +140,7 @@ void ieee802_11_finish_fils_auth(struct hostapd_data *hapd,
                                 struct sta_info *sta, int success,
                                 struct wpabuf *erp_resp,
                                 const u8 *msk, size_t msk_len);
+void fils_hlp_timeout(void *eloop_ctx, void *eloop_data);
+void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta);
 
 #endif /* IEEE802_11_H */
index b87ddeac3be13f6a25cba3aad3fec61092ac5728..af8c754d9e9b16b0b77f0ff423ad90c5b7b5cf1a 100644 (file)
@@ -339,6 +339,13 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
        mbo_ap_sta_free(sta);
        os_free(sta->supp_op_classes);
 
+#ifdef CONFIG_FILS
+       os_free(sta->fils_pending_assoc_req);
+       wpabuf_free(sta->fils_hlp_resp);
+       wpabuf_free(sta->hlp_dhcp_discover);
+       eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+#endif /* CONFIG_FILS */
+
        os_free(sta);
 }
 
index 0b44f7bf3e2517802bce5312ca6602d266f093b9..6f55403b00c775ef67e634ae72cf623c55a4cd75 100644 (file)
@@ -225,6 +225,12 @@ struct sta_info {
 #ifdef CONFIG_FILS
        u8 fils_snonce[FILS_NONCE_LEN];
        u8 fils_session[FILS_SESSION_LEN];
+       u8 *fils_pending_assoc_req;
+       size_t fils_pending_assoc_req_len;
+       unsigned int fils_pending_assoc_is_reassoc:1;
+       unsigned int fils_dhcp_rapid_commit_proxy:1;
+       struct wpabuf *fils_hlp_resp;
+       struct wpabuf *hlp_dhcp_discover;
 #endif /* CONFIG_FILS */
 };
 
index 69e3a5ded1b372b9762703e23e28f9eb7884d1dd..7372a69e6cae9f9208a7677a23e4c8fbbb7f3c68 100644 (file)
@@ -2274,7 +2274,8 @@ int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session,
 
 
 int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf,
-                      size_t current_len, size_t max_len)
+                      size_t current_len, size_t max_len,
+                      const struct wpabuf *hlp)
 {
        u8 *end = buf + max_len;
        u8 *pos = buf + current_len;
@@ -2334,7 +2335,9 @@ int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf,
        wpabuf_put_u8(plain, WLAN_EID_EXT_FILS_KEY_CONFIRM);
        wpabuf_put_data(plain, sm->fils_key_auth_ap, sm->fils_key_auth_len);
 
-       /* TODO: FILS HLP Container */
+       /* FILS HLP Container */
+       if (hlp)
+               wpabuf_put_buf(plain, hlp);
 
        /* TODO: FILS IP Address Assignment */
 
index a44b030b7902818f5ff4e5b9321db3472d32bf1d..9cbe3889b42a152583e4ef03cad8381e9d678b9c 100644 (file)
@@ -247,7 +247,7 @@ enum {
        WPA_MGMT_FRAME_PROTECTION_VIOLATION, WPA_INVALID_MGMT_GROUP_CIPHER,
        WPA_INVALID_MDIE, WPA_INVALID_PROTO
 };
-       
+
 int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
                        struct wpa_state_machine *sm,
                        const u8 *wpa_ie, size_t wpa_ie_len,
@@ -361,7 +361,8 @@ int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session,
                       const struct ieee80211_mgmt *mgmt, size_t frame_len,
                       u8 *pos, size_t left);
 int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf,
-                      size_t current_len, size_t max_len);
+                      size_t current_len, size_t max_len,
+                      const struct wpabuf *hlp);
 int fils_set_tk(struct wpa_state_machine *sm);
 
 #endif /* WPA_AUTH_H */