diff --git a/network/interface.go b/network/interface.go index f33ec40..72af5f4 100644 --- a/network/interface.go +++ b/network/interface.go @@ -184,7 +184,9 @@ func AddSecondaryAddress(interfaceName string, addr string) error { } mask := net.IP(ipNet.Mask).String() - AddIPv4Address(ip.String(), mask) + if err := AddIPv4Address(ip.String(), mask); err != nil { + return err + } if interfaceName == "" { return nil diff --git a/network/settings.go b/network/settings.go index 9203695..f0520ed 100644 --- a/network/settings.go +++ b/network/settings.go @@ -2,6 +2,7 @@ package network import ( "encoding/json" + "fmt" "sync" "github.com/fosrl/newt/logger" @@ -86,14 +87,24 @@ func SetIPv4Settings(addresses []string, subnetMasks []string) { // exposed to mobile (iOS/Android) packet-tunnel providers, which read the // full IPv4Addresses/IPv4SubnetMasks arrays (not just the first entry) and // re-apply them on every settings poll. -func AddIPv4Address(address string, subnetMask string) { +// +// It requires a primary address to already be set (via SetIPv4Settings, +// i.e. ConfigureInterface) and refuses to add otherwise: on mobile +// platforms, array order is what determines which address the OS treats as +// primary, so appending to an empty list would silently make this +// "additional" address the primary one instead. +func AddIPv4Address(address string, subnetMask string) error { networkSettingsMutex.Lock() defer networkSettingsMutex.Unlock() + if len(networkSettings.IPv4Addresses) == 0 { + return fmt.Errorf("cannot add secondary IPv4 address %s: no primary address configured yet", address) + } + for _, a := range networkSettings.IPv4Addresses { if a == address { logger.Info("IPv4 address already exists: %s", address) - return + return nil } } @@ -101,6 +112,7 @@ func AddIPv4Address(address string, subnetMask string) { networkSettings.IPv4SubnetMasks = append(networkSettings.IPv4SubnetMasks, subnetMask) incrementor++ logger.Info("Added IPv4 address: %s/%s", address, subnetMask) + return nil } // RemoveIPv4Address removes a previously added secondary IPv4 address. diff --git a/network/settings_test.go b/network/settings_test.go new file mode 100644 index 0000000..eac4d5f --- /dev/null +++ b/network/settings_test.go @@ -0,0 +1,32 @@ +package network + +import "testing" + +func TestAddIPv4AddressRequiresPrimary(t *testing.T) { + ClearNetworkSettings() + defer ClearNetworkSettings() + + if err := AddIPv4Address("10.10.0.5", "255.255.255.255"); err == nil { + t.Fatal("expected an error adding a secondary address before any primary address is set") + } + if got := GetSettings().IPv4Addresses; len(got) != 0 { + t.Errorf("IPv4Addresses = %v, want empty after a rejected add", got) + } +} + +func TestAddIPv4AddressAfterPrimarySucceeds(t *testing.T) { + ClearNetworkSettings() + defer ClearNetworkSettings() + + SetIPv4Settings([]string{"10.10.0.1"}, []string{"255.255.255.0"}) + + if err := AddIPv4Address("10.10.0.5", "255.255.255.255"); err != nil { + t.Fatalf("unexpected error adding secondary address: %v", err) + } + + got := GetSettings().IPv4Addresses + want := []string{"10.10.0.1", "10.10.0.5"} + if len(got) != len(want) || got[0] != want[0] || got[1] != want[1] { + t.Errorf("IPv4Addresses = %v, want %v", got, want) + } +}