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