Cumulative patch from commit 128f6a98b3d4d6ed103db759707309f451db9682

128f6a98b mka: Fix the order of operations in secure channel deletion
213eb1885 dbus: Set mode to mesh in bss properties when mesh is supported
21fda4ee7 RSN: Fix pre-authentication EAPOL-Start startPeriod configuration
3f23260da nl80211: Notify reason for connection timeout failure
ca1ab9db2 hostapd: Get vendor HE capabilities
7785c70bb QCA vendor command for fetching HE capabilities
d512f406f hostapd: Add IEEE 802.11ax HE IEs into Beacon/Probe Response frames
94380cb40 hostapd: Initial IEEE 802.11ax (HE) definitions
5972dc73c mesh: Use correct rate in VHT and HT mixed environment
84ea61cff mesh: Use correct rate in HT and legacy mixed environment
025c6a47f VHT: Remove a redundant check
a7a638c2c hw_features: Move VHT capabilities checks to common
e01cf2afc Define eapol_sm_get_eap_proxy_imsi() only with CONFIG_EAP_PROXY=y
a8e25deeb FT: Merge similar error paths to use common steps
c6c41f6ea FT: Support addition of RIC elements into Reassociation Request frame
ecbdc1a1f Mark RSN msg 1/2 key data debug dump as key material
834c5d681 FILS: Fix PMK length for initial connection with FILS SHA384 AKM
e491389eb FILS: Fix ifdef for PTK derivation with SHA384-based AKM
62944f7d2 Add HMAC-SHA384 with internal crypto
aeecd4eae OpenSSL: Fix hmac_sha384_vector() implementation
5db32adc9 browser-wpadebug: Send HTTP response with HTTP/1.1 header
79329ae0a P2P: Verify local driver preferred frequencies for P2P use cases
3a7819f0a P2P: Add P2P_SET override_pref_op_chan to allow overriding preference
c06fca04f Add wpa_supplicant SET get_pref_freq_list_override
b4d56efb1 Use throughput estimate-based BSS selection with larger SNR difference
142041487 Drop GREAT_SNR definition from 30 to 25 dB
364c064a4 FT: Check key derivation results explicitly in AP operations
b5562a1a6 FILS: Remove CRC32 dependency from build
5cf0930f9 testS: Additional BSS TM error case coverage
885bbd4de WNM: Remove unused code from BSS TM Req generation
e7ddd86a9 WNM: Use a common error path in ieee802_11_send_wnmsleep_resp()
d6d5970e2 WNM: Fix WNM-Sleep Mode Request parsing for WNM-Sleep element
8492cc79c PeerKey: Remove dead code related to STSL negotiation state
e37c0aa5d OSU server: Remove invalid options from documentation
0d6056703 WMM: Fix estimated medium time calculation for some corner cases
ae26d3021 Fix "IEEE 802.11: Ignored Action frame" debug message
4ead4c7ec WMM: Remove obsolete TODO comments
577e794eb Sync android.config with wpa_supplicant defconfig changes
784710b7f Add bgscan options to wpa_supplicant defconfig
212a8f487 Fix wpa_supplicant defconfig copy-paste description
57c3a605c Add support to sched scan to report relatively better BSSs
20c846d9e nl80211: sched_scan relative RSSI parameters
37e9f511e mka: Send MKPDUs forever if mode is PSK
76aa31838 EAP: Call deinit_for_reauth() for Phase 2 EAP methods
02156b98b EAP-AKA: Don't use anonymous identity in phase2
9e2afe10e EAP-SIM: Don't use anonymous identity in phase2
ed9b1c16d EAP peer: Cache decrypted requests for EAP-SIM/AKA/AKA'
5f11880f6 SME: Remove null ie param from CTRL-EVENT-AUTH-REJECT
4d70b2a4e RRM: Fix a memory leak in beacon request handling
401243b73 RRM: Fix range request overriding
fb81c0a3d RRM: Merge similar error returns to a single one
13b30052d RRM: Fix Range Request max age parsing
bd6ec7f7c Fix MAC ACL query freeing on deinit
b4fd1f0ed Allow PNO scan also in connection completed state
4c6f450ca Add radio_work_is_connect() helper
85b6b6b6e Serialize scan/p2p-scan if already scheduled on the same interface
fcb303a57 P2P: Clear driver scan cache after BSS_FLUSH
0d6dc6830 FILS: Clean up HLP resize check
1d9d21f37 GAS: Add support to randomize transmitter address
8331c9b31 nl80211: Add support for mgmt_tx with random TA
14fa723a9 Sync with mac80211-next.git include/uapi/linux/nl80211.h
65ab7eb1f GAS: Fix OSU Providers List response with invalid configuration
f3e157057 VHT: Fill VHT capability with hardware capability
4bb9b674c Add a log message when GTK rekeying failed
41f140d38 Add hostapd options wpa_group_update_count and wpa_pairwise_update_count
e54691106 mka: Some bug fixes for MACsec in PSK mode
7faf403f9 mka: Fix an incorrect update of participant->to_use_sak
00e0f0b01 hs20-osu-client: Hide a trivial compiler warning
276e93654 hw_features: Clean center freq for falling back HT40 channels
f47f93617 P2P: Override P2P_PEER group_capab with 0 if no matching BSS entry found
bcf66493c Fix estimated throughput based skip-roam case
84bb12aa6 FILS: Fix send_assoc_resp() HLP extension to cover sta == NULL
275cc9428 FILS: Stop processing if fils_rmsk_to_pmk() fails
caab23f19 Set EAPOL-Key Key Length field to 0 for group message 1/2 in RSN
b0fb2be77 Do not send GNonce in EAPOL-Key group message 1/2
3bbc47050 Fix EAPOL-Key Install bit in Group Key 1/2 with FT and FILS auth
db5e53cb0 mesh: Fix struct hostapd_data initialization
9b170991a mesh: Fix mesh interface removal fix
945604a35 Update wpaspy.py to be python3 compatible
4d6e79f86 Use defines in hostapd_set_freq_params()
0217b8d87 eloop: Fix comments mismatch eloop_event/timeout_handler definitions
09a97eb27 Update the copyright notice years for QCA vendor definitions
841e9a8c7 QCA vendor command to set the trace levels for the specific QCA module
d77f33041 FILS: Fix AES-SIV AAD for (Re)Association Request frame decryption
7a6c3de23 ERP: Use macro for EMSKname length instead of hardcoded integer value
bb3ea71a2 ERP: Fix rIK derivation
124ddfa19 FILS: Parse and report received FILS HLP Containers from response
91d91abf6 FILS: DHCP relay for HLP requests
54b04d6f3 FILS: Move HLP request handling into a separate file
5a9d50493 ProxyARP: Use more robust DHCP option parsing
e64c13feb Move DHCP definitions into a common file
70407ee5c Add QCA vendor definitions for BSS transition status
53d171440 AP: Check ACL upon association request for 802.11ad
4cc61c386 GAS: Set temporary session timeout bigger than gas_comeback_delay

Test: Wifi Suite

Change-Id: Id597d7cba5d2b3875f2dbbeb9a10fd5e69a6a7c2
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/ap/fils_hlp.c b/src/ap/fils_hlp.c
new file mode 100644
index 0000000..40d9be1
--- /dev/null
+++ b/src/ap/fils_hlp.c
@@ -0,0 +1,636 @@
+/*
+ * FILS HLP request processing
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#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 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)) {
+		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 *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;
+
+	wpa_printf(MSG_DEBUG, "FILS: HLP request from " MACSTR " (dst=" MACSTR
+		   " src=" MACSTR " len=%u)",
+		   MAC2STR(sta->addr), MAC2STR(pos), MAC2STR(pos + ETH_ALEN),
+		   (unsigned int) len);
+	if (os_memcmp(sta->addr, pos + ETH_ALEN, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Ignore HLP request with unexpected source address"
+			   MACSTR, MAC2STR(pos + ETH_ALEN));
+		return 0;
+	}
+
+	end = pos + len;
+	pkt = pos + 2 * ETH_ALEN;
+	if (end - pkt >= 6 &&
+	    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;
+}
+
+
+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 0;
+		if (pos[0] == WLAN_EID_EXTENSION &&
+		    pos[1] >= 1 + 2 * ETH_ALEN &&
+		    pos[2] == WLAN_EID_EXT_FILS_HLP_CONTAINER)
+			break;
+		pos += 2 + pos[1];
+	}
+	if (end - pos < 2)
+		return 0; /* No FILS HLP Container elements */
+
+	tmp = os_malloc(end - pos);
+	if (!tmp)
+		return 0;
+
+	while (end - pos >= 2) {
+		if (2 + pos[1] > end - pos ||
+		    pos[0] != WLAN_EID_EXTENSION ||
+		    pos[1] < 1 + 2 * ETH_ALEN ||
+		    pos[2] != WLAN_EID_EXT_FILS_HLP_CONTAINER)
+			break;
+		tmp_pos = tmp;
+		os_memcpy(tmp_pos, pos + 3, pos[1] - 1);
+		tmp_pos += pos[1] - 1;
+		pos += 2 + pos[1];
+
+		/* Add possible fragments */
+		while (end - pos >= 2 && pos[0] == WLAN_EID_FRAGMENT &&
+		       2 + pos[1] <= end - pos) {
+			os_memcpy(tmp_pos, pos + 2, pos[1]);
+			tmp_pos += pos[1];
+			pos += 2 + pos[1];
+		}
+
+		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;
+	}
+}