blob: 02099619c06fa3e021dc6279fe27eb9bc17b95d1 [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 <lib/timekeeper/test_clock.h>
#include <wlan/mlme/mesh/hwmp.h>
#include "test_timer.h"
#include "test_utils.h"
namespace wlan {
TEST(Hwmp, HwmpSeqnoLessThan) {
EXPECT_TRUE(HwmpSeqnoLessThan(2, 5));
EXPECT_FALSE(HwmpSeqnoLessThan(5, 2));
EXPECT_FALSE(HwmpSeqnoLessThan(5, 5));
// Edge case: numbers exactly 2^31 apart
EXPECT_FALSE(HwmpSeqnoLessThan(5, 5 + (1u << 31)));
EXPECT_FALSE(HwmpSeqnoLessThan(5 + (1u << 31), 5));
// One step away from the edge case
EXPECT_TRUE(HwmpSeqnoLessThan(6, 5 + (1u << 31)));
EXPECT_FALSE(HwmpSeqnoLessThan(5 + (1u << 31), 6));
// One step away from the edge case in the other direction
EXPECT_FALSE(HwmpSeqnoLessThan(4, 5 + (1u << 31)));
EXPECT_TRUE(HwmpSeqnoLessThan(5 + (1u << 31), 4));
}
struct HwmpTest : public ::testing::Test {
HwmpTest() : state(fbl::make_unique<TestTimer>(123, &clock)) { clock.Set(zx::time(1000)); }
MacHeaderWriter CreateMacHeaderWriter() { return MacHeaderWriter(self_addr(), &seq); }
static common::MacAddr self_addr() { return common::MacAddr("aa:aa:aa:aa:aa:aa"); }
timekeeper::TestClock clock;
HwmpState state;
PathTable table;
Sequence seq;
};
TEST_F(HwmpTest, HandlePreqAddressedToUs) {
// clang-format off
const uint8_t preq[] = {
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
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, // target address
0x09, 0x00, 0x00, 0x00, // target hwmp seqno
};
// clang-format on
auto outgoing_packets =
HandleHwmpAction(preq, common::MacAddr("11:11:11:11:11:11"), self_addr(), 100,
CreateMacHeaderWriter(), &state, &table);
// 1. Expect an outgoing PREP frame
{
ASSERT_EQ(outgoing_packets.size(), 1u);
auto packet = outgoing_packets.Dequeue();
ASSERT_NE(packet, nullptr);
// clang-format off
const uint8_t expected_prep_frame[] = {
// 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
// Prep element
131, 31,
0x00, 0x00, 0x20, // flags, hop count, elem ttl
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, // target addr
0x0a, 0x00, 0x00, 0x00, // target hwmp seqno: should be advanced to incoming seqno + 1
0x05, 0x00, 0x00, 0x00, // lifetime: preserved from preq
0x0, 0x0, 0x0, 0x0, // metric
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, // originator addr
0x07, 0x00, 0x00, 0x00, // originator hwmp seqno
};
// clang-format on
EXPECT_RANGES_EQ(expected_prep_frame, Span<const uint8_t>(*packet));
}
// 2. Expect the path table to be updated with the path to the originator
{
auto orig_path = table.GetPath(common::MacAddr("08:09:0a:0b:0c:0d"));
ASSERT_NE(orig_path, nullptr);
EXPECT_EQ(common::MacAddr("11:11:11:11:11:11"), orig_path->next_hop);
EXPECT_EQ(std::optional<uint32_t>(7), orig_path->hwmp_seqno);
EXPECT_EQ(zx::time(1000 + 5 * 1024 * 1000), orig_path->expiration_time);
EXPECT_EQ(100u + 200u, orig_path->metric);
EXPECT_EQ(4u, orig_path->hop_count);
}
// 3. Expect the path table to be updated with the path to the transmitter
{
auto transmitter_path = table.GetPath(common::MacAddr("11:11:11:11:11:11"));
ASSERT_NE(transmitter_path, nullptr);
EXPECT_EQ(common::MacAddr("11:11:11:11:11:11"), transmitter_path->next_hop);
EXPECT_EQ(std::optional<uint32_t>{}, transmitter_path->hwmp_seqno);
EXPECT_EQ(zx::time(1000 + 5 * 1024 * 1000), transmitter_path->expiration_time);
EXPECT_EQ(100u, transmitter_path->metric);
EXPECT_EQ(1u, transmitter_path->hop_count);
}
// 4. Expect our sequence number to be updated
EXPECT_EQ(10u, state.our_hwmp_seqno);
}
TEST_F(HwmpTest, ForwardPreq) {
// clang-format off
const uint8_t preq[] = {
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
50, 0, 0, 0, // metric
1, // target count
// Target 1
0x01, // target flags: target only
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // target address
0x09, 0x00, 0x00, 0x00, // target hwmp seqno
};
// clang-format on
auto packets_to_tx = HandleHwmpAction(preq, common::MacAddr("11:11:11:11:11:11"), self_addr(),
100, CreateMacHeaderWriter(), &state, &table);
ASSERT_EQ(1u, packets_to_tx.size());
auto packet = packets_to_tx.Dequeue();
// clang-format off
const uint8_t expected_preq_frame[] = {
// Mgmt header
0xd0, 0x00, 0x00, 0x00, // fc, duration
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 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
// Preq element
130, 37,
0x00, // flags: no address extension
0x04, // hop count = previous hop count + 1
0x1f, // element ttl = previous ttl - 1
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
150, 0, 0, 0, // metric: previous metric + last hop
1, // target count
// Target 1
0x01, // target flags: target only
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // target address
0x09, 0x00, 0x00, 0x00, // target hwmp seqno
};
// clang-format on
EXPECT_RANGES_EQ(expected_preq_frame, Span<const uint8_t>(*packet));
}
TEST_F(HwmpTest, PreqTimeToDie) {
// clang-format off
const uint8_t preq[] = {
130, 37,
0x00, // flags: no address extension
0x03, // hop count
0x01, // 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
50, 0, 0, 0, // metric
1, // target count
// Target 1
0x01, // target flags: target only
0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, // target address
0x09, 0x00, 0x00, 0x00, // target hwmp seqno
};
// clang-format on
auto packets_to_tx = HandleHwmpAction(preq, common::MacAddr("11:11:11:11:11:11"), self_addr(),
100, CreateMacHeaderWriter(), &state, &table);
// PREQ should not be forwarded because TTL has dropped to zero
ASSERT_EQ(0u, packets_to_tx.size());
}
TEST_F(HwmpTest, PathDiscoveryWithRetry) {
auto expected_preq_frame = [](uint8_t i) {
// clang-format off
return std::vector<uint8_t> {
// Mgmt header
0xd0, 0x00, 0x00, 0x00, // fc, duration
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // addr1
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, // addr2
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, // addr3
static_cast<uint8_t>(i << 4), 0x00, // seq ctl
// Action
13, // category (mesh)
1, // action = HWMP mesh path selection
// Preq element
130, 37,
0x00, // flags: no address extension
0x00, // hop count
0x20, // element ttl
i, 0x00, 0x00, 0x00, // path discovery ID
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, // originator addr
i, 0x00, 0x00, 0x00, // originator hwmp seqno
0x88, 0x13, 0x00, 0x00, // lifetime = 5000 TU
0, 0, 0, 0, // metric
1, // target count
// Target 1
0x05, // target flags: unknown target seqno + target only
0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, // target address
0x00, 0x00, 0x00, 0x00, // target hwmp seqno
};
// clang-format on
};
// 1. Initiate path discovery and check that a PREQ is sent
{
PacketQueue packets_to_tx;
zx_status_t status =
InitiatePathDiscovery(common::MacAddr("bb:bb:bb:bb:bb:bb"), self_addr(),
CreateMacHeaderWriter(), &state, table, &packets_to_tx);
EXPECT_EQ(ZX_OK, status);
ASSERT_EQ(1u, packets_to_tx.size());
auto packet = packets_to_tx.Dequeue();
EXPECT_RANGES_EQ(expected_preq_frame(1), Span<const uint8_t>(*packet));
}
// 2. Trigger a timeout and verify that another PREQ is sent
{
PacketQueue packets_to_tx;
clock.Set(zx::time(ZX_SEC(1u)));
zx_status_t status =
HandleHwmpTimeout(self_addr(), CreateMacHeaderWriter(), &state, table, &packets_to_tx);
EXPECT_EQ(ZX_OK, status);
ASSERT_EQ(1u, packets_to_tx.size());
auto packet = packets_to_tx.Dequeue();
EXPECT_RANGES_EQ(expected_preq_frame(2), Span<const uint8_t>(*packet));
}
// 3. Reply with a PREP and verify that we have a path now
{
// clang-format off
const uint8_t prep[] = {
131, 31,
0x00, 0x01, 0x20, // flags, hop count, elem ttl
0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, // target addr
0x07, 0x00, 0x00, 0x00, // target hwmp seqno
0x00, 0x01, 0x00, 0x00, // lifetime
150, 0x0, 0x0, 0x0, // metric
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, // originator addr
0x02, 0x00, 0x00, 0x00, // originator hwmp seqno
};
// clang-format on
HandleHwmpAction(prep, common::MacAddr("cc:cc:cc:cc:cc:cc"), self_addr(), 100,
CreateMacHeaderWriter(), &state, &table);
auto path = table.GetPath(common::MacAddr("bb:bb:bb:bb:bb:bb"));
ASSERT_NE(nullptr, path);
EXPECT_EQ(common::MacAddr("cc:cc:cc:cc:cc:cc"), path->next_hop);
EXPECT_EQ(std::optional<uint32_t>(7), path->hwmp_seqno);
EXPECT_EQ(zx::time(ZX_SEC(1u)) + WLAN_TU(256u), path->expiration_time);
EXPECT_EQ(100u + 150u, path->metric);
EXPECT_EQ(2u, path->hop_count);
}
// 4. Trigger another timeout and verify that nothing happens
{
PacketQueue packets_to_tx;
clock.Set(zx::time(ZX_SEC(2u)));
zx_status_t status =
HandleHwmpTimeout(self_addr(), CreateMacHeaderWriter(), &state, table, &packets_to_tx);
EXPECT_EQ(ZX_OK, status);
EXPECT_EQ(0u, packets_to_tx.size());
}
}
TEST_F(HwmpTest, ForwardPrep) {
// Assume we have a path to the originator
table.AddOrUpdatePath(common::MacAddr("33:33:33:33:33:33"),
{
.next_hop = common::MacAddr("22:22:22:22:22:22"),
});
// clang-format off
const uint8_t prep[] = {
131, 31,
0x00, 0x01, 0x20, // flags, hop count, elem ttl
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, // target addr
0x07, 0x00, 0x00, 0x00, // target hwmp seqno
0x00, 0x01, 0x00, 0x00, // lifetime
50, 0x0, 0x0, 0x0, // metric
0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // originator addr
0x02, 0x00, 0x00, 0x00, // originator hwmp seqno
};
// clang-format on
PacketQueue packets_to_tx =
HandleHwmpAction(prep, common::MacAddr("44:44:44:44:44:44"), self_addr(), 100,
CreateMacHeaderWriter(), &state, &table);
ASSERT_EQ(packets_to_tx.size(), 1u);
auto packet = packets_to_tx.Dequeue();
ASSERT_NE(packet, nullptr);
// clang-format off
const uint8_t expected_prep_frame[] = {
// Mgmt header
0xd0, 0x00, 0x00, 0x00, // fc, duration
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, // addr1: next hop to the originator
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
// Prep element
131, 31,
0x00, 0x02, 0x1f, // flags, hop count (+1), elem ttl (-1)
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, // target addr
0x07, 0x00, 0x00, 0x00, // target hwmp seqno
0x00, 0x01, 0x00, 0x00, // lifetime
150, 0x0, 0x0, 0x0, // metric (+ last hop)
0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // originator addr
0x02, 0x00, 0x00, 0x00, // originator hwmp seqno
};
// clang-format on
EXPECT_RANGES_EQ(expected_prep_frame, Span<const uint8_t>(*packet));
}
TEST_F(HwmpTest, PrepTimeToDie) {
// Assume we have a path to the originator
table.AddOrUpdatePath(common::MacAddr("33:33:33:33:33:33"),
{
.next_hop = common::MacAddr("22:22:22:22:22:22"),
});
// clang-format off
const uint8_t prep[] = {
131, 31,
0x00, 0x01, 0x01, // flags, hop count, elem ttl
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, // target addr
0x07, 0x00, 0x00, 0x00, // target hwmp seqno
0x00, 0x01, 0x00, 0x00, // lifetime
50, 0x0, 0x0, 0x0, // metric
0x33, 0x33, 0x33, 0x33, 0x33, 0x33, // originator addr
0x02, 0x00, 0x00, 0x00, // originator hwmp seqno
};
// clang-format on
PacketQueue packets_to_tx =
HandleHwmpAction(prep, common::MacAddr("44:44:44:44:44:44"), self_addr(), 100,
CreateMacHeaderWriter(), &state, &table);
// PREP should not be forwarded because TTL has dropped to zero
ASSERT_EQ(packets_to_tx.size(), 0u);
}
} // namespace wlan