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