Annotation of embedaddon/strongswan/src/libstrongswan/networking/tun_device.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (C) 2012 Tobias Brunner
! 3: * Copyright (C) 2012 Giuliano Grassi
! 4: * Copyright (C) 2012 Ralf Sager
! 5: * HSR Hochschule fuer Technik Rapperswil
! 6: * Copyright (C) 2012 Martin Willi
! 7: *
! 8: * This program is free software; you can redistribute it and/or modify it
! 9: * under the terms of the GNU General Public License as published by the
! 10: * Free Software Foundation; either version 2 of the License, or (at your
! 11: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
! 12: *
! 13: * This program is distributed in the hope that it will be useful, but
! 14: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
! 15: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
! 16: * for more details.
! 17: */
! 18:
! 19: #include "tun_device.h"
! 20:
! 21: #include <utils/debug.h>
! 22: #include <threading/thread.h>
! 23:
! 24: #if defined(__APPLE__)
! 25: #include "TargetConditionals.h"
! 26: #if !TARGET_OS_OSX
! 27: #define TUN_DEVICE_NOT_SUPPORTED
! 28: #endif
! 29: #elif !defined(__linux__) && !defined(HAVE_NET_IF_TUN_H)
! 30: #define TUN_DEVICE_NOT_SUPPORTED
! 31: #endif
! 32:
! 33: #ifdef TUN_DEVICE_NOT_SUPPORTED
! 34:
! 35: tun_device_t *tun_device_create(const char *name_tmpl)
! 36: {
! 37: DBG1(DBG_LIB, "TUN devices are not supported");
! 38: return NULL;
! 39: }
! 40:
! 41: #else /* TUN devices supported */
! 42:
! 43: #include <errno.h>
! 44: #include <fcntl.h>
! 45: #include <netinet/in.h>
! 46: #include <string.h>
! 47: #include <sys/ioctl.h>
! 48: #include <sys/types.h>
! 49: #include <sys/socket.h>
! 50: #include <sys/stat.h>
! 51: #include <unistd.h>
! 52: #include <net/if.h>
! 53:
! 54: #ifdef __APPLE__
! 55: #include <net/if_utun.h>
! 56: #include <netinet/in_var.h>
! 57: #include <sys/kern_control.h>
! 58: #elif defined(__linux__)
! 59: #include <linux/types.h>
! 60: #include <linux/if_tun.h>
! 61: #elif __FreeBSD__ >= 10
! 62: #include <net/if_tun.h>
! 63: #include <net/if_var.h>
! 64: #include <netinet/in_var.h>
! 65: #else
! 66: #include <net/if_tun.h>
! 67: #endif
! 68:
! 69: #define TUN_DEFAULT_MTU 1500
! 70:
! 71: typedef struct private_tun_device_t private_tun_device_t;
! 72:
! 73: struct private_tun_device_t {
! 74:
! 75: /**
! 76: * Public interface
! 77: */
! 78: tun_device_t public;
! 79:
! 80: /**
! 81: * The TUN device's file descriptor
! 82: */
! 83: int tunfd;
! 84:
! 85: /**
! 86: * Name of the TUN device
! 87: */
! 88: char if_name[IFNAMSIZ];
! 89:
! 90: /**
! 91: * Socket used for ioctl() to set interface addr, ...
! 92: */
! 93: int sock;
! 94:
! 95: /**
! 96: * The current MTU
! 97: */
! 98: int mtu;
! 99:
! 100: /**
! 101: * Associated address
! 102: */
! 103: host_t *address;
! 104:
! 105: /**
! 106: * Netmask for address
! 107: */
! 108: uint8_t netmask;
! 109: };
! 110:
! 111: /**
! 112: * FreeBSD 10 deprecated the SIOCSIFADDR etc. commands.
! 113: */
! 114: #if __FreeBSD__ >= 10
! 115:
! 116: static bool set_address_and_mask(struct in_aliasreq *ifra, host_t *addr,
! 117: uint8_t netmask)
! 118: {
! 119: host_t *mask;
! 120:
! 121: memcpy(&ifra->ifra_addr, addr->get_sockaddr(addr),
! 122: *addr->get_sockaddr_len(addr));
! 123: /* set the same address as destination address */
! 124: memcpy(&ifra->ifra_dstaddr, addr->get_sockaddr(addr),
! 125: *addr->get_sockaddr_len(addr));
! 126:
! 127: mask = host_create_netmask(addr->get_family(addr), netmask);
! 128: if (!mask)
! 129: {
! 130: DBG1(DBG_LIB, "invalid netmask: %d", netmask);
! 131: return FALSE;
! 132: }
! 133: memcpy(&ifra->ifra_mask, mask->get_sockaddr(mask),
! 134: *mask->get_sockaddr_len(mask));
! 135: mask->destroy(mask);
! 136: return TRUE;
! 137: }
! 138:
! 139: /**
! 140: * Set the address using the more flexible SIOCAIFADDR/SIOCDIFADDR commands
! 141: * on FreeBSD 10 an newer.
! 142: */
! 143: static bool set_address_impl(private_tun_device_t *this, host_t *addr,
! 144: uint8_t netmask)
! 145: {
! 146: struct in_aliasreq ifra;
! 147:
! 148: memset(&ifra, 0, sizeof(ifra));
! 149: strncpy(ifra.ifra_name, this->if_name, IFNAMSIZ);
! 150:
! 151: if (this->address)
! 152: { /* remove the existing address first */
! 153: if (!set_address_and_mask(&ifra, this->address, this->netmask))
! 154: {
! 155: return FALSE;
! 156: }
! 157: if (ioctl(this->sock, SIOCDIFADDR, &ifra) < 0)
! 158: {
! 159: DBG1(DBG_LIB, "failed to remove existing address on %s: %s",
! 160: this->if_name, strerror(errno));
! 161: return FALSE;
! 162: }
! 163: }
! 164: if (!set_address_and_mask(&ifra, addr, netmask))
! 165: {
! 166: return FALSE;
! 167: }
! 168: if (ioctl(this->sock, SIOCAIFADDR, &ifra) < 0)
! 169: {
! 170: DBG1(DBG_LIB, "failed to add address on %s: %s",
! 171: this->if_name, strerror(errno));
! 172: return FALSE;
! 173: }
! 174: return TRUE;
! 175: }
! 176:
! 177: #else /* __FreeBSD__ */
! 178:
! 179: /**
! 180: * Set the address using the classic SIOCSIFADDR etc. commands on other systems.
! 181: */
! 182: static bool set_address_impl(private_tun_device_t *this, host_t *addr,
! 183: uint8_t netmask)
! 184: {
! 185: struct ifreq ifr;
! 186: host_t *mask;
! 187:
! 188: memset(&ifr, 0, sizeof(ifr));
! 189: strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ);
! 190: memcpy(&ifr.ifr_addr, addr->get_sockaddr(addr),
! 191: *addr->get_sockaddr_len(addr));
! 192:
! 193: if (ioctl(this->sock, SIOCSIFADDR, &ifr) < 0)
! 194: {
! 195: DBG1(DBG_LIB, "failed to set address on %s: %s",
! 196: this->if_name, strerror(errno));
! 197: return FALSE;
! 198: }
! 199: #ifdef __APPLE__
! 200: if (ioctl(this->sock, SIOCSIFDSTADDR, &ifr) < 0)
! 201: {
! 202: DBG1(DBG_LIB, "failed to set dest address on %s: %s",
! 203: this->if_name, strerror(errno));
! 204: return FALSE;
! 205: }
! 206: #endif /* __APPLE__ */
! 207:
! 208: mask = host_create_netmask(addr->get_family(addr), netmask);
! 209: if (!mask)
! 210: {
! 211: DBG1(DBG_LIB, "invalid netmask: %d", netmask);
! 212: return FALSE;
! 213: }
! 214: memcpy(&ifr.ifr_addr, mask->get_sockaddr(mask),
! 215: *mask->get_sockaddr_len(mask));
! 216: mask->destroy(mask);
! 217:
! 218: if (ioctl(this->sock, SIOCSIFNETMASK, &ifr) < 0)
! 219: {
! 220: DBG1(DBG_LIB, "failed to set netmask on %s: %s",
! 221: this->if_name, strerror(errno));
! 222: return FALSE;
! 223: }
! 224: return TRUE;
! 225: }
! 226:
! 227: #endif /* __FreeBSD__ */
! 228:
! 229: METHOD(tun_device_t, set_address, bool,
! 230: private_tun_device_t *this, host_t *addr, uint8_t netmask)
! 231: {
! 232: if (!set_address_impl(this, addr, netmask))
! 233: {
! 234: return FALSE;
! 235: }
! 236: DESTROY_IF(this->address);
! 237: this->address = addr->clone(addr);
! 238: this->netmask = netmask;
! 239: return TRUE;
! 240: }
! 241:
! 242: METHOD(tun_device_t, get_address, host_t*,
! 243: private_tun_device_t *this, uint8_t *netmask)
! 244: {
! 245: if (netmask && this->address)
! 246: {
! 247: *netmask = this->netmask;
! 248: }
! 249: return this->address;
! 250: }
! 251:
! 252: METHOD(tun_device_t, up, bool,
! 253: private_tun_device_t *this)
! 254: {
! 255: struct ifreq ifr;
! 256:
! 257: memset(&ifr, 0, sizeof(ifr));
! 258: strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ);
! 259:
! 260: if (ioctl(this->sock, SIOCGIFFLAGS, &ifr) < 0)
! 261: {
! 262: DBG1(DBG_LIB, "failed to get interface flags for %s: %s", this->if_name,
! 263: strerror(errno));
! 264: return FALSE;
! 265: }
! 266:
! 267: ifr.ifr_flags |= IFF_RUNNING | IFF_UP;
! 268:
! 269: if (ioctl(this->sock, SIOCSIFFLAGS, &ifr) < 0)
! 270: {
! 271: DBG1(DBG_LIB, "failed to set interface flags on %s: %s", this->if_name,
! 272: strerror(errno));
! 273: return FALSE;
! 274: }
! 275: return TRUE;
! 276: }
! 277:
! 278: METHOD(tun_device_t, set_mtu, bool,
! 279: private_tun_device_t *this, int mtu)
! 280: {
! 281: struct ifreq ifr;
! 282:
! 283: memset(&ifr, 0, sizeof(ifr));
! 284: strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ);
! 285: ifr.ifr_mtu = mtu;
! 286:
! 287: if (ioctl(this->sock, SIOCSIFMTU, &ifr) < 0)
! 288: {
! 289: DBG1(DBG_LIB, "failed to set MTU on %s: %s", this->if_name,
! 290: strerror(errno));
! 291: return FALSE;
! 292: }
! 293: this->mtu = mtu;
! 294: return TRUE;
! 295: }
! 296:
! 297: METHOD(tun_device_t, get_mtu, int,
! 298: private_tun_device_t *this)
! 299: {
! 300: struct ifreq ifr;
! 301:
! 302: if (this->mtu > 0)
! 303: {
! 304: return this->mtu;
! 305: }
! 306:
! 307: memset(&ifr, 0, sizeof(ifr));
! 308: strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ);
! 309: this->mtu = TUN_DEFAULT_MTU;
! 310:
! 311: if (ioctl(this->sock, SIOCGIFMTU, &ifr) == 0)
! 312: {
! 313: this->mtu = ifr.ifr_mtu;
! 314: }
! 315: return this->mtu;
! 316: }
! 317:
! 318: METHOD(tun_device_t, get_name, char*,
! 319: private_tun_device_t *this)
! 320: {
! 321: return this->if_name;
! 322: }
! 323:
! 324: METHOD(tun_device_t, get_fd, int,
! 325: private_tun_device_t *this)
! 326: {
! 327: return this->tunfd;
! 328: }
! 329:
! 330: METHOD(tun_device_t, write_packet, bool,
! 331: private_tun_device_t *this, chunk_t packet)
! 332: {
! 333: ssize_t s;
! 334:
! 335: #ifdef __APPLE__
! 336: /* UTUN's expect the packets to be prepended by a 32-bit protocol number
! 337: * instead of parsing the packet again, we assume IPv4 for now */
! 338: uint32_t proto = htonl(AF_INET);
! 339: packet = chunk_cata("cc", chunk_from_thing(proto), packet);
! 340: #endif
! 341: s = write(this->tunfd, packet.ptr, packet.len);
! 342: if (s < 0)
! 343: {
! 344: DBG1(DBG_LIB, "failed to write packet to TUN device %s: %s",
! 345: this->if_name, strerror(errno));
! 346: return FALSE;
! 347: }
! 348: else if (s != packet.len)
! 349: {
! 350: return FALSE;
! 351: }
! 352: return TRUE;
! 353: }
! 354:
! 355: METHOD(tun_device_t, read_packet, bool,
! 356: private_tun_device_t *this, chunk_t *packet)
! 357: {
! 358: chunk_t data;
! 359: ssize_t len;
! 360: bool old;
! 361:
! 362: data = chunk_alloca(get_mtu(this));
! 363:
! 364: old = thread_cancelability(TRUE);
! 365: len = read(this->tunfd, data.ptr, data.len);
! 366: thread_cancelability(old);
! 367: if (len < 0)
! 368: {
! 369: DBG1(DBG_LIB, "reading from TUN device %s failed: %s", this->if_name,
! 370: strerror(errno));
! 371: return FALSE;
! 372: }
! 373: data.len = len;
! 374: #ifdef __APPLE__
! 375: /* UTUN's prepend packets with a 32-bit protocol number */
! 376: data = chunk_skip(data, sizeof(uint32_t));
! 377: #endif
! 378: *packet = chunk_clone(data);
! 379: return TRUE;
! 380: }
! 381:
! 382: METHOD(tun_device_t, destroy, void,
! 383: private_tun_device_t *this)
! 384: {
! 385: if (this->tunfd > 0)
! 386: {
! 387: close(this->tunfd);
! 388: #ifdef __FreeBSD__
! 389: /* tun(4) says the following: "These network interfaces persist until
! 390: * the if_tun.ko module is unloaded, or until removed with the
! 391: * ifconfig(8) command." So simply closing the FD is not enough. */
! 392: struct ifreq ifr;
! 393:
! 394: memset(&ifr, 0, sizeof(ifr));
! 395: strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ);
! 396: if (ioctl(this->sock, SIOCIFDESTROY, &ifr) < 0)
! 397: {
! 398: DBG1(DBG_LIB, "failed to destroy %s: %s", this->if_name,
! 399: strerror(errno));
! 400: }
! 401: #endif /* __FreeBSD__ */
! 402: }
! 403: if (this->sock > 0)
! 404: {
! 405: close(this->sock);
! 406: }
! 407: DESTROY_IF(this->address);
! 408: free(this);
! 409: }
! 410:
! 411: /**
! 412: * Initialize the tun device
! 413: */
! 414: static bool init_tun(private_tun_device_t *this, const char *name_tmpl)
! 415: {
! 416: #ifdef __APPLE__
! 417:
! 418: struct ctl_info info;
! 419: struct sockaddr_ctl addr;
! 420: socklen_t size = IFNAMSIZ;
! 421:
! 422: memset(&info, 0, sizeof(info));
! 423: memset(&addr, 0, sizeof(addr));
! 424:
! 425: this->tunfd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL);
! 426: if (this->tunfd < 0)
! 427: {
! 428: DBG1(DBG_LIB, "failed to open tundevice PF_SYSTEM socket: %s",
! 429: strerror(errno));
! 430: return FALSE;
! 431: }
! 432:
! 433: /* get a control identifier for the utun kernel extension */
! 434: strncpy(info.ctl_name, UTUN_CONTROL_NAME, strlen(UTUN_CONTROL_NAME));
! 435: if (ioctl(this->tunfd, CTLIOCGINFO, &info) < 0)
! 436: {
! 437: DBG1(DBG_LIB, "failed to ioctl tundevice: %s", strerror(errno));
! 438: close(this->tunfd);
! 439: return FALSE;
! 440: }
! 441:
! 442: addr.sc_id = info.ctl_id;
! 443: addr.sc_len = sizeof(addr);
! 444: addr.sc_family = AF_SYSTEM;
! 445: addr.ss_sysaddr = AF_SYS_CONTROL;
! 446: /* allocate identifier dynamically */
! 447: addr.sc_unit = 0;
! 448:
! 449: if (connect(this->tunfd, (struct sockaddr*)&addr, sizeof(addr)) < 0)
! 450: {
! 451: DBG1(DBG_LIB, "failed to connect tundevice: %s", strerror(errno));
! 452: close(this->tunfd);
! 453: return FALSE;
! 454: }
! 455: if (getsockopt(this->tunfd, SYSPROTO_CONTROL, UTUN_OPT_IFNAME,
! 456: this->if_name, &size) < 0)
! 457: {
! 458: DBG1(DBG_LIB, "getting tundevice name failed: %s", strerror(errno));
! 459: close(this->tunfd);
! 460: return FALSE;
! 461: }
! 462: return TRUE;
! 463:
! 464: #elif defined(IFF_TUN)
! 465:
! 466: struct ifreq ifr;
! 467:
! 468: strncpy(this->if_name, name_tmpl ?: "tun%d", IFNAMSIZ);
! 469: this->if_name[IFNAMSIZ-1] = '\0';
! 470:
! 471: this->tunfd = open("/dev/net/tun", O_RDWR);
! 472: if (this->tunfd < 0)
! 473: {
! 474: DBG1(DBG_LIB, "failed to open /dev/net/tun: %s", strerror(errno));
! 475: return FALSE;
! 476: }
! 477:
! 478: memset(&ifr, 0, sizeof(ifr));
! 479:
! 480: /* TUN device, no packet info */
! 481: ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
! 482:
! 483: strncpy(ifr.ifr_name, this->if_name, IFNAMSIZ);
! 484: if (ioctl(this->tunfd, TUNSETIFF, (void*)&ifr) < 0)
! 485: {
! 486: DBG1(DBG_LIB, "failed to configure TUN device: %s", strerror(errno));
! 487: close(this->tunfd);
! 488: return FALSE;
! 489: }
! 490: strncpy(this->if_name, ifr.ifr_name, IFNAMSIZ);
! 491: return TRUE;
! 492:
! 493: #elif defined(__FreeBSD__)
! 494:
! 495: if (name_tmpl)
! 496: {
! 497: DBG1(DBG_LIB, "arbitrary naming of TUN devices is not supported");
! 498: }
! 499:
! 500: this->tunfd = open("/dev/tun", O_RDWR);
! 501: if (this->tunfd < 0)
! 502: {
! 503: DBG1(DBG_LIB, "failed to open /dev/tun: %s", strerror(errno));
! 504: return FALSE;
! 505: }
! 506: fdevname_r(this->tunfd, this->if_name, IFNAMSIZ);
! 507: return TRUE;
! 508:
! 509: #else /* !__FreeBSD__ */
! 510:
! 511: /* this might work on Linux with older TUN driver versions (no IFF_TUN) */
! 512: char devname[IFNAMSIZ];
! 513: /* the same process is allowed to open a device again, but that's not what
! 514: * we want (unless we previously closed a device, which we don't know at
! 515: * this point). therefore, this counter is static so we don't accidentally
! 516: * open a device twice */
! 517: static int i = -1;
! 518:
! 519: if (name_tmpl)
! 520: {
! 521: DBG1(DBG_LIB, "arbitrary naming of TUN devices is not supported");
! 522: }
! 523:
! 524: for (; ++i < 256; )
! 525: {
! 526: snprintf(devname, IFNAMSIZ, "/dev/tun%d", i);
! 527: this->tunfd = open(devname, O_RDWR);
! 528: if (this->tunfd > 0)
! 529: { /* for ioctl(2) calls only the interface name is used */
! 530: snprintf(this->if_name, IFNAMSIZ, "tun%d", i);
! 531: break;
! 532: }
! 533: DBG1(DBG_LIB, "failed to open %s: %s", this->if_name, strerror(errno));
! 534: }
! 535: return this->tunfd > 0;
! 536:
! 537: #endif /* !__APPLE__ */
! 538: }
! 539:
! 540: /*
! 541: * Described in header
! 542: */
! 543: tun_device_t *tun_device_create(const char *name_tmpl)
! 544: {
! 545: private_tun_device_t *this;
! 546:
! 547: INIT(this,
! 548: .public = {
! 549: .read_packet = _read_packet,
! 550: .write_packet = _write_packet,
! 551: .get_mtu = _get_mtu,
! 552: .set_mtu = _set_mtu,
! 553: .get_name = _get_name,
! 554: .get_fd = _get_fd,
! 555: .set_address = _set_address,
! 556: .get_address = _get_address,
! 557: .up = _up,
! 558: .destroy = _destroy,
! 559: },
! 560: .tunfd = -1,
! 561: .sock = -1,
! 562: );
! 563:
! 564: if (!init_tun(this, name_tmpl))
! 565: {
! 566: free(this);
! 567: return NULL;
! 568: }
! 569: DBG1(DBG_LIB, "created TUN device: %s", this->if_name);
! 570:
! 571: this->sock = socket(AF_INET, SOCK_DGRAM, 0);
! 572: if (this->sock < 0)
! 573: {
! 574: DBG1(DBG_LIB, "failed to open socket to configure TUN device");
! 575: destroy(this);
! 576: return NULL;
! 577: }
! 578: return &this->public;
! 579: }
! 580:
! 581: #endif /* TUN devices supported */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>