1
#include "cilium/uds_client.h"
2

            
3
#include <fmt/format.h>
4
#include <sys/socket.h>
5
#include <sys/types.h>
6
#include <unistd.h>
7

            
8
#include <cerrno>
9
#include <memory>
10
#include <string>
11

            
12
#include "envoy/common/exception.h"
13
#include "envoy/common/time.h"
14

            
15
#include "source/common/common/lock_guard.h"
16
#include "source/common/common/logger.h"
17
#include "source/common/common/token_bucket_impl.h"
18
#include "source/common/common/utility.h"
19
#include "source/common/network/address_impl.h"
20

            
21
namespace Envoy {
22
namespace Cilium {
23

            
24
UDSClient::UDSClient(const std::string& path, TimeSource& time_source)
25
87
    : addr_(THROW_OR_RETURN_VALUE(Network::Address::PipeInstance::create(path),
26
87
                                  std::unique_ptr<Network::Address::PipeInstance>)),
27
87
      fd_(-1), errno_(0), logging_limiter_(std::make_unique<TokenBucketImpl>(10, time_source)) {
28
87
  if (path.length() == 0) {
29
    throw EnvoyException(fmt::format("cilium: Invalid Unix domain socket path: {}", path));
30
  }
31

            
32
87
  fd_ = -1;
33
87
  errno_ = 0;
34
87
  logging_limiter_ = std::make_unique<TokenBucketImpl>(10, time_source);
35
87
}
36

            
37
87
UDSClient::~UDSClient() {
38
87
  fd_mutex_.lock();
39
87
  if (fd_ != -1) {
40
86
    ::close(fd_);
41
86
    fd_ = -1;
42
86
  }
43
87
  fd_mutex_.unlock();
44
87
}
45

            
46
210
void UDSClient::log(const std::string& msg) {
47
210
  {
48
210
    int tries = 2;
49
210
    ssize_t length = msg.length();
50

            
51
210
    Thread::LockGuard guard(fd_mutex_);
52
210
    while (tries-- > 0) {
53
210
      if (!tryConnect()) {
54
        continue; // retry
55
      }
56

            
57
210
      ssize_t sent = ::send(fd_, msg.data(), length, MSG_DONTWAIT | MSG_EOR | MSG_NOSIGNAL);
58
210
      if (sent == -1) {
59
        errno_ = errno;
60
        continue;
61
      }
62

            
63
210
      if (sent < length) {
64
        ENVOY_LOG(debug, "Cilium access log send truncated by {} bytes.", length - sent);
65
      }
66
210
      return;
67
210
    }
68
210
  }
69

            
70
  // rate-limit to 1/second to avoid spamming the logs
71
  fd_mutex_.lock();
72
  if (logging_limiter_->consume(1, false)) {
73
    ENVOY_LOG(warn, "Logging to {} failed: {}", asStringView(), Envoy::errorDetails(errno_));
74
  }
75
  fd_mutex_.unlock();
76
}
77

            
78
210
bool UDSClient::tryConnect() {
79
210
  if (fd_ != -1) {
80
124
    if (errno_ == 0) {
81
124
      return true;
82
124
    }
83
    ENVOY_LOG(debug, "Cilium access log resetting socket due to error: {}",
84
              Envoy::errorDetails(errno_));
85
    ::close(fd_);
86
    fd_ = -1;
87
  }
88

            
89
86
  errno_ = 0;
90
86
  fd_ = ::socket(AF_UNIX, SOCK_SEQPACKET, 0);
91
86
  if (fd_ == -1) {
92
    errno_ = errno;
93
    ENVOY_LOG(error, "Can't create socket: {}", Envoy::errorDetails(errno_));
94
    return false;
95
  }
96

            
97
86
  if (::connect(fd_, addr_->sockAddr(), addr_->sockAddrLen()) == -1) {
98
    errno_ = errno;
99
    ::close(fd_);
100
    fd_ = -1;
101
    return false;
102
  }
103

            
104
86
  return true;
105
86
}
106

            
107
} // namespace Cilium
108
} // namespace Envoy