Training courses

Kernel and Embedded Linux

Bootlin training courses

Embedded Linux, kernel,
Yocto Project, Buildroot, real-time,
graphics, boot time, debugging...

Bootlin logo

Elixir Cross Referencer

/*-
 * SPDX-License-Identifier: BSD-4-Clause
 *
 * Copyright (c) 2005
 *      Bill Paul <wpaul@windriver.com>.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by Bill Paul.
 * 4. Neither the name of the author nor the names of any co-contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");

/*
 * This program simulates the behavior of the ndis_events utility
 * supplied with wpa_supplicant for Windows. The original utility
 * is designed to translate Windows WMI events. We don't have WMI,
 * but we need to supply certain event info to wpa_supplicant in
 * order to make WPA2 work correctly, so we fake up the interface.
 */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_dl.h>

#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <net/route.h>

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <err.h>
#include <syslog.h>
#include <stdarg.h>

static int verbose = 0;
static int debug = 0;
static int all_events = 0;

#define PROGNAME "ndis_events"

#define WPA_SUPPLICANT_PORT	9876
#define NDIS_INDICATION_LEN	2048

#define EVENT_CONNECT		0
#define EVENT_DISCONNECT	1
#define EVENT_MEDIA_SPECIFIC	2

#define NDIS_STATUS_MEDIA_CONNECT		0x4001000B
#define NDIS_STATUS_MEDIA_DISCONNECT		0x4001000C
#define NDIS_STATUS_MEDIA_SPECIFIC_INDICATION	0x40010012

struct ndis_evt {
	uint32_t		ne_sts;
	uint32_t		ne_len;
#ifdef notdef
	char			ne_buf[1];
#endif
};

static int find_ifname(int, char *);
static int announce_event(char *, int, struct sockaddr_in *);
static void usage(void);

static void
dbgmsg(const char *fmt, ...)
{
	va_list			ap;

	va_start(ap, fmt);
	if (debug)
		vwarnx(fmt, ap);
	else
		vsyslog(LOG_ERR, fmt, ap);
	va_end(ap);

	return;
}

static int
find_ifname(idx, name)
	int			idx;
	char			*name;
{
	int			mib[6];
	size_t			needed;
	struct if_msghdr	*ifm;
	struct sockaddr_dl	*sdl;
	char			*buf, *lim, *next;

	needed = 0;
	mib[0] = CTL_NET;
	mib[1] = PF_ROUTE;
	mib[2] = 0;             /* protocol */
	mib[3] = 0;             /* wildcard address family */
	mib[4] = NET_RT_IFLIST;
	mib[5] = 0;             /* no flags */

	if (sysctl (mib, 6, NULL, &needed, NULL, 0) < 0)
		return(EIO);

	buf = malloc (needed);
	if (buf == NULL)
		return(ENOMEM);

	if (sysctl (mib, 6, buf, &needed, NULL, 0) < 0) {
		free(buf);
		return(EIO);
	}

	lim = buf + needed;

	next = buf;
	while (next < lim) {
		ifm = (struct if_msghdr *)next;
		if (ifm->ifm_type == RTM_IFINFO) {
			sdl = (struct sockaddr_dl *)(ifm + 1);
			if (ifm->ifm_index == idx) {
				strncpy(name, sdl->sdl_data, sdl->sdl_nlen);
				name[sdl->sdl_nlen] = '\0';
				free (buf);
				return (0);
			}
		}
		next += ifm->ifm_msglen;
	}

	free (buf);

	return(ENOENT);
}

static int 
announce_event(ifname, sock, dst)
	char			*ifname;
	int			sock;
	struct sockaddr_in	*dst;
{
	int			s;
	char			indication[NDIS_INDICATION_LEN];
        struct ifreq            ifr;
	struct ndis_evt		*e;
	char			buf[512], *pos, *end;
	int			len, type, _type;

	s = socket(PF_INET, SOCK_DGRAM, 0);

	if (s < 0) {
		dbgmsg("socket creation failed");
                return(EINVAL);
	}

        bzero((char *)&ifr, sizeof(ifr));
	e = (struct ndis_evt *)indication;
	e->ne_len = NDIS_INDICATION_LEN - sizeof(struct ndis_evt);

        strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
        ifr.ifr_data = indication;

	if (ioctl(s, SIOCGPRIVATE_0, &ifr) < 0) {
		close(s);
		if (verbose) {
			if (errno == ENOENT)
				dbgmsg("drained all events from %s",
				    ifname, errno);
			else
				dbgmsg("failed to read event info from %s: %d",
				    ifname, errno);
		}
		return(ENOENT);
	}

	if (e->ne_sts == NDIS_STATUS_MEDIA_CONNECT) {
		type = EVENT_CONNECT;
		if (verbose)
			dbgmsg("Received a connect event for %s", ifname);
		if (!all_events) {
			close(s);
			return(0);
		}
	}
	if (e->ne_sts == NDIS_STATUS_MEDIA_DISCONNECT) {
		type = EVENT_DISCONNECT;
		if (verbose)
			dbgmsg("Received a disconnect event for %s", ifname);
		if (!all_events) {
			close(s);
			return(0);
		}
	}
	if (e->ne_sts == NDIS_STATUS_MEDIA_SPECIFIC_INDICATION) {
		type = EVENT_MEDIA_SPECIFIC;
		if (verbose)
			dbgmsg("Received a media-specific event for %s",
			    ifname);
	}

	end = buf + sizeof(buf);
	_type = (int) type;
	memcpy(buf, &_type, sizeof(_type));
	pos = buf + sizeof(_type);

	len = snprintf(pos + 1, end - pos - 1, "%s", ifname);
	if (len < 0) {
		close(s);
		return(ENOSPC);
	}
	if (len > 255)
		len = 255;
	*pos = (unsigned char) len;
	pos += 1 + len;
	if (e->ne_len) {
		if (e->ne_len > 255 || 1 + e->ne_len > end - pos) {
			dbgmsg("Not enough room for send_event data (%d)\n",
			    e->ne_len);
			close(s);
			return(ENOSPC);
 		}
		*pos++ = (unsigned char) e->ne_len;
		memcpy(pos, (indication) + sizeof(struct ndis_evt), e->ne_len);
		pos += e->ne_len;
	}

	len = sendto(sock, buf, pos - buf, 0, (struct sockaddr *) dst,
	    sizeof(struct sockaddr_in));

	close(s);
	return(0);
}

static void
usage()
{
	fprintf(stderr, "Usage: ndis_events [-a] [-d] [-v]\n");
	exit(1);
}

int
main(argc, argv)
	int			argc;
	char			*argv[];
{
	int			s, r, n;
	struct sockaddr_in	sin;
	char			msg[NDIS_INDICATION_LEN];
	struct rt_msghdr	*rtm;
	struct if_msghdr	*ifm;
	char			ifname[IFNAMSIZ];
	int			ch;

	while ((ch = getopt(argc, argv, "dva")) != -1) {
		switch(ch) {
		case 'd':
			debug++;
			break;
		case 'v':
			verbose++;
			break;
		case 'a':
			all_events++;
			break;
		default:
			usage();
			break;
		}
	}

	if (!debug && daemon(0, 0))
		err(1, "failed to daemonize ourselves");

	if (!debug)
		openlog(PROGNAME, LOG_PID | LOG_CONS, LOG_DAEMON);

	bzero((char *)&sin, sizeof(sin));

	/* Create a datagram  socket. */

	s = socket(PF_INET, SOCK_DGRAM, 0);
	if (s < 0) {
		dbgmsg("socket creation failed");
		exit(1);
	}

	sin.sin_family = AF_INET;
	sin.sin_addr.s_addr = inet_addr("127.0.0.1");
	sin.sin_port = htons(WPA_SUPPLICANT_PORT);

	/* Create a routing socket. */

	r = socket (PF_ROUTE, SOCK_RAW, 0);
	if (r < 0) {
		dbgmsg("routing socket creation failed");
		exit(1);
	}

	/* Now sit and spin, waiting for events. */

	if (verbose)
		dbgmsg("Listening for events");

	while (1) {
		n = read(r, msg, NDIS_INDICATION_LEN);
		rtm = (struct rt_msghdr *)msg;
		if (rtm->rtm_type != RTM_IFINFO)
			continue;
		ifm = (struct if_msghdr *)msg;
		if (find_ifname(ifm->ifm_index, ifname))
			continue;
		if (strstr(ifname, "ndis")) {
			while(announce_event(ifname, s, &sin) == 0)
				;
		} else {
			if (verbose)
				dbgmsg("Skipping ifinfo message from %s",
				    ifname);
		}
	}

	/* NOTREACHED */
	exit(0);
}