1/*	$OpenBSD: iapp.c,v 1.20 2019/05/10 01:29:31 guenther Exp $	*/
2
3/*
4 * Copyright (c) 2004, 2005 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/ioctl.h>
20#include <sys/types.h>
21#include <sys/socket.h>
22#include <sys/time.h>
23#include <sys/uio.h>
24
25#include <net/if.h>
26#include <net/if_media.h>
27#include <net/if_arp.h>
28#include <net/if_llc.h>
29#include <net/bpf.h>
30
31#include <netinet/in.h>
32#include <netinet/if_ether.h>
33#include <arpa/inet.h>
34
35#include <fcntl.h>
36#include <stdlib.h>
37#include <string.h>
38#include <unistd.h>
39#include <limits.h>
40
41#include "hostapd.h"
42#include "iapp.h"
43
44void
45hostapd_iapp_init(struct hostapd_config *cfg)
46{
47	struct hostapd_apme *apme;
48	struct hostapd_iapp *iapp = &cfg->c_iapp;
49
50	if ((cfg->c_flags & HOSTAPD_CFG_F_APME) == 0)
51		return;
52
53	TAILQ_FOREACH(apme, &cfg->c_apmes, a_entries) {
54		/* Get Host AP's BSSID */
55		hostapd_priv_apme_bssid(apme);
56		hostapd_log(HOSTAPD_LOG,
57		    "%s/%s: attached Host AP interface with BSSID %s",
58		    apme->a_iface, iapp->i_iface,
59		    etheraddr_string(apme->a_bssid));
60
61		/* Deauthenticate all stations on startup */
62		(void)hostapd_apme_deauth(apme);
63	}
64}
65
66void
67hostapd_iapp_term(struct hostapd_config *cfg)
68{
69	struct hostapd_apme *apme;
70	struct hostapd_iapp *iapp = &cfg->c_iapp;
71
72	if ((cfg->c_flags & HOSTAPD_CFG_F_APME) == 0)
73		return;
74
75	TAILQ_FOREACH(apme, &cfg->c_apmes, a_entries) {
76		hostapd_log(HOSTAPD_LOG_VERBOSE,
77		    "%s/%s: detaching from Host AP",
78		    apme->a_iface, iapp->i_iface);
79	}
80}
81
82int
83hostapd_iapp_add_notify(struct hostapd_apme *apme, struct hostapd_node *node)
84{
85	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
86	struct hostapd_iapp *iapp = &cfg->c_iapp;
87	struct sockaddr_in *addr;
88	struct {
89		struct ieee80211_iapp_frame hdr;
90		struct ieee80211_iapp_add_notify add;
91	} __packed frame;
92
93	if ((iapp->i_flags & HOSTAPD_IAPP_F_ADD_NOTIFY) == 0)
94		return (0);
95
96	/*
97	 * Send an ADD.notify message to other access points to notify
98	 * about a new association on our Host AP.
99	 */
100	bzero(&frame, sizeof(frame));
101
102	frame.hdr.i_version = IEEE80211_IAPP_VERSION;
103	frame.hdr.i_command = IEEE80211_IAPP_FRAME_ADD_NOTIFY;
104	frame.hdr.i_identifier = htons(iapp->i_cnt++);
105	frame.hdr.i_length = sizeof(struct ieee80211_iapp_add_notify);
106
107	frame.add.a_length = IEEE80211_ADDR_LEN;
108	frame.add.a_seqnum = htons(node->ni_rxseq);
109	bcopy(node->ni_macaddr, frame.add.a_macaddr, IEEE80211_ADDR_LEN);
110
111	if (cfg->c_flags & HOSTAPD_CFG_F_BRDCAST)
112		addr = &iapp->i_broadcast;
113	else
114		addr = &iapp->i_multicast;
115
116	if (sendto(iapp->i_udp, &frame, sizeof(frame),
117	    0, (struct sockaddr *)addr, sizeof(struct sockaddr_in)) == -1) {
118		hostapd_log(HOSTAPD_LOG,
119		    "%s: failed to send ADD notification: %s",
120		    iapp->i_iface, strerror(errno));
121		return (errno);
122	}
123
124	hostapd_log(HOSTAPD_LOG, "%s/%s: sent ADD notification for %s",
125	    apme->a_iface, iapp->i_iface,
126	    etheraddr_string(frame.add.a_macaddr));
127
128	/* Send a LLC XID frame, see llc.c for details */
129	return (hostapd_priv_llc_xid(cfg, node));
130}
131
132int
133hostapd_iapp_radiotap(struct hostapd_apme *apme, u_int8_t *buf,
134    const u_int len)
135{
136	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
137	struct hostapd_iapp *iapp = &cfg->c_iapp;
138	struct sockaddr_in *addr;
139	struct ieee80211_iapp_frame hdr;
140	struct msghdr msg;
141	struct iovec iov[2];
142
143	/*
144	 * Send an HOSTAPD.pcap/radiotap message to other access points
145	 * with an appended network dump. This is an hostapd extension to
146	 * IAPP.
147	 */
148	bzero(&hdr, sizeof(hdr));
149
150	hdr.i_version = IEEE80211_IAPP_VERSION;
151	if (cfg->c_apme_dlt == DLT_IEEE802_11_RADIO)
152		hdr.i_command = IEEE80211_IAPP_FRAME_HOSTAPD_RADIOTAP;
153	else if (cfg->c_apme_dlt == DLT_IEEE802_11)
154		hdr.i_command = IEEE80211_IAPP_FRAME_HOSTAPD_PCAP;
155	else
156		return (EINVAL);
157	hdr.i_identifier = htons(iapp->i_cnt++);
158	hdr.i_length = len;
159
160	if (cfg->c_flags & HOSTAPD_CFG_F_BRDCAST)
161		addr = &iapp->i_broadcast;
162	else
163		addr = &iapp->i_multicast;
164
165	iov[0].iov_base = &hdr;
166	iov[0].iov_len = sizeof(hdr);
167	iov[1].iov_base = buf;
168	iov[1].iov_len = len;
169	msg.msg_name = (caddr_t)addr;
170	msg.msg_namelen = sizeof(struct sockaddr_in);
171	msg.msg_iov = iov;
172	msg.msg_iovlen = 2;
173	msg.msg_control = 0;
174	msg.msg_controllen = 0;
175	msg.msg_flags = 0;
176
177	if (sendmsg(iapp->i_udp, &msg, 0) == -1) {
178		hostapd_log(HOSTAPD_LOG,
179		    "%s: failed to send HOSTAPD %s: %s",
180		    iapp->i_iface, cfg->c_apme_dlt ==
181		    DLT_IEEE802_11_RADIO ? "radiotap" : "pcap",
182		    strerror(errno));
183		return (errno);
184	}
185
186	return (0);
187}
188
189void
190hostapd_iapp_input(int fd, short sig, void *arg)
191{
192	struct hostapd_config *cfg = (struct hostapd_config *)arg;
193	struct hostapd_iapp *iapp = &cfg->c_iapp;
194	struct hostapd_apme *apme;
195	struct sockaddr_in addr;
196	socklen_t addr_len;
197	ssize_t len;
198	u_int8_t buf[IAPP_MAXSIZE];
199	struct hostapd_node node;
200	struct ieee80211_iapp_recv {
201		struct ieee80211_iapp_frame hdr;
202		union {
203			struct ieee80211_iapp_add_notify add;
204			u_int8_t buf[1];
205		} u;
206	} __packed *frame;
207	u_int dlt;
208	int ret = 0;
209
210	/* Ignore invalid signals */
211	if (sig != EV_READ)
212		return;
213
214	/*
215	 * Listen to possible messages from other IAPP
216	 */
217	bzero(buf, sizeof(buf));
218
219	if ((len = recvfrom(fd, buf, sizeof(buf), 0,
220	    (struct sockaddr*)&addr, &addr_len)) < 1)
221		return;
222
223	if (bcmp(&iapp->i_addr.sin_addr, &addr.sin_addr,
224	    sizeof(addr.sin_addr)) == 0)
225		return;
226
227	frame = (struct ieee80211_iapp_recv*)buf;
228
229	/* Validate the IAPP version */
230	if (len < (ssize_t)sizeof(struct ieee80211_iapp_frame) ||
231	    frame->hdr.i_version != IEEE80211_IAPP_VERSION ||
232	    addr_len < sizeof(struct sockaddr_in))
233		return;
234
235	cfg->c_stats.cn_rx_iapp++;
236
237	/*
238	 * Process the IAPP frame
239	 */
240	switch (frame->hdr.i_command) {
241	case IEEE80211_IAPP_FRAME_ADD_NOTIFY:
242		/* Short frame */
243		if (len < (ssize_t)(sizeof(struct ieee80211_iapp_frame) +
244		    sizeof(struct ieee80211_iapp_add_notify)))
245			return;
246
247		/* Don't support non-48bit MAC addresses, yet */
248		if (frame->u.add.a_length != IEEE80211_ADDR_LEN)
249			return;
250
251		node.ni_rxseq = frame->u.add.a_seqnum;
252		bcopy(frame->u.add.a_macaddr, node.ni_macaddr,
253		    IEEE80211_ADDR_LEN);
254
255		/*
256		 * Try to remove a node from our Host AP and to free
257		 * any allocated resources. Otherwise the received
258		 * ADD.notify message will be ignored.
259		 */
260		if (iapp->i_flags & HOSTAPD_IAPP_F_ADD &&
261		    cfg->c_flags & HOSTAPD_CFG_F_APME) {
262			TAILQ_FOREACH(apme, &cfg->c_apmes, a_entries) {
263				if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING)
264					(void)hostapd_roaming_del(apme, &node);
265				if (iapp->i_flags & HOSTAPD_IAPP_F_ADD_NOTIFY &&
266				    (ret = hostapd_apme_delnode(apme,
267				    &node)) == 0)
268					cfg->c_stats.cn_tx_apme++;
269			}
270		} else
271			ret = 0;
272
273		hostapd_log(iapp->i_flags & HOSTAPD_IAPP_F_ADD ?
274		    HOSTAPD_LOG : HOSTAPD_LOG_VERBOSE,
275		    "%s: %s ADD notification for %s at %s",
276		    iapp->i_iface, ret == 0 ?
277		    "received" : "ignored",
278		    etheraddr_string(node.ni_macaddr),
279		    inet_ntoa(addr.sin_addr));
280		break;
281
282	case IEEE80211_IAPP_FRAME_HOSTAPD_PCAP:
283	case IEEE80211_IAPP_FRAME_HOSTAPD_RADIOTAP:
284		if ((iapp->i_flags & HOSTAPD_IAPP_F_RADIOTAP) == 0)
285			return;
286
287		/* Short frame */
288		if (len <= (ssize_t)sizeof(struct ieee80211_iapp_frame) ||
289		    frame->hdr.i_length < sizeof(struct ieee80211_frame))
290			return;
291
292		dlt = frame->hdr.i_command ==
293		    IEEE80211_IAPP_FRAME_HOSTAPD_PCAP ?
294		    DLT_IEEE802_11 : DLT_IEEE802_11_RADIO;
295
296		hostapd_print_ieee80211(dlt, 1, (u_int8_t *)frame->u.buf,
297		    len - sizeof(struct ieee80211_iapp_frame));
298		return;
299
300	case IEEE80211_IAPP_FRAME_MOVE_NOTIFY:
301	case IEEE80211_IAPP_FRAME_MOVE_RESPONSE:
302	case IEEE80211_IAPP_FRAME_SEND_SECURITY_BLOCK:
303	case IEEE80211_IAPP_FRAME_ACK_SECURITY_BLOCK:
304	case IEEE80211_IAPP_FRAME_CACHE_NOTIFY:
305	case IEEE80211_IAPP_FRAME_CACHE_RESPONSE:
306
307		/*
308		 * XXX TODO
309		 */
310
311		hostapd_log(HOSTAPD_LOG_VERBOSE,
312		    "%s: received unsupported IAPP message %d",
313		    iapp->i_iface, frame->hdr.i_command);
314		return;
315
316	default:
317		return;
318	}
319}
320