mirror of
https://github.com/luscis/openlan.git
synced 2025-10-28 11:11:45 +08:00
335 lines
6.7 KiB
Go
Executable File
335 lines
6.7 KiB
Go
Executable File
package network
|
|
|
|
import (
|
|
"errors"
|
|
"github.com/luscis/openlan/pkg/libol"
|
|
"net"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
type VirtualBridge struct {
|
|
ipMtu int
|
|
name string
|
|
lock sync.RWMutex
|
|
ports map[string]Taper
|
|
macs map[string]*MacFdb
|
|
done chan bool
|
|
ticker *time.Ticker
|
|
timeout int
|
|
address string
|
|
kernel Taper
|
|
out *libol.SubLogger
|
|
sts DeviceStats
|
|
}
|
|
|
|
func NewVirtualBridge(name string, mtu int) *VirtualBridge {
|
|
b := &VirtualBridge{
|
|
name: name,
|
|
ipMtu: mtu,
|
|
ports: make(map[string]Taper, 1024),
|
|
macs: make(map[string]*MacFdb, 1024),
|
|
done: make(chan bool),
|
|
ticker: time.NewTicker(5 * time.Second),
|
|
timeout: 5 * 60,
|
|
out: libol.NewSubLogger(name),
|
|
}
|
|
Bridges.Add(b)
|
|
return b
|
|
}
|
|
|
|
func (b *VirtualBridge) Open(addr string) {
|
|
tapCfg := TapConfig{
|
|
Type: TAP,
|
|
Mtu: b.ipMtu,
|
|
}
|
|
b.out.Info("VirtualBridge.Open %s", addr)
|
|
libol.Go(b.Start)
|
|
if tap, err := NewKernelTap("", tapCfg); err != nil {
|
|
b.out.Error("VirtualBridge.Open new kernel %s", err)
|
|
} else {
|
|
out, err := libol.IpLinkUp(tap.Name())
|
|
if err != nil {
|
|
b.out.Error("VirtualBridge.Open IpAddr %s:%s", err, out)
|
|
}
|
|
b.kernel = tap
|
|
b.out.Info("VirtualBridge.Open %s", tap.Name())
|
|
_ = b.AddSlave(tap.name)
|
|
}
|
|
if addr != "" && b.kernel != nil {
|
|
b.address = addr
|
|
if out, err := libol.IpAddrAdd(b.kernel.Name(), b.address); err != nil {
|
|
b.out.Error("VirtualBridge.Open IpAddr %s:%s", err, out)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (b *VirtualBridge) Kernel() string {
|
|
if b.kernel == nil {
|
|
return ""
|
|
}
|
|
return b.kernel.Name()
|
|
}
|
|
|
|
func (b *VirtualBridge) Close() error {
|
|
if b.kernel != nil {
|
|
if b.address != "" {
|
|
out, err := libol.IpAddrDel(b.kernel.Name(), b.address)
|
|
if err != nil {
|
|
b.out.Error("VirtualBridge.Close: IpAddr %s:%s", err, out)
|
|
}
|
|
}
|
|
_ = b.kernel.Close()
|
|
}
|
|
b.ticker.Stop()
|
|
b.done <- true
|
|
return nil
|
|
}
|
|
|
|
func (b *VirtualBridge) AddSlave(name string) error {
|
|
tap := Taps.Get(name)
|
|
if tap == nil {
|
|
return libol.NewErr("%s notFound", name)
|
|
}
|
|
_ = tap.SetMaster(b)
|
|
b.lock.Lock()
|
|
b.ports[name] = tap
|
|
b.lock.Unlock()
|
|
b.out.Info("VirtualBridge.AddSlave: %s", name)
|
|
libol.Go(func() {
|
|
for {
|
|
data := make([]byte, b.ipMtu)
|
|
n, err := tap.Recv(data)
|
|
if err != nil || n == 0 {
|
|
break
|
|
}
|
|
if libol.HasLog(libol.DEBUG) {
|
|
libol.Debug("VirtualBridge.KernelTap: %s % x", tap.Name(), data[:20])
|
|
}
|
|
m := &Framer{Data: data[:n], Source: tap}
|
|
_ = b.Input(m)
|
|
}
|
|
})
|
|
return nil
|
|
}
|
|
|
|
func (b *VirtualBridge) DelSlave(name string) error {
|
|
b.lock.Lock()
|
|
defer b.lock.Unlock()
|
|
if _, ok := b.ports[name]; ok {
|
|
delete(b.ports, name)
|
|
}
|
|
b.out.Info("VirtualBridge.DelSlave: %s", name)
|
|
return nil
|
|
}
|
|
|
|
func (b *VirtualBridge) ListSlave() <-chan Taper {
|
|
data := make(chan Taper, 32)
|
|
go func() {
|
|
b.lock.RLock()
|
|
defer b.lock.RUnlock()
|
|
for _, obj := range b.ports {
|
|
data <- obj
|
|
}
|
|
data <- nil
|
|
}()
|
|
return data
|
|
}
|
|
|
|
func (b *VirtualBridge) Type() string {
|
|
return ProviderVir
|
|
}
|
|
|
|
func (b *VirtualBridge) String() string {
|
|
return b.name
|
|
}
|
|
|
|
func (b *VirtualBridge) Name() string {
|
|
return b.name
|
|
}
|
|
|
|
func (b *VirtualBridge) Forward(m *Framer) error {
|
|
if err := b.UniCast(m); err != nil {
|
|
_ = b.Flood(m)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (b *VirtualBridge) Expire() error {
|
|
deletes := make([]string, 0, 1024)
|
|
//collect need deleted.
|
|
b.lock.RLock()
|
|
for index, learn := range b.macs {
|
|
now := time.Now().Unix()
|
|
if now-learn.Uptime > int64(b.timeout) {
|
|
deletes = append(deletes, index)
|
|
}
|
|
}
|
|
b.lock.RUnlock()
|
|
b.out.Debug("VirtualBridge.Expire delete %d", len(deletes))
|
|
//execute delete.
|
|
b.lock.Lock()
|
|
for _, d := range deletes {
|
|
if _, ok := b.macs[d]; ok {
|
|
delete(b.macs, d)
|
|
b.out.Event("VirtualBridge.Expire: delete %s", d)
|
|
}
|
|
}
|
|
b.lock.Unlock()
|
|
return nil
|
|
}
|
|
|
|
func (b *VirtualBridge) Start() {
|
|
libol.Go(func() {
|
|
for {
|
|
select {
|
|
case <-b.done:
|
|
return
|
|
case t := <-b.ticker.C:
|
|
b.out.Log("VirtualBridge.Start: Tick at %s", t)
|
|
_ = b.Expire()
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func (b *VirtualBridge) Input(m *Framer) error {
|
|
b.sts.Recv++
|
|
b.Learn(m)
|
|
return b.Forward(m)
|
|
}
|
|
|
|
func (b *VirtualBridge) Eth2Str(addr []byte) string {
|
|
if len(addr) < 6 {
|
|
return ""
|
|
}
|
|
return net.HardwareAddr(addr).String()
|
|
}
|
|
|
|
func (b *VirtualBridge) Learn(m *Framer) {
|
|
mac := m.Data[6:12]
|
|
if mac[0]&0x01 == 0x01 {
|
|
return
|
|
}
|
|
key := b.Eth2Str(mac)
|
|
if l := b.GetMac(key); l != nil {
|
|
b.UpdateMac(key, m.Source)
|
|
return
|
|
}
|
|
learn := &MacFdb{
|
|
Device: m.Source,
|
|
Uptime: time.Now().Unix(),
|
|
NewTime: time.Now().Unix(),
|
|
Address: make([]byte, 6),
|
|
}
|
|
copy(learn.Address, mac)
|
|
b.out.Event("VirtualBridge.Learn: %s on %s", key, m.Source)
|
|
b.AddMac(key, learn)
|
|
}
|
|
|
|
func (b *VirtualBridge) GetMac(mac string) *MacFdb {
|
|
b.lock.RLock()
|
|
defer b.lock.RUnlock()
|
|
if l, ok := b.macs[mac]; ok {
|
|
return l
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (b *VirtualBridge) AddMac(mac string, fdb *MacFdb) {
|
|
b.lock.Lock()
|
|
defer b.lock.Unlock()
|
|
b.macs[mac] = fdb
|
|
}
|
|
|
|
func (b *VirtualBridge) UpdateMac(mac string, device Taper) {
|
|
b.lock.RLock()
|
|
defer b.lock.RUnlock()
|
|
if fdb, ok := b.macs[mac]; ok {
|
|
fdb.Uptime = time.Now().Unix()
|
|
fdb.Device = device
|
|
}
|
|
}
|
|
|
|
func (b *VirtualBridge) ListMac() <-chan *MacFdb {
|
|
data := make(chan *MacFdb, 32)
|
|
go func() {
|
|
b.lock.RLock()
|
|
defer b.lock.RUnlock()
|
|
for _, obj := range b.macs {
|
|
data <- obj
|
|
}
|
|
data <- nil
|
|
}()
|
|
return data
|
|
}
|
|
|
|
func (b *VirtualBridge) Flood(m *Framer) error {
|
|
data := m.Data
|
|
from := m.Source
|
|
if b.out.Has(libol.FLOW) {
|
|
b.out.Flow("VirtualBridge.Flood: % x", data[:20])
|
|
}
|
|
outs := make([]Taper, 0, 32)
|
|
b.lock.RLock()
|
|
for _, port := range b.ports {
|
|
if from != port {
|
|
outs = append(outs, port)
|
|
}
|
|
}
|
|
b.lock.RUnlock()
|
|
for _, port := range outs {
|
|
if b.out.Has(libol.FLOW) {
|
|
b.out.Flow("VirtualBridge.Flood: %s % x", port, data[:20])
|
|
}
|
|
b.sts.Send++
|
|
if _, err := port.Send(data); err != nil {
|
|
b.out.Error("VirtualBridge.Flood: %s %s", port, err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (b *VirtualBridge) UniCast(m *Framer) error {
|
|
data := m.Data
|
|
from := m.Source
|
|
dest := b.Eth2Str(data[:6])
|
|
learn := b.GetMac(dest)
|
|
if learn == nil {
|
|
return errors.New(dest + " notFound")
|
|
}
|
|
out := learn.Device
|
|
if out != from && out.Has(UsUp) { // out should running
|
|
b.sts.Send++
|
|
if _, err := out.Send(data); err != nil {
|
|
b.out.Warn("VirtualBridge.UniCast: %s %s", out, err)
|
|
}
|
|
} else {
|
|
b.sts.Drop++
|
|
}
|
|
if b.out.Has(libol.FLOW) {
|
|
b.out.Flow("VirtualBridge.UniCast: %s to %s % x", from, out, data[:20])
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (b *VirtualBridge) Mtu() int {
|
|
return b.ipMtu
|
|
}
|
|
|
|
func (b *VirtualBridge) Stp(enable bool) error {
|
|
return libol.NewErr("operation notSupport")
|
|
}
|
|
|
|
func (b *VirtualBridge) Delay(value int) error {
|
|
return libol.NewErr("operation notSupport")
|
|
}
|
|
|
|
func (b *VirtualBridge) Stats() DeviceStats {
|
|
return b.sts
|
|
}
|
|
|
|
func (b *VirtualBridge) CallIptables(value int) error {
|
|
return libol.NewErr("operation notSupport")
|
|
}
|