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 = l2_num_states + l3l4_num_states +
176                                             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