E2SAR 0.2.0
Loading...
Searching...
No Matches
e2sarUtil.hpp
1#ifndef E2SARUTILHPP
2#define E2SARUTILHPP
3
4#include <fstream>
5#include <vector>
6#include <boost/url.hpp>
7#include <boost/algorithm/string.hpp>
8#include <boost/asio.hpp>
9#include <boost/chrono.hpp>
10#include <boost/thread.hpp>
11
12#include "e2sarError.hpp"
13
14using namespace boost::asio;
15using namespace std::string_literals;
16
17/***
18 * Supporting classes for E2SAR
19 */
20namespace e2sar
21{
22 const u_int16_t DATAPLANE_PORT = 19522;
23
31 {
32 public:
33 enum class TokenType {
34 admin, instance, session
35 };
36
37 private:
38 std::string rawURI;
40 bool haveDatav4;
41 bool haveDatav6;
43 bool haveSync;
45 bool useTls;
47 bool preferV6;
48
50 u_int16_t syncPort;
52 u_int16_t cpPort;
54 u_int16_t dataPort;
55
57 std::string lbName;
59 std::string lbId;
61 std::string adminToken;
63 std::string instanceToken;
65 std::string sessionToken;
67 std::string sessionId;
68
70 ip::address dataAddrv4;
71 ip::address dataAddrv6;
73 ip::address syncAddr;
75 ip::address cpAddr;
76 std::string cpHost;
77
78 public:
85 EjfatURI(const std::string &uri, TokenType tt=TokenType::admin, bool preferV6=false);
86
89
90 friend bool operator== (const EjfatURI &u1, const EjfatURI &u2);
91
92 friend inline bool operator!= (const EjfatURI &u1, const EjfatURI &u2)
93 {
94 return !(u1 == u2);
95 }
96
98 inline bool get_useTls() const
99 {
100 return useTls;
101 }
102
104 inline void set_InstanceToken(const std::string &t)
105 {
106 instanceToken = t;
107 }
108
110 inline void set_SessionToken(const std::string &t)
111 {
112 sessionToken = t;
113 }
114
116 inline const result<std::string> get_InstanceToken() const
117 {
118 if (!instanceToken.empty())
119 return instanceToken;
120 else
121 return E2SARErrorInfo{E2SARErrorc::ParameterNotAvailable, "Instance token not available"s};
122 }
123
125 inline const result<std::string> get_SessionToken() const
126 {
127 if (!sessionToken.empty())
128 return sessionToken;
129 else
130 return E2SARErrorInfo{E2SARErrorc::ParameterNotAvailable, "Session token not available"s};
131 }
132
134 inline const result<std::string> get_AdminToken() const
135 {
136 if (!adminToken.empty())
137 return adminToken;
138 else
139 return E2SARErrorInfo{E2SARErrorc::ParameterNotAvailable, "Admin token not available"s};
140 }
141
143 inline void set_lbName(const std::string &n)
144 {
145 lbName = n;
146 }
147
149 inline void set_lbId(const std::string &i)
150 {
151 lbId = i;
152 }
153
155 inline void set_sessionId(const std::string &i)
156 {
157 sessionId = i;
158 }
159
163 inline void set_syncAddr(const std::pair<ip::address, u_int16_t> &a)
164 {
165 syncAddr = a.first;
166 syncPort = a.second;
167 haveSync = true;
168 }
169
173 inline void set_dataAddr(const std::pair<ip::address, u_int16_t> &a)
174 {
175 if (a.first.is_v4()) {
176 dataAddrv4 = a.first;
177 haveDatav4 = true;
178 }
179 else {
180 dataAddrv6 = a.first;
181 haveDatav6 = true;
182 }
183 }
184
186 inline const std::string get_lbName() const
187 {
188 return lbName;
189 }
190
192 inline const std::string get_lbId() const
193 {
194 return lbId;
195 }
196
198 inline const std::string get_sessionId() const
199 {
200 return sessionId;
201 }
202
204 inline const result<std::pair<ip::address, u_int16_t>> get_cpAddr() const
205 {
206 return std::pair<ip::address, u_int16_t>(cpAddr, cpPort);
207 }
208
210 inline const result<std::pair<std::string, u_int16_t>> get_cpHost() const
211 {
212 if (!cpHost.empty())
213 return std::pair<std::string, u_int16_t>(cpHost, cpPort);
214 else
215 return E2SARErrorInfo{E2SARErrorc::ParameterNotAvailable, "Control plane hostname not available"s};
216 }
217
219 inline const bool has_dataAddrv4() const
220 {
221 return haveDatav4;
222 }
223
225 inline const bool has_dataAddrv6() const
226 {
227 return haveDatav6;
228 }
229
231 inline const bool has_dataAddr() const
232 {
233 return haveDatav4 || haveDatav6;
234 }
235
237 inline const bool has_syncAddr() const
238 {
239 return haveSync;
240 }
241
243 inline const result<std::pair<ip::address, u_int16_t>> get_dataAddrv4() const noexcept
244 {
245 if (haveDatav4)
246 return std::pair<ip::address, u_int16_t>(dataAddrv4, dataPort);
247 return E2SARErrorInfo{E2SARErrorc::ParameterNotAvailable, "Data plane address not available"s};
248 }
249
251 inline const result<std::pair<ip::address, u_int16_t>> get_dataAddrv6() const noexcept
252 {
253 if (haveDatav6)
254 return std::pair<ip::address, u_int16_t>(dataAddrv6, dataPort);
255 return E2SARErrorInfo{E2SARErrorc::ParameterNotAvailable, "Data plane address not available"s};
256 }
257
259 inline const result<std::pair<ip::address, u_int16_t>> get_syncAddr() const noexcept
260 {
261 if (haveSync)
262 return std::pair<ip::address, u_int16_t>(syncAddr, syncPort);
263 return E2SARErrorInfo{E2SARErrorc::ParameterNotAvailable, "Sync address not available"s};
264 }
269 operator std::string() const;
270
271 const std::string to_string(TokenType tt = TokenType::admin) const;
272
278 static inline result<EjfatURI> getFromEnv(const std::string &envVar = "EJFAT_URI"s,
279 TokenType tt=TokenType::admin, bool preferV6=false) noexcept
280 {
281 const char *envStr = std::getenv(envVar.c_str());
282 if (envStr != nullptr)
283 {
284 try
285 {
286 return EjfatURI(envStr, tt, preferV6);
287 }
288 catch (const E2SARException &e)
289 {
290 return E2SARErrorInfo{E2SARErrorc::CaughtException, "Unable to parse EJFAT_URI from environment variable: "s + static_cast<std::string>(e)};
291 }
292 }
293 return E2SARErrorInfo{E2SARErrorc::Undefined, "Environment variable "s + envVar + " not defined."s};
294 }
295
301 static inline result<EjfatURI> getFromString(const std::string &uriStr,
302 TokenType tt=TokenType::admin, bool preferV6=false) noexcept
303 {
304 try
305 {
306 return EjfatURI(uriStr, tt, preferV6);
307 }
308 catch (const E2SARException &e)
309 {
310 return E2SARErrorInfo{E2SARErrorc::CaughtException, "Unable to parse URI from string: "s + static_cast<std::string>(e)};
311 }
312 }
313
319 static inline result<EjfatURI> getFromFile(const std::string &fileName = "/tmp/ejfat_uri"s,
320 TokenType tt=TokenType::admin, bool preferV6=false) noexcept
321 {
322 if (!fileName.empty())
323 {
324 std::ifstream file(fileName);
325 if (file.is_open())
326 {
327 std::string uriLine;
328 if (std::getline(file, uriLine))
329 {
330 file.close();
331 try
332 {
333 return EjfatURI(uriLine, tt, preferV6);
334 }
335 catch (const E2SARException &e)
336 {
337 return E2SARErrorInfo{E2SARErrorc::CaughtException, "Unable to parse URI: "s + static_cast<std::string>(e)};
338 }
339 }
340 file.close();
341 return E2SARErrorInfo{E2SARErrorc::Undefined, "Unable to parse URI."s};
342 }
343 }
344 return E2SARErrorInfo{E2SARErrorc::NotFound, "Unable to find file "s + fileName};
345 }
346
352 result<std::vector<ip::address>> getDataplaneLocalAddresses(bool v6=false) noexcept;
353 };
354
358 static inline const result<ip::address> string_to_ip(const std::string &addr) noexcept
359 {
360 try
361 {
362 if (addr[0] == '[')
363 {
364 // strip '[]' from IPv6
365 try
366 {
367 ip::make_address(addr.substr(1, addr.length() - 2));
368 }
369 catch (...)
370 {
371 ;
372 }
373 return ip::make_address(addr.substr(1, addr.length() - 2));
374 }
375 else
376 return ip::make_address(addr);
377 }
378 catch (boost::system::system_error &e)
379 {
380 return E2SARErrorInfo{E2SARErrorc::ParameterError, "Unable to convert IP address from "s + addr};
381 }
382 }
383
387 static inline const result<u_int16_t> string_to_port(const std::string &port_string) noexcept
388 {
389 try
390 {
391 u_int16_t port = std::stoi(port_string);
392 if (port < 1024 || port > 65535)
393 {
394 // port is out of range
395 return E2SARErrorInfo{E2SARErrorc::OutOfRange, "Port value "s + port_string + " is out of range"s};
396 }
397 return port;
398 }
399 catch (const std::exception &e)
400 {
401 return E2SARErrorInfo{E2SARErrorc::ParameterError, "Unable to convert "s + port_string + " to integer"s};
402 }
403 }
404
408 static inline const result<std::pair<ip::address, u_int16_t>> string_tuple_to_ip_and_port(const std::string &t) noexcept
409 {
410 // search for last ":" (ip:port) or "]" (ipv6 by itself) whichever comes last
411 auto const pos = t.find_last_of("]:");
412
413 // IPv4 or IPv6 by itself
414 if ((pos == std::string::npos) || (t[pos] == ']'))
415 {
416 auto r1 = string_to_ip(t);
417 if (r1)
418 return std::pair<ip::address, u_int16_t>(r1.value(), 0);
419 else
420 return E2SARErrorInfo{E2SARErrorc::ParameterError, "Unable to convert "s + t + " to ip address and port"s};
421 }
422
423 // port with either IPv4 or IPv6 address x.x.x.x:num or [x:x:x:x::y]:num
424 auto r1 = string_to_ip(t.substr(0, pos));
425 auto r2 = string_to_port(t.substr(pos + 1));
426 if (r1 && r2)
427 return std::pair<ip::address, int>(r1.value(), r2.value());
428 return E2SARErrorInfo{E2SARErrorc::ParameterError, "Unable to convert "s + t + " to ip address and port"s};
429 }
430
437 static inline result<std::vector<ip::address>> resolveHost(const std::string &host_name) noexcept
438 {
439
440 std::vector<ip::address> addresses;
441 boost::asio::io_service io_service;
442
443 try
444 {
445 ip::udp::resolver resolver(io_service);
446 ip::udp::resolver::query query(host_name, "443");
447 ip::udp::resolver::iterator iter = resolver.resolve(query);
448 ip::udp::resolver::iterator end; // End marker.
449
450 while (iter != end)
451 {
452 ip::udp::endpoint endpoint = *iter++;
453 addresses.push_back(endpoint.address());
454 }
455 return addresses;
456 }
457 catch (...)
458 {
459 // anything happens - we can't find the host
460 return E2SARErrorInfo{E2SARErrorc::NotFound, "Unable to convert "s + host_name + " to ip address"s};
461 }
462 }
463
464 // to support unordered maps of pairs
465 struct pair_hash {
466 std::size_t operator()(const std::pair<u_int64_t, u_int16_t>& p) const {
467 u_int64_t hash1 = p.first;
468 u_int64_t tmp = p.second;
469 u_int64_t hash2 = tmp | tmp << 16 | tmp << 32 | tmp << 48;
470 return hash1 ^ hash2; // Combine the two hashes
471 }
472 };
473
474 struct pair_equal {
475 bool operator()(const std::pair<u_int64_t, u_int16_t>& lhs, const std::pair<u_int64_t, u_int16_t>& rhs) const {
476 return lhs.first == rhs.first && lhs.second == rhs.second;
477 }
478 };
479
488 static inline float clockEntropyTest(int totalTests = 5000, int sleepMs = 1)
489 {
490 std::vector<int_least64_t> points;
491 std::vector<int> bins(256, 0);
492
493 for (int i = 0; i < totalTests; i++)
494 {
495 auto now = boost::chrono::system_clock::now();
496 auto nowUsec = boost::chrono::duration_cast<boost::chrono::microseconds>(now.time_since_epoch()).count();
497 bins[nowUsec & 0xff]++;
498 auto until = now + boost::chrono::milliseconds(sleepMs);
499 boost::this_thread::sleep_until(until);
500 }
501
502 // compute the probabilities and entropy
503 float entropy{0.0};
504 for (size_t i = 0; i < bins.size(); i++)
505 {
506 float prob = static_cast<float>(bins[i])/(totalTests*1.0);
507 entropy += prob * std::log(prob);
508 }
509
510 // normalize into bits
511 entropy *= -1.0/std::log(2);
512 return entropy;
513 }
514
515 template<typename Container>
516 std::string concatWithSeparator(const Container& c, const std::string& sep=","s)
517 {
518 typename Container::const_iterator it = c.begin();
519 std::string rets{};
520 while(it != c.end())
521 {
522 rets += *it;
523 if (++it != c.end())
524 rets += sep;
525 }
526 return rets;
527 }
528
529 // busy wait for a given number of microseconds using high_resolution_clock timepoints
530 inline void busyWaitUsecs(const boost::chrono::steady_clock::time_point &tp, int64_t usecs)
531 {
532 while(true)
533 {
534 // busy wait checking the clock
535 if (boost::chrono::duration_cast<boost::chrono::microseconds>(boost::chrono::high_resolution_clock::now()
536 - tp).count() > usecs)
537 break;
538 }
539 }
540
541 using OptimizationsWord = u_int16_t;
547 public:
548 // go as powers of 2 to make calculations easier
549 enum class Code {
550 none = 0,
551 sendmmsg = 1,
552 liburing_send = 2,
553 liburing_recv = 3,
554 // always last
555 unknown = 15
556 };
557 public:
558 inline static OptimizationsWord toWord(Code o)
559 {
560 return 1 << static_cast<int>(o);
561 }
562 inline static std::string toString(Code o)
563 {
564 switch(o)
565 {
566 case Code::none: return "none"s;
567 case Code::sendmmsg: return "sendmmsg";
568 case Code::liburing_recv: return "liburing_recv";
569 case Code::liburing_send: return "liburing_send";
570 default: "unknown"s;
571 }
572 return "unknown"s;
573 }
574 inline static Code fromString(const std::string& opt)
575 {
576 if (opt == "none"s)
577 return Code::none;
578 else if (opt == "sendmmsg"s)
579 return Code::sendmmsg;
580 else if (opt == "liburing_recv"s)
581 return Code::liburing_recv;
582 else if (opt == "liburing_send"s)
583 return Code::liburing_send;
584 return Code::unknown;
585 }
589 const static std::vector<std::string> availableAsStrings() noexcept;
590
594 const static OptimizationsWord availableAsWord() noexcept;
595
600 static result<int> select(std::vector<std::string>& opt) noexcept;
601
606 static result <int> select(std::vector<Code> &opt) noexcept;
607
611 const static std::vector<std::string> selectedAsStrings() noexcept;
612
616 const static OptimizationsWord selectedAsWord() noexcept;
617
621 const static std::vector<Code> selectedAsList() noexcept;
622
626 const static bool isSelected(Code o) noexcept;
627 private:
628 // all available compiled in optimizations
629 static const std::vector<Optimizations::Code> available;
630
631 // this is where we store selected optimizations;
632 OptimizationsWord selected_optimizations;
633
634 // c-tor is private
635 Optimizations():selected_optimizations{toWord(Code::none)}
636 {}
637 // d-tor doesn't exist
638 ~Optimizations() = delete;
639
640 static Optimizations* instance;
641 static inline Optimizations* _get()
642 {
643 if (not instance)
644 instance = new Optimizations();
645 return instance;
646 }
647 };
648};
649#endif
Definition e2sarError.hpp:61
Definition e2sarUtil.hpp:31
EjfatURI(const std::string &uri, TokenType tt=TokenType::admin, bool preferV6=false)
Definition e2sarUtil.cpp:188
const result< std::pair< ip::address, u_int16_t > > get_syncAddr() const noexcept
Definition e2sarUtil.hpp:259
const result< std::string > get_SessionToken() const
Definition e2sarUtil.hpp:125
void set_lbName(const std::string &n)
Definition e2sarUtil.hpp:143
~EjfatURI()
Definition e2sarUtil.hpp:88
bool get_useTls() const
Definition e2sarUtil.hpp:98
const bool has_dataAddrv4() const
Definition e2sarUtil.hpp:219
void set_InstanceToken(const std::string &t)
Definition e2sarUtil.hpp:104
const result< std::pair< ip::address, u_int16_t > > get_dataAddrv6() const noexcept
Definition e2sarUtil.hpp:251
const std::string get_lbId() const
Definition e2sarUtil.hpp:192
const result< std::pair< std::string, u_int16_t > > get_cpHost() const
Definition e2sarUtil.hpp:210
static result< EjfatURI > getFromEnv(const std::string &envVar="EJFAT_URI"s, TokenType tt=TokenType::admin, bool preferV6=false) noexcept
Definition e2sarUtil.hpp:278
const bool has_syncAddr() const
Definition e2sarUtil.hpp:237
void set_sessionId(const std::string &i)
Definition e2sarUtil.hpp:155
const result< std::string > get_AdminToken() const
Definition e2sarUtil.hpp:134
const result< std::pair< ip::address, u_int16_t > > get_cpAddr() const
Definition e2sarUtil.hpp:204
const std::string get_sessionId() const
Definition e2sarUtil.hpp:198
void set_dataAddr(const std::pair< ip::address, u_int16_t > &a)
Definition e2sarUtil.hpp:173
const result< std::string > get_InstanceToken() const
Definition e2sarUtil.hpp:116
static result< EjfatURI > getFromFile(const std::string &fileName="/tmp/ejfat_uri"s, TokenType tt=TokenType::admin, bool preferV6=false) noexcept
Definition e2sarUtil.hpp:319
void set_SessionToken(const std::string &t)
Definition e2sarUtil.hpp:110
const bool has_dataAddrv6() const
Definition e2sarUtil.hpp:225
static result< EjfatURI > getFromString(const std::string &uriStr, TokenType tt=TokenType::admin, bool preferV6=false) noexcept
Definition e2sarUtil.hpp:301
result< std::vector< ip::address > > getDataplaneLocalAddresses(bool v6=false) noexcept
Definition e2sarUtil.cpp:399
const bool has_dataAddr() const
Definition e2sarUtil.hpp:231
const result< std::pair< ip::address, u_int16_t > > get_dataAddrv4() const noexcept
Definition e2sarUtil.hpp:243
void set_syncAddr(const std::pair< ip::address, u_int16_t > &a)
Definition e2sarUtil.hpp:163
const std::string get_lbName() const
Definition e2sarUtil.hpp:186
void set_lbId(const std::string &i)
Definition e2sarUtil.hpp:149
Definition e2sarUtil.hpp:546
Definition e2sar.hpp:11
Definition e2sarError.hpp:41
Definition e2sarUtil.hpp:474
Definition e2sarUtil.hpp:465