Cleanups to prepare this code for wider use (likely merged into hostapd):
o add (required) cmd line args to specify the set of ifnet's to monitor for WDS discovery msgs; "any" is a wildcard o change the default script run on wds vap create to the "null script" o auto-daemonize; add -f option to force foreground operation o add -P option for integration with rc.d (implementation missing, tba) o use syslog; default to log up to LOG_INFO, -t (terse) gives you up to LOG_ERR, and -v (verbose) gives you up to LOG_DEBUG o scan for existing vaps on startup to recover existing state o correct some types
This commit is contained in:
parent
7278fd42b9
commit
6f20ea67ac
@ -36,12 +36,6 @@
|
||||
* and launch a script to handle adding the vap to the
|
||||
* bridge, etc.
|
||||
* o destroy wds vap's when station leaves
|
||||
*
|
||||
* Note we query only internal state which means if we don't see
|
||||
* a vap created we won't handle leave/delete properly. Also there
|
||||
* are several fixed pathnames/strings.
|
||||
*
|
||||
* Code liberaly swiped from wlanwatch; probably should nuke printfs.
|
||||
*/
|
||||
#include <sys/param.h>
|
||||
#include <sys/file.h>
|
||||
@ -66,10 +60,12 @@
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <paths.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
#include <ifaddrs.h>
|
||||
|
||||
@ -83,11 +79,14 @@ struct wds {
|
||||
};
|
||||
static struct wds *wds;
|
||||
|
||||
static const char *script = "/usr/local/bin/wdsup";
|
||||
static const char *script = NULL;
|
||||
static char **ifnets;
|
||||
static int nifnets = 0;
|
||||
static int verbose = 0;
|
||||
static int discover_on_join = 0;
|
||||
|
||||
static void handle_rtmsg(struct rt_msghdr *rtm, int msglen);
|
||||
static void scanforvaps(int s);
|
||||
static void handle_rtmsg(struct rt_msghdr *rtm, ssize_t msglen);
|
||||
static void wds_discovery(const char *ifname,
|
||||
const uint8_t bssid[IEEE80211_ADDR_LEN]);
|
||||
static void wds_destroy(const char *ifname);
|
||||
@ -95,42 +94,80 @@ static void wds_leave(const uint8_t bssid[IEEE80211_ADDR_LEN]);
|
||||
static int wds_vap_create(const char *ifname, struct wds *);
|
||||
static int wds_vap_destroy(const char *ifname);
|
||||
|
||||
static void
|
||||
usage(const char *progname)
|
||||
{
|
||||
fprintf(stderr, "usage: %s [-fjtv] [-P pidfile] [-s <set_scriptname>] [ifnet0 ... | any]\n",
|
||||
progname);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int n, s, c;
|
||||
const char *progname = argv[0];
|
||||
const char *pidfile = NULL;
|
||||
int s, c, logmask, bg = 1;
|
||||
char msg[2048];
|
||||
|
||||
while ((c = getopt(argc, argv, "js:vn")) != -1)
|
||||
logmask = LOG_UPTO(LOG_INFO);
|
||||
while ((c = getopt(argc, argv, "fjP:s:tv")) != -1)
|
||||
switch (c) {
|
||||
case 'f':
|
||||
bg = 0;
|
||||
break;
|
||||
case 'j':
|
||||
discover_on_join = 1;
|
||||
break;
|
||||
case 'P':
|
||||
pidfile = optarg;
|
||||
break;
|
||||
case 's':
|
||||
script = optarg;
|
||||
break;
|
||||
case 't':
|
||||
logmask = LOG_UPTO(LOG_ERR);
|
||||
break;
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
logmask = LOG_UPTO(LOG_DEBUG);
|
||||
break;
|
||||
case '?':
|
||||
errx(1, "usage: %s [-s <set_scriptname>]\n"
|
||||
" [-v (for verbose)]\n"
|
||||
" [-j (act on join/rejoin events)]\n", argv[0]);
|
||||
usage(progname);
|
||||
/*NOTREACHED*/
|
||||
}
|
||||
argc -= optind, argv += optind;
|
||||
if (argc == 0) {
|
||||
fprintf(stderr, "%s: no ifnet's specified to monitor\n",
|
||||
progname);
|
||||
usage(progname);
|
||||
}
|
||||
ifnets = argv;
|
||||
nifnets = argc;
|
||||
|
||||
s = socket(PF_ROUTE, SOCK_RAW, 0);
|
||||
if (s < 0)
|
||||
err(EX_OSERR, "socket");
|
||||
for(;;) {
|
||||
n = read(s, msg, 2048);
|
||||
/*
|
||||
* Scan for inherited state.
|
||||
*/
|
||||
scanforvaps(s);
|
||||
|
||||
/* XXX what directory to work in? */
|
||||
if (bg && daemon(0, 0) < 0)
|
||||
err(EX_OSERR, "daemon");
|
||||
|
||||
openlog("wlanwds", LOG_PID | LOG_CONS, LOG_DAEMON);
|
||||
setlogmask(logmask);
|
||||
|
||||
for (;;) {
|
||||
ssize_t n = read(s, msg, sizeof(msg));
|
||||
handle_rtmsg((struct rt_msghdr *)msg, n);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *
|
||||
ether_sprintf(const uint8_t mac[6])
|
||||
ether_sprintf(const uint8_t mac[IEEE80211_ADDR_LEN])
|
||||
{
|
||||
static char buf[32];
|
||||
|
||||
@ -139,76 +176,182 @@ ether_sprintf(const uint8_t mac[6])
|
||||
return buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fetch a vap's parent ifnet name.
|
||||
*/
|
||||
static int
|
||||
getparent(const char *ifname, char parent[IFNAMSIZ+1])
|
||||
{
|
||||
char oid[256];
|
||||
int parentlen;
|
||||
|
||||
/* fetch parent interface name */
|
||||
snprintf(oid, sizeof(oid), "net.wlan.%s.%%parent", ifname+4);
|
||||
parentlen = IFNAMSIZ;
|
||||
if (sysctlbyname(oid, parent, &parentlen, NULL, 0) < 0)
|
||||
return -1;
|
||||
parent[parentlen] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the specified ifnet is one we're supposed to monitor.
|
||||
* The ifnet is assumed to be a vap; we find it's parent and check
|
||||
* it against the set of ifnet's specified on the command line.
|
||||
*/
|
||||
static int
|
||||
checkifnet(const char *ifname, int complain)
|
||||
{
|
||||
char parent[256];
|
||||
int i;
|
||||
|
||||
if (getparent(ifname, parent) < 0) {
|
||||
if (complain)
|
||||
syslog(LOG_ERR,
|
||||
"%s: no pointer to parent interface: %m", ifname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < nifnets; i++)
|
||||
if (strcasecmp(ifnets[i], "any") == 0 ||
|
||||
strcmp(ifnets[i], parent) == 0)
|
||||
return 1;
|
||||
syslog(LOG_DEBUG, "%s: parent %s not being monitored", ifname, parent);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return 1 if the specified ifnet is a WDS vap.
|
||||
*/
|
||||
static int
|
||||
iswdsvap(int s, const char *ifname)
|
||||
{
|
||||
struct ifmediareq ifmr;
|
||||
|
||||
memset(&ifmr, 0, sizeof(ifmr));
|
||||
strncpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
|
||||
if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0)
|
||||
err(-1, "%s: cannot get media", ifname);
|
||||
return (ifmr.ifm_current & IFM_IEEE80211_WDS) != 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fetch the bssid for an ifnet. The caller is assumed
|
||||
* to have already verified this is possible.
|
||||
*/
|
||||
static void
|
||||
handle_rtmsg(struct rt_msghdr *rtm, int msglen)
|
||||
getbssid(int s, const char *ifname, char bssid[IEEE80211_ADDR_LEN])
|
||||
{
|
||||
struct ieee80211req ireq;
|
||||
|
||||
memset(&ireq, 0, sizeof(ireq));
|
||||
strncpy(ireq.i_name, ifname, sizeof(ireq.i_name));
|
||||
ireq.i_type = IEEE80211_IOC_BSSID;
|
||||
ireq.i_data = bssid;
|
||||
ireq.i_len = IEEE80211_ADDR_LEN;
|
||||
if (ioctl(s, SIOCG80211, &ireq) < 0)
|
||||
err(-1, "%s: cannot fetch bssid", ifname);
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan the system for WDS vaps associated with the ifnet's we're
|
||||
* supposed to monitor. Any vaps are added to our internal table
|
||||
* so we can find them (and destroy them) on station leave.
|
||||
*/
|
||||
static void
|
||||
scanforvaps(int s)
|
||||
{
|
||||
char ifname[IFNAMSIZ+1];
|
||||
char bssid[IEEE80211_ADDR_LEN];
|
||||
int i;
|
||||
|
||||
/* XXX brutal; should just walk sysctl tree */
|
||||
for (i = 0; i < 128; i++) {
|
||||
snprintf(ifname, sizeof(ifname), "wlan%d", i);
|
||||
if (checkifnet(ifname, 0) && iswdsvap(s, ifname)) {
|
||||
struct wds *p = malloc(sizeof(struct wds));
|
||||
if (p == NULL)
|
||||
err(-1, "%s: malloc failed", __func__);
|
||||
strlcpy(p->ifname, ifname, IFNAMSIZ);
|
||||
getbssid(s, ifname, p->bssid);
|
||||
p->next = wds;
|
||||
wds = p;
|
||||
|
||||
syslog(LOG_INFO, "[%s] discover wds vap %s",
|
||||
ether_sprintf(bssid), ifname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Process a routing socket message. We handle messages related
|
||||
* to dynamic WDS:
|
||||
* o on WDS discovery (rx of a 4-address frame with DWDS enabled)
|
||||
* we create a WDS vap for the specified mac address
|
||||
* o on station leave we destroy any associated WDS vap
|
||||
* o on ifnet destroy we update state if this is manual destroy of
|
||||
* a WDS vap in our table
|
||||
* o if the -j option is supplied on the command line we create
|
||||
* WDS vaps on station join/rejoin, this is useful for some setups
|
||||
* where a WDS vap is required for 4-address traffic to flow
|
||||
*/
|
||||
static void
|
||||
handle_rtmsg(struct rt_msghdr *rtm, ssize_t msglen)
|
||||
{
|
||||
struct if_announcemsghdr *ifan;
|
||||
time_t now = time(NULL);
|
||||
char *cnow = ctime(&now);
|
||||
|
||||
if (rtm->rtm_version != RTM_VERSION) {
|
||||
(void) printf("routing message version %d not understood\n",
|
||||
syslog(LOG_ERR, "routing message version %d not understood",
|
||||
rtm->rtm_version);
|
||||
return;
|
||||
}
|
||||
switch (rtm->rtm_type) {
|
||||
case RTM_IFANNOUNCE:
|
||||
ifan = (struct if_announcemsghdr *)rtm;
|
||||
if (!verbose)
|
||||
break;
|
||||
printf("%.19s RTM_IFANNOUNCE: if# %d, what: ",
|
||||
cnow, ifan->ifan_index);
|
||||
switch (ifan->ifan_what) {
|
||||
case IFAN_ARRIVAL:
|
||||
printf("arrival");
|
||||
syslog(LOG_DEBUG,
|
||||
"RTM_IFANNOUNCE: if# %d, what: arrival",
|
||||
ifan->ifan_index);
|
||||
break;
|
||||
case IFAN_DEPARTURE:
|
||||
printf("departure");
|
||||
syslog(LOG_DEBUG,
|
||||
"RTM_IFANNOUNCE: if# %d, what: departure",
|
||||
ifan->ifan_index);
|
||||
/* NB: ok to call w/ unmonitored ifnets */
|
||||
wds_destroy(ifan->ifan_name);
|
||||
break;
|
||||
default:
|
||||
printf("#%d", ifan->ifan_what);
|
||||
break;
|
||||
}
|
||||
printf("\n");
|
||||
break;
|
||||
case RTM_IEEE80211:
|
||||
#define V(type) ((struct type *)(&ifan[1]))
|
||||
ifan = (struct if_announcemsghdr *)rtm;
|
||||
switch (ifan->ifan_what) {
|
||||
case RTM_IEEE80211_DISASSOC:
|
||||
if (!discover_on_join)
|
||||
break;
|
||||
/* fall thru... */
|
||||
case RTM_IEEE80211_LEAVE:
|
||||
if (verbose)
|
||||
printf("%.19s %s station leave", cnow,
|
||||
ether_sprintf(V(ieee80211_leave_event)->iev_addr));
|
||||
if (!checkifnet(ifan->ifan_name, 1))
|
||||
break;
|
||||
syslog(LOG_INFO, "[%s] station leave",
|
||||
ether_sprintf(V(ieee80211_leave_event)->iev_addr));
|
||||
wds_leave(V(ieee80211_leave_event)->iev_addr);
|
||||
if (verbose)
|
||||
printf("\n");
|
||||
break;
|
||||
case RTM_IEEE80211_JOIN:
|
||||
case RTM_IEEE80211_REJOIN:
|
||||
case RTM_IEEE80211_ASSOC:
|
||||
case RTM_IEEE80211_REASSOC:
|
||||
if (!discover_on_join)
|
||||
break;
|
||||
/* fall thru... */
|
||||
case RTM_IEEE80211_WDS:
|
||||
if (verbose)
|
||||
printf("%.19s %s wds discovery", cnow,
|
||||
ether_sprintf(V(ieee80211_wds_event)->iev_addr));
|
||||
syslog(LOG_INFO, "[%s] wds discovery",
|
||||
ether_sprintf(V(ieee80211_wds_event)->iev_addr));
|
||||
if (!checkifnet(ifan->ifan_name, 1))
|
||||
break;
|
||||
wds_discovery(ifan->ifan_name,
|
||||
V(ieee80211_wds_event)->iev_addr);
|
||||
if (verbose)
|
||||
printf("\n");
|
||||
break;
|
||||
case RTM_IEEE80211_ASSOC:
|
||||
case RTM_IEEE80211_REASSOC:
|
||||
case RTM_IEEE80211_DISASSOC:
|
||||
case RTM_IEEE80211_SCAN:
|
||||
case RTM_IEEE80211_REPLAY:
|
||||
case RTM_IEEE80211_MICHAEL:
|
||||
break;
|
||||
default:
|
||||
if (verbose)
|
||||
printf("%.19s RTM_IEEE80211: if# %d, what: #%d\n", cnow,
|
||||
ifan->ifan_index, ifan->ifan_what);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@ -216,58 +359,61 @@ handle_rtmsg(struct rt_msghdr *rtm, int msglen)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle WDS discovery; create a WDS vap for the specified bssid.
|
||||
* If a vap already exists then do nothing (can happen when a flood
|
||||
* of 4-address frames causes multiple events to be queued before
|
||||
* we create a vap).
|
||||
*/
|
||||
static void
|
||||
wds_discovery(const char *ifname, const uint8_t bssid[IEEE80211_ADDR_LEN])
|
||||
{
|
||||
struct wds *p;
|
||||
char oid[256], parent[256];
|
||||
int parentlen;
|
||||
char parent[256];
|
||||
char cmd[1024];
|
||||
int status;
|
||||
|
||||
for (p = wds; p != NULL; p = p->next)
|
||||
if (IEEE80211_ADDR_EQ(p->bssid, bssid)) {
|
||||
if (verbose)
|
||||
printf(" (already created)");
|
||||
syslog(LOG_INFO, "[%s] wds vap already created (%s)",
|
||||
ether_sprintf(bssid), ifname);
|
||||
return;
|
||||
}
|
||||
|
||||
/* fetch parent interface name */
|
||||
snprintf(oid, sizeof(oid), "net.wlan.%s.%%parent", ifname+4);
|
||||
parentlen = sizeof(parent);
|
||||
if (sysctlbyname(oid, parent, &parentlen, NULL, 0) < 0) {
|
||||
warn("%s: no pointer to parent interface", __func__);
|
||||
if (getparent(ifname, parent) < 0) {
|
||||
syslog(LOG_ERR, "%s: no pointer to parent interface: %m",
|
||||
ifname);
|
||||
return;
|
||||
}
|
||||
parent[parentlen] = '\0';
|
||||
|
||||
p = malloc(sizeof(struct wds));
|
||||
if (p == NULL) {
|
||||
warn("%s: malloc", __func__);
|
||||
syslog(LOG_ERR, "%s: malloc failed: %m", __func__);
|
||||
return;
|
||||
}
|
||||
IEEE80211_ADDR_COPY(p->bssid, bssid);
|
||||
if (wds_vap_create(parent, p) >= 0) {
|
||||
char cmd[1024];
|
||||
int status;
|
||||
|
||||
/*
|
||||
* Add to table.
|
||||
*/
|
||||
p->next = wds;
|
||||
wds = p;
|
||||
if (verbose)
|
||||
printf(" (create %s)", p->ifname);
|
||||
/*
|
||||
* XXX launch script to setup bridge, etc.
|
||||
*/
|
||||
if (wds_vap_create(parent, p) < 0) {
|
||||
free(p);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Add to table and launch setup script.
|
||||
*/
|
||||
p->next = wds;
|
||||
wds = p;
|
||||
syslog(LOG_INFO, "[%s] create wds vap %s", ether_sprintf(bssid),
|
||||
p->ifname);
|
||||
if (script != NULL) {
|
||||
snprintf(cmd, sizeof(cmd), "%s %s", script, p->ifname);
|
||||
status = system(cmd);
|
||||
if (status)
|
||||
warnx("vap setup script %s exited with status %d\n",
|
||||
script, status);
|
||||
} else
|
||||
free(p);
|
||||
syslog(LOG_ERR, "vap setup script %s exited with "
|
||||
"status %d", script, status);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy a WDS vap (if known).
|
||||
*/
|
||||
static void
|
||||
wds_destroy(const char *ifname)
|
||||
{
|
||||
@ -276,16 +422,17 @@ wds_destroy(const char *ifname)
|
||||
for (pp = &wds; (p = *pp) != NULL; pp = &p->next)
|
||||
if (strncmp(p->ifname, ifname, IFNAMSIZ) == 0)
|
||||
break;
|
||||
/* XXX check for device directly */
|
||||
if (p == NULL) /* not ours/known */
|
||||
if (p != NULL) {
|
||||
*pp = p->next;
|
||||
/* NB: vap already destroyed */
|
||||
free(p);
|
||||
return;
|
||||
*pp = p->next;
|
||||
if (wds_vap_destroy(p->ifname) >= 0)
|
||||
if (verbose)
|
||||
printf(" (wds vap destroyed)");
|
||||
free(p);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle a station leave event; destroy any associated WDS vap.
|
||||
*/
|
||||
static void
|
||||
wds_leave(const uint8_t bssid[IEEE80211_ADDR_LEN])
|
||||
{
|
||||
@ -294,13 +441,13 @@ wds_leave(const uint8_t bssid[IEEE80211_ADDR_LEN])
|
||||
for (pp = &wds; (p = *pp) != NULL; pp = &p->next)
|
||||
if (IEEE80211_ADDR_EQ(p->bssid, bssid))
|
||||
break;
|
||||
/* XXX fall back to check device */
|
||||
if (p == NULL) /* not ours/known */
|
||||
return;
|
||||
*pp = p->next;
|
||||
if (wds_vap_destroy(p->ifname) >= 0)
|
||||
printf(" (wds vap destroyed)");
|
||||
free(p);
|
||||
if (p != NULL) {
|
||||
*pp = p->next;
|
||||
if (wds_vap_destroy(p->ifname) >= 0)
|
||||
syslog(LOG_INFO, "[%s] wds vap %s destroyed",
|
||||
ether_sprintf(bssid), p->ifname);
|
||||
free(p);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
@ -326,14 +473,14 @@ wds_vap_create(const char *parent, struct wds *p)
|
||||
strlcpy(p->ifname, ifr.ifr_name, IFNAMSIZ);
|
||||
status = 0;
|
||||
} else {
|
||||
warn("SIOCIFCREATE2("
|
||||
"mode %u flags 0x%x parent %s bssid %s)",
|
||||
syslog(LOG_ERR, "SIOCIFCREATE2("
|
||||
"mode %u flags 0x%x parent %s bssid %s): %m",
|
||||
cp.icp_opmode, cp.icp_flags, parent,
|
||||
ether_sprintf(cp.icp_bssid));
|
||||
}
|
||||
close(s);
|
||||
} else
|
||||
warn("socket(SOCK_DRAGM)");
|
||||
syslog(LOG_ERR, "socket(SOCK_DRAGM): %m");
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -345,13 +492,13 @@ wds_vap_destroy(const char *ifname)
|
||||
|
||||
s = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (s < 0) {
|
||||
warn("socket(SOCK_DRAGM)");
|
||||
syslog(LOG_ERR, "socket(SOCK_DRAGM): %m");
|
||||
return -1;
|
||||
}
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
strncpy(ifr.i_name, ifname, IFNAMSIZ);
|
||||
if (ioctl(s, SIOCIFDESTROY, &ifr) < 0) {
|
||||
warn("ioctl(SIOCIFDESTROY)");
|
||||
syslog(LOG_ERR, "ioctl(SIOCIFDESTROY): %m");
|
||||
status = -1;
|
||||
} else
|
||||
status = 0;
|
||||
|
Loading…
Reference in New Issue
Block a user