From owner-svn-src-all@FreeBSD.ORG Sat Apr 18 16:14:03 2009 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 95097106577B; Sat, 18 Apr 2009 16:14:03 +0000 (UTC) (envelope-from sam@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 820698FC1C; Sat, 18 Apr 2009 16:14:03 +0000 (UTC) (envelope-from sam@FreeBSD.org) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id n3IGE38V080713; Sat, 18 Apr 2009 16:14:03 GMT (envelope-from sam@svn.freebsd.org) Received: (from sam@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id n3IGE3JL080712; Sat, 18 Apr 2009 16:14:03 GMT (envelope-from sam@svn.freebsd.org) Message-Id: <200904181614.n3IGE3JL080712@svn.freebsd.org> From: Sam Leffler Date: Sat, 18 Apr 2009 16:14:03 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r191247 - head/tools/tools/net80211/wlanwds X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 18 Apr 2009 16:14:06 -0000 Author: sam Date: Sat Apr 18 16:14:03 2009 New Revision: 191247 URL: http://svn.freebsd.org/changeset/base/191247 Log: 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 Modified: head/tools/tools/net80211/wlanwds/wlanwds.c Modified: head/tools/tools/net80211/wlanwds/wlanwds.c ============================================================================== --- head/tools/tools/net80211/wlanwds/wlanwds.c Sat Apr 18 15:59:09 2009 (r191246) +++ head/tools/tools/net80211/wlanwds/wlanwds.c Sat Apr 18 16:14:03 2009 (r191247) @@ -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 #include @@ -66,10 +60,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -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 bssi 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 ] [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 ]\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 } } +/* + * 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_ 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, struc 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;