1612c05bdSAlexander von Gluck IV/*
2612c05bdSAlexander von Gluck IV * /dev/config/tun network tunnel driver for BeOS
3612c05bdSAlexander von Gluck IV * (c) 2003, mmu_man, revol@free.fr
4612c05bdSAlexander von Gluck IV * licenced under MIT licence.
5612c05bdSAlexander von Gluck IV */
6b110fce1SFrançois Revol#include <Drivers.h>
7b110fce1SFrançois Revol#include <KernelExport.h>
8b110fce1SFrançois Revol#include <OS.h>
9b110fce1SFrançois Revol#include <stdlib.h>
10b110fce1SFrançois Revol#include <string.h>
11b110fce1SFrançois Revol#include <sys/param.h>
12b110fce1SFrançois Revol#include <sys/types.h>
13b110fce1SFrançois Revol#include <unistd.h>
14b110fce1SFrançois Revol#include <fcntl.h>
15b110fce1SFrançois Revol#include <fsproto.h>
16b110fce1SFrançois Revol#include "bone_tun.h"
17b110fce1SFrançois Revol
18b110fce1SFrançois Revol
19612c05bdSAlexander von Gluck IVconst char * device_names[] = {TUN_DRIVER_NAME, NULL};
20b110fce1SFrançois Revolextern device_hooks tun_hooks;
21b110fce1SFrançois Revol
22b110fce1SFrançois Revolint32 api_version = B_CUR_DRIVER_API_VERSION;
23b110fce1SFrançois Revol
24b110fce1SFrançois Revolvint32 if_mod_ref_count = 0;
25b110fce1SFrançois Revolbone_tun_interface_info_t *gIfaceModule = NULL;
26b110fce1SFrançois Revolbone_util_info_t *gUtil = NULL;
27b110fce1SFrançois Revol
28612c05bdSAlexander von Gluck IV
29612c05bdSAlexander von Gluck IVstatus_t
30612c05bdSAlexander von Gluck IVinit_hardware(void)
31612c05bdSAlexander von Gluck IV{
32b110fce1SFrançois Revol	dprintf("tun:init_hardware()\n");
33b110fce1SFrançois Revol	return B_OK;
34b110fce1SFrançois Revol}
35b110fce1SFrançois Revol
36612c05bdSAlexander von Gluck IV
37612c05bdSAlexander von Gluck IVstatus_t
38612c05bdSAlexander von Gluck IVinit_driver(void)
39612c05bdSAlexander von Gluck IV{
40b110fce1SFrançois Revol	dprintf("tun:init_driver()\n");
41b110fce1SFrançois Revol	return B_OK;
42b110fce1SFrançois Revol}
43b110fce1SFrançois Revol
44612c05bdSAlexander von Gluck IV
45612c05bdSAlexander von Gluck IVvoid
46612c05bdSAlexander von Gluck IVuninit_driver(void)
47612c05bdSAlexander von Gluck IV{
48b110fce1SFrançois Revol	dprintf("tun:uninit_driver()\n");
49b110fce1SFrançois Revol}
50b110fce1SFrançois Revol
51612c05bdSAlexander von Gluck IV
52612c05bdSAlexander von Gluck IVconst char**
53612c05bdSAlexander von Gluck IVpublish_devices()
54612c05bdSAlexander von Gluck IV{
55b110fce1SFrançois Revol	return device_names;
56b110fce1SFrançois Revol}
57b110fce1SFrançois Revol
58612c05bdSAlexander von Gluck IV
59612c05bdSAlexander von Gluck IVdevice_hooks*
60612c05bdSAlexander von Gluck IVfind_device(const char *name)
61612c05bdSAlexander von Gluck IV{
62b110fce1SFrançois Revol	(void)name;
63b110fce1SFrançois Revol	return &tun_hooks;
64b110fce1SFrançois Revol}
65b110fce1SFrançois Revol
66612c05bdSAlexander von Gluck IV
67612c05bdSAlexander von Gluck IVstatus_t
68612c05bdSAlexander von Gluck IVtun_open(const char *name, uint32 flags, cookie_t **cookie)
69612c05bdSAlexander von Gluck IV{
70b110fce1SFrançois Revol	status_t err = B_OK;
71b110fce1SFrançois Revol	(void)name; (void)flags;
72b110fce1SFrançois Revol	/* XXX: add O_NONBLOCK + FIONBIO */
73b110fce1SFrançois Revol#if DEBUG > 1
74b110fce1SFrançois Revol	dprintf("tun:open(%s, 0x%08lx,)\n", name, flags);
75b110fce1SFrançois Revol#endif
76b110fce1SFrançois Revol	err = get_module(BONE_UTIL_MOD_NAME, (struct module_info **)&gUtil);
77b110fce1SFrançois Revol	if (err < B_OK)
78b110fce1SFrançois Revol		return err;
79b110fce1SFrançois Revol	err = get_module(TUN_INTERFACE_MODULE, (struct module_info **)&gIfaceModule);
80b110fce1SFrançois Revol	if (err < B_OK) {
81b110fce1SFrançois Revol		put_module(BONE_UTIL_MOD_NAME);
82b110fce1SFrançois Revol		return err;
83b110fce1SFrançois Revol	}
84b110fce1SFrançois Revol
85b110fce1SFrançois Revol	/* XXX: FIXME, still not ok (rescan) */
86b110fce1SFrançois Revol	if (atomic_add(&if_mod_ref_count, 1) < 1) /* force one more open to keep loaded */
87b110fce1SFrançois Revol		get_module(TUN_INTERFACE_MODULE, (struct module_info **)&gIfaceModule);
88b110fce1SFrançois Revol
89b110fce1SFrançois Revol	*cookie = (void*)malloc(sizeof(cookie_t));
90b110fce1SFrançois Revol	if (*cookie == NULL) {
91b110fce1SFrançois Revol		dprintf("tun_open : error allocating cookie\n");
92b110fce1SFrançois Revol		goto err0;
93b110fce1SFrançois Revol	}
94b110fce1SFrançois Revol	memset(*cookie, 0, sizeof(cookie_t));
95b110fce1SFrançois Revol	(*cookie)->blocking_io = true;
96b110fce1SFrançois Revol	return B_OK;
97b110fce1SFrançois Revol
98b110fce1SFrançois Revolerr1:
99b110fce1SFrançois Revol	dprintf("tun_open : cleanup : will free cookie\n");
100b110fce1SFrançois Revol	free(*cookie);
101b110fce1SFrançois Revol	*cookie = NULL;
102b110fce1SFrançois Revol	put_module(TUN_INTERFACE_MODULE);
103b110fce1SFrançois Revol	put_module(BONE_UTIL_MOD_NAME);
104b110fce1SFrançois Revolerr0:
105b110fce1SFrançois Revol	return B_ERROR;
106b110fce1SFrançois Revol}
107b110fce1SFrançois Revol
108612c05bdSAlexander von Gluck IV
109612c05bdSAlexander von Gluck IVstatus_t
110612c05bdSAlexander von Gluck IVtun_close(void *cookie)
111612c05bdSAlexander von Gluck IV{
112b110fce1SFrançois Revol	(void)cookie;
113b110fce1SFrançois Revol	return B_OK;
114b110fce1SFrançois Revol}
115b110fce1SFrançois Revol
116612c05bdSAlexander von Gluck IV
117612c05bdSAlexander von Gluck IVstatus_t
118612c05bdSAlexander von Gluck IVtun_free(cookie_t *cookie)
119612c05bdSAlexander von Gluck IV{
120b110fce1SFrançois Revol	status_t err = B_OK;
121b110fce1SFrançois Revol#if DEBUG > 1
122b110fce1SFrançois Revol	dprintf("tun_close()\n");
123b110fce1SFrançois Revol#endif
124b110fce1SFrançois Revol	if (cookie->iface)
125b110fce1SFrançois Revol		err = gIfaceModule->tun_detach_driver(cookie->iface, true);
126b110fce1SFrançois Revol	free(cookie);
127b110fce1SFrançois Revol	atomic_add(&if_mod_ref_count, -1);
128b110fce1SFrançois Revol	put_module(TUN_INTERFACE_MODULE);
129b110fce1SFrançois Revol	put_module(BONE_UTIL_MOD_NAME);
130b110fce1SFrançois Revol	return err;
131b110fce1SFrançois Revol}
132b110fce1SFrançois Revol
133612c05bdSAlexander von Gluck IV
134612c05bdSAlexander von Gluck IVstatus_t
135612c05bdSAlexander von Gluck IVtun_ioctl(cookie_t *cookie, uint32 op, void *data, size_t len)
136612c05bdSAlexander von Gluck IV{
137b110fce1SFrançois Revol	ifreq_t *ifr;
138b110fce1SFrançois Revol	bone_tun_if_interface_t *iface;
139b110fce1SFrançois Revol	(void)cookie; (void)op; (void)data; (void)len;
140b110fce1SFrançois Revol	iface = cookie->iface;
141b110fce1SFrançois Revol#if DEBUG > 1
142b110fce1SFrançois Revol	dprintf("tun_ioctl(%d(0x%08lx), , %d)\n", op, op, len);
143b110fce1SFrançois Revol#endif
144b110fce1SFrançois Revol
145b110fce1SFrançois Revol	switch (op) {
146b110fce1SFrançois Revol	case B_SET_NONBLOCKING_IO:
147b110fce1SFrançois Revol		cookie->blocking_io = false;
148b110fce1SFrançois Revol		return B_OK;
149b110fce1SFrançois Revol	case B_SET_BLOCKING_IO:
150b110fce1SFrançois Revol		cookie->blocking_io = true;
151b110fce1SFrançois Revol		return B_OK;
152b110fce1SFrançois Revol	case TUNSETNOCSUM:
153b110fce1SFrançois Revol		return B_OK;//EOPNOTSUPP;
154b110fce1SFrançois Revol	case TUNSETDEBUG:
155b110fce1SFrançois Revol		return B_OK;//EOPNOTSUPP;
156b110fce1SFrançois Revol	case TUNSETIFF:
157b110fce1SFrançois Revol		if (data == NULL)
158b110fce1SFrançois Revol			return EINVAL;
159b110fce1SFrançois Revol		ifr = (ifreq_t *)data;
160b110fce1SFrançois Revol
161b110fce1SFrançois Revol		iface = gIfaceModule->tun_reuse_or_create(ifr, cookie);
162b110fce1SFrançois Revol		if (iface != NULL) {
163b110fce1SFrançois Revol			dprintf("tun: new tunnel created: %s, flags: 0x%08lx\n", ifr->ifr_name, iface->flags);
164b110fce1SFrançois Revol			return B_OK;
165b110fce1SFrançois Revol		} else
166b110fce1SFrançois Revol			dprintf("tun: can't allocate a new tunnel!\n");
167b110fce1SFrançois Revol		break;
168b110fce1SFrançois Revol
169b110fce1SFrançois Revol	case SIOCGIFHWADDR:
170b110fce1SFrançois Revol		if (data == NULL)
171b110fce1SFrançois Revol			return EINVAL;
172b110fce1SFrançois Revol		ifr = (ifreq_t *)data;
173b110fce1SFrançois Revol		if (iface == NULL)
174b110fce1SFrançois Revol			return EINVAL;
175b110fce1SFrançois Revol		if (strncmp(ifr->ifr_name, iface->ifn->if_name, IFNAMSIZ) != 0)
176b110fce1SFrançois Revol			return EINVAL;
177b110fce1SFrançois Revol		memcpy(ifr->ifr_hwaddr, iface->fakemac.octet, 6);
178b110fce1SFrançois Revol		return B_OK;
179b110fce1SFrançois Revol	case SIOCSIFHWADDR:
180b110fce1SFrançois Revol		if (data == NULL)
181b110fce1SFrançois Revol			return EINVAL;
182b110fce1SFrançois Revol		ifr = (ifreq_t *)data;
183b110fce1SFrançois Revol		if (iface == NULL)
184b110fce1SFrançois Revol			return EINVAL;
185b110fce1SFrançois Revol		if (strncmp(ifr->ifr_name, iface->ifn->if_name, IFNAMSIZ) != 0)
186b110fce1SFrançois Revol			return EINVAL;
187b110fce1SFrançois Revol		memcpy(iface->fakemac.octet, ifr->ifr_hwaddr, 6);
188b110fce1SFrançois Revol		return B_OK;
189b110fce1SFrançois Revol
190b110fce1SFrançois Revol	}
191b110fce1SFrançois Revol	return B_ERROR;
192b110fce1SFrançois Revol}
193b110fce1SFrançois Revol
194612c05bdSAlexander von Gluck IV
195612c05bdSAlexander von Gluck IVstatus_t
196612c05bdSAlexander von Gluck IVtun_read(cookie_t *cookie, off_t position, void *data, size_t *numbytes)
197612c05bdSAlexander von Gluck IV{
198b110fce1SFrançois Revol	bone_data_t *bdata;
199b110fce1SFrançois Revol	uint32 got;
200b110fce1SFrançois Revol	ssize_t pktsize;
201b110fce1SFrançois Revol	if (cookie->iface == NULL)
202b110fce1SFrançois Revol		return EBUSY;
203b110fce1SFrançois Revol
204b110fce1SFrançois Revol	//if ((pktsize = gUtil->dequeue_fifo_data(cookie->iface->wfifo, &bdata, B_INFINITE_TIMEOUT, false)) < 0)
205b110fce1SFrançois Revol	pktsize = gUtil->dequeue_fifo_data(cookie->iface->wfifo, &bdata, cookie->blocking_io?B_INFINITE_TIMEOUT:0LL, false);
206b110fce1SFrançois Revol
207b110fce1SFrançois Revol	if (pktsize < 0) {
208b110fce1SFrançois Revol		*numbytes = 0;
209b110fce1SFrançois Revol		return pktsize;
210b110fce1SFrançois Revol	}
211b110fce1SFrançois Revol#if DEBUG > 2
212b110fce1SFrançois Revol	dprintf("tun: dequeue = %ld, datalen = %ld, \n", pktsize, bdata?bdata->datalen:0);
213b110fce1SFrançois Revol#endif
214b110fce1SFrançois Revol	pktsize = (ssize_t)bdata->datalen;
215b110fce1SFrançois Revol
216b110fce1SFrançois Revol	got = gUtil->copy_from_data(bdata, 0L, data, MIN((bdata->datalen), (*numbytes)));
217b110fce1SFrançois Revol	//got = MIN((*numbytes), bdata->datalen);
218b110fce1SFrançois Revol	if ((pktsize > *numbytes) && !(cookie->iface->flags & TUN_NO_PI))
219b110fce1SFrançois Revol		((struct tun_pi *)data)->flags |= TUN_PKT_STRIP;
220b110fce1SFrançois Revol	gUtil->delete_data(bdata);
221b110fce1SFrançois Revol#if DEBUG > 2
222b110fce1SFrançois Revol	dprintf("tun: read() got %ld bytes, buf is %ld\n", got, *numbytes);
223b110fce1SFrançois Revol	dump_data(bdata);
224b110fce1SFrançois Revol	iovec_dump_data(bdata);
225b110fce1SFrançois Revol#endif
226b110fce1SFrançois Revol	*numbytes = got;
227b110fce1SFrançois Revol	return B_OK;
228b110fce1SFrançois RevolERROR_EOF:
229b110fce1SFrançois Revol	*numbytes = 0;
230b110fce1SFrançois Revol	return B_OK;
231b110fce1SFrançois Revol}
232b110fce1SFrançois Revol
233612c05bdSAlexander von Gluck IV
234612c05bdSAlexander von Gluck IVstatus_t
235612c05bdSAlexander von Gluck IVtun_write(cookie_t *cookie, off_t position, const void *data, size_t *numbytes)
236612c05bdSAlexander von Gluck IV{
237b110fce1SFrançois Revol	bone_data_t *bdata = NULL;
238b110fce1SFrançois Revol	void *buf;
239b110fce1SFrançois Revol	status_t err = B_NO_MEMORY;
240b110fce1SFrançois Revol	(void)position;
241b110fce1SFrançois Revol
242b110fce1SFrançois Revol	/* XXX: max for *numbytes ? */
243b110fce1SFrançois Revol
244b110fce1SFrançois Revol	if (cookie->iface == NULL)
245b110fce1SFrançois Revol		return EBUSY;
246b110fce1SFrançois Revol	bdata = gUtil->new_data();
247b110fce1SFrançois Revol	if (bdata == NULL)
248b110fce1SFrançois Revol		return B_NO_MEMORY;
249b110fce1SFrançois Revol	buf = gUtil->falloc(*numbytes);
250b110fce1SFrançois Revol	if (buf == NULL)
251b110fce1SFrançois Revol		goto ERROR_1;
252b110fce1SFrançois Revol	memcpy(buf, data, *numbytes);
253b110fce1SFrançois Revol
254b110fce1SFrançois Revol	if (gUtil->prepend_data(bdata, buf, *numbytes, gUtil->free) < 0) {
255b110fce1SFrançois Revol		gUtil->free(buf);
256b110fce1SFrançois Revol		goto ERROR_1;
257b110fce1SFrançois Revol	}
258b110fce1SFrançois Revol	if ((err = gUtil->enqueue_fifo_data(cookie->iface->rfifo, bdata)) < B_OK)
259b110fce1SFrançois Revol		goto ERROR_1;
260b110fce1SFrançois Revol	return B_OK;
261b110fce1SFrançois Revol
262b110fce1SFrançois RevolERROR_1:
263b110fce1SFrançois Revol	if (bdata)
264b110fce1SFrançois Revol		gUtil->delete_data(bdata);
265b110fce1SFrançois Revol	return err;
266b110fce1SFrançois Revol}
267b110fce1SFrançois Revol
268612c05bdSAlexander von Gluck IV
269612c05bdSAlexander von Gluck IVstatus_t
270612c05bdSAlexander von Gluck IVtun_select(cookie_t *cookie, uint8 event, uint32 ref, selectsync *sync)
271b110fce1SFrançois Revol{
272b110fce1SFrançois Revol	status_t err = B_OK;
273b110fce1SFrançois Revol#if DEBUG > 1
274b110fce1SFrançois Revol	dprintf("tun: select(, %d, %ld, )\n", event, ref);
275b110fce1SFrançois Revol#endif
276b110fce1SFrançois Revol	if (cookie->iface == NULL)
277b110fce1SFrançois Revol		return B_NO_INIT;
278b110fce1SFrançois Revol	if (event > B_SELECT_EXCEPTION || !event)
279b110fce1SFrançois Revol		return EINVAL;
280b110fce1SFrançois Revol	err = gUtil->lock_benaphore(&cookie->iface->lock);
281b110fce1SFrançois Revol	if (err < B_OK)
282b110fce1SFrançois Revol		return err;
283b110fce1SFrançois Revol	/* iface LOCKED */
284b110fce1SFrançois Revol	if (cookie->iface->sel[event].sync)
285b110fce1SFrançois Revol		err = EALREADY;
286b110fce1SFrançois Revol	else {
287b110fce1SFrançois Revol		bone_fifo_t *fifo = NULL;
288b110fce1SFrançois Revol		switch (event) {
289b110fce1SFrançois Revol		case B_SELECT_READ:
290b110fce1SFrançois Revol			fifo = cookie->iface->wfifo;
291b110fce1SFrançois Revol			break;
292b110fce1SFrançois Revol		case B_SELECT_WRITE:
293b110fce1SFrançois Revol			fifo = cookie->iface->rfifo;
294b110fce1SFrançois Revol			break;
295b110fce1SFrançois Revol		}
296b110fce1SFrançois Revol		if (fifo) {
297b110fce1SFrançois Revol			/* XXX: is it safe ??? shouldn't we dequeue(peek=true) ? */
298b110fce1SFrançois Revol			err = gUtil->lock_benaphore(&fifo->lock);
299b110fce1SFrançois Revol			if (err >= B_OK) {
300b110fce1SFrançois Revol				bool avail;
301b110fce1SFrançois Revol				switch (event) {
302b110fce1SFrançois Revol				case B_SELECT_READ:
303b110fce1SFrançois Revol					avail = (fifo->current_bytes > 1);
304b110fce1SFrançois Revol					break;
305b110fce1SFrançois Revol				case B_SELECT_WRITE:
306b110fce1SFrançois Revol					avail = ((fifo->max_bytes - fifo->current_bytes) > cookie->iface->ifn->if_mtu);
307b110fce1SFrançois Revol					break;
308b110fce1SFrançois Revol				}
309b110fce1SFrançois Revol				/* check if we don't already have the event */
310b110fce1SFrançois Revol				if (avail) {
311b110fce1SFrançois Revol					notify_select_event(sync, ref);
312b110fce1SFrançois Revol				}
313b110fce1SFrançois Revol				else {
314b110fce1SFrançois Revol					cookie->iface->sel[event].sync = sync;
315b110fce1SFrançois Revol					cookie->iface->sel[event].ref = ref;
316b110fce1SFrançois Revol				}
317b110fce1SFrançois Revol				gUtil->unlock_benaphore(&fifo->lock);
318b110fce1SFrançois Revol			}
319b110fce1SFrançois Revol		} else {
320b110fce1SFrançois Revol			cookie->iface->sel[event].sync = sync;
321b110fce1SFrançois Revol			cookie->iface->sel[event].ref = ref;
322b110fce1SFrançois Revol		}
323b110fce1SFrançois Revol	}
324b110fce1SFrançois Revol	gUtil->unlock_benaphore(&cookie->iface->lock);
325b110fce1SFrançois Revol	/* iface UNLOCKED */
326b110fce1SFrançois Revol	return err;
327b110fce1SFrançois Revol}
328612c05bdSAlexander von Gluck IV
329612c05bdSAlexander von Gluck IV
330612c05bdSAlexander von Gluck IVstatus_t
331612c05bdSAlexander von Gluck IVtun_deselect(cookie_t *cookie, uint8 event, selectsync *sync)
332b110fce1SFrançois Revol{
333b110fce1SFrançois Revol	status_t err = B_OK;
334b110fce1SFrançois Revol#if DEBUG > 1
335b110fce1SFrançois Revol	dprintf("tun: deselect(, %d, )\n", event);
336b110fce1SFrançois Revol#endif
337b110fce1SFrançois Revol	if (cookie->iface == NULL)
338b110fce1SFrançois Revol		return B_NO_INIT;
339b110fce1SFrançois Revol	if (event > B_SELECT_EXCEPTION || !event)
340b110fce1SFrançois Revol		return EINVAL;
341b110fce1SFrançois Revol	err = gUtil->lock_benaphore(&cookie->iface->lock);
342b110fce1SFrançois Revol	if (err < B_OK)
343b110fce1SFrançois Revol		return err;
344b110fce1SFrançois Revol	cookie->iface->sel[event].sync = NULL;
345b110fce1SFrançois Revol	cookie->iface->sel[event].ref = 0;
346b110fce1SFrançois Revol	gUtil->unlock_benaphore(&cookie->iface->lock);
347b110fce1SFrançois Revol	/* iface LOCKED */
348b110fce1SFrançois Revol	return B_OK;
349b110fce1SFrançois Revol}
350612c05bdSAlexander von Gluck IV
351612c05bdSAlexander von Gluck IV
352612c05bdSAlexander von Gluck IVstatus_t
353612c05bdSAlexander von Gluck IVtun_readv(cookie_t *cookie, off_t position, const iovec *vec, size_t count, size_t *numBytes)
354b110fce1SFrançois Revol{
355b110fce1SFrançois Revol	dprintf("tun: readv(, %Ld, , %ld)\n", position, count);
356b110fce1SFrançois Revol	return EOPNOTSUPP;
357b110fce1SFrançois Revol}
358612c05bdSAlexander von Gluck IV
359612c05bdSAlexander von Gluck IV
360612c05bdSAlexander von Gluck IVstatus_t
361612c05bdSAlexander von Gluck IVtun_writev(cookie_t *cookie, off_t position, const iovec *vec, size_t count, size_t *numBytes)
362b110fce1SFrançois Revol{
363b110fce1SFrançois Revol	dprintf("tun: writev(, %Ld, , %ld)\n", position, count);
364b110fce1SFrançois Revol	return EOPNOTSUPP;
365b110fce1SFrançois Revol}
366b110fce1SFrançois Revol
367612c05bdSAlexander von Gluck IV
368612c05bdSAlexander von Gluck IVdevice_hooks tun_hooks = {
369b110fce1SFrançois Revol	(device_open_hook)tun_open,
370b110fce1SFrançois Revol	tun_close,
371b110fce1SFrançois Revol	(device_free_hook)tun_free,
372b110fce1SFrançois Revol	(device_control_hook)tun_ioctl,
373b110fce1SFrançois Revol	(device_read_hook)tun_read,
374b110fce1SFrançois Revol	(device_write_hook)tun_write,
375b110fce1SFrançois Revol	(device_select_hook)tun_select,
376b110fce1SFrançois Revol	(device_deselect_hook)tun_deselect,
377b110fce1SFrançois Revol	(device_readv_hook)tun_readv,
378b110fce1SFrançois Revol	(device_writev_hook)tun_writev
379b110fce1SFrançois Revol};
380