Define DebianService for host-guest communication
1. Use TCP/IP socket for now
2. IP address reporter uses that
3. Refactoring: remove timeout for shell page because systemd unit for
ip addr reporter 'requires' ttyd
4. move source files for guest(debian) to guest dir
Bug: 372666638
Test: check if shell shows
Change-Id: Ida94a05de9998a06b4b5c7efebbae97c78617bf4
diff --git a/build/debian/build.sh b/build/debian/build.sh
index 0d13019..97d9373 100755
--- a/build/debian/build.sh
+++ b/build/debian/build.sh
@@ -60,6 +60,7 @@
fai-setup-storage
fdisk
make
+ protobuf-compiler
python3
python3-libcloud
python3-marshmallow
@@ -115,7 +116,7 @@
wget "${url}" -O "${dst}/files/usr/local/bin/ttyd/AVF"
chmod 777 "${dst}/files/usr/local/bin/ttyd/AVF"
- pushd "$(dirname "$0")/forwarder_guest" > /dev/null
+ pushd "$(dirname "$0")/../../guest/forwarder_guest" > /dev/null
RUSTFLAGS="-C linker=${arch}-linux-gnu-gcc" cargo build \
--target "${arch}-unknown-linux-gnu" \
--target-dir "${workdir}/forwarder_guest"
@@ -123,6 +124,15 @@
cp "${workdir}/forwarder_guest/${arch}-unknown-linux-gnu/debug/forwarder_guest" "${dst}/files/usr/local/bin/forwarder_guest/AVF"
chmod 777 "${dst}/files/usr/local/bin/forwarder_guest/AVF"
popd > /dev/null
+
+ pushd $(dirname $0)/../../guest/ip_addr_reporter > /dev/null
+ RUSTFLAGS="-C linker=aarch64-linux-gnu-gcc" cargo build \
+ --target aarch64-unknown-linux-gnu \
+ --target-dir ${workdir}/ip_addr_reporter
+ mkdir -p ${dst}/files/usr/local/bin/ip_addr_reporter
+ cp ${workdir}/ip_addr_reporter/aarch64-unknown-linux-gnu/debug/ip_addr_reporter ${dst}/files/usr/local/bin/ip_addr_reporter/AVF
+ chmod 777 ${dst}/files/usr/local/bin/ip_addr_reporter/AVF
+ popd > /dev/null
}
run_fai() {
diff --git a/build/debian/fai_config/files/etc/systemd/system/ip_addr_reporter.service/AVF b/build/debian/fai_config/files/etc/systemd/system/ip_addr_reporter.service/AVF
new file mode 100644
index 0000000..7d163fb
--- /dev/null
+++ b/build/debian/fai_config/files/etc/systemd/system/ip_addr_reporter.service/AVF
@@ -0,0 +1,13 @@
+[Unit]
+Description=ip report service
+After=syslog.target
+After=network.target
+Requires=ttyd.service
+[Service]
+ExecStart=/usr/local/bin/ip_addr_reporter
+Type=simple
+Restart=on-failure
+User=root
+Group=root
+[Install]
+WantedBy=multi-user.target
diff --git a/build/debian/fai_config/files/etc/systemd/system/vsockip.service/AVF b/build/debian/fai_config/files/etc/systemd/system/vsockip.service/AVF
deleted file mode 100644
index a29020b..0000000
--- a/build/debian/fai_config/files/etc/systemd/system/vsockip.service/AVF
+++ /dev/null
@@ -1,12 +0,0 @@
-[Unit]
-Description=vsock ip service
-After=syslog.target
-After=network.target
-[Service]
-ExecStart=/usr/bin/python3 /usr/local/bin/vsock.py
-Type=simple
-Restart=always
-User=root
-Group=root
-[Install]
-WantedBy=multi-user.target
diff --git a/build/debian/fai_config/files/usr/local/bin/vsock.py/AVF b/build/debian/fai_config/files/usr/local/bin/vsock.py/AVF
deleted file mode 100755
index 292d953..0000000
--- a/build/debian/fai_config/files/usr/local/bin/vsock.py/AVF
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/usr/bin/env python3
-
-import socket
-
-# Constants for vsock (from linux/vm_sockets.h)
-AF_VSOCK = 40
-SOCK_STREAM = 1
-VMADDR_CID_ANY = -1
-
-def get_local_ip():
- """Retrieves the first IPv4 address found on the system.
-
- Returns:
- str: The local IPv4 address, or '127.0.0.1' if no IPv4 address is found.
- """
-
- s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- try:
- s.connect(('8.8.8.8', 80))
- ip = s.getsockname()[0]
- except Exception:
- ip = '127.0.0.1'
- finally:
- s.close()
- return ip
-
-def main():
- PORT = 1024
-
- # Create a vsock socket
- server_socket = socket.socket(AF_VSOCK, SOCK_STREAM)
-
- # Bind the socket to the server address
- server_address = (VMADDR_CID_ANY, PORT)
- server_socket.bind(server_address)
-
- # Listen for incoming connections
- server_socket.listen(1)
- print(f"VSOCK server listening on port {PORT}...")
-
- while True:
- # Accept a connection
- connection, client_address = server_socket.accept()
- print(f"Connection from: {client_address}")
-
- try:
- # Get the local IP address
- local_ip = get_local_ip()
-
- # Send the IP address to the client
- connection.sendall(local_ip.encode())
- finally:
- # Close the connection
- connection.close()
-
-if __name__ == "__main__":
- main()
diff --git a/build/debian/fai_config/scripts/AVF/10-systemd b/build/debian/fai_config/scripts/AVF/10-systemd
index d33b92a..09d1bd1 100755
--- a/build/debian/fai_config/scripts/AVF/10-systemd
+++ b/build/debian/fai_config/scripts/AVF/10-systemd
@@ -1,7 +1,7 @@
#!/bin/bash
chmod +x $target/usr/local/bin/forwarder_guest
+chmod +x $target/usr/local/bin/ip_addr_reporter
chmod +x $target/usr/local/bin/ttyd
-chmod +x $target/usr/local/bin/vsock.py
ln -s /etc/systemd/system/ttyd.service $target/etc/systemd/system/multi-user.target.wants/ttyd.service
-ln -s /etc/systemd/system/vsockip.service $target/etc/systemd/system/multi-user.target.wants/vsockip.service
\ No newline at end of file
+ln -s /etc/systemd/system/ip_addr_reporter.service $target/etc/systemd/system/multi-user.target.wants/ip_addr_reporter.service
\ No newline at end of file
diff --git a/build/debian/forwarder_guest/Cargo.toml b/build/debian/forwarder_guest/Cargo.toml
deleted file mode 100644
index e70dcd4..0000000
--- a/build/debian/forwarder_guest/Cargo.toml
+++ /dev/null
@@ -1,11 +0,0 @@
-[package]
-name = "forwarder_guest"
-version = "0.1.0"
-edition = "2021"
-
-[dependencies]
-clap = { version = "4.5.19", features = ["derive"] }
-forwarder = { path = "../../../libs/libforwarder" }
-poll_token_derive = "0.1.0"
-remain = "0.2.14"
-vmm-sys-util = "0.12.1"
diff --git a/build/debian/forwarder_guest/src/main.rs b/build/debian/forwarder_guest/src/main.rs
deleted file mode 100644
index 6ebd4ef..0000000
--- a/build/debian/forwarder_guest/src/main.rs
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright 2024 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Copied from ChromiumOS with relicensing:
-// src/platform2/vm_tools/chunnel/src/bin/chunnel.rs
-
-//! Guest-side stream socket forwarder
-
-use std::fmt;
-use std::result;
-
-use clap::Parser;
-use forwarder::forwarder::{ForwarderError, ForwarderSession};
-use forwarder::stream::{StreamSocket, StreamSocketError};
-use poll_token_derive::PollToken;
-use vmm_sys_util::poll::{PollContext, PollToken};
-
-#[remain::sorted]
-#[derive(Debug)]
-enum Error {
- ConnectSocket(StreamSocketError),
- Forward(ForwarderError),
- PollContextAdd(vmm_sys_util::errno::Error),
- PollContextDelete(vmm_sys_util::errno::Error),
- PollContextNew(vmm_sys_util::errno::Error),
- PollWait(vmm_sys_util::errno::Error),
-}
-
-type Result<T> = result::Result<T, Error>;
-
-impl fmt::Display for Error {
- #[remain::check]
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- use self::Error::*;
-
- #[remain::sorted]
- match self {
- ConnectSocket(e) => write!(f, "failed to connect socket: {}", e),
- Forward(e) => write!(f, "failed to forward traffic: {}", e),
- PollContextAdd(e) => write!(f, "failed to add fd to poll context: {}", e),
- PollContextDelete(e) => write!(f, "failed to delete fd from poll context: {}", e),
- PollContextNew(e) => write!(f, "failed to create poll context: {}", e),
- PollWait(e) => write!(f, "failed to wait for poll: {}", e),
- }
- }
-}
-
-fn run_forwarder(local_stream: StreamSocket, remote_stream: StreamSocket) -> Result<()> {
- #[derive(PollToken)]
- enum Token {
- LocalStreamReadable,
- RemoteStreamReadable,
- }
- let poll_ctx: PollContext<Token> = PollContext::new().map_err(Error::PollContextNew)?;
- poll_ctx.add(&local_stream, Token::LocalStreamReadable).map_err(Error::PollContextAdd)?;
- poll_ctx.add(&remote_stream, Token::RemoteStreamReadable).map_err(Error::PollContextAdd)?;
-
- let mut forwarder = ForwarderSession::new(local_stream, remote_stream);
-
- loop {
- let events = poll_ctx.wait().map_err(Error::PollWait)?;
-
- for event in events.iter_readable() {
- match event.token() {
- Token::LocalStreamReadable => {
- let shutdown = forwarder.forward_from_local().map_err(Error::Forward)?;
- if shutdown {
- poll_ctx
- .delete(forwarder.local_stream())
- .map_err(Error::PollContextDelete)?;
- }
- }
- Token::RemoteStreamReadable => {
- let shutdown = forwarder.forward_from_remote().map_err(Error::Forward)?;
- if shutdown {
- poll_ctx
- .delete(forwarder.remote_stream())
- .map_err(Error::PollContextDelete)?;
- }
- }
- }
- }
- if forwarder.is_shut_down() {
- return Ok(());
- }
- }
-}
-
-#[derive(Parser)]
-/// Flags for running command
-pub struct Args {
- /// Local socket address
- #[arg(long)]
- #[arg(alias = "local")]
- local_sockaddr: String,
-
- /// Remote socket address
- #[arg(long)]
- #[arg(alias = "remote")]
- remote_sockaddr: String,
-}
-
-// TODO(b/370897694): Support forwarding for datagram socket
-fn main() -> Result<()> {
- let args = Args::parse();
-
- let local_stream = StreamSocket::connect(&args.local_sockaddr).map_err(Error::ConnectSocket)?;
- let remote_stream =
- StreamSocket::connect(&args.remote_sockaddr).map_err(Error::ConnectSocket)?;
-
- run_forwarder(local_stream, remote_stream)
-}
diff --git a/build/debian/port_listener/build.sh b/build/debian/port_listener/build.sh
deleted file mode 100755
index a1d0205..0000000
--- a/build/debian/port_listener/build.sh
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/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
deleted file mode 100644
index d6e507c..0000000
--- a/build/debian/port_listener/src/common.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2024 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Copied from ChromiumOS with relicensing:
-// src/platform2/vm_tools/port_listener/common.h
-
-#ifndef VM_TOOLS_PORT_LISTENER_COMMON_H_
-#define VM_TOOLS_PORT_LISTENER_COMMON_H_
-
-enum State {
- kPortListenerUp,
- kPortListenerDown,
-};
-
-struct event {
- enum State state;
- uint16_t port;
-};
-
-#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
deleted file mode 100644
index 030ded0..0000000
--- a/build/debian/port_listener/src/listen_tracker.ebpf.c
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2024 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Copied from ChromiumOS with relicensing:
-// src/platform2/vm_tools/port_listener/listen_tracker.ebpf.c
-
-// bpf_helpers.h uses types defined here
-#include "vmlinux.h"
-
-#include <bpf/bpf_helpers.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
-// well before 6.1.
-#define BPF_F_NO_PREALLOC (1U << 0)
-#define BPF_ANY 0
-
-struct {
- __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);
-} 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;
- }
-
- 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;
-}
diff --git a/build/debian/port_listener/src/main.cc b/build/debian/port_listener/src/main.cc
deleted file mode 100644
index b0b0979..0000000
--- a/build/debian/port_listener/src/main.cc
+++ /dev/null
@@ -1,168 +0,0 @@
-// Copyright 2024 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-// Copied from ChromiumOS with relicensing:
-// src/platform2/vm_tools/port_listener/main.cc
-
-#include <bpf/libbpf.h>
-#include <bpf/libbpf_legacy.h>
-#include <glog/logging.h>
-#include <sys/socket.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;
-
-namespace port_listener {
-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;
-
- 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";
- }
-
- break;
-
- default:
- LOG(ERROR) << "Unknown event state " << ev->state;
- }
-
- LOG(INFO) << "Listen event: port=" << ev->port << " state=" << ev->state;
-
- 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;
-
-// 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;
-
- // 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;
- }
-
- 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)));
- }
-
- // 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;
- }
-
- 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_;
-};
-
-} // namespace
-} // namespace port_listener
-
-int main(int argc, char** argv) {
- 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;
- }
-
- // 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
-
- 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.
- }
-}