diff options
author | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2018-07-07 13:48:38 +0900 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2018-07-07 20:44:25 +0900 |
commit | c4775c42510d1f1ddd55036dc19e982712fa6a0b (patch) | |
tree | 6ec8b61d4338c809e239e3003a2d32d480898e22 /pkg/server/bmp.go | |
parent | b3079759aa13172fcb548a83da9a9653d8d5fed4 (diff) |
follow Standard Go Project Layout
https://github.com/golang-standards/project-layout
Now you can see clearly what are private and public library code.
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
Diffstat (limited to 'pkg/server/bmp.go')
-rw-r--r-- | pkg/server/bmp.go | 358 |
1 files changed, 358 insertions, 0 deletions
diff --git a/pkg/server/bmp.go b/pkg/server/bmp.go new file mode 100644 index 00000000..7904fae1 --- /dev/null +++ b/pkg/server/bmp.go @@ -0,0 +1,358 @@ +// Copyright (C) 2015-2016 Nippon Telegraph and Telephone Corporation. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +// implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package server + +import ( + "fmt" + "net" + "strconv" + "time" + + "github.com/osrg/gobgp/internal/pkg/config" + "github.com/osrg/gobgp/internal/pkg/table" + "github.com/osrg/gobgp/pkg/packet/bgp" + "github.com/osrg/gobgp/pkg/packet/bmp" + log "github.com/sirupsen/logrus" +) + +type ribout map[string][]*table.Path + +func newribout() ribout { + return make(map[string][]*table.Path) +} + +// return true if we need to send the path to the BMP server +func (r ribout) update(p *table.Path) bool { + key := p.GetNlri().String() // TODO expose (*Path).getPrefix() + l := r[key] + if p.IsWithdraw { + if len(l) == 0 { + return false + } + n := make([]*table.Path, 0, len(l)) + for _, q := range l { + if p.GetSource() == q.GetSource() { + continue + } + n = append(n, q) + } + if len(n) == 0 { + delete(r, key) + } else { + r[key] = n + } + return true + } + + if len(l) == 0 { + r[key] = []*table.Path{p} + return true + } + + doAppend := true + for idx, q := range l { + if p.GetSource() == q.GetSource() { + // if we have sent the same path, don't send it again + if p.Equal(q) { + return false + } + l[idx] = p + doAppend = false + } + } + if doAppend { + r[key] = append(r[key], p) + } + return true +} + +func (b *bmpClient) tryConnect() *net.TCPConn { + interval := 1 + for { + log.WithFields(log.Fields{"Topic": "bmp"}).Debugf("Connecting BMP server:%s", b.host) + conn, err := net.Dial("tcp", b.host) + if err != nil { + select { + case <-b.dead: + return nil + default: + } + time.Sleep(time.Duration(interval) * time.Second) + if interval < 30 { + interval *= 2 + } + } else { + log.WithFields(log.Fields{"Topic": "bmp"}).Infof("BMP server is connected:%s", b.host) + return conn.(*net.TCPConn) + } + } +} + +func (b *bmpClient) Stop() { + close(b.dead) +} + +func (b *bmpClient) loop() { + for { + conn := b.tryConnect() + if conn == nil { + break + } + + if func() bool { + ops := []WatchOption{WatchPeerState(true)} + if b.c.RouteMonitoringPolicy == config.BMP_ROUTE_MONITORING_POLICY_TYPE_BOTH { + log.WithFields( + log.Fields{"Topic": "bmp"}, + ).Warn("both option for route-monitoring-policy is obsoleted") + } + if b.c.RouteMonitoringPolicy == config.BMP_ROUTE_MONITORING_POLICY_TYPE_PRE_POLICY || b.c.RouteMonitoringPolicy == config.BMP_ROUTE_MONITORING_POLICY_TYPE_ALL { + ops = append(ops, WatchUpdate(true)) + } + if b.c.RouteMonitoringPolicy == config.BMP_ROUTE_MONITORING_POLICY_TYPE_POST_POLICY || b.c.RouteMonitoringPolicy == config.BMP_ROUTE_MONITORING_POLICY_TYPE_ALL { + ops = append(ops, WatchPostUpdate(true)) + } + if b.c.RouteMonitoringPolicy == config.BMP_ROUTE_MONITORING_POLICY_TYPE_LOCAL_RIB || b.c.RouteMonitoringPolicy == config.BMP_ROUTE_MONITORING_POLICY_TYPE_ALL { + ops = append(ops, WatchBestPath(true)) + } + if b.c.RouteMirroringEnabled { + ops = append(ops, WatchMessage(false)) + } + w := b.s.Watch(ops...) + defer w.Stop() + + var tickerCh <-chan time.Time + if b.c.StatisticsTimeout == 0 { + log.WithFields(log.Fields{"Topic": "bmp"}).Debug("statistics reports disabled") + } else { + t := time.NewTicker(time.Duration(b.c.StatisticsTimeout) * time.Second) + defer t.Stop() + tickerCh = t.C + } + + write := func(msg *bmp.BMPMessage) error { + buf, _ := msg.Serialize() + _, err := conn.Write(buf) + if err != nil { + log.Warnf("failed to write to bmp server %s", b.host) + } + return err + } + + if err := write(bmp.NewBMPInitiation([]bmp.BMPInfoTLVInterface{})); err != nil { + return false + } + + for { + select { + case ev := <-w.Event(): + switch msg := ev.(type) { + case *WatchEventUpdate: + info := &table.PeerInfo{ + Address: msg.PeerAddress, + AS: msg.PeerAS, + ID: msg.PeerID, + } + if msg.Payload == nil { + var pathList []*table.Path + if msg.Init { + pathList = msg.PathList + } else { + for _, p := range msg.PathList { + if b.ribout.update(p) { + pathList = append(pathList, p) + } + } + } + for _, path := range pathList { + for _, u := range table.CreateUpdateMsgFromPaths([]*table.Path{path}) { + payload, _ := u.Serialize() + if err := write(bmpPeerRoute(bmp.BMP_PEER_TYPE_GLOBAL, msg.PostPolicy, 0, true, info, msg.Timestamp.Unix(), payload)); err != nil { + return false + } + } + } + } else { + if err := write(bmpPeerRoute(bmp.BMP_PEER_TYPE_GLOBAL, msg.PostPolicy, 0, msg.FourBytesAs, info, msg.Timestamp.Unix(), msg.Payload)); err != nil { + return false + } + } + case *WatchEventBestPath: + info := &table.PeerInfo{ + Address: net.ParseIP("0.0.0.0").To4(), + AS: b.s.bgpConfig.Global.Config.As, + ID: net.ParseIP(b.s.bgpConfig.Global.Config.RouterId).To4(), + } + for _, p := range msg.PathList { + u := table.CreateUpdateMsgFromPaths([]*table.Path{p})[0] + if payload, err := u.Serialize(); err != nil { + return false + } else if err = write(bmpPeerRoute(bmp.BMP_PEER_TYPE_LOCAL_RIB, false, 0, true, info, p.GetTimestamp().Unix(), payload)); err != nil { + return false + } + } + case *WatchEventPeerState: + if msg.State == bgp.BGP_FSM_ESTABLISHED { + if err := write(bmpPeerUp(msg, bmp.BMP_PEER_TYPE_GLOBAL, false, 0)); err != nil { + return false + } + } else { + if err := write(bmpPeerDown(msg, bmp.BMP_PEER_TYPE_GLOBAL, false, 0)); err != nil { + return false + } + } + case *WatchEventMessage: + info := &table.PeerInfo{ + Address: msg.PeerAddress, + AS: msg.PeerAS, + ID: msg.PeerID, + } + if err := write(bmpPeerRouteMirroring(bmp.BMP_PEER_TYPE_GLOBAL, 0, info, msg.Timestamp.Unix(), msg.Message)); err != nil { + return false + } + } + case <-tickerCh: + neighborList := b.s.GetNeighbor("", true) + for _, n := range neighborList { + if n.State.SessionState != config.SESSION_STATE_ESTABLISHED { + continue + } + if err := write(bmpPeerStats(bmp.BMP_PEER_TYPE_GLOBAL, 0, 0, n)); err != nil { + return false + } + } + case <-b.dead: + term := bmp.NewBMPTermination([]bmp.BMPTermTLVInterface{ + bmp.NewBMPTermTLV16(bmp.BMP_TERM_TLV_TYPE_REASON, bmp.BMP_TERM_REASON_PERMANENTLY_ADMIN), + }) + if err := write(term); err != nil { + return false + } + conn.Close() + return true + } + } + }() { + return + } + } +} + +type bmpClient struct { + s *BgpServer + dead chan struct{} + host string + c *config.BmpServerConfig + ribout ribout +} + +func bmpPeerUp(ev *WatchEventPeerState, t uint8, policy bool, pd uint64) *bmp.BMPMessage { + var flags uint8 = 0 + if policy { + flags |= bmp.BMP_PEER_FLAG_POST_POLICY + } + ph := bmp.NewBMPPeerHeader(t, flags, pd, ev.PeerAddress.String(), ev.PeerAS, ev.PeerID.String(), float64(ev.Timestamp.Unix())) + return bmp.NewBMPPeerUpNotification(*ph, ev.LocalAddress.String(), ev.LocalPort, ev.PeerPort, ev.SentOpen, ev.RecvOpen) +} + +func bmpPeerDown(ev *WatchEventPeerState, t uint8, policy bool, pd uint64) *bmp.BMPMessage { + var flags uint8 = 0 + if policy { + flags |= bmp.BMP_PEER_FLAG_POST_POLICY + } + ph := bmp.NewBMPPeerHeader(t, flags, pd, ev.PeerAddress.String(), ev.PeerAS, ev.PeerID.String(), float64(ev.Timestamp.Unix())) + return bmp.NewBMPPeerDownNotification(*ph, uint8(ev.StateReason.PeerDownReason), ev.StateReason.BGPNotification, ev.StateReason.Data) +} + +func bmpPeerRoute(t uint8, policy bool, pd uint64, fourBytesAs bool, peeri *table.PeerInfo, timestamp int64, payload []byte) *bmp.BMPMessage { + var flags uint8 = 0 + if policy { + flags |= bmp.BMP_PEER_FLAG_POST_POLICY + } + if !fourBytesAs { + flags |= bmp.BMP_PEER_FLAG_TWO_AS + } + ph := bmp.NewBMPPeerHeader(t, flags, pd, peeri.Address.String(), peeri.AS, peeri.ID.String(), float64(timestamp)) + m := bmp.NewBMPRouteMonitoring(*ph, nil) + body := m.Body.(*bmp.BMPRouteMonitoring) + body.BGPUpdatePayload = payload + return m +} + +func bmpPeerStats(peerType uint8, peerDist uint64, timestamp int64, neighConf *config.Neighbor) *bmp.BMPMessage { + var peerFlags uint8 = 0 + ph := bmp.NewBMPPeerHeader(peerType, peerFlags, peerDist, neighConf.State.NeighborAddress, neighConf.State.PeerAs, neighConf.State.RemoteRouterId, float64(timestamp)) + return bmp.NewBMPStatisticsReport( + *ph, + []bmp.BMPStatsTLVInterface{ + bmp.NewBMPStatsTLV64(bmp.BMP_STAT_TYPE_ADJ_RIB_IN, uint64(neighConf.State.AdjTable.Accepted)), + bmp.NewBMPStatsTLV64(bmp.BMP_STAT_TYPE_LOC_RIB, uint64(neighConf.State.AdjTable.Advertised+neighConf.State.AdjTable.Filtered)), + bmp.NewBMPStatsTLV32(bmp.BMP_STAT_TYPE_WITHDRAW_UPDATE, neighConf.State.Messages.Received.WithdrawUpdate), + bmp.NewBMPStatsTLV32(bmp.BMP_STAT_TYPE_WITHDRAW_PREFIX, neighConf.State.Messages.Received.WithdrawPrefix), + }, + ) +} + +func bmpPeerRouteMirroring(peerType uint8, peerDist uint64, peerInfo *table.PeerInfo, timestamp int64, msg *bgp.BGPMessage) *bmp.BMPMessage { + var peerFlags uint8 = 0 + ph := bmp.NewBMPPeerHeader(peerType, peerFlags, peerDist, peerInfo.Address.String(), peerInfo.AS, peerInfo.ID.String(), float64(timestamp)) + return bmp.NewBMPRouteMirroring( + *ph, + []bmp.BMPRouteMirrTLVInterface{ + // RFC7854: BGP Message TLV MUST occur last in the list of TLVs + bmp.NewBMPRouteMirrTLVBGPMsg(bmp.BMP_ROUTE_MIRRORING_TLV_TYPE_BGP_MSG, msg), + }, + ) +} + +func (b *bmpClientManager) addServer(c *config.BmpServerConfig) error { + host := net.JoinHostPort(c.Address, strconv.Itoa(int(c.Port))) + if _, y := b.clientMap[host]; y { + return fmt.Errorf("bmp client %s is already configured", host) + } + b.clientMap[host] = &bmpClient{ + s: b.s, + dead: make(chan struct{}), + host: host, + c: c, + ribout: newribout(), + } + go b.clientMap[host].loop() + return nil +} + +func (b *bmpClientManager) deleteServer(c *config.BmpServerConfig) error { + host := net.JoinHostPort(c.Address, strconv.Itoa(int(c.Port))) + if c, y := b.clientMap[host]; !y { + return fmt.Errorf("bmp client %s isn't found", host) + } else { + c.Stop() + delete(b.clientMap, host) + } + return nil +} + +type bmpClientManager struct { + s *BgpServer + clientMap map[string]*bmpClient +} + +func newBmpClientManager(s *BgpServer) *bmpClientManager { + return &bmpClientManager{ + s: s, + clientMap: make(map[string]*bmpClient), + } +} |