xref: /openbsd/usr.sbin/hostapd/handle.c (revision a6445c1d)
1 /*	$OpenBSD: handle.c,v 1.11 2007/02/08 11:15:55 reyk Exp $	*/
2 
3 /*
4  * Copyright (c) 2005, 2006 Reyk Floeter <reyk@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/ioctl.h>
21 #include <sys/types.h>
22 #include <sys/socket.h>
23 #include <sys/time.h>
24 
25 #include <net/if.h>
26 #include <net/if_dl.h>
27 #include <net/if_media.h>
28 #include <net/if_arp.h>
29 #include <net/if_llc.h>
30 #include <net/bpf.h>
31 
32 #include <netinet/in.h>
33 #include <netinet/if_ether.h>
34 #include <arpa/inet.h>
35 
36 #include <net80211/ieee80211.h>
37 #include <net80211/ieee80211_radiotap.h>
38 
39 #include <fcntl.h>
40 #include <stdlib.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <unistd.h>
44 
45 #include "hostapd.h"
46 
47 int	 hostapd_handle_frame(struct hostapd_apme *, struct hostapd_frame *,
48 	    u_int8_t *, const u_int);
49 int	 hostapd_handle_action(struct hostapd_apme *, struct hostapd_frame *,
50 	    u_int8_t *, u_int8_t *, u_int8_t *, u_int8_t *, const u_int);
51 void	 hostapd_handle_addr(const u_int32_t, u_int32_t *, u_int8_t *,
52 	    u_int8_t *, struct hostapd_table *);
53 void	 hostapd_handle_ref(u_int, u_int, u_int8_t *, u_int8_t *, u_int8_t *,
54 	    u_int8_t *);
55 int	 hostapd_handle_radiotap(struct hostapd_radiotap *, u_int8_t *,
56 	    const u_int);
57 int	 hostapd_cmp(enum hostapd_op, int, int);
58 
59 int
60 hostapd_handle_input(struct hostapd_apme *apme, u_int8_t *buf, u_int len)
61 {
62 	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
63 	struct hostapd_frame *frame;
64 	int ret;
65 
66 	TAILQ_FOREACH(frame, &cfg->c_frames, f_entries) {
67 		if ((ret = hostapd_handle_frame(apme, frame, buf, len)) != 0)
68 			return (ret);
69 	}
70 
71 	return (0);
72 }
73 
74 void
75 hostapd_handle_addr(const u_int32_t mask, u_int32_t *flags, u_int8_t *addr,
76     u_int8_t *maddr, struct hostapd_table *table)
77 {
78 	int ret = 0;
79 
80 	if ((*flags & mask) & HOSTAPD_FRAME_TABLE) {
81 		if (hostapd_entry_lookup(table, addr) == NULL)
82 			ret = 1;
83 	} else if (bcmp(addr, maddr, IEEE80211_ADDR_LEN) != 0)
84 			ret = 1;
85 
86 	if ((ret == 1 && (*flags & mask) & HOSTAPD_FRAME_N) ||
87 	    (ret == 0 && ((*flags & mask) & HOSTAPD_FRAME_N) == 0))
88 		*flags &= ~mask;
89 }
90 
91 void
92 hostapd_handle_ref(u_int flags, u_int shift, u_int8_t *wfrom, u_int8_t *wto,
93     u_int8_t *wbssid, u_int8_t *addr)
94 {
95 	if (flags & (HOSTAPD_ACTION_F_REF_FROM << shift))
96 		bcopy(wfrom, addr, IEEE80211_ADDR_LEN);
97 	else if (flags & (HOSTAPD_ACTION_F_REF_TO << shift))
98 		bcopy(wto, addr, IEEE80211_ADDR_LEN);
99 	else if (flags & (HOSTAPD_ACTION_F_REF_BSSID << shift))
100 		bcopy(wbssid, addr, IEEE80211_ADDR_LEN);
101 	else if (flags & (HOSTAPD_ACTION_F_REF_RANDOM << shift)) {
102 		hostapd_randval(addr, IEEE80211_ADDR_LEN);
103 		/* Avoid multicast/broadcast addresses */
104 		addr[0] &= ~0x1;
105 	}
106 }
107 
108 int
109 hostapd_handle_frame(struct hostapd_apme *apme, struct hostapd_frame *frame,
110     u_int8_t *buf, const u_int len)
111 {
112 	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
113 	struct ieee80211_frame *wh;
114 	struct hostapd_ieee80211_frame *mh;
115 	struct hostapd_radiotap rtap;
116 	u_int8_t *wfrom, *wto, *wbssid;
117 	struct timeval t_now;
118 	u_int32_t flags;
119 	int offset, min_rate = 0, val;
120 
121 	if ((offset = hostapd_apme_offset(apme, buf, len)) < 0)
122 		return (0);
123 	wh = (struct ieee80211_frame *)(buf + offset);
124 
125 	mh = &frame->f_frame;
126 	flags = frame->f_flags;
127 
128 	/* Get timestamp */
129 	if (gettimeofday(&t_now, NULL) == -1)
130 		hostapd_fatal("gettimeofday");
131 
132 	/* Handle optional limit */
133 	if (frame->f_limit.tv_sec || frame->f_limit.tv_usec) {
134 		if (timercmp(&t_now, &frame->f_then, <))
135 			return (0);
136 		timeradd(&t_now, &frame->f_limit, &frame->f_then);
137 	}
138 
139 	switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
140 	case IEEE80211_FC1_DIR_NODS:
141 		wfrom = wh->i_addr2;
142 		wto = wh->i_addr1;
143 		wbssid = wh->i_addr3;
144 		break;
145 	case IEEE80211_FC1_DIR_TODS:
146 		wfrom = wh->i_addr2;
147 		wto = wh->i_addr3;
148 		wbssid = wh->i_addr1;
149 		break;
150 	case IEEE80211_FC1_DIR_FROMDS:
151 		wfrom = wh->i_addr3;
152 		wto = wh->i_addr1;
153 		wbssid = wh->i_addr2;
154 		break;
155 	default:
156 	case IEEE80211_FC1_DIR_DSTODS:
157 		return (0);
158 	}
159 
160 	if (flags & HOSTAPD_FRAME_F_APME_M) {
161 		if (frame->f_apme == NULL)
162 			return (0);
163 		/* Match hostap interface */
164 		if ((flags & HOSTAPD_FRAME_F_APME &&
165 		    apme == frame->f_apme) ||
166 		    (flags & HOSTAPD_FRAME_F_APME_N &&
167 		    apme != frame->f_apme))
168 			flags &= ~HOSTAPD_FRAME_F_APME_M;
169 	}
170 
171 	if (flags & HOSTAPD_FRAME_F_TYPE) {
172 		/* type $type */
173 		if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
174 		    (mh->i_fc[0] & IEEE80211_FC0_TYPE_MASK))
175 			flags &= ~HOSTAPD_FRAME_F_TYPE;
176 	} else if (flags & HOSTAPD_FRAME_F_TYPE_N) {
177 		/* type !$type */
178 		if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) !=
179 		    (mh->i_fc[0] & IEEE80211_FC0_TYPE_MASK))
180 			flags &= ~HOSTAPD_FRAME_F_TYPE_N;
181 	}
182 
183 	if (flags & HOSTAPD_FRAME_F_SUBTYPE) {
184 		/* subtype $subtype */
185 		if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
186 		    (mh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK))
187 			flags &= ~HOSTAPD_FRAME_F_SUBTYPE;
188 	} else if (flags & HOSTAPD_FRAME_F_SUBTYPE_N) {
189 		/* subtype !$subtype */
190 		if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) !=
191 		    (mh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK))
192 			flags &= ~HOSTAPD_FRAME_F_SUBTYPE_N;
193 	}
194 
195 	if (flags & HOSTAPD_FRAME_F_DIR) {
196 		/* dir $dir */
197 		if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) ==
198 		    (mh->i_fc[1] & IEEE80211_FC1_DIR_MASK))
199 			flags &= ~HOSTAPD_FRAME_F_DIR;
200 	} else if (flags & HOSTAPD_FRAME_F_DIR_N) {
201 		/* dir !$dir */
202 		if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) !=
203 		    (mh->i_fc[1] & IEEE80211_FC1_DIR_MASK))
204 			flags &= ~HOSTAPD_FRAME_F_DIR_N;
205 	}
206 
207 	/* from/to/bssid [!]$addr/<table> */
208 	hostapd_handle_addr(HOSTAPD_FRAME_F_FROM_M, &flags, wfrom,
209 	    mh->i_from, frame->f_from);
210 	hostapd_handle_addr(HOSTAPD_FRAME_F_TO_M, &flags, wto,
211 	    mh->i_to, frame->f_to);
212 	hostapd_handle_addr(HOSTAPD_FRAME_F_BSSID_M, &flags, wbssid,
213 	    mh->i_bssid, frame->f_bssid);
214 
215 	/* parse the optional radiotap header if required */
216 	if (frame->f_radiotap) {
217 		if (hostapd_handle_radiotap(&rtap, buf, len) != 0)
218 			return (0);
219 		else if ((rtap.r_present & frame->f_radiotap) !=
220 		    frame->f_radiotap) {
221 			cfg->c_stats.cn_rtap_miss++;
222 			return (0);
223 		}
224 		if (flags & HOSTAPD_FRAME_F_RSSI && rtap.r_max_rssi) {
225 			val = ((float)rtap.r_rssi / rtap.r_max_rssi) * 100;
226 			if (hostapd_cmp(frame->f_rssi_op,
227 			    val, frame->f_rssi))
228 				flags &= ~HOSTAPD_FRAME_F_RSSI;
229 		}
230 		if (flags & HOSTAPD_FRAME_F_RATE) {
231 			val = rtap.r_txrate;
232 			if (hostapd_cmp(frame->f_txrate_op,
233 			    val, frame->f_txrate))
234 				flags &= ~HOSTAPD_FRAME_F_RATE;
235 		}
236 		if (flags & HOSTAPD_FRAME_F_CHANNEL) {
237 			val = rtap.r_chan;
238 			if (hostapd_cmp(frame->f_chan_op,
239 			    val, frame->f_chan))
240 				flags &= ~HOSTAPD_FRAME_F_CHANNEL;
241 		}
242 	}
243 
244 	/* Handle if frame matches */
245 	if ((flags & HOSTAPD_FRAME_F_M) != 0)
246 		return (0);
247 
248 	/* Handle optional minimal rate */
249 	if (frame->f_rate && frame->f_rate_intval) {
250 		frame->f_rate_delay = t_now.tv_sec - frame->f_last.tv_sec;
251 		if (frame->f_rate_delay < frame->f_rate_intval) {
252 			frame->f_rate_cnt++;
253 			if (frame->f_rate_cnt < frame->f_rate)
254 				min_rate = 1;
255 		} else {
256 			min_rate = 1;
257 			frame->f_rate_cnt = 0;
258 		}
259 	}
260 
261 	/* Update timestamp for the last match of this event */
262 	if (frame->f_rate_cnt == 0 || min_rate == 0)
263 		bcopy(&t_now, &frame->f_last, sizeof(struct timeval));
264 
265 	/* Return if the minimal rate is not reached, yet */
266 	if (min_rate)
267 		return (0);
268 
269 	if (hostapd_handle_action(apme, frame, wfrom, wto, wbssid, buf,
270 	    len) != 0)
271 		return (0);
272 
273 	/* Reset minimal rate counter after successfully handled the frame */
274 	frame->f_rate_cnt = 0;
275 
276 	return ((frame->f_flags & HOSTAPD_FRAME_F_RET_M) >>
277 	    HOSTAPD_FRAME_F_RET_S);
278 }
279 
280 int
281 hostapd_handle_action(struct hostapd_apme *apme, struct hostapd_frame *frame,
282     u_int8_t *wfrom, u_int8_t *wto, u_int8_t *wbssid, u_int8_t *buf,
283     const u_int len)
284 {
285 	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
286 	struct hostapd_iapp *iapp = &cfg->c_iapp;
287 	struct hostapd_action_data *action = &frame->f_action_data;
288 	struct hostapd_node node;
289 	u_int8_t *lladdr = NULL;
290 	int ret = 0, offset;
291 
292 	switch (frame->f_action) {
293 	case HOSTAPD_ACTION_RADIOTAP:
294 		/* Send IAPP frame with radiotap/pcap payload */
295 		if ((ret = hostapd_iapp_radiotap(apme, buf, len)) != 0)
296 			return (ret);
297 
298 		if ((frame->f_action_flags & HOSTAPD_ACTION_VERBOSE) == 0)
299 			return (0);
300 
301 		hostapd_log(HOSTAPD_LOG,
302 		    "%s: sent IAPP frame HOSTAPD_%s (%u bytes)",
303 		    iapp->i_iface, cfg->c_apme_dlt ==
304 		    DLT_IEEE802_11_RADIO ? "RADIOTAP" : "PCAP", len);
305 		break;
306 
307 	case HOSTAPD_ACTION_LOG:
308 		/* Log frame to syslog/stderr */
309 		if (frame->f_rate && frame->f_rate_intval) {
310 			hostapd_printf("%s: (rate: %ld/%ld sec) ",
311 			    apme->a_iface, frame->f_rate_cnt,
312 			    frame->f_rate_delay + 1);
313 		} else
314 			hostapd_printf("%s: ", apme->a_iface);
315 
316 		hostapd_print_ieee80211(cfg->c_apme_dlt, frame->f_action_flags &
317 		    HOSTAPD_ACTION_VERBOSE, buf, len);
318 
319 		/* Flush output buffer */
320 		hostapd_printf(NULL);
321 		break;
322 
323 	case HOSTAPD_ACTION_DELNODE:
324 	case HOSTAPD_ACTION_ADDNODE:
325 		bzero(&node, sizeof(node));
326 
327 		if (action->a_flags & HOSTAPD_ACTION_F_REF_FROM)
328 			lladdr = wfrom;
329 		else if (action->a_flags & HOSTAPD_ACTION_F_REF_TO)
330 			lladdr = wto;
331 		else if (action->a_flags & HOSTAPD_ACTION_F_REF_BSSID)
332 			lladdr = wbssid;
333 		else
334 			lladdr = action->a_lladdr;
335 
336 		bcopy(lladdr, &node.ni_macaddr, IEEE80211_ADDR_LEN);
337 
338 		if (frame->f_action == HOSTAPD_ACTION_DELNODE)
339 			ret = hostapd_apme_delnode(apme, &node);
340 		else
341 			ret = hostapd_apme_addnode(apme, &node);
342 
343 		if (ret != 0)  {
344 			hostapd_log(HOSTAPD_LOG_DEBUG,
345 			    "%s: node add/delete %s failed: %s",
346 			    apme->a_iface, etheraddr_string(lladdr),
347 			    strerror(ret));
348 		}
349 		break;
350 
351 	case HOSTAPD_ACTION_NONE:
352 		/* Nothing */
353 		break;
354 
355 	case HOSTAPD_ACTION_RESEND:
356 		/* Resend received raw IEEE 802.11 frame */
357 		if ((offset = hostapd_apme_offset(apme, buf, len)) < 0)
358 			return (EINVAL);
359 		if (write(apme->a_raw, buf + offset, len - offset) == -1)
360 			ret = errno;
361 		break;
362 
363 	case HOSTAPD_ACTION_FRAME:
364 		if (action->a_flags & HOSTAPD_ACTION_F_REF_M) {
365 			hostapd_handle_ref(action->a_flags &
366 			    HOSTAPD_ACTION_F_REF_FROM_M,
367 			    HOSTAPD_ACTION_F_REF_FROM_S, wfrom, wto, wbssid,
368 			    action->a_frame.i_from);
369 			hostapd_handle_ref(action->a_flags &
370 			    HOSTAPD_ACTION_F_REF_TO_M,
371 			    HOSTAPD_ACTION_F_REF_TO_S, wfrom, wto, wbssid,
372 			    action->a_frame.i_to);
373 			hostapd_handle_ref(action->a_flags &
374 			    HOSTAPD_ACTION_F_REF_BSSID_M,
375 			    HOSTAPD_ACTION_F_REF_BSSID_S, wfrom, wto, wbssid,
376 			    action->a_frame.i_bssid);
377 		}
378 
379 		/* Send a raw IEEE 802.11 frame */
380 		return (hostapd_apme_output(apme, &action->a_frame));
381 
382 	default:
383 		return (0);
384 	}
385 
386 	return (ret);
387 }
388 
389 int
390 hostapd_handle_radiotap(struct hostapd_radiotap *rtap,
391     u_int8_t *buf, const u_int len)
392 {
393 	struct ieee80211_radiotap_header *rh =
394 	    (struct ieee80211_radiotap_header*)buf;
395 	u_int8_t *t, *ptr = NULL;
396 	u_int rh_len;
397 	const u_int8_t *snapend = buf + len;
398 
399 	TCHECK(*rh);
400 
401 	rh_len = letoh16(rh->it_len);
402 	if (rh->it_version != 0)
403 		return (EINVAL);
404 	if (len <= rh_len)
405 		goto trunc;
406 
407 	bzero(rtap, sizeof(struct hostapd_radiotap));
408 
409 	t = (u_int8_t*)buf + sizeof(struct ieee80211_radiotap_header);
410 	if ((rtap->r_present = letoh32(rh->it_present)) == 0)
411 		return (0);
412 
413 #define RADIOTAP(_x, _len)						\
414 	if (rtap->r_present & HOSTAPD_RADIOTAP_F(_x)) {			\
415 		TCHECK2(*t, _len);					\
416 		ptr = t;						\
417 		t += _len;						\
418 	} else								\
419 		ptr = NULL;
420 
421 	/* radiotap doesn't use TLV header fields, ugh */
422 	RADIOTAP(TSFT, 8);
423 	RADIOTAP(FLAGS, 1);
424 
425 	RADIOTAP(RATE, 1);
426 	if (ptr != NULL) {
427 		rtap->r_txrate = *(u_int8_t *)ptr;
428 	}
429 
430 	RADIOTAP(CHANNEL, 4);
431 	if (ptr != NULL) {
432 		rtap->r_chan = letoh16(*(u_int16_t*)ptr);
433 		rtap->r_chan_flags = letoh16(*(u_int16_t*)ptr + 1);
434 	}
435 
436 	RADIOTAP(FHSS, 2);
437 	RADIOTAP(DBM_ANTSIGNAL, 1);
438 	RADIOTAP(DBM_ANTNOISE, 1);
439 	RADIOTAP(LOCK_QUALITY, 2);
440 	RADIOTAP(TX_ATTENUATION, 2);
441 	RADIOTAP(DB_TX_ATTENUATION, 2);
442 	RADIOTAP(DBM_TX_POWER, 1);
443 	RADIOTAP(ANTENNA, 1);
444 	RADIOTAP(DB_ANTSIGNAL, 1);
445 	RADIOTAP(DB_ANTNOISE, 1);
446 	RADIOTAP(FCS, 4);
447 
448 	RADIOTAP(RSSI, 2);
449 	if (ptr != NULL) {
450 		rtap->r_rssi = *(u_int8_t *)ptr;
451 		rtap->r_max_rssi = *(u_int8_t *)ptr + 1;
452 	}
453 
454 	return (0);
455 
456  trunc:
457 	return (EINVAL);
458 }
459 
460 int
461 hostapd_cmp(enum hostapd_op op, int val1, int val2)
462 {
463 	if ((op == HOSTAPD_OP_EQ && val1 == val2) ||
464 	    (op == HOSTAPD_OP_NE && val1 != val2) ||
465 	    (op == HOSTAPD_OP_LE && val1 <= val2) ||
466 	    (op == HOSTAPD_OP_LT && val1 <  val2) ||
467 	    (op == HOSTAPD_OP_GE && val1 >= val2) ||
468 	    (op == HOSTAPD_OP_GT && val1 >  val2))
469 		return (1);
470 	return (0);
471 }
472