Make port_listener buildable
Bug: 340126051
Test: cd build/debian/port_listener && sudo ./build.sh
Change-Id: I01d54c6f5ef7c1b2f4706eedfbfea7d8c3064bd5
diff --git a/build/debian/port_listener/build.sh b/build/debian/port_listener/build.sh
new file mode 100755
index 0000000..a1d0205
--- /dev/null
+++ b/build/debian/port_listener/build.sh
@@ -0,0 +1,54 @@
+#!/bin/bash
+
+set -e
+
+check_sudo() {
+ if [ "$EUID" -ne 0 ]; then
+ echo "Please run as root."
+ exit
+ fi
+}
+
+install_prerequisites() {
+ apt update
+ apt install --no-install-recommends --assume-yes \
+ bpftool \
+ clang \
+ libbpf-dev \
+ libgoogle-glog-dev \
+ libstdc++-14-dev
+}
+
+build_port_listener() {
+ cp $(dirname $0)/src/* ${workdir}
+ out_dir=${PWD}
+ pushd ${workdir}
+ bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
+ clang \
+ -O2 \
+ -Wall \
+ -target bpf \
+ -g \
+ -c listen_tracker.ebpf.c \
+ -o listen_tracker.ebpf.o
+ bpftool gen skeleton listen_tracker.ebpf.o > listen_tracker.skel.h
+ clang++ \
+ -O2 \
+ -Wall \
+ -lbpf \
+ -lglog \
+ -o port_listener \
+ main.cc
+ cp port_listener ${out_dir}
+ popd
+}
+
+clean_up() {
+ rm -rf ${workdir}
+}
+trap clean_up EXIT
+workdir=$(mktemp -d)
+
+check_sudo
+install_prerequisites
+build_port_listener
diff --git a/build/debian/port_listener/src/common.h b/build/debian/port_listener/src/common.h
index 05386a4..d6e507c 100644
--- a/build/debian/port_listener/src/common.h
+++ b/build/debian/port_listener/src/common.h
@@ -19,13 +19,13 @@
#define VM_TOOLS_PORT_LISTENER_COMMON_H_
enum State {
- kPortListenerUp,
- kPortListenerDown,
+ kPortListenerUp,
+ kPortListenerDown,
};
struct event {
- enum State state;
- uint16_t port;
+ enum State state;
+ uint16_t port;
};
-#endif // VM_TOOLS_PORT_LISTENER_COMMON_H_
+#endif // VM_TOOLS_PORT_LISTENER_COMMON_H_
diff --git a/build/debian/port_listener/src/listen_tracker.ebpf.c b/build/debian/port_listener/src/listen_tracker.ebpf.c
index 4ecc423..030ded0 100644
--- a/build/debian/port_listener/src/listen_tracker.ebpf.c
+++ b/build/debian/port_listener/src/listen_tracker.ebpf.c
@@ -16,11 +16,11 @@
// src/platform2/vm_tools/port_listener/listen_tracker.ebpf.c
// bpf_helpers.h uses types defined here
-#include "include/vm_tools/port_listener/vmlinux/vmlinux.h"
+#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
-#include "vm_tools/port_listener/common.h"
+#include "common.h"
// For some reason 6.1 doesn't include these symbols in the debug build
// so they don't get included in vmlinux.h. These features have existed since
@@ -29,55 +29,53 @@
#define BPF_ANY 0
struct {
- __uint(type, BPF_MAP_TYPE_RINGBUF);
- __uint(max_entries, 1 << 24);
+ __uint(type, BPF_MAP_TYPE_RINGBUF);
+ __uint(max_entries, 1 << 24);
} events SEC(".maps");
struct {
- __uint(type, BPF_MAP_TYPE_HASH);
- __type(key, struct sock*);
- __type(value, __u8);
- __uint(max_entries, 65535);
- __uint(map_flags, BPF_F_NO_PREALLOC);
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __type(key, struct sock*);
+ __type(value, __u8);
+ __uint(max_entries, 65535);
+ __uint(map_flags, BPF_F_NO_PREALLOC);
} sockmap SEC(".maps");
const __u8 set_value = 0;
SEC("tp/sock/inet_sock_set_state")
-int tracepoint_inet_sock_set_state(
- struct trace_event_raw_inet_sock_set_state* ctx) {
- // We don't support anything other than TCP.
- if (ctx->protocol != IPPROTO_TCP) {
- return 0;
- }
- struct sock* sk = (struct sock*)ctx->skaddr;
- // If we're transitioning away from LISTEN but we don't know about this
- // socket yet then don't report anything.
- if (ctx->oldstate == BPF_TCP_LISTEN &&
- bpf_map_lookup_elem(&sockmap, &sk) == NULL) {
- return 0;
- }
- // If we aren't transitioning to or from TCP_LISTEN then we don't care.
- if (ctx->newstate != BPF_TCP_LISTEN && ctx->oldstate != BPF_TCP_LISTEN) {
- return 0;
- }
+int tracepoint_inet_sock_set_state(struct trace_event_raw_inet_sock_set_state* ctx) {
+ // We don't support anything other than TCP.
+ if (ctx->protocol != IPPROTO_TCP) {
+ return 0;
+ }
+ struct sock* sk = (struct sock*)ctx->skaddr;
+ // If we're transitioning away from LISTEN but we don't know about this
+ // socket yet then don't report anything.
+ if (ctx->oldstate == BPF_TCP_LISTEN && bpf_map_lookup_elem(&sockmap, &sk) == NULL) {
+ return 0;
+ }
+ // If we aren't transitioning to or from TCP_LISTEN then we don't care.
+ if (ctx->newstate != BPF_TCP_LISTEN && ctx->oldstate != BPF_TCP_LISTEN) {
+ return 0;
+ }
- struct event* ev;
- ev = bpf_ringbuf_reserve(&events, sizeof(*ev), 0);
- if (!ev) {
+ struct event* ev;
+ ev = bpf_ringbuf_reserve(&events, sizeof(*ev), 0);
+ if (!ev) {
+ return 0;
+ }
+ ev->port = ctx->sport;
+
+ if (ctx->newstate == BPF_TCP_LISTEN) {
+ bpf_map_update_elem(&sockmap, &sk, &set_value, BPF_ANY);
+ ev->state = kPortListenerUp;
+ }
+ if (ctx->oldstate == BPF_TCP_LISTEN) {
+ bpf_map_delete_elem(&sockmap, &sk);
+ ev->state = kPortListenerDown;
+ }
+ bpf_ringbuf_submit(ev, 0);
+
return 0;
- }
- ev->port = ctx->sport;
-
- if (ctx->newstate == BPF_TCP_LISTEN) {
- bpf_map_update_elem(&sockmap, &sk, &set_value, BPF_ANY);
- ev->state = kPortListenerUp;
- }
- if (ctx->oldstate == BPF_TCP_LISTEN) {
- bpf_map_delete_elem(&sockmap, &sk);
- ev->state = kPortListenerDown;
- }
- bpf_ringbuf_submit(ev, 0);
-
- return 0;
}
diff --git a/build/debian/port_listener/src/main.cc b/build/debian/port_listener/src/main.cc
index cc1f91e..b0b0979 100644
--- a/build/debian/port_listener/src/main.cc
+++ b/build/debian/port_listener/src/main.cc
@@ -15,24 +15,18 @@
// Copied from ChromiumOS with relicensing:
// src/platform2/vm_tools/port_listener/main.cc
-#include <sys/socket.h>
-
-#include <linux/vm_sockets.h> // Needs to come after sys/socket.h
-
-#include <memory>
-
-#include <base/logging.h>
-#include <base/memory/ptr_util.h>
-#include <base/strings/stringprintf.h>
#include <bpf/libbpf.h>
#include <bpf/libbpf_legacy.h>
-#include <chromeos/constants/vm_tools.h>
-#include <grpcpp/grpcpp.h>
-#include <vm_protos/proto_bindings/common.pb.h>
-#include <vm_protos/proto_bindings/tremplin.grpc.pb.h>
+#include <glog/logging.h>
+#include <sys/socket.h>
-#include "vm_tools/port_listener/bpf/generated/skeleton_listen_tracker.ebpf.h"
-#include "vm_tools/port_listener/common.h"
+#include <linux/vm_sockets.h> // Needs to come after sys/socket.h
+
+#include <memory>
+#include <unordered_map>
+
+#include "common.h"
+#include "listen_tracker.skel.h"
typedef std::unordered_map<int, int> port_usage_map;
@@ -40,158 +34,135 @@
namespace {
int HandleEvent(void* ctx, void* const data, size_t size) {
- port_usage_map* map = reinterpret_cast<port_usage_map*>(ctx);
- const struct event* ev = (struct event*)data;
+ port_usage_map* map = reinterpret_cast<port_usage_map*>(ctx);
+ const struct event* ev = (struct event*)data;
- switch (ev->state) {
- case kPortListenerUp:
- (*map)[ev->port]++;
- break;
+ switch (ev->state) {
+ case kPortListenerUp:
+ (*map)[ev->port]++;
+ break;
- case kPortListenerDown:
- if ((*map)[ev->port] > 0) {
- (*map)[ev->port]--;
- } else {
- LOG(INFO) << "Received down event while port count was 0; ignoring";
- }
+ case kPortListenerDown:
+ if ((*map)[ev->port] > 0) {
+ (*map)[ev->port]--;
+ } else {
+ LOG(INFO) << "Received down event while port count was 0; ignoring";
+ }
- break;
+ break;
- default:
- LOG(ERROR) << "Unknown event state " << ev->state;
- }
+ default:
+ LOG(ERROR) << "Unknown event state " << ev->state;
+ }
- LOG(INFO) << "Listen event: port=" << ev->port << " state=" << ev->state;
+ LOG(INFO) << "Listen event: port=" << ev->port << " state=" << ev->state;
- return 0;
+ return 0;
}
-typedef std::unique_ptr<struct ring_buffer, decltype(&ring_buffer__free)>
- ring_buffer_ptr;
-typedef std::unique_ptr<listen_tracker_ebpf,
- decltype(&listen_tracker_ebpf__destroy)>
- listen_tracker_ptr;
+typedef std::unique_ptr<struct ring_buffer, decltype(&ring_buffer__free)> ring_buffer_ptr;
+typedef std::unique_ptr<listen_tracker_ebpf, decltype(&listen_tracker_ebpf__destroy)>
+ listen_tracker_ptr;
// BPFProgram tracks the state and resources of the listen_tracker BPF program.
class BPFProgram {
- public:
- // Default movable but not copyable.
- BPFProgram(BPFProgram&& other) = default;
- BPFProgram(const BPFProgram& other) = delete;
- BPFProgram& operator=(BPFProgram&& other) = default;
- BPFProgram& operator=(const BPFProgram& other) = delete;
+public:
+ // Default movable but not copyable.
+ BPFProgram(BPFProgram&& other) = default;
+ BPFProgram(const BPFProgram& other) = delete;
+ BPFProgram& operator=(BPFProgram&& other) = default;
+ BPFProgram& operator=(const BPFProgram& other) = delete;
- // Load loads the listen_tracker BPF program and prepares it for polling. On
- // error nullptr is returned.
- static std::unique_ptr<BPFProgram> Load() {
- auto* skel = listen_tracker_ebpf__open();
- if (!skel) {
- PLOG(ERROR) << "Failed to open listen_tracker BPF skeleton";
- return nullptr;
- }
- listen_tracker_ptr skeleton(skel, listen_tracker_ebpf__destroy);
+ // Load loads the listen_tracker BPF program and prepares it for polling. On
+ // error nullptr is returned.
+ static std::unique_ptr<BPFProgram> Load() {
+ auto* skel = listen_tracker_ebpf__open();
+ if (!skel) {
+ PLOG(ERROR) << "Failed to open listen_tracker BPF skeleton";
+ return nullptr;
+ }
+ listen_tracker_ptr skeleton(skel, listen_tracker_ebpf__destroy);
- int err = listen_tracker_ebpf__load(skeleton.get());
- if (err) {
- PLOG(ERROR) << "Failed to load listen_tracker BPF program";
- return nullptr;
+ int err = listen_tracker_ebpf__load(skeleton.get());
+ if (err) {
+ PLOG(ERROR) << "Failed to load listen_tracker BPF program";
+ return nullptr;
+ }
+
+ auto map = std::make_unique<port_usage_map>();
+ auto* rb = ring_buffer__new(bpf_map__fd(skel->maps.events), HandleEvent, map.get(), NULL);
+ if (!rb) {
+ PLOG(ERROR) << "Failed to open ring buffer for listen_tracker";
+ return nullptr;
+ }
+ ring_buffer_ptr ringbuf(rb, ring_buffer__free);
+
+ err = listen_tracker_ebpf__attach(skeleton.get());
+ if (err) {
+ PLOG(ERROR) << "Failed to attach listen_tracker";
+ return nullptr;
+ }
+
+ return std::unique_ptr<BPFProgram>(
+ new BPFProgram(std::move(skeleton), std::move(ringbuf), std::move(map)));
}
- auto map = std::make_unique<port_usage_map>();
- auto* rb = ring_buffer__new(bpf_map__fd(skel->maps.events), HandleEvent,
- map.get(), NULL);
- if (!rb) {
- PLOG(ERROR) << "Failed to open ring buffer for listen_tracker";
- return nullptr;
- }
- ring_buffer_ptr ringbuf(rb, ring_buffer__free);
+ // Poll waits for the listen_tracker BPF program to post a new event to the
+ // ring buffer. BPFProgram handles integrating this new event into the
+ // port_usage map and callers should consult port_usage() after Poll returns
+ // for the latest data.
+ const bool Poll() {
+ int err = ring_buffer__poll(rb_.get(), -1);
+ if (err < 0) {
+ LOG(ERROR) << "Error polling ring buffer ret=" << err;
+ return false;
+ }
- err = listen_tracker_ebpf__attach(skeleton.get());
- if (err) {
- PLOG(ERROR) << "Failed to attach listen_tracker";
- return nullptr;
+ return true;
}
- return base::WrapUnique(new BPFProgram(std::move(skeleton),
- std::move(ringbuf), std::move(map)));
- }
+ const port_usage_map& port_usage() { return *port_usage_; }
- // Poll waits for the listen_tracker BPF program to post a new event to the
- // ring buffer. BPFProgram handles integrating this new event into the
- // port_usage map and callers should consult port_usage() after Poll returns
- // for the latest data.
- const bool Poll() {
- int err = ring_buffer__poll(rb_.get(), -1);
- if (err < 0) {
- LOG(ERROR) << "Error polling ring buffer ret=" << err;
- return false;
- }
+private:
+ BPFProgram(listen_tracker_ptr&& skeleton, ring_buffer_ptr&& rb,
+ std::unique_ptr<port_usage_map>&& port_usage)
+ : skeleton_(std::move(skeleton)),
+ rb_(std::move(rb)),
+ port_usage_(std::move(port_usage)) {}
- return true;
- }
-
- const port_usage_map& port_usage() { return *port_usage_; }
-
- private:
- BPFProgram(listen_tracker_ptr&& skeleton,
- ring_buffer_ptr&& rb,
- std::unique_ptr<port_usage_map>&& port_usage)
- : skeleton_(std::move(skeleton)),
- rb_(std::move(rb)),
- port_usage_(std::move(port_usage)) {}
-
- listen_tracker_ptr skeleton_;
- ring_buffer_ptr rb_;
- std::unique_ptr<port_usage_map> port_usage_;
+ listen_tracker_ptr skeleton_;
+ ring_buffer_ptr rb_;
+ std::unique_ptr<port_usage_map> port_usage_;
};
-} // namespace
-} // namespace port_listener
+} // namespace
+} // namespace port_listener
int main(int argc, char** argv) {
- logging::InitLogging(logging::LoggingSettings());
- libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
+ google::InitGoogleLogging(argv[0]);
+ libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
- // Load our BPF program.
- auto program = port_listener::BPFProgram::Load();
- if (program == nullptr) {
- LOG(ERROR) << "Failed to load BPF program";
- return EXIT_FAILURE;
- }
-
- // Connect back to TremplinListener
- vm_tools::tremplin::TremplinListener::Stub tremplin_listener(
- grpc::CreateChannel(base::StringPrintf("vsock:%u:%u", VMADDR_CID_HOST,
- vm_tools::kTremplinListenerPort),
- grpc::InsecureChannelCredentials()));
-
- // main loop: poll for listen updates, when an update comes send an rpc to
- // tremplin listener letting it know.
- for (;;) {
- if (!program->Poll()) {
- LOG(ERROR) << "Failure while polling BPF program";
- return EXIT_FAILURE;
- }
- // port_usage will be updated with the latest usage data
-
- vm_tools::tremplin::ListeningPortInfo_ContainerPortInfo cpi;
- for (auto it : program->port_usage()) {
- if (it.second <= 0) {
- continue;
- }
- cpi.add_listening_tcp4_ports(it.first);
+ // Load our BPF program.
+ auto program = port_listener::BPFProgram::Load();
+ if (program == nullptr) {
+ LOG(ERROR) << "Failed to load BPF program";
+ return EXIT_FAILURE;
}
- vm_tools::tremplin::ListeningPortInfo lpi;
- (*lpi.mutable_container_ports())["penguin"] = cpi;
+ // main loop: poll for listen updates
+ for (;;) {
+ if (!program->Poll()) {
+ LOG(ERROR) << "Failure while polling BPF program";
+ return EXIT_FAILURE;
+ }
+ // port_usage will be updated with the latest usage data
- grpc::ClientContext ctx;
- vm_tools::tremplin::EmptyMessage empty;
- grpc::Status status =
- tremplin_listener.UpdateListeningPorts(&ctx, lpi, &empty);
- if (!status.ok()) {
- LOG(WARNING) << "Failed to notify tremplin of new listening ports: "
- << status.error_message();
+ for (auto it : program->port_usage()) {
+ if (it.second <= 0) {
+ continue;
+ }
+ // TODO(b/340126051): Add listening TCP4 ports.
+ }
+ // TODO(b/340126051): Notify port information to the guest agent.
}
- }
}