1
#include "cilium/socket_option_source_address.h"
2

            
3
#include <sys/socket.h>
4

            
5
#include <cstdint>
6
#include <memory>
7
#include <utility>
8
#include <vector>
9

            
10
#include "envoy/api/os_sys_calls_common.h"
11
#include "envoy/config/core/v3/socket_option.pb.h"
12
#include "envoy/network/address.h"
13
#include "envoy/network/socket.h"
14

            
15
#include "source/common/common/hex.h"
16
#include "source/common/common/logger.h"
17
#include "source/common/common/utility.h"
18

            
19
#include "absl/numeric/int128.h"
20
#include "cilium/filter_state_cilium_destination.h"
21
#include "cilium/filter_state_cilium_policy.h"
22

            
23
namespace Envoy {
24
namespace Cilium {
25

            
26
SourceAddressSocketOption::SourceAddressSocketOption(
27
    uint32_t source_identity, int linger_time,
28
    Network::Address::InstanceConstSharedPtr original_source_address,
29
    Network::Address::InstanceConstSharedPtr ipv4_source_address,
30
    Network::Address::InstanceConstSharedPtr ipv6_source_address,
31
    std::shared_ptr<CiliumDestinationFilterState> dest_fs,
32
    std::shared_ptr<CiliumPolicyFilterState> policy_fs)
33
125
    : source_identity_(source_identity), linger_time_(linger_time),
34
125
      original_source_address_(std::move(original_source_address)),
35
125
      ipv4_source_address_(std::move(ipv4_source_address)),
36
125
      ipv6_source_address_(std::move(ipv6_source_address)), dest_fs_(std::move(dest_fs)),
37
125
      policy_fs_(std::move(policy_fs)) {
38
125
  ENVOY_LOG(debug,
39
125
            "Cilium SourceAddressSocketOption(): source_identity: {}, linger_time: {}, "
40
125
            "source_addresses: {}/{}/{}",
41
125
            source_identity_, linger_time_,
42
125
            original_source_address_ ? original_source_address_->asString() : "",
43
125
            ipv4_source_address_ ? ipv4_source_address_->asString() : "",
44
125
            ipv6_source_address_ ? ipv6_source_address_->asString() : "");
45
125
}
46

            
47
bool SourceAddressSocketOption::setOption(
48
107
    Network::Socket& socket, envoy::config::core::v3::SocketOption::SocketState state) const {
49
  // Only set the option once per socket
50
107
  if (state != envoy::config::core::v3::SocketOption::STATE_PREBIND) {
51
    ENVOY_LOG(trace, "Skipping setting socket ({}) source address, state != STATE_PREBIND",
52
              socket.ioHandle().fdDoNotUse());
53
    return true;
54
  }
55

            
56
107
  auto ip_version = socket.ipVersion();
57
107
  if (!ip_version.has_value()) {
58
    ENVOY_LOG(critical, "Socket address family is not available, can not choose source address");
59
    return false;
60
  }
61

            
62
107
  Network::Address::InstanceConstSharedPtr source_address = original_source_address_;
63

            
64
  // Do not use source address of wrong IP version
65
107
  if (source_address && source_address->ip() && source_address->ip()->version() != *ip_version) {
66
    source_address = nullptr;
67
  }
68

            
69
107
  if (!source_address && (ipv4_source_address_ || ipv6_source_address_)) {
70
    // Select source address based on the socket address family
71
    source_address = ipv6_source_address_;
72
    if (*ip_version == Network::Address::IpVersion::v4) {
73
      source_address = ipv4_source_address_;
74
    }
75
  }
76

            
77
107
  if (!source_address) {
78
107
    ENVOY_LOG(trace, "Skipping restore of local address on socket: {} - no source address",
79
107
              socket.ioHandle().fdDoNotUse());
80
107
    return true;
81
107
  }
82

            
83
  if (source_address->ip() && dest_fs_ && dest_fs_->getDestinationAddress() &&
84
      dest_fs_->getDestinationAddress()->ip()) {
85
    const auto& dst_addr = dest_fs_->getDestinationAddress()->ip()->addressAsString();
86
    if (source_address->ip()->addressAsString() == dst_addr) {
87
      ENVOY_LOG(trace,
88
                "Skipping restore of local address on socket: {} - source address is same as "
89
                "destination address {}",
90
                socket.ioHandle().fdDoNotUse(), dst_addr);
91
      return true;
92
    }
93
    // Also skip using the original source address if destination is local.
94
    // Local destinations have a policy configured on their address.
95
    if (policy_fs_ && policy_fs_->exists(dst_addr)) {
96
      ENVOY_LOG(trace, "Skipping restore of local address on socket: {} - destination is local {}",
97
                socket.ioHandle().fdDoNotUse(), dst_addr);
98
      return true;
99
    }
100
  }
101

            
102
  // Note: SO_LINGER option is set on the socket of the upstream connection.
103
  if (source_address->ip() != nullptr && source_address->ip()->port() != 0 && linger_time_ >= 0) {
104
    struct linger linger;
105
    linger.l_onoff = 1;
106
    linger.l_linger = linger_time_;
107

            
108
    ENVOY_LOG(trace, "Setting SO_LINGER option on socket {} to {} seconds",
109
              socket.ioHandle().fdDoNotUse(), linger_time_);
110

            
111
    const Api::SysCallIntResult result =
112
        socket.setSocketOption(SOL_SOCKET, SO_LINGER, &linger, sizeof(linger));
113

            
114
    if (result.return_value_ != 0) {
115
      ENVOY_LOG(error, "Setting SO_LINGER option on socket {} failed: {}",
116
                socket.ioHandle().fdDoNotUse(), errorDetails(result.errno_));
117
      return false;
118
    }
119
  }
120

            
121
  // Note: Restoration of the original source address happens on the socket of the upstream
122
  // connection.
123
  ENVOY_LOG(trace, "Restoring local address (original source) on socket: {} ({} -> {})",
124
            socket.ioHandle().fdDoNotUse(),
125
            socket.connectionInfoProvider().localAddress()
126
                ? socket.connectionInfoProvider().localAddress()->asString()
127
                : "n/a",
128
            source_address->asString());
129

            
130
  socket.connectionInfoProvider().setLocalAddress(std::move(source_address));
131

            
132
  return true;
133
}
134

            
135
template <typename T> void addressIntoVector(std::vector<uint8_t>& vec, const T& address) {
136
  const uint8_t* byte_array = reinterpret_cast<const uint8_t*>(&address);
137
  vec.insert(vec.end(), byte_array, byte_array + sizeof(T));
138
}
139

            
140
98
void SourceAddressSocketOption::hashKey(std::vector<uint8_t>& key) const {
141
  // Source address is more specific than policy ID. If using an original
142
  // source address, we do not need to also add the source security ID to the
143
  // hash key. Note that since the identity is 3 bytes it will not collide
144
  // with neither an IPv4 nor IPv6 address.
145
98
  if (original_source_address_) {
146
    const auto& ip = original_source_address_->ip();
147
    uint16_t port = ip->port();
148
    if (ip->version() == Network::Address::IpVersion::v4) {
149
      uint32_t raw_address = ip->ipv4()->address();
150
      addressIntoVector(key, raw_address);
151
    } else if (ip->version() == Network::Address::IpVersion::v6) {
152
      absl::uint128 raw_address = ip->ipv6()->address();
153
      addressIntoVector(key, raw_address);
154
    }
155
    // Add source port to the hash key if defined
156
    if (port != 0) {
157
      ENVOY_LOG(trace, "hashKey port: {:x}", port);
158
      key.emplace_back(uint8_t(port >> 8));
159
      key.emplace_back(uint8_t(port));
160
    }
161
    ENVOY_LOG(trace, "hashKey after with original source address: {}, original_source_address: {}",
162
              Hex::encode(key), original_source_address_->asString());
163
98
  } else {
164
    // Add the source identity to the hash key. This will separate upstream
165
    // connection pools per security ID.
166
98
    key.emplace_back(uint8_t(source_identity_ >> 16));
167
98
    key.emplace_back(uint8_t(source_identity_ >> 8));
168
98
    key.emplace_back(uint8_t(source_identity_));
169
98
    ENVOY_LOG(trace, "hashKey with source identity: {}, source_identity: {}", Hex::encode(key),
170
98
              source_identity_);
171
98
  }
172
98
}
173

            
174
} // namespace Cilium
175
} // namespace Envoy