blob: 358cea58cd1b9c3a11e185c2156327031220921c [file] [log] [blame]
/*
*
* Copyright (c) 2014-2017 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.
*/
/**
* @file
* This file implements the TunnelEndPoint abstraction APIs in the Inet
* Layer for creation and management of tunnel interfaces instantiated
* within either Linux Sockets or LwIP.
*
*/
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
#endif
#include <InetLayer/TunEndPoint.h>
#include <InetLayer/InetLayer.h>
#include <string.h>
#include <stdio.h>
#include <Weave/Core/WeaveEncoding.h>
#include <Weave/Support/CodeUtils.h>
#include "arpa-inet-compatibility.h"
#if WEAVE_SYSTEM_CONFIG_USE_FUCHSIA_TUN
#include <fuchsia/netstack/cpp/fidl.h>
#endif
namespace nl {
namespace Inet {
using Weave::System::PacketBuffer;
Weave::System::ObjectPool<TunEndPoint, INET_CONFIG_NUM_TUN_ENDPOINTS> TunEndPoint::sPool;
using namespace nl::Weave::Encoding;
#if WEAVE_SYSTEM_CONFIG_USE_FUCHSIA_TUN
constexpr uint32_t kDefaultMtu = 1500;
constexpr uint8_t kTunPortId = 0;
namespace {
fuchsia::net::tun::DevicePortConfig DefaultPortConfig() {
fuchsia::net::tun::BasePortConfig base;
base.set_id(kTunPortId);
// Add both ipv4 and ipv6 for supported rx frame types.
std::vector<fuchsia::hardware::network::FrameType> rxFrameTypes;
rxFrameTypes.push_back(fuchsia::hardware::network::FrameType::IPV4);
rxFrameTypes.push_back(fuchsia::hardware::network::FrameType::IPV6);
base.set_rx_types(std::move(rxFrameTypes));
// Configure default MTU
base.set_mtu(kDefaultMtu);
// Add both ipv4 and ipv6 for supported tx frame types.
// FrameType specifies the type of frame(IPv4/IPv6), features specific to
// the frametype and the supported flags. We set both to 0, as we don't
// need any specific features or flags.
std::vector<fuchsia::hardware::network::FrameTypeSupport> txFrameTypes;
txFrameTypes.push_back(fuchsia::hardware::network::FrameTypeSupport{
fuchsia::hardware::network::FrameType::IPV4, 0,
static_cast<fuchsia::hardware::network::TxFlags>(0)});
txFrameTypes.push_back(fuchsia::hardware::network::FrameTypeSupport{
fuchsia::hardware::network::FrameType::IPV6, 0,
static_cast<fuchsia::hardware::network::TxFlags>(0)});
base.set_tx_types(std::move(txFrameTypes));
fuchsia::net::tun::DevicePortConfig config;
config.set_base(std::move(base));
return config;
}
fuchsia::net::tun::DeviceConfig2 DefaultDeviceConfig() {
fuchsia::net::tun::DeviceConfig2 config;
config.set_blocking(false);
return config;
}
} // namespace
// Creates a tun device.
INET_ERROR TunEndPoint::CreateTunDevice(void) {
fidl::InterfaceHandle<fuchsia::net::tun::Control> tun;
auto config = DefaultDeviceConfig();
zx_status_t err;
InetLayer::FuchsiaPlatformData *platformData = static_cast<InetLayer::FuchsiaPlatformData*>(Layer().GetPlatformData());
if (platformData == NULL || platformData->ctx == NULL) {
WeaveLogError(Inet, "PlatformData is NULL");
return INET_ERROR_INCORRECT_STATE;
}
err = platformData->ctx->svc()->Connect(tun.NewRequest());
if (err != ZX_OK) {
WeaveLogError(Inet, "Connect to network tun failed: %s", zx_status_get_string(err));
return err;
}
mTunCtl = tun.BindSync();
if (!mTunCtl.is_bound()) {
WeaveLogError(Inet, "tunctl is not bound");
return INET_ERROR_INTERFACE_INIT_FAILURE;
}
err = mTunCtl->CreateDevice2(std::move(config), mTunDevice.NewRequest());
if(err != ZX_OK) {
WeaveLogError(Inet, "tunctl failed to create device: %s", zx_status_get_string(err));
return INET_ERROR_INTERFACE_INIT_FAILURE;
}
err = mTunDevice->AddPort(DefaultPortConfig(), mTunPort.NewRequest());
if(err != ZX_OK) {
WeaveLogError(Inet, "tunctl failed to add device port: %s", zx_status_get_string(err));
return INET_ERROR_INTERFACE_INIT_FAILURE;
}
return INET_NO_ERROR;
}
// Add the tun interface to netstack. If successful the tun interface shows up in "net if list".
INET_ERROR TunEndPoint::AddInterfaceToNetstack(fuchsia::hardware::network::DeviceHandle & device, const char *intfName) {
zx_status_t err;
fuchsia::net::stack::DeviceDefinition deviceDefinition;
fuchsia::net::stack::InterfaceConfig config;
config.set_name(intfName);
deviceDefinition.set_ip(std::move(device));
InetLayer::FuchsiaPlatformData *platformData = static_cast<InetLayer::FuchsiaPlatformData*>(Layer().GetPlatformData());
if (platformData == NULL || platformData->ctx == NULL) {
WeaveLogError(Inet, "PlatformData is NULL");
return INET_ERROR_BAD_ARGS;
}
err = platformData->ctx->svc()->Connect(mStackPtr.NewRequest());
if (err != ZX_OK) {
WeaveLogError(Inet,"connect to stack fidl failed");
return err;
}
fuchsia::net::stack::Stack_AddInterface_Result result;
err = mStackPtr->AddInterface(std::move(config), std::move(deviceDefinition), &result);
if (err != ZX_OK || result.is_err()) {
WeaveLogError(Inet,"AddInterface failed %d:%s", err, zx_status_get_string(err));
return err;
}
mInterfaceId = result.response().id;
return INET_NO_ERROR;
}
INET_ERROR TunEndPoint::CreateHostDevice(const char *intfName) {
fuchsia::hardware::network::DeviceHandle device;
mTunDevice->GetDevice(device.NewRequest());
return AddInterfaceToNetstack(device, intfName);
}
/* Read packets from TUN device in Linux */
zx_status_t TunEndPoint::GetSignalsEventPair(zx::eventpair &events)
{
if (!mTunCtl.is_bound()) {
WeaveLogError(Inet, "Can't read as mtunctl is not bound");
return ZX_ERR_INTERNAL;
}
zx_status_t status = mTunDevice->GetSignals(&events);
if (status != 0) {
WeaveLogError(Inet, "Failed to get signals from Tun: %s", zx_status_get_string(status));
return status;
}
return ZX_OK;
}
#endif //WEAVE_SYSTEM_CONFIG_USE_FUCHSIA_TUN
/**
* Initialize the Tunnel EndPoint object.
*
* @note
* By convention, the \c Init method on \c EndPointBasis
* subclasses is \c private. It should not be used outside \c InetLayer.
*
* @param[in] inetLayer A pointer to the Inet layer object that
* created the Tunnel EndPoint.
*
*/
void TunEndPoint::Init(InetLayer *inetLayer)
{
InitEndPointBasis(*inetLayer);
}
/**
* Open a tunnel pseudo interface and create a handle to it.
*
* @note
* This method has different signatures on LwIP systems and
* POSIX systems. On LwIP, there is an argument for specifying the name
* of the tunnel interface. On POSIX, the method has no arguments and the
* name of the tunnel device is implied.
*
* @return INET_NO_ERROR on success, else a corresponding INET mapped OS error.
*/
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
INET_ERROR TunEndPoint::Open (void)
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
INET_ERROR TunEndPoint::Open (const char *intfName)
#endif //WEAVE_SYSTEM_CONFIG_USE_SOCKETS
{
INET_ERROR err = INET_NO_ERROR;
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
struct netif *tNetif = NULL;
// Lock LwIP stack
LOCK_TCPIP_CORE();
tNetif = netif_add(&mTunNetIf, NULL, NULL, NULL, this, TunInterfaceNetifInit, tcpip_input);
// UnLock LwIP stack
UNLOCK_TCPIP_CORE();
VerifyOrExit(tNetif != NULL, err = INET_ERROR_INTERFACE_INIT_FAILURE);
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
//Create the tunnel device
err = TunDevOpen(intfName);
SuccessOrExit(err);
WeaveLogProgress(Inet, "Opened tunnel device: %s", intfName);
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
if (err == INET_NO_ERROR)
mState = kState_Open;
exit:
return err;
}
/**
* Close the tunnel pseudo interface device.
*
*/
void TunEndPoint::Close (void)
{
if (mState != kState_Closed)
{
// For LwIP, we do not remove the netif as it would have
// an impact on the interface iterator in Weave which
// might lose reference to a particular netif index that
// it might be holding on to.
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
#if WEAVE_SYSTEM_CONFIG_USE_FUCHSIA_TUN
if (mInterfaceId >= 0)
#else
if (mSocket >= 0)
#endif // WEAVE_SYSTEM_CONFIG_USE_FUCHSIA_TUN
{
Weave::System::Layer& lSystemLayer = SystemLayer();
// Wake the thread calling select so that it recognizes the socket is closed.
lSystemLayer.WakeSelect();
TunDevClose();
}
// Clear any results from select() that indicate pending I/O for the socket.
mPendingIO.Clear();
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
mState = kState_Closed;
}
}
/**
* Close the tunnel pseudo interface device and decrement the reference count
* of the InetLayer object.
*
*/
void TunEndPoint::Free()
{
Close();
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
DeferredFree(kReleaseDeferralErrorTactic_Release);
#else // !WEAVE_SYSTEM_CONFIG_USE_LWIP
Release();
#endif // !WEAVE_SYSTEM_CONFIG_USE_LWIP
}
/**
* Send an IPv6 packet to the tun device to be sent out.
*
* @note
* This method performs a couple of minimal sanity checks on the packet to
* be sure it is IP version 6 then dispatches it for encapsulation in a
* Weave tunneling message.
*
* @param[in] message the IPv6 packet to send.
*
* @retval INET_NO_ERROR success: packet encapsulated and queued to send
* @retval INET_ERROR_NOT_SUPPORTED packet not IP version 6
* @retval INET_ERROR_BAD_ARGS \c message is a \c NULL pointer
*
*/
INET_ERROR TunEndPoint::Send (PacketBuffer *msg)
{
INET_ERROR ret = INET_NO_ERROR;
ret = CheckV6Sanity(msg);
if (ret == INET_NO_ERROR)
{
ret = TunDevSendMessage(msg);
}
return ret;
}
/**
* Extract the activation state of the tunnel interface.
*
* @returns \c true if the tunnel interface is active,
* otherwise \c false.
*/
bool TunEndPoint::IsInterfaceUp (void) const
{
bool ret = false;
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
// Lock LwIP stack
LOCK_TCPIP_CORE();
ret = netif_is_up(&mTunNetIf);
// UnLock LwIP stack
UNLOCK_TCPIP_CORE();
ExitNow();
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
#if WEAVE_SYSTEM_CONFIG_USE_FUCHSIA_TUN
if (!mStackPtr.is_bound()) {
WeaveLogError(Inet, "mStackPtr is not bound");
ExitNow(ret = false);
}
else {
zx_status_t err;
fuchsia::net::stack::Stack_GetInterfaceInfo_Result result;
err = mStackPtr->GetInterfaceInfo(mInterfaceId, &result);
if (err != ZX_OK) {
WeaveLogError(Inet, "GetInterfaceInfo failed: %s", zx_status_get_string(err));
ExitNow(ret = false);
}
fuchsia::net::stack::Stack_GetInterfaceInfo_Response resp = std::move(result.response());
if (resp.info.properties.administrative_status == fuchsia::net::stack::AdministrativeStatus::ENABLED &&
resp.info.properties.physical_status == fuchsia::net::stack::PhysicalStatus::UP) {
ExitNow(ret = true);
}
}
#elif WEAVE_SYSTEM_CONFIG_USE_SOCKETS
int sockfd = INET_INVALID_SOCKET_FD;
struct ::ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
//Get interface
if (TunGetInterface(mSocket, &ifr) < 0)
{
ExitNow();
}
sockfd = socket(AF_INET6, SOCK_DGRAM | NL_SOCK_CLOEXEC, IPPROTO_IP);
if (sockfd < 0)
{
ExitNow();
}
//Get interface flags
if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0)
{
ExitNow();
}
ret = ((ifr.ifr_flags & IFF_UP) == IFF_UP);
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
exit:
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS && !WEAVE_SYSTEM_CONFIG_USE_FUCHSIA_TUN
if (sockfd >= 0)
{
close(sockfd);
}
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
return ret;
}
/**
* Activate the tunnel interface.
*
* @retval INET_NO_ERROR success: tunnel interface is activated.
* @retval other another system or platform error
*/
INET_ERROR TunEndPoint::InterfaceUp (void)
{
INET_ERROR err = INET_NO_ERROR;
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
// Lock LwIP stack
LOCK_TCPIP_CORE();
netif_set_up(&mTunNetIf);
// UnLock LwIP stack
UNLOCK_TCPIP_CORE();
ExitNow();
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
#if WEAVE_SYSTEM_CONFIG_USE_FUCHSIA_TUN
// Set tun device to online.
fuchsia::net::stack::Stack_EnableInterface_Result result;
err = mTunPort->SetOnline(true);
if (err != ZX_OK) {
WeaveLogError(Inet, "InterfaceUp failed due to SetOnline error: %s", zx_status_get_string(err));
ExitNow();
}
if (!mStackPtr.is_bound()) {
ExitNow(err = ZX_ERR_BAD_STATE);
}
// Enable the tun interface.
err = mStackPtr->EnableInterface(mInterfaceId, &result);
#elif WEAVE_SYSTEM_CONFIG_USE_SOCKETS
int sockfd = INET_INVALID_SOCKET_FD;
struct ::ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
//Get interface
if (TunGetInterface(mSocket, &ifr) < 0)
{
ExitNow(err = Weave::System::MapErrorPOSIX(errno));
}
sockfd = socket(AF_INET6, SOCK_DGRAM | NL_SOCK_CLOEXEC, IPPROTO_IP);
if (sockfd < 0)
{
ExitNow(err = Weave::System::MapErrorPOSIX(errno));
}
//Get interface flags
if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0)
{
ExitNow(err = Weave::System::MapErrorPOSIX(errno));
}
//Set flag to activate interface
ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0)
{
err = Weave::System::MapErrorPOSIX(errno);
}
//Set the MTU
ifr.ifr_mtu = WEAVE_CONFIG_TUNNEL_INTERFACE_MTU;
if (ioctl(sockfd, SIOCSIFMTU, &ifr) < 0)
{
ExitNow(err = Weave::System::MapErrorPOSIX(errno));
}
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
exit:
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS && !WEAVE_SYSTEM_CONFIG_USE_FUCHSIA_TUN
if (sockfd >= 0)
{
close(sockfd);
}
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
return err;
}
/**
* @brief Deactivate the tunnel interface.
*
* @retval INET_NO_ERROR success: tunnel interface is deactivated.
* @retval other another system or platform error
*/
INET_ERROR TunEndPoint::InterfaceDown (void)
{
INET_ERROR err = INET_NO_ERROR;
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
// Lock LwIP stack
LOCK_TCPIP_CORE();
//Remove the link local address from the netif
memset(&(mTunNetIf.ip6_addr[0]), 0, sizeof(ip6_addr_t));
netif_set_down(&mTunNetIf);
// UnLock LwIP stack
UNLOCK_TCPIP_CORE();
ExitNow();
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
#if WEAVE_SYSTEM_CONFIG_USE_FUCHSIA_TUN
fuchsia::net::stack::Stack_DisableInterface_Result result;
if (!mStackPtr.is_bound()) {
WeaveLogError(Inet, "InterfaceDown failed as mStackPtr is not bound");
ExitNow(err = ZX_ERR_BAD_STATE);
}
// Set offline and disable the interface.
err = mTunPort->SetOnline(false);
if (err != ZX_OK) {
WeaveLogError(Inet, "InterfaceDown failed due to SetOnline error: %s", zx_status_get_string(err));
ExitNow();
}
err = mStackPtr->DisableInterface(mInterfaceId, &result);
if (err != ZX_OK) {
WeaveLogError(Inet, "DisableInterface failed: %s", zx_status_get_string(err));
return err;
}
#elif WEAVE_SYSTEM_CONFIG_USE_SOCKETS
int sockfd = INET_INVALID_SOCKET_FD;
struct ::ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
//Get interface
if (TunGetInterface(mSocket, &ifr) < 0)
{
ExitNow(err = Weave::System::MapErrorPOSIX(errno));
}
sockfd = socket(AF_INET6, SOCK_DGRAM | NL_SOCK_CLOEXEC, IPPROTO_IP);
if (sockfd < 0)
{
ExitNow(err = Weave::System::MapErrorPOSIX(errno));
}
//Get interface flags
if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0)
{
ExitNow(err = Weave::System::MapErrorPOSIX(errno));
}
//Set flag to deactivate interface
ifr.ifr_flags &= ~(IFF_UP);
if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0)
{
err = Weave::System::MapErrorPOSIX(errno);
}
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
exit:
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS && !WEAVE_SYSTEM_CONFIG_USE_FUCHSIA_TUN
if (sockfd >= 0)
{
close(sockfd);
}
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
return err;
}
/**
* @brief Get the tunnel interface identifier.
*
* @return The tunnel interface identifier.
*/
InterfaceId TunEndPoint::GetTunnelInterfaceId(void)
{
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
return &mTunNetIf;
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS && !WEAVE_SYSTEM_CONFIG_USE_FUCHSIA_TUN
INET_ERROR err = INET_NO_ERROR;
InterfaceId tunIntfId = INET_NULL_INTERFACEID;
const char *tunIntfPtr = &tunIntfName[0];
err = InterfaceNameToId(tunIntfPtr, tunIntfId);
if (err != INET_NO_ERROR)
{
tunIntfId = INET_NULL_INTERFACEID;
}
return tunIntfId;
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
#if WEAVE_SYSTEM_CONFIG_USE_FUCHSIA_TUN
return mInterfaceId;
#endif
}
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
/* Function for sending the IPv6 packets over LwIP */
INET_ERROR TunEndPoint::TunDevSendMessage(PacketBuffer *msg)
{
INET_ERROR ret = INET_NO_ERROR;
struct pbuf *p = NULL;
err_t err = ERR_OK;
// no packet could be read, silently ignore this
VerifyOrExit(msg != NULL, ret = INET_ERROR_BAD_ARGS);
p = (struct pbuf *)msg;
//Call the input function for the netif object in LWIP.
//This essentially creates a TCP_IP msg and puts into
//the mbox message queue for processing by the TCP/IP
//stack.
if ((err = tcpip_input(p, &mTunNetIf)) != ERR_OK)
{
LWIP_DEBUGF(NETIF_DEBUG, ("tunNetif_input: IP input error"));
ExitNow(ret = Weave::System::MapErrorLwIP(err));
}
exit:
return (ret);
}
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
/* Function for sending the IPv6 packets over Linux sockets */
INET_ERROR TunEndPoint::TunDevSendMessage(PacketBuffer *msg)
{
INET_ERROR ret = INET_NO_ERROR;
uint8_t *p = NULL;
#if WEAVE_SYSTEM_CONFIG_USE_FUCHSIA_TUN
fuchsia::net::tun::Frame frame;
fuchsia::net::tun::Device2_WriteFrame_Result result;
if (msg == NULL) {
return INET_ERROR_BAD_ARGS;
}
p = msg->Start();
std::vector<uint8_t> data(p, p + msg->DataLength());
if (!mTunCtl.is_bound()) {
WeaveLogError(Inet, "send failed as tunctl is not bound");
return ZX_ERR_BAD_STATE;
}
frame.set_port(kTunPortId);
frame.set_frame_type(fuchsia::hardware::network::FrameType::IPV6);
frame.set_data(data);
ret = mTunDevice->WriteFrame(std::move(frame), &result);
if (ret != ZX_OK) {
WeaveLogError(Inet, "write failed: %s", zx_status_get_string(ret));
return ret;
}
if (result.is_err()) {
WeaveLogError(Inet, "write failed: %s", zx_status_get_string(result.err()));
return result.err();
}
#else
ssize_t lenSent = 0;
// no packet could be read, silently ignore this
VerifyOrExit(msg != NULL, ret = INET_ERROR_BAD_ARGS);
p = msg->Start();
lenSent = write(mSocket, p, msg->DataLength());
if (lenSent < 0)
{
ExitNow(ret = Weave::System::MapErrorPOSIX(errno));
}
else if (lenSent < msg->DataLength())
{
ExitNow(ret = INET_ERROR_OUTBOUND_MESSAGE_TRUNCATED);
}
exit:
#endif
if (msg != NULL)
{
PacketBuffer::Free(msg);
}
return (ret);
}
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
/* Function that performs some basic sanity checks for IPv6 packets */
INET_ERROR TunEndPoint::CheckV6Sanity (PacketBuffer *msg)
{
INET_ERROR err = INET_NO_ERROR;
uint8_t *p = NULL;
struct ip6_hdr *ip6hdr = NULL;
p = msg->Start();
ip6hdr = (struct ip6_hdr *)p;
VerifyOrExit(ip6hdr != NULL, err = INET_ERROR_BAD_ARGS);
//Do some IPv6 sanity checks
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
if (IP6H_V(ip6hdr) != 6)
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
if ((ip6hdr->ip6_vfc >> 4) != 6)
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
{
ExitNow(err = INET_ERROR_NOT_SUPPORTED);
}
exit:
return err;
}
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
/* Handler to send received packet to upper layer callback */
void TunEndPoint::HandleDataReceived (PacketBuffer *msg)
{
INET_ERROR err = INET_NO_ERROR;
if (mState == kState_Open && OnPacketReceived != NULL)
{
err = CheckV6Sanity(msg);
if (err == INET_NO_ERROR)
{
OnPacketReceived(this, msg);
}
else
{
if (OnReceiveError != NULL)
{
OnReceiveError(this, err);
}
PacketBuffer::Free(msg);
}
}
else
{
PacketBuffer::Free(msg);
}
}
/* Post an event to the Inet layer event queue from LwIP */
err_t TunEndPoint::LwIPPostToInetEventQ (struct netif *netif, struct pbuf *p)
{
err_t lwipErr = ERR_OK;
INET_ERROR err = INET_NO_ERROR;
TunEndPoint* ep = static_cast<TunEndPoint *>(netif->state);
Weave::System::Layer& lSystemLayer = ep->SystemLayer();
// Allocate space for the tunneled IP packet. The reserved space will be the
// default for the Weave and underlying TCP/IP headers (WEAVE_SYSTEM_CONFIG_HEADER_RESERVE_SIZE).
// The requested data size will include the full pbuf received from LwIP plus
// WEAVE_TRAILER_RESERVE_SIZE for holding the HMAC.
PacketBuffer* buf = PacketBuffer::NewWithAvailableSize(p->tot_len + WEAVE_TRAILER_RESERVE_SIZE);
VerifyOrExit(buf != NULL, lwipErr = ERR_MEM);
buf->SetDataLength(p->tot_len);
// Make a pbuf alloc and copy to post to Inetlayer queue because LwIP would free the
// passed pbuf as it made a down-call to send it out the tunnel netif.
lwipErr = pbuf_copy((struct pbuf *)buf, p);
VerifyOrExit(lwipErr == ERR_OK, (void)lwipErr);
err = lSystemLayer.PostEvent(*ep, kInetEvent_TunDataReceived, (uintptr_t)buf);
VerifyOrExit(err == INET_NO_ERROR, lwipErr = ERR_MEM);
buf = NULL;
exit:
if (buf != NULL)
{
PacketBuffer::Free(buf);
}
return lwipErr;
}
#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5
#if LWIP_IPV4
/* Output handler for netif */
err_t TunEndPoint::LwIPOutputIPv4(struct netif *netif, struct pbuf *p, const ip4_addr_t *addr)
{
LWIP_UNUSED_ARG(addr);
return LwIPPostToInetEventQ(netif, p);
}
#endif // LWIP_IPV4
#if LWIP_IPV6
/* Output handler for netif */
err_t TunEndPoint::LwIPOutputIPv6(struct netif *netif, struct pbuf *p, const ip6_addr_t *addr)
{
LWIP_UNUSED_ARG(addr);
return LwIPPostToInetEventQ(netif, p);
}
#endif // LWIP_IPV4
#else // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR < 5
/* Receive message in LwIP */
err_t TunEndPoint::LwIPReceiveTunMessage (struct netif *netif, struct pbuf *p, ip4_addr_t *addr)
{
LWIP_UNUSED_ARG(addr);
return LwIPPostToInetEventQ(netif, p);
}
#if LWIP_IPV6
err_t TunEndPoint::LwIPReceiveTunV6Message (struct netif *netif, struct pbuf *p, ip6_addr_t *addr)
{
LWIP_UNUSED_ARG(addr);
return LwIPPostToInetEventQ(netif, p);
}
#endif // LWIP_IPV6
#endif // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR < 5
/* Initialize the LwIP tunnel netif interface */
err_t TunEndPoint::TunInterfaceNetifInit (struct netif *netif)
{
netif->name[0] = 't';
netif->name[1] = 'n';
#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5
#if LWIP_IPV4
netif->output = LwIPOutputIPv4;
#endif /* LWIP_IPV6 */
#if LWIP_IPV6
netif->output_ip6 = LwIPOutputIPv6;
#endif /* LWIP_IPV6 */
#else // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR < 5
netif->output = LwIPReceiveTunMessage;
#if LWIP_IPV6
netif->output_ip6 = LwIPReceiveTunV6Message;
#endif /* LWIP_IPV6 */
#endif // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR < 5
netif->linkoutput = NULL;
netif->mtu = WEAVE_CONFIG_TUNNEL_INTERFACE_MTU;
netif->hwaddr_len = 6;
memset(netif->hwaddr, 0, NETIF_MAX_HWADDR_LEN);
netif->hwaddr[5] = 1;
#if LWIP_VERSION_MAJOR == 1 && LWIP_VERSION_MINOR < 5
/* device capabilities */
netif->flags |= NETIF_FLAG_POINTTOPOINT;
#endif // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5
return ERR_OK;
}
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
/* Open a tun device in linux */
INET_ERROR TunEndPoint::TunDevOpen (const char *intfName)
{
#if WEAVE_SYSTEM_CONFIG_USE_FUCHSIA_TUN
INET_ERROR ret = INET_NO_ERROR;
//Create the tunnel device
ret = CreateTunDevice();
if (ret != 0) {
WeaveLogError(Inet, "CreateTunDevice failed %d", ret);
return ret;
}
ret = CreateHostDevice(intfName);
if (ret != 0) {
WeaveLogError(Inet, "CreateHostDevice failed %d", ret);
return ret;
}
return ret;
#else
struct ::ifreq ifr;
int fd = INET_INVALID_SOCKET_FD;
INET_ERROR ret = INET_NO_ERROR;
if ((fd = open(INET_CONFIG_TUNNEL_DEVICE_NAME, O_RDWR | NL_O_CLOEXEC)) < 0)
{
ExitNow(ret = Weave::System::MapErrorPOSIX(errno));
}
//Keep copy of open device fd
mSocket = fd;
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
if (*intfName)
{
strncpy(ifr.ifr_name, intfName, sizeof(ifr.ifr_name) - 1);
}
if (ioctl(fd, TUNSETIFF, (void *) &ifr) < 0)
{
ExitNow(ret = Weave::System::MapErrorPOSIX(errno));
}
//Verify name
memset(&ifr, 0, sizeof(ifr));
if (TunGetInterface(fd, &ifr) < 0)
{
ExitNow(ret = Weave::System::MapErrorPOSIX(errno));
}
if (ifr.ifr_name[0] != '\0')
{
//Keep member copy of interface name and Id
strncpy(tunIntfName, ifr.ifr_name, sizeof(tunIntfName) - 1);
tunIntfName[sizeof(tunIntfName) - 1] = '\0';
}
else
{
ExitNow(ret = Weave::System::MapErrorPOSIX(errno));
}
exit:
if (ret != INET_NO_ERROR)
{
TunDevClose();
}
return ret;
#endif
}
/* Close a tun device */
void TunEndPoint::TunDevClose (void)
{
#if WEAVE_SYSTEM_CONFIG_USE_FUCHSIA_TUN
INET_ERROR err = INET_NO_ERROR;
fuchsia::net::stack::Stack_DelEthernetInterface_Result result;
if (!mStackPtr.is_bound()) {
WeaveLogError(Inet, "TunDevClose failed as mStackPtr is not bound to service");
return;
}
err = mStackPtr->DelEthernetInterface(mInterfaceId, &result);
if (err != ZX_OK) {
WeaveLogError(Inet, "DelInterface failed %d: %s", err, zx_status_get_string(err));
}
mInterfaceId = 0;
#else
if (mSocket >= 0)
{
close(mSocket);
}
mSocket = INET_INVALID_SOCKET_FD;
#endif
}
#if !WEAVE_SYSTEM_CONFIG_USE_FUCHSIA_TUN
/* Get the tun device interface in Linux */
int TunEndPoint::TunGetInterface (int fd,
struct ::ifreq *ifr)
{
return ioctl(fd, TUNGETIFF, (void*)ifr);
}
#endif // !WEAVE_SYSTEM_CONFIG_USE_FUCHSIA_TUN
INET_ERROR TunEndPoint::TunDevRead (PacketBuffer *msg)
{
INET_ERROR err = INET_NO_ERROR;
uint8_t *p = NULL;
p = msg->Start();
#ifdef WEAVE_SYSTEM_CONFIG_USE_FUCHSIA_TUN
fuchsia::net::tun::Device2_ReadFrame_Result result;
if (!mTunCtl.is_bound()) {
WeaveLogError(Inet, "Can't read as mTunCtl is not bound");
ExitNow(err = ZX_ERR_BAD_STATE);
}
err = mTunDevice->ReadFrame(&result);
if (result.is_err()) {
ExitNow(err = result.err());
}
if (result.is_response()) {
std::vector<uint8_t> data = result.response().frame.data();
memcpy(p, data.data(), data.size());
msg->SetDataLength((uint16_t)data.size());
}
#else
ssize_t rcvLen;
rcvLen = read(mSocket, p, msg->AvailableDataLength());
if (rcvLen < 0)
{
err = Weave::System::MapErrorPOSIX(errno);
}
else if (rcvLen > msg->AvailableDataLength())
{
err = INET_ERROR_INBOUND_MESSAGE_TOO_BIG;
}
else
{
msg->SetDataLength((uint16_t)rcvLen);
}
#endif //WEAVE_SYSTEM_CONFIG_USE_FUCHSIA_TUN
exit:
return err;
}
/* Prepare socket for reading */
SocketEvents TunEndPoint::PrepareIO ()
{
SocketEvents res;
if (mState == kState_Open && OnPacketReceived != NULL)
{
res.SetRead();
}
return res;
}
/* Read from the Tun device in Linux and pass up to upper layer callback */
void TunEndPoint::HandlePendingIO ()
{
INET_ERROR err = INET_NO_ERROR;
if (mState == kState_Open && OnPacketReceived != NULL && mPendingIO.IsReadable())
{
PacketBuffer *buf = PacketBuffer::New(0);
if (buf != NULL)
{
//Read data from Tun Device
err = TunDevRead(buf);
if (err == INET_NO_ERROR)
{
err = CheckV6Sanity(buf);
}
}
else
{
err = INET_ERROR_NO_MEMORY;
}
if (err == INET_NO_ERROR)
{
OnPacketReceived(this, buf);
}
else
{
PacketBuffer::Free(buf);
if (OnReceiveError != NULL)
{
OnReceiveError(this, err);
}
}
}
mPendingIO.Clear();
}
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
} // namespace Inet
} // namespace nl