github.com/osrg/gobgp@v2.0.0+incompatible/pkg/server/bmp.go (about)

     1  // Copyright (C) 2015-2016 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  	"time"
    24  
    25  	api "github.com/osrg/gobgp/api"
    26  	"github.com/osrg/gobgp/internal/pkg/config"
    27  	"github.com/osrg/gobgp/internal/pkg/table"
    28  	"github.com/osrg/gobgp/pkg/packet/bgp"
    29  	"github.com/osrg/gobgp/pkg/packet/bmp"
    30  	log "github.com/sirupsen/logrus"
    31  )
    32  
    33  type ribout map[string][]*table.Path
    34  
    35  func newribout() ribout {
    36  	return make(map[string][]*table.Path)
    37  }
    38  
    39  // return true if we need to send the path to the BMP server
    40  func (r ribout) update(p *table.Path) bool {
    41  	key := p.GetNlri().String() // TODO expose (*Path).getPrefix()
    42  	l := r[key]
    43  	if p.IsWithdraw {
    44  		if len(l) == 0 {
    45  			return false
    46  		}
    47  		n := make([]*table.Path, 0, len(l))
    48  		for _, q := range l {
    49  			if p.GetSource() == q.GetSource() {
    50  				continue
    51  			}
    52  			n = append(n, q)
    53  		}
    54  		if len(n) == 0 {
    55  			delete(r, key)
    56  		} else {
    57  			r[key] = n
    58  		}
    59  		return true
    60  	}
    61  
    62  	if len(l) == 0 {
    63  		r[key] = []*table.Path{p}
    64  		return true
    65  	}
    66  
    67  	doAppend := true
    68  	for idx, q := range l {
    69  		if p.GetSource() == q.GetSource() {
    70  			// if we have sent the same path, don't send it again
    71  			if p.Equal(q) {
    72  				return false
    73  			}
    74  			l[idx] = p
    75  			doAppend = false
    76  		}
    77  	}
    78  	if doAppend {
    79  		r[key] = append(r[key], p)
    80  	}
    81  	return true
    82  }
    83  
    84  func (b *bmpClient) tryConnect() *net.TCPConn {
    85  	interval := 1
    86  	for {
    87  		log.WithFields(log.Fields{"Topic": "bmp"}).Debugf("Connecting BMP server:%s", b.host)
    88  		conn, err := net.Dial("tcp", b.host)
    89  		if err != nil {
    90  			select {
    91  			case <-b.dead:
    92  				return nil
    93  			default:
    94  			}
    95  			time.Sleep(time.Duration(interval) * time.Second)
    96  			if interval < 30 {
    97  				interval *= 2
    98  			}
    99  		} else {
   100  			log.WithFields(log.Fields{"Topic": "bmp"}).Infof("BMP server is connected:%s", b.host)
   101  			return conn.(*net.TCPConn)
   102  		}
   103  	}
   104  }
   105  
   106  func (b *bmpClient) Stop() {
   107  	close(b.dead)
   108  }
   109  
   110  func (b *bmpClient) loop() {
   111  	for {
   112  		conn := b.tryConnect()
   113  		if conn == nil {
   114  			break
   115  		}
   116  
   117  		if func() bool {
   118  			ops := []watchOption{watchPeerState(true)}
   119  			if b.c.RouteMonitoringPolicy == config.BMP_ROUTE_MONITORING_POLICY_TYPE_BOTH {
   120  				log.WithFields(
   121  					log.Fields{"Topic": "bmp"},
   122  				).Warn("both option for route-monitoring-policy is obsoleted")
   123  			}
   124  			if b.c.RouteMonitoringPolicy == config.BMP_ROUTE_MONITORING_POLICY_TYPE_PRE_POLICY || b.c.RouteMonitoringPolicy == config.BMP_ROUTE_MONITORING_POLICY_TYPE_ALL {
   125  				ops = append(ops, watchUpdate(true))
   126  			}
   127  			if b.c.RouteMonitoringPolicy == config.BMP_ROUTE_MONITORING_POLICY_TYPE_POST_POLICY || b.c.RouteMonitoringPolicy == config.BMP_ROUTE_MONITORING_POLICY_TYPE_ALL {
   128  				ops = append(ops, watchPostUpdate(true))
   129  			}
   130  			if b.c.RouteMonitoringPolicy == config.BMP_ROUTE_MONITORING_POLICY_TYPE_LOCAL_RIB || b.c.RouteMonitoringPolicy == config.BMP_ROUTE_MONITORING_POLICY_TYPE_ALL {
   131  				ops = append(ops, watchBestPath(true))
   132  			}
   133  			if b.c.RouteMirroringEnabled {
   134  				ops = append(ops, watchMessage(false))
   135  			}
   136  			w := b.s.watch(ops...)
   137  			defer w.Stop()
   138  
   139  			var tickerCh <-chan time.Time
   140  			if b.c.StatisticsTimeout == 0 {
   141  				log.WithFields(log.Fields{"Topic": "bmp"}).Debug("statistics reports disabled")
   142  			} else {
   143  				t := time.NewTicker(time.Duration(b.c.StatisticsTimeout) * time.Second)
   144  				defer t.Stop()
   145  				tickerCh = t.C
   146  			}
   147  
   148  			write := func(msg *bmp.BMPMessage) error {
   149  				buf, _ := msg.Serialize()
   150  				_, err := conn.Write(buf)
   151  				if err != nil {
   152  					log.Warnf("failed to write to bmp server %s", b.host)
   153  				}
   154  				return err
   155  			}
   156  
   157  			if err := write(bmp.NewBMPInitiation([]bmp.BMPInfoTLVInterface{})); err != nil {
   158  				return false
   159  			}
   160  
   161  			for {
   162  				select {
   163  				case ev := <-w.Event():
   164  					switch msg := ev.(type) {
   165  					case *watchEventUpdate:
   166  						info := &table.PeerInfo{
   167  							Address: msg.PeerAddress,
   168  							AS:      msg.PeerAS,
   169  							ID:      msg.PeerID,
   170  						}
   171  						if msg.Payload == nil {
   172  							var pathList []*table.Path
   173  							if msg.Init {
   174  								pathList = msg.PathList
   175  							} else {
   176  								for _, p := range msg.PathList {
   177  									if b.ribout.update(p) {
   178  										pathList = append(pathList, p)
   179  									}
   180  								}
   181  							}
   182  							for _, path := range pathList {
   183  								for _, u := range table.CreateUpdateMsgFromPaths([]*table.Path{path}) {
   184  									payload, _ := u.Serialize()
   185  									if err := write(bmpPeerRoute(bmp.BMP_PEER_TYPE_GLOBAL, msg.PostPolicy, 0, true, info, path.GetTimestamp().Unix(), payload)); err != nil {
   186  										return false
   187  									}
   188  								}
   189  							}
   190  						} else {
   191  							if err := write(bmpPeerRoute(bmp.BMP_PEER_TYPE_GLOBAL, msg.PostPolicy, 0, msg.FourBytesAs, info, msg.Timestamp.Unix(), msg.Payload)); err != nil {
   192  								return false
   193  							}
   194  						}
   195  					case *watchEventBestPath:
   196  						info := &table.PeerInfo{
   197  							Address: net.ParseIP("0.0.0.0").To4(),
   198  							AS:      b.s.bgpConfig.Global.Config.As,
   199  							ID:      net.ParseIP(b.s.bgpConfig.Global.Config.RouterId).To4(),
   200  						}
   201  						for _, p := range msg.PathList {
   202  							u := table.CreateUpdateMsgFromPaths([]*table.Path{p})[0]
   203  							if payload, err := u.Serialize(); err != nil {
   204  								return false
   205  							} else if err = write(bmpPeerRoute(bmp.BMP_PEER_TYPE_LOCAL_RIB, false, 0, true, info, p.GetTimestamp().Unix(), payload)); err != nil {
   206  								return false
   207  							}
   208  						}
   209  					case *watchEventPeerState:
   210  						if msg.State == bgp.BGP_FSM_ESTABLISHED {
   211  							if err := write(bmpPeerUp(msg, bmp.BMP_PEER_TYPE_GLOBAL, false, 0)); err != nil {
   212  								return false
   213  							}
   214  						} else {
   215  							if err := write(bmpPeerDown(msg, bmp.BMP_PEER_TYPE_GLOBAL, false, 0)); err != nil {
   216  								return false
   217  							}
   218  						}
   219  					case *watchEventMessage:
   220  						info := &table.PeerInfo{
   221  							Address: msg.PeerAddress,
   222  							AS:      msg.PeerAS,
   223  							ID:      msg.PeerID,
   224  						}
   225  						if err := write(bmpPeerRouteMirroring(bmp.BMP_PEER_TYPE_GLOBAL, 0, info, msg.Timestamp.Unix(), msg.Message)); err != nil {
   226  							return false
   227  						}
   228  					}
   229  				case <-tickerCh:
   230  					var err error
   231  					b.s.ListPeer(context.Background(), &api.ListPeerRequest{EnableAdvertised: true},
   232  						func(peer *api.Peer) {
   233  							if err == nil && peer.State.SessionState == api.PeerState_ESTABLISHED {
   234  								err = write(bmpPeerStats(bmp.BMP_PEER_TYPE_GLOBAL, 0, time.Now().Unix(), peer))
   235  							}
   236  						})
   237  					if err != nil {
   238  						return false
   239  					}
   240  				case <-b.dead:
   241  					term := bmp.NewBMPTermination([]bmp.BMPTermTLVInterface{
   242  						bmp.NewBMPTermTLV16(bmp.BMP_TERM_TLV_TYPE_REASON, bmp.BMP_TERM_REASON_PERMANENTLY_ADMIN),
   243  					})
   244  					if err := write(term); err != nil {
   245  						return false
   246  					}
   247  					conn.Close()
   248  					return true
   249  				}
   250  			}
   251  		}() {
   252  			return
   253  		}
   254  	}
   255  }
   256  
   257  type bmpClient struct {
   258  	s      *BgpServer
   259  	dead   chan struct{}
   260  	host   string
   261  	c      *config.BmpServerConfig
   262  	ribout ribout
   263  }
   264  
   265  func bmpPeerUp(ev *watchEventPeerState, t uint8, policy bool, pd uint64) *bmp.BMPMessage {
   266  	var flags uint8 = 0
   267  	if policy {
   268  		flags |= bmp.BMP_PEER_FLAG_POST_POLICY
   269  	}
   270  	ph := bmp.NewBMPPeerHeader(t, flags, pd, ev.PeerAddress.String(), ev.PeerAS, ev.PeerID.String(), float64(ev.Timestamp.Unix()))
   271  	return bmp.NewBMPPeerUpNotification(*ph, ev.LocalAddress.String(), ev.LocalPort, ev.PeerPort, ev.SentOpen, ev.RecvOpen)
   272  }
   273  
   274  func bmpPeerDown(ev *watchEventPeerState, t uint8, policy bool, pd uint64) *bmp.BMPMessage {
   275  	var flags uint8 = 0
   276  	if policy {
   277  		flags |= bmp.BMP_PEER_FLAG_POST_POLICY
   278  	}
   279  	ph := bmp.NewBMPPeerHeader(t, flags, pd, ev.PeerAddress.String(), ev.PeerAS, ev.PeerID.String(), float64(ev.Timestamp.Unix()))
   280  	return bmp.NewBMPPeerDownNotification(*ph, uint8(ev.StateReason.peerDownReason), ev.StateReason.BGPNotification, ev.StateReason.Data)
   281  }
   282  
   283  func bmpPeerRoute(t uint8, policy bool, pd uint64, fourBytesAs bool, peeri *table.PeerInfo, timestamp int64, payload []byte) *bmp.BMPMessage {
   284  	var flags uint8 = 0
   285  	if policy {
   286  		flags |= bmp.BMP_PEER_FLAG_POST_POLICY
   287  	}
   288  	if !fourBytesAs {
   289  		flags |= bmp.BMP_PEER_FLAG_TWO_AS
   290  	}
   291  	ph := bmp.NewBMPPeerHeader(t, flags, pd, peeri.Address.String(), peeri.AS, peeri.ID.String(), float64(timestamp))
   292  	m := bmp.NewBMPRouteMonitoring(*ph, nil)
   293  	body := m.Body.(*bmp.BMPRouteMonitoring)
   294  	body.BGPUpdatePayload = payload
   295  	return m
   296  }
   297  
   298  func bmpPeerStats(peerType uint8, peerDist uint64, timestamp int64, peer *api.Peer) *bmp.BMPMessage {
   299  	var peerFlags uint8 = 0
   300  	ph := bmp.NewBMPPeerHeader(peerType, peerFlags, peerDist, peer.State.NeighborAddress, peer.State.PeerAs, peer.State.RouterId, float64(timestamp))
   301  	received := uint64(0)
   302  	accepted := uint64(0)
   303  	for _, a := range peer.AfiSafis {
   304  		received += a.State.Received
   305  		accepted += a.State.Accepted
   306  	}
   307  	return bmp.NewBMPStatisticsReport(
   308  		*ph,
   309  		[]bmp.BMPStatsTLVInterface{
   310  			bmp.NewBMPStatsTLV64(bmp.BMP_STAT_TYPE_ADJ_RIB_IN, received),
   311  			bmp.NewBMPStatsTLV64(bmp.BMP_STAT_TYPE_LOC_RIB, accepted),
   312  			bmp.NewBMPStatsTLV32(bmp.BMP_STAT_TYPE_WITHDRAW_UPDATE, uint32(peer.State.Messages.Received.WithdrawUpdate)),
   313  			bmp.NewBMPStatsTLV32(bmp.BMP_STAT_TYPE_WITHDRAW_PREFIX, uint32(peer.State.Messages.Received.WithdrawPrefix)),
   314  		},
   315  	)
   316  }
   317  
   318  func bmpPeerRouteMirroring(peerType uint8, peerDist uint64, peerInfo *table.PeerInfo, timestamp int64, msg *bgp.BGPMessage) *bmp.BMPMessage {
   319  	var peerFlags uint8 = 0
   320  	ph := bmp.NewBMPPeerHeader(peerType, peerFlags, peerDist, peerInfo.Address.String(), peerInfo.AS, peerInfo.ID.String(), float64(timestamp))
   321  	return bmp.NewBMPRouteMirroring(
   322  		*ph,
   323  		[]bmp.BMPRouteMirrTLVInterface{
   324  			// RFC7854: BGP Message TLV MUST occur last in the list of TLVs
   325  			bmp.NewBMPRouteMirrTLVBGPMsg(bmp.BMP_ROUTE_MIRRORING_TLV_TYPE_BGP_MSG, msg),
   326  		},
   327  	)
   328  }
   329  
   330  func (b *bmpClientManager) addServer(c *config.BmpServerConfig) error {
   331  	host := net.JoinHostPort(c.Address, strconv.Itoa(int(c.Port)))
   332  	if _, y := b.clientMap[host]; y {
   333  		return fmt.Errorf("bmp client %s is already configured", host)
   334  	}
   335  	b.clientMap[host] = &bmpClient{
   336  		s:      b.s,
   337  		dead:   make(chan struct{}),
   338  		host:   host,
   339  		c:      c,
   340  		ribout: newribout(),
   341  	}
   342  	go b.clientMap[host].loop()
   343  	return nil
   344  }
   345  
   346  func (b *bmpClientManager) deleteServer(c *config.BmpServerConfig) error {
   347  	host := net.JoinHostPort(c.Address, strconv.Itoa(int(c.Port)))
   348  	if c, y := b.clientMap[host]; !y {
   349  		return fmt.Errorf("bmp client %s isn't found", host)
   350  	} else {
   351  		c.Stop()
   352  		delete(b.clientMap, host)
   353  	}
   354  	return nil
   355  }
   356  
   357  type bmpClientManager struct {
   358  	s         *BgpServer
   359  	clientMap map[string]*bmpClient
   360  }
   361  
   362  func newBmpClientManager(s *BgpServer) *bmpClientManager {
   363  	return &bmpClientManager{
   364  		s:         s,
   365  		clientMap: make(map[string]*bmpClient),
   366  	}
   367  }