blob: c86a3fd6c6043fbcda2affeac1de0094b8617dc8 [file] [log] [blame]
/*
* Copyright (c) 2018, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file implements the platform network on Linux.
*/
#include "openthread-core-config.h"
#include "platform-posix.h"
#include <arpa/inet.h>
#include <assert.h>
#include <fcntl.h>
#include <ifaddrs.h>
#if __linux__
#include <linux/if_tun.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#endif
#include <net/if.h>
#include <net/if_arp.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <openthread/icmp6.h>
#include <openthread/instance.h>
#include <openthread/ip6.h>
#include <openthread/message.h>
#include "common/code_utils.hpp"
#include "common/logging.hpp"
#include "net/ip6_address.hpp"
#if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE
#ifndef OPENTHREAD_POSIX_TUN_DEVICE
#define OPENTHREAD_POSIX_TUN_DEVICE "/dev/net/tun"
#endif // OPENTHREAD_TUN_DEVICE
// from linux/ipv6.h
struct in6_ifreq
{
struct in6_addr ifr6_addr;
__u32 ifr6_prefixlen;
int ifr6_ifindex;
};
static otInstance * sInstance = NULL;
static int sTunFd = -1; ///< Used to exchange IPv6 packets.
static int sIpFd = -1; ///< Used to manage IPv6 stack on Thread interface.
static int sNetlinkFd = -1; ///< Used to receive netlink events.
static unsigned int sTunIndex = 0;
static char sTunName[IFNAMSIZ];
static const size_t kMaxIp6Size = 1536;
static void UpdateUnicast(otInstance *aInstance, const otIp6Address &aAddress, uint8_t aPrefixLength, bool aIsAdded)
{
struct in6_ifreq ifr6;
otError error = OT_ERROR_NONE;
assert(sInstance == aInstance);
VerifyOrExit(sIpFd > 0, error = OT_ERROR_INVALID_STATE);
memcpy(&ifr6.ifr6_addr, &aAddress, sizeof(ifr6.ifr6_addr));
ifr6.ifr6_ifindex = static_cast<int>(sTunIndex);
ifr6.ifr6_prefixlen = aPrefixLength;
if (aIsAdded)
{
VerifyOrDie(ioctl(sIpFd, SIOCSIFADDR, &ifr6) == 0, OT_EXIT_ERROR_ERRNO);
}
else
{
VerifyOrExit(ioctl(sIpFd, SIOCDIFADDR, &ifr6) == 0, perror("ioctl"); error = OT_ERROR_FAILED);
}
exit:
if (error != OT_ERROR_NONE)
{
otLogWarnPlat("%s: %s", __func__, otThreadErrorToString(error));
}
else
{
otLogInfoPlat("%s: %s", __func__, otThreadErrorToString(error));
}
}
static void UpdateMulticast(otInstance *aInstance, const otIp6Address &aAddress, bool aIsAdded)
{
struct ipv6_mreq mreq;
otError error = OT_ERROR_NONE;
assert(sInstance == aInstance);
VerifyOrExit(sIpFd > 0);
memcpy(&mreq.ipv6mr_multiaddr, &aAddress, sizeof(mreq.ipv6mr_multiaddr));
mreq.ipv6mr_interface = sTunIndex;
VerifyOrExit(
setsockopt(sIpFd, IPPROTO_IPV6, (aIsAdded ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP), &mreq, sizeof(mreq)) == 0,
perror("setsockopt");
error = OT_ERROR_FAILED);
exit:
SuccessOrDie(error);
otLogInfoPlat("%s: %s", __func__, otThreadErrorToString(error));
}
static void UpdateLink(otInstance *aInstance)
{
otError error = OT_ERROR_NONE;
struct ifreq ifr;
assert(sInstance == aInstance);
VerifyOrExit(sIpFd > 0);
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, sTunName, sizeof(ifr.ifr_name));
VerifyOrExit(ioctl(sIpFd, SIOCGIFFLAGS, &ifr) == 0, perror("ioctl"); error = OT_ERROR_FAILED);
if (otIp6IsEnabled(aInstance))
{
ifr.ifr_flags |= IFF_UP;
}
else
{
ifr.ifr_flags &= ~IFF_UP;
}
VerifyOrExit(ioctl(sIpFd, SIOCSIFFLAGS, &ifr) == 0, perror("ioctl"); error = OT_ERROR_FAILED);
exit:
if (error == OT_ERROR_NONE)
{
otLogInfoPlat("%s: %s", __func__, otThreadErrorToString(error));
}
else
{
otLogWarnPlat("%s: %s", __func__, otThreadErrorToString(error));
}
}
static void processAddressChange(const otIp6Address *aAddress, uint8_t aPrefixLength, bool aIsAdded, void *aContext)
{
if (aAddress->mFields.m8[0] == 0xff)
{
UpdateMulticast(static_cast<otInstance *>(aContext), *aAddress, aIsAdded);
}
else
{
UpdateUnicast(static_cast<otInstance *>(aContext), *aAddress, aPrefixLength, aIsAdded);
}
}
static void processStateChange(otChangedFlags aFlags, void *aContext)
{
if (OT_CHANGED_THREAD_NETIF_STATE | aFlags)
{
UpdateLink(static_cast<otInstance *>(aContext));
}
}
static void processReceive(otMessage *aMessage, void *aContext)
{
char packet[kMaxIp6Size];
otError error = OT_ERROR_NONE;
uint16_t length = otMessageGetLength(aMessage);
assert(sInstance == aContext);
VerifyOrExit(sTunFd > 0);
VerifyOrExit(otMessageRead(aMessage, 0, packet, sizeof(packet)) == length, error = OT_ERROR_NO_BUFS);
VerifyOrExit(write(sTunFd, packet, length) == length, perror("write"); error = OT_ERROR_FAILED);
exit:
otMessageFree(aMessage);
if (error == OT_ERROR_NONE)
{
otLogInfoPlat("%s: %s", __func__, otThreadErrorToString(error));
}
else
{
otLogWarnPlat("%s: %s", __func__, otThreadErrorToString(error));
}
}
static void processTransmit(otInstance *aInstance)
{
otMessage *message = NULL;
ssize_t rval;
char packet[kMaxIp6Size];
otError error = OT_ERROR_NONE;
assert(sInstance == aInstance);
rval = read(sTunFd, packet, sizeof(packet));
VerifyOrExit(rval > 0, error = OT_ERROR_FAILED);
message = otIp6NewMessage(aInstance, NULL);
VerifyOrExit(message != NULL, error = OT_ERROR_NO_BUFS);
SuccessOrExit(error = otMessageAppend(message, packet, static_cast<uint16_t>(rval)));
error = otIp6Send(aInstance, message);
message = NULL;
exit:
if (message != NULL)
{
otMessageFree(message);
}
if (error == OT_ERROR_NONE)
{
otLogInfoPlat("%s: %s", __func__, otThreadErrorToString(error));
}
else
{
otLogWarnPlat("%s: %s", __func__, otThreadErrorToString(error));
}
}
static void processNetifAddrEvent(otInstance *aInstance, struct nlmsghdr *aNetlinkMessage)
{
struct ifaddrmsg *ifaddr = reinterpret_cast<struct ifaddrmsg *>(NLMSG_DATA(aNetlinkMessage));
size_t rtaLength;
otError error = OT_ERROR_NONE;
VerifyOrExit(ifaddr->ifa_index == static_cast<unsigned int>(sTunIndex) && ifaddr->ifa_family == AF_INET6);
rtaLength = IFA_PAYLOAD(aNetlinkMessage);
for (struct rtattr *rta = reinterpret_cast<struct rtattr *>(IFA_RTA(ifaddr)); RTA_OK(rta, rtaLength);
rta = RTA_NEXT(rta, rtaLength))
{
switch (rta->rta_type)
{
case IFA_ADDRESS:
case IFA_LOCAL:
case IFA_BROADCAST:
case IFA_ANYCAST:
{
ot::Ip6::Address addr;
memcpy(&addr, RTA_DATA(rta), sizeof(addr));
if (aNetlinkMessage->nlmsg_type == RTM_NEWADDR)
{
if (!addr.IsMulticast())
{
otNetifAddress netAddr;
netAddr.mAddress = addr;
netAddr.mPrefixLength = ifaddr->ifa_prefixlen;
SuccessOrExit(error = otIp6AddUnicastAddress(aInstance, &netAddr));
}
else
{
SuccessOrExit(error = otIp6SubscribeMulticastAddress(aInstance, &addr));
}
}
else if (aNetlinkMessage->nlmsg_type == RTM_DELADDR)
{
if (!addr.IsMulticast())
{
otIp6RemoveUnicastAddress(aInstance, &addr);
}
else
{
otIp6UnsubscribeMulticastAddress(aInstance, &addr);
}
}
else
{
continue;
}
break;
}
default:
break;
}
}
exit:
if (error == OT_ERROR_NONE)
{
otLogInfoPlat("%s: %s", __func__, otThreadErrorToString(error));
}
else
{
otLogWarnPlat("%s: %s", __func__, otThreadErrorToString(error));
}
}
static void processNetifLinkEvent(otInstance *aInstance, struct nlmsghdr *aNetlinkMessage)
{
struct ifinfomsg *ifinfo = reinterpret_cast<struct ifinfomsg *>(NLMSG_DATA(aNetlinkMessage));
otError error = OT_ERROR_NONE;
VerifyOrExit(ifinfo->ifi_index == static_cast<int>(sTunIndex));
SuccessOrExit(error = otIp6SetEnabled(aInstance, ifinfo->ifi_flags & IFF_UP));
exit:
if (error == OT_ERROR_NONE)
{
otLogInfoPlat("%s: %s", __func__, otThreadErrorToString(error));
}
else
{
otLogWarnPlat("%s: %s", __func__, otThreadErrorToString(error));
}
}
static void processNetifEvent(otInstance *aInstance)
{
const size_t kMaxNetifEvent = 8192;
ssize_t length;
char buffer[kMaxNetifEvent];
length = recv(sNetlinkFd, buffer, sizeof(buffer), 0);
VerifyOrExit(length > 0);
for (struct nlmsghdr *msg = reinterpret_cast<struct nlmsghdr *>(buffer); NLMSG_OK(msg, length);
msg = NLMSG_NEXT(msg, length))
{
switch (msg->nlmsg_type)
{
case RTM_NEWADDR:
case RTM_DELADDR:
processNetifAddrEvent(aInstance, msg);
break;
case RTM_NEWLINK:
case RTM_DELLINK:
processNetifLinkEvent(aInstance, msg);
break;
default:
break;
}
}
exit:
return;
}
void platformNetifInit(otInstance *aInstance)
{
struct ifreq ifr;
sIpFd = SocketWithCloseExec(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
VerifyOrExit(sIpFd >= 0);
sNetlinkFd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
VerifyOrExit(sNetlinkFd > 0);
otIcmp6SetEchoMode(aInstance, OT_ICMP6_ECHO_HANDLER_DISABLED);
{
struct sockaddr_nl sa;
memset(&sa, 0, sizeof(sa));
sa.nl_family = AF_NETLINK;
sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV6_IFADDR;
VerifyOrExit(bind(sNetlinkFd, reinterpret_cast<struct sockaddr *>(&sa), sizeof(sa)) == 0);
}
sTunFd = open(OPENTHREAD_POSIX_TUN_DEVICE, O_RDWR | O_CLOEXEC);
VerifyOrExit(sTunFd > 0, otLogCritPlat("Unable to open tun device %s", OPENTHREAD_POSIX_TUN_DEVICE));
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
strncpy(ifr.ifr_name, "wpan%d", IFNAMSIZ);
VerifyOrExit(ioctl(sTunFd, TUNSETIFF, static_cast<void *>(&ifr)) == 0,
otLogCritPlat("Unable to configure tun device %s", OPENTHREAD_POSIX_TUN_DEVICE));
VerifyOrExit(ioctl(sTunFd, TUNSETLINK, ARPHRD_VOID) == 0,
otLogCritPlat("Unable to set link type of tun device %s", OPENTHREAD_POSIX_TUN_DEVICE));
sTunIndex = if_nametoindex(ifr.ifr_name);
VerifyOrExit(sTunIndex > 0);
strncpy(sTunName, ifr.ifr_name, sizeof(sTunName));
#if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE
platformUdpInit(sTunName);
#endif
otIp6SetReceiveCallback(aInstance, processReceive, aInstance);
otIp6SetAddressCallback(aInstance, processAddressChange, aInstance);
otSetStateChangedCallback(aInstance, processStateChange, aInstance);
sInstance = aInstance;
exit:
if (sTunIndex == 0)
{
if (sTunFd != -1)
{
close(sTunFd);
sTunFd = -1;
}
if (sIpFd != -1)
{
close(sIpFd);
sIpFd = -1;
}
if (sNetlinkFd != -1)
{
close(sNetlinkFd);
sNetlinkFd = -1;
}
DieNow(OT_EXIT_FAILURE);
}
}
void platformNetifUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, fd_set *aErrorFdSet, int *aMaxFd)
{
OT_UNUSED_VARIABLE(aWriteFdSet);
VerifyOrExit(sTunIndex > 0);
assert(sTunFd > 0);
assert(sNetlinkFd > 0);
assert(sIpFd > 0);
FD_SET(sTunFd, aReadFdSet);
FD_SET(sTunFd, aErrorFdSet);
FD_SET(sNetlinkFd, aReadFdSet);
FD_SET(sNetlinkFd, aErrorFdSet);
if (sTunFd > *aMaxFd)
{
*aMaxFd = sTunFd;
}
if (sNetlinkFd > *aMaxFd)
{
*aMaxFd = sNetlinkFd;
}
exit:
return;
}
void platformNetifProcess(const fd_set *aReadFdSet, const fd_set *aWriteFdSet, const fd_set *aErrorFdSet)
{
OT_UNUSED_VARIABLE(aWriteFdSet);
VerifyOrExit(sTunIndex > 0);
if (FD_ISSET(sTunFd, aErrorFdSet))
{
close(sTunFd);
DieNow(OT_EXIT_FAILURE);
}
if (FD_ISSET(sNetlinkFd, aErrorFdSet))
{
close(sNetlinkFd);
DieNow(OT_EXIT_FAILURE);
}
if (FD_ISSET(sTunFd, aReadFdSet))
{
processTransmit(sInstance);
}
if (FD_ISSET(sNetlinkFd, aReadFdSet))
{
processNetifEvent(sInstance);
}
exit:
return;
}
#endif // OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE