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