diff --git a/.local/bin/tmux-ip b/.local/bin/tmux-ip index 4af64dd0..29c742c6 100755 --- a/.local/bin/tmux-ip +++ b/.local/bin/tmux-ip @@ -1,34 +1,53 @@ #!/bin/bash # # Print the "best" IP for the tmux status bar. -# Preference: DO reserved/floating IP > public egress (ipify) > first local IP. +# +# Shows a directly-held public/WAN IP when the machine has one on an interface, +# otherwise its LAN/private IP. No external "what's my IP" lookup -- a NAT'd box +# shows its LAN IP, never the gateway's egress. Works the same everywhere: a box +# with a public IP on a NIC (incl. a cloud droplet's eth0) shows that WAN IP; a +# NAT'd box shows its LAN IP. The VPN overlay is added separately by tmux-net via +# tmux-vpn-ip and is untouched here. # set -u -META="http://169.254.169.254/metadata/v1" -CURL="curl -sf --connect-timeout 1 --max-time 2" - -is_digitalocean() { - [ -r /sys/class/dmi/id/sys_vendor ] \ - && grep -qi digitalocean /sys/class/dmi/id/sys_vendor +# This machine's own non-loopback IPv4 addresses, one per line. VPN tunnels are +# excluded (tmux-net surfaces the VPN address separately as the ⇡ segment). +list_ipv4() { + if command -v ip >/dev/null 2>&1; then + ip -4 -o addr show scope global 2>/dev/null \ + | awk '$2 !~ /^(tun|wg|ppp|tap)/ {print $4}' | cut -d/ -f1 + elif hostname -I >/dev/null 2>&1; then + hostname -I 2>/dev/null | tr ' ' '\n' + else + ifconfig 2>/dev/null | awk '/inet /{print $2}' + fi | grep -E '^([0-9]{1,3}\.){3}[0-9]{1,3}$' | grep -v '^127\.' } -do_anchor_ip() { - local kind active ip - for kind in reserved_ip floating_ip; do - active=$($CURL "$META/$kind/ipv4/active" 2>/dev/null) || continue - [ "$active" = "true" ] || continue - ip=$($CURL "$META/$kind/ipv4/ip_address" 2>/dev/null) || continue - [ -n "$ip" ] && { printf '%s\n' "$ip"; return 0; } - done +# RFC1918 / link-local / CGNAT -- not a directly-routable WAN address. +is_private_ipv4() { + case "$1" in + 10.*|192.168.*|169.254.*) return 0 ;; + 172.1[6-9].*|172.2[0-9].*|172.3[01].*) return 0 ;; + 100.6[4-9].*|100.[7-9][0-9].*|100.1[01][0-9].*|100.12[0-7].*) return 0 ;; + *) return 1 ;; + esac +} + +# First directly-held public/WAN IP, if the machine has one on an interface. +public_ip() { + local ip + while IFS= read -r ip; do + [ -n "$ip" ] || continue + is_private_ipv4 "$ip" || { printf '%s\n' "$ip"; return 0; } + done </dev/null 2>&1; then @@ -44,6 +63,7 @@ print_local_ip() { ifconfig 2>/dev/null | awk '/inet /{ if ($2 != "127.0.0.1") { print $2; exit } }' } -ip=$(curl -s --connect-timeout 3 https://api.ipify.org 2>/dev/null || true) +# A directly-held public/WAN IP if the box has one, else the LAN IP. +ip=$(public_ip || true) [ -z "${ip:-}" ] && ip=$(print_local_ip) printf '%s\n' "${ip:-}" diff --git a/.local/bin/tmux-net b/.local/bin/tmux-net index c4f1d74d..0dd6e558 100755 --- a/.local/bin/tmux-net +++ b/.local/bin/tmux-net @@ -3,6 +3,7 @@ # Emit the tmux status-bar network segment. # No VPN: "⌂ " # VPN up: "⌂ / ⇡ " +# Offline: "✕" (no local IP and no VPN tunnel) # set -u @@ -10,7 +11,9 @@ set -u local_ip=$("$HOME/.local/bin/tmux-ip" 2>/dev/null || true) vpn_ip=$("$HOME/.local/bin/tmux-vpn-ip" 2>/dev/null || true) -if [ -n "${vpn_ip:-}" ]; then +if [ -z "${local_ip:-}" ] && [ -z "${vpn_ip:-}" ]; then + printf '\xe2\x9c\x95\n' # ✕ no active connection +elif [ -n "${vpn_ip:-}" ]; then printf '\xe2\x8c\x82 %s / \xe2\x87\xa1 %s\n' "${local_ip:-}" "$vpn_ip" else printf '\xe2\x8c\x82 %s\n' "${local_ip:-}" diff --git a/.tmux.conf b/.tmux.conf index 87a48e80..43d0a9ec 100644 --- a/.tmux.conf +++ b/.tmux.conf @@ -80,8 +80,8 @@ set -g pane-active-border-style fg=colour39 set -g message-style fg=colour16,bg=colour221,bold set -g status-left '#[fg=colour235,bg=colour252,bold] ❐ #S #[fg=colour252,bg=colour238,nobold]❯#[fg=colour245,bg=colour238,bold] #(whoami) ' set -g status-right '#[bold][#[nobold,fg=colour229]#h#[fg=default] / #[fg=colour229]#(~/.local/bin/tmux-net)#[fg=default,bold]]#[nobold,fg=colour255] %-I:%M%P %d-%b-%Y ' -set -g window-status-format '#[fg=colour235,bg=colour252,nobold] #(~/.local/bin/tmux-window-icon #{window_index}) #(pwd="#{pane_current_path}"; echo ${pwd####*/}) #W ' -set -g window-status-current-format '#[fg=colour234,bg=colour39,bold] [#[fg=colour232,bold]#{?window_zoomed_flag,#[fg=colour228],} #(~/.local/bin/tmux-window-icon #{window_index}) #(pwd="#{pane_current_path}"; echo ${pwd####*/}) #W #[fg=colour234,bold]] ' +set -g window-status-format '#[fg=colour235,bg=colour252,nobold] #{?#{==:#{window_index},1},❶,#{?#{==:#{window_index},2},❷,#{?#{==:#{window_index},3},❸,#{?#{==:#{window_index},4},❹,#{?#{==:#{window_index},5},❺,#{?#{==:#{window_index},6},❻,#{?#{==:#{window_index},7},❼,#{?#{==:#{window_index},8},❽,#{?#{==:#{window_index},9},❾,#{?#{==:#{window_index},10},❿,#{window_index}}}}}}}}}}} #{b:pane_current_path} #W ' +set -g window-status-current-format '#[fg=colour234,bg=colour39,bold] [#[fg=colour232,bold]#{?window_zoomed_flag,#[fg=colour228],} #{?#{==:#{window_index},1},❶,#{?#{==:#{window_index},2},❷,#{?#{==:#{window_index},3},❸,#{?#{==:#{window_index},4},❹,#{?#{==:#{window_index},5},❺,#{?#{==:#{window_index},6},❻,#{?#{==:#{window_index},7},❼,#{?#{==:#{window_index},8},❽,#{?#{==:#{window_index},9},❾,#{?#{==:#{window_index},10},❿,#{window_index}}}}}}}}}}} #{b:pane_current_path} #W #[fg=colour234,bold]] ' set-option -g status-interval 60 # Patch for OS X pbpaste and pbcopy under tmux.