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