1 #include "net_iface_mock.h"
2 #include "nic_mock.h"
3 #include "platforms/nemora/portable/default_addresses.h"
4 #include "platforms/nemora/portable/ncsi.h"
5 #include "platforms/nemora/portable/ncsi_fsm.h"
6 #include "platforms/nemora/portable/net_types.h"
7 
8 #include <ncsi_state_machine.h>
9 #include <net_config.h>
10 #include <net_sockio.h>
11 #include <netinet/ether.h>
12 #include <netinet/in.h>
13 
14 #include <gmock/gmock.h>
15 
16 namespace
17 {
18 
19 constexpr uint32_t ETHER_NCSI = 0x88f8;
20 
21 class MockConfig : public net::ConfigBase
22 {
23   public:
24     int get_mac_addr(mac_addr_t* mac) override
25     {
26         std::memcpy(mac, &mac_addr, sizeof(mac_addr_t));
27 
28         return 0;
29     }
30 
31     int set_mac_addr(const mac_addr_t& mac) override
32     {
33         mac_addr = mac;
34 
35         return 0;
36     }
37 
38     int set_nic_hostless(bool is_hostless) override
39     {
40         is_nic_hostless = is_hostless;
41 
42         return 0;
43     }
44 
45     mac_addr_t mac_addr;
46     bool is_nic_hostless = true;
47 };
48 
49 class NICConnection : public net::SockIO
50 {
51   public:
52     int write(const void* buf, size_t len) override
53     {
54         conseq_reads = 0;
55         ++n_writes;
56         std::memcpy(last_write.data, buf, len);
57         last_write.len = len;
58         const auto* hdr = reinterpret_cast<const struct ether_header*>(buf);
59         if (ETHER_NCSI == ntohs(hdr->ether_type))
60         {
61             ++n_handles;
62             next_read.len = nic_mock.handle_request(last_write, &next_read);
63         }
64 
65         return len;
66     }
67 
68     int recv(void* buf, size_t maxlen) override
69     {
70         ++n_reads;
71         ++conseq_reads;
72 
73         if (read_timeout > 0)
74         {
75             if (conseq_reads > read_timeout)
76             {
77                 return 0;
78             }
79         }
80 
81         if (maxlen < next_read.len)
82         {
83             ++n_read_errs;
84             return 0;
85         }
86 
87         std::memcpy(buf, next_read.data, next_read.len);
88 
89         return next_read.len;
90     }
91 
92     mock::NIC nic_mock{false, 2};
93     int n_writes = 0;
94     int n_reads = 0;
95     int n_handles = 0;
96     int n_read_errs = 0;
97 
98     // Max number of consequitive reads without writes.
99     int read_timeout = -1;
100     int conseq_reads = 0;
101 
102     ncsi_buf_t last_write = {};
103     ncsi_buf_t next_read = {};
104 };
105 
106 } // namespace
107 
108 class TestNcsi : public testing::Test
109 {
110   public:
111     void SetUp() override
112     {
113         ncsi_sm.set_sockio(&ncsi_sock);
114         ncsi_sm.set_net_config(&net_config_mock);
115         ncsi_sm.set_retest_delay(0);
116         ncsi_sock.nic_mock.set_mac(nic_mac);
117         ncsi_sock.nic_mock.set_hostless(true);
118         ncsi_sock.read_timeout = 10;
119     }
120 
121   protected:
122     void ExpectFiltersNotConfigured()
123     {
124         for (uint8_t i = 0; i < ncsi_sock.nic_mock.get_channel_count(); ++i)
125         {
126             EXPECT_FALSE(ncsi_sock.nic_mock.is_filter_configured(i));
127         }
128     }
129 
130     void ExpectFiltersConfigured()
131     {
132         // Check that filters are configured on all channels.
133         for (uint8_t i = 0; i < ncsi_sock.nic_mock.get_channel_count(); ++i)
134         {
135             EXPECT_TRUE(ncsi_sock.nic_mock.is_filter_configured(i));
136             const ncsi_oem_filter_t& ch_filter =
137                 ncsi_sock.nic_mock.get_filter(i);
138 
139             for (unsigned i = 0; i < sizeof(nic_mac.octet); ++i)
140             {
141                 EXPECT_EQ(nic_mac.octet[i], ch_filter.mac[i]);
142             }
143 
144             EXPECT_EQ(ch_filter.ip, 0);
145             const uint16_t filter_port = ntohs(ch_filter.port);
146             EXPECT_EQ(filter_port, DEFAULT_ADDRESSES_RX_PORT);
147         }
148     }
149 
150     MockConfig net_config_mock;
151     NICConnection ncsi_sock;
152     ncsi::StateMachine ncsi_sm;
153     const mac_addr_t nic_mac = {{0xde, 0xca, 0xfb, 0xad, 0x01, 0x02}};
154 
155     // Number of states in each state machine
156     static constexpr int l2_num_states = 26;
157     static constexpr int l3l4_num_states = 2;
158     static constexpr int test_num_states = 9;
159 
160     // Total number of states in all three state machines.
161     static constexpr int total_num_states =
162         l2_num_states + l3l4_num_states + test_num_states;
163 };
164 
165 TEST_F(TestNcsi, TestMACAddrPropagation)
166 {
167     ncsi_sm.run(total_num_states);
168     EXPECT_EQ(ncsi_sock.n_read_errs, 0);
169     EXPECT_EQ(ncsi_sock.n_handles, ncsi_sock.n_writes);
170     EXPECT_EQ(0, std::memcmp(nic_mac.octet, net_config_mock.mac_addr.octet,
171                              sizeof(nic_mac.octet)));
172 
173     // Since network is not configured, the filters should not be configured
174     // either.
175     ExpectFiltersNotConfigured();
176 }
177 
178 TEST_F(TestNcsi, TestFilterConfiguration)
179 {
180     ncsi_sm.run(total_num_states);
181     EXPECT_EQ(ncsi_sock.n_read_errs, 0);
182     EXPECT_EQ(ncsi_sock.n_handles, ncsi_sock.n_writes);
183 
184     ExpectFiltersConfigured();
185 }
186 
187 TEST_F(TestNcsi, TestFilterReset)
188 {
189     ncsi_sm.run(total_num_states);
190     EXPECT_EQ(ncsi_sock.n_read_errs, 0);
191     EXPECT_EQ(ncsi_sock.n_handles, ncsi_sock.n_writes);
192 
193     // Since network is not configured, the filters should not be configured
194     // either.
195     ExpectFiltersNotConfigured();
196 
197     ncsi_sm.run(total_num_states);
198 
199     ExpectFiltersConfigured();
200 }
201 
202 TEST_F(TestNcsi, TestRetest)
203 {
204     ncsi_sm.run(total_num_states + test_num_states);
205 
206     // Verify that the test state machine was stepped through twice,
207     // by counting how many times the last command of the state machine
208     // has been executed.
209     const uint8_t last_test_command = NCSI_GET_LINK_STATUS;
210     const auto& cmd_log = ncsi_sock.nic_mock.get_command_log();
211     int num_test_runs = 0;
212     for (const auto& ncsi_frame : cmd_log)
213     {
214         if (ncsi_frame.get_control_packet_type() == last_test_command)
215         {
216             ++num_test_runs;
217         }
218     }
219 
220     EXPECT_EQ(num_test_runs, 2);
221 }
222 
223 TEST_F(TestNcsi, TestHostlessSwitch)
224 {
225     // By default the NIC is in hostless mode.
226     // Verify that net config flag changes after FSM run.
227     net_config_mock.is_nic_hostless = false;
228     ncsi_sm.run(total_num_states);
229     EXPECT_EQ(ncsi_sock.n_read_errs, 0);
230     EXPECT_EQ(ncsi_sock.n_handles, ncsi_sock.n_writes);
231     EXPECT_TRUE(net_config_mock.is_nic_hostless);
232 
233     // Now disable the hostless mode and verify that net config
234     // flag changes to false.
235     ncsi_sock.nic_mock.set_hostless(false);
236     ncsi_sm.run(total_num_states);
237     EXPECT_EQ(ncsi_sock.n_read_errs, 0);
238     EXPECT_EQ(ncsi_sock.n_handles, ncsi_sock.n_writes);
239     EXPECT_FALSE(net_config_mock.is_nic_hostless);
240 }
241