1
#pragma once
2

            
3
#include <chrono>
4
#include <cstddef>
5
#include <memory>
6
#include <string>
7
#include <utility>
8
#include <vector>
9

            
10
#include "envoy/api/io_error.h"
11
#include "envoy/common/random_generator.h"
12
#include "envoy/network/address.h"
13
#include "envoy/network/filter.h"
14
#include "envoy/network/listener_filter_buffer.h"
15
#include "envoy/server/factory_context.h"
16

            
17
#include "source/common/common/logger.h"
18

            
19
#include "absl/strings/string_view.h"
20
#include "absl/types/optional.h"
21
#include "cilium/api/bpf_metadata.pb.h"
22
#include "cilium/conntrack.h"
23
#include "cilium/filter_state_cilium_destination.h"
24
#include "cilium/filter_state_cilium_policy.h"
25
#include "cilium/host_map.h"
26
#include "cilium/ipcache.h"
27
#include "cilium/network_policy.h"
28
#include "cilium/socket_option_cilium_mark.h"
29
#include "cilium/socket_option_source_address.h"
30

            
31
namespace Envoy {
32
namespace Cilium {
33
namespace BpfMetadata {
34

            
35
#define DEFAULT_CACHE_ENTRY_TTL_MS 3
36

            
37
struct SocketMetadata : public Logger::Loggable<Logger::Id::filter> {
38
  SocketMetadata(uint32_t mark, uint32_t ingress_source_identity, uint32_t source_identity,
39
                 bool ingress, bool l7lb, uint16_t port, std::string&& pod_ip,
40
                 std::string&& ingress_policy_name,
41
                 Network::Address::InstanceConstSharedPtr original_source_address,
42
                 Network::Address::InstanceConstSharedPtr source_address_ipv4,
43
                 Network::Address::InstanceConstSharedPtr source_address_ipv6,
44
                 Network::Address::InstanceConstSharedPtr original_dest_address,
45
                 const PolicyResolverSharedPtr& policy_resolver, uint32_t proxy_id,
46
                 std::string&& proxylib_l7_proto, absl::string_view sni)
47
128
      : ingress_source_identity_(ingress_source_identity), source_identity_(source_identity),
48
128
        ingress_(ingress), is_l7lb_(l7lb), port_(port), pod_ip_(std::move(pod_ip)),
49
128
        ingress_policy_name_(std::move(ingress_policy_name)), proxy_id_(proxy_id),
50
128
        proxylib_l7_proto_(std::move(proxylib_l7_proto)), sni_(sni),
51
128
        policy_resolver_(policy_resolver), mark_(mark),
52
128
        original_source_address_(std::move(original_source_address)),
53
128
        source_address_ipv4_(std::move(source_address_ipv4)),
54
128
        source_address_ipv6_(std::move(source_address_ipv6)),
55
128
        original_dest_address_(std::move(original_dest_address)) {}
56

            
57
125
  std::shared_ptr<Envoy::Cilium::CiliumPolicyFilterState> buildCiliumPolicyFilterState() {
58
125
    return std::make_shared<Envoy::Cilium::CiliumPolicyFilterState>(
59
125
        ingress_source_identity_, source_identity_, ingress_, is_l7lb_, port_, std::move(pod_ip_),
60
125
        std::move(ingress_policy_name_), policy_resolver_, proxy_id_, sni_);
61
125
  };
62

            
63
119
  std::shared_ptr<Envoy::Cilium::CiliumDestinationFilterState> buildCiliumDestinationFilterState() {
64
119
    return std::make_shared<Envoy::Cilium::CiliumDestinationFilterState>(nullptr);
65
119
  };
66

            
67
6
  std::shared_ptr<Envoy::Cilium::CiliumMarkSocketOption> buildCiliumMarkSocketOption() {
68
6
    return std::make_shared<Envoy::Cilium::CiliumMarkSocketOption>(mark_);
69
6
  };
70

            
71
  std::shared_ptr<Envoy::Cilium::SourceAddressSocketOption> buildSourceAddressSocketOption(
72
      int linger_time, const std::shared_ptr<CiliumDestinationFilterState>& dest_fs = nullptr,
73
125
      const std::shared_ptr<CiliumPolicyFilterState>& policy_fs = nullptr) {
74
125
    return std::make_shared<Envoy::Cilium::SourceAddressSocketOption>(
75
125
        source_identity_, linger_time, original_source_address_, source_address_ipv4_,
76
125
        source_address_ipv6_, dest_fs, policy_fs);
77
125
  };
78

            
79
  // Add ProxyLib L7 protocol as requested application protocol on the socket.
80
121
  void configureProxyLibApplicationProtocol(Network::ConnectionSocket& socket) {
81
121
    if (!proxylib_l7_proto_.empty()) {
82
19
      const auto& old_protocols = socket.requestedApplicationProtocols();
83
19
      std::vector<absl::string_view> protocols;
84
19
      protocols.reserve(old_protocols.size());
85
19
      for (const auto& old_protocol : old_protocols) {
86
1
        protocols.emplace_back(old_protocol);
87
1
      }
88
19
      protocols.emplace_back(proxylib_l7_proto_);
89
19
      socket.setRequestedApplicationProtocols(protocols);
90
19
      ENVOY_LOG(info, "cilium.bpf_metadata: setRequestedApplicationProtocols(..., {})",
91
19
                proxylib_l7_proto_);
92
19
    }
93
121
  }
94

            
95
120
  void configureOriginalDstAddress(Network::ConnectionSocket& socket) {
96
120
    if (!original_dest_address_) {
97
      return;
98
    }
99

            
100
120
    if (*original_dest_address_ == *socket.connectionInfoProvider().localAddress()) {
101
      // Only set the local address if it really changed, and mark it as address being restored.
102
      return;
103
    }
104

            
105
    // Restoration of the original destination address lets the OriginalDstCluster know the
106
    // destination address that can be used.
107
120
    ENVOY_LOG(trace, "Restoring local address (original destination) on socket {} ({} -> {})",
108
120
              socket.ioHandle().fdDoNotUse(),
109
120
              socket.connectionInfoProvider().localAddress()->asString(),
110
120
              original_dest_address_->asString());
111

            
112
120
    socket.connectionInfoProvider().restoreLocalAddress(original_dest_address_);
113
120
  }
114

            
115
  uint32_t ingress_source_identity_;
116
  uint32_t source_identity_;
117
  bool ingress_;
118
  bool is_l7lb_;
119
  uint16_t port_;
120
  std::string pod_ip_; // pod policy to enforce, if any; empty only when there is no local pod (i.e.
121
                       // north/south l7lb)
122
  std::string ingress_policy_name_; // Ingress policy to enforce, if any
123
  uint32_t proxy_id_;
124
  std::string proxylib_l7_proto_;
125
  std::string sni_;
126
  const PolicyResolverSharedPtr policy_resolver_;
127

            
128
  uint32_t mark_;
129

            
130
  Network::Address::InstanceConstSharedPtr original_source_address_;
131
  Network::Address::InstanceConstSharedPtr source_address_ipv4_;
132
  Network::Address::InstanceConstSharedPtr source_address_ipv6_;
133
  Network::Address::InstanceConstSharedPtr original_dest_address_;
134
};
135

            
136
/**
137
 * Global configuration for Bpf Metadata listener filter. This
138
 * represents all global state shared among the working thread
139
 * instances of the filter.
140
 */
141
class Config : public Cilium::PolicyResolver,
142
               public std::enable_shared_from_this<Config>,
143
               Logger::Loggable<Logger::Id::config> {
144
public:
145
  Config(const ::cilium::BpfMetadata& config,
146
         Server::Configuration::ListenerFactoryContext& context);
147
126
  ~Config() override = default;
148

            
149
  // PolicyResolver
150
  uint32_t resolvePolicyId(const Network::Address::Ip*) const override;
151
  const PolicyInstance& getPolicy(const std::string&) const override;
152
  bool exists(const std::string&) const override;
153

            
154
  virtual absl::optional<SocketMetadata> extractSocketMetadata(Network::ConnectionSocket& socket);
155

            
156
  // Possibility to prevent socket options that require
157
  // NET_ADMIN privileges from being applied. Used by tests.
158
  virtual bool addPrivilegedSocketOptions() { return true; };
159

            
160
  int so_linger_; // negative if disabled
161
  uint32_t proxy_id_;
162
  bool is_ingress_;
163
  bool use_original_source_address_;
164
  bool is_l7lb_;
165
  Network::Address::InstanceConstSharedPtr ipv4_source_address_;
166
  Network::Address::InstanceConstSharedPtr ipv6_source_address_;
167
  bool enforce_policy_on_l7lb_;
168
  std::string l7lb_policy_name_;
169
  std::chrono::milliseconds ipcache_entry_ttl_;
170
  Random::RandomGenerator& random_;
171

            
172
  std::shared_ptr<const Cilium::NetworkPolicyMap> npmap_{};
173
  Cilium::CtMapSharedPtr ct_maps_{};
174
  Cilium::IpCacheSharedPtr ipcache_{};
175
  std::shared_ptr<const Cilium::PolicyHostMap> hosts_{};
176

            
177
private:
178
  uint32_t resolveSourceIdentity(const PolicyInstance& policy, const Network::Address::Ip* sip,
179
                                 const Network::Address::Ip* dip, bool ingress, bool is_l7_lb);
180

            
181
  IpAddressPair getIpAddressPairWithPort(uint16_t port, const IpAddressPair& addresses);
182

            
183
  const Network::Address::Ip* selectIpVersion(const Network::Address::IpVersion version,
184
                                              const IpAddressPair& source_addresses);
185
};
186

            
187
using ConfigSharedPtr = std::shared_ptr<Config>;
188

            
189
/**
190
 * Implementation of a bpf metadata listener filter.
191
 */
192
class Instance : public Network::ListenerFilter, Logger::Loggable<Logger::Id::filter> {
193
public:
194
119
  Instance(const ConfigSharedPtr& config) : config_(config) {}
195

            
196
  // Network::ListenerFilter
197
  Network::FilterStatus onAccept(Network::ListenerFilterCallbacks& cb) override;
198

            
199
  // Network::ListenerFilter
200
  Network::FilterStatus onData(Network::ListenerFilterBuffer& buffer) override;
201

            
202
  // Network::ListenerFilter
203
  size_t maxReadBytes() const override;
204

            
205
private:
206
  const ConfigSharedPtr config_;
207
};
208

            
209
/**
210
 * Implementation of a UDP bpf metadata listener filter.
211
 */
212
class UdpInstance : public Network::UdpListenerReadFilter, Logger::Loggable<Logger::Id::filter> {
213
public:
214
  UdpInstance(const ConfigSharedPtr& config, Network::UdpReadFilterCallbacks& callbacks)
215
      : UdpListenerReadFilter(callbacks), config_(config) {}
216

            
217
  // Network::UdpListenerReadFilter
218
  Network::FilterStatus onData(Network::UdpRecvData& data) override;
219

            
220
  // Network::UdpListenerReadFilter
221
  Network::FilterStatus onReceiveError(Api::IoError::IoErrorCode error_code) override;
222

            
223
private:
224
  const ConfigSharedPtr config_;
225
};
226

            
227
} // namespace BpfMetadata
228
} // namespace Cilium
229
} // namespace Envoy