1e5b75505Sopenharmony_ci/* 2e5b75505Sopenharmony_ci * FILS HLP request processing 3e5b75505Sopenharmony_ci * Copyright (c) 2017, Qualcomm Atheros, Inc. 4e5b75505Sopenharmony_ci * 5e5b75505Sopenharmony_ci * This software may be distributed under the terms of the BSD license. 6e5b75505Sopenharmony_ci * See README for more details. 7e5b75505Sopenharmony_ci */ 8e5b75505Sopenharmony_ci 9e5b75505Sopenharmony_ci#include "utils/includes.h" 10e5b75505Sopenharmony_ci 11e5b75505Sopenharmony_ci#include "utils/common.h" 12e5b75505Sopenharmony_ci#include "utils/eloop.h" 13e5b75505Sopenharmony_ci#include "common/dhcp.h" 14e5b75505Sopenharmony_ci#include "hostapd.h" 15e5b75505Sopenharmony_ci#include "sta_info.h" 16e5b75505Sopenharmony_ci#include "ieee802_11.h" 17e5b75505Sopenharmony_ci#include "fils_hlp.h" 18e5b75505Sopenharmony_ci 19e5b75505Sopenharmony_ci 20e5b75505Sopenharmony_cistatic be16 ip_checksum(const void *buf, size_t len) 21e5b75505Sopenharmony_ci{ 22e5b75505Sopenharmony_ci u32 sum = 0; 23e5b75505Sopenharmony_ci const u16 *pos; 24e5b75505Sopenharmony_ci 25e5b75505Sopenharmony_ci for (pos = buf; len >= 2; len -= 2) 26e5b75505Sopenharmony_ci sum += ntohs(*pos++); 27e5b75505Sopenharmony_ci if (len) 28e5b75505Sopenharmony_ci sum += ntohs(*pos << 8); 29e5b75505Sopenharmony_ci 30e5b75505Sopenharmony_ci sum = (sum >> 16) + (sum & 0xffff); 31e5b75505Sopenharmony_ci sum += sum >> 16; 32e5b75505Sopenharmony_ci return htons(~sum); 33e5b75505Sopenharmony_ci} 34e5b75505Sopenharmony_ci 35e5b75505Sopenharmony_ci 36e5b75505Sopenharmony_cistatic int fils_dhcp_request(struct hostapd_data *hapd, struct sta_info *sta, 37e5b75505Sopenharmony_ci struct dhcp_data *dhcpoffer, u8 *dhcpofferend) 38e5b75505Sopenharmony_ci{ 39e5b75505Sopenharmony_ci u8 *pos, *end; 40e5b75505Sopenharmony_ci struct dhcp_data *dhcp; 41e5b75505Sopenharmony_ci struct sockaddr_in addr; 42e5b75505Sopenharmony_ci ssize_t res; 43e5b75505Sopenharmony_ci const u8 *server_id = NULL; 44e5b75505Sopenharmony_ci 45e5b75505Sopenharmony_ci if (!sta->hlp_dhcp_discover) { 46e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 47e5b75505Sopenharmony_ci "FILS: No pending HLP DHCPDISCOVER available"); 48e5b75505Sopenharmony_ci return -1; 49e5b75505Sopenharmony_ci } 50e5b75505Sopenharmony_ci 51e5b75505Sopenharmony_ci /* Convert to DHCPREQUEST, remove rapid commit option, replace requested 52e5b75505Sopenharmony_ci * IP address option with yiaddr. */ 53e5b75505Sopenharmony_ci pos = wpabuf_mhead(sta->hlp_dhcp_discover); 54e5b75505Sopenharmony_ci end = pos + wpabuf_len(sta->hlp_dhcp_discover); 55e5b75505Sopenharmony_ci dhcp = (struct dhcp_data *) pos; 56e5b75505Sopenharmony_ci pos = (u8 *) (dhcp + 1); 57e5b75505Sopenharmony_ci pos += 4; /* skip magic */ 58e5b75505Sopenharmony_ci while (pos < end && *pos != DHCP_OPT_END) { 59e5b75505Sopenharmony_ci u8 opt, olen; 60e5b75505Sopenharmony_ci 61e5b75505Sopenharmony_ci opt = *pos++; 62e5b75505Sopenharmony_ci if (opt == DHCP_OPT_PAD) 63e5b75505Sopenharmony_ci continue; 64e5b75505Sopenharmony_ci if (pos >= end) 65e5b75505Sopenharmony_ci break; 66e5b75505Sopenharmony_ci olen = *pos++; 67e5b75505Sopenharmony_ci if (olen > end - pos) 68e5b75505Sopenharmony_ci break; 69e5b75505Sopenharmony_ci 70e5b75505Sopenharmony_ci switch (opt) { 71e5b75505Sopenharmony_ci case DHCP_OPT_MSG_TYPE: 72e5b75505Sopenharmony_ci if (olen > 0) 73e5b75505Sopenharmony_ci *pos = DHCPREQUEST; 74e5b75505Sopenharmony_ci break; 75e5b75505Sopenharmony_ci case DHCP_OPT_RAPID_COMMIT: 76e5b75505Sopenharmony_ci case DHCP_OPT_REQUESTED_IP_ADDRESS: 77e5b75505Sopenharmony_ci case DHCP_OPT_SERVER_ID: 78e5b75505Sopenharmony_ci /* Remove option */ 79e5b75505Sopenharmony_ci pos -= 2; 80e5b75505Sopenharmony_ci os_memmove(pos, pos + 2 + olen, end - pos - 2 - olen); 81e5b75505Sopenharmony_ci end -= 2 + olen; 82e5b75505Sopenharmony_ci olen = 0; 83e5b75505Sopenharmony_ci break; 84e5b75505Sopenharmony_ci } 85e5b75505Sopenharmony_ci pos += olen; 86e5b75505Sopenharmony_ci } 87e5b75505Sopenharmony_ci if (pos >= end || *pos != DHCP_OPT_END) { 88e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "FILS: Could not update DHCPDISCOVER"); 89e5b75505Sopenharmony_ci return -1; 90e5b75505Sopenharmony_ci } 91e5b75505Sopenharmony_ci sta->hlp_dhcp_discover->used = pos - (u8 *) dhcp; 92e5b75505Sopenharmony_ci 93e5b75505Sopenharmony_ci /* Copy Server ID option from DHCPOFFER to DHCPREQUEST */ 94e5b75505Sopenharmony_ci pos = (u8 *) (dhcpoffer + 1); 95e5b75505Sopenharmony_ci end = dhcpofferend; 96e5b75505Sopenharmony_ci pos += 4; /* skip magic */ 97e5b75505Sopenharmony_ci while (pos < end && *pos != DHCP_OPT_END) { 98e5b75505Sopenharmony_ci u8 opt, olen; 99e5b75505Sopenharmony_ci 100e5b75505Sopenharmony_ci opt = *pos++; 101e5b75505Sopenharmony_ci if (opt == DHCP_OPT_PAD) 102e5b75505Sopenharmony_ci continue; 103e5b75505Sopenharmony_ci if (pos >= end) 104e5b75505Sopenharmony_ci break; 105e5b75505Sopenharmony_ci olen = *pos++; 106e5b75505Sopenharmony_ci if (olen > end - pos) 107e5b75505Sopenharmony_ci break; 108e5b75505Sopenharmony_ci 109e5b75505Sopenharmony_ci switch (opt) { 110e5b75505Sopenharmony_ci case DHCP_OPT_SERVER_ID: 111e5b75505Sopenharmony_ci server_id = pos - 2; 112e5b75505Sopenharmony_ci break; 113e5b75505Sopenharmony_ci } 114e5b75505Sopenharmony_ci pos += olen; 115e5b75505Sopenharmony_ci } 116e5b75505Sopenharmony_ci 117e5b75505Sopenharmony_ci if (wpabuf_resize(&sta->hlp_dhcp_discover, 118e5b75505Sopenharmony_ci 6 + 1 + (server_id ? 2 + server_id[1] : 0))) 119e5b75505Sopenharmony_ci return -1; 120e5b75505Sopenharmony_ci if (server_id) 121e5b75505Sopenharmony_ci wpabuf_put_data(sta->hlp_dhcp_discover, server_id, 122e5b75505Sopenharmony_ci 2 + server_id[1]); 123e5b75505Sopenharmony_ci wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_REQUESTED_IP_ADDRESS); 124e5b75505Sopenharmony_ci wpabuf_put_u8(sta->hlp_dhcp_discover, 4); 125e5b75505Sopenharmony_ci wpabuf_put_data(sta->hlp_dhcp_discover, &dhcpoffer->your_ip, 4); 126e5b75505Sopenharmony_ci wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_END); 127e5b75505Sopenharmony_ci 128e5b75505Sopenharmony_ci os_memset(&addr, 0, sizeof(addr)); 129e5b75505Sopenharmony_ci addr.sin_family = AF_INET; 130e5b75505Sopenharmony_ci addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr; 131e5b75505Sopenharmony_ci addr.sin_port = htons(hapd->conf->dhcp_server_port); 132e5b75505Sopenharmony_ci res = sendto(hapd->dhcp_sock, wpabuf_head(sta->hlp_dhcp_discover), 133e5b75505Sopenharmony_ci wpabuf_len(sta->hlp_dhcp_discover), 0, 134e5b75505Sopenharmony_ci (const struct sockaddr *) &addr, sizeof(addr)); 135e5b75505Sopenharmony_ci if (res < 0) { 136e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s", 137e5b75505Sopenharmony_ci strerror(errno)); 138e5b75505Sopenharmony_ci return -1; 139e5b75505Sopenharmony_ci } 140e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 141e5b75505Sopenharmony_ci "FILS: Acting as DHCP rapid commit proxy for %s:%d", 142e5b75505Sopenharmony_ci inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); 143e5b75505Sopenharmony_ci wpabuf_free(sta->hlp_dhcp_discover); 144e5b75505Sopenharmony_ci sta->hlp_dhcp_discover = NULL; 145e5b75505Sopenharmony_ci sta->fils_dhcp_rapid_commit_proxy = 1; 146e5b75505Sopenharmony_ci return 0; 147e5b75505Sopenharmony_ci} 148e5b75505Sopenharmony_ci 149e5b75505Sopenharmony_ci 150e5b75505Sopenharmony_cistatic void fils_dhcp_handler(int sd, void *eloop_ctx, void *sock_ctx) 151e5b75505Sopenharmony_ci{ 152e5b75505Sopenharmony_ci struct hostapd_data *hapd = sock_ctx; 153e5b75505Sopenharmony_ci struct sta_info *sta; 154e5b75505Sopenharmony_ci u8 buf[1500], *pos, *end, *end_opt = NULL; 155e5b75505Sopenharmony_ci struct dhcp_data *dhcp; 156e5b75505Sopenharmony_ci struct sockaddr_in addr; 157e5b75505Sopenharmony_ci socklen_t addr_len; 158e5b75505Sopenharmony_ci ssize_t res; 159e5b75505Sopenharmony_ci u8 msgtype = 0; 160e5b75505Sopenharmony_ci int rapid_commit = 0; 161e5b75505Sopenharmony_ci struct iphdr *iph; 162e5b75505Sopenharmony_ci struct udphdr *udph; 163e5b75505Sopenharmony_ci struct wpabuf *resp; 164e5b75505Sopenharmony_ci const u8 *rpos; 165e5b75505Sopenharmony_ci size_t left, len; 166e5b75505Sopenharmony_ci 167e5b75505Sopenharmony_ci addr_len = sizeof(addr); 168e5b75505Sopenharmony_ci res = recvfrom(sd, buf, sizeof(buf), 0, 169e5b75505Sopenharmony_ci (struct sockaddr *) &addr, &addr_len); 170e5b75505Sopenharmony_ci if (res < 0) { 171e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "FILS: DHCP read failed: %s", 172e5b75505Sopenharmony_ci strerror(errno)); 173e5b75505Sopenharmony_ci return; 174e5b75505Sopenharmony_ci } 175e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "FILS: DHCP response from server %s:%d (len=%d)", 176e5b75505Sopenharmony_ci inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), (int) res); 177e5b75505Sopenharmony_ci wpa_hexdump(MSG_MSGDUMP, "FILS: HLP - DHCP server response", buf, res); 178e5b75505Sopenharmony_ci if ((size_t) res < sizeof(*dhcp)) 179e5b75505Sopenharmony_ci return; 180e5b75505Sopenharmony_ci dhcp = (struct dhcp_data *) buf; 181e5b75505Sopenharmony_ci if (dhcp->op != 2) 182e5b75505Sopenharmony_ci return; /* Not a BOOTREPLY */ 183e5b75505Sopenharmony_ci if (dhcp->relay_ip != hapd->conf->own_ip_addr.u.v4.s_addr) { 184e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 185e5b75505Sopenharmony_ci "FILS: HLP - DHCP response to unknown relay address 0x%x", 186e5b75505Sopenharmony_ci dhcp->relay_ip); 187e5b75505Sopenharmony_ci return; 188e5b75505Sopenharmony_ci } 189e5b75505Sopenharmony_ci dhcp->relay_ip = 0; 190e5b75505Sopenharmony_ci pos = (u8 *) (dhcp + 1); 191e5b75505Sopenharmony_ci end = &buf[res]; 192e5b75505Sopenharmony_ci 193e5b75505Sopenharmony_ci if (end - pos < 4 || WPA_GET_BE32(pos) != DHCP_MAGIC) { 194e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "FILS: HLP - no DHCP magic in response"); 195e5b75505Sopenharmony_ci return; 196e5b75505Sopenharmony_ci } 197e5b75505Sopenharmony_ci pos += 4; 198e5b75505Sopenharmony_ci 199e5b75505Sopenharmony_ci wpa_hexdump(MSG_DEBUG, "FILS: HLP - DHCP options in response", 200e5b75505Sopenharmony_ci pos, end - pos); 201e5b75505Sopenharmony_ci while (pos < end && *pos != DHCP_OPT_END) { 202e5b75505Sopenharmony_ci u8 opt, olen; 203e5b75505Sopenharmony_ci 204e5b75505Sopenharmony_ci opt = *pos++; 205e5b75505Sopenharmony_ci if (opt == DHCP_OPT_PAD) 206e5b75505Sopenharmony_ci continue; 207e5b75505Sopenharmony_ci if (pos >= end) 208e5b75505Sopenharmony_ci break; 209e5b75505Sopenharmony_ci olen = *pos++; 210e5b75505Sopenharmony_ci if (olen > end - pos) 211e5b75505Sopenharmony_ci break; 212e5b75505Sopenharmony_ci 213e5b75505Sopenharmony_ci switch (opt) { 214e5b75505Sopenharmony_ci case DHCP_OPT_MSG_TYPE: 215e5b75505Sopenharmony_ci if (olen > 0) 216e5b75505Sopenharmony_ci msgtype = pos[0]; 217e5b75505Sopenharmony_ci break; 218e5b75505Sopenharmony_ci case DHCP_OPT_RAPID_COMMIT: 219e5b75505Sopenharmony_ci rapid_commit = 1; 220e5b75505Sopenharmony_ci break; 221e5b75505Sopenharmony_ci } 222e5b75505Sopenharmony_ci pos += olen; 223e5b75505Sopenharmony_ci } 224e5b75505Sopenharmony_ci if (pos < end && *pos == DHCP_OPT_END) 225e5b75505Sopenharmony_ci end_opt = pos; 226e5b75505Sopenharmony_ci 227e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 228e5b75505Sopenharmony_ci "FILS: HLP - DHCP message type %u (rapid_commit=%d hw_addr=" 229e5b75505Sopenharmony_ci MACSTR ")", 230e5b75505Sopenharmony_ci msgtype, rapid_commit, MAC2STR(dhcp->hw_addr)); 231e5b75505Sopenharmony_ci 232e5b75505Sopenharmony_ci sta = ap_get_sta(hapd, dhcp->hw_addr); 233e5b75505Sopenharmony_ci if (!sta || !sta->fils_pending_assoc_req) { 234e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 235e5b75505Sopenharmony_ci "FILS: No pending HLP DHCP exchange with hw_addr " 236e5b75505Sopenharmony_ci MACSTR, MAC2STR(dhcp->hw_addr)); 237e5b75505Sopenharmony_ci return; 238e5b75505Sopenharmony_ci } 239e5b75505Sopenharmony_ci 240e5b75505Sopenharmony_ci if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPOFFER && 241e5b75505Sopenharmony_ci !rapid_commit) { 242e5b75505Sopenharmony_ci /* Use hostapd to take care of 4-message exchange and convert 243e5b75505Sopenharmony_ci * the final DHCPACK to rapid commit version. */ 244e5b75505Sopenharmony_ci if (fils_dhcp_request(hapd, sta, dhcp, end) == 0) 245e5b75505Sopenharmony_ci return; 246e5b75505Sopenharmony_ci /* failed, so send the server response as-is */ 247e5b75505Sopenharmony_ci } else if (msgtype != DHCPACK) { 248e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 249e5b75505Sopenharmony_ci "FILS: No DHCPACK available from the server and cannot do rapid commit proxying"); 250e5b75505Sopenharmony_ci } 251e5b75505Sopenharmony_ci 252e5b75505Sopenharmony_ci pos = buf; 253e5b75505Sopenharmony_ci resp = wpabuf_alloc(2 * ETH_ALEN + 6 + 2 + 254e5b75505Sopenharmony_ci sizeof(*iph) + sizeof(*udph) + (end - pos) + 2); 255e5b75505Sopenharmony_ci if (!resp) 256e5b75505Sopenharmony_ci return; 257e5b75505Sopenharmony_ci wpabuf_put_data(resp, sta->addr, ETH_ALEN); 258e5b75505Sopenharmony_ci wpabuf_put_data(resp, hapd->own_addr, ETH_ALEN); 259e5b75505Sopenharmony_ci wpabuf_put_data(resp, "\xaa\xaa\x03\x00\x00\x00", 6); 260e5b75505Sopenharmony_ci wpabuf_put_be16(resp, ETH_P_IP); 261e5b75505Sopenharmony_ci iph = wpabuf_put(resp, sizeof(*iph)); 262e5b75505Sopenharmony_ci iph->version = 4; 263e5b75505Sopenharmony_ci iph->ihl = sizeof(*iph) / 4; 264e5b75505Sopenharmony_ci iph->tot_len = htons(sizeof(*iph) + sizeof(*udph) + (end - pos)); 265e5b75505Sopenharmony_ci iph->ttl = 1; 266e5b75505Sopenharmony_ci iph->protocol = 17; /* UDP */ 267e5b75505Sopenharmony_ci iph->saddr = hapd->conf->dhcp_server.u.v4.s_addr; 268e5b75505Sopenharmony_ci iph->daddr = dhcp->client_ip; 269e5b75505Sopenharmony_ci iph->check = ip_checksum(iph, sizeof(*iph)); 270e5b75505Sopenharmony_ci udph = wpabuf_put(resp, sizeof(*udph)); 271e5b75505Sopenharmony_ci udph->uh_sport = htons(DHCP_SERVER_PORT); 272e5b75505Sopenharmony_ci udph->uh_dport = htons(DHCP_CLIENT_PORT); 273e5b75505Sopenharmony_ci udph->uh_ulen = htons(sizeof(*udph) + (end - pos)); 274e5b75505Sopenharmony_ci udph->uh_sum = htons(0x0000); /* TODO: calculate checksum */ 275e5b75505Sopenharmony_ci if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPACK && 276e5b75505Sopenharmony_ci !rapid_commit && sta->fils_dhcp_rapid_commit_proxy && end_opt) { 277e5b75505Sopenharmony_ci /* Add rapid commit option */ 278e5b75505Sopenharmony_ci wpabuf_put_data(resp, pos, end_opt - pos); 279e5b75505Sopenharmony_ci wpabuf_put_u8(resp, DHCP_OPT_RAPID_COMMIT); 280e5b75505Sopenharmony_ci wpabuf_put_u8(resp, 0); 281e5b75505Sopenharmony_ci wpabuf_put_data(resp, end_opt, end - end_opt); 282e5b75505Sopenharmony_ci } else { 283e5b75505Sopenharmony_ci wpabuf_put_data(resp, pos, end - pos); 284e5b75505Sopenharmony_ci } 285e5b75505Sopenharmony_ci if (wpabuf_resize(&sta->fils_hlp_resp, wpabuf_len(resp) + 286e5b75505Sopenharmony_ci 2 * wpabuf_len(resp) / 255 + 100)) { 287e5b75505Sopenharmony_ci wpabuf_free(resp); 288e5b75505Sopenharmony_ci return; 289e5b75505Sopenharmony_ci } 290e5b75505Sopenharmony_ci 291e5b75505Sopenharmony_ci rpos = wpabuf_head(resp); 292e5b75505Sopenharmony_ci left = wpabuf_len(resp); 293e5b75505Sopenharmony_ci 294e5b75505Sopenharmony_ci wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_EXTENSION); /* Element ID */ 295e5b75505Sopenharmony_ci if (left <= 254) 296e5b75505Sopenharmony_ci len = 1 + left; 297e5b75505Sopenharmony_ci else 298e5b75505Sopenharmony_ci len = 255; 299e5b75505Sopenharmony_ci wpabuf_put_u8(sta->fils_hlp_resp, len); /* Length */ 300e5b75505Sopenharmony_ci /* Element ID Extension */ 301e5b75505Sopenharmony_ci wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_EXT_FILS_HLP_CONTAINER); 302e5b75505Sopenharmony_ci /* Destination MAC Address, Source MAC Address, HLP Packet. 303e5b75505Sopenharmony_ci * HLP Packet is in MSDU format (i.e., including the LLC/SNAP header 304e5b75505Sopenharmony_ci * when LPD is used). */ 305e5b75505Sopenharmony_ci wpabuf_put_data(sta->fils_hlp_resp, rpos, len - 1); 306e5b75505Sopenharmony_ci rpos += len - 1; 307e5b75505Sopenharmony_ci left -= len - 1; 308e5b75505Sopenharmony_ci while (left) { 309e5b75505Sopenharmony_ci wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_FRAGMENT); 310e5b75505Sopenharmony_ci len = left > 255 ? 255 : left; 311e5b75505Sopenharmony_ci wpabuf_put_u8(sta->fils_hlp_resp, len); 312e5b75505Sopenharmony_ci wpabuf_put_data(sta->fils_hlp_resp, rpos, len); 313e5b75505Sopenharmony_ci rpos += len; 314e5b75505Sopenharmony_ci left -= len; 315e5b75505Sopenharmony_ci } 316e5b75505Sopenharmony_ci wpabuf_free(resp); 317e5b75505Sopenharmony_ci 318e5b75505Sopenharmony_ci if (sta->fils_drv_assoc_finish) 319e5b75505Sopenharmony_ci hostapd_notify_assoc_fils_finish(hapd, sta); 320e5b75505Sopenharmony_ci else 321e5b75505Sopenharmony_ci fils_hlp_finish_assoc(hapd, sta); 322e5b75505Sopenharmony_ci} 323e5b75505Sopenharmony_ci 324e5b75505Sopenharmony_ci 325e5b75505Sopenharmony_cistatic int fils_process_hlp_dhcp(struct hostapd_data *hapd, 326e5b75505Sopenharmony_ci struct sta_info *sta, 327e5b75505Sopenharmony_ci const u8 *msg, size_t len) 328e5b75505Sopenharmony_ci{ 329e5b75505Sopenharmony_ci const struct dhcp_data *dhcp; 330e5b75505Sopenharmony_ci struct wpabuf *dhcp_buf; 331e5b75505Sopenharmony_ci struct dhcp_data *dhcp_msg; 332e5b75505Sopenharmony_ci u8 msgtype = 0; 333e5b75505Sopenharmony_ci int rapid_commit = 0; 334e5b75505Sopenharmony_ci const u8 *pos = msg, *end; 335e5b75505Sopenharmony_ci struct sockaddr_in addr; 336e5b75505Sopenharmony_ci ssize_t res; 337e5b75505Sopenharmony_ci 338e5b75505Sopenharmony_ci if (len < sizeof(*dhcp)) 339e5b75505Sopenharmony_ci return 0; 340e5b75505Sopenharmony_ci dhcp = (const struct dhcp_data *) pos; 341e5b75505Sopenharmony_ci end = pos + len; 342e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 343e5b75505Sopenharmony_ci "FILS: HLP request DHCP: op=%u htype=%u hlen=%u hops=%u xid=0x%x", 344e5b75505Sopenharmony_ci dhcp->op, dhcp->htype, dhcp->hlen, dhcp->hops, 345e5b75505Sopenharmony_ci ntohl(dhcp->xid)); 346e5b75505Sopenharmony_ci pos += sizeof(*dhcp); 347e5b75505Sopenharmony_ci if (dhcp->op != 1) 348e5b75505Sopenharmony_ci return 0; /* Not a BOOTREQUEST */ 349e5b75505Sopenharmony_ci 350e5b75505Sopenharmony_ci if (end - pos < 4) 351e5b75505Sopenharmony_ci return 0; 352e5b75505Sopenharmony_ci if (WPA_GET_BE32(pos) != DHCP_MAGIC) { 353e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "FILS: HLP - no DHCP magic"); 354e5b75505Sopenharmony_ci return 0; 355e5b75505Sopenharmony_ci } 356e5b75505Sopenharmony_ci pos += 4; 357e5b75505Sopenharmony_ci 358e5b75505Sopenharmony_ci wpa_hexdump(MSG_DEBUG, "FILS: HLP - DHCP options", pos, end - pos); 359e5b75505Sopenharmony_ci while (pos < end && *pos != DHCP_OPT_END) { 360e5b75505Sopenharmony_ci u8 opt, olen; 361e5b75505Sopenharmony_ci 362e5b75505Sopenharmony_ci opt = *pos++; 363e5b75505Sopenharmony_ci if (opt == DHCP_OPT_PAD) 364e5b75505Sopenharmony_ci continue; 365e5b75505Sopenharmony_ci if (pos >= end) 366e5b75505Sopenharmony_ci break; 367e5b75505Sopenharmony_ci olen = *pos++; 368e5b75505Sopenharmony_ci if (olen > end - pos) 369e5b75505Sopenharmony_ci break; 370e5b75505Sopenharmony_ci 371e5b75505Sopenharmony_ci switch (opt) { 372e5b75505Sopenharmony_ci case DHCP_OPT_MSG_TYPE: 373e5b75505Sopenharmony_ci if (olen > 0) 374e5b75505Sopenharmony_ci msgtype = pos[0]; 375e5b75505Sopenharmony_ci break; 376e5b75505Sopenharmony_ci case DHCP_OPT_RAPID_COMMIT: 377e5b75505Sopenharmony_ci rapid_commit = 1; 378e5b75505Sopenharmony_ci break; 379e5b75505Sopenharmony_ci } 380e5b75505Sopenharmony_ci pos += olen; 381e5b75505Sopenharmony_ci } 382e5b75505Sopenharmony_ci 383e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "FILS: HLP - DHCP message type %u", msgtype); 384e5b75505Sopenharmony_ci if (msgtype != DHCPDISCOVER) 385e5b75505Sopenharmony_ci return 0; 386e5b75505Sopenharmony_ci 387e5b75505Sopenharmony_ci if (hapd->conf->dhcp_server.af != AF_INET || 388e5b75505Sopenharmony_ci hapd->conf->dhcp_server.u.v4.s_addr == 0) { 389e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 390e5b75505Sopenharmony_ci "FILS: HLP - no DHCPv4 server configured - drop request"); 391e5b75505Sopenharmony_ci return 0; 392e5b75505Sopenharmony_ci } 393e5b75505Sopenharmony_ci 394e5b75505Sopenharmony_ci if (hapd->conf->own_ip_addr.af != AF_INET || 395e5b75505Sopenharmony_ci hapd->conf->own_ip_addr.u.v4.s_addr == 0) { 396e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 397e5b75505Sopenharmony_ci "FILS: HLP - no IPv4 own_ip_addr configured - drop request"); 398e5b75505Sopenharmony_ci return 0; 399e5b75505Sopenharmony_ci } 400e5b75505Sopenharmony_ci 401e5b75505Sopenharmony_ci if (hapd->dhcp_sock < 0) { 402e5b75505Sopenharmony_ci int s; 403e5b75505Sopenharmony_ci 404e5b75505Sopenharmony_ci s = socket(AF_INET, SOCK_DGRAM, 0); 405e5b75505Sopenharmony_ci if (s < 0) { 406e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, 407e5b75505Sopenharmony_ci "FILS: Failed to open DHCP socket: %s", 408e5b75505Sopenharmony_ci strerror(errno)); 409e5b75505Sopenharmony_ci return 0; 410e5b75505Sopenharmony_ci } 411e5b75505Sopenharmony_ci 412e5b75505Sopenharmony_ci if (hapd->conf->dhcp_relay_port) { 413e5b75505Sopenharmony_ci os_memset(&addr, 0, sizeof(addr)); 414e5b75505Sopenharmony_ci addr.sin_family = AF_INET; 415e5b75505Sopenharmony_ci addr.sin_addr.s_addr = 416e5b75505Sopenharmony_ci hapd->conf->own_ip_addr.u.v4.s_addr; 417e5b75505Sopenharmony_ci addr.sin_port = htons(hapd->conf->dhcp_relay_port); 418e5b75505Sopenharmony_ci if (bind(s, (struct sockaddr *) &addr, sizeof(addr))) { 419e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, 420e5b75505Sopenharmony_ci "FILS: Failed to bind DHCP socket: %s", 421e5b75505Sopenharmony_ci strerror(errno)); 422e5b75505Sopenharmony_ci close(s); 423e5b75505Sopenharmony_ci return 0; 424e5b75505Sopenharmony_ci } 425e5b75505Sopenharmony_ci } 426e5b75505Sopenharmony_ci if (eloop_register_sock(s, EVENT_TYPE_READ, 427e5b75505Sopenharmony_ci fils_dhcp_handler, NULL, hapd)) { 428e5b75505Sopenharmony_ci close(s); 429e5b75505Sopenharmony_ci return 0; 430e5b75505Sopenharmony_ci } 431e5b75505Sopenharmony_ci 432e5b75505Sopenharmony_ci hapd->dhcp_sock = s; 433e5b75505Sopenharmony_ci } 434e5b75505Sopenharmony_ci 435e5b75505Sopenharmony_ci dhcp_buf = wpabuf_alloc(len); 436e5b75505Sopenharmony_ci if (!dhcp_buf) 437e5b75505Sopenharmony_ci return 0; 438e5b75505Sopenharmony_ci dhcp_msg = wpabuf_put(dhcp_buf, len); 439e5b75505Sopenharmony_ci os_memcpy(dhcp_msg, msg, len); 440e5b75505Sopenharmony_ci dhcp_msg->relay_ip = hapd->conf->own_ip_addr.u.v4.s_addr; 441e5b75505Sopenharmony_ci os_memset(&addr, 0, sizeof(addr)); 442e5b75505Sopenharmony_ci addr.sin_family = AF_INET; 443e5b75505Sopenharmony_ci addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr; 444e5b75505Sopenharmony_ci addr.sin_port = htons(hapd->conf->dhcp_server_port); 445e5b75505Sopenharmony_ci res = sendto(hapd->dhcp_sock, dhcp_msg, len, 0, 446e5b75505Sopenharmony_ci (const struct sockaddr *) &addr, sizeof(addr)); 447e5b75505Sopenharmony_ci if (res < 0) { 448e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s", 449e5b75505Sopenharmony_ci strerror(errno)); 450e5b75505Sopenharmony_ci wpabuf_free(dhcp_buf); 451e5b75505Sopenharmony_ci /* Close the socket to try to recover from error */ 452e5b75505Sopenharmony_ci eloop_unregister_read_sock(hapd->dhcp_sock); 453e5b75505Sopenharmony_ci close(hapd->dhcp_sock); 454e5b75505Sopenharmony_ci hapd->dhcp_sock = -1; 455e5b75505Sopenharmony_ci return 0; 456e5b75505Sopenharmony_ci } 457e5b75505Sopenharmony_ci 458e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 459e5b75505Sopenharmony_ci "FILS: HLP relayed DHCP request to server %s:%d (rapid_commit=%d)", 460e5b75505Sopenharmony_ci inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), 461e5b75505Sopenharmony_ci rapid_commit); 462e5b75505Sopenharmony_ci if (hapd->conf->dhcp_rapid_commit_proxy && rapid_commit) { 463e5b75505Sopenharmony_ci /* Store a copy of the DHCPDISCOVER for rapid commit proxying 464e5b75505Sopenharmony_ci * purposes if the server does not support the rapid commit 465e5b75505Sopenharmony_ci * option. */ 466e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 467e5b75505Sopenharmony_ci "FILS: Store DHCPDISCOVER for rapid commit proxy"); 468e5b75505Sopenharmony_ci wpabuf_free(sta->hlp_dhcp_discover); 469e5b75505Sopenharmony_ci sta->hlp_dhcp_discover = dhcp_buf; 470e5b75505Sopenharmony_ci } else { 471e5b75505Sopenharmony_ci wpabuf_free(dhcp_buf); 472e5b75505Sopenharmony_ci } 473e5b75505Sopenharmony_ci 474e5b75505Sopenharmony_ci return 1; 475e5b75505Sopenharmony_ci} 476e5b75505Sopenharmony_ci 477e5b75505Sopenharmony_ci 478e5b75505Sopenharmony_cistatic int fils_process_hlp_udp(struct hostapd_data *hapd, 479e5b75505Sopenharmony_ci struct sta_info *sta, const u8 *dst, 480e5b75505Sopenharmony_ci const u8 *pos, size_t len) 481e5b75505Sopenharmony_ci{ 482e5b75505Sopenharmony_ci const struct iphdr *iph; 483e5b75505Sopenharmony_ci const struct udphdr *udph; 484e5b75505Sopenharmony_ci u16 sport, dport, ulen; 485e5b75505Sopenharmony_ci 486e5b75505Sopenharmony_ci if (len < sizeof(*iph) + sizeof(*udph)) 487e5b75505Sopenharmony_ci return 0; 488e5b75505Sopenharmony_ci iph = (const struct iphdr *) pos; 489e5b75505Sopenharmony_ci udph = (const struct udphdr *) (iph + 1); 490e5b75505Sopenharmony_ci sport = ntohs(udph->uh_sport); 491e5b75505Sopenharmony_ci dport = ntohs(udph->uh_dport); 492e5b75505Sopenharmony_ci ulen = ntohs(udph->uh_ulen); 493e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 494e5b75505Sopenharmony_ci "FILS: HLP request UDP: sport=%u dport=%u ulen=%u sum=0x%x", 495e5b75505Sopenharmony_ci sport, dport, ulen, ntohs(udph->uh_sum)); 496e5b75505Sopenharmony_ci /* TODO: Check UDP checksum */ 497e5b75505Sopenharmony_ci if (ulen < sizeof(*udph) || ulen > len - sizeof(*iph)) 498e5b75505Sopenharmony_ci return 0; 499e5b75505Sopenharmony_ci 500e5b75505Sopenharmony_ci if (dport == DHCP_SERVER_PORT && sport == DHCP_CLIENT_PORT) { 501e5b75505Sopenharmony_ci return fils_process_hlp_dhcp(hapd, sta, (const u8 *) (udph + 1), 502e5b75505Sopenharmony_ci ulen - sizeof(*udph)); 503e5b75505Sopenharmony_ci } 504e5b75505Sopenharmony_ci 505e5b75505Sopenharmony_ci return 0; 506e5b75505Sopenharmony_ci} 507e5b75505Sopenharmony_ci 508e5b75505Sopenharmony_ci 509e5b75505Sopenharmony_cistatic int fils_process_hlp_ip(struct hostapd_data *hapd, 510e5b75505Sopenharmony_ci struct sta_info *sta, const u8 *dst, 511e5b75505Sopenharmony_ci const u8 *pos, size_t len) 512e5b75505Sopenharmony_ci{ 513e5b75505Sopenharmony_ci const struct iphdr *iph; 514e5b75505Sopenharmony_ci u16 tot_len; 515e5b75505Sopenharmony_ci 516e5b75505Sopenharmony_ci if (len < sizeof(*iph)) 517e5b75505Sopenharmony_ci return 0; 518e5b75505Sopenharmony_ci iph = (const struct iphdr *) pos; 519e5b75505Sopenharmony_ci if (ip_checksum(iph, sizeof(*iph)) != 0) { 520e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 521e5b75505Sopenharmony_ci "FILS: HLP request IPv4 packet had invalid header checksum - dropped"); 522e5b75505Sopenharmony_ci return 0; 523e5b75505Sopenharmony_ci } 524e5b75505Sopenharmony_ci tot_len = ntohs(iph->tot_len); 525e5b75505Sopenharmony_ci if (tot_len > len) 526e5b75505Sopenharmony_ci return 0; 527e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 528e5b75505Sopenharmony_ci "FILS: HLP request IPv4: saddr=%08x daddr=%08x protocol=%u", 529e5b75505Sopenharmony_ci iph->saddr, iph->daddr, iph->protocol); 530e5b75505Sopenharmony_ci switch (iph->protocol) { 531e5b75505Sopenharmony_ci case 17: 532e5b75505Sopenharmony_ci return fils_process_hlp_udp(hapd, sta, dst, pos, len); 533e5b75505Sopenharmony_ci } 534e5b75505Sopenharmony_ci 535e5b75505Sopenharmony_ci return 0; 536e5b75505Sopenharmony_ci} 537e5b75505Sopenharmony_ci 538e5b75505Sopenharmony_ci 539e5b75505Sopenharmony_cistatic int fils_process_hlp_req(struct hostapd_data *hapd, 540e5b75505Sopenharmony_ci struct sta_info *sta, 541e5b75505Sopenharmony_ci const u8 *pos, size_t len) 542e5b75505Sopenharmony_ci{ 543e5b75505Sopenharmony_ci const u8 *pkt, *end; 544e5b75505Sopenharmony_ci 545e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "FILS: HLP request from " MACSTR " (dst=" MACSTR 546e5b75505Sopenharmony_ci " src=" MACSTR " len=%u)", 547e5b75505Sopenharmony_ci MAC2STR(sta->addr), MAC2STR(pos), MAC2STR(pos + ETH_ALEN), 548e5b75505Sopenharmony_ci (unsigned int) len); 549e5b75505Sopenharmony_ci if (os_memcmp(sta->addr, pos + ETH_ALEN, ETH_ALEN) != 0) { 550e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 551e5b75505Sopenharmony_ci "FILS: Ignore HLP request with unexpected source address" 552e5b75505Sopenharmony_ci MACSTR, MAC2STR(pos + ETH_ALEN)); 553e5b75505Sopenharmony_ci return 0; 554e5b75505Sopenharmony_ci } 555e5b75505Sopenharmony_ci 556e5b75505Sopenharmony_ci end = pos + len; 557e5b75505Sopenharmony_ci pkt = pos + 2 * ETH_ALEN; 558e5b75505Sopenharmony_ci if (end - pkt >= 6 && 559e5b75505Sopenharmony_ci os_memcmp(pkt, "\xaa\xaa\x03\x00\x00\x00", 6) == 0) 560e5b75505Sopenharmony_ci pkt += 6; /* Remove SNAP/LLC header */ 561e5b75505Sopenharmony_ci wpa_hexdump(MSG_MSGDUMP, "FILS: HLP request packet", pkt, end - pkt); 562e5b75505Sopenharmony_ci 563e5b75505Sopenharmony_ci if (end - pkt < 2) 564e5b75505Sopenharmony_ci return 0; 565e5b75505Sopenharmony_ci 566e5b75505Sopenharmony_ci switch (WPA_GET_BE16(pkt)) { 567e5b75505Sopenharmony_ci case ETH_P_IP: 568e5b75505Sopenharmony_ci return fils_process_hlp_ip(hapd, sta, pos, pkt + 2, 569e5b75505Sopenharmony_ci end - pkt - 2); 570e5b75505Sopenharmony_ci } 571e5b75505Sopenharmony_ci 572e5b75505Sopenharmony_ci return 0; 573e5b75505Sopenharmony_ci} 574e5b75505Sopenharmony_ci 575e5b75505Sopenharmony_ci 576e5b75505Sopenharmony_ciint fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta, 577e5b75505Sopenharmony_ci const u8 *pos, int left) 578e5b75505Sopenharmony_ci{ 579e5b75505Sopenharmony_ci const u8 *end = pos + left; 580e5b75505Sopenharmony_ci u8 *tmp, *tmp_pos; 581e5b75505Sopenharmony_ci int ret = 0; 582e5b75505Sopenharmony_ci 583e5b75505Sopenharmony_ci if (sta->fils_pending_assoc_req && 584e5b75505Sopenharmony_ci eloop_is_timeout_registered(fils_hlp_timeout, hapd, sta)) { 585e5b75505Sopenharmony_ci /* Do not process FILS HLP request again if the station 586e5b75505Sopenharmony_ci * retransmits (Re)Association Request frame before the previous 587e5b75505Sopenharmony_ci * HLP response has either been received or timed out. */ 588e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 589e5b75505Sopenharmony_ci "FILS: Do not relay another HLP request from " 590e5b75505Sopenharmony_ci MACSTR 591e5b75505Sopenharmony_ci " before processing of the already pending one has been completed", 592e5b75505Sopenharmony_ci MAC2STR(sta->addr)); 593e5b75505Sopenharmony_ci return 1; 594e5b75505Sopenharmony_ci } 595e5b75505Sopenharmony_ci 596e5b75505Sopenharmony_ci /* Old DHCPDISCOVER is not needed anymore, if it was still pending */ 597e5b75505Sopenharmony_ci wpabuf_free(sta->hlp_dhcp_discover); 598e5b75505Sopenharmony_ci sta->hlp_dhcp_discover = NULL; 599e5b75505Sopenharmony_ci sta->fils_dhcp_rapid_commit_proxy = 0; 600e5b75505Sopenharmony_ci 601e5b75505Sopenharmony_ci /* Check if there are any FILS HLP Container elements */ 602e5b75505Sopenharmony_ci while (end - pos >= 2) { 603e5b75505Sopenharmony_ci if (2 + pos[1] > end - pos) 604e5b75505Sopenharmony_ci return 0; 605e5b75505Sopenharmony_ci if (pos[0] == WLAN_EID_EXTENSION && 606e5b75505Sopenharmony_ci pos[1] >= 1 + 2 * ETH_ALEN && 607e5b75505Sopenharmony_ci pos[2] == WLAN_EID_EXT_FILS_HLP_CONTAINER) 608e5b75505Sopenharmony_ci break; 609e5b75505Sopenharmony_ci pos += 2 + pos[1]; 610e5b75505Sopenharmony_ci } 611e5b75505Sopenharmony_ci if (end - pos < 2) 612e5b75505Sopenharmony_ci return 0; /* No FILS HLP Container elements */ 613e5b75505Sopenharmony_ci 614e5b75505Sopenharmony_ci tmp = os_malloc(end - pos); 615e5b75505Sopenharmony_ci if (!tmp) 616e5b75505Sopenharmony_ci return 0; 617e5b75505Sopenharmony_ci 618e5b75505Sopenharmony_ci while (end - pos >= 2) { 619e5b75505Sopenharmony_ci if (2 + pos[1] > end - pos || 620e5b75505Sopenharmony_ci pos[0] != WLAN_EID_EXTENSION || 621e5b75505Sopenharmony_ci pos[1] < 1 + 2 * ETH_ALEN || 622e5b75505Sopenharmony_ci pos[2] != WLAN_EID_EXT_FILS_HLP_CONTAINER) 623e5b75505Sopenharmony_ci break; 624e5b75505Sopenharmony_ci tmp_pos = tmp; 625e5b75505Sopenharmony_ci os_memcpy(tmp_pos, pos + 3, pos[1] - 1); 626e5b75505Sopenharmony_ci tmp_pos += pos[1] - 1; 627e5b75505Sopenharmony_ci pos += 2 + pos[1]; 628e5b75505Sopenharmony_ci 629e5b75505Sopenharmony_ci /* Add possible fragments */ 630e5b75505Sopenharmony_ci while (end - pos >= 2 && pos[0] == WLAN_EID_FRAGMENT && 631e5b75505Sopenharmony_ci 2 + pos[1] <= end - pos) { 632e5b75505Sopenharmony_ci os_memcpy(tmp_pos, pos + 2, pos[1]); 633e5b75505Sopenharmony_ci tmp_pos += pos[1]; 634e5b75505Sopenharmony_ci pos += 2 + pos[1]; 635e5b75505Sopenharmony_ci } 636e5b75505Sopenharmony_ci 637e5b75505Sopenharmony_ci if (fils_process_hlp_req(hapd, sta, tmp, tmp_pos - tmp) > 0) 638e5b75505Sopenharmony_ci ret = 1; 639e5b75505Sopenharmony_ci } 640e5b75505Sopenharmony_ci 641e5b75505Sopenharmony_ci os_free(tmp); 642e5b75505Sopenharmony_ci 643e5b75505Sopenharmony_ci return ret; 644e5b75505Sopenharmony_ci} 645e5b75505Sopenharmony_ci 646e5b75505Sopenharmony_ci 647e5b75505Sopenharmony_civoid fils_hlp_deinit(struct hostapd_data *hapd) 648e5b75505Sopenharmony_ci{ 649e5b75505Sopenharmony_ci if (hapd->dhcp_sock >= 0) { 650e5b75505Sopenharmony_ci eloop_unregister_read_sock(hapd->dhcp_sock); 651e5b75505Sopenharmony_ci close(hapd->dhcp_sock); 652e5b75505Sopenharmony_ci hapd->dhcp_sock = -1; 653e5b75505Sopenharmony_ci } 654e5b75505Sopenharmony_ci} 655