ndis_events.c revision 151246
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: head/usr.sbin/wpa/ndis_events/ndis_events.c 151246 2005-10-12 00:57:31Z wpaul $"); 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/cdefs.h> 46#include <sys/param.h> 47#include <sys/socket.h> 48#include <sys/ioctl.h> 49#include <sys/socket.h> 50#include <sys/errno.h> 51#include <sys/sysctl.h> 52#include <net/if.h> 53#include <net/if_dl.h> 54#include <net/if_var.h> 55 56#include <netinet/in.h> 57#include <arpa/inet.h> 58#include <netdb.h> 59#include <net/route.h> 60 61#include <stdio.h> 62#include <string.h> 63#include <stdlib.h> 64#include <unistd.h> 65#include <err.h> 66#include <syslog.h> 67#include <stdarg.h> 68 69static int verbose = 0; 70static int debug = 0; 71static int all_events = 0; 72 73#define PROGNAME "ndis_events" 74 75#define WPA_SUPPLICANT_PORT 9876 76#define NDIS_INDICATION_LEN 2048 77 78#define EVENT_CONNECT 0 79#define EVENT_DISCONNECT 1 80#define EVENT_MEDIA_SPECIFIC 2 81 82#define NDIS_STATUS_MEDIA_CONNECT 0x4001000B 83#define NDIS_STATUS_MEDIA_DISCONNECT 0x4001000C 84#define NDIS_STATUS_MEDIA_SPECIFIC_INDICATION 0x40010012 85 86struct ndis_evt { 87 uint32_t ne_sts; 88 uint32_t ne_len; 89#ifdef notdef 90 char ne_buf[1]; 91#endif 92}; 93 94static int find_ifname(int, char *); 95static int announce_event(char *, int, struct sockaddr_in *); 96static void usage(char *); 97 98static void 99dbgmsg(const char *fmt, ...) 100{ 101 va_list ap; 102 103 va_start(ap, fmt); 104 if (debug) 105 vwarnx(fmt, ap); 106 else 107 vsyslog(LOG_ERR, fmt, ap); 108 va_end(ap); 109 110 return; 111} 112 113static int 114find_ifname(idx, name) 115 int idx; 116 char *name; 117{ 118 int mib[6]; 119 size_t needed; 120 struct if_msghdr *ifm; 121 struct sockaddr_dl *sdl; 122 char *buf, *lim, *next; 123 124 needed = 0; 125 mib[0] = CTL_NET; 126 mib[1] = PF_ROUTE; 127 mib[2] = 0; /* protocol */ 128 mib[3] = 0; /* wildcard address family */ 129 mib[4] = NET_RT_IFLIST; 130 mib[5] = 0; /* no flags */ 131 132 if (sysctl (mib, 6, NULL, &needed, NULL, 0) < 0) 133 return(EIO); 134 135 buf = malloc (needed); 136 if (buf == NULL) 137 return(ENOMEM); 138 139 if (sysctl (mib, 6, buf, &needed, NULL, 0) < 0) { 140 free(buf); 141 return(EIO); 142 } 143 144 lim = buf + needed; 145 146 next = buf; 147 while (next < lim) { 148 ifm = (struct if_msghdr *)next; 149 if (ifm->ifm_type == RTM_IFINFO) { 150 sdl = (struct sockaddr_dl *)(ifm + 1); 151 if (ifm->ifm_index == idx) { 152 strncpy(name, sdl->sdl_data, sdl->sdl_nlen); 153 name[sdl->sdl_nlen] = '\0'; 154 free (buf); 155 return (0); 156 } 157 } 158 next += ifm->ifm_msglen; 159 } 160 161 free (buf); 162 163 return(ENOENT); 164} 165 166static int 167announce_event(ifname, sock, dst) 168 char *ifname; 169 int sock; 170 struct sockaddr_in *dst; 171{ 172 int s; 173 char indication[NDIS_INDICATION_LEN]; 174 struct ifreq ifr; 175 struct ndis_evt *e; 176 char buf[512], *pos, *end; 177 int len, type, _type; 178 179 s = socket(PF_INET, SOCK_DGRAM, 0); 180 181 if (s < 0) { 182 dbgmsg("socket creation failed"); 183 return(EINVAL); 184 } 185 186 bzero((char *)&ifr, sizeof(ifr)); 187 e = (struct ndis_evt *)indication; 188 e->ne_len = NDIS_INDICATION_LEN - sizeof(struct ndis_evt); 189 190 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); 191 ifr.ifr_data = indication; 192 193 if (ioctl(s, SIOCGPRIVATE_0, &ifr) < 0) { 194 close(s); 195 if (errno == ENOENT) 196 dbgmsg("drained all events from %s", ifname, errno); 197 else 198 dbgmsg("failed to read event info from %s: %d", 199 ifname, errno); 200 return(ENOENT); 201 } 202 203 if (e->ne_sts == NDIS_STATUS_MEDIA_CONNECT) { 204 type = EVENT_CONNECT; 205 if (verbose) 206 dbgmsg("Received a connect event for %s", ifname); 207 if (!all_events) { 208 close(s); 209 return(0); 210 } 211 } 212 if (e->ne_sts == NDIS_STATUS_MEDIA_DISCONNECT) { 213 type = EVENT_DISCONNECT; 214 if (verbose) 215 dbgmsg("Received a disconnect event for %s", ifname); 216 if (!all_events) { 217 close(s); 218 return(0); 219 } 220 } 221 if (e->ne_sts == NDIS_STATUS_MEDIA_SPECIFIC_INDICATION) { 222 type = EVENT_MEDIA_SPECIFIC; 223 if (verbose) 224 dbgmsg("Received a media-specific event for %s", 225 ifname); 226 } 227 228 end = buf + sizeof(buf); 229 _type = (int) type; 230 memcpy(buf, &_type, sizeof(_type)); 231 pos = buf + sizeof(_type); 232 233 len = snprintf(pos + 1, end - pos - 1, "%s", ifname); 234 if (len < 0) { 235 close(s); 236 return(ENOSPC); 237 } 238 if (len > 255) 239 len = 255; 240 *pos = (unsigned char) len; 241 pos += 1 + len; 242 if (e->ne_len) { 243 if (e->ne_len > 255 || 1 + e->ne_len > end - pos) { 244 dbgmsg("Not enough room for send_event data (%d)\n", 245 e->ne_len); 246 close(s); 247 return(ENOSPC); 248 } 249 *pos++ = (unsigned char) e->ne_len; 250 memcpy(pos, (indication) + sizeof(struct ndis_evt), e->ne_len); 251 pos += e->ne_len; 252 } 253 254 len = sendto(sock, buf, pos - buf, 0, (struct sockaddr *) dst, 255 sizeof(struct sockaddr_in)); 256 257 close(s); 258 return(0); 259} 260 261static void 262usage(progname) 263 char *progname; 264{ 265 fprintf(stderr, "Usage: ndis_events [-a] [-d] [-v]\n", progname); 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(PROGNAME); 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