1 #include "sol_context.hpp"
2 
3 #include "main.hpp"
4 #include "sd_event_loop.hpp"
5 #include "sol_manager.hpp"
6 
7 #include <phosphor-logging/log.hpp>
8 
9 namespace sol
10 {
11 
12 using namespace phosphor::logging;
13 
14 void Context::processInboundPayload(uint8_t seqNum, uint8_t ackSeqNum,
15                                     uint8_t count, bool status,
16                                     const std::vector<uint8_t>& input)
17 {
18     uint8_t respAckSeqNum = 0;
19     uint8_t acceptedCount = 0;
20     auto ack = false;
21 
22     /*
23      * Check if the Inbound sequence number is same as the expected one.
24      * If the Packet Sequence Number is 0, it is an ACK-Only packet. Multiple
25      * outstanding sequence numbers are not supported in this version of the SOL
26      * specification. Retried packets use the same sequence number as the first
27      * packet.
28      */
29     if (seqNum && (seqNum != seqNums.get(true)))
30     {
31         log<level::INFO>("Out of sequence SOL packet - packet is dropped");
32         return;
33     }
34 
35     /*
36      * Check if the expected ACK/NACK sequence number is same as the
37      * ACK/NACK sequence number in the packet. If packet ACK/NACK sequence
38      * number is 0, then it is an informational packet. No request packet being
39      * ACK'd or NACK'd.
40      */
41     if (ackSeqNum && (ackSeqNum != seqNums.get(false)))
42     {
43         log<level::INFO>("Out of sequence ack number - SOL packet is dropped");
44         return;
45     }
46 
47     /*
48      * Retry the SOL payload packet in the following conditions:
49      *
50      * a) NACK in Operation/Status
51      * b) Accepted Character Count does not match with the sent out SOL payload
52      * c) Non-zero Packet ACK/NACK Sequence Number
53      */
54     if (status || ((count != expectedCharCount) && ackSeqNum))
55     {
56         resendPayload(noClear);
57         std::get<eventloop::EventLoop&>(singletonPool)
58             .switchTimer(payloadInstance, eventloop::Timers::RETRY, false);
59         std::get<eventloop::EventLoop&>(singletonPool)
60             .switchTimer(payloadInstance, eventloop::Timers::RETRY, true);
61         return;
62     }
63     /*
64      * Clear the sent data once the acknowledgment sequence number matches
65      * and the expected character count matches.
66      */
67     else if ((count == expectedCharCount) && ackSeqNum)
68     {
69         // Clear the Host Console Buffer
70         std::get<sol::Manager&>(singletonPool).dataBuffer.erase(count);
71 
72         // Once it is acknowledged stop the retry interval timer
73         std::get<eventloop::EventLoop&>(singletonPool)
74             .switchTimer(payloadInstance, eventloop::Timers::RETRY, false);
75 
76         retryCounter = maxRetryCount;
77         expectedCharCount = 0;
78         payloadCache.clear();
79     }
80 
81     // Write character data to the Host Console
82     if (!input.empty() && seqNum)
83     {
84         auto rc =
85             std::get<sol::Manager&>(singletonPool).writeConsoleSocket(input);
86         if (rc)
87         {
88             log<level::ERR>("Writing to console socket descriptor failed");
89             ack = true;
90         }
91         else
92         {
93             respAckSeqNum = seqNum;
94             ack = false;
95             acceptedCount = input.size();
96         }
97     }
98     /*
99      * SOL payload with no character data and valid sequence number can be used
100      * as method to keep the SOL session active.
101      */
102     else if (input.empty() && seqNum)
103     {
104         respAckSeqNum = seqNum;
105     }
106 
107     if (seqNum != 0)
108     {
109         seqNums.incInboundSeqNum();
110         prepareResponse(respAckSeqNum, acceptedCount, ack);
111     }
112     else
113     {
114         std::get<eventloop::EventLoop&>(singletonPool)
115             .switchTimer(payloadInstance, eventloop::Timers::ACCUMULATE, true);
116     }
117 }
118 
119 void Context::prepareResponse(uint8_t ackSeqNum, uint8_t count, bool ack)
120 {
121     auto bufferSize = std::get<sol::Manager&>(singletonPool).dataBuffer.size();
122 
123     /* Sent a ACK only response */
124     if (payloadCache.size() != 0 || (bufferSize < sendThreshold))
125     {
126         std::get<eventloop::EventLoop&>(singletonPool)
127             .switchTimer(payloadInstance, eventloop::Timers::ACCUMULATE, true);
128 
129         std::vector<uint8_t> outPayload(sizeof(Payload));
130         auto response = reinterpret_cast<Payload*>(outPayload.data());
131         response->packetSeqNum = 0;
132         response->packetAckSeqNum = ackSeqNum;
133         response->acceptedCharCount = count;
134         response->outOperation.ack = ack;
135         sendPayload(outPayload);
136         return;
137     }
138 
139     auto readSize = std::min(bufferSize, MAX_PAYLOAD_SIZE);
140     payloadCache.resize(sizeof(Payload) + readSize);
141     auto response = reinterpret_cast<Payload*>(payloadCache.data());
142     response->packetAckSeqNum = ackSeqNum;
143     response->acceptedCharCount = count;
144     response->outOperation.ack = ack;
145     response->packetSeqNum = seqNums.incOutboundSeqNum();
146 
147     auto handle = std::get<sol::Manager&>(singletonPool).dataBuffer.read();
148     std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload));
149     expectedCharCount = readSize;
150 
151     std::get<eventloop::EventLoop&>(singletonPool)
152         .switchTimer(payloadInstance, eventloop::Timers::RETRY, true);
153     std::get<eventloop::EventLoop&>(singletonPool)
154         .switchTimer(payloadInstance, eventloop::Timers::ACCUMULATE, false);
155 
156     sendPayload(payloadCache);
157 }
158 
159 int Context::sendOutboundPayload()
160 {
161     if (payloadCache.size() != 0)
162     {
163         std::get<eventloop::EventLoop&>(singletonPool)
164             .switchTimer(payloadInstance, eventloop::Timers::ACCUMULATE, true);
165         return -1;
166     }
167 
168     auto bufferSize = std::get<sol::Manager&>(singletonPool).dataBuffer.size();
169     auto readSize = std::min(bufferSize, MAX_PAYLOAD_SIZE);
170 
171     payloadCache.resize(sizeof(Payload) + readSize);
172     auto response = reinterpret_cast<Payload*>(payloadCache.data());
173     response->packetAckSeqNum = 0;
174     response->acceptedCharCount = 0;
175     response->outOperation.ack = false;
176     response->packetSeqNum = seqNums.incOutboundSeqNum();
177 
178     auto handle = std::get<sol::Manager&>(singletonPool).dataBuffer.read();
179     std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload));
180     expectedCharCount = readSize;
181 
182     std::get<eventloop::EventLoop&>(singletonPool)
183         .switchTimer(payloadInstance, eventloop::Timers::RETRY, true);
184     std::get<eventloop::EventLoop&>(singletonPool)
185         .switchTimer(payloadInstance, eventloop::Timers::ACCUMULATE, false);
186 
187     sendPayload(payloadCache);
188 
189     return 0;
190 }
191 
192 void Context::resendPayload(bool clear)
193 {
194     sendPayload(payloadCache);
195 
196     if (clear)
197     {
198         payloadCache.clear();
199         expectedCharCount = 0;
200         std::get<sol::Manager&>(singletonPool)
201             .dataBuffer.erase(expectedCharCount);
202     }
203 }
204 
205 void Context::sendPayload(const std::vector<uint8_t>& out) const
206 {
207     auto session =
208         std::get<session::Manager&>(singletonPool).getSession(sessionID);
209 
210     message::Handler msgHandler(session->channelPtr, sessionID);
211 
212     msgHandler.sendSOLPayload(out);
213 }
214 
215 } // namespace sol
216