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