add funcs to add and remove ip from interface

This commit is contained in:
Owen
2026-07-31 10:34:07 -04:00
parent 69d3925167
commit 01cb6f39ca
5 changed files with 187 additions and 2 deletions
+92
View File
@@ -167,3 +167,95 @@ func configureLinux(interfaceName string, ip net.IP, ipNet *net.IPNet) error {
return nil
}
// AddSecondaryAddress adds an additional IP address (given as CIDR, e.g. "10.10.0.5/32")
// to an already-configured interface. It also records the address in the shared
// NetworkSettings (see AddIPv4Address) so mobile (iOS/Android) packet-tunnel providers
// pick it up on their next settings poll - those platforms have no OS-level interface
// to configure directly, so this is the only way they learn about the address.
func AddSecondaryAddress(interfaceName string, addr string) error {
ip, ipNet, err := net.ParseCIDR(addr)
if err != nil {
return fmt.Errorf("invalid IP address: %v", err)
}
mask := net.IP(ipNet.Mask).String()
AddIPv4Address(ip.String(), mask)
if interfaceName == "" {
return nil
}
switch runtime.GOOS {
case "linux":
return configureLinux(interfaceName, ip, ipNet)
case "darwin":
return configureDarwin(interfaceName, ip, ipNet)
case "windows":
return configureWindows(interfaceName, ip, ipNet)
default:
return nil
}
}
// RemoveSecondaryAddress removes an IP address (given as CIDR) previously added with
// AddSecondaryAddress, including from the shared NetworkSettings used by mobile
// packet-tunnel providers.
func RemoveSecondaryAddress(interfaceName string, addr string) error {
ip, ipNet, err := net.ParseCIDR(addr)
if err != nil {
return fmt.Errorf("invalid IP address: %v", err)
}
RemoveIPv4Address(ip.String())
if interfaceName == "" {
return nil
}
switch runtime.GOOS {
case "linux":
return removeLinuxAddress(interfaceName, ip, ipNet)
case "darwin":
return removeDarwinAddress(interfaceName, ip, ipNet)
case "windows":
return removeWindowsAddress(interfaceName, ip, ipNet)
default:
return nil
}
}
func removeLinuxAddress(interfaceName string, ip net.IP, ipNet *net.IPNet) error {
link, err := netlink.LinkByName(interfaceName)
if err != nil {
return fmt.Errorf("failed to get interface %s: %v", interfaceName, err)
}
addr := &netlink.Addr{
IPNet: &net.IPNet{
IP: ip,
Mask: ipNet.Mask,
},
}
if err := netlink.AddrDel(link, addr); err != nil {
return fmt.Errorf("failed to remove IP address: %v", err)
}
return nil
}
func removeDarwinAddress(interfaceName string, ip net.IP, ipNet *net.IPNet) error {
prefix, _ := ipNet.Mask.Size()
ipStr := fmt.Sprintf("%s/%d", ip.String(), prefix)
cmd := exec.Command("/sbin/ifconfig", interfaceName, "inet", ipStr, "-alias")
logger.Info("Running command: %v", cmd)
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("ifconfig command failed: %v, output: %s", err, out)
}
return nil
}
+4
View File
@@ -10,3 +10,7 @@ import (
func configureWindows(interfaceName string, ip net.IP, ipNet *net.IPNet) error {
return fmt.Errorf("configureWindows called on non-Windows platform")
}
func removeWindowsAddress(interfaceName string, ip net.IP, ipNet *net.IPNet) error {
return fmt.Errorf("removeWindowsAddress called on non-Windows platform")
}
+32
View File
@@ -61,3 +61,35 @@ func configureWindows(interfaceName string, ip net.IP, ipNet *net.IPNet) error {
return nil
}
func removeWindowsAddress(interfaceName string, ip net.IP, ipNet *net.IPNet) error {
iface, err := net.InterfaceByName(interfaceName)
if err != nil {
return fmt.Errorf("failed to get interface %s: %v", interfaceName, err)
}
luid, err := winipcfg.LUIDFromIndex(uint32(iface.Index))
if err != nil {
return fmt.Errorf("failed to get LUID for interface %s: %v", interfaceName, err)
}
maskBits, _ := ipNet.Mask.Size()
var addr netip.Addr
if ip4 := ip.To4(); ip4 != nil {
addr, _ = netip.AddrFromSlice(ip4)
} else {
addr, _ = netip.AddrFromSlice(ip)
}
if !addr.IsValid() {
return fmt.Errorf("failed to convert IP address")
}
prefix := netip.PrefixFrom(addr, maskBits)
logger.Info("Removing IP address %s from interface %s", prefix.String(), interfaceName)
if err := luid.DeleteIPAddress(prefix); err != nil {
return fmt.Errorf("failed to remove IP address: %v", err)
}
return nil
}
+39
View File
@@ -81,6 +81,45 @@ func SetIPv4Settings(addresses []string, subnetMasks []string) {
logger.Info("Set IPv4 addresses: %v, subnet masks: %v", addresses, subnetMasks)
}
// AddIPv4Address appends an additional IPv4 address/subnet mask pair to the
// tunnel's network settings. This is how a secondary interface address gets
// 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) {
networkSettingsMutex.Lock()
defer networkSettingsMutex.Unlock()
for _, a := range networkSettings.IPv4Addresses {
if a == address {
logger.Info("IPv4 address already exists: %s", address)
return
}
}
networkSettings.IPv4Addresses = append(networkSettings.IPv4Addresses, address)
networkSettings.IPv4SubnetMasks = append(networkSettings.IPv4SubnetMasks, subnetMask)
incrementor++
logger.Info("Added IPv4 address: %s/%s", address, subnetMask)
}
// RemoveIPv4Address removes a previously added secondary IPv4 address.
func RemoveIPv4Address(address string) {
networkSettingsMutex.Lock()
defer networkSettingsMutex.Unlock()
for i, a := range networkSettings.IPv4Addresses {
if a == address {
networkSettings.IPv4Addresses = append(networkSettings.IPv4Addresses[:i], networkSettings.IPv4Addresses[i+1:]...)
networkSettings.IPv4SubnetMasks = append(networkSettings.IPv4SubnetMasks[:i], networkSettings.IPv4SubnetMasks[i+1:]...)
incrementor++
logger.Info("Removed IPv4 address: %s", address)
return
}
}
logger.Info("IPv4 address not found for removal: %s", address)
}
// SetIPv4IncludedRoutes sets the included IPv4 routes
func SetIPv4IncludedRoutes(routes []IPv4Route) {
networkSettingsMutex.Lock()