From 4e1a077877bef3501d272d7435e2f41e2d1e3153 Mon Sep 17 00:00:00 2001 From: k1yoto Date: Tue, 8 Apr 2025 12:13:12 +0000 Subject: [PATCH 01/87] fix(getLsNodeNLRI): add srCapabilies length in error message --- internal/pkg/gobgp/interface.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/internal/pkg/gobgp/interface.go b/internal/pkg/gobgp/interface.go index 3867dbff..9d4153af 100644 --- a/internal/pkg/gobgp/interface.go +++ b/internal/pkg/gobgp/interface.go @@ -159,10 +159,11 @@ func getLsNodeNLRI(typedLinkStateNlri *api.LsNodeNLRI, pathAttrs []*anypb.Any) ( srCapabilities := bgplsAttr.GetNode().GetSrCapabilities().GetRanges() if len(srCapabilities) != 1 { - return nil, errors.New("invalid SR Capability TLV") + return nil, fmt.Errorf("expected 1 SR Capability TLV, got: %d", len(srCapabilities)) + } else { + lsNode.SrgbBegin = srCapabilities[0].GetBegin() + lsNode.SrgbEnd = srCapabilities[0].GetEnd() } - lsNode.SrgbBegin = srCapabilities[0].GetBegin() - lsNode.SrgbEnd = srCapabilities[0].GetEnd() } return lsNode, nil From e6c159f11d2e029f6ffbf2d2be4e25f7cc2b154c Mon Sep 17 00:00:00 2001 From: Motok1 Date: Wed, 27 Aug 2025 15:12:39 +0900 Subject: [PATCH 02/87] feat(gobgp,ted): add LsSRv6SIDNLRI for BGP-LS as defined in RFC9154 --- cmd/pola/ted.go | 11 ++++++ internal/pkg/gobgp/interface.go | 65 ++++++++++++++++++++++++++++++++- internal/pkg/table/ted.go | 42 ++++++++++++++++++++- 3 files changed, 116 insertions(+), 2 deletions(-) diff --git a/cmd/pola/ted.go b/cmd/pola/ted.go index d8a4c4cc..fa20ffaf 100644 --- a/cmd/pola/ted.go +++ b/cmd/pola/ted.go @@ -86,6 +86,17 @@ func print(jsonFlag bool) error { } tmpNode["prefixes"] = prefixes + srv6SIDs := []map[string]interface{}{} + for _, srv6SID := range node.SRv6SIDs { + tmpSrv6SID := map[string]interface{}{ + "sids": srv6SID.Sids, + "endpointBehavior": srv6SID.EndpointBehavior, + "multiTopoIDs": srv6SID.MultiTopoIDs, + } + srv6SIDs = append(srv6SIDs, tmpSrv6SID) + } + tmpNode["srv6SIDs"] = srv6SIDs + nodes = append(nodes, tmpNode) } } diff --git a/internal/pkg/gobgp/interface.go b/internal/pkg/gobgp/interface.go index 9d4153af..15713c16 100644 --- a/internal/pkg/gobgp/interface.go +++ b/internal/pkg/gobgp/interface.go @@ -72,7 +72,6 @@ func GetBgplsNlris(serverAddr string, serverPort string) ([]table.TedElem, error } return nil, fmt.Errorf("error receiving stream data: %v", err) } - convertedElems, err := ConvertToTedElem(r.Destination) if err != nil { return nil, fmt.Errorf("failed to convert path to TED element: %v", err) @@ -120,6 +119,15 @@ func ConvertToTedElem(dst *api.Destination) ([]table.TedElem, error) { return nil, fmt.Errorf("failed to process LS Prefix V4 NLRI: %v", err) } return lsPrefixV4List, nil + case *api.LsSrv6SIDNLRI: + lsSrv6SIDList, err := getLsSrv6SIDNLRIList(linkStateNlri, path.GetPattrs()) + if err != nil { + return nil, err + } + return lsSrv6SIDList, nil + // TODO: Implement LsPrefixV6NLRI handling + case *api.LsPrefixV6NLRI: + return nil, nil default: return nil, errors.New("invalid LS Link State NLRI type") } @@ -275,3 +283,58 @@ func getLsPrefixV4(lsNlri *api.LsAddrPrefix, sidIndex uint32) (*table.LsPrefixV4 return lsPrefixV4, nil } + +func getLsSrv6SIDNLRIList(lsSRv6SIDNlri *api.LsSrv6SIDNLRI, pathAttrs []*anypb.Any) ([]table.TedElem, error) { + var lsSrv6SIDList []table.TedElem + var endpointBehavior uint32 + + for _, pathAttr := range pathAttrs { + typedPathAttr, err := pathAttr.UnmarshalNew() + if err != nil { + return nil, err + } + + switch typedPathAttr := typedPathAttr.(type) { + case *api.SRv6EndPointBehavior: + endpointBehavior = uint32(typedPathAttr.GetBehavior()) + case *api.MpReachNLRIAttribute: + for _, nlri := range typedPathAttr.GetNlris() { + typedNlri, err := nlri.UnmarshalNew() + if err != nil { + return nil, err + } + if lsNlri, ok := typedNlri.(*api.LsAddrPrefix); ok { + lsSrv6SID, err := getLsSrv6SIDNLRI(lsNlri, endpointBehavior) + if err != nil { + return nil, err + } + lsSrv6SIDList = append(lsSrv6SIDList, lsSrv6SID) + } + } + } + } + return lsSrv6SIDList, nil +} + +func getLsSrv6SIDNLRI(lsNlri *api.LsAddrPrefix, endpointBehavior uint32) (*table.LsSrv6SID, error) { + srv6Nlri, err := lsNlri.GetNlri().UnmarshalNew() + if err != nil { + return nil, err + } + srv6SIDNlri, ok := srv6Nlri.(*api.LsSrv6SIDNLRI) + if !ok { + return nil, errors.New("invalid LS SRv6 SID NLRI type") + } + localNodeID := srv6SIDNlri.GetLocalNode().GetIgpRouterId() + localNodeAsn := srv6SIDNlri.GetLocalNode().GetAsn() + srv6SIDs := srv6SIDNlri.GetSrv6SidInformation().GetSids() + multiTopoIDs := srv6SIDNlri.GetMultiTopoId().GetMultiTopoIds() + + localNode := table.NewLsNode(localNodeAsn, localNodeID) + lsSrv6SID := table.NewLsSrv6SID(localNode) + lsSrv6SID.EndpointBehavior = endpointBehavior + lsSrv6SID.Sids = srv6SIDs + lsSrv6SID.MultiTopoIDs = multiTopoIDs + + return lsSrv6SID, nil +} diff --git a/internal/pkg/table/ted.go b/internal/pkg/table/ted.go index da278ea8..c84eee0f 100644 --- a/internal/pkg/table/ted.go +++ b/internal/pkg/table/ted.go @@ -39,7 +39,6 @@ func (ted *LsTed) Print() { fmt.Printf(" index: %d\n", prefix.SidIndex) } } - fmt.Printf(" Links:\n") for _, link := range node.Links { fmt.Printf(" Local: %s Remote: %s\n", link.LocalIP.String(), link.RemoteIP.String()) @@ -50,6 +49,13 @@ func (ted *LsTed) Print() { } fmt.Printf(" Adj-SID: %d\n", link.AdjSid) } + fmt.Printf(" SRv6 SIDs:\n") + for _, srv6SID := range node.SRv6SIDs { + fmt.Printf(" SIDs: %v\n", srv6SID.Sids) + fmt.Printf(" EndpointBehavior: %d\n", srv6SID.EndpointBehavior) + fmt.Printf(" MultiTopoIDs: %v\n", srv6SID.MultiTopoIDs) + } + nodeCnt++ fmt.Printf("\n") } @@ -69,6 +75,7 @@ type LsNode struct { SrgbEnd uint32 // in BGP-LS Attr Links []*LsLink Prefixes []*LsPrefixV4 + SRv6SIDs []*LsSrv6SID } func NewLsNode(asn uint32, nodeID string) *LsNode { @@ -205,6 +212,39 @@ func (lp *LsPrefixV4) UpdateTed(ted *LsTed) { localNode.Prefixes = append(localNode.Prefixes, lp) } +type LsSrv6SID struct { + LocalNode *LsNode // primary key, in MP_REACH_NLRI Attr + Sids []string // in LsSrv6SID Attr + EndpointBehavior uint32 // in srv6EndpointBehavior Attr + MultiTopoIDs []uint32 // in LsSrv6SID Attr +} + +func NewLsSrv6SID(node *LsNode) *LsSrv6SID { + return &LsSrv6SID{ + LocalNode: node, + } +} + +func (s *LsSrv6SID) UpdateTed(ted *LsTed) { + nodes, asn := ted.Nodes, s.LocalNode.Asn + + if _, ok := nodes[asn]; !ok { + nodes[asn] = make(map[string]*LsNode) + } + + if _, ok := nodes[asn][s.LocalNode.RouterID]; !ok { + nodes[asn][s.LocalNode.RouterID] = NewLsNode(s.LocalNode.Asn, s.LocalNode.RouterID) + } + + s.LocalNode = nodes[asn][s.LocalNode.RouterID] + + s.LocalNode.AddSrv6SID(s) +} + +func (n *LsNode) AddSrv6SID(s *LsSrv6SID) { + n.SRv6SIDs = append(n.SRv6SIDs, s) +} + type Metric struct { Type MetricType Value uint32 From b34d5a5299cbc3f897c10b2281baca62a0b270fa Mon Sep 17 00:00:00 2001 From: watal Date: Thu, 10 Apr 2025 17:34:20 +0900 Subject: [PATCH 03/87] refactor(gobgp): remove redundant argument and struct --- internal/pkg/gobgp/interface.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/internal/pkg/gobgp/interface.go b/internal/pkg/gobgp/interface.go index 15713c16..9609138a 100644 --- a/internal/pkg/gobgp/interface.go +++ b/internal/pkg/gobgp/interface.go @@ -22,11 +22,6 @@ import ( "google.golang.org/protobuf/types/known/anypb" ) -type GobgpOptions struct { - GobgpAddr string - GobgpPort string -} - func GetBgplsNlris(serverAddr string, serverPort string) ([]table.TedElem, error) { gobgpAddress := serverAddr + ":" + serverPort @@ -120,7 +115,7 @@ func ConvertToTedElem(dst *api.Destination) ([]table.TedElem, error) { } return lsPrefixV4List, nil case *api.LsSrv6SIDNLRI: - lsSrv6SIDList, err := getLsSrv6SIDNLRIList(linkStateNlri, path.GetPattrs()) + lsSrv6SIDList, err := getLsSrv6SIDNLRIList(path.GetPattrs()) if err != nil { return nil, err } @@ -284,7 +279,7 @@ func getLsPrefixV4(lsNlri *api.LsAddrPrefix, sidIndex uint32) (*table.LsPrefixV4 return lsPrefixV4, nil } -func getLsSrv6SIDNLRIList(lsSRv6SIDNlri *api.LsSrv6SIDNLRI, pathAttrs []*anypb.Any) ([]table.TedElem, error) { +func getLsSrv6SIDNLRIList(pathAttrs []*anypb.Any) ([]table.TedElem, error) { var lsSrv6SIDList []table.TedElem var endpointBehavior uint32 From 4385f853bc8d00c5a746bb7c473b6bc8e251d18e Mon Sep 17 00:00:00 2001 From: watal Date: Fri, 11 Apr 2025 17:38:31 +0900 Subject: [PATCH 04/87] refactor(gobgp): simplify address format and comment --- internal/pkg/gobgp/interface.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/internal/pkg/gobgp/interface.go b/internal/pkg/gobgp/interface.go index 9609138a..bcdd9fc9 100644 --- a/internal/pkg/gobgp/interface.go +++ b/internal/pkg/gobgp/interface.go @@ -23,9 +23,9 @@ import ( ) func GetBgplsNlris(serverAddr string, serverPort string) ([]table.TedElem, error) { - gobgpAddress := serverAddr + ":" + serverPort + gobgpAddress := fmt.Sprintf("%s:%s", serverAddr, serverPort) - // Get connection + // Establish gRPC connection cc, err := grpc.NewClient( gobgpAddress, grpc.WithTransportCredentials(insecure.NewCredentials()), @@ -120,9 +120,8 @@ func ConvertToTedElem(dst *api.Destination) ([]table.TedElem, error) { return nil, err } return lsSrv6SIDList, nil - // TODO: Implement LsPrefixV6NLRI handling case *api.LsPrefixV6NLRI: - return nil, nil + return nil, nil // TODO: Implement LsPrefixV6NLRI handling default: return nil, errors.New("invalid LS Link State NLRI type") } From 1690050daf8aa0caa53173af1d9707c158c478dd Mon Sep 17 00:00:00 2001 From: Motok1 Date: Thu, 28 Aug 2025 02:38:23 +0900 Subject: [PATCH 05/87] refactor(gobgp): improve error wrapping and context --- internal/pkg/gobgp/interface.go | 64 ++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/internal/pkg/gobgp/interface.go b/internal/pkg/gobgp/interface.go index bcdd9fc9..bb831ed2 100644 --- a/internal/pkg/gobgp/interface.go +++ b/internal/pkg/gobgp/interface.go @@ -31,11 +31,11 @@ func GetBgplsNlris(serverAddr string, serverPort string) ([]table.TedElem, error grpc.WithTransportCredentials(insecure.NewCredentials()), ) if err != nil { - return nil, fmt.Errorf("failed to create gRPC client: %v", err) + return nil, fmt.Errorf("failed to create gRPC client (address: %s): %w", gobgpAddress, err) } defer func() { if err := cc.Close(); err != nil { - fmt.Fprintf(os.Stderr, "warning: failed to close gRPC client connection: %v\n", err) + fmt.Fprintf(os.Stderr, "failed to close gRPC client connection: %v\n", err) } }() @@ -55,7 +55,7 @@ func GetBgplsNlris(serverAddr string, serverPort string) ([]table.TedElem, error stream, err := client.ListPath(ctx, req) if err != nil { - return nil, fmt.Errorf("failed to retrieve paths: %v", err) + return nil, fmt.Errorf("failed to retrieve paths from gRPC server: %w", err) } var tedElems []table.TedElem @@ -65,15 +65,17 @@ func GetBgplsNlris(serverAddr string, serverPort string) ([]table.TedElem, error if err == io.EOF { break } - return nil, fmt.Errorf("error receiving stream data: %v", err) + return nil, fmt.Errorf("error receiving stream data: %w", err) } + convertedElems, err := ConvertToTedElem(r.Destination) if err != nil { - return nil, fmt.Errorf("failed to convert path to TED element: %v", err) + return nil, fmt.Errorf("failed to convert path to TED element (destination: %v): %w", r.Destination, err) } tedElems = append(tedElems, convertedElems...) } + return tedElems, nil } @@ -85,48 +87,48 @@ func ConvertToTedElem(dst *api.Destination) ([]table.TedElem, error) { path := dst.GetPaths()[0] nlri, err := path.GetNlri().UnmarshalNew() if err != nil { - return nil, fmt.Errorf("failed to unmarshal NLRI: %v", err) + return nil, fmt.Errorf("failed to unmarshal NLRI: %w", err) } switch nlri := nlri.(type) { case *api.LsAddrPrefix: linkStateNlri, err := nlri.GetNlri().UnmarshalNew() if err != nil { - return nil, fmt.Errorf("failed to unmarshal LS Address Prefix: %v", err) + return nil, fmt.Errorf("failed to unmarshal LS Address Prefix: %w", err) } switch linkStateNlri := linkStateNlri.(type) { case *api.LsNodeNLRI: lsNode, err := getLsNodeNLRI(linkStateNlri, path.GetPattrs()) if err != nil { - return nil, fmt.Errorf("failed to process LS Node NLRI: %v", err) + return nil, fmt.Errorf("failed to process LS Node NLRI: %w", err) } return []table.TedElem{lsNode}, nil case *api.LsLinkNLRI: lsLink, err := getLsLinkNLRI(linkStateNlri, path.GetPattrs()) if err != nil { - return nil, fmt.Errorf("failed to process LS Link NLRI: %v", err) + return nil, fmt.Errorf("failed to process LS Link NLRI: %w", err) } return []table.TedElem{lsLink}, nil case *api.LsPrefixV4NLRI: lsPrefixV4List, err := getLsPrefixV4List(path.GetPattrs()) if err != nil { - return nil, fmt.Errorf("failed to process LS Prefix V4 NLRI: %v", err) + return nil, fmt.Errorf("failed to process LS Prefix V4 NLRI: %w", err) } return lsPrefixV4List, nil case *api.LsSrv6SIDNLRI: lsSrv6SIDList, err := getLsSrv6SIDNLRIList(path.GetPattrs()) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to process LS SRv6 SID NLRI: %w", err) } return lsSrv6SIDList, nil case *api.LsPrefixV6NLRI: return nil, nil // TODO: Implement LsPrefixV6NLRI handling default: - return nil, errors.New("invalid LS Link State NLRI type") + return nil, fmt.Errorf("invalid LS Link State NLRI type: %T", linkStateNlri) } default: - return nil, errors.New("invalid NLRI type") + return nil, fmt.Errorf("invalid NLRI type: %T", nlri) } } @@ -139,7 +141,7 @@ func getLsNodeNLRI(typedLinkStateNlri *api.LsNodeNLRI, pathAttrs []*anypb.Any) ( for _, pathAttr := range pathAttrs { typedPathAttr, err := pathAttr.UnmarshalNew() if err != nil { - return nil, fmt.Errorf("failed to unmarshal path attribute: %v", err) + return nil, fmt.Errorf("failed to unmarshal path attribute: %w", err) } bgplsAttr, ok := typedPathAttr.(*api.LsAttribute) @@ -177,12 +179,12 @@ func getLsLinkNLRI(typedLinkStateNlri *api.LsLinkNLRI, pathAttrs []*anypb.Any) ( localIP, err := netip.ParseAddr(typedLinkStateNlri.GetLinkDescriptor().GetInterfaceAddrIpv4()) if err != nil { - return nil, fmt.Errorf("failed to parse local IP address: %v", err) + return nil, fmt.Errorf("failed to parse local IP address %q: %v", typedLinkStateNlri.GetLinkDescriptor().GetInterfaceAddrIpv4(), err) } remoteIP, err := netip.ParseAddr(typedLinkStateNlri.GetLinkDescriptor().GetNeighborAddrIpv4()) if err != nil { - return nil, fmt.Errorf("failed to parse remote IP address: %v", err) + return nil, fmt.Errorf("failed to parse remote IP address %q: %v", typedLinkStateNlri.GetLinkDescriptor().GetNeighborAddrIpv4(), err) } lsLink := table.NewLsLink(localNode, remoteNode) @@ -192,7 +194,7 @@ func getLsLinkNLRI(typedLinkStateNlri *api.LsLinkNLRI, pathAttrs []*anypb.Any) ( for _, pathAttr := range pathAttrs { typedPathAttr, err := pathAttr.UnmarshalNew() if err != nil { - return nil, fmt.Errorf("failed to unmarshal path attribute: %v", err) + return nil, fmt.Errorf("failed to unmarshal path attribute %v: %v", pathAttr, err) } bgplsAttr, ok := typedPathAttr.(*api.LsAttribute) @@ -220,7 +222,7 @@ func getLsPrefixV4List(pathAttrs []*anypb.Any) ([]table.TedElem, error) { for _, pathAttr := range pathAttrs { typedPathAttr, err := pathAttr.UnmarshalNew() if err != nil { - return nil, fmt.Errorf("failed to unmarshal path attribute: %v", err) + return nil, fmt.Errorf("failed to unmarshal path attribute: %w", err) } switch typedPathAttr := typedPathAttr.(type) { @@ -231,15 +233,17 @@ func getLsPrefixV4List(pathAttrs []*anypb.Any) ([]table.TedElem, error) { for _, nlri := range typedPathAttr.GetNlris() { typedNlri, err := nlri.UnmarshalNew() if err != nil { - return nil, fmt.Errorf("failed to unmarshal NLRI: %v", err) + return nil, fmt.Errorf("failed to unmarshal NLRI: %w", err) } if lsNlri, ok := typedNlri.(*api.LsAddrPrefix); ok { lsPrefixV4, err := getLsPrefixV4(lsNlri, sidIndex) if err != nil { - return nil, fmt.Errorf("failed to get LS Prefix V4: %v", err) + return nil, fmt.Errorf("failed to get LS Prefix V4: %w", err) } lsPrefixV4List = append(lsPrefixV4List, lsPrefixV4) + } else { + return nil, fmt.Errorf("unexpected NLRI type: %T", typedNlri) } } } @@ -251,11 +255,11 @@ func getLsPrefixV4List(pathAttrs []*anypb.Any) ([]table.TedElem, error) { func getLsPrefixV4(lsNlri *api.LsAddrPrefix, sidIndex uint32) (*table.LsPrefixV4, error) { prefNlri, err := lsNlri.GetNlri().UnmarshalNew() if err != nil { - return nil, fmt.Errorf("failed to unmarshal LS Prefix V4: %v", err) + return nil, fmt.Errorf("failed to unmarshal LS Prefix V4: %w", err) } prefv4Nlri, ok := prefNlri.(*api.LsPrefixV4NLRI) if !ok { - return nil, errors.New("invalid LS prefix v4 NLRI type") + return nil, fmt.Errorf("invalid LS prefix v4 NLRI type: %T", prefNlri) } localNodeID := prefv4Nlri.GetLocalNode().GetIgpRouterId() @@ -267,12 +271,12 @@ func getLsPrefixV4(lsNlri *api.LsAddrPrefix, sidIndex uint32) (*table.LsPrefixV4 lsPrefixV4.SidIndex = sidIndex if len(prefixV4) != 1 { - return nil, errors.New("invalid prefix length: expected 1 prefix") + return nil, fmt.Errorf("invalid prefix length: expected 1, got %d", len(prefixV4)) } lsPrefixV4.Prefix, err = netip.ParsePrefix(prefixV4[0]) if err != nil { - return nil, fmt.Errorf("failed to parse prefix: %v", err) + return nil, fmt.Errorf("failed to parse prefix %q: %w", prefixV4[0], err) } return lsPrefixV4, nil @@ -285,7 +289,7 @@ func getLsSrv6SIDNLRIList(pathAttrs []*anypb.Any) ([]table.TedElem, error) { for _, pathAttr := range pathAttrs { typedPathAttr, err := pathAttr.UnmarshalNew() if err != nil { - return nil, err + return nil, fmt.Errorf("failed to unmarshal path attribute: %w", err) } switch typedPathAttr := typedPathAttr.(type) { @@ -295,14 +299,16 @@ func getLsSrv6SIDNLRIList(pathAttrs []*anypb.Any) ([]table.TedElem, error) { for _, nlri := range typedPathAttr.GetNlris() { typedNlri, err := nlri.UnmarshalNew() if err != nil { - return nil, err + return nil, fmt.Errorf("failed to unmarshal NLRI: %w", err) } if lsNlri, ok := typedNlri.(*api.LsAddrPrefix); ok { lsSrv6SID, err := getLsSrv6SIDNLRI(lsNlri, endpointBehavior) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to process LS SRv6 SID NLRI: %w", err) } lsSrv6SIDList = append(lsSrv6SIDList, lsSrv6SID) + } else { + return nil, fmt.Errorf("unexpected NLRI type: %T", typedNlri) } } } @@ -313,11 +319,11 @@ func getLsSrv6SIDNLRIList(pathAttrs []*anypb.Any) ([]table.TedElem, error) { func getLsSrv6SIDNLRI(lsNlri *api.LsAddrPrefix, endpointBehavior uint32) (*table.LsSrv6SID, error) { srv6Nlri, err := lsNlri.GetNlri().UnmarshalNew() if err != nil { - return nil, err + return nil, fmt.Errorf("failed to unmarshal LS NLRI: %w", err) } srv6SIDNlri, ok := srv6Nlri.(*api.LsSrv6SIDNLRI) if !ok { - return nil, errors.New("invalid LS SRv6 SID NLRI type") + return nil, fmt.Errorf("invalid LS SRv6 SID NLRI type: %T", srv6Nlri) } localNodeID := srv6SIDNlri.GetLocalNode().GetIgpRouterId() localNodeAsn := srv6SIDNlri.GetLocalNode().GetAsn() From 40d49ada33c4b9ad17d4d5f43a44f9b93c4381db Mon Sep 17 00:00:00 2001 From: watal Date: Fri, 11 Apr 2025 17:44:54 +0900 Subject: [PATCH 06/87] refactor(gobgp) use context.WithCancel to prepare for future cancellation support --- internal/pkg/gobgp/interface.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/pkg/gobgp/interface.go b/internal/pkg/gobgp/interface.go index bb831ed2..6522eed2 100644 --- a/internal/pkg/gobgp/interface.go +++ b/internal/pkg/gobgp/interface.go @@ -41,7 +41,8 @@ func GetBgplsNlris(serverAddr string, serverPort string) ([]table.TedElem, error // Create gRPC client client := api.NewGobgpApiClient(cc) - ctx := context.Background() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() req := &api.ListPathRequest{ TableType: api.TableType_GLOBAL, From a64011ce3c8fe883f485afa46c4d3c68a7477176 Mon Sep 17 00:00:00 2001 From: watal Date: Fri, 11 Apr 2025 17:45:54 +0900 Subject: [PATCH 07/87] refactor(gobgp): ISIS Area ID formatting into helper function --- internal/pkg/gobgp/interface.go | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/internal/pkg/gobgp/interface.go b/internal/pkg/gobgp/interface.go index 6522eed2..f2acf3d0 100644 --- a/internal/pkg/gobgp/interface.go +++ b/internal/pkg/gobgp/interface.go @@ -133,6 +133,19 @@ func ConvertToTedElem(dst *api.Destination) ([]table.TedElem, error) { } } +// formatIsisAreaID formats the ISIS Area ID into a human-readable string. +func formatIsisAreaID(isisArea []byte) string { + tmpIsisArea := hex.EncodeToString(isisArea) + var strIsisArea strings.Builder + for i, s := range strings.Split(tmpIsisArea, "") { + if (len(tmpIsisArea)-i)%4 == 0 && i != 0 { + strIsisArea.WriteString(".") + } + strIsisArea.WriteString(s) + } + return strIsisArea.String() +} + func getLsNodeNLRI(typedLinkStateNlri *api.LsNodeNLRI, pathAttrs []*anypb.Any) (*table.LsNode, error) { asn := typedLinkStateNlri.GetLocalNode().GetAsn() routerID := typedLinkStateNlri.GetLocalNode().GetIgpRouterId() @@ -151,24 +164,14 @@ func getLsNodeNLRI(typedLinkStateNlri *api.LsNodeNLRI, pathAttrs []*anypb.Any) ( } isisArea := bgplsAttr.GetNode().GetIsisArea() - tmpIsisArea := hex.EncodeToString(isisArea) - strIsisArea := "" - for i, s := range strings.Split(tmpIsisArea, "") { - if (len(tmpIsisArea)-i)%4 == 0 { - strIsisArea += "." - } - strIsisArea += s - } - lsNode.IsisAreaID = strIsisArea + lsNode.IsisAreaID = formatIsisAreaID(isisArea) lsNode.Hostname = bgplsAttr.GetNode().GetName() - srCapabilities := bgplsAttr.GetNode().GetSrCapabilities().GetRanges() if len(srCapabilities) != 1 { return nil, fmt.Errorf("expected 1 SR Capability TLV, got: %d", len(srCapabilities)) - } else { - lsNode.SrgbBegin = srCapabilities[0].GetBegin() - lsNode.SrgbEnd = srCapabilities[0].GetEnd() } + lsNode.SrgbBegin = srCapabilities[0].GetBegin() + lsNode.SrgbEnd = srCapabilities[0].GetEnd() } return lsNode, nil From 14b8c63521510416cd9dec8ba461b34ca02cefe9 Mon Sep 17 00:00:00 2001 From: watal Date: Mon, 7 Apr 2025 12:30:21 +0900 Subject: [PATCH 08/87] refactor(pcep): rename const/variable to follow Go naming conventions and reorder functions --- internal/pkg/table/sr_policy.go | 8 +- pkg/packet/pcep/capability.go | 14 +- pkg/packet/pcep/message.go | 213 +++++---- pkg/packet/pcep/object.go | 757 +++++++++++++++++++------------- pkg/packet/pcep/tlv.go | 581 ++++++++++++++---------- pkg/server/grpc_server.go | 17 +- pkg/server/session.go | 72 +-- 7 files changed, 994 insertions(+), 668 deletions(-) diff --git a/internal/pkg/table/sr_policy.go b/internal/pkg/table/sr_policy.go index dd02a3c1..17f7898f 100644 --- a/internal/pkg/table/sr_policy.go +++ b/internal/pkg/table/sr_policy.go @@ -29,7 +29,7 @@ type SRPolicy struct { DstAddr netip.Addr Color uint32 Preference uint32 - LspID uint16 + LSPID uint16 State PolicyState } @@ -52,7 +52,7 @@ func NewSRPolicy( DstAddr: dstAddr, Color: color, Preference: preference, - LspID: lspID, + LSPID: lspID, State: state, } @@ -65,13 +65,13 @@ type PolicyDiff struct { Color *uint32 Preference *uint32 SegmentList []Segment - LspID uint16 + LSPID uint16 State PolicyState } func (p *SRPolicy) Update(df PolicyDiff) { p.State = df.State - p.LspID = df.LspID + p.LSPID = df.LSPID if df.Name != nil { p.Name = *df.Name } diff --git a/pkg/packet/pcep/capability.go b/pkg/packet/pcep/capability.go index aad3ffd4..b948021d 100644 --- a/pkg/packet/pcep/capability.go +++ b/pkg/packet/pcep/capability.go @@ -16,17 +16,17 @@ func PolaCapability(caps []CapabilityInterface) []CapabilityInterface { switch tlv := cap.(type) { case *StatefulPceCapability: tlv = &StatefulPceCapability{ - LspUpdateCapability: true, + LSPUpdateCapability: true, IncludeDBVersion: false, - LspInstantiationCapability: true, + LSPInstantiationCapability: true, TriggeredResync: false, - DeltaLspSyncCapability: false, + DeltaLSPSyncCapability: false, TriggeredInitialSync: false, P2mpCapability: false, - P2mpLspUpdateCapability: false, - P2mpLspInstantiationCapability: false, - LspSchedulingCapability: false, - PdLspCapability: false, + P2mpLSPUpdateCapability: false, + P2mpLSPInstantiationCapability: false, + LSPSchedulingCapability: false, + PdLSPCapability: false, ColorCapability: true, PathRecomputationCapability: false, StrictPathCapability: false, diff --git a/pkg/packet/pcep/message.go b/pkg/packet/pcep/message.go index 21efd114..c81888c0 100644 --- a/pkg/packet/pcep/message.go +++ b/pkg/packet/pcep/message.go @@ -14,37 +14,72 @@ import ( "github.com/nttcom/pola/internal/pkg/table" ) -const COMMON_HEADER_LENGTH uint16 = 4 - -const ( // PCEP Message-Type (1byte) - MT_RESERVED uint8 = 0x00 // RFC5440 - MT_OPEN uint8 = 0x01 // RFC5440 - MT_KEEPALIVE uint8 = 0x02 // RFC5440 - MT_PCREQ uint8 = 0x03 // RFC5440 - MT_PCREP uint8 = 0x04 // RFC5440 - MT_NOTIFICATION uint8 = 0x05 // RFC5440 - MT_ERROR uint8 = 0x06 // RFC5440 - MT_CLOSE uint8 = 0x07 // RFC5440 - MT_PCMONREQ uint8 = 0x08 // RFC5886 - MT_PCMONREP uint8 = 0x09 // RFC5886 - MT_REPORT uint8 = 0x0a // RFC8231 - MT_UPDATE uint8 = 0x0b // RFC8281 - MT_LSPINITREQ uint8 = 0x0c // RFC8281 - MT_STARTTLS uint8 = 0x0d // RFC8253 +const CommonHeaderLength uint16 = 4 + +// PCEP Message-Type (1 byte) +type MessageType uint8 + +const ( + MessageTypeOpen MessageType = 0x01 + MessageTypeKeepalive MessageType = 0x02 + MessageTypePcreq MessageType = 0x03 + MessageTypePcrep MessageType = 0x04 + MessageTypeNotification MessageType = 0x05 + MessageTypeError MessageType = 0x06 + MessageTypeClose MessageType = 0x07 + MessageTypePcmReq MessageType = 0x08 + MessageTypePcmRep MessageType = 0x09 + MessageTypeReport MessageType = 0x0a + MessageTypeUpdate MessageType = 0x0b + MessageTypeLSPInitReq MessageType = 0x0c + MessageTypeStartTLS MessageType = 0x0d ) +var messageTypeDescriptions = map[MessageType]struct { + Description string + Reference string +}{ + MessageTypeOpen: {"Open", "RFC5440"}, + MessageTypeKeepalive: {"Keepalive", "RFC5440"}, + MessageTypePcreq: {"Path Computation Request", "RFC5440"}, + MessageTypePcrep: {"Path Computation Reply", "RFC5440"}, + MessageTypeNotification: {"Notification", "RFC5440"}, + MessageTypeError: {"Error", "RFC5440"}, + MessageTypeClose: {"Close", "RFC5440"}, + MessageTypePcmReq: {"Path Computation Monitoring Request", "RFC5886"}, + MessageTypePcmRep: {"Path Computation Monitoring Reply", "RFC5886"}, + MessageTypeReport: {"Report", "RFC8231"}, + MessageTypeUpdate: {"Update", "RFC8281"}, + MessageTypeLSPInitReq: {"LSP Initiate Request", "RFC8281"}, + MessageTypeStartTLS: {"StartTLS", "RFC8253"}, +} + +func (t MessageType) String() string { + if desc, ok := messageTypeDescriptions[t]; ok { + return fmt.Sprintf("%s (0x%02x)", desc.Description, uint8(t)) + } + return fmt.Sprintf("Unknown MessageType (0x%02x)", uint8(t)) +} + +func (t MessageType) StringWithReference() string { + if desc, ok := messageTypeDescriptions[t]; ok { + return fmt.Sprintf("%s (0x%02x) [%s]", desc.Description, uint8(t), desc.Reference) + } + return fmt.Sprintf("Unknown MessageType (0x%02x)", uint8(t)) +} + // Common header of PCEP Message type CommonHeader struct { // RFC5440 6.1 Version uint8 // Current version is 1 Flag uint8 - MessageType uint8 + MessageType MessageType MessageLength uint16 } func (h *CommonHeader) DecodeFromBytes(header []uint8) error { h.Version = uint8(header[0] >> 5) h.Flag = uint8(header[0] & 0x1f) - h.MessageType = uint8(header[1]) + h.MessageType = MessageType(header[1]) h.MessageLength = binary.BigEndian.Uint16(header[2:4]) return nil } @@ -53,14 +88,14 @@ func (h *CommonHeader) Serialize() []uint8 { buf := make([]uint8, 0, 4) verFlag := uint8(h.Version<<5 | h.Flag) buf = append(buf, verFlag) - buf = append(buf, h.MessageType) + buf = append(buf, uint8(h.MessageType)) messageLength := make([]uint8, 2) binary.BigEndian.PutUint16(messageLength, h.MessageLength) buf = append(buf, messageLength...) return buf } -func NewCommonHeader(messageType uint8, messageLength uint16) *CommonHeader { +func NewCommonHeader(messageType MessageType, messageLength uint16) *CommonHeader { h := &CommonHeader{ Version: uint8(1), Flag: uint8(0), @@ -82,20 +117,19 @@ type OpenMessage struct { func (m *OpenMessage) DecodeFromBytes(messageBody []uint8) error { var commonObjectHeader CommonObjectHeader if err := commonObjectHeader.DecodeFromBytes(messageBody); err != nil { - return err + return fmt.Errorf("failed to decode common object header: %w", err) } - if commonObjectHeader.ObjectClass != OC_OPEN { + if commonObjectHeader.ObjectClass != ObjectClassOpen { return fmt.Errorf("unsupported ObjectClass: %d", commonObjectHeader.ObjectClass) } - if commonObjectHeader.ObjectType != OT_OPEN_OPEN { + if commonObjectHeader.ObjectType != ObjectTypeOpenOpen { return fmt.Errorf("unsupported ObjectType: %d", commonObjectHeader.ObjectType) } openObject := &OpenObject{} - err := openObject.DecodeFromBytes(commonObjectHeader.ObjectType, messageBody[COMMON_OBJECT_HEADER_LENGTH:commonObjectHeader.ObjectLength]) - if err != nil { - return err + if err := openObject.DecodeFromBytes(commonObjectHeader.ObjectType, messageBody[commonObjectHeaderLength:commonObjectHeader.ObjectLength]); err != nil { + return fmt.Errorf("failed to decode OpenObject: %w", err) } m.OpenObject = openObject @@ -104,8 +138,8 @@ func (m *OpenMessage) DecodeFromBytes(messageBody []uint8) error { func (m *OpenMessage) Serialize() ([]uint8, error) { byteOpenObject := m.OpenObject.Serialize() - openMessageLength := COMMON_HEADER_LENGTH + m.OpenObject.Len() - openHeader := NewCommonHeader(MT_OPEN, openMessageLength) + openMessageLength := CommonHeaderLength + m.OpenObject.Len() + openHeader := NewCommonHeader(MessageTypeOpen, openMessageLength) byteOpenHeader := openHeader.Serialize() byteOpenMessage := AppendByteSlices(byteOpenHeader, byteOpenObject) return byteOpenMessage, nil @@ -127,8 +161,8 @@ type KeepaliveMessage struct { } func (m *KeepaliveMessage) Serialize() ([]uint8, error) { - keepaliveMessageLength := COMMON_HEADER_LENGTH - keepaliveHeader := NewCommonHeader(MT_KEEPALIVE, keepaliveMessageLength) + keepaliveMessageLength := CommonHeaderLength + keepaliveHeader := NewCommonHeader(MessageTypeKeepalive, keepaliveMessageLength) byteKeepaliveHeader := keepaliveHeader.Serialize() byteKeepaliveMessage := byteKeepaliveHeader return byteKeepaliveMessage, nil @@ -150,7 +184,7 @@ func (m *PCErrMessage) DecodeFromBytes(messageBody []uint8) error { return err } pcepErrorObject := &PcepErrorObject{} - if err := pcepErrorObject.DecodeFromBytes(commonObjectHeader.ObjectType, messageBody[COMMON_OBJECT_HEADER_LENGTH:commonObjectHeader.ObjectLength]); err != nil { + if err := pcepErrorObject.DecodeFromBytes(commonObjectHeader.ObjectType, messageBody[commonObjectHeaderLength:commonObjectHeader.ObjectLength]); err != nil { return err } m.PcepErrorObject = pcepErrorObject @@ -158,8 +192,8 @@ func (m *PCErrMessage) DecodeFromBytes(messageBody []uint8) error { } func (m *PCErrMessage) Serialize() []uint8 { - pcerrMessageLength := COMMON_HEADER_LENGTH + m.PcepErrorObject.Len() - pcerrHeader := NewCommonHeader(MT_ERROR, pcerrMessageLength) + pcerrMessageLength := CommonHeaderLength + m.PcepErrorObject.Len() + pcerrHeader := NewCommonHeader(MessageTypeError, pcerrMessageLength) bytePCErrHeader := pcerrHeader.Serialize() bytePcepErrorObject := m.PcepErrorObject.Serialize() bytePCErrMessage := AppendByteSlices(bytePCErrHeader, bytePcepErrorObject) @@ -188,7 +222,7 @@ func (m *CloseMessage) DecodeFromBytes(messageBody []uint8) error { return err } closeObject := &CloseObject{} - if err := closeObject.DecodeFromBytes(commonObjectHeader.ObjectType, messageBody[COMMON_OBJECT_HEADER_LENGTH:commonObjectHeader.ObjectLength]); err != nil { + if err := closeObject.DecodeFromBytes(commonObjectHeader.ObjectType, messageBody[commonObjectHeaderLength:commonObjectHeader.ObjectLength]); err != nil { return err } m.CloseObject = closeObject @@ -196,15 +230,15 @@ func (m *CloseMessage) DecodeFromBytes(messageBody []uint8) error { } func (m *CloseMessage) Serialize() []uint8 { - closeMessageLength := COMMON_HEADER_LENGTH + m.CloseObject.Len() - closeHeader := NewCommonHeader(MT_CLOSE, closeMessageLength) + closeMessageLength := CommonHeaderLength + m.CloseObject.Len() + closeHeader := NewCommonHeader(MessageTypeClose, closeMessageLength) byteCloseHeader := closeHeader.Serialize() byteCloseObject := m.CloseObject.Serialize() byteCloseMessage := AppendByteSlices(byteCloseHeader, byteCloseObject) return byteCloseMessage } -func NewCloseMessage(reason uint8) (*CloseMessage, error) { +func NewCloseMessage(reason CloseReason) (*CloseMessage, error) { o, err := NewCloseObject(reason) if err != nil { return nil, err @@ -217,9 +251,9 @@ func NewCloseMessage(reason uint8) (*CloseMessage, error) { type StateReport struct { SrpObject *SrpObject - LspObject *LspObject + LSPObject *LSPObject EroObject *EroObject - LspaObject *LspaObject + LSPAObject *LSPAObject MetricObjects []*MetricObject BandwidthObjects []*BandwidthObject AssociationObject *AssociationObject @@ -229,9 +263,9 @@ type StateReport struct { func NewStateReport() (*StateReport, error) { sr := &StateReport{ SrpObject: &SrpObject{}, - LspObject: &LspObject{}, + LSPObject: &LSPObject{}, EroObject: &EroObject{}, - LspaObject: &LspaObject{}, + LSPAObject: &LSPAObject{}, MetricObjects: []*MetricObject{}, BandwidthObjects: []*BandwidthObject{}, AssociationObject: &AssociationObject{}, @@ -240,7 +274,7 @@ func NewStateReport() (*StateReport, error) { return sr, nil } -func (r *StateReport) decodeBandwidthObject(objectType uint8, objectBody []uint8) error { +func (r *StateReport) decodeBandwidthObject(objectType ObjectType, objectBody []uint8) error { bandwidthObject := &BandwidthObject{} if err := bandwidthObject.DecodeFromBytes(objectType, objectBody); err != nil { return err @@ -249,7 +283,7 @@ func (r *StateReport) decodeBandwidthObject(objectType uint8, objectBody []uint8 return nil } -func (r *StateReport) decodeMetricObject(objectType uint8, objectBody []uint8) error { +func (r *StateReport) decodeMetricObject(objectType ObjectType, objectBody []uint8) error { metricObject := &MetricObject{} if err := metricObject.DecodeFromBytes(objectType, objectBody); err != nil { return err @@ -258,19 +292,19 @@ func (r *StateReport) decodeMetricObject(objectType uint8, objectBody []uint8) e return nil } -func (r *StateReport) decodeEroObject(objectType uint8, objectBody []uint8) error { +func (r *StateReport) decodeEroObject(objectType ObjectType, objectBody []uint8) error { return r.EroObject.DecodeFromBytes(objectType, objectBody) } -func (r *StateReport) decodeLspaObject(objectType uint8, objectBody []uint8) error { - return r.LspaObject.DecodeFromBytes(objectType, objectBody) +func (r *StateReport) decodeLSPAObject(objectType ObjectType, objectBody []uint8) error { + return r.LSPAObject.DecodeFromBytes(objectType, objectBody) } -func (r *StateReport) decodeLspObject(objectType uint8, objectBody []uint8) error { - return r.LspObject.DecodeFromBytes(objectType, objectBody) +func (r *StateReport) decodeLSPObject(objectType ObjectType, objectBody []uint8) error { + return r.LSPObject.DecodeFromBytes(objectType, objectBody) } -func (r *StateReport) decodeSrpObject(objectType uint8, objectBody []uint8) error { +func (r *StateReport) decodeSrpObject(objectType ObjectType, objectBody []uint8) error { srpObject := &SrpObject{} if err := srpObject.DecodeFromBytes(objectType, objectBody); err != nil { return err @@ -279,11 +313,11 @@ func (r *StateReport) decodeSrpObject(objectType uint8, objectBody []uint8) erro return nil } -func (r *StateReport) decodeAssociationObject(objectType uint8, objectBody []uint8) error { +func (r *StateReport) decodeAssociationObject(objectType ObjectType, objectBody []uint8) error { return r.AssociationObject.DecodeFromBytes(objectType, objectBody) } -func (r *StateReport) decodeVendorInformationObject(objectType uint8, objectBody []uint8) error { +func (r *StateReport) decodeVendorInformationObject(objectType ObjectType, objectBody []uint8) error { return r.VendorInformationObject.DecodeFromBytes(objectType, objectBody) } @@ -292,20 +326,20 @@ type PCRptMessage struct { StateReports []*StateReport } -var decodeFuncs = map[uint8]func(*StateReport, uint8, []uint8) error{ - OC_BANDWIDTH: (*StateReport).decodeBandwidthObject, - OC_METRIC: (*StateReport).decodeMetricObject, - OC_ERO: (*StateReport).decodeEroObject, - OC_LSPA: (*StateReport).decodeLspaObject, - OC_LSP: (*StateReport).decodeLspObject, - OC_SRP: (*StateReport).decodeSrpObject, - OC_ASSOCIATION: (*StateReport).decodeAssociationObject, - OC_VENDOR_INFORMATION: (*StateReport).decodeVendorInformationObject, +var decodeFuncs = map[ObjectClass]func(*StateReport, ObjectType, []uint8) error{ + ObjectClassBandwidth: (*StateReport).decodeBandwidthObject, + ObjectClassMetric: (*StateReport).decodeMetricObject, + ObjectClassERO: (*StateReport).decodeEroObject, + ObjectClassLSPA: (*StateReport).decodeLSPAObject, + ObjectClassLSP: (*StateReport).decodeLSPObject, + ObjectClassSRP: (*StateReport).decodeSrpObject, + ObjectClassAssociation: (*StateReport).decodeAssociationObject, + ObjectClassVendorInformation: (*StateReport).decodeVendorInformationObject, } func (m *PCRptMessage) DecodeFromBytes(messageBody []uint8) error { - // previousOC: To determine the delimitation of StateReports from the order of object classes - var previousOC uint8 + // previousObjectClass: To track the object class and handle the delimitation of StateReports + var previousObjectClass ObjectClass var sr *StateReport for len(messageBody) > 0 { var commonObjectHeader CommonObjectHeader @@ -314,30 +348,31 @@ func (m *PCRptMessage) DecodeFromBytes(messageBody []uint8) error { } decodeFunc, ok := decodeFuncs[commonObjectHeader.ObjectClass] if !ok { - // Skip if object class not registered in decodeFunc + // Skip the current object if the object class is not registered in decodeFuncs messageBody = messageBody[commonObjectHeader.ObjectLength:] continue } - if (previousOC != OC_SRP && commonObjectHeader.ObjectClass == OC_LSP) || commonObjectHeader.ObjectClass == OC_SRP { - // If sr is not zero value, this StateReport is already updated. - var err error + if (previousObjectClass != ObjectClassSRP && commonObjectHeader.ObjectClass == ObjectClassLSP) || commonObjectHeader.ObjectClass == ObjectClassSRP { if sr != nil { m.StateReports = append(m.StateReports, sr) } + + var err error sr, err = NewStateReport() if err != nil { return err } } - if err := decodeFunc(sr, commonObjectHeader.ObjectType, messageBody[COMMON_OBJECT_HEADER_LENGTH:commonObjectHeader.ObjectLength]); err != nil { + if err := decodeFunc(sr, commonObjectHeader.ObjectType, messageBody[commonObjectHeaderLength:commonObjectHeader.ObjectLength]); err != nil { return err } - previousOC = commonObjectHeader.ObjectClass + previousObjectClass = commonObjectHeader.ObjectClass messageBody = messageBody[commonObjectHeader.ObjectLength:] } if sr != nil { m.StateReports = append(m.StateReports, sr) } + return nil } @@ -350,7 +385,7 @@ func NewPCRptMessage() *PCRptMessage { // PCInitiate Message type PCInitiateMessage struct { SrpObject *SrpObject - LspObject *LspObject + LSPObject *LSPObject EndpointsObject *EndpointsObject EroObject *EroObject AssociationObject *AssociationObject @@ -374,14 +409,14 @@ func (m *PCInitiateMessage) Serialize() ([]uint8, error) { return nil, err } } - pcinitiateMessageLength := COMMON_HEADER_LENGTH + + pcinitiateMessageLength := CommonHeaderLength + m.SrpObject.Len() + - m.LspObject.Len() + + m.LSPObject.Len() + endpointsObjectLength + eroObjectLength byteSrpObject := m.SrpObject.Serialize() - byteLspObject := m.LspObject.Serialize() + byteLSPObject := m.LSPObject.Serialize() byteEndpointsObject := []uint8{} if m.EndpointsObject != nil { @@ -417,17 +452,17 @@ func (m *PCInitiateMessage) Serialize() ([]uint8, error) { pcinitiateMessageLength += m.VendorInformationObject.Len() } - pcinitiateHeader := NewCommonHeader(MT_LSPINITREQ, pcinitiateMessageLength) + pcinitiateHeader := NewCommonHeader(MessageTypeLSPInitReq, pcinitiateMessageLength) bytePCInitiateHeader := pcinitiateHeader.Serialize() bytePCInitiateMessage := AppendByteSlices( - bytePCInitiateHeader, byteSrpObject, byteLspObject, byteEndpointsObject, byteEroObject, byteAssociationObject, byteVendorInformationObject, + bytePCInitiateHeader, byteSrpObject, byteLSPObject, byteEndpointsObject, byteEroObject, byteAssociationObject, byteVendorInformationObject, ) return bytePCInitiateMessage, nil } func NewPCInitiateMessage(srpID uint32, lspName string, lspDelete bool, plspID uint32, segmentList []table.Segment, color uint32, preference uint32, srcAddr netip.Addr, dstAddr netip.Addr, opt ...Opt) (*PCInitiateMessage, error) { opts := optParams{ - pccType: RFC_COMPLIANT, + pccType: RFCCompliant, } for _, o := range opt { @@ -441,12 +476,12 @@ func NewPCInitiateMessage(srpID uint32, lspName string, lspDelete bool, plspID u } if lspDelete { - if m.LspObject, err = NewLspObject(lspName, &color, plspID); err != nil { + if m.LSPObject, err = NewLSPObject(lspName, &color, plspID); err != nil { return nil, err } return m, nil } else { - if m.LspObject, err = NewLspObject(lspName, &color, 0); err != nil { + if m.LSPObject, err = NewLSPObject(lspName, &color, 0); err != nil { return nil, err } } @@ -459,20 +494,20 @@ func NewPCInitiateMessage(srpID uint32, lspName string, lspDelete bool, plspID u } switch opts.pccType { - case JUNIPER_LEGACY: + case JuniperLegacy: if m.AssociationObject, err = NewAssociationObject(srcAddr, dstAddr, color, preference, VendorSpecific(opts.pccType)); err != nil { return nil, err } - case CISCO_LEGACY: - if m.VendorInformationObject, err = NewVendorInformationObject(CISCO_LEGACY, color, preference); err != nil { + case CiscoLegacy: + if m.VendorInformationObject, err = NewVendorInformationObject(CiscoLegacy, color, preference); err != nil { return nil, err } - case RFC_COMPLIANT: + case RFCCompliant: if m.AssociationObject, err = NewAssociationObject(srcAddr, dstAddr, color, preference); err != nil { return nil, err } // FRRouting is considered RFC compliant - if m.VendorInformationObject, err = NewVendorInformationObject(CISCO_LEGACY, color, preference); err != nil { + if m.VendorInformationObject, err = NewVendorInformationObject(CiscoLegacy, color, preference); err != nil { return nil, err } default: @@ -485,13 +520,13 @@ func NewPCInitiateMessage(srpID uint32, lspName string, lspDelete bool, plspID u // PCUpdate Message type PCUpdMessage struct { SrpObject *SrpObject - LspObject *LspObject + LSPObject *LSPObject EroObject *EroObject } func (m *PCUpdMessage) Serialize() ([]uint8, error) { byteSrpObject := m.SrpObject.Serialize() - byteLspObject := m.LspObject.Serialize() + byteLSPObject := m.LSPObject.Serialize() byteEroObject, err := m.EroObject.Serialize() if err != nil { return nil, err @@ -501,10 +536,10 @@ func (m *PCUpdMessage) Serialize() ([]uint8, error) { if err != nil { return nil, err } - pcupdMessageLength := COMMON_HEADER_LENGTH + m.SrpObject.Len() + m.LspObject.Len() + eroObjectLength - pcupdHeader := NewCommonHeader(MT_UPDATE, pcupdMessageLength) + pcupdMessageLength := CommonHeaderLength + m.SrpObject.Len() + m.LSPObject.Len() + eroObjectLength + pcupdHeader := NewCommonHeader(MessageTypeUpdate, pcupdMessageLength) bytePCUpdHeader := pcupdHeader.Serialize() - bytePCUpdMessage := AppendByteSlices(bytePCUpdHeader, byteSrpObject, byteLspObject, byteEroObject) + bytePCUpdMessage := AppendByteSlices(bytePCUpdHeader, byteSrpObject, byteLSPObject, byteEroObject) return bytePCUpdMessage, err } @@ -515,7 +550,7 @@ func NewPCUpdMessage(srpID uint32, lspName string, plspID uint32, segmentList [] if m.SrpObject, err = NewSrpObject(segmentList, srpID, false); err != nil { return nil, err } - if m.LspObject, err = NewLspObject(lspName, nil, plspID); err != nil { + if m.LSPObject, err = NewLSPObject(lspName, nil, plspID); err != nil { return nil, err } if m.EroObject, err = NewEroObject(segmentList); err != nil { diff --git a/pkg/packet/pcep/object.go b/pkg/packet/pcep/object.go index ea6e9e43..485c1081 100644 --- a/pkg/packet/pcep/object.go +++ b/pkg/packet/pcep/object.go @@ -8,6 +8,7 @@ package pcep import ( "encoding/binary" "errors" + "fmt" "math" "net/netip" @@ -17,21 +18,21 @@ import ( type PccType int const ( - CISCO_LEGACY PccType = iota - JUNIPER_LEGACY - RFC_COMPLIANT + CiscoLegacy PccType = iota + JuniperLegacy + RFCCompliant ) // Determine PCC type from capability func DeterminePccType(caps []CapabilityInterface) (pccType PccType) { - pccType = RFC_COMPLIANT + pccType = RFCCompliant for _, cap := range caps { if t, ok := cap.(*AssocTypeList); ok { for _, v := range t.AssocTypes { - if v == AssocType(20) { // Cisco specific Assoc-Type - pccType = CISCO_LEGACY - } else if v == AssocType(65505) { // Juniper specific Assoc-Type - pccType = JUNIPER_LEGACY + if v == AssociationTypeSRPolicyAssociationCisco { + pccType = CiscoLegacy + } else if v == AssociationTypeSRPolicyAssociationJuniper { + pccType = JuniperLegacy break } } @@ -40,95 +41,170 @@ func DeterminePccType(caps []CapabilityInterface) (pccType PccType) { return } -const COMMON_OBJECT_HEADER_LENGTH uint16 = 4 - -const ( // PCEP Object-Class (1 byte) Ref: https://www.iana.org/assignments/pcep/pcep.xhtml#pcep-objects - OC_RESERVED uint8 = 0x00 // RFC5440 - OC_OPEN uint8 = 0x01 // RFC5440 - OC_RP uint8 = 0x02 // RFC5440 - OC_NO_PATH uint8 = 0x03 // RFC5440 - OC_END_POINTS uint8 = 0x04 // RFC5440 - OC_BANDWIDTH uint8 = 0x05 // RFC5440 - OC_METRIC uint8 = 0x06 // RFC5440 - OC_ERO uint8 = 0x07 // RFC5440 - OC_RRO uint8 = 0x08 // RFC5440 - OC_LSPA uint8 = 0x09 // RFC5440 - OC_IRO uint8 = 0x0a // RFC5440 - OC_SVRC uint8 = 0x0b // RFC5440 - OC_NOTIFICATION uint8 = 0x0c // RFC5440 - OC_PCEP_ERROR uint8 = 0x0d // RFC5440 - OC_LOAD_BALANCING uint8 = 0x0e // RFC5440 - OC_CLOSE uint8 = 0x0f // RFC5440 - OC_PATH_KEY uint8 = 0x10 // RFC5520 - OC_XRO uint8 = 0x11 // RFC5521 - OC_MONITORING uint8 = 0x13 // RFC5886 - OC_PCC_REQ_ID uint8 = 0x14 // RFC5886 - OC_OF uint8 = 0x15 // RFC5541 - OC_CLASSTYPE uint8 = 0x16 // RFC5455 - OC_GLOBAL_CONSTRAINTS uint8 = 0x18 // RFC5557 - OC_PCE_ID uint8 = 0x19 // RFC5886 - OC_PROC_TIME uint8 = 0x1a // RFC5886 - OC_OVERLOAD uint8 = 0x1b // RFC5886 - OC_UNREACH_DESTINATION uint8 = 0x1c // RFC8306 - OC_SERO uint8 = 0x1d // RFC8306 - OC_SRRO uint8 = 0x1e // RFC8306 - OC_BNC uint8 = 0x1f // RFC8306 - OC_LSP uint8 = 0x20 // RFC8231 - OC_SRP uint8 = 0x21 // RFC8231 - OC_VENDOR_INFORMATION uint8 = 0x22 // RFC7470 - OC_BU uint8 = 0x23 // RFC8233 - OC_INTER_LAYER uint8 = 0x24 // RFC8282 - OC_SWITCH_LAYER uint8 = 0x25 // RFC8282 - OC_REQ_ADAP_CAP uint8 = 0x26 // RFC8282 - OC_SERVER_INDICATION uint8 = 0x27 // RFC8282 - OC_ASSOCIATION uint8 = 0x28 // RFC8697 - OC_S2LS uint8 = 0x29 // RFC8623 - OC_WA uint8 = 0x2a // RFC8780 - OC_FLOWSPEC uint8 = 0x2b // RFC9168 - OC_CCI_OBJECT_TYPE uint8 = 0x2c // RFC9050 - OC_PATH_ATTRIB uint8 = 0x2d // draft-ietf-pce-multipath-07 - OC_BGP_PEER_INFO_OBJECT_TYPE uint8 = 0x2c // RFC9757 - OC_EXPLICIT_PEER_ROUTE_OBJECT_TYPE uint8 = 0x2d // RFC9757 - OC_PEER_PREFIX_ADVERTISEMENT_OBJECT_TYPE uint8 = 0x2e // RFC9757 +const commonObjectHeaderLength uint16 = 4 + +// PCEP Object-Class (1 byte) Ref: https://www.iana.org/assignments/pcep/pcep.xhtml#pcep-objects +type ObjectClass uint8 +type ObjectType uint8 +type SubObjectType uint8 + +const ( + ObjectClassOpen ObjectClass = 0x01 + ObjectClassRP ObjectClass = 0x02 + ObjectClassNoPath ObjectClass = 0x03 + ObjectClassEndpoints ObjectClass = 0x04 + ObjectClassBandwidth ObjectClass = 0x05 + ObjectClassMetric ObjectClass = 0x06 + ObjectClassERO ObjectClass = 0x07 + ObjectClassRRO ObjectClass = 0x08 + ObjectClassLSPA ObjectClass = 0x09 + ObjectClassIRO ObjectClass = 0x0a + ObjectClassSVEC ObjectClass = 0x0b + ObjectClassNotification ObjectClass = 0x0c + ObjectClassPCEPError ObjectClass = 0x0d + ObjectClassLoadBalancing ObjectClass = 0x0e + ObjectClassClose ObjectClass = 0x0f + ObjectClassPathKey ObjectClass = 0x10 + ObjectClassXRO ObjectClass = 0x11 + ObjectClassMonitoring ObjectClass = 0x13 + ObjectClassPCCReqID ObjectClass = 0x14 + ObjectClassOF ObjectClass = 0x15 + ObjectClassClassType ObjectClass = 0x16 + ObjectClassGlobalConstraints ObjectClass = 0x18 + ObjectClassPCEID ObjectClass = 0x19 + ObjectClassProcTime ObjectClass = 0x1a + ObjectClassOverload ObjectClass = 0x1b + ObjectClassUnreachDestination ObjectClass = 0x1c + ObjectClassSERO ObjectClass = 0x1d + ObjectClassSRRO ObjectClass = 0x1e + ObjectClassBNC ObjectClass = 0x1f + ObjectClassLSP ObjectClass = 0x20 + ObjectClassSRP ObjectClass = 0x21 + ObjectClassVendorInformation ObjectClass = 0x22 + ObjectClassBU ObjectClass = 0x23 + ObjectClassInterLayer ObjectClass = 0x24 + ObjectClassSwitchLayer ObjectClass = 0x25 + ObjectClassReqAdapCap ObjectClass = 0x26 + ObjectClassServerIndication ObjectClass = 0x27 + ObjectClassAssociation ObjectClass = 0x28 + ObjectClassS2LS ObjectClass = 0x29 + ObjectClassWA ObjectClass = 0x2a + ObjectClassFlowSpec ObjectClass = 0x2b + ObjectClassCCIObjectType ObjectClass = 0x2c + ObjectClassPathAttrib ObjectClass = 0x2d + ObjectClassBGPPeerInfoObjectType ObjectClass = 0x2e + ObjectClassExplicitPeerRouteObjectType ObjectClass = 0x2f + ObjectClassPeerPrefixAdvertisementObjectType ObjectClass = 0x30 ) +var objectClassDescriptions = map[ObjectClass]struct { + Description string + Reference string +}{ + ObjectClassOpen: {"Open", "RFC5440"}, + ObjectClassRP: {"RP", "RFC5440"}, + ObjectClassNoPath: {"NO-PATH", "RFC5440"}, + ObjectClassEndpoints: {"END-POINTS", "RFC5440"}, + ObjectClassBandwidth: {"BANDWIDTH", "RFC5440"}, + ObjectClassMetric: {"METRIC", "RFC5440"}, + ObjectClassERO: {"ERO", "RFC5440"}, + ObjectClassRRO: {"RRO", "RFC5440"}, + ObjectClassLSPA: {"LSPA", "RFC5440"}, + ObjectClassIRO: {"IRO", "RFC5440"}, + ObjectClassSVEC: {"SVEC", "RFC5440"}, + ObjectClassNotification: {"NOTIFICATION", "RFC5440"}, + ObjectClassPCEPError: {"PCEP-ERROR", "RFC5440"}, + ObjectClassLoadBalancing: {"LOAD-BALANCING", "RFC5440"}, + ObjectClassClose: {"CLOSE", "RFC5440"}, + ObjectClassPathKey: {"PATH-KEY", "RFC5520"}, + ObjectClassXRO: {"XRO", "RFC5521"}, + ObjectClassMonitoring: {"MONITORING", "RFC5886"}, + ObjectClassPCCReqID: {"PCC-REQ-ID", "RFC5886"}, + ObjectClassOF: {"OF", "RFC5541"}, + ObjectClassClassType: {"CLASSTYPE", "RFC5455"}, + ObjectClassGlobalConstraints: {"GLOBAL-CONSTRAINTS", "RFC5557"}, + ObjectClassPCEID: {"PCE-ID", "RFC5886"}, + ObjectClassProcTime: {"PROC-TIME", "RFC5886"}, + ObjectClassOverload: {"OVERLOAD", "RFC5886"}, + ObjectClassUnreachDestination: {"UNREACH-DESTINATION", "RFC8306"}, + ObjectClassSERO: {"SERO", "RFC8306"}, + ObjectClassSRRO: {"SRRO", "RFC8306"}, + ObjectClassBNC: {"BNC", "RFC8306"}, + ObjectClassLSP: {"LSP", "RFC8231"}, + ObjectClassSRP: {"SRP", "RFC8231"}, + ObjectClassVendorInformation: {"VENDOR-INFORMATION", "RFC7470"}, + ObjectClassBU: {"BU", "RFC8233"}, + ObjectClassInterLayer: {"INTER-LAYER", "RFC8282"}, + ObjectClassSwitchLayer: {"SWITCH-LAYER", "RFC8282"}, + ObjectClassReqAdapCap: {"REQ-ADAP-CAP", "RFC8282"}, + ObjectClassServerIndication: {"SERVER-INDICATION", "RFC8282"}, + ObjectClassAssociation: {"ASSOCIATION", "RFC8697"}, + ObjectClassS2LS: {"S2LS", "RFC8623"}, + ObjectClassWA: {"WA", "RFC8780"}, + ObjectClassFlowSpec: {"FLOWSPEC", "RFC9168"}, + ObjectClassCCIObjectType: {"CCI", "RFC9050"}, + ObjectClassPathAttrib: {"PATH-ATTRIB", "draft-ietf-pce-multipath-07"}, + ObjectClassBGPPeerInfoObjectType: {"BGP-PEER-INFO", "RFC9757"}, + ObjectClassExplicitPeerRouteObjectType: {"EXPLICIT-PEER-ROUTE", "RFC9757"}, + ObjectClassPeerPrefixAdvertisementObjectType: {"PEER-PREFIX-ADVERTISEMENT", "RFC9757"}, +} + +func (c ObjectClass) String() string { + if desc, ok := objectClassDescriptions[c]; ok { + return fmt.Sprintf("%s (0x%02x)", desc.Description, uint8(c)) + } + return fmt.Sprintf("Unknown Object Class (0x%02x)", uint8(c)) +} + +func (c ObjectClass) StringWithReference() string { + if desc, ok := objectClassDescriptions[c]; ok { + return fmt.Sprintf("%s (0x%02x) [%s]", desc.Description, c, desc.Reference) + } + return fmt.Sprintf("Unknown Object Class (0x%02x)", uint8(c)) +} + type CommonObjectHeader struct { // RFC5440 7.2 - ObjectClass uint8 - ObjectType uint8 + ObjectClass ObjectClass + ObjectType ObjectType ResFlags uint8 // MUST be set to zero PFlag bool // 0: optional, 1: MUST IFlag bool // 0: processed, 1: ignored ObjectLength uint16 } +const ( + IFlagMask uint8 = 0x01 + PFlagMask uint8 = 0x02 +) + func (h *CommonObjectHeader) DecodeFromBytes(objectHeader []uint8) error { - h.ObjectClass = uint8(objectHeader[0]) - h.ObjectType = uint8(objectHeader[1] & 0xf0 >> 4) - h.ResFlags = uint8(objectHeader[1] & 0x0c >> 2) - h.PFlag = (objectHeader[1] & 0x02) != 0 - h.IFlag = (objectHeader[1] & 0x01) != 0 + h.ObjectClass = ObjectClass(objectHeader[0]) + h.ObjectType = ObjectType((objectHeader[1] & 0xf0) >> 4) + h.ResFlags = uint8((objectHeader[1] & 0x0c) >> 2) + h.PFlag = (objectHeader[1] & PFlagMask) != 0 + h.IFlag = (objectHeader[1] & IFlagMask) != 0 h.ObjectLength = binary.BigEndian.Uint16(objectHeader[2:4]) return nil } func (h *CommonObjectHeader) Serialize() []uint8 { buf := make([]uint8, 0, 4) - buf = append(buf, h.ObjectClass) - otFlags := uint8(h.ObjectType<<4 | h.ResFlags<<2) + buf = append(buf, uint8(h.ObjectClass)) + Flagbyte := uint8(h.ObjectType)<<4 | uint8(h.ResFlags)<<2 if h.PFlag { - otFlags = otFlags | 0x02 + Flagbyte = Flagbyte | PFlagMask } if h.IFlag { - otFlags = otFlags | 0x01 + Flagbyte = Flagbyte | IFlagMask } - buf = append(buf, otFlags) + buf = append(buf, Flagbyte) objectLength := make([]uint8, 2) binary.BigEndian.PutUint16(objectLength, h.ObjectLength) buf = append(buf, objectLength...) return buf } -func NewCommonObjectHeader(objectClass uint8, objectType uint8, messageLength uint16) *CommonObjectHeader { +func NewCommonObjectHeader(objectClass ObjectClass, objectType ObjectType, messageLength uint16) *CommonObjectHeader { h := &CommonObjectHeader{ ObjectClass: objectClass, ObjectType: objectType, @@ -140,25 +216,13 @@ func NewCommonObjectHeader(objectClass uint8, objectType uint8, messageLength ui return h } -type optParams struct { - pccType PccType -} - -type Opt func(*optParams) - -func VendorSpecific(pt PccType) Opt { - return func(op *optParams) { - op.pccType = pt - } -} - // OPEN Object (RFC5440 7.3) const ( - OT_OPEN_OPEN uint8 = 0x01 + ObjectTypeOpenOpen ObjectType = 0x01 ) type OpenObject struct { - ObjectType uint8 + ObjectType ObjectType Version uint8 Flag uint8 Keepalive uint8 @@ -167,7 +231,7 @@ type OpenObject struct { Caps []CapabilityInterface } -func (o *OpenObject) DecodeFromBytes(typ uint8, objectBody []uint8) error { +func (o *OpenObject) DecodeFromBytes(typ ObjectType, objectBody []uint8) error { o.ObjectType = typ o.Version = uint8(objectBody[0] >> 5) o.Flag = uint8(objectBody[0] & 0x1f) @@ -188,7 +252,7 @@ func (o *OpenObject) DecodeFromBytes(typ uint8, objectBody []uint8) error { } func (o *OpenObject) Serialize() []uint8 { - openObjectHeader := NewCommonObjectHeader(OC_OPEN, o.ObjectType, o.Len()) + openObjectHeader := NewCommonObjectHeader(ObjectClassOpen, o.ObjectType, o.Len()) byteOpenObjectHeader := openObjectHeader.Serialize() buf := make([]uint8, 4) buf[0] = o.Version << 5 @@ -212,12 +276,12 @@ func (o *OpenObject) Len() uint16 { } // TODO: Calculate TLV length and record in open_object_length // CommonObjectHeader(4byte) + openObject(4byte) + tlvslength(valiable) - return COMMON_OBJECT_HEADER_LENGTH + 4 + tlvsByteLength + return commonObjectHeaderLength + 4 + tlvsByteLength } func NewOpenObject(sessionID uint8, keepalive uint8, capabilities []CapabilityInterface) (*OpenObject, error) { o := &OpenObject{ - ObjectType: OT_OPEN_OPEN, + ObjectType: ObjectTypeOpenOpen, Version: uint8(1), // PCEP version. Current version is 1 Flag: uint8(0), Keepalive: keepalive, @@ -230,26 +294,26 @@ func NewOpenObject(sessionID uint8, keepalive uint8, capabilities []CapabilityIn // BANDWIDTH Object (RFC5440 7.7) type BandwidthObject struct { - ObjectType uint8 + ObjectType ObjectType Bandwidth uint32 } -func (o *BandwidthObject) DecodeFromBytes(typ uint8, objectBody []uint8) error { - o.ObjectType = typ +func (o *BandwidthObject) DecodeFromBytes(objectType ObjectType, objectBody []uint8) error { + o.ObjectType = objectType o.Bandwidth = binary.BigEndian.Uint32(objectBody[:]) return nil } // METRIC Object (RFC5440 7.8) type MetricObject struct { - ObjectType uint8 + ObjectType ObjectType CFlag bool BFlag bool MetricType uint8 MetricValue uint32 } -func (o *MetricObject) DecodeFromBytes(typ uint8, objectBody []uint8) error { +func (o *MetricObject) DecodeFromBytes(typ ObjectType, objectBody []uint8) error { o.ObjectType = typ o.CFlag = (objectBody[2] & 0x02) != 0 o.BFlag = (objectBody[2] & 0x01) != 0 @@ -259,7 +323,7 @@ func (o *MetricObject) DecodeFromBytes(typ uint8, objectBody []uint8) error { } func (o *MetricObject) Serialize() []uint8 { - metricObjectHeader := NewCommonObjectHeader(OC_METRIC, o.ObjectType, o.Len()) + metricObjectHeader := NewCommonObjectHeader(ObjectClassMetric, o.ObjectType, o.Len()) byteMetricObjectHeader := metricObjectHeader.Serialize() buf := make([]uint8, 8) @@ -278,12 +342,12 @@ func (o *MetricObject) Serialize() []uint8 { func (o *MetricObject) Len() uint16 { // CommonObjectHeader(4byte) + Flags, SRP-ID(8byte) - return COMMON_OBJECT_HEADER_LENGTH + 8 + return commonObjectHeaderLength + 8 } func NewMetricObject() (*MetricObject, error) { o := &MetricObject{ - ObjectType: uint8(1), + ObjectType: ObjectType(1), MetricType: uint8(2), MetricValue: uint32(30), } @@ -291,8 +355,8 @@ func NewMetricObject() (*MetricObject, error) { } // LSPA Object (RFC5440 7.11) -type LspaObject struct { - ObjectType uint8 +type LSPAObject struct { + ObjectType ObjectType ExcludeAny uint32 IncludeAny uint32 IncludeAll uint32 @@ -301,7 +365,7 @@ type LspaObject struct { LFlag bool } -func (o *LspaObject) DecodeFromBytes(typ uint8, objectBody []uint8) error { +func (o *LSPAObject) DecodeFromBytes(typ ObjectType, objectBody []uint8) error { o.ObjectType = typ o.ExcludeAny = binary.BigEndian.Uint32(objectBody[0:4]) o.IncludeAny = binary.BigEndian.Uint32(objectBody[4:8]) @@ -312,9 +376,9 @@ func (o *LspaObject) DecodeFromBytes(typ uint8, objectBody []uint8) error { return nil } -func (o *LspaObject) Serialize() []uint8 { - lspaObjectHeader := NewCommonObjectHeader(OC_LSPA, o.ObjectType, o.Len()) - byteLspaObjectHeader := lspaObjectHeader.Serialize() +func (o *LSPAObject) Serialize() []uint8 { + lspaObjectHeader := NewCommonObjectHeader(ObjectClassLSPA, o.ObjectType, o.Len()) + byteLSPAObjectHeader := lspaObjectHeader.Serialize() buf := make([]uint8, 16) binary.BigEndian.PutUint32(buf[0:4], o.ExcludeAny) @@ -326,18 +390,18 @@ func (o *LspaObject) Serialize() []uint8 { buf[14] = buf[14] | 0x01 } - byteLspaObject := AppendByteSlices(byteLspaObjectHeader, buf) - return byteLspaObject + byteLSPAObject := AppendByteSlices(byteLSPAObjectHeader, buf) + return byteLSPAObject } -func (o *LspaObject) Len() uint16 { +func (o *LSPAObject) Len() uint16 { // CommonObjectHeader(4byte) + Flags, SRP-ID(8byte) - return COMMON_OBJECT_HEADER_LENGTH + 16 + return commonObjectHeaderLength + 16 } -func NewLspaObject() (*LspaObject, error) { - o := &LspaObject{ - ObjectType: uint8(1), +func NewLSPAObject() (*LSPAObject, error) { + o := &LSPAObject{ + ObjectType: ObjectType(1), SetupPriority: uint8(7), HoldingPriority: uint8(7), LFlag: true, @@ -347,17 +411,17 @@ func NewLspaObject() (*LspaObject, error) { // PCEP Error Object (RFC5440 7.15) const ( - OT_ERROR_ERROR uint8 = 0x01 + ObjectTypeErrorError ObjectType = 0x01 ) type PcepErrorObject struct { - ObjectType uint8 + ObjectType ObjectType ErrorType uint8 ErrorValue uint8 Tlvs []TLVInterface } -func (o *PcepErrorObject) DecodeFromBytes(typ uint8, objectBody []uint8) error { +func (o *PcepErrorObject) DecodeFromBytes(typ ObjectType, objectBody []uint8) error { o.ObjectType = typ o.ErrorType = objectBody[2] o.ErrorValue = objectBody[3] @@ -372,7 +436,7 @@ func (o *PcepErrorObject) DecodeFromBytes(typ uint8, objectBody []uint8) error { } func (o *PcepErrorObject) Serialize() []uint8 { - pcepErrorObjectHeader := NewCommonObjectHeader(OC_PCEP_ERROR, o.ObjectType, o.Len()) + pcepErrorObjectHeader := NewCommonObjectHeader(ObjectClassPCEPError, o.ObjectType, o.Len()) bytePcepErrorObjectHeader := pcepErrorObjectHeader.Serialize() buf := make([]uint8, 4) @@ -389,12 +453,12 @@ func (o *PcepErrorObject) Len() uint16 { tlvsByteLength += tlv.Len() } // CommonObjectHeader(4byte) + Flags,Error-Type,Error-value(4byte) + tlvslength(valiable) - return COMMON_OBJECT_HEADER_LENGTH + 4 + tlvsByteLength + return commonObjectHeaderLength + 4 + tlvsByteLength } func NewPcepErrorObject(errorType uint8, errorValue uint8, tlvs []TLVInterface) (*PcepErrorObject, error) { o := &PcepErrorObject{ - ObjectType: OT_ERROR_ERROR, + ObjectType: ObjectTypeErrorError, ErrorType: errorType, ErrorValue: errorValue, Tlvs: tlvs, @@ -404,45 +468,74 @@ func NewPcepErrorObject(errorType uint8, errorValue uint8, tlvs []TLVInterface) // Close Object (RFC5440 7.17) const ( - OT_CLOSE_CLOSE uint8 = 0x01 + ObjectTypeCloseClose ObjectType = 0x01 ) +type CloseReason uint8 + const ( - R_NO_EXPLANATION_PROVIDED uint8 = 0x01 - R_DEADTIMER_EXPIRED uint8 = 0x02 - R_RECEPTION_OF_A_MALFORMED_PCEP_MESSAGE uint8 = 0x03 + CloseReasonNoExplanationProvided CloseReason = 0x01 + CloseReasonDeadTimerExpired CloseReason = 0x02 + CloseReasonMalformedPCEPMessage CloseReason = 0x03 + CloseReasonTooManyUnknownRequestsReplies CloseReason = 0x04 + CloseReasonTooManyUnrecognizedPCEPMessages CloseReason = 0x05 ) +var closeReasonDescriptions = map[CloseReason]struct { + Description string + Reference string +}{ + CloseReasonNoExplanationProvided: {"No explanation provided", "RFC5440"}, + CloseReasonDeadTimerExpired: {"DeadTimer expired", "RFC5440"}, + CloseReasonMalformedPCEPMessage: {"Reception of a malformed PCEP message", "RFC5440"}, + CloseReasonTooManyUnknownRequestsReplies: {"Reception of an unacceptable number of unknown requests/replies", "RFC5440"}, + CloseReasonTooManyUnrecognizedPCEPMessages: {"Reception of an unacceptable number of unrecognized PCEP messages", "RFC5440"}, +} + +func (r CloseReason) String() string { + if desc, ok := closeReasonDescriptions[r]; ok { + return fmt.Sprintf("%s (0x%02x)", desc.Description, uint8(r)) + } + return fmt.Sprintf("Unknown Close Reason (0x%02x)", uint8(r)) +} + +func (r CloseReason) StringWithReference() string { + if desc, ok := closeReasonDescriptions[r]; ok { + return fmt.Sprintf("%s (0x%02x) [%s]", desc.Description, r, desc.Reference) + } + return fmt.Sprintf("Unknown Close Reason (0x%02x)", uint8(r)) +} + type CloseObject struct { - ObjectType uint8 - Reason uint8 + ObjectType ObjectType + Reason CloseReason } -func (o *CloseObject) DecodeFromBytes(typ uint8, objectBody []uint8) error { +func (o *CloseObject) DecodeFromBytes(typ ObjectType, objectBody []uint8) error { o.ObjectType = typ - o.Reason = objectBody[3] + o.Reason = CloseReason(objectBody[3]) return nil } func (o *CloseObject) Serialize() []uint8 { - closeObjectHeader := NewCommonObjectHeader(OC_CLOSE, o.ObjectType, o.Len()) + closeObjectHeader := NewCommonObjectHeader(ObjectClassClose, o.ObjectType, o.Len()) byteCloseObjectHeader := closeObjectHeader.Serialize() buf := make([]uint8, 4) - buf[3] = o.Reason + buf[3] = uint8(o.Reason) byteCloseObject := AppendByteSlices(byteCloseObjectHeader, buf) return byteCloseObject } func (o *CloseObject) Len() uint16 { // CommonObjectHeader(4byte) + CloseObjectBody(4byte) - return COMMON_OBJECT_HEADER_LENGTH + 4 + return commonObjectHeaderLength + 4 } -func NewCloseObject(reason uint8) (*CloseObject, error) { +func NewCloseObject(reason CloseReason) (*CloseObject, error) { o := &CloseObject{ - ObjectType: OT_CLOSE_CLOSE, + ObjectType: ObjectTypeCloseClose, Reason: reason, } return o, nil @@ -450,17 +543,17 @@ func NewCloseObject(reason uint8) (*CloseObject, error) { // SRP Object (RFC8231 7.2) const ( - OT_SRP_SRP uint8 = 0x01 + ObjectTypeSRPSRP ObjectType = 0x01 ) type SrpObject struct { - ObjectType uint8 + ObjectType ObjectType RFlag bool SrpID uint32 // 0x00000000 and 0xFFFFFFFF are reserved. TLVs []TLVInterface } -func (o *SrpObject) DecodeFromBytes(typ uint8, objectBody []uint8) error { +func (o *SrpObject) DecodeFromBytes(typ ObjectType, objectBody []uint8) error { o.ObjectType = typ o.RFlag = (objectBody[3] & 0x01) != 0 o.SrpID = binary.BigEndian.Uint32(objectBody[4:8]) @@ -468,7 +561,7 @@ func (o *SrpObject) DecodeFromBytes(typ uint8, objectBody []uint8) error { } func (o *SrpObject) Serialize() []uint8 { - srpObjectHeader := NewCommonObjectHeader(OC_SRP, o.ObjectType, o.Len()) + srpObjectHeader := NewCommonObjectHeader(ObjectClassSRP, o.ObjectType, o.Len()) byteSrpObjectHeader := srpObjectHeader.Serialize() byteFlags := make([]uint8, 4) @@ -492,12 +585,12 @@ func (o *SrpObject) Len() uint16 { tlvsByteLength += tlv.Len() } // CommonObjectHeader(4byte) + Flags, SRP-ID(8byte) - return COMMON_OBJECT_HEADER_LENGTH + 8 + tlvsByteLength + return commonObjectHeaderLength + 8 + tlvsByteLength } func NewSrpObject(segs []table.Segment, srpID uint32, isRemove bool) (*SrpObject, error) { o := &SrpObject{ - ObjectType: OT_SRP_SRP, + ObjectType: ObjectTypeSRPSRP, RFlag: isRemove, // RFC8281 5.2 SrpID: srpID, TLVs: []TLVInterface{}, @@ -506,9 +599,9 @@ func NewSrpObject(segs []table.Segment, srpID uint32, isRemove bool) (*SrpObject return o, nil } if _, ok := segs[0].(table.SegmentSRMPLS); ok { - o.TLVs = append(o.TLVs, &PathSetupType{PathSetupType: PST_SR_TE}) + o.TLVs = append(o.TLVs, &PathSetupType{PathSetupType: PathSetupTypeSRTE}) } else if _, ok := segs[0].(table.SegmentSRv6); ok { - o.TLVs = append(o.TLVs, &PathSetupType{PathSetupType: PST_SRV6_TE}) + o.TLVs = append(o.TLVs, &PathSetupType{PathSetupType: PathSetupTypeSRv6TE}) } else { return nil, errors.New("invalid Segment type") } @@ -517,16 +610,16 @@ func NewSrpObject(segs []table.Segment, srpID uint32, isRemove bool) (*SrpObject // LSP Object (RFC8281 5.3.1) const ( - OT_LSP_LSP uint8 = 0x01 + ObjectTypeLSPLSP ObjectType = 0x01 ) -type LspObject struct { - ObjectType uint8 +type LSPObject struct { + ObjectType ObjectType Name string SrcAddr netip.Addr DstAddr netip.Addr PlspID uint32 - LspID uint16 + LSPID uint16 CFlag bool OFlag uint8 AFlag bool @@ -536,7 +629,7 @@ type LspObject struct { TLVs []TLVInterface } -func (o *LspObject) DecodeFromBytes(typ uint8, objectBody []uint8) error { +func (o *LSPObject) DecodeFromBytes(typ ObjectType, objectBody []uint8) error { o.ObjectType = typ o.PlspID = uint32(binary.BigEndian.Uint32(objectBody[0:4]) >> 12) // 20 bits from top o.OFlag = uint8(objectBody[3] & 0x0070 >> 4) @@ -556,24 +649,24 @@ func (o *LspObject) DecodeFromBytes(typ uint8, objectBody []uint8) error { if t, ok := tlv.(*SymbolicPathName); ok { o.Name = t.Name } - if t, ok := tlv.(*IPv4LspIdentifiers); ok { + if t, ok := tlv.(*IPv4LSPIdentifiers); ok { o.SrcAddr = t.IPv4TunnelSenderAddress o.DstAddr = t.IPv4TunnelEndpointAddress - o.LspID = t.LspID + o.LSPID = t.LSPID } - if t, ok := tlv.(*IPv6LspIdentifiers); ok { + if t, ok := tlv.(*IPv6LSPIdentifiers); ok { o.SrcAddr = t.IPv6TunnelSenderAddress o.DstAddr = t.IPv6TunnelEndpointAddress - o.LspID = t.LspID + o.LSPID = t.LSPID } } } return nil } -func (o *LspObject) Serialize() []uint8 { - lspObjectHeader := NewCommonObjectHeader(OC_LSP, o.ObjectType, o.Len()) - byteLspObjectHeader := lspObjectHeader.Serialize() +func (o *LSPObject) Serialize() []uint8 { + lspObjectHeader := NewCommonObjectHeader(ObjectClassLSP, o.ObjectType, o.Len()) + byteLSPObjectHeader := lspObjectHeader.Serialize() buf := make([]uint8, 4) binary.BigEndian.PutUint32(buf, uint32(o.PlspID<<12)+uint32(o.OFlag<<4)) @@ -597,11 +690,11 @@ func (o *LspObject) Serialize() []uint8 { byteTLVs = AppendByteSlices(byteTLVs, tlv.Serialize()) } - byteLspObject := AppendByteSlices(byteLspObjectHeader, buf, byteTLVs) - return byteLspObject + byteLSPObject := AppendByteSlices(byteLSPObjectHeader, buf, byteTLVs) + return byteLSPObject } -func (o *LspObject) Len() uint16 { +func (o *LSPObject) Len() uint16 { tlvsByteLength := uint16(0) for _, tlv := range o.TLVs { tlvsByteLength += tlv.Len() @@ -609,12 +702,12 @@ func (o *LspObject) Len() uint16 { // Flags, SRP-ID (4byte) lspObjectBodyLength := uint16(4) + tlvsByteLength // CommonObjectHeader(4byte) + Flags, SRP-ID - return uint16(COMMON_OBJECT_HEADER_LENGTH) + lspObjectBodyLength + return uint16(commonObjectHeaderLength) + lspObjectBodyLength } -func NewLspObject(lspName string, color *uint32, plspID uint32) (*LspObject, error) { - o := &LspObject{ - ObjectType: OT_LSP_LSP, +func NewLSPObject(lspName string, color *uint32, plspID uint32) (*LSPObject, error) { + o := &LSPObject{ + ObjectType: ObjectTypeLSPLSP, Name: lspName, PlspID: plspID, CFlag: true, // (RFC8281 5.3.1) @@ -644,7 +737,7 @@ func NewLspObject(lspName string, color *uint32, plspID uint32) (*LspObject, err } // (I.D.draft-ietf-pce-pcep-color-12) -func (o *LspObject) Color() uint32 { +func (o *LSPObject) Color() uint32 { for _, tlv := range o.TLVs { if t, ok := tlv.(*Color); ok { return t.Color @@ -656,23 +749,23 @@ func (o *LspObject) Color() uint32 { // ERO Object (RFC5440 7.9) const ( - OT_ERO_EXPLICIT_ROUTE uint8 = 0x01 + ObjectTypeEROExplicitRoute ObjectType = 0x01 ) type EroObject struct { - ObjectType uint8 + ObjectType ObjectType EroSubobjects []EroSubobject } -func (o *EroObject) DecodeFromBytes(typ uint8, objectBody []uint8) error { +func (o *EroObject) DecodeFromBytes(typ ObjectType, objectBody []uint8) error { o.ObjectType = typ if len(objectBody) == 0 { return nil } for { var eroSubobj EroSubobject - switch objectBody[0] & 0x7f { - case OT_ERO_SR: + switch SubObjectType(objectBody[0] & 0x7f) { + case SubObjectTypeEROSR: eroSubobj = &SREroSubobject{} case OT_ERO_SRV6: eroSubobj = &SRv6EroSubobject{} @@ -701,7 +794,7 @@ func (o EroObject) Serialize() ([]uint8, error) { if err != nil { return nil, err } - eroObjectHeader := NewCommonObjectHeader(OC_ERO, o.ObjectType, eroObjectLength) + eroObjectHeader := NewCommonObjectHeader(ObjectClassERO, o.ObjectType, eroObjectLength) byteEroObjectHeader := eroObjectHeader.Serialize() byteEroObject := byteEroObjectHeader @@ -722,12 +815,12 @@ func (o EroObject) Len() (uint16, error) { eroSubobjByteLength += objByteLength } // CommonObjectHeader(4byte) + eroSubobjects(valiable) - return uint16(COMMON_OBJECT_HEADER_LENGTH) + eroSubobjByteLength, nil + return uint16(commonObjectHeaderLength) + eroSubobjByteLength, nil } func NewEroObject(segmentList []table.Segment) (*EroObject, error) { o := &EroObject{ - ObjectType: OT_ERO_EXPLICIT_ROUTE, + ObjectType: ObjectTypeEROExplicitRoute, EroSubobjects: []EroSubobject{}, } err := o.AddEroSubobjects(segmentList) @@ -786,58 +879,87 @@ func NewEroSubobject(seg table.Segment) (EroSubobject, error) { // SR-ERO Subobject (RFC8664 4.3.1) const ( - OT_ERO_SR uint8 = 0x24 + SubObjectTypeEROSR SubObjectType = 0x24 ) +type NAITypeSR uint8 + const ( - NT_ABSENT uint8 = 0x00 // RFC 8664 4.3.1 - NT_IPV4_NODE uint8 = 0x01 // RFC 8664 4.3.1 - NT_IPV6_NODE uint8 = 0x02 // RFC 8664 4.3.1 - NT_IPV4_ADJACENCY uint8 = 0x03 // RFC 8664 4.3.1 - NT_IPV6_ADJACENCY_GLOBAL uint8 = 0x04 // RFC 8664 4.3.1 - NT_UNNUMBERED_ADJACENCY uint8 = 0x05 // RFC 8664 4.3.1 - NT_IPV6_ADJACENCY_LINKLOCAL uint8 = 0x06 // RFC 8664 4.3.1 + NAITypeSRAbsent NAITypeSR = 0x00 + NAITypeSRIPv4Node NAITypeSR = 0x01 + NAITypeSRIPv6Node NAITypeSR = 0x02 + NAITypeSRIPv4Adjacency NAITypeSR = 0x03 + NAITypeSRIPv6AdjacencyGlobal NAITypeSR = 0x04 + NAITypeSRUnnumberedAdjacency NAITypeSR = 0x05 + NAITypeSRIPv6AdjacencyLinkLocal NAITypeSR = 0x06 ) +var naiTypeSRDescriptions = map[NAITypeSR]struct { + Description string + Reference string +}{ + NAITypeSRAbsent: {"NAI is absent", "RFC8664"}, + NAITypeSRIPv4Node: {"NAI is an IPv4 node ID", "RFC8664"}, + NAITypeSRIPv6Node: {"NAI is an IPv6 node ID", "RFC8664"}, + NAITypeSRIPv4Adjacency: {"NAI is an IPv4 adjacency", "RFC8664"}, + NAITypeSRIPv6AdjacencyGlobal: {"NAI is an IPv6 adjacency with global IPv6 addresses", "RFC8664"}, + NAITypeSRUnnumberedAdjacency: {"NAI is an unnumbered adjacency with IPv4 node IDs", "RFC8664"}, + NAITypeSRIPv6AdjacencyLinkLocal: {"NAI is an IPv6 adjacency with link-local IPv6 addresses", "RFC8664"}, +} + +func (nt NAITypeSR) String() string { + if desc, ok := naiTypeSRDescriptions[nt]; ok { + return fmt.Sprintf("%s (0x%02x)", desc.Description, uint8(nt)) + } + return fmt.Sprintf("Unknown NAI Type (0x%02x)", uint8(nt)) +} + +func (nt NAITypeSR) StringWithReference() string { + if desc, ok := naiTypeSRDescriptions[nt]; ok { + return fmt.Sprintf("%s (0x%02x) [%s]", desc.Description, uint8(nt), desc.Reference) + } + return fmt.Sprintf("Unknown NAI Type (0x%02x)", uint8(nt)) +} + type SREroSubobject struct { LFlag bool - SubobjectType uint8 + SubobjectType SubObjectType Length uint8 - NaiType uint8 + NAIType NAITypeSR FFlag bool SFlag bool CFlag bool MFlag bool Segment table.SegmentSRMPLS - Nai netip.Addr + NAI netip.Addr } -func (o *SREroSubobject) DecodeFromBytes(subObj []uint8) error { - o.LFlag = (subObj[0] & 0x80) != 0 - o.SubobjectType = subObj[0] & 0x7f - o.Length = subObj[1] - o.NaiType = subObj[2] >> 4 - o.FFlag = (subObj[3] & 0x08) != 0 - o.SFlag = (subObj[3] & 0x04) != 0 - o.CFlag = (subObj[3] & 0x02) != 0 - o.MFlag = (subObj[3] & 0x01) != 0 +func (o *SREroSubobject) DecodeFromBytes(subObject []uint8) error { + o.LFlag = (subObject[0] & 0x80) != 0 + o.SubobjectType = SubObjectType(subObject[0] & 0x7f) + o.Length = subObject[1] + o.NAIType = NAITypeSR(subObject[2] >> 4) + o.FFlag = (subObject[3] & 0x08) != 0 + o.SFlag = (subObject[3] & 0x04) != 0 + o.CFlag = (subObject[3] & 0x02) != 0 + o.MFlag = (subObject[3] & 0x01) != 0 - sid := binary.BigEndian.Uint32(subObj[4:8]) >> 12 + sid := binary.BigEndian.Uint32(subObject[4:8]) >> 12 o.Segment = table.NewSegmentSRMPLS(sid) - if o.NaiType == 1 { - o.Nai, _ = netip.AddrFromSlice(subObj[8:12]) + if o.NAIType == NAITypeSRIPv4Node { + o.NAI, _ = netip.AddrFromSlice(subObject[8:12]) } return nil } func (o *SREroSubobject) Serialize() []uint8 { buf := make([]uint8, 4) - buf[0] = o.SubobjectType + buf[0] = uint8(o.SubobjectType) if o.LFlag { buf[0] = buf[0] | 0x80 } buf[1] = o.Length - buf[2] = o.NaiType * 16 + buf[2] = uint8(o.NAIType) * 16 if o.FFlag { buf[3] = buf[3] | 0x08 } @@ -858,15 +980,15 @@ func (o *SREroSubobject) Serialize() []uint8 { } func (o *SREroSubobject) Len() (uint16, error) { - switch o.NaiType { - case NT_ABSENT: + switch o.NAIType { + case NAITypeSRAbsent: // Type, Length, Flags (4byte) + SID (4byte) return uint16(8), nil - case NT_IPV4_NODE: - // Type, Length, Flags (4byte) + SID (4byte) + Nai (4byte) + case NAITypeSRIPv4Node: + // Type, Length, Flags (4byte) + SID (4byte) + NAI (4byte) return uint16(12), nil - case NT_IPV6_NODE: - // Type, Length, Flags (4byte) + SID (4byte) + Nai (16byte) + case NAITypeSRIPv6Node: + // Type, Length, Flags (4byte) + SID (4byte) + NAI (16byte) return uint16(24), nil default: return uint16(0), errors.New("unsupported naitype") @@ -876,9 +998,9 @@ func (o *SREroSubobject) Len() (uint16, error) { func NewSREroSubObject(seg table.SegmentSRMPLS) (*SREroSubobject, error) { subo := &SREroSubobject{ LFlag: false, - SubobjectType: OT_ERO_SR, - NaiType: NT_ABSENT, - FFlag: true, // Nai is absent + SubobjectType: SubObjectTypeEROSR, + NAIType: NAITypeSRAbsent, + FFlag: true, // NAI is absent SFlag: false, CFlag: false, MFlag: true, // TODO: Determine either MPLS label or index @@ -898,21 +1020,47 @@ func (o *SREroSubobject) ToSegment() table.Segment { // SRv6-ERO Subobject (RFC9603 4.3.1) const ( - OT_ERO_SRV6 uint8 = 0x28 + OT_ERO_SRV6 SubObjectType = 0x28 ) +type NAITypeSRv6 uint8 + const ( - NT_MUST_NOT_BE_INCLUDED uint8 = 0x00 // draft-ietf-pce-segment-routing-ipv6 4.3.1 - NT_SRV6_NODE uint8 = 0x02 // draft-ietf-pce-segment-routing-ipv6 4.3.1 - NT_SRV6_ADJACENCY_GLOBAL uint8 = 0x04 // draft-ietf-pce-segment-routing-ipv6 4.3.1 - NT_SRV6_ADJACENCY_LINKLOCAL uint8 = 0x06 // draft-ietf-pce-segment-routing-ipv6 4.3.1 + NAITypeSRv6Absent NAITypeSRv6 = 0x00 + NAITypeSRv6IPv6Node NAITypeSRv6 = 0x02 + NAITypeSRv6IPv6AdjacencyGlobal NAITypeSRv6 = 0x04 + NAITypeSRv6IPv6AdjacencyLinkLocal NAITypeSRv6 = 0x06 ) +var naiTypeSRv6Descriptions = map[NAITypeSRv6]struct { + Description string + Reference string +}{ + NAITypeSRv6Absent: {"NAI is absent", "RFC9603"}, + NAITypeSRv6IPv6Node: {"NAI is an IPv6 node ID", "RFC9603"}, + NAITypeSRv6IPv6AdjacencyGlobal: {"NAI is an IPv6 adjacency with global IPv6 addresses", "RFC9603"}, + NAITypeSRv6IPv6AdjacencyLinkLocal: {"NAI is an IPv6 adjacency with link-local IPv6 addresses", "RFC9603"}, +} + +func (nt NAITypeSRv6) String() string { + if desc, ok := naiTypeSRv6Descriptions[nt]; ok { + return fmt.Sprintf("%s (0x%02x)", desc.Description, uint8(nt)) + } + return fmt.Sprintf("Unknown NAI Type (0x%02x)", uint8(nt)) +} + +func (nt NAITypeSRv6) StringWithReference() string { + if desc, ok := naiTypeSRv6Descriptions[nt]; ok { + return fmt.Sprintf("%s (0x%02x) [%s]", desc.Description, uint8(nt), desc.Reference) + } + return fmt.Sprintf("Unknown NAI Type (0x%02x)", uint8(nt)) +} + type SRv6EroSubobject struct { LFlag bool - SubobjectType uint8 + SubobjectType SubObjectType Length uint8 - NaiType uint8 + NAIType NAITypeSRv6 VFlag bool TFlag bool FFlag bool @@ -920,36 +1068,36 @@ type SRv6EroSubobject struct { Segment table.SegmentSRv6 } -func (o *SRv6EroSubobject) DecodeFromBytes(subObj []uint8) error { - o.LFlag = (subObj[0] & 0x80) != 0 - o.SubobjectType = subObj[0] & 0x7f - o.Length = subObj[1] - o.NaiType = subObj[2] >> 4 - o.VFlag = (subObj[3] & 0x08) != 0 - o.TFlag = (subObj[3] & 0x04) != 0 - o.FFlag = (subObj[3] & 0x02) != 0 - o.SFlag = (subObj[3] & 0x01) != 0 +func (o *SRv6EroSubobject) DecodeFromBytes(subObject []uint8) error { + o.LFlag = (subObject[0] & 0x80) != 0 + o.SubobjectType = SubObjectType(subObject[0] & 0x7f) + o.Length = subObject[1] + o.NAIType = NAITypeSRv6(subObject[2] >> 4) + o.VFlag = (subObject[3] & 0x08) != 0 + o.TFlag = (subObject[3] & 0x04) != 0 + o.FFlag = (subObject[3] & 0x02) != 0 + o.SFlag = (subObject[3] & 0x01) != 0 - sid, _ := netip.AddrFromSlice(subObj[8:24]) + sid, _ := netip.AddrFromSlice(subObject[8:24]) o.Segment = table.NewSegmentSRv6(sid) - if o.NaiType == NT_SRV6_NODE { - o.Segment.LocalAddr, _ = netip.AddrFromSlice(subObj[24:40]) + if o.NAIType == NAITypeSRv6IPv6Node { + o.Segment.LocalAddr, _ = netip.AddrFromSlice(subObject[24:40]) } - if o.NaiType == NT_SRV6_ADJACENCY_GLOBAL { - o.Segment.LocalAddr, _ = netip.AddrFromSlice(subObj[24:40]) - o.Segment.RemoteAddr, _ = netip.AddrFromSlice(subObj[40:56]) + if o.NAIType == NAITypeSRv6IPv6AdjacencyGlobal { + o.Segment.LocalAddr, _ = netip.AddrFromSlice(subObject[24:40]) + o.Segment.RemoteAddr, _ = netip.AddrFromSlice(subObject[40:56]) } return nil } func (o *SRv6EroSubobject) Serialize() []uint8 { buf := make([]uint8, 4) - buf[0] = o.SubobjectType + buf[0] = uint8(o.SubobjectType) if o.LFlag { buf[0] = buf[0] | 0x80 } buf[1] = o.Length - buf[2] = o.NaiType * 16 + buf[2] = uint8(o.NAIType) * 16 if o.VFlag { buf[3] = buf[3] | 0x08 } @@ -967,11 +1115,11 @@ func (o *SRv6EroSubobject) Serialize() []uint8 { binary.BigEndian.PutUint16(behavior, o.Segment.Behavior()) byteSid := o.Segment.Sid.AsSlice() - byteNai := []uint8{} + byteNAI := []uint8{} if o.Segment.LocalAddr.IsValid() { - byteNai = append(byteNai, o.Segment.LocalAddr.AsSlice()...) + byteNAI = append(byteNAI, o.Segment.LocalAddr.AsSlice()...) if o.Segment.RemoteAddr.IsValid() { - byteNai = append(byteNai, o.Segment.RemoteAddr.AsSlice()...) + byteNAI = append(byteNAI, o.Segment.RemoteAddr.AsSlice()...) } } @@ -981,7 +1129,7 @@ func (o *SRv6EroSubobject) Serialize() []uint8 { byteSidStructure = append(byteSidStructure, make([]uint8, 4)...) } - byteSRv6EroSubobject := AppendByteSlices(buf, reserved, behavior, byteSid, byteNai, byteSidStructure) + byteSRv6EroSubobject := AppendByteSlices(buf, reserved, behavior, byteSid, byteNAI, byteSidStructure) return byteSRv6EroSubobject } @@ -997,14 +1145,14 @@ func (o *SRv6EroSubobject) Len() (uint16, error) { } // NAI value in the subobject body is NOT absent if !o.FFlag { - switch o.NaiType { - case NT_IPV6_NODE: + switch o.NAIType { + case NAITypeSRv6IPv6Node: length += 16 - case NT_SRV6_ADJACENCY_GLOBAL: + case NAITypeSRv6IPv6AdjacencyGlobal: length += 32 - case NT_SRV6_ADJACENCY_LINKLOCAL: + case NAITypeSRv6IPv6AdjacencyLinkLocal: length += 40 - case NT_MUST_NOT_BE_INCLUDED: + case NAITypeSRv6Absent: return uint16(0), errors.New("when naitype is 0 then FFlag must be 1") default: return uint16(0), errors.New("unsupported naitype") @@ -1031,18 +1179,18 @@ func NewSRv6EroSubObject(seg table.SegmentSRv6) (*SRv6EroSubobject, error) { subo.TFlag = false } if seg.LocalAddr.IsValid() { - subo.FFlag = false // Nai is present + subo.FFlag = false // NAI is present if seg.RemoteAddr.IsValid() { // End.X or uA - subo.NaiType = NT_SRV6_ADJACENCY_GLOBAL + subo.NAIType = NAITypeSRv6IPv6AdjacencyGlobal } else { // End or uN - subo.NaiType = NT_SRV6_NODE + subo.NAIType = NAITypeSRv6IPv6Node } } else { subo.FFlag = true // SID is absent - subo.NaiType = NT_MUST_NOT_BE_INCLUDED + subo.NAIType = NAITypeSRv6Absent } length, err := subo.Len() @@ -1059,12 +1207,12 @@ func (o *SRv6EroSubobject) ToSegment() table.Segment { // END-POINTS Object (RFC5440 7.6) const ( - OT_EP_IPV4 uint8 = 1 - OT_EP_IPV6 uint8 = 2 + ObjectTypeEndpointIPv4 ObjectType = 0x01 + ObjectTypeEndpointIPv6 ObjectType = 0x02 ) type EndpointsObject struct { - ObjectType uint8 + ObjectType ObjectType SrcAddr netip.Addr DstAddr netip.Addr } @@ -1074,7 +1222,7 @@ func (o *EndpointsObject) Serialize() ([]uint8, error) { if err != nil { return nil, err } - endpointsObjectHeader := NewCommonObjectHeader(OC_END_POINTS, o.ObjectType, endpointsObjectLength) + endpointsObjectHeader := NewCommonObjectHeader(ObjectClassEndpoints, o.ObjectType, endpointsObjectLength) byteEroObjectHeader := endpointsObjectHeader.Serialize() byteEndpointsObject := AppendByteSlices(byteEroObjectHeader, o.SrcAddr.AsSlice(), o.DstAddr.AsSlice()) @@ -1085,10 +1233,10 @@ func (o EndpointsObject) Len() (uint16, error) { var length uint16 if o.SrcAddr.Is4() && o.DstAddr.Is4() { // CommonObjectHeader(4byte) + srcIPv4 (4byte) + dstIPv4 (4byte) - length = COMMON_OBJECT_HEADER_LENGTH + 4 + 4 + length = commonObjectHeaderLength + 4 + 4 } else if o.SrcAddr.Is6() && o.DstAddr.Is6() { // CommonObjectHeader(4byte) + srcIPv4 (16byte) + dstIPv4 (16byte) - length = COMMON_OBJECT_HEADER_LENGTH + 16 + 16 + length = commonObjectHeaderLength + 16 + 16 } else { return uint16(0), errors.New("invalid endpoints address") } @@ -1096,17 +1244,17 @@ func (o EndpointsObject) Len() (uint16, error) { } func NewEndpointsObject(dstAddr netip.Addr, srcAddr netip.Addr) (*EndpointsObject, error) { - var objType uint8 + var objectType ObjectType if dstAddr.Is4() && srcAddr.Is4() { - objType = OT_EP_IPV4 + objectType = ObjectTypeEndpointIPv4 } else if dstAddr.Is6() && srcAddr.Is6() { - objType = OT_EP_IPV6 + objectType = ObjectTypeEndpointIPv6 } else { return nil, errors.New("invalid endpoints address") } o := &EndpointsObject{ - ObjectType: objType, + ObjectType: objectType, DstAddr: dstAddr, SrcAddr: srcAddr, } @@ -1115,40 +1263,33 @@ func NewEndpointsObject(dstAddr netip.Addr, srcAddr netip.Addr) (*EndpointsObjec // ASSOCIATION Object (RFC8697 6.) const ( - OT_ASSOC_IPV4 uint8 = 1 - OT_ASSOC_IPV6 uint8 = 2 + ObjectTypeAssociationIPv4 ObjectType = 0x01 + ObjectTypeAssociationIPv6 ObjectType = 0x02 ) const ( - ASSOC_TYPE_SR_POLICY_ASSOCIATION uint16 = 0x06 -) - -// Juniper specific TLV (deprecated) -const ( - JUNIPER_SPEC_TLV_EXTENDED_ASSOCIATION_ID uint16 = 65507 - JUNIPER_SPEC_TLV_SRPOLICY_CPATH_ID uint16 = 65508 - JUNIPER_SPEC_TLV_SRPOLICY_CPATH_PREFERENCE uint16 = 65509 - - JUNIPER_SPEC_ASSOC_TYPE_SR_POLICY_ASSOCIATION uint16 = 65505 + AssociationTypeSRPolicyAssociation AssocType = 0x06 + AssociationTypeSRPolicyAssociationCisco AssocType = 0x14 + AssociationTypeSRPolicyAssociationJuniper AssocType = 0xffe1 // Juniper specific TLV (deprecated) ) type AssociationObject struct { - ObjectType uint8 + ObjectType ObjectType RFlag bool - AssocType uint16 + AssocType AssocType AssocID uint16 AssocSrc netip.Addr TLVs []TLVInterface } -func (o *AssociationObject) DecodeFromBytes(typ uint8, objectBody []uint8) error { +func (o *AssociationObject) DecodeFromBytes(typ ObjectType, objectBody []uint8) error { o.ObjectType = typ o.RFlag = (objectBody[3] & 0x01) != 0 - o.AssocType = uint16(binary.BigEndian.Uint16(objectBody[4:6])) + o.AssocType = AssocType(binary.BigEndian.Uint16(objectBody[4:6])) o.AssocID = uint16(binary.BigEndian.Uint16(objectBody[6:8])) switch o.ObjectType { - case OT_ASSOC_IPV4: + case ObjectTypeAssociationIPv4: assocSrcBytes, _ := netip.AddrFromSlice(objectBody[8:12]) o.AssocSrc = assocSrcBytes if len(objectBody) > 12 { @@ -1158,7 +1299,7 @@ func (o *AssociationObject) DecodeFromBytes(typ uint8, objectBody []uint8) error return err } } - case OT_ASSOC_IPV6: + case ObjectTypeAssociationIPv6: o.AssocSrc, _ = netip.AddrFromSlice(objectBody[8:24]) if len(objectBody) > 24 { byteTLVs := objectBody[24:] @@ -1179,7 +1320,7 @@ func (o *AssociationObject) Serialize() ([]uint8, error) { if err != nil { return nil, err } - associationObjectHeader := NewCommonObjectHeader(OC_ASSOCIATION, o.ObjectType, associationObjectLength) + associationObjectHeader := NewCommonObjectHeader(ObjectClassAssociation, o.ObjectType, associationObjectLength) byteAssociationObjectHeader := associationObjectHeader.Serialize() @@ -1189,7 +1330,7 @@ func (o *AssociationObject) Serialize() ([]uint8, error) { buf[4] = buf[4] | 0x01 } - assocType := Uint16ToByteSlice(o.AssocType) + assocType := Uint16ToByteSlice(uint16(o.AssocType)) assocID := Uint16ToByteSlice(o.AssocID) byteTLVs := []uint8{} @@ -1218,45 +1359,45 @@ func (o AssociationObject) Len() (uint16, error) { } else { return uint16(0), errors.New("invalid association source address") } - return (COMMON_OBJECT_HEADER_LENGTH + associationObjectBodyLength), nil + return (commonObjectHeaderLength + associationObjectBodyLength), nil } func NewAssociationObject(srcAddr netip.Addr, dstAddr netip.Addr, color uint32, preference uint32, opt ...Opt) (*AssociationObject, error) { opts := optParams{ - pccType: RFC_COMPLIANT, + pccType: RFCCompliant, } for _, o := range opt { o(&opts) } - var objType uint8 + var objectType ObjectType if dstAddr.Is4() && srcAddr.Is4() { - objType = OT_EP_IPV4 + objectType = ObjectTypeEndpointIPv4 } else if dstAddr.Is6() && srcAddr.Is6() { - objType = OT_EP_IPV6 + objectType = ObjectTypeEndpointIPv6 } else { return nil, errors.New("invalid endpoints address") } o := &AssociationObject{ - ObjectType: objType, + ObjectType: objectType, RFlag: false, TLVs: []TLVInterface{}, AssocSrc: srcAddr, } - if opts.pccType == JUNIPER_LEGACY { + if opts.pccType == JuniperLegacy { o.AssocID = 0 - o.AssocType = JUNIPER_SPEC_ASSOC_TYPE_SR_POLICY_ASSOCIATION + o.AssocType = AssociationTypeSRPolicyAssociationJuniper associationObjectTLVs := []TLVInterface{ &UndefinedTLV{ - Typ: JUNIPER_SPEC_TLV_EXTENDED_ASSOCIATION_ID, - Length: TLV_EXTENDED_ASSOCIATION_ID_IPV4_LENGTH, // JUNIPER_LEGACY has only IPv4 implementation + Typ: TLVExtendedAssociationIDIPv4Juniper, + Length: TLVExtendedAssociationIDIPv4ValueLength, // JuniperLegacy has only IPv4 implementation Value: AppendByteSlices( Uint32ToByteSlice(color), dstAddr.AsSlice(), ), }, &UndefinedTLV{ - Typ: JUNIPER_SPEC_TLV_SRPOLICY_CPATH_ID, - Length: TLV_SRPOLICY_CPATH_ID_LENGTH, + Typ: TLVSRPolicyCPathIDJuniper, + Length: TLVSRPolicyCPathIDValueLength, Value: []uint8{ 0x00, // protocol origin 0x00, 0x00, 0x00, // mbz @@ -1266,15 +1407,15 @@ func NewAssociationObject(srcAddr netip.Addr, dstAddr netip.Addr, color uint32, }, }, &UndefinedTLV{ - Typ: JUNIPER_SPEC_TLV_SRPOLICY_CPATH_PREFERENCE, - Length: TLV_SRPOLICY_CPATH_PREFERENCE_LENGTH, + Typ: TLVSRPolicyCPathPreferenceJuniper, + Length: TLVSRPolicyCPathPreferenceValueLength, Value: Uint32ToByteSlice(preference), }, } o.TLVs = append(o.TLVs, associationObjectTLVs...) } else { - o.AssocID = 1 // (I.D. pce-segment-routing-policy-cp-07 5.1) - o.AssocType = ASSOC_TYPE_SR_POLICY_ASSOCIATION // (I.D. pce-segment-routing-policy-cp-07 5.1) + o.AssocID = 1 // (I.D. pce-segment-routing-policy-cp-07 5.1) + o.AssocType = AssociationTypeSRPolicyAssociation // (I.D. pce-segment-routing-policy-cp-07 5.1) associationObjectTLVs := []TLVInterface{ &ExtendedAssociationID{ Color: color, @@ -1297,7 +1438,7 @@ func NewAssociationObject(srcAddr netip.Addr, dstAddr netip.Addr, color uint32, func (o *AssociationObject) Color() uint32 { for _, tlv := range o.TLVs { if t, ok := tlv.(*UndefinedTLV); ok { - if t.Type() == JUNIPER_SPEC_TLV_EXTENDED_ASSOCIATION_ID { + if t.Type() == TLVExtendedAssociationIDIPv4Juniper { return uint32(binary.BigEndian.Uint32(t.Value[:4])) } } else if t, ok := tlv.(*ExtendedAssociationID); ok { @@ -1312,7 +1453,7 @@ func (o *AssociationObject) Color() uint32 { func (o *AssociationObject) Preference() uint32 { for _, tlv := range o.TLVs { if t, ok := tlv.(*UndefinedTLV); ok { - if t.Type() == JUNIPER_SPEC_TLV_SRPOLICY_CPATH_PREFERENCE { + if t.Type() == TLVSRPolicyCPathPreferenceJuniper { return uint32(binary.BigEndian.Uint32(t.Value)) } } else if t, ok := tlv.(*SRPolicyCandidatePathPreference); ok { @@ -1333,26 +1474,20 @@ func (o *AssociationObject) Endpoint() netip.Addr { // VENDOR-INFORMATION Object (RFC7470 4) const ( - OT_VENDOR_SPECIFIC_CONSTRAINTS uint8 = 1 + ObjectTypeVendorSpecificConstraints ObjectType = 0x01 ) const ( - EN_CISCO uint32 = 9 - - CISCO_SPEC_TLV_COLOR uint16 = 1 - CISCO_SPEC_TLV_PREFERENCE uint16 = 3 - - CISCO_SPEC_TLV_COLOR_LENGTH uint16 = 4 - CISCO_SPEC_TLV_PREFERENCE_LENGTH uint16 = 4 + EnterpriseNumberCisco uint32 = 9 ) type VendorInformationObject struct { - ObjectType uint8 // vendor specific constraints: 1 + ObjectType ObjectType // vendor specific constraints: 1 EnterpriseNumber uint32 TLVs []TLVInterface } -func (o *VendorInformationObject) DecodeFromBytes(typ uint8, objectBody []uint8) error { +func (o *VendorInformationObject) DecodeFromBytes(typ ObjectType, objectBody []uint8) error { o.ObjectType = typ o.EnterpriseNumber = binary.BigEndian.Uint32(objectBody[0:4]) if len(objectBody) > 4 { @@ -1367,7 +1502,7 @@ func (o *VendorInformationObject) DecodeFromBytes(typ uint8, objectBody []uint8) } func (o *VendorInformationObject) Serialize() []uint8 { - vendorInformationObjectHeader := NewCommonObjectHeader(OC_VENDOR_INFORMATION, o.ObjectType, o.Len()) + vendorInformationObjectHeader := NewCommonObjectHeader(ObjectClassVendorInformation, o.ObjectType, o.Len()) byteVendorInformationObjectHeader := vendorInformationObjectHeader.Serialize() enterpriseNumber := Uint32ToByteSlice(o.EnterpriseNumber) @@ -1386,27 +1521,27 @@ func (o *VendorInformationObject) Serialize() []uint8 { func (o VendorInformationObject) Len() uint16 { // TODO: Expantion for IPv6 Endpoint // CommonObjectHeader(4byte) + Enterprise Number (4byte) + colorTLV (8byte) + preferenceTLV (8byte) - return uint16(COMMON_OBJECT_HEADER_LENGTH + 4 + 8 + 8) + return uint16(commonObjectHeaderLength + 4 + 8 + 8) } func NewVendorInformationObject(vendor PccType, color uint32, preference uint32) (*VendorInformationObject, error) { o := &VendorInformationObject{ // for Cisco PCC - ObjectType: OT_VENDOR_SPECIFIC_CONSTRAINTS, // (RFC7470 4) + ObjectType: ObjectTypeVendorSpecificConstraints, // (RFC7470 4) TLVs: []TLVInterface{}, } - if vendor == CISCO_LEGACY { - o.EnterpriseNumber = EN_CISCO + if vendor == CiscoLegacy { + o.EnterpriseNumber = EnterpriseNumberCisco vendorInformationObjectTLVs := []TLVInterface{ &UndefinedTLV{ - Typ: CISCO_SPEC_TLV_COLOR, - Length: CISCO_SPEC_TLV_COLOR_LENGTH, // TODO: 20 if ipv6 endpoint + Typ: SubTLVColorCisco, + Length: SubTLVColorCiscoValueLength, // TODO: 20 if ipv6 endpoint Value: AppendByteSlices( Uint32ToByteSlice(color), ), }, &UndefinedTLV{ - Typ: CISCO_SPEC_TLV_PREFERENCE, - Length: CISCO_SPEC_TLV_PREFERENCE_LENGTH, + Typ: SubTLVPreferenceCisco, + Length: SubTLVPreferenceCiscoValueLength, Value: Uint32ToByteSlice(preference), }, } @@ -1420,7 +1555,7 @@ func NewVendorInformationObject(vendor PccType, color uint32, preference uint32) func (o *VendorInformationObject) Color() uint32 { for _, tlv := range o.TLVs { if t, ok := tlv.(*UndefinedTLV); ok { - if t.Type() == CISCO_SPEC_TLV_COLOR { + if t.Type() == SubTLVColorCisco { return uint32(binary.BigEndian.Uint32(t.Value)) } } @@ -1431,10 +1566,22 @@ func (o *VendorInformationObject) Color() uint32 { func (o *VendorInformationObject) Preference() uint32 { for _, tlv := range o.TLVs { if t, ok := tlv.(*UndefinedTLV); ok { - if t.Type() == CISCO_SPEC_TLV_PREFERENCE { + if t.Type() == SubTLVPreferenceCisco { return uint32(binary.BigEndian.Uint32(t.Value)) } } } return 0 } + +type optParams struct { + pccType PccType +} + +type Opt func(*optParams) + +func VendorSpecific(pt PccType) Opt { + return func(op *optParams) { + op.pccType = pt + } +} diff --git a/pkg/packet/pcep/tlv.go b/pkg/packet/pcep/tlv.go index b9494421..6673c98a 100644 --- a/pkg/packet/pcep/tlv.go +++ b/pkg/packet/pcep/tlv.go @@ -17,130 +17,234 @@ import ( "go.uber.org/zap/zapcore" ) +type TLVType uint16 + const ( // PCEP TLV - TLV_RESERVED uint16 = 0x00 // RFC5440 - TLV_NO_PATH_VECTOR uint16 = 0x01 // RFC5440 - TLV_OVERLOAD_DURATION uint16 = 0x02 // RFC5440 - TLV_REQ_MISSING uint16 = 0x03 // RFC5440 - TLV_OF_LIST uint16 = 0x04 // RFC5541 - TLV_ORDER uint16 = 0x05 // RFC5557 - TLV_P2MP_CAPABLE uint16 = 0x06 // RFC8306 - TLV_VENDOR_INFORMATION uint16 = 0x07 // RFC7470 - TLV_WAVELENGTH_SELECTION uint16 = 0x08 // RFC8780 - TLV_WAVELENGTH_RESTRICTION uint16 = 0x09 // RFC8780 - TLV_WAVELENGTH_ALLOCATION uint16 = 0x0a // RFC8780 - TLV_OPTICAL_INTERFACE_CLASS_LIST uint16 = 0x0b // RFC8780 - TLV_CLIENT_SIGNAL_INFORMATION uint16 = 0x0c // RFC8780 - TLV_H_PCE_CAPABILITY uint16 = 0x0d // RFC8685 - TLV_DOMAIN_ID uint16 = 0x0e // RFC8685 - TLV_H_PCE_FLAG uint16 = 0x0f // RFC8685 - TLV_STATEFUL_PCE_CAPABILITY uint16 = 0x10 // RFC8231 - TLV_SYMBOLIC_PATH_NAME uint16 = 0x11 // RFC8231 - TLV_IPV4_LSP_IDENTIFIERS uint16 = 0x12 // RFC8231 - TLV_IPV6_LSP_IDENTIFIERS uint16 = 0x13 // RFC8231 - TLV_LSP_ERROR_CODE uint16 = 0x14 // RFC8231 - TLV_RSVP_ERROR_SPEC uint16 = 0x15 // RFC8231 - TLV_LSP_DB_VERSION uint16 = 0x17 // RFC8232 - TLV_SPEAKER_ENTITY_ID uint16 = 0x18 // RFC8232 - TLV_SR_PCE_CAPABILITY uint16 = 0x1a // RFC8664 - TLV_PATH_SETUP_TYPE uint16 = 0x1c // RFC8408 - TLV_OPERATOR_CONFIGURED_ASSOCIATION_RANGE uint16 = 0x1d // RFC8697 - TLV_GLOBAL_ASSOCIATION_SOURCE uint16 = 0x1e // RFC8697 - TLV_EXTENDED_ASSOCIATION_ID uint16 = 0x1f // RFC8697 - TLV_P2MP_IPV4_LSP_IDENTIFIERS uint16 = 0x20 // RFC8623 - TLV_P2MP_IPV6_LSP_IDENTIFIERS uint16 = 0x21 // RFC8623 - TLV_PATH_SETUP_TYPE_CAPABILITY uint16 = 0x22 // RFC8408 - TLV_ASSOC_TYPE_LIST uint16 = 0x23 // RFC8697 - TLV_AUTO_BANDWIDTH_CAPABILITY uint16 = 0x24 // RFC8733 - TLV_AUTO_BANDWIDTH_ATTRIBUTES uint16 = 0x25 // RFC8733 - TLV_PATH_PROTECTION_ASSOCIATION_GROUP_TLV uint16 = 0x26 // RFC8745 - TLV_IPV4_ADDRESS uint16 = 0x27 // RFC8779 - TLV_IPV6_ADDRESS uint16 = 0x28 // RFC8779 - TLV_UNNUMBERED_ENDPOINT uint16 = 0x29 // RFC8779 - TLV_LABEL_REQUEST uint16 = 0x2a // RFC8779 - TLV_LABEL_SET uint16 = 0x2b // RFC8779 - TLV_PROTECTION_ATTRIBUTE uint16 = 0x2c // RFC8779 - TLV_GMPLS_CAPABILITY uint16 = 0x2d // RFC8779 - TLV_DISJOINTNESS_CONFIGURATION uint16 = 0x2e // RFC8800 - TLV_DISJOINTNESS_STATUS uint16 = 0x2f // RFC8800 - TLV_POLICY_PARAMETERSjTLV uint16 = 0x30 // RFC9005 - TLV_SCHED_LSP_ATTRIBUTE uint16 = 0x31 // RFC8934 - TLV_SCHED_PD_LSP_ATTRIBUTE uint16 = 0x32 // RFC8934 - TLV_PCE_FLOWSPEC_CAPABILITY uint16 = 0x33 // RFC9168 - TLV_FLOW_FILTER uint16 = 0x34 // RFC9168 - TLV_BIDIRECTIONAL_LSP_ASSOCIATION_GROUP uint16 = 0x36 // RFC9059 - TLV_TE_PATH_BINDING uint16 = 0x37 // RFC9604 - TLV_SRPOLICY_POL_NAME uint16 = 0x38 // ietf-pce-segment-routing-policy-cp-07 - TLV_SRPOLICY_CPATH_ID uint16 = 0x39 // ietf-pce-segment-routing-policy-cp-07 - TLV_SRPOLICY_CPATH_NAME uint16 = 0x3a // ietf-pce-segment-routing-policy-cp-07 - TLV_SRPOLICY_CPATH_PREFERENCE uint16 = 0x3b // ietf-pce-segment-routing-policy-cp-07 - TLV_MULTIPATH_CAP uint16 = 0x3c // ietf-pce-pcep-multipath-07 - TLV_MULTIPATH_WIGHT uint16 = 0x3d // ietf-pce-pcep-multipath-07 - TLV_MULTIPATH_BACKUP uint16 = 0x3e // ietf-pce-pcep-multipath-07 - TLV_LSP_EXTENDED_FLAG uint16 = 0x3f // RFC9357 - TLV_VIRTUAL_NETWORK_TLV uint16 = 0x41 // RFC9358 - TLV_SR_ALGORITHM uint16 = 0x42 // ietf-pce-sid-algo-12 - TLV_COLOR uint16 = 0x43 // ietf-pce-pcep-color-06 - TLV_COMPUTATION_PRIORITY uint16 = 0x44 // ietf-pce-segment-routing-policy-cp-14 - TLV_EXPLICIT_NULL_LABEL_POLICY uint16 = 0x45 // draft-ietf-pce-segment-routing-policy-cp-14 - TLV_INVALIDATION uint16 = 0x4c // draft-ietf-pce-segment-routing-policy-cp-14 - TLV_SRPOLICY_CAPABILITY uint16 = 0x4d // draft-ietf-pce-segment-routing-policy-cp-14 - TLV_PATH_RECOMPUTATION uint16 = 0x4e // draft-ietf-pce-circuit-style-pcep-extensions-03 - TLV_SR_P2MP_POLICY_CAPABILITY uint16 = 0x4f // draft-ietf-pce-sr-p2mp-policy-09 - TLV_IPV4_SR_P2MP_INSTANCE_ID uint16 = 0x50 // draft-ietf-pce-sr-p2mp-policy-09 - TLV_IPV6_SR_P2MP_INSTANCE_ID uint16 = 0x51 // draft-ietf-pce-sr-p2mp-policy-09 + TLVNoPathVector TLVType = 0x01 + TLVOverloadDuration TLVType = 0x02 + TLVReqMissing TLVType = 0x03 + TLVOFList TLVType = 0x04 + TLVOrder TLVType = 0x05 + TLVP2MPCapable TLVType = 0x06 + TLVVendorInformation TLVType = 0x07 + TLVWavelengthSelection TLVType = 0x08 + TLVWavelengthRestriction TLVType = 0x09 + TLVWavelengthAllocation TLVType = 0x0a + TLVOpticalInterfaceClassList TLVType = 0x0b + TLVClientSignalInformation TLVType = 0x0c + TLVHPceCapability TLVType = 0x0d + TLVDomainID TLVType = 0x0e + TLVHPceFlag TLVType = 0x0f + TLVStatefulPceCapability TLVType = 0x10 + TLVSymbolicPathName TLVType = 0x11 + TLVIPv4LSPIdentifiers TLVType = 0x12 + TLVIPv6LSPIdentifiers TLVType = 0x13 + TLVLSPErrorCode TLVType = 0x14 + TLVRsvpErrorSpec TLVType = 0x15 + TLVLSPDBVersion TLVType = 0x17 + TLVSpeakerEntityID TLVType = 0x18 + TLVSRPceCapability TLVType = 0x1a + TLVPathSetupType TLVType = 0x1c + TLVOperatorConfiguredAssociationRange TLVType = 0x1d + TLVGlobalAssociationSource TLVType = 0x1e + TLVExtendedAssociationID TLVType = 0x1f + TLVP2MPIPv4LSPIdentifiers TLVType = 0x20 + TLVP2MPIPv6LSPIdentifiers TLVType = 0x21 + TLVPathSetupTypeCapability TLVType = 0x22 + TLVAssocTypeList TLVType = 0x23 + TLVAutoBandwidthCapability TLVType = 0x24 + TLVAutoBandwidthAttributes TLVType = 0x25 + TLVPathProtectionAssociationGroupTLV TLVType = 0x26 + TLVIPv4Address TLVType = 0x27 + TLVIPv6Address TLVType = 0x28 + TLVUnnumberedEndpoint TLVType = 0x29 + TLVLabelRequest TLVType = 0x2a + TLVLabelSet TLVType = 0x2b + TLVProtectionAttribute TLVType = 0x2c + TLVGmplsCapability TLVType = 0x2d + TLVDisjointnessConfiguration TLVType = 0x2e + TLVDisjointnessStatus TLVType = 0x2f + TLVPolicyParameters TLVType = 0x30 + TLVSchedLSPAttribute TLVType = 0x31 + TLVSchedPdLSPAttribute TLVType = 0x32 + TLVPceFlowspecCapability TLVType = 0x33 + TLVFlowFilter TLVType = 0x34 + TLVBidirectionalLSPAssociationGroup TLVType = 0x36 + TLVTePathBinding TLVType = 0x37 + TLVSRPolicyPolName TLVType = 0x38 + TLVSRPolicyCPathID TLVType = 0x39 + TLVSRPolicyCPathName TLVType = 0x3a + TLVSRPolicyCPathPreference TLVType = 0x3b + TLVMultipathCap TLVType = 0x3c + TLVMultipathWeight TLVType = 0x3d + TLVMultipathBackup TLVType = 0x3e + TLVMultipathOppdirPath TLVType = 0x3f + TLVLSPExtendedFlag TLVType = 0x40 + TLVVirtualNetwork TLVType = 0x41 + TLVSrAlgorithm TLVType = 0x42 + TLVColor TLVType = 0x43 + TLVComputationPriority TLVType = 0x44 + TLVExplicitNullLabelPolicy TLVType = 0x45 + TLVInvalidation TLVType = 0x46 + TLVSRPolicyCapability TLVType = 0x47 + TLVPathRecomputation TLVType = 0x48 + TLVSRP2MPPolicyCapability TLVType = 0x49 + TLVIPv4SrP2MPInstanceID TLVType = 0x4a + TLVIPv6SrP2MPInstanceID TLVType = 0x4b ) -var tlvMap = map[uint16]func() TLVInterface{ - TLV_STATEFUL_PCE_CAPABILITY: func() TLVInterface { return &StatefulPceCapability{} }, - TLV_SYMBOLIC_PATH_NAME: func() TLVInterface { return &SymbolicPathName{} }, - TLV_IPV4_LSP_IDENTIFIERS: func() TLVInterface { return &IPv4LspIdentifiers{} }, - TLV_IPV6_LSP_IDENTIFIERS: func() TLVInterface { return &IPv6LspIdentifiers{} }, - TLV_LSP_DB_VERSION: func() TLVInterface { return &LSPDBVersion{} }, - TLV_SR_PCE_CAPABILITY: func() TLVInterface { return &SRPceCapability{} }, - TLV_PATH_SETUP_TYPE: func() TLVInterface { return &PathSetupType{} }, - TLV_EXTENDED_ASSOCIATION_ID: func() TLVInterface { return &ExtendedAssociationID{} }, - TLV_PATH_SETUP_TYPE_CAPABILITY: func() TLVInterface { return &PathSetupTypeCapability{} }, - TLV_ASSOC_TYPE_LIST: func() TLVInterface { return &AssocTypeList{} }, - TLV_COLOR: func() TLVInterface { return &Color{} }, +var tlvDescriptions = map[TLVType]struct { + Description string + Reference string +}{ + TLVNoPathVector: {"NO-PATH-VECTOR", "RFC5440"}, + TLVOverloadDuration: {"OVERLOAD-DURATION", "RFC5440"}, + TLVReqMissing: {"REQ-MISSING", "RFC5440"}, + TLVOFList: {"OF-LIST", "RFC5541"}, + TLVOrder: {"ORDER", "RFC5557"}, + TLVP2MPCapable: {"P2MP-CAPABLE", "RFC8306"}, + TLVVendorInformation: {"VENDOR-INFORMATION", "RFC7470"}, + TLVWavelengthSelection: {"WAVELENGTH-SELECTION", "RFC8780"}, + TLVWavelengthRestriction: {"WAVELENGTH-RESTRICTION", "RFC8780"}, + TLVWavelengthAllocation: {"WAVELENGTH-ALLOCATION", "RFC8780"}, + TLVOpticalInterfaceClassList: {"OPTICAL-INTERFACE-CLASS-LIST", "RFC8780"}, + TLVClientSignalInformation: {"CLIENT-SIGNAL-INFORMATION", "RFC8780"}, + TLVHPceCapability: {"H-PCE-CAPABILITY", "RFC8685"}, + TLVDomainID: {"DOMAIN-ID", "RFC8685"}, + TLVHPceFlag: {"H-PCE-FLAG", "RFC8685"}, + TLVStatefulPceCapability: {"STATEFUL-PCE-CAPABILITY", "RFC8231"}, + TLVSymbolicPathName: {"SYMBOLIC-PATH-NAME", "RFC8231"}, + TLVIPv4LSPIdentifiers: {"IPV4-LSP-IDENTIFIERS", "RFC8231"}, + TLVIPv6LSPIdentifiers: {"IPV6-LSP-IDENTIFIERS", "RFC8231"}, + TLVLSPErrorCode: {"LSP-ERROR-CODE", "RFC8231"}, + TLVRsvpErrorSpec: {"RSVP-ERROR-SPEC", "RFC8231"}, + TLVLSPDBVersion: {"LSP-DB-VERSION", "RFC8232"}, + TLVSpeakerEntityID: {"SPEAKER-ENTITY-ID", "RFC8232"}, + TLVSRPceCapability: {"SR-PCE-CAPABILITY", "RFC8664"}, + TLVPathSetupType: {"PATH-SETUP-TYPE", "RFC8408"}, + TLVOperatorConfiguredAssociationRange: {"OPERATOR-CONFIGURED-ASSOCIATION-RANGE", "RFC8697"}, + TLVGlobalAssociationSource: {"GLOBAL-ASSOCIATION-SOURCE", "RFC8697"}, + TLVExtendedAssociationID: {"EXTENDED-ASSOCIATION-ID", "RFC8697"}, + TLVP2MPIPv4LSPIdentifiers: {"P2MP-IPV4-LSP-IDENTIFIERS", "RFC8623"}, + TLVP2MPIPv6LSPIdentifiers: {"P2MP-IPV6-LSP-IDENTIFIERS", "RFC8623"}, + TLVPathSetupTypeCapability: {"PATH-SETUP-TYPE-CAPABILITY", "RFC8408"}, + TLVAssocTypeList: {"ASSOC-TYPE-LIST", "RFC8697"}, + TLVAutoBandwidthCapability: {"AUTO-BANDWIDTH-CAPABILITY", "RFC8733"}, + TLVAutoBandwidthAttributes: {"AUTO-BANDWIDTH-ATTRIBUTES", "RFC8733"}, + TLVPathProtectionAssociationGroupTLV: {"PATH-PROTECTION-ASSOCIATION-GROUP", "RFC8745"}, + TLVIPv4Address: {"IPV4-ADDRESS", "RFC8779"}, + TLVIPv6Address: {"IPV6-ADDRESS", "RFC8779"}, + TLVUnnumberedEndpoint: {"UNNUMBERED-ENDPOINT", "RFC8779"}, + TLVLabelRequest: {"LABEL-REQUEST", "RFC8779"}, + TLVLabelSet: {"LABEL-SET", "RFC8779"}, + TLVProtectionAttribute: {"PROTECTION-ATTRIBUTE", "RFC8779"}, + TLVGmplsCapability: {"GMPLS-CAPABILITY", "RFC8779"}, + TLVDisjointnessConfiguration: {"DISJOINTNESS-CONFIGURATION", "RFC8800"}, + TLVDisjointnessStatus: {"DISJOINTNESS-STATUS", "RFC8800"}, + TLVPolicyParameters: {"POLICY-PARAMETERS-TLV", "RFC9005"}, + TLVSchedLSPAttribute: {"SCHED-LSP-ATTRIBUTE", "RFC8934"}, + TLVSchedPdLSPAttribute: {"SCHED-PD-LSP-ATTRIBUTE", "RFC8934"}, + TLVPceFlowspecCapability: {"PCE-FLOWSPEC-CAPABILITY TLV", "RFC9168"}, + TLVFlowFilter: {"FLOW-FILTER-TLV", "RFC9168"}, + TLVBidirectionalLSPAssociationGroup: {"BIDIRECTIONAL-LSP Association Group TLV", "RFC9059"}, + TLVTePathBinding: {"TE-PATH-BINDING", "RFC9604"}, + TLVSRPolicyPolName: {"SRPOLICY-POL-NAME", "draft-ietf-pce-segment-routing-policy-cp-14"}, + TLVSRPolicyCPathID: {"SRPOLICY-CPATH-ID", "draft-ietf-pce-segment-routing-policy-cp-14"}, + TLVSRPolicyCPathName: {"SRPOLICY-CPATH-NAME", "draft-ietf-pce-segment-routing-policy-cp-14"}, + TLVSRPolicyCPathPreference: {"SRPOLICY-CPATH-PREFERENCE", "draft-ietf-pce-segment-routing-policy-cp-14"}, + TLVMultipathCap: {"MULTIPATH-CAP", "draft-ietf-pce-multipath-07"}, + TLVMultipathWeight: {"MULTIPATH-WEIGHT", "draft-ietf-pce-multipath-07"}, + TLVMultipathBackup: {"MULTIPATH-BACKUP", "draft-ietf-pce-multipath-07"}, + TLVMultipathOppdirPath: {"MULTIPATH-OPPDIR-PATH", "draft-ietf-pce-multipath-07"}, + TLVLSPExtendedFlag: {"LSP-EXTENDED-FLAG", "RFC9357"}, + TLVVirtualNetwork: {"VIRTUAL-NETWORK", "RFC9358"}, + TLVSrAlgorithm: {"SR-ALGORITHM", "draft-ietf-pce-sid-algo-12"}, + TLVColor: {"COLOR", "RFC-ietf-pce-pcep-color-12"}, + TLVComputationPriority: {"COMPUTATION-PRIORITY", "draft-ietf-pce-segment-routing-policy-cp-14"}, + TLVExplicitNullLabelPolicy: {"EXPLICIT-NULL-LABEL-POLICY", "draft-ietf-pce-segment-routing-policy-cp-14"}, + TLVInvalidation: {"INVALIDATION", "draft-ietf-pce-segment-routing-policy-cp-14"}, + TLVSRPolicyCapability: {"SRPOLICY-CAPABILITY", "draft-ietf-pce-segment-routing-policy-cp-14"}, + TLVPathRecomputation: {"PATH-RECOMPUTATION", "draft-ietf-pce-circuit-style-pcep-extensions-03"}, + TLVSRP2MPPolicyCapability: {"SRP2MP-POLICY-CAPABILITY", "draft-ietf-pce-sr-p2mp-policy-09"}, + TLVIPv4SrP2MPInstanceID: {"IPV4-SR-P2MP-INSTANCE-ID", "draft-ietf-pce-sr-p2mp-policy-09"}, + TLVIPv6SrP2MPInstanceID: {"IPV6-SR-P2MP-INSTANCE-ID", "draft-ietf-pce-sr-p2mp-policy-09"}, +} + +func (t TLVType) String() string { + if desc, ok := tlvDescriptions[t]; ok { + return fmt.Sprintf("%s (%s)", desc.Description, desc.Reference) + } + return fmt.Sprintf("Unknown TLV (0x%04x)", uint16(t)) +} + +var tlvMap = map[TLVType]func() TLVInterface{ + TLVStatefulPceCapability: func() TLVInterface { return &StatefulPceCapability{} }, + TLVSymbolicPathName: func() TLVInterface { return &SymbolicPathName{} }, + TLVIPv4LSPIdentifiers: func() TLVInterface { return &IPv4LSPIdentifiers{} }, + TLVIPv6LSPIdentifiers: func() TLVInterface { return &IPv6LSPIdentifiers{} }, + TLVLSPDBVersion: func() TLVInterface { return &LSPDBVersion{} }, + TLVSRPceCapability: func() TLVInterface { return &SRPceCapability{} }, + TLVPathSetupType: func() TLVInterface { return &PathSetupType{} }, + TLVExtendedAssociationID: func() TLVInterface { return &ExtendedAssociationID{} }, + TLVPathSetupTypeCapability: func() TLVInterface { return &PathSetupTypeCapability{} }, + TLVAssocTypeList: func() TLVInterface { return &AssocTypeList{} }, + TLVColor: func() TLVInterface { return &Color{} }, } const ( - TLV_STATEFUL_PCE_CAPABILITY_LENGTH uint16 = 4 - TLV_LSP_DB_VERSION_LENGTH uint16 = 8 - TLV_SR_PCE_CAPABILITY_LENGTH uint16 = 4 - TLV_PATH_SETUP_TYPE_LENGTH uint16 = 4 - TLV_EXTENDED_ASSOCIATION_ID_IPV4_LENGTH uint16 = 8 - TLV_EXTENDED_ASSOCIATION_ID_IPV6_LENGTH uint16 = 20 - TLV_IPV4_LSP_IDENTIFIERS_LENGTH uint16 = 16 - TLV_IPV6_LSP_IDENTIFIERS_LENGTH uint16 = 52 - TLV_SRPOLICY_CPATH_ID_LENGTH uint16 = 28 - TLV_SRPOLICY_CPATH_PREFERENCE_LENGTH uint16 = 4 - TLV_COLOR_LENGTH uint16 = 4 + TLVStatefulPceCapabilityValueLength uint16 = 4 + TLVLSPDBVersionValueLength uint16 = 8 + TLVSRPceCapabilityValueLength uint16 = 4 + TLVPathSetupTypeValueLength uint16 = 4 + TLVExtendedAssociationIDIPv4ValueLength uint16 = 8 + TLVExtendedAssociationIDIPv6ValueLength uint16 = 20 + TLVIPv4LSPIdentifiersValueLength uint16 = 16 + TLVIPv6LSPIdentifiersValueLength uint16 = 52 + TLVSRPolicyCPathIDValueLength uint16 = 28 + TLVSRPolicyCPathPreferenceValueLength uint16 = 4 + TLVColorValueLength uint16 = 4 +) + +// Juniper specific TLV (deprecated) +const ( + TLVExtendedAssociationIDIPv4Juniper TLVType = 0xffe3 + TLVSRPolicyCPathIDJuniper TLVType = 0xffe4 + TLVSRPolicyCPathPreferenceJuniper TLVType = 0xffe5 +) + +// Cisco specific SubTLV +const ( + SubTLVColorCisco TLVType = 0x01 + SubTLVPreferenceCisco TLVType = 0x03 ) -const TL_LENGTH = 4 +const ( + SubTLVColorCiscoValueLength uint16 = 4 + SubTLVPreferenceCiscoValueLength uint16 = 4 +) + +const TLVHeaderLength = 4 type TLVInterface interface { DecodeFromBytes(data []uint8) error Serialize() []uint8 MarshalLogObject(enc zapcore.ObjectEncoder) error - Type() uint16 + Type() TLVType Len() uint16 // Total length of Type, Length, and Value } type StatefulPceCapability struct { - LspUpdateCapability bool // 31 + LSPUpdateCapability bool // 31 IncludeDBVersion bool // 30 - LspInstantiationCapability bool // 29 + LSPInstantiationCapability bool // 29 TriggeredResync bool // 28 - DeltaLspSyncCapability bool // 27 + DeltaLSPSyncCapability bool // 27 TriggeredInitialSync bool // 26 P2mpCapability bool // 25 - P2mpLspUpdateCapability bool // 24 - P2mpLspInstantiationCapability bool // 23 - LspSchedulingCapability bool // 22 - PdLspCapability bool // 21 + P2mpLSPUpdateCapability bool // 24 + P2mpLSPInstantiationCapability bool // 23 + LSPSchedulingCapability bool // 22 + PdLSPCapability bool // 21 ColorCapability bool // 20 PathRecomputationCapability bool // 19 StrictPathCapability bool // 18 @@ -157,17 +261,17 @@ func (tlv *StatefulPceCapability) DecodeFromBytes(flags []uint8) error { mask uint8 index int }{ - {&tlv.LspUpdateCapability, 0x01, 3}, + {&tlv.LSPUpdateCapability, 0x01, 3}, {&tlv.IncludeDBVersion, 0x02, 3}, - {&tlv.LspInstantiationCapability, 0x04, 3}, + {&tlv.LSPInstantiationCapability, 0x04, 3}, {&tlv.TriggeredResync, 0x08, 3}, - {&tlv.DeltaLspSyncCapability, 0x10, 3}, + {&tlv.DeltaLSPSyncCapability, 0x10, 3}, {&tlv.TriggeredInitialSync, 0x20, 3}, {&tlv.P2mpCapability, 0x40, 3}, - {&tlv.P2mpLspUpdateCapability, 0x80, 3}, - {&tlv.P2mpLspInstantiationCapability, 0x01, 2}, - {&tlv.LspSchedulingCapability, 0x02, 2}, - {&tlv.PdLspCapability, 0x04, 2}, + {&tlv.P2mpLSPUpdateCapability, 0x80, 3}, + {&tlv.P2mpLSPInstantiationCapability, 0x01, 2}, + {&tlv.LSPSchedulingCapability, 0x02, 2}, + {&tlv.PdLSPCapability, 0x04, 2}, {&tlv.ColorCapability, 0x08, 2}, {&tlv.PathRecomputationCapability, 0x10, 2}, {&tlv.StrictPathCapability, 0x20, 2}, @@ -191,26 +295,26 @@ func (tlv *StatefulPceCapability) Serialize() []uint8 { buf := []uint8{} typ := make([]uint8, 2) - binary.BigEndian.PutUint16(typ, tlv.Type()) + binary.BigEndian.PutUint16(typ, uint16(tlv.Type())) buf = append(buf, typ...) length := make([]uint8, 2) - binary.BigEndian.PutUint16(length, TLV_STATEFUL_PCE_CAPABILITY_LENGTH) + binary.BigEndian.PutUint16(length, TLVStatefulPceCapabilityValueLength) buf = append(buf, length...) - flags := make([]uint8, TLV_STATEFUL_PCE_CAPABILITY_LENGTH) + flags := make([]uint8, TLVStatefulPceCapabilityValueLength) - setFlag(flags, 3, 0x01, tlv.LspUpdateCapability) + setFlag(flags, 3, 0x01, tlv.LSPUpdateCapability) setFlag(flags, 3, 0x02, tlv.IncludeDBVersion) - setFlag(flags, 3, 0x04, tlv.LspInstantiationCapability) + setFlag(flags, 3, 0x04, tlv.LSPInstantiationCapability) setFlag(flags, 3, 0x08, tlv.TriggeredResync) - setFlag(flags, 3, 0x10, tlv.DeltaLspSyncCapability) + setFlag(flags, 3, 0x10, tlv.DeltaLSPSyncCapability) setFlag(flags, 3, 0x20, tlv.TriggeredInitialSync) setFlag(flags, 3, 0x40, tlv.P2mpCapability) - setFlag(flags, 3, 0x80, tlv.P2mpLspUpdateCapability) - setFlag(flags, 2, 0x01, tlv.P2mpLspInstantiationCapability) - setFlag(flags, 2, 0x02, tlv.LspSchedulingCapability) - setFlag(flags, 2, 0x04, tlv.PdLspCapability) + setFlag(flags, 3, 0x80, tlv.P2mpLSPUpdateCapability) + setFlag(flags, 2, 0x01, tlv.P2mpLSPInstantiationCapability) + setFlag(flags, 2, 0x02, tlv.LSPSchedulingCapability) + setFlag(flags, 2, 0x04, tlv.PdLSPCapability) setFlag(flags, 2, 0x08, tlv.ColorCapability) setFlag(flags, 2, 0x10, tlv.PathRecomputationCapability) setFlag(flags, 2, 0x20, tlv.StrictPathCapability) @@ -225,30 +329,30 @@ func (tlv *StatefulPceCapability) MarshalLogObject(enc zapcore.ObjectEncoder) er return nil } -func (tlv *StatefulPceCapability) Type() uint16 { - return TLV_STATEFUL_PCE_CAPABILITY +func (tlv *StatefulPceCapability) Type() TLVType { + return TLVStatefulPceCapability } func (tlv *StatefulPceCapability) Len() uint16 { - return TL_LENGTH + TLV_STATEFUL_PCE_CAPABILITY_LENGTH + return TLVHeaderLength + TLVStatefulPceCapabilityValueLength } func (tlv *StatefulPceCapability) CapStrings() []string { ret := []string{} ret = append(ret, "Stateful") - if tlv.LspUpdateCapability { + if tlv.LSPUpdateCapability { ret = append(ret, "Update") } if tlv.IncludeDBVersion { ret = append(ret, "Include-DB-Ver") } - if tlv.LspInstantiationCapability { + if tlv.LSPInstantiationCapability { ret = append(ret, "Initiate") } if tlv.TriggeredResync { ret = append(ret, "Triggerd-Resync") } - if tlv.DeltaLspSyncCapability { + if tlv.DeltaLSPSyncCapability { ret = append(ret, "Delta-LSP-Sync") } if tlv.TriggeredInitialSync { @@ -274,7 +378,7 @@ func (tlv *SymbolicPathName) Serialize() []uint8 { buf := []uint8{} typ := make([]uint8, 2) - binary.BigEndian.PutUint16(typ, tlv.Type()) + binary.BigEndian.PutUint16(typ, uint16(tlv.Type())) buf = append(buf, typ...) l := uint16(len(tlv.Name)) @@ -295,8 +399,8 @@ func (tlv *SymbolicPathName) MarshalLogObject(enc zapcore.ObjectEncoder) error { return nil } -func (tlv *SymbolicPathName) Type() uint16 { - return TLV_SYMBOLIC_PATH_NAME +func (tlv *SymbolicPathName) Type() TLVType { + return TLVSymbolicPathName } func (tlv *SymbolicPathName) Len() uint16 { @@ -305,72 +409,72 @@ func (tlv *SymbolicPathName) Len() uint16 { if l%4 != 0 { padding = (4 - l%4) } - return TL_LENGTH + l + padding + return TLVHeaderLength + l + padding } -type IPv4LspIdentifiers struct { +type IPv4LSPIdentifiers struct { IPv4TunnelSenderAddress netip.Addr IPv4TunnelEndpointAddress netip.Addr - LspID uint16 + LSPID uint16 TunnelID uint16 } -func (tlv *IPv4LspIdentifiers) DecodeFromBytes(data []uint8) error { +func (tlv *IPv4LSPIdentifiers) DecodeFromBytes(data []uint8) error { var ok bool if tlv.IPv4TunnelSenderAddress, ok = netip.AddrFromSlice(data[12:16]); !ok { tlv.IPv4TunnelSenderAddress, _ = netip.AddrFromSlice(data[4:8]) } - tlv.LspID = binary.BigEndian.Uint16(data[8:10]) + tlv.LSPID = binary.BigEndian.Uint16(data[8:10]) tlv.TunnelID = binary.BigEndian.Uint16(data[10:12]) tlv.IPv4TunnelEndpointAddress, _ = netip.AddrFromSlice(data[16:20]) return nil } -func (tlv *IPv4LspIdentifiers) Serialize() []uint8 { +func (tlv *IPv4LSPIdentifiers) Serialize() []uint8 { return nil } -func (tlv *IPv4LspIdentifiers) MarshalLogObject(enc zapcore.ObjectEncoder) error { +func (tlv *IPv4LSPIdentifiers) MarshalLogObject(enc zapcore.ObjectEncoder) error { return nil } -func (tlv *IPv4LspIdentifiers) Type() uint16 { - return TLV_IPV4_LSP_IDENTIFIERS +func (tlv *IPv4LSPIdentifiers) Type() TLVType { + return TLVIPv4LSPIdentifiers } -func (tlv *IPv4LspIdentifiers) Len() uint16 { - return TL_LENGTH + TLV_IPV4_LSP_IDENTIFIERS_LENGTH +func (tlv *IPv4LSPIdentifiers) Len() uint16 { + return TLVHeaderLength + TLVIPv4LSPIdentifiersValueLength } -type IPv6LspIdentifiers struct { +type IPv6LSPIdentifiers struct { IPv6TunnelSenderAddress netip.Addr IPv6TunnelEndpointAddress netip.Addr - LspID uint16 + LSPID uint16 TunnelID uint16 } -func (tlv *IPv6LspIdentifiers) DecodeFromBytes(data []uint8) error { +func (tlv *IPv6LSPIdentifiers) DecodeFromBytes(data []uint8) error { tlv.IPv6TunnelSenderAddress, _ = netip.AddrFromSlice(data[4:20]) - tlv.LspID = binary.BigEndian.Uint16(data[20:22]) + tlv.LSPID = binary.BigEndian.Uint16(data[20:22]) tlv.TunnelID = binary.BigEndian.Uint16(data[22:24]) tlv.IPv6TunnelEndpointAddress, _ = netip.AddrFromSlice(data[40:56]) return nil } -func (tlv *IPv6LspIdentifiers) Serialize() []uint8 { +func (tlv *IPv6LSPIdentifiers) Serialize() []uint8 { return nil } -func (tlv *IPv6LspIdentifiers) MarshalLogObject(enc zapcore.ObjectEncoder) error { +func (tlv *IPv6LSPIdentifiers) MarshalLogObject(enc zapcore.ObjectEncoder) error { return nil } -func (tlv *IPv6LspIdentifiers) Type() uint16 { - return TLV_IPV6_LSP_IDENTIFIERS +func (tlv *IPv6LSPIdentifiers) Type() TLVType { + return TLVIPv6LSPIdentifiers } -func (tlv *IPv6LspIdentifiers) Len() uint16 { - return TL_LENGTH + TLV_IPV6_LSP_IDENTIFIERS_LENGTH +func (tlv *IPv6LSPIdentifiers) Len() uint16 { + return TLVHeaderLength + TLVIPv6LSPIdentifiersValueLength } type LSPDBVersion struct { @@ -386,14 +490,14 @@ func (tlv *LSPDBVersion) Serialize() []uint8 { buf := []uint8{} typ := make([]uint8, 2) - binary.BigEndian.PutUint16(typ, tlv.Type()) + binary.BigEndian.PutUint16(typ, uint16(tlv.Type())) buf = append(buf, typ...) length := make([]uint8, 2) - binary.BigEndian.PutUint16(length, TLV_LSP_DB_VERSION_LENGTH) + binary.BigEndian.PutUint16(length, TLVLSPDBVersionValueLength) buf = append(buf, length...) - val := make([]uint8, TLV_LSP_DB_VERSION_LENGTH) + val := make([]uint8, TLVLSPDBVersionValueLength) binary.BigEndian.PutUint64(val, tlv.VersionNumber) buf = append(buf, val...) @@ -404,12 +508,12 @@ func (tlv *LSPDBVersion) MarshalLogObject(enc zapcore.ObjectEncoder) error { return nil } -func (tlv *LSPDBVersion) Type() uint16 { - return TLV_LSP_DB_VERSION +func (tlv *LSPDBVersion) Type() TLVType { + return TLVLSPDBVersion } func (tlv *LSPDBVersion) Len() uint16 { - return TL_LENGTH + TLV_LSP_DB_VERSION_LENGTH + return TLVHeaderLength + TLVLSPDBVersionValueLength } func (tlv *LSPDBVersion) CapStrings() []string { @@ -433,14 +537,14 @@ func (tlv *SRPceCapability) Serialize() []uint8 { buf := []uint8{} typ := make([]uint8, 2) - binary.BigEndian.PutUint16(typ, tlv.Type()) + binary.BigEndian.PutUint16(typ, uint16(tlv.Type())) buf = append(buf, typ...) length := make([]uint8, 2) - binary.BigEndian.PutUint16(length, TLV_SR_PCE_CAPABILITY_LENGTH) + binary.BigEndian.PutUint16(length, TLVSRPceCapabilityValueLength) buf = append(buf, length...) - val := make([]uint8, TLV_SR_PCE_CAPABILITY_LENGTH) + val := make([]uint8, TLVSRPceCapabilityValueLength) if tlv.UnlimitedMSD { val[2] = val[2] | 0x01 } @@ -457,12 +561,12 @@ func (tlv *SRPceCapability) MarshalLogObject(enc zapcore.ObjectEncoder) error { return nil } -func (tlv *SRPceCapability) Type() uint16 { - return TLV_SR_PCE_CAPABILITY +func (tlv *SRPceCapability) Type() TLVType { + return TLVSRPceCapability } func (tlv *SRPceCapability) Len() uint16 { - return TL_LENGTH + TLV_SR_PCE_CAPABILITY_LENGTH + return TLVHeaderLength + TLVSRPceCapabilityValueLength } func (tlv *SRPceCapability) CapStrings() []string { @@ -472,12 +576,31 @@ func (tlv *SRPceCapability) CapStrings() []string { type Pst uint8 const ( - PST_RSVP_TE Pst = 0x0 - PST_SR_TE Pst = 0x1 - PST_PCECC_TE Pst = 0x2 - PST_SRV6_TE Pst = 0x3 + PathSetupTypeRSVPTE Pst = 0x0 + PathSetupTypeSRTE Pst = 0x1 + PathSetupTypePCECCTE Pst = 0x2 + PathSetupTypeSRv6TE Pst = 0x3 + PathSetupTypeIPTE Pst = 0x4 ) +var pathSetupDescriptions = map[Pst]struct { + Description string + Reference string +}{ + PathSetupTypeRSVPTE: {"Path is set up using the RSVP-TE signaling protocol", "RFC8408"}, + PathSetupTypeSRTE: {"Traffic engineering path is set up using Segment Routing", "RFC8664"}, + PathSetupTypePCECCTE: {"Traffic engineering path is set up using PCECC mode", "RFC9050"}, + PathSetupTypeSRv6TE: {"Traffic engineering path is set up using SRv6", "RFC9603"}, + PathSetupTypeIPTE: {"Native IP TE Path", "RFC9757"}, +} + +func (pst Pst) String() string { + if desc, found := pathSetupDescriptions[pst]; found { + return fmt.Sprintf("%s (%s)", desc.Description, desc.Reference) + } + return fmt.Sprintf("Unknown PathSetupType (0x%02x)", uint16(pst)) +} + type Psts []Pst func (ts Psts) MarshalJSON() ([]byte, error) { @@ -503,14 +626,14 @@ func (tlv *PathSetupType) Serialize() []uint8 { buf := []uint8{} typ := make([]uint8, 2) - binary.BigEndian.PutUint16(typ, tlv.Type()) + binary.BigEndian.PutUint16(typ, uint16(tlv.Type())) buf = append(buf, typ...) length := make([]uint8, 2) - binary.BigEndian.PutUint16(length, TLV_PATH_SETUP_TYPE_LENGTH) + binary.BigEndian.PutUint16(length, TLVPathSetupTypeValueLength) buf = append(buf, length...) - val := make([]uint8, TLV_PATH_SETUP_TYPE_LENGTH) + val := make([]uint8, TLVPathSetupTypeValueLength) val[3] = uint8(tlv.PathSetupType) buf = append(buf, val...) @@ -521,12 +644,12 @@ func (tlv *PathSetupType) MarshalLogObject(enc zapcore.ObjectEncoder) error { return nil } -func (tlv *PathSetupType) Type() uint16 { - return TLV_PATH_SETUP_TYPE +func (tlv *PathSetupType) Type() TLVType { + return TLVPathSetupType } func (tlv *PathSetupType) Len() uint16 { - return TL_LENGTH + TLV_PATH_SETUP_TYPE_LENGTH + return TLVHeaderLength + TLVPathSetupTypeValueLength } type ExtendedAssociationID struct { @@ -540,9 +663,9 @@ func (tlv *ExtendedAssociationID) DecodeFromBytes(data []uint8) error { tlv.Color = binary.BigEndian.Uint32(data[4:8]) switch l { - case TLV_EXTENDED_ASSOCIATION_ID_IPV4_LENGTH: + case TLVExtendedAssociationIDIPv4ValueLength: tlv.Endpoint, _ = netip.AddrFromSlice(data[8:12]) - case TLV_EXTENDED_ASSOCIATION_ID_IPV6_LENGTH: + case TLVExtendedAssociationIDIPv6ValueLength: tlv.Endpoint, _ = netip.AddrFromSlice(data[8:24]) } @@ -553,14 +676,14 @@ func (tlv *ExtendedAssociationID) Serialize() []uint8 { buf := []uint8{} typ := make([]uint8, 2) - binary.BigEndian.PutUint16(typ, tlv.Type()) + binary.BigEndian.PutUint16(typ, uint16(tlv.Type())) buf = append(buf, typ...) length := make([]uint8, 2) if tlv.Endpoint.Is4() { - binary.BigEndian.PutUint16(length, TLV_EXTENDED_ASSOCIATION_ID_IPV4_LENGTH) + binary.BigEndian.PutUint16(length, TLVExtendedAssociationIDIPv4ValueLength) } else if tlv.Endpoint.Is6() { - binary.BigEndian.PutUint16(length, TLV_EXTENDED_ASSOCIATION_ID_IPV6_LENGTH) + binary.BigEndian.PutUint16(length, TLVExtendedAssociationIDIPv6ValueLength) } buf = append(buf, length...) @@ -576,15 +699,15 @@ func (tlv *ExtendedAssociationID) MarshalLogObject(enc zapcore.ObjectEncoder) er return nil } -func (tlv *ExtendedAssociationID) Type() uint16 { - return TLV_EXTENDED_ASSOCIATION_ID +func (tlv *ExtendedAssociationID) Type() TLVType { + return TLVExtendedAssociationID } func (tlv *ExtendedAssociationID) Len() uint16 { if tlv.Endpoint.Is4() { - return TL_LENGTH + TLV_EXTENDED_ASSOCIATION_ID_IPV4_LENGTH + return TLVHeaderLength + TLVExtendedAssociationIDIPv4ValueLength } else if tlv.Endpoint.Is6() { - return TL_LENGTH + TLV_EXTENDED_ASSOCIATION_ID_IPV6_LENGTH + return TLVHeaderLength + TLVExtendedAssociationIDIPv6ValueLength } return 0 @@ -607,7 +730,7 @@ func (tlv *PathSetupTypeCapability) DecodeFromBytes(data []uint8) error { pstNum += 4 - (pstNum % 4) // padding byte } var err error - tlv.SubTLVs, err = DecodeTLVs(data[8+pstNum : TL_LENGTH+l]) // 8 byte: Type&Length (4 byte) + Reserve&pstNum (4 byte) + tlv.SubTLVs, err = DecodeTLVs(data[8+pstNum : TLVHeaderLength+l]) // 8 byte: Type&Length (4 byte) + Reserve&pstNum (4 byte) if err != nil { return err } @@ -618,7 +741,7 @@ func (tlv *PathSetupTypeCapability) Serialize() []uint8 { buf := []uint8{} typ := make([]uint8, 2) - binary.BigEndian.PutUint16(typ, tlv.Type()) + binary.BigEndian.PutUint16(typ, uint16(tlv.Type())) buf = append(buf, typ...) numOfPst := uint16(len(tlv.PathSetupTypes)) @@ -659,8 +782,8 @@ func (tlv *PathSetupTypeCapability) MarshalLogObject(enc zapcore.ObjectEncoder) return nil } -func (tlv *PathSetupTypeCapability) Type() uint16 { - return TLV_PATH_SETUP_TYPE_CAPABILITY +func (tlv *PathSetupTypeCapability) Type() TLVType { + return TLVPathSetupTypeCapability } func (tlv *PathSetupTypeCapability) Len() uint16 { @@ -673,15 +796,15 @@ func (tlv *PathSetupTypeCapability) Len() uint16 { for _, subTLV := range tlv.SubTLVs { l += subTLV.Len() } - return TL_LENGTH + l + return TLVHeaderLength + l } func (tlv *PathSetupTypeCapability) CapStrings() []string { ret := []string{} - if slices.Contains(tlv.PathSetupTypes, PST_SR_TE) { + if slices.Contains(tlv.PathSetupTypes, PathSetupTypeSRTE) { ret = append(ret, "SR-TE") } - if slices.Contains(tlv.PathSetupTypes, PST_SRV6_TE) { + if slices.Contains(tlv.PathSetupTypes, PathSetupTypeSRv6TE) { ret = append(ret, "SRv6-TE") } return ret @@ -690,16 +813,32 @@ func (tlv *PathSetupTypeCapability) CapStrings() []string { type AssocType uint16 const ( - AT_RESERVED AssocType = 0x00 - AT_PATH_PROTECTION_ASSOCIATION AssocType = 0x01 - AT_DISCOINT_ASSOCIATION AssocType = 0x02 - AT_POLICY_ASSOCIATION AssocType = 0x03 - AT_SINGLE_SIDED_BIDIRECTIONAL_LSP_ASSOCIATION AssocType = 0x04 - AT_DOUBLE_SIDED_BIDIRECTIONAL_LSP_ASSOCIATION AssocType = 0x05 - AT_SR_POLICY_ASSOCIATION AssocType = 0x06 - AT_VN_ASSOCIATION_TYPE AssocType = 0x07 + AssocTypePathProtectionAssociation AssocType = 0x01 + AssocTypeDisjointAssociation AssocType = 0x02 + AssocTypePolicyAssociation AssocType = 0x03 + AssocTypeSingleSidedBidirectionalLSPAssociation AssocType = 0x04 + AssocTypeDoubleSidedBidirectionalLSPAssociation AssocType = 0x05 + AssocTypeSrPolicyAssociation AssocType = 0x06 + AssocTypeVnAssociationType AssocType = 0x07 ) +var assocTypeNames = map[AssocType]string{ + AssocTypePathProtectionAssociation: "Path Protection Association", + AssocTypeDisjointAssociation: "Disjoint Association", + AssocTypePolicyAssociation: "Policy Association", + AssocTypeSingleSidedBidirectionalLSPAssociation: "Single Sided Bidirectional LSP Association", + AssocTypeDoubleSidedBidirectionalLSPAssociation: "Double Sided Bidirectional LSP Association", + AssocTypeSrPolicyAssociation: "SR Policy Association", + AssocTypeVnAssociationType: "VN Association Type", +} + +func (at AssocType) String() string { + if name, ok := assocTypeNames[at]; ok { + return name + } + return fmt.Sprintf("Unknown AssocType (0x%04x)", uint16(at)) +} + type AssocTypeList struct { AssocTypes []AssocType } @@ -717,7 +856,7 @@ func (tlv *AssocTypeList) Serialize() []uint8 { buf := []uint8{} typ := make([]uint8, 2) - binary.BigEndian.PutUint16(typ, tlv.Type()) + binary.BigEndian.PutUint16(typ, uint16(tlv.Type())) buf = append(buf, typ...) l := uint16(len(tlv.AssocTypes)) * 2 @@ -741,8 +880,8 @@ func (tlv *AssocTypeList) MarshalLogObject(enc zapcore.ObjectEncoder) error { return nil } -func (tlv *AssocTypeList) Type() uint16 { - return TLV_ASSOC_TYPE_LIST +func (tlv *AssocTypeList) Type() TLVType { + return TLVAssocTypeList } func (tlv *AssocTypeList) Len() uint16 { @@ -751,7 +890,7 @@ func (tlv *AssocTypeList) Len() uint16 { if l%4 != 0 { padding = 2 } - return TL_LENGTH + l + padding + return TLVHeaderLength + l + padding } func (tlv *AssocTypeList) CapStrings() []string { @@ -771,11 +910,11 @@ func (tlv *SRPolicyCandidatePathIdentifier) Serialize() []uint8 { buf := []uint8{} typ := make([]uint8, 2) - binary.BigEndian.PutUint16(typ, tlv.Type()) + binary.BigEndian.PutUint16(typ, uint16(tlv.Type())) buf = append(buf, typ...) length := make([]uint8, 2) - binary.BigEndian.PutUint16(length, TLV_SRPOLICY_CPATH_ID_LENGTH) + binary.BigEndian.PutUint16(length, TLVSRPolicyCPathIDValueLength) buf = append(buf, length...) buf = append(buf, 0x0a) // protocol origin, PCEP = 10 @@ -797,12 +936,12 @@ func (tlv *SRPolicyCandidatePathIdentifier) MarshalLogObject(enc zapcore.ObjectE return nil } -func (tlv *SRPolicyCandidatePathIdentifier) Type() uint16 { - return TLV_SRPOLICY_CPATH_ID +func (tlv *SRPolicyCandidatePathIdentifier) Type() TLVType { + return TLVSRPolicyCPathID } func (tlv *SRPolicyCandidatePathIdentifier) Len() uint16 { - return TL_LENGTH + TLV_SRPOLICY_CPATH_ID_LENGTH + return TLVHeaderLength + TLVSRPolicyCPathIDValueLength } type SRPolicyCandidatePathPreference struct { @@ -818,11 +957,11 @@ func (tlv *SRPolicyCandidatePathPreference) Serialize() []uint8 { buf := []uint8{} typ := make([]uint8, 2) - binary.BigEndian.PutUint16(typ, tlv.Type()) + binary.BigEndian.PutUint16(typ, uint16(tlv.Type())) buf = append(buf, typ...) length := make([]uint8, 2) - binary.BigEndian.PutUint16(length, TLV_SRPOLICY_CPATH_PREFERENCE_LENGTH) + binary.BigEndian.PutUint16(length, TLVSRPolicyCPathPreferenceValueLength) buf = append(buf, length...) preference := make([]uint8, 4) @@ -836,12 +975,12 @@ func (tlv *SRPolicyCandidatePathPreference) MarshalLogObject(enc zapcore.ObjectE return nil } -func (tlv *SRPolicyCandidatePathPreference) Type() uint16 { - return TLV_SRPOLICY_CPATH_PREFERENCE +func (tlv *SRPolicyCandidatePathPreference) Type() TLVType { + return TLVSRPolicyCPathPreference } func (tlv *SRPolicyCandidatePathPreference) Len() uint16 { - return TL_LENGTH + TLV_SRPOLICY_CPATH_PREFERENCE_LENGTH + return TLVHeaderLength + TLVSRPolicyCPathPreferenceValueLength } type Color struct { @@ -857,11 +996,11 @@ func (tlv *Color) Serialize() []uint8 { buf := []uint8{} typ := make([]uint8, 2) - binary.BigEndian.PutUint16(typ, tlv.Type()) + binary.BigEndian.PutUint16(typ, uint16(tlv.Type())) buf = append(buf, typ...) length := make([]uint8, 2) - binary.BigEndian.PutUint16(length, TLV_COLOR) + binary.BigEndian.PutUint16(length, uint16(TLVColor)) buf = append(buf, length...) color := make([]uint8, 4) @@ -875,22 +1014,22 @@ func (tlv *Color) MarshalLogObject(enc zapcore.ObjectEncoder) error { return nil } -func (tlv *Color) Type() uint16 { - return TLV_COLOR +func (tlv *Color) Type() TLVType { + return TLVColor } func (tlv *Color) Len() uint16 { - return TL_LENGTH + TLV_COLOR_LENGTH + return TLVHeaderLength + TLVColorValueLength } type UndefinedTLV struct { - Typ uint16 + Typ TLVType Length uint16 Value []uint8 } func (tlv *UndefinedTLV) DecodeFromBytes(data []uint8) error { - tlv.Typ = binary.BigEndian.Uint16(data[0:2]) + tlv.Typ = TLVType(binary.BigEndian.Uint16(data[0:2])) tlv.Length = binary.BigEndian.Uint16(data[2:4]) tlv.Value = data[4 : 4+tlv.Length] @@ -901,7 +1040,7 @@ func (tlv *UndefinedTLV) Serialize() []uint8 { bytePcepTLV := []uint8{} byteTLVType := make([]uint8, 2) - binary.BigEndian.PutUint16(byteTLVType, tlv.Typ) + binary.BigEndian.PutUint16(byteTLVType, uint16(tlv.Typ)) bytePcepTLV = append(bytePcepTLV, byteTLVType...) // Type (2byte) byteTLVLength := make([]uint8, 2) @@ -920,7 +1059,7 @@ func (tlv *UndefinedTLV) MarshalLogObject(enc zapcore.ObjectEncoder) error { return nil } -func (tlv *UndefinedTLV) Type() uint16 { +func (tlv *UndefinedTLV) Type() TLVType { return tlv.Typ } @@ -929,7 +1068,7 @@ func (tlv *UndefinedTLV) Len() uint16 { if tlv.Length%4 != 0 { padding = (4 - tlv.Length%4) } - return TL_LENGTH + tlv.Length + padding + return TLVHeaderLength + tlv.Length + padding } func (tlv *UndefinedTLV) CapStrings() []string { @@ -948,7 +1087,7 @@ func DecodeTLV(data []uint8) (TLVInterface, error) { tlvType := binary.BigEndian.Uint16(data[0:2]) - if createTLV, found := tlvMap[tlvType]; found { + if createTLV, found := tlvMap[TLVType(tlvType)]; found { tlv := createTLV() if err := tlv.DecodeFromBytes(data); err != nil { return nil, fmt.Errorf("error decoding TLV type %x: %w", tlvType, err) diff --git a/pkg/server/grpc_server.go b/pkg/server/grpc_server.go index 743ee213..f8c3d2cd 100644 --- a/pkg/server/grpc_server.go +++ b/pkg/server/grpc_server.go @@ -441,16 +441,21 @@ func (s *APIServer) GetTed(context.Context, *empty.Empty) (*pb.Ted, error) { } func (c *APIServer) DeleteSession(ctx context.Context, input *pb.Session) (*pb.RequestStatus, error) { - ssAddr, _ := netip.AddrFromSlice(input.GetAddr()) + ssAddr, ok := netip.AddrFromSlice(input.GetAddr()) + if !ok { + return nil, fmt.Errorf("invalid address: %v", input.GetAddr()) + } s := c.pce - var ss *Session - if ss = s.SearchSession(ssAddr, false); ss == nil { - return nil, fmt.Errorf("no session with %s", ssAddr) + ss := s.SearchSession(ssAddr, false) + if ss == nil { + return nil, fmt.Errorf("no session with address %s found", ssAddr) } - if err := ss.SendClose(pcep.R_NO_EXPLANATION_PROVIDED); err != nil { - return &pb.RequestStatus{IsSuccess: false}, err + + if err := ss.SendClose(pcep.CloseReasonNoExplanationProvided); err != nil { + return &pb.RequestStatus{IsSuccess: false}, fmt.Errorf("failed to send close message: %v", err) } + // Remove session info from PCE server s.closeSession(ss) diff --git a/pkg/server/session.go b/pkg/server/session.go index 1026a348..79d51e48 100644 --- a/pkg/server/session.go +++ b/pkg/server/session.go @@ -36,7 +36,7 @@ func NewSession(sessionID uint8, peerAddr netip.Addr, tcpConn *net.TCPConn, logg isSynced: false, srpIDHead: uint32(1), logger: logger.With(zap.String("server", "pcep"), zap.String("session", peerAddr.String())), - pccType: pcep.RFC_COMPLIANT, + pccType: pcep.RFCCompliant, peerAddr: peerAddr, tcpConn: tcpConn, } @@ -102,7 +102,7 @@ func (ss *Session) Open() error { } func (ss *Session) parseOpenMessage() (*pcep.OpenMessage, error) { - byteOpenHeader := make([]uint8, pcep.COMMON_HEADER_LENGTH) + byteOpenHeader := make([]uint8, pcep.CommonHeaderLength) if _, err := ss.tcpConn.Read(byteOpenHeader); err != nil { return nil, err } @@ -115,11 +115,11 @@ func (ss *Session) parseOpenMessage() (*pcep.OpenMessage, error) { if openHeader.Version != 1 { return nil, fmt.Errorf("PCEP version mismatch (receive version: %d)", openHeader.Version) } - if openHeader.MessageType != pcep.MT_OPEN { - return nil, fmt.Errorf("this peer has not been opened (messageType: %d)", openHeader.MessageType) + if openHeader.MessageType != pcep.MessageTypeOpen { + return nil, fmt.Errorf("this peer has not been opened (messageType: %s)", openHeader.MessageType.String()) } - byteOpenObject := make([]uint8, openHeader.MessageLength-pcep.COMMON_HEADER_LENGTH) + byteOpenObject := make([]uint8, openHeader.MessageLength-pcep.CommonHeaderLength) if _, err := ss.tcpConn.Read(byteOpenObject); err != nil { return nil, err } @@ -159,7 +159,7 @@ func (ss *Session) SendKeepalive() error { return ss.sendPcepMessage(keepaliveMessage) } -func (ss *Session) SendClose(reason uint8) error { +func (ss *Session) SendClose(reason pcep.CloseReason) error { closeMessage, err := pcep.NewCloseMessage(reason) if err != nil { return err @@ -167,7 +167,7 @@ func (ss *Session) SendClose(reason uint8) error { byteCloseMessage := closeMessage.Serialize() ss.logger.Debug("Send Close Message", - zap.Uint8("reason", closeMessage.CloseObject.Reason), + zap.Uint8("reason", uint8(closeMessage.CloseObject.Reason)), zap.String("detail", "See https://www.iana.org/assignments/pcep/pcep.xhtml#close-object-reason-field")) if _, err := ss.tcpConn.Write(byteCloseMessage); err != nil { return err @@ -185,15 +185,15 @@ func (ss *Session) ReceivePcepMessage() error { time.Sleep(10 * time.Millisecond) switch commonHeader.MessageType { - case pcep.MT_KEEPALIVE: + case pcep.MessageTypeKeepalive: ss.logger.Debug("Received Keepalive") - case pcep.MT_REPORT: + case pcep.MessageTypeReport: err = ss.handlePCRpt(commonHeader.MessageLength) if err != nil { return err } - case pcep.MT_ERROR: - bytePCErrMessageBody := make([]uint8, commonHeader.MessageLength-pcep.COMMON_HEADER_LENGTH) + case pcep.MessageTypeError: + bytePCErrMessageBody := make([]uint8, commonHeader.MessageLength-pcep.CommonHeaderLength) if _, err := ss.tcpConn.Read(bytePCErrMessageBody); err != nil { return err } @@ -206,8 +206,8 @@ func (ss *Session) ReceivePcepMessage() error { zap.Uint8("error-Type", pcerrMessage.PcepErrorObject.ErrorType), zap.Uint8("error-value", pcerrMessage.PcepErrorObject.ErrorValue), zap.String("detail", "See https://www.iana.org/assignments/pcep/pcep.xhtml#pcep-error-object")) - case pcep.MT_CLOSE: - byteCloseMessageBody := make([]uint8, commonHeader.MessageLength-pcep.COMMON_HEADER_LENGTH) + case pcep.MessageTypeClose: + byteCloseMessageBody := make([]uint8, commonHeader.MessageLength-pcep.CommonHeaderLength) if _, err := ss.tcpConn.Read(byteCloseMessageBody); err != nil { return err } @@ -216,19 +216,19 @@ func (ss *Session) ReceivePcepMessage() error { return err } ss.logger.Debug("Received Close", - zap.Uint8("reason", closeMessage.CloseObject.Reason), + zap.String("reason", closeMessage.CloseObject.Reason.String()), zap.String("detail", "See https://www.iana.org/assignments/pcep/pcep.xhtml#close-object-reason-field")) // Close session if get Close Message return nil default: ss.logger.Debug("Received unsupported MessageType", - zap.Uint8("MessageType", commonHeader.MessageType)) + zap.String("MessageType", commonHeader.MessageType.String())) } } } func (ss *Session) readCommonHeader() (*pcep.CommonHeader, error) { - commonHeaderBytes := make([]uint8, pcep.COMMON_HEADER_LENGTH) + commonHeaderBytes := make([]uint8, pcep.CommonHeaderLength) if _, err := ss.tcpConn.Read(commonHeaderBytes); err != nil { return nil, err } @@ -244,7 +244,7 @@ func (ss *Session) readCommonHeader() (*pcep.CommonHeader, error) { func (ss *Session) handlePCRpt(length uint16) error { ss.logger.Debug("Received PCRpt Message") - messageBodyBytes := make([]uint8, length-pcep.COMMON_HEADER_LENGTH) + messageBodyBytes := make([]uint8, length-pcep.CommonHeaderLength) if _, err := ss.tcpConn.Read(messageBodyBytes); err != nil { return err } @@ -256,26 +256,26 @@ func (ss *Session) handlePCRpt(length uint16) error { for _, sr := range message.StateReports { // synchronization - if sr.LspObject.SFlag { + if sr.LSPObject.SFlag { ss.logger.Debug("Synchronize SR Policy information", zap.Any("Message", message)) ss.RegisterSRPolicy(*sr) - } else if !sr.LspObject.SFlag { + } else if !sr.LSPObject.SFlag { switch { // finish synchronization - case sr.LspObject.PlspID == 0: + case sr.LSPObject.PlspID == 0: ss.logger.Debug("Finish PCRpt state synchronization") ss.isSynced = true // response to request from PCE case sr.SrpObject.SrpID != 0: ss.logger.Debug("Finish Stateful PCE request", zap.Uint32("srpID", sr.SrpObject.SrpID)) - if sr.LspObject.RFlag { + if sr.LSPObject.RFlag { ss.DeleteSRPolicy(*sr) } else { ss.RegisterSRPolicy(*sr) } default: - if sr.LspObject.RFlag { + if sr.LSPObject.RFlag { ss.DeleteSRPolicy(*sr) } else { ss.RegisterSRPolicy(*sr) @@ -338,7 +338,7 @@ func (ss *Session) RegisterSRPolicy(sr pcep.StateReport) { var color uint32 = 0 // Default color value (RFC does not specify a default) var preference uint32 = 0 // Default preference value (RFC does not specify a default) - if ss.pccType == pcep.CISCO_LEGACY { + if ss.pccType == pcep.CiscoLegacy { // In Cisco legacy mode, get color and preference from Vendor Information Object color = sr.VendorInformationObject.Color() preference = sr.VendorInformationObject.Preference() @@ -359,16 +359,16 @@ func (ss *Session) RegisterSRPolicy(sr pcep.StateReport) { if sr.AssociationObject.Color() != 0 { color = sr.AssociationObject.Color() } else if hasColorCapability { - color = sr.LspObject.Color() + color = sr.LSPObject.Color() } preference = sr.AssociationObject.Preference() } - lspID := sr.LspObject.LspID + lspID := sr.LSPObject.LSPID var state table.PolicyState - switch sr.LspObject.OFlag { + switch sr.LSPObject.OFlag { case uint8(0x00): state = table.POLICY_DOWN case uint8(0x01): @@ -379,17 +379,17 @@ func (ss *Session) RegisterSRPolicy(sr pcep.StateReport) { state = table.POLICY_UNKNOWN } - if p, ok := ss.SearchSRPolicy(sr.LspObject.PlspID); ok { + if p, ok := ss.SearchSRPolicy(sr.LSPObject.PlspID); ok { // update // If the LSP ID is old, it is not the latest data update. - if p.LspID <= lspID { + if p.LSPID <= lspID { p.Update( table.PolicyDiff{ - Name: &sr.LspObject.Name, + Name: &sr.LSPObject.Name, Color: &color, Preference: &preference, SegmentList: sr.EroObject.ToSegmentList(), - LspID: lspID, + LSPID: lspID, State: state, }, ) @@ -397,15 +397,15 @@ func (ss *Session) RegisterSRPolicy(sr pcep.StateReport) { } else { // create var src, dst netip.Addr - if src = sr.LspObject.SrcAddr; !src.IsValid() { + if src = sr.LSPObject.SrcAddr; !src.IsValid() { src = sr.AssociationObject.AssocSrc } - if dst = sr.LspObject.DstAddr; !dst.IsValid() { + if dst = sr.LSPObject.DstAddr; !dst.IsValid() { dst = sr.AssociationObject.Endpoint() } p := table.NewSRPolicy( - sr.LspObject.PlspID, - sr.LspObject.Name, + sr.LSPObject.PlspID, + sr.LSPObject.Name, sr.EroObject.ToSegmentList(), src, dst, @@ -419,10 +419,10 @@ func (ss *Session) RegisterSRPolicy(sr pcep.StateReport) { } func (ss *Session) DeleteSRPolicy(sr pcep.StateReport) { - lspID := sr.LspObject.LspID + lspID := sr.LSPObject.LSPID for i, v := range ss.srPolicies { // If the LSP ID is old, it is not the latest data update. - if v.PlspID == sr.LspObject.PlspID && v.LspID <= lspID { + if v.PlspID == sr.LSPObject.PlspID && v.LSPID <= lspID { ss.srPolicies[i] = ss.srPolicies[len(ss.srPolicies)-1] ss.srPolicies = ss.srPolicies[:len(ss.srPolicies)-1] break From 644cb7d69de9b722fa62a01a9c4e31fc5cc6861b Mon Sep 17 00:00:00 2001 From: watal Date: Mon, 14 Apr 2025 21:24:43 +0900 Subject: [PATCH 09/87] refactor(polad): rename constant --- cmd/polad/main.go | 4 ++-- internal/pkg/version/version.go | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cmd/polad/main.go b/cmd/polad/main.go index 96d4f162..b56f6033 100644 --- a/cmd/polad/main.go +++ b/cmd/polad/main.go @@ -22,7 +22,7 @@ import ( "github.com/nttcom/pola/pkg/server" ) -const TED_UPDATE_INTERVAL = 1 // (min) +const TEDUpdateInterval = 1 // (min) type flags struct { configFile string @@ -118,7 +118,7 @@ func startGobgpUpdate(c *config.Config, logger *zap.Logger) chan []table.TedElem } else { tedElemsChan <- tedElems } - time.Sleep(TED_UPDATE_INTERVAL * time.Minute) + time.Sleep(TEDUpdateInterval * time.Minute) } }() diff --git a/internal/pkg/version/version.go b/internal/pkg/version/version.go index 4cf922ea..f88dc4be 100644 --- a/internal/pkg/version/version.go +++ b/internal/pkg/version/version.go @@ -7,10 +7,10 @@ package version import "fmt" -const MAJOR uint = 1 -const MINOR uint = 3 -const PATCH uint = 0 +const Major uint = 1 +const Minor uint = 3 +const Patch uint = 0 func Version() string { - return fmt.Sprintf("%d.%d.%d", MAJOR, MINOR, PATCH) + return fmt.Sprintf("%d.%d.%d", Major, Minor, Patch) } From 08531c1a31f0ca67e1ddef32513e0927c3ca7e8e Mon Sep 17 00:00:00 2001 From: watal Date: Tue, 15 Apr 2025 09:53:57 +0900 Subject: [PATCH 10/87] refactor: rename acronym variables to follow Go naming conventions --- api/grpc/pola.pb.go | 577 ++++++------------ api/grpc/pola.proto | 21 +- api/grpc/pola.zap.go | 6 +- api/grpc/pola_grpc.pb.go | 218 +++---- cmd/pola/grpc/grpc_client.go | 54 +- cmd/pola/root.go | 6 +- cmd/pola/sr_policy_add.go | 14 +- cmd/pola/sr_policy_delete.go | 6 +- cmd/pola/ted.go | 6 +- cmd/polad/main.go | 32 +- examples/tinet/sr-mpls_te_l3vpn/README.md | 43 +- internal/config/config.go | 20 +- internal/pkg/cspf/cspf.go | 2 +- internal/pkg/gobgp/interface.go | 106 ++-- internal/pkg/table/sr_policy.go | 50 +- internal/pkg/table/ted.go | 60 +- pkg/packet/pcep/capability.go | 4 +- pkg/packet/pcep/message.go | 24 +- pkg/packet/pcep/object.go | 25 +- pkg/packet/pcep/tlv.go | 84 +-- pkg/server/error.go | 2 +- pkg/server/grpc_server.go | 56 +- pkg/server/server.go | 32 +- pkg/server/session.go | 28 +- tools/grpc/go/add_sr-policy/add_sr-policy.go | 4 +- .../add_sr-policy_no_ls.go | 4 +- tools/grpc/go/del_session/del_session.go | 2 +- tools/grpc/go/show_session/show_session.go | 2 +- .../go/show_sr-policy/show_sr-policy_list.go | 4 +- tools/grpc/go/show_ted/show_ted.go | 4 +- 30 files changed, 641 insertions(+), 855 deletions(-) diff --git a/api/grpc/pola.pb.go b/api/grpc/pola.pb.go index 060773f3..cfb7533e 100644 --- a/api/grpc/pola.pb.go +++ b/api/grpc/pola.pb.go @@ -5,18 +5,19 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.34.1 -// protoc v3.12.4 +// protoc-gen-go v1.36.5 +// protoc v5.29.3 // source: pola.proto package grpc import ( - empty "github.com/golang/protobuf/ptypes/empty" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + emptypb "google.golang.org/protobuf/types/known/emptypb" reflect "reflect" sync "sync" + unsafe "unsafe" ) const ( @@ -171,23 +172,20 @@ func (MetricType) EnumDescriptor() ([]byte, []int) { } type Segment struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Sid string `protobuf:"bytes,1,opt,name=sid,proto3" json:"sid,omitempty"` + SidStructure string `protobuf:"bytes,2,opt,name=sidStructure,proto3" json:"sidStructure,omitempty"` + LocalAddr string `protobuf:"bytes,3,opt,name=localAddr,proto3" json:"localAddr,omitempty"` + RemoteAddr string `protobuf:"bytes,4,opt,name=remoteAddr,proto3" json:"remoteAddr,omitempty"` unknownFields protoimpl.UnknownFields - - Sid string `protobuf:"bytes,1,opt,name=sid,proto3" json:"sid,omitempty"` - SidStructure string `protobuf:"bytes,2,opt,name=sidStructure,proto3" json:"sidStructure,omitempty"` - LocalAddr string `protobuf:"bytes,3,opt,name=localAddr,proto3" json:"localAddr,omitempty"` - RemoteAddr string `protobuf:"bytes,4,opt,name=remoteAddr,proto3" json:"remoteAddr,omitempty"` + sizeCache protoimpl.SizeCache } func (x *Segment) Reset() { *x = Segment{} - if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_pola_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *Segment) String() string { @@ -198,7 +196,7 @@ func (*Segment) ProtoMessage() {} func (x *Segment) ProtoReflect() protoreflect.Message { mi := &file_pola_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -242,30 +240,27 @@ func (x *Segment) GetRemoteAddr() string { } type SRPolicy struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - PcepSessionAddr []byte `protobuf:"bytes,1,opt,name=pcepSessionAddr,proto3" json:"pcepSessionAddr,omitempty"` - SrcAddr []byte `protobuf:"bytes,2,opt,name=srcAddr,proto3" json:"srcAddr,omitempty"` - DstAddr []byte `protobuf:"bytes,3,opt,name=dstAddr,proto3" json:"dstAddr,omitempty"` - SrcRouterID string `protobuf:"bytes,4,opt,name=srcRouterID,proto3" json:"srcRouterID,omitempty"` - DstRouterID string `protobuf:"bytes,5,opt,name=dstRouterID,proto3" json:"dstRouterID,omitempty"` - Color uint32 `protobuf:"varint,6,opt,name=color,proto3" json:"color,omitempty"` - Preference uint32 `protobuf:"varint,7,opt,name=preference,proto3" json:"preference,omitempty"` - PolicyName string `protobuf:"bytes,8,opt,name=policyName,proto3" json:"policyName,omitempty"` - Type SRPolicyType `protobuf:"varint,9,opt,name=type,proto3,enum=pb.SRPolicyType" json:"type,omitempty"` - SegmentList []*Segment `protobuf:"bytes,10,rep,name=segmentList,proto3" json:"segmentList,omitempty"` - Metric MetricType `protobuf:"varint,11,opt,name=metric,proto3,enum=pb.MetricType" json:"metric,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + PCEPSessionAddr []byte `protobuf:"bytes,1,opt,name=PCEPSessionAddr,proto3" json:"PCEPSessionAddr,omitempty"` + SrcAddr []byte `protobuf:"bytes,2,opt,name=srcAddr,proto3" json:"srcAddr,omitempty"` + DstAddr []byte `protobuf:"bytes,3,opt,name=dstAddr,proto3" json:"dstAddr,omitempty"` + SrcRouterID string `protobuf:"bytes,4,opt,name=srcRouterID,proto3" json:"srcRouterID,omitempty"` + DstRouterID string `protobuf:"bytes,5,opt,name=dstRouterID,proto3" json:"dstRouterID,omitempty"` + Color uint32 `protobuf:"varint,6,opt,name=color,proto3" json:"color,omitempty"` + Preference uint32 `protobuf:"varint,7,opt,name=preference,proto3" json:"preference,omitempty"` + PolicyName string `protobuf:"bytes,8,opt,name=policyName,proto3" json:"policyName,omitempty"` + Type SRPolicyType `protobuf:"varint,9,opt,name=type,proto3,enum=pb.SRPolicyType" json:"type,omitempty"` + SegmentList []*Segment `protobuf:"bytes,10,rep,name=segmentList,proto3" json:"segmentList,omitempty"` + Metric MetricType `protobuf:"varint,11,opt,name=metric,proto3,enum=pb.MetricType" json:"metric,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *SRPolicy) Reset() { *x = SRPolicy{} - if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_pola_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *SRPolicy) String() string { @@ -276,7 +271,7 @@ func (*SRPolicy) ProtoMessage() {} func (x *SRPolicy) ProtoReflect() protoreflect.Message { mi := &file_pola_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -291,9 +286,9 @@ func (*SRPolicy) Descriptor() ([]byte, []int) { return file_pola_proto_rawDescGZIP(), []int{1} } -func (x *SRPolicy) GetPcepSessionAddr() []byte { +func (x *SRPolicy) GetPCEPSessionAddr() []byte { if x != nil { - return x.PcepSessionAddr + return x.PCEPSessionAddr } return nil } @@ -369,21 +364,18 @@ func (x *SRPolicy) GetMetric() MetricType { } type CreateSRPolicyInput struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + SRPolicy *SRPolicy `protobuf:"bytes,1,opt,name=SRPolicy,proto3" json:"SRPolicy,omitempty"` + Asn uint32 `protobuf:"varint,2,opt,name=asn,proto3" json:"asn,omitempty"` unknownFields protoimpl.UnknownFields - - SRPolicy *SRPolicy `protobuf:"bytes,1,opt,name=SRPolicy,proto3" json:"SRPolicy,omitempty"` - Asn uint32 `protobuf:"varint,2,opt,name=asn,proto3" json:"asn,omitempty"` + sizeCache protoimpl.SizeCache } func (x *CreateSRPolicyInput) Reset() { *x = CreateSRPolicyInput{} - if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_pola_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *CreateSRPolicyInput) String() string { @@ -394,7 +386,7 @@ func (*CreateSRPolicyInput) ProtoMessage() {} func (x *CreateSRPolicyInput) ProtoReflect() protoreflect.Message { mi := &file_pola_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -424,21 +416,18 @@ func (x *CreateSRPolicyInput) GetAsn() uint32 { } type DeleteSRPolicyInput struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + SRPolicy *SRPolicy `protobuf:"bytes,1,opt,name=SRPolicy,proto3" json:"SRPolicy,omitempty"` + Asn uint32 `protobuf:"varint,2,opt,name=asn,proto3" json:"asn,omitempty"` unknownFields protoimpl.UnknownFields - - SRPolicy *SRPolicy `protobuf:"bytes,1,opt,name=SRPolicy,proto3" json:"SRPolicy,omitempty"` - Asn uint32 `protobuf:"varint,2,opt,name=asn,proto3" json:"asn,omitempty"` + sizeCache protoimpl.SizeCache } func (x *DeleteSRPolicyInput) Reset() { *x = DeleteSRPolicyInput{} - if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_pola_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *DeleteSRPolicyInput) String() string { @@ -449,7 +438,7 @@ func (*DeleteSRPolicyInput) ProtoMessage() {} func (x *DeleteSRPolicyInput) ProtoReflect() protoreflect.Message { mi := &file_pola_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -479,20 +468,17 @@ func (x *DeleteSRPolicyInput) GetAsn() uint32 { } type RequestStatus struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + IsSuccess bool `protobuf:"varint,1,opt,name=isSuccess,proto3" json:"isSuccess,omitempty"` unknownFields protoimpl.UnknownFields - - IsSuccess bool `protobuf:"varint,1,opt,name=isSuccess,proto3" json:"isSuccess,omitempty"` + sizeCache protoimpl.SizeCache } func (x *RequestStatus) Reset() { *x = RequestStatus{} - if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_pola_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *RequestStatus) String() string { @@ -503,7 +489,7 @@ func (*RequestStatus) ProtoMessage() {} func (x *RequestStatus) ProtoReflect() protoreflect.Message { mi := &file_pola_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -526,23 +512,20 @@ func (x *RequestStatus) GetIsSuccess() bool { } type Session struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Addr []byte `protobuf:"bytes,1,opt,name=Addr,proto3" json:"Addr,omitempty"` + State SessionState `protobuf:"varint,2,opt,name=State,proto3,enum=pb.SessionState" json:"State,omitempty"` + Caps []string `protobuf:"bytes,3,rep,name=Caps,proto3" json:"Caps,omitempty"` + IsSynced bool `protobuf:"varint,4,opt,name=IsSynced,proto3" json:"IsSynced,omitempty"` unknownFields protoimpl.UnknownFields - - Addr []byte `protobuf:"bytes,1,opt,name=Addr,proto3" json:"Addr,omitempty"` - State SessionState `protobuf:"varint,2,opt,name=State,proto3,enum=pb.SessionState" json:"State,omitempty"` - Caps []string `protobuf:"bytes,3,rep,name=Caps,proto3" json:"Caps,omitempty"` - IsSynced bool `protobuf:"varint,4,opt,name=IsSynced,proto3" json:"IsSynced,omitempty"` + sizeCache protoimpl.SizeCache } func (x *Session) Reset() { *x = Session{} - if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_pola_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *Session) String() string { @@ -553,7 +536,7 @@ func (*Session) ProtoMessage() {} func (x *Session) ProtoReflect() protoreflect.Message { mi := &file_pola_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -597,20 +580,17 @@ func (x *Session) GetIsSynced() bool { } type SessionList struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Sessions []*Session `protobuf:"bytes,1,rep,name=Sessions,proto3" json:"Sessions,omitempty"` unknownFields protoimpl.UnknownFields - - Sessions []*Session `protobuf:"bytes,1,rep,name=Sessions,proto3" json:"Sessions,omitempty"` + sizeCache protoimpl.SizeCache } func (x *SessionList) Reset() { *x = SessionList{} - if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_pola_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *SessionList) String() string { @@ -621,7 +601,7 @@ func (*SessionList) ProtoMessage() {} func (x *SessionList) ProtoReflect() protoreflect.Message { mi := &file_pola_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -644,20 +624,17 @@ func (x *SessionList) GetSessions() []*Session { } type SRPolicyList struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + SRPolicies []*SRPolicy `protobuf:"bytes,1,rep,name=SRPolicies,proto3" json:"SRPolicies,omitempty"` unknownFields protoimpl.UnknownFields - - SRPolicies []*SRPolicy `protobuf:"bytes,1,rep,name=SRPolicies,proto3" json:"SRPolicies,omitempty"` + sizeCache protoimpl.SizeCache } func (x *SRPolicyList) Reset() { *x = SRPolicyList{} - if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_pola_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *SRPolicyList) String() string { @@ -668,7 +645,7 @@ func (*SRPolicyList) ProtoMessage() {} func (x *SRPolicyList) ProtoReflect() protoreflect.Message { mi := &file_pola_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -691,21 +668,18 @@ func (x *SRPolicyList) GetSRPolicies() []*SRPolicy { } type LsPrefix struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Prefix string `protobuf:"bytes,1,opt,name=prefix,proto3" json:"prefix,omitempty"` + SidIndex uint32 `protobuf:"varint,2,opt,name=sidIndex,proto3" json:"sidIndex,omitempty"` unknownFields protoimpl.UnknownFields - - Prefix string `protobuf:"bytes,1,opt,name=prefix,proto3" json:"prefix,omitempty"` - SidIndex uint32 `protobuf:"varint,2,opt,name=sidIndex,proto3" json:"sidIndex,omitempty"` + sizeCache protoimpl.SizeCache } func (x *LsPrefix) Reset() { *x = LsPrefix{} - if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_pola_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *LsPrefix) String() string { @@ -716,7 +690,7 @@ func (*LsPrefix) ProtoMessage() {} func (x *LsPrefix) ProtoReflect() protoreflect.Message { mi := &file_pola_proto_msgTypes[8] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -746,21 +720,18 @@ func (x *LsPrefix) GetSidIndex() uint32 { } type Metric struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Type MetricType `protobuf:"varint,1,opt,name=type,proto3,enum=pb.MetricType" json:"type,omitempty"` + Value uint32 `protobuf:"varint,2,opt,name=value,proto3" json:"value,omitempty"` unknownFields protoimpl.UnknownFields - - Type MetricType `protobuf:"varint,1,opt,name=type,proto3,enum=pb.MetricType" json:"type,omitempty"` - Value uint32 `protobuf:"varint,2,opt,name=value,proto3" json:"value,omitempty"` + sizeCache protoimpl.SizeCache } func (x *Metric) Reset() { *x = Metric{} - if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_pola_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *Metric) String() string { @@ -771,7 +742,7 @@ func (*Metric) ProtoMessage() {} func (x *Metric) ProtoReflect() protoreflect.Message { mi := &file_pola_proto_msgTypes[9] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -801,27 +772,24 @@ func (x *Metric) GetValue() uint32 { } type LsLink struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - LocalRouterID string `protobuf:"bytes,1,opt,name=localRouterID,proto3" json:"localRouterID,omitempty"` - LocalAsn uint32 `protobuf:"varint,2,opt,name=localAsn,proto3" json:"localAsn,omitempty"` - LocalIP string `protobuf:"bytes,3,opt,name=localIP,proto3" json:"localIP,omitempty"` - RemoteRouterID string `protobuf:"bytes,4,opt,name=remoteRouterID,proto3" json:"remoteRouterID,omitempty"` - RemoteAsn uint32 `protobuf:"varint,5,opt,name=remoteAsn,proto3" json:"remoteAsn,omitempty"` - RemoteIP string `protobuf:"bytes,6,opt,name=remoteIP,proto3" json:"remoteIP,omitempty"` - Metrics []*Metric `protobuf:"bytes,7,rep,name=metrics,proto3" json:"metrics,omitempty"` - AdjSid uint32 `protobuf:"varint,8,opt,name=adjSid,proto3" json:"adjSid,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + LocalRouterID string `protobuf:"bytes,1,opt,name=localRouterID,proto3" json:"localRouterID,omitempty"` + LocalASN uint32 `protobuf:"varint,2,opt,name=localASN,proto3" json:"localASN,omitempty"` + LocalIP string `protobuf:"bytes,3,opt,name=localIP,proto3" json:"localIP,omitempty"` + RemoteRouterID string `protobuf:"bytes,4,opt,name=remoteRouterID,proto3" json:"remoteRouterID,omitempty"` + RemoteASN uint32 `protobuf:"varint,5,opt,name=remoteASN,proto3" json:"remoteASN,omitempty"` + RemoteIP string `protobuf:"bytes,6,opt,name=remoteIP,proto3" json:"remoteIP,omitempty"` + Metrics []*Metric `protobuf:"bytes,7,rep,name=metrics,proto3" json:"metrics,omitempty"` + AdjSID uint32 `protobuf:"varint,8,opt,name=adjSID,proto3" json:"adjSID,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *LsLink) Reset() { *x = LsLink{} - if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_pola_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *LsLink) String() string { @@ -832,7 +800,7 @@ func (*LsLink) ProtoMessage() {} func (x *LsLink) ProtoReflect() protoreflect.Message { mi := &file_pola_proto_msgTypes[10] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -854,9 +822,9 @@ func (x *LsLink) GetLocalRouterID() string { return "" } -func (x *LsLink) GetLocalAsn() uint32 { +func (x *LsLink) GetLocalASN() uint32 { if x != nil { - return x.LocalAsn + return x.LocalASN } return 0 } @@ -875,9 +843,9 @@ func (x *LsLink) GetRemoteRouterID() string { return "" } -func (x *LsLink) GetRemoteAsn() uint32 { +func (x *LsLink) GetRemoteASN() uint32 { if x != nil { - return x.RemoteAsn + return x.RemoteASN } return 0 } @@ -896,35 +864,32 @@ func (x *LsLink) GetMetrics() []*Metric { return nil } -func (x *LsLink) GetAdjSid() uint32 { +func (x *LsLink) GetAdjSID() uint32 { if x != nil { - return x.AdjSid + return x.AdjSID } return 0 } type LsNode struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Asn uint32 `protobuf:"varint,1,opt,name=asn,proto3" json:"asn,omitempty"` + RouterID string `protobuf:"bytes,2,opt,name=routerID,proto3" json:"routerID,omitempty"` + IsisAreaID string `protobuf:"bytes,3,opt,name=isisAreaID,proto3" json:"isisAreaID,omitempty"` + Hostname string `protobuf:"bytes,4,opt,name=hostname,proto3" json:"hostname,omitempty"` + SrgbBegin uint32 `protobuf:"varint,5,opt,name=srgbBegin,proto3" json:"srgbBegin,omitempty"` + SrgbEnd uint32 `protobuf:"varint,6,opt,name=srgbEnd,proto3" json:"srgbEnd,omitempty"` + LsLinks []*LsLink `protobuf:"bytes,7,rep,name=lsLinks,proto3" json:"lsLinks,omitempty"` + LsPrefixes []*LsPrefix `protobuf:"bytes,8,rep,name=lsPrefixes,proto3" json:"lsPrefixes,omitempty"` unknownFields protoimpl.UnknownFields - - Asn uint32 `protobuf:"varint,1,opt,name=asn,proto3" json:"asn,omitempty"` - RouterID string `protobuf:"bytes,2,opt,name=routerID,proto3" json:"routerID,omitempty"` - IsisAreaID string `protobuf:"bytes,3,opt,name=isisAreaID,proto3" json:"isisAreaID,omitempty"` - Hostname string `protobuf:"bytes,4,opt,name=hostname,proto3" json:"hostname,omitempty"` - SrgbBegin uint32 `protobuf:"varint,5,opt,name=srgbBegin,proto3" json:"srgbBegin,omitempty"` - SrgbEnd uint32 `protobuf:"varint,6,opt,name=srgbEnd,proto3" json:"srgbEnd,omitempty"` - LsLinks []*LsLink `protobuf:"bytes,7,rep,name=lsLinks,proto3" json:"lsLinks,omitempty"` - LsPrefixes []*LsPrefix `protobuf:"bytes,8,rep,name=lsPrefixes,proto3" json:"lsPrefixes,omitempty"` + sizeCache protoimpl.SizeCache } func (x *LsNode) Reset() { *x = LsNode{} - if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } + mi := &file_pola_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } func (x *LsNode) String() string { @@ -935,7 +900,7 @@ func (*LsNode) ProtoMessage() {} func (x *LsNode) ProtoReflect() protoreflect.Message { mi := &file_pola_proto_msgTypes[11] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1006,33 +971,30 @@ func (x *LsNode) GetLsPrefixes() []*LsPrefix { return nil } -type Ted struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache +type TED struct { + state protoimpl.MessageState `protogen:"open.v1"` + Enable bool `protobuf:"varint,1,opt,name=enable,proto3" json:"enable,omitempty"` + LsNodes []*LsNode `protobuf:"bytes,2,rep,name=lsNodes,proto3" json:"lsNodes,omitempty"` unknownFields protoimpl.UnknownFields - - Enable bool `protobuf:"varint,1,opt,name=enable,proto3" json:"enable,omitempty"` - LsNodes []*LsNode `protobuf:"bytes,2,rep,name=lsNodes,proto3" json:"lsNodes,omitempty"` + sizeCache protoimpl.SizeCache } -func (x *Ted) Reset() { - *x = Ted{} - if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[12] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } +func (x *TED) Reset() { + *x = TED{} + mi := &file_pola_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } -func (x *Ted) String() string { +func (x *TED) String() string { return protoimpl.X.MessageStringOf(x) } -func (*Ted) ProtoMessage() {} +func (*TED) ProtoMessage() {} -func (x *Ted) ProtoReflect() protoreflect.Message { +func (x *TED) ProtoReflect() protoreflect.Message { mi := &file_pola_proto_msgTypes[12] - if protoimpl.UnsafeEnabled && x != nil { + if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) @@ -1042,19 +1004,19 @@ func (x *Ted) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use Ted.ProtoReflect.Descriptor instead. -func (*Ted) Descriptor() ([]byte, []int) { +// Deprecated: Use TED.ProtoReflect.Descriptor instead. +func (*TED) Descriptor() ([]byte, []int) { return file_pola_proto_rawDescGZIP(), []int{12} } -func (x *Ted) GetEnable() bool { +func (x *TED) GetEnable() bool { if x != nil { return x.Enable } return false } -func (x *Ted) GetLsNodes() []*LsNode { +func (x *TED) GetLsNodes() []*LsNode { if x != nil { return x.LsNodes } @@ -1063,7 +1025,7 @@ func (x *Ted) GetLsNodes() []*LsNode { var File_pola_proto protoreflect.FileDescriptor -var file_pola_proto_rawDesc = []byte{ +var file_pola_proto_rawDesc = string([]byte{ 0x0a, 0x0a, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x02, 0x70, 0x62, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x7d, 0x0a, @@ -1075,9 +1037,9 @@ var file_pola_proto_rawDesc = []byte{ 0x09, 0x52, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x41, 0x64, 0x64, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x64, 0x64, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x64, 0x64, 0x72, 0x22, 0xff, 0x02, 0x0a, - 0x08, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x70, 0x63, 0x65, - 0x70, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x0f, 0x70, 0x63, 0x65, 0x70, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x41, + 0x08, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x50, 0x43, 0x45, + 0x50, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x0f, 0x50, 0x43, 0x45, 0x50, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x72, 0x63, 0x41, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x73, 0x72, 0x63, 0x41, 0x64, 0x64, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x73, 0x74, 0x41, 0x64, 0x64, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, @@ -1138,20 +1100,20 @@ var file_pola_proto_rawDesc = []byte{ 0x61, 0x6c, 0x75, 0x65, 0x22, 0x84, 0x02, 0x0a, 0x06, 0x4c, 0x73, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x24, 0x0a, 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x52, 0x6f, 0x75, - 0x74, 0x65, 0x72, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x41, 0x73, - 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x41, 0x73, - 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x50, 0x18, 0x03, 0x20, 0x01, + 0x74, 0x65, 0x72, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x41, 0x53, + 0x4e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x41, 0x53, + 0x4e, 0x12, 0x18, 0x0a, 0x07, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x50, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x50, 0x12, 0x26, 0x0a, 0x0e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, - 0x72, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x73, 0x6e, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x73, - 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x50, 0x18, 0x06, 0x20, + 0x72, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x53, 0x4e, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x53, + 0x4e, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x50, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x50, 0x12, 0x24, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x72, - 0x69, 0x63, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x64, 0x6a, 0x53, 0x69, 0x64, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x06, 0x61, 0x64, 0x6a, 0x53, 0x69, 0x64, 0x22, 0xfe, 0x01, 0x0a, 0x06, + 0x69, 0x63, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x64, 0x6a, 0x53, 0x49, 0x44, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x06, 0x61, 0x64, 0x6a, 0x53, 0x49, 0x44, 0x22, 0xfe, 0x01, 0x0a, 0x06, 0x4c, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x73, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x61, 0x73, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x6f, 0x75, 0x74, @@ -1168,7 +1130,7 @@ var file_pola_proto_rawDesc = []byte{ 0x0a, 0x0a, 0x6c, 0x73, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x4c, 0x73, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x52, 0x0a, 0x6c, 0x73, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, 0x73, 0x22, 0x43, 0x0a, 0x03, - 0x54, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, + 0x54, 0x45, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x24, 0x0a, 0x07, 0x6c, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x70, 0x62, 0x2e, 0x4c, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x07, 0x6c, 0x73, 0x4e, 0x6f, 0x64, 0x65, @@ -1180,7 +1142,7 @@ var file_pola_proto_rawDesc = []byte{ 0x0a, 0x0a, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x49, 0x47, 0x50, 0x10, 0x00, 0x12, 0x06, 0x0a, 0x02, 0x54, 0x45, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x44, 0x45, 0x4c, 0x41, 0x59, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x48, 0x4f, 0x50, 0x43, - 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x03, 0x32, 0x88, 0x04, 0x0a, 0x0a, 0x50, 0x63, 0x65, 0x53, 0x65, + 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x03, 0x32, 0x88, 0x04, 0x0a, 0x0a, 0x50, 0x43, 0x45, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3e, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x17, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x6e, 0x70, 0x75, 0x74, @@ -1207,32 +1169,32 @@ var file_pola_proto_rawDesc = []byte{ 0x79, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x22, - 0x00, 0x12, 0x2b, 0x0a, 0x06, 0x47, 0x65, 0x74, 0x54, 0x65, 0x64, 0x12, 0x16, 0x2e, 0x67, 0x6f, + 0x00, 0x12, 0x2b, 0x0a, 0x06, 0x47, 0x65, 0x74, 0x54, 0x45, 0x44, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, - 0x70, 0x74, 0x79, 0x1a, 0x07, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x65, 0x64, 0x22, 0x00, 0x12, 0x31, + 0x70, 0x74, 0x79, 0x1a, 0x07, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x45, 0x44, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0x11, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x00, 0x42, 0x21, 0x5a, 0x1f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6e, 0x74, 0x74, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6f, 0x6c, 0x61, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} +}) var ( file_pola_proto_rawDescOnce sync.Once - file_pola_proto_rawDescData = file_pola_proto_rawDesc + file_pola_proto_rawDescData []byte ) func file_pola_proto_rawDescGZIP() []byte { file_pola_proto_rawDescOnce.Do(func() { - file_pola_proto_rawDescData = protoimpl.X.CompressGZIP(file_pola_proto_rawDescData) + file_pola_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_pola_proto_rawDesc), len(file_pola_proto_rawDesc))) }) return file_pola_proto_rawDescData } var file_pola_proto_enumTypes = make([]protoimpl.EnumInfo, 3) var file_pola_proto_msgTypes = make([]protoimpl.MessageInfo, 13) -var file_pola_proto_goTypes = []interface{}{ +var file_pola_proto_goTypes = []any{ (SRPolicyType)(0), // 0: pb.SRPolicyType (SessionState)(0), // 1: pb.SessionState (MetricType)(0), // 2: pb.MetricType @@ -1248,8 +1210,8 @@ var file_pola_proto_goTypes = []interface{}{ (*Metric)(nil), // 12: pb.Metric (*LsLink)(nil), // 13: pb.LsLink (*LsNode)(nil), // 14: pb.LsNode - (*Ted)(nil), // 15: pb.Ted - (*empty.Empty)(nil), // 16: google.protobuf.Empty + (*TED)(nil), // 15: pb.TED + (*emptypb.Empty)(nil), // 16: google.protobuf.Empty } var file_pola_proto_depIdxs = []int32{ 0, // 0: pb.SRPolicy.type:type_name -> pb.SRPolicyType @@ -1264,23 +1226,23 @@ var file_pola_proto_depIdxs = []int32{ 12, // 9: pb.LsLink.metrics:type_name -> pb.Metric 13, // 10: pb.LsNode.lsLinks:type_name -> pb.LsLink 11, // 11: pb.LsNode.lsPrefixes:type_name -> pb.LsPrefix - 14, // 12: pb.Ted.lsNodes:type_name -> pb.LsNode - 5, // 13: pb.PceService.CreateSRPolicy:input_type -> pb.CreateSRPolicyInput - 5, // 14: pb.PceService.CreateSRPolicyWithoutLinkState:input_type -> pb.CreateSRPolicyInput - 6, // 15: pb.PceService.DeleteSRPolicy:input_type -> pb.DeleteSRPolicyInput - 6, // 16: pb.PceService.DeleteSRPolicyWithoutLinkState:input_type -> pb.DeleteSRPolicyInput - 16, // 17: pb.PceService.GetSessionList:input_type -> google.protobuf.Empty - 16, // 18: pb.PceService.GetSRPolicyList:input_type -> google.protobuf.Empty - 16, // 19: pb.PceService.GetTed:input_type -> google.protobuf.Empty - 8, // 20: pb.PceService.DeleteSession:input_type -> pb.Session - 7, // 21: pb.PceService.CreateSRPolicy:output_type -> pb.RequestStatus - 7, // 22: pb.PceService.CreateSRPolicyWithoutLinkState:output_type -> pb.RequestStatus - 7, // 23: pb.PceService.DeleteSRPolicy:output_type -> pb.RequestStatus - 7, // 24: pb.PceService.DeleteSRPolicyWithoutLinkState:output_type -> pb.RequestStatus - 9, // 25: pb.PceService.GetSessionList:output_type -> pb.SessionList - 10, // 26: pb.PceService.GetSRPolicyList:output_type -> pb.SRPolicyList - 15, // 27: pb.PceService.GetTed:output_type -> pb.Ted - 7, // 28: pb.PceService.DeleteSession:output_type -> pb.RequestStatus + 14, // 12: pb.TED.lsNodes:type_name -> pb.LsNode + 5, // 13: pb.PCEService.CreateSRPolicy:input_type -> pb.CreateSRPolicyInput + 5, // 14: pb.PCEService.CreateSRPolicyWithoutLinkState:input_type -> pb.CreateSRPolicyInput + 6, // 15: pb.PCEService.DeleteSRPolicy:input_type -> pb.DeleteSRPolicyInput + 6, // 16: pb.PCEService.DeleteSRPolicyWithoutLinkState:input_type -> pb.DeleteSRPolicyInput + 16, // 17: pb.PCEService.GetSessionList:input_type -> google.protobuf.Empty + 16, // 18: pb.PCEService.GetSRPolicyList:input_type -> google.protobuf.Empty + 16, // 19: pb.PCEService.GetTED:input_type -> google.protobuf.Empty + 8, // 20: pb.PCEService.DeleteSession:input_type -> pb.Session + 7, // 21: pb.PCEService.CreateSRPolicy:output_type -> pb.RequestStatus + 7, // 22: pb.PCEService.CreateSRPolicyWithoutLinkState:output_type -> pb.RequestStatus + 7, // 23: pb.PCEService.DeleteSRPolicy:output_type -> pb.RequestStatus + 7, // 24: pb.PCEService.DeleteSRPolicyWithoutLinkState:output_type -> pb.RequestStatus + 9, // 25: pb.PCEService.GetSessionList:output_type -> pb.SessionList + 10, // 26: pb.PCEService.GetSRPolicyList:output_type -> pb.SRPolicyList + 15, // 27: pb.PCEService.GetTED:output_type -> pb.TED + 7, // 28: pb.PCEService.DeleteSession:output_type -> pb.RequestStatus 21, // [21:29] is the sub-list for method output_type 13, // [13:21] is the sub-list for method input_type 13, // [13:13] is the sub-list for extension type_name @@ -1293,169 +1255,11 @@ func file_pola_proto_init() { if File_pola_proto != nil { return } - if !protoimpl.UnsafeEnabled { - file_pola_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Segment); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pola_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SRPolicy); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pola_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CreateSRPolicyInput); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pola_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteSRPolicyInput); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pola_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RequestStatus); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pola_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Session); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pola_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SessionList); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pola_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SRPolicyList); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pola_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LsPrefix); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pola_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Metric); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pola_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LsLink); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pola_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LsNode); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pola_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Ted); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_pola_proto_rawDesc, + RawDescriptor: unsafe.Slice(unsafe.StringData(file_pola_proto_rawDesc), len(file_pola_proto_rawDesc)), NumEnums: 3, NumMessages: 13, NumExtensions: 0, @@ -1467,7 +1271,6 @@ func file_pola_proto_init() { MessageInfos: file_pola_proto_msgTypes, }.Build() File_pola_proto = out.File - file_pola_proto_rawDesc = nil file_pola_proto_goTypes = nil file_pola_proto_depIdxs = nil } diff --git a/api/grpc/pola.proto b/api/grpc/pola.proto index 3b91ed9e..ee42958a 100644 --- a/api/grpc/pola.proto +++ b/api/grpc/pola.proto @@ -11,21 +11,14 @@ option go_package = "github.com/nttcom/pola/api/grpc"; import "google/protobuf/empty.proto"; -service PceService { +service PCEService { rpc CreateSRPolicy (CreateSRPolicyInput) returns (RequestStatus) {}; - rpc CreateSRPolicyWithoutLinkState (CreateSRPolicyInput) returns (RequestStatus) {}; - rpc DeleteSRPolicy (DeleteSRPolicyInput) returns (RequestStatus) {}; - rpc DeleteSRPolicyWithoutLinkState (DeleteSRPolicyInput) returns (RequestStatus) {}; - rpc GetSessionList (google.protobuf.Empty) returns (SessionList) {}; - rpc GetSRPolicyList (google.protobuf.Empty) returns (SRPolicyList) {}; - - rpc GetTed (google.protobuf.Empty) returns (Ted) {}; - + rpc GetTED (google.protobuf.Empty) returns (TED) {}; rpc DeleteSession (Session) returns (RequestStatus) {}; } @@ -42,7 +35,7 @@ enum SRPolicyType { } message SRPolicy { - bytes pcepSessionAddr = 1; + bytes PCEPSessionAddr = 1; bytes srcAddr = 2; bytes dstAddr = 3; string srcRouterID = 4; @@ -108,13 +101,13 @@ message Metric { message LsLink { string localRouterID = 1; - uint32 localAsn = 2; + uint32 localASN = 2; string localIP = 3; string remoteRouterID = 4; - uint32 remoteAsn = 5; + uint32 remoteASN = 5; string remoteIP = 6; repeated Metric metrics = 7; - uint32 adjSid = 8; + uint32 adjSID = 8; } message LsNode { @@ -129,7 +122,7 @@ message LsNode { } -message Ted { +message TED { bool enable = 1; repeated LsNode lsNodes = 2; } diff --git a/api/grpc/pola.zap.go b/api/grpc/pola.zap.go index b4c360b9..135393a6 100644 --- a/api/grpc/pola.zap.go +++ b/api/grpc/pola.zap.go @@ -14,8 +14,8 @@ import ( // Implements zapcore.ObjectMarshaler interface for SRPolicy func (x *SRPolicy) MarshalLogObject(enc zapcore.ObjectEncoder) error { // Convert IP address slices to netip.Addr - ssAddr, _ := netip.AddrFromSlice(x.GetPcepSessionAddr()) - enc.AddString("PcepSessionAddr", ssAddr.String()) + ssAddr, _ := netip.AddrFromSlice(x.GetPCEPSessionAddr()) + enc.AddString("PCEPSessionAddr", ssAddr.String()) srcAddr, _ := netip.AddrFromSlice(x.GetSrcAddr()) enc.AddString("SrcAddr", srcAddr.String()) dstAddr, _ := netip.AddrFromSlice(x.GetDstAddr()) @@ -43,6 +43,6 @@ func (x *SRPolicy) MarshalLogObject(enc zapcore.ObjectEncoder) error { // Implements zapcore.ObjectMarshaler interface for Segment func (x *Segment) MarshalLogObject(enc zapcore.ObjectEncoder) error { - enc.AddString("Sid", x.GetSid()) + enc.AddString("SID", x.GetSid()) return nil } diff --git a/api/grpc/pola_grpc.pb.go b/api/grpc/pola_grpc.pb.go index 85236891..366e2ffa 100644 --- a/api/grpc/pola_grpc.pb.go +++ b/api/grpc/pola_grpc.pb.go @@ -1,22 +1,13 @@ -// Copyright (c) 2022 NTT Communications Corporation -// -// This software is released under the MIT License. -// see https://github.com/nttcom/pola/blob/main/LICENSE - // Code generated by protoc-gen-go-grpc. DO NOT EDIT. -// versions: -// - protoc-gen-go-grpc v1.3.0 -// - protoc v3.12.4 -// source: pola.proto package grpc import ( context "context" - empty "github.com/golang/protobuf/ptypes/empty" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" + emptypb "google.golang.org/protobuf/types/known/emptypb" ) // This is a compile-time assertion to ensure that this generated file @@ -24,349 +15,338 @@ import ( // Requires gRPC-Go v1.32.0 or later. const _ = grpc.SupportPackageIsVersion7 -const ( - PceService_CreateSRPolicy_FullMethodName = "/pb.PceService/CreateSRPolicy" - PceService_CreateSRPolicyWithoutLinkState_FullMethodName = "/pb.PceService/CreateSRPolicyWithoutLinkState" - PceService_DeleteSRPolicy_FullMethodName = "/pb.PceService/DeleteSRPolicy" - PceService_DeleteSRPolicyWithoutLinkState_FullMethodName = "/pb.PceService/DeleteSRPolicyWithoutLinkState" - PceService_GetSessionList_FullMethodName = "/pb.PceService/GetSessionList" - PceService_GetSRPolicyList_FullMethodName = "/pb.PceService/GetSRPolicyList" - PceService_GetTed_FullMethodName = "/pb.PceService/GetTed" - PceService_DeleteSession_FullMethodName = "/pb.PceService/DeleteSession" -) - -// PceServiceClient is the client API for PceService service. +// PCEServiceClient is the client API for PCEService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type PceServiceClient interface { +type PCEServiceClient interface { CreateSRPolicy(ctx context.Context, in *CreateSRPolicyInput, opts ...grpc.CallOption) (*RequestStatus, error) CreateSRPolicyWithoutLinkState(ctx context.Context, in *CreateSRPolicyInput, opts ...grpc.CallOption) (*RequestStatus, error) DeleteSRPolicy(ctx context.Context, in *DeleteSRPolicyInput, opts ...grpc.CallOption) (*RequestStatus, error) DeleteSRPolicyWithoutLinkState(ctx context.Context, in *DeleteSRPolicyInput, opts ...grpc.CallOption) (*RequestStatus, error) - GetSessionList(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*SessionList, error) - GetSRPolicyList(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*SRPolicyList, error) - GetTed(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*Ted, error) + GetSessionList(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*SessionList, error) + GetSRPolicyList(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*SRPolicyList, error) + GetTED(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*TED, error) DeleteSession(ctx context.Context, in *Session, opts ...grpc.CallOption) (*RequestStatus, error) } -type pceServiceClient struct { +type pCEServiceClient struct { cc grpc.ClientConnInterface } -func NewPceServiceClient(cc grpc.ClientConnInterface) PceServiceClient { - return &pceServiceClient{cc} +func NewPCEServiceClient(cc grpc.ClientConnInterface) PCEServiceClient { + return &pCEServiceClient{cc} } -func (c *pceServiceClient) CreateSRPolicy(ctx context.Context, in *CreateSRPolicyInput, opts ...grpc.CallOption) (*RequestStatus, error) { +func (c *pCEServiceClient) CreateSRPolicy(ctx context.Context, in *CreateSRPolicyInput, opts ...grpc.CallOption) (*RequestStatus, error) { out := new(RequestStatus) - err := c.cc.Invoke(ctx, PceService_CreateSRPolicy_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, "/pb.PCEService/CreateSRPolicy", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *pceServiceClient) CreateSRPolicyWithoutLinkState(ctx context.Context, in *CreateSRPolicyInput, opts ...grpc.CallOption) (*RequestStatus, error) { +func (c *pCEServiceClient) CreateSRPolicyWithoutLinkState(ctx context.Context, in *CreateSRPolicyInput, opts ...grpc.CallOption) (*RequestStatus, error) { out := new(RequestStatus) - err := c.cc.Invoke(ctx, PceService_CreateSRPolicyWithoutLinkState_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, "/pb.PCEService/CreateSRPolicyWithoutLinkState", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *pceServiceClient) DeleteSRPolicy(ctx context.Context, in *DeleteSRPolicyInput, opts ...grpc.CallOption) (*RequestStatus, error) { +func (c *pCEServiceClient) DeleteSRPolicy(ctx context.Context, in *DeleteSRPolicyInput, opts ...grpc.CallOption) (*RequestStatus, error) { out := new(RequestStatus) - err := c.cc.Invoke(ctx, PceService_DeleteSRPolicy_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, "/pb.PCEService/DeleteSRPolicy", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *pceServiceClient) DeleteSRPolicyWithoutLinkState(ctx context.Context, in *DeleteSRPolicyInput, opts ...grpc.CallOption) (*RequestStatus, error) { +func (c *pCEServiceClient) DeleteSRPolicyWithoutLinkState(ctx context.Context, in *DeleteSRPolicyInput, opts ...grpc.CallOption) (*RequestStatus, error) { out := new(RequestStatus) - err := c.cc.Invoke(ctx, PceService_DeleteSRPolicyWithoutLinkState_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, "/pb.PCEService/DeleteSRPolicyWithoutLinkState", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *pceServiceClient) GetSessionList(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*SessionList, error) { +func (c *pCEServiceClient) GetSessionList(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*SessionList, error) { out := new(SessionList) - err := c.cc.Invoke(ctx, PceService_GetSessionList_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, "/pb.PCEService/GetSessionList", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *pceServiceClient) GetSRPolicyList(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*SRPolicyList, error) { +func (c *pCEServiceClient) GetSRPolicyList(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*SRPolicyList, error) { out := new(SRPolicyList) - err := c.cc.Invoke(ctx, PceService_GetSRPolicyList_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, "/pb.PCEService/GetSRPolicyList", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *pceServiceClient) GetTed(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*Ted, error) { - out := new(Ted) - err := c.cc.Invoke(ctx, PceService_GetTed_FullMethodName, in, out, opts...) +func (c *pCEServiceClient) GetTED(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*TED, error) { + out := new(TED) + err := c.cc.Invoke(ctx, "/pb.PCEService/GetTED", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *pceServiceClient) DeleteSession(ctx context.Context, in *Session, opts ...grpc.CallOption) (*RequestStatus, error) { +func (c *pCEServiceClient) DeleteSession(ctx context.Context, in *Session, opts ...grpc.CallOption) (*RequestStatus, error) { out := new(RequestStatus) - err := c.cc.Invoke(ctx, PceService_DeleteSession_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, "/pb.PCEService/DeleteSession", in, out, opts...) if err != nil { return nil, err } return out, nil } -// PceServiceServer is the server API for PceService service. -// All implementations must embed UnimplementedPceServiceServer +// PCEServiceServer is the server API for PCEService service. +// All implementations must embed UnimplementedPCEServiceServer // for forward compatibility -type PceServiceServer interface { +type PCEServiceServer interface { CreateSRPolicy(context.Context, *CreateSRPolicyInput) (*RequestStatus, error) CreateSRPolicyWithoutLinkState(context.Context, *CreateSRPolicyInput) (*RequestStatus, error) DeleteSRPolicy(context.Context, *DeleteSRPolicyInput) (*RequestStatus, error) DeleteSRPolicyWithoutLinkState(context.Context, *DeleteSRPolicyInput) (*RequestStatus, error) - GetSessionList(context.Context, *empty.Empty) (*SessionList, error) - GetSRPolicyList(context.Context, *empty.Empty) (*SRPolicyList, error) - GetTed(context.Context, *empty.Empty) (*Ted, error) + GetSessionList(context.Context, *emptypb.Empty) (*SessionList, error) + GetSRPolicyList(context.Context, *emptypb.Empty) (*SRPolicyList, error) + GetTED(context.Context, *emptypb.Empty) (*TED, error) DeleteSession(context.Context, *Session) (*RequestStatus, error) - mustEmbedUnimplementedPceServiceServer() + mustEmbedUnimplementedPCEServiceServer() } -// UnimplementedPceServiceServer must be embedded to have forward compatible implementations. -type UnimplementedPceServiceServer struct { +// UnimplementedPCEServiceServer must be embedded to have forward compatible implementations. +type UnimplementedPCEServiceServer struct { } -func (UnimplementedPceServiceServer) CreateSRPolicy(context.Context, *CreateSRPolicyInput) (*RequestStatus, error) { +func (UnimplementedPCEServiceServer) CreateSRPolicy(context.Context, *CreateSRPolicyInput) (*RequestStatus, error) { return nil, status.Errorf(codes.Unimplemented, "method CreateSRPolicy not implemented") } -func (UnimplementedPceServiceServer) CreateSRPolicyWithoutLinkState(context.Context, *CreateSRPolicyInput) (*RequestStatus, error) { +func (UnimplementedPCEServiceServer) CreateSRPolicyWithoutLinkState(context.Context, *CreateSRPolicyInput) (*RequestStatus, error) { return nil, status.Errorf(codes.Unimplemented, "method CreateSRPolicyWithoutLinkState not implemented") } -func (UnimplementedPceServiceServer) DeleteSRPolicy(context.Context, *DeleteSRPolicyInput) (*RequestStatus, error) { +func (UnimplementedPCEServiceServer) DeleteSRPolicy(context.Context, *DeleteSRPolicyInput) (*RequestStatus, error) { return nil, status.Errorf(codes.Unimplemented, "method DeleteSRPolicy not implemented") } -func (UnimplementedPceServiceServer) DeleteSRPolicyWithoutLinkState(context.Context, *DeleteSRPolicyInput) (*RequestStatus, error) { +func (UnimplementedPCEServiceServer) DeleteSRPolicyWithoutLinkState(context.Context, *DeleteSRPolicyInput) (*RequestStatus, error) { return nil, status.Errorf(codes.Unimplemented, "method DeleteSRPolicyWithoutLinkState not implemented") } -func (UnimplementedPceServiceServer) GetSessionList(context.Context, *empty.Empty) (*SessionList, error) { +func (UnimplementedPCEServiceServer) GetSessionList(context.Context, *emptypb.Empty) (*SessionList, error) { return nil, status.Errorf(codes.Unimplemented, "method GetSessionList not implemented") } -func (UnimplementedPceServiceServer) GetSRPolicyList(context.Context, *empty.Empty) (*SRPolicyList, error) { +func (UnimplementedPCEServiceServer) GetSRPolicyList(context.Context, *emptypb.Empty) (*SRPolicyList, error) { return nil, status.Errorf(codes.Unimplemented, "method GetSRPolicyList not implemented") } -func (UnimplementedPceServiceServer) GetTed(context.Context, *empty.Empty) (*Ted, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetTed not implemented") +func (UnimplementedPCEServiceServer) GetTED(context.Context, *emptypb.Empty) (*TED, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetTED not implemented") } -func (UnimplementedPceServiceServer) DeleteSession(context.Context, *Session) (*RequestStatus, error) { +func (UnimplementedPCEServiceServer) DeleteSession(context.Context, *Session) (*RequestStatus, error) { return nil, status.Errorf(codes.Unimplemented, "method DeleteSession not implemented") } -func (UnimplementedPceServiceServer) mustEmbedUnimplementedPceServiceServer() {} +func (UnimplementedPCEServiceServer) mustEmbedUnimplementedPCEServiceServer() {} -// UnsafePceServiceServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to PceServiceServer will +// UnsafePCEServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to PCEServiceServer will // result in compilation errors. -type UnsafePceServiceServer interface { - mustEmbedUnimplementedPceServiceServer() +type UnsafePCEServiceServer interface { + mustEmbedUnimplementedPCEServiceServer() } -func RegisterPceServiceServer(s grpc.ServiceRegistrar, srv PceServiceServer) { - s.RegisterService(&PceService_ServiceDesc, srv) +func RegisterPCEServiceServer(s grpc.ServiceRegistrar, srv PCEServiceServer) { + s.RegisterService(&PCEService_ServiceDesc, srv) } -func _PceService_CreateSRPolicy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { +func _PCEService_CreateSRPolicy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(CreateSRPolicyInput) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(PceServiceServer).CreateSRPolicy(ctx, in) + return srv.(PCEServiceServer).CreateSRPolicy(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: PceService_CreateSRPolicy_FullMethodName, + FullMethod: "/pb.PCEService/CreateSRPolicy", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(PceServiceServer).CreateSRPolicy(ctx, req.(*CreateSRPolicyInput)) + return srv.(PCEServiceServer).CreateSRPolicy(ctx, req.(*CreateSRPolicyInput)) } return interceptor(ctx, in, info, handler) } -func _PceService_CreateSRPolicyWithoutLinkState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { +func _PCEService_CreateSRPolicyWithoutLinkState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(CreateSRPolicyInput) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(PceServiceServer).CreateSRPolicyWithoutLinkState(ctx, in) + return srv.(PCEServiceServer).CreateSRPolicyWithoutLinkState(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: PceService_CreateSRPolicyWithoutLinkState_FullMethodName, + FullMethod: "/pb.PCEService/CreateSRPolicyWithoutLinkState", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(PceServiceServer).CreateSRPolicyWithoutLinkState(ctx, req.(*CreateSRPolicyInput)) + return srv.(PCEServiceServer).CreateSRPolicyWithoutLinkState(ctx, req.(*CreateSRPolicyInput)) } return interceptor(ctx, in, info, handler) } -func _PceService_DeleteSRPolicy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { +func _PCEService_DeleteSRPolicy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(DeleteSRPolicyInput) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(PceServiceServer).DeleteSRPolicy(ctx, in) + return srv.(PCEServiceServer).DeleteSRPolicy(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: PceService_DeleteSRPolicy_FullMethodName, + FullMethod: "/pb.PCEService/DeleteSRPolicy", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(PceServiceServer).DeleteSRPolicy(ctx, req.(*DeleteSRPolicyInput)) + return srv.(PCEServiceServer).DeleteSRPolicy(ctx, req.(*DeleteSRPolicyInput)) } return interceptor(ctx, in, info, handler) } -func _PceService_DeleteSRPolicyWithoutLinkState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { +func _PCEService_DeleteSRPolicyWithoutLinkState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(DeleteSRPolicyInput) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(PceServiceServer).DeleteSRPolicyWithoutLinkState(ctx, in) + return srv.(PCEServiceServer).DeleteSRPolicyWithoutLinkState(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: PceService_DeleteSRPolicyWithoutLinkState_FullMethodName, + FullMethod: "/pb.PCEService/DeleteSRPolicyWithoutLinkState", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(PceServiceServer).DeleteSRPolicyWithoutLinkState(ctx, req.(*DeleteSRPolicyInput)) + return srv.(PCEServiceServer).DeleteSRPolicyWithoutLinkState(ctx, req.(*DeleteSRPolicyInput)) } return interceptor(ctx, in, info, handler) } -func _PceService_GetSessionList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(empty.Empty) +func _PCEService_GetSessionList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(emptypb.Empty) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(PceServiceServer).GetSessionList(ctx, in) + return srv.(PCEServiceServer).GetSessionList(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: PceService_GetSessionList_FullMethodName, + FullMethod: "/pb.PCEService/GetSessionList", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(PceServiceServer).GetSessionList(ctx, req.(*empty.Empty)) + return srv.(PCEServiceServer).GetSessionList(ctx, req.(*emptypb.Empty)) } return interceptor(ctx, in, info, handler) } -func _PceService_GetSRPolicyList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(empty.Empty) +func _PCEService_GetSRPolicyList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(emptypb.Empty) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(PceServiceServer).GetSRPolicyList(ctx, in) + return srv.(PCEServiceServer).GetSRPolicyList(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: PceService_GetSRPolicyList_FullMethodName, + FullMethod: "/pb.PCEService/GetSRPolicyList", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(PceServiceServer).GetSRPolicyList(ctx, req.(*empty.Empty)) + return srv.(PCEServiceServer).GetSRPolicyList(ctx, req.(*emptypb.Empty)) } return interceptor(ctx, in, info, handler) } -func _PceService_GetTed_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(empty.Empty) +func _PCEService_GetTED_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(emptypb.Empty) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(PceServiceServer).GetTed(ctx, in) + return srv.(PCEServiceServer).GetTED(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: PceService_GetTed_FullMethodName, + FullMethod: "/pb.PCEService/GetTED", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(PceServiceServer).GetTed(ctx, req.(*empty.Empty)) + return srv.(PCEServiceServer).GetTED(ctx, req.(*emptypb.Empty)) } return interceptor(ctx, in, info, handler) } -func _PceService_DeleteSession_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { +func _PCEService_DeleteSession_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(Session) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(PceServiceServer).DeleteSession(ctx, in) + return srv.(PCEServiceServer).DeleteSession(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: PceService_DeleteSession_FullMethodName, + FullMethod: "/pb.PCEService/DeleteSession", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(PceServiceServer).DeleteSession(ctx, req.(*Session)) + return srv.(PCEServiceServer).DeleteSession(ctx, req.(*Session)) } return interceptor(ctx, in, info, handler) } -// PceService_ServiceDesc is the grpc.ServiceDesc for PceService service. +// PCEService_ServiceDesc is the grpc.ServiceDesc for PCEService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) -var PceService_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "pb.PceService", - HandlerType: (*PceServiceServer)(nil), +var PCEService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "pb.PCEService", + HandlerType: (*PCEServiceServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "CreateSRPolicy", - Handler: _PceService_CreateSRPolicy_Handler, + Handler: _PCEService_CreateSRPolicy_Handler, }, { MethodName: "CreateSRPolicyWithoutLinkState", - Handler: _PceService_CreateSRPolicyWithoutLinkState_Handler, + Handler: _PCEService_CreateSRPolicyWithoutLinkState_Handler, }, { MethodName: "DeleteSRPolicy", - Handler: _PceService_DeleteSRPolicy_Handler, + Handler: _PCEService_DeleteSRPolicy_Handler, }, { MethodName: "DeleteSRPolicyWithoutLinkState", - Handler: _PceService_DeleteSRPolicyWithoutLinkState_Handler, + Handler: _PCEService_DeleteSRPolicyWithoutLinkState_Handler, }, { MethodName: "GetSessionList", - Handler: _PceService_GetSessionList_Handler, + Handler: _PCEService_GetSessionList_Handler, }, { MethodName: "GetSRPolicyList", - Handler: _PceService_GetSRPolicyList_Handler, + Handler: _PCEService_GetSRPolicyList_Handler, }, { - MethodName: "GetTed", - Handler: _PceService_GetTed_Handler, + MethodName: "GetTED", + Handler: _PCEService_GetTED_Handler, }, { MethodName: "DeleteSession", - Handler: _PceService_DeleteSession_Handler, + Handler: _PCEService_DeleteSession_Handler, }, }, Streams: []grpc.StreamDesc{}, diff --git a/cmd/pola/grpc/grpc_client.go b/cmd/pola/grpc/grpc_client.go index ebd115f9..010f3852 100644 --- a/cmd/pola/grpc/grpc_client.go +++ b/cmd/pola/grpc/grpc_client.go @@ -27,7 +27,7 @@ type Session struct { IsSynced bool } -func GetSessions(client pb.PceServiceClient) ([]Session, error) { +func GetSessions(client pb.PCEServiceClient) ([]Session, error) { ctx, cancel := withTimeout() defer cancel() @@ -52,7 +52,7 @@ func GetSessions(client pb.PceServiceClient) ([]Session, error) { return sessions, nil } -func DeleteSession(client pb.PceServiceClient, session *pb.Session) error { +func DeleteSession(client pb.PCEServiceClient, session *pb.Session) error { ctx, cancel := withTimeout() defer cancel() _, err := client.DeleteSession(ctx, session) @@ -62,7 +62,7 @@ func DeleteSession(client pb.PceServiceClient, session *pb.Session) error { return nil } -func GetSRPolicyList(client pb.PceServiceClient) (map[netip.Addr][]table.SRPolicy, error) { +func GetSRPolicyList(client pb.PCEServiceClient) (map[netip.Addr][]table.SRPolicy, error) { ctx, cancel := withTimeout() defer cancel() @@ -74,7 +74,7 @@ func GetSRPolicyList(client pb.PceServiceClient) (map[netip.Addr][]table.SRPolic policies := make(map[netip.Addr][]table.SRPolicy, len(ret.GetSRPolicies())) for _, p := range ret.GetSRPolicies() { - peerAddr, _ := netip.AddrFromSlice(p.PcepSessionAddr) + peerAddr, _ := netip.AddrFromSlice(p.PCEPSessionAddr) srcAddr, _ := netip.AddrFromSlice(p.SrcAddr) dstAddr, _ := netip.AddrFromSlice(p.DstAddr) var segmentList []table.Segment @@ -99,7 +99,7 @@ func GetSRPolicyList(client pb.PceServiceClient) (map[netip.Addr][]table.SRPolic return policies, nil } -func CreateSRPolicy(client pb.PceServiceClient, input *pb.CreateSRPolicyInput) error { +func CreateSRPolicy(client pb.PCEServiceClient, input *pb.CreateSRPolicyInput) error { ctx, cancel := withTimeout() defer cancel() @@ -107,7 +107,7 @@ func CreateSRPolicy(client pb.PceServiceClient, input *pb.CreateSRPolicyInput) e return err } -func CreateSRPolicyWithoutLinkState(client pb.PceServiceClient, input *pb.CreateSRPolicyInput) error { +func CreateSRPolicyWithoutLinkState(client pb.PCEServiceClient, input *pb.CreateSRPolicyInput) error { ctx, cancel := withTimeout() defer cancel() @@ -115,7 +115,7 @@ func CreateSRPolicyWithoutLinkState(client pb.PceServiceClient, input *pb.Create return err } -func DeleteSRPolicy(client pb.PceServiceClient, input *pb.DeleteSRPolicyInput) error { +func DeleteSRPolicy(client pb.PCEServiceClient, input *pb.DeleteSRPolicyInput) error { ctx, cancel := withTimeout() defer cancel() @@ -123,11 +123,11 @@ func DeleteSRPolicy(client pb.PceServiceClient, input *pb.DeleteSRPolicyInput) e return err } -func GetTed(client pb.PceServiceClient) (*table.LsTed, error) { +func GetTED(client pb.PCEServiceClient) (*table.LsTED, error) { ctx, cancel := withTimeout() defer cancel() - ret, err := client.GetTed(ctx, &empty.Empty{}) + ret, err := client.GetTED(ctx, &empty.Empty{}) if err != nil { return nil, err } @@ -136,7 +136,7 @@ func GetTed(client pb.PceServiceClient) (*table.LsTed, error) { return nil, errors.New("ted is disabled") } - ted := &table.LsTed{ + ted := &table.LsTED{ ID: 1, Nodes: make(map[uint32]map[string]*table.LsNode), } @@ -152,8 +152,8 @@ func GetTed(client pb.PceServiceClient) (*table.LsTed, error) { return ted, nil } -// initializeLsNodes initializes LsNodes in the LsTed table using the given array of nodes -func initializeLsNodes(ted *table.LsTed, nodes []*pb.LsNode) { +// initializeLsNodes initializes LsNodes in the LsTED table using the given array of nodes +func initializeLsNodes(ted *table.LsTED, nodes []*pb.LsNode) { for _, node := range nodes { lsNode := table.NewLsNode(node.GetAsn(), node.GetRouterID()) lsNode.Hostname = node.GetHostname() @@ -161,17 +161,17 @@ func initializeLsNodes(ted *table.LsTed, nodes []*pb.LsNode) { lsNode.SrgbBegin = node.GetSrgbBegin() lsNode.SrgbEnd = node.GetSrgbEnd() - if _, ok := ted.Nodes[lsNode.Asn]; !ok { - ted.Nodes[lsNode.Asn] = map[string]*table.LsNode{} + if _, ok := ted.Nodes[lsNode.ASN]; !ok { + ted.Nodes[lsNode.ASN] = map[string]*table.LsNode{} } - ted.Nodes[lsNode.Asn][lsNode.RouterID] = lsNode + ted.Nodes[lsNode.ASN][lsNode.RouterID] = lsNode } } -func addLsNode(ted *table.LsTed, node *pb.LsNode) error { +func addLsNode(ted *table.LsTED, node *pb.LsNode) error { for _, link := range node.GetLsLinks() { - localNode := ted.Nodes[link.LocalAsn][link.LocalRouterID] - remoteNode := ted.Nodes[link.RemoteAsn][link.RemoteRouterID] + localNode := ted.Nodes[link.LocalASN][link.LocalRouterID] + remoteNode := ted.Nodes[link.RemoteASN][link.RemoteRouterID] lsLink, err := createLsLink(localNode, remoteNode, link) if err != nil { return err @@ -206,7 +206,7 @@ func createLsLink(localNode, remoteNode *table.LsNode, link *pb.LsLink) (*table. lsLink := &table.LsLink{ LocalNode: localNode, RemoteNode: remoteNode, - AdjSid: link.GetAdjSid(), + AdjSid: link.GetAdjSID(), } var err error lsLink.LocalIP, err = netip.ParseAddr(link.GetLocalIP()) @@ -229,14 +229,14 @@ func createLsLink(localNode, remoteNode *table.LsNode, link *pb.LsLink) (*table. func createMetric(metricInfo *pb.Metric) (*table.Metric, error) { switch metricInfo.GetType() { - case pb.MetricType_IGP: - return table.NewMetric(table.IGP_METRIC, metricInfo.GetValue()), nil - case pb.MetricType_TE: - return table.NewMetric(table.TE_METRIC, metricInfo.GetValue()), nil - case pb.MetricType_DELAY: - return table.NewMetric(table.DELAY_METRIC, metricInfo.GetValue()), nil - case pb.MetricType_HOPCOUNT: - return table.NewMetric(table.HOPCOUNT_METRIC, metricInfo.GetValue()), nil + case pb.MetricType_METRIC_TYPE_IGP: + return table.NewMetric(table.IGPMetric, metricInfo.GetValue()), nil + case pb.MetricType_METRIC_TYPE_TE: + return table.NewMetric(table.TEMetric, metricInfo.GetValue()), nil + case pb.MetricType_METRIC_TYPE_DELAY: + return table.NewMetric(table.DelayMetric, metricInfo.GetValue()), nil + case pb.MetricType_METRIC_TYPE_HOPCOUNT: + return table.NewMetric(table.HopcountMetric, metricInfo.GetValue()), nil default: return nil, errors.New("unknown metric type") } diff --git a/cmd/pola/root.go b/cmd/pola/root.go index 27d97149..b4203bd6 100644 --- a/cmd/pola/root.go +++ b/cmd/pola/root.go @@ -16,7 +16,7 @@ import ( ) var ( - client pb.PceServiceClient + client pb.PCEServiceClient jsonFmt bool ) @@ -28,7 +28,7 @@ func newRootCmd() *cobra.Command { rootCmd.PersistentFlags().String("host", "127.0.0.1", "polad connection address") rootCmd.PersistentFlags().StringP("port", "p", "50051", "polad connection port") - rootCmd.AddCommand(newSessionCmd(), newSRPolicyCmd(), newTedCmd()) + rootCmd.AddCommand(newSessionCmd(), newSRPolicyCmd(), newTEDCmd()) rootCmd.PersistentPreRunE = persistentPreRunE rootCmd.Run = runRootCmd @@ -44,7 +44,7 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error { return fmt.Errorf("failed to dial polad connection: %v", err) } - client = pb.NewPceServiceClient(conn) + client = pb.NewPCEServiceClient(conn) return nil } diff --git a/cmd/pola/sr_policy_add.go b/cmd/pola/sr_policy_add.go index 5a142b2d..40f93c6a 100644 --- a/cmd/pola/sr_policy_add.go +++ b/cmd/pola/sr_policy_add.go @@ -73,7 +73,7 @@ type Segment struct { // Unify with table.SRPolciy type SRPolicy struct { - PcepSessionAddr netip.Addr `yaml:"pcepSessionAddr"` + PCEPSessionAddr netip.Addr `yaml:"pcepSessionAddr"` SrcAddr netip.Addr `yaml:"srcAddr"` DstAddr netip.Addr `yaml:"dstAddr"` SrcRouterID string `yaml:"srcRouterID"` @@ -87,7 +87,7 @@ type SRPolicy struct { type InputFormat struct { SRPolicy SRPolicy `yaml:"srPolicy"` - Asn uint32 `yaml:"asn"` + ASN uint32 `yaml:"asn"` } func addSRPolicy(input InputFormat, jsonFlag bool, noLinkStateFlag bool) error { @@ -110,7 +110,7 @@ func addSRPolicy(input InputFormat, jsonFlag bool, noLinkStateFlag bool) error { } func addSRPolicyNoLinkState(input InputFormat) error { - if !input.SRPolicy.PcepSessionAddr.IsValid() || input.SRPolicy.Color == 0 || !input.SRPolicy.SrcAddr.IsValid() || !input.SRPolicy.DstAddr.IsValid() || len(input.SRPolicy.SegmentList) == 0 { + if !input.SRPolicy.PCEPSessionAddr.IsValid() || input.SRPolicy.Color == 0 || !input.SRPolicy.SrcAddr.IsValid() || !input.SRPolicy.DstAddr.IsValid() || len(input.SRPolicy.SegmentList) == 0 { sampleInput := "srPolicy:\n" + " pcepSessionAddr: 192.0.2.1\n" + " srcAddr: 192.0.2.1\n" + @@ -138,7 +138,7 @@ func addSRPolicyNoLinkState(input InputFormat) error { segmentList = append(segmentList, pbSeg) } srPolicy := &pb.SRPolicy{ - PcepSessionAddr: input.SRPolicy.PcepSessionAddr.AsSlice(), + PCEPSessionAddr: input.SRPolicy.PCEPSessionAddr.AsSlice(), SrcAddr: input.SRPolicy.SrcAddr.AsSlice(), DstAddr: input.SRPolicy.DstAddr.AsSlice(), SegmentList: segmentList, @@ -179,7 +179,7 @@ func addSRPolicyLinkState(input InputFormat) error { " segmentList:\n" + " - sid: 16003\n" + " - sid: 16002\n" - if input.Asn == 0 || !input.SRPolicy.PcepSessionAddr.IsValid() || input.SRPolicy.Color == 0 || input.SRPolicy.SrcRouterID == "" || input.SRPolicy.DstRouterID == "" { + if input.ASN == 0 || !input.SRPolicy.PCEPSessionAddr.IsValid() || input.SRPolicy.Color == 0 || input.SRPolicy.SrcRouterID == "" || input.SRPolicy.DstRouterID == "" { errMsg := "invalid input\n" + "input example is below\n\n" + sampleInputDynamic + @@ -228,7 +228,7 @@ func addSRPolicyLinkState(input InputFormat) error { } srPolicy := &pb.SRPolicy{ - PcepSessionAddr: input.SRPolicy.PcepSessionAddr.AsSlice(), + PCEPSessionAddr: input.SRPolicy.PCEPSessionAddr.AsSlice(), SrcRouterID: input.SRPolicy.SrcRouterID, DstRouterID: input.SRPolicy.DstRouterID, Color: input.SRPolicy.Color, @@ -239,7 +239,7 @@ func addSRPolicyLinkState(input InputFormat) error { } inputData := &pb.CreateSRPolicyInput{ SRPolicy: srPolicy, - Asn: input.Asn, + Asn: input.ASN, } if err := grpc.CreateSRPolicy(client, inputData); err != nil { return fmt.Errorf("gRPC Server Error: %s", err.Error()) diff --git a/cmd/pola/sr_policy_delete.go b/cmd/pola/sr_policy_delete.go index a0389cf2..5bf780c8 100644 --- a/cmd/pola/sr_policy_delete.go +++ b/cmd/pola/sr_policy_delete.go @@ -57,7 +57,7 @@ func newSRPolicyDeleteCmd() *cobra.Command { } func deleteSRPolicy(input InputFormat, jsonFlag bool) error { - if !input.SRPolicy.PcepSessionAddr.IsValid() || input.SRPolicy.Color == 0 || !input.SRPolicy.DstAddr.IsValid() || input.SRPolicy.Name == "" { + if !input.SRPolicy.PCEPSessionAddr.IsValid() || input.SRPolicy.Color == 0 || !input.SRPolicy.DstAddr.IsValid() || input.SRPolicy.Name == "" { sampleInput := "srPolicy:\n" + " pcepSessionAddr: 192.0.2.1\n" + " dstAddr: 192.0.2.2\n" + @@ -70,14 +70,14 @@ func deleteSRPolicy(input InputFormat, jsonFlag bool) error { } srPolicy := &pb.SRPolicy{ - PcepSessionAddr: input.SRPolicy.PcepSessionAddr.AsSlice(), + PCEPSessionAddr: input.SRPolicy.PCEPSessionAddr.AsSlice(), DstAddr: input.SRPolicy.DstAddr.AsSlice(), Color: input.SRPolicy.Color, PolicyName: input.SRPolicy.Name, } inputData := &pb.DeleteSRPolicyInput{ SRPolicy: srPolicy, - Asn: input.Asn, + Asn: input.ASN, } if err := grpc.DeleteSRPolicy(client, inputData); err != nil { return fmt.Errorf("gRPC Server Error: %s", err.Error()) diff --git a/cmd/pola/ted.go b/cmd/pola/ted.go index fa20ffaf..0136ee9d 100644 --- a/cmd/pola/ted.go +++ b/cmd/pola/ted.go @@ -13,7 +13,7 @@ import ( "github.com/spf13/cobra" ) -func newTedCmd() *cobra.Command { +func newTEDCmd() *cobra.Command { return &cobra.Command{ Use: "ted", RunE: func(cmd *cobra.Command, args []string) error { @@ -26,7 +26,7 @@ func newTedCmd() *cobra.Command { } func print(jsonFlag bool) error { - ted, err := grpc.GetTed(client) + ted, err := grpc.GetTED(client) if err != nil { return err } @@ -42,7 +42,7 @@ func print(jsonFlag bool) error { for _, as := range ted.Nodes { for _, node := range as { tmpNode := map[string]interface{}{ // TODO: Fix format according to readme - "asn": node.Asn, + "asn": node.ASN, "routerID": node.RouterID, "isisAreaID": node.IsisAreaID, "hostname": node.Hostname, diff --git a/cmd/polad/main.go b/cmd/polad/main.go index b56f6033..537d86ba 100644 --- a/cmd/polad/main.go +++ b/cmd/polad/main.go @@ -72,11 +72,11 @@ func main() { }() // Prepare TED update tools - var tedElemsChan chan []table.TedElem - if c.Global.Ted.Enable { - switch c.Global.Ted.Source { + var tedElemsChan chan []table.TEDElem + if c.Global.TED.Enable { + switch c.Global.TED.Source { case "gobgp": - tedElemsChan = startGobgpUpdate(&c, logger) + tedElemsChan = startGoBGPUpdate(&c, logger) if tedElemsChan == nil { logger.Panic("GoBGP update channel is nil") log.Panic("GoBGP update channel is nil") @@ -88,31 +88,31 @@ func main() { } // Start PCE server - o := &server.PceOptions{ - PcepAddr: c.Global.Pcep.Address, - PcepPort: c.Global.Pcep.Port, - GrpcAddr: c.Global.GrpcServer.Address, - GrpcPort: c.Global.GrpcServer.Port, - TedEnable: c.Global.Ted.Enable, + o := &server.PCEOptions{ + PCEPAddr: c.Global.PCEP.Address, + PCEPPort: c.Global.PCEP.Port, + GRPCAddr: c.Global.GRPCServer.Address, + GRPCPort: c.Global.GRPCServer.Port, + TEDEnable: c.Global.TED.Enable, USidMode: c.Global.USidMode, } - if serverErr := server.NewPce(o, logger, tedElemsChan); serverErr.Error != nil { + if serverErr := server.NewPCE(o, logger, tedElemsChan); serverErr.Error != nil { logger.Panic("Failed to start new server", zap.String("server", serverErr.Server), zap.Error(serverErr.Error)) log.Panicf("failed to start new server: %v", serverErr.Error) } } -func startGobgpUpdate(c *config.Config, logger *zap.Logger) chan []table.TedElem { - if c.Global.Ted == nil { +func startGoBGPUpdate(c *config.Config, logger *zap.Logger) chan []table.TEDElem { + if c.Global.TED == nil { logger.Error("TED does not exist") return nil } - tedElemsChan := make(chan []table.TedElem) + tedElemsChan := make(chan []table.TEDElem) go func() { for { - tedElems, err := gobgp.GetBgplsNlris(c.Global.Gobgp.GrpcClient.Address, c.Global.Gobgp.GrpcClient.Port) - logger.Debug("Request TED update", zap.String("source", "GoBGP"), zap.String("session", c.Global.Gobgp.GrpcClient.Address+":"+c.Global.Gobgp.GrpcClient.Port)) + tedElems, err := gobgp.GetBGPlsNLRIs(c.Global.GoBGP.GRPCClient.Address, c.Global.GoBGP.GRPCClient.Port) + logger.Debug("Request TED update", zap.String("source", "GoBGP"), zap.String("session", c.Global.GoBGP.GRPCClient.Address+":"+c.Global.GoBGP.GRPCClient.Port)) if err != nil { logger.Error("Failed session with GoBGP", zap.Error(err)) } else { diff --git a/examples/tinet/sr-mpls_te_l3vpn/README.md b/examples/tinet/sr-mpls_te_l3vpn/README.md index ea1c3d30..042df9d8 100644 --- a/examples/tinet/sr-mpls_te_l3vpn/README.md +++ b/examples/tinet/sr-mpls_te_l3vpn/README.md @@ -2,37 +2,43 @@ Example topology powered by [Tinet](https://github.com/tinynetwork/tinet) -![](./topo.png) +![Topology](./topo.png) ## Usage ### Install Tinet + Install Open vSwitch + ```bash -$ sudo apt update -$ sudo apt install openvswitch-switch openvswitch-common +sudo apt update +sudo apt install openvswitch-switch openvswitch-common ``` Install Tinet + ```bash -$ curl -Lo /usr/bin/tinet https://github.com/tinynetwork/tinet/releases/download/v0.0.2/tinet -$ chmod +x /usr/bin/tinet -$ tinet --version +curl -Lo /usr/bin/tinet https://github.com/tinynetwork/tinet/releases/download/v0.0.2/tinet +chmod +x /usr/bin/tinet +tinet --version ``` ### Building a Lab Network + Get spec.yaml, and start Docker network + ```bash -$ git clone https://github.com/nttcom/pola -$ cd pola/examples/tinet/sr-mpls_te_l3vpn +git clone https://github.com/nttcom/pola +cd pola/examples/tinet/sr-mpls_te_l3vpn -$ tinet upconf | sudo sh -x -$ docker ps +tinet upconf | sudo sh -x +docker ps ``` Connect to PCEP container, check PCEP session and SR policy + ```bash -$ docker exec -it pola /bin/bash +docker exec -it pola /bin/bash # pola session peerAddr(0): 10.0.255.1 @@ -40,10 +46,13 @@ peerAddr(0): 10.0.255.1 ``` ### Apply SR Policy + Create policy1.yaml (Explicit SR Policy: Segment List 16002/16004/16003 to pe01) + ```bash # vi policy1.yaml ``` + ```yaml srPolicy: name: policy1 @@ -61,13 +70,14 @@ srPolicy: ``` Apply and check SR Policy + ```bash # pola sr-policy add -f policy1.yaml --no-link-state success! # pola sr-policy list LSP(0): - PcepSessionAddr: 10.0.255.1 + PCEPSessionAddr: 10.0.255.1 PolicyName: policy1 SrcAddr: 10.0.255.1 DstAddr: 10.255.0.3 @@ -79,9 +89,10 @@ LSP(0): ``` Enter container pe01 and check SR Policy + ```bash # exit -$ docker exec -it pe01 /bin/bash +docker exec -it pe01 /bin/bash root@pe01:/# vtysh Hello, this is FRRouting (version 8.2.2). @@ -105,12 +116,14 @@ Endpoint: 10.255.0.3 Color: 1 Name: name BSID: - Status: Active ``` Add Color setting + ```bash -$ docker exec -it pe01 /bin/bash -$ vtysh -c 'conf t' -c 'router bgp 65000' -c 'address-family ipv4 vpn' -c 'neighbor 10.255.0.3 route-map color1 in' +docker exec -it pe01 /bin/bash +vtysh -c 'conf t' -c 'router bgp 65000' -c 'address-family ipv4 vpn' -c 'neighbor 10.255.0.3 route-map color1 in' ``` Check SR header with tcpdump + ```bash root@host01:/# ping 192.168.1.2 PING 192.168.1.2 (192.168.1.2) 56(84) bytes of data. diff --git a/internal/config/config.go b/internal/config/config.go index 263f118e..6e5b9474 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -12,17 +12,17 @@ import ( "gopkg.in/yaml.v3" ) -type Pcep struct { +type PCEP struct { Address string `yaml:"address"` Port string `yaml:"port"` } -type GrpcServer struct { +type GRPCServer struct { Address string `yaml:"address"` Port string `yaml:"port"` } -type GrpcClient struct { +type GRPCClient struct { Address string `yaml:"address"` Port string `yaml:"port"` } @@ -33,21 +33,21 @@ type Log struct { Debug bool `yaml:"debug"` } -type Gobgp struct { - GrpcClient GrpcClient `yaml:"grpc-client"` +type GoBGP struct { + GRPCClient GRPCClient `yaml:"grpc-client"` } -type Ted struct { +type TED struct { Enable bool `yaml:"enable"` Source string `yaml:"source"` } type Global struct { - Pcep Pcep `yaml:"pcep"` - GrpcServer GrpcServer `yaml:"grpc-server"` + PCEP PCEP `yaml:"pcep"` + GRPCServer GRPCServer `yaml:"grpc-server"` Log Log `yaml:"log"` - Ted *Ted `yaml:"ted"` - Gobgp Gobgp `yaml:"gobgp"` + TED *TED `yaml:"ted"` + GoBGP GoBGP `yaml:"gobgp"` USidMode bool `yaml:"usid-mode"` } diff --git a/internal/pkg/cspf/cspf.go b/internal/pkg/cspf/cspf.go index 3709a038..88ac1d4d 100644 --- a/internal/pkg/cspf/cspf.go +++ b/internal/pkg/cspf/cspf.go @@ -27,7 +27,7 @@ func newNode(id string, cost uint32, nodeSeg table.Segment) *node { } } -func Cspf(srcRouterID string, dstRouterID string, as uint32, metric table.MetricType, ted *table.LsTed) ([]table.Segment, error) { +func Cspf(srcRouterID string, dstRouterID string, as uint32, metric table.MetricType, ted *table.LsTED) ([]table.Segment, error) { network := ted.Nodes[as] // TODO: update network information according to constraints segmentList, err := spf(srcRouterID, dstRouterID, metric, network) diff --git a/internal/pkg/gobgp/interface.go b/internal/pkg/gobgp/interface.go index f2acf3d0..d190020c 100644 --- a/internal/pkg/gobgp/interface.go +++ b/internal/pkg/gobgp/interface.go @@ -22,7 +22,7 @@ import ( "google.golang.org/protobuf/types/known/anypb" ) -func GetBgplsNlris(serverAddr string, serverPort string) ([]table.TedElem, error) { +func GetBGPlsNLRIs(serverAddr string, serverPort string) ([]table.TEDElem, error) { gobgpAddress := fmt.Sprintf("%s:%s", serverAddr, serverPort) // Establish gRPC connection @@ -59,7 +59,7 @@ func GetBgplsNlris(serverAddr string, serverPort string) ([]table.TedElem, error return nil, fmt.Errorf("failed to retrieve paths from gRPC server: %w", err) } - var tedElems []table.TedElem + var tedElems []table.TEDElem for { r, err := stream.Recv() if err != nil { @@ -69,7 +69,7 @@ func GetBgplsNlris(serverAddr string, serverPort string) ([]table.TedElem, error return nil, fmt.Errorf("error receiving stream data: %w", err) } - convertedElems, err := ConvertToTedElem(r.Destination) + convertedElems, err := ConvertToTEDElem(r.Destination) if err != nil { return nil, fmt.Errorf("failed to convert path to TED element (destination: %v): %w", r.Destination, err) } @@ -80,7 +80,7 @@ func GetBgplsNlris(serverAddr string, serverPort string) ([]table.TedElem, error return tedElems, nil } -func ConvertToTedElem(dst *api.Destination) ([]table.TedElem, error) { +func ConvertToTEDElem(dst *api.Destination) ([]table.TEDElem, error) { if len(dst.GetPaths()) != 1 { return nil, errors.New("invalid path length: expected 1 path") } @@ -93,24 +93,24 @@ func ConvertToTedElem(dst *api.Destination) ([]table.TedElem, error) { switch nlri := nlri.(type) { case *api.LsAddrPrefix: - linkStateNlri, err := nlri.GetNlri().UnmarshalNew() + linkStateNLRI, err := nlri.GetNlri().UnmarshalNew() if err != nil { return nil, fmt.Errorf("failed to unmarshal LS Address Prefix: %w", err) } - switch linkStateNlri := linkStateNlri.(type) { + switch linkStateNLRI := linkStateNLRI.(type) { case *api.LsNodeNLRI: - lsNode, err := getLsNodeNLRI(linkStateNlri, path.GetPattrs()) + lsNode, err := getLsNodeNLRI(linkStateNLRI, path.GetPattrs()) if err != nil { return nil, fmt.Errorf("failed to process LS Node NLRI: %w", err) } - return []table.TedElem{lsNode}, nil + return []table.TEDElem{lsNode}, nil case *api.LsLinkNLRI: - lsLink, err := getLsLinkNLRI(linkStateNlri, path.GetPattrs()) + lsLink, err := getLsLinkNLRI(linkStateNLRI, path.GetPattrs()) if err != nil { return nil, fmt.Errorf("failed to process LS Link NLRI: %w", err) } - return []table.TedElem{lsLink}, nil + return []table.TEDElem{lsLink}, nil case *api.LsPrefixV4NLRI: lsPrefixV4List, err := getLsPrefixV4List(path.GetPattrs()) if err != nil { @@ -126,7 +126,7 @@ func ConvertToTedElem(dst *api.Destination) ([]table.TedElem, error) { case *api.LsPrefixV6NLRI: return nil, nil // TODO: Implement LsPrefixV6NLRI handling default: - return nil, fmt.Errorf("invalid LS Link State NLRI type: %T", linkStateNlri) + return nil, fmt.Errorf("invalid LS Link State NLRI type: %T", linkStateNLRI) } default: return nil, fmt.Errorf("invalid NLRI type: %T", nlri) @@ -146,9 +146,9 @@ func formatIsisAreaID(isisArea []byte) string { return strIsisArea.String() } -func getLsNodeNLRI(typedLinkStateNlri *api.LsNodeNLRI, pathAttrs []*anypb.Any) (*table.LsNode, error) { - asn := typedLinkStateNlri.GetLocalNode().GetAsn() - routerID := typedLinkStateNlri.GetLocalNode().GetIgpRouterId() +func getLsNodeNLRI(typedLinkStateNLRI *api.LsNodeNLRI, pathAttrs []*anypb.Any) (*table.LsNode, error) { + asn := typedLinkStateNLRI.GetLocalNode().GetAsn() + routerID := typedLinkStateNLRI.GetLocalNode().GetIgpRouterId() lsNode := table.NewLsNode(asn, routerID) @@ -177,18 +177,18 @@ func getLsNodeNLRI(typedLinkStateNlri *api.LsNodeNLRI, pathAttrs []*anypb.Any) ( return lsNode, nil } -func getLsLinkNLRI(typedLinkStateNlri *api.LsLinkNLRI, pathAttrs []*anypb.Any) (*table.LsLink, error) { - localNode := table.NewLsNode(typedLinkStateNlri.GetLocalNode().GetAsn(), typedLinkStateNlri.GetLocalNode().GetIgpRouterId()) - remoteNode := table.NewLsNode(typedLinkStateNlri.GetRemoteNode().GetAsn(), typedLinkStateNlri.GetRemoteNode().GetIgpRouterId()) +func getLsLinkNLRI(typedLinkStateNLRI *api.LsLinkNLRI, pathAttrs []*anypb.Any) (*table.LsLink, error) { + localNode := table.NewLsNode(typedLinkStateNLRI.GetLocalNode().GetAsn(), typedLinkStateNLRI.GetLocalNode().GetIgpRouterId()) + remoteNode := table.NewLsNode(typedLinkStateNLRI.GetRemoteNode().GetAsn(), typedLinkStateNLRI.GetRemoteNode().GetIgpRouterId()) - localIP, err := netip.ParseAddr(typedLinkStateNlri.GetLinkDescriptor().GetInterfaceAddrIpv4()) + localIP, err := netip.ParseAddr(typedLinkStateNLRI.GetLinkDescriptor().GetInterfaceAddrIpv4()) if err != nil { - return nil, fmt.Errorf("failed to parse local IP address %q: %v", typedLinkStateNlri.GetLinkDescriptor().GetInterfaceAddrIpv4(), err) + return nil, fmt.Errorf("failed to parse local IP address %q: %v", typedLinkStateNLRI.GetLinkDescriptor().GetInterfaceAddrIpv4(), err) } - remoteIP, err := netip.ParseAddr(typedLinkStateNlri.GetLinkDescriptor().GetNeighborAddrIpv4()) + remoteIP, err := netip.ParseAddr(typedLinkStateNLRI.GetLinkDescriptor().GetNeighborAddrIpv4()) if err != nil { - return nil, fmt.Errorf("failed to parse remote IP address %q: %v", typedLinkStateNlri.GetLinkDescriptor().GetNeighborAddrIpv4(), err) + return nil, fmt.Errorf("failed to parse remote IP address %q: %v", typedLinkStateNLRI.GetLinkDescriptor().GetNeighborAddrIpv4(), err) } lsLink := table.NewLsLink(localNode, remoteNode) @@ -206,11 +206,11 @@ func getLsLinkNLRI(typedLinkStateNlri *api.LsLinkNLRI, pathAttrs []*anypb.Any) ( continue } - lsLink.Metrics = append(lsLink.Metrics, table.NewMetric(table.MetricType(table.IGP_METRIC), bgplsAttr.GetLink().GetIgpMetric())) + lsLink.Metrics = append(lsLink.Metrics, table.NewMetric(table.MetricType(table.IGPMetric), bgplsAttr.GetLink().GetIgpMetric())) teMetric := bgplsAttr.GetLink().GetDefaultTeMetric() if teMetric != 0 { - lsLink.Metrics = append(lsLink.Metrics, table.NewMetric(table.MetricType(table.TE_METRIC), teMetric)) + lsLink.Metrics = append(lsLink.Metrics, table.NewMetric(table.MetricType(table.TEMetric), teMetric)) } lsLink.AdjSid = bgplsAttr.GetLink().GetSrAdjacencySid() @@ -219,8 +219,8 @@ func getLsLinkNLRI(typedLinkStateNlri *api.LsLinkNLRI, pathAttrs []*anypb.Any) ( return lsLink, nil } -func getLsPrefixV4List(pathAttrs []*anypb.Any) ([]table.TedElem, error) { - var lsPrefixV4List []table.TedElem +func getLsPrefixV4List(pathAttrs []*anypb.Any) ([]table.TEDElem, error) { + var lsPrefixV4List []table.TEDElem var sidIndex uint32 for _, pathAttr := range pathAttrs { @@ -235,19 +235,19 @@ func getLsPrefixV4List(pathAttrs []*anypb.Any) ([]table.TedElem, error) { case *api.MpReachNLRIAttribute: for _, nlri := range typedPathAttr.GetNlris() { - typedNlri, err := nlri.UnmarshalNew() + typedNLRI, err := nlri.UnmarshalNew() if err != nil { return nil, fmt.Errorf("failed to unmarshal NLRI: %w", err) } - if lsNlri, ok := typedNlri.(*api.LsAddrPrefix); ok { - lsPrefixV4, err := getLsPrefixV4(lsNlri, sidIndex) + if lsNLRI, ok := typedNLRI.(*api.LsAddrPrefix); ok { + lsPrefixV4, err := getLsPrefixV4(lsNLRI, sidIndex) if err != nil { return nil, fmt.Errorf("failed to get LS Prefix V4: %w", err) } lsPrefixV4List = append(lsPrefixV4List, lsPrefixV4) } else { - return nil, fmt.Errorf("unexpected NLRI type: %T", typedNlri) + return nil, fmt.Errorf("unexpected NLRI type: %T", typedNLRI) } } } @@ -256,21 +256,21 @@ func getLsPrefixV4List(pathAttrs []*anypb.Any) ([]table.TedElem, error) { return lsPrefixV4List, nil } -func getLsPrefixV4(lsNlri *api.LsAddrPrefix, sidIndex uint32) (*table.LsPrefixV4, error) { - prefNlri, err := lsNlri.GetNlri().UnmarshalNew() +func getLsPrefixV4(lsNLRI *api.LsAddrPrefix, sidIndex uint32) (*table.LsPrefixV4, error) { + prefixNLRI, err := lsNLRI.GetNlri().UnmarshalNew() if err != nil { return nil, fmt.Errorf("failed to unmarshal LS Prefix V4: %w", err) } - prefv4Nlri, ok := prefNlri.(*api.LsPrefixV4NLRI) + prefixv4NLRI, ok := prefixNLRI.(*api.LsPrefixV4NLRI) if !ok { - return nil, fmt.Errorf("invalid LS prefix v4 NLRI type: %T", prefNlri) + return nil, fmt.Errorf("invalid LS prefix v4 NLRI type: %T", prefixNLRI) } - localNodeID := prefv4Nlri.GetLocalNode().GetIgpRouterId() - localNodeAsn := prefv4Nlri.GetLocalNode().GetAsn() - prefixV4 := prefv4Nlri.GetPrefixDescriptor().GetIpReachability() + localNodeID := prefixv4NLRI.GetLocalNode().GetIgpRouterId() + localNodeASN := prefixv4NLRI.GetLocalNode().GetAsn() + prefixV4 := prefixv4NLRI.GetPrefixDescriptor().GetIpReachability() - localNode := table.NewLsNode(localNodeAsn, localNodeID) + localNode := table.NewLsNode(localNodeASN, localNodeID) lsPrefixV4 := table.NewLsPrefixV4(localNode) lsPrefixV4.SidIndex = sidIndex @@ -286,8 +286,8 @@ func getLsPrefixV4(lsNlri *api.LsAddrPrefix, sidIndex uint32) (*table.LsPrefixV4 return lsPrefixV4, nil } -func getLsSrv6SIDNLRIList(pathAttrs []*anypb.Any) ([]table.TedElem, error) { - var lsSrv6SIDList []table.TedElem +func getLsSrv6SIDNLRIList(pathAttrs []*anypb.Any) ([]table.TEDElem, error) { + var lsSrv6SIDList []table.TEDElem var endpointBehavior uint32 for _, pathAttr := range pathAttrs { @@ -301,18 +301,18 @@ func getLsSrv6SIDNLRIList(pathAttrs []*anypb.Any) ([]table.TedElem, error) { endpointBehavior = uint32(typedPathAttr.GetBehavior()) case *api.MpReachNLRIAttribute: for _, nlri := range typedPathAttr.GetNlris() { - typedNlri, err := nlri.UnmarshalNew() + typedNLRI, err := nlri.UnmarshalNew() if err != nil { return nil, fmt.Errorf("failed to unmarshal NLRI: %w", err) } - if lsNlri, ok := typedNlri.(*api.LsAddrPrefix); ok { - lsSrv6SID, err := getLsSrv6SIDNLRI(lsNlri, endpointBehavior) + if lsNLRI, ok := typedNLRI.(*api.LsAddrPrefix); ok { + lsSrv6SID, err := getLsSrv6SIDNLRI(lsNLRI, endpointBehavior) if err != nil { return nil, fmt.Errorf("failed to process LS SRv6 SID NLRI: %w", err) } lsSrv6SIDList = append(lsSrv6SIDList, lsSrv6SID) } else { - return nil, fmt.Errorf("unexpected NLRI type: %T", typedNlri) + return nil, fmt.Errorf("unexpected NLRI type: %T", typedNLRI) } } } @@ -320,21 +320,23 @@ func getLsSrv6SIDNLRIList(pathAttrs []*anypb.Any) ([]table.TedElem, error) { return lsSrv6SIDList, nil } -func getLsSrv6SIDNLRI(lsNlri *api.LsAddrPrefix, endpointBehavior uint32) (*table.LsSrv6SID, error) { - srv6Nlri, err := lsNlri.GetNlri().UnmarshalNew() +// getLsSrv6SIDNLRI processes the LS SRv6 SID NLRI and returns a corresponding LsSrv6SID. +func getLsSrv6SIDNLRI(lsNLRI *api.LsAddrPrefix, endpointBehavior uint32) (*table.LsSrv6SID, error) { + srv6NLRI, err := lsNLRI.GetNlri().UnmarshalNew() if err != nil { return nil, fmt.Errorf("failed to unmarshal LS NLRI: %w", err) } - srv6SIDNlri, ok := srv6Nlri.(*api.LsSrv6SIDNLRI) + srv6SIDNLRI, ok := srv6NLRI.(*api.LsSrv6SIDNLRI) if !ok { - return nil, fmt.Errorf("invalid LS SRv6 SID NLRI type: %T", srv6Nlri) + return nil, fmt.Errorf("invalid LS SRv6 SID NLRI type: %T", srv6NLRI) } - localNodeID := srv6SIDNlri.GetLocalNode().GetIgpRouterId() - localNodeAsn := srv6SIDNlri.GetLocalNode().GetAsn() - srv6SIDs := srv6SIDNlri.GetSrv6SidInformation().GetSids() - multiTopoIDs := srv6SIDNlri.GetMultiTopoId().GetMultiTopoIds() - localNode := table.NewLsNode(localNodeAsn, localNodeID) + localNodeID := srv6SIDNLRI.GetLocalNode().GetIgpRouterId() + localNodeASN := srv6SIDNLRI.GetLocalNode().GetAsn() + srv6SIDs := srv6SIDNLRI.GetSrv6SidInformation().GetSids() + multiTopoIDs := srv6SIDNLRI.GetMultiTopoId().GetMultiTopoIds() + + localNode := table.NewLsNode(localNodeASN, localNodeID) lsSrv6SID := table.NewLsSrv6SID(localNode) lsSrv6SID.EndpointBehavior = endpointBehavior lsSrv6SID.Sids = srv6SIDs diff --git a/internal/pkg/table/sr_policy.go b/internal/pkg/table/sr_policy.go index 17f7898f..6c834e45 100644 --- a/internal/pkg/table/sr_policy.go +++ b/internal/pkg/table/sr_policy.go @@ -15,10 +15,10 @@ import ( type PolicyState string const ( - POLICY_DOWN = PolicyState("down") - POLICY_UP = PolicyState("up") - POLICY_ACTIVE = PolicyState("active") - POLICY_UNKNOWN = PolicyState("unknown") + PolicyDown = PolicyState("down") + PolicyUp = PolicyState("up") + PolicyActive = PolicyState("active") + PolicyUnknown = PolicyState("unknown") ) type SRPolicy struct { @@ -34,7 +34,7 @@ type SRPolicy struct { } func NewSRPolicy( - plspId uint32, + plspID uint32, name string, segmentList []Segment, srcAddr netip.Addr, @@ -45,7 +45,7 @@ func NewSRPolicy( state PolicyState, ) *SRPolicy { p := &SRPolicy{ - PlspID: plspId, + PlspID: plspID, Name: name, SegmentList: segmentList, SrcAddr: srcAddr, @@ -86,7 +86,7 @@ func (p *SRPolicy) Update(df PolicyDiff) { } } -const SRV6_SID_BIT_LENGTH = 128 +const SRv6SIDBitLength = 128 type Segment interface { SidString() string @@ -107,11 +107,11 @@ func NewSegment(sid string) (Segment, error) { } const ( - BEHAVIOR_RESERVED uint16 = 0x0000 - BEHAVIOR_END uint16 = 0x0001 - BEHAVIOR_END_X uint16 = 0x0005 - BEHAVIOR_UN uint16 = 0x0030 - BEHAVIOR_UA uint16 = 0x0039 + BehaviorReserved uint16 = 0x0000 + BehaviorEND uint16 = 0x0001 + BehaviorENDX uint16 = 0x0005 + BehaviorUN uint16 = 0x0030 + BehaviorUA uint16 = 0x0039 ) type SegmentSRv6 struct { @@ -127,23 +127,19 @@ func (seg SegmentSRv6) SidString() string { } func (seg SegmentSRv6) Behavior() uint16 { - if seg.LocalAddr.IsValid() { - if seg.USid { - if seg.RemoteAddr.IsValid() { - return BEHAVIOR_UA - } else { - return BEHAVIOR_UN - } - } else { - if seg.RemoteAddr.IsValid() { - return BEHAVIOR_END_X - } else { - return BEHAVIOR_END - } + if !seg.LocalAddr.IsValid() { + return BehaviorReserved + } + if seg.USid { + if seg.RemoteAddr.IsValid() { + return BehaviorUA } - } else { - return BEHAVIOR_RESERVED + return BehaviorUN + } + if seg.RemoteAddr.IsValid() { + return BehaviorENDX } + return BehaviorEND } func NewSegmentSRv6(sid netip.Addr) SegmentSRv6 { diff --git a/internal/pkg/table/ted.go b/internal/pkg/table/ted.go index c84eee0f..51bd705f 100644 --- a/internal/pkg/table/ted.go +++ b/internal/pkg/table/ted.go @@ -12,18 +12,18 @@ import ( "strconv" ) -type LsTed struct { +type LsTED struct { ID int Nodes map[uint32]map[string]*LsNode // { ASN1: {"NodeID1": node1, "NodeID2": node2}, ASN2: {"NodeID3": node3, "NodeID4": node4}} } -func (ted *LsTed) Update(tedElems []TedElem) { +func (ted *LsTED) Update(tedElems []TEDElem) { for _, tedElem := range tedElems { - tedElem.UpdateTed(ted) + tedElem.UpdateTED(ted) } } -func (ted *LsTed) Print() { +func (ted *LsTED) Print() { for _, nodes := range ted.Nodes { nodeCnt := 1 for nodeID, node := range nodes { @@ -62,12 +62,12 @@ func (ted *LsTed) Print() { } } -type TedElem interface { - UpdateTed(ted *LsTed) +type TEDElem interface { + UpdateTED(ted *LsTED) } type LsNode struct { - Asn uint32 // primary key, in MP_REACH_NLRI Attr + ASN uint32 // primary key, in MP_REACH_NLRI Attr RouterID string // primary key, in MP_REACH_NLRI Attr IsisAreaID string // in BGP-LS Attr Hostname string // in BGP-LS Attr @@ -80,7 +80,7 @@ type LsNode struct { func NewLsNode(asn uint32, nodeID string) *LsNode { return &LsNode{ - Asn: asn, + ASN: asn, RouterID: nodeID, } } @@ -112,8 +112,8 @@ func (n *LsNode) LoopbackAddr() (netip.Addr, error) { return netip.Addr{}, errors.New("node doesn't have a loopback address") } -func (n *LsNode) UpdateTed(ted *LsTed) { - nodes, asn := ted.Nodes, n.Asn +func (n *LsNode) UpdateTED(ted *LsTED) { + nodes, asn := ted.Nodes, n.ASN if _, ok := nodes[asn]; !ok { nodes[asn] = make(map[string]*LsNode) @@ -159,22 +159,22 @@ func (l *LsLink) Metric(metricType MetricType) (uint32, error) { return 0, fmt.Errorf("metric %s not defined", metricType) } -func (l *LsLink) UpdateTed(ted *LsTed) { - nodes, asn := ted.Nodes, l.LocalNode.Asn +func (l *LsLink) UpdateTED(ted *LsTED) { + nodes, asn := ted.Nodes, l.LocalNode.ASN if _, ok := nodes[asn]; !ok { nodes[asn] = make(map[string]*LsNode) } if _, ok := nodes[asn][l.LocalNode.RouterID]; !ok { - nodes[asn][l.LocalNode.RouterID] = NewLsNode(l.LocalNode.Asn, l.LocalNode.RouterID) + nodes[asn][l.LocalNode.RouterID] = NewLsNode(l.LocalNode.ASN, l.LocalNode.RouterID) } - if _, ok := nodes[l.RemoteNode.Asn][l.RemoteNode.RouterID]; !ok { - nodes[l.RemoteNode.Asn][l.RemoteNode.RouterID] = NewLsNode(l.RemoteNode.Asn, l.RemoteNode.RouterID) + if _, ok := nodes[l.RemoteNode.ASN][l.RemoteNode.RouterID]; !ok { + nodes[l.RemoteNode.ASN][l.RemoteNode.RouterID] = NewLsNode(l.RemoteNode.ASN, l.RemoteNode.RouterID) } - l.LocalNode, l.RemoteNode = nodes[asn][l.LocalNode.RouterID], nodes[l.RemoteNode.Asn][l.RemoteNode.RouterID] + l.LocalNode, l.RemoteNode = nodes[asn][l.LocalNode.RouterID], nodes[l.RemoteNode.ASN][l.RemoteNode.RouterID] l.LocalNode.AddLink(l) } @@ -191,15 +191,15 @@ func NewLsPrefixV4(localNode *LsNode) *LsPrefixV4 { } } -func (lp *LsPrefixV4) UpdateTed(ted *LsTed) { - nodes, asn := ted.Nodes, lp.LocalNode.Asn +func (lp *LsPrefixV4) UpdateTED(ted *LsTED) { + nodes, asn := ted.Nodes, lp.LocalNode.ASN if _, ok := nodes[asn]; !ok { nodes[asn] = make(map[string]*LsNode) } if _, ok := nodes[asn][lp.LocalNode.RouterID]; !ok { - nodes[asn][lp.LocalNode.RouterID] = NewLsNode(lp.LocalNode.Asn, lp.LocalNode.RouterID) + nodes[asn][lp.LocalNode.RouterID] = NewLsNode(lp.LocalNode.ASN, lp.LocalNode.RouterID) } localNode := nodes[asn][lp.LocalNode.RouterID] @@ -225,15 +225,15 @@ func NewLsSrv6SID(node *LsNode) *LsSrv6SID { } } -func (s *LsSrv6SID) UpdateTed(ted *LsTed) { - nodes, asn := ted.Nodes, s.LocalNode.Asn +func (s *LsSrv6SID) UpdateTED(ted *LsTED) { + nodes, asn := ted.Nodes, s.LocalNode.ASN if _, ok := nodes[asn]; !ok { nodes[asn] = make(map[string]*LsNode) } if _, ok := nodes[asn][s.LocalNode.RouterID]; !ok { - nodes[asn][s.LocalNode.RouterID] = NewLsNode(s.LocalNode.Asn, s.LocalNode.RouterID) + nodes[asn][s.LocalNode.RouterID] = NewLsNode(s.LocalNode.ASN, s.LocalNode.RouterID) } s.LocalNode = nodes[asn][s.LocalNode.RouterID] @@ -260,21 +260,21 @@ func NewMetric(metricType MetricType, value uint32) *Metric { type MetricType int const ( - IGP_METRIC MetricType = iota - TE_METRIC - DELAY_METRIC - HOPCOUNT_METRIC + IGPMetric MetricType = iota + TEMetric + DelayMetric + HopcountMetric ) func (m MetricType) String() string { switch m { - case IGP_METRIC: + case IGPMetric: return "IGP" - case TE_METRIC: + case TEMetric: return "TE" - case DELAY_METRIC: + case DelayMetric: return "DELAY" - case HOPCOUNT_METRIC: + case HopcountMetric: return "HOPCOUNT" default: return "Unknown" diff --git a/pkg/packet/pcep/capability.go b/pkg/packet/pcep/capability.go index b948021d..3bdc89b2 100644 --- a/pkg/packet/pcep/capability.go +++ b/pkg/packet/pcep/capability.go @@ -14,8 +14,8 @@ func PolaCapability(caps []CapabilityInterface) []CapabilityInterface { polaCaps := []CapabilityInterface{} for _, cap := range caps { switch tlv := cap.(type) { - case *StatefulPceCapability: - tlv = &StatefulPceCapability{ + case *StatefulPCECapability: + tlv = &StatefulPCECapability{ LSPUpdateCapability: true, IncludeDBVersion: false, LSPInstantiationCapability: true, diff --git a/pkg/packet/pcep/message.go b/pkg/packet/pcep/message.go index c81888c0..2d3bd77d 100644 --- a/pkg/packet/pcep/message.go +++ b/pkg/packet/pcep/message.go @@ -175,7 +175,7 @@ func NewKeepaliveMessage() (*KeepaliveMessage, error) { // PCErr Message type PCErrMessage struct { - PcepErrorObject *PcepErrorObject + PCEPErrorObject *PCEPErrorObject } func (m *PCErrMessage) DecodeFromBytes(messageBody []uint8) error { @@ -183,30 +183,30 @@ func (m *PCErrMessage) DecodeFromBytes(messageBody []uint8) error { if err := commonObjectHeader.DecodeFromBytes(messageBody); err != nil { return err } - pcepErrorObject := &PcepErrorObject{} + pcepErrorObject := &PCEPErrorObject{} if err := pcepErrorObject.DecodeFromBytes(commonObjectHeader.ObjectType, messageBody[commonObjectHeaderLength:commonObjectHeader.ObjectLength]); err != nil { return err } - m.PcepErrorObject = pcepErrorObject + m.PCEPErrorObject = pcepErrorObject return nil } func (m *PCErrMessage) Serialize() []uint8 { - pcerrMessageLength := CommonHeaderLength + m.PcepErrorObject.Len() + pcerrMessageLength := CommonHeaderLength + m.PCEPErrorObject.Len() pcerrHeader := NewCommonHeader(MessageTypeError, pcerrMessageLength) bytePCErrHeader := pcerrHeader.Serialize() - bytePcepErrorObject := m.PcepErrorObject.Serialize() - bytePCErrMessage := AppendByteSlices(bytePCErrHeader, bytePcepErrorObject) + bytePCEPErrorObject := m.PCEPErrorObject.Serialize() + bytePCErrMessage := AppendByteSlices(bytePCErrHeader, bytePCEPErrorObject) return bytePCErrMessage } func NewPCErrMessage(errorType uint8, errorValue uint8, tlvs []TLVInterface) (*PCErrMessage, error) { - o, err := NewPcepErrorObject(errorType, errorValue, tlvs) + o, err := NewPCEPErrorObject(errorType, errorValue, tlvs) if err != nil { return nil, err } m := &PCErrMessage{ - PcepErrorObject: o, + PCEPErrorObject: o, } return m, nil } @@ -471,6 +471,7 @@ func NewPCInitiateMessage(srpID uint32, lspName string, lspDelete bool, plspID u m := &PCInitiateMessage{} var err error + if m.SrpObject, err = NewSrpObject(segmentList, srpID, lspDelete); err != nil { return nil, err } @@ -480,12 +481,11 @@ func NewPCInitiateMessage(srpID uint32, lspName string, lspDelete bool, plspID u return nil, err } return m, nil - } else { - if m.LSPObject, err = NewLSPObject(lspName, &color, 0); err != nil { - return nil, err - } } + if m.LSPObject, err = NewLSPObject(lspName, &color, 0); err != nil { + return nil, err + } if m.EndpointsObject, err = NewEndpointsObject(dstAddr, srcAddr); err != nil { return nil, err } diff --git a/pkg/packet/pcep/object.go b/pkg/packet/pcep/object.go index 485c1081..a3eeae84 100644 --- a/pkg/packet/pcep/object.go +++ b/pkg/packet/pcep/object.go @@ -414,14 +414,14 @@ const ( ObjectTypeErrorError ObjectType = 0x01 ) -type PcepErrorObject struct { +type PCEPErrorObject struct { ObjectType ObjectType ErrorType uint8 ErrorValue uint8 Tlvs []TLVInterface } -func (o *PcepErrorObject) DecodeFromBytes(typ ObjectType, objectBody []uint8) error { +func (o *PCEPErrorObject) DecodeFromBytes(typ ObjectType, objectBody []uint8) error { o.ObjectType = typ o.ErrorType = objectBody[2] o.ErrorValue = objectBody[3] @@ -435,19 +435,19 @@ func (o *PcepErrorObject) DecodeFromBytes(typ ObjectType, objectBody []uint8) er return nil } -func (o *PcepErrorObject) Serialize() []uint8 { +func (o *PCEPErrorObject) Serialize() []uint8 { pcepErrorObjectHeader := NewCommonObjectHeader(ObjectClassPCEPError, o.ObjectType, o.Len()) - bytePcepErrorObjectHeader := pcepErrorObjectHeader.Serialize() + bytePCEPErrorObjectHeader := pcepErrorObjectHeader.Serialize() buf := make([]uint8, 4) buf[2] = o.ErrorType buf[3] = o.ErrorValue - bytePcepErrorObject := AppendByteSlices(bytePcepErrorObjectHeader, buf) - return bytePcepErrorObject + bytePCEPErrorObject := AppendByteSlices(bytePCEPErrorObjectHeader, buf) + return bytePCEPErrorObject } -func (o *PcepErrorObject) Len() uint16 { +func (o *PCEPErrorObject) Len() uint16 { tlvsByteLength := uint16(0) for _, tlv := range o.Tlvs { tlvsByteLength += tlv.Len() @@ -456,8 +456,8 @@ func (o *PcepErrorObject) Len() uint16 { return commonObjectHeaderLength + 4 + tlvsByteLength } -func NewPcepErrorObject(errorType uint8, errorValue uint8, tlvs []TLVInterface) (*PcepErrorObject, error) { - o := &PcepErrorObject{ +func NewPCEPErrorObject(errorType uint8, errorValue uint8, tlvs []TLVInterface) (*PCEPErrorObject, error) { + o := &PCEPErrorObject{ ObjectType: ObjectTypeErrorError, ErrorType: errorType, ErrorValue: errorValue, @@ -600,7 +600,6 @@ func NewSrpObject(segs []table.Segment, srpID uint32, isRemove bool) (*SrpObject } if _, ok := segs[0].(table.SegmentSRMPLS); ok { o.TLVs = append(o.TLVs, &PathSetupType{PathSetupType: PathSetupTypeSRTE}) - } else if _, ok := segs[0].(table.SegmentSRv6); ok { o.TLVs = append(o.TLVs, &PathSetupType{PathSetupType: PathSetupTypeSRv6TE}) } else { return nil, errors.New("invalid Segment type") @@ -767,7 +766,7 @@ func (o *EroObject) DecodeFromBytes(typ ObjectType, objectBody []uint8) error { switch SubObjectType(objectBody[0] & 0x7f) { case SubObjectTypeEROSR: eroSubobj = &SREroSubobject{} - case OT_ERO_SRV6: + case SubObjectTypeEROSRv6: eroSubobj = &SRv6EroSubobject{} default: return errors.New("invalid Subobject type") @@ -1020,7 +1019,7 @@ func (o *SREroSubobject) ToSegment() table.Segment { // SRv6-ERO Subobject (RFC9603 4.3.1) const ( - OT_ERO_SRV6 SubObjectType = 0x28 + SubObjectTypeEROSRv6 SubObjectType = 0x28 ) type NAITypeSRv6 uint8 @@ -1167,7 +1166,7 @@ func (o *SRv6EroSubobject) Len() (uint16, error) { func NewSRv6EroSubObject(seg table.SegmentSRv6) (*SRv6EroSubobject, error) { subo := &SRv6EroSubobject{ LFlag: false, - SubobjectType: OT_ERO_SRV6, + SubobjectType: SubObjectTypeEROSRv6, VFlag: false, SFlag: false, // SID is absent Segment: seg, diff --git a/pkg/packet/pcep/tlv.go b/pkg/packet/pcep/tlv.go index 6673c98a..8016d417 100644 --- a/pkg/packet/pcep/tlv.go +++ b/pkg/packet/pcep/tlv.go @@ -32,10 +32,10 @@ const ( // PCEP TLV TLVWavelengthAllocation TLVType = 0x0a TLVOpticalInterfaceClassList TLVType = 0x0b TLVClientSignalInformation TLVType = 0x0c - TLVHPceCapability TLVType = 0x0d + TLVHPCECapability TLVType = 0x0d TLVDomainID TLVType = 0x0e - TLVHPceFlag TLVType = 0x0f - TLVStatefulPceCapability TLVType = 0x10 + TLVHPCEFlag TLVType = 0x0f + TLVStatefulPCECapability TLVType = 0x10 TLVSymbolicPathName TLVType = 0x11 TLVIPv4LSPIdentifiers TLVType = 0x12 TLVIPv6LSPIdentifiers TLVType = 0x13 @@ -43,7 +43,7 @@ const ( // PCEP TLV TLVRsvpErrorSpec TLVType = 0x15 TLVLSPDBVersion TLVType = 0x17 TLVSpeakerEntityID TLVType = 0x18 - TLVSRPceCapability TLVType = 0x1a + TLVSRPCECapability TLVType = 0x1a TLVPathSetupType TLVType = 0x1c TLVOperatorConfiguredAssociationRange TLVType = 0x1d TLVGlobalAssociationSource TLVType = 0x1e @@ -67,7 +67,7 @@ const ( // PCEP TLV TLVPolicyParameters TLVType = 0x30 TLVSchedLSPAttribute TLVType = 0x31 TLVSchedPdLSPAttribute TLVType = 0x32 - TLVPceFlowspecCapability TLVType = 0x33 + TLVPCEFlowspecCapability TLVType = 0x33 TLVFlowFilter TLVType = 0x34 TLVBidirectionalLSPAssociationGroup TLVType = 0x36 TLVTePathBinding TLVType = 0x37 @@ -109,10 +109,10 @@ var tlvDescriptions = map[TLVType]struct { TLVWavelengthAllocation: {"WAVELENGTH-ALLOCATION", "RFC8780"}, TLVOpticalInterfaceClassList: {"OPTICAL-INTERFACE-CLASS-LIST", "RFC8780"}, TLVClientSignalInformation: {"CLIENT-SIGNAL-INFORMATION", "RFC8780"}, - TLVHPceCapability: {"H-PCE-CAPABILITY", "RFC8685"}, + TLVHPCECapability: {"H-PCE-CAPABILITY", "RFC8685"}, TLVDomainID: {"DOMAIN-ID", "RFC8685"}, - TLVHPceFlag: {"H-PCE-FLAG", "RFC8685"}, - TLVStatefulPceCapability: {"STATEFUL-PCE-CAPABILITY", "RFC8231"}, + TLVHPCEFlag: {"H-PCE-FLAG", "RFC8685"}, + TLVStatefulPCECapability: {"STATEFUL-PCE-CAPABILITY", "RFC8231"}, TLVSymbolicPathName: {"SYMBOLIC-PATH-NAME", "RFC8231"}, TLVIPv4LSPIdentifiers: {"IPV4-LSP-IDENTIFIERS", "RFC8231"}, TLVIPv6LSPIdentifiers: {"IPV6-LSP-IDENTIFIERS", "RFC8231"}, @@ -120,7 +120,7 @@ var tlvDescriptions = map[TLVType]struct { TLVRsvpErrorSpec: {"RSVP-ERROR-SPEC", "RFC8231"}, TLVLSPDBVersion: {"LSP-DB-VERSION", "RFC8232"}, TLVSpeakerEntityID: {"SPEAKER-ENTITY-ID", "RFC8232"}, - TLVSRPceCapability: {"SR-PCE-CAPABILITY", "RFC8664"}, + TLVSRPCECapability: {"SR-PCE-CAPABILITY", "RFC8664"}, TLVPathSetupType: {"PATH-SETUP-TYPE", "RFC8408"}, TLVOperatorConfiguredAssociationRange: {"OPERATOR-CONFIGURED-ASSOCIATION-RANGE", "RFC8697"}, TLVGlobalAssociationSource: {"GLOBAL-ASSOCIATION-SOURCE", "RFC8697"}, @@ -144,7 +144,7 @@ var tlvDescriptions = map[TLVType]struct { TLVPolicyParameters: {"POLICY-PARAMETERS-TLV", "RFC9005"}, TLVSchedLSPAttribute: {"SCHED-LSP-ATTRIBUTE", "RFC8934"}, TLVSchedPdLSPAttribute: {"SCHED-PD-LSP-ATTRIBUTE", "RFC8934"}, - TLVPceFlowspecCapability: {"PCE-FLOWSPEC-CAPABILITY TLV", "RFC9168"}, + TLVPCEFlowspecCapability: {"PCE-FLOWSPEC-CAPABILITY TLV", "RFC9168"}, TLVFlowFilter: {"FLOW-FILTER-TLV", "RFC9168"}, TLVBidirectionalLSPAssociationGroup: {"BIDIRECTIONAL-LSP Association Group TLV", "RFC9059"}, TLVTePathBinding: {"TE-PATH-BINDING", "RFC9604"}, @@ -178,12 +178,12 @@ func (t TLVType) String() string { } var tlvMap = map[TLVType]func() TLVInterface{ - TLVStatefulPceCapability: func() TLVInterface { return &StatefulPceCapability{} }, + TLVStatefulPCECapability: func() TLVInterface { return &StatefulPCECapability{} }, TLVSymbolicPathName: func() TLVInterface { return &SymbolicPathName{} }, TLVIPv4LSPIdentifiers: func() TLVInterface { return &IPv4LSPIdentifiers{} }, TLVIPv6LSPIdentifiers: func() TLVInterface { return &IPv6LSPIdentifiers{} }, TLVLSPDBVersion: func() TLVInterface { return &LSPDBVersion{} }, - TLVSRPceCapability: func() TLVInterface { return &SRPceCapability{} }, + TLVSRPCECapability: func() TLVInterface { return &SRPCECapability{} }, TLVPathSetupType: func() TLVInterface { return &PathSetupType{} }, TLVExtendedAssociationID: func() TLVInterface { return &ExtendedAssociationID{} }, TLVPathSetupTypeCapability: func() TLVInterface { return &PathSetupTypeCapability{} }, @@ -192,9 +192,9 @@ var tlvMap = map[TLVType]func() TLVInterface{ } const ( - TLVStatefulPceCapabilityValueLength uint16 = 4 + TLVStatefulPCECapabilityValueLength uint16 = 4 TLVLSPDBVersionValueLength uint16 = 8 - TLVSRPceCapabilityValueLength uint16 = 4 + TLVSRPCECapabilityValueLength uint16 = 4 TLVPathSetupTypeValueLength uint16 = 4 TLVExtendedAssociationIDIPv4ValueLength uint16 = 8 TLVExtendedAssociationIDIPv6ValueLength uint16 = 20 @@ -233,7 +233,7 @@ type TLVInterface interface { Len() uint16 // Total length of Type, Length, and Value } -type StatefulPceCapability struct { +type StatefulPCECapability struct { LSPUpdateCapability bool // 31 IncludeDBVersion bool // 30 LSPInstantiationCapability bool // 29 @@ -251,7 +251,7 @@ type StatefulPceCapability struct { Relax bool // 17 } -func (tlv *StatefulPceCapability) DecodeFromBytes(flags []uint8) error { +func (tlv *StatefulPCECapability) DecodeFromBytes(flags []uint8) error { if len(flags) < 4 { return fmt.Errorf("flags array is too short, expected at least 4 bytes but got %d", len(flags)) } @@ -291,7 +291,7 @@ func setFlag(flags []uint8, index int, mask uint8, condition bool) { } } -func (tlv *StatefulPceCapability) Serialize() []uint8 { +func (tlv *StatefulPCECapability) Serialize() []uint8 { buf := []uint8{} typ := make([]uint8, 2) @@ -299,10 +299,10 @@ func (tlv *StatefulPceCapability) Serialize() []uint8 { buf = append(buf, typ...) length := make([]uint8, 2) - binary.BigEndian.PutUint16(length, TLVStatefulPceCapabilityValueLength) + binary.BigEndian.PutUint16(length, TLVStatefulPCECapabilityValueLength) buf = append(buf, length...) - flags := make([]uint8, TLVStatefulPceCapabilityValueLength) + flags := make([]uint8, TLVStatefulPCECapabilityValueLength) setFlag(flags, 3, 0x01, tlv.LSPUpdateCapability) setFlag(flags, 3, 0x02, tlv.IncludeDBVersion) @@ -325,19 +325,19 @@ func (tlv *StatefulPceCapability) Serialize() []uint8 { return buf } -func (tlv *StatefulPceCapability) MarshalLogObject(enc zapcore.ObjectEncoder) error { +func (tlv *StatefulPCECapability) MarshalLogObject(enc zapcore.ObjectEncoder) error { return nil } -func (tlv *StatefulPceCapability) Type() TLVType { - return TLVStatefulPceCapability +func (tlv *StatefulPCECapability) Type() TLVType { + return TLVStatefulPCECapability } -func (tlv *StatefulPceCapability) Len() uint16 { - return TLVHeaderLength + TLVStatefulPceCapabilityValueLength +func (tlv *StatefulPCECapability) Len() uint16 { + return TLVHeaderLength + TLVStatefulPCECapabilityValueLength } -func (tlv *StatefulPceCapability) CapStrings() []string { +func (tlv *StatefulPCECapability) CapStrings() []string { ret := []string{} ret = append(ret, "Stateful") if tlv.LSPUpdateCapability { @@ -520,20 +520,20 @@ func (tlv *LSPDBVersion) CapStrings() []string { return []string{"LSP-DB-VERSION"} } -type SRPceCapability struct { +type SRPCECapability struct { UnlimitedMSD bool SupportNAI bool MaximumSidDepth uint8 } -func (tlv *SRPceCapability) DecodeFromBytes(data []uint8) error { +func (tlv *SRPCECapability) DecodeFromBytes(data []uint8) error { tlv.UnlimitedMSD = (data[6] & 0x01) != 0 tlv.SupportNAI = (data[6] & 0x02) != 0 tlv.MaximumSidDepth = data[7] return nil } -func (tlv *SRPceCapability) Serialize() []uint8 { +func (tlv *SRPCECapability) Serialize() []uint8 { buf := []uint8{} typ := make([]uint8, 2) @@ -541,10 +541,10 @@ func (tlv *SRPceCapability) Serialize() []uint8 { buf = append(buf, typ...) length := make([]uint8, 2) - binary.BigEndian.PutUint16(length, TLVSRPceCapabilityValueLength) + binary.BigEndian.PutUint16(length, TLVSRPCECapabilityValueLength) buf = append(buf, length...) - val := make([]uint8, TLVSRPceCapabilityValueLength) + val := make([]uint8, TLVSRPCECapabilityValueLength) if tlv.UnlimitedMSD { val[2] = val[2] | 0x01 } @@ -557,19 +557,19 @@ func (tlv *SRPceCapability) Serialize() []uint8 { return buf } -func (tlv *SRPceCapability) MarshalLogObject(enc zapcore.ObjectEncoder) error { +func (tlv *SRPCECapability) MarshalLogObject(enc zapcore.ObjectEncoder) error { return nil } -func (tlv *SRPceCapability) Type() TLVType { - return TLVSRPceCapability +func (tlv *SRPCECapability) Type() TLVType { + return TLVSRPCECapability } -func (tlv *SRPceCapability) Len() uint16 { - return TLVHeaderLength + TLVSRPceCapabilityValueLength +func (tlv *SRPCECapability) Len() uint16 { + return TLVHeaderLength + TLVSRPCECapabilityValueLength } -func (tlv *SRPceCapability) CapStrings() []string { +func (tlv *SRPCECapability) CapStrings() []string { return []string{"SR-TE"} } @@ -1037,22 +1037,22 @@ func (tlv *UndefinedTLV) DecodeFromBytes(data []uint8) error { } func (tlv *UndefinedTLV) Serialize() []uint8 { - bytePcepTLV := []uint8{} + bytePCEPTLV := []uint8{} byteTLVType := make([]uint8, 2) binary.BigEndian.PutUint16(byteTLVType, uint16(tlv.Typ)) - bytePcepTLV = append(bytePcepTLV, byteTLVType...) // Type (2byte) + bytePCEPTLV = append(bytePCEPTLV, byteTLVType...) // Type (2byte) byteTLVLength := make([]uint8, 2) binary.BigEndian.PutUint16(byteTLVLength, tlv.Length) - bytePcepTLV = append(bytePcepTLV, byteTLVLength...) // Length (2byte) + bytePCEPTLV = append(bytePCEPTLV, byteTLVLength...) // Length (2byte) - bytePcepTLV = append(bytePcepTLV, tlv.Value...) // Value (Length byte) + bytePCEPTLV = append(bytePCEPTLV, tlv.Value...) // Value (Length byte) if padding := tlv.Length % 4; padding != 0 { bytePadding := make([]uint8, 4-padding) - bytePcepTLV = append(bytePcepTLV, bytePadding...) + bytePCEPTLV = append(bytePCEPTLV, bytePadding...) } - return bytePcepTLV + return bytePCEPTLV } func (tlv *UndefinedTLV) MarshalLogObject(enc zapcore.ObjectEncoder) error { diff --git a/pkg/server/error.go b/pkg/server/error.go index b670d724..a25feabc 100644 --- a/pkg/server/error.go +++ b/pkg/server/error.go @@ -5,7 +5,7 @@ package server -type ServerError struct { +type Error struct { Error error Server string } diff --git a/pkg/server/grpc_server.go b/pkg/server/grpc_server.go index f8c3d2cd..491f93fc 100644 --- a/pkg/server/grpc_server.go +++ b/pkg/server/grpc_server.go @@ -28,7 +28,7 @@ type APIServer struct { grpcServer *grpc.Server usidMode bool logger *zap.Logger - pb.UnimplementedPceServiceServer + pb.UnimplementedPCEServiceServer } func NewAPIServer(pce *Server, grpcServer *grpc.Server, usidMode bool, logger *zap.Logger) *APIServer { @@ -38,7 +38,7 @@ func NewAPIServer(pce *Server, grpcServer *grpc.Server, usidMode bool, logger *z usidMode: usidMode, logger: logger.With(zap.String("server", "grpc")), } - pb.RegisterPceServiceServer(grpcServer, s) + pb.RegisterPCEServiceServer(grpcServer, s) return s } @@ -112,7 +112,7 @@ func buildSegmentList(s *APIServer, input *pb.CreateSRPolicyInput, withLinkState func sendSRPolicyRequest(s *APIServer, input *pb.CreateSRPolicyInput, segmentList []table.Segment, srcAddr, dstAddr netip.Addr) (*pb.RequestStatus, error) { inputSRPolicy := input.GetSRPolicy() - pcepSession, err := getSyncedPcepSession(s.pce, inputSRPolicy.GetPcepSessionAddr()) + pcepSession, err := getSyncedPCEPSession(s.pce, inputSRPolicy.GetPCEPSessionAddr()) if err != nil { return &pb.RequestStatus{IsSuccess: false}, err } @@ -176,14 +176,14 @@ func (s *APIServer) DeleteSRPolicy(ctx context.Context, input *pb.DeleteSRPolicy segmentList = append(segmentList, seg) } - inputJson, err := json.Marshal(input) + inputJSON, err := json.Marshal(input) if err != nil { return nil, err } s.logger.Info("Received DeleteSRPolicy API request") - s.logger.Debug("Received paramater", zap.String("input", string(inputJson))) + s.logger.Debug("Received paramater", zap.String("input", string(inputJSON))) - pcepSession, err := getSyncedPcepSession(s.pce, inputSRPolicy.GetPcepSessionAddr()) + pcepSession, err := getSyncedPCEPSession(s.pce, inputSRPolicy.GetPCEPSessionAddr()) if err != nil { return &pb.RequestStatus{IsSuccess: false}, err } @@ -232,26 +232,26 @@ const ( var validator = map[ValidationKind]func(policy *pb.SRPolicy, asn uint32) bool{ ValidationKind("Add"): func(policy *pb.SRPolicy, asn uint32) bool { return asn != 0 && - policy.PcepSessionAddr != nil && + policy.PCEPSessionAddr != nil && policy.Color != 0 && policy.SrcRouterID != "" && policy.DstRouterID != "" }, ValidationKind("AddWithoutLinkState"): func(policy *pb.SRPolicy, asn uint32) bool { - return policy.PcepSessionAddr != nil && + return policy.PCEPSessionAddr != nil && len(policy.SrcAddr) > 0 && len(policy.DstAddr) > 0 && len(policy.SegmentList) > 0 }, ValidationKind("Delete"): func(policy *pb.SRPolicy, asn uint32) bool { - return policy.PcepSessionAddr != nil && + return policy.PCEPSessionAddr != nil && policy.Color != 0 && len(policy.DstAddr) > 0 && policy.PolicyName != "" }, } -func getSyncedPcepSession(pce *Server, addr []byte) (*Session, error) { +func getSyncedPCEPSession(pce *Server, addr []byte) (*Session, error) { pcepSessionAddr, _ := netip.AddrFromSlice(addr) pcepSession := pce.SearchSession(pcepSessionAddr, true) if pcepSession == nil { @@ -268,7 +268,7 @@ func getLoopbackAddr(pce *Server, asn uint32, routerID string) (netip.Addr, erro return node.LoopbackAddr() } -func getSegmentList(inputSRPolicy *pb.SRPolicy, asn uint32, ted *table.LsTed) ([]table.Segment, error) { +func getSegmentList(inputSRPolicy *pb.SRPolicy, asn uint32, ted *table.LsTED) ([]table.Segment, error) { var segmentList []table.Segment switch inputSRPolicy.GetType() { @@ -301,14 +301,14 @@ func getSegmentList(inputSRPolicy *pb.SRPolicy, asn uint32, ted *table.LsTed) ([ func getMetricType(metricType pb.MetricType) (table.MetricType, error) { switch metricType { - case pb.MetricType_IGP: - return table.IGP_METRIC, nil - case pb.MetricType_TE: - return table.TE_METRIC, nil - case pb.MetricType_DELAY: - return table.DELAY_METRIC, nil - case pb.MetricType_HOPCOUNT: - return table.HOPCOUNT_METRIC, nil + case pb.MetricType_METRIC_TYPE_IGP: + return table.IGPMetric, nil + case pb.MetricType_METRIC_TYPE_TE: + return table.TEMetric, nil + case pb.MetricType_METRIC_TYPE_DELAY: + return table.DelayMetric, nil + case pb.MetricType_METRIC_TYPE_HOPCOUNT: + return table.HopcountMetric, nil default: return 0, fmt.Errorf("unknown metric type: %v", metricType) } @@ -343,7 +343,7 @@ func (s *APIServer) GetSRPolicyList(context.Context, *empty.Empty) (*pb.SRPolicy for ssAddr, policies := range s.pce.SRPolicies() { for _, policy := range policies { srPolicyData := &pb.SRPolicy{ - PcepSessionAddr: ssAddr.AsSlice(), + PCEPSessionAddr: ssAddr.AsSlice(), SegmentList: make([]*pb.Segment, 0), Color: policy.Color, Preference: policy.Preference, @@ -366,10 +366,10 @@ func (s *APIServer) GetSRPolicyList(context.Context, *empty.Empty) (*pb.SRPolicy return &ret, nil } -func (s *APIServer) GetTed(context.Context, *empty.Empty) (*pb.Ted, error) { - s.logger.Info("Received GetTed API request") +func (s *APIServer) GetTED(context.Context, *empty.Empty) (*pb.TED, error) { + s.logger.Info("Received GetTED API request") - ret := &pb.Ted{ + ret := &pb.TED{ Enable: true, } @@ -383,7 +383,7 @@ func (s *APIServer) GetTed(context.Context, *empty.Empty) (*pb.Ted, error) { for _, lsNodes := range s.pce.ted.Nodes { for _, lsNode := range lsNodes { node := &pb.LsNode{ - Asn: lsNode.Asn, + Asn: lsNode.ASN, RouterID: lsNode.RouterID, IsisAreaID: lsNode.IsisAreaID, Hostname: lsNode.Hostname, @@ -396,13 +396,13 @@ func (s *APIServer) GetTed(context.Context, *empty.Empty) (*pb.Ted, error) { for _, lsLink := range lsNode.Links { link := &pb.LsLink{ LocalRouterID: lsLink.LocalNode.RouterID, - LocalAsn: lsLink.LocalNode.Asn, + LocalASN: lsLink.LocalNode.ASN, LocalIP: lsLink.LocalIP.String(), RemoteRouterID: lsLink.RemoteNode.RouterID, - RemoteAsn: lsLink.RemoteNode.Asn, + RemoteASN: lsLink.RemoteNode.ASN, RemoteIP: lsLink.RemoteIP.String(), Metrics: make([]*pb.Metric, 0, len(lsLink.Metrics)), - AdjSid: lsLink.AdjSid, + AdjSID: lsLink.AdjSid, } for _, lsMetric := range lsLink.Metrics { @@ -436,7 +436,7 @@ func (s *APIServer) GetTed(context.Context, *empty.Empty) (*pb.Ted, error) { } } - s.logger.Debug("Send GetTed API reply") + s.logger.Debug("Send GetTED API reply") return ret, nil } diff --git a/pkg/server/server.go b/pkg/server/server.go index d08dc5f0..49a71d01 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -21,23 +21,23 @@ import ( type Server struct { sessionList []*Session - ted *table.LsTed + ted *table.LsTED logger *zap.Logger } -type PceOptions struct { - PcepAddr string - PcepPort string - GrpcAddr string - GrpcPort string - TedEnable bool +type PCEOptions struct { + PCEPAddr string + PCEPPort string + GRPCAddr string + GRPCPort string + TEDEnable bool USidMode bool } -func NewPce(o *PceOptions, logger *zap.Logger, tedElemsChan chan []table.TedElem) ServerError { +func NewPCE(o *PCEOptions, logger *zap.Logger, tedElemsChan chan []table.TEDElem) Error { s := &Server{logger: logger} - if o.TedEnable { - s.ted = &table.LsTed{ + if o.TEDEnable { + s.ted = &table.LsTED{ ID: 1, Nodes: map[uint32]map[string]*table.LsNode{}, } @@ -46,7 +46,7 @@ func NewPce(o *PceOptions, logger *zap.Logger, tedElemsChan chan []table.TedElem go func() { for { tedElems := <-tedElemsChan - ted := &table.LsTed{ + ted := &table.LsTED{ ID: s.ted.ID, Nodes: map[uint32]map[string]*table.LsNode{}, } @@ -57,10 +57,10 @@ func NewPce(o *PceOptions, logger *zap.Logger, tedElemsChan chan []table.TedElem }() } - errChan := make(chan ServerError) + errChan := make(chan Error) go func() { - if err := s.Serve(o.PcepAddr, o.PcepPort, o.USidMode); err != nil { - errChan <- ServerError{ + if err := s.Serve(o.PCEPAddr, o.PCEPPort, o.USidMode); err != nil { + errChan <- Error{ Server: "pcep", Error: err, } @@ -70,8 +70,8 @@ func NewPce(o *PceOptions, logger *zap.Logger, tedElemsChan chan []table.TedElem go func() { grpcServer := grpc.NewServer() apiServer := NewAPIServer(s, grpcServer, o.USidMode, logger) - if err := apiServer.Serve(o.GrpcAddr, o.GrpcPort); err != nil { - errChan <- ServerError{ + if err := apiServer.Serve(o.GRPCAddr, o.GRPCPort); err != nil { + errChan <- Error{ Server: "grpc", Error: err, } diff --git a/pkg/server/session.go b/pkg/server/session.go index 79d51e48..74d5c6de 100644 --- a/pkg/server/session.go +++ b/pkg/server/session.go @@ -60,7 +60,7 @@ func (ss *Session) Established() { // Receive PCEP messages in a separate goroutine go func() { - if err := ss.ReceivePcepMessage(); err != nil { + if err := ss.ReceivePCEPMessage(); err != nil { ss.logger.Debug("ERROR! Receive PCEP Message", zap.Error(err)) } done <- struct{}{} @@ -82,7 +82,7 @@ func (ss *Session) Established() { } } -func (ss *Session) sendPcepMessage(message pcep.Message) error { +func (ss *Session) sendPCEPMessage(message pcep.Message) error { byteMessage, err := message.Serialize() if err != nil { return err @@ -156,7 +156,7 @@ func (ss *Session) SendKeepalive() error { return err } ss.logger.Debug("Send Keepalive Message") - return ss.sendPcepMessage(keepaliveMessage) + return ss.sendPCEPMessage(keepaliveMessage) } func (ss *Session) SendClose(reason pcep.CloseReason) error { @@ -175,7 +175,7 @@ func (ss *Session) SendClose(reason pcep.CloseReason) error { return nil } -func (ss *Session) ReceivePcepMessage() error { +func (ss *Session) ReceivePCEPMessage() error { for { commonHeader, err := ss.readCommonHeader() if err != nil { @@ -203,8 +203,8 @@ func (ss *Session) ReceivePcepMessage() error { } ss.logger.Debug("Received PCErr", - zap.Uint8("error-Type", pcerrMessage.PcepErrorObject.ErrorType), - zap.Uint8("error-value", pcerrMessage.PcepErrorObject.ErrorValue), + zap.Uint8("error-Type", pcerrMessage.PCEPErrorObject.ErrorType), + zap.Uint8("error-value", pcerrMessage.PCEPErrorObject.ErrorValue), zap.String("detail", "See https://www.iana.org/assignments/pcep/pcep.xhtml#pcep-error-object")) case pcep.MessageTypeClose: byteCloseMessageBody := make([]uint8, commonHeader.MessageLength-pcep.CommonHeaderLength) @@ -305,7 +305,7 @@ func (ss *Session) SendOpen() error { return err } ss.logger.Debug("Send Open Message") - return ss.sendPcepMessage(openMessage) + return ss.sendPCEPMessage(openMessage) } func (ss *Session) SendPCInitiate(srPolicy table.SRPolicy, lspDelete bool) error { @@ -314,7 +314,7 @@ func (ss *Session) SendPCInitiate(srPolicy table.SRPolicy, lspDelete bool) error return err } ss.logger.Debug("Send PCInitiate Message") - err = ss.sendPcepMessage(pcinitiateMessage) + err = ss.sendPCEPMessage(pcinitiateMessage) if err == nil { ss.srpIDHead++ } @@ -327,7 +327,7 @@ func (ss *Session) SendPCUpdate(srPolicy table.SRPolicy) error { return err } ss.logger.Debug("Send Update Message") - err = ss.sendPcepMessage(pcupdateMessage) + err = ss.sendPCEPMessage(pcupdateMessage) if err == nil { ss.srpIDHead++ } @@ -346,7 +346,7 @@ func (ss *Session) RegisterSRPolicy(sr pcep.StateReport) { // TODO: Move hasColorCapability to Session struct hasColorCapability := false for _, cap := range ss.pccCapabilities { - if statefulCap, ok := cap.(*pcep.StatefulPceCapability); ok { + if statefulCap, ok := cap.(*pcep.StatefulPCECapability); ok { if statefulCap.ColorCapability { hasColorCapability = true break @@ -370,13 +370,13 @@ func (ss *Session) RegisterSRPolicy(sr pcep.StateReport) { var state table.PolicyState switch sr.LSPObject.OFlag { case uint8(0x00): - state = table.POLICY_DOWN + state = table.PolicyDown case uint8(0x01): - state = table.POLICY_UP + state = table.PolicyUp case uint8(0x02): - state = table.POLICY_ACTIVE + state = table.PolicyActive default: - state = table.POLICY_UNKNOWN + state = table.PolicyUnknown } if p, ok := ss.SearchSRPolicy(sr.LSPObject.PlspID); ok { diff --git a/tools/grpc/go/add_sr-policy/add_sr-policy.go b/tools/grpc/go/add_sr-policy/add_sr-policy.go index 5b29a379..a2f2de71 100644 --- a/tools/grpc/go/add_sr-policy/add_sr-policy.go +++ b/tools/grpc/go/add_sr-policy/add_sr-policy.go @@ -34,7 +34,7 @@ func main() { } }() - c := pb.NewPceServiceClient(conn) + c := pb.NewPCEServiceClient(conn) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() @@ -44,7 +44,7 @@ func main() { r, err := c.CreateSRPolicy(ctx, &pb.CreateSRPolicyInput{ Asn: 65000, SRPolicy: &pb.SRPolicy{ - PcepSessionAddr: ssAddr.AsSlice(), + PCEPSessionAddr: ssAddr.AsSlice(), SrcRouterID: "0000.0aff.0001", DstRouterID: "0000.0aff.0004", Color: uint32(100), diff --git a/tools/grpc/go/add_sr-policy_no_ls/add_sr-policy_no_ls.go b/tools/grpc/go/add_sr-policy_no_ls/add_sr-policy_no_ls.go index 8e17cd7b..faeb8d33 100644 --- a/tools/grpc/go/add_sr-policy_no_ls/add_sr-policy_no_ls.go +++ b/tools/grpc/go/add_sr-policy_no_ls/add_sr-policy_no_ls.go @@ -34,7 +34,7 @@ func main() { } }() - c := pb.NewPceServiceClient(conn) + c := pb.NewPCEServiceClient(conn) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() @@ -45,7 +45,7 @@ func main() { r, err := c.CreateSRPolicyWithoutLinkState(ctx, &pb.CreateSRPolicyInput{ SRPolicy: &pb.SRPolicy{ - PcepSessionAddr: ssAddr.AsSlice(), + PCEPSessionAddr: ssAddr.AsSlice(), SrcAddr: srcAddr.AsSlice(), DstAddr: dstAddr.AsSlice(), Color: uint32(100), diff --git a/tools/grpc/go/del_session/del_session.go b/tools/grpc/go/del_session/del_session.go index 5703562d..e395d56e 100644 --- a/tools/grpc/go/del_session/del_session.go +++ b/tools/grpc/go/del_session/del_session.go @@ -34,7 +34,7 @@ func main() { } }() - c := pb.NewPceServiceClient(conn) + c := pb.NewPCEServiceClient(conn) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() diff --git a/tools/grpc/go/show_session/show_session.go b/tools/grpc/go/show_session/show_session.go index 5a153a51..91b2b5df 100644 --- a/tools/grpc/go/show_session/show_session.go +++ b/tools/grpc/go/show_session/show_session.go @@ -37,7 +37,7 @@ func main() { } }() - c := pb.NewPceServiceClient(conn) + c := pb.NewPCEServiceClient(conn) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() diff --git a/tools/grpc/go/show_sr-policy/show_sr-policy_list.go b/tools/grpc/go/show_sr-policy/show_sr-policy_list.go index 4afd752d..9ad2ece8 100644 --- a/tools/grpc/go/show_sr-policy/show_sr-policy_list.go +++ b/tools/grpc/go/show_sr-policy/show_sr-policy_list.go @@ -37,7 +37,7 @@ func main() { } }() - c := pb.NewPceServiceClient(conn) + c := pb.NewPCEServiceClient(conn) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() @@ -50,7 +50,7 @@ func main() { for i, srPolicy := range ret.GetSRPolicies() { fmt.Printf("srPolicy(%d): \n", i) - sessionAddr := net.IP(srPolicy.GetPcepSessionAddr()) + sessionAddr := net.IP(srPolicy.GetPCEPSessionAddr()) fmt.Printf(" sessionAddr: %s\n", sessionAddr.String()) fmt.Printf(" policyName: %s\n", srPolicy.GetPolicyName()) fmt.Printf(" SrcAddr: %s\n", net.IP(srPolicy.GetSrcAddr())) diff --git a/tools/grpc/go/show_ted/show_ted.go b/tools/grpc/go/show_ted/show_ted.go index 8afff069..1c9d10eb 100644 --- a/tools/grpc/go/show_ted/show_ted.go +++ b/tools/grpc/go/show_ted/show_ted.go @@ -35,14 +35,14 @@ func main() { } }() - c := pb.NewPceServiceClient(conn) + c := pb.NewPCEServiceClient(conn) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() var empty empty.Empty - ret, err := c.GetTed(ctx, &empty) + ret, err := c.GetTED(ctx, &empty) if err != nil { log.Fatalf("unable to get TED info: %v", err) } From a19b7b2c03ccd235c8ca6cf4c7de56eea294c70d Mon Sep 17 00:00:00 2001 From: Motok1 Date: Thu, 28 Aug 2025 11:11:01 +0900 Subject: [PATCH 11/87] refactor(grpc): align with Protocol Buffers Style Guide and rename option --- api/grpc/pola.pb.go | 1276 ---------- api/grpc/pola.proto | 128 - api/pola/v1/pola.pb.go | 2063 +++++++++++++++++ api/pola/v1/pola.proto | 161 ++ api/{grpc => pola/v1}/pola.zap.go | 12 +- api/{grpc => pola/v1}/pola_grpc.pb.go | 185 +- cmd/pola/README.md | 2 +- cmd/pola/grpc/grpc_client.go | 56 +- cmd/pola/root.go | 2 +- cmd/pola/session_del.go | 6 +- cmd/pola/sr_policy_add.go | 52 +- cmd/pola/sr_policy_delete.go | 8 +- examples/containerlab/srv6_te_l3vpn/README.md | 4 +- examples/tinet/sr-mpls_te_l3vpn/README.md | 2 +- pkg/server/grpc_server.go | 172 +- tools/grpc/go/add_sr-policy/add_sr-policy.go | 17 +- .../add_sr-policy_no_ls.go | 11 +- tools/grpc/go/del_session/del_session.go | 4 +- tools/grpc/go/show_session/show_session.go | 6 +- .../go/show_sr-policy/show_sr-policy_list.go | 10 +- tools/grpc/go/show_ted/show_ted.go | 7 +- 21 files changed, 2458 insertions(+), 1726 deletions(-) delete mode 100644 api/grpc/pola.pb.go delete mode 100644 api/grpc/pola.proto create mode 100644 api/pola/v1/pola.pb.go create mode 100644 api/pola/v1/pola.proto rename api/{grpc => pola/v1}/pola.zap.go (80%) rename api/{grpc => pola/v1}/pola_grpc.pb.go (52%) diff --git a/api/grpc/pola.pb.go b/api/grpc/pola.pb.go deleted file mode 100644 index cfb7533e..00000000 --- a/api/grpc/pola.pb.go +++ /dev/null @@ -1,1276 +0,0 @@ -// Copyright (c) 2022 NTT Communications Corporation -// -// This software is released under the MIT License. -// see https://github.com/nttcom/pola/blob/main/LICENSE - -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.36.5 -// protoc v5.29.3 -// source: pola.proto - -package grpc - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - emptypb "google.golang.org/protobuf/types/known/emptypb" - reflect "reflect" - sync "sync" - unsafe "unsafe" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type SRPolicyType int32 - -const ( - SRPolicyType_EXPLICIT SRPolicyType = 0 - SRPolicyType_DYNAMIC SRPolicyType = 1 -) - -// Enum value maps for SRPolicyType. -var ( - SRPolicyType_name = map[int32]string{ - 0: "EXPLICIT", - 1: "DYNAMIC", - } - SRPolicyType_value = map[string]int32{ - "EXPLICIT": 0, - "DYNAMIC": 1, - } -) - -func (x SRPolicyType) Enum() *SRPolicyType { - p := new(SRPolicyType) - *p = x - return p -} - -func (x SRPolicyType) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (SRPolicyType) Descriptor() protoreflect.EnumDescriptor { - return file_pola_proto_enumTypes[0].Descriptor() -} - -func (SRPolicyType) Type() protoreflect.EnumType { - return &file_pola_proto_enumTypes[0] -} - -func (x SRPolicyType) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use SRPolicyType.Descriptor instead. -func (SRPolicyType) EnumDescriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{0} -} - -type SessionState int32 - -const ( - SessionState_DOWN SessionState = 0 - SessionState_UP SessionState = 1 -) - -// Enum value maps for SessionState. -var ( - SessionState_name = map[int32]string{ - 0: "DOWN", - 1: "UP", - } - SessionState_value = map[string]int32{ - "DOWN": 0, - "UP": 1, - } -) - -func (x SessionState) Enum() *SessionState { - p := new(SessionState) - *p = x - return p -} - -func (x SessionState) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (SessionState) Descriptor() protoreflect.EnumDescriptor { - return file_pola_proto_enumTypes[1].Descriptor() -} - -func (SessionState) Type() protoreflect.EnumType { - return &file_pola_proto_enumTypes[1] -} - -func (x SessionState) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use SessionState.Descriptor instead. -func (SessionState) EnumDescriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{1} -} - -type MetricType int32 - -const ( - MetricType_IGP MetricType = 0 - MetricType_TE MetricType = 1 - MetricType_DELAY MetricType = 2 - MetricType_HOPCOUNT MetricType = 3 -) - -// Enum value maps for MetricType. -var ( - MetricType_name = map[int32]string{ - 0: "IGP", - 1: "TE", - 2: "DELAY", - 3: "HOPCOUNT", - } - MetricType_value = map[string]int32{ - "IGP": 0, - "TE": 1, - "DELAY": 2, - "HOPCOUNT": 3, - } -) - -func (x MetricType) Enum() *MetricType { - p := new(MetricType) - *p = x - return p -} - -func (x MetricType) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (MetricType) Descriptor() protoreflect.EnumDescriptor { - return file_pola_proto_enumTypes[2].Descriptor() -} - -func (MetricType) Type() protoreflect.EnumType { - return &file_pola_proto_enumTypes[2] -} - -func (x MetricType) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use MetricType.Descriptor instead. -func (MetricType) EnumDescriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{2} -} - -type Segment struct { - state protoimpl.MessageState `protogen:"open.v1"` - Sid string `protobuf:"bytes,1,opt,name=sid,proto3" json:"sid,omitempty"` - SidStructure string `protobuf:"bytes,2,opt,name=sidStructure,proto3" json:"sidStructure,omitempty"` - LocalAddr string `protobuf:"bytes,3,opt,name=localAddr,proto3" json:"localAddr,omitempty"` - RemoteAddr string `protobuf:"bytes,4,opt,name=remoteAddr,proto3" json:"remoteAddr,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *Segment) Reset() { - *x = Segment{} - mi := &file_pola_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *Segment) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Segment) ProtoMessage() {} - -func (x *Segment) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[0] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Segment.ProtoReflect.Descriptor instead. -func (*Segment) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{0} -} - -func (x *Segment) GetSid() string { - if x != nil { - return x.Sid - } - return "" -} - -func (x *Segment) GetSidStructure() string { - if x != nil { - return x.SidStructure - } - return "" -} - -func (x *Segment) GetLocalAddr() string { - if x != nil { - return x.LocalAddr - } - return "" -} - -func (x *Segment) GetRemoteAddr() string { - if x != nil { - return x.RemoteAddr - } - return "" -} - -type SRPolicy struct { - state protoimpl.MessageState `protogen:"open.v1"` - PCEPSessionAddr []byte `protobuf:"bytes,1,opt,name=PCEPSessionAddr,proto3" json:"PCEPSessionAddr,omitempty"` - SrcAddr []byte `protobuf:"bytes,2,opt,name=srcAddr,proto3" json:"srcAddr,omitempty"` - DstAddr []byte `protobuf:"bytes,3,opt,name=dstAddr,proto3" json:"dstAddr,omitempty"` - SrcRouterID string `protobuf:"bytes,4,opt,name=srcRouterID,proto3" json:"srcRouterID,omitempty"` - DstRouterID string `protobuf:"bytes,5,opt,name=dstRouterID,proto3" json:"dstRouterID,omitempty"` - Color uint32 `protobuf:"varint,6,opt,name=color,proto3" json:"color,omitempty"` - Preference uint32 `protobuf:"varint,7,opt,name=preference,proto3" json:"preference,omitempty"` - PolicyName string `protobuf:"bytes,8,opt,name=policyName,proto3" json:"policyName,omitempty"` - Type SRPolicyType `protobuf:"varint,9,opt,name=type,proto3,enum=pb.SRPolicyType" json:"type,omitempty"` - SegmentList []*Segment `protobuf:"bytes,10,rep,name=segmentList,proto3" json:"segmentList,omitempty"` - Metric MetricType `protobuf:"varint,11,opt,name=metric,proto3,enum=pb.MetricType" json:"metric,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SRPolicy) Reset() { - *x = SRPolicy{} - mi := &file_pola_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SRPolicy) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SRPolicy) ProtoMessage() {} - -func (x *SRPolicy) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[1] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SRPolicy.ProtoReflect.Descriptor instead. -func (*SRPolicy) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{1} -} - -func (x *SRPolicy) GetPCEPSessionAddr() []byte { - if x != nil { - return x.PCEPSessionAddr - } - return nil -} - -func (x *SRPolicy) GetSrcAddr() []byte { - if x != nil { - return x.SrcAddr - } - return nil -} - -func (x *SRPolicy) GetDstAddr() []byte { - if x != nil { - return x.DstAddr - } - return nil -} - -func (x *SRPolicy) GetSrcRouterID() string { - if x != nil { - return x.SrcRouterID - } - return "" -} - -func (x *SRPolicy) GetDstRouterID() string { - if x != nil { - return x.DstRouterID - } - return "" -} - -func (x *SRPolicy) GetColor() uint32 { - if x != nil { - return x.Color - } - return 0 -} - -func (x *SRPolicy) GetPreference() uint32 { - if x != nil { - return x.Preference - } - return 0 -} - -func (x *SRPolicy) GetPolicyName() string { - if x != nil { - return x.PolicyName - } - return "" -} - -func (x *SRPolicy) GetType() SRPolicyType { - if x != nil { - return x.Type - } - return SRPolicyType_EXPLICIT -} - -func (x *SRPolicy) GetSegmentList() []*Segment { - if x != nil { - return x.SegmentList - } - return nil -} - -func (x *SRPolicy) GetMetric() MetricType { - if x != nil { - return x.Metric - } - return MetricType_IGP -} - -type CreateSRPolicyInput struct { - state protoimpl.MessageState `protogen:"open.v1"` - SRPolicy *SRPolicy `protobuf:"bytes,1,opt,name=SRPolicy,proto3" json:"SRPolicy,omitempty"` - Asn uint32 `protobuf:"varint,2,opt,name=asn,proto3" json:"asn,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *CreateSRPolicyInput) Reset() { - *x = CreateSRPolicyInput{} - mi := &file_pola_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *CreateSRPolicyInput) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CreateSRPolicyInput) ProtoMessage() {} - -func (x *CreateSRPolicyInput) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[2] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CreateSRPolicyInput.ProtoReflect.Descriptor instead. -func (*CreateSRPolicyInput) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{2} -} - -func (x *CreateSRPolicyInput) GetSRPolicy() *SRPolicy { - if x != nil { - return x.SRPolicy - } - return nil -} - -func (x *CreateSRPolicyInput) GetAsn() uint32 { - if x != nil { - return x.Asn - } - return 0 -} - -type DeleteSRPolicyInput struct { - state protoimpl.MessageState `protogen:"open.v1"` - SRPolicy *SRPolicy `protobuf:"bytes,1,opt,name=SRPolicy,proto3" json:"SRPolicy,omitempty"` - Asn uint32 `protobuf:"varint,2,opt,name=asn,proto3" json:"asn,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DeleteSRPolicyInput) Reset() { - *x = DeleteSRPolicyInput{} - mi := &file_pola_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DeleteSRPolicyInput) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteSRPolicyInput) ProtoMessage() {} - -func (x *DeleteSRPolicyInput) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[3] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteSRPolicyInput.ProtoReflect.Descriptor instead. -func (*DeleteSRPolicyInput) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{3} -} - -func (x *DeleteSRPolicyInput) GetSRPolicy() *SRPolicy { - if x != nil { - return x.SRPolicy - } - return nil -} - -func (x *DeleteSRPolicyInput) GetAsn() uint32 { - if x != nil { - return x.Asn - } - return 0 -} - -type RequestStatus struct { - state protoimpl.MessageState `protogen:"open.v1"` - IsSuccess bool `protobuf:"varint,1,opt,name=isSuccess,proto3" json:"isSuccess,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RequestStatus) Reset() { - *x = RequestStatus{} - mi := &file_pola_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RequestStatus) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RequestStatus) ProtoMessage() {} - -func (x *RequestStatus) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[4] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RequestStatus.ProtoReflect.Descriptor instead. -func (*RequestStatus) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{4} -} - -func (x *RequestStatus) GetIsSuccess() bool { - if x != nil { - return x.IsSuccess - } - return false -} - -type Session struct { - state protoimpl.MessageState `protogen:"open.v1"` - Addr []byte `protobuf:"bytes,1,opt,name=Addr,proto3" json:"Addr,omitempty"` - State SessionState `protobuf:"varint,2,opt,name=State,proto3,enum=pb.SessionState" json:"State,omitempty"` - Caps []string `protobuf:"bytes,3,rep,name=Caps,proto3" json:"Caps,omitempty"` - IsSynced bool `protobuf:"varint,4,opt,name=IsSynced,proto3" json:"IsSynced,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *Session) Reset() { - *x = Session{} - mi := &file_pola_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *Session) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Session) ProtoMessage() {} - -func (x *Session) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[5] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Session.ProtoReflect.Descriptor instead. -func (*Session) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{5} -} - -func (x *Session) GetAddr() []byte { - if x != nil { - return x.Addr - } - return nil -} - -func (x *Session) GetState() SessionState { - if x != nil { - return x.State - } - return SessionState_DOWN -} - -func (x *Session) GetCaps() []string { - if x != nil { - return x.Caps - } - return nil -} - -func (x *Session) GetIsSynced() bool { - if x != nil { - return x.IsSynced - } - return false -} - -type SessionList struct { - state protoimpl.MessageState `protogen:"open.v1"` - Sessions []*Session `protobuf:"bytes,1,rep,name=Sessions,proto3" json:"Sessions,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SessionList) Reset() { - *x = SessionList{} - mi := &file_pola_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SessionList) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SessionList) ProtoMessage() {} - -func (x *SessionList) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[6] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SessionList.ProtoReflect.Descriptor instead. -func (*SessionList) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{6} -} - -func (x *SessionList) GetSessions() []*Session { - if x != nil { - return x.Sessions - } - return nil -} - -type SRPolicyList struct { - state protoimpl.MessageState `protogen:"open.v1"` - SRPolicies []*SRPolicy `protobuf:"bytes,1,rep,name=SRPolicies,proto3" json:"SRPolicies,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SRPolicyList) Reset() { - *x = SRPolicyList{} - mi := &file_pola_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SRPolicyList) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SRPolicyList) ProtoMessage() {} - -func (x *SRPolicyList) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[7] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SRPolicyList.ProtoReflect.Descriptor instead. -func (*SRPolicyList) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{7} -} - -func (x *SRPolicyList) GetSRPolicies() []*SRPolicy { - if x != nil { - return x.SRPolicies - } - return nil -} - -type LsPrefix struct { - state protoimpl.MessageState `protogen:"open.v1"` - Prefix string `protobuf:"bytes,1,opt,name=prefix,proto3" json:"prefix,omitempty"` - SidIndex uint32 `protobuf:"varint,2,opt,name=sidIndex,proto3" json:"sidIndex,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *LsPrefix) Reset() { - *x = LsPrefix{} - mi := &file_pola_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *LsPrefix) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*LsPrefix) ProtoMessage() {} - -func (x *LsPrefix) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[8] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use LsPrefix.ProtoReflect.Descriptor instead. -func (*LsPrefix) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{8} -} - -func (x *LsPrefix) GetPrefix() string { - if x != nil { - return x.Prefix - } - return "" -} - -func (x *LsPrefix) GetSidIndex() uint32 { - if x != nil { - return x.SidIndex - } - return 0 -} - -type Metric struct { - state protoimpl.MessageState `protogen:"open.v1"` - Type MetricType `protobuf:"varint,1,opt,name=type,proto3,enum=pb.MetricType" json:"type,omitempty"` - Value uint32 `protobuf:"varint,2,opt,name=value,proto3" json:"value,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *Metric) Reset() { - *x = Metric{} - mi := &file_pola_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *Metric) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Metric) ProtoMessage() {} - -func (x *Metric) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[9] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Metric.ProtoReflect.Descriptor instead. -func (*Metric) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{9} -} - -func (x *Metric) GetType() MetricType { - if x != nil { - return x.Type - } - return MetricType_IGP -} - -func (x *Metric) GetValue() uint32 { - if x != nil { - return x.Value - } - return 0 -} - -type LsLink struct { - state protoimpl.MessageState `protogen:"open.v1"` - LocalRouterID string `protobuf:"bytes,1,opt,name=localRouterID,proto3" json:"localRouterID,omitempty"` - LocalASN uint32 `protobuf:"varint,2,opt,name=localASN,proto3" json:"localASN,omitempty"` - LocalIP string `protobuf:"bytes,3,opt,name=localIP,proto3" json:"localIP,omitempty"` - RemoteRouterID string `protobuf:"bytes,4,opt,name=remoteRouterID,proto3" json:"remoteRouterID,omitempty"` - RemoteASN uint32 `protobuf:"varint,5,opt,name=remoteASN,proto3" json:"remoteASN,omitempty"` - RemoteIP string `protobuf:"bytes,6,opt,name=remoteIP,proto3" json:"remoteIP,omitempty"` - Metrics []*Metric `protobuf:"bytes,7,rep,name=metrics,proto3" json:"metrics,omitempty"` - AdjSID uint32 `protobuf:"varint,8,opt,name=adjSID,proto3" json:"adjSID,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *LsLink) Reset() { - *x = LsLink{} - mi := &file_pola_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *LsLink) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*LsLink) ProtoMessage() {} - -func (x *LsLink) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[10] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use LsLink.ProtoReflect.Descriptor instead. -func (*LsLink) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{10} -} - -func (x *LsLink) GetLocalRouterID() string { - if x != nil { - return x.LocalRouterID - } - return "" -} - -func (x *LsLink) GetLocalASN() uint32 { - if x != nil { - return x.LocalASN - } - return 0 -} - -func (x *LsLink) GetLocalIP() string { - if x != nil { - return x.LocalIP - } - return "" -} - -func (x *LsLink) GetRemoteRouterID() string { - if x != nil { - return x.RemoteRouterID - } - return "" -} - -func (x *LsLink) GetRemoteASN() uint32 { - if x != nil { - return x.RemoteASN - } - return 0 -} - -func (x *LsLink) GetRemoteIP() string { - if x != nil { - return x.RemoteIP - } - return "" -} - -func (x *LsLink) GetMetrics() []*Metric { - if x != nil { - return x.Metrics - } - return nil -} - -func (x *LsLink) GetAdjSID() uint32 { - if x != nil { - return x.AdjSID - } - return 0 -} - -type LsNode struct { - state protoimpl.MessageState `protogen:"open.v1"` - Asn uint32 `protobuf:"varint,1,opt,name=asn,proto3" json:"asn,omitempty"` - RouterID string `protobuf:"bytes,2,opt,name=routerID,proto3" json:"routerID,omitempty"` - IsisAreaID string `protobuf:"bytes,3,opt,name=isisAreaID,proto3" json:"isisAreaID,omitempty"` - Hostname string `protobuf:"bytes,4,opt,name=hostname,proto3" json:"hostname,omitempty"` - SrgbBegin uint32 `protobuf:"varint,5,opt,name=srgbBegin,proto3" json:"srgbBegin,omitempty"` - SrgbEnd uint32 `protobuf:"varint,6,opt,name=srgbEnd,proto3" json:"srgbEnd,omitempty"` - LsLinks []*LsLink `protobuf:"bytes,7,rep,name=lsLinks,proto3" json:"lsLinks,omitempty"` - LsPrefixes []*LsPrefix `protobuf:"bytes,8,rep,name=lsPrefixes,proto3" json:"lsPrefixes,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *LsNode) Reset() { - *x = LsNode{} - mi := &file_pola_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *LsNode) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*LsNode) ProtoMessage() {} - -func (x *LsNode) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[11] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use LsNode.ProtoReflect.Descriptor instead. -func (*LsNode) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{11} -} - -func (x *LsNode) GetAsn() uint32 { - if x != nil { - return x.Asn - } - return 0 -} - -func (x *LsNode) GetRouterID() string { - if x != nil { - return x.RouterID - } - return "" -} - -func (x *LsNode) GetIsisAreaID() string { - if x != nil { - return x.IsisAreaID - } - return "" -} - -func (x *LsNode) GetHostname() string { - if x != nil { - return x.Hostname - } - return "" -} - -func (x *LsNode) GetSrgbBegin() uint32 { - if x != nil { - return x.SrgbBegin - } - return 0 -} - -func (x *LsNode) GetSrgbEnd() uint32 { - if x != nil { - return x.SrgbEnd - } - return 0 -} - -func (x *LsNode) GetLsLinks() []*LsLink { - if x != nil { - return x.LsLinks - } - return nil -} - -func (x *LsNode) GetLsPrefixes() []*LsPrefix { - if x != nil { - return x.LsPrefixes - } - return nil -} - -type TED struct { - state protoimpl.MessageState `protogen:"open.v1"` - Enable bool `protobuf:"varint,1,opt,name=enable,proto3" json:"enable,omitempty"` - LsNodes []*LsNode `protobuf:"bytes,2,rep,name=lsNodes,proto3" json:"lsNodes,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *TED) Reset() { - *x = TED{} - mi := &file_pola_proto_msgTypes[12] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *TED) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TED) ProtoMessage() {} - -func (x *TED) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[12] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TED.ProtoReflect.Descriptor instead. -func (*TED) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{12} -} - -func (x *TED) GetEnable() bool { - if x != nil { - return x.Enable - } - return false -} - -func (x *TED) GetLsNodes() []*LsNode { - if x != nil { - return x.LsNodes - } - return nil -} - -var File_pola_proto protoreflect.FileDescriptor - -var file_pola_proto_rawDesc = string([]byte{ - 0x0a, 0x0a, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x02, 0x70, 0x62, - 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x7d, 0x0a, - 0x07, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x73, 0x69, 0x64, 0x12, 0x22, 0x0a, 0x0c, 0x73, 0x69, - 0x64, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0c, 0x73, 0x69, 0x64, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1c, - 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x41, 0x64, 0x64, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x41, 0x64, 0x64, 0x72, 0x12, 0x1e, 0x0a, 0x0a, - 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x64, 0x64, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x64, 0x64, 0x72, 0x22, 0xff, 0x02, 0x0a, - 0x08, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x50, 0x43, 0x45, - 0x50, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x0f, 0x50, 0x43, 0x45, 0x50, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x41, - 0x64, 0x64, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x72, 0x63, 0x41, 0x64, 0x64, 0x72, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x73, 0x72, 0x63, 0x41, 0x64, 0x64, 0x72, 0x12, 0x18, 0x0a, - 0x07, 0x64, 0x73, 0x74, 0x41, 0x64, 0x64, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, - 0x64, 0x73, 0x74, 0x41, 0x64, 0x64, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x72, 0x63, 0x52, 0x6f, - 0x75, 0x74, 0x65, 0x72, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x72, - 0x63, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x73, 0x74, - 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x64, 0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x63, - 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x63, 0x6f, 0x6c, 0x6f, - 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, - 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x24, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x79, 0x70, - 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2d, 0x0a, 0x0b, 0x73, 0x65, 0x67, 0x6d, 0x65, - 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x70, - 0x62, 0x2e, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0b, 0x73, 0x65, 0x67, 0x6d, 0x65, - 0x6e, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, - 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x74, 0x72, - 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x22, 0x51, - 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x28, 0x0a, 0x08, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x52, 0x50, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x08, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, - 0x10, 0x0a, 0x03, 0x61, 0x73, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x61, 0x73, - 0x6e, 0x22, 0x51, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x52, 0x50, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x28, 0x0a, 0x08, 0x53, 0x52, 0x50, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x62, 0x2e, - 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x08, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x73, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x03, 0x61, 0x73, 0x6e, 0x22, 0x2d, 0x0a, 0x0d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x73, 0x53, 0x75, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x53, 0x75, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x22, 0x75, 0x0a, 0x07, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, - 0x0a, 0x04, 0x41, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x41, 0x64, - 0x64, 0x72, 0x12, 0x26, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x52, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x43, 0x61, - 0x70, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x43, 0x61, 0x70, 0x73, 0x12, 0x1a, - 0x0a, 0x08, 0x49, 0x73, 0x53, 0x79, 0x6e, 0x63, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x08, 0x49, 0x73, 0x53, 0x79, 0x6e, 0x63, 0x65, 0x64, 0x22, 0x36, 0x0a, 0x0b, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x08, 0x53, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x70, 0x62, - 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x73, 0x22, 0x3c, 0x0a, 0x0c, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69, - 0x73, 0x74, 0x12, 0x2c, 0x0a, 0x0a, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x52, 0x50, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0a, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, - 0x22, 0x3e, 0x0a, 0x08, 0x4c, 0x73, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x16, 0x0a, 0x06, - 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x72, - 0x65, 0x66, 0x69, 0x78, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x69, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x73, 0x69, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, - 0x22, 0x42, 0x0a, 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x22, 0x0a, 0x04, 0x74, 0x79, - 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x4d, 0x65, - 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x22, 0x84, 0x02, 0x0a, 0x06, 0x4c, 0x73, 0x4c, 0x69, 0x6e, 0x6b, 0x12, - 0x24, 0x0a, 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x49, 0x44, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x52, 0x6f, 0x75, - 0x74, 0x65, 0x72, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x41, 0x53, - 0x4e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x41, 0x53, - 0x4e, 0x12, 0x18, 0x0a, 0x07, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x50, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x50, 0x12, 0x26, 0x0a, 0x0e, 0x72, - 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x49, 0x44, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, - 0x72, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x53, 0x4e, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x53, - 0x4e, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x50, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x50, 0x12, 0x24, 0x0a, - 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, - 0x2e, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x72, - 0x69, 0x63, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x64, 0x6a, 0x53, 0x49, 0x44, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x06, 0x61, 0x64, 0x6a, 0x53, 0x49, 0x44, 0x22, 0xfe, 0x01, 0x0a, 0x06, - 0x4c, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x73, 0x6e, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x03, 0x61, 0x73, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x6f, 0x75, 0x74, - 0x65, 0x72, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x6f, 0x75, 0x74, - 0x65, 0x72, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x73, 0x69, 0x73, 0x41, 0x72, 0x65, 0x61, - 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x73, 0x69, 0x73, 0x41, 0x72, - 0x65, 0x61, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x72, 0x67, 0x62, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x09, 0x73, 0x72, 0x67, 0x62, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x12, 0x18, - 0x0a, 0x07, 0x73, 0x72, 0x67, 0x62, 0x45, 0x6e, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x07, 0x73, 0x72, 0x67, 0x62, 0x45, 0x6e, 0x64, 0x12, 0x24, 0x0a, 0x07, 0x6c, 0x73, 0x4c, 0x69, - 0x6e, 0x6b, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x70, 0x62, 0x2e, 0x4c, - 0x73, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x07, 0x6c, 0x73, 0x4c, 0x69, 0x6e, 0x6b, 0x73, 0x12, 0x2c, - 0x0a, 0x0a, 0x6c, 0x73, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x4c, 0x73, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, - 0x52, 0x0a, 0x6c, 0x73, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, 0x73, 0x22, 0x43, 0x0a, 0x03, - 0x54, 0x45, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x24, 0x0a, 0x07, 0x6c, - 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x70, - 0x62, 0x2e, 0x4c, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x07, 0x6c, 0x73, 0x4e, 0x6f, 0x64, 0x65, - 0x73, 0x2a, 0x29, 0x0a, 0x0c, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x79, 0x70, - 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x45, 0x58, 0x50, 0x4c, 0x49, 0x43, 0x49, 0x54, 0x10, 0x00, 0x12, - 0x0b, 0x0a, 0x07, 0x44, 0x59, 0x4e, 0x41, 0x4d, 0x49, 0x43, 0x10, 0x01, 0x2a, 0x20, 0x0a, 0x0c, - 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x08, 0x0a, 0x04, - 0x44, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x06, 0x0a, 0x02, 0x55, 0x50, 0x10, 0x01, 0x2a, 0x36, - 0x0a, 0x0a, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x12, 0x07, 0x0a, 0x03, - 0x49, 0x47, 0x50, 0x10, 0x00, 0x12, 0x06, 0x0a, 0x02, 0x54, 0x45, 0x10, 0x01, 0x12, 0x09, 0x0a, - 0x05, 0x44, 0x45, 0x4c, 0x41, 0x59, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x48, 0x4f, 0x50, 0x43, - 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x03, 0x32, 0x88, 0x04, 0x0a, 0x0a, 0x50, 0x43, 0x45, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3e, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, - 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x17, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x6e, 0x70, 0x75, 0x74, - 0x1a, 0x11, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x1e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, - 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x57, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x4c, 0x69, - 0x6e, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x17, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x6e, 0x70, 0x75, 0x74, - 0x1a, 0x11, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, - 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x17, 0x2e, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x6e, 0x70, 0x75, 0x74, - 0x1a, 0x11, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x1e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, - 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x57, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x4c, 0x69, - 0x6e, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x17, 0x2e, 0x70, 0x62, 0x2e, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x49, 0x6e, 0x70, 0x75, 0x74, - 0x1a, 0x11, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, - 0x0f, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, - 0x22, 0x00, 0x12, 0x3d, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x10, 0x2e, - 0x70, 0x62, 0x2e, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x22, - 0x00, 0x12, 0x2b, 0x0a, 0x06, 0x47, 0x65, 0x74, 0x54, 0x45, 0x44, 0x12, 0x16, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, - 0x70, 0x74, 0x79, 0x1a, 0x07, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x45, 0x44, 0x22, 0x00, 0x12, 0x31, - 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, - 0x0b, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0x11, 0x2e, 0x70, - 0x62, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, - 0x00, 0x42, 0x21, 0x5a, 0x1f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x6e, 0x74, 0x74, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6f, 0x6c, 0x61, 0x2f, 0x61, 0x70, 0x69, 0x2f, - 0x67, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -}) - -var ( - file_pola_proto_rawDescOnce sync.Once - file_pola_proto_rawDescData []byte -) - -func file_pola_proto_rawDescGZIP() []byte { - file_pola_proto_rawDescOnce.Do(func() { - file_pola_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_pola_proto_rawDesc), len(file_pola_proto_rawDesc))) - }) - return file_pola_proto_rawDescData -} - -var file_pola_proto_enumTypes = make([]protoimpl.EnumInfo, 3) -var file_pola_proto_msgTypes = make([]protoimpl.MessageInfo, 13) -var file_pola_proto_goTypes = []any{ - (SRPolicyType)(0), // 0: pb.SRPolicyType - (SessionState)(0), // 1: pb.SessionState - (MetricType)(0), // 2: pb.MetricType - (*Segment)(nil), // 3: pb.Segment - (*SRPolicy)(nil), // 4: pb.SRPolicy - (*CreateSRPolicyInput)(nil), // 5: pb.CreateSRPolicyInput - (*DeleteSRPolicyInput)(nil), // 6: pb.DeleteSRPolicyInput - (*RequestStatus)(nil), // 7: pb.RequestStatus - (*Session)(nil), // 8: pb.Session - (*SessionList)(nil), // 9: pb.SessionList - (*SRPolicyList)(nil), // 10: pb.SRPolicyList - (*LsPrefix)(nil), // 11: pb.LsPrefix - (*Metric)(nil), // 12: pb.Metric - (*LsLink)(nil), // 13: pb.LsLink - (*LsNode)(nil), // 14: pb.LsNode - (*TED)(nil), // 15: pb.TED - (*emptypb.Empty)(nil), // 16: google.protobuf.Empty -} -var file_pola_proto_depIdxs = []int32{ - 0, // 0: pb.SRPolicy.type:type_name -> pb.SRPolicyType - 3, // 1: pb.SRPolicy.segmentList:type_name -> pb.Segment - 2, // 2: pb.SRPolicy.metric:type_name -> pb.MetricType - 4, // 3: pb.CreateSRPolicyInput.SRPolicy:type_name -> pb.SRPolicy - 4, // 4: pb.DeleteSRPolicyInput.SRPolicy:type_name -> pb.SRPolicy - 1, // 5: pb.Session.State:type_name -> pb.SessionState - 8, // 6: pb.SessionList.Sessions:type_name -> pb.Session - 4, // 7: pb.SRPolicyList.SRPolicies:type_name -> pb.SRPolicy - 2, // 8: pb.Metric.type:type_name -> pb.MetricType - 12, // 9: pb.LsLink.metrics:type_name -> pb.Metric - 13, // 10: pb.LsNode.lsLinks:type_name -> pb.LsLink - 11, // 11: pb.LsNode.lsPrefixes:type_name -> pb.LsPrefix - 14, // 12: pb.TED.lsNodes:type_name -> pb.LsNode - 5, // 13: pb.PCEService.CreateSRPolicy:input_type -> pb.CreateSRPolicyInput - 5, // 14: pb.PCEService.CreateSRPolicyWithoutLinkState:input_type -> pb.CreateSRPolicyInput - 6, // 15: pb.PCEService.DeleteSRPolicy:input_type -> pb.DeleteSRPolicyInput - 6, // 16: pb.PCEService.DeleteSRPolicyWithoutLinkState:input_type -> pb.DeleteSRPolicyInput - 16, // 17: pb.PCEService.GetSessionList:input_type -> google.protobuf.Empty - 16, // 18: pb.PCEService.GetSRPolicyList:input_type -> google.protobuf.Empty - 16, // 19: pb.PCEService.GetTED:input_type -> google.protobuf.Empty - 8, // 20: pb.PCEService.DeleteSession:input_type -> pb.Session - 7, // 21: pb.PCEService.CreateSRPolicy:output_type -> pb.RequestStatus - 7, // 22: pb.PCEService.CreateSRPolicyWithoutLinkState:output_type -> pb.RequestStatus - 7, // 23: pb.PCEService.DeleteSRPolicy:output_type -> pb.RequestStatus - 7, // 24: pb.PCEService.DeleteSRPolicyWithoutLinkState:output_type -> pb.RequestStatus - 9, // 25: pb.PCEService.GetSessionList:output_type -> pb.SessionList - 10, // 26: pb.PCEService.GetSRPolicyList:output_type -> pb.SRPolicyList - 15, // 27: pb.PCEService.GetTED:output_type -> pb.TED - 7, // 28: pb.PCEService.DeleteSession:output_type -> pb.RequestStatus - 21, // [21:29] is the sub-list for method output_type - 13, // [13:21] is the sub-list for method input_type - 13, // [13:13] is the sub-list for extension type_name - 13, // [13:13] is the sub-list for extension extendee - 0, // [0:13] is the sub-list for field type_name -} - -func init() { file_pola_proto_init() } -func file_pola_proto_init() { - if File_pola_proto != nil { - return - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: unsafe.Slice(unsafe.StringData(file_pola_proto_rawDesc), len(file_pola_proto_rawDesc)), - NumEnums: 3, - NumMessages: 13, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_pola_proto_goTypes, - DependencyIndexes: file_pola_proto_depIdxs, - EnumInfos: file_pola_proto_enumTypes, - MessageInfos: file_pola_proto_msgTypes, - }.Build() - File_pola_proto = out.File - file_pola_proto_goTypes = nil - file_pola_proto_depIdxs = nil -} diff --git a/api/grpc/pola.proto b/api/grpc/pola.proto deleted file mode 100644 index ee42958a..00000000 --- a/api/grpc/pola.proto +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (c) 2022 NTT Communications Corporation -// -// This software is released under the MIT License. -// see https://github.com/nttcom/pola/blob/main/LICENSE - -syntax = "proto3"; - -package pb; - -option go_package = "github.com/nttcom/pola/api/grpc"; - -import "google/protobuf/empty.proto"; - -service PCEService { - rpc CreateSRPolicy (CreateSRPolicyInput) returns (RequestStatus) {}; - rpc CreateSRPolicyWithoutLinkState (CreateSRPolicyInput) returns (RequestStatus) {}; - rpc DeleteSRPolicy (DeleteSRPolicyInput) returns (RequestStatus) {}; - rpc DeleteSRPolicyWithoutLinkState (DeleteSRPolicyInput) returns (RequestStatus) {}; - rpc GetSessionList (google.protobuf.Empty) returns (SessionList) {}; - rpc GetSRPolicyList (google.protobuf.Empty) returns (SRPolicyList) {}; - rpc GetTED (google.protobuf.Empty) returns (TED) {}; - rpc DeleteSession (Session) returns (RequestStatus) {}; -} - -message Segment { - string sid = 1; - string sidStructure = 2; - string localAddr = 3; - string remoteAddr = 4; -} - -enum SRPolicyType { - EXPLICIT = 0; - DYNAMIC = 1; -} - -message SRPolicy { - bytes PCEPSessionAddr = 1; - bytes srcAddr = 2; - bytes dstAddr = 3; - string srcRouterID = 4; - string dstRouterID = 5; - uint32 color = 6; - uint32 preference = 7; - string policyName = 8; - SRPolicyType type = 9; - repeated Segment segmentList = 10; - MetricType metric = 11; -} - -message CreateSRPolicyInput { - SRPolicy SRPolicy = 1; - uint32 asn = 2; -} - -message DeleteSRPolicyInput { - SRPolicy SRPolicy = 1; - uint32 asn = 2; -} - -message RequestStatus { - bool isSuccess = 1; -} - -enum SessionState { - DOWN = 0; - UP = 1; -} - -message Session { - bytes Addr = 1; - SessionState State = 2; - repeated string Caps = 3; - bool IsSynced = 4; -} - -message SessionList { - repeated Session Sessions = 1; -} - -message SRPolicyList { - repeated SRPolicy SRPolicies = 1; -} - -message LsPrefix { - string prefix = 1; - uint32 sidIndex = 2; -} - -enum MetricType { - IGP = 0; - TE = 1; - DELAY = 2; - HOPCOUNT = 3; -} - -message Metric { - MetricType type = 1; - uint32 value = 2; -} - -message LsLink { - string localRouterID = 1; - uint32 localASN = 2; - string localIP = 3; - string remoteRouterID = 4; - uint32 remoteASN = 5; - string remoteIP = 6; - repeated Metric metrics = 7; - uint32 adjSID = 8; -} - -message LsNode { - uint32 asn = 1; - string routerID = 2; - string isisAreaID = 3; - string hostname = 4; - uint32 srgbBegin = 5; - uint32 srgbEnd = 6; - repeated LsLink lsLinks = 7; - repeated LsPrefix lsPrefixes = 8; -} - - -message TED { - bool enable = 1; - repeated LsNode lsNodes = 2; -} diff --git a/api/pola/v1/pola.pb.go b/api/pola/v1/pola.pb.go new file mode 100644 index 00000000..c56ab5d0 --- /dev/null +++ b/api/pola/v1/pola.pb.go @@ -0,0 +1,2063 @@ +// Copyright (c) 2022 NTT Communications Corporation +// +// This software is released under the MIT License. +// see https://github.com/nttcom/pola/blob/main/LICENSE + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc v3.12.4 +// source: pola.proto + +package v1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type SRPolicyType int32 + +const ( + SRPolicyType_SR_POLICY_TYPE_UNSPECIFIED SRPolicyType = 0 + SRPolicyType_SR_POLICY_TYPE_EXPLICIT SRPolicyType = 1 + SRPolicyType_SR_POLICY_TYPE_DYNAMIC SRPolicyType = 2 +) + +// Enum value maps for SRPolicyType. +var ( + SRPolicyType_name = map[int32]string{ + 0: "SR_POLICY_TYPE_UNSPECIFIED", + 1: "SR_POLICY_TYPE_EXPLICIT", + 2: "SR_POLICY_TYPE_DYNAMIC", + } + SRPolicyType_value = map[string]int32{ + "SR_POLICY_TYPE_UNSPECIFIED": 0, + "SR_POLICY_TYPE_EXPLICIT": 1, + "SR_POLICY_TYPE_DYNAMIC": 2, + } +) + +func (x SRPolicyType) Enum() *SRPolicyType { + p := new(SRPolicyType) + *p = x + return p +} + +func (x SRPolicyType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (SRPolicyType) Descriptor() protoreflect.EnumDescriptor { + return file_pola_proto_enumTypes[0].Descriptor() +} + +func (SRPolicyType) Type() protoreflect.EnumType { + return &file_pola_proto_enumTypes[0] +} + +func (x SRPolicyType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use SRPolicyType.Descriptor instead. +func (SRPolicyType) EnumDescriptor() ([]byte, []int) { + return file_pola_proto_rawDescGZIP(), []int{0} +} + +type SessionState int32 + +const ( + SessionState_SESSION_STATE_UNSPECIFIED SessionState = 0 + SessionState_SESSION_STATE_DOWN SessionState = 1 + SessionState_SESSION_STATE_UP SessionState = 2 +) + +// Enum value maps for SessionState. +var ( + SessionState_name = map[int32]string{ + 0: "SESSION_STATE_UNSPECIFIED", + 1: "SESSION_STATE_DOWN", + 2: "SESSION_STATE_UP", + } + SessionState_value = map[string]int32{ + "SESSION_STATE_UNSPECIFIED": 0, + "SESSION_STATE_DOWN": 1, + "SESSION_STATE_UP": 2, + } +) + +func (x SessionState) Enum() *SessionState { + p := new(SessionState) + *p = x + return p +} + +func (x SessionState) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (SessionState) Descriptor() protoreflect.EnumDescriptor { + return file_pola_proto_enumTypes[1].Descriptor() +} + +func (SessionState) Type() protoreflect.EnumType { + return &file_pola_proto_enumTypes[1] +} + +func (x SessionState) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use SessionState.Descriptor instead. +func (SessionState) EnumDescriptor() ([]byte, []int) { + return file_pola_proto_rawDescGZIP(), []int{1} +} + +type MetricType int32 + +const ( + MetricType_METRIC_TYPE_UNSPECIFIED MetricType = 0 + MetricType_METRIC_TYPE_IGP MetricType = 1 + MetricType_METRIC_TYPE_TE MetricType = 2 + MetricType_METRIC_TYPE_DELAY MetricType = 3 + MetricType_METRIC_TYPE_HOPCOUNT MetricType = 4 +) + +// Enum value maps for MetricType. +var ( + MetricType_name = map[int32]string{ + 0: "METRIC_TYPE_UNSPECIFIED", + 1: "METRIC_TYPE_IGP", + 2: "METRIC_TYPE_TE", + 3: "METRIC_TYPE_DELAY", + 4: "METRIC_TYPE_HOPCOUNT", + } + MetricType_value = map[string]int32{ + "METRIC_TYPE_UNSPECIFIED": 0, + "METRIC_TYPE_IGP": 1, + "METRIC_TYPE_TE": 2, + "METRIC_TYPE_DELAY": 3, + "METRIC_TYPE_HOPCOUNT": 4, + } +) + +func (x MetricType) Enum() *MetricType { + p := new(MetricType) + *p = x + return p +} + +func (x MetricType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (MetricType) Descriptor() protoreflect.EnumDescriptor { + return file_pola_proto_enumTypes[2].Descriptor() +} + +func (MetricType) Type() protoreflect.EnumType { + return &file_pola_proto_enumTypes[2] +} + +func (x MetricType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use MetricType.Descriptor instead. +func (MetricType) EnumDescriptor() ([]byte, []int) { + return file_pola_proto_rawDescGZIP(), []int{2} +} + +type Segment struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Sid string `protobuf:"bytes,1,opt,name=sid,proto3" json:"sid,omitempty"` + SidStructure string `protobuf:"bytes,2,opt,name=sid_structure,json=sidStructure,proto3" json:"sid_structure,omitempty"` + LocalAddr string `protobuf:"bytes,3,opt,name=local_addr,json=localAddr,proto3" json:"local_addr,omitempty"` + RemoteAddr string `protobuf:"bytes,4,opt,name=remote_addr,json=remoteAddr,proto3" json:"remote_addr,omitempty"` +} + +func (x *Segment) Reset() { + *x = Segment{} + if protoimpl.UnsafeEnabled { + mi := &file_pola_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Segment) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Segment) ProtoMessage() {} + +func (x *Segment) ProtoReflect() protoreflect.Message { + mi := &file_pola_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Segment.ProtoReflect.Descriptor instead. +func (*Segment) Descriptor() ([]byte, []int) { + return file_pola_proto_rawDescGZIP(), []int{0} +} + +func (x *Segment) GetSid() string { + if x != nil { + return x.Sid + } + return "" +} + +func (x *Segment) GetSidStructure() string { + if x != nil { + return x.SidStructure + } + return "" +} + +func (x *Segment) GetLocalAddr() string { + if x != nil { + return x.LocalAddr + } + return "" +} + +func (x *Segment) GetRemoteAddr() string { + if x != nil { + return x.RemoteAddr + } + return "" +} + +type SRPolicy struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PcepSessionAddr []byte `protobuf:"bytes,1,opt,name=pcep_session_addr,json=pcepSessionAddr,proto3" json:"pcep_session_addr,omitempty"` + SrcAddr []byte `protobuf:"bytes,2,opt,name=src_addr,json=srcAddr,proto3" json:"src_addr,omitempty"` + DstAddr []byte `protobuf:"bytes,3,opt,name=dst_addr,json=dstAddr,proto3" json:"dst_addr,omitempty"` + SrcRouterId string `protobuf:"bytes,4,opt,name=src_router_id,json=srcRouterId,proto3" json:"src_router_id,omitempty"` + DstRouterId string `protobuf:"bytes,5,opt,name=dst_router_id,json=dstRouterId,proto3" json:"dst_router_id,omitempty"` + Color uint32 `protobuf:"varint,6,opt,name=color,proto3" json:"color,omitempty"` + Preference uint32 `protobuf:"varint,7,opt,name=preference,proto3" json:"preference,omitempty"` + PolicyName string `protobuf:"bytes,8,opt,name=policy_name,json=policyName,proto3" json:"policy_name,omitempty"` + Type SRPolicyType `protobuf:"varint,9,opt,name=type,proto3,enum=api.pola.v1.SRPolicyType" json:"type,omitempty"` + SegmentList []*Segment `protobuf:"bytes,10,rep,name=segment_list,json=segmentList,proto3" json:"segment_list,omitempty"` + Metric MetricType `protobuf:"varint,11,opt,name=metric,proto3,enum=api.pola.v1.MetricType" json:"metric,omitempty"` +} + +func (x *SRPolicy) Reset() { + *x = SRPolicy{} + if protoimpl.UnsafeEnabled { + mi := &file_pola_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SRPolicy) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SRPolicy) ProtoMessage() {} + +func (x *SRPolicy) ProtoReflect() protoreflect.Message { + mi := &file_pola_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SRPolicy.ProtoReflect.Descriptor instead. +func (*SRPolicy) Descriptor() ([]byte, []int) { + return file_pola_proto_rawDescGZIP(), []int{1} +} + +func (x *SRPolicy) GetPcepSessionAddr() []byte { + if x != nil { + return x.PcepSessionAddr + } + return nil +} + +func (x *SRPolicy) GetSrcAddr() []byte { + if x != nil { + return x.SrcAddr + } + return nil +} + +func (x *SRPolicy) GetDstAddr() []byte { + if x != nil { + return x.DstAddr + } + return nil +} + +func (x *SRPolicy) GetSrcRouterId() string { + if x != nil { + return x.SrcRouterId + } + return "" +} + +func (x *SRPolicy) GetDstRouterId() string { + if x != nil { + return x.DstRouterId + } + return "" +} + +func (x *SRPolicy) GetColor() uint32 { + if x != nil { + return x.Color + } + return 0 +} + +func (x *SRPolicy) GetPreference() uint32 { + if x != nil { + return x.Preference + } + return 0 +} + +func (x *SRPolicy) GetPolicyName() string { + if x != nil { + return x.PolicyName + } + return "" +} + +func (x *SRPolicy) GetType() SRPolicyType { + if x != nil { + return x.Type + } + return SRPolicyType_SR_POLICY_TYPE_UNSPECIFIED +} + +func (x *SRPolicy) GetSegmentList() []*Segment { + if x != nil { + return x.SegmentList + } + return nil +} + +func (x *SRPolicy) GetMetric() MetricType { + if x != nil { + return x.Metric + } + return MetricType_METRIC_TYPE_UNSPECIFIED +} + +type CreateSRPolicyRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SrPolicy *SRPolicy `protobuf:"bytes,1,opt,name=sr_policy,json=srPolicy,proto3" json:"sr_policy,omitempty"` + Asn uint32 `protobuf:"varint,2,opt,name=asn,proto3" json:"asn,omitempty"` + SidValidate bool `protobuf:"varint,3,opt,name=sid_validate,json=sidValidate,proto3" json:"sid_validate,omitempty"` +} + +func (x *CreateSRPolicyRequest) Reset() { + *x = CreateSRPolicyRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_pola_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateSRPolicyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateSRPolicyRequest) ProtoMessage() {} + +func (x *CreateSRPolicyRequest) ProtoReflect() protoreflect.Message { + mi := &file_pola_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateSRPolicyRequest.ProtoReflect.Descriptor instead. +func (*CreateSRPolicyRequest) Descriptor() ([]byte, []int) { + return file_pola_proto_rawDescGZIP(), []int{2} +} + +func (x *CreateSRPolicyRequest) GetSrPolicy() *SRPolicy { + if x != nil { + return x.SrPolicy + } + return nil +} + +func (x *CreateSRPolicyRequest) GetAsn() uint32 { + if x != nil { + return x.Asn + } + return 0 +} + +func (x *CreateSRPolicyRequest) GetSidValidate() bool { + if x != nil { + return x.SidValidate + } + return false +} + +type CreateSRPolicyResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + IsSuccess bool `protobuf:"varint,1,opt,name=is_success,json=isSuccess,proto3" json:"is_success,omitempty"` +} + +func (x *CreateSRPolicyResponse) Reset() { + *x = CreateSRPolicyResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_pola_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateSRPolicyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateSRPolicyResponse) ProtoMessage() {} + +func (x *CreateSRPolicyResponse) ProtoReflect() protoreflect.Message { + mi := &file_pola_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateSRPolicyResponse.ProtoReflect.Descriptor instead. +func (*CreateSRPolicyResponse) Descriptor() ([]byte, []int) { + return file_pola_proto_rawDescGZIP(), []int{3} +} + +func (x *CreateSRPolicyResponse) GetIsSuccess() bool { + if x != nil { + return x.IsSuccess + } + return false +} + +type DeleteSRPolicyRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SrPolicy *SRPolicy `protobuf:"bytes,1,opt,name=sr_policy,json=srPolicy,proto3" json:"sr_policy,omitempty"` + Asn uint32 `protobuf:"varint,2,opt,name=asn,proto3" json:"asn,omitempty"` +} + +func (x *DeleteSRPolicyRequest) Reset() { + *x = DeleteSRPolicyRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_pola_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteSRPolicyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteSRPolicyRequest) ProtoMessage() {} + +func (x *DeleteSRPolicyRequest) ProtoReflect() protoreflect.Message { + mi := &file_pola_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteSRPolicyRequest.ProtoReflect.Descriptor instead. +func (*DeleteSRPolicyRequest) Descriptor() ([]byte, []int) { + return file_pola_proto_rawDescGZIP(), []int{4} +} + +func (x *DeleteSRPolicyRequest) GetSrPolicy() *SRPolicy { + if x != nil { + return x.SrPolicy + } + return nil +} + +func (x *DeleteSRPolicyRequest) GetAsn() uint32 { + if x != nil { + return x.Asn + } + return 0 +} + +type DeleteSRPolicyResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + IsSuccess bool `protobuf:"varint,1,opt,name=is_success,json=isSuccess,proto3" json:"is_success,omitempty"` +} + +func (x *DeleteSRPolicyResponse) Reset() { + *x = DeleteSRPolicyResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_pola_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteSRPolicyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteSRPolicyResponse) ProtoMessage() {} + +func (x *DeleteSRPolicyResponse) ProtoReflect() protoreflect.Message { + mi := &file_pola_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteSRPolicyResponse.ProtoReflect.Descriptor instead. +func (*DeleteSRPolicyResponse) Descriptor() ([]byte, []int) { + return file_pola_proto_rawDescGZIP(), []int{5} +} + +func (x *DeleteSRPolicyResponse) GetIsSuccess() bool { + if x != nil { + return x.IsSuccess + } + return false +} + +type Session struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Addr []byte `protobuf:"bytes,1,opt,name=addr,proto3" json:"addr,omitempty"` + State SessionState `protobuf:"varint,2,opt,name=state,proto3,enum=api.pola.v1.SessionState" json:"state,omitempty"` + Caps []string `protobuf:"bytes,3,rep,name=caps,proto3" json:"caps,omitempty"` + IsSynced bool `protobuf:"varint,4,opt,name=is_synced,json=isSynced,proto3" json:"is_synced,omitempty"` +} + +func (x *Session) Reset() { + *x = Session{} + if protoimpl.UnsafeEnabled { + mi := &file_pola_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Session) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Session) ProtoMessage() {} + +func (x *Session) ProtoReflect() protoreflect.Message { + mi := &file_pola_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Session.ProtoReflect.Descriptor instead. +func (*Session) Descriptor() ([]byte, []int) { + return file_pola_proto_rawDescGZIP(), []int{6} +} + +func (x *Session) GetAddr() []byte { + if x != nil { + return x.Addr + } + return nil +} + +func (x *Session) GetState() SessionState { + if x != nil { + return x.State + } + return SessionState_SESSION_STATE_UNSPECIFIED +} + +func (x *Session) GetCaps() []string { + if x != nil { + return x.Caps + } + return nil +} + +func (x *Session) GetIsSynced() bool { + if x != nil { + return x.IsSynced + } + return false +} + +type SessionList struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Sessions []*Session `protobuf:"bytes,1,rep,name=sessions,proto3" json:"sessions,omitempty"` +} + +func (x *SessionList) Reset() { + *x = SessionList{} + if protoimpl.UnsafeEnabled { + mi := &file_pola_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SessionList) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SessionList) ProtoMessage() {} + +func (x *SessionList) ProtoReflect() protoreflect.Message { + mi := &file_pola_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SessionList.ProtoReflect.Descriptor instead. +func (*SessionList) Descriptor() ([]byte, []int) { + return file_pola_proto_rawDescGZIP(), []int{7} +} + +func (x *SessionList) GetSessions() []*Session { + if x != nil { + return x.Sessions + } + return nil +} + +type SRPolicyList struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SrPolicies []*SRPolicy `protobuf:"bytes,1,rep,name=sr_policies,json=srPolicies,proto3" json:"sr_policies,omitempty"` +} + +func (x *SRPolicyList) Reset() { + *x = SRPolicyList{} + if protoimpl.UnsafeEnabled { + mi := &file_pola_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SRPolicyList) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SRPolicyList) ProtoMessage() {} + +func (x *SRPolicyList) ProtoReflect() protoreflect.Message { + mi := &file_pola_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SRPolicyList.ProtoReflect.Descriptor instead. +func (*SRPolicyList) Descriptor() ([]byte, []int) { + return file_pola_proto_rawDescGZIP(), []int{8} +} + +func (x *SRPolicyList) GetSrPolicies() []*SRPolicy { + if x != nil { + return x.SrPolicies + } + return nil +} + +type LsPrefix struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Prefix string `protobuf:"bytes,1,opt,name=prefix,proto3" json:"prefix,omitempty"` + SidIndex uint32 `protobuf:"varint,2,opt,name=sid_index,json=sidIndex,proto3" json:"sid_index,omitempty"` +} + +func (x *LsPrefix) Reset() { + *x = LsPrefix{} + if protoimpl.UnsafeEnabled { + mi := &file_pola_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LsPrefix) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LsPrefix) ProtoMessage() {} + +func (x *LsPrefix) ProtoReflect() protoreflect.Message { + mi := &file_pola_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LsPrefix.ProtoReflect.Descriptor instead. +func (*LsPrefix) Descriptor() ([]byte, []int) { + return file_pola_proto_rawDescGZIP(), []int{9} +} + +func (x *LsPrefix) GetPrefix() string { + if x != nil { + return x.Prefix + } + return "" +} + +func (x *LsPrefix) GetSidIndex() uint32 { + if x != nil { + return x.SidIndex + } + return 0 +} + +type Metric struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Type MetricType `protobuf:"varint,1,opt,name=type,proto3,enum=api.pola.v1.MetricType" json:"type,omitempty"` + Value uint32 `protobuf:"varint,2,opt,name=value,proto3" json:"value,omitempty"` +} + +func (x *Metric) Reset() { + *x = Metric{} + if protoimpl.UnsafeEnabled { + mi := &file_pola_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Metric) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Metric) ProtoMessage() {} + +func (x *Metric) ProtoReflect() protoreflect.Message { + mi := &file_pola_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Metric.ProtoReflect.Descriptor instead. +func (*Metric) Descriptor() ([]byte, []int) { + return file_pola_proto_rawDescGZIP(), []int{10} +} + +func (x *Metric) GetType() MetricType { + if x != nil { + return x.Type + } + return MetricType_METRIC_TYPE_UNSPECIFIED +} + +func (x *Metric) GetValue() uint32 { + if x != nil { + return x.Value + } + return 0 +} + +type LsLink struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + LocalRouterId string `protobuf:"bytes,1,opt,name=local_router_id,json=localRouterId,proto3" json:"local_router_id,omitempty"` + LocalAsn uint32 `protobuf:"varint,2,opt,name=local_asn,json=localAsn,proto3" json:"local_asn,omitempty"` + LocalIp string `protobuf:"bytes,3,opt,name=local_ip,json=localIp,proto3" json:"local_ip,omitempty"` + RemoteRouterId string `protobuf:"bytes,4,opt,name=remote_router_id,json=remoteRouterId,proto3" json:"remote_router_id,omitempty"` + RemoteAsn uint32 `protobuf:"varint,5,opt,name=remote_asn,json=remoteAsn,proto3" json:"remote_asn,omitempty"` + RemoteIp string `protobuf:"bytes,6,opt,name=remote_ip,json=remoteIp,proto3" json:"remote_ip,omitempty"` + Metrics []*Metric `protobuf:"bytes,7,rep,name=metrics,proto3" json:"metrics,omitempty"` + AdjSid uint32 `protobuf:"varint,8,opt,name=adj_sid,json=adjSid,proto3" json:"adj_sid,omitempty"` +} + +func (x *LsLink) Reset() { + *x = LsLink{} + if protoimpl.UnsafeEnabled { + mi := &file_pola_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LsLink) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LsLink) ProtoMessage() {} + +func (x *LsLink) ProtoReflect() protoreflect.Message { + mi := &file_pola_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LsLink.ProtoReflect.Descriptor instead. +func (*LsLink) Descriptor() ([]byte, []int) { + return file_pola_proto_rawDescGZIP(), []int{11} +} + +func (x *LsLink) GetLocalRouterId() string { + if x != nil { + return x.LocalRouterId + } + return "" +} + +func (x *LsLink) GetLocalAsn() uint32 { + if x != nil { + return x.LocalAsn + } + return 0 +} + +func (x *LsLink) GetLocalIp() string { + if x != nil { + return x.LocalIp + } + return "" +} + +func (x *LsLink) GetRemoteRouterId() string { + if x != nil { + return x.RemoteRouterId + } + return "" +} + +func (x *LsLink) GetRemoteAsn() uint32 { + if x != nil { + return x.RemoteAsn + } + return 0 +} + +func (x *LsLink) GetRemoteIp() string { + if x != nil { + return x.RemoteIp + } + return "" +} + +func (x *LsLink) GetMetrics() []*Metric { + if x != nil { + return x.Metrics + } + return nil +} + +func (x *LsLink) GetAdjSid() uint32 { + if x != nil { + return x.AdjSid + } + return 0 +} + +type LsNode struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Asn uint32 `protobuf:"varint,1,opt,name=asn,proto3" json:"asn,omitempty"` + RouterId string `protobuf:"bytes,2,opt,name=router_id,json=routerId,proto3" json:"router_id,omitempty"` + IsisAreaId string `protobuf:"bytes,3,opt,name=isis_area_id,json=isisAreaId,proto3" json:"isis_area_id,omitempty"` + Hostname string `protobuf:"bytes,4,opt,name=hostname,proto3" json:"hostname,omitempty"` + SrgbBegin uint32 `protobuf:"varint,5,opt,name=srgb_begin,json=srgbBegin,proto3" json:"srgb_begin,omitempty"` + SrgbEnd uint32 `protobuf:"varint,6,opt,name=srgb_end,json=srgbEnd,proto3" json:"srgb_end,omitempty"` + LsLinks []*LsLink `protobuf:"bytes,7,rep,name=ls_links,json=lsLinks,proto3" json:"ls_links,omitempty"` + LsPrefixes []*LsPrefix `protobuf:"bytes,8,rep,name=ls_prefixes,json=lsPrefixes,proto3" json:"ls_prefixes,omitempty"` +} + +func (x *LsNode) Reset() { + *x = LsNode{} + if protoimpl.UnsafeEnabled { + mi := &file_pola_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LsNode) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LsNode) ProtoMessage() {} + +func (x *LsNode) ProtoReflect() protoreflect.Message { + mi := &file_pola_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LsNode.ProtoReflect.Descriptor instead. +func (*LsNode) Descriptor() ([]byte, []int) { + return file_pola_proto_rawDescGZIP(), []int{12} +} + +func (x *LsNode) GetAsn() uint32 { + if x != nil { + return x.Asn + } + return 0 +} + +func (x *LsNode) GetRouterId() string { + if x != nil { + return x.RouterId + } + return "" +} + +func (x *LsNode) GetIsisAreaId() string { + if x != nil { + return x.IsisAreaId + } + return "" +} + +func (x *LsNode) GetHostname() string { + if x != nil { + return x.Hostname + } + return "" +} + +func (x *LsNode) GetSrgbBegin() uint32 { + if x != nil { + return x.SrgbBegin + } + return 0 +} + +func (x *LsNode) GetSrgbEnd() uint32 { + if x != nil { + return x.SrgbEnd + } + return 0 +} + +func (x *LsNode) GetLsLinks() []*LsLink { + if x != nil { + return x.LsLinks + } + return nil +} + +func (x *LsNode) GetLsPrefixes() []*LsPrefix { + if x != nil { + return x.LsPrefixes + } + return nil +} + +type TED struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Enable bool `protobuf:"varint,1,opt,name=enable,proto3" json:"enable,omitempty"` + LsNodes []*LsNode `protobuf:"bytes,2,rep,name=ls_nodes,json=lsNodes,proto3" json:"ls_nodes,omitempty"` +} + +func (x *TED) Reset() { + *x = TED{} + if protoimpl.UnsafeEnabled { + mi := &file_pola_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TED) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TED) ProtoMessage() {} + +func (x *TED) ProtoReflect() protoreflect.Message { + mi := &file_pola_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TED.ProtoReflect.Descriptor instead. +func (*TED) Descriptor() ([]byte, []int) { + return file_pola_proto_rawDescGZIP(), []int{13} +} + +func (x *TED) GetEnable() bool { + if x != nil { + return x.Enable + } + return false +} + +func (x *TED) GetLsNodes() []*LsNode { + if x != nil { + return x.LsNodes + } + return nil +} + +type GetSessionListRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *GetSessionListRequest) Reset() { + *x = GetSessionListRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_pola_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetSessionListRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetSessionListRequest) ProtoMessage() {} + +func (x *GetSessionListRequest) ProtoReflect() protoreflect.Message { + mi := &file_pola_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetSessionListRequest.ProtoReflect.Descriptor instead. +func (*GetSessionListRequest) Descriptor() ([]byte, []int) { + return file_pola_proto_rawDescGZIP(), []int{14} +} + +type GetSessionListResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Sessions []*Session `protobuf:"bytes,1,rep,name=sessions,proto3" json:"sessions,omitempty"` +} + +func (x *GetSessionListResponse) Reset() { + *x = GetSessionListResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_pola_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetSessionListResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetSessionListResponse) ProtoMessage() {} + +func (x *GetSessionListResponse) ProtoReflect() protoreflect.Message { + mi := &file_pola_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetSessionListResponse.ProtoReflect.Descriptor instead. +func (*GetSessionListResponse) Descriptor() ([]byte, []int) { + return file_pola_proto_rawDescGZIP(), []int{15} +} + +func (x *GetSessionListResponse) GetSessions() []*Session { + if x != nil { + return x.Sessions + } + return nil +} + +type GetSRPolicyListRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *GetSRPolicyListRequest) Reset() { + *x = GetSRPolicyListRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_pola_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetSRPolicyListRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetSRPolicyListRequest) ProtoMessage() {} + +func (x *GetSRPolicyListRequest) ProtoReflect() protoreflect.Message { + mi := &file_pola_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetSRPolicyListRequest.ProtoReflect.Descriptor instead. +func (*GetSRPolicyListRequest) Descriptor() ([]byte, []int) { + return file_pola_proto_rawDescGZIP(), []int{16} +} + +type GetSRPolicyListResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SrPolicies []*SRPolicy `protobuf:"bytes,1,rep,name=sr_policies,json=srPolicies,proto3" json:"sr_policies,omitempty"` +} + +func (x *GetSRPolicyListResponse) Reset() { + *x = GetSRPolicyListResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_pola_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetSRPolicyListResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetSRPolicyListResponse) ProtoMessage() {} + +func (x *GetSRPolicyListResponse) ProtoReflect() protoreflect.Message { + mi := &file_pola_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetSRPolicyListResponse.ProtoReflect.Descriptor instead. +func (*GetSRPolicyListResponse) Descriptor() ([]byte, []int) { + return file_pola_proto_rawDescGZIP(), []int{17} +} + +func (x *GetSRPolicyListResponse) GetSrPolicies() []*SRPolicy { + if x != nil { + return x.SrPolicies + } + return nil +} + +type GetTEDRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *GetTEDRequest) Reset() { + *x = GetTEDRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_pola_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetTEDRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetTEDRequest) ProtoMessage() {} + +func (x *GetTEDRequest) ProtoReflect() protoreflect.Message { + mi := &file_pola_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetTEDRequest.ProtoReflect.Descriptor instead. +func (*GetTEDRequest) Descriptor() ([]byte, []int) { + return file_pola_proto_rawDescGZIP(), []int{18} +} + +type GetTEDResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Enable bool `protobuf:"varint,1,opt,name=enable,proto3" json:"enable,omitempty"` + LsNodes []*LsNode `protobuf:"bytes,2,rep,name=ls_nodes,json=lsNodes,proto3" json:"ls_nodes,omitempty"` +} + +func (x *GetTEDResponse) Reset() { + *x = GetTEDResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_pola_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetTEDResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetTEDResponse) ProtoMessage() {} + +func (x *GetTEDResponse) ProtoReflect() protoreflect.Message { + mi := &file_pola_proto_msgTypes[19] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetTEDResponse.ProtoReflect.Descriptor instead. +func (*GetTEDResponse) Descriptor() ([]byte, []int) { + return file_pola_proto_rawDescGZIP(), []int{19} +} + +func (x *GetTEDResponse) GetEnable() bool { + if x != nil { + return x.Enable + } + return false +} + +func (x *GetTEDResponse) GetLsNodes() []*LsNode { + if x != nil { + return x.LsNodes + } + return nil +} + +type DeleteSessionRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Addr []byte `protobuf:"bytes,1,opt,name=addr,proto3" json:"addr,omitempty"` +} + +func (x *DeleteSessionRequest) Reset() { + *x = DeleteSessionRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_pola_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteSessionRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteSessionRequest) ProtoMessage() {} + +func (x *DeleteSessionRequest) ProtoReflect() protoreflect.Message { + mi := &file_pola_proto_msgTypes[20] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteSessionRequest.ProtoReflect.Descriptor instead. +func (*DeleteSessionRequest) Descriptor() ([]byte, []int) { + return file_pola_proto_rawDescGZIP(), []int{20} +} + +func (x *DeleteSessionRequest) GetAddr() []byte { + if x != nil { + return x.Addr + } + return nil +} + +type DeleteSessionResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + IsSuccess bool `protobuf:"varint,1,opt,name=is_success,json=isSuccess,proto3" json:"is_success,omitempty"` +} + +func (x *DeleteSessionResponse) Reset() { + *x = DeleteSessionResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_pola_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteSessionResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteSessionResponse) ProtoMessage() {} + +func (x *DeleteSessionResponse) ProtoReflect() protoreflect.Message { + mi := &file_pola_proto_msgTypes[21] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteSessionResponse.ProtoReflect.Descriptor instead. +func (*DeleteSessionResponse) Descriptor() ([]byte, []int) { + return file_pola_proto_rawDescGZIP(), []int{21} +} + +func (x *DeleteSessionResponse) GetIsSuccess() bool { + if x != nil { + return x.IsSuccess + } + return false +} + +var File_pola_proto protoreflect.FileDescriptor + +var file_pola_proto_rawDesc = []byte{ + 0x0a, 0x0a, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0b, 0x61, 0x70, + 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x22, 0x80, 0x01, 0x0a, 0x07, 0x53, 0x65, + 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x73, 0x69, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x69, 0x64, 0x5f, 0x73, + 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, + 0x73, 0x69, 0x64, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1d, 0x0a, 0x0a, + 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x41, 0x64, 0x64, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x72, + 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x64, 0x64, 0x72, 0x22, 0xa4, 0x03, 0x0a, + 0x08, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x63, 0x65, + 0x70, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x70, 0x63, 0x65, 0x70, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x41, 0x64, 0x64, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x72, 0x63, 0x5f, 0x61, 0x64, 0x64, + 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x73, 0x72, 0x63, 0x41, 0x64, 0x64, 0x72, + 0x12, 0x19, 0x0a, 0x08, 0x64, 0x73, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x07, 0x64, 0x73, 0x74, 0x41, 0x64, 0x64, 0x72, 0x12, 0x22, 0x0a, 0x0d, 0x73, + 0x72, 0x63, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x73, 0x72, 0x63, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, + 0x22, 0x0a, 0x0d, 0x64, 0x73, 0x74, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, + 0x72, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x05, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x70, 0x72, 0x65, + 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x70, + 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2d, 0x0a, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, + 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, + 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x37, 0x0a, 0x0c, 0x73, 0x65, 0x67, + 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, + 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0b, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x4c, 0x69, + 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x18, 0x0b, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, + 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x52, 0x06, 0x6d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x22, 0x80, 0x01, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x52, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x32, 0x0a, + 0x09, 0x73, 0x72, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, + 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x08, 0x73, 0x72, 0x50, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x73, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, + 0x61, 0x73, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x69, 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x73, 0x69, 0x64, 0x56, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x22, 0x37, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, + 0x5d, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x32, 0x0a, 0x09, 0x73, 0x72, 0x5f, 0x70, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x52, 0x08, 0x73, 0x72, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x61, 0x73, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x61, 0x73, 0x6e, 0x22, 0x37, + 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, 0x73, + 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, + 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0x7f, 0x0a, 0x07, 0x53, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x2f, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x61, 0x70, 0x73, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x63, 0x61, 0x70, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x69, + 0x73, 0x5f, 0x73, 0x79, 0x6e, 0x63, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, + 0x69, 0x73, 0x53, 0x79, 0x6e, 0x63, 0x65, 0x64, 0x22, 0x3f, 0x0a, 0x0b, 0x53, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x30, 0x0a, 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, + 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x46, 0x0a, 0x0c, 0x53, 0x52, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x36, 0x0a, 0x0b, 0x73, 0x72, 0x5f, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x52, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0a, 0x73, 0x72, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, + 0x73, 0x22, 0x3f, 0x0a, 0x08, 0x4c, 0x73, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x16, 0x0a, + 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, + 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x69, 0x64, 0x5f, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x73, 0x69, 0x64, 0x49, 0x6e, 0x64, + 0x65, 0x78, 0x22, 0x4b, 0x0a, 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x2b, 0x0a, 0x04, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, + 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, + 0x96, 0x02, 0x0a, 0x06, 0x4c, 0x73, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x26, 0x0a, 0x0f, 0x6c, 0x6f, + 0x63, 0x61, 0x6c, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, + 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x61, 0x73, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x41, 0x73, 0x6e, 0x12, + 0x19, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x70, 0x12, 0x28, 0x0a, 0x10, 0x72, 0x65, + 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, + 0x65, 0x72, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x61, + 0x73, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, + 0x41, 0x73, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x69, 0x70, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x70, + 0x12, 0x2d, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, + 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, + 0x17, 0x0a, 0x07, 0x61, 0x64, 0x6a, 0x5f, 0x73, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x06, 0x61, 0x64, 0x6a, 0x53, 0x69, 0x64, 0x22, 0x97, 0x02, 0x0a, 0x06, 0x4c, 0x73, 0x4e, + 0x6f, 0x64, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x73, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x03, 0x61, 0x73, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x5f, + 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, + 0x49, 0x64, 0x12, 0x20, 0x0a, 0x0c, 0x69, 0x73, 0x69, 0x73, 0x5f, 0x61, 0x72, 0x65, 0x61, 0x5f, + 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x73, 0x69, 0x73, 0x41, 0x72, + 0x65, 0x61, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x72, 0x67, 0x62, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x73, 0x72, 0x67, 0x62, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x12, + 0x19, 0x0a, 0x08, 0x73, 0x72, 0x67, 0x62, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x07, 0x73, 0x72, 0x67, 0x62, 0x45, 0x6e, 0x64, 0x12, 0x2e, 0x0a, 0x08, 0x6c, 0x73, + 0x5f, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x73, 0x4c, 0x69, 0x6e, + 0x6b, 0x52, 0x07, 0x6c, 0x73, 0x4c, 0x69, 0x6e, 0x6b, 0x73, 0x12, 0x36, 0x0a, 0x0b, 0x6c, 0x73, + 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x73, + 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x52, 0x0a, 0x6c, 0x73, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, + 0x65, 0x73, 0x22, 0x4d, 0x0a, 0x03, 0x54, 0x45, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61, + 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, + 0x65, 0x12, 0x2e, 0x0a, 0x08, 0x6c, 0x73, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, + 0x31, 0x2e, 0x4c, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x07, 0x6c, 0x73, 0x4e, 0x6f, 0x64, 0x65, + 0x73, 0x22, 0x17, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, + 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x4a, 0x0a, 0x16, 0x47, 0x65, + 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, + 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x73, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x18, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x53, 0x52, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x22, 0x51, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, + 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x0b, 0x73, + 0x72, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, + 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0a, 0x73, 0x72, 0x50, 0x6f, 0x6c, 0x69, 0x63, + 0x69, 0x65, 0x73, 0x22, 0x0f, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x54, 0x45, 0x44, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x22, 0x58, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x54, 0x45, 0x44, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x2e, + 0x0a, 0x08, 0x6c, 0x73, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, + 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x07, 0x6c, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x2a, + 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x22, 0x36, 0x0a, 0x15, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x53, 0x75, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x2a, 0x67, 0x0a, 0x0c, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x1a, 0x53, 0x52, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, + 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x52, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x58, 0x50, 0x4c, 0x49, 0x43, 0x49, 0x54, 0x10, 0x01, 0x12, + 0x1a, 0x0a, 0x16, 0x53, 0x52, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x44, 0x59, 0x4e, 0x41, 0x4d, 0x49, 0x43, 0x10, 0x02, 0x2a, 0x5b, 0x0a, 0x0c, 0x53, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1d, 0x0a, 0x19, 0x53, + 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x53, + 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x53, 0x45, + 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x44, 0x4f, 0x57, 0x4e, + 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, + 0x41, 0x54, 0x45, 0x5f, 0x55, 0x50, 0x10, 0x02, 0x2a, 0x83, 0x01, 0x0a, 0x0a, 0x4d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x4d, 0x45, 0x54, 0x52, 0x49, + 0x43, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, + 0x45, 0x44, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x54, + 0x59, 0x50, 0x45, 0x5f, 0x49, 0x47, 0x50, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x4d, 0x45, 0x54, + 0x52, 0x49, 0x43, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x54, 0x45, 0x10, 0x02, 0x12, 0x15, 0x0a, + 0x11, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x45, 0x4c, + 0x41, 0x59, 0x10, 0x03, 0x12, 0x18, 0x0a, 0x14, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x54, + 0x59, 0x50, 0x45, 0x5f, 0x48, 0x4f, 0x50, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x04, 0x32, 0x96, + 0x04, 0x0a, 0x0a, 0x50, 0x43, 0x45, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x59, 0x0a, + 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, + 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x59, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, + 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x59, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, + 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, + 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69, 0x73, + 0x74, 0x12, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, + 0x47, 0x65, 0x74, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, + 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x06, + 0x47, 0x65, 0x74, 0x54, 0x45, 0x44, 0x12, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, + 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x45, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x65, 0x74, 0x54, 0x45, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x56, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x12, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, + 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x24, 0x5a, 0x22, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6e, 0x74, 0x74, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6f, 0x6c, + 0x61, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x6f, 0x6c, 0x61, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_pola_proto_rawDescOnce sync.Once + file_pola_proto_rawDescData = file_pola_proto_rawDesc +) + +func file_pola_proto_rawDescGZIP() []byte { + file_pola_proto_rawDescOnce.Do(func() { + file_pola_proto_rawDescData = protoimpl.X.CompressGZIP(file_pola_proto_rawDescData) + }) + return file_pola_proto_rawDescData +} + +var file_pola_proto_enumTypes = make([]protoimpl.EnumInfo, 3) +var file_pola_proto_msgTypes = make([]protoimpl.MessageInfo, 22) +var file_pola_proto_goTypes = []interface{}{ + (SRPolicyType)(0), // 0: api.pola.v1.SRPolicyType + (SessionState)(0), // 1: api.pola.v1.SessionState + (MetricType)(0), // 2: api.pola.v1.MetricType + (*Segment)(nil), // 3: api.pola.v1.Segment + (*SRPolicy)(nil), // 4: api.pola.v1.SRPolicy + (*CreateSRPolicyRequest)(nil), // 5: api.pola.v1.CreateSRPolicyRequest + (*CreateSRPolicyResponse)(nil), // 6: api.pola.v1.CreateSRPolicyResponse + (*DeleteSRPolicyRequest)(nil), // 7: api.pola.v1.DeleteSRPolicyRequest + (*DeleteSRPolicyResponse)(nil), // 8: api.pola.v1.DeleteSRPolicyResponse + (*Session)(nil), // 9: api.pola.v1.Session + (*SessionList)(nil), // 10: api.pola.v1.SessionList + (*SRPolicyList)(nil), // 11: api.pola.v1.SRPolicyList + (*LsPrefix)(nil), // 12: api.pola.v1.LsPrefix + (*Metric)(nil), // 13: api.pola.v1.Metric + (*LsLink)(nil), // 14: api.pola.v1.LsLink + (*LsNode)(nil), // 15: api.pola.v1.LsNode + (*TED)(nil), // 16: api.pola.v1.TED + (*GetSessionListRequest)(nil), // 17: api.pola.v1.GetSessionListRequest + (*GetSessionListResponse)(nil), // 18: api.pola.v1.GetSessionListResponse + (*GetSRPolicyListRequest)(nil), // 19: api.pola.v1.GetSRPolicyListRequest + (*GetSRPolicyListResponse)(nil), // 20: api.pola.v1.GetSRPolicyListResponse + (*GetTEDRequest)(nil), // 21: api.pola.v1.GetTEDRequest + (*GetTEDResponse)(nil), // 22: api.pola.v1.GetTEDResponse + (*DeleteSessionRequest)(nil), // 23: api.pola.v1.DeleteSessionRequest + (*DeleteSessionResponse)(nil), // 24: api.pola.v1.DeleteSessionResponse +} +var file_pola_proto_depIdxs = []int32{ + 0, // 0: api.pola.v1.SRPolicy.type:type_name -> api.pola.v1.SRPolicyType + 3, // 1: api.pola.v1.SRPolicy.segment_list:type_name -> api.pola.v1.Segment + 2, // 2: api.pola.v1.SRPolicy.metric:type_name -> api.pola.v1.MetricType + 4, // 3: api.pola.v1.CreateSRPolicyRequest.sr_policy:type_name -> api.pola.v1.SRPolicy + 4, // 4: api.pola.v1.DeleteSRPolicyRequest.sr_policy:type_name -> api.pola.v1.SRPolicy + 1, // 5: api.pola.v1.Session.state:type_name -> api.pola.v1.SessionState + 9, // 6: api.pola.v1.SessionList.sessions:type_name -> api.pola.v1.Session + 4, // 7: api.pola.v1.SRPolicyList.sr_policies:type_name -> api.pola.v1.SRPolicy + 2, // 8: api.pola.v1.Metric.type:type_name -> api.pola.v1.MetricType + 13, // 9: api.pola.v1.LsLink.metrics:type_name -> api.pola.v1.Metric + 14, // 10: api.pola.v1.LsNode.ls_links:type_name -> api.pola.v1.LsLink + 12, // 11: api.pola.v1.LsNode.ls_prefixes:type_name -> api.pola.v1.LsPrefix + 15, // 12: api.pola.v1.TED.ls_nodes:type_name -> api.pola.v1.LsNode + 9, // 13: api.pola.v1.GetSessionListResponse.sessions:type_name -> api.pola.v1.Session + 4, // 14: api.pola.v1.GetSRPolicyListResponse.sr_policies:type_name -> api.pola.v1.SRPolicy + 15, // 15: api.pola.v1.GetTEDResponse.ls_nodes:type_name -> api.pola.v1.LsNode + 5, // 16: api.pola.v1.PCEService.CreateSRPolicy:input_type -> api.pola.v1.CreateSRPolicyRequest + 7, // 17: api.pola.v1.PCEService.DeleteSRPolicy:input_type -> api.pola.v1.DeleteSRPolicyRequest + 17, // 18: api.pola.v1.PCEService.GetSessionList:input_type -> api.pola.v1.GetSessionListRequest + 19, // 19: api.pola.v1.PCEService.GetSRPolicyList:input_type -> api.pola.v1.GetSRPolicyListRequest + 21, // 20: api.pola.v1.PCEService.GetTED:input_type -> api.pola.v1.GetTEDRequest + 23, // 21: api.pola.v1.PCEService.DeleteSession:input_type -> api.pola.v1.DeleteSessionRequest + 6, // 22: api.pola.v1.PCEService.CreateSRPolicy:output_type -> api.pola.v1.CreateSRPolicyResponse + 8, // 23: api.pola.v1.PCEService.DeleteSRPolicy:output_type -> api.pola.v1.DeleteSRPolicyResponse + 18, // 24: api.pola.v1.PCEService.GetSessionList:output_type -> api.pola.v1.GetSessionListResponse + 20, // 25: api.pola.v1.PCEService.GetSRPolicyList:output_type -> api.pola.v1.GetSRPolicyListResponse + 22, // 26: api.pola.v1.PCEService.GetTED:output_type -> api.pola.v1.GetTEDResponse + 24, // 27: api.pola.v1.PCEService.DeleteSession:output_type -> api.pola.v1.DeleteSessionResponse + 22, // [22:28] is the sub-list for method output_type + 16, // [16:22] is the sub-list for method input_type + 16, // [16:16] is the sub-list for extension type_name + 16, // [16:16] is the sub-list for extension extendee + 0, // [0:16] is the sub-list for field type_name +} + +func init() { file_pola_proto_init() } +func file_pola_proto_init() { + if File_pola_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_pola_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Segment); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pola_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SRPolicy); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pola_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateSRPolicyRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pola_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateSRPolicyResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pola_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteSRPolicyRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pola_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteSRPolicyResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pola_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Session); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pola_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SessionList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pola_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SRPolicyList); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pola_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LsPrefix); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pola_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Metric); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pola_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LsLink); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pola_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LsNode); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pola_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TED); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pola_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetSessionListRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pola_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetSessionListResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pola_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetSRPolicyListRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pola_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetSRPolicyListResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pola_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetTEDRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pola_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetTEDResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pola_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteSessionRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pola_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteSessionResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_pola_proto_rawDesc, + NumEnums: 3, + NumMessages: 22, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_pola_proto_goTypes, + DependencyIndexes: file_pola_proto_depIdxs, + EnumInfos: file_pola_proto_enumTypes, + MessageInfos: file_pola_proto_msgTypes, + }.Build() + File_pola_proto = out.File + file_pola_proto_rawDesc = nil + file_pola_proto_goTypes = nil + file_pola_proto_depIdxs = nil +} diff --git a/api/pola/v1/pola.proto b/api/pola/v1/pola.proto new file mode 100644 index 00000000..b3769f64 --- /dev/null +++ b/api/pola/v1/pola.proto @@ -0,0 +1,161 @@ +// Copyright (c) 2022 NTT Communications Corporation +// +// This software is released under the MIT License. +// see https://github.com/nttcom/pola/blob/main/LICENSE + +syntax = "proto3"; + +package api.pola.v1; + +option go_package = "github.com/nttcom/pola/api/pola/v1"; + +service PCEService { + rpc CreateSRPolicy (CreateSRPolicyRequest) returns (CreateSRPolicyResponse); + rpc DeleteSRPolicy (DeleteSRPolicyRequest) returns (DeleteSRPolicyResponse); + rpc GetSessionList (GetSessionListRequest) returns (GetSessionListResponse); + rpc GetSRPolicyList (GetSRPolicyListRequest) returns (GetSRPolicyListResponse); + rpc GetTED (GetTEDRequest) returns (GetTEDResponse); + rpc DeleteSession (DeleteSessionRequest) returns (DeleteSessionResponse); +} + +message Segment { + string sid = 1; + string sid_structure = 2; + string local_addr = 3; + string remote_addr = 4; +} + +enum SRPolicyType { + SR_POLICY_TYPE_UNSPECIFIED = 0; + SR_POLICY_TYPE_EXPLICIT = 1; + SR_POLICY_TYPE_DYNAMIC = 2; +} + +message SRPolicy { + bytes pcep_session_addr = 1; + bytes src_addr = 2; + bytes dst_addr = 3; + string src_router_id = 4; + string dst_router_id = 5; + uint32 color = 6; + uint32 preference = 7; + string policy_name = 8; + SRPolicyType type = 9; + repeated Segment segment_list = 10; + MetricType metric = 11; +} + +message CreateSRPolicyRequest { + SRPolicy sr_policy = 1; + uint32 asn = 2; + bool sid_validate = 3; +} + +message CreateSRPolicyResponse { + bool is_success = 1; +} + +message DeleteSRPolicyRequest { + SRPolicy sr_policy = 1; + uint32 asn = 2; +} + +message DeleteSRPolicyResponse { + bool is_success = 1; +} + +enum SessionState { + SESSION_STATE_UNSPECIFIED = 0; + SESSION_STATE_DOWN = 1; + SESSION_STATE_UP = 2; +} + +message Session { + bytes addr = 1; + SessionState state = 2; + repeated string caps = 3; + bool is_synced = 4; +} + +message SessionList { + repeated Session sessions = 1; +} + +message SRPolicyList { + repeated SRPolicy sr_policies = 1; +} + +message LsPrefix { + string prefix = 1; + uint32 sid_index = 2; +} + +enum MetricType { + METRIC_TYPE_UNSPECIFIED = 0; + METRIC_TYPE_IGP = 1; + METRIC_TYPE_TE = 2; + METRIC_TYPE_DELAY = 3; + METRIC_TYPE_HOPCOUNT = 4; +} + +message Metric { + MetricType type = 1; + uint32 value = 2; +} + +message LsLink { + string local_router_id = 1; + uint32 local_asn = 2; + string local_ip = 3; + string remote_router_id = 4; + uint32 remote_asn = 5; + string remote_ip = 6; + repeated Metric metrics = 7; + uint32 adj_sid = 8; +} + +message LsNode { + uint32 asn = 1; + string router_id = 2; + string isis_area_id = 3; + string hostname = 4; + uint32 srgb_begin = 5; + uint32 srgb_end = 6; + repeated LsLink ls_links = 7; + repeated LsPrefix ls_prefixes = 8; +} + +message TED { + bool enable = 1; + repeated LsNode ls_nodes = 2; +} + +message GetSessionListRequest { +} + +message GetSessionListResponse { + repeated Session sessions = 1; +} + +message GetSRPolicyListRequest { +} + +message GetSRPolicyListResponse { + repeated SRPolicy sr_policies = 1; +} + +message GetTEDRequest { +} + +message GetTEDResponse { + bool enable = 1; + repeated LsNode ls_nodes = 2; +} + +message DeleteSessionRequest { + bytes addr = 1; +} + +message DeleteSessionResponse { + bool is_success = 1; +} diff --git a/api/grpc/pola.zap.go b/api/pola/v1/pola.zap.go similarity index 80% rename from api/grpc/pola.zap.go rename to api/pola/v1/pola.zap.go index 135393a6..e13fdbdd 100644 --- a/api/grpc/pola.zap.go +++ b/api/pola/v1/pola.zap.go @@ -3,7 +3,7 @@ // This software is released under the MIT License. // see https://github.com/nttcom/pola/blob/main/LICENSE -package grpc +package v1 import ( "net/netip" @@ -14,16 +14,16 @@ import ( // Implements zapcore.ObjectMarshaler interface for SRPolicy func (x *SRPolicy) MarshalLogObject(enc zapcore.ObjectEncoder) error { // Convert IP address slices to netip.Addr - ssAddr, _ := netip.AddrFromSlice(x.GetPCEPSessionAddr()) + ssAddr, _ := netip.AddrFromSlice(x.GetPcepSessionAddr()) enc.AddString("PCEPSessionAddr", ssAddr.String()) srcAddr, _ := netip.AddrFromSlice(x.GetSrcAddr()) enc.AddString("SrcAddr", srcAddr.String()) dstAddr, _ := netip.AddrFromSlice(x.GetDstAddr()) enc.AddString("DstAddr", dstAddr.String()) - if srcRouterID := x.GetSrcRouterID(); srcRouterID != "" { + if srcRouterID := x.GetSrcRouterId(); srcRouterID != "" { enc.AddString("SrcRouterID", srcRouterID) } - if dstRouterID := x.DstRouterID; dstRouterID != "" { + if dstRouterID := x.DstRouterId; dstRouterID != "" { enc.AddString("DstRouterID", dstRouterID) } enc.AddUint32("Color", x.GetColor()) @@ -31,11 +31,11 @@ func (x *SRPolicy) MarshalLogObject(enc zapcore.ObjectEncoder) error { enc.AddString("PolicyName", x.GetPolicyName()) enc.AddString("Type", x.GetType().String()) - if x.GetType() == SRPolicyType_EXPLICIT { + if x.GetType() == SRPolicyType_SR_POLICY_TYPE_EXPLICIT { if err := enc.AddReflected("SegmentList", x.GetSegmentList()); err != nil { return err } - } else if x.GetType() == SRPolicyType_DYNAMIC { + } else if x.GetType() == SRPolicyType_SR_POLICY_TYPE_DYNAMIC { enc.AddString("Metric", x.Metric.String()) } return nil diff --git a/api/grpc/pola_grpc.pb.go b/api/pola/v1/pola_grpc.pb.go similarity index 52% rename from api/grpc/pola_grpc.pb.go rename to api/pola/v1/pola_grpc.pb.go index 366e2ffa..3c6b0a90 100644 --- a/api/grpc/pola_grpc.pb.go +++ b/api/pola/v1/pola_grpc.pb.go @@ -1,13 +1,12 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. -package grpc +package v1 import ( context "context" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" - emptypb "google.golang.org/protobuf/types/known/emptypb" ) // This is a compile-time assertion to ensure that this generated file @@ -19,14 +18,12 @@ const _ = grpc.SupportPackageIsVersion7 // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type PCEServiceClient interface { - CreateSRPolicy(ctx context.Context, in *CreateSRPolicyInput, opts ...grpc.CallOption) (*RequestStatus, error) - CreateSRPolicyWithoutLinkState(ctx context.Context, in *CreateSRPolicyInput, opts ...grpc.CallOption) (*RequestStatus, error) - DeleteSRPolicy(ctx context.Context, in *DeleteSRPolicyInput, opts ...grpc.CallOption) (*RequestStatus, error) - DeleteSRPolicyWithoutLinkState(ctx context.Context, in *DeleteSRPolicyInput, opts ...grpc.CallOption) (*RequestStatus, error) - GetSessionList(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*SessionList, error) - GetSRPolicyList(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*SRPolicyList, error) - GetTED(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*TED, error) - DeleteSession(ctx context.Context, in *Session, opts ...grpc.CallOption) (*RequestStatus, error) + CreateSRPolicy(ctx context.Context, in *CreateSRPolicyRequest, opts ...grpc.CallOption) (*CreateSRPolicyResponse, error) + DeleteSRPolicy(ctx context.Context, in *DeleteSRPolicyRequest, opts ...grpc.CallOption) (*DeleteSRPolicyResponse, error) + GetSessionList(ctx context.Context, in *GetSessionListRequest, opts ...grpc.CallOption) (*GetSessionListResponse, error) + GetSRPolicyList(ctx context.Context, in *GetSRPolicyListRequest, opts ...grpc.CallOption) (*GetSRPolicyListResponse, error) + GetTED(ctx context.Context, in *GetTEDRequest, opts ...grpc.CallOption) (*GetTEDResponse, error) + DeleteSession(ctx context.Context, in *DeleteSessionRequest, opts ...grpc.CallOption) (*DeleteSessionResponse, error) } type pCEServiceClient struct { @@ -37,72 +34,54 @@ func NewPCEServiceClient(cc grpc.ClientConnInterface) PCEServiceClient { return &pCEServiceClient{cc} } -func (c *pCEServiceClient) CreateSRPolicy(ctx context.Context, in *CreateSRPolicyInput, opts ...grpc.CallOption) (*RequestStatus, error) { - out := new(RequestStatus) - err := c.cc.Invoke(ctx, "/pb.PCEService/CreateSRPolicy", in, out, opts...) +func (c *pCEServiceClient) CreateSRPolicy(ctx context.Context, in *CreateSRPolicyRequest, opts ...grpc.CallOption) (*CreateSRPolicyResponse, error) { + out := new(CreateSRPolicyResponse) + err := c.cc.Invoke(ctx, "/api.pola.v1.PCEService/CreateSRPolicy", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *pCEServiceClient) CreateSRPolicyWithoutLinkState(ctx context.Context, in *CreateSRPolicyInput, opts ...grpc.CallOption) (*RequestStatus, error) { - out := new(RequestStatus) - err := c.cc.Invoke(ctx, "/pb.PCEService/CreateSRPolicyWithoutLinkState", in, out, opts...) +func (c *pCEServiceClient) DeleteSRPolicy(ctx context.Context, in *DeleteSRPolicyRequest, opts ...grpc.CallOption) (*DeleteSRPolicyResponse, error) { + out := new(DeleteSRPolicyResponse) + err := c.cc.Invoke(ctx, "/api.pola.v1.PCEService/DeleteSRPolicy", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *pCEServiceClient) DeleteSRPolicy(ctx context.Context, in *DeleteSRPolicyInput, opts ...grpc.CallOption) (*RequestStatus, error) { - out := new(RequestStatus) - err := c.cc.Invoke(ctx, "/pb.PCEService/DeleteSRPolicy", in, out, opts...) +func (c *pCEServiceClient) GetSessionList(ctx context.Context, in *GetSessionListRequest, opts ...grpc.CallOption) (*GetSessionListResponse, error) { + out := new(GetSessionListResponse) + err := c.cc.Invoke(ctx, "/api.pola.v1.PCEService/GetSessionList", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *pCEServiceClient) DeleteSRPolicyWithoutLinkState(ctx context.Context, in *DeleteSRPolicyInput, opts ...grpc.CallOption) (*RequestStatus, error) { - out := new(RequestStatus) - err := c.cc.Invoke(ctx, "/pb.PCEService/DeleteSRPolicyWithoutLinkState", in, out, opts...) +func (c *pCEServiceClient) GetSRPolicyList(ctx context.Context, in *GetSRPolicyListRequest, opts ...grpc.CallOption) (*GetSRPolicyListResponse, error) { + out := new(GetSRPolicyListResponse) + err := c.cc.Invoke(ctx, "/api.pola.v1.PCEService/GetSRPolicyList", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *pCEServiceClient) GetSessionList(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*SessionList, error) { - out := new(SessionList) - err := c.cc.Invoke(ctx, "/pb.PCEService/GetSessionList", in, out, opts...) +func (c *pCEServiceClient) GetTED(ctx context.Context, in *GetTEDRequest, opts ...grpc.CallOption) (*GetTEDResponse, error) { + out := new(GetTEDResponse) + err := c.cc.Invoke(ctx, "/api.pola.v1.PCEService/GetTED", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *pCEServiceClient) GetSRPolicyList(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*SRPolicyList, error) { - out := new(SRPolicyList) - err := c.cc.Invoke(ctx, "/pb.PCEService/GetSRPolicyList", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *pCEServiceClient) GetTED(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*TED, error) { - out := new(TED) - err := c.cc.Invoke(ctx, "/pb.PCEService/GetTED", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *pCEServiceClient) DeleteSession(ctx context.Context, in *Session, opts ...grpc.CallOption) (*RequestStatus, error) { - out := new(RequestStatus) - err := c.cc.Invoke(ctx, "/pb.PCEService/DeleteSession", in, out, opts...) +func (c *pCEServiceClient) DeleteSession(ctx context.Context, in *DeleteSessionRequest, opts ...grpc.CallOption) (*DeleteSessionResponse, error) { + out := new(DeleteSessionResponse) + err := c.cc.Invoke(ctx, "/api.pola.v1.PCEService/DeleteSession", in, out, opts...) if err != nil { return nil, err } @@ -113,14 +92,12 @@ func (c *pCEServiceClient) DeleteSession(ctx context.Context, in *Session, opts // All implementations must embed UnimplementedPCEServiceServer // for forward compatibility type PCEServiceServer interface { - CreateSRPolicy(context.Context, *CreateSRPolicyInput) (*RequestStatus, error) - CreateSRPolicyWithoutLinkState(context.Context, *CreateSRPolicyInput) (*RequestStatus, error) - DeleteSRPolicy(context.Context, *DeleteSRPolicyInput) (*RequestStatus, error) - DeleteSRPolicyWithoutLinkState(context.Context, *DeleteSRPolicyInput) (*RequestStatus, error) - GetSessionList(context.Context, *emptypb.Empty) (*SessionList, error) - GetSRPolicyList(context.Context, *emptypb.Empty) (*SRPolicyList, error) - GetTED(context.Context, *emptypb.Empty) (*TED, error) - DeleteSession(context.Context, *Session) (*RequestStatus, error) + CreateSRPolicy(context.Context, *CreateSRPolicyRequest) (*CreateSRPolicyResponse, error) + DeleteSRPolicy(context.Context, *DeleteSRPolicyRequest) (*DeleteSRPolicyResponse, error) + GetSessionList(context.Context, *GetSessionListRequest) (*GetSessionListResponse, error) + GetSRPolicyList(context.Context, *GetSRPolicyListRequest) (*GetSRPolicyListResponse, error) + GetTED(context.Context, *GetTEDRequest) (*GetTEDResponse, error) + DeleteSession(context.Context, *DeleteSessionRequest) (*DeleteSessionResponse, error) mustEmbedUnimplementedPCEServiceServer() } @@ -128,28 +105,22 @@ type PCEServiceServer interface { type UnimplementedPCEServiceServer struct { } -func (UnimplementedPCEServiceServer) CreateSRPolicy(context.Context, *CreateSRPolicyInput) (*RequestStatus, error) { +func (UnimplementedPCEServiceServer) CreateSRPolicy(context.Context, *CreateSRPolicyRequest) (*CreateSRPolicyResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method CreateSRPolicy not implemented") } -func (UnimplementedPCEServiceServer) CreateSRPolicyWithoutLinkState(context.Context, *CreateSRPolicyInput) (*RequestStatus, error) { - return nil, status.Errorf(codes.Unimplemented, "method CreateSRPolicyWithoutLinkState not implemented") -} -func (UnimplementedPCEServiceServer) DeleteSRPolicy(context.Context, *DeleteSRPolicyInput) (*RequestStatus, error) { +func (UnimplementedPCEServiceServer) DeleteSRPolicy(context.Context, *DeleteSRPolicyRequest) (*DeleteSRPolicyResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method DeleteSRPolicy not implemented") } -func (UnimplementedPCEServiceServer) DeleteSRPolicyWithoutLinkState(context.Context, *DeleteSRPolicyInput) (*RequestStatus, error) { - return nil, status.Errorf(codes.Unimplemented, "method DeleteSRPolicyWithoutLinkState not implemented") -} -func (UnimplementedPCEServiceServer) GetSessionList(context.Context, *emptypb.Empty) (*SessionList, error) { +func (UnimplementedPCEServiceServer) GetSessionList(context.Context, *GetSessionListRequest) (*GetSessionListResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetSessionList not implemented") } -func (UnimplementedPCEServiceServer) GetSRPolicyList(context.Context, *emptypb.Empty) (*SRPolicyList, error) { +func (UnimplementedPCEServiceServer) GetSRPolicyList(context.Context, *GetSRPolicyListRequest) (*GetSRPolicyListResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetSRPolicyList not implemented") } -func (UnimplementedPCEServiceServer) GetTED(context.Context, *emptypb.Empty) (*TED, error) { +func (UnimplementedPCEServiceServer) GetTED(context.Context, *GetTEDRequest) (*GetTEDResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetTED not implemented") } -func (UnimplementedPCEServiceServer) DeleteSession(context.Context, *Session) (*RequestStatus, error) { +func (UnimplementedPCEServiceServer) DeleteSession(context.Context, *DeleteSessionRequest) (*DeleteSessionResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method DeleteSession not implemented") } func (UnimplementedPCEServiceServer) mustEmbedUnimplementedPCEServiceServer() {} @@ -166,7 +137,7 @@ func RegisterPCEServiceServer(s grpc.ServiceRegistrar, srv PCEServiceServer) { } func _PCEService_CreateSRPolicy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CreateSRPolicyInput) + in := new(CreateSRPolicyRequest) if err := dec(in); err != nil { return nil, err } @@ -175,34 +146,16 @@ func _PCEService_CreateSRPolicy_Handler(srv interface{}, ctx context.Context, de } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/pb.PCEService/CreateSRPolicy", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(PCEServiceServer).CreateSRPolicy(ctx, req.(*CreateSRPolicyInput)) - } - return interceptor(ctx, in, info, handler) -} - -func _PCEService_CreateSRPolicyWithoutLinkState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CreateSRPolicyInput) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(PCEServiceServer).CreateSRPolicyWithoutLinkState(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/pb.PCEService/CreateSRPolicyWithoutLinkState", + FullMethod: "/api.pola.v1.PCEService/CreateSRPolicy", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(PCEServiceServer).CreateSRPolicyWithoutLinkState(ctx, req.(*CreateSRPolicyInput)) + return srv.(PCEServiceServer).CreateSRPolicy(ctx, req.(*CreateSRPolicyRequest)) } return interceptor(ctx, in, info, handler) } func _PCEService_DeleteSRPolicy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DeleteSRPolicyInput) + in := new(DeleteSRPolicyRequest) if err := dec(in); err != nil { return nil, err } @@ -211,34 +164,16 @@ func _PCEService_DeleteSRPolicy_Handler(srv interface{}, ctx context.Context, de } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/pb.PCEService/DeleteSRPolicy", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(PCEServiceServer).DeleteSRPolicy(ctx, req.(*DeleteSRPolicyInput)) - } - return interceptor(ctx, in, info, handler) -} - -func _PCEService_DeleteSRPolicyWithoutLinkState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DeleteSRPolicyInput) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(PCEServiceServer).DeleteSRPolicyWithoutLinkState(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/pb.PCEService/DeleteSRPolicyWithoutLinkState", + FullMethod: "/api.pola.v1.PCEService/DeleteSRPolicy", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(PCEServiceServer).DeleteSRPolicyWithoutLinkState(ctx, req.(*DeleteSRPolicyInput)) + return srv.(PCEServiceServer).DeleteSRPolicy(ctx, req.(*DeleteSRPolicyRequest)) } return interceptor(ctx, in, info, handler) } func _PCEService_GetSessionList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(emptypb.Empty) + in := new(GetSessionListRequest) if err := dec(in); err != nil { return nil, err } @@ -247,16 +182,16 @@ func _PCEService_GetSessionList_Handler(srv interface{}, ctx context.Context, de } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/pb.PCEService/GetSessionList", + FullMethod: "/api.pola.v1.PCEService/GetSessionList", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(PCEServiceServer).GetSessionList(ctx, req.(*emptypb.Empty)) + return srv.(PCEServiceServer).GetSessionList(ctx, req.(*GetSessionListRequest)) } return interceptor(ctx, in, info, handler) } func _PCEService_GetSRPolicyList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(emptypb.Empty) + in := new(GetSRPolicyListRequest) if err := dec(in); err != nil { return nil, err } @@ -265,16 +200,16 @@ func _PCEService_GetSRPolicyList_Handler(srv interface{}, ctx context.Context, d } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/pb.PCEService/GetSRPolicyList", + FullMethod: "/api.pola.v1.PCEService/GetSRPolicyList", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(PCEServiceServer).GetSRPolicyList(ctx, req.(*emptypb.Empty)) + return srv.(PCEServiceServer).GetSRPolicyList(ctx, req.(*GetSRPolicyListRequest)) } return interceptor(ctx, in, info, handler) } func _PCEService_GetTED_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(emptypb.Empty) + in := new(GetTEDRequest) if err := dec(in); err != nil { return nil, err } @@ -283,16 +218,16 @@ func _PCEService_GetTED_Handler(srv interface{}, ctx context.Context, dec func(i } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/pb.PCEService/GetTED", + FullMethod: "/api.pola.v1.PCEService/GetTED", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(PCEServiceServer).GetTED(ctx, req.(*emptypb.Empty)) + return srv.(PCEServiceServer).GetTED(ctx, req.(*GetTEDRequest)) } return interceptor(ctx, in, info, handler) } func _PCEService_DeleteSession_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Session) + in := new(DeleteSessionRequest) if err := dec(in); err != nil { return nil, err } @@ -301,10 +236,10 @@ func _PCEService_DeleteSession_Handler(srv interface{}, ctx context.Context, dec } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/pb.PCEService/DeleteSession", + FullMethod: "/api.pola.v1.PCEService/DeleteSession", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(PCEServiceServer).DeleteSession(ctx, req.(*Session)) + return srv.(PCEServiceServer).DeleteSession(ctx, req.(*DeleteSessionRequest)) } return interceptor(ctx, in, info, handler) } @@ -313,25 +248,17 @@ func _PCEService_DeleteSession_Handler(srv interface{}, ctx context.Context, dec // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var PCEService_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "pb.PCEService", + ServiceName: "api.pola.v1.PCEService", HandlerType: (*PCEServiceServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "CreateSRPolicy", Handler: _PCEService_CreateSRPolicy_Handler, }, - { - MethodName: "CreateSRPolicyWithoutLinkState", - Handler: _PCEService_CreateSRPolicyWithoutLinkState_Handler, - }, { MethodName: "DeleteSRPolicy", Handler: _PCEService_DeleteSRPolicy_Handler, }, - { - MethodName: "DeleteSRPolicyWithoutLinkState", - Handler: _PCEService_DeleteSRPolicyWithoutLinkState_Handler, - }, { MethodName: "GetSessionList", Handler: _PCEService_GetSessionList_Handler, diff --git a/cmd/pola/README.md b/cmd/pola/README.md index 5db54f4b..2206bddd 100644 --- a/cmd/pola/README.md +++ b/cmd/pola/README.md @@ -209,7 +209,7 @@ JSON formatted response } ``` -### pola sr-policy add -f _filepath_ --no-link-state +### pola sr-policy add -f `filepath` --no-sid-validate Create a new SR Policy **without using TED** diff --git a/cmd/pola/grpc/grpc_client.go b/cmd/pola/grpc/grpc_client.go index 010f3852..86abe104 100644 --- a/cmd/pola/grpc/grpc_client.go +++ b/cmd/pola/grpc/grpc_client.go @@ -11,8 +11,7 @@ import ( "net/netip" "time" - "github.com/golang/protobuf/ptypes/empty" - pb "github.com/nttcom/pola/api/grpc" + pb "github.com/nttcom/pola/api/pola/v1" "github.com/nttcom/pola/internal/pkg/table" ) @@ -31,7 +30,7 @@ func GetSessions(client pb.PCEServiceClient) ([]Session, error) { ctx, cancel := withTimeout() defer cancel() - ret, err := client.GetSessionList(ctx, &empty.Empty{}) + ret, err := client.GetSessionList(ctx, &pb.GetSessionListRequest{}) if err != nil { return nil, err } @@ -52,10 +51,11 @@ func GetSessions(client pb.PCEServiceClient) ([]Session, error) { return sessions, nil } -func DeleteSession(client pb.PCEServiceClient, session *pb.Session) error { +func DeleteSession(client pb.PCEServiceClient, req *pb.DeleteSessionRequest) error { ctx, cancel := withTimeout() defer cancel() - _, err := client.DeleteSession(ctx, session) + + _, err := client.DeleteSession(ctx, req) if err != nil { return err } @@ -66,15 +66,15 @@ func GetSRPolicyList(client pb.PCEServiceClient) (map[netip.Addr][]table.SRPolic ctx, cancel := withTimeout() defer cancel() - ret, err := client.GetSRPolicyList(ctx, &empty.Empty{}) + ret, err := client.GetSRPolicyList(ctx, &pb.GetSRPolicyListRequest{}) if err != nil { return nil, err } - policies := make(map[netip.Addr][]table.SRPolicy, len(ret.GetSRPolicies())) + policies := make(map[netip.Addr][]table.SRPolicy, len(ret.GetSrPolicies())) - for _, p := range ret.GetSRPolicies() { - peerAddr, _ := netip.AddrFromSlice(p.PCEPSessionAddr) + for _, p := range ret.GetSrPolicies() { + peerAddr, _ := netip.AddrFromSlice(p.PcepSessionAddr) srcAddr, _ := netip.AddrFromSlice(p.SrcAddr) dstAddr, _ := netip.AddrFromSlice(p.DstAddr) var segmentList []table.Segment @@ -99,27 +99,19 @@ func GetSRPolicyList(client pb.PCEServiceClient) (map[netip.Addr][]table.SRPolic return policies, nil } -func CreateSRPolicy(client pb.PCEServiceClient, input *pb.CreateSRPolicyInput) error { - ctx, cancel := withTimeout() - defer cancel() - - _, err := client.CreateSRPolicy(ctx, input) - return err -} - -func CreateSRPolicyWithoutLinkState(client pb.PCEServiceClient, input *pb.CreateSRPolicyInput) error { +func CreateSRPolicy(client pb.PCEServiceClient, req *pb.CreateSRPolicyRequest) error { ctx, cancel := withTimeout() defer cancel() - _, err := client.CreateSRPolicyWithoutLinkState(ctx, input) + _, err := client.CreateSRPolicy(ctx, req) return err } -func DeleteSRPolicy(client pb.PCEServiceClient, input *pb.DeleteSRPolicyInput) error { +func DeleteSRPolicy(client pb.PCEServiceClient, req *pb.DeleteSRPolicyRequest) error { ctx, cancel := withTimeout() defer cancel() - _, err := client.DeleteSRPolicy(ctx, input) + _, err := client.DeleteSRPolicy(ctx, req) return err } @@ -127,7 +119,7 @@ func GetTED(client pb.PCEServiceClient) (*table.LsTED, error) { ctx, cancel := withTimeout() defer cancel() - ret, err := client.GetTED(ctx, &empty.Empty{}) + ret, err := client.GetTED(ctx, &pb.GetTEDRequest{}) if err != nil { return nil, err } @@ -155,9 +147,9 @@ func GetTED(client pb.PCEServiceClient) (*table.LsTED, error) { // initializeLsNodes initializes LsNodes in the LsTED table using the given array of nodes func initializeLsNodes(ted *table.LsTED, nodes []*pb.LsNode) { for _, node := range nodes { - lsNode := table.NewLsNode(node.GetAsn(), node.GetRouterID()) + lsNode := table.NewLsNode(node.GetAsn(), node.GetRouterId()) lsNode.Hostname = node.GetHostname() - lsNode.IsisAreaID = node.GetIsisAreaID() + lsNode.IsisAreaID = node.GetIsisAreaId() lsNode.SrgbBegin = node.GetSrgbBegin() lsNode.SrgbEnd = node.GetSrgbEnd() @@ -170,21 +162,21 @@ func initializeLsNodes(ted *table.LsTED, nodes []*pb.LsNode) { func addLsNode(ted *table.LsTED, node *pb.LsNode) error { for _, link := range node.GetLsLinks() { - localNode := ted.Nodes[link.LocalASN][link.LocalRouterID] - remoteNode := ted.Nodes[link.RemoteASN][link.RemoteRouterID] + localNode := ted.Nodes[link.LocalAsn][link.LocalRouterId] + remoteNode := ted.Nodes[link.RemoteAsn][link.RemoteRouterId] lsLink, err := createLsLink(localNode, remoteNode, link) if err != nil { return err } - ted.Nodes[node.GetAsn()][node.GetRouterID()].Links = append(ted.Nodes[node.GetAsn()][node.GetRouterID()].Links, lsLink) + ted.Nodes[node.GetAsn()][node.GetRouterId()].Links = append(ted.Nodes[node.GetAsn()][node.GetRouterId()].Links, lsLink) } for _, prefix := range node.LsPrefixes { - lsPrefix, err := createLsPrefix(ted.Nodes[node.GetAsn()][node.GetRouterID()], prefix) + lsPrefix, err := createLsPrefix(ted.Nodes[node.GetAsn()][node.GetRouterId()], prefix) if err != nil { return err } - ted.Nodes[node.GetAsn()][node.GetRouterID()].Prefixes = append(ted.Nodes[node.GetAsn()][node.GetRouterID()].Prefixes, lsPrefix) + ted.Nodes[node.GetAsn()][node.GetRouterId()].Prefixes = append(ted.Nodes[node.GetAsn()][node.GetRouterId()].Prefixes, lsPrefix) } return nil @@ -206,14 +198,14 @@ func createLsLink(localNode, remoteNode *table.LsNode, link *pb.LsLink) (*table. lsLink := &table.LsLink{ LocalNode: localNode, RemoteNode: remoteNode, - AdjSid: link.GetAdjSID(), + AdjSid: link.GetAdjSid(), } var err error - lsLink.LocalIP, err = netip.ParseAddr(link.GetLocalIP()) + lsLink.LocalIP, err = netip.ParseAddr(link.GetLocalIp()) if err != nil { return nil, err } - lsLink.RemoteIP, err = netip.ParseAddr(link.GetRemoteIP()) + lsLink.RemoteIP, err = netip.ParseAddr(link.GetRemoteIp()) if err != nil { return nil, err } diff --git a/cmd/pola/root.go b/cmd/pola/root.go index b4203bd6..12fb57cb 100644 --- a/cmd/pola/root.go +++ b/cmd/pola/root.go @@ -9,7 +9,7 @@ import ( "fmt" "net" - pb "github.com/nttcom/pola/api/grpc" + pb "github.com/nttcom/pola/api/pola/v1" "github.com/spf13/cobra" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" diff --git a/cmd/pola/session_del.go b/cmd/pola/session_del.go index 357a03ab..21b32c14 100644 --- a/cmd/pola/session_del.go +++ b/cmd/pola/session_del.go @@ -9,7 +9,7 @@ import ( "fmt" "net/netip" - pb "github.com/nttcom/pola/api/grpc" + pb "github.com/nttcom/pola/api/pola/v1" "github.com/nttcom/pola/cmd/pola/grpc" "github.com/spf13/cobra" ) @@ -35,10 +35,10 @@ func newSessionDelCmd() *cobra.Command { } func delSession(session netip.Addr, jsonFlag bool) error { - ss := &pb.Session{ + request := &pb.DeleteSessionRequest{ Addr: session.AsSlice(), } - err := grpc.DeleteSession(client, ss) + err := grpc.DeleteSession(client, request) if err != nil { return err } diff --git a/cmd/pola/sr_policy_add.go b/cmd/pola/sr_policy_add.go index 40f93c6a..e90cfe03 100644 --- a/cmd/pola/sr_policy_add.go +++ b/cmd/pola/sr_policy_add.go @@ -14,7 +14,7 @@ import ( "github.com/spf13/cobra" yaml "gopkg.in/yaml.v2" - pb "github.com/nttcom/pola/api/grpc" + pb "github.com/nttcom/pola/api/pola/v1" "github.com/nttcom/pola/cmd/pola/grpc" ) @@ -22,9 +22,9 @@ func newSRPolicyAddCmd() *cobra.Command { srPolicyAddCmd := &cobra.Command{ Use: "add", RunE: func(cmd *cobra.Command, args []string) error { - noLinkStateFlag, err := cmd.Flags().GetBool("no-link-state") + noSIDValidateFlag, err := cmd.Flags().GetBool("no-sid-validate") if err != nil { - return fmt.Errorf("failed to retrieve 'no-link-state' flag: %v", err) + return fmt.Errorf("failed to retrieve 'no-sid-validate' flag: %v", err) } filepath, err := cmd.Flags().GetString("file") @@ -50,14 +50,14 @@ func newSRPolicyAddCmd() *cobra.Command { return fmt.Errorf("YAML syntax error in file \"%s\": %v", filepath, err) } - if err := addSRPolicy(inputData, jsonFmt, noLinkStateFlag); err != nil { + if err := addSRPolicy(inputData, jsonFmt, noSIDValidateFlag); err != nil { return fmt.Errorf("failed to add SR policy: %v", err) } return nil }, } - srPolicyAddCmd.Flags().BoolP("no-link-state", "l", false, "add SR Policy without Link State") + srPolicyAddCmd.Flags().BoolP("no-sid-validate", "s", false, "disable SR policy SID validation") srPolicyAddCmd.Flags().StringP("file", "f", "", "[mandatory] path to YAML formatted LSP information file") return srPolicyAddCmd @@ -90,13 +90,13 @@ type InputFormat struct { ASN uint32 `yaml:"asn"` } -func addSRPolicy(input InputFormat, jsonFlag bool, noLinkStateFlag bool) error { - if noLinkStateFlag { - if err := addSRPolicyNoLinkState(input); err != nil { +func addSRPolicy(input InputFormat, jsonFlag bool, explicitPathFlag bool) error { + if explicitPathFlag { + if err := addSRPolicyWithoutSIDValidation(input); err != nil { return err } } else { - if err := addSRPolicyLinkState(input); err != nil { + if err := addSRPolicyWithSIDValidation(input); err != nil { return err } } @@ -109,7 +109,7 @@ func addSRPolicy(input InputFormat, jsonFlag bool, noLinkStateFlag bool) error { return nil } -func addSRPolicyNoLinkState(input InputFormat) error { +func addSRPolicyWithoutSIDValidation(input InputFormat) error { if !input.SRPolicy.PCEPSessionAddr.IsValid() || input.SRPolicy.Color == 0 || !input.SRPolicy.SrcAddr.IsValid() || !input.SRPolicy.DstAddr.IsValid() || len(input.SRPolicy.SegmentList) == 0 { sampleInput := "srPolicy:\n" + " pcepSessionAddr: 192.0.2.1\n" + @@ -138,7 +138,7 @@ func addSRPolicyNoLinkState(input InputFormat) error { segmentList = append(segmentList, pbSeg) } srPolicy := &pb.SRPolicy{ - PCEPSessionAddr: input.SRPolicy.PCEPSessionAddr.AsSlice(), + PcepSessionAddr: input.SRPolicy.PCEPSessionAddr.AsSlice(), SrcAddr: input.SRPolicy.SrcAddr.AsSlice(), DstAddr: input.SRPolicy.DstAddr.AsSlice(), SegmentList: segmentList, @@ -146,17 +146,17 @@ func addSRPolicyNoLinkState(input InputFormat) error { PolicyName: input.SRPolicy.Name, } - inputData := &pb.CreateSRPolicyInput{ - SRPolicy: srPolicy, + request := &pb.CreateSRPolicyRequest{ + SrPolicy: srPolicy, } - if err := grpc.CreateSRPolicyWithoutLinkState(client, inputData); err != nil { + if err := grpc.CreateSRPolicy(client, request); err != nil { return err } return nil } -func addSRPolicyLinkState(input InputFormat) error { +func addSRPolicyWithSIDValidation(input InputFormat) error { sampleInputDynamic := "#case: dynamic path\n" + "asn: 65000\n" + "srPolicy:\n" + @@ -184,7 +184,7 @@ func addSRPolicyLinkState(input InputFormat) error { "input example is below\n\n" + sampleInputDynamic + sampleInputExplicit + - "or, if create SR Policy without TED, then use `--no-link-state` flag\n" + "or, if create SR Policy without TED, then use `--no-sid-validate` flag\n" return errors.New(errMsg) } @@ -200,7 +200,7 @@ func addSRPolicyLinkState(input InputFormat) error { return errors.New(errMsg) } - srPolicyType = pb.SRPolicyType_EXPLICIT + srPolicyType = pb.SRPolicyType_SR_POLICY_TYPE_EXPLICIT for _, segment := range input.SRPolicy.SegmentList { segmentList = append(segmentList, &pb.Segment{Sid: segment.Sid}) } @@ -211,14 +211,14 @@ func addSRPolicyLinkState(input InputFormat) error { sampleInputDynamic return errors.New(errMsg) } - srPolicyType = pb.SRPolicyType_DYNAMIC + srPolicyType = pb.SRPolicyType_SR_POLICY_TYPE_DYNAMIC switch input.SRPolicy.Metric { case "igp": - metric = pb.MetricType_IGP + metric = pb.MetricType_METRIC_TYPE_IGP case "delay": - metric = pb.MetricType_DELAY + metric = pb.MetricType_METRIC_TYPE_DELAY case "te": - metric = pb.MetricType_TE + metric = pb.MetricType_METRIC_TYPE_TE default: return fmt.Errorf("invalid input `metric`") } @@ -228,17 +228,17 @@ func addSRPolicyLinkState(input InputFormat) error { } srPolicy := &pb.SRPolicy{ - PCEPSessionAddr: input.SRPolicy.PCEPSessionAddr.AsSlice(), - SrcRouterID: input.SRPolicy.SrcRouterID, - DstRouterID: input.SRPolicy.DstRouterID, + PcepSessionAddr: input.SRPolicy.PCEPSessionAddr.AsSlice(), + SrcRouterId: input.SRPolicy.SrcRouterID, + DstRouterId: input.SRPolicy.DstRouterID, Color: input.SRPolicy.Color, PolicyName: input.SRPolicy.Name, Type: srPolicyType, SegmentList: segmentList, Metric: metric, } - inputData := &pb.CreateSRPolicyInput{ - SRPolicy: srPolicy, + inputData := &pb.CreateSRPolicyRequest{ + SrPolicy: srPolicy, Asn: input.ASN, } if err := grpc.CreateSRPolicy(client, inputData); err != nil { diff --git a/cmd/pola/sr_policy_delete.go b/cmd/pola/sr_policy_delete.go index 5bf780c8..e5d86b2d 100644 --- a/cmd/pola/sr_policy_delete.go +++ b/cmd/pola/sr_policy_delete.go @@ -10,7 +10,7 @@ import ( "fmt" "os" - pb "github.com/nttcom/pola/api/grpc" + pb "github.com/nttcom/pola/api/pola/v1" "github.com/spf13/cobra" yaml "gopkg.in/yaml.v2" @@ -70,13 +70,13 @@ func deleteSRPolicy(input InputFormat, jsonFlag bool) error { } srPolicy := &pb.SRPolicy{ - PCEPSessionAddr: input.SRPolicy.PCEPSessionAddr.AsSlice(), + PcepSessionAddr: input.SRPolicy.PCEPSessionAddr.AsSlice(), DstAddr: input.SRPolicy.DstAddr.AsSlice(), Color: input.SRPolicy.Color, PolicyName: input.SRPolicy.Name, } - inputData := &pb.DeleteSRPolicyInput{ - SRPolicy: srPolicy, + inputData := &pb.DeleteSRPolicyRequest{ + SrPolicy: srPolicy, Asn: input.ASN, } if err := grpc.DeleteSRPolicy(client, inputData); err != nil { diff --git a/examples/containerlab/srv6_te_l3vpn/README.md b/examples/containerlab/srv6_te_l3vpn/README.md index 7d25f91b..95212ff3 100644 --- a/examples/containerlab/srv6_te_l3vpn/README.md +++ b/examples/containerlab/srv6_te_l3vpn/README.md @@ -72,9 +72,9 @@ no SR Policies Apply and check SR Policy ```bash -# pola sr-policy add -f pe01-policy1.yaml --no-link-state +# pola sr-policy add -f pe01-policy1.yaml --no-sid-validate success! -# pola sr-policy add -f pe02-policy1.yaml --no-link-state +# pola sr-policy add -f pe02-policy1.yaml --no-sid-validate success! # pola sr-policy list diff --git a/examples/tinet/sr-mpls_te_l3vpn/README.md b/examples/tinet/sr-mpls_te_l3vpn/README.md index 042df9d8..e372b472 100644 --- a/examples/tinet/sr-mpls_te_l3vpn/README.md +++ b/examples/tinet/sr-mpls_te_l3vpn/README.md @@ -72,7 +72,7 @@ srPolicy: Apply and check SR Policy ```bash -# pola sr-policy add -f policy1.yaml --no-link-state +# pola sr-policy add -f policy1.yaml --no-sid-validate success! # pola sr-policy list diff --git a/pkg/server/grpc_server.go b/pkg/server/grpc_server.go index 491f93fc..f57559f2 100644 --- a/pkg/server/grpc_server.go +++ b/pkg/server/grpc_server.go @@ -14,8 +14,7 @@ import ( "net/netip" "slices" - "github.com/golang/protobuf/ptypes/empty" - pb "github.com/nttcom/pola/api/grpc" + pb "github.com/nttcom/pola/api/pola/v1" "github.com/nttcom/pola/internal/pkg/cspf" "github.com/nttcom/pola/internal/pkg/table" "github.com/nttcom/pola/pkg/packet/pcep" @@ -52,39 +51,31 @@ func (s *APIServer) Serve(address string, port string) error { return s.grpcServer.Serve(grpcListener) } -func (s *APIServer) CreateSRPolicy(ctx context.Context, input *pb.CreateSRPolicyInput) (*pb.RequestStatus, error) { - return s.createSRPolicy(ctx, input, true) -} - -func (s *APIServer) CreateSRPolicyWithoutLinkState(ctx context.Context, input *pb.CreateSRPolicyInput) (*pb.RequestStatus, error) { - return s.createSRPolicy(ctx, input, false) -} - -func validateCreateSRPolicy(input *pb.CreateSRPolicyInput, withLinkState bool) error { - if withLinkState { - return validate(input.GetSRPolicy(), input.GetAsn(), ValidationAdd) +func validateCreateSRPolicy(req *pb.CreateSRPolicyRequest, disablePathCompute bool) error { + if disablePathCompute { + return validate(req.GetSrPolicy(), req.GetAsn(), ValidationAddDisablePathCompute) } - return validate(input.GetSRPolicy(), input.GetAsn(), ValidationAddWithoutLinkState) + return validate(req.GetSrPolicy(), req.GetAsn(), ValidationAdd) } -func buildSegmentList(s *APIServer, input *pb.CreateSRPolicyInput, withLinkState bool) ([]table.Segment, netip.Addr, netip.Addr, error) { +func buildSegmentList(s *APIServer, input *pb.CreateSRPolicyRequest, disablePathCompute bool) ([]table.Segment, netip.Addr, netip.Addr, error) { var srcAddr, dstAddr netip.Addr var segmentList []table.Segment var err error - inputSRPolicy := input.GetSRPolicy() + inputSRPolicy := input.GetSrPolicy() - if withLinkState { + if !disablePathCompute { if s.pce.ted == nil { return nil, netip.Addr{}, netip.Addr{}, errors.New("ted is disabled") } - srcAddr, err = getLoopbackAddr(s.pce, input.GetAsn(), inputSRPolicy.GetSrcRouterID()) + srcAddr, err = getLoopbackAddr(s.pce, input.GetAsn(), inputSRPolicy.GetSrcRouterId()) if err != nil { return nil, netip.Addr{}, netip.Addr{}, err } - dstAddr, err = getLoopbackAddr(s.pce, input.GetAsn(), inputSRPolicy.GetDstRouterID()) + dstAddr, err = getLoopbackAddr(s.pce, input.GetAsn(), inputSRPolicy.GetDstRouterId()) if err != nil { return nil, netip.Addr{}, netip.Addr{}, err } @@ -109,12 +100,12 @@ func buildSegmentList(s *APIServer, input *pb.CreateSRPolicyInput, withLinkState return segmentList, srcAddr, dstAddr, nil } -func sendSRPolicyRequest(s *APIServer, input *pb.CreateSRPolicyInput, segmentList []table.Segment, srcAddr, dstAddr netip.Addr) (*pb.RequestStatus, error) { - inputSRPolicy := input.GetSRPolicy() +func sendSRPolicyRequest(s *APIServer, input *pb.CreateSRPolicyRequest, segmentList []table.Segment, srcAddr, dstAddr netip.Addr) error { + inputSRPolicy := input.GetSrPolicy() - pcepSession, err := getSyncedPCEPSession(s.pce, inputSRPolicy.GetPCEPSessionAddr()) + pcepSession, err := getSyncedPCEPSession(s.pce, inputSRPolicy.GetPcepSessionAddr()) if err != nil { - return &pb.RequestStatus{IsSuccess: false}, err + return fmt.Errorf("failed to get synchronized PCEP session: %w", err) } srPolicy := table.SRPolicy{ @@ -131,38 +122,43 @@ func sendSRPolicyRequest(s *APIServer, input *pb.CreateSRPolicyInput, segmentLis srPolicy.PlspID = id if err := pcepSession.SendPCUpdate(srPolicy); err != nil { - return &pb.RequestStatus{IsSuccess: false}, err + return fmt.Errorf("failed to send PC update: %w", err) } } else { s.logger.Debug("Request to create SR Policy") if err := pcepSession.RequestSRPolicyCreated(srPolicy); err != nil { - return &pb.RequestStatus{IsSuccess: false}, err + return fmt.Errorf("failed to request SR policy creation: %w", err) } } - return &pb.RequestStatus{IsSuccess: true}, nil + return nil } -func (s *APIServer) createSRPolicy(_ context.Context, input *pb.CreateSRPolicyInput, withLinkState bool) (*pb.RequestStatus, error) { - if err := validateCreateSRPolicy(input, withLinkState); err != nil { - return &pb.RequestStatus{IsSuccess: false}, err +func (s *APIServer) CreateSRPolicy(ctx context.Context, req *pb.CreateSRPolicyRequest) (*pb.CreateSRPolicyResponse, error) { + sidvalidate := req.GetSidValidate() + if err := validateCreateSRPolicy(req, sidvalidate); err != nil { + return nil, fmt.Errorf("failed to validate SR policy creation: %w", err) } - segmentList, srcAddr, dstAddr, err := buildSegmentList(s, input, withLinkState) + segmentList, srcAddr, dstAddr, err := buildSegmentList(s, req, sidvalidate) if err != nil { - return &pb.RequestStatus{IsSuccess: false}, err + return nil, fmt.Errorf("failed to build segment list: %w", err) + } + + if err := sendSRPolicyRequest(s, req, segmentList, srcAddr, dstAddr); err != nil { + return nil, fmt.Errorf("failed to send SR policy request: %w", err) } - return sendSRPolicyRequest(s, input, segmentList, srcAddr, dstAddr) + return &pb.CreateSRPolicyResponse{IsSuccess: true}, nil } -func (s *APIServer) DeleteSRPolicy(ctx context.Context, input *pb.DeleteSRPolicyInput) (*pb.RequestStatus, error) { - err := validate(input.GetSRPolicy(), input.GetAsn(), ValidationDelete) +func (s *APIServer) DeleteSRPolicy(ctx context.Context, input *pb.DeleteSRPolicyRequest) (*pb.DeleteSRPolicyResponse, error) { + err := validate(input.GetSrPolicy(), input.GetAsn(), ValidationDelete) if err != nil { - return &pb.RequestStatus{IsSuccess: false}, err + return &pb.DeleteSRPolicyResponse{IsSuccess: false}, err } - inputSRPolicy := input.GetSRPolicy() + inputSRPolicy := input.GetSrPolicy() var srcAddr, dstAddr netip.Addr var segmentList []table.Segment @@ -171,7 +167,7 @@ func (s *APIServer) DeleteSRPolicy(ctx context.Context, input *pb.DeleteSRPolicy for _, segment := range inputSRPolicy.GetSegmentList() { seg, err := table.NewSegment(segment.GetSid()) if err != nil { - return &pb.RequestStatus{IsSuccess: false}, err + return &pb.DeleteSRPolicyResponse{IsSuccess: false}, err } segmentList = append(segmentList, seg) } @@ -183,9 +179,9 @@ func (s *APIServer) DeleteSRPolicy(ctx context.Context, input *pb.DeleteSRPolicy s.logger.Info("Received DeleteSRPolicy API request") s.logger.Debug("Received paramater", zap.String("input", string(inputJSON))) - pcepSession, err := getSyncedPCEPSession(s.pce, inputSRPolicy.GetPCEPSessionAddr()) + pcepSession, err := getSyncedPCEPSession(s.pce, inputSRPolicy.GetPcepSessionAddr()) if err != nil { - return &pb.RequestStatus{IsSuccess: false}, err + return &pb.DeleteSRPolicyResponse{IsSuccess: false}, err } srPolicy := table.SRPolicy{ @@ -198,19 +194,18 @@ func (s *APIServer) DeleteSRPolicy(ctx context.Context, input *pb.DeleteSRPolicy } if id, exists := pcepSession.SearchPlspID(inputSRPolicy.GetColor(), dstAddr); exists { - // Delete SR Policy s.logger.Debug("Request to delete SR Policy", zap.Uint32("plspID", id)) srPolicy.PlspID = id if err := pcepSession.RequestSRPolicyDeleted(srPolicy); err != nil { - return &pb.RequestStatus{IsSuccess: false}, nil + return &pb.DeleteSRPolicyResponse{IsSuccess: false}, nil } } else { // Invalid SR Policy - return &pb.RequestStatus{IsSuccess: false}, fmt.Errorf("requested SR Policy not found") + return &pb.DeleteSRPolicyResponse{IsSuccess: false}, fmt.Errorf("requested SR Policy not found") } - return &pb.RequestStatus{IsSuccess: true}, nil + return &pb.DeleteSRPolicyResponse{IsSuccess: true}, nil } func validate(inputSRPolicy *pb.SRPolicy, asn uint32, validationKind ValidationKind) error { @@ -224,27 +219,27 @@ func validate(inputSRPolicy *pb.SRPolicy, asn uint32, validationKind ValidationK type ValidationKind string const ( - ValidationAdd ValidationKind = "Add" - ValidationAddWithoutLinkState ValidationKind = "AddWithoutLinkState" - ValidationDelete ValidationKind = "Delete" + ValidationAdd ValidationKind = "Add" + ValidationAddDisablePathCompute ValidationKind = "AddDisablePathCompute" + ValidationDelete ValidationKind = "Delete" ) var validator = map[ValidationKind]func(policy *pb.SRPolicy, asn uint32) bool{ ValidationKind("Add"): func(policy *pb.SRPolicy, asn uint32) bool { return asn != 0 && - policy.PCEPSessionAddr != nil && + policy.PcepSessionAddr != nil && policy.Color != 0 && - policy.SrcRouterID != "" && - policy.DstRouterID != "" + policy.SrcRouterId != "" && + policy.DstRouterId != "" }, ValidationKind("AddWithoutLinkState"): func(policy *pb.SRPolicy, asn uint32) bool { - return policy.PCEPSessionAddr != nil && + return policy.PcepSessionAddr != nil && len(policy.SrcAddr) > 0 && len(policy.DstAddr) > 0 && len(policy.SegmentList) > 0 }, ValidationKind("Delete"): func(policy *pb.SRPolicy, asn uint32) bool { - return policy.PCEPSessionAddr != nil && + return policy.PcepSessionAddr != nil && policy.Color != 0 && len(policy.DstAddr) > 0 && policy.PolicyName != "" @@ -272,7 +267,7 @@ func getSegmentList(inputSRPolicy *pb.SRPolicy, asn uint32, ted *table.LsTED) ([ var segmentList []table.Segment switch inputSRPolicy.GetType() { - case pb.SRPolicyType_EXPLICIT: + case pb.SRPolicyType_SR_POLICY_TYPE_EXPLICIT: if len(inputSRPolicy.GetSegmentList()) == 0 { return nil, errors.New("no segments in SRPolicy input") } @@ -283,12 +278,12 @@ func getSegmentList(inputSRPolicy *pb.SRPolicy, asn uint32, ted *table.LsTED) ([ } segmentList = append(segmentList, sid) } - case pb.SRPolicyType_DYNAMIC: + case pb.SRPolicyType_SR_POLICY_TYPE_DYNAMIC: metricType, err := getMetricType(inputSRPolicy.GetMetric()) if err != nil { return nil, err } - segmentList, err = cspf.Cspf(inputSRPolicy.GetSrcRouterID(), inputSRPolicy.GetDstRouterID(), asn, metricType, ted) + segmentList, err = cspf.Cspf(inputSRPolicy.GetSrcRouterId(), inputSRPolicy.GetDstRouterId(), asn, metricType, ted) if err != nil { return nil, err } @@ -314,14 +309,14 @@ func getMetricType(metricType pb.MetricType) (table.MetricType, error) { } } -func (s *APIServer) GetSessionList(context.Context, *empty.Empty) (*pb.SessionList, error) { +func (s *APIServer) GetSessionList(ctx context.Context, _ *pb.GetSessionListRequest) (*pb.GetSessionListResponse, error) { s.logger.Info("Received GetSessionList API request") - var ret pb.SessionList + var sessions []*pb.Session for _, pcepSession := range s.pce.sessionList { ss := &pb.Session{ Addr: pcepSession.peerAddr.AsSlice(), - State: pb.SessionState_UP, // Only the UP state in the current specification + State: pb.SessionState_SESSION_STATE_UP, // Only the UP state in the current specification Caps: []string{}, IsSynced: pcepSession.isSynced, } @@ -329,21 +324,23 @@ func (s *APIServer) GetSessionList(context.Context, *empty.Empty) (*pb.SessionLi ss.Caps = append(ss.Caps, cap.CapStrings()...) } ss.Caps = slices.Compact(ss.Caps) - ret.Sessions = append(ret.Sessions, ss) + sessions = append(sessions, ss) } - s.logger.Debug("Send GetPeerAddrList API reply") - return &ret, nil + s.logger.Debug("Send GetSessionList API reply") + return &pb.GetSessionListResponse{ + Sessions: sessions, + }, nil } -func (s *APIServer) GetSRPolicyList(context.Context, *empty.Empty) (*pb.SRPolicyList, error) { +func (s *APIServer) GetSRPolicyList(ctx context.Context, _ *pb.GetSRPolicyListRequest) (*pb.GetSRPolicyListResponse, error) { s.logger.Info("Received GetSRPolicyList API request") - var ret pb.SRPolicyList + var srPolicies []*pb.SRPolicy for ssAddr, policies := range s.pce.SRPolicies() { for _, policy := range policies { - srPolicyData := &pb.SRPolicy{ - PCEPSessionAddr: ssAddr.AsSlice(), + srPolicy := &pb.SRPolicy{ + PcepSessionAddr: ssAddr.AsSlice(), SegmentList: make([]*pb.Segment, 0), Color: policy.Color, Preference: policy.Preference, @@ -353,23 +350,25 @@ func (s *APIServer) GetSRPolicyList(context.Context, *empty.Empty) (*pb.SRPolicy } for _, segment := range policy.SegmentList { - srPolicyData.SegmentList = append(srPolicyData.SegmentList, &pb.Segment{ + srPolicy.SegmentList = append(srPolicy.SegmentList, &pb.Segment{ Sid: segment.SidString(), }) } - ret.SRPolicies = append(ret.SRPolicies, srPolicyData) + srPolicies = append(srPolicies, srPolicy) } } s.logger.Debug("Send SRPolicyList API reply") - return &ret, nil + return &pb.GetSRPolicyListResponse{ + SrPolicies: srPolicies, + }, nil } -func (s *APIServer) GetTED(context.Context, *empty.Empty) (*pb.TED, error) { +func (s *APIServer) GetTED(ctx context.Context, req *pb.GetTEDRequest) (*pb.GetTEDResponse, error) { s.logger.Info("Received GetTED API request") - ret := &pb.TED{ + ret := &pb.GetTEDResponse{ Enable: true, } @@ -384,8 +383,8 @@ func (s *APIServer) GetTED(context.Context, *empty.Empty) (*pb.TED, error) { for _, lsNode := range lsNodes { node := &pb.LsNode{ Asn: lsNode.ASN, - RouterID: lsNode.RouterID, - IsisAreaID: lsNode.IsisAreaID, + RouterId: lsNode.RouterID, + IsisAreaId: lsNode.IsisAreaID, Hostname: lsNode.Hostname, SrgbBegin: lsNode.SrgbBegin, SrgbEnd: lsNode.SrgbEnd, @@ -395,19 +394,18 @@ func (s *APIServer) GetTED(context.Context, *empty.Empty) (*pb.TED, error) { for _, lsLink := range lsNode.Links { link := &pb.LsLink{ - LocalRouterID: lsLink.LocalNode.RouterID, - LocalASN: lsLink.LocalNode.ASN, - LocalIP: lsLink.LocalIP.String(), - RemoteRouterID: lsLink.RemoteNode.RouterID, - RemoteASN: lsLink.RemoteNode.ASN, - RemoteIP: lsLink.RemoteIP.String(), + LocalRouterId: lsLink.LocalNode.RouterID, + LocalAsn: lsLink.LocalNode.ASN, + LocalIp: lsLink.LocalIP.String(), + RemoteRouterId: lsLink.RemoteNode.RouterID, + RemoteAsn: lsLink.RemoteNode.ASN, + RemoteIp: lsLink.RemoteIP.String(), Metrics: make([]*pb.Metric, 0, len(lsLink.Metrics)), - AdjSID: lsLink.AdjSid, + AdjSid: lsLink.AdjSid, } for _, lsMetric := range lsLink.Metrics { metricType, ok := pb.MetricType_value[lsMetric.Type.String()] - if !ok { return nil, fmt.Errorf("invalid metric type: %s", lsMetric.Type.String()) } @@ -440,24 +438,24 @@ func (s *APIServer) GetTED(context.Context, *empty.Empty) (*pb.TED, error) { return ret, nil } -func (c *APIServer) DeleteSession(ctx context.Context, input *pb.Session) (*pb.RequestStatus, error) { - ssAddr, ok := netip.AddrFromSlice(input.GetAddr()) +func (s *APIServer) DeleteSession(ctx context.Context, req *pb.DeleteSessionRequest) (*pb.DeleteSessionResponse, error) { + ssAddr, ok := netip.AddrFromSlice(req.GetAddr()) if !ok { - return nil, fmt.Errorf("invalid address: %v", input.GetAddr()) + return nil, fmt.Errorf("invalid address: %v", req.GetAddr()) } - s := c.pce - ss := s.SearchSession(ssAddr, false) + pce := s.pce + ss := pce.SearchSession(ssAddr, false) if ss == nil { return nil, fmt.Errorf("no session with address %s found", ssAddr) } if err := ss.SendClose(pcep.CloseReasonNoExplanationProvided); err != nil { - return &pb.RequestStatus{IsSuccess: false}, fmt.Errorf("failed to send close message: %v", err) + return &pb.DeleteSessionResponse{IsSuccess: false}, fmt.Errorf("failed to send close message: %v", err) } // Remove session info from PCE server - s.closeSession(ss) + pce.closeSession(ss) - return &pb.RequestStatus{IsSuccess: true}, nil + return &pb.DeleteSessionResponse{IsSuccess: true}, nil } diff --git a/tools/grpc/go/add_sr-policy/add_sr-policy.go b/tools/grpc/go/add_sr-policy/add_sr-policy.go index a2f2de71..c628a98b 100644 --- a/tools/grpc/go/add_sr-policy/add_sr-policy.go +++ b/tools/grpc/go/add_sr-policy/add_sr-policy.go @@ -15,7 +15,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" - pb "github.com/nttcom/pola/api/grpc" + pb "github.com/nttcom/pola/api/pola/v1" ) func main() { @@ -41,17 +41,18 @@ func main() { ssAddr := netip.MustParseAddr("192.0.2.1") - r, err := c.CreateSRPolicy(ctx, &pb.CreateSRPolicyInput{ + r, err := c.CreateSRPolicy(ctx, &pb.CreateSRPolicyRequest{ Asn: 65000, - SRPolicy: &pb.SRPolicy{ - PCEPSessionAddr: ssAddr.AsSlice(), - SrcRouterID: "0000.0aff.0001", - DstRouterID: "0000.0aff.0004", + SrPolicy: &pb.SRPolicy{ + PcepSessionAddr: ssAddr.AsSlice(), + SrcRouterId: "0000.0aff.0001", + DstRouterId: "0000.0aff.0004", Color: uint32(100), PolicyName: "sample-name", - Type: pb.SRPolicyType_DYNAMIC, - Metric: pb.MetricType_TE, + Type: pb.SRPolicyType_SR_POLICY_TYPE_DYNAMIC, + Metric: pb.MetricType_METRIC_TYPE_TE, }, + SidValidate: true, }) if err != nil { log.Fatalf("c.CreateSRPolicy error: %v", err) diff --git a/tools/grpc/go/add_sr-policy_no_ls/add_sr-policy_no_ls.go b/tools/grpc/go/add_sr-policy_no_ls/add_sr-policy_no_ls.go index faeb8d33..328347a9 100644 --- a/tools/grpc/go/add_sr-policy_no_ls/add_sr-policy_no_ls.go +++ b/tools/grpc/go/add_sr-policy_no_ls/add_sr-policy_no_ls.go @@ -15,7 +15,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" - pb "github.com/nttcom/pola/api/grpc" + pb "github.com/nttcom/pola/api/pola/v1" ) func main() { @@ -43,9 +43,9 @@ func main() { srcAddr := netip.MustParseAddr("192.0.2.1") dstAddr := netip.MustParseAddr("192.0.2.2") - r, err := c.CreateSRPolicyWithoutLinkState(ctx, &pb.CreateSRPolicyInput{ - SRPolicy: &pb.SRPolicy{ - PCEPSessionAddr: ssAddr.AsSlice(), + r, err := c.CreateSRPolicy(ctx, &pb.CreateSRPolicyRequest{ + SrPolicy: &pb.SRPolicy{ + PcepSessionAddr: ssAddr.AsSlice(), SrcAddr: srcAddr.AsSlice(), DstAddr: dstAddr.AsSlice(), Color: uint32(100), @@ -56,9 +56,10 @@ func main() { {Sid: "16004"}, }, }, + SidValidate: false, }) if err != nil { - log.Fatalf("c.CreateSRPolicyWithoutLinkState error: %v", err) + log.Fatalf("c.CreateSRPolicy error: %v", err) } log.Printf("Success: %#v", r) diff --git a/tools/grpc/go/del_session/del_session.go b/tools/grpc/go/del_session/del_session.go index e395d56e..2b871b1e 100644 --- a/tools/grpc/go/del_session/del_session.go +++ b/tools/grpc/go/del_session/del_session.go @@ -15,7 +15,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" - pb "github.com/nttcom/pola/api/grpc" + pb "github.com/nttcom/pola/api/pola/v1" ) func main() { @@ -39,7 +39,7 @@ func main() { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - ss := &pb.Session{ + ss := &pb.DeleteSessionRequest{ Addr: netip.MustParseAddr("192.0.2.1").AsSlice(), } diff --git a/tools/grpc/go/show_session/show_session.go b/tools/grpc/go/show_session/show_session.go index 91b2b5df..13313687 100644 --- a/tools/grpc/go/show_session/show_session.go +++ b/tools/grpc/go/show_session/show_session.go @@ -16,8 +16,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" - empty "github.com/golang/protobuf/ptypes/empty" - pb "github.com/nttcom/pola/api/grpc" + pb "github.com/nttcom/pola/api/pola/v1" ) func main() { @@ -41,9 +40,8 @@ func main() { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - var empty empty.Empty - ret, err := c.GetSessionList(ctx, &empty) + ret, err := c.GetSessionList(ctx, &pb.GetSessionListRequest{}) if err != nil { log.Fatalf("unable to get session list from server: %v", err) } diff --git a/tools/grpc/go/show_sr-policy/show_sr-policy_list.go b/tools/grpc/go/show_sr-policy/show_sr-policy_list.go index 9ad2ece8..4c5f224d 100644 --- a/tools/grpc/go/show_sr-policy/show_sr-policy_list.go +++ b/tools/grpc/go/show_sr-policy/show_sr-policy_list.go @@ -16,8 +16,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" - empty "github.com/golang/protobuf/ptypes/empty" - pb "github.com/nttcom/pola/api/grpc" + pb "github.com/nttcom/pola/api/pola/v1" ) func main() { @@ -42,15 +41,14 @@ func main() { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - var empty empty.Empty - ret, err := c.GetSRPolicyList(ctx, &empty) + ret, err := c.GetSRPolicyList(ctx, &pb.GetSRPolicyListRequest{}) if err != nil { log.Fatalf("unable to get SR policy list from server: %v", err) } - for i, srPolicy := range ret.GetSRPolicies() { + for i, srPolicy := range ret.GetSrPolicies() { fmt.Printf("srPolicy(%d): \n", i) - sessionAddr := net.IP(srPolicy.GetPCEPSessionAddr()) + sessionAddr := net.IP(srPolicy.GetPcepSessionAddr()) fmt.Printf(" sessionAddr: %s\n", sessionAddr.String()) fmt.Printf(" policyName: %s\n", srPolicy.GetPolicyName()) fmt.Printf(" SrcAddr: %s\n", net.IP(srPolicy.GetSrcAddr())) diff --git a/tools/grpc/go/show_ted/show_ted.go b/tools/grpc/go/show_ted/show_ted.go index 1c9d10eb..04eea043 100644 --- a/tools/grpc/go/show_ted/show_ted.go +++ b/tools/grpc/go/show_ted/show_ted.go @@ -15,8 +15,7 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" - empty "github.com/golang/protobuf/ptypes/empty" - pb "github.com/nttcom/pola/api/grpc" + pb "github.com/nttcom/pola/api/pola/v1" ) func main() { @@ -40,9 +39,7 @@ func main() { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() - var empty empty.Empty - - ret, err := c.GetTED(ctx, &empty) + ret, err := c.GetTED(ctx, &pb.GetTEDRequest{}) if err != nil { log.Fatalf("unable to get TED info: %v", err) } From 9ea9ce62ced46b200e4411f7be2765946589f4c5 Mon Sep 17 00:00:00 2001 From: watal Date: Tue, 15 Apr 2025 13:31:11 +0900 Subject: [PATCH 12/87] refactor(yaml): use snake_case for YAML field names and improve consistency --- cmd/pola/sr_policy_add.go | 30 ++++++++++++++---------------- internal/config/config.go | 6 +++--- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/cmd/pola/sr_policy_add.go b/cmd/pola/sr_policy_add.go index e90cfe03..9ccf1847 100644 --- a/cmd/pola/sr_policy_add.go +++ b/cmd/pola/sr_policy_add.go @@ -63,30 +63,28 @@ func newSRPolicyAddCmd() *cobra.Command { return srPolicyAddCmd } -// Unify with table.Segment type Segment struct { - Sid string `yaml:"sid"` - LocalAddr string `yaml:"localAddr"` - RemoteAddr string `yaml:"remoteAddr"` - SidStructure string `yaml:"sidStructure"` + SID string `yaml:"sid"` + LocalAddr string `yaml:"local_addr"` + RemoteAddr string `yaml:"remote_addr"` + SIDStructure string `yaml:"sid_structure"` } -// Unify with table.SRPolciy type SRPolicy struct { - PCEPSessionAddr netip.Addr `yaml:"pcepSessionAddr"` - SrcAddr netip.Addr `yaml:"srcAddr"` - DstAddr netip.Addr `yaml:"dstAddr"` - SrcRouterID string `yaml:"srcRouterID"` - DstRouterID string `yaml:"dstRouterID"` + PCEPSessionAddr netip.Addr `yaml:"pcep_session_addr"` + SrcAddr netip.Addr `yaml:"src_addr"` + DstAddr netip.Addr `yaml:"dst_addr"` + SrcRouterID string `yaml:"src_router_id"` + DstRouterID string `yaml:"dst_router_id"` Name string `yaml:"name"` - SegmentList []Segment `yaml:"segmentList"` + SegmentList []Segment `yaml:"segment_list"` Color uint32 `yaml:"color"` Type string `yaml:"type"` Metric string `yaml:"metric"` } type InputFormat struct { - SRPolicy SRPolicy `yaml:"srPolicy"` + SRPolicy SRPolicy `yaml:"sr_policy"` ASN uint32 `yaml:"asn"` } @@ -130,10 +128,10 @@ func addSRPolicyWithoutSIDValidation(input InputFormat) error { segmentList := []*pb.Segment{} for _, segment := range input.SRPolicy.SegmentList { pbSeg := &pb.Segment{ - Sid: segment.Sid, + Sid: segment.SID, LocalAddr: segment.LocalAddr, RemoteAddr: segment.RemoteAddr, - SidStructure: segment.SidStructure, + SidStructure: segment.SIDStructure, } segmentList = append(segmentList, pbSeg) } @@ -202,7 +200,7 @@ func addSRPolicyWithSIDValidation(input InputFormat) error { } srPolicyType = pb.SRPolicyType_SR_POLICY_TYPE_EXPLICIT for _, segment := range input.SRPolicy.SegmentList { - segmentList = append(segmentList, &pb.Segment{Sid: segment.Sid}) + segmentList = append(segmentList, &pb.Segment{Sid: segment.SID}) } case "dynamic": if input.SRPolicy.Metric == "" { diff --git a/internal/config/config.go b/internal/config/config.go index 6e5b9474..fc24948e 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -34,7 +34,7 @@ type Log struct { } type GoBGP struct { - GRPCClient GRPCClient `yaml:"grpc-client"` + GRPCClient GRPCClient `yaml:"grpc_client"` } type TED struct { @@ -44,11 +44,11 @@ type TED struct { type Global struct { PCEP PCEP `yaml:"pcep"` - GRPCServer GRPCServer `yaml:"grpc-server"` + GRPCServer GRPCServer `yaml:"grpc_server"` Log Log `yaml:"log"` TED *TED `yaml:"ted"` GoBGP GoBGP `yaml:"gobgp"` - USidMode bool `yaml:"usid-mode"` + USidMode bool `yaml:"usid_mode"` } type Config struct { From 0951d9300257eb9b34d598ebd52494bc3571a945 Mon Sep 17 00:00:00 2001 From: watal Date: Tue, 15 Apr 2025 16:03:45 +0900 Subject: [PATCH 13/87] refactor(markdown): Apply markdownlint --- .markdownlint.json | 5 + CODE_OF_CONDUCT.md | 6 +- CONTRIBUTING.md | 16 +- README.md | 27 +- SECURITY.md | 7 +- build/package/README.md | 5 +- cmd/pola/README.md | 478 +++++++++--------- docs/sources/getting-started.md | 11 +- examples/containerlab/sr-mpls_pcep/README.md | 43 +- examples/containerlab/srv6_te_l3vpn/README.md | 32 +- 10 files changed, 363 insertions(+), 267 deletions(-) create mode 100644 .markdownlint.json diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 00000000..e10fb3b4 --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,5 @@ +{ + "MD013": false, + "MD033": false, + "MD041": false + } diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 0509a052..535f6a0f 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -116,7 +116,7 @@ the community. This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, available at -https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. +. Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). @@ -124,5 +124,5 @@ enforcement ladder](https://github.com/mozilla/diversity). [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see the FAQ at -https://www.contributor-covenant.org/faq. Translations are available at -https://www.contributor-covenant.org/translations. +. Translations are available at +. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c3c10578..3c5f03a6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,15 @@ +# Contributing + +Thank you for considering contributing to Pola PCE! +Below are the guidelines for contributing. + ## Creating Pull Requests -All updates, including those contributed by project members, must be reviewed via Pull Request. -## Creating issues -If you have a bug report, feature request, or other question, please create an issue. However, please refer to [Security Policy](https://github.com/nttcom/pola/blob/main/SECURITY.md) for vulnerabilities. +All updates, including those contributed by project members, +must be reviewed via pull request. + +## Creating Issues + +If you have a bug report, feature request, or other question, please create an issue. +However, please refer to the [security policy](https://github.com/nttcom/pola/blob/main/security.md) +for vulnerabilities. diff --git a/README.md b/README.md index 105b296a..efd5ddcf 100644 --- a/README.md +++ b/README.md @@ -3,16 +3,19 @@

# Pola PCE + [![Linter](https://github.com/nttcom/pola/actions/workflows/ci.yml/badge.svg)](https://github.com/nttcom/pola/actions) [![Releaser](https://github.com/nttcom/pola/actions/workflows/release.yml/badge.svg)](https://github.com/nttcom/pola/actions) -[![Go Report Card](https://goreportcard.com/badge/nttcom/pola)](https://goreportcard.com/report/github.com/nttcom/pola) +[![Go Report Card](https://goreportcard.com/badge/nttcom/pola)](https://goreportcard.com/report/github.com/nttcom/pola) [![Go Reference](https://pkg.go.dev/badge/github.com/nttcom/pola.svg)](https://pkg.go.dev/github.com/nttcom/pola) [![Go version](https://img.shields.io/github/go-mod/go-version/nttcom/pola)](https://go.dev/) [![License](https://img.shields.io/badge/license-MIT-blue)](LICENSE) -Pola PCE is an implementation of the Path Computation Element (PCE) and a PCEP Library in Go. +Pola PCE is an implementation of the Path Computation Element (PCE) +and a PCEP Library in Go. ## Features + * Support for SRv6(full-SID/uSID) and SR-MPLS * Implementation of active stateful PCE functionality (PCInitiate, PCUpdate, etc.) * Dynamic and explicit SR policy definition using YAML @@ -20,29 +23,41 @@ Pola PCE is an implementation of the Path Computation Element (PCE) and a PCEP L * Explicit path: Allows users to define and provision any Segment List ## Interoperability + ### SR-MPLS + * IOS-XR * Junos * FRRouting ### SRv6 (full-SID) + * Junos ### SRv6 (uSID) + * IOS-XR * Junos * VRP ## Installation & Use + * [Getting Started](docs/sources/getting-started.md) * Examples (powered by [Containerlab](https://containerlab.dev/)/[Tinet](https://github.com/tinynetwork/tinet)) * [SR-MPLS Example](examples/tinet/sr-mpls_te_l3vpn) * [SRv6 Example](examples/containerlab/srv6_te_l3vpn) ## Contributing -If you are interested in contributing to the project, please refer to the [CONTRIBUTING](https://github.com/nttcom/pola/blob/main/CONTRIBUTING.md) guidelines. -Feel free to fork the repository and create a Pull Request. Your contributions are highly appreciated. + +If you are interested in contributing to the project, please refer to the +[CONTRIBUTING](https://github.com/nttcom/pola/blob/main/CONTRIBUTING.md) +guidelines. +Feel free to fork the repository and create a Pull Request. +Your contributions are highly appreciated. ## Licensing -Pola PCE is licensed under the [MIT license](https://en.wikipedia.org/wiki/MIT_License). -For the full license text, see [LICENSE](https://github.com/nttcom/pola/blob/master/LICENSE). + +Pola PCE is licensed under the +[MIT license](https://en.wikipedia.org/wiki/MIT_License). +For the full license text, see +[LICENSE](https://github.com/nttcom/pola/blob/master/LICENSE). diff --git a/SECURITY.md b/SECURITY.md index d10b4a52..e133da03 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -4,8 +4,9 @@ **Please do not report security vulnerabilities in public issues on GitHub.** -Instead, please report by email to the Pola PCE maintainer ([watal](https://github.com/watal)). -Include "Vulnerability" in the subject line of the email. +Instead, please report by email to the Pola PCE maintainer ([watal](https://github.com/watal)). +Include "Vulnerability" in the subject line of the email. We will action the vulnerability within 72 hours of receiving the vulnerability report. -If you have suggestions for improving this policy, please submit a PR or Issue for discussion. +If you have suggestions for improving this policy, +please submit a PR or Issue for discussion. diff --git a/build/package/README.md b/build/package/README.md index 5ea7c29a..958a1fce 100644 --- a/build/package/README.md +++ b/build/package/README.md @@ -25,7 +25,6 @@ docker run -itd --network host \ /bin/bash -c "source ~/.bashrc;polad" ``` - ## Use bridge network mode ```bash @@ -49,7 +48,9 @@ docker pull ghcr.io/nttcom/pola:latest # Container up and detach CURRENTDIR=`pwd` docker run -itd --network pcep_net --ip \ - -v $CURRENTDIR/$MOUNTDIR:/$MOUNTDIR -v $LOGDIR:$LOGDIR -w /$MOUNTDIR ghcr.io/nttcom/pola:latest \ + -v $CURRENTDIR/$MOUNTDIR:/$MOUNTDIR \ + -v $LOGDIR:$LOGDIR \ + -w /$MOUNTDIR ghcr.io/nttcom/pola:latest \ /bin/bash -c "source ~/.bashrc;polad" # Connect the container's PCC to the network diff --git a/cmd/pola/README.md b/cmd/pola/README.md index 2206bddd..ad95d62e 100644 --- a/cmd/pola/README.md +++ b/cmd/pola/README.md @@ -5,33 +5,35 @@ ### From Go Package ```bash -$ go install github.com/nttcom/pola/cmd/pola@latest +go install github.com/nttcom/pola/cmd/pola@latest ``` ### From Source -**Getting the Source** +#### Getting the Source ```bash -$ git clone https://github.com/nttcom/pola.git +git clone https://github.com/nttcom/pola.git ``` -**Build & install** +#### Build & install ```bash -$ cd pola -$ go install ./cmd/pola +cd pola +go install ./cmd/pola # or, install with daemon -$ go install ./... +go install ./... ``` ## Command Reference ### pola session \[-j\] + Displays the peer addresses of the active session. JSON formatted response + ```json [ { @@ -59,9 +61,11 @@ JSON formatted response ``` ### pola session del *Address* \[-j\] + Deletes the specified session. JSON formatted response + ```json { "status": "success" @@ -69,102 +73,107 @@ JSON formatted response ``` ### pola sr-policy list \[-j\] + Displays the lsp list managed by polad. JSON formatted response + ```json { - "lsps": [ - { - "color": 999, - "dstAddr": "192.0.2.1", - "segmentList": [ - 16003, - 16001 - ], - "peerAddr": "192.0.2.2", - "policyName": "sample_policy1", - "preference": 100, - "srcAddr": "192.0.2.2" - }, - { - "color": 888 - "dstAddr": "192.0.2.2", - "segmentList": [ - 16003, - 16002 - ], - "peerAddr": "192.0.2.1", - "policyName": "sample_policy2", - "preference": 100, - "srcAddr": "192.0.2.1" - } - ] + "lsps": [ + { + "color": 999, + "dstAddr": "192.0.2.1", + "segmentList": [ + 16003, + 16001 + ], + "peerAddr": "192.0.2.2", + "policyName": "sample_policy1", + "preference": 100, + "srcAddr": "192.0.2.2" + }, + { + "color": 888, + "dstAddr": "192.0.2.2", + "segmentList": [ + 16003, + 16002 + ], + "peerAddr": "192.0.2.1", + "policyName": "sample_policy2", + "preference": 100, + "srcAddr": "192.0.2.1" + } + ] } ``` ※ want to change to this format later. + ```json -{ - "peers": [ + "peers": [ + { + "peerAddr": "192.0.2.1", + "lsps": [ { - "peerAddr": "192.0.2.1", - "lsps": [ - { - "policyName": "sample_policy1", - "srcAddr": "192.0.2.1", - "dstAddr": "192.0.2.2", - "segmentList": [ - 16003, - 16002 - ] - }, - { - "policyName": "sample_policy2", - "srcAddr": "192.0.2.1", - "dstAddr": "192.0.2.2", - "segmentList": [ - 16003, - 16001, - 16002 - ] - }, - ] + "policyName": "sample_policy1", + "srcAddr": "192.0.2.1", + "dstAddr": "192.0.2.2", + "segmentList": [ + 16003, + 16002 + ] }, { - "peerAddr": "192.0.2.2", - "lsps": [ - { - "policyName": "sample_policy3", - "srcAddr": "192.0.2.2", - "dstAddr": "192.0.2.1", - "segmentList": [ - 16003, - 16001 - ] - }, - { - "policyName": "sample_policy4", - "srcAddr": "192.0.2.2", - "dstAddr": "192.0.2.1", - "segmentList": [ - 16003, - 16002, - 16001 - ] - }, - ] - }, -] + "policyName": "sample_policy2", + "srcAddr": "192.0.2.1", + "dstAddr": "192.0.2.2", + "segmentList": [ + 16003, + 16001, + 16002 + ] + } + ] + }, + { + "peerAddr": "192.0.2.2", + "lsps": [ + { + "policyName": "sample_policy3", + "srcAddr": "192.0.2.2", + "dstAddr": "192.0.2.1", + "segmentList": [ + 16003, + 16001 + ] + }, + { + "policyName": "sample_policy4", + "srcAddr": "192.0.2.2", + "dstAddr": "192.0.2.1", + "segmentList": [ + 16003, + 16002, + 16001 + ] + } + ] + } + ] +} + ``` -### pola sr-policy add -f _filepath_ +### pola sr-policy add -f `filepath` -Create a new SR Policy **using TED** +Create a new SR Policy **using TED** #### Case: Dynamic Path calculate YAML input format + ```yaml asn: 65000 srPolicy: @@ -178,15 +187,17 @@ srPolicy: ``` JSON formatted response + ```json { - "status": "success" + "status": "success" } ``` #### Case: Explicit Path YAML input format + ```yaml asn: 65000 srPolicy: @@ -203,9 +214,10 @@ srPolicy: ``` JSON formatted response + ```json { - "status": "success" + "status": "success" } ``` @@ -213,11 +225,13 @@ JSON formatted response Create a new SR Policy **without using TED** -Should write the `localAddress` (and `remoteAddr` if Adj-SID) of each sid for creation of Nai +For each SID, write the `localAddress` (and `remoteAddr` if it's an Adj-SID) +to construct the NAI. See [JSON shema](schemas/polad_config.json) for input details. YAML input format + ```yaml srPolicy: pcepSessionAddr: "2001:0db8::1" @@ -241,177 +255,183 @@ srPolicy: ``` json formatted response + ```json { - "status": "success" + "status": "success" } ``` ### pola ted \[-j\] + Displays the ted managed by polad. JSON formatted response + ```json { - "ted": [ - { - "asn": 65000, - "hostname": "host1", - "isisAreaID": "490000", - "links": [ - { - "adjSid": 17, - "localIP": "10.0.1.1", - "metrics": [ - { - "type": "IGP", - "value": 10 - } - ], - "remoteIP": "10.0.1.2", - "remoteNode": "0000.0aff.0003" - }, - { - "adjSid": 18, - "localIP": "10.0.0.1", - "metrics": [ - { - "type": "IGP", - "value": 10 - } - ], - "remoteIP": "10.0.0.2", - "remoteNode": "0000.0aff.0002" - } - ], - "prefixes": [ - { - "prefix": "10.0.1.0/30" - }, - { - "prefix": "10.0.0.0/30" - }, - { - "prefix": "10.255.0.1/32", - "sidIndex": 1 - } - ], - "routerID": "0000.0aff.0001", - "srgbBegin": 16000, - "srgbEnd": 24000 - }, - { - "asn": 65000, - "hostname": "host2", - "isisAreaID": "490000", - "links": [ - { - "adjSid": 17, - "localIP": "10.0.1.2", - "metrics": [ - { - "type": "IGP", - "value": 10 - } - ], - "remoteIP": "10.0.1.1", - "remoteNode": "0000.0aff.0001" - }, - { - "adjSid": 16, - "localIP": "10.0.2.2", - "metrics": [ - { - "type": "IGP", - "value": 10 - } - ], - "remoteIP": "10.0.2.1", - "remoteNode": "0000.0aff.0002" - } - ], - "prefixes": [ - { - "prefix": "10.255.0.3/32", - "sidIndex": 3 - }, - { - "prefix": "10.0.2.0/30" - }, - { - "prefix": "10.0.1.0/30" - } - ], - "routerID": "0000.0aff.0003", - "srgbBegin": 16000, - "srgbEnd": 24000 - }, - { - "asn": 65000, - "hostname": "host3", - "isisAreaID": "490000", - "links": [ - { - "adjSid": 24001, - "localIP": "10.0.0.2", - "metrics": [ - { - "type": "IGP", - "value": 10 - } - ], - "remoteIP": "10.0.0.1", - "remoteNode": "0000.0aff.0001" - }, - { - "adjSid": 24003, - "localIP": "10.0.2.1", - "metrics": [ - { - "type": "IGP", - "value": 10 - } - ], - "remoteIP": "10.0.2.2", - "remoteNode": "0000.0aff.0201" - } - ], - "prefixes": [ - { - "prefix": "10.0.2.0/30" - }, - { - "prefix": "10.0.0.0/30" - }, - { - "prefix": "10.255.0.2/32", - "sidIndex": 2 - } - ], - "routerID": "0000.0aff.0002", - "srgbBegin": 16000, - "srgbEnd": 24000 - } - ] + "ted": [ + { + "asn": 65000, + "hostname": "host1", + "isisAreaID": "490000", + "links": [ + { + "adjSid": 17, + "localIP": "10.0.1.1", + "metrics": [ + { + "type": "IGP", + "value": 10 + } + ], + "remoteIP": "10.0.1.2", + "remoteNode": "0000.0aff.0003" + }, + { + "adjSid": 18, + "localIP": "10.0.0.1", + "metrics": [ + { + "type": "IGP", + "value": 10 + } + ], + "remoteIP": "10.0.0.2", + "remoteNode": "0000.0aff.0002" + } + ], + "prefixes": [ + { + "prefix": "10.0.1.0/30" + }, + { + "prefix": "10.0.0.0/30" + }, + { + "prefix": "10.255.0.1/32", + "sidIndex": 1 + } + ], + "routerID": "0000.0aff.0001", + "srgbBegin": 16000, + "srgbEnd": 24000 + }, + { + "asn": 65000, + "hostname": "host2", + "isisAreaID": "490000", + "links": [ + { + "adjSid": 17, + "localIP": "10.0.1.2", + "metrics": [ + { + "type": "IGP", + "value": 10 + } + ], + "remoteIP": "10.0.1.1", + "remoteNode": "0000.0aff.0001" + }, + { + "adjSid": 16, + "localIP": "10.0.2.2", + "metrics": [ + { + "type": "IGP", + "value": 10 + } + ], + "remoteIP": "10.0.2.1", + "remoteNode": "0000.0aff.0002" + } + ], + "prefixes": [ + { + "prefix": "10.255.0.3/32", + "sidIndex": 3 + }, + { + "prefix": "10.0.2.0/30" + }, + { + "prefix": "10.0.1.0/30" + } + ], + "routerID": "0000.0aff.0003", + "srgbBegin": 16000, + "srgbEnd": 24000 + }, + { + "asn": 65000, + "hostname": "host3", + "isisAreaID": "490000", + "links": [ + { + "adjSid": 24001, + "localIP": "10.0.0.2", + "metrics": [ + { + "type": "IGP", + "value": 10 + } + ], + "remoteIP": "10.0.0.1", + "remoteNode": "0000.0aff.0001" + }, + { + "adjSid": 24003, + "localIP": "10.0.2.1", + "metrics": [ + { + "type": "IGP", + "value": 10 + } + ], + "remoteIP": "10.0.2.2", + "remoteNode": "0000.0aff.0201" + } + ], + "prefixes": [ + { + "prefix": "10.0.2.0/30" + }, + { + "prefix": "10.0.0.0/30" + }, + { + "prefix": "10.255.0.2/32", + "sidIndex": 2 + } + ], + "routerID": "0000.0aff.0002", + "srgbBegin": 16000, + "srgbEnd": 24000 + } + ] } ``` ## Completion ## Bash + ```bash pola completion bash | sudo tee -a /usr/share/bash-completion/completions/pola >/dev/null source /usr/share/bash-completion/completions/pola ``` ## Zsh -```sh + +```bash pola completion zsh > /usr/local/share/zsh/site-functions/_pola compinit ``` ## Fish -```sh + +```bash pola completion fish > ~/.config/fish/completions/pola.fish fish_update_completions ``` diff --git a/docs/sources/getting-started.md b/docs/sources/getting-started.md index 8b691e5e..8d8ad443 100644 --- a/docs/sources/getting-started.md +++ b/docs/sources/getting-started.md @@ -7,18 +7,18 @@ This page explains how to use Pola PCE. ### From Go Package ```bash -$ go install github.com/nttcom/pola/cmd/polad@latest +go install github.com/nttcom/pola/cmd/polad@latest ``` ### From Source -**Getting the Source** +#### Getting the Source ```bash -$ git clone https://github.com/nttcom/pola.git +git clone https://github.com/nttcom/pola.git ``` -**Build & install** +#### Build & install ```bash $ cd pola @@ -116,4 +116,5 @@ $ sudo polad -f polad.yaml 2022-06-05T22:57:59.823Z info PCEP listen {"listenInfo": "192.0.2.254:4189"} ``` -After Polad is running, use [pola cmd](../../cmd/pola/README.md) or the [gRCP client](../../api/grpc/) for daemon operations +After Polad is running, use [pola cmd](../../cmd/pola/README.md) or the +[gRCP client](../../api/grpc/) for daemon operations diff --git a/examples/containerlab/sr-mpls_pcep/README.md b/examples/containerlab/sr-mpls_pcep/README.md index 1e481175..bd8a4ce5 100644 --- a/examples/containerlab/sr-mpls_pcep/README.md +++ b/examples/containerlab/sr-mpls_pcep/README.md @@ -1,19 +1,27 @@ # SR-MPLS + PCEP (IOS-XR/Junos/FRRouting) + Example topology powered by [Containerlab](https://containerlab.dev/) -![](./topo.png) +![Topology](./topo.png) + ## Requirements + * container host (Linux) * Cisco XRd image * Juniper vMX image + ## Usage + ### Install Containerlab & XRd/vMX + **[Install Containerlab](https://containerlab.dev/install/)** + ```bash -$ sudo bash -c "$(curl -sL https://get.containerlab.dev)" +sudo bash -c "$(curl -sL https://get.containerlab.dev)" ``` **Install Cisco XRd** Configure sysctl on host + ```bash $ vi /etc/sysctl.conf fs.inotify.max_user_instances=64000 @@ -27,17 +35,23 @@ net.core.wmem_max=67108864 net.ipv4.udp_mem=1124736 10000000 67108864 $ sysctl -p ``` + host-check (see: [link](https://xrdocs.io/virtual-routing/tutorials/2022-08-22-setting-up-host-environment-to-run-xrd/)) + ```bash git clone https://github.com/ios-xr/xrd-tools cd xrd-tools/scripts ./host-check --platform xrd-control-plane ``` + Load XRd container image + ```bash docker load -i .tar.gz ``` + Install Juniper vMX on Vrnetlab + ```bash $ sudo apt install make $ git clone https://github.com/hellt/vrnetlab && cd vrnetlab/vmx @@ -51,32 +65,44 @@ vrnetlab/vr-vmx 22.4R1.10 6d2704750cd7 3 minutes ago 10.8GB $ sudo rm -rf vrnetlab $ sudo docker builder prune -a ``` + ### Building a Lab Network + Create bridge + ```bash -$ sudo ip link add switch type bridge -$ sudo ip link set dev switch up +sudo ip link add switch type bridge +sudo ip link set dev switch up ``` + Enable MPLS kernel module -``` + +```bash sudo modprobe mpls_router sudo modprobe mpls_gso sudo modprobe mpls_iptunnel ``` + Start Containerlab network + ```bash -$ git clone https://github.com/nttcom/pola -$ cd pola/examples/containerlab/sr-mpls_pcep -$ sudo containerlab deploy +git clone https://github.com/nttcom/pola +cd pola/examples/containerlab/sr-mpls_pcep +sudo containerlab deploy ``` + Wait for starting vMX after execute `sudo containerlab deploy` (it takes some time). + ```bash $ docker logs clab-sr-mpls_pcep-pe02 -f 2023-02-20 15:03:26,233: launch INFO Startup complete in: 0:09:06.969773 ``` + ### Check PCEP Session + Connect to PCEP container, check PCEP session and SR policy + ```bash $ sudo docker exec -it clab-sr-mpls_pcep-pola-pce bash # polad -f polad.yaml > /dev/null 2>&1 & @@ -87,4 +113,5 @@ sessionAddr(2): 10.0.255.2 # pola sr-policy list no SR Policies ``` + Now the environment for PCEP verification is ready! diff --git a/examples/containerlab/srv6_te_l3vpn/README.md b/examples/containerlab/srv6_te_l3vpn/README.md index 95212ff3..9a91903c 100644 --- a/examples/containerlab/srv6_te_l3vpn/README.md +++ b/examples/containerlab/srv6_te_l3vpn/README.md @@ -2,21 +2,25 @@ Example topology powered by [Containerlab](https://containerlab.dev/) -![](./topo.png) +![Topology](./topo.png) ## Requirements + * container host (Linux) * Juniper vMX image ## Usage ### Install Containerlab & Juniper vMX + [Install Containerlab](https://containerlab.dev/install/) + ```bash -$ sudo bash -c "$(curl -sL https://get.containerlab.dev)" +sudo bash -c "$(curl -sL https://get.containerlab.dev)" ``` Install Juniper vMX on [Vrnetlab](https://containerlab.dev/manual/vrnetlab/) + ```bash $ sudo apt install make $ git clone https://github.com/hellt/vrnetlab && cd vrnetlab/vmx @@ -34,21 +38,25 @@ $ sudo docker builder prune -a ``` ### Building a Lab Network + Create bridge + ```bash -$ sudo ip link add switch type bridge -$ sudo ip link set dev switch up +sudo ip link add switch type bridge +sudo ip link set dev switch up ``` Start Containerlab network + ```bash -$ git clone https://github.com/nttcom/pola -$ cd pola/examples/containerlab/srv6_te_l3vpn +git clone https://github.com/nttcom/pola +cd pola/examples/containerlab/srv6_te_l3vpn -$ sudo containerlab deploy +sudo containerlab deploy ``` Wait for starting vMX after execute `sudo containerlab deploy` (it takes some time). + ```bash $ docker logs clab-srv6_te_l3vpn-pe01 -f @@ -56,7 +64,9 @@ $ docker logs clab-srv6_te_l3vpn-pe01 -f ``` ### Apply SR Policy + Connect to PCEP container, check PCEP session and SR policy + ```bash $ sudo docker exec -it clab-srv6_te_l3vpn-pola-pce bash @@ -71,6 +81,7 @@ no SR Policies ``` Apply and check SR Policy + ```bash # pola sr-policy add -f pe01-policy1.yaml --no-sid-validate success! @@ -96,8 +107,10 @@ Session: fd00::2 ``` Enter container pe01 and check SR Policy + * user: admin * pass: admin@123 + ```bash # exit $ ssh clab-srv6_te_l3vpn-pe01 -l admin @@ -151,6 +164,7 @@ fd00:a2::/64 *[BGP/170] 00:32:08, localpref 100, from fd00:ffff::2 Enter container host01 and check SRv6-TE * ping over VPN + ```bash admin@pe01> exit @@ -170,6 +184,7 @@ PING fd00:a2::1(fd00:a2::1) 56 data bytes ``` * Capture on containerlab host + ```bash $ sudo ip netns exec clab-srv6_te_l3vpn-pe01 tcpdump -nni eth1 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode @@ -182,7 +197,8 @@ listening on eth1, link-type EN10MB (Ethernet), capture size 262144 bytes 01:05:34.067531 IP6 fd00:ffff::2 > fd00:ffff:1:0:4:a::: srcrt (len=4, type=4, segleft=0[|srcrt] ``` -Also, you can analyze with Wireshark on your Local PC ([ref: Packet capture & Wireshark](https://containerlab.dev/manual/wireshark/)). +Also, you can analyze with Wireshark on your Local PC +([ref: Packet capture & Wireshark](https://containerlab.dev/manual/wireshark/)). ```bash ssh $clab_host "sudo -S ip netns exec clab-srv6_te_l3vpn-pe01 tcpdump -U -nni eth1 -w -" | wireshark -k -i - From e5e57b571c6cc23f3a4a117ba27072224bc2a1b6 Mon Sep 17 00:00:00 2001 From: watal Date: Tue, 15 Apr 2025 16:44:47 +0900 Subject: [PATCH 14/87] chore(ci): update CI workflow to improve buf and markdown lint checks --- .github/workflows/ci.yml | 43 ++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 20503e42..9603c796 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,10 +1,12 @@ -name: golangci-lint +name: Lint on: push: - branches: [ 'main' ] + branches: + - main pull_request: - branches: [ 'main' ] + branches: + - main types: - opened - reopened @@ -12,15 +14,40 @@ on: - ready_for_review jobs: - golangci: - name: lint + lint: + name: Lint runs-on: ubuntu-latest - if: github.event.pull_request.draft == false + if: github.event_name == 'push' && (github.event.pull_request.draft == false || github.event.pull_request == null) steps: - - uses: actions/checkout@v5 - - uses: actions/setup-go@v6 + - name: Checkout code + uses: actions/checkout@v5 + + - name: Setup Go + uses: actions/setup-go@v6 + with: + go-version-file: go.mod + - name: Run golangci-lint uses: golangci/golangci-lint-action@v8 with: version: latest args: --config=.golangci.yml + + - name: Install buf CLI + uses: bufbuild/buf-setup-action@v1 + with: + version: latest + + - name: Run buf lint + run: buf lint + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: latest + + - name: Install markdownlint-cli + run: npm install -g markdownlint-cli + + - name: Run markdownlint + run: markdownlint '**/*.md' --ignore node_modules From 0ec7aee463a87c11195445290190de732d01ecbc Mon Sep 17 00:00:00 2001 From: watal Date: Tue, 15 Apr 2025 16:46:14 +0900 Subject: [PATCH 15/87] chore(ci): apply lint checks to both main and develop branches --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9603c796..1f5a7301 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,9 +4,11 @@ on: push: branches: - main + - develop pull_request: branches: - main + - develop types: - opened - reopened @@ -17,7 +19,7 @@ jobs: lint: name: Lint runs-on: ubuntu-latest - if: github.event_name == 'push' && (github.event.pull_request.draft == false || github.event.pull_request == null) + if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.draft == false) steps: - name: Checkout code uses: actions/checkout@v5 From 4ab8b095d4f9c6d9c5cf42f09c2ea81f00a4aa26 Mon Sep 17 00:00:00 2001 From: watal Date: Tue, 15 Apr 2025 17:06:28 +0900 Subject: [PATCH 16/87] chore(ci): add buf.yml --- buf.yaml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 buf.yaml diff --git a/buf.yaml b/buf.yaml new file mode 100644 index 00000000..8808a922 --- /dev/null +++ b/buf.yaml @@ -0,0 +1,2 @@ +version: v1 +name: github.com/nttcom/pola From 20e1e03639f0feae5bef0b2fc5b99853201051ec Mon Sep 17 00:00:00 2001 From: watal Date: Wed, 8 Mar 2023 17:16:29 +0900 Subject: [PATCH 17/87] refactor,test(pcep): improve decode/serialization and add new tests for pcep_util, capability and tlv --- CREDITS | 33 ++ go.mod | 5 +- go.sum | 6 +- pkg/packet/pcep/capability_test.go | 97 ++++++ pkg/packet/pcep/pcep_util.go | 22 +- pkg/packet/pcep/pcep_util_test.go | 198 +++++++++++ pkg/packet/pcep/tlv.go | 452 ++++++++++++++++-------- pkg/packet/pcep/tlv_test.go | 535 +++++++++++++++++++++++++++++ 8 files changed, 1196 insertions(+), 152 deletions(-) create mode 100644 pkg/packet/pcep/capability_test.go create mode 100644 pkg/packet/pcep/pcep_util_test.go create mode 100644 pkg/packet/pcep/tlv_test.go diff --git a/CREDITS b/CREDITS index adc34a8f..a68ab975 100644 --- a/CREDITS +++ b/CREDITS @@ -2573,6 +2573,39 @@ THE SOFTWARE. ================================================================ +golang.org/x/exp +https://golang.org/x/exp +---------------------------------------------------------------- +Copyright 2009 The Go Authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google LLC nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +================================================================ + golang.org/x/net https://golang.org/x/net ---------------------------------------------------------------- diff --git a/go.mod b/go.mod index 64f5c189..4f009320 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,11 @@ module github.com/nttcom/pola go 1.24.2 require ( - github.com/golang/protobuf v1.5.4 github.com/osrg/gobgp/v3 v3.37.0 github.com/spf13/cobra v1.10.1 + github.com/stretchr/testify v1.11.1 go.uber.org/zap v1.27.0 + golang.org/x/exp v0.0.0-20251017212417-90e834f514db google.golang.org/grpc v1.76.0 google.golang.org/protobuf v1.36.10 gopkg.in/yaml.v2 v2.4.0 @@ -14,7 +15,9 @@ require ( ) require ( + github.com/davecgh/go-spew v1.1.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.9 // indirect go.uber.org/multierr v1.10.0 // indirect golang.org/x/net v0.42.0 // indirect diff --git a/go.sum b/go.sum index 303dd379..d32a8a74 100644 --- a/go.sum +++ b/go.sum @@ -22,8 +22,8 @@ github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= @@ -42,6 +42,8 @@ go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/exp v0.0.0-20251017212417-90e834f514db h1:by6IehL4BH5k3e3SJmcoNbOobMey2SLpAF79iPOEBvw= +golang.org/x/exp v0.0.0-20251017212417-90e834f514db/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70= golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= diff --git a/pkg/packet/pcep/capability_test.go b/pkg/packet/pcep/capability_test.go new file mode 100644 index 00000000..505acfa9 --- /dev/null +++ b/pkg/packet/pcep/capability_test.go @@ -0,0 +1,97 @@ +// Copyright (c) 2022 NTT Communications Corporation +// +// This software is released under the MIT License. +// see https://github.com/nttcom/pola/blob/main/LICENSE + +package pcep + +import ( + "reflect" + "testing" +) + +func TestPolaCapability(t *testing.T) { + tests := []struct { + name string + input []CapabilityInterface + expected []CapabilityInterface + }{ + { + name: "Basic Capability Test", + input: []CapabilityInterface{ + &StatefulPCECapability{ + LSPUpdateCapability: false, + IncludeDBVersion: true, + LSPInstantiationCapability: false, + TriggeredResync: true, + DeltaLSPSyncCapability: true, + TriggeredInitialSync: true, + }, + &PathSetupTypeCapability{ + PathSetupTypes: Psts{PathSetupTypeRSVPTE, PathSetupTypeSRTE, PathSetupTypeSRv6TE}, + SubTLVs: []TLVInterface{ + &SRPCECapability{ + HasUnlimitedMaxSIDDepth: false, + IsNAISupported: false, + MaximumSidDepth: uint8(16), + }, + }, + }, + &SRPCECapability{ + HasUnlimitedMaxSIDDepth: false, + IsNAISupported: false, + MaximumSidDepth: uint8(16), + }, + &AssocTypeList{ + AssocTypes: []AssocType{AssocTypePathProtectionAssociation, AssocTypeSRPolicyAssociation}, + }, + }, + expected: []CapabilityInterface{ + &StatefulPCECapability{ + LSPUpdateCapability: true, + IncludeDBVersion: false, + LSPInstantiationCapability: true, + TriggeredResync: false, + DeltaLSPSyncCapability: false, + TriggeredInitialSync: false, + P2mpCapability: false, + P2mpLSPUpdateCapability: false, + P2mpLSPInstantiationCapability: false, + LSPSchedulingCapability: false, + PdLSPCapability: false, + ColorCapability: true, + PathRecomputationCapability: false, + StrictPathCapability: false, + Relax: false, + }, + &PathSetupTypeCapability{ + PathSetupTypes: Psts{PathSetupTypeRSVPTE, PathSetupTypeSRTE, PathSetupTypeSRv6TE}, + SubTLVs: []TLVInterface{ + &SRPCECapability{ + HasUnlimitedMaxSIDDepth: false, + IsNAISupported: false, + MaximumSidDepth: uint8(16), + }, + }, + }, + &SRPCECapability{ + HasUnlimitedMaxSIDDepth: false, + IsNAISupported: false, + MaximumSidDepth: uint8(16), + }, + &AssocTypeList{ + AssocTypes: []AssocType{AssocTypePathProtectionAssociation, AssocTypeSRPolicyAssociation}, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := PolaCapability(tt.input) + if !reflect.DeepEqual(result, tt.expected) { + t.Fatalf("%s: expected %+v, got %+v", tt.name, tt.expected, result) + } + }) + } +} diff --git a/pkg/packet/pcep/pcep_util.go b/pkg/packet/pcep/pcep_util.go index a5fa3f0e..80ff0a4a 100644 --- a/pkg/packet/pcep/pcep_util.go +++ b/pkg/packet/pcep/pcep_util.go @@ -5,7 +5,11 @@ package pcep -import "encoding/binary" +import ( + "encoding/binary" + + "golang.org/x/exp/constraints" +) // AppendByteSlices concatenates byte slices into a single slice. func AppendByteSlices(byteSlices ...[]byte) []byte { @@ -37,3 +41,19 @@ func Uint32ToByteSlice(input uint32) []byte { binary.BigEndian.PutUint32(uint32Bytes, input) return uint32Bytes } + +// Bitwise is a type constraint for unsigned integer types (uint8, uint16, uint32). +type Bitwise interface { + constraints.Unsigned + ~uint8 | ~uint16 | ~uint32 +} + +// IsBitSet checks if a bit is set, with bit 0 as the least significant bit (LSB). +func IsBitSet[T Bitwise](value, mask T) bool { + return value&mask != 0 +} + +// SetBit sets a specific bit in a value of any unsigned integer type. +func SetBit[T Bitwise](value, bit T) T { + return value | bit +} diff --git a/pkg/packet/pcep/pcep_util_test.go b/pkg/packet/pcep/pcep_util_test.go new file mode 100644 index 00000000..2c7f51a1 --- /dev/null +++ b/pkg/packet/pcep/pcep_util_test.go @@ -0,0 +1,198 @@ +// Copyright (c) 2022 NTT Communications Corporation +// +// This software is released under the MIT License. +// see https://github.com/nttcom/pola/blob/main/LICENSE + +package pcep + +import ( + "bytes" + "testing" +) + +func TestAppendByteSlices(t *testing.T) { + tests := []struct { + name string + input [][]byte + expected []byte + }{ + { + name: "Concatenate non-empty slices", + input: [][]byte{{0x01, 0x02}, {0x03, 0x04, 0x05}}, + expected: []byte{0x01, 0x02, 0x03, 0x04, 0x05}, + }, + { + name: "Concatenate empty slices", + input: [][]byte{{}, {}}, + expected: []byte{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := AppendByteSlices(tt.input...) + if !bytes.Equal(result, tt.expected) { + t.Errorf("expected %v, got %v", tt.expected, result) + } + }) + } +} + +func TestUint16ToByteSlice(t *testing.T) { + tests := []struct { + name string + input uint16 + expected []byte + }{ + { + name: "Convert 0x0102 to bytes", + input: 0x0102, + expected: []byte{0x01, 0x02}, + }, + { + name: "Convert 0xFFFF to bytes", + input: 0xFFFF, + expected: []byte{0xFF, 0xFF}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := Uint16ToByteSlice(tt.input) + if !bytes.Equal(result, tt.expected) { + t.Errorf("expected %v, got %v", tt.expected, result) + } + }) + } +} + +func TestUint32ToByteSlice(t *testing.T) { + tests := []struct { + name string + input uint32 + expected []byte + }{ + { + name: "Convert 0x01020304 to bytes", + input: 0x01020304, + expected: []byte{0x01, 0x02, 0x03, 0x04}, + }, + { + name: "Convert 0xFFFFFFFF to bytes", + input: 0xFFFFFFFF, + expected: []byte{0xFF, 0xFF, 0xFF, 0xFF}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := Uint32ToByteSlice(tt.input) + if !bytes.Equal(result, tt.expected) { + t.Errorf("expected %v, got %v", tt.expected, result) + } + }) + } +} + +func TestIsBitSet(t *testing.T) { + type testCase[T Bitwise] struct { + name string + value T + mask T + expected bool + } + + t.Run("uint8", func(t *testing.T) { + tests := []testCase[uint8]{ + {"bit 0 set", 0x01, 0x01, true}, + {"bit 1 set", 0x03, 0x02, true}, + {"bit 2 not set", 0x03, 0x04, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := IsBitSet(tt.value, tt.mask); got != tt.expected { + t.Errorf("expected %v, got %v", tt.expected, got) + } + }) + } + }) + + t.Run("uint16", func(t *testing.T) { + tests := []testCase[uint16]{ + {"bit 8 set", 0x0100, 0x0100, true}, + {"bit 9 set", 0x0201, 0x0200, true}, + {"bit 10 not set", 0x0201, 0x0400, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := IsBitSet(tt.value, tt.mask); got != tt.expected { + t.Errorf("expected %v, got %v", tt.expected, got) + } + }) + } + }) + + t.Run("uint32", func(t *testing.T) { + tests := []testCase[uint32]{ + {"bit 16 set", 0x00010000, 0x00010000, true}, + {"bit 17 set", 0x00020001, 0x00020000, true}, + {"bit 18 not set", 0x00020001, 0x00040000, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := IsBitSet(tt.value, tt.mask); got != tt.expected { + t.Errorf("expected %v, got %v", tt.expected, got) + } + }) + } + }) +} + +func TestSetBit(t *testing.T) { + tests := []struct { + name string + value uint8 + bit uint8 + expected uint8 + }{ + { + name: "Set bit 0", + value: 0x00, + bit: 0x01, + expected: 0x01, + }, + { + name: "Set bit 1", + value: 0x00, + bit: 0x02, + expected: 0x02, + }, + { + name: "Set bit 0 when bit 0 is already set", + value: 0x01, + bit: 0x01, + expected: 0x01, + }, + { + name: "Set bit 1 when bit 0 is already set", + value: 0x01, + bit: 0x02, + expected: 0x03, + }, + { + name: "Set bit 0 when bit 1 is already set", + value: 0x02, + bit: 0x01, + expected: 0x03, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := SetBit(tt.value, tt.bit) + if result != tt.expected { + t.Errorf("Test %s failed: expected %v, got %v", tt.name, tt.expected, result) + } + }) + } +} diff --git a/pkg/packet/pcep/tlv.go b/pkg/packet/pcep/tlv.go index 8016d417..faf3179e 100644 --- a/pkg/packet/pcep/tlv.go +++ b/pkg/packet/pcep/tlv.go @@ -191,6 +191,9 @@ var tlvMap = map[TLVType]func() TLVInterface{ TLVColor: func() TLVInterface { return &Color{} }, } +const TLVHeaderLength = 4 + +// TLV value lengths (in bytes), excluding the 4-byte TLV header (type + length) const ( TLVStatefulPCECapabilityValueLength uint16 = 4 TLVLSPDBVersionValueLength uint16 = 8 @@ -223,8 +226,6 @@ const ( SubTLVPreferenceCiscoValueLength uint16 = 4 ) -const TLVHeaderLength = 4 - type TLVInterface interface { DecodeFromBytes(data []uint8) error Serialize() []uint8 @@ -251,81 +252,54 @@ type StatefulPCECapability struct { Relax bool // 17 } -func (tlv *StatefulPCECapability) DecodeFromBytes(flags []uint8) error { - if len(flags) < 4 { - return fmt.Errorf("flags array is too short, expected at least 4 bytes but got %d", len(flags)) - } +const ( + LSPUpdateCapabilityBit uint32 = 0x01 + IncludeDBVersionCapabilityBit uint32 = 0x02 + LSPInstantiationCapabilityBit uint32 = 0x04 + TriggeredResyncCapabilityBit uint32 = 0x08 + DeltaLSPSyncCapabilityBit uint32 = 0x10 + TriggeredInitialSyncBit uint32 = 0x20 +) - flagMap := []struct { - field *bool - mask uint8 - index int - }{ - {&tlv.LSPUpdateCapability, 0x01, 3}, - {&tlv.IncludeDBVersion, 0x02, 3}, - {&tlv.LSPInstantiationCapability, 0x04, 3}, - {&tlv.TriggeredResync, 0x08, 3}, - {&tlv.DeltaLSPSyncCapability, 0x10, 3}, - {&tlv.TriggeredInitialSync, 0x20, 3}, - {&tlv.P2mpCapability, 0x40, 3}, - {&tlv.P2mpLSPUpdateCapability, 0x80, 3}, - {&tlv.P2mpLSPInstantiationCapability, 0x01, 2}, - {&tlv.LSPSchedulingCapability, 0x02, 2}, - {&tlv.PdLSPCapability, 0x04, 2}, - {&tlv.ColorCapability, 0x08, 2}, - {&tlv.PathRecomputationCapability, 0x10, 2}, - {&tlv.StrictPathCapability, 0x20, 2}, - {&tlv.Relax, 0x40, 2}, - } +const ( + StatefulPCECapabilityFlagsIndex = 3 +) - for _, f := range flagMap { - *f.field = (flags[f.index] & f.mask) != 0 +func (tlv *StatefulPCECapability) DecodeFromBytes(data []uint8) error { + if len(data) < int(tlv.Len()) { + return fmt.Errorf("data is too short: expected at least %d bytes, but got %d bytes for StatefulPCECapability", tlv.Len(), len(data)) } + flagByte := uint32(data[TLVHeaderLength+StatefulPCECapabilityFlagsIndex]) + tlv.LSPUpdateCapability = IsBitSet(flagByte, LSPUpdateCapabilityBit) + tlv.IncludeDBVersion = IsBitSet(flagByte, IncludeDBVersionCapabilityBit) + tlv.LSPInstantiationCapability = IsBitSet(flagByte, LSPInstantiationCapabilityBit) + tlv.TriggeredResync = IsBitSet(flagByte, TriggeredResyncCapabilityBit) + tlv.DeltaLSPSyncCapability = IsBitSet(flagByte, DeltaLSPSyncCapabilityBit) + tlv.TriggeredInitialSync = IsBitSet(flagByte, TriggeredInitialSyncBit) + return nil } -func setFlag(flags []uint8, index int, mask uint8, condition bool) { - if condition { - flags[index] = flags[index] | mask - } -} +func (tlv *StatefulPCECapability) Serialize() []byte { + buf := make([]byte, 0, TLVHeaderLength+TLVStatefulPCECapabilityValueLength) + buf = append(buf, byte(tlv.Type()>>8), byte(tlv.Type())) + buf = append(buf, byte(TLVStatefulPCECapabilityValueLength>>8), byte(TLVStatefulPCECapabilityValueLength)) -func (tlv *StatefulPCECapability) Serialize() []uint8 { - buf := []uint8{} - - typ := make([]uint8, 2) - binary.BigEndian.PutUint16(typ, uint16(tlv.Type())) - buf = append(buf, typ...) - - length := make([]uint8, 2) - binary.BigEndian.PutUint16(length, TLVStatefulPCECapabilityValueLength) - buf = append(buf, length...) - - flags := make([]uint8, TLVStatefulPCECapabilityValueLength) - - setFlag(flags, 3, 0x01, tlv.LSPUpdateCapability) - setFlag(flags, 3, 0x02, tlv.IncludeDBVersion) - setFlag(flags, 3, 0x04, tlv.LSPInstantiationCapability) - setFlag(flags, 3, 0x08, tlv.TriggeredResync) - setFlag(flags, 3, 0x10, tlv.DeltaLSPSyncCapability) - setFlag(flags, 3, 0x20, tlv.TriggeredInitialSync) - setFlag(flags, 3, 0x40, tlv.P2mpCapability) - setFlag(flags, 3, 0x80, tlv.P2mpLSPUpdateCapability) - setFlag(flags, 2, 0x01, tlv.P2mpLSPInstantiationCapability) - setFlag(flags, 2, 0x02, tlv.LSPSchedulingCapability) - setFlag(flags, 2, 0x04, tlv.PdLSPCapability) - setFlag(flags, 2, 0x08, tlv.ColorCapability) - setFlag(flags, 2, 0x10, tlv.PathRecomputationCapability) - setFlag(flags, 2, 0x20, tlv.StrictPathCapability) - setFlag(flags, 2, 0x40, tlv.Relax) - - buf = append(buf, flags...) + val := make([]byte, TLVStatefulPCECapabilityValueLength) + binary.BigEndian.PutUint32(val, tlv.CapabilityBits()) + buf = append(buf, val...) return buf } func (tlv *StatefulPCECapability) MarshalLogObject(enc zapcore.ObjectEncoder) error { + enc.AddBool("lspUpdateCapability", tlv.LSPUpdateCapability) + enc.AddBool("includeDBVersion", tlv.IncludeDBVersion) + enc.AddBool("lspInstantiationCapability", tlv.LSPInstantiationCapability) + enc.AddBool("triggeredResync", tlv.TriggeredResync) + enc.AddBool("deltaLSPSyncCapability", tlv.DeltaLSPSyncCapability) + enc.AddBool("triggeredInitialSync", tlv.TriggeredInitialSync) return nil } @@ -338,8 +312,7 @@ func (tlv *StatefulPCECapability) Len() uint16 { } func (tlv *StatefulPCECapability) CapStrings() []string { - ret := []string{} - ret = append(ret, "Stateful") + ret := []string{"Stateful"} if tlv.LSPUpdateCapability { ret = append(ret, "Update") } @@ -347,16 +320,16 @@ func (tlv *StatefulPCECapability) CapStrings() []string { ret = append(ret, "Include-DB-Ver") } if tlv.LSPInstantiationCapability { - ret = append(ret, "Initiate") + ret = append(ret, "Instantiation") } if tlv.TriggeredResync { - ret = append(ret, "Triggerd-Resync") + ret = append(ret, "Triggered-Resync") } if tlv.DeltaLSPSyncCapability { ret = append(ret, "Delta-LSP-Sync") } if tlv.TriggeredInitialSync { - ret = append(ret, "Triggerd-init-sync") + ret = append(ret, "Triggered-Initial-Sync") } if tlv.ColorCapability { ret = append(ret, "Color") @@ -364,39 +337,77 @@ func (tlv *StatefulPCECapability) CapStrings() []string { return ret } +func (tlv *StatefulPCECapability) FromBits(bits uint32) { + tlv.LSPUpdateCapability = bits&LSPUpdateCapabilityBit != 0 + tlv.IncludeDBVersion = bits&IncludeDBVersionCapabilityBit != 0 + tlv.LSPInstantiationCapability = bits&LSPInstantiationCapabilityBit != 0 + tlv.TriggeredResync = bits&TriggeredResyncCapabilityBit != 0 + tlv.DeltaLSPSyncCapability = bits&DeltaLSPSyncCapabilityBit != 0 + tlv.TriggeredInitialSync = bits&TriggeredInitialSyncBit != 0 +} + +func NewStatefulPCECapability(bits uint32) *StatefulPCECapability { + tlv := &StatefulPCECapability{} + tlv.FromBits(bits) + return tlv +} + +func (tlv *StatefulPCECapability) CapabilityBits() uint32 { + var flags uint32 + if tlv.LSPUpdateCapability { + flags = SetBit(flags, LSPUpdateCapabilityBit) + } + if tlv.IncludeDBVersion { + flags = SetBit(flags, IncludeDBVersionCapabilityBit) + } + if tlv.LSPInstantiationCapability { + flags = SetBit(flags, LSPInstantiationCapabilityBit) + } + if tlv.TriggeredResync { + flags = SetBit(flags, TriggeredResyncCapabilityBit) + } + if tlv.DeltaLSPSyncCapability { + flags = SetBit(flags, DeltaLSPSyncCapabilityBit) + } + if tlv.TriggeredInitialSync { + flags = SetBit(flags, TriggeredInitialSyncBit) + } + return flags +} + type SymbolicPathName struct { Name string } -func (tlv *SymbolicPathName) DecodeFromBytes(data []uint8) error { +func (tlv *SymbolicPathName) DecodeFromBytes(data []byte) error { + if len(data) < TLVHeaderLength { + return fmt.Errorf("data is too short: expected at least %d bytes, but got %d bytes for SymbolicPathName", TLVHeaderLength, len(data)) + } + length := binary.BigEndian.Uint16(data[2:4]) - tlv.Name = string(data[4 : 4+length]) + totalLength := int(TLVHeaderLength + length) + if len(data) != totalLength { + return fmt.Errorf("data length mismatch: expected %d bytes, but got %d bytes for SymbolicPathName", totalLength, len(data)) + } + + tlv.Name = string(data[TLVHeaderLength:totalLength]) return nil } -func (tlv *SymbolicPathName) Serialize() []uint8 { - buf := []uint8{} - - typ := make([]uint8, 2) - binary.BigEndian.PutUint16(typ, uint16(tlv.Type())) - buf = append(buf, typ...) - - l := uint16(len(tlv.Name)) - length := make([]uint8, 2) - binary.BigEndian.PutUint16(length, l) - buf = append(buf, length...) +func (tlv *SymbolicPathName) Serialize() []byte { + nameLen := uint16(len(tlv.Name)) + padding := (4 - (nameLen % 4)) % 4 // Padding for 4-byte alignment - buf = append(buf, []uint8(tlv.Name)...) + buf := make([]byte, 0, TLVHeaderLength+int(nameLen)+int(padding)) + buf = append(buf, Uint16ToByteSlice(uint16(tlv.Type()))...) + buf = append(buf, Uint16ToByteSlice(nameLen)...) + buf = append(buf, []byte(tlv.Name)...) - if l%4 != 0 { - pad := make([]uint8, 4-l%4) - buf = append(buf, pad...) + if padding > 0 { + buf = append(buf, make([]byte, padding)...) } - return buf -} -func (tlv *SymbolicPathName) MarshalLogObject(enc zapcore.ObjectEncoder) error { - return nil + return buf } func (tlv *SymbolicPathName) Type() TLVType { @@ -404,12 +415,22 @@ func (tlv *SymbolicPathName) Type() TLVType { } func (tlv *SymbolicPathName) Len() uint16 { - l := uint16(len(tlv.Name)) + length := uint16(len(tlv.Name)) padding := uint16(0) - if l%4 != 0 { - padding = (4 - l%4) + if mod := length % 4; mod != 0 { + padding = 4 - mod } - return TLVHeaderLength + l + padding + + return TLVHeaderLength + length + padding +} + +func (tlv *SymbolicPathName) MarshalLogObject(enc zapcore.ObjectEncoder) error { + enc.AddString("symbolicPathName", tlv.Name) + return nil +} + +func NewSymbolicPathName(name string) *SymbolicPathName { + return &SymbolicPathName{Name: name} } type IPv4LSPIdentifiers struct { @@ -417,21 +438,58 @@ type IPv4LSPIdentifiers struct { IPv4TunnelEndpointAddress netip.Addr LSPID uint16 TunnelID uint16 + ExtendedTunnelID uint32 } +const ( + IPv4LSPIdentifiersTunnelSenderAddressIndex = 4 + IPv4LSPIdentifiersLSPIDIndex = 6 + IPv4LSPIdentifiersTunnelIDIndex = 8 + IPv4LSPIdentifiersExtendedTunnelIDIndex = 12 +) + func (tlv *IPv4LSPIdentifiers) DecodeFromBytes(data []uint8) error { + expectedLength := TLVHeaderLength + int(TLVIPv4LSPIdentifiersValueLength) + if len(data) != expectedLength { + return fmt.Errorf("data length mismatch: expected %d bytes, but got %d bytes for IPv4LSPIdentifiers", expectedLength, len(data)) + } + var ok bool - if tlv.IPv4TunnelSenderAddress, ok = netip.AddrFromSlice(data[12:16]); !ok { - tlv.IPv4TunnelSenderAddress, _ = netip.AddrFromSlice(data[4:8]) + if tlv.IPv4TunnelSenderAddress, ok = netip.AddrFromSlice(data[TLVHeaderLength : TLVHeaderLength+IPv4LSPIdentifiersTunnelSenderAddressIndex]); !ok { + return fmt.Errorf("failed to parse IPv4TunnelSenderAddress") } - tlv.LSPID = binary.BigEndian.Uint16(data[8:10]) - tlv.TunnelID = binary.BigEndian.Uint16(data[10:12]) - tlv.IPv4TunnelEndpointAddress, _ = netip.AddrFromSlice(data[16:20]) + + tlv.LSPID = binary.BigEndian.Uint16(data[TLVHeaderLength+IPv4LSPIdentifiersTunnelSenderAddressIndex : TLVHeaderLength+IPv4LSPIdentifiersLSPIDIndex]) + tlv.TunnelID = binary.BigEndian.Uint16(data[TLVHeaderLength+IPv4LSPIdentifiersLSPIDIndex : TLVHeaderLength+IPv4LSPIdentifiersTunnelIDIndex]) + tlv.ExtendedTunnelID = binary.BigEndian.Uint32(data[TLVHeaderLength+IPv4LSPIdentifiersTunnelIDIndex : TLVHeaderLength+IPv4LSPIdentifiersExtendedTunnelIDIndex]) + + if tlv.IPv4TunnelEndpointAddress, ok = netip.AddrFromSlice(data[TLVHeaderLength+IPv4LSPIdentifiersExtendedTunnelIDIndex : TLVHeaderLength+TLVIPv4LSPIdentifiersValueLength]); !ok { + return fmt.Errorf("failed to parse IPv4TunnelEndpointAddress") + } + return nil } func (tlv *IPv4LSPIdentifiers) Serialize() []uint8 { - return nil + buf := make([]uint8, 0, TLVHeaderLength+TLVIPv4LSPIdentifiersValueLength) + + typ := make([]uint8, 2) + binary.BigEndian.PutUint16(typ, uint16(tlv.Type())) + buf = append(buf, typ...) + + length := make([]uint8, 2) + binary.BigEndian.PutUint16(length, TLVIPv4LSPIdentifiersValueLength) + buf = append(buf, length...) + + val := make([]byte, TLVIPv4LSPIdentifiersValueLength) + copy(val[0:4], tlv.IPv4TunnelSenderAddress.AsSlice()) + binary.BigEndian.PutUint16(val[4:6], tlv.LSPID) + binary.BigEndian.PutUint16(val[6:8], tlv.TunnelID) + binary.BigEndian.PutUint32(val[8:12], tlv.ExtendedTunnelID) + copy(val[12:16], tlv.IPv4TunnelEndpointAddress.AsSlice()) + buf = append(buf, val...) + + return buf } func (tlv *IPv4LSPIdentifiers) MarshalLogObject(enc zapcore.ObjectEncoder) error { @@ -446,23 +504,58 @@ func (tlv *IPv4LSPIdentifiers) Len() uint16 { return TLVHeaderLength + TLVIPv4LSPIdentifiersValueLength } +func NewIPv4LSPIdentifiers(senderAddr, endpointAddr netip.Addr, lspID, tunnelID uint16, extendedTunnelID uint32) *IPv4LSPIdentifiers { + return &IPv4LSPIdentifiers{ + IPv4TunnelSenderAddress: senderAddr, + IPv4TunnelEndpointAddress: endpointAddr, + LSPID: lspID, + TunnelID: tunnelID, + ExtendedTunnelID: extendedTunnelID, + } +} + type IPv6LSPIdentifiers struct { IPv6TunnelSenderAddress netip.Addr IPv6TunnelEndpointAddress netip.Addr LSPID uint16 TunnelID uint16 + ExtendedTunnelID [16]byte } func (tlv *IPv6LSPIdentifiers) DecodeFromBytes(data []uint8) error { - tlv.IPv6TunnelSenderAddress, _ = netip.AddrFromSlice(data[4:20]) + expectedLength := TLVHeaderLength + int(TLVIPv6LSPIdentifiersValueLength) + if len(data) != expectedLength { + return fmt.Errorf("data length mismatch: expected %d bytes, but got %d bytes for IPv6LSPIdentifiers", expectedLength, len(data)) + } + + var ok bool + if tlv.IPv6TunnelSenderAddress, ok = netip.AddrFromSlice(data[4:20]); !ok { + return fmt.Errorf("failed to parse IPv6TunnelSenderAddress") + } + tlv.LSPID = binary.BigEndian.Uint16(data[20:22]) tlv.TunnelID = binary.BigEndian.Uint16(data[22:24]) - tlv.IPv6TunnelEndpointAddress, _ = netip.AddrFromSlice(data[40:56]) + copy(tlv.ExtendedTunnelID[:], data[24:40]) + + if tlv.IPv6TunnelEndpointAddress, ok = netip.AddrFromSlice(data[40:56]); !ok { + return fmt.Errorf("failed to parse IPv6TunnelEndpointAddress") + } + return nil } func (tlv *IPv6LSPIdentifiers) Serialize() []uint8 { - return nil + buf := make([]uint8, tlv.Len()) + + binary.BigEndian.PutUint16(buf[0:2], uint16(tlv.Type())) + binary.BigEndian.PutUint16(buf[2:4], TLVIPv6LSPIdentifiersValueLength) + copy(buf[4:20], tlv.IPv6TunnelSenderAddress.AsSlice()) + binary.BigEndian.PutUint16(buf[20:22], tlv.LSPID) + binary.BigEndian.PutUint16(buf[22:24], tlv.TunnelID) + copy(buf[24:40], tlv.ExtendedTunnelID[:]) + copy(buf[40:56], tlv.IPv6TunnelEndpointAddress.AsSlice()) + + return buf } func (tlv *IPv6LSPIdentifiers) MarshalLogObject(enc zapcore.ObjectEncoder) error { @@ -477,11 +570,26 @@ func (tlv *IPv6LSPIdentifiers) Len() uint16 { return TLVHeaderLength + TLVIPv6LSPIdentifiersValueLength } +func NewIPv6LSPIdentifiers(senderAddr, endpointAddr netip.Addr, lspID, tunnelID uint16, extendedTunnelID [16]byte) *IPv6LSPIdentifiers { + return &IPv6LSPIdentifiers{ + IPv6TunnelSenderAddress: senderAddr, + IPv6TunnelEndpointAddress: endpointAddr, + LSPID: lspID, + TunnelID: tunnelID, + ExtendedTunnelID: extendedTunnelID, + } +} + type LSPDBVersion struct { VersionNumber uint64 } func (tlv *LSPDBVersion) DecodeFromBytes(data []uint8) error { + expectedLength := TLVHeaderLength + int(TLVLSPDBVersionValueLength) + if len(data) != expectedLength { + return fmt.Errorf("data length mismatch: expected %d bytes, but got %d bytes for LSPDBVersion", expectedLength, len(data)) + } + tlv.VersionNumber = binary.BigEndian.Uint64(data[4:12]) return nil } @@ -520,44 +628,77 @@ func (tlv *LSPDBVersion) CapStrings() []string { return []string{"LSP-DB-VERSION"} } +func NewLSPDBVersion(version uint64) *LSPDBVersion { + return &LSPDBVersion{ + VersionNumber: version, + } +} + type SRPCECapability struct { - UnlimitedMSD bool - SupportNAI bool - MaximumSidDepth uint8 + HasUnlimitedMaxSIDDepth bool + IsNAISupported bool + MaximumSidDepth uint8 } -func (tlv *SRPCECapability) DecodeFromBytes(data []uint8) error { - tlv.UnlimitedMSD = (data[6] & 0x01) != 0 - tlv.SupportNAI = (data[6] & 0x02) != 0 - tlv.MaximumSidDepth = data[7] +const ( + UnlimitedMaximumSIDDepthFlag uint8 = 0x01 + NAISupportedFlag uint8 = 0x02 +) + +const ( + SRPCECapabilityFlagsIndex = 2 + SRPCECapabilityMSDIndex = 3 +) + +func (tlv *SRPCECapability) DecodeFromBytes(data []byte) error { + expectedLength := TLVHeaderLength + int(TLVSRPCECapabilityValueLength) + if len(data) != expectedLength { + return fmt.Errorf("data length mismatch: expected %d bytes, but got %d bytes for SRPCECapability", expectedLength, len(data)) + } + + // Extract TLV value field (after 4-byte TLV header) + val := data[TLVHeaderLength:] + + if len(val) != int(TLVSRPCECapabilityValueLength) { + return fmt.Errorf("invalid value length for SRPCECapability: expected %d bytes, but got %d bytes", TLVSRPCECapabilityValueLength, len(val)) + } + + flags := val[SRPCECapabilityFlagsIndex] + tlv.HasUnlimitedMaxSIDDepth = IsBitSet(flags, UnlimitedMaximumSIDDepthFlag) + tlv.IsNAISupported = IsBitSet(flags, NAISupportedFlag) + tlv.MaximumSidDepth = val[SRPCECapabilityMSDIndex] + return nil } -func (tlv *SRPCECapability) Serialize() []uint8 { - buf := []uint8{} +func (tlv *SRPCECapability) Serialize() []byte { + buf := make([]byte, 0, TLVHeaderLength+TLVSRPCECapabilityValueLength) - typ := make([]uint8, 2) + typ := make([]byte, 2) binary.BigEndian.PutUint16(typ, uint16(tlv.Type())) buf = append(buf, typ...) - length := make([]uint8, 2) + length := make([]byte, 2) binary.BigEndian.PutUint16(length, TLVSRPCECapabilityValueLength) buf = append(buf, length...) - val := make([]uint8, TLVSRPCECapabilityValueLength) - if tlv.UnlimitedMSD { - val[2] = val[2] | 0x01 + val := make([]byte, TLVSRPCECapabilityValueLength) + if tlv.HasUnlimitedMaxSIDDepth { + val[SRPCECapabilityFlagsIndex] = SetBit(val[SRPCECapabilityFlagsIndex], UnlimitedMaximumSIDDepthFlag) } - if tlv.SupportNAI { - val[2] = val[2] | 0x02 + if tlv.IsNAISupported { + val[SRPCECapabilityFlagsIndex] = SetBit(val[SRPCECapabilityFlagsIndex], NAISupportedFlag) } - val[3] = tlv.MaximumSidDepth + val[SRPCECapabilityMSDIndex] = tlv.MaximumSidDepth buf = append(buf, val...) return buf } func (tlv *SRPCECapability) MarshalLogObject(enc zapcore.ObjectEncoder) error { + enc.AddBool("unlimited_max_sid_depth", tlv.HasUnlimitedMaxSIDDepth) + enc.AddBool("nai_is_supported", tlv.IsNAISupported) + enc.AddUint8("maximum_sid_depth", tlv.MaximumSidDepth) return nil } @@ -570,7 +711,22 @@ func (tlv *SRPCECapability) Len() uint16 { } func (tlv *SRPCECapability) CapStrings() []string { - return []string{"SR-TE"} + var ret []string + if tlv.HasUnlimitedMaxSIDDepth { + ret = append(ret, "Unlimited-SID-Depth") + } + if tlv.IsNAISupported { + ret = append(ret, "NAI-Supported") + } + return ret +} + +func NewSRPCECapability(hasUnlimitedMaxSIDDepth bool, isNAISupported bool, maximumSidDepth uint8) *SRPCECapability { + return &SRPCECapability{ + HasUnlimitedMaxSIDDepth: hasUnlimitedMaxSIDDepth, + IsNAISupported: isNAISupported, + MaximumSidDepth: maximumSidDepth, + } } type Pst uint8 @@ -658,11 +814,11 @@ type ExtendedAssociationID struct { } func (tlv *ExtendedAssociationID) DecodeFromBytes(data []uint8) error { - l := binary.BigEndian.Uint16(data[2:4]) + length := binary.BigEndian.Uint16(data[2:4]) tlv.Color = binary.BigEndian.Uint32(data[4:8]) - switch l { + switch length { case TLVExtendedAssociationIDIPv4ValueLength: tlv.Endpoint, _ = netip.AddrFromSlice(data[8:12]) case TLVExtendedAssociationIDIPv6ValueLength: @@ -719,7 +875,7 @@ type PathSetupTypeCapability struct { } func (tlv *PathSetupTypeCapability) DecodeFromBytes(data []uint8) error { - l := binary.BigEndian.Uint16(data[2:4]) + length := binary.BigEndian.Uint16(data[2:4]) pstNum := int(data[7]) for i := 0; i < pstNum; i++ { @@ -730,7 +886,7 @@ func (tlv *PathSetupTypeCapability) DecodeFromBytes(data []uint8) error { pstNum += 4 - (pstNum % 4) // padding byte } var err error - tlv.SubTLVs, err = DecodeTLVs(data[8+pstNum : TLVHeaderLength+l]) // 8 byte: Type&Length (4 byte) + Reserve&pstNum (4 byte) + tlv.SubTLVs, err = DecodeTLVs(data[8+pstNum : TLVHeaderLength+length]) // 8 byte: Type&Length (4 byte) + Reserve&pstNum (4 byte) if err != nil { return err } @@ -746,17 +902,17 @@ func (tlv *PathSetupTypeCapability) Serialize() []uint8 { numOfPst := uint16(len(tlv.PathSetupTypes)) - l := uint16(4) // 4 byte: reserve & num of PSTs field - l += numOfPst + length := uint16(4) // 4 byte: reserve & num of PSTs field + length += numOfPst if numOfPst%4 != 0 { - l += 4 - (numOfPst % 4) + length += 4 - (numOfPst % 4) } for _, subTLV := range tlv.SubTLVs { - l += subTLV.Len() + length += subTLV.Len() } - length := make([]uint8, 2) - binary.BigEndian.PutUint16(length, l) - buf = append(buf, length...) + lengthBytes := make([]uint8, 2) + binary.BigEndian.PutUint16(lengthBytes, length) + buf = append(buf, lengthBytes...) var val []uint8 if numOfPst%4 == 0 { @@ -787,16 +943,16 @@ func (tlv *PathSetupTypeCapability) Type() TLVType { } func (tlv *PathSetupTypeCapability) Len() uint16 { - l := uint16(4) // 4 byte: reserve & num of PSTs field + length := uint16(4) // 4 byte: reserve & num of PSTs field numOfPst := uint16(len(tlv.PathSetupTypes)) - l += numOfPst + length += numOfPst if numOfPst%4 != 0 { - l += 4 - (numOfPst % 4) + length += 4 - (numOfPst % 4) } for _, subTLV := range tlv.SubTLVs { - l += subTLV.Len() + length += subTLV.Len() } - return TLVHeaderLength + l + return TLVHeaderLength + length } func (tlv *PathSetupTypeCapability) CapStrings() []string { @@ -818,7 +974,7 @@ const ( AssocTypePolicyAssociation AssocType = 0x03 AssocTypeSingleSidedBidirectionalLSPAssociation AssocType = 0x04 AssocTypeDoubleSidedBidirectionalLSPAssociation AssocType = 0x05 - AssocTypeSrPolicyAssociation AssocType = 0x06 + AssocTypeSRPolicyAssociation AssocType = 0x06 AssocTypeVnAssociationType AssocType = 0x07 ) @@ -828,7 +984,7 @@ var assocTypeNames = map[AssocType]string{ AssocTypePolicyAssociation: "Policy Association", AssocTypeSingleSidedBidirectionalLSPAssociation: "Single Sided Bidirectional LSP Association", AssocTypeDoubleSidedBidirectionalLSPAssociation: "Double Sided Bidirectional LSP Association", - AssocTypeSrPolicyAssociation: "SR Policy Association", + AssocTypeSRPolicyAssociation: "SR Policy Association", AssocTypeVnAssociationType: "VN Association Type", } @@ -859,18 +1015,18 @@ func (tlv *AssocTypeList) Serialize() []uint8 { binary.BigEndian.PutUint16(typ, uint16(tlv.Type())) buf = append(buf, typ...) - l := uint16(len(tlv.AssocTypes)) * 2 - length := make([]uint8, 2) - binary.BigEndian.PutUint16(length, l) - buf = append(buf, length...) + length := uint16(len(tlv.AssocTypes)) * 2 + lengthBytes := make([]uint8, 2) + binary.BigEndian.PutUint16(lengthBytes, length) + buf = append(buf, lengthBytes...) for _, at := range tlv.AssocTypes { binAt := make([]uint8, 2) binary.BigEndian.PutUint16(binAt, uint16(at)) buf = append(buf, binAt...) } - if l%4 != 0 { - pad := make([]uint8, 4-(l%4)) + if length%4 != 0 { + pad := make([]uint8, 4-(length%4)) buf = append(buf, pad...) } return buf @@ -885,12 +1041,12 @@ func (tlv *AssocTypeList) Type() TLVType { } func (tlv *AssocTypeList) Len() uint16 { - l := uint16(len(tlv.AssocTypes)) * 2 + length := uint16(len(tlv.AssocTypes)) * 2 padding := uint16(0) - if l%4 != 0 { + if length%4 != 0 { padding = 2 } - return TLVHeaderLength + l + padding + return TLVHeaderLength + length + padding } func (tlv *AssocTypeList) CapStrings() []string { diff --git a/pkg/packet/pcep/tlv_test.go b/pkg/packet/pcep/tlv_test.go new file mode 100644 index 00000000..eb4a64cd --- /dev/null +++ b/pkg/packet/pcep/tlv_test.go @@ -0,0 +1,535 @@ +// Copyright (c) 2022 NTT Communications Corporation +// +// This software is released under the MIT License. +// see https://github.com/nttcom/pola/blob/main/LICENSE + +package pcep + +import ( + "encoding/binary" + "net/netip" + "testing" + + "github.com/stretchr/testify/assert" + "go.uber.org/zap/zapcore" +) + +func TestStatefulPCECapability_DecodeFromBytes(t *testing.T) { + tests := []struct { + name string + input []uint8 + expected *StatefulPCECapability + err bool + }{ + { + name: "Single capability: LSP Update enabled", + input: NewStatefulPCECapability(0x01).Serialize(), + expected: NewStatefulPCECapability(0x01), + err: false, + }, + { + name: "All capabilities enabled", + input: NewStatefulPCECapability(0x3F).Serialize(), + expected: NewStatefulPCECapability(0x3F), + err: false, + }, + { + name: "Input too short (missing TLV body)", + input: []uint8{uint8(TLVStatefulPCECapability >> 8), uint8(TLVStatefulPCECapability & 0xFF), 0x00, 0x04}, // type=0x0010, length=4, but body missing + expected: nil, + err: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var actual StatefulPCECapability + err := actual.DecodeFromBytes(tt.input) + if tt.err { + assert.Error(t, err, "expected error for input: %v", tt.input) + } else { + assert.NoError(t, err, "unexpected error for input: %v", tt.input) + assert.Equal(t, *tt.expected, actual, "decoded capability mismatch") + } + }) + } +} + +func TestStatefulPCECapability_Serialize(t *testing.T) { + tests := []struct { + name string + bits uint32 + }{ + { + name: "LSP Update Capability enabled", + bits: 0x01, + }, + { + name: "All capabilities enabled", + bits: 0x3F, + }, + { + name: "No capabilities enabled", + bits: 0x00, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tlv := NewStatefulPCECapability(tt.bits) + expected := make([]uint8, TLVHeaderLength+TLVStatefulPCECapabilityValueLength) + binary.BigEndian.PutUint16(expected[0:2], uint16(TLVStatefulPCECapability)) + binary.BigEndian.PutUint16(expected[2:4], TLVStatefulPCECapabilityValueLength) + binary.BigEndian.PutUint32(expected[4:8], tlv.CapabilityBits()) + + assert.Equal(t, expected, tlv.Serialize(), "serialized output mismatch") + }) + } +} + +func TestStatefulPCECapability_MarshalLogObject(t *testing.T) { + tests := []struct { + name string + tlv *StatefulPCECapability + expected bool + }{ + { + name: "LSP Update Capability enabled", + tlv: &StatefulPCECapability{ + LSPUpdateCapability: true, + }, + expected: true, + }, + { + name: "LSP Update Capability disabled", + tlv: &StatefulPCECapability{ + LSPUpdateCapability: false, + }, + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + enc := zapcore.NewMapObjectEncoder() + err := tt.tlv.MarshalLogObject(enc) + + assert.NoError(t, err, "expected no error while marshaling log object") + assert.Equal(t, tt.expected, enc.Fields["lspUpdateCapability"], "field 'lspUpdateCapability' mismatch") + }) + } +} + +func TestStatefulPCECapability_CapStrings(t *testing.T) { + tests := []struct { + name string + bits uint32 + expected []string + }{ + { + name: "All capabilities enabled", + bits: 0x3F, + expected: []string{"Stateful", "Update", "Include-DB-Ver", "Instantiation", "Triggered-Resync", "Delta-LSP-Sync", "Triggered-Initial-Sync"}, + }, + { + name: "No capabilities enabled", + bits: 0x00, + expected: []string{"Stateful"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + input := NewStatefulPCECapability(tt.bits) + assert.ElementsMatch(t, tt.expected, input.CapStrings(), "capabilities mismatch") + }) + } +} + +func TestSymbolicPathName_DecodeFromBytes(t *testing.T) { + tests := []struct { + name string + input []uint8 + expected *SymbolicPathName + err bool + }{ + { + name: "Valid Symbolic Path Name", + input: NewSymbolicPathName("Test").Serialize(), + expected: NewSymbolicPathName("Test"), + err: false, + }, + { + name: "Invalid input (too short data)", + input: []byte{0x00, 0x11, 0x00, 0x02, 'T'}, // Input too short for valid decoding + expected: NewSymbolicPathName(""), + err: true, + }, + { + name: "Invalid input (too long data)", + input: []byte{0x00, 0x11, 0x00, 0x01, 'T', 'e'}, // Input too long for valid decoding + expected: NewSymbolicPathName(""), + err: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var tlv SymbolicPathName + err := tlv.DecodeFromBytes(tt.input) + if tt.err { + assert.Error(t, err, "expected error for input: %v", tt.input) + } else { + assert.NoError(t, err, "unexpected error for input: %v", tt.input) + assert.Equal(t, tt.expected, &tlv) + } + }) + } +} + +func TestSymbolicPathName_Serialize(t *testing.T) { + tests := []struct { + name string + input *SymbolicPathName + expected []uint8 + }{ + { + name: "Valid Symbolic Path Name", + input: NewSymbolicPathName("Test"), + expected: NewSymbolicPathName("Test").Serialize(), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.expected, tt.input.Serialize()) + }) + } +} + +func TestSymbolicPathName_Len(t *testing.T) { + tests := []struct { + name string + input *SymbolicPathName + expected uint16 + }{ + { + name: "Symbolic Path Name length", + input: NewSymbolicPathName("Test"), + expected: TLVHeaderLength + 4, + }, + { + name: "Symbolic Path Name with padding", + input: NewSymbolicPathName("ABC"), // 3 bytes + 1 byte padding + expected: TLVHeaderLength + 3 + 1, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.expected, tt.input.Len()) + }) + } +} + +func TestIPv4LSPIdentifiers_DecodeFromBytes(t *testing.T) { + tests := []struct { + name string + input []uint8 + expected *IPv4LSPIdentifiers + err bool + }{ + { + name: "Valid IPv4 LSP Identifiers", + input: NewIPv4LSPIdentifiers(netip.MustParseAddr("192.0.2.1"), netip.MustParseAddr("192.0.2.2"), 1, 2, 1234).Serialize(), + expected: NewIPv4LSPIdentifiers(netip.MustParseAddr("192.0.2.1"), netip.MustParseAddr("192.0.2.2"), 1, 2, 1234), + err: false, + }, + { + name: "Invalid IPv4 LSP Identifiers (truncated '192.0.2.1')", + input: []uint8{ + 0x00, 0x12, 0x00, 0x14, // Type (0x12) and Length (0x10) + 0xC0, 0x00, 0x02, // Incomplete address: missing last byte (0x01) + }, + expected: NewIPv4LSPIdentifiers(netip.Addr{}, netip.Addr{}, 0, 0, 0), + err: true, + }, + { + name: "Invalid IPv4 LSP Identifiers (extra bytes after '192.0.2.1')", + input: []uint8{ + 0x00, 0x12, 0x00, 0x14, // Type (0x12) and Length (0x10) + 0xC0, 0x00, 0x02, 0x01, // Valid IPv4 address: 192.0.2.1 + 0xDE, 0xAD, 0xBE, 0xEF, // Extra unexpected bytes + }, + expected: NewIPv4LSPIdentifiers(netip.Addr{}, netip.Addr{}, 0, 0, 0), + err: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var tlv IPv4LSPIdentifiers + err := tlv.DecodeFromBytes(tt.input) + if tt.err { + assert.Error(t, err, "expected error for input: %v", tt.input) + } else { + assert.NoError(t, err, "unexpected error for input: %v", tt.input) + assert.Equal(t, tt.expected, &tlv) + } + }) + } +} + +func TestIPv4LSPIdentifiers_Serialize(t *testing.T) { + tests := []struct { + name string + input *IPv4LSPIdentifiers + expected []uint8 + }{ + { + name: "Valid IPv4 LSP Identifiers", + input: NewIPv4LSPIdentifiers(netip.MustParseAddr("192.0.2.1"), netip.MustParseAddr("192.0.2.2"), 1, 2, 1234), + expected: NewIPv4LSPIdentifiers(netip.MustParseAddr("192.0.2.1"), netip.MustParseAddr("192.0.2.2"), 1, 2, 1234).Serialize(), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.expected, tt.input.Serialize()) + }) + } +} + +func TestIPv6LSPIdentifiers_DecodeFromBytes(t *testing.T) { + tests := []struct { + name string + input []uint8 + expected *IPv6LSPIdentifiers + err bool + }{ + { + name: "Valid IPv6 LSP Identifiers", + input: NewIPv6LSPIdentifiers(netip.MustParseAddr("2001:db8::1"), netip.MustParseAddr("2001:db8::2"), 1, 2, [16]byte{}).Serialize(), + expected: NewIPv6LSPIdentifiers(netip.MustParseAddr("2001:db8::1"), netip.MustParseAddr("2001:db8::2"), 1, 2, [16]byte{}), + err: false, + }, + { + name: "Invalid IPv6 LSP Identifiers (truncated '2001:db8::1')", + input: []uint8{ + 0x00, 0x13, 0x00, 0x20, // Type IPV6-LSP-IDENTIFIERS (0x13)、Length 56 (0x38) + 0x20, 0x01, 0x0D, 0xB8, // Start of '2001:db8::' + 0x00, 0x00, 0x00, // Incomplete (should be 16 bytes total) + }, + expected: NewIPv6LSPIdentifiers(netip.Addr{}, netip.Addr{}, 0, 0, [16]byte{}), + err: true, + }, + { + name: "Invalid IPv6 LSP Identifiers (extra bytes after '2001:db8::1')", + input: []uint8{ + 0x00, 0x13, 0x00, 0x20, // Type IPV6-LSP-IDENTIFIERS (0x13)、Length 56 (0x38) + 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // Valid IPv6: 2001:db8::1 + 0xCA, 0xFE, 0xBA, 0xBE, // Extra unexpected bytes + }, + expected: NewIPv6LSPIdentifiers(netip.Addr{}, netip.Addr{}, 0, 0, [16]byte{}), + err: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var tlv IPv6LSPIdentifiers + err := tlv.DecodeFromBytes(tt.input) + if tt.err { + assert.Error(t, err, "expected error for input: %v", tt.input) + } else { + assert.NoError(t, err, "unexpected error for input: %v", tt.input) + assert.Equal(t, tt.expected, &tlv) + } + }) + } +} + +func TestIPv6LSPIdentifiers_Serialize(t *testing.T) { + tests := []struct { + name string + input *IPv6LSPIdentifiers + expected []uint8 + }{ + { + name: "Valid IPv6 LSP Identifiers", + input: NewIPv6LSPIdentifiers(netip.MustParseAddr("2001:db8::1"), netip.MustParseAddr("2001:db8::2"), 1, 2, [16]byte{}), + expected: NewIPv6LSPIdentifiers(netip.MustParseAddr("2001:db8::1"), netip.MustParseAddr("2001:db8::2"), 1, 2, [16]byte{}).Serialize(), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.expected, tt.input.Serialize()) + }) + } +} + +func TestLSPDBVersion_DecodeFromBytes(t *testing.T) { + tests := []struct { + name string + input []uint8 + expected *LSPDBVersion + err bool + }{ + { + name: "Valid LSPDB Version", + input: NewLSPDBVersion(12345).Serialize(), + expected: NewLSPDBVersion(12345), + err: false, + }, + { + name: "Invalid input (too short data)", + input: []byte{0x00, 0x17, 0x00, 0x02}, // Type LSP-DB-VERSION (0x17)、Input too short for valid decoding + expected: NewLSPDBVersion(0), + err: true, + }, + { + name: "Invalid input (too long data)", + input: []byte{ + 0x00, 0x17, 0x00, 0x09, // Type LSP-DB-VERSION (0x17)、Length 8 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x39, + 0x00, // Extra bytes after version + }, + expected: NewLSPDBVersion(0), + err: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var tlv LSPDBVersion + err := tlv.DecodeFromBytes(tt.input) + if tt.err { + assert.Error(t, err, "expected error for input: %v", tt.input) + } else { + assert.NoError(t, err, "unexpected error for input: %v", tt.input) + assert.Equal(t, tt.expected, &tlv) + } + }) + } +} + +func TestLSPDBVersion_Serialize(t *testing.T) { + tests := []struct { + name string + input *LSPDBVersion + expected []uint8 + }{ + { + name: "Valid LSPDB Version", + input: NewLSPDBVersion(12345), + expected: NewLSPDBVersion(12345).Serialize(), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.expected, tt.input.Serialize()) + }) + } +} + +func TestLSPDBVersion_Len(t *testing.T) { + tests := []struct { + name string + input *LSPDBVersion + expected uint16 + }{ + { + name: "LSPDB Version length", + input: NewLSPDBVersion(12345), + expected: TLVHeaderLength + TLVLSPDBVersionValueLength, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.expected, tt.input.Len()) + }) + } +} + +func TestSRPCECapability_DecodeFromBytes(t *testing.T) { + tests := []struct { + name string + input []uint8 + expected *SRPCECapability + err bool + }{ + { + name: "Valid SRPCE Capability", + input: NewSRPCECapability(true, true, 42).Serialize(), // Maximum SID Depth 42 + expected: NewSRPCECapability(true, true, 42), + err: false, + }, + { + name: "Invalid input (too short data)", + input: []uint8{0x00, 0x11, 0x00, 0x02}, // Too short for valid decoding + expected: NewSRPCECapability(false, false, 0), + err: true, + }, + { + name: "Invalid input (too long data)", + input: []uint8{0x00, 0x11, 0x00, 0x02, 0x00, 0x00, 0x03, 0x05, 0x01}, // Too long for valid decoding + expected: NewSRPCECapability(false, false, 0), + err: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var tlv SRPCECapability + err := tlv.DecodeFromBytes(tt.input) + if tt.err { + assert.Error(t, err, "expected error for input: %v", tt.input) + } else { + assert.NoError(t, err, "unexpected error for input: %v", tt.input) + assert.Equal(t, tt.expected, &tlv) + } + }) + } +} + +func TestSRPCECapability_Serialize(t *testing.T) { + tests := []struct { + name string + input *SRPCECapability + expected []uint8 + }{ + { + name: "Valid SRPCE Capability", + input: NewSRPCECapability(true, true, 5), + expected: NewSRPCECapability(true, true, 5).Serialize(), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.expected, tt.input.Serialize()) + }) + } +} + +func TestSRPCECapability_Len(t *testing.T) { + tests := []struct { + name string + input *SRPCECapability + expected uint16 + }{ + { + name: "SRPCE Capability length", + input: NewSRPCECapability(true, true, 5), + expected: TLVHeaderLength + TLVSRPCECapabilityValueLength, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.expected, tt.input.Len()) + }) + } +} From beaf7f2b1dce91e6d8694d34dc79f0460085aed5 Mon Sep 17 00:00:00 2001 From: watal Date: Mon, 7 Apr 2025 12:01:01 +0900 Subject: [PATCH 18/87] fix(json): replace strings.Fields with explicit loop in Psts.MarshalJSON to avoid whitespace issues --- pkg/packet/pcep/tlv.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/packet/pcep/tlv.go b/pkg/packet/pcep/tlv.go index faf3179e..b997b742 100644 --- a/pkg/packet/pcep/tlv.go +++ b/pkg/packet/pcep/tlv.go @@ -764,7 +764,11 @@ func (ts Psts) MarshalJSON() ([]byte, error) { if ts == nil { result = "null" } else { - result = strings.Join(strings.Fields(fmt.Sprintf("%d", ts)), ",") + var values []string + for _, pst := range ts { + values = append(values, fmt.Sprintf("%d", pst)) + } + result = strings.Join(values, ",") } return []byte(result), nil } From a65a8e10c964e73bb60820655a969f229cfb34fd Mon Sep 17 00:00:00 2001 From: watal Date: Wed, 16 Apr 2025 08:24:13 +0900 Subject: [PATCH 19/87] refactor(pcep): add length check, index const, and tests for PathSetupType --- pkg/packet/pcep/tlv.go | 19 ++++++++- pkg/packet/pcep/tlv_test.go | 81 +++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 2 deletions(-) diff --git a/pkg/packet/pcep/tlv.go b/pkg/packet/pcep/tlv.go index b997b742..59d26ab1 100644 --- a/pkg/packet/pcep/tlv.go +++ b/pkg/packet/pcep/tlv.go @@ -777,8 +777,17 @@ type PathSetupType struct { PathSetupType Pst } +const ( + PathSetupTypePathSetupTypeIndex = 3 +) + func (tlv *PathSetupType) DecodeFromBytes(data []uint8) error { - tlv.PathSetupType = Pst(data[7]) + expectedLength := TLVHeaderLength + int(TLVPathSetupTypeValueLength) + if len(data) != expectedLength { + return fmt.Errorf("data length mismatch: expected %d bytes, but got %d bytes for PathSetupType", expectedLength, len(data)) + } + + tlv.PathSetupType = Pst(data[TLVHeaderLength+PathSetupTypePathSetupTypeIndex]) return nil } @@ -794,7 +803,7 @@ func (tlv *PathSetupType) Serialize() []uint8 { buf = append(buf, length...) val := make([]uint8, TLVPathSetupTypeValueLength) - val[3] = uint8(tlv.PathSetupType) + val[PathSetupTypePathSetupTypeIndex] = uint8(tlv.PathSetupType) buf = append(buf, val...) return buf @@ -812,6 +821,12 @@ func (tlv *PathSetupType) Len() uint16 { return TLVHeaderLength + TLVPathSetupTypeValueLength } +func NewPathSetupType(pst Pst) *PathSetupType { + return &PathSetupType{ + PathSetupType: pst, + } +} + type ExtendedAssociationID struct { Color uint32 Endpoint netip.Addr diff --git a/pkg/packet/pcep/tlv_test.go b/pkg/packet/pcep/tlv_test.go index eb4a64cd..5c623a01 100644 --- a/pkg/packet/pcep/tlv_test.go +++ b/pkg/packet/pcep/tlv_test.go @@ -533,3 +533,84 @@ func TestSRPCECapability_Len(t *testing.T) { }) } } + +func TestPathSetupType_DecodeFromBytes(t *testing.T) { + tests := []struct { + name string + input []uint8 + expected *PathSetupType + err bool + }{ + { + name: "Valid PathSetupType SRv6TE", + input: NewPathSetupType(PathSetupTypeSRv6TE).Serialize(), + expected: NewPathSetupType(PathSetupTypeSRv6TE), + err: false, + }, + { + name: "Invalid input (too short)", + input: []uint8{0x00, 0x15, 0x00, 0x04}, // insufficient data + expected: NewPathSetupType(0), + err: true, + }, + { + name: "Invalid input (too long)", + input: append(NewPathSetupType(PathSetupTypeSRTE).Serialize(), 0x00, 0x00), + expected: NewPathSetupType(0), + err: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var tlv PathSetupType + err := tlv.DecodeFromBytes(tt.input) + if tt.err { + assert.Error(t, err, "expected error for input: %v", tt.input) + } else { + assert.NoError(t, err, "unexpected error for input: %v", tt.input) + assert.Equal(t, tt.expected, &tlv) + } + }) + } +} + +func TestPathSetupType_Serialize(t *testing.T) { + tests := []struct { + name string + input *PathSetupType + expected []uint8 + }{ + { + name: "Serialize PathSetupType SRTE", + input: NewPathSetupType(PathSetupTypeSRTE), + expected: NewPathSetupType(PathSetupTypeSRTE).Serialize(), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.expected, tt.input.Serialize()) + }) + } +} + +func TestPathSetupType_Len(t *testing.T) { + tests := []struct { + name string + input *PathSetupType + expected uint16 + }{ + { + name: "Length should be header + value length", + input: NewPathSetupType(PathSetupTypeRSVPTE), + expected: TLVHeaderLength + TLVPathSetupTypeValueLength, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.expected, tt.input.Len()) + }) + } +} From 50bce3b9172822437c753705d51025211ceafe26 Mon Sep 17 00:00:00 2001 From: watal Date: Fri, 18 Apr 2025 09:02:31 +0900 Subject: [PATCH 20/87] refactor(pcep): fix TLV constant names and improve TLV parsing with named offset --- pkg/packet/pcep/tlv.go | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/pkg/packet/pcep/tlv.go b/pkg/packet/pcep/tlv.go index 59d26ab1..6df99da0 100644 --- a/pkg/packet/pcep/tlv.go +++ b/pkg/packet/pcep/tlv.go @@ -19,7 +19,8 @@ import ( type TLVType uint16 -const ( // PCEP TLV +// PCEP TLV types +const ( TLVNoPathVector TLVType = 0x01 TLVOverloadDuration TLVType = 0x02 TLVReqMissing TLVType = 0x03 @@ -177,32 +178,19 @@ func (t TLVType) String() string { return fmt.Sprintf("Unknown TLV (0x%04x)", uint16(t)) } -var tlvMap = map[TLVType]func() TLVInterface{ - TLVStatefulPCECapability: func() TLVInterface { return &StatefulPCECapability{} }, - TLVSymbolicPathName: func() TLVInterface { return &SymbolicPathName{} }, - TLVIPv4LSPIdentifiers: func() TLVInterface { return &IPv4LSPIdentifiers{} }, - TLVIPv6LSPIdentifiers: func() TLVInterface { return &IPv6LSPIdentifiers{} }, - TLVLSPDBVersion: func() TLVInterface { return &LSPDBVersion{} }, - TLVSRPCECapability: func() TLVInterface { return &SRPCECapability{} }, - TLVPathSetupType: func() TLVInterface { return &PathSetupType{} }, - TLVExtendedAssociationID: func() TLVInterface { return &ExtendedAssociationID{} }, - TLVPathSetupTypeCapability: func() TLVInterface { return &PathSetupTypeCapability{} }, - TLVAssocTypeList: func() TLVInterface { return &AssocTypeList{} }, - TLVColor: func() TLVInterface { return &Color{} }, -} - +// TLV header length (type + length) const TLVHeaderLength = 4 -// TLV value lengths (in bytes), excluding the 4-byte TLV header (type + length) +// TLV value lengths, excluding the 4-byte TLV header (type + length) const ( TLVStatefulPCECapabilityValueLength uint16 = 4 + TLVIPv4LSPIdentifiersValueLength uint16 = 16 + TLVIPv6LSPIdentifiersValueLength uint16 = 52 TLVLSPDBVersionValueLength uint16 = 8 TLVSRPCECapabilityValueLength uint16 = 4 TLVPathSetupTypeValueLength uint16 = 4 TLVExtendedAssociationIDIPv4ValueLength uint16 = 8 TLVExtendedAssociationIDIPv6ValueLength uint16 = 20 - TLVIPv4LSPIdentifiersValueLength uint16 = 16 - TLVIPv6LSPIdentifiersValueLength uint16 = 52 TLVSRPolicyCPathIDValueLength uint16 = 28 TLVSRPolicyCPathPreferenceValueLength uint16 = 4 TLVColorValueLength uint16 = 4 @@ -221,6 +209,7 @@ const ( SubTLVPreferenceCisco TLVType = 0x03 ) +// Cisco specific SubTLV length const ( SubTLVColorCiscoValueLength uint16 = 4 SubTLVPreferenceCiscoValueLength uint16 = 4 @@ -234,6 +223,20 @@ type TLVInterface interface { Len() uint16 // Total length of Type, Length, and Value } +var tlvMap = map[TLVType]func() TLVInterface{ + TLVStatefulPCECapability: func() TLVInterface { return &StatefulPCECapability{} }, + TLVSymbolicPathName: func() TLVInterface { return &SymbolicPathName{} }, + TLVIPv4LSPIdentifiers: func() TLVInterface { return &IPv4LSPIdentifiers{} }, + TLVIPv6LSPIdentifiers: func() TLVInterface { return &IPv6LSPIdentifiers{} }, + TLVLSPDBVersion: func() TLVInterface { return &LSPDBVersion{} }, + TLVSRPCECapability: func() TLVInterface { return &SRPCECapability{} }, + TLVPathSetupType: func() TLVInterface { return &PathSetupType{} }, + TLVExtendedAssociationID: func() TLVInterface { return &ExtendedAssociationID{} }, + TLVPathSetupTypeCapability: func() TLVInterface { return &PathSetupTypeCapability{} }, + TLVAssocTypeList: func() TLVInterface { return &AssocTypeList{} }, + TLVColor: func() TLVInterface { return &Color{} }, +} + type StatefulPCECapability struct { LSPUpdateCapability bool // 31 IncludeDBVersion bool // 30 From e2e237f1474a6fba5a38be47278accc42efe5788 Mon Sep 17 00:00:00 2001 From: Motok1 Date: Wed, 27 Aug 2025 18:45:18 +0900 Subject: [PATCH 21/87] refactor(ted): simplify types --- cmd/pola/ted.go | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/cmd/pola/ted.go b/cmd/pola/ted.go index 0136ee9d..0d0438fb 100644 --- a/cmd/pola/ted.go +++ b/cmd/pola/ted.go @@ -38,70 +38,70 @@ func print(jsonFlag bool) error { if jsonFlag { // Output JSON format - nodes := []map[string]interface{}{} + nodes := []map[string]any{} for _, as := range ted.Nodes { for _, node := range as { - tmpNode := map[string]interface{}{ // TODO: Fix format according to readme + nodeMap := map[string]any{ // TODO: Fix format according to readme "asn": node.ASN, "routerID": node.RouterID, "isisAreaID": node.IsisAreaID, "hostname": node.Hostname, "srgbBegin": node.SrgbBegin, "srgbEnd": node.SrgbEnd, - "prefixes": []map[string]interface{}{}, - "links": []map[string]interface{}{}, + "prefixes": []map[string]any{}, + "links": []map[string]any{}, } - links := []map[string]interface{}{} + links := []map[string]any{} for _, link := range node.Links { - metrics := []map[string]interface{}{} + metrics := []map[string]any{} for _, metric := range link.Metrics { - tmpMetric := map[string]interface{}{ + metricMap := map[string]any{ "type": metric.Type.String(), "value": metric.Value, } - metrics = append(metrics, tmpMetric) + metrics = append(metrics, metricMap) } - tmpLink := map[string]interface{}{ + linkMap := map[string]any{ "localIP": link.LocalIP.String(), "remoteIP": link.RemoteIP.String(), "remoteNode": link.RemoteNode.RouterID, "metrics": metrics, "adjSid": link.AdjSid, } - links = append(links, tmpLink) + links = append(links, linkMap) } - tmpNode["links"] = links + nodeMap["links"] = links - prefixes := []map[string]interface{}{} + prefixes := []map[string]any{} for _, prefix := range node.Prefixes { - tmpPrefix := map[string]interface{}{ + prefixMap := map[string]any{ "prefix": prefix.Prefix.String(), } if prefix.SidIndex != 0 { - tmpPrefix["sidIndex"] = prefix.SidIndex + prefixMap["sidIndex"] = prefix.SidIndex } - prefixes = append(prefixes, tmpPrefix) + prefixes = append(prefixes, prefixMap) } - tmpNode["prefixes"] = prefixes + nodeMap["prefixes"] = prefixes - srv6SIDs := []map[string]interface{}{} + srv6SIDs := []map[string]any{} for _, srv6SID := range node.SRv6SIDs { - tmpSrv6SID := map[string]interface{}{ + srv6SIDMap := map[string]any{ "sids": srv6SID.Sids, "endpointBehavior": srv6SID.EndpointBehavior, "multiTopoIDs": srv6SID.MultiTopoIDs, } - srv6SIDs = append(srv6SIDs, tmpSrv6SID) + srv6SIDs = append(srv6SIDs, srv6SIDMap) } - tmpNode["srv6SIDs"] = srv6SIDs + nodeMap["srv6SIDs"] = srv6SIDs - nodes = append(nodes, tmpNode) + nodes = append(nodes, nodeMap) } } - outputMap := map[string]interface{}{ + outputMap := map[string]any{ "ted": nodes, } From dcd1ae78cb19a283be9cec249bf3442715f1fb92 Mon Sep 17 00:00:00 2001 From: watal Date: Fri, 18 Apr 2025 10:14:03 +0900 Subject: [PATCH 22/87] refactor,test(pcep): make Uint16ToByteSlice generic (~uint16) and add TLVType tests --- pkg/packet/pcep/object.go | 2 +- pkg/packet/pcep/pcep_util.go | 57 ++++++++++++++----------------- pkg/packet/pcep/pcep_util_test.go | 30 ++++++++++++---- pkg/packet/pcep/tlv.go | 2 +- 4 files changed, 52 insertions(+), 39 deletions(-) diff --git a/pkg/packet/pcep/object.go b/pkg/packet/pcep/object.go index a3eeae84..012c76cd 100644 --- a/pkg/packet/pcep/object.go +++ b/pkg/packet/pcep/object.go @@ -1329,7 +1329,7 @@ func (o *AssociationObject) Serialize() ([]uint8, error) { buf[4] = buf[4] | 0x01 } - assocType := Uint16ToByteSlice(uint16(o.AssocType)) + assocType := Uint16ToByteSlice(o.AssocType) assocID := Uint16ToByteSlice(o.AssocID) byteTLVs := []uint8{} diff --git a/pkg/packet/pcep/pcep_util.go b/pkg/packet/pcep/pcep_util.go index 80ff0a4a..a8dd7fa0 100644 --- a/pkg/packet/pcep/pcep_util.go +++ b/pkg/packet/pcep/pcep_util.go @@ -5,55 +5,50 @@ package pcep -import ( - "encoding/binary" - - "golang.org/x/exp/constraints" -) - -// AppendByteSlices concatenates byte slices into a single slice. -func AppendByteSlices(byteSlices ...[]byte) []byte { - // Calculate the total length of the joined slice. - joinedSliceLength := 0 - for _, byteSlice := range byteSlices { - joinedSliceLength += len(byteSlice) +import "encoding/binary" + +// AppendByteSlices concatenates multiple byte slices into a single slice. +func AppendByteSlices(slices ...[]byte) []byte { + totalLength := 0 + for _, s := range slices { + totalLength += len(s) } - // Allocate the joined slice with the total length and copy the byte slices. - joinedSlice := make([]byte, joinedSliceLength) - var index int - for _, byteSlice := range byteSlices { - copy(joinedSlice[index:], byteSlice) - index += len(byteSlice) + + result := make([]byte, totalLength) + offset := 0 + for _, s := range slices { + copy(result[offset:], s) + offset += len(s) } - return joinedSlice + + return result } -// Uint16ToByteSlice converts a uint16 value to a big-endian byte slice. -func Uint16ToByteSlice(input uint16) []byte { - uint16Bytes := make([]byte, 2) - binary.BigEndian.PutUint16(uint16Bytes, input) - return uint16Bytes +// Uint16ToByteSlice converts a uint16 or TLVType value to a big-endian byte slice. +func Uint16ToByteSlice[T ~uint16](v T) []byte { + b := make([]byte, 2) + binary.BigEndian.PutUint16(b, uint16(v)) + return b } // Uint32ToByteSlice converts a uint32 value to a big-endian byte slice. -func Uint32ToByteSlice(input uint32) []byte { - uint32Bytes := make([]byte, 4) - binary.BigEndian.PutUint32(uint32Bytes, input) - return uint32Bytes +func Uint32ToByteSlice(v uint32) []byte { + b := make([]byte, 4) + binary.BigEndian.PutUint32(b, v) + return b } // Bitwise is a type constraint for unsigned integer types (uint8, uint16, uint32). type Bitwise interface { - constraints.Unsigned ~uint8 | ~uint16 | ~uint32 } -// IsBitSet checks if a bit is set, with bit 0 as the least significant bit (LSB). +// IsBitSet checks if a specific bit is set in the value, with bit 0 as the least significant bit (LSB). func IsBitSet[T Bitwise](value, mask T) bool { return value&mask != 0 } -// SetBit sets a specific bit in a value of any unsigned integer type. +// SetBit sets a specific bit in the value of any unsigned integer type. func SetBit[T Bitwise](value, bit T) T { return value | bit } diff --git a/pkg/packet/pcep/pcep_util_test.go b/pkg/packet/pcep/pcep_util_test.go index 2c7f51a1..a8360917 100644 --- a/pkg/packet/pcep/pcep_util_test.go +++ b/pkg/packet/pcep/pcep_util_test.go @@ -41,24 +41,42 @@ func TestAppendByteSlices(t *testing.T) { func TestUint16ToByteSlice(t *testing.T) { tests := []struct { name string - input uint16 + input any expected []byte }{ { - name: "Convert 0x0102 to bytes", - input: 0x0102, + name: "Convert uint16 0x0102 to bytes", + input: uint16(0x0102), expected: []byte{0x01, 0x02}, }, { - name: "Convert 0xFFFF to bytes", - input: 0xFFFF, + name: "Convert uint16 0xFFFF to bytes", + input: uint16(0xFFFF), + expected: []byte{0xFF, 0xFF}, + }, + { + name: "Convert TLVType 0x0102 to bytes", + input: TLVType(0x0102), + expected: []byte{0x01, 0x02}, + }, + { + name: "Convert TLVType 0xFFFF to bytes", + input: TLVType(0xFFFF), expected: []byte{0xFF, 0xFF}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result := Uint16ToByteSlice(tt.input) + var result []byte + switch v := tt.input.(type) { + case uint16: + result = Uint16ToByteSlice(v) + case TLVType: + result = Uint16ToByteSlice(v) + default: + t.Fatalf("unexpected type %T", v) + } if !bytes.Equal(result, tt.expected) { t.Errorf("expected %v, got %v", tt.expected, result) } diff --git a/pkg/packet/pcep/tlv.go b/pkg/packet/pcep/tlv.go index 6df99da0..89f66b53 100644 --- a/pkg/packet/pcep/tlv.go +++ b/pkg/packet/pcep/tlv.go @@ -402,7 +402,7 @@ func (tlv *SymbolicPathName) Serialize() []byte { padding := (4 - (nameLen % 4)) % 4 // Padding for 4-byte alignment buf := make([]byte, 0, TLVHeaderLength+int(nameLen)+int(padding)) - buf = append(buf, Uint16ToByteSlice(uint16(tlv.Type()))...) + buf = append(buf, Uint16ToByteSlice(tlv.Type())...) buf = append(buf, Uint16ToByteSlice(nameLen)...) buf = append(buf, []byte(tlv.Name)...) From b3354c1b356e0461ccd4b12fe3bd035d3a54ede3 Mon Sep 17 00:00:00 2001 From: watal Date: Fri, 18 Apr 2025 16:54:17 +0900 Subject: [PATCH 23/87] refactor(pcep): simplify serialization by making SetBit conditional --- pkg/packet/pcep/message.go | 4 +- pkg/packet/pcep/pcep_util.go | 7 +++- pkg/packet/pcep/pcep_util_test.go | 63 +++++++++++++++++++------------ pkg/packet/pcep/tlv_test.go | 10 ++--- 4 files changed, 49 insertions(+), 35 deletions(-) diff --git a/pkg/packet/pcep/message.go b/pkg/packet/pcep/message.go index 2d3bd77d..55269972 100644 --- a/pkg/packet/pcep/message.go +++ b/pkg/packet/pcep/message.go @@ -89,9 +89,7 @@ func (h *CommonHeader) Serialize() []uint8 { verFlag := uint8(h.Version<<5 | h.Flag) buf = append(buf, verFlag) buf = append(buf, uint8(h.MessageType)) - messageLength := make([]uint8, 2) - binary.BigEndian.PutUint16(messageLength, h.MessageLength) - buf = append(buf, messageLength...) + buf = append(buf, Uint16ToByteSlice(h.MessageLength)...) return buf } diff --git a/pkg/packet/pcep/pcep_util.go b/pkg/packet/pcep/pcep_util.go index a8dd7fa0..9643cc06 100644 --- a/pkg/packet/pcep/pcep_util.go +++ b/pkg/packet/pcep/pcep_util.go @@ -49,6 +49,9 @@ func IsBitSet[T Bitwise](value, mask T) bool { } // SetBit sets a specific bit in the value of any unsigned integer type. -func SetBit[T Bitwise](value, bit T) T { - return value | bit +func SetBit[T Bitwise](value, bit T, condition bool) T { + if condition { + return value | bit + } + return value } diff --git a/pkg/packet/pcep/pcep_util_test.go b/pkg/packet/pcep/pcep_util_test.go index a8360917..f7554242 100644 --- a/pkg/packet/pcep/pcep_util_test.go +++ b/pkg/packet/pcep/pcep_util_test.go @@ -168,46 +168,59 @@ func TestIsBitSet(t *testing.T) { func TestSetBit(t *testing.T) { tests := []struct { - name string - value uint8 - bit uint8 - expected uint8 + name string + value uint8 + bit uint8 + condition bool + expected uint8 }{ { - name: "Set bit 0", - value: 0x00, - bit: 0x01, - expected: 0x01, + name: "Set bit 0", + value: 0x00, + bit: 0x01, + condition: true, + expected: 0x01, + }, + { + name: "Set bit 1", + value: 0x00, + bit: 0x02, + condition: true, + expected: 0x02, }, { - name: "Set bit 1", - value: 0x00, - bit: 0x02, - expected: 0x02, + name: "Set bit 0 when bit 0 is already set", + value: 0x01, + bit: 0x01, + condition: true, + expected: 0x01, }, { - name: "Set bit 0 when bit 0 is already set", - value: 0x01, - bit: 0x01, - expected: 0x01, + name: "Set bit 1 when bit 0 is already set", + value: 0x01, + bit: 0x02, + condition: true, + expected: 0x03, }, { - name: "Set bit 1 when bit 0 is already set", - value: 0x01, - bit: 0x02, - expected: 0x03, + name: "Set bit 0 when bit 1 is already set", + value: 0x02, + bit: 0x01, + condition: true, + expected: 0x03, }, { - name: "Set bit 0 when bit 1 is already set", - value: 0x02, - bit: 0x01, - expected: 0x03, + name: "Do not set bit 0 when condition is false", + value: 0x00, + bit: 0x01, + condition: false, + expected: 0x00, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result := SetBit(tt.value, tt.bit) + result := SetBit(tt.value, tt.bit, tt.condition) if result != tt.expected { t.Errorf("Test %s failed: expected %v, got %v", tt.name, tt.expected, result) } diff --git a/pkg/packet/pcep/tlv_test.go b/pkg/packet/pcep/tlv_test.go index 5c623a01..46c186ee 100644 --- a/pkg/packet/pcep/tlv_test.go +++ b/pkg/packet/pcep/tlv_test.go @@ -6,7 +6,6 @@ package pcep import ( - "encoding/binary" "net/netip" "testing" @@ -76,10 +75,11 @@ func TestStatefulPCECapability_Serialize(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tlv := NewStatefulPCECapability(tt.bits) - expected := make([]uint8, TLVHeaderLength+TLVStatefulPCECapabilityValueLength) - binary.BigEndian.PutUint16(expected[0:2], uint16(TLVStatefulPCECapability)) - binary.BigEndian.PutUint16(expected[2:4], TLVStatefulPCECapabilityValueLength) - binary.BigEndian.PutUint32(expected[4:8], tlv.CapabilityBits()) + expected := AppendByteSlices( + Uint16ToByteSlice(TLVStatefulPCECapability), + Uint16ToByteSlice(TLVStatefulPCECapabilityValueLength), + Uint32ToByteSlice(tlv.SetFlags()), + ) assert.Equal(t, expected, tlv.Serialize(), "serialized output mismatch") }) From 0012b3129349ec91ac3a2c09d80cd7d8b1522df8 Mon Sep 17 00:00:00 2001 From: watal Date: Fri, 18 Apr 2025 17:27:05 +0900 Subject: [PATCH 24/87] refactor(pcep): use []byte and byte consistently --- pkg/packet/pcep/pcep_util.go | 6 +- pkg/packet/pcep/tlv.go | 156 +++++++++++++++++------------------ 2 files changed, 81 insertions(+), 81 deletions(-) diff --git a/pkg/packet/pcep/pcep_util.go b/pkg/packet/pcep/pcep_util.go index 9643cc06..adc11ec3 100644 --- a/pkg/packet/pcep/pcep_util.go +++ b/pkg/packet/pcep/pcep_util.go @@ -9,12 +9,12 @@ import "encoding/binary" // AppendByteSlices concatenates multiple byte slices into a single slice. func AppendByteSlices(slices ...[]byte) []byte { - totalLength := 0 + totalLen := 0 for _, s := range slices { - totalLength += len(s) + totalLen += len(s) } - result := make([]byte, totalLength) + result := make([]byte, totalLen) offset := 0 for _, s := range slices { copy(result[offset:], s) diff --git a/pkg/packet/pcep/tlv.go b/pkg/packet/pcep/tlv.go index 89f66b53..822f2a71 100644 --- a/pkg/packet/pcep/tlv.go +++ b/pkg/packet/pcep/tlv.go @@ -216,8 +216,8 @@ const ( ) type TLVInterface interface { - DecodeFromBytes(data []uint8) error - Serialize() []uint8 + DecodeFromBytes(data []byte) error + Serialize() []byte MarshalLogObject(enc zapcore.ObjectEncoder) error Type() TLVType Len() uint16 // Total length of Type, Length, and Value @@ -268,7 +268,7 @@ const ( StatefulPCECapabilityFlagsIndex = 3 ) -func (tlv *StatefulPCECapability) DecodeFromBytes(data []uint8) error { +func (tlv *StatefulPCECapability) DecodeFromBytes(data []byte) error { if len(data) < int(tlv.Len()) { return fmt.Errorf("data is too short: expected at least %d bytes, but got %d bytes for StatefulPCECapability", tlv.Len(), len(data)) } @@ -387,8 +387,8 @@ func (tlv *SymbolicPathName) DecodeFromBytes(data []byte) error { return fmt.Errorf("data is too short: expected at least %d bytes, but got %d bytes for SymbolicPathName", TLVHeaderLength, len(data)) } - length := binary.BigEndian.Uint16(data[2:4]) - totalLength := int(TLVHeaderLength + length) + nameLen := binary.BigEndian.Uint16(data[2:4]) + totalLength := TLVHeaderLength + int(nameLen) if len(data) != totalLength { return fmt.Errorf("data length mismatch: expected %d bytes, but got %d bytes for SymbolicPathName", totalLength, len(data)) } @@ -451,7 +451,7 @@ const ( IPv4LSPIdentifiersExtendedTunnelIDIndex = 12 ) -func (tlv *IPv4LSPIdentifiers) DecodeFromBytes(data []uint8) error { +func (tlv *IPv4LSPIdentifiers) DecodeFromBytes(data []byte) error { expectedLength := TLVHeaderLength + int(TLVIPv4LSPIdentifiersValueLength) if len(data) != expectedLength { return fmt.Errorf("data length mismatch: expected %d bytes, but got %d bytes for IPv4LSPIdentifiers", expectedLength, len(data)) @@ -525,7 +525,7 @@ type IPv6LSPIdentifiers struct { ExtendedTunnelID [16]byte } -func (tlv *IPv6LSPIdentifiers) DecodeFromBytes(data []uint8) error { +func (tlv *IPv6LSPIdentifiers) DecodeFromBytes(data []byte) error { expectedLength := TLVHeaderLength + int(TLVIPv6LSPIdentifiersValueLength) if len(data) != expectedLength { return fmt.Errorf("data length mismatch: expected %d bytes, but got %d bytes for IPv6LSPIdentifiers", expectedLength, len(data)) @@ -547,8 +547,8 @@ func (tlv *IPv6LSPIdentifiers) DecodeFromBytes(data []uint8) error { return nil } -func (tlv *IPv6LSPIdentifiers) Serialize() []uint8 { - buf := make([]uint8, tlv.Len()) +func (tlv *IPv6LSPIdentifiers) Serialize() []byte { + buf := make([]byte, tlv.Len()) binary.BigEndian.PutUint16(buf[0:2], uint16(tlv.Type())) binary.BigEndian.PutUint16(buf[2:4], TLVIPv6LSPIdentifiersValueLength) @@ -587,7 +587,7 @@ type LSPDBVersion struct { VersionNumber uint64 } -func (tlv *LSPDBVersion) DecodeFromBytes(data []uint8) error { +func (tlv *LSPDBVersion) DecodeFromBytes(data []byte) error { expectedLength := TLVHeaderLength + int(TLVLSPDBVersionValueLength) if len(data) != expectedLength { return fmt.Errorf("data length mismatch: expected %d bytes, but got %d bytes for LSPDBVersion", expectedLength, len(data)) @@ -597,18 +597,18 @@ func (tlv *LSPDBVersion) DecodeFromBytes(data []uint8) error { return nil } -func (tlv *LSPDBVersion) Serialize() []uint8 { - buf := []uint8{} +func (tlv *LSPDBVersion) Serialize() []byte { + buf := []byte{} - typ := make([]uint8, 2) + typ := make([]byte, 2) binary.BigEndian.PutUint16(typ, uint16(tlv.Type())) buf = append(buf, typ...) - length := make([]uint8, 2) + length := make([]byte, 2) binary.BigEndian.PutUint16(length, TLVLSPDBVersionValueLength) buf = append(buf, length...) - val := make([]uint8, TLVLSPDBVersionValueLength) + val := make([]byte, TLVLSPDBVersionValueLength) binary.BigEndian.PutUint64(val, tlv.VersionNumber) buf = append(buf, val...) @@ -644,8 +644,8 @@ type SRPCECapability struct { } const ( - UnlimitedMaximumSIDDepthFlag uint8 = 0x01 - NAISupportedFlag uint8 = 0x02 + UnlimitedMaximumSIDDepthFlag byte = 0x01 + NAISupportedFlag byte = 0x02 ) const ( @@ -735,11 +735,11 @@ func NewSRPCECapability(hasUnlimitedMaxSIDDepth bool, isNAISupported bool, maxim type Pst uint8 const ( - PathSetupTypeRSVPTE Pst = 0x0 - PathSetupTypeSRTE Pst = 0x1 - PathSetupTypePCECCTE Pst = 0x2 - PathSetupTypeSRv6TE Pst = 0x3 - PathSetupTypeIPTE Pst = 0x4 + PathSetupTypeRSVPTE Pst = 0x00 + PathSetupTypeSRTE Pst = 0x01 + PathSetupTypePCECCTE Pst = 0x02 + PathSetupTypeSRv6TE Pst = 0x03 + PathSetupTypeIPTE Pst = 0x04 ) var pathSetupDescriptions = map[Pst]struct { @@ -784,7 +784,7 @@ const ( PathSetupTypePathSetupTypeIndex = 3 ) -func (tlv *PathSetupType) DecodeFromBytes(data []uint8) error { +func (tlv *PathSetupType) DecodeFromBytes(data []byte) error { expectedLength := TLVHeaderLength + int(TLVPathSetupTypeValueLength) if len(data) != expectedLength { return fmt.Errorf("data length mismatch: expected %d bytes, but got %d bytes for PathSetupType", expectedLength, len(data)) @@ -794,19 +794,19 @@ func (tlv *PathSetupType) DecodeFromBytes(data []uint8) error { return nil } -func (tlv *PathSetupType) Serialize() []uint8 { - buf := []uint8{} +func (tlv *PathSetupType) Serialize() []byte { + buf := []byte{} - typ := make([]uint8, 2) + typ := make([]byte, 2) binary.BigEndian.PutUint16(typ, uint16(tlv.Type())) buf = append(buf, typ...) - length := make([]uint8, 2) + length := make([]byte, 2) binary.BigEndian.PutUint16(length, TLVPathSetupTypeValueLength) buf = append(buf, length...) - val := make([]uint8, TLVPathSetupTypeValueLength) - val[PathSetupTypePathSetupTypeIndex] = uint8(tlv.PathSetupType) + val := make([]byte, TLVPathSetupTypeValueLength) + val[PathSetupTypePathSetupTypeIndex] = byte(tlv.PathSetupType) buf = append(buf, val...) return buf @@ -835,7 +835,7 @@ type ExtendedAssociationID struct { Endpoint netip.Addr } -func (tlv *ExtendedAssociationID) DecodeFromBytes(data []uint8) error { +func (tlv *ExtendedAssociationID) DecodeFromBytes(data []byte) error { length := binary.BigEndian.Uint16(data[2:4]) tlv.Color = binary.BigEndian.Uint32(data[4:8]) @@ -850,14 +850,14 @@ func (tlv *ExtendedAssociationID) DecodeFromBytes(data []uint8) error { return nil } -func (tlv *ExtendedAssociationID) Serialize() []uint8 { - buf := []uint8{} +func (tlv *ExtendedAssociationID) Serialize() []byte { + buf := []byte{} - typ := make([]uint8, 2) + typ := make([]byte, 2) binary.BigEndian.PutUint16(typ, uint16(tlv.Type())) buf = append(buf, typ...) - length := make([]uint8, 2) + length := make([]byte, 2) if tlv.Endpoint.Is4() { binary.BigEndian.PutUint16(length, TLVExtendedAssociationIDIPv4ValueLength) } else if tlv.Endpoint.Is6() { @@ -865,7 +865,7 @@ func (tlv *ExtendedAssociationID) Serialize() []uint8 { } buf = append(buf, length...) - color := make([]uint8, 4) + color := make([]byte, 4) binary.BigEndian.PutUint32(color, tlv.Color) buf = append(buf, color...) @@ -896,7 +896,7 @@ type PathSetupTypeCapability struct { SubTLVs []TLVInterface } -func (tlv *PathSetupTypeCapability) DecodeFromBytes(data []uint8) error { +func (tlv *PathSetupTypeCapability) DecodeFromBytes(data []byte) error { length := binary.BigEndian.Uint16(data[2:4]) pstNum := int(data[7]) @@ -915,10 +915,10 @@ func (tlv *PathSetupTypeCapability) DecodeFromBytes(data []uint8) error { return nil } -func (tlv *PathSetupTypeCapability) Serialize() []uint8 { - buf := []uint8{} +func (tlv *PathSetupTypeCapability) Serialize() []byte { + buf := []byte{} - typ := make([]uint8, 2) + typ := make([]byte, 2) binary.BigEndian.PutUint16(typ, uint16(tlv.Type())) buf = append(buf, typ...) @@ -932,21 +932,21 @@ func (tlv *PathSetupTypeCapability) Serialize() []uint8 { for _, subTLV := range tlv.SubTLVs { length += subTLV.Len() } - lengthBytes := make([]uint8, 2) + lengthBytes := make([]byte, 2) binary.BigEndian.PutUint16(lengthBytes, length) buf = append(buf, lengthBytes...) - var val []uint8 + var val []byte if numOfPst%4 == 0 { - val = make([]uint8, 4+numOfPst) // 4 byte: Reserve & Num of PST + val = make([]byte, 4+numOfPst) // 4 byte: Reserve & Num of PST } else { - val = make([]uint8, 4+numOfPst+(4-(numOfPst%4))) // 4 byte: Reserve & Num of PST + val = make([]byte, 4+numOfPst+(4-(numOfPst%4))) // 4 byte: Reserve & Num of PST } - val[3] = uint8(numOfPst) + val[3] = byte(numOfPst) for i, pst := range tlv.PathSetupTypes { - val[4+i] = uint8(pst) + val[4+i] = byte(pst) } for _, subTLV := range tlv.SubTLVs { @@ -1021,7 +1021,7 @@ type AssocTypeList struct { AssocTypes []AssocType } -func (tlv *AssocTypeList) DecodeFromBytes(data []uint8) error { +func (tlv *AssocTypeList) DecodeFromBytes(data []byte) error { AssocTypeNum := binary.BigEndian.Uint16(data[2:4]) / 2 for i := 0; i < int(AssocTypeNum); i++ { at := binary.BigEndian.Uint16(data[4+2*i : 6+2*i]) @@ -1030,25 +1030,25 @@ func (tlv *AssocTypeList) DecodeFromBytes(data []uint8) error { return nil } -func (tlv *AssocTypeList) Serialize() []uint8 { - buf := []uint8{} +func (tlv *AssocTypeList) Serialize() []byte { + buf := []byte{} - typ := make([]uint8, 2) + typ := make([]byte, 2) binary.BigEndian.PutUint16(typ, uint16(tlv.Type())) buf = append(buf, typ...) length := uint16(len(tlv.AssocTypes)) * 2 - lengthBytes := make([]uint8, 2) + lengthBytes := make([]byte, 2) binary.BigEndian.PutUint16(lengthBytes, length) buf = append(buf, lengthBytes...) for _, at := range tlv.AssocTypes { - binAt := make([]uint8, 2) + binAt := make([]byte, 2) binary.BigEndian.PutUint16(binAt, uint16(at)) buf = append(buf, binAt...) } if length%4 != 0 { - pad := make([]uint8, 4-(length%4)) + pad := make([]byte, 4-(length%4)) buf = append(buf, pad...) } return buf @@ -1079,19 +1079,19 @@ type SRPolicyCandidatePathIdentifier struct { OriginatorAddr netip.Addr // After DecodeFromBytes, even ipv4 addresses are assigned in ipv6 format } -func (tlv *SRPolicyCandidatePathIdentifier) DecodeFromBytes(data []uint8) error { +func (tlv *SRPolicyCandidatePathIdentifier) DecodeFromBytes(data []byte) error { tlv.OriginatorAddr, _ = netip.AddrFromSlice(data[12:28]) return nil } -func (tlv *SRPolicyCandidatePathIdentifier) Serialize() []uint8 { - buf := []uint8{} +func (tlv *SRPolicyCandidatePathIdentifier) Serialize() []byte { + buf := []byte{} - typ := make([]uint8, 2) + typ := make([]byte, 2) binary.BigEndian.PutUint16(typ, uint16(tlv.Type())) buf = append(buf, typ...) - length := make([]uint8, 2) + length := make([]byte, 2) binary.BigEndian.PutUint16(length, TLVSRPolicyCPathIDValueLength) buf = append(buf, length...) @@ -1126,23 +1126,23 @@ type SRPolicyCandidatePathPreference struct { Preference uint32 } -func (tlv *SRPolicyCandidatePathPreference) DecodeFromBytes(data []uint8) error { +func (tlv *SRPolicyCandidatePathPreference) DecodeFromBytes(data []byte) error { tlv.Preference = binary.BigEndian.Uint32(data[4:8]) return nil } -func (tlv *SRPolicyCandidatePathPreference) Serialize() []uint8 { - buf := []uint8{} +func (tlv *SRPolicyCandidatePathPreference) Serialize() []byte { + buf := []byte{} - typ := make([]uint8, 2) + typ := make([]byte, 2) binary.BigEndian.PutUint16(typ, uint16(tlv.Type())) buf = append(buf, typ...) - length := make([]uint8, 2) + length := make([]byte, 2) binary.BigEndian.PutUint16(length, TLVSRPolicyCPathPreferenceValueLength) buf = append(buf, length...) - preference := make([]uint8, 4) + preference := make([]byte, 4) binary.BigEndian.PutUint32(preference, tlv.Preference) buf = append(buf, preference...) @@ -1165,23 +1165,23 @@ type Color struct { Color uint32 } -func (tlv *Color) DecodeFromBytes(data []uint8) error { +func (tlv *Color) DecodeFromBytes(data []byte) error { tlv.Color = binary.BigEndian.Uint32(data[4:8]) return nil } -func (tlv *Color) Serialize() []uint8 { - buf := []uint8{} +func (tlv *Color) Serialize() []byte { + buf := []byte{} - typ := make([]uint8, 2) + typ := make([]byte, 2) binary.BigEndian.PutUint16(typ, uint16(tlv.Type())) buf = append(buf, typ...) - length := make([]uint8, 2) + length := make([]byte, 2) binary.BigEndian.PutUint16(length, uint16(TLVColor)) buf = append(buf, length...) - color := make([]uint8, 4) + color := make([]byte, 4) binary.BigEndian.PutUint32(color, tlv.Color) buf = append(buf, color...) @@ -1203,10 +1203,10 @@ func (tlv *Color) Len() uint16 { type UndefinedTLV struct { Typ TLVType Length uint16 - Value []uint8 + Value []byte } -func (tlv *UndefinedTLV) DecodeFromBytes(data []uint8) error { +func (tlv *UndefinedTLV) DecodeFromBytes(data []byte) error { tlv.Typ = TLVType(binary.BigEndian.Uint16(data[0:2])) tlv.Length = binary.BigEndian.Uint16(data[2:4]) @@ -1214,20 +1214,20 @@ func (tlv *UndefinedTLV) DecodeFromBytes(data []uint8) error { return nil } -func (tlv *UndefinedTLV) Serialize() []uint8 { - bytePCEPTLV := []uint8{} +func (tlv *UndefinedTLV) Serialize() []byte { + bytePCEPTLV := []byte{} - byteTLVType := make([]uint8, 2) + byteTLVType := make([]byte, 2) binary.BigEndian.PutUint16(byteTLVType, uint16(tlv.Typ)) bytePCEPTLV = append(bytePCEPTLV, byteTLVType...) // Type (2byte) - byteTLVLength := make([]uint8, 2) + byteTLVLength := make([]byte, 2) binary.BigEndian.PutUint16(byteTLVLength, tlv.Length) bytePCEPTLV = append(bytePCEPTLV, byteTLVLength...) // Length (2byte) bytePCEPTLV = append(bytePCEPTLV, tlv.Value...) // Value (Length byte) if padding := tlv.Length % 4; padding != 0 { - bytePadding := make([]uint8, 4-padding) + bytePadding := make([]byte, 4-padding) bytePCEPTLV = append(bytePCEPTLV, bytePadding...) } return bytePCEPTLV @@ -1258,7 +1258,7 @@ func (tlv *UndefinedTLV) SetLength() { tlv.Length = uint16(len(tlv.Value)) } -func DecodeTLV(data []uint8) (TLVInterface, error) { +func DecodeTLV(data []byte) (TLVInterface, error) { if len(data) < 2 { return nil, errors.New("insufficient data to read TLV type") } @@ -1281,7 +1281,7 @@ func DecodeTLV(data []uint8) (TLVInterface, error) { return tlv, nil } -func DecodeTLVs(data []uint8) ([]TLVInterface, error) { +func DecodeTLVs(data []byte) ([]TLVInterface, error) { var tlvs []TLVInterface for len(data) > 0 { From 11b1f491359f3a8949d9f0771412f681f3b00375 Mon Sep 17 00:00:00 2001 From: watal Date: Fri, 18 Apr 2025 17:27:50 +0900 Subject: [PATCH 25/87] refactor(pcep): extend SetBit to support conditional flag setting --- pkg/packet/pcep/tlv.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/pkg/packet/pcep/tlv.go b/pkg/packet/pcep/tlv.go index 822f2a71..b3649ded 100644 --- a/pkg/packet/pcep/tlv.go +++ b/pkg/packet/pcep/tlv.go @@ -686,12 +686,8 @@ func (tlv *SRPCECapability) Serialize() []byte { buf = append(buf, length...) val := make([]byte, TLVSRPCECapabilityValueLength) - if tlv.HasUnlimitedMaxSIDDepth { - val[SRPCECapabilityFlagsIndex] = SetBit(val[SRPCECapabilityFlagsIndex], UnlimitedMaximumSIDDepthFlag) - } - if tlv.IsNAISupported { - val[SRPCECapabilityFlagsIndex] = SetBit(val[SRPCECapabilityFlagsIndex], NAISupportedFlag) - } + val[SRPCECapabilityFlagsIndex] = SetBit(val[SRPCECapabilityFlagsIndex], UnlimitedMaximumSIDDepthFlag, tlv.HasUnlimitedMaxSIDDepth) + val[SRPCECapabilityFlagsIndex] = SetBit(val[SRPCECapabilityFlagsIndex], NAISupportedFlag, tlv.IsNAISupported) val[SRPCECapabilityMSDIndex] = tlv.MaximumSidDepth buf = append(buf, val...) From 0e05e617b0d6a5d4ee9e104847bac8a3f611f7e9 Mon Sep 17 00:00:00 2001 From: watal Date: Fri, 18 Apr 2025 17:29:04 +0900 Subject: [PATCH 26/87] refactor(SymbolicPathName): add utf8 validation --- pkg/packet/pcep/tlv.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg/packet/pcep/tlv.go b/pkg/packet/pcep/tlv.go index b3649ded..392158aa 100644 --- a/pkg/packet/pcep/tlv.go +++ b/pkg/packet/pcep/tlv.go @@ -13,6 +13,7 @@ import ( "slices" "strconv" "strings" + "unicode/utf8" "go.uber.org/zap/zapcore" ) @@ -394,6 +395,10 @@ func (tlv *SymbolicPathName) DecodeFromBytes(data []byte) error { } tlv.Name = string(data[TLVHeaderLength:totalLength]) + if !utf8.Valid([]byte(tlv.Name)) { + return fmt.Errorf("invalid UTF-8 sequence in SymbolicPathName") + } + return nil } From a9b587f37ee1444f384907b0c29c320fc347c6a8 Mon Sep 17 00:00:00 2001 From: watal Date: Fri, 18 Apr 2025 17:35:04 +0900 Subject: [PATCH 27/87] refactor(pcep): improve serialization and decoding using pcep_utils --- pkg/packet/pcep/object.go | 7 +- pkg/packet/pcep/tlv.go | 130 +++++++++++++++----------------------- 2 files changed, 54 insertions(+), 83 deletions(-) diff --git a/pkg/packet/pcep/object.go b/pkg/packet/pcep/object.go index 012c76cd..9174dc85 100644 --- a/pkg/packet/pcep/object.go +++ b/pkg/packet/pcep/object.go @@ -198,9 +198,7 @@ func (h *CommonObjectHeader) Serialize() []uint8 { Flagbyte = Flagbyte | IFlagMask } buf = append(buf, Flagbyte) - objectLength := make([]uint8, 2) - binary.BigEndian.PutUint16(objectLength, h.ObjectLength) - buf = append(buf, objectLength...) + buf = append(buf, Uint16ToByteSlice(h.ObjectLength)...) return buf } @@ -1110,8 +1108,7 @@ func (o *SRv6EroSubobject) Serialize() []uint8 { buf[3] = buf[3] | 0x01 } reserved := make([]uint8, 2) - behavior := make([]uint8, 2) - binary.BigEndian.PutUint16(behavior, o.Segment.Behavior()) + behavior := Uint16ToByteSlice(o.Segment.Behavior()) byteSid := o.Segment.Sid.AsSlice() byteNAI := []uint8{} diff --git a/pkg/packet/pcep/tlv.go b/pkg/packet/pcep/tlv.go index 392158aa..858e367e 100644 --- a/pkg/packet/pcep/tlv.go +++ b/pkg/packet/pcep/tlv.go @@ -274,27 +274,18 @@ func (tlv *StatefulPCECapability) DecodeFromBytes(data []byte) error { return fmt.Errorf("data is too short: expected at least %d bytes, but got %d bytes for StatefulPCECapability", tlv.Len(), len(data)) } - flagByte := uint32(data[TLVHeaderLength+StatefulPCECapabilityFlagsIndex]) - tlv.LSPUpdateCapability = IsBitSet(flagByte, LSPUpdateCapabilityBit) - tlv.IncludeDBVersion = IsBitSet(flagByte, IncludeDBVersionCapabilityBit) - tlv.LSPInstantiationCapability = IsBitSet(flagByte, LSPInstantiationCapabilityBit) - tlv.TriggeredResync = IsBitSet(flagByte, TriggeredResyncCapabilityBit) - tlv.DeltaLSPSyncCapability = IsBitSet(flagByte, DeltaLSPSyncCapabilityBit) - tlv.TriggeredInitialSync = IsBitSet(flagByte, TriggeredInitialSyncBit) + flags := uint32(data[TLVHeaderLength+StatefulPCECapabilityFlagsIndex]) + tlv.ExtractCapabilities(flags) return nil } func (tlv *StatefulPCECapability) Serialize() []byte { - buf := make([]byte, 0, TLVHeaderLength+TLVStatefulPCECapabilityValueLength) - buf = append(buf, byte(tlv.Type()>>8), byte(tlv.Type())) - buf = append(buf, byte(TLVStatefulPCECapabilityValueLength>>8), byte(TLVStatefulPCECapabilityValueLength)) - - val := make([]byte, TLVStatefulPCECapabilityValueLength) - binary.BigEndian.PutUint32(val, tlv.CapabilityBits()) - buf = append(buf, val...) - - return buf + return AppendByteSlices( + Uint16ToByteSlice(tlv.Type()), + Uint16ToByteSlice(TLVStatefulPCECapabilityValueLength), + Uint32ToByteSlice(tlv.SetFlags()), + ) } func (tlv *StatefulPCECapability) MarshalLogObject(enc zapcore.ObjectEncoder) error { @@ -315,6 +306,26 @@ func (tlv *StatefulPCECapability) Len() uint16 { return TLVHeaderLength + TLVStatefulPCECapabilityValueLength } +func (tlv *StatefulPCECapability) ExtractCapabilities(flags uint32) { + tlv.LSPUpdateCapability = flags&LSPUpdateCapabilityBit != 0 + tlv.IncludeDBVersion = flags&IncludeDBVersionCapabilityBit != 0 + tlv.LSPInstantiationCapability = flags&LSPInstantiationCapabilityBit != 0 + tlv.TriggeredResync = flags&TriggeredResyncCapabilityBit != 0 + tlv.DeltaLSPSyncCapability = flags&DeltaLSPSyncCapabilityBit != 0 + tlv.TriggeredInitialSync = flags&TriggeredInitialSyncBit != 0 +} + +func (tlv *StatefulPCECapability) SetFlags() uint32 { + var flags uint32 + flags = SetBit(flags, LSPUpdateCapabilityBit, tlv.LSPUpdateCapability) + flags = SetBit(flags, IncludeDBVersionCapabilityBit, tlv.IncludeDBVersion) + flags = SetBit(flags, LSPInstantiationCapabilityBit, tlv.LSPInstantiationCapability) + flags = SetBit(flags, TriggeredResyncCapabilityBit, tlv.TriggeredResync) + flags = SetBit(flags, DeltaLSPSyncCapabilityBit, tlv.DeltaLSPSyncCapability) + flags = SetBit(flags, TriggeredInitialSyncBit, tlv.TriggeredInitialSync) + return flags +} + func (tlv *StatefulPCECapability) CapStrings() []string { ret := []string{"Stateful"} if tlv.LSPUpdateCapability { @@ -341,44 +352,12 @@ func (tlv *StatefulPCECapability) CapStrings() []string { return ret } -func (tlv *StatefulPCECapability) FromBits(bits uint32) { - tlv.LSPUpdateCapability = bits&LSPUpdateCapabilityBit != 0 - tlv.IncludeDBVersion = bits&IncludeDBVersionCapabilityBit != 0 - tlv.LSPInstantiationCapability = bits&LSPInstantiationCapabilityBit != 0 - tlv.TriggeredResync = bits&TriggeredResyncCapabilityBit != 0 - tlv.DeltaLSPSyncCapability = bits&DeltaLSPSyncCapabilityBit != 0 - tlv.TriggeredInitialSync = bits&TriggeredInitialSyncBit != 0 -} - -func NewStatefulPCECapability(bits uint32) *StatefulPCECapability { +func NewStatefulPCECapability(flags uint32) *StatefulPCECapability { tlv := &StatefulPCECapability{} - tlv.FromBits(bits) + tlv.ExtractCapabilities(flags) return tlv } -func (tlv *StatefulPCECapability) CapabilityBits() uint32 { - var flags uint32 - if tlv.LSPUpdateCapability { - flags = SetBit(flags, LSPUpdateCapabilityBit) - } - if tlv.IncludeDBVersion { - flags = SetBit(flags, IncludeDBVersionCapabilityBit) - } - if tlv.LSPInstantiationCapability { - flags = SetBit(flags, LSPInstantiationCapabilityBit) - } - if tlv.TriggeredResync { - flags = SetBit(flags, TriggeredResyncCapabilityBit) - } - if tlv.DeltaLSPSyncCapability { - flags = SetBit(flags, DeltaLSPSyncCapabilityBit) - } - if tlv.TriggeredInitialSync { - flags = SetBit(flags, TriggeredInitialSyncBit) - } - return flags -} - type SymbolicPathName struct { Name string } @@ -403,19 +382,20 @@ func (tlv *SymbolicPathName) DecodeFromBytes(data []byte) error { } func (tlv *SymbolicPathName) Serialize() []byte { + const alignment = 4 + nameLen := uint16(len(tlv.Name)) - padding := (4 - (nameLen % 4)) % 4 // Padding for 4-byte alignment + padding := (alignment - (nameLen % alignment)) % alignment // Padding for 4-byte alignment - buf := make([]byte, 0, TLVHeaderLength+int(nameLen)+int(padding)) - buf = append(buf, Uint16ToByteSlice(tlv.Type())...) - buf = append(buf, Uint16ToByteSlice(nameLen)...) - buf = append(buf, []byte(tlv.Name)...) + value := make([]byte, 0, int(nameLen)+int(padding)) + value = append(value, []byte(tlv.Name)...) + value = append(value, make([]byte, padding)...) - if padding > 0 { - buf = append(buf, make([]byte, padding)...) - } - - return buf + return AppendByteSlices( + Uint16ToByteSlice(tlv.Type()), + Uint16ToByteSlice(nameLen), + value, + ) } func (tlv *SymbolicPathName) Type() TLVType { @@ -478,26 +458,20 @@ func (tlv *IPv4LSPIdentifiers) DecodeFromBytes(data []byte) error { return nil } -func (tlv *IPv4LSPIdentifiers) Serialize() []uint8 { - buf := make([]uint8, 0, TLVHeaderLength+TLVIPv4LSPIdentifiersValueLength) +func (tlv *IPv4LSPIdentifiers) Serialize() []byte { + value := make([]byte, TLVIPv4LSPIdentifiersValueLength) - typ := make([]uint8, 2) - binary.BigEndian.PutUint16(typ, uint16(tlv.Type())) - buf = append(buf, typ...) + copy(value[0:4], tlv.IPv4TunnelSenderAddress.AsSlice()) + binary.BigEndian.PutUint16(value[4:6], tlv.LSPID) + binary.BigEndian.PutUint16(value[6:8], tlv.TunnelID) + binary.BigEndian.PutUint32(value[8:12], tlv.ExtendedTunnelID) + copy(value[12:16], tlv.IPv4TunnelEndpointAddress.AsSlice()) - length := make([]uint8, 2) - binary.BigEndian.PutUint16(length, TLVIPv4LSPIdentifiersValueLength) - buf = append(buf, length...) - - val := make([]byte, TLVIPv4LSPIdentifiersValueLength) - copy(val[0:4], tlv.IPv4TunnelSenderAddress.AsSlice()) - binary.BigEndian.PutUint16(val[4:6], tlv.LSPID) - binary.BigEndian.PutUint16(val[6:8], tlv.TunnelID) - binary.BigEndian.PutUint32(val[8:12], tlv.ExtendedTunnelID) - copy(val[12:16], tlv.IPv4TunnelEndpointAddress.AsSlice()) - buf = append(buf, val...) - - return buf + return AppendByteSlices( + Uint16ToByteSlice(tlv.Type()), + Uint16ToByteSlice(TLVIPv4LSPIdentifiersValueLength), + value, + ) } func (tlv *IPv4LSPIdentifiers) MarshalLogObject(enc zapcore.ObjectEncoder) error { From 112b3913f47665553074b4e6a44161d7855835ed Mon Sep 17 00:00:00 2001 From: k1yoto Date: Sat, 17 May 2025 00:44:20 +0000 Subject: [PATCH 28/87] feat(LsPrefixNLRI): merge LsPrefixV4NLRI and LsPrefixV6NLRI processing --- internal/pkg/gobgp/interface.go | 82 ++++++++++++++++++--------------- internal/pkg/table/ted.go | 10 ++-- 2 files changed, 50 insertions(+), 42 deletions(-) diff --git a/internal/pkg/gobgp/interface.go b/internal/pkg/gobgp/interface.go index d190020c..87afe20c 100644 --- a/internal/pkg/gobgp/interface.go +++ b/internal/pkg/gobgp/interface.go @@ -111,20 +111,18 @@ func ConvertToTEDElem(dst *api.Destination) ([]table.TEDElem, error) { return nil, fmt.Errorf("failed to process LS Link NLRI: %w", err) } return []table.TEDElem{lsLink}, nil - case *api.LsPrefixV4NLRI: - lsPrefixV4List, err := getLsPrefixV4List(path.GetPattrs()) + case *api.LsPrefixV4NLRI, *api.LsPrefixV6NLRI: + lsPrefixList, err := getLsPrefixList(path.GetPattrs()) if err != nil { return nil, fmt.Errorf("failed to process LS Prefix V4 NLRI: %w", err) } - return lsPrefixV4List, nil + return lsPrefixList, nil case *api.LsSrv6SIDNLRI: lsSrv6SIDList, err := getLsSrv6SIDNLRIList(path.GetPattrs()) if err != nil { return nil, fmt.Errorf("failed to process LS SRv6 SID NLRI: %w", err) } return lsSrv6SIDList, nil - case *api.LsPrefixV6NLRI: - return nil, nil // TODO: Implement LsPrefixV6NLRI handling default: return nil, fmt.Errorf("invalid LS Link State NLRI type: %T", linkStateNLRI) } @@ -219,71 +217,81 @@ func getLsLinkNLRI(typedLinkStateNLRI *api.LsLinkNLRI, pathAttrs []*anypb.Any) ( return lsLink, nil } -func getLsPrefixV4List(pathAttrs []*anypb.Any) ([]table.TEDElem, error) { - var lsPrefixV4List []table.TEDElem +func getLsPrefixList(pathAttrs []*anypb.Any) ([]table.TEDElem, error) { + var lsPrefixList []table.TEDElem var sidIndex uint32 for _, pathAttr := range pathAttrs { typedPathAttr, err := pathAttr.UnmarshalNew() if err != nil { - return nil, fmt.Errorf("failed to unmarshal path attribute: %w", err) + return nil, fmt.Errorf("failed to unmarshal path attribute: %v", err) } switch typedPathAttr := typedPathAttr.(type) { case *api.LsAttribute: - sidIndex = typedPathAttr.GetPrefix().GetSrPrefixSid() - + if typedPathAttr.GetPrefix().GetSrPrefixSid() != 0 { + sidIndex = typedPathAttr.GetPrefix().GetSrPrefixSid() + } else { + sidIndex = 0 + } case *api.MpReachNLRIAttribute: for _, nlri := range typedPathAttr.GetNlris() { - typedNLRI, err := nlri.UnmarshalNew() + typedNlri, err := nlri.UnmarshalNew() if err != nil { - return nil, fmt.Errorf("failed to unmarshal NLRI: %w", err) + return nil, fmt.Errorf("failed to unmarshal NLRI: %v", err) } - if lsNLRI, ok := typedNLRI.(*api.LsAddrPrefix); ok { - lsPrefixV4, err := getLsPrefixV4(lsNLRI, sidIndex) + if lsNlri, ok := typedNlri.(*api.LsAddrPrefix); ok { + lsPrefix, err := getLsPrefix(lsNlri, sidIndex) if err != nil { - return nil, fmt.Errorf("failed to get LS Prefix V4: %w", err) + return nil, fmt.Errorf("failed to get LS Prefix: %v", err) } - lsPrefixV4List = append(lsPrefixV4List, lsPrefixV4) - } else { - return nil, fmt.Errorf("unexpected NLRI type: %T", typedNLRI) + lsPrefixList = append(lsPrefixList, lsPrefix) } } } } - return lsPrefixV4List, nil + return lsPrefixList, nil } -func getLsPrefixV4(lsNLRI *api.LsAddrPrefix, sidIndex uint32) (*table.LsPrefixV4, error) { - prefixNLRI, err := lsNLRI.GetNlri().UnmarshalNew() +func getLsPrefix(lsNlri *api.LsAddrPrefix, sidIndex uint32) (*table.LsPrefix, error) { + nlri, err := lsNlri.GetNlri().UnmarshalNew() if err != nil { - return nil, fmt.Errorf("failed to unmarshal LS Prefix V4: %w", err) - } - prefixv4NLRI, ok := prefixNLRI.(*api.LsPrefixV4NLRI) - if !ok { - return nil, fmt.Errorf("invalid LS prefix v4 NLRI type: %T", prefixNLRI) + return nil, fmt.Errorf("failed to unmarshal LS Prefix V4: %v", err) } - localNodeID := prefixv4NLRI.GetLocalNode().GetIgpRouterId() - localNodeASN := prefixv4NLRI.GetLocalNode().GetAsn() - prefixV4 := prefixv4NLRI.GetPrefixDescriptor().GetIpReachability() + var localNodeID string + var localNodeAsn uint32 + var prefix []string + + switch prefNlri := nlri.(type) { + case *api.LsPrefixV4NLRI: + localNodeID = prefNlri.GetLocalNode().GetIgpRouterId() + localNodeAsn = prefNlri.GetLocalNode().GetAsn() + prefix = prefNlri.GetPrefixDescriptor().GetIpReachability() + case *api.LsPrefixV6NLRI: + localNodeID = prefNlri.GetLocalNode().GetIgpRouterId() + localNodeAsn = prefNlri.GetLocalNode().GetAsn() + prefix = prefNlri.GetPrefixDescriptor().GetIpReachability() + default: + return nil, errors.New("invalid LS prefix NLRI type") + } - localNode := table.NewLsNode(localNodeASN, localNodeID) - lsPrefixV4 := table.NewLsPrefixV4(localNode) - lsPrefixV4.SidIndex = sidIndex + localNode := table.NewLsNode(localNodeAsn, localNodeID) + lsPrefix := table.NewLsPrefix(localNode) + lsPrefix.SidIndex = sidIndex - if len(prefixV4) != 1 { - return nil, fmt.Errorf("invalid prefix length: expected 1, got %d", len(prefixV4)) + if len(prefix) != 1 { + return nil, errors.New("invalid prefix length: expected 1 prefix") } - lsPrefixV4.Prefix, err = netip.ParsePrefix(prefixV4[0]) + lsPrefix.Prefix, err = netip.ParsePrefix(prefix[0]) if err != nil { - return nil, fmt.Errorf("failed to parse prefix %q: %w", prefixV4[0], err) + return nil, fmt.Errorf("failed to parse prefix: %v", err) } - return lsPrefixV4, nil + return lsPrefix, nil } func getLsSrv6SIDNLRIList(pathAttrs []*anypb.Any) ([]table.TEDElem, error) { diff --git a/internal/pkg/table/ted.go b/internal/pkg/table/ted.go index 51bd705f..07d10f4f 100644 --- a/internal/pkg/table/ted.go +++ b/internal/pkg/table/ted.go @@ -74,7 +74,7 @@ type LsNode struct { SrgbBegin uint32 // in BGP-LS Attr SrgbEnd uint32 // in BGP-LS Attr Links []*LsLink - Prefixes []*LsPrefixV4 + Prefixes []*LsPrefix SRv6SIDs []*LsSrv6SID } @@ -179,19 +179,19 @@ func (l *LsLink) UpdateTED(ted *LsTED) { l.LocalNode.AddLink(l) } -type LsPrefixV4 struct { +type LsPrefix struct { LocalNode *LsNode // primary key, in MP_REACH_NLRI Attr Prefix netip.Prefix // in MP_REACH_NLRI Attr SidIndex uint32 // in BGP-LS Attr (only for Lo Address Prefix) } -func NewLsPrefixV4(localNode *LsNode) *LsPrefixV4 { - return &LsPrefixV4{ +func NewLsPrefix(localNode *LsNode) *LsPrefix { + return &LsPrefix{ LocalNode: localNode, } } -func (lp *LsPrefixV4) UpdateTED(ted *LsTED) { +func (lp *LsPrefix) UpdateTED(ted *LsTED) { nodes, asn := ted.Nodes, lp.LocalNode.ASN if _, ok := nodes[asn]; !ok { From ff67e9a47b5ff94e50b8b7ad8cc8b9d3c86f0e94 Mon Sep 17 00:00:00 2001 From: k1yoto Date: Sat, 17 May 2025 00:48:52 +0000 Subject: [PATCH 29/87] feat(grpc): support LsSRv6SIDNLRI and LsPrefixV6NLRI --- api/pola/v1/pola.pb.go | 687 +++++++++++++++++++++++------------ api/pola/v1/pola.proto | 15 + cmd/pola/grpc/grpc_client.go | 30 +- pkg/server/grpc_server.go | 22 ++ 4 files changed, 521 insertions(+), 233 deletions(-) diff --git a/api/pola/v1/pola.pb.go b/api/pola/v1/pola.pb.go index c56ab5d0..27e62d8e 100644 --- a/api/pola/v1/pola.pb.go +++ b/api/pola/v1/pola.pb.go @@ -753,6 +753,163 @@ func (x *SRPolicyList) GetSrPolicies() []*SRPolicy { return nil } +type LsSrv6SID struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Sids []*SID `protobuf:"bytes,1,rep,name=sids,proto3" json:"sids,omitempty"` + EndpointBehavior uint32 `protobuf:"varint,2,opt,name=endpoint_behavior,json=endpointBehavior,proto3" json:"endpoint_behavior,omitempty"` + MultiTopoIds []*MultiTopoID `protobuf:"bytes,3,rep,name=multi_topo_ids,json=multiTopoIds,proto3" json:"multi_topo_ids,omitempty"` +} + +func (x *LsSrv6SID) Reset() { + *x = LsSrv6SID{} + if protoimpl.UnsafeEnabled { + mi := &file_pola_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LsSrv6SID) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LsSrv6SID) ProtoMessage() {} + +func (x *LsSrv6SID) ProtoReflect() protoreflect.Message { + mi := &file_pola_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LsSrv6SID.ProtoReflect.Descriptor instead. +func (*LsSrv6SID) Descriptor() ([]byte, []int) { + return file_pola_proto_rawDescGZIP(), []int{9} +} + +func (x *LsSrv6SID) GetSids() []*SID { + if x != nil { + return x.Sids + } + return nil +} + +func (x *LsSrv6SID) GetEndpointBehavior() uint32 { + if x != nil { + return x.EndpointBehavior + } + return 0 +} + +func (x *LsSrv6SID) GetMultiTopoIds() []*MultiTopoID { + if x != nil { + return x.MultiTopoIds + } + return nil +} + +type SID struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Sid string `protobuf:"bytes,1,opt,name=sid,proto3" json:"sid,omitempty"` +} + +func (x *SID) Reset() { + *x = SID{} + if protoimpl.UnsafeEnabled { + mi := &file_pola_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SID) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SID) ProtoMessage() {} + +func (x *SID) ProtoReflect() protoreflect.Message { + mi := &file_pola_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SID.ProtoReflect.Descriptor instead. +func (*SID) Descriptor() ([]byte, []int) { + return file_pola_proto_rawDescGZIP(), []int{10} +} + +func (x *SID) GetSid() string { + if x != nil { + return x.Sid + } + return "" +} + +type MultiTopoID struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + MultiTopoId uint32 `protobuf:"varint,1,opt,name=multi_topo_id,json=multiTopoId,proto3" json:"multi_topo_id,omitempty"` +} + +func (x *MultiTopoID) Reset() { + *x = MultiTopoID{} + if protoimpl.UnsafeEnabled { + mi := &file_pola_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MultiTopoID) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MultiTopoID) ProtoMessage() {} + +func (x *MultiTopoID) ProtoReflect() protoreflect.Message { + mi := &file_pola_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MultiTopoID.ProtoReflect.Descriptor instead. +func (*MultiTopoID) Descriptor() ([]byte, []int) { + return file_pola_proto_rawDescGZIP(), []int{11} +} + +func (x *MultiTopoID) GetMultiTopoId() uint32 { + if x != nil { + return x.MultiTopoId + } + return 0 +} + type LsPrefix struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -765,7 +922,7 @@ type LsPrefix struct { func (x *LsPrefix) Reset() { *x = LsPrefix{} if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[9] + mi := &file_pola_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -778,7 +935,7 @@ func (x *LsPrefix) String() string { func (*LsPrefix) ProtoMessage() {} func (x *LsPrefix) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[9] + mi := &file_pola_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -791,7 +948,7 @@ func (x *LsPrefix) ProtoReflect() protoreflect.Message { // Deprecated: Use LsPrefix.ProtoReflect.Descriptor instead. func (*LsPrefix) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{9} + return file_pola_proto_rawDescGZIP(), []int{12} } func (x *LsPrefix) GetPrefix() string { @@ -820,7 +977,7 @@ type Metric struct { func (x *Metric) Reset() { *x = Metric{} if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[10] + mi := &file_pola_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -833,7 +990,7 @@ func (x *Metric) String() string { func (*Metric) ProtoMessage() {} func (x *Metric) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[10] + mi := &file_pola_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -846,7 +1003,7 @@ func (x *Metric) ProtoReflect() protoreflect.Message { // Deprecated: Use Metric.ProtoReflect.Descriptor instead. func (*Metric) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{10} + return file_pola_proto_rawDescGZIP(), []int{13} } func (x *Metric) GetType() MetricType { @@ -881,7 +1038,7 @@ type LsLink struct { func (x *LsLink) Reset() { *x = LsLink{} if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[11] + mi := &file_pola_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -894,7 +1051,7 @@ func (x *LsLink) String() string { func (*LsLink) ProtoMessage() {} func (x *LsLink) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[11] + mi := &file_pola_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -907,7 +1064,7 @@ func (x *LsLink) ProtoReflect() protoreflect.Message { // Deprecated: Use LsLink.ProtoReflect.Descriptor instead. func (*LsLink) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{11} + return file_pola_proto_rawDescGZIP(), []int{14} } func (x *LsLink) GetLocalRouterId() string { @@ -971,20 +1128,21 @@ type LsNode struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Asn uint32 `protobuf:"varint,1,opt,name=asn,proto3" json:"asn,omitempty"` - RouterId string `protobuf:"bytes,2,opt,name=router_id,json=routerId,proto3" json:"router_id,omitempty"` - IsisAreaId string `protobuf:"bytes,3,opt,name=isis_area_id,json=isisAreaId,proto3" json:"isis_area_id,omitempty"` - Hostname string `protobuf:"bytes,4,opt,name=hostname,proto3" json:"hostname,omitempty"` - SrgbBegin uint32 `protobuf:"varint,5,opt,name=srgb_begin,json=srgbBegin,proto3" json:"srgb_begin,omitempty"` - SrgbEnd uint32 `protobuf:"varint,6,opt,name=srgb_end,json=srgbEnd,proto3" json:"srgb_end,omitempty"` - LsLinks []*LsLink `protobuf:"bytes,7,rep,name=ls_links,json=lsLinks,proto3" json:"ls_links,omitempty"` - LsPrefixes []*LsPrefix `protobuf:"bytes,8,rep,name=ls_prefixes,json=lsPrefixes,proto3" json:"ls_prefixes,omitempty"` + Asn uint32 `protobuf:"varint,1,opt,name=asn,proto3" json:"asn,omitempty"` + RouterId string `protobuf:"bytes,2,opt,name=router_id,json=routerId,proto3" json:"router_id,omitempty"` + IsisAreaId string `protobuf:"bytes,3,opt,name=isis_area_id,json=isisAreaId,proto3" json:"isis_area_id,omitempty"` + Hostname string `protobuf:"bytes,4,opt,name=hostname,proto3" json:"hostname,omitempty"` + SrgbBegin uint32 `protobuf:"varint,5,opt,name=srgb_begin,json=srgbBegin,proto3" json:"srgb_begin,omitempty"` + SrgbEnd uint32 `protobuf:"varint,6,opt,name=srgb_end,json=srgbEnd,proto3" json:"srgb_end,omitempty"` + LsLinks []*LsLink `protobuf:"bytes,7,rep,name=ls_links,json=lsLinks,proto3" json:"ls_links,omitempty"` + LsPrefixes []*LsPrefix `protobuf:"bytes,8,rep,name=ls_prefixes,json=lsPrefixes,proto3" json:"ls_prefixes,omitempty"` + LsSrv6Sids []*LsSrv6SID `protobuf:"bytes,9,rep,name=ls_srv6_sids,json=lsSrv6Sids,proto3" json:"ls_srv6_sids,omitempty"` } func (x *LsNode) Reset() { *x = LsNode{} if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[12] + mi := &file_pola_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -997,7 +1155,7 @@ func (x *LsNode) String() string { func (*LsNode) ProtoMessage() {} func (x *LsNode) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[12] + mi := &file_pola_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1010,7 +1168,7 @@ func (x *LsNode) ProtoReflect() protoreflect.Message { // Deprecated: Use LsNode.ProtoReflect.Descriptor instead. func (*LsNode) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{12} + return file_pola_proto_rawDescGZIP(), []int{15} } func (x *LsNode) GetAsn() uint32 { @@ -1069,6 +1227,13 @@ func (x *LsNode) GetLsPrefixes() []*LsPrefix { return nil } +func (x *LsNode) GetLsSrv6Sids() []*LsSrv6SID { + if x != nil { + return x.LsSrv6Sids + } + return nil +} + type TED struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1081,7 +1246,7 @@ type TED struct { func (x *TED) Reset() { *x = TED{} if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[13] + mi := &file_pola_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1094,7 +1259,7 @@ func (x *TED) String() string { func (*TED) ProtoMessage() {} func (x *TED) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[13] + mi := &file_pola_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1107,7 +1272,7 @@ func (x *TED) ProtoReflect() protoreflect.Message { // Deprecated: Use TED.ProtoReflect.Descriptor instead. func (*TED) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{13} + return file_pola_proto_rawDescGZIP(), []int{16} } func (x *TED) GetEnable() bool { @@ -1133,7 +1298,7 @@ type GetSessionListRequest struct { func (x *GetSessionListRequest) Reset() { *x = GetSessionListRequest{} if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[14] + mi := &file_pola_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1146,7 +1311,7 @@ func (x *GetSessionListRequest) String() string { func (*GetSessionListRequest) ProtoMessage() {} func (x *GetSessionListRequest) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[14] + mi := &file_pola_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1159,7 +1324,7 @@ func (x *GetSessionListRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSessionListRequest.ProtoReflect.Descriptor instead. func (*GetSessionListRequest) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{14} + return file_pola_proto_rawDescGZIP(), []int{17} } type GetSessionListResponse struct { @@ -1173,7 +1338,7 @@ type GetSessionListResponse struct { func (x *GetSessionListResponse) Reset() { *x = GetSessionListResponse{} if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[15] + mi := &file_pola_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1186,7 +1351,7 @@ func (x *GetSessionListResponse) String() string { func (*GetSessionListResponse) ProtoMessage() {} func (x *GetSessionListResponse) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[15] + mi := &file_pola_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1199,7 +1364,7 @@ func (x *GetSessionListResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSessionListResponse.ProtoReflect.Descriptor instead. func (*GetSessionListResponse) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{15} + return file_pola_proto_rawDescGZIP(), []int{18} } func (x *GetSessionListResponse) GetSessions() []*Session { @@ -1218,7 +1383,7 @@ type GetSRPolicyListRequest struct { func (x *GetSRPolicyListRequest) Reset() { *x = GetSRPolicyListRequest{} if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[16] + mi := &file_pola_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1231,7 +1396,7 @@ func (x *GetSRPolicyListRequest) String() string { func (*GetSRPolicyListRequest) ProtoMessage() {} func (x *GetSRPolicyListRequest) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[16] + mi := &file_pola_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1244,7 +1409,7 @@ func (x *GetSRPolicyListRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSRPolicyListRequest.ProtoReflect.Descriptor instead. func (*GetSRPolicyListRequest) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{16} + return file_pola_proto_rawDescGZIP(), []int{19} } type GetSRPolicyListResponse struct { @@ -1258,7 +1423,7 @@ type GetSRPolicyListResponse struct { func (x *GetSRPolicyListResponse) Reset() { *x = GetSRPolicyListResponse{} if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[17] + mi := &file_pola_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1271,7 +1436,7 @@ func (x *GetSRPolicyListResponse) String() string { func (*GetSRPolicyListResponse) ProtoMessage() {} func (x *GetSRPolicyListResponse) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[17] + mi := &file_pola_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1284,7 +1449,7 @@ func (x *GetSRPolicyListResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSRPolicyListResponse.ProtoReflect.Descriptor instead. func (*GetSRPolicyListResponse) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{17} + return file_pola_proto_rawDescGZIP(), []int{20} } func (x *GetSRPolicyListResponse) GetSrPolicies() []*SRPolicy { @@ -1303,7 +1468,7 @@ type GetTEDRequest struct { func (x *GetTEDRequest) Reset() { *x = GetTEDRequest{} if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[18] + mi := &file_pola_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1316,7 +1481,7 @@ func (x *GetTEDRequest) String() string { func (*GetTEDRequest) ProtoMessage() {} func (x *GetTEDRequest) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[18] + mi := &file_pola_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1329,7 +1494,7 @@ func (x *GetTEDRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetTEDRequest.ProtoReflect.Descriptor instead. func (*GetTEDRequest) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{18} + return file_pola_proto_rawDescGZIP(), []int{21} } type GetTEDResponse struct { @@ -1344,7 +1509,7 @@ type GetTEDResponse struct { func (x *GetTEDResponse) Reset() { *x = GetTEDResponse{} if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[19] + mi := &file_pola_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1357,7 +1522,7 @@ func (x *GetTEDResponse) String() string { func (*GetTEDResponse) ProtoMessage() {} func (x *GetTEDResponse) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[19] + mi := &file_pola_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1370,7 +1535,7 @@ func (x *GetTEDResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetTEDResponse.ProtoReflect.Descriptor instead. func (*GetTEDResponse) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{19} + return file_pola_proto_rawDescGZIP(), []int{22} } func (x *GetTEDResponse) GetEnable() bool { @@ -1398,7 +1563,7 @@ type DeleteSessionRequest struct { func (x *DeleteSessionRequest) Reset() { *x = DeleteSessionRequest{} if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[20] + mi := &file_pola_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1411,7 +1576,7 @@ func (x *DeleteSessionRequest) String() string { func (*DeleteSessionRequest) ProtoMessage() {} func (x *DeleteSessionRequest) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[20] + mi := &file_pola_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1424,7 +1589,7 @@ func (x *DeleteSessionRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteSessionRequest.ProtoReflect.Descriptor instead. func (*DeleteSessionRequest) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{20} + return file_pola_proto_rawDescGZIP(), []int{23} } func (x *DeleteSessionRequest) GetAddr() []byte { @@ -1445,7 +1610,7 @@ type DeleteSessionResponse struct { func (x *DeleteSessionResponse) Reset() { *x = DeleteSessionResponse{} if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[21] + mi := &file_pola_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1458,7 +1623,7 @@ func (x *DeleteSessionResponse) String() string { func (*DeleteSessionResponse) ProtoMessage() {} func (x *DeleteSessionResponse) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[21] + mi := &file_pola_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1471,7 +1636,7 @@ func (x *DeleteSessionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteSessionResponse.ProtoReflect.Descriptor instead. func (*DeleteSessionResponse) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{21} + return file_pola_proto_rawDescGZIP(), []int{24} } func (x *DeleteSessionResponse) GetIsSuccess() bool { @@ -1558,139 +1723,157 @@ var file_pola_proto_rawDesc = []byte{ 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0a, 0x73, 0x72, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, - 0x73, 0x22, 0x3f, 0x0a, 0x08, 0x4c, 0x73, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x16, 0x0a, - 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, - 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x69, 0x64, 0x5f, 0x69, 0x6e, 0x64, - 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x73, 0x69, 0x64, 0x49, 0x6e, 0x64, - 0x65, 0x78, 0x22, 0x4b, 0x0a, 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x2b, 0x0a, 0x04, - 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, - 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, - 0x96, 0x02, 0x0a, 0x06, 0x4c, 0x73, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x26, 0x0a, 0x0f, 0x6c, 0x6f, - 0x63, 0x61, 0x6c, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, - 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x61, 0x73, 0x6e, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x41, 0x73, 0x6e, 0x12, - 0x19, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x70, 0x12, 0x28, 0x0a, 0x10, 0x72, 0x65, - 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, - 0x65, 0x72, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x61, - 0x73, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, - 0x41, 0x73, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x69, 0x70, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x70, - 0x12, 0x2d, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, - 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, - 0x17, 0x0a, 0x07, 0x61, 0x64, 0x6a, 0x5f, 0x73, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x06, 0x61, 0x64, 0x6a, 0x53, 0x69, 0x64, 0x22, 0x97, 0x02, 0x0a, 0x06, 0x4c, 0x73, 0x4e, - 0x6f, 0x64, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x73, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x03, 0x61, 0x73, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x5f, - 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, - 0x49, 0x64, 0x12, 0x20, 0x0a, 0x0c, 0x69, 0x73, 0x69, 0x73, 0x5f, 0x61, 0x72, 0x65, 0x61, 0x5f, - 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x73, 0x69, 0x73, 0x41, 0x72, - 0x65, 0x61, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x72, 0x67, 0x62, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x73, 0x72, 0x67, 0x62, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x12, - 0x19, 0x0a, 0x08, 0x73, 0x72, 0x67, 0x62, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x07, 0x73, 0x72, 0x67, 0x62, 0x45, 0x6e, 0x64, 0x12, 0x2e, 0x0a, 0x08, 0x6c, 0x73, - 0x5f, 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x73, 0x4c, 0x69, 0x6e, - 0x6b, 0x52, 0x07, 0x6c, 0x73, 0x4c, 0x69, 0x6e, 0x6b, 0x73, 0x12, 0x36, 0x0a, 0x0b, 0x6c, 0x73, - 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x73, - 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x52, 0x0a, 0x6c, 0x73, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, - 0x65, 0x73, 0x22, 0x4d, 0x0a, 0x03, 0x54, 0x45, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61, - 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x12, 0x2e, 0x0a, 0x08, 0x6c, 0x73, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, - 0x31, 0x2e, 0x4c, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x07, 0x6c, 0x73, 0x4e, 0x6f, 0x64, 0x65, - 0x73, 0x22, 0x17, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, - 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x4a, 0x0a, 0x16, 0x47, 0x65, - 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, - 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x73, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x18, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x53, 0x52, 0x50, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x22, 0x51, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, - 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x0b, 0x73, - 0x72, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, - 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0a, 0x73, 0x72, 0x50, 0x6f, 0x6c, 0x69, 0x63, - 0x69, 0x65, 0x73, 0x22, 0x0f, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x54, 0x45, 0x44, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x22, 0x58, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x54, 0x45, 0x44, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x2e, - 0x0a, 0x08, 0x6c, 0x73, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, - 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x07, 0x6c, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x2a, - 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x22, 0x36, 0x0a, 0x15, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x53, 0x75, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x2a, 0x67, 0x0a, 0x0c, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x79, - 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x1a, 0x53, 0x52, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, - 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, - 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x52, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, - 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x58, 0x50, 0x4c, 0x49, 0x43, 0x49, 0x54, 0x10, 0x01, 0x12, - 0x1a, 0x0a, 0x16, 0x53, 0x52, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x54, 0x59, 0x50, - 0x45, 0x5f, 0x44, 0x59, 0x4e, 0x41, 0x4d, 0x49, 0x43, 0x10, 0x02, 0x2a, 0x5b, 0x0a, 0x0c, 0x53, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1d, 0x0a, 0x19, 0x53, - 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x53, - 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x53, 0x45, - 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x44, 0x4f, 0x57, 0x4e, - 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, - 0x41, 0x54, 0x45, 0x5f, 0x55, 0x50, 0x10, 0x02, 0x2a, 0x83, 0x01, 0x0a, 0x0a, 0x4d, 0x65, 0x74, - 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x17, 0x4d, 0x45, 0x54, 0x52, 0x49, - 0x43, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, - 0x45, 0x44, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x54, - 0x59, 0x50, 0x45, 0x5f, 0x49, 0x47, 0x50, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x4d, 0x45, 0x54, - 0x52, 0x49, 0x43, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x54, 0x45, 0x10, 0x02, 0x12, 0x15, 0x0a, - 0x11, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x45, 0x4c, - 0x41, 0x59, 0x10, 0x03, 0x12, 0x18, 0x0a, 0x14, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x54, - 0x59, 0x50, 0x45, 0x5f, 0x48, 0x4f, 0x50, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x04, 0x32, 0x96, - 0x04, 0x0a, 0x0a, 0x50, 0x43, 0x45, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x59, 0x0a, - 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, - 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, - 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x59, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, + 0x73, 0x22, 0x9e, 0x01, 0x0a, 0x09, 0x4c, 0x73, 0x53, 0x72, 0x76, 0x36, 0x53, 0x49, 0x44, 0x12, + 0x24, 0x0a, 0x04, 0x73, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x49, 0x44, 0x52, + 0x04, 0x73, 0x69, 0x64, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, + 0x74, 0x5f, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x10, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, + 0x6f, 0x72, 0x12, 0x3e, 0x0a, 0x0e, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x5f, 0x74, 0x6f, 0x70, 0x6f, + 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x54, 0x6f, + 0x70, 0x6f, 0x49, 0x44, 0x52, 0x0c, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x54, 0x6f, 0x70, 0x6f, 0x49, + 0x64, 0x73, 0x22, 0x17, 0x0a, 0x03, 0x53, 0x49, 0x44, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x73, 0x69, 0x64, 0x22, 0x31, 0x0a, 0x0b, 0x4d, + 0x75, 0x6c, 0x74, 0x69, 0x54, 0x6f, 0x70, 0x6f, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x75, + 0x6c, 0x74, 0x69, 0x5f, 0x74, 0x6f, 0x70, 0x6f, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x0b, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x54, 0x6f, 0x70, 0x6f, 0x49, 0x64, 0x22, 0x3f, + 0x0a, 0x08, 0x4c, 0x73, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, + 0x65, 0x66, 0x69, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, + 0x69, 0x78, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x69, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x73, 0x69, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, + 0x4b, 0x0a, 0x06, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x12, 0x2b, 0x0a, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, + 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, + 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x96, 0x02, 0x0a, + 0x06, 0x4c, 0x73, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x26, 0x0a, 0x0f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, + 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, + 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x61, 0x73, 0x6e, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x41, 0x73, 0x6e, 0x12, 0x19, 0x0a, 0x08, + 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x70, 0x12, 0x28, 0x0a, 0x10, 0x72, 0x65, 0x6d, 0x6f, 0x74, + 0x65, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x49, + 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x61, 0x73, 0x6e, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x73, 0x6e, + 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x69, 0x70, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x70, 0x12, 0x2d, 0x0a, + 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, + 0x72, 0x69, 0x63, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x17, 0x0a, 0x07, + 0x61, 0x64, 0x6a, 0x5f, 0x73, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x61, + 0x64, 0x6a, 0x53, 0x69, 0x64, 0x22, 0xd1, 0x02, 0x0a, 0x06, 0x4c, 0x73, 0x4e, 0x6f, 0x64, 0x65, + 0x12, 0x10, 0x0a, 0x03, 0x61, 0x73, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x61, + 0x73, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, + 0x20, 0x0a, 0x0c, 0x69, 0x73, 0x69, 0x73, 0x5f, 0x61, 0x72, 0x65, 0x61, 0x5f, 0x69, 0x64, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x73, 0x69, 0x73, 0x41, 0x72, 0x65, 0x61, 0x49, + 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, + 0x0a, 0x73, 0x72, 0x67, 0x62, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x09, 0x73, 0x72, 0x67, 0x62, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x12, 0x19, 0x0a, 0x08, + 0x73, 0x72, 0x67, 0x62, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, + 0x73, 0x72, 0x67, 0x62, 0x45, 0x6e, 0x64, 0x12, 0x2e, 0x0a, 0x08, 0x6c, 0x73, 0x5f, 0x6c, 0x69, + 0x6e, 0x6b, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x73, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x07, + 0x6c, 0x73, 0x4c, 0x69, 0x6e, 0x6b, 0x73, 0x12, 0x36, 0x0a, 0x0b, 0x6c, 0x73, 0x5f, 0x70, 0x72, + 0x65, 0x66, 0x69, 0x78, 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x73, 0x50, 0x72, 0x65, + 0x66, 0x69, 0x78, 0x52, 0x0a, 0x6c, 0x73, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, 0x73, 0x12, + 0x38, 0x0a, 0x0c, 0x6c, 0x73, 0x5f, 0x73, 0x72, 0x76, 0x36, 0x5f, 0x73, 0x69, 0x64, 0x73, 0x18, + 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, + 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x73, 0x53, 0x72, 0x76, 0x36, 0x53, 0x49, 0x44, 0x52, 0x0a, 0x6c, + 0x73, 0x53, 0x72, 0x76, 0x36, 0x53, 0x69, 0x64, 0x73, 0x22, 0x4d, 0x0a, 0x03, 0x54, 0x45, 0x44, + 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x2e, 0x0a, 0x08, 0x6c, 0x73, 0x5f, 0x6e, + 0x6f, 0x64, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x52, + 0x07, 0x6c, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x17, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x53, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x22, 0x4a, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, + 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x08, 0x73, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x18, 0x0a, + 0x16, 0x47, 0x65, 0x74, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69, 0x73, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x51, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x53, 0x52, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x36, 0x0a, 0x0b, 0x73, 0x72, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, + 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0a, + 0x73, 0x72, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x22, 0x0f, 0x0a, 0x0d, 0x47, 0x65, + 0x74, 0x54, 0x45, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x58, 0x0a, 0x0e, 0x47, + 0x65, 0x74, 0x54, 0x45, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, + 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x65, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x2e, 0x0a, 0x08, 0x6c, 0x73, 0x5f, 0x6e, 0x6f, 0x64, 0x65, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, + 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x07, 0x6c, 0x73, + 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x2a, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, + 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x61, 0x64, 0x64, + 0x72, 0x22, 0x36, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, + 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, + 0x69, 0x73, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x2a, 0x67, 0x0a, 0x0c, 0x53, 0x52, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x1a, 0x53, 0x52, 0x5f, + 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, + 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x52, 0x5f, + 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x58, 0x50, 0x4c, + 0x49, 0x43, 0x49, 0x54, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x53, 0x52, 0x5f, 0x50, 0x4f, 0x4c, + 0x49, 0x43, 0x59, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x59, 0x4e, 0x41, 0x4d, 0x49, 0x43, + 0x10, 0x02, 0x2a, 0x5b, 0x0a, 0x0c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x12, 0x1d, 0x0a, 0x19, 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, + 0x41, 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, + 0x00, 0x12, 0x16, 0x0a, 0x12, 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, + 0x54, 0x45, 0x5f, 0x44, 0x4f, 0x57, 0x4e, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x53, 0x45, 0x53, + 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55, 0x50, 0x10, 0x02, 0x2a, + 0x83, 0x01, 0x0a, 0x0a, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, + 0x0a, 0x17, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, + 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x4d, + 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x47, 0x50, 0x10, 0x01, + 0x12, 0x12, 0x0a, 0x0e, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x54, 0x45, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x54, + 0x59, 0x50, 0x45, 0x5f, 0x44, 0x45, 0x4c, 0x41, 0x59, 0x10, 0x03, 0x12, 0x18, 0x0a, 0x14, 0x4d, + 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x48, 0x4f, 0x50, 0x43, 0x4f, + 0x55, 0x4e, 0x54, 0x10, 0x04, 0x32, 0x96, 0x04, 0x0a, 0x0a, 0x50, 0x43, 0x45, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x12, 0x59, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x52, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, + 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x52, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, + 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x59, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, + 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x59, 0x0a, 0x0e, 0x47, 0x65, + 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x22, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x53, 0x52, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, + 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, + 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x06, 0x47, 0x65, 0x74, 0x54, 0x45, 0x44, 0x12, 0x1a, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, + 0x45, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x45, 0x44, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, + 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, - 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x59, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, - 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, - 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69, 0x73, - 0x74, 0x12, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, - 0x47, 0x65, 0x74, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, - 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, - 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x06, - 0x47, 0x65, 0x74, 0x54, 0x45, 0x44, 0x12, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, - 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x45, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, - 0x2e, 0x47, 0x65, 0x74, 0x54, 0x45, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x56, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x12, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, - 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x24, 0x5a, 0x22, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6e, 0x74, 0x74, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6f, 0x6c, - 0x61, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x6f, 0x6c, 0x61, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x24, + 0x5a, 0x22, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6e, 0x74, 0x74, + 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6f, 0x6c, 0x61, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x6f, 0x6c, + 0x61, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1706,7 +1889,7 @@ func file_pola_proto_rawDescGZIP() []byte { } var file_pola_proto_enumTypes = make([]protoimpl.EnumInfo, 3) -var file_pola_proto_msgTypes = make([]protoimpl.MessageInfo, 22) +var file_pola_proto_msgTypes = make([]protoimpl.MessageInfo, 25) var file_pola_proto_goTypes = []interface{}{ (SRPolicyType)(0), // 0: api.pola.v1.SRPolicyType (SessionState)(0), // 1: api.pola.v1.SessionState @@ -1720,19 +1903,22 @@ var file_pola_proto_goTypes = []interface{}{ (*Session)(nil), // 9: api.pola.v1.Session (*SessionList)(nil), // 10: api.pola.v1.SessionList (*SRPolicyList)(nil), // 11: api.pola.v1.SRPolicyList - (*LsPrefix)(nil), // 12: api.pola.v1.LsPrefix - (*Metric)(nil), // 13: api.pola.v1.Metric - (*LsLink)(nil), // 14: api.pola.v1.LsLink - (*LsNode)(nil), // 15: api.pola.v1.LsNode - (*TED)(nil), // 16: api.pola.v1.TED - (*GetSessionListRequest)(nil), // 17: api.pola.v1.GetSessionListRequest - (*GetSessionListResponse)(nil), // 18: api.pola.v1.GetSessionListResponse - (*GetSRPolicyListRequest)(nil), // 19: api.pola.v1.GetSRPolicyListRequest - (*GetSRPolicyListResponse)(nil), // 20: api.pola.v1.GetSRPolicyListResponse - (*GetTEDRequest)(nil), // 21: api.pola.v1.GetTEDRequest - (*GetTEDResponse)(nil), // 22: api.pola.v1.GetTEDResponse - (*DeleteSessionRequest)(nil), // 23: api.pola.v1.DeleteSessionRequest - (*DeleteSessionResponse)(nil), // 24: api.pola.v1.DeleteSessionResponse + (*LsSrv6SID)(nil), // 12: api.pola.v1.LsSrv6SID + (*SID)(nil), // 13: api.pola.v1.SID + (*MultiTopoID)(nil), // 14: api.pola.v1.MultiTopoID + (*LsPrefix)(nil), // 15: api.pola.v1.LsPrefix + (*Metric)(nil), // 16: api.pola.v1.Metric + (*LsLink)(nil), // 17: api.pola.v1.LsLink + (*LsNode)(nil), // 18: api.pola.v1.LsNode + (*TED)(nil), // 19: api.pola.v1.TED + (*GetSessionListRequest)(nil), // 20: api.pola.v1.GetSessionListRequest + (*GetSessionListResponse)(nil), // 21: api.pola.v1.GetSessionListResponse + (*GetSRPolicyListRequest)(nil), // 22: api.pola.v1.GetSRPolicyListRequest + (*GetSRPolicyListResponse)(nil), // 23: api.pola.v1.GetSRPolicyListResponse + (*GetTEDRequest)(nil), // 24: api.pola.v1.GetTEDRequest + (*GetTEDResponse)(nil), // 25: api.pola.v1.GetTEDResponse + (*DeleteSessionRequest)(nil), // 26: api.pola.v1.DeleteSessionRequest + (*DeleteSessionResponse)(nil), // 27: api.pola.v1.DeleteSessionResponse } var file_pola_proto_depIdxs = []int32{ 0, // 0: api.pola.v1.SRPolicy.type:type_name -> api.pola.v1.SRPolicyType @@ -1743,31 +1929,34 @@ var file_pola_proto_depIdxs = []int32{ 1, // 5: api.pola.v1.Session.state:type_name -> api.pola.v1.SessionState 9, // 6: api.pola.v1.SessionList.sessions:type_name -> api.pola.v1.Session 4, // 7: api.pola.v1.SRPolicyList.sr_policies:type_name -> api.pola.v1.SRPolicy - 2, // 8: api.pola.v1.Metric.type:type_name -> api.pola.v1.MetricType - 13, // 9: api.pola.v1.LsLink.metrics:type_name -> api.pola.v1.Metric - 14, // 10: api.pola.v1.LsNode.ls_links:type_name -> api.pola.v1.LsLink - 12, // 11: api.pola.v1.LsNode.ls_prefixes:type_name -> api.pola.v1.LsPrefix - 15, // 12: api.pola.v1.TED.ls_nodes:type_name -> api.pola.v1.LsNode - 9, // 13: api.pola.v1.GetSessionListResponse.sessions:type_name -> api.pola.v1.Session - 4, // 14: api.pola.v1.GetSRPolicyListResponse.sr_policies:type_name -> api.pola.v1.SRPolicy - 15, // 15: api.pola.v1.GetTEDResponse.ls_nodes:type_name -> api.pola.v1.LsNode - 5, // 16: api.pola.v1.PCEService.CreateSRPolicy:input_type -> api.pola.v1.CreateSRPolicyRequest - 7, // 17: api.pola.v1.PCEService.DeleteSRPolicy:input_type -> api.pola.v1.DeleteSRPolicyRequest - 17, // 18: api.pola.v1.PCEService.GetSessionList:input_type -> api.pola.v1.GetSessionListRequest - 19, // 19: api.pola.v1.PCEService.GetSRPolicyList:input_type -> api.pola.v1.GetSRPolicyListRequest - 21, // 20: api.pola.v1.PCEService.GetTED:input_type -> api.pola.v1.GetTEDRequest - 23, // 21: api.pola.v1.PCEService.DeleteSession:input_type -> api.pola.v1.DeleteSessionRequest - 6, // 22: api.pola.v1.PCEService.CreateSRPolicy:output_type -> api.pola.v1.CreateSRPolicyResponse - 8, // 23: api.pola.v1.PCEService.DeleteSRPolicy:output_type -> api.pola.v1.DeleteSRPolicyResponse - 18, // 24: api.pola.v1.PCEService.GetSessionList:output_type -> api.pola.v1.GetSessionListResponse - 20, // 25: api.pola.v1.PCEService.GetSRPolicyList:output_type -> api.pola.v1.GetSRPolicyListResponse - 22, // 26: api.pola.v1.PCEService.GetTED:output_type -> api.pola.v1.GetTEDResponse - 24, // 27: api.pola.v1.PCEService.DeleteSession:output_type -> api.pola.v1.DeleteSessionResponse - 22, // [22:28] is the sub-list for method output_type - 16, // [16:22] is the sub-list for method input_type - 16, // [16:16] is the sub-list for extension type_name - 16, // [16:16] is the sub-list for extension extendee - 0, // [0:16] is the sub-list for field type_name + 13, // 8: api.pola.v1.LsSrv6SID.sids:type_name -> api.pola.v1.SID + 14, // 9: api.pola.v1.LsSrv6SID.multi_topo_ids:type_name -> api.pola.v1.MultiTopoID + 2, // 10: api.pola.v1.Metric.type:type_name -> api.pola.v1.MetricType + 16, // 11: api.pola.v1.LsLink.metrics:type_name -> api.pola.v1.Metric + 17, // 12: api.pola.v1.LsNode.ls_links:type_name -> api.pola.v1.LsLink + 15, // 13: api.pola.v1.LsNode.ls_prefixes:type_name -> api.pola.v1.LsPrefix + 12, // 14: api.pola.v1.LsNode.ls_srv6_sids:type_name -> api.pola.v1.LsSrv6SID + 18, // 15: api.pola.v1.TED.ls_nodes:type_name -> api.pola.v1.LsNode + 9, // 16: api.pola.v1.GetSessionListResponse.sessions:type_name -> api.pola.v1.Session + 4, // 17: api.pola.v1.GetSRPolicyListResponse.sr_policies:type_name -> api.pola.v1.SRPolicy + 18, // 18: api.pola.v1.GetTEDResponse.ls_nodes:type_name -> api.pola.v1.LsNode + 5, // 19: api.pola.v1.PCEService.CreateSRPolicy:input_type -> api.pola.v1.CreateSRPolicyRequest + 7, // 20: api.pola.v1.PCEService.DeleteSRPolicy:input_type -> api.pola.v1.DeleteSRPolicyRequest + 20, // 21: api.pola.v1.PCEService.GetSessionList:input_type -> api.pola.v1.GetSessionListRequest + 22, // 22: api.pola.v1.PCEService.GetSRPolicyList:input_type -> api.pola.v1.GetSRPolicyListRequest + 24, // 23: api.pola.v1.PCEService.GetTED:input_type -> api.pola.v1.GetTEDRequest + 26, // 24: api.pola.v1.PCEService.DeleteSession:input_type -> api.pola.v1.DeleteSessionRequest + 6, // 25: api.pola.v1.PCEService.CreateSRPolicy:output_type -> api.pola.v1.CreateSRPolicyResponse + 8, // 26: api.pola.v1.PCEService.DeleteSRPolicy:output_type -> api.pola.v1.DeleteSRPolicyResponse + 21, // 27: api.pola.v1.PCEService.GetSessionList:output_type -> api.pola.v1.GetSessionListResponse + 23, // 28: api.pola.v1.PCEService.GetSRPolicyList:output_type -> api.pola.v1.GetSRPolicyListResponse + 25, // 29: api.pola.v1.PCEService.GetTED:output_type -> api.pola.v1.GetTEDResponse + 27, // 30: api.pola.v1.PCEService.DeleteSession:output_type -> api.pola.v1.DeleteSessionResponse + 25, // [25:31] is the sub-list for method output_type + 19, // [19:25] is the sub-list for method input_type + 19, // [19:19] is the sub-list for extension type_name + 19, // [19:19] is the sub-list for extension extendee + 0, // [0:19] is the sub-list for field type_name } func init() { file_pola_proto_init() } @@ -1885,7 +2074,7 @@ func file_pola_proto_init() { } } file_pola_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LsPrefix); i { + switch v := v.(*LsSrv6SID); i { case 0: return &v.state case 1: @@ -1897,7 +2086,7 @@ func file_pola_proto_init() { } } file_pola_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Metric); i { + switch v := v.(*SID); i { case 0: return &v.state case 1: @@ -1909,7 +2098,7 @@ func file_pola_proto_init() { } } file_pola_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LsLink); i { + switch v := v.(*MultiTopoID); i { case 0: return &v.state case 1: @@ -1921,7 +2110,7 @@ func file_pola_proto_init() { } } file_pola_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LsNode); i { + switch v := v.(*LsPrefix); i { case 0: return &v.state case 1: @@ -1933,7 +2122,7 @@ func file_pola_proto_init() { } } file_pola_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TED); i { + switch v := v.(*Metric); i { case 0: return &v.state case 1: @@ -1945,7 +2134,7 @@ func file_pola_proto_init() { } } file_pola_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetSessionListRequest); i { + switch v := v.(*LsLink); i { case 0: return &v.state case 1: @@ -1957,7 +2146,7 @@ func file_pola_proto_init() { } } file_pola_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetSessionListResponse); i { + switch v := v.(*LsNode); i { case 0: return &v.state case 1: @@ -1969,7 +2158,7 @@ func file_pola_proto_init() { } } file_pola_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetSRPolicyListRequest); i { + switch v := v.(*TED); i { case 0: return &v.state case 1: @@ -1981,7 +2170,7 @@ func file_pola_proto_init() { } } file_pola_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetSRPolicyListResponse); i { + switch v := v.(*GetSessionListRequest); i { case 0: return &v.state case 1: @@ -1993,7 +2182,7 @@ func file_pola_proto_init() { } } file_pola_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetTEDRequest); i { + switch v := v.(*GetSessionListResponse); i { case 0: return &v.state case 1: @@ -2005,7 +2194,7 @@ func file_pola_proto_init() { } } file_pola_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetTEDResponse); i { + switch v := v.(*GetSRPolicyListRequest); i { case 0: return &v.state case 1: @@ -2017,7 +2206,7 @@ func file_pola_proto_init() { } } file_pola_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteSessionRequest); i { + switch v := v.(*GetSRPolicyListResponse); i { case 0: return &v.state case 1: @@ -2029,6 +2218,42 @@ func file_pola_proto_init() { } } file_pola_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetTEDRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pola_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetTEDResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pola_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteSessionRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pola_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DeleteSessionResponse); i { case 0: return &v.state @@ -2047,7 +2272,7 @@ func file_pola_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_pola_proto_rawDesc, NumEnums: 3, - NumMessages: 22, + NumMessages: 25, NumExtensions: 0, NumServices: 1, }, diff --git a/api/pola/v1/pola.proto b/api/pola/v1/pola.proto index b3769f64..797494b7 100644 --- a/api/pola/v1/pola.proto +++ b/api/pola/v1/pola.proto @@ -85,6 +85,20 @@ message SRPolicyList { repeated SRPolicy sr_policies = 1; } +message LsSrv6SID { + repeated SID sids = 1; + uint32 endpoint_behavior = 2; + repeated MultiTopoID multi_topo_ids = 3; +} + +message SID { + string sid = 1; +} + +message MultiTopoID { + uint32 multi_topo_id = 1; +} + message LsPrefix { string prefix = 1; uint32 sid_index = 2; @@ -123,6 +137,7 @@ message LsNode { uint32 srgb_end = 6; repeated LsLink ls_links = 7; repeated LsPrefix ls_prefixes = 8; + repeated LsSrv6SID ls_srv6_sids = 9; } message TED { diff --git a/cmd/pola/grpc/grpc_client.go b/cmd/pola/grpc/grpc_client.go index 86abe104..05033135 100644 --- a/cmd/pola/grpc/grpc_client.go +++ b/cmd/pola/grpc/grpc_client.go @@ -179,11 +179,19 @@ func addLsNode(ted *table.LsTED, node *pb.LsNode) error { ted.Nodes[node.GetAsn()][node.GetRouterId()].Prefixes = append(ted.Nodes[node.GetAsn()][node.GetRouterId()].Prefixes, lsPrefix) } + for _, srv6SID := range node.LsSrv6Sids { + lsSrv6SID, err := createSrv6SID(ted.Nodes[node.GetAsn()][node.GetRouterId()], srv6SID) + if err != nil { + return err + } + ted.Nodes[node.GetAsn()][node.GetRouterId()].SRv6SIDs = append(ted.Nodes[node.GetAsn()][node.GetRouterId()].SRv6SIDs, lsSrv6SID) + } + return nil } -func createLsPrefix(lsNode *table.LsNode, prefix *pb.LsPrefix) (*table.LsPrefixV4, error) { - lsPrefix := table.NewLsPrefixV4(lsNode) +func createLsPrefix(lsNode *table.LsNode, prefix *pb.LsPrefix) (*table.LsPrefix, error) { + lsPrefix := table.NewLsPrefix(lsNode) var err error lsPrefix.Prefix, err = netip.ParsePrefix(prefix.GetPrefix()) if err != nil { @@ -233,3 +241,21 @@ func createMetric(metricInfo *pb.Metric) (*table.Metric, error) { return nil, errors.New("unknown metric type") } } + +func createSrv6SID(lsNode *table.LsNode, srv6SID *pb.LsSrv6SID) (*table.LsSrv6SID, error) { + lsSrv6SID := table.NewLsSrv6SID(lsNode) + + lsSrv6SID.EndpointBehavior = srv6SID.GetEndpointBehavior() + lsSrv6SID.ServiceType = srv6SID.GetServiceType() + lsSrv6SID.TrafficType = srv6SID.GetTrafficType() + lsSrv6SID.OpaqueType = srv6SID.GetOpaqueType() + lsSrv6SID.Value = srv6SID.GetValue() + for _, sid := range srv6SID.GetSids() { + lsSrv6SID.Sids = append(lsSrv6SID.Sids, sid.GetSid()) + } + for _, topoID := range srv6SID.GetMultiTopoIds() { + lsSrv6SID.MultiTopoIDs = append(lsSrv6SID.MultiTopoIDs, topoID.GetMultiTopoId()) + } + + return lsSrv6SID, nil +} diff --git a/pkg/server/grpc_server.go b/pkg/server/grpc_server.go index f57559f2..b768c992 100644 --- a/pkg/server/grpc_server.go +++ b/pkg/server/grpc_server.go @@ -390,6 +390,7 @@ func (s *APIServer) GetTED(ctx context.Context, req *pb.GetTEDRequest) (*pb.GetT SrgbEnd: lsNode.SrgbEnd, LsLinks: make([]*pb.LsLink, 0, len(lsNode.Links)), LsPrefixes: make([]*pb.LsPrefix, 0, len(lsNode.Prefixes)), + LsSrv6Sids: make([]*pb.LsSrv6SID, 0, len(lsNode.SRv6SIDs)), } for _, lsLink := range lsNode.Links { @@ -430,6 +431,27 @@ func (s *APIServer) GetTED(ctx context.Context, req *pb.GetTEDRequest) (*pb.GetT node.LsPrefixes = append(node.LsPrefixes, prefix) } + for _, lsSrv6SID := range lsNode.SRv6SIDs { + srv6SID := &pb.LsSrv6SID{ + EndpointBehavior: lsSrv6SID.EndpointBehavior, + Sids: make([]*pb.SID, 0, len(lsSrv6SID.Sids)), + MultiTopoIds: make([]*pb.MultiTopoID, 0, len(lsSrv6SID.MultiTopoIDs)), + } + + for _, sid := range lsSrv6SID.Sids { + srv6SID.Sids = append(srv6SID.Sids, &pb.SID{ + Sid: sid, + }) + } + + for _, topoID := range lsSrv6SID.MultiTopoIDs { + srv6SID.MultiTopoIds = append(srv6SID.MultiTopoIds, &pb.MultiTopoID{ + MultiTopoId: topoID, + }) + } + node.LsSrv6Sids = append(node.LsSrv6Sids, srv6SID) + } + ret.LsNodes = append(ret.LsNodes, node) } } From d19513c0783445a9d25644d77bc2ed7fe2e2427f Mon Sep 17 00:00:00 2001 From: k1yoto Date: Sat, 17 May 2025 00:52:14 +0000 Subject: [PATCH 30/87] fix(interface): support IPv6 for LsNodeNLRI and LsLinkNLRI --- internal/pkg/gobgp/interface.go | 42 ++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/internal/pkg/gobgp/interface.go b/internal/pkg/gobgp/interface.go index 87afe20c..f346e32a 100644 --- a/internal/pkg/gobgp/interface.go +++ b/internal/pkg/gobgp/interface.go @@ -164,12 +164,15 @@ func getLsNodeNLRI(typedLinkStateNLRI *api.LsNodeNLRI, pathAttrs []*anypb.Any) ( isisArea := bgplsAttr.GetNode().GetIsisArea() lsNode.IsisAreaID = formatIsisAreaID(isisArea) lsNode.Hostname = bgplsAttr.GetNode().GetName() - srCapabilities := bgplsAttr.GetNode().GetSrCapabilities().GetRanges() - if len(srCapabilities) != 1 { - return nil, fmt.Errorf("expected 1 SR Capability TLV, got: %d", len(srCapabilities)) + if bgplsAttr.GetNode().GetSrCapabilities() != nil { + srCapabilities := bgplsAttr.GetNode().GetSrCapabilities().GetRanges() + if len(srCapabilities) != 1 { + return nil, fmt.Errorf("expected 1 SR Capability TLV, got: %d", len(srCapabilities)) + } else { + lsNode.SrgbBegin = srCapabilities[0].GetBegin() + lsNode.SrgbEnd = srCapabilities[0].GetEnd() + } } - lsNode.SrgbBegin = srCapabilities[0].GetBegin() - lsNode.SrgbEnd = srCapabilities[0].GetEnd() } return lsNode, nil @@ -179,14 +182,31 @@ func getLsLinkNLRI(typedLinkStateNLRI *api.LsLinkNLRI, pathAttrs []*anypb.Any) ( localNode := table.NewLsNode(typedLinkStateNLRI.GetLocalNode().GetAsn(), typedLinkStateNLRI.GetLocalNode().GetIgpRouterId()) remoteNode := table.NewLsNode(typedLinkStateNLRI.GetRemoteNode().GetAsn(), typedLinkStateNLRI.GetRemoteNode().GetIgpRouterId()) - localIP, err := netip.ParseAddr(typedLinkStateNLRI.GetLinkDescriptor().GetInterfaceAddrIpv4()) - if err != nil { - return nil, fmt.Errorf("failed to parse local IP address %q: %v", typedLinkStateNLRI.GetLinkDescriptor().GetInterfaceAddrIpv4(), err) + var err error + var localIP netip.Addr + if typedLinkStateNLRI.GetLinkDescriptor().GetInterfaceAddrIpv4() != "" { + localIP, err = netip.ParseAddr(typedLinkStateNLRI.GetLinkDescriptor().GetInterfaceAddrIpv4()) + if err != nil { + return nil, fmt.Errorf("failed to parse local IPv4 address: %v", err) + } + } else { + localIP, err = netip.ParseAddr(typedLinkStateNLRI.GetLinkDescriptor().GetInterfaceAddrIpv6()) + if err != nil { + return nil, fmt.Errorf("failed to parse local IPv6 address: %v", err) + } } - remoteIP, err := netip.ParseAddr(typedLinkStateNLRI.GetLinkDescriptor().GetNeighborAddrIpv4()) - if err != nil { - return nil, fmt.Errorf("failed to parse remote IP address %q: %v", typedLinkStateNLRI.GetLinkDescriptor().GetNeighborAddrIpv4(), err) + var remoteIP netip.Addr + if typedLinkStateNLRI.GetLinkDescriptor().GetNeighborAddrIpv4() != "" { + remoteIP, err = netip.ParseAddr(typedLinkStateNLRI.GetLinkDescriptor().GetNeighborAddrIpv4()) + if err != nil { + return nil, fmt.Errorf("failed to parse remote IPv4 address: %v", err) + } + } else { + remoteIP, err = netip.ParseAddr(typedLinkStateNLRI.GetLinkDescriptor().GetNeighborAddrIpv6()) + if err != nil { + return nil, fmt.Errorf("failed to parse remote IPv6 address: %v", err) + } } lsLink := table.NewLsLink(localNode, remoteNode) From d99dfe1dc3158467138ebbd05656595d086e74ec Mon Sep 17 00:00:00 2001 From: k1yoto Date: Sat, 17 May 2025 00:54:39 +0000 Subject: [PATCH 31/87] chore(LsSRv6SIDNLRI): add comment-outs and tools --- internal/pkg/gobgp/interface.go | 4 ++-- internal/pkg/table/ted.go | 2 +- tools/grpc/go/show_ted/show_ted.go | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/internal/pkg/gobgp/interface.go b/internal/pkg/gobgp/interface.go index f346e32a..3b02e9e8 100644 --- a/internal/pkg/gobgp/interface.go +++ b/internal/pkg/gobgp/interface.go @@ -325,8 +325,8 @@ func getLsSrv6SIDNLRIList(pathAttrs []*anypb.Any) ([]table.TEDElem, error) { } switch typedPathAttr := typedPathAttr.(type) { - case *api.SRv6EndPointBehavior: - endpointBehavior = uint32(typedPathAttr.GetBehavior()) + case *api.LsAttribute: + // TODO: Handle LsAttribute for SRv6 SID case *api.MpReachNLRIAttribute: for _, nlri := range typedPathAttr.GetNlris() { typedNLRI, err := nlri.UnmarshalNew() diff --git a/internal/pkg/table/ted.go b/internal/pkg/table/ted.go index 07d10f4f..87d8c41a 100644 --- a/internal/pkg/table/ted.go +++ b/internal/pkg/table/ted.go @@ -215,7 +215,7 @@ func (lp *LsPrefix) UpdateTED(ted *LsTED) { type LsSrv6SID struct { LocalNode *LsNode // primary key, in MP_REACH_NLRI Attr Sids []string // in LsSrv6SID Attr - EndpointBehavior uint32 // in srv6EndpointBehavior Attr + EndpointBehavior uint32 // in BGP-LS Attr MultiTopoIDs []uint32 // in LsSrv6SID Attr } diff --git a/tools/grpc/go/show_ted/show_ted.go b/tools/grpc/go/show_ted/show_ted.go index 04eea043..a2591b4d 100644 --- a/tools/grpc/go/show_ted/show_ted.go +++ b/tools/grpc/go/show_ted/show_ted.go @@ -52,6 +52,9 @@ func main() { for _, link := range node.GetLsLinks() { fmt.Printf("link info: %#v\n", link) } + for _, srv6SID := range node.GetLsSrv6Sids() { + fmt.Printf("srv6SID info: %#v\n", srv6SID) + } fmt.Println() } } From 89ecb1eb4160b0a9ad387710592d752b3d3f2e38 Mon Sep 17 00:00:00 2001 From: k1yoto Date: Sat, 17 May 2025 03:26:30 +0000 Subject: [PATCH 32/87] fix(gobgp): fix to be able to display TED in CLI tool --- internal/pkg/table/ted.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/pkg/table/ted.go b/internal/pkg/table/ted.go index 87d8c41a..88073625 100644 --- a/internal/pkg/table/ted.go +++ b/internal/pkg/table/ted.go @@ -269,14 +269,14 @@ const ( func (m MetricType) String() string { switch m { case IGPMetric: - return "IGP" + return "METRIC_TYPE_IGP" case TEMetric: - return "TE" + return "METRIC_TYPE_TE" case DelayMetric: - return "DELAY" + return "METRIC_TYPE_DELAY" case HopcountMetric: - return "HOPCOUNT" + return "METRIC_TYPE_HOPCOUNT" default: - return "Unknown" + return "METRIC_TYPE_UNSPECIFIED" } } From 8d31734effb3a45fe5d6d6e16efbd27fc508db51 Mon Sep 17 00:00:00 2001 From: k1yoto Date: Tue, 27 May 2025 10:16:06 +0000 Subject: [PATCH 33/87] chore(README): fix link text to be descriptive --- examples/containerlab/sr-mpls_pcep/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/containerlab/sr-mpls_pcep/README.md b/examples/containerlab/sr-mpls_pcep/README.md index bd8a4ce5..bdb04c1f 100644 --- a/examples/containerlab/sr-mpls_pcep/README.md +++ b/examples/containerlab/sr-mpls_pcep/README.md @@ -36,7 +36,7 @@ net.ipv4.udp_mem=1124736 10000000 67108864 $ sysctl -p ``` -host-check (see: [link](https://xrdocs.io/virtual-routing/tutorials/2022-08-22-setting-up-host-environment-to-run-xrd/)) +host-check (see: [Setting up the Host Environment to run XRd](https://xrdocs.io/virtual-routing/tutorials/2022-08-22-setting-up-host-environment-to-run-xrd/)) ```bash git clone https://github.com/ios-xr/xrd-tools From a0a4c5561518cde661454de53eb42654d7f8a52f Mon Sep 17 00:00:00 2001 From: k1yoto Date: Sun, 22 Jun 2025 08:26:40 +0000 Subject: [PATCH 34/87] feat(api): add SRv6 SIDStructure and EndpointBehavior protobuf definitions --- api/pola/v1/pola.pb.go | 843 +++++++++++++++++++++++------------ api/pola/v1/pola.proto | 33 +- cmd/pola/grpc/grpc_client.go | 4 - 3 files changed, 595 insertions(+), 285 deletions(-) diff --git a/api/pola/v1/pola.pb.go b/api/pola/v1/pola.pb.go index 27e62d8e..aec3d106 100644 --- a/api/pola/v1/pola.pb.go +++ b/api/pola/v1/pola.pb.go @@ -12,10 +12,11 @@ package v1 import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( @@ -753,18 +754,18 @@ func (x *SRPolicyList) GetSrPolicies() []*SRPolicy { return nil } -type LsSrv6SID struct { +type EndpointBehavior struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Sids []*SID `protobuf:"bytes,1,rep,name=sids,proto3" json:"sids,omitempty"` - EndpointBehavior uint32 `protobuf:"varint,2,opt,name=endpoint_behavior,json=endpointBehavior,proto3" json:"endpoint_behavior,omitempty"` - MultiTopoIds []*MultiTopoID `protobuf:"bytes,3,rep,name=multi_topo_ids,json=multiTopoIds,proto3" json:"multi_topo_ids,omitempty"` + Behavior uint32 `protobuf:"varint,1,opt,name=behavior,proto3" json:"behavior,omitempty"` + Flags uint32 `protobuf:"varint,2,opt,name=flags,proto3" json:"flags,omitempty"` + Algorithm uint32 `protobuf:"varint,3,opt,name=algorithm,proto3" json:"algorithm,omitempty"` } -func (x *LsSrv6SID) Reset() { - *x = LsSrv6SID{} +func (x *EndpointBehavior) Reset() { + *x = EndpointBehavior{} if protoimpl.UnsafeEnabled { mi := &file_pola_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -772,13 +773,13 @@ func (x *LsSrv6SID) Reset() { } } -func (x *LsSrv6SID) String() string { +func (x *EndpointBehavior) String() string { return protoimpl.X.MessageStringOf(x) } -func (*LsSrv6SID) ProtoMessage() {} +func (*EndpointBehavior) ProtoMessage() {} -func (x *LsSrv6SID) ProtoReflect() protoreflect.Message { +func (x *EndpointBehavior) ProtoReflect() protoreflect.Message { mi := &file_pola_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -790,30 +791,101 @@ func (x *LsSrv6SID) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use LsSrv6SID.ProtoReflect.Descriptor instead. -func (*LsSrv6SID) Descriptor() ([]byte, []int) { +// Deprecated: Use EndpointBehavior.ProtoReflect.Descriptor instead. +func (*EndpointBehavior) Descriptor() ([]byte, []int) { return file_pola_proto_rawDescGZIP(), []int{9} } -func (x *LsSrv6SID) GetSids() []*SID { +func (x *EndpointBehavior) GetBehavior() uint32 { if x != nil { - return x.Sids + return x.Behavior } - return nil + return 0 } -func (x *LsSrv6SID) GetEndpointBehavior() uint32 { +func (x *EndpointBehavior) GetFlags() uint32 { if x != nil { - return x.EndpointBehavior + return x.Flags } return 0 } -func (x *LsSrv6SID) GetMultiTopoIds() []*MultiTopoID { +func (x *EndpointBehavior) GetAlgorithm() uint32 { if x != nil { - return x.MultiTopoIds + return x.Algorithm } - return nil + return 0 +} + +type SidStructure struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + LocalBlock uint32 `protobuf:"varint,1,opt,name=local_block,json=localBlock,proto3" json:"local_block,omitempty"` + LocalNode uint32 `protobuf:"varint,2,opt,name=local_node,json=localNode,proto3" json:"local_node,omitempty"` + LocalFunc uint32 `protobuf:"varint,3,opt,name=local_func,json=localFunc,proto3" json:"local_func,omitempty"` + LocalArg uint32 `protobuf:"varint,4,opt,name=local_arg,json=localArg,proto3" json:"local_arg,omitempty"` +} + +func (x *SidStructure) Reset() { + *x = SidStructure{} + if protoimpl.UnsafeEnabled { + mi := &file_pola_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SidStructure) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SidStructure) ProtoMessage() {} + +func (x *SidStructure) ProtoReflect() protoreflect.Message { + mi := &file_pola_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SidStructure.ProtoReflect.Descriptor instead. +func (*SidStructure) Descriptor() ([]byte, []int) { + return file_pola_proto_rawDescGZIP(), []int{10} +} + +func (x *SidStructure) GetLocalBlock() uint32 { + if x != nil { + return x.LocalBlock + } + return 0 +} + +func (x *SidStructure) GetLocalNode() uint32 { + if x != nil { + return x.LocalNode + } + return 0 +} + +func (x *SidStructure) GetLocalFunc() uint32 { + if x != nil { + return x.LocalFunc + } + return 0 +} + +func (x *SidStructure) GetLocalArg() uint32 { + if x != nil { + return x.LocalArg + } + return 0 } type SID struct { @@ -827,7 +899,7 @@ type SID struct { func (x *SID) Reset() { *x = SID{} if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[10] + mi := &file_pola_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -840,7 +912,7 @@ func (x *SID) String() string { func (*SID) ProtoMessage() {} func (x *SID) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[10] + mi := &file_pola_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -853,7 +925,7 @@ func (x *SID) ProtoReflect() protoreflect.Message { // Deprecated: Use SID.ProtoReflect.Descriptor instead. func (*SID) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{10} + return file_pola_proto_rawDescGZIP(), []int{11} } func (x *SID) GetSid() string { @@ -874,7 +946,7 @@ type MultiTopoID struct { func (x *MultiTopoID) Reset() { *x = MultiTopoID{} if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[11] + mi := &file_pola_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -887,7 +959,7 @@ func (x *MultiTopoID) String() string { func (*MultiTopoID) ProtoMessage() {} func (x *MultiTopoID) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[11] + mi := &file_pola_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -900,7 +972,7 @@ func (x *MultiTopoID) ProtoReflect() protoreflect.Message { // Deprecated: Use MultiTopoID.ProtoReflect.Descriptor instead. func (*MultiTopoID) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{11} + return file_pola_proto_rawDescGZIP(), []int{12} } func (x *MultiTopoID) GetMultiTopoId() uint32 { @@ -910,6 +982,77 @@ func (x *MultiTopoID) GetMultiTopoId() uint32 { return 0 } +type LsSrv6SID struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Sids []*SID `protobuf:"bytes,1,rep,name=sids,proto3" json:"sids,omitempty"` + EndpointBehavior *EndpointBehavior `protobuf:"bytes,2,opt,name=endpoint_behavior,json=endpointBehavior,proto3" json:"endpoint_behavior,omitempty"` + SidStructure *SidStructure `protobuf:"bytes,3,opt,name=sid_structure,json=sidStructure,proto3" json:"sid_structure,omitempty"` + MultiTopoIds []*MultiTopoID `protobuf:"bytes,4,rep,name=multi_topo_ids,json=multiTopoIds,proto3" json:"multi_topo_ids,omitempty"` +} + +func (x *LsSrv6SID) Reset() { + *x = LsSrv6SID{} + if protoimpl.UnsafeEnabled { + mi := &file_pola_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LsSrv6SID) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LsSrv6SID) ProtoMessage() {} + +func (x *LsSrv6SID) ProtoReflect() protoreflect.Message { + mi := &file_pola_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LsSrv6SID.ProtoReflect.Descriptor instead. +func (*LsSrv6SID) Descriptor() ([]byte, []int) { + return file_pola_proto_rawDescGZIP(), []int{13} +} + +func (x *LsSrv6SID) GetSids() []*SID { + if x != nil { + return x.Sids + } + return nil +} + +func (x *LsSrv6SID) GetEndpointBehavior() *EndpointBehavior { + if x != nil { + return x.EndpointBehavior + } + return nil +} + +func (x *LsSrv6SID) GetSidStructure() *SidStructure { + if x != nil { + return x.SidStructure + } + return nil +} + +func (x *LsSrv6SID) GetMultiTopoIds() []*MultiTopoID { + if x != nil { + return x.MultiTopoIds + } + return nil +} + type LsPrefix struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -922,7 +1065,7 @@ type LsPrefix struct { func (x *LsPrefix) Reset() { *x = LsPrefix{} if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[12] + mi := &file_pola_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -935,7 +1078,7 @@ func (x *LsPrefix) String() string { func (*LsPrefix) ProtoMessage() {} func (x *LsPrefix) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[12] + mi := &file_pola_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -948,7 +1091,7 @@ func (x *LsPrefix) ProtoReflect() protoreflect.Message { // Deprecated: Use LsPrefix.ProtoReflect.Descriptor instead. func (*LsPrefix) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{12} + return file_pola_proto_rawDescGZIP(), []int{14} } func (x *LsPrefix) GetPrefix() string { @@ -977,7 +1120,7 @@ type Metric struct { func (x *Metric) Reset() { *x = Metric{} if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[13] + mi := &file_pola_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -990,7 +1133,7 @@ func (x *Metric) String() string { func (*Metric) ProtoMessage() {} func (x *Metric) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[13] + mi := &file_pola_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1003,7 +1146,7 @@ func (x *Metric) ProtoReflect() protoreflect.Message { // Deprecated: Use Metric.ProtoReflect.Descriptor instead. func (*Metric) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{13} + return file_pola_proto_rawDescGZIP(), []int{15} } func (x *Metric) GetType() MetricType { @@ -1020,25 +1163,89 @@ func (x *Metric) GetValue() uint32 { return 0 } +type Srv6EndXSID struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + EndpointBehavior uint32 `protobuf:"varint,1,opt,name=endpoint_behavior,json=endpointBehavior,proto3" json:"endpoint_behavior,omitempty"` + Sids []*SID `protobuf:"bytes,2,rep,name=sids,proto3" json:"sids,omitempty"` + SidStructure *SidStructure `protobuf:"bytes,3,opt,name=sid_structure,json=sidStructure,proto3" json:"sid_structure,omitempty"` +} + +func (x *Srv6EndXSID) Reset() { + *x = Srv6EndXSID{} + if protoimpl.UnsafeEnabled { + mi := &file_pola_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Srv6EndXSID) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Srv6EndXSID) ProtoMessage() {} + +func (x *Srv6EndXSID) ProtoReflect() protoreflect.Message { + mi := &file_pola_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Srv6EndXSID.ProtoReflect.Descriptor instead. +func (*Srv6EndXSID) Descriptor() ([]byte, []int) { + return file_pola_proto_rawDescGZIP(), []int{16} +} + +func (x *Srv6EndXSID) GetEndpointBehavior() uint32 { + if x != nil { + return x.EndpointBehavior + } + return 0 +} + +func (x *Srv6EndXSID) GetSids() []*SID { + if x != nil { + return x.Sids + } + return nil +} + +func (x *Srv6EndXSID) GetSidStructure() *SidStructure { + if x != nil { + return x.SidStructure + } + return nil +} + type LsLink struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - LocalRouterId string `protobuf:"bytes,1,opt,name=local_router_id,json=localRouterId,proto3" json:"local_router_id,omitempty"` - LocalAsn uint32 `protobuf:"varint,2,opt,name=local_asn,json=localAsn,proto3" json:"local_asn,omitempty"` - LocalIp string `protobuf:"bytes,3,opt,name=local_ip,json=localIp,proto3" json:"local_ip,omitempty"` - RemoteRouterId string `protobuf:"bytes,4,opt,name=remote_router_id,json=remoteRouterId,proto3" json:"remote_router_id,omitempty"` - RemoteAsn uint32 `protobuf:"varint,5,opt,name=remote_asn,json=remoteAsn,proto3" json:"remote_asn,omitempty"` - RemoteIp string `protobuf:"bytes,6,opt,name=remote_ip,json=remoteIp,proto3" json:"remote_ip,omitempty"` - Metrics []*Metric `protobuf:"bytes,7,rep,name=metrics,proto3" json:"metrics,omitempty"` - AdjSid uint32 `protobuf:"varint,8,opt,name=adj_sid,json=adjSid,proto3" json:"adj_sid,omitempty"` + LocalRouterId string `protobuf:"bytes,1,opt,name=local_router_id,json=localRouterId,proto3" json:"local_router_id,omitempty"` + LocalAsn uint32 `protobuf:"varint,2,opt,name=local_asn,json=localAsn,proto3" json:"local_asn,omitempty"` + LocalIp string `protobuf:"bytes,3,opt,name=local_ip,json=localIp,proto3" json:"local_ip,omitempty"` + RemoteRouterId string `protobuf:"bytes,4,opt,name=remote_router_id,json=remoteRouterId,proto3" json:"remote_router_id,omitempty"` + RemoteAsn uint32 `protobuf:"varint,5,opt,name=remote_asn,json=remoteAsn,proto3" json:"remote_asn,omitempty"` + RemoteIp string `protobuf:"bytes,6,opt,name=remote_ip,json=remoteIp,proto3" json:"remote_ip,omitempty"` + Metrics []*Metric `protobuf:"bytes,7,rep,name=metrics,proto3" json:"metrics,omitempty"` + AdjSid uint32 `protobuf:"varint,8,opt,name=adj_sid,json=adjSid,proto3" json:"adj_sid,omitempty"` + Srv6EndXSid *Srv6EndXSID `protobuf:"bytes,9,opt,name=srv6_end_x_sid,json=srv6EndXSid,proto3" json:"srv6_end_x_sid,omitempty"` } func (x *LsLink) Reset() { *x = LsLink{} if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[14] + mi := &file_pola_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1051,7 +1258,7 @@ func (x *LsLink) String() string { func (*LsLink) ProtoMessage() {} func (x *LsLink) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[14] + mi := &file_pola_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1064,7 +1271,7 @@ func (x *LsLink) ProtoReflect() protoreflect.Message { // Deprecated: Use LsLink.ProtoReflect.Descriptor instead. func (*LsLink) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{14} + return file_pola_proto_rawDescGZIP(), []int{17} } func (x *LsLink) GetLocalRouterId() string { @@ -1123,6 +1330,13 @@ func (x *LsLink) GetAdjSid() uint32 { return 0 } +func (x *LsLink) GetSrv6EndXSid() *Srv6EndXSID { + if x != nil { + return x.Srv6EndXSid + } + return nil +} + type LsNode struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1142,7 +1356,7 @@ type LsNode struct { func (x *LsNode) Reset() { *x = LsNode{} if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[15] + mi := &file_pola_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1155,7 +1369,7 @@ func (x *LsNode) String() string { func (*LsNode) ProtoMessage() {} func (x *LsNode) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[15] + mi := &file_pola_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1168,7 +1382,7 @@ func (x *LsNode) ProtoReflect() protoreflect.Message { // Deprecated: Use LsNode.ProtoReflect.Descriptor instead. func (*LsNode) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{15} + return file_pola_proto_rawDescGZIP(), []int{18} } func (x *LsNode) GetAsn() uint32 { @@ -1246,7 +1460,7 @@ type TED struct { func (x *TED) Reset() { *x = TED{} if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[16] + mi := &file_pola_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1259,7 +1473,7 @@ func (x *TED) String() string { func (*TED) ProtoMessage() {} func (x *TED) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[16] + mi := &file_pola_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1272,7 +1486,7 @@ func (x *TED) ProtoReflect() protoreflect.Message { // Deprecated: Use TED.ProtoReflect.Descriptor instead. func (*TED) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{16} + return file_pola_proto_rawDescGZIP(), []int{19} } func (x *TED) GetEnable() bool { @@ -1298,7 +1512,7 @@ type GetSessionListRequest struct { func (x *GetSessionListRequest) Reset() { *x = GetSessionListRequest{} if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[17] + mi := &file_pola_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1311,7 +1525,7 @@ func (x *GetSessionListRequest) String() string { func (*GetSessionListRequest) ProtoMessage() {} func (x *GetSessionListRequest) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[17] + mi := &file_pola_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1324,7 +1538,7 @@ func (x *GetSessionListRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSessionListRequest.ProtoReflect.Descriptor instead. func (*GetSessionListRequest) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{17} + return file_pola_proto_rawDescGZIP(), []int{20} } type GetSessionListResponse struct { @@ -1338,7 +1552,7 @@ type GetSessionListResponse struct { func (x *GetSessionListResponse) Reset() { *x = GetSessionListResponse{} if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[18] + mi := &file_pola_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1351,7 +1565,7 @@ func (x *GetSessionListResponse) String() string { func (*GetSessionListResponse) ProtoMessage() {} func (x *GetSessionListResponse) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[18] + mi := &file_pola_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1364,7 +1578,7 @@ func (x *GetSessionListResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSessionListResponse.ProtoReflect.Descriptor instead. func (*GetSessionListResponse) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{18} + return file_pola_proto_rawDescGZIP(), []int{21} } func (x *GetSessionListResponse) GetSessions() []*Session { @@ -1383,7 +1597,7 @@ type GetSRPolicyListRequest struct { func (x *GetSRPolicyListRequest) Reset() { *x = GetSRPolicyListRequest{} if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[19] + mi := &file_pola_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1396,7 +1610,7 @@ func (x *GetSRPolicyListRequest) String() string { func (*GetSRPolicyListRequest) ProtoMessage() {} func (x *GetSRPolicyListRequest) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[19] + mi := &file_pola_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1409,7 +1623,7 @@ func (x *GetSRPolicyListRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSRPolicyListRequest.ProtoReflect.Descriptor instead. func (*GetSRPolicyListRequest) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{19} + return file_pola_proto_rawDescGZIP(), []int{22} } type GetSRPolicyListResponse struct { @@ -1423,7 +1637,7 @@ type GetSRPolicyListResponse struct { func (x *GetSRPolicyListResponse) Reset() { *x = GetSRPolicyListResponse{} if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[20] + mi := &file_pola_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1436,7 +1650,7 @@ func (x *GetSRPolicyListResponse) String() string { func (*GetSRPolicyListResponse) ProtoMessage() {} func (x *GetSRPolicyListResponse) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[20] + mi := &file_pola_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1449,7 +1663,7 @@ func (x *GetSRPolicyListResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSRPolicyListResponse.ProtoReflect.Descriptor instead. func (*GetSRPolicyListResponse) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{20} + return file_pola_proto_rawDescGZIP(), []int{23} } func (x *GetSRPolicyListResponse) GetSrPolicies() []*SRPolicy { @@ -1468,7 +1682,7 @@ type GetTEDRequest struct { func (x *GetTEDRequest) Reset() { *x = GetTEDRequest{} if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[21] + mi := &file_pola_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1481,7 +1695,7 @@ func (x *GetTEDRequest) String() string { func (*GetTEDRequest) ProtoMessage() {} func (x *GetTEDRequest) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[21] + mi := &file_pola_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1494,7 +1708,7 @@ func (x *GetTEDRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetTEDRequest.ProtoReflect.Descriptor instead. func (*GetTEDRequest) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{21} + return file_pola_proto_rawDescGZIP(), []int{24} } type GetTEDResponse struct { @@ -1509,7 +1723,7 @@ type GetTEDResponse struct { func (x *GetTEDResponse) Reset() { *x = GetTEDResponse{} if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[22] + mi := &file_pola_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1522,7 +1736,7 @@ func (x *GetTEDResponse) String() string { func (*GetTEDResponse) ProtoMessage() {} func (x *GetTEDResponse) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[22] + mi := &file_pola_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1535,7 +1749,7 @@ func (x *GetTEDResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetTEDResponse.ProtoReflect.Descriptor instead. func (*GetTEDResponse) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{22} + return file_pola_proto_rawDescGZIP(), []int{25} } func (x *GetTEDResponse) GetEnable() bool { @@ -1563,7 +1777,7 @@ type DeleteSessionRequest struct { func (x *DeleteSessionRequest) Reset() { *x = DeleteSessionRequest{} if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[23] + mi := &file_pola_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1576,7 +1790,7 @@ func (x *DeleteSessionRequest) String() string { func (*DeleteSessionRequest) ProtoMessage() {} func (x *DeleteSessionRequest) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[23] + mi := &file_pola_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1589,7 +1803,7 @@ func (x *DeleteSessionRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteSessionRequest.ProtoReflect.Descriptor instead. func (*DeleteSessionRequest) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{23} + return file_pola_proto_rawDescGZIP(), []int{26} } func (x *DeleteSessionRequest) GetAddr() []byte { @@ -1610,7 +1824,7 @@ type DeleteSessionResponse struct { func (x *DeleteSessionResponse) Reset() { *x = DeleteSessionResponse{} if protoimpl.UnsafeEnabled { - mi := &file_pola_proto_msgTypes[24] + mi := &file_pola_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1623,7 +1837,7 @@ func (x *DeleteSessionResponse) String() string { func (*DeleteSessionResponse) ProtoMessage() {} func (x *DeleteSessionResponse) ProtoReflect() protoreflect.Message { - mi := &file_pola_proto_msgTypes[24] + mi := &file_pola_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1636,7 +1850,7 @@ func (x *DeleteSessionResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteSessionResponse.ProtoReflect.Descriptor instead. func (*DeleteSessionResponse) Descriptor() ([]byte, []int) { - return file_pola_proto_rawDescGZIP(), []int{24} + return file_pola_proto_rawDescGZIP(), []int{27} } func (x *DeleteSessionResponse) GetIsSuccess() bool { @@ -1723,21 +1937,42 @@ var file_pola_proto_rawDesc = []byte{ 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0a, 0x73, 0x72, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, - 0x73, 0x22, 0x9e, 0x01, 0x0a, 0x09, 0x4c, 0x73, 0x53, 0x72, 0x76, 0x36, 0x53, 0x49, 0x44, 0x12, - 0x24, 0x0a, 0x04, 0x73, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x49, 0x44, 0x52, - 0x04, 0x73, 0x69, 0x64, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x5f, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x10, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, - 0x6f, 0x72, 0x12, 0x3e, 0x0a, 0x0e, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x5f, 0x74, 0x6f, 0x70, 0x6f, - 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x54, 0x6f, - 0x70, 0x6f, 0x49, 0x44, 0x52, 0x0c, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x54, 0x6f, 0x70, 0x6f, 0x49, - 0x64, 0x73, 0x22, 0x17, 0x0a, 0x03, 0x53, 0x49, 0x44, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x64, + 0x73, 0x22, 0x62, 0x0a, 0x10, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x42, 0x65, 0x68, + 0x61, 0x76, 0x69, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, + 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, + 0x72, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x6c, 0x67, 0x6f, 0x72, + 0x69, 0x74, 0x68, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x61, 0x6c, 0x67, 0x6f, + 0x72, 0x69, 0x74, 0x68, 0x6d, 0x22, 0x8a, 0x01, 0x0a, 0x0c, 0x53, 0x69, 0x64, 0x53, 0x74, 0x72, + 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6c, 0x6f, 0x63, + 0x61, 0x6c, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, + 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6c, 0x6f, 0x63, + 0x61, 0x6c, 0x4e, 0x6f, 0x64, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, + 0x66, 0x75, 0x6e, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6c, 0x6f, 0x63, 0x61, + 0x6c, 0x46, 0x75, 0x6e, 0x63, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x61, + 0x72, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x41, + 0x72, 0x67, 0x22, 0x17, 0x0a, 0x03, 0x53, 0x49, 0x44, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x73, 0x69, 0x64, 0x22, 0x31, 0x0a, 0x0b, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x54, 0x6f, 0x70, 0x6f, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x5f, 0x74, 0x6f, 0x70, 0x6f, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x0b, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x54, 0x6f, 0x70, 0x6f, 0x49, 0x64, 0x22, 0x3f, + 0x0d, 0x52, 0x0b, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x54, 0x6f, 0x70, 0x6f, 0x49, 0x64, 0x22, 0xfd, + 0x01, 0x0a, 0x09, 0x4c, 0x73, 0x53, 0x72, 0x76, 0x36, 0x53, 0x49, 0x44, 0x12, 0x24, 0x0a, 0x04, + 0x73, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x49, 0x44, 0x52, 0x04, 0x73, 0x69, + 0x64, 0x73, 0x12, 0x4a, 0x0a, 0x11, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x62, + 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x64, 0x70, + 0x6f, 0x69, 0x6e, 0x74, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x52, 0x10, 0x65, 0x6e, + 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x12, 0x3e, + 0x0a, 0x0d, 0x73, 0x69, 0x64, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, 0x64, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, + 0x52, 0x0c, 0x73, 0x69, 0x64, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x12, 0x3e, + 0x0a, 0x0e, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x5f, 0x74, 0x6f, 0x70, 0x6f, 0x5f, 0x69, 0x64, 0x73, + 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, + 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x54, 0x6f, 0x70, 0x6f, 0x49, 0x44, + 0x52, 0x0c, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x54, 0x6f, 0x70, 0x6f, 0x49, 0x64, 0x73, 0x22, 0x3f, 0x0a, 0x08, 0x4c, 0x73, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x69, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, @@ -1746,134 +1981,148 @@ var file_pola_proto_rawDesc = []byte{ 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x96, 0x02, 0x0a, - 0x06, 0x4c, 0x73, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x26, 0x0a, 0x0f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, - 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, - 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x61, 0x73, 0x6e, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x41, 0x73, 0x6e, 0x12, 0x19, 0x0a, 0x08, - 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x70, 0x12, 0x28, 0x0a, 0x10, 0x72, 0x65, 0x6d, 0x6f, 0x74, - 0x65, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x49, - 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x61, 0x73, 0x6e, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x73, 0x6e, - 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x69, 0x70, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x70, 0x12, 0x2d, 0x0a, - 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x74, - 0x72, 0x69, 0x63, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x17, 0x0a, 0x07, - 0x61, 0x64, 0x6a, 0x5f, 0x73, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x61, - 0x64, 0x6a, 0x53, 0x69, 0x64, 0x22, 0xd1, 0x02, 0x0a, 0x06, 0x4c, 0x73, 0x4e, 0x6f, 0x64, 0x65, - 0x12, 0x10, 0x0a, 0x03, 0x61, 0x73, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x61, - 0x73, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x49, 0x64, 0x12, - 0x20, 0x0a, 0x0c, 0x69, 0x73, 0x69, 0x73, 0x5f, 0x61, 0x72, 0x65, 0x61, 0x5f, 0x69, 0x64, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x73, 0x69, 0x73, 0x41, 0x72, 0x65, 0x61, 0x49, - 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, - 0x0a, 0x73, 0x72, 0x67, 0x62, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x0d, 0x52, 0x09, 0x73, 0x72, 0x67, 0x62, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x12, 0x19, 0x0a, 0x08, - 0x73, 0x72, 0x67, 0x62, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, - 0x73, 0x72, 0x67, 0x62, 0x45, 0x6e, 0x64, 0x12, 0x2e, 0x0a, 0x08, 0x6c, 0x73, 0x5f, 0x6c, 0x69, - 0x6e, 0x6b, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x73, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x07, - 0x6c, 0x73, 0x4c, 0x69, 0x6e, 0x6b, 0x73, 0x12, 0x36, 0x0a, 0x0b, 0x6c, 0x73, 0x5f, 0x70, 0x72, - 0x65, 0x66, 0x69, 0x78, 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x73, 0x50, 0x72, 0x65, - 0x66, 0x69, 0x78, 0x52, 0x0a, 0x6c, 0x73, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, 0x73, 0x12, - 0x38, 0x0a, 0x0c, 0x6c, 0x73, 0x5f, 0x73, 0x72, 0x76, 0x36, 0x5f, 0x73, 0x69, 0x64, 0x73, 0x18, - 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, - 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x73, 0x53, 0x72, 0x76, 0x36, 0x53, 0x49, 0x44, 0x52, 0x0a, 0x6c, - 0x73, 0x53, 0x72, 0x76, 0x36, 0x53, 0x69, 0x64, 0x73, 0x22, 0x4d, 0x0a, 0x03, 0x54, 0x45, 0x44, - 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x2e, 0x0a, 0x08, 0x6c, 0x73, 0x5f, 0x6e, - 0x6f, 0x64, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x52, - 0x07, 0x6c, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x17, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x53, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x22, 0x4a, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, - 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x08, 0x73, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x18, 0x0a, - 0x16, 0x47, 0x65, 0x74, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69, 0x73, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x51, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x53, 0x52, - 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x36, 0x0a, 0x0b, 0x73, 0x72, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, - 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0a, - 0x73, 0x72, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x22, 0x0f, 0x0a, 0x0d, 0x47, 0x65, - 0x74, 0x54, 0x45, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x58, 0x0a, 0x0e, 0x47, - 0x65, 0x74, 0x54, 0x45, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, - 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x65, - 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x2e, 0x0a, 0x08, 0x6c, 0x73, 0x5f, 0x6e, 0x6f, 0x64, 0x65, - 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, - 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x07, 0x6c, 0x73, - 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x2a, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, - 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x61, 0x64, 0x64, - 0x72, 0x22, 0x36, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, - 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, - 0x69, 0x73, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x2a, 0x67, 0x0a, 0x0c, 0x53, 0x52, 0x50, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x1a, 0x53, 0x52, 0x5f, - 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, - 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x52, 0x5f, - 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x58, 0x50, 0x4c, - 0x49, 0x43, 0x49, 0x54, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x53, 0x52, 0x5f, 0x50, 0x4f, 0x4c, - 0x49, 0x43, 0x59, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x59, 0x4e, 0x41, 0x4d, 0x49, 0x43, - 0x10, 0x02, 0x2a, 0x5b, 0x0a, 0x0c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x12, 0x1d, 0x0a, 0x19, 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, - 0x41, 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, - 0x00, 0x12, 0x16, 0x0a, 0x12, 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, - 0x54, 0x45, 0x5f, 0x44, 0x4f, 0x57, 0x4e, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x53, 0x45, 0x53, - 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55, 0x50, 0x10, 0x02, 0x2a, - 0x83, 0x01, 0x0a, 0x0a, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, - 0x0a, 0x17, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, - 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x4d, - 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x47, 0x50, 0x10, 0x01, - 0x12, 0x12, 0x0a, 0x0e, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, - 0x54, 0x45, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x54, - 0x59, 0x50, 0x45, 0x5f, 0x44, 0x45, 0x4c, 0x41, 0x59, 0x10, 0x03, 0x12, 0x18, 0x0a, 0x14, 0x4d, - 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x48, 0x4f, 0x50, 0x43, 0x4f, - 0x55, 0x4e, 0x54, 0x10, 0x04, 0x32, 0x96, 0x04, 0x0a, 0x0a, 0x50, 0x43, 0x45, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x12, 0x59, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x52, - 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, - 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x52, 0x50, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, - 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x59, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, - 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x59, 0x0a, 0x0e, 0x47, 0x65, - 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x22, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x53, 0x52, 0x50, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, - 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, - 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x06, 0x47, 0x65, 0x74, 0x54, 0x45, 0x44, 0x12, 0x1a, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, - 0x45, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x45, 0x44, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, - 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x24, - 0x5a, 0x22, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6e, 0x74, 0x74, - 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6f, 0x6c, 0x61, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x6f, 0x6c, - 0x61, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xa0, 0x01, 0x0a, + 0x0b, 0x53, 0x72, 0x76, 0x36, 0x45, 0x6e, 0x64, 0x58, 0x53, 0x49, 0x44, 0x12, 0x2b, 0x0a, 0x11, + 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, + 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x10, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, + 0x74, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x12, 0x24, 0x0a, 0x04, 0x73, 0x69, 0x64, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, + 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x49, 0x44, 0x52, 0x04, 0x73, 0x69, 0x64, 0x73, 0x12, + 0x3e, 0x0a, 0x0d, 0x73, 0x69, 0x64, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, + 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, 0x64, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, + 0x65, 0x52, 0x0c, 0x73, 0x69, 0x64, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x22, + 0xd5, 0x02, 0x0a, 0x06, 0x4c, 0x73, 0x4c, 0x69, 0x6e, 0x6b, 0x12, 0x26, 0x0a, 0x0f, 0x6c, 0x6f, + 0x63, 0x61, 0x6c, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, + 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x61, 0x73, 0x6e, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x41, 0x73, 0x6e, 0x12, + 0x19, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x69, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x49, 0x70, 0x12, 0x28, 0x0a, 0x10, 0x72, 0x65, + 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, + 0x65, 0x72, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x61, + 0x73, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, + 0x41, 0x73, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x69, 0x70, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x70, + 0x12, 0x2d, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, + 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, + 0x17, 0x0a, 0x07, 0x61, 0x64, 0x6a, 0x5f, 0x73, 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x06, 0x61, 0x64, 0x6a, 0x53, 0x69, 0x64, 0x12, 0x3d, 0x0a, 0x0e, 0x73, 0x72, 0x76, 0x36, + 0x5f, 0x65, 0x6e, 0x64, 0x5f, 0x78, 0x5f, 0x73, 0x69, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, + 0x72, 0x76, 0x36, 0x45, 0x6e, 0x64, 0x58, 0x53, 0x49, 0x44, 0x52, 0x0b, 0x73, 0x72, 0x76, 0x36, + 0x45, 0x6e, 0x64, 0x58, 0x53, 0x69, 0x64, 0x22, 0xd1, 0x02, 0x0a, 0x06, 0x4c, 0x73, 0x4e, 0x6f, + 0x64, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x73, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x03, 0x61, 0x73, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x5f, 0x69, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x49, + 0x64, 0x12, 0x20, 0x0a, 0x0c, 0x69, 0x73, 0x69, 0x73, 0x5f, 0x61, 0x72, 0x65, 0x61, 0x5f, 0x69, + 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x73, 0x69, 0x73, 0x41, 0x72, 0x65, + 0x61, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x1d, 0x0a, 0x0a, 0x73, 0x72, 0x67, 0x62, 0x5f, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x09, 0x73, 0x72, 0x67, 0x62, 0x42, 0x65, 0x67, 0x69, 0x6e, 0x12, 0x19, + 0x0a, 0x08, 0x73, 0x72, 0x67, 0x62, 0x5f, 0x65, 0x6e, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x07, 0x73, 0x72, 0x67, 0x62, 0x45, 0x6e, 0x64, 0x12, 0x2e, 0x0a, 0x08, 0x6c, 0x73, 0x5f, + 0x6c, 0x69, 0x6e, 0x6b, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x73, 0x4c, 0x69, 0x6e, 0x6b, + 0x52, 0x07, 0x6c, 0x73, 0x4c, 0x69, 0x6e, 0x6b, 0x73, 0x12, 0x36, 0x0a, 0x0b, 0x6c, 0x73, 0x5f, + 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x73, 0x50, + 0x72, 0x65, 0x66, 0x69, 0x78, 0x52, 0x0a, 0x6c, 0x73, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x65, + 0x73, 0x12, 0x38, 0x0a, 0x0c, 0x6c, 0x73, 0x5f, 0x73, 0x72, 0x76, 0x36, 0x5f, 0x73, 0x69, 0x64, + 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, + 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x73, 0x53, 0x72, 0x76, 0x36, 0x53, 0x49, 0x44, 0x52, + 0x0a, 0x6c, 0x73, 0x53, 0x72, 0x76, 0x36, 0x53, 0x69, 0x64, 0x73, 0x22, 0x4d, 0x0a, 0x03, 0x54, + 0x45, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x2e, 0x0a, 0x08, 0x6c, 0x73, + 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x73, 0x4e, 0x6f, 0x64, + 0x65, 0x52, 0x07, 0x6c, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x17, 0x0a, 0x15, 0x47, 0x65, + 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x22, 0x4a, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, + 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, + 0x18, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69, + 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x51, 0x0a, 0x17, 0x47, 0x65, 0x74, + 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x0b, 0x73, 0x72, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x52, 0x0a, 0x73, 0x72, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x22, 0x0f, 0x0a, 0x0d, + 0x47, 0x65, 0x74, 0x54, 0x45, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x58, 0x0a, + 0x0e, 0x47, 0x65, 0x74, 0x54, 0x45, 0x44, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x2e, 0x0a, 0x08, 0x6c, 0x73, 0x5f, 0x6e, 0x6f, + 0x64, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x52, 0x07, + 0x6c, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x2a, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x61, + 0x64, 0x64, 0x72, 0x22, 0x36, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, + 0x69, 0x73, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x09, 0x69, 0x73, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x2a, 0x67, 0x0a, 0x0c, 0x53, + 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x1a, 0x53, + 0x52, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, + 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x53, + 0x52, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x58, + 0x50, 0x4c, 0x49, 0x43, 0x49, 0x54, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x53, 0x52, 0x5f, 0x50, + 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x59, 0x4e, 0x41, 0x4d, + 0x49, 0x43, 0x10, 0x02, 0x2a, 0x5b, 0x0a, 0x0c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x12, 0x1d, 0x0a, 0x19, 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, + 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, + 0x44, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x53, + 0x54, 0x41, 0x54, 0x45, 0x5f, 0x44, 0x4f, 0x57, 0x4e, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x53, + 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x55, 0x50, 0x10, + 0x02, 0x2a, 0x83, 0x01, 0x0a, 0x0a, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x1b, 0x0a, 0x17, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x13, 0x0a, + 0x0f, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x47, 0x50, + 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x54, 0x45, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x45, 0x4c, 0x41, 0x59, 0x10, 0x03, 0x12, 0x18, 0x0a, + 0x14, 0x4d, 0x45, 0x54, 0x52, 0x49, 0x43, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x48, 0x4f, 0x50, + 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x04, 0x32, 0x96, 0x04, 0x0a, 0x0a, 0x50, 0x43, 0x45, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x59, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, + 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x52, 0x50, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x59, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x52, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x12, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, + 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, + 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x52, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x59, 0x0a, 0x0e, + 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x22, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, + 0x2e, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x53, 0x52, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x23, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x52, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, + 0x74, 0x53, 0x52, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x06, 0x47, 0x65, 0x74, 0x54, 0x45, 0x44, 0x12, + 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, + 0x74, 0x54, 0x45, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x45, 0x44, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x70, 0x6f, 0x6c, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x42, 0x24, 0x5a, 0x22, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6e, + 0x74, 0x74, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6f, 0x6c, 0x61, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, + 0x6f, 0x6c, 0x61, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1889,7 +2138,7 @@ func file_pola_proto_rawDescGZIP() []byte { } var file_pola_proto_enumTypes = make([]protoimpl.EnumInfo, 3) -var file_pola_proto_msgTypes = make([]protoimpl.MessageInfo, 25) +var file_pola_proto_msgTypes = make([]protoimpl.MessageInfo, 28) var file_pola_proto_goTypes = []interface{}{ (SRPolicyType)(0), // 0: api.pola.v1.SRPolicyType (SessionState)(0), // 1: api.pola.v1.SessionState @@ -1903,22 +2152,25 @@ var file_pola_proto_goTypes = []interface{}{ (*Session)(nil), // 9: api.pola.v1.Session (*SessionList)(nil), // 10: api.pola.v1.SessionList (*SRPolicyList)(nil), // 11: api.pola.v1.SRPolicyList - (*LsSrv6SID)(nil), // 12: api.pola.v1.LsSrv6SID - (*SID)(nil), // 13: api.pola.v1.SID - (*MultiTopoID)(nil), // 14: api.pola.v1.MultiTopoID - (*LsPrefix)(nil), // 15: api.pola.v1.LsPrefix - (*Metric)(nil), // 16: api.pola.v1.Metric - (*LsLink)(nil), // 17: api.pola.v1.LsLink - (*LsNode)(nil), // 18: api.pola.v1.LsNode - (*TED)(nil), // 19: api.pola.v1.TED - (*GetSessionListRequest)(nil), // 20: api.pola.v1.GetSessionListRequest - (*GetSessionListResponse)(nil), // 21: api.pola.v1.GetSessionListResponse - (*GetSRPolicyListRequest)(nil), // 22: api.pola.v1.GetSRPolicyListRequest - (*GetSRPolicyListResponse)(nil), // 23: api.pola.v1.GetSRPolicyListResponse - (*GetTEDRequest)(nil), // 24: api.pola.v1.GetTEDRequest - (*GetTEDResponse)(nil), // 25: api.pola.v1.GetTEDResponse - (*DeleteSessionRequest)(nil), // 26: api.pola.v1.DeleteSessionRequest - (*DeleteSessionResponse)(nil), // 27: api.pola.v1.DeleteSessionResponse + (*EndpointBehavior)(nil), // 12: api.pola.v1.EndpointBehavior + (*SidStructure)(nil), // 13: api.pola.v1.SidStructure + (*SID)(nil), // 14: api.pola.v1.SID + (*MultiTopoID)(nil), // 15: api.pola.v1.MultiTopoID + (*LsSrv6SID)(nil), // 16: api.pola.v1.LsSrv6SID + (*LsPrefix)(nil), // 17: api.pola.v1.LsPrefix + (*Metric)(nil), // 18: api.pola.v1.Metric + (*Srv6EndXSID)(nil), // 19: api.pola.v1.Srv6EndXSID + (*LsLink)(nil), // 20: api.pola.v1.LsLink + (*LsNode)(nil), // 21: api.pola.v1.LsNode + (*TED)(nil), // 22: api.pola.v1.TED + (*GetSessionListRequest)(nil), // 23: api.pola.v1.GetSessionListRequest + (*GetSessionListResponse)(nil), // 24: api.pola.v1.GetSessionListResponse + (*GetSRPolicyListRequest)(nil), // 25: api.pola.v1.GetSRPolicyListRequest + (*GetSRPolicyListResponse)(nil), // 26: api.pola.v1.GetSRPolicyListResponse + (*GetTEDRequest)(nil), // 27: api.pola.v1.GetTEDRequest + (*GetTEDResponse)(nil), // 28: api.pola.v1.GetTEDResponse + (*DeleteSessionRequest)(nil), // 29: api.pola.v1.DeleteSessionRequest + (*DeleteSessionResponse)(nil), // 30: api.pola.v1.DeleteSessionResponse } var file_pola_proto_depIdxs = []int32{ 0, // 0: api.pola.v1.SRPolicy.type:type_name -> api.pola.v1.SRPolicyType @@ -1929,34 +2181,39 @@ var file_pola_proto_depIdxs = []int32{ 1, // 5: api.pola.v1.Session.state:type_name -> api.pola.v1.SessionState 9, // 6: api.pola.v1.SessionList.sessions:type_name -> api.pola.v1.Session 4, // 7: api.pola.v1.SRPolicyList.sr_policies:type_name -> api.pola.v1.SRPolicy - 13, // 8: api.pola.v1.LsSrv6SID.sids:type_name -> api.pola.v1.SID - 14, // 9: api.pola.v1.LsSrv6SID.multi_topo_ids:type_name -> api.pola.v1.MultiTopoID - 2, // 10: api.pola.v1.Metric.type:type_name -> api.pola.v1.MetricType - 16, // 11: api.pola.v1.LsLink.metrics:type_name -> api.pola.v1.Metric - 17, // 12: api.pola.v1.LsNode.ls_links:type_name -> api.pola.v1.LsLink - 15, // 13: api.pola.v1.LsNode.ls_prefixes:type_name -> api.pola.v1.LsPrefix - 12, // 14: api.pola.v1.LsNode.ls_srv6_sids:type_name -> api.pola.v1.LsSrv6SID - 18, // 15: api.pola.v1.TED.ls_nodes:type_name -> api.pola.v1.LsNode - 9, // 16: api.pola.v1.GetSessionListResponse.sessions:type_name -> api.pola.v1.Session - 4, // 17: api.pola.v1.GetSRPolicyListResponse.sr_policies:type_name -> api.pola.v1.SRPolicy - 18, // 18: api.pola.v1.GetTEDResponse.ls_nodes:type_name -> api.pola.v1.LsNode - 5, // 19: api.pola.v1.PCEService.CreateSRPolicy:input_type -> api.pola.v1.CreateSRPolicyRequest - 7, // 20: api.pola.v1.PCEService.DeleteSRPolicy:input_type -> api.pola.v1.DeleteSRPolicyRequest - 20, // 21: api.pola.v1.PCEService.GetSessionList:input_type -> api.pola.v1.GetSessionListRequest - 22, // 22: api.pola.v1.PCEService.GetSRPolicyList:input_type -> api.pola.v1.GetSRPolicyListRequest - 24, // 23: api.pola.v1.PCEService.GetTED:input_type -> api.pola.v1.GetTEDRequest - 26, // 24: api.pola.v1.PCEService.DeleteSession:input_type -> api.pola.v1.DeleteSessionRequest - 6, // 25: api.pola.v1.PCEService.CreateSRPolicy:output_type -> api.pola.v1.CreateSRPolicyResponse - 8, // 26: api.pola.v1.PCEService.DeleteSRPolicy:output_type -> api.pola.v1.DeleteSRPolicyResponse - 21, // 27: api.pola.v1.PCEService.GetSessionList:output_type -> api.pola.v1.GetSessionListResponse - 23, // 28: api.pola.v1.PCEService.GetSRPolicyList:output_type -> api.pola.v1.GetSRPolicyListResponse - 25, // 29: api.pola.v1.PCEService.GetTED:output_type -> api.pola.v1.GetTEDResponse - 27, // 30: api.pola.v1.PCEService.DeleteSession:output_type -> api.pola.v1.DeleteSessionResponse - 25, // [25:31] is the sub-list for method output_type - 19, // [19:25] is the sub-list for method input_type - 19, // [19:19] is the sub-list for extension type_name - 19, // [19:19] is the sub-list for extension extendee - 0, // [0:19] is the sub-list for field type_name + 14, // 8: api.pola.v1.LsSrv6SID.sids:type_name -> api.pola.v1.SID + 12, // 9: api.pola.v1.LsSrv6SID.endpoint_behavior:type_name -> api.pola.v1.EndpointBehavior + 13, // 10: api.pola.v1.LsSrv6SID.sid_structure:type_name -> api.pola.v1.SidStructure + 15, // 11: api.pola.v1.LsSrv6SID.multi_topo_ids:type_name -> api.pola.v1.MultiTopoID + 2, // 12: api.pola.v1.Metric.type:type_name -> api.pola.v1.MetricType + 14, // 13: api.pola.v1.Srv6EndXSID.sids:type_name -> api.pola.v1.SID + 13, // 14: api.pola.v1.Srv6EndXSID.sid_structure:type_name -> api.pola.v1.SidStructure + 18, // 15: api.pola.v1.LsLink.metrics:type_name -> api.pola.v1.Metric + 19, // 16: api.pola.v1.LsLink.srv6_end_x_sid:type_name -> api.pola.v1.Srv6EndXSID + 20, // 17: api.pola.v1.LsNode.ls_links:type_name -> api.pola.v1.LsLink + 17, // 18: api.pola.v1.LsNode.ls_prefixes:type_name -> api.pola.v1.LsPrefix + 16, // 19: api.pola.v1.LsNode.ls_srv6_sids:type_name -> api.pola.v1.LsSrv6SID + 21, // 20: api.pola.v1.TED.ls_nodes:type_name -> api.pola.v1.LsNode + 9, // 21: api.pola.v1.GetSessionListResponse.sessions:type_name -> api.pola.v1.Session + 4, // 22: api.pola.v1.GetSRPolicyListResponse.sr_policies:type_name -> api.pola.v1.SRPolicy + 21, // 23: api.pola.v1.GetTEDResponse.ls_nodes:type_name -> api.pola.v1.LsNode + 5, // 24: api.pola.v1.PCEService.CreateSRPolicy:input_type -> api.pola.v1.CreateSRPolicyRequest + 7, // 25: api.pola.v1.PCEService.DeleteSRPolicy:input_type -> api.pola.v1.DeleteSRPolicyRequest + 23, // 26: api.pola.v1.PCEService.GetSessionList:input_type -> api.pola.v1.GetSessionListRequest + 25, // 27: api.pola.v1.PCEService.GetSRPolicyList:input_type -> api.pola.v1.GetSRPolicyListRequest + 27, // 28: api.pola.v1.PCEService.GetTED:input_type -> api.pola.v1.GetTEDRequest + 29, // 29: api.pola.v1.PCEService.DeleteSession:input_type -> api.pola.v1.DeleteSessionRequest + 6, // 30: api.pola.v1.PCEService.CreateSRPolicy:output_type -> api.pola.v1.CreateSRPolicyResponse + 8, // 31: api.pola.v1.PCEService.DeleteSRPolicy:output_type -> api.pola.v1.DeleteSRPolicyResponse + 24, // 32: api.pola.v1.PCEService.GetSessionList:output_type -> api.pola.v1.GetSessionListResponse + 26, // 33: api.pola.v1.PCEService.GetSRPolicyList:output_type -> api.pola.v1.GetSRPolicyListResponse + 28, // 34: api.pola.v1.PCEService.GetTED:output_type -> api.pola.v1.GetTEDResponse + 30, // 35: api.pola.v1.PCEService.DeleteSession:output_type -> api.pola.v1.DeleteSessionResponse + 30, // [30:36] is the sub-list for method output_type + 24, // [24:30] is the sub-list for method input_type + 24, // [24:24] is the sub-list for extension type_name + 24, // [24:24] is the sub-list for extension extendee + 0, // [0:24] is the sub-list for field type_name } func init() { file_pola_proto_init() } @@ -2074,7 +2331,7 @@ func file_pola_proto_init() { } } file_pola_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LsSrv6SID); i { + switch v := v.(*EndpointBehavior); i { case 0: return &v.state case 1: @@ -2086,7 +2343,7 @@ func file_pola_proto_init() { } } file_pola_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SID); i { + switch v := v.(*SidStructure); i { case 0: return &v.state case 1: @@ -2098,7 +2355,7 @@ func file_pola_proto_init() { } } file_pola_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*MultiTopoID); i { + switch v := v.(*SID); i { case 0: return &v.state case 1: @@ -2110,7 +2367,7 @@ func file_pola_proto_init() { } } file_pola_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LsPrefix); i { + switch v := v.(*MultiTopoID); i { case 0: return &v.state case 1: @@ -2122,7 +2379,7 @@ func file_pola_proto_init() { } } file_pola_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Metric); i { + switch v := v.(*LsSrv6SID); i { case 0: return &v.state case 1: @@ -2134,7 +2391,7 @@ func file_pola_proto_init() { } } file_pola_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LsLink); i { + switch v := v.(*LsPrefix); i { case 0: return &v.state case 1: @@ -2146,7 +2403,7 @@ func file_pola_proto_init() { } } file_pola_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*LsNode); i { + switch v := v.(*Metric); i { case 0: return &v.state case 1: @@ -2158,7 +2415,7 @@ func file_pola_proto_init() { } } file_pola_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TED); i { + switch v := v.(*Srv6EndXSID); i { case 0: return &v.state case 1: @@ -2170,7 +2427,7 @@ func file_pola_proto_init() { } } file_pola_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetSessionListRequest); i { + switch v := v.(*LsLink); i { case 0: return &v.state case 1: @@ -2182,7 +2439,7 @@ func file_pola_proto_init() { } } file_pola_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetSessionListResponse); i { + switch v := v.(*LsNode); i { case 0: return &v.state case 1: @@ -2194,7 +2451,7 @@ func file_pola_proto_init() { } } file_pola_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetSRPolicyListRequest); i { + switch v := v.(*TED); i { case 0: return &v.state case 1: @@ -2206,7 +2463,7 @@ func file_pola_proto_init() { } } file_pola_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetSRPolicyListResponse); i { + switch v := v.(*GetSessionListRequest); i { case 0: return &v.state case 1: @@ -2218,7 +2475,7 @@ func file_pola_proto_init() { } } file_pola_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetTEDRequest); i { + switch v := v.(*GetSessionListResponse); i { case 0: return &v.state case 1: @@ -2230,7 +2487,7 @@ func file_pola_proto_init() { } } file_pola_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetTEDResponse); i { + switch v := v.(*GetSRPolicyListRequest); i { case 0: return &v.state case 1: @@ -2242,7 +2499,7 @@ func file_pola_proto_init() { } } file_pola_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteSessionRequest); i { + switch v := v.(*GetSRPolicyListResponse); i { case 0: return &v.state case 1: @@ -2254,6 +2511,42 @@ func file_pola_proto_init() { } } file_pola_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetTEDRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pola_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetTEDResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pola_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteSessionRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pola_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DeleteSessionResponse); i { case 0: return &v.state @@ -2272,7 +2565,7 @@ func file_pola_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_pola_proto_rawDesc, NumEnums: 3, - NumMessages: 25, + NumMessages: 28, NumExtensions: 0, NumServices: 1, }, diff --git a/api/pola/v1/pola.proto b/api/pola/v1/pola.proto index 797494b7..100f6346 100644 --- a/api/pola/v1/pola.proto +++ b/api/pola/v1/pola.proto @@ -85,18 +85,32 @@ message SRPolicyList { repeated SRPolicy sr_policies = 1; } -message LsSrv6SID { - repeated SID sids = 1; - uint32 endpoint_behavior = 2; - repeated MultiTopoID multi_topo_ids = 3; +message EndpointBehavior { + uint32 behavior = 1; + uint32 flags = 2; + uint32 algorithm = 3; +} + +message SidStructure { + uint32 local_block = 1; + uint32 local_node = 2; + uint32 local_func = 3; + uint32 local_arg = 4; } message SID { - string sid = 1; + string sid = 1; } message MultiTopoID { - uint32 multi_topo_id = 1; + uint32 multi_topo_id = 1; +} + +message LsSrv6SID { + repeated SID sids = 1; + EndpointBehavior endpoint_behavior = 2; + SidStructure sid_structure = 3; + repeated MultiTopoID multi_topo_ids = 4; } message LsPrefix { @@ -117,6 +131,12 @@ message Metric { uint32 value = 2; } +message Srv6EndXSID { + uint32 endpoint_behavior = 1; + repeated SID sids = 2; + SidStructure sid_structure = 3; +} + message LsLink { string local_router_id = 1; uint32 local_asn = 2; @@ -126,6 +146,7 @@ message LsLink { string remote_ip = 6; repeated Metric metrics = 7; uint32 adj_sid = 8; + Srv6EndXSID srv6_end_x_sid = 9; } message LsNode { diff --git a/cmd/pola/grpc/grpc_client.go b/cmd/pola/grpc/grpc_client.go index 05033135..bdd3e315 100644 --- a/cmd/pola/grpc/grpc_client.go +++ b/cmd/pola/grpc/grpc_client.go @@ -246,10 +246,6 @@ func createSrv6SID(lsNode *table.LsNode, srv6SID *pb.LsSrv6SID) (*table.LsSrv6SI lsSrv6SID := table.NewLsSrv6SID(lsNode) lsSrv6SID.EndpointBehavior = srv6SID.GetEndpointBehavior() - lsSrv6SID.ServiceType = srv6SID.GetServiceType() - lsSrv6SID.TrafficType = srv6SID.GetTrafficType() - lsSrv6SID.OpaqueType = srv6SID.GetOpaqueType() - lsSrv6SID.Value = srv6SID.GetValue() for _, sid := range srv6SID.GetSids() { lsSrv6SID.Sids = append(lsSrv6SID.Sids, sid.GetSid()) } From a1be49738691dc1bf38193c99b4d0eff836da6b4 Mon Sep 17 00:00:00 2001 From: k1yoto Date: Sun, 22 Jun 2025 08:35:42 +0000 Subject: [PATCH 35/87] feat(srv6): add SRv6 End.X SID and SID Structure support --- cmd/pola/grpc/grpc_client.go | 36 +++++++++++++++++++++- internal/pkg/gobgp/interface.go | 35 ++++++++++++++++++--- internal/pkg/table/ted.go | 54 ++++++++++++++++++++++++++------- pkg/server/grpc_server.go | 38 ++++++++++++++++++++--- 4 files changed, 142 insertions(+), 21 deletions(-) diff --git a/cmd/pola/grpc/grpc_client.go b/cmd/pola/grpc/grpc_client.go index bdd3e315..a57afe22 100644 --- a/cmd/pola/grpc/grpc_client.go +++ b/cmd/pola/grpc/grpc_client.go @@ -224,6 +224,13 @@ func createLsLink(localNode, remoteNode *table.LsNode, link *pb.LsLink) (*table. } lsLink.Metrics = append(lsLink.Metrics, metric) } + if link.GetSrv6EndXSid() != nil { + srv6EndXSID, err := createSrv6EndXSID(link.GetSrv6EndXSid()) + if err != nil { + return nil, err + } + lsLink.Srv6EndXSID = srv6EndXSID + } return lsLink, nil } @@ -242,10 +249,28 @@ func createMetric(metricInfo *pb.Metric) (*table.Metric, error) { } } +func createSrv6EndXSID(srv6EndXSID *pb.Srv6EndXSID) (*table.Srv6EndXSID, error) { + lsSrv6EndXSID := &table.Srv6EndXSID{ + EndpointBehavior: uint16(srv6EndXSID.EndpointBehavior), + Sids: []string{}, + Srv6SIDStructure: table.SIDStructure{ + LocalBlock: uint8(srv6EndXSID.GetSidStructure().GetLocalBlock()), + LocalNode: uint8(srv6EndXSID.GetSidStructure().GetLocalNode()), + LocalFunc: uint8(srv6EndXSID.GetSidStructure().GetLocalFunc()), + LocalArg: uint8(srv6EndXSID.GetSidStructure().GetLocalArg()), + }, + } + + for _, sid := range srv6EndXSID.GetSids() { + lsSrv6EndXSID.Sids = append(lsSrv6EndXSID.Sids, sid.GetSid()) + } + + return lsSrv6EndXSID, nil +} + func createSrv6SID(lsNode *table.LsNode, srv6SID *pb.LsSrv6SID) (*table.LsSrv6SID, error) { lsSrv6SID := table.NewLsSrv6SID(lsNode) - lsSrv6SID.EndpointBehavior = srv6SID.GetEndpointBehavior() for _, sid := range srv6SID.GetSids() { lsSrv6SID.Sids = append(lsSrv6SID.Sids, sid.GetSid()) } @@ -253,5 +278,14 @@ func createSrv6SID(lsNode *table.LsNode, srv6SID *pb.LsSrv6SID) (*table.LsSrv6SI lsSrv6SID.MultiTopoIDs = append(lsSrv6SID.MultiTopoIDs, topoID.GetMultiTopoId()) } + lsSrv6SID.EndpointBehavior.Behavior = uint16(srv6SID.GetEndpointBehavior().GetBehavior()) + lsSrv6SID.EndpointBehavior.Flags = uint8(srv6SID.GetEndpointBehavior().GetFlags()) + lsSrv6SID.EndpointBehavior.Algorithm = uint8(srv6SID.GetEndpointBehavior().GetAlgorithm()) + + lsSrv6SID.SIDStructure.LocalBlock = uint8(srv6SID.GetSidStructure().GetLocalBlock()) + lsSrv6SID.SIDStructure.LocalNode = uint8(srv6SID.GetSidStructure().GetLocalNode()) + lsSrv6SID.SIDStructure.LocalFunc = uint8(srv6SID.GetSidStructure().GetLocalFunc()) + lsSrv6SID.SIDStructure.LocalArg = uint8(srv6SID.GetSidStructure().GetLocalArg()) + return lsSrv6SID, nil } diff --git a/internal/pkg/gobgp/interface.go b/internal/pkg/gobgp/interface.go index 3b02e9e8..c89743df 100644 --- a/internal/pkg/gobgp/interface.go +++ b/internal/pkg/gobgp/interface.go @@ -232,6 +232,22 @@ func getLsLinkNLRI(typedLinkStateNLRI *api.LsLinkNLRI, pathAttrs []*anypb.Any) ( } lsLink.AdjSid = bgplsAttr.GetLink().GetSrAdjacencySid() + + // handle SRv6 SID TLV + var srv6EndXSID *api.LsSrv6EndXSID + srv6EndXSID = bgplsAttr.GetLink().GetSrv6EndXSid() + if srv6EndXSID != nil { + lsLink.Srv6EndXSID = &table.Srv6EndXSID{ + EndpointBehavior: uint16(srv6EndXSID.EndpointBehavior), + Sids: srv6EndXSID.Sids, + Srv6SIDStructure: table.SIDStructure{ + LocalBlock: uint8(srv6EndXSID.Srv6SidStructure.GetLocalBlock()), + LocalNode: uint8(srv6EndXSID.Srv6SidStructure.GetLocalNode()), + LocalFunc: uint8(srv6EndXSID.Srv6SidStructure.GetLocalFunc()), + LocalArg: uint8(srv6EndXSID.Srv6SidStructure.GetLocalArg()), + }, + } + } } return lsLink, nil @@ -316,7 +332,8 @@ func getLsPrefix(lsNlri *api.LsAddrPrefix, sidIndex uint32) (*table.LsPrefix, er func getLsSrv6SIDNLRIList(pathAttrs []*anypb.Any) ([]table.TEDElem, error) { var lsSrv6SIDList []table.TEDElem - var endpointBehavior uint32 + var endpointBehavior *api.LsSrv6EndpointBehavior + var srv6SIDStructure *api.LsSrv6SIDStructure for _, pathAttr := range pathAttrs { typedPathAttr, err := pathAttr.UnmarshalNew() @@ -326,7 +343,9 @@ func getLsSrv6SIDNLRIList(pathAttrs []*anypb.Any) ([]table.TEDElem, error) { switch typedPathAttr := typedPathAttr.(type) { case *api.LsAttribute: - // TODO: Handle LsAttribute for SRv6 SID + // handle LsAttribute for SRv6 SID + srv6SIDStructure = typedPathAttr.GetSrv6Sid().GetSrv6SidStructure() + endpointBehavior = typedPathAttr.GetSrv6Sid().GetSrv6EndpointBehavior() case *api.MpReachNLRIAttribute: for _, nlri := range typedPathAttr.GetNlris() { typedNLRI, err := nlri.UnmarshalNew() @@ -334,7 +353,7 @@ func getLsSrv6SIDNLRIList(pathAttrs []*anypb.Any) ([]table.TEDElem, error) { return nil, fmt.Errorf("failed to unmarshal NLRI: %w", err) } if lsNLRI, ok := typedNLRI.(*api.LsAddrPrefix); ok { - lsSrv6SID, err := getLsSrv6SIDNLRI(lsNLRI, endpointBehavior) + lsSrv6SID, err := getLsSrv6SIDNLRI(lsNLRI, endpointBehavior, srv6SIDStructure) if err != nil { return nil, fmt.Errorf("failed to process LS SRv6 SID NLRI: %w", err) } @@ -349,7 +368,7 @@ func getLsSrv6SIDNLRIList(pathAttrs []*anypb.Any) ([]table.TEDElem, error) { } // getLsSrv6SIDNLRI processes the LS SRv6 SID NLRI and returns a corresponding LsSrv6SID. -func getLsSrv6SIDNLRI(lsNLRI *api.LsAddrPrefix, endpointBehavior uint32) (*table.LsSrv6SID, error) { +func getLsSrv6SIDNLRI(lsNLRI *api.LsAddrPrefix, endpointBehavior *api.LsSrv6EndpointBehavior, srv6SIDStructure *api.LsSrv6SIDStructure) (*table.LsSrv6SID, error) { srv6NLRI, err := lsNLRI.GetNlri().UnmarshalNew() if err != nil { return nil, fmt.Errorf("failed to unmarshal LS NLRI: %w", err) @@ -366,7 +385,13 @@ func getLsSrv6SIDNLRI(lsNLRI *api.LsAddrPrefix, endpointBehavior uint32) (*table localNode := table.NewLsNode(localNodeASN, localNodeID) lsSrv6SID := table.NewLsSrv6SID(localNode) - lsSrv6SID.EndpointBehavior = endpointBehavior + lsSrv6SID.SIDStructure.LocalBlock = uint8(srv6SIDStructure.GetLocalBlock()) + lsSrv6SID.SIDStructure.LocalNode = uint8(srv6SIDStructure.GetLocalNode()) + lsSrv6SID.SIDStructure.LocalFunc = uint8(srv6SIDStructure.GetLocalFunc()) + lsSrv6SID.SIDStructure.LocalArg = uint8(srv6SIDStructure.GetLocalArg()) + lsSrv6SID.EndpointBehavior.Behavior = uint16(endpointBehavior.GetEndpointBehavior()) + lsSrv6SID.EndpointBehavior.Flags = uint8(endpointBehavior.GetFlags()) + lsSrv6SID.EndpointBehavior.Algorithm = uint8(endpointBehavior.GetAlgorithm()) lsSrv6SID.Sids = srv6SIDs lsSrv6SID.MultiTopoIDs = multiTopoIDs diff --git a/internal/pkg/table/ted.go b/internal/pkg/table/ted.go index 88073625..7be62763 100644 --- a/internal/pkg/table/ted.go +++ b/internal/pkg/table/ted.go @@ -48,11 +48,22 @@ func (ted *LsTED) Print() { fmt.Printf(" %s: %d\n", metric.Type.String(), metric.Value) } fmt.Printf(" Adj-SID: %d\n", link.AdjSid) + fmt.Printf(" SRv6 End.X SID:\n") + fmt.Printf(" EndpointBehavior: %x\n", link.Srv6EndXSID.EndpointBehavior) + fmt.Printf(" SIDs: %v\n", link.Srv6EndXSID.Sids) + fmt.Printf(" SID Structure: Block: %d, Node: %d, Func: %d, Arg: %d\n", + link.Srv6EndXSID.Srv6SIDStructure.LocalBlock, + link.Srv6EndXSID.Srv6SIDStructure.LocalNode, + link.Srv6EndXSID.Srv6SIDStructure.LocalFunc, + link.Srv6EndXSID.Srv6SIDStructure.LocalArg) } fmt.Printf(" SRv6 SIDs:\n") for _, srv6SID := range node.SRv6SIDs { fmt.Printf(" SIDs: %v\n", srv6SID.Sids) - fmt.Printf(" EndpointBehavior: %d\n", srv6SID.EndpointBehavior) + fmt.Printf(" Block: %d, Node: %d, Func: %d, Arg: %d\n", srv6SID.SIDStructure.LocalBlock, + srv6SID.SIDStructure.LocalNode, srv6SID.SIDStructure.LocalFunc, srv6SID.SIDStructure.LocalArg) + fmt.Printf(" EndpointBehavior: %x, Flags: %d, Algorithm: %d\n", srv6SID.EndpointBehavior.Behavior, + srv6SID.EndpointBehavior.Flags, srv6SID.EndpointBehavior.Algorithm) fmt.Printf(" MultiTopoIDs: %v\n", srv6SID.MultiTopoIDs) } @@ -134,12 +145,13 @@ func (n *LsNode) AddLink(link *LsLink) { } type LsLink struct { - LocalNode *LsNode // Primary key, in MP_REACH_NLRI Attr - RemoteNode *LsNode // Primary key, in MP_REACH_NLRI Attr - LocalIP netip.Addr // In MP_REACH_NLRI Attr - RemoteIP netip.Addr // In MP_REACH_NLRI Attr - Metrics []*Metric // In BGP-LS Attr - AdjSid uint32 // In BGP-LS Attr + LocalNode *LsNode // Primary key, in MP_REACH_NLRI Attr + RemoteNode *LsNode // Primary key, in MP_REACH_NLRI Attr + LocalIP netip.Addr // In MP_REACH_NLRI Attr + RemoteIP netip.Addr // In MP_REACH_NLRI Attr + Metrics []*Metric // In BGP-LS Attr + AdjSid uint32 // In BGP-LS Attr + Srv6EndXSID *Srv6EndXSID // In BGP-LS Attr } func NewLsLink(localNode *LsNode, remoteNode *LsNode) *LsLink { @@ -212,11 +224,25 @@ func (lp *LsPrefix) UpdateTED(ted *LsTED) { localNode.Prefixes = append(localNode.Prefixes, lp) } +type SIDStructure struct { + LocalBlock uint8 + LocalNode uint8 + LocalFunc uint8 + LocalArg uint8 +} + +type EndpointBehavior struct { + Behavior uint16 + Flags uint8 + Algorithm uint8 +} + type LsSrv6SID struct { - LocalNode *LsNode // primary key, in MP_REACH_NLRI Attr - Sids []string // in LsSrv6SID Attr - EndpointBehavior uint32 // in BGP-LS Attr - MultiTopoIDs []uint32 // in LsSrv6SID Attr + LocalNode *LsNode // primary key, in MP_REACH_NLRI Attr + Sids []string // in LsSrv6SID Attr + EndpointBehavior EndpointBehavior // in BGP-LS Attr + SIDStructure SIDStructure // in BGP-LS Attr + MultiTopoIDs []uint32 // in LsSrv6SID Attr } func NewLsSrv6SID(node *LsNode) *LsSrv6SID { @@ -280,3 +306,9 @@ func (m MetricType) String() string { return "METRIC_TYPE_UNSPECIFIED" } } + +type Srv6EndXSID struct { + EndpointBehavior uint16 + Sids []string + Srv6SIDStructure SIDStructure +} diff --git a/pkg/server/grpc_server.go b/pkg/server/grpc_server.go index b768c992..00279ec8 100644 --- a/pkg/server/grpc_server.go +++ b/pkg/server/grpc_server.go @@ -232,7 +232,7 @@ var validator = map[ValidationKind]func(policy *pb.SRPolicy, asn uint32) bool{ policy.SrcRouterId != "" && policy.DstRouterId != "" }, - ValidationKind("AddWithoutLinkState"): func(policy *pb.SRPolicy, asn uint32) bool { + ValidationKind("AddDisablePathCompute"): func(policy *pb.SRPolicy, asn uint32) bool { return policy.PcepSessionAddr != nil && len(policy.SrcAddr) > 0 && len(policy.DstAddr) > 0 && @@ -419,6 +419,23 @@ func (s *APIServer) GetTED(ctx context.Context, req *pb.GetTEDRequest) (*pb.GetT link.Metrics = append(link.Metrics, metric) } + link.Srv6EndXSid = &pb.Srv6EndXSID{ + EndpointBehavior: uint32(lsLink.Srv6EndXSID.EndpointBehavior), + Sids: make([]*pb.SID, 0, len(lsLink.Srv6EndXSID.Sids)), + SidStructure: &pb.SidStructure{ + LocalBlock: uint32(lsLink.Srv6EndXSID.Srv6SIDStructure.LocalBlock), + LocalNode: uint32(lsLink.Srv6EndXSID.Srv6SIDStructure.LocalNode), + LocalFunc: uint32(lsLink.Srv6EndXSID.Srv6SIDStructure.LocalFunc), + LocalArg: uint32(lsLink.Srv6EndXSID.Srv6SIDStructure.LocalArg), + }, + } + + for _, sid := range lsLink.Srv6EndXSID.Sids { + link.Srv6EndXSid.Sids = append(link.Srv6EndXSid.Sids, &pb.SID{ + Sid: sid, + }) + } + node.LsLinks = append(node.LsLinks, link) } @@ -433,9 +450,8 @@ func (s *APIServer) GetTED(ctx context.Context, req *pb.GetTEDRequest) (*pb.GetT for _, lsSrv6SID := range lsNode.SRv6SIDs { srv6SID := &pb.LsSrv6SID{ - EndpointBehavior: lsSrv6SID.EndpointBehavior, - Sids: make([]*pb.SID, 0, len(lsSrv6SID.Sids)), - MultiTopoIds: make([]*pb.MultiTopoID, 0, len(lsSrv6SID.MultiTopoIDs)), + Sids: make([]*pb.SID, 0, len(lsSrv6SID.Sids)), + MultiTopoIds: make([]*pb.MultiTopoID, 0, len(lsSrv6SID.MultiTopoIDs)), } for _, sid := range lsSrv6SID.Sids { @@ -449,6 +465,20 @@ func (s *APIServer) GetTED(ctx context.Context, req *pb.GetTEDRequest) (*pb.GetT MultiTopoId: topoID, }) } + + srv6SID.EndpointBehavior = &pb.EndpointBehavior{ + Behavior: uint32(lsSrv6SID.EndpointBehavior.Behavior), + Flags: uint32(lsSrv6SID.EndpointBehavior.Flags), + Algorithm: uint32(lsSrv6SID.EndpointBehavior.Algorithm), + } + + srv6SID.SidStructure = &pb.SidStructure{ + LocalBlock: uint32(lsSrv6SID.SIDStructure.LocalBlock), + LocalNode: uint32(lsSrv6SID.SIDStructure.LocalNode), + LocalFunc: uint32(lsSrv6SID.SIDStructure.LocalFunc), + LocalArg: uint32(lsSrv6SID.SIDStructure.LocalArg), + } + node.LsSrv6Sids = append(node.LsSrv6Sids, srv6SID) } From 8355759e574d8d040ee45d8f6ee167362d480219 Mon Sep 17 00:00:00 2001 From: k1yoto Date: Sun, 22 Jun 2025 08:59:29 +0000 Subject: [PATCH 36/87] feat(srv6): enhance SRv6 Segment creation and node support --- internal/pkg/table/sr_policy.go | 59 +++++++++++++++++++++++++++++++++ internal/pkg/table/ted.go | 19 +++++++++-- 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/internal/pkg/table/sr_policy.go b/internal/pkg/table/sr_policy.go index 6c834e45..0fa0db7c 100644 --- a/internal/pkg/table/sr_policy.go +++ b/internal/pkg/table/sr_policy.go @@ -114,6 +114,28 @@ const ( BehaviorUA uint16 = 0x0039 ) +func BehaviorToString(behavior uint16) string { + switch behavior { + case BehaviorReserved: + return "RESERVED" + case BehaviorEND: + return "END" + case BehaviorENDX: + return "ENDX" + case BehaviorUN: + return "UN" + case BehaviorUA: + return "UA" + default: + return "UNKNOWN" + } +} + +const ( + FirstSRv6SIDIndex = 0 // Index for first SRv6 SID in array + FirstSIDIndex = 0 // Index for first SID in Sids array +) + type SegmentSRv6 struct { Sid netip.Addr LocalAddr netip.Addr @@ -148,6 +170,43 @@ func NewSegmentSRv6(sid netip.Addr) SegmentSRv6 { } } +func NewSegmentSRv6WithNodeInfo(sid netip.Addr, n *LsNode) (SegmentSRv6, error) { + seg := SegmentSRv6{ + Sid: sid, + } + + if len(n.SRv6SIDs) == 0 || len(n.SRv6SIDs[FirstSRv6SIDIndex].Sids) == 0 { + return seg, errors.New("no SRv6 SIDs available") + } + + addr, err := netip.ParseAddr(n.SRv6SIDs[FirstSRv6SIDIndex].Sids[FirstSIDIndex]) + if err != nil { + return seg, err + } + seg.LocalAddr = addr + + for _, srv6SID := range n.SRv6SIDs { + if len(srv6SID.Sids) > 0 { + seg.Structure = []uint8{ + srv6SID.SIDStructure.LocalBlock, + srv6SID.SIDStructure.LocalNode, + srv6SID.SIDStructure.LocalFunc, + srv6SID.SIDStructure.LocalArg, + } + + switch srv6SID.EndpointBehavior.Behavior { + case BehaviorUN, BehaviorUA: + seg.USid = true + default: + seg.USid = false + } + break + } + } + + return seg, nil +} + type SegmentSRMPLS struct { Sid uint32 } diff --git a/internal/pkg/table/ted.go b/internal/pkg/table/ted.go index 7be62763..4f5fd9ee 100644 --- a/internal/pkg/table/ted.go +++ b/internal/pkg/table/ted.go @@ -49,7 +49,7 @@ func (ted *LsTED) Print() { } fmt.Printf(" Adj-SID: %d\n", link.AdjSid) fmt.Printf(" SRv6 End.X SID:\n") - fmt.Printf(" EndpointBehavior: %x\n", link.Srv6EndXSID.EndpointBehavior) + fmt.Printf(" EndpointBehavior: %s\n", BehaviorToString(link.Srv6EndXSID.EndpointBehavior)) fmt.Printf(" SIDs: %v\n", link.Srv6EndXSID.Sids) fmt.Printf(" SID Structure: Block: %d, Node: %d, Func: %d, Arg: %d\n", link.Srv6EndXSID.Srv6SIDStructure.LocalBlock, @@ -62,7 +62,7 @@ func (ted *LsTED) Print() { fmt.Printf(" SIDs: %v\n", srv6SID.Sids) fmt.Printf(" Block: %d, Node: %d, Func: %d, Arg: %d\n", srv6SID.SIDStructure.LocalBlock, srv6SID.SIDStructure.LocalNode, srv6SID.SIDStructure.LocalFunc, srv6SID.SIDStructure.LocalArg) - fmt.Printf(" EndpointBehavior: %x, Flags: %d, Algorithm: %d\n", srv6SID.EndpointBehavior.Behavior, + fmt.Printf(" EndpointBehavior: %s, Flags: %d, Algorithm: %d\n", BehaviorToString(srv6SID.EndpointBehavior.Behavior), srv6SID.EndpointBehavior.Flags, srv6SID.EndpointBehavior.Algorithm) fmt.Printf(" MultiTopoIDs: %v\n", srv6SID.MultiTopoIDs) } @@ -108,7 +108,20 @@ func (n *LsNode) NodeSegment() (Segment, error) { return seg, nil } } - // TODO: for SRv6 Segment + // for SRv6 Segment + for _, srv6SID := range n.SRv6SIDs { + if len(srv6SID.Sids) > 0 { + addr, err := netip.ParseAddr(srv6SID.Sids[FirstSIDIndex]) + if err != nil { + return nil, err + } + seg, err := NewSegmentSRv6WithNodeInfo(addr, n) + if err != nil { + return nil, err + } + return seg, nil + } + } return nil, errors.New("node doesn't have a Node SID") } From 86ef7f80cbe52e57b81fda3a992276b98a3d74d6 Mon Sep 17 00:00:00 2001 From: k1yoto Date: Sun, 22 Jun 2025 09:00:48 +0000 Subject: [PATCH 37/87] feat(cspf): implement dynamic path computation from TED --- pkg/server/server.go | 2 +- pkg/server/session.go | 153 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 153 insertions(+), 2 deletions(-) diff --git a/pkg/server/server.go b/pkg/server/server.go index 49a71d01..4d803fe9 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -118,7 +118,7 @@ func (s *Server) Serve(address string, port string, usidMode bool) error { if err != nil { return fmt.Errorf("failed to parse remote address %s: %w", tcpConn.RemoteAddr().String(), err) } - ss := NewSession(sessionID, peerAddrPort.Addr(), tcpConn, s.logger) + ss := NewSession(sessionID, peerAddrPort.Addr(), tcpConn, s.logger, s.ted) ss.logger.Info("start PCEP session") s.sessionList = append(s.sessionList, ss) diff --git a/pkg/server/session.go b/pkg/server/session.go index 74d5c6de..034b6e29 100644 --- a/pkg/server/session.go +++ b/pkg/server/session.go @@ -6,11 +6,13 @@ package server import ( + "errors" "fmt" "net" "net/netip" "time" + "github.com/nttcom/pola/internal/pkg/cspf" "github.com/nttcom/pola/internal/pkg/table" "github.com/nttcom/pola/pkg/packet/pcep" @@ -28,9 +30,10 @@ type Session struct { keepAlive uint8 pccType pcep.PccType pccCapabilities []pcep.CapabilityInterface + ted *table.LsTED } -func NewSession(sessionID uint8, peerAddr netip.Addr, tcpConn *net.TCPConn, logger *zap.Logger) *Session { +func NewSession(sessionID uint8, peerAddr netip.Addr, tcpConn *net.TCPConn, logger *zap.Logger, ted *table.LsTED) *Session { return &Session{ sessionID: sessionID, isSynced: false, @@ -39,6 +42,7 @@ func NewSession(sessionID uint8, peerAddr netip.Addr, tcpConn *net.TCPConn, logg pccType: pcep.RFCCompliant, peerAddr: peerAddr, tcpConn: tcpConn, + ted: ted, } } @@ -273,7 +277,21 @@ func (ss *Session) handlePCRpt(length uint16) error { } else { ss.RegisterSRPolicy(*sr) } + // receive SR Policy with PLSP-ID + case sr.LSPObject.PlspID != 0: + ss.logger.Debug("Received SR Policy", zap.Uint32("plspID", sr.LSPObject.PlspID)) + computedSegmentList, err := ss.computePathFromTED(*sr) + if err != nil { + ss.logger.Error("Failed to compute path from TED", zap.Error(err)) + return err + } + sr.EroObject = createEroFromSegmentList(computedSegmentList) + + ss.RegisterSRPolicy(*sr) + if policy, found := ss.SearchSRPolicy(sr.LSPObject.PlspID); found { + ss.SendPCUpdate(*policy) + } default: if sr.LSPObject.RFlag { ss.DeleteSRPolicy(*sr) @@ -286,6 +304,139 @@ func (ss *Session) handlePCRpt(length uint16) error { return nil } +func (ss *Session) computePathFromTED(sr pcep.StateReport) ([]table.Segment, error) { + if ss.ted == nil { + return nil, errors.New("TED not available") + } + + srcRouterID, dstRouterID, err := ss.extractSrcDstRouterIDs(sr) + if err != nil { + return nil, fmt.Errorf("failed to extract router IDs: %w", err) + } + + asn, err := ss.extractASN(srcRouterID) + if err != nil { + ss.logger.Error("Could not determine ASN", zap.Error(err)) + } + + metricType := ss.selectMetricType(sr) + + ss.logger.Debug("Computed CSPF parameters", + zap.String("srcRouterID", srcRouterID), + zap.String("dstRouterID", dstRouterID), + zap.Uint32("asn", asn), + zap.String("metricType", metricType.String())) + + segmentList, err := cspf.Cspf(srcRouterID, dstRouterID, asn, metricType, ss.ted) + if err != nil { + return nil, fmt.Errorf("CSPF computation failed: %w", err) + } + + return segmentList, nil +} + +func (ss *Session) extractSrcDstRouterIDs(sr pcep.StateReport) (string, string, error) { + var srcAddr, dstAddr netip.Addr + + if sr.LSPObject.SrcAddr.IsValid() { + srcAddr = sr.LSPObject.SrcAddr + } + if sr.LSPObject.DstAddr.IsValid() { + dstAddr = sr.LSPObject.DstAddr + } + + if !srcAddr.IsValid() || !dstAddr.IsValid() { + return "", "", errors.New("could not extract valid source and destination addresses") + } + + srcRouterID, err := ss.findRouterIDFromAddress(srcAddr) + if err != nil { + return "", "", fmt.Errorf("cannot find source router ID for %s: %w", srcAddr, err) + } + + dstRouterID, err := ss.findRouterIDFromAddress(dstAddr) + if err != nil { + return "", "", fmt.Errorf("cannot find destination router ID for %s: %w", dstAddr, err) + } + + return srcRouterID, dstRouterID, nil +} + +func (ss *Session) findRouterIDFromAddress(addr netip.Addr) (string, error) { + for _, nodes := range ss.ted.Nodes { + for routerID, node := range nodes { + for _, prefix := range node.Prefixes { + if prefix.Prefix.Contains(addr) { + return routerID, nil + } + } + if node.RouterID == addr.String() { + return routerID, nil + } + } + } + return "", fmt.Errorf("address %s not found in TED", addr) +} + +func (ss *Session) extractASN(srcRouterID string) (uint32, error) { + for asn, nodes := range ss.ted.Nodes { + if _, exists := nodes[srcRouterID]; exists { + return asn, nil + } + } + return 0, fmt.Errorf("ASN not found for router %s", srcRouterID) +} + +func (ss *Session) selectMetricType(sr pcep.StateReport) table.MetricType { + if len(sr.MetricObjects) > 0 { + switch sr.MetricObjects[0].MetricType { + case 1: + return table.IGPMetric + case 2: + return table.TEMetric + case 3: + return table.DelayMetric + case 4: + return table.HopcountMetric + default: + return table.TEMetric + } + } + + switch ss.pccType { + case pcep.CiscoLegacy: + return table.TEMetric + case pcep.JuniperLegacy: + return table.IGPMetric + default: + return table.TEMetric + } +} + +func createEroFromSegmentList(segmentList []table.Segment) *pcep.EroObject { + eroObject := &pcep.EroObject{ + ObjectType: pcep.ObjectTypeEROExplicitRoute, + EroSubobjects: make([]pcep.EroSubobject, 0), + } + + for _, segment := range segmentList { + switch seg := segment.(type) { + case table.SegmentSRMPLS: + subobj, err := pcep.NewSREroSubObject(seg) + if err == nil { + eroObject.EroSubobjects = append(eroObject.EroSubobjects, subobj) + } + case table.SegmentSRv6: + subobj, err := pcep.NewSRv6EroSubObject(seg) + if err == nil { + eroObject.EroSubobjects = append(eroObject.EroSubobjects, subobj) + } + } + } + + return eroObject +} + func (ss *Session) RequestAllSRPolicyDeleted() error { var srPolicy table.SRPolicy return ss.SendPCInitiate(srPolicy, true) From 8717757b9ae9d0a0497f146861a94e95787d5a53 Mon Sep 17 00:00:00 2001 From: k1yoto Date: Sun, 22 Jun 2025 09:04:06 +0000 Subject: [PATCH 38/87] chore(srPolicy): improve input validation and error handling --- pkg/packet/pcep/object.go | 1 + pkg/server/grpc_server.go | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/pkg/packet/pcep/object.go b/pkg/packet/pcep/object.go index 9174dc85..e4b029d5 100644 --- a/pkg/packet/pcep/object.go +++ b/pkg/packet/pcep/object.go @@ -598,6 +598,7 @@ func NewSrpObject(segs []table.Segment, srpID uint32, isRemove bool) (*SrpObject } if _, ok := segs[0].(table.SegmentSRMPLS); ok { o.TLVs = append(o.TLVs, &PathSetupType{PathSetupType: PathSetupTypeSRTE}) + } else if _, ok := segs[0].(table.SegmentSRv6); ok { o.TLVs = append(o.TLVs, &PathSetupType{PathSetupType: PathSetupTypeSRv6TE}) } else { return nil, errors.New("invalid Segment type") diff --git a/pkg/server/grpc_server.go b/pkg/server/grpc_server.go index 00279ec8..393cfdcc 100644 --- a/pkg/server/grpc_server.go +++ b/pkg/server/grpc_server.go @@ -209,6 +209,12 @@ func (s *APIServer) DeleteSRPolicy(ctx context.Context, input *pb.DeleteSRPolicy } func validate(inputSRPolicy *pb.SRPolicy, asn uint32, validationKind ValidationKind) error { + if inputSRPolicy == nil { + return errors.New("validate error, input is nil") + } + if asn == 0 { + return errors.New("validate error, ASN is nil") + } if !validator[validationKind](inputSRPolicy, asn) { return errors.New("validate error, invalid input") } From d7ce7c3a29d0909d6b7d8e86454df160449da597 Mon Sep 17 00:00:00 2001 From: Wataru Mishima Date: Mon, 25 Aug 2025 18:23:47 +0900 Subject: [PATCH 39/87] feat(gobgp): migrate GoBGP gRPC API support from v3 to v4 Co-authored-by: Takenaka Motoki <84055826+Motok1@users.noreply.github.com> --- go.mod | 4 +- go.sum | 4 +- internal/pkg/gobgp/interface.go | 366 +++++++++++++++----------------- 3 files changed, 178 insertions(+), 196 deletions(-) diff --git a/go.mod b/go.mod index 4f009320..9d46a676 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,9 @@ module github.com/nttcom/pola -go 1.24.2 +go 1.24.5 require ( - github.com/osrg/gobgp/v3 v3.37.0 + github.com/osrg/gobgp/v4 v4.0.0-20251020140220-f6bf251a9a7f github.com/spf13/cobra v1.10.1 github.com/stretchr/testify v1.11.1 go.uber.org/zap v1.27.0 diff --git a/go.sum b/go.sum index d32a8a74..ca5e8e4d 100644 --- a/go.sum +++ b/go.sum @@ -13,8 +13,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/osrg/gobgp/v3 v3.37.0 h1:+ObuOdvj7G7nxrT0fKFta+EAupdWf/q1WzbXydr8IOY= -github.com/osrg/gobgp/v3 v3.37.0/go.mod h1:kVHVFy1/fyZHJ8P32+ctvPeJogn9qKwa1YCeMRXXrP0= +github.com/osrg/gobgp/v4 v4.0.0-20251020140220-f6bf251a9a7f h1:tDfeX3Q8obMQrArGzU9K/LDzht1LGhk/OeoDxWJVxAs= +github.com/osrg/gobgp/v4 v4.0.0-20251020140220-f6bf251a9a7f/go.mod h1:1a0YiXMuyRPqcCX+fkXZ2UUtcOnUxAWynFunUOSZepk= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= diff --git a/internal/pkg/gobgp/interface.go b/internal/pkg/gobgp/interface.go index c89743df..e037b768 100644 --- a/internal/pkg/gobgp/interface.go +++ b/internal/pkg/gobgp/interface.go @@ -16,10 +16,9 @@ import ( "strings" "github.com/nttcom/pola/internal/pkg/table" - api "github.com/osrg/gobgp/v3/api" + api "github.com/osrg/gobgp/v4/api" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" - "google.golang.org/protobuf/types/known/anypb" ) func GetBGPlsNLRIs(serverAddr string, serverPort string) ([]table.TEDElem, error) { @@ -40,18 +39,18 @@ func GetBGPlsNLRIs(serverAddr string, serverPort string) ([]table.TEDElem, error }() // Create gRPC client - client := api.NewGobgpApiClient(cc) + client := api.NewGoBgpServiceClient(cc) ctx, cancel := context.WithCancel(context.Background()) defer cancel() req := &api.ListPathRequest{ - TableType: api.TableType_GLOBAL, + TableType: api.TableType_TABLE_TYPE_GLOBAL, Family: &api.Family{ Afi: api.Family_AFI_LS, Safi: api.Family_SAFI_LS, }, Name: "", - SortType: api.ListPathRequest_PREFIX, + SortType: api.ListPathRequest_SORT_TYPE_PREFIX, } stream, err := client.ListPath(ctx, req) @@ -86,48 +85,96 @@ func ConvertToTEDElem(dst *api.Destination) ([]table.TEDElem, error) { } path := dst.GetPaths()[0] - nlri, err := path.GetNlri().UnmarshalNew() - if err != nil { - return nil, fmt.Errorf("failed to unmarshal NLRI: %w", err) + nlri := path.GetNlri() + if nlri == nil { + return nil, errors.New("NLRI is nil") } - - switch nlri := nlri.(type) { - case *api.LsAddrPrefix: - linkStateNLRI, err := nlri.GetNlri().UnmarshalNew() + lsAddrPrefix := nlri.GetLsAddrPrefix() + if lsAddrPrefix == nil { + return nil, errors.New("LSAddrPrefix is nil") + } + // Get BGP-LS Attribute + var lsAttr *api.Attribute_Ls + var ok bool + for _, pathAttr := range path.GetPattrs() { + if lsAttr, ok = pathAttr.Attr.(*api.Attribute_Ls); ok { + // Found BGP-LS Attribute + break + } + } + if lsAttr == nil { + return nil, errors.New("BGP-LS Attribute is nil") + } + switch lsAddrPrefix.GetType() { + case api.LsNLRIType_LS_NLRI_TYPE_NODE: + lsAttrNode := lsAttr.Ls.GetNode() + if lsAttrNode == nil { + return nil, fmt.Errorf("LS Node Attribute is nil") + } + lsNode, err := getLsNode(lsAddrPrefix, lsAttrNode) if err != nil { - return nil, fmt.Errorf("failed to unmarshal LS Address Prefix: %w", err) + return nil, fmt.Errorf("failed to process LS Node NLRI: %w", err) } + return []table.TEDElem{lsNode}, nil - switch linkStateNLRI := linkStateNLRI.(type) { - case *api.LsNodeNLRI: - lsNode, err := getLsNodeNLRI(linkStateNLRI, path.GetPattrs()) - if err != nil { - return nil, fmt.Errorf("failed to process LS Node NLRI: %w", err) - } - return []table.TEDElem{lsNode}, nil - case *api.LsLinkNLRI: - lsLink, err := getLsLinkNLRI(linkStateNLRI, path.GetPattrs()) - if err != nil { - return nil, fmt.Errorf("failed to process LS Link NLRI: %w", err) - } - return []table.TEDElem{lsLink}, nil - case *api.LsPrefixV4NLRI, *api.LsPrefixV6NLRI: - lsPrefixList, err := getLsPrefixList(path.GetPattrs()) - if err != nil { - return nil, fmt.Errorf("failed to process LS Prefix V4 NLRI: %w", err) + case api.LsNLRIType_LS_NLRI_TYPE_LINK: + lsAttrLink := lsAttr.Ls.GetLink() + if lsAttrLink == nil { + return nil, fmt.Errorf("LS Link Attribute is nil") + } + lsLink, err := getLsLink(lsAddrPrefix, lsAttrLink) + if err != nil { + return nil, fmt.Errorf("failed to process LS Link NLRI: %w", err) + } + return []table.TEDElem{lsLink}, nil + case api.LsNLRIType_LS_NLRI_TYPE_PREFIX_V4, api.LsNLRIType_LS_NLRI_TYPE_PREFIX_V6: + lsAttrPrefix := lsAttr.Ls.GetPrefix() + if lsAttrPrefix == nil { + return nil, fmt.Errorf("LS Prefix Attribute is nil") + } + // Link-State Prefix NLRI may contain one or more entries within the MP-REACH NLRI. + // Since path.GetNLRI() only includes one of them, you need to retrieve all of them from path.GetPattrs(). + var mpReachAttr *api.MpReachNLRIAttribute + for _, pathAttr := range path.GetPattrs() { + mpReachAttr = pathAttr.GetMpReach() + if mpReachAttr != nil { + // Found MP-REACH NLRI Attribute + break } - return lsPrefixList, nil - case *api.LsSrv6SIDNLRI: - lsSrv6SIDList, err := getLsSrv6SIDNLRIList(path.GetPattrs()) - if err != nil { - return nil, fmt.Errorf("failed to process LS SRv6 SID NLRI: %w", err) + } + if mpReachAttr == nil { + return nil, errors.New("MP-REACH NLRI Attribute is nil") + } + lsPrefixList, err := getLsPrefixList(mpReachAttr.GetNlris(), lsAttrPrefix) + if err != nil { + return nil, fmt.Errorf("failed to process LS Prefix V4 NLRI: %w", err) + } + return lsPrefixList, nil + case api.LsNLRIType_LS_NLRI_TYPE_SRV6_SID: + lsAttrSrv6SID := lsAttr.Ls.GetSrv6Sid() + if lsAttrSrv6SID == nil { + return nil, fmt.Errorf("LS SRv6 SID Attribute is nil") + } + // Link-State Prefix NLRI may contain one or more entries within the MP-REACH NLRI. + // Since path.GetNLRI() only includes one of them, you need to retrieve the others from path.GetPattrs() instead of using path.GetNLRI(). + var mpReachAttr *api.MpReachNLRIAttribute + for _, pathAttr := range path.GetPattrs() { + mpReachAttr = pathAttr.GetMpReach() + if mpReachAttr != nil { + break } - return lsSrv6SIDList, nil - default: - return nil, fmt.Errorf("invalid LS Link State NLRI type: %T", linkStateNLRI) } + if mpReachAttr == nil { + return nil, errors.New("MP-REACH NLRI Attribute is nil") + } + lsSrv6SIDList, err := getLsSrv6SIDList(mpReachAttr.GetNlris(), lsAttrSrv6SID) + if err != nil { + return nil, fmt.Errorf("failed to process LS SRv6 SID NLRI: %w", err) + } + return lsSrv6SIDList, nil + default: - return nil, fmt.Errorf("invalid NLRI type: %T", nlri) + return nil, fmt.Errorf("invalid LS Link State NLRI type: %s", lsAddrPrefix.GetType().String()) } } @@ -144,172 +191,131 @@ func formatIsisAreaID(isisArea []byte) string { return strIsisArea.String() } -func getLsNodeNLRI(typedLinkStateNLRI *api.LsNodeNLRI, pathAttrs []*anypb.Any) (*table.LsNode, error) { - asn := typedLinkStateNLRI.GetLocalNode().GetAsn() - routerID := typedLinkStateNLRI.GetLocalNode().GetIgpRouterId() +func getLsNode(typedLinkStateNLRI *api.LsAddrPrefix, lsAttrNode *api.LsAttributeNode) (*table.LsNode, error) { + lsNodeNLRI := typedLinkStateNLRI.Nlri.GetNode() + asn := lsNodeNLRI.GetLocalNode().GetAsn() + routerID := lsNodeNLRI.GetLocalNode().GetIgpRouterId() lsNode := table.NewLsNode(asn, routerID) - for _, pathAttr := range pathAttrs { - typedPathAttr, err := pathAttr.UnmarshalNew() - if err != nil { - return nil, fmt.Errorf("failed to unmarshal path attribute: %w", err) - } - - bgplsAttr, ok := typedPathAttr.(*api.LsAttribute) - if !ok { - continue - } - - isisArea := bgplsAttr.GetNode().GetIsisArea() - lsNode.IsisAreaID = formatIsisAreaID(isisArea) - lsNode.Hostname = bgplsAttr.GetNode().GetName() - if bgplsAttr.GetNode().GetSrCapabilities() != nil { - srCapabilities := bgplsAttr.GetNode().GetSrCapabilities().GetRanges() - if len(srCapabilities) != 1 { - return nil, fmt.Errorf("expected 1 SR Capability TLV, got: %d", len(srCapabilities)) - } else { - lsNode.SrgbBegin = srCapabilities[0].GetBegin() - lsNode.SrgbEnd = srCapabilities[0].GetEnd() - } + isisArea := lsAttrNode.GetIsisArea() + lsNode.IsisAreaID = formatIsisAreaID(isisArea) + lsNode.Hostname = lsAttrNode.GetName() + if lsAttrNode.GetSrCapabilities() != nil { + srCapabilities := lsAttrNode.GetSrCapabilities().GetRanges() + if len(srCapabilities) != 1 { + return nil, fmt.Errorf("expected 1 SR Capability TLV, got: %d", len(srCapabilities)) + } else { + lsNode.SrgbBegin = srCapabilities[0].GetBegin() + lsNode.SrgbEnd = srCapabilities[0].GetEnd() } } - return lsNode, nil } -func getLsLinkNLRI(typedLinkStateNLRI *api.LsLinkNLRI, pathAttrs []*anypb.Any) (*table.LsLink, error) { - localNode := table.NewLsNode(typedLinkStateNLRI.GetLocalNode().GetAsn(), typedLinkStateNLRI.GetLocalNode().GetIgpRouterId()) - remoteNode := table.NewLsNode(typedLinkStateNLRI.GetRemoteNode().GetAsn(), typedLinkStateNLRI.GetRemoteNode().GetIgpRouterId()) +func getLsLink(typedLinkStateNLRI *api.LsAddrPrefix, lsAttrLink *api.LsAttributeLink) (*table.LsLink, error) { + lsLinkNLRI := typedLinkStateNLRI.Nlri.GetLink() + localNode := table.NewLsNode(lsLinkNLRI.GetLocalNode().GetAsn(), lsLinkNLRI.GetLocalNode().GetIgpRouterId()) + remoteNode := table.NewLsNode(lsLinkNLRI.GetRemoteNode().GetAsn(), lsLinkNLRI.GetRemoteNode().GetIgpRouterId()) var err error var localIP netip.Addr - if typedLinkStateNLRI.GetLinkDescriptor().GetInterfaceAddrIpv4() != "" { - localIP, err = netip.ParseAddr(typedLinkStateNLRI.GetLinkDescriptor().GetInterfaceAddrIpv4()) + if lsLinkNLRI.GetLinkDescriptor().GetInterfaceAddrIpv4() != "" { + localIP, err = netip.ParseAddr(lsLinkNLRI.GetLinkDescriptor().GetInterfaceAddrIpv4()) if err != nil { return nil, fmt.Errorf("failed to parse local IPv4 address: %v", err) } - } else { - localIP, err = netip.ParseAddr(typedLinkStateNLRI.GetLinkDescriptor().GetInterfaceAddrIpv6()) + } else if lsLinkNLRI.GetLinkDescriptor().GetInterfaceAddrIpv6() != "" { + localIP, err = netip.ParseAddr(lsLinkNLRI.GetLinkDescriptor().GetInterfaceAddrIpv6()) if err != nil { return nil, fmt.Errorf("failed to parse local IPv6 address: %v", err) } + } else { + localIP = netip.Addr{} } var remoteIP netip.Addr - if typedLinkStateNLRI.GetLinkDescriptor().GetNeighborAddrIpv4() != "" { - remoteIP, err = netip.ParseAddr(typedLinkStateNLRI.GetLinkDescriptor().GetNeighborAddrIpv4()) + if lsLinkNLRI.GetLinkDescriptor().GetNeighborAddrIpv4() != "" { + remoteIP, err = netip.ParseAddr(lsLinkNLRI.GetLinkDescriptor().GetNeighborAddrIpv4()) if err != nil { return nil, fmt.Errorf("failed to parse remote IPv4 address: %v", err) } - } else { - remoteIP, err = netip.ParseAddr(typedLinkStateNLRI.GetLinkDescriptor().GetNeighborAddrIpv6()) + } else if lsLinkNLRI.GetLinkDescriptor().GetNeighborAddrIpv6() != "" { + remoteIP, err = netip.ParseAddr(lsLinkNLRI.GetLinkDescriptor().GetNeighborAddrIpv6()) if err != nil { return nil, fmt.Errorf("failed to parse remote IPv6 address: %v", err) } + } else { + remoteIP = netip.Addr{} } lsLink := table.NewLsLink(localNode, remoteNode) lsLink.LocalIP = localIP lsLink.RemoteIP = remoteIP - for _, pathAttr := range pathAttrs { - typedPathAttr, err := pathAttr.UnmarshalNew() - if err != nil { - return nil, fmt.Errorf("failed to unmarshal path attribute %v: %v", pathAttr, err) - } - - bgplsAttr, ok := typedPathAttr.(*api.LsAttribute) - if !ok { - continue - } + lsLink.Metrics = append(lsLink.Metrics, table.NewMetric(table.MetricType(table.IGPMetric), lsAttrLink.GetIgpMetric())) - lsLink.Metrics = append(lsLink.Metrics, table.NewMetric(table.MetricType(table.IGPMetric), bgplsAttr.GetLink().GetIgpMetric())) - - teMetric := bgplsAttr.GetLink().GetDefaultTeMetric() - if teMetric != 0 { - lsLink.Metrics = append(lsLink.Metrics, table.NewMetric(table.MetricType(table.TEMetric), teMetric)) - } + teMetric := lsAttrLink.GetDefaultTeMetric() + if teMetric != 0 { + lsLink.Metrics = append(lsLink.Metrics, table.NewMetric(table.MetricType(table.TEMetric), teMetric)) + } - lsLink.AdjSid = bgplsAttr.GetLink().GetSrAdjacencySid() - - // handle SRv6 SID TLV - var srv6EndXSID *api.LsSrv6EndXSID - srv6EndXSID = bgplsAttr.GetLink().GetSrv6EndXSid() - if srv6EndXSID != nil { - lsLink.Srv6EndXSID = &table.Srv6EndXSID{ - EndpointBehavior: uint16(srv6EndXSID.EndpointBehavior), - Sids: srv6EndXSID.Sids, - Srv6SIDStructure: table.SIDStructure{ - LocalBlock: uint8(srv6EndXSID.Srv6SidStructure.GetLocalBlock()), - LocalNode: uint8(srv6EndXSID.Srv6SidStructure.GetLocalNode()), - LocalFunc: uint8(srv6EndXSID.Srv6SidStructure.GetLocalFunc()), - LocalArg: uint8(srv6EndXSID.Srv6SidStructure.GetLocalArg()), - }, - } + lsLink.AdjSid = lsAttrLink.GetSrAdjacencySid() + + // handle SRv6 SID TLV + srv6EndXSID := lsAttrLink.GetSrv6EndXSid() + if srv6EndXSID != nil { + lsLink.Srv6EndXSID = &table.Srv6EndXSID{ + EndpointBehavior: uint16(srv6EndXSID.EndpointBehavior), + Sids: srv6EndXSID.Sids, + Srv6SIDStructure: table.SIDStructure{ + LocalBlock: uint8(srv6EndXSID.Srv6SidStructure.GetLocalBlock()), + LocalNode: uint8(srv6EndXSID.Srv6SidStructure.GetLocalNode()), + LocalFunc: uint8(srv6EndXSID.Srv6SidStructure.GetLocalFunc()), + LocalArg: uint8(srv6EndXSID.Srv6SidStructure.GetLocalArg()), + }, } } return lsLink, nil } -func getLsPrefixList(pathAttrs []*anypb.Any) ([]table.TEDElem, error) { +func getLsPrefixList(nlris []*api.NLRI, lsAttrPrefix *api.LsAttributePrefix) ([]table.TEDElem, error) { var lsPrefixList []table.TEDElem - var sidIndex uint32 - for _, pathAttr := range pathAttrs { - typedPathAttr, err := pathAttr.UnmarshalNew() - if err != nil { - return nil, fmt.Errorf("failed to unmarshal path attribute: %v", err) - } + for _, nlri := range nlris { + lsAddrPrefix := nlri.GetLsAddrPrefix() - switch typedPathAttr := typedPathAttr.(type) { - case *api.LsAttribute: - if typedPathAttr.GetPrefix().GetSrPrefixSid() != 0 { - sidIndex = typedPathAttr.GetPrefix().GetSrPrefixSid() - } else { - sidIndex = 0 - } - case *api.MpReachNLRIAttribute: - for _, nlri := range typedPathAttr.GetNlris() { - typedNlri, err := nlri.UnmarshalNew() - if err != nil { - return nil, fmt.Errorf("failed to unmarshal NLRI: %v", err) - } - - if lsNlri, ok := typedNlri.(*api.LsAddrPrefix); ok { - lsPrefix, err := getLsPrefix(lsNlri, sidIndex) - if err != nil { - return nil, fmt.Errorf("failed to get LS Prefix: %v", err) - } - lsPrefixList = append(lsPrefixList, lsPrefix) - } - } + lsPrefix, err := getLsPrefix(lsAddrPrefix, lsAttrPrefix) + if err != nil { + return nil, fmt.Errorf("failed to get LS Prefix: %v", err) } + lsPrefixList = append(lsPrefixList, lsPrefix) } - return lsPrefixList, nil } -func getLsPrefix(lsNlri *api.LsAddrPrefix, sidIndex uint32) (*table.LsPrefix, error) { - nlri, err := lsNlri.GetNlri().UnmarshalNew() - if err != nil { - return nil, fmt.Errorf("failed to unmarshal LS Prefix V4: %v", err) - } - +func getLsPrefix(typedLinkStateNLRI *api.LsAddrPrefix, lsAttrPrefix *api.LsAttributePrefix) (*table.LsPrefix, error) { var localNodeID string var localNodeAsn uint32 var prefix []string + var sidIndex uint32 + + if lsAttrPrefix.GetSrPrefixSid() != 0 { + sidIndex = lsAttrPrefix.GetSrPrefixSid() + } else { + sidIndex = 0 + } - switch prefNlri := nlri.(type) { - case *api.LsPrefixV4NLRI: - localNodeID = prefNlri.GetLocalNode().GetIgpRouterId() - localNodeAsn = prefNlri.GetLocalNode().GetAsn() - prefix = prefNlri.GetPrefixDescriptor().GetIpReachability() - case *api.LsPrefixV6NLRI: - localNodeID = prefNlri.GetLocalNode().GetIgpRouterId() - localNodeAsn = prefNlri.GetLocalNode().GetAsn() - prefix = prefNlri.GetPrefixDescriptor().GetIpReachability() + switch prefNLRI := typedLinkStateNLRI.Nlri.Nlri.(type) { + case *api.LsAddrPrefix_LsNLRI_PrefixV4: + localNodeID = prefNLRI.PrefixV4.GetLocalNode().GetIgpRouterId() + localNodeAsn = prefNLRI.PrefixV4.GetLocalNode().GetAsn() + prefix = prefNLRI.PrefixV4.GetPrefixDescriptor().GetIpReachability() + case *api.LsAddrPrefix_LsNLRI_PrefixV6: + localNodeID = prefNLRI.PrefixV6.GetLocalNode().GetIgpRouterId() + localNodeAsn = prefNLRI.PrefixV6.GetLocalNode().GetAsn() + prefix = prefNLRI.PrefixV6.GetPrefixDescriptor().GetIpReachability() default: return nil, errors.New("invalid LS prefix NLRI type") } @@ -322,6 +328,7 @@ func getLsPrefix(lsNlri *api.LsAddrPrefix, sidIndex uint32) (*table.LsPrefix, er return nil, errors.New("invalid prefix length: expected 1 prefix") } + var err error lsPrefix.Prefix, err = netip.ParsePrefix(prefix[0]) if err != nil { return nil, fmt.Errorf("failed to parse prefix: %v", err) @@ -330,53 +337,28 @@ func getLsPrefix(lsNlri *api.LsAddrPrefix, sidIndex uint32) (*table.LsPrefix, er return lsPrefix, nil } -func getLsSrv6SIDNLRIList(pathAttrs []*anypb.Any) ([]table.TEDElem, error) { +func getLsSrv6SIDList(nlris []*api.NLRI, lsAttrSrv6SID *api.LsAttributeSrv6SID) ([]table.TEDElem, error) { var lsSrv6SIDList []table.TEDElem - var endpointBehavior *api.LsSrv6EndpointBehavior - var srv6SIDStructure *api.LsSrv6SIDStructure - for _, pathAttr := range pathAttrs { - typedPathAttr, err := pathAttr.UnmarshalNew() - if err != nil { - return nil, fmt.Errorf("failed to unmarshal path attribute: %w", err) - } + for _, nlri := range nlris { + lsAddrPrefix := nlri.GetLsAddrPrefix() - switch typedPathAttr := typedPathAttr.(type) { - case *api.LsAttribute: - // handle LsAttribute for SRv6 SID - srv6SIDStructure = typedPathAttr.GetSrv6Sid().GetSrv6SidStructure() - endpointBehavior = typedPathAttr.GetSrv6Sid().GetSrv6EndpointBehavior() - case *api.MpReachNLRIAttribute: - for _, nlri := range typedPathAttr.GetNlris() { - typedNLRI, err := nlri.UnmarshalNew() - if err != nil { - return nil, fmt.Errorf("failed to unmarshal NLRI: %w", err) - } - if lsNLRI, ok := typedNLRI.(*api.LsAddrPrefix); ok { - lsSrv6SID, err := getLsSrv6SIDNLRI(lsNLRI, endpointBehavior, srv6SIDStructure) - if err != nil { - return nil, fmt.Errorf("failed to process LS SRv6 SID NLRI: %w", err) - } - lsSrv6SIDList = append(lsSrv6SIDList, lsSrv6SID) - } else { - return nil, fmt.Errorf("unexpected NLRI type: %T", typedNLRI) - } - } + lsPrefix, err := getLsSrv6SID(lsAddrPrefix, lsAttrSrv6SID) + if err != nil { + return nil, fmt.Errorf("failed to get LS Prefix: %v", err) } + lsSrv6SIDList = append(lsSrv6SIDList, lsPrefix) } + return lsSrv6SIDList, nil } -// getLsSrv6SIDNLRI processes the LS SRv6 SID NLRI and returns a corresponding LsSrv6SID. -func getLsSrv6SIDNLRI(lsNLRI *api.LsAddrPrefix, endpointBehavior *api.LsSrv6EndpointBehavior, srv6SIDStructure *api.LsSrv6SIDStructure) (*table.LsSrv6SID, error) { - srv6NLRI, err := lsNLRI.GetNlri().UnmarshalNew() - if err != nil { - return nil, fmt.Errorf("failed to unmarshal LS NLRI: %w", err) - } - srv6SIDNLRI, ok := srv6NLRI.(*api.LsSrv6SIDNLRI) - if !ok { - return nil, fmt.Errorf("invalid LS SRv6 SID NLRI type: %T", srv6NLRI) - } +// getLsSrv6SID processes the LS SRv6 SID NLRI and returns a corresponding LsSrv6SID. +func getLsSrv6SID(typedLinkStateNLRI *api.LsAddrPrefix, lsAttrSrv6SID *api.LsAttributeSrv6SID) (*table.LsSrv6SID, error) { + + srv6SIDStructure := lsAttrSrv6SID.GetSrv6SidStructure() + endpointBehavior := lsAttrSrv6SID.GetSrv6EndpointBehavior() + srv6SIDNLRI := typedLinkStateNLRI.Nlri.GetSrv6Sid() localNodeID := srv6SIDNLRI.GetLocalNode().GetIgpRouterId() localNodeASN := srv6SIDNLRI.GetLocalNode().GetAsn() From b426df39d1d858255e1ff0d8672caf4580d8c8a9 Mon Sep 17 00:00:00 2001 From: Motok1 Date: Tue, 26 Aug 2025 18:51:17 +0900 Subject: [PATCH 40/87] fix(ted): TED struct and "show ted" command for ipv6 unnumbered --- cmd/pola/grpc/grpc_client.go | 4 ++-- cmd/pola/ted.go | 17 +++++++++++++++-- internal/pkg/table/ted.go | 14 +++++++++++++- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/cmd/pola/grpc/grpc_client.go b/cmd/pola/grpc/grpc_client.go index a57afe22..b695c193 100644 --- a/cmd/pola/grpc/grpc_client.go +++ b/cmd/pola/grpc/grpc_client.go @@ -209,11 +209,11 @@ func createLsLink(localNode, remoteNode *table.LsNode, link *pb.LsLink) (*table. AdjSid: link.GetAdjSid(), } var err error - lsLink.LocalIP, err = netip.ParseAddr(link.GetLocalIp()) + err = lsLink.LocalIP.UnmarshalText([]byte(link.GetLocalIp())) if err != nil { return nil, err } - lsLink.RemoteIP, err = netip.ParseAddr(link.GetRemoteIp()) + err = lsLink.RemoteIP.UnmarshalText([]byte(link.GetRemoteIp())) if err != nil { return nil, err } diff --git a/cmd/pola/ted.go b/cmd/pola/ted.go index 0d0438fb..8590c465 100644 --- a/cmd/pola/ted.go +++ b/cmd/pola/ted.go @@ -63,9 +63,22 @@ func print(jsonFlag bool) error { metrics = append(metrics, metricMap) } + var localIP string + var remoteIP string + if link.LocalIP.IsValid() { + localIP = link.LocalIP.String() + } else { + localIP = "None" + } + if link.RemoteIP.IsValid() { + remoteIP = link.RemoteIP.String() + } else { + remoteIP = "None" + } + linkMap := map[string]any{ - "localIP": link.LocalIP.String(), - "remoteIP": link.RemoteIP.String(), + "localIP": localIP, + "remoteIP": remoteIP, "remoteNode": link.RemoteNode.RouterID, "metrics": metrics, "adjSid": link.AdjSid, diff --git a/internal/pkg/table/ted.go b/internal/pkg/table/ted.go index 4f5fd9ee..cac16956 100644 --- a/internal/pkg/table/ted.go +++ b/internal/pkg/table/ted.go @@ -41,7 +41,19 @@ func (ted *LsTED) Print() { } fmt.Printf(" Links:\n") for _, link := range node.Links { - fmt.Printf(" Local: %s Remote: %s\n", link.LocalIP.String(), link.RemoteIP.String()) + var localIP string + var remoteIP string + if link.LocalIP.IsValid() { + localIP = link.LocalIP.String() + } else { + localIP = "None" + } + if link.RemoteIP.IsValid() { + remoteIP = link.RemoteIP.String() + } else { + remoteIP = "None" + } + fmt.Printf(" Local: %s Remote: %s\n", localIP, remoteIP) fmt.Printf(" RemoteNode: %s\n", link.RemoteNode.RouterID) fmt.Printf(" Metrics:\n") for _, metric := range link.Metrics { From a738ff5b0770e51b3f39fafaab9e088579a6fc77 Mon Sep 17 00:00:00 2001 From: Motok1 Date: Tue, 26 Aug 2025 18:51:48 +0900 Subject: [PATCH 41/87] fix(grpc): fix create sr policy validator --- pkg/server/grpc_server.go | 86 +++++++++++++++++++++++++++++---------- 1 file changed, 65 insertions(+), 21 deletions(-) diff --git a/pkg/server/grpc_server.go b/pkg/server/grpc_server.go index 393cfdcc..d0daf56e 100644 --- a/pkg/server/grpc_server.go +++ b/pkg/server/grpc_server.go @@ -215,8 +215,12 @@ func validate(inputSRPolicy *pb.SRPolicy, asn uint32, validationKind ValidationK if asn == 0 { return errors.New("validate error, ASN is nil") } - if !validator[validationKind](inputSRPolicy, asn) { - return errors.New("validate error, invalid input") + if validateFunc, ok := validator[validationKind]; ok { + if err := validateFunc(inputSRPolicy, asn); err != nil { + return fmt.Errorf("validate error: %w", err) + } + } else { + return fmt.Errorf("validate error: unknown validation kind %q", validationKind) } return nil @@ -230,25 +234,56 @@ const ( ValidationDelete ValidationKind = "Delete" ) -var validator = map[ValidationKind]func(policy *pb.SRPolicy, asn uint32) bool{ - ValidationKind("Add"): func(policy *pb.SRPolicy, asn uint32) bool { - return asn != 0 && - policy.PcepSessionAddr != nil && - policy.Color != 0 && - policy.SrcRouterId != "" && - policy.DstRouterId != "" +var validator = map[ValidationKind]func(policy *pb.SRPolicy, asn uint32) error{ + ValidationAdd: func(policy *pb.SRPolicy, asn uint32) error { + if asn == 0 { + return errors.New("ASN must not be zero") + } + if policy.PcepSessionAddr == nil { + return errors.New("PCEP session address must not be nil") + } + if policy.Color == 0 { + return errors.New("Color must not be zero") + } + if policy.SrcRouterId == "" { + return errors.New("SrcRouterId must not be empty") + } + if policy.DstRouterId == "" { + return errors.New("DstRouterId must not be empty") + } + return nil }, - ValidationKind("AddDisablePathCompute"): func(policy *pb.SRPolicy, asn uint32) bool { - return policy.PcepSessionAddr != nil && - len(policy.SrcAddr) > 0 && - len(policy.DstAddr) > 0 && - len(policy.SegmentList) > 0 + + ValidationAddDisablePathCompute: func(policy *pb.SRPolicy, asn uint32) error { + if policy.PcepSessionAddr == nil { + return errors.New("PCEP session address must not be nil") + } + if len(policy.SrcAddr) == 0 { + return errors.New("SrcAddr must not be empty") + } + if len(policy.DstAddr) == 0 { + return errors.New("DstAddr must not be empty") + } + if len(policy.SegmentList) == 0 { + return errors.New("SegmentList must not be empty") + } + return nil }, - ValidationKind("Delete"): func(policy *pb.SRPolicy, asn uint32) bool { - return policy.PcepSessionAddr != nil && - policy.Color != 0 && - len(policy.DstAddr) > 0 && - policy.PolicyName != "" + + ValidationDelete: func(policy *pb.SRPolicy, asn uint32) error { + if policy.PcepSessionAddr == nil { + return errors.New("PCEP session address must not be nil") + } + if policy.Color == 0 { + return errors.New("Color must not be zero") + } + if len(policy.DstAddr) == 0 { + return errors.New("DstAddr must not be empty") + } + if policy.PolicyName == "" { + return errors.New("PolicyName must not be empty") + } + return nil }, } @@ -400,13 +435,22 @@ func (s *APIServer) GetTED(ctx context.Context, req *pb.GetTEDRequest) (*pb.GetT } for _, lsLink := range lsNode.Links { + localIp, err := lsLink.LocalIP.MarshalText() + if err != nil { + return nil, fmt.Errorf("failed to marshal local IP: %v", err) + } + remoteIp, err := lsLink.RemoteIP.MarshalText() + if err != nil { + return nil, fmt.Errorf("failed to marshal remote IP: %v", err) + } + link := &pb.LsLink{ LocalRouterId: lsLink.LocalNode.RouterID, LocalAsn: lsLink.LocalNode.ASN, - LocalIp: lsLink.LocalIP.String(), + LocalIp: string(localIp), RemoteRouterId: lsLink.RemoteNode.RouterID, RemoteAsn: lsLink.RemoteNode.ASN, - RemoteIp: lsLink.RemoteIP.String(), + RemoteIp: string(remoteIp), Metrics: make([]*pb.Metric, 0, len(lsLink.Metrics)), AdjSid: lsLink.AdjSid, } From 726c482c1d7ded50dedecd052e909ea5c0c9e745 Mon Sep 17 00:00:00 2001 From: Wataru Mishima Date: Fri, 5 Sep 2025 16:09:38 +0900 Subject: [PATCH 42/87] fix(ted): add nil checks in TED Print --- internal/pkg/table/ted.go | 122 ++++++++++++++++++++++++++------------ 1 file changed, 84 insertions(+), 38 deletions(-) diff --git a/internal/pkg/table/ted.go b/internal/pkg/table/ted.go index cac16956..511f001e 100644 --- a/internal/pkg/table/ted.go +++ b/internal/pkg/table/ted.go @@ -24,59 +24,105 @@ func (ted *LsTED) Update(tedElems []TEDElem) { } func (ted *LsTED) Print() { + if ted == nil || ted.Nodes == nil { + fmt.Println("TED is empty") + return + } + for _, nodes := range ted.Nodes { + if nodes == nil { + continue + } nodeCnt := 1 for nodeID, node := range nodes { + if node == nil { + continue + } fmt.Printf("Node: %d\n", nodeCnt) fmt.Printf(" %s\n", nodeID) fmt.Printf(" Hostname: %s\n", node.Hostname) fmt.Printf(" ISIS Area ID: %s\n", node.IsisAreaID) fmt.Printf(" SRGB: %d - %d\n", node.SrgbBegin, node.SrgbEnd) + fmt.Printf(" Prefixes:\n") - for _, prefix := range node.Prefixes { - fmt.Printf(" %s\n", prefix.Prefix.String()) - if prefix.SidIndex != 0 { - fmt.Printf(" index: %d\n", prefix.SidIndex) + if node.Prefixes != nil { + for _, prefix := range node.Prefixes { + if prefix == nil { + continue + } + fmt.Printf(" %s\n", prefix.Prefix.String()) + if prefix.SidIndex != 0 { + fmt.Printf(" index: %d\n", prefix.SidIndex) + } } } + fmt.Printf(" Links:\n") - for _, link := range node.Links { - var localIP string - var remoteIP string - if link.LocalIP.IsValid() { - localIP = link.LocalIP.String() - } else { - localIP = "None" - } - if link.RemoteIP.IsValid() { - remoteIP = link.RemoteIP.String() - } else { - remoteIP = "None" - } - fmt.Printf(" Local: %s Remote: %s\n", localIP, remoteIP) - fmt.Printf(" RemoteNode: %s\n", link.RemoteNode.RouterID) - fmt.Printf(" Metrics:\n") - for _, metric := range link.Metrics { - fmt.Printf(" %s: %d\n", metric.Type.String(), metric.Value) + if node.Links != nil { + for _, link := range node.Links { + if link == nil { + continue + } + + localIP := "None" + remoteIP := "None" + if link.LocalIP.IsValid() { + localIP = link.LocalIP.String() + } + if link.RemoteIP.IsValid() { + remoteIP = link.RemoteIP.String() + } + fmt.Printf(" Local: %s Remote: %s\n", localIP, remoteIP) + + remoteNodeID := "None" + if link.RemoteNode != nil { + remoteNodeID = link.RemoteNode.RouterID + } + fmt.Printf(" RemoteNode: %s\n", remoteNodeID) + + fmt.Printf(" Metrics:\n") + if link.Metrics != nil { + for _, metric := range link.Metrics { + if metric == nil { + continue + } + fmt.Printf(" %s: %d\n", metric.Type.String(), metric.Value) + } + } + + fmt.Printf(" Adj-SID: %d\n", link.AdjSid) + + if link.Srv6EndXSID != nil { + fmt.Printf(" SRv6 End.X SID:\n") + fmt.Printf(" EndpointBehavior: %s\n", BehaviorToString(link.Srv6EndXSID.EndpointBehavior)) + fmt.Printf(" SIDs: %v\n", link.Srv6EndXSID.Sids) + fmt.Printf(" SID Structure: Block: %d, Node: %d, Func: %d, Arg: %d\n", + link.Srv6EndXSID.Srv6SIDStructure.LocalBlock, + link.Srv6EndXSID.Srv6SIDStructure.LocalNode, + link.Srv6EndXSID.Srv6SIDStructure.LocalFunc, + link.Srv6EndXSID.Srv6SIDStructure.LocalArg) + } } - fmt.Printf(" Adj-SID: %d\n", link.AdjSid) - fmt.Printf(" SRv6 End.X SID:\n") - fmt.Printf(" EndpointBehavior: %s\n", BehaviorToString(link.Srv6EndXSID.EndpointBehavior)) - fmt.Printf(" SIDs: %v\n", link.Srv6EndXSID.Sids) - fmt.Printf(" SID Structure: Block: %d, Node: %d, Func: %d, Arg: %d\n", - link.Srv6EndXSID.Srv6SIDStructure.LocalBlock, - link.Srv6EndXSID.Srv6SIDStructure.LocalNode, - link.Srv6EndXSID.Srv6SIDStructure.LocalFunc, - link.Srv6EndXSID.Srv6SIDStructure.LocalArg) } + fmt.Printf(" SRv6 SIDs:\n") - for _, srv6SID := range node.SRv6SIDs { - fmt.Printf(" SIDs: %v\n", srv6SID.Sids) - fmt.Printf(" Block: %d, Node: %d, Func: %d, Arg: %d\n", srv6SID.SIDStructure.LocalBlock, - srv6SID.SIDStructure.LocalNode, srv6SID.SIDStructure.LocalFunc, srv6SID.SIDStructure.LocalArg) - fmt.Printf(" EndpointBehavior: %s, Flags: %d, Algorithm: %d\n", BehaviorToString(srv6SID.EndpointBehavior.Behavior), - srv6SID.EndpointBehavior.Flags, srv6SID.EndpointBehavior.Algorithm) - fmt.Printf(" MultiTopoIDs: %v\n", srv6SID.MultiTopoIDs) + if node.SRv6SIDs != nil { + for _, srv6SID := range node.SRv6SIDs { + if srv6SID == nil { + continue + } + fmt.Printf(" SIDs: %v\n", srv6SID.Sids) + fmt.Printf(" Block: %d, Node: %d, Func: %d, Arg: %d\n", + srv6SID.SIDStructure.LocalBlock, + srv6SID.SIDStructure.LocalNode, + srv6SID.SIDStructure.LocalFunc, + srv6SID.SIDStructure.LocalArg) + fmt.Printf(" EndpointBehavior: %s, Flags: %d, Algorithm: %d\n", + BehaviorToString(srv6SID.EndpointBehavior.Behavior), + srv6SID.EndpointBehavior.Flags, + srv6SID.EndpointBehavior.Algorithm) + fmt.Printf(" MultiTopoIDs: %v\n", srv6SID.MultiTopoIDs) + } } nodeCnt++ From 1719c2618c25db996bd1800a1325819e97994d35 Mon Sep 17 00:00:00 2001 From: Wataru Mishima Date: Fri, 5 Sep 2025 16:22:09 +0900 Subject: [PATCH 43/87] fix(ted): add nil checks in GetTED --- pkg/server/grpc_server.go | 98 +++++++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 41 deletions(-) diff --git a/pkg/server/grpc_server.go b/pkg/server/grpc_server.go index d0daf56e..70887d33 100644 --- a/pkg/server/grpc_server.go +++ b/pkg/server/grpc_server.go @@ -413,7 +413,7 @@ func (s *APIServer) GetTED(ctx context.Context, req *pb.GetTEDRequest) (*pb.GetT Enable: true, } - if s.pce.ted == nil { + if s.pce == nil || s.pce.ted == nil { ret.Enable = false return ret, nil } @@ -422,6 +422,10 @@ func (s *APIServer) GetTED(ctx context.Context, req *pb.GetTEDRequest) (*pb.GetT for _, lsNodes := range s.pce.ted.Nodes { for _, lsNode := range lsNodes { + if lsNode == nil { + continue + } + node := &pb.LsNode{ Asn: lsNode.ASN, RouterId: lsNode.RouterID, @@ -435,79 +439,89 @@ func (s *APIServer) GetTED(ctx context.Context, req *pb.GetTEDRequest) (*pb.GetT } for _, lsLink := range lsNode.Links { - localIp, err := lsLink.LocalIP.MarshalText() - if err != nil { - return nil, fmt.Errorf("failed to marshal local IP: %v", err) - } - remoteIp, err := lsLink.RemoteIP.MarshalText() - if err != nil { - return nil, fmt.Errorf("failed to marshal remote IP: %v", err) + if lsLink == nil || lsLink.LocalNode == nil || lsLink.RemoteNode == nil { + s.logger.Debug("skip link with nil node", zap.Any("link", lsLink)) + continue } + localIP, _ := lsLink.LocalIP.MarshalText() + remoteIP, _ := lsLink.RemoteIP.MarshalText() + link := &pb.LsLink{ LocalRouterId: lsLink.LocalNode.RouterID, LocalAsn: lsLink.LocalNode.ASN, - LocalIp: string(localIp), + LocalIp: string(localIP), RemoteRouterId: lsLink.RemoteNode.RouterID, RemoteAsn: lsLink.RemoteNode.ASN, - RemoteIp: string(remoteIp), + RemoteIp: string(remoteIP), Metrics: make([]*pb.Metric, 0, len(lsLink.Metrics)), AdjSid: lsLink.AdjSid, } for _, lsMetric := range lsLink.Metrics { + if lsMetric == nil { + continue + } metricType, ok := pb.MetricType_value[lsMetric.Type.String()] if !ok { - return nil, fmt.Errorf("invalid metric type: %s", lsMetric.Type.String()) + s.logger.Debug("invalid metric type", zap.String("type", lsMetric.Type.String())) + continue } - - metric := &pb.Metric{ + link.Metrics = append(link.Metrics, &pb.Metric{ Type: pb.MetricType(metricType), Value: lsMetric.Value, - } - - link.Metrics = append(link.Metrics, metric) + }) } - link.Srv6EndXSid = &pb.Srv6EndXSID{ - EndpointBehavior: uint32(lsLink.Srv6EndXSID.EndpointBehavior), - Sids: make([]*pb.SID, 0, len(lsLink.Srv6EndXSID.Sids)), - SidStructure: &pb.SidStructure{ - LocalBlock: uint32(lsLink.Srv6EndXSID.Srv6SIDStructure.LocalBlock), - LocalNode: uint32(lsLink.Srv6EndXSID.Srv6SIDStructure.LocalNode), - LocalFunc: uint32(lsLink.Srv6EndXSID.Srv6SIDStructure.LocalFunc), - LocalArg: uint32(lsLink.Srv6EndXSID.Srv6SIDStructure.LocalArg), - }, - } + if lsLink.Srv6EndXSID != nil { + srv6 := &pb.Srv6EndXSID{ + EndpointBehavior: uint32(lsLink.Srv6EndXSID.EndpointBehavior), + Sids: make([]*pb.SID, 0, len(lsLink.Srv6EndXSID.Sids)), + SidStructure: &pb.SidStructure{ + LocalBlock: uint32(lsLink.Srv6EndXSID.Srv6SIDStructure.LocalBlock), + LocalNode: uint32(lsLink.Srv6EndXSID.Srv6SIDStructure.LocalNode), + LocalFunc: uint32(lsLink.Srv6EndXSID.Srv6SIDStructure.LocalFunc), + LocalArg: uint32(lsLink.Srv6EndXSID.Srv6SIDStructure.LocalArg), + }, + } - for _, sid := range lsLink.Srv6EndXSID.Sids { - link.Srv6EndXSid.Sids = append(link.Srv6EndXSid.Sids, &pb.SID{ - Sid: sid, - }) + for _, sid := range lsLink.Srv6EndXSID.Sids { + if sid == "" { + continue + } + srv6.Sids = append(srv6.Sids, &pb.SID{Sid: sid}) + } + + link.Srv6EndXSid = srv6 } node.LsLinks = append(node.LsLinks, link) } for _, lsPrefix := range lsNode.Prefixes { - prefix := &pb.LsPrefix{ + if lsPrefix == nil { + continue + } + node.LsPrefixes = append(node.LsPrefixes, &pb.LsPrefix{ Prefix: lsPrefix.Prefix.String(), SidIndex: lsPrefix.SidIndex, - } - - node.LsPrefixes = append(node.LsPrefixes, prefix) + }) } for _, lsSrv6SID := range lsNode.SRv6SIDs { + if lsSrv6SID == nil { + continue + } srv6SID := &pb.LsSrv6SID{ Sids: make([]*pb.SID, 0, len(lsSrv6SID.Sids)), MultiTopoIds: make([]*pb.MultiTopoID, 0, len(lsSrv6SID.MultiTopoIDs)), } for _, sid := range lsSrv6SID.Sids { - srv6SID.Sids = append(srv6SID.Sids, &pb.SID{ - Sid: sid, - }) + if sid == "" { + continue + } + srv6SID.Sids = append(srv6SID.Sids, &pb.SID{Sid: sid}) } for _, topoID := range lsSrv6SID.MultiTopoIDs { @@ -516,10 +530,12 @@ func (s *APIServer) GetTED(ctx context.Context, req *pb.GetTEDRequest) (*pb.GetT }) } - srv6SID.EndpointBehavior = &pb.EndpointBehavior{ - Behavior: uint32(lsSrv6SID.EndpointBehavior.Behavior), - Flags: uint32(lsSrv6SID.EndpointBehavior.Flags), - Algorithm: uint32(lsSrv6SID.EndpointBehavior.Algorithm), + if lsSrv6SID.EndpointBehavior != (table.EndpointBehavior{}) { + srv6SID.EndpointBehavior = &pb.EndpointBehavior{ + Behavior: uint32(lsSrv6SID.EndpointBehavior.Behavior), + Flags: uint32(lsSrv6SID.EndpointBehavior.Flags), + Algorithm: uint32(lsSrv6SID.EndpointBehavior.Algorithm), + } } srv6SID.SidStructure = &pb.SidStructure{ From 5399d6c000f6eb3a29f60744bf1d28380635e332 Mon Sep 17 00:00:00 2001 From: Wataru Mishima Date: Fri, 5 Sep 2025 16:13:58 +0900 Subject: [PATCH 44/87] refactor(ted): update loopback address detection logic --- internal/pkg/table/ted.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/internal/pkg/table/ted.go b/internal/pkg/table/ted.go index 511f001e..7b7b1f34 100644 --- a/internal/pkg/table/ted.go +++ b/internal/pkg/table/ted.go @@ -186,8 +186,14 @@ func (n *LsNode) NodeSegment() (Segment, error) { func (n *LsNode) LoopbackAddr() (netip.Addr, error) { for _, prefix := range n.Prefixes { - if prefix.SidIndex != 0 { - return prefix.Prefix.Addr(), nil + if prefix.Prefix.Addr().Is4() { + if prefix.Prefix.Bits() == 32 { + return prefix.Prefix.Addr(), nil + } + } else if prefix.Prefix.Addr().Is6() { + if prefix.Prefix.Bits() == 128 { + return prefix.Prefix.Addr(), nil + } } } From f4f485461dddf4e7ec2cf018e5a47366499b69be Mon Sep 17 00:00:00 2001 From: Wataru Mishima Date: Fri, 5 Sep 2025 16:24:39 +0900 Subject: [PATCH 45/87] fix(tlv): improve multiple TLV decoding with offset and truncation checks --- pkg/packet/pcep/tlv.go | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/pkg/packet/pcep/tlv.go b/pkg/packet/pcep/tlv.go index 858e367e..15edff64 100644 --- a/pkg/packet/pcep/tlv.go +++ b/pkg/packet/pcep/tlv.go @@ -1258,21 +1258,29 @@ func DecodeTLV(data []byte) (TLVInterface, error) { func DecodeTLVs(data []byte) ([]TLVInterface, error) { var tlvs []TLVInterface + offset := 0 - for len(data) > 0 { - tlv, err := DecodeTLV(data) - if err != nil { - return nil, err + for offset < len(data) { + if len(data[offset:]) < 4 { + return nil, fmt.Errorf("truncated TLV header at offset %d", offset) } - tlvs = append(tlvs, tlv) + tlvType := binary.BigEndian.Uint16(data[offset : offset+2]) + valueLen := int(binary.BigEndian.Uint16(data[offset+2 : offset+4])) + totalLen := 4 + valueLen + + if len(data[offset:]) < totalLen { + return nil, fmt.Errorf("truncated TLV value for type 0x%x at offset %d", tlvType, offset) + } - tlvLen := int(tlv.Len()) - if len(data) < tlvLen { - return nil, fmt.Errorf("expected TLV length %d but found %d bytes remaining", tlvLen, len(data)) + tlv, err := DecodeTLV(data[offset : offset+totalLen]) + if err != nil { + return nil, fmt.Errorf("error decoding TLV type 0x%x: %w", tlvType, err) } - data = data[tlvLen:] + tlvs = append(tlvs, tlv) + paddedLen := (totalLen + 3) & ^3 + offset += paddedLen } return tlvs, nil From 3af5753abd59cab147daa776a1b0072c5f03ec06 Mon Sep 17 00:00:00 2001 From: Wataru Mishima Date: Fri, 5 Sep 2025 16:25:14 +0900 Subject: [PATCH 46/87] fix(tlv): change EndpointsObject receiver to pointer for Len method --- pkg/packet/pcep/object.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/packet/pcep/object.go b/pkg/packet/pcep/object.go index e4b029d5..7fe2bc75 100644 --- a/pkg/packet/pcep/object.go +++ b/pkg/packet/pcep/object.go @@ -1226,7 +1226,7 @@ func (o *EndpointsObject) Serialize() ([]uint8, error) { return byteEndpointsObject, nil } -func (o EndpointsObject) Len() (uint16, error) { +func (o *EndpointsObject) Len() (uint16, error) { var length uint16 if o.SrcAddr.Is4() && o.DstAddr.Is4() { // CommonObjectHeader(4byte) + srcIPv4 (4byte) + dstIPv4 (4byte) From 9a98d9d7988259bf50d5071e1bc5c78621694dc7 Mon Sep 17 00:00:00 2001 From: Wataru Mishima Date: Fri, 5 Sep 2025 16:27:20 +0900 Subject: [PATCH 47/87] fix(object): add details to Endpoints and Association Object errors --- pkg/packet/pcep/object.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/packet/pcep/object.go b/pkg/packet/pcep/object.go index 7fe2bc75..d1f7406b 100644 --- a/pkg/packet/pcep/object.go +++ b/pkg/packet/pcep/object.go @@ -1235,7 +1235,7 @@ func (o *EndpointsObject) Len() (uint16, error) { // CommonObjectHeader(4byte) + srcIPv4 (16byte) + dstIPv4 (16byte) length = commonObjectHeaderLength + 16 + 16 } else { - return uint16(0), errors.New("invalid endpoints address") + return uint16(0), fmt.Errorf("invalid endpoints address (Len()): src=%v dst=%v", o.SrcAddr, o.DstAddr) } return length, nil } @@ -1247,7 +1247,7 @@ func NewEndpointsObject(dstAddr netip.Addr, srcAddr netip.Addr) (*EndpointsObjec } else if dstAddr.Is6() && srcAddr.Is6() { objectType = ObjectTypeEndpointIPv6 } else { - return nil, errors.New("invalid endpoints address") + return nil, fmt.Errorf("invalid endpoints address (NewEndpointsObject): dst=%v src=%v", dstAddr, srcAddr) } o := &EndpointsObject{ @@ -1306,7 +1306,7 @@ func (o *AssociationObject) DecodeFromBytes(typ ObjectType, objectBody []uint8) } } default: - return errors.New("invalid association source address") + return errors.New("invalid association source address (DecodeFromBytes)") } return nil @@ -1354,7 +1354,7 @@ func (o AssociationObject) Len() (uint16, error) { // Reserved(2byte) + Flags(2byte) + Assoc Type(2byte) + Assoc ID(2byte) + IPv6 Assoc Src(16byte) associationObjectBodyLength = uint16(24) + tlvsByteLength } else { - return uint16(0), errors.New("invalid association source address") + return uint16(0), errors.New("invalid association source address (Len())") } return (commonObjectHeaderLength + associationObjectBodyLength), nil } @@ -1373,7 +1373,7 @@ func NewAssociationObject(srcAddr netip.Addr, dstAddr netip.Addr, color uint32, } else if dstAddr.Is6() && srcAddr.Is6() { objectType = ObjectTypeEndpointIPv6 } else { - return nil, errors.New("invalid endpoints address") + return nil, fmt.Errorf("invalid endpoints address (NewAssociationObject): src=%v dst=%v", srcAddr, dstAddr) } o := &AssociationObject{ ObjectType: objectType, From 3d9395d31828af42cc6b8eeb5b87a49f4f3510c5 Mon Sep 17 00:00:00 2001 From: watal Date: Fri, 5 Sep 2025 21:31:06 +0900 Subject: [PATCH 48/87] fix(validation): clarify error messages with policy. prefix --- pkg/server/grpc_server.go | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/pkg/server/grpc_server.go b/pkg/server/grpc_server.go index 70887d33..88b65266 100644 --- a/pkg/server/grpc_server.go +++ b/pkg/server/grpc_server.go @@ -237,51 +237,51 @@ const ( var validator = map[ValidationKind]func(policy *pb.SRPolicy, asn uint32) error{ ValidationAdd: func(policy *pb.SRPolicy, asn uint32) error { if asn == 0 { - return errors.New("ASN must not be zero") + return errors.New("policy.ASN must not be zero") } if policy.PcepSessionAddr == nil { - return errors.New("PCEP session address must not be nil") + return errors.New("policy.PCEP session address must not be nil") } if policy.Color == 0 { - return errors.New("Color must not be zero") + return errors.New("policy.Color must not be zero") } if policy.SrcRouterId == "" { - return errors.New("SrcRouterId must not be empty") + return errors.New("policy.SrcRouterId must not be empty") } if policy.DstRouterId == "" { - return errors.New("DstRouterId must not be empty") + return errors.New("policy.DstRouterId must not be empty") } return nil }, ValidationAddDisablePathCompute: func(policy *pb.SRPolicy, asn uint32) error { if policy.PcepSessionAddr == nil { - return errors.New("PCEP session address must not be nil") + return errors.New("policy.PCEP session address must not be nil") } if len(policy.SrcAddr) == 0 { - return errors.New("SrcAddr must not be empty") + return errors.New("policy.SrcAddr must not be empty") } if len(policy.DstAddr) == 0 { - return errors.New("DstAddr must not be empty") + return errors.New("policy.DstAddr must not be empty") } if len(policy.SegmentList) == 0 { - return errors.New("SegmentList must not be empty") + return errors.New("policy.SegmentList must not be empty") } return nil }, ValidationDelete: func(policy *pb.SRPolicy, asn uint32) error { if policy.PcepSessionAddr == nil { - return errors.New("PCEP session address must not be nil") + return errors.New("policy.PCEP session address must not be nil") } if policy.Color == 0 { - return errors.New("Color must not be zero") + return errors.New("policy.Color must not be zero") } if len(policy.DstAddr) == 0 { - return errors.New("DstAddr must not be empty") + return errors.New("policy.DstAddr must not be empty") } if policy.PolicyName == "" { - return errors.New("PolicyName must not be empty") + return errors.New("policy.PolicyName must not be empty") } return nil }, From cc8b8a3cd939571028f748e0ed7ce8aa6154c9e5 Mon Sep 17 00:00:00 2001 From: Motok1 Date: Wed, 10 Sep 2025 00:31:22 +0900 Subject: [PATCH 49/87] refactor(test): update input and expected data --- test/README.md | 4 ++-- .../show_ted/srmpls/expected/srmpls.json | 14 ++++++++------ .../show_ted/srmpls/input/polad.cfg.yaml | 4 ++-- test/scenario_test/show_ted/srmpls/topo.clab.yaml | 2 +- test/scenario_test/show_ted/test_show_ted.py | 1 + 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/test/README.md b/test/README.md index f447db7a..dfe9f773 100644 --- a/test/README.md +++ b/test/README.md @@ -40,8 +40,8 @@ uv 0.8.13 ### vjunos-router ``` -$ docker images | grep vrnetlab/juniper_vjunos-router | grep 24.2R1-S2.5 -vrnetlab/juniper_vjunos-router 24.2R1-S2.5 27c5af1efc9c 2 months ago 4.17GB +$ docker images | grep vrnetlab/juniper_vjunos-router | grep 25.2R1.9 +vrnetlab/juniper_vjunos-router 25.2R1.9 6e9b1472b46b 37 minutes ago 4.18GB ``` #### how to install image diff --git a/test/scenario_test/show_ted/srmpls/expected/srmpls.json b/test/scenario_test/show_ted/srmpls/expected/srmpls.json index fc1631b9..c0cec496 100644 --- a/test/scenario_test/show_ted/srmpls/expected/srmpls.json +++ b/test/scenario_test/show_ted/srmpls/expected/srmpls.json @@ -10,11 +10,11 @@ "localIP": "10.0.0.1", "metrics": [ { - "type": "IGP", + "type": "METRIC_TYPE_IGP", "value": 10 }, { - "type": "TE", + "type": "METRIC_TYPE_TE", "value": 10 } ], @@ -30,7 +30,8 @@ ], "routerID": "0000.0aff.0001", "srgbBegin": 800000, - "srgbEnd": 804096 + "srgbEnd": 804096, + "srv6SIDs": [] }, { "asn": 65000, @@ -42,11 +43,11 @@ "localIP": "10.0.0.2", "metrics": [ { - "type": "IGP", + "type": "METRIC_TYPE_IGP", "value": 10 }, { - "type": "TE", + "type": "METRIC_TYPE_TE", "value": 10 } ], @@ -62,7 +63,8 @@ ], "routerID": "0000.0aff.0002", "srgbBegin": 800000, - "srgbEnd": 804096 + "srgbEnd": 804096, + "srv6SIDs": [] } ] } diff --git a/test/scenario_test/show_ted/srmpls/input/polad.cfg.yaml b/test/scenario_test/show_ted/srmpls/input/polad.cfg.yaml index 1a5e381f..ccc4218a 100644 --- a/test/scenario_test/show_ted/srmpls/input/polad.cfg.yaml +++ b/test/scenario_test/show_ted/srmpls/input/polad.cfg.yaml @@ -2,7 +2,7 @@ global: pcep: address: "0.0.0.0" port: 4189 - grpc-server: + grpc_server: address: "127.0.0.1" port: 50052 log: @@ -12,6 +12,6 @@ global: enable: true source: "gobgp" gobgp: - grpc-client: + grpc_client: address: "127.0.0.1" port: 50051 diff --git a/test/scenario_test/show_ted/srmpls/topo.clab.yaml b/test/scenario_test/show_ted/srmpls/topo.clab.yaml index 6d029bc4..8a6a4581 100644 --- a/test/scenario_test/show_ted/srmpls/topo.clab.yaml +++ b/test/scenario_test/show_ted/srmpls/topo.clab.yaml @@ -2,7 +2,7 @@ name: srmpls topology: kinds: juniper_vjunosrouter: - image: vrnetlab/juniper_vjunos-router:24.2R1-S2.5 + image: vrnetlab/juniper_vjunos-router:25.2R1.9 nodes: pola: kind: linux diff --git a/test/scenario_test/show_ted/test_show_ted.py b/test/scenario_test/show_ted/test_show_ted.py index 32d6a306..0c6d282e 100644 --- a/test/scenario_test/show_ted/test_show_ted.py +++ b/test/scenario_test/show_ted/test_show_ted.py @@ -33,6 +33,7 @@ def test__srmpls(self, clab_deploy): time.sleep(60) output = json.loads(subprocess.run("docker exec -it clab-srmpls-pola /bin/pola -p 50052 ted -j", shell=True, capture_output=True, text=True).stdout) + print("output is", output) with open(TEST_SRMPLS_DIR+"/expected/srmpls.json") as f: expected_output = json.load(f) From 75b60fbe48628c5c9207a90998ac4e28e7fdd40c Mon Sep 17 00:00:00 2001 From: Wataru Mishima Date: Wed, 10 Sep 2025 14:17:08 +0900 Subject: [PATCH 50/87] chore(example): add SRv6 uSID dynamic-path example --- .../srv6_usid_dynamic-path/README.md | 148 ++++++++++++++++++ .../srv6_usid_dynamic-path/bin/.gitkeep | 0 .../srv6_usid_dynamic-path/gobgpd/gobgpd.yml | 11 ++ .../srv6_usid_dynamic-path/polad/polad.yaml | 18 +++ .../sr-policies/pe02-policy1.yaml | 9 ++ .../startup-configs/p01.cfg | 106 +++++++++++++ .../startup-configs/p02.cfg | 106 +++++++++++++ .../startup-configs/pe01.cfg | 124 +++++++++++++++ .../startup-configs/pe02.cfg | 134 ++++++++++++++++ .../srv6_usid_dynamic-path/topo.clab.yml | 59 +++++++ .../srv6_usid_dynamic-path/topo.png | Bin 0 -> 279139 bytes 11 files changed, 715 insertions(+) create mode 100644 examples/containerlab/srv6_usid_dynamic-path/README.md create mode 100644 examples/containerlab/srv6_usid_dynamic-path/bin/.gitkeep create mode 100644 examples/containerlab/srv6_usid_dynamic-path/gobgpd/gobgpd.yml create mode 100644 examples/containerlab/srv6_usid_dynamic-path/polad/polad.yaml create mode 100644 examples/containerlab/srv6_usid_dynamic-path/sr-policies/pe02-policy1.yaml create mode 100644 examples/containerlab/srv6_usid_dynamic-path/startup-configs/p01.cfg create mode 100644 examples/containerlab/srv6_usid_dynamic-path/startup-configs/p02.cfg create mode 100644 examples/containerlab/srv6_usid_dynamic-path/startup-configs/pe01.cfg create mode 100644 examples/containerlab/srv6_usid_dynamic-path/startup-configs/pe02.cfg create mode 100644 examples/containerlab/srv6_usid_dynamic-path/topo.clab.yml create mode 100644 examples/containerlab/srv6_usid_dynamic-path/topo.png diff --git a/examples/containerlab/srv6_usid_dynamic-path/README.md b/examples/containerlab/srv6_usid_dynamic-path/README.md new file mode 100644 index 00000000..24adf9a1 --- /dev/null +++ b/examples/containerlab/srv6_usid_dynamic-path/README.md @@ -0,0 +1,148 @@ +# SRv6 uSID Dynamic Path + +Example topology powered by [Containerlab](https://containerlab.dev/) + +![Topology](./topo.png) + +## Requirements + +* container host (Linux) +* vJunos image + +## Usage + +### Install Containerlab & vJunos-router + +[Install Containerlab](https://containerlab.dev/install/) + +```bash +sudo bash -c "$(curl -sL https://get.containerlab.dev)" +``` + +Install vJunos on [Vrnetlab](https://containerlab.dev/manual/vrnetlab/) + +### Building a Lab Network + +Create bridge + +```bash +sudo ip link add switch type bridge +sudo ip link set dev switch up +``` + +Copy Pola PCE & GoBGP to bin + +* GoBGP: Use [this version](https://github.com/k1yoto/gobgp/tree/feature/bgp-ls-srv6) +* Pola PCE: Replace the GoBGP module in go.mod with your local GoBGP version, e.g.: + +```text +replace github.com/osrg/gobgp/v4 => ../gobgp +``` + +Start Containerlab network + +```bash +git clone https://github.com/nttcom/pola +cd pola/examples/containerlab/srv6_usid_dynamic-path + +sudo containerlab deploy +``` + +### Starting Daemons + +```bash +$ sudo docker exec -it clab-dynamic-path-gobgp bash +# gobgpd -f /gobgpd.yml +``` + +```bash +$ sudo docker exec -it clab-dynamic-path-pola bash +# polad -f /polad.yaml +``` + +### Show TED +```bash +$ sudo docker exec -it clab-dynamic-path-pola bash +# pola -p 50052 ted +``` + +### Apply SR Policy + +Connect to PCEP container, check PCEP session and SR policy + +```bash +$ sudo docker exec -it clab-dynamic-path-pola bash + +# pola session -p 50052 +sessionAddr(0): fd00::2 + +# pola sr-policy list -p 50052 +No SR Policies found. +``` + +Apply and check SR Policy + +```bash +# pola sr-policy add -f /pe02-policy1.yaml -p 50052 +success! + +# pola sr-policy list -p 50052 +Session: fd00::2 + PolicyName: DYNAMIC-POLICY + SrcAddr: fd00:ffff::2 + DstAddr: fd00:ffff::1 + Color: 100 + Preference: 0 + SegmentList: fcbb:bb00:1004:: -> fcbb:bb00:1003:: -> fcbb:bb00:1001:: +``` + +Enter container pe02 and check SR Policy + +* user: admin +* pass: admin@123 + +```text +$ ssh clab-dynamic-path-pe02 -l admin + +admin@pe02> show spring-traffic-engineering lsp brief +To State LSPname +fd00:ffff::1-100 Up DYNAMIC-POLICY + + +Total displayed LSPs: 1 (Up: 1, Down: 0, Initializing: 0) + +admin@pe02> show spring-traffic-engineering lsp name DYNAMIC-POLICY detail +E = Entropy-label Capability + +Name: DYNAMIC-POLICY + Tunnel-source: Path computation element protocol(PCEP) + Tunnel Forward Type: SRV6 + To: fd00:ffff::1-100 + From: fd00:ffff::2 + State: Up + Path Status: NA + Outgoing interface: NA + Delegation compute constraints info: + Actual-Bandwidth from PCUpdate: 0 + Bandwidth-Requested from PCUpdate: 0 + Setup-Priority: 0 + Reservation-Priority: 0 + Auto-translate status: Disabled Auto-translate result: N/A + BFD status: N/A BFD name: N/A + BFD remote-discriminator: N/A + Segment ID : 129 + ERO Valid: true + SR-ERO hop count: 3 + Hop 1 (Strict): + NAI: IPv6 Node ID, Node address: fcbb:bb00:1004:: + SID type: srv6-sid, Value: fcbb:bb00:1004:: + Hop 2 (Strict): + NAI: IPv6 Node ID, Node address: fcbb:bb00:1003:: + SID type: srv6-sid, Value: fcbb:bb00:1003:: + Hop 3 (Strict): + NAI: IPv6 Node ID, Node address: fcbb:bb00:1001:: + SID type: srv6-sid, Value: fcbb:bb00:1001:: + + +Total displayed LSPs: 1 (Up: 1, Down: 0, Initializing: 0) +``` diff --git a/examples/containerlab/srv6_usid_dynamic-path/bin/.gitkeep b/examples/containerlab/srv6_usid_dynamic-path/bin/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/examples/containerlab/srv6_usid_dynamic-path/gobgpd/gobgpd.yml b/examples/containerlab/srv6_usid_dynamic-path/gobgpd/gobgpd.yml new file mode 100644 index 00000000..37a1af5b --- /dev/null +++ b/examples/containerlab/srv6_usid_dynamic-path/gobgpd/gobgpd.yml @@ -0,0 +1,11 @@ +global: + config: + as: 65000 + router-id: "10.255.0.255" +neighbors: + - config: + peer-as: 65000 + neighbor-address: "fd00::1" + afi-safis: + - config: + afi-safi-name: ls diff --git a/examples/containerlab/srv6_usid_dynamic-path/polad/polad.yaml b/examples/containerlab/srv6_usid_dynamic-path/polad/polad.yaml new file mode 100644 index 00000000..ecf1fe43 --- /dev/null +++ b/examples/containerlab/srv6_usid_dynamic-path/polad/polad.yaml @@ -0,0 +1,18 @@ +global: + pcep: + address: "fd00::3" + port: 4189 + grpc_server: + address: "127.0.0.1" + port: 50052 + log: + path: "/var/log/pola/" + debug: true + name: "polad.log" + ted: + enable: true + source: "gobgp" + gobgp: + grpc_client: + address: "10.0.0.4" + port: 50051 diff --git a/examples/containerlab/srv6_usid_dynamic-path/sr-policies/pe02-policy1.yaml b/examples/containerlab/srv6_usid_dynamic-path/sr-policies/pe02-policy1.yaml new file mode 100644 index 00000000..2d926641 --- /dev/null +++ b/examples/containerlab/srv6_usid_dynamic-path/sr-policies/pe02-policy1.yaml @@ -0,0 +1,9 @@ +asn: 65000 +srPolicy: + pcepSessionAddr: "fd00::2" + srcRouterID: "0000.0001.0002" + dstRouterID: "0000.0001.0001" + name: "DYNAMIC-POLICY" + color: 100 + type: dynamic + metric: igp diff --git a/examples/containerlab/srv6_usid_dynamic-path/startup-configs/p01.cfg b/examples/containerlab/srv6_usid_dynamic-path/startup-configs/p01.cfg new file mode 100644 index 00000000..5d004516 --- /dev/null +++ b/examples/containerlab/srv6_usid_dynamic-path/startup-configs/p01.cfg @@ -0,0 +1,106 @@ +hostname p01 +username admin + group root-lr + group cisco-support + secret 10 $6$MTEbC5od1RuC....$xvQt1LVtwS9akjpzHYOZyj6ZMLZwJ5R9PvRCMaqjrR9iqeoPXisq.rXiiJZPoPE6Gi5XL1yjIlzAY4dfWC6Gr1 +! +grpc + vrf MGMT + no-tls + address-family dual +! +vrf MGMT + address-family ipv4 unicast + ! + address-family ipv6 unicast + ! +! +line default + transport input ssh +! +call-home + service active + contact smart-licensing + profile CiscoTAC-1 + active + destination transport-method email disable + destination transport-method http + ! +! +netconf-yang agent + ssh +! +interface Loopback0 + ipv6 address fd00:ffff::3/128 +! +interface GigabitEthernet0/0/0/0 + description to:pe01 + ipv6 enable +! +interface GigabitEthernet0/0/0/1 + description to:pe02 + ipv6 enable +! +interface GigabitEthernet0/0/0/2 + description to:p02 + ipv6 enable +! +router isis 1 + is-type level-2-only + net 49.0000.0000.0001.0003.00 + address-family ipv6 unicast + metric-style wide + router-id Loopback0 + segment-routing srv6 + locator uSID + level 2 + ! + ! + ! + interface Loopback0 + passive + address-family ipv6 unicast + ! + ! + interface GigabitEthernet0/0/0/0 + circuit-type level-2-only + point-to-point + hello-interval 1 + address-family ipv6 unicast + metric 10 level 2 + ! + ! + interface GigabitEthernet0/0/0/1 + circuit-type level-2-only + point-to-point + hello-interval 1 + address-family ipv6 unicast + metric 100 level 2 + ! + ! + interface GigabitEthernet0/0/0/2 + circuit-type level-2-only + point-to-point + hello-interval 1 + address-family ipv6 unicast + metric 10 level 2 + ! + ! +! +mpls oam +! +segment-routing + srv6 + logging locator status + locators + locator uSID + micro-segment behavior unode psp-usd + prefix fcbb:bb00:1003::/48 + ! + ! + ! +! +ssh server v2 +ssh server vrf MGMT +ssh server netconf vrf MGMT +end diff --git a/examples/containerlab/srv6_usid_dynamic-path/startup-configs/p02.cfg b/examples/containerlab/srv6_usid_dynamic-path/startup-configs/p02.cfg new file mode 100644 index 00000000..db3b3e43 --- /dev/null +++ b/examples/containerlab/srv6_usid_dynamic-path/startup-configs/p02.cfg @@ -0,0 +1,106 @@ +hostname p02 +username admin + group root-lr + group cisco-support + secret 10 $6$MTEbC5od1RuC....$xvQt1LVtwS9akjpzHYOZyj6ZMLZwJ5R9PvRCMaqjrR9iqeoPXisq.rXiiJZPoPE6Gi5XL1yjIlzAY4dfWC6Gr1 +! +grpc + vrf MGMT + no-tls + address-family dual +! +vrf MGMT + address-family ipv4 unicast + ! + address-family ipv6 unicast + ! +! +line default + transport input ssh +! +call-home + service active + contact smart-licensing + profile CiscoTAC-1 + active + destination transport-method email disable + destination transport-method http + ! +! +netconf-yang agent + ssh +! +interface Loopback0 + ipv6 address fd00:ffff::4/128 +! +interface GigabitEthernet0/0/0/0 + description to:pe01 + ipv6 enable +! +interface GigabitEthernet0/0/0/1 + description to:pe02 + ipv6 enable +! +interface GigabitEthernet0/0/0/2 + description to:p01 + ipv6 enable +! +router isis 1 + is-type level-2-only + net 49.0000.0000.0001.0004.00 + address-family ipv6 unicast + metric-style wide + router-id Loopback0 + segment-routing srv6 + locator uSID + level 2 + ! + ! + ! + interface Loopback0 + passive + address-family ipv6 unicast + ! + ! + interface GigabitEthernet0/0/0/0 + circuit-type level-2-only + point-to-point + hello-interval 1 + address-family ipv6 unicast + metric 100 level 2 + ! + ! + interface GigabitEthernet0/0/0/1 + circuit-type level-2-only + point-to-point + hello-interval 1 + address-family ipv6 unicast + metric 10 level 2 + ! + ! + interface GigabitEthernet0/0/0/2 + circuit-type level-2-only + point-to-point + hello-interval 1 + address-family ipv6 unicast + metric 10 level 2 + ! + ! +! +mpls oam +! +segment-routing + srv6 + logging locator status + locators + locator uSID + micro-segment behavior unode psp-usd + prefix fcbb:bb00:1004::/48 + ! + ! + ! +! +ssh server v2 +ssh server vrf MGMT +ssh server netconf vrf MGMT +end diff --git a/examples/containerlab/srv6_usid_dynamic-path/startup-configs/pe01.cfg b/examples/containerlab/srv6_usid_dynamic-path/startup-configs/pe01.cfg new file mode 100644 index 00000000..815f3672 --- /dev/null +++ b/examples/containerlab/srv6_usid_dynamic-path/startup-configs/pe01.cfg @@ -0,0 +1,124 @@ +hostname pe01 +username admin + group root-lr + group cisco-support + secret admin@123 +! +grpc + vrf MGMT + port 9339 + no-tls + address-family dual +! +vrf MGMT + address-family ipv4 unicast + ! + address-family ipv6 unicast + ! +! +line default + transport input ssh +! +call-home + service active + contact smart-licensing + profile CiscoTAC-1 + active + destination transport-method email disable + destination transport-method http + ! +! +netconf-yang agent + ssh +! +interface Loopback0 + ipv6 address fd00:ffff::1/128 +! +interface GigabitEthernet0/0/0/0 + description to:p01 + ipv6 enable +! +interface GigabitEthernet0/0/0/1 + description to:p02 + ipv6 enable +! +interface GigabitEthernet0/0/0/2 + description to:pce + ipv6 address fd00::1/64 +! +router isis 1 + is-type level-2-only + net 49.0000.0000.0001.0001.00 + distribute link-state level 2 + address-family ipv6 unicast + metric-style wide + router-id Loopback0 + segment-routing srv6 + locator uSID + level 2 + ! + ! + ! + interface Loopback0 + passive + address-family ipv6 unicast + ! + ! + interface GigabitEthernet0/0/0/0 + circuit-type level-2-only + point-to-point + hello-interval 1 + address-family ipv6 unicast + metric 10 level 2 + ! + ! + interface GigabitEthernet0/0/0/1 + circuit-type level-2-only + point-to-point + hello-interval 1 + address-family ipv6 unicast + metric 100 level 2 + ! + ! +! +router bgp 65000 + bgp router-id 10.255.0.1 + address-family link-state link-state + ! + neighbor fd00::4 + remote-as 65000 + update-source GigabitEthernet0/0/0/2 + address-family link-state link-state + ! + ! +! +mpls oam +! +segment-routing + traffic-eng + candidate-paths + pcep + ! + ! + pcc + pce address ipv6 fd00::3 + ! + ! + ! + srv6 + logging locator status + micro-segment + merge-overlay-underlay-sids + ! + locators + locator uSID + micro-segment behavior unode psp-usd + prefix fcbb:bb00:1001::/48 + ! + ! + ! +! +ssh server v2 +ssh server vrf MGMT +ssh server netconf vrf MGMT +end diff --git a/examples/containerlab/srv6_usid_dynamic-path/startup-configs/pe02.cfg b/examples/containerlab/srv6_usid_dynamic-path/startup-configs/pe02.cfg new file mode 100644 index 00000000..2eea3283 --- /dev/null +++ b/examples/containerlab/srv6_usid_dynamic-path/startup-configs/pe02.cfg @@ -0,0 +1,134 @@ +chassis { + network-services enhanced-ip; +} + +interfaces { + ge-0/0/0 { + description "to:p01"; + unit 0 { + family iso; + family inet6; + } + } + ge-0/0/1 { + description "to:p02"; + unit 0 { + family iso; + family inet6; + } + } + ge-0/0/2 { + description "POLA-PCE"; + unit 0 { + family inet6 { + address fd00::2/64; + } + } + } + lo0 { + unit 0 { + family iso { + address 49.0000.0000.0001.0002.00; + } + family inet6 { + address fd00:ffff::2/128; + } + } + } +} + +routing-options { + router-id 10.255.0.2; + autonomous-system 65000; + forwarding-table { + srv6-chain-merge; + } + source-packet-routing { + srv6 { + no-reduced-srh; + block usid-block { + fcbb:bb00::/32; + local-micro-sid { + maximum-static-sids 1000; + } + } + locator loc-pe02 { + fcbb:bb00:1002::/48; + micro-sid { + block-name usid-block; + flavor psp; + flavor usd; + } + } + } + } +} + +protocols { + isis { + level 1 disable; + level 2 { + wide-metrics-only; + } + no-ipv4-routing; + topologies { + ipv6-unicast; + } + net 49.0000.0000.0001.0002.00; + interface ge-0/0/0.0 { + point-to-point; + level 2 { + ipv6-unicast-metric 100; + srv6-adjacency-segment { + unprotected { + locator loc-pe02 { + micro-adjacency-sid; + } + } + } + } + } + interface ge-0/0/1.0 { + point-to-point; + level 2 { + ipv6-unicast-metric 10; + srv6-adjacency-segment { + unprotected { + locator loc-pe02 { + micro-adjacency-sid; + } + } + } + } + } + interface lo0.0 { + passive; + } + source-packet-routing { + srv6 { + locator loc-pe02 { + micro-node-sid; + } + } + } + } + mpls { + lsp-external-controller pccd; + } + source-packet-routing { + lsp-external-controller pccd; + srv6; + preserve-nexthop-hierarchy; + } + pcep { + pce POLA-PCE { + local-ipv6-address fd00::2; + destination-ipv6-address fd00::3; + pce-type active; + pce-type stateful; + lsp-provisioning; + spring-capability; + srv6-capability; + } + } +} diff --git a/examples/containerlab/srv6_usid_dynamic-path/topo.clab.yml b/examples/containerlab/srv6_usid_dynamic-path/topo.clab.yml new file mode 100644 index 00000000..61217159 --- /dev/null +++ b/examples/containerlab/srv6_usid_dynamic-path/topo.clab.yml @@ -0,0 +1,59 @@ +name: dynamic-path +topology: + kinds: + xrd: + image: ios-xr/xrd-control-plane:7.8.1 + juniper_vjunosrouter: + image: vrnetlab/juniper_vjunos-router:24.2R1-S2.5 + nodes: + pe01: + kind: xrd + startup-config: startup-configs/pe01.cfg + pe02: + kind: juniper_vjunosrouter + startup-config: startup-configs/pe02.cfg + p01: + kind: xrd + startup-config: startup-configs/p01.cfg + p02: + kind: xrd + startup-config: startup-configs/p02.cfg + pola: + kind: linux + image: golang:1.24.1 + binds: + - bin/polad:/bin/polad + - bin/pola:/bin/pola + - polad/polad.yaml:/polad.yaml + - sr-policies/pe02-policy1.yaml:/pe02-policy1.yaml + exec: + - apt update + - apt install iproute2 -y + - ip addr add 10.0.0.3/24 dev eth1 + - ip -6 addr add fd00::3/64 dev eth1 + gobgp: + kind: linux + image: golang:1.24.1 + binds: + - bin/gobgpd:/bin/gobgpd + - bin/gobgp:/bin/gobgp + - gobgpd/gobgpd.yml:/gobgpd.yml + exec: + - apt update + - apt install iproute2 -y + - ip addr add 10.0.0.4/24 dev eth1 + - ip -6 addr add fd00::4/64 dev eth1 + switch: + kind: bridge + links: + # SRv6 domain + - endpoints: ["pe01:Gi0-0-0-0", "p01:Gi0-0-0-0"] + - endpoints: ["pe01:Gi0-0-0-1", "p02:Gi0-0-0-0"] + - endpoints: ["pe02:eth1", "p01:Gi0-0-0-1"] + - endpoints: ["pe02:eth2", "p02:Gi0-0-0-1"] + - endpoints: ["p01:Gi0-0-0-2", "p02:Gi0-0-0-2"] + # Switch + - endpoints: ["pe01:Gi0-0-0-2", "switch:eth1"] + - endpoints: ["pe02:eth3", "switch:eth2"] + - endpoints: ["pola:eth1", "switch:eth3"] + - endpoints: ["gobgp:eth1", "switch:eth4"] diff --git a/examples/containerlab/srv6_usid_dynamic-path/topo.png b/examples/containerlab/srv6_usid_dynamic-path/topo.png new file mode 100644 index 0000000000000000000000000000000000000000..4a458b629d56d4e99eaec10aeb709d5e39732c0d GIT binary patch literal 279139 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%0Dk3Cq@--di(7U%}v# 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

IKDBQh}Iq5HL~{j~7t5f7k+ux~ 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

l9@6pDT%h#aG<}Rxoj}*>hx4N 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

bP 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 literal 0 HcmV?d00001 From 79c93115e2b0f3bcf6798e3253246cac3babc4b5 Mon Sep 17 00:00:00 2001 From: Wataru Mishima Date: Wed, 10 Sep 2025 16:29:12 +0900 Subject: [PATCH 51/87] fix(color): fix color TLV length --- pkg/packet/pcep/tlv.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/packet/pcep/tlv.go b/pkg/packet/pcep/tlv.go index 15edff64..c4c0a371 100644 --- a/pkg/packet/pcep/tlv.go +++ b/pkg/packet/pcep/tlv.go @@ -1153,7 +1153,7 @@ func (tlv *Color) Serialize() []byte { buf = append(buf, typ...) length := make([]byte, 2) - binary.BigEndian.PutUint16(length, uint16(TLVColor)) + binary.BigEndian.PutUint16(length, TLVColorValueLength) buf = append(buf, length...) color := make([]byte, 4) From a25c5559c62e548c29daa226f4ebbb09123cbcb6 Mon Sep 17 00:00:00 2001 From: watal Date: Fri, 12 Sep 2025 18:30:40 +0900 Subject: [PATCH 52/87] fix(interface): allow nil BGP-LS Attribute --- internal/pkg/gobgp/interface.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/internal/pkg/gobgp/interface.go b/internal/pkg/gobgp/interface.go index e037b768..2d2a9a6d 100644 --- a/internal/pkg/gobgp/interface.go +++ b/internal/pkg/gobgp/interface.go @@ -103,8 +103,9 @@ func ConvertToTEDElem(dst *api.Destination) ([]table.TEDElem, error) { } } if lsAttr == nil { - return nil, errors.New("BGP-LS Attribute is nil") + return nil, nil } + switch lsAddrPrefix.GetType() { case api.LsNLRIType_LS_NLRI_TYPE_NODE: lsAttrNode := lsAttr.Ls.GetNode() @@ -116,7 +117,6 @@ func ConvertToTEDElem(dst *api.Destination) ([]table.TEDElem, error) { return nil, fmt.Errorf("failed to process LS Node NLRI: %w", err) } return []table.TEDElem{lsNode}, nil - case api.LsNLRIType_LS_NLRI_TYPE_LINK: lsAttrLink := lsAttr.Ls.GetLink() if lsAttrLink == nil { @@ -172,7 +172,6 @@ func ConvertToTEDElem(dst *api.Destination) ([]table.TEDElem, error) { return nil, fmt.Errorf("failed to process LS SRv6 SID NLRI: %w", err) } return lsSrv6SIDList, nil - default: return nil, fmt.Errorf("invalid LS Link State NLRI type: %s", lsAddrPrefix.GetType().String()) } From ed39fb7ade9ce0ad92d9439878418e350e153f29 Mon Sep 17 00:00:00 2001 From: Motok1 Date: Sun, 14 Sep 2025 00:52:08 +0900 Subject: [PATCH 53/87] feature(test): add test of "show ted" for SRv6 uSID --- .../srv6-usid/expected/srv6-usid.json | 99 +++++++++++++ .../show_ted/srv6-usid/input/gobgpd.cfg.yaml | 11 ++ .../show_ted/srv6-usid/input/polad.cfg.yaml | 17 +++ .../show_ted/srv6-usid/jun-rt1_conf.txt | 133 ++++++++++++++++++ .../show_ted/srv6-usid/jun-rt2_conf.txt | 89 ++++++++++++ .../show_ted/srv6-usid/topo.clab.yaml | 39 +++++ test/scenario_test/show_ted/test_show_ted.py | 22 +++ 7 files changed, 410 insertions(+) create mode 100644 test/scenario_test/show_ted/srv6-usid/expected/srv6-usid.json create mode 100644 test/scenario_test/show_ted/srv6-usid/input/gobgpd.cfg.yaml create mode 100644 test/scenario_test/show_ted/srv6-usid/input/polad.cfg.yaml create mode 100644 test/scenario_test/show_ted/srv6-usid/jun-rt1_conf.txt create mode 100644 test/scenario_test/show_ted/srv6-usid/jun-rt2_conf.txt create mode 100644 test/scenario_test/show_ted/srv6-usid/topo.clab.yaml diff --git a/test/scenario_test/show_ted/srv6-usid/expected/srv6-usid.json b/test/scenario_test/show_ted/srv6-usid/expected/srv6-usid.json new file mode 100644 index 00000000..f0f6e877 --- /dev/null +++ b/test/scenario_test/show_ted/srv6-usid/expected/srv6-usid.json @@ -0,0 +1,99 @@ +{ + "ted": [ + { + "asn": 65000, + "hostname": "jun-rt1", + "isisAreaID": "49.0000", + "links": [ + { + "adjSid": 0, + "localIP": "None", + "metrics": [ + { + "type": "METRIC_TYPE_IGP", + "value": 10 + }, + { + "type": "METRIC_TYPE_TE", + "value": 10 + } + ], + "remoteIP": "None", + "remoteNode": "0000.0aff.0002" + } + ], + "prefixes": [ + { + "prefix": "fd00::/64" + }, + { + "prefix": "fcbb:bb00:1001::/48" + } + ], + "routerID": "0000.0aff.0001", + "srgbBegin": 0, + "srgbEnd": 0, + "srv6SIDs": [ + { + "endpointBehavior": { + "Behavior": 48, + "Flags": 0, + "Algorithm": 0 + }, + "multiTopoIDs": [ + 2 + ], + "sids": [ + "fcbb:bb00:1001::" + ] + } + ] + }, + { + "asn": 65000, + "hostname": "jun-rt2", + "isisAreaID": "49.0000", + "links": [ + { + "adjSid": 0, + "localIP": "None", + "metrics": [ + { + "type": "METRIC_TYPE_IGP", + "value": 10 + }, + { + "type": "METRIC_TYPE_TE", + "value": 10 + } + ], + "remoteIP": "None", + "remoteNode": "0000.0aff.0001" + } + ], + "prefixes": [ + { + "prefix": "fcbb:bb00:1002::/48" + } + ], + "routerID": "0000.0aff.0002", + "srgbBegin": 0, + "srgbEnd": 0, + "srv6SIDs": [ + { + "endpointBehavior": { + "Behavior": 48, + "Flags": 0, + "Algorithm": 0 + }, + "multiTopoIDs": [ + 2 + ], + "sids": [ + "fcbb:bb00:1002::" + ] + } + ] + } + ] +} diff --git a/test/scenario_test/show_ted/srv6-usid/input/gobgpd.cfg.yaml b/test/scenario_test/show_ted/srv6-usid/input/gobgpd.cfg.yaml new file mode 100644 index 00000000..3945da09 --- /dev/null +++ b/test/scenario_test/show_ted/srv6-usid/input/gobgpd.cfg.yaml @@ -0,0 +1,11 @@ +global: + config: + as: 65000 + router-id: "10.255.0.255" +neighbors: + - config: + peer-as: 65000 + neighbor-address: "10.100.0.1" + afi-safis: + - config: + afi-safi-name: ls diff --git a/test/scenario_test/show_ted/srv6-usid/input/polad.cfg.yaml b/test/scenario_test/show_ted/srv6-usid/input/polad.cfg.yaml new file mode 100644 index 00000000..9fd9766e --- /dev/null +++ b/test/scenario_test/show_ted/srv6-usid/input/polad.cfg.yaml @@ -0,0 +1,17 @@ +global: + pcep: + address: "::" + port: 4189 + grpc_server: + address: "127.0.0.1" + port: 50052 + log: + path: "/var/log/pola/" + name: "polad.log" + ted: + enable: true + source: "gobgp" + gobgp: + grpc_client: + address: "127.0.0.1" + port: 50051 diff --git a/test/scenario_test/show_ted/srv6-usid/jun-rt1_conf.txt b/test/scenario_test/show_ted/srv6-usid/jun-rt1_conf.txt new file mode 100644 index 00000000..8b7c352f --- /dev/null +++ b/test/scenario_test/show_ted/srv6-usid/jun-rt1_conf.txt @@ -0,0 +1,133 @@ +interfaces { + ge-0/0/0 { + description to:rt2; + unit 0 { + family iso; + family inet6; + } + } + ge-0/0/1 { + description to:gobgp; + unit 0 { + family inet { + address 10.100.0.1/24; + } + family inet6 { + address fd00::1/64; + } + } + } + lo0 { + unit 0 { + family iso { + address 49.0000.0000.0aff.0001.00; + } + family inet6 { + address fd00:ffff::1/128; + } + } + } +} +policy-options { + policy-statement IGP-REDISTRIBUTE { + term 1 { + from protocol direct; + then accept; + } + } + policy-statement TE { + term 1 { + from family traffic-engineering; + then accept; + } + } +} +routing-options { + source-packet-routing { + srv6 { + block usid-block { + fcbb:bb00:1001::/32; + local-micro-sid { + maximum-static-sids 1791; + } + } + locator loc-rt1 { + fcbb:bb00:1001::/48; + micro-sid { + block-name usid-block; + flavor { + psp; + usd; + } + } + } + no-reduced-srh; + } + } + router-id 10.255.0.1; + autonomous-system 65000; + forwarding-table { + srv6-chain-merge; + } +} +protocols { + bgp { + group GOBGP { + type internal; + local-address 10.100.0.1; + family traffic-engineering { + unicast; + } + export TE; + neighbor 10.100.0.2; + } + } + isis { + interface ge-0/0/0.0 { + level 2 { + srv6-adjacency-segment { + unprotected { + locator loc-rt1 { + micro-adjacency-sid; + } + } + } + } + point-to-point; + } + interface lo0.0 { + passive; + } + source-packet-routing { + srv6 { + locator loc-rt1 micro-node-sid; + } + } + level 1 disable; + level 2 wide-metrics-only; + traffic-engineering { + l3-unicast-topology; + ipv6; + advertisement always; + } + no-ipv4-routing; + topologies ipv6-unicast; + export IGP-REDISTRIBUTE; + } + mpls { + traffic-engineering { + database { + import { + l3-unicast-topology { + bgp-link-state; + } + policy TE; + } + } + } + } + source-packet-routing { + srv6; + preserve-nexthop-hierarchy; + } +} diff --git a/test/scenario_test/show_ted/srv6-usid/jun-rt2_conf.txt b/test/scenario_test/show_ted/srv6-usid/jun-rt2_conf.txt new file mode 100644 index 00000000..5ae03b7a --- /dev/null +++ b/test/scenario_test/show_ted/srv6-usid/jun-rt2_conf.txt @@ -0,0 +1,89 @@ +interfaces { + ge-0/0/0 { + description to:rt1; + unit 0 { + family iso; + family inet6; + } + } + fxp0 { + unit 0 { + family inet { + address 10.0.0.15/24; + } + family inet6 { + address 2001:db8::2/64; + } + } + } + lo0 { + unit 0 { + family iso { + address 49.0000.0000.0aff.0002.00; + } + family inet6 { + address fd00:ffff::2/128; + } + } + } +} +routing-options { + source-packet-routing { + srv6 { + block usid-block { + fcbb:bb00:1002::/32; + local-micro-sid { + maximum-static-sids 1791; + } + } + locator loc-rt2 { + fcbb:bb00:1002::/48; + micro-sid { + block-name usid-block; + flavor { + psp; + usd; + } + } + } + no-reduced-srh; + } + } + router-id 10.255.0.2; + autonomous-system 65000; + forwarding-table { + srv6-chain-merge; + } +} +protocols { + isis { + interface ge-0/0/0.0 { + level 2 { + srv6-adjacency-segment { + unprotected { + locator loc-rt2 { + micro-adjacency-sid; + } + } + } + } + point-to-point; + } + interface lo0.0 { + passive; + } + source-packet-routing { + srv6 { + locator loc-rt2 micro-node-sid; + } + } + level 1 disable; + level 2 wide-metrics-only; + no-ipv4-routing; + topologies ipv6-unicast; + } + source-packet-routing { + srv6; + preserve-nexthop-hierarchy; + } +} diff --git a/test/scenario_test/show_ted/srv6-usid/topo.clab.yaml b/test/scenario_test/show_ted/srv6-usid/topo.clab.yaml new file mode 100644 index 00000000..aac26d9b --- /dev/null +++ b/test/scenario_test/show_ted/srv6-usid/topo.clab.yaml @@ -0,0 +1,39 @@ +name: srv6-usid +topology: + kinds: + juniper_vjunosrouter: + image: vrnetlab/juniper_vjunos-router:25.2R1.9 + nodes: + pola: + kind: linux + image: golang:1.24.2 + network-mode: container:gobgp + startup-delay: 10 + binds: + - ../../../bin/polad:/bin/polad + - ../../../bin/pola:/bin/pola + - input/polad.cfg.yaml:/polad.cfg.yaml + cmd: /bin/polad -f /polad.cfg.yaml & + gobgp: + kind: linux + image: golang:1.24.2 + binds: + - ../../../bin/gobgpd:/bin/gobgpd + - ../../../bin/gobgp:/bin/gobgp + - input/gobgpd.cfg.yaml:/gobgpd.cfg.yaml + exec: + - apt update + - apt install -y iproute2 iputils-ping + - ip a add dev eth1 10.100.0.2/24 + - ip -6 a add dev eth1 fd00::2/64 + - ip -6 r add fd00:ffff::/64 via fd00::1 + cmd: /bin/gobgpd -f /gobgpd.cfg.yaml & + jun-rt1: + kind: juniper_vjunosrouter + startup-config: jun-rt1_conf.txt + jun-rt2: + kind: juniper_vjunosrouter + startup-config: jun-rt2_conf.txt + links: + - endpoints: ["jun-rt1:ge-0/0/0", "jun-rt2:ge-0/0/0"] + - endpoints: ["gobgp:eth1", "jun-rt1:ge-0/0/1"] diff --git a/test/scenario_test/show_ted/test_show_ted.py b/test/scenario_test/show_ted/test_show_ted.py index 0c6d282e..2c0959db 100644 --- a/test/scenario_test/show_ted/test_show_ted.py +++ b/test/scenario_test/show_ted/test_show_ted.py @@ -39,3 +39,25 @@ def test__srmpls(self, clab_deploy): # Run "pola ted" cmd and ensure it returns the expected result. assert DeepDiff(output, expected_output, ignore_order=True) == {} + + + def test__srv6_usid(self, clab_deploy): + TEST_SRV6_USID_DIR = self.TEST_SHOW_TED_DIR + "/srv6-usid" + # deploy test environment by containerlab + clab_deploy(TEST_SRV6_USID_DIR) + + # wait for vJunosRouter booting + while subprocess.run("docker exec -it clab-srv6-usid-gobgp ping fd00:ffff::2 -c 1", shell=True).returncode != 0: + print("Wait for deploy vJunosRouter ...") + time.sleep(10) + + print("Wait for pola's TED to finish syncing...") + time.sleep(60) + + output = json.loads(subprocess.run("docker exec -it clab-srv6-usid-pola /bin/pola -p 50052 ted -j", shell=True, capture_output=True, text=True).stdout) + print("output is", output) + with open(TEST_SRV6_USID_DIR+"/expected/srv6-usid.json") as f: + expected_output = json.load(f) + + # Run "pola ted" cmd and ensure it returns the expected result. + assert DeepDiff(output, expected_output, ignore_order=True) == {} From 93d14dd2edbc33e4b7223b161cadefc37a08245a Mon Sep 17 00:00:00 2001 From: watal Date: Fri, 26 Sep 2025 21:09:43 +0900 Subject: [PATCH 54/87] fix(ted): rename print() --- cmd/pola/sr_policy_add.go | 20 ++++++++++---------- cmd/pola/ted.go | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cmd/pola/sr_policy_add.go b/cmd/pola/sr_policy_add.go index 9ccf1847..51063397 100644 --- a/cmd/pola/sr_policy_add.go +++ b/cmd/pola/sr_policy_add.go @@ -65,26 +65,26 @@ func newSRPolicyAddCmd() *cobra.Command { type Segment struct { SID string `yaml:"sid"` - LocalAddr string `yaml:"local_addr"` - RemoteAddr string `yaml:"remote_addr"` - SIDStructure string `yaml:"sid_structure"` + LocalAddr string `yaml:"localAddr"` + RemoteAddr string `yaml:"remoteAddr"` + SIDStructure string `yaml:"sidStructure"` } type SRPolicy struct { - PCEPSessionAddr netip.Addr `yaml:"pcep_session_addr"` - SrcAddr netip.Addr `yaml:"src_addr"` - DstAddr netip.Addr `yaml:"dst_addr"` - SrcRouterID string `yaml:"src_router_id"` - DstRouterID string `yaml:"dst_router_id"` + PCEPSessionAddr netip.Addr `yaml:"pcepSessionAddr"` + SrcAddr netip.Addr `yaml:"srcAddr"` + DstAddr netip.Addr `yaml:"dstAddr"` + SrcRouterID string `yaml:"srcRouterID"` + DstRouterID string `yaml:"dstRouterID"` Name string `yaml:"name"` - SegmentList []Segment `yaml:"segment_list"` + SegmentList []Segment `yaml:"segmentList"` Color uint32 `yaml:"color"` Type string `yaml:"type"` Metric string `yaml:"metric"` } type InputFormat struct { - SRPolicy SRPolicy `yaml:"sr_policy"` + SRPolicy SRPolicy `yaml:"srPolicy"` ASN uint32 `yaml:"asn"` } diff --git a/cmd/pola/ted.go b/cmd/pola/ted.go index 8590c465..b254a24d 100644 --- a/cmd/pola/ted.go +++ b/cmd/pola/ted.go @@ -17,7 +17,7 @@ func newTEDCmd() *cobra.Command { return &cobra.Command{ Use: "ted", RunE: func(cmd *cobra.Command, args []string) error { - if err := print(jsonFmt); err != nil { + if err := printTED(jsonFmt); err != nil { return err } return nil @@ -25,7 +25,7 @@ func newTEDCmd() *cobra.Command { } } -func print(jsonFlag bool) error { +func printTED(jsonFlag bool) error { ted, err := grpc.GetTED(client) if err != nil { return err From 6fb92cf4c02cb28a15c5e0d33a4b7d4ab6908056 Mon Sep 17 00:00:00 2001 From: watal Date: Fri, 26 Sep 2025 21:17:24 +0900 Subject: [PATCH 55/87] fix(config): camelCase --- configs/polad.yaml.sample | 6 +++--- docs/schemas/polad_config.json | 10 +++++----- docs/sources/getting-started.md | 8 ++++---- examples/containerlab/sr-mpls_pcep/polad/polad.yaml | 2 +- examples/containerlab/srv6_te_l3vpn/polad/polad.yaml | 2 +- .../srv6_usid_dynamic-path/polad/polad.yaml | 4 ++-- examples/tinet/sr-mpls_te_l3vpn/polad/polad.yaml | 2 +- internal/config/config.go | 6 +++--- .../scenario_test/show_ted/srmpls/input/polad.cfg.yaml | 4 ++-- .../show_ted/srv6-usid/input/polad.cfg.yaml | 4 ++-- 10 files changed, 24 insertions(+), 24 deletions(-) diff --git a/configs/polad.yaml.sample b/configs/polad.yaml.sample index 66fa3d74..42b13f68 100644 --- a/configs/polad.yaml.sample +++ b/configs/polad.yaml.sample @@ -2,7 +2,7 @@ global: pcep: address: "127.0.0.1" port: 4189 - grpc-server: + grpcServer: address: "127.0.0.1" port: 50052 log: @@ -12,7 +12,7 @@ global: enable: true source: "gobgp" gobgp: - grpc-client: + grpcClient: address: "127.0.0.1" port: 50051 - usid-mode: true + usidMode: true diff --git a/docs/schemas/polad_config.json b/docs/schemas/polad_config.json index a499bb2d..676abb1e 100644 --- a/docs/schemas/polad_config.json +++ b/docs/schemas/polad_config.json @@ -28,7 +28,7 @@ "port" ] }, - "grpc-server": { + "grpcServer": { "type": "object", "properties": { "address": { @@ -89,7 +89,7 @@ "gobgp": { "type": "object", "properties": { - "grpc-client": { + "grpcClient": { "type": "object", "properties": { "address": { @@ -114,17 +114,17 @@ } }, "required": [ - "grpc-client" + "grpcClient" ] }, - "usid-mode": { + "usidMode": { "type": "boolean", "default": false } }, "required": [ "pcep", - "grpc-server", + "grpcServer", "log", "ted" ] diff --git a/docs/sources/getting-started.md b/docs/sources/getting-started.md index 8d8ad443..95c70a9f 100644 --- a/docs/sources/getting-started.md +++ b/docs/sources/getting-started.md @@ -46,7 +46,7 @@ global: pcep: address: "2001:0db8::254" port: 4189 - grpc-server: + grpcServer: address: "127.0.0.1" port: 50051 log: @@ -54,7 +54,7 @@ global: name: "polad.log" ted: enable: false - usid-mode: false + usidMode: false ``` ### case: TED enable @@ -72,7 +72,7 @@ global: pcep: address: "192.0.2.254" port: 4189 - grpc-server: + grpcServer: address: "127.0.0.1" port: 50052 log: @@ -82,7 +82,7 @@ global: enable: true source: "gobgp" gobgp: - grpc-client: + grpcClient: address: "127.0.0.1" port: 50051 ``` diff --git a/examples/containerlab/sr-mpls_pcep/polad/polad.yaml b/examples/containerlab/sr-mpls_pcep/polad/polad.yaml index 2c83de01..993e1c17 100644 --- a/examples/containerlab/sr-mpls_pcep/polad/polad.yaml +++ b/examples/containerlab/sr-mpls_pcep/polad/polad.yaml @@ -2,7 +2,7 @@ global: pcep: address: "10.0.255.254" port: 4189 - grpc-server: + grpcServer: address: "127.0.0.1" port: 50051 log: diff --git a/examples/containerlab/srv6_te_l3vpn/polad/polad.yaml b/examples/containerlab/srv6_te_l3vpn/polad/polad.yaml index d638913a..bb2b48f4 100644 --- a/examples/containerlab/srv6_te_l3vpn/polad/polad.yaml +++ b/examples/containerlab/srv6_te_l3vpn/polad/polad.yaml @@ -2,7 +2,7 @@ global: pcep: address: "[fd00::ffff]" port: 4189 - grpc-server: + grpcServer: address: "127.0.0.1" port: 50051 log: diff --git a/examples/containerlab/srv6_usid_dynamic-path/polad/polad.yaml b/examples/containerlab/srv6_usid_dynamic-path/polad/polad.yaml index ecf1fe43..da35d9bc 100644 --- a/examples/containerlab/srv6_usid_dynamic-path/polad/polad.yaml +++ b/examples/containerlab/srv6_usid_dynamic-path/polad/polad.yaml @@ -2,7 +2,7 @@ global: pcep: address: "fd00::3" port: 4189 - grpc_server: + grpcServer: address: "127.0.0.1" port: 50052 log: @@ -13,6 +13,6 @@ global: enable: true source: "gobgp" gobgp: - grpc_client: + grpcClient: address: "10.0.0.4" port: 50051 diff --git a/examples/tinet/sr-mpls_te_l3vpn/polad/polad.yaml b/examples/tinet/sr-mpls_te_l3vpn/polad/polad.yaml index bedd729d..3a8302f4 100644 --- a/examples/tinet/sr-mpls_te_l3vpn/polad/polad.yaml +++ b/examples/tinet/sr-mpls_te_l3vpn/polad/polad.yaml @@ -2,7 +2,7 @@ global: pcep: address: "10.0.255.254" port: 4189 - grpc-server: + grpcServer: address: "127.0.0.1" port: 50051 log: diff --git a/internal/config/config.go b/internal/config/config.go index fc24948e..bd993afc 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -34,7 +34,7 @@ type Log struct { } type GoBGP struct { - GRPCClient GRPCClient `yaml:"grpc_client"` + GRPCClient GRPCClient `yaml:"grpcClient"` } type TED struct { @@ -44,11 +44,11 @@ type TED struct { type Global struct { PCEP PCEP `yaml:"pcep"` - GRPCServer GRPCServer `yaml:"grpc_server"` + GRPCServer GRPCServer `yaml:"grpcServer"` Log Log `yaml:"log"` TED *TED `yaml:"ted"` GoBGP GoBGP `yaml:"gobgp"` - USidMode bool `yaml:"usid_mode"` + USidMode bool `yaml:"usidMode"` } type Config struct { diff --git a/test/scenario_test/show_ted/srmpls/input/polad.cfg.yaml b/test/scenario_test/show_ted/srmpls/input/polad.cfg.yaml index ccc4218a..c3e3b881 100644 --- a/test/scenario_test/show_ted/srmpls/input/polad.cfg.yaml +++ b/test/scenario_test/show_ted/srmpls/input/polad.cfg.yaml @@ -2,7 +2,7 @@ global: pcep: address: "0.0.0.0" port: 4189 - grpc_server: + grpcServer: address: "127.0.0.1" port: 50052 log: @@ -12,6 +12,6 @@ global: enable: true source: "gobgp" gobgp: - grpc_client: + grpcClient: address: "127.0.0.1" port: 50051 diff --git a/test/scenario_test/show_ted/srv6-usid/input/polad.cfg.yaml b/test/scenario_test/show_ted/srv6-usid/input/polad.cfg.yaml index 9fd9766e..ebd16397 100644 --- a/test/scenario_test/show_ted/srv6-usid/input/polad.cfg.yaml +++ b/test/scenario_test/show_ted/srv6-usid/input/polad.cfg.yaml @@ -2,7 +2,7 @@ global: pcep: address: "::" port: 4189 - grpc_server: + grpcServer: address: "127.0.0.1" port: 50052 log: @@ -12,6 +12,6 @@ global: enable: true source: "gobgp" gobgp: - grpc_client: + grpcClient: address: "127.0.0.1" port: 50051 From 0f2cc7106af60a15afa6b36648062a28e298715a Mon Sep 17 00:00:00 2001 From: watal Date: Tue, 21 Oct 2025 19:24:00 +0900 Subject: [PATCH 56/87] fix(docs): fix headings, code blocks, and links per markdownlint --- .../srv6_usid_dynamic-path/README.md | 1 + test/README.md | 26 +++++++++---------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/examples/containerlab/srv6_usid_dynamic-path/README.md b/examples/containerlab/srv6_usid_dynamic-path/README.md index 24adf9a1..07e68552 100644 --- a/examples/containerlab/srv6_usid_dynamic-path/README.md +++ b/examples/containerlab/srv6_usid_dynamic-path/README.md @@ -61,6 +61,7 @@ $ sudo docker exec -it clab-dynamic-path-pola bash ``` ### Show TED + ```bash $ sudo docker exec -it clab-dynamic-path-pola bash # pola -p 50052 ted diff --git a/test/README.md b/test/README.md index dfe9f773..93abc77a 100644 --- a/test/README.md +++ b/test/README.md @@ -1,6 +1,5 @@ # Scenario Test - ## 1. Install Required Tools and Container Images Make sure the following tools and container images are installed: @@ -8,14 +7,15 @@ Make sure the following tools and container images are installed: ### Docker \* Note: Please make it executable without using sudo -``` + +```bash $ docker --version Docker version 28.3.3, build 980b856 ``` ### containerlab -``` +```bash $ containerlab version ____ ___ _ _ _____ _ ___ _ _ _____ ____ _ _ / ___/ _ \| \ | |_ _|/ \ |_ _| \ | | ____| _ \| | __ _| |__ @@ -32,29 +32,27 @@ $ containerlab version ### uv -``` +```bash $ uv -V uv 0.8.13 ``` ### vjunos-router -``` +```bash $ docker images | grep vrnetlab/juniper_vjunos-router | grep 25.2R1.9 vrnetlab/juniper_vjunos-router 25.2R1.9 6e9b1472b46b 37 minutes ago 4.18GB ``` #### how to install image -1. Get VM image from [here](https://support.juniper.net/support/downloads/) +1. Get VM image from [Juniper support downloads page](https://support.juniper.net/support/downloads/) 2. Clone the vrnetlab repository from [GitHub](https://github.com/srl-labs/vrnetlab/tree/master) -3. Create vjunos-router image - +3. Create vjunos-router image ## 2. Synchronize Dependencies with uv -``` - +```bash cd /test uv sync ``` @@ -64,7 +62,8 @@ uv sync Place the binary you want to test in the appropriate location. ex: -``` + +```bash $ ls -la /test/bin drwxrwxr-x 2 --- --- 4096 Sep 3 06:06 . drwxrwxr-x 9 --- --- 4096 Aug 28 01:23 .. @@ -76,10 +75,11 @@ drwxrwxr-x 9 --- --- 4096 Aug 28 01:23 .. ``` ## 4. Run the Test + Execute the test using the appropriate command or script. \* Note: Make sure to run `uv run pytest` with the `-s` option. Otherwise, the test fail. -``` -$ uv run pytest -s +```bash +uv run pytest -s ``` From 65e006bb1d01f849832b8e56735932c5c878a5cb Mon Sep 17 00:00:00 2001 From: watal Date: Fri, 26 Sep 2025 21:19:44 +0900 Subject: [PATCH 57/87] refactor(interface): simplify getLsNode --- internal/pkg/gobgp/interface.go | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/internal/pkg/gobgp/interface.go b/internal/pkg/gobgp/interface.go index 2d2a9a6d..9977da56 100644 --- a/internal/pkg/gobgp/interface.go +++ b/internal/pkg/gobgp/interface.go @@ -191,23 +191,19 @@ func formatIsisAreaID(isisArea []byte) string { } func getLsNode(typedLinkStateNLRI *api.LsAddrPrefix, lsAttrNode *api.LsAttributeNode) (*table.LsNode, error) { - lsNodeNLRI := typedLinkStateNLRI.Nlri.GetNode() - asn := lsNodeNLRI.GetLocalNode().GetAsn() - routerID := lsNodeNLRI.GetLocalNode().GetIgpRouterId() + localNode := typedLinkStateNLRI.Nlri.GetNode().GetLocalNode() + lsNode := table.NewLsNode(localNode.GetAsn(), localNode.GetIgpRouterId()) - lsNode := table.NewLsNode(asn, routerID) - - isisArea := lsAttrNode.GetIsisArea() - lsNode.IsisAreaID = formatIsisAreaID(isisArea) + lsNode.IsisAreaID = formatIsisAreaID(lsAttrNode.GetIsisArea()) lsNode.Hostname = lsAttrNode.GetName() + if lsAttrNode.GetSrCapabilities() != nil { srCapabilities := lsAttrNode.GetSrCapabilities().GetRanges() if len(srCapabilities) != 1 { return nil, fmt.Errorf("expected 1 SR Capability TLV, got: %d", len(srCapabilities)) - } else { - lsNode.SrgbBegin = srCapabilities[0].GetBegin() - lsNode.SrgbEnd = srCapabilities[0].GetEnd() } + lsNode.SrgbBegin = srCapabilities[0].GetBegin() + lsNode.SrgbEnd = srCapabilities[0].GetEnd() } return lsNode, nil } From 53b6fd61db505a5474ffe94491ad5879758ab704 Mon Sep 17 00:00:00 2001 From: watal Date: Fri, 26 Sep 2025 21:21:07 +0900 Subject: [PATCH 58/87] refactor(object): early return --- pkg/packet/pcep/object.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/packet/pcep/object.go b/pkg/packet/pcep/object.go index d1f7406b..f5e5b53b 100644 --- a/pkg/packet/pcep/object.go +++ b/pkg/packet/pcep/object.go @@ -870,9 +870,8 @@ func NewEroSubobject(seg table.Segment) (EroSubobject, error) { return nil, err } return subo, nil - } else { - return nil, errors.New("invalid Segment type") } + return nil, errors.New("invalid Segment type") } // SR-ERO Subobject (RFC8664 4.3.1) From 4d199e694f9c1c169ff498a188e25b7c98789e39 Mon Sep 17 00:00:00 2001 From: watal Date: Fri, 26 Sep 2025 21:25:26 +0900 Subject: [PATCH 59/87] refactor(tlv): rename cap --- pkg/packet/pcep/tlv.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/packet/pcep/tlv.go b/pkg/packet/pcep/tlv.go index c4c0a371..f1cf8dad 100644 --- a/pkg/packet/pcep/tlv.go +++ b/pkg/packet/pcep/tlv.go @@ -1225,8 +1225,8 @@ func (tlv *UndefinedTLV) Len() uint16 { } func (tlv *UndefinedTLV) CapStrings() []string { - cap := "unknown_type_" + strconv.FormatInt(int64(tlv.Typ), 10) - return []string{cap} + capStr := "unknown_type_" + strconv.FormatInt(int64(tlv.Typ), 10) + return []string{capStr} } func (tlv *UndefinedTLV) SetLength() { From 4c69402cadc16ec8780980a54fa5824d79cb130a Mon Sep 17 00:00:00 2001 From: watal Date: Fri, 26 Sep 2025 21:27:25 +0900 Subject: [PATCH 60/87] refactor(session): remove explicit 0 assignment --- pkg/server/session.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/server/session.go b/pkg/server/session.go index 034b6e29..8dc4d812 100644 --- a/pkg/server/session.go +++ b/pkg/server/session.go @@ -486,8 +486,10 @@ func (ss *Session) SendPCUpdate(srPolicy table.SRPolicy) error { } func (ss *Session) RegisterSRPolicy(sr pcep.StateReport) { - var color uint32 = 0 // Default color value (RFC does not specify a default) - var preference uint32 = 0 // Default preference value (RFC does not specify a default) + var ( + color uint32 // initialized to zero (RFC does not specify a default) + preference uint32 // initialized to zero (RFC does not specify a default) + ) if ss.pccType == pcep.CiscoLegacy { // In Cisco legacy mode, get color and preference from Vendor Information Object From 56532ff2383a6fee6b75b1b86e34db4ebb3a0045 Mon Sep 17 00:00:00 2001 From: watal Date: Mon, 29 Sep 2025 13:53:59 +0900 Subject: [PATCH 61/87] Bump go from 1.24.5 to 1.25.3 --- go.mod | 3 +-- go.sum | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 9d46a676..954ac177 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,12 @@ module github.com/nttcom/pola -go 1.24.5 +go 1.25.3 require ( github.com/osrg/gobgp/v4 v4.0.0-20251020140220-f6bf251a9a7f github.com/spf13/cobra v1.10.1 github.com/stretchr/testify v1.11.1 go.uber.org/zap v1.27.0 - golang.org/x/exp v0.0.0-20251017212417-90e834f514db google.golang.org/grpc v1.76.0 google.golang.org/protobuf v1.36.10 gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum index ca5e8e4d..5f0e0b81 100644 --- a/go.sum +++ b/go.sum @@ -42,8 +42,6 @@ go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -golang.org/x/exp v0.0.0-20251017212417-90e834f514db h1:by6IehL4BH5k3e3SJmcoNbOobMey2SLpAF79iPOEBvw= -golang.org/x/exp v0.0.0-20251017212417-90e834f514db/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70= golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= From 456349c89722424a2bf80afcbffd947aead28fc2 Mon Sep 17 00:00:00 2001 From: watal Date: Fri, 26 Sep 2025 21:35:38 +0900 Subject: [PATCH 62/87] update CREDITS --- CREDITS | 63 ++++++++++++++++++++++++++------------------------------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/CREDITS b/CREDITS index a68ab975..8707a45c 100644 --- a/CREDITS +++ b/CREDITS @@ -773,8 +773,8 @@ https://github.com/inconshreveable/mousetrap ================================================================ -github.com/osrg/gobgp/v3 -https://github.com/osrg/gobgp/v3 +github.com/osrg/gobgp/v4 +https://github.com/osrg/gobgp/v4 ---------------------------------------------------------------- Apache License Version 2.0, January 2004 @@ -2573,8 +2573,8 @@ THE SOFTWARE. ================================================================ -golang.org/x/exp -https://golang.org/x/exp +golang.org/x/net +https://golang.org/x/net ---------------------------------------------------------------- Copyright 2009 The Go Authors. @@ -2606,8 +2606,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================================ -golang.org/x/net -https://golang.org/x/net +golang.org/x/sys +https://golang.org/x/sys ---------------------------------------------------------------- Copyright 2009 The Go Authors. @@ -2639,8 +2639,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================================ -golang.org/x/sys -https://golang.org/x/sys +golang.org/x/text +https://golang.org/x/text ---------------------------------------------------------------- Copyright 2009 The Go Authors. @@ -2672,37 +2672,32 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================================ -golang.org/x/text -https://golang.org/x/text +gonum.org/v1/gonum +https://gonum.org/v1/gonum ---------------------------------------------------------------- -Copyright 2009 The Go Authors. +Copyright ©2013 The Gonum Authors. All rights reserved. Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google LLC nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Gonum project nor the names of its authors and + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ================================================================ google.golang.org/genproto/googleapis/rpc From fd14cb022ed8c592d42892f6bd986780acc14c38 Mon Sep 17 00:00:00 2001 From: watal Date: Tue, 30 Sep 2025 14:53:39 +0900 Subject: [PATCH 63/87] refactor(object): move PCC type constants and DeterminePccType function closer to AssociationType constants --- pkg/packet/pcep/object.go | 48 +++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/pkg/packet/pcep/object.go b/pkg/packet/pcep/object.go index f5e5b53b..a252ee7c 100644 --- a/pkg/packet/pcep/object.go +++ b/pkg/packet/pcep/object.go @@ -17,30 +17,6 @@ import ( type PccType int -const ( - CiscoLegacy PccType = iota - JuniperLegacy - RFCCompliant -) - -// Determine PCC type from capability -func DeterminePccType(caps []CapabilityInterface) (pccType PccType) { - pccType = RFCCompliant - for _, cap := range caps { - if t, ok := cap.(*AssocTypeList); ok { - for _, v := range t.AssocTypes { - if v == AssociationTypeSRPolicyAssociationCisco { - pccType = CiscoLegacy - } else if v == AssociationTypeSRPolicyAssociationJuniper { - pccType = JuniperLegacy - break - } - } - } - } - return -} - const commonObjectHeaderLength uint16 = 4 // PCEP Object-Class (1 byte) Ref: https://www.iana.org/assignments/pcep/pcep.xhtml#pcep-objects @@ -1269,6 +1245,30 @@ const ( AssociationTypeSRPolicyAssociationJuniper AssocType = 0xffe1 // Juniper specific TLV (deprecated) ) +const ( + CiscoLegacy PccType = iota + JuniperLegacy + RFCCompliant +) + +// Determine PCC type from capability +func DeterminePccType(caps []CapabilityInterface) (pccType PccType) { + pccType = RFCCompliant + for _, cap := range caps { + if t, ok := cap.(*AssocTypeList); ok { + for _, v := range t.AssocTypes { + if v == AssociationTypeSRPolicyAssociationCisco { + pccType = CiscoLegacy + } else if v == AssociationTypeSRPolicyAssociationJuniper { + pccType = JuniperLegacy + break + } + } + } + } + return +} + type AssociationObject struct { ObjectType ObjectType RFlag bool From 38cdfff8bace7327cd690090abe571200e77fcd5 Mon Sep 17 00:00:00 2001 From: watal Date: Wed, 1 Oct 2025 19:47:54 +0900 Subject: [PATCH 64/87] refactor(session): improve SR Policy handling with error checks and segment validation --- pkg/server/session.go | 121 ++++++++++++++++++++++++++++-------------- 1 file changed, 82 insertions(+), 39 deletions(-) diff --git a/pkg/server/session.go b/pkg/server/session.go index 8dc4d812..f7c3038e 100644 --- a/pkg/server/session.go +++ b/pkg/server/session.go @@ -259,48 +259,73 @@ func (ss *Session) handlePCRpt(length uint16) error { } for _, sr := range message.StateReports { - // synchronization + // Synchronization if sr.LSPObject.SFlag { ss.logger.Debug("Synchronize SR Policy information", zap.Any("Message", message)) - ss.RegisterSRPolicy(*sr) - } else if !sr.LSPObject.SFlag { - switch { - // finish synchronization - case sr.LSPObject.PlspID == 0: - ss.logger.Debug("Finish PCRpt state synchronization") - ss.isSynced = true - // response to request from PCE - case sr.SrpObject.SrpID != 0: - ss.logger.Debug("Finish Stateful PCE request", zap.Uint32("srpID", sr.SrpObject.SrpID)) - if sr.LSPObject.RFlag { - ss.DeleteSRPolicy(*sr) - } else { - ss.RegisterSRPolicy(*sr) - } - // receive SR Policy with PLSP-ID - case sr.LSPObject.PlspID != 0: - ss.logger.Debug("Received SR Policy", zap.Uint32("plspID", sr.LSPObject.PlspID)) - computedSegmentList, err := ss.computePathFromTED(*sr) - if err != nil { - ss.logger.Error("Failed to compute path from TED", zap.Error(err)) + if err := ss.RegisterSRPolicy(*sr); err != nil { + ss.logger.Error("Failed to register SR Policy during synchronization", zap.Error(err), zap.Uint32("plspID", sr.LSPObject.PlspID)) + return err + } + continue + } + + switch { + // Finish synchronization + case sr.LSPObject.PlspID == 0: + ss.logger.Debug("Finish PCRpt state synchronization") + ss.isSynced = true + + // Response to request from PCE + case sr.SrpObject.SrpID != 0: + ss.logger.Debug("Finish Stateful PCE request", zap.Uint32("srpID", sr.SrpObject.SrpID)) + if sr.LSPObject.RFlag { + ss.DeleteSRPolicy(*sr) + } else { + if err := ss.RegisterSRPolicy(*sr); err != nil { + ss.logger.Error("Failed to register SR Policy for Stateful PCE request", zap.Error(err), zap.Uint32("plspID", sr.LSPObject.PlspID)) return err } - sr.EroObject = createEroFromSegmentList(computedSegmentList) + } - ss.RegisterSRPolicy(*sr) + // Receive SR Policy with PLSP-ID + case sr.LSPObject.PlspID != 0: + ss.logger.Debug("Received SR Policy", zap.Uint32("plspID", sr.LSPObject.PlspID)) - if policy, found := ss.SearchSRPolicy(sr.LSPObject.PlspID); found { - ss.SendPCUpdate(*policy) - } - default: - if sr.LSPObject.RFlag { - ss.DeleteSRPolicy(*sr) - } else { - ss.RegisterSRPolicy(*sr) + computedSegmentList, err := ss.computePathFromTED(*sr) + if err != nil { + ss.logger.Error("Failed to compute path from TED", zap.Error(err)) + return err + } + sr.EroObject = createEroFromSegmentList(computedSegmentList) + + if err := ss.RegisterSRPolicy(*sr); err != nil { + ss.logger.Error("Failed to register SR Policy", zap.Error(err), zap.Uint32("plspID", sr.LSPObject.PlspID)) + return err + } + + policy, found := ss.SearchSRPolicy(sr.LSPObject.PlspID) + if !found { + ss.logger.Warn("SR Policy not found after registration", zap.Uint32("plspID", sr.LSPObject.PlspID)) + return fmt.Errorf("SR Policy %d not found after registration", sr.LSPObject.PlspID) + } + + if err := ss.SendPCUpdate(*policy); err != nil { + ss.logger.Error("Failed to send PC update", zap.Uint32("plspID", sr.LSPObject.PlspID), zap.Error(err)) + return err + } + + default: + if sr.LSPObject.RFlag { + ss.DeleteSRPolicy(*sr) + } else { + if err := ss.RegisterSRPolicy(*sr); err != nil { + ss.logger.Error("Failed to register SR Policy (default case)", zap.Error(err), zap.Uint32("plspID", sr.LSPObject.PlspID)) + return err } } } } + return nil } @@ -485,14 +510,14 @@ func (ss *Session) SendPCUpdate(srPolicy table.SRPolicy) error { return err } -func (ss *Session) RegisterSRPolicy(sr pcep.StateReport) { +func (ss *Session) RegisterSRPolicy(sr pcep.StateReport) error { var ( color uint32 // initialized to zero (RFC does not specify a default) preference uint32 // initialized to zero (RFC does not specify a default) ) if ss.pccType == pcep.CiscoLegacy { - // In Cisco legacy mode, get color and preference from Vendor Information Object + // Cisco legacy mode: get color and preference from Vendor Information Object color = sr.VendorInformationObject.Color() preference = sr.VendorInformationObject.Preference() } else { @@ -532,34 +557,50 @@ func (ss *Session) RegisterSRPolicy(sr pcep.StateReport) { state = table.PolicyUnknown } + // Validate Segment List + if sr.EroObject == nil { + return fmt.Errorf("EroObject is nil for PlspID %d", sr.LSPObject.PlspID) + } + segmentList := sr.EroObject.ToSegmentList() + if segmentList == nil { + return fmt.Errorf("SegmentList is nil for PlspID %d", sr.LSPObject.PlspID) + } + if p, ok := ss.SearchSRPolicy(sr.LSPObject.PlspID); ok { - // update - // If the LSP ID is old, it is not the latest data update. + // Update existing policy if LSPID is new or equal if p.LSPID <= lspID { p.Update( table.PolicyDiff{ Name: &sr.LSPObject.Name, Color: &color, Preference: &preference, - SegmentList: sr.EroObject.ToSegmentList(), + SegmentList: segmentList, LSPID: lspID, State: state, }, ) } } else { - // create + // Create a new SR policy var src, dst netip.Addr if src = sr.LSPObject.SrcAddr; !src.IsValid() { src = sr.AssociationObject.AssocSrc } + if !src.IsValid() { + return fmt.Errorf("invalid source address for PlspID %d", sr.LSPObject.PlspID) + } + if dst = sr.LSPObject.DstAddr; !dst.IsValid() { dst = sr.AssociationObject.Endpoint() } + if !dst.IsValid() { + return fmt.Errorf("invalid destination address for PlspID %d", sr.LSPObject.PlspID) + } + p := table.NewSRPolicy( sr.LSPObject.PlspID, sr.LSPObject.Name, - sr.EroObject.ToSegmentList(), + segmentList, src, dst, color, @@ -569,6 +610,8 @@ func (ss *Session) RegisterSRPolicy(sr pcep.StateReport) { ) ss.srPolicies = append(ss.srPolicies, p) } + + return nil } func (ss *Session) DeleteSRPolicy(sr pcep.StateReport) { From c3d360668a86431e43e9456f8f973b9d9d7f8a40 Mon Sep 17 00:00:00 2001 From: watal Date: Tue, 7 Oct 2025 22:08:52 +0900 Subject: [PATCH 65/87] fix(pcep): add boundary check in DecodeFromBytes --- pkg/packet/pcep/object.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg/packet/pcep/object.go b/pkg/packet/pcep/object.go index a252ee7c..e5044b87 100644 --- a/pkg/packet/pcep/object.go +++ b/pkg/packet/pcep/object.go @@ -154,6 +154,11 @@ const ( ) func (h *CommonObjectHeader) DecodeFromBytes(objectHeader []uint8) error { + if len(objectHeader) < int(commonObjectHeaderLength) { + return fmt.Errorf("object header too short: got %d bytes, need at least %d", len(objectHeader), commonObjectHeaderLength) + + } + h.ObjectClass = ObjectClass(objectHeader[0]) h.ObjectType = ObjectType((objectHeader[1] & 0xf0) >> 4) h.ResFlags = uint8((objectHeader[1] & 0x0c) >> 2) From 1c42a90a7a5bfcfad7bd156cfecf0a01f11206a2 Mon Sep 17 00:00:00 2001 From: watal Date: Tue, 7 Oct 2025 22:24:18 +0900 Subject: [PATCH 66/87] fix(ted): prevent out-of-bounds by checking indices in NodeSegment --- internal/pkg/table/ted.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/pkg/table/ted.go b/internal/pkg/table/ted.go index 7b7b1f34..ae6ed63f 100644 --- a/internal/pkg/table/ted.go +++ b/internal/pkg/table/ted.go @@ -157,7 +157,7 @@ func NewLsNode(asn uint32, nodeID string) *LsNode { func (n *LsNode) NodeSegment() (Segment, error) { // for SR-MPLS Segment for _, prefix := range n.Prefixes { - if prefix.SidIndex != 0 { + if prefix.SidIndex > FirstSIDIndex { sid := strconv.Itoa(int(n.SrgbBegin + prefix.SidIndex)) seg, err := NewSegment(sid) if err != nil { @@ -168,7 +168,7 @@ func (n *LsNode) NodeSegment() (Segment, error) { } // for SRv6 Segment for _, srv6SID := range n.SRv6SIDs { - if len(srv6SID.Sids) > 0 { + if len(srv6SID.Sids) > FirstSIDIndex { addr, err := netip.ParseAddr(srv6SID.Sids[FirstSIDIndex]) if err != nil { return nil, err From 5e91e5e0476fb4e46f71502c7e5cdf1aea0ebf90 Mon Sep 17 00:00:00 2001 From: watal Date: Fri, 17 Oct 2025 13:52:44 +0900 Subject: [PATCH 67/87] fix(ted): use exact prefix address match and prioritize RouterID in findRouterIDFromAddress --- pkg/server/session.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pkg/server/session.go b/pkg/server/session.go index f7c3038e..97cb16c8 100644 --- a/pkg/server/session.go +++ b/pkg/server/session.go @@ -390,14 +390,15 @@ func (ss *Session) extractSrcDstRouterIDs(sr pcep.StateReport) (string, string, func (ss *Session) findRouterIDFromAddress(addr netip.Addr) (string, error) { for _, nodes := range ss.ted.Nodes { for routerID, node := range nodes { + if node.RouterID == addr.String() { + return routerID, nil + } + for _, prefix := range node.Prefixes { - if prefix.Prefix.Contains(addr) { + if prefix.Prefix.Addr() == addr { return routerID, nil } } - if node.RouterID == addr.String() { - return routerID, nil - } } } return "", fmt.Errorf("address %s not found in TED", addr) From 3fbd7f0b09eeb9fafdc312b2b589f2e637c21b74 Mon Sep 17 00:00:00 2001 From: Wataru Mishima Date: Fri, 17 Oct 2025 13:56:08 +0900 Subject: [PATCH 68/87] fix(pcep): use constant expected length for StatefulPCECapability DecodeFromBytes validation Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- pkg/packet/pcep/tlv.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/packet/pcep/tlv.go b/pkg/packet/pcep/tlv.go index f1cf8dad..159a6a82 100644 --- a/pkg/packet/pcep/tlv.go +++ b/pkg/packet/pcep/tlv.go @@ -270,8 +270,8 @@ const ( ) func (tlv *StatefulPCECapability) DecodeFromBytes(data []byte) error { - if len(data) < int(tlv.Len()) { - return fmt.Errorf("data is too short: expected at least %d bytes, but got %d bytes for StatefulPCECapability", tlv.Len(), len(data)) + if len(data) < TLVHeaderLength+int(TLVStatefulPCECapabilityValueLength) { + return fmt.Errorf("data is too short: expected at least %d bytes, but got %d bytes for StatefulPCECapability", TLVHeaderLength+TLVStatefulPCECapabilityValueLength, len(data)) } flags := uint32(data[TLVHeaderLength+StatefulPCECapabilityFlagsIndex]) From abffa8e77e013dbaeb38009aa23c398991f84587 Mon Sep 17 00:00:00 2001 From: Wataru Mishima Date: Fri, 17 Oct 2025 14:11:21 +0900 Subject: [PATCH 69/87] fix(table): handle first non-empty SRv6SID in NewSegmentSRv6WithNodeInfo Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- internal/pkg/table/sr_policy.go | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/internal/pkg/table/sr_policy.go b/internal/pkg/table/sr_policy.go index 0fa0db7c..84c7edcc 100644 --- a/internal/pkg/table/sr_policy.go +++ b/internal/pkg/table/sr_policy.go @@ -131,10 +131,7 @@ func BehaviorToString(behavior uint16) string { } } -const ( - FirstSRv6SIDIndex = 0 // Index for first SRv6 SID in array - FirstSIDIndex = 0 // Index for first SID in Sids array -) +const FirstSIDIndex = 0 // Index for first SID in Sids array type SegmentSRv6 struct { Sid netip.Addr @@ -175,35 +172,33 @@ func NewSegmentSRv6WithNodeInfo(sid netip.Addr, n *LsNode) (SegmentSRv6, error) Sid: sid, } - if len(n.SRv6SIDs) == 0 || len(n.SRv6SIDs[FirstSRv6SIDIndex].Sids) == 0 { - return seg, errors.New("no SRv6 SIDs available") - } - - addr, err := netip.ParseAddr(n.SRv6SIDs[FirstSRv6SIDIndex].Sids[FirstSIDIndex]) - if err != nil { - return seg, err - } - seg.LocalAddr = addr - + var found bool for _, srv6SID := range n.SRv6SIDs { if len(srv6SID.Sids) > 0 { + addr, err := netip.ParseAddr(srv6SID.Sids[FirstSIDIndex]) + if err != nil { + return seg, err + } + seg.LocalAddr = addr seg.Structure = []uint8{ srv6SID.SIDStructure.LocalBlock, srv6SID.SIDStructure.LocalNode, srv6SID.SIDStructure.LocalFunc, srv6SID.SIDStructure.LocalArg, } - switch srv6SID.EndpointBehavior.Behavior { case BehaviorUN, BehaviorUA: seg.USid = true default: seg.USid = false } + found = true break } } - + if !found { + return seg, errors.New("no SRv6 SIDs available") + } return seg, nil } From a45d96d4be06e96ed8e9de04d4dc964eaafa2940 Mon Sep 17 00:00:00 2001 From: Wataru Mishima Date: Tue, 21 Oct 2025 17:30:52 +0900 Subject: [PATCH 70/87] fix(grpc): return error on SR Policy deletion failure Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- pkg/server/grpc_server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/server/grpc_server.go b/pkg/server/grpc_server.go index 88b65266..75e57801 100644 --- a/pkg/server/grpc_server.go +++ b/pkg/server/grpc_server.go @@ -198,7 +198,7 @@ func (s *APIServer) DeleteSRPolicy(ctx context.Context, input *pb.DeleteSRPolicy srPolicy.PlspID = id if err := pcepSession.RequestSRPolicyDeleted(srPolicy); err != nil { - return &pb.DeleteSRPolicyResponse{IsSuccess: false}, nil + return &pb.DeleteSRPolicyResponse{IsSuccess: false}, err } } else { // Invalid SR Policy From 3fd09736d6eecf8497cf42820fcd637d3ece63a6 Mon Sep 17 00:00:00 2001 From: Wataru Mishima Date: Tue, 21 Oct 2025 17:33:13 +0900 Subject: [PATCH 71/87] fix(pcep): allow padded TLVs in SymbolicPathName decoding Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- pkg/packet/pcep/tlv.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/packet/pcep/tlv.go b/pkg/packet/pcep/tlv.go index 159a6a82..8c98654d 100644 --- a/pkg/packet/pcep/tlv.go +++ b/pkg/packet/pcep/tlv.go @@ -369,8 +369,8 @@ func (tlv *SymbolicPathName) DecodeFromBytes(data []byte) error { nameLen := binary.BigEndian.Uint16(data[2:4]) totalLength := TLVHeaderLength + int(nameLen) - if len(data) != totalLength { - return fmt.Errorf("data length mismatch: expected %d bytes, but got %d bytes for SymbolicPathName", totalLength, len(data)) + if len(data) < totalLength { + return fmt.Errorf("data is too short: expected at least %d bytes, but got %d bytes for SymbolicPathName", totalLength, len(data)) } tlv.Name = string(data[TLVHeaderLength:totalLength]) From c0a29f98a186cf1af095e3b5b7adad6de7e6092e Mon Sep 17 00:00:00 2001 From: watal Date: Tue, 21 Oct 2025 17:50:46 +0900 Subject: [PATCH 72/87] fix(pcep): mask undefined bits when serializing StatefulPCECapability TLV --- pkg/packet/pcep/tlv.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pkg/packet/pcep/tlv.go b/pkg/packet/pcep/tlv.go index 8c98654d..52d86a5f 100644 --- a/pkg/packet/pcep/tlv.go +++ b/pkg/packet/pcep/tlv.go @@ -265,6 +265,13 @@ const ( TriggeredInitialSyncBit uint32 = 0x20 ) +const definedStatefulPCEFlagsMask uint32 = LSPUpdateCapabilityBit | + IncludeDBVersionCapabilityBit | + LSPInstantiationCapabilityBit | + TriggeredResyncCapabilityBit | + DeltaLSPSyncCapabilityBit | + TriggeredInitialSyncBit + const ( StatefulPCECapabilityFlagsIndex = 3 ) @@ -281,10 +288,11 @@ func (tlv *StatefulPCECapability) DecodeFromBytes(data []byte) error { } func (tlv *StatefulPCECapability) Serialize() []byte { + flags := tlv.SetFlags() & definedStatefulPCEFlagsMask return AppendByteSlices( Uint16ToByteSlice(tlv.Type()), Uint16ToByteSlice(TLVStatefulPCECapabilityValueLength), - Uint32ToByteSlice(tlv.SetFlags()), + Uint32ToByteSlice(flags), ) } From 6b7133a132260796d23669cbc22f1e84f6de0096 Mon Sep 17 00:00:00 2001 From: watal Date: Tue, 21 Oct 2025 17:58:19 +0900 Subject: [PATCH 73/87] fix(session): check for empty SegmentList instead of nil in RegisterSRPolicy --- pkg/server/session.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/server/session.go b/pkg/server/session.go index 97cb16c8..ddc80a91 100644 --- a/pkg/server/session.go +++ b/pkg/server/session.go @@ -563,8 +563,8 @@ func (ss *Session) RegisterSRPolicy(sr pcep.StateReport) error { return fmt.Errorf("EroObject is nil for PlspID %d", sr.LSPObject.PlspID) } segmentList := sr.EroObject.ToSegmentList() - if segmentList == nil { - return fmt.Errorf("SegmentList is nil for PlspID %d", sr.LSPObject.PlspID) + if len(segmentList) == 0 { + return fmt.Errorf("SegmentList is empty for PlspID %d", sr.LSPObject.PlspID) } if p, ok := ss.SearchSRPolicy(sr.LSPObject.PlspID); ok { From 143c752ff2ed5a7bfff2221b1621b458c5649491 Mon Sep 17 00:00:00 2001 From: watal Date: Tue, 21 Oct 2025 18:48:33 +0900 Subject: [PATCH 74/87] fix(pcep): update Serialize() to return error and handle SRv6 Behavior error --- internal/pkg/table/sr_policy.go | 12 +++---- pkg/packet/pcep/object.go | 57 +++++++++++++++++++-------------- 2 files changed, 39 insertions(+), 30 deletions(-) diff --git a/internal/pkg/table/sr_policy.go b/internal/pkg/table/sr_policy.go index 84c7edcc..c183abc5 100644 --- a/internal/pkg/table/sr_policy.go +++ b/internal/pkg/table/sr_policy.go @@ -145,20 +145,20 @@ func (seg SegmentSRv6) SidString() string { return seg.Sid.String() } -func (seg SegmentSRv6) Behavior() uint16 { +func (seg SegmentSRv6) Behavior() (uint16, error) { if !seg.LocalAddr.IsValid() { - return BehaviorReserved + return 0, errors.New("SegmentSRv6: LocalAddr is invalid") } if seg.USid { if seg.RemoteAddr.IsValid() { - return BehaviorUA + return BehaviorUA, nil } - return BehaviorUN + return BehaviorUN, nil } if seg.RemoteAddr.IsValid() { - return BehaviorENDX + return BehaviorENDX, nil } - return BehaviorEND + return BehaviorEND, nil } func NewSegmentSRv6(sid netip.Addr) SegmentSRv6 { diff --git a/pkg/packet/pcep/object.go b/pkg/packet/pcep/object.go index e5044b87..98486a93 100644 --- a/pkg/packet/pcep/object.go +++ b/pkg/packet/pcep/object.go @@ -778,7 +778,10 @@ func (o EroObject) Serialize() ([]uint8, error) { byteEroObject := byteEroObjectHeader for _, eroSubobject := range o.EroSubobjects { - buf := eroSubobject.Serialize() + buf, err := eroSubobject.Serialize() + if err != nil { + return nil, fmt.Errorf("failed to serialize subobject: %w", err) + } byteEroObject = append(byteEroObject, buf...) } return byteEroObject, nil @@ -834,7 +837,7 @@ func (o *EroObject) ToSegmentList() []table.Segment { type EroSubobject interface { DecodeFromBytes([]uint8) error Len() (uint16, error) - Serialize() []uint8 + Serialize() ([]uint8, error) ToSegment() table.Segment } @@ -930,31 +933,33 @@ func (o *SREroSubobject) DecodeFromBytes(subObject []uint8) error { return nil } -func (o *SREroSubobject) Serialize() []uint8 { +func (o *SREroSubobject) Serialize() ([]uint8, error) { buf := make([]uint8, 4) buf[0] = uint8(o.SubobjectType) if o.LFlag { - buf[0] = buf[0] | 0x80 + buf[0] |= 0x80 } buf[1] = o.Length buf[2] = uint8(o.NAIType) * 16 if o.FFlag { - buf[3] = buf[3] | 0x08 + buf[3] |= 0x08 } if o.SFlag { - buf[3] = buf[3] | 0x04 + buf[3] |= 0x04 } if o.CFlag { - buf[3] = buf[3] | 0x02 + buf[3] |= 0x02 } if o.MFlag { - buf[3] = buf[3] | 0x01 + buf[3] |= 0x01 } + byteSid := make([]uint8, 4) binary.BigEndian.PutUint32(byteSid, o.Segment.Sid<<12) byteSREroSubobject := AppendByteSlices(buf, byteSid) - return byteSREroSubobject + + return byteSREroSubobject, nil } func (o *SREroSubobject) Len() (uint16, error) { @@ -1068,36 +1073,40 @@ func (o *SRv6EroSubobject) DecodeFromBytes(subObject []uint8) error { return nil } -func (o *SRv6EroSubobject) Serialize() []uint8 { +func (o *SRv6EroSubobject) Serialize() ([]uint8, error) { buf := make([]uint8, 4) buf[0] = uint8(o.SubobjectType) if o.LFlag { - buf[0] = buf[0] | 0x80 + buf[0] |= 0x80 } buf[1] = o.Length buf[2] = uint8(o.NAIType) * 16 if o.VFlag { - buf[3] = buf[3] | 0x08 + buf[3] |= 0x08 } if o.TFlag { - buf[3] = buf[3] | 0x04 + buf[3] |= 0x04 } if o.FFlag { - buf[3] = buf[3] | 0x02 + buf[3] |= 0x02 } if o.SFlag { - buf[3] = buf[3] | 0x01 + buf[3] |= 0x01 } + reserved := make([]uint8, 2) - behavior := Uint16ToByteSlice(o.Segment.Behavior()) + + behavior, err := o.Segment.Behavior() + if err != nil { + return nil, err + } + behaviorBytes := Uint16ToByteSlice(behavior) + byteSid := o.Segment.Sid.AsSlice() - byteNAI := []uint8{} - if o.Segment.LocalAddr.IsValid() { - byteNAI = append(byteNAI, o.Segment.LocalAddr.AsSlice()...) - if o.Segment.RemoteAddr.IsValid() { - byteNAI = append(byteNAI, o.Segment.RemoteAddr.AsSlice()...) - } + byteNAI := o.Segment.LocalAddr.AsSlice() + if o.Segment.RemoteAddr.IsValid() { + byteNAI = append(byteNAI, o.Segment.RemoteAddr.AsSlice()...) } byteSidStructure := []uint8{} @@ -1106,8 +1115,8 @@ func (o *SRv6EroSubobject) Serialize() []uint8 { byteSidStructure = append(byteSidStructure, make([]uint8, 4)...) } - byteSRv6EroSubobject := AppendByteSlices(buf, reserved, behavior, byteSid, byteNAI, byteSidStructure) - return byteSRv6EroSubobject + byteSRv6EroSubobject := AppendByteSlices(buf, reserved, behaviorBytes, byteSid, byteNAI, byteSidStructure) + return byteSRv6EroSubobject, nil } func (o *SRv6EroSubobject) Len() (uint16, error) { From 5f6f962ae914c490a737b07f98addd8a080ae5e8 Mon Sep 17 00:00:00 2001 From: Wataru Mishima Date: Tue, 21 Oct 2025 18:51:50 +0900 Subject: [PATCH 75/87] fix(grpc_server): correct spelling of "parameter" in DeleteSRPolicy logs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- pkg/server/grpc_server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/server/grpc_server.go b/pkg/server/grpc_server.go index 75e57801..5dd3febb 100644 --- a/pkg/server/grpc_server.go +++ b/pkg/server/grpc_server.go @@ -177,7 +177,7 @@ func (s *APIServer) DeleteSRPolicy(ctx context.Context, input *pb.DeleteSRPolicy return nil, err } s.logger.Info("Received DeleteSRPolicy API request") - s.logger.Debug("Received paramater", zap.String("input", string(inputJSON))) + s.logger.Debug("Received parameter", zap.String("input", string(inputJSON))) pcepSession, err := getSyncedPCEPSession(s.pce, inputSRPolicy.GetPcepSessionAddr()) if err != nil { From 868393fedcd9c149083d2fb5492aa5c5a4a3510f Mon Sep 17 00:00:00 2001 From: watal Date: Tue, 21 Oct 2025 19:01:12 +0900 Subject: [PATCH 76/87] refactor(gobgp): split ConvertToTEDElem per NLRI type to reduce cyclomatic complexity --- internal/pkg/gobgp/interface.go | 174 +++++++++++++++++++------------- 1 file changed, 102 insertions(+), 72 deletions(-) diff --git a/internal/pkg/gobgp/interface.go b/internal/pkg/gobgp/interface.go index 9977da56..f9754972 100644 --- a/internal/pkg/gobgp/interface.go +++ b/internal/pkg/gobgp/interface.go @@ -79,6 +79,7 @@ func GetBGPlsNLRIs(serverAddr string, serverPort string) ([]table.TEDElem, error return tedElems, nil } +// ConvertToTEDElem converts a single api.Destination to TEDElem(s) with low cyclomatic complexity. func ConvertToTEDElem(dst *api.Destination) ([]table.TEDElem, error) { if len(dst.GetPaths()) != 1 { return nil, errors.New("invalid path length: expected 1 path") @@ -89,92 +90,121 @@ func ConvertToTEDElem(dst *api.Destination) ([]table.TEDElem, error) { if nlri == nil { return nil, errors.New("NLRI is nil") } + lsAddrPrefix := nlri.GetLsAddrPrefix() if lsAddrPrefix == nil { return nil, errors.New("LSAddrPrefix is nil") } - // Get BGP-LS Attribute - var lsAttr *api.Attribute_Ls - var ok bool - for _, pathAttr := range path.GetPattrs() { - if lsAttr, ok = pathAttr.Attr.(*api.Attribute_Ls); ok { - // Found BGP-LS Attribute - break - } - } + + lsAttr := findLsAttribute(path) if lsAttr == nil { + // BGP-LS Attribute not found, return empty return nil, nil } - switch lsAddrPrefix.GetType() { - case api.LsNLRIType_LS_NLRI_TYPE_NODE: - lsAttrNode := lsAttr.Ls.GetNode() - if lsAttrNode == nil { - return nil, fmt.Errorf("LS Node Attribute is nil") - } - lsNode, err := getLsNode(lsAddrPrefix, lsAttrNode) - if err != nil { - return nil, fmt.Errorf("failed to process LS Node NLRI: %w", err) + return convertByNlriType(lsAddrPrefix, lsAttr, path) +} + +// findLsAttribute extracts the BGP-LS attribute from a path. +func findLsAttribute(path *api.Path) *api.Attribute_Ls { + for _, pathAttr := range path.GetPattrs() { + if lsAttr, ok := pathAttr.Attr.(*api.Attribute_Ls); ok { + return lsAttr } - return []table.TEDElem{lsNode}, nil + } + return nil +} + +// convertByNlriType dispatches NLRI processing based on its type. +func convertByNlriType(nlri *api.LsAddrPrefix, lsAttr *api.Attribute_Ls, path *api.Path) ([]table.TEDElem, error) { + switch nlri.GetType() { + case api.LsNLRIType_LS_NLRI_TYPE_NODE: + return convertNode(nlri, lsAttr) case api.LsNLRIType_LS_NLRI_TYPE_LINK: - lsAttrLink := lsAttr.Ls.GetLink() - if lsAttrLink == nil { - return nil, fmt.Errorf("LS Link Attribute is nil") - } - lsLink, err := getLsLink(lsAddrPrefix, lsAttrLink) - if err != nil { - return nil, fmt.Errorf("failed to process LS Link NLRI: %w", err) - } - return []table.TEDElem{lsLink}, nil + return convertLink(nlri, lsAttr) case api.LsNLRIType_LS_NLRI_TYPE_PREFIX_V4, api.LsNLRIType_LS_NLRI_TYPE_PREFIX_V6: - lsAttrPrefix := lsAttr.Ls.GetPrefix() - if lsAttrPrefix == nil { - return nil, fmt.Errorf("LS Prefix Attribute is nil") - } - // Link-State Prefix NLRI may contain one or more entries within the MP-REACH NLRI. - // Since path.GetNLRI() only includes one of them, you need to retrieve all of them from path.GetPattrs(). - var mpReachAttr *api.MpReachNLRIAttribute - for _, pathAttr := range path.GetPattrs() { - mpReachAttr = pathAttr.GetMpReach() - if mpReachAttr != nil { - // Found MP-REACH NLRI Attribute - break - } - } - if mpReachAttr == nil { - return nil, errors.New("MP-REACH NLRI Attribute is nil") - } - lsPrefixList, err := getLsPrefixList(mpReachAttr.GetNlris(), lsAttrPrefix) - if err != nil { - return nil, fmt.Errorf("failed to process LS Prefix V4 NLRI: %w", err) - } - return lsPrefixList, nil + return convertPrefix(nlri, lsAttr, path) case api.LsNLRIType_LS_NLRI_TYPE_SRV6_SID: - lsAttrSrv6SID := lsAttr.Ls.GetSrv6Sid() - if lsAttrSrv6SID == nil { - return nil, fmt.Errorf("LS SRv6 SID Attribute is nil") - } - // Link-State Prefix NLRI may contain one or more entries within the MP-REACH NLRI. - // Since path.GetNLRI() only includes one of them, you need to retrieve the others from path.GetPattrs() instead of using path.GetNLRI(). - var mpReachAttr *api.MpReachNLRIAttribute - for _, pathAttr := range path.GetPattrs() { - mpReachAttr = pathAttr.GetMpReach() - if mpReachAttr != nil { - break - } - } - if mpReachAttr == nil { - return nil, errors.New("MP-REACH NLRI Attribute is nil") - } - lsSrv6SIDList, err := getLsSrv6SIDList(mpReachAttr.GetNlris(), lsAttrSrv6SID) - if err != nil { - return nil, fmt.Errorf("failed to process LS SRv6 SID NLRI: %w", err) - } - return lsSrv6SIDList, nil + return convertSrv6SID(nlri, lsAttr, path) default: - return nil, fmt.Errorf("invalid LS Link State NLRI type: %s", lsAddrPrefix.GetType().String()) + return nil, fmt.Errorf("invalid LS NLRI type: %s", nlri.GetType().String()) + } +} + +// convertNode handles LS Node NLRI. +func convertNode(nlri *api.LsAddrPrefix, lsAttr *api.Attribute_Ls) ([]table.TEDElem, error) { + nodeAttr := lsAttr.Ls.GetNode() + if nodeAttr == nil { + return nil, fmt.Errorf("LS Node Attribute is nil") + } + lsNode, err := getLsNode(nlri, nodeAttr) + if err != nil { + return nil, fmt.Errorf("failed to process LS Node NLRI: %w", err) + } + return []table.TEDElem{lsNode}, nil +} + +// convertLink handles LS Link NLRI. +func convertLink(nlri *api.LsAddrPrefix, lsAttr *api.Attribute_Ls) ([]table.TEDElem, error) { + linkAttr := lsAttr.Ls.GetLink() + if linkAttr == nil { + return nil, fmt.Errorf("LS Link Attribute is nil") + } + lsLink, err := getLsLink(nlri, linkAttr) + if err != nil { + return nil, fmt.Errorf("failed to process LS Link NLRI: %w", err) + } + return []table.TEDElem{lsLink}, nil +} + +// convertPrefix handles LS Prefix V4/V6 NLRI. +func convertPrefix(nlri *api.LsAddrPrefix, lsAttr *api.Attribute_Ls, path *api.Path) ([]table.TEDElem, error) { + prefixAttr := lsAttr.Ls.GetPrefix() + if prefixAttr == nil { + return nil, fmt.Errorf("LS Prefix Attribute is nil") + } + + mpReach := findMpReach(path) + if mpReach == nil { + return nil, errors.New("MP-REACH NLRI Attribute is nil") + } + + lsPrefixList, err := getLsPrefixList(mpReach.GetNlris(), prefixAttr) + if err != nil { + return nil, fmt.Errorf("failed to process LS Prefix NLRI: %w", err) + } + + return lsPrefixList, nil +} + +// convertSrv6SID handles LS SRv6 SID NLRI. +func convertSrv6SID(nlri *api.LsAddrPrefix, lsAttr *api.Attribute_Ls, path *api.Path) ([]table.TEDElem, error) { + srv6Attr := lsAttr.Ls.GetSrv6Sid() + if srv6Attr == nil { + return nil, fmt.Errorf("LS SRv6 SID Attribute is nil") + } + + mpReach := findMpReach(path) + if mpReach == nil { + return nil, errors.New("MP-REACH NLRI Attribute is nil") + } + + lsSrv6List, err := getLsSrv6SIDList(mpReach.GetNlris(), srv6Attr) + if err != nil { + return nil, fmt.Errorf("failed to process LS SRv6 SID NLRI: %w", err) + } + + return lsSrv6List, nil +} + +// findMpReach searches for MP-REACH NLRI attribute in path. +func findMpReach(path *api.Path) *api.MpReachNLRIAttribute { + for _, attr := range path.GetPattrs() { + if mp := attr.GetMpReach(); mp != nil { + return mp + } } + return nil } // formatIsisAreaID formats the ISIS Area ID into a human-readable string. From ff96bf348ca941c29ae8883a243ec2cb6a510558 Mon Sep 17 00:00:00 2001 From: watal Date: Tue, 21 Oct 2025 19:04:24 +0900 Subject: [PATCH 77/87] refactor(ted): split Print into helper functions to reduce cyclomatic complexity --- internal/pkg/table/ted.go | 209 ++++++++++++++++++++++---------------- 1 file changed, 119 insertions(+), 90 deletions(-) diff --git a/internal/pkg/table/ted.go b/internal/pkg/table/ted.go index ae6ed63f..8e68fc0c 100644 --- a/internal/pkg/table/ted.go +++ b/internal/pkg/table/ted.go @@ -23,6 +23,7 @@ func (ted *LsTED) Update(tedElems []TEDElem) { } } +// Print outputs the TED in a structured way with low cyclomatic complexity. func (ted *LsTED) Print() { if ted == nil || ted.Nodes == nil { fmt.Println("TED is empty") @@ -33,101 +34,129 @@ func (ted *LsTED) Print() { if nodes == nil { continue } - nodeCnt := 1 - for nodeID, node := range nodes { - if node == nil { - continue - } - fmt.Printf("Node: %d\n", nodeCnt) - fmt.Printf(" %s\n", nodeID) - fmt.Printf(" Hostname: %s\n", node.Hostname) - fmt.Printf(" ISIS Area ID: %s\n", node.IsisAreaID) - fmt.Printf(" SRGB: %d - %d\n", node.SrgbBegin, node.SrgbEnd) - - fmt.Printf(" Prefixes:\n") - if node.Prefixes != nil { - for _, prefix := range node.Prefixes { - if prefix == nil { - continue - } - fmt.Printf(" %s\n", prefix.Prefix.String()) - if prefix.SidIndex != 0 { - fmt.Printf(" index: %d\n", prefix.SidIndex) - } - } - } + printNodes(nodes) + } +} - fmt.Printf(" Links:\n") - if node.Links != nil { - for _, link := range node.Links { - if link == nil { - continue - } - - localIP := "None" - remoteIP := "None" - if link.LocalIP.IsValid() { - localIP = link.LocalIP.String() - } - if link.RemoteIP.IsValid() { - remoteIP = link.RemoteIP.String() - } - fmt.Printf(" Local: %s Remote: %s\n", localIP, remoteIP) - - remoteNodeID := "None" - if link.RemoteNode != nil { - remoteNodeID = link.RemoteNode.RouterID - } - fmt.Printf(" RemoteNode: %s\n", remoteNodeID) - - fmt.Printf(" Metrics:\n") - if link.Metrics != nil { - for _, metric := range link.Metrics { - if metric == nil { - continue - } - fmt.Printf(" %s: %d\n", metric.Type.String(), metric.Value) - } - } - - fmt.Printf(" Adj-SID: %d\n", link.AdjSid) - - if link.Srv6EndXSID != nil { - fmt.Printf(" SRv6 End.X SID:\n") - fmt.Printf(" EndpointBehavior: %s\n", BehaviorToString(link.Srv6EndXSID.EndpointBehavior)) - fmt.Printf(" SIDs: %v\n", link.Srv6EndXSID.Sids) - fmt.Printf(" SID Structure: Block: %d, Node: %d, Func: %d, Arg: %d\n", - link.Srv6EndXSID.Srv6SIDStructure.LocalBlock, - link.Srv6EndXSID.Srv6SIDStructure.LocalNode, - link.Srv6EndXSID.Srv6SIDStructure.LocalFunc, - link.Srv6EndXSID.Srv6SIDStructure.LocalArg) - } - } - } +// printNodes iterates over each node in the map and prints its details. +func printNodes(nodes map[string]*LsNode) { + nodeCnt := 1 + for nodeID, node := range nodes { + if node == nil { + continue + } + fmt.Printf("Node: %d\n", nodeCnt) + printNodeBasic(nodeID, node) + printNodePrefixes(node) + printNodeLinks(node) + printNodeSRv6SIDs(node) + fmt.Println() + nodeCnt++ + } +} - fmt.Printf(" SRv6 SIDs:\n") - if node.SRv6SIDs != nil { - for _, srv6SID := range node.SRv6SIDs { - if srv6SID == nil { - continue - } - fmt.Printf(" SIDs: %v\n", srv6SID.Sids) - fmt.Printf(" Block: %d, Node: %d, Func: %d, Arg: %d\n", - srv6SID.SIDStructure.LocalBlock, - srv6SID.SIDStructure.LocalNode, - srv6SID.SIDStructure.LocalFunc, - srv6SID.SIDStructure.LocalArg) - fmt.Printf(" EndpointBehavior: %s, Flags: %d, Algorithm: %d\n", - BehaviorToString(srv6SID.EndpointBehavior.Behavior), - srv6SID.EndpointBehavior.Flags, - srv6SID.EndpointBehavior.Algorithm) - fmt.Printf(" MultiTopoIDs: %v\n", srv6SID.MultiTopoIDs) - } +// printNodeBasic prints the basic information of a node. +func printNodeBasic(nodeID string, node *LsNode) { + fmt.Printf(" %s\n", nodeID) + fmt.Printf(" Hostname: %s\n", node.Hostname) + fmt.Printf(" ISIS Area ID: %s\n", node.IsisAreaID) + fmt.Printf(" SRGB: %d - %d\n", node.SrgbBegin, node.SrgbEnd) +} + +// printNodePrefixes prints the prefixes associated with a node. +func printNodePrefixes(node *LsNode) { + fmt.Println(" Prefixes:") + if node.Prefixes == nil { + return + } + for _, prefix := range node.Prefixes { + if prefix == nil { + continue + } + fmt.Printf(" %s\n", prefix.Prefix.String()) + if prefix.SidIndex != 0 { + fmt.Printf(" index: %d\n", prefix.SidIndex) + } + } +} + +// printNodeLinks prints the links associated with a node. +func printNodeLinks(node *LsNode) { + fmt.Println(" Links:") + if node.Links == nil { + return + } + for _, link := range node.Links { + if link == nil { + continue + } + printLink(link) + } +} + +// printLink prints the details of a single link. +func printLink(link *LsLink) { + localIP := "None" + remoteIP := "None" + if link.LocalIP.IsValid() { + localIP = link.LocalIP.String() + } + if link.RemoteIP.IsValid() { + remoteIP = link.RemoteIP.String() + } + fmt.Printf(" Local: %s Remote: %s\n", localIP, remoteIP) + + remoteNodeID := "None" + if link.RemoteNode != nil { + remoteNodeID = link.RemoteNode.RouterID + } + fmt.Printf(" RemoteNode: %s\n", remoteNodeID) + + fmt.Println(" Metrics:") + if link.Metrics != nil { + for _, metric := range link.Metrics { + if metric == nil { + continue } + fmt.Printf(" %s: %d\n", metric.Type.String(), metric.Value) + } + } + + fmt.Printf(" Adj-SID: %d\n", link.AdjSid) + + if link.Srv6EndXSID != nil { + fmt.Println(" SRv6 End.X SID:") + fmt.Printf(" EndpointBehavior: %s\n", BehaviorToString(link.Srv6EndXSID.EndpointBehavior)) + fmt.Printf(" SIDs: %v\n", link.Srv6EndXSID.Sids) + fmt.Printf(" SID Structure: Block: %d, Node: %d, Func: %d, Arg: %d\n", + link.Srv6EndXSID.Srv6SIDStructure.LocalBlock, + link.Srv6EndXSID.Srv6SIDStructure.LocalNode, + link.Srv6EndXSID.Srv6SIDStructure.LocalFunc, + link.Srv6EndXSID.Srv6SIDStructure.LocalArg) + } +} - nodeCnt++ - fmt.Printf("\n") +// printNodeSRv6SIDs prints the SRv6 SIDs associated with a node. +func printNodeSRv6SIDs(node *LsNode) { + fmt.Println(" SRv6 SIDs:") + if node.SRv6SIDs == nil { + return + } + for _, srv6SID := range node.SRv6SIDs { + if srv6SID == nil { + continue } + fmt.Printf(" SIDs: %v\n", srv6SID.Sids) + fmt.Printf(" Block: %d, Node: %d, Func: %d, Arg: %d\n", + srv6SID.SIDStructure.LocalBlock, + srv6SID.SIDStructure.LocalNode, + srv6SID.SIDStructure.LocalFunc, + srv6SID.SIDStructure.LocalArg) + fmt.Printf(" EndpointBehavior: %s, Flags: %d, Algorithm: %d\n", + BehaviorToString(srv6SID.EndpointBehavior.Behavior), + srv6SID.EndpointBehavior.Flags, + srv6SID.EndpointBehavior.Algorithm) + fmt.Printf(" MultiTopoIDs: %v\n", srv6SID.MultiTopoIDs) } } From c3bd4f0903bc20e26763c1710f3be2dbabecebae Mon Sep 17 00:00:00 2001 From: watal Date: Tue, 21 Oct 2025 19:09:20 +0900 Subject: [PATCH 78/87] refactor(grpc_server): split GetTED into helper functions to reduce cyclomatic complexity --- pkg/server/grpc_server.go | 289 +++++++++++++++++++++----------------- 1 file changed, 159 insertions(+), 130 deletions(-) diff --git a/pkg/server/grpc_server.go b/pkg/server/grpc_server.go index 5dd3febb..be351a8c 100644 --- a/pkg/server/grpc_server.go +++ b/pkg/server/grpc_server.go @@ -406,154 +406,183 @@ func (s *APIServer) GetSRPolicyList(ctx context.Context, _ *pb.GetSRPolicyListRe }, nil } +// GetTED returns the TED information in a structured way. func (s *APIServer) GetTED(ctx context.Context, req *pb.GetTEDRequest) (*pb.GetTEDResponse, error) { s.logger.Info("Received GetTED API request") - ret := &pb.GetTEDResponse{ - Enable: true, - } - + ret := &pb.GetTEDResponse{Enable: true} if s.pce == nil || s.pce.ted == nil { ret.Enable = false return ret, nil } - ret.LsNodes = make([]*pb.LsNode, 0, len(s.pce.ted.Nodes)) - - for _, lsNodes := range s.pce.ted.Nodes { - for _, lsNode := range lsNodes { - if lsNode == nil { - continue + for _, nodes := range s.pce.ted.Nodes { + for _, node := range nodes { + if n := convertLsNode(node, s.logger); n != nil { + ret.LsNodes = append(ret.LsNodes, n) } + } + } - node := &pb.LsNode{ - Asn: lsNode.ASN, - RouterId: lsNode.RouterID, - IsisAreaId: lsNode.IsisAreaID, - Hostname: lsNode.Hostname, - SrgbBegin: lsNode.SrgbBegin, - SrgbEnd: lsNode.SrgbEnd, - LsLinks: make([]*pb.LsLink, 0, len(lsNode.Links)), - LsPrefixes: make([]*pb.LsPrefix, 0, len(lsNode.Prefixes)), - LsSrv6Sids: make([]*pb.LsSrv6SID, 0, len(lsNode.SRv6SIDs)), - } + s.logger.Debug("Send GetTED API reply") + return ret, nil +} - for _, lsLink := range lsNode.Links { - if lsLink == nil || lsLink.LocalNode == nil || lsLink.RemoteNode == nil { - s.logger.Debug("skip link with nil node", zap.Any("link", lsLink)) - continue - } - - localIP, _ := lsLink.LocalIP.MarshalText() - remoteIP, _ := lsLink.RemoteIP.MarshalText() - - link := &pb.LsLink{ - LocalRouterId: lsLink.LocalNode.RouterID, - LocalAsn: lsLink.LocalNode.ASN, - LocalIp: string(localIP), - RemoteRouterId: lsLink.RemoteNode.RouterID, - RemoteAsn: lsLink.RemoteNode.ASN, - RemoteIp: string(remoteIP), - Metrics: make([]*pb.Metric, 0, len(lsLink.Metrics)), - AdjSid: lsLink.AdjSid, - } - - for _, lsMetric := range lsLink.Metrics { - if lsMetric == nil { - continue - } - metricType, ok := pb.MetricType_value[lsMetric.Type.String()] - if !ok { - s.logger.Debug("invalid metric type", zap.String("type", lsMetric.Type.String())) - continue - } - link.Metrics = append(link.Metrics, &pb.Metric{ - Type: pb.MetricType(metricType), - Value: lsMetric.Value, - }) - } - - if lsLink.Srv6EndXSID != nil { - srv6 := &pb.Srv6EndXSID{ - EndpointBehavior: uint32(lsLink.Srv6EndXSID.EndpointBehavior), - Sids: make([]*pb.SID, 0, len(lsLink.Srv6EndXSID.Sids)), - SidStructure: &pb.SidStructure{ - LocalBlock: uint32(lsLink.Srv6EndXSID.Srv6SIDStructure.LocalBlock), - LocalNode: uint32(lsLink.Srv6EndXSID.Srv6SIDStructure.LocalNode), - LocalFunc: uint32(lsLink.Srv6EndXSID.Srv6SIDStructure.LocalFunc), - LocalArg: uint32(lsLink.Srv6EndXSID.Srv6SIDStructure.LocalArg), - }, - } - - for _, sid := range lsLink.Srv6EndXSID.Sids { - if sid == "" { - continue - } - srv6.Sids = append(srv6.Sids, &pb.SID{Sid: sid}) - } - - link.Srv6EndXSid = srv6 - } - - node.LsLinks = append(node.LsLinks, link) - } +// convertLsNode converts a table.LsNode to a protobuf LsNode. +func convertLsNode(lsNode *table.LsNode, logger *zap.Logger) *pb.LsNode { + if lsNode == nil { + return nil + } - for _, lsPrefix := range lsNode.Prefixes { - if lsPrefix == nil { - continue - } - node.LsPrefixes = append(node.LsPrefixes, &pb.LsPrefix{ - Prefix: lsPrefix.Prefix.String(), - SidIndex: lsPrefix.SidIndex, - }) - } + return &pb.LsNode{ + Asn: lsNode.ASN, + RouterId: lsNode.RouterID, + IsisAreaId: lsNode.IsisAreaID, + Hostname: lsNode.Hostname, + SrgbBegin: lsNode.SrgbBegin, + SrgbEnd: lsNode.SrgbEnd, + LsLinks: convertLsLinks(lsNode.Links, logger), + LsPrefixes: convertLsPrefixes(lsNode.Prefixes), + LsSrv6Sids: convertLsSrv6SIDs(lsNode.SRv6SIDs), + } +} + +// convertLsLinks converts a slice of table.LsLink to protobuf LsLink. +func convertLsLinks(links []*table.LsLink, logger *zap.Logger) []*pb.LsLink { + if links == nil { + return nil + } + result := make([]*pb.LsLink, 0, len(links)) + for _, link := range links { + if link == nil || link.LocalNode == nil || link.RemoteNode == nil { + logger.Debug("skip link with nil node", zap.Any("link", link)) + continue + } + result = append(result, buildLsLink(link)) + } + return result +} - for _, lsSrv6SID := range lsNode.SRv6SIDs { - if lsSrv6SID == nil { - continue - } - srv6SID := &pb.LsSrv6SID{ - Sids: make([]*pb.SID, 0, len(lsSrv6SID.Sids)), - MultiTopoIds: make([]*pb.MultiTopoID, 0, len(lsSrv6SID.MultiTopoIDs)), - } - - for _, sid := range lsSrv6SID.Sids { - if sid == "" { - continue - } - srv6SID.Sids = append(srv6SID.Sids, &pb.SID{Sid: sid}) - } - - for _, topoID := range lsSrv6SID.MultiTopoIDs { - srv6SID.MultiTopoIds = append(srv6SID.MultiTopoIds, &pb.MultiTopoID{ - MultiTopoId: topoID, - }) - } - - if lsSrv6SID.EndpointBehavior != (table.EndpointBehavior{}) { - srv6SID.EndpointBehavior = &pb.EndpointBehavior{ - Behavior: uint32(lsSrv6SID.EndpointBehavior.Behavior), - Flags: uint32(lsSrv6SID.EndpointBehavior.Flags), - Algorithm: uint32(lsSrv6SID.EndpointBehavior.Algorithm), - } - } - - srv6SID.SidStructure = &pb.SidStructure{ - LocalBlock: uint32(lsSrv6SID.SIDStructure.LocalBlock), - LocalNode: uint32(lsSrv6SID.SIDStructure.LocalNode), - LocalFunc: uint32(lsSrv6SID.SIDStructure.LocalFunc), - LocalArg: uint32(lsSrv6SID.SIDStructure.LocalArg), - } - - node.LsSrv6Sids = append(node.LsSrv6Sids, srv6SID) +// buildLsLink converts a single table.LsLink to protobuf LsLink. +func buildLsLink(link *table.LsLink) *pb.LsLink { + localIP, _ := link.LocalIP.MarshalText() + remoteIP, _ := link.RemoteIP.MarshalText() + + pbLink := &pb.LsLink{ + LocalRouterId: link.LocalNode.RouterID, + LocalAsn: link.LocalNode.ASN, + LocalIp: string(localIP), + RemoteRouterId: link.RemoteNode.RouterID, + RemoteAsn: link.RemoteNode.ASN, + RemoteIp: string(remoteIP), + Metrics: convertMetrics(link.Metrics), + AdjSid: link.AdjSid, + } + + if link.Srv6EndXSID != nil { + pbLink.Srv6EndXSid = convertSrv6EndXSID(link.Srv6EndXSID) + } + return pbLink +} + +// convertMetrics converts a slice of table.Metric to protobuf Metric. +func convertMetrics(metrics []*table.Metric) []*pb.Metric { + if metrics == nil { + return nil + } + result := make([]*pb.Metric, 0, len(metrics)) + for _, m := range metrics { + if m != nil { + if mt, ok := pb.MetricType_value[m.Type.String()]; ok { + result = append(result, &pb.Metric{Type: pb.MetricType(mt), Value: m.Value}) } + } + } + return result +} - ret.LsNodes = append(ret.LsNodes, node) +// convertLsPrefixes converts a slice of table.LsPrefix to protobuf LsPrefix. +func convertLsPrefixes(prefixes []*table.LsPrefix) []*pb.LsPrefix { + if prefixes == nil { + return nil + } + result := make([]*pb.LsPrefix, 0, len(prefixes)) + for _, p := range prefixes { + if p != nil { + result = append(result, &pb.LsPrefix{Prefix: p.Prefix.String(), SidIndex: p.SidIndex}) } } + return result +} - s.logger.Debug("Send GetTED API reply") - return ret, nil +// convertLsSrv6SIDs converts a slice of table.LsSrv6SID to protobuf LsSrv6SID. +func convertLsSrv6SIDs(sids []*table.LsSrv6SID) []*pb.LsSrv6SID { + if sids == nil { + return nil + } + result := make([]*pb.LsSrv6SID, 0, len(sids)) + for _, s := range sids { + if s != nil { + result = append(result, buildLsSrv6SID(s)) + } + } + return result +} + +// buildLsSrv6SID converts a single table.LsSrv6SID to protobuf LsSrv6SID. +func buildLsSrv6SID(s *table.LsSrv6SID) *pb.LsSrv6SID { + pbSID := &pb.LsSrv6SID{ + Sids: make([]*pb.SID, 0, len(s.Sids)), + MultiTopoIds: make([]*pb.MultiTopoID, 0, len(s.MultiTopoIDs)), + SidStructure: &pb.SidStructure{ + LocalBlock: uint32(s.SIDStructure.LocalBlock), + LocalNode: uint32(s.SIDStructure.LocalNode), + LocalFunc: uint32(s.SIDStructure.LocalFunc), + LocalArg: uint32(s.SIDStructure.LocalArg), + }, + } + + for _, sid := range s.Sids { + if sid != "" { + pbSID.Sids = append(pbSID.Sids, &pb.SID{Sid: sid}) + } + } + + for _, topoID := range s.MultiTopoIDs { + pbSID.MultiTopoIds = append(pbSID.MultiTopoIds, &pb.MultiTopoID{MultiTopoId: topoID}) + } + + if s.EndpointBehavior != (table.EndpointBehavior{}) { + pbSID.EndpointBehavior = &pb.EndpointBehavior{ + Behavior: uint32(s.EndpointBehavior.Behavior), + Flags: uint32(s.EndpointBehavior.Flags), + Algorithm: uint32(s.EndpointBehavior.Algorithm), + } + } + + return pbSID +} + +// convertSrv6EndXSID converts table.Srv6EndXSID to protobuf Srv6EndXSID. +func convertSrv6EndXSID(sid *table.Srv6EndXSID) *pb.Srv6EndXSID { + pbSID := &pb.Srv6EndXSID{ + EndpointBehavior: uint32(sid.EndpointBehavior), + Sids: make([]*pb.SID, 0, len(sid.Sids)), + SidStructure: &pb.SidStructure{ + LocalBlock: uint32(sid.Srv6SIDStructure.LocalBlock), + LocalNode: uint32(sid.Srv6SIDStructure.LocalNode), + LocalFunc: uint32(sid.Srv6SIDStructure.LocalFunc), + LocalArg: uint32(sid.Srv6SIDStructure.LocalArg), + }, + } + + for _, s := range sid.Sids { + if s != "" { + pbSID.Sids = append(pbSID.Sids, &pb.SID{Sid: s}) + } + } + + return pbSID } func (s *APIServer) DeleteSession(ctx context.Context, req *pb.DeleteSessionRequest) (*pb.DeleteSessionResponse, error) { From 15411f0246d9dded36e1e3269eee60f75efd0bba Mon Sep 17 00:00:00 2001 From: watal Date: Tue, 21 Oct 2025 19:11:59 +0900 Subject: [PATCH 79/87] refactor(session): split handlePCRpt into helper functions to reduce cyclomatic complexity --- pkg/server/session.go | 120 +++++++++++++++++++++++++----------------- 1 file changed, 73 insertions(+), 47 deletions(-) diff --git a/pkg/server/session.go b/pkg/server/session.go index ddc80a91..0973bc5e 100644 --- a/pkg/server/session.go +++ b/pkg/server/session.go @@ -259,73 +259,99 @@ func (ss *Session) handlePCRpt(length uint16) error { } for _, sr := range message.StateReports { - // Synchronization if sr.LSPObject.SFlag { - ss.logger.Debug("Synchronize SR Policy information", zap.Any("Message", message)) - if err := ss.RegisterSRPolicy(*sr); err != nil { - ss.logger.Error("Failed to register SR Policy during synchronization", zap.Error(err), zap.Uint32("plspID", sr.LSPObject.PlspID)) + if err := ss.handleSynchronization(sr, message); err != nil { return err } continue } switch { - // Finish synchronization case sr.LSPObject.PlspID == 0: - ss.logger.Debug("Finish PCRpt state synchronization") - ss.isSynced = true - - // Response to request from PCE + ss.handleFinishSynchronization() case sr.SrpObject.SrpID != 0: - ss.logger.Debug("Finish Stateful PCE request", zap.Uint32("srpID", sr.SrpObject.SrpID)) - if sr.LSPObject.RFlag { - ss.DeleteSRPolicy(*sr) - } else { - if err := ss.RegisterSRPolicy(*sr); err != nil { - ss.logger.Error("Failed to register SR Policy for Stateful PCE request", zap.Error(err), zap.Uint32("plspID", sr.LSPObject.PlspID)) - return err - } + if err := ss.handleStatefulPCERequest(sr); err != nil { + return err } - - // Receive SR Policy with PLSP-ID case sr.LSPObject.PlspID != 0: - ss.logger.Debug("Received SR Policy", zap.Uint32("plspID", sr.LSPObject.PlspID)) - - computedSegmentList, err := ss.computePathFromTED(*sr) - if err != nil { - ss.logger.Error("Failed to compute path from TED", zap.Error(err)) + if err := ss.handleSRPolicyWithPLSPID(sr); err != nil { return err } - sr.EroObject = createEroFromSegmentList(computedSegmentList) - - if err := ss.RegisterSRPolicy(*sr); err != nil { - ss.logger.Error("Failed to register SR Policy", zap.Error(err), zap.Uint32("plspID", sr.LSPObject.PlspID)) + default: + if err := ss.handleDefaultSRPolicy(sr); err != nil { return err } + } + } - policy, found := ss.SearchSRPolicy(sr.LSPObject.PlspID) - if !found { - ss.logger.Warn("SR Policy not found after registration", zap.Uint32("plspID", sr.LSPObject.PlspID)) - return fmt.Errorf("SR Policy %d not found after registration", sr.LSPObject.PlspID) - } + return nil +} - if err := ss.SendPCUpdate(*policy); err != nil { - ss.logger.Error("Failed to send PC update", zap.Uint32("plspID", sr.LSPObject.PlspID), zap.Error(err)) - return err - } +// Synchronization (S-Flag) +func (ss *Session) handleSynchronization(sr *pcep.StateReport, message *pcep.PCRptMessage) error { + ss.logger.Debug("Synchronize SR Policy information", zap.Any("Message", message)) + if err := ss.RegisterSRPolicy(*sr); err != nil { + ss.logger.Error("Failed to register SR Policy during synchronization", zap.Error(err), zap.Uint32("plspID", sr.LSPObject.PlspID)) + return err + } + return nil +} - default: - if sr.LSPObject.RFlag { - ss.DeleteSRPolicy(*sr) - } else { - if err := ss.RegisterSRPolicy(*sr); err != nil { - ss.logger.Error("Failed to register SR Policy (default case)", zap.Error(err), zap.Uint32("plspID", sr.LSPObject.PlspID)) - return err - } - } - } +// Finish synchronization (PlspID == 0) +func (ss *Session) handleFinishSynchronization() { + ss.logger.Debug("Finish PCRpt state synchronization") + ss.isSynced = true +} + +// Response to request from PCE (SrpID != 0) +func (ss *Session) handleStatefulPCERequest(sr *pcep.StateReport) error { + ss.logger.Debug("Finish Stateful PCE request", zap.Uint32("srpID", sr.SrpObject.SrpID)) + if sr.LSPObject.RFlag { + ss.DeleteSRPolicy(*sr) + } else if err := ss.RegisterSRPolicy(*sr); err != nil { + ss.logger.Error("Failed to register SR Policy for Stateful PCE request", zap.Error(err), zap.Uint32("plspID", sr.LSPObject.PlspID)) + return err + } + return nil +} + +// Receive SR Policy with PLSP-ID +func (ss *Session) handleSRPolicyWithPLSPID(sr *pcep.StateReport) error { + ss.logger.Debug("Received SR Policy", zap.Uint32("plspID", sr.LSPObject.PlspID)) + + computedSegmentList, err := ss.computePathFromTED(*sr) + if err != nil { + ss.logger.Error("Failed to compute path from TED", zap.Error(err)) + return err + } + sr.EroObject = createEroFromSegmentList(computedSegmentList) + + if err := ss.RegisterSRPolicy(*sr); err != nil { + ss.logger.Error("Failed to register SR Policy", zap.Error(err), zap.Uint32("plspID", sr.LSPObject.PlspID)) + return err + } + + policy, found := ss.SearchSRPolicy(sr.LSPObject.PlspID) + if !found { + ss.logger.Warn("SR Policy not found after registration", zap.Uint32("plspID", sr.LSPObject.PlspID)) + return fmt.Errorf("SR Policy %d not found after registration", sr.LSPObject.PlspID) } + if err := ss.SendPCUpdate(*policy); err != nil { + ss.logger.Error("Failed to send PC update", zap.Uint32("plspID", sr.LSPObject.PlspID), zap.Error(err)) + return err + } + return nil +} + +// Default case +func (ss *Session) handleDefaultSRPolicy(sr *pcep.StateReport) error { + if sr.LSPObject.RFlag { + ss.DeleteSRPolicy(*sr) + } else if err := ss.RegisterSRPolicy(*sr); err != nil { + ss.logger.Error("Failed to register SR Policy (default case)", zap.Error(err), zap.Uint32("plspID", sr.LSPObject.PlspID)) + return err + } return nil } From 1a9f005870b298cd113776d14c51ba28aeab2215 Mon Sep 17 00:00:00 2001 From: watal Date: Tue, 21 Oct 2025 19:16:36 +0900 Subject: [PATCH 80/87] refactor(session): split RegisterSRPolicy into helper functions to reduce cyclomatic complexity --- pkg/server/session.go | 147 +++++++++++++++++++++++------------------- 1 file changed, 79 insertions(+), 68 deletions(-) diff --git a/pkg/server/session.go b/pkg/server/session.go index 0973bc5e..7c4ef510 100644 --- a/pkg/server/session.go +++ b/pkg/server/session.go @@ -538,106 +538,117 @@ func (ss *Session) SendPCUpdate(srPolicy table.SRPolicy) error { } func (ss *Session) RegisterSRPolicy(sr pcep.StateReport) error { - var ( - color uint32 // initialized to zero (RFC does not specify a default) - preference uint32 // initialized to zero (RFC does not specify a default) - ) + // Resolve color and preference for this SR Policy + color, preference := ss.resolveColorPreference(&sr) + + // Determine the policy state based on O-Flag + state := resolvePolicyState(sr.LSPObject.OFlag) + + // Validate Segment List + segmentList, err := validateSegmentList(sr) + if err != nil { + return err + } + + // Update existing policy or create a new one + return ss.updateOrCreatePolicy(sr, segmentList, color, preference, state) +} + +// resolveColorPreference returns the color and preference for the SR Policy +func (ss *Session) resolveColorPreference(sr *pcep.StateReport) (uint32, uint32) { + var color, preference uint32 if ss.pccType == pcep.CiscoLegacy { // Cisco legacy mode: get color and preference from Vendor Information Object color = sr.VendorInformationObject.Color() preference = sr.VendorInformationObject.Preference() } else { - // TODO: Move hasColorCapability to Session struct - hasColorCapability := false + // Check if PCC supports color capability + hasColor := false for _, cap := range ss.pccCapabilities { - if statefulCap, ok := cap.(*pcep.StatefulPCECapability); ok { - if statefulCap.ColorCapability { - hasColorCapability = true - break - } + if c, ok := cap.(*pcep.StatefulPCECapability); ok && c.ColorCapability { + hasColor = true + break } } - // SR Policy Association color takes precedence over LSP Object Color TLV - // Ref: https://datatracker.ietf.org/doc/draft-ietf-pce-pcep-color/12/ Section 2 - if sr.AssociationObject.Color() != 0 { - color = sr.AssociationObject.Color() - } else if hasColorCapability { + // SR Policy Association color takes precedence + if c := sr.AssociationObject.Color(); c != 0 { + color = c + } else if hasColor { color = sr.LSPObject.Color() } preference = sr.AssociationObject.Preference() } - lspID := sr.LSPObject.LSPID + return color, preference +} - var state table.PolicyState - switch sr.LSPObject.OFlag { - case uint8(0x00): - state = table.PolicyDown - case uint8(0x01): - state = table.PolicyUp - case uint8(0x02): - state = table.PolicyActive +// resolvePolicyState converts O-Flag to internal PolicyState +func resolvePolicyState(oflag uint8) table.PolicyState { + switch oflag { + case 0x00: + return table.PolicyDown + case 0x01: + return table.PolicyUp + case 0x02: + return table.PolicyActive default: - state = table.PolicyUnknown + return table.PolicyUnknown } +} - // Validate Segment List +// validateSegmentList checks if the Segment List exists and is non-empty +func validateSegmentList(sr pcep.StateReport) ([]table.Segment, error) { if sr.EroObject == nil { - return fmt.Errorf("EroObject is nil for PlspID %d", sr.LSPObject.PlspID) + return nil, fmt.Errorf("EroObject is nil for PlspID %d", sr.LSPObject.PlspID) } - segmentList := sr.EroObject.ToSegmentList() - if len(segmentList) == 0 { - return fmt.Errorf("SegmentList is empty for PlspID %d", sr.LSPObject.PlspID) + list := sr.EroObject.ToSegmentList() + if len(list) == 0 { + return nil, fmt.Errorf("SegmentList is empty for PlspID %d", sr.LSPObject.PlspID) } + return list, nil +} + +// updateOrCreatePolicy updates an existing SR Policy or creates a new one +func (ss *Session) updateOrCreatePolicy(sr pcep.StateReport, segmentList []table.Segment, color, preference uint32, state table.PolicyState) error { + lspID := sr.LSPObject.LSPID if p, ok := ss.SearchSRPolicy(sr.LSPObject.PlspID); ok { // Update existing policy if LSPID is new or equal if p.LSPID <= lspID { - p.Update( - table.PolicyDiff{ - Name: &sr.LSPObject.Name, - Color: &color, - Preference: &preference, - SegmentList: segmentList, - LSPID: lspID, - State: state, - }, - ) - } - } else { - // Create a new SR policy - var src, dst netip.Addr - if src = sr.LSPObject.SrcAddr; !src.IsValid() { - src = sr.AssociationObject.AssocSrc - } - if !src.IsValid() { - return fmt.Errorf("invalid source address for PlspID %d", sr.LSPObject.PlspID) + p.Update(table.PolicyDiff{ + Name: &sr.LSPObject.Name, + Color: &color, + Preference: &preference, + SegmentList: segmentList, + LSPID: lspID, + State: state, + }) } + return nil + } - if dst = sr.LSPObject.DstAddr; !dst.IsValid() { - dst = sr.AssociationObject.Endpoint() - } - if !dst.IsValid() { - return fmt.Errorf("invalid destination address for PlspID %d", sr.LSPObject.PlspID) - } + // Create a new SR Policy + src := sr.LSPObject.SrcAddr + if !src.IsValid() { + src = sr.AssociationObject.AssocSrc + } + if !src.IsValid() { + return fmt.Errorf("invalid source address for PlspID %d", sr.LSPObject.PlspID) + } - p := table.NewSRPolicy( - sr.LSPObject.PlspID, - sr.LSPObject.Name, - segmentList, - src, - dst, - color, - preference, - lspID, - state, - ) - ss.srPolicies = append(ss.srPolicies, p) + dst := sr.LSPObject.DstAddr + if !dst.IsValid() { + dst = sr.AssociationObject.Endpoint() + } + if !dst.IsValid() { + return fmt.Errorf("invalid destination address for PlspID %d", sr.LSPObject.PlspID) } + p := table.NewSRPolicy(sr.LSPObject.PlspID, sr.LSPObject.Name, segmentList, src, dst, color, preference, lspID, state) + ss.srPolicies = append(ss.srPolicies, p) return nil } From 92ecb568f36558ad1e54da5fab7d8c778f27f0c1 Mon Sep 17 00:00:00 2001 From: Wataru Mishima Date: Wed, 22 Oct 2025 10:34:55 +0900 Subject: [PATCH 81/87] Update pkg/server/grpc_server.go fix(server): correct validation message for ASN to indicate zero value Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- pkg/server/grpc_server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/server/grpc_server.go b/pkg/server/grpc_server.go index be351a8c..c76e8e15 100644 --- a/pkg/server/grpc_server.go +++ b/pkg/server/grpc_server.go @@ -213,7 +213,7 @@ func validate(inputSRPolicy *pb.SRPolicy, asn uint32, validationKind ValidationK return errors.New("validate error, input is nil") } if asn == 0 { - return errors.New("validate error, ASN is nil") + return errors.New("validate error, ASN must not be zero") } if validateFunc, ok := validator[validationKind]; ok { if err := validateFunc(inputSRPolicy, asn); err != nil { From 31e41aabd58dc929e572385299f62634b42cf68a Mon Sep 17 00:00:00 2001 From: Wataru Mishima Date: Wed, 22 Oct 2025 10:36:05 +0900 Subject: [PATCH 82/87] Update pkg/packet/pcep/tlv.go refactor(pcep): extract expected length into variable Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- pkg/packet/pcep/tlv.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/packet/pcep/tlv.go b/pkg/packet/pcep/tlv.go index 52d86a5f..e0929ac9 100644 --- a/pkg/packet/pcep/tlv.go +++ b/pkg/packet/pcep/tlv.go @@ -277,8 +277,9 @@ const ( ) func (tlv *StatefulPCECapability) DecodeFromBytes(data []byte) error { - if len(data) < TLVHeaderLength+int(TLVStatefulPCECapabilityValueLength) { - return fmt.Errorf("data is too short: expected at least %d bytes, but got %d bytes for StatefulPCECapability", TLVHeaderLength+TLVStatefulPCECapabilityValueLength, len(data)) + expectedLength := TLVHeaderLength + int(TLVStatefulPCECapabilityValueLength) + if len(data) < expectedLength { + return fmt.Errorf("data is too short: expected at least %d bytes, but got %d bytes for StatefulPCECapability", expectedLength, len(data)) } flags := uint32(data[TLVHeaderLength+StatefulPCECapabilityFlagsIndex]) From 204f4f40ece9f28de7813fa8cc6a89fb494552f4 Mon Sep 17 00:00:00 2001 From: Wataru Mishima Date: Wed, 22 Oct 2025 10:53:35 +0900 Subject: [PATCH 83/87] test(tlv): fix length comments Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- pkg/packet/pcep/tlv_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/packet/pcep/tlv_test.go b/pkg/packet/pcep/tlv_test.go index 46c186ee..9b2f3c8c 100644 --- a/pkg/packet/pcep/tlv_test.go +++ b/pkg/packet/pcep/tlv_test.go @@ -315,7 +315,7 @@ func TestIPv6LSPIdentifiers_DecodeFromBytes(t *testing.T) { { name: "Invalid IPv6 LSP Identifiers (truncated '2001:db8::1')", input: []uint8{ - 0x00, 0x13, 0x00, 0x20, // Type IPV6-LSP-IDENTIFIERS (0x13)、Length 56 (0x38) + 0x00, 0x13, 0x00, 0x20, // Type IPV6-LSP-IDENTIFIERS (0x13)、Length 32 (0x20) 0x20, 0x01, 0x0D, 0xB8, // Start of '2001:db8::' 0x00, 0x00, 0x00, // Incomplete (should be 16 bytes total) }, @@ -325,7 +325,7 @@ func TestIPv6LSPIdentifiers_DecodeFromBytes(t *testing.T) { { name: "Invalid IPv6 LSP Identifiers (extra bytes after '2001:db8::1')", input: []uint8{ - 0x00, 0x13, 0x00, 0x20, // Type IPV6-LSP-IDENTIFIERS (0x13)、Length 56 (0x38) + 0x00, 0x13, 0x00, 0x20, // Type IPV6-LSP-IDENTIFIERS (0x13), Length 32 (0x20) 0x20, 0x01, 0x0D, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // Valid IPv6: 2001:db8::1 0xCA, 0xFE, 0xBA, 0xBE, // Extra unexpected bytes }, From 45a8ecb4e8898256996d48882ac336d046dbd9da Mon Sep 17 00:00:00 2001 From: watal Date: Wed, 22 Oct 2025 10:56:17 +0900 Subject: [PATCH 84/87] test(tlv): remove unnecessary test for SymbolicPathName --- pkg/packet/pcep/tlv_test.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pkg/packet/pcep/tlv_test.go b/pkg/packet/pcep/tlv_test.go index 9b2f3c8c..3784d4de 100644 --- a/pkg/packet/pcep/tlv_test.go +++ b/pkg/packet/pcep/tlv_test.go @@ -164,12 +164,6 @@ func TestSymbolicPathName_DecodeFromBytes(t *testing.T) { expected: NewSymbolicPathName(""), err: true, }, - { - name: "Invalid input (too long data)", - input: []byte{0x00, 0x11, 0x00, 0x01, 'T', 'e'}, // Input too long for valid decoding - expected: NewSymbolicPathName(""), - err: true, - }, } for _, tt := range tests { From b0afeab5f8bf1917cf1f691a0db87a409fcdebad Mon Sep 17 00:00:00 2001 From: Wataru Mishima Date: Wed, 22 Oct 2025 11:09:10 +0900 Subject: [PATCH 85/87] Update test/README.md docs(test): replace fixed timestamp with generic placeholder in example output Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- test/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/README.md b/test/README.md index 93abc77a..8f67230d 100644 --- a/test/README.md +++ b/test/README.md @@ -41,7 +41,7 @@ uv 0.8.13 ```bash $ docker images | grep vrnetlab/juniper_vjunos-router | grep 25.2R1.9 -vrnetlab/juniper_vjunos-router 25.2R1.9 6e9b1472b46b 37 minutes ago 4.18GB +vrnetlab/juniper_vjunos-router 25.2R1.9 6e9b1472b46b X hours/days ago 4.18GB ``` #### how to install image From 61fd049158339a28d244b380ec45b5d4d2d982c6 Mon Sep 17 00:00:00 2001 From: watal Date: Wed, 22 Oct 2025 15:09:16 +0900 Subject: [PATCH 86/87] fix(pcep): clarify error message for invalid mixed endpoint addresses --- pkg/packet/pcep/object.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/packet/pcep/object.go b/pkg/packet/pcep/object.go index 98486a93..b78bec49 100644 --- a/pkg/packet/pcep/object.go +++ b/pkg/packet/pcep/object.go @@ -1224,7 +1224,7 @@ func (o *EndpointsObject) Len() (uint16, error) { // CommonObjectHeader(4byte) + srcIPv4 (16byte) + dstIPv4 (16byte) length = commonObjectHeaderLength + 16 + 16 } else { - return uint16(0), fmt.Errorf("invalid endpoints address (Len()): src=%v dst=%v", o.SrcAddr, o.DstAddr) + return uint16(0), fmt.Errorf("invalid endpoint addresses (Len()): source and destination must be both IPv4 or both IPv6: src=%v dst=%v", o.SrcAddr, o.DstAddr) } return length, nil } @@ -1236,7 +1236,7 @@ func NewEndpointsObject(dstAddr netip.Addr, srcAddr netip.Addr) (*EndpointsObjec } else if dstAddr.Is6() && srcAddr.Is6() { objectType = ObjectTypeEndpointIPv6 } else { - return nil, fmt.Errorf("invalid endpoints address (NewEndpointsObject): dst=%v src=%v", dstAddr, srcAddr) + return nil, fmt.Errorf("invalid endpoint addresses (NewEndpointsObject): source and destination must be both IPv4 or both IPv6 (dst=%v src=%v)", dstAddr, srcAddr) } o := &EndpointsObject{ From 42a4e981e33469ff9948b568a22248033d9d7449 Mon Sep 17 00:00:00 2001 From: watal Date: Wed, 22 Oct 2025 09:48:13 +0900 Subject: [PATCH 87/87] docs(example): update GoBGP and Pola PCE version requirements in README --- examples/containerlab/srv6_usid_dynamic-path/README.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/examples/containerlab/srv6_usid_dynamic-path/README.md b/examples/containerlab/srv6_usid_dynamic-path/README.md index 07e68552..0365c8f0 100644 --- a/examples/containerlab/srv6_usid_dynamic-path/README.md +++ b/examples/containerlab/srv6_usid_dynamic-path/README.md @@ -32,12 +32,8 @@ sudo ip link set dev switch up Copy Pola PCE & GoBGP to bin -* GoBGP: Use [this version](https://github.com/k1yoto/gobgp/tree/feature/bgp-ls-srv6) -* Pola PCE: Replace the GoBGP module in go.mod with your local GoBGP version, e.g.: - -```text -replace github.com/osrg/gobgp/v4 => ../gobgp -``` +* GoBGP: v4.0.0 or later +* Pola PCE: [commit 7a72c02](https://github.com/nttcom/pola/commit/7a72c02085d72d9b6a0dc1a887745fd9ec25fe60) or later Start Containerlab network