blob: 2a355510a4055d68195297978c16250584a4fc38 [file] [edit]
/*
*
* Copyright (c) 2016 Nest Labs, Inc.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Description:
* This file implements the code which managed the TUN interface.
*
*/
#if HAVE_CONFIG_H
#include <config.h>
#endif
#ifndef ASSERT_MACROS_USE_SYSLOG
#define ASSERT_MACROS_USE_SYSLOG 1
#endif
#include "assert-macros.h"
#include "pt.h"
#include <stdio.h>
#include <stdlib.h>
#include "tunnel.h"
#include "string-utils.h"
#include <syslog.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <netinet/in.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <unistd.h>
#ifndef __APPLE__
#include <linux/if_tun.h>
#endif
#include <net/if.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <net/route.h> // AF_ROUTE things
#ifdef __APPLE__
#include <netinet6/in6_var.h>
#include <netinet6/nd6.h> // ND6_INFINITE_LIFETIME
#include <net/if_dl.h> // struct sockaddr_dl
#include <net/if_utun.h>
#include <net/if.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/kern_control.h>
#include <sys/ioctl.h>
#include <sys/sys_domain.h>
#include <sys/kern_event.h>
#define IFEF_NOAUTOIPV6LL 0x2000 /* Interface IPv6 LinkLocal address not provided by kernel */
#endif
#ifndef TUNNEL_TUNTAP_DEVICE
#define TUNNEL_TUNTAP_DEVICE "/dev/net/tun"
#endif
int
tunnel_open(const char* tun_name)
{
int fd = -1;
char *device = NULL;
if ((tun_name == NULL) || (tun_name[0] == 0)) {
tun_name = TUNNEL_DEFAULT_INTERFACE_NAME;
}
syslog(LOG_INFO, "Opening tun interface socket with name \"%s\"", tun_name);
#if defined(UTUN_CONTROL_NAME)
int error = 0;
fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL);
struct sockaddr_ctl addr;
/* get/set the id */
struct ctl_info info;
memset(&info, 0, sizeof(info));
strncpy(info.ctl_name, UTUN_CONTROL_NAME, strlen(UTUN_CONTROL_NAME));
error = ioctl(fd, CTLIOCGINFO, &info);
if (error) {
syslog(LOG_ERR, "Failed to open utun interface: %s", strerror(errno));
close(fd);
fd = -1;
goto bail;
}
addr.sc_id = info.ctl_id;
addr.sc_len = sizeof(addr);
addr.sc_family = AF_SYSTEM;
addr.ss_sysaddr = AF_SYS_CONTROL;
addr.sc_unit = 0; /* allocate dynamically */
if (strhasprefix(tun_name, "utun"))
addr.sc_unit = (int)strtol(tun_name + 4, NULL, 10) + 1;
error = connect(fd, (struct sockaddr*)&addr, sizeof(addr));
if (error && errno == EBUSY) {
addr.sc_unit = 0; /* allocate dynamically */
error = connect(fd, (struct sockaddr*)&addr, sizeof(addr));
}
if (error) {
syslog(LOG_ERR, "Failed to open tun interface: %s", strerror(errno));
close(fd);
fd = -1;
goto bail;
}
goto bail;
#else
#ifdef __APPLE__
if (strhasprefix(tun_name, "utun"))
tun_name = "tun0";
asprintf(&device, "/dev/%s", tun_name);
#else
device = strdup(TUNNEL_TUNTAP_DEVICE);
#endif
require(NULL != device, bail);
fd = open(device, O_RDWR | O_NONBLOCK);
if (0 > fd) {
syslog(LOG_ERR, "Failed to open tun interface: %s", strerror(errno));
perror("open-tun");
goto bail;
}
#endif
#ifdef TUNSETIFF
struct ifreq ifr = { .ifr_flags = IFF_TUN | IFF_NO_PI };
strncpy(ifr.ifr_name, tun_name, IFNAMSIZ);
require(0 == ioctl(fd, TUNSETIFF, (void*)&ifr), bail);
// Verify that the name was set. If it wasn't
// we need to fail.
char name[20] = "";
if (tunnel_get_name(fd, name, sizeof(name)) != 0) {
syslog(LOG_ERR, "Unable to set name on tun interface: %s", strerror(errno));
perror("open-tun");
close(fd);
fd = -1;
goto bail;
}
if (name[0] == 0) {
syslog(LOG_ERR, "Unable to set name on tun interface");
close(fd);
fd = -1;
goto bail;
}
#endif
#if defined(TUNSETLINK) && defined(ARPHRD_6LOWPAN)
int val = ARPHRD_6LOWPAN;
if (ioctl(fd, TUNSETLINK, (unsigned long) val) < 0) {
perror("TUNSETLINK");
}
#endif
bail:
free(device);
return fd;
}
void
tunnel_close(int fd)
{
close(fd);
}
int
tunnel_get_name(
int fd, char* name, int maxlen
)
{
int ret = -1;
if (maxlen && name) name[0] = 0;
#if defined(UTUN_CONTROL_NAME)
socklen_t len = maxlen;
if (0 == getsockopt(fd, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, name, &len)) {
ret = 0;
goto bail;
}
#elif defined(TUNGETIFF)
struct ifreq ifr = { };
require(0 == ioctl(fd, TUNGETIFF, (void*)&ifr), bail);
strncpy(name, ifr.ifr_name, maxlen);
#else
struct stat st;
ret = fstat(fd, &st);
if (ret) {
perror("tunnel_get_name: fstat failed.");
goto bail;
}
devname_r(st.st_rdev, S_IFCHR, name, (int)maxlen);
#endif
ret = 0;
bail:
return ret;
}
int
tunnel_set_hwaddr(int fd, uint8_t *addr, int addr_len)
{
// TODO: Implement me
// Maybe also TUNSETLINK
// Should use SIOCSIFHWADDR...?
return -1;
}