2 * FILS HLP request processing
3 * Copyright (c) 2017, Qualcomm Atheros, Inc.
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
9 #include "utils/includes.h"
11 #include "utils/common.h"
12 #include "utils/eloop.h"
13 #include "common/dhcp.h"
16 #include "ieee802_11.h"
20 static be16 ip_checksum(const void *buf, size_t len)
25 for (pos = buf; len >= 2; len -= 2)
28 sum += ntohs(*pos << 8);
30 sum = (sum >> 16) + (sum & 0xffff);
36 static int fils_dhcp_request(struct hostapd_data *hapd, struct sta_info *sta,
37 struct dhcp_data *dhcpoffer, u8 *dhcpofferend)
40 struct dhcp_data *dhcp;
41 struct sockaddr_in addr;
43 const u8 *server_id = NULL;
45 if (!sta->hlp_dhcp_discover) {
47 "FILS: No pending HLP DHCPDISCOVER available");
51 /* Convert to DHCPREQUEST, remove rapid commit option, replace requested
52 * IP address option with yiaddr. */
53 pos = wpabuf_mhead(sta->hlp_dhcp_discover);
54 end = pos + wpabuf_len(sta->hlp_dhcp_discover);
55 dhcp = (struct dhcp_data *) pos;
56 pos = (u8 *) (dhcp + 1);
57 pos += 4; /* skip magic */
58 while (pos < end && *pos != DHCP_OPT_END) {
62 if (opt == DHCP_OPT_PAD)
71 case DHCP_OPT_MSG_TYPE:
75 case DHCP_OPT_RAPID_COMMIT:
76 case DHCP_OPT_REQUESTED_IP_ADDRESS:
77 case DHCP_OPT_SERVER_ID:
80 os_memmove(pos, pos + 2 + olen, end - pos - 2 - olen);
87 if (pos >= end || *pos != DHCP_OPT_END) {
88 wpa_printf(MSG_DEBUG, "FILS: Could not update DHCPDISCOVER");
91 sta->hlp_dhcp_discover->used = pos - (u8 *) dhcp;
93 /* Copy Server ID option from DHCPOFFER to DHCPREQUEST */
94 pos = (u8 *) (dhcpoffer + 1);
96 pos += 4; /* skip magic */
97 while (pos < end && *pos != DHCP_OPT_END) {
101 if (opt == DHCP_OPT_PAD)
106 if (olen > end - pos)
110 case DHCP_OPT_SERVER_ID:
117 if (wpabuf_resize(&sta->hlp_dhcp_discover,
118 6 + 1 + (server_id ? 2 + server_id[1] : 0)))
121 wpabuf_put_data(sta->hlp_dhcp_discover, server_id,
123 wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_REQUESTED_IP_ADDRESS);
124 wpabuf_put_u8(sta->hlp_dhcp_discover, 4);
125 wpabuf_put_data(sta->hlp_dhcp_discover, &dhcpoffer->your_ip, 4);
126 wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_END);
128 os_memset(&addr, 0, sizeof(addr));
129 addr.sin_family = AF_INET;
130 addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr;
131 addr.sin_port = htons(hapd->conf->dhcp_server_port);
132 res = sendto(hapd->dhcp_sock, wpabuf_head(sta->hlp_dhcp_discover),
133 wpabuf_len(sta->hlp_dhcp_discover), 0,
134 (const struct sockaddr *) &addr, sizeof(addr));
136 wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s",
140 wpa_printf(MSG_DEBUG,
141 "FILS: Acting as DHCP rapid commit proxy for %s:%d",
142 inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
143 wpabuf_free(sta->hlp_dhcp_discover);
144 sta->hlp_dhcp_discover = NULL;
145 sta->fils_dhcp_rapid_commit_proxy = 1;
150 static void fils_dhcp_handler(int sd, void *eloop_ctx, void *sock_ctx)
152 struct hostapd_data *hapd = sock_ctx;
153 struct sta_info *sta;
154 u8 buf[1500], *pos, *end, *end_opt = NULL;
155 struct dhcp_data *dhcp;
156 struct sockaddr_in addr;
160 int rapid_commit = 0;
167 addr_len = sizeof(addr);
168 res = recvfrom(sd, buf, sizeof(buf), 0,
169 (struct sockaddr *) &addr, &addr_len);
171 wpa_printf(MSG_DEBUG, "FILS: DHCP read failed: %s",
175 wpa_printf(MSG_DEBUG, "FILS: DHCP response from server %s:%d (len=%d)",
176 inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), (int) res);
177 wpa_hexdump(MSG_MSGDUMP, "FILS: HLP - DHCP server response", buf, res);
178 if ((size_t) res < sizeof(*dhcp))
180 dhcp = (struct dhcp_data *) buf;
182 return; /* Not a BOOTREPLY */
183 if (dhcp->relay_ip != hapd->conf->own_ip_addr.u.v4.s_addr) {
184 wpa_printf(MSG_DEBUG,
185 "FILS: HLP - DHCP response to unknown relay address 0x%x",
190 pos = (u8 *) (dhcp + 1);
193 if (end - pos < 4 || WPA_GET_BE32(pos) != DHCP_MAGIC) {
194 wpa_printf(MSG_DEBUG, "FILS: HLP - no DHCP magic in response");
199 wpa_hexdump(MSG_DEBUG, "FILS: HLP - DHCP options in response",
201 while (pos < end && *pos != DHCP_OPT_END) {
205 if (opt == DHCP_OPT_PAD)
210 if (olen > end - pos)
214 case DHCP_OPT_MSG_TYPE:
218 case DHCP_OPT_RAPID_COMMIT:
224 if (pos < end && *pos == DHCP_OPT_END)
227 wpa_printf(MSG_DEBUG,
228 "FILS: HLP - DHCP message type %u (rapid_commit=%d hw_addr="
230 msgtype, rapid_commit, MAC2STR(dhcp->hw_addr));
232 sta = ap_get_sta(hapd, dhcp->hw_addr);
233 if (!sta || !sta->fils_pending_assoc_req) {
234 wpa_printf(MSG_DEBUG,
235 "FILS: No pending HLP DHCP exchange with hw_addr "
236 MACSTR, MAC2STR(dhcp->hw_addr));
240 if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPOFFER &&
242 /* Use hostapd to take care of 4-message exchange and convert
243 * the final DHCPACK to rapid commit version. */
244 if (fils_dhcp_request(hapd, sta, dhcp, end) == 0)
246 /* failed, so send the server response as-is */
247 } else if (msgtype != DHCPACK) {
248 wpa_printf(MSG_DEBUG,
249 "FILS: No DHCPACK available from the server and cannot do rapid commit proxying");
253 resp = wpabuf_alloc(2 * ETH_ALEN + 6 + 2 +
254 sizeof(*iph) + sizeof(*udph) + (end - pos) + 2);
257 wpabuf_put_data(resp, sta->addr, ETH_ALEN);
258 wpabuf_put_data(resp, hapd->own_addr, ETH_ALEN);
259 wpabuf_put_data(resp, "\xaa\xaa\x03\x00\x00\x00", 6);
260 wpabuf_put_be16(resp, ETH_P_IP);
261 iph = wpabuf_put(resp, sizeof(*iph));
263 iph->ihl = sizeof(*iph) / 4;
264 iph->tot_len = htons(sizeof(*iph) + sizeof(*udph) + (end - pos));
266 iph->protocol = 17; /* UDP */
267 iph->saddr = hapd->conf->dhcp_server.u.v4.s_addr;
268 iph->daddr = dhcp->client_ip;
269 iph->check = ip_checksum(iph, sizeof(*iph));
270 udph = wpabuf_put(resp, sizeof(*udph));
271 udph->uh_sport = htons(DHCP_SERVER_PORT);
272 udph->uh_dport = htons(DHCP_CLIENT_PORT);
273 udph->uh_ulen = htons(sizeof(*udph) + (end - pos));
274 udph->uh_sum = htons(0x0000); /* TODO: calculate checksum */
275 if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPACK &&
276 !rapid_commit && sta->fils_dhcp_rapid_commit_proxy && end_opt) {
277 /* Add rapid commit option */
278 wpabuf_put_data(resp, pos, end_opt - pos);
279 wpabuf_put_u8(resp, DHCP_OPT_RAPID_COMMIT);
280 wpabuf_put_u8(resp, 0);
281 wpabuf_put_data(resp, end_opt, end - end_opt);
283 wpabuf_put_data(resp, pos, end - pos);
285 if (wpabuf_resize(&sta->fils_hlp_resp, wpabuf_len(resp) +
286 2 * wpabuf_len(resp) / 255 + 100)) {
291 rpos = wpabuf_head(resp);
292 left = wpabuf_len(resp);
294 wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_EXTENSION); /* Element ID */
299 wpabuf_put_u8(sta->fils_hlp_resp, len); /* Length */
300 /* Element ID Extension */
301 wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_EXT_FILS_HLP_CONTAINER);
302 /* Destination MAC Address, Source MAC Address, HLP Packet.
303 * HLP Packet is in MSDU format (i.e., including the LLC/SNAP header
304 * when LPD is used). */
305 wpabuf_put_data(sta->fils_hlp_resp, rpos, len - 1);
309 wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_FRAGMENT);
310 len = left > 255 ? 255 : left;
311 wpabuf_put_u8(sta->fils_hlp_resp, len);
312 wpabuf_put_data(sta->fils_hlp_resp, rpos, len);
318 if (sta->fils_drv_assoc_finish)
319 hostapd_notify_assoc_fils_finish(hapd, sta);
321 fils_hlp_finish_assoc(hapd, sta);
325 static int fils_process_hlp_dhcp(struct hostapd_data *hapd,
326 struct sta_info *sta,
327 const u8 *msg, size_t len)
329 const struct dhcp_data *dhcp;
330 struct wpabuf *dhcp_buf;
331 struct dhcp_data *dhcp_msg;
333 int rapid_commit = 0;
334 const u8 *pos = msg, *end;
335 struct sockaddr_in addr;
338 if (len < sizeof(*dhcp))
340 dhcp = (const struct dhcp_data *) pos;
342 wpa_printf(MSG_DEBUG,
343 "FILS: HLP request DHCP: op=%u htype=%u hlen=%u hops=%u xid=0x%x",
344 dhcp->op, dhcp->htype, dhcp->hlen, dhcp->hops,
346 pos += sizeof(*dhcp);
348 return 0; /* Not a BOOTREQUEST */
352 if (WPA_GET_BE32(pos) != DHCP_MAGIC) {
353 wpa_printf(MSG_DEBUG, "FILS: HLP - no DHCP magic");
358 wpa_hexdump(MSG_DEBUG, "FILS: HLP - DHCP options", pos, end - pos);
359 while (pos < end && *pos != DHCP_OPT_END) {
363 if (opt == DHCP_OPT_PAD)
368 if (olen > end - pos)
372 case DHCP_OPT_MSG_TYPE:
376 case DHCP_OPT_RAPID_COMMIT:
383 wpa_printf(MSG_DEBUG, "FILS: HLP - DHCP message type %u", msgtype);
384 if (msgtype != DHCPDISCOVER)
387 if (hapd->conf->dhcp_server.af != AF_INET ||
388 hapd->conf->dhcp_server.u.v4.s_addr == 0) {
389 wpa_printf(MSG_DEBUG,
390 "FILS: HLP - no DHCPv4 server configured - drop request");
394 if (hapd->conf->own_ip_addr.af != AF_INET ||
395 hapd->conf->own_ip_addr.u.v4.s_addr == 0) {
396 wpa_printf(MSG_DEBUG,
397 "FILS: HLP - no IPv4 own_ip_addr configured - drop request");
401 if (hapd->dhcp_sock < 0) {
404 s = socket(AF_INET, SOCK_DGRAM, 0);
406 wpa_printf(MSG_ERROR,
407 "FILS: Failed to open DHCP socket: %s",
412 if (hapd->conf->dhcp_relay_port) {
413 os_memset(&addr, 0, sizeof(addr));
414 addr.sin_family = AF_INET;
415 addr.sin_addr.s_addr =
416 hapd->conf->own_ip_addr.u.v4.s_addr;
417 addr.sin_port = htons(hapd->conf->dhcp_relay_port);
418 if (bind(s, (struct sockaddr *) &addr, sizeof(addr))) {
419 wpa_printf(MSG_ERROR,
420 "FILS: Failed to bind DHCP socket: %s",
426 if (eloop_register_sock(s, EVENT_TYPE_READ,
427 fils_dhcp_handler, NULL, hapd)) {
435 dhcp_buf = wpabuf_alloc(len);
438 dhcp_msg = wpabuf_put(dhcp_buf, len);
439 os_memcpy(dhcp_msg, msg, len);
440 dhcp_msg->relay_ip = hapd->conf->own_ip_addr.u.v4.s_addr;
441 os_memset(&addr, 0, sizeof(addr));
442 addr.sin_family = AF_INET;
443 addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr;
444 addr.sin_port = htons(hapd->conf->dhcp_server_port);
445 res = sendto(hapd->dhcp_sock, dhcp_msg, len, 0,
446 (const struct sockaddr *) &addr, sizeof(addr));
448 wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s",
450 wpabuf_free(dhcp_buf);
451 /* Close the socket to try to recover from error */
452 eloop_unregister_read_sock(hapd->dhcp_sock);
453 close(hapd->dhcp_sock);
454 hapd->dhcp_sock = -1;
458 wpa_printf(MSG_DEBUG,
459 "FILS: HLP relayed DHCP request to server %s:%d (rapid_commit=%d)",
460 inet_ntoa(addr.sin_addr), ntohs(addr.sin_port),
462 if (hapd->conf->dhcp_rapid_commit_proxy && rapid_commit) {
463 /* Store a copy of the DHCPDISCOVER for rapid commit proxying
464 * purposes if the server does not support the rapid commit
466 wpa_printf(MSG_DEBUG,
467 "FILS: Store DHCPDISCOVER for rapid commit proxy");
468 wpabuf_free(sta->hlp_dhcp_discover);
469 sta->hlp_dhcp_discover = dhcp_buf;
471 wpabuf_free(dhcp_buf);
478 static int fils_process_hlp_udp(struct hostapd_data *hapd,
479 struct sta_info *sta, const u8 *dst,
480 const u8 *pos, size_t len)
482 const struct iphdr *iph;
483 const struct udphdr *udph;
484 u16 sport, dport, ulen;
486 if (len < sizeof(*iph) + sizeof(*udph))
488 iph = (const struct iphdr *) pos;
489 udph = (const struct udphdr *) (iph + 1);
490 sport = ntohs(udph->uh_sport);
491 dport = ntohs(udph->uh_dport);
492 ulen = ntohs(udph->uh_ulen);
493 wpa_printf(MSG_DEBUG,
494 "FILS: HLP request UDP: sport=%u dport=%u ulen=%u sum=0x%x",
495 sport, dport, ulen, ntohs(udph->uh_sum));
496 /* TODO: Check UDP checksum */
497 if (ulen < sizeof(*udph) || ulen > len - sizeof(*iph))
500 if (dport == DHCP_SERVER_PORT && sport == DHCP_CLIENT_PORT) {
501 return fils_process_hlp_dhcp(hapd, sta, (const u8 *) (udph + 1),
502 ulen - sizeof(*udph));
509 static int fils_process_hlp_ip(struct hostapd_data *hapd,
510 struct sta_info *sta, const u8 *dst,
511 const u8 *pos, size_t len)
513 const struct iphdr *iph;
516 if (len < sizeof(*iph))
518 iph = (const struct iphdr *) pos;
519 if (ip_checksum(iph, sizeof(*iph)) != 0) {
520 wpa_printf(MSG_DEBUG,
521 "FILS: HLP request IPv4 packet had invalid header checksum - dropped");
524 tot_len = ntohs(iph->tot_len);
527 wpa_printf(MSG_DEBUG,
528 "FILS: HLP request IPv4: saddr=%08x daddr=%08x protocol=%u",
529 iph->saddr, iph->daddr, iph->protocol);
530 switch (iph->protocol) {
532 return fils_process_hlp_udp(hapd, sta, dst, pos, len);
539 static int fils_process_hlp_req(struct hostapd_data *hapd,
540 struct sta_info *sta,
541 const u8 *pos, size_t len)
545 wpa_printf(MSG_DEBUG, "FILS: HLP request from " MACSTR " (dst=" MACSTR
546 " src=" MACSTR " len=%u)",
547 MAC2STR(sta->addr), MAC2STR(pos), MAC2STR(pos + ETH_ALEN),
549 if (os_memcmp(sta->addr, pos + ETH_ALEN, ETH_ALEN) != 0) {
550 wpa_printf(MSG_DEBUG,
551 "FILS: Ignore HLP request with unexpected source address"
552 MACSTR, MAC2STR(pos + ETH_ALEN));
557 pkt = pos + 2 * ETH_ALEN;
558 if (end - pkt >= 6 &&
559 os_memcmp(pkt, "\xaa\xaa\x03\x00\x00\x00", 6) == 0)
560 pkt += 6; /* Remove SNAP/LLC header */
561 wpa_hexdump(MSG_MSGDUMP, "FILS: HLP request packet", pkt, end - pkt);
566 switch (WPA_GET_BE16(pkt)) {
568 return fils_process_hlp_ip(hapd, sta, pos, pkt + 2,
576 int fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta,
577 const u8 *pos, int left)
579 const u8 *end = pos + left;
583 if (sta->fils_pending_assoc_req &&
584 eloop_is_timeout_registered(fils_hlp_timeout, hapd, sta)) {
585 /* Do not process FILS HLP request again if the station
586 * retransmits (Re)Association Request frame before the previous
587 * HLP response has either been received or timed out. */
588 wpa_printf(MSG_DEBUG,
589 "FILS: Do not relay another HLP request from "
591 " before processing of the already pending one has been completed",
596 /* Old DHCPDISCOVER is not needed anymore, if it was still pending */
597 wpabuf_free(sta->hlp_dhcp_discover);
598 sta->hlp_dhcp_discover = NULL;
599 sta->fils_dhcp_rapid_commit_proxy = 0;
601 /* Check if there are any FILS HLP Container elements */
602 while (end - pos >= 2) {
603 if (2 + pos[1] > end - pos)
605 if (pos[0] == WLAN_EID_EXTENSION &&
606 pos[1] >= 1 + 2 * ETH_ALEN &&
607 pos[2] == WLAN_EID_EXT_FILS_HLP_CONTAINER)
612 return 0; /* No FILS HLP Container elements */
614 tmp = os_malloc(end - pos);
618 while (end - pos >= 2) {
619 if (2 + pos[1] > end - pos ||
620 pos[0] != WLAN_EID_EXTENSION ||
621 pos[1] < 1 + 2 * ETH_ALEN ||
622 pos[2] != WLAN_EID_EXT_FILS_HLP_CONTAINER)
625 os_memcpy(tmp_pos, pos + 3, pos[1] - 1);
626 tmp_pos += pos[1] - 1;
629 /* Add possible fragments */
630 while (end - pos >= 2 && pos[0] == WLAN_EID_FRAGMENT &&
631 2 + pos[1] <= end - pos) {
632 os_memcpy(tmp_pos, pos + 2, pos[1]);
637 if (fils_process_hlp_req(hapd, sta, tmp, tmp_pos - tmp) > 0)
647 void fils_hlp_deinit(struct hostapd_data *hapd)
649 if (hapd->dhcp_sock >= 0) {
650 eloop_unregister_read_sock(hapd->dhcp_sock);
651 close(hapd->dhcp_sock);
652 hapd->dhcp_sock = -1;