| /* |
| * |
| * 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 |