xref: /openbmc/google-misc/subprojects/ncsid/test/nic_mock.cpp (revision dab96f131fb3a46d93f1093feccc9095d8589ece)
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 "nic_mock.h"
16* 
17* #include "platforms/nemora/portable/ncsi.h"
18* #include "platforms/nemora/portable/ncsi_server.h"
19* 
20* #include <algorithm>
21* #include <cstddef>
22* #include <cstring>
23* #include <stdexcept>
24* 
25* namespace mock
26* {
27* 
parse_ethernet_frame(const ncsi_buf_t & ncsi_buf)28* bool NCSIFrame::parse_ethernet_frame(const ncsi_buf_t& ncsi_buf)
29* {
30*     std::memcpy(&dst_mac_, ncsi_buf.data, sizeof(dst_mac_));
31*     std::memcpy(&src_mac_, ncsi_buf.data + sizeof(dst_mac_), sizeof(src_mac_));
32*     // The constant defined in a way that assumes big-endian platform, so we are
33*     // just going to calculate it here properly.
34*     const uint8_t et_hi = *(ncsi_buf.data + 2 * sizeof(mac_addr_t));
35*     const uint8_t et_lo = *(ncsi_buf.data + 2 * sizeof(mac_addr_t) + 1);
36*     ethertype_ = (et_hi << 8) + et_lo;
37* 
38*     if (ethertype_ != NCSI_ETHERTYPE)
39*     {
40*         return false;
41*     }
42* 
43*     // This code parses the NC-SI command, according to spec and
44*     // as defined in platforms/nemora/portable/ncsi.h
45*     // It takes some shortcuts to only retrieve the data we are interested in,
46*     // such as using offsetof ot get to a particular field.
47*     control_packet_type_ =
48*         *(ncsi_buf.data + offsetof(ncsi_header_t, control_packet_type));
49*     channel_id_ = *(ncsi_buf.data + offsetof(ncsi_header_t, channel_id));
50* 
51*     size_t payload_offset = sizeof(ncsi_header_t);
52*     if (control_packet_type_ & NCSI_RESPONSE)
53*     {
54*         is_response_ = true;
55*         control_packet_type_ &= ~NCSI_RESPONSE;
56*         std::memcpy(&response_code_, ncsi_buf.data + payload_offset,
57*                     sizeof(response_code_));
58*         response_code_ = ntohs(response_code_);
59*         std::memcpy(&reason_code_,
60*                     ncsi_buf.data + payload_offset + sizeof(reason_code_),
61*                     sizeof(reason_code_));
62*         reason_code_ = ntohs(reason_code_);
63*         payload_offset += sizeof(reason_code_) + sizeof(response_code_);
64*     }
65* 
66*     if (control_packet_type_ == NCSI_OEM_COMMAND)
67*     {
68*         std::memcpy(&manufacturer_id_, ncsi_buf.data + payload_offset,
69*                     sizeof(manufacturer_id_));
70*         manufacturer_id_ = ntohl(manufacturer_id_);
71*         // Number of reserved bytes after manufacturer_id_ = 3
72*         oem_command_ =
73*             *(ncsi_buf.data + payload_offset + sizeof(manufacturer_id_) + 3);
74*         payload_offset += sizeof(ncsi_oem_extension_header_t);
75*     }
76* 
77*     packet_raw_ =
78*         std::vector<uint8_t>(ncsi_buf.data, ncsi_buf.data + ncsi_buf.len);
79*     // TODO: Verify payload length.
80* 
81*     return true;
82* }
83* 
handle_request(const ncsi_buf_t & request_buf,ncsi_buf_t * response_buf)84* uint32_t NIC::handle_request(const ncsi_buf_t& request_buf,
85*                              ncsi_buf_t* response_buf)
86* {
87*     const ncsi_header_t* ncsi_header =
88*         reinterpret_cast<const ncsi_header_t*>(request_buf.data);
89* 
90*     NCSIFrame request_frame;
91*     request_frame.parse_ethernet_frame(request_buf);
92*     save_frame_to_log(request_frame);
93* 
94*     uint32_t response_size;
95*     if (is_loopback_)
96*     {
97*         std::memcpy(response_buf, &request_buf, sizeof(request_buf));
98*         response_size = request_buf.len;
99*     }
100*     else if (std::find(simple_commands_.begin(), simple_commands_.end(),
101*                        ncsi_header->control_packet_type) !=
102*              simple_commands_.end())
103*     {
104*         // Simple Response
105*         response_size =
106*             ncsi_build_simple_ack(request_buf.data, response_buf->data);
107*     }
108*     else
109*     {
110*         // Not-so-Simple Response
111*         switch (ncsi_header->control_packet_type)
112*         {
113*             case NCSI_GET_VERSION_ID:
114*                 response_size = ncsi_build_version_id_ack(
115*                     request_buf.data, response_buf->data, &version_);
116*                 break;
117*             case NCSI_GET_CAPABILITIES:
118*                 response_size = sizeof(ncsi_capabilities_response_t);
119*                 {
120*                     ncsi_capabilities_response_t response;
121*                     ncsi_build_response_header(
122*                         request_buf.data, reinterpret_cast<uint8_t*>(&response),
123*                         0, 0, response_size - sizeof(ncsi_header_t));
124*                     response.channel_count = channel_count_;
125*                     std::memcpy(response_buf->data, &response,
126*                                 sizeof(response));
127*                 }
128*                 break;
129*             case NCSI_GET_PASSTHROUGH_STATISTICS:
130*                 if (is_legacy_)
131*                 {
132*                     response_size = ncsi_build_pt_stats_legacy_ack(
133*                         request_buf.data, response_buf->data, &stats_legacy_);
134*                 }
135*                 else
136*                 {
137*                     response_size = ncsi_build_pt_stats_ack(
138*                         request_buf.data, response_buf->data, &stats_);
139*                 }
140*                 break;
141*             case NCSI_GET_LINK_STATUS:
142*                 response_size = ncsi_build_link_status_ack(
143*                     request_buf.data, response_buf->data, &link_status_);
144*                 break;
145*             case NCSI_OEM_COMMAND:
146*                 response_size = handle_oem_request(request_buf, response_buf);
147*                 break;
148*             default:
149*                 response_size = ncsi_build_simple_nack(
150*                     request_buf.data, response_buf->data, 1, 1);
151*                 break;
152*         }
153*     }
154* 
155*     response_buf->len = response_size;
156* 
157*     return response_size;
158* }
159* 
handle_oem_request(const ncsi_buf_t & request_buf,ncsi_buf_t * response_buf)160* uint32_t NIC::handle_oem_request(const ncsi_buf_t& request_buf,
161*                                  ncsi_buf_t* response_buf)
162* {
163*     const ncsi_oem_simple_cmd_t* oem_cmd =
164*         reinterpret_cast<const ncsi_oem_simple_cmd_t*>(request_buf.data);
165*     uint32_t response_size;
166*     switch (oem_cmd->oem_header.oem_cmd)
167*     {
168*         case NCSI_OEM_COMMAND_GET_HOST_MAC:
169*             response_size = ncsi_build_oem_get_mac_ack(
170*                 request_buf.data, response_buf->data, &mac_);
171*             break;
172*         case NCSI_OEM_COMMAND_SET_FILTER:
173*         {
174*             const ncsi_oem_set_filter_cmd_t* cmd =
175*                 reinterpret_cast<const ncsi_oem_set_filter_cmd_t*>(
176*                     request_buf.data);
177*             if (set_filter(cmd->hdr.channel_id, cmd->filter))
178*             {
179*                 response_size = ncsi_build_oem_simple_ack(request_buf.data,
180*                                                           response_buf->data);
181*             }
182*             else
183*             {
184*                 response_size = ncsi_build_simple_nack(
185*                     request_buf.data, response_buf->data, 3, 4);
186*             }
187*         }
188*         break;
189*         case NCSI_OEM_COMMAND_ECHO:
190*             response_size =
191*                 ncsi_build_oem_echo_ack(request_buf.data, response_buf->data);
192*             break;
193*         case NCSI_OEM_COMMAND_GET_FILTER:
194*         {
195*             const ncsi_simple_command_t* cmd =
196*                 reinterpret_cast<const ncsi_simple_command_t*>(
197*                     request_buf.data);
198*             if (cmd->hdr.channel_id == 0)
199*             {
200*                 response_size = ncsi_build_oem_get_filter_ack(
201*                     request_buf.data, response_buf->data, &ch0_filter_);
202*             }
203*             else if (cmd->hdr.channel_id == 1)
204*             {
205*                 response_size = ncsi_build_oem_get_filter_ack(
206*                     request_buf.data, response_buf->data, &ch1_filter_);
207*             }
208*             else
209*             {
210*                 response_size = ncsi_build_simple_nack(
211*                     request_buf.data, response_buf->data, 3, 4);
212*             }
213*         }
214*         break;
215*         default:
216*             response_size = ncsi_build_simple_nack(request_buf.data,
217*                                                    response_buf->data, 1, 2);
218*             break;
219*     }
220* 
221*     return response_size;
222* }
223* 
is_filter_configured(uint8_t channel) const224* bool NIC::is_filter_configured(uint8_t channel) const
225* {
226*     if (channel == 0)
227*     {
228*         return is_ch0_filter_configured_;
229*     }
230*     else if (channel == 1)
231*     {
232*         return is_ch1_filter_configured_;
233*     }
234* 
235*     throw std::invalid_argument("Unsupported channel");
236* }
237* 
set_filter(uint8_t channel,const ncsi_oem_filter_t & filter)238* bool NIC::set_filter(uint8_t channel, const ncsi_oem_filter_t& filter)
239* {
240*     ncsi_oem_filter_t* nic_filter;
241*     if (channel == 0)
242*     {
243*         nic_filter = &ch0_filter_;
244*         is_ch0_filter_configured_ = true;
245*     }
246*     else if (channel == 1)
247*     {
248*         nic_filter = &ch1_filter_;
249*         is_ch1_filter_configured_ = true;
250*     }
251*     else
252*     {
253*         throw std::invalid_argument("Unsupported channel");
254*     }
255* 
256*     std::memcpy(nic_filter->mac, filter.mac, MAC_ADDR_SIZE);
257*     nic_filter->ip = 0;
258*     nic_filter->port = filter.port;
259*     return true;
260* }
261* 
get_filter(uint8_t channel) const262* const ncsi_oem_filter_t& NIC::get_filter(uint8_t channel) const
263* {
264*     if (channel == 0)
265*     {
266*         return ch0_filter_;
267*     }
268*     else if (channel == 1)
269*     {
270*         return ch1_filter_;
271*     }
272* 
273*     throw std::invalid_argument("Unsupported channel");
274* }
275* 
set_hostless(bool is_hostless)276* void NIC::set_hostless(bool is_hostless)
277* {
278*     auto set_flag_op = [](uint8_t lhs, uint8_t rhs) -> auto {
279*         return lhs | rhs;
280*     };
281* 
282*     auto clear_flag_op = [](uint8_t lhs, uint8_t rhs) -> auto {
283*         return lhs & ~rhs;
284*     };
285* 
286*     auto flag_op = is_hostless ? set_flag_op : clear_flag_op;
287* 
288*     if (channel_count_ > 0)
289*     {
290*         ch0_filter_.flags =
291*             flag_op(ch0_filter_.flags, NCSI_OEM_FILTER_FLAGS_HOSTLESS);
292*     }
293* 
294*     if (channel_count_ > 1)
295*     {
296*         ch1_filter_.flags =
297*             flag_op(ch1_filter_.flags, NCSI_OEM_FILTER_FLAGS_HOSTLESS);
298*     }
299* }
300* 
toggle_hostless()301* void NIC::toggle_hostless()
302* {
303*     if (channel_count_ > 0)
304*     {
305*         ch0_filter_.flags ^= NCSI_OEM_FILTER_FLAGS_HOSTLESS;
306*     }
307* 
308*     if (channel_count_ > 1)
309*     {
310*         ch1_filter_.flags ^= NCSI_OEM_FILTER_FLAGS_HOSTLESS;
311*     }
312* }
313* 
is_hostless()314* bool NIC::is_hostless()
315* {
316*     return ch0_filter_.flags & NCSI_OEM_FILTER_FLAGS_HOSTLESS;
317* }
318* 
save_frame_to_log(const NCSIFrame & frame)319* void NIC::save_frame_to_log(const NCSIFrame& frame)
320* {
321*     if (cmd_log_.size() >= max_log_size_)
322*     {
323*         cmd_log_.erase(cmd_log_.begin());
324*     }
325* 
326*     cmd_log_.push_back(frame);
327* }
328* 
329* const std::vector<uint8_t> NIC::simple_commands_ = {
330*     NCSI_CLEAR_INITIAL_STATE,
331*     NCSI_SELECT_PACKAGE,
332*     NCSI_DESELECT_PACKAGE,
333*     NCSI_ENABLE_CHANNEL,
334*     NCSI_DISABLE_CHANNEL,
335*     NCSI_RESET_CHANNEL,
336*     NCSI_ENABLE_CHANNEL_NETWORK_TX,
337*     NCSI_DISABLE_CHANNEL_NETWORK_TX,
338*     NCSI_AEN_ENABLE,
339*     NCSI_SET_LINK,
340*     NCSI_SET_VLAN_FILTER,
341*     NCSI_ENABLE_VLAN,
342*     NCSI_DISABLE_VLAN,
343*     NCSI_SET_MAC_ADDRESS,
344*     NCSI_ENABLE_BROADCAST_FILTER,
345*     NCSI_DISABLE_BROADCAST_FILTER,
346*     NCSI_ENABLE_GLOBAL_MULTICAST_FILTER,
347*     NCSI_DISABLE_GLOBAL_MULTICAST_FILTER,
348*     NCSI_SET_NCSI_FLOW_CONTROL,
349* };
350* 
351* } // namespace mock
352*