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