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