func (peer *Peer) handleRouteRefresh(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} peer.adjRibOut.Drop(rfList) accepted, filtered := peer.getBestFromLocal(rfList) peer.adjRibOut.Update(accepted) for _, path := range filtered { path.IsWithdraw = true accepted = append(accepted, path) } return accepted }
func (path *Path) ToApiStruct(id string) *api.Path { nlri := path.GetNlri() n, _ := nlri.Serialize() family := uint32(bgp.AfiSafiToRouteFamily(nlri.AFI(), nlri.SAFI())) pattrs := func(arg []bgp.PathAttributeInterface) [][]byte { ret := make([][]byte, 0, len(arg)) for _, a := range arg { aa, _ := a.Serialize() ret = append(ret, aa) } return ret }(path.GetPathAttrs()) return &api.Path{ Nlri: n, Pattrs: pattrs, Age: path.OriginInfo().timestamp.Unix(), IsWithdraw: path.IsWithdraw, Validation: int32(path.OriginInfo().validation.ToInt()), Filtered: path.Filtered(id) == POLICY_DIRECTION_IN, Family: family, SourceAsn: path.OriginInfo().source.AS, SourceId: path.OriginInfo().source.ID.String(), NeighborIp: path.OriginInfo().source.Address.String(), Stale: path.IsStale(), IsFromExternal: path.OriginInfo().isFromExternal, } }
func ToPathApi(path *table.Path) *Path { nlri := path.GetNlri() n, _ := nlri.Serialize() family := uint32(bgp.AfiSafiToRouteFamily(nlri.AFI(), nlri.SAFI())) pattrs := func(arg []bgp.PathAttributeInterface) [][]byte { ret := make([][]byte, 0, len(arg)) for _, a := range arg { aa, _ := a.Serialize() ret = append(ret, aa) } return ret }(path.GetPathAttrs()) return &Path{ Nlri: n, Pattrs: pattrs, Age: path.GetTimestamp().Unix(), IsWithdraw: path.IsWithdraw, Validation: int32(path.Validation().ToInt()), Filtered: path.Filtered("") == table.POLICY_DIRECTION_IN, Family: family, SourceAsn: path.GetSource().AS, SourceId: path.GetSource().ID.String(), NeighborIp: path.GetSource().Address.String(), Stale: path.IsStale(), IsFromExternal: path.IsFromExternal(), } }
func (u *Rib) Serialize() ([]byte, error) { buf := make([]byte, 4) binary.BigEndian.PutUint32(buf, u.SequenceNumber) rf := bgp.AfiSafiToRouteFamily(u.Prefix.AFI(), u.Prefix.SAFI()) switch rf { case bgp.RF_IPv4_UC, bgp.RF_IPv4_MC, bgp.RF_IPv6_UC, bgp.RF_IPv6_MC: default: bbuf := make([]byte, 0, 2) binary.BigEndian.PutUint16(bbuf, u.Prefix.AFI()) buf = append(buf, bbuf...) buf = append(buf, u.Prefix.SAFI()) } bbuf, err := u.Prefix.Serialize() if err != nil { return nil, err } buf = append(buf, bbuf...) bbuf, err = packValues([]interface{}{uint16(len(u.Entries))}) if err != nil { return nil, err } buf = append(buf, bbuf...) for _, entry := range u.Entries { bbuf, err = entry.Serialize() if err != nil { return nil, err } buf = append(buf, bbuf...) } return buf, nil }
func NewRib(seq uint32, prefix bgp.AddrPrefixInterface, entries []*RibEntry) *Rib { rf := bgp.AfiSafiToRouteFamily(prefix.AFI(), prefix.SAFI()) return &Rib{ SequenceNumber: seq, Prefix: prefix, Entries: entries, RouteFamily: rf, } }
func NewDestination(nlri bgp.AddrPrefixInterface) *Destination { d := &Destination{ routeFamily: bgp.AfiSafiToRouteFamily(nlri.AFI(), nlri.SAFI()), nlri: nlri, knownPathList: make([]*Path, 0), withdrawList: make([]*Path, 0), newPathList: make([]*Path, 0), } switch d.routeFamily { case bgp.RF_IPv4_UC, bgp.RF_IPv6_UC: d.RadixKey = CidrToRadixkey(nlri.String()) } return d }
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, FSM_DYING case conn, ok := <-fsm.connCh: if !ok { break } conn.Close() log.WithFields(log.Fields{ "Topic": "Peer", "Key": fsm.pConf.Config.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.Config.NeighborAddress, "State": fsm.state.String(), }).Warn("graceful restart timer expired") return bgp.BGP_FSM_IDLE, FSM_RESTART_TIMER_EXPIRED } 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) err := bgp.ValidateOpenMsg(body, fsm.pConf.Config.PeerAs) if err != nil { fsm.sendNotificationFromErrorMsg(err.(*bgp.MessageError)) return bgp.BGP_FSM_IDLE, FSM_INVALID_MSG } fsm.peerInfo.ID = body.ID fsm.capMap, fsm.rfMap = open2Cap(body, fsm.pConf) // 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 { log.WithFields(log.Fields{ "Topic": "Peer", "Key": fsm.pConf.Config.NeighborAddress, "State": fsm.state.String(), }).Warn("restart flag is not set") // send notification? h.conn.Close() return bgp.BGP_FSM_IDLE, FSM_INVALID_MSG } } msg := bgp.NewBGPKeepAliveMessage() b, _ := msg.Serialize() fsm.conn.Write(b) fsm.bgpMessageStateUpdate(msg.Header.Type, false) return bgp.BGP_FSM_OPENCONFIRM, FSM_OPEN_MSG_RECEIVED } else { // send notification? h.conn.Close() return bgp.BGP_FSM_IDLE, FSM_INVALID_MSG } case *bgp.MessageError: fsm.sendNotificationFromErrorMsg(e.MsgData.(*bgp.MessageError)) return bgp.BGP_FSM_IDLE, FSM_INVALID_MSG default: log.WithFields(log.Fields{ "Topic": "Peer", "Key": fsm.pConf.Config.NeighborAddress, "State": fsm.state.String(), "Data": e.MsgData, }).Panic("unknown msg type") } case err := <-h.errorCh: h.conn.Close() return bgp.BGP_FSM_IDLE, err case <-holdTimer.C: fsm.sendNotification(bgp.BGP_ERROR_HOLD_TIMER_EXPIRED, 0, nil, "hold timer expired") h.t.Kill(nil) return bgp.BGP_FSM_IDLE, FSM_HOLD_TIMER_EXPIRED case s := <-fsm.adminStateCh: err := h.changeAdminState(s) if err == nil { switch s { case ADMIN_STATE_DOWN: h.conn.Close() return bgp.BGP_FSM_IDLE, FSM_ADMIN_DOWN case ADMIN_STATE_UP: log.WithFields(log.Fields{ "Topic": "Peer", "Key": fsm.pConf.Config.NeighborAddress, "State": fsm.state.String(), "AdminState": s.String(), }).Panic("code logic bug") } } } } }
func showNeighbor(args []string) error { p, e := getNeighbor(args[0]) if e != nil { return e } if globalOpts.Json { j, _ := json.Marshal(p) fmt.Println(string(j)) return nil } fmt.Printf("BGP neighbor is %s, remote AS %d\n", p.Conf.RemoteIp, p.Conf.RemoteAs) id := "unknown" if p.Conf.Id != nil { id = p.Conf.Id.String() } fmt.Printf(" BGP version 4, remote router ID %s\n", id) fmt.Printf(" BGP state = %s, up for %s\n", p.Info.BgpState, formatTimedelta(int64(p.Timers.State.Uptime)-time.Now().Unix())) fmt.Printf(" BGP OutQ = %d, Flops = %d\n", p.Info.OutQ, p.Info.Flops) fmt.Printf(" Hold time is %d, keepalive interval is %d seconds\n", p.Timers.State.NegotiatedHoldTime, p.Timers.State.KeepaliveInterval) fmt.Printf(" Configured hold time is %d, keepalive interval is %d seconds\n", p.Timers.Config.HoldTime, p.Timers.Config.KeepaliveInterval) fmt.Printf(" Neighbor capabilities:\n") caps := capabilities{} lookup := func(val bgp.ParameterCapabilityInterface, l capabilities) bgp.ParameterCapabilityInterface { for _, v := range l { if v.Code() == val.Code() { if v.Code() == bgp.BGP_CAP_MULTIPROTOCOL { lhs := v.(*bgp.CapMultiProtocol).CapValue rhs := val.(*bgp.CapMultiProtocol).CapValue if lhs == rhs { return v } continue } return v } } return nil } for _, c := range p.Conf.LocalCap { caps = append(caps, c) } for _, c := range p.Conf.RemoteCap { if lookup(c, caps) == nil { caps = append(caps, c) } } sort.Sort(caps) firstMp := true for _, c := range caps { support := "" if m := lookup(c, p.Conf.LocalCap); m != nil { support += "advertised" } if lookup(c, p.Conf.RemoteCap) != nil { if len(support) != 0 { support += " and " } support += "received" } switch c.Code() { case bgp.BGP_CAP_MULTIPROTOCOL: if firstMp { fmt.Printf(" %s:\n", c.Code()) firstMp = false } m := c.(*bgp.CapMultiProtocol).CapValue fmt.Printf(" %s:\t%s\n", m, support) case bgp.BGP_CAP_GRACEFUL_RESTART: fmt.Printf(" %s:\t%s\n", c.Code(), support) grStr := func(g *bgp.CapGracefulRestart) string { str := "" if len(g.Tuples) > 0 { str += fmt.Sprintf("restart time %d sec", g.Time) } if g.Flags == 0x08 { if len(str) > 0 { str += ", " } str += "restart flag set" } if len(str) > 0 { str += "\n" } for _, t := range g.Tuples { str += fmt.Sprintf(" %s", bgp.AfiSafiToRouteFamily(t.AFI, t.SAFI)) if t.Flags == 0x80 { str += ", forward flag set" } str += "\n" } return str } if m := lookup(c, p.Conf.LocalCap); m != nil { g := m.(*bgp.CapGracefulRestart) if s := grStr(g); len(s) > 0 { fmt.Printf(" Local: %s", s) } } if m := lookup(c, p.Conf.RemoteCap); m != nil { g := m.(*bgp.CapGracefulRestart) if s := grStr(g); len(s) > 0 { fmt.Printf(" Remote: %s", s) } } default: fmt.Printf(" %s:\t%s\n", c.Code(), support) } } fmt.Print(" Message statistics:\n") fmt.Print(" Sent Rcvd\n") fmt.Printf(" Opens: %10d %10d\n", p.Info.Messages.Sent.OPEN, p.Info.Messages.Received.OPEN) fmt.Printf(" Notifications: %10d %10d\n", p.Info.Messages.Sent.NOTIFICATION, p.Info.Messages.Received.NOTIFICATION) fmt.Printf(" Updates: %10d %10d\n", p.Info.Messages.Sent.UPDATE, p.Info.Messages.Received.UPDATE) fmt.Printf(" Keepalives: %10d %10d\n", p.Info.Messages.Sent.KEEPALIVE, p.Info.Messages.Received.KEEPALIVE) fmt.Printf(" Route Refesh: %10d %10d\n", p.Info.Messages.Sent.REFRESH, p.Info.Messages.Received.REFRESH) fmt.Printf(" Discarded: %10d %10d\n", p.Info.Messages.Sent.DISCARDED, p.Info.Messages.Received.DISCARDED) fmt.Printf(" Total: %10d %10d\n", p.Info.Messages.Sent.TOTAL, p.Info.Messages.Received.TOTAL) fmt.Print(" Route statistics:\n") fmt.Printf(" Advertised: %10d\n", p.Info.Advertised) fmt.Printf(" Received: %10d\n", p.Info.Received) fmt.Printf(" Accepted: %10d\n", p.Info.Accepted) if len(p.Conf.PrefixLimits) > 0 { fmt.Println(" Prefix Limits:") for _, c := range p.Conf.PrefixLimits { fmt.Printf(" %s:\tMaximum prefixes allowed %d", bgp.RouteFamily(c.Family), c.MaxPrefixes) if c.ShutdownThresholdPct > 0 { fmt.Printf(", Threshold for warning message %d%%\n", c.ShutdownThresholdPct) } else { fmt.Printf("\n") } } } return nil }
func (path *Path) GetRouteFamily() bgp.RouteFamily { return bgp.AfiSafiToRouteFamily(path.OriginInfo().nlri.AFI(), path.OriginInfo().nlri.SAFI()) }
func (s *Server) api2PathList(resource Resource, ApiPathList []*Path) ([]*table.Path, error) { var nlri bgp.AddrPrefixInterface var nexthop string var pi *table.PeerInfo pathList := make([]*table.Path, 0, len(ApiPathList)) for _, path := range ApiPathList { seen := make(map[bgp.BGPAttrType]bool) pattr := make([]bgp.PathAttributeInterface, 0) extcomms := make([]bgp.ExtendedCommunityInterface, 0) if path.SourceAsn != 0 { pi = &table.PeerInfo{ AS: path.SourceAsn, LocalID: net.ParseIP(path.SourceId), } } if len(path.Nlri) > 0 { nlri = &bgp.IPAddrPrefix{} err := nlri.DecodeFromBytes(path.Nlri) if err != nil { return nil, err } } for _, attr := range path.Pattrs { p, err := bgp.GetPathAttribute(attr) if err != nil { return nil, err } err = p.DecodeFromBytes(attr) if err != nil { return nil, err } if _, ok := seen[p.GetType()]; !ok { seen[p.GetType()] = true } else { return nil, fmt.Errorf("the path attribute apears twice. Type : " + strconv.Itoa(int(p.GetType()))) } switch p.GetType() { case bgp.BGP_ATTR_TYPE_NEXT_HOP: nexthop = p.(*bgp.PathAttributeNextHop).Value.String() case bgp.BGP_ATTR_TYPE_EXTENDED_COMMUNITIES: value := p.(*bgp.PathAttributeExtendedCommunities).Value if len(value) > 0 { extcomms = append(extcomms, value...) } case bgp.BGP_ATTR_TYPE_MP_REACH_NLRI: mpreach := p.(*bgp.PathAttributeMpReachNLRI) if len(mpreach.Value) != 1 { return nil, fmt.Errorf("include only one route in mp_reach_nlri") } nlri = mpreach.Value[0] nexthop = mpreach.Nexthop.String() default: pattr = append(pattr, p) } } if nlri == nil || nexthop == "" { return nil, fmt.Errorf("not found nlri or nexthop") } rf := bgp.AfiSafiToRouteFamily(nlri.AFI(), nlri.SAFI()) if resource != Resource_VRF && rf == bgp.RF_IPv4_UC { pattr = append(pattr, bgp.NewPathAttributeNextHop(nexthop)) } else { pattr = append(pattr, bgp.NewPathAttributeMpReachNLRI(nexthop, []bgp.AddrPrefixInterface{nlri})) } if len(extcomms) > 0 { pattr = append(pattr, bgp.NewPathAttributeExtendedCommunities(extcomms)) } newPath := table.NewPath(pi, nlri, path.IsWithdraw, pattr, time.Now(), path.NoImplicitWithdraw) newPath.SetIsFromExternal(path.IsFromExternal) pathList = append(pathList, newPath) } return pathList, nil }
func showNeighbor(args []string) error { p, e := getNeighbor(args[0]) if e != nil { return e } if globalOpts.Json { j, _ := json.Marshal(p) fmt.Println(string(j)) return nil } fmt.Printf("BGP neighbor is %s, remote AS %d", p.Config.NeighborAddress, p.Config.PeerAs) if p.RouteReflector.Config.RouteReflectorClient { fmt.Printf(", route-reflector-client\n") } else if p.RouteServer.Config.RouteServerClient { fmt.Printf(", route-server-client\n") } else { fmt.Printf("\n") } id := "unknown" if p.State.RemoteRouterId != "" { id = p.State.RemoteRouterId } fmt.Printf(" BGP version 4, remote router ID %s\n", id) fmt.Printf(" BGP state = %s, up for %s\n", p.State.SessionState, formatTimedelta(int64(p.Timers.State.Uptime)-time.Now().Unix())) fmt.Printf(" BGP OutQ = %d, Flops = %d\n", p.State.Queues.Output, p.State.Flops) fmt.Printf(" Hold time is %d, keepalive interval is %d seconds\n", int(p.Timers.State.NegotiatedHoldTime), int(p.Timers.State.KeepaliveInterval)) fmt.Printf(" Configured hold time is %d, keepalive interval is %d seconds\n", int(p.Timers.Config.HoldTime), int(p.Timers.Config.KeepaliveInterval)) fmt.Printf(" Neighbor capabilities:\n") caps := capabilities{} lookup := func(val bgp.ParameterCapabilityInterface, l capabilities) bgp.ParameterCapabilityInterface { for _, v := range l { if v.Code() == val.Code() { if v.Code() == bgp.BGP_CAP_MULTIPROTOCOL { lhs := v.(*bgp.CapMultiProtocol).CapValue rhs := val.(*bgp.CapMultiProtocol).CapValue if lhs == rhs { return v } continue } return v } } return nil } for _, c := range p.State.LocalCapabilityList { caps = append(caps, c) } for _, c := range p.State.RemoteCapabilityList { if lookup(c, caps) == nil { caps = append(caps, c) } } sort.Sort(caps) firstMp := true for _, c := range caps { support := "" if m := lookup(c, p.State.LocalCapabilityList); m != nil { support += "advertised" } if lookup(c, p.State.RemoteCapabilityList) != nil { if len(support) != 0 { support += " and " } support += "received" } switch c.Code() { case bgp.BGP_CAP_MULTIPROTOCOL: if firstMp { fmt.Printf(" %s:\n", c.Code()) firstMp = false } m := c.(*bgp.CapMultiProtocol).CapValue fmt.Printf(" %s:\t%s\n", m, support) case bgp.BGP_CAP_GRACEFUL_RESTART: fmt.Printf(" %s:\t%s\n", c.Code(), support) grStr := func(g *bgp.CapGracefulRestart) string { str := "" if len(g.Tuples) > 0 { str += fmt.Sprintf("restart time %d sec", g.Time) } if g.Flags&0x08 > 0 { if len(str) > 0 { str += ", " } str += "restart flag set" } if g.Flags&0x04 > 0 { if len(str) > 0 { str += ", " } str += "notification flag set" } if len(str) > 0 { str += "\n" } for _, t := range g.Tuples { str += fmt.Sprintf(" %s", bgp.AfiSafiToRouteFamily(t.AFI, t.SAFI)) if t.Flags == 0x80 { str += ", forward flag set" } str += "\n" } return str } if m := lookup(c, p.State.LocalCapabilityList); m != nil { g := m.(*bgp.CapGracefulRestart) if s := grStr(g); len(s) > 0 { fmt.Printf(" Local: %s", s) } } if m := lookup(c, p.State.LocalCapabilityList); m != nil { g := m.(*bgp.CapGracefulRestart) if s := grStr(g); len(s) > 0 { fmt.Printf(" Remote: %s", s) } } case bgp.BGP_CAP_LONG_LIVED_GRACEFUL_RESTART: fmt.Printf(" %s:\t%s\n", c.Code(), support) grStr := func(g *bgp.CapLongLivedGracefulRestart) string { var str string for _, t := range g.Tuples { str += fmt.Sprintf(" %s, restart time %d sec", bgp.AfiSafiToRouteFamily(t.AFI, t.SAFI), t.RestartTime) if t.Flags == 0x80 { str += ", forward flag set" } str += "\n" } return str } if m := lookup(c, p.State.LocalCapabilityList); m != nil { g := m.(*bgp.CapLongLivedGracefulRestart) if s := grStr(g); len(s) > 0 { fmt.Printf(" Local:\n%s", s) } } if m := lookup(c, p.State.LocalCapabilityList); m != nil { g := m.(*bgp.CapLongLivedGracefulRestart) if s := grStr(g); len(s) > 0 { fmt.Printf(" Remote:\n%s", s) } } default: fmt.Printf(" %s:\t%s\n", c.Code(), support) } } fmt.Print(" Message statistics:\n") fmt.Print(" Sent Rcvd\n") fmt.Printf(" Opens: %10d %10d\n", p.State.Messages.Sent.Open, p.State.Messages.Received.Open) fmt.Printf(" Notifications: %10d %10d\n", p.State.Messages.Sent.Notification, p.State.Messages.Received.Notification) fmt.Printf(" Updates: %10d %10d\n", p.State.Messages.Sent.Update, p.State.Messages.Received.Update) fmt.Printf(" Keepalives: %10d %10d\n", p.State.Messages.Sent.Keepalive, p.State.Messages.Received.Keepalive) fmt.Printf(" Route Refesh: %10d %10d\n", p.State.Messages.Sent.Refresh, p.State.Messages.Received.Refresh) fmt.Printf(" Discarded: %10d %10d\n", p.State.Messages.Sent.Discarded, p.State.Messages.Received.Discarded) fmt.Printf(" Total: %10d %10d\n", p.State.Messages.Sent.Total, p.State.Messages.Received.Total) fmt.Print(" Route statistics:\n") fmt.Printf(" Advertised: %10d\n", p.State.AdjTable.Advertised) fmt.Printf(" Received: %10d\n", p.State.AdjTable.Received) fmt.Printf(" Accepted: %10d\n", p.State.AdjTable.Accepted) first := true for _, afisafi := range p.AfiSafis { if afisafi.PrefixLimit.Config.MaxPrefixes > 0 { if first { fmt.Println(" Prefix Limits:") first = false } fmt.Printf(" %s:\tMaximum prefixes allowed %d", afisafi.Config.AfiSafiName, afisafi.PrefixLimit.Config.MaxPrefixes) if afisafi.PrefixLimit.Config.ShutdownThresholdPct > 0 { fmt.Printf(", Threshold for warning message %d%%\n", afisafi.PrefixLimit.Config.ShutdownThresholdPct) } else { fmt.Printf("\n") } } } return nil }