wlanwds.c revision 178360
1/*- 2 * Copyright (c) 2006-2007 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 178360 2008-04-20 20:43:13Z 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. Some require fixing 43 * kernel support (e.g. sysctl to find parent device of a vap). 44 * 45 * Code liberaly swiped from wlanwatch; probably should nuke printfs. 46 */ 47#include <sys/param.h> 48#include <sys/file.h> 49#include <sys/socket.h> 50#include <sys/ioctl.h> 51#include <sys/sysctl.h> 52#include <sys/types.h> 53 54#include <net/if.h> 55#include "net/if_media.h" 56#include <net/route.h> 57#include <net/if_dl.h> 58#include <netinet/in.h> 59#include <netinet/if_ether.h> 60#include <netatalk/at.h> 61#include "net80211/ieee80211_ioctl.h" 62#include "net80211/ieee80211_freebsd.h" 63#include <arpa/inet.h> 64#include <netdb.h> 65 66#include <ctype.h> 67#include <err.h> 68#include <errno.h> 69#include <paths.h> 70#include <stdio.h> 71#include <stdlib.h> 72#include <string.h> 73#include <sysexits.h> 74#include <unistd.h> 75#include <ifaddrs.h> 76 77#define IEEE80211_ADDR_EQ(a1,a2) (memcmp(a1,a2,IEEE80211_ADDR_LEN) == 0) 78#define IEEE80211_ADDR_COPY(dst,src) memcpy(dst,src,IEEE80211_ADDR_LEN) 79 80struct wds { 81 struct wds *next; 82 uint8_t bssid[IEEE80211_ADDR_LEN]; /* bssid of associated sta */ 83 char ifname[IFNAMSIZ]; /* vap interface name */ 84}; 85static struct wds *wds; 86 87static const char *bridge = "bridge0"; 88static const char *parent = "mv0"; /* XXX no sysctl to find this */ 89static const char *script = "/usr/local/bin/wdsup"; 90static int verbose = 0; 91 92static void handle_rtmsg(struct rt_msghdr *rtm, int msglen); 93static void wds_discovery(const char *ifname, 94 const uint8_t bssid[IEEE80211_ADDR_LEN]); 95static void wds_destroy(const char *ifname); 96static void wds_leave(const uint8_t bssid[IEEE80211_ADDR_LEN]); 97static int wds_vap_create(const char *ifname, struct wds *); 98static int wds_vap_destroy(const char *ifname); 99 100int 101main(int argc, char *argv[]) 102{ 103 int n, s, c; 104 char msg[2048]; 105 106 while ((c = getopt(argc, argv, "b:p:s:vn")) != -1) 107 switch (c) { 108 case 'b': 109 bridge = optarg; 110 break; 111 case 'p': 112 parent = optarg; 113 break; 114 case 's': 115 script = optarg; 116 break; 117 case 'v': 118 verbose = 1; 119 break; 120 case '?': 121 errx(1, "usage: %s [-b <bridgename>] [-p <parentname>] [-s <set_scriptname>]\n" 122 " [-v (for verbose)]\n", argv[0]); 123 /*NOTREACHED*/ 124 } 125 126 s = socket(PF_ROUTE, SOCK_RAW, 0); 127 if (s < 0) 128 err(EX_OSERR, "socket"); 129 for(;;) { 130 n = read(s, msg, 2048); 131 handle_rtmsg((struct rt_msghdr *)msg, n); 132 } 133 return 0; 134} 135 136static const char * 137ether_sprintf(const uint8_t mac[6]) 138{ 139 static char buf[32]; 140 141 snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x", 142 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 143 return buf; 144} 145 146static void 147handle_rtmsg(struct rt_msghdr *rtm, int msglen) 148{ 149 struct if_announcemsghdr *ifan; 150 time_t now = time(NULL); 151 char *cnow = ctime(&now); 152 153 if (rtm->rtm_version != RTM_VERSION) { 154 (void) printf("routing message version %d not understood\n", 155 rtm->rtm_version); 156 return; 157 } 158 switch (rtm->rtm_type) { 159 case RTM_IFANNOUNCE: 160 ifan = (struct if_announcemsghdr *)rtm; 161 if (!verbose) 162 break; 163 printf("%.19s RTM_IFANNOUNCE: if# %d, what: ", 164 cnow, ifan->ifan_index); 165 switch (ifan->ifan_what) { 166 case IFAN_ARRIVAL: 167 printf("arrival"); 168 break; 169 case IFAN_DEPARTURE: 170 printf("departure"); 171 wds_destroy(ifan->ifan_name); 172 break; 173 default: 174 printf("#%d", ifan->ifan_what); 175 break; 176 } 177 printf("\n"); 178 break; 179 case RTM_IEEE80211: 180#define V(type) ((struct type *)(&ifan[1])) 181 ifan = (struct if_announcemsghdr *)rtm; 182 switch (ifan->ifan_what) { 183 case RTM_IEEE80211_LEAVE: 184 if (verbose) 185 printf("%.19s %s station leave", cnow, 186 ether_sprintf(V(ieee80211_leave_event)->iev_addr)); 187 wds_leave(V(ieee80211_leave_event)->iev_addr); 188 if (verbose) 189 printf("\n"); 190 break; 191 case RTM_IEEE80211_WDS: 192 if (verbose) 193 printf("%.19s %s wds discovery", cnow, 194 ether_sprintf(V(ieee80211_wds_event)->iev_addr)); 195 /* XXX wlan0 */ 196 wds_discovery("wlan0", V(ieee80211_wds_event)->iev_addr); 197 if (verbose) 198 printf("\n"); 199 break; 200 case RTM_IEEE80211_ASSOC: 201 case RTM_IEEE80211_REASSOC: 202 case RTM_IEEE80211_DISASSOC: 203 case RTM_IEEE80211_JOIN: 204 case RTM_IEEE80211_REJOIN: 205 case RTM_IEEE80211_SCAN: 206 case RTM_IEEE80211_REPLAY: 207 case RTM_IEEE80211_MICHAEL: 208 break; 209 default: 210 if (verbose) 211 printf("%.19s RTM_IEEE80211: if# %d, what: #%d\n", cnow, 212 ifan->ifan_index, ifan->ifan_what); 213 break; 214 } 215 break; 216#undef V 217 } 218} 219 220static void 221wds_discovery(const char *ifname, const uint8_t bssid[IEEE80211_ADDR_LEN]) 222{ 223 struct wds *p; 224 225 for (p = wds; p != NULL; p = p->next) 226 if (IEEE80211_ADDR_EQ(p->bssid, bssid)) { 227 if (verbose) 228 printf(" (already created)"); 229 return; 230 } 231 p = malloc(sizeof(struct wds)); 232 if (p == NULL) { 233 warn("%s: malloc", __func__); 234 return; 235 } 236 IEEE80211_ADDR_COPY(p->bssid, bssid); 237 /* XXX mv0: no sysctl to find parent device */ 238 if (wds_vap_create(parent, p) >= 0) { 239 char cmd[1024]; 240 int status; 241 242 /* 243 * Add to table. 244 */ 245 p->next = wds; 246 wds = p; 247 if (verbose) 248 printf(" (create %s)", p->ifname); 249 /* 250 * XXX launch script to setup bridge, etc. 251 */ 252 snprintf(cmd, sizeof(cmd), "%s %s %s", 253 script, p->ifname, bridge); 254 status = system(cmd); 255 if (status) 256 warnx("vap setup script %s exited with status %d\n", 257 script, status); 258 } else 259 free(p); 260} 261 262static void 263wds_destroy(const char *ifname) 264{ 265 struct wds *p, **pp; 266 267 for (pp = &wds; (p = *pp) != NULL; pp = &p->next) 268 if (strncmp(p->ifname, ifname, IFNAMSIZ) == 0) 269 break; 270 /* XXX check for device directly */ 271 if (p == NULL) /* not ours/known */ 272 return; 273 *pp = p->next; 274 if (wds_vap_destroy(p->ifname) >= 0) 275 if (verbose) 276 printf(" (wds vap destroyed)"); 277 free(p); 278} 279 280static void 281wds_leave(const uint8_t bssid[IEEE80211_ADDR_LEN]) 282{ 283 struct wds *p, **pp; 284 285 for (pp = &wds; (p = *pp) != NULL; pp = &p->next) 286 if (IEEE80211_ADDR_EQ(p->bssid, bssid)) 287 break; 288 /* XXX fall back to check device */ 289 if (p == NULL) /* not ours/known */ 290 return; 291 *pp = p->next; 292 if (wds_vap_destroy(p->ifname) >= 0) 293 printf(" (wds vap destroyed)"); 294 free(p); 295} 296 297static int 298wds_vap_create(const char *parent, struct wds *p) 299{ 300 struct ieee80211_clone_params cp; 301 struct ifreq ifr; 302 int s, status; 303 304 memset(&cp, 0, sizeof(cp)); 305 strncpy(cp.icp_parent, parent, IFNAMSIZ); 306 cp.icp_opmode = IEEE80211_M_WDS; 307 IEEE80211_ADDR_COPY(cp.icp_bssid, p->bssid); 308 309 memset(&ifr, 0, sizeof(ifr)); 310 strncpy(ifr.ifr_name, "wlan", IFNAMSIZ); 311 ifr.ifr_data = (void *) &cp; 312 313 status = -1; 314 s = socket(AF_INET, SOCK_DGRAM, 0); 315 if (s >= 0) { 316 if (ioctl(s, SIOCIFCREATE2, &ifr) >= 0) { 317 strlcpy(p->ifname, ifr.ifr_name, IFNAMSIZ); 318 status = 0; 319 } else { 320 warn("SIOCIFCREATE2(" 321 "mode %u flags 0x%x parent %s bssid %s)", 322 cp.icp_opmode, cp.icp_flags, parent, 323 ether_sprintf(cp.icp_bssid)); 324 } 325 close(s); 326 } else 327 warn("socket(SOCK_DRAGM)"); 328 return status; 329} 330 331static int 332wds_vap_destroy(const char *ifname) 333{ 334 struct ieee80211req ifr; 335 int s, status; 336 337 s = socket(AF_INET, SOCK_DGRAM, 0); 338 if (s < 0) { 339 warn("socket(SOCK_DRAGM)"); 340 return -1; 341 } 342 memset(&ifr, 0, sizeof(ifr)); 343 strncpy(ifr.i_name, ifname, IFNAMSIZ); 344 if (ioctl(s, SIOCIFDESTROY, &ifr) < 0) { 345 warn("ioctl(SIOCIFDESTROY)"); 346 status = -1; 347 } else 348 status = 0; 349 close(s); 350 return status; 351} 352