summaryrefslogtreecommitdiffhomepage
path: root/api/grpc_server.go
diff options
context:
space:
mode:
Diffstat (limited to 'api/grpc_server.go')
-rw-r--r--api/grpc_server.go1673
1 files changed, 1673 insertions, 0 deletions
diff --git a/api/grpc_server.go b/api/grpc_server.go
new file mode 100644
index 00000000..69864831
--- /dev/null
+++ b/api/grpc_server.go
@@ -0,0 +1,1673 @@
+// 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 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"
+ "regexp"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+)
+
+type Server struct {
+ bgpServer *server.BgpServer
+ grpcServer *grpc.Server
+ hosts string
+}
+
+func NewGrpcServer(b *server.BgpServer, hosts string) *Server {
+ grpc.EnableTracing = false
+ grpcServer := grpc.NewServer()
+ server := &Server{
+ bgpServer: b,
+ grpcServer: grpcServer,
+ hosts: hosts,
+ }
+ RegisterGobgpApiServer(grpcServer, server)
+ return server
+}
+
+func (s *Server) Serve() error {
+ var wg sync.WaitGroup
+ l := strings.Split(s.hosts, ",")
+ wg.Add(len(l))
+
+ serve := func(host string) {
+ for {
+ defer wg.Done()
+ lis, err := net.Listen("tcp", fmt.Sprintf(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 (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),
+ })
+ }
+ }
+
+ timer := pconf.Timers
+ s := pconf.State
+ return &Peer{
+ Conf: &PeerConf{
+ NeighborAddress: pconf.Config.NeighborAddress,
+ Id: s.Description,
+ 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: s.Capabilities.RemoteList,
+ LocalCap: s.Capabilities.LocalList,
+ PrefixLimits: prefixLimits,
+ },
+ 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),
+ },
+ 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,
+ },
+ }
+ }
+
+ p := []*Peer{}
+ for _, e := range s.bgpServer.GetNeighbor() {
+ p = append(p, toApi(e))
+ }
+ return &GetNeighborResponse{Peers: p}, nil
+}
+
+func toPathApi(id string, 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(id) == 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 (s *Server) GetRib(ctx context.Context, arg *GetRibRequest) (*GetRibResponse, error) {
+ f := func() []*server.LookupPrefix {
+ l := make([]*server.LookupPrefix, 0, len(arg.Table.Destinations))
+ for _, p := range arg.Table.Destinations {
+ l = append(l, &server.LookupPrefix{
+ Prefix: p.Prefix,
+ LookupOption: func() server.LookupOption {
+ if p.LongerPrefixes {
+ return server.LOOKUP_LONGER
+ } else if p.ShorterPrefixes {
+ return server.LOOKUP_SHORTER
+ }
+ return server.LOOKUP_EXACT
+ }(),
+ })
+ }
+ return l
+ }
+
+ var in bool
+ var err error
+ var id string
+ var r map[string][]*table.Path
+
+ family := bgp.RouteFamily(arg.Table.Family)
+ switch arg.Table.Type {
+ case Resource_LOCAL, Resource_GLOBAL:
+ id, r, err = s.bgpServer.GetRib(arg.Table.Name, family, f())
+ case Resource_ADJ_IN:
+ in = true
+ fallthrough
+ case Resource_ADJ_OUT:
+ id, r, err = s.bgpServer.GetAdjRib(arg.Table.Name, family, in, f())
+ case Resource_VRF:
+ id, r, err = s.bgpServer.GetVrfRib(arg.Table.Name, family, []*server.LookupPrefix{})
+ default:
+ return nil, fmt.Errorf("unsupported resource type: %v", arg.Table.Type)
+ }
+
+ dsts := make([]*Destination, 0, len(r))
+ if err == nil {
+ for k, v := range r {
+ dsts = append(dsts, &Destination{
+ Prefix: k,
+ Paths: func(paths []*table.Path) []*Path {
+ l := make([]*Path, 0, len(v))
+ for i, p := range paths {
+ pp := toPathApi(id, p)
+ switch arg.Table.Type {
+ case Resource_LOCAL, Resource_GLOBAL:
+ if i == 0 {
+ pp.Best = true
+ }
+ }
+ l = append(l, pp)
+ }
+ return l
+ }(v),
+ })
+ }
+ }
+ return &GetRibResponse{Table: &Table{
+ Type: arg.Table.Type,
+ Family: arg.Table.Family,
+ Destinations: dsts},
+ }, err
+}
+
+func (s *Server) MonitorRib(arg *Table, stream GobgpApi_MonitorRibServer) error {
+ w, err := func() (*server.Watcher, error) {
+ switch arg.Type {
+ case Resource_GLOBAL:
+ return s.bgpServer.Watch(server.WatchBestPath()), nil
+ case Resource_ADJ_IN:
+ if arg.PostPolicy {
+ return s.bgpServer.Watch(server.WatchPostUpdate(false)), nil
+ }
+ return s.bgpServer.Watch(server.WatchUpdate(false)), nil
+ default:
+ return nil, fmt.Errorf("unsupported resource type: %v", arg.Type)
+ }
+ }()
+ if err != nil {
+ return nil
+ }
+
+ return func() error {
+ defer func() { w.Stop() }()
+
+ sendPath := func(pathList []*table.Path) error {
+ dsts := make(map[string]*Destination)
+ for _, path := range pathList {
+ if path == nil {
+ continue
+ }
+ if dst, y := dsts[path.GetNlri().String()]; y {
+ dst.Paths = append(dst.Paths, toPathApi(table.GLOBAL_RIB_NAME, path))
+ } else {
+ dsts[path.GetNlri().String()] = &Destination{
+ Prefix: path.GetNlri().String(),
+ Paths: []*Path{toPathApi(table.GLOBAL_RIB_NAME, path)},
+ }
+ }
+ }
+ for _, dst := range dsts {
+ if err := stream.Send(dst); err != nil {
+ return err
+ }
+ }
+ return nil
+ }
+ for {
+ select {
+ case ev := <-w.Event():
+ switch msg := ev.(type) {
+ case *server.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 *server.WatchEventUpdate:
+ if err := sendPath(msg.PathList); err != nil {
+ return err
+ }
+ }
+ }
+ }
+ }()
+}
+
+func (s *Server) MonitorPeerState(arg *Arguments, stream GobgpApi_MonitorPeerStateServer) error {
+ return func() error {
+ w := s.bgpServer.Watch(server.WatchPeerState(false))
+ defer func() { w.Stop() }()
+
+ for {
+ select {
+ case ev := <-w.Event():
+ switch msg := ev.(type) {
+ case *server.WatchEventPeerState:
+ if len(arg.Name) > 0 && arg.Name != msg.PeerAddress.String() {
+ continue
+ }
+ if err := stream.Send(&Peer{
+ Conf: &PeerConf{
+ PeerAs: msg.PeerAS,
+ LocalAs: msg.LocalAS,
+ NeighborAddress: msg.PeerAddress.String(),
+ Id: msg.PeerID.String(),
+ },
+ Info: &PeerState{
+ PeerAs: msg.PeerAS,
+ LocalAs: msg.LocalAS,
+ NeighborAddress: msg.PeerAddress.String(),
+ BgpState: msg.State.String(),
+ AdminState: msg.AdminState.String(),
+ },
+ Transport: &Transport{
+ LocalAddress: msg.LocalAddress.String(),
+ LocalPort: uint32(msg.LocalPort),
+ RemotePort: uint32(msg.PeerPort),
+ },
+ }); err != nil {
+ return err
+ }
+ }
+ }
+ }
+ }()
+}
+
+func (s *Server) ResetNeighbor(ctx context.Context, arg *ResetNeighborRequest) (*ResetNeighborResponse, error) {
+ return &ResetNeighborResponse{}, s.bgpServer.ResetNeighbor(arg.Address)
+}
+
+func (s *Server) SoftResetNeighbor(ctx context.Context, arg *SoftResetNeighborRequest) (*SoftResetNeighborResponse, error) {
+ var err error
+ addr := arg.Address
+ if addr == "all" {
+ addr = ""
+ }
+ family := bgp.RouteFamily(0)
+ switch arg.Direction {
+ case SoftResetNeighborRequest_IN:
+ err = s.bgpServer.SoftResetIn(addr, family)
+ case SoftResetNeighborRequest_OUT:
+ err = s.bgpServer.SoftResetOut(addr, family)
+ default:
+ err = s.bgpServer.SoftReset(addr, family)
+ }
+ return &SoftResetNeighborResponse{}, err
+}
+
+func (s *Server) ShutdownNeighbor(ctx context.Context, arg *ShutdownNeighborRequest) (*ShutdownNeighborResponse, error) {
+ return &ShutdownNeighborResponse{}, s.bgpServer.ShutdownNeighbor(arg.Address)
+}
+
+func (s *Server) EnableNeighbor(ctx context.Context, arg *EnableNeighborRequest) (*EnableNeighborResponse, error) {
+ return &EnableNeighborResponse{}, s.bgpServer.EnableNeighbor(arg.Address)
+}
+
+func (s *Server) DisableNeighbor(ctx context.Context, arg *DisableNeighborRequest) (*DisableNeighborResponse, error) {
+ return &DisableNeighborResponse{}, s.bgpServer.DisableNeighbor(arg.Address)
+}
+
+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 (s *Server) AddPath(ctx context.Context, arg *AddPathRequest) (*AddPathResponse, error) {
+ pathList, err := s.api2PathList(arg.Resource, []*Path{arg.Path})
+ var uuid []byte
+ if err == nil {
+ uuid, err = s.bgpServer.AddPath(arg.VrfId, pathList)
+ }
+ return &AddPathResponse{Uuid: uuid}, err
+}
+
+func (s *Server) DeletePath(ctx context.Context, arg *DeletePathRequest) (*DeletePathResponse, error) {
+ pathList, err := func() ([]*table.Path, error) {
+ if arg.Path != nil {
+ arg.Path.IsWithdraw = true
+ return s.api2PathList(arg.Resource, []*Path{arg.Path})
+ }
+ return []*table.Path{}, nil
+ }()
+ if err != nil {
+ return nil, err
+ }
+ return &DeletePathResponse{}, s.bgpServer.DeletePath(arg.Uuid, bgp.RouteFamily(arg.Family), arg.VrfId, pathList)
+}
+
+func (s *Server) EnableMrt(ctx context.Context, arg *EnableMrtRequest) (*EnableMrtResponse, error) {
+ return &EnableMrtResponse{}, s.bgpServer.EnableMrt(&config.Mrt{
+ Interval: arg.Interval,
+ DumpType: config.IntToMrtTypeMap[int(arg.DumpType)],
+ FileName: arg.Filename,
+ })
+}
+
+func (s *Server) DisableMrt(ctx context.Context, arg *DisableMrtRequest) (*DisableMrtResponse, error) {
+ return &DisableMrtResponse{}, s.bgpServer.DisableMrt()
+}
+
+func (s *Server) InjectMrt(stream GobgpApi_InjectMrtServer) error {
+ for {
+ arg, err := stream.Recv()
+
+ if err == io.EOF {
+ break
+ } else if err != nil {
+ return err
+ }
+
+ if arg.Resource != Resource_GLOBAL && arg.Resource != 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(&InjectMrtResponse{})
+}
+
+func (s *Server) AddBmp(ctx context.Context, arg *AddBmpRequest) (*AddBmpResponse, error) {
+ return &AddBmpResponse{}, s.bgpServer.AddBmp(&config.BmpServerConfig{
+ Address: arg.Address,
+ Port: arg.Port,
+ RouteMonitoringPolicy: config.BmpRouteMonitoringPolicyType(arg.Type),
+ })
+}
+
+func (s *Server) DeleteBmp(ctx context.Context, arg *DeleteBmpRequest) (*DeleteBmpResponse, error) {
+ return &DeleteBmpResponse{}, s.bgpServer.DeleteBmp(&config.BmpServerConfig{
+ Address: arg.Address,
+ Port: arg.Port,
+ })
+}
+
+func (s *Server) ValidateRib(ctx context.Context, arg *ValidateRibRequest) (*ValidateRibResponse, error) {
+ return &ValidateRibResponse{}, s.bgpServer.ValidateRib(arg.Prefix)
+}
+
+func (s *Server) AddRpki(ctx context.Context, arg *AddRpkiRequest) (*AddRpkiResponse, error) {
+ return &AddRpkiResponse{}, s.bgpServer.AddRpki(&config.RpkiServerConfig{
+ Address: arg.Address,
+ Port: arg.Port,
+ RecordLifetime: arg.Lifetime,
+ })
+}
+
+func (s *Server) DeleteRpki(ctx context.Context, arg *DeleteRpkiRequest) (*DeleteRpkiResponse, error) {
+ return &DeleteRpkiResponse{}, s.bgpServer.DeleteRpki(&config.RpkiServerConfig{
+ Address: arg.Address,
+ Port: arg.Port,
+ })
+}
+
+func (s *Server) EnableRpki(ctx context.Context, arg *EnableRpkiRequest) (*EnableRpkiResponse, error) {
+ return &EnableRpkiResponse{}, s.bgpServer.EnableRpki(&config.RpkiServerConfig{
+ Address: arg.Address,
+ })
+}
+
+func (s *Server) DisableRpki(ctx context.Context, arg *DisableRpkiRequest) (*DisableRpkiResponse, error) {
+ return &DisableRpkiResponse{}, s.bgpServer.DisableRpki(&config.RpkiServerConfig{
+ Address: arg.Address,
+ })
+}
+
+func (s *Server) ResetRpki(ctx context.Context, arg *ResetRpkiRequest) (*ResetRpkiResponse, error) {
+ return &ResetRpkiResponse{}, s.bgpServer.ResetRpki(&config.RpkiServerConfig{
+ Address: arg.Address,
+ })
+}
+
+func (s *Server) SoftResetRpki(ctx context.Context, arg *SoftResetRpkiRequest) (*SoftResetRpkiResponse, error) {
+ return &SoftResetRpkiResponse{}, s.bgpServer.SoftResetRpki(&config.RpkiServerConfig{
+ Address: arg.Address,
+ })
+}
+
+func (s *Server) GetRpki(ctx context.Context, arg *GetRpkiRequest) (*GetRpkiResponse, error) {
+ servers, err := s.bgpServer.GetRpki()
+ if err != nil {
+ return nil, err
+ }
+ l := make([]*Rpki, 0, len(servers))
+ for _, s := range servers {
+ received := &s.State.RpkiMessages.RpkiReceived
+ sent := &s.State.RpkiMessages.RpkiSent
+ rpki := &Rpki{
+ Conf: &RPKIConf{
+ Address: s.Config.Address,
+ RemotePort: strconv.Itoa(int(s.Config.Port)),
+ },
+ State: &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 &GetRpkiResponse{Servers: l}, nil
+}
+
+func (s *Server) GetRoa(ctx context.Context, arg *GetRoaRequest) (*GetRoaResponse, error) {
+ roas, err := s.bgpServer.GetRoa(bgp.RouteFamily(arg.Family))
+ if err != nil {
+ return nil, err
+ }
+ l := make([]*Roa, 0, len(roas))
+ for _, r := range roas {
+ host, port, _ := net.SplitHostPort(r.Src)
+ l = append(l, &Roa{
+ As: r.AS,
+ Maxlen: uint32(r.MaxLen),
+ Prefixlen: uint32(r.Prefix.Length),
+ Prefix: r.Prefix.Prefix.String(),
+ Conf: &RPKIConf{
+ Address: host,
+ RemotePort: port,
+ },
+ })
+ }
+ return &GetRoaResponse{Roas: l}, nil
+}
+
+func (s *Server) GetVrf(ctx context.Context, arg *GetVrfRequest) (*GetVrfResponse, error) {
+ toApi := func(v *table.Vrf) *Vrf {
+ f := func(rts []bgp.ExtendedCommunityInterface) [][]byte {
+ ret := make([][]byte, 0, len(rts))
+ for _, rt := range rts {
+ b, _ := rt.Serialize()
+ ret = append(ret, b)
+ }
+ return ret
+ }
+ rd, _ := v.Rd.Serialize()
+ return &Vrf{
+ Name: v.Name,
+ Rd: rd,
+ ImportRt: f(v.ImportRt),
+ ExportRt: f(v.ExportRt),
+ }
+ }
+ vrfs := s.bgpServer.GetVrf()
+ l := make([]*Vrf, 0, len(vrfs))
+ for _, v := range vrfs {
+ l = append(l, toApi(v))
+ }
+ return &GetVrfResponse{Vrfs: l}, nil
+}
+
+func (s *Server) AddVrf(ctx context.Context, arg *AddVrfRequest) (r *AddVrfResponse, err error) {
+ rd := bgp.GetRouteDistinguisher(arg.Vrf.Rd)
+ 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
+ }
+ im, err := f(arg.Vrf.ImportRt)
+ if err != nil {
+ return &AddVrfResponse{}, err
+ }
+ ex, err := f(arg.Vrf.ExportRt)
+ if err != nil {
+ return &AddVrfResponse{}, err
+ }
+ return &AddVrfResponse{}, s.bgpServer.AddVrf(arg.Vrf.Name, rd, im, ex)
+}
+
+func (s *Server) DeleteVrf(ctx context.Context, arg *DeleteVrfRequest) (*DeleteVrfResponse, error) {
+ 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
+
+ if pconf.Config.PeerAs != pconf.Config.LocalAs {
+ pconf.Config.PeerType = config.PEER_TYPE_EXTERNAL
+ } else {
+ pconf.Config.PeerType = config.PEER_TYPE_INTERNAL
+ }
+ 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
+ }
+ 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)
+ }
+ } else {
+ pconf.Timers.Config.ConnectRetry = float64(config.DEFAULT_CONNECT_RETRY)
+ pconf.Timers.Config.HoldTime = float64(config.DEFAULT_HOLDTIME)
+ pconf.Timers.Config.KeepaliveInterval = float64(config.DEFAULT_HOLDTIME / 3)
+ }
+ 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.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.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.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)
+ }
+ } else {
+ if net.ParseIP(a.Conf.NeighborAddress).To4() != nil {
+ pconf.AfiSafis = []config.AfiSafi{
+ config.AfiSafi{
+ Config: config.AfiSafiConfig{
+ AfiSafiName: "ipv4-unicast",
+ },
+ },
+ }
+ } else {
+ pconf.AfiSafis = []config.AfiSafi{
+ config.AfiSafi{
+ Config: config.AfiSafiConfig{
+ AfiSafiName: "ipv6-unicast",
+ },
+ },
+ }
+ }
+ }
+ if a.Transport != nil {
+ pconf.Transport.Config.LocalAddress = a.Transport.LocalAddress
+ pconf.Transport.Config.PassiveMode = a.Transport.PassiveMode
+ } else {
+ if net.ParseIP(a.Conf.NeighborAddress).To4() != nil {
+ pconf.Transport.Config.LocalAddress = "0.0.0.0"
+ } else {
+ pconf.Transport.Config.LocalAddress = "::"
+ }
+ }
+ if a.EbgpMultihop != nil {
+ pconf.EbgpMultihop.Config.Enabled = a.EbgpMultihop.Enabled
+ pconf.EbgpMultihop.Config.MultihopTtl = uint8(a.EbgpMultihop.MultihopTtl)
+ }
+ return pconf, nil
+ }(arg.Peer)
+ if err != nil {
+ return nil, err
+ }
+ return &AddNeighborResponse{}, s.bgpServer.AddNeighbor(&c)
+}
+
+func (s *Server) DeleteNeighbor(ctx context.Context, arg *DeleteNeighborRequest) (*DeleteNeighborResponse, error) {
+ return &DeleteNeighborResponse{}, s.bgpServer.DeleteNeighbor(&config.Neighbor{Config: config.NeighborConfig{
+ NeighborAddress: arg.Peer.Conf.NeighborAddress,
+ }})
+}
+
+func NewPrefixFromApiStruct(a *Prefix) (*table.Prefix, error) {
+ addr, prefix, err := net.ParseCIDR(a.IpPrefix)
+ if err != nil {
+ return nil, err
+ }
+ rf := bgp.RF_IPv4_UC
+ if addr.To4() == nil {
+ rf = bgp.RF_IPv6_UC
+ }
+ return &table.Prefix{
+ Prefix: prefix,
+ AddressFamily: rf,
+ MasklengthRangeMin: uint8(a.MaskLengthMin),
+ MasklengthRangeMax: uint8(a.MaskLengthMax),
+ }, nil
+}
+
+func NewDefinedSetFromApiStruct(a *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.IP, 0, len(a.List))
+ for _, x := range a.List {
+ addr := net.ParseIP(x)
+ if addr == nil {
+ return nil, fmt.Errorf("invalid ip address format: %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,
+ })
+ default:
+ return nil, fmt.Errorf("invalid defined type")
+ }
+}
+
+func (s *Server) GetDefinedSet(ctx context.Context, arg *GetDefinedSetRequest) (*GetDefinedSetResponse, error) {
+ cd, err := s.bgpServer.GetDefinedSet(table.DefinedType(arg.Type))
+ if err != nil {
+ return nil, err
+ }
+ sets := make([]*DefinedSet, 0)
+ for _, cs := range cd.PrefixSets {
+ ad := &DefinedSet{
+ Type: DefinedType_PREFIX,
+ Name: cs.PrefixSetName,
+ Prefixes: func() []*Prefix {
+ l := make([]*Prefix, 0, len(cs.PrefixList))
+ for _, p := range cs.PrefixList {
+ exp := regexp.MustCompile("(\\d+)\\.\\.(\\d+)")
+ elems := exp.FindStringSubmatch(p.MasklengthRange)
+ min, _ := strconv.Atoi(elems[1])
+ max, _ := strconv.Atoi(elems[2])
+
+ l = append(l, &Prefix{IpPrefix: p.IpPrefix, MaskLengthMin: uint32(min), MaskLengthMax: uint32(max)})
+ }
+ return l
+ }(),
+ }
+ sets = append(sets, ad)
+
+ }
+ for _, cs := range cd.NeighborSets {
+ ad := &DefinedSet{
+ Type: DefinedType_NEIGHBOR,
+ Name: cs.NeighborSetName,
+ List: cs.NeighborInfoList,
+ }
+ sets = append(sets, ad)
+ }
+ for _, cs := range cd.BgpDefinedSets.CommunitySets {
+ ad := &DefinedSet{
+ Type: DefinedType_COMMUNITY,
+ Name: cs.CommunitySetName,
+ List: cs.CommunityList,
+ }
+ sets = append(sets, ad)
+ }
+ for _, cs := range cd.BgpDefinedSets.ExtCommunitySets {
+ ad := &DefinedSet{
+ Type: DefinedType_EXT_COMMUNITY,
+ Name: cs.ExtCommunitySetName,
+ List: cs.ExtCommunityList,
+ }
+ sets = append(sets, ad)
+ }
+ for _, cs := range cd.BgpDefinedSets.AsPathSets {
+ ad := &DefinedSet{
+ Type: DefinedType_AS_PATH,
+ Name: cs.AsPathSetName,
+ List: cs.AsPathList,
+ }
+ sets = append(sets, ad)
+ }
+
+ return &GetDefinedSetResponse{Sets: sets}, nil
+}
+
+func (s *Server) AddDefinedSet(ctx context.Context, arg *AddDefinedSetRequest) (*AddDefinedSetResponse, error) {
+ set, err := NewDefinedSetFromApiStruct(arg.Set)
+ if err != nil {
+ return nil, err
+ }
+ return &AddDefinedSetResponse{}, s.bgpServer.AddDefinedSet(set)
+}
+
+func (s *Server) DeleteDefinedSet(ctx context.Context, arg *DeleteDefinedSetRequest) (*DeleteDefinedSetResponse, error) {
+ set, err := NewDefinedSetFromApiStruct(arg.Set)
+ if err != nil {
+ return nil, err
+ }
+ return &DeleteDefinedSetResponse{}, s.bgpServer.DeleteDefinedSet(set, arg.All)
+}
+
+func (s *Server) ReplaceDefinedSet(ctx context.Context, arg *ReplaceDefinedSetRequest) (*ReplaceDefinedSetResponse, error) {
+ set, err := NewDefinedSetFromApiStruct(arg.Set)
+ if err != nil {
+ return nil, err
+ }
+ return &ReplaceDefinedSetResponse{}, s.bgpServer.ReplaceDefinedSet(set)
+}
+
+func toStatementApi(s *config.Statement) *Statement {
+ cs := &Conditions{}
+ if s.Conditions.MatchPrefixSet.PrefixSet != "" {
+ cs.PrefixSet = &MatchSet{
+ Type: MatchType(s.Conditions.MatchPrefixSet.MatchSetOptions.ToInt()),
+ Name: s.Conditions.MatchPrefixSet.PrefixSet,
+ }
+ }
+ if s.Conditions.MatchNeighborSet.NeighborSet != "" {
+ cs.NeighborSet = &MatchSet{
+ Type: MatchType(s.Conditions.MatchNeighborSet.MatchSetOptions.ToInt()),
+ Name: s.Conditions.MatchNeighborSet.NeighborSet,
+ }
+ }
+ if s.Conditions.BgpConditions.AsPathLength.Operator != "" {
+ cs.AsPathLength = &AsPathLength{
+ Length: s.Conditions.BgpConditions.AsPathLength.Value,
+ Type: AsPathLengthType(s.Conditions.BgpConditions.AsPathLength.Operator.ToInt()),
+ }
+ }
+ if s.Conditions.BgpConditions.MatchAsPathSet.AsPathSet != "" {
+ cs.AsPathSet = &MatchSet{
+ Type: MatchType(s.Conditions.BgpConditions.MatchAsPathSet.MatchSetOptions.ToInt()),
+ Name: s.Conditions.BgpConditions.MatchAsPathSet.AsPathSet,
+ }
+ }
+ if s.Conditions.BgpConditions.MatchCommunitySet.CommunitySet != "" {
+ cs.CommunitySet = &MatchSet{
+ Type: MatchType(s.Conditions.BgpConditions.MatchCommunitySet.MatchSetOptions.ToInt()),
+ Name: s.Conditions.BgpConditions.MatchCommunitySet.CommunitySet,
+ }
+ }
+ if s.Conditions.BgpConditions.MatchExtCommunitySet.ExtCommunitySet != "" {
+ cs.CommunitySet = &MatchSet{
+ Type: MatchType(s.Conditions.BgpConditions.MatchExtCommunitySet.MatchSetOptions.ToInt()),
+ Name: s.Conditions.BgpConditions.MatchExtCommunitySet.ExtCommunitySet,
+ }
+ }
+ cs.RpkiResult = int32(s.Conditions.BgpConditions.RpkiValidationResult.ToInt())
+ as := &Actions{
+ RouteAction: func() RouteAction {
+ if s.Actions.RouteDisposition.AcceptRoute {
+ return RouteAction_ACCEPT
+ }
+ return RouteAction_REJECT
+ }(),
+ Community: func() *CommunityAction {
+ if len(s.Actions.BgpActions.SetCommunity.SetCommunityMethod.CommunitiesList) == 0 {
+ return nil
+ }
+ return &CommunityAction{
+ Type: CommunityActionType(config.BgpSetCommunityOptionTypeToIntMap[config.BgpSetCommunityOptionType(s.Actions.BgpActions.SetCommunity.Options)]),
+ Communities: s.Actions.BgpActions.SetCommunity.SetCommunityMethod.CommunitiesList}
+ }(),
+ Med: func() *MedAction {
+ if len(string(s.Actions.BgpActions.SetMed)) == 0 {
+ return nil
+ }
+ exp := regexp.MustCompile("^(\\+|\\-)?(\\d+)$")
+ elems := exp.FindStringSubmatch(string(s.Actions.BgpActions.SetMed))
+ action := MedActionType_MED_REPLACE
+ switch elems[1] {
+ case "+", "-":
+ action = MedActionType_MED_MOD
+ }
+ value, _ := strconv.Atoi(string(s.Actions.BgpActions.SetMed))
+ return &MedAction{
+ Value: int64(value),
+ Type: action,
+ }
+ }(),
+ AsPrepend: func() *AsPrependAction {
+ if len(s.Actions.BgpActions.SetAsPathPrepend.As) == 0 {
+ return nil
+ }
+ asn := 0
+ useleft := false
+ if s.Actions.BgpActions.SetAsPathPrepend.As != "last-as" {
+ asn, _ = strconv.Atoi(s.Actions.BgpActions.SetAsPathPrepend.As)
+ } else {
+ useleft = true
+ }
+ return &AsPrependAction{
+ Asn: uint32(asn),
+ Repeat: uint32(s.Actions.BgpActions.SetAsPathPrepend.RepeatN),
+ UseLeftMost: useleft,
+ }
+ }(),
+ ExtCommunity: func() *CommunityAction {
+ if len(s.Actions.BgpActions.SetExtCommunity.SetExtCommunityMethod.CommunitiesList) == 0 {
+ return nil
+ }
+ return &CommunityAction{
+ Type: CommunityActionType(config.BgpSetCommunityOptionTypeToIntMap[config.BgpSetCommunityOptionType(s.Actions.BgpActions.SetExtCommunity.Options)]),
+ Communities: s.Actions.BgpActions.SetExtCommunity.SetExtCommunityMethod.CommunitiesList,
+ }
+ }(),
+ Nexthop: func() *NexthopAction {
+ if len(string(s.Actions.BgpActions.SetNextHop)) == 0 {
+ return nil
+ }
+
+ if string(s.Actions.BgpActions.SetNextHop) == "self" {
+ return &NexthopAction{
+ Self: true,
+ }
+ }
+ return &NexthopAction{
+ Address: string(s.Actions.BgpActions.SetNextHop),
+ }
+ }(),
+ LocalPref: func() *LocalPrefAction {
+ if s.Actions.BgpActions.SetLocalPref == 0 {
+ return nil
+ }
+ return &LocalPrefAction{Value: s.Actions.BgpActions.SetLocalPref}
+ }(),
+ }
+ return &Statement{
+ Name: s.Name,
+ Conditions: cs,
+ Actions: as,
+ }
+}
+
+func toConfigMatchSetOption(a MatchType) (config.MatchSetOptionsType, error) {
+ var typ config.MatchSetOptionsType
+ switch a {
+ case MatchType_ANY:
+ typ = config.MATCH_SET_OPTIONS_TYPE_ANY
+ case MatchType_ALL:
+ typ = config.MATCH_SET_OPTIONS_TYPE_ALL
+ case MatchType_INVERT:
+ typ = config.MATCH_SET_OPTIONS_TYPE_INVERT
+ default:
+ return typ, fmt.Errorf("invalid match type")
+ }
+ return typ, nil
+}
+
+func toConfigMatchSetOptionRestricted(a MatchType) (config.MatchSetOptionsRestrictedType, error) {
+ var typ config.MatchSetOptionsRestrictedType
+ switch a {
+ case MatchType_ANY:
+ typ = config.MATCH_SET_OPTIONS_RESTRICTED_TYPE_ANY
+ case MatchType_INVERT:
+ typ = config.MATCH_SET_OPTIONS_RESTRICTED_TYPE_INVERT
+ default:
+ return typ, fmt.Errorf("invalid match type")
+ }
+ return typ, nil
+}
+
+func NewPrefixConditionFromApiStruct(a *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 *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 *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 *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 NewCommunityConditionFromApiStruct(a *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 *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 NewRoutingActionFromApiStruct(a RouteAction) (*table.RoutingAction, error) {
+ if a == RouteAction_NONE {
+ return nil, nil
+ }
+ accept := false
+ if a == RouteAction_ACCEPT {
+ accept = true
+ }
+ return &table.RoutingAction{
+ AcceptRoute: accept,
+ }, nil
+}
+
+func NewCommunityActionFromApiStruct(a *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 *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 NewMedActionFromApiStruct(a *MedAction) (*table.MedAction, error) {
+ if a == nil {
+ return nil, nil
+ }
+ return table.NewMedActionFromApiStruct(table.MedActionType(a.Type), int(a.Value)), nil
+}
+
+func NewLocalPrefActionFromApiStruct(a *LocalPrefAction) (*table.LocalPrefAction, error) {
+ if a == nil || a.Value == 0 {
+ return nil, nil
+ }
+ return table.NewLocalPrefAction(a.Value)
+}
+
+func NewAsPathPrependActionFromApiStruct(a *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 *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 *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 NewAsPathConditionFromApiStruct(a.Conditions.AsPathSet)
+ },
+ func() (table.Condition, error) {
+ return NewCommunityConditionFromApiStruct(a.Conditions.CommunitySet)
+ },
+ func() (table.Condition, error) {
+ return NewExtCommunityConditionFromApiStruct(a.Conditions.ExtCommunitySet)
+ },
+ }
+ 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 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 *GetStatementRequest) (*GetStatementResponse, error) {
+ l := make([]*Statement, 0)
+ for _, s := range s.bgpServer.GetStatement() {
+ l = append(l, toStatementApi(s))
+ }
+ return &GetStatementResponse{Statements: l}, nil
+}
+
+func (s *Server) AddStatement(ctx context.Context, arg *AddStatementRequest) (*AddStatementResponse, error) {
+ st, err := NewStatementFromApiStruct(arg.Statement)
+ if err == nil {
+ err = s.bgpServer.AddStatement(st)
+ }
+ return &AddStatementResponse{}, err
+}
+
+func (s *Server) DeleteStatement(ctx context.Context, arg *DeleteStatementRequest) (*DeleteStatementResponse, error) {
+ st, err := NewStatementFromApiStruct(arg.Statement)
+ if err == nil {
+ err = s.bgpServer.DeleteStatement(st, arg.All)
+ }
+ return &DeleteStatementResponse{}, err
+}
+
+func (s *Server) ReplaceStatement(ctx context.Context, arg *ReplaceStatementRequest) (*ReplaceStatementResponse, error) {
+ st, err := NewStatementFromApiStruct(arg.Statement)
+ if err == nil {
+ err = s.bgpServer.ReplaceStatement(st)
+ }
+ return &ReplaceStatementResponse{}, err
+}
+
+func toPolicyApi(p *config.PolicyDefinition) *Policy {
+ return &Policy{
+ Name: p.Name,
+ Statements: func() []*Statement {
+ l := make([]*Statement, 0)
+ for _, s := range p.Statements {
+ l = append(l, toStatementApi(&s))
+ }
+ return l
+ }(),
+ }
+}
+
+func NewPolicyFromApiStruct(a *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 (s *Server) GetPolicy(ctx context.Context, arg *GetPolicyRequest) (*GetPolicyResponse, error) {
+ l := make([]*Policy, 0)
+ for _, p := range s.bgpServer.GetPolicy() {
+ l = append(l, toPolicyApi(p))
+ }
+ return &GetPolicyResponse{Policies: l}, nil
+}
+
+func (s *Server) AddPolicy(ctx context.Context, arg *AddPolicyRequest) (*AddPolicyResponse, error) {
+ x, err := NewPolicyFromApiStruct(arg.Policy)
+ if err != nil {
+ return nil, err
+ }
+ return &AddPolicyResponse{}, s.bgpServer.AddPolicy(x, arg.ReferExistingStatements)
+}
+
+func (s *Server) DeletePolicy(ctx context.Context, arg *DeletePolicyRequest) (*DeletePolicyResponse, error) {
+ x, err := NewPolicyFromApiStruct(arg.Policy)
+ if err != nil {
+ return nil, err
+ }
+ return &DeletePolicyResponse{}, s.bgpServer.DeletePolicy(x, arg.All, arg.PreserveStatements)
+}
+
+func (s *Server) ReplacePolicy(ctx context.Context, arg *ReplacePolicyRequest) (*ReplacePolicyResponse, error) {
+ x, err := NewPolicyFromApiStruct(arg.Policy)
+ if err != nil {
+ return nil, err
+ }
+ return &ReplacePolicyResponse{}, s.bgpServer.ReplacePolicy(x, arg.ReferExistingStatements, arg.PreserveStatements)
+}
+
+func toPolicyAssignmentName(a *PolicyAssignment) (string, table.PolicyDirection, error) {
+ switch a.Resource {
+ case Resource_GLOBAL:
+ switch a.Type {
+ case PolicyType_IMPORT:
+ return "", table.POLICY_DIRECTION_IMPORT, nil
+ case PolicyType_EXPORT:
+ return "", table.POLICY_DIRECTION_EXPORT, nil
+ default:
+ return "", table.POLICY_DIRECTION_NONE, fmt.Errorf("invalid policy type")
+ }
+ case Resource_LOCAL:
+ switch a.Type {
+ case PolicyType_IN:
+ return a.Name, table.POLICY_DIRECTION_IN, nil
+ case PolicyType_IMPORT:
+ return a.Name, table.POLICY_DIRECTION_IMPORT, nil
+ case 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 *GetPolicyAssignmentRequest) (*GetPolicyAssignmentResponse, error) {
+ name, dir, err := toPolicyAssignmentName(arg.Assignment)
+ if err != nil {
+ return nil, err
+ }
+ d, a, 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
+}
+
+func defaultRouteType(d RouteAction) table.RouteType {
+ switch d {
+ case RouteAction_ACCEPT:
+ return table.ROUTE_TYPE_ACCEPT
+ case RouteAction_REJECT:
+ return table.ROUTE_TYPE_REJECT
+ default:
+ return table.ROUTE_TYPE_NONE
+ }
+}
+
+func toPolicyDefinition(policies []*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 *AddPolicyAssignmentRequest) (*AddPolicyAssignmentResponse, error) {
+ name, dir, err := toPolicyAssignmentName(arg.Assignment)
+ if err != nil {
+ return nil, err
+ }
+ return &AddPolicyAssignmentResponse{}, s.bgpServer.AddPolicyAssignment(name, dir, toPolicyDefinition(arg.Assignment.Policies), defaultRouteType(arg.Assignment.Default))
+}
+
+func (s *Server) DeletePolicyAssignment(ctx context.Context, arg *DeletePolicyAssignmentRequest) (*DeletePolicyAssignmentResponse, error) {
+ name, dir, err := toPolicyAssignmentName(arg.Assignment)
+ if err != nil {
+ return nil, err
+ }
+ return &DeletePolicyAssignmentResponse{}, s.bgpServer.DeletePolicyAssignment(name, dir, toPolicyDefinition(arg.Assignment.Policies), arg.All)
+}
+
+func (s *Server) ReplacePolicyAssignment(ctx context.Context, arg *ReplacePolicyAssignmentRequest) (*ReplacePolicyAssignmentResponse, error) {
+ name, dir, err := toPolicyAssignmentName(arg.Assignment)
+ if err != nil {
+ return nil, err
+ }
+ return &ReplacePolicyAssignmentResponse{}, s.bgpServer.ReplacePolicyAssignment(name, dir, toPolicyDefinition(arg.Assignment.Policies), defaultRouteType(arg.Assignment.Default))
+}
+
+func (s *Server) GetServer(ctx context.Context, arg *GetServerRequest) (*GetServerResponse, error) {
+ g := s.bgpServer.GetServer()
+ return &GetServerResponse{
+ Global: &Global{
+ As: g.Config.As,
+ RouterId: g.Config.RouterId,
+ ListenPort: g.Config.Port,
+ ListenAddresses: g.Config.LocalAddressList,
+ MplsLabelMin: g.MplsLabelRange.MinLabel,
+ MplsLabelMax: g.MplsLabelRange.MaxLabel,
+ },
+ }, nil
+}
+
+func (s *Server) StartServer(ctx context.Context, arg *StartServerRequest) (*StartServerResponse, error) {
+ g := arg.Global
+ if net.ParseIP(g.RouterId) == nil {
+ return nil, fmt.Errorf("invalid router-id format: %s", g.RouterId)
+ }
+ families := make([]config.AfiSafi, 0, len(g.Families))
+ for _, f := range g.Families {
+ name := config.AfiSafiType(bgp.RouteFamily(f).String())
+ families = append(families, config.AfiSafi{
+ Config: config.AfiSafiConfig{
+ AfiSafiName: name,
+ Enabled: true,
+ },
+ State: config.AfiSafiState{
+ AfiSafiName: name,
+ },
+ })
+ }
+ b := &config.BgpConfigSet{
+ Global: config.Global{
+ Config: config.GlobalConfig{
+ As: g.As,
+ RouterId: g.RouterId,
+ Port: g.ListenPort,
+ LocalAddressList: g.ListenAddresses,
+ },
+ MplsLabelRange: config.MplsLabelRange{
+ MinLabel: g.MplsLabelMin,
+ MaxLabel: g.MplsLabelMax,
+ },
+ AfiSafis: families,
+ },
+ }
+ if err := config.SetDefaultConfigValues(nil, b); err != nil {
+ return nil, err
+ }
+ return &StartServerResponse{}, s.bgpServer.Start(&b.Global)
+}
+
+func (s *Server) StopServer(ctx context.Context, arg *StopServerRequest) (*StopServerResponse, error) {
+ return &StopServerResponse{}, s.bgpServer.Stop()
+}