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