1 #pragma once
2 
3 #include "console_buffer.hpp"
4 #include "session.hpp"
5 #include "sol_context.hpp"
6 
7 #include <boost/asio/io_context.hpp>
8 #include <boost/asio/local/stream_protocol.hpp>
9 #include <map>
10 #include <memory>
11 #include <string>
12 
13 namespace sol
14 {
15 
16 constexpr size_t MAX_PAYLOAD_SIZE = 255;
17 constexpr uint8_t MAJOR_VERSION = 0x01;
18 constexpr uint8_t MINOR_VERSION = 0x00;
19 
20 constexpr char CONSOLE_SOCKET_PATH[] = "\0obmc-console";
21 constexpr size_t CONSOLE_SOCKET_PATH_LEN = sizeof(CONSOLE_SOCKET_PATH) - 1;
22 
23 constexpr uint8_t accIntervalFactor = 5;
24 constexpr uint8_t retryIntervalFactor = 10;
25 
26 using Instance = uint8_t;
27 
28 using namespace std::chrono_literals;
29 
30 /** @class Manager
31  *
32  *  Manager class acts a manager for the SOL payload instances and provides
33  *  interfaces to start a payload instance, stop a payload instance and get
34  *  reference to the context object.
35  */
36 class Manager
37 {
38   public:
39     /** @brief SOL Payload Instance is the key for the map, the value is the
40      *         SOL context.
41      */
42     using SOLPayloadMap = std::map<Instance, std::shared_ptr<Context>>;
43 
44     Manager() = delete;
45     ~Manager() = default;
46     Manager(const Manager&) = delete;
47     Manager& operator=(const Manager&) = delete;
48     Manager(Manager&&) = default;
49     Manager& operator=(Manager&&) = default;
50 
51     Manager(std::shared_ptr<boost::asio::io_context> io) : io(io)
52     {
53     }
54 
55     /** @brief io context to add events to */
56     std::shared_ptr<boost::asio::io_context> io;
57 
58     /** @brief Host Console Buffer. */
59     ConsoleData dataBuffer;
60 
61     /** @brief Set in Progress.
62      *
63      *  This parameter is used to indicate when any of the SOL parameters
64      *  are being updated, and when the changes are completed. The bit is
65      *  primarily provided to alert software than some other software or
66      *  utility is in the process of making changes to the data. This field
67      *  is initialized to set complete.
68      */
69     uint8_t progress = 0;
70 
71     /** @brief SOL enable
72      *
73      *  This controls whether the SOL payload can be activated. By default
74      *  the SOL is enabled.
75      */
76     bool enable = true;
77 
78     /** @brief SOL payload encryption.
79      *
80      *  Force encryption: if the cipher suite for the session supports
81      *  encryption, then this setting will force the use of encryption for
82      *  all SOL payload data. Encryption controlled by remote console:
83      *  Whether SOL packets are encrypted or not is selectable by the remote
84      *  console at the time the payload is activated. The default is force
85      *  encryption.
86      */
87     bool forceEncrypt = true;
88 
89     /** @brief SOL payload authentication.
90      *
91      *  Force authentication: if the cipher suite for the session supports
92      *  authentication, then this setting will force the use of  for
93      *  authentication for all SOL payload data. Authentication controlled
94      *  by remote console: Note that for the standard Cipher Suites,
95      *  if encryption is used authentication must also be used. Therefore,
96      *  while encryption is being used software will not be able to select
97      *  using unauthenticated payloads.
98      */
99     bool forceAuth = true;
100 
101     /** @brief SOL privilege level.
102      *
103      *  Sets the minimum operating privilege level that is required to be
104      *  able to activate SOL using the Activate Payload command.
105      */
106     session::Privilege solMinPrivilege = session::Privilege::USER;
107 
108     /** @brief Character Accumulate Interval
109      *
110      *  This sets the typical amount of time that the BMC will wait before
111      *  transmitting a partial SOL character data packet. (Where a partial
112      *  packet is defined as a packet that has fewer characters to transmit
113      *  than the number of characters specified by the character send
114      *  threshold. This parameter can be modified by the set SOL
115      *  configuration parameters command. The SOL configuration parameter,
116      *  Character Accumulate Interval is 5 ms increments, 1-based value. The
117      *  parameter value is accumulateInterval/5. The accumulateInterval
118      *  needs to be a multiple of 5.
119      */
120     std::chrono::milliseconds accumulateInterval = 100ms;
121 
122     /** @brief Character Send Threshold
123      *
124      *  The BMC will automatically send an SOL character data packet
125      *  containing this number of characters as soon as this number of
126      *  characters (or greater) has been accepted from the baseboard serial
127      *  controller into the BMC. This provides a mechanism to tune the
128      *  buffer to reduce latency to when the first characters are received
129      *  after an idle interval. In the degenerate case, setting this value
130      *  to a ‘1’ would cause the BMC to send a packet as soon as the first
131      *  character was received. This parameter can be modified by the set
132      *  SOL configuration parameters command.
133      */
134     uint8_t sendThreshold = 1;
135 
136     /** @brief Retry Count
137      *
138      *  1-based. 0 = no retries after packet is transmitted. Packet will be
139      *  dropped if no ACK/NACK received by time retries expire. The maximum
140      *  value for retry count is 7. This parameter can be modified by the
141      *  set SOL configuration parameters command.
142      */
143     uint8_t retryCount = 7;
144 
145     /** @brief Retry Interval
146      *
147      *  Sets the time that the BMC will wait before the first retry and the
148      *  time between retries when sending SOL packets to the remote console.
149      *  This parameter can be modified by the set SOL configuration
150      *  parameters command. The SOL configuration parameter Retry Interval
151      *  is 10 ms increments, 1-based value. The parameter value is
152      *  retryInterval/10. The retryInterval needs to be a multiple of 10.
153      */
154     std::chrono::milliseconds retryInterval = 100ms;
155 
156     /** @brief Channel Number
157      *
158      *  This parameter indicates which IPMI channel is being used for the
159      *  communication parameters (e.g. IP address, MAC address) for the SOL
160      *  Payload. Typically, these parameters will come from the same channel
161      *  that the Activate Payload command for SOL was accepted over. The
162      *  network channel number is defaulted to 1.
163      */
164     uint8_t channel = 1;
165 
166     /** @brief Add host console I/O event source to the event loop.  */
167     void startHostConsole();
168 
169     /** @brief Remove host console I/O event source. */
170     void stopHostConsole();
171 
172     /** @brief Start a SOL payload instance.
173      *
174      *  Starting a payload instance involves creating the context object,
175      *  add the accumulate interval timer and retry interval timer to the
176      *  event loop.
177      *
178      *  @param[in] payloadInstance - SOL payload instance.
179      *  @param[in] sessionID - BMC session ID.
180      */
181     void startPayloadInstance(uint8_t payloadInstance,
182                               session::SessionID sessionID);
183 
184     /** @brief Stop SOL payload instance.
185      *
186      *  Stopping a payload instance involves stopping and removing the
187      *  accumulate interval timer and retry interval timer from the event
188      *  loop, delete the context object.
189      *
190      *  @param[in] payloadInstance - SOL payload instance
191      */
192     void stopPayloadInstance(uint8_t payloadInstance);
193 
194     /** @brief Get SOL Context by Payload Instance.
195      *
196      *  @param[in] payloadInstance - SOL payload instance.
197      *
198      *  @return reference to the SOL payload context.
199      */
200     Context& getContext(uint8_t payloadInstance)
201     {
202         auto iter = payloadMap.find(payloadInstance);
203 
204         if (iter != payloadMap.end())
205         {
206             return *(iter->second);
207         }
208 
209         std::string msg = "Invalid SOL payload instance " + payloadInstance;
210         throw std::runtime_error(msg.c_str());
211     }
212 
213     /** @brief Get SOL Context by Session ID.
214      *
215      *  @param[in] sessionID - IPMI Session ID.
216      *
217      *  @return reference to the SOL payload context.
218      */
219     Context& getContext(session::SessionID sessionID)
220     {
221         for (const auto& kv : payloadMap)
222         {
223             if (kv.second->sessionID == sessionID)
224             {
225                 return *kv.second;
226             }
227         }
228 
229         std::string msg = "Invalid SOL SessionID " + sessionID;
230         throw std::runtime_error(msg.c_str());
231     }
232 
233     /** @brief Check if SOL payload is active.
234      *
235      *  @param[in] payloadInstance - SOL payload instance.
236      *
237      *  @return true if the instance is active and false it is not active.
238      */
239     auto isPayloadActive(uint8_t payloadInstance) const
240     {
241         return (0 != payloadMap.count(payloadInstance));
242     }
243 
244     /** @brief Write data to the host console unix socket.
245      *
246      *  @param[in] input - Data from the remote console.
247      *
248      *  @return 0 on success and errno on failure.
249      */
250     int writeConsoleSocket(const std::vector<uint8_t>& input) const;
251 
252   private:
253     SOLPayloadMap payloadMap;
254 
255     /** @brief Local stream socket for the host console. */
256     std::unique_ptr<boost::asio::local::stream_protocol::socket> consoleSocket =
257         nullptr;
258 
259     /** @brief Initialize the host console file descriptor. */
260     void initConsoleSocket();
261 
262     /** @brief Handle incoming console data on the console socket */
263     void consoleInputHandler();
264 };
265 
266 } // namespace sol
267