1178360Ssam/*- 2191127Ssam * Copyright (c) 2006-2009 Sam Leffler, Errno Consulting 3178360Ssam * All rights reserved. 4178360Ssam * 5178360Ssam * Redistribution and use in source and binary forms, with or without 6178360Ssam * modification, are permitted provided that the following conditions 7178360Ssam * are met: 8178360Ssam * 1. Redistributions of source code must retain the above copyright 9178360Ssam * notice, this list of conditions and the following disclaimer, 10178360Ssam * without modification. 11178360Ssam * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12178360Ssam * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 13178360Ssam * redistribution must be conditioned upon including a substantially 14178360Ssam * similar Disclaimer requirement for further binary redistribution. 15178360Ssam * 16178360Ssam * NO WARRANTY 17178360Ssam * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18178360Ssam * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19178360Ssam * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 20178360Ssam * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 21178360Ssam * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 22178360Ssam * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23178360Ssam * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24178360Ssam * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25178360Ssam * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26178360Ssam * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27178360Ssam * THE POSSIBILITY OF SUCH DAMAGES. 28178360Ssam * 29178360Ssam * $FreeBSD$ 30178360Ssam */ 31178360Ssam 32178360Ssam/* 33178360Ssam * Test app to demonstrate how to handle dynamic WDS links: 34178360Ssam * o monitor 802.11 events for wds discovery events 35178360Ssam * o create wds vap's in response to wds discovery events 36178360Ssam * and launch a script to handle adding the vap to the 37178360Ssam * bridge, etc. 38178360Ssam * o destroy wds vap's when station leaves 39178360Ssam */ 40178360Ssam#include <sys/param.h> 41178360Ssam#include <sys/file.h> 42178360Ssam#include <sys/socket.h> 43178360Ssam#include <sys/ioctl.h> 44178360Ssam#include <sys/sysctl.h> 45178360Ssam#include <sys/types.h> 46178360Ssam 47178360Ssam#include <net/if.h> 48178360Ssam#include "net/if_media.h" 49178360Ssam#include <net/route.h> 50178360Ssam#include <net/if_dl.h> 51178360Ssam#include <netinet/in.h> 52178360Ssam#include <netinet/if_ether.h> 53178360Ssam#include "net80211/ieee80211_ioctl.h" 54178360Ssam#include "net80211/ieee80211_freebsd.h" 55178360Ssam#include <arpa/inet.h> 56178360Ssam#include <netdb.h> 57178360Ssam 58178360Ssam#include <ctype.h> 59178360Ssam#include <err.h> 60178360Ssam#include <errno.h> 61178360Ssam#include <paths.h> 62191247Ssam#include <stdarg.h> 63178360Ssam#include <stdio.h> 64178360Ssam#include <stdlib.h> 65178360Ssam#include <string.h> 66178360Ssam#include <sysexits.h> 67191247Ssam#include <syslog.h> 68178360Ssam#include <unistd.h> 69178360Ssam#include <ifaddrs.h> 70178360Ssam 71178360Ssam#define IEEE80211_ADDR_EQ(a1,a2) (memcmp(a1,a2,IEEE80211_ADDR_LEN) == 0) 72178360Ssam#define IEEE80211_ADDR_COPY(dst,src) memcpy(dst,src,IEEE80211_ADDR_LEN) 73178360Ssam 74178360Ssamstruct wds { 75178360Ssam struct wds *next; 76178360Ssam uint8_t bssid[IEEE80211_ADDR_LEN]; /* bssid of associated sta */ 77178360Ssam char ifname[IFNAMSIZ]; /* vap interface name */ 78178360Ssam}; 79178360Ssamstatic struct wds *wds; 80178360Ssam 81191247Ssamstatic const char *script = NULL; 82191247Ssamstatic char **ifnets; 83191247Ssamstatic int nifnets = 0; 84178360Ssamstatic int verbose = 0; 85191127Ssamstatic int discover_on_join = 0; 86178360Ssam 87191247Ssamstatic void scanforvaps(int s); 88191247Ssamstatic void handle_rtmsg(struct rt_msghdr *rtm, ssize_t msglen); 89178360Ssamstatic void wds_discovery(const char *ifname, 90178360Ssam const uint8_t bssid[IEEE80211_ADDR_LEN]); 91178360Ssamstatic void wds_destroy(const char *ifname); 92178360Ssamstatic void wds_leave(const uint8_t bssid[IEEE80211_ADDR_LEN]); 93178360Ssamstatic int wds_vap_create(const char *ifname, struct wds *); 94178360Ssamstatic int wds_vap_destroy(const char *ifname); 95178360Ssam 96191247Ssamstatic void 97191247Ssamusage(const char *progname) 98191247Ssam{ 99191247Ssam fprintf(stderr, "usage: %s [-fjtv] [-P pidfile] [-s <set_scriptname>] [ifnet0 ... | any]\n", 100191247Ssam progname); 101191247Ssam exit(-1); 102191247Ssam} 103191247Ssam 104178360Ssamint 105178360Ssammain(int argc, char *argv[]) 106178360Ssam{ 107191247Ssam const char *progname = argv[0]; 108191247Ssam const char *pidfile = NULL; 109191247Ssam int s, c, logmask, bg = 1; 110178360Ssam char msg[2048]; 111178360Ssam 112191247Ssam logmask = LOG_UPTO(LOG_INFO); 113191247Ssam while ((c = getopt(argc, argv, "fjP:s:tv")) != -1) 114178360Ssam switch (c) { 115191247Ssam case 'f': 116191247Ssam bg = 0; 117191247Ssam break; 118191127Ssam case 'j': 119191127Ssam discover_on_join = 1; 120178360Ssam break; 121191247Ssam case 'P': 122191247Ssam pidfile = optarg; 123191247Ssam break; 124178360Ssam case 's': 125178360Ssam script = optarg; 126178360Ssam break; 127191247Ssam case 't': 128191247Ssam logmask = LOG_UPTO(LOG_ERR); 129191247Ssam break; 130178360Ssam case 'v': 131191247Ssam logmask = LOG_UPTO(LOG_DEBUG); 132178360Ssam break; 133178360Ssam case '?': 134191247Ssam usage(progname); 135178360Ssam /*NOTREACHED*/ 136178360Ssam } 137191247Ssam argc -= optind, argv += optind; 138191247Ssam if (argc == 0) { 139191247Ssam fprintf(stderr, "%s: no ifnet's specified to monitor\n", 140191247Ssam progname); 141191247Ssam usage(progname); 142191247Ssam } 143191247Ssam ifnets = argv; 144191247Ssam nifnets = argc; 145178360Ssam 146178360Ssam s = socket(PF_ROUTE, SOCK_RAW, 0); 147178360Ssam if (s < 0) 148178360Ssam err(EX_OSERR, "socket"); 149191247Ssam /* 150191247Ssam * Scan for inherited state. 151191247Ssam */ 152191247Ssam scanforvaps(s); 153191247Ssam 154191247Ssam /* XXX what directory to work in? */ 155191247Ssam if (bg && daemon(0, 0) < 0) 156191247Ssam err(EX_OSERR, "daemon"); 157191247Ssam 158191247Ssam openlog("wlanwds", LOG_PID | LOG_CONS, LOG_DAEMON); 159191247Ssam setlogmask(logmask); 160191247Ssam 161191247Ssam for (;;) { 162191247Ssam ssize_t n = read(s, msg, sizeof(msg)); 163178360Ssam handle_rtmsg((struct rt_msghdr *)msg, n); 164178360Ssam } 165178360Ssam return 0; 166178360Ssam} 167178360Ssam 168178360Ssamstatic const char * 169191247Ssamether_sprintf(const uint8_t mac[IEEE80211_ADDR_LEN]) 170178360Ssam{ 171178360Ssam static char buf[32]; 172178360Ssam 173178360Ssam snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x", 174178360Ssam mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 175178360Ssam return buf; 176178360Ssam} 177178360Ssam 178191247Ssam/* 179191247Ssam * Fetch a vap's parent ifnet name. 180191247Ssam */ 181191247Ssamstatic int 182191247Ssamgetparent(const char *ifname, char parent[IFNAMSIZ+1]) 183191247Ssam{ 184191247Ssam char oid[256]; 185191247Ssam int parentlen; 186191247Ssam 187191247Ssam /* fetch parent interface name */ 188191247Ssam snprintf(oid, sizeof(oid), "net.wlan.%s.%%parent", ifname+4); 189191247Ssam parentlen = IFNAMSIZ; 190191247Ssam if (sysctlbyname(oid, parent, &parentlen, NULL, 0) < 0) 191191247Ssam return -1; 192191247Ssam parent[parentlen] = '\0'; 193191247Ssam return 0; 194191247Ssam} 195191247Ssam 196191247Ssam/* 197191247Ssam * Check if the specified ifnet is one we're supposed to monitor. 198191247Ssam * The ifnet is assumed to be a vap; we find it's parent and check 199191247Ssam * it against the set of ifnet's specified on the command line. 200191247Ssam */ 201191247Ssamstatic int 202191247Ssamcheckifnet(const char *ifname, int complain) 203191247Ssam{ 204191247Ssam char parent[256]; 205191247Ssam int i; 206191247Ssam 207191247Ssam if (getparent(ifname, parent) < 0) { 208191247Ssam if (complain) 209191247Ssam syslog(LOG_ERR, 210191247Ssam "%s: no pointer to parent interface: %m", ifname); 211191247Ssam return 0; 212191247Ssam } 213191247Ssam 214191247Ssam for (i = 0; i < nifnets; i++) 215191247Ssam if (strcasecmp(ifnets[i], "any") == 0 || 216191247Ssam strcmp(ifnets[i], parent) == 0) 217191247Ssam return 1; 218191247Ssam syslog(LOG_DEBUG, "%s: parent %s not being monitored", ifname, parent); 219191247Ssam return 0; 220191247Ssam} 221191247Ssam 222191247Ssam/* 223191247Ssam * Return 1 if the specified ifnet is a WDS vap. 224191247Ssam */ 225191247Ssamstatic int 226191247Ssamiswdsvap(int s, const char *ifname) 227191247Ssam{ 228191247Ssam struct ifmediareq ifmr; 229191247Ssam 230191247Ssam memset(&ifmr, 0, sizeof(ifmr)); 231191247Ssam strncpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name)); 232191247Ssam if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) 233191247Ssam err(-1, "%s: cannot get media", ifname); 234191247Ssam return (ifmr.ifm_current & IFM_IEEE80211_WDS) != 0; 235191247Ssam} 236191247Ssam 237191247Ssam/* 238191247Ssam * Fetch the bssid for an ifnet. The caller is assumed 239191247Ssam * to have already verified this is possible. 240191247Ssam */ 241178360Ssamstatic void 242191247Ssamgetbssid(int s, const char *ifname, char bssid[IEEE80211_ADDR_LEN]) 243178360Ssam{ 244191247Ssam struct ieee80211req ireq; 245191247Ssam 246191247Ssam memset(&ireq, 0, sizeof(ireq)); 247191247Ssam strncpy(ireq.i_name, ifname, sizeof(ireq.i_name)); 248191247Ssam ireq.i_type = IEEE80211_IOC_BSSID; 249191247Ssam ireq.i_data = bssid; 250191247Ssam ireq.i_len = IEEE80211_ADDR_LEN; 251191247Ssam if (ioctl(s, SIOCG80211, &ireq) < 0) 252191247Ssam err(-1, "%s: cannot fetch bssid", ifname); 253191247Ssam} 254191247Ssam 255191247Ssam/* 256191247Ssam * Scan the system for WDS vaps associated with the ifnet's we're 257191247Ssam * supposed to monitor. Any vaps are added to our internal table 258191247Ssam * so we can find them (and destroy them) on station leave. 259191247Ssam */ 260191247Ssamstatic void 261191247Ssamscanforvaps(int s) 262191247Ssam{ 263191247Ssam char ifname[IFNAMSIZ+1]; 264191247Ssam char bssid[IEEE80211_ADDR_LEN]; 265191247Ssam int i; 266191247Ssam 267191247Ssam /* XXX brutal; should just walk sysctl tree */ 268191247Ssam for (i = 0; i < 128; i++) { 269191247Ssam snprintf(ifname, sizeof(ifname), "wlan%d", i); 270191247Ssam if (checkifnet(ifname, 0) && iswdsvap(s, ifname)) { 271191247Ssam struct wds *p = malloc(sizeof(struct wds)); 272191247Ssam if (p == NULL) 273191247Ssam err(-1, "%s: malloc failed", __func__); 274191247Ssam strlcpy(p->ifname, ifname, IFNAMSIZ); 275191247Ssam getbssid(s, ifname, p->bssid); 276191247Ssam p->next = wds; 277191247Ssam wds = p; 278191247Ssam 279191247Ssam syslog(LOG_INFO, "[%s] discover wds vap %s", 280191247Ssam ether_sprintf(bssid), ifname); 281191247Ssam } 282191247Ssam } 283191247Ssam} 284191247Ssam 285191247Ssam/* 286191247Ssam * Process a routing socket message. We handle messages related 287191247Ssam * to dynamic WDS: 288191247Ssam * o on WDS discovery (rx of a 4-address frame with DWDS enabled) 289191247Ssam * we create a WDS vap for the specified mac address 290191247Ssam * o on station leave we destroy any associated WDS vap 291191247Ssam * o on ifnet destroy we update state if this is manual destroy of 292191247Ssam * a WDS vap in our table 293191247Ssam * o if the -j option is supplied on the command line we create 294191247Ssam * WDS vaps on station join/rejoin, this is useful for some setups 295191247Ssam * where a WDS vap is required for 4-address traffic to flow 296191247Ssam */ 297191247Ssamstatic void 298191247Ssamhandle_rtmsg(struct rt_msghdr *rtm, ssize_t msglen) 299191247Ssam{ 300178360Ssam struct if_announcemsghdr *ifan; 301178360Ssam 302178360Ssam if (rtm->rtm_version != RTM_VERSION) { 303191247Ssam syslog(LOG_ERR, "routing message version %d not understood", 304178360Ssam rtm->rtm_version); 305178360Ssam return; 306178360Ssam } 307178360Ssam switch (rtm->rtm_type) { 308178360Ssam case RTM_IFANNOUNCE: 309178360Ssam ifan = (struct if_announcemsghdr *)rtm; 310178360Ssam switch (ifan->ifan_what) { 311178360Ssam case IFAN_ARRIVAL: 312191247Ssam syslog(LOG_DEBUG, 313191247Ssam "RTM_IFANNOUNCE: if# %d, what: arrival", 314191247Ssam ifan->ifan_index); 315178360Ssam break; 316178360Ssam case IFAN_DEPARTURE: 317191247Ssam syslog(LOG_DEBUG, 318191247Ssam "RTM_IFANNOUNCE: if# %d, what: departure", 319191247Ssam ifan->ifan_index); 320191247Ssam /* NB: ok to call w/ unmonitored ifnets */ 321178360Ssam wds_destroy(ifan->ifan_name); 322178360Ssam break; 323178360Ssam } 324178360Ssam break; 325178360Ssam case RTM_IEEE80211: 326178360Ssam#define V(type) ((struct type *)(&ifan[1])) 327178360Ssam ifan = (struct if_announcemsghdr *)rtm; 328178360Ssam switch (ifan->ifan_what) { 329191247Ssam case RTM_IEEE80211_DISASSOC: 330191247Ssam if (!discover_on_join) 331191247Ssam break; 332191247Ssam /* fall thru... */ 333178360Ssam case RTM_IEEE80211_LEAVE: 334191247Ssam if (!checkifnet(ifan->ifan_name, 1)) 335191247Ssam break; 336191247Ssam syslog(LOG_INFO, "[%s] station leave", 337191247Ssam ether_sprintf(V(ieee80211_leave_event)->iev_addr)); 338178360Ssam wds_leave(V(ieee80211_leave_event)->iev_addr); 339178360Ssam break; 340191127Ssam case RTM_IEEE80211_JOIN: 341191127Ssam case RTM_IEEE80211_REJOIN: 342191247Ssam case RTM_IEEE80211_ASSOC: 343191247Ssam case RTM_IEEE80211_REASSOC: 344191127Ssam if (!discover_on_join) 345191127Ssam break; 346191127Ssam /* fall thru... */ 347178360Ssam case RTM_IEEE80211_WDS: 348191247Ssam syslog(LOG_INFO, "[%s] wds discovery", 349191247Ssam ether_sprintf(V(ieee80211_wds_event)->iev_addr)); 350191247Ssam if (!checkifnet(ifan->ifan_name, 1)) 351191247Ssam break; 352191127Ssam wds_discovery(ifan->ifan_name, 353191127Ssam V(ieee80211_wds_event)->iev_addr); 354178360Ssam break; 355178360Ssam } 356178360Ssam break; 357178360Ssam#undef V 358178360Ssam } 359178360Ssam} 360178360Ssam 361191247Ssam/* 362191247Ssam * Handle WDS discovery; create a WDS vap for the specified bssid. 363191247Ssam * If a vap already exists then do nothing (can happen when a flood 364191247Ssam * of 4-address frames causes multiple events to be queued before 365191247Ssam * we create a vap). 366191247Ssam */ 367178360Ssamstatic void 368178360Ssamwds_discovery(const char *ifname, const uint8_t bssid[IEEE80211_ADDR_LEN]) 369178360Ssam{ 370178360Ssam struct wds *p; 371191247Ssam char parent[256]; 372191247Ssam char cmd[1024]; 373191247Ssam int status; 374178360Ssam 375178360Ssam for (p = wds; p != NULL; p = p->next) 376178360Ssam if (IEEE80211_ADDR_EQ(p->bssid, bssid)) { 377191247Ssam syslog(LOG_INFO, "[%s] wds vap already created (%s)", 378191247Ssam ether_sprintf(bssid), ifname); 379178360Ssam return; 380178360Ssam } 381191247Ssam if (getparent(ifname, parent) < 0) { 382191247Ssam syslog(LOG_ERR, "%s: no pointer to parent interface: %m", 383191247Ssam ifname); 384191127Ssam return; 385191127Ssam } 386191127Ssam 387178360Ssam p = malloc(sizeof(struct wds)); 388178360Ssam if (p == NULL) { 389191247Ssam syslog(LOG_ERR, "%s: malloc failed: %m", __func__); 390178360Ssam return; 391178360Ssam } 392178360Ssam IEEE80211_ADDR_COPY(p->bssid, bssid); 393191247Ssam if (wds_vap_create(parent, p) < 0) { 394191247Ssam free(p); 395191247Ssam return; 396191247Ssam } 397191247Ssam /* 398191247Ssam * Add to table and launch setup script. 399191247Ssam */ 400191247Ssam p->next = wds; 401191247Ssam wds = p; 402191247Ssam syslog(LOG_INFO, "[%s] create wds vap %s", ether_sprintf(bssid), 403191247Ssam p->ifname); 404191247Ssam if (script != NULL) { 405191127Ssam snprintf(cmd, sizeof(cmd), "%s %s", script, p->ifname); 406178360Ssam status = system(cmd); 407178360Ssam if (status) 408191247Ssam syslog(LOG_ERR, "vap setup script %s exited with " 409191247Ssam "status %d", script, status); 410191247Ssam } 411178360Ssam} 412178360Ssam 413191247Ssam/* 414191247Ssam * Destroy a WDS vap (if known). 415191247Ssam */ 416178360Ssamstatic void 417178360Ssamwds_destroy(const char *ifname) 418178360Ssam{ 419178360Ssam struct wds *p, **pp; 420178360Ssam 421178360Ssam for (pp = &wds; (p = *pp) != NULL; pp = &p->next) 422178360Ssam if (strncmp(p->ifname, ifname, IFNAMSIZ) == 0) 423178360Ssam break; 424191247Ssam if (p != NULL) { 425191247Ssam *pp = p->next; 426191247Ssam /* NB: vap already destroyed */ 427191247Ssam free(p); 428178360Ssam return; 429191247Ssam } 430178360Ssam} 431178360Ssam 432191247Ssam/* 433191247Ssam * Handle a station leave event; destroy any associated WDS vap. 434191247Ssam */ 435178360Ssamstatic void 436178360Ssamwds_leave(const uint8_t bssid[IEEE80211_ADDR_LEN]) 437178360Ssam{ 438178360Ssam struct wds *p, **pp; 439178360Ssam 440178360Ssam for (pp = &wds; (p = *pp) != NULL; pp = &p->next) 441178360Ssam if (IEEE80211_ADDR_EQ(p->bssid, bssid)) 442178360Ssam break; 443191247Ssam if (p != NULL) { 444191247Ssam *pp = p->next; 445191247Ssam if (wds_vap_destroy(p->ifname) >= 0) 446191247Ssam syslog(LOG_INFO, "[%s] wds vap %s destroyed", 447191247Ssam ether_sprintf(bssid), p->ifname); 448191247Ssam free(p); 449191247Ssam } 450178360Ssam} 451178360Ssam 452178360Ssamstatic int 453178360Ssamwds_vap_create(const char *parent, struct wds *p) 454178360Ssam{ 455178360Ssam struct ieee80211_clone_params cp; 456178360Ssam struct ifreq ifr; 457178360Ssam int s, status; 458178360Ssam 459178360Ssam memset(&cp, 0, sizeof(cp)); 460178360Ssam strncpy(cp.icp_parent, parent, IFNAMSIZ); 461178360Ssam cp.icp_opmode = IEEE80211_M_WDS; 462178360Ssam IEEE80211_ADDR_COPY(cp.icp_bssid, p->bssid); 463178360Ssam 464178360Ssam memset(&ifr, 0, sizeof(ifr)); 465178360Ssam strncpy(ifr.ifr_name, "wlan", IFNAMSIZ); 466178360Ssam ifr.ifr_data = (void *) &cp; 467178360Ssam 468178360Ssam status = -1; 469178360Ssam s = socket(AF_INET, SOCK_DGRAM, 0); 470178360Ssam if (s >= 0) { 471178360Ssam if (ioctl(s, SIOCIFCREATE2, &ifr) >= 0) { 472178360Ssam strlcpy(p->ifname, ifr.ifr_name, IFNAMSIZ); 473178360Ssam status = 0; 474178360Ssam } else { 475191247Ssam syslog(LOG_ERR, "SIOCIFCREATE2(" 476191247Ssam "mode %u flags 0x%x parent %s bssid %s): %m", 477178360Ssam cp.icp_opmode, cp.icp_flags, parent, 478178360Ssam ether_sprintf(cp.icp_bssid)); 479178360Ssam } 480178360Ssam close(s); 481178360Ssam } else 482191247Ssam syslog(LOG_ERR, "socket(SOCK_DRAGM): %m"); 483178360Ssam return status; 484178360Ssam} 485178360Ssam 486178360Ssamstatic int 487178360Ssamwds_vap_destroy(const char *ifname) 488178360Ssam{ 489178360Ssam struct ieee80211req ifr; 490178360Ssam int s, status; 491178360Ssam 492178360Ssam s = socket(AF_INET, SOCK_DGRAM, 0); 493178360Ssam if (s < 0) { 494191247Ssam syslog(LOG_ERR, "socket(SOCK_DRAGM): %m"); 495178360Ssam return -1; 496178360Ssam } 497178360Ssam memset(&ifr, 0, sizeof(ifr)); 498178360Ssam strncpy(ifr.i_name, ifname, IFNAMSIZ); 499178360Ssam if (ioctl(s, SIOCIFDESTROY, &ifr) < 0) { 500191247Ssam syslog(LOG_ERR, "ioctl(SIOCIFDESTROY): %m"); 501178360Ssam status = -1; 502178360Ssam } else 503178360Ssam status = 0; 504178360Ssam close(s); 505178360Ssam return status; 506178360Ssam} 507