summaryrefslogtreecommitdiff
path: root/src/tuntap.h
blob: d5f398d01927ef4962e5fc2b80c357d5a6d3a5d1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
/*
 * ip tunnel/ethertap device for MacOSX.
 *
 * The class tuntaptap_interface contains the common functionality of tuntap_interface and
 * tap_interface.
 */
/*
 * Copyright (c) 2011 Mattias Nissler <mattias.nissler@gmx.de>
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
 *
 *   1. Redistributions of source code must retain the above copyright notice, this list of
 *      conditions and the following disclaimer.
 *   2. Redistributions in binary form must reproduce the above copyright notice, this list of
 *      conditions and the following disclaimer in the documentation and/or other materials provided
 *      with the distribution.
 *   3. The name of the author may not be used to endorse or promote products derived from this
 *      software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef __TUNTAP_H__
#define __TUNTAP_H__

#include "util.h"
#include "lock.h"

extern "C" {

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/systm.h>
#include <sys/kpi_mbuf.h>

#include <kern/locks.h>

#include <net/if.h>
#include <net/bpf.h>
#include <net/kpi_interface.h>

}

extern "C" {

errno_t tuntap_if_output(ifnet_t ifp, mbuf_t m);
errno_t tuntap_if_ioctl(ifnet_t ifp, long unsigned int cmd, void *arg);
errno_t tuntap_if_set_bpf_tap(ifnet_t ifp, bpf_tap_mode mode, int (*cb)(ifnet_t, mbuf_t));
errno_t tuntap_if_demux(ifnet_t ifp, mbuf_t m, char *header, protocol_family_t *proto);
errno_t tuntap_if_framer(ifnet_t ifp, mbuf_t *m, const struct sockaddr *dest,
		const char *dest_linkaddr, const char *frame_type);
errno_t tuntap_if_add_proto(ifnet_t ifp, protocol_family_t proto,
		const struct ifnet_demux_desc *ddesc, u_int32_t ndesc);
errno_t tuntap_if_del_proto(ifnet_t ifp, protocol_family_t proto);
errno_t tuntap_if_check_multi(ifnet_t ifp, const struct sockaddr *maddr);
void tuntap_if_detached(ifnet_t ifp);

}

/* forward declaration */
class tuntap_interface;

/* both interface families have their manager object that will create, initialize, shutdown and
 * delete interfaces. This is (mostly) generic so it can be used both for tun and tap. The only
 * exception is the interface creation, therefore this class is abstract. tun and tap have their own
 * versions that simply fill in create_interface().
 */
class tuntap_manager {

	protected:
		/* manager cdev gate */
		tt_gate cdev_gate;
		/* interface count */
		unsigned int count;
		/* an array holding all the interface instances */
		tuntap_interface **tuntaps;
		/* the major device number */
		int dev_major;
		/* family name */
		char *family;

		/* wether static members are initialized */
		static bool statics_initialized;

		/* major-to-manager-map */
		static const int MAX_CDEV = 256;
		static tuntap_manager *mgr_map[MAX_CDEV];

		/* initializes static members */
		void initialize_statics();

	public:
		/* sets major device number, allocates the interface table. */
		bool initialize(unsigned int count, char *family);

		/* tries to shutdown the family. returns true if successful. the manager object may
		 * not be deleted if this wasn't called successfully.
		 */
		bool shutdown();

		/* the destructor deletes allocated memory and unregisters the character device
		 * switch */
		virtual ~tuntap_manager();

		/* here are the cdev routines for the class. They will figure out the manager object
		 * and call the service methods declared below.
		 */
		static int cdev_open(dev_t dev, int flags, int devtype, proc_t p);
		static int cdev_close(dev_t dev, int flags, int devtype, proc_t p);
		static int cdev_read(dev_t dev, uio_t uio, int ioflag);
		static int cdev_write(dev_t dev, uio_t uio, int ioflag);
		static int cdev_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag,
				proc_t p);
		static int cdev_select(dev_t dev, int which, void *wql, proc_t p);

	protected:
		/* Here are the actual service routines that will do the required things (creating
		 * interfaces and such) and forward to the interface's implementation.
		 */
		int do_cdev_open(dev_t dev, int flags, int devtype, proc_t p);
		int do_cdev_close(dev_t dev, int flags, int devtype, proc_t p);
		int do_cdev_read(dev_t dev, uio_t uio, int ioflag);
		int do_cdev_write(dev_t dev, uio_t uio, int ioflag);
		int do_cdev_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, proc_t p);
		int do_cdev_select(dev_t dev, int which, void *wql, proc_t p);

		/* abstract method that will create an interface. Implemented by tun and tap */
		virtual tuntap_interface *create_interface() = 0;
		
		/* makes sure there is one idle interface available (if nothing fails */
		void ensure_idle_device();

};

/* a class implementing a mbuf packet queue. On Darwin 7 we had struct ifqueue, but that is now
 * internal to the kernel for Darwin 8. So lets have our own.
 */
class tuntap_mbuf_queue {

	private:
		/* output end of the queue. dequeueing takes mbufs from here */
		mbuf_t head;
		/* input end. new mbufs are appended here. */
		mbuf_t tail;

		/* size */
		unsigned int size;

		/* maximum queue size */
		static const unsigned int QUEUE_SIZE = 128;

	public:
		/* initialize new empty queue */
		tuntap_mbuf_queue();
		~tuntap_mbuf_queue();

		/* is the queue full? */
		bool full() { return size == QUEUE_SIZE; }
		/* is it emtpy? */
		bool empty() { return size == 0; }

		/* enqueue an mbuf. returns true if there was space left, so the mbuf could be
		 * queued, false otherwise */
		bool enqueue(mbuf_t mb);

		/* tries to dequeue the next mbuf. If the queue is empty, NULL is returned */
		mbuf_t dequeue();

		/* makes the queue empty, discarding any queue packets */
		void clear();
};

class tuntap_interface {

	protected:
		/* interface number */
		unsigned int unit;
		/* family name */
		char *family_name;
		/* family identifier */
		ifnet_family_t family;
		/* interface type */
		u_int32_t type;
		/* id string */
		static const unsigned int UIDLEN = 20;
		char unique_id[UIDLEN];

		/* synchronization */
		tt_mutex lock;
		tt_mutex bpf_lock;
		tt_mutex thread_sync_lock;

		/* the interface structure registered */
		ifnet_t ifp;
		/* whether the device has been opened */
		bool open;
		/* whether we are doing blocking i/o */
		bool block_io;
		/* whether the interface has properly been detached */
		bool interface_detached;
		/* handle to the devfs node for the character device */
		void *dev_handle;
		/* the pid of the process that opened the cdev, if any */
		pid_t pid;
		/* read select info */
		struct selinfo rsel;
		/* bpf mode, wether filtering is on or off */
		bpf_tap_mode bpf_mode;
		/* bpf callback. called when packet arrives/leaves */
		int (*bpf_callback)(ifnet_t, mbuf_t);
		/* pending packets queue (for output), must be accessed with the lock held */
		tuntap_mbuf_queue send_queue;
		/* whether an ioctl that we issued is currently being processed */
		bool in_ioctl;

		/* protected constructor. initializes most of the members */
		tuntap_interface();
		virtual ~tuntap_interface();

		/* initialize the device */
		virtual bool initialize(unsigned short major, unsigned short unit) = 0;

		/* character device management */
		virtual bool register_chardev(unsigned short major);
		virtual void unregister_chardev();

		/* network interface management */
		virtual bool register_interface(const struct sockaddr_dl *lladdr,
				void *bcaddr, u_int32_t bcaddrlen);
		virtual void unregister_interface();
		virtual void cleanup_interface();

		/* called when the character device is opened in order to intialize the network
		 * interface.
		 */
		virtual int initialize_interface() = 0;
		/* called when the character device is closed to shutdown the network interface */
		virtual void shutdown_interface() = 0;

		/* check wether the interface is idle (so it can be brought down) */
		virtual bool idle();

		/* shut it down */
		virtual void shutdown() = 0;

		/* notifies BPF of a packet coming through */
		virtual void notify_bpf(mbuf_t mb, bool out);

		/* executes a socket ioctl through a temporary socket */
		virtual void do_sock_ioctl(sa_family_t af, unsigned long cmd, void* arg);

		/* character device service methods. Called by the manager */
		virtual int cdev_open(int flags, int devtype, proc_t p);
		virtual int cdev_close(int flags, int devtype, proc_t p);
		virtual int cdev_read(uio_t uio, int ioflag);
		virtual int cdev_write(uio_t uio, int ioflag);
		virtual int cdev_ioctl(u_long cmd, caddr_t data, int fflag, proc_t p);
		virtual int cdev_select(int which, void *wql, proc_t p);

		/* interface functions. friends and implementation methods */
		friend errno_t tuntap_if_output(ifnet_t ifp, mbuf_t m);
		friend errno_t tuntap_if_ioctl(ifnet_t ifp, long unsigned int cmd, void *arg);
		friend errno_t tuntap_if_set_bpf_tap(ifnet_t ifp, bpf_tap_mode mode,
				int (*cb)(ifnet_t, mbuf_t));
		friend errno_t tuntap_if_demux(ifnet_t ifp, mbuf_t m, char *header,
				protocol_family_t *proto);
		friend errno_t tuntap_if_framer(ifnet_t ifp, mbuf_t *m, const struct sockaddr *dest,
				const char *dest_linkaddr, const char *frame_type);
		friend errno_t tuntap_if_add_proto(ifnet_t ifp, protocol_family_t proto,
				const struct ifnet_demux_desc *ddesc, u_int32_t ndesc);
		friend errno_t tuntap_if_del_proto(ifnet_t ifp, protocol_family_t proto);
		friend errno_t tuntap_if_check_multi(ifnet_t ifp, const struct sockaddr *maddr);
		friend void tuntap_if_detached(ifnet_t ifp);

		virtual errno_t if_output(mbuf_t m);
		virtual errno_t if_ioctl(u_int32_t cmd, void *arg);
		virtual errno_t if_set_bpf_tap(bpf_tap_mode mode, int (*cb)(ifnet_t, mbuf_t));
		virtual errno_t if_demux(mbuf_t m, char *header, protocol_family_t *proto) = 0;
		virtual errno_t if_framer(mbuf_t *m, const struct sockaddr *dest,
				const char *dest_linkaddr, const char *frame_type) = 0;
		virtual errno_t if_add_proto(protocol_family_t proto,
				const struct ifnet_demux_desc *ddesc, u_int32_t ndesc) = 0;
		virtual errno_t if_del_proto(protocol_family_t proto) = 0;
		virtual errno_t if_check_multi(const struct sockaddr *maddr);
		virtual void if_detached();

		/* tuntap_manager feeds us with cdev input, so it is our friend */
		friend class tuntap_manager;
};

#endif /* __TUNTAP_H__ */