1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

OpenThreadのIPv6プロトコルスタックを読む

Posted at

OpenThreadのIPv6実装のを読んでみました。

OpenThreadは Googleが開発しているIOT向けのネットワークプロトコル Threadのオープンソース実装です。
OpenThreadには、IPv6の実装が含まれているので調べてみました。

まとめ

  • OpenThreadのIPv6実装の送信処理を読んでみた。
  • UDPの送信からCC2538のUART送信までのデータの流れを辿った。
  • UdpSocketクラス→Ip6クラス→ThreadNetifクラス→NcpBaseクラス→ NcpUartクラス→cc2538 UARTドライバ というデータの流れ

送信の流れ

  • UdpSocket::SendTo()
ThreadError UdpSocket::SendTo(Message &aMessage, const MessageInfo &aMessageInfo)
{
    ThreadError error = kThreadError_None;
    MessageInfo messageInfoLocal;
    UdpHeader udpHeader;

    messageInfoLocal = aMessageInfo;

    if (messageInfoLocal.GetSockAddr().IsUnspecified())
    {
        messageInfoLocal.GetSockAddr() = GetSockName().GetAddress();
    }

    if (GetSockName().mPort == 0)
    {
        GetSockName().mPort = Udp::sEphemeralPort;

        if (Udp::sEphemeralPort < Udp::kDynamicPortMax)
        {
            Udp::sEphemeralPort++;
        }
        else
        {
            Udp::sEphemeralPort = Udp::kDynamicPortMin;
        }
    }

    udpHeader.SetSourcePort(GetSockName().mPort);
    udpHeader.SetDestinationPort(messageInfoLocal.mPeerPort);
    udpHeader.SetLength(sizeof(udpHeader) + aMessage.GetLength());
    udpHeader.SetChecksum(0);

    SuccessOrExit(error = aMessage.Prepend(&udpHeader, sizeof(udpHeader)));
    aMessage.SetOffset(0);
    SuccessOrExit(error = Ip6::SendDatagram(aMessage, messageInfoLocal, kProtoUdp));

exit:
    return error;
}
  • Ip6::SendDatagram()
ThreadError Ip6::SendDatagram(Message &message, MessageInfo &messageInfo, IpProto ipproto)
{
    ThreadError error = kThreadError_None;
    Header header;
    uint16_t payloadLength = message.GetLength();
    uint16_t checksum;
    const NetifUnicastAddress *source;

    header.Init();
    header.SetPayloadLength(payloadLength);
    header.SetNextHeader(ipproto);
    header.SetHopLimit(messageInfo.mHopLimit ? messageInfo.mHopLimit : kDefaultHopLimit);

    if (messageInfo.GetSockAddr().IsUnspecified())
    {
        VerifyOrExit((source = Netif::SelectSourceAddress(messageInfo)) != NULL, error = kThreadError_Error);
        header.SetSource(source->GetAddress());
    }
    else
    {
        header.SetSource(messageInfo.GetSockAddr());
    }

    header.SetDestination(messageInfo.GetPeerAddr());

    if (header.GetDestination().IsLinkLocal() || header.GetDestination().IsLinkLocalMulticast())
    {
        VerifyOrExit(messageInfo.mInterfaceId != 0, error = kThreadError_Drop);
    }

    if (messageInfo.GetPeerAddr().IsRealmLocalMulticast())
    {
        SuccessOrExit(error = AddMplOption(message, header, ipproto, payloadLength));
    }

    SuccessOrExit(error = message.Prepend(&header, sizeof(header)));

    // compute checksum
    checksum = ComputePseudoheaderChecksum(header.GetSource(), header.GetDestination(),
                                           payloadLength, ipproto);

    switch (ipproto)
    {
    case kProtoUdp:
        SuccessOrExit(error = Udp::UpdateChecksum(message, checksum));
        break;

    case kProtoIcmp6:
        SuccessOrExit(error = Icmp::UpdateChecksum(message, checksum));
        break;

    default:
        break;
    }

exit:

    if (error == kThreadError_None)
    {
        error = HandleDatagram(message, NULL, messageInfo.mInterfaceId, NULL, false);
    }

    return error;
}
  • Ip6::HandleDatagram()
ThreadError Ip6::HandleDatagram(Message &message, Netif *netif, uint8_t interfaceId, const void *linkMessageInfo,
                                bool fromLocalHost)
{
    ThreadError error = kThreadError_Drop;
    MessageInfo messageInfo;
    Header header;
    uint16_t payloadLength;
    bool receive = false;
    bool forward = false;
    uint8_t nextHeader;
    uint8_t hopLimit;

# if 0
    uint8_t buf[1024];
    message.Read(0, sizeof(buf), buf);
    dump("handle datagram", buf, message.GetLength());
# endif

    // check message length
    VerifyOrExit(message.GetLength() >= sizeof(header), ;);
    message.Read(0, sizeof(header), &header);
    payloadLength = header.GetPayloadLength();

    // check Version
    VerifyOrExit(header.IsVersion6(), ;);

    // check Payload Length
    VerifyOrExit(sizeof(header) + payloadLength == message.GetLength() &&
                 sizeof(header) + payloadLength <= Ip6::kMaxDatagramLength, ;);

    memset(&messageInfo, 0, sizeof(messageInfo));
    messageInfo.GetPeerAddr() = header.GetSource();
    messageInfo.GetSockAddr() = header.GetDestination();
    messageInfo.mInterfaceId = interfaceId;
    messageInfo.mHopLimit = header.GetHopLimit();
    messageInfo.mLinkInfo = linkMessageInfo;

    // determine destination of packet
    if (header.GetDestination().IsMulticast())
    {
        if (netif != NULL && netif->IsMulticastSubscribed(header.GetDestination()))
        {
            receive = true;
        }

        if (header.GetDestination().GetScope() > Address::kLinkLocalScope)
        {
            forward = true;
        }
        else if (netif == NULL)
        {
            forward = true;
        }
    }
    else
    {
        if (Netif::IsUnicastAddress(header.GetDestination()))
        {
            receive = true;
        }
        else if (!header.GetDestination().IsLinkLocal())
        {
            forward = true;
        }
        else if (netif == NULL)
        {
            forward = true;
        }
    }

    message.SetOffset(sizeof(header));

    // process IPv6 Extension Headers
    nextHeader = header.GetNextHeader();
    SuccessOrExit(HandleExtensionHeaders(message, nextHeader, receive));

    // process IPv6 Payload
    if (receive)
    {
        if (fromLocalHost == false)
        {
            ProcessReceiveCallback(message);
        }

        SuccessOrExit(HandlePayload(message, messageInfo, nextHeader));
    }

    if (forward)
    {
        if (netif != NULL)
        {
            header.SetHopLimit(header.GetHopLimit() - 1);
        }

        if (header.GetHopLimit() == 0)
        {
            // send time exceeded
        }
        else
        {
            hopLimit = header.GetHopLimit();
            message.Write(Header::GetHopLimitOffset(), Header::GetHopLimitSize(), &hopLimit);
            SuccessOrExit(ForwardMessage(message, messageInfo));
            ExitNow(error = kThreadError_None);
        }
    }

exit:

    if (error == kThreadError_Drop)
    {
        Message::Free(message);
    }

    return kThreadError_None;
}
  • ForwardMessage()
ThreadError ForwardMessage(Message &message, MessageInfo &messageInfo)
{
    ThreadError error = kThreadError_None;
    int interfaceId;
    Netif *netif;

    if (messageInfo.GetSockAddr().IsMulticast())
    {
        // multicast
        interfaceId = messageInfo.mInterfaceId;
    }
    else if (messageInfo.GetSockAddr().IsLinkLocal())
    {
        // on-link link-local address
        interfaceId = messageInfo.mInterfaceId;
    }
    else if ((interfaceId = Netif::GetOnLinkNetif(messageInfo.GetSockAddr())) > 0)
    {
        // on-link global address
        ;
    }
    else if ((interfaceId = Routes::Lookup(messageInfo.GetPeerAddr(), messageInfo.GetSockAddr())) > 0)
    {
        // route
        ;
    }
    else
    {
        otDumpDebgIp6("no route", &messageInfo.GetSockAddr(), 16);
        ExitNow(error = kThreadError_NoRoute);
    }

    // submit message to interface
    VerifyOrExit((netif = Netif::GetNetifById(interfaceId)) != NULL, error = kThreadError_NoRoute);
    SuccessOrExit(error = netif->SendMessage(message));

exit:
    return error;
}
  • netif->SendMessage(), ThreadNetif::SendMessage
ThreadError ThreadNetif::SendMessage(Message &message)
{
    return mMeshForwarder.SendMessage(message);
}
  • MeshForwarder::SendMessage
ThreadError MeshForwarder::SendMessage(Message &aMessage)
{
    ThreadError error = kThreadError_None;
    Neighbor *neighbor;
    Ip6::Header ip6Header;
    uint8_t numChildren;
    Child *children;
    Lowpan::MeshHeader meshHeader;

    switch (aMessage.GetType())
    {
    case Message::kTypeIp6:
        aMessage.Read(0, sizeof(ip6Header), &ip6Header);

        if (!memcmp(&ip6Header.GetDestination(), mMle.GetLinkLocalAllThreadNodesAddress(),
                    sizeof(ip6Header.GetDestination())) ||
            !memcmp(&ip6Header.GetDestination(), mMle.GetRealmLocalAllThreadNodesAddress(), sizeof(ip6Header.GetDestination())))
        {
            // schedule direct transmission
            aMessage.SetDirectTransmission();

            // destined for all sleepy children
            children = mMle.GetChildren(&numChildren);

            for (int i = 0; i < numChildren; i++)
            {
                if (children[i].mState == Neighbor::kStateValid && (children[i].mMode & Mle::ModeTlv::kModeRxOnWhenIdle) == 0)
                {
                    aMessage.SetChildMask(i);
                }
            }
        }
        else if ((neighbor = mMle.GetNeighbor(ip6Header.GetDestination())) != NULL &&
                 (neighbor->mMode & Mle::ModeTlv::kModeRxOnWhenIdle) == 0)
        {
            // destined for a sleepy child
            aMessage.SetChildMask(mMle.GetChildIndex(*reinterpret_cast<Child *>(neighbor)));
        }
        else
        {
            // schedule direct transmission
            aMessage.SetDirectTransmission();
        }

        break;

    case Message::kType6lowpan:
        aMessage.Read(0, meshHeader.GetHeaderLength(), &meshHeader);

        if ((neighbor = mMle.GetNeighbor(meshHeader.GetDestination())) != NULL &&
            (neighbor->mMode & Mle::ModeTlv::kModeRxOnWhenIdle) == 0)
        {
            // destined for a sleepy child
            aMessage.SetChildMask(mMle.GetChildIndex(*reinterpret_cast<Child *>(neighbor)));
        }
        else
        {
            // not destined for a sleepy child
            aMessage.SetDirectTransmission();
        }

        break;

    case Message::kTypeMacDataPoll:
        aMessage.SetDirectTransmission();
        break;
    }

    aMessage.SetOffset(0);
    SuccessOrExit(error = mSendQueue.Enqueue(aMessage));
    mScheduleTransmissionTask.Post();

exit:
    return error;
}
  • NcpBase::HandleDatagramFromStack()
void NcpBase::HandleDatagramFromStack(Message &message)
{
    ThreadError errorCode;

    if (mSending == false)
    {
        errorCode = OutboundFrameBegin();

        if (errorCode == kThreadError_None)
        {
            errorCode = OutboundFrameFeedPacked(
                            "CiiS",
                            SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0,
                            SPINEL_CMD_PROP_VALUE_IS,
                            message.IsLinkSecurityEnabled()
                            ? SPINEL_PROP_STREAM_NET
                            : SPINEL_PROP_STREAM_NET_INSECURE,
                            message.GetLength()
                        );
        }

        if (errorCode == kThreadError_None)
        {
            errorCode = OutboundFrameFeedMessage(message);
        }

        // TODO: Append any metadata (rssi, lqi, channel, etc) here!

        if (errorCode == kThreadError_None)
        {
            errorCode = OutboundFrameSend();
        }

        if (errorCode != kThreadError_None)
        {
            SendLastStatus(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_STATUS_DROPPED);
        }

        Message::Free(message);
    }
    else
    {
        if (mSendQueue.Enqueue(message) != kThreadError_None)
        {
            Message::Free(message);
        }
    }
}
  • NcpUart::OutboundFrameSend (NcpSpiクラスにも同様のメソッドがある)
ThreadError
NcpUart::OutboundFrameSend(void)
{
    ThreadError errorCode;

    errorCode = mFrameEncoder.Finalize(mSendFrame);

    if (errorCode == kThreadError_None)
    {
        errorCode = otPlatUartSend(mSendFrame.GetBuffer(), mSendFrame.GetLength());
    }

    if (errorCode == kThreadError_None)
    {
        mSending = true;
    }

    return errorCode;
}
  • otPlatUartSend
ThreadError otPlatUartSend(const uint8_t *aBuf, uint16_t aBufLength)
{
    ThreadError error = kThreadError_None;

    VerifyOrExit(sTransmitBuffer == NULL, error = kThreadError_Busy);

    sTransmitBuffer = aBuf;
    sTransmitLength = aBufLength;

exit:
    return error;
}
  • ここから別のプロセスでの処理

  • cc2538UartProcess

void cc2538UartProcess(void)
{
    processReceive();
    processTransmit();
}
  • processTransmit
void processTransmit(void)
{
    VerifyOrExit(sTransmitBuffer != NULL, ;);

    for (; sTransmitLength > 0; sTransmitLength--)
    {
        while (HWREG(UART0_BASE + UART_O_FR) & UART_FR_TXFF);

        HWREG(UART0_BASE + UART_O_DR) = *sTransmitBuffer++;
    }

    sTransmitBuffer = NULL;
    otPlatUartSendDone();

exit:
    return;
}
  • 最後はUARTに送信してる。 想定では無線で送信するのかと思ったのだけど、どうしてなんだろう?
  • CC2538で動かしてみた人はいないんでしょうか?

受信 (途中)

  • SetNcpReceivedHandler

  • HandleDatagramFromStack

  • Ncp::OutboundFrameFeedMessage(Message &message)

  • Mac::ReceiveDoneTask(void *aContext)

  • Mac::ReceiveDoneTask(void)

  • MeshForwarder::HandleReceivedFrame(void *aContext, Mac::Frame &aFrame, ThreadError aError)

  • MeshForwarder::HandleReceivedFrame(Mac::Frame &aFrame, ThreadError aError)

  • MeshForwarder::HandleLowpanHC() or MeshForwarder::HandleFragment()

  • Ip6::HandleDatagram()

  • HandlePayload

参考

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?