Home | History | Annotate | Download | only in openbsd-compat
      1 /*
      2  * Copyright (c) 2005 Reyk Floeter <reyk (at) 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 
     58 int
     59 sys_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 
    118 int
    119 sys_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 
    205 int
    206 sys_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 
    252 u_char *
    253 sys_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