diff options
-rw-r--r-- | api/grpc_server.go | 11 | ||||
-rw-r--r-- | config/default.go | 50 | ||||
-rw-r--r-- | config/serve.go | 1 | ||||
-rw-r--r-- | docs/sources/configuration.md | 5 | ||||
-rw-r--r-- | gobgpd/main.go | 6 | ||||
-rw-r--r-- | server/peer.go | 76 | ||||
-rw-r--r-- | server/server.go | 78 | ||||
-rw-r--r-- | server/server_test.go | 68 |
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() |