wlanwds.c revision 178360
154359Sroberto/*- 254359Sroberto * Copyright (c) 2006-2007 Sam Leffler, Errno Consulting 354359Sroberto * All rights reserved. 454359Sroberto * 554359Sroberto * Redistribution and use in source and binary forms, with or without 654359Sroberto * modification, are permitted provided that the following conditions 754359Sroberto * are met: 854359Sroberto * 1. Redistributions of source code must retain the above copyright 954359Sroberto * notice, this list of conditions and the following disclaimer, 1054359Sroberto * without modification. 1154359Sroberto * 2. Redistributions in binary form must reproduce at minimum a disclaimer 1254359Sroberto * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 1354359Sroberto * redistribution must be conditioned upon including a substantially 1454359Sroberto * similar Disclaimer requirement for further binary redistribution. 1554359Sroberto * 1654359Sroberto * NO WARRANTY 1754359Sroberto * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1854359Sroberto * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1954359Sroberto * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 2054359Sroberto * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 2154359Sroberto * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 2254359Sroberto * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2354359Sroberto * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2454359Sroberto * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 2554359Sroberto * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2654359Sroberto * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 2754359Sroberto * THE POSSIBILITY OF SUCH DAMAGES. 2854359Sroberto * 2954359Sroberto * $FreeBSD: head/tools/tools/net80211/wlanwds/wlanwds.c 178360 2008-04-20 20:43:13Z sam $ 3054359Sroberto */ 3154359Sroberto 3254359Sroberto/* 3354359Sroberto * Test app to demonstrate how to handle dynamic WDS links: 3454359Sroberto * o monitor 802.11 events for wds discovery events 35280849Scy * o create wds vap's in response to wds discovery events 3654359Sroberto * and launch a script to handle adding the vap to the 3754359Sroberto * bridge, etc. 3854359Sroberto * o destroy wds vap's when station leaves 3954359Sroberto * 4054359Sroberto * Note we query only internal state which means if we don't see 4154359Sroberto * a vap created we won't handle leave/delete properly. Also there 4254359Sroberto * are several fixed pathnames/strings. Some require fixing 4354359Sroberto * kernel support (e.g. sysctl to find parent device of a vap). 4454359Sroberto * 4554359Sroberto * Code liberaly swiped from wlanwatch; probably should nuke printfs. 4654359Sroberto */ 4754359Sroberto#include <sys/param.h> 4854359Sroberto#include <sys/file.h> 4954359Sroberto#include <sys/socket.h> 5054359Sroberto#include <sys/ioctl.h> 5154359Sroberto#include <sys/sysctl.h> 5254359Sroberto#include <sys/types.h> 5354359Sroberto 5454359Sroberto#include <net/if.h> 5554359Sroberto#include "net/if_media.h" 5654359Sroberto#include <net/route.h> 5754359Sroberto#include <net/if_dl.h> 5854359Sroberto#include <netinet/in.h> 5954359Sroberto#include <netinet/if_ether.h> 6054359Sroberto#include <netatalk/at.h> 6154359Sroberto#include "net80211/ieee80211_ioctl.h" 6254359Sroberto#include "net80211/ieee80211_freebsd.h" 6354359Sroberto#include <arpa/inet.h> 6454359Sroberto#include <netdb.h> 6554359Sroberto 6654359Sroberto#include <ctype.h> 6754359Sroberto#include <err.h> 68280849Scy#include <errno.h> 6954359Sroberto#include <paths.h> 7054359Sroberto#include <stdio.h> 7154359Sroberto#include <stdlib.h> 7254359Sroberto#include <string.h> 7354359Sroberto#include <sysexits.h> 7454359Sroberto#include <unistd.h> 7554359Sroberto#include <ifaddrs.h> 7654359Sroberto 7754359Sroberto#define IEEE80211_ADDR_EQ(a1,a2) (memcmp(a1,a2,IEEE80211_ADDR_LEN) == 0) 78280849Scy#define IEEE80211_ADDR_COPY(dst,src) memcpy(dst,src,IEEE80211_ADDR_LEN) 79280849Scy 80280849Scystruct wds { 81280849Scy struct wds *next; 82280849Scy uint8_t bssid[IEEE80211_ADDR_LEN]; /* bssid of associated sta */ 8354359Sroberto char ifname[IFNAMSIZ]; /* vap interface name */ 84280849Scy}; 85280849Scystatic struct wds *wds; 86280849Scy 87280849Scystatic const char *bridge = "bridge0"; 88280849Scystatic const char *parent = "mv0"; /* XXX no sysctl to find this */ 89280849Scystatic const char *script = "/usr/local/bin/wdsup"; 90280849Scystatic int verbose = 0; 91280849Scy 92280849Scystatic void handle_rtmsg(struct rt_msghdr *rtm, int msglen); 93280849Scystatic void wds_discovery(const char *ifname, 94280849Scy const uint8_t bssid[IEEE80211_ADDR_LEN]); 95280849Scystatic void wds_destroy(const char *ifname); 96280849Scystatic void wds_leave(const uint8_t bssid[IEEE80211_ADDR_LEN]); 97280849Scystatic int wds_vap_create(const char *ifname, struct wds *); 98280849Scystatic int wds_vap_destroy(const char *ifname); 99280849Scy 100280849Scyint 101280849Scymain(int argc, char *argv[]) 102280849Scy{ 10354359Sroberto int n, s, c; 10454359Sroberto char msg[2048]; 10554359Sroberto 10654359Sroberto while ((c = getopt(argc, argv, "b:p:s:vn")) != -1) 10754359Sroberto switch (c) { 10854359Sroberto case 'b': 109280849Scy bridge = optarg; 11054359Sroberto break; 11154359Sroberto case 'p': 11254359Sroberto parent = optarg; 11354359Sroberto break; 114280849Scy case 's': 115280849Scy script = optarg; 11654359Sroberto break; 11754359Sroberto case 'v': 11854359Sroberto verbose = 1; 11954359Sroberto break; 12054359Sroberto case '?': 12154359Sroberto errx(1, "usage: %s [-b <bridgename>] [-p <parentname>] [-s <set_scriptname>]\n" 12254359Sroberto " [-v (for verbose)]\n", argv[0]); 123280849Scy /*NOTREACHED*/ 12454359Sroberto } 125280849Scy 126280849Scy s = socket(PF_ROUTE, SOCK_RAW, 0); 127280849Scy if (s < 0) 12854359Sroberto err(EX_OSERR, "socket"); 129280849Scy for(;;) { 13054359Sroberto n = read(s, msg, 2048); 131280849Scy handle_rtmsg((struct rt_msghdr *)msg, n); 132280849Scy } 133280849Scy return 0; 13454359Sroberto} 135280849Scy 13654359Srobertostatic const char * 137280849Scyether_sprintf(const uint8_t mac[6]) 138280849Scy{ 139280849Scy static char buf[32]; 140280849Scy 14154359Sroberto snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x", 142280849Scy mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 14354359Sroberto return buf; 144280849Scy} 145280849Scy 146280849Scystatic void 147280849Scyhandle_rtmsg(struct rt_msghdr *rtm, int msglen) 148280849Scy{ 149280849Scy struct if_announcemsghdr *ifan; 150280849Scy time_t now = time(NULL); 151280849Scy char *cnow = ctime(&now); 152280849Scy 153280849Scy if (rtm->rtm_version != RTM_VERSION) { 154280849Scy (void) printf("routing message version %d not understood\n", 15554359Sroberto rtm->rtm_version); 15654359Sroberto return; 15754359Sroberto } 158280849Scy switch (rtm->rtm_type) { 159280849Scy case RTM_IFANNOUNCE: 160280849Scy ifan = (struct if_announcemsghdr *)rtm; 161280849Scy if (!verbose) 16254359Sroberto break; 16354359Sroberto printf("%.19s RTM_IFANNOUNCE: if# %d, what: ", 16454359Sroberto cnow, ifan->ifan_index); 165280849Scy switch (ifan->ifan_what) { 166280849Scy case IFAN_ARRIVAL: 167280849Scy printf("arrival"); 16854359Sroberto break; 16954359Sroberto case IFAN_DEPARTURE: 17054359Sroberto printf("departure"); 171280849Scy wds_destroy(ifan->ifan_name); 172280849Scy break; 173280849Scy default: 17454359Sroberto printf("#%d", ifan->ifan_what); 17554359Sroberto break; 17654359Sroberto } 177280849Scy printf("\n"); 178280849Scy break; 179280849Scy case RTM_IEEE80211: 18054359Sroberto#define V(type) ((struct type *)(&ifan[1])) 181280849Scy ifan = (struct if_announcemsghdr *)rtm; 18254359Sroberto switch (ifan->ifan_what) { 183280849Scy case RTM_IEEE80211_LEAVE: 184280849Scy if (verbose) 185280849Scy printf("%.19s %s station leave", cnow, 186280849Scy ether_sprintf(V(ieee80211_leave_event)->iev_addr)); 18754359Sroberto wds_leave(V(ieee80211_leave_event)->iev_addr); 188280849Scy if (verbose) 18954359Sroberto printf("\n"); 19054359Sroberto break; 19154359Sroberto case RTM_IEEE80211_WDS: 19254359Sroberto if (verbose) 19354359Sroberto printf("%.19s %s wds discovery", cnow, 19454359Sroberto ether_sprintf(V(ieee80211_wds_event)->iev_addr)); 19554359Sroberto /* XXX wlan0 */ 196280849Scy wds_discovery("wlan0", V(ieee80211_wds_event)->iev_addr); 197280849Scy if (verbose) 198280849Scy printf("\n"); 199280849Scy break; 200280849Scy case RTM_IEEE80211_ASSOC: 20154359Sroberto case RTM_IEEE80211_REASSOC: 20254359Sroberto case RTM_IEEE80211_DISASSOC: 203280849Scy case RTM_IEEE80211_JOIN: 20454359Sroberto case RTM_IEEE80211_REJOIN: 20554359Sroberto case RTM_IEEE80211_SCAN: 206280849Scy case RTM_IEEE80211_REPLAY: 207280849Scy case RTM_IEEE80211_MICHAEL: 208280849Scy break; 209280849Scy default: 210280849Scy if (verbose) 211280849Scy printf("%.19s RTM_IEEE80211: if# %d, what: #%d\n", cnow, 212280849Scy ifan->ifan_index, ifan->ifan_what); 213280849Scy break; 21454359Sroberto } 21554359Sroberto break; 21654359Sroberto#undef V 21754359Sroberto } 21854359Sroberto} 219280849Scy 220280849Scystatic void 22154359Srobertowds_discovery(const char *ifname, const uint8_t bssid[IEEE80211_ADDR_LEN]) 22254359Sroberto{ 223280849Scy struct wds *p; 22454359Sroberto 22554359Sroberto for (p = wds; p != NULL; p = p->next) 22654359Sroberto if (IEEE80211_ADDR_EQ(p->bssid, bssid)) { 22754359Sroberto if (verbose) 22854359Sroberto printf(" (already created)"); 22954359Sroberto return; 23054359Sroberto } 23154359Sroberto p = malloc(sizeof(struct wds)); 23254359Sroberto if (p == NULL) { 23354359Sroberto warn("%s: malloc", __func__); 23454359Sroberto return; 235182007Sroberto } 23654359Sroberto IEEE80211_ADDR_COPY(p->bssid, bssid); 23754359Sroberto /* XXX mv0: no sysctl to find parent device */ 23854359Sroberto if (wds_vap_create(parent, p) >= 0) { 239280849Scy char cmd[1024]; 240280849Scy int status; 241280849Scy 242280849Scy /* 243280849Scy * Add to table. 244280849Scy */ 24554359Sroberto p->next = wds; 24654359Sroberto wds = p; 24754359Sroberto if (verbose) 24854359Sroberto printf(" (create %s)", p->ifname); 24954359Sroberto /* 250280849Scy * XXX launch script to setup bridge, etc. 25154359Sroberto */ 25254359Sroberto snprintf(cmd, sizeof(cmd), "%s %s %s", 25354359Sroberto script, p->ifname, bridge); 25454359Sroberto status = system(cmd); 25554359Sroberto if (status) 25654359Sroberto warnx("vap setup script %s exited with status %d\n", 25754359Sroberto script, status); 258280849Scy } else 259280849Scy free(p); 260280849Scy} 261280849Scy 262280849Scystatic void 263280849Scywds_destroy(const char *ifname) 264280849Scy{ 265280849Scy struct wds *p, **pp; 266280849Scy 267282408Scy for (pp = &wds; (p = *pp) != NULL; pp = &p->next) 268282408Scy if (strncmp(p->ifname, ifname, IFNAMSIZ) == 0) 269280849Scy break; 270280849Scy /* XXX check for device directly */ 271280849Scy if (p == NULL) /* not ours/known */ 272282408Scy return; 273282408Scy *pp = p->next; 274280849Scy if (wds_vap_destroy(p->ifname) >= 0) 275280849Scy if (verbose) 276280849Scy printf(" (wds vap destroyed)"); 277280849Scy free(p); 278280849Scy} 279280849Scy 280280849Scystatic void 281280849Scywds_leave(const uint8_t bssid[IEEE80211_ADDR_LEN]) 282280849Scy{ 283280849Scy struct wds *p, **pp; 284280849Scy 285280849Scy for (pp = &wds; (p = *pp) != NULL; pp = &p->next) 286280849Scy if (IEEE80211_ADDR_EQ(p->bssid, bssid)) 287280849Scy break; 288280849Scy /* XXX fall back to check device */ 289280849Scy if (p == NULL) /* not ours/known */ 290280849Scy return; 291280849Scy *pp = p->next; 292280849Scy if (wds_vap_destroy(p->ifname) >= 0) 293280849Scy printf(" (wds vap destroyed)"); 294280849Scy free(p); 295280849Scy} 296280849Scy 297280849Scystatic int 298280849Scywds_vap_create(const char *parent, struct wds *p) 299280849Scy{ 300280849Scy struct ieee80211_clone_params cp; 301280849Scy struct ifreq ifr; 302280849Scy int s, status; 303280849Scy 304280849Scy memset(&cp, 0, sizeof(cp)); 305280849Scy strncpy(cp.icp_parent, parent, IFNAMSIZ); 306280849Scy cp.icp_opmode = IEEE80211_M_WDS; 307280849Scy IEEE80211_ADDR_COPY(cp.icp_bssid, p->bssid); 308280849Scy 309280849Scy memset(&ifr, 0, sizeof(ifr)); 310280849Scy strncpy(ifr.ifr_name, "wlan", IFNAMSIZ); 311280849Scy ifr.ifr_data = (void *) &cp; 312280849Scy 313280849Scy status = -1; 314280849Scy s = socket(AF_INET, SOCK_DGRAM, 0); 315280849Scy if (s >= 0) { 316280849Scy if (ioctl(s, SIOCIFCREATE2, &ifr) >= 0) { 31754359Sroberto strlcpy(p->ifname, ifr.ifr_name, IFNAMSIZ); 318280849Scy status = 0; 319280849Scy } else { 320280849Scy warn("SIOCIFCREATE2(" 321280849Scy "mode %u flags 0x%x parent %s bssid %s)", 322280849Scy cp.icp_opmode, cp.icp_flags, parent, 32354359Sroberto ether_sprintf(cp.icp_bssid)); 324280849Scy } 325280849Scy close(s); 32654359Sroberto } else 32754359Sroberto warn("socket(SOCK_DRAGM)"); 328280849Scy return status; 32954359Sroberto} 330280849Scy 331280849Scystatic int 332280849Scywds_vap_destroy(const char *ifname) 333280849Scy{ 334280849Scy struct ieee80211req ifr; 33554359Sroberto int s, status; 336280849Scy 33754359Sroberto s = socket(AF_INET, SOCK_DGRAM, 0); 33854359Sroberto if (s < 0) { 339280849Scy warn("socket(SOCK_DRAGM)"); 340280849Scy return -1; 34154359Sroberto } 34254359Sroberto memset(&ifr, 0, sizeof(ifr)); 34354359Sroberto strncpy(ifr.i_name, ifname, IFNAMSIZ); 34454359Sroberto if (ioctl(s, SIOCIFDESTROY, &ifr) < 0) { 34554359Sroberto warn("ioctl(SIOCIFDESTROY)"); 34654359Sroberto status = -1; 347358659Scy } else 348358659Scy status = 0; 34954359Sroberto close(s); 350280849Scy return status; 351280849Scy} 352280849Scy