Skip to content

Tsi: handle protocol field to make DGRAM+ICMP ping work #690

Draft
mtjhrc wants to merge 5 commits into
containers:mainfrom
mtjhrc:tsi-icmp-ping
Draft

Tsi: handle protocol field to make DGRAM+ICMP ping work #690
mtjhrc wants to merge 5 commits into
containers:mainfrom
mtjhrc:tsi-icmp-ping

Conversation

@mtjhrc
Copy link
Copy Markdown
Collaborator

@mtjhrc mtjhrc commented May 21, 2026

Three things were missing for ping to work through TSI:

  1. Host used to create UDP sockets instead of ICMP — now reads the protocol field from the guest - needs tsi: forward protocol field in proxy create enabling DGRAM+ICMP ping libkrunfw#127

  2. Guest used to reject SOCK_DGRAM+ICMP with EACCES — init now sets ping_group_range

  3. macOS host includes the IP header in ICMP recv — we need to strip it before forwarding to guest

NOTE: This only works with modern ping implementations using DGRAM+ICMP "ping" sockets ( works on modern Fedora, Ubuntu). Busybox ping uses SOCK_RAW which TSI doesn't hijack.

mtjhrc added 5 commits May 21, 2026 17:58
Read the protocol field from TsiProxyCreate (backward compatible —
old guests that don't send it get 0, treated as default/UDP).

When protocol is IPPROTO_ICMP or IPPROTO_ICMPV6, create a ping
socket (SOCK_DGRAM + IPPROTO_ICMP) on the host instead of a plain
UDP socket. This enables rootless ping through TSI.

Only works for ping implementations using SOCK_DGRAM ping sockets
(iputils on Fedora, Ubuntu, etc.). SOCK_RAW-based ping (busybox,
alpine) is not supported as TSI only hijacks SOCK_STREAM and
SOCK_DGRAM.

Assisted-by: OpenCode:claude-opus-4.6
Signed-off-by: Matej Hrica <mhrica@redhat.com>
Tests that need real network connectivity (e.g. external ping) can
return true from needs_host_network() to skip the unshare --net
namespace isolation automatically.

Assisted-by: OpenCode:claude-opus-4.6
Signed-off-by: Matej Hrica <mhrica@redhat.com>
Run Fedora's /usr/bin/ping against 8.8.8.8 from inside the guest to
verify SOCK_DGRAM+IPPROTO_ICMP is properly proxied through TSI.

Uses needs_host_network() since the test requires real connectivity.

Assisted-by: OpenCode:claude-opus-4.6
Signed-off-by: Matej Hrica <mhrica@redhat.com>
Set /proc/sys/net/ipv4/ping_group_range to allow all GIDs when TSI
is enabled. Without this, the guest kernel rejects SOCK_DGRAM +
IPPROTO_ICMP sockets with EACCES, preventing TSI from hijacking
them.

Assisted-by: OpenCode:claude-opus-4.6
Signed-off-by: Matej Hrica <mhrica@redhat.com>
macOS DGRAM ICMP sockets include the IP header in recv, unlike Linux
which strips it. Detect this and remove the IP header before
forwarding to the guest so the guest sees the same format as a Linux
ping socket.

Assisted-by: OpenCode:claude-opus-4.6
Signed-off-by: Matej Hrica <mhrica@redhat.com>
@mtjhrc
Copy link
Copy Markdown
Collaborator Author

mtjhrc commented May 29, 2026

/gemini review

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request adds support for proxying unprivileged ICMP ping sockets (SOCK_DGRAM with IPPROTO_ICMP or IPPROTO_ICMPV6) via TSI. It configures the ping group range during initialization, extends the TSI protocol to transmit the socket protocol, and implements macOS-specific IP header stripping for received ICMP packets. A new integration test is also introduced. Review feedback points out that the macOS IP header stripping logic should not be applied to ICMPv6, as macOS does not prepend the IPv6 header to ICMPv6 socket data. Additionally, the test code should use String::from_utf8(...).unwrap() instead of String::from_utf8_lossy(...) to ensure immediate failure on invalid UTF-8.

Comment on lines +196 to +199
if matches!(
self.protocol as _,
libc::IPPROTO_ICMP | libc::IPPROTO_ICMPV6
) && cnt >= 20
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

On macOS, SOCK_DGRAM sockets with IPPROTO_ICMPV6 do not prepend the IPv6 header to the received data (unlike IPv4 ICMP sockets which prepend the IPv4 header). Furthermore, the IPv6 header does not have an Internet Header Length (IHL) field at buf[0] & 0x0F.

Including IPPROTO_ICMPV6 in this stripping logic will cause the code to incorrectly strip bytes from the actual ICMPv6 header (for example, corrupting the ICMPv6 Echo Reply type/code), leading to broken IPv6 ping functionality on macOS. This stripping logic should only be applied to IPPROTO_ICMP (IPv4).

                    if self.protocol as i32 == libc::IPPROTO_ICMP && cnt >= 20

Comment on lines +75 to +76
let stderr = String::from_utf8_lossy(&output.stderr);
let stdout = String::from_utf8_lossy(&output.stdout);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

According to the repository's general rules, test code should prefer using String::from_utf8(...).unwrap() over String::from_utf8_lossy(...) when processing command output. This ensures that the test fails immediately if the output contains invalid UTF-8.

Suggested change
let stderr = String::from_utf8_lossy(&output.stderr);
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8(output.stderr).unwrap();
let stdout = String::from_utf8(output.stdout).unwrap();
References
  1. In test code, prefer using String::from_utf8(...).unwrap() over String::from_utf8_lossy(...) when processing command output to ensure the test fails immediately if the output contains invalid UTF-8.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant