1341618Scy/* 2341618Scy * FILS HLP request processing 3341618Scy * Copyright (c) 2017, Qualcomm Atheros, Inc. 4341618Scy * 5341618Scy * This software may be distributed under the terms of the BSD license. 6341618Scy * See README for more details. 7341618Scy */ 8341618Scy 9341618Scy#include "utils/includes.h" 10341618Scy 11341618Scy#include "utils/common.h" 12341618Scy#include "utils/eloop.h" 13341618Scy#include "common/dhcp.h" 14341618Scy#include "hostapd.h" 15341618Scy#include "sta_info.h" 16341618Scy#include "ieee802_11.h" 17341618Scy#include "fils_hlp.h" 18341618Scy 19341618Scy 20341618Scystatic be16 ip_checksum(const void *buf, size_t len) 21341618Scy{ 22341618Scy u32 sum = 0; 23341618Scy const u16 *pos; 24341618Scy 25341618Scy for (pos = buf; len >= 2; len -= 2) 26341618Scy sum += ntohs(*pos++); 27341618Scy if (len) 28341618Scy sum += ntohs(*pos << 8); 29341618Scy 30341618Scy sum = (sum >> 16) + (sum & 0xffff); 31341618Scy sum += sum >> 16; 32341618Scy return htons(~sum); 33341618Scy} 34341618Scy 35341618Scy 36341618Scystatic int fils_dhcp_request(struct hostapd_data *hapd, struct sta_info *sta, 37341618Scy struct dhcp_data *dhcpoffer, u8 *dhcpofferend) 38341618Scy{ 39341618Scy u8 *pos, *end; 40341618Scy struct dhcp_data *dhcp; 41341618Scy struct sockaddr_in addr; 42341618Scy ssize_t res; 43341618Scy const u8 *server_id = NULL; 44341618Scy 45341618Scy if (!sta->hlp_dhcp_discover) { 46341618Scy wpa_printf(MSG_DEBUG, 47341618Scy "FILS: No pending HLP DHCPDISCOVER available"); 48341618Scy return -1; 49341618Scy } 50341618Scy 51341618Scy /* Convert to DHCPREQUEST, remove rapid commit option, replace requested 52341618Scy * IP address option with yiaddr. */ 53341618Scy pos = wpabuf_mhead(sta->hlp_dhcp_discover); 54341618Scy end = pos + wpabuf_len(sta->hlp_dhcp_discover); 55341618Scy dhcp = (struct dhcp_data *) pos; 56341618Scy pos = (u8 *) (dhcp + 1); 57341618Scy pos += 4; /* skip magic */ 58341618Scy while (pos < end && *pos != DHCP_OPT_END) { 59341618Scy u8 opt, olen; 60341618Scy 61341618Scy opt = *pos++; 62341618Scy if (opt == DHCP_OPT_PAD) 63341618Scy continue; 64341618Scy if (pos >= end) 65341618Scy break; 66341618Scy olen = *pos++; 67341618Scy if (olen > end - pos) 68341618Scy break; 69341618Scy 70341618Scy switch (opt) { 71341618Scy case DHCP_OPT_MSG_TYPE: 72341618Scy if (olen > 0) 73341618Scy *pos = DHCPREQUEST; 74341618Scy break; 75341618Scy case DHCP_OPT_RAPID_COMMIT: 76341618Scy case DHCP_OPT_REQUESTED_IP_ADDRESS: 77341618Scy case DHCP_OPT_SERVER_ID: 78341618Scy /* Remove option */ 79341618Scy pos -= 2; 80341618Scy os_memmove(pos, pos + 2 + olen, end - pos - 2 - olen); 81341618Scy end -= 2 + olen; 82341618Scy olen = 0; 83341618Scy break; 84341618Scy } 85341618Scy pos += olen; 86341618Scy } 87341618Scy if (pos >= end || *pos != DHCP_OPT_END) { 88341618Scy wpa_printf(MSG_DEBUG, "FILS: Could not update DHCPDISCOVER"); 89341618Scy return -1; 90341618Scy } 91341618Scy sta->hlp_dhcp_discover->used = pos - (u8 *) dhcp; 92341618Scy 93341618Scy /* Copy Server ID option from DHCPOFFER to DHCPREQUEST */ 94341618Scy pos = (u8 *) (dhcpoffer + 1); 95341618Scy end = dhcpofferend; 96341618Scy pos += 4; /* skip magic */ 97341618Scy while (pos < end && *pos != DHCP_OPT_END) { 98341618Scy u8 opt, olen; 99341618Scy 100341618Scy opt = *pos++; 101341618Scy if (opt == DHCP_OPT_PAD) 102341618Scy continue; 103341618Scy if (pos >= end) 104341618Scy break; 105341618Scy olen = *pos++; 106341618Scy if (olen > end - pos) 107341618Scy break; 108341618Scy 109341618Scy switch (opt) { 110341618Scy case DHCP_OPT_SERVER_ID: 111341618Scy server_id = pos - 2; 112341618Scy break; 113341618Scy } 114341618Scy pos += olen; 115341618Scy } 116341618Scy 117341618Scy if (wpabuf_resize(&sta->hlp_dhcp_discover, 118341618Scy 6 + 1 + (server_id ? 2 + server_id[1] : 0))) 119341618Scy return -1; 120341618Scy if (server_id) 121341618Scy wpabuf_put_data(sta->hlp_dhcp_discover, server_id, 122341618Scy 2 + server_id[1]); 123341618Scy wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_REQUESTED_IP_ADDRESS); 124341618Scy wpabuf_put_u8(sta->hlp_dhcp_discover, 4); 125341618Scy wpabuf_put_data(sta->hlp_dhcp_discover, &dhcpoffer->your_ip, 4); 126341618Scy wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_END); 127341618Scy 128341618Scy os_memset(&addr, 0, sizeof(addr)); 129341618Scy addr.sin_family = AF_INET; 130341618Scy addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr; 131341618Scy addr.sin_port = htons(hapd->conf->dhcp_server_port); 132341618Scy res = sendto(hapd->dhcp_sock, wpabuf_head(sta->hlp_dhcp_discover), 133341618Scy wpabuf_len(sta->hlp_dhcp_discover), 0, 134341618Scy (const struct sockaddr *) &addr, sizeof(addr)); 135341618Scy if (res < 0) { 136341618Scy wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s", 137341618Scy strerror(errno)); 138341618Scy return -1; 139341618Scy } 140341618Scy wpa_printf(MSG_DEBUG, 141341618Scy "FILS: Acting as DHCP rapid commit proxy for %s:%d", 142341618Scy inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); 143341618Scy wpabuf_free(sta->hlp_dhcp_discover); 144341618Scy sta->hlp_dhcp_discover = NULL; 145341618Scy sta->fils_dhcp_rapid_commit_proxy = 1; 146341618Scy return 0; 147341618Scy} 148341618Scy 149341618Scy 150341618Scystatic void fils_dhcp_handler(int sd, void *eloop_ctx, void *sock_ctx) 151341618Scy{ 152341618Scy struct hostapd_data *hapd = sock_ctx; 153341618Scy struct sta_info *sta; 154341618Scy u8 buf[1500], *pos, *end, *end_opt = NULL; 155341618Scy struct dhcp_data *dhcp; 156341618Scy struct sockaddr_in addr; 157341618Scy socklen_t addr_len; 158341618Scy ssize_t res; 159341618Scy u8 msgtype = 0; 160341618Scy int rapid_commit = 0; 161341618Scy struct iphdr *iph; 162341618Scy struct udphdr *udph; 163341618Scy struct wpabuf *resp; 164341618Scy const u8 *rpos; 165341618Scy size_t left, len; 166341618Scy 167341618Scy addr_len = sizeof(addr); 168341618Scy res = recvfrom(sd, buf, sizeof(buf), 0, 169341618Scy (struct sockaddr *) &addr, &addr_len); 170341618Scy if (res < 0) { 171341618Scy wpa_printf(MSG_DEBUG, "FILS: DHCP read failed: %s", 172341618Scy strerror(errno)); 173341618Scy return; 174341618Scy } 175341618Scy wpa_printf(MSG_DEBUG, "FILS: DHCP response from server %s:%d (len=%d)", 176341618Scy inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), (int) res); 177341618Scy wpa_hexdump(MSG_MSGDUMP, "FILS: HLP - DHCP server response", buf, res); 178341618Scy if ((size_t) res < sizeof(*dhcp)) 179341618Scy return; 180341618Scy dhcp = (struct dhcp_data *) buf; 181341618Scy if (dhcp->op != 2) 182341618Scy return; /* Not a BOOTREPLY */ 183341618Scy if (dhcp->relay_ip != hapd->conf->own_ip_addr.u.v4.s_addr) { 184341618Scy wpa_printf(MSG_DEBUG, 185341618Scy "FILS: HLP - DHCP response to unknown relay address 0x%x", 186341618Scy dhcp->relay_ip); 187341618Scy return; 188341618Scy } 189341618Scy dhcp->relay_ip = 0; 190341618Scy pos = (u8 *) (dhcp + 1); 191341618Scy end = &buf[res]; 192341618Scy 193341618Scy if (end - pos < 4 || WPA_GET_BE32(pos) != DHCP_MAGIC) { 194341618Scy wpa_printf(MSG_DEBUG, "FILS: HLP - no DHCP magic in response"); 195341618Scy return; 196341618Scy } 197341618Scy pos += 4; 198341618Scy 199341618Scy wpa_hexdump(MSG_DEBUG, "FILS: HLP - DHCP options in response", 200341618Scy pos, end - pos); 201341618Scy while (pos < end && *pos != DHCP_OPT_END) { 202341618Scy u8 opt, olen; 203341618Scy 204341618Scy opt = *pos++; 205341618Scy if (opt == DHCP_OPT_PAD) 206341618Scy continue; 207341618Scy if (pos >= end) 208341618Scy break; 209341618Scy olen = *pos++; 210341618Scy if (olen > end - pos) 211341618Scy break; 212341618Scy 213341618Scy switch (opt) { 214341618Scy case DHCP_OPT_MSG_TYPE: 215341618Scy if (olen > 0) 216341618Scy msgtype = pos[0]; 217341618Scy break; 218341618Scy case DHCP_OPT_RAPID_COMMIT: 219341618Scy rapid_commit = 1; 220341618Scy break; 221341618Scy } 222341618Scy pos += olen; 223341618Scy } 224341618Scy if (pos < end && *pos == DHCP_OPT_END) 225341618Scy end_opt = pos; 226341618Scy 227341618Scy wpa_printf(MSG_DEBUG, 228341618Scy "FILS: HLP - DHCP message type %u (rapid_commit=%d hw_addr=" 229341618Scy MACSTR ")", 230341618Scy msgtype, rapid_commit, MAC2STR(dhcp->hw_addr)); 231341618Scy 232341618Scy sta = ap_get_sta(hapd, dhcp->hw_addr); 233341618Scy if (!sta || !sta->fils_pending_assoc_req) { 234341618Scy wpa_printf(MSG_DEBUG, 235341618Scy "FILS: No pending HLP DHCP exchange with hw_addr " 236341618Scy MACSTR, MAC2STR(dhcp->hw_addr)); 237341618Scy return; 238341618Scy } 239341618Scy 240341618Scy if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPOFFER && 241341618Scy !rapid_commit) { 242341618Scy /* Use hostapd to take care of 4-message exchange and convert 243341618Scy * the final DHCPACK to rapid commit version. */ 244341618Scy if (fils_dhcp_request(hapd, sta, dhcp, end) == 0) 245341618Scy return; 246341618Scy /* failed, so send the server response as-is */ 247341618Scy } else if (msgtype != DHCPACK) { 248341618Scy wpa_printf(MSG_DEBUG, 249341618Scy "FILS: No DHCPACK available from the server and cannot do rapid commit proxying"); 250341618Scy } 251341618Scy 252341618Scy pos = buf; 253341618Scy resp = wpabuf_alloc(2 * ETH_ALEN + 6 + 2 + 254341618Scy sizeof(*iph) + sizeof(*udph) + (end - pos) + 2); 255341618Scy if (!resp) 256341618Scy return; 257341618Scy wpabuf_put_data(resp, sta->addr, ETH_ALEN); 258341618Scy wpabuf_put_data(resp, hapd->own_addr, ETH_ALEN); 259341618Scy wpabuf_put_data(resp, "\xaa\xaa\x03\x00\x00\x00", 6); 260341618Scy wpabuf_put_be16(resp, ETH_P_IP); 261341618Scy iph = wpabuf_put(resp, sizeof(*iph)); 262341618Scy iph->version = 4; 263341618Scy iph->ihl = sizeof(*iph) / 4; 264341618Scy iph->tot_len = htons(sizeof(*iph) + sizeof(*udph) + (end - pos)); 265341618Scy iph->ttl = 1; 266341618Scy iph->protocol = 17; /* UDP */ 267341618Scy iph->saddr = hapd->conf->dhcp_server.u.v4.s_addr; 268341618Scy iph->daddr = dhcp->client_ip; 269341618Scy iph->check = ip_checksum(iph, sizeof(*iph)); 270341618Scy udph = wpabuf_put(resp, sizeof(*udph)); 271341618Scy udph->uh_sport = htons(DHCP_SERVER_PORT); 272341618Scy udph->uh_dport = htons(DHCP_CLIENT_PORT); 273341618Scy udph->uh_ulen = htons(sizeof(*udph) + (end - pos)); 274341618Scy udph->uh_sum = htons(0x0000); /* TODO: calculate checksum */ 275341618Scy if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPACK && 276341618Scy !rapid_commit && sta->fils_dhcp_rapid_commit_proxy && end_opt) { 277341618Scy /* Add rapid commit option */ 278341618Scy wpabuf_put_data(resp, pos, end_opt - pos); 279341618Scy wpabuf_put_u8(resp, DHCP_OPT_RAPID_COMMIT); 280341618Scy wpabuf_put_u8(resp, 0); 281341618Scy wpabuf_put_data(resp, end_opt, end - end_opt); 282341618Scy } else { 283341618Scy wpabuf_put_data(resp, pos, end - pos); 284341618Scy } 285341618Scy if (wpabuf_resize(&sta->fils_hlp_resp, wpabuf_len(resp) + 286341618Scy 2 * wpabuf_len(resp) / 255 + 100)) { 287341618Scy wpabuf_free(resp); 288341618Scy return; 289341618Scy } 290341618Scy 291341618Scy rpos = wpabuf_head(resp); 292341618Scy left = wpabuf_len(resp); 293341618Scy 294341618Scy wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_EXTENSION); /* Element ID */ 295341618Scy if (left <= 254) 296341618Scy len = 1 + left; 297341618Scy else 298341618Scy len = 255; 299341618Scy wpabuf_put_u8(sta->fils_hlp_resp, len); /* Length */ 300341618Scy /* Element ID Extension */ 301341618Scy wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_EXT_FILS_HLP_CONTAINER); 302341618Scy /* Destination MAC Address, Source MAC Address, HLP Packet. 303341618Scy * HLP Packet is in MSDU format (i.e., including the LLC/SNAP header 304341618Scy * when LPD is used). */ 305341618Scy wpabuf_put_data(sta->fils_hlp_resp, rpos, len - 1); 306341618Scy rpos += len - 1; 307341618Scy left -= len - 1; 308341618Scy while (left) { 309341618Scy wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_FRAGMENT); 310341618Scy len = left > 255 ? 255 : left; 311341618Scy wpabuf_put_u8(sta->fils_hlp_resp, len); 312341618Scy wpabuf_put_data(sta->fils_hlp_resp, rpos, len); 313341618Scy rpos += len; 314341618Scy left -= len; 315341618Scy } 316341618Scy wpabuf_free(resp); 317341618Scy 318341618Scy if (sta->fils_drv_assoc_finish) 319341618Scy hostapd_notify_assoc_fils_finish(hapd, sta); 320341618Scy else 321341618Scy fils_hlp_finish_assoc(hapd, sta); 322341618Scy} 323341618Scy 324341618Scy 325341618Scystatic int fils_process_hlp_dhcp(struct hostapd_data *hapd, 326341618Scy struct sta_info *sta, 327341618Scy const u8 *msg, size_t len) 328341618Scy{ 329341618Scy const struct dhcp_data *dhcp; 330341618Scy struct wpabuf *dhcp_buf; 331341618Scy struct dhcp_data *dhcp_msg; 332341618Scy u8 msgtype = 0; 333341618Scy int rapid_commit = 0; 334341618Scy const u8 *pos = msg, *end; 335341618Scy struct sockaddr_in addr; 336341618Scy ssize_t res; 337341618Scy 338341618Scy if (len < sizeof(*dhcp)) 339341618Scy return 0; 340341618Scy dhcp = (const struct dhcp_data *) pos; 341341618Scy end = pos + len; 342341618Scy wpa_printf(MSG_DEBUG, 343341618Scy "FILS: HLP request DHCP: op=%u htype=%u hlen=%u hops=%u xid=0x%x", 344341618Scy dhcp->op, dhcp->htype, dhcp->hlen, dhcp->hops, 345341618Scy ntohl(dhcp->xid)); 346341618Scy pos += sizeof(*dhcp); 347341618Scy if (dhcp->op != 1) 348341618Scy return 0; /* Not a BOOTREQUEST */ 349341618Scy 350341618Scy if (end - pos < 4) 351341618Scy return 0; 352341618Scy if (WPA_GET_BE32(pos) != DHCP_MAGIC) { 353341618Scy wpa_printf(MSG_DEBUG, "FILS: HLP - no DHCP magic"); 354341618Scy return 0; 355341618Scy } 356341618Scy pos += 4; 357341618Scy 358341618Scy wpa_hexdump(MSG_DEBUG, "FILS: HLP - DHCP options", pos, end - pos); 359341618Scy while (pos < end && *pos != DHCP_OPT_END) { 360341618Scy u8 opt, olen; 361341618Scy 362341618Scy opt = *pos++; 363341618Scy if (opt == DHCP_OPT_PAD) 364341618Scy continue; 365341618Scy if (pos >= end) 366341618Scy break; 367341618Scy olen = *pos++; 368341618Scy if (olen > end - pos) 369341618Scy break; 370341618Scy 371341618Scy switch (opt) { 372341618Scy case DHCP_OPT_MSG_TYPE: 373341618Scy if (olen > 0) 374341618Scy msgtype = pos[0]; 375341618Scy break; 376341618Scy case DHCP_OPT_RAPID_COMMIT: 377341618Scy rapid_commit = 1; 378341618Scy break; 379341618Scy } 380341618Scy pos += olen; 381341618Scy } 382341618Scy 383341618Scy wpa_printf(MSG_DEBUG, "FILS: HLP - DHCP message type %u", msgtype); 384341618Scy if (msgtype != DHCPDISCOVER) 385341618Scy return 0; 386341618Scy 387341618Scy if (hapd->conf->dhcp_server.af != AF_INET || 388341618Scy hapd->conf->dhcp_server.u.v4.s_addr == 0) { 389341618Scy wpa_printf(MSG_DEBUG, 390341618Scy "FILS: HLP - no DHCPv4 server configured - drop request"); 391341618Scy return 0; 392341618Scy } 393341618Scy 394341618Scy if (hapd->conf->own_ip_addr.af != AF_INET || 395341618Scy hapd->conf->own_ip_addr.u.v4.s_addr == 0) { 396341618Scy wpa_printf(MSG_DEBUG, 397341618Scy "FILS: HLP - no IPv4 own_ip_addr configured - drop request"); 398341618Scy return 0; 399341618Scy } 400341618Scy 401341618Scy if (hapd->dhcp_sock < 0) { 402341618Scy int s; 403341618Scy 404341618Scy s = socket(AF_INET, SOCK_DGRAM, 0); 405341618Scy if (s < 0) { 406341618Scy wpa_printf(MSG_ERROR, 407341618Scy "FILS: Failed to open DHCP socket: %s", 408341618Scy strerror(errno)); 409341618Scy return 0; 410341618Scy } 411341618Scy 412341618Scy if (hapd->conf->dhcp_relay_port) { 413341618Scy os_memset(&addr, 0, sizeof(addr)); 414341618Scy addr.sin_family = AF_INET; 415341618Scy addr.sin_addr.s_addr = 416341618Scy hapd->conf->own_ip_addr.u.v4.s_addr; 417341618Scy addr.sin_port = htons(hapd->conf->dhcp_relay_port); 418341618Scy if (bind(s, (struct sockaddr *) &addr, sizeof(addr))) { 419341618Scy wpa_printf(MSG_ERROR, 420341618Scy "FILS: Failed to bind DHCP socket: %s", 421341618Scy strerror(errno)); 422341618Scy close(s); 423341618Scy return 0; 424341618Scy } 425341618Scy } 426341618Scy if (eloop_register_sock(s, EVENT_TYPE_READ, 427341618Scy fils_dhcp_handler, NULL, hapd)) { 428341618Scy close(s); 429341618Scy return 0; 430341618Scy } 431341618Scy 432341618Scy hapd->dhcp_sock = s; 433341618Scy } 434341618Scy 435341618Scy dhcp_buf = wpabuf_alloc(len); 436341618Scy if (!dhcp_buf) 437341618Scy return 0; 438341618Scy dhcp_msg = wpabuf_put(dhcp_buf, len); 439341618Scy os_memcpy(dhcp_msg, msg, len); 440341618Scy dhcp_msg->relay_ip = hapd->conf->own_ip_addr.u.v4.s_addr; 441341618Scy os_memset(&addr, 0, sizeof(addr)); 442341618Scy addr.sin_family = AF_INET; 443341618Scy addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr; 444341618Scy addr.sin_port = htons(hapd->conf->dhcp_server_port); 445341618Scy res = sendto(hapd->dhcp_sock, dhcp_msg, len, 0, 446341618Scy (const struct sockaddr *) &addr, sizeof(addr)); 447341618Scy if (res < 0) { 448341618Scy wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s", 449341618Scy strerror(errno)); 450341618Scy wpabuf_free(dhcp_buf); 451341618Scy /* Close the socket to try to recover from error */ 452341618Scy eloop_unregister_read_sock(hapd->dhcp_sock); 453341618Scy close(hapd->dhcp_sock); 454341618Scy hapd->dhcp_sock = -1; 455341618Scy return 0; 456341618Scy } 457341618Scy 458341618Scy wpa_printf(MSG_DEBUG, 459341618Scy "FILS: HLP relayed DHCP request to server %s:%d (rapid_commit=%d)", 460341618Scy inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), 461341618Scy rapid_commit); 462341618Scy if (hapd->conf->dhcp_rapid_commit_proxy && rapid_commit) { 463341618Scy /* Store a copy of the DHCPDISCOVER for rapid commit proxying 464341618Scy * purposes if the server does not support the rapid commit 465341618Scy * option. */ 466341618Scy wpa_printf(MSG_DEBUG, 467341618Scy "FILS: Store DHCPDISCOVER for rapid commit proxy"); 468341618Scy wpabuf_free(sta->hlp_dhcp_discover); 469341618Scy sta->hlp_dhcp_discover = dhcp_buf; 470341618Scy } else { 471341618Scy wpabuf_free(dhcp_buf); 472341618Scy } 473341618Scy 474341618Scy return 1; 475341618Scy} 476341618Scy 477341618Scy 478341618Scystatic int fils_process_hlp_udp(struct hostapd_data *hapd, 479341618Scy struct sta_info *sta, const u8 *dst, 480341618Scy const u8 *pos, size_t len) 481341618Scy{ 482341618Scy const struct iphdr *iph; 483341618Scy const struct udphdr *udph; 484341618Scy u16 sport, dport, ulen; 485341618Scy 486341618Scy if (len < sizeof(*iph) + sizeof(*udph)) 487341618Scy return 0; 488341618Scy iph = (const struct iphdr *) pos; 489341618Scy udph = (const struct udphdr *) (iph + 1); 490341618Scy sport = ntohs(udph->uh_sport); 491341618Scy dport = ntohs(udph->uh_dport); 492341618Scy ulen = ntohs(udph->uh_ulen); 493341618Scy wpa_printf(MSG_DEBUG, 494341618Scy "FILS: HLP request UDP: sport=%u dport=%u ulen=%u sum=0x%x", 495341618Scy sport, dport, ulen, ntohs(udph->uh_sum)); 496341618Scy /* TODO: Check UDP checksum */ 497341618Scy if (ulen < sizeof(*udph) || ulen > len - sizeof(*iph)) 498341618Scy return 0; 499341618Scy 500341618Scy if (dport == DHCP_SERVER_PORT && sport == DHCP_CLIENT_PORT) { 501341618Scy return fils_process_hlp_dhcp(hapd, sta, (const u8 *) (udph + 1), 502341618Scy ulen - sizeof(*udph)); 503341618Scy } 504341618Scy 505341618Scy return 0; 506341618Scy} 507341618Scy 508341618Scy 509341618Scystatic int fils_process_hlp_ip(struct hostapd_data *hapd, 510341618Scy struct sta_info *sta, const u8 *dst, 511341618Scy const u8 *pos, size_t len) 512341618Scy{ 513341618Scy const struct iphdr *iph; 514341618Scy u16 tot_len; 515341618Scy 516341618Scy if (len < sizeof(*iph)) 517341618Scy return 0; 518341618Scy iph = (const struct iphdr *) pos; 519341618Scy if (ip_checksum(iph, sizeof(*iph)) != 0) { 520341618Scy wpa_printf(MSG_DEBUG, 521341618Scy "FILS: HLP request IPv4 packet had invalid header checksum - dropped"); 522341618Scy return 0; 523341618Scy } 524341618Scy tot_len = ntohs(iph->tot_len); 525341618Scy if (tot_len > len) 526341618Scy return 0; 527341618Scy wpa_printf(MSG_DEBUG, 528341618Scy "FILS: HLP request IPv4: saddr=%08x daddr=%08x protocol=%u", 529341618Scy iph->saddr, iph->daddr, iph->protocol); 530341618Scy switch (iph->protocol) { 531341618Scy case 17: 532341618Scy return fils_process_hlp_udp(hapd, sta, dst, pos, len); 533341618Scy } 534341618Scy 535341618Scy return 0; 536341618Scy} 537341618Scy 538341618Scy 539341618Scystatic int fils_process_hlp_req(struct hostapd_data *hapd, 540341618Scy struct sta_info *sta, 541341618Scy const u8 *pos, size_t len) 542341618Scy{ 543341618Scy const u8 *pkt, *end; 544341618Scy 545341618Scy wpa_printf(MSG_DEBUG, "FILS: HLP request from " MACSTR " (dst=" MACSTR 546341618Scy " src=" MACSTR " len=%u)", 547341618Scy MAC2STR(sta->addr), MAC2STR(pos), MAC2STR(pos + ETH_ALEN), 548341618Scy (unsigned int) len); 549341618Scy if (os_memcmp(sta->addr, pos + ETH_ALEN, ETH_ALEN) != 0) { 550341618Scy wpa_printf(MSG_DEBUG, 551341618Scy "FILS: Ignore HLP request with unexpected source address" 552341618Scy MACSTR, MAC2STR(pos + ETH_ALEN)); 553341618Scy return 0; 554341618Scy } 555341618Scy 556341618Scy end = pos + len; 557341618Scy pkt = pos + 2 * ETH_ALEN; 558341618Scy if (end - pkt >= 6 && 559341618Scy os_memcmp(pkt, "\xaa\xaa\x03\x00\x00\x00", 6) == 0) 560341618Scy pkt += 6; /* Remove SNAP/LLC header */ 561341618Scy wpa_hexdump(MSG_MSGDUMP, "FILS: HLP request packet", pkt, end - pkt); 562341618Scy 563341618Scy if (end - pkt < 2) 564341618Scy return 0; 565341618Scy 566341618Scy switch (WPA_GET_BE16(pkt)) { 567341618Scy case ETH_P_IP: 568341618Scy return fils_process_hlp_ip(hapd, sta, pos, pkt + 2, 569341618Scy end - pkt - 2); 570341618Scy } 571341618Scy 572341618Scy return 0; 573341618Scy} 574341618Scy 575341618Scy 576341618Scyint fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta, 577341618Scy const u8 *pos, int left) 578341618Scy{ 579341618Scy const u8 *end = pos + left; 580341618Scy u8 *tmp, *tmp_pos; 581341618Scy int ret = 0; 582341618Scy 583346981Scy if (sta->fils_pending_assoc_req && 584346981Scy eloop_is_timeout_registered(fils_hlp_timeout, hapd, sta)) { 585346981Scy /* Do not process FILS HLP request again if the station 586346981Scy * retransmits (Re)Association Request frame before the previous 587346981Scy * HLP response has either been received or timed out. */ 588346981Scy wpa_printf(MSG_DEBUG, 589346981Scy "FILS: Do not relay another HLP request from " 590346981Scy MACSTR 591346981Scy " before processing of the already pending one has been completed", 592346981Scy MAC2STR(sta->addr)); 593346981Scy return 1; 594346981Scy } 595346981Scy 596341618Scy /* Old DHCPDISCOVER is not needed anymore, if it was still pending */ 597341618Scy wpabuf_free(sta->hlp_dhcp_discover); 598341618Scy sta->hlp_dhcp_discover = NULL; 599341618Scy sta->fils_dhcp_rapid_commit_proxy = 0; 600341618Scy 601341618Scy /* Check if there are any FILS HLP Container elements */ 602341618Scy while (end - pos >= 2) { 603341618Scy if (2 + pos[1] > end - pos) 604341618Scy return 0; 605341618Scy if (pos[0] == WLAN_EID_EXTENSION && 606341618Scy pos[1] >= 1 + 2 * ETH_ALEN && 607341618Scy pos[2] == WLAN_EID_EXT_FILS_HLP_CONTAINER) 608341618Scy break; 609341618Scy pos += 2 + pos[1]; 610341618Scy } 611341618Scy if (end - pos < 2) 612341618Scy return 0; /* No FILS HLP Container elements */ 613341618Scy 614341618Scy tmp = os_malloc(end - pos); 615341618Scy if (!tmp) 616341618Scy return 0; 617341618Scy 618341618Scy while (end - pos >= 2) { 619341618Scy if (2 + pos[1] > end - pos || 620341618Scy pos[0] != WLAN_EID_EXTENSION || 621341618Scy pos[1] < 1 + 2 * ETH_ALEN || 622341618Scy pos[2] != WLAN_EID_EXT_FILS_HLP_CONTAINER) 623341618Scy break; 624341618Scy tmp_pos = tmp; 625341618Scy os_memcpy(tmp_pos, pos + 3, pos[1] - 1); 626341618Scy tmp_pos += pos[1] - 1; 627341618Scy pos += 2 + pos[1]; 628341618Scy 629341618Scy /* Add possible fragments */ 630341618Scy while (end - pos >= 2 && pos[0] == WLAN_EID_FRAGMENT && 631341618Scy 2 + pos[1] <= end - pos) { 632341618Scy os_memcpy(tmp_pos, pos + 2, pos[1]); 633341618Scy tmp_pos += pos[1]; 634341618Scy pos += 2 + pos[1]; 635341618Scy } 636341618Scy 637341618Scy if (fils_process_hlp_req(hapd, sta, tmp, tmp_pos - tmp) > 0) 638341618Scy ret = 1; 639341618Scy } 640341618Scy 641341618Scy os_free(tmp); 642341618Scy 643341618Scy return ret; 644341618Scy} 645341618Scy 646341618Scy 647341618Scyvoid fils_hlp_deinit(struct hostapd_data *hapd) 648341618Scy{ 649341618Scy if (hapd->dhcp_sock >= 0) { 650341618Scy eloop_unregister_read_sock(hapd->dhcp_sock); 651341618Scy close(hapd->dhcp_sock); 652341618Scy hapd->dhcp_sock = -1; 653341618Scy } 654341618Scy} 655