1
#include "cilium/socket_option_ip_transparent.h"
2

            
3
#include <netinet/in.h>
4

            
5
#include <cerrno>
6
#include <cstdint>
7

            
8
#include "envoy/config/core/v3/socket_option.pb.h"
9
#include "envoy/network/address.h"
10
#include "envoy/network/socket.h"
11

            
12
#include "source/common/common/logger.h"
13
#include "source/common/common/utility.h"
14

            
15
#include "cilium/privileged_service_client.h"
16

            
17
namespace Envoy {
18
namespace Cilium {
19

            
20
IpTransparentSocketOption::IpTransparentSocketOption() {
21
  ENVOY_LOG(debug, "Cilium IpTransparentSocketOption()");
22
}
23

            
24
bool IpTransparentSocketOption::setOption(
25
    Network::Socket& socket, envoy::config::core::v3::SocketOption::SocketState state) const {
26
  // Only set the option once per socket
27
  if (state != envoy::config::core::v3::SocketOption::STATE_PREBIND) {
28
    ENVOY_LOG(trace, "Skipping setting socket ({}) option IP_TRANSPARENT, state != STATE_PREBIND",
29
              socket.ioHandle().fdDoNotUse());
30
    return true;
31
  }
32

            
33
  auto& cilium_calls = PrivilegedService::Singleton::get();
34

            
35
  auto ip_version = socket.ipVersion();
36
  if (!ip_version.has_value()) {
37
    ENVOY_LOG(critical, "Socket address family is not available, can not choose source address");
38
    return false;
39
  }
40

            
41
  uint32_t one = 1;
42

            
43
  // Set ip transparent option based on the socket address family
44
  auto ip_socket_level = SOL_IP;
45
  auto ip_transparent_socket_option = IP_TRANSPARENT;
46
  auto ip_transparent_socket_option_name = "IP_TRANSPARENT";
47
  if (*ip_version == Network::Address::IpVersion::v6) {
48
    ip_socket_level = SOL_IPV6;
49
    ip_transparent_socket_option = IPV6_TRANSPARENT;
50
    ip_transparent_socket_option_name = "IPV6_TRANSPARENT";
51
  }
52

            
53
  auto status = cilium_calls.setsockopt(socket.ioHandle().fdDoNotUse(), ip_socket_level,
54
                                        ip_transparent_socket_option, &one, sizeof(one));
55
  if (status.return_value_ < 0) {
56
    if (status.errno_ == EPERM) {
57
      // Do not assert out in this case so that we can run tests without
58
      // CAP_NET_ADMIN.
59
      ENVOY_LOG(critical,
60
                "Failed to set socket option {}, capability "
61
                "CAP_NET_ADMIN needed: {}",
62
                ip_transparent_socket_option_name, Envoy::errorDetails(status.errno_));
63
    } else {
64
      ENVOY_LOG(critical, "Socket option failure. Failed to set {}: {}",
65
                ip_transparent_socket_option_name, Envoy::errorDetails(status.errno_));
66
      return false;
67
    }
68
  }
69

            
70
  ENVOY_LOG(trace, "Successfully set socket option {} on socket: {}",
71
            ip_transparent_socket_option_name, socket.ioHandle().fdDoNotUse());
72

            
73
  return true;
74
}
75

            
76
} // namespace Cilium
77
} // namespace Envoy