diff options
author | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2018-07-07 13:48:38 +0900 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2018-07-07 20:44:25 +0900 |
commit | c4775c42510d1f1ddd55036dc19e982712fa6a0b (patch) | |
tree | 6ec8b61d4338c809e239e3003a2d32d480898e22 /server | |
parent | b3079759aa13172fcb548a83da9a9653d8d5fed4 (diff) |
follow Standard Go Project Layout
https://github.com/golang-standards/project-layout
Now you can see clearly what are private and public library code.
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
Diffstat (limited to 'server')
-rw-r--r-- | server/bmp.go | 358 | ||||
-rw-r--r-- | server/collector.go | 221 | ||||
-rw-r--r-- | server/fsm.go | 1752 | ||||
-rw-r--r-- | server/fsm_test.go | 344 | ||||
-rw-r--r-- | server/grpc_server.go | 3016 | ||||
-rw-r--r-- | server/mrt.go | 409 | ||||
-rw-r--r-- | server/peer.go | 522 | ||||
-rw-r--r-- | server/rpki.go | 712 | ||||
-rw-r--r-- | server/rpki_test.go | 258 | ||||
-rw-r--r-- | server/server.go | 3048 | ||||
-rw-r--r-- | server/server_test.go | 707 | ||||
-rw-r--r-- | server/sockopt.go | 90 | ||||
-rw-r--r-- | server/sockopt_bsd.go | 89 | ||||
-rw-r--r-- | server/sockopt_darwin.go | 64 | ||||
-rw-r--r-- | server/sockopt_linux.go | 282 | ||||
-rw-r--r-- | server/sockopt_linux_test.go | 106 | ||||
-rw-r--r-- | server/sockopt_openbsd.go | 469 | ||||
-rw-r--r-- | server/sockopt_stub.go | 38 | ||||
-rw-r--r-- | server/util.go | 117 | ||||
-rw-r--r-- | server/zclient.go | 450 | ||||
-rw-r--r-- | server/zclient_test.go | 113 |
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) -} |