1214501Srpaulo/* 2214501Srpaulo * hostapd / VLAN initialization 3214501Srpaulo * Copyright 2003, Instant802 Networks, Inc. 4214501Srpaulo * Copyright 2005-2006, Devicescape Software, Inc. 5214501Srpaulo * Copyright (c) 2009, Jouni Malinen <j@w1.fi> 6214501Srpaulo * 7281806Srpaulo * This software may be distributed under the terms of the BSD license. 8281806Srpaulo * See README for more details. 9214501Srpaulo */ 10214501Srpaulo 11214501Srpaulo#include "utils/includes.h" 12214501Srpaulo 13214501Srpaulo#include "utils/common.h" 14214501Srpaulo#include "hostapd.h" 15214501Srpaulo#include "ap_config.h" 16252726Srpaulo#include "ap_drv_ops.h" 17337817Scy#include "wpa_auth.h" 18214501Srpaulo#include "vlan_init.h" 19252726Srpaulo#include "vlan_util.h" 20214501Srpaulo 21214501Srpaulo 22337817Scystatic int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan, 23337817Scy int existsok) 24289549Srpaulo{ 25337817Scy int ret, i; 26289549Srpaulo 27337817Scy for (i = 0; i < NUM_WEP_KEYS; i++) { 28337817Scy if (!hapd->conf->ssid.wep.key[i]) 29337817Scy continue; 30337817Scy wpa_printf(MSG_ERROR, 31337817Scy "VLAN: Refusing to set up VLAN iface %s with WEP", 32337817Scy vlan->ifname); 33214501Srpaulo return -1; 34214501Srpaulo } 35214501Srpaulo 36337817Scy if (!iface_exists(vlan->ifname)) 37337817Scy ret = hostapd_vlan_if_add(hapd, vlan->ifname); 38337817Scy else if (!existsok) 39214501Srpaulo return -1; 40214501Srpaulo else 41337817Scy ret = 0; 42214501Srpaulo 43337817Scy if (ret) 44337817Scy return ret; 45214501Srpaulo 46337817Scy ifconfig_up(vlan->ifname); /* else wpa group will fail fatal */ 47214501Srpaulo 48337817Scy if (hapd->wpa_auth) 49337817Scy ret = wpa_auth_ensure_group(hapd->wpa_auth, vlan->vlan_id); 50214501Srpaulo 51337817Scy if (ret == 0) 52337817Scy return ret; 53214501Srpaulo 54337817Scy wpa_printf(MSG_ERROR, "WPA initialization for VLAN %d failed (%d)", 55337817Scy vlan->vlan_id, ret); 56337817Scy if (wpa_auth_release_group(hapd->wpa_auth, vlan->vlan_id)) 57337817Scy wpa_printf(MSG_ERROR, "WPA deinit of %s failed", vlan->ifname); 58214501Srpaulo 59337817Scy /* group state machine setup failed */ 60337817Scy if (hostapd_vlan_if_remove(hapd, vlan->ifname)) 61337817Scy wpa_printf(MSG_ERROR, "Removal of %s failed", vlan->ifname); 62214501Srpaulo 63337817Scy return ret; 64214501Srpaulo} 65214501Srpaulo 66214501Srpaulo 67337817Scyint vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan) 68214501Srpaulo{ 69337817Scy int ret; 70214501Srpaulo 71337817Scy ret = wpa_auth_release_group(hapd->wpa_auth, vlan->vlan_id); 72337817Scy if (ret) 73337817Scy wpa_printf(MSG_ERROR, 74337817Scy "WPA deinitialization for VLAN %d failed (%d)", 75337817Scy vlan->vlan_id, ret); 76214501Srpaulo 77337817Scy return hostapd_vlan_if_remove(hapd, vlan->ifname); 78214501Srpaulo} 79214501Srpaulo 80214501Srpaulo 81214501Srpaulostatic int vlan_dynamic_add(struct hostapd_data *hapd, 82214501Srpaulo struct hostapd_vlan *vlan) 83214501Srpaulo{ 84214501Srpaulo while (vlan) { 85214501Srpaulo if (vlan->vlan_id != VLAN_ID_WILDCARD) { 86337817Scy if (vlan_if_add(hapd, vlan, 1)) { 87337817Scy wpa_printf(MSG_ERROR, 88337817Scy "VLAN: Could not add VLAN %s: %s", 89337817Scy vlan->ifname, strerror(errno)); 90337817Scy return -1; 91214501Srpaulo } 92214501Srpaulo#ifdef CONFIG_FULL_DYNAMIC_VLAN 93337817Scy vlan_newlink(vlan->ifname, hapd); 94214501Srpaulo#endif /* CONFIG_FULL_DYNAMIC_VLAN */ 95214501Srpaulo } 96214501Srpaulo 97214501Srpaulo vlan = vlan->next; 98214501Srpaulo } 99214501Srpaulo 100214501Srpaulo return 0; 101214501Srpaulo} 102214501Srpaulo 103214501Srpaulo 104214501Srpaulostatic void vlan_dynamic_remove(struct hostapd_data *hapd, 105214501Srpaulo struct hostapd_vlan *vlan) 106214501Srpaulo{ 107214501Srpaulo struct hostapd_vlan *next; 108214501Srpaulo 109214501Srpaulo while (vlan) { 110214501Srpaulo next = vlan->next; 111214501Srpaulo 112337817Scy#ifdef CONFIG_FULL_DYNAMIC_VLAN 113337817Scy /* vlan_dellink() takes care of cleanup and interface removal */ 114337817Scy if (vlan->vlan_id != VLAN_ID_WILDCARD) 115337817Scy vlan_dellink(vlan->ifname, hapd); 116337817Scy#else /* CONFIG_FULL_DYNAMIC_VLAN */ 117214501Srpaulo if (vlan->vlan_id != VLAN_ID_WILDCARD && 118337817Scy vlan_if_remove(hapd, vlan)) { 119214501Srpaulo wpa_printf(MSG_ERROR, "VLAN: Could not remove VLAN " 120214501Srpaulo "iface: %s: %s", 121214501Srpaulo vlan->ifname, strerror(errno)); 122214501Srpaulo } 123214501Srpaulo#endif /* CONFIG_FULL_DYNAMIC_VLAN */ 124214501Srpaulo 125214501Srpaulo vlan = next; 126214501Srpaulo } 127214501Srpaulo} 128214501Srpaulo 129214501Srpaulo 130214501Srpauloint vlan_init(struct hostapd_data *hapd) 131214501Srpaulo{ 132214501Srpaulo#ifdef CONFIG_FULL_DYNAMIC_VLAN 133214501Srpaulo hapd->full_dynamic_vlan = full_dynamic_vlan_init(hapd); 134214501Srpaulo#endif /* CONFIG_FULL_DYNAMIC_VLAN */ 135214501Srpaulo 136337817Scy if ((hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED || 137337817Scy hapd->conf->ssid.per_sta_vif) && 138281806Srpaulo !hapd->conf->vlan) { 139281806Srpaulo /* dynamic vlans enabled but no (or empty) vlan_file given */ 140281806Srpaulo struct hostapd_vlan *vlan; 141346981Scy int ret; 142346981Scy 143281806Srpaulo vlan = os_zalloc(sizeof(*vlan)); 144281806Srpaulo if (vlan == NULL) { 145281806Srpaulo wpa_printf(MSG_ERROR, "Out of memory while assigning " 146281806Srpaulo "VLAN interfaces"); 147281806Srpaulo return -1; 148281806Srpaulo } 149281806Srpaulo 150281806Srpaulo vlan->vlan_id = VLAN_ID_WILDCARD; 151346981Scy ret = os_snprintf(vlan->ifname, sizeof(vlan->ifname), "%s.#", 152346981Scy hapd->conf->iface); 153346981Scy if (ret >= (int) sizeof(vlan->ifname)) { 154346981Scy wpa_printf(MSG_WARNING, 155346981Scy "VLAN: Interface name was truncated to %s", 156346981Scy vlan->ifname); 157346981Scy } else if (ret < 0) { 158346981Scy os_free(vlan); 159346981Scy return ret; 160346981Scy } 161281806Srpaulo vlan->next = hapd->conf->vlan; 162281806Srpaulo hapd->conf->vlan = vlan; 163281806Srpaulo } 164281806Srpaulo 165214501Srpaulo if (vlan_dynamic_add(hapd, hapd->conf->vlan)) 166214501Srpaulo return -1; 167214501Srpaulo 168214501Srpaulo return 0; 169214501Srpaulo} 170214501Srpaulo 171214501Srpaulo 172214501Srpaulovoid vlan_deinit(struct hostapd_data *hapd) 173214501Srpaulo{ 174214501Srpaulo vlan_dynamic_remove(hapd, hapd->conf->vlan); 175214501Srpaulo 176214501Srpaulo#ifdef CONFIG_FULL_DYNAMIC_VLAN 177214501Srpaulo full_dynamic_vlan_deinit(hapd->full_dynamic_vlan); 178281806Srpaulo hapd->full_dynamic_vlan = NULL; 179214501Srpaulo#endif /* CONFIG_FULL_DYNAMIC_VLAN */ 180214501Srpaulo} 181214501Srpaulo 182214501Srpaulo 183214501Srpaulostruct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, 184214501Srpaulo struct hostapd_vlan *vlan, 185337817Scy int vlan_id, 186337817Scy struct vlan_description *vlan_desc) 187214501Srpaulo{ 188337817Scy struct hostapd_vlan *n; 189337817Scy char ifname[IFNAMSIZ + 1], *pos; 190346981Scy int ret; 191214501Srpaulo 192337817Scy if (vlan == NULL || vlan->vlan_id != VLAN_ID_WILDCARD) 193214501Srpaulo return NULL; 194214501Srpaulo 195214501Srpaulo wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d ifname=%s)", 196214501Srpaulo __func__, vlan_id, vlan->ifname); 197337817Scy os_strlcpy(ifname, vlan->ifname, sizeof(ifname)); 198214501Srpaulo pos = os_strchr(ifname, '#'); 199281806Srpaulo if (pos == NULL) 200337817Scy return NULL; 201214501Srpaulo *pos++ = '\0'; 202214501Srpaulo 203214501Srpaulo n = os_zalloc(sizeof(*n)); 204281806Srpaulo if (n == NULL) 205337817Scy return NULL; 206214501Srpaulo 207214501Srpaulo n->vlan_id = vlan_id; 208337817Scy if (vlan_desc) 209337817Scy n->vlan_desc = *vlan_desc; 210214501Srpaulo n->dynamic_vlan = 1; 211214501Srpaulo 212346981Scy ret = os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", 213346981Scy ifname, vlan_id, pos); 214346981Scy if (os_snprintf_error(sizeof(n->ifname), ret)) { 215346981Scy os_free(n); 216346981Scy return NULL; 217346981Scy } 218346981Scy os_strlcpy(n->bridge, vlan->bridge, sizeof(n->bridge)); 219214501Srpaulo 220337817Scy n->next = hapd->conf->vlan; 221337817Scy hapd->conf->vlan = n; 222337817Scy 223337817Scy /* hapd->conf->vlan needs this new VLAN here for WPA setup */ 224337817Scy if (vlan_if_add(hapd, n, 0)) { 225337817Scy hapd->conf->vlan = n->next; 226214501Srpaulo os_free(n); 227281806Srpaulo n = NULL; 228214501Srpaulo } 229214501Srpaulo 230214501Srpaulo return n; 231214501Srpaulo} 232214501Srpaulo 233214501Srpaulo 234214501Srpauloint vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id) 235214501Srpaulo{ 236214501Srpaulo struct hostapd_vlan *vlan; 237214501Srpaulo 238337817Scy if (vlan_id <= 0) 239214501Srpaulo return 1; 240214501Srpaulo 241289549Srpaulo wpa_printf(MSG_DEBUG, "VLAN: %s(ifname=%s vlan_id=%d)", 242289549Srpaulo __func__, hapd->conf->iface, vlan_id); 243214501Srpaulo 244214501Srpaulo vlan = hapd->conf->vlan; 245214501Srpaulo while (vlan) { 246214501Srpaulo if (vlan->vlan_id == vlan_id && vlan->dynamic_vlan > 0) { 247214501Srpaulo vlan->dynamic_vlan--; 248214501Srpaulo break; 249214501Srpaulo } 250214501Srpaulo vlan = vlan->next; 251214501Srpaulo } 252214501Srpaulo 253214501Srpaulo if (vlan == NULL) 254214501Srpaulo return 1; 255214501Srpaulo 256289549Srpaulo if (vlan->dynamic_vlan == 0) { 257337817Scy vlan_if_remove(hapd, vlan); 258289549Srpaulo#ifdef CONFIG_FULL_DYNAMIC_VLAN 259289549Srpaulo vlan_dellink(vlan->ifname, hapd); 260289549Srpaulo#endif /* CONFIG_FULL_DYNAMIC_VLAN */ 261289549Srpaulo } 262214501Srpaulo 263214501Srpaulo return 0; 264214501Srpaulo} 265