1 /*
2  * IEEE 802.11 Common routines
3  * Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi>
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 "defs.h"
13 #include "ieee802_11_defs.h"
14 #include "ieee802_11_common.h"
15 #include "common/wpa_supplicant_i.h"
16 
17 /**
18  * get_ie - Fetch a specified information element from IEs buffer
19  * @ies: Information elements buffer
20  * @len: Information elements buffer length
21  * @eid: Information element identifier (WLAN_EID_*)
22  * Returns: Pointer to the information element (id field) or %NULL if not found
23  *
24  * This function returns the first matching information element in the IEs
25  * buffer or %NULL in case the element is not found.
26  */
get_ie(const u8 * ies,size_t len,u8 eid)27 const u8 * get_ie(const u8 *ies, size_t len, u8 eid)
28 {
29 	const struct element *elem;
30 
31 	if (!ies)
32 		return NULL;
33 
34 	for_each_element_id(elem, eid, ies, len)
35 		return &elem->id;
36 
37 	return NULL;
38 }
39 
get_vendor_ie(const u8 * ies,size_t len,u32 vendor_type)40 const u8 * get_vendor_ie(const u8 *ies, size_t len, u32 vendor_type)
41 {
42 	const struct element *elem;
43 
44 	for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, len) {
45 		if (elem->datalen >= 4 &&
46 				vendor_type == WPA_GET_BE32(elem->data))
47 			return &elem->id;
48 	}
49 
50 	return NULL;
51 }
52 
mbo_add_ie(u8 * buf,size_t len,const u8 * attr,size_t attr_len)53 size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len)
54 {
55 	/*
56 	 * MBO IE requires 6 bytes without the attributes: EID (1), length (1),
57 	 * OUI (3), OUI type (1).
58 	 */
59 	if (len < 6 + attr_len) {
60 		wpa_printf(MSG_DEBUG,
61 			   "MBO: Not enough room in buffer for MBO IE: buf len = %zu, attr_len = %zu",
62 			   len, attr_len);
63 		return 0;
64 	}
65 
66 	*buf++ = WLAN_EID_VENDOR_SPECIFIC;
67 	*buf++ = attr_len + 4;
68 	WPA_PUT_BE24(buf, OUI_WFA);
69 	buf += 3;
70 	*buf++ = MBO_OUI_TYPE;
71 	os_memcpy(buf, attr, attr_len);
72 
73 	return 6 + attr_len;
74 }
75 
ieee802_11_parse_candidate_list(const char * pos,u8 * nei_rep,size_t nei_rep_len)76 int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
77 				    size_t nei_rep_len)
78 {
79 	u8 *nei_pos = nei_rep;
80 	const char *end;
81 
82 	/*
83 	 * BSS Transition Candidate List Entries - Neighbor Report elements
84 	 * neighbor=<BSSID>,<BSSID Information>,<Operating Class>,
85 	 * <Channel Number>,<PHY Type>[,<hexdump of Optional Subelements>]
86 	 */
87 	while (pos) {
88 		u8 *nei_start;
89 		long int val;
90 		char *endptr, *tmp;
91 
92 		pos = os_strstr(pos, " neighbor=");
93 		if (!pos)
94 			break;
95 		if (nei_pos + 15 > nei_rep + nei_rep_len) {
96 			wpa_printf(MSG_DEBUG,
97 				   "Not enough room for additional neighbor");
98 			return -1;
99 		}
100 		pos += 10;
101 
102 		nei_start = nei_pos;
103 		*nei_pos++ = WLAN_EID_NEIGHBOR_REPORT;
104 		nei_pos++; /* length to be filled in */
105 
106 		if (hwaddr_aton2(pos, nei_pos) < 0) {
107 			wpa_printf(MSG_DEBUG, "Invalid BSSID");
108 			return -1;
109 		}
110 		nei_pos += ETH_ALEN;
111 		pos += 17;
112 		if (*pos != ',') {
113 			wpa_printf(MSG_DEBUG, "Missing BSSID Information");
114 			return -1;
115 		}
116 		pos++;
117 
118 		val = strtol(pos, &endptr, 0);
119 		WPA_PUT_LE32(nei_pos, val);
120 		nei_pos += 4;
121 		if (*endptr != ',') {
122 			wpa_printf(MSG_DEBUG, "Missing Operating Class");
123 			return -1;
124 		}
125 		pos = endptr + 1;
126 
127 		*nei_pos++ = atoi(pos); /* Operating Class */
128 		pos = os_strchr(pos, ',');
129 		if (pos == NULL) {
130 			wpa_printf(MSG_DEBUG, "Missing Channel Number");
131 			return -1;
132 		}
133 		pos++;
134 
135 		*nei_pos++ = atoi(pos); /* Channel Number */
136 		pos = os_strchr(pos, ',');
137 		if (pos == NULL) {
138 			wpa_printf(MSG_DEBUG, "Missing PHY Type");
139 			return -1;
140 		}
141 		pos++;
142 
143 		*nei_pos++ = atoi(pos); /* PHY Type */
144 		end = os_strchr(pos, ' ');
145 		tmp = os_strchr(pos, ',');
146 		if (tmp && (!end || tmp < end)) {
147 			/* Optional Subelements (hexdump) */
148 			size_t len;
149 
150 			pos = tmp + 1;
151 			end = os_strchr(pos, ' ');
152 			if (end)
153 				len = end - pos;
154 			else
155 				len = os_strlen(pos);
156 			if (nei_pos + len / 2 > nei_rep + nei_rep_len) {
157 				wpa_printf(MSG_DEBUG,
158 					   "Not enough room for neighbor subelements");
159 				return -1;
160 			}
161 			if (len & 0x01 ||
162 			    hexstr2bin(pos, nei_pos, len / 2) < 0) {
163 				wpa_printf(MSG_DEBUG,
164 					   "Invalid neighbor subelement info");
165 				return -1;
166 			}
167 			nei_pos += len / 2;
168 			pos = end;
169 		}
170 
171 		nei_start[1] = nei_pos - nei_start - 2;
172 	}
173 
174 	return nei_pos - nei_rep;
175 }
176 
177 /**
178  * ieee802_11_parse_elems - Parse information elements in management frames
179  * @start: Pointer to the start of IEs
180  * @len: Length of IE buffer in octets
181  * @elems: Data structure for parsed elements
182  * @show_errors: Whether to show parsing errors in debug log
183  * Returns: Parsing result
184  */
ieee802_11_parse_elems(struct wpa_supplicant * wpa_s,const u8 * start,size_t len)185 int ieee802_11_parse_elems(struct wpa_supplicant *wpa_s, const u8 *start, size_t len)
186 {
187 	const struct element *elem;
188 
189 	if (!start)
190 		return 0;
191 
192 	for_each_element(elem, start, len) {
193 		u8 id = elem->id;
194 		const u8 *pos = elem->data;
195 
196 		switch (id) {
197 		case WLAN_EID_RRM_ENABLED_CAPABILITIES:
198 			os_memcpy(wpa_s->rrm_ie, pos, 5);
199 			wpa_s->rrm.rrm_used = true;
200 			break;
201 		case WLAN_EID_EXT_CAPAB:
202 			/* extended caps can go beyond 8 octacts but we aren't using them now */
203 			os_memcpy(wpa_s->extend_caps, pos, 5);
204 			break;
205 		default:
206 			break;
207 
208 		}
209 	}
210 	return 0;
211 }
212 
ieee802_11_ext_capab(const u8 * ie,unsigned int capab)213 int ieee802_11_ext_capab(const u8 *ie, unsigned int capab)
214 {
215 	if (!ie || ie[1] <= capab / 8)
216 		return 0;
217 	return !!(ie[2 + capab / 8] & BIT(capab % 8));
218 }
219 
get_operating_class(u8 chan,int sec_channel)220 u8 get_operating_class(u8 chan, int sec_channel)
221 {
222 	u8 op_class;
223 
224 	if (chan < 1 || chan > 14)
225 		return 0;
226 	if (sec_channel == 1)
227 		op_class = 83;
228 	else if (sec_channel == -1)
229 		op_class = 84;
230 	else
231 		op_class = 81;
232 
233 	return op_class;
234 }
235