1
#include "cilium/bpf.h"
2

            
3
#include <unistd.h>
4

            
5
#include <cerrno>
6
#include <cstdint>
7
#include <fstream>
8
#include <sstream>
9
#include <string>
10

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

            
14
#include "cilium/privileged_service_client.h"
15
#include "linux/bpf.h"
16

            
17
namespace Envoy {
18
namespace Cilium {
19

            
20
enum {
21
  BpfKeyMaxLen = 64,
22
};
23

            
24
Bpf::Bpf(uint32_t map_type, uint32_t key_size, uint32_t min_value_size, uint32_t max_value_size)
25
    : fd_(-1), map_type_(map_type), key_size_(key_size), min_value_size_(min_value_size),
26
      max_value_size_(max_value_size), real_value_size_(0) {
27
  if (max_value_size_ == 0) {
28
    max_value_size_ = min_value_size_;
29
  }
30
}
31

            
32
Bpf::~Bpf() { close(); }
33

            
34
void Bpf::close() {
35
  if (fd_ >= 0) {
36
    ::close(fd_);
37
  }
38
  fd_ = -1;
39
  real_value_size_ = 0;
40
}
41

            
42
bool Bpf::open(const std::string& path) {
43
  bool log_on_error = ENVOY_LOG_CHECK_LEVEL(trace);
44

            
45
  // close old fd if any
46
  close();
47

            
48
  // store the path for later
49
  if (path != path_) {
50
    path_ = path;
51
  }
52

            
53
  auto& cilium_calls = PrivilegedService::Singleton::get();
54
  auto ret = cilium_calls.bpfOpen(path.c_str());
55
  fd_ = ret.return_value_;
56
  if (fd_ >= 0) {
57
    // Open fdinfo to check the map type and key and value size.
58
    std::string line;
59
    std::string bpf_file_path("/proc/" + std::to_string(getpid()) + "/fdinfo/" +
60
                              std::to_string(fd_));
61
    std::ifstream bpf_file(bpf_file_path);
62
    if (bpf_file.is_open()) {
63
      uint32_t map_type = UINT32_MAX, key_size = UINT32_MAX, value_size = UINT32_MAX;
64

            
65
      while (std::getline(bpf_file, line)) {
66
        std::istringstream iss(line);
67
        std::string tag;
68

            
69
        if (std::getline(iss, tag, ':')) {
70
          unsigned int value;
71

            
72
          if (iss >> value) {
73
            if (tag == "map_type") {
74
              map_type = value;
75
            } else if (tag == "key_size") {
76
              key_size = value;
77
            } else if (tag == "value_size") {
78
              value_size = value;
79
            }
80
          }
81
        }
82
      }
83
      bpf_file.close();
84

            
85
      if ((map_type == map_type_ ||
86
           (map_type == BPF_MAP_TYPE_LRU_HASH && map_type_ == BPF_MAP_TYPE_HASH)) &&
87
          key_size == key_size_ && min_value_size_ <= value_size && value_size <= max_value_size_) {
88
        // keep the actual value size.
89
        real_value_size_ = value_size;
90
        return true;
91
      }
92
      if (log_on_error) {
93
        if (map_type != map_type_) {
94
          ENVOY_LOG(warn, "cilium.bpf_metadata: map type mismatch on {}: got {}, wanted {}", path,
95
                    map_type, map_type_);
96
        } else if (key_size != key_size_) {
97
          ENVOY_LOG(warn,
98
                    "cilium.bpf_metadata: map key size mismatch on {}: got {}, "
99
                    "wanted {}",
100
                    path, key_size, key_size_);
101
        } else {
102
          ENVOY_LOG(warn,
103
                    "cilium.bpf_metadata: map value size mismatch on {}: got "
104
                    "{}, wanted {}-{}",
105
                    path, value_size, min_value_size_, max_value_size_);
106
        }
107
      }
108
    } else if (log_on_error) {
109
      ENVOY_LOG(warn, "cilium.bpf_metadata: map {} could not open bpf file {}", path,
110
                bpf_file_path);
111
    }
112
    close();
113
  } else if (ret.errno_ == ENOENT && log_on_error) {
114
    ENVOY_LOG(debug, "cilium.bpf_metadata: bpf syscall for map {} failed: {}", path,
115
              Envoy::errorDetails(ret.errno_));
116
  } else if (log_on_error) {
117
    ENVOY_LOG(warn, "cilium.bpf_metadata: bpf syscall for map {} failed: {}", path,
118
              Envoy::errorDetails(ret.errno_));
119
  }
120

            
121
  errno = ret.errno_;
122

            
123
  return false;
124
}
125

            
126
// value must point to space of at least 'max_value_size_' as passed in to the constructor.
127
bool Bpf::lookup(const void* key, void* value) {
128
  // Try reopen if open failed previously
129
  if (fd_ < 0) {
130
    if (!open(path_)) {
131
      return false;
132
    }
133
  }
134

            
135
  auto& cilium_calls = PrivilegedService::Singleton::get();
136
  auto result = cilium_calls.bpfLookup(fd_, key, key_size_, value, real_value_size_);
137

            
138
  if (result.return_value_ == 0) {
139
    return true;
140
  }
141

            
142
  errno = result.errno_;
143
  return false;
144
}
145

            
146
} // namespace Cilium
147
} // namespace Envoy