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 "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 file contains various portability code for network support, 41 * including tun/tap forwarding and routing domains. 42 */ 43 44 #if defined(SYS_RDOMAIN_LINUX) || defined(SSH_TUN_LINUX) 45 #include <linux/if.h> 46 #endif 47 48 #if defined(SYS_RDOMAIN_LINUX) 49 char * 50 sys_get_rdomain(int fd) 51 { 52 char dev[IFNAMSIZ + 1]; 53 socklen_t len = sizeof(dev) - 1; 54 55 if (getsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, dev, &len) == -1) { 56 error("%s: cannot determine VRF for fd=%d : %s", 57 __func__, fd, strerror(errno)); 58 return NULL; 59 } 60 dev[len] = '\0'; 61 return strdup(dev); 62 } 63 64 int 65 sys_set_rdomain(int fd, const char *name) 66 { 67 if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, 68 name, strlen(name)) == -1) { 69 error("%s: setsockopt(%d, SO_BINDTODEVICE, %s): %s", 70 __func__, fd, name, strerror(errno)); 71 return -1; 72 } 73 return 0; 74 } 75 76 int 77 sys_valid_rdomain(const char *name) 78 { 79 int fd; 80 81 /* 82 * This is a pretty crappy way to test. It would be better to 83 * check whether "name" represents a VRF device, but apparently 84 * that requires an rtnetlink transaction. 85 */ 86 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) 87 return 0; 88 if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, 89 name, strlen(name)) == -1) { 90 close(fd); 91 return 0; 92 } 93 close(fd); 94 return 1; 95 } 96 #elif defined(SYS_RDOMAIN_XXX) 97 /* XXX examples */ 98 char * 99 sys_get_rdomain(int fd) 100 { 101 return NULL; 102 } 103 104 int 105 sys_set_rdomain(int fd, const char *name) 106 { 107 return -1; 108 } 109 110 int 111 valid_rdomain(const char *name) 112 { 113 return 0; 114 } 115 116 void 117 sys_set_process_rdomain(const char *name) 118 { 119 fatal("%s: not supported", __func__); 120 } 121 #endif /* defined(SYS_RDOMAIN_XXX) */ 122 123 /* 124 * This is the portable version of the SSH tunnel forwarding, it 125 * uses some preprocessor definitions for various platform-specific 126 * settings. 127 * 128 * SSH_TUN_LINUX Use the (newer) Linux tun/tap device 129 * SSH_TUN_FREEBSD Use the FreeBSD tun/tap device 130 * SSH_TUN_COMPAT_AF Translate the OpenBSD address family 131 * SSH_TUN_PREPEND_AF Prepend/remove the address family 132 */ 133 134 /* 135 * System-specific tunnel open function 136 */ 137 138 #if defined(SSH_TUN_LINUX) 139 #include <linux/if_tun.h> 140 #define TUN_CTRL_DEV "/dev/net/tun" 141 142 int 143 sys_tun_open(int tun, int mode, char **ifname) 144 { 145 struct ifreq ifr; 146 int fd = -1; 147 const char *name = NULL; 148 149 if (ifname != NULL) 150 *ifname = NULL; 151 if ((fd = open(TUN_CTRL_DEV, O_RDWR)) == -1) { 152 debug("%s: failed to open tunnel control device \"%s\": %s", 153 __func__, TUN_CTRL_DEV, strerror(errno)); 154 return (-1); 155 } 156 157 bzero(&ifr, sizeof(ifr)); 158 159 if (mode == SSH_TUNMODE_ETHERNET) { 160 ifr.ifr_flags = IFF_TAP; 161 name = "tap%d"; 162 } else { 163 ifr.ifr_flags = IFF_TUN; 164 name = "tun%d"; 165 } 166 ifr.ifr_flags |= IFF_NO_PI; 167 168 if (tun != SSH_TUNID_ANY) { 169 if (tun > SSH_TUNID_MAX) { 170 debug("%s: invalid tunnel id %x: %s", __func__, 171 tun, strerror(errno)); 172 goto failed; 173 } 174 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), name, tun); 175 } 176 177 if (ioctl(fd, TUNSETIFF, &ifr) == -1) { 178 debug("%s: failed to configure tunnel (mode %d): %s", __func__, 179 mode, strerror(errno)); 180 goto failed; 181 } 182 183 if (tun == SSH_TUNID_ANY) 184 debug("%s: tunnel mode %d fd %d", __func__, mode, fd); 185 else 186 debug("%s: %s mode %d fd %d", __func__, ifr.ifr_name, mode, fd); 187 188 if (ifname != NULL && (*ifname = strdup(ifr.ifr_name)) == NULL) 189 goto failed; 190 191 return (fd); 192 193 failed: 194 close(fd); 195 return (-1); 196 } 197 #endif /* SSH_TUN_LINUX */ 198 199 #ifdef SSH_TUN_FREEBSD 200 #include <sys/socket.h> 201 #include <net/if.h> 202 203 #ifdef HAVE_NET_IF_TUN_H 204 #include <net/if_tun.h> 205 #endif 206 #ifdef __DragonFly__ 207 #include <net/tun/if_tun.h> 208 #endif 209 210 int 211 sys_tun_open(int tun, int mode, char **ifname) 212 { 213 struct ifreq ifr; 214 char name[100]; 215 int fd = -1, sock, flag; 216 const char *tunbase = "tun"; 217 218 if (ifname != NULL) 219 *ifname = NULL; 220 221 if (mode == SSH_TUNMODE_ETHERNET) { 222 #ifdef SSH_TUN_NO_L2 223 debug("%s: no layer 2 tunnelling support", __func__); 224 return (-1); 225 #else 226 tunbase = "tap"; 227 #endif 228 } 229 230 /* Open the tunnel device */ 231 if (tun <= SSH_TUNID_MAX) { 232 snprintf(name, sizeof(name), "/dev/%s%d", tunbase, tun); 233 fd = open(name, O_RDWR); 234 } else if (tun == SSH_TUNID_ANY) { 235 for (tun = 100; tun >= 0; tun--) { 236 snprintf(name, sizeof(name), "/dev/%s%d", 237 tunbase, tun); 238 if ((fd = open(name, O_RDWR)) >= 0) 239 break; 240 } 241 } else { 242 debug("%s: invalid tunnel %u\n", __func__, tun); 243 return (-1); 244 } 245 246 if (fd < 0) { 247 debug("%s: %s open failed: %s", __func__, name, 248 strerror(errno)); 249 return (-1); 250 } 251 252 /* Turn on tunnel headers */ 253 flag = 1; 254 #if defined(TUNSIFHEAD) && !defined(SSH_TUN_PREPEND_AF) 255 if (mode != SSH_TUNMODE_ETHERNET && 256 ioctl(fd, TUNSIFHEAD, &flag) == -1) { 257 debug("%s: ioctl(%d, TUNSIFHEAD, 1): %s", __func__, fd, 258 strerror(errno)); 259 close(fd); 260 } 261 #endif 262 263 debug("%s: %s mode %d fd %d", __func__, name, mode, fd); 264 265 /* Set the tunnel device operation mode */ 266 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", tunbase, tun); 267 if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) 268 goto failed; 269 270 if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) 271 goto failed; 272 if ((ifr.ifr_flags & IFF_UP) == 0) { 273 ifr.ifr_flags |= IFF_UP; 274 if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) 275 goto failed; 276 } 277 278 if (ifname != NULL && (*ifname = strdup(ifr.ifr_name)) == NULL) 279 goto failed; 280 281 close(sock); 282 return (fd); 283 284 failed: 285 if (fd >= 0) 286 close(fd); 287 if (sock >= 0) 288 close(sock); 289 debug("%s: failed to set %s mode %d: %s", __func__, name, 290 mode, strerror(errno)); 291 return (-1); 292 } 293 #endif /* SSH_TUN_FREEBSD */ 294 295 /* 296 * System-specific channel filters 297 */ 298 299 #if defined(SSH_TUN_FILTER) 300 /* 301 * The tunnel forwarding protocol prepends the address family of forwarded 302 * IP packets using OpenBSD's numbers. 303 */ 304 #define OPENBSD_AF_INET 2 305 #define OPENBSD_AF_INET6 24 306 307 int 308 sys_tun_infilter(struct ssh *ssh, struct Channel *c, char *buf, int _len) 309 { 310 int r; 311 size_t len; 312 char *ptr = buf; 313 #if defined(SSH_TUN_PREPEND_AF) 314 char rbuf[CHAN_RBUF]; 315 struct ip iph; 316 #endif 317 #if defined(SSH_TUN_PREPEND_AF) || defined(SSH_TUN_COMPAT_AF) 318 u_int32_t af; 319 #endif 320 321 /* XXX update channel input filter API to use unsigned length */ 322 if (_len < 0) 323 return -1; 324 len = _len; 325 326 #if defined(SSH_TUN_PREPEND_AF) 327 if (len <= sizeof(iph) || len > sizeof(rbuf) - 4) 328 return -1; 329 /* Determine address family from packet IP header. */ 330 memcpy(&iph, buf, sizeof(iph)); 331 af = iph.ip_v == 6 ? OPENBSD_AF_INET6 : OPENBSD_AF_INET; 332 /* Prepend address family to packet using OpenBSD constants */ 333 memcpy(rbuf + 4, buf, len); 334 len += 4; 335 POKE_U32(rbuf, af); 336 ptr = rbuf; 337 #elif defined(SSH_TUN_COMPAT_AF) 338 /* Convert existing address family header to OpenBSD value */ 339 if (len <= 4) 340 return -1; 341 af = PEEK_U32(buf); 342 /* Put it back */ 343 POKE_U32(buf, af == AF_INET6 ? OPENBSD_AF_INET6 : OPENBSD_AF_INET); 344 #endif 345 346 if ((r = sshbuf_put_string(c->input, ptr, len)) != 0) 347 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 348 return (0); 349 } 350 351 u_char * 352 sys_tun_outfilter(struct ssh *ssh, struct Channel *c, 353 u_char **data, size_t *dlen) 354 { 355 u_char *buf; 356 u_int32_t af; 357 int r; 358 359 /* XXX new API is incompatible with this signature. */ 360 if ((r = sshbuf_get_string(c->output, data, dlen)) != 0) 361 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 362 if (*dlen < sizeof(af)) 363 return (NULL); 364 buf = *data; 365 366 #if defined(SSH_TUN_PREPEND_AF) 367 /* skip address family */ 368 *dlen -= sizeof(af); 369 buf = *data + sizeof(af); 370 #elif defined(SSH_TUN_COMPAT_AF) 371 /* translate address family */ 372 af = (PEEK_U32(buf) == OPENBSD_AF_INET6) ? AF_INET6 : AF_INET; 373 POKE_U32(buf, af); 374 #endif 375 return (buf); 376 } 377 #endif /* SSH_TUN_FILTER */ 378