xref: /freebsd/crypto/openssh/openbsd-compat/port-net.c (revision f2a2dfa72920e6c5c03165c8866ce0d10d78e053)
147dd1d1bSDag-Erling Smørgrav /*
247dd1d1bSDag-Erling Smørgrav  * Copyright (c) 2005 Reyk Floeter <reyk@openbsd.org>
347dd1d1bSDag-Erling Smørgrav  *
447dd1d1bSDag-Erling Smørgrav  * Permission to use, copy, modify, and distribute this software for any
547dd1d1bSDag-Erling Smørgrav  * purpose with or without fee is hereby granted, provided that the above
647dd1d1bSDag-Erling Smørgrav  * copyright notice and this permission notice appear in all copies.
747dd1d1bSDag-Erling Smørgrav  *
847dd1d1bSDag-Erling Smørgrav  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
947dd1d1bSDag-Erling Smørgrav  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1047dd1d1bSDag-Erling Smørgrav  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1147dd1d1bSDag-Erling Smørgrav  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1247dd1d1bSDag-Erling Smørgrav  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1347dd1d1bSDag-Erling Smørgrav  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1447dd1d1bSDag-Erling Smørgrav  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1547dd1d1bSDag-Erling Smørgrav  */
1647dd1d1bSDag-Erling Smørgrav 
1747dd1d1bSDag-Erling Smørgrav #include "includes.h"
1847dd1d1bSDag-Erling Smørgrav 
1947dd1d1bSDag-Erling Smørgrav #include <sys/types.h>
2047dd1d1bSDag-Erling Smørgrav #include <sys/ioctl.h>
2147dd1d1bSDag-Erling Smørgrav 
2247dd1d1bSDag-Erling Smørgrav #include <netinet/in.h>
2347dd1d1bSDag-Erling Smørgrav #include <arpa/inet.h>
2447dd1d1bSDag-Erling Smørgrav #include <netinet/ip.h>
2547dd1d1bSDag-Erling Smørgrav 
2647dd1d1bSDag-Erling Smørgrav #include <errno.h>
2747dd1d1bSDag-Erling Smørgrav #include <fcntl.h>
2847dd1d1bSDag-Erling Smørgrav #include <stdarg.h>
2947dd1d1bSDag-Erling Smørgrav #include <string.h>
3047dd1d1bSDag-Erling Smørgrav #include <unistd.h>
3147dd1d1bSDag-Erling Smørgrav 
3247dd1d1bSDag-Erling Smørgrav #include "openbsd-compat/sys-queue.h"
3347dd1d1bSDag-Erling Smørgrav #include "log.h"
3447dd1d1bSDag-Erling Smørgrav #include "misc.h"
3547dd1d1bSDag-Erling Smørgrav #include "sshbuf.h"
3647dd1d1bSDag-Erling Smørgrav #include "channels.h"
3747dd1d1bSDag-Erling Smørgrav #include "ssherr.h"
3847dd1d1bSDag-Erling Smørgrav 
3947dd1d1bSDag-Erling Smørgrav /*
4047dd1d1bSDag-Erling Smørgrav  * This file contains various portability code for network support,
4147dd1d1bSDag-Erling Smørgrav  * including tun/tap forwarding and routing domains.
4247dd1d1bSDag-Erling Smørgrav  */
4347dd1d1bSDag-Erling Smørgrav 
4447dd1d1bSDag-Erling Smørgrav #if defined(SYS_RDOMAIN_LINUX) || defined(SSH_TUN_LINUX)
4547dd1d1bSDag-Erling Smørgrav #include <linux/if.h>
4647dd1d1bSDag-Erling Smørgrav #endif
4747dd1d1bSDag-Erling Smørgrav 
4847dd1d1bSDag-Erling Smørgrav #if defined(SYS_RDOMAIN_LINUX)
4947dd1d1bSDag-Erling Smørgrav char *
5047dd1d1bSDag-Erling Smørgrav sys_get_rdomain(int fd)
5147dd1d1bSDag-Erling Smørgrav {
5247dd1d1bSDag-Erling Smørgrav 	char dev[IFNAMSIZ + 1];
5347dd1d1bSDag-Erling Smørgrav 	socklen_t len = sizeof(dev) - 1;
5447dd1d1bSDag-Erling Smørgrav 
5547dd1d1bSDag-Erling Smørgrav 	if (getsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, dev, &len) == -1) {
5647dd1d1bSDag-Erling Smørgrav 		error("%s: cannot determine VRF for fd=%d : %s",
5747dd1d1bSDag-Erling Smørgrav 		    __func__, fd, strerror(errno));
5847dd1d1bSDag-Erling Smørgrav 		return NULL;
5947dd1d1bSDag-Erling Smørgrav 	}
6047dd1d1bSDag-Erling Smørgrav 	dev[len] = '\0';
6147dd1d1bSDag-Erling Smørgrav 	return strdup(dev);
6247dd1d1bSDag-Erling Smørgrav }
6347dd1d1bSDag-Erling Smørgrav 
6447dd1d1bSDag-Erling Smørgrav int
6547dd1d1bSDag-Erling Smørgrav sys_set_rdomain(int fd, const char *name)
6647dd1d1bSDag-Erling Smørgrav {
6747dd1d1bSDag-Erling Smørgrav 	if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
6847dd1d1bSDag-Erling Smørgrav 	    name, strlen(name)) == -1) {
6947dd1d1bSDag-Erling Smørgrav 		error("%s: setsockopt(%d, SO_BINDTODEVICE, %s): %s",
7047dd1d1bSDag-Erling Smørgrav 		      __func__, fd, name, strerror(errno));
7147dd1d1bSDag-Erling Smørgrav 		return -1;
7247dd1d1bSDag-Erling Smørgrav 	}
7347dd1d1bSDag-Erling Smørgrav 	return 0;
7447dd1d1bSDag-Erling Smørgrav }
7547dd1d1bSDag-Erling Smørgrav 
7647dd1d1bSDag-Erling Smørgrav int
7747dd1d1bSDag-Erling Smørgrav sys_valid_rdomain(const char *name)
7847dd1d1bSDag-Erling Smørgrav {
7947dd1d1bSDag-Erling Smørgrav 	int fd;
8047dd1d1bSDag-Erling Smørgrav 
8147dd1d1bSDag-Erling Smørgrav 	/*
8247dd1d1bSDag-Erling Smørgrav 	 * This is a pretty crappy way to test. It would be better to
8347dd1d1bSDag-Erling Smørgrav 	 * check whether "name" represents a VRF device, but apparently
8447dd1d1bSDag-Erling Smørgrav 	 * that requires an rtnetlink transaction.
8547dd1d1bSDag-Erling Smørgrav 	 */
8647dd1d1bSDag-Erling Smørgrav 	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
8747dd1d1bSDag-Erling Smørgrav 		return 0;
8847dd1d1bSDag-Erling Smørgrav 	if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
8947dd1d1bSDag-Erling Smørgrav 	    name, strlen(name)) == -1) {
9047dd1d1bSDag-Erling Smørgrav 		close(fd);
9147dd1d1bSDag-Erling Smørgrav 		return 0;
9247dd1d1bSDag-Erling Smørgrav 	}
9347dd1d1bSDag-Erling Smørgrav 	close(fd);
9447dd1d1bSDag-Erling Smørgrav 	return 1;
9547dd1d1bSDag-Erling Smørgrav }
9647dd1d1bSDag-Erling Smørgrav #elif defined(SYS_RDOMAIN_XXX)
9747dd1d1bSDag-Erling Smørgrav /* XXX examples */
9847dd1d1bSDag-Erling Smørgrav char *
9947dd1d1bSDag-Erling Smørgrav sys_get_rdomain(int fd)
10047dd1d1bSDag-Erling Smørgrav {
10147dd1d1bSDag-Erling Smørgrav 	return NULL;
10247dd1d1bSDag-Erling Smørgrav }
10347dd1d1bSDag-Erling Smørgrav 
10447dd1d1bSDag-Erling Smørgrav int
10547dd1d1bSDag-Erling Smørgrav sys_set_rdomain(int fd, const char *name)
10647dd1d1bSDag-Erling Smørgrav {
10747dd1d1bSDag-Erling Smørgrav 	return -1;
10847dd1d1bSDag-Erling Smørgrav }
10947dd1d1bSDag-Erling Smørgrav 
11047dd1d1bSDag-Erling Smørgrav int
11147dd1d1bSDag-Erling Smørgrav valid_rdomain(const char *name)
11247dd1d1bSDag-Erling Smørgrav {
11347dd1d1bSDag-Erling Smørgrav 	return 0;
11447dd1d1bSDag-Erling Smørgrav }
11547dd1d1bSDag-Erling Smørgrav 
11647dd1d1bSDag-Erling Smørgrav void
11747dd1d1bSDag-Erling Smørgrav sys_set_process_rdomain(const char *name)
11847dd1d1bSDag-Erling Smørgrav {
11947dd1d1bSDag-Erling Smørgrav 	fatal("%s: not supported", __func__);
12047dd1d1bSDag-Erling Smørgrav }
12147dd1d1bSDag-Erling Smørgrav #endif /* defined(SYS_RDOMAIN_XXX) */
12247dd1d1bSDag-Erling Smørgrav 
12347dd1d1bSDag-Erling Smørgrav /*
12447dd1d1bSDag-Erling Smørgrav  * This is the portable version of the SSH tunnel forwarding, it
12547dd1d1bSDag-Erling Smørgrav  * uses some preprocessor definitions for various platform-specific
12647dd1d1bSDag-Erling Smørgrav  * settings.
12747dd1d1bSDag-Erling Smørgrav  *
12847dd1d1bSDag-Erling Smørgrav  * SSH_TUN_LINUX	Use the (newer) Linux tun/tap device
12947dd1d1bSDag-Erling Smørgrav  * SSH_TUN_FREEBSD	Use the FreeBSD tun/tap device
13047dd1d1bSDag-Erling Smørgrav  * SSH_TUN_COMPAT_AF	Translate the OpenBSD address family
13147dd1d1bSDag-Erling Smørgrav  * SSH_TUN_PREPEND_AF	Prepend/remove the address family
13247dd1d1bSDag-Erling Smørgrav  */
13347dd1d1bSDag-Erling Smørgrav 
13447dd1d1bSDag-Erling Smørgrav /*
13547dd1d1bSDag-Erling Smørgrav  * System-specific tunnel open function
13647dd1d1bSDag-Erling Smørgrav  */
13747dd1d1bSDag-Erling Smørgrav 
13847dd1d1bSDag-Erling Smørgrav #if defined(SSH_TUN_LINUX)
13947dd1d1bSDag-Erling Smørgrav #include <linux/if_tun.h>
14047dd1d1bSDag-Erling Smørgrav 
14147dd1d1bSDag-Erling Smørgrav int
14247dd1d1bSDag-Erling Smørgrav sys_tun_open(int tun, int mode, char **ifname)
14347dd1d1bSDag-Erling Smørgrav {
14447dd1d1bSDag-Erling Smørgrav 	struct ifreq ifr;
14547dd1d1bSDag-Erling Smørgrav 	int fd = -1;
14647dd1d1bSDag-Erling Smørgrav 	const char *name = NULL;
14747dd1d1bSDag-Erling Smørgrav 
14847dd1d1bSDag-Erling Smørgrav 	if (ifname != NULL)
14947dd1d1bSDag-Erling Smørgrav 		*ifname = NULL;
15047dd1d1bSDag-Erling Smørgrav 
15147dd1d1bSDag-Erling Smørgrav 	if ((fd = open("/dev/net/tun", O_RDWR)) == -1) {
15247dd1d1bSDag-Erling Smørgrav 		debug("%s: failed to open tunnel control interface: %s",
15347dd1d1bSDag-Erling Smørgrav 		    __func__, strerror(errno));
15447dd1d1bSDag-Erling Smørgrav 		return (-1);
15547dd1d1bSDag-Erling Smørgrav 	}
15647dd1d1bSDag-Erling Smørgrav 
15747dd1d1bSDag-Erling Smørgrav 	bzero(&ifr, sizeof(ifr));
15847dd1d1bSDag-Erling Smørgrav 
15947dd1d1bSDag-Erling Smørgrav 	if (mode == SSH_TUNMODE_ETHERNET) {
16047dd1d1bSDag-Erling Smørgrav 		ifr.ifr_flags = IFF_TAP;
16147dd1d1bSDag-Erling Smørgrav 		name = "tap%d";
16247dd1d1bSDag-Erling Smørgrav 	} else {
16347dd1d1bSDag-Erling Smørgrav 		ifr.ifr_flags = IFF_TUN;
16447dd1d1bSDag-Erling Smørgrav 		name = "tun%d";
16547dd1d1bSDag-Erling Smørgrav 	}
16647dd1d1bSDag-Erling Smørgrav 	ifr.ifr_flags |= IFF_NO_PI;
16747dd1d1bSDag-Erling Smørgrav 
16847dd1d1bSDag-Erling Smørgrav 	if (tun != SSH_TUNID_ANY) {
16947dd1d1bSDag-Erling Smørgrav 		if (tun > SSH_TUNID_MAX) {
17047dd1d1bSDag-Erling Smørgrav 			debug("%s: invalid tunnel id %x: %s", __func__,
17147dd1d1bSDag-Erling Smørgrav 			    tun, strerror(errno));
17247dd1d1bSDag-Erling Smørgrav 			goto failed;
17347dd1d1bSDag-Erling Smørgrav 		}
17447dd1d1bSDag-Erling Smørgrav 		snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), name, tun);
17547dd1d1bSDag-Erling Smørgrav 	}
17647dd1d1bSDag-Erling Smørgrav 
17747dd1d1bSDag-Erling Smørgrav 	if (ioctl(fd, TUNSETIFF, &ifr) == -1) {
17847dd1d1bSDag-Erling Smørgrav 		debug("%s: failed to configure tunnel (mode %d): %s", __func__,
17947dd1d1bSDag-Erling Smørgrav 		    mode, strerror(errno));
18047dd1d1bSDag-Erling Smørgrav 		goto failed;
18147dd1d1bSDag-Erling Smørgrav 	}
18247dd1d1bSDag-Erling Smørgrav 
18347dd1d1bSDag-Erling Smørgrav 	if (tun == SSH_TUNID_ANY)
18447dd1d1bSDag-Erling Smørgrav 		debug("%s: tunnel mode %d fd %d", __func__, mode, fd);
18547dd1d1bSDag-Erling Smørgrav 	else
18647dd1d1bSDag-Erling Smørgrav 		debug("%s: %s mode %d fd %d", __func__, ifr.ifr_name, mode, fd);
18747dd1d1bSDag-Erling Smørgrav 
188*f2a2dfa7SDag-Erling Smørgrav 	if (ifname != NULL && (*ifname = strdup(ifr.ifr_name)) == NULL)
18947dd1d1bSDag-Erling Smørgrav 		goto failed;
19047dd1d1bSDag-Erling Smørgrav 
19147dd1d1bSDag-Erling Smørgrav 	return (fd);
19247dd1d1bSDag-Erling Smørgrav 
19347dd1d1bSDag-Erling Smørgrav  failed:
19447dd1d1bSDag-Erling Smørgrav 	close(fd);
19547dd1d1bSDag-Erling Smørgrav 	return (-1);
19647dd1d1bSDag-Erling Smørgrav }
19747dd1d1bSDag-Erling Smørgrav #endif /* SSH_TUN_LINUX */
19847dd1d1bSDag-Erling Smørgrav 
19947dd1d1bSDag-Erling Smørgrav #ifdef SSH_TUN_FREEBSD
20047dd1d1bSDag-Erling Smørgrav #include <sys/socket.h>
20147dd1d1bSDag-Erling Smørgrav #include <net/if.h>
20247dd1d1bSDag-Erling Smørgrav 
20347dd1d1bSDag-Erling Smørgrav #ifdef HAVE_NET_IF_TUN_H
20447dd1d1bSDag-Erling Smørgrav #include <net/if_tun.h>
20547dd1d1bSDag-Erling Smørgrav #endif
20647dd1d1bSDag-Erling Smørgrav 
20747dd1d1bSDag-Erling Smørgrav int
20847dd1d1bSDag-Erling Smørgrav sys_tun_open(int tun, int mode, char **ifname)
20947dd1d1bSDag-Erling Smørgrav {
21047dd1d1bSDag-Erling Smørgrav 	struct ifreq ifr;
21147dd1d1bSDag-Erling Smørgrav 	char name[100];
21247dd1d1bSDag-Erling Smørgrav 	int fd = -1, sock, flag;
21347dd1d1bSDag-Erling Smørgrav 	const char *tunbase = "tun";
21447dd1d1bSDag-Erling Smørgrav 
21547dd1d1bSDag-Erling Smørgrav 	if (ifname != NULL)
21647dd1d1bSDag-Erling Smørgrav 		*ifname = NULL;
21747dd1d1bSDag-Erling Smørgrav 
21847dd1d1bSDag-Erling Smørgrav 	if (mode == SSH_TUNMODE_ETHERNET) {
21947dd1d1bSDag-Erling Smørgrav #ifdef SSH_TUN_NO_L2
22047dd1d1bSDag-Erling Smørgrav 		debug("%s: no layer 2 tunnelling support", __func__);
22147dd1d1bSDag-Erling Smørgrav 		return (-1);
22247dd1d1bSDag-Erling Smørgrav #else
22347dd1d1bSDag-Erling Smørgrav 		tunbase = "tap";
22447dd1d1bSDag-Erling Smørgrav #endif
22547dd1d1bSDag-Erling Smørgrav 	}
22647dd1d1bSDag-Erling Smørgrav 
22747dd1d1bSDag-Erling Smørgrav 	/* Open the tunnel device */
22847dd1d1bSDag-Erling Smørgrav 	if (tun <= SSH_TUNID_MAX) {
22947dd1d1bSDag-Erling Smørgrav 		snprintf(name, sizeof(name), "/dev/%s%d", tunbase, tun);
23047dd1d1bSDag-Erling Smørgrav 		fd = open(name, O_RDWR);
23147dd1d1bSDag-Erling Smørgrav 	} else if (tun == SSH_TUNID_ANY) {
23247dd1d1bSDag-Erling Smørgrav 		for (tun = 100; tun >= 0; tun--) {
23347dd1d1bSDag-Erling Smørgrav 			snprintf(name, sizeof(name), "/dev/%s%d",
23447dd1d1bSDag-Erling Smørgrav 			    tunbase, tun);
23547dd1d1bSDag-Erling Smørgrav 			if ((fd = open(name, O_RDWR)) >= 0)
23647dd1d1bSDag-Erling Smørgrav 				break;
23747dd1d1bSDag-Erling Smørgrav 		}
23847dd1d1bSDag-Erling Smørgrav 	} else {
23947dd1d1bSDag-Erling Smørgrav 		debug("%s: invalid tunnel %u\n", __func__, tun);
24047dd1d1bSDag-Erling Smørgrav 		return (-1);
24147dd1d1bSDag-Erling Smørgrav 	}
24247dd1d1bSDag-Erling Smørgrav 
24347dd1d1bSDag-Erling Smørgrav 	if (fd < 0) {
24447dd1d1bSDag-Erling Smørgrav 		debug("%s: %s open failed: %s", __func__, name,
24547dd1d1bSDag-Erling Smørgrav 		    strerror(errno));
24647dd1d1bSDag-Erling Smørgrav 		return (-1);
24747dd1d1bSDag-Erling Smørgrav 	}
24847dd1d1bSDag-Erling Smørgrav 
24947dd1d1bSDag-Erling Smørgrav 	/* Turn on tunnel headers */
25047dd1d1bSDag-Erling Smørgrav 	flag = 1;
25147dd1d1bSDag-Erling Smørgrav #if defined(TUNSIFHEAD) && !defined(SSH_TUN_PREPEND_AF)
25247dd1d1bSDag-Erling Smørgrav 	if (mode != SSH_TUNMODE_ETHERNET &&
25347dd1d1bSDag-Erling Smørgrav 	    ioctl(fd, TUNSIFHEAD, &flag) == -1) {
25447dd1d1bSDag-Erling Smørgrav 		debug("%s: ioctl(%d, TUNSIFHEAD, 1): %s", __func__, fd,
25547dd1d1bSDag-Erling Smørgrav 		    strerror(errno));
25647dd1d1bSDag-Erling Smørgrav 		close(fd);
25747dd1d1bSDag-Erling Smørgrav 	}
25847dd1d1bSDag-Erling Smørgrav #endif
25947dd1d1bSDag-Erling Smørgrav 
26047dd1d1bSDag-Erling Smørgrav 	debug("%s: %s mode %d fd %d", __func__, name, mode, fd);
26147dd1d1bSDag-Erling Smørgrav 
26247dd1d1bSDag-Erling Smørgrav 	/* Set the tunnel device operation mode */
26347dd1d1bSDag-Erling Smørgrav 	snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", tunbase, tun);
26447dd1d1bSDag-Erling Smørgrav 	if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
26547dd1d1bSDag-Erling Smørgrav 		goto failed;
26647dd1d1bSDag-Erling Smørgrav 
26747dd1d1bSDag-Erling Smørgrav 	if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1)
26847dd1d1bSDag-Erling Smørgrav 		goto failed;
26947dd1d1bSDag-Erling Smørgrav 	if ((ifr.ifr_flags & IFF_UP) == 0) {
27047dd1d1bSDag-Erling Smørgrav 		ifr.ifr_flags |= IFF_UP;
27147dd1d1bSDag-Erling Smørgrav 		if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1)
27247dd1d1bSDag-Erling Smørgrav 			goto failed;
27347dd1d1bSDag-Erling Smørgrav 	}
27447dd1d1bSDag-Erling Smørgrav 
275*f2a2dfa7SDag-Erling Smørgrav 	if (ifname != NULL && (*ifname = strdup(ifr.ifr_name)) == NULL)
27647dd1d1bSDag-Erling Smørgrav 		goto failed;
27747dd1d1bSDag-Erling Smørgrav 
27847dd1d1bSDag-Erling Smørgrav 	close(sock);
27947dd1d1bSDag-Erling Smørgrav 	return (fd);
28047dd1d1bSDag-Erling Smørgrav 
28147dd1d1bSDag-Erling Smørgrav  failed:
28247dd1d1bSDag-Erling Smørgrav 	if (fd >= 0)
28347dd1d1bSDag-Erling Smørgrav 		close(fd);
28447dd1d1bSDag-Erling Smørgrav 	if (sock >= 0)
28547dd1d1bSDag-Erling Smørgrav 		close(sock);
28647dd1d1bSDag-Erling Smørgrav 	debug("%s: failed to set %s mode %d: %s", __func__, name,
28747dd1d1bSDag-Erling Smørgrav 	    mode, strerror(errno));
28847dd1d1bSDag-Erling Smørgrav 	return (-1);
28947dd1d1bSDag-Erling Smørgrav }
29047dd1d1bSDag-Erling Smørgrav #endif /* SSH_TUN_FREEBSD */
29147dd1d1bSDag-Erling Smørgrav 
29247dd1d1bSDag-Erling Smørgrav /*
29347dd1d1bSDag-Erling Smørgrav  * System-specific channel filters
29447dd1d1bSDag-Erling Smørgrav  */
29547dd1d1bSDag-Erling Smørgrav 
29647dd1d1bSDag-Erling Smørgrav #if defined(SSH_TUN_FILTER)
29747dd1d1bSDag-Erling Smørgrav /*
29847dd1d1bSDag-Erling Smørgrav  * The tunnel forwarding protocol prepends the address family of forwarded
29947dd1d1bSDag-Erling Smørgrav  * IP packets using OpenBSD's numbers.
30047dd1d1bSDag-Erling Smørgrav  */
30147dd1d1bSDag-Erling Smørgrav #define OPENBSD_AF_INET		2
30247dd1d1bSDag-Erling Smørgrav #define OPENBSD_AF_INET6	24
30347dd1d1bSDag-Erling Smørgrav 
30447dd1d1bSDag-Erling Smørgrav int
30547dd1d1bSDag-Erling Smørgrav sys_tun_infilter(struct ssh *ssh, struct Channel *c, char *buf, int _len)
30647dd1d1bSDag-Erling Smørgrav {
30747dd1d1bSDag-Erling Smørgrav 	int r;
30847dd1d1bSDag-Erling Smørgrav 	size_t len;
30947dd1d1bSDag-Erling Smørgrav 	char *ptr = buf;
31047dd1d1bSDag-Erling Smørgrav #if defined(SSH_TUN_PREPEND_AF)
31147dd1d1bSDag-Erling Smørgrav 	char rbuf[CHAN_RBUF];
31247dd1d1bSDag-Erling Smørgrav 	struct ip iph;
31347dd1d1bSDag-Erling Smørgrav #endif
31447dd1d1bSDag-Erling Smørgrav #if defined(SSH_TUN_PREPEND_AF) || defined(SSH_TUN_COMPAT_AF)
31547dd1d1bSDag-Erling Smørgrav 	u_int32_t af;
31647dd1d1bSDag-Erling Smørgrav #endif
31747dd1d1bSDag-Erling Smørgrav 
31847dd1d1bSDag-Erling Smørgrav 	/* XXX update channel input filter API to use unsigned length */
31947dd1d1bSDag-Erling Smørgrav 	if (_len < 0)
32047dd1d1bSDag-Erling Smørgrav 		return -1;
32147dd1d1bSDag-Erling Smørgrav 	len = _len;
32247dd1d1bSDag-Erling Smørgrav 
32347dd1d1bSDag-Erling Smørgrav #if defined(SSH_TUN_PREPEND_AF)
32447dd1d1bSDag-Erling Smørgrav 	if (len <= sizeof(iph) || len > sizeof(rbuf) - 4)
32547dd1d1bSDag-Erling Smørgrav 		return -1;
32647dd1d1bSDag-Erling Smørgrav 	/* Determine address family from packet IP header. */
32747dd1d1bSDag-Erling Smørgrav 	memcpy(&iph, buf, sizeof(iph));
32847dd1d1bSDag-Erling Smørgrav 	af = iph.ip_v == 6 ? OPENBSD_AF_INET6 : OPENBSD_AF_INET;
32947dd1d1bSDag-Erling Smørgrav 	/* Prepend address family to packet using OpenBSD constants */
33047dd1d1bSDag-Erling Smørgrav 	memcpy(rbuf + 4, buf, len);
33147dd1d1bSDag-Erling Smørgrav 	len += 4;
33247dd1d1bSDag-Erling Smørgrav 	POKE_U32(rbuf, af);
33347dd1d1bSDag-Erling Smørgrav 	ptr = rbuf;
33447dd1d1bSDag-Erling Smørgrav #elif defined(SSH_TUN_COMPAT_AF)
33547dd1d1bSDag-Erling Smørgrav 	/* Convert existing address family header to OpenBSD value */
33647dd1d1bSDag-Erling Smørgrav 	if (len <= 4)
33747dd1d1bSDag-Erling Smørgrav 		return -1;
33847dd1d1bSDag-Erling Smørgrav 	af = PEEK_U32(buf);
33947dd1d1bSDag-Erling Smørgrav 	/* Put it back */
34047dd1d1bSDag-Erling Smørgrav 	POKE_U32(buf, af == AF_INET6 ? OPENBSD_AF_INET6 : OPENBSD_AF_INET);
34147dd1d1bSDag-Erling Smørgrav #endif
34247dd1d1bSDag-Erling Smørgrav 
34347dd1d1bSDag-Erling Smørgrav 	if ((r = sshbuf_put_string(c->input, ptr, len)) != 0)
34447dd1d1bSDag-Erling Smørgrav 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
34547dd1d1bSDag-Erling Smørgrav 	return (0);
34647dd1d1bSDag-Erling Smørgrav }
34747dd1d1bSDag-Erling Smørgrav 
34847dd1d1bSDag-Erling Smørgrav u_char *
34947dd1d1bSDag-Erling Smørgrav sys_tun_outfilter(struct ssh *ssh, struct Channel *c,
35047dd1d1bSDag-Erling Smørgrav     u_char **data, size_t *dlen)
35147dd1d1bSDag-Erling Smørgrav {
35247dd1d1bSDag-Erling Smørgrav 	u_char *buf;
35347dd1d1bSDag-Erling Smørgrav 	u_int32_t af;
35447dd1d1bSDag-Erling Smørgrav 	int r;
35547dd1d1bSDag-Erling Smørgrav 
35647dd1d1bSDag-Erling Smørgrav 	/* XXX new API is incompatible with this signature. */
35747dd1d1bSDag-Erling Smørgrav 	if ((r = sshbuf_get_string(c->output, data, dlen)) != 0)
35847dd1d1bSDag-Erling Smørgrav 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
35947dd1d1bSDag-Erling Smørgrav 	if (*dlen < sizeof(af))
36047dd1d1bSDag-Erling Smørgrav 		return (NULL);
36147dd1d1bSDag-Erling Smørgrav 	buf = *data;
36247dd1d1bSDag-Erling Smørgrav 
36347dd1d1bSDag-Erling Smørgrav #if defined(SSH_TUN_PREPEND_AF)
36447dd1d1bSDag-Erling Smørgrav 	/* skip address family */
36547dd1d1bSDag-Erling Smørgrav 	*dlen -= sizeof(af);
36647dd1d1bSDag-Erling Smørgrav 	buf = *data + sizeof(af);
36747dd1d1bSDag-Erling Smørgrav #elif defined(SSH_TUN_COMPAT_AF)
36847dd1d1bSDag-Erling Smørgrav 	/* translate address family */
36947dd1d1bSDag-Erling Smørgrav 	af = (PEEK_U32(buf) == OPENBSD_AF_INET6) ? AF_INET6 : AF_INET;
37047dd1d1bSDag-Erling Smørgrav 	POKE_U32(buf, af);
37147dd1d1bSDag-Erling Smørgrav #endif
37247dd1d1bSDag-Erling Smørgrav 	return (buf);
37347dd1d1bSDag-Erling Smørgrav }
37447dd1d1bSDag-Erling Smørgrav #endif /* SSH_TUN_FILTER */
375