diff --git a/.azure-pipelines/build.yml b/.azure-pipelines/build.yml index 667d6c3..f27db6c 100644 --- a/.azure-pipelines/build.yml +++ b/.azure-pipelines/build.yml @@ -30,6 +30,8 @@ jobs: sudo apt-get install -y dotnet-sdk-6.0 displayName: install .Net - script: | + set -ex + sudo apt-get update sudo apt-get install -y \ libboost-system-dev \ libboost-thread-dev \ @@ -51,7 +53,13 @@ jobs: displayName: "Install dependencies" - checkout: self clean: true - submodules: true + submodules: recursive + - script: | + git submodule foreach --recursive 'git clean -xfdf || true' + git submodule foreach --recursive 'git reset --hard || true' + git submodule foreach --recursive 'git remote update || true' + git submodule update --init --recursive + displayName: 'Reset submodules' - task: DownloadPipelineArtifact@2 inputs: source: specific diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..73f0083 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "gmock_global"] + path = gmock_global + url = https://github.com/apriorit/gmock-global.git diff --git a/Makefile b/Makefile index a83ccb2..eb77866 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,6 @@ +.ONESHELL: +SHELL = /bin/bash + RM := rm -rf BUILD_DIR := build BUILD_TEST_DIR := build-test @@ -12,7 +15,7 @@ override LDLIBS += -levent -lhiredis -lswsscommon -pthread -lboost_thread -lboos override CPPFLAGS += -Wall -std=c++17 -fPIE -I/usr/include/swss override CPPFLAGS += -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" CPPFLAGS_TEST := --coverage -fprofile-arcs -ftest-coverage -fprofile-generate -fsanitize=address -LDLIBS_TEST := --coverage -lgtest -pthread -lstdc++fs -fsanitize=address +LDLIBS_TEST := --coverage -lgtest -lgmock -pthread -lstdc++fs -fsanitize=address PWD := $(shell pwd) all: $(DHCP6RELAY_TARGET) $(DHCP6RELAY_TEST_TARGET) diff --git a/azurepipelines-coverage.yml b/azurepipelines-coverage.yml index 508de56..3946e2c 100644 --- a/azurepipelines-coverage.yml +++ b/azurepipelines-coverage.yml @@ -2,4 +2,4 @@ coverage: status: # Code coverage status will be posted to pull requests based on targets defined below. comments: on # Off by default. When on, details about coverage for each file changed will be posted as a pull request comment. diff: # Diff coverage is code coverage only for the lines changed in a pull request. - target: 60% # Set this to a desired percentage. Default is 70 percent + target: 90% # Set this to a desired percentage. Default is 70 percent diff --git a/gmock_global b/gmock_global new file mode 160000 index 0000000..6552faa --- /dev/null +++ b/gmock_global @@ -0,0 +1 @@ +Subproject commit 6552faa2a94250ed0192dbd62e608d37fafcc216 diff --git a/src/configInterface.cpp b/src/config_interface.cpp similarity index 88% rename from src/configInterface.cpp rename to src/config_interface.cpp index d0801af..7d7d04b 100644 --- a/src/configInterface.cpp +++ b/src/config_interface.cpp @@ -1,12 +1,11 @@ #include #include #include -#include "configInterface.h" +#include "config_interface.h" constexpr auto DEFAULT_TIMEOUT_MSEC = 1000; bool pollSwssNotifcation = true; -std::shared_ptr mSwssThreadPtr; swss::Select swssSelect; /** @@ -29,6 +28,17 @@ void initialize_swss(std::unordered_map &vlans) } } +/** +*@code stopSwssNotificationPoll +* +*@brief stop SWSS listening thread +* +*@return none +*/ +static void stopSwssNotificationPoll() { + pollSwssNotifcation = false; +}; + /** * @code void deinitialize_swss() * @@ -39,12 +49,8 @@ void initialize_swss(std::unordered_map &vlans) void deinitialize_swss() { stopSwssNotificationPoll(); - if (mSwssThreadPtr != nullptr) { - mSwssThreadPtr->interrupt(); - } } - /** * @code void get_dhcp(std::unordered_map &vlans, swss::SubscriberStateTable *ipHelpersTable, bool dynamic) @@ -58,6 +64,7 @@ void get_dhcp(std::unordered_map &vlans, swss::Subscr int ret = swssSelect.select(&selectable, DEFAULT_TIMEOUT_MSEC); if (ret == swss::Select::ERROR) { syslog(LOG_WARNING, "Select: returned ERROR"); + return; } else if (ret == swss::Select::TIMEOUT) { } if (selectable == static_cast (ipHelpersTable)) { @@ -69,21 +76,6 @@ void get_dhcp(std::unordered_map &vlans, swss::Subscr } } } -/** - * @code void handleSwssNotification(std::vector *vlans) - * - * @brief main thread for handling SWSS notification - * - * @param context map of vlans/argument config that contains strings of server and option - * - * @return none - */ -void handleSwssNotification(swssNotification test) -{ - while (pollSwssNotifcation) { - get_dhcp(test.vlans, test.ipHelpersTable, true); - } -} /** * @code void handleRelayNotification(swss::SubscriberStateTable &ipHelpersTable, std::unordered_map &vlans) @@ -152,14 +144,3 @@ void processRelayNotification(std::deque &entries, vlans[vlan] = intf; } } - -/** -*@code stopSwssNotificationPoll -* -*@brief stop SWSS listening thread -* -*@return none -*/ -void stopSwssNotificationPoll() { - pollSwssNotifcation = false; -}; diff --git a/src/configInterface.h b/src/config_interface.h similarity index 83% rename from src/configInterface.h rename to src/config_interface.h index 94a0578..96f221b 100644 --- a/src/configInterface.h +++ b/src/config_interface.h @@ -37,16 +37,6 @@ void deinitialize_swss(); * @return none */ void get_dhcp(std::unordered_map &vlans, swss::SubscriberStateTable *ipHelpersTable, bool dynamic); -/** - * @code void swssNotification test - * - * @brief main thread for handling SWSS notification - * - * @param test swssNotification that includes list of vlans/argument config that contains strings of server and option - * - * @return none - */ -void handleSwssNotification(swssNotification test); /** * @code void handleRelayNotification(swss::SubscriberStateTable &ipHelpersTable, std::unordered_map &vlans) @@ -71,12 +61,3 @@ void handleRelayNotification(swss::SubscriberStateTable &ipHelpersTable, std::un * @return none */ void processRelayNotification(std::deque &entries, std::unordered_map &vlans); - -/** -*@code stopSwssNotificationPoll -* -*@brief stop SWSS listening thread -* -*@return none -*/ -void stopSwssNotificationPoll(); diff --git a/src/main.cpp b/src/main.cpp index 9da3cb8..17101b6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,7 @@ #include #include #include -#include "configInterface.h" +#include "config_interface.h" bool dual_tor_sock = false; diff --git a/src/relay.cpp b/src/relay.cpp index 376c4b9..2342853 100644 --- a/src/relay.cpp +++ b/src/relay.cpp @@ -9,7 +9,7 @@ #include "configdb.h" #include "sonicv2connector.h" #include "dbconnector.h" -#include "configInterface.h" +#include "config_interface.h" struct event *listen_event; struct event *server_listen_event; @@ -163,6 +163,12 @@ uint8_t *RelayMsg::MarshalBinary(uint16_t &len) { auto opt = m_option_list.MarshalBinary(); if (opt && !opt->empty()) { + if (opt->size() + sizeof(dhcpv6_relay_msg) > BUFFER_SIZE) { + syslog(LOG_WARNING, "Failed to marshal relay msg, packet size %lu over limit\n", + opt->size() + sizeof(dhcpv6_relay_msg)); + len = 0; + return nullptr; + } std::memcpy(ptr + sizeof(dhcpv6_relay_msg), opt->data(), opt->size()); len += opt->size(); } @@ -210,6 +216,12 @@ uint8_t *DHCPv6Msg::MarshalBinary(uint16_t &len) { auto opt = m_option_list.MarshalBinary(); if (opt && !opt->empty()) { + if (opt->size() + sizeof(dhcpv6_msg) > BUFFER_SIZE) { + syslog(LOG_WARNING, "Failed to marshal dhcpv6 msg, packet size %lu over limit\n", + opt->size() + sizeof(dhcpv6_msg)); + len = 0; + return nullptr; + } std::memcpy(ptr + sizeof(dhcpv6_msg), opt->data(), opt->size()); len += opt->size(); } @@ -637,7 +649,7 @@ void relay_client(int sock, const uint8_t *msg, uint16_t len, const ip6_hdr *ip_ uint16_t relay_pkt_len = 0; auto relay_pkt = relay.MarshalBinary(relay_pkt_len); - if (!relay_pkt_len) { + if (!relay_pkt_len || !relay_pkt) { char addr_str[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &ip_hdr->ip6_src, addr_str, INET6_ADDRSTRLEN); syslog(LOG_ERR, "Relay-forward marshal error, client dhcpv6 from %s", addr_str); @@ -683,10 +695,10 @@ void relay_relay_forw(int sock, const uint8_t *msg, int32_t len, const ip6_hdr * /* add relay-msg option */ relay.m_option_list.Add(OPTION_RELAY_MSG, msg, len); - + uint16_t send_buffer_len = 0; auto send_buffer = relay.MarshalBinary(send_buffer_len); - if (!send_buffer_len) { + if (!send_buffer_len || !send_buffer) { char addr_str[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &ip_hdr->ip6_src, addr_str, INET6_ADDRSTRLEN); syslog(LOG_ERR, "Marshal relay-forward message from %s error", addr_str); @@ -929,7 +941,7 @@ void server_callback(evutil_socket_t fd, short event, void *arg) { } if (buffer_sz < (int32_t)sizeof(struct dhcpv6_msg)) { - syslog(LOG_WARNING, "Invalid DHCPv6 packet length %ld, no space for dhcpv6 msg header\n", buffer_sz); + syslog(LOG_WARNING, "Invalid DHCPv6 packet length %zd, no space for dhcpv6 msg header\n", buffer_sz); continue; } @@ -955,7 +967,7 @@ void server_callback(evutil_socket_t fd, short event, void *arg) { */ int signal_init() { int rv = -1; - do { + do { ev_sigint = evsignal_new(base, SIGINT, signal_callback, base); if (ev_sigint == NULL) { syslog(LOG_ERR, "Could not create SIGINT libevent signal\n"); @@ -1043,13 +1055,14 @@ void loop_relay(std::unordered_map &vlans) { base = event_base_new(); if(base == NULL) { syslog(LOG_ERR, "libevent: Failed to create base\n"); + exit(EXIT_FAILURE); } std::shared_ptr state_db = std::make_shared ("STATE_DB", 0); std::shared_ptr config_db = std::make_shared ("CONFIG_DB", 0); std::shared_ptr mStateDbMuxTablePtr = std::make_shared ( - state_db.get(), "HW_MUX_CABLE_TABLE" - ); + state_db.get(), "HW_MUX_CABLE_TABLE" + ); auto filter = sock_open(ðer_relay_fprog); if (filter != -1) { @@ -1102,23 +1115,23 @@ void loop_relay(std::unordered_map &vlans) { syslog(LOG_INFO, "libevent: Add server listen socket for %s\n", vlan.first.c_str()); } - if((signal_init() == 0) && signal_start() == 0) { - shutdown(); - for(std::size_t i = 0; i state_db, std::string counterVlan); diff --git a/src/subdir.mk b/src/subdir.mk index c2f37b1..451f144 100644 --- a/src/subdir.mk +++ b/src/subdir.mk @@ -1,5 +1,5 @@ SRCS += \ src/sender.cpp \ src/relay.cpp \ -src/configInterface.cpp \ +src/config_interface.cpp \ src/main.cpp diff --git a/test/MockRelay.h b/test/MockRelay.h deleted file mode 100644 index 2d27799..0000000 --- a/test/MockRelay.h +++ /dev/null @@ -1,3 +0,0 @@ -#include "../src/relay.h" -#include "../src/configInterface.h" -#include "mock_send.h" diff --git a/test/mock_config_interface.cpp b/test/mock_config_interface.cpp new file mode 100644 index 0000000..9298f3c --- /dev/null +++ b/test/mock_config_interface.cpp @@ -0,0 +1,67 @@ +#include "mock_config_interface.h" + +using namespace ::testing; + +TEST(configInterface, initialize_swss) { + std::shared_ptr config_db = std::make_shared ("CONFIG_DB", 0); + config_db->hset("DHCP_RELAY|Vlan1000", "dhcpv6_servers@", "fc02:2000::1,fc02:2000::2,fc02:2000::3,fc02:2000::4"); + config_db->hset("DHCP_RELAY|Vlan1000", "dhcpv6_option|rfc6939_support", "false"); + config_db->hset("DHCP_RELAY|Vlan1000", "dhcpv6_option|interface_id", "true"); + std::unordered_map vlans; + ASSERT_NO_THROW(initialize_swss(vlans)); + EXPECT_EQ(vlans.size(), 1); +} + +TEST(configInterface, deinitialize_swss) { + ASSERT_NO_THROW(deinitialize_swss()); +} + +TEST(configInterface, get_dhcp) { + std::shared_ptr config_db = std::make_shared ("CONFIG_DB", 0); + config_db->hset("DHCP_RELAY|Vlan1000", "dhcpv6_servers@", "fc02:2000::1,fc02:2000::2,fc02:2000::3,fc02:2000::4"); + config_db->hset("DHCP_RELAY|Vlan1000", "dhcpv6_option|rfc6939_support", "false"); + config_db->hset("DHCP_RELAY|Vlan1000", "dhcpv6_option|interface_id", "true"); + swss::SubscriberStateTable ipHelpersTable(config_db.get(), "DHCP_RELAY"); + std::unordered_map vlans; + + ASSERT_NO_THROW(get_dhcp(vlans, &ipHelpersTable, false)); + EXPECT_EQ(vlans.size(), 0); + + swssSelect.addSelectable(&ipHelpersTable); + + ASSERT_NO_THROW(get_dhcp(vlans, &ipHelpersTable, false)); + EXPECT_EQ(vlans.size(), 1); +} + +TEST(configInterface, handleRelayNotification) { + std::shared_ptr cfg_db = std::make_shared ("CONFIG_DB", 0); + swss::SubscriberStateTable ipHelpersTable(cfg_db.get(), "DHCP_RELAY"); + std::unordered_map vlans; + handleRelayNotification(ipHelpersTable, vlans); +} + +TEST(configInterface, processRelayNotification) { + std::shared_ptr config_db = std::make_shared ("CONFIG_DB", 0); + config_db->hset("DHCP_RELAY|Vlan1000", "dhcpv6_servers@", "fc02:2000::1,fc02:2000::2,fc02:2000::3,fc02:2000::4"); + config_db->hset("DHCP_RELAY|Vlan1000", "dhcpv6_option|rfc6939_support", "false"); + config_db->hset("DHCP_RELAY|Vlan1000", "dhcpv6_option|interface_id", "true"); + swss::SubscriberStateTable ipHelpersTable(config_db.get(), "DHCP_RELAY"); + swssSelect.addSelectable(&ipHelpersTable); + std::deque entries; + ipHelpersTable.pops(entries); + std::unordered_map vlans; + + processRelayNotification(entries, vlans); + + EXPECT_EQ(vlans.size(), 1); + EXPECT_FALSE(vlans["Vlan1000"].is_option_79); + EXPECT_TRUE(vlans["Vlan1000"].is_interface_id); + EXPECT_FALSE(vlans["Vlan1000"].state_db); +} + +MOCK_GLOBAL_FUNC0(stopSwssNotificationPoll, void(void)); + +TEST(configInterface, stopSwssNotificationPoll) { + EXPECT_GLOBAL_CALL(stopSwssNotificationPoll, stopSwssNotificationPoll()).Times(1); + ASSERT_NO_THROW(stopSwssNotificationPoll()); +} \ No newline at end of file diff --git a/test/mock_config_interface.h b/test/mock_config_interface.h new file mode 100644 index 0000000..51c2b09 --- /dev/null +++ b/test/mock_config_interface.h @@ -0,0 +1,12 @@ +#pragma once + +#include "../src/config_interface.h" +#include "mock_send.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "../gmock_global/include/gmock-global/gmock-global.h" +#include +#include + +extern bool pollSwssNotifcation; +extern swss::Select swssSelect; \ No newline at end of file diff --git a/test/MockRelay.cpp b/test/mock_relay.cpp similarity index 76% rename from test/MockRelay.cpp rename to test/mock_relay.cpp index bbd89a5..8cc27d9 100644 --- a/test/MockRelay.cpp +++ b/test/mock_relay.cpp @@ -9,12 +9,11 @@ #include "gtest/gtest.h" #include "gmock/gmock.h" -#include "MockRelay.h" +#include "mock_relay.h" + +using namespace ::testing; bool dual_tor_sock = false; -extern struct event_base *base; -extern struct event *ev_sigint; -extern struct event *ev_sigterm; static struct sock_filter ether_relay_filter[] = { @@ -291,11 +290,9 @@ TEST(prepareConfig, prepare_socket) prepare_socket(local_sock, server_sock, config); EXPECT_GE(local_sock, 0); - EXPECT_GE(server_sock, 0); } - TEST(counter, initialize_counter) { std::shared_ptr state_db = std::make_shared ("STATE_DB", 0); @@ -341,6 +338,7 @@ TEST(relay, relay_client) struct relay_config config{}; config.is_option_79 = true; + config.is_interface_id = true; std::vector servers; servers.push_back("fc02:2000::1"); servers.push_back("fc02:2000::2"); @@ -367,7 +365,14 @@ TEST(relay, relay_client) ip6_hdr ip_hdr; std::string s_addr = "2000::3"; - relay_client(mock_sock, msg, msg_len, &ip_hdr, ðer_hdr, &config); + // invalid msg_len testing + ASSERT_NO_THROW(relay_client(mock_sock, msg, 2, &ip_hdr, ðer_hdr, &config)); + + // packet with a super length > sizeof(msg) + EXPECT_DEATH(relay_client(mock_sock, msg, 65535, &ip_hdr, ðer_hdr, &config), ""); + + // normal packet testing + ASSERT_NO_THROW(relay_client(mock_sock, msg, msg_len, &ip_hdr, ðer_hdr, &config)); EXPECT_EQ(last_used_sock, 124); @@ -443,7 +448,19 @@ TEST(relay, relay_relay_forw) { std::string s_addr = "2000::3"; inet_pton(AF_INET6, s_addr.c_str(), &ip_hdr.ip6_src); - relay_relay_forw(mock_sock, msg, msg_len, &ip_hdr, &config); + // msg with hop count > HOP_LIMIT + auto hop = msg[1]; + msg[1] = 65; + ASSERT_NO_THROW(relay_relay_forw(mock_sock, msg, msg_len, &ip_hdr, &config)); + msg[1] = hop; + + // super frame over size limit for secondary relay + uint8_t super_frame[BUFFER_SIZE] = {}; + ::memcpy(super_frame, msg, msg_len); + ASSERT_NO_THROW(relay_relay_forw(mock_sock, super_frame, BUFFER_SIZE, &ip_hdr, &config)); + + // normal packet + ASSERT_NO_THROW(relay_relay_forw(mock_sock, msg, msg_len, &ip_hdr, &config)); EXPECT_EQ(last_used_sock, 125); @@ -483,7 +500,7 @@ TEST(relay, relay_relay_reply) 0x02, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x25, 0x3a, 0x32, 0x33, 0x50, 0xe5, 0x49, 0x50, 0x9e, 0x40 - }; + }; int32_t msg_len = sizeof(msg); struct relay_config config{}; @@ -507,7 +524,34 @@ TEST(relay, relay_relay_reply) prepare_relay_config(config, local_sock, filter); - relay_relay_reply(mock_sock, msg, msg_len, &config); + // invalid message length + ASSERT_NO_THROW(relay_relay_reply(mock_sock, msg, 2, &config)); + + // invalid relay msg, without OPTION_RELAY_MSG + uint8_t invalid_msg[] = { + 0x0d, 0x00, 0x20, 0x01, 0x0d, 0xb8, 0x01, 0x5a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x12, 0x00, 0x03, 0x47, 0x69, + 0x32, 0x00, 0x10, 0x00, 0x54, 0x07, 0x4f, 0x6d, + 0x04, 0x00, 0x03, 0x00, 0x28, 0xb0, 0x12, 0xe8, + 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0x00, 0x18, 0x20, 0x01, 0x0d, + 0xb8, 0x01, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x78, 0x00, 0x00, 0x1c, + 0x20, 0x00, 0x00, 0x1d, 0x4c, 0x00, 0x01, 0x00, + 0x0e, 0x00, 0x01, 0x00, 0x01, 0x25, 0x3a, 0x37, + 0xb9, 0x5a, 0xc6, 0xb0, 0x12, 0xe8, 0xb4, 0x00, + 0x02, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x25, + 0x3a, 0x32, 0x33, 0x50, 0xe5, 0x49, 0x50, 0x9e, + 0x40 + }; + ASSERT_NO_THROW(relay_relay_reply(mock_sock, invalid_msg, msg_len, &config)); + + + // normal message + ASSERT_NO_THROW(relay_relay_reply(mock_sock, msg, msg_len, &config)); EXPECT_EQ(last_used_sock, 123); @@ -547,27 +591,31 @@ TEST(relay, signal_init) { EXPECT_NE((uintptr_t)ev_sigterm, NULL); } +MOCK_GLOBAL_FUNC1(event_base_dispatch, int(struct event_base *)); +MOCK_GLOBAL_FUNC2(event_add, int(struct event *, const struct timeval *)); + TEST(relay, signal_start) { - signal_init(); - EXPECT_NE((uintptr_t)ev_sigint, NULL); - EXPECT_NE((uintptr_t)ev_sigterm, NULL); - signal_start(); + EXPECT_GLOBAL_CALL(event_add, event_add(_, NULL)).Times(5) + .WillOnce(Return(-1)) + .WillOnce(Return(0)).WillOnce(Return(-1)) + .WillOnce(Return(0)).WillOnce(Return(0)); + EXPECT_EQ(signal_start(), -1); + EXPECT_EQ(signal_start(), -1); + EXPECT_GLOBAL_CALL(event_base_dispatch, event_base_dispatch(_)).Times(1).WillOnce(Return(-1)); + EXPECT_EQ(signal_start(), 0); } +MOCK_GLOBAL_FUNC2(event_base_loopexit, int(struct event_base *, const struct timeval *)); + TEST(relay, signal_callback) { - signal_callback(1, 1, &base); + ASSERT_NO_THROW(signal_callback(1, 1, &base)); + EXPECT_GLOBAL_CALL(event_base_loopexit, event_base_loopexit(_, _)); + signal_callback(SIGTERM, 1, &base); } TEST(relay, dhcp6relay_stop) { - int filter = 1; - std::unordered_map vlans; - base = event_base_new(); - struct event* event = event_new(base, filter, EV_READ|EV_PERSIST, client_callback, - reinterpret_cast(&vlans)); - dhcp6relay_stop(); - event_free(event); - event_base_free(base); - base = NULL; + EXPECT_GLOBAL_CALL(event_base_loopexit, event_base_loopexit(_, _)); + ASSERT_NO_THROW(dhcp6relay_stop()); } TEST(relay, update_vlan_mapping) { @@ -618,19 +666,45 @@ TEST(relay, client_packet_handler) { 0x15, 0x18 }; - try { - // invalid packet length - client_packet_handler(client_raw_solicit, 4, &config, ifname); + uint8_t client_raw_solicit_with_externsion[] = { + 0x33, 0x33, 0x00, 0x01, 0x00, 0x02, 0x08, 0x00, 0x27, 0xfe, 0x8f, 0x95, 0x86, 0xdd, 0x60, 0x00, + 0x00, 0x00, 0x00, 0x44, 0x2c, 0x01, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, + 0x27, 0xff, 0xfe, 0xfe, 0x8f, 0x95, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x11, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, + 0x02, 0x22, 0x02, 0x23, 0x00, 0x3c, 0xad, 0x08, 0x01, 0x10, + 0x08, 0x74, 0x00, 0x01, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x1c, 0x39, 0xcf, 0x88, 0x08, 0x00, + 0x27, 0xfe, 0x8f, 0x95, 0x00, 0x06, 0x00, 0x04, 0x00, 0x17, 0x00, 0x18, 0x00, 0x08, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x19, 0x00, 0x0c, 0x27, 0xfe, 0x8f, 0x95, 0x00, 0x00, 0x0e, 0x10, 0x00, 0x00, + 0x15, 0x18 + }; + uint8_t non_udp_with_externsion[] = { + 0x33, 0x33, 0x00, 0x01, 0x00, 0x02, 0x08, 0x00, 0x27, 0xfe, 0x8f, 0x95, 0x86, 0xdd, 0x60, 0x00, + 0x00, 0x00, 0x00, 0x44, 0x2c, 0x01, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, + 0x27, 0xff, 0xfe, 0xfe, 0x8f, 0x95, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, + 0x2c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, + 0x11, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, + 0x02, 0x22, 0x02, 0x23, 0x00, 0x3c, 0xad, 0x08, 0x01, 0x10, + 0x08, 0x74, 0x00, 0x01, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x1c, 0x39, 0xcf, 0x88, 0x08, 0x00, + 0x27, 0xfe, 0x8f, 0x95, 0x00, 0x06, 0x00, 0x04, 0x00, 0x17, 0x00, 0x18, 0x00, 0x08, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x19, 0x00, 0x0c, 0x27, 0xfe, 0x8f, 0x95, 0x00, 0x00, 0x0e, 0x10, 0x00, 0x00, + 0x15, 0x18 + }; + + // invalid packet length + ASSERT_NO_THROW(client_packet_handler(client_raw_solicit, 4, &config, ifname)); - client_packet_handler(client_raw_solicit, sizeof(client_raw_solicit), &config, ifname); + ASSERT_NO_THROW(client_packet_handler(client_raw_solicit, sizeof(client_raw_solicit), &config, ifname)); - client_packet_handler(client_raw_solicit_invalid_type, sizeof(client_raw_solicit_invalid_type), &config, ifname); - } - catch (const std::exception& e) { - EXPECT_TRUE(false); - } + ASSERT_NO_THROW(client_packet_handler(client_raw_solicit_invalid_type, sizeof(client_raw_solicit_invalid_type), &config, ifname)); + + ASSERT_NO_THROW(client_packet_handler(client_raw_solicit_with_externsion, sizeof(client_raw_solicit_with_externsion), &config, ifname)); + + ASSERT_NO_THROW(client_packet_handler(non_udp_with_externsion, sizeof(non_udp_with_externsion), &config, ifname)); } +MOCK_GLOBAL_FUNC6(recvfrom, ssize_t(int, void *, size_t, int, struct sockaddr *, socklen_t *)); + TEST(relay, server_callback) { std::shared_ptr state_db = std::make_shared ("STATE_DB", 0); initialize_counter(state_db, "DHCPv6_COUNTER_TABLE|Vlan1000"); @@ -643,18 +717,27 @@ TEST(relay, server_callback) { config.interface = "Vlan1000"; config.state_db = state_db; config.local_sock = -1; + // simulator normal dhcpv6 packet length + ssize_t msg_len = 129; - // negative case testing - try { - server_callback(0, 0, &config); - } - catch (const std::exception& e) { - EXPECT_TRUE(false); - } + // cover buffer_sz <= 0 + EXPECT_GLOBAL_CALL(recvfrom, recvfrom(_, _, _, _, _, _)).Times(5).WillOnce(Return(0)) + .WillOnce(Return(2)).WillOnce(Return(0)) + .WillOnce(Return(msg_len)).WillOnce(Return(0)); + ASSERT_NO_THROW(server_callback(0, 0, &config)); + // cover 0 < buffer_sz < sizeof(struct dhcpv6_msg) + ASSERT_NO_THROW(server_callback(0, 0, &config)); + + ASSERT_NO_THROW(server_callback(0, 0, &config)); } +MOCK_GLOBAL_FUNC2(if_indextoname, char*(unsigned int, char *)); + TEST(relay, client_callback) { std::shared_ptr state_db = std::make_shared ("STATE_DB", 0); + std::shared_ptr mux_table = std::make_shared ( + state_db.get(), "HW_MUX_CABLE_TABLE" + ); initialize_counter(state_db, "DHCPv6_COUNTER_TABLE|Vlan1000"); struct relay_config config{}; @@ -664,34 +747,61 @@ TEST(relay, client_callback) { config.servers.push_back("fc02:2000::2"); config.interface = "Vlan1000"; config.state_db = state_db; + config.mux_table = mux_table; config.local_sock = -1; + std::unordered_map vlans; + // mock normal dhcpv6 packet length + ssize_t msg_len = 114; + std::string vlan1000 = "Vlan1000"; + std::string vlan2000 = "Vlan2000"; + char ethernet1[IF_NAMESIZE] = "Ethernet1"; + char ethernet2[IF_NAMESIZE] = "Ethernet2"; + char ethernet3[IF_NAMESIZE] = "Ethernet3"; + + char ptr[20] = "vlan"; + vlans[vlan1000] = config; + vlan_map["Ethernet1"] = vlan1000; + vlan_map["Ethernet2"] = vlan2000; + // negative case testing - try { - client_callback(-1, 0, &config); - } - catch (const std::exception& e) { - EXPECT_TRUE(false); - } + EXPECT_GLOBAL_CALL(recvfrom, recvfrom(_, _, _, _, _, _)).Times(11) + .WillOnce(Return(0)) + .WillOnce(Return(2)).WillOnce(Return(0)) + .WillOnce(Return(msg_len)).WillOnce(Return(0)) + .WillOnce(Return(msg_len)).WillOnce(Return(0)) + .WillOnce(Return(msg_len)).WillOnce(Return(0)) + .WillOnce(Return(msg_len)).WillOnce(Return(0)); + + EXPECT_GLOBAL_CALL(if_indextoname, if_indextoname(_, _)).Times(5).WillOnce(Return(nullptr)) + .WillOnce(DoAll(SetArrayArgument<1>(ethernet1, ethernet1 + IF_NAMESIZE), Return(ptr))) + .WillOnce(DoAll(SetArrayArgument<1>(ethernet2, ethernet2 + IF_NAMESIZE), Return(ptr))) + .WillOnce(DoAll(SetArrayArgument<1>(ethernet1, ethernet1 + IF_NAMESIZE), Return(ptr))) + .WillOnce(DoAll(SetArrayArgument<1>(ethernet3, ethernet3 + IF_NAMESIZE), Return(ptr))); + // test buffer_sz <=0 early return + ASSERT_NO_THROW(client_callback(-1, 0, &vlans)); + // test buffer_sz > 0, if_indextoname == null early return + ASSERT_NO_THROW(client_callback(-1, 0, &vlans)); + // test normal msg but vlan not found + ASSERT_NO_THROW(client_callback(-1, 0, &vlans)); + // test normal msg and vlan found + ASSERT_NO_THROW(client_callback(-1, 0, &vlans)); + + dual_tor_sock = true; + // test normal msg and vlan found + dual tor + ASSERT_NO_THROW(client_callback(-1, 0, &vlans)); + dual_tor_sock = false; + + // normal msg but interface mapping missing + ASSERT_NO_THROW(client_callback(-1, 0, &vlans)); } -TEST(relay, shutdown) { +TEST(relay, shutdown_relay) { signal_init(); EXPECT_NE((uintptr_t)ev_sigint, NULL); EXPECT_NE((uintptr_t)ev_sigterm, NULL); - try { - shutdown(); - } - catch (const std::exception& e) { - EXPECT_TRUE(false); - } -} - -TEST(relay, initialize_swss) { - std::unordered_map vlans; - initialize_swss(vlans); - EXPECT_FALSE(vlans.size()); + ASSERT_NO_THROW(shutdown_relay()); } TEST(options, Add) { @@ -848,6 +958,18 @@ TEST(dhcpv6_msg, MarshalBinary) { msg = dhcpv6.MarshalBinary(length); EXPECT_TRUE(msg); EXPECT_EQ(length, sizeof(solicit)); + + // negative test for marshal error + class DHCPv6Msg dhcpv6_neg; + result = dhcpv6_neg.UnmarshalBinary(solicit, sizeof(solicit)); + EXPECT_TRUE(result); + + uint8_t super_frame[65530] = {}; + + dhcpv6_neg.m_option_list.Add(100, super_frame, 65530); + msg = dhcpv6_neg.MarshalBinary(length); + EXPECT_FALSE(msg); + EXPECT_FALSE(length); } TEST(dhcpv6_msg, UnmarshalBinary) { @@ -880,3 +1002,19 @@ TEST(dhcpv6_msg, UnmarshalBinary) { EXPECT_EQ(dhcpv6.m_msg_hdr.msg_type, 1); } +TEST(relay, loop_relay) { + std::unordered_map vlans_in_loop; + std::shared_ptr state_db = std::make_shared ("STATE_DB", 0); + struct relay_config config{ + .state_db = state_db, + .interface = "Vlan1000", + .is_option_79 = true + }; + vlans_in_loop["Vlan1000"] = config; + EXPECT_EQ(vlans_in_loop.size(), 1); + + EXPECT_GLOBAL_CALL(event_base_dispatch, event_base_dispatch(_)).Times(1).WillOnce(Return(-1)); + EXPECT_GLOBAL_CALL(event_add, event_add(_, NULL)).Times(AtLeast(2)).WillOnce(Return(0)) + .WillOnce(Return(0)); + ASSERT_NO_THROW(loop_relay(vlans_in_loop)); +} \ No newline at end of file diff --git a/test/mock_relay.h b/test/mock_relay.h new file mode 100644 index 0000000..67bf28c --- /dev/null +++ b/test/mock_relay.h @@ -0,0 +1,14 @@ +#pragma once + +#include "../src/relay.h" +#include "mock_send.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "../gmock_global/include/gmock-global/gmock-global.h" +#include +#include + +extern struct event_base *base; +extern struct event *ev_sigint; +extern struct event *ev_sigterm; +extern std::unordered_map vlan_map; \ No newline at end of file diff --git a/test/mock_send.cpp b/test/mock_send.cpp index 19a9835..a194689 100644 --- a/test/mock_send.cpp +++ b/test/mock_send.cpp @@ -1,7 +1,7 @@ #include "mock_send.h" #include -uint8_t sender_buffer[4096]; +uint8_t sender_buffer[65535]; int32_t valid_byte_count; int last_used_sock; sockaddr_in6 last_target; diff --git a/test/mock_send.h b/test/mock_send.h index fb39fb7..8b92949 100644 --- a/test/mock_send.h +++ b/test/mock_send.h @@ -1,9 +1,11 @@ +#pragma once + #include #include #include #include "../src/sender.h" -extern uint8_t sender_buffer[4096]; +extern uint8_t sender_buffer[65535]; extern int32_t valid_byte_count; extern int last_used_sock; extern sockaddr_in6 last_target; diff --git a/test/subdir.mk b/test/subdir.mk index 2523d4b..8c8a55d 100644 --- a/test/subdir.mk +++ b/test/subdir.mk @@ -2,5 +2,6 @@ TEST_SRCS += \ test/mock_send.cpp \ test/main.cpp \ src/relay.cpp \ -src/configInterface.cpp \ -test/MockRelay.cpp +src/config_interface.cpp \ +test/mock_relay.cpp \ +test/mock_config_interface.cpp