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/guest/forwarder_guest/Cargo.toml b/guest/forwarder_guest/Cargo.toml
new file mode 100644
index 0000000..65f57cf
--- /dev/null
+++ b/guest/forwarder_guest/Cargo.toml
@@ -0,0 +1,11 @@
+[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/guest/forwarder_guest/src/main.rs b/guest/forwarder_guest/src/main.rs
new file mode 100644
index 0000000..6ebd4ef
--- /dev/null
+++ b/guest/forwarder_guest/src/main.rs
@@ -0,0 +1,123 @@
+// 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/guest/ip_addr_reporter/.gitignore b/guest/ip_addr_reporter/.gitignore
new file mode 100644
index 0000000..ea8c4bf
--- /dev/null
+++ b/guest/ip_addr_reporter/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/guest/ip_addr_reporter/Cargo.toml b/guest/ip_addr_reporter/Cargo.toml
new file mode 100644
index 0000000..e255eaf
--- /dev/null
+++ b/guest/ip_addr_reporter/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "ip_addr_reporter"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+netdev = "0.31.0"
+prost = "0.13.3"
+tokio = { version = "1.40.0", features = ["rt-multi-thread"] }
+tonic = "0.12.3"
+
+[build-dependencies]
+tonic-build = "0.12.3"
diff --git a/guest/ip_addr_reporter/build.rs b/guest/ip_addr_reporter/build.rs
new file mode 100644
index 0000000..e3939d4
--- /dev/null
+++ b/guest/ip_addr_reporter/build.rs
@@ -0,0 +1,7 @@
+fn main() -> Result<(), Box<dyn std::error::Error>> {
+    let proto_file = "../../libs/debian_service/proto/DebianService.proto";
+
+    tonic_build::compile_protos(proto_file).unwrap();
+
+    Ok(())
+}
diff --git a/guest/ip_addr_reporter/src/main.rs b/guest/ip_addr_reporter/src/main.rs
new file mode 100644
index 0000000..5784a83
--- /dev/null
+++ b/guest/ip_addr_reporter/src/main.rs
@@ -0,0 +1,26 @@
+use api::debian_service_client::DebianServiceClient;
+use api::IpAddr;
+
+pub mod api {
+    tonic::include_proto!("com.android.virtualization.vmlauncher.proto");
+}
+
+#[tokio::main]
+async fn main() -> Result<(), String> {
+    let gateway_ip_addr = netdev::get_default_gateway()?.ipv4[0];
+    let ip_addr = netdev::get_default_interface()?.ipv4[0].addr();
+    const PORT: i32 = 12000;
+
+    let server_addr = format!("http://{}:{}", gateway_ip_addr.to_string(), PORT);
+
+    println!("local ip addr: {}", ip_addr.to_string());
+    println!("coonect to grpc server {}", server_addr);
+
+    let mut client = DebianServiceClient::connect(server_addr).await.map_err(|e| e.to_string())?;
+
+    let request = tonic::Request::new(IpAddr { addr: ip_addr.to_string() });
+
+    let response = client.report_vm_ip_addr(request).await.map_err(|e| e.to_string())?;
+    println!("response from server: {:?}", response);
+    Ok(())
+}
diff --git a/guest/port_listener/build.sh b/guest/port_listener/build.sh
new file mode 100755
index 0000000..a1d0205
--- /dev/null
+++ b/guest/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/guest/port_listener/src/common.h b/guest/port_listener/src/common.h
new file mode 100644
index 0000000..d6e507c
--- /dev/null
+++ b/guest/port_listener/src/common.h
@@ -0,0 +1,31 @@
+// 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/guest/port_listener/src/listen_tracker.ebpf.c b/guest/port_listener/src/listen_tracker.ebpf.c
new file mode 100644
index 0000000..9e98aad
--- /dev/null
+++ b/guest/port_listener/src/listen_tracker.ebpf.c
@@ -0,0 +1,80 @@
+// 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 <bpf/bpf_helpers.h>
+
+#include "common.h"
+#include "vmlinux.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/guest/port_listener/src/main.cc b/guest/port_listener/src/main.cc
new file mode 100644
index 0000000..1caceaf
--- /dev/null
+++ b/guest/port_listener/src/main.cc
@@ -0,0 +1,167 @@
+// 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 <linux/vm_sockets.h> // Needs to come after sys/socket.h
+#include <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.
+    }
+}