fix: lease not update when switch restart #12

This commit is contained in:
Daniel Ding
2022-10-10 16:05:03 +08:00
parent afebb43dbe
commit c86cf5040d
10 changed files with 161 additions and 90 deletions

View File

@@ -184,16 +184,20 @@ test: ## execute unit test
go test -v -mod=vendor -bench=. github.com/luscis/openlan/pkg/access go test -v -mod=vendor -bench=. github.com/luscis/openlan/pkg/access
go test -v -mod=vendor -bench=. github.com/luscis/openlan/pkg/libol go test -v -mod=vendor -bench=. github.com/luscis/openlan/pkg/libol
go test -v -mod=vendor -bench=. github.com/luscis/openlan/pkg/models go test -v -mod=vendor -bench=. github.com/luscis/openlan/pkg/models
go test -v -mod=vendor -bench=. github.com/luscis/openlan/pkg/cache
go test -v -mod=vendor -bench=. github.com/luscis/openlan/pkg/config
go test -v -mod=vendor -bench=. github.com/luscis/openlan/pkg/network
## coverage ## coverage
cover: env ## execute unit test and output coverage cover: env ## execute unit test and output coverage
@rm -rvf $(CD) @rm -rvf $(CD) && mkdir -p $(CD)
@mkdir -p $(CD) @go test -mod=vendor github.com/luscis/openlan/pkg/access -coverprofile=$(CD)/0.out -race -covermode=atomic
go test -mod=vendor github.com/luscis/openlan/pkg/access -coverprofile=$(CD)/access.out -race -covermode=atomic @go test -mod=vendor github.com/luscis/openlan/pkg/libol -coverprofile=$(CD)/1.out -race -covermode=atomic
go test -mod=vendor github.com/luscis/openlan/pkg/libol -coverprofile=$(CD)/libol.out -race -covermode=atomic @go test -mod=vendor github.com/luscis/openlan/pkg/models -coverprofile=$(CD)/2.out -race -covermode=atomic
go test -mod=vendor github.com/luscis/openlan/pkg/models -coverprofile=$(CD)/models.out -race -covermode=atomic @go test -mod=vendor github.com/luscis/openlan/pkg/cache -coverprofile=$(CD)/3.out -race -covermode=atomic
@go test -mod=vendor github.com/luscis/openlan/pkg/config -coverprofile=$(CD)/4.out -race -covermode=atomic
@go test -mod=vendor github.com/luscis/openlan/pkg/network -coverprofile=$(CD)/5.out -race -covermode=atomic
echo 'mode: atomic' > $(SD)/coverage.out @echo 'mode: atomic' > $(SD)/coverage.out && \
tail -q -n +2 $(CD)/*.out >> $(SD)/coverage.out tail -q -n +2 $(CD)/*.out >> $(SD)/coverage.out
go tool cover -html=coverage.out -o coverage.html go tool cover -html=coverage.out -o coverage.html

View File

@@ -20,9 +20,9 @@ func (u Lease) Url(prefix, name string) string {
func (u Lease) Tmpl() string { func (u Lease) Tmpl() string {
return `# total {{ len . }} return `# total {{ len . }}
{{ps -16 "uuid"}} {{ps -16 "alias"}} {{ ps -16 "address" }} {{ps -22 "client"}} {{ps -8 "network"}} {{ ps -6 "type"}} {{ps -16 "alias"}} {{ ps -16 "address" }} {{ps -22 "client"}} {{ps -8 "network"}} {{ ps -6 "type"}}
{{- range . }} {{- range . }}
{{ps -16 .UUID}} {{ps -16 .Alias}} {{ ps -16 .Address}} {{ps -22 .Client}} {{ps -8 .Network}} {{ ps -6 .Type}} {{ps -16 .Alias}} {{ ps -16 .Address}} {{ps -22 .Client}} {{ps -8 .Network}} {{ ps -6 .Type}}
{{- end }} {{- end }}
` `
} }

View File

@@ -12,14 +12,10 @@ func TestTapWrite(t *testing.T) {
t.Errorf("Tap.open %s", err) t.Errorf("Tap.open %s", err)
return return
} }
//t.Logf("Tap.write: %s\n", dev.Name())
frame := make([]byte, 65) frame := make([]byte, 65)
for i := 0; i < 64; i++ { for i := 0; i < 64; i++ {
frame[i] = uint8(i) frame[i] = uint8(i)
} }
//t.Logf("Tap.write: %x", frame)
n, err := dev.Write(frame) n, err := dev.Write(frame)
if err != nil { if err != nil {
t.Errorf("Tap.write: %s", err) t.Errorf("Tap.write: %s", err)

View File

@@ -65,35 +65,24 @@ func (r *Request) onNeighbor(client libol.SocketClient, data []byte) {
} }
} }
func (r *Request) getLease(ifAddr string, p *models.Point, n *models.Network) *schema.Lease { func (r *Request) findLease(ifAddr string, p *models.Point, n *models.Network) *schema.Lease {
if n == nil { if n == nil {
return nil return nil
} }
uuid := p.UUID
alias := p.Alias alias := p.Alias
network := n.Name network := n.Name
lease := cache.Network.GetLeaseByAlias(alias) // try by alias firstly lease := cache.Network.GetLease(alias, network) // try by alias firstly
if ifAddr == "" { if ifAddr == "" {
if lease == nil { // now to alloc it. if lease == nil { // now to alloc it.
lease = cache.Network.NewLease(uuid, network) lease = cache.Network.NewLease(alias, network)
if lease != nil {
lease.Alias = alias
}
} else {
lease.UUID = uuid
} }
} else { } else {
ipAddr := strings.SplitN(ifAddr, "/", 2)[0] ipAddr := strings.SplitN(ifAddr, "/", 2)[0]
if lease != nil && lease.Address == ipAddr {
lease.UUID = uuid
}
if lease == nil || lease.Address != ipAddr { if lease == nil || lease.Address != ipAddr {
lease = cache.Network.AddLease(uuid, ipAddr) lease = cache.Network.AddLease(alias, ipAddr, network)
lease.Alias = alias
} }
} }
if lease != nil { if lease != nil {
lease.Network = network
lease.Client = p.Client.String() lease.Client = p.Client.String()
} }
return lease return lease
@@ -129,20 +118,18 @@ func (r *Request) onIpAddr(client libol.SocketClient, data []byte) {
Netmask: recv.Netmask, Netmask: recv.Netmask,
Routes: n.Routes, Routes: n.Routes,
} }
if recv.IfAddr == "" { // not interface address, and try to alloc it. lease := r.findLease(recv.IfAddr, p, n)
lease := r.getLease(recv.IfAddr, p, n) if lease != nil {
if lease != nil { resp.IfAddr = lease.Address
resp.IfAddr = lease.Address resp.Netmask = n.Netmask
resp.Netmask = n.Netmask resp.Routes = n.Routes
resp.Routes = n.Routes } else {
} else { resp.IfAddr = "169.254.0.0"
resp.IfAddr = "169.254.0.0" resp.Netmask = n.Netmask
resp.Netmask = n.Netmask if resp.Netmask == "" {
if resp.Netmask == "" { resp.Netmask = "255.255.0.0"
resp.Netmask = "255.255.0.0"
}
resp.Routes = n.Routes
} }
resp.Routes = n.Routes
} }
out.Cmd("Request.onIpAddr: resp %s", resp) out.Cmd("Request.onIpAddr: resp %s", resp)
if respStr, err := json.Marshal(resp); err == nil { if respStr, err := json.Marshal(resp); err == nil {

70
pkg/cache/network.go vendored
View File

@@ -10,8 +10,8 @@ import (
type network struct { type network struct {
Networks *libol.SafeStrMap Networks *libol.SafeStrMap
UUID *libol.SafeStrMap // TODO with network UUID *libol.SafeStrMap
Addr *libol.SafeStrMap // TODO with network Addr *libol.SafeStrMap
} }
func (w *network) Add(n *models.Network) { func (w *network) Add(n *models.Network) {
@@ -57,7 +57,7 @@ func (w *network) ListLease() <-chan *schema.Lease {
return c return c
} }
func (w *network) allocLease(sAddr, eAddr string) string { func (w *network) allocLease(sAddr, eAddr, network string) string {
sIp := net.ParseIP(sAddr) sIp := net.ParseIP(sAddr)
eIp := net.ParseIP(eAddr) eIp := net.ParseIP(eAddr)
if sIp == nil || eIp == nil { if sIp == nil || eIp == nil {
@@ -69,62 +69,67 @@ func (w *network) allocLease(sAddr, eAddr string) string {
tmp := make([]byte, 4) tmp := make([]byte, 4)
binary.BigEndian.PutUint32(tmp[:4], i) binary.BigEndian.PutUint32(tmp[:4], i)
tmpStr := net.IP(tmp).String() tmpStr := net.IP(tmp).String()
if _, ok := w.Addr.GetEx(tmpStr); !ok { if ok := w.GetLeaseByAddr(tmpStr, network); ok == nil {
return tmpStr return tmpStr
} }
} }
return "" return ""
} }
func (w *network) NewLease(uuid, network string) *schema.Lease { func (w *network) NewLease(alias, network string) *schema.Lease {
n := w.Get(network) n := w.Get(network)
if n == nil || uuid == "" { if n == nil || alias == "" {
return nil return nil
} }
uuid := alias + "@" + network
if obj, ok := w.UUID.GetEx(uuid); ok { if obj, ok := w.UUID.GetEx(uuid); ok {
l := obj.(*schema.Lease) l := obj.(*schema.Lease)
return l // how to resolve conflict with new point?. return l // how to resolve conflict with new point?.
} }
ipStr := w.allocLease(n.IpStart, n.IpEnd) ipStr := w.allocLease(n.IpStart, n.IpEnd, network)
if ipStr == "" { if ipStr == "" {
return nil return nil
} }
w.AddLease(uuid, ipStr) w.AddLease(alias, ipStr, network)
return w.GetLease(uuid) return w.GetLease(alias, network)
} }
func (w *network) GetLease(uuid string) *schema.Lease { func (w *network) GetLease(alias string, network string) *schema.Lease {
uuid := alias + "@" + network
if obj, ok := w.UUID.GetEx(uuid); ok { if obj, ok := w.UUID.GetEx(uuid); ok {
return obj.(*schema.Lease) return obj.(*schema.Lease)
} }
return nil return nil
} }
func (w *network) GetLeaseByAlias(name string) *schema.Lease { func (w *network) GetLeaseByAddr(addr string, network string) *schema.Lease {
if obj, ok := w.UUID.GetEx(name); ok { ruid := addr + "@" + network
if obj, ok := w.Addr.GetEx(ruid); ok {
return obj.(*schema.Lease) return obj.(*schema.Lease)
} }
return nil return nil
} }
func (w *network) AddLease(uuid, ipStr string) *schema.Lease { func (w *network) AddLease(alias, ipStr, network string) *schema.Lease {
libol.Info("network.AddLease %s %s", uuid, ipStr) if ipStr == "" || alias == "" {
if ipStr != "" { return nil
l := &schema.Lease{
UUID: uuid,
Alias: uuid,
Address: ipStr,
}
_ = w.UUID.Set(uuid, l)
_ = w.Addr.Set(ipStr, l)
return l
} }
return nil uuid := alias + "@" + network
libol.Info("network.AddLease %s %s", uuid, ipStr)
obj := &schema.Lease{
Alias: alias,
Address: ipStr,
Network: network,
}
_ = w.UUID.Set(uuid, obj)
ruid := ipStr + "@" + network
_ = w.Addr.Set(ruid, obj)
return obj
} }
func (w *network) DelLease(uuid string) { func (w *network) DelLease(alias string, network string) {
uuid := alias + "@" + network
libol.Debug("network.DelLease %s", uuid) libol.Debug("network.DelLease %s", uuid)
// TODO record free address for alias and wait timeout to release.
addr := "" addr := ""
if obj, ok := w.UUID.GetEx(uuid); ok { if obj, ok := w.UUID.GetEx(uuid); ok {
lease := obj.(*schema.Lease) lease := obj.(*schema.Lease)
@@ -134,19 +139,18 @@ func (w *network) DelLease(uuid string) {
w.UUID.Del(uuid) w.UUID.Del(uuid)
} }
} }
if obj, ok := w.Addr.GetEx(addr); ok { ruid := addr + "@" + network
if obj, ok := w.Addr.GetEx(ruid); ok {
lease := obj.(*schema.Lease) lease := obj.(*schema.Lease)
if lease.UUID == uuid { // avoid address conflict by different points. libol.Info("network.DelLease (%s, %s) by Addr", ruid, alias)
libol.Info("network.DelLease (%s, %s) by Addr", uuid, addr) if lease.Type != "static" {
if lease.Type != "static" { w.Addr.Del(ruid)
w.Addr.Del(addr)
}
} }
} }
} }
var Network = network{ var Network = network{
Networks: libol.NewSafeStrMap(1024), Networks: libol.NewSafeStrMap(128),
UUID: libol.NewSafeStrMap(1024), UUID: libol.NewSafeStrMap(1024),
Addr: libol.NewSafeStrMap(1024), Addr: libol.NewSafeStrMap(1024),
} }

74
pkg/cache/network_test.go vendored Normal file
View File

@@ -0,0 +1,74 @@
package cache
import (
"github.com/luscis/openlan/pkg/models"
"github.com/luscis/openlan/pkg/schema"
"github.com/stretchr/testify/assert"
"testing"
)
func Test_Network_LeaseAdd(t *testing.T) {
// 0
Network.AddLease("fake-alias", "192.168.1.1", "fake-net")
{
leAddr := Network.GetLeaseByAddr("192.168.1.1", "fake-net")
assert.Equal(t, "fake-alias", leAddr.Alias, "MUST be found")
leAlias := Network.GetLease("fake-alias", "fake-net")
assert.Equal(t, "192.168.1.1", leAlias.Address, "MUST be found")
}
Network.DelLease("fake-alias", "fake-net")
{
leAddr := Network.GetLeaseByAddr("192.168.1.1", "fake-net")
assert.Equal(t, (*schema.Lease)(nil), leAddr, "MUST be not found")
leAlias := Network.GetLease("fake=alias", "fake-net")
assert.Equal(t, (*schema.Lease)(nil), leAlias, "MUST be not found")
}
Network.AddLease("fake-aa", "192.168.1.1", "fake-aa")
Network.AddLease("fake-cc", "192.168.1.1", "fake-aa")
Network.DelLease("fake-aa", "fake-net")
Network.DelLease("fake-cc", "fake-net")
{
leAddr := Network.GetLeaseByAddr("192.168.1.1", "fake-net")
assert.Equal(t, (*schema.Lease)(nil), leAddr, "MUST be not found")
leAlias := Network.GetLease("fake=alias", "fake-net")
assert.Equal(t, (*schema.Lease)(nil), leAlias, "MUST be not found")
}
// 1
Network.AddLease("fake-aa", "192.168.1.1", "fake-aa")
Network.AddLease("fake-aa", "192.168.1.1", "fake-cc")
{
lea := Network.GetLease("fake-aa", "fake-aa")
assert.Equal(t, "192.168.1.1", lea.Address, "MUST be found")
lec := Network.GetLease("fake-aa", "fake-cc")
assert.Equal(t, "192.168.1.1", lec.Address, "MUST be found")
}
n0 := &models.Network{
IpStart: "192.168.1.0",
IpEnd: "192.168.1.222",
Name: "fake-aa",
}
Network.Add(n0)
Network.NewLease("fake-vv", n0.Name)
Network.NewLease("fake-dd", n0.Name)
{
le1 := Network.GetLease("fake-vv", n0.Name)
assert.Equal(t, "192.168.1.0", le1.Address, "MUST be .0")
le2 := Network.GetLease("fake-dd", n0.Name)
assert.Equal(t, "192.168.1.2", le2.Address, "MUST be .2")
}
// 2
n1 := &models.Network{
IpStart: "192.168.1.0",
IpEnd: "192.168.1.222",
Name: "fake-dd",
}
Network.Add(n1)
Network.NewLease("fake-vv", n1.Name)
Network.NewLease("fake-dd", n1.Name)
{
le1 := Network.GetLease("fake-vv", n1.Name)
assert.Equal(t, "192.168.1.0", le1.Address, "MUST be .0")
le2 := Network.GetLease("fake-dd", n1.Name)
assert.Equal(t, "192.168.1.1", le2.Address, "MUST be .1")
}
}

View File

@@ -15,7 +15,7 @@ func TestBridgeWriteAndReadByTap(t *testing.T) {
//open tap kernel //open tap kernel
dev01, err := NewKernelTap("true", TapConfig{Type: TAP}) dev01, err := NewKernelTap("true", TapConfig{Type: TAP})
if err != nil { if err != nil {
t.Errorf("Tap.Open %s", err) t.Skipf("Tap.Open %s", err)
return return
} }
dev02, err := NewKernelTap("true", TapConfig{Type: TAP}) dev02, err := NewKernelTap("true", TapConfig{Type: TAP})

View File

@@ -2,7 +2,6 @@ package schema
type Lease struct { type Lease struct {
Address string `json:"address"` Address string `json:"address"`
UUID string `json:"uuid"`
Alias string `json:"alias"` Alias string `json:"alias"`
Client string `json:"client"` Client string `json:"client"`
Type string `json:"type"` Type string `json:"type"`

View File

@@ -61,7 +61,7 @@ func (w *OpenLANWorker) Initialize() {
} }
cache.Network.Add(&n) cache.Network.Add(&n)
for _, ht := range w.cfg.Hosts { for _, ht := range w.cfg.Hosts {
lease := cache.Network.AddLease(ht.Hostname, ht.Address) lease := cache.Network.AddLease(ht.Hostname, ht.Address, n.Name)
if lease != nil { if lease != nil {
lease.Type = "static" lease.Type = "static"
lease.Network = w.cfg.Name lease.Network = w.cfg.Name

View File

@@ -492,6 +492,19 @@ func (v *Switch) SignIn(client libol.SocketClient) error {
return nil return nil
} }
func client2Point(client libol.SocketClient) (*models.Point, error) {
addr := client.RemoteAddr()
if private := client.Private(); private == nil {
return nil, libol.NewErr("point %s notFound.", addr)
} else {
obj, ok := private.(*models.Point)
if !ok {
return nil, libol.NewErr("point %s notRight.", addr)
}
return obj, nil
}
}
func (v *Switch) ReadClient(client libol.SocketClient, frame *libol.FrameMessage) error { func (v *Switch) ReadClient(client libol.SocketClient, frame *libol.FrameMessage) error {
addr := client.RemoteAddr() addr := client.RemoteAddr()
if v.out.Has(libol.LOG) { if v.out.Has(libol.LOG) {
@@ -510,16 +523,12 @@ func (v *Switch) ReadClient(client libol.SocketClient, frame *libol.FrameMessage
return nil return nil
} }
// process ethernet frame message. // process ethernet frame message.
private := client.Private() obj, err := client2Point(client)
if private == nil { if err != nil {
return libol.NewErr("point %s notFound.", addr) return err
} }
point, ok := private.(*models.Point) device := obj.Device
if !ok { if device == nil {
return libol.NewErr("point %s notRight.", addr)
}
device := point.Device
if point == nil || device == nil {
return libol.NewErr("Tap devices is nil") return libol.NewErr("Tap devices is nil")
} }
if _, err := device.Write(frame.Frame()); err != nil { if _, err := device.Write(frame.Frame()); err != nil {
@@ -532,10 +541,8 @@ func (v *Switch) ReadClient(client libol.SocketClient, frame *libol.FrameMessage
func (v *Switch) OnClose(client libol.SocketClient) error { func (v *Switch) OnClose(client libol.SocketClient) error {
addr := client.RemoteAddr() addr := client.RemoteAddr()
v.out.Info("Switch.OnClose: %s", addr) v.out.Info("Switch.OnClose: %s", addr)
// already not need support free list for device. if obj, err := client2Point(client); err == nil {
uuid := cache.Point.GetUUID(addr) cache.Network.DelLease(obj.Alias, obj.Network)
if cache.Point.GetAddr(uuid) == addr { // not has newer
cache.Network.DelLease(uuid)
} }
cache.Point.Del(addr) cache.Point.Del(addr)
return nil return nil