1e5b75505Sopenharmony_ci/*
2e5b75505Sopenharmony_ci * hostapd / VLAN initialization - full dynamic VLAN
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#include <net/if.h>
13e5b75505Sopenharmony_ci/* Avoid conflicts due to NetBSD net/if.h if_type define with driver.h */
14e5b75505Sopenharmony_ci#undef if_type
15e5b75505Sopenharmony_ci#include <sys/ioctl.h>
16e5b75505Sopenharmony_ci
17e5b75505Sopenharmony_ci#include "utils/common.h"
18e5b75505Sopenharmony_ci#include "drivers/priv_netlink.h"
19e5b75505Sopenharmony_ci#include "drivers/linux_ioctl.h"
20e5b75505Sopenharmony_ci#include "common/linux_bridge.h"
21e5b75505Sopenharmony_ci#include "common/linux_vlan.h"
22e5b75505Sopenharmony_ci#include "utils/eloop.h"
23e5b75505Sopenharmony_ci#include "hostapd.h"
24e5b75505Sopenharmony_ci#include "ap_config.h"
25e5b75505Sopenharmony_ci#include "ap_drv_ops.h"
26e5b75505Sopenharmony_ci#include "wpa_auth.h"
27e5b75505Sopenharmony_ci#include "vlan_init.h"
28e5b75505Sopenharmony_ci#include "vlan_util.h"
29e5b75505Sopenharmony_ci
30e5b75505Sopenharmony_ci
31e5b75505Sopenharmony_cistruct full_dynamic_vlan {
32e5b75505Sopenharmony_ci	int s; /* socket on which to listen for new/removed interfaces. */
33e5b75505Sopenharmony_ci};
34e5b75505Sopenharmony_ci
35e5b75505Sopenharmony_ci#define DVLAN_CLEAN_BR         0x1
36e5b75505Sopenharmony_ci#define DVLAN_CLEAN_VLAN       0x2
37e5b75505Sopenharmony_ci#define DVLAN_CLEAN_VLAN_PORT  0x4
38e5b75505Sopenharmony_ci
39e5b75505Sopenharmony_cistruct dynamic_iface {
40e5b75505Sopenharmony_ci	char ifname[IFNAMSIZ + 1];
41e5b75505Sopenharmony_ci	int usage;
42e5b75505Sopenharmony_ci	int clean;
43e5b75505Sopenharmony_ci	struct dynamic_iface *next;
44e5b75505Sopenharmony_ci};
45e5b75505Sopenharmony_ci
46e5b75505Sopenharmony_ci
47e5b75505Sopenharmony_ci/* Increment ref counter for ifname and add clean flag.
48e5b75505Sopenharmony_ci * If not in list, add it only if some flags are given.
49e5b75505Sopenharmony_ci */
50e5b75505Sopenharmony_cistatic void dyn_iface_get(struct hostapd_data *hapd, const char *ifname,
51e5b75505Sopenharmony_ci			  int clean)
52e5b75505Sopenharmony_ci{
53e5b75505Sopenharmony_ci	struct dynamic_iface *next, **dynamic_ifaces;
54e5b75505Sopenharmony_ci	struct hapd_interfaces *interfaces;
55e5b75505Sopenharmony_ci
56e5b75505Sopenharmony_ci	interfaces = hapd->iface->interfaces;
57e5b75505Sopenharmony_ci	dynamic_ifaces = &interfaces->vlan_priv;
58e5b75505Sopenharmony_ci
59e5b75505Sopenharmony_ci	for (next = *dynamic_ifaces; next; next = next->next) {
60e5b75505Sopenharmony_ci		if (os_strcmp(ifname, next->ifname) == 0)
61e5b75505Sopenharmony_ci			break;
62e5b75505Sopenharmony_ci	}
63e5b75505Sopenharmony_ci
64e5b75505Sopenharmony_ci	if (next) {
65e5b75505Sopenharmony_ci		next->usage++;
66e5b75505Sopenharmony_ci		next->clean |= clean;
67e5b75505Sopenharmony_ci		return;
68e5b75505Sopenharmony_ci	}
69e5b75505Sopenharmony_ci
70e5b75505Sopenharmony_ci	if (!clean)
71e5b75505Sopenharmony_ci		return;
72e5b75505Sopenharmony_ci
73e5b75505Sopenharmony_ci	next = os_zalloc(sizeof(*next));
74e5b75505Sopenharmony_ci	if (!next)
75e5b75505Sopenharmony_ci		return;
76e5b75505Sopenharmony_ci	os_strlcpy(next->ifname, ifname, sizeof(next->ifname));
77e5b75505Sopenharmony_ci	next->usage = 1;
78e5b75505Sopenharmony_ci	next->clean = clean;
79e5b75505Sopenharmony_ci	next->next = *dynamic_ifaces;
80e5b75505Sopenharmony_ci	*dynamic_ifaces = next;
81e5b75505Sopenharmony_ci}
82e5b75505Sopenharmony_ci
83e5b75505Sopenharmony_ci
84e5b75505Sopenharmony_ci/* Decrement reference counter for given ifname.
85e5b75505Sopenharmony_ci * Return clean flag iff reference counter was decreased to zero, else zero
86e5b75505Sopenharmony_ci */
87e5b75505Sopenharmony_cistatic int dyn_iface_put(struct hostapd_data *hapd, const char *ifname)
88e5b75505Sopenharmony_ci{
89e5b75505Sopenharmony_ci	struct dynamic_iface *next, *prev = NULL, **dynamic_ifaces;
90e5b75505Sopenharmony_ci	struct hapd_interfaces *interfaces;
91e5b75505Sopenharmony_ci	int clean;
92e5b75505Sopenharmony_ci
93e5b75505Sopenharmony_ci	interfaces = hapd->iface->interfaces;
94e5b75505Sopenharmony_ci	dynamic_ifaces = &interfaces->vlan_priv;
95e5b75505Sopenharmony_ci
96e5b75505Sopenharmony_ci	for (next = *dynamic_ifaces; next; next = next->next) {
97e5b75505Sopenharmony_ci		if (os_strcmp(ifname, next->ifname) == 0)
98e5b75505Sopenharmony_ci			break;
99e5b75505Sopenharmony_ci		prev = next;
100e5b75505Sopenharmony_ci	}
101e5b75505Sopenharmony_ci
102e5b75505Sopenharmony_ci	if (!next)
103e5b75505Sopenharmony_ci		return 0;
104e5b75505Sopenharmony_ci
105e5b75505Sopenharmony_ci	next->usage--;
106e5b75505Sopenharmony_ci	if (next->usage)
107e5b75505Sopenharmony_ci		return 0;
108e5b75505Sopenharmony_ci
109e5b75505Sopenharmony_ci	if (prev)
110e5b75505Sopenharmony_ci		prev->next = next->next;
111e5b75505Sopenharmony_ci	else
112e5b75505Sopenharmony_ci		*dynamic_ifaces = next->next;
113e5b75505Sopenharmony_ci	clean = next->clean;
114e5b75505Sopenharmony_ci	os_free(next);
115e5b75505Sopenharmony_ci
116e5b75505Sopenharmony_ci	return clean;
117e5b75505Sopenharmony_ci}
118e5b75505Sopenharmony_ci
119e5b75505Sopenharmony_ci
120e5b75505Sopenharmony_cistatic int ifconfig_down(const char *if_name)
121e5b75505Sopenharmony_ci{
122e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", if_name);
123e5b75505Sopenharmony_ci	return ifconfig_helper(if_name, 0);
124e5b75505Sopenharmony_ci}
125e5b75505Sopenharmony_ci
126e5b75505Sopenharmony_ci
127e5b75505Sopenharmony_ci/* This value should be 256 ONLY. If it is something else, then hostapd
128e5b75505Sopenharmony_ci * might crash!, as this value has been hard-coded in 2.4.x kernel
129e5b75505Sopenharmony_ci * bridging code.
130e5b75505Sopenharmony_ci */
131e5b75505Sopenharmony_ci#define MAX_BR_PORTS      		256
132e5b75505Sopenharmony_ci
133e5b75505Sopenharmony_cistatic int br_delif(const char *br_name, const char *if_name)
134e5b75505Sopenharmony_ci{
135e5b75505Sopenharmony_ci	int fd;
136e5b75505Sopenharmony_ci	struct ifreq ifr;
137e5b75505Sopenharmony_ci	unsigned long args[2];
138e5b75505Sopenharmony_ci	int if_index;
139e5b75505Sopenharmony_ci
140e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "VLAN: br_delif(%s, %s)", br_name, if_name);
141e5b75505Sopenharmony_ci	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
142e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
143e5b75505Sopenharmony_ci			   "failed: %s", __func__, strerror(errno));
144e5b75505Sopenharmony_ci		return -1;
145e5b75505Sopenharmony_ci	}
146e5b75505Sopenharmony_ci
147e5b75505Sopenharmony_ci	if (linux_br_del_if(fd, br_name, if_name) == 0)
148e5b75505Sopenharmony_ci		goto done;
149e5b75505Sopenharmony_ci
150e5b75505Sopenharmony_ci	if_index = if_nametoindex(if_name);
151e5b75505Sopenharmony_ci
152e5b75505Sopenharmony_ci	if (if_index == 0) {
153e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
154e5b75505Sopenharmony_ci			   "interface index for '%s'",
155e5b75505Sopenharmony_ci			   __func__, if_name);
156e5b75505Sopenharmony_ci		close(fd);
157e5b75505Sopenharmony_ci		return -1;
158e5b75505Sopenharmony_ci	}
159e5b75505Sopenharmony_ci
160e5b75505Sopenharmony_ci	args[0] = BRCTL_DEL_IF;
161e5b75505Sopenharmony_ci	args[1] = if_index;
162e5b75505Sopenharmony_ci
163e5b75505Sopenharmony_ci	os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
164e5b75505Sopenharmony_ci	ifr.ifr_data = (void *) args;
165e5b75505Sopenharmony_ci
166e5b75505Sopenharmony_ci	if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) {
167e5b75505Sopenharmony_ci		/* No error if interface already removed. */
168e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
169e5b75505Sopenharmony_ci			   "BRCTL_DEL_IF] failed for br_name=%s if_name=%s: "
170e5b75505Sopenharmony_ci			   "%s", __func__, br_name, if_name, strerror(errno));
171e5b75505Sopenharmony_ci		close(fd);
172e5b75505Sopenharmony_ci		return -1;
173e5b75505Sopenharmony_ci	}
174e5b75505Sopenharmony_ci
175e5b75505Sopenharmony_cidone:
176e5b75505Sopenharmony_ci	close(fd);
177e5b75505Sopenharmony_ci	return 0;
178e5b75505Sopenharmony_ci}
179e5b75505Sopenharmony_ci
180e5b75505Sopenharmony_ci
181e5b75505Sopenharmony_ci/*
182e5b75505Sopenharmony_ci	Add interface 'if_name' to the bridge 'br_name'
183e5b75505Sopenharmony_ci
184e5b75505Sopenharmony_ci	returns -1 on error
185e5b75505Sopenharmony_ci	returns 1 if the interface is already part of the bridge
186e5b75505Sopenharmony_ci	returns 0 otherwise
187e5b75505Sopenharmony_ci*/
188e5b75505Sopenharmony_cistatic int br_addif(const char *br_name, const char *if_name)
189e5b75505Sopenharmony_ci{
190e5b75505Sopenharmony_ci	int fd;
191e5b75505Sopenharmony_ci	struct ifreq ifr;
192e5b75505Sopenharmony_ci	unsigned long args[2];
193e5b75505Sopenharmony_ci	int if_index;
194e5b75505Sopenharmony_ci
195e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "VLAN: br_addif(%s, %s)", br_name, if_name);
196e5b75505Sopenharmony_ci	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
197e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
198e5b75505Sopenharmony_ci			   "failed: %s", __func__, strerror(errno));
199e5b75505Sopenharmony_ci		return -1;
200e5b75505Sopenharmony_ci	}
201e5b75505Sopenharmony_ci
202e5b75505Sopenharmony_ci	if (linux_br_add_if(fd, br_name, if_name) == 0)
203e5b75505Sopenharmony_ci		goto done;
204e5b75505Sopenharmony_ci	if (errno == EBUSY) {
205e5b75505Sopenharmony_ci		/* The interface is already added. */
206e5b75505Sopenharmony_ci		close(fd);
207e5b75505Sopenharmony_ci		return 1;
208e5b75505Sopenharmony_ci	}
209e5b75505Sopenharmony_ci
210e5b75505Sopenharmony_ci	if_index = if_nametoindex(if_name);
211e5b75505Sopenharmony_ci
212e5b75505Sopenharmony_ci	if (if_index == 0) {
213e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
214e5b75505Sopenharmony_ci			   "interface index for '%s'",
215e5b75505Sopenharmony_ci			   __func__, if_name);
216e5b75505Sopenharmony_ci		close(fd);
217e5b75505Sopenharmony_ci		return -1;
218e5b75505Sopenharmony_ci	}
219e5b75505Sopenharmony_ci
220e5b75505Sopenharmony_ci	args[0] = BRCTL_ADD_IF;
221e5b75505Sopenharmony_ci	args[1] = if_index;
222e5b75505Sopenharmony_ci
223e5b75505Sopenharmony_ci	os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
224e5b75505Sopenharmony_ci	ifr.ifr_data = (void *) args;
225e5b75505Sopenharmony_ci
226e5b75505Sopenharmony_ci	if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
227e5b75505Sopenharmony_ci		if (errno == EBUSY) {
228e5b75505Sopenharmony_ci			/* The interface is already added. */
229e5b75505Sopenharmony_ci			close(fd);
230e5b75505Sopenharmony_ci			return 1;
231e5b75505Sopenharmony_ci		}
232e5b75505Sopenharmony_ci
233e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
234e5b75505Sopenharmony_ci			   "BRCTL_ADD_IF] failed for br_name=%s if_name=%s: "
235e5b75505Sopenharmony_ci			   "%s", __func__, br_name, if_name, strerror(errno));
236e5b75505Sopenharmony_ci		close(fd);
237e5b75505Sopenharmony_ci		return -1;
238e5b75505Sopenharmony_ci	}
239e5b75505Sopenharmony_ci
240e5b75505Sopenharmony_cidone:
241e5b75505Sopenharmony_ci	close(fd);
242e5b75505Sopenharmony_ci	return 0;
243e5b75505Sopenharmony_ci}
244e5b75505Sopenharmony_ci
245e5b75505Sopenharmony_ci
246e5b75505Sopenharmony_cistatic int br_delbr(const char *br_name)
247e5b75505Sopenharmony_ci{
248e5b75505Sopenharmony_ci	int fd;
249e5b75505Sopenharmony_ci	unsigned long arg[2];
250e5b75505Sopenharmony_ci
251e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "VLAN: br_delbr(%s)", br_name);
252e5b75505Sopenharmony_ci	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
253e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
254e5b75505Sopenharmony_ci			   "failed: %s", __func__, strerror(errno));
255e5b75505Sopenharmony_ci		return -1;
256e5b75505Sopenharmony_ci	}
257e5b75505Sopenharmony_ci
258e5b75505Sopenharmony_ci	if (linux_br_del(fd, br_name) == 0)
259e5b75505Sopenharmony_ci		goto done;
260e5b75505Sopenharmony_ci
261e5b75505Sopenharmony_ci	arg[0] = BRCTL_DEL_BRIDGE;
262e5b75505Sopenharmony_ci	arg[1] = (unsigned long) br_name;
263e5b75505Sopenharmony_ci
264e5b75505Sopenharmony_ci	if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) {
265e5b75505Sopenharmony_ci		/* No error if bridge already removed. */
266e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_DEL_BRIDGE failed for "
267e5b75505Sopenharmony_ci			   "%s: %s", __func__, br_name, strerror(errno));
268e5b75505Sopenharmony_ci		close(fd);
269e5b75505Sopenharmony_ci		return -1;
270e5b75505Sopenharmony_ci	}
271e5b75505Sopenharmony_ci
272e5b75505Sopenharmony_cidone:
273e5b75505Sopenharmony_ci	close(fd);
274e5b75505Sopenharmony_ci	return 0;
275e5b75505Sopenharmony_ci}
276e5b75505Sopenharmony_ci
277e5b75505Sopenharmony_ci
278e5b75505Sopenharmony_ci/*
279e5b75505Sopenharmony_ci	Add a bridge with the name 'br_name'.
280e5b75505Sopenharmony_ci
281e5b75505Sopenharmony_ci	returns -1 on error
282e5b75505Sopenharmony_ci	returns 1 if the bridge already exists
283e5b75505Sopenharmony_ci	returns 0 otherwise
284e5b75505Sopenharmony_ci*/
285e5b75505Sopenharmony_cistatic int br_addbr(const char *br_name)
286e5b75505Sopenharmony_ci{
287e5b75505Sopenharmony_ci	int fd;
288e5b75505Sopenharmony_ci	unsigned long arg[4];
289e5b75505Sopenharmony_ci	struct ifreq ifr;
290e5b75505Sopenharmony_ci
291e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "VLAN: br_addbr(%s)", br_name);
292e5b75505Sopenharmony_ci	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
293e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
294e5b75505Sopenharmony_ci			   "failed: %s", __func__, strerror(errno));
295e5b75505Sopenharmony_ci		return -1;
296e5b75505Sopenharmony_ci	}
297e5b75505Sopenharmony_ci
298e5b75505Sopenharmony_ci	if (linux_br_add(fd, br_name) == 0)
299e5b75505Sopenharmony_ci		goto done;
300e5b75505Sopenharmony_ci	if (errno == EEXIST) {
301e5b75505Sopenharmony_ci		/* The bridge is already added. */
302e5b75505Sopenharmony_ci		close(fd);
303e5b75505Sopenharmony_ci		return 1;
304e5b75505Sopenharmony_ci	}
305e5b75505Sopenharmony_ci
306e5b75505Sopenharmony_ci	arg[0] = BRCTL_ADD_BRIDGE;
307e5b75505Sopenharmony_ci	arg[1] = (unsigned long) br_name;
308e5b75505Sopenharmony_ci
309e5b75505Sopenharmony_ci	if (ioctl(fd, SIOCGIFBR, arg) < 0) {
310e5b75505Sopenharmony_ci		if (errno == EEXIST) {
311e5b75505Sopenharmony_ci			/* The bridge is already added. */
312e5b75505Sopenharmony_ci			close(fd);
313e5b75505Sopenharmony_ci			return 1;
314e5b75505Sopenharmony_ci		} else {
315e5b75505Sopenharmony_ci			wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_ADD_BRIDGE "
316e5b75505Sopenharmony_ci				   "failed for %s: %s",
317e5b75505Sopenharmony_ci				   __func__, br_name, strerror(errno));
318e5b75505Sopenharmony_ci			close(fd);
319e5b75505Sopenharmony_ci			return -1;
320e5b75505Sopenharmony_ci		}
321e5b75505Sopenharmony_ci	}
322e5b75505Sopenharmony_ci
323e5b75505Sopenharmony_cidone:
324e5b75505Sopenharmony_ci	/* Decrease forwarding delay to avoid EAPOL timeouts. */
325e5b75505Sopenharmony_ci	os_memset(&ifr, 0, sizeof(ifr));
326e5b75505Sopenharmony_ci	os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ);
327e5b75505Sopenharmony_ci	arg[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY;
328e5b75505Sopenharmony_ci	arg[1] = 1;
329e5b75505Sopenharmony_ci	arg[2] = 0;
330e5b75505Sopenharmony_ci	arg[3] = 0;
331e5b75505Sopenharmony_ci	ifr.ifr_data = (char *) &arg;
332e5b75505Sopenharmony_ci	if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
333e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "VLAN: %s: "
334e5b75505Sopenharmony_ci			   "BRCTL_SET_BRIDGE_FORWARD_DELAY (1 sec) failed for "
335e5b75505Sopenharmony_ci			   "%s: %s", __func__, br_name, strerror(errno));
336e5b75505Sopenharmony_ci		/* Continue anyway */
337e5b75505Sopenharmony_ci	}
338e5b75505Sopenharmony_ci
339e5b75505Sopenharmony_ci	close(fd);
340e5b75505Sopenharmony_ci	return 0;
341e5b75505Sopenharmony_ci}
342e5b75505Sopenharmony_ci
343e5b75505Sopenharmony_ci
344e5b75505Sopenharmony_cistatic int br_getnumports(const char *br_name)
345e5b75505Sopenharmony_ci{
346e5b75505Sopenharmony_ci	int fd;
347e5b75505Sopenharmony_ci	int i;
348e5b75505Sopenharmony_ci	int port_cnt = 0;
349e5b75505Sopenharmony_ci	unsigned long arg[4];
350e5b75505Sopenharmony_ci	int ifindices[MAX_BR_PORTS];
351e5b75505Sopenharmony_ci	struct ifreq ifr;
352e5b75505Sopenharmony_ci
353e5b75505Sopenharmony_ci	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
354e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
355e5b75505Sopenharmony_ci			   "failed: %s", __func__, strerror(errno));
356e5b75505Sopenharmony_ci		return -1;
357e5b75505Sopenharmony_ci	}
358e5b75505Sopenharmony_ci
359e5b75505Sopenharmony_ci	arg[0] = BRCTL_GET_PORT_LIST;
360e5b75505Sopenharmony_ci	arg[1] = (unsigned long) ifindices;
361e5b75505Sopenharmony_ci	arg[2] = MAX_BR_PORTS;
362e5b75505Sopenharmony_ci	arg[3] = 0;
363e5b75505Sopenharmony_ci
364e5b75505Sopenharmony_ci	os_memset(ifindices, 0, sizeof(ifindices));
365e5b75505Sopenharmony_ci	os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
366e5b75505Sopenharmony_ci	ifr.ifr_data = (void *) arg;
367e5b75505Sopenharmony_ci
368e5b75505Sopenharmony_ci	if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
369e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_GET_PORT_LIST "
370e5b75505Sopenharmony_ci			   "failed for %s: %s",
371e5b75505Sopenharmony_ci			   __func__, br_name, strerror(errno));
372e5b75505Sopenharmony_ci		close(fd);
373e5b75505Sopenharmony_ci		return -1;
374e5b75505Sopenharmony_ci	}
375e5b75505Sopenharmony_ci
376e5b75505Sopenharmony_ci	for (i = 1; i < MAX_BR_PORTS; i++) {
377e5b75505Sopenharmony_ci		if (ifindices[i] > 0) {
378e5b75505Sopenharmony_ci			port_cnt++;
379e5b75505Sopenharmony_ci		}
380e5b75505Sopenharmony_ci	}
381e5b75505Sopenharmony_ci
382e5b75505Sopenharmony_ci	close(fd);
383e5b75505Sopenharmony_ci	return port_cnt;
384e5b75505Sopenharmony_ci}
385e5b75505Sopenharmony_ci
386e5b75505Sopenharmony_ci
387e5b75505Sopenharmony_cistatic void vlan_newlink_tagged(int vlan_naming, const char *tagged_interface,
388e5b75505Sopenharmony_ci				const char *br_name, int vid,
389e5b75505Sopenharmony_ci				struct hostapd_data *hapd)
390e5b75505Sopenharmony_ci{
391e5b75505Sopenharmony_ci	char vlan_ifname[IFNAMSIZ];
392e5b75505Sopenharmony_ci	int clean;
393e5b75505Sopenharmony_ci	int ret;
394e5b75505Sopenharmony_ci
395e5b75505Sopenharmony_ci	if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE)
396e5b75505Sopenharmony_ci		ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d",
397e5b75505Sopenharmony_ci				  tagged_interface, vid);
398e5b75505Sopenharmony_ci	else
399e5b75505Sopenharmony_ci		ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d",
400e5b75505Sopenharmony_ci				  vid);
401e5b75505Sopenharmony_ci	if (ret >= (int) sizeof(vlan_ifname))
402e5b75505Sopenharmony_ci		wpa_printf(MSG_WARNING,
403e5b75505Sopenharmony_ci			   "VLAN: Interface name was truncated to %s",
404e5b75505Sopenharmony_ci			   vlan_ifname);
405e5b75505Sopenharmony_ci
406e5b75505Sopenharmony_ci	clean = 0;
407e5b75505Sopenharmony_ci	ifconfig_up(tagged_interface);
408e5b75505Sopenharmony_ci	if (!vlan_add(tagged_interface, vid, vlan_ifname))
409e5b75505Sopenharmony_ci		clean |= DVLAN_CLEAN_VLAN;
410e5b75505Sopenharmony_ci
411e5b75505Sopenharmony_ci	if (!br_addif(br_name, vlan_ifname))
412e5b75505Sopenharmony_ci		clean |= DVLAN_CLEAN_VLAN_PORT;
413e5b75505Sopenharmony_ci
414e5b75505Sopenharmony_ci	dyn_iface_get(hapd, vlan_ifname, clean);
415e5b75505Sopenharmony_ci
416e5b75505Sopenharmony_ci	ifconfig_up(vlan_ifname);
417e5b75505Sopenharmony_ci}
418e5b75505Sopenharmony_ci
419e5b75505Sopenharmony_ci
420e5b75505Sopenharmony_cistatic void vlan_bridge_name(char *br_name, struct hostapd_data *hapd,
421e5b75505Sopenharmony_ci			     struct hostapd_vlan *vlan, int vid)
422e5b75505Sopenharmony_ci{
423e5b75505Sopenharmony_ci	char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
424e5b75505Sopenharmony_ci	int ret;
425e5b75505Sopenharmony_ci
426e5b75505Sopenharmony_ci	if (vlan->bridge[0]) {
427e5b75505Sopenharmony_ci		os_strlcpy(br_name, vlan->bridge, IFNAMSIZ);
428e5b75505Sopenharmony_ci		ret = 0;
429e5b75505Sopenharmony_ci	} else if (hapd->conf->vlan_bridge[0]) {
430e5b75505Sopenharmony_ci		ret = os_snprintf(br_name, IFNAMSIZ, "%s%d",
431e5b75505Sopenharmony_ci				  hapd->conf->vlan_bridge, vid);
432e5b75505Sopenharmony_ci	} else if (tagged_interface) {
433e5b75505Sopenharmony_ci		ret = os_snprintf(br_name, IFNAMSIZ, "br%s.%d",
434e5b75505Sopenharmony_ci				  tagged_interface, vid);
435e5b75505Sopenharmony_ci	} else {
436e5b75505Sopenharmony_ci		ret = os_snprintf(br_name, IFNAMSIZ, "brvlan%d", vid);
437e5b75505Sopenharmony_ci	}
438e5b75505Sopenharmony_ci	if (ret >= IFNAMSIZ)
439e5b75505Sopenharmony_ci		wpa_printf(MSG_WARNING,
440e5b75505Sopenharmony_ci			   "VLAN: Interface name was truncated to %s",
441e5b75505Sopenharmony_ci			   br_name);
442e5b75505Sopenharmony_ci}
443e5b75505Sopenharmony_ci
444e5b75505Sopenharmony_ci
445e5b75505Sopenharmony_cistatic void vlan_get_bridge(const char *br_name, struct hostapd_data *hapd,
446e5b75505Sopenharmony_ci			    int vid)
447e5b75505Sopenharmony_ci{
448e5b75505Sopenharmony_ci	char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
449e5b75505Sopenharmony_ci	int vlan_naming = hapd->conf->ssid.vlan_naming;
450e5b75505Sopenharmony_ci
451e5b75505Sopenharmony_ci	dyn_iface_get(hapd, br_name, br_addbr(br_name) ? 0 : DVLAN_CLEAN_BR);
452e5b75505Sopenharmony_ci
453e5b75505Sopenharmony_ci	ifconfig_up(br_name);
454e5b75505Sopenharmony_ci
455e5b75505Sopenharmony_ci	if (tagged_interface)
456e5b75505Sopenharmony_ci		vlan_newlink_tagged(vlan_naming, tagged_interface, br_name,
457e5b75505Sopenharmony_ci				    vid, hapd);
458e5b75505Sopenharmony_ci}
459e5b75505Sopenharmony_ci
460e5b75505Sopenharmony_ci
461e5b75505Sopenharmony_civoid vlan_newlink(const char *ifname, struct hostapd_data *hapd)
462e5b75505Sopenharmony_ci{
463e5b75505Sopenharmony_ci	char br_name[IFNAMSIZ];
464e5b75505Sopenharmony_ci	struct hostapd_vlan *vlan;
465e5b75505Sopenharmony_ci	int untagged, *tagged, i, notempty;
466e5b75505Sopenharmony_ci
467e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname);
468e5b75505Sopenharmony_ci
469e5b75505Sopenharmony_ci	for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
470e5b75505Sopenharmony_ci		if (vlan->configured ||
471e5b75505Sopenharmony_ci		    os_strcmp(ifname, vlan->ifname) != 0)
472e5b75505Sopenharmony_ci			continue;
473e5b75505Sopenharmony_ci		break;
474e5b75505Sopenharmony_ci	}
475e5b75505Sopenharmony_ci	if (!vlan)
476e5b75505Sopenharmony_ci		return;
477e5b75505Sopenharmony_ci
478e5b75505Sopenharmony_ci	vlan->configured = 1;
479e5b75505Sopenharmony_ci
480e5b75505Sopenharmony_ci	notempty = vlan->vlan_desc.notempty;
481e5b75505Sopenharmony_ci	untagged = vlan->vlan_desc.untagged;
482e5b75505Sopenharmony_ci	tagged = vlan->vlan_desc.tagged;
483e5b75505Sopenharmony_ci
484e5b75505Sopenharmony_ci	if (!notempty) {
485e5b75505Sopenharmony_ci		/* Non-VLAN STA */
486e5b75505Sopenharmony_ci		if (hapd->conf->bridge[0] &&
487e5b75505Sopenharmony_ci		    !br_addif(hapd->conf->bridge, ifname))
488e5b75505Sopenharmony_ci			vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
489e5b75505Sopenharmony_ci	} else if (untagged > 0 && untagged <= MAX_VLAN_ID) {
490e5b75505Sopenharmony_ci		vlan_bridge_name(br_name, hapd, vlan, untagged);
491e5b75505Sopenharmony_ci
492e5b75505Sopenharmony_ci		vlan_get_bridge(br_name, hapd, untagged);
493e5b75505Sopenharmony_ci
494e5b75505Sopenharmony_ci		if (!br_addif(br_name, ifname))
495e5b75505Sopenharmony_ci			vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
496e5b75505Sopenharmony_ci	}
497e5b75505Sopenharmony_ci
498e5b75505Sopenharmony_ci	for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) {
499e5b75505Sopenharmony_ci		if (tagged[i] == untagged ||
500e5b75505Sopenharmony_ci		    tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID ||
501e5b75505Sopenharmony_ci		    (i > 0 && tagged[i] == tagged[i - 1]))
502e5b75505Sopenharmony_ci			continue;
503e5b75505Sopenharmony_ci		vlan_bridge_name(br_name, hapd, vlan, tagged[i]);
504e5b75505Sopenharmony_ci		vlan_get_bridge(br_name, hapd, tagged[i]);
505e5b75505Sopenharmony_ci		vlan_newlink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE,
506e5b75505Sopenharmony_ci				    ifname, br_name, tagged[i], hapd);
507e5b75505Sopenharmony_ci	}
508e5b75505Sopenharmony_ci
509e5b75505Sopenharmony_ci	ifconfig_up(ifname);
510e5b75505Sopenharmony_ci}
511e5b75505Sopenharmony_ci
512e5b75505Sopenharmony_ci
513e5b75505Sopenharmony_cistatic void vlan_dellink_tagged(int vlan_naming, const char *tagged_interface,
514e5b75505Sopenharmony_ci				const char *br_name, int vid,
515e5b75505Sopenharmony_ci				struct hostapd_data *hapd)
516e5b75505Sopenharmony_ci{
517e5b75505Sopenharmony_ci	char vlan_ifname[IFNAMSIZ];
518e5b75505Sopenharmony_ci	int clean;
519e5b75505Sopenharmony_ci	int ret;
520e5b75505Sopenharmony_ci
521e5b75505Sopenharmony_ci	if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE)
522e5b75505Sopenharmony_ci		ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d",
523e5b75505Sopenharmony_ci				  tagged_interface, vid);
524e5b75505Sopenharmony_ci	else
525e5b75505Sopenharmony_ci		ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d",
526e5b75505Sopenharmony_ci				  vid);
527e5b75505Sopenharmony_ci	if (ret >= (int) sizeof(vlan_ifname))
528e5b75505Sopenharmony_ci		wpa_printf(MSG_WARNING,
529e5b75505Sopenharmony_ci			   "VLAN: Interface name was truncated to %s",
530e5b75505Sopenharmony_ci			   vlan_ifname);
531e5b75505Sopenharmony_ci
532e5b75505Sopenharmony_ci
533e5b75505Sopenharmony_ci	clean = dyn_iface_put(hapd, vlan_ifname);
534e5b75505Sopenharmony_ci
535e5b75505Sopenharmony_ci	if (clean & DVLAN_CLEAN_VLAN_PORT)
536e5b75505Sopenharmony_ci		br_delif(br_name, vlan_ifname);
537e5b75505Sopenharmony_ci
538e5b75505Sopenharmony_ci	if (clean & DVLAN_CLEAN_VLAN) {
539e5b75505Sopenharmony_ci		ifconfig_down(vlan_ifname);
540e5b75505Sopenharmony_ci		vlan_rem(vlan_ifname);
541e5b75505Sopenharmony_ci	}
542e5b75505Sopenharmony_ci}
543e5b75505Sopenharmony_ci
544e5b75505Sopenharmony_ci
545e5b75505Sopenharmony_cistatic void vlan_put_bridge(const char *br_name, struct hostapd_data *hapd,
546e5b75505Sopenharmony_ci			    int vid)
547e5b75505Sopenharmony_ci{
548e5b75505Sopenharmony_ci	int clean;
549e5b75505Sopenharmony_ci	char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
550e5b75505Sopenharmony_ci	int vlan_naming = hapd->conf->ssid.vlan_naming;
551e5b75505Sopenharmony_ci
552e5b75505Sopenharmony_ci	if (tagged_interface)
553e5b75505Sopenharmony_ci		vlan_dellink_tagged(vlan_naming, tagged_interface, br_name,
554e5b75505Sopenharmony_ci				    vid, hapd);
555e5b75505Sopenharmony_ci
556e5b75505Sopenharmony_ci	clean = dyn_iface_put(hapd, br_name);
557e5b75505Sopenharmony_ci	if ((clean & DVLAN_CLEAN_BR) && br_getnumports(br_name) == 0) {
558e5b75505Sopenharmony_ci		ifconfig_down(br_name);
559e5b75505Sopenharmony_ci		br_delbr(br_name);
560e5b75505Sopenharmony_ci	}
561e5b75505Sopenharmony_ci}
562e5b75505Sopenharmony_ci
563e5b75505Sopenharmony_ci
564e5b75505Sopenharmony_civoid vlan_dellink(const char *ifname, struct hostapd_data *hapd)
565e5b75505Sopenharmony_ci{
566e5b75505Sopenharmony_ci	struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan;
567e5b75505Sopenharmony_ci
568e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname);
569e5b75505Sopenharmony_ci
570e5b75505Sopenharmony_ci	first = prev = vlan;
571e5b75505Sopenharmony_ci
572e5b75505Sopenharmony_ci	while (vlan) {
573e5b75505Sopenharmony_ci		if (os_strcmp(ifname, vlan->ifname) != 0) {
574e5b75505Sopenharmony_ci			prev = vlan;
575e5b75505Sopenharmony_ci			vlan = vlan->next;
576e5b75505Sopenharmony_ci			continue;
577e5b75505Sopenharmony_ci		}
578e5b75505Sopenharmony_ci		break;
579e5b75505Sopenharmony_ci	}
580e5b75505Sopenharmony_ci	if (!vlan)
581e5b75505Sopenharmony_ci		return;
582e5b75505Sopenharmony_ci
583e5b75505Sopenharmony_ci	if (vlan->configured) {
584e5b75505Sopenharmony_ci		int notempty = vlan->vlan_desc.notempty;
585e5b75505Sopenharmony_ci		int untagged = vlan->vlan_desc.untagged;
586e5b75505Sopenharmony_ci		int *tagged = vlan->vlan_desc.tagged;
587e5b75505Sopenharmony_ci		char br_name[IFNAMSIZ];
588e5b75505Sopenharmony_ci		int i;
589e5b75505Sopenharmony_ci
590e5b75505Sopenharmony_ci		for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) {
591e5b75505Sopenharmony_ci			if (tagged[i] == untagged ||
592e5b75505Sopenharmony_ci			    tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID ||
593e5b75505Sopenharmony_ci			    (i > 0 && tagged[i] == tagged[i - 1]))
594e5b75505Sopenharmony_ci				continue;
595e5b75505Sopenharmony_ci			vlan_bridge_name(br_name, hapd, vlan, tagged[i]);
596e5b75505Sopenharmony_ci			vlan_dellink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE,
597e5b75505Sopenharmony_ci					    ifname, br_name, tagged[i], hapd);
598e5b75505Sopenharmony_ci			vlan_put_bridge(br_name, hapd, tagged[i]);
599e5b75505Sopenharmony_ci		}
600e5b75505Sopenharmony_ci
601e5b75505Sopenharmony_ci		if (!notempty) {
602e5b75505Sopenharmony_ci			/* Non-VLAN STA */
603e5b75505Sopenharmony_ci			if (hapd->conf->bridge[0] &&
604e5b75505Sopenharmony_ci			    (vlan->clean & DVLAN_CLEAN_WLAN_PORT))
605e5b75505Sopenharmony_ci				br_delif(hapd->conf->bridge, ifname);
606e5b75505Sopenharmony_ci		} else if (untagged > 0 && untagged <= MAX_VLAN_ID) {
607e5b75505Sopenharmony_ci			vlan_bridge_name(br_name, hapd, vlan, untagged);
608e5b75505Sopenharmony_ci
609e5b75505Sopenharmony_ci			if (vlan->clean & DVLAN_CLEAN_WLAN_PORT)
610e5b75505Sopenharmony_ci				br_delif(br_name, vlan->ifname);
611e5b75505Sopenharmony_ci
612e5b75505Sopenharmony_ci			vlan_put_bridge(br_name, hapd, untagged);
613e5b75505Sopenharmony_ci		}
614e5b75505Sopenharmony_ci	}
615e5b75505Sopenharmony_ci
616e5b75505Sopenharmony_ci	/*
617e5b75505Sopenharmony_ci	 * Ensure this VLAN interface is actually removed even if
618e5b75505Sopenharmony_ci	 * NEWLINK message is only received later.
619e5b75505Sopenharmony_ci	 */
620e5b75505Sopenharmony_ci	if (if_nametoindex(vlan->ifname) && vlan_if_remove(hapd, vlan))
621e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR,
622e5b75505Sopenharmony_ci			   "VLAN: Could not remove VLAN iface: %s: %s",
623e5b75505Sopenharmony_ci			   vlan->ifname, strerror(errno));
624e5b75505Sopenharmony_ci
625e5b75505Sopenharmony_ci	if (vlan == first)
626e5b75505Sopenharmony_ci		hapd->conf->vlan = vlan->next;
627e5b75505Sopenharmony_ci	else
628e5b75505Sopenharmony_ci		prev->next = vlan->next;
629e5b75505Sopenharmony_ci
630e5b75505Sopenharmony_ci	os_free(vlan);
631e5b75505Sopenharmony_ci}
632e5b75505Sopenharmony_ci
633e5b75505Sopenharmony_ci
634e5b75505Sopenharmony_cistatic void
635e5b75505Sopenharmony_civlan_read_ifnames(struct nlmsghdr *h, size_t len, int del,
636e5b75505Sopenharmony_ci		  struct hostapd_data *hapd)
637e5b75505Sopenharmony_ci{
638e5b75505Sopenharmony_ci	struct ifinfomsg *ifi;
639e5b75505Sopenharmony_ci	int attrlen, nlmsg_len, rta_len;
640e5b75505Sopenharmony_ci	struct rtattr *attr;
641e5b75505Sopenharmony_ci	char ifname[IFNAMSIZ + 1];
642e5b75505Sopenharmony_ci
643e5b75505Sopenharmony_ci	if (len < sizeof(*ifi))
644e5b75505Sopenharmony_ci		return;
645e5b75505Sopenharmony_ci
646e5b75505Sopenharmony_ci	ifi = NLMSG_DATA(h);
647e5b75505Sopenharmony_ci
648e5b75505Sopenharmony_ci	nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg));
649e5b75505Sopenharmony_ci
650e5b75505Sopenharmony_ci	attrlen = h->nlmsg_len - nlmsg_len;
651e5b75505Sopenharmony_ci	if (attrlen < 0)
652e5b75505Sopenharmony_ci		return;
653e5b75505Sopenharmony_ci
654e5b75505Sopenharmony_ci	attr = (struct rtattr *) (((char *) ifi) + nlmsg_len);
655e5b75505Sopenharmony_ci
656e5b75505Sopenharmony_ci	os_memset(ifname, 0, sizeof(ifname));
657e5b75505Sopenharmony_ci	rta_len = RTA_ALIGN(sizeof(struct rtattr));
658e5b75505Sopenharmony_ci	while (RTA_OK(attr, attrlen)) {
659e5b75505Sopenharmony_ci		if (attr->rta_type == IFLA_IFNAME) {
660e5b75505Sopenharmony_ci			int n = attr->rta_len - rta_len;
661e5b75505Sopenharmony_ci			if (n < 0)
662e5b75505Sopenharmony_ci				break;
663e5b75505Sopenharmony_ci
664e5b75505Sopenharmony_ci			if ((size_t) n >= sizeof(ifname))
665e5b75505Sopenharmony_ci				n = sizeof(ifname) - 1;
666e5b75505Sopenharmony_ci			os_memcpy(ifname, ((char *) attr) + rta_len, n);
667e5b75505Sopenharmony_ci
668e5b75505Sopenharmony_ci		}
669e5b75505Sopenharmony_ci
670e5b75505Sopenharmony_ci		attr = RTA_NEXT(attr, attrlen);
671e5b75505Sopenharmony_ci	}
672e5b75505Sopenharmony_ci
673e5b75505Sopenharmony_ci	if (!ifname[0])
674e5b75505Sopenharmony_ci		return;
675e5b75505Sopenharmony_ci	if (del && if_nametoindex(ifname)) {
676e5b75505Sopenharmony_ci		 /* interface still exists, race condition ->
677e5b75505Sopenharmony_ci		  * iface has just been recreated */
678e5b75505Sopenharmony_ci		return;
679e5b75505Sopenharmony_ci	}
680e5b75505Sopenharmony_ci
681e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG,
682e5b75505Sopenharmony_ci		   "VLAN: RTM_%sLINK: ifi_index=%d ifname=%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)",
683e5b75505Sopenharmony_ci		   del ? "DEL" : "NEW",
684e5b75505Sopenharmony_ci		   ifi->ifi_index, ifname, ifi->ifi_family, ifi->ifi_flags,
685e5b75505Sopenharmony_ci		   (ifi->ifi_flags & IFF_UP) ? "[UP]" : "",
686e5b75505Sopenharmony_ci		   (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
687e5b75505Sopenharmony_ci		   (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
688e5b75505Sopenharmony_ci		   (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
689e5b75505Sopenharmony_ci
690e5b75505Sopenharmony_ci	if (del)
691e5b75505Sopenharmony_ci		vlan_dellink(ifname, hapd);
692e5b75505Sopenharmony_ci	else
693e5b75505Sopenharmony_ci		vlan_newlink(ifname, hapd);
694e5b75505Sopenharmony_ci}
695e5b75505Sopenharmony_ci
696e5b75505Sopenharmony_ci
697e5b75505Sopenharmony_cistatic void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx)
698e5b75505Sopenharmony_ci{
699e5b75505Sopenharmony_ci	char buf[8192];
700e5b75505Sopenharmony_ci	int left;
701e5b75505Sopenharmony_ci	struct sockaddr_nl from;
702e5b75505Sopenharmony_ci	socklen_t fromlen;
703e5b75505Sopenharmony_ci	struct nlmsghdr *h;
704e5b75505Sopenharmony_ci	struct hostapd_data *hapd = eloop_ctx;
705e5b75505Sopenharmony_ci
706e5b75505Sopenharmony_ci	fromlen = sizeof(from);
707e5b75505Sopenharmony_ci	left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
708e5b75505Sopenharmony_ci			(struct sockaddr *) &from, &fromlen);
709e5b75505Sopenharmony_ci	if (left < 0) {
710e5b75505Sopenharmony_ci		if (errno != EINTR && errno != EAGAIN)
711e5b75505Sopenharmony_ci			wpa_printf(MSG_ERROR, "VLAN: %s: recvfrom failed: %s",
712e5b75505Sopenharmony_ci				   __func__, strerror(errno));
713e5b75505Sopenharmony_ci		return;
714e5b75505Sopenharmony_ci	}
715e5b75505Sopenharmony_ci
716e5b75505Sopenharmony_ci	h = (struct nlmsghdr *) buf;
717e5b75505Sopenharmony_ci	while (NLMSG_OK(h, left)) {
718e5b75505Sopenharmony_ci		int len, plen;
719e5b75505Sopenharmony_ci
720e5b75505Sopenharmony_ci		len = h->nlmsg_len;
721e5b75505Sopenharmony_ci		plen = len - sizeof(*h);
722e5b75505Sopenharmony_ci		if (len > left || plen < 0) {
723e5b75505Sopenharmony_ci			wpa_printf(MSG_DEBUG, "VLAN: Malformed netlink "
724e5b75505Sopenharmony_ci				   "message: len=%d left=%d plen=%d",
725e5b75505Sopenharmony_ci				   len, left, plen);
726e5b75505Sopenharmony_ci			break;
727e5b75505Sopenharmony_ci		}
728e5b75505Sopenharmony_ci
729e5b75505Sopenharmony_ci		switch (h->nlmsg_type) {
730e5b75505Sopenharmony_ci		case RTM_NEWLINK:
731e5b75505Sopenharmony_ci			vlan_read_ifnames(h, plen, 0, hapd);
732e5b75505Sopenharmony_ci			break;
733e5b75505Sopenharmony_ci		case RTM_DELLINK:
734e5b75505Sopenharmony_ci			vlan_read_ifnames(h, plen, 1, hapd);
735e5b75505Sopenharmony_ci			break;
736e5b75505Sopenharmony_ci		}
737e5b75505Sopenharmony_ci
738e5b75505Sopenharmony_ci		h = NLMSG_NEXT(h, left);
739e5b75505Sopenharmony_ci	}
740e5b75505Sopenharmony_ci
741e5b75505Sopenharmony_ci	if (left > 0) {
742e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "VLAN: %s: %d extra bytes in the end of "
743e5b75505Sopenharmony_ci			   "netlink message", __func__, left);
744e5b75505Sopenharmony_ci	}
745e5b75505Sopenharmony_ci}
746e5b75505Sopenharmony_ci
747e5b75505Sopenharmony_ci
748e5b75505Sopenharmony_cistruct full_dynamic_vlan *
749e5b75505Sopenharmony_cifull_dynamic_vlan_init(struct hostapd_data *hapd)
750e5b75505Sopenharmony_ci{
751e5b75505Sopenharmony_ci	struct sockaddr_nl local;
752e5b75505Sopenharmony_ci	struct full_dynamic_vlan *priv;
753e5b75505Sopenharmony_ci
754e5b75505Sopenharmony_ci	priv = os_zalloc(sizeof(*priv));
755e5b75505Sopenharmony_ci	if (priv == NULL)
756e5b75505Sopenharmony_ci		return NULL;
757e5b75505Sopenharmony_ci
758e5b75505Sopenharmony_ci	vlan_set_name_type(hapd->conf->ssid.vlan_naming ==
759e5b75505Sopenharmony_ci			   DYNAMIC_VLAN_NAMING_WITH_DEVICE ?
760e5b75505Sopenharmony_ci			   VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD :
761e5b75505Sopenharmony_ci			   VLAN_NAME_TYPE_PLUS_VID_NO_PAD);
762e5b75505Sopenharmony_ci
763e5b75505Sopenharmony_ci	priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
764e5b75505Sopenharmony_ci	if (priv->s < 0) {
765e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "VLAN: %s: socket(PF_NETLINK,SOCK_RAW,"
766e5b75505Sopenharmony_ci			   "NETLINK_ROUTE) failed: %s",
767e5b75505Sopenharmony_ci			   __func__, strerror(errno));
768e5b75505Sopenharmony_ci		os_free(priv);
769e5b75505Sopenharmony_ci		return NULL;
770e5b75505Sopenharmony_ci	}
771e5b75505Sopenharmony_ci
772e5b75505Sopenharmony_ci	os_memset(&local, 0, sizeof(local));
773e5b75505Sopenharmony_ci	local.nl_family = AF_NETLINK;
774e5b75505Sopenharmony_ci	local.nl_groups = RTMGRP_LINK;
775e5b75505Sopenharmony_ci	if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) {
776e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "VLAN: %s: bind(netlink) failed: %s",
777e5b75505Sopenharmony_ci			   __func__, strerror(errno));
778e5b75505Sopenharmony_ci		close(priv->s);
779e5b75505Sopenharmony_ci		os_free(priv);
780e5b75505Sopenharmony_ci		return NULL;
781e5b75505Sopenharmony_ci	}
782e5b75505Sopenharmony_ci
783e5b75505Sopenharmony_ci	if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, NULL))
784e5b75505Sopenharmony_ci	{
785e5b75505Sopenharmony_ci		close(priv->s);
786e5b75505Sopenharmony_ci		os_free(priv);
787e5b75505Sopenharmony_ci		return NULL;
788e5b75505Sopenharmony_ci	}
789e5b75505Sopenharmony_ci
790e5b75505Sopenharmony_ci	return priv;
791e5b75505Sopenharmony_ci}
792e5b75505Sopenharmony_ci
793e5b75505Sopenharmony_ci
794e5b75505Sopenharmony_civoid full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv)
795e5b75505Sopenharmony_ci{
796e5b75505Sopenharmony_ci	if (priv == NULL)
797e5b75505Sopenharmony_ci		return;
798e5b75505Sopenharmony_ci	eloop_unregister_read_sock(priv->s);
799e5b75505Sopenharmony_ci	close(priv->s);
800e5b75505Sopenharmony_ci	os_free(priv);
801e5b75505Sopenharmony_ci}
802