Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[infra if] add infra if icmp6 nd receiving #2547

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 88 additions & 4 deletions src/ncp/posix/infra_if.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ otbrError InfraIf::Dependencies::SetInfraIf(unsigned int aInfr
return OTBR_ERROR_NONE;
}

otbrError InfraIf::Dependencies::HandleIcmp6Nd(uint32_t, const Ip6Address &, const uint8_t *, uint16_t)
{
return OTBR_ERROR_NONE;
}

InfraIf::InfraIf(Dependencies &aDependencies)
: mDeps(aDependencies)
, mInfraIfIndex(0)
Expand Down Expand Up @@ -118,31 +123,41 @@ void InfraIf::Deinit(void)

void InfraIf::Process(const MainloopContext &aContext)
{
OT_UNUSED_VARIABLE(aContext);
VerifyOrExit(mInfraIfIcmp6Socket != -1);
#ifdef __linux__
VerifyOrExit(mNetlinkSocket != -1);
#endif

if (FD_ISSET(mInfraIfIcmp6Socket, &aContext.mReadFdSet))
{
ReceiveIcmp6Message();
}
#ifdef __linux__
if (FD_ISSET(mNetlinkSocket, &aContext.mReadFdSet))
{
ReceiveNetlinkMessage();
}
#endif

exit:
#endif
return;
}

void InfraIf::UpdateFdSet(MainloopContext &aContext)
{
OT_UNUSED_VARIABLE(aContext);
VerifyOrExit(mInfraIfIcmp6Socket != -1);
#ifdef __linux__
VerifyOrExit(mNetlinkSocket != -1);
#endif

FD_SET(mInfraIfIcmp6Socket, &aContext.mReadFdSet);
aContext.mMaxFd = std::max(aContext.mMaxFd, mInfraIfIcmp6Socket);
#ifdef __linux__
FD_SET(mNetlinkSocket, &aContext.mReadFdSet);
aContext.mMaxFd = std::max(aContext.mMaxFd, mNetlinkSocket);
#endif

exit:
#endif
return;
}

Expand Down Expand Up @@ -376,6 +391,75 @@ bool InfraIf::HasLinkLocalAddress(const std::vector<Ip6Address> &aAddrs)
return hasLla;
}

void InfraIf::ReceiveIcmp6Message(void)
{
static constexpr size_t kIp6Mtu = 1280;

otbrError error = OTBR_ERROR_NONE;
uint8_t buffer[kIp6Mtu];
uint16_t bufferLength;

ssize_t rval;
struct msghdr msg;
struct iovec bufp;
char cmsgbuf[128];
struct cmsghdr *cmh;
uint32_t ifIndex = 0;
int hopLimit = -1;

struct sockaddr_in6 srcAddr;
struct in6_addr dstAddr;

memset(&srcAddr, 0, sizeof(srcAddr));
memset(&dstAddr, 0, sizeof(dstAddr));

bufp.iov_base = buffer;
bufp.iov_len = sizeof(buffer);
msg.msg_iov = &bufp;
msg.msg_iovlen = 1;
msg.msg_name = &srcAddr;
msg.msg_namelen = sizeof(srcAddr);
msg.msg_control = cmsgbuf;
msg.msg_controllen = sizeof(cmsgbuf);

rval = recvmsg(mInfraIfIcmp6Socket, &msg, 0);
if (rval < 0)
{
otbrLogWarning("Failed to receive ICMPv6 message: %s", strerror(errno));
ExitNow(error = OTBR_ERROR_DROPPED);
}

Irving-cl marked this conversation as resolved.
Show resolved Hide resolved
bufferLength = static_cast<uint16_t>(rval);

for (cmh = CMSG_FIRSTHDR(&msg); cmh; cmh = CMSG_NXTHDR(&msg, cmh))
{
if (cmh->cmsg_level == IPPROTO_IPV6 && cmh->cmsg_type == IPV6_PKTINFO &&
cmh->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo)))
{
const struct in6_pktinfo *pktinfo = reinterpret_cast<struct in6_pktinfo *>(CMSG_DATA(cmh));
ifIndex = pktinfo->ipi6_ifindex;
dstAddr = pktinfo->ipi6_addr;
}
else if (cmh->cmsg_level == IPPROTO_IPV6 && cmh->cmsg_type == IPV6_HOPLIMIT &&
cmh->cmsg_len == CMSG_LEN(sizeof(int)))
{
hopLimit = *(int *)CMSG_DATA(cmh);
}
}

VerifyOrExit(ifIndex == mInfraIfIndex, error = OTBR_ERROR_DROPPED);

// We currently accept only RA & RS messages for the Border Router and it requires that
// the hoplimit must be 255 and the source address must be a link-local address.
VerifyOrExit(hopLimit == 255 && IN6_IS_ADDR_LINKLOCAL(&srcAddr.sin6_addr), error = OTBR_ERROR_DROPPED);

mDeps.HandleIcmp6Nd(mInfraIfIndex, Ip6Address(reinterpret_cast<otIp6Address &>(srcAddr.sin6_addr)), buffer,
bufferLength);

exit:
otbrLogResult(error, "InfraIf: %s", __FUNCTION__);
}

#ifdef __linux__
void InfraIf::ReceiveNetlinkMessage(void)
{
Expand Down
5 changes: 5 additions & 0 deletions src/ncp/posix/infra_if.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ class InfraIf
virtual otbrError SetInfraIf(unsigned int aInfraIfIndex,
bool aIsRunning,
const std::vector<Ip6Address> &aIp6Addresses);
virtual otbrError HandleIcmp6Nd(uint32_t aInfraIfIndex,
const Ip6Address &aSrcAddress,
const uint8_t *aData,
uint16_t aDataLen);
};

InfraIf(Dependencies &aDependencies);
Expand All @@ -80,6 +84,7 @@ class InfraIf
short GetFlags(void) const;
std::vector<Ip6Address> GetAddresses(void);
static bool HasLinkLocalAddress(const std::vector<Ip6Address> &aAddrs);
void ReceiveIcmp6Message(void);
#ifdef __linux__
void ReceiveNetlinkMessage(void);
#endif
Expand Down
85 changes: 85 additions & 0 deletions tests/gtest/test_infra_if.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ class InfraIfDependencyTest : public otbr::InfraIf::Dependencies
: mInfraIfIndex(0)
, mIsRunning(false)
, mSetInfraIfInvoked(false)
, mIcmp6NdDataLen(0)
, mHandleIcmp6NdInvoked(false)
{
memset(mIcmp6NdData, 0, sizeof(mIcmp6NdData));
}

otbrError SetInfraIf(unsigned int aInfraIfIndex,
Expand All @@ -57,10 +60,29 @@ class InfraIfDependencyTest : public otbr::InfraIf::Dependencies
return OTBR_ERROR_NONE;
}

otbrError HandleIcmp6Nd(uint32_t aInfraIfIndex,
const otbr::Ip6Address &aSrcAddress,
const uint8_t *aData,
uint16_t aDataLen) override
{
mInfraIfIndex = aInfraIfIndex;
mIcmp6NdSrcAddress = aSrcAddress;
memcpy(mIcmp6NdData, aData, aDataLen);
mIcmp6NdDataLen = aDataLen;
mHandleIcmp6NdInvoked = true;

return OTBR_ERROR_NONE;
}

unsigned int mInfraIfIndex;
bool mIsRunning;
std::vector<otbr::Ip6Address> mIp6Addresses;
bool mSetInfraIfInvoked;

otbr::Ip6Address mIcmp6NdSrcAddress;
uint8_t mIcmp6NdData[1280];
uint16_t mIcmp6NdDataLen;
bool mHandleIcmp6NdInvoked;
};

TEST(InfraIf, DepsSetInfraIfInvokedCorrectly_AfterSpecifyingInfraIf)
Expand Down Expand Up @@ -170,4 +192,67 @@ TEST(InfraIf, DepsUpdateInfraIfStateInvokedCorrectly_AfterInfraIfStateChange)
netif.Deinit();
}

TEST(InfraIf, DepsHandleIcmp6NdInvokedCorrectly_AfterInfraIfReceivesIcmp6Nd)
{
const std::string fakeInfraIf = "wlx123";
otbr::MainloopContext context;

// Utilize the Netif module to create a network interface as the fake infrastructure interface.
otbr::Netif::Dependencies defaultNetifDep;
otbr::Netif netif(defaultNetifDep);
EXPECT_EQ(netif.Init(fakeInfraIf), OTBR_ERROR_NONE);

const otIp6Address kLinkLocalAddr = {
{0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa8, 0xa5, 0x42, 0xb7, 0x91, 0x80, 0xc3, 0xf8}};
const otIp6Address kPeerLinkLocalAddr = {
{0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0xe5, 0x5b, 0xff, 0xfe, 0xc6, 0x8a, 0xf3}};
std::vector<otbr::Ip6AddressInfo> addrs = {{kLinkLocalAddr, 64, 0, 1, 0}};
netif.UpdateIp6UnicastAddresses(addrs);

InfraIfDependencyTest testInfraIfDep;
otbr::InfraIf infraIf(testInfraIfDep);
infraIf.Init();
EXPECT_EQ(infraIf.SetInfraIf(fakeInfraIf.c_str()), OTBR_ERROR_NONE);
netif.SetNetifState(true);

// Let the fake infrastructure interface receive a fake Icmp6 Nd message
// - Source Address: fe80::dee5:5bff:fec6:8af3
const uint8_t kTestMsg[] = {
0x60, 0x06, 0xce, 0x11, 0x00, 0x48, 0x3a, 0xff, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xde, 0xe5, 0x5b, 0xff, 0xfe, 0xc6, 0x8a, 0xf3, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x86, 0x00, 0xac, 0xf5, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x07, 0x08, 0x00, 0x00, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00,
0xfd, 0x38, 0x5f, 0xf4, 0x61, 0x0b, 0x40, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x18, 0x02, 0x40, 0x00, 0x00, 0x00, 0x07, 0x08, 0xfd, 0x9f, 0x5c, 0xfa, 0x66, 0x3e, 0x00, 0x01,
};
const uint8_t kTestMsgBodyOffset = 40;
const uint16_t kTestMsgBodySize = sizeof(kTestMsg) - kTestMsgBodyOffset;
netif.Ip6Receive(kTestMsg, sizeof(kTestMsg));

while (!testInfraIfDep.mHandleIcmp6NdInvoked)
{
context.mMaxFd = -1;
context.mTimeout = {100, 0};
FD_ZERO(&context.mReadFdSet);
FD_ZERO(&context.mWriteFdSet);
FD_ZERO(&context.mErrorFdSet);

infraIf.UpdateFdSet(context);
int rval = select(context.mMaxFd + 1, &context.mReadFdSet, &context.mWriteFdSet, &context.mErrorFdSet,
&context.mTimeout);
if (rval < 0)
{
perror("select failed");
exit(EXIT_FAILURE);
}
infraIf.Process(context);
}
EXPECT_EQ(testInfraIfDep.mIcmp6NdSrcAddress, otbr::Ip6Address(kPeerLinkLocalAddr));
EXPECT_EQ(testInfraIfDep.mIcmp6NdDataLen, kTestMsgBodySize);
EXPECT_EQ(memcmp(testInfraIfDep.mIcmp6NdData, kTestMsg + kTestMsgBodyOffset, kTestMsgBodySize), 0);

infraIf.Deinit();
netif.Deinit();
}
#endif // __linux__
Loading