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