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 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 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 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; 16185732ac8SCy Schubert struct iphdr *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)); 26285732ac8SCy Schubert iph->version = 4; 26385732ac8SCy Schubert iph->ihl = sizeof(*iph) / 4; 26485732ac8SCy Schubert iph->tot_len = htons(sizeof(*iph) + sizeof(*udph) + (end - pos)); 26585732ac8SCy Schubert iph->ttl = 1; 26685732ac8SCy Schubert iph->protocol = 17; /* UDP */ 26785732ac8SCy Schubert iph->saddr = hapd->conf->dhcp_server.u.v4.s_addr; 26885732ac8SCy Schubert iph->daddr = dhcp->client_ip; 26985732ac8SCy Schubert iph->check = 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 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 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 { 48285732ac8SCy Schubert const struct iphdr *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; 48885732ac8SCy Schubert iph = (const struct iphdr *) 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 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 { 51385732ac8SCy Schubert const struct iphdr *iph; 51485732ac8SCy Schubert u16 tot_len; 51585732ac8SCy Schubert 51685732ac8SCy Schubert if (len < sizeof(*iph)) 51785732ac8SCy Schubert return 0; 51885732ac8SCy Schubert iph = (const struct iphdr *) 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 } 52485732ac8SCy Schubert tot_len = ntohs(iph->tot_len); 52585732ac8SCy Schubert if (tot_len > len) 52685732ac8SCy Schubert return 0; 52785732ac8SCy Schubert wpa_printf(MSG_DEBUG, 52885732ac8SCy Schubert "FILS: HLP request IPv4: saddr=%08x daddr=%08x protocol=%u", 52985732ac8SCy Schubert iph->saddr, iph->daddr, iph->protocol); 53085732ac8SCy Schubert switch (iph->protocol) { 53185732ac8SCy Schubert case 17: 53285732ac8SCy Schubert return fils_process_hlp_udp(hapd, sta, dst, pos, len); 53385732ac8SCy Schubert } 53485732ac8SCy Schubert 53585732ac8SCy Schubert return 0; 53685732ac8SCy Schubert } 53785732ac8SCy Schubert 53885732ac8SCy Schubert 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); 54985732ac8SCy Schubert if (os_memcmp(sta->addr, pos + ETH_ALEN, ETH_ALEN) != 0) { 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); 57085732ac8SCy Schubert } 57185732ac8SCy Schubert 57285732ac8SCy Schubert return 0; 57385732ac8SCy Schubert } 57485732ac8SCy Schubert 57585732ac8SCy Schubert 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 583*4bc52338SCy Schubert if (sta->fils_pending_assoc_req && 584*4bc52338SCy Schubert eloop_is_timeout_registered(fils_hlp_timeout, hapd, sta)) { 585*4bc52338SCy Schubert /* Do not process FILS HLP request again if the station 586*4bc52338SCy Schubert * retransmits (Re)Association Request frame before the previous 587*4bc52338SCy Schubert * HLP response has either been received or timed out. */ 588*4bc52338SCy Schubert wpa_printf(MSG_DEBUG, 589*4bc52338SCy Schubert "FILS: Do not relay another HLP request from " 590*4bc52338SCy Schubert MACSTR 591*4bc52338SCy Schubert " before processing of the already pending one has been completed", 592*4bc52338SCy Schubert MAC2STR(sta->addr)); 593*4bc52338SCy Schubert return 1; 594*4bc52338SCy Schubert } 595*4bc52338SCy 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 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