Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions vm/devices/net/net_consomme/consomme/src/tcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,18 @@ impl<T: Client> Access<'_, T> {
}
}
}

// Replace the loopback IP with the gateway IP so
// the guest's reply routes back through the virtual
// adapter instead of its own loopback interface.
match &mut other_addr {
SocketAddr::V4(v4) => {
v4.set_ip(self.inner.state.params.gateway_ip);
}
SocketAddr::V6(v6) => {
v6.set_ip(self.inner.state.params.gateway_link_local_ipv6);
}
}
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are existing TCP listener forwarding tests, but none appear to assert the new loopback-to-gateway source-IP rewrite. Consider updating test_tcp_bind_port_forward (or adding a dedicated test) to parse the forwarded SYN and confirm its source IP is the gateway when the host connector is loopback.

Copilot uses AI. Check for mistakes.
}

let ft = match other_addr {
Expand Down
34 changes: 30 additions & 4 deletions vm/devices/net/net_consomme/consomme/src/udp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,19 @@ impl UdpConnection {
},
) {
Poll::Ready(Ok((n, src_addr))) => {
// Replace loopback source IPs with the gateway IP so
// the guest's reply routes back through the virtual
// adapter instead of its own loopback interface.
let (packet_len, checksum_state) = match (dst_addr, src_addr.ip()) {
(SocketAddr::V4(dst), IpAddr::V4(src_ip)) => {
let effective_src_ip: IpAddress = if src_ip.is_loopback() {
state.params.gateway_ip.into()
} else {
src_ip.into()
};
let len = build_udp_packet(
&mut eth,
src_ip.into(),
effective_src_ip,
Comment on lines +165 to +177
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replacing a loopback source IP with gateway_ip changes the remote address the guest sees. For UDP, replies will typically be sent to the packet’s source IP; that means the guest will reply to gateway_ip, and handle_udp() will then send the response to gateway_ip:<port> (no special-case remap exists beyond DHCP/DNS). This likely breaks bidirectional UDP flows involving host loopback senders. Consider adding a reverse mapping on egress (e.g., when dst is gateway_ip/gateway_link_local_ipv6 and port matches a forwarded flow) or preserving the original loopback IP while fixing routing another way.

Copilot uses AI. Check for mistakes.
Comment on lines +165 to +177
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This behavior change (rewriting loopback source IPs to the gateway) isn’t currently asserted by the existing UDP forwarding tests. Consider extending test_udp_bind_port_forward to validate the forwarded packet’s IP source address is rewritten as expected (and ideally add a regression test that a guest reply can be forwarded back to the host sender).

Copilot uses AI. Check for mistakes.
(*dst.ip()).into(),
src_addr.port(),
dst.port(),
Expand All @@ -177,9 +185,14 @@ impl UdpConnection {
(len, ChecksumState::UDP4)
}
(SocketAddr::V6(dst), IpAddr::V6(src_ip)) => {
let effective_src_ip: IpAddress = if src_ip.is_loopback() {
state.params.gateway_link_local_ipv6.into()
} else {
src_ip.into()
};
let len = build_udp_packet(
&mut eth,
src_ip.into(),
effective_src_ip,
(*dst.ip()).into(),
src_addr.port(),
dst.port(),
Expand Down Expand Up @@ -254,11 +267,19 @@ impl UdpListener {
.recv_from(&mut eth.payload_mut()[header_offset..])
}) {
Poll::Ready(Ok((n, src_addr))) => {
// Replace loopback source IPs with the gateway IP so
// the guest's reply routes back through the virtual
// adapter instead of its own loopback interface.
let Some((packet_len, checksum_state)) = (match (guest_dst_ip, src_addr.ip()) {
(IpAddr::V4(dst_ip), IpAddr::V4(src_ip)) => {
let effective_src_ip: IpAddress = if src_ip.is_loopback() {
state.params.gateway_ip.into()
} else {
src_ip.into()
};
let len = build_udp_packet(
&mut eth,
src_ip.into(),
effective_src_ip,
dst_ip.into(),
src_addr.port(),
self.guest_port,
Expand All @@ -269,9 +290,14 @@ impl UdpListener {
Some((len, ChecksumState::UDP4))
}
(IpAddr::V6(dst_ip), IpAddr::V6(src_ip)) => {
let effective_src_ip: IpAddress = if src_ip.is_loopback() {
state.params.gateway_link_local_ipv6.into()
} else {
src_ip.into()
};
let len = build_udp_packet(
&mut eth,
src_ip.into(),
effective_src_ip,
dst_ip.into(),
src_addr.port(),
self.guest_port,
Comment on lines 270 to 303
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same concern as above for inbound UDP port-forwarding: rewriting a loopback src_ip to the gateway makes the guest’s response target the gateway address, but the egress path will send to that gateway address rather than the original host loopback sender (no tracking/remap is performed). If the intent is to treat gateway as an alias for host loopback, the egress path needs an explicit rewrite/mapping for these flows.

Copilot uses AI. Check for mistakes.
Expand Down
Loading