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