xref: /freebsd/contrib/wpa/src/ap/fils_hlp.c (revision 85732ac8bccbc0adcf5a261ea1ffec8ca7b3a92d)
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