summaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
authorFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2018-07-07 13:48:38 +0900
committerFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2018-07-07 20:44:25 +0900
commitc4775c42510d1f1ddd55036dc19e982712fa6a0b (patch)
tree6ec8b61d4338c809e239e3003a2d32d480898e22 /server
parentb3079759aa13172fcb548a83da9a9653d8d5fed4 (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 'server')
-rw-r--r--server/bmp.go358
-rw-r--r--server/collector.go221
-rw-r--r--server/fsm.go1752
-rw-r--r--server/fsm_test.go344
-rw-r--r--server/grpc_server.go3016
-rw-r--r--server/mrt.go409
-rw-r--r--server/peer.go522
-rw-r--r--server/rpki.go712
-rw-r--r--server/rpki_test.go258
-rw-r--r--server/server.go3048
-rw-r--r--server/server_test.go707
-rw-r--r--server/sockopt.go90
-rw-r--r--server/sockopt_bsd.go89
-rw-r--r--server/sockopt_darwin.go64
-rw-r--r--server/sockopt_linux.go282
-rw-r--r--server/sockopt_linux_test.go106
-rw-r--r--server/sockopt_openbsd.go469
-rw-r--r--server/sockopt_stub.go38
-rw-r--r--server/util.go117
-rw-r--r--server/zclient.go450
-rw-r--r--server/zclient_test.go113
21 files changed, 0 insertions, 13165 deletions
diff --git a/server/bmp.go b/server/bmp.go
deleted file mode 100644
index 91507cd3..00000000
--- a/server/bmp.go
+++ /dev/null
@@ -1,358 +0,0 @@
-// 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/config"
- "github.com/osrg/gobgp/packet/bgp"
- "github.com/osrg/gobgp/packet/bmp"
- "github.com/osrg/gobgp/table"
- 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),
- }
-}
diff --git a/server/collector.go b/server/collector.go
deleted file mode 100644
index 50fb72e9..00000000
--- a/server/collector.go
+++ /dev/null
@@ -1,221 +0,0 @@
-// Copyright (C) 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"
- "github.com/influxdata/influxdb/client/v2"
- "github.com/osrg/gobgp/packet/bgp"
- "github.com/osrg/gobgp/table"
- log "github.com/sirupsen/logrus"
- "time"
-)
-
-type Collector struct {
- s *BgpServer
- url string
- dbName string
- interval uint64
- client client.Client
-}
-
-const (
- MEATUREMENT_UPDATE = "update"
- MEATUREMENT_PEER = "peer"
- MEATUREMENT_TABLE = "table"
-)
-
-func (c *Collector) writePoints(points []*client.Point) error {
- bp, _ := client.NewBatchPoints(client.BatchPointsConfig{
- Database: c.dbName,
- Precision: "ms",
- })
- bp.AddPoints(points)
- return c.client.Write(bp)
-}
-
-func (c *Collector) writePeer(msg *WatchEventPeerState) error {
- var state string
- switch msg.State {
- case bgp.BGP_FSM_ESTABLISHED:
- state = "Established"
- case bgp.BGP_FSM_IDLE:
- state = "Idle"
- default:
- return fmt.Errorf("unexpected fsm state %v", msg.State)
- }
-
- tags := map[string]string{
- "PeerAddress": msg.PeerAddress.String(),
- "PeerAS": fmt.Sprintf("%v", msg.PeerAS),
- "State": state,
- }
-
- fields := map[string]interface{}{
- "PeerID": msg.PeerID.String(),
- }
-
- pt, err := client.NewPoint(MEATUREMENT_PEER, tags, fields, msg.Timestamp)
- if err != nil {
- return err
- }
- return c.writePoints([]*client.Point{pt})
-}
-
-func path2data(path *table.Path) (map[string]interface{}, map[string]string) {
- fields := map[string]interface{}{
- "RouterID": path.GetSource().ID,
- }
- if asPath := path.GetAsPath(); asPath != nil {
- fields["ASPath"] = asPath.String()
- }
- if origin, err := path.GetOrigin(); err == nil {
- typ := "-"
- switch origin {
- case bgp.BGP_ORIGIN_ATTR_TYPE_IGP:
- typ = "i"
- case bgp.BGP_ORIGIN_ATTR_TYPE_EGP:
- typ = "e"
- case bgp.BGP_ORIGIN_ATTR_TYPE_INCOMPLETE:
- typ = "?"
- }
- fields["Origin"] = typ
- }
- if med, err := path.GetMed(); err == nil {
- fields["Med"] = med
- }
-
- tags := map[string]string{
- "PeerAddress": path.GetSource().Address.String(),
- "PeerAS": fmt.Sprintf("%v", path.GetSource().AS),
- "Timestamp": path.GetTimestamp().String(),
- }
- if nexthop := path.GetNexthop(); len(nexthop) > 0 {
- fields["NextHop"] = nexthop.String()
- }
- if originAS := path.GetSourceAs(); originAS != 0 {
- fields["OriginAS"] = fmt.Sprintf("%v", originAS)
- }
-
- if err := bgp.FlatUpdate(tags, path.GetNlri().Flat()); err != nil {
- log.WithFields(log.Fields{"Type": "collector", "Error": err}).Error("NLRI FlatUpdate failed")
- }
- for _, p := range path.GetPathAttrs() {
- if err := bgp.FlatUpdate(tags, p.Flat()); err != nil {
- log.WithFields(log.Fields{"Type": "collector", "Error": err}).Error("PathAttr FlatUpdate failed")
- }
- }
- return fields, tags
-}
-
-func (c *Collector) writeUpdate(msg *WatchEventUpdate) error {
- if len(msg.PathList) == 0 {
- // EOR
- return nil
- }
- now := time.Now()
- points := make([]*client.Point, 0, len(msg.PathList))
- for _, path := range msg.PathList {
- fields, tags := path2data(path)
- tags["Withdraw"] = fmt.Sprintf("%v", path.IsWithdraw)
- pt, err := client.NewPoint(MEATUREMENT_UPDATE, tags, fields, now)
- if err != nil {
- return fmt.Errorf("failed to write update, %v", err)
- }
- points = append(points, pt)
- }
- return c.writePoints(points)
-}
-
-func (c *Collector) writeTable(msg *WatchEventAdjIn) error {
- now := time.Now()
- points := make([]*client.Point, 0, len(msg.PathList))
- for _, path := range msg.PathList {
- fields, tags := path2data(path)
- pt, err := client.NewPoint(MEATUREMENT_TABLE, tags, fields, now)
- if err != nil {
- return fmt.Errorf("failed to write table, %v", err)
- }
- points = append(points, pt)
- }
- return c.writePoints(points)
-}
-
-func (c *Collector) loop() {
- w := c.s.Watch(WatchPeerState(true), WatchUpdate(false))
- defer w.Stop()
-
- ticker := func() *time.Ticker {
- if c.interval == 0 {
- return &time.Ticker{}
- }
- return time.NewTicker(time.Second * time.Duration(c.interval))
- }()
-
- for {
- select {
- case <-ticker.C:
- w.Generate(WATCH_EVENT_TYPE_PRE_UPDATE)
- case ev := <-w.Event():
- switch msg := ev.(type) {
- case *WatchEventUpdate:
- if err := c.writeUpdate(msg); err != nil {
- log.WithFields(log.Fields{"Type": "collector", "Error": err}).Error("Failed to write update event message")
- }
- case *WatchEventPeerState:
- if err := c.writePeer(msg); err != nil {
- log.WithFields(log.Fields{"Type": "collector", "Error": err}).Error("Failed to write state changed event message")
- }
- case *WatchEventAdjIn:
- if err := c.writeTable(msg); err != nil {
- log.WithFields(log.Fields{"Type": "collector", "Error": err}).Error("Failed to write Adj-In event message")
- }
- }
- }
- }
-}
-
-func NewCollector(s *BgpServer, url, dbName string, interval uint64) (*Collector, error) {
- c, err := client.NewHTTPClient(client.HTTPConfig{
- Addr: url,
- })
- if err != nil {
- return nil, err
- }
-
- _, _, err = c.Ping(0)
- if err != nil {
- log.Error("can not connect to InfluxDB")
- log.WithFields(log.Fields{"Type": "collector", "Error": err}).Error("Failed to connect to InfluxDB")
- return nil, err
- }
-
- q := client.NewQuery("CREATE DATABASE "+dbName, "", "")
- if response, err := c.Query(q); err != nil || response.Error() != nil {
- log.WithFields(log.Fields{"Type": "collector", "Error": err}).Errorf("Failed to create database:%s", dbName)
- return nil, err
- }
-
- collector := &Collector{
- s: s,
- url: url,
- dbName: dbName,
- interval: interval,
- client: c,
- }
- go collector.loop()
- return collector, nil
-}
diff --git a/server/fsm.go b/server/fsm.go
deleted file mode 100644
index d65107df..00000000
--- a/server/fsm.go
+++ /dev/null
@@ -1,1752 +0,0 @@
-// Copyright (C) 2014 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"
- "io"
- "math/rand"
- "net"
- "strconv"
- "time"
-
- "github.com/osrg/gobgp/config"
- "github.com/osrg/gobgp/packet/bgp"
- "github.com/osrg/gobgp/packet/bmp"
- "github.com/osrg/gobgp/table"
-
- "github.com/eapache/channels"
- log "github.com/sirupsen/logrus"
- "gopkg.in/tomb.v2"
-)
-
-type PeerDownReason int
-
-const (
- PEER_DOWN_REASON_UNKNOWN PeerDownReason = iota
- PEER_DOWN_BY_LOCAL
- PEER_DOWN_BY_LOCAL_WITHOUT_NOTIFICATION
- PEER_DOWN_BY_REMOTE
- PEER_DOWN_BY_REMOTE_WITHOUT_NOTIFICATION
- PEER_DOWN_BY_BMP_CONFIGURATION
-)
-
-type FsmStateReasonType uint8
-
-const (
- FSM_DYING FsmStateReasonType = iota
- FSM_ADMIN_DOWN
- FSM_READ_FAILED
- FSM_WRITE_FAILED
- FSM_NOTIFICATION_SENT
- FSM_NOTIFICATION_RECV
- FSM_HOLD_TIMER_EXPIRED
- FSM_IDLE_HOLD_TIMER_EXPIRED
- FSM_RESTART_TIMER_EXPIRED
- FSM_GRACEFUL_RESTART
- FSM_INVALID_MSG
- FSM_NEW_CONNECTION
- FSM_OPEN_MSG_RECEIVED
- FSM_OPEN_MSG_NEGOTIATED
- FSM_HARD_RESET
-)
-
-type FsmStateReason struct {
- Type FsmStateReasonType
- PeerDownReason PeerDownReason
- BGPNotification *bgp.BGPMessage
- Data []byte
-}
-
-func NewFsmStateReason(typ FsmStateReasonType, notif *bgp.BGPMessage, data []byte) *FsmStateReason {
- var reasonCode PeerDownReason
- switch typ {
- case FSM_DYING, FSM_INVALID_MSG, FSM_NOTIFICATION_SENT, FSM_HOLD_TIMER_EXPIRED, FSM_IDLE_HOLD_TIMER_EXPIRED, FSM_RESTART_TIMER_EXPIRED:
- reasonCode = PEER_DOWN_BY_LOCAL
- case FSM_ADMIN_DOWN:
- reasonCode = PEER_DOWN_BY_LOCAL_WITHOUT_NOTIFICATION
- case FSM_NOTIFICATION_RECV, FSM_GRACEFUL_RESTART, FSM_HARD_RESET:
- reasonCode = PEER_DOWN_BY_REMOTE
- case FSM_READ_FAILED, FSM_WRITE_FAILED:
- reasonCode = PEER_DOWN_BY_REMOTE_WITHOUT_NOTIFICATION
- }
- return &FsmStateReason{
- Type: typ,
- PeerDownReason: reasonCode,
- BGPNotification: notif,
- Data: data,
- }
-}
-
-func (r FsmStateReason) String() string {
- switch r.Type {
- case FSM_DYING:
- return "dying"
- case FSM_ADMIN_DOWN:
- return "admin-down"
- case FSM_READ_FAILED:
- return "read-failed"
- case FSM_WRITE_FAILED:
- return "write-failed"
- case FSM_NOTIFICATION_SENT:
- body := r.BGPNotification.Body.(*bgp.BGPNotification)
- return fmt.Sprintf("notification-sent %s", bgp.NewNotificationErrorCode(body.ErrorCode, body.ErrorSubcode).String())
- case FSM_NOTIFICATION_RECV:
- body := r.BGPNotification.Body.(*bgp.BGPNotification)
- return fmt.Sprintf("notification-received %s", bgp.NewNotificationErrorCode(body.ErrorCode, body.ErrorSubcode).String())
- case FSM_HOLD_TIMER_EXPIRED:
- return "hold-timer-expired"
- case FSM_IDLE_HOLD_TIMER_EXPIRED:
- return "idle-hold-timer-expired"
- case FSM_RESTART_TIMER_EXPIRED:
- return "restart-timer-expired"
- case FSM_GRACEFUL_RESTART:
- return "graceful-restart"
- case FSM_INVALID_MSG:
- return "invalid-msg"
- case FSM_NEW_CONNECTION:
- return "new-connection"
- case FSM_OPEN_MSG_RECEIVED:
- return "open-msg-received"
- case FSM_OPEN_MSG_NEGOTIATED:
- return "open-msg-negotiated"
- case FSM_HARD_RESET:
- return "hard-reset"
- default:
- return "unknown"
- }
-}
-
-type FsmMsgType int
-
-const (
- _ FsmMsgType = iota
- FSM_MSG_STATE_CHANGE
- FSM_MSG_BGP_MESSAGE
- FSM_MSG_ROUTE_REFRESH
-)
-
-type FsmMsg struct {
- MsgType FsmMsgType
- MsgSrc string
- MsgData interface{}
- StateReason *FsmStateReason
- PathList []*table.Path
- timestamp time.Time
- payload []byte
- Version uint
-}
-
-type FsmOutgoingMsg struct {
- Paths []*table.Path
- Notification *bgp.BGPMessage
- StayIdle bool
-}
-
-const (
- HOLDTIME_OPENSENT = 240
- HOLDTIME_IDLE = 5
-)
-
-type AdminState int
-
-const (
- ADMIN_STATE_UP AdminState = iota
- ADMIN_STATE_DOWN
- ADMIN_STATE_PFX_CT
-)
-
-func (s AdminState) String() string {
- switch s {
- case ADMIN_STATE_UP:
- return "ADMIN_STATE_UP"
- case ADMIN_STATE_DOWN:
- return "ADMIN_STATE_DOWN"
- case ADMIN_STATE_PFX_CT:
- return "ADMIN_STATE_PFX_CT"
- default:
- return "Unknown"
- }
-}
-
-type AdminStateOperation struct {
- State AdminState
- Communication []byte
-}
-
-var fsmVersion uint
-
-type FSM struct {
- t tomb.Tomb
- gConf *config.Global
- pConf *config.Neighbor
- state bgp.FSMState
- reason *FsmStateReason
- conn net.Conn
- connCh chan net.Conn
- idleHoldTime float64
- opensentHoldTime float64
- adminState AdminState
- adminStateCh chan AdminStateOperation
- getActiveCh chan struct{}
- h *FSMHandler
- rfMap map[bgp.RouteFamily]bgp.BGPAddPathMode
- capMap map[bgp.BGPCapabilityCode][]bgp.ParameterCapabilityInterface
- recvOpen *bgp.BGPMessage
- peerInfo *table.PeerInfo
- policy *table.RoutingPolicy
- gracefulRestartTimer *time.Timer
- twoByteAsTrans bool
- version uint
- marshallingOptions *bgp.MarshallingOption
-}
-
-func (fsm *FSM) bgpMessageStateUpdate(MessageType uint8, isIn bool) {
- state := &fsm.pConf.State.Messages
- timer := &fsm.pConf.Timers
- if isIn {
- state.Received.Total++
- } else {
- state.Sent.Total++
- }
- switch MessageType {
- case bgp.BGP_MSG_OPEN:
- if isIn {
- state.Received.Open++
- } else {
- state.Sent.Open++
- }
- case bgp.BGP_MSG_UPDATE:
- if isIn {
- state.Received.Update++
- timer.State.UpdateRecvTime = time.Now().Unix()
- } else {
- state.Sent.Update++
- }
- case bgp.BGP_MSG_NOTIFICATION:
- if isIn {
- state.Received.Notification++
- } else {
- state.Sent.Notification++
- }
- case bgp.BGP_MSG_KEEPALIVE:
- if isIn {
- state.Received.Keepalive++
- } else {
- state.Sent.Keepalive++
- }
- case bgp.BGP_MSG_ROUTE_REFRESH:
- if isIn {
- state.Received.Refresh++
- } else {
- state.Sent.Refresh++
- }
- default:
- if isIn {
- state.Received.Discarded++
- } else {
- state.Sent.Discarded++
- }
- }
-}
-
-func (fsm *FSM) bmpStatsUpdate(statType uint16, increment int) {
- stats := &fsm.pConf.State.Messages.Received
- switch statType {
- // TODO
- // Support other stat types.
- case bmp.BMP_STAT_TYPE_WITHDRAW_UPDATE:
- stats.WithdrawUpdate += uint32(increment)
- case bmp.BMP_STAT_TYPE_WITHDRAW_PREFIX:
- stats.WithdrawPrefix += uint32(increment)
- }
-}
-
-func NewFSM(gConf *config.Global, pConf *config.Neighbor, policy *table.RoutingPolicy) *FSM {
- adminState := ADMIN_STATE_UP
- if pConf.Config.AdminDown {
- adminState = ADMIN_STATE_DOWN
- }
- pConf.State.SessionState = config.IntToSessionStateMap[int(bgp.BGP_FSM_IDLE)]
- pConf.Timers.State.Downtime = time.Now().Unix()
- fsmVersion++
- fsm := &FSM{
- gConf: gConf,
- pConf: pConf,
- state: bgp.BGP_FSM_IDLE,
- connCh: make(chan net.Conn, 1),
- opensentHoldTime: float64(HOLDTIME_OPENSENT),
- adminState: adminState,
- adminStateCh: make(chan AdminStateOperation, 1),
- getActiveCh: make(chan struct{}),
- rfMap: make(map[bgp.RouteFamily]bgp.BGPAddPathMode),
- capMap: make(map[bgp.BGPCapabilityCode][]bgp.ParameterCapabilityInterface),
- peerInfo: table.NewPeerInfo(gConf, pConf),
- policy: policy,
- gracefulRestartTimer: time.NewTimer(time.Hour),
- version: fsmVersion,
- }
- fsm.gracefulRestartTimer.Stop()
- fsm.t.Go(fsm.connectLoop)
- return fsm
-}
-
-func (fsm *FSM) StateChange(nextState bgp.FSMState) {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "old": fsm.state.String(),
- "new": nextState.String(),
- "reason": fsm.reason,
- }).Debug("state changed")
- fsm.state = nextState
- switch nextState {
- case bgp.BGP_FSM_ESTABLISHED:
- fsm.pConf.Timers.State.Uptime = time.Now().Unix()
- fsm.pConf.State.EstablishedCount++
- // reset the state set by the previous session
- fsm.twoByteAsTrans = false
- if _, y := fsm.capMap[bgp.BGP_CAP_FOUR_OCTET_AS_NUMBER]; !y {
- fsm.twoByteAsTrans = true
- break
- }
- y := func() bool {
- for _, c := range capabilitiesFromConfig(fsm.pConf) {
- switch c.(type) {
- case *bgp.CapFourOctetASNumber:
- return true
- }
- }
- return false
- }()
- if !y {
- fsm.twoByteAsTrans = true
- }
- case bgp.BGP_FSM_ACTIVE:
- if !fsm.pConf.Transport.Config.PassiveMode {
- fsm.getActiveCh <- struct{}{}
- }
- fallthrough
- default:
- fsm.pConf.Timers.State.Downtime = time.Now().Unix()
- }
-}
-
-func hostport(addr net.Addr) (string, uint16) {
- if addr != nil {
- host, port, err := net.SplitHostPort(addr.String())
- if err != nil {
- return "", 0
- }
- p, _ := strconv.ParseUint(port, 10, 16)
- return host, uint16(p)
- }
- return "", 0
-}
-
-func (fsm *FSM) RemoteHostPort() (string, uint16) {
- return hostport(fsm.conn.RemoteAddr())
-
-}
-
-func (fsm *FSM) LocalHostPort() (string, uint16) {
- return hostport(fsm.conn.LocalAddr())
-}
-
-func (fsm *FSM) sendNotificationFromErrorMsg(e *bgp.MessageError) (*bgp.BGPMessage, error) {
- if fsm.h != nil && fsm.h.conn != nil {
- m := bgp.NewBGPNotificationMessage(e.TypeCode, e.SubTypeCode, e.Data)
- b, _ := m.Serialize()
- _, err := fsm.h.conn.Write(b)
- if err == nil {
- fsm.bgpMessageStateUpdate(m.Header.Type, false)
- fsm.h.sentNotification = m
- }
- fsm.h.conn.Close()
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "Data": e,
- }).Warn("sent notification")
- return m, nil
- }
- return nil, fmt.Errorf("can't send notification to %s since TCP connection is not established", fsm.pConf.State.NeighborAddress)
-}
-
-func (fsm *FSM) sendNotification(code, subType uint8, data []byte, msg string) (*bgp.BGPMessage, error) {
- e := bgp.NewMessageError(code, subType, data, msg)
- return fsm.sendNotificationFromErrorMsg(e.(*bgp.MessageError))
-}
-
-func (fsm *FSM) connectLoop() error {
- tick := int(fsm.pConf.Timers.Config.ConnectRetry)
- if tick < MIN_CONNECT_RETRY {
- tick = MIN_CONNECT_RETRY
- }
-
- r := rand.New(rand.NewSource(time.Now().UnixNano()))
-
- timer := time.NewTimer(time.Duration(tick) * time.Second)
- timer.Stop()
-
- connect := func() {
- addr := fsm.pConf.State.NeighborAddress
- port := int(bgp.BGP_PORT)
- if fsm.pConf.Transport.Config.RemotePort != 0 {
- port = int(fsm.pConf.Transport.Config.RemotePort)
- }
- laddr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort(fsm.pConf.Transport.Config.LocalAddress, "0"))
- if err != nil {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- }).Warn("failed to resolve local address: %s", err)
- return
- }
- var conn net.Conn
- d := TCPDialer{
- Dialer: net.Dialer{
- LocalAddr: laddr,
- Timeout: time.Duration(MIN_CONNECT_RETRY-1) * time.Second,
- },
- AuthPassword: fsm.pConf.Config.AuthPassword,
- }
- if fsm.pConf.TtlSecurity.Config.Enabled {
- d.Ttl = 255
- d.TtlMin = fsm.pConf.TtlSecurity.Config.TtlMin
- } else if fsm.pConf.Config.PeerAs != 0 && fsm.pConf.Config.PeerType == config.PEER_TYPE_EXTERNAL {
- d.Ttl = 1
- if fsm.pConf.EbgpMultihop.Config.Enabled {
- d.Ttl = fsm.pConf.EbgpMultihop.Config.MultihopTtl
- }
- }
- conn, err = d.DialTCP(addr, port)
- if err == nil {
- select {
- case fsm.connCh <- conn:
- return
- default:
- conn.Close()
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- }).Warn("active conn is closed to avoid being blocked")
- }
- } else {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- }).Debugf("failed to connect: %s", err)
- }
-
- if fsm.state == bgp.BGP_FSM_ACTIVE && !fsm.pConf.GracefulRestart.State.PeerRestarting {
- timer.Reset(time.Duration(tick) * time.Second)
- }
- }
-
- for {
- select {
- case <-fsm.t.Dying():
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- }).Debug("stop connect loop")
- return nil
- case <-timer.C:
- if fsm.state == bgp.BGP_FSM_ACTIVE && !fsm.pConf.GracefulRestart.State.PeerRestarting {
- go connect()
- }
- case <-fsm.getActiveCh:
- timer.Reset(time.Duration(r.Intn(MIN_CONNECT_RETRY)+MIN_CONNECT_RETRY) * time.Second)
- }
- }
-}
-
-type FSMHandler struct {
- t tomb.Tomb
- fsm *FSM
- conn net.Conn
- msgCh *channels.InfiniteChannel
- stateReasonCh chan FsmStateReason
- incoming *channels.InfiniteChannel
- stateCh chan *FsmMsg
- outgoing *channels.InfiniteChannel
- holdTimerResetCh chan bool
- sentNotification *bgp.BGPMessage
-}
-
-func NewFSMHandler(fsm *FSM, incoming *channels.InfiniteChannel, stateCh chan *FsmMsg, outgoing *channels.InfiniteChannel) *FSMHandler {
- h := &FSMHandler{
- fsm: fsm,
- stateReasonCh: make(chan FsmStateReason, 2),
- incoming: incoming,
- stateCh: stateCh,
- outgoing: outgoing,
- holdTimerResetCh: make(chan bool, 2),
- }
- fsm.t.Go(h.loop)
- return h
-}
-
-func (h *FSMHandler) idle() (bgp.FSMState, *FsmStateReason) {
- fsm := h.fsm
-
- idleHoldTimer := time.NewTimer(time.Second * time.Duration(fsm.idleHoldTime))
- for {
- select {
- case <-h.t.Dying():
- return -1, NewFsmStateReason(FSM_DYING, nil, nil)
- case <-fsm.gracefulRestartTimer.C:
- if fsm.pConf.GracefulRestart.State.PeerRestarting {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "State": fsm.state.String(),
- }).Warn("graceful restart timer expired")
- return bgp.BGP_FSM_IDLE, NewFsmStateReason(FSM_RESTART_TIMER_EXPIRED, nil, nil)
- }
- case conn, ok := <-fsm.connCh:
- if !ok {
- break
- }
- conn.Close()
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "State": fsm.state.String(),
- }).Warn("Closed an accepted connection")
- case <-idleHoldTimer.C:
-
- if fsm.adminState == ADMIN_STATE_UP {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "Duration": fsm.idleHoldTime,
- }).Debug("IdleHoldTimer expired")
- fsm.idleHoldTime = HOLDTIME_IDLE
- return bgp.BGP_FSM_ACTIVE, NewFsmStateReason(FSM_IDLE_HOLD_TIMER_EXPIRED, nil, nil)
-
- } else {
- log.WithFields(log.Fields{"Topic": "Peer"}).Debug("IdleHoldTimer expired, but stay at idle because the admin state is DOWN")
- }
-
- case stateOp := <-fsm.adminStateCh:
- err := h.changeAdminState(stateOp.State)
- if err == nil {
- switch stateOp.State {
- case ADMIN_STATE_DOWN:
- // stop idle hold timer
- idleHoldTimer.Stop()
-
- case ADMIN_STATE_UP:
- // restart idle hold timer
- idleHoldTimer.Reset(time.Second * time.Duration(fsm.idleHoldTime))
- }
- }
- }
- }
-}
-
-func (h *FSMHandler) active() (bgp.FSMState, *FsmStateReason) {
- fsm := h.fsm
- for {
- select {
- case <-h.t.Dying():
- return -1, NewFsmStateReason(FSM_DYING, nil, nil)
- case conn, ok := <-fsm.connCh:
- if !ok {
- break
- }
- fsm.conn = conn
- ttl := 0
- ttlMin := 0
- if fsm.pConf.TtlSecurity.Config.Enabled {
- ttl = 255
- ttlMin = int(fsm.pConf.TtlSecurity.Config.TtlMin)
- } else if fsm.pConf.Config.PeerAs != 0 && fsm.pConf.Config.PeerType == config.PEER_TYPE_EXTERNAL {
- if fsm.pConf.EbgpMultihop.Config.Enabled {
- ttl = int(fsm.pConf.EbgpMultihop.Config.MultihopTtl)
- } else if fsm.pConf.Transport.Config.Ttl != 0 {
- ttl = int(fsm.pConf.Transport.Config.Ttl)
- } else {
- ttl = 1
- }
- } else if fsm.pConf.Transport.Config.Ttl != 0 {
- ttl = int(fsm.pConf.Transport.Config.Ttl)
- }
- if ttl != 0 {
- if err := SetTcpTTLSockopt(conn.(*net.TCPConn), ttl); err != nil {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.Config.NeighborAddress,
- "State": fsm.state.String(),
- }).Warnf("cannot set TTL(=%d) for peer: %s", ttl, err)
- }
- }
- if ttlMin != 0 {
- if err := SetTcpMinTTLSockopt(conn.(*net.TCPConn), ttlMin); err != nil {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.Config.NeighborAddress,
- "State": fsm.state.String(),
- }).Warnf("cannot set minimal TTL(=%d) for peer: %s", ttl, err)
- }
- }
- // we don't implement delayed open timer so move to opensent right
- // away.
- return bgp.BGP_FSM_OPENSENT, NewFsmStateReason(FSM_NEW_CONNECTION, nil, nil)
- case <-fsm.gracefulRestartTimer.C:
- if fsm.pConf.GracefulRestart.State.PeerRestarting {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "State": fsm.state.String(),
- }).Warn("graceful restart timer expired")
- return bgp.BGP_FSM_IDLE, NewFsmStateReason(FSM_RESTART_TIMER_EXPIRED, nil, nil)
- }
- case err := <-h.stateReasonCh:
- return bgp.BGP_FSM_IDLE, &err
- case stateOp := <-fsm.adminStateCh:
- err := h.changeAdminState(stateOp.State)
- if err == nil {
- switch stateOp.State {
- case ADMIN_STATE_DOWN:
- return bgp.BGP_FSM_IDLE, NewFsmStateReason(FSM_ADMIN_DOWN, nil, nil)
- case ADMIN_STATE_UP:
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "State": fsm.state.String(),
- "AdminState": stateOp.State.String(),
- }).Panic("code logic bug")
- }
- }
- }
- }
-}
-
-func capAddPathFromConfig(pConf *config.Neighbor) bgp.ParameterCapabilityInterface {
- tuples := make([]*bgp.CapAddPathTuple, 0, len(pConf.AfiSafis))
- for _, af := range pConf.AfiSafis {
- var mode bgp.BGPAddPathMode
- if af.AddPaths.State.Receive {
- mode |= bgp.BGP_ADD_PATH_RECEIVE
- }
- if af.AddPaths.State.SendMax > 0 {
- mode |= bgp.BGP_ADD_PATH_SEND
- }
- if mode > 0 {
- tuples = append(tuples, bgp.NewCapAddPathTuple(af.State.Family, mode))
- }
- }
- if len(tuples) == 0 {
- return nil
- }
- return bgp.NewCapAddPath(tuples)
-}
-
-func capabilitiesFromConfig(pConf *config.Neighbor) []bgp.ParameterCapabilityInterface {
- caps := make([]bgp.ParameterCapabilityInterface, 0, 4)
- caps = append(caps, bgp.NewCapRouteRefresh())
- for _, af := range pConf.AfiSafis {
- caps = append(caps, bgp.NewCapMultiProtocol(af.State.Family))
- }
- caps = append(caps, bgp.NewCapFourOctetASNumber(pConf.Config.LocalAs))
-
- if c := pConf.GracefulRestart.Config; c.Enabled {
- tuples := []*bgp.CapGracefulRestartTuple{}
- ltuples := []*bgp.CapLongLivedGracefulRestartTuple{}
-
- // RFC 4724 4.1
- // To re-establish the session with its peer, the Restarting Speaker
- // MUST set the "Restart State" bit in the Graceful Restart Capability
- // of the OPEN message.
- restarting := pConf.GracefulRestart.State.LocalRestarting
-
- if !c.HelperOnly {
- for i, rf := range pConf.AfiSafis {
- if m := rf.MpGracefulRestart.Config; m.Enabled {
- // When restarting, always flag forwaring bit.
- // This can be a lie, depending on how gobgpd is used.
- // For a route-server use-case, since a route-server
- // itself doesn't forward packets, and the dataplane
- // is a l2 switch which continues to work with no
- // relation to bgpd, this behavior is ok.
- // TODO consideration of other use-cases
- tuples = append(tuples, bgp.NewCapGracefulRestartTuple(rf.State.Family, restarting))
- pConf.AfiSafis[i].MpGracefulRestart.State.Advertised = true
- }
- if m := rf.LongLivedGracefulRestart.Config; m.Enabled {
- ltuples = append(ltuples, bgp.NewCapLongLivedGracefulRestartTuple(rf.State.Family, restarting, m.RestartTime))
- }
- }
- }
- restartTime := c.RestartTime
- notification := c.NotificationEnabled
- caps = append(caps, bgp.NewCapGracefulRestart(restarting, notification, restartTime, tuples))
- if c.LongLivedEnabled {
- caps = append(caps, bgp.NewCapLongLivedGracefulRestart(ltuples))
- }
- }
-
- // unnumbered BGP
- if pConf.Config.NeighborInterface != "" {
- tuples := []*bgp.CapExtendedNexthopTuple{}
- families, _ := config.AfiSafis(pConf.AfiSafis).ToRfList()
- for _, family := range families {
- if family == bgp.RF_IPv6_UC {
- continue
- }
- tuple := bgp.NewCapExtendedNexthopTuple(family, bgp.AFI_IP6)
- tuples = append(tuples, tuple)
- }
- caps = append(caps, bgp.NewCapExtendedNexthop(tuples))
- }
-
- // ADD-PATH Capability
- if c := capAddPathFromConfig(pConf); c != nil {
- caps = append(caps, capAddPathFromConfig(pConf))
- }
-
- return caps
-}
-
-func buildopen(gConf *config.Global, pConf *config.Neighbor) *bgp.BGPMessage {
- caps := capabilitiesFromConfig(pConf)
- opt := bgp.NewOptionParameterCapability(caps)
- holdTime := uint16(pConf.Timers.Config.HoldTime)
- as := pConf.Config.LocalAs
- if as > (1<<16)-1 {
- as = bgp.AS_TRANS
- }
- return bgp.NewBGPOpenMessage(uint16(as), holdTime, gConf.Config.RouterId,
- []bgp.OptionParameterInterface{opt})
-}
-
-func readAll(conn net.Conn, length int) ([]byte, error) {
- buf := make([]byte, length)
- _, err := io.ReadFull(conn, buf)
- if err != nil {
- return nil, err
- }
- return buf, nil
-}
-
-func getPathAttrFromBGPUpdate(m *bgp.BGPUpdate, typ bgp.BGPAttrType) bgp.PathAttributeInterface {
- for _, a := range m.PathAttributes {
- if a.GetType() == typ {
- return a
- }
- }
- return nil
-}
-
-func hasOwnASLoop(ownAS uint32, limit int, asPath *bgp.PathAttributeAsPath) bool {
- cnt := 0
- for _, param := range asPath.Value {
- for _, as := range param.GetAS() {
- if as == ownAS {
- cnt++
- if cnt > limit {
- return true
- }
- }
- }
- }
- return false
-}
-
-func extractRouteFamily(p *bgp.PathAttributeInterface) *bgp.RouteFamily {
- attr := *p
-
- var afi uint16
- var safi uint8
-
- switch a := attr.(type) {
- case *bgp.PathAttributeMpReachNLRI:
- afi = a.AFI
- safi = a.SAFI
- case *bgp.PathAttributeMpUnreachNLRI:
- afi = a.AFI
- safi = a.SAFI
- default:
- return nil
- }
-
- rf := bgp.AfiSafiToRouteFamily(afi, safi)
- return &rf
-}
-
-func (h *FSMHandler) afiSafiDisable(rf bgp.RouteFamily) string {
- n := bgp.AddressFamilyNameMap[rf]
-
- for i, a := range h.fsm.pConf.AfiSafis {
- if string(a.Config.AfiSafiName) == n {
- h.fsm.pConf.AfiSafis[i].State.Enabled = false
- break
- }
- }
- newList := make([]bgp.ParameterCapabilityInterface, 0)
- for _, c := range h.fsm.capMap[bgp.BGP_CAP_MULTIPROTOCOL] {
- if c.(*bgp.CapMultiProtocol).CapValue == rf {
- continue
- }
- newList = append(newList, c)
- }
- h.fsm.capMap[bgp.BGP_CAP_MULTIPROTOCOL] = newList
- return n
-}
-
-func (h *FSMHandler) handlingError(m *bgp.BGPMessage, e error, useRevisedError bool) bgp.ErrorHandling {
- handling := bgp.ERROR_HANDLING_NONE
- if m.Header.Type == bgp.BGP_MSG_UPDATE && useRevisedError {
- factor := e.(*bgp.MessageError)
- handling = factor.ErrorHandling
- switch handling {
- case bgp.ERROR_HANDLING_ATTRIBUTE_DISCARD:
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": h.fsm.pConf.State.NeighborAddress,
- "State": h.fsm.state.String(),
- "error": e,
- }).Warn("Some attributes were discarded")
- case bgp.ERROR_HANDLING_TREAT_AS_WITHDRAW:
- m.Body = bgp.TreatAsWithdraw(m.Body.(*bgp.BGPUpdate))
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": h.fsm.pConf.State.NeighborAddress,
- "State": h.fsm.state.String(),
- "error": e,
- }).Warn("the received Update message was treated as withdraw")
- case bgp.ERROR_HANDLING_AFISAFI_DISABLE:
- rf := extractRouteFamily(factor.ErrorAttribute)
- if rf == nil {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": h.fsm.pConf.State.NeighborAddress,
- "State": h.fsm.state.String(),
- }).Warn("Error occurred during AFI/SAFI disabling")
- } else {
- n := h.afiSafiDisable(*rf)
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": h.fsm.pConf.State.NeighborAddress,
- "State": h.fsm.state.String(),
- "error": e,
- }).Warnf("Capability %s was disabled", n)
- }
- }
- } else {
- handling = bgp.ERROR_HANDLING_SESSION_RESET
- }
- return handling
-}
-
-func (h *FSMHandler) recvMessageWithError() (*FsmMsg, error) {
- sendToStateReasonCh := func(typ FsmStateReasonType, notif *bgp.BGPMessage) {
- // probably doesn't happen but be cautious
- select {
- case h.stateReasonCh <- *NewFsmStateReason(typ, notif, nil):
- default:
- }
- }
-
- headerBuf, err := readAll(h.conn, bgp.BGP_HEADER_LENGTH)
- if err != nil {
- sendToStateReasonCh(FSM_READ_FAILED, nil)
- return nil, err
- }
-
- hd := &bgp.BGPHeader{}
- err = hd.DecodeFromBytes(headerBuf)
- if err != nil {
- h.fsm.bgpMessageStateUpdate(0, true)
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": h.fsm.pConf.State.NeighborAddress,
- "State": h.fsm.state.String(),
- "error": err,
- }).Warn("Session will be reset due to malformed BGP Header")
- fmsg := &FsmMsg{
- MsgType: FSM_MSG_BGP_MESSAGE,
- MsgSrc: h.fsm.pConf.State.NeighborAddress,
- MsgData: err,
- Version: h.fsm.version,
- }
- return fmsg, err
- }
-
- bodyBuf, err := readAll(h.conn, int(hd.Len)-bgp.BGP_HEADER_LENGTH)
- if err != nil {
- sendToStateReasonCh(FSM_READ_FAILED, nil)
- return nil, err
- }
-
- now := time.Now()
- useRevisedError := h.fsm.pConf.ErrorHandling.Config.TreatAsWithdraw
- handling := bgp.ERROR_HANDLING_NONE
-
- m, err := bgp.ParseBGPBody(hd, bodyBuf, h.fsm.marshallingOptions)
- if err != nil {
- handling = h.handlingError(m, err, useRevisedError)
- h.fsm.bgpMessageStateUpdate(0, true)
- } else {
- h.fsm.bgpMessageStateUpdate(m.Header.Type, true)
- err = bgp.ValidateBGPMessage(m)
- }
- fmsg := &FsmMsg{
- MsgType: FSM_MSG_BGP_MESSAGE,
- MsgSrc: h.fsm.pConf.State.NeighborAddress,
- timestamp: now,
- Version: h.fsm.version,
- }
-
- switch handling {
- case bgp.ERROR_HANDLING_AFISAFI_DISABLE:
- fmsg.MsgData = m
- return fmsg, nil
- case bgp.ERROR_HANDLING_SESSION_RESET:
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": h.fsm.pConf.State.NeighborAddress,
- "State": h.fsm.state.String(),
- "error": err,
- }).Warn("Session will be reset due to malformed BGP message")
- fmsg.MsgData = err
- return fmsg, err
- default:
- fmsg.MsgData = m
- if h.fsm.state == bgp.BGP_FSM_ESTABLISHED {
- switch m.Header.Type {
- case bgp.BGP_MSG_ROUTE_REFRESH:
- fmsg.MsgType = FSM_MSG_ROUTE_REFRESH
- case bgp.BGP_MSG_UPDATE:
- body := m.Body.(*bgp.BGPUpdate)
- isEBGP := h.fsm.pConf.IsEBGPPeer(h.fsm.gConf)
- isConfed := h.fsm.pConf.IsConfederationMember(h.fsm.gConf)
-
- fmsg.payload = make([]byte, len(headerBuf)+len(bodyBuf))
- copy(fmsg.payload, headerBuf)
- copy(fmsg.payload[len(headerBuf):], bodyBuf)
-
- ok, err := bgp.ValidateUpdateMsg(body, h.fsm.rfMap, isEBGP, isConfed)
- if !ok {
- handling = h.handlingError(m, err, useRevisedError)
- }
- if handling == bgp.ERROR_HANDLING_SESSION_RESET {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": h.fsm.pConf.State.NeighborAddress,
- "State": h.fsm.state.String(),
- "error": err,
- }).Warn("Session will be reset due to malformed BGP update message")
- fmsg.MsgData = err
- return fmsg, err
- }
-
- if routes := len(body.WithdrawnRoutes); routes > 0 {
- h.fsm.bmpStatsUpdate(bmp.BMP_STAT_TYPE_WITHDRAW_UPDATE, 1)
- h.fsm.bmpStatsUpdate(bmp.BMP_STAT_TYPE_WITHDRAW_PREFIX, routes)
- } else if attr := getPathAttrFromBGPUpdate(body, bgp.BGP_ATTR_TYPE_MP_UNREACH_NLRI); attr != nil {
- mpUnreach := attr.(*bgp.PathAttributeMpUnreachNLRI)
- if routes = len(mpUnreach.Value); routes > 0 {
- h.fsm.bmpStatsUpdate(bmp.BMP_STAT_TYPE_WITHDRAW_UPDATE, 1)
- h.fsm.bmpStatsUpdate(bmp.BMP_STAT_TYPE_WITHDRAW_PREFIX, routes)
- }
- }
-
- table.UpdatePathAttrs4ByteAs(body)
- if err = table.UpdatePathAggregator4ByteAs(body); err != nil {
- fmsg.MsgData = err
- return fmsg, err
- }
-
- fmsg.PathList = table.ProcessMessage(m, h.fsm.peerInfo, fmsg.timestamp)
- fallthrough
- case bgp.BGP_MSG_KEEPALIVE:
- // if the length of h.holdTimerResetCh
- // isn't zero, the timer will be reset
- // soon anyway.
- select {
- case h.holdTimerResetCh <- true:
- default:
- }
- if m.Header.Type == bgp.BGP_MSG_KEEPALIVE {
- return nil, nil
- }
- case bgp.BGP_MSG_NOTIFICATION:
- body := m.Body.(*bgp.BGPNotification)
- if body.ErrorCode == bgp.BGP_ERROR_CEASE && (body.ErrorSubcode == bgp.BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN || body.ErrorSubcode == bgp.BGP_ERROR_SUB_ADMINISTRATIVE_RESET) {
- communication, rest := decodeAdministrativeCommunication(body.Data)
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": h.fsm.pConf.State.NeighborAddress,
- "Code": body.ErrorCode,
- "Subcode": body.ErrorSubcode,
- "Communicated-Reason": communication,
- "Data": rest,
- }).Warn("received notification")
- } else {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": h.fsm.pConf.State.NeighborAddress,
- "Code": body.ErrorCode,
- "Subcode": body.ErrorSubcode,
- "Data": body.Data,
- }).Warn("received notification")
- }
-
- if s := h.fsm.pConf.GracefulRestart.State; s.Enabled && s.NotificationEnabled && body.ErrorCode == bgp.BGP_ERROR_CEASE && body.ErrorSubcode == bgp.BGP_ERROR_SUB_HARD_RESET {
- sendToStateReasonCh(FSM_HARD_RESET, m)
- } else {
- sendToStateReasonCh(FSM_NOTIFICATION_RECV, m)
- }
- return nil, nil
- }
- }
- }
- return fmsg, nil
-}
-
-func (h *FSMHandler) recvMessage() error {
- defer h.msgCh.Close()
- fmsg, _ := h.recvMessageWithError()
- if fmsg != nil {
- h.msgCh.In() <- fmsg
- }
- return nil
-}
-
-func open2Cap(open *bgp.BGPOpen, n *config.Neighbor) (map[bgp.BGPCapabilityCode][]bgp.ParameterCapabilityInterface, map[bgp.RouteFamily]bgp.BGPAddPathMode) {
- capMap := make(map[bgp.BGPCapabilityCode][]bgp.ParameterCapabilityInterface)
- for _, p := range open.OptParams {
- if paramCap, y := p.(*bgp.OptionParameterCapability); y {
- for _, c := range paramCap.Capability {
- m, ok := capMap[c.Code()]
- if !ok {
- m = make([]bgp.ParameterCapabilityInterface, 0, 1)
- }
- capMap[c.Code()] = append(m, c)
- }
- }
- }
-
- // squash add path cap
- if caps, y := capMap[bgp.BGP_CAP_ADD_PATH]; y {
- items := make([]*bgp.CapAddPathTuple, 0, len(caps))
- for _, c := range caps {
- items = append(items, c.(*bgp.CapAddPath).Tuples...)
- }
- capMap[bgp.BGP_CAP_ADD_PATH] = []bgp.ParameterCapabilityInterface{bgp.NewCapAddPath(items)}
- }
-
- // remote open message may not include multi-protocol capability
- if _, y := capMap[bgp.BGP_CAP_MULTIPROTOCOL]; !y {
- capMap[bgp.BGP_CAP_MULTIPROTOCOL] = []bgp.ParameterCapabilityInterface{bgp.NewCapMultiProtocol(bgp.RF_IPv4_UC)}
- }
-
- local := n.CreateRfMap()
- remote := make(map[bgp.RouteFamily]bgp.BGPAddPathMode)
- for _, c := range capMap[bgp.BGP_CAP_MULTIPROTOCOL] {
- family := c.(*bgp.CapMultiProtocol).CapValue
- remote[family] = bgp.BGP_ADD_PATH_NONE
- for _, a := range capMap[bgp.BGP_CAP_ADD_PATH] {
- for _, i := range a.(*bgp.CapAddPath).Tuples {
- if i.RouteFamily == family {
- remote[family] = i.Mode
- }
- }
- }
- }
- negotiated := make(map[bgp.RouteFamily]bgp.BGPAddPathMode)
- for family, mode := range local {
- if m, y := remote[family]; y {
- n := bgp.BGP_ADD_PATH_NONE
- if mode&bgp.BGP_ADD_PATH_SEND > 0 && m&bgp.BGP_ADD_PATH_RECEIVE > 0 {
- n |= bgp.BGP_ADD_PATH_SEND
- }
- if mode&bgp.BGP_ADD_PATH_RECEIVE > 0 && m&bgp.BGP_ADD_PATH_SEND > 0 {
- n |= bgp.BGP_ADD_PATH_RECEIVE
- }
- negotiated[family] = n
- }
- }
- return capMap, negotiated
-}
-
-func (h *FSMHandler) opensent() (bgp.FSMState, *FsmStateReason) {
- fsm := h.fsm
- m := buildopen(fsm.gConf, fsm.pConf)
- b, _ := m.Serialize()
- fsm.conn.Write(b)
- fsm.bgpMessageStateUpdate(m.Header.Type, false)
-
- h.msgCh = channels.NewInfiniteChannel()
- h.conn = fsm.conn
-
- h.t.Go(h.recvMessage)
-
- // RFC 4271 P.60
- // sets its HoldTimer to a large value
- // A HoldTimer value of 4 minutes is suggested as a "large value"
- // for the HoldTimer
- holdTimer := time.NewTimer(time.Second * time.Duration(fsm.opensentHoldTime))
-
- for {
- select {
- case <-h.t.Dying():
- h.conn.Close()
- return -1, NewFsmStateReason(FSM_DYING, nil, nil)
- case conn, ok := <-fsm.connCh:
- if !ok {
- break
- }
- conn.Close()
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "State": fsm.state.String(),
- }).Warn("Closed an accepted connection")
- case <-fsm.gracefulRestartTimer.C:
- if fsm.pConf.GracefulRestart.State.PeerRestarting {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "State": fsm.state.String(),
- }).Warn("graceful restart timer expired")
- h.conn.Close()
- return bgp.BGP_FSM_IDLE, NewFsmStateReason(FSM_RESTART_TIMER_EXPIRED, nil, nil)
- }
- case i, ok := <-h.msgCh.Out():
- if !ok {
- continue
- }
- e := i.(*FsmMsg)
- switch e.MsgData.(type) {
- case *bgp.BGPMessage:
- m := e.MsgData.(*bgp.BGPMessage)
- if m.Header.Type == bgp.BGP_MSG_OPEN {
- fsm.recvOpen = m
- body := m.Body.(*bgp.BGPOpen)
- peerAs, err := bgp.ValidateOpenMsg(body, fsm.pConf.Config.PeerAs)
- if err != nil {
- m, _ := fsm.sendNotificationFromErrorMsg(err.(*bgp.MessageError))
- return bgp.BGP_FSM_IDLE, NewFsmStateReason(FSM_INVALID_MSG, m, nil)
- }
-
- // ASN negotiation was skipped
- if fsm.pConf.Config.PeerAs == 0 {
- typ := config.PEER_TYPE_EXTERNAL
- if fsm.peerInfo.LocalAS == peerAs {
- typ = config.PEER_TYPE_INTERNAL
- }
- fsm.pConf.State.PeerType = typ
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "State": fsm.state.String(),
- }).Infof("skipped asn negotiation: peer-as: %d, peer-type: %s", peerAs, typ)
- } else {
- fsm.pConf.State.PeerType = fsm.pConf.Config.PeerType
- }
- fsm.pConf.State.PeerAs = peerAs
- fsm.peerInfo.AS = peerAs
- fsm.peerInfo.ID = body.ID
- fsm.capMap, fsm.rfMap = open2Cap(body, fsm.pConf)
-
- if _, y := fsm.capMap[bgp.BGP_CAP_ADD_PATH]; y {
- fsm.marshallingOptions = &bgp.MarshallingOption{
- AddPath: fsm.rfMap,
- }
- } else {
- fsm.marshallingOptions = nil
- }
-
- // calculate HoldTime
- // RFC 4271 P.13
- // a BGP speaker MUST calculate the value of the Hold Timer
- // by using the smaller of its configured Hold Time and the Hold Time
- // received in the OPEN message.
- holdTime := float64(body.HoldTime)
- myHoldTime := fsm.pConf.Timers.Config.HoldTime
- if holdTime > myHoldTime {
- fsm.pConf.Timers.State.NegotiatedHoldTime = myHoldTime
- } else {
- fsm.pConf.Timers.State.NegotiatedHoldTime = holdTime
- }
-
- keepalive := fsm.pConf.Timers.Config.KeepaliveInterval
- if n := fsm.pConf.Timers.State.NegotiatedHoldTime; n < myHoldTime {
- keepalive = n / 3
- }
- fsm.pConf.Timers.State.KeepaliveInterval = keepalive
-
- gr, ok := fsm.capMap[bgp.BGP_CAP_GRACEFUL_RESTART]
- if fsm.pConf.GracefulRestart.Config.Enabled && ok {
- state := &fsm.pConf.GracefulRestart.State
- state.Enabled = true
- cap := gr[len(gr)-1].(*bgp.CapGracefulRestart)
- state.PeerRestartTime = uint16(cap.Time)
-
- for _, t := range cap.Tuples {
- n := bgp.AddressFamilyNameMap[bgp.AfiSafiToRouteFamily(t.AFI, t.SAFI)]
- for i, a := range fsm.pConf.AfiSafis {
- if string(a.Config.AfiSafiName) == n {
- fsm.pConf.AfiSafis[i].MpGracefulRestart.State.Enabled = true
- fsm.pConf.AfiSafis[i].MpGracefulRestart.State.Received = true
- break
- }
- }
- }
-
- // RFC 4724 4.1
- // To re-establish the session with its peer, the Restarting Speaker
- // MUST set the "Restart State" bit in the Graceful Restart Capability
- // of the OPEN message.
- if fsm.pConf.GracefulRestart.State.PeerRestarting && cap.Flags&0x08 == 0 {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "State": fsm.state.String(),
- }).Warn("restart flag is not set")
- // send notification?
- h.conn.Close()
- return bgp.BGP_FSM_IDLE, NewFsmStateReason(FSM_INVALID_MSG, nil, nil)
- }
-
- // RFC 4724 3
- // The most significant bit is defined as the Restart State (R)
- // bit, ...(snip)... When set (value 1), this bit
- // indicates that the BGP speaker has restarted, and its peer MUST
- // NOT wait for the End-of-RIB marker from the speaker before
- // advertising routing information to the speaker.
- if fsm.pConf.GracefulRestart.State.LocalRestarting && cap.Flags&0x08 != 0 {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "State": fsm.state.String(),
- }).Debug("peer has restarted, skipping wait for EOR")
- for i := range fsm.pConf.AfiSafis {
- fsm.pConf.AfiSafis[i].MpGracefulRestart.State.EndOfRibReceived = true
- }
- }
- if fsm.pConf.GracefulRestart.Config.NotificationEnabled && cap.Flags&0x04 > 0 {
- fsm.pConf.GracefulRestart.State.NotificationEnabled = true
- }
- }
- llgr, ok2 := fsm.capMap[bgp.BGP_CAP_LONG_LIVED_GRACEFUL_RESTART]
- if fsm.pConf.GracefulRestart.Config.LongLivedEnabled && ok && ok2 {
- fsm.pConf.GracefulRestart.State.LongLivedEnabled = true
- cap := llgr[len(llgr)-1].(*bgp.CapLongLivedGracefulRestart)
- for _, t := range cap.Tuples {
- n := bgp.AddressFamilyNameMap[bgp.AfiSafiToRouteFamily(t.AFI, t.SAFI)]
- for i, a := range fsm.pConf.AfiSafis {
- if string(a.Config.AfiSafiName) == n {
- fsm.pConf.AfiSafis[i].LongLivedGracefulRestart.State.Enabled = true
- fsm.pConf.AfiSafis[i].LongLivedGracefulRestart.State.Received = true
- fsm.pConf.AfiSafis[i].LongLivedGracefulRestart.State.PeerRestartTime = t.RestartTime
- break
- }
- }
- }
- }
-
- msg := bgp.NewBGPKeepAliveMessage()
- b, _ := msg.Serialize()
- fsm.conn.Write(b)
- fsm.bgpMessageStateUpdate(msg.Header.Type, false)
- return bgp.BGP_FSM_OPENCONFIRM, NewFsmStateReason(FSM_OPEN_MSG_RECEIVED, nil, nil)
- } else {
- // send notification?
- h.conn.Close()
- return bgp.BGP_FSM_IDLE, NewFsmStateReason(FSM_INVALID_MSG, nil, nil)
- }
- case *bgp.MessageError:
- m, _ := fsm.sendNotificationFromErrorMsg(e.MsgData.(*bgp.MessageError))
- return bgp.BGP_FSM_IDLE, NewFsmStateReason(FSM_INVALID_MSG, m, nil)
- default:
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "State": fsm.state.String(),
- "Data": e.MsgData,
- }).Panic("unknown msg type")
- }
- case err := <-h.stateReasonCh:
- h.conn.Close()
- return bgp.BGP_FSM_IDLE, &err
- case <-holdTimer.C:
- m, _ := fsm.sendNotification(bgp.BGP_ERROR_HOLD_TIMER_EXPIRED, 0, nil, "hold timer expired")
- h.t.Kill(nil)
- return bgp.BGP_FSM_IDLE, NewFsmStateReason(FSM_HOLD_TIMER_EXPIRED, m, nil)
- case stateOp := <-fsm.adminStateCh:
- err := h.changeAdminState(stateOp.State)
- if err == nil {
- switch stateOp.State {
- case ADMIN_STATE_DOWN:
- h.conn.Close()
- return bgp.BGP_FSM_IDLE, NewFsmStateReason(FSM_ADMIN_DOWN, m, nil)
- case ADMIN_STATE_UP:
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "State": fsm.state.String(),
- "AdminState": stateOp.State.String(),
- }).Panic("code logic bug")
- }
- }
- }
- }
-}
-
-func keepaliveTicker(fsm *FSM) *time.Ticker {
- negotiatedTime := fsm.pConf.Timers.State.NegotiatedHoldTime
- if negotiatedTime == 0 {
- return &time.Ticker{}
- }
- sec := time.Second * time.Duration(fsm.pConf.Timers.State.KeepaliveInterval)
- if sec == 0 {
- sec = time.Second
- }
- return time.NewTicker(sec)
-}
-
-func (h *FSMHandler) openconfirm() (bgp.FSMState, *FsmStateReason) {
- fsm := h.fsm
- ticker := keepaliveTicker(fsm)
- h.msgCh = channels.NewInfiniteChannel()
- h.conn = fsm.conn
-
- h.t.Go(h.recvMessage)
-
- var holdTimer *time.Timer
- if fsm.pConf.Timers.State.NegotiatedHoldTime == 0 {
- holdTimer = &time.Timer{}
- } else {
- // RFC 4271 P.65
- // sets the HoldTimer according to the negotiated value
- holdTimer = time.NewTimer(time.Second * time.Duration(fsm.pConf.Timers.State.NegotiatedHoldTime))
- }
-
- for {
- select {
- case <-h.t.Dying():
- h.conn.Close()
- return -1, NewFsmStateReason(FSM_DYING, nil, nil)
- case conn, ok := <-fsm.connCh:
- if !ok {
- break
- }
- conn.Close()
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "State": fsm.state.String(),
- }).Warn("Closed an accepted connection")
- case <-fsm.gracefulRestartTimer.C:
- if fsm.pConf.GracefulRestart.State.PeerRestarting {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "State": fsm.state.String(),
- }).Warn("graceful restart timer expired")
- h.conn.Close()
- return bgp.BGP_FSM_IDLE, NewFsmStateReason(FSM_RESTART_TIMER_EXPIRED, nil, nil)
- }
- case <-ticker.C:
- m := bgp.NewBGPKeepAliveMessage()
- b, _ := m.Serialize()
- // TODO: check error
- fsm.conn.Write(b)
- fsm.bgpMessageStateUpdate(m.Header.Type, false)
- case i, ok := <-h.msgCh.Out():
- if !ok {
- continue
- }
- e := i.(*FsmMsg)
- switch e.MsgData.(type) {
- case *bgp.BGPMessage:
- m := e.MsgData.(*bgp.BGPMessage)
- if m.Header.Type == bgp.BGP_MSG_KEEPALIVE {
- return bgp.BGP_FSM_ESTABLISHED, NewFsmStateReason(FSM_OPEN_MSG_NEGOTIATED, nil, nil)
- }
- // send notification ?
- h.conn.Close()
- return bgp.BGP_FSM_IDLE, NewFsmStateReason(FSM_INVALID_MSG, nil, nil)
- case *bgp.MessageError:
- m, _ := fsm.sendNotificationFromErrorMsg(e.MsgData.(*bgp.MessageError))
- return bgp.BGP_FSM_IDLE, NewFsmStateReason(FSM_INVALID_MSG, m, nil)
- default:
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "State": fsm.state.String(),
- "Data": e.MsgData,
- }).Panic("unknown msg type")
- }
- case err := <-h.stateReasonCh:
- h.conn.Close()
- return bgp.BGP_FSM_IDLE, &err
- case <-holdTimer.C:
- m, _ := fsm.sendNotification(bgp.BGP_ERROR_HOLD_TIMER_EXPIRED, 0, nil, "hold timer expired")
- h.t.Kill(nil)
- return bgp.BGP_FSM_IDLE, NewFsmStateReason(FSM_HOLD_TIMER_EXPIRED, m, nil)
- case stateOp := <-fsm.adminStateCh:
- err := h.changeAdminState(stateOp.State)
- if err == nil {
- switch stateOp.State {
- case ADMIN_STATE_DOWN:
- h.conn.Close()
- return bgp.BGP_FSM_IDLE, NewFsmStateReason(FSM_ADMIN_DOWN, nil, nil)
- case ADMIN_STATE_UP:
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "State": fsm.state.String(),
- "AdminState": stateOp.State.String(),
- }).Panic("code logic bug")
- }
- }
- }
- }
-}
-
-func (h *FSMHandler) sendMessageloop() error {
- conn := h.conn
- fsm := h.fsm
- ticker := keepaliveTicker(fsm)
- send := func(m *bgp.BGPMessage) error {
- if fsm.twoByteAsTrans && m.Header.Type == bgp.BGP_MSG_UPDATE {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "State": fsm.state.String(),
- "Data": m,
- }).Debug("update for 2byte AS peer")
- table.UpdatePathAttrs2ByteAs(m.Body.(*bgp.BGPUpdate))
- table.UpdatePathAggregator2ByteAs(m.Body.(*bgp.BGPUpdate))
- }
- b, err := m.Serialize(h.fsm.marshallingOptions)
- if err != nil {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "State": fsm.state.String(),
- "Data": err,
- }).Warn("failed to serialize")
- fsm.bgpMessageStateUpdate(0, false)
- return nil
- }
- if err := conn.SetWriteDeadline(time.Now().Add(time.Second * time.Duration(fsm.pConf.Timers.State.NegotiatedHoldTime))); err != nil {
- h.stateReasonCh <- *NewFsmStateReason(FSM_WRITE_FAILED, nil, nil)
- conn.Close()
- return fmt.Errorf("failed to set write deadline")
- }
- _, err = conn.Write(b)
- if err != nil {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "State": fsm.state.String(),
- "Data": err,
- }).Warn("failed to send")
- h.stateReasonCh <- *NewFsmStateReason(FSM_WRITE_FAILED, nil, nil)
- conn.Close()
- return fmt.Errorf("closed")
- }
- fsm.bgpMessageStateUpdate(m.Header.Type, false)
-
- switch m.Header.Type {
- case bgp.BGP_MSG_NOTIFICATION:
- body := m.Body.(*bgp.BGPNotification)
- if body.ErrorCode == bgp.BGP_ERROR_CEASE && (body.ErrorSubcode == bgp.BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN || body.ErrorSubcode == bgp.BGP_ERROR_SUB_ADMINISTRATIVE_RESET) {
- communication, rest := decodeAdministrativeCommunication(body.Data)
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "State": fsm.state.String(),
- "Code": body.ErrorCode,
- "Subcode": body.ErrorSubcode,
- "Communicated-Reason": communication,
- "Data": rest,
- }).Warn("sent notification")
- } else {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "State": fsm.state.String(),
- "Code": body.ErrorCode,
- "Subcode": body.ErrorSubcode,
- "Data": body.Data,
- }).Warn("sent notification")
- }
- h.stateReasonCh <- *NewFsmStateReason(FSM_NOTIFICATION_SENT, m, nil)
- conn.Close()
- return fmt.Errorf("closed")
- case bgp.BGP_MSG_UPDATE:
- update := m.Body.(*bgp.BGPUpdate)
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "State": fsm.state.String(),
- "nlri": update.NLRI,
- "withdrawals": update.WithdrawnRoutes,
- "attributes": update.PathAttributes,
- }).Debug("sent update")
- default:
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "State": fsm.state.String(),
- "data": m,
- }).Debug("sent")
- }
- return nil
- }
-
- for {
- select {
- case <-h.t.Dying():
- return nil
- case o := <-h.outgoing.Out():
- m := o.(*FsmOutgoingMsg)
- for _, msg := range table.CreateUpdateMsgFromPaths(m.Paths, h.fsm.marshallingOptions) {
- if err := send(msg); err != nil {
- return nil
- }
- }
- if m.Notification != nil {
- if m.StayIdle {
- // current user is only prefix-limit
- // fix me if this is not the case
- h.changeAdminState(ADMIN_STATE_PFX_CT)
- }
- if err := send(m.Notification); err != nil {
- return nil
- }
- }
- case <-ticker.C:
- if err := send(bgp.NewBGPKeepAliveMessage()); err != nil {
- return nil
- }
- }
- }
-}
-
-func (h *FSMHandler) recvMessageloop() error {
- for {
- fmsg, err := h.recvMessageWithError()
- if fmsg != nil {
- h.msgCh.In() <- fmsg
- }
- if err != nil {
- return nil
- }
- }
-}
-
-func (h *FSMHandler) established() (bgp.FSMState, *FsmStateReason) {
- fsm := h.fsm
- h.conn = fsm.conn
- h.t.Go(h.sendMessageloop)
- h.msgCh = h.incoming
- h.t.Go(h.recvMessageloop)
-
- var holdTimer *time.Timer
- if fsm.pConf.Timers.State.NegotiatedHoldTime == 0 {
- holdTimer = &time.Timer{}
- } else {
- holdTimer = time.NewTimer(time.Second * time.Duration(fsm.pConf.Timers.State.NegotiatedHoldTime))
- }
-
- fsm.gracefulRestartTimer.Stop()
-
- for {
- select {
- case <-h.t.Dying():
- return -1, NewFsmStateReason(FSM_DYING, nil, nil)
- case conn, ok := <-fsm.connCh:
- if !ok {
- break
- }
- conn.Close()
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "State": fsm.state.String(),
- }).Warn("Closed an accepted connection")
- case err := <-h.stateReasonCh:
- h.conn.Close()
- h.t.Kill(nil)
- if s := fsm.pConf.GracefulRestart.State; s.Enabled &&
- (s.NotificationEnabled && err.Type == FSM_NOTIFICATION_RECV ||
- err.Type == FSM_READ_FAILED ||
- err.Type == FSM_WRITE_FAILED) {
- err = *NewFsmStateReason(FSM_GRACEFUL_RESTART, nil, nil)
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "State": fsm.state.String(),
- }).Info("peer graceful restart")
- fsm.gracefulRestartTimer.Reset(time.Duration(fsm.pConf.GracefulRestart.State.PeerRestartTime) * time.Second)
- }
- return bgp.BGP_FSM_IDLE, &err
- case <-holdTimer.C:
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "State": fsm.state.String(),
- }).Warn("hold timer expired")
- m := bgp.NewBGPNotificationMessage(bgp.BGP_ERROR_HOLD_TIMER_EXPIRED, 0, nil)
- h.outgoing.In() <- &FsmOutgoingMsg{Notification: m}
- return bgp.BGP_FSM_IDLE, NewFsmStateReason(FSM_HOLD_TIMER_EXPIRED, m, nil)
- case <-h.holdTimerResetCh:
- if fsm.pConf.Timers.State.NegotiatedHoldTime != 0 {
- holdTimer.Reset(time.Second * time.Duration(fsm.pConf.Timers.State.NegotiatedHoldTime))
- }
- case stateOp := <-fsm.adminStateCh:
- err := h.changeAdminState(stateOp.State)
- if err == nil {
- switch stateOp.State {
- case ADMIN_STATE_DOWN:
- m := bgp.NewBGPNotificationMessage(bgp.BGP_ERROR_CEASE, bgp.BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN, stateOp.Communication)
- h.outgoing.In() <- &FsmOutgoingMsg{Notification: m}
- }
- }
- }
- }
-}
-
-func (h *FSMHandler) loop() error {
- fsm := h.fsm
- ch := make(chan bgp.FSMState)
- oldState := fsm.state
-
- var reason *FsmStateReason
- f := func() error {
- nextState := bgp.FSMState(-1)
- switch fsm.state {
- case bgp.BGP_FSM_IDLE:
- nextState, reason = h.idle()
- // case bgp.BGP_FSM_CONNECT:
- // nextState = h.connect()
- case bgp.BGP_FSM_ACTIVE:
- nextState, reason = h.active()
- case bgp.BGP_FSM_OPENSENT:
- nextState, reason = h.opensent()
- case bgp.BGP_FSM_OPENCONFIRM:
- nextState, reason = h.openconfirm()
- case bgp.BGP_FSM_ESTABLISHED:
- nextState, reason = h.established()
- }
- fsm.reason = reason
- ch <- nextState
- return nil
- }
-
- h.t.Go(f)
-
- nextState := <-ch
-
- if nextState == bgp.BGP_FSM_ESTABLISHED && oldState == bgp.BGP_FSM_OPENCONFIRM {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "State": fsm.state.String(),
- }).Info("Peer Up")
- }
-
- if oldState == bgp.BGP_FSM_ESTABLISHED {
- // The main goroutine sent the notificaiton due to
- // deconfiguration or something.
- reason := fsm.reason
- if fsm.h.sentNotification != nil {
- reason.Type = FSM_NOTIFICATION_SENT
- reason.PeerDownReason = PEER_DOWN_BY_LOCAL
- reason.BGPNotification = fsm.h.sentNotification
- }
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "State": fsm.state.String(),
- "Reason": reason.String(),
- }).Info("Peer Down")
- }
-
- e := time.AfterFunc(time.Second*120, func() {
- log.WithFields(log.Fields{"Topic": "Peer"}).Fatalf("failed to free the fsm.h.t for %s %s %s", fsm.pConf.State.NeighborAddress, oldState, nextState)
- })
- h.t.Wait()
- e.Stop()
-
- // under zero means that tomb.Dying()
- if nextState >= bgp.BGP_FSM_IDLE {
- h.stateCh <- &FsmMsg{
- MsgType: FSM_MSG_STATE_CHANGE,
- MsgSrc: fsm.pConf.State.NeighborAddress,
- MsgData: nextState,
- StateReason: reason,
- Version: h.fsm.version,
- }
- }
- return nil
-}
-
-func (h *FSMHandler) changeAdminState(s AdminState) error {
- fsm := h.fsm
- if fsm.adminState != s {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "State": fsm.state.String(),
- "AdminState": s.String(),
- }).Debug("admin state changed")
-
- fsm.adminState = s
- fsm.pConf.State.AdminDown = !fsm.pConf.State.AdminDown
-
- switch s {
- case ADMIN_STATE_UP:
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "State": fsm.state.String(),
- }).Info("Administrative start")
- case ADMIN_STATE_DOWN:
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "State": fsm.state.String(),
- }).Info("Administrative shutdown")
- case ADMIN_STATE_PFX_CT:
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "State": fsm.state.String(),
- }).Info("Administrative shutdown(Prefix limit reached)")
- }
-
- } else {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": fsm.pConf.State.NeighborAddress,
- "State": fsm.state.String(),
- }).Warn("cannot change to the same state")
-
- return fmt.Errorf("cannot change to the same state.")
- }
- return nil
-}
diff --git a/server/fsm_test.go b/server/fsm_test.go
deleted file mode 100644
index 73159556..00000000
--- a/server/fsm_test.go
+++ /dev/null
@@ -1,344 +0,0 @@
-// Copyright (C) 2014 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 (
- "errors"
- "net"
- "strconv"
- "sync"
- "testing"
- "time"
-
- "github.com/eapache/channels"
- "github.com/osrg/gobgp/config"
- "github.com/osrg/gobgp/packet/bgp"
- "github.com/osrg/gobgp/table"
- log "github.com/sirupsen/logrus"
- "github.com/stretchr/testify/assert"
-)
-
-type MockConnection struct {
- *testing.T
- net.Conn
- recvCh chan chan byte
- sendBuf [][]byte
- currentCh chan byte
- isClosed bool
- wait int
- mtx sync.Mutex
-}
-
-func NewMockConnection(t *testing.T) *MockConnection {
- m := &MockConnection{
- T: t,
- recvCh: make(chan chan byte, 128),
- sendBuf: make([][]byte, 0),
- isClosed: false,
- }
- return m
-}
-
-func (m *MockConnection) SetWriteDeadline(t time.Time) error {
- return nil
-}
-
-func (m *MockConnection) setData(data []byte) int {
- dataChan := make(chan byte, 4096)
- for _, b := range data {
- dataChan <- b
- }
- m.recvCh <- dataChan
- return len(dataChan)
-}
-
-func (m *MockConnection) Read(buf []byte) (int, error) {
- m.mtx.Lock()
- closed := m.isClosed
- m.mtx.Unlock()
- if closed {
- return 0, errors.New("already closed")
- }
-
- if m.currentCh == nil {
- m.currentCh = <-m.recvCh
- }
-
- length := 0
- rest := len(buf)
- for i := 0; i < rest; i++ {
- if len(m.currentCh) > 0 {
- val := <-m.currentCh
- buf[i] = val
- length++
- } else {
- m.currentCh = nil
- break
- }
- }
-
- m.Logf("%d bytes read from peer", length)
- return length, nil
-}
-
-func (m *MockConnection) Write(buf []byte) (int, error) {
- time.Sleep(time.Duration(m.wait) * time.Millisecond)
- m.sendBuf = append(m.sendBuf, buf)
- msg, _ := bgp.ParseBGPMessage(buf)
- m.Logf("%d bytes written by gobgp message type : %s",
- len(buf), showMessageType(msg.Header.Type))
- return len(buf), nil
-}
-
-func showMessageType(t uint8) string {
- switch t {
- case bgp.BGP_MSG_KEEPALIVE:
- return "BGP_MSG_KEEPALIVE"
- case bgp.BGP_MSG_NOTIFICATION:
- return "BGP_MSG_NOTIFICATION"
- case bgp.BGP_MSG_OPEN:
- return "BGP_MSG_OPEN"
- case bgp.BGP_MSG_UPDATE:
- return "BGP_MSG_UPDATE"
- case bgp.BGP_MSG_ROUTE_REFRESH:
- return "BGP_MSG_ROUTE_REFRESH"
- }
- return strconv.Itoa(int(t))
-}
-
-func (m *MockConnection) Close() error {
- m.mtx.Lock()
- defer m.mtx.Unlock()
- if !m.isClosed {
- close(m.recvCh)
- m.isClosed = true
- }
- return nil
-}
-
-func (m *MockConnection) LocalAddr() net.Addr {
- return &net.TCPAddr{
- IP: net.ParseIP("10.10.10.10"),
- Port: bgp.BGP_PORT}
-}
-
-func TestReadAll(t *testing.T) {
- assert := assert.New(t)
- m := NewMockConnection(t)
- msg := open()
- expected1, _ := msg.Header.Serialize()
- expected2, _ := msg.Body.Serialize()
-
- pushBytes := func() {
- m.Log("push 5 bytes")
- m.setData(expected1[0:5])
- m.Log("push rest")
- m.setData(expected1[5:])
- m.Log("push bytes at once")
- m.setData(expected2)
- }
-
- go pushBytes()
-
- var actual1 []byte
- actual1, _ = readAll(m, bgp.BGP_HEADER_LENGTH)
- m.Log(actual1)
- assert.Equal(expected1, actual1)
-
- var actual2 []byte
- actual2, _ = readAll(m, len(expected2))
- m.Log(actual2)
- assert.Equal(expected2, actual2)
-}
-
-func TestFSMHandlerOpensent_HoldTimerExpired(t *testing.T) {
- assert := assert.New(t)
- m := NewMockConnection(t)
-
- p, h := makePeerAndHandler()
-
- // push mock connection
- p.fsm.conn = m
- p.fsm.h = h
-
- // set keepalive ticker
- p.fsm.pConf.Timers.State.NegotiatedHoldTime = 3
-
- // set holdtime
- p.fsm.opensentHoldTime = 2
-
- state, _ := h.opensent()
-
- assert.Equal(bgp.BGP_FSM_IDLE, state)
- lastMsg := m.sendBuf[len(m.sendBuf)-1]
- sent, _ := bgp.ParseBGPMessage(lastMsg)
- assert.Equal(uint8(bgp.BGP_MSG_NOTIFICATION), sent.Header.Type)
- assert.Equal(uint8(bgp.BGP_ERROR_HOLD_TIMER_EXPIRED), sent.Body.(*bgp.BGPNotification).ErrorCode)
-
-}
-
-func TestFSMHandlerOpenconfirm_HoldTimerExpired(t *testing.T) {
- assert := assert.New(t)
- m := NewMockConnection(t)
-
- p, h := makePeerAndHandler()
-
- // push mock connection
- p.fsm.conn = m
- p.fsm.h = h
-
- // set up keepalive ticker
- p.fsm.pConf.Timers.Config.KeepaliveInterval = 1
-
- // set holdtime
- p.fsm.pConf.Timers.State.NegotiatedHoldTime = 2
- state, _ := h.openconfirm()
-
- assert.Equal(bgp.BGP_FSM_IDLE, state)
- lastMsg := m.sendBuf[len(m.sendBuf)-1]
- sent, _ := bgp.ParseBGPMessage(lastMsg)
- assert.Equal(uint8(bgp.BGP_MSG_NOTIFICATION), sent.Header.Type)
- assert.Equal(uint8(bgp.BGP_ERROR_HOLD_TIMER_EXPIRED), sent.Body.(*bgp.BGPNotification).ErrorCode)
-
-}
-
-func TestFSMHandlerEstablish_HoldTimerExpired(t *testing.T) {
- assert := assert.New(t)
- m := NewMockConnection(t)
-
- p, h := makePeerAndHandler()
-
- // push mock connection
- p.fsm.conn = m
- p.fsm.h = h
-
- // set keepalive ticker
- p.fsm.pConf.Timers.State.NegotiatedHoldTime = 3
-
- msg := keepalive()
- header, _ := msg.Header.Serialize()
- body, _ := msg.Body.Serialize()
-
- pushPackets := func() {
- // first keepalive from peer
- m.setData(header)
- m.setData(body)
- }
-
- // set holdtime
- p.fsm.pConf.Timers.Config.HoldTime = 2
- p.fsm.pConf.Timers.State.NegotiatedHoldTime = 2
-
- go pushPackets()
- state, _ := h.established()
- time.Sleep(time.Second * 1)
- assert.Equal(bgp.BGP_FSM_IDLE, state)
- m.mtx.Lock()
- lastMsg := m.sendBuf[len(m.sendBuf)-1]
- m.mtx.Unlock()
- sent, _ := bgp.ParseBGPMessage(lastMsg)
- assert.Equal(uint8(bgp.BGP_MSG_NOTIFICATION), sent.Header.Type)
- assert.Equal(uint8(bgp.BGP_ERROR_HOLD_TIMER_EXPIRED), sent.Body.(*bgp.BGPNotification).ErrorCode)
-}
-
-func TestFSMHandlerOpenconfirm_HoldtimeZero(t *testing.T) {
- log.SetLevel(log.DebugLevel)
- assert := assert.New(t)
- m := NewMockConnection(t)
-
- p, h := makePeerAndHandler()
-
- // push mock connection
- p.fsm.conn = m
- p.fsm.h = h
-
- // set up keepalive ticker
- p.fsm.pConf.Timers.Config.KeepaliveInterval = 1
- // set holdtime
- p.fsm.pConf.Timers.State.NegotiatedHoldTime = 0
- go h.openconfirm()
-
- time.Sleep(100 * time.Millisecond)
-
- assert.Equal(0, len(m.sendBuf))
-
-}
-
-func TestFSMHandlerEstablished_HoldtimeZero(t *testing.T) {
- log.SetLevel(log.DebugLevel)
- assert := assert.New(t)
- m := NewMockConnection(t)
-
- p, h := makePeerAndHandler()
-
- // push mock connection
- p.fsm.conn = m
- p.fsm.h = h
-
- // set holdtime
- p.fsm.pConf.Timers.State.NegotiatedHoldTime = 0
-
- go h.established()
-
- time.Sleep(100 * time.Millisecond)
-
- assert.Equal(0, len(m.sendBuf))
-}
-
-func TestCheckOwnASLoop(t *testing.T) {
- assert := assert.New(t)
- aspathParam := []bgp.AsPathParamInterface{bgp.NewAs4PathParam(2, []uint32{65100})}
- aspath := bgp.NewPathAttributeAsPath(aspathParam)
- assert.False(hasOwnASLoop(65100, 10, aspath))
- assert.True(hasOwnASLoop(65100, 0, aspath))
- assert.False(hasOwnASLoop(65200, 0, aspath))
-}
-
-func makePeerAndHandler() (*Peer, *FSMHandler) {
- p := &Peer{
- fsm: NewFSM(&config.Global{}, &config.Neighbor{}, table.NewRoutingPolicy()),
- outgoing: channels.NewInfiniteChannel(),
- }
-
- h := &FSMHandler{
- fsm: p.fsm,
- stateReasonCh: make(chan FsmStateReason, 2),
- incoming: channels.NewInfiniteChannel(),
- outgoing: p.outgoing,
- }
-
- return p, h
-
-}
-
-func open() *bgp.BGPMessage {
- p1 := bgp.NewOptionParameterCapability(
- []bgp.ParameterCapabilityInterface{bgp.NewCapRouteRefresh()})
- p2 := bgp.NewOptionParameterCapability(
- []bgp.ParameterCapabilityInterface{bgp.NewCapMultiProtocol(bgp.RF_IPv4_UC)})
- g := &bgp.CapGracefulRestartTuple{AFI: 4, SAFI: 2, Flags: 3}
- p3 := bgp.NewOptionParameterCapability(
- []bgp.ParameterCapabilityInterface{bgp.NewCapGracefulRestart(true, true, 100,
- []*bgp.CapGracefulRestartTuple{g})})
- p4 := bgp.NewOptionParameterCapability(
- []bgp.ParameterCapabilityInterface{bgp.NewCapFourOctetASNumber(100000)})
- return bgp.NewBGPOpenMessage(11033, 303, "100.4.10.3",
- []bgp.OptionParameterInterface{p1, p2, p3, p4})
-}
-
-func keepalive() *bgp.BGPMessage {
- return bgp.NewBGPKeepAliveMessage()
-}
diff --git a/server/grpc_server.go b/server/grpc_server.go
deleted file mode 100644
index 4dab8182..00000000
--- a/server/grpc_server.go
+++ /dev/null
@@ -1,3016 +0,0 @@
-// Copyright (C) 2014-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 (
- "bytes"
- "fmt"
- "io"
- "net"
- "reflect"
- "regexp"
- "strconv"
- "strings"
- "sync"
- "time"
-
- farm "github.com/dgryski/go-farm"
- "github.com/golang/protobuf/ptypes/any"
- log "github.com/sirupsen/logrus"
- "golang.org/x/net/context"
- "google.golang.org/grpc"
-
- api "github.com/osrg/gobgp/api"
- "github.com/osrg/gobgp/config"
- "github.com/osrg/gobgp/packet/bgp"
- "github.com/osrg/gobgp/table"
- "github.com/osrg/gobgp/zebra"
-)
-
-type Server struct {
- bgpServer *BgpServer
- grpcServer *grpc.Server
- hosts string
-}
-
-func NewGrpcServer(b *BgpServer, hosts string) *Server {
- size := 256 << 20
- return NewServer(b, grpc.NewServer(grpc.MaxRecvMsgSize(size), grpc.MaxSendMsgSize(size)), hosts)
-}
-
-func NewServer(b *BgpServer, g *grpc.Server, hosts string) *Server {
- grpc.EnableTracing = false
- s := &Server{
- bgpServer: b,
- grpcServer: g,
- hosts: hosts,
- }
- api.RegisterGobgpApiServer(g, s)
- return s
-}
-
-func (s *Server) Serve() error {
- var wg sync.WaitGroup
- l := strings.Split(s.hosts, ",")
- wg.Add(len(l))
-
- serve := func(host string) {
- defer wg.Done()
- lis, err := net.Listen("tcp", host)
- if err != nil {
- log.WithFields(log.Fields{
- "Topic": "grpc",
- "Key": host,
- "Error": err,
- }).Warn("listen failed")
- return
- }
- err = s.grpcServer.Serve(lis)
- log.WithFields(log.Fields{
- "Topic": "grpc",
- "Key": host,
- "Error": err,
- }).Warn("accept failed")
- }
-
- for _, host := range l {
- go serve(host)
- }
- wg.Wait()
- return nil
-}
-
-func NewMpGracefulRestartFromConfigStruct(c *config.MpGracefulRestart) *api.MpGracefulRestart {
- return &api.MpGracefulRestart{
- Config: &api.MpGracefulRestartConfig{
- Enabled: c.Config.Enabled,
- },
- }
-}
-
-func extractFamilyFromConfigAfiSafi(c *config.AfiSafi) uint32 {
- if c == nil {
- return 0
- }
- // If address family value is already stored in AfiSafiState structure,
- // we prefer to use this value.
- if c.State.Family != 0 {
- return uint32(c.State.Family)
- }
- // In case that Neighbor structure came from CLI or gRPC, address family
- // value in AfiSafiState structure can be omitted.
- // Here extracts value from AfiSafiName field in AfiSafiConfig structure.
- if rf, err := bgp.GetRouteFamily(string(c.Config.AfiSafiName)); err == nil {
- return uint32(rf)
- }
- // Ignores invalid address family name
- return 0
-}
-
-func NewAfiSafiConfigFromConfigStruct(c *config.AfiSafi) *api.AfiSafiConfig {
- return &api.AfiSafiConfig{
- Family: extractFamilyFromConfigAfiSafi(c),
- Enabled: c.Config.Enabled,
- }
-}
-
-func NewApplyPolicyFromConfigStruct(c *config.ApplyPolicy) *api.ApplyPolicy {
- applyPolicy := &api.ApplyPolicy{
- ImportPolicy: &api.PolicyAssignment{
- Type: api.PolicyType_IMPORT,
- Default: api.RouteAction(c.Config.DefaultImportPolicy.ToInt()),
- },
- ExportPolicy: &api.PolicyAssignment{
- Type: api.PolicyType_EXPORT,
- Default: api.RouteAction(c.Config.DefaultExportPolicy.ToInt()),
- },
- InPolicy: &api.PolicyAssignment{
- Type: api.PolicyType_IN,
- Default: api.RouteAction(c.Config.DefaultInPolicy.ToInt()),
- },
- }
-
- for _, pname := range c.Config.ImportPolicyList {
- applyPolicy.ImportPolicy.Policies = append(applyPolicy.ImportPolicy.Policies, &api.Policy{Name: pname})
- }
- for _, pname := range c.Config.ExportPolicyList {
- applyPolicy.ExportPolicy.Policies = append(applyPolicy.ExportPolicy.Policies, &api.Policy{Name: pname})
- }
- for _, pname := range c.Config.InPolicyList {
- applyPolicy.InPolicy.Policies = append(applyPolicy.InPolicy.Policies, &api.Policy{Name: pname})
- }
-
- return applyPolicy
-}
-
-func NewRouteSelectionOptionsFromConfigStruct(c *config.RouteSelectionOptions) *api.RouteSelectionOptions {
- return &api.RouteSelectionOptions{
- Config: &api.RouteSelectionOptionsConfig{
- AlwaysCompareMed: c.Config.AlwaysCompareMed,
- IgnoreAsPathLength: c.Config.IgnoreAsPathLength,
- ExternalCompareRouterId: c.Config.ExternalCompareRouterId,
- AdvertiseInactiveRoutes: c.Config.AdvertiseInactiveRoutes,
- EnableAigp: c.Config.EnableAigp,
- IgnoreNextHopIgpMetric: c.Config.IgnoreNextHopIgpMetric,
- },
- }
-}
-
-func NewUseMultiplePathsFromConfigStruct(c *config.UseMultiplePaths) *api.UseMultiplePaths {
- return &api.UseMultiplePaths{
- Config: &api.UseMultiplePathsConfig{
- Enabled: c.Config.Enabled,
- },
- Ebgp: &api.Ebgp{
- Config: &api.EbgpConfig{
- AllowMultipleAs: c.Ebgp.Config.AllowMultipleAs,
- MaximumPaths: c.Ebgp.Config.MaximumPaths,
- },
- },
- Ibgp: &api.Ibgp{
- Config: &api.IbgpConfig{
- MaximumPaths: c.Ibgp.Config.MaximumPaths,
- },
- },
- }
-}
-
-func NewPrefixLimitFromConfigStruct(c *config.AfiSafi) *api.PrefixLimit {
- if c.PrefixLimit.Config.MaxPrefixes == 0 {
- return nil
- }
-
- return &api.PrefixLimit{
- Family: uint32(c.State.Family),
- MaxPrefixes: c.PrefixLimit.Config.MaxPrefixes,
- ShutdownThresholdPct: uint32(c.PrefixLimit.Config.ShutdownThresholdPct),
- }
-}
-
-func NewRouteTargetMembershipFromConfigStruct(c *config.RouteTargetMembership) *api.RouteTargetMembership {
- return &api.RouteTargetMembership{
- Config: &api.RouteTargetMembershipConfig{
- DeferralTime: uint32(c.Config.DeferralTime),
- },
- }
-}
-
-func NewLongLivedGracefulRestartFromConfigStruct(c *config.LongLivedGracefulRestart) *api.LongLivedGracefulRestart {
- return &api.LongLivedGracefulRestart{
- Config: &api.LongLivedGracefulRestartConfig{
- Enabled: c.Config.Enabled,
- RestartTime: c.Config.RestartTime,
- },
- }
-}
-
-func NewAddPathsFromConfigStruct(c *config.AddPaths) *api.AddPaths {
- return &api.AddPaths{
- Config: &api.AddPathsConfig{
- Receive: c.Config.Receive,
- SendMax: uint32(c.Config.SendMax),
- },
- }
-}
-
-func NewAfiSafiFromConfigStruct(c *config.AfiSafi) *api.AfiSafi {
- return &api.AfiSafi{
- MpGracefulRestart: NewMpGracefulRestartFromConfigStruct(&c.MpGracefulRestart),
- Config: NewAfiSafiConfigFromConfigStruct(c),
- ApplyPolicy: NewApplyPolicyFromConfigStruct(&c.ApplyPolicy),
- RouteSelectionOptions: NewRouteSelectionOptionsFromConfigStruct(&c.RouteSelectionOptions),
- UseMultiplePaths: NewUseMultiplePathsFromConfigStruct(&c.UseMultiplePaths),
- PrefixLimits: NewPrefixLimitFromConfigStruct(c),
- RouteTargetMembership: NewRouteTargetMembershipFromConfigStruct(&c.RouteTargetMembership),
- LongLivedGracefulRestart: NewLongLivedGracefulRestartFromConfigStruct(&c.LongLivedGracefulRestart),
- AddPaths: NewAddPathsFromConfigStruct(&c.AddPaths),
- }
-}
-
-func NewPeerFromConfigStruct(pconf *config.Neighbor) *api.Peer {
- families := make([]uint32, 0, len(pconf.AfiSafis))
- prefixLimits := make([]*api.PrefixLimit, 0, len(pconf.AfiSafis))
- afiSafis := make([]*api.AfiSafi, 0, len(pconf.AfiSafis))
- for _, f := range pconf.AfiSafis {
- families = append(families, extractFamilyFromConfigAfiSafi(&f))
- if prefixLimit := NewPrefixLimitFromConfigStruct(&f); prefixLimit != nil {
- prefixLimits = append(prefixLimits, prefixLimit)
- }
- if afiSafi := NewAfiSafiFromConfigStruct(&f); afiSafi != nil {
- afiSafis = append(afiSafis, afiSafi)
- }
- }
-
- timer := pconf.Timers
- s := pconf.State
- localAddress := pconf.Transport.Config.LocalAddress
- if pconf.Transport.State.LocalAddress != "" {
- localAddress = pconf.Transport.State.LocalAddress
- }
- remoteCap, err := api.MarshalCapabilities(pconf.State.RemoteCapabilityList)
- if err != nil {
- return nil
- }
- localCap, err := api.MarshalCapabilities(pconf.State.LocalCapabilityList)
- if err != nil {
- return nil
- }
- var removePrivateAs api.PeerConf_RemovePrivateAs
- switch pconf.Config.RemovePrivateAs {
- case config.REMOVE_PRIVATE_AS_OPTION_ALL:
- removePrivateAs = api.PeerConf_ALL
- case config.REMOVE_PRIVATE_AS_OPTION_REPLACE:
- removePrivateAs = api.PeerConf_REPLACE
- }
- return &api.Peer{
- Families: families,
- ApplyPolicy: NewApplyPolicyFromConfigStruct(&pconf.ApplyPolicy),
- Conf: &api.PeerConf{
- NeighborAddress: pconf.Config.NeighborAddress,
- Id: s.RemoteRouterId,
- PeerAs: pconf.Config.PeerAs,
- LocalAs: pconf.Config.LocalAs,
- PeerType: uint32(pconf.Config.PeerType.ToInt()),
- AuthPassword: pconf.Config.AuthPassword,
- RouteFlapDamping: pconf.Config.RouteFlapDamping,
- Description: pconf.Config.Description,
- PeerGroup: pconf.Config.PeerGroup,
- RemoteCap: remoteCap,
- LocalCap: localCap,
- PrefixLimits: prefixLimits,
- LocalAddress: localAddress,
- NeighborInterface: pconf.Config.NeighborInterface,
- Vrf: pconf.Config.Vrf,
- AllowOwnAs: uint32(pconf.AsPathOptions.Config.AllowOwnAs),
- RemovePrivateAs: removePrivateAs,
- ReplacePeerAs: pconf.AsPathOptions.Config.ReplacePeerAs,
- },
- Info: &api.PeerState{
- BgpState: string(s.SessionState),
- AdminState: api.PeerState_AdminState(s.AdminState.ToInt()),
- Messages: &api.Messages{
- Received: &api.Message{
- NOTIFICATION: s.Messages.Received.Notification,
- UPDATE: s.Messages.Received.Update,
- OPEN: s.Messages.Received.Open,
- KEEPALIVE: s.Messages.Received.Keepalive,
- REFRESH: s.Messages.Received.Refresh,
- DISCARDED: s.Messages.Received.Discarded,
- TOTAL: s.Messages.Received.Total,
- },
- Sent: &api.Message{
- NOTIFICATION: s.Messages.Sent.Notification,
- UPDATE: s.Messages.Sent.Update,
- OPEN: s.Messages.Sent.Open,
- KEEPALIVE: s.Messages.Sent.Keepalive,
- REFRESH: s.Messages.Sent.Refresh,
- DISCARDED: s.Messages.Sent.Discarded,
- 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()),
- NeighborAddress: pconf.State.NeighborAddress,
- },
- Timers: &api.Timers{
- Config: &api.TimersConfig{
- ConnectRetry: uint64(timer.Config.ConnectRetry),
- HoldTime: uint64(timer.Config.HoldTime),
- KeepaliveInterval: uint64(timer.Config.KeepaliveInterval),
- },
- State: &api.TimersState{
- KeepaliveInterval: uint64(timer.State.KeepaliveInterval),
- NegotiatedHoldTime: uint64(timer.State.NegotiatedHoldTime),
- Uptime: uint64(timer.State.Uptime),
- Downtime: uint64(timer.State.Downtime),
- },
- },
- RouteReflector: &api.RouteReflector{
- RouteReflectorClient: pconf.RouteReflector.Config.RouteReflectorClient,
- RouteReflectorClusterId: string(pconf.RouteReflector.State.RouteReflectorClusterId),
- },
- RouteServer: &api.RouteServer{
- RouteServerClient: pconf.RouteServer.Config.RouteServerClient,
- },
- GracefulRestart: &api.GracefulRestart{
- Enabled: pconf.GracefulRestart.Config.Enabled,
- RestartTime: uint32(pconf.GracefulRestart.Config.RestartTime),
- HelperOnly: pconf.GracefulRestart.Config.HelperOnly,
- DeferralTime: uint32(pconf.GracefulRestart.Config.DeferralTime),
- NotificationEnabled: pconf.GracefulRestart.Config.NotificationEnabled,
- LonglivedEnabled: pconf.GracefulRestart.Config.LongLivedEnabled,
- LocalRestarting: pconf.GracefulRestart.State.LocalRestarting,
- },
- Transport: &api.Transport{
- RemotePort: uint32(pconf.Transport.Config.RemotePort),
- LocalAddress: pconf.Transport.Config.LocalAddress,
- PassiveMode: pconf.Transport.Config.PassiveMode,
- },
- AfiSafis: afiSafis,
- AddPaths: NewAddPathsFromConfigStruct(&pconf.AddPaths),
- }
-}
-
-func NewPeerGroupFromConfigStruct(pconf *config.PeerGroup) *api.PeerGroup {
- families := make([]uint32, 0, len(pconf.AfiSafis))
- afiSafis := make([]*api.AfiSafi, 0, len(pconf.AfiSafis))
- for _, f := range pconf.AfiSafis {
- families = append(families, extractFamilyFromConfigAfiSafi(&f))
- if afiSafi := NewAfiSafiFromConfigStruct(&f); afiSafi != nil {
- afiSafis = append(afiSafis, afiSafi)
- }
- }
-
- timer := pconf.Timers
- s := pconf.State
- return &api.PeerGroup{
- Families: families,
- ApplyPolicy: NewApplyPolicyFromConfigStruct(&pconf.ApplyPolicy),
- Conf: &api.PeerGroupConf{
- PeerAs: pconf.Config.PeerAs,
- LocalAs: pconf.Config.LocalAs,
- PeerType: uint32(pconf.Config.PeerType.ToInt()),
- AuthPassword: pconf.Config.AuthPassword,
- RouteFlapDamping: pconf.Config.RouteFlapDamping,
- Description: pconf.Config.Description,
- PeerGroupName: pconf.Config.PeerGroupName,
- },
- Info: &api.PeerGroupState{
- PeerAs: s.PeerAs,
- PeerType: uint32(s.PeerType.ToInt()),
- TotalPaths: s.TotalPaths,
- TotalPrefixes: s.TotalPrefixes,
- },
- Timers: &api.Timers{
- Config: &api.TimersConfig{
- ConnectRetry: uint64(timer.Config.ConnectRetry),
- HoldTime: uint64(timer.Config.HoldTime),
- KeepaliveInterval: uint64(timer.Config.KeepaliveInterval),
- },
- State: &api.TimersState{
- KeepaliveInterval: uint64(timer.State.KeepaliveInterval),
- NegotiatedHoldTime: uint64(timer.State.NegotiatedHoldTime),
- Uptime: uint64(timer.State.Uptime),
- Downtime: uint64(timer.State.Downtime),
- },
- },
- RouteReflector: &api.RouteReflector{
- RouteReflectorClient: pconf.RouteReflector.Config.RouteReflectorClient,
- RouteReflectorClusterId: string(pconf.RouteReflector.Config.RouteReflectorClusterId),
- },
- RouteServer: &api.RouteServer{
- RouteServerClient: pconf.RouteServer.Config.RouteServerClient,
- },
- GracefulRestart: &api.GracefulRestart{
- Enabled: pconf.GracefulRestart.Config.Enabled,
- RestartTime: uint32(pconf.GracefulRestart.Config.RestartTime),
- HelperOnly: pconf.GracefulRestart.Config.HelperOnly,
- DeferralTime: uint32(pconf.GracefulRestart.Config.DeferralTime),
- NotificationEnabled: pconf.GracefulRestart.Config.NotificationEnabled,
- LonglivedEnabled: pconf.GracefulRestart.Config.LongLivedEnabled,
- LocalRestarting: pconf.GracefulRestart.State.LocalRestarting,
- },
- Transport: &api.Transport{
- RemotePort: uint32(pconf.Transport.Config.RemotePort),
- LocalAddress: pconf.Transport.Config.LocalAddress,
- PassiveMode: pconf.Transport.Config.PassiveMode,
- },
- AfiSafis: afiSafis,
- AddPaths: NewAddPathsFromConfigStruct(&pconf.AddPaths),
- }
-}
-
-func (s *Server) GetNeighbor(ctx context.Context, arg *api.GetNeighborRequest) (*api.GetNeighborResponse, error) {
- if arg == nil {
- return nil, fmt.Errorf("invalid request")
- }
- neighbors := s.bgpServer.GetNeighbor(arg.Address, arg.EnableAdvertised)
- peers := make([]*api.Peer, 0, len(neighbors))
- for _, e := range neighbors {
- peers = append(peers, NewPeerFromConfigStruct(e))
- }
- return &api.GetNeighborResponse{Peers: peers}, nil
-}
-
-func NewValidationFromTableStruct(v *table.Validation) *api.RPKIValidation {
- if v == nil {
- return &api.RPKIValidation{}
- }
- return &api.RPKIValidation{
- Reason: api.RPKIValidation_Reason(v.Reason.ToInt()),
- Matched: NewRoaListFromTableStructList(v.Matched),
- UnmatchedAs: NewRoaListFromTableStructList(v.UnmatchedAs),
- UnmatchedLength: NewRoaListFromTableStructList(v.UnmatchedLength),
- }
-}
-
-func toPathAPI(binNlri []byte, binPattrs [][]byte, anyNlri *any.Any, anyPattrs []*any.Any, path *table.Path, v *table.Validation) *api.Path {
- nlri := path.GetNlri()
- family := uint32(path.GetRouteFamily())
- vv := config.RPKI_VALIDATION_RESULT_TYPE_NONE.ToInt()
- if v != nil {
- vv = v.Status.ToInt()
- }
- p := &api.Path{
- Nlri: binNlri,
- Pattrs: binPattrs,
- Age: path.GetTimestamp().Unix(),
- IsWithdraw: path.IsWithdraw,
- Validation: int32(vv),
- ValidationDetail: NewValidationFromTableStruct(v),
- Family: family,
- Stale: path.IsStale(),
- IsFromExternal: path.IsFromExternal(),
- NoImplicitWithdraw: path.NoImplicitWithdraw(),
- IsNexthopInvalid: path.IsNexthopInvalid,
- Identifier: nlri.PathIdentifier(),
- LocalIdentifier: nlri.PathLocalIdentifier(),
- AnyNlri: anyNlri,
- AnyPattrs: anyPattrs,
- }
- if s := path.GetSource(); s != nil {
- p.SourceAsn = s.AS
- p.SourceId = s.ID.String()
- p.NeighborIp = s.Address.String()
- }
- return p
-}
-
-func ToPathApi(path *table.Path, v *table.Validation) *api.Path {
- nlri := path.GetNlri()
- anyNlri := api.MarshalNLRI(nlri)
- if path.IsWithdraw {
- return toPathAPI(nil, nil, anyNlri, nil, path, v)
- }
- anyPattrs := api.MarshalPathAttributes(path.GetPathAttrs())
- return toPathAPI(nil, nil, anyNlri, anyPattrs, path, v)
-}
-
-func getValidation(v []*table.Validation, i int) *table.Validation {
- if v == nil {
- return nil
- } else {
- return v[i]
- }
-}
-
-func (s *Server) GetRib(ctx context.Context, arg *api.GetRibRequest) (*api.GetRibResponse, error) {
- if arg == nil || arg.Table == nil {
- return nil, fmt.Errorf("invalid request")
- }
- f := func() []*table.LookupPrefix {
- l := make([]*table.LookupPrefix, 0, len(arg.Table.Destinations))
- for _, p := range arg.Table.Destinations {
- l = append(l, &table.LookupPrefix{
- Prefix: p.Prefix,
- LookupOption: func() table.LookupOption {
- if p.LongerPrefixes {
- return table.LOOKUP_LONGER
- } else if p.ShorterPrefixes {
- return table.LOOKUP_SHORTER
- }
- return table.LOOKUP_EXACT
- }(),
- })
- }
- return l
- }
-
- var in bool
- var err error
- var tbl *table.Table
- var v []*table.Validation
-
- family := bgp.RouteFamily(arg.Table.Family)
- switch arg.Table.Type {
- case api.Resource_LOCAL, api.Resource_GLOBAL:
- tbl, v, err = s.bgpServer.GetRib(arg.Table.Name, family, f())
- case api.Resource_ADJ_IN:
- in = true
- fallthrough
- case api.Resource_ADJ_OUT:
- tbl, v, err = s.bgpServer.GetAdjRib(arg.Table.Name, family, in, f())
- case api.Resource_VRF:
- tbl, err = s.bgpServer.GetVrfRib(arg.Table.Name, family, []*table.LookupPrefix{})
- default:
- return nil, fmt.Errorf("unsupported resource type: %v", arg.Table.Type)
- }
-
- if err != nil {
- return nil, err
- }
-
- tblDsts := tbl.GetDestinations()
- dsts := make([]*api.Destination, 0, len(tblDsts))
- idx := 0
- for _, dst := range tblDsts {
- dsts = append(dsts, &api.Destination{
- Prefix: dst.GetNlri().String(),
- Paths: func(paths []*table.Path) []*api.Path {
- l := make([]*api.Path, 0, len(paths))
- for i, p := range paths {
- pp := ToPathApi(p, getValidation(v, idx))
- idx++
- switch arg.Table.Type {
- case api.Resource_LOCAL, api.Resource_GLOBAL:
- if i == 0 && !table.SelectionOptions.DisableBestPathSelection {
- pp.Best = true
- }
- }
- l = append(l, pp)
- }
- return l
- }(dst.GetAllKnownPathList()),
- })
- }
-
- return &api.GetRibResponse{Table: &api.Table{
- Type: arg.Table.Type,
- Family: uint32(tbl.GetRoutefamily()),
- Destinations: dsts},
- }, err
-}
-
-func (s *Server) GetPath(arg *api.GetPathRequest, stream api.GobgpApi_GetPathServer) error {
- f := func() []*table.LookupPrefix {
- l := make([]*table.LookupPrefix, 0, len(arg.Prefixes))
- for _, p := range arg.Prefixes {
- l = append(l, &table.LookupPrefix{
- Prefix: p.Prefix,
- LookupOption: table.LookupOption(p.LookupOption),
- })
- }
- return l
- }
-
- in := false
- family := bgp.RouteFamily(arg.Family)
- var tbl *table.Table
- var err error
- var v []*table.Validation
- switch arg.Type {
- case api.Resource_LOCAL, api.Resource_GLOBAL:
- tbl, v, err = s.bgpServer.GetRib(arg.Name, family, f())
- case api.Resource_ADJ_IN:
- in = true
- fallthrough
- case api.Resource_ADJ_OUT:
- tbl, v, err = s.bgpServer.GetAdjRib(arg.Name, family, in, f())
- case api.Resource_VRF:
- tbl, err = s.bgpServer.GetVrfRib(arg.Name, family, []*table.LookupPrefix{})
- default:
- return fmt.Errorf("unsupported resource type: %v", arg.Type)
- }
- if err != nil {
- return err
- }
-
- idx := 0
- return func() error {
- for _, dst := range tbl.GetDestinations() {
- for i, path := range dst.GetAllKnownPathList() {
- p := ToPathApi(path, getValidation(v, idx))
- idx++
- if i == 0 && !table.SelectionOptions.DisableBestPathSelection {
- switch arg.Type {
- case api.Resource_LOCAL, api.Resource_GLOBAL:
- p.Best = true
- }
- }
- if err := stream.Send(p); err != nil {
- return err
- }
- }
- }
- return nil
- }()
-}
-
-func (s *Server) MonitorRib(arg *api.MonitorRibRequest, stream api.GobgpApi_MonitorRibServer) error {
- if arg == nil || arg.Table == nil {
- return fmt.Errorf("invalid request")
- }
- t := arg.Table
- w, err := func() (*Watcher, error) {
- switch t.Type {
- case api.Resource_GLOBAL:
- return s.bgpServer.Watch(WatchBestPath(arg.Current)), nil
- case api.Resource_ADJ_IN:
- if t.PostPolicy {
- return s.bgpServer.Watch(WatchPostUpdate(arg.Current)), nil
- }
- return s.bgpServer.Watch(WatchUpdate(arg.Current)), nil
- default:
- return nil, fmt.Errorf("unsupported resource type: %v", t.Type)
- }
- }()
- if err != nil {
- return nil
- }
-
- return func() error {
- defer func() { w.Stop() }()
-
- sendPath := func(pathList []*table.Path) error {
- dsts := make(map[string]*api.Destination)
- for _, path := range pathList {
- if path == nil || (t.Family != 0 && bgp.RouteFamily(t.Family) != path.GetRouteFamily()) {
- continue
- }
- if dst, y := dsts[path.GetNlri().String()]; y {
- dst.Paths = append(dst.Paths, ToPathApi(path, nil))
- } else {
- dsts[path.GetNlri().String()] = &api.Destination{
- Prefix: path.GetNlri().String(),
- Paths: []*api.Path{ToPathApi(path, nil)},
- }
- }
- }
- for _, dst := range dsts {
- if err := stream.Send(dst); err != nil {
- return err
- }
- }
- return nil
- }
-
- for ev := range w.Event() {
- switch msg := ev.(type) {
- case *WatchEventBestPath:
- if err := sendPath(func() []*table.Path {
- if len(msg.MultiPathList) > 0 {
- l := make([]*table.Path, 0)
- for _, p := range msg.MultiPathList {
- l = append(l, p...)
- }
- return l
- } else {
- return msg.PathList
- }
- }()); err != nil {
- return err
- }
- case *WatchEventUpdate:
- if err := sendPath(msg.PathList); err != nil {
- return err
- }
- }
- }
- return nil
- }()
-}
-
-func (s *Server) MonitorPeerState(arg *api.Arguments, stream api.GobgpApi_MonitorPeerStateServer) error {
- if arg == nil {
- return fmt.Errorf("invalid request")
- }
- return func() error {
- w := s.bgpServer.Watch(WatchPeerState(arg.Current))
- defer func() { w.Stop() }()
-
- for ev := range w.Event() {
- switch msg := ev.(type) {
- case *WatchEventPeerState:
- if len(arg.Name) > 0 && arg.Name != msg.PeerAddress.String() && arg.Name != msg.PeerInterface {
- continue
- }
- if err := stream.Send(&api.Peer{
- Conf: &api.PeerConf{
- PeerAs: msg.PeerAS,
- LocalAs: msg.LocalAS,
- NeighborAddress: msg.PeerAddress.String(),
- Id: msg.PeerID.String(),
- NeighborInterface: msg.PeerInterface,
- },
- Info: &api.PeerState{
- PeerAs: msg.PeerAS,
- LocalAs: msg.LocalAS,
- NeighborAddress: msg.PeerAddress.String(),
- BgpState: msg.State.String(),
- AdminState: api.PeerState_AdminState(msg.AdminState),
- },
- Transport: &api.Transport{
- LocalAddress: msg.LocalAddress.String(),
- LocalPort: uint32(msg.LocalPort),
- RemotePort: uint32(msg.PeerPort),
- },
- }); err != nil {
- return err
- }
- }
- }
- return nil
- }()
-}
-
-func (s *Server) ResetNeighbor(ctx context.Context, arg *api.ResetNeighborRequest) (*api.ResetNeighborResponse, error) {
- return &api.ResetNeighborResponse{}, s.bgpServer.ResetNeighbor(arg.Address, arg.Communication)
-}
-
-func (s *Server) SoftResetNeighbor(ctx context.Context, arg *api.SoftResetNeighborRequest) (*api.SoftResetNeighborResponse, error) {
- var err error
- addr := arg.Address
- if addr == "all" {
- addr = ""
- }
- family := bgp.RouteFamily(0)
- switch arg.Direction {
- case api.SoftResetNeighborRequest_IN:
- err = s.bgpServer.SoftResetIn(addr, family)
- case api.SoftResetNeighborRequest_OUT:
- err = s.bgpServer.SoftResetOut(addr, family)
- default:
- err = s.bgpServer.SoftReset(addr, family)
- }
- return &api.SoftResetNeighborResponse{}, err
-}
-
-func (s *Server) ShutdownNeighbor(ctx context.Context, arg *api.ShutdownNeighborRequest) (*api.ShutdownNeighborResponse, error) {
- return &api.ShutdownNeighborResponse{}, s.bgpServer.ShutdownNeighbor(arg.Address, arg.Communication)
-}
-
-func (s *Server) EnableNeighbor(ctx context.Context, arg *api.EnableNeighborRequest) (*api.EnableNeighborResponse, error) {
- return &api.EnableNeighborResponse{}, s.bgpServer.EnableNeighbor(arg.Address)
-}
-
-func (s *Server) DisableNeighbor(ctx context.Context, arg *api.DisableNeighborRequest) (*api.DisableNeighborResponse, error) {
- return &api.DisableNeighborResponse{}, s.bgpServer.DisableNeighbor(arg.Address, arg.Communication)
-}
-
-func (s *Server) UpdatePolicy(ctx context.Context, arg *api.UpdatePolicyRequest) (*api.UpdatePolicyResponse, error) {
- rp, err := NewRoutingPolicyFromApiStruct(arg)
- if err != nil {
- return nil, err
- }
- return &api.UpdatePolicyResponse{}, s.bgpServer.UpdatePolicy(*rp)
-}
-
-func NewAPIRoutingPolicyFromConfigStruct(c *config.RoutingPolicy) (*api.RoutingPolicy, error) {
- definedSets, err := NewAPIDefinedSetsFromConfigStruct(&c.DefinedSets)
- if err != nil {
- return nil, err
- }
- policies := make([]*api.Policy, 0, len(c.PolicyDefinitions))
- for _, policy := range c.PolicyDefinitions {
- policies = append(policies, toPolicyApi(&policy))
- }
-
- return &api.RoutingPolicy{
- DefinedSet: definedSets,
- PolicyDefinition: policies,
- }, nil
-}
-
-func NewRoutingPolicyFromApiStruct(arg *api.UpdatePolicyRequest) (*config.RoutingPolicy, error) {
- policyDefinitions := make([]config.PolicyDefinition, 0, len(arg.Policies))
- for _, p := range arg.Policies {
- pd, err := NewConfigPolicyFromApiStruct(p)
- if err != nil {
- return nil, err
- }
- policyDefinitions = append(policyDefinitions, *pd)
- }
-
- definedSets, err := NewConfigDefinedSetsFromApiStruct(arg.Sets)
- if err != nil {
- return nil, err
- }
-
- return &config.RoutingPolicy{
- DefinedSets: *definedSets,
- PolicyDefinitions: policyDefinitions,
- }, nil
-}
-
-func (s *Server) api2PathList(resource api.Resource, ApiPathList []*api.Path) ([]*table.Path, error) {
- var pi *table.PeerInfo
-
- pathList := make([]*table.Path, 0, len(ApiPathList))
- for _, path := range ApiPathList {
- var nlri bgp.AddrPrefixInterface
- var nexthop string
-
- if path.SourceAsn != 0 {
- pi = &table.PeerInfo{
- AS: path.SourceAsn,
- LocalID: net.ParseIP(path.SourceId),
- }
- }
-
- nlri, err := path.GetNativeNlri()
- if err != nil {
- return nil, err
- }
- nlri.SetPathIdentifier(path.Identifier)
-
- attrList, err := path.GetNativePathAttributes()
- if err != nil {
- return nil, err
- }
-
- pattrs := make([]bgp.PathAttributeInterface, 0)
- seen := make(map[bgp.BGPAttrType]struct{})
- for _, attr := range attrList {
- attrType := attr.GetType()
- if _, ok := seen[attrType]; !ok {
- seen[attrType] = struct{}{}
- } else {
- return nil, fmt.Errorf("duplicated path attribute type: %d", attrType)
- }
-
- switch a := attr.(type) {
- case *bgp.PathAttributeNextHop:
- nexthop = a.Value.String()
- case *bgp.PathAttributeMpReachNLRI:
- nlri = a.Value[0]
- nexthop = a.Nexthop.String()
- default:
- pattrs = append(pattrs, attr)
- }
- }
-
- if nlri == nil {
- return nil, fmt.Errorf("nlri not found")
- } else if !path.IsWithdraw && nexthop == "" {
- return nil, fmt.Errorf("nexthop not found")
- }
-
- if resource != api.Resource_VRF && bgp.RouteFamily(path.Family) == bgp.RF_IPv4_UC && net.ParseIP(nexthop).To4() != nil {
- pattrs = append(pattrs, bgp.NewPathAttributeNextHop(nexthop))
- } else {
- pattrs = append(pattrs, bgp.NewPathAttributeMpReachNLRI(nexthop, []bgp.AddrPrefixInterface{nlri}))
- }
-
- newPath := table.NewPath(pi, nlri, path.IsWithdraw, pattrs, time.Now(), path.NoImplicitWithdraw)
- if !path.IsWithdraw {
- total := bytes.NewBuffer(make([]byte, 0))
- for _, a := range newPath.GetPathAttrs() {
- if a.GetType() == bgp.BGP_ATTR_TYPE_MP_REACH_NLRI {
- continue
- }
- b, _ := a.Serialize()
- total.Write(b)
- }
- newPath.SetHash(farm.Hash32(total.Bytes()))
- }
- newPath.SetIsFromExternal(path.IsFromExternal)
- pathList = append(pathList, newPath)
- }
- return pathList, nil
-}
-
-func (s *Server) AddPath(ctx context.Context, arg *api.AddPathRequest) (*api.AddPathResponse, error) {
- pathList, err := s.api2PathList(arg.Resource, []*api.Path{arg.Path})
- var uuid []byte
- if err == nil {
- uuid, err = s.bgpServer.AddPath(arg.VrfId, pathList)
- }
- return &api.AddPathResponse{Uuid: uuid}, err
-}
-
-func (s *Server) DeletePath(ctx context.Context, arg *api.DeletePathRequest) (*api.DeletePathResponse, error) {
- pathList, err := func() ([]*table.Path, error) {
- if arg.Path != nil {
- arg.Path.IsWithdraw = true
- return s.api2PathList(arg.Resource, []*api.Path{arg.Path})
- }
- return []*table.Path{}, nil
- }()
- if err != nil {
- return nil, err
- }
- return &api.DeletePathResponse{}, s.bgpServer.DeletePath(arg.Uuid, bgp.RouteFamily(arg.Family), arg.VrfId, pathList)
-}
-
-func (s *Server) EnableMrt(ctx context.Context, arg *api.EnableMrtRequest) (*api.EnableMrtResponse, error) {
- return &api.EnableMrtResponse{}, s.bgpServer.EnableMrt(&config.MrtConfig{
- RotationInterval: arg.Interval,
- DumpType: config.IntToMrtTypeMap[int(arg.DumpType)],
- FileName: arg.Filename,
- })
-}
-
-func (s *Server) DisableMrt(ctx context.Context, arg *api.DisableMrtRequest) (*api.DisableMrtResponse, error) {
- return &api.DisableMrtResponse{}, s.bgpServer.DisableMrt(&config.MrtConfig{})
-}
-
-func (s *Server) InjectMrt(stream api.GobgpApi_InjectMrtServer) error {
- for {
- arg, err := stream.Recv()
-
- if err == io.EOF {
- break
- } else if err != nil {
- return err
- }
-
- if arg.Resource != api.Resource_GLOBAL && arg.Resource != api.Resource_VRF {
- return fmt.Errorf("unsupported resource: %s", arg.Resource)
- }
-
- if pathList, err := s.api2PathList(arg.Resource, arg.Paths); err != nil {
- return err
- } else {
- if _, err = s.bgpServer.AddPath("", pathList); err != nil {
- return err
- }
- }
- }
- return stream.SendAndClose(&api.InjectMrtResponse{})
-}
-
-func (s *Server) AddBmp(ctx context.Context, arg *api.AddBmpRequest) (*api.AddBmpResponse, error) {
- t, ok := config.IntToBmpRouteMonitoringPolicyTypeMap[int(arg.Type)]
- if !ok {
- return nil, fmt.Errorf("invalid bmp route monitoring policy: %d", arg.Type)
- }
- return &api.AddBmpResponse{}, s.bgpServer.AddBmp(&config.BmpServerConfig{
- Address: arg.Address,
- Port: arg.Port,
- RouteMonitoringPolicy: t,
- })
-}
-
-func (s *Server) DeleteBmp(ctx context.Context, arg *api.DeleteBmpRequest) (*api.DeleteBmpResponse, error) {
- return &api.DeleteBmpResponse{}, s.bgpServer.DeleteBmp(&config.BmpServerConfig{
- Address: arg.Address,
- Port: arg.Port,
- })
-}
-
-func (s *Server) ValidateRib(ctx context.Context, arg *api.ValidateRibRequest) (*api.ValidateRibResponse, error) {
- return &api.ValidateRibResponse{}, nil
-}
-
-func (s *Server) AddRpki(ctx context.Context, arg *api.AddRpkiRequest) (*api.AddRpkiResponse, error) {
- return &api.AddRpkiResponse{}, s.bgpServer.AddRpki(&config.RpkiServerConfig{
- Address: arg.Address,
- Port: arg.Port,
- RecordLifetime: arg.Lifetime,
- })
-}
-
-func (s *Server) DeleteRpki(ctx context.Context, arg *api.DeleteRpkiRequest) (*api.DeleteRpkiResponse, error) {
- return &api.DeleteRpkiResponse{}, s.bgpServer.DeleteRpki(&config.RpkiServerConfig{
- Address: arg.Address,
- Port: arg.Port,
- })
-}
-
-func (s *Server) EnableRpki(ctx context.Context, arg *api.EnableRpkiRequest) (*api.EnableRpkiResponse, error) {
- return &api.EnableRpkiResponse{}, s.bgpServer.EnableRpki(&config.RpkiServerConfig{
- Address: arg.Address,
- })
-}
-
-func (s *Server) DisableRpki(ctx context.Context, arg *api.DisableRpkiRequest) (*api.DisableRpkiResponse, error) {
- return &api.DisableRpkiResponse{}, s.bgpServer.DisableRpki(&config.RpkiServerConfig{
- Address: arg.Address,
- })
-}
-
-func (s *Server) ResetRpki(ctx context.Context, arg *api.ResetRpkiRequest) (*api.ResetRpkiResponse, error) {
- return &api.ResetRpkiResponse{}, s.bgpServer.ResetRpki(&config.RpkiServerConfig{
- Address: arg.Address,
- })
-}
-
-func (s *Server) SoftResetRpki(ctx context.Context, arg *api.SoftResetRpkiRequest) (*api.SoftResetRpkiResponse, error) {
- return &api.SoftResetRpkiResponse{}, s.bgpServer.SoftResetRpki(&config.RpkiServerConfig{
- Address: arg.Address,
- })
-}
-
-func (s *Server) GetRpki(ctx context.Context, arg *api.GetRpkiRequest) (*api.GetRpkiResponse, error) {
- servers, err := s.bgpServer.GetRpki()
- if err != nil {
- return nil, err
- }
- l := make([]*api.Rpki, 0, len(servers))
- for _, s := range servers {
- received := &s.State.RpkiMessages.RpkiReceived
- sent := &s.State.RpkiMessages.RpkiSent
- rpki := &api.Rpki{
- Conf: &api.RPKIConf{
- Address: s.Config.Address,
- RemotePort: strconv.Itoa(int(s.Config.Port)),
- },
- State: &api.RPKIState{
- Uptime: s.State.Uptime,
- Downtime: s.State.Downtime,
- Up: s.State.Up,
- RecordIpv4: s.State.RecordsV4,
- RecordIpv6: s.State.RecordsV6,
- PrefixIpv4: s.State.PrefixesV4,
- PrefixIpv6: s.State.PrefixesV6,
- Serial: s.State.SerialNumber,
- ReceivedIpv4: received.Ipv4Prefix,
- ReceivedIpv6: received.Ipv6Prefix,
- SerialNotify: received.SerialNotify,
- CacheReset: received.CacheReset,
- CacheResponse: received.CacheResponse,
- EndOfData: received.EndOfData,
- Error: received.Error,
- SerialQuery: sent.SerialQuery,
- ResetQuery: sent.ResetQuery,
- },
- }
- l = append(l, rpki)
- }
- return &api.GetRpkiResponse{Servers: l}, nil
-}
-
-func (s *Server) GetRoa(ctx context.Context, arg *api.GetRoaRequest) (*api.GetRoaResponse, error) {
- roas, err := s.bgpServer.GetRoa(bgp.RouteFamily(arg.Family))
- if err != nil {
- return nil, err
- }
- return &api.GetRoaResponse{Roas: NewRoaListFromTableStructList(roas)}, nil
-}
-
-func (s *Server) EnableZebra(ctx context.Context, arg *api.EnableZebraRequest) (*api.EnableZebraResponse, error) {
- for _, p := range arg.RouteTypes {
- if _, err := zebra.RouteTypeFromString(p); err != nil {
- return &api.EnableZebraResponse{}, err
- }
- }
- return &api.EnableZebraResponse{}, s.bgpServer.StartZebraClient(&config.ZebraConfig{
- Url: arg.Url,
- RedistributeRouteTypeList: arg.RouteTypes,
- Version: uint8(arg.Version),
- NexthopTriggerEnable: arg.NexthopTriggerEnable,
- NexthopTriggerDelay: uint8(arg.NexthopTriggerDelay),
- })
-}
-
-func (s *Server) GetVrf(ctx context.Context, arg *api.GetVrfRequest) (*api.GetVrfResponse, error) {
- toApi := func(v *table.Vrf) *api.Vrf {
- return &api.Vrf{
- Name: v.Name,
- Rd: api.MarshalRD(v.Rd),
- Id: v.Id,
- ImportRt: api.MarshalRTs(v.ImportRt),
- ExportRt: api.MarshalRTs(v.ExportRt),
- }
- }
- vrfs := s.bgpServer.GetVrf()
- l := make([]*api.Vrf, 0, len(vrfs))
- for _, v := range vrfs {
- l = append(l, toApi(v))
- }
- return &api.GetVrfResponse{Vrfs: l}, nil
-}
-
-func (s *Server) AddVrf(ctx context.Context, arg *api.AddVrfRequest) (r *api.AddVrfResponse, err error) {
- if arg == nil || arg.Vrf == nil {
- return nil, fmt.Errorf("invalid request")
- }
- rd, err := api.UnmarshalRD(arg.Vrf.Rd)
- if err != nil {
- return nil, err
- }
- im, err := api.UnmarshalRTs(arg.Vrf.ImportRt)
- if err != nil {
- return nil, err
- }
- ex, err := api.UnmarshalRTs(arg.Vrf.ExportRt)
- if err != nil {
- return nil, err
- }
- return &api.AddVrfResponse{}, s.bgpServer.AddVrf(arg.Vrf.Name, arg.Vrf.Id, rd, im, ex)
-}
-
-func (s *Server) DeleteVrf(ctx context.Context, arg *api.DeleteVrfRequest) (*api.DeleteVrfResponse, error) {
- if arg == nil || arg.Vrf == nil {
- return nil, fmt.Errorf("invalid request")
- }
- return &api.DeleteVrfResponse{}, s.bgpServer.DeleteVrf(arg.Vrf.Name)
-}
-
-func ReadMpGracefulRestartFromAPIStruct(c *config.MpGracefulRestart, a *api.MpGracefulRestart) {
- if c == nil || a == nil {
- return
- }
- if a.Config != nil {
- c.Config.Enabled = a.Config.Enabled
- }
-}
-
-func ReadAfiSafiConfigFromAPIStruct(c *config.AfiSafiConfig, a *api.AfiSafiConfig) {
- if c == nil || a == nil {
- return
- }
- c.AfiSafiName = config.AfiSafiType(bgp.RouteFamily(a.Family).String())
- c.Enabled = a.Enabled
-}
-
-func ReadAfiSafiStateFromAPIStruct(s *config.AfiSafiState, a *api.AfiSafiConfig) {
- if s == nil || a == nil {
- return
- }
- // Store only address family value for the convenience
- s.Family = bgp.RouteFamily(a.Family)
-}
-
-func ReadPrefixLimitFromAPIStruct(c *config.PrefixLimit, a *api.PrefixLimit) {
- if c == nil || a == nil {
- return
- }
- c.Config.MaxPrefixes = a.MaxPrefixes
- c.Config.ShutdownThresholdPct = config.Percentage(a.ShutdownThresholdPct)
-}
-
-func ReadApplyPolicyFromAPIStruct(c *config.ApplyPolicy, a *api.ApplyPolicy) {
- if c == nil || a == nil {
- return
- }
- if a.ImportPolicy != nil {
- c.Config.DefaultImportPolicy = config.IntToDefaultPolicyTypeMap[int(a.ImportPolicy.Default)]
- for _, p := range a.ImportPolicy.Policies {
- c.Config.ImportPolicyList = append(c.Config.ImportPolicyList, p.Name)
- }
- }
- if a.ExportPolicy != nil {
- c.Config.DefaultExportPolicy = config.IntToDefaultPolicyTypeMap[int(a.ExportPolicy.Default)]
- for _, p := range a.ExportPolicy.Policies {
- c.Config.ExportPolicyList = append(c.Config.ExportPolicyList, p.Name)
- }
- }
- if a.InPolicy != nil {
- c.Config.DefaultInPolicy = config.IntToDefaultPolicyTypeMap[int(a.InPolicy.Default)]
- for _, p := range a.InPolicy.Policies {
- c.Config.InPolicyList = append(c.Config.InPolicyList, p.Name)
- }
- }
-}
-
-func ReadRouteSelectionOptionsFromAPIStruct(c *config.RouteSelectionOptions, a *api.RouteSelectionOptions) {
- if c == nil || a == nil {
- return
- }
- if a.Config != nil {
- c.Config.AlwaysCompareMed = a.Config.AlwaysCompareMed
- c.Config.IgnoreAsPathLength = a.Config.IgnoreAsPathLength
- c.Config.ExternalCompareRouterId = a.Config.ExternalCompareRouterId
- c.Config.AdvertiseInactiveRoutes = a.Config.AdvertiseInactiveRoutes
- c.Config.EnableAigp = a.Config.EnableAigp
- c.Config.IgnoreNextHopIgpMetric = a.Config.IgnoreNextHopIgpMetric
- }
-}
-
-func ReadUseMultiplePathsFromAPIStruct(c *config.UseMultiplePaths, a *api.UseMultiplePaths) {
- if c == nil || a == nil {
- return
- }
- if a.Config != nil {
- c.Config.Enabled = a.Config.Enabled
- }
- if a.Ebgp != nil && a.Ebgp.Config != nil {
- c.Ebgp = config.Ebgp{
- Config: config.EbgpConfig{
- AllowMultipleAs: a.Ebgp.Config.AllowMultipleAs,
- MaximumPaths: a.Ebgp.Config.MaximumPaths,
- },
- }
- }
- if a.Ibgp != nil && a.Ibgp.Config != nil {
- c.Ibgp = config.Ibgp{
- Config: config.IbgpConfig{
- MaximumPaths: a.Ibgp.Config.MaximumPaths,
- },
- }
- }
-}
-
-func ReadRouteTargetMembershipFromAPIStruct(c *config.RouteTargetMembership, a *api.RouteTargetMembership) {
- if c == nil || a == nil {
- return
- }
- if a.Config != nil {
- c.Config.DeferralTime = uint16(a.Config.DeferralTime)
- }
-}
-
-func ReadLongLivedGracefulRestartFromAPIStruct(c *config.LongLivedGracefulRestart, a *api.LongLivedGracefulRestart) {
- if c == nil || a == nil {
- return
- }
- if a.Config != nil {
- c.Config.Enabled = a.Config.Enabled
- c.Config.RestartTime = a.Config.RestartTime
- }
-}
-
-func ReadAddPathsFromAPIStruct(c *config.AddPaths, a *api.AddPaths) {
- if c == nil || a == nil {
- return
- }
- if a.Config != nil {
- c.Config.Receive = a.Config.Receive
- c.Config.SendMax = uint8(a.Config.SendMax)
- }
-}
-
-func NewNeighborFromAPIStruct(a *api.Peer) (*config.Neighbor, error) {
- pconf := &config.Neighbor{}
- if a.Conf != nil {
- pconf.Config.PeerAs = a.Conf.PeerAs
- pconf.Config.LocalAs = a.Conf.LocalAs
- pconf.Config.AuthPassword = a.Conf.AuthPassword
- pconf.Config.RouteFlapDamping = a.Conf.RouteFlapDamping
- pconf.Config.Description = a.Conf.Description
- pconf.Config.PeerGroup = a.Conf.PeerGroup
- pconf.Config.PeerType = config.IntToPeerTypeMap[int(a.Conf.PeerType)]
- pconf.Config.NeighborAddress = a.Conf.NeighborAddress
- pconf.Config.NeighborInterface = a.Conf.NeighborInterface
- pconf.Config.Vrf = a.Conf.Vrf
- pconf.AsPathOptions.Config.AllowOwnAs = uint8(a.Conf.AllowOwnAs)
- pconf.AsPathOptions.Config.ReplacePeerAs = a.Conf.ReplacePeerAs
-
- switch a.Conf.RemovePrivateAs {
- case api.PeerConf_ALL:
- pconf.Config.RemovePrivateAs = config.REMOVE_PRIVATE_AS_OPTION_ALL
- case api.PeerConf_REPLACE:
- pconf.Config.RemovePrivateAs = config.REMOVE_PRIVATE_AS_OPTION_REPLACE
- }
-
- localCaps, err := api.UnmarshalCapabilities(a.Conf.LocalCap)
- if err != nil {
- return nil, err
- }
- remoteCaps, err := api.UnmarshalCapabilities(a.Conf.RemoteCap)
- if err != nil {
- return nil, err
- }
- pconf.State.LocalCapabilityList = localCaps
- pconf.State.RemoteCapabilityList = remoteCaps
-
- pconf.State.RemoteRouterId = a.Conf.Id
-
- for _, af := range a.AfiSafis {
- afiSafi := config.AfiSafi{}
- ReadMpGracefulRestartFromAPIStruct(&afiSafi.MpGracefulRestart, af.MpGracefulRestart)
- ReadAfiSafiConfigFromAPIStruct(&afiSafi.Config, af.Config)
- ReadAfiSafiStateFromAPIStruct(&afiSafi.State, af.Config)
- ReadApplyPolicyFromAPIStruct(&afiSafi.ApplyPolicy, af.ApplyPolicy)
- ReadRouteSelectionOptionsFromAPIStruct(&afiSafi.RouteSelectionOptions, af.RouteSelectionOptions)
- ReadUseMultiplePathsFromAPIStruct(&afiSafi.UseMultiplePaths, af.UseMultiplePaths)
- ReadPrefixLimitFromAPIStruct(&afiSafi.PrefixLimit, af.PrefixLimits)
- ReadRouteTargetMembershipFromAPIStruct(&afiSafi.RouteTargetMembership, af.RouteTargetMembership)
- ReadLongLivedGracefulRestartFromAPIStruct(&afiSafi.LongLivedGracefulRestart, af.LongLivedGracefulRestart)
- ReadAddPathsFromAPIStruct(&afiSafi.AddPaths, af.AddPaths)
- pconf.AfiSafis = append(pconf.AfiSafis, afiSafi)
- }
- // For the backward compatibility, we override AfiSafi configurations
- // with Peer.Families.
- for _, family := range a.Families {
- found := false
- for _, afiSafi := range pconf.AfiSafis {
- if uint32(afiSafi.State.Family) == family {
- // If Peer.Families contains the same address family,
- // we enable this address family.
- afiSafi.Config.Enabled = true
- found = true
- }
- }
- if !found {
- // If Peer.Families does not contain the same address family,
- // we append AfiSafi structure with the default value.
- pconf.AfiSafis = append(pconf.AfiSafis, config.AfiSafi{
- Config: config.AfiSafiConfig{
- AfiSafiName: config.AfiSafiType(bgp.RouteFamily(family).String()),
- Enabled: true,
- },
- })
- }
- }
- // For the backward compatibility, we override AfiSafi configurations
- // with Peer.Conf.PrefixLimits.
- for _, prefixLimit := range a.Conf.PrefixLimits {
- for _, afiSafi := range pconf.AfiSafis {
- // If Peer.Conf.PrefixLimits contains the configuration for
- // the same address family, we override AfiSafi.PrefixLimit.
- if uint32(afiSafi.State.Family) == prefixLimit.Family {
- ReadPrefixLimitFromAPIStruct(&afiSafi.PrefixLimit, prefixLimit)
- }
- }
- }
- }
-
- if a.Timers != nil {
- if a.Timers.Config != nil {
- pconf.Timers.Config.ConnectRetry = float64(a.Timers.Config.ConnectRetry)
- pconf.Timers.Config.HoldTime = float64(a.Timers.Config.HoldTime)
- pconf.Timers.Config.KeepaliveInterval = float64(a.Timers.Config.KeepaliveInterval)
- pconf.Timers.Config.MinimumAdvertisementInterval = float64(a.Timers.Config.MinimumAdvertisementInterval)
- }
- if a.Timers.State != nil {
- pconf.Timers.State.KeepaliveInterval = float64(a.Timers.State.KeepaliveInterval)
- pconf.Timers.State.NegotiatedHoldTime = float64(a.Timers.State.NegotiatedHoldTime)
- pconf.Timers.State.Uptime = int64(a.Timers.State.Uptime)
- pconf.Timers.State.Downtime = int64(a.Timers.State.Downtime)
- }
- }
- if a.RouteReflector != nil {
- pconf.RouteReflector.Config.RouteReflectorClusterId = config.RrClusterIdType(a.RouteReflector.RouteReflectorClusterId)
- pconf.RouteReflector.Config.RouteReflectorClient = a.RouteReflector.RouteReflectorClient
- }
- if a.RouteServer != nil {
- pconf.RouteServer.Config.RouteServerClient = a.RouteServer.RouteServerClient
- }
- if a.GracefulRestart != nil {
- pconf.GracefulRestart.Config.Enabled = a.GracefulRestart.Enabled
- pconf.GracefulRestart.Config.RestartTime = uint16(a.GracefulRestart.RestartTime)
- pconf.GracefulRestart.Config.HelperOnly = a.GracefulRestart.HelperOnly
- pconf.GracefulRestart.Config.DeferralTime = uint16(a.GracefulRestart.DeferralTime)
- pconf.GracefulRestart.Config.NotificationEnabled = a.GracefulRestart.NotificationEnabled
- pconf.GracefulRestart.Config.LongLivedEnabled = a.GracefulRestart.LonglivedEnabled
- pconf.GracefulRestart.State.LocalRestarting = a.GracefulRestart.LocalRestarting
- }
- ReadApplyPolicyFromAPIStruct(&pconf.ApplyPolicy, a.ApplyPolicy)
- if a.Transport != nil {
- pconf.Transport.Config.LocalAddress = a.Transport.LocalAddress
- pconf.Transport.Config.PassiveMode = a.Transport.PassiveMode
- pconf.Transport.Config.RemotePort = uint16(a.Transport.RemotePort)
- }
- if a.EbgpMultihop != nil {
- pconf.EbgpMultihop.Config.Enabled = a.EbgpMultihop.Enabled
- pconf.EbgpMultihop.Config.MultihopTtl = uint8(a.EbgpMultihop.MultihopTtl)
- }
- if a.Info != nil {
- pconf.State.SessionState = config.SessionState(a.Info.BgpState)
- pconf.State.AdminState = config.IntToAdminStateMap[int(a.Info.AdminState)]
-
- pconf.State.AdjTable.Received = a.Info.Received
- pconf.State.AdjTable.Accepted = a.Info.Accepted
- pconf.State.AdjTable.Advertised = a.Info.Advertised
- pconf.State.PeerAs = a.Info.PeerAs
- pconf.State.PeerType = config.IntToPeerTypeMap[int(a.Info.PeerType)]
- pconf.State.NeighborAddress = a.Info.NeighborAddress
-
- if a.Info.Messages != nil {
- if a.Info.Messages.Sent != nil {
- pconf.State.Messages.Sent.Update = a.Info.Messages.Sent.UPDATE
- pconf.State.Messages.Sent.Notification = a.Info.Messages.Sent.NOTIFICATION
- pconf.State.Messages.Sent.Open = a.Info.Messages.Sent.OPEN
- pconf.State.Messages.Sent.Refresh = a.Info.Messages.Sent.REFRESH
- pconf.State.Messages.Sent.Keepalive = a.Info.Messages.Sent.KEEPALIVE
- pconf.State.Messages.Sent.Discarded = a.Info.Messages.Sent.DISCARDED
- pconf.State.Messages.Sent.Total = a.Info.Messages.Sent.TOTAL
- }
- if a.Info.Messages.Received != nil {
- pconf.State.Messages.Received.Update = a.Info.Messages.Received.UPDATE
- pconf.State.Messages.Received.Open = a.Info.Messages.Received.OPEN
- pconf.State.Messages.Received.Refresh = a.Info.Messages.Received.REFRESH
- pconf.State.Messages.Received.Keepalive = a.Info.Messages.Received.KEEPALIVE
- pconf.State.Messages.Received.Discarded = a.Info.Messages.Received.DISCARDED
- pconf.State.Messages.Received.Total = a.Info.Messages.Received.TOTAL
- }
- }
- }
- ReadAddPathsFromAPIStruct(&pconf.AddPaths, a.AddPaths)
- return pconf, nil
-}
-
-func NewPeerGroupFromAPIStruct(a *api.PeerGroup) (*config.PeerGroup, error) {
- pconf := &config.PeerGroup{}
- if a.Conf != nil {
- pconf.Config.PeerAs = a.Conf.PeerAs
- pconf.Config.LocalAs = a.Conf.LocalAs
- pconf.Config.AuthPassword = a.Conf.AuthPassword
- pconf.Config.RouteFlapDamping = a.Conf.RouteFlapDamping
- pconf.Config.Description = a.Conf.Description
- pconf.Config.PeerGroupName = a.Conf.PeerGroupName
-
- switch a.Conf.RemovePrivateAs {
- case api.PeerGroupConf_ALL:
- pconf.Config.RemovePrivateAs = config.REMOVE_PRIVATE_AS_OPTION_ALL
- case api.PeerGroupConf_REPLACE:
- pconf.Config.RemovePrivateAs = config.REMOVE_PRIVATE_AS_OPTION_REPLACE
- }
-
- for _, af := range a.AfiSafis {
- afiSafi := config.AfiSafi{}
- ReadMpGracefulRestartFromAPIStruct(&afiSafi.MpGracefulRestart, af.MpGracefulRestart)
- ReadAfiSafiConfigFromAPIStruct(&afiSafi.Config, af.Config)
- ReadAfiSafiStateFromAPIStruct(&afiSafi.State, af.Config)
- ReadApplyPolicyFromAPIStruct(&afiSafi.ApplyPolicy, af.ApplyPolicy)
- ReadRouteSelectionOptionsFromAPIStruct(&afiSafi.RouteSelectionOptions, af.RouteSelectionOptions)
- ReadUseMultiplePathsFromAPIStruct(&afiSafi.UseMultiplePaths, af.UseMultiplePaths)
- ReadPrefixLimitFromAPIStruct(&afiSafi.PrefixLimit, af.PrefixLimits)
- ReadRouteTargetMembershipFromAPIStruct(&afiSafi.RouteTargetMembership, af.RouteTargetMembership)
- ReadLongLivedGracefulRestartFromAPIStruct(&afiSafi.LongLivedGracefulRestart, af.LongLivedGracefulRestart)
- ReadAddPathsFromAPIStruct(&afiSafi.AddPaths, af.AddPaths)
- pconf.AfiSafis = append(pconf.AfiSafis, afiSafi)
- }
- // For the backward compatibility, we override AfiSafi configurations
- // with Peer.Families.
- for _, family := range a.Families {
- found := false
- for _, afiSafi := range pconf.AfiSafis {
- if uint32(afiSafi.State.Family) == family {
- // If Peer.Families contains the same address family,
- // we enable this address family.
- afiSafi.Config.Enabled = true
- found = true
- }
- }
- if !found {
- // If Peer.Families does not contain the same address family,
- // we append AfiSafi structure with the default value.
- pconf.AfiSafis = append(pconf.AfiSafis, config.AfiSafi{
- Config: config.AfiSafiConfig{
- AfiSafiName: config.AfiSafiType(bgp.RouteFamily(family).String()),
- Enabled: true,
- },
- })
- }
- }
- }
-
- if a.Timers != nil {
- if a.Timers.Config != nil {
- pconf.Timers.Config.ConnectRetry = float64(a.Timers.Config.ConnectRetry)
- pconf.Timers.Config.HoldTime = float64(a.Timers.Config.HoldTime)
- pconf.Timers.Config.KeepaliveInterval = float64(a.Timers.Config.KeepaliveInterval)
- pconf.Timers.Config.MinimumAdvertisementInterval = float64(a.Timers.Config.MinimumAdvertisementInterval)
- }
- if a.Timers.State != nil {
- pconf.Timers.State.KeepaliveInterval = float64(a.Timers.State.KeepaliveInterval)
- pconf.Timers.State.NegotiatedHoldTime = float64(a.Timers.State.NegotiatedHoldTime)
- pconf.Timers.State.Uptime = int64(a.Timers.State.Uptime)
- pconf.Timers.State.Downtime = int64(a.Timers.State.Downtime)
- }
- }
- if a.RouteReflector != nil {
- pconf.RouteReflector.Config.RouteReflectorClusterId = config.RrClusterIdType(a.RouteReflector.RouteReflectorClusterId)
- pconf.RouteReflector.Config.RouteReflectorClient = a.RouteReflector.RouteReflectorClient
- }
- if a.RouteServer != nil {
- pconf.RouteServer.Config.RouteServerClient = a.RouteServer.RouteServerClient
- }
- if a.GracefulRestart != nil {
- pconf.GracefulRestart.Config.Enabled = a.GracefulRestart.Enabled
- pconf.GracefulRestart.Config.RestartTime = uint16(a.GracefulRestart.RestartTime)
- pconf.GracefulRestart.Config.HelperOnly = a.GracefulRestart.HelperOnly
- pconf.GracefulRestart.Config.DeferralTime = uint16(a.GracefulRestart.DeferralTime)
- pconf.GracefulRestart.Config.NotificationEnabled = a.GracefulRestart.NotificationEnabled
- pconf.GracefulRestart.Config.LongLivedEnabled = a.GracefulRestart.LonglivedEnabled
- pconf.GracefulRestart.State.LocalRestarting = a.GracefulRestart.LocalRestarting
- }
- ReadApplyPolicyFromAPIStruct(&pconf.ApplyPolicy, a.ApplyPolicy)
- if a.Transport != nil {
- pconf.Transport.Config.LocalAddress = a.Transport.LocalAddress
- pconf.Transport.Config.PassiveMode = a.Transport.PassiveMode
- pconf.Transport.Config.RemotePort = uint16(a.Transport.RemotePort)
- }
- if a.EbgpMultihop != nil {
- pconf.EbgpMultihop.Config.Enabled = a.EbgpMultihop.Enabled
- pconf.EbgpMultihop.Config.MultihopTtl = uint8(a.EbgpMultihop.MultihopTtl)
- }
- if a.Info != nil {
- pconf.State.TotalPaths = a.Info.TotalPaths
- pconf.State.TotalPrefixes = a.Info.TotalPrefixes
- pconf.State.PeerAs = a.Info.PeerAs
- pconf.State.PeerType = config.IntToPeerTypeMap[int(a.Info.PeerType)]
- }
- ReadAddPathsFromAPIStruct(&pconf.AddPaths, a.AddPaths)
- return pconf, nil
-}
-
-func (s *Server) AddNeighbor(ctx context.Context, arg *api.AddNeighborRequest) (*api.AddNeighborResponse, error) {
- c, err := NewNeighborFromAPIStruct(arg.Peer)
- if err != nil {
- return nil, err
- }
- return &api.AddNeighborResponse{}, s.bgpServer.AddNeighbor(c)
-}
-
-func (s *Server) DeleteNeighbor(ctx context.Context, arg *api.DeleteNeighborRequest) (*api.DeleteNeighborResponse, error) {
- return &api.DeleteNeighborResponse{}, s.bgpServer.DeleteNeighbor(&config.Neighbor{Config: config.NeighborConfig{
- NeighborAddress: arg.Peer.Conf.NeighborAddress,
- NeighborInterface: arg.Peer.Conf.NeighborInterface,
- }})
-}
-
-func (s *Server) UpdateNeighbor(ctx context.Context, arg *api.UpdateNeighborRequest) (*api.UpdateNeighborResponse, error) {
- c, err := NewNeighborFromAPIStruct(arg.Peer)
- if err != nil {
- return nil, err
- }
- needsSoftResetIn, err := s.bgpServer.UpdateNeighbor(c)
- if err != nil {
- return nil, err
- }
- if arg.DoSoftResetIn && needsSoftResetIn {
- return &api.UpdateNeighborResponse{NeedsSoftResetIn: false}, s.bgpServer.SoftResetIn("", bgp.RouteFamily(0))
- }
- return &api.UpdateNeighborResponse{NeedsSoftResetIn: needsSoftResetIn}, nil
-}
-
-func (s *Server) AddPeerGroup(ctx context.Context, arg *api.AddPeerGroupRequest) (*api.AddPeerGroupResponse, error) {
- c, err := NewPeerGroupFromAPIStruct(arg.PeerGroup)
- if err != nil {
- return nil, err
- }
- return &api.AddPeerGroupResponse{}, s.bgpServer.AddPeerGroup(c)
-}
-
-func (s *Server) DeletePeerGroup(ctx context.Context, arg *api.DeletePeerGroupRequest) (*api.DeletePeerGroupResponse, error) {
- return &api.DeletePeerGroupResponse{}, s.bgpServer.DeletePeerGroup(&config.PeerGroup{Config: config.PeerGroupConfig{
- PeerGroupName: arg.PeerGroup.Conf.PeerGroupName,
- }})
-}
-
-func (s *Server) UpdatePeerGroup(ctx context.Context, arg *api.UpdatePeerGroupRequest) (*api.UpdatePeerGroupResponse, error) {
- c, err := NewPeerGroupFromAPIStruct(arg.PeerGroup)
- if err != nil {
- return nil, err
- }
- needsSoftResetIn, err := s.bgpServer.UpdatePeerGroup(c)
- if err != nil {
- return nil, err
- }
- if arg.DoSoftResetIn && needsSoftResetIn {
- return &api.UpdatePeerGroupResponse{NeedsSoftResetIn: false}, s.bgpServer.SoftResetIn("", bgp.RouteFamily(0))
- }
- return &api.UpdatePeerGroupResponse{NeedsSoftResetIn: needsSoftResetIn}, nil
-}
-
-func (s *Server) AddDynamicNeighbor(ctx context.Context, arg *api.AddDynamicNeighborRequest) (*api.AddDynamicNeighborResponse, error) {
- return &api.AddDynamicNeighborResponse{}, s.bgpServer.AddDynamicNeighbor(&config.DynamicNeighbor{Config: config.DynamicNeighborConfig{
- Prefix: arg.DynamicNeighbor.Prefix,
- PeerGroup: arg.DynamicNeighbor.PeerGroup,
- }})
-}
-
-func NewPrefixFromApiStruct(a *api.Prefix) (*table.Prefix, error) {
- _, prefix, err := net.ParseCIDR(a.IpPrefix)
- if err != nil {
- return nil, err
- }
- rf := bgp.RF_IPv4_UC
- if strings.Contains(a.IpPrefix, ":") {
- rf = bgp.RF_IPv6_UC
- }
- return &table.Prefix{
- Prefix: prefix,
- AddressFamily: rf,
- MasklengthRangeMin: uint8(a.MaskLengthMin),
- MasklengthRangeMax: uint8(a.MaskLengthMax),
- }, nil
-}
-
-func NewConfigPrefixFromAPIStruct(a *api.Prefix) (*config.Prefix, error) {
- _, prefix, err := net.ParseCIDR(a.IpPrefix)
- if err != nil {
- return nil, err
- }
- return &config.Prefix{
- IpPrefix: prefix.String(),
- MasklengthRange: fmt.Sprintf("%d..%d", a.MaskLengthMin, a.MaskLengthMax),
- }, nil
-}
-
-func NewAPIPrefixFromConfigStruct(c config.Prefix) (*api.Prefix, error) {
- min, max, err := config.ParseMaskLength(c.IpPrefix, c.MasklengthRange)
- if err != nil {
- return nil, err
- }
- return &api.Prefix{
- IpPrefix: c.IpPrefix,
- MaskLengthMin: uint32(min),
- MaskLengthMax: uint32(max),
- }, nil
-}
-
-func NewAPIDefinedSetFromTableStruct(t table.DefinedSet) (*api.DefinedSet, error) {
- a := &api.DefinedSet{
- Type: api.DefinedType(t.Type()),
- Name: t.Name(),
- }
- switch t.Type() {
- case table.DEFINED_TYPE_PREFIX:
- s := t.(*table.PrefixSet)
- c := s.ToConfig()
- for _, p := range c.PrefixList {
- ap, err := NewAPIPrefixFromConfigStruct(p)
- if err != nil {
- return nil, err
- }
- a.Prefixes = append(a.Prefixes, ap)
- }
- case table.DEFINED_TYPE_NEIGHBOR:
- s := t.(*table.NeighborSet)
- c := s.ToConfig()
- a.List = append(a.List, c.NeighborInfoList...)
- case table.DEFINED_TYPE_AS_PATH:
- s := t.(*table.AsPathSet)
- c := s.ToConfig()
- a.List = append(a.List, c.AsPathList...)
- case table.DEFINED_TYPE_COMMUNITY:
- s := t.(*table.CommunitySet)
- c := s.ToConfig()
- a.List = append(a.List, c.CommunityList...)
- case table.DEFINED_TYPE_EXT_COMMUNITY:
- s := t.(*table.ExtCommunitySet)
- c := s.ToConfig()
- a.List = append(a.List, c.ExtCommunityList...)
- case table.DEFINED_TYPE_LARGE_COMMUNITY:
- s := t.(*table.LargeCommunitySet)
- c := s.ToConfig()
- a.List = append(a.List, c.LargeCommunityList...)
- default:
- return nil, fmt.Errorf("invalid defined type")
- }
- return a, nil
-}
-
-func NewAPIDefinedSetsFromConfigStruct(t *config.DefinedSets) ([]*api.DefinedSet, error) {
- definedSets := make([]*api.DefinedSet, 0)
-
- for _, ps := range t.PrefixSets {
- prefixes := make([]*api.Prefix, 0)
- for _, p := range ps.PrefixList {
- ap, err := NewAPIPrefixFromConfigStruct(p)
- if err != nil {
- return nil, err
- }
- prefixes = append(prefixes, ap)
- }
- definedSets = append(definedSets, &api.DefinedSet{
- Type: api.DefinedType_PREFIX,
- Name: ps.PrefixSetName,
- Prefixes: prefixes,
- })
- }
-
- for _, ns := range t.NeighborSets {
- definedSets = append(definedSets, &api.DefinedSet{
- Type: api.DefinedType_NEIGHBOR,
- Name: ns.NeighborSetName,
- List: ns.NeighborInfoList,
- })
- }
-
- bs := t.BgpDefinedSets
- for _, cs := range bs.CommunitySets {
- definedSets = append(definedSets, &api.DefinedSet{
- Type: api.DefinedType_COMMUNITY,
- Name: cs.CommunitySetName,
- List: cs.CommunityList,
- })
- }
-
- for _, es := range bs.ExtCommunitySets {
- definedSets = append(definedSets, &api.DefinedSet{
- Type: api.DefinedType_EXT_COMMUNITY,
- Name: es.ExtCommunitySetName,
- List: es.ExtCommunityList,
- })
- }
-
- for _, ls := range bs.LargeCommunitySets {
- definedSets = append(definedSets, &api.DefinedSet{
- Type: api.DefinedType_LARGE_COMMUNITY,
- Name: ls.LargeCommunitySetName,
- List: ls.LargeCommunityList,
- })
- }
-
- for _, as := range bs.AsPathSets {
- definedSets = append(definedSets, &api.DefinedSet{
- Type: api.DefinedType_AS_PATH,
- Name: as.AsPathSetName,
- List: as.AsPathList,
- })
- }
-
- return definedSets, nil
-}
-
-func NewConfigDefinedSetsFromApiStruct(a []*api.DefinedSet) (*config.DefinedSets, error) {
- ps := make([]config.PrefixSet, 0)
- ns := make([]config.NeighborSet, 0)
- as := make([]config.AsPathSet, 0)
- cs := make([]config.CommunitySet, 0)
- es := make([]config.ExtCommunitySet, 0)
- ls := make([]config.LargeCommunitySet, 0)
-
- for _, ds := range a {
- if ds.Name == "" {
- return nil, fmt.Errorf("empty neighbor set name")
- }
- switch table.DefinedType(ds.Type) {
- case table.DEFINED_TYPE_PREFIX:
- prefixes := make([]config.Prefix, 0, len(ds.Prefixes))
- for _, p := range ds.Prefixes {
- prefix, err := NewConfigPrefixFromAPIStruct(p)
- if err != nil {
- return nil, err
- }
- prefixes = append(prefixes, *prefix)
- }
- ps = append(ps, config.PrefixSet{
- PrefixSetName: ds.Name,
- PrefixList: prefixes,
- })
- case table.DEFINED_TYPE_NEIGHBOR:
- ns = append(ns, config.NeighborSet{
- NeighborSetName: ds.Name,
- NeighborInfoList: ds.List,
- })
- case table.DEFINED_TYPE_AS_PATH:
- as = append(as, config.AsPathSet{
- AsPathSetName: ds.Name,
- AsPathList: ds.List,
- })
- case table.DEFINED_TYPE_COMMUNITY:
- cs = append(cs, config.CommunitySet{
- CommunitySetName: ds.Name,
- CommunityList: ds.List,
- })
- case table.DEFINED_TYPE_EXT_COMMUNITY:
- es = append(es, config.ExtCommunitySet{
- ExtCommunitySetName: ds.Name,
- ExtCommunityList: ds.List,
- })
- case table.DEFINED_TYPE_LARGE_COMMUNITY:
- ls = append(ls, config.LargeCommunitySet{
- LargeCommunitySetName: ds.Name,
- LargeCommunityList: ds.List,
- })
- default:
- return nil, fmt.Errorf("invalid defined type")
- }
- }
-
- return &config.DefinedSets{
- PrefixSets: ps,
- NeighborSets: ns,
- BgpDefinedSets: config.BgpDefinedSets{
- AsPathSets: as,
- CommunitySets: cs,
- ExtCommunitySets: es,
- LargeCommunitySets: ls,
- },
- }, nil
-}
-
-func NewDefinedSetFromApiStruct(a *api.DefinedSet) (table.DefinedSet, error) {
- if a.Name == "" {
- return nil, fmt.Errorf("empty neighbor set name")
- }
- switch table.DefinedType(a.Type) {
- case table.DEFINED_TYPE_PREFIX:
- prefixes := make([]*table.Prefix, 0, len(a.Prefixes))
- for _, p := range a.Prefixes {
- prefix, err := NewPrefixFromApiStruct(p)
- if err != nil {
- return nil, err
- }
- prefixes = append(prefixes, prefix)
- }
- return table.NewPrefixSetFromApiStruct(a.Name, prefixes)
- case table.DEFINED_TYPE_NEIGHBOR:
- list := make([]net.IPNet, 0, len(a.List))
- for _, x := range a.List {
- _, addr, err := net.ParseCIDR(x)
- if err != nil {
- return nil, fmt.Errorf("invalid address or prefix: %s", x)
- }
- list = append(list, *addr)
- }
- return table.NewNeighborSetFromApiStruct(a.Name, list)
- case table.DEFINED_TYPE_AS_PATH:
- return table.NewAsPathSet(config.AsPathSet{
- AsPathSetName: a.Name,
- AsPathList: a.List,
- })
- case table.DEFINED_TYPE_COMMUNITY:
- return table.NewCommunitySet(config.CommunitySet{
- CommunitySetName: a.Name,
- CommunityList: a.List,
- })
- case table.DEFINED_TYPE_EXT_COMMUNITY:
- return table.NewExtCommunitySet(config.ExtCommunitySet{
- ExtCommunitySetName: a.Name,
- ExtCommunityList: a.List,
- })
- case table.DEFINED_TYPE_LARGE_COMMUNITY:
- return table.NewLargeCommunitySet(config.LargeCommunitySet{
- LargeCommunitySetName: a.Name,
- LargeCommunityList: a.List,
- })
- default:
- return nil, fmt.Errorf("invalid defined type")
- }
-}
-
-var _regexpPrefixMaskLengthRange = regexp.MustCompile(`(\d+)\.\.(\d+)`)
-
-func (s *Server) GetDefinedSet(ctx context.Context, arg *api.GetDefinedSetRequest) (*api.GetDefinedSetResponse, error) {
- cd, err := s.bgpServer.GetDefinedSet(table.DefinedType(arg.Type), arg.Name)
- if err != nil {
- return nil, err
- }
- sets := make([]*api.DefinedSet, 0)
- for _, cs := range cd.PrefixSets {
- ad := &api.DefinedSet{
- Type: api.DefinedType_PREFIX,
- Name: cs.PrefixSetName,
- Prefixes: func() []*api.Prefix {
- l := make([]*api.Prefix, 0, len(cs.PrefixList))
- for _, p := range cs.PrefixList {
- elems := _regexpPrefixMaskLengthRange.FindStringSubmatch(p.MasklengthRange)
- min, _ := strconv.ParseUint(elems[1], 10, 32)
- max, _ := strconv.ParseUint(elems[2], 10, 32)
-
- l = append(l, &api.Prefix{IpPrefix: p.IpPrefix, MaskLengthMin: uint32(min), MaskLengthMax: uint32(max)})
- }
- return l
- }(),
- }
- sets = append(sets, ad)
-
- }
- for _, cs := range cd.NeighborSets {
- ad := &api.DefinedSet{
- Type: api.DefinedType_NEIGHBOR,
- Name: cs.NeighborSetName,
- List: cs.NeighborInfoList,
- }
- sets = append(sets, ad)
- }
- for _, cs := range cd.BgpDefinedSets.CommunitySets {
- ad := &api.DefinedSet{
- Type: api.DefinedType_COMMUNITY,
- Name: cs.CommunitySetName,
- List: cs.CommunityList,
- }
- sets = append(sets, ad)
- }
- for _, cs := range cd.BgpDefinedSets.ExtCommunitySets {
- ad := &api.DefinedSet{
- Type: api.DefinedType_EXT_COMMUNITY,
- Name: cs.ExtCommunitySetName,
- List: cs.ExtCommunityList,
- }
- sets = append(sets, ad)
- }
- for _, cs := range cd.BgpDefinedSets.LargeCommunitySets {
- ad := &api.DefinedSet{
- Type: api.DefinedType_LARGE_COMMUNITY,
- Name: cs.LargeCommunitySetName,
- List: cs.LargeCommunityList,
- }
- sets = append(sets, ad)
- }
- for _, cs := range cd.BgpDefinedSets.AsPathSets {
- ad := &api.DefinedSet{
- Type: api.DefinedType_AS_PATH,
- Name: cs.AsPathSetName,
- List: cs.AsPathList,
- }
- sets = append(sets, ad)
- }
-
- return &api.GetDefinedSetResponse{Sets: sets}, nil
-}
-
-func (s *Server) AddDefinedSet(ctx context.Context, arg *api.AddDefinedSetRequest) (*api.AddDefinedSetResponse, error) {
- if arg == nil || arg.Set == nil {
- return nil, fmt.Errorf("invalid request")
- }
- set, err := NewDefinedSetFromApiStruct(arg.Set)
- if err != nil {
- return nil, err
- }
- return &api.AddDefinedSetResponse{}, s.bgpServer.AddDefinedSet(set)
-}
-
-func (s *Server) DeleteDefinedSet(ctx context.Context, arg *api.DeleteDefinedSetRequest) (*api.DeleteDefinedSetResponse, error) {
- if arg == nil || arg.Set == nil {
- return nil, fmt.Errorf("invalid request")
- }
- set, err := NewDefinedSetFromApiStruct(arg.Set)
- if err != nil {
- return nil, err
- }
- return &api.DeleteDefinedSetResponse{}, s.bgpServer.DeleteDefinedSet(set, arg.All)
-}
-
-func (s *Server) ReplaceDefinedSet(ctx context.Context, arg *api.ReplaceDefinedSetRequest) (*api.ReplaceDefinedSetResponse, error) {
- if arg == nil || arg.Set == nil {
- return nil, fmt.Errorf("invalid request")
- }
- set, err := NewDefinedSetFromApiStruct(arg.Set)
- if err != nil {
- return nil, err
- }
- return &api.ReplaceDefinedSetResponse{}, s.bgpServer.ReplaceDefinedSet(set)
-}
-
-func NewAPIStatementFromTableStruct(t *table.Statement) *api.Statement {
- return toStatementApi(t.ToConfig())
-}
-
-var _regexpMedActionType = regexp.MustCompile(`([+-]?)(\d+)`)
-
-func toStatementApi(s *config.Statement) *api.Statement {
- cs := &api.Conditions{}
- if s.Conditions.MatchPrefixSet.PrefixSet != "" {
- o, _ := table.NewMatchOption(s.Conditions.MatchPrefixSet.MatchSetOptions)
- cs.PrefixSet = &api.MatchSet{
- Type: api.MatchType(o),
- Name: s.Conditions.MatchPrefixSet.PrefixSet,
- }
- }
- if s.Conditions.MatchNeighborSet.NeighborSet != "" {
- o, _ := table.NewMatchOption(s.Conditions.MatchNeighborSet.MatchSetOptions)
- cs.NeighborSet = &api.MatchSet{
- Type: api.MatchType(o),
- Name: s.Conditions.MatchNeighborSet.NeighborSet,
- }
- }
- if s.Conditions.BgpConditions.AsPathLength.Operator != "" {
- cs.AsPathLength = &api.AsPathLength{
- Length: s.Conditions.BgpConditions.AsPathLength.Value,
- Type: api.AsPathLengthType(s.Conditions.BgpConditions.AsPathLength.Operator.ToInt()),
- }
- }
- if s.Conditions.BgpConditions.MatchAsPathSet.AsPathSet != "" {
- cs.AsPathSet = &api.MatchSet{
- Type: api.MatchType(s.Conditions.BgpConditions.MatchAsPathSet.MatchSetOptions.ToInt()),
- Name: s.Conditions.BgpConditions.MatchAsPathSet.AsPathSet,
- }
- }
- if s.Conditions.BgpConditions.MatchCommunitySet.CommunitySet != "" {
- cs.CommunitySet = &api.MatchSet{
- Type: api.MatchType(s.Conditions.BgpConditions.MatchCommunitySet.MatchSetOptions.ToInt()),
- Name: s.Conditions.BgpConditions.MatchCommunitySet.CommunitySet,
- }
- }
- if s.Conditions.BgpConditions.MatchExtCommunitySet.ExtCommunitySet != "" {
- cs.ExtCommunitySet = &api.MatchSet{
- Type: api.MatchType(s.Conditions.BgpConditions.MatchExtCommunitySet.MatchSetOptions.ToInt()),
- Name: s.Conditions.BgpConditions.MatchExtCommunitySet.ExtCommunitySet,
- }
- }
- if s.Conditions.BgpConditions.MatchLargeCommunitySet.LargeCommunitySet != "" {
- cs.LargeCommunitySet = &api.MatchSet{
- Type: api.MatchType(s.Conditions.BgpConditions.MatchLargeCommunitySet.MatchSetOptions.ToInt()),
- Name: s.Conditions.BgpConditions.MatchLargeCommunitySet.LargeCommunitySet,
- }
- }
- if s.Conditions.BgpConditions.RouteType != "" {
- cs.RouteType = api.Conditions_RouteType(s.Conditions.BgpConditions.RouteType.ToInt())
- }
- if len(s.Conditions.BgpConditions.NextHopInList) > 0 {
- cs.NextHopInList = s.Conditions.BgpConditions.NextHopInList
- }
- if s.Conditions.BgpConditions.AfiSafiInList != nil {
- afiSafiIn := make([]api.Family, 0)
- for _, afiSafiType := range s.Conditions.BgpConditions.AfiSafiInList {
- if mapped, ok := bgp.AddressFamilyValueMap[string(afiSafiType)]; ok {
- afiSafiIn = append(afiSafiIn, api.Family(mapped))
- }
- }
- cs.AfiSafiIn = afiSafiIn
- }
- cs.RpkiResult = int32(s.Conditions.BgpConditions.RpkiValidationResult.ToInt())
- as := &api.Actions{
- RouteAction: func() api.RouteAction {
- switch s.Actions.RouteDisposition {
- case config.ROUTE_DISPOSITION_ACCEPT_ROUTE:
- return api.RouteAction_ACCEPT
- case config.ROUTE_DISPOSITION_REJECT_ROUTE:
- return api.RouteAction_REJECT
- }
- return api.RouteAction_NONE
- }(),
- Community: func() *api.CommunityAction {
- if len(s.Actions.BgpActions.SetCommunity.SetCommunityMethod.CommunitiesList) == 0 {
- return nil
- }
- return &api.CommunityAction{
- Type: api.CommunityActionType(config.BgpSetCommunityOptionTypeToIntMap[config.BgpSetCommunityOptionType(s.Actions.BgpActions.SetCommunity.Options)]),
- Communities: s.Actions.BgpActions.SetCommunity.SetCommunityMethod.CommunitiesList}
- }(),
- Med: func() *api.MedAction {
- medStr := strings.TrimSpace(string(s.Actions.BgpActions.SetMed))
- if len(medStr) == 0 {
- return nil
- }
- matches := _regexpMedActionType.FindStringSubmatch(medStr)
- if len(matches) == 0 {
- return nil
- }
- action := api.MedActionType_MED_REPLACE
- switch matches[1] {
- case "+", "-":
- action = api.MedActionType_MED_MOD
- }
- value, err := strconv.ParseInt(matches[1]+matches[2], 10, 64)
- if err != nil {
- return nil
- }
- return &api.MedAction{
- Value: value,
- Type: action,
- }
- }(),
- AsPrepend: func() *api.AsPrependAction {
- if len(s.Actions.BgpActions.SetAsPathPrepend.As) == 0 {
- return nil
- }
- var asn uint64
- useleft := false
- if s.Actions.BgpActions.SetAsPathPrepend.As != "last-as" {
- asn, _ = strconv.ParseUint(s.Actions.BgpActions.SetAsPathPrepend.As, 10, 32)
- } else {
- useleft = true
- }
- return &api.AsPrependAction{
- Asn: uint32(asn),
- Repeat: uint32(s.Actions.BgpActions.SetAsPathPrepend.RepeatN),
- UseLeftMost: useleft,
- }
- }(),
- ExtCommunity: func() *api.CommunityAction {
- if len(s.Actions.BgpActions.SetExtCommunity.SetExtCommunityMethod.CommunitiesList) == 0 {
- return nil
- }
- return &api.CommunityAction{
- Type: api.CommunityActionType(config.BgpSetCommunityOptionTypeToIntMap[config.BgpSetCommunityOptionType(s.Actions.BgpActions.SetExtCommunity.Options)]),
- Communities: s.Actions.BgpActions.SetExtCommunity.SetExtCommunityMethod.CommunitiesList,
- }
- }(),
- LargeCommunity: func() *api.CommunityAction {
- if len(s.Actions.BgpActions.SetLargeCommunity.SetLargeCommunityMethod.CommunitiesList) == 0 {
- return nil
- }
- return &api.CommunityAction{
- Type: api.CommunityActionType(config.BgpSetCommunityOptionTypeToIntMap[config.BgpSetCommunityOptionType(s.Actions.BgpActions.SetLargeCommunity.Options)]),
- Communities: s.Actions.BgpActions.SetLargeCommunity.SetLargeCommunityMethod.CommunitiesList,
- }
- }(),
- Nexthop: func() *api.NexthopAction {
- if len(string(s.Actions.BgpActions.SetNextHop)) == 0 {
- return nil
- }
-
- if string(s.Actions.BgpActions.SetNextHop) == "self" {
- return &api.NexthopAction{
- Self: true,
- }
- }
- return &api.NexthopAction{
- Address: string(s.Actions.BgpActions.SetNextHop),
- }
- }(),
- LocalPref: func() *api.LocalPrefAction {
- if s.Actions.BgpActions.SetLocalPref == 0 {
- return nil
- }
- return &api.LocalPrefAction{Value: s.Actions.BgpActions.SetLocalPref}
- }(),
- }
- return &api.Statement{
- Name: s.Name,
- Conditions: cs,
- Actions: as,
- }
-}
-
-func toConfigMatchSetOption(a api.MatchType) (config.MatchSetOptionsType, error) {
- var typ config.MatchSetOptionsType
- switch a {
- case api.MatchType_ANY:
- typ = config.MATCH_SET_OPTIONS_TYPE_ANY
- case api.MatchType_ALL:
- typ = config.MATCH_SET_OPTIONS_TYPE_ALL
- case api.MatchType_INVERT:
- typ = config.MATCH_SET_OPTIONS_TYPE_INVERT
- default:
- return typ, fmt.Errorf("invalid match type")
- }
- return typ, nil
-}
-
-func toConfigMatchSetOptionRestricted(a api.MatchType) (config.MatchSetOptionsRestrictedType, error) {
- var typ config.MatchSetOptionsRestrictedType
- switch a {
- case api.MatchType_ANY:
- typ = config.MATCH_SET_OPTIONS_RESTRICTED_TYPE_ANY
- case api.MatchType_INVERT:
- typ = config.MATCH_SET_OPTIONS_RESTRICTED_TYPE_INVERT
- default:
- return typ, fmt.Errorf("invalid match type")
- }
- return typ, nil
-}
-
-func NewPrefixConditionFromApiStruct(a *api.MatchSet) (*table.PrefixCondition, error) {
- if a == nil {
- return nil, nil
- }
- typ, err := toConfigMatchSetOptionRestricted(a.Type)
- if err != nil {
- return nil, err
- }
- c := config.MatchPrefixSet{
- PrefixSet: a.Name,
- MatchSetOptions: typ,
- }
- return table.NewPrefixCondition(c)
-}
-
-func NewNeighborConditionFromApiStruct(a *api.MatchSet) (*table.NeighborCondition, error) {
- if a == nil {
- return nil, nil
- }
- typ, err := toConfigMatchSetOptionRestricted(a.Type)
- if err != nil {
- return nil, err
- }
- c := config.MatchNeighborSet{
- NeighborSet: a.Name,
- MatchSetOptions: typ,
- }
- return table.NewNeighborCondition(c)
-}
-
-func NewAsPathLengthConditionFromApiStruct(a *api.AsPathLength) (*table.AsPathLengthCondition, error) {
- if a == nil {
- return nil, nil
- }
- return table.NewAsPathLengthCondition(config.AsPathLength{
- Operator: config.IntToAttributeComparisonMap[int(a.Type)],
- Value: a.Length,
- })
-}
-
-func NewAsPathConditionFromApiStruct(a *api.MatchSet) (*table.AsPathCondition, error) {
- if a == nil {
- return nil, nil
- }
- typ, err := toConfigMatchSetOption(a.Type)
- if err != nil {
- return nil, err
- }
- c := config.MatchAsPathSet{
- AsPathSet: a.Name,
- MatchSetOptions: typ,
- }
- return table.NewAsPathCondition(c)
-}
-
-func NewRpkiValidationConditionFromApiStruct(a int32) (*table.RpkiValidationCondition, error) {
- if a < 1 {
- return nil, nil
- }
- return table.NewRpkiValidationCondition(config.IntToRpkiValidationResultTypeMap[int(a)])
-}
-
-func NewRouteTypeConditionFromApiStruct(a api.Conditions_RouteType) (*table.RouteTypeCondition, error) {
- if a == 0 {
- return nil, nil
- }
- typ, ok := config.IntToRouteTypeMap[int(a)]
- if !ok {
- return nil, fmt.Errorf("invalid route type: %d", a)
- }
- return table.NewRouteTypeCondition(typ)
-}
-
-func NewCommunityConditionFromApiStruct(a *api.MatchSet) (*table.CommunityCondition, error) {
- if a == nil {
- return nil, nil
- }
- typ, err := toConfigMatchSetOption(a.Type)
- if err != nil {
- return nil, err
- }
- c := config.MatchCommunitySet{
- CommunitySet: a.Name,
- MatchSetOptions: typ,
- }
- return table.NewCommunityCondition(c)
-}
-
-func NewExtCommunityConditionFromApiStruct(a *api.MatchSet) (*table.ExtCommunityCondition, error) {
- if a == nil {
- return nil, nil
- }
- typ, err := toConfigMatchSetOption(a.Type)
- if err != nil {
- return nil, err
- }
- c := config.MatchExtCommunitySet{
- ExtCommunitySet: a.Name,
- MatchSetOptions: typ,
- }
- return table.NewExtCommunityCondition(c)
-}
-
-func NewLargeCommunityConditionFromApiStruct(a *api.MatchSet) (*table.LargeCommunityCondition, error) {
- if a == nil {
- return nil, nil
- }
- typ, err := toConfigMatchSetOption(a.Type)
- if err != nil {
- return nil, err
- }
- c := config.MatchLargeCommunitySet{
- LargeCommunitySet: a.Name,
- MatchSetOptions: typ,
- }
- return table.NewLargeCommunityCondition(c)
-}
-
-func NewNextHopConditionFromApiStruct(a []string) (*table.NextHopCondition, error) {
- if a == nil {
- return nil, nil
- }
-
- return table.NewNextHopCondition(a)
-}
-
-func NewAfiSafiInConditionFromApiStruct(a []api.Family) (*table.AfiSafiInCondition, error) {
- if a == nil {
- return nil, nil
- }
- afiSafiTypes := make([]config.AfiSafiType, 0, len(a))
- for _, aType := range a {
- if configType, ok := bgp.AddressFamilyNameMap[bgp.RouteFamily(aType)]; ok {
- afiSafiTypes = append(afiSafiTypes, config.AfiSafiType(configType))
- } else {
- return nil, fmt.Errorf("unknown afi-safi-in type value: %d", aType)
- }
- }
- return table.NewAfiSafiInCondition(afiSafiTypes)
-}
-
-func NewRoutingActionFromApiStruct(a api.RouteAction) (*table.RoutingAction, error) {
- if a == api.RouteAction_NONE {
- return nil, nil
- }
- accept := false
- if a == api.RouteAction_ACCEPT {
- accept = true
- }
- return &table.RoutingAction{
- AcceptRoute: accept,
- }, nil
-}
-
-func NewCommunityActionFromApiStruct(a *api.CommunityAction) (*table.CommunityAction, error) {
- if a == nil {
- return nil, nil
- }
- return table.NewCommunityAction(config.SetCommunity{
- Options: string(config.IntToBgpSetCommunityOptionTypeMap[int(a.Type)]),
- SetCommunityMethod: config.SetCommunityMethod{
- CommunitiesList: a.Communities,
- },
- })
-}
-
-func NewExtCommunityActionFromApiStruct(a *api.CommunityAction) (*table.ExtCommunityAction, error) {
- if a == nil {
- return nil, nil
- }
- return table.NewExtCommunityAction(config.SetExtCommunity{
- Options: string(config.IntToBgpSetCommunityOptionTypeMap[int(a.Type)]),
- SetExtCommunityMethod: config.SetExtCommunityMethod{
- CommunitiesList: a.Communities,
- },
- })
-}
-
-func NewLargeCommunityActionFromApiStruct(a *api.CommunityAction) (*table.LargeCommunityAction, error) {
- if a == nil {
- return nil, nil
- }
- return table.NewLargeCommunityAction(config.SetLargeCommunity{
- Options: config.IntToBgpSetCommunityOptionTypeMap[int(a.Type)],
- SetLargeCommunityMethod: config.SetLargeCommunityMethod{
- CommunitiesList: a.Communities,
- },
- })
-}
-
-func NewMedActionFromApiStruct(a *api.MedAction) (*table.MedAction, error) {
- if a == nil {
- return nil, nil
- }
- return table.NewMedActionFromApiStruct(table.MedActionType(a.Type), a.Value), nil
-}
-
-func NewLocalPrefActionFromApiStruct(a *api.LocalPrefAction) (*table.LocalPrefAction, error) {
- if a == nil || a.Value == 0 {
- return nil, nil
- }
- return table.NewLocalPrefAction(a.Value)
-}
-
-func NewAsPathPrependActionFromApiStruct(a *api.AsPrependAction) (*table.AsPathPrependAction, error) {
- if a == nil {
- return nil, nil
- }
- return table.NewAsPathPrependAction(config.SetAsPathPrepend{
- RepeatN: uint8(a.Repeat),
- As: func() string {
- if a.UseLeftMost {
- return "last-as"
- }
- return fmt.Sprintf("%d", a.Asn)
- }(),
- })
-}
-
-func NewNexthopActionFromApiStruct(a *api.NexthopAction) (*table.NexthopAction, error) {
- if a == nil {
- return nil, nil
- }
- return table.NewNexthopAction(config.BgpNextHopType(
- func() string {
- if a.Self {
- return "self"
- }
- return a.Address
- }(),
- ))
-}
-
-func NewStatementFromApiStruct(a *api.Statement) (*table.Statement, error) {
- if a.Name == "" {
- return nil, fmt.Errorf("empty statement name")
- }
- var ra table.Action
- var as []table.Action
- var cs []table.Condition
- var err error
- if a.Conditions != nil {
- cfs := []func() (table.Condition, error){
- func() (table.Condition, error) {
- return NewPrefixConditionFromApiStruct(a.Conditions.PrefixSet)
- },
- func() (table.Condition, error) {
- return NewNeighborConditionFromApiStruct(a.Conditions.NeighborSet)
- },
- func() (table.Condition, error) {
- return NewAsPathLengthConditionFromApiStruct(a.Conditions.AsPathLength)
- },
- func() (table.Condition, error) {
- return NewRpkiValidationConditionFromApiStruct(a.Conditions.RpkiResult)
- },
- func() (table.Condition, error) {
- return NewRouteTypeConditionFromApiStruct(a.Conditions.RouteType)
- },
- func() (table.Condition, error) {
- return NewAsPathConditionFromApiStruct(a.Conditions.AsPathSet)
- },
- func() (table.Condition, error) {
- return NewCommunityConditionFromApiStruct(a.Conditions.CommunitySet)
- },
- func() (table.Condition, error) {
- return NewExtCommunityConditionFromApiStruct(a.Conditions.ExtCommunitySet)
- },
- func() (table.Condition, error) {
- return NewLargeCommunityConditionFromApiStruct(a.Conditions.LargeCommunitySet)
- },
- func() (table.Condition, error) {
- return NewNextHopConditionFromApiStruct(a.Conditions.NextHopInList)
- },
- func() (table.Condition, error) {
- return NewAfiSafiInConditionFromApiStruct(a.Conditions.AfiSafiIn)
- },
- }
- cs = make([]table.Condition, 0, len(cfs))
- for _, f := range cfs {
- c, err := f()
- if err != nil {
- return nil, err
- }
- if !reflect.ValueOf(c).IsNil() {
- cs = append(cs, c)
- }
- }
- }
- if a.Actions != nil {
- ra, err = NewRoutingActionFromApiStruct(a.Actions.RouteAction)
- if err != nil {
- return nil, err
- }
- afs := []func() (table.Action, error){
- func() (table.Action, error) {
- return NewCommunityActionFromApiStruct(a.Actions.Community)
- },
- func() (table.Action, error) {
- return NewExtCommunityActionFromApiStruct(a.Actions.ExtCommunity)
- },
- func() (table.Action, error) {
- return NewLargeCommunityActionFromApiStruct(a.Actions.LargeCommunity)
- },
- func() (table.Action, error) {
- return NewMedActionFromApiStruct(a.Actions.Med)
- },
- func() (table.Action, error) {
- return NewLocalPrefActionFromApiStruct(a.Actions.LocalPref)
- },
- func() (table.Action, error) {
- return NewAsPathPrependActionFromApiStruct(a.Actions.AsPrepend)
- },
- func() (table.Action, error) {
- return NewNexthopActionFromApiStruct(a.Actions.Nexthop)
- },
- }
- as = make([]table.Action, 0, len(afs))
- for _, f := range afs {
- a, err := f()
- if err != nil {
- return nil, err
- }
- if !reflect.ValueOf(a).IsNil() {
- as = append(as, a)
- }
- }
- }
- return &table.Statement{
- Name: a.Name,
- Conditions: cs,
- RouteAction: ra,
- ModActions: as,
- }, nil
-}
-
-func (s *Server) GetStatement(ctx context.Context, arg *api.GetStatementRequest) (*api.GetStatementResponse, error) {
- l := make([]*api.Statement, 0)
- for _, s := range s.bgpServer.GetStatement() {
- l = append(l, toStatementApi(s))
- }
- return &api.GetStatementResponse{Statements: l}, nil
-}
-
-func (s *Server) AddStatement(ctx context.Context, arg *api.AddStatementRequest) (*api.AddStatementResponse, error) {
- if arg == nil || arg.Statement == nil {
- return nil, fmt.Errorf("invalid request")
- }
- st, err := NewStatementFromApiStruct(arg.Statement)
- if err == nil {
- err = s.bgpServer.AddStatement(st)
- }
- return &api.AddStatementResponse{}, err
-}
-
-func (s *Server) DeleteStatement(ctx context.Context, arg *api.DeleteStatementRequest) (*api.DeleteStatementResponse, error) {
- if arg == nil || arg.Statement == nil {
- return nil, fmt.Errorf("invalid request")
- }
- st, err := NewStatementFromApiStruct(arg.Statement)
- if err == nil {
- err = s.bgpServer.DeleteStatement(st, arg.All)
- }
- return &api.DeleteStatementResponse{}, err
-}
-
-func (s *Server) ReplaceStatement(ctx context.Context, arg *api.ReplaceStatementRequest) (*api.ReplaceStatementResponse, error) {
- if arg == nil || arg.Statement == nil {
- return nil, fmt.Errorf("invalid request")
- }
- st, err := NewStatementFromApiStruct(arg.Statement)
- if err == nil {
- err = s.bgpServer.ReplaceStatement(st)
- }
- return &api.ReplaceStatementResponse{}, err
-}
-
-func NewAPIPolicyFromTableStruct(p *table.Policy) *api.Policy {
- return toPolicyApi(p.ToConfig())
-}
-
-func toPolicyApi(p *config.PolicyDefinition) *api.Policy {
- return &api.Policy{
- Name: p.Name,
- Statements: func() []*api.Statement {
- l := make([]*api.Statement, 0)
- for _, s := range p.Statements {
- l = append(l, toStatementApi(&s))
- }
- return l
- }(),
- }
-}
-
-func NewAPIPolicyAssignmentFromTableStruct(t *table.PolicyAssignment) *api.PolicyAssignment {
- return &api.PolicyAssignment{
- Type: func() api.PolicyType {
- switch t.Type {
- case table.POLICY_DIRECTION_IMPORT:
- return api.PolicyType_IMPORT
- case table.POLICY_DIRECTION_EXPORT:
- return api.PolicyType_EXPORT
- }
- log.Errorf("invalid policy-type: %s", t.Type)
- return api.PolicyType(-1)
- }(),
- Default: func() api.RouteAction {
- switch t.Default {
- case table.ROUTE_TYPE_ACCEPT:
- return api.RouteAction_ACCEPT
- case table.ROUTE_TYPE_REJECT:
- return api.RouteAction_REJECT
- }
- return api.RouteAction_NONE
- }(),
- Name: t.Name,
- Resource: func() api.Resource {
- if t.Name != "" {
- return api.Resource_LOCAL
- }
- return api.Resource_GLOBAL
- }(),
- Policies: func() []*api.Policy {
- l := make([]*api.Policy, 0)
- for _, p := range t.Policies {
- l = append(l, NewAPIPolicyFromTableStruct(p))
- }
- return l
- }(),
- }
-}
-
-func NewConfigPolicyFromApiStruct(a *api.Policy) (*config.PolicyDefinition, error) {
- if a.Name == "" {
- return nil, fmt.Errorf("empty policy name")
- }
- stmts := make([]config.Statement, 0, len(a.Statements))
- for idx, x := range a.Statements {
- if x.Name == "" {
- x.Name = fmt.Sprintf("%s_stmt%d", a.Name, idx)
- }
- y, err := NewStatementFromApiStruct(x)
- if err != nil {
- return nil, err
- }
- stmt := y.ToConfig()
- stmts = append(stmts, *stmt)
- }
- return &config.PolicyDefinition{
- Name: a.Name,
- Statements: stmts,
- }, nil
-}
-
-func NewPolicyFromApiStruct(a *api.Policy) (*table.Policy, error) {
- if a.Name == "" {
- return nil, fmt.Errorf("empty policy name")
- }
- stmts := make([]*table.Statement, 0, len(a.Statements))
- for idx, x := range a.Statements {
- if x.Name == "" {
- x.Name = fmt.Sprintf("%s_stmt%d", a.Name, idx)
- }
- y, err := NewStatementFromApiStruct(x)
- if err != nil {
- return nil, err
- }
- stmts = append(stmts, y)
- }
- return &table.Policy{
- Name: a.Name,
- Statements: stmts,
- }, nil
-}
-
-func NewRoaListFromTableStructList(origin []*table.ROA) []*api.Roa {
- l := make([]*api.Roa, 0)
- for _, r := range origin {
- host, port, _ := net.SplitHostPort(r.Src)
- l = append(l, &api.Roa{
- As: r.AS,
- Maxlen: uint32(r.MaxLen),
- Prefixlen: uint32(r.Prefix.Length),
- Prefix: r.Prefix.Prefix.String(),
- Conf: &api.RPKIConf{
- Address: host,
- RemotePort: port,
- },
- })
- }
- return l
-}
-
-func (s *Server) GetPolicy(ctx context.Context, arg *api.GetPolicyRequest) (*api.GetPolicyResponse, error) {
- l := make([]*api.Policy, 0)
- for _, p := range s.bgpServer.GetPolicy() {
- l = append(l, toPolicyApi(p))
- }
- return &api.GetPolicyResponse{Policies: l}, nil
-}
-
-func (s *Server) AddPolicy(ctx context.Context, arg *api.AddPolicyRequest) (*api.AddPolicyResponse, error) {
- if arg == nil || arg.Policy == nil {
- return nil, fmt.Errorf("invalid request")
- }
- x, err := NewPolicyFromApiStruct(arg.Policy)
- if err != nil {
- return nil, err
- }
- return &api.AddPolicyResponse{}, s.bgpServer.AddPolicy(x, arg.ReferExistingStatements)
-}
-
-func (s *Server) DeletePolicy(ctx context.Context, arg *api.DeletePolicyRequest) (*api.DeletePolicyResponse, error) {
- if arg == nil || arg.Policy == nil {
- return nil, fmt.Errorf("invalid request")
- }
- x, err := NewPolicyFromApiStruct(arg.Policy)
- if err != nil {
- return nil, err
- }
- return &api.DeletePolicyResponse{}, s.bgpServer.DeletePolicy(x, arg.All, arg.PreserveStatements)
-}
-
-func (s *Server) ReplacePolicy(ctx context.Context, arg *api.ReplacePolicyRequest) (*api.ReplacePolicyResponse, error) {
- if arg == nil || arg.Policy == nil {
- return nil, fmt.Errorf("invalid request")
- }
- x, err := NewPolicyFromApiStruct(arg.Policy)
- if err != nil {
- return nil, err
- }
- return &api.ReplacePolicyResponse{}, s.bgpServer.ReplacePolicy(x, arg.ReferExistingStatements, arg.PreserveStatements)
-}
-
-func toPolicyAssignmentName(a *api.PolicyAssignment) (string, table.PolicyDirection, error) {
- switch a.Resource {
- case api.Resource_GLOBAL:
- switch a.Type {
- case api.PolicyType_IMPORT:
- return "", table.POLICY_DIRECTION_IMPORT, nil
- case api.PolicyType_EXPORT:
- return "", table.POLICY_DIRECTION_EXPORT, nil
- default:
- return "", table.POLICY_DIRECTION_NONE, fmt.Errorf("invalid policy type")
- }
- case api.Resource_LOCAL:
- switch a.Type {
- case api.PolicyType_IMPORT:
- return a.Name, table.POLICY_DIRECTION_IMPORT, nil
- case api.PolicyType_EXPORT:
- return a.Name, table.POLICY_DIRECTION_EXPORT, nil
- default:
- return "", table.POLICY_DIRECTION_NONE, fmt.Errorf("invalid policy type")
- }
- default:
- return "", table.POLICY_DIRECTION_NONE, fmt.Errorf("invalid resource type")
- }
-
-}
-
-func (s *Server) GetPolicyAssignment(ctx context.Context, arg *api.GetPolicyAssignmentRequest) (*api.GetPolicyAssignmentResponse, error) {
- if arg == nil || arg.Assignment == nil {
- return nil, fmt.Errorf("invalid request")
- }
- name, dir, err := toPolicyAssignmentName(arg.Assignment)
- if err != nil {
- return nil, err
- }
- def, pols, err := s.bgpServer.GetPolicyAssignment(name, dir)
- if err != nil {
- return nil, err
- }
- policies := make([]*table.Policy, 0, len(pols))
- for _, p := range pols {
- t, err := table.NewPolicy(*p)
- if err != nil {
- return nil, err
- }
- policies = append(policies, t)
- }
- t := &table.PolicyAssignment{
- Name: name,
- Type: dir,
- Default: def,
- Policies: policies,
- }
- return &api.GetPolicyAssignmentResponse{NewAPIPolicyAssignmentFromTableStruct(t)}, err
-}
-
-func defaultRouteType(d api.RouteAction) table.RouteType {
- switch d {
- case api.RouteAction_ACCEPT:
- return table.ROUTE_TYPE_ACCEPT
- case api.RouteAction_REJECT:
- return table.ROUTE_TYPE_REJECT
- default:
- return table.ROUTE_TYPE_NONE
- }
-}
-
-func toPolicyDefinition(policies []*api.Policy) []*config.PolicyDefinition {
- l := make([]*config.PolicyDefinition, 0, len(policies))
- for _, p := range policies {
- l = append(l, &config.PolicyDefinition{Name: p.Name})
- }
- return l
-}
-
-func (s *Server) AddPolicyAssignment(ctx context.Context, arg *api.AddPolicyAssignmentRequest) (*api.AddPolicyAssignmentResponse, error) {
- if arg == nil || arg.Assignment == nil {
- return nil, fmt.Errorf("invalid request")
- }
- name, dir, err := toPolicyAssignmentName(arg.Assignment)
- if err != nil {
- return nil, err
- }
- return &api.AddPolicyAssignmentResponse{}, s.bgpServer.AddPolicyAssignment(name, dir, toPolicyDefinition(arg.Assignment.Policies), defaultRouteType(arg.Assignment.Default))
-}
-
-func (s *Server) DeletePolicyAssignment(ctx context.Context, arg *api.DeletePolicyAssignmentRequest) (*api.DeletePolicyAssignmentResponse, error) {
- if arg == nil || arg.Assignment == nil {
- return nil, fmt.Errorf("invalid request")
- }
- name, dir, err := toPolicyAssignmentName(arg.Assignment)
- if err != nil {
- return nil, err
- }
- return &api.DeletePolicyAssignmentResponse{}, s.bgpServer.DeletePolicyAssignment(name, dir, toPolicyDefinition(arg.Assignment.Policies), arg.All)
-}
-
-func (s *Server) ReplacePolicyAssignment(ctx context.Context, arg *api.ReplacePolicyAssignmentRequest) (*api.ReplacePolicyAssignmentResponse, error) {
- if arg == nil || arg.Assignment == nil {
- return nil, fmt.Errorf("invalid request")
- }
- name, dir, err := toPolicyAssignmentName(arg.Assignment)
- if err != nil {
- return nil, err
- }
- return &api.ReplacePolicyAssignmentResponse{}, s.bgpServer.ReplacePolicyAssignment(name, dir, toPolicyDefinition(arg.Assignment.Policies), defaultRouteType(arg.Assignment.Default))
-}
-
-func (s *Server) GetServer(ctx context.Context, arg *api.GetServerRequest) (*api.GetServerResponse, error) {
- g := s.bgpServer.GetServer()
- return &api.GetServerResponse{
- Global: &api.Global{
- As: g.Config.As,
- RouterId: g.Config.RouterId,
- ListenPort: g.Config.Port,
- ListenAddresses: g.Config.LocalAddressList,
- UseMultiplePaths: g.UseMultiplePaths.Config.Enabled,
- },
- }, nil
-}
-
-func NewGlobalFromAPIStruct(a *api.Global) *config.Global {
- families := make([]config.AfiSafi, 0, len(a.Families))
- for _, f := range a.Families {
- name := config.IntToAfiSafiTypeMap[int(f)]
- rf, _ := bgp.GetRouteFamily(string(name))
- families = append(families, config.AfiSafi{
- Config: config.AfiSafiConfig{
- AfiSafiName: name,
- Enabled: true,
- },
- State: config.AfiSafiState{
- AfiSafiName: name,
- Enabled: true,
- Family: rf,
- },
- })
- }
-
- applyPolicy := &config.ApplyPolicy{}
- ReadApplyPolicyFromAPIStruct(applyPolicy, a.ApplyPolicy)
-
- global := &config.Global{
- Config: config.GlobalConfig{
- As: a.As,
- RouterId: a.RouterId,
- Port: a.ListenPort,
- LocalAddressList: a.ListenAddresses,
- },
- ApplyPolicy: *applyPolicy,
- AfiSafis: families,
- UseMultiplePaths: config.UseMultiplePaths{
- Config: config.UseMultiplePathsConfig{
- Enabled: a.UseMultiplePaths,
- },
- },
- }
- if a.RouteSelectionOptions != nil {
- global.RouteSelectionOptions = config.RouteSelectionOptions{
- Config: config.RouteSelectionOptionsConfig{
- AlwaysCompareMed: a.RouteSelectionOptions.AlwaysCompareMed,
- IgnoreAsPathLength: a.RouteSelectionOptions.IgnoreAsPathLength,
- ExternalCompareRouterId: a.RouteSelectionOptions.ExternalCompareRouterId,
- AdvertiseInactiveRoutes: a.RouteSelectionOptions.AdvertiseInactiveRoutes,
- EnableAigp: a.RouteSelectionOptions.EnableAigp,
- IgnoreNextHopIgpMetric: a.RouteSelectionOptions.IgnoreNextHopIgpMetric,
- DisableBestPathSelection: a.RouteSelectionOptions.DisableBestPathSelection,
- },
- }
- }
- if a.DefaultRouteDistance != nil {
- global.DefaultRouteDistance = config.DefaultRouteDistance{
- Config: config.DefaultRouteDistanceConfig{
- ExternalRouteDistance: uint8(a.DefaultRouteDistance.ExternalRouteDistance),
- InternalRouteDistance: uint8(a.DefaultRouteDistance.InternalRouteDistance),
- },
- }
- }
- if a.Confederation != nil {
- global.Confederation = config.Confederation{
- Config: config.ConfederationConfig{
- Enabled: a.Confederation.Enabled,
- Identifier: a.Confederation.Identifier,
- MemberAsList: a.Confederation.MemberAsList,
- },
- }
- }
- if a.GracefulRestart != nil {
- global.GracefulRestart = config.GracefulRestart{
- Config: config.GracefulRestartConfig{
- Enabled: a.GracefulRestart.Enabled,
- RestartTime: uint16(a.GracefulRestart.RestartTime),
- StaleRoutesTime: float64(a.GracefulRestart.StaleRoutesTime),
- HelperOnly: a.GracefulRestart.HelperOnly,
- DeferralTime: uint16(a.GracefulRestart.DeferralTime),
- NotificationEnabled: a.GracefulRestart.NotificationEnabled,
- LongLivedEnabled: a.GracefulRestart.LonglivedEnabled,
- },
- }
- }
- return global
-}
-
-func NewGlobalFromConfigStruct(c *config.Global) *api.Global {
- families := make([]uint32, 0, len(c.AfiSafis))
- for _, f := range c.AfiSafis {
- families = append(families, uint32(config.AfiSafiTypeToIntMap[f.Config.AfiSafiName]))
- }
-
- applyPolicy := NewApplyPolicyFromConfigStruct(&c.ApplyPolicy)
-
- return &api.Global{
- As: c.Config.As,
- RouterId: c.Config.RouterId,
- ListenPort: c.Config.Port,
- ListenAddresses: c.Config.LocalAddressList,
- Families: families,
- UseMultiplePaths: c.UseMultiplePaths.Config.Enabled,
- RouteSelectionOptions: &api.RouteSelectionOptionsConfig{
- AlwaysCompareMed: c.RouteSelectionOptions.Config.AlwaysCompareMed,
- IgnoreAsPathLength: c.RouteSelectionOptions.Config.IgnoreAsPathLength,
- ExternalCompareRouterId: c.RouteSelectionOptions.Config.ExternalCompareRouterId,
- AdvertiseInactiveRoutes: c.RouteSelectionOptions.Config.AdvertiseInactiveRoutes,
- EnableAigp: c.RouteSelectionOptions.Config.EnableAigp,
- IgnoreNextHopIgpMetric: c.RouteSelectionOptions.Config.IgnoreNextHopIgpMetric,
- DisableBestPathSelection: c.RouteSelectionOptions.Config.DisableBestPathSelection,
- },
- DefaultRouteDistance: &api.DefaultRouteDistance{
- ExternalRouteDistance: uint32(c.DefaultRouteDistance.Config.ExternalRouteDistance),
- InternalRouteDistance: uint32(c.DefaultRouteDistance.Config.InternalRouteDistance),
- },
- Confederation: &api.Confederation{
- Enabled: c.Confederation.Config.Enabled,
- Identifier: c.Confederation.Config.Identifier,
- MemberAsList: c.Confederation.Config.MemberAsList,
- },
- GracefulRestart: &api.GracefulRestart{
- Enabled: c.GracefulRestart.Config.Enabled,
- RestartTime: uint32(c.GracefulRestart.Config.RestartTime),
- StaleRoutesTime: uint32(c.GracefulRestart.Config.StaleRoutesTime),
- HelperOnly: c.GracefulRestart.Config.HelperOnly,
- DeferralTime: uint32(c.GracefulRestart.Config.DeferralTime),
- NotificationEnabled: c.GracefulRestart.Config.NotificationEnabled,
- LonglivedEnabled: c.GracefulRestart.Config.LongLivedEnabled,
- },
- ApplyPolicy: applyPolicy,
- }
-}
-
-func (s *Server) StartServer(ctx context.Context, arg *api.StartServerRequest) (*api.StartServerResponse, error) {
- if arg == nil || arg.Global == nil {
- return nil, fmt.Errorf("invalid request")
- }
- g := arg.Global
- if net.ParseIP(g.RouterId) == nil {
- return nil, fmt.Errorf("invalid router-id format: %s", g.RouterId)
- }
-
- global := NewGlobalFromAPIStruct(arg.Global)
-
- return &api.StartServerResponse{}, s.bgpServer.Start(global)
-}
-
-func (s *Server) StopServer(ctx context.Context, arg *api.StopServerRequest) (*api.StopServerResponse, error) {
- return &api.StopServerResponse{}, s.bgpServer.Stop()
-}
-
-func (s *Server) GetRibInfo(ctx context.Context, arg *api.GetRibInfoRequest) (*api.GetRibInfoResponse, error) {
- if arg == nil || arg.Info == nil {
- return nil, fmt.Errorf("invalid request")
- }
- family := bgp.RouteFamily(arg.Info.Family)
- var in bool
- var err error
- var info *table.TableInfo
- switch arg.Info.Type {
- case api.Resource_GLOBAL, api.Resource_LOCAL:
- info, err = s.bgpServer.GetRibInfo(arg.Info.Name, family)
- case api.Resource_ADJ_IN:
- in = true
- fallthrough
- case api.Resource_ADJ_OUT:
- info, err = s.bgpServer.GetAdjRibInfo(arg.Info.Name, family, in)
- default:
- return nil, fmt.Errorf("unsupported resource type: %s", arg.Info.Type)
- }
-
- if err != nil {
- return nil, err
- }
-
- return &api.GetRibInfoResponse{
- Info: &api.TableInfo{
- Type: arg.Info.Type,
- Family: arg.Info.Family,
- Name: arg.Info.Name,
- NumDestination: uint64(info.NumDestination),
- NumPath: uint64(info.NumPath),
- NumAccepted: uint64(info.NumAccepted),
- },
- }, nil
-}
-
-func (s *Server) AddCollector(ctx context.Context, arg *api.AddCollectorRequest) (*api.AddCollectorResponse, error) {
- return &api.AddCollectorResponse{}, s.bgpServer.AddCollector(&config.CollectorConfig{
- Url: arg.Url,
- DbName: arg.DbName,
- TableDumpInterval: arg.TableDumpInterval,
- })
-}
-
-func (s *Server) Shutdown(ctx context.Context, arg *api.ShutdownRequest) (*api.ShutdownResponse, error) {
- s.bgpServer.Shutdown()
- return &api.ShutdownResponse{}, nil
-}
diff --git a/server/mrt.go b/server/mrt.go
deleted file mode 100644
index 54e774d3..00000000
--- a/server/mrt.go
+++ /dev/null
@@ -1,409 +0,0 @@
-// Copyright (C) 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 (
- "bytes"
- "fmt"
- "os"
- "time"
-
- log "github.com/sirupsen/logrus"
-
- "github.com/osrg/gobgp/config"
- "github.com/osrg/gobgp/packet/bgp"
- "github.com/osrg/gobgp/packet/mrt"
- "github.com/osrg/gobgp/table"
-)
-
-const (
- MIN_ROTATION_INTERVAL = 60
- MIN_DUMP_INTERVAL = 60
-)
-
-type mrtWriter struct {
- dead chan struct{}
- s *BgpServer
- c *config.MrtConfig
- file *os.File
- rotationInterval uint64
- dumpInterval uint64
-}
-
-func (m *mrtWriter) Stop() {
- close(m.dead)
-}
-
-func (m *mrtWriter) loop() error {
- ops := []WatchOption{}
- switch m.c.DumpType {
- case config.MRT_TYPE_UPDATES:
- ops = append(ops, WatchUpdate(false))
- case config.MRT_TYPE_TABLE:
- if len(m.c.TableName) > 0 {
- ops = append(ops, WatchTableName(m.c.TableName))
- }
- }
- w := m.s.Watch(ops...)
- rotator := func() *time.Ticker {
- if m.rotationInterval == 0 {
- return &time.Ticker{}
- }
- return time.NewTicker(time.Second * time.Duration(m.rotationInterval))
- }()
- dump := func() *time.Ticker {
- if m.dumpInterval == 0 {
- return &time.Ticker{}
- }
- return time.NewTicker(time.Second * time.Duration(m.dumpInterval))
- }()
-
- defer func() {
- if m.file != nil {
- m.file.Close()
- }
- if m.rotationInterval != 0 {
- rotator.Stop()
- }
- if m.dumpInterval != 0 {
- dump.Stop()
- }
- w.Stop()
- }()
-
- for {
- serialize := func(ev WatchEvent) []*mrt.MRTMessage {
- msg := make([]*mrt.MRTMessage, 0, 1)
- switch e := ev.(type) {
- case *WatchEventUpdate:
- if e.Init {
- return nil
- }
- mp := mrt.NewBGP4MPMessage(e.PeerAS, e.LocalAS, 0, e.PeerAddress.String(), e.LocalAddress.String(), e.FourBytesAs, nil)
- mp.BGPMessagePayload = e.Payload
- isAddPath := e.Neighbor.IsAddPathReceiveEnabled(e.PathList[0].GetRouteFamily())
- subtype := mrt.MESSAGE
- switch {
- case isAddPath && e.FourBytesAs:
- subtype = mrt.MESSAGE_AS4_ADDPATH
- case isAddPath:
- subtype = mrt.MESSAGE_ADDPATH
- case e.FourBytesAs:
- subtype = mrt.MESSAGE_AS4
- }
- if bm, err := mrt.NewMRTMessage(uint32(e.Timestamp.Unix()), mrt.BGP4MP, subtype, mp); err != nil {
- log.WithFields(log.Fields{
- "Topic": "mrt",
- "Data": e,
- "Error": err,
- }).Warnf("Failed to create MRT BGP4MP message (subtype %d)", subtype)
- } else {
- msg = append(msg, bm)
- }
- case *WatchEventTable:
- t := uint32(time.Now().Unix())
-
- peers := make([]*mrt.Peer, 1, len(e.Neighbor)+1)
- // Adding dummy Peer record for locally generated routes
- peers[0] = mrt.NewPeer("0.0.0.0", "0.0.0.0", 0, true)
- neighborMap := make(map[string]*config.Neighbor)
- for _, pconf := range e.Neighbor {
- peers = append(peers, mrt.NewPeer(pconf.State.RemoteRouterId, pconf.State.NeighborAddress, pconf.Config.PeerAs, true))
- neighborMap[pconf.State.NeighborAddress] = pconf
- }
-
- if bm, err := mrt.NewMRTMessage(t, mrt.TABLE_DUMPv2, mrt.PEER_INDEX_TABLE, mrt.NewPeerIndexTable(e.RouterId, "", peers)); err != nil {
- log.WithFields(log.Fields{
- "Topic": "mrt",
- "Data": e,
- "Error": err,
- }).Warnf("Failed to create MRT TABLE_DUMPv2 message (subtype %d)", mrt.PEER_INDEX_TABLE)
- break
- } else {
- msg = append(msg, bm)
- }
-
- idx := func(p *table.Path) uint16 {
- for i, pconf := range e.Neighbor {
- if p.GetSource().Address.String() == pconf.State.NeighborAddress {
- return uint16(i)
- }
- }
- return uint16(len(e.Neighbor))
- }
-
- subtype := func(p *table.Path, isAddPath bool) mrt.MRTSubTypeTableDumpv2 {
- t := mrt.RIB_GENERIC
- switch p.GetRouteFamily() {
- case bgp.RF_IPv4_UC:
- t = mrt.RIB_IPV4_UNICAST
- case bgp.RF_IPv4_MC:
- t = mrt.RIB_IPV4_MULTICAST
- case bgp.RF_IPv6_UC:
- t = mrt.RIB_IPV6_UNICAST
- case bgp.RF_IPv6_MC:
- t = mrt.RIB_IPV6_MULTICAST
- }
- if isAddPath {
- // Shift non-additional-path version to *_ADDPATH
- t += 6
- }
- return t
- }
-
- seq := uint32(0)
- appendTableDumpMsg := func(path *table.Path, entries []*mrt.RibEntry, isAddPath bool) {
- st := subtype(path, isAddPath)
- if bm, err := mrt.NewMRTMessage(t, mrt.TABLE_DUMPv2, st, mrt.NewRib(seq, path.GetNlri(), entries)); err != nil {
- log.WithFields(log.Fields{
- "Topic": "mrt",
- "Data": e,
- "Error": err,
- }).Warnf("Failed to create MRT TABLE_DUMPv2 message (subtype %d)", st)
- } else {
- msg = append(msg, bm)
- seq++
- }
- }
- for _, pathList := range e.PathList {
- entries := make([]*mrt.RibEntry, 0, len(pathList))
- entriesAddPath := make([]*mrt.RibEntry, 0, len(pathList))
- for _, path := range pathList {
- isAddPath := false
- if path.IsLocal() {
- isAddPath = true
- } else if neighbor, ok := neighborMap[path.GetSource().Address.String()]; ok {
- isAddPath = neighbor.IsAddPathReceiveEnabled(path.GetRouteFamily())
- }
- if !isAddPath {
- entries = append(entries, mrt.NewRibEntry(idx(path), uint32(path.GetTimestamp().Unix()), 0, path.GetPathAttrs(), false))
- } else {
- entriesAddPath = append(entriesAddPath, mrt.NewRibEntry(idx(path), uint32(path.GetTimestamp().Unix()), path.GetNlri().PathIdentifier(), path.GetPathAttrs(), true))
- }
- }
- if len(entries) > 0 {
- appendTableDumpMsg(pathList[0], entries, false)
- }
- if len(entriesAddPath) > 0 {
- appendTableDumpMsg(pathList[0], entriesAddPath, true)
- }
- }
- }
- return msg
- }
-
- drain := func(ev WatchEvent) {
- events := make([]WatchEvent, 0, 1+len(w.Event()))
- if ev != nil {
- events = append(events, ev)
- }
-
- for len(w.Event()) > 0 {
- events = append(events, <-w.Event())
- }
-
- w := func(buf []byte) {
- if _, err := m.file.Write(buf); err == nil {
- m.file.Sync()
- } else {
- log.WithFields(log.Fields{
- "Topic": "mrt",
- "Error": err,
- }).Warn("Can't write to destination MRT file")
- }
- }
-
- var b bytes.Buffer
- for _, e := range events {
- for _, m := range serialize(e) {
- if buf, err := m.Serialize(); err != nil {
- log.WithFields(log.Fields{
- "Topic": "mrt",
- "Data": e,
- "Error": err,
- }).Warn("Failed to serialize event")
- } else {
- b.Write(buf)
- if b.Len() > 1*1000*1000 {
- w(b.Bytes())
- b.Reset()
- }
- }
- }
- }
- if b.Len() > 0 {
- w(b.Bytes())
- }
- }
- rotate := func() {
- m.file.Close()
- file, err := mrtFileOpen(m.c.FileName, m.rotationInterval)
- if err == nil {
- m.file = file
- } else {
- log.WithFields(log.Fields{
- "Topic": "mrt",
- "Error": err,
- }).Warn("can't rotate MRT file")
- }
- }
-
- select {
- case <-m.dead:
- drain(nil)
- return nil
- case e := <-w.Event():
- drain(e)
- if m.c.DumpType == config.MRT_TYPE_TABLE && m.rotationInterval != 0 {
- rotate()
- }
- case <-rotator.C:
- if m.c.DumpType == config.MRT_TYPE_UPDATES {
- rotate()
- } else {
- w.Generate(WATCH_EVENT_TYPE_TABLE)
- }
- case <-dump.C:
- w.Generate(WATCH_EVENT_TYPE_TABLE)
- }
- }
-}
-
-func mrtFileOpen(filename string, interval uint64) (*os.File, error) {
- realname := filename
- if interval != 0 {
- realname = time.Now().Format(filename)
- }
- log.WithFields(log.Fields{
- "Topic": "mrt",
- "Filename": realname,
- "Dump Interval": interval,
- }).Debug("Setting new MRT destination file")
-
- i := len(realname)
- for i > 0 && os.IsPathSeparator(realname[i-1]) {
- // skip trailing path separators
- i--
- }
- j := i
-
- for j > 0 && !os.IsPathSeparator(realname[j-1]) {
- j--
- }
-
- if j > 0 {
- if err := os.MkdirAll(realname[0:j-1], 0755); err != nil {
- log.WithFields(log.Fields{
- "Topic": "mrt",
- "Error": err,
- }).Warn("can't create MRT destination directory")
- return nil, err
- }
- }
-
- file, err := os.OpenFile(realname, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644)
- if err != nil {
- log.WithFields(log.Fields{
- "Topic": "mrt",
- "Error": err,
- }).Warn("can't create MRT destination file")
- }
- return file, err
-}
-
-func newMrtWriter(s *BgpServer, c *config.MrtConfig, rInterval, dInterval uint64) (*mrtWriter, error) {
- file, err := mrtFileOpen(c.FileName, rInterval)
- if err != nil {
- return nil, err
- }
- m := mrtWriter{
- s: s,
- c: c,
- file: file,
- rotationInterval: rInterval,
- dumpInterval: dInterval,
- }
- go m.loop()
- return &m, nil
-}
-
-type mrtManager struct {
- bgpServer *BgpServer
- writer map[string]*mrtWriter
-}
-
-func (m *mrtManager) enable(c *config.MrtConfig) error {
- if _, ok := m.writer[c.FileName]; ok {
- return fmt.Errorf("%s already exists", c.FileName)
- }
-
- rInterval := c.RotationInterval
- dInterval := c.DumpInterval
-
- setRotationMin := func() {
- if rInterval < MIN_ROTATION_INTERVAL {
- log.WithFields(log.Fields{
- "Topic": "MRT",
- }).Infof("minimum mrt rotation interval is %d seconds", MIN_ROTATION_INTERVAL)
- rInterval = MIN_ROTATION_INTERVAL
- }
- }
-
- if c.DumpType == config.MRT_TYPE_TABLE {
- if rInterval == 0 {
- if dInterval < MIN_DUMP_INTERVAL {
- log.WithFields(log.Fields{
- "Topic": "MRT",
- }).Infof("minimum mrt dump interval is %d seconds", MIN_DUMP_INTERVAL)
- dInterval = MIN_DUMP_INTERVAL
- }
- } else if dInterval == 0 {
- setRotationMin()
- } else {
- return fmt.Errorf("can't specify both intervals in the table dump type")
- }
- } else if c.DumpType == config.MRT_TYPE_UPDATES {
- // ignore the dump interval
- dInterval = 0
- if len(c.TableName) > 0 {
- return fmt.Errorf("can't specify the table name with the update dump type")
- }
- setRotationMin()
- }
-
- w, err := newMrtWriter(m.bgpServer, c, rInterval, dInterval)
- if err == nil {
- m.writer[c.FileName] = w
- }
- return err
-}
-
-func (m *mrtManager) disable(c *config.MrtConfig) error {
- w, ok := m.writer[c.FileName]
- if !ok {
- return fmt.Errorf("%s doesn't exists", c.FileName)
- }
- w.Stop()
- delete(m.writer, c.FileName)
- return nil
-}
-
-func newMrtManager(s *BgpServer) *mrtManager {
- return &mrtManager{
- bgpServer: s,
- writer: make(map[string]*mrtWriter),
- }
-}
diff --git a/server/peer.go b/server/peer.go
deleted file mode 100644
index 3a43fac9..00000000
--- a/server/peer.go
+++ /dev/null
@@ -1,522 +0,0 @@
-// Copyright (C) 2014-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"
- "time"
-
- "github.com/eapache/channels"
- log "github.com/sirupsen/logrus"
-
- "github.com/osrg/gobgp/config"
- "github.com/osrg/gobgp/packet/bgp"
- "github.com/osrg/gobgp/table"
-)
-
-const (
- FLOP_THRESHOLD = time.Second * 30
- MIN_CONNECT_RETRY = 10
-)
-
-type PeerGroup struct {
- 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),
- dynamicNeighbors: make(map[string]*config.DynamicNeighbor),
- }
-}
-
-func (pg *PeerGroup) AddMember(c config.Neighbor) {
- pg.members[c.State.NeighborAddress] = c
-}
-
-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,
- },
- State: config.NeighborState{
- NeighborAddress: neighborAddress,
- },
- Transport: config.Transport{
- Config: config.TransportConfig{
- PassiveMode: true,
- },
- },
- }
- if err := config.OverwriteNeighborConfigWithPeerGroup(&conf, pg); err != nil {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": neighborAddress,
- }).Debugf("Can't overwrite neighbor config: %s", err)
- return nil
- }
- if err := config.SetDefaultNeighborConfigValues(&conf, pg, g); err != nil {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": neighborAddress,
- }).Debugf("Can't set default config: %s", err)
- return nil
- }
- peer := NewPeer(g, &conf, loc, policy)
- peer.fsm.state = bgp.BGP_FSM_ACTIVE
- return peer
-}
-
-type Peer struct {
- tableId string
- fsm *FSM
- adjRibIn *table.AdjRib
- outgoing *channels.InfiniteChannel
- policy *table.RoutingPolicy
- localRib *table.TableManager
- prefixLimitWarned map[bgp.RouteFamily]bool
- llgrEndChs []chan struct{}
-}
-
-func NewPeer(g *config.Global, conf *config.Neighbor, loc *table.TableManager, policy *table.RoutingPolicy) *Peer {
- peer := &Peer{
- outgoing: channels.NewInfiniteChannel(),
- localRib: loc,
- policy: policy,
- fsm: NewFSM(g, conf, policy),
- prefixLimitWarned: make(map[bgp.RouteFamily]bool),
- }
- if peer.isRouteServerClient() {
- peer.tableId = conf.State.NeighborAddress
- } else {
- peer.tableId = table.GLOBAL_RIB_NAME
- }
- rfs, _ := config.AfiSafis(conf.AfiSafis).ToRfList()
- peer.adjRibIn = table.NewAdjRib(rfs)
- return peer
-}
-
-func (peer *Peer) AS() uint32 {
- return peer.fsm.pConf.State.PeerAs
-}
-
-func (peer *Peer) ID() string {
- return peer.fsm.pConf.State.NeighborAddress
-}
-
-func (peer *Peer) TableID() string {
- return peer.tableId
-}
-
-func (peer *Peer) isIBGPPeer() bool {
- return peer.fsm.pConf.State.PeerAs == peer.fsm.gConf.Config.As
-}
-
-func (peer *Peer) isRouteServerClient() bool {
- return peer.fsm.pConf.RouteServer.Config.RouteServerClient
-}
-
-func (peer *Peer) isRouteReflectorClient() bool {
- return peer.fsm.pConf.RouteReflector.Config.RouteReflectorClient
-}
-
-func (peer *Peer) isGracefulRestartEnabled() bool {
- return peer.fsm.pConf.GracefulRestart.State.Enabled
-}
-
-func (peer *Peer) getAddPathMode(family bgp.RouteFamily) bgp.BGPAddPathMode {
- if mode, y := peer.fsm.rfMap[family]; y {
- return mode
- }
- return bgp.BGP_ADD_PATH_NONE
-}
-
-func (peer *Peer) isAddPathReceiveEnabled(family bgp.RouteFamily) bool {
- return (peer.getAddPathMode(family) & bgp.BGP_ADD_PATH_RECEIVE) > 0
-}
-
-func (peer *Peer) isAddPathSendEnabled(family bgp.RouteFamily) bool {
- return (peer.getAddPathMode(family) & bgp.BGP_ADD_PATH_SEND) > 0
-}
-
-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 {
- return false
- }
- }
- return true
-}
-
-func (peer *Peer) configuredRFlist() []bgp.RouteFamily {
- rfs, _ := config.AfiSafis(peer.fsm.pConf.AfiSafis).ToRfList()
- return rfs
-}
-
-func (peer *Peer) negotiatedRFList() []bgp.RouteFamily {
- l := make([]bgp.RouteFamily, 0, len(peer.fsm.rfMap))
- for family, _ := range peer.fsm.rfMap {
- l = append(l, family)
- }
- return l
-}
-
-func (peer *Peer) toGlobalFamilies(families []bgp.RouteFamily) []bgp.RouteFamily {
- if peer.fsm.pConf.Config.Vrf != "" {
- fs := make([]bgp.RouteFamily, 0, len(families))
- for _, f := range families {
- switch f {
- case bgp.RF_IPv4_UC:
- fs = append(fs, bgp.RF_IPv4_VPN)
- case bgp.RF_IPv6_UC:
- fs = append(fs, bgp.RF_IPv6_VPN)
- default:
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": peer.ID(),
- "Family": f,
- "VRF": peer.fsm.pConf.Config.Vrf,
- }).Warn("invalid family configured for neighbor with vrf")
- }
- }
- families = fs
- }
- return families
-}
-
-func classifyFamilies(all, part []bgp.RouteFamily) ([]bgp.RouteFamily, []bgp.RouteFamily) {
- a := []bgp.RouteFamily{}
- b := []bgp.RouteFamily{}
- for _, f := range all {
- p := true
- for _, g := range part {
- if f == g {
- p = false
- a = append(a, f)
- break
- }
- }
- if p {
- b = append(b, f)
- }
- }
- return a, b
-}
-
-func (peer *Peer) forwardingPreservedFamilies() ([]bgp.RouteFamily, []bgp.RouteFamily) {
- list := []bgp.RouteFamily{}
- for _, a := range peer.fsm.pConf.AfiSafis {
- if s := a.MpGracefulRestart.State; s.Enabled && s.Received {
- list = append(list, a.State.Family)
- }
- }
- return classifyFamilies(peer.configuredRFlist(), list)
-}
-
-func (peer *Peer) llgrFamilies() ([]bgp.RouteFamily, []bgp.RouteFamily) {
- list := []bgp.RouteFamily{}
- for _, a := range peer.fsm.pConf.AfiSafis {
- if a.LongLivedGracefulRestart.State.Enabled {
- list = append(list, a.State.Family)
- }
- }
- return classifyFamilies(peer.configuredRFlist(), list)
-}
-
-func (peer *Peer) isLLGREnabledFamily(family bgp.RouteFamily) bool {
- if !peer.fsm.pConf.GracefulRestart.Config.LongLivedEnabled {
- return false
- }
- fs, _ := peer.llgrFamilies()
- for _, f := range fs {
- if f == family {
- return true
- }
- }
- return false
-}
-
-func (peer *Peer) llgrRestartTime(family bgp.RouteFamily) uint32 {
- for _, a := range peer.fsm.pConf.AfiSafis {
- if a.State.Family == family {
- return a.LongLivedGracefulRestart.State.PeerRestartTime
- }
- }
- return 0
-}
-
-func (peer *Peer) llgrRestartTimerExpired(family bgp.RouteFamily) bool {
- all := true
- for _, a := range peer.fsm.pConf.AfiSafis {
- if a.State.Family == family {
- a.LongLivedGracefulRestart.State.PeerRestartTimerExpired = true
- }
- s := a.LongLivedGracefulRestart.State
- if s.Received && !s.PeerRestartTimerExpired {
- all = false
- }
- }
- return all
-}
-
-func (peer *Peer) markLLGRStale(fs []bgp.RouteFamily) []*table.Path {
- paths := peer.adjRibIn.PathList(fs, true)
- for i, p := range paths {
- doStale := true
- for _, c := range p.GetCommunities() {
- if c == uint32(bgp.COMMUNITY_NO_LLGR) {
- doStale = false
- p = p.Clone(true)
- break
- }
- }
- if doStale {
- p = p.Clone(false)
- p.SetCommunities([]uint32{uint32(bgp.COMMUNITY_LLGR_STALE)}, false)
- }
- paths[i] = p
- }
- return paths
-}
-
-func (peer *Peer) stopPeerRestarting() {
- peer.fsm.pConf.GracefulRestart.State.PeerRestarting = false
- for _, ch := range peer.llgrEndChs {
- close(ch)
- }
- peer.llgrEndChs = make([]chan struct{}, 0)
-
-}
-
-func (peer *Peer) filterPathFromSourcePeer(path, old *table.Path) *table.Path {
- if peer.ID() != path.GetSource().Address.String() {
- return path
- }
-
- // Note: Multiple paths having the same prefix could exist the withdrawals
- // list in the case of Route Server setup with import policies modifying
- // paths. In such case, gobgp sends duplicated update messages; withdraw
- // messages for the same prefix.
- if !peer.isRouteServerClient() {
- if peer.isRouteReflectorClient() && path.GetRouteFamily() == bgp.RF_RTC_UC {
- // When the peer is a Route Reflector client and the given path
- // contains the Route Tartget Membership NLRI, the path should not
- // be withdrawn in order to signal the client to distribute routes
- // with the specific RT to Route Reflector.
- return path
- } else if !path.IsWithdraw && old != nil && old.GetSource().Address.String() != peer.ID() {
- // Say, peer A and B advertized same prefix P, and best path
- // calculation chose a path from B as best. When B withdraws prefix
- // P, best path calculation chooses the path from A as best. For
- // peers other than A, this path should be advertised (as implicit
- // withdrawal). However for A, we should advertise the withdrawal
- // path. Thing is same when peer A and we advertized prefix P (as
- // local route), then, we withdraws the prefix.
- return old.Clone(true)
- }
- }
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": peer.ID(),
- "Data": path,
- }).Debug("From me, ignore.")
- return nil
-}
-
-func (peer *Peer) doPrefixLimit(k bgp.RouteFamily, c *config.PrefixLimitConfig) *bgp.BGPMessage {
- if maxPrefixes := int(c.MaxPrefixes); maxPrefixes > 0 {
- count := peer.adjRibIn.Count([]bgp.RouteFamily{k})
- pct := int(c.ShutdownThresholdPct)
- if pct > 0 && !peer.prefixLimitWarned[k] && count > (maxPrefixes*pct/100) {
- peer.prefixLimitWarned[k] = true
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": peer.ID(),
- "AddressFamily": k.String(),
- }).Warnf("prefix limit %d%% reached", pct)
- }
- if count > maxPrefixes {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": peer.ID(),
- "AddressFamily": k.String(),
- }).Warnf("prefix limit reached")
- return bgp.NewBGPNotificationMessage(bgp.BGP_ERROR_CEASE, bgp.BGP_ERROR_SUB_MAXIMUM_NUMBER_OF_PREFIXES_REACHED, nil)
- }
- }
- return nil
-
-}
-
-func (peer *Peer) updatePrefixLimitConfig(c []config.AfiSafi) error {
- x := peer.fsm.pConf.AfiSafis
- y := c
- if len(x) != len(y) {
- return fmt.Errorf("changing supported afi-safi is not allowed")
- }
- m := make(map[bgp.RouteFamily]config.PrefixLimitConfig)
- for _, e := range x {
- m[e.State.Family] = e.PrefixLimit.Config
- }
- for _, e := range y {
- if p, ok := m[e.State.Family]; !ok {
- return fmt.Errorf("changing supported afi-safi is not allowed")
- } else if !p.Equal(&e.PrefixLimit.Config) {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": peer.ID(),
- "AddressFamily": e.Config.AfiSafiName,
- "OldMaxPrefixes": p.MaxPrefixes,
- "NewMaxPrefixes": e.PrefixLimit.Config.MaxPrefixes,
- "OldShutdownThresholdPct": p.ShutdownThresholdPct,
- "NewShutdownThresholdPct": e.PrefixLimit.Config.ShutdownThresholdPct,
- }).Warnf("update prefix limit configuration")
- peer.prefixLimitWarned[e.State.Family] = false
- if msg := peer.doPrefixLimit(e.State.Family, &e.PrefixLimit.Config); msg != nil {
- sendFsmOutgoingMsg(peer, nil, msg, true)
- }
- }
- }
- peer.fsm.pConf.AfiSafis = c
- return nil
-}
-
-func (peer *Peer) handleUpdate(e *FsmMsg) ([]*table.Path, []bgp.RouteFamily, *bgp.BGPMessage) {
- m := e.MsgData.(*bgp.BGPMessage)
- update := m.Body.(*bgp.BGPUpdate)
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": peer.fsm.pConf.State.NeighborAddress,
- "nlri": update.NLRI,
- "withdrawals": update.WithdrawnRoutes,
- "attributes": update.PathAttributes,
- }).Debug("received update")
- peer.fsm.pConf.Timers.State.UpdateRecvTime = time.Now().Unix()
- if len(e.PathList) > 0 {
- paths := make([]*table.Path, 0, len(e.PathList))
- eor := []bgp.RouteFamily{}
- for _, path := range e.PathList {
- if path.IsEOR() {
- family := path.GetRouteFamily()
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": peer.ID(),
- "AddressFamily": family,
- }).Debug("EOR received")
- eor = append(eor, family)
- continue
- }
- // RFC4271 9.1.2 Phase 2: Route Selection
- //
- // If the AS_PATH attribute of a BGP route contains an AS loop, the BGP
- // route should be excluded from the Phase 2 decision function.
- if aspath := path.GetAsPath(); aspath != nil {
- if hasOwnASLoop(peer.fsm.peerInfo.LocalAS, int(peer.fsm.pConf.AsPathOptions.Config.AllowOwnAs), aspath) {
- path.SetAsLooped(true)
- continue
- }
- }
- paths = append(paths, path)
- }
- peer.adjRibIn.Update(e.PathList)
- for _, af := range peer.fsm.pConf.AfiSafis {
- if msg := peer.doPrefixLimit(af.State.Family, &af.PrefixLimit.Config); msg != nil {
- return nil, nil, msg
- }
- }
- return paths, eor, nil
- }
- return nil, nil, nil
-}
-
-func (peer *Peer) startFSMHandler(incoming *channels.InfiniteChannel, stateCh chan *FsmMsg) {
- peer.fsm.h = NewFSMHandler(peer.fsm, incoming, stateCh, peer.outgoing)
-}
-
-func (peer *Peer) StaleAll(rfList []bgp.RouteFamily) []*table.Path {
- return peer.adjRibIn.StaleAll(rfList)
-}
-
-func (peer *Peer) PassConn(conn *net.TCPConn) {
- select {
- case peer.fsm.connCh <- conn:
- default:
- conn.Close()
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": peer.ID(),
- }).Warn("accepted conn is closed to avoid be blocked")
- }
-}
-
-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/rpki.go b/server/rpki.go
deleted file mode 100644
index 6356888a..00000000
--- a/server/rpki.go
+++ /dev/null
@@ -1,712 +0,0 @@
-// 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 (
- "encoding/binary"
- "fmt"
- "io"
- "net"
- "sort"
- "strconv"
- "time"
-
- "github.com/armon/go-radix"
- log "github.com/sirupsen/logrus"
- "golang.org/x/net/context"
-
- "github.com/osrg/gobgp/config"
- "github.com/osrg/gobgp/packet/bgp"
- "github.com/osrg/gobgp/packet/rtr"
- "github.com/osrg/gobgp/table"
-)
-
-const (
- CONNECT_RETRY_INTERVAL = 30
-)
-
-func before(a, b uint32) bool {
- return int32(a-b) < 0
-}
-
-type RoaBucket struct {
- Prefix *table.IPPrefix
- entries []*table.ROA
-}
-
-func (r *RoaBucket) GetEntries() []*table.ROA {
- return r.entries
-}
-
-type roas []*table.ROA
-
-func (r roas) Len() int {
- return len(r)
-}
-
-func (r roas) Swap(i, j int) {
- r[i], r[j] = r[j], r[i]
-}
-
-func (r roas) Less(i, j int) bool {
- r1 := r[i]
- r2 := r[j]
-
- if r1.MaxLen < r2.MaxLen {
- return true
- } else if r1.MaxLen > r2.MaxLen {
- return false
- }
-
- if r1.AS < r2.AS {
- return true
- }
- return false
-}
-
-type ROAEventType uint8
-
-const (
- CONNECTED ROAEventType = iota
- DISCONNECTED
- RTR
- LIFETIMEOUT
-)
-
-type ROAEvent struct {
- EventType ROAEventType
- Src string
- Data []byte
- conn *net.TCPConn
-}
-
-type roaManager struct {
- AS uint32
- Roas map[bgp.RouteFamily]*radix.Tree
- eventCh chan *ROAEvent
- clientMap map[string]*roaClient
-}
-
-func NewROAManager(as uint32) (*roaManager, error) {
- m := &roaManager{
- AS: as,
- Roas: make(map[bgp.RouteFamily]*radix.Tree),
- }
- m.Roas[bgp.RF_IPv4_UC] = radix.New()
- m.Roas[bgp.RF_IPv6_UC] = radix.New()
- m.eventCh = make(chan *ROAEvent)
- m.clientMap = make(map[string]*roaClient)
- return m, nil
-}
-
-func (c *roaManager) enabled() bool {
- return len(c.clientMap) != 0
-}
-
-func (m *roaManager) SetAS(as uint32) error {
- if m.AS != 0 {
- return fmt.Errorf("AS was already configured")
- }
- m.AS = as
- return nil
-}
-
-func (m *roaManager) AddServer(host string, lifetime int64) error {
- address, port, err := net.SplitHostPort(host)
- if err != nil {
- return err
- }
- if lifetime == 0 {
- lifetime = 3600
- }
- if _, ok := m.clientMap[host]; ok {
- return fmt.Errorf("ROA server exists %s", host)
- }
- m.clientMap[host] = NewRoaClient(address, port, m.eventCh, lifetime)
- return nil
-}
-
-func (m *roaManager) DeleteServer(host string) error {
- client, ok := m.clientMap[host]
- if !ok {
- return fmt.Errorf("ROA server doesn't exists %s", host)
- }
- client.stop()
- m.deleteAllROA(host)
- delete(m.clientMap, host)
- return nil
-}
-
-func (m *roaManager) deleteAllROA(network string) {
- for _, tree := range m.Roas {
- deleteKeys := make([]string, 0, tree.Len())
- tree.Walk(func(s string, v interface{}) bool {
- b, _ := v.(*RoaBucket)
- newEntries := make([]*table.ROA, 0, len(b.entries))
- for _, r := range b.entries {
- if r.Src != network {
- newEntries = append(newEntries, r)
- }
- }
- if len(newEntries) > 0 {
- b.entries = newEntries
- } else {
- deleteKeys = append(deleteKeys, s)
- }
- return false
- })
- for _, key := range deleteKeys {
- tree.Delete(key)
- }
- }
-}
-
-func (m *roaManager) Enable(address string) error {
- for network, client := range m.clientMap {
- add, _, _ := net.SplitHostPort(network)
- if add == address {
- client.enable(client.serialNumber)
- return nil
- }
- }
- return fmt.Errorf("ROA server not found %s", address)
-}
-
-func (m *roaManager) Disable(address string) error {
- for network, client := range m.clientMap {
- add, _, _ := net.SplitHostPort(network)
- if add == address {
- client.reset()
- m.deleteAllROA(add)
- return nil
- }
- }
- return fmt.Errorf("ROA server not found %s", address)
-}
-
-func (m *roaManager) Reset(address string) error {
- return m.Disable(address)
-}
-
-func (m *roaManager) SoftReset(address string) error {
- for network, client := range m.clientMap {
- add, _, _ := net.SplitHostPort(network)
- if add == address {
- client.softReset()
- m.deleteAllROA(network)
- return nil
- }
- }
- return fmt.Errorf("ROA server not found %s", address)
-}
-
-func (c *roaManager) ReceiveROA() chan *ROAEvent {
- return c.eventCh
-}
-
-func (c *roaClient) lifetimeout() {
- c.eventCh <- &ROAEvent{
- EventType: LIFETIMEOUT,
- Src: c.host,
- }
-}
-
-func (m *roaManager) HandleROAEvent(ev *ROAEvent) {
- client, y := m.clientMap[ev.Src]
- if !y {
- if ev.EventType == CONNECTED {
- ev.conn.Close()
- }
- log.WithFields(log.Fields{"Topic": "rpki"}).Errorf("Can't find %s ROA server configuration", ev.Src)
- return
- }
- switch ev.EventType {
- case DISCONNECTED:
- log.WithFields(log.Fields{"Topic": "rpki"}).Infof("ROA server %s is disconnected", ev.Src)
- client.state.Downtime = time.Now().Unix()
- // clear state
- client.endOfData = false
- client.pendingROAs = make([]*table.ROA, 0)
- client.state.RpkiMessages = config.RpkiMessages{}
- client.conn = nil
- go client.tryConnect()
- client.timer = time.AfterFunc(time.Duration(client.lifetime)*time.Second, client.lifetimeout)
- client.oldSessionID = client.sessionID
- case CONNECTED:
- log.WithFields(log.Fields{"Topic": "rpki"}).Infof("ROA server %s is connected", ev.Src)
- client.conn = ev.conn
- client.state.Uptime = time.Now().Unix()
- go client.established()
- case RTR:
- m.handleRTRMsg(client, &client.state, ev.Data)
- case LIFETIMEOUT:
- // a) already reconnected but hasn't received
- // EndOfData -> needs to delete stale ROAs
- // b) not reconnected -> needs to delete stale ROAs
- //
- // c) already reconnected and received EndOfData so
- // all stale ROAs were deleted -> timer was cancelled
- // so should not be here.
- if client.oldSessionID != client.sessionID {
- log.WithFields(log.Fields{"Topic": "rpki"}).Infof("Reconnected to %s. Ignore timeout", client.host)
- } else {
- log.WithFields(log.Fields{"Topic": "rpki"}).Infof("Deleting all ROAs due to timeout with:%s", client.host)
- m.deleteAllROA(client.host)
- }
- }
-}
-
-func (m *roaManager) roa2tree(roa *table.ROA) (*radix.Tree, string) {
- tree := m.Roas[bgp.RF_IPv4_UC]
- if roa.Family == bgp.AFI_IP6 {
- tree = m.Roas[bgp.RF_IPv6_UC]
- }
- return tree, table.IpToRadixkey(roa.Prefix.Prefix, roa.Prefix.Length)
-}
-
-func (m *roaManager) deleteROA(roa *table.ROA) {
- tree, key := m.roa2tree(roa)
- b, _ := tree.Get(key)
- if b != nil {
- bucket := b.(*RoaBucket)
- newEntries := make([]*table.ROA, 0, len(bucket.entries))
- for _, r := range bucket.entries {
- if !r.Equal(roa) {
- newEntries = append(newEntries, r)
- }
- }
- if len(newEntries) != len(bucket.entries) {
- bucket.entries = newEntries
- if len(newEntries) == 0 {
- tree.Delete(key)
- }
- return
- }
- }
- log.WithFields(log.Fields{
- "Topic": "rpki",
- "Prefix": roa.Prefix.Prefix.String(),
- "Prefix Length": roa.Prefix.Length,
- "AS": roa.AS,
- "Max Length": roa.MaxLen,
- }).Info("Can't withdraw a ROA")
-}
-
-func (m *roaManager) DeleteROA(roa *table.ROA) {
- m.deleteROA(roa)
-}
-
-func (m *roaManager) addROA(roa *table.ROA) {
- tree, key := m.roa2tree(roa)
- b, _ := tree.Get(key)
- var bucket *RoaBucket
- if b == nil {
- bucket = &RoaBucket{
- Prefix: roa.Prefix,
- entries: make([]*table.ROA, 0),
- }
- tree.Insert(key, bucket)
- } else {
- bucket = b.(*RoaBucket)
- for _, r := range bucket.entries {
- if r.Equal(roa) {
- // we already have the same one
- return
- }
- }
- }
- bucket.entries = append(bucket.entries, roa)
-}
-
-func (m *roaManager) AddROA(roa *table.ROA) {
- m.addROA(roa)
-}
-
-func (c *roaManager) handleRTRMsg(client *roaClient, state *config.RpkiServerState, buf []byte) {
- received := &state.RpkiMessages.RpkiReceived
-
- m, err := rtr.ParseRTR(buf)
- if err == nil {
- switch msg := m.(type) {
- case *rtr.RTRSerialNotify:
- if before(client.serialNumber, msg.RTRCommon.SerialNumber) {
- client.enable(client.serialNumber)
- } else if client.serialNumber == msg.RTRCommon.SerialNumber {
- // nothing
- } else {
- // should not happen. try to get the whole ROAs.
- client.softReset()
- }
- received.SerialNotify++
- case *rtr.RTRSerialQuery:
- case *rtr.RTRResetQuery:
- case *rtr.RTRCacheResponse:
- received.CacheResponse++
- client.endOfData = false
- case *rtr.RTRIPPrefix:
- family := bgp.AFI_IP
- if msg.Type == rtr.RTR_IPV4_PREFIX {
- received.Ipv4Prefix++
- } else {
- family = bgp.AFI_IP6
- received.Ipv6Prefix++
- }
- roa := table.NewROA(family, msg.Prefix, msg.PrefixLen, msg.MaxLen, msg.AS, client.host)
- if (msg.Flags & 1) == 1 {
- if client.endOfData {
- c.addROA(roa)
- } else {
- client.pendingROAs = append(client.pendingROAs, roa)
- }
- } else {
- c.deleteROA(roa)
- }
- case *rtr.RTREndOfData:
- received.EndOfData++
- if client.sessionID != msg.RTRCommon.SessionID {
- // remove all ROAs related with the
- // previous session
- c.deleteAllROA(client.host)
- }
- client.sessionID = msg.RTRCommon.SessionID
- client.serialNumber = msg.RTRCommon.SerialNumber
- client.endOfData = true
- if client.timer != nil {
- client.timer.Stop()
- client.timer = nil
- }
- for _, roa := range client.pendingROAs {
- c.addROA(roa)
- }
- client.pendingROAs = make([]*table.ROA, 0)
- case *rtr.RTRCacheReset:
- client.softReset()
- received.CacheReset++
- case *rtr.RTRErrorReport:
- received.Error++
- }
- } else {
- log.WithFields(log.Fields{
- "Topic": "rpki",
- "Host": client.host,
- "Error": err,
- }).Info("Failed to parse an RTR message")
- }
-}
-
-func (c *roaManager) GetServers() []*config.RpkiServer {
- f := func(tree *radix.Tree) (map[string]uint32, map[string]uint32) {
- records := make(map[string]uint32)
- prefixes := make(map[string]uint32)
-
- tree.Walk(func(s string, v interface{}) bool {
- b, _ := v.(*RoaBucket)
- tmpRecords := make(map[string]uint32)
- for _, roa := range b.entries {
- tmpRecords[roa.Src]++
- }
-
- for src, r := range tmpRecords {
- if r > 0 {
- records[src] += r
- prefixes[src]++
- }
- }
- return false
- })
- return records, prefixes
- }
-
- recordsV4, prefixesV4 := f(c.Roas[bgp.RF_IPv4_UC])
- recordsV6, prefixesV6 := f(c.Roas[bgp.RF_IPv6_UC])
-
- l := make([]*config.RpkiServer, 0, len(c.clientMap))
- for _, client := range c.clientMap {
- state := &client.state
-
- if client.conn == nil {
- state.Up = false
- } else {
- state.Up = true
- }
- f := func(m map[string]uint32, key string) uint32 {
- if r, ok := m[key]; ok {
- return r
- }
- return 0
- }
- state.RecordsV4 = f(recordsV4, client.host)
- state.RecordsV6 = f(recordsV6, client.host)
- state.PrefixesV4 = f(prefixesV4, client.host)
- state.PrefixesV6 = f(prefixesV6, client.host)
- state.SerialNumber = client.serialNumber
-
- addr, port, _ := net.SplitHostPort(client.host)
- l = append(l, &config.RpkiServer{
- Config: config.RpkiServerConfig{
- Address: addr,
- // Note: RpkiServerConfig.Port is uint32 type, but the TCP/UDP
- // port is 16-bit length.
- Port: func() uint32 { p, _ := strconv.ParseUint(port, 10, 16); return uint32(p) }(),
- },
- State: client.state,
- })
- }
- return l
-}
-
-func (c *roaManager) GetRoa(family bgp.RouteFamily) ([]*table.ROA, error) {
- if len(c.clientMap) == 0 {
- return []*table.ROA{}, fmt.Errorf("RPKI server isn't configured.")
- }
- var rfList []bgp.RouteFamily
- switch family {
- case bgp.RF_IPv4_UC:
- rfList = []bgp.RouteFamily{bgp.RF_IPv4_UC}
- case bgp.RF_IPv6_UC:
- rfList = []bgp.RouteFamily{bgp.RF_IPv6_UC}
- default:
- rfList = []bgp.RouteFamily{bgp.RF_IPv4_UC, bgp.RF_IPv6_UC}
- }
- l := make([]*table.ROA, 0)
- for _, rf := range rfList {
- if tree, ok := c.Roas[rf]; ok {
- tree.Walk(func(s string, v interface{}) bool {
- b, _ := v.(*RoaBucket)
- var roaList roas
- for _, r := range b.entries {
- roaList = append(roaList, r)
- }
- sort.Sort(roaList)
- for _, roa := range roaList {
- l = append(l, roa)
- }
- return false
- })
- }
- }
- return l, nil
-}
-
-func ValidatePath(ownAs uint32, tree *radix.Tree, cidr string, asPath *bgp.PathAttributeAsPath) *table.Validation {
- var as uint32
-
- validation := &table.Validation{
- Status: config.RPKI_VALIDATION_RESULT_TYPE_NOT_FOUND,
- Reason: table.RPKI_VALIDATION_REASON_TYPE_NONE,
- Matched: make([]*table.ROA, 0),
- UnmatchedLength: make([]*table.ROA, 0),
- UnmatchedAs: make([]*table.ROA, 0),
- }
-
- if asPath == nil || len(asPath.Value) == 0 {
- as = ownAs
- } else {
- param := asPath.Value[len(asPath.Value)-1]
- switch param.GetType() {
- case bgp.BGP_ASPATH_ATTR_TYPE_SEQ:
- asList := param.GetAS()
- if len(asList) == 0 {
- as = ownAs
- } else {
- as = asList[len(asList)-1]
- }
- case bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET, bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ:
- as = ownAs
- default:
- return validation
- }
- }
- _, n, _ := net.ParseCIDR(cidr)
- ones, _ := n.Mask.Size()
- prefixLen := uint8(ones)
- key := table.IpToRadixkey(n.IP, prefixLen)
- _, b, _ := tree.LongestPrefix(key)
- if b == nil {
- return validation
- }
-
- var bucket *RoaBucket
- fn := radix.WalkFn(func(k string, v interface{}) bool {
- bucket, _ = v.(*RoaBucket)
- for _, r := range bucket.entries {
- if prefixLen <= r.MaxLen {
- if r.AS != 0 && r.AS == as {
- validation.Matched = append(validation.Matched, r)
- } else {
- validation.UnmatchedAs = append(validation.UnmatchedAs, r)
- }
- } else {
- validation.UnmatchedLength = append(validation.UnmatchedLength, r)
- }
- }
- return false
- })
- tree.WalkPath(key, fn)
-
- if len(validation.Matched) != 0 {
- validation.Status = config.RPKI_VALIDATION_RESULT_TYPE_VALID
- validation.Reason = table.RPKI_VALIDATION_REASON_TYPE_NONE
- } else if len(validation.UnmatchedAs) != 0 {
- validation.Status = config.RPKI_VALIDATION_RESULT_TYPE_INVALID
- validation.Reason = table.RPKI_VALIDATION_REASON_TYPE_AS
- } else if len(validation.UnmatchedLength) != 0 {
- validation.Status = config.RPKI_VALIDATION_RESULT_TYPE_INVALID
- validation.Reason = table.RPKI_VALIDATION_REASON_TYPE_LENGTH
- } else {
- validation.Status = config.RPKI_VALIDATION_RESULT_TYPE_NOT_FOUND
- validation.Reason = table.RPKI_VALIDATION_REASON_TYPE_NONE
- }
-
- return validation
-}
-
-func (c *roaManager) validate(path *table.Path) *table.Validation {
- if len(c.clientMap) == 0 || path.IsWithdraw || path.IsEOR() {
- // RPKI isn't enabled or invalid path
- return nil
- }
- if tree, ok := c.Roas[path.GetRouteFamily()]; ok {
- return ValidatePath(c.AS, tree, path.GetNlri().String(), path.GetAsPath())
- }
- return nil
-}
-
-type roaClient struct {
- host string
- conn *net.TCPConn
- state config.RpkiServerState
- eventCh chan *ROAEvent
- sessionID uint16
- oldSessionID uint16
- serialNumber uint32
- timer *time.Timer
- lifetime int64
- endOfData bool
- pendingROAs []*table.ROA
- cancelfnc context.CancelFunc
- ctx context.Context
-}
-
-func NewRoaClient(address, port string, ch chan *ROAEvent, lifetime int64) *roaClient {
- ctx, cancel := context.WithCancel(context.Background())
- c := &roaClient{
- host: net.JoinHostPort(address, port),
- eventCh: ch,
- lifetime: lifetime,
- pendingROAs: make([]*table.ROA, 0),
- ctx: ctx,
- cancelfnc: cancel,
- }
- go c.tryConnect()
- return c
-}
-
-func (c *roaClient) enable(serial uint32) error {
- if c.conn != nil {
- r := rtr.NewRTRSerialQuery(c.sessionID, serial)
- data, _ := r.Serialize()
- _, err := c.conn.Write(data)
- if err != nil {
- return err
- }
- c.state.RpkiMessages.RpkiSent.SerialQuery++
- }
- return nil
-}
-
-func (c *roaClient) softReset() error {
- if c.conn != nil {
- r := rtr.NewRTRResetQuery()
- data, _ := r.Serialize()
- _, err := c.conn.Write(data)
- if err != nil {
- return err
- }
- c.state.RpkiMessages.RpkiSent.ResetQuery++
- c.endOfData = false
- c.pendingROAs = make([]*table.ROA, 0)
- }
- return nil
-}
-
-func (c *roaClient) reset() {
- if c.conn != nil {
- c.conn.Close()
- }
-}
-
-func (c *roaClient) stop() {
- c.cancelfnc()
- c.reset()
-}
-
-func (c *roaClient) tryConnect() {
- for {
- select {
- case <-c.ctx.Done():
- return
- default:
- }
- if conn, err := net.Dial("tcp", c.host); err != nil {
- // better to use context with timeout
- time.Sleep(CONNECT_RETRY_INTERVAL * time.Second)
- } else {
- c.eventCh <- &ROAEvent{
- EventType: CONNECTED,
- Src: c.host,
- conn: conn.(*net.TCPConn),
- }
- return
- }
- }
-}
-
-func (c *roaClient) established() (err error) {
- defer func() {
- c.conn.Close()
- c.eventCh <- &ROAEvent{
- EventType: DISCONNECTED,
- Src: c.host,
- }
- }()
-
- if err := c.softReset(); err != nil {
- return err
- }
-
- for {
- header := make([]byte, rtr.RTR_MIN_LEN)
- if _, err = io.ReadFull(c.conn, header); err != nil {
- return err
- }
- totalLen := binary.BigEndian.Uint32(header[4:8])
- if totalLen < rtr.RTR_MIN_LEN {
- return fmt.Errorf("too short header length %v", totalLen)
- }
-
- body := make([]byte, totalLen-rtr.RTR_MIN_LEN)
- if _, err = io.ReadFull(c.conn, body); err != nil {
- return
- }
-
- c.eventCh <- &ROAEvent{
- EventType: RTR,
- Src: c.host,
- Data: append(header, body...),
- }
- }
-}
diff --git a/server/rpki_test.go b/server/rpki_test.go
deleted file mode 100644
index c7c24669..00000000
--- a/server/rpki_test.go
+++ /dev/null
@@ -1,258 +0,0 @@
-// Copyright (C) 2015 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 (
- "net"
- "strconv"
- "strings"
- "testing"
-
- "github.com/stretchr/testify/assert"
-
- radix "github.com/armon/go-radix"
-
- "github.com/osrg/gobgp/config"
- "github.com/osrg/gobgp/packet/bgp"
- "github.com/osrg/gobgp/table"
-)
-
-func strToASParam(str string) *bgp.PathAttributeAsPath {
- toList := func(asstr, sep string) []uint32 {
- as := make([]uint32, 0)
- l := strings.Split(asstr, sep)
- for _, s := range l {
- v, _ := strconv.ParseUint(s, 10, 32)
- as = append(as, uint32(v))
- }
- return as
- }
- var atype uint8
- var as []uint32
- if strings.HasPrefix(str, "{") {
- atype = bgp.BGP_ASPATH_ATTR_TYPE_SET
- as = toList(str[1:len(str)-1], ",")
- } else if strings.HasPrefix(str, "(") {
- atype = bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET
- as = toList(str[1:len(str)-1], " ")
- } else {
- atype = bgp.BGP_ASPATH_ATTR_TYPE_SEQ
- as = toList(str, " ")
- }
-
- return bgp.NewPathAttributeAsPath([]bgp.AsPathParamInterface{bgp.NewAs4PathParam(atype, as)})
-}
-
-func validateOne(tree *radix.Tree, cidr, aspathStr string) config.RpkiValidationResultType {
- r := ValidatePath(65500, tree, cidr, strToASParam(aspathStr))
- return r.Status
-}
-
-func TestValidate0(t *testing.T) {
- assert := assert.New(t)
-
- manager, _ := NewROAManager(0)
- manager.addROA(table.NewROA(bgp.AFI_IP, net.ParseIP("192.168.0.0").To4(), 24, 32, 100, ""))
- manager.addROA(table.NewROA(bgp.AFI_IP, net.ParseIP("192.168.0.0").To4(), 24, 24, 200, ""))
-
- var r config.RpkiValidationResultType
-
- tree := manager.Roas[bgp.RF_IPv4_UC]
- r = validateOne(tree, "192.168.0.0/24", "100")
- assert.Equal(r, config.RPKI_VALIDATION_RESULT_TYPE_VALID)
-
- r = validateOne(tree, "192.168.0.0/24", "100 200")
- assert.Equal(r, config.RPKI_VALIDATION_RESULT_TYPE_VALID)
-
- r = validateOne(tree, "192.168.0.0/24", "300")
- assert.Equal(r, config.RPKI_VALIDATION_RESULT_TYPE_INVALID)
-
- r = validateOne(tree, "192.168.0.0/25", "100")
- assert.Equal(r, config.RPKI_VALIDATION_RESULT_TYPE_VALID)
-
- r = validateOne(tree, "192.168.0.0/25", "200")
- assert.Equal(r, config.RPKI_VALIDATION_RESULT_TYPE_INVALID)
-
- r = validateOne(tree, "192.168.0.0/25", "300")
- assert.Equal(r, config.RPKI_VALIDATION_RESULT_TYPE_INVALID)
-}
-
-func TestValidate1(t *testing.T) {
- assert := assert.New(t)
-
- manager, _ := NewROAManager(0)
- manager.addROA(table.NewROA(bgp.AFI_IP, net.ParseIP("10.0.0.0").To4(), 16, 16, 65000, ""))
-
- var r config.RpkiValidationResultType
-
- tree := manager.Roas[bgp.RF_IPv4_UC]
- r = validateOne(tree, "10.0.0.0/16", "65000")
- assert.Equal(r, config.RPKI_VALIDATION_RESULT_TYPE_VALID)
-
- r = validateOne(tree, "10.0.0.0/16", "65001")
- assert.Equal(r, config.RPKI_VALIDATION_RESULT_TYPE_INVALID)
-}
-
-func TestValidate2(t *testing.T) {
- assert := assert.New(t)
-
- manager, _ := NewROAManager(0)
-
- var r config.RpkiValidationResultType
-
- tree := manager.Roas[bgp.RF_IPv4_UC]
- r = validateOne(tree, "10.0.0.0/16", "65000")
- assert.Equal(r, config.RPKI_VALIDATION_RESULT_TYPE_NOT_FOUND)
-
- r = validateOne(tree, "10.0.0.0/16", "65001")
- assert.Equal(r, config.RPKI_VALIDATION_RESULT_TYPE_NOT_FOUND)
-}
-
-func TestValidate3(t *testing.T) {
- assert := assert.New(t)
-
- manager, _ := NewROAManager(0)
- manager.addROA(table.NewROA(bgp.AFI_IP, net.ParseIP("10.0.0.0").To4(), 16, 16, 65000, ""))
-
- var r config.RpkiValidationResultType
-
- tree := manager.Roas[bgp.RF_IPv4_UC]
- r = validateOne(tree, "10.0.0.0/8", "65000")
- assert.Equal(r, config.RPKI_VALIDATION_RESULT_TYPE_NOT_FOUND)
-
- r = validateOne(tree, "10.0.0.0/17", "65000")
- assert.Equal(r, config.RPKI_VALIDATION_RESULT_TYPE_INVALID)
-
- manager, _ = NewROAManager(0)
- manager.addROA(table.NewROA(bgp.AFI_IP, net.ParseIP("10.0.0.0").To4(), 16, 24, 65000, ""))
-
- tree = manager.Roas[bgp.RF_IPv4_UC]
- r = validateOne(tree, "10.0.0.0/17", "65000")
- assert.Equal(r, config.RPKI_VALIDATION_RESULT_TYPE_VALID)
-}
-
-func TestValidate4(t *testing.T) {
- assert := assert.New(t)
-
- manager, _ := NewROAManager(0)
- manager.addROA(table.NewROA(bgp.AFI_IP, net.ParseIP("10.0.0.0").To4(), 16, 16, 65000, ""))
- manager.addROA(table.NewROA(bgp.AFI_IP, net.ParseIP("10.0.0.0").To4(), 16, 16, 65001, ""))
-
- var r config.RpkiValidationResultType
- tree := manager.Roas[bgp.RF_IPv4_UC]
- r = validateOne(tree, "10.0.0.0/16", "65000")
- assert.Equal(r, config.RPKI_VALIDATION_RESULT_TYPE_VALID)
-
- r = validateOne(tree, "10.0.0.0/16", "65001")
- assert.Equal(r, config.RPKI_VALIDATION_RESULT_TYPE_VALID)
-}
-
-func TestValidate5(t *testing.T) {
- assert := assert.New(t)
-
- manager, _ := NewROAManager(0)
- manager.addROA(table.NewROA(bgp.AFI_IP, net.ParseIP("10.0.0.0").To4(), 17, 17, 65000, ""))
- manager.addROA(table.NewROA(bgp.AFI_IP, net.ParseIP("10.0.128.0").To4(), 17, 17, 65000, ""))
-
- var r config.RpkiValidationResultType
- tree := manager.Roas[bgp.RF_IPv4_UC]
- r = validateOne(tree, "10.0.0.0/16", "65000")
- assert.Equal(r, config.RPKI_VALIDATION_RESULT_TYPE_NOT_FOUND)
-}
-
-func TestValidate6(t *testing.T) {
- assert := assert.New(t)
-
- manager, _ := NewROAManager(0)
- manager.addROA(table.NewROA(bgp.AFI_IP, net.ParseIP("10.0.0.0").To4(), 8, 32, 0, ""))
-
- var r config.RpkiValidationResultType
- tree := manager.Roas[bgp.RF_IPv4_UC]
- r = validateOne(tree, "10.0.0.0/7", "65000")
- assert.Equal(r, config.RPKI_VALIDATION_RESULT_TYPE_NOT_FOUND)
-
- r = validateOne(tree, "10.0.0.0/8", "65000")
- assert.Equal(r, config.RPKI_VALIDATION_RESULT_TYPE_INVALID)
-
- r = validateOne(tree, "10.0.0.0/24", "65000")
- assert.Equal(r, config.RPKI_VALIDATION_RESULT_TYPE_INVALID)
-}
-
-func TestValidate7(t *testing.T) {
- assert := assert.New(t)
-
- manager, _ := NewROAManager(0)
- manager.addROA(table.NewROA(bgp.AFI_IP, net.ParseIP("10.0.0.0").To4(), 16, 24, 65000, ""))
-
- var r config.RpkiValidationResultType
- tree := manager.Roas[bgp.RF_IPv4_UC]
- r = validateOne(tree, "10.0.0.0/24", "{65000}")
- assert.Equal(r, config.RPKI_VALIDATION_RESULT_TYPE_NOT_FOUND)
-
- r = validateOne(tree, "10.0.0.0/24", "{65001}")
- assert.Equal(r, config.RPKI_VALIDATION_RESULT_TYPE_NOT_FOUND)
-
- r = validateOne(tree, "10.0.0.0/24", "{65000,65001}")
- assert.Equal(r, config.RPKI_VALIDATION_RESULT_TYPE_NOT_FOUND)
-}
-
-func TestValidate8(t *testing.T) {
- assert := assert.New(t)
-
- manager, _ := NewROAManager(0)
- manager.addROA(table.NewROA(bgp.AFI_IP, net.ParseIP("10.0.0.0").To4(), 16, 24, 0, ""))
- manager.addROA(table.NewROA(bgp.AFI_IP, net.ParseIP("10.0.0.0").To4(), 16, 24, 65000, ""))
-
- var r config.RpkiValidationResultType
- tree := manager.Roas[bgp.RF_IPv4_UC]
- r = validateOne(tree, "10.0.0.0/24", "65000")
- assert.Equal(r, config.RPKI_VALIDATION_RESULT_TYPE_VALID)
-
- r = validateOne(tree, "10.0.0.0/24", "65001")
- assert.Equal(r, config.RPKI_VALIDATION_RESULT_TYPE_INVALID)
-}
-
-func TestValidate9(t *testing.T) {
- assert := assert.New(t)
-
- manager, _ := NewROAManager(0)
- manager.addROA(table.NewROA(bgp.AFI_IP, net.ParseIP("10.0.0.0").To4(), 24, 24, 65000, ""))
- manager.addROA(table.NewROA(bgp.AFI_IP, net.ParseIP("10.0.0.0").To4(), 16, 24, 65001, ""))
-
- var r config.RpkiValidationResultType
- tree := manager.Roas[bgp.RF_IPv4_UC]
- r = validateOne(tree, "10.0.0.0/24", "65000")
- assert.Equal(r, config.RPKI_VALIDATION_RESULT_TYPE_VALID)
-
- r = validateOne(tree, "10.0.0.0/24", "65001")
- assert.Equal(r, config.RPKI_VALIDATION_RESULT_TYPE_VALID)
-}
-
-func TestValidate10(t *testing.T) {
- assert := assert.New(t)
-
- manager, _ := NewROAManager(0)
- manager.addROA(table.NewROA(bgp.AFI_IP, net.ParseIP("10.0.0.0").To4(), 24, 24, 0, ""))
- manager.addROA(table.NewROA(bgp.AFI_IP, net.ParseIP("10.0.0.0").To4(), 16, 24, 65001, ""))
-
- var r config.RpkiValidationResultType
- tree := manager.Roas[bgp.RF_IPv4_UC]
- r = validateOne(tree, "10.0.0.0/24", "65000")
- assert.Equal(r, config.RPKI_VALIDATION_RESULT_TYPE_INVALID)
-
- r = validateOne(tree, "10.0.0.0/24", "65001")
- assert.Equal(r, config.RPKI_VALIDATION_RESULT_TYPE_VALID)
-}
diff --git a/server/server.go b/server/server.go
deleted file mode 100644
index 6faecba9..00000000
--- a/server/server.go
+++ /dev/null
@@ -1,3048 +0,0 @@
-// Copyright (C) 2014-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 (
- "bytes"
- "fmt"
- "net"
- "strconv"
- "sync"
- "time"
-
- "github.com/eapache/channels"
- uuid "github.com/satori/go.uuid"
- log "github.com/sirupsen/logrus"
-
- "github.com/osrg/gobgp/config"
- "github.com/osrg/gobgp/packet/bgp"
- "github.com/osrg/gobgp/table"
-)
-
-type TCPListener struct {
- l *net.TCPListener
- ch chan struct{}
-}
-
-func (l *TCPListener) Close() error {
- if err := l.l.Close(); err != nil {
- return err
- }
- t := time.NewTicker(time.Second)
- select {
- case <-l.ch:
- case <-t.C:
- return fmt.Errorf("close timeout")
- }
- return nil
-}
-
-// avoid mapped IPv6 address
-func NewTCPListener(address string, port uint32, ch chan *net.TCPConn) (*TCPListener, error) {
- proto := "tcp4"
- if ip := net.ParseIP(address); ip == nil {
- return nil, fmt.Errorf("can't listen on %s", address)
- } else if ip.To4() == nil {
- proto = "tcp6"
- }
- addr, err := net.ResolveTCPAddr(proto, net.JoinHostPort(address, strconv.Itoa(int(port))))
- if err != nil {
- return nil, err
- }
-
- l, err := net.ListenTCP(proto, addr)
- if err != nil {
- return nil, err
- }
- // Note: Set TTL=255 for incoming connection listener in order to accept
- // connection in case for the neighbor has TTL Security settings.
- if err := SetListenTcpTTLSockopt(l, 255); err != nil {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": addr,
- }).Warnf("cannot set TTL(=%d) for TCPListener: %s", 255, err)
- }
-
- closeCh := make(chan struct{})
- go func() error {
- for {
- conn, err := l.AcceptTCP()
- if err != nil {
- close(closeCh)
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Error": err,
- }).Warn("Failed to AcceptTCP")
- return err
- }
- ch <- conn
- }
- }()
- return &TCPListener{
- l: l,
- ch: closeCh,
- }, nil
-}
-
-type BgpServer struct {
- bgpConfig config.Bgp
- fsmincomingCh *channels.InfiniteChannel
- fsmStateCh chan *FsmMsg
- acceptCh chan *net.TCPConn
-
- mgmtCh chan *mgmtOp
- policy *table.RoutingPolicy
- listeners []*TCPListener
- neighborMap map[string]*Peer
- peerGroupMap map[string]*PeerGroup
- globalRib *table.TableManager
- rsRib *table.TableManager
- roaManager *roaManager
- shutdownWG *sync.WaitGroup
- watcherMap map[WatchEventType][]*Watcher
- zclient *zebraClient
- bmpManager *bmpClientManager
- mrtManager *mrtManager
- uuidMap map[uuid.UUID]string
-}
-
-func NewBgpServer() *BgpServer {
- roaManager, _ := NewROAManager(0)
- s := &BgpServer{
- neighborMap: make(map[string]*Peer),
- peerGroupMap: make(map[string]*PeerGroup),
- policy: table.NewRoutingPolicy(),
- roaManager: roaManager,
- mgmtCh: make(chan *mgmtOp, 1),
- watcherMap: make(map[WatchEventType][]*Watcher),
- uuidMap: make(map[uuid.UUID]string),
- }
- s.bmpManager = newBmpClientManager(s)
- s.mrtManager = newMrtManager(s)
- return s
-}
-
-func (server *BgpServer) Listeners(addr string) []*net.TCPListener {
- list := make([]*net.TCPListener, 0, len(server.listeners))
- rhs := net.ParseIP(addr).To4() != nil
- for _, l := range server.listeners {
- host, _, _ := net.SplitHostPort(l.l.Addr().String())
- lhs := net.ParseIP(host).To4() != nil
- if lhs == rhs {
- list = append(list, l.l)
- }
- }
- return list
-}
-
-func (s *BgpServer) active() error {
- if s.bgpConfig.Global.Config.As == 0 {
- return fmt.Errorf("bgp server hasn't started yet")
- }
- return nil
-}
-
-type mgmtOp struct {
- f func() error
- errCh chan error
- checkActive bool // check BGP global setting is configured before calling f()
-}
-
-func (server *BgpServer) handleMGMTOp(op *mgmtOp) {
- if op.checkActive {
- if err := server.active(); err != nil {
- op.errCh <- err
- return
- }
- }
- op.errCh <- op.f()
-}
-
-func (s *BgpServer) mgmtOperation(f func() error, checkActive bool) (err error) {
- ch := make(chan error)
- defer func() { err = <-ch }()
- s.mgmtCh <- &mgmtOp{
- f: f,
- errCh: ch,
- checkActive: checkActive,
- }
- return
-}
-
-func (server *BgpServer) Serve() {
- server.listeners = make([]*TCPListener, 0, 2)
- server.fsmincomingCh = channels.NewInfiniteChannel()
- server.fsmStateCh = make(chan *FsmMsg, 4096)
-
- handleFsmMsg := func(e *FsmMsg) {
- peer, found := server.neighborMap[e.MsgSrc]
- if !found {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- }).Warnf("Can't find the neighbor %s", e.MsgSrc)
- return
- }
- if e.Version != peer.fsm.version {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- }).Debug("FSM version inconsistent")
- return
- }
- server.handleFSMMessage(peer, e)
- }
-
- for {
- passConn := func(conn *net.TCPConn) {
- host, _, _ := net.SplitHostPort(conn.RemoteAddr().String())
- ipaddr, _ := net.ResolveIPAddr("ip", host)
- remoteAddr := ipaddr.String()
- peer, found := server.neighborMap[remoteAddr]
- if found {
- if peer.fsm.adminState != ADMIN_STATE_UP {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Remote Addr": remoteAddr,
- "Admin State": peer.fsm.adminState,
- }).Debug("New connection for non admin-state-up peer")
- conn.Close()
- return
- }
- localAddrValid := func(laddr string) bool {
- if laddr == "0.0.0.0" || laddr == "::" {
- return true
- }
- l := conn.LocalAddr()
- if l == nil {
- // already closed
- return false
- }
-
- host, _, _ := net.SplitHostPort(l.String())
- if host != laddr {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": remoteAddr,
- "Configured addr": laddr,
- "Addr": host,
- }).Info("Mismatched local address")
- return false
- }
- return true
- }(peer.fsm.pConf.Transport.Config.LocalAddress)
-
- if !localAddrValid {
- conn.Close()
- return
- }
-
- log.WithFields(log.Fields{
- "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)
- rib := server.globalRib
- if pg.Conf.RouteServer.Config.RouteServerClient {
- rib = server.rsRib
- }
- peer := newDynamicPeer(&server.bgpConfig.Global, remoteAddr, pg.Conf, rib, server.policy)
- if peer == nil {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": remoteAddr,
- }).Infof("Can't create new Dynamic Peer")
- conn.Close()
- return
- }
- 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, nil)
- peer.PassConn(conn)
- } else {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- }).Infof("Can't find configuration for a new passive connection from:%s", remoteAddr)
- conn.Close()
- }
- }
-
- select {
- case op := <-server.mgmtCh:
- server.handleMGMTOp(op)
- case conn := <-server.acceptCh:
- passConn(conn)
- default:
- }
-
- for {
- select {
- case e := <-server.fsmStateCh:
- handleFsmMsg(e)
- default:
- goto CONT
- }
- }
- CONT:
-
- select {
- case op := <-server.mgmtCh:
- server.handleMGMTOp(op)
- case rmsg := <-server.roaManager.ReceiveROA():
- server.roaManager.HandleROAEvent(rmsg)
- case conn := <-server.acceptCh:
- passConn(conn)
- case e, ok := <-server.fsmincomingCh.Out():
- if !ok {
- continue
- }
- handleFsmMsg(e.(*FsmMsg))
- case e := <-server.fsmStateCh:
- handleFsmMsg(e)
- }
- }
-}
-
-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,
- Notification: notification,
- StayIdle: stayIdle,
- }
-}
-
-func isASLoop(peer *Peer, path *table.Path) bool {
- for _, as := range path.GetAsList() {
- if as == peer.AS() {
- return true
- }
- }
- return false
-}
-
-func filterpath(peer *Peer, path, old *table.Path) *table.Path {
- if path == nil {
- return nil
- }
- if _, ok := peer.fsm.rfMap[path.GetRouteFamily()]; !ok {
- return nil
- }
-
- //RFC4684 Constrained Route Distribution
- if _, y := peer.fsm.rfMap[bgp.RF_RTC_UC]; y && path.GetRouteFamily() != bgp.RF_RTC_UC {
- ignore := true
- for _, ext := range path.GetExtCommunities() {
- for _, p := range peer.adjRibIn.PathList([]bgp.RouteFamily{bgp.RF_RTC_UC}, true) {
- rt := p.GetNlri().(*bgp.RouteTargetMembershipNLRI).RouteTarget
- // Note: nil RT means the default route target
- if rt == nil || ext.String() == rt.String() {
- ignore = false
- break
- }
- }
- if !ignore {
- break
- }
- }
- if ignore {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": peer.ID(),
- "Data": path,
- }).Debug("Filtered by Route Target Constraint, ignore")
- return nil
- }
- }
-
- //iBGP handling
- if peer.isIBGPPeer() {
- ignore := false
- if !path.IsLocal() {
- ignore = true
- info := path.GetSource()
- //if the path comes from eBGP peer
- if info.AS != peer.AS() {
- ignore = false
- }
- // RFC4456 8. Avoiding Routing Information Loops
- // A router that recognizes the ORIGINATOR_ID attribute SHOULD
- // ignore a route received with its BGP Identifier as the ORIGINATOR_ID.
- if id := path.GetOriginatorID(); peer.fsm.gConf.Config.RouterId == id.String() {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": peer.ID(),
- "OriginatorID": id,
- "Data": path,
- }).Debug("Originator ID is mine, ignore")
- return nil
- }
- if info.RouteReflectorClient {
- ignore = false
- }
- if peer.isRouteReflectorClient() {
- // RFC4456 8. Avoiding Routing Information Loops
- // If the local CLUSTER_ID is found in the CLUSTER_LIST,
- // the advertisement received SHOULD be ignored.
- for _, clusterID := range path.GetClusterList() {
- if clusterID.Equal(peer.fsm.peerInfo.RouteReflectorClusterID) {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": peer.ID(),
- "ClusterID": clusterID,
- "Data": path,
- }).Debug("cluster list path attribute has local cluster id, ignore")
- return nil
- }
- }
- ignore = false
- }
- }
-
- if ignore {
- if !path.IsWithdraw && old != nil {
- oldSource := old.GetSource()
- if old.IsLocal() || oldSource.Address.String() != peer.ID() && oldSource.AS != peer.AS() {
- // In this case, we suppose this peer has the same prefix
- // received from another iBGP peer.
- // So we withdraw the old best which was injected locally
- // (from CLI or gRPC for example) in order to avoid the
- // old best left on peers.
- // Also, we withdraw the eBGP route which is the old best.
- // When we got the new best from iBGP, we don't advertise
- // the new best and need to withdraw the old best.
- return old.Clone(true)
- }
- }
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": peer.ID(),
- "Data": path,
- }).Debug("From same AS, ignore.")
- return nil
- }
- }
-
- if path = peer.filterPathFromSourcePeer(path, old); path == nil {
- return nil
- }
-
- if !peer.isRouteServerClient() && isASLoop(peer, path) {
- return nil
- }
- return path
-}
-
-func (s *BgpServer) filterpath(peer *Peer, path, old *table.Path) *table.Path {
- // Special handling for RTM NLRI.
- if path != nil && path.GetRouteFamily() == bgp.RF_RTC_UC && !path.IsWithdraw {
- // If the given "path" is locally generated and the same with "old", we
- // assumes "path" was already sent before. This assumption avoids the
- // infinite UPDATE loop between Route Reflector and its clients.
- if path.IsLocal() && path == old {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": peer.fsm.pConf.State.NeighborAddress,
- "Path": path,
- }).Debug("given rtm nlri is already sent, skipping to advertise")
- return nil
- }
-
- if old != nil && old.IsLocal() {
- // We assumes VRF with the specific RT is deleted.
- path = old.Clone(true)
- } else if peer.isRouteReflectorClient() {
- // We need to send the path even if the peer is originator of the
- // path in order to signal that the client should distribute route
- // with the given RT.
- } else {
- // We send a path even if it is not the best path. See comments in
- // (*Destination) GetChanges().
- dst := peer.localRib.GetDestination(path)
- path = nil
- for _, p := range dst.GetKnownPathList(peer.TableID(), peer.AS()) {
- srcPeer := p.GetSource()
- if peer.ID() != srcPeer.Address.String() {
- if srcPeer.RouteReflectorClient {
- // The path from a RR client is preferred than others
- // for the case that RR and non RR client peering
- // (e.g., peering of different RR clusters).
- path = p
- break
- } else if path == nil {
- path = p
- }
- }
- }
- }
- }
-
- // only allow vpnv4 and vpnv6 paths to be advertised to VRFed neighbors.
- // also check we can import this path using table.CanImportToVrf()
- // if we can, make it local path by calling (*Path).ToLocal()
- if path != nil && peer.fsm.pConf.Config.Vrf != "" {
- if f := path.GetRouteFamily(); f != bgp.RF_IPv4_VPN && f != bgp.RF_IPv6_VPN {
- return nil
- }
- vrf := peer.localRib.Vrfs[peer.fsm.pConf.Config.Vrf]
- if table.CanImportToVrf(vrf, path) {
- path = path.ToLocal()
- } else {
- return nil
- }
- }
-
- // replace-peer-as handling
- if path != nil && !path.IsWithdraw && peer.fsm.pConf.AsPathOptions.State.ReplacePeerAs {
- path = path.ReplaceAS(peer.fsm.pConf.Config.LocalAs, peer.fsm.pConf.Config.PeerAs)
- }
-
- if path = filterpath(peer, path, old); path == nil {
- return nil
- }
-
- options := &table.PolicyOptions{
- Info: peer.fsm.peerInfo,
- OldNextHop: path.GetNexthop(),
- }
- path = table.UpdatePathAttrs(peer.fsm.gConf, peer.fsm.pConf, peer.fsm.peerInfo, path)
-
- if v := s.roaManager.validate(path); v != nil {
- options.ValidationResult = v
- }
-
- path = peer.policy.ApplyPolicy(peer.TableID(), table.POLICY_DIRECTION_EXPORT, path, options)
- // When 'path' is filtered (path == nil), check 'old' has been sent to this peer.
- // If it has, send withdrawal to the peer.
- if path == nil && old != nil {
- o := peer.policy.ApplyPolicy(peer.TableID(), table.POLICY_DIRECTION_EXPORT, old, options)
- if o != nil {
- path = old.Clone(true)
- }
- }
-
- // draft-uttaro-idr-bgp-persistence-02
- // 4.3. Processing LLGR_STALE Routes
- //
- // The route SHOULD NOT be advertised to any neighbor from which the
- // Long-lived Graceful Restart Capability has not been received. The
- // exception is described in the Optional Partial Deployment
- // Procedure section (Section 4.7). Note that this requirement
- // implies that such routes should be withdrawn from any such neighbor.
- if path != nil && !path.IsWithdraw && !peer.isLLGREnabledFamily(path.GetRouteFamily()) && path.IsLLGRStale() {
- // we send unnecessary withdrawn even if we didn't
- // sent the route.
- path = path.Clone(true)
- }
-
- // remove local-pref attribute
- // we should do this after applying export policy since policy may
- // set local-preference
- if path != nil && !peer.isIBGPPeer() && !peer.isRouteServerClient() {
- path.RemoveLocalPref()
- }
- return path
-}
-
-func clonePathList(pathList []*table.Path) []*table.Path {
- l := make([]*table.Path, 0, len(pathList))
- for _, p := range pathList {
- if p != nil {
- l = append(l, p.Clone(p.IsWithdraw))
- }
- }
- return l
-}
-
-func (server *BgpServer) notifyBestWatcher(best []*table.Path, multipath [][]*table.Path) {
- if table.SelectionOptions.DisableBestPathSelection {
- // Note: If best path selection disabled, no best path to notify.
- return
- }
- clonedM := make([][]*table.Path, len(multipath))
- for i, pathList := range multipath {
- clonedM[i] = clonePathList(pathList)
- }
- clonedB := clonePathList(best)
- m := make(map[string]uint16)
- for _, p := range clonedB {
- switch p.GetRouteFamily() {
- case bgp.RF_IPv4_VPN, bgp.RF_IPv6_VPN:
- for _, vrf := range server.globalRib.Vrfs {
- if vrf.Id != 0 && table.CanImportToVrf(vrf, p) {
- m[p.GetNlri().String()] = uint16(vrf.Id)
- }
- }
- }
- }
- w := &WatchEventBestPath{PathList: clonedB, MultiPathList: clonedM}
- if len(m) > 0 {
- w.Vrf = m
- }
- server.notifyWatcher(WATCH_EVENT_TYPE_BEST_PATH, w)
-}
-
-func (s *BgpServer) ToConfig(peer *Peer, getAdvertised bool) *config.Neighbor {
- // create copy which can be access to without mutex
- conf := *peer.fsm.pConf
-
- conf.AfiSafis = make([]config.AfiSafi, len(peer.fsm.pConf.AfiSafis))
- for i, af := range peer.fsm.pConf.AfiSafis {
- conf.AfiSafis[i] = af
- conf.AfiSafis[i].AddPaths.State.Receive = peer.isAddPathReceiveEnabled(af.State.Family)
- if peer.isAddPathSendEnabled(af.State.Family) {
- conf.AfiSafis[i].AddPaths.State.SendMax = af.AddPaths.State.SendMax
- } else {
- conf.AfiSafis[i].AddPaths.State.SendMax = 0
- }
- }
-
- remoteCap := make([]bgp.ParameterCapabilityInterface, 0, len(peer.fsm.capMap))
- for _, caps := range peer.fsm.capMap {
- for _, m := range caps {
- // need to copy all values here
- buf, _ := m.Serialize()
- c, _ := bgp.DecodeCapability(buf)
- remoteCap = append(remoteCap, c)
- }
- }
- conf.State.RemoteCapabilityList = remoteCap
- conf.State.LocalCapabilityList = capabilitiesFromConfig(peer.fsm.pConf)
-
- conf.State.SessionState = config.IntToSessionStateMap[int(peer.fsm.state)]
- conf.State.AdminState = config.IntToAdminStateMap[int(peer.fsm.adminState)]
-
- if peer.fsm.state == bgp.BGP_FSM_ESTABLISHED {
- rfList := peer.configuredRFlist()
- if getAdvertised {
- pathList, filtered := s.getBestFromLocal(peer, rfList)
- conf.State.AdjTable.Advertised = uint32(len(pathList))
- conf.State.AdjTable.Filtered = uint32(len(filtered))
- } else {
- conf.State.AdjTable.Advertised = 0
- }
- conf.State.AdjTable.Received = uint32(peer.adjRibIn.Count(rfList))
- conf.State.AdjTable.Accepted = uint32(peer.adjRibIn.Accepted(rfList))
-
- conf.Transport.State.LocalAddress, conf.Transport.State.LocalPort = peer.fsm.LocalHostPort()
- _, conf.Transport.State.RemotePort = peer.fsm.RemoteHostPort()
- buf, _ := peer.fsm.recvOpen.Serialize()
- // need to copy all values here
- conf.State.ReceivedOpenMessage, _ = bgp.ParseBGPMessage(buf)
- conf.State.RemoteRouterId = peer.fsm.peerInfo.ID.To4().String()
- }
- return &conf
-}
-
-func (server *BgpServer) notifyPrePolicyUpdateWatcher(peer *Peer, pathList []*table.Path, msg *bgp.BGPMessage, timestamp time.Time, payload []byte) {
- if !server.isWatched(WATCH_EVENT_TYPE_PRE_UPDATE) || peer == nil {
- return
- }
- cloned := clonePathList(pathList)
- if len(cloned) == 0 {
- return
- }
- _, y := peer.fsm.capMap[bgp.BGP_CAP_FOUR_OCTET_AS_NUMBER]
- l, _ := peer.fsm.LocalHostPort()
- ev := &WatchEventUpdate{
- Message: msg,
- PeerAS: peer.fsm.peerInfo.AS,
- LocalAS: peer.fsm.peerInfo.LocalAS,
- PeerAddress: peer.fsm.peerInfo.Address,
- LocalAddress: net.ParseIP(l),
- PeerID: peer.fsm.peerInfo.ID,
- FourBytesAs: y,
- Timestamp: timestamp,
- Payload: payload,
- PostPolicy: false,
- PathList: cloned,
- Neighbor: server.ToConfig(peer, false),
- }
- server.notifyWatcher(WATCH_EVENT_TYPE_PRE_UPDATE, ev)
-}
-
-func (server *BgpServer) notifyPostPolicyUpdateWatcher(peer *Peer, pathList []*table.Path) {
- if !server.isWatched(WATCH_EVENT_TYPE_POST_UPDATE) || peer == nil {
- return
- }
- cloned := clonePathList(pathList)
- if len(cloned) == 0 {
- return
- }
- _, y := peer.fsm.capMap[bgp.BGP_CAP_FOUR_OCTET_AS_NUMBER]
- l, _ := peer.fsm.LocalHostPort()
- ev := &WatchEventUpdate{
- PeerAS: peer.fsm.peerInfo.AS,
- LocalAS: peer.fsm.peerInfo.LocalAS,
- PeerAddress: peer.fsm.peerInfo.Address,
- LocalAddress: net.ParseIP(l),
- PeerID: peer.fsm.peerInfo.ID,
- FourBytesAs: y,
- Timestamp: cloned[0].GetTimestamp(),
- PostPolicy: true,
- PathList: cloned,
- Neighbor: server.ToConfig(peer, false),
- }
- server.notifyWatcher(WATCH_EVENT_TYPE_POST_UPDATE, ev)
-}
-
-func newWatchEventPeerState(peer *Peer, m *FsmMsg) *WatchEventPeerState {
- _, rport := peer.fsm.RemoteHostPort()
- laddr, lport := peer.fsm.LocalHostPort()
- sentOpen := buildopen(peer.fsm.gConf, peer.fsm.pConf)
- recvOpen := peer.fsm.recvOpen
- e := &WatchEventPeerState{
- PeerAS: peer.fsm.peerInfo.AS,
- LocalAS: peer.fsm.peerInfo.LocalAS,
- PeerAddress: peer.fsm.peerInfo.Address,
- LocalAddress: net.ParseIP(laddr),
- PeerPort: rport,
- LocalPort: lport,
- PeerID: peer.fsm.peerInfo.ID,
- SentOpen: sentOpen,
- RecvOpen: recvOpen,
- State: peer.fsm.state,
- AdminState: peer.fsm.adminState,
- Timestamp: time.Now(),
- PeerInterface: peer.fsm.pConf.Config.NeighborInterface,
- }
-
- if m != nil {
- e.StateReason = m.StateReason
- }
- return e
-}
-
-func (server *BgpServer) broadcastPeerState(peer *Peer, oldState bgp.FSMState, e *FsmMsg) {
- newState := peer.fsm.state
- if oldState == bgp.BGP_FSM_ESTABLISHED || newState == bgp.BGP_FSM_ESTABLISHED {
- server.notifyWatcher(WATCH_EVENT_TYPE_PEER_STATE, newWatchEventPeerState(peer, e))
- }
-}
-
-func (server *BgpServer) notifyMessageWatcher(peer *Peer, timestamp time.Time, msg *bgp.BGPMessage, isSent bool) {
- // validation should be done in the caller of this function
- _, y := peer.fsm.capMap[bgp.BGP_CAP_FOUR_OCTET_AS_NUMBER]
- l, _ := peer.fsm.LocalHostPort()
- ev := &WatchEventMessage{
- Message: msg,
- PeerAS: peer.fsm.peerInfo.AS,
- LocalAS: peer.fsm.peerInfo.LocalAS,
- PeerAddress: peer.fsm.peerInfo.Address,
- LocalAddress: net.ParseIP(l),
- PeerID: peer.fsm.peerInfo.ID,
- FourBytesAs: y,
- Timestamp: timestamp,
- IsSent: isSent,
- }
- if !isSent {
- server.notifyWatcher(WATCH_EVENT_TYPE_RECV_MSG, ev)
- }
-}
-
-func (server *BgpServer) notifyRecvMessageWatcher(peer *Peer, timestamp time.Time, msg *bgp.BGPMessage) {
- if peer == nil || !server.isWatched(WATCH_EVENT_TYPE_RECV_MSG) {
- return
- }
- server.notifyMessageWatcher(peer, timestamp, msg, false)
-}
-
-func (s *BgpServer) getBestFromLocal(peer *Peer, rfList []bgp.RouteFamily) ([]*table.Path, []*table.Path) {
- pathList := []*table.Path{}
- filtered := []*table.Path{}
- for _, family := range peer.toGlobalFamilies(rfList) {
- pl := func() []*table.Path {
- if peer.isAddPathSendEnabled(family) {
- return peer.localRib.GetPathList(peer.TableID(), peer.AS(), []bgp.RouteFamily{family})
- }
- return peer.localRib.GetBestPathList(peer.TableID(), peer.AS(), []bgp.RouteFamily{family})
- }()
- for _, path := range pl {
- if p := s.filterpath(peer, path, nil); p != nil {
- pathList = append(pathList, p)
- } else {
- filtered = append(filtered, path)
- }
- }
- }
- if peer.isGracefulRestartEnabled() {
- for _, family := range rfList {
- pathList = append(pathList, table.NewEOR(family))
- }
- }
- return pathList, filtered
-}
-
-func (s *BgpServer) processOutgoingPaths(peer *Peer, paths, olds []*table.Path) []*table.Path {
- if peer.fsm.state != bgp.BGP_FSM_ESTABLISHED {
- return nil
- }
- if peer.fsm.pConf.GracefulRestart.State.LocalRestarting {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": peer.fsm.pConf.State.NeighborAddress,
- }).Debug("now syncing, suppress sending updates")
- return nil
- }
-
- outgoing := make([]*table.Path, 0, len(paths))
-
- for idx, path := range paths {
- var old *table.Path
- if olds != nil {
- old = olds[idx]
- }
- if p := s.filterpath(peer, path, old); p != nil {
- outgoing = append(outgoing, p)
- }
- }
- return outgoing
-}
-
-func (s *BgpServer) handleRouteRefresh(peer *Peer, e *FsmMsg) []*table.Path {
- m := e.MsgData.(*bgp.BGPMessage)
- rr := m.Body.(*bgp.BGPRouteRefresh)
- rf := bgp.AfiSafiToRouteFamily(rr.AFI, rr.SAFI)
- if _, ok := peer.fsm.rfMap[rf]; !ok {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": peer.ID(),
- "Data": rf,
- }).Warn("Route family isn't supported")
- return nil
- }
- if _, ok := peer.fsm.capMap[bgp.BGP_CAP_ROUTE_REFRESH]; !ok {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": peer.ID(),
- }).Warn("ROUTE_REFRESH received but the capability wasn't advertised")
- return nil
- }
- rfList := []bgp.RouteFamily{rf}
- accepted, filtered := s.getBestFromLocal(peer, rfList)
- for _, path := range filtered {
- path.IsWithdraw = true
- accepted = append(accepted, path)
- }
- return accepted
-}
-
-func (server *BgpServer) propagateUpdate(peer *Peer, pathList []*table.Path) {
- rs := peer != nil && peer.isRouteServerClient()
- vrf := !rs && peer != nil && peer.fsm.pConf.Config.Vrf != ""
-
- tableId := table.GLOBAL_RIB_NAME
- rib := server.globalRib
- if rs {
- tableId = peer.TableID()
- rib = server.rsRib
- }
-
- for _, path := range pathList {
- if vrf {
- path = path.ToGlobal(rib.Vrfs[peer.fsm.pConf.Config.Vrf])
- }
-
- policyOptions := &table.PolicyOptions{}
-
- if !rs && peer != nil {
- policyOptions.Info = peer.fsm.peerInfo
- }
- if v := server.roaManager.validate(path); v != nil {
- policyOptions.ValidationResult = v
- }
-
- if p := server.policy.ApplyPolicy(tableId, table.POLICY_DIRECTION_IMPORT, path, policyOptions); p != nil {
- path = p
- } else {
- path = path.Clone(true)
- }
-
- if !rs {
- server.notifyPostPolicyUpdateWatcher(peer, []*table.Path{path})
-
- // RFC4684 Constrained Route Distribution 6. Operation
- //
- // When a BGP speaker receives a BGP UPDATE that advertises or withdraws
- // a given Route Target membership NLRI, it should examine the RIB-OUTs
- // of VPN NLRIs and re-evaluate the advertisement status of routes that
- // match the Route Target in question.
- //
- // A BGP speaker should generate the minimum set of BGP VPN route
- // updates (advertisements and/or withdraws) necessary to transition
- // between the previous and current state of the route distribution
- // graph that is derived from Route Target membership information.
- if peer != nil && path != nil && path.GetRouteFamily() == bgp.RF_RTC_UC {
- rt := path.GetNlri().(*bgp.RouteTargetMembershipNLRI).RouteTarget
- fs := make([]bgp.RouteFamily, 0, len(peer.negotiatedRFList()))
- for _, f := range peer.negotiatedRFList() {
- if f != bgp.RF_RTC_UC {
- fs = append(fs, f)
- }
- }
- var candidates []*table.Path
- if path.IsWithdraw {
- // Note: The paths to be withdrawn are filtered because the
- // given RT on RTM NLRI is already removed from adj-RIB-in.
- _, candidates = server.getBestFromLocal(peer, fs)
- } else {
- candidates = server.globalRib.GetBestPathList(peer.TableID(), 0, fs)
- }
- paths := make([]*table.Path, 0, len(candidates))
- for _, p := range candidates {
- for _, ext := range p.GetExtCommunities() {
- if rt == nil || ext.String() == rt.String() {
- if path.IsWithdraw {
- p = p.Clone(true)
- }
- paths = append(paths, p)
- break
- }
- }
- }
- if path.IsWithdraw {
- // Skips filtering because the paths are already filtered
- // and the withdrawal does not need the path attributes.
- } else {
- paths = server.processOutgoingPaths(peer, paths, nil)
- }
- sendFsmOutgoingMsg(peer, paths, nil, false)
- }
- }
-
- if dsts := rib.Update(path); len(dsts) > 0 {
- server.propagateUpdateToNeighbors(peer, path, dsts, true)
- }
- }
-}
-
-func (server *BgpServer) dropPeerAllRoutes(peer *Peer, families []bgp.RouteFamily) {
- rib := server.globalRib
- if peer.isRouteServerClient() {
- rib = server.rsRib
- }
- for _, family := range peer.toGlobalFamilies(families) {
- for _, path := range rib.GetPathListByPeer(peer.fsm.peerInfo, family) {
- p := path.Clone(true)
- if dsts := rib.Update(p); len(dsts) > 0 {
- server.propagateUpdateToNeighbors(peer, p, dsts, false)
- }
- }
- }
-}
-
-func dstsToPaths(id string, as uint32, dsts []*table.Update) ([]*table.Path, []*table.Path, [][]*table.Path) {
- bestList := make([]*table.Path, 0, len(dsts))
- oldList := make([]*table.Path, 0, len(dsts))
- mpathList := make([][]*table.Path, 0, len(dsts))
-
- for _, dst := range dsts {
- best, old, mpath := dst.GetChanges(id, as, false)
- bestList = append(bestList, best)
- oldList = append(oldList, old)
- if mpath != nil {
- mpathList = append(mpathList, mpath)
- }
- }
- return bestList, oldList, mpathList
-}
-
-func (server *BgpServer) propagateUpdateToNeighbors(source *Peer, newPath *table.Path, dsts []*table.Update, needOld bool) {
- if table.SelectionOptions.DisableBestPathSelection {
- return
- }
- var gBestList, gOldList, bestList, oldList []*table.Path
- var mpathList [][]*table.Path
- if source == nil || !source.isRouteServerClient() {
- gBestList, gOldList, mpathList = dstsToPaths(table.GLOBAL_RIB_NAME, 0, dsts)
- server.notifyBestWatcher(gBestList, mpathList)
- }
- family := newPath.GetRouteFamily()
- for _, targetPeer := range server.neighborMap {
- if (source == nil && targetPeer.isRouteServerClient()) || (source != nil && source.isRouteServerClient() != targetPeer.isRouteServerClient()) {
- continue
- }
- f := func() bgp.RouteFamily {
- if targetPeer.fsm.pConf.Config.Vrf != "" {
- switch family {
- case bgp.RF_IPv4_VPN:
- return bgp.RF_IPv4_UC
- case bgp.RF_IPv6_VPN:
- return bgp.RF_IPv6_UC
- }
- }
- return family
- }()
- if targetPeer.isAddPathSendEnabled(f) {
- if newPath.IsWithdraw {
- bestList = func() []*table.Path {
- l := make([]*table.Path, 0, len(dsts))
- for _, d := range dsts {
- l = append(l, d.GetWithdrawnPath()...)
- }
- return l
- }()
- } else {
- bestList = []*table.Path{newPath}
- if newPath.GetRouteFamily() == bgp.RF_RTC_UC {
- // we assumes that new "path" nlri was already sent before. This assumption avoids the
- // infinite UPDATE loop between Route Reflector and its clients.
- for _, old := range dsts[0].OldKnownPathList {
- if old.IsLocal() {
- bestList = []*table.Path{}
- break
- }
- }
- }
- }
- oldList = nil
- } else if targetPeer.isRouteServerClient() {
- bestList, oldList, _ = dstsToPaths(targetPeer.TableID(), targetPeer.AS(), dsts)
- } else {
- bestList = gBestList
- oldList = gOldList
- }
- if !needOld {
- oldList = nil
- }
- if paths := server.processOutgoingPaths(targetPeer, bestList, oldList); len(paths) > 0 {
- sendFsmOutgoingMsg(targetPeer, paths, nil, false)
- }
- }
-}
-
-func (server *BgpServer) handleFSMMessage(peer *Peer, e *FsmMsg) {
- switch e.MsgType {
- case FSM_MSG_STATE_CHANGE:
- nextState := e.MsgData.(bgp.FSMState)
- oldState := bgp.FSMState(peer.fsm.pConf.State.SessionState.ToInt())
- peer.fsm.pConf.State.SessionState = config.IntToSessionStateMap[int(nextState)]
- peer.fsm.StateChange(nextState)
-
- // PeerDown
- if oldState == bgp.BGP_FSM_ESTABLISHED {
- t := time.Now()
- if t.Sub(time.Unix(peer.fsm.pConf.Timers.State.Uptime, 0)) < FLOP_THRESHOLD {
- peer.fsm.pConf.State.Flops++
- }
- var drop []bgp.RouteFamily
- if peer.fsm.reason.Type == FSM_GRACEFUL_RESTART {
- peer.fsm.pConf.GracefulRestart.State.PeerRestarting = true
- var p []bgp.RouteFamily
- p, drop = peer.forwardingPreservedFamilies()
- server.propagateUpdate(peer, peer.StaleAll(p))
- } else {
- drop = peer.configuredRFlist()
- }
- peer.prefixLimitWarned = make(map[bgp.RouteFamily]bool)
- peer.DropAll(drop)
- server.dropPeerAllRoutes(peer, drop)
- if peer.fsm.pConf.Config.PeerAs == 0 {
- 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)
- server.broadcastPeerState(peer, oldState, e)
- return
- }
- } 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()
-
- peer.DropAll(no_llgr)
- server.dropPeerAllRoutes(peer, no_llgr)
-
- // attach LLGR_STALE community to paths in peer's adj-rib-in
- // paths with NO_LLGR are deleted
- pathList := peer.markLLGRStale(llgr)
-
- // calculate again
- // wheh path with LLGR_STALE chosen as best,
- // peer which doesn't support LLGR will drop the path
- // if it is in adj-rib-out, do withdrawal
- server.propagateUpdate(peer, pathList)
-
- for _, f := range llgr {
- endCh := make(chan struct{})
- peer.llgrEndChs = append(peer.llgrEndChs, endCh)
- go func(family bgp.RouteFamily, endCh chan struct{}) {
- t := peer.llgrRestartTime(family)
- timer := time.NewTimer(time.Second * time.Duration(t))
-
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": peer.ID(),
- "Family": family,
- }).Debugf("start LLGR restart timer (%d sec) for %s", t, family)
-
- select {
- case <-timer.C:
- server.mgmtOperation(func() error {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": peer.ID(),
- "Family": family,
- }).Debugf("LLGR restart timer (%d sec) for %s expired", t, family)
- peer.DropAll([]bgp.RouteFamily{family})
- server.dropPeerAllRoutes(peer, []bgp.RouteFamily{family})
-
- // when all llgr restart timer expired, stop PeerRestarting
- if peer.llgrRestartTimerExpired(family) {
- peer.stopPeerRestarting()
- }
- return nil
- }, false)
- case <-endCh:
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": peer.ID(),
- "Family": family,
- }).Debugf("stop LLGR restart timer (%d sec) for %s", t, family)
- }
- }(f, endCh)
- }
- } else {
- // RFC 4724 4.2
- // If the session does not get re-established within the "Restart Time"
- // that the peer advertised previously, the Receiving Speaker MUST
- // delete all the stale routes from the peer that it is retaining.
- peer.fsm.pConf.GracefulRestart.State.PeerRestarting = false
- peer.DropAll(peer.configuredRFlist())
- server.dropPeerAllRoutes(peer, peer.configuredRFlist())
- }
- }
-
- cleanInfiniteChannel(peer.outgoing)
- peer.outgoing = channels.NewInfiniteChannel()
- if nextState == bgp.BGP_FSM_ESTABLISHED {
- // update for export policy
- laddr, _ := peer.fsm.LocalHostPort()
- // may include zone info
- peer.fsm.pConf.Transport.State.LocalAddress = laddr
- // exclude zone info
- ipaddr, _ := net.ResolveIPAddr("ip", laddr)
- peer.fsm.peerInfo.LocalAddress = ipaddr.IP
- deferralExpiredFunc := func(family bgp.RouteFamily) func() {
- return func() {
- server.mgmtOperation(func() error {
- server.softResetOut(peer.fsm.pConf.State.NeighborAddress, family, true)
- return nil
- }, false)
- }
- }
- if !peer.fsm.pConf.GracefulRestart.State.LocalRestarting {
- // When graceful-restart cap (which means intention
- // of sending EOR) and route-target address family are negotiated,
- // send route-target NLRIs first, and wait to send others
- // till receiving EOR of route-target address family.
- // This prevents sending uninterested routes to peers.
- //
- // However, when the peer is graceful restarting, give up
- // waiting sending non-route-target NLRIs since the peer won't send
- // any routes (and EORs) before we send ours (or deferral-timer expires).
- var pathList []*table.Path
- _, y := peer.fsm.rfMap[bgp.RF_RTC_UC]
- if c := peer.fsm.pConf.GetAfiSafi(bgp.RF_RTC_UC); y && !peer.fsm.pConf.GracefulRestart.State.PeerRestarting && c.RouteTargetMembership.Config.DeferralTime > 0 {
- pathList, _ = server.getBestFromLocal(peer, []bgp.RouteFamily{bgp.RF_RTC_UC})
- t := c.RouteTargetMembership.Config.DeferralTime
- for _, f := range peer.negotiatedRFList() {
- if f != bgp.RF_RTC_UC {
- time.AfterFunc(time.Second*time.Duration(t), deferralExpiredFunc(f))
- }
- }
- } else {
- pathList, _ = server.getBestFromLocal(peer, peer.negotiatedRFList())
- }
-
- if len(pathList) > 0 {
- sendFsmOutgoingMsg(peer, pathList, nil, false)
- }
- } else {
- // RFC 4724 4.1
- // Once the session between the Restarting Speaker and the Receiving
- // Speaker is re-established, ...snip... it MUST defer route
- // selection for an address family until it either (a) receives the
- // End-of-RIB marker from all its peers (excluding the ones with the
- // "Restart State" bit set in the received capability and excluding the
- // ones that do not advertise the graceful restart capability) or (b)
- // the Selection_Deferral_Timer referred to below has expired.
- allEnd := func() bool {
- for _, p := range server.neighborMap {
- if !p.recvedAllEOR() {
- return false
- }
- }
- return true
- }()
- if allEnd {
- for _, p := range server.neighborMap {
- p.fsm.pConf.GracefulRestart.State.LocalRestarting = false
- if !p.isGracefulRestartEnabled() {
- continue
- }
- paths, _ := server.getBestFromLocal(p, p.configuredRFlist())
- if len(paths) > 0 {
- sendFsmOutgoingMsg(p, paths, nil, false)
- }
- }
- log.WithFields(log.Fields{
- "Topic": "Server",
- }).Info("sync finished")
- } else {
- deferral := peer.fsm.pConf.GracefulRestart.Config.DeferralTime
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": peer.ID(),
- }).Debugf("Now syncing, suppress sending updates. start deferral timer(%d)", deferral)
- time.AfterFunc(time.Second*time.Duration(deferral), deferralExpiredFunc(bgp.RouteFamily(0)))
- }
- }
- } else {
- if server.shutdownWG != nil && nextState == bgp.BGP_FSM_IDLE {
- die := true
- for _, p := range server.neighborMap {
- if p.fsm.state != bgp.BGP_FSM_IDLE {
- die = false
- break
- }
- }
- if die {
- server.shutdownWG.Done()
- }
- }
- peer.fsm.pConf.Timers.State.Downtime = time.Now().Unix()
- }
- // clear counter
- if peer.fsm.adminState == ADMIN_STATE_DOWN {
- peer.fsm.pConf.State = config.NeighborState{}
- peer.fsm.pConf.State.NeighborAddress = peer.fsm.pConf.Config.NeighborAddress
- peer.fsm.pConf.State.PeerAs = peer.fsm.pConf.Config.PeerAs
- peer.fsm.pConf.Timers.State = config.TimersState{}
- }
- peer.startFSMHandler(server.fsmincomingCh, server.fsmStateCh)
- server.broadcastPeerState(peer, oldState, e)
- case FSM_MSG_ROUTE_REFRESH:
- if peer.fsm.state != bgp.BGP_FSM_ESTABLISHED || e.timestamp.Unix() < peer.fsm.pConf.Timers.State.Uptime {
- return
- }
- if paths := server.handleRouteRefresh(peer, e); len(paths) > 0 {
- sendFsmOutgoingMsg(peer, paths, nil, false)
- return
- }
- case FSM_MSG_BGP_MESSAGE:
- switch m := e.MsgData.(type) {
- case *bgp.MessageError:
- sendFsmOutgoingMsg(peer, nil, bgp.NewBGPNotificationMessage(m.TypeCode, m.SubTypeCode, m.Data), false)
- return
- case *bgp.BGPMessage:
- server.notifyRecvMessageWatcher(peer, e.timestamp, m)
- if peer.fsm.state != bgp.BGP_FSM_ESTABLISHED || e.timestamp.Unix() < peer.fsm.pConf.Timers.State.Uptime {
- return
- }
- pathList, eor, notification := peer.handleUpdate(e)
- if notification != nil {
- sendFsmOutgoingMsg(peer, nil, notification, true)
- return
- }
- if m.Header.Type == bgp.BGP_MSG_UPDATE {
- server.notifyPrePolicyUpdateWatcher(peer, pathList, m, e.timestamp, e.payload)
- }
-
- if len(pathList) > 0 {
- server.propagateUpdate(peer, pathList)
- }
-
- if len(eor) > 0 {
- rtc := false
- for _, f := range eor {
- if f == bgp.RF_RTC_UC {
- rtc = true
- }
- for i, a := range peer.fsm.pConf.AfiSafis {
- if a.State.Family == f {
- peer.fsm.pConf.AfiSafis[i].MpGracefulRestart.State.EndOfRibReceived = true
- }
- }
- }
-
- // RFC 4724 4.1
- // Once the session between the Restarting Speaker and the Receiving
- // Speaker is re-established, ...snip... it MUST defer route
- // selection for an address family until it either (a) receives the
- // End-of-RIB marker from all its peers (excluding the ones with the
- // "Restart State" bit set in the received capability and excluding the
- // ones that do not advertise the graceful restart capability) or ...snip...
- if peer.fsm.pConf.GracefulRestart.State.LocalRestarting {
- allEnd := func() bool {
- for _, p := range server.neighborMap {
- if !p.recvedAllEOR() {
- return false
- }
- }
- return true
- }()
- if allEnd {
- for _, p := range server.neighborMap {
- p.fsm.pConf.GracefulRestart.State.LocalRestarting = false
- if !p.isGracefulRestartEnabled() {
- continue
- }
- paths, _ := server.getBestFromLocal(p, p.negotiatedRFList())
- if len(paths) > 0 {
- sendFsmOutgoingMsg(p, paths, nil, false)
- }
- }
- log.WithFields(log.Fields{
- "Topic": "Server",
- }).Info("sync finished")
-
- }
-
- // we don't delay non-route-target NLRIs when local-restarting
- rtc = false
- }
- if peer.fsm.pConf.GracefulRestart.State.PeerRestarting {
- if peer.recvedAllEOR() {
- peer.stopPeerRestarting()
- pathList := peer.adjRibIn.DropStale(peer.configuredRFlist())
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": peer.fsm.pConf.State.NeighborAddress,
- }).Debugf("withdraw %d stale routes", len(pathList))
- server.propagateUpdate(peer, pathList)
- }
-
- // we don't delay non-route-target NLRIs when peer is restarting
- rtc = false
- }
-
- // received EOR of route-target address family
- // outbound filter is now ready, let's flash non-route-target NLRIs
- if c := peer.fsm.pConf.GetAfiSafi(bgp.RF_RTC_UC); rtc && c != nil && c.RouteTargetMembership.Config.DeferralTime > 0 {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": peer.ID(),
- }).Debug("received route-target eor. flash non-route-target NLRIs")
- families := make([]bgp.RouteFamily, 0, len(peer.negotiatedRFList()))
- for _, f := range peer.negotiatedRFList() {
- if f != bgp.RF_RTC_UC {
- families = append(families, f)
- }
- }
- if paths, _ := server.getBestFromLocal(peer, families); len(paths) > 0 {
- sendFsmOutgoingMsg(peer, paths, nil, false)
- }
- }
- }
- default:
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": peer.fsm.pConf.State.NeighborAddress,
- "Data": e.MsgData,
- }).Panic("unknown msg type")
- }
- }
-}
-
-func (s *BgpServer) AddCollector(c *config.CollectorConfig) error {
- return s.mgmtOperation(func() error {
- _, err := NewCollector(s, c.Url, c.DbName, c.TableDumpInterval)
- return err
- }, false)
-}
-
-func (s *BgpServer) StartZebraClient(c *config.ZebraConfig) error {
- return s.mgmtOperation(func() error {
- if s.zclient != nil {
- return fmt.Errorf("already connected to Zebra")
- }
- protos := make([]string, 0, len(c.RedistributeRouteTypeList))
- for _, p := range c.RedistributeRouteTypeList {
- protos = append(protos, string(p))
- }
- var err error
- s.zclient, err = newZebraClient(s, c.Url, protos, c.Version, c.NexthopTriggerEnable, c.NexthopTriggerDelay)
- return err
- }, false)
-}
-
-func (s *BgpServer) AddBmp(c *config.BmpServerConfig) error {
- return s.mgmtOperation(func() error {
- return s.bmpManager.addServer(c)
- }, true)
-}
-
-func (s *BgpServer) DeleteBmp(c *config.BmpServerConfig) error {
- return s.mgmtOperation(func() error {
- return s.bmpManager.deleteServer(c)
- }, true)
-}
-
-func (s *BgpServer) Shutdown() {
- s.mgmtOperation(func() error {
- s.shutdownWG = new(sync.WaitGroup)
- s.shutdownWG.Add(1)
- stateOp := AdminStateOperation{
- State: ADMIN_STATE_DOWN,
- Communication: nil,
- }
- for _, p := range s.neighborMap {
- p.fsm.adminStateCh <- stateOp
- }
- // TODO: call fsmincomingCh.Close()
- return nil
- }, false)
-
- // Waits for all goroutines per peer to stop.
- // Note: This should not be wrapped with s.mgmtOperation() in order to
- // avoid the deadlock in the main goroutine of BgpServer.
- if s.shutdownWG != nil {
- s.shutdownWG.Wait()
- s.shutdownWG = nil
- }
-}
-
-func (s *BgpServer) UpdatePolicy(policy config.RoutingPolicy) error {
- return s.mgmtOperation(func() error {
- ap := make(map[string]config.ApplyPolicy, len(s.neighborMap)+1)
- ap[table.GLOBAL_RIB_NAME] = s.bgpConfig.Global.ApplyPolicy
- for _, peer := range s.neighborMap {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": peer.fsm.pConf.State.NeighborAddress,
- }).Info("call set policy")
- ap[peer.ID()] = peer.fsm.pConf.ApplyPolicy
- }
- return s.policy.Reset(&policy, ap)
- }, false)
-}
-
-// EVPN MAC MOBILITY HANDLING
-//
-// We don't have multihoming function now, so ignore
-// ESI comparison.
-//
-// RFC7432 15. MAC Mobility
-//
-// A PE detecting a locally attached MAC address for which it had
-// previously received a MAC/IP Advertisement route with the same zero
-// Ethernet segment identifier (single-homed scenarios) advertises it
-// with a MAC Mobility extended community attribute with the sequence
-// number set properly. In the case of single-homed scenarios, there
-// is no need for ESI comparison.
-
-func getMacMobilityExtendedCommunity(etag uint32, mac net.HardwareAddr, evpnPaths []*table.Path) *bgp.MacMobilityExtended {
- seqs := make([]struct {
- seq int
- isLocal bool
- }, 0)
-
- for _, path := range evpnPaths {
- nlri := path.GetNlri().(*bgp.EVPNNLRI)
- target, ok := nlri.RouteTypeData.(*bgp.EVPNMacIPAdvertisementRoute)
- if !ok {
- continue
- }
- if target.ETag == etag && bytes.Equal(target.MacAddress, mac) {
- found := false
- for _, ec := range path.GetExtCommunities() {
- if t, st := ec.GetTypes(); t == bgp.EC_TYPE_EVPN && st == bgp.EC_SUBTYPE_MAC_MOBILITY {
- seqs = append(seqs, struct {
- seq int
- isLocal bool
- }{int(ec.(*bgp.MacMobilityExtended).Sequence), path.IsLocal()})
- found = true
- break
- }
- }
-
- if !found {
- seqs = append(seqs, struct {
- seq int
- isLocal bool
- }{-1, path.IsLocal()})
- }
- }
- }
-
- if len(seqs) > 0 {
- newSeq := -2
- var isLocal bool
- for _, seq := range seqs {
- if seq.seq > newSeq {
- newSeq = seq.seq
- isLocal = seq.isLocal
- }
- }
-
- if !isLocal {
- newSeq += 1
- }
-
- if newSeq != -1 {
- return &bgp.MacMobilityExtended{
- Sequence: uint32(newSeq),
- }
- }
- }
- return nil
-}
-
-func (server *BgpServer) fixupApiPath(vrfId string, pathList []*table.Path) error {
- pi := &table.PeerInfo{
- AS: server.bgpConfig.Global.Config.As,
- LocalID: net.ParseIP(server.bgpConfig.Global.Config.RouterId).To4(),
- }
-
- for _, path := range pathList {
- if !path.IsWithdraw {
- if _, err := path.GetOrigin(); err != nil {
- return err
- }
- }
-
- if path.GetSource() == nil {
- path.SetSource(pi)
- }
-
- if vrfId != "" {
- vrf := server.globalRib.Vrfs[vrfId]
- if vrf == nil {
- return fmt.Errorf("vrf %s not found", vrfId)
- }
- if err := vrf.ToGlobalPath(path); err != nil {
- return err
- }
- }
-
- // Address Family specific Handling
- switch nlri := path.GetNlri().(type) {
- case *bgp.EVPNNLRI:
- switch r := nlri.RouteTypeData.(type) {
- case *bgp.EVPNMacIPAdvertisementRoute:
- // MAC Mobility Extended Community
- paths := server.globalRib.GetBestPathList(table.GLOBAL_RIB_NAME, 0, []bgp.RouteFamily{bgp.RF_EVPN})
- if m := getMacMobilityExtendedCommunity(r.ETag, r.MacAddress, paths); m != nil {
- path.SetExtCommunities([]bgp.ExtendedCommunityInterface{m}, false)
- }
- case *bgp.EVPNEthernetSegmentRoute:
- // RFC7432: BGP MPLS-Based Ethernet VPN
- // 7.6. ES-Import Route Target
- // The value is derived automatically for the ESI Types 1, 2,
- // and 3, by encoding the high-order 6-octet portion of the 9-octet ESI
- // Value, which corresponds to a MAC address, in the ES-Import Route
- // Target.
- // Note: If the given path already has the ES-Import Route Target,
- // skips deriving a new one.
- found := false
- for _, extComm := range path.GetExtCommunities() {
- if _, found = extComm.(*bgp.ESImportRouteTarget); found {
- break
- }
- }
- if !found {
- switch r.ESI.Type {
- case bgp.ESI_LACP, bgp.ESI_MSTP, bgp.ESI_MAC:
- mac := net.HardwareAddr(r.ESI.Value[0:6])
- rt := &bgp.ESImportRouteTarget{ESImport: mac}
- path.SetExtCommunities([]bgp.ExtendedCommunityInterface{rt}, false)
- }
- }
- }
- }
- }
- return nil
-}
-
-func pathTokey(path *table.Path) string {
- return fmt.Sprintf("%d:%s", path.GetNlri().PathIdentifier(), path.GetNlri().String())
-}
-
-func (s *BgpServer) AddPath(vrfId string, pathList []*table.Path) (uuidBytes []byte, err error) {
- err = s.mgmtOperation(func() error {
- if err := s.fixupApiPath(vrfId, pathList); err != nil {
- return err
- }
- if len(pathList) == 1 {
- path := pathList[0]
- id, _ := uuid.NewV4()
- s.uuidMap[id] = pathTokey(path)
- uuidBytes = id.Bytes()
- }
- s.propagateUpdate(nil, pathList)
- return nil
- }, true)
- return
-}
-
-func (s *BgpServer) DeletePath(uuidBytes []byte, f bgp.RouteFamily, vrfId string, pathList []*table.Path) error {
- return s.mgmtOperation(func() error {
- deletePathList := make([]*table.Path, 0)
- if len(uuidBytes) > 0 {
- // Delete locally generated path which has the given UUID
- path := func() *table.Path {
- id, _ := uuid.FromBytes(uuidBytes)
- if key, ok := s.uuidMap[id]; !ok {
- return nil
- } else {
- for _, path := range s.globalRib.GetPathList(table.GLOBAL_RIB_NAME, 0, s.globalRib.GetRFlist()) {
- if path.IsLocal() && key == pathTokey(path) {
- delete(s.uuidMap, id)
- return path
- }
- }
- }
- return nil
- }()
- if path == nil {
- return fmt.Errorf("Can't find a specified path")
- }
- deletePathList = append(deletePathList, path.Clone(true))
- } else if len(pathList) == 0 {
- // Delete all locally generated paths
- families := s.globalRib.GetRFlist()
- if f != 0 {
- families = []bgp.RouteFamily{f}
- }
- for _, path := range s.globalRib.GetPathList(table.GLOBAL_RIB_NAME, 0, families) {
- if path.IsLocal() {
- deletePathList = append(deletePathList, path.Clone(true))
- }
- }
- s.uuidMap = make(map[uuid.UUID]string)
- } else {
- if err := s.fixupApiPath(vrfId, pathList); err != nil {
- return err
- }
- deletePathList = pathList
- }
- s.propagateUpdate(nil, deletePathList)
- return nil
- }, true)
-}
-
-func (s *BgpServer) UpdatePath(vrfId string, pathList []*table.Path) error {
- err := s.mgmtOperation(func() error {
- if err := s.fixupApiPath(vrfId, pathList); err != nil {
- return err
- }
- s.propagateUpdate(nil, pathList)
- return nil
- }, true)
- return err
-}
-
-func (s *BgpServer) Start(c *config.Global) error {
- return s.mgmtOperation(func() error {
- if err := config.SetDefaultGlobalConfigValues(c); err != nil {
- return err
- }
-
- if c.Config.Port > 0 {
- acceptCh := make(chan *net.TCPConn, 4096)
- for _, addr := range c.Config.LocalAddressList {
- l, err := NewTCPListener(addr, uint32(c.Config.Port), acceptCh)
- if err != nil {
- return err
- }
- s.listeners = append(s.listeners, l)
- }
- s.acceptCh = acceptCh
- }
-
- rfs, _ := config.AfiSafis(c.AfiSafis).ToRfList()
- s.globalRib = table.NewTableManager(rfs)
- s.rsRib = table.NewTableManager(rfs)
-
- if err := s.policy.Reset(&config.RoutingPolicy{}, map[string]config.ApplyPolicy{}); err != nil {
- return err
- }
- s.bgpConfig.Global = *c
- // update route selection options
- table.SelectionOptions = c.RouteSelectionOptions.Config
- table.UseMultiplePaths = c.UseMultiplePaths.Config
-
- s.roaManager.SetAS(s.bgpConfig.Global.Config.As)
- return nil
- }, false)
-}
-
-func (s *BgpServer) GetVrf() (l []*table.Vrf) {
- s.mgmtOperation(func() error {
- l = make([]*table.Vrf, 0, len(s.globalRib.Vrfs))
- for _, vrf := range s.globalRib.Vrfs {
- l = append(l, vrf.Clone())
- }
- return nil
- }, true)
- return l
-}
-
-func (s *BgpServer) AddVrf(name string, id uint32, rd bgp.RouteDistinguisherInterface, im, ex []bgp.ExtendedCommunityInterface) error {
- return s.mgmtOperation(func() error {
- pi := &table.PeerInfo{
- AS: s.bgpConfig.Global.Config.As,
- LocalID: net.ParseIP(s.bgpConfig.Global.Config.RouterId).To4(),
- }
- if pathList, err := s.globalRib.AddVrf(name, id, rd, im, ex, pi); err != nil {
- return err
- } else if len(pathList) > 0 {
- s.propagateUpdate(nil, pathList)
- }
- return nil
- }, true)
-}
-
-func (s *BgpServer) DeleteVrf(name string) error {
- return s.mgmtOperation(func() error {
- for _, n := range s.neighborMap {
- if n.fsm.pConf.Config.Vrf == name {
- return fmt.Errorf("failed to delete VRF %s: neighbor %s is in use", name, n.ID())
- }
- }
- pathList, err := s.globalRib.DeleteVrf(name)
- if err != nil {
- return err
- }
- if len(pathList) > 0 {
- s.propagateUpdate(nil, pathList)
- }
- return nil
- }, true)
-}
-
-func (s *BgpServer) Stop() error {
- return s.mgmtOperation(func() error {
- for k, _ := range s.neighborMap {
- if err := s.deleteNeighbor(&config.Neighbor{Config: config.NeighborConfig{
- NeighborAddress: k}}, bgp.BGP_ERROR_CEASE, bgp.BGP_ERROR_SUB_PEER_DECONFIGURED); err != nil {
- return err
- }
- }
- for _, l := range s.listeners {
- l.Close()
- }
- s.bgpConfig.Global = config.Global{}
- return nil
- }, true)
-}
-
-func familiesForSoftreset(peer *Peer, family bgp.RouteFamily) []bgp.RouteFamily {
- if family == bgp.RouteFamily(0) {
- configured := peer.configuredRFlist()
- families := make([]bgp.RouteFamily, 0, len(configured))
- for _, f := range configured {
- if f != bgp.RF_RTC_UC {
- families = append(families, f)
- }
- }
- return families
- }
- return []bgp.RouteFamily{family}
-}
-
-func (s *BgpServer) softResetIn(addr string, family bgp.RouteFamily) error {
- peers, err := s.addrToPeers(addr)
- if err != nil {
- return err
- }
- for _, peer := range peers {
- families := familiesForSoftreset(peer, family)
-
- pathList := make([]*table.Path, 0, peer.adjRibIn.Count(families))
- for _, path := range peer.adjRibIn.PathList(families, false) {
- // RFC4271 9.1.2 Phase 2: Route Selection
- //
- // If the AS_PATH attribute of a BGP route contains an AS loop, the BGP
- // route should be excluded from the Phase 2 decision function.
- isLooped := false
- if aspath := path.GetAsPath(); aspath != nil {
- isLooped = hasOwnASLoop(peer.fsm.peerInfo.LocalAS, int(peer.fsm.pConf.AsPathOptions.Config.AllowOwnAs), aspath)
- }
- if path.IsAsLooped() != isLooped {
- // can't modify the existing one. needs to create one
- path = path.Clone(false)
- path.SetAsLooped(isLooped)
- // update accepted counter
- peer.adjRibIn.Update([]*table.Path{path})
- }
- if !path.IsAsLooped() {
- pathList = append(pathList, path)
- }
- }
- s.propagateUpdate(peer, pathList)
- }
- return err
-}
-
-func (s *BgpServer) softResetOut(addr string, family bgp.RouteFamily, deferral bool) error {
- peers, err := s.addrToPeers(addr)
- if err != nil {
- return err
- }
- for _, peer := range peers {
- if peer.fsm.state != bgp.BGP_FSM_ESTABLISHED {
- continue
- }
- families := familiesForSoftreset(peer, family)
-
- if deferral {
- _, y := peer.fsm.rfMap[bgp.RF_RTC_UC]
- if peer.fsm.pConf.GracefulRestart.State.LocalRestarting {
- peer.fsm.pConf.GracefulRestart.State.LocalRestarting = false
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": peer.ID(),
- "Families": families,
- }).Debug("deferral timer expired")
- } else if c := peer.fsm.pConf.GetAfiSafi(bgp.RF_RTC_UC); y && !c.MpGracefulRestart.State.EndOfRibReceived {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": peer.ID(),
- "Families": families,
- }).Debug("route-target deferral timer expired")
- } else {
- continue
- }
- }
-
- pathList, filtered := s.getBestFromLocal(peer, families)
- if len(pathList) > 0 {
- sendFsmOutgoingMsg(peer, pathList, nil, false)
- }
- if !deferral && len(filtered) > 0 {
- withdrawnList := make([]*table.Path, 0, len(filtered))
- for _, p := range filtered {
- withdrawnList = append(withdrawnList, p.Clone(true))
- }
- sendFsmOutgoingMsg(peer, withdrawnList, nil, false)
- }
- }
- return nil
-}
-
-func (s *BgpServer) SoftResetIn(addr string, family bgp.RouteFamily) error {
- return s.mgmtOperation(func() error {
- log.WithFields(log.Fields{
- "Topic": "Operation",
- "Key": addr,
- }).Info("Neighbor soft reset in")
- return s.softResetIn(addr, family)
- }, true)
-}
-
-func (s *BgpServer) SoftResetOut(addr string, family bgp.RouteFamily) error {
- return s.mgmtOperation(func() error {
- log.WithFields(log.Fields{
- "Topic": "Operation",
- "Key": addr,
- }).Info("Neighbor soft reset out")
- return s.softResetOut(addr, family, false)
- }, true)
-}
-
-func (s *BgpServer) SoftReset(addr string, family bgp.RouteFamily) error {
- return s.mgmtOperation(func() error {
- log.WithFields(log.Fields{
- "Topic": "Operation",
- "Key": addr,
- }).Info("Neighbor soft reset")
- err := s.softResetIn(addr, family)
- if err != nil {
- return err
- }
- return s.softResetOut(addr, family, false)
- }, true)
-}
-
-func (s *BgpServer) validateTable(r *table.Table) (v []*table.Validation) {
- if s.roaManager.enabled() {
- v = make([]*table.Validation, 0, len(r.GetDestinations()))
- for _, d := range r.GetDestinations() {
- for _, p := range d.GetAllKnownPathList() {
- v = append(v, s.roaManager.validate(p))
- }
- }
- }
- return
-}
-
-func (s *BgpServer) GetRib(addr string, family bgp.RouteFamily, prefixes []*table.LookupPrefix) (rib *table.Table, v []*table.Validation, err error) {
- err = s.mgmtOperation(func() error {
- m := s.globalRib
- id := table.GLOBAL_RIB_NAME
- as := uint32(0)
- if len(addr) > 0 {
- peer, ok := s.neighborMap[addr]
- if !ok {
- return fmt.Errorf("Neighbor that has %v doesn't exist.", addr)
- }
- if !peer.isRouteServerClient() {
- return fmt.Errorf("Neighbor %v doesn't have local rib", addr)
- }
- id = peer.ID()
- as = peer.AS()
- m = s.rsRib
- }
- af := bgp.RouteFamily(family)
- tbl, ok := m.Tables[af]
- if !ok {
- return fmt.Errorf("address family: %s not supported", af)
- }
- rib, err = tbl.Select(table.TableSelectOption{ID: id, AS: as, LookupPrefixes: prefixes})
- v = s.validateTable(rib)
- return err
- }, true)
- return
-}
-
-func (s *BgpServer) GetVrfRib(name string, family bgp.RouteFamily, prefixes []*table.LookupPrefix) (rib *table.Table, err error) {
- err = s.mgmtOperation(func() error {
- m := s.globalRib
- vrfs := m.Vrfs
- if _, ok := vrfs[name]; !ok {
- return fmt.Errorf("vrf %s not found", name)
- }
- var af bgp.RouteFamily
- switch family {
- case bgp.RF_IPv4_UC:
- af = bgp.RF_IPv4_VPN
- case bgp.RF_IPv6_UC:
- af = bgp.RF_IPv6_VPN
- case bgp.RF_EVPN:
- af = bgp.RF_EVPN
- }
- tbl, ok := m.Tables[af]
- if !ok {
- return fmt.Errorf("address family: %s not supported", af)
- }
- rib, err = tbl.Select(table.TableSelectOption{VRF: vrfs[name], LookupPrefixes: prefixes})
- return err
- }, true)
- return
-}
-
-func (s *BgpServer) GetAdjRib(addr string, family bgp.RouteFamily, in bool, prefixes []*table.LookupPrefix) (rib *table.Table, v []*table.Validation, err error) {
- err = s.mgmtOperation(func() error {
- peer, ok := s.neighborMap[addr]
- if !ok {
- return fmt.Errorf("Neighbor that has %v doesn't exist.", addr)
- }
- id := peer.ID()
- as := peer.AS()
-
- var adjRib *table.AdjRib
- if in {
- adjRib = peer.adjRibIn
- } else {
- adjRib = table.NewAdjRib(peer.configuredRFlist())
- accepted, _ := s.getBestFromLocal(peer, peer.configuredRFlist())
- adjRib.Update(accepted)
- }
- rib, err = adjRib.Select(family, false, table.TableSelectOption{ID: id, AS: as, LookupPrefixes: prefixes})
- v = s.validateTable(rib)
- return err
- }, true)
- return
-}
-
-func (s *BgpServer) GetRibInfo(addr string, family bgp.RouteFamily) (info *table.TableInfo, err error) {
- err = s.mgmtOperation(func() error {
- m := s.globalRib
- id := table.GLOBAL_RIB_NAME
- as := uint32(0)
- if len(addr) > 0 {
- peer, ok := s.neighborMap[addr]
- if !ok {
- return fmt.Errorf("Neighbor that has %v doesn't exist.", addr)
- }
- if !peer.isRouteServerClient() {
- return fmt.Errorf("Neighbor %v doesn't have local rib", addr)
- }
- id = peer.ID()
- as = peer.AS()
- m = s.rsRib
- }
- info, err = m.TableInfo(id, as, family)
- return err
- }, true)
- return
-}
-
-func (s *BgpServer) GetAdjRibInfo(addr string, family bgp.RouteFamily, in bool) (info *table.TableInfo, err error) {
- err = s.mgmtOperation(func() error {
- peer, ok := s.neighborMap[addr]
- if !ok {
- return fmt.Errorf("Neighbor that has %v doesn't exist.", addr)
- }
-
- var adjRib *table.AdjRib
- if in {
- adjRib = peer.adjRibIn
- } else {
- adjRib = table.NewAdjRib(peer.configuredRFlist())
- accepted, _ := s.getBestFromLocal(peer, peer.configuredRFlist())
- adjRib.Update(accepted)
- }
- info, err = adjRib.TableInfo(family)
- return err
- }, true)
- return
-}
-
-func (s *BgpServer) GetServer() (c *config.Global) {
- s.mgmtOperation(func() error {
- g := s.bgpConfig.Global
- c = &g
- return nil
- }, false)
- return c
-}
-
-func (s *BgpServer) GetNeighbor(address string, getAdvertised bool) (l []*config.Neighbor) {
- s.mgmtOperation(func() error {
- l = make([]*config.Neighbor, 0, len(s.neighborMap))
- for k, peer := range s.neighborMap {
- if address != "" && address != k && address != peer.fsm.pConf.Config.NeighborInterface {
- continue
- }
- l = append(l, s.ToConfig(peer, getAdvertised))
- }
- return nil
- }, false)
- return l
-}
-
-func (server *BgpServer) addPeerGroup(c *config.PeerGroup) error {
- name := c.Config.PeerGroupName
- if _, y := server.peerGroupMap[name]; y {
- return fmt.Errorf("Can't overwrite the existing peer-group: %s", name)
- }
-
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Name": name,
- }).Info("Add a peer group configuration")
-
- server.peerGroupMap[c.Config.PeerGroupName] = NewPeerGroup(c)
-
- return nil
-}
-
-func (server *BgpServer) addNeighbor(c *config.Neighbor) error {
- addr, err := c.ExtractNeighborAddress()
- if err != nil {
- return err
- }
-
- if _, y := server.neighborMap[addr]; y {
- return fmt.Errorf("Can't overwrite the existing peer: %s", addr)
- }
-
- var pgConf *config.PeerGroup
- if c.Config.PeerGroup != "" {
- pg, ok := server.peerGroupMap[c.Config.PeerGroup]
- if !ok {
- return fmt.Errorf("no such peer-group: %s", c.Config.PeerGroup)
- }
- pgConf = pg.Conf
- }
-
- if err := config.SetDefaultNeighborConfigValues(c, pgConf, &server.bgpConfig.Global); err != nil {
- return err
- }
-
- if vrf := c.Config.Vrf; vrf != "" {
- if c.RouteServer.Config.RouteServerClient {
- return fmt.Errorf("route server client can't be enslaved to VRF")
- }
- families, _ := config.AfiSafis(c.AfiSafis).ToRfList()
- for _, f := range families {
- if f != bgp.RF_IPv4_UC && f != bgp.RF_IPv6_UC {
- return fmt.Errorf("%s is not supported for VRF enslaved neighbor", f)
- }
- }
- _, y := server.globalRib.Vrfs[vrf]
- if !y {
- return fmt.Errorf("VRF not found: %s", vrf)
- }
- }
-
- if c.RouteServer.Config.RouteServerClient && c.RouteReflector.Config.RouteReflectorClient {
- return fmt.Errorf("can't be both route-server-client and route-reflector-client")
- }
-
- if server.bgpConfig.Global.Config.Port > 0 {
- for _, l := range server.Listeners(addr) {
- if c.Config.AuthPassword != "" {
- if err := SetTcpMD5SigSockopt(l, addr, c.Config.AuthPassword); err != nil {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": addr,
- }).Warnf("failed to set md5: %s", err)
- }
- }
- }
- }
- log.WithFields(log.Fields{
- "Topic": "Peer",
- }).Infof("Add a peer configuration for:%s", addr)
-
- rib := server.globalRib
- if c.RouteServer.Config.RouteServerClient {
- rib = server.rsRib
- }
- peer := NewPeer(&server.bgpConfig.Global, c, rib, server.policy)
- server.policy.Reset(nil, map[string]config.ApplyPolicy{peer.ID(): c.ApplyPolicy})
- server.neighborMap[addr] = peer
- if name := c.Config.PeerGroup; name != "" {
- server.peerGroupMap[name].AddMember(*c)
- }
- peer.startFSMHandler(server.fsmincomingCh, server.fsmStateCh)
- server.broadcastPeerState(peer, bgp.BGP_FSM_IDLE, nil)
- return nil
-}
-
-func (s *BgpServer) AddPeerGroup(c *config.PeerGroup) error {
- return s.mgmtOperation(func() error {
- return s.addPeerGroup(c)
- }, true)
-}
-
-func (s *BgpServer) AddNeighbor(c *config.Neighbor) error {
- return s.mgmtOperation(func() error {
- return s.addNeighbor(c)
- }, 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
-
- if _, y := server.peerGroupMap[name]; !y {
- return fmt.Errorf("Can't delete a peer-group %s which does not exist", name)
- }
-
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Name": name,
- }).Info("Delete a peer group configuration")
-
- delete(server.peerGroupMap, name)
- return nil
-}
-
-func (server *BgpServer) deleteNeighbor(c *config.Neighbor, code, subcode uint8) error {
- if c.Config.PeerGroup != "" {
- _, y := server.peerGroupMap[c.Config.PeerGroup]
- if y {
- server.peerGroupMap[c.Config.PeerGroup].DeleteMember(*c)
- }
- }
-
- addr, err := c.ExtractNeighborAddress()
- if err != nil {
- return err
- }
-
- if intf := c.Config.NeighborInterface; intf != "" {
- var err error
- addr, err = config.GetIPv6LinkLocalNeighborAddress(intf)
- if err != nil {
- return err
- }
- }
- n, y := server.neighborMap[addr]
- if !y {
- return fmt.Errorf("Can't delete a peer configuration for %s", addr)
- }
- for _, l := range server.Listeners(addr) {
- if err := SetTcpMD5SigSockopt(l, addr, ""); err != nil {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": addr,
- }).Warnf("failed to unset md5: %s", err)
- }
- }
- log.WithFields(log.Fields{
- "Topic": "Peer",
- }).Infof("Delete a peer configuration for:%s", addr)
-
- n.fsm.sendNotification(code, subcode, nil, "")
- n.stopPeerRestarting()
-
- go n.stopFSM()
- delete(server.neighborMap, addr)
- server.dropPeerAllRoutes(n, n.configuredRFlist())
- return nil
-}
-
-func (s *BgpServer) DeletePeerGroup(c *config.PeerGroup) error {
- return s.mgmtOperation(func() error {
- name := c.Config.PeerGroupName
- for _, n := range s.neighborMap {
- if n.fsm.pConf.Config.PeerGroup == name {
- return fmt.Errorf("failed to delete peer-group %s: neighbor %s is in use", name, n.ID())
- }
- }
- return s.deletePeerGroup(c)
- }, true)
-}
-
-func (s *BgpServer) DeleteNeighbor(c *config.Neighbor) error {
- return s.mgmtOperation(func() error {
- return s.deleteNeighbor(c, bgp.BGP_ERROR_CEASE, bgp.BGP_ERROR_SUB_PEER_DECONFIGURED)
- }, true)
-}
-
-func (s *BgpServer) updatePeerGroup(pg *config.PeerGroup) (needsSoftResetIn bool, err error) {
- name := pg.Config.PeerGroupName
-
- _, ok := s.peerGroupMap[name]
- if !ok {
- return false, fmt.Errorf("Peer-group %s doesn't exist.", name)
- }
- s.peerGroupMap[name].Conf = pg
-
- for _, n := range s.peerGroupMap[name].members {
- c := n
- u, err := s.updateNeighbor(&c)
- if err != nil {
- return needsSoftResetIn, err
- }
- needsSoftResetIn = needsSoftResetIn || u
- }
- return needsSoftResetIn, nil
-}
-
-func (s *BgpServer) UpdatePeerGroup(pg *config.PeerGroup) (needsSoftResetIn bool, err error) {
- err = s.mgmtOperation(func() error {
- needsSoftResetIn, err = s.updatePeerGroup(pg)
- return err
- }, true)
- return needsSoftResetIn, err
-}
-
-func (s *BgpServer) updateNeighbor(c *config.Neighbor) (needsSoftResetIn bool, err error) {
- if c.Config.PeerGroup != "" {
- if pg, ok := s.peerGroupMap[c.Config.PeerGroup]; ok {
- if err := config.SetDefaultNeighborConfigValues(c, pg.Conf, &s.bgpConfig.Global); err != nil {
- return needsSoftResetIn, err
- }
- } else {
- return needsSoftResetIn, fmt.Errorf("no such peer-group: %s", c.Config.PeerGroup)
- }
- }
-
- addr, err := c.ExtractNeighborAddress()
- if err != nil {
- return needsSoftResetIn, err
- }
-
- peer, ok := s.neighborMap[addr]
- if !ok {
- return needsSoftResetIn, fmt.Errorf("Neighbor that has %v doesn't exist.", addr)
- }
-
- if !peer.fsm.pConf.ApplyPolicy.Equal(&c.ApplyPolicy) {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": addr,
- }).Info("Update ApplyPolicy")
- s.policy.Reset(nil, map[string]config.ApplyPolicy{peer.ID(): c.ApplyPolicy})
- peer.fsm.pConf.ApplyPolicy = c.ApplyPolicy
- needsSoftResetIn = true
- }
- original := peer.fsm.pConf
-
- if !original.AsPathOptions.Config.Equal(&c.AsPathOptions.Config) {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": peer.ID(),
- }).Info("Update aspath options")
- peer.fsm.pConf.AsPathOptions = c.AsPathOptions
- needsSoftResetIn = true
- }
-
- if original.NeedsResendOpenMessage(c) {
- sub := uint8(bgp.BGP_ERROR_SUB_OTHER_CONFIGURATION_CHANGE)
- if original.Config.AdminDown != c.Config.AdminDown {
- sub = bgp.BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN
- state := "Admin Down"
-
- if !c.Config.AdminDown {
- state = "Admin Up"
- }
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": peer.ID(),
- "State": state,
- }).Info("Update admin-state configuration")
- } else if original.Config.PeerAs != c.Config.PeerAs {
- sub = bgp.BGP_ERROR_SUB_PEER_DECONFIGURED
- }
- if err = s.deleteNeighbor(peer.fsm.pConf, bgp.BGP_ERROR_CEASE, sub); err != nil {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": addr,
- }).Error(err)
- return needsSoftResetIn, err
- }
- err = s.addNeighbor(c)
- if err != nil {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": addr,
- }).Error(err)
- }
- return needsSoftResetIn, err
- }
-
- if !original.Timers.Config.Equal(&c.Timers.Config) {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": peer.ID(),
- }).Info("Update timer configuration")
- peer.fsm.pConf.Timers.Config = c.Timers.Config
- }
-
- err = peer.updatePrefixLimitConfig(c.AfiSafis)
- if err != nil {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": addr,
- }).Error(err)
- // rollback to original state
- peer.fsm.pConf = original
- }
- return needsSoftResetIn, err
-}
-
-func (s *BgpServer) UpdateNeighbor(c *config.Neighbor) (needsSoftResetIn bool, err error) {
- err = s.mgmtOperation(func() error {
- needsSoftResetIn, err = s.updateNeighbor(c)
- return err
- }, true)
- return needsSoftResetIn, err
-}
-
-func (s *BgpServer) addrToPeers(addr string) (l []*Peer, err error) {
- if len(addr) == 0 {
- for _, p := range s.neighborMap {
- l = append(l, p)
- }
- return l, nil
- }
- peer, found := s.neighborMap[addr]
- if !found {
- return l, fmt.Errorf("Neighbor that has %v doesn't exist.", addr)
- }
- return []*Peer{peer}, nil
-}
-
-func (s *BgpServer) resetNeighbor(op, addr string, subcode uint8, data []byte) error {
- log.WithFields(log.Fields{
- "Topic": "Operation",
- "Key": addr,
- }).Info(op)
-
- peers, err := s.addrToPeers(addr)
- if err == nil {
- m := bgp.NewBGPNotificationMessage(bgp.BGP_ERROR_CEASE, subcode, data)
- for _, peer := range peers {
- sendFsmOutgoingMsg(peer, nil, m, false)
- }
- }
- return err
-}
-
-func (s *BgpServer) ShutdownNeighbor(addr, communication string) error {
- return s.mgmtOperation(func() error {
- return s.resetNeighbor("Neighbor shutdown", addr, bgp.BGP_ERROR_SUB_ADMINISTRATIVE_SHUTDOWN, newAdministrativeCommunication(communication))
- }, true)
-}
-
-func (s *BgpServer) ResetNeighbor(addr, communication string) error {
- return s.mgmtOperation(func() error {
- err := s.resetNeighbor("Neighbor reset", addr, bgp.BGP_ERROR_SUB_ADMINISTRATIVE_RESET, newAdministrativeCommunication(communication))
- if err != nil {
- return err
- }
- peers, _ := s.addrToPeers(addr)
- for _, peer := range peers {
- peer.fsm.idleHoldTime = peer.fsm.pConf.Timers.Config.IdleHoldTimeAfterReset
- }
- return nil
- }, true)
-}
-
-func (s *BgpServer) setAdminState(addr, communication string, enable bool) error {
- peers, err := s.addrToPeers(addr)
- if err != nil {
- return err
- }
- for _, peer := range peers {
- f := func(stateOp *AdminStateOperation, message string) {
- select {
- case peer.fsm.adminStateCh <- *stateOp:
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": peer.fsm.pConf.State.NeighborAddress,
- }).Debug(message)
- default:
- log.Warning("previous request is still remaining. : ", peer.fsm.pConf.State.NeighborAddress)
- }
- }
- if enable {
- f(&AdminStateOperation{ADMIN_STATE_UP, nil}, "ADMIN_STATE_UP requested")
- } else {
- f(&AdminStateOperation{ADMIN_STATE_DOWN, newAdministrativeCommunication(communication)}, "ADMIN_STATE_DOWN requested")
- }
- }
- return nil
-}
-
-func (s *BgpServer) EnableNeighbor(addr string) error {
- return s.mgmtOperation(func() error {
- return s.setAdminState(addr, "", true)
- }, true)
-}
-
-func (s *BgpServer) DisableNeighbor(addr, communication string) error {
- return s.mgmtOperation(func() error {
- return s.setAdminState(addr, communication, false)
- }, true)
-}
-
-func (s *BgpServer) GetDefinedSet(typ table.DefinedType, name string) (sets *config.DefinedSets, err error) {
- err = s.mgmtOperation(func() error {
- sets, err = s.policy.GetDefinedSet(typ, name)
- return nil
- }, false)
- return sets, err
-}
-
-func (s *BgpServer) AddDefinedSet(a table.DefinedSet) error {
- return s.mgmtOperation(func() error {
- return s.policy.AddDefinedSet(a)
- }, false)
-}
-
-func (s *BgpServer) DeleteDefinedSet(a table.DefinedSet, all bool) error {
- return s.mgmtOperation(func() error {
- return s.policy.DeleteDefinedSet(a, all)
- }, false)
-}
-
-func (s *BgpServer) ReplaceDefinedSet(a table.DefinedSet) error {
- return s.mgmtOperation(func() error {
- return s.policy.ReplaceDefinedSet(a)
- }, false)
-}
-
-func (s *BgpServer) GetStatement() (l []*config.Statement) {
- s.mgmtOperation(func() error {
- l = s.policy.GetStatement()
- return nil
- }, false)
- return l
-}
-
-func (s *BgpServer) AddStatement(st *table.Statement) error {
- return s.mgmtOperation(func() error {
- return s.policy.AddStatement(st)
- }, false)
-}
-
-func (s *BgpServer) DeleteStatement(st *table.Statement, all bool) error {
- return s.mgmtOperation(func() error {
- return s.policy.DeleteStatement(st, all)
- }, false)
-}
-
-func (s *BgpServer) ReplaceStatement(st *table.Statement) error {
- return s.mgmtOperation(func() error {
- return s.policy.ReplaceStatement(st)
- }, false)
-}
-
-func (s *BgpServer) GetPolicy() (l []*config.PolicyDefinition) {
- s.mgmtOperation(func() error {
- l = s.policy.GetAllPolicy()
- return nil
- }, false)
- return l
-}
-
-func (s *BgpServer) AddPolicy(x *table.Policy, refer bool) error {
- return s.mgmtOperation(func() error {
- return s.policy.AddPolicy(x, refer)
- }, false)
-}
-
-func (s *BgpServer) DeletePolicy(x *table.Policy, all, preserve bool) error {
- return s.mgmtOperation(func() error {
- l := make([]string, 0, len(s.neighborMap)+1)
- for _, peer := range s.neighborMap {
- l = append(l, peer.ID())
- }
- l = append(l, table.GLOBAL_RIB_NAME)
-
- return s.policy.DeletePolicy(x, all, preserve, l)
- }, false)
-}
-
-func (s *BgpServer) ReplacePolicy(x *table.Policy, refer, preserve bool) error {
- return s.mgmtOperation(func() error {
- return s.policy.ReplacePolicy(x, refer, preserve)
- }, false)
-}
-
-func (server *BgpServer) toPolicyInfo(name string, dir table.PolicyDirection) (string, error) {
- if name == "" {
- switch dir {
- case table.POLICY_DIRECTION_IMPORT, table.POLICY_DIRECTION_EXPORT:
- return table.GLOBAL_RIB_NAME, nil
- }
- return "", fmt.Errorf("invalid policy type")
- } else {
- peer, ok := server.neighborMap[name]
- if !ok {
- return "", fmt.Errorf("not found peer %s", name)
- }
- if !peer.isRouteServerClient() {
- return "", fmt.Errorf("non-rs-client peer %s doesn't have per peer policy", name)
- }
- return peer.ID(), nil
- }
-}
-
-func (s *BgpServer) GetPolicyAssignment(name string, dir table.PolicyDirection) (rt table.RouteType, l []*config.PolicyDefinition, err error) {
- err = s.mgmtOperation(func() error {
- var id string
- id, err = s.toPolicyInfo(name, dir)
- if err != nil {
- rt = table.ROUTE_TYPE_NONE
- return err
- }
- rt, l, err = s.policy.GetPolicyAssignment(id, dir)
- return nil
- }, false)
- return rt, l, err
-}
-
-func (s *BgpServer) AddPolicyAssignment(name string, dir table.PolicyDirection, policies []*config.PolicyDefinition, def table.RouteType) error {
- return s.mgmtOperation(func() error {
- id, err := s.toPolicyInfo(name, dir)
- if err != nil {
- return err
- }
- return s.policy.AddPolicyAssignment(id, dir, policies, def)
- }, false)
-}
-
-func (s *BgpServer) DeletePolicyAssignment(name string, dir table.PolicyDirection, policies []*config.PolicyDefinition, all bool) error {
- return s.mgmtOperation(func() error {
- id, err := s.toPolicyInfo(name, dir)
- if err != nil {
- return err
- }
- return s.policy.DeletePolicyAssignment(id, dir, policies, all)
- }, false)
-}
-
-func (s *BgpServer) ReplacePolicyAssignment(name string, dir table.PolicyDirection, policies []*config.PolicyDefinition, def table.RouteType) error {
- return s.mgmtOperation(func() error {
- id, err := s.toPolicyInfo(name, dir)
- if err != nil {
- return err
- }
- return s.policy.ReplacePolicyAssignment(id, dir, policies, def)
- }, false)
-}
-
-func (s *BgpServer) EnableMrt(c *config.MrtConfig) error {
- return s.mgmtOperation(func() error {
- return s.mrtManager.enable(c)
- }, false)
-}
-
-func (s *BgpServer) DisableMrt(c *config.MrtConfig) error {
- return s.mgmtOperation(func() error {
- return s.mrtManager.disable(c)
- }, false)
-}
-
-func (s *BgpServer) GetRpki() (l []*config.RpkiServer, err error) {
- err = s.mgmtOperation(func() error {
- l = s.roaManager.GetServers()
- return nil
- }, false)
- return l, err
-}
-
-func (s *BgpServer) GetRoa(family bgp.RouteFamily) (l []*table.ROA, err error) {
- s.mgmtOperation(func() error {
- l, err = s.roaManager.GetRoa(family)
- return nil
- }, false)
- return l, err
-}
-
-func (s *BgpServer) AddRpki(c *config.RpkiServerConfig) error {
- return s.mgmtOperation(func() error {
- return s.roaManager.AddServer(net.JoinHostPort(c.Address, strconv.Itoa(int(c.Port))), c.RecordLifetime)
- }, false)
-}
-
-func (s *BgpServer) DeleteRpki(c *config.RpkiServerConfig) error {
- return s.mgmtOperation(func() error {
- return s.roaManager.DeleteServer(c.Address)
- }, false)
-}
-
-func (s *BgpServer) EnableRpki(c *config.RpkiServerConfig) error {
- return s.mgmtOperation(func() error {
- return s.roaManager.Enable(c.Address)
- }, false)
-}
-
-func (s *BgpServer) DisableRpki(c *config.RpkiServerConfig) error {
- return s.mgmtOperation(func() error {
- return s.roaManager.Disable(c.Address)
- }, false)
-}
-
-func (s *BgpServer) ResetRpki(c *config.RpkiServerConfig) error {
- return s.mgmtOperation(func() error {
- return s.roaManager.Reset(c.Address)
- }, false)
-}
-
-func (s *BgpServer) SoftResetRpki(c *config.RpkiServerConfig) error {
- return s.mgmtOperation(func() error {
- return s.roaManager.SoftReset(c.Address)
- }, false)
-}
-
-type WatchEventType string
-
-const (
- WATCH_EVENT_TYPE_BEST_PATH WatchEventType = "bestpath"
- WATCH_EVENT_TYPE_PRE_UPDATE WatchEventType = "preupdate"
- WATCH_EVENT_TYPE_POST_UPDATE WatchEventType = "postupdate"
- WATCH_EVENT_TYPE_PEER_STATE WatchEventType = "peerstate"
- WATCH_EVENT_TYPE_TABLE WatchEventType = "table"
- WATCH_EVENT_TYPE_RECV_MSG WatchEventType = "receivedmessage"
-)
-
-type WatchEvent interface {
-}
-
-type WatchEventUpdate struct {
- Message *bgp.BGPMessage
- PeerAS uint32
- LocalAS uint32
- PeerAddress net.IP
- LocalAddress net.IP
- PeerID net.IP
- FourBytesAs bool
- Timestamp time.Time
- Payload []byte
- PostPolicy bool
- Init bool
- PathList []*table.Path
- Neighbor *config.Neighbor
-}
-
-type WatchEventPeerState struct {
- PeerAS uint32
- LocalAS uint32
- PeerAddress net.IP
- LocalAddress net.IP
- PeerPort uint16
- LocalPort uint16
- PeerID net.IP
- SentOpen *bgp.BGPMessage
- RecvOpen *bgp.BGPMessage
- State bgp.FSMState
- StateReason *FsmStateReason
- AdminState AdminState
- Timestamp time.Time
- PeerInterface string
-}
-
-type WatchEventAdjIn struct {
- PathList []*table.Path
-}
-
-type WatchEventTable struct {
- RouterId string
- PathList map[string][]*table.Path
- Neighbor []*config.Neighbor
-}
-
-type WatchEventBestPath struct {
- PathList []*table.Path
- MultiPathList [][]*table.Path
- Vrf map[string]uint16
-}
-
-type WatchEventMessage struct {
- Message *bgp.BGPMessage
- PeerAS uint32
- LocalAS uint32
- PeerAddress net.IP
- LocalAddress net.IP
- PeerID net.IP
- FourBytesAs bool
- Timestamp time.Time
- IsSent bool
-}
-
-type watchOptions struct {
- bestpath bool
- preUpdate bool
- postUpdate bool
- peerState bool
- initBest bool
- initUpdate bool
- initPostUpdate bool
- initPeerState bool
- tableName string
- recvMessage bool
-}
-
-type WatchOption func(*watchOptions)
-
-func WatchBestPath(current bool) WatchOption {
- return func(o *watchOptions) {
- o.bestpath = true
- if current {
- o.initBest = true
- }
- }
-}
-
-func WatchUpdate(current bool) WatchOption {
- return func(o *watchOptions) {
- o.preUpdate = true
- if current {
- o.initUpdate = true
- }
- }
-}
-
-func WatchPostUpdate(current bool) WatchOption {
- return func(o *watchOptions) {
- o.postUpdate = true
- if current {
- o.initPostUpdate = true
- }
- }
-}
-
-func WatchPeerState(current bool) WatchOption {
- return func(o *watchOptions) {
- o.peerState = true
- if current {
- o.initPeerState = true
- }
- }
-}
-
-func WatchTableName(name string) WatchOption {
- return func(o *watchOptions) {
- o.tableName = name
- }
-}
-
-func WatchMessage(isSent bool) WatchOption {
- return func(o *watchOptions) {
- if isSent {
- log.WithFields(log.Fields{
- "Topic": "Server",
- }).Warn("watch event for sent messages is not implemented yet")
- // o.sentMessage = true
- } else {
- o.recvMessage = true
- }
- }
-}
-
-type Watcher struct {
- opts watchOptions
- realCh chan WatchEvent
- ch *channels.InfiniteChannel
- s *BgpServer
-}
-
-func (w *Watcher) Event() <-chan WatchEvent {
- return w.realCh
-}
-
-func (w *Watcher) Generate(t WatchEventType) error {
- return w.s.mgmtOperation(func() error {
- switch t {
- case WATCH_EVENT_TYPE_PRE_UPDATE:
- pathList := make([]*table.Path, 0)
- for _, peer := range w.s.neighborMap {
- pathList = append(pathList, peer.adjRibIn.PathList(peer.configuredRFlist(), false)...)
- }
- w.notify(&WatchEventAdjIn{PathList: clonePathList(pathList)})
- case WATCH_EVENT_TYPE_TABLE:
- rib := w.s.globalRib
- as := uint32(0)
- id := table.GLOBAL_RIB_NAME
- if len(w.opts.tableName) > 0 {
- peer, ok := w.s.neighborMap[w.opts.tableName]
- if !ok {
- return fmt.Errorf("Neighbor that has %v doesn't exist.", w.opts.tableName)
- }
- if !peer.isRouteServerClient() {
- return fmt.Errorf("Neighbor %v doesn't have local rib", w.opts.tableName)
- }
- id = peer.ID()
- as = peer.AS()
- rib = w.s.rsRib
- }
-
- pathList := func() map[string][]*table.Path {
- pathList := make(map[string][]*table.Path)
- for _, t := range rib.Tables {
- for _, dst := range t.GetDestinations() {
- if paths := dst.GetKnownPathList(id, as); len(paths) > 0 {
- pathList[dst.GetNlri().String()] = clonePathList(paths)
- }
- }
- }
- return pathList
- }()
- l := make([]*config.Neighbor, 0, len(w.s.neighborMap))
- for _, peer := range w.s.neighborMap {
- l = append(l, w.s.ToConfig(peer, false))
- }
- w.notify(&WatchEventTable{PathList: pathList, Neighbor: l})
- default:
- return fmt.Errorf("unsupported type %v", t)
- }
- return nil
- }, false)
-}
-
-func (w *Watcher) notify(v WatchEvent) {
- w.ch.In() <- v
-}
-
-func (w *Watcher) loop() {
- for ev := range w.ch.Out() {
- w.realCh <- ev.(WatchEvent)
- }
- close(w.realCh)
-}
-
-func (w *Watcher) Stop() {
- w.s.mgmtOperation(func() error {
- for k, l := range w.s.watcherMap {
- for i, v := range l {
- if w == v {
- w.s.watcherMap[k] = append(l[:i], l[i+1:]...)
- break
- }
- }
- }
-
- cleanInfiniteChannel(w.ch)
- // the loop function goroutine might be blocked for
- // writing to realCh. make sure it finishes.
- for range w.realCh {
- }
- return nil
- }, false)
-}
-
-func (s *BgpServer) isWatched(typ WatchEventType) bool {
- return len(s.watcherMap[typ]) != 0
-}
-
-func (s *BgpServer) notifyWatcher(typ WatchEventType, ev WatchEvent) {
- for _, w := range s.watcherMap[typ] {
- w.notify(ev)
- }
-}
-
-func (s *BgpServer) Watch(opts ...WatchOption) (w *Watcher) {
- s.mgmtOperation(func() error {
- w = &Watcher{
- s: s,
- realCh: make(chan WatchEvent, 8),
- ch: channels.NewInfiniteChannel(),
- }
-
- for _, opt := range opts {
- opt(&w.opts)
- }
-
- register := func(t WatchEventType, w *Watcher) {
- s.watcherMap[t] = append(s.watcherMap[t], w)
- }
-
- if w.opts.bestpath {
- register(WATCH_EVENT_TYPE_BEST_PATH, w)
- }
- if w.opts.preUpdate {
- register(WATCH_EVENT_TYPE_PRE_UPDATE, w)
- }
- if w.opts.postUpdate {
- register(WATCH_EVENT_TYPE_POST_UPDATE, w)
- }
- if w.opts.peerState {
- register(WATCH_EVENT_TYPE_PEER_STATE, w)
- }
- if w.opts.initPeerState {
- for _, peer := range s.neighborMap {
- if peer.fsm.state != bgp.BGP_FSM_ESTABLISHED {
- continue
- }
- w.notify(newWatchEventPeerState(peer, nil))
- }
- }
- if w.opts.initBest && s.active() == nil {
- w.notify(&WatchEventBestPath{
- PathList: s.globalRib.GetBestPathList(table.GLOBAL_RIB_NAME, 0, nil),
- MultiPathList: s.globalRib.GetBestMultiPathList(table.GLOBAL_RIB_NAME, nil),
- })
- }
- if w.opts.initUpdate {
- for _, peer := range s.neighborMap {
- if peer.fsm.state != bgp.BGP_FSM_ESTABLISHED {
- continue
- }
- configNeighbor := w.s.ToConfig(peer, false)
- for _, rf := range peer.configuredRFlist() {
- _, y := peer.fsm.capMap[bgp.BGP_CAP_FOUR_OCTET_AS_NUMBER]
- l, _ := peer.fsm.LocalHostPort()
- w.notify(&WatchEventUpdate{
- PeerAS: peer.fsm.peerInfo.AS,
- LocalAS: peer.fsm.peerInfo.LocalAS,
- PeerAddress: peer.fsm.peerInfo.Address,
- LocalAddress: net.ParseIP(l),
- PeerID: peer.fsm.peerInfo.ID,
- FourBytesAs: y,
- Init: true,
- PostPolicy: false,
- Neighbor: configNeighbor,
- PathList: peer.adjRibIn.PathList([]bgp.RouteFamily{rf}, false),
- })
-
- eor := bgp.NewEndOfRib(rf)
- eorBuf, _ := eor.Serialize()
- w.notify(&WatchEventUpdate{
- Message: eor,
- PeerAS: peer.fsm.peerInfo.AS,
- LocalAS: peer.fsm.peerInfo.LocalAS,
- PeerAddress: peer.fsm.peerInfo.Address,
- LocalAddress: net.ParseIP(l),
- PeerID: peer.fsm.peerInfo.ID,
- FourBytesAs: y,
- Timestamp: time.Now(),
- Init: true,
- Payload: eorBuf,
- PostPolicy: false,
- Neighbor: configNeighbor,
- })
- }
- }
- }
- if w.opts.initPostUpdate && s.active() == nil {
- for _, rf := range s.globalRib.GetRFlist() {
- if len(s.globalRib.Tables[rf].GetDestinations()) == 0 {
- continue
- }
- pathsByPeer := make(map[*table.PeerInfo][]*table.Path)
- for _, path := range s.globalRib.GetPathList(table.GLOBAL_RIB_NAME, 0, []bgp.RouteFamily{rf}) {
- pathsByPeer[path.GetSource()] = append(pathsByPeer[path.GetSource()], path)
- }
- for peerInfo, paths := range pathsByPeer {
- // create copy which can be access to without mutex
- var configNeighbor *config.Neighbor
- if peer, ok := s.neighborMap[peerInfo.Address.String()]; ok {
- configNeighbor = w.s.ToConfig(peer, false)
- }
-
- w.notify(&WatchEventUpdate{
- PeerAS: peerInfo.AS,
- PeerAddress: peerInfo.Address,
- PeerID: peerInfo.ID,
- PostPolicy: true,
- Neighbor: configNeighbor,
- PathList: paths,
- Init: true,
- })
-
- eor := bgp.NewEndOfRib(rf)
- eorBuf, _ := eor.Serialize()
- w.notify(&WatchEventUpdate{
- Message: eor,
- PeerAS: peerInfo.AS,
- PeerAddress: peerInfo.Address,
- PeerID: peerInfo.ID,
- Timestamp: time.Now(),
- Payload: eorBuf,
- PostPolicy: true,
- Neighbor: configNeighbor,
- Init: true,
- })
- }
- }
- }
- if w.opts.recvMessage {
- register(WATCH_EVENT_TYPE_RECV_MSG, w)
- }
-
- go w.loop()
- return nil
- }, false)
- return w
-}
diff --git a/server/server_test.go b/server/server_test.go
deleted file mode 100644
index 4a6495e4..00000000
--- a/server/server_test.go
+++ /dev/null
@@ -1,707 +0,0 @@
-// Copyright (C) 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 (
- "context"
- "net"
- "runtime"
- "testing"
- "time"
-
- "github.com/stretchr/testify/require"
-
- log "github.com/sirupsen/logrus"
- "github.com/stretchr/testify/assert"
-
- "github.com/osrg/gobgp/config"
- "github.com/osrg/gobgp/packet/bgp"
- "github.com/osrg/gobgp/table"
-)
-
-func TestModPolicyAssign(t *testing.T) {
- assert := assert.New(t)
- s := NewBgpServer()
- go s.Serve()
- err := s.Start(&config.Global{
- Config: config.GlobalConfig{
- As: 1,
- RouterId: "1.1.1.1",
- Port: -1,
- },
- })
- assert.Nil(err)
- defer s.Stop()
-
- err = s.AddPolicy(&table.Policy{Name: "p1"}, false)
- assert.Nil(err)
-
- err = s.AddPolicy(&table.Policy{Name: "p2"}, false)
- assert.Nil(err)
-
- err = s.AddPolicy(&table.Policy{Name: "p3"}, false)
- assert.Nil(err)
-
- err = s.AddPolicyAssignment("", table.POLICY_DIRECTION_IMPORT,
- []*config.PolicyDefinition{&config.PolicyDefinition{Name: "p1"}, &config.PolicyDefinition{Name: "p2"}, &config.PolicyDefinition{Name: "p3"}}, table.ROUTE_TYPE_ACCEPT)
- assert.Nil(err)
-
- err = s.DeletePolicyAssignment("", table.POLICY_DIRECTION_IMPORT,
- []*config.PolicyDefinition{&config.PolicyDefinition{Name: "p1"}}, false)
- assert.Nil(err)
-
- _, ps, _ := s.GetPolicyAssignment("", table.POLICY_DIRECTION_IMPORT)
- assert.Equal(len(ps), 2)
-}
-
-func TestMonitor(test *testing.T) {
- assert := assert.New(test)
- s := NewBgpServer()
- go s.Serve()
- err := s.Start(&config.Global{
- Config: config.GlobalConfig{
- As: 1,
- RouterId: "1.1.1.1",
- Port: 10179,
- },
- })
- assert.Nil(err)
- defer s.Stop()
-
- n := &config.Neighbor{
- Config: config.NeighborConfig{
- NeighborAddress: "127.0.0.1",
- PeerAs: 2,
- },
- Transport: config.Transport{
- Config: config.TransportConfig{
- PassiveMode: true,
- },
- },
- }
- err = s.AddNeighbor(n)
- assert.Nil(err)
-
- t := NewBgpServer()
- go t.Serve()
- err = t.Start(&config.Global{
- Config: config.GlobalConfig{
- As: 2,
- RouterId: "2.2.2.2",
- Port: -1,
- },
- })
- assert.Nil(err)
- defer t.Stop()
-
- m := &config.Neighbor{
- Config: config.NeighborConfig{
- NeighborAddress: "127.0.0.1",
- PeerAs: 1,
- },
- Transport: config.Transport{
- Config: config.TransportConfig{
- RemotePort: 10179,
- },
- },
- }
- err = t.AddNeighbor(m)
- assert.Nil(err)
-
- for {
- time.Sleep(time.Second)
- if t.GetNeighbor("", false)[0].State.SessionState == config.SESSION_STATE_ESTABLISHED {
- break
- }
- }
-
- // Test WatchBestPath.
- w := s.Watch(WatchBestPath(false))
-
- // Advertises a route.
- attrs := []bgp.PathAttributeInterface{
- bgp.NewPathAttributeOrigin(0),
- bgp.NewPathAttributeNextHop("10.0.0.1"),
- }
- if _, err := t.AddPath("", []*table.Path{table.NewPath(nil, bgp.NewIPAddrPrefix(24, "10.0.0.0"), false, attrs, time.Now(), false)}); err != nil {
- log.Fatal(err)
- }
- ev := <-w.Event()
- b := ev.(*WatchEventBestPath)
- assert.Equal(1, len(b.PathList))
- assert.Equal("10.0.0.0/24", b.PathList[0].GetNlri().String())
- assert.False(b.PathList[0].IsWithdraw)
-
- // Withdraws the previous route.
- // NOTE: Withdow should not require any path attribute.
- if _, err := t.AddPath("", []*table.Path{table.NewPath(nil, bgp.NewIPAddrPrefix(24, "10.0.0.0"), true, nil, time.Now(), false)}); err != nil {
- log.Fatal(err)
- }
- ev = <-w.Event()
- b = ev.(*WatchEventBestPath)
- assert.Equal(1, len(b.PathList))
- assert.Equal("10.0.0.0/24", b.PathList[0].GetNlri().String())
- assert.True(b.PathList[0].IsWithdraw)
-
- // Stops the watcher still having an item.
- w.Stop()
-
- // Prepares an initial route to test WatchUpdate with "current" flag.
- if _, err := t.AddPath("", []*table.Path{table.NewPath(nil, bgp.NewIPAddrPrefix(24, "10.1.0.0"), false, attrs, time.Now(), false)}); err != nil {
- log.Fatal(err)
- }
- for {
- // Waits for the initial route will be advertised.
- rib, _, err := s.GetRib("", bgp.RF_IPv4_UC, nil)
- if err != nil {
- log.Fatal(err)
- }
- if len(rib.GetKnownPathList("", 0)) > 0 {
- break
- }
- time.Sleep(100 * time.Millisecond)
- }
-
- // Test WatchUpdate with "current" flag.
- w = s.Watch(WatchUpdate(true))
-
- // Test the initial route.
- ev = <-w.Event()
- u := ev.(*WatchEventUpdate)
- assert.Equal(1, len(u.PathList))
- assert.Equal("10.1.0.0/24", u.PathList[0].GetNlri().String())
- assert.False(u.PathList[0].IsWithdraw)
- ev = <-w.Event()
- u = ev.(*WatchEventUpdate)
- assert.Equal(len(u.PathList), 0) // End of RIB
-
- // Advertises an additional route.
- if _, err := t.AddPath("", []*table.Path{table.NewPath(nil, bgp.NewIPAddrPrefix(24, "10.2.0.0"), false, attrs, time.Now(), false)}); err != nil {
- log.Fatal(err)
- }
- ev = <-w.Event()
- u = ev.(*WatchEventUpdate)
- assert.Equal(1, len(u.PathList))
- assert.Equal("10.2.0.0/24", u.PathList[0].GetNlri().String())
- assert.False(u.PathList[0].IsWithdraw)
-
- // Withdraws the previous route.
- // NOTE: Withdow should not require any path attribute.
- if _, err := t.AddPath("", []*table.Path{table.NewPath(nil, bgp.NewIPAddrPrefix(24, "10.2.0.0"), true, nil, time.Now(), false)}); err != nil {
- log.Fatal(err)
- }
- ev = <-w.Event()
- u = ev.(*WatchEventUpdate)
- assert.Equal(1, len(u.PathList))
- assert.Equal("10.2.0.0/24", u.PathList[0].GetNlri().String())
- assert.True(u.PathList[0].IsWithdraw)
-
- // Stops the watcher still having an item.
- w.Stop()
-}
-
-func TestNumGoroutineWithAddDeleteNeighbor(t *testing.T) {
- assert := assert.New(t)
- s := NewBgpServer()
- go s.Serve()
- err := s.Start(&config.Global{
- Config: config.GlobalConfig{
- As: 1,
- RouterId: "1.1.1.1",
- Port: -1,
- },
- })
- assert.Nil(err)
- defer s.Stop()
-
- // wait a few seconds to avoid taking effect from other test cases.
- time.Sleep(time.Second * 5)
-
- num := runtime.NumGoroutine()
-
- n := &config.Neighbor{
- Config: config.NeighborConfig{
- NeighborAddress: "127.0.0.1",
- PeerAs: 2,
- },
- Transport: config.Transport{
- Config: config.TransportConfig{
- PassiveMode: true,
- },
- },
- }
- err = s.AddNeighbor(n)
- assert.Nil(err)
-
- err = s.DeleteNeighbor(n)
- assert.Nil(err)
- // wait goroutines to finish (e.g. internal goroutine for
- // InfiniteChannel)
- time.Sleep(time.Second * 5)
- assert.Equal(num, runtime.NumGoroutine())
-}
-
-func newPeerandInfo(myAs, as uint32, address string, rib *table.TableManager) (*Peer, *table.PeerInfo) {
- nConf := &config.Neighbor{Config: config.NeighborConfig{PeerAs: as, NeighborAddress: address}}
- gConf := &config.Global{Config: config.GlobalConfig{As: myAs}}
- config.SetDefaultNeighborConfigValues(nConf, nil, gConf)
- policy := table.NewRoutingPolicy()
- policy.Reset(&config.RoutingPolicy{}, nil)
- p := NewPeer(
- &config.Global{Config: config.GlobalConfig{As: myAs}},
- nConf,
- rib,
- policy)
- for _, f := range rib.GetRFlist() {
- p.fsm.rfMap[f] = bgp.BGP_ADD_PATH_NONE
- }
- return p, &table.PeerInfo{AS: as, Address: net.ParseIP(address)}
-}
-
-func process(rib *table.TableManager, l []*table.Path) (*table.Path, *table.Path) {
- dsts := make([]*table.Update, 0)
- for _, path := range l {
- dsts = append(dsts, rib.Update(path)...)
- }
- news, olds, _ := dstsToPaths(table.GLOBAL_RIB_NAME, 0, dsts)
- if len(news) != 1 {
- panic("can't handle multiple paths")
- }
-
- return news[0], olds[0]
-}
-
-func TestFilterpathWitheBGP(t *testing.T) {
- as := uint32(65000)
- p1As := uint32(65001)
- p2As := uint32(65002)
- rib := table.NewTableManager([]bgp.RouteFamily{bgp.RF_IPv4_UC})
- p1, pi1 := newPeerandInfo(as, p1As, "192.168.0.1", rib)
- p2, pi2 := newPeerandInfo(as, p2As, "192.168.0.2", rib)
-
- nlri := bgp.NewIPAddrPrefix(24, "10.10.10.0")
- pa1 := []bgp.PathAttributeInterface{bgp.NewPathAttributeAsPath([]bgp.AsPathParamInterface{bgp.NewAs4PathParam(2, []uint32{p1As})}), bgp.NewPathAttributeLocalPref(200)}
- pa2 := []bgp.PathAttributeInterface{bgp.NewPathAttributeAsPath([]bgp.AsPathParamInterface{bgp.NewAs4PathParam(2, []uint32{p2As})})}
-
- path1 := table.NewPath(pi1, nlri, false, pa1, time.Now(), false)
- path2 := table.NewPath(pi2, nlri, false, pa2, time.Now(), false)
- rib.Update(path2)
- d := rib.Update(path1)
- new, old, _ := d[0].GetChanges(table.GLOBAL_RIB_NAME, 0, false)
- assert.Equal(t, new, path1)
- filterpath(p1, new, old)
- filterpath(p2, new, old)
-
- new, old = process(rib, []*table.Path{path1.Clone(true)})
- assert.Equal(t, new, path2)
- // p1 and p2 advertized the same prefix and p1's was best. Then p1 withdraw it, so p2 must get withdawal.
- path := filterpath(p2, new, old)
- assert.NotNil(t, path)
- assert.True(t, path.IsWithdraw)
-
- // p1 should get the new best (from p2)
- assert.Equal(t, filterpath(p1, new, old), path2)
-
- new, old = process(rib, []*table.Path{path2.Clone(true)})
- assert.True(t, new.IsWithdraw)
- // p2 withdraw so p1 should get withdrawal.
- path = filterpath(p1, new, old)
- assert.True(t, path.IsWithdraw)
-
- // p2 withdraw so p2 should get nothing.
- path = filterpath(p2, new, old)
- assert.Nil(t, path)
-}
-
-func TestFilterpathWithiBGP(t *testing.T) {
- as := uint32(65000)
-
- rib := table.NewTableManager([]bgp.RouteFamily{bgp.RF_IPv4_UC})
- p1, pi1 := newPeerandInfo(as, as, "192.168.0.1", rib)
- //p2, pi2 := newPeerandInfo(as, as, "192.168.0.2", rib)
- p2, _ := newPeerandInfo(as, as, "192.168.0.2", rib)
-
- nlri := bgp.NewIPAddrPrefix(24, "10.10.10.0")
- pa1 := []bgp.PathAttributeInterface{bgp.NewPathAttributeAsPath([]bgp.AsPathParamInterface{bgp.NewAs4PathParam(2, []uint32{as})}), bgp.NewPathAttributeLocalPref(200)}
- //pa2 := []bgp.PathAttributeInterface{bgp.NewPathAttributeAsPath([]bgp.AsPathParamInterface{bgp.NewAs4PathParam(2, []uint32{as})})}
-
- path1 := table.NewPath(pi1, nlri, false, pa1, time.Now(), false)
- //path2 := table.NewPath(pi2, nlri, false, pa2, time.Now(), false)
-
- new, old := process(rib, []*table.Path{path1})
- assert.Equal(t, new, path1)
- path := filterpath(p1, new, old)
- assert.Nil(t, path)
- path = filterpath(p2, new, old)
- assert.Nil(t, path)
-
- new, old = process(rib, []*table.Path{path1.Clone(true)})
- path = filterpath(p1, new, old)
- assert.Nil(t, path)
- path = filterpath(p2, new, old)
- assert.Nil(t, path)
-
-}
-
-func TestFilterpathWithRejectPolicy(t *testing.T) {
- rib1 := table.NewTableManager([]bgp.RouteFamily{bgp.RF_IPv4_UC})
- _, pi1 := newPeerandInfo(1, 2, "192.168.0.1", rib1)
- rib2 := table.NewTableManager([]bgp.RouteFamily{bgp.RF_IPv4_UC})
- p2, _ := newPeerandInfo(1, 3, "192.168.0.2", rib2)
-
- comSet1 := config.CommunitySet{
- CommunitySetName: "comset1",
- CommunityList: []string{"100:100"},
- }
- s, _ := table.NewCommunitySet(comSet1)
- p2.policy.AddDefinedSet(s)
-
- statement := config.Statement{
- Name: "stmt1",
- Conditions: config.Conditions{
- BgpConditions: config.BgpConditions{
- MatchCommunitySet: config.MatchCommunitySet{
- CommunitySet: "comset1",
- },
- },
- },
- Actions: config.Actions{
- RouteDisposition: config.ROUTE_DISPOSITION_REJECT_ROUTE,
- },
- }
- policy := config.PolicyDefinition{
- Name: "policy1",
- Statements: []config.Statement{statement},
- }
- p, _ := table.NewPolicy(policy)
- p2.policy.AddPolicy(p, false)
- policies := []*config.PolicyDefinition{
- &config.PolicyDefinition{
- Name: "policy1",
- },
- }
- p2.policy.AddPolicyAssignment(p2.TableID(), table.POLICY_DIRECTION_EXPORT, policies, table.ROUTE_TYPE_ACCEPT)
-
- for _, addCommunity := range []bool{false, true, false, true} {
- nlri := bgp.NewIPAddrPrefix(24, "10.10.10.0")
- pa1 := []bgp.PathAttributeInterface{bgp.NewPathAttributeAsPath([]bgp.AsPathParamInterface{bgp.NewAs4PathParam(2, []uint32{1})}), bgp.NewPathAttributeLocalPref(200)}
- if addCommunity {
- pa1 = append(pa1, bgp.NewPathAttributeCommunities([]uint32{100<<16 | 100}))
- }
- path1 := table.NewPath(pi1, nlri, false, pa1, time.Now(), false)
- new, old := process(rib2, []*table.Path{path1})
- assert.Equal(t, new, path1)
- s := NewBgpServer()
- path2 := s.filterpath(p2, new, old)
- if addCommunity {
- assert.True(t, path2.IsWithdraw)
- } else {
- assert.False(t, path2.IsWithdraw)
- }
- }
-
-}
-
-func TestPeerGroup(test *testing.T) {
- assert := assert.New(test)
- log.SetLevel(log.DebugLevel)
- s := NewBgpServer()
- go s.Serve()
- err := s.Start(&config.Global{
- Config: config.GlobalConfig{
- As: 1,
- RouterId: "1.1.1.1",
- Port: 10179,
- },
- })
- assert.Nil(err)
- defer s.Stop()
-
- g := &config.PeerGroup{
- Config: config.PeerGroupConfig{
- PeerAs: 2,
- PeerGroupName: "g",
- },
- }
- err = s.AddPeerGroup(g)
- assert.Nil(err)
-
- n := &config.Neighbor{
- Config: config.NeighborConfig{
- NeighborAddress: "127.0.0.1",
- PeerGroup: "g",
- },
- Transport: config.Transport{
- Config: config.TransportConfig{
- PassiveMode: true,
- },
- },
- }
- configured := map[string]interface{}{
- "config": map[string]interface{}{
- "neigbor-address": "127.0.0.1",
- "peer-group": "g",
- },
- "transport": map[string]interface{}{
- "config": map[string]interface{}{
- "passive-mode": true,
- },
- },
- }
- config.RegisterConfiguredFields("127.0.0.1", configured)
- err = s.AddNeighbor(n)
- assert.Nil(err)
-
- t := NewBgpServer()
- go t.Serve()
- err = t.Start(&config.Global{
- Config: config.GlobalConfig{
- As: 2,
- RouterId: "2.2.2.2",
- Port: -1,
- },
- })
- assert.Nil(err)
- defer t.Stop()
-
- m := &config.Neighbor{
- Config: config.NeighborConfig{
- NeighborAddress: "127.0.0.1",
- PeerAs: 1,
- },
- Transport: config.Transport{
- Config: config.TransportConfig{
- RemotePort: 10179,
- },
- },
- }
- err = t.AddNeighbor(m)
- assert.Nil(err)
-
- for {
- time.Sleep(time.Second)
- if t.GetNeighbor("", false)[0].State.SessionState == config.SESSION_STATE_ESTABLISHED {
- break
- }
- }
-}
-
-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()
- 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()
-
- n := &config.Neighbor{
- Config: config.NeighborConfig{
- NeighborAddress: "127.0.0.1",
- PeerAs: 2,
- },
- Transport: config.Transport{
- Config: config.TransportConfig{
- PassiveMode: true,
- },
- },
- GracefulRestart: config.GracefulRestart{
- Config: config.GracefulRestartConfig{
- Enabled: true,
- RestartTime: 1,
- },
- },
- }
- err = s1.AddNeighbor(n)
- 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,
- },
- })
- require.NoError(t, 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,
- },
- },
- GracefulRestart: config.GracefulRestart{
- Config: config.GracefulRestartConfig{
- Enabled: true,
- RestartTime: 1,
- },
- },
- }
- err = s2.AddNeighbor(m)
- assert.Nil(err)
-
- // Waiting for BGP session established.
- for {
- time.Sleep(time.Second)
- if s2.GetNeighbor("", false)[0].State.SessionState == config.SESSION_STATE_ESTABLISHED {
- break
- }
- }
-
- // Force TCP session disconnected in order to cause Graceful Restart at s1
- // side.
- for _, n := range s2.neighborMap {
- n.fsm.conn.Close()
- }
- s2.Stop()
-
- time.Sleep(5 * time.Second)
-
- // Create dummy session which does NOT send BGP OPEN message in order to
- // cause Graceful Restart timer expired.
- var conn net.Conn
-
- conn, err = net.Dial("tcp", "127.0.0.1:10179")
- require.NoError(t, err)
- defer conn.Close()
-
- // this seems to take around 22 seconds... need to address this whole thing
- ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
- defer cancel()
-
- // Waiting for Graceful Restart timer expired and moving on to IDLE state.
- for {
- if s1.GetNeighbor("", false)[0].State.SessionState == config.SESSION_STATE_IDLE {
- break
- }
-
- select {
- case <-time.After(time.Second):
- case <-ctx.Done():
- t.Fatalf("failed to enter IDLE state in the deadline")
- return
- }
- }
-}
-
-func TestFamiliesForSoftreset(t *testing.T) {
- f := func(f bgp.RouteFamily) config.AfiSafi {
- return config.AfiSafi{
- State: config.AfiSafiState{
- Family: f,
- },
- }
- }
- peer := &Peer{
- fsm: &FSM{
- pConf: &config.Neighbor{
- AfiSafis: []config.AfiSafi{f(bgp.RF_RTC_UC), f(bgp.RF_IPv4_UC), f(bgp.RF_IPv6_UC)},
- },
- },
- }
-
- families := familiesForSoftreset(peer, bgp.RF_IPv4_UC)
- assert.Equal(t, len(families), 1)
- assert.Equal(t, families[0], bgp.RF_IPv4_UC)
-
- families = familiesForSoftreset(peer, bgp.RF_RTC_UC)
- assert.Equal(t, len(families), 1)
- assert.Equal(t, families[0], bgp.RF_RTC_UC)
-
- families = familiesForSoftreset(peer, bgp.RouteFamily(0))
- assert.Equal(t, len(families), 2)
- assert.NotContains(t, families, bgp.RF_RTC_UC)
-}
diff --git a/server/sockopt.go b/server/sockopt.go
deleted file mode 100644
index e1c9c467..00000000
--- a/server/sockopt.go
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright (C) 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.
-// +build !linux,!openbsd
-
-package server
-
-import (
- "fmt"
- "net"
-
- log "github.com/sirupsen/logrus"
-)
-
-func SetTcpMD5SigSockopt(l *net.TCPListener, address string, key string) error {
- return setTcpMD5SigSockopt(l, address, key)
-}
-
-func SetListenTcpTTLSockopt(l *net.TCPListener, ttl int) error {
- return setListenTcpTTLSockopt(l, ttl)
-}
-
-func SetTcpTTLSockopt(conn *net.TCPConn, ttl int) error {
- return setTcpTTLSockopt(conn, ttl)
-}
-
-func SetTcpMinTTLSockopt(conn *net.TCPConn, ttl int) error {
- return setTcpMinTTLSockopt(conn, ttl)
-}
-
-type TCPDialer struct {
- net.Dialer
-
- // MD5 authentication password.
- AuthPassword string
-
- // The TTL value to set outgoing connection.
- Ttl uint8
-
- // The minimum TTL value for incoming packets.
- TtlMin uint8
-}
-
-func (d *TCPDialer) DialTCP(addr string, port int) (*net.TCPConn, error) {
- if d.AuthPassword != "" {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": addr,
- }).Warn("setting md5 for active connection is not supported")
- }
- if d.Ttl != 0 {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": addr,
- }).Warn("setting ttl for active connection is not supported")
- }
- if d.TtlMin != 0 {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": addr,
- }).Warn("setting min ttl for active connection is not supported")
- }
-
- raddr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort(addr, fmt.Sprintf("%d", port)))
- if err != nil {
- return nil, fmt.Errorf("invalid remote address: %s", err)
- }
- laddr, err := net.ResolveTCPAddr("tcp", d.LocalAddr.String())
- if err != nil {
- return nil, fmt.Errorf("invalid local address: %s", err)
- }
-
- dialer := net.Dialer{LocalAddr: laddr, Timeout: d.Timeout}
- conn, err := dialer.Dial("tcp", raddr.String())
- if err != nil {
- return nil, err
- }
- return conn.(*net.TCPConn), nil
-}
diff --git a/server/sockopt_bsd.go b/server/sockopt_bsd.go
deleted file mode 100644
index 651e4e58..00000000
--- a/server/sockopt_bsd.go
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright (C) 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.
-// +build dragonfly freebsd netbsd
-
-package server
-
-import (
- "net"
- "os"
- "syscall"
-)
-
-const (
- TCP_MD5SIG = 0x10 // TCP MD5 Signature (RFC2385)
- IPV6_MINHOPCOUNT = 73 // Generalized TTL Security Mechanism (RFC5082)
-)
-
-func setsockoptTcpMD5Sig(fd int, address string, key string) error {
- // always enable and assumes that the configuration is done by setkey()
- return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, TCP_MD5SIG, 1))
-}
-
-func setTcpMD5SigSockopt(l *net.TCPListener, address string, key string) error {
- fi, _, err := extractFileAndFamilyFromTCPListener(l)
- defer fi.Close()
- if err != nil {
- return err
- }
- return setsockoptTcpMD5Sig(int(fi.Fd()), address, key)
-}
-
-func setsockoptIpTtl(fd int, family int, value int) error {
- level := syscall.IPPROTO_IP
- name := syscall.IP_TTL
- if family == syscall.AF_INET6 {
- level = syscall.IPPROTO_IPV6
- name = syscall.IPV6_UNICAST_HOPS
- }
- return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, level, name, value))
-}
-
-func setListenTcpTTLSockopt(l *net.TCPListener, ttl int) error {
- fi, family, err := extractFileAndFamilyFromTCPListener(l)
- defer fi.Close()
- if err != nil {
- return err
- }
- return setsockoptIpTtl(int(fi.Fd()), family, ttl)
-}
-
-func setTcpTTLSockopt(conn *net.TCPConn, ttl int) error {
- fi, family, err := extractFileAndFamilyFromTCPConn(conn)
- defer fi.Close()
- if err != nil {
- return err
- }
- return setsockoptIpTtl(int(fi.Fd()), family, ttl)
-}
-
-func setsockoptIpMinTtl(fd int, family int, value int) error {
- level := syscall.IPPROTO_IP
- name := syscall.IP_MINTTL
- if family == syscall.AF_INET6 {
- level = syscall.IPPROTO_IPV6
- name = IPV6_MINHOPCOUNT
- }
- return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, level, name, value))
-}
-
-func setTcpMinTTLSockopt(conn *net.TCPConn, ttl int) error {
- fi, family, err := extractFileAndFamilyFromTCPConn(conn)
- defer fi.Close()
- if err != nil {
- return err
- }
- return setsockoptIpMinTtl(int(fi.Fd()), family, ttl)
-}
diff --git a/server/sockopt_darwin.go b/server/sockopt_darwin.go
deleted file mode 100644
index 4bad54ff..00000000
--- a/server/sockopt_darwin.go
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright (C) 2016-2017 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.
-// +build darwin
-
-package server
-
-import (
- "fmt"
- "net"
- "os"
- "syscall"
-)
-
-func setTcpMD5SigSockopt(l *net.TCPListener, address string, key string) error {
- return fmt.Errorf("setting md5 is not supported")
-}
-
-func setsockoptIpTtl(fd int, family int, value int) error {
- level := syscall.IPPROTO_IP
- name := syscall.IP_TTL
- if family == syscall.AF_INET6 {
- level = syscall.IPPROTO_IPV6
- name = syscall.IPV6_UNICAST_HOPS
- }
- return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, level, name, value))
-}
-
-func setListenTcpTTLSockopt(l *net.TCPListener, ttl int) error {
- fi, family, err := extractFileAndFamilyFromTCPListener(l)
- if err != nil {
- return err
- }
-
- defer fi.Close()
-
- return setsockoptIpTtl(int(fi.Fd()), family, ttl)
-}
-
-func setTcpTTLSockopt(conn *net.TCPConn, ttl int) error {
- fi, family, err := extractFileAndFamilyFromTCPConn(conn)
- if err != nil {
- return err
- }
-
- defer fi.Close()
-
- return setsockoptIpTtl(int(fi.Fd()), family, ttl)
-}
-
-func setTcpMinTTLSockopt(conn *net.TCPConn, ttl int) error {
- return fmt.Errorf("setting min ttl is not supported")
-}
diff --git a/server/sockopt_linux.go b/server/sockopt_linux.go
deleted file mode 100644
index 9fe02ba5..00000000
--- a/server/sockopt_linux.go
+++ /dev/null
@@ -1,282 +0,0 @@
-// Copyright (C) 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.
-// +build linux
-
-package server
-
-import (
- "fmt"
- "net"
- "os"
- "syscall"
- "unsafe"
-)
-
-const (
- TCP_MD5SIG = 14 // TCP MD5 Signature (RFC2385)
- IPV6_MINHOPCOUNT = 73 // Generalized TTL Security Mechanism (RFC5082)
-)
-
-type tcpmd5sig struct {
- ss_family uint16
- ss [126]byte
- // padding the struct
- _ uint16
- keylen uint16
- // padding the struct
- _ uint32
- key [80]byte
-}
-
-func buildTcpMD5Sig(address string, key string) (tcpmd5sig, error) {
- t := tcpmd5sig{}
- addr := net.ParseIP(address)
- if addr.To4() != nil {
- t.ss_family = syscall.AF_INET
- copy(t.ss[2:], addr.To4())
- } else {
- t.ss_family = syscall.AF_INET6
- copy(t.ss[6:], addr.To16())
- }
-
- t.keylen = uint16(len(key))
- copy(t.key[0:], []byte(key))
-
- return t, nil
-}
-
-func setsockoptTcpMD5Sig(fd int, address string, key string) error {
- t, err := buildTcpMD5Sig(address, key)
- if err != nil {
- return err
- }
- b := *(*[unsafe.Sizeof(t)]byte)(unsafe.Pointer(&t))
- return os.NewSyscallError("setsockopt", syscall.SetsockoptString(fd, syscall.IPPROTO_TCP, TCP_MD5SIG, string(b[:])))
-}
-
-func SetTcpMD5SigSockopt(l *net.TCPListener, address string, key string) error {
- fi, _, err := extractFileAndFamilyFromTCPListener(l)
- if err != nil {
- return err
- }
- defer fi.Close()
-
- return setsockoptTcpMD5Sig(int(fi.Fd()), address, key)
-}
-
-func setsockoptIpTtl(fd int, family int, value int) error {
- level := syscall.IPPROTO_IP
- name := syscall.IP_TTL
- if family == syscall.AF_INET6 {
- level = syscall.IPPROTO_IPV6
- name = syscall.IPV6_UNICAST_HOPS
- }
- return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, level, name, value))
-}
-
-func SetListenTcpTTLSockopt(l *net.TCPListener, ttl int) error {
- fi, family, err := extractFileAndFamilyFromTCPListener(l)
- if err != nil {
- return err
- }
- defer fi.Close()
-
- return setsockoptIpTtl(int(fi.Fd()), family, ttl)
-}
-
-func SetTcpTTLSockopt(conn *net.TCPConn, ttl int) error {
- fi, family, err := extractFileAndFamilyFromTCPConn(conn)
- if err != nil {
- return err
- }
- defer fi.Close()
-
- return setsockoptIpTtl(int(fi.Fd()), family, ttl)
-}
-
-func setsockoptIpMinTtl(fd int, family int, value int) error {
- level := syscall.IPPROTO_IP
- name := syscall.IP_MINTTL
- if family == syscall.AF_INET6 {
- level = syscall.IPPROTO_IPV6
- name = IPV6_MINHOPCOUNT
- }
- return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, level, name, value))
-}
-
-func SetTcpMinTTLSockopt(conn *net.TCPConn, ttl int) error {
- fi, family, err := extractFileAndFamilyFromTCPConn(conn)
- if err != nil {
- return err
- }
- defer fi.Close()
-
- return setsockoptIpMinTtl(int(fi.Fd()), family, ttl)
-}
-
-type TCPDialer struct {
- net.Dialer
-
- // MD5 authentication password.
- AuthPassword string
-
- // The TTL value to set outgoing connection.
- Ttl uint8
-
- // The minimum TTL value for incoming packets.
- TtlMin uint8
-}
-
-func (d *TCPDialer) DialTCP(addr string, port int) (*net.TCPConn, error) {
- var family int
- var ra, la syscall.Sockaddr
-
- raddr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort(addr, fmt.Sprintf("%d", port)))
- if err != nil {
- return nil, fmt.Errorf("invalid remote address: %s", err)
- }
- laddr, err := net.ResolveTCPAddr("tcp", d.LocalAddr.String())
- if err != nil {
- return nil, fmt.Errorf("invalid local address: %s", err)
- }
- if raddr.IP.To4() != nil {
- family = syscall.AF_INET
- rsockaddr := &syscall.SockaddrInet4{Port: port}
- copy(rsockaddr.Addr[:], raddr.IP.To4())
- ra = rsockaddr
- lsockaddr := &syscall.SockaddrInet4{}
- copy(lsockaddr.Addr[:], laddr.IP.To4())
- la = lsockaddr
- } else {
- family = syscall.AF_INET6
- rsockaddr := &syscall.SockaddrInet6{Port: port}
- copy(rsockaddr.Addr[:], raddr.IP.To16())
- ra = rsockaddr
- var zone uint32
- if laddr.Zone != "" {
- if intf, err := net.InterfaceByName(laddr.Zone); err != nil {
- return nil, err
- } else {
- zone = uint32(intf.Index)
- }
- }
- lsockaddr := &syscall.SockaddrInet6{ZoneId: zone}
- copy(lsockaddr.Addr[:], laddr.IP.To16())
- la = lsockaddr
- }
-
- sockType := syscall.SOCK_STREAM | syscall.SOCK_CLOEXEC | syscall.SOCK_NONBLOCK
- proto := 0
- fd, err := syscall.Socket(family, sockType, proto)
- if err != nil {
- return nil, err
- }
- fi := os.NewFile(uintptr(fd), "")
- defer fi.Close()
- // A new socket was created so we must close it before this
- // function returns either on failure or success. On success,
- // net.FileConn() in newTCPConn() increases the refcount of
- // the socket so this fi.Close() doesn't destroy the socket.
- // The caller must call Close() with the file later.
- // Note that the above os.NewFile() doesn't play with the
- // refcount.
-
- if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1); err != nil {
- return nil, os.NewSyscallError("setsockopt", err)
- }
-
- if err = syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, syscall.TCP_NODELAY, 1); err != nil {
- return nil, os.NewSyscallError("setsockopt", err)
- }
-
- if d.AuthPassword != "" {
- if err = setsockoptTcpMD5Sig(fd, addr, d.AuthPassword); err != nil {
- return nil, err
- }
- }
-
- if d.Ttl != 0 {
- if err = setsockoptIpTtl(fd, family, int(d.Ttl)); err != nil {
- return nil, err
- }
- }
-
- if d.TtlMin != 0 {
- if err = setsockoptIpMinTtl(fd, family, int(d.Ttl)); err != nil {
- return nil, err
- }
- }
-
- if err = syscall.Bind(fd, la); err != nil {
- return nil, os.NewSyscallError("bind", err)
- }
-
- newTCPConn := func(fi *os.File) (*net.TCPConn, error) {
- if conn, err := net.FileConn(fi); err != nil {
- return nil, err
- } else {
- return conn.(*net.TCPConn), err
- }
- }
-
- err = syscall.Connect(fd, ra)
- switch err {
- case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
- // do timeout handling
- case nil, syscall.EISCONN:
- return newTCPConn(fi)
- default:
- return nil, os.NewSyscallError("connect", err)
- }
-
- epfd, e := syscall.EpollCreate1(syscall.EPOLL_CLOEXEC)
- if e != nil {
- return nil, e
- }
- defer syscall.Close(epfd)
-
- var event syscall.EpollEvent
- events := make([]syscall.EpollEvent, 1)
-
- event.Events = syscall.EPOLLIN | syscall.EPOLLOUT | syscall.EPOLLPRI
- event.Fd = int32(fd)
- if e = syscall.EpollCtl(epfd, syscall.EPOLL_CTL_ADD, fd, &event); e != nil {
- return nil, e
- }
-
- for {
- nevents, e := syscall.EpollWait(epfd, events, int(d.Timeout/1000000) /*msec*/)
- if e != nil {
- return nil, e
- }
- if nevents == 0 {
- return nil, fmt.Errorf("timeout")
- } else if nevents == 1 && events[0].Fd == int32(fd) {
- nerr, err := syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_ERROR)
- if err != nil {
- return nil, os.NewSyscallError("getsockopt", err)
- }
- switch err := syscall.Errno(nerr); err {
- case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
- case syscall.Errno(0), syscall.EISCONN:
- return newTCPConn(fi)
- default:
- return nil, os.NewSyscallError("getsockopt", err)
- }
- } else {
- return nil, fmt.Errorf("unexpected epoll behavior")
- }
- }
-}
diff --git a/server/sockopt_linux_test.go b/server/sockopt_linux_test.go
deleted file mode 100644
index a08e7fc7..00000000
--- a/server/sockopt_linux_test.go
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright (C) 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.
-// +build linux
-
-package server
-
-import (
- "bytes"
- "fmt"
- "net"
- "os"
- "syscall"
- "testing"
- "time"
- "unsafe"
-)
-
-func Test_buildTcpMD5Sig(t *testing.T) {
- s, _ := buildTcpMD5Sig("1.2.3.4", "hello")
-
- if unsafe.Sizeof(s) != 216 {
- t.Error("TCPM5Sig struct size is wrong", unsafe.Sizeof(s))
- }
-
- buf1 := make([]uint8, 216)
- p := unsafe.Pointer(&s)
- for i := uintptr(0); i < 216; i++ {
- buf1[i] = *(*byte)(unsafe.Pointer(uintptr(p) + i))
- }
-
- buf2 := []uint8{2, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 104, 101, 108, 108, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
-
- if bytes.Equal(buf1, buf2) {
- t.Log("OK")
- } else {
- t.Error("Something wrong v4")
- }
-}
-
-func Test_buildTcpMD5Sigv6(t *testing.T) {
- s, _ := buildTcpMD5Sig("fe80::4850:31ff:fe01:fc55", "helloworld")
-
- buf1 := make([]uint8, 216)
- p := unsafe.Pointer(&s)
- for i := uintptr(0); i < 216; i++ {
- buf1[i] = *(*byte)(unsafe.Pointer(uintptr(p) + i))
- }
-
- buf2 := []uint8{10, 0, 0, 0, 0, 0, 0, 0, 254, 128, 0, 0, 0, 0, 0, 0, 72, 80, 49, 255, 254, 1, 252, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 104, 101, 108, 108, 111, 119, 111, 114, 108, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
-
- buf2[0] = syscall.AF_INET6
-
- if bytes.Equal(buf1, buf2) {
- t.Log("OK")
- } else {
- t.Error("Something wrong v6")
- }
-}
-
-func Test_DialTCP_FDleak(t *testing.T) {
- openFds := func() int {
- pid := os.Getpid()
- f, err := os.OpenFile(fmt.Sprintf("/proc/%d/fdinfo", pid), os.O_RDONLY, 0)
- if err != nil {
- t.Fatal(err)
- }
- defer f.Close()
- names, err := f.Readdirnames(0)
- if err != nil {
- t.Fatal(err)
- }
- return len(names)
- }
-
- before := openFds()
-
- for i := 0; i < 10; i++ {
- laddr, _ := net.ResolveTCPAddr("tcp", net.JoinHostPort("127.0.0.1", "0"))
- d := TCPDialer{
- Dialer: net.Dialer{
- LocalAddr: laddr,
- Timeout: 1 * time.Second,
- },
- }
- if _, err := d.DialTCP("127.0.0.1", 1); err == nil {
- t.Fatalf("should not succeed")
- }
-
- }
-
- if after := openFds(); before != after {
- t.Fatalf("could be fd leak, %d %d", before, after)
- }
-}
diff --git a/server/sockopt_openbsd.go b/server/sockopt_openbsd.go
deleted file mode 100644
index f52c1447..00000000
--- a/server/sockopt_openbsd.go
+++ /dev/null
@@ -1,469 +0,0 @@
-// Copyright (C) 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.
-// +build openbsd
-
-package server
-
-import (
- "encoding/binary"
- "fmt"
- "net"
- "os"
- "syscall"
- "unsafe"
-
- log "github.com/sirupsen/logrus"
-)
-
-const (
- PF_KEY_V2 = 2
-
- SADB_X_SATYPE_TCPSIGNATURE = 8
-
- SADB_EXT_SA = 1
- SADB_EXT_ADDRESS_SRC = 5
- SADB_EXT_ADDRESS_DST = 6
- SADB_EXT_KEY_AUTH = 8
- SADB_EXT_SPIRANGE = 16
-
- SADB_GETSPI = 1
- SADB_UPDATE = 2
- SADB_DELETE = 4
-
- SADB_X_EALG_AES = 12
-
- SADB_SASTATE_MATURE = 1
-)
-
-type sadbMsg struct {
- sadbMsgVersion uint8
- sadbMsgType uint8
- sadbMsgErrno uint8
- sadbMsgSatype uint8
- sadbMsgLen uint16
- sadbMsgReserved uint16
- sadbMsgSeq uint32
- sadbMsgPid uint32
-}
-
-func (s *sadbMsg) DecodeFromBytes(data []byte) error {
- if len(data) < SADB_MSG_SIZE {
- fmt.Errorf("too short for sadbMsg %d", len(data))
- }
- s.sadbMsgVersion = data[0]
- s.sadbMsgType = data[1]
- s.sadbMsgErrno = data[2]
- s.sadbMsgSatype = data[3]
- s.sadbMsgLen = binary.LittleEndian.Uint16(data[4:6])
- s.sadbMsgSeq = binary.LittleEndian.Uint32(data[8:12])
- s.sadbMsgPid = binary.LittleEndian.Uint32(data[12:16])
- return nil
-}
-
-type sadbSpirange struct {
- sadbSpirangeLen uint16
- sadbSpirangeExttype uint16
- sadbSpirangeMin uint32
- sadbSpirangeMax uint32
- sadbSpirangeReserved uint32
-}
-
-type sadbAddress struct {
- sadbAddressLen uint16
- sadbAddressExttype uint16
- sadbAddressReserved uint32
-}
-
-type sadbExt struct {
- sadbExtLen uint16
- sadbExtType uint16
-}
-
-type sadbSa struct {
- sadbSaLen uint16
- sadbSaExttype uint16
- sadbSaSpi uint32
- sadbSaReplay uint8
- sadbSaState uint8
- sadbSaAuth uint8
- sadbSaEncrypt uint8
- sadbSaFlags uint32
-}
-
-type sadbKey struct {
- sadbKeyLen uint16
- sadbKeyExttype uint16
- sadbKeyBits uint16
- sadbKeyReserved uint16
-}
-
-const (
- SADB_MSG_SIZE = int(unsafe.Sizeof(sadbMsg{}))
- SADB_SPIRANGE_SIZE = int(unsafe.Sizeof(sadbSpirange{}))
- SADB_ADDRESS_SIZE = int(unsafe.Sizeof(sadbAddress{}))
- SADB_SA_SIZE = int(unsafe.Sizeof(sadbSa{}))
- SADB_KEY_SIZE = int(unsafe.Sizeof(sadbKey{}))
-)
-
-type sockaddrIn struct {
- ssLen uint8
- ssFamily uint8
- ssPort uint16
- ssAddr uint32
- pad [8]byte
-}
-
-func newSockaddrIn(addr string) sockaddrIn {
- if len(addr) == 0 {
- return sockaddrIn{
- ssLen: 16,
- }
- }
- v := net.ParseIP(addr).To4()
- return sockaddrIn{
- ssAddr: uint32(v[3])<<24 | uint32(v[2])<<16 | uint32(v[1])<<8 | uint32(v[0]),
- ssLen: 16,
- ssFamily: syscall.AF_INET,
- }
-}
-
-func roundUp(v int) int {
- if v%8 != 0 {
- v += 8 - v%8
- }
- return v
-}
-
-func b(p unsafe.Pointer, length int) []byte {
- buf := make([]byte, length)
- for i := 0; i < length; i++ {
- buf[i] = *(*byte)(p)
- p = unsafe.Pointer(uintptr(p) + 1)
- }
- return buf
-}
-
-var seq uint32
-var fd int
-
-var spiInMap map[string]uint32 = map[string]uint32{}
-var spiOutMap map[string]uint32 = map[string]uint32{}
-
-func pfkeyReply() (spi uint32, err error) {
- buf := make([]byte, SADB_MSG_SIZE)
- if count, _, _, _, _ := syscall.Recvmsg(fd, buf, nil, syscall.MSG_PEEK); count != len(buf) {
- return spi, fmt.Errorf("incomplete sadb msg %d %d", len(buf), count)
- }
- h := sadbMsg{}
- h.DecodeFromBytes(buf)
- if h.sadbMsgErrno != 0 {
- return spi, fmt.Errorf("sadb msg reply error %d", h.sadbMsgErrno)
- }
-
- if h.sadbMsgSeq != seq {
- return spi, fmt.Errorf("sadb msg sequence doesn't match %d %d", h.sadbMsgSeq, seq)
- }
-
- if h.sadbMsgPid != uint32(os.Getpid()) {
- return spi, fmt.Errorf("sadb msg pid doesn't match %d %d", h.sadbMsgPid, os.Getpid())
- }
-
- buf = make([]byte, int(8*h.sadbMsgLen))
- if count, _, _, _, _ := syscall.Recvmsg(fd, buf, nil, 0); count != len(buf) {
- return spi, fmt.Errorf("incomplete sadb msg body %d %d", len(buf), count)
- }
-
- buf = buf[SADB_MSG_SIZE:]
-
- for len(buf) >= 4 {
- l := binary.LittleEndian.Uint16(buf[0:2]) * 8
- t := binary.LittleEndian.Uint16(buf[2:4])
- if t == SADB_EXT_SA {
- return binary.LittleEndian.Uint32(buf[4:8]), nil
- }
-
- if len(buf) <= int(l) {
- break
- }
- buf = buf[l:]
- }
- return spi, err
-}
-
-func sendSadbMsg(msg *sadbMsg, body []byte) (err error) {
- if fd == 0 {
- fd, err = syscall.Socket(syscall.AF_KEY, syscall.SOCK_RAW, PF_KEY_V2)
- if err != nil {
- return err
- }
- }
-
- seq++
- msg.sadbMsgSeq = seq
- msg.sadbMsgLen = uint16((len(body) + SADB_MSG_SIZE) / 8)
-
- buf := append(b(unsafe.Pointer(msg), SADB_MSG_SIZE), body...)
-
- r, err := syscall.Write(fd, buf)
- if r != len(buf) {
- return fmt.Errorf("short write %d %d", r, len(buf))
- }
- return err
-}
-
-func rfkeyRequest(msgType uint8, src, dst string, spi uint32, key string) error {
- h := sadbMsg{
- sadbMsgVersion: PF_KEY_V2,
- sadbMsgType: msgType,
- sadbMsgSatype: SADB_X_SATYPE_TCPSIGNATURE,
- sadbMsgPid: uint32(os.Getpid()),
- }
-
- ssrc := newSockaddrIn(src)
- sa_src := sadbAddress{
- sadbAddressExttype: SADB_EXT_ADDRESS_SRC,
- sadbAddressLen: uint16(SADB_ADDRESS_SIZE+roundUp(int(ssrc.ssLen))) / 8,
- }
-
- sdst := newSockaddrIn(dst)
- sa_dst := sadbAddress{
- sadbAddressExttype: SADB_EXT_ADDRESS_DST,
- sadbAddressLen: uint16(SADB_ADDRESS_SIZE+roundUp(int(sdst.ssLen))) / 8,
- }
-
- buf := make([]byte, 0)
- switch msgType {
- case SADB_UPDATE, SADB_DELETE:
- sa := sadbSa{
- sadbSaLen: uint16(SADB_SA_SIZE / 8),
- sadbSaExttype: SADB_EXT_SA,
- sadbSaSpi: spi,
- sadbSaState: SADB_SASTATE_MATURE,
- sadbSaEncrypt: SADB_X_EALG_AES,
- }
- buf = append(buf, b(unsafe.Pointer(&sa), SADB_SA_SIZE)...)
- case SADB_GETSPI:
- spirange := sadbSpirange{
- sadbSpirangeLen: uint16(SADB_SPIRANGE_SIZE) / 8,
- sadbSpirangeExttype: SADB_EXT_SPIRANGE,
- sadbSpirangeMin: 0x100,
- sadbSpirangeMax: 0xffffffff,
- }
- buf = append(buf, b(unsafe.Pointer(&spirange), SADB_SPIRANGE_SIZE)...)
- }
-
- buf = append(buf, b(unsafe.Pointer(&sa_dst), SADB_ADDRESS_SIZE)...)
- buf = append(buf, b(unsafe.Pointer(&sdst), roundUp(int(sdst.ssLen)))...)
- buf = append(buf, b(unsafe.Pointer(&sa_src), SADB_ADDRESS_SIZE)...)
- buf = append(buf, b(unsafe.Pointer(&ssrc), roundUp(int(ssrc.ssLen)))...)
-
- switch msgType {
- case SADB_UPDATE:
- keylen := roundUp(len(key))
- sa_akey := sadbKey{
- sadbKeyLen: uint16((SADB_KEY_SIZE + keylen) / 8),
- sadbKeyExttype: SADB_EXT_KEY_AUTH,
- sadbKeyBits: uint16(len(key) * 8),
- }
- k := []byte(key)
- if pad := keylen - len(k); pad != 0 {
- k = append(k, make([]byte, pad)...)
- }
- buf = append(buf, b(unsafe.Pointer(&sa_akey), SADB_KEY_SIZE)...)
- buf = append(buf, k...)
- }
-
- return sendSadbMsg(&h, buf)
-}
-
-func saAdd(address, key string) error {
- f := func(src, dst string) error {
- if err := rfkeyRequest(SADB_GETSPI, src, dst, 0, ""); err != nil {
- return err
- }
- spi, err := pfkeyReply()
- if err != nil {
- return err
- }
- if src == "" {
- spiOutMap[address] = spi
- } else {
- spiInMap[address] = spi
- }
-
- if err := rfkeyRequest(SADB_UPDATE, src, dst, spi, key); err != nil {
- return err
- }
- _, err = pfkeyReply()
- return err
- }
-
- if err := f(address, ""); err != nil {
- return err
- }
-
- return f("", address)
-}
-
-func saDelete(address string) error {
- if spi, y := spiInMap[address]; y {
- if err := rfkeyRequest(SADB_DELETE, address, "", spi, ""); err != nil {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": address,
- }).Info("failed to delete md5 for incoming")
- } else {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": address,
- }).Info("can't find spi for md5 for incoming")
- }
- }
- if spi, y := spiOutMap[address]; y {
- if err := rfkeyRequest(SADB_DELETE, "", address, spi, ""); err != nil {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": address,
- }).Info("failed to delete md5 for outgoing")
- } else {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": address,
- }).Info("can't find spi for md5 for outgoing")
- }
- }
- return nil
-}
-
-const (
- TCP_MD5SIG = 0x4 // TCP MD5 Signature (RFC2385)
- IPV6_MINHOPCOUNT = 73 // Generalized TTL Security Mechanism (RFC5082)
-)
-
-func setsockoptTcpMD5Sig(fd int, address string, key string) error {
- if err := syscall.SetsockoptInt(fd, syscall.IPPROTO_TCP, TCP_MD5SIG, 1); err != nil {
- return os.NewSyscallError("setsockopt", err)
- }
- if len(key) > 0 {
- return saAdd(address, key)
- }
- return saDelete(address)
-}
-
-func SetTcpMD5SigSockopt(l *net.TCPListener, address string, key string) error {
- fi, _, err := extractFileAndFamilyFromTCPListener(l)
- defer fi.Close()
- if err != nil {
- return err
- }
- return setsockoptTcpMD5Sig(int(fi.Fd()), address, key)
-}
-
-func setsockoptIpTtl(fd int, family int, value int) error {
- level := syscall.IPPROTO_IP
- name := syscall.IP_TTL
- if family == syscall.AF_INET6 {
- level = syscall.IPPROTO_IPV6
- name = syscall.IPV6_UNICAST_HOPS
- }
- return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, level, name, value))
-}
-
-func SetListenTcpTTLSockopt(l *net.TCPListener, ttl int) error {
- fi, family, err := extractFileAndFamilyFromTCPListener(l)
- defer fi.Close()
- if err != nil {
- return err
- }
- return setsockoptIpTtl(int(fi.Fd()), family, ttl)
-}
-
-func SetTcpTTLSockopt(conn *net.TCPConn, ttl int) error {
- fi, family, err := extractFileAndFamilyFromTCPConn(conn)
- defer fi.Close()
- if err != nil {
- return err
- }
- return setsockoptIpTtl(int(fi.Fd()), family, ttl)
-}
-
-func setsockoptIpMinTtl(fd int, family int, value int) error {
- level := syscall.IPPROTO_IP
- name := syscall.IP_MINTTL
- if family == syscall.AF_INET6 {
- level = syscall.IPPROTO_IPV6
- name = IPV6_MINHOPCOUNT
- }
- return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd, level, name, value))
-}
-
-func SetTcpMinTTLSockopt(conn *net.TCPConn, ttl int) error {
- fi, family, err := extractFileAndFamilyFromTCPConn(conn)
- defer fi.Close()
- if err != nil {
- return err
- }
- return setsockoptIpMinTtl(int(fi.Fd()), family, ttl)
-}
-
-type TCPDialer struct {
- net.Dialer
-
- // MD5 authentication password.
- AuthPassword string
-
- // The TTL value to set outgoing connection.
- Ttl uint8
-
- // The minimum TTL value for incoming packets.
- TtlMin uint8
-}
-
-func (d *TCPDialer) DialTCP(addr string, port int) (*net.TCPConn, error) {
- if d.AuthPassword != "" {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": addr,
- }).Warn("setting md5 for active connection is not supported")
- }
- if d.Ttl != 0 {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": addr,
- }).Warn("setting ttl for active connection is not supported")
- }
- if d.TtlMin != 0 {
- log.WithFields(log.Fields{
- "Topic": "Peer",
- "Key": addr,
- }).Warn("setting min ttl for active connection is not supported")
- }
-
- raddr, err := net.ResolveTCPAddr("tcp", net.JoinHostPort(addr, fmt.Sprintf("%d", port)))
- if err != nil {
- return nil, fmt.Errorf("invalid remote address: %s", err)
- }
- laddr, err := net.ResolveTCPAddr("tcp", d.LocalAddr.String())
- if err != nil {
- return nil, fmt.Errorf("invalid local address: %s", err)
- }
-
- dialer := net.Dialer{LocalAddr: laddr, Timeout: d.Timeout}
- conn, err := dialer.Dial("tcp", raddr.String())
- if err != nil {
- return nil, err
- }
- return conn.(*net.TCPConn), nil
-}
diff --git a/server/sockopt_stub.go b/server/sockopt_stub.go
deleted file mode 100644
index 9e888dc5..00000000
--- a/server/sockopt_stub.go
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (C) 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.
-// +build !linux,!dragonfly,!freebsd,!netbsd,!openbsd,!darwin
-
-package server
-
-import (
- "fmt"
- "net"
-)
-
-func setTcpMD5SigSockopt(l *net.TCPListener, address string, key string) error {
- return fmt.Errorf("setting md5 is not supported")
-}
-
-func setListenTcpTTLSockopt(l *net.TCPListener, ttl int) error {
- return fmt.Errorf("setting ttl is not supported")
-}
-
-func setTcpTTLSockopt(conn *net.TCPConn, ttl int) error {
- return fmt.Errorf("setting ttl is not supported")
-}
-
-func setTcpMinTTLSockopt(conn *net.TCPConn, ttl int) error {
- return fmt.Errorf("setting min ttl is not supported")
-}
diff --git a/server/util.go b/server/util.go
deleted file mode 100644
index b122fbdb..00000000
--- a/server/util.go
+++ /dev/null
@@ -1,117 +0,0 @@
-// Copyright (C) 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 (
- "net"
- "os"
- "strings"
- "syscall"
-
- "github.com/eapache/channels"
-
- "github.com/osrg/gobgp/packet/bgp"
-)
-
-func cleanInfiniteChannel(ch *channels.InfiniteChannel) {
- ch.Close()
- // drain all remaining items
- for range ch.Out() {
- }
-}
-
-// Returns the binary formatted Administrative Shutdown Communication from the
-// given string value.
-func newAdministrativeCommunication(communication string) (data []byte) {
- if communication == "" {
- return nil
- }
- com := []byte(communication)
- if len(com) > bgp.BGP_ERROR_ADMINISTRATIVE_COMMUNICATION_MAX {
- data = []byte{bgp.BGP_ERROR_ADMINISTRATIVE_COMMUNICATION_MAX}
- data = append(data, com[:bgp.BGP_ERROR_ADMINISTRATIVE_COMMUNICATION_MAX]...)
- } else {
- data = []byte{byte(len(com))}
- data = append(data, com...)
- }
- return data
-}
-
-// Parses the given NOTIFICATION message data as a binary value and returns
-// the Administrative Shutdown Communication in string and the rest binary.
-func decodeAdministrativeCommunication(data []byte) (string, []byte) {
- if len(data) == 0 {
- return "", data
- }
- communicationLen := int(data[0])
- if communicationLen > bgp.BGP_ERROR_ADMINISTRATIVE_COMMUNICATION_MAX {
- communicationLen = bgp.BGP_ERROR_ADMINISTRATIVE_COMMUNICATION_MAX
- }
- if communicationLen > len(data)+1 {
- communicationLen = len(data) + 1
- }
- return string(data[1 : communicationLen+1]), data[communicationLen+1:]
-}
-
-func extractFileAndFamilyFromTCPListener(l *net.TCPListener) (*os.File, int, error) {
- // Note #1: TCPListener.File() has the unexpected side-effect of putting
- // the original socket into blocking mode. See Note #2.
- fi, err := l.File()
- if err != nil {
- return nil, 0, err
- }
-
- // Note #2: Call net.FileListener() to put the original socket back into
- // non-blocking mode.
- fl, err := net.FileListener(fi)
- if err != nil {
- fi.Close()
- return nil, 0, err
- }
- fl.Close()
-
- family := syscall.AF_INET
- if strings.Contains(l.Addr().String(), "[") {
- family = syscall.AF_INET6
- }
-
- return fi, family, nil
-}
-
-func extractFileAndFamilyFromTCPConn(conn *net.TCPConn) (*os.File, int, error) {
- // Note #1: TCPConn.File() has the unexpected side-effect of putting
- // the original socket into blocking mode. See Note #2.
- fi, err := conn.File()
- if err != nil {
- return nil, 0, err
- }
-
- // Note #2: Call net.FileConn() to put the original socket back into
- // non-blocking mode.
- fc, err := net.FileConn(fi)
- if err != nil {
- fi.Close()
- return nil, 0, err
- }
- fc.Close()
-
- family := syscall.AF_INET
- if strings.Contains(conn.RemoteAddr().String(), "[") {
- family = syscall.AF_INET6
- }
-
- return fi, family, nil
-}
diff --git a/server/zclient.go b/server/zclient.go
deleted file mode 100644
index 37281f7f..00000000
--- a/server/zclient.go
+++ /dev/null
@@ -1,450 +0,0 @@
-// Copyright (C) 2015 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"
- "math"
- "net"
- "strconv"
- "strings"
- "syscall"
- "time"
-
- log "github.com/sirupsen/logrus"
-
- "github.com/osrg/gobgp/packet/bgp"
- "github.com/osrg/gobgp/table"
- "github.com/osrg/gobgp/zebra"
-)
-
-// nexthopStateCache stores a map of nexthop IP to metric value. Especially,
-// the metric value of math.MaxUint32 means the nexthop is unreachable.
-type nexthopStateCache map[string]uint32
-
-func (m nexthopStateCache) applyToPathList(paths []*table.Path) []*table.Path {
- updated := make([]*table.Path, 0, len(paths))
- for _, path := range paths {
- if path == nil || path.IsWithdraw {
- continue
- }
- metric, ok := m[path.GetNexthop().String()]
- if !ok {
- continue
- }
- isNexthopInvalid := metric == math.MaxUint32
- med, err := path.GetMed()
- if err == nil && med == metric && path.IsNexthopInvalid == isNexthopInvalid {
- // If the nexthop state of the given path is already up to date,
- // skips this path.
- continue
- }
- newPath := path.Clone(false)
- if isNexthopInvalid {
- newPath.IsNexthopInvalid = true
- } else {
- newPath.IsNexthopInvalid = false
- newPath.SetMed(int64(metric), true)
- }
- updated = append(updated, newPath)
- }
- return updated
-}
-
-func (m nexthopStateCache) updateByNexthopUpdate(body *zebra.NexthopUpdateBody) (updated bool) {
- if len(body.Nexthops) == 0 {
- // If NEXTHOP_UPDATE message does not contain any nexthop, the given
- // nexthop is unreachable.
- if _, ok := m[body.Prefix.String()]; !ok {
- // Zebra will send an empty NEXTHOP_UPDATE message as the fist
- // response for the NEXTHOP_REGISTER message. Here ignores it.
- return false
- }
- m[body.Prefix.String()] = math.MaxUint32 // means unreachable
- } else {
- m[body.Prefix.String()] = body.Metric
- }
- return true
-}
-
-func (m nexthopStateCache) filterPathToRegister(paths []*table.Path) []*table.Path {
- filteredPaths := make([]*table.Path, 0, len(paths))
- for _, path := range paths {
- // Here filters out:
- // - Nil path
- // - Withdrawn path
- // - External path (advertised from Zebra) in order avoid sending back
- // - Unspecified nexthop address
- // - Already registered nexthop
- if path == nil || path.IsWithdraw || path.IsFromExternal() {
- continue
- } else if nexthop := path.GetNexthop(); nexthop.IsUnspecified() {
- continue
- } else if _, ok := m[nexthop.String()]; ok {
- continue
- }
- filteredPaths = append(filteredPaths, path)
- }
- return filteredPaths
-}
-
-func filterOutExternalPath(paths []*table.Path) []*table.Path {
- filteredPaths := make([]*table.Path, 0, len(paths))
- for _, path := range paths {
- // Here filters out:
- // - Nil path
- // - External path (advertised from Zebra) in order avoid sending back
- // - Unreachable path because invalidated by Zebra
- if path == nil || path.IsFromExternal() || path.IsNexthopInvalid {
- continue
- }
- filteredPaths = append(filteredPaths, path)
- }
- return filteredPaths
-}
-
-func newIPRouteBody(dst []*table.Path) (body *zebra.IPRouteBody, isWithdraw bool) {
- paths := filterOutExternalPath(dst)
- if len(paths) == 0 {
- return nil, false
- }
- path := paths[0]
-
- l := strings.SplitN(path.GetNlri().String(), "/", 2)
- var prefix net.IP
- nexthops := make([]net.IP, 0, len(paths))
- switch path.GetRouteFamily() {
- case bgp.RF_IPv4_UC, bgp.RF_IPv4_VPN:
- if path.GetRouteFamily() == bgp.RF_IPv4_UC {
- prefix = path.GetNlri().(*bgp.IPAddrPrefix).IPAddrPrefixDefault.Prefix.To4()
- } else {
- prefix = path.GetNlri().(*bgp.LabeledVPNIPAddrPrefix).IPAddrPrefixDefault.Prefix.To4()
- }
- for _, p := range paths {
- nexthops = append(nexthops, p.GetNexthop().To4())
- }
- case bgp.RF_IPv6_UC, bgp.RF_IPv6_VPN:
- if path.GetRouteFamily() == bgp.RF_IPv6_UC {
- prefix = path.GetNlri().(*bgp.IPv6AddrPrefix).IPAddrPrefixDefault.Prefix.To16()
- } else {
- prefix = path.GetNlri().(*bgp.LabeledVPNIPv6AddrPrefix).IPAddrPrefixDefault.Prefix.To16()
- }
- for _, p := range paths {
- nexthops = append(nexthops, p.GetNexthop().To16())
- }
- default:
- return nil, false
- }
- msgFlags := zebra.MESSAGE_NEXTHOP
- plen, _ := strconv.ParseUint(l[1], 10, 8)
- med, err := path.GetMed()
- if err == nil {
- msgFlags |= zebra.MESSAGE_METRIC
- }
- var flags zebra.FLAG
- info := path.GetSource()
- if info.AS == info.LocalAS {
- flags = zebra.FLAG_IBGP | zebra.FLAG_INTERNAL
- } else if info.MultihopTtl > 0 {
- flags = zebra.FLAG_INTERNAL
- }
- return &zebra.IPRouteBody{
- Type: zebra.ROUTE_BGP,
- Flags: flags,
- SAFI: zebra.SAFI_UNICAST,
- Message: msgFlags,
- Prefix: prefix,
- PrefixLength: uint8(plen),
- Nexthops: nexthops,
- Metric: med,
- }, path.IsWithdraw
-}
-
-func newNexthopRegisterBody(paths []*table.Path, nexthopCache nexthopStateCache) *zebra.NexthopRegisterBody {
- paths = nexthopCache.filterPathToRegister(paths)
- if len(paths) == 0 {
- return nil
- }
- path := paths[0]
-
- family := path.GetRouteFamily()
- nexthops := make([]*zebra.RegisteredNexthop, 0, len(paths))
- for _, p := range paths {
- nexthop := p.GetNexthop()
- var nh *zebra.RegisteredNexthop
- switch family {
- case bgp.RF_IPv4_UC, bgp.RF_IPv4_VPN:
- nh = &zebra.RegisteredNexthop{
- Family: syscall.AF_INET,
- Prefix: nexthop.To4(),
- }
- case bgp.RF_IPv6_UC, bgp.RF_IPv6_VPN:
- nh = &zebra.RegisteredNexthop{
- Family: syscall.AF_INET6,
- Prefix: nexthop.To16(),
- }
- default:
- continue
- }
- nexthops = append(nexthops, nh)
- }
-
- // If no nexthop needs to be registered or unregistered, skips to send
- // message.
- if len(nexthops) == 0 {
- return nil
- }
-
- return &zebra.NexthopRegisterBody{
- Nexthops: nexthops,
- }
-}
-
-func newNexthopUnregisterBody(family uint16, prefix net.IP) *zebra.NexthopRegisterBody {
- return &zebra.NexthopRegisterBody{
- Nexthops: []*zebra.RegisteredNexthop{{
- Family: family,
- Prefix: prefix,
- }},
- }
-}
-
-func newPathFromIPRouteMessage(m *zebra.Message) *table.Path {
- header := m.Header
- body := m.Body.(*zebra.IPRouteBody)
- family := body.RouteFamily()
- isWithdraw := body.IsWithdraw()
-
- var nlri bgp.AddrPrefixInterface
- pattr := make([]bgp.PathAttributeInterface, 0)
- origin := bgp.NewPathAttributeOrigin(bgp.BGP_ORIGIN_ATTR_TYPE_IGP)
- pattr = append(pattr, origin)
-
- log.WithFields(log.Fields{
- "Topic": "Zebra",
- "RouteType": body.Type.String(),
- "Flag": body.Flags.String(),
- "Message": body.Message,
- "Prefix": body.Prefix,
- "PrefixLength": body.PrefixLength,
- "Nexthop": body.Nexthops,
- "IfIndex": body.Ifindexs,
- "Metric": body.Metric,
- "Distance": body.Distance,
- "Mtu": body.Mtu,
- "api": header.Command.String(),
- }).Debugf("create path from ip route message.")
-
- switch family {
- case bgp.RF_IPv4_UC:
- nlri = bgp.NewIPAddrPrefix(body.PrefixLength, body.Prefix.String())
- if len(body.Nexthops) > 0 {
- pattr = append(pattr, bgp.NewPathAttributeNextHop(body.Nexthops[0].String()))
- }
- case bgp.RF_IPv6_UC:
- nlri = bgp.NewIPv6AddrPrefix(body.PrefixLength, body.Prefix.String())
- nexthop := ""
- if len(body.Nexthops) > 0 {
- nexthop = body.Nexthops[0].String()
- }
- pattr = append(pattr, bgp.NewPathAttributeMpReachNLRI(nexthop, []bgp.AddrPrefixInterface{nlri}))
- default:
- log.WithFields(log.Fields{
- "Topic": "Zebra",
- }).Errorf("unsupport address family: %s", family)
- return nil
- }
-
- med := bgp.NewPathAttributeMultiExitDisc(body.Metric)
- pattr = append(pattr, med)
-
- path := table.NewPath(nil, nlri, isWithdraw, pattr, time.Now(), false)
- path.SetIsFromExternal(true)
- return path
-}
-
-type zebraClient struct {
- client *zebra.Client
- server *BgpServer
- nexthopCache nexthopStateCache
- dead chan struct{}
-}
-
-func (z *zebraClient) getPathListWithNexthopUpdate(body *zebra.NexthopUpdateBody) []*table.Path {
- rib := &table.TableManager{
- Tables: make(map[bgp.RouteFamily]*table.Table),
- }
-
- var rfList []bgp.RouteFamily
- switch body.Family {
- case uint16(syscall.AF_INET):
- rfList = []bgp.RouteFamily{bgp.RF_IPv4_UC, bgp.RF_IPv4_VPN}
- case uint16(syscall.AF_INET6):
- rfList = []bgp.RouteFamily{bgp.RF_IPv6_UC, bgp.RF_IPv6_VPN}
- }
-
- for _, rf := range rfList {
- tbl, _, err := z.server.GetRib("", rf, nil)
- if err != nil {
- log.WithFields(log.Fields{
- "Topic": "Zebra",
- "Family": rf.String(),
- "Error": err,
- }).Error("failed to get global rib")
- continue
- }
- rib.Tables[rf] = tbl
- }
-
- return rib.GetPathListWithNexthop(table.GLOBAL_RIB_NAME, rfList, body.Prefix)
-}
-
-func (z *zebraClient) updatePathByNexthopCache(paths []*table.Path) {
- paths = z.nexthopCache.applyToPathList(paths)
- if len(paths) > 0 {
- if err := z.server.UpdatePath("", paths); err != nil {
- log.WithFields(log.Fields{
- "Topic": "Zebra",
- "PathList": paths,
- }).Error("failed to update nexthop reachability")
- }
- }
-}
-
-func (z *zebraClient) loop() {
- w := z.server.Watch([]WatchOption{
- WatchBestPath(true),
- WatchPostUpdate(true),
- }...)
- defer w.Stop()
-
- for {
- select {
- case <-z.dead:
- return
- case msg := <-z.client.Receive():
- switch body := msg.Body.(type) {
- case *zebra.IPRouteBody:
- if path := newPathFromIPRouteMessage(msg); path != nil {
- if _, err := z.server.AddPath("", []*table.Path{path}); err != nil {
- log.WithFields(log.Fields{
- "Topic": "Zebra",
- "Path": path,
- "Error": err,
- }).Error("failed to add path from zebra")
- }
- }
- case *zebra.NexthopUpdateBody:
- if updated := z.nexthopCache.updateByNexthopUpdate(body); !updated {
- continue
- }
- paths := z.getPathListWithNexthopUpdate(body)
- if len(paths) == 0 {
- // If there is no path bound for the given nexthop, send
- // NEXTHOP_UNREGISTER message.
- z.client.SendNexthopRegister(msg.Header.VrfId, newNexthopUnregisterBody(body.Family, body.Prefix), true)
- delete(z.nexthopCache, body.Prefix.String())
- }
- z.updatePathByNexthopCache(paths)
- }
- case ev := <-w.Event():
- switch msg := ev.(type) {
- case *WatchEventBestPath:
- if table.UseMultiplePaths.Enabled {
- for _, paths := range msg.MultiPathList {
- z.updatePathByNexthopCache(paths)
- if body, isWithdraw := newIPRouteBody(paths); body != nil {
- z.client.SendIPRoute(0, body, isWithdraw)
- }
- if body := newNexthopRegisterBody(paths, z.nexthopCache); body != nil {
- z.client.SendNexthopRegister(0, body, false)
- }
- }
- } else {
- z.updatePathByNexthopCache(msg.PathList)
- for _, path := range msg.PathList {
- vrfs := []uint16{0}
- if msg.Vrf != nil {
- if v, ok := msg.Vrf[path.GetNlri().String()]; ok {
- vrfs = append(vrfs, v)
- }
- }
- for _, i := range vrfs {
- if body, isWithdraw := newIPRouteBody([]*table.Path{path}); body != nil {
- z.client.SendIPRoute(i, body, isWithdraw)
- }
- if body := newNexthopRegisterBody([]*table.Path{path}, z.nexthopCache); body != nil {
- z.client.SendNexthopRegister(i, body, false)
- }
- }
- }
- }
- case *WatchEventUpdate:
- if body := newNexthopRegisterBody(msg.PathList, z.nexthopCache); body != nil {
- vrfID := uint16(0)
- for _, vrf := range z.server.GetVrf() {
- if vrf.Name == msg.Neighbor.Config.Vrf {
- vrfID = uint16(vrf.Id)
- }
- }
- z.client.SendNexthopRegister(vrfID, body, false)
- }
- }
- }
- }
-}
-
-func newZebraClient(s *BgpServer, url string, protos []string, version uint8, nhtEnable bool, nhtDelay uint8) (*zebraClient, error) {
- l := strings.SplitN(url, ":", 2)
- if len(l) != 2 {
- return nil, fmt.Errorf("unsupported url: %s", url)
- }
- var cli *zebra.Client
- var err error
- for _, ver := range []uint8{version, 2, 3, 4} {
- cli, err = zebra.NewClient(l[0], l[1], zebra.ROUTE_BGP, ver)
- if err == nil {
- break
- }
- // Retry with another Zebra message version
- log.WithFields(log.Fields{
- "Topic": "Zebra",
- }).Warnf("cannot connect to Zebra with message version %d. going to retry another version...", ver)
- }
- if cli == nil {
- return nil, err
- }
- // Note: HELLO/ROUTER_ID_ADD messages are automatically sent to negotiate
- // the Zebra message version in zebra.NewClient().
- // cli.SendHello()
- // cli.SendRouterIDAdd()
- cli.SendInterfaceAdd()
- for _, typ := range protos {
- t, err := zebra.RouteTypeFromString(typ)
- if err != nil {
- return nil, err
- }
- cli.SendRedistribute(t, zebra.VRF_DEFAULT)
- }
- w := &zebraClient{
- client: cli,
- server: s,
- nexthopCache: make(nexthopStateCache),
- dead: make(chan struct{}),
- }
- go w.loop()
- return w, nil
-}
diff --git a/server/zclient_test.go b/server/zclient_test.go
deleted file mode 100644
index 46f40539..00000000
--- a/server/zclient_test.go
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright (C) 2014 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 (
- "net"
- "testing"
- "time"
-
- "github.com/stretchr/testify/assert"
-
- "github.com/osrg/gobgp/table"
- "github.com/osrg/gobgp/zebra"
-)
-
-func Test_newPathFromIPRouteMessage(t *testing.T) {
- assert := assert.New(t)
-
- // IPv4 Route Add
- m := &zebra.Message{}
- h := &zebra.Header{
- Len: zebra.HeaderSize(2),
- Marker: zebra.HEADER_MARKER,
- Version: 2,
- Command: zebra.IPV4_ROUTE_ADD,
- }
- b := &zebra.IPRouteBody{
- Type: zebra.ROUTE_TYPE(zebra.ROUTE_STATIC),
- Flags: zebra.FLAG(zebra.FLAG_SELECTED),
- Message: zebra.MESSAGE_NEXTHOP | zebra.MESSAGE_DISTANCE | zebra.MESSAGE_METRIC | zebra.MESSAGE_MTU,
- SAFI: zebra.SAFI(zebra.SAFI_UNICAST),
- Prefix: net.ParseIP("192.168.100.0"),
- PrefixLength: uint8(24),
- Nexthops: []net.IP{net.ParseIP("0.0.0.0")},
- Ifindexs: []uint32{1},
- Distance: uint8(0),
- Metric: uint32(100),
- Mtu: uint32(0),
- Api: zebra.API_TYPE(zebra.IPV4_ROUTE_ADD),
- }
- m.Header = *h
- m.Body = b
-
- path := newPathFromIPRouteMessage(m)
- pp := table.NewPath(nil, path.GetNlri(), path.IsWithdraw, path.GetPathAttrs(), time.Now(), false)
- pp.SetIsFromExternal(path.IsFromExternal())
- assert.Equal("0.0.0.0", pp.GetNexthop().String())
- assert.Equal("192.168.100.0/24", pp.GetNlri().String())
- assert.True(pp.IsFromExternal())
- assert.False(pp.IsWithdraw)
-
- // IPv4 Route Delete
- h.Command = zebra.IPV4_ROUTE_DELETE
- b.Api = zebra.IPV4_ROUTE_DELETE
- m.Header = *h
- m.Body = b
-
- path = newPathFromIPRouteMessage(m)
- pp = table.NewPath(nil, path.GetNlri(), path.IsWithdraw, path.GetPathAttrs(), time.Now(), false)
- pp.SetIsFromExternal(path.IsFromExternal())
- assert.Equal("0.0.0.0", pp.GetNexthop().String())
- assert.Equal("192.168.100.0/24", pp.GetNlri().String())
- med, _ := pp.GetMed()
- assert.Equal(uint32(100), med)
- assert.True(pp.IsFromExternal())
- assert.True(pp.IsWithdraw)
-
- // IPv6 Route Add
- h.Command = zebra.IPV6_ROUTE_ADD
- b.Api = zebra.IPV6_ROUTE_ADD
- b.Prefix = net.ParseIP("2001:db8:0:f101::")
- b.PrefixLength = uint8(64)
- b.Nexthops = []net.IP{net.ParseIP("::")}
- m.Header = *h
- m.Body = b
-
- path = newPathFromIPRouteMessage(m)
- pp = table.NewPath(nil, path.GetNlri(), path.IsWithdraw, path.GetPathAttrs(), time.Now(), false)
- pp.SetIsFromExternal(path.IsFromExternal())
- assert.Equal("::", pp.GetNexthop().String())
- assert.Equal("2001:db8:0:f101::/64", pp.GetNlri().String())
- med, _ = pp.GetMed()
- assert.Equal(uint32(100), med)
- assert.True(pp.IsFromExternal())
- assert.False(pp.IsWithdraw)
-
- // IPv6 Route Delete
- h.Command = zebra.IPV6_ROUTE_DELETE
- b.Api = zebra.IPV6_ROUTE_DELETE
- m.Header = *h
- m.Body = b
-
- path = newPathFromIPRouteMessage(m)
- pp = table.NewPath(nil, path.GetNlri(), path.IsWithdraw, path.GetPathAttrs(), time.Now(), false)
- pp.SetIsFromExternal(path.IsFromExternal())
- assert.Equal("::", pp.GetNexthop().String())
- assert.Equal("2001:db8:0:f101::/64", pp.GetNlri().String())
- assert.True(pp.IsFromExternal())
- assert.True(pp.IsWithdraw)
-}