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 <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 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 <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 
        }
 }
 
+/*
+ * 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;
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to