Line data Source code
1 : /*
2 : * Copyright (c) 2014 CodiLime, Inc. All rights reserved.
3 : */
4 :
5 : #include "bfd/bfd_session.h"
6 : #include "bfd/bfd_control_packet.h"
7 : #include "bfd/bfd_common.h"
8 : #include "bfd/bfd_connection.h"
9 :
10 : #include <boost/asio.hpp>
11 : #include <boost/random.hpp>
12 : #include <string>
13 : #include <algorithm>
14 :
15 : #include "base/logging.h"
16 :
17 : namespace BFD {
18 :
19 29 : Session::Session(Discriminator localDiscriminator,
20 : const SessionKey &key,
21 : EventManager *evm,
22 29 : const SessionConfig &config, Connection *communicator) :
23 29 : localDiscriminator_(localDiscriminator),
24 29 : key_(key),
25 29 : sendTimer_(TimerManager::CreateTimer(*evm->io_service(),
26 : "BFD TX", TaskScheduler::GetInstance()->GetTaskId("BFD"), 0)),
27 29 : recvTimer_(TimerManager::CreateTimer(*evm->io_service(),
28 : "BFD RX", TaskScheduler::GetInstance()->GetTaskId("BFD"), 0)),
29 29 : currentConfig_(config),
30 29 : nextConfig_(config),
31 29 : sm_(CreateStateMachine(evm, this)),
32 29 : pollSequence_(false),
33 29 : communicator_(communicator),
34 29 : local_endpoint_(key.local_address, GetRandomLocalPort()),
35 29 : remote_endpoint_(key.remote_address, key.remote_port),
36 29 : started_(false),
37 87 : stopped_(false) {
38 29 : ScheduleSendTimer();
39 29 : ScheduleRecvDeadlineTimer();
40 58 : sm_->SetCallback(boost::optional<ChangeCb>(
41 29 : boost::bind(&Session::CallStateChangeCallbacks, this, _1, _2)));
42 29 : }
43 :
44 29 : uint16_t Session::GetRandomLocalPort() const {
45 29 : boost::random::uniform_int_distribution<> dist(kSendPortMin, kSendPortMax);
46 29 : return dist(randomGen);
47 : }
48 :
49 57 : Session::~Session() {
50 32 : Stop();
51 32 : callbacks_.clear();
52 57 : }
53 :
54 116 : bool Session::SendTimerExpired() {
55 116 : ControlPacket packet;
56 :
57 116 : stats_.send_timer_expired_count++;
58 116 : PreparePacket(nextConfig_, &packet);
59 116 : SendPacket(&packet);
60 :
61 : // Workaround: Timer code isn't re-entrant
62 116 : this->sendTimer_->Reschedule(tx_interval().total_milliseconds());
63 116 : return true;
64 116 : }
65 :
66 8 : bool Session::RecvTimerExpired() {
67 :
68 8 : if (local_state_non_locking() == kUp) {
69 : // Bfd state will transition to Down state,
70 : // restore to default values
71 3 : remoteSession_.minRxInterval = boost::posix_time::seconds(1);
72 3 : remoteSession_.minTxInterval = boost::posix_time::seconds(0);
73 : }
74 8 : sm_->ProcessTimeout();
75 8 : stats_.receive_timer_expired_count++;
76 :
77 8 : return false;
78 : }
79 :
80 1 : std::string Session::toString() const {
81 1 : std::ostringstream out;
82 1 : out << "SessionKey: " << key_.to_string() << "\n";
83 1 : out << "LocalDiscriminator: 0x" << std::hex << localDiscriminator_ << "\n";
84 1 : out << "RemoteDiscriminator: 0x" << std::hex << remoteSession_.discriminator
85 1 : << "\n";
86 1 : out << "DesiredMinTxInterval: " << currentConfig_.desiredMinTxInterval
87 1 : << "\n";
88 1 : out << "RequiredMinRxInterval: " << currentConfig_.requiredMinRxInterval
89 1 : << "\n";
90 1 : out << "RemoteMinRxInterval: " << remoteSession_.minRxInterval << "\n";
91 1 : out << "RemoteMinTxInterval: " << remoteSession_.minTxInterval << "\n";
92 1 : out << "Local State:" << local_state() << "\n";
93 1 : out << "Remote State:" << remote_state().state << "\n";
94 :
95 2 : return out.str();
96 1 : }
97 :
98 33 : void Session::ScheduleSendTimer() {
99 : int elapsed_time_ms;
100 : int remaining_time_ms;
101 33 : TimeInterval ti = tx_interval();
102 :
103 : // get the elapsed time only if the bfd session timer is running,
104 : // otherwise program the config send timer value
105 33 : if (started_ == true) {
106 4 : elapsed_time_ms = sendTimer_->GetElapsedTime();
107 4 : sendTimer_->Cancel();
108 4 : if (elapsed_time_ms < 0) {
109 0 : remaining_time_ms = 0;
110 : } else {
111 4 : remaining_time_ms = ti.total_milliseconds() - elapsed_time_ms;
112 : }
113 : } else {
114 : // timer not yet started, program with config value
115 29 : remaining_time_ms = ti.total_milliseconds();
116 : }
117 :
118 33 : if (remaining_time_ms > 0) {
119 30 : sendTimer_->Start(remaining_time_ms,
120 : boost::bind(&Session::SendTimerExpired, this));
121 : } else {
122 : // fire the timer now!
123 3 : sendTimer_->Start(0,
124 : boost::bind(&Session::SendTimerExpired, this));
125 : }
126 33 : if (started_ != true) {
127 29 : started_ = true;
128 : }
129 33 : }
130 :
131 142 : void Session::ScheduleRecvDeadlineTimer() {
132 142 : TimeInterval ti = detection_time();
133 :
134 142 : recvTimer_->Cancel();
135 142 : recvTimer_->Start(ti.total_milliseconds(),
136 : boost::bind(&Session::RecvTimerExpired, this));
137 142 : }
138 :
139 11852 : BFDState Session::local_state_non_locking() const {
140 11852 : return sm_->GetState();
141 : }
142 :
143 11261 : BFDState Session::local_state() const {
144 11261 : return local_state_non_locking();
145 : }
146 :
147 : // If periodic BFD Control packets are already being sent (the remote
148 : // system is not in Demand mode), the Poll Sequence MUST be performed by
149 : // setting the Poll (P) bit on those scheduled periodic transmissions;
150 : // additional packets MUST NOT be sent.
151 1 : void Session::InitPollSequence() {
152 1 : pollSequence_ = true;
153 2 : if (local_state_non_locking() != kUp &&
154 1 : local_state_non_locking() != kAdminDown) {
155 1 : ControlPacket packet;
156 1 : PreparePacket(nextConfig_, &packet);
157 1 : SendPacket(&packet);
158 1 : }
159 1 : }
160 :
161 121 : void Session::PreparePacket(const SessionConfig &config,
162 : ControlPacket *packet) {
163 :
164 121 : packet->state = local_state_non_locking();
165 121 : packet->poll = pollSequence_;
166 121 : packet->sender_discriminator = localDiscriminator_;
167 121 : packet->receiver_discriminator = remoteSession_.discriminator;
168 121 : packet->detection_time_multiplier = config.detectionTimeMultiplier;
169 121 : packet->desired_min_tx_interval = config.desiredMinTxInterval;
170 121 : packet->required_min_rx_interval = config.requiredMinRxInterval;
171 121 : }
172 :
173 115 : ResultCode Session::ProcessControlPacket(const ControlPacket *packet) {
174 115 : TimeInterval oldMinRxInterval = remoteSession_.minRxInterval;
175 115 : remoteSession_.discriminator = packet->sender_discriminator;
176 115 : remoteSession_.detectionTimeMultiplier = packet->detection_time_multiplier;
177 115 : remoteSession_.state = packet->state;
178 115 : if ((local_state_non_locking() == kUp) && packet->poll) {
179 3 : remoteSession_.minTxInterval = packet->desired_min_tx_interval;
180 3 : if (packet->required_min_rx_interval < remoteSession_.minRxInterval) {
181 2 : remoteSession_.minRxInterval = packet->required_min_rx_interval;
182 2 : ScheduleSendTimer();
183 : } else {
184 : // After sending the BFD pkt with previous agreed rate, update
185 : // the SendTimer() with new remoteSession_.minRxInterval so as to
186 : // not impact the remote Session's detection time.
187 1 : remoteSession_.minRxInterval = packet->required_min_rx_interval;
188 : }
189 206 : } else if (local_state_non_locking() == kInit ||
190 94 : local_state_non_locking() == kDown) {
191 51 : remoteSession_.minRxInterval = packet->required_min_rx_interval;
192 51 : remoteSession_.minTxInterval = packet->desired_min_tx_interval;
193 51 : remoteSession_.detectionTimeMultiplier =
194 51 : packet->detection_time_multiplier;
195 102 : if (packet->required_min_rx_interval.total_microseconds() &&
196 102 : oldMinRxInterval >= (packet->required_min_rx_interval * 10)) {
197 : // reschedule the sendtimer to the new value
198 2 : ScheduleSendTimer();
199 : }
200 : }
201 :
202 115 : sm_->ProcessRemoteState(packet->state);
203 :
204 : // poll sequence
205 115 : if (packet->poll) {
206 4 : ControlPacket newPacket;
207 4 : PreparePacket(nextConfig_, &newPacket);
208 4 : newPacket.poll = false; // poll & final are forbidden in single packet
209 4 : newPacket.final = true;
210 4 : SendPacket(&newPacket);
211 4 : }
212 115 : if (packet->final) {
213 3 : pollSequence_ = false;
214 3 : currentConfig_ = nextConfig_;
215 : }
216 :
217 139 : if (local_state_non_locking() == kUp ||
218 24 : local_state_non_locking() == kInit) {
219 113 : ScheduleRecvDeadlineTimer();
220 : }
221 :
222 115 : return kResultCode_Ok;
223 : }
224 :
225 121 : void Session::SendPacket(const ControlPacket *packet) {
226 : boost::asio::mutable_buffer buffer =
227 : boost::asio::mutable_buffer(new uint8_t[kMinimalPacketLength],
228 121 : kMinimalPacketLength);
229 121 : int pktSize = EncodeControlPacket(packet,
230 : boost::asio::buffer_cast<uint8_t *>(buffer), kMinimalPacketLength);
231 121 : if (pktSize != kMinimalPacketLength) {
232 0 : LOG(ERROR,
233 : "Unable to encode packet: pktSize " << pktSize
234 : << ", session: " << toString());
235 0 : stats_.tx_error_count++;
236 0 : const uint8_t *p = boost::asio::buffer_cast<const uint8_t *>(buffer);
237 0 : delete[] p;
238 : } else {
239 121 : communicator_->SendPacket(local_endpoint_, remote_endpoint_,
240 121 : key_.index, buffer, pktSize);
241 121 : stats_.tx_count++;
242 : }
243 121 : }
244 :
245 146 : TimeInterval Session::detection_time() {
246 292 : return std::max(currentConfig_.requiredMinRxInterval,
247 146 : remoteSession_.minTxInterval) *
248 146 : remoteSession_.detectionTimeMultiplier;
249 : }
250 :
251 149 : TimeInterval Session::tx_interval() {
252 149 : TimeInterval minInterval, maxInterval;
253 :
254 : TimeInterval negotiatedInterval =
255 298 : std::max(currentConfig_.desiredMinTxInterval,
256 149 : remoteSession_.minRxInterval);
257 :
258 149 : minInterval = negotiatedInterval * 3/4;
259 149 : if (currentConfig_.detectionTimeMultiplier == 1) {
260 0 : maxInterval = negotiatedInterval * 9/10;
261 : } else {
262 149 : maxInterval = negotiatedInterval;
263 : }
264 :
265 : boost::random::uniform_int_distribution<>
266 298 : dist(minInterval.total_microseconds(),
267 149 : maxInterval.total_microseconds());
268 149 : return boost::posix_time::microseconds(dist(randomGen));
269 : }
270 :
271 86 : const SessionKey &Session::key() const {
272 86 : return key_;
273 : }
274 :
275 32 : void Session::Stop() {
276 32 : if (stopped_ == false) {
277 29 : TimerManager::DeleteTimer(sendTimer_);
278 29 : TimerManager::DeleteTimer(recvTimer_);
279 29 : stopped_ = true;
280 29 : started_ = false;
281 29 : sm_->SetCallback(boost::optional<ChangeCb>());
282 : }
283 32 : }
284 :
285 0 : SessionConfig Session::config() const {
286 0 : return nextConfig_;
287 : }
288 :
289 3 : BFDRemoteSessionState Session::remote_state() const {
290 3 : return remoteSession_;
291 : }
292 :
293 54 : Discriminator Session::local_discriminator() const {
294 54 : return localDiscriminator_;
295 : }
296 :
297 52 : void Session::CallStateChangeCallbacks(
298 : const SessionKey &key, const BFD::BFDState &new_state) {
299 52 : for (Callbacks::const_iterator it = callbacks_.begin();
300 99 : it != callbacks_.end(); ++it) {
301 47 : it->second(key, new_state);
302 : }
303 52 : }
304 :
305 28 : void Session::RegisterChangeCallback(ClientId client_id, ChangeCb cb) {
306 28 : callbacks_[client_id] = cb;
307 28 : }
308 :
309 0 : void Session::UnregisterChangeCallback(ClientId client_id) {
310 0 : callbacks_.erase(client_id);
311 0 : }
312 :
313 3 : void Session::UpdateConfig(const SessionConfig& config) {
314 3 : nextConfig_.desiredMinTxInterval = config.desiredMinTxInterval;
315 3 : nextConfig_.requiredMinRxInterval = config.requiredMinRxInterval;
316 3 : nextConfig_.detectionTimeMultiplier = config.detectionTimeMultiplier;
317 3 : pollSequence_ = true;
318 3 : }
319 :
320 0 : int Session::reference_count() {
321 0 : return callbacks_.size();
322 : }
323 :
324 11217 : bool Session::Up() const {
325 11217 : return local_state() == kUp;
326 : }
327 :
328 : } // namespace BFD
|