Make hostapd_for_each_interface() take context pointer as argument
[libeap.git] / hostapd / vlan_init.c
1 /*
2  * hostapd / VLAN initialization
3  * Copyright 2003, Instant802 Networks, Inc.
4  * Copyright 2005-2006, Devicescape Software, Inc.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  * Alternatively, this software may be distributed under the terms of BSD
11  * license.
12  *
13  * See README and COPYING for more details.
14  */
15
16 #include "includes.h"
17
18 #include "common.h"
19 #include "hostapd.h"
20 #include "driver_i.h"
21 #include "vlan_init.h"
22
23
24 #ifdef CONFIG_FULL_DYNAMIC_VLAN
25
26 #include <net/if.h>
27 #include <sys/ioctl.h>
28 #include <linux/sockios.h>
29 #include <linux/if_vlan.h>
30 #include <linux/if_bridge.h>
31
32 #include "drivers/priv_netlink.h"
33 #include "eloop.h"
34
35
36 struct full_dynamic_vlan {
37         int s; /* socket on which to listen for new/removed interfaces. */
38 };
39
40
41 static int ifconfig_helper(const char *if_name, int up)
42 {
43         int fd;
44         struct ifreq ifr;
45
46         if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
47                 perror("socket[AF_INET,SOCK_STREAM]");
48                 return -1;
49         }
50
51         os_memset(&ifr, 0, sizeof(ifr));
52         os_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ);
53
54         if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) {
55                 perror("ioctl[SIOCGIFFLAGS]");
56                 close(fd);
57                 return -1;
58         }
59
60         if (up)
61                 ifr.ifr_flags |= IFF_UP;
62         else
63                 ifr.ifr_flags &= ~IFF_UP;
64
65         if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) {
66                 perror("ioctl[SIOCSIFFLAGS]");
67                 close(fd);
68                 return -1;
69         }
70
71         close(fd);
72         return 0;
73 }
74
75
76 static int ifconfig_up(const char *if_name)
77 {
78         return ifconfig_helper(if_name, 1);
79 }
80
81
82 static int ifconfig_down(const char *if_name)
83 {
84         return ifconfig_helper(if_name, 0);
85 }
86
87
88 /*
89  * These are only available in recent linux headers (without the leading
90  * underscore).
91  */
92 #define _GET_VLAN_REALDEV_NAME_CMD      8
93 #define _GET_VLAN_VID_CMD               9
94
95 /* This value should be 256 ONLY. If it is something else, then hostapd
96  * might crash!, as this value has been hard-coded in 2.4.x kernel
97  * bridging code.
98  */
99 #define MAX_BR_PORTS                    256
100
101 static int br_delif(const char *br_name, const char *if_name)
102 {
103         int fd;
104         struct ifreq ifr;
105         unsigned long args[2];
106         int if_index;
107
108         if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
109                 perror("socket[AF_INET,SOCK_STREAM]");
110                 return -1;
111         }
112
113         if_index = if_nametoindex(if_name);
114
115         if (if_index == 0) {
116                 printf("Failure determining interface index for '%s'\n",
117                        if_name);
118                 close(fd);
119                 return -1;
120         }
121
122         args[0] = BRCTL_DEL_IF;
123         args[1] = if_index;
124
125         os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
126         ifr.ifr_data = (__caddr_t) args;
127
128         if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) {
129                 /* No error if interface already removed. */
130                 perror("ioctl[SIOCDEVPRIVATE,BRCTL_DEL_IF]");
131                 close(fd);
132                 return -1;
133         }
134
135         close(fd);
136         return 0;
137 }
138
139
140 /*
141         Add interface 'if_name' to the bridge 'br_name'
142
143         returns -1 on error
144         returns 1 if the interface is already part of the bridge
145         returns 0 otherwise
146 */
147 static int br_addif(const char *br_name, const char *if_name)
148 {
149         int fd;
150         struct ifreq ifr;
151         unsigned long args[2];
152         int if_index;
153
154         if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
155                 perror("socket[AF_INET,SOCK_STREAM]");
156                 return -1;
157         }
158
159         if_index = if_nametoindex(if_name);
160
161         if (if_index == 0) {
162                 printf("Failure determining interface index for '%s'\n",
163                        if_name);
164                 close(fd);
165                 return -1;
166         }
167
168         args[0] = BRCTL_ADD_IF;
169         args[1] = if_index;
170
171         os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
172         ifr.ifr_data = (__caddr_t) args;
173
174         if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
175                 if (errno == EBUSY) {
176                         /* The interface is already added. */
177                         close(fd);
178                         return 1;
179                 }
180
181                 perror("ioctl[SIOCDEVPRIVATE,BRCTL_ADD_IF]");
182                 close(fd);
183                 return -1;
184         }
185
186         close(fd);
187         return 0;
188 }
189
190
191 static int br_delbr(const char *br_name)
192 {
193         int fd;
194         unsigned long arg[2];
195
196         if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
197                 perror("socket[AF_INET,SOCK_STREAM]");
198                 return -1;
199         }
200
201         arg[0] = BRCTL_DEL_BRIDGE;
202         arg[1] = (unsigned long) br_name;
203
204         if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) {
205                 /* No error if bridge already removed. */
206                 perror("ioctl[BRCTL_DEL_BRIDGE]");
207                 close(fd);
208                 return -1;
209         }
210
211         close(fd);
212         return 0;
213 }
214
215
216 /*
217         Add a bridge with the name 'br_name'.
218
219         returns -1 on error
220         returns 1 if the bridge already exists
221         returns 0 otherwise
222 */
223 static int br_addbr(const char *br_name)
224 {
225         int fd;
226         unsigned long arg[2];
227
228         if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
229                 perror("socket[AF_INET,SOCK_STREAM]");
230                 return -1;
231         }
232
233         arg[0] = BRCTL_ADD_BRIDGE;
234         arg[1] = (unsigned long) br_name;
235
236         if (ioctl(fd, SIOCGIFBR, arg) < 0) {
237                 if (errno == EEXIST) {
238                         /* The bridge is already added. */
239                         close(fd);
240                         return 1;
241                 } else {
242                         perror("ioctl[BRCTL_ADD_BRIDGE]");
243                         close(fd);
244                         return -1;
245                 }
246         }
247
248         close(fd);
249         return 0;
250 }
251
252
253 static int br_getnumports(const char *br_name)
254 {
255         int fd;
256         int i;
257         int port_cnt = 0;
258         unsigned long arg[4];
259         int ifindices[MAX_BR_PORTS];
260         struct ifreq ifr;
261
262         if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
263                 perror("socket[AF_INET,SOCK_STREAM]");
264                 return -1;
265         }
266
267         arg[0] = BRCTL_GET_PORT_LIST;
268         arg[1] = (unsigned long) ifindices;
269         arg[2] = MAX_BR_PORTS;
270         arg[3] = 0;
271
272         os_memset(ifindices, 0, sizeof(ifindices));
273         os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
274         ifr.ifr_data = (__caddr_t) arg;
275
276         if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
277                 perror("ioctl[SIOCDEVPRIVATE,BRCTL_GET_PORT_LIST]");
278                 close(fd);
279                 return -1;
280         }
281
282         for (i = 1; i < MAX_BR_PORTS; i++) {
283                 if (ifindices[i] > 0) {
284                         port_cnt++;
285                 }
286         }
287
288         close(fd);
289         return port_cnt;
290 }
291
292
293 static int vlan_rem(const char *if_name)
294 {
295         int fd;
296         struct vlan_ioctl_args if_request;
297
298         if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) {
299                 fprintf(stderr, "Interface name to long.\n");
300                 return -1;
301         }
302
303         if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
304                 perror("socket[AF_INET,SOCK_STREAM]");
305                 return -1;
306         }
307
308         os_memset(&if_request, 0, sizeof(if_request));
309
310         os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1));
311         if_request.cmd = DEL_VLAN_CMD;
312
313         if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
314                 perror("ioctl[SIOCSIFVLAN,DEL_VLAN_CMD]");
315                 close(fd);
316                 return -1;
317         }
318
319         close(fd);
320         return 0;
321 }
322
323
324 /*
325         Add a vlan interface with VLAN ID 'vid' and tagged interface
326         'if_name'.
327
328         returns -1 on error
329         returns 1 if the interface already exists
330         returns 0 otherwise
331 */
332 static int vlan_add(const char *if_name, int vid)
333 {
334         int fd;
335         struct vlan_ioctl_args if_request;
336
337         ifconfig_up(if_name);
338
339         if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) {
340                 fprintf(stderr, "Interface name to long.\n");
341                 return -1;
342         }
343
344         if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
345                 perror("socket[AF_INET,SOCK_STREAM]");
346                 return -1;
347         }
348
349         os_memset(&if_request, 0, sizeof(if_request));
350
351         /* Determine if a suitable vlan device already exists. */
352
353         os_snprintf(if_request.device1, sizeof(if_request.device1), "vlan%d",
354                     vid);
355
356         if_request.cmd = _GET_VLAN_VID_CMD;
357
358         if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0) {
359
360                 if (if_request.u.VID == vid) {
361                         if_request.cmd = _GET_VLAN_REALDEV_NAME_CMD;
362
363                         if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 &&
364                             os_strncmp(if_request.u.device2, if_name,
365                                        sizeof(if_request.u.device2)) == 0) {
366                                 close(fd);
367                                 return 1;
368                         }
369                 }
370         }
371
372         /* A suitable vlan device does not already exist, add one. */
373
374         os_memset(&if_request, 0, sizeof(if_request));
375         os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1));
376         if_request.u.VID = vid;
377         if_request.cmd = ADD_VLAN_CMD;
378
379         if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
380                 perror("ioctl[SIOCSIFVLAN,ADD_VLAN_CMD]");
381                 close(fd);
382                 return -1;
383         }
384
385         close(fd);
386         return 0;
387 }
388
389
390 static int vlan_set_name_type(unsigned int name_type)
391 {
392         int fd;
393         struct vlan_ioctl_args if_request;
394
395         if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
396                 perror("socket[AF_INET,SOCK_STREAM]");
397                 return -1;
398         }
399
400         os_memset(&if_request, 0, sizeof(if_request));
401
402         if_request.u.name_type = name_type;
403         if_request.cmd = SET_VLAN_NAME_TYPE_CMD;
404         if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
405                 perror("ioctl[SIOCSIFVLAN,SET_VLAN_NAME_TYPE_CMD]");
406                 close(fd);
407                 return -1;
408         }
409
410         close(fd);
411         return 0;
412 }
413
414
415 static void vlan_newlink(char *ifname, struct hostapd_data *hapd)
416 {
417         char vlan_ifname[IFNAMSIZ];
418         char br_name[IFNAMSIZ];
419         struct hostapd_vlan *vlan = hapd->conf->vlan;
420         char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
421
422         while (vlan) {
423                 if (os_strcmp(ifname, vlan->ifname) == 0) {
424
425                         os_snprintf(br_name, sizeof(br_name), "brvlan%d",
426                                     vlan->vlan_id);
427
428                         if (!br_addbr(br_name))
429                                 vlan->clean |= DVLAN_CLEAN_BR;
430
431                         ifconfig_up(br_name);
432
433                         if (tagged_interface) {
434
435                                 if (!vlan_add(tagged_interface, vlan->vlan_id))
436                                         vlan->clean |= DVLAN_CLEAN_VLAN;
437
438                                 os_snprintf(vlan_ifname, sizeof(vlan_ifname),
439                                             "vlan%d", vlan->vlan_id);
440
441                                 if (!br_addif(br_name, vlan_ifname))
442                                         vlan->clean |= DVLAN_CLEAN_VLAN_PORT;
443
444                                 ifconfig_up(vlan_ifname);
445                         }
446
447                         if (!br_addif(br_name, ifname))
448                                 vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
449
450                         ifconfig_up(ifname);
451
452                         break;
453                 }
454                 vlan = vlan->next;
455         }
456 }
457
458
459 static void vlan_dellink(char *ifname, struct hostapd_data *hapd)
460 {
461         char vlan_ifname[IFNAMSIZ];
462         char br_name[IFNAMSIZ];
463         struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan;
464         char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
465         int numports;
466
467         first = prev = vlan;
468
469         while (vlan) {
470                 if (os_strcmp(ifname, vlan->ifname) == 0) {
471                         os_snprintf(br_name, sizeof(br_name), "brvlan%d",
472                                     vlan->vlan_id);
473
474                         if (tagged_interface) {
475                                 os_snprintf(vlan_ifname, sizeof(vlan_ifname),
476                                             "vlan%d", vlan->vlan_id);
477
478                                 numports = br_getnumports(br_name);
479                                 if (numports == 1) {
480                                         br_delif(br_name, vlan_ifname);
481
482                                         vlan_rem(vlan_ifname);
483
484                                         ifconfig_down(br_name);
485                                         br_delbr(br_name);
486                                 }
487                         }
488
489                         if (vlan == first) {
490                                 hapd->conf->vlan = vlan->next;
491                         } else {
492                                 prev->next = vlan->next;
493                         }
494                         os_free(vlan);
495
496                         break;
497                 }
498                 prev = vlan;
499                 vlan = vlan->next;
500         }
501 }
502
503
504 static void
505 vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del,
506                   struct hostapd_data *hapd)
507 {
508         struct ifinfomsg *ifi;
509         int attrlen, nlmsg_len, rta_len;
510         struct rtattr *attr;
511
512         if (len < sizeof(*ifi))
513                 return;
514
515         ifi = NLMSG_DATA(h);
516
517         nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg));
518
519         attrlen = h->nlmsg_len - nlmsg_len;
520         if (attrlen < 0)
521                 return;
522
523         attr = (struct rtattr *) (((char *) ifi) + nlmsg_len);
524
525         rta_len = RTA_ALIGN(sizeof(struct rtattr));
526         while (RTA_OK(attr, attrlen)) {
527                 char ifname[IFNAMSIZ + 1];
528
529                 if (attr->rta_type == IFLA_IFNAME) {
530                         int n = attr->rta_len - rta_len;
531                         if (n < 0)
532                                 break;
533
534                         os_memset(ifname, 0, sizeof(ifname));
535
536                         if ((size_t) n > sizeof(ifname))
537                                 n = sizeof(ifname);
538                         os_memcpy(ifname, ((char *) attr) + rta_len, n);
539
540                         if (del)
541                                 vlan_dellink(ifname, hapd);
542                         else
543                                 vlan_newlink(ifname, hapd);
544                 }
545
546                 attr = RTA_NEXT(attr, attrlen);
547         }
548 }
549
550
551 static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx)
552 {
553         char buf[8192];
554         int left;
555         struct sockaddr_nl from;
556         socklen_t fromlen;
557         struct nlmsghdr *h;
558         struct hostapd_data *hapd = eloop_ctx;
559
560         fromlen = sizeof(from);
561         left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
562                         (struct sockaddr *) &from, &fromlen);
563         if (left < 0) {
564                 if (errno != EINTR && errno != EAGAIN)
565                         perror("recvfrom(netlink)");
566                 return;
567         }
568
569         h = (struct nlmsghdr *) buf;
570         while (left >= (int) sizeof(*h)) {
571                 int len, plen;
572
573                 len = h->nlmsg_len;
574                 plen = len - sizeof(*h);
575                 if (len > left || plen < 0) {
576                         printf("Malformed netlink message: "
577                                "len=%d left=%d plen=%d", len, left, plen);
578                         break;
579                 }
580
581                 switch (h->nlmsg_type) {
582                 case RTM_NEWLINK:
583                         vlan_read_ifnames(h, plen, 0, hapd);
584                         break;
585                 case RTM_DELLINK:
586                         vlan_read_ifnames(h, plen, 1, hapd);
587                         break;
588                 }
589
590                 len = NLMSG_ALIGN(len);
591                 left -= len;
592                 h = (struct nlmsghdr *) ((char *) h + len);
593         }
594
595         if (left > 0) {
596                 printf("%d extra bytes in the end of netlink message",
597                        left);
598         }
599 }
600
601
602 static struct full_dynamic_vlan *
603 full_dynamic_vlan_init(struct hostapd_data *hapd)
604 {
605         struct sockaddr_nl local;
606         struct full_dynamic_vlan *priv;
607
608         priv = os_zalloc(sizeof(*priv));
609         if (priv == NULL)
610                 return NULL;
611
612         vlan_set_name_type(VLAN_NAME_TYPE_PLUS_VID_NO_PAD);
613
614         priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
615         if (priv->s < 0) {
616                 perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)");
617                 os_free(priv);
618                 return NULL;
619         }
620
621         os_memset(&local, 0, sizeof(local));
622         local.nl_family = AF_NETLINK;
623         local.nl_groups = RTMGRP_LINK;
624         if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) {
625                 perror("bind(netlink)");
626                 close(priv->s);
627                 os_free(priv);
628                 return NULL;
629         }
630
631         if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, NULL))
632         {
633                 close(priv->s);
634                 os_free(priv);
635                 return NULL;
636         }
637
638         return priv;
639 }
640
641
642 static void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv)
643 {
644         if (priv == NULL)
645                 return;
646         eloop_unregister_read_sock(priv->s);
647         close(priv->s);
648         os_free(priv);
649 }
650 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
651
652
653 int vlan_setup_encryption_dyn(struct hostapd_data *hapd,
654                               struct hostapd_ssid *mssid, const char *dyn_vlan)
655 {
656         int i;
657
658         if (dyn_vlan == NULL)
659                 return 0;
660
661         /* Static WEP keys are set here; IEEE 802.1X and WPA uses their own
662          * functions for setting up dynamic broadcast keys. */
663         for (i = 0; i < 4; i++) {
664                 if (mssid->wep.key[i] &&
665                     hostapd_set_key(dyn_vlan, hapd, WPA_ALG_WEP, NULL, i,
666                                     i == mssid->wep.idx, NULL, 0,
667                                     mssid->wep.key[i], mssid->wep.len[i])) {
668                         printf("VLAN: Could not set WEP encryption for "
669                                "dynamic VLAN.\n");
670                         return -1;
671                 }
672         }
673
674         return 0;
675 }
676
677
678 static int vlan_dynamic_add(struct hostapd_data *hapd,
679                             struct hostapd_vlan *vlan)
680 {
681         while (vlan) {
682                 if (vlan->vlan_id != VLAN_ID_WILDCARD &&
683                     hostapd_if_add(hapd, WPA_IF_AP_VLAN, vlan->ifname, NULL,
684                                    NULL)) {
685                         if (errno != EEXIST) {
686                                 printf("Could not add VLAN iface: %s: %s\n",
687                                        vlan->ifname, strerror(errno));
688                                 return -1;
689                         }
690                 }
691
692                 vlan = vlan->next;
693         }
694
695         return 0;
696 }
697
698
699 static void vlan_dynamic_remove(struct hostapd_data *hapd,
700                                 struct hostapd_vlan *vlan)
701 {
702         struct hostapd_vlan *next;
703
704         while (vlan) {
705                 next = vlan->next;
706
707                 if (vlan->vlan_id != VLAN_ID_WILDCARD &&
708                     hostapd_if_remove(hapd, WPA_IF_AP_VLAN, vlan->ifname)) {
709                         printf("Could not remove VLAN iface: %s: %s\n",
710                                vlan->ifname, strerror(errno));
711                 }
712 #ifdef CONFIG_FULL_DYNAMIC_VLAN
713                 if (vlan->clean)
714                         vlan_dellink(vlan->ifname, hapd);
715 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
716
717                 vlan = next;
718         }
719 }
720
721
722 int vlan_init(struct hostapd_data *hapd)
723 {
724         if (vlan_dynamic_add(hapd, hapd->conf->vlan))
725                 return -1;
726
727 #ifdef CONFIG_FULL_DYNAMIC_VLAN
728         hapd->full_dynamic_vlan = full_dynamic_vlan_init(hapd);
729 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
730
731         return 0;
732 }
733
734
735 void vlan_deinit(struct hostapd_data *hapd)
736 {
737         vlan_dynamic_remove(hapd, hapd->conf->vlan);
738
739 #ifdef CONFIG_FULL_DYNAMIC_VLAN
740         full_dynamic_vlan_deinit(hapd->full_dynamic_vlan);
741 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
742 }
743
744
745 struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
746                                        struct hostapd_vlan *vlan,
747                                        int vlan_id)
748 {
749         struct hostapd_vlan *n;
750         char *ifname, *pos;
751
752         if (vlan == NULL || vlan_id <= 0 || vlan_id > MAX_VLAN_ID ||
753             vlan->vlan_id != VLAN_ID_WILDCARD)
754                 return NULL;
755
756         ifname = os_strdup(vlan->ifname);
757         if (ifname == NULL)
758                 return NULL;
759         pos = os_strchr(ifname, '#');
760         if (pos == NULL) {
761                 os_free(ifname);
762                 return NULL;
763         }
764         *pos++ = '\0';
765
766         n = os_zalloc(sizeof(*n));
767         if (n == NULL) {
768                 os_free(ifname);
769                 return NULL;
770         }
771
772         n->vlan_id = vlan_id;
773         n->dynamic_vlan = 1;
774
775         os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id,
776                     pos);
777         os_free(ifname);
778
779         if (hostapd_if_add(hapd, WPA_IF_AP_VLAN, n->ifname, NULL, NULL)) {
780                 os_free(n);
781                 return NULL;
782         }
783
784         n->next = hapd->conf->vlan;
785         hapd->conf->vlan = n;
786
787         return n;
788 }
789
790
791 int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id)
792 {
793         struct hostapd_vlan *vlan;
794
795         if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID)
796                 return 1;
797
798         vlan = hapd->conf->vlan;
799         while (vlan) {
800                 if (vlan->vlan_id == vlan_id && vlan->dynamic_vlan > 0) {
801                         vlan->dynamic_vlan--;
802                         break;
803                 }
804                 vlan = vlan->next;
805         }
806
807         if (vlan == NULL)
808                 return 1;
809
810         if (vlan->dynamic_vlan == 0)
811                 hostapd_if_remove(hapd, WPA_IF_AP_VLAN, vlan->ifname);
812
813         return 0;
814 }