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