1 // SoftEther VPN Source Code - Developer Edition Master Branch
2 // Cedar Communication Module
3 
4 
5 // VLanUnix.c
6 // Virtual device driver library for UNIX
7 
8 #ifdef UNIX
9 
10 #include "VLanUnix.h"
11 
12 #include "Connection.h"
13 #include "Session.h"
14 
15 #include "Mayaqua/FileIO.h"
16 #include "Mayaqua/Mayaqua.h"
17 #include "Mayaqua/Memory.h"
18 #include "Mayaqua/Str.h"
19 #include "Mayaqua/TunTap.h"
20 
21 #ifdef UNIX_BSD
22 // For "sockaddr" in <net/if_arp.h>
23 #include <sys/socket.h>
24 #endif
25 
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <net/if_arp.h>
29 #include <net/if.h>
30 #include <sys/ioctl.h>
31 
32 #ifdef UNIX_OPENBSD
33 #include <netinet/if_ether.h>
34 #else
35 #include <net/ethernet.h>
36 #endif
37 
38 static LIST *unix_vlan = NULL;
39 
40 #ifndef NO_VLAN
41 
42 // Get the PACKET_ADAPTER
VLanGetPacketAdapter()43 PACKET_ADAPTER *VLanGetPacketAdapter()
44 {
45 	PACKET_ADAPTER *pa;
46 
47 	pa = NewPacketAdapter(VLanPaInit, VLanPaGetCancel,
48 		VLanPaGetNextPacket, VLanPaPutPacket, VLanPaFree);
49 	if (pa == NULL)
50 	{
51 		return NULL;
52 	}
53 
54 	return pa;
55 }
56 
57 // PA initialization
VLanPaInit(SESSION * s)58 bool VLanPaInit(SESSION *s)
59 {
60 	VLAN *v;
61 	// Validate arguments
62 	if (s == NULL)
63 	{
64 		return false;
65 	}
66 
67 	// Connect to the driver
68 	v = NewVLan(s->ClientOption->DeviceName, NULL);
69 	if (v == NULL)
70 	{
71 		// Failure
72 		return false;
73 	}
74 
75 	s->PacketAdapter->Param = v;
76 
77 	return true;
78 }
79 
80 // Get the cancel object
VLanPaGetCancel(SESSION * s)81 CANCEL *VLanPaGetCancel(SESSION *s)
82 {
83 	VLAN *v;
84 	// Validate arguments
85 	if ((s == NULL) || ((v = s->PacketAdapter->Param) == NULL))
86 	{
87 		return NULL;
88 	}
89 
90 	return VLanGetCancel(v);
91 }
92 
93 // Release the packet adapter
VLanPaFree(SESSION * s)94 void VLanPaFree(SESSION *s)
95 {
96 	VLAN *v;
97 	// Validate arguments
98 	if ((s == NULL) || ((v = s->PacketAdapter->Param) == NULL))
99 	{
100 		return;
101 	}
102 
103 	// End the virtual LAN card
104 	FreeVLan(v);
105 
106 	s->PacketAdapter->Param = NULL;
107 }
108 
109 // Write a packet
VLanPaPutPacket(SESSION * s,void * data,UINT size)110 bool VLanPaPutPacket(SESSION *s, void *data, UINT size)
111 {
112 	VLAN *v;
113 	// Validate arguments
114 	if ((s == NULL) || ((v = s->PacketAdapter->Param) == NULL))
115 	{
116 		return false;
117 	}
118 
119 	return VLanPutPacket(v, data, size);
120 }
121 
122 // Get the next packet
VLanPaGetNextPacket(SESSION * s,void ** data)123 UINT VLanPaGetNextPacket(SESSION *s, void **data)
124 {
125 	VLAN *v;
126 	UINT size;
127 	// Validate arguments
128 	if (data == NULL || (s == NULL) || ((v = s->PacketAdapter->Param) == NULL))
129 	{
130 		return INFINITE;
131 	}
132 
133 	if (VLanGetNextPacket(v, data, &size) == false)
134 	{
135 		return INFINITE;
136 	}
137 
138 	return size;
139 }
140 
141 // Write a packet to the virtual LAN card
VLanPutPacket(VLAN * v,void * buf,UINT size)142 bool VLanPutPacket(VLAN *v, void *buf, UINT size)
143 {
144 	UINT ret;
145 	// Validate arguments
146 	if (v == NULL)
147 	{
148 		return false;
149 	}
150 	if (v->Halt)
151 	{
152 		return false;
153 	}
154 	if (size > MAX_PACKET_SIZE)
155 	{
156 		return false;
157 	}
158 	if (buf == NULL || size == 0)
159 	{
160 		if (buf != NULL)
161 		{
162 			Free(buf);
163 		}
164 		return true;
165 	}
166 
167 	ret = write(v->fd, buf, size);
168 
169 	if (ret >= 1)
170 	{
171 		Free(buf);
172 		return true;
173 	}
174 
175 	if (errno == EAGAIN || ret == 0)
176 	{
177 		Free(buf);
178 		return true;
179 	}
180 
181 	return false;
182 }
183 
184 // Get the next packet from the virtual LAN card
VLanGetNextPacket(VLAN * v,void ** buf,UINT * size)185 bool VLanGetNextPacket(VLAN *v, void **buf, UINT *size)
186 {
187 	UCHAR tmp[TAP_READ_BUF_SIZE];
188 	int ret;
189 	// Validate arguments
190 	if (v == NULL || buf == NULL || size == 0)
191 	{
192 		return false;
193 	}
194 	if (v->Halt)
195 	{
196 		return false;
197 	}
198 
199 	// Read
200 	ret = read(v->fd, tmp, sizeof(tmp));
201 
202 	if (ret == 0 ||
203 		(ret == -1 && errno == EAGAIN))
204 	{
205 		// No packet
206 		*buf = NULL;
207 		*size = 0;
208 		return true;
209 	}
210 	else if (ret == -1 || ret > TAP_READ_BUF_SIZE)
211 	{
212 		// Error
213 		return false;
214 	}
215 	else
216 	{
217 		// Reading packet success
218 		*buf = Malloc(ret);
219 		Copy(*buf, tmp, ret);
220 		*size = ret;
221 		return true;
222 	}
223 }
224 
225 // Get the cancel object
VLanGetCancel(VLAN * v)226 CANCEL *VLanGetCancel(VLAN *v)
227 {
228 	CANCEL *c;
229 	int fd;
230 	int yes = 0;
231 	// Validate arguments
232 	if (v == NULL)
233 	{
234 		return NULL;
235 	}
236 
237 	c = NewCancel();
238 	UnixDeletePipe(c->pipe_read, c->pipe_write);
239 	c->pipe_read = c->pipe_write = -1;
240 
241 	fd = v->fd;
242 
243 	UnixSetSocketNonBlockingMode(fd, true);
244 
245 	c->SpecialFlag = true;
246 	c->pipe_read = fd;
247 
248 	return c;
249 }
250 
251 // Close the Virtual LAN card
FreeVLan(VLAN * v)252 void FreeVLan(VLAN *v)
253 {
254 	// Validate arguments
255 	if (v == NULL)
256 	{
257 		return;
258 	}
259 
260 	Free(v->InstanceName);
261 
262 	Free(v);
263 }
264 
265 // Create a tap
NewTap(char * name,char * mac_address,bool create_up)266 VLAN *NewTap(char *name, char *mac_address, bool create_up)
267 {
268 	int fd;
269 	VLAN *v;
270 	// Validate arguments
271 	if (name == NULL || mac_address == NULL)
272 	{
273 		return NULL;
274 	}
275 
276 	fd = UnixCreateTapDeviceEx(name, "tap", mac_address, create_up);
277 	if (fd == -1)
278 	{
279 		return NULL;
280 	}
281 
282 	v = ZeroMalloc(sizeof(VLAN));
283 	v->Halt = false;
284 	v->InstanceName = CopyStr(name);
285 	v->fd = fd;
286 
287 	return v;
288 }
289 
290 // Close the tap
FreeTap(VLAN * v)291 void FreeTap(VLAN *v)
292 {
293 	// Validate arguments
294 	if (v == NULL)
295 	{
296 		return;
297 	}
298 
299 	close(v->fd);
300 	FreeVLan(v);
301 }
302 
303 // Get the Virtual LAN card list
NewVLan(char * instance_name,VLAN_PARAM * param)304 VLAN *NewVLan(char *instance_name, VLAN_PARAM *param)
305 {
306 	int fd;
307 	VLAN *v;
308 	// Validate arguments
309 	if (instance_name == NULL)
310 	{
311 		return NULL;
312 	}
313 
314 	// Open the tap
315 	fd = UnixVLanGet(instance_name);
316 	if (fd == -1)
317 	{
318 		return NULL;
319 	}
320 
321 	v = ZeroMalloc(sizeof(VLAN));
322 	v->Halt = false;
323 	v->InstanceName = CopyStr(instance_name);
324 	v->fd = fd;
325 
326 	return v;
327 }
328 
329 // Generate TUN interface name
GenerateTunName(char * name,char * prefix,char * tun_name,size_t tun_name_len)330 void GenerateTunName(char *name, char *prefix, char *tun_name, size_t tun_name_len)
331 {
332 	char instance_name_lower[MAX_SIZE];
333 
334 	// Generate the device name
335 	StrCpy(instance_name_lower, sizeof(instance_name_lower), name);
336 	Trim(instance_name_lower);
337 	StrLower(instance_name_lower);
338 	Format(tun_name, tun_name_len, "%s_%s", prefix, instance_name_lower);
339 
340 	tun_name[15] = 0;
341 }
342 // Create a tap device
UnixCreateTapDeviceEx(char * name,char * prefix,UCHAR * mac_address,bool create_up)343 int UnixCreateTapDeviceEx(char *name, char *prefix, UCHAR *mac_address, bool create_up)
344 {
345 	int fd = -1, s = -1;
346 	char tap_name[MAX_SIZE], tap_path[MAX_SIZE];
347 	struct ifreq ifr;
348 
349 	// Validate arguments
350 	if (name == NULL)
351 	{
352 		return -1;
353 	}
354 
355 	GenerateTunName(name, prefix, tap_name, sizeof(tap_name));
356 
357 	// Open the tun / tap
358 #ifndef	UNIX_BSD
359 	if (GetOsInfo()->OsType == OSTYPE_LINUX)
360 	{
361 		// Linux
362 		if (IsFile(TAP_FILENAME_1) == false)
363 		{
364 			char tmp[MAX_SIZE];
365 
366 			Format(tmp, sizeof(tmp), "%s c 10 200", TAP_FILENAME_1);
367 			Run("mknod", tmp, true, true);
368 
369 			Format(tmp, sizeof(tmp), "600 %s", TAP_FILENAME_1);
370 			Run("chmod", tmp, true, true);
371 		}
372 	}
373 
374 	fd = open(TAP_FILENAME_1, O_RDWR);
375 	if (fd == -1)
376 	{
377 		// Failure
378 		fd = open(TAP_FILENAME_2, O_RDWR);
379 		if (fd == -1)
380 		{
381 			return -1;
382 		}
383 	}
384 #else	// UNIX_BSD
385 	{
386 		sprintf(tap_path, "%s", TAP_DIR TAP_NAME);
387 		for (int i = 0; i < TAP_MAX; i++) {
388 			sprintf(tap_path + StrLen(TAP_DIR TAP_NAME), "%d", i);
389 			fd = open(tap_path, O_RDWR);
390 			if (fd != -1)
391 			{
392 				break;
393 			}
394 		}
395 
396 		if (fd == -1)
397 		{
398 			return -1;
399 		}
400 	}
401 #endif	// UNIX_BSD
402 
403 #ifdef	UNIX_LINUX
404 	// Create a TAP device for Linux
405 
406 	// Set the name and the flags
407 	Zero(&ifr, sizeof(ifr));
408 	StrCpy(ifr.ifr_name, sizeof(ifr.ifr_name), tap_name);
409 	ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
410 
411 	if (ioctl(fd, TUNSETIFF, &ifr) == -1)
412 	{
413 		// Failure
414 		close(fd);
415 		return -1;
416 	}
417 
418 	s = socket(AF_INET, SOCK_DGRAM, 0);
419 	if (s != -1)
420 	{
421 		// Set the MAC address
422 		if (mac_address != NULL)
423 		{
424 			Zero(&ifr, sizeof(ifr));
425 			StrCpy(ifr.ifr_name, sizeof(ifr.ifr_name), tap_name);
426 			ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
427 			Copy(&ifr.ifr_hwaddr.sa_data, mac_address, ETHER_ADDR_LEN);
428 			ioctl(s, SIOCSIFHWADDR, &ifr);
429 		}
430 
431 		if (create_up)
432 		{
433 			Zero(&ifr, sizeof(ifr));
434 			StrCpy(ifr.ifr_name, sizeof(ifr.ifr_name), tap_name);
435 			ioctl(s, SIOCGIFFLAGS, &ifr);
436 			ifr.ifr_flags |= IFF_UP;
437 			ioctl(s, SIOCSIFFLAGS, &ifr);
438 		}
439 
440 		close(s);
441 	}
442 #endif	// UNIX_LINUX
443 
444 #ifdef	UNIX_BSD
445 	// Create a TAP device for BSD
446 	Zero(&ifr, sizeof(ifr));
447 
448 	// Get the current name
449 	StrCpy(ifr.ifr_name, sizeof(ifr.ifr_name), tap_path + StrLen(TAP_DIR));
450 
451 	s = socket(AF_INET, SOCK_DGRAM, 0);
452 	if (s != -1)
453 	{
454 		// Set the name, if possible
455 #ifdef	SIOCSIFNAME
456 		ifr.ifr_data = tap_name;
457 		ioctl(s, SIOCSIFNAME, &ifr);
458 #else	// SIOCSIFNAME
459 		StrCpy(tap_name, sizeof(tap_name), ifr.ifr_name);
460 #endif	// SIOCSIFNAME
461 
462 		// Set the MAC address
463 		if (mac_address != NULL)
464 		{
465 			Zero(&ifr, sizeof(ifr));
466 			StrCpy(ifr.ifr_name, sizeof(ifr.ifr_name), tap_name);
467 			ifr.ifr_addr.sa_len = ETHER_ADDR_LEN;
468 			ifr.ifr_addr.sa_family = AF_LINK;
469 			Copy(&ifr.ifr_addr.sa_data, mac_address, ETHER_ADDR_LEN);
470 			ioctl(s, SIOCSIFLLADDR, &ifr);
471 		}
472 
473 		if (create_up)
474 		{
475 			Zero(&ifr, sizeof(ifr));
476 			StrCpy(ifr.ifr_name, sizeof(ifr.ifr_name), tap_name);
477 			ioctl(s, SIOCGIFFLAGS, &ifr);
478 			ifr.ifr_flags |= IFF_UP;
479 			ioctl(s, SIOCSIFFLAGS, &ifr);
480 		}
481 
482 		close(s);
483 	}
484 #endif	// UNIX_BSD
485 
486 #ifdef	UNIX_SOLARIS
487 	// Create a TAP device for Solaris
488 	{
489 		int ip_fd;
490 		int tun_fd;
491 		int ppa;
492 
493 		tun_fd = open(tap_name, O_RDWR);
494 		if (tun_fd == -1)
495 		{
496 			// Failure
497 			close(fd);
498 			return -1;
499 		}
500 
501 		ip_fd = open("/dev/ip", O_RDWR);
502 		if (ip_fd == -1)
503 		{
504 			// Failure
505 			close(tun_fd);
506 			close(fd);
507 			return -1;
508 		}
509 
510 		ppa = -1;
511 		ppa = ioctl(tun_fd, TUNNEWPPA, ppa);
512 		if (ppa == -1)
513 		{
514 			// Failure
515 			close(tun_fd);
516 			close(fd);
517 			close(ip_fd);
518 			return -1;
519 		}
520 
521 		if (ioctl(fd, I_PUSH, "ip") < 0)
522 		{
523 			// Failure
524 			close(tun_fd);
525 			close(fd);
526 			close(ip_fd);
527 			return -1;
528 		}
529 
530 		if (ioctl(fd, IF_UNITSEL, (char *)&ppa) < 0)
531 		{
532 			// Failure
533 			close(tun_fd);
534 			close(fd);
535 			close(ip_fd);
536 			return -1;
537 		}
538 
539 		if (ioctl(ip_fd, I_LINK, fd) < 0)
540 		{
541 			// Failure
542 			close(tun_fd);
543 			close(fd);
544 			close(ip_fd);
545 			return -1;
546 		}
547 
548 		close(tun_fd);
549 		close(ip_fd);
550 	}
551 #endif	// UNIX_SOLARIS
552 
553 	return fd;
554 }
UnixCreateTapDevice(char * name,UCHAR * mac_address,bool create_up)555 int UnixCreateTapDevice(char *name, UCHAR *mac_address, bool create_up)
556 {
557 	return UnixCreateTapDeviceEx(name, "vpn", mac_address, create_up);
558 }
559 
560 // Close the tap device
UnixCloseTapDevice(int fd)561 void UnixCloseTapDevice(int fd)
562 {
563 	// Validate arguments
564 	if (fd == -1)
565 	{
566 		return;
567 	}
568 
569 	close(fd);
570 }
571 
572 #else	// NO_VLAN
573 
UnixCloseTapDevice(int fd)574 void UnixCloseTapDevice(int fd)
575 {
576 }
577 
UnixCreateTapDeviceEx(char * name,char * prefix,UCHAR * mac_address,bool create_up)578 int UnixCreateTapDeviceEx(char *name, char *prefix, UCHAR *mac_address, bool create_up)
579 {
580 	return -1;
581 }
UnixCreateTapDevice(char * name,UCHAR * mac_address,bool create_up)582 int UnixCreateTapDevice(char *name, UCHAR *mac_address, bool create_up)
583 {
584 	return -1;
585 }
586 
587 #endif	// NO_VLAN
588 
589 // Comparison of the VLAN list entries
UnixCompareVLan(void * p1,void * p2)590 int UnixCompareVLan(void *p1, void *p2)
591 {
592 	UNIX_VLAN_LIST *v1, *v2;
593 	if (p1 == NULL || p2 == NULL)
594 	{
595 		return 0;
596 	}
597 	v1 = *(UNIX_VLAN_LIST **)p1;
598 	v2 = *(UNIX_VLAN_LIST **)p2;
599 	if (v1 == NULL || v2 == NULL)
600 	{
601 		return 0;
602 	}
603 
604 	return StrCmpi(v1->Name, v2->Name);
605 }
606 
607 // Initialize the VLAN list
UnixVLanInit()608 void UnixVLanInit()
609 {
610 	unix_vlan = NewList(UnixCompareVLan);
611 }
612 
613 // Create a VLAN
UnixVLanCreateEx(char * name,char * prefix,UCHAR * mac_address,bool create_up)614 bool UnixVLanCreateEx(char *name, char *prefix, UCHAR *mac_address, bool create_up)
615 {
616 	// Validate arguments
617 	char tmp[MAX_SIZE];
618 	if (name == NULL)
619 	{
620 		return false;
621 	}
622 
623 	StrCpy(tmp, sizeof(tmp), name);
624 	Trim(tmp);
625 	name = tmp;
626 
627 	LockList(unix_vlan);
628 	{
629 		UNIX_VLAN_LIST *t, tt;
630 		int fd;
631 
632 		// Check whether a device with the same name exists
633 		Zero(&tt, sizeof(tt));
634 		StrCpy(tt.Name, sizeof(tt.Name), name);
635 
636 		t = Search(unix_vlan, &tt);
637 		if (t != NULL)
638 		{
639 			// Already exist
640 			UnlockList(unix_vlan);
641 			return false;
642 		}
643 
644 		// Create a tap device
645 		fd = UnixCreateTapDeviceEx(name, prefix, mac_address, create_up);
646 		if (fd == -1)
647 		{
648 			// Failure to create
649 			UnlockList(unix_vlan);
650 			return false;
651 		}
652 
653 		t = ZeroMalloc(sizeof(UNIX_VLAN_LIST));
654 		t->fd = fd;
655 		StrCpy(t->Name, sizeof(t->Name), name);
656 
657 		Insert(unix_vlan, t);
658 	}
659 	UnlockList(unix_vlan);
660 
661 	return true;
662 }
UnixVLanCreate(char * name,UCHAR * mac_address,bool create_up)663 bool UnixVLanCreate(char *name, UCHAR *mac_address, bool create_up)
664 {
665 	return UnixVLanCreateEx(name, "vpn", mac_address, create_up);
666 }
667 
668 // Set a VLAN up/down
UnixVLanSetState(char * name,bool state_up)669 bool UnixVLanSetState(char* name, bool state_up)
670 {
671 #ifdef UNIX_LINUX
672 	UNIX_VLAN_LIST *t, tt;
673 	struct ifreq ifr;
674 	int s;
675 	char eth_name[MAX_SIZE];
676 
677 	LockList(unix_vlan);
678 	{
679 		int result;
680 		// Find a device with the same name
681 		Zero(&tt, sizeof(tt));
682 		StrCpy(tt.Name, sizeof(tt.Name), name);
683 
684 		t = Search(unix_vlan, &tt);
685 		if (t == NULL)
686 		{
687 			// No such device
688 			UnlockList(unix_vlan);
689 			return false;
690 		}
691 
692 		GenerateTunName(name, "vpn", eth_name, sizeof(eth_name));
693 		Zero(&ifr, sizeof(ifr));
694 		StrCpy(ifr.ifr_name, sizeof(ifr.ifr_name), eth_name);
695 
696 		s = socket(AF_INET, SOCK_DGRAM, 0);
697 		if (s == -1)
698 		{
699 			// Failed to create socket
700 			UnlockList(unix_vlan);
701 			return false;
702 		}
703 
704 		ioctl(s, SIOCGIFFLAGS, &ifr);
705 		if (state_up)
706 		{
707 			ifr.ifr_flags |= IFF_UP;
708 		}
709 		else
710 		{
711 			ifr.ifr_flags &= ~IFF_UP;
712 		}
713 		result = ioctl(s, SIOCSIFFLAGS, &ifr);
714 		close(s);
715 	}
716 	UnlockList(unix_vlan);
717 #endif // UNIX_LINUX
718 
719 	return true;
720 }
721 
722 // Enumerate VLANs
UnixVLanEnum()723 TOKEN_LIST *UnixVLanEnum()
724 {
725 	TOKEN_LIST *ret;
726 	UINT i;
727 	if (unix_vlan == NULL)
728 	{
729 		return NullToken();
730 	}
731 
732 	ret = ZeroMalloc(sizeof(TOKEN_LIST));
733 
734 	LockList(unix_vlan);
735 	{
736 		ret->NumTokens = LIST_NUM(unix_vlan);
737 		ret->Token = ZeroMalloc(sizeof(char *) * ret->NumTokens);
738 
739 		for (i = 0;i < ret->NumTokens;i++)
740 		{
741 			UNIX_VLAN_LIST *t = LIST_DATA(unix_vlan, i);
742 
743 			ret->Token[i] = CopyStr(t->Name);
744 		}
745 	}
746 	UnlockList(unix_vlan);
747 
748 	return ret;
749 }
750 
751 // Delete the VLAN
UnixVLanDelete(char * name)752 void UnixVLanDelete(char *name)
753 {
754 	// Validate arguments
755 	if (name == NULL || unix_vlan == NULL)
756 	{
757 		return;
758 	}
759 
760 	LockList(unix_vlan);
761 	{
762 		UINT i;
763 		UNIX_VLAN_LIST *t, tt;
764 
765 		Zero(&tt, sizeof(tt));
766 		StrCpy(tt.Name, sizeof(tt.Name), name);
767 
768 		t = Search(unix_vlan, &tt);
769 		if (t != NULL)
770 		{
771 			UnixCloseTapDevice(t->fd);
772 			Delete(unix_vlan, t);
773 			Free(t);
774 		}
775 	}
776 	UnlockList(unix_vlan);
777 }
778 
779 // Get the VLAN
UnixVLanGet(char * name)780 int UnixVLanGet(char *name)
781 {
782 	int fd = -1;
783 	// Validate arguments
784 	if (name == NULL || unix_vlan == NULL)
785 	{
786 		return -1;
787 	}
788 
789 	LockList(unix_vlan);
790 	{
791 		UINT i;
792 		UNIX_VLAN_LIST *t, tt;
793 
794 		Zero(&tt, sizeof(tt));
795 		StrCpy(tt.Name, sizeof(tt.Name), name);
796 
797 		t = Search(unix_vlan, &tt);
798 		if (t != NULL)
799 		{
800 			fd = t->fd;
801 		}
802 	}
803 	UnlockList(unix_vlan);
804 
805 	return fd;
806 }
807 
808 // Release the VLAN list
UnixVLanFree()809 void UnixVLanFree()
810 {
811 	UINT i;
812 
813 	for (i = 0;i < LIST_NUM(unix_vlan);i++)
814 	{
815 		UNIX_VLAN_LIST *t = LIST_DATA(unix_vlan, i);
816 
817 		UnixCloseTapDevice(t->fd);
818 		Free(t);
819 	}
820 
821 	ReleaseList(unix_vlan);
822 	unix_vlan = NULL;
823 }
824 
825 #endif
826