From f9928a1a08c57fe853888119a996c3acc98ee09d Mon Sep 17 00:00:00 2001 From: Johannes Stoelp Date: Fri, 14 Jan 2022 23:51:05 +0100 Subject: Initial version of nodemcuv2 dhcp server Able to offer IP address + DNS/Gateway ... Worked with devices at my hand. --- test/native/dhcp.cc | 65 +++++++++++++++++++++++++++++++++++++ test/native/hash.cc | 71 +++++++++++++++++++++++++++++++++++++++++ test/native/lease_db.cc | 85 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 221 insertions(+) create mode 100644 test/native/dhcp.cc create mode 100644 test/native/hash.cc create mode 100644 test/native/lease_db.cc (limited to 'test') diff --git a/test/native/dhcp.cc b/test/native/dhcp.cc new file mode 100644 index 0000000..983c6c8 --- /dev/null +++ b/test/native/dhcp.cc @@ -0,0 +1,65 @@ +// Copyright (c) 2022 Johannes Stoelp + +#include +#include + +#include + +TEST(option, get_available_opt) { + u8 opts[] = { + into_raw(dhcp_option::PAD), into_raw(dhcp_option::PAD), into_raw(dhcp_option::DHCP_MESSAGE_TYPE), 5 /* len */, 0, 1, 2, 3, 4, + into_raw(dhcp_option::END), + }; + + auto ov = get_option(opts, sizeof(opts), dhcp_option::DHCP_MESSAGE_TYPE); + + ASSERT_EQ(true, ov.has_value()); + ASSERT_EQ(0, ov.value().data[0]); + ASSERT_EQ(1, ov.value().data[1]); + ASSERT_EQ(2, ov.value().data[2]); + ASSERT_EQ(3, ov.value().data[3]); + ASSERT_EQ(4, ov.value().data[4]); + ASSERT_EQ(5, ov.value().len); +} + +TEST(option, maleformed_len) { + u8 opts[] = { + into_raw(dhcp_option::PAD), into_raw(dhcp_option::PAD), into_raw(dhcp_option::DHCP_MESSAGE_TYPE), 5 /* len */, 0, + into_raw(dhcp_option::END), + }; + + { + auto ov = get_option(opts, sizeof(opts), dhcp_option::DHCP_MESSAGE_TYPE); + + ASSERT_EQ(false, ov.has_value()); + } + { + auto ov = get_option(opts, sizeof(opts), dhcp_option::CLASS_ID); + + ASSERT_EQ(false, ov.has_value()); + } +} + +TEST(option, maleformed_missing_end) { + u8 opts[] = { + into_raw(dhcp_option::PAD), + into_raw(dhcp_option::PAD), + }; + + auto ov = get_option(opts, sizeof(opts), dhcp_option::DHCP_MESSAGE_TYPE); + + ASSERT_EQ(false, ov.has_value()); +} + +TEST(option, put_opt_val) { + u8 options[8] = {0}; + u32 val = 0xdeadbeef; + + u8* nextp = put_opt_val(options, val); + + ASSERT_EQ(options + 4, nextp); + ASSERT_EQ(0xde, options[0]); + ASSERT_EQ(0xad, options[1]); + ASSERT_EQ(0xbe, options[2]); + ASSERT_EQ(0xef, options[3]); +} \ No newline at end of file diff --git a/test/native/hash.cc b/test/native/hash.cc new file mode 100644 index 0000000..8aa0a35 --- /dev/null +++ b/test/native/hash.cc @@ -0,0 +1,71 @@ +// Copyright (c) 2022 Johannes Stoelp + +#include +#include + +#include +#include +#include +#include +#include + +using ID = std::array; +namespace fs = std::filesystem; + +std::vector read_blob() { + if (!fs::exists("blob")) { + std::system("dd if=/dev/urandom of=blob count=16 bs=1M"); + } + + std::vector ids; + ids.reserve(fs::file_size("blob") / sizeof(ID)); + + auto ifs = std::ifstream("blob"); + assert(ifs.is_open()); + ID id; + while (ifs.read((char*)id.data(), sizeof(id))) { + ids.push_back(id); + } + + return ids; +} + +TEST(hash, uniform_distribuation) { + constexpr usize BUCKETS = 64; + constexpr float BUCKET_SIZE = static_cast(100) / BUCKETS; // Bucket size in percent. + constexpr float BUCKET_ERR = BUCKET_SIZE * 0.05 /* 5% */; // Allowed distribution error. + + const auto ids = read_blob(); + + usize cnt[BUCKETS] = {0}; + for (const auto& id : ids) { + u32 h = hash(id.data(), id.size()); + cnt[h % BUCKETS] += 1; + } + + for (usize b = 0; b < BUCKETS; ++b) { + const float dist = static_cast(cnt[b]) / ids.size() * 100; + ASSERT_GT(dist, BUCKET_SIZE - BUCKET_ERR); + ASSERT_LT(dist, BUCKET_SIZE + BUCKET_ERR); + // printf("bucket %2ld: %5.2f (%ld)\n", b, dist, cnt[b]); + } +} + +TEST(hash, DISABLED_collisions) { + const auto ids = read_blob(); + + std::unordered_map hits; + for (const auto& id : ids) { + u32 h = hash(id.data(), id.size()); + hits[h] = hits[h] + 1; + } + + usize collisions = 0; + for (const auto& hit : hits) { + if (hit.second > 1) { + ++collisions; + } + } + + printf("Hashed %ld values got %ld collisions\n", ids.size(), collisions); +} diff --git a/test/native/lease_db.cc b/test/native/lease_db.cc new file mode 100644 index 0000000..8f87912 --- /dev/null +++ b/test/native/lease_db.cc @@ -0,0 +1,85 @@ +// Copyright (c) 2022 Johannes Stoelp + +#include + +#include + +TEST(lease_db, null_client_hash) { + lease_db<2> db; + + ASSERT_EQ(std::nullopt, db.new_lease(0, 0)); + ASSERT_EQ(std::nullopt, db.get_lease(0)); + ASSERT_EQ(0, db.active_leases()); +} + +TEST(lease_db, get_new_lease) { + lease_db<2> db; + + ASSERT_EQ(std::optional(0), db.new_lease(10, 0)); + ASSERT_EQ(std::optional(1), db.new_lease(20, 0)); + ASSERT_EQ(std::nullopt, db.new_lease(30, 0)); // exhausted + + ASSERT_EQ(std::optional(0), db.get_lease(10)); + ASSERT_EQ(std::optional(1), db.get_lease(20)); + ASSERT_EQ(std::nullopt, db.get_lease(30)); // exhausted + + ASSERT_EQ(2, db.active_leases()); +} + +TEST(lease_db, get_new_lease_twice) { + lease_db<2> db; + + ASSERT_EQ(std::optional(0), db.new_lease(10, 0)); + ASSERT_EQ(std::nullopt, db.new_lease(10, 0)); + + ASSERT_EQ(1, db.active_leases()); +} + +TEST(lease_db, flush_expired) { + lease_db<2> db; + + ASSERT_EQ(std::optional(0), db.new_lease(10, 100 /* lease end */)); + ASSERT_EQ(std::optional(1), db.new_lease(20, 200 /* lease end */)); + + db.flush_expired(50 /* current time */); + ASSERT_EQ(2, db.active_leases()); + ASSERT_EQ(std::optional(0), db.get_lease(10)); + ASSERT_EQ(std::optional(1), db.get_lease(20)); + + db.flush_expired(150 /* current time */); + ASSERT_EQ(1, db.active_leases()); + ASSERT_EQ(std::nullopt, db.get_lease(10)); + ASSERT_EQ(std::optional(1), db.get_lease(20)); + + db.flush_expired(250 /* current time */); + ASSERT_EQ(0, db.active_leases()); + ASSERT_EQ(std::nullopt, db.get_lease(10)); + ASSERT_EQ(std::nullopt, db.get_lease(20)); +} + +TEST(lease_db, update_lease) { + lease_db<2> db; + + ASSERT_EQ(std::optional(0), db.new_lease(10, 100 /* lease end */)); + ASSERT_EQ(std::optional(1), db.new_lease(20, 200 /* lease end */)); + + ASSERT_EQ(2, db.active_leases()); + + db.flush_expired(150 /* current time */); + ASSERT_EQ(1, db.active_leases()); + ASSERT_EQ(std::nullopt, db.get_lease(10)); + ASSERT_EQ(std::optional(1), db.get_lease(20)); + + ASSERT_EQ(false, db.update_lease(10, 300 /* lease end */)); + ASSERT_EQ(true, db.update_lease(20, 300 /* lease end */)); + + db.flush_expired(250 /* current time */); + ASSERT_EQ(1, db.active_leases()); + ASSERT_EQ(std::nullopt, db.get_lease(10)); + ASSERT_EQ(std::optional(1), db.get_lease(20)); + + db.flush_expired(350 /* current time */); + ASSERT_EQ(0, db.active_leases()); + ASSERT_EQ(std::nullopt, db.get_lease(10)); + ASSERT_EQ(std::nullopt, db.get_lease(20)); +} -- cgit v1.2.3