185732ac8SCy Schubert /*
285732ac8SCy Schubert * FILS HLP request processing
385732ac8SCy Schubert * Copyright (c) 2017, Qualcomm Atheros, Inc.
485732ac8SCy Schubert *
585732ac8SCy Schubert * This software may be distributed under the terms of the BSD license.
685732ac8SCy Schubert * See README for more details.
785732ac8SCy Schubert */
885732ac8SCy Schubert
985732ac8SCy Schubert #include "utils/includes.h"
1085732ac8SCy Schubert
1185732ac8SCy Schubert #include "utils/common.h"
1285732ac8SCy Schubert #include "utils/eloop.h"
1385732ac8SCy Schubert #include "common/dhcp.h"
1485732ac8SCy Schubert #include "hostapd.h"
1585732ac8SCy Schubert #include "sta_info.h"
1685732ac8SCy Schubert #include "ieee802_11.h"
1785732ac8SCy Schubert #include "fils_hlp.h"
1885732ac8SCy Schubert
1985732ac8SCy Schubert
ip_checksum(const void * buf,size_t len)2085732ac8SCy Schubert static be16 ip_checksum(const void *buf, size_t len)
2185732ac8SCy Schubert {
2285732ac8SCy Schubert u32 sum = 0;
2385732ac8SCy Schubert const u16 *pos;
2485732ac8SCy Schubert
2585732ac8SCy Schubert for (pos = buf; len >= 2; len -= 2)
2685732ac8SCy Schubert sum += ntohs(*pos++);
2785732ac8SCy Schubert if (len)
2885732ac8SCy Schubert sum += ntohs(*pos << 8);
2985732ac8SCy Schubert
3085732ac8SCy Schubert sum = (sum >> 16) + (sum & 0xffff);
3185732ac8SCy Schubert sum += sum >> 16;
3285732ac8SCy Schubert return htons(~sum);
3385732ac8SCy Schubert }
3485732ac8SCy Schubert
3585732ac8SCy Schubert
fils_dhcp_request(struct hostapd_data * hapd,struct sta_info * sta,struct dhcp_data * dhcpoffer,u8 * dhcpofferend)3685732ac8SCy Schubert static int fils_dhcp_request(struct hostapd_data *hapd, struct sta_info *sta,
3785732ac8SCy Schubert struct dhcp_data *dhcpoffer, u8 *dhcpofferend)
3885732ac8SCy Schubert {
3985732ac8SCy Schubert u8 *pos, *end;
4085732ac8SCy Schubert struct dhcp_data *dhcp;
4185732ac8SCy Schubert struct sockaddr_in addr;
4285732ac8SCy Schubert ssize_t res;
4385732ac8SCy Schubert const u8 *server_id = NULL;
4485732ac8SCy Schubert
4585732ac8SCy Schubert if (!sta->hlp_dhcp_discover) {
4685732ac8SCy Schubert wpa_printf(MSG_DEBUG,
4785732ac8SCy Schubert "FILS: No pending HLP DHCPDISCOVER available");
4885732ac8SCy Schubert return -1;
4985732ac8SCy Schubert }
5085732ac8SCy Schubert
5185732ac8SCy Schubert /* Convert to DHCPREQUEST, remove rapid commit option, replace requested
5285732ac8SCy Schubert * IP address option with yiaddr. */
5385732ac8SCy Schubert pos = wpabuf_mhead(sta->hlp_dhcp_discover);
5485732ac8SCy Schubert end = pos + wpabuf_len(sta->hlp_dhcp_discover);
5585732ac8SCy Schubert dhcp = (struct dhcp_data *) pos;
5685732ac8SCy Schubert pos = (u8 *) (dhcp + 1);
5785732ac8SCy Schubert pos += 4; /* skip magic */
5885732ac8SCy Schubert while (pos < end && *pos != DHCP_OPT_END) {
5985732ac8SCy Schubert u8 opt, olen;
6085732ac8SCy Schubert
6185732ac8SCy Schubert opt = *pos++;
6285732ac8SCy Schubert if (opt == DHCP_OPT_PAD)
6385732ac8SCy Schubert continue;
6485732ac8SCy Schubert if (pos >= end)
6585732ac8SCy Schubert break;
6685732ac8SCy Schubert olen = *pos++;
6785732ac8SCy Schubert if (olen > end - pos)
6885732ac8SCy Schubert break;
6985732ac8SCy Schubert
7085732ac8SCy Schubert switch (opt) {
7185732ac8SCy Schubert case DHCP_OPT_MSG_TYPE:
7285732ac8SCy Schubert if (olen > 0)
7385732ac8SCy Schubert *pos = DHCPREQUEST;
7485732ac8SCy Schubert break;
7585732ac8SCy Schubert case DHCP_OPT_RAPID_COMMIT:
7685732ac8SCy Schubert case DHCP_OPT_REQUESTED_IP_ADDRESS:
7785732ac8SCy Schubert case DHCP_OPT_SERVER_ID:
7885732ac8SCy Schubert /* Remove option */
7985732ac8SCy Schubert pos -= 2;
8085732ac8SCy Schubert os_memmove(pos, pos + 2 + olen, end - pos - 2 - olen);
8185732ac8SCy Schubert end -= 2 + olen;
8285732ac8SCy Schubert olen = 0;
8385732ac8SCy Schubert break;
8485732ac8SCy Schubert }
8585732ac8SCy Schubert pos += olen;
8685732ac8SCy Schubert }
8785732ac8SCy Schubert if (pos >= end || *pos != DHCP_OPT_END) {
8885732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FILS: Could not update DHCPDISCOVER");
8985732ac8SCy Schubert return -1;
9085732ac8SCy Schubert }
9185732ac8SCy Schubert sta->hlp_dhcp_discover->used = pos - (u8 *) dhcp;
9285732ac8SCy Schubert
9385732ac8SCy Schubert /* Copy Server ID option from DHCPOFFER to DHCPREQUEST */
9485732ac8SCy Schubert pos = (u8 *) (dhcpoffer + 1);
9585732ac8SCy Schubert end = dhcpofferend;
9685732ac8SCy Schubert pos += 4; /* skip magic */
9785732ac8SCy Schubert while (pos < end && *pos != DHCP_OPT_END) {
9885732ac8SCy Schubert u8 opt, olen;
9985732ac8SCy Schubert
10085732ac8SCy Schubert opt = *pos++;
10185732ac8SCy Schubert if (opt == DHCP_OPT_PAD)
10285732ac8SCy Schubert continue;
10385732ac8SCy Schubert if (pos >= end)
10485732ac8SCy Schubert break;
10585732ac8SCy Schubert olen = *pos++;
10685732ac8SCy Schubert if (olen > end - pos)
10785732ac8SCy Schubert break;
10885732ac8SCy Schubert
10985732ac8SCy Schubert switch (opt) {
11085732ac8SCy Schubert case DHCP_OPT_SERVER_ID:
11185732ac8SCy Schubert server_id = pos - 2;
11285732ac8SCy Schubert break;
11385732ac8SCy Schubert }
11485732ac8SCy Schubert pos += olen;
11585732ac8SCy Schubert }
11685732ac8SCy Schubert
11785732ac8SCy Schubert if (wpabuf_resize(&sta->hlp_dhcp_discover,
11885732ac8SCy Schubert 6 + 1 + (server_id ? 2 + server_id[1] : 0)))
11985732ac8SCy Schubert return -1;
12085732ac8SCy Schubert if (server_id)
12185732ac8SCy Schubert wpabuf_put_data(sta->hlp_dhcp_discover, server_id,
12285732ac8SCy Schubert 2 + server_id[1]);
12385732ac8SCy Schubert wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_REQUESTED_IP_ADDRESS);
12485732ac8SCy Schubert wpabuf_put_u8(sta->hlp_dhcp_discover, 4);
12585732ac8SCy Schubert wpabuf_put_data(sta->hlp_dhcp_discover, &dhcpoffer->your_ip, 4);
12685732ac8SCy Schubert wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_END);
12785732ac8SCy Schubert
12885732ac8SCy Schubert os_memset(&addr, 0, sizeof(addr));
12985732ac8SCy Schubert addr.sin_family = AF_INET;
13085732ac8SCy Schubert addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr;
13185732ac8SCy Schubert addr.sin_port = htons(hapd->conf->dhcp_server_port);
13285732ac8SCy Schubert res = sendto(hapd->dhcp_sock, wpabuf_head(sta->hlp_dhcp_discover),
13385732ac8SCy Schubert wpabuf_len(sta->hlp_dhcp_discover), 0,
13485732ac8SCy Schubert (const struct sockaddr *) &addr, sizeof(addr));
13585732ac8SCy Schubert if (res < 0) {
13685732ac8SCy Schubert wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s",
13785732ac8SCy Schubert strerror(errno));
13885732ac8SCy Schubert return -1;
13985732ac8SCy Schubert }
14085732ac8SCy Schubert wpa_printf(MSG_DEBUG,
14185732ac8SCy Schubert "FILS: Acting as DHCP rapid commit proxy for %s:%d",
14285732ac8SCy Schubert inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
14385732ac8SCy Schubert wpabuf_free(sta->hlp_dhcp_discover);
14485732ac8SCy Schubert sta->hlp_dhcp_discover = NULL;
14585732ac8SCy Schubert sta->fils_dhcp_rapid_commit_proxy = 1;
14685732ac8SCy Schubert return 0;
14785732ac8SCy Schubert }
14885732ac8SCy Schubert
14985732ac8SCy Schubert
fils_dhcp_handler(int sd,void * eloop_ctx,void * sock_ctx)15085732ac8SCy Schubert static void fils_dhcp_handler(int sd, void *eloop_ctx, void *sock_ctx)
15185732ac8SCy Schubert {
15285732ac8SCy Schubert struct hostapd_data *hapd = sock_ctx;
15385732ac8SCy Schubert struct sta_info *sta;
15485732ac8SCy Schubert u8 buf[1500], *pos, *end, *end_opt = NULL;
15585732ac8SCy Schubert struct dhcp_data *dhcp;
15685732ac8SCy Schubert struct sockaddr_in addr;
15785732ac8SCy Schubert socklen_t addr_len;
15885732ac8SCy Schubert ssize_t res;
15985732ac8SCy Schubert u8 msgtype = 0;
16085732ac8SCy Schubert int rapid_commit = 0;
161c1d255d3SCy Schubert struct ip *iph;
16285732ac8SCy Schubert struct udphdr *udph;
16385732ac8SCy Schubert struct wpabuf *resp;
16485732ac8SCy Schubert const u8 *rpos;
16585732ac8SCy Schubert size_t left, len;
16685732ac8SCy Schubert
16785732ac8SCy Schubert addr_len = sizeof(addr);
16885732ac8SCy Schubert res = recvfrom(sd, buf, sizeof(buf), 0,
16985732ac8SCy Schubert (struct sockaddr *) &addr, &addr_len);
17085732ac8SCy Schubert if (res < 0) {
17185732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FILS: DHCP read failed: %s",
17285732ac8SCy Schubert strerror(errno));
17385732ac8SCy Schubert return;
17485732ac8SCy Schubert }
17585732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FILS: DHCP response from server %s:%d (len=%d)",
17685732ac8SCy Schubert inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), (int) res);
17785732ac8SCy Schubert wpa_hexdump(MSG_MSGDUMP, "FILS: HLP - DHCP server response", buf, res);
17885732ac8SCy Schubert if ((size_t) res < sizeof(*dhcp))
17985732ac8SCy Schubert return;
18085732ac8SCy Schubert dhcp = (struct dhcp_data *) buf;
18185732ac8SCy Schubert if (dhcp->op != 2)
18285732ac8SCy Schubert return; /* Not a BOOTREPLY */
18385732ac8SCy Schubert if (dhcp->relay_ip != hapd->conf->own_ip_addr.u.v4.s_addr) {
18485732ac8SCy Schubert wpa_printf(MSG_DEBUG,
18585732ac8SCy Schubert "FILS: HLP - DHCP response to unknown relay address 0x%x",
18685732ac8SCy Schubert dhcp->relay_ip);
18785732ac8SCy Schubert return;
18885732ac8SCy Schubert }
18985732ac8SCy Schubert dhcp->relay_ip = 0;
19085732ac8SCy Schubert pos = (u8 *) (dhcp + 1);
19185732ac8SCy Schubert end = &buf[res];
19285732ac8SCy Schubert
19385732ac8SCy Schubert if (end - pos < 4 || WPA_GET_BE32(pos) != DHCP_MAGIC) {
19485732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FILS: HLP - no DHCP magic in response");
19585732ac8SCy Schubert return;
19685732ac8SCy Schubert }
19785732ac8SCy Schubert pos += 4;
19885732ac8SCy Schubert
19985732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "FILS: HLP - DHCP options in response",
20085732ac8SCy Schubert pos, end - pos);
20185732ac8SCy Schubert while (pos < end && *pos != DHCP_OPT_END) {
20285732ac8SCy Schubert u8 opt, olen;
20385732ac8SCy Schubert
20485732ac8SCy Schubert opt = *pos++;
20585732ac8SCy Schubert if (opt == DHCP_OPT_PAD)
20685732ac8SCy Schubert continue;
20785732ac8SCy Schubert if (pos >= end)
20885732ac8SCy Schubert break;
20985732ac8SCy Schubert olen = *pos++;
21085732ac8SCy Schubert if (olen > end - pos)
21185732ac8SCy Schubert break;
21285732ac8SCy Schubert
21385732ac8SCy Schubert switch (opt) {
21485732ac8SCy Schubert case DHCP_OPT_MSG_TYPE:
21585732ac8SCy Schubert if (olen > 0)
21685732ac8SCy Schubert msgtype = pos[0];
21785732ac8SCy Schubert break;
21885732ac8SCy Schubert case DHCP_OPT_RAPID_COMMIT:
21985732ac8SCy Schubert rapid_commit = 1;
22085732ac8SCy Schubert break;
22185732ac8SCy Schubert }
22285732ac8SCy Schubert pos += olen;
22385732ac8SCy Schubert }
22485732ac8SCy Schubert if (pos < end && *pos == DHCP_OPT_END)
22585732ac8SCy Schubert end_opt = pos;
22685732ac8SCy Schubert
22785732ac8SCy Schubert wpa_printf(MSG_DEBUG,
22885732ac8SCy Schubert "FILS: HLP - DHCP message type %u (rapid_commit=%d hw_addr="
22985732ac8SCy Schubert MACSTR ")",
23085732ac8SCy Schubert msgtype, rapid_commit, MAC2STR(dhcp->hw_addr));
23185732ac8SCy Schubert
23285732ac8SCy Schubert sta = ap_get_sta(hapd, dhcp->hw_addr);
23385732ac8SCy Schubert if (!sta || !sta->fils_pending_assoc_req) {
23485732ac8SCy Schubert wpa_printf(MSG_DEBUG,
23585732ac8SCy Schubert "FILS: No pending HLP DHCP exchange with hw_addr "
23685732ac8SCy Schubert MACSTR, MAC2STR(dhcp->hw_addr));
23785732ac8SCy Schubert return;
23885732ac8SCy Schubert }
23985732ac8SCy Schubert
24085732ac8SCy Schubert if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPOFFER &&
24185732ac8SCy Schubert !rapid_commit) {
24285732ac8SCy Schubert /* Use hostapd to take care of 4-message exchange and convert
24385732ac8SCy Schubert * the final DHCPACK to rapid commit version. */
24485732ac8SCy Schubert if (fils_dhcp_request(hapd, sta, dhcp, end) == 0)
24585732ac8SCy Schubert return;
24685732ac8SCy Schubert /* failed, so send the server response as-is */
24785732ac8SCy Schubert } else if (msgtype != DHCPACK) {
24885732ac8SCy Schubert wpa_printf(MSG_DEBUG,
24985732ac8SCy Schubert "FILS: No DHCPACK available from the server and cannot do rapid commit proxying");
25085732ac8SCy Schubert }
25185732ac8SCy Schubert
25285732ac8SCy Schubert pos = buf;
25385732ac8SCy Schubert resp = wpabuf_alloc(2 * ETH_ALEN + 6 + 2 +
25485732ac8SCy Schubert sizeof(*iph) + sizeof(*udph) + (end - pos) + 2);
25585732ac8SCy Schubert if (!resp)
25685732ac8SCy Schubert return;
25785732ac8SCy Schubert wpabuf_put_data(resp, sta->addr, ETH_ALEN);
25885732ac8SCy Schubert wpabuf_put_data(resp, hapd->own_addr, ETH_ALEN);
25985732ac8SCy Schubert wpabuf_put_data(resp, "\xaa\xaa\x03\x00\x00\x00", 6);
26085732ac8SCy Schubert wpabuf_put_be16(resp, ETH_P_IP);
26185732ac8SCy Schubert iph = wpabuf_put(resp, sizeof(*iph));
262c1d255d3SCy Schubert iph->ip_v = 4;
263c1d255d3SCy Schubert iph->ip_hl = sizeof(*iph) / 4;
264c1d255d3SCy Schubert iph->ip_len = htons(sizeof(*iph) + sizeof(*udph) + (end - pos));
265c1d255d3SCy Schubert iph->ip_ttl = 1;
266c1d255d3SCy Schubert iph->ip_p = 17; /* UDP */
267c1d255d3SCy Schubert iph->ip_src.s_addr = hapd->conf->dhcp_server.u.v4.s_addr;
268c1d255d3SCy Schubert iph->ip_dst.s_addr = dhcp->client_ip;
269c1d255d3SCy Schubert iph->ip_sum = ip_checksum(iph, sizeof(*iph));
27085732ac8SCy Schubert udph = wpabuf_put(resp, sizeof(*udph));
27185732ac8SCy Schubert udph->uh_sport = htons(DHCP_SERVER_PORT);
27285732ac8SCy Schubert udph->uh_dport = htons(DHCP_CLIENT_PORT);
27385732ac8SCy Schubert udph->uh_ulen = htons(sizeof(*udph) + (end - pos));
27485732ac8SCy Schubert udph->uh_sum = htons(0x0000); /* TODO: calculate checksum */
27585732ac8SCy Schubert if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPACK &&
27685732ac8SCy Schubert !rapid_commit && sta->fils_dhcp_rapid_commit_proxy && end_opt) {
27785732ac8SCy Schubert /* Add rapid commit option */
27885732ac8SCy Schubert wpabuf_put_data(resp, pos, end_opt - pos);
27985732ac8SCy Schubert wpabuf_put_u8(resp, DHCP_OPT_RAPID_COMMIT);
28085732ac8SCy Schubert wpabuf_put_u8(resp, 0);
28185732ac8SCy Schubert wpabuf_put_data(resp, end_opt, end - end_opt);
28285732ac8SCy Schubert } else {
28385732ac8SCy Schubert wpabuf_put_data(resp, pos, end - pos);
28485732ac8SCy Schubert }
28585732ac8SCy Schubert if (wpabuf_resize(&sta->fils_hlp_resp, wpabuf_len(resp) +
28685732ac8SCy Schubert 2 * wpabuf_len(resp) / 255 + 100)) {
28785732ac8SCy Schubert wpabuf_free(resp);
28885732ac8SCy Schubert return;
28985732ac8SCy Schubert }
29085732ac8SCy Schubert
29185732ac8SCy Schubert rpos = wpabuf_head(resp);
29285732ac8SCy Schubert left = wpabuf_len(resp);
29385732ac8SCy Schubert
29485732ac8SCy Schubert wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_EXTENSION); /* Element ID */
29585732ac8SCy Schubert if (left <= 254)
29685732ac8SCy Schubert len = 1 + left;
29785732ac8SCy Schubert else
29885732ac8SCy Schubert len = 255;
29985732ac8SCy Schubert wpabuf_put_u8(sta->fils_hlp_resp, len); /* Length */
30085732ac8SCy Schubert /* Element ID Extension */
30185732ac8SCy Schubert wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_EXT_FILS_HLP_CONTAINER);
30285732ac8SCy Schubert /* Destination MAC Address, Source MAC Address, HLP Packet.
30385732ac8SCy Schubert * HLP Packet is in MSDU format (i.e., including the LLC/SNAP header
30485732ac8SCy Schubert * when LPD is used). */
30585732ac8SCy Schubert wpabuf_put_data(sta->fils_hlp_resp, rpos, len - 1);
30685732ac8SCy Schubert rpos += len - 1;
30785732ac8SCy Schubert left -= len - 1;
30885732ac8SCy Schubert while (left) {
30985732ac8SCy Schubert wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_FRAGMENT);
31085732ac8SCy Schubert len = left > 255 ? 255 : left;
31185732ac8SCy Schubert wpabuf_put_u8(sta->fils_hlp_resp, len);
31285732ac8SCy Schubert wpabuf_put_data(sta->fils_hlp_resp, rpos, len);
31385732ac8SCy Schubert rpos += len;
31485732ac8SCy Schubert left -= len;
31585732ac8SCy Schubert }
31685732ac8SCy Schubert wpabuf_free(resp);
31785732ac8SCy Schubert
31885732ac8SCy Schubert if (sta->fils_drv_assoc_finish)
31985732ac8SCy Schubert hostapd_notify_assoc_fils_finish(hapd, sta);
32085732ac8SCy Schubert else
32185732ac8SCy Schubert fils_hlp_finish_assoc(hapd, sta);
32285732ac8SCy Schubert }
32385732ac8SCy Schubert
32485732ac8SCy Schubert
fils_process_hlp_dhcp(struct hostapd_data * hapd,struct sta_info * sta,const u8 * msg,size_t len)32585732ac8SCy Schubert static int fils_process_hlp_dhcp(struct hostapd_data *hapd,
32685732ac8SCy Schubert struct sta_info *sta,
32785732ac8SCy Schubert const u8 *msg, size_t len)
32885732ac8SCy Schubert {
32985732ac8SCy Schubert const struct dhcp_data *dhcp;
33085732ac8SCy Schubert struct wpabuf *dhcp_buf;
33185732ac8SCy Schubert struct dhcp_data *dhcp_msg;
33285732ac8SCy Schubert u8 msgtype = 0;
33385732ac8SCy Schubert int rapid_commit = 0;
33485732ac8SCy Schubert const u8 *pos = msg, *end;
33585732ac8SCy Schubert struct sockaddr_in addr;
33685732ac8SCy Schubert ssize_t res;
33785732ac8SCy Schubert
33885732ac8SCy Schubert if (len < sizeof(*dhcp))
33985732ac8SCy Schubert return 0;
34085732ac8SCy Schubert dhcp = (const struct dhcp_data *) pos;
34185732ac8SCy Schubert end = pos + len;
34285732ac8SCy Schubert wpa_printf(MSG_DEBUG,
34385732ac8SCy Schubert "FILS: HLP request DHCP: op=%u htype=%u hlen=%u hops=%u xid=0x%x",
34485732ac8SCy Schubert dhcp->op, dhcp->htype, dhcp->hlen, dhcp->hops,
34585732ac8SCy Schubert ntohl(dhcp->xid));
34685732ac8SCy Schubert pos += sizeof(*dhcp);
34785732ac8SCy Schubert if (dhcp->op != 1)
34885732ac8SCy Schubert return 0; /* Not a BOOTREQUEST */
34985732ac8SCy Schubert
35085732ac8SCy Schubert if (end - pos < 4)
35185732ac8SCy Schubert return 0;
35285732ac8SCy Schubert if (WPA_GET_BE32(pos) != DHCP_MAGIC) {
35385732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FILS: HLP - no DHCP magic");
35485732ac8SCy Schubert return 0;
35585732ac8SCy Schubert }
35685732ac8SCy Schubert pos += 4;
35785732ac8SCy Schubert
35885732ac8SCy Schubert wpa_hexdump(MSG_DEBUG, "FILS: HLP - DHCP options", pos, end - pos);
35985732ac8SCy Schubert while (pos < end && *pos != DHCP_OPT_END) {
36085732ac8SCy Schubert u8 opt, olen;
36185732ac8SCy Schubert
36285732ac8SCy Schubert opt = *pos++;
36385732ac8SCy Schubert if (opt == DHCP_OPT_PAD)
36485732ac8SCy Schubert continue;
36585732ac8SCy Schubert if (pos >= end)
36685732ac8SCy Schubert break;
36785732ac8SCy Schubert olen = *pos++;
36885732ac8SCy Schubert if (olen > end - pos)
36985732ac8SCy Schubert break;
37085732ac8SCy Schubert
37185732ac8SCy Schubert switch (opt) {
37285732ac8SCy Schubert case DHCP_OPT_MSG_TYPE:
37385732ac8SCy Schubert if (olen > 0)
37485732ac8SCy Schubert msgtype = pos[0];
37585732ac8SCy Schubert break;
37685732ac8SCy Schubert case DHCP_OPT_RAPID_COMMIT:
37785732ac8SCy Schubert rapid_commit = 1;
37885732ac8SCy Schubert break;
37985732ac8SCy Schubert }
38085732ac8SCy Schubert pos += olen;
38185732ac8SCy Schubert }
38285732ac8SCy Schubert
38385732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FILS: HLP - DHCP message type %u", msgtype);
38485732ac8SCy Schubert if (msgtype != DHCPDISCOVER)
38585732ac8SCy Schubert return 0;
38685732ac8SCy Schubert
38785732ac8SCy Schubert if (hapd->conf->dhcp_server.af != AF_INET ||
38885732ac8SCy Schubert hapd->conf->dhcp_server.u.v4.s_addr == 0) {
38985732ac8SCy Schubert wpa_printf(MSG_DEBUG,
39085732ac8SCy Schubert "FILS: HLP - no DHCPv4 server configured - drop request");
39185732ac8SCy Schubert return 0;
39285732ac8SCy Schubert }
39385732ac8SCy Schubert
39485732ac8SCy Schubert if (hapd->conf->own_ip_addr.af != AF_INET ||
39585732ac8SCy Schubert hapd->conf->own_ip_addr.u.v4.s_addr == 0) {
39685732ac8SCy Schubert wpa_printf(MSG_DEBUG,
39785732ac8SCy Schubert "FILS: HLP - no IPv4 own_ip_addr configured - drop request");
39885732ac8SCy Schubert return 0;
39985732ac8SCy Schubert }
40085732ac8SCy Schubert
40185732ac8SCy Schubert if (hapd->dhcp_sock < 0) {
40285732ac8SCy Schubert int s;
40385732ac8SCy Schubert
40485732ac8SCy Schubert s = socket(AF_INET, SOCK_DGRAM, 0);
40585732ac8SCy Schubert if (s < 0) {
40685732ac8SCy Schubert wpa_printf(MSG_ERROR,
40785732ac8SCy Schubert "FILS: Failed to open DHCP socket: %s",
40885732ac8SCy Schubert strerror(errno));
40985732ac8SCy Schubert return 0;
41085732ac8SCy Schubert }
41185732ac8SCy Schubert
41285732ac8SCy Schubert if (hapd->conf->dhcp_relay_port) {
41385732ac8SCy Schubert os_memset(&addr, 0, sizeof(addr));
41485732ac8SCy Schubert addr.sin_family = AF_INET;
41585732ac8SCy Schubert addr.sin_addr.s_addr =
41685732ac8SCy Schubert hapd->conf->own_ip_addr.u.v4.s_addr;
41785732ac8SCy Schubert addr.sin_port = htons(hapd->conf->dhcp_relay_port);
41885732ac8SCy Schubert if (bind(s, (struct sockaddr *) &addr, sizeof(addr))) {
41985732ac8SCy Schubert wpa_printf(MSG_ERROR,
42085732ac8SCy Schubert "FILS: Failed to bind DHCP socket: %s",
42185732ac8SCy Schubert strerror(errno));
42285732ac8SCy Schubert close(s);
42385732ac8SCy Schubert return 0;
42485732ac8SCy Schubert }
42585732ac8SCy Schubert }
42685732ac8SCy Schubert if (eloop_register_sock(s, EVENT_TYPE_READ,
42785732ac8SCy Schubert fils_dhcp_handler, NULL, hapd)) {
42885732ac8SCy Schubert close(s);
42985732ac8SCy Schubert return 0;
43085732ac8SCy Schubert }
43185732ac8SCy Schubert
43285732ac8SCy Schubert hapd->dhcp_sock = s;
43385732ac8SCy Schubert }
43485732ac8SCy Schubert
43585732ac8SCy Schubert dhcp_buf = wpabuf_alloc(len);
43685732ac8SCy Schubert if (!dhcp_buf)
43785732ac8SCy Schubert return 0;
43885732ac8SCy Schubert dhcp_msg = wpabuf_put(dhcp_buf, len);
43985732ac8SCy Schubert os_memcpy(dhcp_msg, msg, len);
44085732ac8SCy Schubert dhcp_msg->relay_ip = hapd->conf->own_ip_addr.u.v4.s_addr;
44185732ac8SCy Schubert os_memset(&addr, 0, sizeof(addr));
44285732ac8SCy Schubert addr.sin_family = AF_INET;
44385732ac8SCy Schubert addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr;
44485732ac8SCy Schubert addr.sin_port = htons(hapd->conf->dhcp_server_port);
44585732ac8SCy Schubert res = sendto(hapd->dhcp_sock, dhcp_msg, len, 0,
44685732ac8SCy Schubert (const struct sockaddr *) &addr, sizeof(addr));
44785732ac8SCy Schubert if (res < 0) {
44885732ac8SCy Schubert wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s",
44985732ac8SCy Schubert strerror(errno));
45085732ac8SCy Schubert wpabuf_free(dhcp_buf);
45185732ac8SCy Schubert /* Close the socket to try to recover from error */
45285732ac8SCy Schubert eloop_unregister_read_sock(hapd->dhcp_sock);
45385732ac8SCy Schubert close(hapd->dhcp_sock);
45485732ac8SCy Schubert hapd->dhcp_sock = -1;
45585732ac8SCy Schubert return 0;
45685732ac8SCy Schubert }
45785732ac8SCy Schubert
45885732ac8SCy Schubert wpa_printf(MSG_DEBUG,
45985732ac8SCy Schubert "FILS: HLP relayed DHCP request to server %s:%d (rapid_commit=%d)",
46085732ac8SCy Schubert inet_ntoa(addr.sin_addr), ntohs(addr.sin_port),
46185732ac8SCy Schubert rapid_commit);
46285732ac8SCy Schubert if (hapd->conf->dhcp_rapid_commit_proxy && rapid_commit) {
46385732ac8SCy Schubert /* Store a copy of the DHCPDISCOVER for rapid commit proxying
46485732ac8SCy Schubert * purposes if the server does not support the rapid commit
46585732ac8SCy Schubert * option. */
46685732ac8SCy Schubert wpa_printf(MSG_DEBUG,
46785732ac8SCy Schubert "FILS: Store DHCPDISCOVER for rapid commit proxy");
46885732ac8SCy Schubert wpabuf_free(sta->hlp_dhcp_discover);
46985732ac8SCy Schubert sta->hlp_dhcp_discover = dhcp_buf;
47085732ac8SCy Schubert } else {
47185732ac8SCy Schubert wpabuf_free(dhcp_buf);
47285732ac8SCy Schubert }
47385732ac8SCy Schubert
47485732ac8SCy Schubert return 1;
47585732ac8SCy Schubert }
47685732ac8SCy Schubert
47785732ac8SCy Schubert
fils_process_hlp_udp(struct hostapd_data * hapd,struct sta_info * sta,const u8 * dst,const u8 * pos,size_t len)47885732ac8SCy Schubert static int fils_process_hlp_udp(struct hostapd_data *hapd,
47985732ac8SCy Schubert struct sta_info *sta, const u8 *dst,
48085732ac8SCy Schubert const u8 *pos, size_t len)
48185732ac8SCy Schubert {
482c1d255d3SCy Schubert const struct ip *iph;
48385732ac8SCy Schubert const struct udphdr *udph;
48485732ac8SCy Schubert u16 sport, dport, ulen;
48585732ac8SCy Schubert
48685732ac8SCy Schubert if (len < sizeof(*iph) + sizeof(*udph))
48785732ac8SCy Schubert return 0;
488c1d255d3SCy Schubert iph = (const struct ip *) pos;
48985732ac8SCy Schubert udph = (const struct udphdr *) (iph + 1);
49085732ac8SCy Schubert sport = ntohs(udph->uh_sport);
49185732ac8SCy Schubert dport = ntohs(udph->uh_dport);
49285732ac8SCy Schubert ulen = ntohs(udph->uh_ulen);
49385732ac8SCy Schubert wpa_printf(MSG_DEBUG,
49485732ac8SCy Schubert "FILS: HLP request UDP: sport=%u dport=%u ulen=%u sum=0x%x",
49585732ac8SCy Schubert sport, dport, ulen, ntohs(udph->uh_sum));
49685732ac8SCy Schubert /* TODO: Check UDP checksum */
49785732ac8SCy Schubert if (ulen < sizeof(*udph) || ulen > len - sizeof(*iph))
49885732ac8SCy Schubert return 0;
49985732ac8SCy Schubert
50085732ac8SCy Schubert if (dport == DHCP_SERVER_PORT && sport == DHCP_CLIENT_PORT) {
50185732ac8SCy Schubert return fils_process_hlp_dhcp(hapd, sta, (const u8 *) (udph + 1),
50285732ac8SCy Schubert ulen - sizeof(*udph));
50385732ac8SCy Schubert }
50485732ac8SCy Schubert
50585732ac8SCy Schubert return 0;
50685732ac8SCy Schubert }
50785732ac8SCy Schubert
50885732ac8SCy Schubert
fils_process_hlp_ip(struct hostapd_data * hapd,struct sta_info * sta,const u8 * dst,const u8 * pos,size_t len)50985732ac8SCy Schubert static int fils_process_hlp_ip(struct hostapd_data *hapd,
51085732ac8SCy Schubert struct sta_info *sta, const u8 *dst,
51185732ac8SCy Schubert const u8 *pos, size_t len)
51285732ac8SCy Schubert {
513c1d255d3SCy Schubert const struct ip *iph;
514c1d255d3SCy Schubert uint16_t ip_len;
51585732ac8SCy Schubert
51685732ac8SCy Schubert if (len < sizeof(*iph))
51785732ac8SCy Schubert return 0;
518c1d255d3SCy Schubert iph = (const struct ip *) pos;
51985732ac8SCy Schubert if (ip_checksum(iph, sizeof(*iph)) != 0) {
52085732ac8SCy Schubert wpa_printf(MSG_DEBUG,
52185732ac8SCy Schubert "FILS: HLP request IPv4 packet had invalid header checksum - dropped");
52285732ac8SCy Schubert return 0;
52385732ac8SCy Schubert }
524c1d255d3SCy Schubert ip_len = ntohs(iph->ip_len);
525c1d255d3SCy Schubert if (ip_len > len)
52685732ac8SCy Schubert return 0;
52785732ac8SCy Schubert wpa_printf(MSG_DEBUG,
52885732ac8SCy Schubert "FILS: HLP request IPv4: saddr=%08x daddr=%08x protocol=%u",
529c1d255d3SCy Schubert iph->ip_src.s_addr, iph->ip_dst.s_addr, iph->ip_p);
530c1d255d3SCy Schubert switch (iph->ip_p) {
53185732ac8SCy Schubert case 17:
53285732ac8SCy Schubert return fils_process_hlp_udp(hapd, sta, dst, pos, len);
533*a90b9d01SCy Schubert default:
53485732ac8SCy Schubert return 0;
53585732ac8SCy Schubert }
536*a90b9d01SCy Schubert }
53785732ac8SCy Schubert
53885732ac8SCy Schubert
fils_process_hlp_req(struct hostapd_data * hapd,struct sta_info * sta,const u8 * pos,size_t len)53985732ac8SCy Schubert static int fils_process_hlp_req(struct hostapd_data *hapd,
54085732ac8SCy Schubert struct sta_info *sta,
54185732ac8SCy Schubert const u8 *pos, size_t len)
54285732ac8SCy Schubert {
54385732ac8SCy Schubert const u8 *pkt, *end;
54485732ac8SCy Schubert
54585732ac8SCy Schubert wpa_printf(MSG_DEBUG, "FILS: HLP request from " MACSTR " (dst=" MACSTR
54685732ac8SCy Schubert " src=" MACSTR " len=%u)",
54785732ac8SCy Schubert MAC2STR(sta->addr), MAC2STR(pos), MAC2STR(pos + ETH_ALEN),
54885732ac8SCy Schubert (unsigned int) len);
549*a90b9d01SCy Schubert if (!ether_addr_equal(sta->addr, pos + ETH_ALEN)) {
55085732ac8SCy Schubert wpa_printf(MSG_DEBUG,
55185732ac8SCy Schubert "FILS: Ignore HLP request with unexpected source address"
55285732ac8SCy Schubert MACSTR, MAC2STR(pos + ETH_ALEN));
55385732ac8SCy Schubert return 0;
55485732ac8SCy Schubert }
55585732ac8SCy Schubert
55685732ac8SCy Schubert end = pos + len;
55785732ac8SCy Schubert pkt = pos + 2 * ETH_ALEN;
55885732ac8SCy Schubert if (end - pkt >= 6 &&
55985732ac8SCy Schubert os_memcmp(pkt, "\xaa\xaa\x03\x00\x00\x00", 6) == 0)
56085732ac8SCy Schubert pkt += 6; /* Remove SNAP/LLC header */
56185732ac8SCy Schubert wpa_hexdump(MSG_MSGDUMP, "FILS: HLP request packet", pkt, end - pkt);
56285732ac8SCy Schubert
56385732ac8SCy Schubert if (end - pkt < 2)
56485732ac8SCy Schubert return 0;
56585732ac8SCy Schubert
56685732ac8SCy Schubert switch (WPA_GET_BE16(pkt)) {
56785732ac8SCy Schubert case ETH_P_IP:
56885732ac8SCy Schubert return fils_process_hlp_ip(hapd, sta, pos, pkt + 2,
56985732ac8SCy Schubert end - pkt - 2);
570*a90b9d01SCy Schubert default:
57185732ac8SCy Schubert return 0;
57285732ac8SCy Schubert }
573*a90b9d01SCy Schubert }
57485732ac8SCy Schubert
57585732ac8SCy Schubert
fils_process_hlp(struct hostapd_data * hapd,struct sta_info * sta,const u8 * pos,int left)57685732ac8SCy Schubert int fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta,
57785732ac8SCy Schubert const u8 *pos, int left)
57885732ac8SCy Schubert {
57985732ac8SCy Schubert const u8 *end = pos + left;
58085732ac8SCy Schubert u8 *tmp, *tmp_pos;
58185732ac8SCy Schubert int ret = 0;
58285732ac8SCy Schubert
5834bc52338SCy Schubert if (sta->fils_pending_assoc_req &&
5844bc52338SCy Schubert eloop_is_timeout_registered(fils_hlp_timeout, hapd, sta)) {
5854bc52338SCy Schubert /* Do not process FILS HLP request again if the station
5864bc52338SCy Schubert * retransmits (Re)Association Request frame before the previous
5874bc52338SCy Schubert * HLP response has either been received or timed out. */
5884bc52338SCy Schubert wpa_printf(MSG_DEBUG,
5894bc52338SCy Schubert "FILS: Do not relay another HLP request from "
5904bc52338SCy Schubert MACSTR
5914bc52338SCy Schubert " before processing of the already pending one has been completed",
5924bc52338SCy Schubert MAC2STR(sta->addr));
5934bc52338SCy Schubert return 1;
5944bc52338SCy Schubert }
5954bc52338SCy Schubert
59685732ac8SCy Schubert /* Old DHCPDISCOVER is not needed anymore, if it was still pending */
59785732ac8SCy Schubert wpabuf_free(sta->hlp_dhcp_discover);
59885732ac8SCy Schubert sta->hlp_dhcp_discover = NULL;
59985732ac8SCy Schubert sta->fils_dhcp_rapid_commit_proxy = 0;
60085732ac8SCy Schubert
60185732ac8SCy Schubert /* Check if there are any FILS HLP Container elements */
60285732ac8SCy Schubert while (end - pos >= 2) {
60385732ac8SCy Schubert if (2 + pos[1] > end - pos)
60485732ac8SCy Schubert return 0;
60585732ac8SCy Schubert if (pos[0] == WLAN_EID_EXTENSION &&
60685732ac8SCy Schubert pos[1] >= 1 + 2 * ETH_ALEN &&
60785732ac8SCy Schubert pos[2] == WLAN_EID_EXT_FILS_HLP_CONTAINER)
60885732ac8SCy Schubert break;
60985732ac8SCy Schubert pos += 2 + pos[1];
61085732ac8SCy Schubert }
61185732ac8SCy Schubert if (end - pos < 2)
61285732ac8SCy Schubert return 0; /* No FILS HLP Container elements */
61385732ac8SCy Schubert
61485732ac8SCy Schubert tmp = os_malloc(end - pos);
61585732ac8SCy Schubert if (!tmp)
61685732ac8SCy Schubert return 0;
61785732ac8SCy Schubert
61885732ac8SCy Schubert while (end - pos >= 2) {
61985732ac8SCy Schubert if (2 + pos[1] > end - pos ||
62085732ac8SCy Schubert pos[0] != WLAN_EID_EXTENSION ||
62185732ac8SCy Schubert pos[1] < 1 + 2 * ETH_ALEN ||
62285732ac8SCy Schubert pos[2] != WLAN_EID_EXT_FILS_HLP_CONTAINER)
62385732ac8SCy Schubert break;
62485732ac8SCy Schubert tmp_pos = tmp;
62585732ac8SCy Schubert os_memcpy(tmp_pos, pos + 3, pos[1] - 1);
62685732ac8SCy Schubert tmp_pos += pos[1] - 1;
62785732ac8SCy Schubert pos += 2 + pos[1];
62885732ac8SCy Schubert
62985732ac8SCy Schubert /* Add possible fragments */
63085732ac8SCy Schubert while (end - pos >= 2 && pos[0] == WLAN_EID_FRAGMENT &&
63185732ac8SCy Schubert 2 + pos[1] <= end - pos) {
63285732ac8SCy Schubert os_memcpy(tmp_pos, pos + 2, pos[1]);
63385732ac8SCy Schubert tmp_pos += pos[1];
63485732ac8SCy Schubert pos += 2 + pos[1];
63585732ac8SCy Schubert }
63685732ac8SCy Schubert
63785732ac8SCy Schubert if (fils_process_hlp_req(hapd, sta, tmp, tmp_pos - tmp) > 0)
63885732ac8SCy Schubert ret = 1;
63985732ac8SCy Schubert }
64085732ac8SCy Schubert
64185732ac8SCy Schubert os_free(tmp);
64285732ac8SCy Schubert
64385732ac8SCy Schubert return ret;
64485732ac8SCy Schubert }
64585732ac8SCy Schubert
64685732ac8SCy Schubert
fils_hlp_deinit(struct hostapd_data * hapd)64785732ac8SCy Schubert void fils_hlp_deinit(struct hostapd_data *hapd)
64885732ac8SCy Schubert {
64985732ac8SCy Schubert if (hapd->dhcp_sock >= 0) {
65085732ac8SCy Schubert eloop_unregister_read_sock(hapd->dhcp_sock);
65185732ac8SCy Schubert close(hapd->dhcp_sock);
65285732ac8SCy Schubert hapd->dhcp_sock = -1;
65385732ac8SCy Schubert }
65485732ac8SCy Schubert }
655