vlan_init.c revision 252726
1219019Sgabor/*
2219019Sgabor * hostapd / VLAN initialization
3219019Sgabor * Copyright 2003, Instant802 Networks, Inc.
4219019Sgabor * Copyright 2005-2006, Devicescape Software, Inc.
5219019Sgabor * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
6219019Sgabor *
7219019Sgabor * This program is free software; you can redistribute it and/or modify
8219019Sgabor * it under the terms of the GNU General Public License version 2 as
9219019Sgabor * published by the Free Software Foundation.
10219019Sgabor *
11219019Sgabor * Alternatively, this software may be distributed under the terms of BSD
12219019Sgabor * license.
13219019Sgabor *
14219019Sgabor * See README and COPYING for more details.
15219019Sgabor */
16219019Sgabor
17219019Sgabor#include "utils/includes.h"
18219019Sgabor
19219019Sgabor#include "utils/common.h"
20219019Sgabor#include "hostapd.h"
21219019Sgabor#include "ap_config.h"
22219019Sgabor#include "ap_drv_ops.h"
23219019Sgabor#include "vlan_init.h"
24219019Sgabor#include "vlan_util.h"
25219019Sgabor
26219019Sgabor
27219019Sgabor#ifdef CONFIG_FULL_DYNAMIC_VLAN
28219019Sgabor
29219019Sgabor#include <net/if.h>
30219019Sgabor#include <sys/ioctl.h>
31219019Sgabor#include <linux/sockios.h>
32219019Sgabor#include <linux/if_vlan.h>
33219019Sgabor#include <linux/if_bridge.h>
34219019Sgabor
35219019Sgabor#include "drivers/priv_netlink.h"
36219019Sgabor#include "utils/eloop.h"
37219019Sgabor
38219019Sgabor
39219019Sgaborstruct full_dynamic_vlan {
40219019Sgabor	int s; /* socket on which to listen for new/removed interfaces. */
41219019Sgabor};
42219019Sgabor
43219019Sgabor
44219019Sgaborstatic int ifconfig_helper(const char *if_name, int up)
45219019Sgabor{
46219019Sgabor	int fd;
47219019Sgabor	struct ifreq ifr;
48219019Sgabor
49219019Sgabor	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
50219019Sgabor		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
51219019Sgabor			   "failed: %s", __func__, strerror(errno));
52219019Sgabor		return -1;
53219019Sgabor	}
54219019Sgabor
55219019Sgabor	os_memset(&ifr, 0, sizeof(ifr));
56219019Sgabor	os_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ);
57219019Sgabor
58219019Sgabor	if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) {
59219019Sgabor		wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCGIFFLAGS) failed "
60219019Sgabor			   "for interface %s: %s",
61219019Sgabor			   __func__, if_name, strerror(errno));
62219019Sgabor		close(fd);
63219019Sgabor		return -1;
64219019Sgabor	}
65219019Sgabor
66219019Sgabor	if (up)
67219019Sgabor		ifr.ifr_flags |= IFF_UP;
68219019Sgabor	else
69219019Sgabor		ifr.ifr_flags &= ~IFF_UP;
70219019Sgabor
71219019Sgabor	if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) {
72219019Sgabor		wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCSIFFLAGS) failed "
73219019Sgabor			   "for interface %s (up=%d): %s",
74219019Sgabor			   __func__, if_name, up, strerror(errno));
75219019Sgabor		close(fd);
76219019Sgabor		return -1;
77219019Sgabor	}
78219019Sgabor
79219019Sgabor	close(fd);
80219019Sgabor	return 0;
81219019Sgabor}
82219019Sgabor
83219019Sgabor
84219019Sgaborstatic int ifconfig_up(const char *if_name)
85219019Sgabor{
86219019Sgabor	wpa_printf(MSG_DEBUG, "VLAN: Set interface %s up", if_name);
87219019Sgabor	return ifconfig_helper(if_name, 1);
88219019Sgabor}
89219019Sgabor
90219019Sgabor
91219019Sgaborstatic int ifconfig_down(const char *if_name)
92219019Sgabor{
93219019Sgabor	wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", if_name);
94219019Sgabor	return ifconfig_helper(if_name, 0);
95219019Sgabor}
96219019Sgabor
97219019Sgabor
98219019Sgabor/*
99219019Sgabor * These are only available in recent linux headers (without the leading
100219019Sgabor * underscore).
101219019Sgabor */
102219019Sgabor#define _GET_VLAN_REALDEV_NAME_CMD	8
103219019Sgabor#define _GET_VLAN_VID_CMD		9
104219019Sgabor
105219019Sgabor/* This value should be 256 ONLY. If it is something else, then hostapd
106219019Sgabor * might crash!, as this value has been hard-coded in 2.4.x kernel
107219019Sgabor * bridging code.
108219019Sgabor */
109219019Sgabor#define MAX_BR_PORTS      		256
110219019Sgabor
111219019Sgaborstatic int br_delif(const char *br_name, const char *if_name)
112219019Sgabor{
113219019Sgabor	int fd;
114219019Sgabor	struct ifreq ifr;
115219019Sgabor	unsigned long args[2];
116219019Sgabor	int if_index;
117219019Sgabor
118219019Sgabor	wpa_printf(MSG_DEBUG, "VLAN: br_delif(%s, %s)", br_name, if_name);
119219019Sgabor	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
120219019Sgabor		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
121219019Sgabor			   "failed: %s", __func__, strerror(errno));
122219019Sgabor		return -1;
123219019Sgabor	}
124219019Sgabor
125219019Sgabor	if_index = if_nametoindex(if_name);
126219019Sgabor
127219019Sgabor	if (if_index == 0) {
128219019Sgabor		wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
129219019Sgabor			   "interface index for '%s'",
130219019Sgabor			   __func__, if_name);
131219019Sgabor		close(fd);
132219019Sgabor		return -1;
133219019Sgabor	}
134219019Sgabor
135219019Sgabor	args[0] = BRCTL_DEL_IF;
136219019Sgabor	args[1] = if_index;
137219019Sgabor
138219019Sgabor	os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
139219019Sgabor	ifr.ifr_data = (__caddr_t) args;
140219019Sgabor
141219019Sgabor	if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) {
142219019Sgabor		/* No error if interface already removed. */
143219019Sgabor		wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
144219019Sgabor			   "BRCTL_DEL_IF] failed for br_name=%s if_name=%s: "
145219019Sgabor			   "%s", __func__, br_name, if_name, strerror(errno));
146219019Sgabor		close(fd);
147219019Sgabor		return -1;
148219019Sgabor	}
149219019Sgabor
150219019Sgabor	close(fd);
151219019Sgabor	return 0;
152219019Sgabor}
153219019Sgabor
154219019Sgabor
155219019Sgabor/*
156219019Sgabor	Add interface 'if_name' to the bridge 'br_name'
157219019Sgabor
158219019Sgabor	returns -1 on error
159219019Sgabor	returns 1 if the interface is already part of the bridge
160219019Sgabor	returns 0 otherwise
161219019Sgabor*/
162219019Sgaborstatic int br_addif(const char *br_name, const char *if_name)
163219019Sgabor{
164219019Sgabor	int fd;
165219019Sgabor	struct ifreq ifr;
166219019Sgabor	unsigned long args[2];
167219019Sgabor	int if_index;
168219019Sgabor
169219019Sgabor	wpa_printf(MSG_DEBUG, "VLAN: br_addif(%s, %s)", br_name, if_name);
170219019Sgabor	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
171219019Sgabor		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
172219019Sgabor			   "failed: %s", __func__, strerror(errno));
173219019Sgabor		return -1;
174219019Sgabor	}
175219019Sgabor
176219019Sgabor	if_index = if_nametoindex(if_name);
177219019Sgabor
178219019Sgabor	if (if_index == 0) {
179219019Sgabor		wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
180219019Sgabor			   "interface index for '%s'",
181219019Sgabor			   __func__, if_name);
182219019Sgabor		close(fd);
183219019Sgabor		return -1;
184219019Sgabor	}
185219019Sgabor
186219019Sgabor	args[0] = BRCTL_ADD_IF;
187219019Sgabor	args[1] = if_index;
188219019Sgabor
189219019Sgabor	os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
190219019Sgabor	ifr.ifr_data = (__caddr_t) args;
191219019Sgabor
192219019Sgabor	if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
193219019Sgabor		if (errno == EBUSY) {
194219019Sgabor			/* The interface is already added. */
195219019Sgabor			close(fd);
196219019Sgabor			return 1;
197219019Sgabor		}
198219019Sgabor
199219019Sgabor		wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
200219019Sgabor			   "BRCTL_ADD_IF] failed for br_name=%s if_name=%s: "
201219019Sgabor			   "%s", __func__, br_name, if_name, strerror(errno));
202219019Sgabor		close(fd);
203219019Sgabor		return -1;
204219019Sgabor	}
205219019Sgabor
206219019Sgabor	close(fd);
207219019Sgabor	return 0;
208219019Sgabor}
209219019Sgabor
210219019Sgabor
211219019Sgaborstatic int br_delbr(const char *br_name)
212219019Sgabor{
213219019Sgabor	int fd;
214219019Sgabor	unsigned long arg[2];
215219019Sgabor
216219019Sgabor	wpa_printf(MSG_DEBUG, "VLAN: br_delbr(%s)", br_name);
217219019Sgabor	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
218219019Sgabor		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
219219019Sgabor			   "failed: %s", __func__, strerror(errno));
220219019Sgabor		return -1;
221219019Sgabor	}
222219019Sgabor
223219019Sgabor	arg[0] = BRCTL_DEL_BRIDGE;
224219019Sgabor	arg[1] = (unsigned long) br_name;
225219019Sgabor
226219019Sgabor	if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) {
227219019Sgabor		/* No error if bridge already removed. */
228219019Sgabor		wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_DEL_BRIDGE failed for "
229219019Sgabor			   "%s: %s", __func__, br_name, strerror(errno));
230219019Sgabor		close(fd);
231219019Sgabor		return -1;
232219019Sgabor	}
233219019Sgabor
234219019Sgabor	close(fd);
235219019Sgabor	return 0;
236219019Sgabor}
237219019Sgabor
238219019Sgabor
239219019Sgabor/*
240219019Sgabor	Add a bridge with the name 'br_name'.
241219019Sgabor
242219019Sgabor	returns -1 on error
243219019Sgabor	returns 1 if the bridge already exists
244219019Sgabor	returns 0 otherwise
245219019Sgabor*/
246219019Sgaborstatic int br_addbr(const char *br_name)
247219019Sgabor{
248219019Sgabor	int fd;
249219019Sgabor	unsigned long arg[4];
250219019Sgabor	struct ifreq ifr;
251219019Sgabor
252219019Sgabor	wpa_printf(MSG_DEBUG, "VLAN: br_addbr(%s)", br_name);
253219019Sgabor	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
254219019Sgabor		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
255219019Sgabor			   "failed: %s", __func__, strerror(errno));
256219019Sgabor		return -1;
257219019Sgabor	}
258219019Sgabor
259219019Sgabor	arg[0] = BRCTL_ADD_BRIDGE;
260219019Sgabor	arg[1] = (unsigned long) br_name;
261219019Sgabor
262219019Sgabor	if (ioctl(fd, SIOCGIFBR, arg) < 0) {
263219019Sgabor 		if (errno == EEXIST) {
264219019Sgabor			/* The bridge is already added. */
265219019Sgabor			close(fd);
266219019Sgabor			return 1;
267219019Sgabor		} else {
268219019Sgabor			wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_ADD_BRIDGE "
269219019Sgabor				   "failed for %s: %s",
270219019Sgabor				   __func__, br_name, strerror(errno));
271219019Sgabor			close(fd);
272219019Sgabor			return -1;
273219019Sgabor		}
274219019Sgabor	}
275219019Sgabor
276219019Sgabor	/* Decrease forwarding delay to avoid EAPOL timeouts. */
277219019Sgabor	os_memset(&ifr, 0, sizeof(ifr));
278219019Sgabor	os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ);
279219019Sgabor	arg[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY;
280219019Sgabor	arg[1] = 1;
281219019Sgabor	arg[2] = 0;
282219019Sgabor	arg[3] = 0;
283219019Sgabor	ifr.ifr_data = (char *) &arg;
284219019Sgabor	if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
285219019Sgabor		wpa_printf(MSG_ERROR, "VLAN: %s: "
286219019Sgabor			   "BRCTL_SET_BRIDGE_FORWARD_DELAY (1 sec) failed for "
287219019Sgabor			   "%s: %s", __func__, br_name, strerror(errno));
288219019Sgabor		/* Continue anyway */
289219019Sgabor	}
290219019Sgabor
291219019Sgabor	close(fd);
292219019Sgabor	return 0;
293219019Sgabor}
294219019Sgabor
295219019Sgabor
296219019Sgaborstatic int br_getnumports(const char *br_name)
297219019Sgabor{
298219019Sgabor	int fd;
299219019Sgabor	int i;
300219019Sgabor	int port_cnt = 0;
301219019Sgabor	unsigned long arg[4];
302219019Sgabor	int ifindices[MAX_BR_PORTS];
303219019Sgabor	struct ifreq ifr;
304219019Sgabor
305219019Sgabor	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
306219019Sgabor		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
307219019Sgabor			   "failed: %s", __func__, strerror(errno));
308219019Sgabor		return -1;
309219019Sgabor	}
310219019Sgabor
311219019Sgabor	arg[0] = BRCTL_GET_PORT_LIST;
312219019Sgabor	arg[1] = (unsigned long) ifindices;
313219019Sgabor	arg[2] = MAX_BR_PORTS;
314219019Sgabor	arg[3] = 0;
315219019Sgabor
316219019Sgabor	os_memset(ifindices, 0, sizeof(ifindices));
317219019Sgabor	os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
318219019Sgabor	ifr.ifr_data = (__caddr_t) arg;
319219019Sgabor
320219019Sgabor	if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
321219019Sgabor		wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_GET_PORT_LIST "
322219019Sgabor			   "failed for %s: %s",
323219019Sgabor			   __func__, br_name, strerror(errno));
324219019Sgabor		close(fd);
325219019Sgabor		return -1;
326219019Sgabor	}
327219019Sgabor
328219019Sgabor	for (i = 1; i < MAX_BR_PORTS; i++) {
329219019Sgabor		if (ifindices[i] > 0) {
330219019Sgabor			port_cnt++;
331219019Sgabor		}
332219019Sgabor	}
333219019Sgabor
334219019Sgabor	close(fd);
335219019Sgabor	return port_cnt;
336219019Sgabor}
337219019Sgabor
338219019Sgabor
339219019Sgabor#ifndef CONFIG_VLAN_NETLINK
340219019Sgabor
341219019Sgaborint vlan_rem(const char *if_name)
342219019Sgabor{
343219019Sgabor	int fd;
344219019Sgabor	struct vlan_ioctl_args if_request;
345219019Sgabor
346219019Sgabor	wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(%s)", if_name);
347219019Sgabor	if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) {
348219019Sgabor		wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
349219019Sgabor			   if_name);
350219019Sgabor		return -1;
351219019Sgabor	}
352219019Sgabor
353219019Sgabor	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
354219019Sgabor		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
355219019Sgabor			   "failed: %s", __func__, strerror(errno));
356219019Sgabor		return -1;
357219019Sgabor	}
358219019Sgabor
359219019Sgabor	os_memset(&if_request, 0, sizeof(if_request));
360219019Sgabor
361219019Sgabor	os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1));
362219019Sgabor	if_request.cmd = DEL_VLAN_CMD;
363219019Sgabor
364219019Sgabor	if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
365219019Sgabor		wpa_printf(MSG_ERROR, "VLAN: %s: DEL_VLAN_CMD failed for %s: "
366219019Sgabor			   "%s", __func__, if_name, strerror(errno));
367219019Sgabor		close(fd);
368219019Sgabor		return -1;
369219019Sgabor	}
370219019Sgabor
371219019Sgabor	close(fd);
372219019Sgabor	return 0;
373219019Sgabor}
374219019Sgabor
375219019Sgabor
376219019Sgabor/*
377219019Sgabor	Add a vlan interface with VLAN ID 'vid' and tagged interface
378219019Sgabor	'if_name'.
379219019Sgabor
380219019Sgabor	returns -1 on error
381219019Sgabor	returns 1 if the interface already exists
382219019Sgabor	returns 0 otherwise
383219019Sgabor*/
384219019Sgaborint vlan_add(const char *if_name, int vid, const char *vlan_if_name)
385219019Sgabor{
386219019Sgabor	int fd;
387219019Sgabor	struct vlan_ioctl_args if_request;
388219019Sgabor
389219019Sgabor	wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d)",
390219019Sgabor		   if_name, vid);
391219019Sgabor	ifconfig_up(if_name);
392219019Sgabor
393219019Sgabor	if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) {
394219019Sgabor		wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
395219019Sgabor			   if_name);
396219019Sgabor		return -1;
397219019Sgabor	}
398219019Sgabor
399219019Sgabor	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
400219019Sgabor		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
401219019Sgabor			   "failed: %s", __func__, strerror(errno));
402219019Sgabor		return -1;
403219019Sgabor	}
404219019Sgabor
405219019Sgabor	os_memset(&if_request, 0, sizeof(if_request));
406219019Sgabor
407219019Sgabor	/* Determine if a suitable vlan device already exists. */
408219019Sgabor
409219019Sgabor	os_snprintf(if_request.device1, sizeof(if_request.device1), "vlan%d",
410219019Sgabor		    vid);
411219019Sgabor
412219019Sgabor	if_request.cmd = _GET_VLAN_VID_CMD;
413219019Sgabor
414219019Sgabor	if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0) {
415219019Sgabor
416219019Sgabor		if (if_request.u.VID == vid) {
417219019Sgabor			if_request.cmd = _GET_VLAN_REALDEV_NAME_CMD;
418219019Sgabor
419219019Sgabor			if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 &&
420219019Sgabor			    os_strncmp(if_request.u.device2, if_name,
421219019Sgabor				       sizeof(if_request.u.device2)) == 0) {
422219019Sgabor				close(fd);
423219019Sgabor				wpa_printf(MSG_DEBUG, "VLAN: vlan_add: "
424219019Sgabor					   "if_name %s exists already",
425219019Sgabor					   if_request.device1);
426219019Sgabor				return 1;
427219019Sgabor			}
428219019Sgabor		}
429219019Sgabor	}
430219019Sgabor
431219019Sgabor	/* A suitable vlan device does not already exist, add one. */
432219019Sgabor
433219019Sgabor	os_memset(&if_request, 0, sizeof(if_request));
434219019Sgabor	os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1));
435219019Sgabor	if_request.u.VID = vid;
436219019Sgabor	if_request.cmd = ADD_VLAN_CMD;
437219019Sgabor
438219019Sgabor	if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
439219019Sgabor		wpa_printf(MSG_ERROR, "VLAN: %s: ADD_VLAN_CMD failed for %s: "
440219019Sgabor			   "%s",
441219019Sgabor			   __func__, if_request.device1, strerror(errno));
442219019Sgabor		close(fd);
443219019Sgabor		return -1;
444219019Sgabor	}
445219019Sgabor
446219019Sgabor	close(fd);
447219019Sgabor	return 0;
448219019Sgabor}
449219019Sgabor
450219019Sgabor
451219019Sgaborstatic int vlan_set_name_type(unsigned int name_type)
452219019Sgabor{
453219019Sgabor	int fd;
454219019Sgabor	struct vlan_ioctl_args if_request;
455219019Sgabor
456219019Sgabor	wpa_printf(MSG_DEBUG, "VLAN: vlan_set_name_type(name_type=%u)",
457219019Sgabor		   name_type);
458219019Sgabor	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
459219019Sgabor		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
460219019Sgabor			   "failed: %s", __func__, strerror(errno));
461219019Sgabor		return -1;
462219019Sgabor	}
463219019Sgabor
464219019Sgabor	os_memset(&if_request, 0, sizeof(if_request));
465219019Sgabor
466219019Sgabor	if_request.u.name_type = name_type;
467219019Sgabor	if_request.cmd = SET_VLAN_NAME_TYPE_CMD;
468219019Sgabor	if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
469219019Sgabor		wpa_printf(MSG_ERROR, "VLAN: %s: SET_VLAN_NAME_TYPE_CMD "
470219019Sgabor			   "name_type=%u failed: %s",
471219019Sgabor			   __func__, name_type, strerror(errno));
472219019Sgabor		close(fd);
473219019Sgabor		return -1;
474219019Sgabor	}
475219019Sgabor
476219019Sgabor	close(fd);
477219019Sgabor	return 0;
478219019Sgabor}
479219019Sgabor
480219019Sgabor#endif /* CONFIG_VLAN_NETLINK */
481219019Sgabor
482219019Sgabor
483219019Sgaborstatic void vlan_newlink(char *ifname, struct hostapd_data *hapd)
484219019Sgabor{
485219019Sgabor	char vlan_ifname[IFNAMSIZ];
486219019Sgabor	char br_name[IFNAMSIZ];
487219019Sgabor	struct hostapd_vlan *vlan = hapd->conf->vlan;
488219019Sgabor	char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
489219019Sgabor	int vlan_naming = hapd->conf->ssid.vlan_naming;
490219019Sgabor
491219019Sgabor	wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname);
492219019Sgabor
493219019Sgabor	while (vlan) {
494219019Sgabor		if (os_strcmp(ifname, vlan->ifname) == 0) {
495219019Sgabor
496219019Sgabor			os_snprintf(br_name, sizeof(br_name), "brvlan%d",
497219019Sgabor				    vlan->vlan_id);
498219019Sgabor
499219019Sgabor			if (!br_addbr(br_name))
500219019Sgabor				vlan->clean |= DVLAN_CLEAN_BR;
501219019Sgabor
502219019Sgabor			ifconfig_up(br_name);
503219019Sgabor
504219019Sgabor			if (tagged_interface) {
505219019Sgabor				if (vlan_naming ==
506219019Sgabor				    DYNAMIC_VLAN_NAMING_WITH_DEVICE)
507219019Sgabor					os_snprintf(vlan_ifname,
508219019Sgabor						    sizeof(vlan_ifname),
509219019Sgabor						    "%s.%d", tagged_interface,
510219019Sgabor						    vlan->vlan_id);
511219019Sgabor				else
512219019Sgabor					os_snprintf(vlan_ifname,
513219019Sgabor						    sizeof(vlan_ifname),
514219019Sgabor						    "vlan%d", vlan->vlan_id);
515219019Sgabor
516219019Sgabor				ifconfig_up(tagged_interface);
517219019Sgabor				if (!vlan_add(tagged_interface, vlan->vlan_id,
518219019Sgabor					      vlan_ifname))
519219019Sgabor					vlan->clean |= DVLAN_CLEAN_VLAN;
520219019Sgabor
521219019Sgabor				if (!br_addif(br_name, vlan_ifname))
522219019Sgabor					vlan->clean |= DVLAN_CLEAN_VLAN_PORT;
523219019Sgabor
524219019Sgabor				ifconfig_up(vlan_ifname);
525219019Sgabor			}
526219019Sgabor
527219019Sgabor			if (!br_addif(br_name, ifname))
528219019Sgabor				vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
529219019Sgabor
530219019Sgabor			ifconfig_up(ifname);
531219019Sgabor
532219019Sgabor			break;
533219019Sgabor		}
534219019Sgabor		vlan = vlan->next;
535219019Sgabor	}
536219019Sgabor}
537219019Sgabor
538219019Sgabor
539219019Sgaborstatic void vlan_dellink(char *ifname, struct hostapd_data *hapd)
540219019Sgabor{
541219019Sgabor	char vlan_ifname[IFNAMSIZ];
542219019Sgabor	char br_name[IFNAMSIZ];
543219019Sgabor	struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan;
544219019Sgabor	char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
545219019Sgabor	int vlan_naming = hapd->conf->ssid.vlan_naming;
546219019Sgabor
547219019Sgabor	wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname);
548219019Sgabor
549219019Sgabor	first = prev = vlan;
550219019Sgabor
551219019Sgabor	while (vlan) {
552219019Sgabor		if (os_strcmp(ifname, vlan->ifname) == 0) {
553219019Sgabor			os_snprintf(br_name, sizeof(br_name), "brvlan%d",
554219019Sgabor				    vlan->vlan_id);
555219019Sgabor
556219019Sgabor			if (vlan->clean & DVLAN_CLEAN_WLAN_PORT)
557219019Sgabor				br_delif(br_name, vlan->ifname);
558219019Sgabor
559219019Sgabor			if (tagged_interface) {
560219019Sgabor				if (vlan_naming ==
561219019Sgabor				    DYNAMIC_VLAN_NAMING_WITH_DEVICE)
562219019Sgabor					os_snprintf(vlan_ifname,
563219019Sgabor						    sizeof(vlan_ifname),
564219019Sgabor						    "%s.%d", tagged_interface,
565219019Sgabor						    vlan->vlan_id);
566219019Sgabor				else
567219019Sgabor					os_snprintf(vlan_ifname,
568219019Sgabor						    sizeof(vlan_ifname),
569219019Sgabor						    "vlan%d", vlan->vlan_id);
570219019Sgabor				if (vlan->clean & DVLAN_CLEAN_VLAN_PORT)
571219019Sgabor					br_delif(br_name, vlan_ifname);
572219019Sgabor				ifconfig_down(vlan_ifname);
573219019Sgabor
574219019Sgabor				if (vlan->clean & DVLAN_CLEAN_VLAN)
575219019Sgabor					vlan_rem(vlan_ifname);
576219019Sgabor			}
577219019Sgabor
578219019Sgabor			if ((vlan->clean & DVLAN_CLEAN_BR) &&
579219019Sgabor			    br_getnumports(br_name) == 0) {
580219019Sgabor				ifconfig_down(br_name);
581219019Sgabor				br_delbr(br_name);
582219019Sgabor			}
583219019Sgabor
584219019Sgabor			if (vlan == first) {
585219019Sgabor				hapd->conf->vlan = vlan->next;
586219019Sgabor			} else {
587219019Sgabor				prev->next = vlan->next;
588219019Sgabor			}
589219019Sgabor			os_free(vlan);
590219019Sgabor
591219019Sgabor			break;
592219019Sgabor		}
593219019Sgabor		prev = vlan;
594219019Sgabor		vlan = vlan->next;
595219019Sgabor	}
596219019Sgabor}
597219019Sgabor
598219019Sgabor
599219019Sgaborstatic void
600219019Sgaborvlan_read_ifnames(struct nlmsghdr *h, size_t len, int del,
601219019Sgabor		  struct hostapd_data *hapd)
602219019Sgabor{
603219019Sgabor	struct ifinfomsg *ifi;
604219019Sgabor	int attrlen, nlmsg_len, rta_len;
605219019Sgabor	struct rtattr *attr;
606219019Sgabor
607219019Sgabor	if (len < sizeof(*ifi))
608219019Sgabor		return;
609219019Sgabor
610219019Sgabor	ifi = NLMSG_DATA(h);
611219019Sgabor
612219019Sgabor	nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg));
613219019Sgabor
614219019Sgabor	attrlen = h->nlmsg_len - nlmsg_len;
615219019Sgabor	if (attrlen < 0)
616219019Sgabor		return;
617219019Sgabor
618219019Sgabor	attr = (struct rtattr *) (((char *) ifi) + nlmsg_len);
619219019Sgabor
620219019Sgabor	rta_len = RTA_ALIGN(sizeof(struct rtattr));
621219019Sgabor	while (RTA_OK(attr, attrlen)) {
622219019Sgabor		char ifname[IFNAMSIZ + 1];
623219019Sgabor
624219019Sgabor		if (attr->rta_type == IFLA_IFNAME) {
625219019Sgabor			int n = attr->rta_len - rta_len;
626219019Sgabor			if (n < 0)
627219019Sgabor				break;
628219019Sgabor
629219019Sgabor			os_memset(ifname, 0, sizeof(ifname));
630219019Sgabor
631219019Sgabor			if ((size_t) n > sizeof(ifname))
632219019Sgabor				n = sizeof(ifname);
633219019Sgabor			os_memcpy(ifname, ((char *) attr) + rta_len, n);
634219019Sgabor
635219019Sgabor			if (del)
636219019Sgabor				vlan_dellink(ifname, hapd);
637219019Sgabor			else
638219019Sgabor				vlan_newlink(ifname, hapd);
639219019Sgabor		}
640219019Sgabor
641219019Sgabor		attr = RTA_NEXT(attr, attrlen);
642219019Sgabor	}
643219019Sgabor}
644219019Sgabor
645219019Sgabor
646219019Sgaborstatic void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx)
647219019Sgabor{
648219019Sgabor	char buf[8192];
649219019Sgabor	int left;
650219019Sgabor	struct sockaddr_nl from;
651219019Sgabor	socklen_t fromlen;
652219019Sgabor	struct nlmsghdr *h;
653219019Sgabor	struct hostapd_data *hapd = eloop_ctx;
654219019Sgabor
655219019Sgabor	fromlen = sizeof(from);
656219019Sgabor	left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
657219019Sgabor			(struct sockaddr *) &from, &fromlen);
658219019Sgabor	if (left < 0) {
659219019Sgabor		if (errno != EINTR && errno != EAGAIN)
660219019Sgabor			wpa_printf(MSG_ERROR, "VLAN: %s: recvfrom failed: %s",
661219019Sgabor				   __func__, strerror(errno));
662219019Sgabor		return;
663219019Sgabor	}
664219019Sgabor
665219019Sgabor	h = (struct nlmsghdr *) buf;
666219019Sgabor	while (left >= (int) sizeof(*h)) {
667219019Sgabor		int len, plen;
668219019Sgabor
669219019Sgabor		len = h->nlmsg_len;
670219019Sgabor		plen = len - sizeof(*h);
671219019Sgabor		if (len > left || plen < 0) {
672219019Sgabor			wpa_printf(MSG_DEBUG, "VLAN: Malformed netlink "
673219019Sgabor				   "message: len=%d left=%d plen=%d",
674219019Sgabor				   len, left, plen);
675219019Sgabor			break;
676219019Sgabor		}
677219019Sgabor
678219019Sgabor		switch (h->nlmsg_type) {
679219019Sgabor		case RTM_NEWLINK:
680219019Sgabor			vlan_read_ifnames(h, plen, 0, hapd);
681219019Sgabor			break;
682219019Sgabor		case RTM_DELLINK:
683219019Sgabor			vlan_read_ifnames(h, plen, 1, hapd);
684219019Sgabor			break;
685219019Sgabor		}
686219019Sgabor
687219019Sgabor		len = NLMSG_ALIGN(len);
688219019Sgabor		left -= len;
689219019Sgabor		h = (struct nlmsghdr *) ((char *) h + len);
690219019Sgabor	}
691219019Sgabor
692219019Sgabor	if (left > 0) {
693219019Sgabor		wpa_printf(MSG_DEBUG, "VLAN: %s: %d extra bytes in the end of "
694219019Sgabor			   "netlink message", __func__, left);
695219019Sgabor	}
696219019Sgabor}
697219019Sgabor
698219019Sgabor
699219019Sgaborstatic struct full_dynamic_vlan *
700219019Sgaborfull_dynamic_vlan_init(struct hostapd_data *hapd)
701219019Sgabor{
702219019Sgabor	struct sockaddr_nl local;
703219019Sgabor	struct full_dynamic_vlan *priv;
704219019Sgabor
705219019Sgabor	priv = os_zalloc(sizeof(*priv));
706219019Sgabor	if (priv == NULL)
707219019Sgabor		return NULL;
708219019Sgabor
709219019Sgabor#ifndef CONFIG_VLAN_NETLINK
710219019Sgabor	vlan_set_name_type(hapd->conf->ssid.vlan_naming ==
711219019Sgabor			   DYNAMIC_VLAN_NAMING_WITH_DEVICE ?
712219019Sgabor			   VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD :
713219019Sgabor			   VLAN_NAME_TYPE_PLUS_VID_NO_PAD);
714219019Sgabor#endif /* CONFIG_VLAN_NETLINK */
715219019Sgabor
716219019Sgabor	priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
717219019Sgabor	if (priv->s < 0) {
718219019Sgabor		wpa_printf(MSG_ERROR, "VLAN: %s: socket(PF_NETLINK,SOCK_RAW,"
719219019Sgabor			   "NETLINK_ROUTE) failed: %s",
720219019Sgabor			   __func__, strerror(errno));
721219019Sgabor		os_free(priv);
722219019Sgabor		return NULL;
723219019Sgabor	}
724219019Sgabor
725219019Sgabor	os_memset(&local, 0, sizeof(local));
726219019Sgabor	local.nl_family = AF_NETLINK;
727219019Sgabor	local.nl_groups = RTMGRP_LINK;
728219019Sgabor	if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) {
729219019Sgabor		wpa_printf(MSG_ERROR, "VLAN: %s: bind(netlink) failed: %s",
730219019Sgabor			   __func__, strerror(errno));
731219019Sgabor		close(priv->s);
732219019Sgabor		os_free(priv);
733219019Sgabor		return NULL;
734219019Sgabor	}
735219019Sgabor
736219019Sgabor	if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, NULL))
737219019Sgabor	{
738219019Sgabor		close(priv->s);
739219019Sgabor		os_free(priv);
740219019Sgabor		return NULL;
741219019Sgabor	}
742219019Sgabor
743219019Sgabor	return priv;
744219019Sgabor}
745219019Sgabor
746219019Sgabor
747219019Sgaborstatic void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv)
748219019Sgabor{
749219019Sgabor	if (priv == NULL)
750219019Sgabor		return;
751219019Sgabor	eloop_unregister_read_sock(priv->s);
752219019Sgabor	close(priv->s);
753219019Sgabor	os_free(priv);
754219019Sgabor}
755219019Sgabor#endif /* CONFIG_FULL_DYNAMIC_VLAN */
756219019Sgabor
757219019Sgabor
758219019Sgaborint vlan_setup_encryption_dyn(struct hostapd_data *hapd,
759219019Sgabor			      struct hostapd_ssid *mssid, const char *dyn_vlan)
760219019Sgabor{
761219019Sgabor        int i;
762219019Sgabor
763219019Sgabor        if (dyn_vlan == NULL)
764219019Sgabor		return 0;
765219019Sgabor
766219019Sgabor	/* Static WEP keys are set here; IEEE 802.1X and WPA uses their own
767219019Sgabor	 * functions for setting up dynamic broadcast keys. */
768219019Sgabor	for (i = 0; i < 4; i++) {
769219019Sgabor		if (mssid->wep.key[i] &&
770219019Sgabor		    hostapd_drv_set_key(dyn_vlan, hapd, WPA_ALG_WEP, NULL, i,
771219019Sgabor					i == mssid->wep.idx, NULL, 0,
772219019Sgabor					mssid->wep.key[i], mssid->wep.len[i]))
773219019Sgabor		{
774219019Sgabor			wpa_printf(MSG_ERROR, "VLAN: Could not set WEP "
775219019Sgabor				   "encryption for dynamic VLAN");
776219019Sgabor			return -1;
777219019Sgabor		}
778219019Sgabor	}
779219019Sgabor
780219019Sgabor	return 0;
781219019Sgabor}
782219019Sgabor
783219019Sgabor
784219019Sgaborstatic int vlan_dynamic_add(struct hostapd_data *hapd,
785219019Sgabor			    struct hostapd_vlan *vlan)
786219019Sgabor{
787219019Sgabor	while (vlan) {
788219019Sgabor		if (vlan->vlan_id != VLAN_ID_WILDCARD) {
789219019Sgabor			if (hostapd_vlan_if_add(hapd, vlan->ifname)) {
790219019Sgabor				if (errno != EEXIST) {
791219019Sgabor					wpa_printf(MSG_ERROR, "VLAN: Could "
792219019Sgabor						   "not add VLAN %s: %s",
793219019Sgabor						   vlan->ifname,
794219019Sgabor						   strerror(errno));
795219019Sgabor					return -1;
796219019Sgabor				}
797219019Sgabor			}
798219019Sgabor#ifdef CONFIG_FULL_DYNAMIC_VLAN
799219019Sgabor			ifconfig_up(vlan->ifname);
800219019Sgabor#endif /* CONFIG_FULL_DYNAMIC_VLAN */
801219019Sgabor		}
802219019Sgabor
803219019Sgabor		vlan = vlan->next;
804219019Sgabor	}
805219019Sgabor
806219019Sgabor	return 0;
807219019Sgabor}
808219019Sgabor
809219019Sgabor
810219019Sgaborstatic void vlan_dynamic_remove(struct hostapd_data *hapd,
811219019Sgabor				struct hostapd_vlan *vlan)
812219019Sgabor{
813219019Sgabor	struct hostapd_vlan *next;
814219019Sgabor
815219019Sgabor	while (vlan) {
816219019Sgabor		next = vlan->next;
817219019Sgabor
818219019Sgabor		if (vlan->vlan_id != VLAN_ID_WILDCARD &&
819219019Sgabor		    hostapd_vlan_if_remove(hapd, vlan->ifname)) {
820219019Sgabor			wpa_printf(MSG_ERROR, "VLAN: Could not remove VLAN "
821219019Sgabor				   "iface: %s: %s",
822219019Sgabor				   vlan->ifname, strerror(errno));
823219019Sgabor		}
824219019Sgabor#ifdef CONFIG_FULL_DYNAMIC_VLAN
825219019Sgabor		if (vlan->clean)
826219019Sgabor			vlan_dellink(vlan->ifname, hapd);
827219019Sgabor#endif /* CONFIG_FULL_DYNAMIC_VLAN */
828219019Sgabor
829219019Sgabor		vlan = next;
830219019Sgabor	}
831219019Sgabor}
832219019Sgabor
833219019Sgabor
834219019Sgaborint vlan_init(struct hostapd_data *hapd)
835219019Sgabor{
836219019Sgabor#ifdef CONFIG_FULL_DYNAMIC_VLAN
837219019Sgabor	hapd->full_dynamic_vlan = full_dynamic_vlan_init(hapd);
838219019Sgabor#endif /* CONFIG_FULL_DYNAMIC_VLAN */
839219019Sgabor
840219019Sgabor	if (vlan_dynamic_add(hapd, hapd->conf->vlan))
841219019Sgabor		return -1;
842219019Sgabor
843219019Sgabor        return 0;
844219019Sgabor}
845219019Sgabor
846219019Sgabor
847219019Sgaborvoid vlan_deinit(struct hostapd_data *hapd)
848219019Sgabor{
849219019Sgabor	vlan_dynamic_remove(hapd, hapd->conf->vlan);
850219019Sgabor
851219019Sgabor#ifdef CONFIG_FULL_DYNAMIC_VLAN
852219019Sgabor	full_dynamic_vlan_deinit(hapd->full_dynamic_vlan);
853219019Sgabor#endif /* CONFIG_FULL_DYNAMIC_VLAN */
854219019Sgabor}
855219019Sgabor
856219019Sgabor
857219019Sgaborstruct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
858219019Sgabor				       struct hostapd_vlan *vlan,
859219019Sgabor				       int vlan_id)
860219019Sgabor{
861219019Sgabor	struct hostapd_vlan *n;
862219019Sgabor	char *ifname, *pos;
863219019Sgabor
864219019Sgabor	if (vlan == NULL || vlan_id <= 0 || vlan_id > MAX_VLAN_ID ||
865219019Sgabor	    vlan->vlan_id != VLAN_ID_WILDCARD)
866219019Sgabor		return NULL;
867219019Sgabor
868219019Sgabor	wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d ifname=%s)",
869219019Sgabor		   __func__, vlan_id, vlan->ifname);
870219019Sgabor	ifname = os_strdup(vlan->ifname);
871219019Sgabor	if (ifname == NULL)
872219019Sgabor		return NULL;
873219019Sgabor	pos = os_strchr(ifname, '#');
874219019Sgabor	if (pos == NULL) {
875219019Sgabor		os_free(ifname);
876219019Sgabor		return NULL;
877219019Sgabor	}
878219019Sgabor	*pos++ = '\0';
879219019Sgabor
880219019Sgabor	n = os_zalloc(sizeof(*n));
881219019Sgabor	if (n == NULL) {
882219019Sgabor		os_free(ifname);
883219019Sgabor		return NULL;
884219019Sgabor	}
885219019Sgabor
886219019Sgabor	n->vlan_id = vlan_id;
887219019Sgabor	n->dynamic_vlan = 1;
888219019Sgabor
889219019Sgabor	os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id,
890219019Sgabor		    pos);
891219019Sgabor	os_free(ifname);
892219019Sgabor
893219019Sgabor	if (hostapd_vlan_if_add(hapd, n->ifname)) {
894219019Sgabor		os_free(n);
895219019Sgabor		return NULL;
896219019Sgabor	}
897219019Sgabor
898219019Sgabor	n->next = hapd->conf->vlan;
899219019Sgabor	hapd->conf->vlan = n;
900219019Sgabor
901219019Sgabor#ifdef CONFIG_FULL_DYNAMIC_VLAN
902219019Sgabor	ifconfig_up(n->ifname);
903219019Sgabor#endif /* CONFIG_FULL_DYNAMIC_VLAN */
904219019Sgabor
905219019Sgabor	return n;
906219019Sgabor}
907219019Sgabor
908219019Sgabor
909219019Sgaborint vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id)
910219019Sgabor{
911219019Sgabor	struct hostapd_vlan *vlan;
912219019Sgabor
913219019Sgabor	if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID)
914219019Sgabor		return 1;
915219019Sgabor
916219019Sgabor	wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d)", __func__, vlan_id);
917219019Sgabor
918219019Sgabor	vlan = hapd->conf->vlan;
919219019Sgabor	while (vlan) {
920219019Sgabor		if (vlan->vlan_id == vlan_id && vlan->dynamic_vlan > 0) {
921219019Sgabor			vlan->dynamic_vlan--;
922219019Sgabor			break;
923219019Sgabor		}
924219019Sgabor		vlan = vlan->next;
925219019Sgabor	}
926219019Sgabor
927219019Sgabor	if (vlan == NULL)
928219019Sgabor		return 1;
929219019Sgabor
930219019Sgabor	if (vlan->dynamic_vlan == 0)
931219019Sgabor		hostapd_vlan_if_remove(hapd, vlan->ifname);
932219019Sgabor
933219019Sgabor	return 0;
934219019Sgabor}
935219019Sgabor