1151214Swpaul/*- 2151214Swpaul * Copyright (c) 2005 3151214Swpaul * Bill Paul <wpaul@windriver.com>. All rights reserved. 4151214Swpaul * 5151214Swpaul * Redistribution and use in source and binary forms, with or without 6151214Swpaul * modification, are permitted provided that the following conditions 7151214Swpaul * are met: 8151214Swpaul * 1. Redistributions of source code must retain the above copyright 9151214Swpaul * notice, this list of conditions and the following disclaimer. 10151214Swpaul * 2. Redistributions in binary form must reproduce the above copyright 11151214Swpaul * notice, this list of conditions and the following disclaimer in the 12151214Swpaul * documentation and/or other materials provided with the distribution. 13151214Swpaul * 3. All advertising materials mentioning features or use of this software 14151214Swpaul * must display the following acknowledgement: 15151214Swpaul * This product includes software developed by Bill Paul. 16151214Swpaul * 4. Neither the name of the author nor the names of any co-contributors 17151214Swpaul * may be used to endorse or promote products derived from this software 18151214Swpaul * without specific prior written permission. 19151214Swpaul * 20151214Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 21151214Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22151214Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23151214Swpaul * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 24151214Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25151214Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26151214Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27151214Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28151214Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29151214Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 30151214Swpaul * THE POSSIBILITY OF SUCH DAMAGE. 31151214Swpaul */ 32151214Swpaul 33151214Swpaul#include <sys/cdefs.h> 34151214Swpaul__FBSDID("$FreeBSD$"); 35151214Swpaul 36151214Swpaul/* 37151214Swpaul * This program simulates the behavior of the ndis_events utility 38151214Swpaul * supplied with wpa_supplicant for Windows. The original utility 39151214Swpaul * is designed to translate Windows WMI events. We don't have WMI, 40151214Swpaul * but we need to supply certain event info to wpa_supplicant in 41151214Swpaul * order to make WPA2 work correctly, so we fake up the interface. 42151214Swpaul */ 43151214Swpaul 44151214Swpaul#include <sys/types.h> 45151214Swpaul#include <sys/param.h> 46151214Swpaul#include <sys/socket.h> 47151214Swpaul#include <sys/ioctl.h> 48151214Swpaul#include <sys/errno.h> 49151214Swpaul#include <sys/sysctl.h> 50151214Swpaul#include <net/if.h> 51151214Swpaul#include <net/if_dl.h> 52151214Swpaul#include <net/if_var.h> 53151214Swpaul 54151214Swpaul#include <netinet/in.h> 55151214Swpaul#include <arpa/inet.h> 56151214Swpaul#include <netdb.h> 57151214Swpaul#include <net/route.h> 58151214Swpaul 59151214Swpaul#include <stdio.h> 60151214Swpaul#include <string.h> 61151214Swpaul#include <stdlib.h> 62151214Swpaul#include <unistd.h> 63151214Swpaul#include <err.h> 64151214Swpaul#include <syslog.h> 65151214Swpaul#include <stdarg.h> 66151214Swpaul 67151214Swpaulstatic int verbose = 0; 68151214Swpaulstatic int debug = 0; 69151223Swpaulstatic int all_events = 0; 70151214Swpaul 71151214Swpaul#define PROGNAME "ndis_events" 72151214Swpaul 73151214Swpaul#define WPA_SUPPLICANT_PORT 9876 74151214Swpaul#define NDIS_INDICATION_LEN 2048 75151214Swpaul 76151214Swpaul#define EVENT_CONNECT 0 77151214Swpaul#define EVENT_DISCONNECT 1 78151214Swpaul#define EVENT_MEDIA_SPECIFIC 2 79151214Swpaul 80151214Swpaul#define NDIS_STATUS_MEDIA_CONNECT 0x4001000B 81151214Swpaul#define NDIS_STATUS_MEDIA_DISCONNECT 0x4001000C 82151214Swpaul#define NDIS_STATUS_MEDIA_SPECIFIC_INDICATION 0x40010012 83151214Swpaul 84151214Swpaulstruct ndis_evt { 85151214Swpaul uint32_t ne_sts; 86151214Swpaul uint32_t ne_len; 87151214Swpaul#ifdef notdef 88151214Swpaul char ne_buf[1]; 89151214Swpaul#endif 90151214Swpaul}; 91151214Swpaul 92151214Swpaulstatic int find_ifname(int, char *); 93151246Swpaulstatic int announce_event(char *, int, struct sockaddr_in *); 94194686Smaximstatic void usage(void); 95151214Swpaul 96151214Swpaulstatic void 97151214Swpauldbgmsg(const char *fmt, ...) 98151214Swpaul{ 99151214Swpaul va_list ap; 100151214Swpaul 101151214Swpaul va_start(ap, fmt); 102151214Swpaul if (debug) 103151214Swpaul vwarnx(fmt, ap); 104151214Swpaul else 105151246Swpaul vsyslog(LOG_ERR, fmt, ap); 106151214Swpaul va_end(ap); 107151214Swpaul 108151214Swpaul return; 109151214Swpaul} 110151214Swpaul 111151214Swpaulstatic int 112151214Swpaulfind_ifname(idx, name) 113151214Swpaul int idx; 114151214Swpaul char *name; 115151214Swpaul{ 116151214Swpaul int mib[6]; 117151214Swpaul size_t needed; 118151214Swpaul struct if_msghdr *ifm; 119151214Swpaul struct sockaddr_dl *sdl; 120151214Swpaul char *buf, *lim, *next; 121151214Swpaul 122151214Swpaul needed = 0; 123151214Swpaul mib[0] = CTL_NET; 124151214Swpaul mib[1] = PF_ROUTE; 125151214Swpaul mib[2] = 0; /* protocol */ 126151214Swpaul mib[3] = 0; /* wildcard address family */ 127151214Swpaul mib[4] = NET_RT_IFLIST; 128151214Swpaul mib[5] = 0; /* no flags */ 129151214Swpaul 130151214Swpaul if (sysctl (mib, 6, NULL, &needed, NULL, 0) < 0) 131151214Swpaul return(EIO); 132151214Swpaul 133151214Swpaul buf = malloc (needed); 134151214Swpaul if (buf == NULL) 135151214Swpaul return(ENOMEM); 136151214Swpaul 137151214Swpaul if (sysctl (mib, 6, buf, &needed, NULL, 0) < 0) { 138151214Swpaul free(buf); 139151214Swpaul return(EIO); 140151214Swpaul } 141151214Swpaul 142151214Swpaul lim = buf + needed; 143151214Swpaul 144151214Swpaul next = buf; 145151214Swpaul while (next < lim) { 146151214Swpaul ifm = (struct if_msghdr *)next; 147151214Swpaul if (ifm->ifm_type == RTM_IFINFO) { 148151214Swpaul sdl = (struct sockaddr_dl *)(ifm + 1); 149151214Swpaul if (ifm->ifm_index == idx) { 150151214Swpaul strncpy(name, sdl->sdl_data, sdl->sdl_nlen); 151151214Swpaul name[sdl->sdl_nlen] = '\0'; 152151214Swpaul free (buf); 153151214Swpaul return (0); 154151214Swpaul } 155151214Swpaul } 156151214Swpaul next += ifm->ifm_msglen; 157151214Swpaul } 158151214Swpaul 159151214Swpaul free (buf); 160151214Swpaul 161151214Swpaul return(ENOENT); 162151214Swpaul} 163151214Swpaul 164151246Swpaulstatic int 165151214Swpaulannounce_event(ifname, sock, dst) 166151214Swpaul char *ifname; 167151214Swpaul int sock; 168151214Swpaul struct sockaddr_in *dst; 169151214Swpaul{ 170151214Swpaul int s; 171151214Swpaul char indication[NDIS_INDICATION_LEN]; 172151214Swpaul struct ifreq ifr; 173151214Swpaul struct ndis_evt *e; 174151214Swpaul char buf[512], *pos, *end; 175151214Swpaul int len, type, _type; 176151214Swpaul 177151214Swpaul s = socket(PF_INET, SOCK_DGRAM, 0); 178151214Swpaul 179151246Swpaul if (s < 0) { 180151246Swpaul dbgmsg("socket creation failed"); 181151246Swpaul return(EINVAL); 182151246Swpaul } 183151214Swpaul 184151214Swpaul bzero((char *)&ifr, sizeof(ifr)); 185151214Swpaul e = (struct ndis_evt *)indication; 186151214Swpaul e->ne_len = NDIS_INDICATION_LEN - sizeof(struct ndis_evt); 187151214Swpaul 188151214Swpaul strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); 189151214Swpaul ifr.ifr_data = indication; 190151214Swpaul 191151214Swpaul if (ioctl(s, SIOCGPRIVATE_0, &ifr) < 0) { 192151214Swpaul close(s); 193151541Swpaul if (verbose) { 194151541Swpaul if (errno == ENOENT) 195151541Swpaul dbgmsg("drained all events from %s", 196151541Swpaul ifname, errno); 197151541Swpaul else 198151541Swpaul dbgmsg("failed to read event info from %s: %d", 199151541Swpaul ifname, errno); 200151541Swpaul } 201151246Swpaul return(ENOENT); 202151214Swpaul } 203151214Swpaul 204151214Swpaul if (e->ne_sts == NDIS_STATUS_MEDIA_CONNECT) { 205151214Swpaul type = EVENT_CONNECT; 206151214Swpaul if (verbose) 207151214Swpaul dbgmsg("Received a connect event for %s", ifname); 208151246Swpaul if (!all_events) { 209151246Swpaul close(s); 210151246Swpaul return(0); 211151246Swpaul } 212151214Swpaul } 213151214Swpaul if (e->ne_sts == NDIS_STATUS_MEDIA_DISCONNECT) { 214151214Swpaul type = EVENT_DISCONNECT; 215151214Swpaul if (verbose) 216151214Swpaul dbgmsg("Received a disconnect event for %s", ifname); 217151246Swpaul if (!all_events) { 218151246Swpaul close(s); 219151246Swpaul return(0); 220151246Swpaul } 221151214Swpaul } 222151214Swpaul if (e->ne_sts == NDIS_STATUS_MEDIA_SPECIFIC_INDICATION) { 223151214Swpaul type = EVENT_MEDIA_SPECIFIC; 224151214Swpaul if (verbose) 225151214Swpaul dbgmsg("Received a media-specific event for %s", 226151214Swpaul ifname); 227151214Swpaul } 228151214Swpaul 229151214Swpaul end = buf + sizeof(buf); 230151214Swpaul _type = (int) type; 231151214Swpaul memcpy(buf, &_type, sizeof(_type)); 232151214Swpaul pos = buf + sizeof(_type); 233151214Swpaul 234151214Swpaul len = snprintf(pos + 1, end - pos - 1, "%s", ifname); 235151246Swpaul if (len < 0) { 236151246Swpaul close(s); 237151246Swpaul return(ENOSPC); 238151246Swpaul } 239151214Swpaul if (len > 255) 240151214Swpaul len = 255; 241151214Swpaul *pos = (unsigned char) len; 242151214Swpaul pos += 1 + len; 243151214Swpaul if (e->ne_len) { 244151214Swpaul if (e->ne_len > 255 || 1 + e->ne_len > end - pos) { 245151214Swpaul dbgmsg("Not enough room for send_event data (%d)\n", 246151214Swpaul e->ne_len); 247151246Swpaul close(s); 248151246Swpaul return(ENOSPC); 249151214Swpaul } 250151214Swpaul *pos++ = (unsigned char) e->ne_len; 251151214Swpaul memcpy(pos, (indication) + sizeof(struct ndis_evt), e->ne_len); 252151214Swpaul pos += e->ne_len; 253151214Swpaul } 254151214Swpaul 255151214Swpaul len = sendto(sock, buf, pos - buf, 0, (struct sockaddr *) dst, 256151214Swpaul sizeof(struct sockaddr_in)); 257151214Swpaul 258151214Swpaul close(s); 259151246Swpaul return(0); 260151214Swpaul} 261151214Swpaul 262151214Swpaulstatic void 263194681Smaximusage() 264151214Swpaul{ 265194680Smaxim fprintf(stderr, "Usage: ndis_events [-a] [-d] [-v]\n"); 266151214Swpaul exit(1); 267151214Swpaul} 268151214Swpaul 269151214Swpaulint 270151214Swpaulmain(argc, argv) 271151214Swpaul int argc; 272151214Swpaul char *argv[]; 273151214Swpaul{ 274151214Swpaul int s, r, n; 275151214Swpaul struct sockaddr_in sin; 276151214Swpaul char msg[NDIS_INDICATION_LEN]; 277151214Swpaul struct rt_msghdr *rtm; 278151214Swpaul struct if_msghdr *ifm; 279151214Swpaul char ifname[IFNAMSIZ]; 280151214Swpaul int ch; 281151214Swpaul 282151223Swpaul while ((ch = getopt(argc, argv, "dva")) != -1) { 283151214Swpaul switch(ch) { 284151214Swpaul case 'd': 285151214Swpaul debug++; 286151214Swpaul break; 287151214Swpaul case 'v': 288151214Swpaul verbose++; 289151214Swpaul break; 290151223Swpaul case 'a': 291151223Swpaul all_events++; 292151223Swpaul break; 293151214Swpaul default: 294194686Smaxim usage(); 295151214Swpaul break; 296151214Swpaul } 297151214Swpaul } 298151214Swpaul 299151214Swpaul if (!debug && daemon(0, 0)) 300151214Swpaul err(1, "failed to daemonize ourselves"); 301151214Swpaul 302151214Swpaul if (!debug) 303151214Swpaul openlog(PROGNAME, LOG_PID | LOG_CONS, LOG_DAEMON); 304151214Swpaul 305151214Swpaul bzero((char *)&sin, sizeof(sin)); 306151214Swpaul 307151214Swpaul /* Create a datagram socket. */ 308151214Swpaul 309151214Swpaul s = socket(PF_INET, SOCK_DGRAM, 0); 310151214Swpaul if (s < 0) { 311151214Swpaul dbgmsg("socket creation failed"); 312151214Swpaul exit(1); 313151214Swpaul } 314151214Swpaul 315151214Swpaul sin.sin_family = AF_INET; 316151214Swpaul sin.sin_addr.s_addr = inet_addr("127.0.0.1"); 317151214Swpaul sin.sin_port = htons(WPA_SUPPLICANT_PORT); 318151214Swpaul 319151214Swpaul /* Create a routing socket. */ 320151214Swpaul 321151214Swpaul r = socket (PF_ROUTE, SOCK_RAW, 0); 322151214Swpaul if (r < 0) { 323151214Swpaul dbgmsg("routing socket creation failed"); 324151214Swpaul exit(1); 325151214Swpaul } 326151214Swpaul 327151214Swpaul /* Now sit and spin, waiting for events. */ 328151214Swpaul 329151214Swpaul if (verbose) 330151214Swpaul dbgmsg("Listening for events"); 331151214Swpaul 332151214Swpaul while (1) { 333151214Swpaul n = read(r, msg, NDIS_INDICATION_LEN); 334151214Swpaul rtm = (struct rt_msghdr *)msg; 335151214Swpaul if (rtm->rtm_type != RTM_IFINFO) 336151214Swpaul continue; 337151214Swpaul ifm = (struct if_msghdr *)msg; 338151214Swpaul if (find_ifname(ifm->ifm_index, ifname)) 339151214Swpaul continue; 340151246Swpaul if (strstr(ifname, "ndis")) { 341151246Swpaul while(announce_event(ifname, s, &sin) == 0) 342151246Swpaul ; 343151246Swpaul } else { 344151214Swpaul if (verbose) 345151214Swpaul dbgmsg("Skipping ifinfo message from %s", 346151214Swpaul ifname); 347151214Swpaul } 348151214Swpaul } 349151214Swpaul 350151214Swpaul /* NOTREACHED */ 351151214Swpaul exit(0); 352151214Swpaul} 353