summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--api/grpc_server.go11
-rw-r--r--config/default.go50
-rw-r--r--config/serve.go1
-rw-r--r--docs/sources/configuration.md5
-rw-r--r--gobgpd/main.go6
-rw-r--r--server/peer.go76
-rw-r--r--server/server.go78
-rw-r--r--server/server_test.go68
8 files changed, 240 insertions, 55 deletions
diff --git a/api/grpc_server.go b/api/grpc_server.go
index c973e24b..40a567e4 100644
--- a/api/grpc_server.go
+++ b/api/grpc_server.go
@@ -197,11 +197,12 @@ func NewPeerFromConfigStruct(pconf *config.Neighbor) *Peer {
TOTAL: s.Messages.Sent.Total,
},
},
- Received: s.AdjTable.Received,
- Accepted: s.AdjTable.Accepted,
- Advertised: s.AdjTable.Advertised,
- PeerAs: s.PeerAs,
- PeerType: uint32(s.PeerType.ToInt()),
+ Received: s.AdjTable.Received,
+ Accepted: s.AdjTable.Accepted,
+ Advertised: s.AdjTable.Advertised,
+ PeerAs: s.PeerAs,
+ PeerType: uint32(s.PeerType.ToInt()),
+ NeighborAddress: pconf.State.NeighborAddress,
},
Timers: &Timers{
Config: &TimersConfig{
diff --git a/config/default.go b/config/default.go
index 1ff57adb..9daba924 100644
--- a/config/default.go
+++ b/config/default.go
@@ -276,15 +276,38 @@ func validatePeerGroupConfig(n *Neighbor, b *BgpConfigSet) error {
if name == "" {
return nil
}
+
+ pg, err := getPeerGroup(name, b)
+ if err != nil {
+ return err
+ }
+
+ if pg.Config.PeerAs != 0 && n.Config.PeerAs != 0 {
+ return fmt.Errorf("Cannot configure remote-as for members. PeerGroup AS %d.", pg.Config.PeerAs)
+ }
+ return nil
+}
+
+func getPeerGroup(n string, b *BgpConfigSet) (*PeerGroup, error) {
+ if n == "" {
+ return nil, fmt.Errorf("peer-group name is not configured")
+ }
for _, pg := range b.PeerGroups {
- if name == pg.Config.PeerGroupName {
- if pg.Config.PeerAs != 0 && n.Config.PeerAs != 0 {
- return fmt.Errorf("Cannot configure remote-as for members. Peer-group AS %d.", pg.Config.PeerAs)
- }
- return nil
+ if n == pg.Config.PeerGroupName {
+ return &pg, nil
}
}
- return fmt.Errorf("No such peer-group: %s", name)
+ return nil, fmt.Errorf("No such peer-group: %s", n)
+}
+
+func validateDynamicNeighborConfig(d *DynamicNeighborConfig, b *BgpConfigSet) error {
+ if _, err := getPeerGroup(d.PeerGroup, b); err != nil {
+ return err
+ }
+ if _, _, err := net.ParseCIDR(d.Prefix); err != nil {
+ return fmt.Errorf("Invalid Dynamic Neighbor prefix %s", d.Prefix)
+ }
+ return nil
}
func setDefaultConfigValuesWithViper(v *viper.Viper, b *BgpConfigSet) error {
@@ -344,6 +367,12 @@ func setDefaultConfigValuesWithViper(v *viper.Viper, b *BgpConfigSet) error {
}
}
+ for _, d := range b.DynamicNeighbors {
+ if err := validateDynamicNeighborConfig(&d.Config, b); err != nil {
+ return err
+ }
+ }
+
for idx, r := range b.RpkiServers {
if r.Config.Port == 0 {
b.RpkiServers[idx].Config.Port = rtr.RPKI_DEFAULT_PORT
@@ -372,11 +401,12 @@ func setDefaultConfigValuesWithViper(v *viper.Viper, b *BgpConfigSet) error {
func OverwriteNeighborConfigWithPeerGroup(c *Neighbor, pg *PeerGroup) error {
v := viper.New()
- val, ok := configuredFields[c.Config.NeighborAddress]
- if !ok {
- return fmt.Errorf("No such neighbor %s", c.Config.NeighborAddress)
+ val, ok := configuredFields[c.State.NeighborAddress]
+ if ok {
+ v.Set("neighbor", val)
+ } else {
+ v.Set("neighbor.config.peer-group", c.Config.PeerGroup)
}
- v.Set("neighbor", val)
overwriteConfig(&c.Config, &pg.Config, "neighbor.config", v)
overwriteConfig(&c.Timers.Config, &pg.Timers.Config, "neighbor.timers.config", v)
diff --git a/config/serve.go b/config/serve.go
index 8dfda75e..73e2c7bb 100644
--- a/config/serve.go
+++ b/config/serve.go
@@ -19,6 +19,7 @@ type BgpConfigSet struct {
Collector Collector `mapstructure:"collector"`
DefinedSets DefinedSets `mapstructure:"defined-sets"`
PolicyDefinitions []PolicyDefinition `mapstructure:"policy-definitions"`
+ DynamicNeighbors []DynamicNeighbor `mapstructure:"dynamic-neighbors"`
}
func ReadConfigfileServe(path, format string, configCh chan *BgpConfigSet) {
diff --git a/docs/sources/configuration.md b/docs/sources/configuration.md
index 67c7c167..441cade2 100644
--- a/docs/sources/configuration.md
+++ b/docs/sources/configuration.md
@@ -140,6 +140,11 @@
[peer-groups.afi-safis.config]
afi-safi-name = "ipv4-unicast"
+[[dynamic-neighbors]]
+ [dynamic-neighbors.config]
+ prefix = "20.0.0.0/24"
+ peer-group = "my-peer-group"
+
[[defined-sets.prefix-sets]]
prefix-set-name = "ps0"
[[defined-sets.prefix-sets.prefix-list]]
diff --git a/gobgpd/main.go b/gobgpd/main.go
index 4b4b5047..caf1793f 100644
--- a/gobgpd/main.go
+++ b/gobgpd/main.go
@@ -266,6 +266,12 @@ func main() {
}
updatePolicy = updatePolicy || u
}
+ for _, dn := range newConfig.DynamicNeighbors {
+ log.Infof("Dynamic Neighbor %s is added to PeerGroup %s", dn.Config.Prefix, dn.Config.PeerGroup)
+ if err := bgpServer.AddDynamicNeighbor(&dn); err != nil {
+ log.Warn(err)
+ }
+ }
for i, p := range added {
log.Infof("Peer %v is added", p.State.NeighborAddress)
if err := bgpServer.AddNeighbor(&added[i]); err != nil {
diff --git a/server/peer.go b/server/peer.go
index 4e5b7435..a1ec2404 100644
--- a/server/peer.go
+++ b/server/peer.go
@@ -32,14 +32,16 @@ const (
)
type PeerGroup struct {
- Conf *config.PeerGroup
- members map[string]config.Neighbor
+ Conf *config.PeerGroup
+ members map[string]config.Neighbor
+ dynamicNeighbors map[string]*config.DynamicNeighbor
}
func NewPeerGroup(c *config.PeerGroup) *PeerGroup {
return &PeerGroup{
- Conf: c,
- members: make(map[string]config.Neighbor, 0),
+ Conf: c,
+ members: make(map[string]config.Neighbor, 0),
+ dynamicNeighbors: make(map[string]*config.DynamicNeighbor, 0),
}
}
@@ -51,6 +53,29 @@ func (pg *PeerGroup) DeleteMember(c config.Neighbor) {
delete(pg.members, c.State.NeighborAddress)
}
+func (pg *PeerGroup) AddDynamicNeighbor(c *config.DynamicNeighbor) {
+ pg.dynamicNeighbors[c.Config.Prefix] = c
+}
+
+func newDynamicPeer(g *config.Global, neighborAddress string, pg *config.PeerGroup, loc *table.TableManager, policy *table.RoutingPolicy) *Peer {
+ conf := config.Neighbor{
+ Config: config.NeighborConfig{
+ PeerGroup: pg.Config.PeerGroupName,
+ },
+ Transport: config.Transport{
+ Config: config.TransportConfig{
+ PassiveMode: true,
+ },
+ },
+ }
+ config.OverwriteNeighborConfigWithPeerGroup(&conf, pg)
+ config.SetDefaultNeighborConfigValues(&conf, g.Config.As)
+ conf.State.NeighborAddress = neighborAddress
+ peer := NewPeer(g, &conf, loc, policy)
+ peer.fsm.state = bgp.BGP_FSM_ACTIVE
+ return peer
+}
+
type Peer struct {
tableId string
fsm *FSM
@@ -104,6 +129,10 @@ func (peer *Peer) isGracefulRestartEnabled() bool {
return peer.fsm.pConf.GracefulRestart.State.Enabled
}
+func (peer *Peer) isDynamicNeighbor() bool {
+ return peer.fsm.pConf.Config.NeighborAddress == "" && peer.fsm.pConf.Config.NeighborInterface == ""
+}
+
func (peer *Peer) recvedAllEOR() bool {
for _, a := range peer.fsm.pConf.AfiSafis {
if s := a.MpGracefulRestart.State; s.Enabled && !s.EndOfRibReceived {
@@ -585,3 +614,42 @@ func (peer *Peer) ToConfig(getAdvertised bool) *config.Neighbor {
func (peer *Peer) DropAll(rfList []bgp.RouteFamily) {
peer.adjRibIn.Drop(rfList)
}
+
+func (peer *Peer) stopFSM() error {
+ failed := false
+ addr := peer.fsm.pConf.State.NeighborAddress
+ t1 := time.AfterFunc(time.Minute*5, func() {
+ log.WithFields(log.Fields{
+ "Topic": "Peer",
+ }).Warnf("Failed to free the fsm.h.t for %s", addr)
+ failed = true
+ })
+ peer.fsm.h.t.Kill(nil)
+ peer.fsm.h.t.Wait()
+ t1.Stop()
+ if !failed {
+ log.WithFields(log.Fields{
+ "Topic": "Peer",
+ "Key": addr,
+ }).Debug("freed fsm.h.t")
+ cleanInfiniteChannel(peer.outgoing)
+ }
+ failed = false
+ t2 := time.AfterFunc(time.Minute*5, func() {
+ log.WithFields(log.Fields{
+ "Topic": "Peer",
+ }).Warnf("Failed to free the fsm.t for %s", addr)
+ failed = true
+ })
+ peer.fsm.t.Kill(nil)
+ peer.fsm.t.Wait()
+ t2.Stop()
+ if !failed {
+ log.WithFields(log.Fields{
+ "Topic": "Peer",
+ "Key": addr,
+ }).Debug("freed fsm.t")
+ return nil
+ }
+ return fmt.Errorf("Failed to free FSM for %s", addr)
+}
diff --git a/server/server.go b/server/server.go
index 7428d47e..d1b0b0ff 100644
--- a/server/server.go
+++ b/server/server.go
@@ -236,6 +236,16 @@ func (server *BgpServer) Serve() {
"Topic": "Peer",
}).Debugf("Accepted a new passive connection from:%s", remoteAddr)
peer.PassConn(conn)
+ } else if pg := server.matchLongestDynamicNeighborPrefix(remoteAddr); pg != nil {
+ log.WithFields(log.Fields{
+ "Topic": "Peer",
+ }).Debugf("Accepted a new dynamic neighbor from:%s", remoteAddr)
+ peer := newDynamicPeer(&server.bgpConfig.Global, remoteAddr, pg.Conf, server.globalRib, server.policy)
+ server.policy.Reset(nil, map[string]config.ApplyPolicy{peer.ID(): peer.fsm.pConf.ApplyPolicy})
+ server.neighborMap[remoteAddr] = peer
+ peer.startFSMHandler(server.fsmincomingCh, server.fsmStateCh)
+ server.broadcastPeerState(peer, bgp.BGP_FSM_ACTIVE)
+ peer.PassConn(conn)
} else {
log.WithFields(log.Fields{
"Topic": "Peer",
@@ -280,6 +290,24 @@ func (server *BgpServer) Serve() {
}
}
+func (server *BgpServer) matchLongestDynamicNeighborPrefix(a string) *PeerGroup {
+ ipAddr := net.ParseIP(a)
+ longestMask := net.CIDRMask(0, 32).String()
+ var longestPG *PeerGroup
+ for _, pg := range server.peerGroupMap {
+ for _, d := range pg.dynamicNeighbors {
+ _, netAddr, _ := net.ParseCIDR(d.Config.Prefix)
+ if netAddr.Contains(ipAddr) {
+ if netAddr.Mask.String() > longestMask {
+ longestMask = netAddr.Mask.String()
+ longestPG = pg
+ }
+ }
+ }
+ }
+ return longestPG
+}
+
func sendFsmOutgoingMsg(peer *Peer, paths []*table.Path, notification *bgp.BGPMessage, stayIdle bool) {
peer.outgoing.In() <- &FsmOutgoingMsg{
Paths: paths,
@@ -721,6 +749,11 @@ func (server *BgpServer) handleFSMMessage(peer *Peer, e *FsmMsg) {
peer.fsm.pConf.State.PeerAs = 0
peer.fsm.peerInfo.AS = 0
}
+ if peer.isDynamicNeighbor() {
+ peer.stopPeerRestarting()
+ go peer.stopFSM()
+ delete(server.neighborMap, peer.fsm.pConf.State.NeighborAddress)
+ }
} else if peer.fsm.pConf.GracefulRestart.State.PeerRestarting && nextState == bgp.BGP_FSM_IDLE {
if peer.fsm.pConf.GracefulRestart.State.LongLivedEnabled {
llgr, no_llgr := peer.llgrFamilies()
@@ -1716,6 +1749,13 @@ func (s *BgpServer) AddNeighbor(c *config.Neighbor) error {
}, true)
}
+func (s *BgpServer) AddDynamicNeighbor(c *config.DynamicNeighbor) error {
+ return s.mgmtOperation(func() error {
+ s.peerGroupMap[c.Config.PeerGroup].AddDynamicNeighbor(c)
+ return nil
+ }, true)
+}
+
func (server *BgpServer) deletePeerGroup(pg *config.PeerGroup) error {
name := pg.Config.PeerGroupName
@@ -1766,41 +1806,7 @@ func (server *BgpServer) deleteNeighbor(c *config.Neighbor, code, subcode uint8)
n.fsm.sendNotification(code, subcode, nil, "")
n.stopPeerRestarting()
- go func(addr string) {
- failed := false
- t1 := time.AfterFunc(time.Minute*5, func() {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- }).Warnf("Failed to free the fsm.h.t for %s", addr)
- failed = true
- })
- n.fsm.h.t.Kill(nil)
- n.fsm.h.t.Wait()
- t1.Stop()
- if !failed {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": addr,
- }).Debug("freed fsm.h.t")
- cleanInfiniteChannel(n.outgoing)
- }
- failed = false
- t2 := time.AfterFunc(time.Minute*5, func() {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- }).Warnf("Failed to free the fsm.t for %s", addr)
- failed = true
- })
- n.fsm.t.Kill(nil)
- n.fsm.t.Wait()
- t2.Stop()
- if !failed {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": addr,
- }).Debug("freed fsm.t")
- }
- }(addr)
+ go n.stopFSM()
delete(server.neighborMap, addr)
server.dropPeerAllRoutes(n, n.configuredRFlist())
return nil
@@ -1861,7 +1867,7 @@ func (s *BgpServer) updateNeighbor(c *config.Neighbor) (needsSoftResetIn bool, e
addr, err := config.ExtractNeighborAddress(c)
if err != nil {
- return err
+ return needsSoftResetIn, err
}
peer, ok := s.neighborMap[addr]
diff --git a/server/server_test.go b/server/server_test.go
index 887f5ec4..e5deb975 100644
--- a/server/server_test.go
+++ b/server/server_test.go
@@ -441,6 +441,74 @@ func TestPeerGroup(test *testing.T) {
}
}
+func TestDynamicNeighbor(t *testing.T) {
+ assert := assert.New(t)
+ log.SetLevel(log.DebugLevel)
+ s1 := NewBgpServer()
+ go s1.Serve()
+ err := s1.Start(&config.Global{
+ Config: config.GlobalConfig{
+ As: 1,
+ RouterId: "1.1.1.1",
+ Port: 10179,
+ },
+ })
+ assert.Nil(err)
+ defer s1.Stop()
+
+ g := &config.PeerGroup{
+ Config: config.PeerGroupConfig{
+ PeerAs: 2,
+ PeerGroupName: "g",
+ },
+ }
+ err = s1.AddPeerGroup(g)
+ assert.Nil(err)
+
+ d := &config.DynamicNeighbor{
+ Config: config.DynamicNeighborConfig{
+ Prefix: "127.0.0.0/24",
+ PeerGroup: "g",
+ },
+ }
+ err = s1.AddDynamicNeighbor(d)
+ assert.Nil(err)
+
+ s2 := NewBgpServer()
+ go s2.Serve()
+ err = s2.Start(&config.Global{
+ Config: config.GlobalConfig{
+ As: 2,
+ RouterId: "2.2.2.2",
+ Port: -1,
+ },
+ })
+ assert.Nil(err)
+ defer s2.Stop()
+
+ m := &config.Neighbor{
+ Config: config.NeighborConfig{
+ NeighborAddress: "127.0.0.1",
+ PeerAs: 1,
+ },
+ Transport: config.Transport{
+ Config: config.TransportConfig{
+ RemotePort: 10179,
+ },
+ },
+ }
+ err = s2.AddNeighbor(m)
+
+ assert.Nil(err)
+
+ for {
+ time.Sleep(time.Second)
+ if s2.GetNeighbor("", false)[0].State.SessionState == config.SESSION_STATE_ESTABLISHED {
+ break
+ }
+ }
+}
+
func TestGracefulRestartTimerExpired(t *testing.T) {
assert := assert.New(t)
s1 := NewBgpServer()