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