Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions net.go
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,28 @@ func (ifc *Interface) AddAddress(addr net.Addr) {
ifc.addrs = append(ifc.addrs, addr)
}

// RemoveAddress removes an address from the interface.
func (ifc *Interface) RemoveAddress(ip net.IP) bool {
for i, addr := range ifc.addrs {
var addrIP net.IP
switch a := addr.(type) {
case *net.IPNet:
addrIP = a.IP
case *net.IPAddr:
addrIP = a.IP
default:
continue
}
if addrIP.Equal(ip) {
ifc.addrs = append(ifc.addrs[:i], ifc.addrs[i+1:]...)

return true
}
}

return false
}

// Addrs returns a slice of configured addresses on the interface.
func (ifc *Interface) Addrs() ([]net.Addr, error) {
if len(ifc.addrs) == 0 {
Expand Down
43 changes: 43 additions & 0 deletions vnet/net.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,49 @@ func (v *Net) setRouter(r *Router) error {
return nil
}

// AddAddress adds an address to an interface and registers it for routing.
// This method can be called before or after the router has started.
func (v *Net) AddAddress(ifName string, addr *net.IPNet) error {
v.mutex.Lock()
defer v.mutex.Unlock()

ifc, err := v._getInterface(ifName)
if err != nil {
return err
}
ifc.AddAddress(addr)

if v.router != nil {
v.router.mutex.Lock()
defer v.router.mutex.Unlock()

return v.router.addIPToNIC(v, addr.IP)
}

return nil
}

// RemoveAddress removes an address from an interface and unregisters it from routing.
// This method can be called before or after the router has started.
func (v *Net) RemoveAddress(ifName string, ip net.IP) error {
v.mutex.Lock()
defer v.mutex.Unlock()

ifc, err := v._getInterface(ifName)
if err != nil {
return err
}
ifc.RemoveAddress(ip)

if v.router != nil {
v.router.mutex.Lock()
defer v.router.mutex.Unlock()
v.router.removeIPFromNIC(ip)
}

return nil
}

func (v *Net) onInboundChunk(c Chunk) {
v.mutex.Lock()
defer v.mutex.Unlock()
Expand Down
68 changes: 68 additions & 0 deletions vnet/net_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -807,4 +807,72 @@ func TestNetVirtual(t *testing.T) { //nolint:gocyclo,cyclop,maintidx

assert.NoError(t, wan.Stop(), "should succeed")
})

t.Run("AddAddress and RemoveAddress", func(t *testing.T) {
router, err := NewRouter(&RouterConfig{
CIDR: "10.0.0.0/24",
LoggerFactory: loggerFactory,
})
if !assert.NoError(t, err, "should succeed") {
return
}

nw, err := NewNet(&NetConfig{})
if !assert.NoError(t, err, "should succeed") {
return
}

err = router.AddNet(nw)
assert.NoError(t, err, "should succeed")

err = router.Start()
assert.NoError(t, err, "should succeed")

// Add a new address dynamically.
newIP := net.ParseIP("10.0.0.100")
err = nw.AddAddress("eth0", &net.IPNet{
IP: newIP,
Mask: net.CIDRMask(24, 32),
})
assert.NoError(t, err, "should succeed")

// Verify address was added to interface.
eth0, err := nw.InterfaceByName("eth0")
assert.NoError(t, err, "should succeed")
addrs, err := eth0.Addrs()
assert.NoError(t, err, "should succeed")
assert.Equal(t, 2, len(addrs), "should have 2 addresses")

// Remove the address.
err = nw.RemoveAddress("eth0", newIP)
assert.NoError(t, err, "should succeed")

// Verify address was removed.
addrs, err = eth0.Addrs()
assert.NoError(t, err, "should succeed")
assert.Equal(t, 1, len(addrs), "should have 1 address")

// AddAddress with IP outside CIDR should fail.
err = nw.AddAddress("eth0", &net.IPNet{
IP: net.ParseIP("192.168.1.1"),
Mask: net.CIDRMask(24, 32),
})
assert.Error(t, err, "should fail for IP outside CIDR")

// RemoveAddress with IPAddr type.
eth0.AddAddress(&net.IPAddr{IP: net.ParseIP("10.0.0.200")})
removed := eth0.RemoveAddress(net.ParseIP("10.0.0.200"))
assert.True(t, removed, "should remove IPAddr")

// RemoveAddress skips unsupported addr types.
eth0.AddAddress(&net.TCPAddr{IP: net.ParseIP("10.0.0.201"), Port: 1234})
removed = eth0.RemoveAddress(net.ParseIP("10.0.0.201"))
assert.False(t, removed, "TCPAddr not supported")

// RemoveAddress with non-existent IP.
removed = eth0.RemoveAddress(net.ParseIP("10.0.0.250"))
assert.False(t, removed, "should return false")

assert.NoError(t, router.Stop(), "should succeed")
})
}
15 changes: 15 additions & 0 deletions vnet/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,21 @@ func (r *Router) addNIC(nic NIC) error {
return nic.setRouter(r)
}

// caller must hold the mutex.
func (r *Router) addIPToNIC(nic NIC, ip net.IP) error {
if !r.ipv4Net.Contains(ip) {
return fmt.Errorf("%w: %s", errStaticIPisBeyondSubnet, r.ipv4Net.String())
}
r.nics[ip.String()] = nic

return nil
}

// caller must hold the mutex.
func (r *Router) removeIPFromNIC(ip net.IP) {
delete(r.nics, ip.String())
}

// AddRouter adds a child Router.
func (r *Router) AddRouter(router *Router) error {
r.mutex.Lock()
Expand Down
Loading