Host can send required infos to the guest for performing forwarding.
Since forwarder_guest_launcher is not implemented in 100%, we should
manually run forwarder_guest_launcher with `--host-addr 192.168.0.1`,
and accessing `http://localhost:12345` to check the log printed at
forwarder_guest_launcher.
Bug: 340126051
Test: Manual test described above.
Change-Id: If3a7784fb29febc3fed8653297fa6c840ef22b49
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..a32ce20 100644
--- a/android/forwarder_host/src/main.rs
+++ b/android/forwarder_host/src/forwarder_host.rs
@@ -28,7 +28,9 @@
use std::time::Duration;
use forwarder::forwarder::ForwarderSession;
-use log::{error, warn};
+use jni::objects::{JObject, JValue};
+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 +47,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 +56,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 +76,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 +85,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 +132,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 +235,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 +332,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 +341,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 +377,49 @@
}
}
+fn update_listening_ports(
+ update_queue: &Arc<Mutex<VecDeque<TcpForwardTarget>>>,
+ update_evt: &EventFd,
+) -> 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: 157, /* Placeholder value */
+ });
+
+ 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(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)?;
+
+ 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,
+ callback: JObject,
+) {
+ match run_forwarder_host(env, callback) {
+ Ok(_) => {
+ info!("forwarder_host is terminated");
+ }
+ Err(e) => {
+ error!("Error on forwarder_host: {:?}", e);
+ }
+ }
+}
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..d62ccfb 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,6 +18,8 @@
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;
@@ -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;
@@ -49,12 +55,30 @@
public void openForwardingRequestQueue(
Empty request, StreamObserver<ForwardingRequestItem> responseObserver) {
Log.d(DebianServiceImpl.TAG, "OpenForwardingRequestQueue");
-
- // TODO(b/340126051): Bring information from forwarder_host.
-
+ runForwarderHost(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(ForwarderHostCallback callback);
+
protected interface DebianServiceCallback {
void onIpAddressAvailable(String ipAddr);
}