diff --git a/AUTHORS b/AUTHORS index e5550af433c7e0a9484e2dcc6864dfd4c143e3db..08ed97f577221522d6c0a274e2ebf58a988e5e67 100644 --- a/AUTHORS +++ b/AUTHORS @@ -11,6 +11,7 @@ # Please keep the list sorted. Cedric Kienzler +Christoph Petrausch Daniel Czerwonk Oliver Herms -Christoph Petrausch +Serge Bazanski diff --git a/CONTRIBUTORS b/CONTRIBUTORS index fcfb3b8f82816f64cc48ba60b973fad370be3f6a..50ab2c58221250b162b98ca4e6d6cdc0f0f7eaa4 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -5,6 +5,7 @@ # but not in AUTHORS, because Google holds the copyright. Cedric Kienzler +Christoph Petrausch Daniel Czerwonk Oliver Herms -Christoph Petrausch \ No newline at end of file +Serge Bazanski diff --git a/config/server.go b/config/server.go index fed6f908365319418abb5b5dee1290ec666d418e..964f5be1097c48aaaaca6e064d7e048b67caedea 100644 --- a/config/server.go +++ b/config/server.go @@ -9,7 +9,7 @@ import ( ) type Global struct { - LocalAS uint32 + LocalASN uint32 RouterID uint32 Port uint16 LocalAddressList []net.IP diff --git a/main.go b/main.go index e311932915a7f33c7135d45b804ca4ee429a1544..5ca73039c8a12c21bcb7a94f8497b7642b3eaaac 100644 --- a/main.go +++ b/main.go @@ -3,7 +3,6 @@ package main import ( "fmt" "net" - "sync" "time" "github.com/sirupsen/logrus" @@ -83,7 +82,5 @@ func main() { } }() - var wg sync.WaitGroup - wg.Add(1) - wg.Wait() + select {} } diff --git a/protocols/bgp/server/BUILD.bazel b/protocols/bgp/server/BUILD.bazel index e9ebd9630360ff10efb76e29efaf668dc227d3e8..66889d6c4a8b6fa885eb5281f419fe53cf6a5c31 100644 --- a/protocols/bgp/server/BUILD.bazel +++ b/protocols/bgp/server/BUILD.bazel @@ -41,6 +41,7 @@ go_library( go_test( name = "go_default_test", srcs = [ + "server_test.go", "update_helper_test.go", "withdraw_test.go", ], @@ -49,6 +50,7 @@ go_test( "//net:go_default_library", "//protocols/bgp/packet:go_default_library", "//route:go_default_library", + "//routingtable/locRIB:go_default_library", "//vendor/github.com/stretchr/testify/assert:go_default_library", ], ) diff --git a/protocols/bgp/server/fsm2.go b/protocols/bgp/server/fsm2.go index 6e1682c71b7aaf47ff2858a30b199fc07cb43297..33e000d1c6010a2e3b976b13bec591a7e0e67545 100644 --- a/protocols/bgp/server/fsm2.go +++ b/protocols/bgp/server/fsm2.go @@ -28,7 +28,7 @@ type state interface { // FSM implements the BGP finite state machine (RFC4271) type FSM struct { - peer *Peer + peer *peer eventCh chan int con net.Conn conCh chan net.Conn @@ -72,7 +72,7 @@ type FSM struct { } // NewPassiveFSM2 initiates a new passive FSM -func NewPassiveFSM2(peer *Peer, con *net.TCPConn) *FSM { +func NewPassiveFSM2(peer *peer, con *net.TCPConn) *FSM { fsm := newFSM2(peer) fsm.con = con fsm.state = newIdleState(fsm) @@ -80,14 +80,14 @@ func NewPassiveFSM2(peer *Peer, con *net.TCPConn) *FSM { } // NewActiveFSM2 initiates a new passive FSM -func NewActiveFSM2(peer *Peer) *FSM { +func NewActiveFSM2(peer *peer) *FSM { fsm := newFSM2(peer) fsm.active = true fsm.state = newIdleState(fsm) return fsm } -func newFSM2(peer *Peer) *FSM { +func newFSM2(peer *peer) *FSM { return &FSM{ connectRetryTime: time.Minute, peer: peer, diff --git a/protocols/bgp/server/peer.go b/protocols/bgp/server/peer.go index a0e4c623b5d4d30bdb877311c879b7c568332859..58f53bef65995eb08066e3f0987032a7e85aba7b 100644 --- a/protocols/bgp/server/peer.go +++ b/protocols/bgp/server/peer.go @@ -11,13 +11,22 @@ import ( "github.com/bio-routing/bio-rd/routingtable/filter" ) -type Peer struct { - server *BGPServer - addr net.IP - peerASN uint32 - localASN uint32 - fsms []*FSM - fsmsMu sync.Mutex +type PeerInfo struct { + PeerAddr net.IP + PeerASN uint32 + LocalASN uint32 +} + +type peer struct { + server *bgpServer + addr net.IP + peerASN uint32 + localASN uint32 + + // guarded by fsmsMu + fsms []*FSM + fsmsMu sync.Mutex + rib routingtable.RouteTableClient routerID uint32 addPathSend routingtable.ClientOptions @@ -30,7 +39,15 @@ type Peer struct { exportFilter *filter.Filter } -func (p *Peer) collisionHandling(callingFSM *FSM) bool { +func (p *peer) snapshot() PeerInfo { + return PeerInfo{ + PeerAddr: p.addr, + PeerASN: p.peerASN, + LocalASN: p.localASN, + } +} + +func (p *peer) collisionHandling(callingFSM *FSM) bool { p.fsmsMu.Lock() defer p.fsmsMu.Unlock() @@ -82,8 +99,11 @@ func isEstablishedState(s state) bool { // NewPeer creates a new peer with the given config. If an connection is established, the adjRIBIN of the peer is connected // to the given rib. To actually connect the peer, call Start() on the returned peer. -func NewPeer(c config.Peer, rib routingtable.RouteTableClient, server *BGPServer) (*Peer, error) { - p := &Peer{ +func newPeer(c config.Peer, rib routingtable.RouteTableClient, server *bgpServer) (*peer, error) { + if c.LocalAS == 0 { + c.LocalAS = server.localASN + } + p := &peer{ server: server, addr: c.PeerAddress, peerASN: c.PeerAS, @@ -141,10 +161,10 @@ func filterOrDefault(f *filter.Filter) *filter.Filter { } // GetAddr returns the IP address of the peer -func (p *Peer) GetAddr() net.IP { +func (p *peer) GetAddr() net.IP { return p.addr } -func (p *Peer) Start() { +func (p *peer) Start() { p.fsms[0].start() } diff --git a/protocols/bgp/server/server.go b/protocols/bgp/server/server.go index fef8b4e419d3b071ee9235620f85a0f0254d83f0..61c79de10315e5efa65c27e7da820b3a39aee4df 100644 --- a/protocols/bgp/server/server.go +++ b/protocols/bgp/server/server.go @@ -5,6 +5,7 @@ import ( "io" "net" "strings" + "sync" "github.com/bio-routing/bio-rd/config" "github.com/bio-routing/bio-rd/protocols/bgp/packet" @@ -17,30 +18,50 @@ const ( BGPVersion = 4 ) -type BGPServer struct { +type bgpServer struct { listeners []*TCPListener acceptCh chan *net.TCPConn - peers map[string]*Peer + peers sync.Map routerID uint32 + localASN uint32 } -func NewBgpServer() *BGPServer { - return &BGPServer{ - peers: make(map[string]*Peer), - } +type BGPServer interface { + RouterID() uint32 + Start(*config.Global) error + AddPeer(config.Peer, routingtable.RouteTableClient) error + GetPeerInfoAll() map[string]PeerInfo +} + +func NewBgpServer() BGPServer { + return &bgpServer{} +} + +func (b *bgpServer) GetPeerInfoAll() map[string]PeerInfo { + res := make(map[string]PeerInfo) + b.peers.Range(func(key, value interface{}) bool { + name := key.(string) + peer := value.(*peer) + + res[name] = peer.snapshot() + + return true + }) + return res } -func (b *BGPServer) RouterID() uint32 { +func (b *bgpServer) RouterID() uint32 { return b.routerID } -func (b *BGPServer) Start(c *config.Global) error { +func (b *bgpServer) Start(c *config.Global) error { if err := c.SetDefaultGlobalConfigValues(); err != nil { return fmt.Errorf("Failed to load defaults: %v", err) } log.Infof("ROUTER ID: %d\n", c.RouterID) b.routerID = c.RouterID + b.localASN = c.LocalASN if c.Listen { acceptCh := make(chan *net.TCPConn, 4096) @@ -59,51 +80,53 @@ func (b *BGPServer) Start(c *config.Global) error { return nil } -func (b *BGPServer) incomingConnectionWorker() { +func (b *bgpServer) incomingConnectionWorker() { for { c := <-b.acceptCh peerAddr := strings.Split(c.RemoteAddr().String(), ":")[0] - if _, ok := b.peers[peerAddr]; !ok { + peerInterface, ok := b.peers.Load(peerAddr) + if !ok { c.Close() log.WithFields(log.Fields{ "source": c.RemoteAddr(), }).Warning("TCP connection from unknown source") continue } + peer := peerInterface.(*peer) log.WithFields(log.Fields{ "source": c.RemoteAddr(), }).Info("Incoming TCP connection") log.WithField("Peer", peerAddr).Debug("Sending incoming TCP connection to fsm for peer") - fsm := NewActiveFSM2(b.peers[peerAddr]) + fsm := NewActiveFSM2(peer) fsm.state = newActiveState(fsm) fsm.startConnectRetryTimer() - b.peers[peerAddr].fsmsMu.Lock() - b.peers[peerAddr].fsms = append(b.peers[peerAddr].fsms, fsm) - b.peers[peerAddr].fsmsMu.Unlock() + peer.fsmsMu.Lock() + peer.fsms = append(peer.fsms, fsm) + peer.fsmsMu.Unlock() go fsm.run() fsm.conCh <- c } } -func (b *BGPServer) AddPeer(c config.Peer, rib routingtable.RouteTableClient) error { +func (b *bgpServer) AddPeer(c config.Peer, rib routingtable.RouteTableClient) error { if c.LocalAS > uint16max || c.PeerAS > uint16max { return fmt.Errorf("32bit ASNs are not supported yet") } - peer, err := NewPeer(c, rib, b) + peer, err := newPeer(c, rib, b) if err != nil { return err } peer.routerID = c.RouterID peerAddr := peer.GetAddr().String() - b.peers[peerAddr] = peer - b.peers[peerAddr].Start() + b.peers.Store(peerAddr, peer) + peer.Start() return nil } diff --git a/protocols/bgp/server/server_test.go b/protocols/bgp/server/server_test.go new file mode 100644 index 0000000000000000000000000000000000000000..86095be8e22e28168e2b66c1a70217969263fe18 --- /dev/null +++ b/protocols/bgp/server/server_test.go @@ -0,0 +1,85 @@ +package server + +import ( + "net" + "testing" + "time" + + "github.com/bio-routing/bio-rd/config" + "github.com/bio-routing/bio-rd/routingtable" + "github.com/bio-routing/bio-rd/routingtable/filter" + "github.com/bio-routing/bio-rd/routingtable/locRIB" +) + +func TestBgpServerConfigCheck(t *testing.T) { + s := NewBgpServer() + + err := s.Start(&config.Global{}) + if err == nil { + t.Fatalf("server with empty config should not start") + } + + err = s.Start(&config.Global{ + LocalASN: 204880, + RouterID: 2137, + }) + if err != nil { + t.Fatalf("server should have started, got err: %v", err) + } +} + +func TestBgpServerPeerSnapshot(t *testing.T) { + s := NewBgpServer() + err := s.Start(&config.Global{ + LocalASN: 204880, + RouterID: 2137, + }) + if err != nil { + t.Fatalf("server should have started, got err: %v", err) + } + + info := s.GetPeerInfoAll() + if len(info) != 0 { + t.Fatalf("empty server should have 0 peers, has %d", len(info)) + } + + rib := locRIB.New() + pc := config.Peer{ + AdminEnabled: true, + PeerAS: 65300, + PeerAddress: net.IP([]byte{169, 254, 200, 1}), + LocalAddress: net.IP([]byte{169, 254, 200, 0}), + ReconnectInterval: time.Second * 15, + HoldTime: time.Second * 90, + KeepAlive: time.Second * 30, + Passive: true, + RouterID: s.RouterID(), + AddPathSend: routingtable.ClientOptions{ + MaxPaths: 10, + }, + ImportFilter: filter.NewDrainFilter(), + ExportFilter: filter.NewAcceptAllFilter(), + } + s.AddPeer(pc, rib) + + info = s.GetPeerInfoAll() + if want, got := 1, len(info); want != got { + t.Fatalf("empty server should have %d peers, has %d", want, got) + } + + var peer PeerInfo + for _, v := range info { + peer = v + break + } + + if want, got := net.ParseIP("169.254.200.1"), peer.PeerAddr; !want.Equal(got) { + t.Errorf("PeerAddr: got %v, want %v", got, want) + } + if want, got := uint32(65300), peer.PeerASN; want != got { + t.Errorf("PeerASN: got %v, want %v", got, want) + } + if want, got := uint32(204880), peer.LocalASN; want != got { + t.Errorf("PeerASN: got %v, want %v", got, want) + } +}