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 141 int 142 sys_tun_open(int tun, int mode, char **ifname) 143 { 144 struct ifreq ifr; 145 int fd = -1; 146 const char *name = NULL; 147 148 if (ifname != NULL) 149 *ifname = NULL; 150 151 if ((fd = open("/dev/net/tun", O_RDWR)) == -1) { 152 debug("%s: failed to open tunnel control interface: %s", 153 __func__, 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 207 int 208 sys_tun_open(int tun, int mode, char **ifname) 209 { 210 struct ifreq ifr; 211 char name[100]; 212 int fd = -1, sock, flag; 213 const char *tunbase = "tun"; 214 215 if (ifname != NULL) 216 *ifname = NULL; 217 218 if (mode == SSH_TUNMODE_ETHERNET) { 219 #ifdef SSH_TUN_NO_L2 220 debug("%s: no layer 2 tunnelling support", __func__); 221 return (-1); 222 #else 223 tunbase = "tap"; 224 #endif 225 } 226 227 /* Open the tunnel device */ 228 if (tun <= SSH_TUNID_MAX) { 229 snprintf(name, sizeof(name), "/dev/%s%d", tunbase, tun); 230 fd = open(name, O_RDWR); 231 } else if (tun == SSH_TUNID_ANY) { 232 for (tun = 100; tun >= 0; tun--) { 233 snprintf(name, sizeof(name), "/dev/%s%d", 234 tunbase, tun); 235 if ((fd = open(name, O_RDWR)) >= 0) 236 break; 237 } 238 } else { 239 debug("%s: invalid tunnel %u\n", __func__, tun); 240 return (-1); 241 } 242 243 if (fd < 0) { 244 debug("%s: %s open failed: %s", __func__, name, 245 strerror(errno)); 246 return (-1); 247 } 248 249 /* Turn on tunnel headers */ 250 flag = 1; 251 #if defined(TUNSIFHEAD) && !defined(SSH_TUN_PREPEND_AF) 252 if (mode != SSH_TUNMODE_ETHERNET && 253 ioctl(fd, TUNSIFHEAD, &flag) == -1) { 254 debug("%s: ioctl(%d, TUNSIFHEAD, 1): %s", __func__, fd, 255 strerror(errno)); 256 close(fd); 257 } 258 #endif 259 260 debug("%s: %s mode %d fd %d", __func__, name, mode, fd); 261 262 /* Set the tunnel device operation mode */ 263 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", tunbase, tun); 264 if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) 265 goto failed; 266 267 if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) 268 goto failed; 269 if ((ifr.ifr_flags & IFF_UP) == 0) { 270 ifr.ifr_flags |= IFF_UP; 271 if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) 272 goto failed; 273 } 274 275 if (ifname != NULL && (*ifname = strdup(ifr.ifr_name)) == NULL) 276 goto failed; 277 278 close(sock); 279 return (fd); 280 281 failed: 282 if (fd >= 0) 283 close(fd); 284 if (sock >= 0) 285 close(sock); 286 debug("%s: failed to set %s mode %d: %s", __func__, name, 287 mode, strerror(errno)); 288 return (-1); 289 } 290 #endif /* SSH_TUN_FREEBSD */ 291 292 /* 293 * System-specific channel filters 294 */ 295 296 #if defined(SSH_TUN_FILTER) 297 /* 298 * The tunnel forwarding protocol prepends the address family of forwarded 299 * IP packets using OpenBSD's numbers. 300 */ 301 #define OPENBSD_AF_INET 2 302 #define OPENBSD_AF_INET6 24 303 304 int 305 sys_tun_infilter(struct ssh *ssh, struct Channel *c, char *buf, int _len) 306 { 307 int r; 308 size_t len; 309 char *ptr = buf; 310 #if defined(SSH_TUN_PREPEND_AF) 311 char rbuf[CHAN_RBUF]; 312 struct ip iph; 313 #endif 314 #if defined(SSH_TUN_PREPEND_AF) || defined(SSH_TUN_COMPAT_AF) 315 u_int32_t af; 316 #endif 317 318 /* XXX update channel input filter API to use unsigned length */ 319 if (_len < 0) 320 return -1; 321 len = _len; 322 323 #if defined(SSH_TUN_PREPEND_AF) 324 if (len <= sizeof(iph) || len > sizeof(rbuf) - 4) 325 return -1; 326 /* Determine address family from packet IP header. */ 327 memcpy(&iph, buf, sizeof(iph)); 328 af = iph.ip_v == 6 ? OPENBSD_AF_INET6 : OPENBSD_AF_INET; 329 /* Prepend address family to packet using OpenBSD constants */ 330 memcpy(rbuf + 4, buf, len); 331 len += 4; 332 POKE_U32(rbuf, af); 333 ptr = rbuf; 334 #elif defined(SSH_TUN_COMPAT_AF) 335 /* Convert existing address family header to OpenBSD value */ 336 if (len <= 4) 337 return -1; 338 af = PEEK_U32(buf); 339 /* Put it back */ 340 POKE_U32(buf, af == AF_INET6 ? OPENBSD_AF_INET6 : OPENBSD_AF_INET); 341 #endif 342 343 if ((r = sshbuf_put_string(c->input, ptr, len)) != 0) 344 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 345 return (0); 346 } 347 348 u_char * 349 sys_tun_outfilter(struct ssh *ssh, struct Channel *c, 350 u_char **data, size_t *dlen) 351 { 352 u_char *buf; 353 u_int32_t af; 354 int r; 355 356 /* XXX new API is incompatible with this signature. */ 357 if ((r = sshbuf_get_string(c->output, data, dlen)) != 0) 358 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 359 if (*dlen < sizeof(af)) 360 return (NULL); 361 buf = *data; 362 363 #if defined(SSH_TUN_PREPEND_AF) 364 /* skip address family */ 365 *dlen -= sizeof(af); 366 buf = *data + sizeof(af); 367 #elif defined(SSH_TUN_COMPAT_AF) 368 /* translate address family */ 369 af = (PEEK_U32(buf) == OPENBSD_AF_INET6) ? AF_INET6 : AF_INET; 370 POKE_U32(buf, af); 371 #endif 372 return (buf); 373 } 374 #endif /* SSH_TUN_FILTER */ 375