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