port-tun.c revision 323134
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 "openbsd-compat/sys-queue.h"
33#include "log.h"
34#include "misc.h"
35#include "sshbuf.h"
36#include "channels.h"
37#include "ssherr.h"
38
39/*
40 * This is the portable version of the SSH tunnel forwarding, it
41 * uses some preprocessor definitions for various platform-specific
42 * settings.
43 *
44 * SSH_TUN_LINUX	Use the (newer) Linux tun/tap device
45 * SSH_TUN_FREEBSD	Use the FreeBSD tun/tap device
46 * SSH_TUN_COMPAT_AF	Translate the OpenBSD address family
47 * SSH_TUN_PREPEND_AF	Prepend/remove the address family
48 */
49
50/*
51 * System-specific tunnel open function
52 */
53
54#if defined(SSH_TUN_LINUX)
55#include <linux/if.h>
56#include <linux/if_tun.h>
57
58int
59sys_tun_open(int tun, int mode)
60{
61	struct ifreq ifr;
62	int fd = -1;
63	const char *name = NULL;
64
65	if ((fd = open("/dev/net/tun", O_RDWR)) == -1) {
66		debug("%s: failed to open tunnel control interface: %s",
67		    __func__, strerror(errno));
68		return (-1);
69	}
70
71	bzero(&ifr, sizeof(ifr));
72
73	if (mode == SSH_TUNMODE_ETHERNET) {
74		ifr.ifr_flags = IFF_TAP;
75		name = "tap%d";
76	} else {
77		ifr.ifr_flags = IFF_TUN;
78		name = "tun%d";
79	}
80	ifr.ifr_flags |= IFF_NO_PI;
81
82	if (tun != SSH_TUNID_ANY) {
83		if (tun > SSH_TUNID_MAX) {
84			debug("%s: invalid tunnel id %x: %s", __func__,
85			    tun, strerror(errno));
86			goto failed;
87		}
88		snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), name, tun);
89	}
90
91	if (ioctl(fd, TUNSETIFF, &ifr) == -1) {
92		debug("%s: failed to configure tunnel (mode %d): %s", __func__,
93		    mode, strerror(errno));
94		goto failed;
95	}
96
97	if (tun == SSH_TUNID_ANY)
98		debug("%s: tunnel mode %d fd %d", __func__, mode, fd);
99	else
100		debug("%s: %s mode %d fd %d", __func__, ifr.ifr_name, mode, fd);
101
102	return (fd);
103
104 failed:
105	close(fd);
106	return (-1);
107}
108#endif /* SSH_TUN_LINUX */
109
110#ifdef SSH_TUN_FREEBSD
111#include <sys/socket.h>
112#include <net/if.h>
113
114#ifdef HAVE_NET_IF_TUN_H
115#include <net/if_tun.h>
116#endif
117
118int
119sys_tun_open(int tun, int mode)
120{
121	struct ifreq ifr;
122	char name[100];
123	int fd = -1, sock, flag;
124	const char *tunbase = "tun";
125
126	if (mode == SSH_TUNMODE_ETHERNET) {
127#ifdef SSH_TUN_NO_L2
128		debug("%s: no layer 2 tunnelling support", __func__);
129		return (-1);
130#else
131		tunbase = "tap";
132#endif
133	}
134
135	/* Open the tunnel device */
136	if (tun <= SSH_TUNID_MAX) {
137		snprintf(name, sizeof(name), "/dev/%s%d", tunbase, tun);
138		fd = open(name, O_RDWR);
139	} else if (tun == SSH_TUNID_ANY) {
140		for (tun = 100; tun >= 0; tun--) {
141			snprintf(name, sizeof(name), "/dev/%s%d",
142			    tunbase, tun);
143			if ((fd = open(name, O_RDWR)) >= 0)
144				break;
145		}
146	} else {
147		debug("%s: invalid tunnel %u\n", __func__, tun);
148		return (-1);
149	}
150
151	if (fd < 0) {
152		debug("%s: %s open failed: %s", __func__, name,
153		    strerror(errno));
154		return (-1);
155	}
156
157	/* Turn on tunnel headers */
158	flag = 1;
159#if defined(TUNSIFHEAD) && !defined(SSH_TUN_PREPEND_AF)
160	if (mode != SSH_TUNMODE_ETHERNET &&
161	    ioctl(fd, TUNSIFHEAD, &flag) == -1) {
162		debug("%s: ioctl(%d, TUNSIFHEAD, 1): %s", __func__, fd,
163		    strerror(errno));
164		close(fd);
165	}
166#endif
167
168	debug("%s: %s mode %d fd %d", __func__, name, mode, fd);
169
170	/* Set the tunnel device operation mode */
171	snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", tunbase, tun);
172	if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
173		goto failed;
174
175	if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1)
176		goto failed;
177	if ((ifr.ifr_flags & IFF_UP) == 0) {
178		ifr.ifr_flags |= IFF_UP;
179		if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1)
180			goto failed;
181	}
182
183	close(sock);
184	return (fd);
185
186 failed:
187	if (fd >= 0)
188		close(fd);
189	if (sock >= 0)
190		close(sock);
191	debug("%s: failed to set %s mode %d: %s", __func__, name,
192	    mode, strerror(errno));
193	return (-1);
194}
195#endif /* SSH_TUN_FREEBSD */
196
197/*
198 * System-specific channel filters
199 */
200
201#if defined(SSH_TUN_FILTER)
202#define OPENBSD_AF_INET		2
203#define OPENBSD_AF_INET6	24
204
205int
206sys_tun_infilter(struct Channel *c, char *buf, int len)
207{
208#if defined(SSH_TUN_PREPEND_AF)
209	char rbuf[CHAN_RBUF];
210	struct ip *iph;
211#endif
212	u_int32_t *af;
213	char *ptr = buf;
214	int r;
215
216#if defined(SSH_TUN_PREPEND_AF)
217	if (len <= 0 || len > (int)(sizeof(rbuf) - sizeof(*af)))
218		return (-1);
219	ptr = (char *)&rbuf[0];
220	bcopy(buf, ptr + sizeof(u_int32_t), len);
221	len += sizeof(u_int32_t);
222	af = (u_int32_t *)ptr;
223
224	iph = (struct ip *)(ptr + sizeof(u_int32_t));
225	switch (iph->ip_v) {
226	case 6:
227		*af = AF_INET6;
228		break;
229	case 4:
230	default:
231		*af = AF_INET;
232		break;
233	}
234#endif
235
236#if defined(SSH_TUN_COMPAT_AF)
237	if (len < (int)sizeof(u_int32_t))
238		return (-1);
239
240	af = (u_int32_t *)ptr;
241	if (*af == htonl(AF_INET6))
242		*af = htonl(OPENBSD_AF_INET6);
243	else
244		*af = htonl(OPENBSD_AF_INET);
245#endif
246
247	if ((r = sshbuf_put_string(&c->input, ptr, len)) != 0)
248		fatal("%s: buffer error: %s", __func__, ssh_err(r));
249	return (0);
250}
251
252u_char *
253sys_tun_outfilter(struct Channel *c, u_char **data, u_int *dlen)
254{
255	u_char *buf;
256	u_int32_t *af;
257	int r;
258	size_t xxx_dlen;
259
260	/* XXX new API is incompatible with this signature. */
261	if ((r = sshbuf_get_string(&c->output, data, &xxx_dlen)) != 0)
262		fatal("%s: buffer error: %s", __func__, ssh_err(r));
263	if (dlen != NULL)
264		*dlen = xxx_dlen;
265	if (*dlen < sizeof(*af))
266		return (NULL);
267	buf = *data;
268
269#if defined(SSH_TUN_PREPEND_AF)
270	*dlen -= sizeof(u_int32_t);
271	buf = *data + sizeof(u_int32_t);
272#elif defined(SSH_TUN_COMPAT_AF)
273	af = ntohl(*(u_int32_t *)buf);
274	if (*af == OPENBSD_AF_INET6)
275		*af = htonl(AF_INET6);
276	else
277		*af = htonl(AF_INET);
278#endif
279
280	return (buf);
281}
282#endif /* SSH_TUN_FILTER */
283