|
25 | 25 | "Number of sequential ports to try before giving up." |
26 | 26 | 20) |
27 | 27 |
|
| 28 | +(def ^:private virtual-interface-re |
| 29 | + "Matches network interface names that are typically virtual/software-defined. |
| 30 | + These are deprioritized when detecting the LAN IP." |
| 31 | + #"^(docker|br-|veth|vbox|virbr|tailscale|lo|tun|tap|wg|zt)") |
| 32 | + |
| 33 | +(defn ^:private interface-priority |
| 34 | + "Returns a sort priority for a network interface (lower = preferred). |
| 35 | + Real hardware interfaces (wifi, ethernet) are preferred over virtual ones." |
| 36 | + ^long [^NetworkInterface ni] |
| 37 | + (let [name (.getName ni)] |
| 38 | + (if (re-find virtual-interface-re name) 1 0))) |
| 39 | + |
28 | 40 | (defn ^:private detect-lan-ip |
29 | 41 | "Enumerates network interfaces to find a site-local (private) IPv4 address. |
| 42 | + Prefers real hardware interfaces (wifi, ethernet) over virtual ones (docker, vbox). |
30 | 43 | Returns the IP string or nil when none is found." |
31 | 44 | [] |
32 | 45 | (try |
33 | 46 | (->> (enumeration-seq (NetworkInterface/getNetworkInterfaces)) |
34 | 47 | (filter (fn [^NetworkInterface ni] |
35 | 48 | (and (.isUp ni) |
36 | | - (not (.isLoopback ni))))) |
| 49 | + (not (.isLoopback ni)) |
| 50 | + (not (.isVirtual ni))))) |
| 51 | + (sort-by interface-priority) |
37 | 52 | (mapcat (fn [^NetworkInterface ni] |
38 | | - (enumeration-seq (.getInetAddresses ni)))) |
39 | | - (filter (fn [^InetAddress addr] |
40 | | - (and (instance? Inet4Address addr) |
41 | | - (.isSiteLocalAddress addr)))) |
42 | | - (some (fn [^InetAddress addr] (.getHostAddress addr)))) |
| 53 | + (->> (enumeration-seq (.getInetAddresses ni)) |
| 54 | + (filter (fn [^InetAddress addr] |
| 55 | + (and (instance? Inet4Address addr) |
| 56 | + (.isSiteLocalAddress addr)))) |
| 57 | + (map (fn [^InetAddress addr] (.getHostAddress addr)))))) |
| 58 | + first) |
43 | 59 | (catch Exception _ nil))) |
44 | 60 |
|
45 | 61 | (defn ^:private detect-host |
|
0 commit comments