Add support for firstipv4 and firstipv6 interface binding modes with tests

This commit is contained in:
Pavel Siomachkin 2025-10-28 11:10:58 +01:00
parent d7322226c1
commit 595b404fe0
2 changed files with 23 additions and 1 deletions

View file

@ -1049,6 +1049,8 @@ func isInterfaceName(s string) bool {
// Check if the last part is a valid binding mode
lastPart := parts[2]
if lastPart == string(InterfaceBindingAuto) ||
lastPart == string(InterfaceBindingFirstIPv4) ||
lastPart == string(InterfaceBindingFirstIPv6) ||
lastPart == string(InterfaceBindingIPv4) ||
lastPart == string(InterfaceBindingIPv6) ||
lastPart == string(InterfaceBindingAll) {
@ -1113,6 +1115,8 @@ func tryParseInterfaceWithModeInHost(host string) (interfaceWithMode, bool) {
// Check if the last part is a valid binding mode
if parts[2] != string(InterfaceBindingAuto) &&
parts[2] != string(InterfaceBindingFirstIPv4) &&
parts[2] != string(InterfaceBindingFirstIPv6) &&
parts[2] != string(InterfaceBindingIPv4) &&
parts[2] != string(InterfaceBindingIPv6) &&
parts[2] != string(InterfaceBindingAll) {
@ -1162,6 +1166,10 @@ func parseInterfaceAddress(network, host, port string) (NetworkAddress, error) {
switch modeStr {
case "auto":
mode = InterfaceBindingAuto
case "firstipv4":
mode = InterfaceBindingFirstIPv4
case "firstipv6":
mode = InterfaceBindingFirstIPv6
case "ipv4":
mode = InterfaceBindingIPv4
case "ipv6":
@ -1169,7 +1177,7 @@ func parseInterfaceAddress(network, host, port string) (NetworkAddress, error) {
case "all":
mode = InterfaceBindingAll
default:
return NetworkAddress{}, fmt.Errorf("unknown interface binding mode: %s (supported: auto, ipv4, ipv6, all)", modeStr)
return NetworkAddress{}, fmt.Errorf("unknown interface binding mode: %s (supported: auto, firstipv4, firstipv6, ipv4, ipv6, all)", modeStr)
}
}
} else {

View file

@ -149,6 +149,10 @@ func TestIsInterfaceNameWithModes(t *testing.T) {
{"br-901e40e4488d:3000:ipv6", true, "docker bridge with IPv6 mode"},
{"veth1308dcd:8080:auto", true, "veth pair with auto mode"},
{"eth0:8080:all", true, "ethernet interface with all mode"},
{"eth0:8080:firstipv4", true, "ethernet interface with firstipv4 mode"},
{"wlan0:443:firstipv6", true, "wireless interface with firstipv6 mode"},
{"docker0:9000:firstipv4", true, "docker interface with firstipv4 mode"},
{"enp0s3:8080:firstipv6", true, "predictable interface with firstipv6 mode"},
// Invalid - wrong modes
{"eth0:8080:invalid", false, "interface with invalid mode"},
@ -496,11 +500,17 @@ func TestParseInterfaceAddress(t *testing.T) {
{"tcp", "enp0s3", "9000:auto", "enp0s3" + InterfaceDelimiter + "auto", false, "valid interface with explicit auto mode"},
{"tcp", "eth0", "443:all", "eth0" + InterfaceDelimiter + "all", false, "valid interface with all mode"},
{"tcp", "wlan0", "8080-8090:all", "wlan0" + InterfaceDelimiter + "all", false, "port range with all mode"},
{"tcp", "eth0", "443:firstipv4", "eth0" + InterfaceDelimiter + "firstipv4", false, "valid interface with firstipv4 mode"},
{"tcp", "wlan0", "8080:firstipv6", "wlan0" + InterfaceDelimiter + "firstipv6", false, "valid interface with firstipv6 mode"},
{"tcp", "docker0", "9000:firstipv4", "docker0" + InterfaceDelimiter + "firstipv4", false, "docker interface with firstipv4 mode"},
{"tcp", "enp0s3", "8080:firstipv6", "enp0s3" + InterfaceDelimiter + "firstipv6", false, "predictable interface with firstipv6 mode"},
// Valid cases - port ranges with binding modes
{"tcp", "eth0", "8080-8090:ipv4", "eth0" + InterfaceDelimiter + "ipv4", false, "port range with IPv4 mode"},
{"tcp", "wlan0", "443-445:ipv6", "wlan0" + InterfaceDelimiter + "ipv6", false, "port range with IPv6 mode"},
{"tcp", "docker0", "3000-3010:auto", "docker0" + InterfaceDelimiter + "auto", false, "port range with auto mode"},
{"tcp", "eth0", "8080-8090:firstipv4", "eth0" + InterfaceDelimiter + "firstipv4", false, "port range with firstipv4 mode"},
{"tcp", "wlan0", "443-445:firstipv6", "wlan0" + InterfaceDelimiter + "firstipv6", false, "port range with firstipv6 mode"},
// Error cases - invalid hosts
{"tcp", "192.168.1.1", "80", "", true, "IP address should fail"},
@ -588,6 +598,10 @@ func TestTryParseInterfaceWithModeInHost(t *testing.T) {
{"tailscale0:8090:ipv4", "tailscale0", "8090:ipv4", true, "Tailscale interface with IPv4 mode"},
{"docker0:3000:ipv6", "docker0", "3000:ipv6", true, "Docker bridge interface with IPv6 mode"},
{"wlan0:443:all", "wlan0", "443:all", true, "Wireless interface with all mode"},
{"eth0:8080:firstipv4", "eth0", "8080:firstipv4", true, "Ethernet interface with firstipv4 mode"},
{"wlan0:443:firstipv6", "wlan0", "443:firstipv6", true, "Wireless interface with firstipv6 mode"},
{"docker0:9000:firstipv4", "docker0", "9000:firstipv4", true, "Docker interface with firstipv4 mode"},
{"enp0s3:8080:firstipv6", "enp0s3", "8080:firstipv6", true, "Predictable interface with firstipv6 mode"},
// Invalid cases - not enough parts
{"eth0", "", "", false, "Interface name only"},