1324714Scy/* 2324714Scy * hostapd / VLAN initialization - full dynamic VLAN 3324714Scy * Copyright 2003, Instant802 Networks, Inc. 4324714Scy * Copyright 2005-2006, Devicescape Software, Inc. 5324714Scy * Copyright (c) 2009, Jouni Malinen <j@w1.fi> 6324714Scy * 7324714Scy * This software may be distributed under the terms of the BSD license. 8324714Scy * See README for more details. 9324714Scy */ 10324714Scy 11324714Scy#include "utils/includes.h" 12324714Scy#include <net/if.h> 13324714Scy/* Avoid conflicts due to NetBSD net/if.h if_type define with driver.h */ 14324714Scy#undef if_type 15324714Scy#include <sys/ioctl.h> 16324714Scy 17324714Scy#include "utils/common.h" 18324714Scy#include "drivers/priv_netlink.h" 19346981Scy#include "drivers/linux_ioctl.h" 20324714Scy#include "common/linux_bridge.h" 21324714Scy#include "common/linux_vlan.h" 22324714Scy#include "utils/eloop.h" 23324714Scy#include "hostapd.h" 24324714Scy#include "ap_config.h" 25324714Scy#include "ap_drv_ops.h" 26324714Scy#include "wpa_auth.h" 27324714Scy#include "vlan_init.h" 28324714Scy#include "vlan_util.h" 29324714Scy 30324714Scy 31324714Scystruct full_dynamic_vlan { 32324714Scy int s; /* socket on which to listen for new/removed interfaces. */ 33324714Scy}; 34324714Scy 35324714Scy#define DVLAN_CLEAN_BR 0x1 36324714Scy#define DVLAN_CLEAN_VLAN 0x2 37324714Scy#define DVLAN_CLEAN_VLAN_PORT 0x4 38324714Scy 39324714Scystruct dynamic_iface { 40324714Scy char ifname[IFNAMSIZ + 1]; 41324714Scy int usage; 42324714Scy int clean; 43324714Scy struct dynamic_iface *next; 44324714Scy}; 45324714Scy 46324714Scy 47324714Scy/* Increment ref counter for ifname and add clean flag. 48324714Scy * If not in list, add it only if some flags are given. 49324714Scy */ 50324714Scystatic void dyn_iface_get(struct hostapd_data *hapd, const char *ifname, 51324714Scy int clean) 52324714Scy{ 53324714Scy struct dynamic_iface *next, **dynamic_ifaces; 54324714Scy struct hapd_interfaces *interfaces; 55324714Scy 56324714Scy interfaces = hapd->iface->interfaces; 57324714Scy dynamic_ifaces = &interfaces->vlan_priv; 58324714Scy 59324714Scy for (next = *dynamic_ifaces; next; next = next->next) { 60324714Scy if (os_strcmp(ifname, next->ifname) == 0) 61324714Scy break; 62324714Scy } 63324714Scy 64324714Scy if (next) { 65324714Scy next->usage++; 66324714Scy next->clean |= clean; 67324714Scy return; 68324714Scy } 69324714Scy 70324714Scy if (!clean) 71324714Scy return; 72324714Scy 73324714Scy next = os_zalloc(sizeof(*next)); 74324714Scy if (!next) 75324714Scy return; 76324714Scy os_strlcpy(next->ifname, ifname, sizeof(next->ifname)); 77324714Scy next->usage = 1; 78324714Scy next->clean = clean; 79324714Scy next->next = *dynamic_ifaces; 80324714Scy *dynamic_ifaces = next; 81324714Scy} 82324714Scy 83324714Scy 84324714Scy/* Decrement reference counter for given ifname. 85324714Scy * Return clean flag iff reference counter was decreased to zero, else zero 86324714Scy */ 87324714Scystatic int dyn_iface_put(struct hostapd_data *hapd, const char *ifname) 88324714Scy{ 89324714Scy struct dynamic_iface *next, *prev = NULL, **dynamic_ifaces; 90324714Scy struct hapd_interfaces *interfaces; 91324714Scy int clean; 92324714Scy 93324714Scy interfaces = hapd->iface->interfaces; 94324714Scy dynamic_ifaces = &interfaces->vlan_priv; 95324714Scy 96324714Scy for (next = *dynamic_ifaces; next; next = next->next) { 97324714Scy if (os_strcmp(ifname, next->ifname) == 0) 98324714Scy break; 99324714Scy prev = next; 100324714Scy } 101324714Scy 102324714Scy if (!next) 103324714Scy return 0; 104324714Scy 105324714Scy next->usage--; 106324714Scy if (next->usage) 107324714Scy return 0; 108324714Scy 109324714Scy if (prev) 110324714Scy prev->next = next->next; 111324714Scy else 112324714Scy *dynamic_ifaces = next->next; 113324714Scy clean = next->clean; 114324714Scy os_free(next); 115324714Scy 116324714Scy return clean; 117324714Scy} 118324714Scy 119324714Scy 120324714Scystatic int ifconfig_down(const char *if_name) 121324714Scy{ 122324714Scy wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", if_name); 123324714Scy return ifconfig_helper(if_name, 0); 124324714Scy} 125324714Scy 126324714Scy 127324714Scy/* This value should be 256 ONLY. If it is something else, then hostapd 128324714Scy * might crash!, as this value has been hard-coded in 2.4.x kernel 129324714Scy * bridging code. 130324714Scy */ 131324714Scy#define MAX_BR_PORTS 256 132324714Scy 133324714Scystatic int br_delif(const char *br_name, const char *if_name) 134324714Scy{ 135324714Scy int fd; 136324714Scy struct ifreq ifr; 137324714Scy unsigned long args[2]; 138324714Scy int if_index; 139324714Scy 140324714Scy wpa_printf(MSG_DEBUG, "VLAN: br_delif(%s, %s)", br_name, if_name); 141324714Scy if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 142324714Scy wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " 143324714Scy "failed: %s", __func__, strerror(errno)); 144324714Scy return -1; 145324714Scy } 146324714Scy 147346981Scy if (linux_br_del_if(fd, br_name, if_name) == 0) 148346981Scy goto done; 149346981Scy 150324714Scy if_index = if_nametoindex(if_name); 151324714Scy 152324714Scy if (if_index == 0) { 153324714Scy wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining " 154324714Scy "interface index for '%s'", 155324714Scy __func__, if_name); 156324714Scy close(fd); 157324714Scy return -1; 158324714Scy } 159324714Scy 160324714Scy args[0] = BRCTL_DEL_IF; 161324714Scy args[1] = if_index; 162324714Scy 163324714Scy os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); 164324714Scy ifr.ifr_data = (void *) args; 165324714Scy 166324714Scy if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) { 167324714Scy /* No error if interface already removed. */ 168324714Scy wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE," 169324714Scy "BRCTL_DEL_IF] failed for br_name=%s if_name=%s: " 170324714Scy "%s", __func__, br_name, if_name, strerror(errno)); 171324714Scy close(fd); 172324714Scy return -1; 173324714Scy } 174324714Scy 175346981Scydone: 176324714Scy close(fd); 177324714Scy return 0; 178324714Scy} 179324714Scy 180324714Scy 181324714Scy/* 182324714Scy Add interface 'if_name' to the bridge 'br_name' 183324714Scy 184324714Scy returns -1 on error 185324714Scy returns 1 if the interface is already part of the bridge 186324714Scy returns 0 otherwise 187324714Scy*/ 188324714Scystatic int br_addif(const char *br_name, const char *if_name) 189324714Scy{ 190324714Scy int fd; 191324714Scy struct ifreq ifr; 192324714Scy unsigned long args[2]; 193324714Scy int if_index; 194324714Scy 195324714Scy wpa_printf(MSG_DEBUG, "VLAN: br_addif(%s, %s)", br_name, if_name); 196324714Scy if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 197324714Scy wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " 198324714Scy "failed: %s", __func__, strerror(errno)); 199324714Scy return -1; 200324714Scy } 201324714Scy 202346981Scy if (linux_br_add_if(fd, br_name, if_name) == 0) 203346981Scy goto done; 204346981Scy if (errno == EBUSY) { 205346981Scy /* The interface is already added. */ 206346981Scy close(fd); 207346981Scy return 1; 208346981Scy } 209346981Scy 210324714Scy if_index = if_nametoindex(if_name); 211324714Scy 212324714Scy if (if_index == 0) { 213324714Scy wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining " 214324714Scy "interface index for '%s'", 215324714Scy __func__, if_name); 216324714Scy close(fd); 217324714Scy return -1; 218324714Scy } 219324714Scy 220324714Scy args[0] = BRCTL_ADD_IF; 221324714Scy args[1] = if_index; 222324714Scy 223324714Scy os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); 224324714Scy ifr.ifr_data = (void *) args; 225324714Scy 226324714Scy if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { 227324714Scy if (errno == EBUSY) { 228324714Scy /* The interface is already added. */ 229324714Scy close(fd); 230324714Scy return 1; 231324714Scy } 232324714Scy 233324714Scy wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE," 234324714Scy "BRCTL_ADD_IF] failed for br_name=%s if_name=%s: " 235324714Scy "%s", __func__, br_name, if_name, strerror(errno)); 236324714Scy close(fd); 237324714Scy return -1; 238324714Scy } 239324714Scy 240346981Scydone: 241324714Scy close(fd); 242324714Scy return 0; 243324714Scy} 244324714Scy 245324714Scy 246324714Scystatic int br_delbr(const char *br_name) 247324714Scy{ 248324714Scy int fd; 249324714Scy unsigned long arg[2]; 250324714Scy 251324714Scy wpa_printf(MSG_DEBUG, "VLAN: br_delbr(%s)", br_name); 252324714Scy if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 253324714Scy wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " 254324714Scy "failed: %s", __func__, strerror(errno)); 255324714Scy return -1; 256324714Scy } 257324714Scy 258346981Scy if (linux_br_del(fd, br_name) == 0) 259346981Scy goto done; 260346981Scy 261324714Scy arg[0] = BRCTL_DEL_BRIDGE; 262324714Scy arg[1] = (unsigned long) br_name; 263324714Scy 264324714Scy if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) { 265324714Scy /* No error if bridge already removed. */ 266324714Scy wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_DEL_BRIDGE failed for " 267324714Scy "%s: %s", __func__, br_name, strerror(errno)); 268324714Scy close(fd); 269324714Scy return -1; 270324714Scy } 271324714Scy 272346981Scydone: 273324714Scy close(fd); 274324714Scy return 0; 275324714Scy} 276324714Scy 277324714Scy 278324714Scy/* 279324714Scy Add a bridge with the name 'br_name'. 280324714Scy 281324714Scy returns -1 on error 282324714Scy returns 1 if the bridge already exists 283324714Scy returns 0 otherwise 284324714Scy*/ 285324714Scystatic int br_addbr(const char *br_name) 286324714Scy{ 287324714Scy int fd; 288324714Scy unsigned long arg[4]; 289324714Scy struct ifreq ifr; 290324714Scy 291324714Scy wpa_printf(MSG_DEBUG, "VLAN: br_addbr(%s)", br_name); 292324714Scy if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 293324714Scy wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " 294324714Scy "failed: %s", __func__, strerror(errno)); 295324714Scy return -1; 296324714Scy } 297324714Scy 298346981Scy if (linux_br_add(fd, br_name) == 0) 299346981Scy goto done; 300346981Scy if (errno == EEXIST) { 301346981Scy /* The bridge is already added. */ 302346981Scy close(fd); 303346981Scy return 1; 304346981Scy } 305346981Scy 306324714Scy arg[0] = BRCTL_ADD_BRIDGE; 307324714Scy arg[1] = (unsigned long) br_name; 308324714Scy 309324714Scy if (ioctl(fd, SIOCGIFBR, arg) < 0) { 310346981Scy if (errno == EEXIST) { 311324714Scy /* The bridge is already added. */ 312324714Scy close(fd); 313324714Scy return 1; 314324714Scy } else { 315324714Scy wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_ADD_BRIDGE " 316324714Scy "failed for %s: %s", 317324714Scy __func__, br_name, strerror(errno)); 318324714Scy close(fd); 319324714Scy return -1; 320324714Scy } 321324714Scy } 322324714Scy 323346981Scydone: 324324714Scy /* Decrease forwarding delay to avoid EAPOL timeouts. */ 325324714Scy os_memset(&ifr, 0, sizeof(ifr)); 326324714Scy os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ); 327324714Scy arg[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY; 328324714Scy arg[1] = 1; 329324714Scy arg[2] = 0; 330324714Scy arg[3] = 0; 331324714Scy ifr.ifr_data = (char *) &arg; 332324714Scy if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { 333324714Scy wpa_printf(MSG_ERROR, "VLAN: %s: " 334324714Scy "BRCTL_SET_BRIDGE_FORWARD_DELAY (1 sec) failed for " 335324714Scy "%s: %s", __func__, br_name, strerror(errno)); 336324714Scy /* Continue anyway */ 337324714Scy } 338324714Scy 339324714Scy close(fd); 340324714Scy return 0; 341324714Scy} 342324714Scy 343324714Scy 344324714Scystatic int br_getnumports(const char *br_name) 345324714Scy{ 346324714Scy int fd; 347324714Scy int i; 348324714Scy int port_cnt = 0; 349324714Scy unsigned long arg[4]; 350324714Scy int ifindices[MAX_BR_PORTS]; 351324714Scy struct ifreq ifr; 352324714Scy 353324714Scy if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 354324714Scy wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " 355324714Scy "failed: %s", __func__, strerror(errno)); 356324714Scy return -1; 357324714Scy } 358324714Scy 359324714Scy arg[0] = BRCTL_GET_PORT_LIST; 360324714Scy arg[1] = (unsigned long) ifindices; 361324714Scy arg[2] = MAX_BR_PORTS; 362324714Scy arg[3] = 0; 363324714Scy 364324714Scy os_memset(ifindices, 0, sizeof(ifindices)); 365324714Scy os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); 366324714Scy ifr.ifr_data = (void *) arg; 367324714Scy 368324714Scy if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { 369324714Scy wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_GET_PORT_LIST " 370324714Scy "failed for %s: %s", 371324714Scy __func__, br_name, strerror(errno)); 372324714Scy close(fd); 373324714Scy return -1; 374324714Scy } 375324714Scy 376324714Scy for (i = 1; i < MAX_BR_PORTS; i++) { 377324714Scy if (ifindices[i] > 0) { 378324714Scy port_cnt++; 379324714Scy } 380324714Scy } 381324714Scy 382324714Scy close(fd); 383324714Scy return port_cnt; 384324714Scy} 385324714Scy 386324714Scy 387324714Scystatic void vlan_newlink_tagged(int vlan_naming, const char *tagged_interface, 388324714Scy const char *br_name, int vid, 389324714Scy struct hostapd_data *hapd) 390324714Scy{ 391324714Scy char vlan_ifname[IFNAMSIZ]; 392324714Scy int clean; 393346981Scy int ret; 394324714Scy 395324714Scy if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE) 396346981Scy ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d", 397346981Scy tagged_interface, vid); 398324714Scy else 399346981Scy ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", 400346981Scy vid); 401346981Scy if (ret >= (int) sizeof(vlan_ifname)) 402346981Scy wpa_printf(MSG_WARNING, 403346981Scy "VLAN: Interface name was truncated to %s", 404346981Scy vlan_ifname); 405324714Scy 406324714Scy clean = 0; 407324714Scy ifconfig_up(tagged_interface); 408324714Scy if (!vlan_add(tagged_interface, vid, vlan_ifname)) 409324714Scy clean |= DVLAN_CLEAN_VLAN; 410324714Scy 411324714Scy if (!br_addif(br_name, vlan_ifname)) 412324714Scy clean |= DVLAN_CLEAN_VLAN_PORT; 413324714Scy 414324714Scy dyn_iface_get(hapd, vlan_ifname, clean); 415324714Scy 416324714Scy ifconfig_up(vlan_ifname); 417324714Scy} 418324714Scy 419324714Scy 420346981Scystatic void vlan_bridge_name(char *br_name, struct hostapd_data *hapd, 421346981Scy struct hostapd_vlan *vlan, int vid) 422324714Scy{ 423324714Scy char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; 424346981Scy int ret; 425324714Scy 426346981Scy if (vlan->bridge[0]) { 427346981Scy os_strlcpy(br_name, vlan->bridge, IFNAMSIZ); 428346981Scy ret = 0; 429346981Scy } else if (hapd->conf->vlan_bridge[0]) { 430346981Scy ret = os_snprintf(br_name, IFNAMSIZ, "%s%d", 431346981Scy hapd->conf->vlan_bridge, vid); 432324714Scy } else if (tagged_interface) { 433346981Scy ret = os_snprintf(br_name, IFNAMSIZ, "br%s.%d", 434346981Scy tagged_interface, vid); 435324714Scy } else { 436346981Scy ret = os_snprintf(br_name, IFNAMSIZ, "brvlan%d", vid); 437324714Scy } 438346981Scy if (ret >= IFNAMSIZ) 439346981Scy wpa_printf(MSG_WARNING, 440346981Scy "VLAN: Interface name was truncated to %s", 441346981Scy br_name); 442324714Scy} 443324714Scy 444324714Scy 445324714Scystatic void vlan_get_bridge(const char *br_name, struct hostapd_data *hapd, 446324714Scy int vid) 447324714Scy{ 448324714Scy char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; 449324714Scy int vlan_naming = hapd->conf->ssid.vlan_naming; 450324714Scy 451324714Scy dyn_iface_get(hapd, br_name, br_addbr(br_name) ? 0 : DVLAN_CLEAN_BR); 452324714Scy 453324714Scy ifconfig_up(br_name); 454324714Scy 455324714Scy if (tagged_interface) 456324714Scy vlan_newlink_tagged(vlan_naming, tagged_interface, br_name, 457324714Scy vid, hapd); 458324714Scy} 459324714Scy 460324714Scy 461324714Scyvoid vlan_newlink(const char *ifname, struct hostapd_data *hapd) 462324714Scy{ 463324714Scy char br_name[IFNAMSIZ]; 464324714Scy struct hostapd_vlan *vlan; 465324714Scy int untagged, *tagged, i, notempty; 466324714Scy 467324714Scy wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname); 468324714Scy 469324714Scy for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { 470324714Scy if (vlan->configured || 471324714Scy os_strcmp(ifname, vlan->ifname) != 0) 472324714Scy continue; 473324714Scy break; 474324714Scy } 475324714Scy if (!vlan) 476324714Scy return; 477324714Scy 478324714Scy vlan->configured = 1; 479324714Scy 480324714Scy notempty = vlan->vlan_desc.notempty; 481324714Scy untagged = vlan->vlan_desc.untagged; 482324714Scy tagged = vlan->vlan_desc.tagged; 483324714Scy 484324714Scy if (!notempty) { 485324714Scy /* Non-VLAN STA */ 486324714Scy if (hapd->conf->bridge[0] && 487324714Scy !br_addif(hapd->conf->bridge, ifname)) 488324714Scy vlan->clean |= DVLAN_CLEAN_WLAN_PORT; 489324714Scy } else if (untagged > 0 && untagged <= MAX_VLAN_ID) { 490346981Scy vlan_bridge_name(br_name, hapd, vlan, untagged); 491324714Scy 492324714Scy vlan_get_bridge(br_name, hapd, untagged); 493324714Scy 494324714Scy if (!br_addif(br_name, ifname)) 495324714Scy vlan->clean |= DVLAN_CLEAN_WLAN_PORT; 496324714Scy } 497324714Scy 498324714Scy for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) { 499324714Scy if (tagged[i] == untagged || 500324714Scy tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID || 501324714Scy (i > 0 && tagged[i] == tagged[i - 1])) 502324714Scy continue; 503346981Scy vlan_bridge_name(br_name, hapd, vlan, tagged[i]); 504324714Scy vlan_get_bridge(br_name, hapd, tagged[i]); 505324714Scy vlan_newlink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE, 506324714Scy ifname, br_name, tagged[i], hapd); 507324714Scy } 508324714Scy 509324714Scy ifconfig_up(ifname); 510324714Scy} 511324714Scy 512324714Scy 513324714Scystatic void vlan_dellink_tagged(int vlan_naming, const char *tagged_interface, 514324714Scy const char *br_name, int vid, 515324714Scy struct hostapd_data *hapd) 516324714Scy{ 517324714Scy char vlan_ifname[IFNAMSIZ]; 518324714Scy int clean; 519346981Scy int ret; 520324714Scy 521324714Scy if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE) 522346981Scy ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d", 523346981Scy tagged_interface, vid); 524324714Scy else 525346981Scy ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", 526346981Scy vid); 527346981Scy if (ret >= (int) sizeof(vlan_ifname)) 528346981Scy wpa_printf(MSG_WARNING, 529346981Scy "VLAN: Interface name was truncated to %s", 530346981Scy vlan_ifname); 531324714Scy 532346981Scy 533324714Scy clean = dyn_iface_put(hapd, vlan_ifname); 534324714Scy 535324714Scy if (clean & DVLAN_CLEAN_VLAN_PORT) 536324714Scy br_delif(br_name, vlan_ifname); 537324714Scy 538324714Scy if (clean & DVLAN_CLEAN_VLAN) { 539324714Scy ifconfig_down(vlan_ifname); 540324714Scy vlan_rem(vlan_ifname); 541324714Scy } 542324714Scy} 543324714Scy 544324714Scy 545324714Scystatic void vlan_put_bridge(const char *br_name, struct hostapd_data *hapd, 546324714Scy int vid) 547324714Scy{ 548324714Scy int clean; 549324714Scy char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; 550324714Scy int vlan_naming = hapd->conf->ssid.vlan_naming; 551324714Scy 552324714Scy if (tagged_interface) 553324714Scy vlan_dellink_tagged(vlan_naming, tagged_interface, br_name, 554324714Scy vid, hapd); 555324714Scy 556324714Scy clean = dyn_iface_put(hapd, br_name); 557324714Scy if ((clean & DVLAN_CLEAN_BR) && br_getnumports(br_name) == 0) { 558324714Scy ifconfig_down(br_name); 559324714Scy br_delbr(br_name); 560324714Scy } 561324714Scy} 562324714Scy 563324714Scy 564324714Scyvoid vlan_dellink(const char *ifname, struct hostapd_data *hapd) 565324714Scy{ 566324714Scy struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan; 567324714Scy 568324714Scy wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname); 569324714Scy 570324714Scy first = prev = vlan; 571324714Scy 572324714Scy while (vlan) { 573324714Scy if (os_strcmp(ifname, vlan->ifname) != 0) { 574324714Scy prev = vlan; 575324714Scy vlan = vlan->next; 576324714Scy continue; 577324714Scy } 578324714Scy break; 579324714Scy } 580324714Scy if (!vlan) 581324714Scy return; 582324714Scy 583324714Scy if (vlan->configured) { 584324714Scy int notempty = vlan->vlan_desc.notempty; 585324714Scy int untagged = vlan->vlan_desc.untagged; 586324714Scy int *tagged = vlan->vlan_desc.tagged; 587324714Scy char br_name[IFNAMSIZ]; 588324714Scy int i; 589324714Scy 590324714Scy for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) { 591324714Scy if (tagged[i] == untagged || 592324714Scy tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID || 593324714Scy (i > 0 && tagged[i] == tagged[i - 1])) 594324714Scy continue; 595346981Scy vlan_bridge_name(br_name, hapd, vlan, tagged[i]); 596324714Scy vlan_dellink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE, 597324714Scy ifname, br_name, tagged[i], hapd); 598324714Scy vlan_put_bridge(br_name, hapd, tagged[i]); 599324714Scy } 600324714Scy 601324714Scy if (!notempty) { 602324714Scy /* Non-VLAN STA */ 603324714Scy if (hapd->conf->bridge[0] && 604324714Scy (vlan->clean & DVLAN_CLEAN_WLAN_PORT)) 605324714Scy br_delif(hapd->conf->bridge, ifname); 606324714Scy } else if (untagged > 0 && untagged <= MAX_VLAN_ID) { 607346981Scy vlan_bridge_name(br_name, hapd, vlan, untagged); 608324714Scy 609324714Scy if (vlan->clean & DVLAN_CLEAN_WLAN_PORT) 610324714Scy br_delif(br_name, vlan->ifname); 611324714Scy 612324714Scy vlan_put_bridge(br_name, hapd, untagged); 613324714Scy } 614324714Scy } 615324714Scy 616324714Scy /* 617324714Scy * Ensure this VLAN interface is actually removed even if 618324714Scy * NEWLINK message is only received later. 619324714Scy */ 620324714Scy if (if_nametoindex(vlan->ifname) && vlan_if_remove(hapd, vlan)) 621324714Scy wpa_printf(MSG_ERROR, 622324714Scy "VLAN: Could not remove VLAN iface: %s: %s", 623324714Scy vlan->ifname, strerror(errno)); 624324714Scy 625324714Scy if (vlan == first) 626324714Scy hapd->conf->vlan = vlan->next; 627324714Scy else 628324714Scy prev->next = vlan->next; 629324714Scy 630324714Scy os_free(vlan); 631324714Scy} 632324714Scy 633324714Scy 634324714Scystatic void 635324714Scyvlan_read_ifnames(struct nlmsghdr *h, size_t len, int del, 636324714Scy struct hostapd_data *hapd) 637324714Scy{ 638324714Scy struct ifinfomsg *ifi; 639324714Scy int attrlen, nlmsg_len, rta_len; 640324714Scy struct rtattr *attr; 641324714Scy char ifname[IFNAMSIZ + 1]; 642324714Scy 643324714Scy if (len < sizeof(*ifi)) 644324714Scy return; 645324714Scy 646324714Scy ifi = NLMSG_DATA(h); 647324714Scy 648324714Scy nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); 649324714Scy 650324714Scy attrlen = h->nlmsg_len - nlmsg_len; 651324714Scy if (attrlen < 0) 652324714Scy return; 653324714Scy 654324714Scy attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); 655324714Scy 656324714Scy os_memset(ifname, 0, sizeof(ifname)); 657324714Scy rta_len = RTA_ALIGN(sizeof(struct rtattr)); 658324714Scy while (RTA_OK(attr, attrlen)) { 659324714Scy if (attr->rta_type == IFLA_IFNAME) { 660324714Scy int n = attr->rta_len - rta_len; 661324714Scy if (n < 0) 662324714Scy break; 663324714Scy 664324714Scy if ((size_t) n >= sizeof(ifname)) 665324714Scy n = sizeof(ifname) - 1; 666324714Scy os_memcpy(ifname, ((char *) attr) + rta_len, n); 667324714Scy 668324714Scy } 669324714Scy 670324714Scy attr = RTA_NEXT(attr, attrlen); 671324714Scy } 672324714Scy 673324714Scy if (!ifname[0]) 674324714Scy return; 675324714Scy if (del && if_nametoindex(ifname)) { 676324714Scy /* interface still exists, race condition -> 677324714Scy * iface has just been recreated */ 678324714Scy return; 679324714Scy } 680324714Scy 681324714Scy wpa_printf(MSG_DEBUG, 682324714Scy "VLAN: RTM_%sLINK: ifi_index=%d ifname=%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)", 683324714Scy del ? "DEL" : "NEW", 684324714Scy ifi->ifi_index, ifname, ifi->ifi_family, ifi->ifi_flags, 685324714Scy (ifi->ifi_flags & IFF_UP) ? "[UP]" : "", 686324714Scy (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", 687324714Scy (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", 688324714Scy (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); 689324714Scy 690324714Scy if (del) 691324714Scy vlan_dellink(ifname, hapd); 692324714Scy else 693324714Scy vlan_newlink(ifname, hapd); 694324714Scy} 695324714Scy 696324714Scy 697324714Scystatic void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx) 698324714Scy{ 699324714Scy char buf[8192]; 700324714Scy int left; 701324714Scy struct sockaddr_nl from; 702324714Scy socklen_t fromlen; 703324714Scy struct nlmsghdr *h; 704324714Scy struct hostapd_data *hapd = eloop_ctx; 705324714Scy 706324714Scy fromlen = sizeof(from); 707324714Scy left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, 708324714Scy (struct sockaddr *) &from, &fromlen); 709324714Scy if (left < 0) { 710324714Scy if (errno != EINTR && errno != EAGAIN) 711324714Scy wpa_printf(MSG_ERROR, "VLAN: %s: recvfrom failed: %s", 712324714Scy __func__, strerror(errno)); 713324714Scy return; 714324714Scy } 715324714Scy 716324714Scy h = (struct nlmsghdr *) buf; 717324714Scy while (NLMSG_OK(h, left)) { 718324714Scy int len, plen; 719324714Scy 720324714Scy len = h->nlmsg_len; 721324714Scy plen = len - sizeof(*h); 722324714Scy if (len > left || plen < 0) { 723324714Scy wpa_printf(MSG_DEBUG, "VLAN: Malformed netlink " 724324714Scy "message: len=%d left=%d plen=%d", 725324714Scy len, left, plen); 726324714Scy break; 727324714Scy } 728324714Scy 729324714Scy switch (h->nlmsg_type) { 730324714Scy case RTM_NEWLINK: 731324714Scy vlan_read_ifnames(h, plen, 0, hapd); 732324714Scy break; 733324714Scy case RTM_DELLINK: 734324714Scy vlan_read_ifnames(h, plen, 1, hapd); 735324714Scy break; 736324714Scy } 737324714Scy 738324714Scy h = NLMSG_NEXT(h, left); 739324714Scy } 740324714Scy 741324714Scy if (left > 0) { 742324714Scy wpa_printf(MSG_DEBUG, "VLAN: %s: %d extra bytes in the end of " 743324714Scy "netlink message", __func__, left); 744324714Scy } 745324714Scy} 746324714Scy 747324714Scy 748324714Scystruct full_dynamic_vlan * 749324714Scyfull_dynamic_vlan_init(struct hostapd_data *hapd) 750324714Scy{ 751324714Scy struct sockaddr_nl local; 752324714Scy struct full_dynamic_vlan *priv; 753324714Scy 754324714Scy priv = os_zalloc(sizeof(*priv)); 755324714Scy if (priv == NULL) 756324714Scy return NULL; 757324714Scy 758324714Scy vlan_set_name_type(hapd->conf->ssid.vlan_naming == 759324714Scy DYNAMIC_VLAN_NAMING_WITH_DEVICE ? 760324714Scy VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD : 761324714Scy VLAN_NAME_TYPE_PLUS_VID_NO_PAD); 762324714Scy 763324714Scy priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); 764324714Scy if (priv->s < 0) { 765324714Scy wpa_printf(MSG_ERROR, "VLAN: %s: socket(PF_NETLINK,SOCK_RAW," 766324714Scy "NETLINK_ROUTE) failed: %s", 767324714Scy __func__, strerror(errno)); 768324714Scy os_free(priv); 769324714Scy return NULL; 770324714Scy } 771324714Scy 772324714Scy os_memset(&local, 0, sizeof(local)); 773324714Scy local.nl_family = AF_NETLINK; 774324714Scy local.nl_groups = RTMGRP_LINK; 775324714Scy if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) { 776324714Scy wpa_printf(MSG_ERROR, "VLAN: %s: bind(netlink) failed: %s", 777324714Scy __func__, strerror(errno)); 778324714Scy close(priv->s); 779324714Scy os_free(priv); 780324714Scy return NULL; 781324714Scy } 782324714Scy 783324714Scy if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, NULL)) 784324714Scy { 785324714Scy close(priv->s); 786324714Scy os_free(priv); 787324714Scy return NULL; 788324714Scy } 789324714Scy 790324714Scy return priv; 791324714Scy} 792324714Scy 793324714Scy 794324714Scyvoid full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv) 795324714Scy{ 796324714Scy if (priv == NULL) 797324714Scy return; 798324714Scy eloop_unregister_read_sock(priv->s); 799324714Scy close(priv->s); 800324714Scy os_free(priv); 801324714Scy} 802