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*