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
22static 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
70int 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
84static 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
107static 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
133int 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
175void 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
186struct 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
237int 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