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