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$ 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 80 struct wds { 81 struct wds *next; 82 uint8_t bssid[IEEE80211_ADDR_LEN]; /* bssid of associated sta */ 83 char ifname[IFNAMSIZ]; /* vap interface name */ 84 }; 85 static struct wds *wds; 86 87 static const char *bridge = "bridge0"; 88 static const char *parent = "mv0"; /* XXX no sysctl to find this */ 89 static const char *script = "/usr/local/bin/wdsup"; 90 static int verbose = 0; 91 92 static void handle_rtmsg(struct rt_msghdr *rtm, int msglen); 93 static void wds_discovery(const char *ifname, 94 const uint8_t bssid[IEEE80211_ADDR_LEN]); 95 static void wds_destroy(const char *ifname); 96 static void wds_leave(const uint8_t bssid[IEEE80211_ADDR_LEN]); 97 static int wds_vap_create(const char *ifname, struct wds *); 98 static int wds_vap_destroy(const char *ifname); 99 100 int 101 main(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 136 static const char * 137 ether_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 146 static void 147 handle_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 220 static void 221 wds_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 262 static void 263 wds_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 280 static void 281 wds_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 297 static int 298 wds_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 331 static int 332 wds_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