blob: c0b2f0aeea2bc2c61853d2ca15998a76d4c67f48 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <gtest/gtest.h>
#include <wlan/mlme/mesh/mesh_mlme.h>
#include <fuchsia/wlan/mlme/c/fidl.h>
#include "mock_device.h"
#include "test_utils.h"
namespace wlan_mlme = ::fuchsia::wlan::mlme;
namespace wlan {
struct MeshMlmeTest : public ::testing::Test {
MeshMlmeTest() : mlme(&device) {
device.state->set_address(common::MacAddr("11:11:11:11:11:11"));
mlme.Init();
}
wlan_mlme::StartResultCodes JoinMesh() {
wlan_mlme::StartRequest join;
zx_status_t status =
mlme.HandleMlmeMsg(MlmeMsg<wlan_mlme::StartRequest>(std::move(join), 123));
EXPECT_EQ(ZX_OK, status);
auto msgs = device.GetServiceMsgs<wlan_mlme::StartConfirm>();
EXPECT_EQ(msgs.size(), 1ULL);
return msgs[0].body()->result_code;
}
wlan_mlme::StopResultCodes LeaveMesh() {
wlan_mlme::StopRequest leave;
zx_status_t status =
mlme.HandleMlmeMsg(MlmeMsg<wlan_mlme::StopRequest>(std::move(leave), 123));
EXPECT_EQ(ZX_OK, status);
auto msgs = device.GetServiceMsgs<wlan_mlme::StopConfirm>();
EXPECT_EQ(msgs.size(), 1ULL);
return msgs[0].body()->result_code;
}
MockDevice device;
MeshMlme mlme;
};
static fbl::unique_ptr<Packet> MakeWlanPacket(Span<const uint8_t> bytes) {
auto packet = GetWlanPacket(bytes.size());
memcpy(packet->data(), bytes.data(), bytes.size());
return packet;
}
TEST_F(MeshMlmeTest, JoinLeave) {
EXPECT_EQ(LeaveMesh(), wlan_mlme::StopResultCodes::BSS_ALREADY_STOPPED);
EXPECT_EQ(JoinMesh(), wlan_mlme::StartResultCodes::SUCCESS);
EXPECT_TRUE(device.beaconing_enabled);
EXPECT_EQ(JoinMesh(), wlan_mlme::StartResultCodes::BSS_ALREADY_STARTED_OR_JOINED);
EXPECT_EQ(LeaveMesh(), wlan_mlme::StopResultCodes::SUCCESS);
EXPECT_FALSE(device.beaconing_enabled);
EXPECT_EQ(LeaveMesh(), wlan_mlme::StopResultCodes::BSS_ALREADY_STOPPED);
EXPECT_EQ(JoinMesh(), wlan_mlme::StartResultCodes::SUCCESS);
EXPECT_TRUE(device.beaconing_enabled);
}
TEST_F(MeshMlmeTest, HandleMpmOpen) {
EXPECT_EQ(JoinMesh(), wlan_mlme::StartResultCodes::SUCCESS);
// clang-format off
const uint8_t frame[] = {
// Mgmt header
0xd0, 0x00, 0x00, 0x00, // fc, duration
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // addr1
0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, // addr2
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, // addr3
0x00, 0x00, // seq ctl
// Action
15, // category (self-protected)
1, // action = Mesh Peering Open
// Body
0xaa, 0xbb, // capability info
1, 1, 0x81, // supported rates
114, 3, 'f', 'o', 'o', // mesh id
113, 7, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, // mesh config
117, 4, 0xb1, 0xb2, 0xb3, 0xb4, // MPM
};
// clang-format on
ASSERT_EQ(mlme.HandleFramePacket(MakeWlanPacket(frame)), ZX_OK);
auto msgs = device.GetServiceMsgs<wlan_mlme::MeshPeeringOpenAction>();
ASSERT_EQ(msgs.size(), 1ULL);
{
const uint8_t expected[] = {'f', 'o', 'o'};
EXPECT_RANGES_EQ(msgs[0].body()->common.mesh_id, expected);
}
{
const uint8_t expected[] = {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff};
EXPECT_RANGES_EQ(msgs[0].body()->common.peer_sta_address, expected);
}
}
TEST_F(MeshMlmeTest, DeliverProxiedData) {
EXPECT_EQ(JoinMesh(), wlan_mlme::StartResultCodes::SUCCESS);
// Simulate receiving a data frame
zx_status_t status = mlme.HandleFramePacket(test_utils::MakeWlanPacket({
// clang-format off
// Data header
0x88, 0x03, // fc: qos data, 4-address, no ht ctl
0x00, 0x00, // duration
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, // addr1
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, // addr2
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, // addr3: mesh da = ra
0x00, 0x00, // seq ctl
0x44, 0x44, 0x44, 0x44, 0x44, 0x44, // addr4
0x00, 0x01, // qos ctl: mesh control present
// Mesh control
0x02, // flags: addr56 extension
0x20, // ttl
0xaa, 0xbb, 0xcc, 0xdd, // seq
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, // addr5
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, // addr6
// LLC header
0xaa, 0xaa, 0x03, // dsap ssap ctrl
0x00, 0x00, 0x00, // oui
0x12, 0x34, // protocol id
// Payload
0xde, 0xad, 0xbe, 0xef,
// clang-format on
}));
EXPECT_EQ(ZX_OK, status);
auto eth_frames = device.GetEthPackets();
ASSERT_EQ(1u, eth_frames.size());
// clang-format off
const uint8_t expected[] = {
// Destination = addr5
0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
// Source = addr6
0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
// Ethertype = protocol id from the LLC header
0x12, 0x34,
// Payload
0xde, 0xad, 0xbe, 0xef,
};
// clang-format on
EXPECT_RANGES_EQ(expected, eth_frames[0]);
}
TEST_F(MeshMlmeTest, DoNotDeliverWhenNotJoined) {
auto packet = [] (uint8_t mesh_seq) {
return test_utils::MakeWlanPacket({
// clang-format off
// Data header
0x88, 0x03, // fc: qos data, 4-address, no ht ctl
0x00, 0x00, // duration
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, // addr1
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, // addr2
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, // addr3: mesh da = ra
0x00, 0x00, // seq ctl
0x44, 0x44, 0x44, 0x44, 0x44, 0x44, // addr4
0x00, 0x01, // qos ctl: mesh control present
// Mesh control
0x00, 0x20, // flags, ttl
mesh_seq, 0xbb, 0xcc, 0xdd, // seq
// LLC header
0xaa, 0xaa, 0x03, // dsap ssap ctrl
0x00, 0x00, 0x00, // oui
0x12, 0x34, // protocol id
// Payload
0xde, 0xad, 0xbe, 0xef,
// clang-format on
});
};
// Receive a frame while not joined: expect it to be dropped
EXPECT_EQ(mlme.HandleFramePacket(packet(1)), ZX_OK);
EXPECT_TRUE(device.GetEthPackets().empty());
EXPECT_EQ(JoinMesh(), wlan_mlme::StartResultCodes::SUCCESS);
// Receive a frame while joined: expect it to be delivered
EXPECT_EQ(mlme.HandleFramePacket(packet(2)), ZX_OK);
EXPECT_EQ(device.GetEthPackets().size(), 1u);
EXPECT_EQ(LeaveMesh(), wlan_mlme::StopResultCodes::SUCCESS);
// Again, receive a frame while not joined: expect it to be dropped
EXPECT_EQ(mlme.HandleFramePacket(packet(3)), ZX_OK);
EXPECT_TRUE(device.GetEthPackets().empty());
}
TEST_F(MeshMlmeTest, HandlePreq) {
EXPECT_EQ(JoinMesh(), wlan_mlme::StartResultCodes::SUCCESS);
zx_status_t status = mlme.HandleFramePacket(test_utils::MakeWlanPacket({
// clang-format off
// Mgmt header
0xd0, 0x00, 0x00, 0x00, // fc, duration
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, // addr1
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, // addr2
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, // addr3
0x10, 0x00, // seq ctl
// Action
13, // category (mesh)
1, // action = HWMP mesh path selection
130, 37,
0x00, // flags: no address extension
0x03, // hop count
0x20, // element ttl
0x04, 0x05, 0x06, 0x07, // path discovery ID
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, // originator addr
0x07, 0x00, 0x00, 0x00, // originator hwmp seqno
0x05, 0x00, 0x00, 0x00, // lifetime: 5 TU = 5120 mircoseconds
200, 0, 0, 0, // metric
1, // target count
// Target 1
0x00, // target flags
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, // target address
0x09, 0x00, 0x00, 0x00, // target hwmp seqno
// clang-format on
}));
EXPECT_EQ(ZX_OK, status);
auto outgoing_packets = device.GetWlanPackets();
ASSERT_EQ(1u, outgoing_packets.size());
auto& packet = *outgoing_packets[0].pkt;
// Simply check that the PREP element is there. hwmp_unittest.cpp tests the actual
// contents more thoroughly.
ASSERT_GE(packet.len(), 27u);
EXPECT_EQ(packet.data()[24], 13); // mesh action
EXPECT_EQ(packet.data()[25], 1); // hwmp
EXPECT_EQ(packet.data()[26], 131); // prep element
}
TEST_F(MeshMlmeTest, DeliverDuplicateData) {
EXPECT_EQ(JoinMesh(), wlan_mlme::StartResultCodes::SUCCESS);
auto mesh_packet = [](uint8_t addr, uint8_t seq, uint8_t data) {
// clang-format off
return std::vector<uint8_t> {
// Data header
0x88, 0x03, // fc: qos data, 4-address, no ht ctl
0x00, 0x00, // duration
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, // addr1
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, // addr2
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, // addr3: mesh da = ra
0x00, 0x00, // seq ctl
0x44, 0x44, 0x44, 0x44, addr, addr, // addr4
0x00, 0x01, // qos ctl: mesh control present
// Mesh control
0x02, // flags: addr56 extension
0x20, // ttl
seq, seq, seq, seq, // seq
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, // addr5
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, // addr6
// LLC header
0xaa, 0xaa, 0x03, // dsap ssap ctrl
0x00, 0x00, 0x00, // oui
0x12, 0x34, // protocol id
// Payload
0xde, 0xad, 0xbe, data,
};
// clang-format on
};
// clang-format off
const uint8_t expected[] = {
// Destination = addr5
0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
// Source = addr6
0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
// Ethertype = protocol id from the LLC header
0x12, 0x34,
// Payload
0xde, 0xad, 0xbe, 0xef,
};
// clang-format on
// clang-format off
const uint8_t expected2[] = {
// Destination = addr5
0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
// Source = addr6
0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
// Ethertype = protocol id from the LLC header
0x12, 0x34,
// Payload
0xde, 0xad, 0xbe, 0xff,
};
// clang-format on
// send some non-duplicate packets
for (uint8_t addr = 1; addr < 5; addr++) {
for (uint8_t seq = 1; seq < 5; seq++) {
zx_status_t status =
mlme.HandleFramePacket(test_utils::MakeWlanPacket(mesh_packet(addr, seq, 0xef)));
EXPECT_EQ(ZX_OK, status);
auto eth_frames = device.GetEthPackets();
ASSERT_EQ(1u, eth_frames.size());
EXPECT_RANGES_EQ(expected, eth_frames[0]);
}
}
// send some duplicate packets
for (uint8_t addr = 1; addr < 5; addr++) {
for (uint8_t seq = 1; seq < 5; seq++) {
zx_status_t status =
mlme.HandleFramePacket(test_utils::MakeWlanPacket(mesh_packet(addr, seq, 0xef)));
EXPECT_EQ(ZX_OK, status);
auto eth_frames = device.GetEthPackets();
ASSERT_EQ(0u, eth_frames.size()); // expect 0 packets
}
}
// send some more non-duplicate packets with a different payload
for (uint8_t addr = 5; addr < 10; addr++) {
for (uint8_t seq = 0; seq < 5; seq++) {
zx_status_t status =
mlme.HandleFramePacket(test_utils::MakeWlanPacket(mesh_packet(addr, seq, 0xff)));
EXPECT_EQ(ZX_OK, status);
auto eth_frames = device.GetEthPackets();
ASSERT_EQ(1u, eth_frames.size());
EXPECT_RANGES_EQ(expected2, eth_frames[0]);
}
}
}
TEST_F(MeshMlmeTest, DataForwarding) {
EXPECT_EQ(JoinMesh(), wlan_mlme::StartResultCodes::SUCCESS);
// Receive a PREP to establish a path to 33:33:33:33:33:33 via 22:22:22:22:22:22
zx_status_t status = mlme.HandleFramePacket(test_utils::MakeWlanPacket({
// clang-format off
// Mgmt header
0xd0, 0x00, 0x00, 0x00, // fc, duration
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, // addr1
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, // addr2
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, // addr3
0x10, 0x00, // seq ctl
// Action
13, // category (mesh)
1, // action = HWMP mesh path selection
131, 31, // PREP
0x00, 0x01, 0x20, // flags, hop count, elem ttl
0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // target addr
0x07, 0x00, 0x00, 0x00, // target hwmp seqno
0x00, 0x01, 0x00, 0x00, // lifetime
150, 0x0, 0x0, 0x0, // metric
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, // originator addr = self
0x02, 0x00, 0x00, 0x00, // originator hwmp seqno
// clang-format on
}));
EXPECT_EQ(ZX_OK, status);
// Receive a data frame originating from 55:55:55:55:55:55 and targeted at 33:33:33:33:33:33,
// sent to us (11:11:11:11:11:11) by the previous hop (44:44:44:44:44:44)
status = mlme.HandleFramePacket(test_utils::MakeWlanPacket({
// clang-format off
// Data header
0x88, 0x03, // fc: qos data, 4-address, no ht ctl
0x00, 0x00, // duration
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, // addr1
0x44, 0x44, 0x44, 0x44, 0x44, 0x44, // addr2
0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // addr3: mesh da
0x00, 0x00, // seq ctl
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, // addr4: mesh sa
0x00, 0x01, // qos ctl: mesh control present
// Mesh control
0x00, 0x20, // flags, ttl
0xaa, 0xbb, 0xcc, 0xdd, // seq
// LLC header
0xaa, 0xaa, 0x03, // dsap ssap ctrl
0x00, 0x00, 0x00, // oui
0x12, 0x34, // protocol id
// Payload
0xde, 0xad, 0xbe, 0xef,
// clang-format on
}));
EXPECT_EQ(ZX_OK, status);
auto packets = device.GetWlanPackets();
ASSERT_EQ(1u, packets.size());
const uint8_t expected[] = {
// clang-format off
// Data header
0x88, 0x03, // fc: qos data, 4-address, no ht ctl
0x00, 0x00, // duration
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, // addr1: next hop to destination
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, // addr2
0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // addr3: mesh da
0x10, 0x00, // seq ctl: should be filled by us
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, // addr4: mesh sa
0x00, 0x01, // qos ctl: mesh control present
// Mesh control
0x00, 0x1f, // flags, ttl (decreased by one)
0xaa, 0xbb, 0xcc, 0xdd, // seq
// LLC header
0xaa, 0xaa, 0x03, // dsap ssap ctrl
0x00, 0x00, 0x00, // oui
0x12, 0x34, // protocol id
// Payload
0xde, 0xad, 0xbe, 0xef,
// clang-format on
};
EXPECT_RANGES_EQ(expected, Span<const uint8_t>(*packets[0].pkt));
}
} // namespace wlan