xref: /dflybsd-src/lib/libssh/openbsd-compat/port-net.c (revision 2c81fb9c483cc2c8f293c3c199fac04d266b4e1b)
12c0338ffSzrj /*
22c0338ffSzrj  * Copyright (c) 2005 Reyk Floeter <reyk@openbsd.org>
32c0338ffSzrj  *
42c0338ffSzrj  * Permission to use, copy, modify, and distribute this software for any
52c0338ffSzrj  * purpose with or without fee is hereby granted, provided that the above
62c0338ffSzrj  * copyright notice and this permission notice appear in all copies.
72c0338ffSzrj  *
82c0338ffSzrj  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
92c0338ffSzrj  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
102c0338ffSzrj  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
112c0338ffSzrj  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
122c0338ffSzrj  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
132c0338ffSzrj  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
142c0338ffSzrj  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
152c0338ffSzrj  */
162c0338ffSzrj 
172c0338ffSzrj #include "includes.h"
182c0338ffSzrj 
192c0338ffSzrj #include <sys/types.h>
202c0338ffSzrj #include <sys/ioctl.h>
212c0338ffSzrj 
222c0338ffSzrj #include <netinet/in.h>
232c0338ffSzrj #include <arpa/inet.h>
242c0338ffSzrj #include <netinet/ip.h>
252c0338ffSzrj 
262c0338ffSzrj #include <errno.h>
272c0338ffSzrj #include <fcntl.h>
282c0338ffSzrj #include <stdarg.h>
29*2c81fb9cSAntonio Huete Jimenez #include <stdio.h>
302c0338ffSzrj #include <string.h>
312c0338ffSzrj #include <unistd.h>
322c0338ffSzrj 
332c0338ffSzrj #include "openbsd-compat/sys-queue.h"
342c0338ffSzrj #include "log.h"
352c0338ffSzrj #include "misc.h"
362c0338ffSzrj #include "sshbuf.h"
372c0338ffSzrj #include "channels.h"
382c0338ffSzrj #include "ssherr.h"
392c0338ffSzrj 
402c0338ffSzrj /*
412c0338ffSzrj  * This file contains various portability code for network support,
422c0338ffSzrj  * including tun/tap forwarding and routing domains.
432c0338ffSzrj  */
442c0338ffSzrj 
45bc9cc675SDaniel Fojt #if defined(SYS_RDOMAIN_LINUX) || defined(SSH_TUN_LINUX)
46bc9cc675SDaniel Fojt #include <linux/if.h>
47bc9cc675SDaniel Fojt #endif
48bc9cc675SDaniel Fojt 
49bc9cc675SDaniel Fojt #if defined(SYS_RDOMAIN_LINUX)
50bc9cc675SDaniel Fojt char *
sys_get_rdomain(int fd)51bc9cc675SDaniel Fojt sys_get_rdomain(int fd)
52bc9cc675SDaniel Fojt {
53bc9cc675SDaniel Fojt 	char dev[IFNAMSIZ + 1];
54bc9cc675SDaniel Fojt 	socklen_t len = sizeof(dev) - 1;
55bc9cc675SDaniel Fojt 
56bc9cc675SDaniel Fojt 	if (getsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, dev, &len) == -1) {
57bc9cc675SDaniel Fojt 		error("%s: cannot determine VRF for fd=%d : %s",
58bc9cc675SDaniel Fojt 		    __func__, fd, strerror(errno));
59bc9cc675SDaniel Fojt 		return NULL;
60bc9cc675SDaniel Fojt 	}
61bc9cc675SDaniel Fojt 	dev[len] = '\0';
62bc9cc675SDaniel Fojt 	return strdup(dev);
63bc9cc675SDaniel Fojt }
64bc9cc675SDaniel Fojt 
65bc9cc675SDaniel Fojt int
sys_set_rdomain(int fd,const char * name)66bc9cc675SDaniel Fojt sys_set_rdomain(int fd, const char *name)
67bc9cc675SDaniel Fojt {
68bc9cc675SDaniel Fojt 	if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
69bc9cc675SDaniel Fojt 	    name, strlen(name)) == -1) {
70bc9cc675SDaniel Fojt 		error("%s: setsockopt(%d, SO_BINDTODEVICE, %s): %s",
71bc9cc675SDaniel Fojt 		    __func__, fd, name, strerror(errno));
72bc9cc675SDaniel Fojt 		return -1;
73bc9cc675SDaniel Fojt 	}
74bc9cc675SDaniel Fojt 	return 0;
75bc9cc675SDaniel Fojt }
76bc9cc675SDaniel Fojt 
77bc9cc675SDaniel Fojt int
sys_valid_rdomain(const char * name)78bc9cc675SDaniel Fojt sys_valid_rdomain(const char *name)
79bc9cc675SDaniel Fojt {
80bc9cc675SDaniel Fojt 	int fd;
81bc9cc675SDaniel Fojt 
82bc9cc675SDaniel Fojt 	/*
83bc9cc675SDaniel Fojt 	 * This is a pretty crappy way to test. It would be better to
84bc9cc675SDaniel Fojt 	 * check whether "name" represents a VRF device, but apparently
85bc9cc675SDaniel Fojt 	 * that requires an rtnetlink transaction.
86bc9cc675SDaniel Fojt 	 */
87bc9cc675SDaniel Fojt 	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
88bc9cc675SDaniel Fojt 		return 0;
89bc9cc675SDaniel Fojt 	if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
90bc9cc675SDaniel Fojt 	    name, strlen(name)) == -1) {
91bc9cc675SDaniel Fojt 		close(fd);
92bc9cc675SDaniel Fojt 		return 0;
93bc9cc675SDaniel Fojt 	}
94bc9cc675SDaniel Fojt 	close(fd);
95bc9cc675SDaniel Fojt 	return 1;
96bc9cc675SDaniel Fojt }
97bc9cc675SDaniel Fojt #elif defined(SYS_RDOMAIN_XXX)
98bc9cc675SDaniel Fojt /* XXX examples */
99bc9cc675SDaniel Fojt char *
sys_get_rdomain(int fd)100bc9cc675SDaniel Fojt sys_get_rdomain(int fd)
101bc9cc675SDaniel Fojt {
102bc9cc675SDaniel Fojt 	return NULL;
103bc9cc675SDaniel Fojt }
104bc9cc675SDaniel Fojt 
105bc9cc675SDaniel Fojt int
sys_set_rdomain(int fd,const char * name)106bc9cc675SDaniel Fojt sys_set_rdomain(int fd, const char *name)
107bc9cc675SDaniel Fojt {
108bc9cc675SDaniel Fojt 	return -1;
109bc9cc675SDaniel Fojt }
110bc9cc675SDaniel Fojt 
111bc9cc675SDaniel Fojt int
valid_rdomain(const char * name)112bc9cc675SDaniel Fojt valid_rdomain(const char *name)
113bc9cc675SDaniel Fojt {
114bc9cc675SDaniel Fojt 	return 0;
115bc9cc675SDaniel Fojt }
116bc9cc675SDaniel Fojt 
117bc9cc675SDaniel Fojt void
sys_set_process_rdomain(const char * name)118bc9cc675SDaniel Fojt sys_set_process_rdomain(const char *name)
119bc9cc675SDaniel Fojt {
120bc9cc675SDaniel Fojt 	fatal("%s: not supported", __func__);
121bc9cc675SDaniel Fojt }
122bc9cc675SDaniel Fojt #endif /* defined(SYS_RDOMAIN_XXX) */
123bc9cc675SDaniel Fojt 
1242c0338ffSzrj /*
1252c0338ffSzrj  * This is the portable version of the SSH tunnel forwarding, it
1262c0338ffSzrj  * uses some preprocessor definitions for various platform-specific
1272c0338ffSzrj  * settings.
1282c0338ffSzrj  *
1292c0338ffSzrj  * SSH_TUN_LINUX	Use the (newer) Linux tun/tap device
1302c0338ffSzrj  * SSH_TUN_FREEBSD	Use the FreeBSD tun/tap device
1312c0338ffSzrj  * SSH_TUN_COMPAT_AF	Translate the OpenBSD address family
1322c0338ffSzrj  * SSH_TUN_PREPEND_AF	Prepend/remove the address family
1332c0338ffSzrj  */
1342c0338ffSzrj 
1352c0338ffSzrj /*
1362c0338ffSzrj  * System-specific tunnel open function
1372c0338ffSzrj  */
1382c0338ffSzrj 
139bc9cc675SDaniel Fojt #if defined(SSH_TUN_LINUX)
140bc9cc675SDaniel Fojt #include <linux/if_tun.h>
141bc9cc675SDaniel Fojt #define TUN_CTRL_DEV "/dev/net/tun"
142bc9cc675SDaniel Fojt 
143bc9cc675SDaniel Fojt int
sys_tun_open(int tun,int mode,char ** ifname)144bc9cc675SDaniel Fojt sys_tun_open(int tun, int mode, char **ifname)
145bc9cc675SDaniel Fojt {
146bc9cc675SDaniel Fojt 	struct ifreq ifr;
147bc9cc675SDaniel Fojt 	int fd = -1;
148bc9cc675SDaniel Fojt 	const char *name = NULL;
149bc9cc675SDaniel Fojt 
150bc9cc675SDaniel Fojt 	if (ifname != NULL)
151bc9cc675SDaniel Fojt 		*ifname = NULL;
152bc9cc675SDaniel Fojt 	if ((fd = open(TUN_CTRL_DEV, O_RDWR)) == -1) {
153bc9cc675SDaniel Fojt 		debug("%s: failed to open tunnel control device \"%s\": %s",
154bc9cc675SDaniel Fojt 		    __func__, TUN_CTRL_DEV, strerror(errno));
155bc9cc675SDaniel Fojt 		return (-1);
156bc9cc675SDaniel Fojt 	}
157bc9cc675SDaniel Fojt 
158bc9cc675SDaniel Fojt 	bzero(&ifr, sizeof(ifr));
159bc9cc675SDaniel Fojt 
160bc9cc675SDaniel Fojt 	if (mode == SSH_TUNMODE_ETHERNET) {
161bc9cc675SDaniel Fojt 		ifr.ifr_flags = IFF_TAP;
162bc9cc675SDaniel Fojt 		name = "tap%d";
163bc9cc675SDaniel Fojt 	} else {
164bc9cc675SDaniel Fojt 		ifr.ifr_flags = IFF_TUN;
165bc9cc675SDaniel Fojt 		name = "tun%d";
166bc9cc675SDaniel Fojt 	}
167bc9cc675SDaniel Fojt 	ifr.ifr_flags |= IFF_NO_PI;
168bc9cc675SDaniel Fojt 
169bc9cc675SDaniel Fojt 	if (tun != SSH_TUNID_ANY) {
170bc9cc675SDaniel Fojt 		if (tun > SSH_TUNID_MAX) {
171bc9cc675SDaniel Fojt 			debug("%s: invalid tunnel id %x: %s", __func__,
172bc9cc675SDaniel Fojt 			    tun, strerror(errno));
173bc9cc675SDaniel Fojt 			goto failed;
174bc9cc675SDaniel Fojt 		}
175bc9cc675SDaniel Fojt 		snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), name, tun);
176bc9cc675SDaniel Fojt 	}
177bc9cc675SDaniel Fojt 
178bc9cc675SDaniel Fojt 	if (ioctl(fd, TUNSETIFF, &ifr) == -1) {
179bc9cc675SDaniel Fojt 		debug("%s: failed to configure tunnel (mode %d): %s", __func__,
180bc9cc675SDaniel Fojt 		    mode, strerror(errno));
181bc9cc675SDaniel Fojt 		goto failed;
182bc9cc675SDaniel Fojt 	}
183bc9cc675SDaniel Fojt 
184bc9cc675SDaniel Fojt 	if (tun == SSH_TUNID_ANY)
185bc9cc675SDaniel Fojt 		debug("%s: tunnel mode %d fd %d", __func__, mode, fd);
186bc9cc675SDaniel Fojt 	else
187bc9cc675SDaniel Fojt 		debug("%s: %s mode %d fd %d", __func__, ifr.ifr_name, mode, fd);
188bc9cc675SDaniel Fojt 
189bc9cc675SDaniel Fojt 	if (ifname != NULL && (*ifname = strdup(ifr.ifr_name)) == NULL)
190bc9cc675SDaniel Fojt 		goto failed;
191bc9cc675SDaniel Fojt 
192bc9cc675SDaniel Fojt 	return (fd);
193bc9cc675SDaniel Fojt 
194bc9cc675SDaniel Fojt  failed:
195bc9cc675SDaniel Fojt 	close(fd);
196bc9cc675SDaniel Fojt 	return (-1);
197bc9cc675SDaniel Fojt }
198bc9cc675SDaniel Fojt #endif /* SSH_TUN_LINUX */
199bc9cc675SDaniel Fojt 
2002c0338ffSzrj #ifdef SSH_TUN_FREEBSD
2012c0338ffSzrj #include <sys/socket.h>
2022c0338ffSzrj #include <net/if.h>
2032c0338ffSzrj 
2042c0338ffSzrj #ifdef HAVE_NET_IF_TUN_H
2052c0338ffSzrj #include <net/if_tun.h>
2062c0338ffSzrj #endif
2072c0338ffSzrj 
2082c0338ffSzrj int
sys_tun_open(int tun,int mode,char ** ifname)2092c0338ffSzrj sys_tun_open(int tun, int mode, char **ifname)
2102c0338ffSzrj {
2112c0338ffSzrj 	struct ifreq ifr;
2122c0338ffSzrj 	char name[100];
213*2c81fb9cSAntonio Huete Jimenez 	int fd = -1, sock;
2142c0338ffSzrj 	const char *tunbase = "tun";
215*2c81fb9cSAntonio Huete Jimenez #if defined(TUNSIFHEAD) && !defined(SSH_TUN_PREPEND_AF)
216*2c81fb9cSAntonio Huete Jimenez 	int flag;
217*2c81fb9cSAntonio Huete Jimenez #endif
2182c0338ffSzrj 
2192c0338ffSzrj 	if (ifname != NULL)
2202c0338ffSzrj 		*ifname = NULL;
2212c0338ffSzrj 
2222c0338ffSzrj 	if (mode == SSH_TUNMODE_ETHERNET) {
2232c0338ffSzrj #ifdef SSH_TUN_NO_L2
2242c0338ffSzrj 		debug("%s: no layer 2 tunnelling support", __func__);
2252c0338ffSzrj 		return (-1);
2262c0338ffSzrj #else
2272c0338ffSzrj 		tunbase = "tap";
2282c0338ffSzrj #endif
2292c0338ffSzrj 	}
2302c0338ffSzrj 
2312c0338ffSzrj 	/* Open the tunnel device */
2322c0338ffSzrj 	if (tun <= SSH_TUNID_MAX) {
2332c0338ffSzrj 		snprintf(name, sizeof(name), "/dev/%s%d", tunbase, tun);
2342c0338ffSzrj 		fd = open(name, O_RDWR);
2352c0338ffSzrj 	} else if (tun == SSH_TUNID_ANY) {
2362c0338ffSzrj 		for (tun = 100; tun >= 0; tun--) {
2372c0338ffSzrj 			snprintf(name, sizeof(name), "/dev/%s%d",
2382c0338ffSzrj 			    tunbase, tun);
2392c0338ffSzrj 			if ((fd = open(name, O_RDWR)) >= 0)
2402c0338ffSzrj 				break;
2412c0338ffSzrj 		}
2422c0338ffSzrj 	} else {
2432c0338ffSzrj 		debug("%s: invalid tunnel %u\n", __func__, tun);
2442c0338ffSzrj 		return (-1);
2452c0338ffSzrj 	}
2462c0338ffSzrj 
2472c0338ffSzrj 	if (fd < 0) {
2482c0338ffSzrj 		debug("%s: %s open failed: %s", __func__, name,
2492c0338ffSzrj 		    strerror(errno));
2502c0338ffSzrj 		return (-1);
2512c0338ffSzrj 	}
2522c0338ffSzrj 
2532c0338ffSzrj 	/* Turn on tunnel headers */
2542c0338ffSzrj #if defined(TUNSIFHEAD) && !defined(SSH_TUN_PREPEND_AF)
255*2c81fb9cSAntonio Huete Jimenez 	flag = 1;
2562c0338ffSzrj 	if (mode != SSH_TUNMODE_ETHERNET &&
2572c0338ffSzrj 	    ioctl(fd, TUNSIFHEAD, &flag) == -1) {
2582c0338ffSzrj 		debug("%s: ioctl(%d, TUNSIFHEAD, 1): %s", __func__, fd,
2592c0338ffSzrj 		    strerror(errno));
2602c0338ffSzrj 		close(fd);
2612c0338ffSzrj 	}
2622c0338ffSzrj #endif
2632c0338ffSzrj 
2642c0338ffSzrj 	debug("%s: %s mode %d fd %d", __func__, name, mode, fd);
2652c0338ffSzrj 
2662c0338ffSzrj 	/* Set the tunnel device operation mode */
2672c0338ffSzrj 	snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", tunbase, tun);
2682c0338ffSzrj 	if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
2692c0338ffSzrj 		goto failed;
2702c0338ffSzrj 
2712c0338ffSzrj 	if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1)
2722c0338ffSzrj 		goto failed;
2732c0338ffSzrj 	if ((ifr.ifr_flags & IFF_UP) == 0) {
2742c0338ffSzrj 		ifr.ifr_flags |= IFF_UP;
2752c0338ffSzrj 		if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1)
2762c0338ffSzrj 			goto failed;
2772c0338ffSzrj 	}
2782c0338ffSzrj 
2792c0338ffSzrj 	if (ifname != NULL && (*ifname = strdup(ifr.ifr_name)) == NULL)
2802c0338ffSzrj 		goto failed;
2812c0338ffSzrj 
2822c0338ffSzrj 	close(sock);
2832c0338ffSzrj 	return (fd);
2842c0338ffSzrj 
2852c0338ffSzrj  failed:
2862c0338ffSzrj 	if (fd >= 0)
2872c0338ffSzrj 		close(fd);
2882c0338ffSzrj 	if (sock >= 0)
2892c0338ffSzrj 		close(sock);
2902c0338ffSzrj 	debug("%s: failed to set %s mode %d: %s", __func__, name,
2912c0338ffSzrj 	    mode, strerror(errno));
2922c0338ffSzrj 	return (-1);
2932c0338ffSzrj }
2942c0338ffSzrj #endif /* SSH_TUN_FREEBSD */
295bc9cc675SDaniel Fojt 
296bc9cc675SDaniel Fojt /*
297bc9cc675SDaniel Fojt  * System-specific channel filters
298bc9cc675SDaniel Fojt  */
299bc9cc675SDaniel Fojt 
300bc9cc675SDaniel Fojt #if defined(SSH_TUN_FILTER)
301bc9cc675SDaniel Fojt /*
302bc9cc675SDaniel Fojt  * The tunnel forwarding protocol prepends the address family of forwarded
303bc9cc675SDaniel Fojt  * IP packets using OpenBSD's numbers.
304bc9cc675SDaniel Fojt  */
305bc9cc675SDaniel Fojt #define OPENBSD_AF_INET		2
306bc9cc675SDaniel Fojt #define OPENBSD_AF_INET6	24
307bc9cc675SDaniel Fojt 
308bc9cc675SDaniel Fojt int
sys_tun_infilter(struct ssh * ssh,struct Channel * c,char * buf,int _len)309bc9cc675SDaniel Fojt sys_tun_infilter(struct ssh *ssh, struct Channel *c, char *buf, int _len)
310bc9cc675SDaniel Fojt {
311bc9cc675SDaniel Fojt 	int r;
312bc9cc675SDaniel Fojt 	size_t len;
313bc9cc675SDaniel Fojt 	char *ptr = buf;
314bc9cc675SDaniel Fojt #if defined(SSH_TUN_PREPEND_AF)
315bc9cc675SDaniel Fojt 	char rbuf[CHAN_RBUF];
316bc9cc675SDaniel Fojt 	struct ip iph;
317bc9cc675SDaniel Fojt #endif
318bc9cc675SDaniel Fojt #if defined(SSH_TUN_PREPEND_AF) || defined(SSH_TUN_COMPAT_AF)
319bc9cc675SDaniel Fojt 	u_int32_t af;
320bc9cc675SDaniel Fojt #endif
321bc9cc675SDaniel Fojt 
322bc9cc675SDaniel Fojt 	/* XXX update channel input filter API to use unsigned length */
323bc9cc675SDaniel Fojt 	if (_len < 0)
324bc9cc675SDaniel Fojt 		return -1;
325bc9cc675SDaniel Fojt 	len = _len;
326bc9cc675SDaniel Fojt 
327bc9cc675SDaniel Fojt #if defined(SSH_TUN_PREPEND_AF)
328bc9cc675SDaniel Fojt 	if (len <= sizeof(iph) || len > sizeof(rbuf) - 4)
329bc9cc675SDaniel Fojt 		return -1;
330bc9cc675SDaniel Fojt 	/* Determine address family from packet IP header. */
331bc9cc675SDaniel Fojt 	memcpy(&iph, buf, sizeof(iph));
332bc9cc675SDaniel Fojt 	af = iph.ip_v == 6 ? OPENBSD_AF_INET6 : OPENBSD_AF_INET;
333bc9cc675SDaniel Fojt 	/* Prepend address family to packet using OpenBSD constants */
334bc9cc675SDaniel Fojt 	memcpy(rbuf + 4, buf, len);
335bc9cc675SDaniel Fojt 	len += 4;
336bc9cc675SDaniel Fojt 	POKE_U32(rbuf, af);
337bc9cc675SDaniel Fojt 	ptr = rbuf;
338bc9cc675SDaniel Fojt #elif defined(SSH_TUN_COMPAT_AF)
339bc9cc675SDaniel Fojt 	/* Convert existing address family header to OpenBSD value */
340bc9cc675SDaniel Fojt 	if (len <= 4)
341bc9cc675SDaniel Fojt 		return -1;
342bc9cc675SDaniel Fojt 	af = PEEK_U32(buf);
343bc9cc675SDaniel Fojt 	/* Put it back */
344bc9cc675SDaniel Fojt 	POKE_U32(buf, af == AF_INET6 ? OPENBSD_AF_INET6 : OPENBSD_AF_INET);
345bc9cc675SDaniel Fojt #endif
346bc9cc675SDaniel Fojt 
347bc9cc675SDaniel Fojt 	if ((r = sshbuf_put_string(c->input, ptr, len)) != 0)
348bc9cc675SDaniel Fojt 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
349bc9cc675SDaniel Fojt 	return (0);
350bc9cc675SDaniel Fojt }
351bc9cc675SDaniel Fojt 
352bc9cc675SDaniel Fojt u_char *
sys_tun_outfilter(struct ssh * ssh,struct Channel * c,u_char ** data,size_t * dlen)353bc9cc675SDaniel Fojt sys_tun_outfilter(struct ssh *ssh, struct Channel *c,
354bc9cc675SDaniel Fojt     u_char **data, size_t *dlen)
355bc9cc675SDaniel Fojt {
356bc9cc675SDaniel Fojt 	u_char *buf;
357bc9cc675SDaniel Fojt 	u_int32_t af;
358bc9cc675SDaniel Fojt 	int r;
359bc9cc675SDaniel Fojt 
360bc9cc675SDaniel Fojt 	/* XXX new API is incompatible with this signature. */
361bc9cc675SDaniel Fojt 	if ((r = sshbuf_get_string(c->output, data, dlen)) != 0)
362bc9cc675SDaniel Fojt 		fatal("%s: buffer error: %s", __func__, ssh_err(r));
363bc9cc675SDaniel Fojt 	if (*dlen < sizeof(af))
364bc9cc675SDaniel Fojt 		return (NULL);
365bc9cc675SDaniel Fojt 	buf = *data;
366bc9cc675SDaniel Fojt 
367bc9cc675SDaniel Fojt #if defined(SSH_TUN_PREPEND_AF)
368bc9cc675SDaniel Fojt 	/* skip address family */
369bc9cc675SDaniel Fojt 	*dlen -= sizeof(af);
370bc9cc675SDaniel Fojt 	buf = *data + sizeof(af);
371bc9cc675SDaniel Fojt #elif defined(SSH_TUN_COMPAT_AF)
372bc9cc675SDaniel Fojt 	/* translate address family */
373bc9cc675SDaniel Fojt 	af = (PEEK_U32(buf) == OPENBSD_AF_INET6) ? AF_INET6 : AF_INET;
374bc9cc675SDaniel Fojt 	POKE_U32(buf, af);
375bc9cc675SDaniel Fojt #endif
376bc9cc675SDaniel Fojt 	return (buf);
377bc9cc675SDaniel Fojt }
378bc9cc675SDaniel Fojt #endif /* SSH_TUN_FILTER */
379