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