Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow more than one static IP addresses #33

Merged
merged 3 commits into from
Aug 22, 2019
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
28 changes: 22 additions & 6 deletions vnet/net.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func newMACAddress() net.HardwareAddr {

type vNet struct {
interfaces []*Interface // read-only
staticIP net.IP // read-only
staticIPs []net.IP // read-only
router *Router // read-only
udpConns *udpConnMap // read-only
mutex sync.RWMutex
Expand Down Expand Up @@ -432,8 +432,12 @@ func (v *vNet) assignPort(ip net.IP, start, end int) (int, error) {

// NetConfig is a bag of configuration parameters passed to NewNet().
type NetConfig struct {
// StaticIP is a static IP address to be assigned for this Net. If nil,
// the router will automatically assign an IP address.
// StaticIPs is an array of static IP addresses to be assigned for this Net.
// If no static IP address is given, the router will automatically assign
// an IP address.
StaticIPs []string

// StaticIP is deprecated. Use StaticIPs.
StaticIP string
}

Expand Down Expand Up @@ -489,9 +493,21 @@ func NewNet(config *NetConfig) *Net {
Flags: net.FlagUp | net.FlagMulticast,
})

var staticIPs []net.IP
for _, ipStr := range config.StaticIPs {
if ip := net.ParseIP(ipStr); ip != nil {
enobufs marked this conversation as resolved.
Show resolved Hide resolved
staticIPs = append(staticIPs, ip)
}
}
if len(config.StaticIP) > 0 {
if ip := net.ParseIP(config.StaticIP); ip != nil {
staticIPs = append(staticIPs, ip)
}
}

v := &vNet{
interfaces: []*Interface{lo0, eth0},
staticIP: net.ParseIP(config.StaticIP),
staticIPs: staticIPs,
udpConns: newUDPConnMap(),
}

Expand Down Expand Up @@ -607,12 +623,12 @@ func (n *Net) onInboundChunk(c Chunk) {
n.v.onInboundChunk(c)
}

func (n *Net) getStaticIP() net.IP {
func (n *Net) getStaticIPs() []net.IP {
if n.v == nil {
return nil
}

return n.v.staticIP
return n.v.staticIPs
}

// IsVirtual tests if the virtual network is enabled.
Expand Down
6 changes: 3 additions & 3 deletions vnet/net_native_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,8 @@ func TestNetNative(t *testing.T) {
// onInboundChunk (shouldn't crash)
nw.onInboundChunk(nil)

// getStaticIP
ip := nw.getStaticIP()
assert.Nil(t, ip, "should be nil")
// getStaticIPs
ips := nw.getStaticIPs()
assert.Nil(t, ips, "should be nil")
})
}
92 changes: 92 additions & 0 deletions vnet/net_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -644,4 +644,96 @@ func TestNetVirtual(t *testing.T) {
assert.NoError(t, conn.Close(), "should succeed")
assert.Equal(t, 0, nw.v.udpConns.size(), "should match")
})

t.Run("Two IPs on a NIC", func(t *testing.T) {
doneCh := make(chan struct{})

// WAN
wan, err := NewRouter(&RouterConfig{
CIDR: "1.2.3.0/24",
LoggerFactory: loggerFactory,
})
assert.NoError(t, err, "should succeed")
assert.NotNil(t, wan, "should succeed")

net1 := NewNet(&NetConfig{
StaticIPs: []string{
"1.2.3.4",
"1.2.3.5",
},
})

err = wan.AddNet(net1)
assert.NoError(t, err, "should succeed")

// start the router
err = wan.Start()
assert.NoError(t, err, "should succeed")

conn1, err := net1.ListenPacket("udp", "1.2.3.4:1234")
assert.NoError(t, err, "should succeed")

conn2, err := net1.ListenPacket("udp", "1.2.3.5:1234")
assert.NoError(t, err, "should succeed")

conn1RcvdCh := make(chan bool)

// conn1
go func() {
buf := make([]byte, 1500)
for {
log.Debug("conn1: wait for a message..")
n, _, err2 := conn1.ReadFrom(buf)
if err2 != nil {
log.Debugf("ReadFrom returned: %v", err2)
break
}

log.Debugf("conn1 received %s", string(buf[:n]))
conn1RcvdCh <- true
}
close(doneCh)
}()

// conn2
go func() {
buf := make([]byte, 1500)
for {
log.Debug("conn2: wait for a message..")
n, addr, err2 := conn2.ReadFrom(buf)
if err2 != nil {
log.Debugf("ReadFrom returned: %v", err2)
break
}

log.Debugf("conn2 received %s", string(buf[:n]))

// echo back to conn1
nSent, err2 := conn2.WriteTo([]byte("Good-bye!"), addr)
assert.NoError(t, err2, "should succeed")
assert.Equal(t, 9, nSent, "should match")
}
}()

log.Debug("conn1: sending")
nSent, err := conn1.WriteTo(
[]byte("Hello!"),
conn2.LocalAddr(),
)
assert.NoError(t, err, "should succeed")
assert.Equal(t, 6, nSent, "should match")

loop:
for {
select {
case <-conn1RcvdCh:
assert.NoError(t, conn1.Close(), "should succeed")
assert.NoError(t, conn2.Close(), "should succeed")
case <-doneCh:
break loop
}
}

assert.NoError(t, wan.Stop(), "should succeed")
})
}
61 changes: 41 additions & 20 deletions vnet/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,12 @@ type RouterConfig struct {
Name string
// CIDR notation, like "192.0.2.0/24"
CIDR string
// StaticIP is a static IP address to be assigned for this external network.
// StaticIPs is an array of static IP addresses to be assigned for this router.
// If no static IP address is given, the router will automatically assign
// an IP address.
// This will be ignored if this router is the root.
StaticIPs []string
// StaticIP is deprecated. Use StaticIPs.
StaticIP string
// Internal queue size
QueueSize int
Expand All @@ -50,7 +54,7 @@ type RouterConfig struct {
type NIC interface {
getInterface(ifName string) (*Interface, error)
onInboundChunk(c Chunk)
getStaticIP() net.IP
getStaticIPs() []net.IP
setRouter(r *Router) error
}

Expand All @@ -63,7 +67,7 @@ type Router struct {
name string // read-only
interfaces []*Interface // read-only
ipv4Net *net.IPNet // read-only
staticIP net.IP // read-only
staticIPs []net.IP // read-only
lastID byte // requires mutex [x], used to assign the last digit of IPv4 address
queue *chunkQueue // read-only
parent *Router // read-only
Expand Down Expand Up @@ -123,11 +127,23 @@ func NewRouter(config *RouterConfig) (*Router, error) {
name = assignRouterName()
}

var staticIPs []net.IP
for _, ipStr := range config.StaticIPs {
if ip := net.ParseIP(ipStr); ip != nil {
staticIPs = append(staticIPs, ip)
}
}
if len(config.StaticIP) > 0 {
if ip := net.ParseIP(config.StaticIP); ip != nil {
staticIPs = append(staticIPs, ip)
}
}

return &Router{
name: name,
interfaces: []*Interface{lo0, eth0},
ipv4Net: ipNet,
staticIP: net.ParseIP(config.StaticIP),
staticIPs: staticIPs,
queue: newChunkQueue(queueSize),
natType: config.NATType,
nics: map[string]NIC{},
Expand Down Expand Up @@ -247,29 +263,34 @@ func (r *Router) addNIC(nic NIC) error {
return err
}

var ip net.IP
if ip = nic.getStaticIP(); ip != nil {
if !r.ipv4Net.Contains(ip) {
return fmt.Errorf("static IP is beyond subnet: %s", r.ipv4Net.String())
}
} else {
var ips []net.IP

if ips = nic.getStaticIPs(); len(ips) == 0 {
// assign an IP address
ip, err = r.assignIPAddress()
if err != nil {
return err
ip, err2 := r.assignIPAddress()
if err2 != nil {
return err2
}
ips = append(ips, ip)
}

ifc.AddAddr(&net.IPNet{
IP: ip,
Mask: r.ipv4Net.Mask,
})
for _, ip := range ips {
if !r.ipv4Net.Contains(ip) {
return fmt.Errorf("static IP is beyond subnet: %s", r.ipv4Net.String())
}

ifc.AddAddr(&net.IPNet{
IP: ip,
Mask: r.ipv4Net.Mask,
})

r.nics[ip.String()] = nic
}

if err = nic.setRouter(r); err != nil {
return err
}

r.nics[ip.String()] = nic
return nil
}

Expand Down Expand Up @@ -521,6 +542,6 @@ func (r *Router) onInboundChunk(c Chunk) {
r.push(fromParent)
}

func (r *Router) getStaticIP() net.IP {
return r.staticIP
func (r *Router) getStaticIPs() []net.IP {
return r.staticIPs
}
102 changes: 102 additions & 0 deletions vnet/router_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -424,3 +424,105 @@ func TestRouterOneChild(t *testing.T) {
})

}

func TestRouterStaticIPs(t *testing.T) {
loggerFactory := logging.NewDefaultLoggerFactory()
//log := loggerFactory.NewLogger("test")

t.Run("more than one static IP", func(t *testing.T) {
// WAN
wan, err := NewRouter(&RouterConfig{
CIDR: "1.2.3.0/24",
StaticIPs: []string{
"1.2.3.1",
"1.2.3.2",
"1.2.3.3",
},
LoggerFactory: loggerFactory,
})
assert.Nil(t, err, "should succeed")
assert.NotNil(t, wan, "should succeed")

assert.Equal(t, 3, len(wan.staticIPs), "should be 3")
assert.Equal(t, "1.2.3.1", wan.staticIPs[0].String(), "should match")
assert.Equal(t, "1.2.3.2", wan.staticIPs[1].String(), "should match")
assert.Equal(t, "1.2.3.3", wan.staticIPs[2].String(), "should match")
})

t.Run("StaticIPs and StaticIP in the mix", func(t *testing.T) {
// WAN
wan, err := NewRouter(&RouterConfig{
CIDR: "1.2.3.0/24",
StaticIPs: []string{
"1.2.3.1",
"1.2.3.2",
"1.2.3.3",
},
StaticIP: "1.2.3.4",
LoggerFactory: loggerFactory,
})
assert.Nil(t, err, "should succeed")
assert.NotNil(t, wan, "should succeed")

assert.Equal(t, 4, len(wan.staticIPs), "should be 4")
assert.Equal(t, "1.2.3.1", wan.staticIPs[0].String(), "should match")
assert.Equal(t, "1.2.3.2", wan.staticIPs[1].String(), "should match")
assert.Equal(t, "1.2.3.3", wan.staticIPs[2].String(), "should match")
assert.Equal(t, "1.2.3.4", wan.staticIPs[3].String(), "should match")
})
}

func TestRouterFailures(t *testing.T) {
loggerFactory := logging.NewDefaultLoggerFactory()
//log := loggerFactory.NewLogger("test")

t.Run("Stop when router is stopped", func(t *testing.T) {
r, err := NewRouter(&RouterConfig{
CIDR: "1.2.3.0/24",
LoggerFactory: loggerFactory,
})
assert.Nil(t, err, "should succeed")

err = r.Stop()
assert.Error(t, err, "should fail")
})

t.Run("AddNet", func(t *testing.T) {
r, err := NewRouter(&RouterConfig{
CIDR: "1.2.3.0/24",
LoggerFactory: loggerFactory,
})
assert.Nil(t, err, "should succeed")

nic := NewNet(&NetConfig{
StaticIPs: []string{
"5.6.7.8", // out of parent router'c CIDR
},
})
assert.NotNil(t, nic, "should succeed")

err = r.AddNet(nic)
assert.Error(t, err, "should fail")
})

t.Run("AddRouter", func(t *testing.T) {
r1, err := NewRouter(&RouterConfig{
CIDR: "1.2.3.0/24",
LoggerFactory: loggerFactory,
})
assert.Nil(t, err, "should succeed")

r2, err := NewRouter(&RouterConfig{
CIDR: "192.168.0.0/24",
StaticIPs: []string{
"5.6.7.8", // out of parent router'c CIDR
},

LoggerFactory: loggerFactory,
})
assert.Nil(t, err, "should succeed")

err = r1.AddRouter(r2)
assert.Error(t, err, "should fail")
})
}
Loading