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