wlanwds.c revision 191127
1/*- 2 * Copyright (c) 2006-2009 Sam Leffler, Errno Consulting 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer, 10 * without modification. 11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 13 * redistribution must be conditioned upon including a substantially 14 * similar Disclaimer requirement for further binary redistribution. 15 * 16 * NO WARRANTY 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 20 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 21 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 22 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 * THE POSSIBILITY OF SUCH DAMAGES. 28 * 29 * $FreeBSD: head/tools/tools/net80211/wlanwds/wlanwds.c 191127 2009-04-15 22:10:33Z sam $ 30 */ 31 32/* 33 * Test app to demonstrate how to handle dynamic WDS links: 34 * o monitor 802.11 events for wds discovery events 35 * o create wds vap's in response to wds discovery events 36 * and launch a script to handle adding the vap to the 37 * bridge, etc. 38 * o destroy wds vap's when station leaves 39 * 40 * Note we query only internal state which means if we don't see 41 * a vap created we won't handle leave/delete properly. Also there 42 * are several fixed pathnames/strings. 43 * 44 * Code liberaly swiped from wlanwatch; probably should nuke printfs. 45 */ 46#include <sys/param.h> 47#include <sys/file.h> 48#include <sys/socket.h> 49#include <sys/ioctl.h> 50#include <sys/sysctl.h> 51#include <sys/types.h> 52 53#include <net/if.h> 54#include "net/if_media.h" 55#include <net/route.h> 56#include <net/if_dl.h> 57#include <netinet/in.h> 58#include <netinet/if_ether.h> 59#include <netatalk/at.h> 60#include "net80211/ieee80211_ioctl.h" 61#include "net80211/ieee80211_freebsd.h" 62#include <arpa/inet.h> 63#include <netdb.h> 64 65#include <ctype.h> 66#include <err.h> 67#include <errno.h> 68#include <paths.h> 69#include <stdio.h> 70#include <stdlib.h> 71#include <string.h> 72#include <sysexits.h> 73#include <unistd.h> 74#include <ifaddrs.h> 75 76#define IEEE80211_ADDR_EQ(a1,a2) (memcmp(a1,a2,IEEE80211_ADDR_LEN) == 0) 77#define IEEE80211_ADDR_COPY(dst,src) memcpy(dst,src,IEEE80211_ADDR_LEN) 78 79struct wds { 80 struct wds *next; 81 uint8_t bssid[IEEE80211_ADDR_LEN]; /* bssid of associated sta */ 82 char ifname[IFNAMSIZ]; /* vap interface name */ 83}; 84static struct wds *wds; 85 86static const char *script = "/usr/local/bin/wdsup"; 87static int verbose = 0; 88static int discover_on_join = 0; 89 90static void handle_rtmsg(struct rt_msghdr *rtm, int msglen); 91static void wds_discovery(const char *ifname, 92 const uint8_t bssid[IEEE80211_ADDR_LEN]); 93static void wds_destroy(const char *ifname); 94static void wds_leave(const uint8_t bssid[IEEE80211_ADDR_LEN]); 95static int wds_vap_create(const char *ifname, struct wds *); 96static int wds_vap_destroy(const char *ifname); 97 98int 99main(int argc, char *argv[]) 100{ 101 int n, s, c; 102 char msg[2048]; 103 104 while ((c = getopt(argc, argv, "js:vn")) != -1) 105 switch (c) { 106 case 'j': 107 discover_on_join = 1; 108 break; 109 case 's': 110 script = optarg; 111 break; 112 case 'v': 113 verbose = 1; 114 break; 115 case '?': 116 errx(1, "usage: %s [-s <set_scriptname>]\n" 117 " [-v (for verbose)]\n" 118 " [-j (act on join/rejoin events)]\n", argv[0]); 119 /*NOTREACHED*/ 120 } 121 122 s = socket(PF_ROUTE, SOCK_RAW, 0); 123 if (s < 0) 124 err(EX_OSERR, "socket"); 125 for(;;) { 126 n = read(s, msg, 2048); 127 handle_rtmsg((struct rt_msghdr *)msg, n); 128 } 129 return 0; 130} 131 132static const char * 133ether_sprintf(const uint8_t mac[6]) 134{ 135 static char buf[32]; 136 137 snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x", 138 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 139 return buf; 140} 141 142static void 143handle_rtmsg(struct rt_msghdr *rtm, int msglen) 144{ 145 struct if_announcemsghdr *ifan; 146 time_t now = time(NULL); 147 char *cnow = ctime(&now); 148 149 if (rtm->rtm_version != RTM_VERSION) { 150 (void) printf("routing message version %d not understood\n", 151 rtm->rtm_version); 152 return; 153 } 154 switch (rtm->rtm_type) { 155 case RTM_IFANNOUNCE: 156 ifan = (struct if_announcemsghdr *)rtm; 157 if (!verbose) 158 break; 159 printf("%.19s RTM_IFANNOUNCE: if# %d, what: ", 160 cnow, ifan->ifan_index); 161 switch (ifan->ifan_what) { 162 case IFAN_ARRIVAL: 163 printf("arrival"); 164 break; 165 case IFAN_DEPARTURE: 166 printf("departure"); 167 wds_destroy(ifan->ifan_name); 168 break; 169 default: 170 printf("#%d", ifan->ifan_what); 171 break; 172 } 173 printf("\n"); 174 break; 175 case RTM_IEEE80211: 176#define V(type) ((struct type *)(&ifan[1])) 177 ifan = (struct if_announcemsghdr *)rtm; 178 switch (ifan->ifan_what) { 179 case RTM_IEEE80211_LEAVE: 180 if (verbose) 181 printf("%.19s %s station leave", cnow, 182 ether_sprintf(V(ieee80211_leave_event)->iev_addr)); 183 wds_leave(V(ieee80211_leave_event)->iev_addr); 184 if (verbose) 185 printf("\n"); 186 break; 187 case RTM_IEEE80211_JOIN: 188 case RTM_IEEE80211_REJOIN: 189 if (!discover_on_join) 190 break; 191 /* fall thru... */ 192 case RTM_IEEE80211_WDS: 193 if (verbose) 194 printf("%.19s %s wds discovery", cnow, 195 ether_sprintf(V(ieee80211_wds_event)->iev_addr)); 196 wds_discovery(ifan->ifan_name, 197 V(ieee80211_wds_event)->iev_addr); 198 if (verbose) 199 printf("\n"); 200 break; 201 case RTM_IEEE80211_ASSOC: 202 case RTM_IEEE80211_REASSOC: 203 case RTM_IEEE80211_DISASSOC: 204 case RTM_IEEE80211_SCAN: 205 case RTM_IEEE80211_REPLAY: 206 case RTM_IEEE80211_MICHAEL: 207 break; 208 default: 209 if (verbose) 210 printf("%.19s RTM_IEEE80211: if# %d, what: #%d\n", cnow, 211 ifan->ifan_index, ifan->ifan_what); 212 break; 213 } 214 break; 215#undef V 216 } 217} 218 219static void 220wds_discovery(const char *ifname, const uint8_t bssid[IEEE80211_ADDR_LEN]) 221{ 222 struct wds *p; 223 char oid[256], parent[256]; 224 int parentlen; 225 226 for (p = wds; p != NULL; p = p->next) 227 if (IEEE80211_ADDR_EQ(p->bssid, bssid)) { 228 if (verbose) 229 printf(" (already created)"); 230 return; 231 } 232 233 /* fetch parent interface name */ 234 snprintf(oid, sizeof(oid), "net.wlan.%s.%%parent", ifname+4); 235 parentlen = sizeof(parent); 236 if (sysctlbyname(oid, parent, &parentlen, NULL, 0) < 0) { 237 warn("%s: no pointer to parent interface", __func__); 238 return; 239 } 240 parent[parentlen] = '\0'; 241 242 p = malloc(sizeof(struct wds)); 243 if (p == NULL) { 244 warn("%s: malloc", __func__); 245 return; 246 } 247 IEEE80211_ADDR_COPY(p->bssid, bssid); 248 if (wds_vap_create(parent, p) >= 0) { 249 char cmd[1024]; 250 int status; 251 252 /* 253 * Add to table. 254 */ 255 p->next = wds; 256 wds = p; 257 if (verbose) 258 printf(" (create %s)", p->ifname); 259 /* 260 * XXX launch script to setup bridge, etc. 261 */ 262 snprintf(cmd, sizeof(cmd), "%s %s", script, p->ifname); 263 status = system(cmd); 264 if (status) 265 warnx("vap setup script %s exited with status %d\n", 266 script, status); 267 } else 268 free(p); 269} 270 271static void 272wds_destroy(const char *ifname) 273{ 274 struct wds *p, **pp; 275 276 for (pp = &wds; (p = *pp) != NULL; pp = &p->next) 277 if (strncmp(p->ifname, ifname, IFNAMSIZ) == 0) 278 break; 279 /* XXX check for device directly */ 280 if (p == NULL) /* not ours/known */ 281 return; 282 *pp = p->next; 283 if (wds_vap_destroy(p->ifname) >= 0) 284 if (verbose) 285 printf(" (wds vap destroyed)"); 286 free(p); 287} 288 289static void 290wds_leave(const uint8_t bssid[IEEE80211_ADDR_LEN]) 291{ 292 struct wds *p, **pp; 293 294 for (pp = &wds; (p = *pp) != NULL; pp = &p->next) 295 if (IEEE80211_ADDR_EQ(p->bssid, bssid)) 296 break; 297 /* XXX fall back to check device */ 298 if (p == NULL) /* not ours/known */ 299 return; 300 *pp = p->next; 301 if (wds_vap_destroy(p->ifname) >= 0) 302 printf(" (wds vap destroyed)"); 303 free(p); 304} 305 306static int 307wds_vap_create(const char *parent, struct wds *p) 308{ 309 struct ieee80211_clone_params cp; 310 struct ifreq ifr; 311 int s, status; 312 313 memset(&cp, 0, sizeof(cp)); 314 strncpy(cp.icp_parent, parent, IFNAMSIZ); 315 cp.icp_opmode = IEEE80211_M_WDS; 316 IEEE80211_ADDR_COPY(cp.icp_bssid, p->bssid); 317 318 memset(&ifr, 0, sizeof(ifr)); 319 strncpy(ifr.ifr_name, "wlan", IFNAMSIZ); 320 ifr.ifr_data = (void *) &cp; 321 322 status = -1; 323 s = socket(AF_INET, SOCK_DGRAM, 0); 324 if (s >= 0) { 325 if (ioctl(s, SIOCIFCREATE2, &ifr) >= 0) { 326 strlcpy(p->ifname, ifr.ifr_name, IFNAMSIZ); 327 status = 0; 328 } else { 329 warn("SIOCIFCREATE2(" 330 "mode %u flags 0x%x parent %s bssid %s)", 331 cp.icp_opmode, cp.icp_flags, parent, 332 ether_sprintf(cp.icp_bssid)); 333 } 334 close(s); 335 } else 336 warn("socket(SOCK_DRAGM)"); 337 return status; 338} 339 340static int 341wds_vap_destroy(const char *ifname) 342{ 343 struct ieee80211req ifr; 344 int s, status; 345 346 s = socket(AF_INET, SOCK_DGRAM, 0); 347 if (s < 0) { 348 warn("socket(SOCK_DRAGM)"); 349 return -1; 350 } 351 memset(&ifr, 0, sizeof(ifr)); 352 strncpy(ifr.i_name, ifname, IFNAMSIZ); 353 if (ioctl(s, SIOCIFDESTROY, &ifr) < 0) { 354 warn("ioctl(SIOCIFDESTROY)"); 355 status = -1; 356 } else 357 status = 0; 358 close(s); 359 return status; 360} 361