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