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