github.com/osrg/gobgp/v3@v3.30.0/pkg/server/bmp.go (about)

     1  // Copyright (C) 2015-2021 Nippon Telegraph and Telephone Corporation.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    12  // implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  package server
    17  
    18  import (
    19  	"context"
    20  	"fmt"
    21  	"net"
    22  	"strconv"
    23  	"sync/atomic"
    24  	"time"
    25  
    26  	api "github.com/osrg/gobgp/v3/api"
    27  	"github.com/osrg/gobgp/v3/internal/pkg/table"
    28  	"github.com/osrg/gobgp/v3/pkg/config/oc"
    29  	"github.com/osrg/gobgp/v3/pkg/log"
    30  	"github.com/osrg/gobgp/v3/pkg/packet/bgp"
    31  	"github.com/osrg/gobgp/v3/pkg/packet/bmp"
    32  )
    33  
    34  type ribout map[string][]*table.Path
    35  
    36  func newribout() ribout {
    37  	return make(map[string][]*table.Path)
    38  }
    39  
    40  // return true if we need to send the path to the BMP server
    41  func (r ribout) update(p *table.Path) bool {
    42  	key := p.GetNlri().String() // TODO expose (*Path).getPrefix()
    43  	l := r[key]
    44  	if p.IsWithdraw {
    45  		if len(l) == 0 {
    46  			return false
    47  		}
    48  		n := make([]*table.Path, 0, len(l))
    49  		for _, q := range l {
    50  			if p.GetSource() == q.GetSource() {
    51  				continue
    52  			}
    53  			n = append(n, q)
    54  		}
    55  		if len(n) == 0 {
    56  			delete(r, key)
    57  		} else {
    58  			r[key] = n
    59  		}
    60  		return true
    61  	}
    62  
    63  	if len(l) == 0 {
    64  		r[key] = []*table.Path{p}
    65  		return true
    66  	}
    67  
    68  	doAppend := true
    69  	for idx, q := range l {
    70  		if p.GetSource() == q.GetSource() {
    71  			// if we have sent the same path, don't send it again
    72  			if p.Equal(q) {
    73  				return false
    74  			}
    75  			l[idx] = p
    76  			doAppend = false
    77  		}
    78  	}
    79  	if doAppend {
    80  		r[key] = append(r[key], p)
    81  	}
    82  	return true
    83  }
    84  
    85  func (b *bmpClient) tryConnect() *net.TCPConn {
    86  	interval := 1
    87  	for {
    88  		b.s.logger.Debug("Connecting to BMP server",
    89  			log.Fields{
    90  				"Topic": "bmp",
    91  				"Key":   b.host})
    92  		conn, err := net.Dial("tcp", b.host)
    93  		if err != nil {
    94  			select {
    95  			case <-b.dead:
    96  				return nil
    97  			default:
    98  			}
    99  			time.Sleep(time.Duration(interval) * time.Second)
   100  			if interval < 30 {
   101  				interval *= 2
   102  			}
   103  		} else {
   104  			b.s.logger.Debug("Connected to BMP server",
   105  				log.Fields{
   106  					"Topic": "bmp",
   107  					"Key":   b.host})
   108  			return conn.(*net.TCPConn)
   109  		}
   110  	}
   111  }
   112  
   113  func (b *bmpClient) Stop() {
   114  	close(b.dead)
   115  }
   116  
   117  func (b *bmpClient) loop() {
   118  	for {
   119  		conn := b.tryConnect()
   120  		if conn == nil {
   121  			break
   122  		}
   123  		atomic.StoreInt64(&b.uptime, time.Now().Unix())
   124  
   125  		if func() bool {
   126  			defer func() {
   127  				atomic.StoreInt64(&b.downtime, time.Now().Unix())
   128  			}()
   129  			ops := []watchOption{watchPeer()}
   130  			if b.c.RouteMonitoringPolicy == oc.BMP_ROUTE_MONITORING_POLICY_TYPE_BOTH {
   131  				b.s.logger.Warn("both option for route-monitoring-policy is obsoleted", log.Fields{"Topic": "bmp"})
   132  			}
   133  			if b.c.RouteMonitoringPolicy == oc.BMP_ROUTE_MONITORING_POLICY_TYPE_PRE_POLICY || b.c.RouteMonitoringPolicy == oc.BMP_ROUTE_MONITORING_POLICY_TYPE_ALL {
   134  				ops = append(ops, watchUpdate(true, "", ""))
   135  			}
   136  			if b.c.RouteMonitoringPolicy == oc.BMP_ROUTE_MONITORING_POLICY_TYPE_POST_POLICY || b.c.RouteMonitoringPolicy == oc.BMP_ROUTE_MONITORING_POLICY_TYPE_ALL {
   137  				ops = append(ops, watchPostUpdate(true, "", ""))
   138  			}
   139  			if b.c.RouteMonitoringPolicy == oc.BMP_ROUTE_MONITORING_POLICY_TYPE_LOCAL_RIB || b.c.RouteMonitoringPolicy == oc.BMP_ROUTE_MONITORING_POLICY_TYPE_ALL {
   140  				ops = append(ops, watchBestPath(true))
   141  			}
   142  			if b.c.RouteMirroringEnabled {
   143  				ops = append(ops, watchMessage(false))
   144  			}
   145  			w := b.s.watch(ops...)
   146  			defer w.Stop()
   147  
   148  			var tickerCh <-chan time.Time
   149  			if b.c.StatisticsTimeout == 0 {
   150  				b.s.logger.Debug("statistics reports disabled", log.Fields{"Topic": "bmp"})
   151  			} else {
   152  				t := time.NewTicker(time.Duration(b.c.StatisticsTimeout) * time.Second)
   153  				defer t.Stop()
   154  				tickerCh = t.C
   155  			}
   156  
   157  			write := func(msg *bmp.BMPMessage) error {
   158  				buf, _ := msg.Serialize()
   159  				_, err := conn.Write(buf)
   160  				if err != nil {
   161  					b.s.logger.Warn("failed to write to bmp server",
   162  						log.Fields{
   163  							"Topic": "bmp",
   164  							"Key":   b.host})
   165  				}
   166  				return err
   167  			}
   168  
   169  			tlv := []bmp.BMPInfoTLVInterface{
   170  				bmp.NewBMPInfoTLVString(bmp.BMP_INIT_TLV_TYPE_SYS_NAME, b.c.SysName),
   171  				bmp.NewBMPInfoTLVString(bmp.BMP_INIT_TLV_TYPE_SYS_DESCR, b.c.SysDescr),
   172  			}
   173  
   174  			if err := write(bmp.NewBMPInitiation(tlv)); err != nil {
   175  				return false
   176  			}
   177  
   178  			for {
   179  				select {
   180  				case ev := <-w.Event():
   181  					switch msg := ev.(type) {
   182  					case *watchEventUpdate:
   183  						info := &table.PeerInfo{
   184  							Address: msg.PeerAddress,
   185  							AS:      msg.PeerAS,
   186  							ID:      msg.PeerID,
   187  						}
   188  						if msg.Payload == nil {
   189  							var pathList []*table.Path
   190  							if msg.Init {
   191  								pathList = msg.PathList
   192  							} else {
   193  								for _, p := range msg.PathList {
   194  									if b.ribout.update(p) {
   195  										pathList = append(pathList, p)
   196  									}
   197  								}
   198  							}
   199  							for _, path := range pathList {
   200  								for _, u := range table.CreateUpdateMsgFromPaths([]*table.Path{path}) {
   201  									payload, _ := u.Serialize()
   202  									if err := write(bmpPeerRoute(bmp.BMP_PEER_TYPE_GLOBAL, msg.PostPolicy, 0, true, info, path.GetTimestamp().Unix(), payload)); err != nil {
   203  										return false
   204  									}
   205  								}
   206  							}
   207  						} else if err := write(bmpPeerRoute(bmp.BMP_PEER_TYPE_GLOBAL, msg.PostPolicy, 0, msg.FourBytesAs, info, msg.Timestamp.Unix(), msg.Payload)); err != nil {
   208  							return false
   209  						}
   210  					case *watchEventBestPath:
   211  						info := &table.PeerInfo{
   212  							Address: net.ParseIP("0.0.0.0").To4(),
   213  							AS:      b.s.bgpConfig.Global.Config.As,
   214  							ID:      net.ParseIP(b.s.bgpConfig.Global.Config.RouterId).To4(),
   215  						}
   216  						for _, p := range msg.PathList {
   217  							u := table.CreateUpdateMsgFromPaths([]*table.Path{p})[0]
   218  							if payload, err := u.Serialize(); err != nil {
   219  								return false
   220  							} else if err = write(bmpPeerRoute(bmp.BMP_PEER_TYPE_LOCAL_RIB, false, 0, true, info, p.GetTimestamp().Unix(), payload)); err != nil {
   221  								return false
   222  							}
   223  						}
   224  					case *watchEventPeer:
   225  						if msg.Type != PEER_EVENT_END_OF_INIT {
   226  							if msg.State == bgp.BGP_FSM_ESTABLISHED {
   227  								if err := write(bmpPeerUp(msg, bmp.BMP_PEER_TYPE_GLOBAL, false, 0)); err != nil {
   228  									return false
   229  								}
   230  							} else if msg.Type != PEER_EVENT_INIT && msg.OldState == bgp.BGP_FSM_ESTABLISHED {
   231  								if err := write(bmpPeerDown(msg, bmp.BMP_PEER_TYPE_GLOBAL, false, 0)); err != nil {
   232  									return false
   233  								}
   234  							}
   235  						}
   236  					case *watchEventMessage:
   237  						info := &table.PeerInfo{
   238  							Address: msg.PeerAddress,
   239  							AS:      msg.PeerAS,
   240  							ID:      msg.PeerID,
   241  						}
   242  						if err := write(bmpPeerRouteMirroring(bmp.BMP_PEER_TYPE_GLOBAL, 0, info, msg.Timestamp.Unix(), msg.Message)); err != nil {
   243  							return false
   244  						}
   245  					}
   246  				case <-tickerCh:
   247  					var err error
   248  					b.s.ListPeer(context.Background(), &api.ListPeerRequest{EnableAdvertised: true},
   249  						func(peer *api.Peer) {
   250  							if err == nil && peer.State.SessionState == api.PeerState_ESTABLISHED {
   251  								err = write(bmpPeerStats(bmp.BMP_PEER_TYPE_GLOBAL, 0, time.Now().Unix(), peer))
   252  							}
   253  						})
   254  					if err != nil {
   255  						return false
   256  					}
   257  				case <-b.dead:
   258  					term := bmp.NewBMPTermination([]bmp.BMPTermTLVInterface{
   259  						bmp.NewBMPTermTLV16(bmp.BMP_TERM_TLV_TYPE_REASON, bmp.BMP_TERM_REASON_PERMANENTLY_ADMIN),
   260  					})
   261  					if err := write(term); err != nil {
   262  						return false
   263  					}
   264  					conn.Close()
   265  					return true
   266  				}
   267  			}
   268  		}() {
   269  			return
   270  		}
   271  	}
   272  }
   273  
   274  type bmpClient struct {
   275  	s        *BgpServer
   276  	dead     chan struct{}
   277  	host     string
   278  	c        *oc.BmpServerConfig
   279  	ribout   ribout
   280  	uptime   int64
   281  	downtime int64
   282  }
   283  
   284  func bmpPeerUp(ev *watchEventPeer, t uint8, policy bool, pd uint64) *bmp.BMPMessage {
   285  	var flags uint8 = 0
   286  	if policy {
   287  		flags |= bmp.BMP_PEER_FLAG_POST_POLICY
   288  	}
   289  	ph := bmp.NewBMPPeerHeader(t, flags, pd, ev.PeerAddress.String(), ev.PeerAS, ev.PeerID.String(), float64(ev.Timestamp.Unix()))
   290  	return bmp.NewBMPPeerUpNotification(*ph, ev.LocalAddress.String(), ev.LocalPort, ev.PeerPort, ev.SentOpen, ev.RecvOpen)
   291  }
   292  
   293  func bmpPeerDown(ev *watchEventPeer, t uint8, policy bool, pd uint64) *bmp.BMPMessage {
   294  	var flags uint8 = 0
   295  	if policy {
   296  		flags |= bmp.BMP_PEER_FLAG_POST_POLICY
   297  	}
   298  	ph := bmp.NewBMPPeerHeader(t, flags, pd, ev.PeerAddress.String(), ev.PeerAS, ev.PeerID.String(), float64(ev.Timestamp.Unix()))
   299  
   300  	reasonCode := bmp.BMP_peerDownByUnknownReason
   301  	switch ev.StateReason.Type {
   302  	case fsmDying, fsmInvalidMsg, fsmNotificationSent, fsmHoldTimerExpired, fsmIdleTimerExpired, fsmRestartTimerExpired:
   303  		reasonCode = bmp.BMP_PEER_DOWN_REASON_LOCAL_BGP_NOTIFICATION
   304  	case fsmAdminDown:
   305  		reasonCode = bmp.BMP_PEER_DOWN_REASON_LOCAL_NO_NOTIFICATION
   306  	case fsmNotificationRecv, fsmGracefulRestart, fsmHardReset:
   307  		reasonCode = bmp.BMP_PEER_DOWN_REASON_REMOTE_BGP_NOTIFICATION
   308  	case fsmReadFailed, fsmWriteFailed:
   309  		reasonCode = bmp.BMP_PEER_DOWN_REASON_REMOTE_NO_NOTIFICATION
   310  	case fsmDeConfigured:
   311  		reasonCode = bmp.BMP_PEER_DOWN_REASON_PEER_DE_CONFIGURED
   312  	}
   313  	return bmp.NewBMPPeerDownNotification(*ph, uint8(reasonCode), ev.StateReason.BGPNotification, ev.StateReason.Data)
   314  }
   315  
   316  func bmpPeerRoute(t uint8, policy bool, pd uint64, fourBytesAs bool, peeri *table.PeerInfo, timestamp int64, payload []byte) *bmp.BMPMessage {
   317  	var flags uint8 = 0
   318  	if policy {
   319  		flags |= bmp.BMP_PEER_FLAG_POST_POLICY
   320  	}
   321  	if !fourBytesAs {
   322  		flags |= bmp.BMP_PEER_FLAG_TWO_AS
   323  	}
   324  	ph := bmp.NewBMPPeerHeader(t, flags, pd, peeri.Address.String(), peeri.AS, peeri.ID.String(), float64(timestamp))
   325  	m := bmp.NewBMPRouteMonitoring(*ph, nil)
   326  	body := m.Body.(*bmp.BMPRouteMonitoring)
   327  	body.BGPUpdatePayload = payload
   328  	return m
   329  }
   330  
   331  func bmpPeerStats(peerType uint8, peerDist uint64, timestamp int64, peer *api.Peer) *bmp.BMPMessage {
   332  	var peerFlags uint8 = 0
   333  	ph := bmp.NewBMPPeerHeader(peerType, peerFlags, peerDist, peer.State.NeighborAddress, peer.State.PeerAsn, peer.State.RouterId, float64(timestamp))
   334  	received := uint64(0)
   335  	accepted := uint64(0)
   336  	for _, a := range peer.AfiSafis {
   337  		received += a.State.Received
   338  		accepted += a.State.Accepted
   339  	}
   340  	return bmp.NewBMPStatisticsReport(
   341  		*ph,
   342  		[]bmp.BMPStatsTLVInterface{
   343  			bmp.NewBMPStatsTLV64(bmp.BMP_STAT_TYPE_ADJ_RIB_IN, received),
   344  			bmp.NewBMPStatsTLV64(bmp.BMP_STAT_TYPE_LOC_RIB, accepted),
   345  			bmp.NewBMPStatsTLV32(bmp.BMP_STAT_TYPE_WITHDRAW_UPDATE, uint32(peer.State.Messages.Received.WithdrawUpdate)),
   346  			bmp.NewBMPStatsTLV32(bmp.BMP_STAT_TYPE_WITHDRAW_PREFIX, uint32(peer.State.Messages.Received.WithdrawPrefix)),
   347  		},
   348  	)
   349  }
   350  
   351  func bmpPeerRouteMirroring(peerType uint8, peerDist uint64, peerInfo *table.PeerInfo, timestamp int64, msg *bgp.BGPMessage) *bmp.BMPMessage {
   352  	var peerFlags uint8 = 0
   353  	ph := bmp.NewBMPPeerHeader(peerType, peerFlags, peerDist, peerInfo.Address.String(), peerInfo.AS, peerInfo.ID.String(), float64(timestamp))
   354  	return bmp.NewBMPRouteMirroring(
   355  		*ph,
   356  		[]bmp.BMPRouteMirrTLVInterface{
   357  			// RFC7854: BGP Message TLV MUST occur last in the list of TLVs
   358  			bmp.NewBMPRouteMirrTLVBGPMsg(bmp.BMP_ROUTE_MIRRORING_TLV_TYPE_BGP_MSG, msg),
   359  		},
   360  	)
   361  }
   362  
   363  func (b *bmpClientManager) addServer(c *oc.BmpServerConfig) error {
   364  	host := net.JoinHostPort(c.Address, strconv.Itoa(int(c.Port)))
   365  	if _, y := b.clientMap[host]; y {
   366  		return fmt.Errorf("bmp client %s is already configured", host)
   367  	}
   368  	b.clientMap[host] = &bmpClient{
   369  		s:      b.s,
   370  		dead:   make(chan struct{}),
   371  		host:   host,
   372  		c:      c,
   373  		ribout: newribout(),
   374  	}
   375  	go b.clientMap[host].loop()
   376  	return nil
   377  }
   378  
   379  func (b *bmpClientManager) deleteServer(c *oc.BmpServerConfig) error {
   380  	host := net.JoinHostPort(c.Address, strconv.Itoa(int(c.Port)))
   381  	if c, y := b.clientMap[host]; !y {
   382  		return fmt.Errorf("bmp client %s isn't found", host)
   383  	} else {
   384  		c.Stop()
   385  		delete(b.clientMap, host)
   386  	}
   387  	return nil
   388  }
   389  
   390  type bmpClientManager struct {
   391  	s         *BgpServer
   392  	clientMap map[string]*bmpClient
   393  }
   394  
   395  func newBmpClientManager(s *BgpServer) *bmpClientManager {
   396  	return &bmpClientManager{
   397  		s:         s,
   398  		clientMap: make(map[string]*bmpClient),
   399  	}
   400  }