diff options
-rw-r--r-- | api/grpc_server.go | 587 | ||||
-rw-r--r-- | client/client.go | 900 | ||||
-rw-r--r-- | config/util.go | 42 | ||||
-rw-r--r-- | gobgp/cmd/neighbor.go | 19 | ||||
-rw-r--r-- | table/policy.go | 298 | ||||
-rw-r--r-- | test/lib/base.py | 6 |
6 files changed, 1615 insertions, 237 deletions
diff --git a/api/grpc_server.go b/api/grpc_server.go index 3ccfa4cc..4d3da7ec 100644 --- a/api/grpc_server.go +++ b/api/grpc_server.go @@ -17,13 +17,6 @@ package gobgpapi import ( "fmt" - log "github.com/Sirupsen/logrus" - "github.com/osrg/gobgp/config" - "github.com/osrg/gobgp/packet/bgp" - "github.com/osrg/gobgp/server" - "github.com/osrg/gobgp/table" - "golang.org/x/net/context" - "google.golang.org/grpc" "io" "net" "reflect" @@ -32,6 +25,14 @@ import ( "strings" "sync" "time" + + log "github.com/Sirupsen/logrus" + "github.com/osrg/gobgp/config" + "github.com/osrg/gobgp/packet/bgp" + "github.com/osrg/gobgp/server" + "github.com/osrg/gobgp/table" + "golang.org/x/net/context" + "google.golang.org/grpc" ) type Server struct { @@ -87,108 +88,108 @@ func (s *Server) Serve() error { return nil } -func (s *Server) GetNeighbor(ctx context.Context, arg *GetNeighborRequest) (*GetNeighborResponse, error) { - toApi := func(pconf *config.Neighbor) *Peer { - prefixLimits := make([]*PrefixLimit, 0, len(pconf.AfiSafis)) - for _, family := range pconf.AfiSafis { - if c := family.PrefixLimit.Config; c.MaxPrefixes > 0 { - k, _ := bgp.GetRouteFamily(string(family.Config.AfiSafiName)) - prefixLimits = append(prefixLimits, &PrefixLimit{ - Family: uint32(k), - MaxPrefixes: c.MaxPrefixes, - ShutdownThresholdPct: uint32(c.ShutdownThresholdPct), - }) - } +func NewPeerFromConfigStruct(pconf *config.Neighbor) *Peer { + prefixLimits := make([]*PrefixLimit, 0, len(pconf.AfiSafis)) + for _, family := range pconf.AfiSafis { + if c := family.PrefixLimit.Config; c.MaxPrefixes > 0 { + k, _ := bgp.GetRouteFamily(string(family.Config.AfiSafiName)) + prefixLimits = append(prefixLimits, &PrefixLimit{ + Family: uint32(k), + MaxPrefixes: c.MaxPrefixes, + ShutdownThresholdPct: uint32(c.ShutdownThresholdPct), + }) } + } - timer := pconf.Timers - s := pconf.State - localAddress := pconf.Transport.Config.LocalAddress - if pconf.Transport.State.LocalAddress != "" { - localAddress = pconf.Transport.State.LocalAddress - } - var remoteCap, localCap [][]byte - for _, cap := range pconf.State.RemoteCapabilityList { - c, _ := cap.Serialize() - remoteCap = append(remoteCap, c) - } - for _, cap := range pconf.State.LocalCapabilityList { - c, _ := cap.Serialize() - localCap = append(localCap, c) - } - return &Peer{ - Conf: &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, - RemovePrivateAs: uint32(pconf.Config.RemovePrivateAs.ToInt()), - RouteFlapDamping: pconf.Config.RouteFlapDamping, - SendCommunity: uint32(pconf.Config.SendCommunity.ToInt()), - Description: pconf.Config.Description, - PeerGroup: pconf.Config.PeerGroup, - RemoteCap: remoteCap, - LocalCap: localCap, - PrefixLimits: prefixLimits, - LocalAddress: localAddress, - NeighborInterface: pconf.Config.NeighborInterface, - Vrf: pconf.Config.Vrf, - }, - Info: &PeerState{ - BgpState: bgp.FSMState(s.SessionState.ToInt()).String(), - AdminState: s.AdminState, - Messages: &Messages{ - Received: &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: &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, - }, - Timers: &Timers{ - Config: &TimersConfig{ - ConnectRetry: uint64(timer.Config.ConnectRetry), - HoldTime: uint64(timer.Config.HoldTime), - KeepaliveInterval: uint64(timer.Config.KeepaliveInterval), + timer := pconf.Timers + s := pconf.State + localAddress := pconf.Transport.Config.LocalAddress + if pconf.Transport.State.LocalAddress != "" { + localAddress = pconf.Transport.State.LocalAddress + } + var remoteCap, localCap [][]byte + for _, cap := range pconf.State.RemoteCapabilityList { + c, _ := cap.Serialize() + remoteCap = append(remoteCap, c) + } + for _, cap := range pconf.State.LocalCapabilityList { + c, _ := cap.Serialize() + localCap = append(localCap, c) + } + return &Peer{ + Conf: &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, + RemovePrivateAs: uint32(pconf.Config.RemovePrivateAs.ToInt()), + RouteFlapDamping: pconf.Config.RouteFlapDamping, + SendCommunity: uint32(pconf.Config.SendCommunity.ToInt()), + Description: pconf.Config.Description, + PeerGroup: pconf.Config.PeerGroup, + RemoteCap: remoteCap, + LocalCap: localCap, + PrefixLimits: prefixLimits, + LocalAddress: localAddress, + NeighborInterface: pconf.Config.NeighborInterface, + Vrf: pconf.Config.Vrf, + }, + Info: &PeerState{ + BgpState: string(s.SessionState), + AdminState: s.AdminState, + Messages: &Messages{ + Received: &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, }, - State: &TimersState{ - KeepaliveInterval: uint64(timer.State.KeepaliveInterval), - NegotiatedHoldTime: uint64(timer.State.NegotiatedHoldTime), - Uptime: uint64(timer.State.Uptime), - Downtime: uint64(timer.State.Downtime), + Sent: &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, }, }, - RouteReflector: &RouteReflector{ - RouteReflectorClient: pconf.RouteReflector.Config.RouteReflectorClient, - RouteReflectorClusterId: string(pconf.RouteReflector.Config.RouteReflectorClusterId), + Received: s.AdjTable.Received, + Accepted: s.AdjTable.Accepted, + Advertised: s.AdjTable.Advertised, + }, + Timers: &Timers{ + Config: &TimersConfig{ + ConnectRetry: uint64(timer.Config.ConnectRetry), + HoldTime: uint64(timer.Config.HoldTime), + KeepaliveInterval: uint64(timer.Config.KeepaliveInterval), }, - RouteServer: &RouteServer{ - RouteServerClient: pconf.RouteServer.Config.RouteServerClient, + State: &TimersState{ + KeepaliveInterval: uint64(timer.State.KeepaliveInterval), + NegotiatedHoldTime: uint64(timer.State.NegotiatedHoldTime), + Uptime: uint64(timer.State.Uptime), + Downtime: uint64(timer.State.Downtime), }, - } + }, + RouteReflector: &RouteReflector{ + RouteReflectorClient: pconf.RouteReflector.Config.RouteReflectorClient, + RouteReflectorClusterId: string(pconf.RouteReflector.Config.RouteReflectorClusterId), + }, + RouteServer: &RouteServer{ + RouteServerClient: pconf.RouteServer.Config.RouteServerClient, + }, } +} +func (s *Server) GetNeighbor(ctx context.Context, arg *GetNeighborRequest) (*GetNeighborResponse, error) { p := []*Peer{} for _, e := range s.bgpServer.GetNeighbor() { - p = append(p, toApi(e)) + p = append(p, NewPeerFromConfigStruct(e)) } return &GetNeighborResponse{Peers: p}, nil } @@ -205,20 +206,24 @@ func ToPathApi(path *table.Path) *Path { } 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(), - } + p := &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, + Stale: path.IsStale(), + IsFromExternal: path.IsFromExternal(), + NoImplicitWithdraw: path.NoImplicitWithdraw(), + } + if s := path.GetSource(); s != nil { + p.SourceAsn = s.AS + p.SourceId = s.ID.String() + p.NeighborIp = s.Address.String() + } + return p } func (s *Server) GetRib(ctx context.Context, arg *GetRibRequest) (*GetRibResponse, error) { @@ -776,80 +781,163 @@ func (s *Server) DeleteVrf(ctx context.Context, arg *DeleteVrfRequest) (*DeleteV return &DeleteVrfResponse{}, s.bgpServer.DeleteVrf(arg.Vrf.Name) } -func (s *Server) AddNeighbor(ctx context.Context, arg *AddNeighborRequest) (*AddNeighborResponse, error) { - c, err := func(a *Peer) (*config.Neighbor, error) { - pconf := &config.Neighbor{} - if a.Conf != nil { - pconf.Config.NeighborAddress = a.Conf.NeighborAddress - pconf.Config.PeerAs = a.Conf.PeerAs - pconf.Config.LocalAs = a.Conf.LocalAs - pconf.Config.AuthPassword = a.Conf.AuthPassword - pconf.Config.RemovePrivateAs = config.RemovePrivateAsOption(a.Conf.RemovePrivateAs) - pconf.Config.RouteFlapDamping = a.Conf.RouteFlapDamping - pconf.Config.SendCommunity = config.CommunityType(a.Conf.SendCommunity) - pconf.Config.Description = a.Conf.Description - pconf.Config.PeerGroup = a.Conf.PeerGroup - pconf.Config.NeighborAddress = a.Conf.NeighborAddress - pconf.Config.NeighborInterface = a.Conf.NeighborInterface - pconf.Config.Vrf = a.Conf.Vrf - } - if a.Timers != nil && a.Timers.Config != nil { +func NewNeighborFromAPIStruct(a *Peer) (*config.Neighbor, error) { + pconf := &config.Neighbor{} + if a.Conf != nil { + pconf.Config.NeighborAddress = a.Conf.NeighborAddress + pconf.Config.PeerAs = a.Conf.PeerAs + pconf.Config.LocalAs = a.Conf.LocalAs + pconf.Config.AuthPassword = a.Conf.AuthPassword + pconf.Config.RemovePrivateAs = config.RemovePrivateAsOption(a.Conf.RemovePrivateAs) + pconf.Config.RouteFlapDamping = a.Conf.RouteFlapDamping + pconf.Config.SendCommunity = config.CommunityType(a.Conf.SendCommunity) + pconf.Config.Description = a.Conf.Description + pconf.Config.PeerGroup = a.Conf.PeerGroup + pconf.Config.NeighborAddress = a.Conf.NeighborAddress + pconf.Config.NeighborInterface = a.Conf.NeighborInterface + pconf.Config.Vrf = a.Conf.Vrf + + f := func(bufs [][]byte) ([]bgp.ParameterCapabilityInterface, error) { + var caps []bgp.ParameterCapabilityInterface + for _, buf := range bufs { + cap, err := bgp.DecodeCapability(buf) + if err != nil { + return nil, err + } + caps = append(caps, cap) + } + return caps, nil + } + + localCaps, err := f(a.Conf.LocalCap) + if err != nil { + return nil, err + } + remoteCaps, err := f(a.Conf.RemoteCap) + if err != nil { + return nil, err + } + pconf.State.LocalCapabilityList = localCaps + pconf.State.RemoteCapabilityList = remoteCaps + + pconf.State.RemoteRouterId = a.Conf.Id + + for _, f := range a.Families { + family := bgp.RouteFamily(f) + pconf.AfiSafis = append(pconf.AfiSafis, config.AfiSafi{ + Config: config.AfiSafiConfig{ + AfiSafiName: config.AfiSafiType(family.String()), + Enabled: true, + }, + }) + } + + for _, pl := range a.Conf.PrefixLimits { + for _, f := range pconf.AfiSafis { + if f.Config.AfiSafiName == config.AfiSafiType(bgp.RouteFamily(pl.Family).String()) { + f.PrefixLimit.Config.MaxPrefixes = pl.MaxPrefixes + f.PrefixLimit.Config.ShutdownThresholdPct = config.Percentage(pl.ShutdownThresholdPct) + } + } + } + } + + 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.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.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.ApplyPolicy != nil { - if a.ApplyPolicy.ImportPolicy != nil { - pconf.ApplyPolicy.Config.DefaultImportPolicy = config.DefaultPolicyType(a.ApplyPolicy.ImportPolicy.Default) - for _, p := range a.ApplyPolicy.ImportPolicy.Policies { - pconf.ApplyPolicy.Config.ImportPolicyList = append(pconf.ApplyPolicy.Config.ImportPolicyList, p.Name) - } - } - if a.ApplyPolicy.ExportPolicy != nil { - pconf.ApplyPolicy.Config.DefaultExportPolicy = config.DefaultPolicyType(a.ApplyPolicy.ExportPolicy.Default) - for _, p := range a.ApplyPolicy.ExportPolicy.Policies { - pconf.ApplyPolicy.Config.ExportPolicyList = append(pconf.ApplyPolicy.Config.ExportPolicyList, p.Name) - } + } + 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.ApplyPolicy != nil { + if a.ApplyPolicy.ImportPolicy != nil { + pconf.ApplyPolicy.Config.DefaultImportPolicy = config.DefaultPolicyType(a.ApplyPolicy.ImportPolicy.Default) + for _, p := range a.ApplyPolicy.ImportPolicy.Policies { + pconf.ApplyPolicy.Config.ImportPolicyList = append(pconf.ApplyPolicy.Config.ImportPolicyList, p.Name) } - if a.ApplyPolicy.InPolicy != nil { - pconf.ApplyPolicy.Config.DefaultInPolicy = config.DefaultPolicyType(a.ApplyPolicy.InPolicy.Default) - for _, p := range a.ApplyPolicy.InPolicy.Policies { - pconf.ApplyPolicy.Config.InPolicyList = append(pconf.ApplyPolicy.Config.InPolicyList, p.Name) - } + } + if a.ApplyPolicy.ExportPolicy != nil { + pconf.ApplyPolicy.Config.DefaultExportPolicy = config.DefaultPolicyType(a.ApplyPolicy.ExportPolicy.Default) + for _, p := range a.ApplyPolicy.ExportPolicy.Policies { + pconf.ApplyPolicy.Config.ExportPolicyList = append(pconf.ApplyPolicy.Config.ExportPolicyList, p.Name) } } - if a.Families != nil { - for _, family := range a.Families { - name, ok := bgp.AddressFamilyNameMap[bgp.RouteFamily(family)] - if !ok { - return pconf, fmt.Errorf("invalid address family: %d", family) - } - cAfiSafi := config.AfiSafi{ - Config: config.AfiSafiConfig{ - AfiSafiName: config.AfiSafiType(name), - }, - } - pconf.AfiSafis = append(pconf.AfiSafis, cAfiSafi) + if a.ApplyPolicy.InPolicy != nil { + pconf.ApplyPolicy.Config.DefaultInPolicy = config.DefaultPolicyType(a.ApplyPolicy.InPolicy.Default) + for _, p := range a.ApplyPolicy.InPolicy.Policies { + pconf.ApplyPolicy.Config.InPolicyList = append(pconf.ApplyPolicy.Config.InPolicyList, p.Name) } } - if a.Transport != nil { - pconf.Transport.Config.LocalAddress = a.Transport.LocalAddress - pconf.Transport.Config.PassiveMode = a.Transport.PassiveMode + } + if a.Families != nil { + for _, family := range a.Families { + name, ok := bgp.AddressFamilyNameMap[bgp.RouteFamily(family)] + if !ok { + return pconf, fmt.Errorf("invalid address family: %d", family) + } + cAfiSafi := config.AfiSafi{ + Config: config.AfiSafiConfig{ + AfiSafiName: config.AfiSafiType(name), + }, + } + pconf.AfiSafis = append(pconf.AfiSafis, cAfiSafi) } - if a.EbgpMultihop != nil { - pconf.EbgpMultihop.Config.Enabled = a.EbgpMultihop.Enabled - pconf.EbgpMultihop.Config.MultihopTtl = uint8(a.EbgpMultihop.MultihopTtl) + } + if a.Transport != nil { + pconf.Transport.Config.LocalAddress = a.Transport.LocalAddress + pconf.Transport.Config.PassiveMode = a.Transport.PassiveMode + } + 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 = a.Info.AdminState + + pconf.State.AdjTable.Received = a.Info.Received + pconf.State.AdjTable.Accepted = a.Info.Accepted + pconf.State.AdjTable.Advertised = a.Info.Advertised + + 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 + } } - return pconf, nil - }(arg.Peer) + } + return pconf, nil +} + +func (s *Server) AddNeighbor(ctx context.Context, arg *AddNeighborRequest) (*AddNeighborResponse, error) { + c, err := NewNeighborFromAPIStruct(arg.Peer) if err != nil { return nil, err } @@ -880,6 +968,70 @@ func NewPrefixFromApiStruct(a *Prefix) (*table.Prefix, error) { }, nil } +func NewAPIPrefixFromConfigStruct(c config.Prefix) (*Prefix, error) { + min, max, err := config.ParseMaskLength(c.IpPrefix, c.MasklengthRange) + if err != nil { + return nil, err + } + return &Prefix{ + IpPrefix: c.IpPrefix, + MaskLengthMin: uint32(min), + MaskLengthMax: uint32(max), + }, nil +} + +func NewAPIDefinedSetFromTableStruct(t table.DefinedSet) (*DefinedSet, error) { + a := &DefinedSet{ + Type: 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() + for _, n := range c.NeighborInfoList { + a.List = append(a.List, n) + } + case table.DEFINED_TYPE_AS_PATH: + s := t.(*table.AsPathSet) + c := s.ToConfig() + for _, n := range c.AsPathList { + a.List = append(a.List, n) + } + case table.DEFINED_TYPE_COMMUNITY: + s := t.(*table.CommunitySet) + c := s.ToConfig() + for _, n := range c.CommunityList { + a.List = append(a.List, n) + } + case table.DEFINED_TYPE_EXT_COMMUNITY: + s := t.(*table.ExtCommunitySet) + c := s.ToConfig() + for _, n := range c.ExtCommunityList { + a.List = append(a.List, n) + } + case table.DEFINED_TYPE_LARGE_COMMUNITY: + s := t.(*table.LargeCommunitySet) + c := s.ToConfig() + for _, n := range c.LargeCommunityList { + a.List = append(a.List, n) + } + default: + return nil, fmt.Errorf("invalid defined type") + } + return a, nil +} + func NewDefinedSetFromApiStruct(a *DefinedSet) (table.DefinedSet, error) { if a.Name == "" { return nil, fmt.Errorf("empty neighbor set name") @@ -1024,6 +1176,10 @@ func (s *Server) ReplaceDefinedSet(ctx context.Context, arg *ReplaceDefinedSetRe return &ReplaceDefinedSetResponse{}, s.bgpServer.ReplaceDefinedSet(set) } +func NewAPIStatementFromTableStruct(t *table.Statement) *Statement { + return toStatementApi(t.ToConfig()) +} + func toStatementApi(s *config.Statement) *Statement { cs := &Conditions{} if s.Conditions.MatchPrefixSet.PrefixSet != "" { @@ -1076,8 +1232,7 @@ func toStatementApi(s *config.Statement) *Statement { RouteAction: func() RouteAction { if s.Actions.RouteDisposition.AcceptRoute { return RouteAction_ACCEPT - } - if s.Actions.RouteDisposition.RejectRoute { + } else if s.Actions.RouteDisposition.RejectRoute { return RouteAction_REJECT } return RouteAction_NONE @@ -1536,6 +1691,10 @@ func (s *Server) ReplaceStatement(ctx context.Context, arg *ReplaceStatementRequ return &ReplaceStatementResponse{}, err } +func NewAPIPolicyFromTableStruct(p *table.Policy) *Policy { + return toPolicyApi(p.ToConfig()) +} + func toPolicyApi(p *config.PolicyDefinition) *Policy { return &Policy{ Name: p.Name, @@ -1549,6 +1708,46 @@ func toPolicyApi(p *config.PolicyDefinition) *Policy { } } +func NewAPIPolicyAssignmentFromTableStruct(t *table.PolicyAssignment) *PolicyAssignment { + return &PolicyAssignment{ + Type: func() PolicyType { + switch t.Type { + case table.POLICY_DIRECTION_IN: + return PolicyType_IN + case table.POLICY_DIRECTION_IMPORT: + return PolicyType_IMPORT + case table.POLICY_DIRECTION_EXPORT: + return PolicyType_EXPORT + } + log.Errorf("invalid policy-type: %s", t.Type) + return PolicyType(-1) + }(), + Default: func() RouteAction { + switch t.Default { + case table.ROUTE_TYPE_ACCEPT: + return RouteAction_ACCEPT + case table.ROUTE_TYPE_REJECT: + return RouteAction_REJECT + } + return RouteAction_NONE + }(), + Name: t.Name, + Resource: func() Resource { + if t.Name != "" { + return Resource_LOCAL + } + return Resource_GLOBAL + }(), + Policies: func() []*Policy { + l := make([]*Policy, 0) + for _, p := range t.Policies { + l = append(l, NewAPIPolicyFromTableStruct(p)) + } + return l + }(), + } +} + func NewPolicyFromApiStruct(a *Policy) (*table.Policy, error) { if a.Name == "" { return nil, fmt.Errorf("empty policy name") @@ -1635,31 +1834,25 @@ func (s *Server) GetPolicyAssignment(ctx context.Context, arg *GetPolicyAssignme if err != nil { return nil, err } - d, a, err := s.bgpServer.GetPolicyAssignment(name, dir) + def, pols, err := s.bgpServer.GetPolicyAssignment(name, dir) if err != nil { return nil, err } - return &GetPolicyAssignmentResponse{ - Assignment: &PolicyAssignment{ - Default: func() RouteAction { - switch d { - case table.ROUTE_TYPE_ACCEPT: - return RouteAction_ACCEPT - case table.ROUTE_TYPE_REJECT: - return RouteAction_REJECT - } - return RouteAction_NONE - - }(), - Policies: func() []*Policy { - l := make([]*Policy, 0) - for _, p := range a { - l = append(l, toPolicyApi(p)) - } - return l - }(), - }, - }, 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 &GetPolicyAssignmentResponse{NewAPIPolicyAssignmentFromTableStruct(t)}, err } func defaultRouteType(d RouteAction) table.RouteType { diff --git a/client/client.go b/client/client.go new file mode 100644 index 00000000..53a822c5 --- /dev/null +++ b/client/client.go @@ -0,0 +1,900 @@ +// 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 client provides a wrapper for GoBGP's gRPC API +package client + +import ( + "fmt" + "net" + "strconv" + "time" + + api "github.com/osrg/gobgp/api" + "github.com/osrg/gobgp/config" + "github.com/osrg/gobgp/packet/bgp" + "github.com/osrg/gobgp/server" + "github.com/osrg/gobgp/table" + "golang.org/x/net/context" + "google.golang.org/grpc" +) + +type GoBGPClient struct { + conn *grpc.ClientConn + cli api.GobgpApiClient +} + +func defaultGRPCOptions() []grpc.DialOption { + return []grpc.DialOption{grpc.WithTimeout(time.Second), grpc.WithBlock(), grpc.WithInsecure()} +} + +func NewGoBGPClient(target string, opts ...grpc.DialOption) (*GoBGPClient, error) { + if target == "" { + target = ":50051" + } + if len(opts) == 0 { + opts = defaultGRPCOptions() + } + conn, err := grpc.Dial(target, opts...) + if err != nil { + return nil, err + } + cli := api.NewGobgpApiClient(conn) + return &GoBGPClient{conn: conn, cli: cli}, nil +} + +func (cli *GoBGPClient) Close() error { + return cli.conn.Close() +} + +func (cli *GoBGPClient) StartServer(c *config.Global) error { + _, err := cli.cli.StartServer(context.Background(), &api.StartServerRequest{ + Global: &api.Global{ + As: c.Config.As, + RouterId: c.Config.RouterId, + ListenPort: c.Config.Port, + ListenAddresses: c.Config.LocalAddressList, + UseMultiplePaths: c.UseMultiplePaths.Config.Enabled, + }, + }) + return err +} + +func (cli *GoBGPClient) StopServer() error { + _, err := cli.cli.StopServer(context.Background(), &api.StopServerRequest{}) + return err +} + +func (cli *GoBGPClient) GetServer() (*config.Global, error) { + ret, err := cli.cli.GetServer(context.Background(), &api.GetServerRequest{}) + if err != nil { + return nil, err + } + return &config.Global{ + Config: config.GlobalConfig{ + As: ret.Global.As, + RouterId: ret.Global.RouterId, + Port: ret.Global.ListenPort, + LocalAddressList: ret.Global.ListenAddresses, + }, + UseMultiplePaths: config.UseMultiplePaths{ + Config: config.UseMultiplePathsConfig{ + Enabled: ret.Global.UseMultiplePaths, + }, + }, + }, nil +} + +func (cli *GoBGPClient) GetNeighbor() ([]*config.Neighbor, error) { + ret, err := cli.cli.GetNeighbor(context.Background(), &api.GetNeighborRequest{}) + if err != nil { + return nil, err + } + + neighbors := make([]*config.Neighbor, 0, len(ret.Peers)) + + for _, p := range ret.Peers { + n, err := api.NewNeighborFromAPIStruct(p) + if err != nil { + return nil, err + } + neighbors = append(neighbors, n) + } + return neighbors, nil +} + +func (cli *GoBGPClient) AddNeighbor(c *config.Neighbor) error { + peer := api.NewPeerFromConfigStruct(c) + _, err := cli.cli.AddNeighbor(context.Background(), &api.AddNeighborRequest{peer}) + return err +} + +func (cli *GoBGPClient) DeleteNeighbor(c *config.Neighbor) error { + peer := api.NewPeerFromConfigStruct(c) + _, err := cli.cli.DeleteNeighbor(context.Background(), &api.DeleteNeighborRequest{peer}) + return err +} + +//func (cli *GoBGPClient) UpdateNeighbor(c *config.Neighbor) (bool, error) { +//} + +func (cli *GoBGPClient) ShutdownNeighbor(addr string) error { + _, err := cli.cli.ShutdownNeighbor(context.Background(), &api.ShutdownNeighborRequest{addr}) + return err +} + +func (cli *GoBGPClient) ResetNeighbor(addr string) error { + _, err := cli.cli.ResetNeighbor(context.Background(), &api.ResetNeighborRequest{addr}) + return err +} + +func (cli *GoBGPClient) EnableNeighbor(addr string) error { + _, err := cli.cli.EnableNeighbor(context.Background(), &api.EnableNeighborRequest{addr}) + return err +} + +func (cli *GoBGPClient) DisableNeighbor(addr string) error { + _, err := cli.cli.DisableNeighbor(context.Background(), &api.DisableNeighborRequest{addr}) + return err +} + +func (cli *GoBGPClient) softreset(addr string, family bgp.RouteFamily, dir api.SoftResetNeighborRequest_SoftResetDirection) error { + _, err := cli.cli.SoftResetNeighbor(context.Background(), &api.SoftResetNeighborRequest{ + Address: addr, + Direction: dir, + }) + return err +} + +func (cli *GoBGPClient) SoftResetIn(addr string, family bgp.RouteFamily) error { + return cli.softreset(addr, family, api.SoftResetNeighborRequest_IN) +} + +func (cli *GoBGPClient) SoftResetOut(addr string, family bgp.RouteFamily) error { + return cli.softreset(addr, family, api.SoftResetNeighborRequest_OUT) +} + +func (cli *GoBGPClient) SoftReset(addr string, family bgp.RouteFamily) error { + return cli.softreset(addr, family, api.SoftResetNeighborRequest_BOTH) +} + +func (cli *GoBGPClient) getRIB(resource api.Resource, name string, family bgp.RouteFamily, prefixes []*table.LookupPrefix) (*table.Table, error) { + dsts := make([]*api.Destination, 0, len(prefixes)) + for _, p := range prefixes { + longer := false + shorter := false + if p.LookupOption&table.LOOKUP_LONGER > 0 { + longer = true + } + if p.LookupOption&table.LOOKUP_SHORTER > 0 { + shorter = true + } + dsts = append(dsts, &api.Destination{ + Prefix: p.Prefix, + LongerPrefixes: longer, + ShorterPrefixes: shorter, + }) + } + res, err := cli.cli.GetRib(context.Background(), &api.GetRibRequest{ + Table: &api.Table{ + Type: resource, + Family: uint32(family), + Name: name, + Destinations: dsts, + }, + }) + if err != nil { + return nil, err + } + return res.Table.ToNativeTable() +} + +func (cli *GoBGPClient) GetRIB(family bgp.RouteFamily, prefixes []*table.LookupPrefix) (*table.Table, error) { + return cli.getRIB(api.Resource_GLOBAL, "", family, prefixes) +} + +func (cli *GoBGPClient) GetLocalRIB(name string, family bgp.RouteFamily, prefixes []*table.LookupPrefix) (*table.Table, error) { + return cli.getRIB(api.Resource_LOCAL, name, family, prefixes) +} + +func (cli *GoBGPClient) GetAdjRIBIn(name string, family bgp.RouteFamily, prefixes []*table.LookupPrefix) (*table.Table, error) { + return cli.getRIB(api.Resource_ADJ_IN, name, family, prefixes) +} + +func (cli *GoBGPClient) GetAdjRIBOut(name string, family bgp.RouteFamily, prefixes []*table.LookupPrefix) (*table.Table, error) { + return cli.getRIB(api.Resource_ADJ_OUT, name, family, prefixes) +} + +func (cli *GoBGPClient) GetVRFRIB(name string, family bgp.RouteFamily, prefixes []*table.LookupPrefix) (*table.Table, error) { + return cli.getRIB(api.Resource_VRF, name, family, prefixes) +} + +func (cli *GoBGPClient) getRIBInfo(resource api.Resource, name string, family bgp.RouteFamily) (*table.TableInfo, error) { + res, err := cli.cli.GetRibInfo(context.Background(), &api.GetRibInfoRequest{ + Info: &api.TableInfo{ + Type: resource, + Name: name, + Family: uint32(family), + }, + }) + if err != nil { + return nil, err + } + return &table.TableInfo{ + NumDestination: int(res.Info.NumDestination), + NumPath: int(res.Info.NumPath), + NumAccepted: int(res.Info.NumAccepted), + }, nil + +} + +func (cli *GoBGPClient) GetRIBInfo(family bgp.RouteFamily) (*table.TableInfo, error) { + return cli.getRIBInfo(api.Resource_GLOBAL, "", family) +} + +func (cli *GoBGPClient) GetLocalRIBInfo(name string, family bgp.RouteFamily) (*table.TableInfo, error) { + return cli.getRIBInfo(api.Resource_LOCAL, name, family) +} + +func (cli *GoBGPClient) GetAdjRIBInInfo(name string, family bgp.RouteFamily) (*table.TableInfo, error) { + return cli.getRIBInfo(api.Resource_ADJ_IN, name, family) +} + +func (cli *GoBGPClient) GetAdjRIBOutInfo(name string, family bgp.RouteFamily) (*table.TableInfo, error) { + return cli.getRIBInfo(api.Resource_ADJ_OUT, name, family) +} + +type AddPathByStreamClient struct { + stream api.GobgpApi_InjectMrtClient +} + +func (c *AddPathByStreamClient) Send(paths ...*table.Path) error { + ps := make([]*api.Path, 0, len(paths)) + for _, p := range paths { + ps = append(ps, api.ToPathApi(p)) + } + return c.stream.Send(&api.InjectMrtRequest{ + Resource: api.Resource_GLOBAL, + Paths: ps, + }) +} + +func (c *AddPathByStreamClient) Close() error { + _, err := c.stream.CloseAndRecv() + return err +} + +func (cli *GoBGPClient) AddPathByStream() (*AddPathByStreamClient, error) { + stream, err := cli.cli.InjectMrt(context.Background()) + if err != nil { + return nil, err + } + return &AddPathByStreamClient{stream}, nil +} + +func (cli *GoBGPClient) addPath(vrfID string, pathList []*table.Path) ([]byte, error) { + resource := api.Resource_GLOBAL + if vrfID != "" { + resource = api.Resource_VRF + } + var uuid []byte + for _, path := range pathList { + r, err := cli.cli.AddPath(context.Background(), &api.AddPathRequest{ + Resource: resource, + VrfId: vrfID, + Path: api.ToPathApi(path), + }) + if err != nil { + return nil, err + } + uuid = r.Uuid + } + return uuid, nil +} + +func (cli *GoBGPClient) AddPath(pathList []*table.Path) ([]byte, error) { + return cli.addPath("", pathList) +} + +func (cli *GoBGPClient) AddVRFPath(vrfID string, pathList []*table.Path) ([]byte, error) { + if vrfID == "" { + return nil, fmt.Errorf("VRF ID is empty") + } + return cli.addPath(vrfID, pathList) +} + +func (cli *GoBGPClient) deletePath(uuid []byte, f bgp.RouteFamily, vrfID string, pathList []*table.Path) error { + var reqs []*api.DeletePathRequest + + resource := api.Resource_GLOBAL + if vrfID != "" { + resource = api.Resource_VRF + } + switch { + case len(pathList) != 0: + for _, path := range pathList { + nlri := path.GetNlri() + n, err := nlri.Serialize() + if err != nil { + return err + } + p := &api.Path{ + Nlri: n, + Family: uint32(path.GetRouteFamily()), + } + reqs = append(reqs, &api.DeletePathRequest{ + Resource: resource, + VrfId: vrfID, + Path: p, + }) + } + default: + reqs = append(reqs, &api.DeletePathRequest{ + Resource: resource, + VrfId: vrfID, + Uuid: uuid, + Family: uint32(f), + }) + } + + for _, req := range reqs { + if _, err := cli.cli.DeletePath(context.Background(), req); err != nil { + return err + } + } + return nil +} + +func (cli *GoBGPClient) DeletePath(pathList []*table.Path) error { + return cli.deletePath(nil, bgp.RouteFamily(0), "", pathList) +} + +func (cli *GoBGPClient) DeleteVRFPath(vrfID string, pathList []*table.Path) error { + if vrfID == "" { + return fmt.Errorf("VRF ID is empty") + } + return cli.deletePath(nil, bgp.RouteFamily(0), vrfID, pathList) +} + +func (cli *GoBGPClient) DeletePathByUUID(uuid []byte) error { + return cli.deletePath(uuid, bgp.RouteFamily(0), "", nil) +} + +func (cli *GoBGPClient) DeletePathByFamily(family bgp.RouteFamily) error { + return cli.deletePath(nil, family, "", nil) +} + +func (cli *GoBGPClient) GetVRF() ([]*table.Vrf, error) { + ret, err := cli.cli.GetVrf(context.Background(), &api.GetVrfRequest{}) + if err != nil { + return nil, err + } + var vrfs []*table.Vrf + + f := func(bufs [][]byte) ([]bgp.ExtendedCommunityInterface, error) { + ret := make([]bgp.ExtendedCommunityInterface, 0, len(bufs)) + for _, rt := range bufs { + r, err := bgp.ParseExtended(rt) + if err != nil { + return nil, err + } + ret = append(ret, r) + } + return ret, nil + } + + for _, vrf := range ret.Vrfs { + importRT, err := f(vrf.ImportRt) + if err != nil { + return nil, err + } + exportRT, err := f(vrf.ExportRt) + if err != nil { + return nil, err + } + vrfs = append(vrfs, &table.Vrf{ + Name: vrf.Name, + Id: vrf.Id, + Rd: bgp.GetRouteDistinguisher(vrf.Rd), + ImportRt: importRT, + ExportRt: exportRT, + }) + } + + return vrfs, nil +} + +func (cli *GoBGPClient) AddVRF(name string, id int, rd bgp.RouteDistinguisherInterface, im, ex []bgp.ExtendedCommunityInterface) error { + buf, err := rd.Serialize() + if err != nil { + return err + } + + f := func(comms []bgp.ExtendedCommunityInterface) ([][]byte, error) { + var bufs [][]byte + for _, c := range comms { + buf, err := c.Serialize() + if err != nil { + return nil, err + } + bufs = append(bufs, buf) + } + return bufs, err + } + + importRT, err := f(im) + if err != nil { + return err + } + exportRT, err := f(ex) + if err != nil { + return err + } + + arg := &api.AddVrfRequest{ + Vrf: &api.Vrf{ + Name: name, + Rd: buf, + Id: uint32(id), + ImportRt: importRT, + ExportRt: exportRT, + }, + } + _, err = cli.cli.AddVrf(context.Background(), arg) + return err +} + +func (cli *GoBGPClient) DeleteVRF(name string) error { + arg := &api.DeleteVrfRequest{ + Vrf: &api.Vrf{ + Name: name, + }, + } + _, err := cli.cli.DeleteVrf(context.Background(), arg) + return err +} + +func (cli *GoBGPClient) GetDefinedSet(typ table.DefinedType) ([]table.DefinedSet, error) { + ret, err := cli.cli.GetDefinedSet(context.Background(), &api.GetDefinedSetRequest{Type: api.DefinedType(typ)}) + if err != nil { + return nil, err + } + ds := make([]table.DefinedSet, 0, len(ret.Sets)) + for _, s := range ret.Sets { + d, err := api.NewDefinedSetFromApiStruct(s) + if err != nil { + return nil, err + } + ds = append(ds, d) + } + return ds, nil +} + +func (cli *GoBGPClient) AddDefinedSet(d table.DefinedSet) error { + a, err := api.NewAPIDefinedSetFromTableStruct(d) + if err != nil { + return err + } + _, err = cli.cli.AddDefinedSet(context.Background(), &api.AddDefinedSetRequest{ + Set: a, + }) + return err +} + +func (cli *GoBGPClient) DeleteDefinedSet(d table.DefinedSet, all bool) error { + a, err := api.NewAPIDefinedSetFromTableStruct(d) + if err != nil { + return err + } + _, err = cli.cli.DeleteDefinedSet(context.Background(), &api.DeleteDefinedSetRequest{ + Set: a, + All: all, + }) + return err +} + +func (cli *GoBGPClient) ReplaceDefinedSet(d table.DefinedSet) error { + a, err := api.NewAPIDefinedSetFromTableStruct(d) + if err != nil { + return err + } + _, err = cli.cli.ReplaceDefinedSet(context.Background(), &api.ReplaceDefinedSetRequest{ + Set: a, + }) + return err +} + +func (cli *GoBGPClient) GetStatement() ([]*table.Statement, error) { + ret, err := cli.cli.GetStatement(context.Background(), &api.GetStatementRequest{}) + if err != nil { + return nil, err + } + sts := make([]*table.Statement, 0, len(ret.Statements)) + for _, s := range ret.Statements { + st, err := api.NewStatementFromApiStruct(s) + if err != nil { + return nil, err + } + sts = append(sts, st) + } + return sts, nil +} + +func (cli *GoBGPClient) AddStatement(t *table.Statement) error { + a := api.NewAPIStatementFromTableStruct(t) + _, err := cli.cli.AddStatement(context.Background(), &api.AddStatementRequest{ + Statement: a, + }) + return err +} + +func (cli *GoBGPClient) DeleteStatement(t *table.Statement, all bool) error { + a := api.NewAPIStatementFromTableStruct(t) + _, err := cli.cli.DeleteStatement(context.Background(), &api.DeleteStatementRequest{ + Statement: a, + All: all, + }) + return err +} + +func (cli *GoBGPClient) ReplaceStatement(t *table.Statement) error { + a := api.NewAPIStatementFromTableStruct(t) + _, err := cli.cli.ReplaceStatement(context.Background(), &api.ReplaceStatementRequest{ + Statement: a, + }) + return err +} + +func (cli *GoBGPClient) GetPolicy() ([]*table.Policy, error) { + ret, err := cli.cli.GetPolicy(context.Background(), &api.GetPolicyRequest{}) + if err != nil { + return nil, err + } + pols := make([]*table.Policy, 0, len(ret.Policies)) + for _, p := range ret.Policies { + pol, err := api.NewPolicyFromApiStruct(p) + if err != nil { + return nil, err + } + pols = append(pols, pol) + } + return pols, nil +} + +func (cli *GoBGPClient) AddPolicy(t *table.Policy, refer bool) error { + a := api.NewAPIPolicyFromTableStruct(t) + _, err := cli.cli.AddPolicy(context.Background(), &api.AddPolicyRequest{ + Policy: a, + ReferExistingStatements: refer, + }) + return err +} + +func (cli *GoBGPClient) DeletePolicy(t *table.Policy, all, preserve bool) error { + a := api.NewAPIPolicyFromTableStruct(t) + _, err := cli.cli.DeletePolicy(context.Background(), &api.DeletePolicyRequest{ + Policy: a, + All: all, + PreserveStatements: preserve, + }) + return err +} + +func (cli *GoBGPClient) ReplacePolicy(t *table.Policy, refer, preserve bool) error { + a := api.NewAPIPolicyFromTableStruct(t) + _, err := cli.cli.ReplacePolicy(context.Background(), &api.ReplacePolicyRequest{ + Policy: a, + ReferExistingStatements: refer, + PreserveStatements: preserve, + }) + return err +} + +func (cli *GoBGPClient) getPolicyAssignment(name string, dir table.PolicyDirection) (*table.PolicyAssignment, error) { + var typ api.PolicyType + switch dir { + case table.POLICY_DIRECTION_IN: + typ = api.PolicyType_IN + case table.POLICY_DIRECTION_IMPORT: + typ = api.PolicyType_IMPORT + case table.POLICY_DIRECTION_EXPORT: + typ = api.PolicyType_EXPORT + } + resource := api.Resource_GLOBAL + if name != "" { + resource = api.Resource_LOCAL + } + + ret, err := cli.cli.GetPolicyAssignment(context.Background(), &api.GetPolicyAssignmentRequest{ + Assignment: &api.PolicyAssignment{ + Name: name, + Resource: resource, + Type: typ, + }, + }) + if err != nil { + return nil, err + } + + def := table.ROUTE_TYPE_ACCEPT + if ret.Assignment.Default == api.RouteAction_REJECT { + def = table.ROUTE_TYPE_REJECT + } + + pols := make([]*table.Policy, 0, len(ret.Assignment.Policies)) + for _, p := range ret.Assignment.Policies { + pol, err := api.NewPolicyFromApiStruct(p) + if err != nil { + return nil, err + } + pols = append(pols, pol) + } + return &table.PolicyAssignment{ + Name: name, + Type: dir, + Policies: pols, + Default: def, + }, nil +} + +func (cli *GoBGPClient) GetImportPolicy() (*table.PolicyAssignment, error) { + return cli.getPolicyAssignment("", table.POLICY_DIRECTION_IMPORT) +} + +func (cli *GoBGPClient) GetExportPolicy() (*table.PolicyAssignment, error) { + return cli.getPolicyAssignment("", table.POLICY_DIRECTION_EXPORT) +} + +func (cli *GoBGPClient) GetRouteServerInPolicy(name string) (*table.PolicyAssignment, error) { + return cli.getPolicyAssignment(name, table.POLICY_DIRECTION_IN) +} + +func (cli *GoBGPClient) GetRouteServerImportPolicy(name string) (*table.PolicyAssignment, error) { + return cli.getPolicyAssignment(name, table.POLICY_DIRECTION_IMPORT) +} + +func (cli *GoBGPClient) GetRouteServerExportPolicy(name string) (*table.PolicyAssignment, error) { + return cli.getPolicyAssignment(name, table.POLICY_DIRECTION_EXPORT) +} + +func (cli *GoBGPClient) AddPolicyAssignment(assignment *table.PolicyAssignment) error { + _, err := cli.cli.AddPolicyAssignment(context.Background(), &api.AddPolicyAssignmentRequest{ + api.NewAPIPolicyAssignmentFromTableStruct(assignment), + }) + return err +} + +func (cli *GoBGPClient) DeletePolicyAssignment(assignment *table.PolicyAssignment, all bool) error { + a := api.NewAPIPolicyAssignmentFromTableStruct(assignment) + _, err := cli.cli.DeletePolicyAssignment(context.Background(), &api.DeletePolicyAssignmentRequest{a, all}) + return err +} + +func (cli *GoBGPClient) ReplacePolicyAssignment(assignment *table.PolicyAssignment) error { + _, err := cli.cli.ReplacePolicyAssignment(context.Background(), &api.ReplacePolicyAssignmentRequest{ + api.NewAPIPolicyAssignmentFromTableStruct(assignment), + }) + return err +} + +//func (cli *GoBGPClient) EnableMrt(c *config.MrtConfig) error { +//} +// +//func (cli *GoBGPClient) DisableMrt(c *config.MrtConfig) error { +//} +// + +func (cli *GoBGPClient) GetRPKI() ([]*config.RpkiServer, error) { + rsp, err := cli.cli.GetRpki(context.Background(), &api.GetRpkiRequest{}) + if err != nil { + return nil, err + } + servers := make([]*config.RpkiServer, 0, len(rsp.Servers)) + for _, s := range rsp.Servers { + port, err := strconv.Atoi(s.Conf.RemotePort) + if err != nil { + return nil, err + } + server := &config.RpkiServer{ + Config: config.RpkiServerConfig{ + Address: s.Conf.Address, + Port: uint32(port), + }, + State: config.RpkiServerState{ + Up: s.State.Up, + SerialNumber: s.State.Serial, + RecordsV4: s.State.RecordIpv4, + RecordsV6: s.State.RecordIpv6, + PrefixesV4: s.State.PrefixIpv4, + PrefixesV6: s.State.PrefixIpv6, + Uptime: s.State.Uptime, + Downtime: s.State.Downtime, + RpkiMessages: config.RpkiMessages{ + RpkiReceived: config.RpkiReceived{ + SerialNotify: s.State.SerialNotify, + CacheReset: s.State.CacheReset, + CacheResponse: s.State.CacheResponse, + Ipv4Prefix: s.State.ReceivedIpv4, + Ipv6Prefix: s.State.ReceivedIpv6, + EndOfData: s.State.EndOfData, + Error: s.State.Error, + }, + RpkiSent: config.RpkiSent{ + SerialQuery: s.State.SerialQuery, + ResetQuery: s.State.ResetQuery, + }, + }, + }, + } + servers = append(servers, server) + } + return servers, nil +} + +func (cli *GoBGPClient) GetROA(family bgp.RouteFamily) ([]*server.ROA, error) { + rsp, err := cli.cli.GetRoa(context.Background(), &api.GetRoaRequest{ + Family: uint32(family), + }) + if err != nil { + return nil, err + } + roas := make([]*server.ROA, 0, len(rsp.Roas)) + for _, r := range rsp.Roas { + ip := net.ParseIP(r.Prefix) + if ip.To4() != nil { + ip = ip.To4() + } + afi, _ := bgp.RouteFamilyToAfiSafi(family) + roa := server.NewROA(int(afi), []byte(ip), uint8(r.Prefixlen), uint8(r.Maxlen), r.As, net.JoinHostPort(r.Conf.Address, r.Conf.RemotePort)) + roas = append(roas, roa) + } + return roas, nil +} + +func (cli *GoBGPClient) AddRPKIServer(address string, port, lifetime int) error { + _, err := cli.cli.AddRpki(context.Background(), &api.AddRpkiRequest{ + Address: address, + Port: uint32(port), + Lifetime: int64(lifetime), + }) + return err +} + +func (cli *GoBGPClient) DeleteRPKIServer(address string) error { + _, err := cli.cli.DeleteRpki(context.Background(), &api.DeleteRpkiRequest{ + Address: address, + }) + return err +} + +func (cli *GoBGPClient) EnableRPKIServer(address string) error { + _, err := cli.cli.EnableRpki(context.Background(), &api.EnableRpkiRequest{ + Address: address, + }) + return err +} + +func (cli *GoBGPClient) DisableRPKIServer(address string) error { + _, err := cli.cli.DisableRpki(context.Background(), &api.DisableRpkiRequest{ + Address: address, + }) + return err +} + +func (cli *GoBGPClient) ResetRPKIServer(address string) error { + _, err := cli.cli.ResetRpki(context.Background(), &api.ResetRpkiRequest{ + Address: address, + }) + return err +} + +func (cli *GoBGPClient) SoftResetRPKIServer(address string) error { + _, err := cli.cli.SoftResetRpki(context.Background(), &api.SoftResetRpkiRequest{ + Address: address, + }) + return err +} + +func (cli *GoBGPClient) ValidateRIBWithRPKI(prefixes ...string) error { + req := &api.ValidateRibRequest{} + if len(prefixes) > 1 { + return fmt.Errorf("too many prefixes: %d", len(prefixes)) + } else if len(prefixes) == 1 { + req.Prefix = prefixes[0] + } + _, err := cli.cli.ValidateRib(context.Background(), req) + return err +} + +func (cli *GoBGPClient) AddBMP(c *config.BmpServerConfig) error { + _, err := cli.cli.AddBmp(context.Background(), &api.AddBmpRequest{ + Address: c.Address, + Port: c.Port, + Type: api.AddBmpRequest_MonitoringPolicy(c.RouteMonitoringPolicy.ToInt()), + }) + return err +} + +func (cli *GoBGPClient) DeleteBMP(c *config.BmpServerConfig) error { + _, err := cli.cli.DeleteBmp(context.Background(), &api.DeleteBmpRequest{ + Address: c.Address, + Port: c.Port, + }) + return err +} + +type MonitorRIBClient struct { + stream api.GobgpApi_MonitorRibClient +} + +func (c *MonitorRIBClient) Recv() (*table.Destination, error) { + d, err := c.stream.Recv() + if err != nil { + return nil, err + } + return d.ToNativeDestination() +} + +func (cli *GoBGPClient) MonitorRIB(family bgp.RouteFamily) (*MonitorRIBClient, error) { + stream, err := cli.cli.MonitorRib(context.Background(), &api.Table{ + Type: api.Resource_GLOBAL, + Family: uint32(family), + }) + if err != nil { + return nil, err + } + return &MonitorRIBClient{stream}, nil +} + +func (cli *GoBGPClient) MonitorAdjRIBIn(name string, family bgp.RouteFamily) (*MonitorRIBClient, error) { + stream, err := cli.cli.MonitorRib(context.Background(), &api.Table{ + Type: api.Resource_ADJ_IN, + Name: name, + Family: uint32(family), + }) + if err != nil { + return nil, err + } + return &MonitorRIBClient{stream}, nil +} + +type MonitorNeighborStateClient struct { + stream api.GobgpApi_MonitorPeerStateClient +} + +func (c *MonitorNeighborStateClient) Recv() (*config.Neighbor, error) { + p, err := c.stream.Recv() + if err != nil { + return nil, err + } + return api.NewNeighborFromAPIStruct(p) +} + +func (cli *GoBGPClient) MonitorNeighborState(names ...string) (*MonitorNeighborStateClient, error) { + if len(names) > 1 { + return nil, fmt.Errorf("support one name at most: %d", len(names)) + } + name := "" + if len(names) > 0 { + name = names[0] + } + stream, err := cli.cli.MonitorPeerState(context.Background(), &api.Arguments{ + Name: name, + }) + if err != nil { + return nil, err + } + return &MonitorNeighborStateClient{stream}, nil +} diff --git a/config/util.go b/config/util.go index d1696eb0..13f1fa77 100644 --- a/config/util.go +++ b/config/util.go @@ -17,6 +17,10 @@ package config import ( "fmt" + "net" + "regexp" + "strconv" + "github.com/osrg/gobgp/packet/bgp" ) @@ -82,3 +86,41 @@ func CheckAfiSafisChange(x, y []AfiSafi) bool { } return false } + +func ParseMaskLength(prefix, mask string) (int, int, error) { + _, ipNet, err := net.ParseCIDR(prefix) + if err != nil { + return 0, 0, fmt.Errorf("invalid prefix: %s", prefix) + } + if mask == "" { + l, _ := ipNet.Mask.Size() + return l, l, nil + } + exp := regexp.MustCompile("(\\d+)\\.\\.(\\d+)") + elems := exp.FindStringSubmatch(mask) + if len(elems) != 3 { + return 0, 0, fmt.Errorf("invalid mask length range: %s", mask) + } + // we've already checked the range is sane by regexp + min, _ := strconv.Atoi(elems[1]) + max, _ := strconv.Atoi(elems[2]) + if min > max { + return 0, 0, fmt.Errorf("invalid mask length range: %s", mask) + } + if ipv4 := ipNet.IP.To4(); ipv4 != nil { + f := func(i int) bool { + return i >= 0 && i <= 32 + } + if !f(min) || !f(max) { + return 0, 0, fmt.Errorf("ipv4 mask length range outside scope :%s", mask) + } + } else { + f := func(i int) bool { + return i >= 0 && i <= 128 + } + if !f(min) || !f(max) { + return 0, 0, fmt.Errorf("ipv6 mask length range outside scope :%s", mask) + } + } + return min, max, nil +} diff --git a/gobgp/cmd/neighbor.go b/gobgp/cmd/neighbor.go index a238b562..fa6591f2 100644 --- a/gobgp/cmd/neighbor.go +++ b/gobgp/cmd/neighbor.go @@ -109,7 +109,7 @@ func showNeighbors(vrf string) error { timeStr := "never" if p.Timers.State.Uptime != 0 { t := int64(p.Timers.State.Downtime) - if p.Info.BgpState == "BGP_FSM_ESTABLISHED" { + if p.Info.BgpState == string(config.SESSION_STATE_ESTABLISHED) { t = int64(p.Timers.State.Uptime) } timeStr = formatTimedelta(int64(now.Sub(time.Unix(int64(t), 0)).Seconds())) @@ -131,18 +131,21 @@ func showNeighbors(vrf string) error { return "Idle(PfxCt)" } - if fsm == "BGP_FSM_IDLE" { + switch config.SessionState(fsm) { + case config.SESSION_STATE_IDLE: return "Idle" - } else if fsm == "BGP_FSM_CONNECT" { + case config.SESSION_STATE_CONNECT: return "Connect" - } else if fsm == "BGP_FSM_ACTIVE" { + case config.SESSION_STATE_ACTIVE: return "Active" - } else if fsm == "BGP_FSM_OPENSENT" { + case config.SESSION_STATE_OPENSENT: return "Sent" - } else if fsm == "BGP_FSM_OPENCONFIRM" { + case config.SESSION_STATE_OPENCONFIRM: return "Confirm" - } else { + case config.SESSION_STATE_ESTABLISHED: return "Establ" + default: + return fsm } } @@ -590,7 +593,7 @@ func showNeighborRib(r string, name string, args []string) error { if err != nil { return err } - if peer.Info.BgpState != "BGP_FSM_ESTABLISHED" { + if peer.Info.BgpState != string(config.SESSION_STATE_ESTABLISHED) { return fmt.Errorf("Neighbor %v's BGP session is not established", name) } } diff --git a/table/policy.go b/table/policy.go index 07b0f046..7ac8a6c3 100644 --- a/table/policy.go +++ b/table/policy.go @@ -17,6 +17,7 @@ package table import ( "bytes" + "encoding/json" "fmt" "net" "reflect" @@ -178,6 +179,18 @@ const ( ATTRIBUTE_LE ) +func (c AttributeComparison) String() string { + switch c { + case ATTRIBUTE_EQ: + return "=" + case ATTRIBUTE_GE: + return ">=" + case ATTRIBUTE_LE: + return "<=" + } + return "?" +} + const ( ASPATH_REGEXP_MAGIC = "(^|[,{}() ]|$)" ) @@ -188,10 +201,29 @@ type DefinedSet interface { Append(DefinedSet) error Remove(DefinedSet) error Replace(DefinedSet) error + String() string + List() []string } type DefinedSetMap map[DefinedType]map[string]DefinedSet +type DefinedSetList []DefinedSet + +func (l DefinedSetList) Len() int { + return len(l) +} + +func (l DefinedSetList) Swap(i, j int) { + l[i], l[j] = l[j], l[i] +} + +func (l DefinedSetList) Less(i, j int) bool { + if l[i].Type() != l[j].Type() { + return l[i].Type() < l[j].Type() + } + return l[i].Name() < l[j].Name() +} + type Prefix struct { Prefix *net.IPNet AddressFamily bgp.RouteFamily @@ -347,6 +379,18 @@ func (lhs *PrefixSet) Replace(arg DefinedSet) error { return nil } +func (s *PrefixSet) List() []string { + var list []string + s.tree.Walk(func(s string, v interface{}) bool { + ps := v.([]*Prefix) + for _, p := range ps { + list = append(list, fmt.Sprintf("%s %d..%d", p.Prefix.String(), p.MasklengthRangeMin, p.MasklengthRangeMax)) + } + return false + }) + return list +} + func (s *PrefixSet) ToConfig() *config.PrefixSet { list := make([]config.Prefix, 0, s.tree.Len()) s.tree.Walk(func(s string, v interface{}) bool { @@ -362,6 +406,14 @@ func (s *PrefixSet) ToConfig() *config.PrefixSet { } } +func (s *PrefixSet) String() string { + return strings.Join(s.List(), "\n") +} + +func (s *PrefixSet) MarshalJSON() ([]byte, error) { + return json.Marshal(s.ToConfig()) +} + func NewPrefixSetFromApiStruct(name string, prefixes []*Prefix) (*PrefixSet, error) { if name == "" { return nil, fmt.Errorf("empty prefix set name") @@ -465,17 +517,29 @@ func (lhs *NeighborSet) Replace(arg DefinedSet) error { return nil } -func (s *NeighborSet) ToConfig() *config.NeighborSet { +func (s *NeighborSet) List() []string { list := make([]string, 0, len(s.list)) for _, n := range s.list { list = append(list, n.String()) } + return list +} + +func (s *NeighborSet) ToConfig() *config.NeighborSet { return &config.NeighborSet{ NeighborSetName: s.name, - NeighborInfoList: list, + NeighborInfoList: s.List(), } } +func (s *NeighborSet) String() string { + return strings.Join(s.List(), "\n") +} + +func (s *NeighborSet) MarshalJSON() ([]byte, error) { + return json.Marshal(s.ToConfig()) +} + func NewNeighborSetFromApiStruct(name string, list []net.IP) (*NeighborSet, error) { return &NeighborSet{ name: name, @@ -667,7 +731,7 @@ func (lhs *AsPathSet) Replace(arg DefinedSet) error { return nil } -func (s *AsPathSet) ToConfig() *config.AsPathSet { +func (s *AsPathSet) List() []string { list := make([]string, 0, len(s.list)+len(s.singleList)) for _, exp := range s.singleList { list = append(list, exp.String()) @@ -675,12 +739,24 @@ func (s *AsPathSet) ToConfig() *config.AsPathSet { for _, exp := range s.list { list = append(list, exp.String()) } + return list +} + +func (s *AsPathSet) ToConfig() *config.AsPathSet { return &config.AsPathSet{ AsPathSetName: s.name, - AsPathList: list, + AsPathList: s.List(), } } +func (s *AsPathSet) String() string { + return strings.Join(s.List(), "\n") +} + +func (s *AsPathSet) MarshalJSON() ([]byte, error) { + return json.Marshal(s.ToConfig()) +} + func NewAsPathSet(c config.AsPathSet) (*AsPathSet, error) { name := c.AsPathSetName if name == "" { @@ -797,17 +873,29 @@ type CommunitySet struct { regExpSet } -func (s *CommunitySet) ToConfig() *config.CommunitySet { +func (s *CommunitySet) List() []string { list := make([]string, 0, len(s.list)) for _, exp := range s.list { list = append(list, exp.String()) } + return list +} + +func (s *CommunitySet) ToConfig() *config.CommunitySet { return &config.CommunitySet{ CommunitySetName: s.name, - CommunityList: list, + CommunityList: s.List(), } } +func (s *CommunitySet) String() string { + return strings.Join(s.List(), "\n") +} + +func (s *CommunitySet) MarshalJSON() ([]byte, error) { + return json.Marshal(s.ToConfig()) +} + func ParseCommunity(arg string) (uint32, error) { i, err := strconv.ParseUint(arg, 10, 32) if err == nil { @@ -927,7 +1015,7 @@ type ExtCommunitySet struct { subtypeList []bgp.ExtendedCommunityAttrSubType } -func (s *ExtCommunitySet) ToConfig() *config.ExtCommunitySet { +func (s *ExtCommunitySet) List() []string { list := make([]string, 0, len(s.list)) f := func(idx int, arg string) string { switch s.subtypeList[idx] { @@ -944,12 +1032,24 @@ func (s *ExtCommunitySet) ToConfig() *config.ExtCommunitySet { for idx, exp := range s.list { list = append(list, f(idx, exp.String())) } + return list +} + +func (s *ExtCommunitySet) ToConfig() *config.ExtCommunitySet { return &config.ExtCommunitySet{ ExtCommunitySetName: s.name, - ExtCommunityList: list, + ExtCommunityList: s.List(), } } +func (s *ExtCommunitySet) String() string { + return strings.Join(s.List(), "\n") +} + +func (s *ExtCommunitySet) MarshalJSON() ([]byte, error) { + return json.Marshal(s.ToConfig()) +} + func NewExtCommunitySet(c config.ExtCommunitySet) (*ExtCommunitySet, error) { name := c.ExtCommunitySetName if name == "" { @@ -982,17 +1082,29 @@ type LargeCommunitySet struct { regExpSet } -func (s *LargeCommunitySet) ToConfig() *config.LargeCommunitySet { +func (s *LargeCommunitySet) List() []string { list := make([]string, 0, len(s.list)) for _, exp := range s.list { list = append(list, exp.String()) } + return list +} + +func (s *LargeCommunitySet) ToConfig() *config.LargeCommunitySet { return &config.LargeCommunitySet{ LargeCommunitySetName: s.name, - LargeCommunityList: list, + LargeCommunityList: s.List(), } } +func (s *LargeCommunitySet) String() string { + return strings.Join(s.List(), "\n") +} + +func (s *LargeCommunitySet) MarshalJSON() ([]byte, error) { + return json.Marshal(s.ToConfig()) +} + func ParseLargeCommunityRegexp(arg string) (*regexp.Regexp, error) { if regexp.MustCompile("\\d+:\\d+:\\d+").MatchString(arg) { return regexp.MustCompile(fmt.Sprintf("^%s$", arg)), nil @@ -1037,7 +1149,6 @@ type Condition interface { } type PrefixCondition struct { - name string set *PrefixSet option MatchOption } @@ -1096,7 +1207,7 @@ func (c *PrefixCondition) Evaluate(path *Path, _ *PolicyOptions) bool { return result } -func (c *PrefixCondition) Name() string { return c.name } +func (c *PrefixCondition) Name() string { return c.set.name } func NewPrefixCondition(c config.MatchPrefixSet) (*PrefixCondition, error) { if c.PrefixSet == "" { @@ -1107,13 +1218,14 @@ func NewPrefixCondition(c config.MatchPrefixSet) (*PrefixCondition, error) { return nil, err } return &PrefixCondition{ - name: c.PrefixSet, + set: &PrefixSet{ + name: c.PrefixSet, + }, option: o, }, nil } type NeighborCondition struct { - name string set *NeighborSet option MatchOption } @@ -1165,7 +1277,7 @@ func (c *NeighborCondition) Evaluate(path *Path, options *PolicyOptions) bool { return result } -func (c *NeighborCondition) Name() string { return c.name } +func (c *NeighborCondition) Name() string { return c.set.name } func NewNeighborCondition(c config.MatchNeighborSet) (*NeighborCondition, error) { if c.NeighborSet == "" { @@ -1176,13 +1288,14 @@ func NewNeighborCondition(c config.MatchNeighborSet) (*NeighborCondition, error) return nil, err } return &NeighborCondition{ - name: c.NeighborSet, + set: &NeighborSet{ + name: c.NeighborSet, + }, option: o, }, nil } type AsPathCondition struct { - name string set *AsPathSet option MatchOption } @@ -1236,7 +1349,7 @@ func (c *AsPathCondition) Evaluate(path *Path, _ *PolicyOptions) bool { return true } -func (c *AsPathCondition) Name() string { return c.name } +func (c *AsPathCondition) Name() string { return c.set.name } func NewAsPathCondition(c config.MatchAsPathSet) (*AsPathCondition, error) { if c.AsPathSet == "" { @@ -1247,13 +1360,14 @@ func NewAsPathCondition(c config.MatchAsPathSet) (*AsPathCondition, error) { return nil, err } return &AsPathCondition{ - name: c.AsPathSet, + set: &AsPathSet{ + name: c.AsPathSet, + }, option: o, }, nil } type CommunityCondition struct { - name string set *CommunitySet option MatchOption } @@ -1294,7 +1408,7 @@ func (c *CommunityCondition) Evaluate(path *Path, _ *PolicyOptions) bool { return result } -func (c *CommunityCondition) Name() string { return c.name } +func (c *CommunityCondition) Name() string { return c.set.name } func NewCommunityCondition(c config.MatchCommunitySet) (*CommunityCondition, error) { if c.CommunitySet == "" { @@ -1305,13 +1419,16 @@ func NewCommunityCondition(c config.MatchCommunitySet) (*CommunityCondition, err return nil, err } return &CommunityCondition{ - name: c.CommunitySet, + set: &CommunitySet{ + regExpSet: regExpSet{ + name: c.CommunitySet, + }, + }, option: o, }, nil } type ExtCommunityCondition struct { - name string set *ExtCommunitySet option MatchOption } @@ -1357,7 +1474,7 @@ func (c *ExtCommunityCondition) Evaluate(path *Path, _ *PolicyOptions) bool { return result } -func (c *ExtCommunityCondition) Name() string { return c.name } +func (c *ExtCommunityCondition) Name() string { return c.set.name } func NewExtCommunityCondition(c config.MatchExtCommunitySet) (*ExtCommunityCondition, error) { if c.ExtCommunitySet == "" { @@ -1368,13 +1485,16 @@ func NewExtCommunityCondition(c config.MatchExtCommunitySet) (*ExtCommunityCondi return nil, err } return &ExtCommunityCondition{ - name: c.ExtCommunitySet, + set: &ExtCommunitySet{ + regExpSet: regExpSet{ + name: c.ExtCommunitySet, + }, + }, option: o, }, nil } type LargeCommunityCondition struct { - name string set *LargeCommunitySet option MatchOption } @@ -1415,7 +1535,7 @@ func (c *LargeCommunityCondition) Evaluate(path *Path, _ *PolicyOptions) bool { return result } -func (c *LargeCommunityCondition) Name() string { return c.name } +func (c *LargeCommunityCondition) Name() string { return c.set.name } func NewLargeCommunityCondition(c config.MatchLargeCommunitySet) (*LargeCommunityCondition, error) { if c.LargeCommunitySet == "" { @@ -1426,7 +1546,11 @@ func NewLargeCommunityCondition(c config.MatchLargeCommunitySet) (*LargeCommunit return nil, err } return &LargeCommunityCondition{ - name: c.LargeCommunitySet, + set: &LargeCommunitySet{ + regExpSet: regExpSet{ + name: c.LargeCommunitySet, + }, + }, option: o, }, nil } @@ -1464,6 +1588,10 @@ func (c *AsPathLengthCondition) Set() DefinedSet { func (c *AsPathLengthCondition) Name() string { return "" } +func (c *AsPathLengthCondition) String() string { + return fmt.Sprintf("%s%d", c.operator, c.length) +} + func NewAsPathLengthCondition(c config.AsPathLength) (*AsPathLengthCondition, error) { if c.Value == 0 && c.Operator == "" { return nil, nil @@ -1500,6 +1628,10 @@ func (c *RpkiValidationCondition) Set() DefinedSet { func (c *RpkiValidationCondition) Name() string { return "" } +func (c *RpkiValidationCondition) String() string { + return string(c.result) +} + func NewRpkiValidationCondition(c config.RpkiValidationResultType) (*RpkiValidationCondition, error) { if c == config.RPKI_VALIDATION_RESULT_TYPE_NONE { return nil, nil @@ -1535,6 +1667,10 @@ func (c *RouteTypeCondition) Set() DefinedSet { func (c *RouteTypeCondition) Name() string { return "" } +func (c *RouteTypeCondition) String() string { + return string(c.typ) +} + func NewRouteTypeCondition(c config.RouteType) (*RouteTypeCondition, error) { if string(c) == "" || c == config.ROUTE_TYPE_NONE { return nil, nil @@ -1550,6 +1686,7 @@ func NewRouteTypeCondition(c config.RouteType) (*RouteTypeCondition, error) { type Action interface { Type() ActionType Apply(*Path, *PolicyOptions) *Path + String() string } type RoutingAction struct { @@ -1567,6 +1704,14 @@ func (a *RoutingAction) Apply(path *Path, _ *PolicyOptions) *Path { return nil } +func (a *RoutingAction) String() string { + action := "reject" + if a.AcceptRoute { + action = "accept" + } + return action +} + func NewRoutingAction(c config.RouteDisposition) (*RoutingAction, error) { if c.AcceptRoute == c.RejectRoute && c.AcceptRoute { return nil, fmt.Errorf("invalid route disposition") @@ -1680,6 +1825,17 @@ func (a *CommunityAction) ToConfig() *config.SetCommunity { } } +func (a *CommunityAction) MarshalJSON() ([]byte, error) { + return json.Marshal(a.ToConfig()) +} + +func (a *CommunityAction) String() string { + list := a.ToConfig().SetCommunityMethod.CommunitiesList + exp := regexp.MustCompile("[\\^\\$]") + l := exp.ReplaceAllString(strings.Join(list, ", "), "") + return fmt.Sprintf("%s[%s]", a.action, l) +} + func NewCommunityAction(c config.SetCommunity) (*CommunityAction, error) { a, ok := CommunityOptionValueMap[strings.ToLower(c.Options)] if !ok { @@ -1768,6 +1924,17 @@ func (a *ExtCommunityAction) ToConfig() *config.SetExtCommunity { } } +func (a *ExtCommunityAction) String() string { + list := a.ToConfig().SetExtCommunityMethod.CommunitiesList + exp := regexp.MustCompile("[\\^\\$]") + l := exp.ReplaceAllString(strings.Join(list, ", "), "") + return fmt.Sprintf("%s[%s]", a.action, l) +} + +func (a *ExtCommunityAction) MarshalJSON() ([]byte, error) { + return json.Marshal(a.ToConfig()) +} + func NewExtCommunityAction(c config.SetExtCommunity) (*ExtCommunityAction, error) { a, ok := CommunityOptionValueMap[strings.ToLower(c.Options)] if !ok { @@ -1846,6 +2013,17 @@ func (a *LargeCommunityAction) ToConfig() *config.SetLargeCommunity { } } +func (a *LargeCommunityAction) String() string { + list := a.ToConfig().SetLargeCommunityMethod.CommunitiesList + exp := regexp.MustCompile("[\\^\\$]") + l := exp.ReplaceAllString(strings.Join(list, ", "), "") + return fmt.Sprintf("%s[%s]", a.action, l) +} + +func (a *LargeCommunityAction) MarshalJSON() ([]byte, error) { + return json.Marshal(a.ToConfig()) +} + func NewLargeCommunityAction(c config.SetLargeCommunity) (*LargeCommunityAction, error) { a, ok := CommunityOptionValueMap[strings.ToLower(string(c.Options))] if !ok { @@ -1919,6 +2097,14 @@ func (a *MedAction) ToConfig() config.BgpSetMedType { return config.BgpSetMedType(fmt.Sprintf("%d", a.value)) } +func (a *MedAction) String() string { + return string(a.ToConfig()) +} + +func (a *MedAction) MarshalJSON() ([]byte, error) { + return json.Marshal(a.ToConfig()) +} + func NewMedAction(c config.BgpSetMedType) (*MedAction, error) { if string(c) == "" { return nil, nil @@ -1961,6 +2147,14 @@ func (a *LocalPrefAction) ToConfig() uint32 { return a.value } +func (a *LocalPrefAction) String() string { + return fmt.Sprintf("%d", a.value) +} + +func (a *LocalPrefAction) MarshalJSON() ([]byte, error) { + return json.Marshal(a.ToConfig()) +} + func NewLocalPrefAction(value uint32) (*LocalPrefAction, error) { if value == 0 { return nil, nil @@ -2020,6 +2214,15 @@ func (a *AsPathPrependAction) ToConfig() *config.SetAsPathPrepend { } } +func (a *AsPathPrependAction) String() string { + c := a.ToConfig() + return fmt.Sprintf("prepend %s %d times", c.As, c.RepeatN) +} + +func (a *AsPathPrependAction) MarshalJSON() ([]byte, error) { + return json.Marshal(a.ToConfig()) +} + // NewAsPathPrependAction creates AsPathPrependAction object. // If ASN cannot be parsed, nil will be returned. func NewAsPathPrependAction(action config.SetAsPathPrepend) (*AsPathPrependAction, error) { @@ -2071,8 +2274,16 @@ func (a *NexthopAction) ToConfig() config.BgpNextHopType { return config.BgpNextHopType(a.value.String()) } +func (a *NexthopAction) String() string { + return string(a.ToConfig()) +} + +func (a *NexthopAction) MarshalJSON() ([]byte, error) { + return json.Marshal(a.ToConfig()) +} + func NewNexthopAction(c config.BgpNextHopType) (*NexthopAction, error) { - switch string(c) { + switch strings.ToLower(string(c)) { case "": return nil, nil case "self": @@ -2201,6 +2412,10 @@ func (s *Statement) ToConfig() *config.Statement { } } +func (s *Statement) MarshalJSON() ([]byte, error) { + return json.Marshal(s.ToConfig()) +} + type opType int const ( @@ -2483,6 +2698,10 @@ func (lhs *Policy) Replace(rhs *Policy) error { return nil } +func (p *Policy) MarshalJSON() ([]byte, error) { + return json.Marshal(p.ToConfig()) +} + func NewPolicy(c config.PolicyDefinition) (*Policy, error) { if c.Name == "" { return nil, fmt.Errorf("empty policy name") @@ -2508,6 +2727,20 @@ func NewPolicy(c config.PolicyDefinition) (*Policy, error) { }, nil } +type Policies []*Policy + +func (p Policies) Len() int { + return len(p) +} + +func (p Policies) Swap(i, j int) { + p[i], p[j] = p[j], p[i] +} + +func (p Policies) Less(i, j int) bool { + return p[i].Name < p[j].Name +} + type Assignment struct { inPolicies []*Policy defaultInPolicy RouteType @@ -3362,3 +3595,10 @@ func CanImportToVrf(v *Vrf, path *Path) bool { c.set = set return c.Evaluate(path, nil) } + +type PolicyAssignment struct { + Name string + Type PolicyDirection + Policies []*Policy + Default RouteType +} diff --git a/test/lib/base.py b/test/lib/base.py index c37fe75d..10b50b46 100644 --- a/test/lib/base.py +++ b/test/lib/base.py @@ -29,9 +29,9 @@ DEFAULT_TEST_BASE_DIR = '/tmp/gobgp' TEST_PREFIX = DEFAULT_TEST_PREFIX TEST_BASE_DIR = DEFAULT_TEST_BASE_DIR -BGP_FSM_IDLE = 'BGP_FSM_IDLE' -BGP_FSM_ACTIVE = 'BGP_FSM_ACTIVE' -BGP_FSM_ESTABLISHED = 'BGP_FSM_ESTABLISHED' +BGP_FSM_IDLE = 'idle' +BGP_FSM_ACTIVE = 'active' +BGP_FSM_ESTABLISHED = 'established' BGP_ATTR_TYPE_ORIGIN = 1 BGP_ATTR_TYPE_AS_PATH = 2 |