Merge changes from topic "fh_impl" into main
* changes:
forwarder_guest_launcher can execute forwarder_guest
Host can send required infos to the guest for performing forwarding.
diff --git a/android/TerminalApp/Android.bp b/android/TerminalApp/Android.bp
index 84ba041..09287d8 100644
--- a/android/TerminalApp/Android.bp
+++ b/android/TerminalApp/Android.bp
@@ -17,6 +17,10 @@
"com.google.android.material_material",
"androidx.window_window",
],
+ jni_libs: [
+ "libforwarder_host_jni",
+ ],
+ use_embedded_native_libs: true,
platform_apis: true,
privileged: true,
optimize: {
diff --git a/android/forwarder_host/Android.bp b/android/forwarder_host/Android.bp
index 35c478e..e8d0184 100644
--- a/android/forwarder_host/Android.bp
+++ b/android/forwarder_host/Android.bp
@@ -2,12 +2,14 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
-rust_binary {
- name: "forwarder_host",
+rust_ffi_shared {
+ name: "libforwarder_host_jni",
+ crate_name: "forwarder_host",
edition: "2021",
- srcs: ["src/main.rs"],
+ srcs: ["src/forwarder_host.rs"],
rustlibs: [
"libforwarder",
+ "libjni",
"liblog_rust",
"libnix",
"libvmm_sys_util",
@@ -17,5 +19,8 @@
"libpoll_token_derive",
"libremain",
],
- static_executable: true,
+ apex_available: [
+ "com.android.virt",
+ ],
+ prefer_rlib: true,
}
diff --git a/android/forwarder_host/src/main.rs b/android/forwarder_host/src/forwarder_host.rs
similarity index 82%
rename from android/forwarder_host/src/main.rs
rename to android/forwarder_host/src/forwarder_host.rs
index b95b2cc..78f3555 100644
--- a/android/forwarder_host/src/main.rs
+++ b/android/forwarder_host/src/forwarder_host.rs
@@ -28,7 +28,10 @@
use std::time::Duration;
use forwarder::forwarder::ForwarderSession;
-use log::{error, warn};
+use jni::objects::{JObject, JValue};
+use jni::sys::jint;
+use jni::JNIEnv;
+use log::{debug, error, info, warn};
use nix::sys::eventfd::EventFd;
use poll_token_derive::PollToken;
use vmm_sys_util::poll::{PollContext, PollToken};
@@ -45,6 +48,7 @@
BindVsock(io::Error),
EventFdNew(nix::Error),
IncorrectCid(u32),
+ LaunchForwarderGuest(jni::errors::Error),
NoListenerForPort(u16),
NoSessionForTag(SessionTag),
PollContextAdd(vmm_sys_util::errno::Error),
@@ -53,9 +57,12 @@
PollWait(vmm_sys_util::errno::Error),
SetVsockNonblocking(io::Error),
TcpAccept(io::Error),
+ TcpListenerPort(io::Error),
UpdateEventRead(nix::Error),
+ UpdateEventWrite(nix::Error),
VsockAccept(io::Error),
VsockAcceptTimeout,
+ VsockListenerPort(io::Error),
}
type Result<T> = result::Result<T, Error>;
@@ -70,6 +77,7 @@
BindVsock(e) => write!(f, "failed to bind vsock: {}", e),
EventFdNew(e) => write!(f, "failed to create eventfd: {}", e),
IncorrectCid(cid) => write!(f, "chunnel connection from unexpected cid {}", cid),
+ LaunchForwarderGuest(e) => write!(f, "failed to launch forwarder_guest {}", e),
NoListenerForPort(port) => write!(f, "could not find listener for port: {}", port),
NoSessionForTag(tag) => write!(f, "could not find session for tag: {:x}", tag),
PollContextAdd(e) => write!(f, "failed to add fd to poll context: {}", e),
@@ -78,9 +86,14 @@
PollWait(e) => write!(f, "failed to wait for poll: {}", e),
SetVsockNonblocking(e) => write!(f, "failed to set vsock to nonblocking: {}", e),
TcpAccept(e) => write!(f, "failed to accept tcp: {}", e),
+ TcpListenerPort(e) => {
+ write!(f, "failed to read local sockaddr for tcp listener: {}", e)
+ }
UpdateEventRead(e) => write!(f, "failed to read update eventfd: {}", e),
+ UpdateEventWrite(e) => write!(f, "failed to write update eventfd: {}", e),
VsockAccept(e) => write!(f, "failed to accept vsock: {}", e),
VsockAcceptTimeout => write!(f, "timed out waiting for vsock connection"),
+ VsockListenerPort(e) => write!(f, "failed to get vsock listener port: {}", e),
}
}
}
@@ -120,24 +133,30 @@
}
/// ForwarderSessions encapsulates all forwarding state for chunneld.
-struct ForwarderSessions {
+struct ForwarderSessions<'a> {
listening_ports: BTreeMap<u16, PortListeners>,
tcp4_forwarders: HashMap<SessionTag, ForwarderSession>,
update_evt: EventFd,
update_queue: Arc<Mutex<VecDeque<TcpForwardTarget>>>,
+ jni_env: JNIEnv<'a>,
+ jni_cb: JObject<'a>,
}
-impl ForwarderSessions {
+impl<'a> ForwarderSessions<'a> {
/// Creates a new instance of ForwarderSessions.
fn new(
update_evt: EventFd,
update_queue: Arc<Mutex<VecDeque<TcpForwardTarget>>>,
+ jni_env: JNIEnv<'a>,
+ jni_cb: JObject<'a>,
) -> Result<Self> {
Ok(ForwarderSessions {
listening_ports: BTreeMap::new(),
tcp4_forwarders: HashMap::new(),
update_evt,
update_queue,
+ jni_env,
+ jni_cb,
})
}
@@ -217,7 +236,12 @@
// This session should be dropped if any of the PollContext setup fails. Since the only
// extant fds for the underlying sockets will be closed, they will be unregistered from
// epoll set automatically.
- let session = create_forwarder_session(listener, &port_listeners.forward_target)?;
+ let session = create_forwarder_session(
+ listener,
+ &port_listeners.forward_target,
+ &mut self.jni_env,
+ &self.jni_cb,
+ )?;
let tag = session.local_stream().as_raw_fd() as u32;
@@ -309,6 +333,8 @@
fn create_forwarder_session(
listener: &TcpListener,
target: &TcpForwardTarget,
+ jni_env: &mut JNIEnv,
+ jni_cb: &JObject,
) -> Result<ForwarderSession> {
let (tcp_stream, _) = listener.accept().map_err(Error::TcpAccept)?;
// Bind a vsock port, tell the guest to connect, and accept the connection.
@@ -316,6 +342,17 @@
.map_err(Error::BindVsock)?;
vsock_listener.set_nonblocking(true).map_err(Error::SetVsockNonblocking)?;
+ let tcp4_port = listener.local_addr().map_err(Error::TcpListenerPort)?.port();
+ let vsock_port = vsock_listener.local_addr().map_err(Error::VsockListenerPort)?.port();
+ jni_env
+ .call_method(
+ jni_cb,
+ "onForwardingRequestReceived",
+ "(II)V",
+ &[JValue::Int(tcp4_port.into()), JValue::Int(vsock_port as i32)],
+ )
+ .map_err(Error::LaunchForwarderGuest)?;
+
#[derive(PollToken)]
enum Token {
VsockAccept,
@@ -341,12 +378,50 @@
}
}
+fn update_listening_ports(
+ update_queue: &Arc<Mutex<VecDeque<TcpForwardTarget>>>,
+ update_evt: &EventFd,
+ cid: i32,
+) -> Result<()> {
+ let mut update_queue = update_queue.lock().unwrap();
+
+ // TODO(b/340126051): Bring listening ports from the guest.
+ update_queue.push_back(TcpForwardTarget {
+ port: 12345, /* Example value for testing */
+ vsock_cid: cid as u32,
+ });
+
+ update_evt.write(1).map_err(Error::UpdateEventWrite)?;
+ Ok(())
+}
+
// TODO(b/340126051): Host can receive opened ports from the guest.
-// TODO(b/340126051): Host can order executing chunnel on the guest.
-fn main() -> Result<()> {
+fn run_forwarder_host(cid: i32, jni_env: JNIEnv, jni_cb: JObject) -> Result<()> {
+ debug!("Starting forwarder_host");
let update_evt = EventFd::new().map_err(Error::EventFdNew)?;
let update_queue = Arc::new(Mutex::new(VecDeque::new()));
- let mut sessions = ForwarderSessions::new(update_evt, update_queue)?;
+ // TODO(b/340126051): Instead of one-time execution, bring port info with separated thread.
+ update_listening_ports(&update_queue, &update_evt, cid)?;
+
+ let mut sessions = ForwarderSessions::new(update_evt, update_queue, jni_env, jni_cb)?;
sessions.run()
}
+
+/// JNI function for running forwarder_host.
+#[no_mangle]
+pub extern "C" fn Java_com_android_virtualization_vmlauncher_DebianServiceImpl_runForwarderHost(
+ env: JNIEnv,
+ _class: JObject,
+ cid: jint,
+ callback: JObject,
+) {
+ match run_forwarder_host(cid, env, callback) {
+ Ok(_) => {
+ info!("forwarder_host is terminated");
+ }
+ Err(e) => {
+ error!("Error on forwarder_host: {:?}", e);
+ }
+ }
+}
diff --git a/guest/forwarder_guest_launcher/Cargo.toml b/guest/forwarder_guest_launcher/Cargo.toml
index bf0c0ed..03d3f7f 100644
--- a/guest/forwarder_guest_launcher/Cargo.toml
+++ b/guest/forwarder_guest_launcher/Cargo.toml
@@ -4,10 +4,12 @@
edition = "2021"
[dependencies]
+anyhow = "1.0.91"
clap = { version = "4.5.20", features = ["derive"] }
prost = "0.13.3"
-tokio = { version = "1.40.0", features = ["rt-multi-thread"] }
+tokio = { version = "1.40.0", features = ["process", "rt-multi-thread"] }
tonic = "0.12.3"
+vsock = "0.5.1"
[build-dependencies]
tonic-build = "0.12.3"
diff --git a/guest/forwarder_guest_launcher/src/main.rs b/guest/forwarder_guest_launcher/src/main.rs
index 4042fe5..59ee8c6 100644
--- a/guest/forwarder_guest_launcher/src/main.rs
+++ b/guest/forwarder_guest_launcher/src/main.rs
@@ -14,9 +14,11 @@
//! Launcher of forwarder_guest
+use anyhow::Context;
use clap::Parser;
use debian_service::debian_service_client::DebianServiceClient;
-use debian_service::Empty;
+use debian_service::QueueOpeningRequest;
+use tokio::process::Command;
use tonic::transport::Endpoint;
use tonic::Request;
@@ -35,16 +37,34 @@
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
+ println!("Starting forwarder_guest_launcher");
let args = Args::parse();
let addr = format!("https://{}:12000", args.host_addr);
let channel = Endpoint::from_shared(addr)?.connect().await?;
let mut client = DebianServiceClient::new(channel);
- let mut res_stream =
- client.open_forwarding_request_queue(Request::new(Empty {})).await?.into_inner();
+ let cid = vsock::get_local_cid().context("Failed to get CID of VM")?;
+ let mut res_stream = client
+ .open_forwarding_request_queue(Request::new(QueueOpeningRequest { cid: cid as i32 }))
+ .await?
+ .into_inner();
while let Some(response) = res_stream.message().await? {
- println!("Response from the host: {:?}", response);
+ let tcp_port = i16::try_from(response.guest_tcp_port)
+ .context("Failed to convert guest_tcp_port as i16")?;
+ let vsock_port = response.vsock_port as u32;
+
+ println!(
+ "executing forwarder_guest with guest_tcp_port: {:?}, vsock_port: {:?}",
+ &tcp_port, &vsock_port
+ );
+
+ let _ = Command::new("forwarder_guest")
+ .arg("--local")
+ .arg(format!("127.0.0.1:{}", tcp_port))
+ .arg("--remote")
+ .arg(format!("vsock:2:{}", vsock_port))
+ .spawn();
}
Ok(())
}
diff --git a/libs/debian_service/proto/DebianService.proto b/libs/debian_service/proto/DebianService.proto
index 5e3286a..a887bf2 100644
--- a/libs/debian_service/proto/DebianService.proto
+++ b/libs/debian_service/proto/DebianService.proto
@@ -23,10 +23,12 @@
service DebianService {
rpc ReportVmIpAddr (IpAddr) returns (ReportVmIpAddrResponse) {}
- rpc OpenForwardingRequestQueue (Empty) returns (stream ForwardingRequestItem) {}
+ rpc OpenForwardingRequestQueue (QueueOpeningRequest) returns (stream ForwardingRequestItem) {}
}
-message Empty {}
+message QueueOpeningRequest {
+ int32 cid = 1;
+}
message IpAddr {
string addr = 1;
diff --git a/libs/libforwarder/Android.bp b/libs/libforwarder/Android.bp
index 48307e7..1bcb111 100644
--- a/libs/libforwarder/Android.bp
+++ b/libs/libforwarder/Android.bp
@@ -12,4 +12,7 @@
"libvsock",
],
proc_macros: ["libremain"],
+ apex_available: [
+ "com.android.virt",
+ ],
}
diff --git a/libs/vm_launcher_lib/Android.bp b/libs/vm_launcher_lib/Android.bp
index f47f6b6..7dced4e 100644
--- a/libs/vm_launcher_lib/Android.bp
+++ b/libs/vm_launcher_lib/Android.bp
@@ -16,6 +16,7 @@
"apache-commons-compress",
],
libs: [
+ "androidx.annotation_annotation",
"framework-virtualization.impl",
"framework-annotations-lib",
],
diff --git a/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/DebianServiceImpl.java b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/DebianServiceImpl.java
index ccc0ed6..dcc8152 100644
--- a/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/DebianServiceImpl.java
+++ b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/DebianServiceImpl.java
@@ -18,10 +18,12 @@
import android.util.Log;
+import androidx.annotation.Keep;
+
import com.android.virtualization.vmlauncher.proto.DebianServiceGrpc;
-import com.android.virtualization.vmlauncher.proto.Empty;
import com.android.virtualization.vmlauncher.proto.ForwardingRequestItem;
import com.android.virtualization.vmlauncher.proto.IpAddr;
+import com.android.virtualization.vmlauncher.proto.QueueOpeningRequest;
import com.android.virtualization.vmlauncher.proto.ReportVmIpAddrResponse;
import io.grpc.stub.StreamObserver;
@@ -30,6 +32,10 @@
public static final String TAG = "DebianService";
private final DebianServiceCallback mCallback;
+ static {
+ System.loadLibrary("forwarder_host_jni");
+ }
+
protected DebianServiceImpl(DebianServiceCallback callback) {
super();
mCallback = callback;
@@ -47,14 +53,32 @@
@Override
public void openForwardingRequestQueue(
- Empty request, StreamObserver<ForwardingRequestItem> responseObserver) {
+ QueueOpeningRequest request, StreamObserver<ForwardingRequestItem> responseObserver) {
Log.d(DebianServiceImpl.TAG, "OpenForwardingRequestQueue");
-
- // TODO(b/340126051): Bring information from forwarder_host.
-
+ runForwarderHost(request.getCid(), new ForwarderHostCallback(responseObserver));
responseObserver.onCompleted();
}
+ @Keep
+ private static class ForwarderHostCallback {
+ private StreamObserver<ForwardingRequestItem> mResponseObserver;
+
+ ForwarderHostCallback(StreamObserver<ForwardingRequestItem> responseObserver) {
+ mResponseObserver = responseObserver;
+ }
+
+ private void onForwardingRequestReceived(int guestTcpPort, int vsockPort) {
+ ForwardingRequestItem item =
+ ForwardingRequestItem.newBuilder()
+ .setGuestTcpPort(guestTcpPort)
+ .setVsockPort(vsockPort)
+ .build();
+ mResponseObserver.onNext(item);
+ }
+ }
+
+ private static native void runForwarderHost(int cid, ForwarderHostCallback callback);
+
protected interface DebianServiceCallback {
void onIpAddressAvailable(String ipAddr);
}