1/*
2 * /dev/config/tun network tunnel driver for BeOS
3 * (c) 2003, mmu_man, revol@free.fr
4 * licenced under MIT licence.
5 */
6#include <Drivers.h>
7#include <KernelExport.h>
8#include <OS.h>
9#include <stdlib.h>
10#include <string.h>
11#include <sys/param.h>
12#include <sys/types.h>
13#include <unistd.h>
14#include <fcntl.h>
15#include <fsproto.h>
16#include "bone_tun.h"
17
18
19const char * device_names[] = {TUN_DRIVER_NAME, NULL};
20extern device_hooks tun_hooks;
21
22int32 api_version = B_CUR_DRIVER_API_VERSION;
23
24vint32 if_mod_ref_count = 0;
25bone_tun_interface_info_t *gIfaceModule = NULL;
26bone_util_info_t *gUtil = NULL;
27
28
29status_t
30init_hardware(void)
31{
32	dprintf("tun:init_hardware()\n");
33	return B_OK;
34}
35
36
37status_t
38init_driver(void)
39{
40	dprintf("tun:init_driver()\n");
41	return B_OK;
42}
43
44
45void
46uninit_driver(void)
47{
48	dprintf("tun:uninit_driver()\n");
49}
50
51
52const char**
53publish_devices()
54{
55	return device_names;
56}
57
58
59device_hooks*
60find_device(const char *name)
61{
62	(void)name;
63	return &tun_hooks;
64}
65
66
67status_t
68tun_open(const char *name, uint32 flags, cookie_t **cookie)
69{
70	status_t err = B_OK;
71	(void)name; (void)flags;
72	/* XXX: add O_NONBLOCK + FIONBIO */
73#if DEBUG > 1
74	dprintf("tun:open(%s, 0x%08lx,)\n", name, flags);
75#endif
76	err = get_module(BONE_UTIL_MOD_NAME, (struct module_info **)&gUtil);
77	if (err < B_OK)
78		return err;
79	err = get_module(TUN_INTERFACE_MODULE, (struct module_info **)&gIfaceModule);
80	if (err < B_OK) {
81		put_module(BONE_UTIL_MOD_NAME);
82		return err;
83	}
84
85	/* XXX: FIXME, still not ok (rescan) */
86	if (atomic_add(&if_mod_ref_count, 1) < 1) /* force one more open to keep loaded */
87		get_module(TUN_INTERFACE_MODULE, (struct module_info **)&gIfaceModule);
88
89	*cookie = (void*)malloc(sizeof(cookie_t));
90	if (*cookie == NULL) {
91		dprintf("tun_open : error allocating cookie\n");
92		goto err0;
93	}
94	memset(*cookie, 0, sizeof(cookie_t));
95	(*cookie)->blocking_io = true;
96	return B_OK;
97
98err1:
99	dprintf("tun_open : cleanup : will free cookie\n");
100	free(*cookie);
101	*cookie = NULL;
102	put_module(TUN_INTERFACE_MODULE);
103	put_module(BONE_UTIL_MOD_NAME);
104err0:
105	return B_ERROR;
106}
107
108
109status_t
110tun_close(void *cookie)
111{
112	(void)cookie;
113	return B_OK;
114}
115
116
117status_t
118tun_free(cookie_t *cookie)
119{
120	status_t err = B_OK;
121#if DEBUG > 1
122	dprintf("tun_close()\n");
123#endif
124	if (cookie->iface)
125		err = gIfaceModule->tun_detach_driver(cookie->iface, true);
126	free(cookie);
127	atomic_add(&if_mod_ref_count, -1);
128	put_module(TUN_INTERFACE_MODULE);
129	put_module(BONE_UTIL_MOD_NAME);
130	return err;
131}
132
133
134status_t
135tun_ioctl(cookie_t *cookie, uint32 op, void *data, size_t len)
136{
137	ifreq_t *ifr;
138	bone_tun_if_interface_t *iface;
139	(void)cookie; (void)op; (void)data; (void)len;
140	iface = cookie->iface;
141#if DEBUG > 1
142	dprintf("tun_ioctl(%d(0x%08lx), , %d)\n", op, op, len);
143#endif
144
145	switch (op) {
146	case B_SET_NONBLOCKING_IO:
147		cookie->blocking_io = false;
148		return B_OK;
149	case B_SET_BLOCKING_IO:
150		cookie->blocking_io = true;
151		return B_OK;
152	case TUNSETNOCSUM:
153		return B_OK;//EOPNOTSUPP;
154	case TUNSETDEBUG:
155		return B_OK;//EOPNOTSUPP;
156	case TUNSETIFF:
157		if (data == NULL)
158			return EINVAL;
159		ifr = (ifreq_t *)data;
160
161		iface = gIfaceModule->tun_reuse_or_create(ifr, cookie);
162		if (iface != NULL) {
163			dprintf("tun: new tunnel created: %s, flags: 0x%08lx\n", ifr->ifr_name, iface->flags);
164			return B_OK;
165		} else
166			dprintf("tun: can't allocate a new tunnel!\n");
167		break;
168
169	case SIOCGIFHWADDR:
170		if (data == NULL)
171			return EINVAL;
172		ifr = (ifreq_t *)data;
173		if (iface == NULL)
174			return EINVAL;
175		if (strncmp(ifr->ifr_name, iface->ifn->if_name, IFNAMSIZ) != 0)
176			return EINVAL;
177		memcpy(ifr->ifr_hwaddr, iface->fakemac.octet, 6);
178		return B_OK;
179	case SIOCSIFHWADDR:
180		if (data == NULL)
181			return EINVAL;
182		ifr = (ifreq_t *)data;
183		if (iface == NULL)
184			return EINVAL;
185		if (strncmp(ifr->ifr_name, iface->ifn->if_name, IFNAMSIZ) != 0)
186			return EINVAL;
187		memcpy(iface->fakemac.octet, ifr->ifr_hwaddr, 6);
188		return B_OK;
189
190	}
191	return B_ERROR;
192}
193
194
195status_t
196tun_read(cookie_t *cookie, off_t position, void *data, size_t *numbytes)
197{
198	bone_data_t *bdata;
199	uint32 got;
200	ssize_t pktsize;
201	if (cookie->iface == NULL)
202		return EBUSY;
203
204	//if ((pktsize = gUtil->dequeue_fifo_data(cookie->iface->wfifo, &bdata, B_INFINITE_TIMEOUT, false)) < 0)
205	pktsize = gUtil->dequeue_fifo_data(cookie->iface->wfifo, &bdata, cookie->blocking_io?B_INFINITE_TIMEOUT:0LL, false);
206
207	if (pktsize < 0) {
208		*numbytes = 0;
209		return pktsize;
210	}
211#if DEBUG > 2
212	dprintf("tun: dequeue = %ld, datalen = %ld, \n", pktsize, bdata?bdata->datalen:0);
213#endif
214	pktsize = (ssize_t)bdata->datalen;
215
216	got = gUtil->copy_from_data(bdata, 0L, data, MIN((bdata->datalen), (*numbytes)));
217	//got = MIN((*numbytes), bdata->datalen);
218	if ((pktsize > *numbytes) && !(cookie->iface->flags & TUN_NO_PI))
219		((struct tun_pi *)data)->flags |= TUN_PKT_STRIP;
220	gUtil->delete_data(bdata);
221#if DEBUG > 2
222	dprintf("tun: read() got %ld bytes, buf is %ld\n", got, *numbytes);
223	dump_data(bdata);
224	iovec_dump_data(bdata);
225#endif
226	*numbytes = got;
227	return B_OK;
228ERROR_EOF:
229	*numbytes = 0;
230	return B_OK;
231}
232
233
234status_t
235tun_write(cookie_t *cookie, off_t position, const void *data, size_t *numbytes)
236{
237	bone_data_t *bdata = NULL;
238	void *buf;
239	status_t err = B_NO_MEMORY;
240	(void)position;
241
242	/* XXX: max for *numbytes ? */
243
244	if (cookie->iface == NULL)
245		return EBUSY;
246	bdata = gUtil->new_data();
247	if (bdata == NULL)
248		return B_NO_MEMORY;
249	buf = gUtil->falloc(*numbytes);
250	if (buf == NULL)
251		goto ERROR_1;
252	memcpy(buf, data, *numbytes);
253
254	if (gUtil->prepend_data(bdata, buf, *numbytes, gUtil->free) < 0) {
255		gUtil->free(buf);
256		goto ERROR_1;
257	}
258	if ((err = gUtil->enqueue_fifo_data(cookie->iface->rfifo, bdata)) < B_OK)
259		goto ERROR_1;
260	return B_OK;
261
262ERROR_1:
263	if (bdata)
264		gUtil->delete_data(bdata);
265	return err;
266}
267
268
269status_t
270tun_select(cookie_t *cookie, uint8 event, uint32 ref, selectsync *sync)
271{
272	status_t err = B_OK;
273#if DEBUG > 1
274	dprintf("tun: select(, %d, %ld, )\n", event, ref);
275#endif
276	if (cookie->iface == NULL)
277		return B_NO_INIT;
278	if (event > B_SELECT_EXCEPTION || !event)
279		return EINVAL;
280	err = gUtil->lock_benaphore(&cookie->iface->lock);
281	if (err < B_OK)
282		return err;
283	/* iface LOCKED */
284	if (cookie->iface->sel[event].sync)
285		err = EALREADY;
286	else {
287		bone_fifo_t *fifo = NULL;
288		switch (event) {
289		case B_SELECT_READ:
290			fifo = cookie->iface->wfifo;
291			break;
292		case B_SELECT_WRITE:
293			fifo = cookie->iface->rfifo;
294			break;
295		}
296		if (fifo) {
297			/* XXX: is it safe ??? shouldn't we dequeue(peek=true) ? */
298			err = gUtil->lock_benaphore(&fifo->lock);
299			if (err >= B_OK) {
300				bool avail;
301				switch (event) {
302				case B_SELECT_READ:
303					avail = (fifo->current_bytes > 1);
304					break;
305				case B_SELECT_WRITE:
306					avail = ((fifo->max_bytes - fifo->current_bytes) > cookie->iface->ifn->if_mtu);
307					break;
308				}
309				/* check if we don't already have the event */
310				if (avail) {
311					notify_select_event(sync, ref);
312				}
313				else {
314					cookie->iface->sel[event].sync = sync;
315					cookie->iface->sel[event].ref = ref;
316				}
317				gUtil->unlock_benaphore(&fifo->lock);
318			}
319		} else {
320			cookie->iface->sel[event].sync = sync;
321			cookie->iface->sel[event].ref = ref;
322		}
323	}
324	gUtil->unlock_benaphore(&cookie->iface->lock);
325	/* iface UNLOCKED */
326	return err;
327}
328
329
330status_t
331tun_deselect(cookie_t *cookie, uint8 event, selectsync *sync)
332{
333	status_t err = B_OK;
334#if DEBUG > 1
335	dprintf("tun: deselect(, %d, )\n", event);
336#endif
337	if (cookie->iface == NULL)
338		return B_NO_INIT;
339	if (event > B_SELECT_EXCEPTION || !event)
340		return EINVAL;
341	err = gUtil->lock_benaphore(&cookie->iface->lock);
342	if (err < B_OK)
343		return err;
344	cookie->iface->sel[event].sync = NULL;
345	cookie->iface->sel[event].ref = 0;
346	gUtil->unlock_benaphore(&cookie->iface->lock);
347	/* iface LOCKED */
348	return B_OK;
349}
350
351
352status_t
353tun_readv(cookie_t *cookie, off_t position, const iovec *vec, size_t count, size_t *numBytes)
354{
355	dprintf("tun: readv(, %Ld, , %ld)\n", position, count);
356	return EOPNOTSUPP;
357}
358
359
360status_t
361tun_writev(cookie_t *cookie, off_t position, const iovec *vec, size_t count, size_t *numBytes)
362{
363	dprintf("tun: writev(, %Ld, , %ld)\n", position, count);
364	return EOPNOTSUPP;
365}
366
367
368device_hooks tun_hooks = {
369	(device_open_hook)tun_open,
370	tun_close,
371	(device_free_hook)tun_free,
372	(device_control_hook)tun_ioctl,
373	(device_read_hook)tun_read,
374	(device_write_hook)tun_write,
375	(device_select_hook)tun_select,
376	(device_deselect_hook)tun_deselect,
377	(device_readv_hook)tun_readv,
378	(device_writev_hook)tun_writev
379};
380