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 file implements a small portion of the Winpcap API for the
38151214Swpaul * Windows NDIS interface in wpa_supplicant. It provides just enough
39151214Swpaul * routines to fool wpa_supplicant into thinking it's really running
40151214Swpaul * in a Windows environment.
41151214Swpaul */
42151214Swpaul
43151214Swpaul#include <sys/types.h>
44151214Swpaul#include <sys/param.h>
45151214Swpaul#include <sys/socket.h>
46151214Swpaul#include <sys/ioctl.h>
47151214Swpaul#include <sys/errno.h>
48151214Swpaul#include <sys/sysctl.h>
49151214Swpaul#include <sys/fcntl.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
59171371Ssam#include <net80211/ieee80211_ioctl.h>
60171371Ssam
61151214Swpaul#include <stdio.h>
62151214Swpaul#include <string.h>
63151214Swpaul#include <stdlib.h>
64151214Swpaul#include <unistd.h>
65151214Swpaul#include <pcap.h>
66151214Swpaul
67151214Swpaul#include "Packet32.h"
68151214Swpaul
69151214Swpaul#define OID_802_11_ADD_KEY      0x0d01011D
70151214Swpaul
71151214Swpaultypedef ULONGLONG NDIS_802_11_KEY_RSC;
72151214Swpaultypedef UCHAR NDIS_802_11_MAC_ADDRESS[6];
73151214Swpaul
74151214Swpaultypedef struct NDIS_802_11_KEY {
75151214Swpaul	ULONG Length;
76151214Swpaul	ULONG KeyIndex;
77151214Swpaul	ULONG KeyLength;
78151214Swpaul	NDIS_802_11_MAC_ADDRESS BSSID;
79151214Swpaul	NDIS_802_11_KEY_RSC KeyRSC;
80151214Swpaul	UCHAR KeyMaterial[1];
81151214Swpaul} NDIS_802_11_KEY;
82151214Swpaul
83151214Swpaultypedef struct NDIS_802_11_KEY_COMPAT {
84151214Swpaul	ULONG Length;
85151214Swpaul	ULONG KeyIndex;
86151214Swpaul	ULONG KeyLength;
87151214Swpaul	NDIS_802_11_MAC_ADDRESS BSSID;
88151214Swpaul	UCHAR Pad[6]; /* Make struct layout match Windows. */
89151214Swpaul	NDIS_802_11_KEY_RSC KeyRSC;
90151214Swpaul#ifdef notdef
91151214Swpaul	UCHAR KeyMaterial[1];
92151214Swpaul#endif
93151214Swpaul} NDIS_802_11_KEY_COMPAT;
94151214Swpaul
95151214Swpaul#define TRUE 1
96151214Swpaul#define FALSE 0
97151214Swpaul
98151214Swpaulstruct adapter {
99151214Swpaul	int			socket;
100151214Swpaul	char			name[IFNAMSIZ];
101171371Ssam	int			prev_roaming;
102151214Swpaul};
103151214Swpaul
104151517SwpaulPCHAR
105151517SwpaulPacketGetVersion(void)
106151517Swpaul{
107151517Swpaul	return("FreeBSD WinPcap compatibility shim v1.0");
108151517Swpaul}
109151517Swpaul
110151214Swpaulvoid *
111189220SsamPacketOpenAdapter(CHAR *iface)
112151214Swpaul{
113151214Swpaul	struct adapter		*a;
114151214Swpaul	int			s;
115151214Swpaul	int			ifflags;
116151214Swpaul	struct ifreq		ifr;
117171371Ssam	struct ieee80211req	ireq;
118151214Swpaul
119151214Swpaul	s = socket(PF_INET, SOCK_DGRAM, 0);
120151214Swpaul
121151214Swpaul	if (s == -1)
122151214Swpaul		return(NULL);
123151214Swpaul
124151214Swpaul	a = malloc(sizeof(struct adapter));
125151214Swpaul	if (a == NULL)
126151214Swpaul		return(NULL);
127151214Swpaul
128151214Swpaul	a->socket = s;
129171371Ssam	if (strncmp(iface, "\\Device\\NPF_", 12) == 0)
130171371Ssam		iface += 12;
131171371Ssam	else if (strncmp(iface, "\\DEVICE\\", 8) == 0)
132171371Ssam		iface += 8;
133151214Swpaul	snprintf(a->name, IFNAMSIZ, "%s", iface);
134151214Swpaul
135171371Ssam	/* Turn off net80211 roaming */
136171371Ssam	bzero((char *)&ireq, sizeof(ireq));
137171371Ssam	strncpy(ireq.i_name, iface, sizeof (ifr.ifr_name));
138171371Ssam	ireq.i_type = IEEE80211_IOC_ROAMING;
139171371Ssam	if (ioctl(a->socket, SIOCG80211, &ireq) == 0) {
140171371Ssam		a->prev_roaming = ireq.i_val;
141171371Ssam		ireq.i_val = IEEE80211_ROAMING_MANUAL;
142171371Ssam		if (ioctl(a->socket, SIOCS80211, &ireq) < 0)
143171371Ssam			fprintf(stderr,
144171371Ssam			    "Could not set IEEE80211_ROAMING_MANUAL\n");
145171371Ssam	}
146171371Ssam
147151214Swpaul	bzero((char *)&ifr, sizeof(ifr));
148151214Swpaul        strncpy(ifr.ifr_name, iface, sizeof (ifr.ifr_name));
149151214Swpaul        if (ioctl(a->socket, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
150151214Swpaul		free(a);
151151214Swpaul		close(s);
152151214Swpaul		return(NULL);
153151214Swpaul        }
154151214Swpaul        ifr.ifr_flags |= IFF_UP;
155151214Swpaul        if (ioctl(a->socket, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) {
156151214Swpaul		free(a);
157151214Swpaul		close(s);
158151214Swpaul		return(NULL);
159151214Swpaul        }
160151214Swpaul
161151214Swpaul	return(a);
162151214Swpaul}
163151214Swpaul
164151214Swpaulint
165189220SsamPacketRequest(void *iface, BOOLEAN set, PACKET_OID_DATA *oid)
166151214Swpaul{
167151214Swpaul	struct adapter		*a;
168151214Swpaul	uint32_t		retval;
169151214Swpaul	struct ifreq		ifr;
170151214Swpaul	NDIS_802_11_KEY		*old;
171151214Swpaul	NDIS_802_11_KEY_COMPAT	*new;
172151214Swpaul	PACKET_OID_DATA		*o = NULL;
173151214Swpaul
174151214Swpaul	if (iface == NULL)
175151214Swpaul		return(-1);
176151214Swpaul
177151214Swpaul	a = iface;
178151214Swpaul	bzero((char *)&ifr, sizeof(ifr));
179151214Swpaul
180151214Swpaul	/*
181151214Swpaul	 * This hack is necessary to work around a difference
182151214Swpaul	 * betwee the GNU C and Microsoft C compilers. The NDIS_802_11_KEY
183151214Swpaul	 * structure has a uint64_t in it, right after an array of
184151214Swpaul	 * chars. The Microsoft compiler inserts padding right before
185151214Swpaul	 * the 64-bit value to align it on a 64-bit boundary, but
186151214Swpaul	 * GCC only aligns it on a 32-bit boundary. Trying to pass
187151214Swpaul	 * the GCC-formatted structure to an NDIS binary driver
188151214Swpaul	 * fails because some of the fields appear to be at the
189151214Swpaul	 * wrong offsets.
190151214Swpaul	 *
191151214Swpaul	 * To get around this, if we detect someone is trying to do
192151214Swpaul	 * a set operation on OID_802_11_ADD_KEY, we shuffle the data
193151214Swpaul	 * into a properly padded structure and pass that into the
194151214Swpaul	 * driver instead. This allows the driver_ndis.c code supplied
195151214Swpaul	 * with wpa_supplicant to work unmodified.
196151214Swpaul	 */
197151214Swpaul
198151214Swpaul	if (set == TRUE && oid->Oid == OID_802_11_ADD_KEY) {
199151214Swpaul		old = (NDIS_802_11_KEY *)&oid->Data;
200151214Swpaul		o = malloc(sizeof(PACKET_OID_DATA) +
201151214Swpaul		    sizeof(NDIS_802_11_KEY_COMPAT) + old->KeyLength);
202151214Swpaul		if (o == NULL)
203151214Swpaul			return(0);
204151214Swpaul		bzero((char *)o, sizeof(PACKET_OID_DATA) +
205151214Swpaul		    sizeof(NDIS_802_11_KEY_COMPAT) + old->KeyLength);
206151214Swpaul		o->Oid = oid->Oid;
207151214Swpaul		o->Length = sizeof(NDIS_802_11_KEY_COMPAT) + old->KeyLength;
208151214Swpaul		new = (NDIS_802_11_KEY_COMPAT *)&o->Data;
209151214Swpaul		new->KeyRSC = old->KeyRSC;
210151214Swpaul		new->Length = o->Length;
211151214Swpaul		new->KeyIndex = old->KeyIndex;
212151214Swpaul		new->KeyLength = old->KeyLength;
213151214Swpaul		bcopy(old->BSSID, new->BSSID, sizeof(NDIS_802_11_MAC_ADDRESS));
214151214Swpaul		bcopy(old->KeyMaterial, (char *)new +
215151214Swpaul		    sizeof(NDIS_802_11_KEY_COMPAT), new->KeyLength);
216151214Swpaul        	ifr.ifr_data = (caddr_t)o;
217151214Swpaul	} else
218151214Swpaul        	ifr.ifr_data = (caddr_t)oid;
219151214Swpaul
220151214Swpaul        strlcpy(ifr.ifr_name, a->name, sizeof(ifr.ifr_name));
221151214Swpaul
222151214Swpaul	if (set == TRUE)
223151214Swpaul		retval = ioctl(a->socket, SIOCSDRVSPEC, &ifr);
224151214Swpaul	else
225151214Swpaul		retval = ioctl(a->socket, SIOCGDRVSPEC, &ifr);
226151214Swpaul
227151214Swpaul	if (o != NULL)
228151214Swpaul		free(o);
229151214Swpaul
230151214Swpaul	if (retval)
231151214Swpaul		return(0);
232151214Swpaul
233151214Swpaul	return(1);
234151214Swpaul}
235151214Swpaul
236151214Swpaulint
237189220SsamPacketGetAdapterNames(CHAR *namelist, ULONG *len)
238151214Swpaul{
239151214Swpaul	int			mib[6];
240151214Swpaul	size_t			needed;
241151214Swpaul	struct if_msghdr	*ifm;
242151214Swpaul	struct sockaddr_dl	*sdl;
243151214Swpaul	char			*buf, *lim, *next;
244151214Swpaul	char			*plist;
245151214Swpaul	int			spc;
246151214Swpaul	int			i, ifcnt = 0;
247151214Swpaul
248151214Swpaul	plist = namelist;
249151214Swpaul	spc = 0;
250151214Swpaul
251151214Swpaul	bzero(plist, *len);
252151214Swpaul
253151214Swpaul	needed = 0;
254151214Swpaul	mib[0] = CTL_NET;
255151214Swpaul	mib[1] = PF_ROUTE;
256151214Swpaul	mib[2] = 0;             /* protocol */
257151214Swpaul	mib[3] = 0;             /* wildcard address family */
258151214Swpaul	mib[4] = NET_RT_IFLIST;
259151214Swpaul	mib[5] = 0;             /* no flags */
260151214Swpaul
261151214Swpaul	if (sysctl (mib, 6, NULL, &needed, NULL, 0) < 0)
262157687Ssam		return(FALSE);
263151214Swpaul
264151214Swpaul	buf = malloc (needed);
265151214Swpaul	if (buf == NULL)
266157687Ssam		return(FALSE);
267151214Swpaul
268151214Swpaul	if (sysctl (mib, 6, buf, &needed, NULL, 0) < 0) {
269151214Swpaul		free(buf);
270157687Ssam		return(FALSE);
271151214Swpaul	}
272151214Swpaul
273151214Swpaul	lim = buf + needed;
274151214Swpaul
275151214Swpaul	/* Generate interface name list. */
276151214Swpaul
277151214Swpaul	next = buf;
278151214Swpaul	while (next < lim) {
279151214Swpaul		ifm = (struct if_msghdr *)next;
280151214Swpaul		if (ifm->ifm_type == RTM_IFINFO) {
281151214Swpaul			sdl = (struct sockaddr_dl *)(ifm + 1);
282178358Ssam			if (strnstr(sdl->sdl_data, "wlan", sdl->sdl_nlen)) {
283151214Swpaul				if ((spc + sdl->sdl_nlen) > *len) {
284151214Swpaul					free(buf);
285157687Ssam					return(FALSE);
286151214Swpaul				}
287151214Swpaul				strncpy(plist, sdl->sdl_data, sdl->sdl_nlen);
288151214Swpaul				plist += (sdl->sdl_nlen + 1);
289151214Swpaul				spc += (sdl->sdl_nlen + 1);
290151214Swpaul				ifcnt++;
291151214Swpaul			}
292151214Swpaul		}
293151214Swpaul		next += ifm->ifm_msglen;
294151214Swpaul	}
295151214Swpaul
296151214Swpaul
297151214Swpaul	/* Insert an extra "" as a spacer */
298151214Swpaul
299151214Swpaul	plist++;
300151214Swpaul	spc++;
301151214Swpaul
302151214Swpaul	/*
303151214Swpaul	 * Now generate the interface description list. There
304151214Swpaul	 * must be a unique description for each interface, and
305151214Swpaul	 * they have to match what the ndis_events program will
306151214Swpaul	 * feed in later. To keep this simple, we just repeat
307151214Swpaul	 * the interface list over again.
308151214Swpaul	 */
309151214Swpaul
310151214Swpaul	next = buf;
311151214Swpaul	while (next < lim) {
312151214Swpaul		ifm = (struct if_msghdr *)next;
313151214Swpaul		if (ifm->ifm_type == RTM_IFINFO) {
314151214Swpaul			sdl = (struct sockaddr_dl *)(ifm + 1);
315178358Ssam			if (strnstr(sdl->sdl_data, "wlan", sdl->sdl_nlen)) {
316151214Swpaul				if ((spc + sdl->sdl_nlen) > *len) {
317151214Swpaul					free(buf);
318157687Ssam					return(FALSE);
319151214Swpaul				}
320151214Swpaul				strncpy(plist, sdl->sdl_data, sdl->sdl_nlen);
321151214Swpaul				plist += (sdl->sdl_nlen + 1);
322151214Swpaul				spc += (sdl->sdl_nlen + 1);
323151214Swpaul				ifcnt++;
324151214Swpaul			}
325151214Swpaul		}
326151214Swpaul		next += ifm->ifm_msglen;
327151214Swpaul	}
328151214Swpaul
329151214Swpaul	free (buf);
330151214Swpaul
331151214Swpaul	*len = spc + 1;
332151214Swpaul
333157687Ssam	return(TRUE);
334151214Swpaul}
335151214Swpaul
336151214Swpaulvoid
337189220SsamPacketCloseAdapter(void *iface)
338151214Swpaul{
339151214Swpaul	struct adapter		*a;
340151214Swpaul	struct ifreq		ifr;
341171371Ssam	struct ieee80211req	ireq;
342151214Swpaul
343151214Swpaul	if (iface == NULL)
344151214Swpaul		return;
345151214Swpaul
346151214Swpaul	a = iface;
347151214Swpaul
348171371Ssam	/* Reset net80211 roaming */
349171371Ssam	bzero((char *)&ireq, sizeof(ireq));
350171371Ssam	strncpy(ireq.i_name, a->name, sizeof (ifr.ifr_name));
351171371Ssam	ireq.i_type = IEEE80211_IOC_ROAMING;
352171371Ssam	ireq.i_val = a->prev_roaming;
353171371Ssam	ioctl(a->socket, SIOCS80211, &ireq);
354171371Ssam
355151214Swpaul	bzero((char *)&ifr, sizeof(ifr));
356151214Swpaul        strncpy(ifr.ifr_name, a->name, sizeof (ifr.ifr_name));
357151214Swpaul        ioctl(a->socket, SIOCGIFFLAGS, (caddr_t)&ifr);
358151214Swpaul        ifr.ifr_flags &= ~IFF_UP;
359151214Swpaul        ioctl(a->socket, SIOCSIFFLAGS, (caddr_t)&ifr);
360151214Swpaul	close(a->socket);
361151214Swpaul	free(a);
362151214Swpaul
363151214Swpaul	return;
364151214Swpaul}
365151214Swpaul
366151214Swpaul#if __FreeBSD_version < 600000
367151214Swpaul
368151214Swpaul/*
369151214Swpaul * The version of libpcap in FreeBSD 5.2.1 doesn't have these routines.
370151214Swpaul * Call me insane if you will, but I still run 5.2.1 on my laptop, and
371151214Swpaul * I'd like to use WPA there.
372151214Swpaul */
373151214Swpaul
374151214Swpaulint
375151214Swpaulpcap_get_selectable_fd(pcap_t *p)
376151214Swpaul{
377151214Swpaul	return(pcap_fileno(p));
378151214Swpaul}
379151214Swpaul
380151214Swpaul/*
381151214Swpaul * The old version of libpcap opens its BPF descriptor in read-only
382151214Swpaul * mode. We need to temporarily create a new one we can write to.
383151214Swpaul */
384151214Swpaul
385151214Swpaulint
386151214Swpaulpcap_inject(pcap_t *p, const void *buf, size_t len)
387151214Swpaul{
388151214Swpaul	int			fd;
389151214Swpaul	int			res, n = 0;
390151214Swpaul	char			device[sizeof "/dev/bpf0000000000"];
391151214Swpaul	struct ifreq		ifr;
392151214Swpaul
393151214Swpaul        /*
394151214Swpaul         * Go through all the minors and find one that isn't in use.
395151214Swpaul         */
396151214Swpaul	do {
397151214Swpaul		(void)snprintf(device, sizeof(device), "/dev/bpf%d", n++);
398151214Swpaul		fd = open(device, O_RDWR);
399151214Swpaul	} while (fd < 0 && errno == EBUSY);
400151214Swpaul
401151214Swpaul	if (fd == -1)
402151214Swpaul		return(-1);
403151214Swpaul
404151214Swpaul	bzero((char *)&ifr, sizeof(ifr));
405151214Swpaul	ioctl(pcap_fileno(p), BIOCGETIF, (caddr_t)&ifr);
406151214Swpaul	ioctl(fd, BIOCSETIF, (caddr_t)&ifr);
407151214Swpaul
408151214Swpaul	res = write(fd, buf, len);
409151214Swpaul
410151214Swpaul	close(fd);
411151214Swpaul
412151214Swpaul	return(res);
413151214Swpaul}
414151214Swpaul#endif
415