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 file implements a small portion of the Winpcap API for the 38 * Windows NDIS interface in wpa_supplicant. It provides just enough 39 * routines to fool wpa_supplicant into thinking it's really running 40 * in a Windows environment. 41 */ 42 43#include <sys/types.h> 44#include <sys/param.h> 45#include <sys/socket.h> 46#include <sys/ioctl.h> 47#include <sys/errno.h> 48#include <sys/sysctl.h> 49#include <sys/fcntl.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 <net80211/ieee80211_ioctl.h> 60 61#include <stdio.h> 62#include <string.h> 63#include <stdlib.h> 64#include <unistd.h> 65#include <pcap.h> 66 67#include "Packet32.h" 68 69#define OID_802_11_ADD_KEY 0x0d01011D 70 71typedef ULONGLONG NDIS_802_11_KEY_RSC; 72typedef UCHAR NDIS_802_11_MAC_ADDRESS[6]; 73 74typedef struct NDIS_802_11_KEY { 75 ULONG Length; 76 ULONG KeyIndex; 77 ULONG KeyLength; 78 NDIS_802_11_MAC_ADDRESS BSSID; 79 NDIS_802_11_KEY_RSC KeyRSC; 80 UCHAR KeyMaterial[1]; 81} NDIS_802_11_KEY; 82 83typedef struct NDIS_802_11_KEY_COMPAT { 84 ULONG Length; 85 ULONG KeyIndex; 86 ULONG KeyLength; 87 NDIS_802_11_MAC_ADDRESS BSSID; 88 UCHAR Pad[6]; /* Make struct layout match Windows. */ 89 NDIS_802_11_KEY_RSC KeyRSC; 90#ifdef notdef 91 UCHAR KeyMaterial[1]; 92#endif 93} NDIS_802_11_KEY_COMPAT; 94 95#define TRUE 1 96#define FALSE 0 97 98struct adapter { 99 int socket; 100 char name[IFNAMSIZ]; 101 int prev_roaming; 102}; 103 104PCHAR 105PacketGetVersion(void) 106{ 107 return("FreeBSD WinPcap compatibility shim v1.0"); 108} 109 110void * 111PacketOpenAdapter(CHAR *iface) 112{ 113 struct adapter *a; 114 int s; 115 int ifflags; 116 struct ifreq ifr; 117 struct ieee80211req ireq; 118 119 s = socket(PF_INET, SOCK_DGRAM, 0); 120 121 if (s == -1) 122 return(NULL); 123 124 a = malloc(sizeof(struct adapter)); 125 if (a == NULL) 126 return(NULL); 127 128 a->socket = s; 129 if (strncmp(iface, "\\Device\\NPF_", 12) == 0) 130 iface += 12; 131 else if (strncmp(iface, "\\DEVICE\\", 8) == 0) 132 iface += 8; 133 snprintf(a->name, IFNAMSIZ, "%s", iface); 134 135 /* Turn off net80211 roaming */ 136 bzero((char *)&ireq, sizeof(ireq)); 137 strncpy(ireq.i_name, iface, sizeof (ifr.ifr_name)); 138 ireq.i_type = IEEE80211_IOC_ROAMING; 139 if (ioctl(a->socket, SIOCG80211, &ireq) == 0) { 140 a->prev_roaming = ireq.i_val; 141 ireq.i_val = IEEE80211_ROAMING_MANUAL; 142 if (ioctl(a->socket, SIOCS80211, &ireq) < 0) 143 fprintf(stderr, 144 "Could not set IEEE80211_ROAMING_MANUAL\n"); 145 } 146 147 bzero((char *)&ifr, sizeof(ifr)); 148 strncpy(ifr.ifr_name, iface, sizeof (ifr.ifr_name)); 149 if (ioctl(a->socket, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) { 150 free(a); 151 close(s); 152 return(NULL); 153 } 154 ifr.ifr_flags |= IFF_UP; 155 if (ioctl(a->socket, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) { 156 free(a); 157 close(s); 158 return(NULL); 159 } 160 161 return(a); 162} 163 164int 165PacketRequest(void *iface, BOOLEAN set, PACKET_OID_DATA *oid) 166{ 167 struct adapter *a; 168 uint32_t retval; 169 struct ifreq ifr; 170 NDIS_802_11_KEY *old; 171 NDIS_802_11_KEY_COMPAT *new; 172 PACKET_OID_DATA *o = NULL; 173 174 if (iface == NULL) 175 return(-1); 176 177 a = iface; 178 bzero((char *)&ifr, sizeof(ifr)); 179 180 /* 181 * This hack is necessary to work around a difference 182 * betwee the GNU C and Microsoft C compilers. The NDIS_802_11_KEY 183 * structure has a uint64_t in it, right after an array of 184 * chars. The Microsoft compiler inserts padding right before 185 * the 64-bit value to align it on a 64-bit boundary, but 186 * GCC only aligns it on a 32-bit boundary. Trying to pass 187 * the GCC-formatted structure to an NDIS binary driver 188 * fails because some of the fields appear to be at the 189 * wrong offsets. 190 * 191 * To get around this, if we detect someone is trying to do 192 * a set operation on OID_802_11_ADD_KEY, we shuffle the data 193 * into a properly padded structure and pass that into the 194 * driver instead. This allows the driver_ndis.c code supplied 195 * with wpa_supplicant to work unmodified. 196 */ 197 198 if (set == TRUE && oid->Oid == OID_802_11_ADD_KEY) { 199 old = (NDIS_802_11_KEY *)&oid->Data; 200 o = malloc(sizeof(PACKET_OID_DATA) + 201 sizeof(NDIS_802_11_KEY_COMPAT) + old->KeyLength); 202 if (o == NULL) 203 return(0); 204 bzero((char *)o, sizeof(PACKET_OID_DATA) + 205 sizeof(NDIS_802_11_KEY_COMPAT) + old->KeyLength); 206 o->Oid = oid->Oid; 207 o->Length = sizeof(NDIS_802_11_KEY_COMPAT) + old->KeyLength; 208 new = (NDIS_802_11_KEY_COMPAT *)&o->Data; 209 new->KeyRSC = old->KeyRSC; 210 new->Length = o->Length; 211 new->KeyIndex = old->KeyIndex; 212 new->KeyLength = old->KeyLength; 213 bcopy(old->BSSID, new->BSSID, sizeof(NDIS_802_11_MAC_ADDRESS)); 214 bcopy(old->KeyMaterial, (char *)new + 215 sizeof(NDIS_802_11_KEY_COMPAT), new->KeyLength); 216 ifr.ifr_data = (caddr_t)o; 217 } else 218 ifr.ifr_data = (caddr_t)oid; 219 220 strlcpy(ifr.ifr_name, a->name, sizeof(ifr.ifr_name)); 221 222 if (set == TRUE) 223 retval = ioctl(a->socket, SIOCSDRVSPEC, &ifr); 224 else 225 retval = ioctl(a->socket, SIOCGDRVSPEC, &ifr); 226 227 if (o != NULL) 228 free(o); 229 230 if (retval) 231 return(0); 232 233 return(1); 234} 235 236int 237PacketGetAdapterNames(CHAR *namelist, ULONG *len) 238{ 239 int mib[6]; 240 size_t needed; 241 struct if_msghdr *ifm; 242 struct sockaddr_dl *sdl; 243 char *buf, *lim, *next; 244 char *plist; 245 int spc; 246 int i, ifcnt = 0; 247 248 plist = namelist; 249 spc = 0; 250 251 bzero(plist, *len); 252 253 needed = 0; 254 mib[0] = CTL_NET; 255 mib[1] = PF_ROUTE; 256 mib[2] = 0; /* protocol */ 257 mib[3] = 0; /* wildcard address family */ 258 mib[4] = NET_RT_IFLIST; 259 mib[5] = 0; /* no flags */ 260 261 if (sysctl (mib, 6, NULL, &needed, NULL, 0) < 0) 262 return(FALSE); 263 264 buf = malloc (needed); 265 if (buf == NULL) 266 return(FALSE); 267 268 if (sysctl (mib, 6, buf, &needed, NULL, 0) < 0) { 269 free(buf); 270 return(FALSE); 271 } 272 273 lim = buf + needed; 274 275 /* Generate interface name list. */ 276 277 next = buf; 278 while (next < lim) { 279 ifm = (struct if_msghdr *)next; 280 if (ifm->ifm_type == RTM_IFINFO) { 281 sdl = (struct sockaddr_dl *)(ifm + 1); 282 if (strnstr(sdl->sdl_data, "wlan", sdl->sdl_nlen)) { 283 if ((spc + sdl->sdl_nlen) > *len) { 284 free(buf); 285 return(FALSE); 286 } 287 strncpy(plist, sdl->sdl_data, sdl->sdl_nlen); 288 plist += (sdl->sdl_nlen + 1); 289 spc += (sdl->sdl_nlen + 1); 290 ifcnt++; 291 } 292 } 293 next += ifm->ifm_msglen; 294 } 295 296 297 /* Insert an extra "" as a spacer */ 298 299 plist++; 300 spc++; 301 302 /* 303 * Now generate the interface description list. There 304 * must be a unique description for each interface, and 305 * they have to match what the ndis_events program will 306 * feed in later. To keep this simple, we just repeat 307 * the interface list over again. 308 */ 309 310 next = buf; 311 while (next < lim) { 312 ifm = (struct if_msghdr *)next; 313 if (ifm->ifm_type == RTM_IFINFO) { 314 sdl = (struct sockaddr_dl *)(ifm + 1); 315 if (strnstr(sdl->sdl_data, "wlan", sdl->sdl_nlen)) { 316 if ((spc + sdl->sdl_nlen) > *len) { 317 free(buf); 318 return(FALSE); 319 } 320 strncpy(plist, sdl->sdl_data, sdl->sdl_nlen); 321 plist += (sdl->sdl_nlen + 1); 322 spc += (sdl->sdl_nlen + 1); 323 ifcnt++; 324 } 325 } 326 next += ifm->ifm_msglen; 327 } 328 329 free (buf); 330 331 *len = spc + 1; 332 333 return(TRUE); 334} 335 336void 337PacketCloseAdapter(void *iface) 338{ 339 struct adapter *a; 340 struct ifreq ifr; 341 struct ieee80211req ireq; 342 343 if (iface == NULL) 344 return; 345 346 a = iface; 347 348 /* Reset net80211 roaming */ 349 bzero((char *)&ireq, sizeof(ireq)); 350 strncpy(ireq.i_name, a->name, sizeof (ifr.ifr_name)); 351 ireq.i_type = IEEE80211_IOC_ROAMING; 352 ireq.i_val = a->prev_roaming; 353 ioctl(a->socket, SIOCS80211, &ireq); 354 355 bzero((char *)&ifr, sizeof(ifr)); 356 strncpy(ifr.ifr_name, a->name, sizeof (ifr.ifr_name)); 357 ioctl(a->socket, SIOCGIFFLAGS, (caddr_t)&ifr); 358 ifr.ifr_flags &= ~IFF_UP; 359 ioctl(a->socket, SIOCSIFFLAGS, (caddr_t)&ifr); 360 close(a->socket); 361 free(a); 362 363 return; 364} 365 366#if __FreeBSD_version < 600000 367 368/* 369 * The version of libpcap in FreeBSD 5.2.1 doesn't have these routines. 370 * Call me insane if you will, but I still run 5.2.1 on my laptop, and 371 * I'd like to use WPA there. 372 */ 373 374int 375pcap_get_selectable_fd(pcap_t *p) 376{ 377 return(pcap_fileno(p)); 378} 379 380/* 381 * The old version of libpcap opens its BPF descriptor in read-only 382 * mode. We need to temporarily create a new one we can write to. 383 */ 384 385int 386pcap_inject(pcap_t *p, const void *buf, size_t len) 387{ 388 int fd; 389 int res, n = 0; 390 char device[sizeof "/dev/bpf0000000000"]; 391 struct ifreq ifr; 392 393 /* 394 * Go through all the minors and find one that isn't in use. 395 */ 396 do { 397 (void)snprintf(device, sizeof(device), "/dev/bpf%d", n++); 398 fd = open(device, O_RDWR); 399 } while (fd < 0 && errno == EBUSY); 400 401 if (fd == -1) 402 return(-1); 403 404 bzero((char *)&ifr, sizeof(ifr)); 405 ioctl(pcap_fileno(p), BIOCGETIF, (caddr_t)&ifr); 406 ioctl(fd, BIOCSETIF, (caddr_t)&ifr); 407 408 res = write(fd, buf, len); 409 410 close(fd); 411 412 return(res); 413} 414#endif 415