1/*-
2 * Copyright (c) 2005
3 *      Bill Paul <wpaul@windriver.com>.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *      This product includes software developed by Bill Paul.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
30 * THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD$");
35
36/*
37 * This program simulates the behavior of the ndis_events utility
38 * supplied with wpa_supplicant for Windows. The original utility
39 * is designed to translate Windows WMI events. We don't have WMI,
40 * but we need to supply certain event info to wpa_supplicant in
41 * order to make WPA2 work correctly, so we fake up the interface.
42 */
43
44#include <sys/types.h>
45#include <sys/param.h>
46#include <sys/socket.h>
47#include <sys/ioctl.h>
48#include <sys/errno.h>
49#include <sys/sysctl.h>
50#include <net/if.h>
51#include <net/if_dl.h>
52#include <net/if_var.h>
53
54#include <netinet/in.h>
55#include <arpa/inet.h>
56#include <netdb.h>
57#include <net/route.h>
58
59#include <stdio.h>
60#include <string.h>
61#include <stdlib.h>
62#include <unistd.h>
63#include <err.h>
64#include <syslog.h>
65#include <stdarg.h>
66
67static int verbose = 0;
68static int debug = 0;
69static int all_events = 0;
70
71#define PROGNAME "ndis_events"
72
73#define WPA_SUPPLICANT_PORT	9876
74#define NDIS_INDICATION_LEN	2048
75
76#define EVENT_CONNECT		0
77#define EVENT_DISCONNECT	1
78#define EVENT_MEDIA_SPECIFIC	2
79
80#define NDIS_STATUS_MEDIA_CONNECT		0x4001000B
81#define NDIS_STATUS_MEDIA_DISCONNECT		0x4001000C
82#define NDIS_STATUS_MEDIA_SPECIFIC_INDICATION	0x40010012
83
84struct ndis_evt {
85	uint32_t		ne_sts;
86	uint32_t		ne_len;
87#ifdef notdef
88	char			ne_buf[1];
89#endif
90};
91
92static int find_ifname(int, char *);
93static int announce_event(char *, int, struct sockaddr_in *);
94static void usage(void);
95
96static void
97dbgmsg(const char *fmt, ...)
98{
99	va_list			ap;
100
101	va_start(ap, fmt);
102	if (debug)
103		vwarnx(fmt, ap);
104	else
105		vsyslog(LOG_ERR, fmt, ap);
106	va_end(ap);
107
108	return;
109}
110
111static int
112find_ifname(idx, name)
113	int			idx;
114	char			*name;
115{
116	int			mib[6];
117	size_t			needed;
118	struct if_msghdr	*ifm;
119	struct sockaddr_dl	*sdl;
120	char			*buf, *lim, *next;
121
122	needed = 0;
123	mib[0] = CTL_NET;
124	mib[1] = PF_ROUTE;
125	mib[2] = 0;             /* protocol */
126	mib[3] = 0;             /* wildcard address family */
127	mib[4] = NET_RT_IFLIST;
128	mib[5] = 0;             /* no flags */
129
130	if (sysctl (mib, 6, NULL, &needed, NULL, 0) < 0)
131		return(EIO);
132
133	buf = malloc (needed);
134	if (buf == NULL)
135		return(ENOMEM);
136
137	if (sysctl (mib, 6, buf, &needed, NULL, 0) < 0) {
138		free(buf);
139		return(EIO);
140	}
141
142	lim = buf + needed;
143
144	next = buf;
145	while (next < lim) {
146		ifm = (struct if_msghdr *)next;
147		if (ifm->ifm_type == RTM_IFINFO) {
148			sdl = (struct sockaddr_dl *)(ifm + 1);
149			if (ifm->ifm_index == idx) {
150				strncpy(name, sdl->sdl_data, sdl->sdl_nlen);
151				name[sdl->sdl_nlen] = '\0';
152				free (buf);
153				return (0);
154			}
155		}
156		next += ifm->ifm_msglen;
157	}
158
159	free (buf);
160
161	return(ENOENT);
162}
163
164static int
165announce_event(ifname, sock, dst)
166	char			*ifname;
167	int			sock;
168	struct sockaddr_in	*dst;
169{
170	int			s;
171	char			indication[NDIS_INDICATION_LEN];
172        struct ifreq            ifr;
173	struct ndis_evt		*e;
174	char			buf[512], *pos, *end;
175	int			len, type, _type;
176
177	s = socket(PF_INET, SOCK_DGRAM, 0);
178
179	if (s < 0) {
180		dbgmsg("socket creation failed");
181                return(EINVAL);
182	}
183
184        bzero((char *)&ifr, sizeof(ifr));
185	e = (struct ndis_evt *)indication;
186	e->ne_len = NDIS_INDICATION_LEN - sizeof(struct ndis_evt);
187
188        strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
189        ifr.ifr_data = indication;
190
191	if (ioctl(s, SIOCGPRIVATE_0, &ifr) < 0) {
192		close(s);
193		if (verbose) {
194			if (errno == ENOENT)
195				dbgmsg("drained all events from %s",
196				    ifname, errno);
197			else
198				dbgmsg("failed to read event info from %s: %d",
199				    ifname, errno);
200		}
201		return(ENOENT);
202	}
203
204	if (e->ne_sts == NDIS_STATUS_MEDIA_CONNECT) {
205		type = EVENT_CONNECT;
206		if (verbose)
207			dbgmsg("Received a connect event for %s", ifname);
208		if (!all_events) {
209			close(s);
210			return(0);
211		}
212	}
213	if (e->ne_sts == NDIS_STATUS_MEDIA_DISCONNECT) {
214		type = EVENT_DISCONNECT;
215		if (verbose)
216			dbgmsg("Received a disconnect event for %s", ifname);
217		if (!all_events) {
218			close(s);
219			return(0);
220		}
221	}
222	if (e->ne_sts == NDIS_STATUS_MEDIA_SPECIFIC_INDICATION) {
223		type = EVENT_MEDIA_SPECIFIC;
224		if (verbose)
225			dbgmsg("Received a media-specific event for %s",
226			    ifname);
227	}
228
229	end = buf + sizeof(buf);
230	_type = (int) type;
231	memcpy(buf, &_type, sizeof(_type));
232	pos = buf + sizeof(_type);
233
234	len = snprintf(pos + 1, end - pos - 1, "%s", ifname);
235	if (len < 0) {
236		close(s);
237		return(ENOSPC);
238	}
239	if (len > 255)
240		len = 255;
241	*pos = (unsigned char) len;
242	pos += 1 + len;
243	if (e->ne_len) {
244		if (e->ne_len > 255 || 1 + e->ne_len > end - pos) {
245			dbgmsg("Not enough room for send_event data (%d)\n",
246			    e->ne_len);
247			close(s);
248			return(ENOSPC);
249 		}
250		*pos++ = (unsigned char) e->ne_len;
251		memcpy(pos, (indication) + sizeof(struct ndis_evt), e->ne_len);
252		pos += e->ne_len;
253	}
254
255	len = sendto(sock, buf, pos - buf, 0, (struct sockaddr *) dst,
256	    sizeof(struct sockaddr_in));
257
258	close(s);
259	return(0);
260}
261
262static void
263usage()
264{
265	fprintf(stderr, "Usage: ndis_events [-a] [-d] [-v]\n");
266	exit(1);
267}
268
269int
270main(argc, argv)
271	int			argc;
272	char			*argv[];
273{
274	int			s, r, n;
275	struct sockaddr_in	sin;
276	char			msg[NDIS_INDICATION_LEN];
277	struct rt_msghdr	*rtm;
278	struct if_msghdr	*ifm;
279	char			ifname[IFNAMSIZ];
280	int			ch;
281
282	while ((ch = getopt(argc, argv, "dva")) != -1) {
283		switch(ch) {
284		case 'd':
285			debug++;
286			break;
287		case 'v':
288			verbose++;
289			break;
290		case 'a':
291			all_events++;
292			break;
293		default:
294			usage();
295			break;
296		}
297	}
298
299	if (!debug && daemon(0, 0))
300		err(1, "failed to daemonize ourselves");
301
302	if (!debug)
303		openlog(PROGNAME, LOG_PID | LOG_CONS, LOG_DAEMON);
304
305	bzero((char *)&sin, sizeof(sin));
306
307	/* Create a datagram  socket. */
308
309	s = socket(PF_INET, SOCK_DGRAM, 0);
310	if (s < 0) {
311		dbgmsg("socket creation failed");
312		exit(1);
313	}
314
315	sin.sin_family = AF_INET;
316	sin.sin_addr.s_addr = inet_addr("127.0.0.1");
317	sin.sin_port = htons(WPA_SUPPLICANT_PORT);
318
319	/* Create a routing socket. */
320
321	r = socket (PF_ROUTE, SOCK_RAW, 0);
322	if (r < 0) {
323		dbgmsg("routing socket creation failed");
324		exit(1);
325	}
326
327	/* Now sit and spin, waiting for events. */
328
329	if (verbose)
330		dbgmsg("Listening for events");
331
332	while (1) {
333		n = read(r, msg, NDIS_INDICATION_LEN);
334		rtm = (struct rt_msghdr *)msg;
335		if (rtm->rtm_type != RTM_IFINFO)
336			continue;
337		ifm = (struct if_msghdr *)msg;
338		if (find_ifname(ifm->ifm_index, ifname))
339			continue;
340		if (strstr(ifname, "ndis")) {
341			while(announce_event(ifname, s, &sin) == 0)
342				;
343		} else {
344			if (verbose)
345				dbgmsg("Skipping ifinfo message from %s",
346				    ifname);
347		}
348	}
349
350	/* NOTREACHED */
351	exit(0);
352}
353