port-tun.c revision 181110
1/*
2 * Copyright (c) 2005 Reyk Floeter <reyk@openbsd.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include "includes.h"
18
19#include <sys/types.h>
20#include <sys/ioctl.h>
21
22#include <netinet/in.h>
23#include <arpa/inet.h>
24#include <netinet/ip.h>
25
26#include <errno.h>
27#include <fcntl.h>
28#include <stdarg.h>
29#include <string.h>
30#include <unistd.h>
31
32#include "log.h"
33#include "misc.h"
34#include "buffer.h"
35#include "channels.h"
36
37/*
38 * This is the portable version of the SSH tunnel forwarding, it
39 * uses some preprocessor definitions for various platform-specific
40 * settings.
41 *
42 * SSH_TUN_LINUX	Use the (newer) Linux tun/tap device
43 * SSH_TUN_FREEBSD	Use the FreeBSD tun/tap device
44 * SSH_TUN_COMPAT_AF	Translate the OpenBSD address family
45 * SSH_TUN_PREPEND_AF	Prepend/remove the address family
46 */
47
48/*
49 * System-specific tunnel open function
50 */
51
52#if defined(SSH_TUN_LINUX)
53#include <linux/if.h>
54#include <linux/if_tun.h>
55
56int
57sys_tun_open(int tun, int mode)
58{
59	struct ifreq ifr;
60	int fd = -1;
61	const char *name = NULL;
62
63	if ((fd = open("/dev/net/tun", O_RDWR)) == -1) {
64		debug("%s: failed to open tunnel control interface: %s",
65		    __func__, strerror(errno));
66		return (-1);
67	}
68
69	bzero(&ifr, sizeof(ifr));
70
71	if (mode == SSH_TUNMODE_ETHERNET) {
72		ifr.ifr_flags = IFF_TAP;
73		name = "tap%d";
74	} else {
75		ifr.ifr_flags = IFF_TUN;
76		name = "tun%d";
77	}
78	ifr.ifr_flags |= IFF_NO_PI;
79
80	if (tun != SSH_TUNID_ANY) {
81		if (tun > SSH_TUNID_MAX) {
82			debug("%s: invalid tunnel id %x: %s", __func__,
83			    tun, strerror(errno));
84			goto failed;
85		}
86		snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), name, tun);
87	}
88
89	if (ioctl(fd, TUNSETIFF, &ifr) == -1) {
90		debug("%s: failed to configure tunnel (mode %d): %s", __func__,
91		    mode, strerror(errno));
92		goto failed;
93	}
94
95	if (tun == SSH_TUNID_ANY)
96		debug("%s: tunnel mode %d fd %d", __func__, mode, fd);
97	else
98		debug("%s: %s mode %d fd %d", __func__, ifr.ifr_name, mode, fd);
99
100	return (fd);
101
102 failed:
103	close(fd);
104	return (-1);
105}
106#endif /* SSH_TUN_LINUX */
107
108#ifdef SSH_TUN_FREEBSD
109#include <sys/socket.h>
110#include <net/if.h>
111
112#ifdef HAVE_NET_IF_TUN_H
113#include <net/if_tun.h>
114#endif
115
116int
117sys_tun_open(int tun, int mode)
118{
119	struct ifreq ifr;
120	char name[100];
121	int fd = -1, sock, flag;
122	const char *tunbase = "tun";
123
124	if (mode == SSH_TUNMODE_ETHERNET) {
125#ifdef SSH_TUN_NO_L2
126		debug("%s: no layer 2 tunnelling support", __func__);
127		return (-1);
128#else
129		tunbase = "tap";
130#endif
131	}
132
133	/* Open the tunnel device */
134	if (tun <= SSH_TUNID_MAX) {
135		snprintf(name, sizeof(name), "/dev/%s%d", tunbase, tun);
136		fd = open(name, O_RDWR);
137	} else if (tun == SSH_TUNID_ANY) {
138		for (tun = 100; tun >= 0; tun--) {
139			snprintf(name, sizeof(name), "/dev/%s%d",
140			    tunbase, tun);
141			if ((fd = open(name, O_RDWR)) >= 0)
142				break;
143		}
144	} else {
145		debug("%s: invalid tunnel %u\n", __func__, tun);
146		return (-1);
147	}
148
149	if (fd < 0) {
150		debug("%s: %s open failed: %s", __func__, name,
151		    strerror(errno));
152		return (-1);
153	}
154
155	/* Turn on tunnel headers */
156	flag = 1;
157#if defined(TUNSIFHEAD) && !defined(SSH_TUN_PREPEND_AF)
158	if (mode != SSH_TUNMODE_ETHERNET &&
159	    ioctl(fd, TUNSIFHEAD, &flag) == -1) {
160		debug("%s: ioctl(%d, TUNSIFHEAD, 1): %s", __func__, fd,
161		    strerror(errno));
162		close(fd);
163	}
164#endif
165
166	debug("%s: %s mode %d fd %d", __func__, name, mode, fd);
167
168	/* Set the tunnel device operation mode */
169	snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", tunbase, tun);
170	if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
171		goto failed;
172
173	if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1)
174		goto failed;
175	ifr.ifr_flags |= IFF_UP;
176	if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1)
177		goto failed;
178
179	close(sock);
180	return (fd);
181
182 failed:
183	if (fd >= 0)
184		close(fd);
185	if (sock >= 0)
186		close(sock);
187	debug("%s: failed to set %s mode %d: %s", __func__, name,
188	    mode, strerror(errno));
189	return (-1);
190}
191#endif /* SSH_TUN_FREEBSD */
192
193/*
194 * System-specific channel filters
195 */
196
197#if defined(SSH_TUN_FILTER)
198#define OPENBSD_AF_INET		2
199#define OPENBSD_AF_INET6	24
200
201int
202sys_tun_infilter(struct Channel *c, char *buf, int len)
203{
204#if defined(SSH_TUN_PREPEND_AF)
205	char rbuf[CHAN_RBUF];
206	struct ip *iph;
207#endif
208	u_int32_t *af;
209	char *ptr = buf;
210
211#if defined(SSH_TUN_PREPEND_AF)
212	if (len <= 0 || len > (int)(sizeof(rbuf) - sizeof(*af)))
213		return (-1);
214	ptr = (char *)&rbuf[0];
215	bcopy(buf, ptr + sizeof(u_int32_t), len);
216	len += sizeof(u_int32_t);
217	af = (u_int32_t *)ptr;
218
219	iph = (struct ip *)(ptr + sizeof(u_int32_t));
220	switch (iph->ip_v) {
221	case 6:
222		*af = AF_INET6;
223		break;
224	case 4:
225	default:
226		*af = AF_INET;
227		break;
228	}
229#endif
230
231#if defined(SSH_TUN_COMPAT_AF)
232	if (len < (int)sizeof(u_int32_t))
233		return (-1);
234
235	af = (u_int32_t *)ptr;
236	if (*af == htonl(AF_INET6))
237		*af = htonl(OPENBSD_AF_INET6);
238	else
239		*af = htonl(OPENBSD_AF_INET);
240#endif
241
242	buffer_put_string(&c->input, ptr, len);
243	return (0);
244}
245
246u_char *
247sys_tun_outfilter(struct Channel *c, u_char **data, u_int *dlen)
248{
249	u_char *buf;
250	u_int32_t *af;
251
252	*data = buffer_get_string(&c->output, dlen);
253	if (*dlen < sizeof(*af))
254		return (NULL);
255	buf = *data;
256
257#if defined(SSH_TUN_PREPEND_AF)
258	*dlen -= sizeof(u_int32_t);
259	buf = *data + sizeof(u_int32_t);
260#elif defined(SSH_TUN_COMPAT_AF)
261	af = ntohl(*(u_int32_t *)buf);
262	if (*af == OPENBSD_AF_INET6)
263		*af = htonl(AF_INET6);
264	else
265		*af = htonl(AF_INET);
266#endif
267
268	return (buf);
269}
270#endif /* SSH_TUN_FILTER */
271