if_tun.c revision 74810
1/*	$NetBSD: if_tun.c,v 1.14 1994/06/29 06:36:25 cgd Exp $	*/
2
3/*
4 * Copyright (c) 1988, Julian Onions <jpo@cs.nott.ac.uk>
5 * Nottingham University 1987.
6 *
7 * This source may be freely distributed, however I would be interested
8 * in any changes that are made.
9 *
10 * This driver takes packets off the IP i/f and hands them up to a
11 * user process to have its wicked way with. This driver has it's
12 * roots in a similar driver written by Phil Cockcroft (formerly) at
13 * UCL. This driver is based much more on read/write/poll mode of
14 * operation though.
15 *
16 * $FreeBSD: head/sys/net/if_tun.c 74810 2001-03-26 12:41:29Z phk $
17 */
18
19#include "opt_inet.h"
20
21#include <sys/param.h>
22#include <sys/proc.h>
23#include <sys/systm.h>
24#include <sys/mbuf.h>
25#include <sys/module.h>
26#include <sys/socket.h>
27#include <sys/filio.h>
28#include <sys/sockio.h>
29#include <sys/ttycom.h>
30#include <sys/poll.h>
31#include <sys/signalvar.h>
32#include <sys/filedesc.h>
33#include <sys/kernel.h>
34#include <sys/sysctl.h>
35#include <sys/conf.h>
36#include <sys/uio.h>
37#include <sys/vnode.h>
38#include <sys/malloc.h>
39
40#include <net/if.h>
41#include <net/if_types.h>
42#include <net/route.h>
43#include <net/intrq.h>
44
45#ifdef INET
46#include <netinet/in.h>
47#endif
48
49#include <net/bpf.h>
50
51#include <net/if_tunvar.h>
52#include <net/if_tun.h>
53
54static MALLOC_DEFINE(M_TUN, "tun", "Tunnel Interface");
55
56static void tuncreate __P((dev_t dev));
57
58#define TUNDEBUG	if (tundebug) printf
59static int tundebug = 0;
60SYSCTL_INT(_debug, OID_AUTO, if_tun_debug, CTLFLAG_RW, &tundebug, 0, "");
61
62static int tunoutput __P((struct ifnet *, struct mbuf *, struct sockaddr *,
63	    struct rtentry *rt));
64static int tunifioctl __P((struct ifnet *, u_long, caddr_t));
65static int tuninit __P((struct ifnet *));
66static void tunstart __P((struct ifnet *));
67
68static	d_open_t	tunopen;
69static	d_close_t	tunclose;
70static	d_read_t	tunread;
71static	d_write_t	tunwrite;
72static	d_ioctl_t	tunioctl;
73static	d_poll_t	tunpoll;
74
75#define CDEV_MAJOR 52
76static struct cdevsw tun_cdevsw = {
77	/* open */	tunopen,
78	/* close */	tunclose,
79	/* read */	tunread,
80	/* write */	tunwrite,
81	/* ioctl */	tunioctl,
82	/* poll */	tunpoll,
83	/* mmap */	nommap,
84	/* strategy */	nostrategy,
85	/* name */	"tun",
86	/* maj */	CDEV_MAJOR,
87	/* dump */	nodump,
88	/* psize */	nopsize,
89	/* flags */	0,
90};
91
92static void tun_clone __P((void *arg, char *name, int namelen, dev_t *dev));
93
94static void
95tun_clone(arg, name, namelen, dev)
96	void *arg;
97	char *name;
98	int namelen;
99	dev_t *dev;
100{
101	int u;
102
103	if (*dev != NODEV)
104		return;
105	if (dev_stdclone(name, NULL, "tun", &u) != 1)
106		return;
107	*dev = make_dev(&tun_cdevsw, unit2minor(u),
108	    UID_ROOT, GID_WHEEL, 0600, "tun%d", u);
109
110}
111
112static int
113tun_modevent(module_t mod, int type, void *data)
114{
115	switch (type) {
116	case MOD_LOAD:
117		EVENTHANDLER_REGISTER(dev_clone, tun_clone, 0, 1000);
118		cdevsw_add(&tun_cdevsw);
119		break;
120	case MOD_UNLOAD:
121		printf("if_tun module unload - not possible for this module type\n");
122		return EINVAL;
123	}
124	return 0;
125}
126
127static moduledata_t tun_mod = {
128	"if_tun",
129	tun_modevent,
130	0
131};
132
133DECLARE_MODULE(if_tun, tun_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
134
135static void
136tunstart(ifp)
137	struct ifnet *ifp;
138{
139	struct tun_softc *tp = ifp->if_softc;
140
141	if (tp->tun_flags & TUN_RWAIT) {
142		tp->tun_flags &= ~TUN_RWAIT;
143		wakeup((caddr_t)tp);
144	}
145	if (tp->tun_flags & TUN_ASYNC && tp->tun_sigio)
146		pgsigio(tp->tun_sigio, SIGIO, 0);
147	selwakeup(&tp->tun_rsel);
148}
149
150static void
151tuncreate(dev)
152	dev_t dev;
153{
154	struct tun_softc *sc;
155	struct ifnet *ifp;
156
157	dev = make_dev(&tun_cdevsw, minor(dev),
158	    UID_UUCP, GID_DIALER, 0600, "tun%d", dev2unit(dev));
159
160	MALLOC(sc, struct tun_softc *, sizeof(*sc), M_TUN, M_WAITOK | M_ZERO);
161	sc->tun_flags = TUN_INITED;
162
163	ifp = &sc->tun_if;
164	ifp->if_unit = dev2unit(dev);
165	ifp->if_name = "tun";
166	ifp->if_mtu = TUNMTU;
167	ifp->if_ioctl = tunifioctl;
168	ifp->if_output = tunoutput;
169	ifp->if_start = tunstart;
170	ifp->if_flags = IFF_POINTOPOINT | IFF_MULTICAST;
171	ifp->if_type = IFT_PPP;
172	ifp->if_snd.ifq_maxlen = ifqmaxlen;
173	ifp->if_softc = sc;
174	if_attach(ifp);
175	bpfattach(ifp, DLT_NULL, sizeof(u_int));
176	dev->si_drv1 = sc;
177}
178
179/*
180 * tunnel open - must be superuser & the device must be
181 * configured in
182 */
183static	int
184tunopen(dev, flag, mode, p)
185	dev_t	dev;
186	int	flag, mode;
187	struct proc *p;
188{
189	struct ifnet	*ifp;
190	struct tun_softc *tp;
191
192	tp = dev->si_drv1;
193	if (!tp) {
194		tuncreate(dev);
195		tp = dev->si_drv1;
196	}
197	if (tp->tun_flags & TUN_OPEN)
198		return EBUSY;
199	tp->tun_pid = p->p_pid;
200	ifp = &tp->tun_if;
201	tp->tun_flags |= TUN_OPEN;
202	TUNDEBUG("%s%d: open\n", ifp->if_name, ifp->if_unit);
203	return (0);
204}
205
206/*
207 * tunclose - close the device - mark i/f down & delete
208 * routing info
209 */
210static	int
211tunclose(dev, foo, bar, p)
212	dev_t dev;
213	int foo;
214	int bar;
215	struct proc *p;
216{
217	register int	s;
218	struct tun_softc *tp;
219	struct ifnet	*ifp;
220
221	tp = dev->si_drv1;
222	ifp = &tp->tun_if;
223
224	tp->tun_flags &= ~TUN_OPEN;
225	tp->tun_pid = 0;
226
227	/*
228	 * junk all pending output
229	 */
230	IF_DRAIN(&ifp->if_snd);
231
232	if (ifp->if_flags & IFF_UP) {
233		s = splimp();
234		if_down(ifp);
235		splx(s);
236	}
237
238	if (ifp->if_flags & IFF_RUNNING) {
239		register struct ifaddr *ifa;
240
241		s = splimp();
242		/* find internet addresses and delete routes */
243		TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link)
244			if (ifa->ifa_addr->sa_family == AF_INET)
245				rtinit(ifa, (int)RTM_DELETE,
246				    tp->tun_flags & TUN_DSTADDR ? RTF_HOST : 0);
247		ifp->if_flags &= ~IFF_RUNNING;
248		splx(s);
249	}
250
251	funsetown(tp->tun_sigio);
252	selwakeup(&tp->tun_rsel);
253
254	TUNDEBUG ("%s%d: closed\n", ifp->if_name, ifp->if_unit);
255	return (0);
256}
257
258static int
259tuninit(ifp)
260	struct ifnet *ifp;
261{
262	struct tun_softc *tp = ifp->if_softc;
263	register struct ifaddr *ifa;
264	int error = 0;
265
266	TUNDEBUG("%s%d: tuninit\n", ifp->if_name, ifp->if_unit);
267
268	ifp->if_flags |= IFF_UP | IFF_RUNNING;
269	getmicrotime(&ifp->if_lastchange);
270
271	for (ifa = TAILQ_FIRST(&ifp->if_addrhead); ifa;
272	     ifa = TAILQ_NEXT(ifa, ifa_link)) {
273		if (ifa->ifa_addr == NULL)
274			error = EFAULT;
275			/* XXX: Should maybe return straight off? */
276		else {
277#ifdef INET
278			if (ifa->ifa_addr->sa_family == AF_INET) {
279			    struct sockaddr_in *si;
280
281			    si = (struct sockaddr_in *)ifa->ifa_addr;
282			    if (si->sin_addr.s_addr)
283				    tp->tun_flags |= TUN_IASET;
284
285			    si = (struct sockaddr_in *)ifa->ifa_dstaddr;
286			    if (si && si->sin_addr.s_addr)
287				    tp->tun_flags |= TUN_DSTADDR;
288			}
289#endif
290		}
291	}
292	return (error);
293}
294
295/*
296 * Process an ioctl request.
297 */
298int
299tunifioctl(ifp, cmd, data)
300	struct ifnet *ifp;
301	u_long	cmd;
302	caddr_t	data;
303{
304	struct ifreq *ifr = (struct ifreq *)data;
305	struct tun_softc *tp = ifp->if_softc;
306	struct ifstat *ifs;
307	int		error = 0, s;
308
309	s = splimp();
310	switch(cmd) {
311	case SIOCGIFSTATUS:
312		ifs = (struct ifstat *)data;
313		if (tp->tun_pid)
314			sprintf(ifs->ascii + strlen(ifs->ascii),
315			    "\tOpened by PID %d\n", tp->tun_pid);
316		break;
317	case SIOCSIFADDR:
318		error = tuninit(ifp);
319		TUNDEBUG("%s%d: address set, error=%d\n",
320			 ifp->if_name, ifp->if_unit, error);
321		break;
322	case SIOCSIFDSTADDR:
323		error = tuninit(ifp);
324		TUNDEBUG("%s%d: destination address set, error=%d\n",
325			 ifp->if_name, ifp->if_unit, error);
326		break;
327	case SIOCSIFMTU:
328		ifp->if_mtu = ifr->ifr_mtu;
329		TUNDEBUG("%s%d: mtu set\n", ifp->if_name, ifp->if_unit);
330		break;
331	case SIOCADDMULTI:
332	case SIOCDELMULTI:
333		break;
334	default:
335		error = EINVAL;
336	}
337	splx(s);
338	return (error);
339}
340
341/*
342 * tunoutput - queue packets from higher level ready to put out.
343 */
344int
345tunoutput(ifp, m0, dst, rt)
346	struct ifnet   *ifp;
347	struct mbuf    *m0;
348	struct sockaddr *dst;
349	struct rtentry *rt;
350{
351	struct tun_softc *tp = ifp->if_softc;
352
353	TUNDEBUG ("%s%d: tunoutput\n", ifp->if_name, ifp->if_unit);
354
355	if ((tp->tun_flags & TUN_READY) != TUN_READY) {
356		TUNDEBUG ("%s%d: not ready 0%o\n", ifp->if_name,
357			  ifp->if_unit, tp->tun_flags);
358		m_freem (m0);
359		return EHOSTDOWN;
360	}
361
362	/* BPF write needs to be handled specially */
363	if (dst->sa_family == AF_UNSPEC) {
364		dst->sa_family = *(mtod(m0, int *));
365		m0->m_len -= sizeof(int);
366		m0->m_pkthdr.len -= sizeof(int);
367		m0->m_data += sizeof(int);
368	}
369
370	if (ifp->if_bpf) {
371		/*
372		 * We need to prepend the address family as
373		 * a four byte field.  Cons up a dummy header
374		 * to pacify bpf.  This is safe because bpf
375		 * will only read from the mbuf (i.e., it won't
376		 * try to free it or keep a pointer to it).
377		 */
378		struct mbuf m;
379		uint32_t af = dst->sa_family;
380
381		m.m_next = m0;
382		m.m_len = 4;
383		m.m_data = (char *)&af;
384
385		bpf_mtap(ifp, &m);
386	}
387
388	/* prepend sockaddr? this may abort if the mbuf allocation fails */
389	if (tp->tun_flags & TUN_LMODE) {
390		/* allocate space for sockaddr */
391		M_PREPEND(m0, dst->sa_len, M_DONTWAIT);
392
393		/* if allocation failed drop packet */
394		if (m0 == NULL) {
395			ifp->if_iqdrops++;
396			ifp->if_oerrors++;
397			return (ENOBUFS);
398		} else {
399			bcopy(dst, m0->m_data, dst->sa_len);
400		}
401	}
402
403	if (tp->tun_flags & TUN_IFHEAD) {
404		/* Prepend the address family */
405		M_PREPEND(m0, 4, M_DONTWAIT);
406
407		/* if allocation failed drop packet */
408		if (m0 == NULL) {
409			ifp->if_iqdrops++;
410			ifp->if_oerrors++;
411			return ENOBUFS;
412		} else
413			*(u_int32_t *)m0->m_data = htonl(dst->sa_family);
414	} else {
415#ifdef INET
416		if (dst->sa_family != AF_INET)
417#endif
418		{
419			m_freem(m0);
420			return EAFNOSUPPORT;
421		}
422	}
423
424	if (! IF_HANDOFF(&ifp->if_snd, m0, ifp)) {
425		ifp->if_collisions++;
426		return ENOBUFS;
427	}
428	ifp->if_opackets++;
429	return 0;
430}
431
432/*
433 * the cdevsw interface is now pretty minimal.
434 */
435static	int
436tunioctl(dev, cmd, data, flag, p)
437	dev_t		dev;
438	u_long		cmd;
439	caddr_t		data;
440	int		flag;
441	struct proc	*p;
442{
443	int		s;
444	int		error;
445	struct tun_softc *tp = dev->si_drv1;
446 	struct tuninfo *tunp;
447
448	switch (cmd) {
449 	case TUNSIFINFO:
450 		tunp = (struct tuninfo *)data;
451		if (tunp->mtu < IF_MINMTU)
452			return (EINVAL);
453 		if (tp->tun_if.if_mtu != tunp->mtu && (error = suser(p)) != 0)
454			return (error);
455 		tp->tun_if.if_mtu = tunp->mtu;
456 		tp->tun_if.if_type = tunp->type;
457 		tp->tun_if.if_baudrate = tunp->baudrate;
458 		break;
459 	case TUNGIFINFO:
460 		tunp = (struct tuninfo *)data;
461 		tunp->mtu = tp->tun_if.if_mtu;
462 		tunp->type = tp->tun_if.if_type;
463 		tunp->baudrate = tp->tun_if.if_baudrate;
464 		break;
465	case TUNSDEBUG:
466		tundebug = *(int *)data;
467		break;
468	case TUNGDEBUG:
469		*(int *)data = tundebug;
470		break;
471	case TUNSLMODE:
472		if (*(int *)data) {
473			tp->tun_flags |= TUN_LMODE;
474			tp->tun_flags &= ~TUN_IFHEAD;
475		} else
476			tp->tun_flags &= ~TUN_LMODE;
477		break;
478	case TUNSIFHEAD:
479		if (*(int *)data) {
480			tp->tun_flags |= TUN_IFHEAD;
481			tp->tun_flags &= ~TUN_LMODE;
482		} else
483			tp->tun_flags &= ~TUN_IFHEAD;
484		break;
485	case TUNGIFHEAD:
486		*(int *)data = (tp->tun_flags & TUN_IFHEAD) ? 1 : 0;
487		break;
488	case TUNSIFMODE:
489		/* deny this if UP */
490		if (tp->tun_if.if_flags & IFF_UP)
491			return(EBUSY);
492
493		switch (*(int *)data) {
494		case IFF_POINTOPOINT:
495			tp->tun_if.if_flags |= IFF_POINTOPOINT;
496			tp->tun_if.if_flags &= ~IFF_BROADCAST;
497			break;
498		case IFF_BROADCAST:
499			tp->tun_if.if_flags &= ~IFF_POINTOPOINT;
500			tp->tun_if.if_flags |= IFF_BROADCAST;
501			break;
502		default:
503			return(EINVAL);
504		}
505		break;
506	case TUNSIFPID:
507		tp->tun_pid = curproc->p_pid;
508		break;
509	case FIONBIO:
510		break;
511	case FIOASYNC:
512		if (*(int *)data)
513			tp->tun_flags |= TUN_ASYNC;
514		else
515			tp->tun_flags &= ~TUN_ASYNC;
516		break;
517	case FIONREAD:
518		s = splimp();
519		if (tp->tun_if.if_snd.ifq_head) {
520			struct mbuf *mb = tp->tun_if.if_snd.ifq_head;
521			for( *(int *)data = 0; mb != 0; mb = mb->m_next)
522				*(int *)data += mb->m_len;
523		} else
524			*(int *)data = 0;
525		splx(s);
526		break;
527	case FIOSETOWN:
528		return (fsetown(*(int *)data, &tp->tun_sigio));
529
530	case FIOGETOWN:
531		*(int *)data = fgetown(tp->tun_sigio);
532		return (0);
533
534	/* This is deprecated, FIOSETOWN should be used instead. */
535	case TIOCSPGRP:
536		return (fsetown(-(*(int *)data), &tp->tun_sigio));
537
538	/* This is deprecated, FIOGETOWN should be used instead. */
539	case TIOCGPGRP:
540		*(int *)data = -fgetown(tp->tun_sigio);
541		return (0);
542
543	default:
544		return (ENOTTY);
545	}
546	return (0);
547}
548
549/*
550 * The cdevsw read interface - reads a packet at a time, or at
551 * least as much of a packet as can be read.
552 */
553static	int
554tunread(dev, uio, flag)
555	dev_t dev;
556	struct uio *uio;
557	int flag;
558{
559	struct tun_softc *tp = dev->si_drv1;
560	struct ifnet	*ifp = &tp->tun_if;
561	struct mbuf	*m, *m0;
562	int		error=0, len, s;
563
564	TUNDEBUG ("%s%d: read\n", ifp->if_name, ifp->if_unit);
565	if ((tp->tun_flags & TUN_READY) != TUN_READY) {
566		TUNDEBUG ("%s%d: not ready 0%o\n", ifp->if_name,
567			  ifp->if_unit, tp->tun_flags);
568		return EHOSTDOWN;
569	}
570
571	tp->tun_flags &= ~TUN_RWAIT;
572
573	s = splimp();
574	do {
575		IF_DEQUEUE(&ifp->if_snd, m0);
576		if (m0 == 0) {
577			if (flag & IO_NDELAY) {
578				splx(s);
579				return EWOULDBLOCK;
580			}
581			tp->tun_flags |= TUN_RWAIT;
582			if((error = tsleep((caddr_t)tp, PCATCH | (PZERO + 1),
583					"tunread", 0)) != 0) {
584				splx(s);
585				return error;
586			}
587		}
588	} while (m0 == 0);
589	splx(s);
590
591	while (m0 && uio->uio_resid > 0 && error == 0) {
592		len = min(uio->uio_resid, m0->m_len);
593		if (len == 0)
594			break;
595		error = uiomove(mtod(m0, caddr_t), len, uio);
596		MFREE(m0, m);
597		m0 = m;
598	}
599
600	if (m0) {
601		TUNDEBUG("Dropping mbuf\n");
602		m_freem(m0);
603	}
604	return error;
605}
606
607/*
608 * the cdevsw write interface - an atomic write is a packet - or else!
609 */
610static	int
611tunwrite(dev, uio, flag)
612	dev_t dev;
613	struct uio *uio;
614	int flag;
615{
616	struct tun_softc *tp = dev->si_drv1;
617	struct ifnet	*ifp = &tp->tun_if;
618	struct mbuf	*top, **mp, *m;
619	int		error=0, tlen, mlen;
620	uint32_t	family;
621
622	TUNDEBUG("%s%d: tunwrite\n", ifp->if_name, ifp->if_unit);
623
624	if (uio->uio_resid == 0)
625		return 0;
626
627	if (uio->uio_resid < 0 || uio->uio_resid > TUNMRU) {
628		TUNDEBUG("%s%d: len=%d!\n", ifp->if_name, ifp->if_unit,
629		    uio->uio_resid);
630		return EIO;
631	}
632	tlen = uio->uio_resid;
633
634	/* get a header mbuf */
635	MGETHDR(m, M_DONTWAIT, MT_DATA);
636	if (m == NULL)
637		return ENOBUFS;
638	mlen = MHLEN;
639
640	top = 0;
641	mp = &top;
642	while (error == 0 && uio->uio_resid > 0) {
643		m->m_len = min(mlen, uio->uio_resid);
644		error = uiomove(mtod (m, caddr_t), m->m_len, uio);
645		*mp = m;
646		mp = &m->m_next;
647		if (uio->uio_resid > 0) {
648			MGET (m, M_DONTWAIT, MT_DATA);
649			if (m == 0) {
650				error = ENOBUFS;
651				break;
652			}
653			mlen = MLEN;
654		}
655	}
656	if (error) {
657		if (top)
658			m_freem (top);
659		ifp->if_ierrors++;
660		return error;
661	}
662
663	top->m_pkthdr.len = tlen;
664	top->m_pkthdr.rcvif = ifp;
665
666	if (ifp->if_bpf) {
667		if (tp->tun_flags & TUN_IFHEAD) {
668			/*
669			 * Conveniently, we already have a 4-byte address
670			 * family prepended to our packet !
671			 * Inconveniently, it's in the wrong byte order !
672			 */
673			if ((top = m_pullup(top, sizeof(family))) == NULL)
674				return ENOBUFS;
675			*mtod(top, u_int32_t *) =
676			    ntohl(*mtod(top, u_int32_t *));
677			bpf_mtap(ifp, top);
678			*mtod(top, u_int32_t *) =
679			    htonl(*mtod(top, u_int32_t *));
680		} else {
681			/*
682			 * We need to prepend the address family as
683			 * a four byte field.  Cons up a dummy header
684			 * to pacify bpf.  This is safe because bpf
685			 * will only read from the mbuf (i.e., it won't
686			 * try to free it or keep a pointer to it).
687			 */
688			struct mbuf m;
689			uint32_t af = AF_INET;
690
691			m.m_next = top;
692			m.m_len = 4;
693			m.m_data = (char *)&af;
694
695			bpf_mtap(ifp, &m);
696		}
697	}
698
699	if (tp->tun_flags & TUN_IFHEAD) {
700		if (top->m_len < sizeof(family) &&
701		    (top = m_pullup(top, sizeof(family))) == NULL)
702				return ENOBUFS;
703		family = ntohl(*mtod(top, u_int32_t *));
704		m_adj(top, sizeof(family));
705	} else
706		family = AF_INET;
707
708	ifp->if_ibytes += top->m_pkthdr.len;
709	ifp->if_ipackets++;
710
711	return family_enqueue(family, top);
712}
713
714/*
715 * tunpoll - the poll interface, this is only useful on reads
716 * really. The write detect always returns true, write never blocks
717 * anyway, it either accepts the packet or drops it.
718 */
719static	int
720tunpoll(dev, events, p)
721	dev_t dev;
722	int events;
723	struct proc *p;
724{
725	int		s;
726	struct tun_softc *tp = dev->si_drv1;
727	struct ifnet	*ifp = &tp->tun_if;
728	int		revents = 0;
729
730	s = splimp();
731	TUNDEBUG("%s%d: tunpoll\n", ifp->if_name, ifp->if_unit);
732
733	if (events & (POLLIN | POLLRDNORM)) {
734		if (ifp->if_snd.ifq_len > 0) {
735			TUNDEBUG("%s%d: tunpoll q=%d\n", ifp->if_name,
736			    ifp->if_unit, ifp->if_snd.ifq_len);
737			revents |= events & (POLLIN | POLLRDNORM);
738		} else {
739			TUNDEBUG("%s%d: tunpoll waiting\n", ifp->if_name,
740			    ifp->if_unit);
741			selrecord(p, &tp->tun_rsel);
742		}
743	}
744	if (events & (POLLOUT | POLLWRNORM))
745		revents |= events & (POLLOUT | POLLWRNORM);
746
747	splx(s);
748	return (revents);
749}
750