Skip to content
Snippets Groups Projects
Commit b9b019c7 authored by Serge Bazanski's avatar Serge Bazanski
Browse files

Begin implementing public interface for server/peer info

These will be consumed by a gRPC service. The server has a public
interface that returns basic information, and a snapshotted list of peer
status/info.

Also some small drive-by fixes.
parent 78ca72e9
No related branches found
No related tags found
No related merge requests found
......@@ -11,6 +11,7 @@
# Please keep the list sorted.
Cedric Kienzler
Christoph Petrausch
Daniel Czerwonk
Oliver Herms
Christoph Petrausch
Serge Bazanski
......@@ -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
......@@ -9,7 +9,7 @@ import (
)
type Global struct {
LocalAS uint32
LocalASN uint32
RouterID uint32
Port uint16
LocalAddressList []net.IP
......
......@@ -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 {}
}
......@@ -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",
],
)
......@@ -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,
......
......@@ -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()
}
......@@ -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
}
......
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)
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment