dotfiles/private_dot_local/bin/executable_ping
Anton Volnuhin af05b9e245 ping: check IPv6 is actually usable, and split ping4 per platform
The ping wrapper only asked whether the *target* had an AAAA record, never
whether this machine could reach over IPv6, so `ping ya.ru` ran ping6 and
died with "No route to host" while IPv4 worked fine.

Checking for a default v6 route is not sufficient either: this Mac has one
while holding only ::1 and fe80:: link-locals. So require a global unicast
address (2000::/3) as well as the route, which is the condition that
actually fails here.

ping4 is now platform-split rather than branching internally, since macOS
ping rejects -4:
  macOS  ~/.local/bin/ping (wrapper) and ~/.local/bin/ping4 -> /sbin/ping
  Linux  a fish function running `ping -4`
.chezmoiignore gates each side so neither lands where it cannot work.
2026-08-06 03:38:12 +03:00

49 lines
1.3 KiB
Bash

#!/bin/sh
# Unified ping, native output preserved.
#
# macOS still ships separate ping/ping6 binaries, so this picks one the way
# Linux's ping does: IPv6 when the target has an AAAA record *and* this machine
# can actually reach over IPv6, IPv4 otherwise.
v6_usable() {
# A default route on its own is not enough. A Mac with IPv6 switched off
# can still show a default v6 route while holding only ::1 and fe80::
# link-local addresses, and ping6 then dies with "No route to host".
# Global unicast is 2000::/3, so require an address starting with 2 or 3.
route -n get -inet6 default >/dev/null 2>&1 || return 1
ifconfig 2>/dev/null | awk '/inet6 /{ print $2 }' | grep -qE '^[23]'
}
case "$1" in
-6)
shift
exec /sbin/ping6 "$@"
;;
-4)
shift
exec /sbin/ping "$@"
;;
*:*)
# An explicit IPv6 literal: honour it and let ping6 report any problem.
exec /sbin/ping6 "$@"
;;
esac
# First non-option argument is the target.
for a in "$@"; do
case "$a" in
-*) ;;
*)
t="$a"
break
;;
esac
done
if [ -n "$t" ] && v6_usable &&
dscacheutil -q host -a name "$t" 2>/dev/null | grep -q ipv6_address; then
exec /sbin/ping6 "$@"
fi
exec /sbin/ping "$@"