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