fix(network): priorizar Ethernet e APIPA no multicast
LocalNetworkInfo prefere cabo antes de Wi-Fi e 169.254.x na interface; documenta escolha de interface no apendice de multicast. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
39c1e504bb
commit
8fe9bee453
2 files changed
+77
-13
No files matched your search
@@ -29,6 +29,10 @@ Executar o Pitiupi no WSL e peers no Windows (ou vice-versa) pode falhar em mult
|
|||||||
- todos os peers no mesmo ambiente (Linux nativo ou VMs), ou
|
- todos os peers no mesmo ambiente (Linux nativo ou VMs), ou
|
||||||
- dois PCs físicos na mesma sub-rede.
|
- dois PCs físicos na mesma sub-rede.
|
||||||
|
|
||||||
|
## Escolha de interface (multicast)
|
||||||
|
|
||||||
|
O núcleo associa o socket multicast à interface de rede preferida: **cabo (Ethernet) antes de Wi‑Fi**; em cabo com link-local APIPA, prioriza IPv4 **`169.254.*`**. Isso não substitui a exigência de **mesma sub-rede** entre os dois PCs — `169.254.x` e `192.168.x` ao mesmo tempo em hosts diferentes não compartilham heartbeat.
|
||||||
|
|
||||||
## Checklist — dois PCs na mesma Wi‑Fi
|
## Checklist — dois PCs na mesma Wi‑Fi
|
||||||
|
|
||||||
1. **Mesma porta no login** (ex.: `5000` nos dois). Portas diferentes isolam a sala.
|
1. **Mesma porta no login** (ex.: `5000` nos dois). Portas diferentes isolam a sala.
|
||||||
|
|||||||
@@ -11,14 +11,19 @@ import java.util.Locale;
|
|||||||
*/
|
*/
|
||||||
public final class LocalNetworkInfo {
|
public final class LocalNetworkInfo {
|
||||||
|
|
||||||
|
private static final String APIPA_PREFIX = "169.254.";
|
||||||
|
|
||||||
private LocalNetworkInfo() {
|
private LocalNetworkInfo() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface preferida para LAN (Wi‑Fi/cabo), ignorando bridges Docker/WSL/VPN virtuais quando possível.
|
* Interface preferida para LAN: cabo antes de Wi‑Fi; APIPA (169.254.x) antes de outro IPv4 no cabo.
|
||||||
*/
|
*/
|
||||||
public static NetworkInterface preferredNetworkInterface() {
|
public static NetworkInterface preferredNetworkInterface() {
|
||||||
try {
|
try {
|
||||||
|
NetworkInterface wiredApipa = null;
|
||||||
|
NetworkInterface wiredOther = null;
|
||||||
|
NetworkInterface wireless = null;
|
||||||
NetworkInterface fallback = null;
|
NetworkInterface fallback = null;
|
||||||
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
|
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
|
||||||
while (interfaces.hasMoreElements()) {
|
while (interfaces.hasMoreElements()) {
|
||||||
@@ -26,12 +31,30 @@ public final class LocalNetworkInfo {
|
|||||||
if (!isCandidateInterface(ni)) {
|
if (!isCandidateInterface(ni)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (isPreferredLanInterfaceName(ni.getName())) {
|
|
||||||
return ni;
|
|
||||||
}
|
|
||||||
if (fallback == null) {
|
if (fallback == null) {
|
||||||
fallback = ni;
|
fallback = ni;
|
||||||
}
|
}
|
||||||
|
String name = ni.getName();
|
||||||
|
if (isWiredLanInterfaceName(name)) {
|
||||||
|
if (hasApipaIpv4(ni)) {
|
||||||
|
if (wiredApipa == null) {
|
||||||
|
wiredApipa = ni;
|
||||||
|
}
|
||||||
|
} else if (wiredOther == null) {
|
||||||
|
wiredOther = ni;
|
||||||
|
}
|
||||||
|
} else if (isWirelessLanInterfaceName(name) && wireless == null) {
|
||||||
|
wireless = ni;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (wiredApipa != null) {
|
||||||
|
return wiredApipa;
|
||||||
|
}
|
||||||
|
if (wiredOther != null) {
|
||||||
|
return wiredOther;
|
||||||
|
}
|
||||||
|
if (wireless != null) {
|
||||||
|
return wireless;
|
||||||
}
|
}
|
||||||
return fallback;
|
return fallback;
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
@@ -43,7 +66,7 @@ public final class LocalNetworkInfo {
|
|||||||
try {
|
try {
|
||||||
NetworkInterface preferred = preferredNetworkInterface();
|
NetworkInterface preferred = preferredNetworkInterface();
|
||||||
if (preferred != null) {
|
if (preferred != null) {
|
||||||
String fromPreferred = firstIpv4OnInterface(preferred);
|
String fromPreferred = bestIpv4OnInterface(preferred);
|
||||||
if (fromPreferred != null) {
|
if (fromPreferred != null) {
|
||||||
return fromPreferred;
|
return fromPreferred;
|
||||||
}
|
}
|
||||||
@@ -54,7 +77,7 @@ public final class LocalNetworkInfo {
|
|||||||
if (!isCandidateInterface(ni)) {
|
if (!isCandidateInterface(ni)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
String ip = firstIpv4OnInterface(ni);
|
String ip = bestIpv4OnInterface(ni);
|
||||||
if (ip != null) {
|
if (ip != null) {
|
||||||
return ip;
|
return ip;
|
||||||
}
|
}
|
||||||
@@ -80,7 +103,7 @@ public final class LocalNetworkInfo {
|
|||||||
if (ni.isVirtual() || isVirtualInterfaceName(ni.getName())) {
|
if (ni.isVirtual() || isVirtualInterfaceName(ni.getName())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return firstIpv4OnInterface(ni) != null;
|
return bestIpv4OnInterface(ni) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isVirtualInterfaceName(String name) {
|
private static boolean isVirtualInterfaceName(String name) {
|
||||||
@@ -97,18 +120,55 @@ public final class LocalNetworkInfo {
|
|||||||
|| n.startsWith("tailscale");
|
|| n.startsWith("tailscale");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isPreferredLanInterfaceName(String name) {
|
private static boolean isWiredLanInterfaceName(String name) {
|
||||||
|
if (name == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String n = name.toLowerCase(Locale.ROOT);
|
||||||
|
return n.startsWith("eth")
|
||||||
|
|| n.startsWith("eno")
|
||||||
|
|| n.startsWith("enp")
|
||||||
|
|| n.startsWith("ens");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isWirelessLanInterfaceName(String name) {
|
||||||
if (name == null) {
|
if (name == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
String n = name.toLowerCase(Locale.ROOT);
|
String n = name.toLowerCase(Locale.ROOT);
|
||||||
return n.startsWith("wlan")
|
return n.startsWith("wlan")
|
||||||
|| n.startsWith("wlp")
|
|| n.startsWith("wlp")
|
||||||
|| n.startsWith("wl")
|
|| n.startsWith("wl");
|
||||||
|| n.startsWith("eth")
|
}
|
||||||
|| n.startsWith("eno")
|
|
||||||
|| n.startsWith("enp")
|
private static boolean hasApipaIpv4(NetworkInterface ni) {
|
||||||
|| n.startsWith("ens");
|
return apipaIpv4OnInterface(ni) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String apipaIpv4OnInterface(NetworkInterface ni) {
|
||||||
|
try {
|
||||||
|
Enumeration<InetAddress> addresses = ni.getInetAddresses();
|
||||||
|
while (addresses.hasMoreElements()) {
|
||||||
|
InetAddress addr = addresses.nextElement();
|
||||||
|
if (addr instanceof Inet4Address && !addr.isLoopbackAddress()) {
|
||||||
|
String host = addr.getHostAddress();
|
||||||
|
if (host != null && host.startsWith(APIPA_PREFIX)) {
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String bestIpv4OnInterface(NetworkInterface ni) {
|
||||||
|
String apipa = apipaIpv4OnInterface(ni);
|
||||||
|
if (apipa != null) {
|
||||||
|
return apipa;
|
||||||
|
}
|
||||||
|
return firstIpv4OnInterface(ni);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String firstIpv4OnInterface(NetworkInterface ni) {
|
private static String firstIpv4OnInterface(NetworkInterface ni) {
|
||||||
|
|||||||
Reference in new issue
Block a user