1 /*
2  *  OpenVPN -- An application to securely tunnel IP networks
3  *             over a single TCP/UDP port, with support for SSL/TLS-based
4  *             session authentication and key exchange,
5  *             packet encryption, packet authentication, and
6  *             packet compression.
7  *
8  *  Copyright (C) 2002-2022 OpenVPN Inc <sales@openvpn.net>
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License version 2
12  *  as published by the Free Software Foundation.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License along
20  *  with this program; if not, write to the Free Software Foundation, Inc.,
21  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23 
24 #ifndef TUN_H
25 #define TUN_H
26 
27 #ifdef _WIN32
28 #include <winioctl.h>
29 #include <tap-windows.h>
30 #include <setupapi.h>
31 #include <cfgmgr32.h>
32 #endif
33 
34 #include "buffer.h"
35 #include "error.h"
36 #include "mtu.h"
37 #include "win32.h"
38 #include "event.h"
39 #include "proto.h"
40 #include "misc.h"
41 #include "networking.h"
42 #include "ring_buffer.h"
43 
44 #ifdef _WIN32
45 #define WINTUN_COMPONENT_ID "wintun"
46 
47 enum windows_driver_type {
48     WINDOWS_DRIVER_UNSPECIFIED,
49     WINDOWS_DRIVER_TAP_WINDOWS6,
50     WINDOWS_DRIVER_WINTUN
51 };
52 #endif
53 
54 #if defined(_WIN32) || defined(TARGET_ANDROID)
55 
56 #define TUN_ADAPTER_INDEX_INVALID ((DWORD)-1)
57 
58 /* time constants for --ip-win32 adaptive */
59 #define IPW32_SET_ADAPTIVE_DELAY_WINDOW 300
60 #define IPW32_SET_ADAPTIVE_TRY_NETSH    20
61 
62 struct tuntap_options {
63     /* --ip-win32 options */
64     bool ip_win32_defined;
65 
66 #define IPW32_SET_MANUAL       0   /* "--ip-win32 manual" */
67 #define IPW32_SET_NETSH        1   /* "--ip-win32 netsh" */
68 #define IPW32_SET_IPAPI        2   /* "--ip-win32 ipapi" */
69 #define IPW32_SET_DHCP_MASQ    3   /* "--ip-win32 dynamic" */
70 #define IPW32_SET_ADAPTIVE     4   /* "--ip-win32 adaptive" */
71 #define IPW32_SET_N            5
72     int ip_win32_type;
73 
74 #ifdef _WIN32
75     HANDLE msg_channel;
76 #endif
77 
78     /* --ip-win32 dynamic options */
79     bool dhcp_masq_custom_offset;
80     int dhcp_masq_offset;
81     int dhcp_lease_time;
82 
83     /* --tap-sleep option */
84     int tap_sleep;
85 
86     /* --dhcp-option options */
87 
88     bool dhcp_options;
89 
90     const char *domain;      /* DOMAIN (15) */
91 
92     const char *netbios_scope; /* NBS (47) */
93 
94     int netbios_node_type;   /* NBT 1,2,4,8 (46) */
95 
96 #define N_DHCP_ADDR 4        /* Max # of addresses allowed for
97                               * DNS, WINS, etc. */
98 
99     /* DNS (6) */
100     in_addr_t dns[N_DHCP_ADDR];
101     int dns_len;
102 
103     /* WINS (44) */
104     in_addr_t wins[N_DHCP_ADDR];
105     int wins_len;
106 
107     /* NTP (42) */
108     in_addr_t ntp[N_DHCP_ADDR];
109     int ntp_len;
110 
111     /* NBDD (45) */
112     in_addr_t nbdd[N_DHCP_ADDR];
113     int nbdd_len;
114 
115 #define N_SEARCH_LIST_LEN 10 /* Max # of entries in domin-search list */
116 
117     /* SEARCH (119), MacOS, Linux, Win10 1809+ */
118     const char *domain_search_list[N_SEARCH_LIST_LEN];
119     int domain_search_list_len;
120 
121     /* DISABLE_NBT (43, Vendor option 001) */
122     bool disable_nbt;
123 
124     bool dhcp_renew;
125     bool dhcp_pre_release;
126 
127     bool register_dns;
128 
129     struct in6_addr dns6[N_DHCP_ADDR];
130     int dns6_len;
131 };
132 
133 #elif TARGET_LINUX
134 
135 struct tuntap_options {
136     int txqueuelen;
137 };
138 
139 #else  /* if defined(_WIN32) || defined(TARGET_ANDROID) */
140 
141 struct tuntap_options {
142     int dummy; /* not used */
143 };
144 
145 #endif /* if defined(_WIN32) || defined(TARGET_ANDROID) */
146 
147 /*
148  * Define a TUN/TAP dev.
149  */
150 
151 struct tuntap
152 {
153 #define TUNNEL_TYPE(tt) ((tt) ? ((tt)->type) : DEV_TYPE_UNDEF)
154     int type; /* DEV_TYPE_x as defined in proto.h */
155 
156 #define TUNNEL_TOPOLOGY(tt) ((tt) ? ((tt)->topology) : TOP_UNDEF)
157     int topology; /* one of the TOP_x values */
158 
159     bool did_ifconfig_setup;
160     bool did_ifconfig_ipv6_setup;
161 
162     bool persistent_if;         /* if existed before, keep on program end */
163 
164     struct tuntap_options options; /* options set on command line */
165 
166     char *actual_name; /* actual name of TUN/TAP dev, usually including unit number */
167 
168     /* number of TX buffers */
169     int txqueuelen;
170 
171     /* ifconfig parameters */
172     in_addr_t local;
173     in_addr_t remote_netmask;
174 
175     struct in6_addr local_ipv6;
176     struct in6_addr remote_ipv6;
177     int netbits_ipv6;
178 
179 #ifdef _WIN32
180     HANDLE hand;
181     struct overlapped_io reads;
182     struct overlapped_io writes;
183     struct rw_handle rw_handle;
184 
185     /* used for setting interface address via IP Helper API
186      * or DHCP masquerade */
187     bool ipapi_context_defined;
188     ULONG ipapi_context;
189     ULONG ipapi_instance;
190     in_addr_t adapter_netmask;
191 
192     /* Windows adapter index for TAP-Windows adapter,
193      * ~0 if undefined */
194     DWORD adapter_index;
195 
196     enum windows_driver_type windows_driver;
197     int standby_iter;
198 
199     HANDLE wintun_send_ring_handle;
200     HANDLE wintun_receive_ring_handle;
201     struct tun_ring *wintun_send_ring;
202     struct tun_ring *wintun_receive_ring;
203 #else  /* ifdef _WIN32 */
204     int fd; /* file descriptor for TUN/TAP dev */
205 #endif /* ifdef _WIN32 */
206 
207 #ifdef TARGET_SOLARIS
208     int ip_fd;
209 #endif
210 
211 #ifdef HAVE_NET_IF_UTUN_H
212     bool is_utun;
213 #endif
214     /* used for printing status info only */
215     unsigned int rwflags_debug;
216 
217     /* Some TUN/TAP drivers like to be ioctled for mtu
218      * after open */
219     int post_open_mtu;
220 };
221 
222 static inline bool
tuntap_defined(const struct tuntap * tt)223 tuntap_defined(const struct tuntap *tt)
224 {
225 #ifdef _WIN32
226     return tt && tt->hand != NULL;
227 #else
228     return tt && tt->fd >= 0;
229 #endif
230 }
231 
232 #ifdef _WIN32
233 static inline bool
tuntap_is_wintun(struct tuntap * tt)234 tuntap_is_wintun(struct tuntap *tt)
235 {
236     return tt && tt->windows_driver == WINDOWS_DRIVER_WINTUN;
237 }
238 
239 static inline bool
tuntap_ring_empty(struct tuntap * tt)240 tuntap_ring_empty(struct tuntap *tt)
241 {
242     return tuntap_is_wintun(tt) && (tt->wintun_send_ring->head == tt->wintun_send_ring->tail);
243 }
244 #endif
245 
246 /*
247  * Function prototypes
248  */
249 
250 void open_tun(const char *dev, const char *dev_type, const char *dev_node,
251               struct tuntap *tt);
252 
253 void close_tun(struct tuntap *tt, openvpn_net_ctx_t *ctx);
254 
255 int write_tun(struct tuntap *tt, uint8_t *buf, int len);
256 
257 int read_tun(struct tuntap *tt, uint8_t *buf, int len);
258 
259 void tuncfg(const char *dev, const char *dev_type, const char *dev_node,
260             int persist_mode, const char *username,
261             const char *groupname, const struct tuntap_options *options,
262             openvpn_net_ctx_t *ctx);
263 
264 const char *guess_tuntap_dev(const char *dev,
265                              const char *dev_type,
266                              const char *dev_node,
267                              struct gc_arena *gc);
268 
269 struct tuntap *init_tun(const char *dev,        /* --dev option */
270                         const char *dev_type,   /* --dev-type option */
271                         int topology,           /* one of the TOP_x values */
272                         const char *ifconfig_local_parm,           /* --ifconfig parm 1 */
273                         const char *ifconfig_remote_netmask_parm,  /* --ifconfig parm 2 */
274                         const char *ifconfig_ipv6_local_parm,      /* --ifconfig parm 1 / IPv6 */
275                         int ifconfig_ipv6_netbits_parm,            /* --ifconfig parm 1 / bits */
276                         const char *ifconfig_ipv6_remote_parm,     /* --ifconfig parm 2 / IPv6 */
277                         struct addrinfo *local_public,
278                         struct addrinfo *remote_public,
279                         const bool strict_warn,
280                         struct env_set *es,
281                         openvpn_net_ctx_t *ctx);
282 
283 void init_tun_post(struct tuntap *tt,
284                    const struct frame *frame,
285                    const struct tuntap_options *options);
286 
287 void do_ifconfig_setenv(const struct tuntap *tt,
288                         struct env_set *es);
289 
290 /**
291  * do_ifconfig - configure the tunnel interface
292  *
293  * @param tt        the tuntap interface context
294  * @param ifname    the human readable interface name
295  * @param mtu       the MTU value to set the interface to
296  * @param es        the environment to be used when executing the commands
297  * @param ctx       the networking API opaque context
298  */
299 void do_ifconfig(struct tuntap *tt, const char *ifname, int tun_mtu,
300                  const struct env_set *es, openvpn_net_ctx_t *ctx);
301 
302 bool is_dev_type(const char *dev, const char *dev_type, const char *match_type);
303 
304 int dev_type_enum(const char *dev, const char *dev_type);
305 
306 const char *dev_type_string(const char *dev, const char *dev_type);
307 
308 const char *ifconfig_options_string(const struct tuntap *tt, bool remote, bool disable, struct gc_arena *gc);
309 
310 bool is_tun_p2p(const struct tuntap *tt);
311 
312 void check_subnet_conflict(const in_addr_t ip,
313                            const in_addr_t netmask,
314                            const char *prefix);
315 
316 void warn_on_use_of_common_subnets(openvpn_net_ctx_t *ctx);
317 
318 /*
319  * Inline functions
320  */
321 
322 static inline void
tun_adjust_frame_parameters(struct frame * frame,int size)323 tun_adjust_frame_parameters(struct frame *frame, int size)
324 {
325     frame_add_to_extra_tun(frame, size);
326 }
327 
328 /*
329  * Should ifconfig be called before or after
330  * tun dev open?
331  */
332 
333 #define IFCONFIG_BEFORE_TUN_OPEN 0
334 #define IFCONFIG_AFTER_TUN_OPEN  1
335 
336 #define IFCONFIG_DEFAULT         IFCONFIG_AFTER_TUN_OPEN
337 
338 static inline int
ifconfig_order(void)339 ifconfig_order(void)
340 {
341 #if defined(TARGET_LINUX)
342     return IFCONFIG_AFTER_TUN_OPEN;
343 #elif defined(TARGET_SOLARIS)
344     return IFCONFIG_AFTER_TUN_OPEN;
345 #elif defined(TARGET_OPENBSD)
346     return IFCONFIG_AFTER_TUN_OPEN;
347 #elif defined(TARGET_DARWIN)
348     return IFCONFIG_AFTER_TUN_OPEN;
349 #elif defined(TARGET_NETBSD)
350     return IFCONFIG_AFTER_TUN_OPEN;
351 #elif defined(_WIN32)
352     return IFCONFIG_AFTER_TUN_OPEN;
353 #elif defined(TARGET_ANDROID)
354     return IFCONFIG_BEFORE_TUN_OPEN;
355 #else  /* if defined(TARGET_LINUX) */
356     return IFCONFIG_DEFAULT;
357 #endif
358 }
359 
360 #define ROUTE_BEFORE_TUN 0
361 #define ROUTE_AFTER_TUN 1
362 #define ROUTE_ORDER_DEFAULT ROUTE_AFTER_TUN
363 
364 static inline int
route_order(void)365 route_order(void)
366 {
367 #if defined(TARGET_ANDROID)
368     return ROUTE_BEFORE_TUN;
369 #else
370     return ROUTE_ORDER_DEFAULT;
371 #endif
372 }
373 
374 
375 #ifdef _WIN32
376 
377 struct tap_reg
378 {
379     const char *guid;
380     enum windows_driver_type windows_driver;
381     struct tap_reg *next;
382 };
383 
384 struct panel_reg
385 {
386     const char *name;
387     const char *guid;
388     struct panel_reg *next;
389 };
390 
391 struct device_instance_id_interface
392 {
393     const char *net_cfg_instance_id;
394     const char *device_interface_list;
395     struct device_instance_id_interface *next;
396 };
397 
398 int ascii2ipset(const char *name);
399 
400 const char *ipset2ascii(int index);
401 
402 const char *ipset2ascii_all(struct gc_arena *gc);
403 
404 void verify_255_255_255_252(in_addr_t local, in_addr_t remote);
405 
406 const IP_ADAPTER_INFO *get_adapter_info_list(struct gc_arena *gc);
407 
408 const IP_ADAPTER_INFO *get_tun_adapter(const struct tuntap *tt, const IP_ADAPTER_INFO *list);
409 
410 const IP_ADAPTER_INFO *get_adapter_info(DWORD index, struct gc_arena *gc);
411 
412 const IP_PER_ADAPTER_INFO *get_per_adapter_info(const DWORD index, struct gc_arena *gc);
413 
414 const IP_ADAPTER_INFO *get_adapter(const IP_ADAPTER_INFO *ai, DWORD index);
415 
416 bool is_adapter_up(const struct tuntap *tt, const IP_ADAPTER_INFO *list);
417 
418 bool is_ip_in_adapter_subnet(const IP_ADAPTER_INFO *ai, const in_addr_t ip, in_addr_t *highest_netmask);
419 
420 DWORD adapter_index_of_ip(const IP_ADAPTER_INFO *list,
421                           const in_addr_t ip,
422                           int *count,
423                           in_addr_t *netmask);
424 
425 void show_tap_win_adapters(int msglev, int warnlev);
426 
427 void show_adapters(int msglev);
428 
429 void tap_allow_nonadmin_access(const char *dev_node);
430 
431 void show_valid_win32_tun_subnets(void);
432 
433 const char *tap_win_getinfo(const struct tuntap *tt, struct gc_arena *gc);
434 
435 void tun_show_debug(struct tuntap *tt);
436 
437 bool dhcp_release_by_adapter_index(const DWORD adapter_index);
438 
439 bool dhcp_renew_by_adapter_index(const DWORD adapter_index);
440 
441 void fork_register_dns_action(struct tuntap *tt);
442 
443 void ipconfig_register_dns(const struct env_set *es);
444 
445 void tun_standby_init(struct tuntap *tt);
446 
447 bool tun_standby(struct tuntap *tt);
448 
449 int tun_read_queue(struct tuntap *tt, int maxsize);
450 
451 int tun_write_queue(struct tuntap *tt, struct buffer *buf);
452 
453 int tun_finalize(HANDLE h, struct overlapped_io *io, struct buffer *buf);
454 
455 static inline bool
tuntap_stop(int status)456 tuntap_stop(int status)
457 {
458     /*
459      * This corresponds to the STATUS_NO_SUCH_DEVICE
460      * error in tapdrvr.c.
461      */
462     if (status < 0)
463     {
464         return openvpn_errno() == ERROR_FILE_NOT_FOUND;
465     }
466     return false;
467 }
468 
469 static inline bool
tuntap_abort(int status)470 tuntap_abort(int status)
471 {
472     /*
473      * Typically generated when driver is halted.
474      */
475     if (status < 0)
476     {
477         return openvpn_errno() == ERROR_OPERATION_ABORTED;
478     }
479     return false;
480 }
481 
482 static inline int
tun_write_win32(struct tuntap * tt,struct buffer * buf)483 tun_write_win32(struct tuntap *tt, struct buffer *buf)
484 {
485     int err = 0;
486     int status = 0;
487     if (overlapped_io_active(&tt->writes))
488     {
489         status = tun_finalize(tt->hand, &tt->writes, NULL);
490         if (status < 0)
491         {
492             err = GetLastError();
493         }
494     }
495     tun_write_queue(tt, buf);
496     if (status < 0)
497     {
498         SetLastError(err);
499         return status;
500     }
501     else
502     {
503         return BLEN(buf);
504     }
505 }
506 
507 static inline int
read_tun_buffered(struct tuntap * tt,struct buffer * buf)508 read_tun_buffered(struct tuntap *tt, struct buffer *buf)
509 {
510     return tun_finalize(tt->hand, &tt->reads, buf);
511 }
512 
513 static inline ULONG
wintun_ring_packet_align(ULONG size)514 wintun_ring_packet_align(ULONG size)
515 {
516     return (size + (WINTUN_PACKET_ALIGN - 1)) & ~(WINTUN_PACKET_ALIGN - 1);
517 }
518 
519 static inline ULONG
wintun_ring_wrap(ULONG value)520 wintun_ring_wrap(ULONG value)
521 {
522     return value & (WINTUN_RING_CAPACITY - 1);
523 }
524 
525 static inline void
read_wintun(struct tuntap * tt,struct buffer * buf)526 read_wintun(struct tuntap *tt, struct buffer *buf)
527 {
528     struct tun_ring *ring = tt->wintun_send_ring;
529     ULONG head = ring->head;
530     ULONG tail = ring->tail;
531     ULONG content_len;
532     struct TUN_PACKET *packet;
533     ULONG aligned_packet_size;
534 
535     *buf = tt->reads.buf_init;
536     buf->len = 0;
537 
538     if ((head >= WINTUN_RING_CAPACITY) || (tail >= WINTUN_RING_CAPACITY))
539     {
540         msg(M_INFO, "Wintun: ring capacity exceeded");
541         buf->len = -1;
542         return;
543     }
544 
545     if (head == tail)
546     {
547         /* nothing to read */
548         return;
549     }
550 
551     content_len = wintun_ring_wrap(tail - head);
552     if (content_len < sizeof(struct TUN_PACKET_HEADER))
553     {
554         msg(M_INFO, "Wintun: incomplete packet header in send ring");
555         buf->len = -1;
556         return;
557     }
558 
559     packet = (struct TUN_PACKET *) &ring->data[head];
560     if (packet->size > WINTUN_MAX_PACKET_SIZE)
561     {
562         msg(M_INFO, "Wintun: packet too big in send ring");
563         buf->len = -1;
564         return;
565     }
566 
567     aligned_packet_size = wintun_ring_packet_align(sizeof(struct TUN_PACKET_HEADER) + packet->size);
568     if (aligned_packet_size > content_len)
569     {
570         msg(M_INFO, "Wintun: incomplete packet in send ring");
571         buf->len = -1;
572         return;
573     }
574 
575     buf_write(buf, packet->data, packet->size);
576 
577     head = wintun_ring_wrap(head + aligned_packet_size);
578     ring->head = head;
579 }
580 
581 static inline bool
is_ip_packet_valid(const struct buffer * buf)582 is_ip_packet_valid(const struct buffer *buf)
583 {
584     const struct openvpn_iphdr *ih = (const struct openvpn_iphdr *)BPTR(buf);
585 
586     if (OPENVPN_IPH_GET_VER(ih->version_len) == 4)
587     {
588         if (BLEN(buf) < sizeof(struct openvpn_iphdr))
589         {
590             return false;
591         }
592     }
593     else if (OPENVPN_IPH_GET_VER(ih->version_len) == 6)
594     {
595         if (BLEN(buf) < sizeof(struct openvpn_ipv6hdr))
596         {
597             return false;
598         }
599     }
600     else
601     {
602         return false;
603     }
604 
605     return true;
606 }
607 
608 static inline int
write_wintun(struct tuntap * tt,struct buffer * buf)609 write_wintun(struct tuntap *tt, struct buffer *buf)
610 {
611     struct tun_ring *ring = tt->wintun_receive_ring;
612     ULONG head = ring->head;
613     ULONG tail = ring->tail;
614     ULONG aligned_packet_size;
615     ULONG buf_space;
616     struct TUN_PACKET *packet;
617 
618     /* wintun marks ring as corrupted (overcapacity) if it receives invalid IP packet */
619     if (!is_ip_packet_valid(buf))
620     {
621         msg(D_LOW, "write_wintun(): drop invalid IP packet");
622         return 0;
623     }
624 
625     if ((head >= WINTUN_RING_CAPACITY) || (tail >= WINTUN_RING_CAPACITY))
626     {
627         msg(M_INFO, "write_wintun(): head/tail value is over capacity");
628         return -1;
629     }
630 
631     aligned_packet_size = wintun_ring_packet_align(sizeof(struct TUN_PACKET_HEADER) + BLEN(buf));
632     buf_space = wintun_ring_wrap(head - tail - WINTUN_PACKET_ALIGN);
633     if (aligned_packet_size > buf_space)
634     {
635         msg(M_INFO, "write_wintun(): ring is full");
636         return 0;
637     }
638 
639     /* copy packet size and data into ring */
640     packet = (struct TUN_PACKET * )&ring->data[tail];
641     packet->size = BLEN(buf);
642     memcpy(packet->data, BPTR(buf), BLEN(buf));
643 
644     /* move ring tail */
645     ring->tail = wintun_ring_wrap(tail + aligned_packet_size);
646     if (ring->alertable != 0)
647     {
648         SetEvent(tt->rw_handle.write);
649     }
650 
651     return BLEN(buf);
652 }
653 
654 static inline int
write_tun_buffered(struct tuntap * tt,struct buffer * buf)655 write_tun_buffered(struct tuntap *tt, struct buffer *buf)
656 {
657     if (tt->windows_driver == WINDOWS_DRIVER_WINTUN)
658     {
659         return write_wintun(tt, buf);
660     }
661     else
662     {
663         return tun_write_win32(tt, buf);
664     }
665 }
666 
667 #else  /* ifdef _WIN32 */
668 
669 static inline bool
tuntap_stop(int status)670 tuntap_stop(int status)
671 {
672     return false;
673 }
674 
675 static inline bool
tuntap_abort(int status)676 tuntap_abort(int status)
677 {
678     return false;
679 }
680 
681 static inline void
tun_standby_init(struct tuntap * tt)682 tun_standby_init(struct tuntap *tt)
683 {
684 }
685 
686 static inline bool
tun_standby(struct tuntap * tt)687 tun_standby(struct tuntap *tt)
688 {
689     return true;
690 }
691 
692 #endif /* ifdef _WIN32 */
693 
694 /*
695  * TUN/TAP I/O wait functions
696  */
697 
698 static inline event_t
tun_event_handle(const struct tuntap * tt)699 tun_event_handle(const struct tuntap *tt)
700 {
701 #ifdef _WIN32
702     return &tt->rw_handle;
703 #else
704     return tt->fd;
705 #endif
706 }
707 
708 static inline void
tun_set(struct tuntap * tt,struct event_set * es,unsigned int rwflags,void * arg,unsigned int * persistent)709 tun_set(struct tuntap *tt,
710         struct event_set *es,
711         unsigned int rwflags,
712         void *arg,
713         unsigned int *persistent)
714 {
715     if (tuntap_defined(tt))
716     {
717         /* if persistent is defined, call event_ctl only if rwflags has changed since last call */
718         if (!persistent || *persistent != rwflags)
719         {
720             event_ctl(es, tun_event_handle(tt), rwflags, arg);
721             if (persistent)
722             {
723                 *persistent = rwflags;
724             }
725         }
726 #ifdef _WIN32
727         if (tt->windows_driver == WINDOWS_DRIVER_TAP_WINDOWS6 && (rwflags & EVENT_READ))
728         {
729             tun_read_queue(tt, 0);
730         }
731 #endif
732         tt->rwflags_debug = rwflags;
733     }
734 }
735 
736 const char *tun_stat(const struct tuntap *tt, unsigned int rwflags, struct gc_arena *gc);
737 
738 #endif /* TUN_H */
739