1 /*
2  * hostapd / VLAN initialization
3  * Copyright 2003, Instant802 Networks, Inc.
4  * Copyright 2005-2006, Devicescape Software, Inc.
5  * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
6  *
7  * This software may be distributed under the terms of the BSD license.
8  * See README for more details.
9  */
10 
11 #include "utils/includes.h"
12 
13 #include "utils/common.h"
14 #include "hostapd.h"
15 #include "ap_config.h"
16 #include "ap_drv_ops.h"
17 #include "wpa_auth.h"
18 #include "vlan_init.h"
19 #include "vlan_util.h"
20 
21 
vlan_if_add(struct hostapd_data * hapd,struct hostapd_vlan * vlan,int existsok)22 static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
23 		       int existsok)
24 {
25 	int ret;
26 #ifdef CONFIG_WEP
27 	int i;
28 
29 	for (i = 0; i < NUM_WEP_KEYS; i++) {
30 		if (!hapd->conf->ssid.wep.key[i])
31 			continue;
32 		wpa_printf(MSG_ERROR,
33 			   "VLAN: Refusing to set up VLAN iface %s with WEP",
34 			   vlan->ifname);
35 		return -1;
36 	}
37 #endif /* CONFIG_WEP */
38 
39 	if (!iface_exists(vlan->ifname))
40 		ret = hostapd_vlan_if_add(hapd, vlan->ifname);
41 	else if (!existsok)
42 		return -1;
43 	else
44 		ret = 0;
45 
46 	if (ret)
47 		return ret;
48 
49 	ifconfig_up(vlan->ifname); /* else wpa group will fail fatal */
50 
51 	if (hapd->wpa_auth)
52 		ret = wpa_auth_ensure_group(hapd->wpa_auth, vlan->vlan_id);
53 
54 	if (ret == 0)
55 		return ret;
56 
57 	wpa_printf(MSG_ERROR, "WPA initialization for VLAN %d failed (%d)",
58 		   vlan->vlan_id, ret);
59 	if (wpa_auth_release_group(hapd->wpa_auth, vlan->vlan_id))
60 		wpa_printf(MSG_ERROR, "WPA deinit of %s failed", vlan->ifname);
61 
62 	/* group state machine setup failed */
63 	if (hostapd_vlan_if_remove(hapd, vlan->ifname))
64 		wpa_printf(MSG_ERROR, "Removal of %s failed", vlan->ifname);
65 
66 	return ret;
67 }
68 
69 
vlan_if_remove(struct hostapd_data * hapd,struct hostapd_vlan * vlan)70 int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
71 {
72 	int ret;
73 
74 	ret = wpa_auth_release_group(hapd->wpa_auth, vlan->vlan_id);
75 	if (ret)
76 		wpa_printf(MSG_ERROR,
77 			   "WPA deinitialization for VLAN %d failed (%d)",
78 			   vlan->vlan_id, ret);
79 
80 	return hostapd_vlan_if_remove(hapd, vlan->ifname);
81 }
82 
83 
vlan_dynamic_add(struct hostapd_data * hapd,struct hostapd_vlan * vlan)84 static int vlan_dynamic_add(struct hostapd_data *hapd,
85 			    struct hostapd_vlan *vlan)
86 {
87 	while (vlan) {
88 		if (vlan->vlan_id != VLAN_ID_WILDCARD) {
89 			if (vlan_if_add(hapd, vlan, 1)) {
90 				wpa_printf(MSG_ERROR,
91 					   "VLAN: Could not add VLAN %s: %s",
92 					   vlan->ifname, strerror(errno));
93 				return -1;
94 			}
95 #ifdef CONFIG_FULL_DYNAMIC_VLAN
96 			vlan_newlink(vlan->ifname, hapd);
97 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
98 		}
99 
100 		vlan = vlan->next;
101 	}
102 
103 	return 0;
104 }
105 
106 
vlan_dynamic_remove(struct hostapd_data * hapd,struct hostapd_vlan * vlan)107 static void vlan_dynamic_remove(struct hostapd_data *hapd,
108 				struct hostapd_vlan *vlan)
109 {
110 	struct hostapd_vlan *next;
111 
112 	while (vlan) {
113 		next = vlan->next;
114 
115 #ifdef CONFIG_FULL_DYNAMIC_VLAN
116 		/* vlan_dellink() takes care of cleanup and interface removal */
117 		if (vlan->vlan_id != VLAN_ID_WILDCARD)
118 			vlan_dellink(vlan->ifname, hapd);
119 #else /* CONFIG_FULL_DYNAMIC_VLAN */
120 		if (vlan->vlan_id != VLAN_ID_WILDCARD &&
121 		    vlan_if_remove(hapd, vlan)) {
122 			wpa_printf(MSG_ERROR, "VLAN: Could not remove VLAN "
123 				   "iface: %s: %s",
124 				   vlan->ifname, strerror(errno));
125 		}
126 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
127 
128 		vlan = next;
129 	}
130 }
131 
132 
vlan_init(struct hostapd_data * hapd)133 int vlan_init(struct hostapd_data *hapd)
134 {
135 #ifdef CONFIG_FULL_DYNAMIC_VLAN
136 	hapd->full_dynamic_vlan = full_dynamic_vlan_init(hapd);
137 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
138 
139 	if ((hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED ||
140 	     hapd->conf->ssid.per_sta_vif) &&
141 	    !hapd->conf->vlan) {
142 		/* dynamic vlans enabled but no (or empty) vlan_file given */
143 		struct hostapd_vlan *vlan;
144 		int ret;
145 
146 		vlan = os_zalloc(sizeof(*vlan));
147 		if (vlan == NULL) {
148 			wpa_printf(MSG_ERROR, "Out of memory while assigning "
149 				   "VLAN interfaces");
150 			return -1;
151 		}
152 
153 		vlan->vlan_id = VLAN_ID_WILDCARD;
154 		ret = os_snprintf(vlan->ifname, sizeof(vlan->ifname), "%s.#",
155 				  hapd->conf->iface);
156 		if (ret >= (int) sizeof(vlan->ifname)) {
157 			wpa_printf(MSG_WARNING,
158 				   "VLAN: Interface name was truncated to %s",
159 				   vlan->ifname);
160 		} else if (ret < 0) {
161 			os_free(vlan);
162 			return ret;
163 		}
164 		vlan->next = hapd->conf->vlan;
165 		hapd->conf->vlan = vlan;
166 	}
167 
168 	if (vlan_dynamic_add(hapd, hapd->conf->vlan))
169 		return -1;
170 
171         return 0;
172 }
173 
174 
vlan_deinit(struct hostapd_data * hapd)175 void vlan_deinit(struct hostapd_data *hapd)
176 {
177 	vlan_dynamic_remove(hapd, hapd->conf->vlan);
178 
179 #ifdef CONFIG_FULL_DYNAMIC_VLAN
180 	full_dynamic_vlan_deinit(hapd->full_dynamic_vlan);
181 	hapd->full_dynamic_vlan = NULL;
182 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
183 }
184 
185 
vlan_add_dynamic(struct hostapd_data * hapd,struct hostapd_vlan * vlan,int vlan_id,struct vlan_description * vlan_desc)186 struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
187 				       struct hostapd_vlan *vlan,
188 				       int vlan_id,
189 				       struct vlan_description *vlan_desc)
190 {
191 	struct hostapd_vlan *n;
192 	char ifname[IFNAMSIZ + 1], *pos;
193 	int ret;
194 
195 	if (vlan == NULL || vlan->vlan_id != VLAN_ID_WILDCARD)
196 		return NULL;
197 
198 	wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d ifname=%s)",
199 		   __func__, vlan_id, vlan->ifname);
200 	os_strlcpy(ifname, vlan->ifname, sizeof(ifname));
201 	pos = os_strchr(ifname, '#');
202 	if (pos == NULL)
203 		return NULL;
204 	*pos++ = '\0';
205 
206 	n = os_zalloc(sizeof(*n));
207 	if (n == NULL)
208 		return NULL;
209 
210 	n->vlan_id = vlan_id;
211 	if (vlan_desc)
212 		n->vlan_desc = *vlan_desc;
213 	n->dynamic_vlan = 1;
214 
215 	ret = os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s",
216 			  ifname, vlan_id, pos);
217 	if (os_snprintf_error(sizeof(n->ifname), ret)) {
218 		os_free(n);
219 		return NULL;
220 	}
221 	os_strlcpy(n->bridge, vlan->bridge, sizeof(n->bridge));
222 
223 	n->next = hapd->conf->vlan;
224 	hapd->conf->vlan = n;
225 
226 	/* hapd->conf->vlan needs this new VLAN here for WPA setup */
227 	if (vlan_if_add(hapd, n, 0)) {
228 		hapd->conf->vlan = n->next;
229 		os_free(n);
230 		n = NULL;
231 	}
232 
233 	return n;
234 }
235 
236 
vlan_remove_dynamic(struct hostapd_data * hapd,int vlan_id)237 int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id)
238 {
239 	struct hostapd_vlan *vlan;
240 
241 	if (vlan_id <= 0)
242 		return 1;
243 
244 	wpa_printf(MSG_DEBUG, "VLAN: %s(ifname=%s vlan_id=%d)",
245 		   __func__, hapd->conf->iface, vlan_id);
246 
247 	vlan = hapd->conf->vlan;
248 	while (vlan) {
249 		if (vlan->vlan_id == vlan_id && vlan->dynamic_vlan > 0) {
250 			vlan->dynamic_vlan--;
251 			break;
252 		}
253 		vlan = vlan->next;
254 	}
255 
256 	if (vlan == NULL)
257 		return 1;
258 
259 	if (vlan->dynamic_vlan == 0) {
260 		vlan_if_remove(hapd, vlan);
261 #ifdef CONFIG_FULL_DYNAMIC_VLAN
262 		vlan_dellink(vlan->ifname, hapd);
263 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
264 	}
265 
266 	return 0;
267 }
268