1 /*
2  * Copyright 2021 Google LLC
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include "platforms/nemora/portable/ncsi.h"
20 #include "platforms/nemora/portable/ncsi_fsm.h"
21 #include "platforms/nemora/portable/ncsi_server.h"
22 
23 #include <netinet/in.h>
24 
25 #include <cstdint>
26 #include <cstring>
27 #include <vector>
28 
29 namespace mock
30 {
31 
32 class NCSIFrame
33 {
34   public:
35     mac_addr_t get_dst_mac() const
36     {
37         return dst_mac_;
38     }
39 
40     mac_addr_t get_src_mac() const
41     {
42         return src_mac_;
43     }
44 
45     uint16_t get_ethertype() const
46     {
47         return ethertype_;
48     }
49 
50     bool is_ncsi() const
51     {
52         return ethertype_ == NCSI_ETHERTYPE;
53     }
54 
55     uint8_t get_control_packet_type() const
56     {
57         return control_packet_type_;
58     }
59 
60     void set_conrol_packet_type(uint8_t control_packet_type)
61     {
62         control_packet_type_ = control_packet_type;
63     }
64 
65     bool is_oem_command() const
66     {
67         return control_packet_type_ == NCSI_OEM_COMMAND;
68     }
69 
70     uint8_t get_channel_id() const
71     {
72         return channel_id_;
73     }
74 
75     void set_channel_id(uint8_t channel_id)
76     {
77         channel_id_ = channel_id;
78     }
79 
80     uint8_t get_oem_command() const
81     {
82         return oem_command_;
83     }
84 
85     void set_oem_command(uint8_t oem_command)
86     {
87         set_conrol_packet_type(NCSI_OEM_COMMAND);
88         oem_command_ = oem_command;
89     }
90 
91     uint32_t get_manufacturer_id() const
92     {
93         return manufacturer_id_;
94     }
95 
96     std::vector<uint8_t>::size_type get_size() const
97     {
98         return packet_raw_.size();
99     }
100 
101     bool is_response() const
102     {
103         return is_response_;
104     }
105 
106     uint16_t get_response_code() const
107     {
108         return response_code_;
109     }
110 
111     uint16_t get_reason_code() const
112     {
113         return reason_code_;
114     }
115 
116     bool parse_ethernet_frame(const ncsi_buf_t& ncsi_buf);
117 
118   private:
119     mac_addr_t dst_mac_;
120     mac_addr_t src_mac_;
121     uint16_t ethertype_ = NCSI_ETHERTYPE;
122     uint8_t control_packet_type_;
123     uint8_t channel_id_;
124     uint8_t oem_command_;
125     uint32_t manufacturer_id_;
126     uint16_t response_code_ = 0;
127     uint16_t reason_code_ = 0;
128     bool is_response_ = false;
129     std::vector<uint8_t> packet_raw_;
130 };
131 
132 class NIC
133 {
134   public:
135     explicit NIC(bool legacy = false, uint8_t channel_count = 1) :
136         channel_count_{channel_count}
137     {
138         if (legacy)
139         {
140             version_.firmware_version = htonl(0x08000000);
141         }
142         else
143         {
144             version_.firmware_version = 0xabcdef12;
145         }
146 
147         is_legacy_ = legacy;
148 
149         set_link_up();
150     }
151 
152     void set_link_up()
153     {
154         link_status_.link_status |= htonl(NCSI_LINK_STATUS_UP);
155     }
156 
157     void set_mac(const mac_addr_t& mac)
158     {
159         mac_ = mac;
160     }
161 
162     mac_addr_t get_mac() const
163     {
164         return mac_;
165     }
166 
167     uint8_t get_channel_count() const
168     {
169         return channel_count_;
170     }
171 
172     // ????? NICs with Google firmware version ????
173     bool is_legacy() const
174     {
175         return is_legacy_;
176     }
177 
178     uint32_t handle_request(const ncsi_buf_t& request_buf,
179                             ncsi_buf_t* response_buf);
180 
181     const std::vector<NCSIFrame>& get_command_log() const
182     {
183         return cmd_log_;
184     }
185 
186     bool set_filter(uint8_t channel, const ncsi_oem_filter_t& filter);
187     const ncsi_oem_filter_t& get_filter(uint8_t channel) const;
188 
189     void set_hostless(bool is_hostless);
190     void toggle_hostless();
191     bool is_hostless();
192 
193     // The NIC itself does not really have a loopback. This is used to emulate
194     // the *absence* of NIC and loopback plug inserted.
195     void set_loopback()
196     {
197         is_loopback_ = true;
198     }
199 
200     void reset_loopback()
201     {
202         is_loopback_ = false;
203     }
204 
205     bool is_filter_configured(uint8_t channel) const;
206 
207   private:
208     static const std::vector<uint8_t> simple_commands_;
209 
210     uint32_t handle_oem_request(const ncsi_buf_t& request_buf,
211                                 ncsi_buf_t* response_buf);
212 
213     void save_frame_to_log(const NCSIFrame& frame);
214 
215     ncsi_version_id_t version_;
216     ncsi_oem_filter_t ch0_filter_;
217     ncsi_oem_filter_t ch1_filter_;
218     bool is_ch0_filter_configured_ = false;
219     bool is_ch1_filter_configured_ = false;
220     uint8_t channel_count_;
221     mac_addr_t mac_ = {{0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba}};
222     std::vector<NCSIFrame> cmd_log_;
223 
224     /* If used in a continuous loop, cmd_log_ may grow too big over time.
225      * This constant determines how many (most recent) commands will be kept. */
226     const uint32_t max_log_size_ = 1000;
227 
228     bool is_legacy_;
229     bool is_loopback_ = false;
230 
231     // TODO: populate stats somehow.
232     ncsi_passthrough_stats_t stats_;
233     ncsi_passthrough_stats_legacy_t stats_legacy_;
234 
235     ncsi_link_status_t link_status_;
236 };
237 
238 } // namespace mock
239