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