summaryrefslogtreecommitdiffhomepage
path: root/internal
diff options
context:
space:
mode:
authorFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2018-07-07 13:48:38 +0900
committerFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2018-07-07 20:44:25 +0900
commitc4775c42510d1f1ddd55036dc19e982712fa6a0b (patch)
tree6ec8b61d4338c809e239e3003a2d32d480898e22 /internal
parentb3079759aa13172fcb548a83da9a9653d8d5fed4 (diff)
follow Standard Go Project Layout
https://github.com/golang-standards/project-layout Now you can see clearly what are private and public library code. Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
Diffstat (limited to 'internal')
-rw-r--r--internal/pkg/client/client.go849
-rw-r--r--internal/pkg/client/client_test.go55
-rw-r--r--internal/pkg/config/bgp_configs.go6335
-rw-r--r--internal/pkg/config/bgp_configs_test.go113
-rw-r--r--internal/pkg/config/default.go524
-rw-r--r--internal/pkg/config/default_linux.go72
-rw-r--r--internal/pkg/config/default_nonlinux.go25
-rw-r--r--internal/pkg/config/serve.go159
-rw-r--r--internal/pkg/config/util.go264
-rw-r--r--internal/pkg/config/util_test.go32
-rw-r--r--internal/pkg/table/adj.go186
-rw-r--r--internal/pkg/table/adj_test.go52
-rw-r--r--internal/pkg/table/destination.go1041
-rw-r--r--internal/pkg/table/destination_test.go442
-rw-r--r--internal/pkg/table/message.go502
-rw-r--r--internal/pkg/table/message_test.go663
-rw-r--r--internal/pkg/table/path.go1179
-rw-r--r--internal/pkg/table/path_test.go365
-rw-r--r--internal/pkg/table/policy.go3994
-rw-r--r--internal/pkg/table/policy_test.go3140
-rw-r--r--internal/pkg/table/roa.go60
-rw-r--r--internal/pkg/table/table.go451
-rw-r--r--internal/pkg/table/table_manager.go356
-rw-r--r--internal/pkg/table/table_manager_test.go2282
-rw-r--r--internal/pkg/table/table_test.go180
-rw-r--r--internal/pkg/table/vrf.go53
-rw-r--r--internal/pkg/zebra/afi_string.go17
-rw-r--r--internal/pkg/zebra/api_type_string.go16
-rw-r--r--internal/pkg/zebra/link_type_string.go16
-rw-r--r--internal/pkg/zebra/nexthop_flag_string.go17
-rw-r--r--internal/pkg/zebra/ptm_enable_string.go16
-rw-r--r--internal/pkg/zebra/ptm_status_string.go16
-rw-r--r--internal/pkg/zebra/route_type_string.go16
-rw-r--r--internal/pkg/zebra/safi_string.go17
-rw-r--r--internal/pkg/zebra/zapi.go1917
-rw-r--r--internal/pkg/zebra/zapi_bsd.go58
-rw-r--r--internal/pkg/zebra/zapi_darwin.go59
-rw-r--r--internal/pkg/zebra/zapi_linux.go83
-rw-r--r--internal/pkg/zebra/zapi_test.go531
-rw-r--r--internal/pkg/zebra/zapi_windows.go38
40 files changed, 26191 insertions, 0 deletions
diff --git a/internal/pkg/client/client.go b/internal/pkg/client/client.go
new file mode 100644
index 00000000..28adfd62
--- /dev/null
+++ b/internal/pkg/client/client.go
@@ -0,0 +1,849 @@
+// Copyright (C) 2016 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package client provides a wrapper for GoBGP's gRPC API
+package client
+
+import (
+ "fmt"
+ "io"
+ "net"
+ "strconv"
+ "time"
+
+ "golang.org/x/net/context"
+ "google.golang.org/grpc"
+
+ api "github.com/osrg/gobgp/api"
+ "github.com/osrg/gobgp/internal/pkg/config"
+ "github.com/osrg/gobgp/pkg/packet/bgp"
+ "github.com/osrg/gobgp/pkg/server"
+)
+
+type Client struct {
+ conn *grpc.ClientConn
+ cli api.GobgpApiClient
+}
+
+func defaultGRPCOptions() []grpc.DialOption {
+ return []grpc.DialOption{grpc.WithTimeout(time.Second), grpc.WithBlock(), grpc.WithInsecure()}
+}
+
+// New returns a new Client using the given target and options for dialing
+// to the grpc server. If an error occurs during dialing it will be returned and
+// Client will be nil.
+func New(target string, opts ...grpc.DialOption) (*Client, error) {
+ return NewWith(context.Background(), target, opts...)
+}
+
+// NewWith is like New, but uses the given ctx to cancel or expire the current
+// attempt to connect if it becomes Done before the connection succeeds.
+func NewWith(ctx context.Context, target string, opts ...grpc.DialOption) (*Client, error) {
+ if target == "" {
+ target = ":50051"
+ }
+ if len(opts) == 0 {
+ opts = defaultGRPCOptions()
+ }
+ conn, err := grpc.DialContext(ctx, target, opts...)
+ if err != nil {
+ return nil, err
+ }
+ cli := api.NewGobgpApiClient(conn)
+ return &Client{conn: conn, cli: cli}, nil
+}
+
+// NewFrom returns a new Client, using the given conn and cli for the
+// underlying connection. The given grpc.ClientConn connection is expected to be
+// initialized and paired with the api client. See New to have the connection
+// dialed for you.
+func NewFrom(conn *grpc.ClientConn, cli api.GobgpApiClient) *Client {
+ return &Client{conn: conn, cli: cli}
+}
+
+func (cli *Client) Close() error {
+ return cli.conn.Close()
+}
+
+func (cli *Client) StartServer(c *config.Global) error {
+ _, err := cli.cli.StartServer(context.Background(), &api.StartServerRequest{
+ Global: &api.Global{
+ As: c.Config.As,
+ RouterId: c.Config.RouterId,
+ ListenPort: c.Config.Port,
+ ListenAddresses: c.Config.LocalAddressList,
+ UseMultiplePaths: c.UseMultiplePaths.Config.Enabled,
+ },
+ })
+ return err
+}
+
+func (cli *Client) StopServer() error {
+ _, err := cli.cli.StopServer(context.Background(), &api.StopServerRequest{})
+ return err
+}
+
+func (cli *Client) GetServer() (*config.Global, error) {
+ ret, err := cli.cli.GetServer(context.Background(), &api.GetServerRequest{})
+ if err != nil {
+ return nil, err
+ }
+ return &config.Global{
+ Config: config.GlobalConfig{
+ As: ret.Global.As,
+ RouterId: ret.Global.RouterId,
+ Port: ret.Global.ListenPort,
+ LocalAddressList: ret.Global.ListenAddresses,
+ },
+ UseMultiplePaths: config.UseMultiplePaths{
+ Config: config.UseMultiplePathsConfig{
+ Enabled: ret.Global.UseMultiplePaths,
+ },
+ },
+ }, nil
+}
+
+func (cli *Client) EnableZebra(c *config.Zebra) error {
+ req := &api.EnableZebraRequest{
+ Url: c.Config.Url,
+ Version: uint32(c.Config.Version),
+ NexthopTriggerEnable: c.Config.NexthopTriggerEnable,
+ NexthopTriggerDelay: uint32(c.Config.NexthopTriggerDelay),
+ }
+ for _, t := range c.Config.RedistributeRouteTypeList {
+ req.RouteTypes = append(req.RouteTypes, string(t))
+ }
+ _, err := cli.cli.EnableZebra(context.Background(), req)
+ return err
+}
+
+func (cli *Client) getNeighbor(name string, afi int, vrf string, enableAdvertised bool) ([]*config.Neighbor, error) {
+ ret, err := cli.cli.GetNeighbor(context.Background(), &api.GetNeighborRequest{EnableAdvertised: enableAdvertised, Address: name})
+ if err != nil {
+ return nil, err
+ }
+
+ neighbors := make([]*config.Neighbor, 0, len(ret.Peers))
+
+ for _, p := range ret.Peers {
+ if name != "" && name != p.Info.NeighborAddress && name != p.Conf.NeighborInterface {
+ continue
+ }
+ if vrf != "" && name != p.Conf.Vrf {
+ continue
+ }
+ if afi > 0 {
+ v6 := net.ParseIP(p.Info.NeighborAddress).To4() == nil
+ if afi == bgp.AFI_IP && v6 || afi == bgp.AFI_IP6 && !v6 {
+ continue
+ }
+ }
+ n, err := server.NewNeighborFromAPIStruct(p)
+ if err != nil {
+ return nil, err
+ }
+ neighbors = append(neighbors, n)
+ }
+ return neighbors, nil
+}
+
+func (cli *Client) ListNeighbor() ([]*config.Neighbor, error) {
+ return cli.getNeighbor("", 0, "", false)
+}
+
+func (cli *Client) ListNeighborByTransport(afi int) ([]*config.Neighbor, error) {
+ return cli.getNeighbor("", afi, "", false)
+}
+
+func (cli *Client) ListNeighborByVRF(vrf string) ([]*config.Neighbor, error) {
+ return cli.getNeighbor("", 0, vrf, false)
+}
+
+func (cli *Client) GetNeighbor(name string, options ...bool) (*config.Neighbor, error) {
+ enableAdvertised := false
+ if len(options) > 0 && options[0] {
+ enableAdvertised = true
+ }
+ ns, err := cli.getNeighbor(name, 0, "", enableAdvertised)
+ if err != nil {
+ return nil, err
+ }
+ if len(ns) == 0 {
+ return nil, fmt.Errorf("not found neighbor %s", name)
+ }
+ return ns[0], nil
+}
+
+func (cli *Client) AddNeighbor(c *config.Neighbor) error {
+ peer := server.NewPeerFromConfigStruct(c)
+ _, err := cli.cli.AddNeighbor(context.Background(), &api.AddNeighborRequest{Peer: peer})
+ return err
+}
+
+func (cli *Client) DeleteNeighbor(c *config.Neighbor) error {
+ peer := server.NewPeerFromConfigStruct(c)
+ _, err := cli.cli.DeleteNeighbor(context.Background(), &api.DeleteNeighborRequest{Peer: peer})
+ return err
+}
+
+func (cli *Client) UpdateNeighbor(c *config.Neighbor, doSoftResetIn bool) (bool, error) {
+ peer := server.NewPeerFromConfigStruct(c)
+ response, err := cli.cli.UpdateNeighbor(context.Background(), &api.UpdateNeighborRequest{Peer: peer, DoSoftResetIn: doSoftResetIn})
+ return response.NeedsSoftResetIn, err
+}
+
+func (cli *Client) ShutdownNeighbor(addr, communication string) error {
+ _, err := cli.cli.ShutdownNeighbor(context.Background(), &api.ShutdownNeighborRequest{Address: addr, Communication: communication})
+ return err
+}
+
+func (cli *Client) ResetNeighbor(addr, communication string) error {
+ _, err := cli.cli.ResetNeighbor(context.Background(), &api.ResetNeighborRequest{Address: addr, Communication: communication})
+ return err
+}
+
+func (cli *Client) EnableNeighbor(addr string) error {
+ _, err := cli.cli.EnableNeighbor(context.Background(), &api.EnableNeighborRequest{Address: addr})
+ return err
+}
+
+func (cli *Client) DisableNeighbor(addr, communication string) error {
+ _, err := cli.cli.DisableNeighbor(context.Background(), &api.DisableNeighborRequest{Address: addr, Communication: communication})
+ return err
+}
+
+func (cli *Client) softreset(addr string, family bgp.RouteFamily, dir api.SoftResetNeighborRequest_SoftResetDirection) error {
+ _, err := cli.cli.SoftResetNeighbor(context.Background(), &api.SoftResetNeighborRequest{
+ Address: addr,
+ Direction: dir,
+ })
+ return err
+}
+
+func (cli *Client) SoftResetIn(addr string, family bgp.RouteFamily) error {
+ return cli.softreset(addr, family, api.SoftResetNeighborRequest_IN)
+}
+
+func (cli *Client) SoftResetOut(addr string, family bgp.RouteFamily) error {
+ return cli.softreset(addr, family, api.SoftResetNeighborRequest_OUT)
+}
+
+func (cli *Client) SoftReset(addr string, family bgp.RouteFamily) error {
+ return cli.softreset(addr, family, api.SoftResetNeighborRequest_BOTH)
+}
+
+func (cli *Client) getRIB(resource api.Resource, name string, family bgp.RouteFamily, prefixes []*api.TableLookupPrefix) (*api.Table, error) {
+ stream, err := cli.cli.GetPath(context.Background(), &api.GetPathRequest{
+ Type: resource,
+ Family: uint32(family),
+ Name: name,
+ Prefixes: prefixes,
+ })
+ if err != nil {
+ return nil, err
+ }
+ pathMap := make(map[string][]*api.Path)
+ for {
+ p, err := stream.Recv()
+ if err != nil {
+ if err == io.EOF {
+ break
+ }
+ return nil, err
+ }
+ nlri, err := p.GetNativeNlri()
+ if err != nil {
+ return nil, err
+ }
+ nlriStr := nlri.String()
+ pathMap[nlriStr] = append(pathMap[nlriStr], p)
+ }
+ dstList := make([]*api.Destination, 0, len(pathMap))
+ for _, pathList := range pathMap {
+ nlri, _ := pathList[0].GetNativeNlri()
+ dstList = append(dstList, &api.Destination{
+ Prefix: nlri.String(),
+ Paths: pathList,
+ })
+ }
+ return &api.Table{
+ Family: uint32(family),
+ Destinations: dstList,
+ }, nil
+}
+
+func (cli *Client) GetRIB(family bgp.RouteFamily, prefixes []*api.TableLookupPrefix) (*api.Table, error) {
+ return cli.getRIB(api.Resource_GLOBAL, "", family, prefixes)
+}
+
+func (cli *Client) GetLocalRIB(name string, family bgp.RouteFamily, prefixes []*api.TableLookupPrefix) (*api.Table, error) {
+ return cli.getRIB(api.Resource_LOCAL, name, family, prefixes)
+}
+
+func (cli *Client) GetAdjRIBIn(name string, family bgp.RouteFamily, prefixes []*api.TableLookupPrefix) (*api.Table, error) {
+ return cli.getRIB(api.Resource_ADJ_IN, name, family, prefixes)
+}
+
+func (cli *Client) GetAdjRIBOut(name string, family bgp.RouteFamily, prefixes []*api.TableLookupPrefix) (*api.Table, error) {
+ return cli.getRIB(api.Resource_ADJ_OUT, name, family, prefixes)
+}
+
+func (cli *Client) GetVRFRIB(name string, family bgp.RouteFamily, prefixes []*api.TableLookupPrefix) (*api.Table, error) {
+ return cli.getRIB(api.Resource_VRF, name, family, prefixes)
+}
+
+func (cli *Client) getRIBInfo(resource api.Resource, name string, family bgp.RouteFamily) (*api.TableInfo, error) {
+ r, err := cli.cli.GetRibInfo(context.Background(), &api.GetRibInfoRequest{
+ Info: &api.TableInfo{
+ Type: resource,
+ Name: name,
+ Family: uint32(family),
+ },
+ })
+ if err != nil {
+ return nil, err
+ } else {
+ return r.Info, nil
+ }
+}
+
+func (cli *Client) GetRIBInfo(family bgp.RouteFamily) (*api.TableInfo, error) {
+ return cli.getRIBInfo(api.Resource_GLOBAL, "", family)
+}
+
+func (cli *Client) GetLocalRIBInfo(name string, family bgp.RouteFamily) (*api.TableInfo, error) {
+ return cli.getRIBInfo(api.Resource_LOCAL, name, family)
+}
+
+func (cli *Client) GetAdjRIBInInfo(name string, family bgp.RouteFamily) (*api.TableInfo, error) {
+ return cli.getRIBInfo(api.Resource_ADJ_IN, name, family)
+}
+
+func (cli *Client) GetAdjRIBOutInfo(name string, family bgp.RouteFamily) (*api.TableInfo, error) {
+ return cli.getRIBInfo(api.Resource_ADJ_OUT, name, family)
+}
+
+type AddPathByStreamClient struct {
+ stream api.GobgpApi_InjectMrtClient
+}
+
+func (c *AddPathByStreamClient) Send(paths ...*api.Path) error {
+ return c.stream.Send(&api.InjectMrtRequest{
+ Resource: api.Resource_GLOBAL,
+ Paths: paths,
+ })
+}
+
+func (c *AddPathByStreamClient) Close() error {
+ _, err := c.stream.CloseAndRecv()
+ return err
+}
+
+func (cli *Client) AddPathByStream() (*AddPathByStreamClient, error) {
+ stream, err := cli.cli.InjectMrt(context.Background())
+ if err != nil {
+ return nil, err
+ }
+ return &AddPathByStreamClient{stream}, nil
+}
+
+func (cli *Client) addPath(vrfID string, pathList []*api.Path) ([]byte, error) {
+ resource := api.Resource_GLOBAL
+ if vrfID != "" {
+ resource = api.Resource_VRF
+ }
+ var uuid []byte
+ for _, path := range pathList {
+ r, err := cli.cli.AddPath(context.Background(), &api.AddPathRequest{
+ Resource: resource,
+ VrfId: vrfID,
+ Path: path,
+ })
+ if err != nil {
+ return nil, err
+ }
+ uuid = r.Uuid
+ }
+ return uuid, nil
+}
+
+func (cli *Client) AddPath(pathList []*api.Path) ([]byte, error) {
+ return cli.addPath("", pathList)
+}
+
+func (cli *Client) AddVRFPath(vrfID string, pathList []*api.Path) ([]byte, error) {
+ if vrfID == "" {
+ return nil, fmt.Errorf("VRF ID is empty")
+ }
+ return cli.addPath(vrfID, pathList)
+}
+
+func (cli *Client) deletePath(uuid []byte, f bgp.RouteFamily, vrfID string, pathList []*api.Path) error {
+ var reqs []*api.DeletePathRequest
+
+ resource := api.Resource_GLOBAL
+ if vrfID != "" {
+ resource = api.Resource_VRF
+ }
+ switch {
+ case len(pathList) != 0:
+ for _, path := range pathList {
+ reqs = append(reqs, &api.DeletePathRequest{
+ Resource: resource,
+ VrfId: vrfID,
+ Path: path,
+ })
+ }
+ default:
+ reqs = append(reqs, &api.DeletePathRequest{
+ Resource: resource,
+ VrfId: vrfID,
+ Uuid: uuid,
+ Family: uint32(f),
+ })
+ }
+
+ for _, req := range reqs {
+ if _, err := cli.cli.DeletePath(context.Background(), req); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (cli *Client) DeletePath(pathList []*api.Path) error {
+ return cli.deletePath(nil, bgp.RouteFamily(0), "", pathList)
+}
+
+func (cli *Client) DeleteVRFPath(vrfID string, pathList []*api.Path) error {
+ if vrfID == "" {
+ return fmt.Errorf("VRF ID is empty")
+ }
+ return cli.deletePath(nil, bgp.RouteFamily(0), vrfID, pathList)
+}
+
+func (cli *Client) DeletePathByUUID(uuid []byte) error {
+ return cli.deletePath(uuid, bgp.RouteFamily(0), "", nil)
+}
+
+func (cli *Client) DeletePathByFamily(family bgp.RouteFamily) error {
+ return cli.deletePath(nil, family, "", nil)
+}
+
+func (cli *Client) GetVRF() ([]*api.Vrf, error) {
+ ret, err := cli.cli.GetVrf(context.Background(), &api.GetVrfRequest{})
+ if err != nil {
+ return nil, err
+ }
+ return ret.Vrfs, nil
+}
+
+func (cli *Client) AddVRF(name string, id int, rd bgp.RouteDistinguisherInterface, im, ex []bgp.ExtendedCommunityInterface) error {
+ arg := &api.AddVrfRequest{
+ Vrf: &api.Vrf{
+ Name: name,
+ Rd: api.MarshalRD(rd),
+ Id: uint32(id),
+ ImportRt: api.MarshalRTs(im),
+ ExportRt: api.MarshalRTs(ex),
+ },
+ }
+ _, err := cli.cli.AddVrf(context.Background(), arg)
+ return err
+}
+
+func (cli *Client) DeleteVRF(name string) error {
+ arg := &api.DeleteVrfRequest{
+ Vrf: &api.Vrf{
+ Name: name,
+ },
+ }
+ _, err := cli.cli.DeleteVrf(context.Background(), arg)
+ return err
+}
+
+func (cli *Client) getDefinedSet(typ api.DefinedType, name string) ([]*api.DefinedSet, error) {
+ ret, err := cli.cli.GetDefinedSet(context.Background(), &api.GetDefinedSetRequest{
+ Type: api.DefinedType(typ),
+ Name: name,
+ })
+ if err != nil {
+ return nil, err
+ }
+ return ret.Sets, nil
+}
+
+func (cli *Client) GetDefinedSet(typ api.DefinedType) ([]*api.DefinedSet, error) {
+ return cli.getDefinedSet(typ, "")
+}
+
+func (cli *Client) GetDefinedSetByName(typ api.DefinedType, name string) (*api.DefinedSet, error) {
+ sets, err := cli.getDefinedSet(typ, name)
+ if err != nil {
+ return nil, err
+ }
+ if len(sets) == 0 {
+ return nil, fmt.Errorf("not found defined set: %s", name)
+ } else if len(sets) > 1 {
+ return nil, fmt.Errorf("invalid response for GetDefinedSetByName")
+ }
+ return sets[0], nil
+}
+
+func (cli *Client) AddDefinedSet(d *api.DefinedSet) error {
+ _, err := cli.cli.AddDefinedSet(context.Background(), &api.AddDefinedSetRequest{
+ Set: d,
+ })
+ return err
+}
+
+func (cli *Client) DeleteDefinedSet(d *api.DefinedSet, all bool) error {
+ _, err := cli.cli.DeleteDefinedSet(context.Background(), &api.DeleteDefinedSetRequest{
+ Set: d,
+ All: all,
+ })
+ return err
+}
+
+func (cli *Client) ReplaceDefinedSet(d *api.DefinedSet) error {
+ _, err := cli.cli.ReplaceDefinedSet(context.Background(), &api.ReplaceDefinedSetRequest{
+ Set: d,
+ })
+ return err
+}
+
+func (cli *Client) GetStatement() ([]*api.Statement, error) {
+ ret, err := cli.cli.GetStatement(context.Background(), &api.GetStatementRequest{})
+ if err != nil {
+ return nil, err
+ }
+ return ret.Statements, nil
+}
+
+func (cli *Client) AddStatement(t *api.Statement) error {
+ _, err := cli.cli.AddStatement(context.Background(), &api.AddStatementRequest{
+ Statement: t,
+ })
+ return err
+}
+
+func (cli *Client) DeleteStatement(t *api.Statement, all bool) error {
+ _, err := cli.cli.DeleteStatement(context.Background(), &api.DeleteStatementRequest{
+ Statement: t,
+ All: all,
+ })
+ return err
+}
+
+func (cli *Client) ReplaceStatement(t *api.Statement) error {
+ _, err := cli.cli.ReplaceStatement(context.Background(), &api.ReplaceStatementRequest{
+ Statement: t,
+ })
+ return err
+}
+
+func (cli *Client) GetPolicy() ([]*api.Policy, error) {
+ ret, err := cli.cli.GetPolicy(context.Background(), &api.GetPolicyRequest{})
+ if err != nil {
+ return nil, err
+ }
+ return ret.Policies, nil
+}
+
+func (cli *Client) AddPolicy(a *api.Policy, refer bool) error {
+ _, err := cli.cli.AddPolicy(context.Background(), &api.AddPolicyRequest{
+ Policy: a,
+ ReferExistingStatements: refer,
+ })
+ return err
+}
+
+func (cli *Client) DeletePolicy(a *api.Policy, all, preserve bool) error {
+ _, err := cli.cli.DeletePolicy(context.Background(), &api.DeletePolicyRequest{
+ Policy: a,
+ All: all,
+ PreserveStatements: preserve,
+ })
+ return err
+}
+
+func (cli *Client) ReplacePolicy(a *api.Policy, refer, preserve bool) error {
+ _, err := cli.cli.ReplacePolicy(context.Background(), &api.ReplacePolicyRequest{
+ Policy: a,
+ ReferExistingStatements: refer,
+ PreserveStatements: preserve,
+ })
+ return err
+}
+
+func (cli *Client) getPolicyAssignment(name string, typ api.PolicyType) (*api.PolicyAssignment, error) {
+ resource := api.Resource_GLOBAL
+ if name != "" {
+ resource = api.Resource_LOCAL
+ }
+
+ ret, err := cli.cli.GetPolicyAssignment(context.Background(), &api.GetPolicyAssignmentRequest{
+ Assignment: &api.PolicyAssignment{
+ Name: name,
+ Resource: resource,
+ Type: typ,
+ },
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ return &api.PolicyAssignment{
+ Name: name,
+ Type: typ,
+ Policies: ret.Assignment.Policies,
+ Default: ret.Assignment.Default,
+ }, nil
+}
+
+func (cli *Client) GetImportPolicy() (*api.PolicyAssignment, error) {
+ return cli.getPolicyAssignment("", api.PolicyType_IMPORT)
+}
+
+func (cli *Client) GetExportPolicy() (*api.PolicyAssignment, error) {
+ return cli.getPolicyAssignment("", api.PolicyType_EXPORT)
+}
+
+func (cli *Client) GetRouteServerImportPolicy(name string) (*api.PolicyAssignment, error) {
+ return cli.getPolicyAssignment(name, api.PolicyType_IMPORT)
+}
+
+func (cli *Client) GetRouteServerExportPolicy(name string) (*api.PolicyAssignment, error) {
+ return cli.getPolicyAssignment(name, api.PolicyType_EXPORT)
+}
+
+func (cli *Client) AddPolicyAssignment(assignment *api.PolicyAssignment) error {
+ _, err := cli.cli.AddPolicyAssignment(context.Background(), &api.AddPolicyAssignmentRequest{
+ Assignment: assignment,
+ })
+ return err
+}
+
+func (cli *Client) DeletePolicyAssignment(assignment *api.PolicyAssignment, all bool) error {
+ _, err := cli.cli.DeletePolicyAssignment(context.Background(), &api.DeletePolicyAssignmentRequest{
+ Assignment: assignment,
+ All: all})
+ return err
+}
+
+func (cli *Client) ReplacePolicyAssignment(assignment *api.PolicyAssignment) error {
+ _, err := cli.cli.ReplacePolicyAssignment(context.Background(), &api.ReplacePolicyAssignmentRequest{
+ Assignment: assignment,
+ })
+ return err
+}
+
+//func (cli *Client) EnableMrt(c *config.MrtConfig) error {
+//}
+//
+//func (cli *Client) DisableMrt(c *config.MrtConfig) error {
+//}
+//
+
+func (cli *Client) GetRPKI() ([]*config.RpkiServer, error) {
+ rsp, err := cli.cli.GetRpki(context.Background(), &api.GetRpkiRequest{})
+ if err != nil {
+ return nil, err
+ }
+ servers := make([]*config.RpkiServer, 0, len(rsp.Servers))
+ for _, s := range rsp.Servers {
+ // Note: RpkiServerConfig.Port is uint32 type, but the TCP/UDP port is
+ // 16-bit length.
+ port, err := strconv.ParseUint(s.Conf.RemotePort, 10, 16)
+ if err != nil {
+ return nil, err
+ }
+ server := &config.RpkiServer{
+ Config: config.RpkiServerConfig{
+ Address: s.Conf.Address,
+ Port: uint32(port),
+ },
+ State: config.RpkiServerState{
+ Up: s.State.Up,
+ SerialNumber: s.State.Serial,
+ RecordsV4: s.State.RecordIpv4,
+ RecordsV6: s.State.RecordIpv6,
+ PrefixesV4: s.State.PrefixIpv4,
+ PrefixesV6: s.State.PrefixIpv6,
+ Uptime: s.State.Uptime,
+ Downtime: s.State.Downtime,
+ RpkiMessages: config.RpkiMessages{
+ RpkiReceived: config.RpkiReceived{
+ SerialNotify: s.State.SerialNotify,
+ CacheReset: s.State.CacheReset,
+ CacheResponse: s.State.CacheResponse,
+ Ipv4Prefix: s.State.ReceivedIpv4,
+ Ipv6Prefix: s.State.ReceivedIpv6,
+ EndOfData: s.State.EndOfData,
+ Error: s.State.Error,
+ },
+ RpkiSent: config.RpkiSent{
+ SerialQuery: s.State.SerialQuery,
+ ResetQuery: s.State.ResetQuery,
+ },
+ },
+ },
+ }
+ servers = append(servers, server)
+ }
+ return servers, nil
+}
+
+func (cli *Client) GetROA(family bgp.RouteFamily) ([]*api.Roa, error) {
+ rsp, err := cli.cli.GetRoa(context.Background(), &api.GetRoaRequest{
+ Family: uint32(family),
+ })
+ if err != nil {
+ return nil, err
+ }
+ return rsp.Roas, nil
+}
+
+func (cli *Client) AddRPKIServer(address string, port, lifetime int) error {
+ _, err := cli.cli.AddRpki(context.Background(), &api.AddRpkiRequest{
+ Address: address,
+ Port: uint32(port),
+ Lifetime: int64(lifetime),
+ })
+ return err
+}
+
+func (cli *Client) DeleteRPKIServer(address string) error {
+ _, err := cli.cli.DeleteRpki(context.Background(), &api.DeleteRpkiRequest{
+ Address: address,
+ })
+ return err
+}
+
+func (cli *Client) EnableRPKIServer(address string) error {
+ _, err := cli.cli.EnableRpki(context.Background(), &api.EnableRpkiRequest{
+ Address: address,
+ })
+ return err
+}
+
+func (cli *Client) DisableRPKIServer(address string) error {
+ _, err := cli.cli.DisableRpki(context.Background(), &api.DisableRpkiRequest{
+ Address: address,
+ })
+ return err
+}
+
+func (cli *Client) ResetRPKIServer(address string) error {
+ _, err := cli.cli.ResetRpki(context.Background(), &api.ResetRpkiRequest{
+ Address: address,
+ })
+ return err
+}
+
+func (cli *Client) SoftResetRPKIServer(address string) error {
+ _, err := cli.cli.SoftResetRpki(context.Background(), &api.SoftResetRpkiRequest{
+ Address: address,
+ })
+ return err
+}
+
+func (cli *Client) ValidateRIBWithRPKI(prefixes ...string) error {
+ req := &api.ValidateRibRequest{}
+ if len(prefixes) > 1 {
+ return fmt.Errorf("too many prefixes: %d", len(prefixes))
+ } else if len(prefixes) == 1 {
+ req.Prefix = prefixes[0]
+ }
+ _, err := cli.cli.ValidateRib(context.Background(), req)
+ return err
+}
+
+func (cli *Client) AddBMP(c *config.BmpServerConfig) error {
+ _, err := cli.cli.AddBmp(context.Background(), &api.AddBmpRequest{
+ Address: c.Address,
+ Port: c.Port,
+ Type: api.AddBmpRequest_MonitoringPolicy(c.RouteMonitoringPolicy.ToInt()),
+ })
+ return err
+}
+
+func (cli *Client) DeleteBMP(c *config.BmpServerConfig) error {
+ _, err := cli.cli.DeleteBmp(context.Background(), &api.DeleteBmpRequest{
+ Address: c.Address,
+ Port: c.Port,
+ })
+ return err
+}
+
+type MonitorRIBClient struct {
+ stream api.GobgpApi_MonitorRibClient
+}
+
+func (c *MonitorRIBClient) Recv() (*api.Destination, error) {
+ return c.stream.Recv()
+}
+
+func (cli *Client) MonitorRIB(family bgp.RouteFamily, current bool) (*MonitorRIBClient, error) {
+ stream, err := cli.cli.MonitorRib(context.Background(), &api.MonitorRibRequest{
+ Table: &api.Table{
+ Type: api.Resource_GLOBAL,
+ Family: uint32(family),
+ },
+ Current: current,
+ })
+ if err != nil {
+ return nil, err
+ }
+ return &MonitorRIBClient{stream}, nil
+}
+
+func (cli *Client) MonitorAdjRIBIn(name string, family bgp.RouteFamily, current bool) (*MonitorRIBClient, error) {
+ stream, err := cli.cli.MonitorRib(context.Background(), &api.MonitorRibRequest{
+ Table: &api.Table{
+ Type: api.Resource_ADJ_IN,
+ Name: name,
+ Family: uint32(family),
+ },
+ Current: current,
+ })
+ if err != nil {
+ return nil, err
+ }
+ return &MonitorRIBClient{stream}, nil
+}
+
+type MonitorNeighborStateClient struct {
+ stream api.GobgpApi_MonitorPeerStateClient
+}
+
+func (c *MonitorNeighborStateClient) Recv() (*config.Neighbor, error) {
+ p, err := c.stream.Recv()
+ if err != nil {
+ return nil, err
+ }
+ return server.NewNeighborFromAPIStruct(p)
+}
+
+func (cli *Client) MonitorNeighborState(name string, current bool) (*MonitorNeighborStateClient, error) {
+ stream, err := cli.cli.MonitorPeerState(context.Background(), &api.Arguments{
+ Name: name,
+ Current: current,
+ })
+ if err != nil {
+ return nil, err
+ }
+ return &MonitorNeighborStateClient{stream}, nil
+}
diff --git a/internal/pkg/client/client_test.go b/internal/pkg/client/client_test.go
new file mode 100644
index 00000000..a737172d
--- /dev/null
+++ b/internal/pkg/client/client_test.go
@@ -0,0 +1,55 @@
+// Copyright (C) 2016 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package client
+
+import (
+ "testing"
+ "time"
+
+ "github.com/osrg/gobgp/internal/pkg/config"
+ "github.com/osrg/gobgp/pkg/server"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestGetNeighbor(test *testing.T) {
+ assert := assert.New(test)
+ s := server.NewBgpServer()
+ go s.Serve()
+ g := server.NewGrpcServer(s, ":50051")
+ go g.Serve()
+ time.Sleep(time.Second)
+ cli, err := New("")
+ assert.Nil(err)
+ err = cli.StartServer(&config.Global{
+ Config: config.GlobalConfig{
+ As: 1,
+ RouterId: "1.1.1.1",
+ Port: 1790,
+ },
+ })
+ assert.Nil(err)
+ err = cli.AddNeighbor(&config.Neighbor{
+ Config: config.NeighborConfig{
+ NeighborAddress: "10.0.0.1",
+ PeerAs: 2,
+ },
+ })
+ assert.Nil(err)
+ _, err = cli.GetNeighbor("10.0.0.1")
+ assert.Nil(err)
+ _, err = cli.GetNeighbor("10.0.0.2")
+ assert.Equal(err.Error(), "not found neighbor 10.0.0.2")
+}
diff --git a/internal/pkg/config/bgp_configs.go b/internal/pkg/config/bgp_configs.go
new file mode 100644
index 00000000..5c3cff97
--- /dev/null
+++ b/internal/pkg/config/bgp_configs.go
@@ -0,0 +1,6335 @@
+// DO NOT EDIT
+// generated by pyang using OpenConfig https://github.com/openconfig/public
+//
+// Copyright (C) 2014-2016 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+ "fmt"
+
+ "github.com/osrg/gobgp/pkg/packet/bgp"
+)
+
+func mapkey(index int, name string) string {
+ if name != "" {
+ return name
+ }
+ return fmt.Sprintf("%v", index)
+}
+
+// typedef for typedef openconfig-types:std-regexp.
+type StdRegexp string
+
+// typedef for typedef openconfig-types:percentage.
+type Percentage uint8
+
+// typedef for typedef bgp-types:rr-cluster-id-type.
+type RrClusterIdType string
+
+// typedef for identity bgp-types:remove-private-as-option.
+// set of options for configuring how private AS path numbers
+// are removed from advertisements.
+type RemovePrivateAsOption string
+
+const (
+ REMOVE_PRIVATE_AS_OPTION_ALL RemovePrivateAsOption = "all"
+ REMOVE_PRIVATE_AS_OPTION_REPLACE RemovePrivateAsOption = "replace"
+)
+
+var RemovePrivateAsOptionToIntMap = map[RemovePrivateAsOption]int{
+ REMOVE_PRIVATE_AS_OPTION_ALL: 0,
+ REMOVE_PRIVATE_AS_OPTION_REPLACE: 1,
+}
+
+func (v RemovePrivateAsOption) ToInt() int {
+ i, ok := RemovePrivateAsOptionToIntMap[v]
+ if !ok {
+ return -1
+ }
+ return i
+}
+
+var IntToRemovePrivateAsOptionMap = map[int]RemovePrivateAsOption{
+ 0: REMOVE_PRIVATE_AS_OPTION_ALL,
+ 1: REMOVE_PRIVATE_AS_OPTION_REPLACE,
+}
+
+func (v RemovePrivateAsOption) Validate() error {
+ if _, ok := RemovePrivateAsOptionToIntMap[v]; !ok {
+ return fmt.Errorf("invalid RemovePrivateAsOption: %s", v)
+ }
+ return nil
+}
+
+// typedef for typedef bgp-types:bgp-community-regexp-type.
+type BgpCommunityRegexpType StdRegexp
+
+// typedef for identity bgp-types:community-type.
+// type describing variations of community attributes:
+// STANDARD: standard BGP community [rfc1997]
+// EXTENDED: extended BGP community [rfc4360]
+// BOTH: both standard and extended community.
+type CommunityType string
+
+const (
+ COMMUNITY_TYPE_STANDARD CommunityType = "standard"
+ COMMUNITY_TYPE_EXTENDED CommunityType = "extended"
+ COMMUNITY_TYPE_BOTH CommunityType = "both"
+ COMMUNITY_TYPE_NONE CommunityType = "none"
+)
+
+var CommunityTypeToIntMap = map[CommunityType]int{
+ COMMUNITY_TYPE_STANDARD: 0,
+ COMMUNITY_TYPE_EXTENDED: 1,
+ COMMUNITY_TYPE_BOTH: 2,
+ COMMUNITY_TYPE_NONE: 3,
+}
+
+func (v CommunityType) ToInt() int {
+ i, ok := CommunityTypeToIntMap[v]
+ if !ok {
+ return -1
+ }
+ return i
+}
+
+var IntToCommunityTypeMap = map[int]CommunityType{
+ 0: COMMUNITY_TYPE_STANDARD,
+ 1: COMMUNITY_TYPE_EXTENDED,
+ 2: COMMUNITY_TYPE_BOTH,
+ 3: COMMUNITY_TYPE_NONE,
+}
+
+func (v CommunityType) Validate() error {
+ if _, ok := CommunityTypeToIntMap[v]; !ok {
+ return fmt.Errorf("invalid CommunityType: %s", v)
+ }
+ return nil
+}
+
+// typedef for typedef bgp-types:bgp-ext-community-type.
+type BgpExtCommunityType string
+
+// typedef for typedef bgp-types:bgp-std-community-type.
+type BgpStdCommunityType string
+
+// typedef for identity bgp-types:peer-type.
+// labels a peer or peer group as explicitly internal or
+// external.
+type PeerType string
+
+const (
+ PEER_TYPE_INTERNAL PeerType = "internal"
+ PEER_TYPE_EXTERNAL PeerType = "external"
+)
+
+var PeerTypeToIntMap = map[PeerType]int{
+ PEER_TYPE_INTERNAL: 0,
+ PEER_TYPE_EXTERNAL: 1,
+}
+
+func (v PeerType) ToInt() int {
+ i, ok := PeerTypeToIntMap[v]
+ if !ok {
+ return -1
+ }
+ return i
+}
+
+var IntToPeerTypeMap = map[int]PeerType{
+ 0: PEER_TYPE_INTERNAL,
+ 1: PEER_TYPE_EXTERNAL,
+}
+
+func (v PeerType) Validate() error {
+ if _, ok := PeerTypeToIntMap[v]; !ok {
+ return fmt.Errorf("invalid PeerType: %s", v)
+ }
+ return nil
+}
+
+// typedef for identity bgp-types:bgp-session-direction.
+// Type to describe the direction of NLRI transmission.
+type BgpSessionDirection string
+
+const (
+ BGP_SESSION_DIRECTION_INBOUND BgpSessionDirection = "inbound"
+ BGP_SESSION_DIRECTION_OUTBOUND BgpSessionDirection = "outbound"
+)
+
+var BgpSessionDirectionToIntMap = map[BgpSessionDirection]int{
+ BGP_SESSION_DIRECTION_INBOUND: 0,
+ BGP_SESSION_DIRECTION_OUTBOUND: 1,
+}
+
+func (v BgpSessionDirection) ToInt() int {
+ i, ok := BgpSessionDirectionToIntMap[v]
+ if !ok {
+ return -1
+ }
+ return i
+}
+
+var IntToBgpSessionDirectionMap = map[int]BgpSessionDirection{
+ 0: BGP_SESSION_DIRECTION_INBOUND,
+ 1: BGP_SESSION_DIRECTION_OUTBOUND,
+}
+
+func (v BgpSessionDirection) Validate() error {
+ if _, ok := BgpSessionDirectionToIntMap[v]; !ok {
+ return fmt.Errorf("invalid BgpSessionDirection: %s", v)
+ }
+ return nil
+}
+
+// typedef for identity bgp-types:bgp-origin-attr-type.
+// Type definition for standard BGP origin attribute.
+type BgpOriginAttrType string
+
+const (
+ BGP_ORIGIN_ATTR_TYPE_IGP BgpOriginAttrType = "igp"
+ BGP_ORIGIN_ATTR_TYPE_EGP BgpOriginAttrType = "egp"
+ BGP_ORIGIN_ATTR_TYPE_INCOMPLETE BgpOriginAttrType = "incomplete"
+)
+
+var BgpOriginAttrTypeToIntMap = map[BgpOriginAttrType]int{
+ BGP_ORIGIN_ATTR_TYPE_IGP: 0,
+ BGP_ORIGIN_ATTR_TYPE_EGP: 1,
+ BGP_ORIGIN_ATTR_TYPE_INCOMPLETE: 2,
+}
+
+func (v BgpOriginAttrType) ToInt() int {
+ i, ok := BgpOriginAttrTypeToIntMap[v]
+ if !ok {
+ return -1
+ }
+ return i
+}
+
+var IntToBgpOriginAttrTypeMap = map[int]BgpOriginAttrType{
+ 0: BGP_ORIGIN_ATTR_TYPE_IGP,
+ 1: BGP_ORIGIN_ATTR_TYPE_EGP,
+ 2: BGP_ORIGIN_ATTR_TYPE_INCOMPLETE,
+}
+
+func (v BgpOriginAttrType) Validate() error {
+ if _, ok := BgpOriginAttrTypeToIntMap[v]; !ok {
+ return fmt.Errorf("invalid BgpOriginAttrType: %s", v)
+ }
+ return nil
+}
+
+// typedef for identity bgp-types:afi-safi-type.
+// Base identity type for AFI,SAFI tuples for BGP-4.
+type AfiSafiType string
+
+const (
+ AFI_SAFI_TYPE_IPV4_UNICAST AfiSafiType = "ipv4-unicast"
+ AFI_SAFI_TYPE_IPV6_UNICAST AfiSafiType = "ipv6-unicast"
+ AFI_SAFI_TYPE_IPV4_LABELLED_UNICAST AfiSafiType = "ipv4-labelled-unicast"
+ AFI_SAFI_TYPE_IPV6_LABELLED_UNICAST AfiSafiType = "ipv6-labelled-unicast"
+ AFI_SAFI_TYPE_L3VPN_IPV4_UNICAST AfiSafiType = "l3vpn-ipv4-unicast"
+ AFI_SAFI_TYPE_L3VPN_IPV6_UNICAST AfiSafiType = "l3vpn-ipv6-unicast"
+ AFI_SAFI_TYPE_L3VPN_IPV4_MULTICAST AfiSafiType = "l3vpn-ipv4-multicast"
+ AFI_SAFI_TYPE_L3VPN_IPV6_MULTICAST AfiSafiType = "l3vpn-ipv6-multicast"
+ AFI_SAFI_TYPE_L2VPN_VPLS AfiSafiType = "l2vpn-vpls"
+ AFI_SAFI_TYPE_L2VPN_EVPN AfiSafiType = "l2vpn-evpn"
+ AFI_SAFI_TYPE_IPV4_MULTICAST AfiSafiType = "ipv4-multicast"
+ AFI_SAFI_TYPE_IPV6_MULTICAST AfiSafiType = "ipv6-multicast"
+ AFI_SAFI_TYPE_RTC AfiSafiType = "rtc"
+ AFI_SAFI_TYPE_IPV4_ENCAP AfiSafiType = "ipv4-encap"
+ AFI_SAFI_TYPE_IPV6_ENCAP AfiSafiType = "ipv6-encap"
+ AFI_SAFI_TYPE_IPV4_FLOWSPEC AfiSafiType = "ipv4-flowspec"
+ AFI_SAFI_TYPE_L3VPN_IPV4_FLOWSPEC AfiSafiType = "l3vpn-ipv4-flowspec"
+ AFI_SAFI_TYPE_IPV6_FLOWSPEC AfiSafiType = "ipv6-flowspec"
+ AFI_SAFI_TYPE_L3VPN_IPV6_FLOWSPEC AfiSafiType = "l3vpn-ipv6-flowspec"
+ AFI_SAFI_TYPE_L2VPN_FLOWSPEC AfiSafiType = "l2vpn-flowspec"
+ AFI_SAFI_TYPE_OPAQUE AfiSafiType = "opaque"
+)
+
+var AfiSafiTypeToIntMap = map[AfiSafiType]int{
+ AFI_SAFI_TYPE_IPV4_UNICAST: 0,
+ AFI_SAFI_TYPE_IPV6_UNICAST: 1,
+ AFI_SAFI_TYPE_IPV4_LABELLED_UNICAST: 2,
+ AFI_SAFI_TYPE_IPV6_LABELLED_UNICAST: 3,
+ AFI_SAFI_TYPE_L3VPN_IPV4_UNICAST: 4,
+ AFI_SAFI_TYPE_L3VPN_IPV6_UNICAST: 5,
+ AFI_SAFI_TYPE_L3VPN_IPV4_MULTICAST: 6,
+ AFI_SAFI_TYPE_L3VPN_IPV6_MULTICAST: 7,
+ AFI_SAFI_TYPE_L2VPN_VPLS: 8,
+ AFI_SAFI_TYPE_L2VPN_EVPN: 9,
+ AFI_SAFI_TYPE_IPV4_MULTICAST: 10,
+ AFI_SAFI_TYPE_IPV6_MULTICAST: 11,
+ AFI_SAFI_TYPE_RTC: 12,
+ AFI_SAFI_TYPE_IPV4_ENCAP: 13,
+ AFI_SAFI_TYPE_IPV6_ENCAP: 14,
+ AFI_SAFI_TYPE_IPV4_FLOWSPEC: 15,
+ AFI_SAFI_TYPE_L3VPN_IPV4_FLOWSPEC: 16,
+ AFI_SAFI_TYPE_IPV6_FLOWSPEC: 17,
+ AFI_SAFI_TYPE_L3VPN_IPV6_FLOWSPEC: 18,
+ AFI_SAFI_TYPE_L2VPN_FLOWSPEC: 19,
+ AFI_SAFI_TYPE_OPAQUE: 20,
+}
+
+func (v AfiSafiType) ToInt() int {
+ i, ok := AfiSafiTypeToIntMap[v]
+ if !ok {
+ return -1
+ }
+ return i
+}
+
+var IntToAfiSafiTypeMap = map[int]AfiSafiType{
+ 0: AFI_SAFI_TYPE_IPV4_UNICAST,
+ 1: AFI_SAFI_TYPE_IPV6_UNICAST,
+ 2: AFI_SAFI_TYPE_IPV4_LABELLED_UNICAST,
+ 3: AFI_SAFI_TYPE_IPV6_LABELLED_UNICAST,
+ 4: AFI_SAFI_TYPE_L3VPN_IPV4_UNICAST,
+ 5: AFI_SAFI_TYPE_L3VPN_IPV6_UNICAST,
+ 6: AFI_SAFI_TYPE_L3VPN_IPV4_MULTICAST,
+ 7: AFI_SAFI_TYPE_L3VPN_IPV6_MULTICAST,
+ 8: AFI_SAFI_TYPE_L2VPN_VPLS,
+ 9: AFI_SAFI_TYPE_L2VPN_EVPN,
+ 10: AFI_SAFI_TYPE_IPV4_MULTICAST,
+ 11: AFI_SAFI_TYPE_IPV6_MULTICAST,
+ 12: AFI_SAFI_TYPE_RTC,
+ 13: AFI_SAFI_TYPE_IPV4_ENCAP,
+ 14: AFI_SAFI_TYPE_IPV6_ENCAP,
+ 15: AFI_SAFI_TYPE_IPV4_FLOWSPEC,
+ 16: AFI_SAFI_TYPE_L3VPN_IPV4_FLOWSPEC,
+ 17: AFI_SAFI_TYPE_IPV6_FLOWSPEC,
+ 18: AFI_SAFI_TYPE_L3VPN_IPV6_FLOWSPEC,
+ 19: AFI_SAFI_TYPE_L2VPN_FLOWSPEC,
+ 20: AFI_SAFI_TYPE_OPAQUE,
+}
+
+func (v AfiSafiType) Validate() error {
+ if _, ok := AfiSafiTypeToIntMap[v]; !ok {
+ return fmt.Errorf("invalid AfiSafiType: %s", v)
+ }
+ return nil
+}
+
+// typedef for identity bgp-types:bgp-capability.
+// Base identity for a BGP capability.
+type BgpCapability string
+
+const (
+ BGP_CAPABILITY_MPBGP BgpCapability = "mpbgp"
+ BGP_CAPABILITY_ROUTE_REFRESH BgpCapability = "route-refresh"
+ BGP_CAPABILITY_ASN32 BgpCapability = "asn32"
+ BGP_CAPABILITY_GRACEFUL_RESTART BgpCapability = "graceful-restart"
+ BGP_CAPABILITY_ADD_PATHS BgpCapability = "add-paths"
+)
+
+var BgpCapabilityToIntMap = map[BgpCapability]int{
+ BGP_CAPABILITY_MPBGP: 0,
+ BGP_CAPABILITY_ROUTE_REFRESH: 1,
+ BGP_CAPABILITY_ASN32: 2,
+ BGP_CAPABILITY_GRACEFUL_RESTART: 3,
+ BGP_CAPABILITY_ADD_PATHS: 4,
+}
+
+func (v BgpCapability) ToInt() int {
+ i, ok := BgpCapabilityToIntMap[v]
+ if !ok {
+ return -1
+ }
+ return i
+}
+
+var IntToBgpCapabilityMap = map[int]BgpCapability{
+ 0: BGP_CAPABILITY_MPBGP,
+ 1: BGP_CAPABILITY_ROUTE_REFRESH,
+ 2: BGP_CAPABILITY_ASN32,
+ 3: BGP_CAPABILITY_GRACEFUL_RESTART,
+ 4: BGP_CAPABILITY_ADD_PATHS,
+}
+
+func (v BgpCapability) Validate() error {
+ if _, ok := BgpCapabilityToIntMap[v]; !ok {
+ return fmt.Errorf("invalid BgpCapability: %s", v)
+ }
+ return nil
+}
+
+// typedef for identity bgp-types:bgp-well-known-std-community.
+// Reserved communities within the standard community space
+// defined by RFC1997. These communities must fall within the
+// range 0x00000000 to 0xFFFFFFFF.
+type BgpWellKnownStdCommunity string
+
+const (
+ BGP_WELL_KNOWN_STD_COMMUNITY_NO_EXPORT BgpWellKnownStdCommunity = "no_export"
+ BGP_WELL_KNOWN_STD_COMMUNITY_NO_ADVERTISE BgpWellKnownStdCommunity = "no_advertise"
+ BGP_WELL_KNOWN_STD_COMMUNITY_NO_EXPORT_SUBCONFED BgpWellKnownStdCommunity = "no_export_subconfed"
+ BGP_WELL_KNOWN_STD_COMMUNITY_NOPEER BgpWellKnownStdCommunity = "nopeer"
+)
+
+var BgpWellKnownStdCommunityToIntMap = map[BgpWellKnownStdCommunity]int{
+ BGP_WELL_KNOWN_STD_COMMUNITY_NO_EXPORT: 0,
+ BGP_WELL_KNOWN_STD_COMMUNITY_NO_ADVERTISE: 1,
+ BGP_WELL_KNOWN_STD_COMMUNITY_NO_EXPORT_SUBCONFED: 2,
+ BGP_WELL_KNOWN_STD_COMMUNITY_NOPEER: 3,
+}
+
+func (v BgpWellKnownStdCommunity) ToInt() int {
+ i, ok := BgpWellKnownStdCommunityToIntMap[v]
+ if !ok {
+ return -1
+ }
+ return i
+}
+
+var IntToBgpWellKnownStdCommunityMap = map[int]BgpWellKnownStdCommunity{
+ 0: BGP_WELL_KNOWN_STD_COMMUNITY_NO_EXPORT,
+ 1: BGP_WELL_KNOWN_STD_COMMUNITY_NO_ADVERTISE,
+ 2: BGP_WELL_KNOWN_STD_COMMUNITY_NO_EXPORT_SUBCONFED,
+ 3: BGP_WELL_KNOWN_STD_COMMUNITY_NOPEER,
+}
+
+func (v BgpWellKnownStdCommunity) Validate() error {
+ if _, ok := BgpWellKnownStdCommunityToIntMap[v]; !ok {
+ return fmt.Errorf("invalid BgpWellKnownStdCommunity: %s", v)
+ }
+ return nil
+}
+
+// typedef for identity ptypes:match-set-options-restricted-type.
+// Options that govern the behavior of a match statement. The
+// default behavior is ANY, i.e., the given value matches any
+// of the members of the defined set. Note this type is a
+// restricted version of the match-set-options-type.
+type MatchSetOptionsRestrictedType string
+
+const (
+ MATCH_SET_OPTIONS_RESTRICTED_TYPE_ANY MatchSetOptionsRestrictedType = "any"
+ MATCH_SET_OPTIONS_RESTRICTED_TYPE_INVERT MatchSetOptionsRestrictedType = "invert"
+)
+
+var MatchSetOptionsRestrictedTypeToIntMap = map[MatchSetOptionsRestrictedType]int{
+ MATCH_SET_OPTIONS_RESTRICTED_TYPE_ANY: 0,
+ MATCH_SET_OPTIONS_RESTRICTED_TYPE_INVERT: 1,
+}
+
+func (v MatchSetOptionsRestrictedType) ToInt() int {
+ i, ok := MatchSetOptionsRestrictedTypeToIntMap[v]
+ if !ok {
+ return -1
+ }
+ return i
+}
+
+var IntToMatchSetOptionsRestrictedTypeMap = map[int]MatchSetOptionsRestrictedType{
+ 0: MATCH_SET_OPTIONS_RESTRICTED_TYPE_ANY,
+ 1: MATCH_SET_OPTIONS_RESTRICTED_TYPE_INVERT,
+}
+
+func (v MatchSetOptionsRestrictedType) Validate() error {
+ if _, ok := MatchSetOptionsRestrictedTypeToIntMap[v]; !ok {
+ return fmt.Errorf("invalid MatchSetOptionsRestrictedType: %s", v)
+ }
+ return nil
+}
+
+func (v MatchSetOptionsRestrictedType) Default() MatchSetOptionsRestrictedType {
+ return MATCH_SET_OPTIONS_RESTRICTED_TYPE_ANY
+}
+
+func (v MatchSetOptionsRestrictedType) DefaultAsNeeded() MatchSetOptionsRestrictedType {
+ if string(v) == "" {
+ return v.Default()
+ }
+ return v
+}
+
+// typedef for identity ptypes:match-set-options-type.
+// Options that govern the behavior of a match statement. The
+// default behavior is ANY, i.e., the given value matches any
+// of the members of the defined set.
+type MatchSetOptionsType string
+
+const (
+ MATCH_SET_OPTIONS_TYPE_ANY MatchSetOptionsType = "any"
+ MATCH_SET_OPTIONS_TYPE_ALL MatchSetOptionsType = "all"
+ MATCH_SET_OPTIONS_TYPE_INVERT MatchSetOptionsType = "invert"
+)
+
+var MatchSetOptionsTypeToIntMap = map[MatchSetOptionsType]int{
+ MATCH_SET_OPTIONS_TYPE_ANY: 0,
+ MATCH_SET_OPTIONS_TYPE_ALL: 1,
+ MATCH_SET_OPTIONS_TYPE_INVERT: 2,
+}
+
+func (v MatchSetOptionsType) ToInt() int {
+ i, ok := MatchSetOptionsTypeToIntMap[v]
+ if !ok {
+ return -1
+ }
+ return i
+}
+
+var IntToMatchSetOptionsTypeMap = map[int]MatchSetOptionsType{
+ 0: MATCH_SET_OPTIONS_TYPE_ANY,
+ 1: MATCH_SET_OPTIONS_TYPE_ALL,
+ 2: MATCH_SET_OPTIONS_TYPE_INVERT,
+}
+
+func (v MatchSetOptionsType) Validate() error {
+ if _, ok := MatchSetOptionsTypeToIntMap[v]; !ok {
+ return fmt.Errorf("invalid MatchSetOptionsType: %s", v)
+ }
+ return nil
+}
+
+func (v MatchSetOptionsType) Default() MatchSetOptionsType {
+ return MATCH_SET_OPTIONS_TYPE_ANY
+}
+
+func (v MatchSetOptionsType) DefaultAsNeeded() MatchSetOptionsType {
+ if string(v) == "" {
+ return v.Default()
+ }
+ return v
+}
+
+// typedef for typedef ptypes:tag-type.
+type TagType string
+
+// typedef for identity ptypes:install-protocol-type.
+// Base type for protocols which can install prefixes into the
+// RIB.
+type InstallProtocolType string
+
+const (
+ INSTALL_PROTOCOL_TYPE_BGP InstallProtocolType = "bgp"
+ INSTALL_PROTOCOL_TYPE_ISIS InstallProtocolType = "isis"
+ INSTALL_PROTOCOL_TYPE_OSPF InstallProtocolType = "ospf"
+ INSTALL_PROTOCOL_TYPE_OSPF3 InstallProtocolType = "ospf3"
+ INSTALL_PROTOCOL_TYPE_STATIC InstallProtocolType = "static"
+ INSTALL_PROTOCOL_TYPE_DIRECTLY_CONNECTED InstallProtocolType = "directly-connected"
+ INSTALL_PROTOCOL_TYPE_LOCAL_AGGREGATE InstallProtocolType = "local-aggregate"
+)
+
+var InstallProtocolTypeToIntMap = map[InstallProtocolType]int{
+ INSTALL_PROTOCOL_TYPE_BGP: 0,
+ INSTALL_PROTOCOL_TYPE_ISIS: 1,
+ INSTALL_PROTOCOL_TYPE_OSPF: 2,
+ INSTALL_PROTOCOL_TYPE_OSPF3: 3,
+ INSTALL_PROTOCOL_TYPE_STATIC: 4,
+ INSTALL_PROTOCOL_TYPE_DIRECTLY_CONNECTED: 5,
+ INSTALL_PROTOCOL_TYPE_LOCAL_AGGREGATE: 6,
+}
+
+func (v InstallProtocolType) ToInt() int {
+ i, ok := InstallProtocolTypeToIntMap[v]
+ if !ok {
+ return -1
+ }
+ return i
+}
+
+var IntToInstallProtocolTypeMap = map[int]InstallProtocolType{
+ 0: INSTALL_PROTOCOL_TYPE_BGP,
+ 1: INSTALL_PROTOCOL_TYPE_ISIS,
+ 2: INSTALL_PROTOCOL_TYPE_OSPF,
+ 3: INSTALL_PROTOCOL_TYPE_OSPF3,
+ 4: INSTALL_PROTOCOL_TYPE_STATIC,
+ 5: INSTALL_PROTOCOL_TYPE_DIRECTLY_CONNECTED,
+ 6: INSTALL_PROTOCOL_TYPE_LOCAL_AGGREGATE,
+}
+
+func (v InstallProtocolType) Validate() error {
+ if _, ok := InstallProtocolTypeToIntMap[v]; !ok {
+ return fmt.Errorf("invalid InstallProtocolType: %s", v)
+ }
+ return nil
+}
+
+// typedef for identity ptypes:attribute-comparison.
+// base type for supported comparison operators on route
+// attributes.
+type AttributeComparison string
+
+const (
+ ATTRIBUTE_COMPARISON_ATTRIBUTE_EQ AttributeComparison = "attribute-eq"
+ ATTRIBUTE_COMPARISON_ATTRIBUTE_GE AttributeComparison = "attribute-ge"
+ ATTRIBUTE_COMPARISON_ATTRIBUTE_LE AttributeComparison = "attribute-le"
+ ATTRIBUTE_COMPARISON_EQ AttributeComparison = "eq"
+ ATTRIBUTE_COMPARISON_GE AttributeComparison = "ge"
+ ATTRIBUTE_COMPARISON_LE AttributeComparison = "le"
+)
+
+var AttributeComparisonToIntMap = map[AttributeComparison]int{
+ ATTRIBUTE_COMPARISON_ATTRIBUTE_EQ: 0,
+ ATTRIBUTE_COMPARISON_ATTRIBUTE_GE: 1,
+ ATTRIBUTE_COMPARISON_ATTRIBUTE_LE: 2,
+ ATTRIBUTE_COMPARISON_EQ: 3,
+ ATTRIBUTE_COMPARISON_GE: 4,
+ ATTRIBUTE_COMPARISON_LE: 5,
+}
+
+func (v AttributeComparison) ToInt() int {
+ i, ok := AttributeComparisonToIntMap[v]
+ if !ok {
+ return -1
+ }
+ return i
+}
+
+var IntToAttributeComparisonMap = map[int]AttributeComparison{
+ 0: ATTRIBUTE_COMPARISON_ATTRIBUTE_EQ,
+ 1: ATTRIBUTE_COMPARISON_ATTRIBUTE_GE,
+ 2: ATTRIBUTE_COMPARISON_ATTRIBUTE_LE,
+ 3: ATTRIBUTE_COMPARISON_EQ,
+ 4: ATTRIBUTE_COMPARISON_GE,
+ 5: ATTRIBUTE_COMPARISON_LE,
+}
+
+func (v AttributeComparison) Validate() error {
+ if _, ok := AttributeComparisonToIntMap[v]; !ok {
+ return fmt.Errorf("invalid AttributeComparison: %s", v)
+ }
+ return nil
+}
+
+// typedef for identity rpol:route-disposition.
+// Select the final disposition for the route, either
+// accept or reject.
+type RouteDisposition string
+
+const (
+ ROUTE_DISPOSITION_NONE RouteDisposition = "none"
+ ROUTE_DISPOSITION_ACCEPT_ROUTE RouteDisposition = "accept-route"
+ ROUTE_DISPOSITION_REJECT_ROUTE RouteDisposition = "reject-route"
+)
+
+var RouteDispositionToIntMap = map[RouteDisposition]int{
+ ROUTE_DISPOSITION_NONE: 0,
+ ROUTE_DISPOSITION_ACCEPT_ROUTE: 1,
+ ROUTE_DISPOSITION_REJECT_ROUTE: 2,
+}
+
+func (v RouteDisposition) ToInt() int {
+ i, ok := RouteDispositionToIntMap[v]
+ if !ok {
+ return -1
+ }
+ return i
+}
+
+var IntToRouteDispositionMap = map[int]RouteDisposition{
+ 0: ROUTE_DISPOSITION_NONE,
+ 1: ROUTE_DISPOSITION_ACCEPT_ROUTE,
+ 2: ROUTE_DISPOSITION_REJECT_ROUTE,
+}
+
+func (v RouteDisposition) Validate() error {
+ if _, ok := RouteDispositionToIntMap[v]; !ok {
+ return fmt.Errorf("invalid RouteDisposition: %s", v)
+ }
+ return nil
+}
+
+// typedef for identity rpol:route-type.
+// Condition to check the route type in the route update.
+type RouteType string
+
+const (
+ ROUTE_TYPE_NONE RouteType = "none"
+ ROUTE_TYPE_INTERNAL RouteType = "internal"
+ ROUTE_TYPE_EXTERNAL RouteType = "external"
+ ROUTE_TYPE_LOCAL RouteType = "local"
+)
+
+var RouteTypeToIntMap = map[RouteType]int{
+ ROUTE_TYPE_NONE: 0,
+ ROUTE_TYPE_INTERNAL: 1,
+ ROUTE_TYPE_EXTERNAL: 2,
+ ROUTE_TYPE_LOCAL: 3,
+}
+
+func (v RouteType) ToInt() int {
+ i, ok := RouteTypeToIntMap[v]
+ if !ok {
+ return -1
+ }
+ return i
+}
+
+var IntToRouteTypeMap = map[int]RouteType{
+ 0: ROUTE_TYPE_NONE,
+ 1: ROUTE_TYPE_INTERNAL,
+ 2: ROUTE_TYPE_EXTERNAL,
+ 3: ROUTE_TYPE_LOCAL,
+}
+
+func (v RouteType) Validate() error {
+ if _, ok := RouteTypeToIntMap[v]; !ok {
+ return fmt.Errorf("invalid RouteType: %s", v)
+ }
+ return nil
+}
+
+// typedef for identity rpol:default-policy-type.
+// type used to specify default route disposition in
+// a policy chain.
+type DefaultPolicyType string
+
+const (
+ DEFAULT_POLICY_TYPE_ACCEPT_ROUTE DefaultPolicyType = "accept-route"
+ DEFAULT_POLICY_TYPE_REJECT_ROUTE DefaultPolicyType = "reject-route"
+)
+
+var DefaultPolicyTypeToIntMap = map[DefaultPolicyType]int{
+ DEFAULT_POLICY_TYPE_ACCEPT_ROUTE: 0,
+ DEFAULT_POLICY_TYPE_REJECT_ROUTE: 1,
+}
+
+func (v DefaultPolicyType) ToInt() int {
+ i, ok := DefaultPolicyTypeToIntMap[v]
+ if !ok {
+ return -1
+ }
+ return i
+}
+
+var IntToDefaultPolicyTypeMap = map[int]DefaultPolicyType{
+ 0: DEFAULT_POLICY_TYPE_ACCEPT_ROUTE,
+ 1: DEFAULT_POLICY_TYPE_REJECT_ROUTE,
+}
+
+func (v DefaultPolicyType) Validate() error {
+ if _, ok := DefaultPolicyTypeToIntMap[v]; !ok {
+ return fmt.Errorf("invalid DefaultPolicyType: %s", v)
+ }
+ return nil
+}
+
+// typedef for identity bgp:session-state.
+// Operational state of the BGP peer.
+type SessionState string
+
+const (
+ SESSION_STATE_IDLE SessionState = "idle"
+ SESSION_STATE_CONNECT SessionState = "connect"
+ SESSION_STATE_ACTIVE SessionState = "active"
+ SESSION_STATE_OPENSENT SessionState = "opensent"
+ SESSION_STATE_OPENCONFIRM SessionState = "openconfirm"
+ SESSION_STATE_ESTABLISHED SessionState = "established"
+)
+
+var SessionStateToIntMap = map[SessionState]int{
+ SESSION_STATE_IDLE: 0,
+ SESSION_STATE_CONNECT: 1,
+ SESSION_STATE_ACTIVE: 2,
+ SESSION_STATE_OPENSENT: 3,
+ SESSION_STATE_OPENCONFIRM: 4,
+ SESSION_STATE_ESTABLISHED: 5,
+}
+
+func (v SessionState) ToInt() int {
+ i, ok := SessionStateToIntMap[v]
+ if !ok {
+ return -1
+ }
+ return i
+}
+
+var IntToSessionStateMap = map[int]SessionState{
+ 0: SESSION_STATE_IDLE,
+ 1: SESSION_STATE_CONNECT,
+ 2: SESSION_STATE_ACTIVE,
+ 3: SESSION_STATE_OPENSENT,
+ 4: SESSION_STATE_OPENCONFIRM,
+ 5: SESSION_STATE_ESTABLISHED,
+}
+
+func (v SessionState) Validate() error {
+ if _, ok := SessionStateToIntMap[v]; !ok {
+ return fmt.Errorf("invalid SessionState: %s", v)
+ }
+ return nil
+}
+
+// typedef for identity bgp:admin-state.
+type AdminState string
+
+const (
+ ADMIN_STATE_UP AdminState = "up"
+ ADMIN_STATE_DOWN AdminState = "down"
+ ADMIN_STATE_PFX_CT AdminState = "pfx_ct"
+)
+
+var AdminStateToIntMap = map[AdminState]int{
+ ADMIN_STATE_UP: 0,
+ ADMIN_STATE_DOWN: 1,
+ ADMIN_STATE_PFX_CT: 2,
+}
+
+func (v AdminState) ToInt() int {
+ i, ok := AdminStateToIntMap[v]
+ if !ok {
+ return -1
+ }
+ return i
+}
+
+var IntToAdminStateMap = map[int]AdminState{
+ 0: ADMIN_STATE_UP,
+ 1: ADMIN_STATE_DOWN,
+ 2: ADMIN_STATE_PFX_CT,
+}
+
+func (v AdminState) Validate() error {
+ if _, ok := AdminStateToIntMap[v]; !ok {
+ return fmt.Errorf("invalid AdminState: %s", v)
+ }
+ return nil
+}
+
+// typedef for identity bgp:mode.
+// Ths leaf indicates the mode of operation of BGP graceful
+// restart with the peer.
+type Mode string
+
+const (
+ MODE_HELPER_ONLY Mode = "helper-only"
+ MODE_BILATERAL Mode = "bilateral"
+ MODE_REMOTE_HELPER Mode = "remote-helper"
+)
+
+var ModeToIntMap = map[Mode]int{
+ MODE_HELPER_ONLY: 0,
+ MODE_BILATERAL: 1,
+ MODE_REMOTE_HELPER: 2,
+}
+
+func (v Mode) ToInt() int {
+ i, ok := ModeToIntMap[v]
+ if !ok {
+ return -1
+ }
+ return i
+}
+
+var IntToModeMap = map[int]Mode{
+ 0: MODE_HELPER_ONLY,
+ 1: MODE_BILATERAL,
+ 2: MODE_REMOTE_HELPER,
+}
+
+func (v Mode) Validate() error {
+ if _, ok := ModeToIntMap[v]; !ok {
+ return fmt.Errorf("invalid Mode: %s", v)
+ }
+ return nil
+}
+
+// typedef for typedef bgp-pol:bgp-next-hop-type.
+type BgpNextHopType string
+
+// typedef for typedef bgp-pol:bgp-as-path-prepend-repeat.
+type BgpAsPathPrependRepeat uint8
+
+// typedef for typedef bgp-pol:bgp-set-med-type.
+type BgpSetMedType string
+
+// typedef for identity bgp-pol:bgp-set-community-option-type.
+// Type definition for options when setting the community
+// attribute in a policy action.
+type BgpSetCommunityOptionType string
+
+const (
+ BGP_SET_COMMUNITY_OPTION_TYPE_ADD BgpSetCommunityOptionType = "add"
+ BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE BgpSetCommunityOptionType = "remove"
+ BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE BgpSetCommunityOptionType = "replace"
+)
+
+var BgpSetCommunityOptionTypeToIntMap = map[BgpSetCommunityOptionType]int{
+ BGP_SET_COMMUNITY_OPTION_TYPE_ADD: 0,
+ BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE: 1,
+ BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE: 2,
+}
+
+func (v BgpSetCommunityOptionType) ToInt() int {
+ i, ok := BgpSetCommunityOptionTypeToIntMap[v]
+ if !ok {
+ return -1
+ }
+ return i
+}
+
+var IntToBgpSetCommunityOptionTypeMap = map[int]BgpSetCommunityOptionType{
+ 0: BGP_SET_COMMUNITY_OPTION_TYPE_ADD,
+ 1: BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE,
+ 2: BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE,
+}
+
+func (v BgpSetCommunityOptionType) Validate() error {
+ if _, ok := BgpSetCommunityOptionTypeToIntMap[v]; !ok {
+ return fmt.Errorf("invalid BgpSetCommunityOptionType: %s", v)
+ }
+ return nil
+}
+
+// typedef for identity gobgp:bmp-route-monitoring-policy-type.
+type BmpRouteMonitoringPolicyType string
+
+const (
+ BMP_ROUTE_MONITORING_POLICY_TYPE_PRE_POLICY BmpRouteMonitoringPolicyType = "pre-policy"
+ BMP_ROUTE_MONITORING_POLICY_TYPE_POST_POLICY BmpRouteMonitoringPolicyType = "post-policy"
+ BMP_ROUTE_MONITORING_POLICY_TYPE_BOTH BmpRouteMonitoringPolicyType = "both"
+ BMP_ROUTE_MONITORING_POLICY_TYPE_LOCAL_RIB BmpRouteMonitoringPolicyType = "local-rib"
+ BMP_ROUTE_MONITORING_POLICY_TYPE_ALL BmpRouteMonitoringPolicyType = "all"
+)
+
+var BmpRouteMonitoringPolicyTypeToIntMap = map[BmpRouteMonitoringPolicyType]int{
+ BMP_ROUTE_MONITORING_POLICY_TYPE_PRE_POLICY: 0,
+ BMP_ROUTE_MONITORING_POLICY_TYPE_POST_POLICY: 1,
+ BMP_ROUTE_MONITORING_POLICY_TYPE_BOTH: 2,
+ BMP_ROUTE_MONITORING_POLICY_TYPE_LOCAL_RIB: 3,
+ BMP_ROUTE_MONITORING_POLICY_TYPE_ALL: 4,
+}
+
+func (v BmpRouteMonitoringPolicyType) ToInt() int {
+ i, ok := BmpRouteMonitoringPolicyTypeToIntMap[v]
+ if !ok {
+ return -1
+ }
+ return i
+}
+
+var IntToBmpRouteMonitoringPolicyTypeMap = map[int]BmpRouteMonitoringPolicyType{
+ 0: BMP_ROUTE_MONITORING_POLICY_TYPE_PRE_POLICY,
+ 1: BMP_ROUTE_MONITORING_POLICY_TYPE_POST_POLICY,
+ 2: BMP_ROUTE_MONITORING_POLICY_TYPE_BOTH,
+ 3: BMP_ROUTE_MONITORING_POLICY_TYPE_LOCAL_RIB,
+ 4: BMP_ROUTE_MONITORING_POLICY_TYPE_ALL,
+}
+
+func (v BmpRouteMonitoringPolicyType) Validate() error {
+ if _, ok := BmpRouteMonitoringPolicyTypeToIntMap[v]; !ok {
+ return fmt.Errorf("invalid BmpRouteMonitoringPolicyType: %s", v)
+ }
+ return nil
+}
+
+// typedef for identity gobgp:mrt-type.
+type MrtType string
+
+const (
+ MRT_TYPE_UPDATES MrtType = "updates"
+ MRT_TYPE_TABLE MrtType = "table"
+)
+
+var MrtTypeToIntMap = map[MrtType]int{
+ MRT_TYPE_UPDATES: 0,
+ MRT_TYPE_TABLE: 1,
+}
+
+func (v MrtType) ToInt() int {
+ i, ok := MrtTypeToIntMap[v]
+ if !ok {
+ return -1
+ }
+ return i
+}
+
+var IntToMrtTypeMap = map[int]MrtType{
+ 0: MRT_TYPE_UPDATES,
+ 1: MRT_TYPE_TABLE,
+}
+
+func (v MrtType) Validate() error {
+ if _, ok := MrtTypeToIntMap[v]; !ok {
+ return fmt.Errorf("invalid MrtType: %s", v)
+ }
+ return nil
+}
+
+// typedef for identity gobgp:rpki-validation-result-type.
+// indicate the validation result of RPKI based on ROA.
+type RpkiValidationResultType string
+
+const (
+ RPKI_VALIDATION_RESULT_TYPE_NONE RpkiValidationResultType = "none"
+ RPKI_VALIDATION_RESULT_TYPE_NOT_FOUND RpkiValidationResultType = "not-found"
+ RPKI_VALIDATION_RESULT_TYPE_VALID RpkiValidationResultType = "valid"
+ RPKI_VALIDATION_RESULT_TYPE_INVALID RpkiValidationResultType = "invalid"
+)
+
+var RpkiValidationResultTypeToIntMap = map[RpkiValidationResultType]int{
+ RPKI_VALIDATION_RESULT_TYPE_NONE: 0,
+ RPKI_VALIDATION_RESULT_TYPE_NOT_FOUND: 1,
+ RPKI_VALIDATION_RESULT_TYPE_VALID: 2,
+ RPKI_VALIDATION_RESULT_TYPE_INVALID: 3,
+}
+
+func (v RpkiValidationResultType) ToInt() int {
+ i, ok := RpkiValidationResultTypeToIntMap[v]
+ if !ok {
+ return -1
+ }
+ return i
+}
+
+var IntToRpkiValidationResultTypeMap = map[int]RpkiValidationResultType{
+ 0: RPKI_VALIDATION_RESULT_TYPE_NONE,
+ 1: RPKI_VALIDATION_RESULT_TYPE_NOT_FOUND,
+ 2: RPKI_VALIDATION_RESULT_TYPE_VALID,
+ 3: RPKI_VALIDATION_RESULT_TYPE_INVALID,
+}
+
+func (v RpkiValidationResultType) Validate() error {
+ if _, ok := RpkiValidationResultTypeToIntMap[v]; !ok {
+ return fmt.Errorf("invalid RpkiValidationResultType: %s", v)
+ }
+ return nil
+}
+
+// struct for container gobgp:state.
+type DynamicNeighborState struct {
+ // original -> gobgp:prefix
+ Prefix string `mapstructure:"prefix" json:"prefix,omitempty"`
+ // original -> gobgp:peer-group
+ PeerGroup string `mapstructure:"peer-group" json:"peer-group,omitempty"`
+}
+
+// struct for container gobgp:config.
+type DynamicNeighborConfig struct {
+ // original -> gobgp:prefix
+ Prefix string `mapstructure:"prefix" json:"prefix,omitempty"`
+ // original -> gobgp:peer-group
+ PeerGroup string `mapstructure:"peer-group" json:"peer-group,omitempty"`
+}
+
+func (lhs *DynamicNeighborConfig) Equal(rhs *DynamicNeighborConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.Prefix != rhs.Prefix {
+ return false
+ }
+ if lhs.PeerGroup != rhs.PeerGroup {
+ return false
+ }
+ return true
+}
+
+// struct for container gobgp:dynamic-neighbor.
+type DynamicNeighbor struct {
+ // original -> gobgp:prefix
+ // original -> gobgp:dynamic-neighbor-config
+ Config DynamicNeighborConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> gobgp:dynamic-neighbor-state
+ State DynamicNeighborState `mapstructure:"state" json:"state,omitempty"`
+}
+
+func (lhs *DynamicNeighbor) Equal(rhs *DynamicNeighbor) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ return true
+}
+
+// struct for container gobgp:state.
+type CollectorState struct {
+ // original -> gobgp:url
+ Url string `mapstructure:"url" json:"url,omitempty"`
+ // original -> gobgp:db-name
+ DbName string `mapstructure:"db-name" json:"db-name,omitempty"`
+ // original -> gobgp:table-dump-interval
+ TableDumpInterval uint64 `mapstructure:"table-dump-interval" json:"table-dump-interval,omitempty"`
+}
+
+// struct for container gobgp:config.
+type CollectorConfig struct {
+ // original -> gobgp:url
+ Url string `mapstructure:"url" json:"url,omitempty"`
+ // original -> gobgp:db-name
+ DbName string `mapstructure:"db-name" json:"db-name,omitempty"`
+ // original -> gobgp:table-dump-interval
+ TableDumpInterval uint64 `mapstructure:"table-dump-interval" json:"table-dump-interval,omitempty"`
+}
+
+func (lhs *CollectorConfig) Equal(rhs *CollectorConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.Url != rhs.Url {
+ return false
+ }
+ if lhs.DbName != rhs.DbName {
+ return false
+ }
+ if lhs.TableDumpInterval != rhs.TableDumpInterval {
+ return false
+ }
+ return true
+}
+
+// struct for container gobgp:collector.
+type Collector struct {
+ // original -> gobgp:collector-config
+ Config CollectorConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> gobgp:collector-state
+ State CollectorState `mapstructure:"state" json:"state,omitempty"`
+}
+
+func (lhs *Collector) Equal(rhs *Collector) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ return true
+}
+
+// struct for container gobgp:state.
+type ZebraState struct {
+ // original -> gobgp:enabled
+ // gobgp:enabled's original type is boolean.
+ // Configure enabling to connect to zebra.
+ Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"`
+ // original -> gobgp:url
+ // Configure url for zebra.
+ Url string `mapstructure:"url" json:"url,omitempty"`
+ // original -> gobgp:redistribute-route-type
+ RedistributeRouteTypeList []string `mapstructure:"redistribute-route-type-list" json:"redistribute-route-type-list,omitempty"`
+ // original -> gobgp:version
+ // Configure version of zebra protocol. Default is 2. Supported up to 3.
+ Version uint8 `mapstructure:"version" json:"version,omitempty"`
+ // original -> gobgp:nexthop-trigger-enable
+ // gobgp:nexthop-trigger-enable's original type is boolean.
+ NexthopTriggerEnable bool `mapstructure:"nexthop-trigger-enable" json:"nexthop-trigger-enable,omitempty"`
+ // original -> gobgp:nexthop-trigger-delay
+ NexthopTriggerDelay uint8 `mapstructure:"nexthop-trigger-delay" json:"nexthop-trigger-delay,omitempty"`
+}
+
+// struct for container gobgp:config.
+type ZebraConfig struct {
+ // original -> gobgp:enabled
+ // gobgp:enabled's original type is boolean.
+ // Configure enabling to connect to zebra.
+ Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"`
+ // original -> gobgp:url
+ // Configure url for zebra.
+ Url string `mapstructure:"url" json:"url,omitempty"`
+ // original -> gobgp:redistribute-route-type
+ RedistributeRouteTypeList []string `mapstructure:"redistribute-route-type-list" json:"redistribute-route-type-list,omitempty"`
+ // original -> gobgp:version
+ // Configure version of zebra protocol. Default is 2. Supported up to 3.
+ Version uint8 `mapstructure:"version" json:"version,omitempty"`
+ // original -> gobgp:nexthop-trigger-enable
+ // gobgp:nexthop-trigger-enable's original type is boolean.
+ NexthopTriggerEnable bool `mapstructure:"nexthop-trigger-enable" json:"nexthop-trigger-enable,omitempty"`
+ // original -> gobgp:nexthop-trigger-delay
+ NexthopTriggerDelay uint8 `mapstructure:"nexthop-trigger-delay" json:"nexthop-trigger-delay,omitempty"`
+}
+
+func (lhs *ZebraConfig) Equal(rhs *ZebraConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.Enabled != rhs.Enabled {
+ return false
+ }
+ if lhs.Url != rhs.Url {
+ return false
+ }
+ if len(lhs.RedistributeRouteTypeList) != len(rhs.RedistributeRouteTypeList) {
+ return false
+ }
+ for idx, l := range lhs.RedistributeRouteTypeList {
+ if l != rhs.RedistributeRouteTypeList[idx] {
+ return false
+ }
+ }
+ if lhs.Version != rhs.Version {
+ return false
+ }
+ if lhs.NexthopTriggerEnable != rhs.NexthopTriggerEnable {
+ return false
+ }
+ if lhs.NexthopTriggerDelay != rhs.NexthopTriggerDelay {
+ return false
+ }
+ return true
+}
+
+// struct for container gobgp:zebra.
+type Zebra struct {
+ // original -> gobgp:zebra-config
+ Config ZebraConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> gobgp:zebra-state
+ State ZebraState `mapstructure:"state" json:"state,omitempty"`
+}
+
+func (lhs *Zebra) Equal(rhs *Zebra) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ return true
+}
+
+// struct for container gobgp:config.
+type MrtConfig struct {
+ // original -> gobgp:dump-type
+ DumpType MrtType `mapstructure:"dump-type" json:"dump-type,omitempty"`
+ // original -> gobgp:file-name
+ // Configures a file name to be written.
+ FileName string `mapstructure:"file-name" json:"file-name,omitempty"`
+ // original -> gobgp:table-name
+ // specify the table name with route server setup.
+ TableName string `mapstructure:"table-name" json:"table-name,omitempty"`
+ // original -> gobgp:dump-interval
+ DumpInterval uint64 `mapstructure:"dump-interval" json:"dump-interval,omitempty"`
+ // original -> gobgp:rotation-interval
+ RotationInterval uint64 `mapstructure:"rotation-interval" json:"rotation-interval,omitempty"`
+}
+
+func (lhs *MrtConfig) Equal(rhs *MrtConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.DumpType != rhs.DumpType {
+ return false
+ }
+ if lhs.FileName != rhs.FileName {
+ return false
+ }
+ if lhs.TableName != rhs.TableName {
+ return false
+ }
+ if lhs.DumpInterval != rhs.DumpInterval {
+ return false
+ }
+ if lhs.RotationInterval != rhs.RotationInterval {
+ return false
+ }
+ return true
+}
+
+// struct for container gobgp:mrt.
+type Mrt struct {
+ // original -> gobgp:file-name
+ // original -> gobgp:mrt-config
+ Config MrtConfig `mapstructure:"config" json:"config,omitempty"`
+}
+
+func (lhs *Mrt) Equal(rhs *Mrt) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ return true
+}
+
+// struct for container gobgp:state.
+// Configured states of VRF.
+type VrfState struct {
+ // original -> gobgp:name
+ // Unique name among all VRF instances.
+ Name string `mapstructure:"name" json:"name,omitempty"`
+ // original -> gobgp:id
+ // Unique identifier among all VRF instances.
+ Id uint32 `mapstructure:"id" json:"id,omitempty"`
+ // original -> gobgp:rd
+ // Route Distinguisher for this VRF.
+ Rd string `mapstructure:"rd" json:"rd,omitempty"`
+ // original -> gobgp:import-rt
+ // List of import Route Targets for this VRF.
+ ImportRtList []string `mapstructure:"import-rt-list" json:"import-rt-list,omitempty"`
+ // original -> gobgp:export-rt
+ // List of export Route Targets for this VRF.
+ ExportRtList []string `mapstructure:"export-rt-list" json:"export-rt-list,omitempty"`
+}
+
+// struct for container gobgp:config.
+// Configuration parameters for VRF.
+type VrfConfig struct {
+ // original -> gobgp:name
+ // Unique name among all VRF instances.
+ Name string `mapstructure:"name" json:"name,omitempty"`
+ // original -> gobgp:id
+ // Unique identifier among all VRF instances.
+ Id uint32 `mapstructure:"id" json:"id,omitempty"`
+ // original -> gobgp:rd
+ // Route Distinguisher for this VRF.
+ Rd string `mapstructure:"rd" json:"rd,omitempty"`
+ // original -> gobgp:import-rt
+ // List of import Route Targets for this VRF.
+ ImportRtList []string `mapstructure:"import-rt-list" json:"import-rt-list,omitempty"`
+ // original -> gobgp:export-rt
+ // List of export Route Targets for this VRF.
+ ExportRtList []string `mapstructure:"export-rt-list" json:"export-rt-list,omitempty"`
+ // original -> gobgp:both-rt
+ // List of both import and export Route Targets for this VRF. Each
+ // configuration for import and export Route Targets will be preferred.
+ BothRtList []string `mapstructure:"both-rt-list" json:"both-rt-list,omitempty"`
+}
+
+func (lhs *VrfConfig) Equal(rhs *VrfConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.Name != rhs.Name {
+ return false
+ }
+ if lhs.Id != rhs.Id {
+ return false
+ }
+ if lhs.Rd != rhs.Rd {
+ return false
+ }
+ if len(lhs.ImportRtList) != len(rhs.ImportRtList) {
+ return false
+ }
+ for idx, l := range lhs.ImportRtList {
+ if l != rhs.ImportRtList[idx] {
+ return false
+ }
+ }
+ if len(lhs.ExportRtList) != len(rhs.ExportRtList) {
+ return false
+ }
+ for idx, l := range lhs.ExportRtList {
+ if l != rhs.ExportRtList[idx] {
+ return false
+ }
+ }
+ if len(lhs.BothRtList) != len(rhs.BothRtList) {
+ return false
+ }
+ for idx, l := range lhs.BothRtList {
+ if l != rhs.BothRtList[idx] {
+ return false
+ }
+ }
+ return true
+}
+
+// struct for container gobgp:vrf.
+// VRF instance configurations on the local system.
+type Vrf struct {
+ // original -> gobgp:name
+ // original -> gobgp:vrf-config
+ // Configuration parameters for VRF.
+ Config VrfConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> gobgp:vrf-state
+ // Configured states of VRF.
+ State VrfState `mapstructure:"state" json:"state,omitempty"`
+}
+
+func (lhs *Vrf) Equal(rhs *Vrf) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ return true
+}
+
+// struct for container gobgp:state.
+// Configuration parameters relating to BMP server.
+type BmpServerState struct {
+ // original -> gobgp:address
+ // gobgp:address's original type is inet:ip-address.
+ // Reference to the address of the BMP server used as
+ // a key in the BMP server list.
+ Address string `mapstructure:"address" json:"address,omitempty"`
+ // original -> gobgp:port
+ // Reference to the port of the BMP server.
+ Port uint32 `mapstructure:"port" json:"port,omitempty"`
+ // original -> gobgp:route-monitoring-policy
+ RouteMonitoringPolicy BmpRouteMonitoringPolicyType `mapstructure:"route-monitoring-policy" json:"route-monitoring-policy,omitempty"`
+ // original -> gobgp:statistics-timeout
+ // Interval seconds of statistics messages sent to BMP server.
+ StatisticsTimeout uint16 `mapstructure:"statistics-timeout" json:"statistics-timeout,omitempty"`
+ // original -> gobgp:route-mirroring-enabled
+ // gobgp:route-mirroring-enabled's original type is boolean.
+ // Enable feature for mirroring of received BGP messages
+ // mainly for debugging purpose.
+ RouteMirroringEnabled bool `mapstructure:"route-mirroring-enabled" json:"route-mirroring-enabled,omitempty"`
+}
+
+// struct for container gobgp:config.
+// Configuration parameters relating to BMP server.
+type BmpServerConfig struct {
+ // original -> gobgp:address
+ // gobgp:address's original type is inet:ip-address.
+ // Reference to the address of the BMP server used as
+ // a key in the BMP server list.
+ Address string `mapstructure:"address" json:"address,omitempty"`
+ // original -> gobgp:port
+ // Reference to the port of the BMP server.
+ Port uint32 `mapstructure:"port" json:"port,omitempty"`
+ // original -> gobgp:route-monitoring-policy
+ RouteMonitoringPolicy BmpRouteMonitoringPolicyType `mapstructure:"route-monitoring-policy" json:"route-monitoring-policy,omitempty"`
+ // original -> gobgp:statistics-timeout
+ // Interval seconds of statistics messages sent to BMP server.
+ StatisticsTimeout uint16 `mapstructure:"statistics-timeout" json:"statistics-timeout,omitempty"`
+ // original -> gobgp:route-mirroring-enabled
+ // gobgp:route-mirroring-enabled's original type is boolean.
+ // Enable feature for mirroring of received BGP messages
+ // mainly for debugging purpose.
+ RouteMirroringEnabled bool `mapstructure:"route-mirroring-enabled" json:"route-mirroring-enabled,omitempty"`
+}
+
+func (lhs *BmpServerConfig) Equal(rhs *BmpServerConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.Address != rhs.Address {
+ return false
+ }
+ if lhs.Port != rhs.Port {
+ return false
+ }
+ if lhs.RouteMonitoringPolicy != rhs.RouteMonitoringPolicy {
+ return false
+ }
+ if lhs.StatisticsTimeout != rhs.StatisticsTimeout {
+ return false
+ }
+ if lhs.RouteMirroringEnabled != rhs.RouteMirroringEnabled {
+ return false
+ }
+ return true
+}
+
+// struct for container gobgp:bmp-server.
+// List of BMP servers configured on the local system.
+type BmpServer struct {
+ // original -> gobgp:address
+ // original -> gobgp:bmp-server-config
+ // Configuration parameters relating to BMP server.
+ Config BmpServerConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> gobgp:bmp-server-state
+ // Configuration parameters relating to BMP server.
+ State BmpServerState `mapstructure:"state" json:"state,omitempty"`
+}
+
+func (lhs *BmpServer) Equal(rhs *BmpServer) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ return true
+}
+
+// struct for container gobgp:rpki-received.
+// Counters for reception RPKI Message types.
+type RpkiReceived struct {
+ // original -> gobgp:serial-notify
+ // Number of serial notify message received from RPKI server.
+ SerialNotify int64 `mapstructure:"serial-notify" json:"serial-notify,omitempty"`
+ // original -> gobgp:cache-reset
+ // Number of cache reset message received from RPKI server.
+ CacheReset int64 `mapstructure:"cache-reset" json:"cache-reset,omitempty"`
+ // original -> gobgp:cache-response
+ // Number of cache response message received from RPKI server.
+ CacheResponse int64 `mapstructure:"cache-response" json:"cache-response,omitempty"`
+ // original -> gobgp:ipv4-prefix
+ // Number of ipv4 prefix message received from RPKI server.
+ Ipv4Prefix int64 `mapstructure:"ipv4-prefix" json:"ipv4-prefix,omitempty"`
+ // original -> gobgp:ipv6-prefix
+ // Number of ipv6 prefix message received from RPKI server.
+ Ipv6Prefix int64 `mapstructure:"ipv6-prefix" json:"ipv6-prefix,omitempty"`
+ // original -> gobgp:end-of-data
+ // Number of end of data message received from RPKI server.
+ EndOfData int64 `mapstructure:"end-of-data" json:"end-of-data,omitempty"`
+ // original -> gobgp:error
+ // Number of error message received from RPKI server.
+ Error int64 `mapstructure:"error" json:"error,omitempty"`
+}
+
+func (lhs *RpkiReceived) Equal(rhs *RpkiReceived) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.SerialNotify != rhs.SerialNotify {
+ return false
+ }
+ if lhs.CacheReset != rhs.CacheReset {
+ return false
+ }
+ if lhs.CacheResponse != rhs.CacheResponse {
+ return false
+ }
+ if lhs.Ipv4Prefix != rhs.Ipv4Prefix {
+ return false
+ }
+ if lhs.Ipv6Prefix != rhs.Ipv6Prefix {
+ return false
+ }
+ if lhs.EndOfData != rhs.EndOfData {
+ return false
+ }
+ if lhs.Error != rhs.Error {
+ return false
+ }
+ return true
+}
+
+// struct for container gobgp:rpki-sent.
+// Counters for transmission RPKI Message types.
+type RpkiSent struct {
+ // original -> gobgp:serial-query
+ // Number of serial query message sent to RPKI server.
+ SerialQuery int64 `mapstructure:"serial-query" json:"serial-query,omitempty"`
+ // original -> gobgp:reset-query
+ // Number of reset query message sent to RPKI server.
+ ResetQuery int64 `mapstructure:"reset-query" json:"reset-query,omitempty"`
+ // original -> gobgp:error
+ // Number of error message sent to RPKI server.
+ Error int64 `mapstructure:"error" json:"error,omitempty"`
+}
+
+func (lhs *RpkiSent) Equal(rhs *RpkiSent) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.SerialQuery != rhs.SerialQuery {
+ return false
+ }
+ if lhs.ResetQuery != rhs.ResetQuery {
+ return false
+ }
+ if lhs.Error != rhs.Error {
+ return false
+ }
+ return true
+}
+
+// struct for container gobgp:rpki-messages.
+// Counters for transmission and reception RPKI Message types.
+type RpkiMessages struct {
+ // original -> gobgp:rpki-sent
+ // Counters for transmission RPKI Message types.
+ RpkiSent RpkiSent `mapstructure:"rpki-sent" json:"rpki-sent,omitempty"`
+ // original -> gobgp:rpki-received
+ // Counters for reception RPKI Message types.
+ RpkiReceived RpkiReceived `mapstructure:"rpki-received" json:"rpki-received,omitempty"`
+}
+
+func (lhs *RpkiMessages) Equal(rhs *RpkiMessages) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.RpkiSent.Equal(&(rhs.RpkiSent)) {
+ return false
+ }
+ if !lhs.RpkiReceived.Equal(&(rhs.RpkiReceived)) {
+ return false
+ }
+ return true
+}
+
+// struct for container gobgp:state.
+// State information relating to RPKI server.
+type RpkiServerState struct {
+ // original -> gobgp:up
+ // gobgp:up's original type is boolean.
+ Up bool `mapstructure:"up" json:"up,omitempty"`
+ // original -> gobgp:serial-number
+ SerialNumber uint32 `mapstructure:"serial-number" json:"serial-number,omitempty"`
+ // original -> gobgp:records-v4
+ RecordsV4 uint32 `mapstructure:"records-v4" json:"records-v4,omitempty"`
+ // original -> gobgp:records-v6
+ RecordsV6 uint32 `mapstructure:"records-v6" json:"records-v6,omitempty"`
+ // original -> gobgp:prefixes-v4
+ PrefixesV4 uint32 `mapstructure:"prefixes-v4" json:"prefixes-v4,omitempty"`
+ // original -> gobgp:prefixes-v6
+ PrefixesV6 uint32 `mapstructure:"prefixes-v6" json:"prefixes-v6,omitempty"`
+ // original -> gobgp:uptime
+ // This timer determines the amount of time since the
+ // RPKI last transitioned in of the Established state.
+ Uptime int64 `mapstructure:"uptime" json:"uptime,omitempty"`
+ // original -> gobgp:downtime
+ // This timer determines the amount of time since the
+ // RPKI last transitioned out of the Established state.
+ Downtime int64 `mapstructure:"downtime" json:"downtime,omitempty"`
+ // original -> gobgp:last-pdu-recv-time
+ // last time the received an pdu message from RPKI server.
+ LastPduRecvTime int64 `mapstructure:"last-pdu-recv-time" json:"last-pdu-recv-time,omitempty"`
+ // original -> gobgp:rpki-messages
+ // Counters for transmission and reception RPKI Message types.
+ RpkiMessages RpkiMessages `mapstructure:"rpki-messages" json:"rpki-messages,omitempty"`
+}
+
+// struct for container gobgp:config.
+// Configuration parameters relating to RPKI server.
+type RpkiServerConfig struct {
+ // original -> gobgp:address
+ // gobgp:address's original type is inet:ip-address.
+ // Reference to the address of the RPKI server used as
+ // a key in the RPKI server list.
+ Address string `mapstructure:"address" json:"address,omitempty"`
+ // original -> gobgp:port
+ // Reference to the port of the RPKI server.
+ Port uint32 `mapstructure:"port" json:"port,omitempty"`
+ // original -> gobgp:refresh-time
+ // Check interval for a configured RPKI server.
+ RefreshTime int64 `mapstructure:"refresh-time" json:"refresh-time,omitempty"`
+ // original -> gobgp:hold-time
+ // Specify the length of time in seconds that the session between
+ // the router and RPKI server is to be considered operational
+ // without any activity.
+ HoldTime int64 `mapstructure:"hold-time" json:"hold-time,omitempty"`
+ // original -> gobgp:record-lifetime
+ // Indicate the expiration date of the route validation recode
+ // received from RPKI server.
+ RecordLifetime int64 `mapstructure:"record-lifetime" json:"record-lifetime,omitempty"`
+ // original -> gobgp:preference
+ // RPKI server has a static preference.
+ // Higher the preference values indicates a higher priority RPKI server.
+ Preference uint8 `mapstructure:"preference" json:"preference,omitempty"`
+}
+
+func (lhs *RpkiServerConfig) Equal(rhs *RpkiServerConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.Address != rhs.Address {
+ return false
+ }
+ if lhs.Port != rhs.Port {
+ return false
+ }
+ if lhs.RefreshTime != rhs.RefreshTime {
+ return false
+ }
+ if lhs.HoldTime != rhs.HoldTime {
+ return false
+ }
+ if lhs.RecordLifetime != rhs.RecordLifetime {
+ return false
+ }
+ if lhs.Preference != rhs.Preference {
+ return false
+ }
+ return true
+}
+
+// struct for container gobgp:rpki-server.
+// List of RPKI servers configured on the local system.
+type RpkiServer struct {
+ // original -> gobgp:address
+ // original -> gobgp:rpki-server-config
+ // Configuration parameters relating to RPKI server.
+ Config RpkiServerConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> gobgp:rpki-server-state
+ // State information relating to RPKI server.
+ State RpkiServerState `mapstructure:"state" json:"state,omitempty"`
+}
+
+func (lhs *RpkiServer) Equal(rhs *RpkiServer) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp:state.
+// State information relating to the BGP neighbor or group.
+type PeerGroupState struct {
+ // original -> bgp:peer-as
+ // bgp:peer-as's original type is inet:as-number.
+ // AS number of the peer.
+ PeerAs uint32 `mapstructure:"peer-as" json:"peer-as,omitempty"`
+ // original -> bgp:local-as
+ // bgp:local-as's original type is inet:as-number.
+ // The local autonomous system number that is to be used
+ // when establishing sessions with the remote peer or peer
+ // group, if this differs from the global BGP router
+ // autonomous system number.
+ LocalAs uint32 `mapstructure:"local-as" json:"local-as,omitempty"`
+ // original -> bgp:peer-type
+ // Explicitly designate the peer or peer group as internal
+ // (iBGP) or external (eBGP).
+ PeerType PeerType `mapstructure:"peer-type" json:"peer-type,omitempty"`
+ // original -> bgp:auth-password
+ // Configures an MD5 authentication password for use with
+ // neighboring devices.
+ AuthPassword string `mapstructure:"auth-password" json:"auth-password,omitempty"`
+ // original -> bgp:remove-private-as
+ // Remove private AS numbers from updates sent to peers.
+ RemovePrivateAs RemovePrivateAsOption `mapstructure:"remove-private-as" json:"remove-private-as,omitempty"`
+ // original -> bgp:route-flap-damping
+ // bgp:route-flap-damping's original type is boolean.
+ // Enable route flap damping.
+ RouteFlapDamping bool `mapstructure:"route-flap-damping" json:"route-flap-damping,omitempty"`
+ // original -> bgp:send-community
+ // Specify which types of community should be sent to the
+ // neighbor or group. The default is to not send the
+ // community attribute.
+ SendCommunity CommunityType `mapstructure:"send-community" json:"send-community,omitempty"`
+ // original -> bgp:description
+ // An optional textual description (intended primarily for use
+ // with a peer or group.
+ Description string `mapstructure:"description" json:"description,omitempty"`
+ // original -> bgp:peer-group-name
+ // Name of the BGP peer-group.
+ PeerGroupName string `mapstructure:"peer-group-name" json:"peer-group-name,omitempty"`
+ // original -> bgp-op:total-paths
+ // Total number of BGP paths within the context.
+ TotalPaths uint32 `mapstructure:"total-paths" json:"total-paths,omitempty"`
+ // original -> bgp-op:total-prefixes
+ // .
+ TotalPrefixes uint32 `mapstructure:"total-prefixes" json:"total-prefixes,omitempty"`
+}
+
+// struct for container bgp:config.
+// Configuration parameters relating to the BGP neighbor or
+// group.
+type PeerGroupConfig struct {
+ // original -> bgp:peer-as
+ // bgp:peer-as's original type is inet:as-number.
+ // AS number of the peer.
+ PeerAs uint32 `mapstructure:"peer-as" json:"peer-as,omitempty"`
+ // original -> bgp:local-as
+ // bgp:local-as's original type is inet:as-number.
+ // The local autonomous system number that is to be used
+ // when establishing sessions with the remote peer or peer
+ // group, if this differs from the global BGP router
+ // autonomous system number.
+ LocalAs uint32 `mapstructure:"local-as" json:"local-as,omitempty"`
+ // original -> bgp:peer-type
+ // Explicitly designate the peer or peer group as internal
+ // (iBGP) or external (eBGP).
+ PeerType PeerType `mapstructure:"peer-type" json:"peer-type,omitempty"`
+ // original -> bgp:auth-password
+ // Configures an MD5 authentication password for use with
+ // neighboring devices.
+ AuthPassword string `mapstructure:"auth-password" json:"auth-password,omitempty"`
+ // original -> bgp:remove-private-as
+ // Remove private AS numbers from updates sent to peers.
+ RemovePrivateAs RemovePrivateAsOption `mapstructure:"remove-private-as" json:"remove-private-as,omitempty"`
+ // original -> bgp:route-flap-damping
+ // bgp:route-flap-damping's original type is boolean.
+ // Enable route flap damping.
+ RouteFlapDamping bool `mapstructure:"route-flap-damping" json:"route-flap-damping,omitempty"`
+ // original -> bgp:send-community
+ // Specify which types of community should be sent to the
+ // neighbor or group. The default is to not send the
+ // community attribute.
+ SendCommunity CommunityType `mapstructure:"send-community" json:"send-community,omitempty"`
+ // original -> bgp:description
+ // An optional textual description (intended primarily for use
+ // with a peer or group.
+ Description string `mapstructure:"description" json:"description,omitempty"`
+ // original -> bgp:peer-group-name
+ // Name of the BGP peer-group.
+ PeerGroupName string `mapstructure:"peer-group-name" json:"peer-group-name,omitempty"`
+}
+
+func (lhs *PeerGroupConfig) Equal(rhs *PeerGroupConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.PeerAs != rhs.PeerAs {
+ return false
+ }
+ if lhs.LocalAs != rhs.LocalAs {
+ return false
+ }
+ if lhs.PeerType != rhs.PeerType {
+ return false
+ }
+ if lhs.AuthPassword != rhs.AuthPassword {
+ return false
+ }
+ if lhs.RemovePrivateAs != rhs.RemovePrivateAs {
+ return false
+ }
+ if lhs.RouteFlapDamping != rhs.RouteFlapDamping {
+ return false
+ }
+ if lhs.SendCommunity != rhs.SendCommunity {
+ return false
+ }
+ if lhs.Description != rhs.Description {
+ return false
+ }
+ if lhs.PeerGroupName != rhs.PeerGroupName {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp:peer-group.
+// List of BGP peer-groups configured on the local system -
+// uniquely identified by peer-group name.
+type PeerGroup struct {
+ // original -> bgp:peer-group-name
+ // original -> bgp:peer-group-config
+ // Configuration parameters relating to the BGP neighbor or
+ // group.
+ Config PeerGroupConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> bgp:peer-group-state
+ // State information relating to the BGP neighbor or group.
+ State PeerGroupState `mapstructure:"state" json:"state,omitempty"`
+ // original -> bgp:timers
+ // Timers related to a BGP neighbor or group.
+ Timers Timers `mapstructure:"timers" json:"timers,omitempty"`
+ // original -> bgp:transport
+ // Transport session parameters for the BGP neighbor or group.
+ Transport Transport `mapstructure:"transport" json:"transport,omitempty"`
+ // original -> bgp:error-handling
+ // Error handling parameters used for the BGP neighbor or
+ // group.
+ ErrorHandling ErrorHandling `mapstructure:"error-handling" json:"error-handling,omitempty"`
+ // original -> bgp:logging-options
+ // Logging options for events related to the BGP neighbor or
+ // group.
+ LoggingOptions LoggingOptions `mapstructure:"logging-options" json:"logging-options,omitempty"`
+ // original -> bgp:ebgp-multihop
+ // eBGP multi-hop parameters for the BGP neighbor or group.
+ EbgpMultihop EbgpMultihop `mapstructure:"ebgp-multihop" json:"ebgp-multihop,omitempty"`
+ // original -> bgp:route-reflector
+ // Route reflector parameters for the BGP neighbor or group.
+ RouteReflector RouteReflector `mapstructure:"route-reflector" json:"route-reflector,omitempty"`
+ // original -> bgp:as-path-options
+ // AS_PATH manipulation parameters for the BGP neighbor or
+ // group.
+ AsPathOptions AsPathOptions `mapstructure:"as-path-options" json:"as-path-options,omitempty"`
+ // original -> bgp:add-paths
+ // Parameters relating to the advertisement and receipt of
+ // multiple paths for a single NLRI (add-paths).
+ AddPaths AddPaths `mapstructure:"add-paths" json:"add-paths,omitempty"`
+ // original -> bgp:afi-safis
+ // Per-address-family configuration parameters associated with
+ // the neighbor or group.
+ AfiSafis []AfiSafi `mapstructure:"afi-safis" json:"afi-safis,omitempty"`
+ // original -> bgp:graceful-restart
+ // Parameters relating the graceful restart mechanism for BGP.
+ GracefulRestart GracefulRestart `mapstructure:"graceful-restart" json:"graceful-restart,omitempty"`
+ // original -> rpol:apply-policy
+ // Anchor point for routing policies in the model.
+ // Import and export policies are with respect to the local
+ // routing table, i.e., export (send) and import (receive),
+ // depending on the context.
+ ApplyPolicy ApplyPolicy `mapstructure:"apply-policy" json:"apply-policy,omitempty"`
+ // original -> bgp-mp:use-multiple-paths
+ // Parameters related to the use of multiple paths for the
+ // same NLRI.
+ UseMultiplePaths UseMultiplePaths `mapstructure:"use-multiple-paths" json:"use-multiple-paths,omitempty"`
+ // original -> gobgp:route-server
+ // Configure the local router as a route server.
+ RouteServer RouteServer `mapstructure:"route-server" json:"route-server,omitempty"`
+ // original -> gobgp:ttl-security
+ // Configure TTL Security feature.
+ TtlSecurity TtlSecurity `mapstructure:"ttl-security" json:"ttl-security,omitempty"`
+}
+
+func (lhs *PeerGroup) Equal(rhs *PeerGroup) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ if !lhs.Timers.Equal(&(rhs.Timers)) {
+ return false
+ }
+ if !lhs.Transport.Equal(&(rhs.Transport)) {
+ return false
+ }
+ if !lhs.ErrorHandling.Equal(&(rhs.ErrorHandling)) {
+ return false
+ }
+ if !lhs.LoggingOptions.Equal(&(rhs.LoggingOptions)) {
+ return false
+ }
+ if !lhs.EbgpMultihop.Equal(&(rhs.EbgpMultihop)) {
+ return false
+ }
+ if !lhs.RouteReflector.Equal(&(rhs.RouteReflector)) {
+ return false
+ }
+ if !lhs.AsPathOptions.Equal(&(rhs.AsPathOptions)) {
+ return false
+ }
+ if !lhs.AddPaths.Equal(&(rhs.AddPaths)) {
+ return false
+ }
+ if len(lhs.AfiSafis) != len(rhs.AfiSafis) {
+ return false
+ }
+ {
+ lmap := make(map[string]*AfiSafi)
+ for i, l := range lhs.AfiSafis {
+ lmap[mapkey(i, string(l.Config.AfiSafiName))] = &lhs.AfiSafis[i]
+ }
+ for i, r := range rhs.AfiSafis {
+ if l, y := lmap[mapkey(i, string(r.Config.AfiSafiName))]; !y {
+ return false
+ } else if !r.Equal(l) {
+ return false
+ }
+ }
+ }
+ if !lhs.GracefulRestart.Equal(&(rhs.GracefulRestart)) {
+ return false
+ }
+ if !lhs.ApplyPolicy.Equal(&(rhs.ApplyPolicy)) {
+ return false
+ }
+ if !lhs.UseMultiplePaths.Equal(&(rhs.UseMultiplePaths)) {
+ return false
+ }
+ if !lhs.RouteServer.Equal(&(rhs.RouteServer)) {
+ return false
+ }
+ if !lhs.TtlSecurity.Equal(&(rhs.TtlSecurity)) {
+ return false
+ }
+ return true
+}
+
+// struct for container gobgp:state.
+// State information for TTL Security.
+type TtlSecurityState struct {
+ // original -> gobgp:enabled
+ // gobgp:enabled's original type is boolean.
+ // Enable features for TTL Security.
+ Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"`
+ // original -> gobgp:ttl-min
+ // Reference to the port of the BMP server.
+ TtlMin uint8 `mapstructure:"ttl-min" json:"ttl-min,omitempty"`
+}
+
+// struct for container gobgp:config.
+// Configuration parameters for TTL Security.
+type TtlSecurityConfig struct {
+ // original -> gobgp:enabled
+ // gobgp:enabled's original type is boolean.
+ // Enable features for TTL Security.
+ Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"`
+ // original -> gobgp:ttl-min
+ // Reference to the port of the BMP server.
+ TtlMin uint8 `mapstructure:"ttl-min" json:"ttl-min,omitempty"`
+}
+
+func (lhs *TtlSecurityConfig) Equal(rhs *TtlSecurityConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.Enabled != rhs.Enabled {
+ return false
+ }
+ if lhs.TtlMin != rhs.TtlMin {
+ return false
+ }
+ return true
+}
+
+// struct for container gobgp:ttl-security.
+// Configure TTL Security feature.
+type TtlSecurity struct {
+ // original -> gobgp:ttl-security-config
+ // Configuration parameters for TTL Security.
+ Config TtlSecurityConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> gobgp:ttl-security-state
+ // State information for TTL Security.
+ State TtlSecurityState `mapstructure:"state" json:"state,omitempty"`
+}
+
+func (lhs *TtlSecurity) Equal(rhs *TtlSecurity) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ return true
+}
+
+// struct for container gobgp:state.
+// State information relating to route server
+// client(s) used for the BGP neighbor.
+type RouteServerState struct {
+ // original -> gobgp:route-server-client
+ // gobgp:route-server-client's original type is boolean.
+ // Configure the neighbor as a route server client.
+ RouteServerClient bool `mapstructure:"route-server-client" json:"route-server-client,omitempty"`
+}
+
+// struct for container gobgp:config.
+// Configuration parameters relating to route server
+// client(s) used for the BGP neighbor.
+type RouteServerConfig struct {
+ // original -> gobgp:route-server-client
+ // gobgp:route-server-client's original type is boolean.
+ // Configure the neighbor as a route server client.
+ RouteServerClient bool `mapstructure:"route-server-client" json:"route-server-client,omitempty"`
+}
+
+func (lhs *RouteServerConfig) Equal(rhs *RouteServerConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.RouteServerClient != rhs.RouteServerClient {
+ return false
+ }
+ return true
+}
+
+// struct for container gobgp:route-server.
+// Configure the local router as a route server.
+type RouteServer struct {
+ // original -> gobgp:route-server-config
+ // Configuration parameters relating to route server
+ // client(s) used for the BGP neighbor.
+ Config RouteServerConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> gobgp:route-server-state
+ // State information relating to route server
+ // client(s) used for the BGP neighbor.
+ State RouteServerState `mapstructure:"state" json:"state,omitempty"`
+}
+
+func (lhs *RouteServer) Equal(rhs *RouteServer) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-op:prefixes.
+// Prefix counters for the BGP session.
+type Prefixes struct {
+ // original -> bgp-op:received
+ // The number of prefixes received from the neighbor.
+ Received uint32 `mapstructure:"received" json:"received,omitempty"`
+ // original -> bgp-op:sent
+ // The number of prefixes advertised to the neighbor.
+ Sent uint32 `mapstructure:"sent" json:"sent,omitempty"`
+ // original -> bgp-op:installed
+ // The number of advertised prefixes installed in the
+ // Loc-RIB.
+ Installed uint32 `mapstructure:"installed" json:"installed,omitempty"`
+}
+
+func (lhs *Prefixes) Equal(rhs *Prefixes) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.Received != rhs.Received {
+ return false
+ }
+ if lhs.Sent != rhs.Sent {
+ return false
+ }
+ if lhs.Installed != rhs.Installed {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp:state.
+// State information associated with ADD_PATHS.
+type AddPathsState struct {
+ // original -> bgp:receive
+ // bgp:receive's original type is boolean.
+ // Enable ability to receive multiple path advertisements
+ // for an NLRI from the neighbor or group.
+ Receive bool `mapstructure:"receive" json:"receive,omitempty"`
+ // original -> bgp:send-max
+ // The maximum number of paths to advertise to neighbors
+ // for a single NLRI.
+ SendMax uint8 `mapstructure:"send-max" json:"send-max,omitempty"`
+}
+
+// struct for container bgp:config.
+// Configuration parameters relating to ADD_PATHS.
+type AddPathsConfig struct {
+ // original -> bgp:receive
+ // bgp:receive's original type is boolean.
+ // Enable ability to receive multiple path advertisements
+ // for an NLRI from the neighbor or group.
+ Receive bool `mapstructure:"receive" json:"receive,omitempty"`
+ // original -> bgp:send-max
+ // The maximum number of paths to advertise to neighbors
+ // for a single NLRI.
+ SendMax uint8 `mapstructure:"send-max" json:"send-max,omitempty"`
+}
+
+func (lhs *AddPathsConfig) Equal(rhs *AddPathsConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.Receive != rhs.Receive {
+ return false
+ }
+ if lhs.SendMax != rhs.SendMax {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp:add-paths.
+// Parameters relating to the advertisement and receipt of
+// multiple paths for a single NLRI (add-paths).
+type AddPaths struct {
+ // original -> bgp:add-paths-config
+ // Configuration parameters relating to ADD_PATHS.
+ Config AddPathsConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> bgp:add-paths-state
+ // State information associated with ADD_PATHS.
+ State AddPathsState `mapstructure:"state" json:"state,omitempty"`
+}
+
+func (lhs *AddPaths) Equal(rhs *AddPaths) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp:state.
+// State information relating to the AS_PATH manipulation
+// mechanisms for the BGP peer or group.
+type AsPathOptionsState struct {
+ // original -> bgp:allow-own-as
+ // Specify the number of occurrences of the local BGP speaker's
+ // AS that can occur within the AS_PATH before it is rejected.
+ AllowOwnAs uint8 `mapstructure:"allow-own-as" json:"allow-own-as,omitempty"`
+ // original -> bgp:replace-peer-as
+ // bgp:replace-peer-as's original type is boolean.
+ // Replace occurrences of the peer's AS in the AS_PATH
+ // with the local autonomous system number.
+ ReplacePeerAs bool `mapstructure:"replace-peer-as" json:"replace-peer-as,omitempty"`
+}
+
+// struct for container bgp:config.
+// Configuration parameters relating to AS_PATH manipulation
+// for the BGP peer or group.
+type AsPathOptionsConfig struct {
+ // original -> bgp:allow-own-as
+ // Specify the number of occurrences of the local BGP speaker's
+ // AS that can occur within the AS_PATH before it is rejected.
+ AllowOwnAs uint8 `mapstructure:"allow-own-as" json:"allow-own-as,omitempty"`
+ // original -> bgp:replace-peer-as
+ // bgp:replace-peer-as's original type is boolean.
+ // Replace occurrences of the peer's AS in the AS_PATH
+ // with the local autonomous system number.
+ ReplacePeerAs bool `mapstructure:"replace-peer-as" json:"replace-peer-as,omitempty"`
+}
+
+func (lhs *AsPathOptionsConfig) Equal(rhs *AsPathOptionsConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.AllowOwnAs != rhs.AllowOwnAs {
+ return false
+ }
+ if lhs.ReplacePeerAs != rhs.ReplacePeerAs {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp:as-path-options.
+// AS_PATH manipulation parameters for the BGP neighbor or
+// group.
+type AsPathOptions struct {
+ // original -> bgp:as-path-options-config
+ // Configuration parameters relating to AS_PATH manipulation
+ // for the BGP peer or group.
+ Config AsPathOptionsConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> bgp:as-path-options-state
+ // State information relating to the AS_PATH manipulation
+ // mechanisms for the BGP peer or group.
+ State AsPathOptionsState `mapstructure:"state" json:"state,omitempty"`
+}
+
+func (lhs *AsPathOptions) Equal(rhs *AsPathOptions) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp:state.
+// State information relating to route reflection for the
+// BGP neighbor or group.
+type RouteReflectorState struct {
+ // original -> bgp:route-reflector-cluster-id
+ // route-reflector cluster id to use when local router is
+ // configured as a route reflector. Commonly set at the group
+ // level, but allows a different cluster
+ // id to be set for each neighbor.
+ RouteReflectorClusterId RrClusterIdType `mapstructure:"route-reflector-cluster-id" json:"route-reflector-cluster-id,omitempty"`
+ // original -> bgp:route-reflector-client
+ // bgp:route-reflector-client's original type is boolean.
+ // Configure the neighbor as a route reflector client.
+ RouteReflectorClient bool `mapstructure:"route-reflector-client" json:"route-reflector-client,omitempty"`
+}
+
+// struct for container bgp:config.
+// Configuraton parameters relating to route reflection
+// for the BGP neighbor or group.
+type RouteReflectorConfig struct {
+ // original -> bgp:route-reflector-cluster-id
+ // route-reflector cluster id to use when local router is
+ // configured as a route reflector. Commonly set at the group
+ // level, but allows a different cluster
+ // id to be set for each neighbor.
+ RouteReflectorClusterId RrClusterIdType `mapstructure:"route-reflector-cluster-id" json:"route-reflector-cluster-id,omitempty"`
+ // original -> bgp:route-reflector-client
+ // bgp:route-reflector-client's original type is boolean.
+ // Configure the neighbor as a route reflector client.
+ RouteReflectorClient bool `mapstructure:"route-reflector-client" json:"route-reflector-client,omitempty"`
+}
+
+func (lhs *RouteReflectorConfig) Equal(rhs *RouteReflectorConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.RouteReflectorClusterId != rhs.RouteReflectorClusterId {
+ return false
+ }
+ if lhs.RouteReflectorClient != rhs.RouteReflectorClient {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp:route-reflector.
+// Route reflector parameters for the BGP neighbor or group.
+type RouteReflector struct {
+ // original -> bgp:route-reflector-config
+ // Configuraton parameters relating to route reflection
+ // for the BGP neighbor or group.
+ Config RouteReflectorConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> bgp:route-reflector-state
+ // State information relating to route reflection for the
+ // BGP neighbor or group.
+ State RouteReflectorState `mapstructure:"state" json:"state,omitempty"`
+}
+
+func (lhs *RouteReflector) Equal(rhs *RouteReflector) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp:state.
+// State information for eBGP multihop, for the BGP neighbor
+// or group.
+type EbgpMultihopState struct {
+ // original -> bgp:enabled
+ // bgp:enabled's original type is boolean.
+ // When enabled the referenced group or neighbors are permitted
+ // to be indirectly connected - including cases where the TTL
+ // can be decremented between the BGP peers.
+ Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"`
+ // original -> bgp:multihop-ttl
+ // Time-to-live value to use when packets are sent to the
+ // referenced group or neighbors and ebgp-multihop is enabled.
+ MultihopTtl uint8 `mapstructure:"multihop-ttl" json:"multihop-ttl,omitempty"`
+}
+
+// struct for container bgp:config.
+// Configuration parameters relating to eBGP multihop for the
+// BGP neighbor or group.
+type EbgpMultihopConfig struct {
+ // original -> bgp:enabled
+ // bgp:enabled's original type is boolean.
+ // When enabled the referenced group or neighbors are permitted
+ // to be indirectly connected - including cases where the TTL
+ // can be decremented between the BGP peers.
+ Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"`
+ // original -> bgp:multihop-ttl
+ // Time-to-live value to use when packets are sent to the
+ // referenced group or neighbors and ebgp-multihop is enabled.
+ MultihopTtl uint8 `mapstructure:"multihop-ttl" json:"multihop-ttl,omitempty"`
+}
+
+func (lhs *EbgpMultihopConfig) Equal(rhs *EbgpMultihopConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.Enabled != rhs.Enabled {
+ return false
+ }
+ if lhs.MultihopTtl != rhs.MultihopTtl {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp:ebgp-multihop.
+// eBGP multi-hop parameters for the BGP neighbor or group.
+type EbgpMultihop struct {
+ // original -> bgp:ebgp-multihop-config
+ // Configuration parameters relating to eBGP multihop for the
+ // BGP neighbor or group.
+ Config EbgpMultihopConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> bgp:ebgp-multihop-state
+ // State information for eBGP multihop, for the BGP neighbor
+ // or group.
+ State EbgpMultihopState `mapstructure:"state" json:"state,omitempty"`
+}
+
+func (lhs *EbgpMultihop) Equal(rhs *EbgpMultihop) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp:state.
+// State information relating to logging for the BGP neighbor
+// or group.
+type LoggingOptionsState struct {
+ // original -> bgp:log-neighbor-state-changes
+ // bgp:log-neighbor-state-changes's original type is boolean.
+ // Configure logging of peer state changes. Default is
+ // to enable logging of peer state changes.
+ LogNeighborStateChanges bool `mapstructure:"log-neighbor-state-changes" json:"log-neighbor-state-changes,omitempty"`
+}
+
+// struct for container bgp:config.
+// Configuration parameters enabling or modifying logging
+// for events relating to the BGP neighbor or group.
+type LoggingOptionsConfig struct {
+ // original -> bgp:log-neighbor-state-changes
+ // bgp:log-neighbor-state-changes's original type is boolean.
+ // Configure logging of peer state changes. Default is
+ // to enable logging of peer state changes.
+ LogNeighborStateChanges bool `mapstructure:"log-neighbor-state-changes" json:"log-neighbor-state-changes,omitempty"`
+}
+
+func (lhs *LoggingOptionsConfig) Equal(rhs *LoggingOptionsConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.LogNeighborStateChanges != rhs.LogNeighborStateChanges {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp:logging-options.
+// Logging options for events related to the BGP neighbor or
+// group.
+type LoggingOptions struct {
+ // original -> bgp:logging-options-config
+ // Configuration parameters enabling or modifying logging
+ // for events relating to the BGP neighbor or group.
+ Config LoggingOptionsConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> bgp:logging-options-state
+ // State information relating to logging for the BGP neighbor
+ // or group.
+ State LoggingOptionsState `mapstructure:"state" json:"state,omitempty"`
+}
+
+func (lhs *LoggingOptions) Equal(rhs *LoggingOptions) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp:state.
+// State information relating to enhanced error handling
+// mechanisms for the BGP neighbor or group.
+type ErrorHandlingState struct {
+ // original -> bgp:treat-as-withdraw
+ // bgp:treat-as-withdraw's original type is boolean.
+ // Specify whether erroneous UPDATE messages for which the
+ // NLRI can be extracted are reated as though the NLRI is
+ // withdrawn - avoiding session reset.
+ TreatAsWithdraw bool `mapstructure:"treat-as-withdraw" json:"treat-as-withdraw,omitempty"`
+ // original -> bgp-op:erroneous-update-messages
+ // The number of BGP UPDATE messages for which the
+ // treat-as-withdraw mechanism has been applied based
+ // on erroneous message contents.
+ ErroneousUpdateMessages uint32 `mapstructure:"erroneous-update-messages" json:"erroneous-update-messages,omitempty"`
+}
+
+// struct for container bgp:config.
+// Configuration parameters enabling or modifying the
+// behavior or enhanced error handling mechanisms for the BGP
+// neighbor or group.
+type ErrorHandlingConfig struct {
+ // original -> bgp:treat-as-withdraw
+ // bgp:treat-as-withdraw's original type is boolean.
+ // Specify whether erroneous UPDATE messages for which the
+ // NLRI can be extracted are reated as though the NLRI is
+ // withdrawn - avoiding session reset.
+ TreatAsWithdraw bool `mapstructure:"treat-as-withdraw" json:"treat-as-withdraw,omitempty"`
+}
+
+func (lhs *ErrorHandlingConfig) Equal(rhs *ErrorHandlingConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.TreatAsWithdraw != rhs.TreatAsWithdraw {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp:error-handling.
+// Error handling parameters used for the BGP neighbor or
+// group.
+type ErrorHandling struct {
+ // original -> bgp:error-handling-config
+ // Configuration parameters enabling or modifying the
+ // behavior or enhanced error handling mechanisms for the BGP
+ // neighbor or group.
+ Config ErrorHandlingConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> bgp:error-handling-state
+ // State information relating to enhanced error handling
+ // mechanisms for the BGP neighbor or group.
+ State ErrorHandlingState `mapstructure:"state" json:"state,omitempty"`
+}
+
+func (lhs *ErrorHandling) Equal(rhs *ErrorHandling) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp:state.
+// State information relating to the transport session(s)
+// used for the BGP neighbor or group.
+type TransportState struct {
+ // original -> bgp:tcp-mss
+ // Sets the max segment size for BGP TCP sessions.
+ TcpMss uint16 `mapstructure:"tcp-mss" json:"tcp-mss,omitempty"`
+ // original -> bgp:mtu-discovery
+ // bgp:mtu-discovery's original type is boolean.
+ // Turns path mtu discovery for BGP TCP sessions on (true)
+ // or off (false).
+ MtuDiscovery bool `mapstructure:"mtu-discovery" json:"mtu-discovery,omitempty"`
+ // original -> bgp:passive-mode
+ // bgp:passive-mode's original type is boolean.
+ // Wait for peers to issue requests to open a BGP session,
+ // rather than initiating sessions from the local router.
+ PassiveMode bool `mapstructure:"passive-mode" json:"passive-mode,omitempty"`
+ // original -> bgp:local-address
+ // bgp:local-address's original type is union.
+ // Set the local IP (either IPv4 or IPv6) address to use
+ // for the session when sending BGP update messages. This
+ // may be expressed as either an IP address or reference
+ // to the name of an interface.
+ LocalAddress string `mapstructure:"local-address" json:"local-address,omitempty"`
+ // original -> bgp-op:local-port
+ // bgp-op:local-port's original type is inet:port-number.
+ // Local TCP port being used for the TCP session supporting
+ // the BGP session.
+ LocalPort uint16 `mapstructure:"local-port" json:"local-port,omitempty"`
+ // original -> bgp-op:remote-address
+ // bgp-op:remote-address's original type is inet:ip-address.
+ // Remote address to which the BGP session has been
+ // established.
+ RemoteAddress string `mapstructure:"remote-address" json:"remote-address,omitempty"`
+ // original -> bgp-op:remote-port
+ // bgp-op:remote-port's original type is inet:port-number.
+ // Remote port being used by the peer for the TCP session
+ // supporting the BGP session.
+ RemotePort uint16 `mapstructure:"remote-port" json:"remote-port,omitempty"`
+}
+
+// struct for container bgp:config.
+// Configuration parameters relating to the transport
+// session(s) used for the BGP neighbor or group.
+type TransportConfig struct {
+ // original -> bgp:tcp-mss
+ // Sets the max segment size for BGP TCP sessions.
+ TcpMss uint16 `mapstructure:"tcp-mss" json:"tcp-mss,omitempty"`
+ // original -> bgp:mtu-discovery
+ // bgp:mtu-discovery's original type is boolean.
+ // Turns path mtu discovery for BGP TCP sessions on (true)
+ // or off (false).
+ MtuDiscovery bool `mapstructure:"mtu-discovery" json:"mtu-discovery,omitempty"`
+ // original -> bgp:passive-mode
+ // bgp:passive-mode's original type is boolean.
+ // Wait for peers to issue requests to open a BGP session,
+ // rather than initiating sessions from the local router.
+ PassiveMode bool `mapstructure:"passive-mode" json:"passive-mode,omitempty"`
+ // original -> bgp:local-address
+ // bgp:local-address's original type is union.
+ // Set the local IP (either IPv4 or IPv6) address to use
+ // for the session when sending BGP update messages. This
+ // may be expressed as either an IP address or reference
+ // to the name of an interface.
+ LocalAddress string `mapstructure:"local-address" json:"local-address,omitempty"`
+ // original -> gobgp:remote-port
+ // gobgp:remote-port's original type is inet:port-number.
+ RemotePort uint16 `mapstructure:"remote-port" json:"remote-port,omitempty"`
+ // original -> gobgp:ttl
+ // TTL value for BGP packets.
+ Ttl uint8 `mapstructure:"ttl" json:"ttl,omitempty"`
+}
+
+func (lhs *TransportConfig) Equal(rhs *TransportConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.TcpMss != rhs.TcpMss {
+ return false
+ }
+ if lhs.MtuDiscovery != rhs.MtuDiscovery {
+ return false
+ }
+ if lhs.PassiveMode != rhs.PassiveMode {
+ return false
+ }
+ if lhs.LocalAddress != rhs.LocalAddress {
+ return false
+ }
+ if lhs.RemotePort != rhs.RemotePort {
+ return false
+ }
+ if lhs.Ttl != rhs.Ttl {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp:transport.
+// Transport session parameters for the BGP neighbor or group.
+type Transport struct {
+ // original -> bgp:transport-config
+ // Configuration parameters relating to the transport
+ // session(s) used for the BGP neighbor or group.
+ Config TransportConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> bgp:transport-state
+ // State information relating to the transport session(s)
+ // used for the BGP neighbor or group.
+ State TransportState `mapstructure:"state" json:"state,omitempty"`
+}
+
+func (lhs *Transport) Equal(rhs *Transport) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp:state.
+// State information relating to the timers used for the BGP
+// neighbor or group.
+type TimersState struct {
+ // original -> bgp:connect-retry
+ // bgp:connect-retry's original type is decimal64.
+ // Time interval in seconds between attempts to establish a
+ // session with the peer.
+ ConnectRetry float64 `mapstructure:"connect-retry" json:"connect-retry,omitempty"`
+ // original -> bgp:hold-time
+ // bgp:hold-time's original type is decimal64.
+ // Time interval in seconds that a BGP session will be
+ // considered active in the absence of keepalive or other
+ // messages from the peer. The hold-time is typically
+ // set to 3x the keepalive-interval.
+ HoldTime float64 `mapstructure:"hold-time" json:"hold-time,omitempty"`
+ // original -> bgp:keepalive-interval
+ // bgp:keepalive-interval's original type is decimal64.
+ // Time interval in seconds between transmission of keepalive
+ // messages to the neighbor. Typically set to 1/3 the
+ // hold-time.
+ KeepaliveInterval float64 `mapstructure:"keepalive-interval" json:"keepalive-interval,omitempty"`
+ // original -> bgp:minimum-advertisement-interval
+ // bgp:minimum-advertisement-interval's original type is decimal64.
+ // Minimum time which must elapse between subsequent UPDATE
+ // messages relating to a common set of NLRI being transmitted
+ // to a peer. This timer is referred to as
+ // MinRouteAdvertisementIntervalTimer by RFC 4721 and serves to
+ // reduce the number of UPDATE messages transmitted when a
+ // particular set of NLRI exhibit instability.
+ MinimumAdvertisementInterval float64 `mapstructure:"minimum-advertisement-interval" json:"minimum-advertisement-interval,omitempty"`
+ // original -> bgp-op:uptime
+ // bgp-op:uptime's original type is yang:timeticks.
+ // This timer determines the amount of time since the
+ // BGP last transitioned in or out of the Established
+ // state.
+ Uptime int64 `mapstructure:"uptime" json:"uptime,omitempty"`
+ // original -> bgp-op:negotiated-hold-time
+ // bgp-op:negotiated-hold-time's original type is decimal64.
+ // The negotiated hold-time for the BGP session.
+ NegotiatedHoldTime float64 `mapstructure:"negotiated-hold-time" json:"negotiated-hold-time,omitempty"`
+ // original -> gobgp:idle-hold-time-after-reset
+ // gobgp:idle-hold-time-after-reset's original type is decimal64.
+ // Time interval in seconds that a BGP session will be
+ // in idle state after neighbor reset operation.
+ IdleHoldTimeAfterReset float64 `mapstructure:"idle-hold-time-after-reset" json:"idle-hold-time-after-reset,omitempty"`
+ // original -> gobgp:downtime
+ // gobgp:downtime's original type is yang:timeticks.
+ // This timer determines the amount of time since the
+ // BGP last transitioned out of the Established state.
+ Downtime int64 `mapstructure:"downtime" json:"downtime,omitempty"`
+ // original -> gobgp:update-recv-time
+ // The number of seconds elasped since January 1, 1970 UTC
+ // last time the BGP session received an UPDATE message.
+ UpdateRecvTime int64 `mapstructure:"update-recv-time" json:"update-recv-time,omitempty"`
+}
+
+// struct for container bgp:config.
+// Configuration parameters relating to timers used for the
+// BGP neighbor or group.
+type TimersConfig struct {
+ // original -> bgp:connect-retry
+ // bgp:connect-retry's original type is decimal64.
+ // Time interval in seconds between attempts to establish a
+ // session with the peer.
+ ConnectRetry float64 `mapstructure:"connect-retry" json:"connect-retry,omitempty"`
+ // original -> bgp:hold-time
+ // bgp:hold-time's original type is decimal64.
+ // Time interval in seconds that a BGP session will be
+ // considered active in the absence of keepalive or other
+ // messages from the peer. The hold-time is typically
+ // set to 3x the keepalive-interval.
+ HoldTime float64 `mapstructure:"hold-time" json:"hold-time,omitempty"`
+ // original -> bgp:keepalive-interval
+ // bgp:keepalive-interval's original type is decimal64.
+ // Time interval in seconds between transmission of keepalive
+ // messages to the neighbor. Typically set to 1/3 the
+ // hold-time.
+ KeepaliveInterval float64 `mapstructure:"keepalive-interval" json:"keepalive-interval,omitempty"`
+ // original -> bgp:minimum-advertisement-interval
+ // bgp:minimum-advertisement-interval's original type is decimal64.
+ // Minimum time which must elapse between subsequent UPDATE
+ // messages relating to a common set of NLRI being transmitted
+ // to a peer. This timer is referred to as
+ // MinRouteAdvertisementIntervalTimer by RFC 4721 and serves to
+ // reduce the number of UPDATE messages transmitted when a
+ // particular set of NLRI exhibit instability.
+ MinimumAdvertisementInterval float64 `mapstructure:"minimum-advertisement-interval" json:"minimum-advertisement-interval,omitempty"`
+ // original -> gobgp:idle-hold-time-after-reset
+ // gobgp:idle-hold-time-after-reset's original type is decimal64.
+ // Time interval in seconds that a BGP session will be
+ // in idle state after neighbor reset operation.
+ IdleHoldTimeAfterReset float64 `mapstructure:"idle-hold-time-after-reset" json:"idle-hold-time-after-reset,omitempty"`
+}
+
+func (lhs *TimersConfig) Equal(rhs *TimersConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.ConnectRetry != rhs.ConnectRetry {
+ return false
+ }
+ if lhs.HoldTime != rhs.HoldTime {
+ return false
+ }
+ if lhs.KeepaliveInterval != rhs.KeepaliveInterval {
+ return false
+ }
+ if lhs.MinimumAdvertisementInterval != rhs.MinimumAdvertisementInterval {
+ return false
+ }
+ if lhs.IdleHoldTimeAfterReset != rhs.IdleHoldTimeAfterReset {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp:timers.
+// Timers related to a BGP neighbor or group.
+type Timers struct {
+ // original -> bgp:timers-config
+ // Configuration parameters relating to timers used for the
+ // BGP neighbor or group.
+ Config TimersConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> bgp:timers-state
+ // State information relating to the timers used for the BGP
+ // neighbor or group.
+ State TimersState `mapstructure:"state" json:"state,omitempty"`
+}
+
+func (lhs *Timers) Equal(rhs *Timers) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ return true
+}
+
+// struct for container gobgp:adj-table.
+type AdjTable struct {
+ // original -> gobgp:ADVERTISED
+ Advertised uint32 `mapstructure:"advertised" json:"advertised,omitempty"`
+ // original -> gobgp:FILTERED
+ Filtered uint32 `mapstructure:"filtered" json:"filtered,omitempty"`
+ // original -> gobgp:RECEIVED
+ Received uint32 `mapstructure:"received" json:"received,omitempty"`
+ // original -> gobgp:ACCEPTED
+ Accepted uint32 `mapstructure:"accepted" json:"accepted,omitempty"`
+}
+
+func (lhs *AdjTable) Equal(rhs *AdjTable) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.Advertised != rhs.Advertised {
+ return false
+ }
+ if lhs.Filtered != rhs.Filtered {
+ return false
+ }
+ if lhs.Received != rhs.Received {
+ return false
+ }
+ if lhs.Accepted != rhs.Accepted {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp:queues.
+// Counters related to queued messages associated with the
+// BGP neighbor.
+type Queues struct {
+ // original -> bgp-op:input
+ // The number of messages received from the peer currently
+ // queued.
+ Input uint32 `mapstructure:"input" json:"input,omitempty"`
+ // original -> bgp-op:output
+ // The number of messages queued to be sent to the peer.
+ Output uint32 `mapstructure:"output" json:"output,omitempty"`
+}
+
+func (lhs *Queues) Equal(rhs *Queues) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.Input != rhs.Input {
+ return false
+ }
+ if lhs.Output != rhs.Output {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp:received.
+// Counters for BGP messages received from the neighbor.
+type Received struct {
+ // original -> bgp-op:UPDATE
+ // Number of BGP UPDATE messages announcing, withdrawing
+ // or modifying paths exchanged.
+ Update uint64 `mapstructure:"update" json:"update,omitempty"`
+ // original -> bgp-op:NOTIFICATION
+ // Number of BGP NOTIFICATION messages indicating an
+ // error condition has occurred exchanged.
+ Notification uint64 `mapstructure:"notification" json:"notification,omitempty"`
+ // original -> gobgp:OPEN
+ // Number of BGP open messages announcing, withdrawing
+ // or modifying paths exchanged.
+ Open uint64 `mapstructure:"open" json:"open,omitempty"`
+ // original -> gobgp:REFRESH
+ // Number of BGP Route-Refresh messages indicating an
+ // error condition has occurred exchanged.
+ Refresh uint64 `mapstructure:"refresh" json:"refresh,omitempty"`
+ // original -> gobgp:KEEPALIVE
+ // Number of BGP Keepalive messages indicating an
+ // error condition has occurred exchanged.
+ Keepalive uint64 `mapstructure:"keepalive" json:"keepalive,omitempty"`
+ // original -> gobgp:DYNAMIC-CAP
+ // Number of BGP dynamic-cap messages indicating an
+ // error condition has occurred exchanged.
+ DynamicCap uint64 `mapstructure:"dynamic-cap" json:"dynamic-cap,omitempty"`
+ // original -> gobgp:WITHDRAW-UPDATE
+ // Number of updates subjected to treat-as-withdraw treatment.
+ WithdrawUpdate uint32 `mapstructure:"withdraw-update" json:"withdraw-update,omitempty"`
+ // original -> gobgp:WITHDRAW-PREFIX
+ // Number of prefixes subjected to treat-as-withdraw treatment.
+ WithdrawPrefix uint32 `mapstructure:"withdraw-prefix" json:"withdraw-prefix,omitempty"`
+ // original -> gobgp:DISCARDED
+ // Number of discarded messages indicating an
+ // error condition has occurred exchanged.
+ Discarded uint64 `mapstructure:"discarded" json:"discarded,omitempty"`
+ // original -> gobgp:TOTAL
+ // Number of total messages indicating an
+ // error condition has occurred exchanged.
+ Total uint64 `mapstructure:"total" json:"total,omitempty"`
+}
+
+func (lhs *Received) Equal(rhs *Received) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.Update != rhs.Update {
+ return false
+ }
+ if lhs.Notification != rhs.Notification {
+ return false
+ }
+ if lhs.Open != rhs.Open {
+ return false
+ }
+ if lhs.Refresh != rhs.Refresh {
+ return false
+ }
+ if lhs.Keepalive != rhs.Keepalive {
+ return false
+ }
+ if lhs.DynamicCap != rhs.DynamicCap {
+ return false
+ }
+ if lhs.WithdrawUpdate != rhs.WithdrawUpdate {
+ return false
+ }
+ if lhs.WithdrawPrefix != rhs.WithdrawPrefix {
+ return false
+ }
+ if lhs.Discarded != rhs.Discarded {
+ return false
+ }
+ if lhs.Total != rhs.Total {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp:sent.
+// Counters relating to BGP messages sent to the neighbor.
+type Sent struct {
+ // original -> bgp-op:UPDATE
+ // Number of BGP UPDATE messages announcing, withdrawing
+ // or modifying paths exchanged.
+ Update uint64 `mapstructure:"update" json:"update,omitempty"`
+ // original -> bgp-op:NOTIFICATION
+ // Number of BGP NOTIFICATION messages indicating an
+ // error condition has occurred exchanged.
+ Notification uint64 `mapstructure:"notification" json:"notification,omitempty"`
+ // original -> gobgp:OPEN
+ // Number of BGP open messages announcing, withdrawing
+ // or modifying paths exchanged.
+ Open uint64 `mapstructure:"open" json:"open,omitempty"`
+ // original -> gobgp:REFRESH
+ // Number of BGP Route-Refresh messages indicating an
+ // error condition has occurred exchanged.
+ Refresh uint64 `mapstructure:"refresh" json:"refresh,omitempty"`
+ // original -> gobgp:KEEPALIVE
+ // Number of BGP Keepalive messages indicating an
+ // error condition has occurred exchanged.
+ Keepalive uint64 `mapstructure:"keepalive" json:"keepalive,omitempty"`
+ // original -> gobgp:DYNAMIC-CAP
+ // Number of BGP dynamic-cap messages indicating an
+ // error condition has occurred exchanged.
+ DynamicCap uint64 `mapstructure:"dynamic-cap" json:"dynamic-cap,omitempty"`
+ // original -> gobgp:WITHDRAW-UPDATE
+ // Number of updates subjected to treat-as-withdraw treatment.
+ WithdrawUpdate uint32 `mapstructure:"withdraw-update" json:"withdraw-update,omitempty"`
+ // original -> gobgp:WITHDRAW-PREFIX
+ // Number of prefixes subjected to treat-as-withdraw treatment.
+ WithdrawPrefix uint32 `mapstructure:"withdraw-prefix" json:"withdraw-prefix,omitempty"`
+ // original -> gobgp:DISCARDED
+ // Number of discarded messages indicating an
+ // error condition has occurred exchanged.
+ Discarded uint64 `mapstructure:"discarded" json:"discarded,omitempty"`
+ // original -> gobgp:TOTAL
+ // Number of total messages indicating an
+ // error condition has occurred exchanged.
+ Total uint64 `mapstructure:"total" json:"total,omitempty"`
+}
+
+func (lhs *Sent) Equal(rhs *Sent) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.Update != rhs.Update {
+ return false
+ }
+ if lhs.Notification != rhs.Notification {
+ return false
+ }
+ if lhs.Open != rhs.Open {
+ return false
+ }
+ if lhs.Refresh != rhs.Refresh {
+ return false
+ }
+ if lhs.Keepalive != rhs.Keepalive {
+ return false
+ }
+ if lhs.DynamicCap != rhs.DynamicCap {
+ return false
+ }
+ if lhs.WithdrawUpdate != rhs.WithdrawUpdate {
+ return false
+ }
+ if lhs.WithdrawPrefix != rhs.WithdrawPrefix {
+ return false
+ }
+ if lhs.Discarded != rhs.Discarded {
+ return false
+ }
+ if lhs.Total != rhs.Total {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp:messages.
+// Counters for BGP messages sent and received from the
+// neighbor.
+type Messages struct {
+ // original -> bgp:sent
+ // Counters relating to BGP messages sent to the neighbor.
+ Sent Sent `mapstructure:"sent" json:"sent,omitempty"`
+ // original -> bgp:received
+ // Counters for BGP messages received from the neighbor.
+ Received Received `mapstructure:"received" json:"received,omitempty"`
+}
+
+func (lhs *Messages) Equal(rhs *Messages) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Sent.Equal(&(rhs.Sent)) {
+ return false
+ }
+ if !lhs.Received.Equal(&(rhs.Received)) {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp:state.
+// State information relating to the BGP neighbor or group.
+type NeighborState struct {
+ // original -> bgp:peer-as
+ // bgp:peer-as's original type is inet:as-number.
+ // AS number of the peer.
+ PeerAs uint32 `mapstructure:"peer-as" json:"peer-as,omitempty"`
+ // original -> bgp:local-as
+ // bgp:local-as's original type is inet:as-number.
+ // The local autonomous system number that is to be used
+ // when establishing sessions with the remote peer or peer
+ // group, if this differs from the global BGP router
+ // autonomous system number.
+ LocalAs uint32 `mapstructure:"local-as" json:"local-as,omitempty"`
+ // original -> bgp:peer-type
+ // Explicitly designate the peer or peer group as internal
+ // (iBGP) or external (eBGP).
+ PeerType PeerType `mapstructure:"peer-type" json:"peer-type,omitempty"`
+ // original -> bgp:auth-password
+ // Configures an MD5 authentication password for use with
+ // neighboring devices.
+ AuthPassword string `mapstructure:"auth-password" json:"auth-password,omitempty"`
+ // original -> bgp:remove-private-as
+ // Remove private AS numbers from updates sent to peers.
+ RemovePrivateAs RemovePrivateAsOption `mapstructure:"remove-private-as" json:"remove-private-as,omitempty"`
+ // original -> bgp:route-flap-damping
+ // bgp:route-flap-damping's original type is boolean.
+ // Enable route flap damping.
+ RouteFlapDamping bool `mapstructure:"route-flap-damping" json:"route-flap-damping,omitempty"`
+ // original -> bgp:send-community
+ // Specify which types of community should be sent to the
+ // neighbor or group. The default is to not send the
+ // community attribute.
+ SendCommunity CommunityType `mapstructure:"send-community" json:"send-community,omitempty"`
+ // original -> bgp:description
+ // An optional textual description (intended primarily for use
+ // with a peer or group.
+ Description string `mapstructure:"description" json:"description,omitempty"`
+ // original -> bgp:peer-group
+ // The peer-group with which this neighbor is associated.
+ PeerGroup string `mapstructure:"peer-group" json:"peer-group,omitempty"`
+ // original -> bgp:neighbor-address
+ // bgp:neighbor-address's original type is inet:ip-address.
+ // Address of the BGP peer, either in IPv4 or IPv6.
+ NeighborAddress string `mapstructure:"neighbor-address" json:"neighbor-address,omitempty"`
+ // original -> bgp-op:session-state
+ // Operational state of the BGP peer.
+ SessionState SessionState `mapstructure:"session-state" json:"session-state,omitempty"`
+ // original -> bgp-op:supported-capabilities
+ // BGP capabilities negotiated as supported with the peer.
+ SupportedCapabilitiesList []BgpCapability `mapstructure:"supported-capabilities-list" json:"supported-capabilities-list,omitempty"`
+ // original -> bgp:messages
+ // Counters for BGP messages sent and received from the
+ // neighbor.
+ Messages Messages `mapstructure:"messages" json:"messages,omitempty"`
+ // original -> bgp:queues
+ // Counters related to queued messages associated with the
+ // BGP neighbor.
+ Queues Queues `mapstructure:"queues" json:"queues,omitempty"`
+ // original -> gobgp:adj-table
+ AdjTable AdjTable `mapstructure:"adj-table" json:"adj-table,omitempty"`
+ // original -> gobgp:remote-capability
+ // original type is list of bgp-capability
+ RemoteCapabilityList []bgp.ParameterCapabilityInterface `mapstructure:"remote-capability-list" json:"remote-capability-list,omitempty"`
+ // original -> gobgp:local-capability
+ // original type is list of bgp-capability
+ LocalCapabilityList []bgp.ParameterCapabilityInterface `mapstructure:"local-capability-list" json:"local-capability-list,omitempty"`
+ // original -> gobgp:received-open-message
+ // gobgp:received-open-message's original type is bgp-open-message.
+ ReceivedOpenMessage *bgp.BGPMessage `mapstructure:"received-open-message" json:"received-open-message,omitempty"`
+ // original -> gobgp:admin-down
+ // gobgp:admin-down's original type is boolean.
+ // The state of administrative operation. If the state is true, it indicates the neighbor is disabled by the administrator.
+ AdminDown bool `mapstructure:"admin-down" json:"admin-down,omitempty"`
+ // original -> gobgp:admin-state
+ AdminState AdminState `mapstructure:"admin-state" json:"admin-state,omitempty"`
+ // original -> gobgp:established-count
+ // The number of how many the peer became established state.
+ EstablishedCount uint32 `mapstructure:"established-count" json:"established-count,omitempty"`
+ // original -> gobgp:flops
+ // The number of flip-flops.
+ Flops uint32 `mapstructure:"flops" json:"flops,omitempty"`
+ // original -> gobgp:neighbor-interface
+ NeighborInterface string `mapstructure:"neighbor-interface" json:"neighbor-interface,omitempty"`
+ // original -> gobgp:vrf
+ Vrf string `mapstructure:"vrf" json:"vrf,omitempty"`
+ // original -> gobgp:remote-router-id
+ RemoteRouterId string `mapstructure:"remote-router-id" json:"remote-router-id,omitempty"`
+}
+
+// struct for container bgp:config.
+// Configuration parameters relating to the BGP neighbor or
+// group.
+type NeighborConfig struct {
+ // original -> bgp:peer-as
+ // bgp:peer-as's original type is inet:as-number.
+ // AS number of the peer.
+ PeerAs uint32 `mapstructure:"peer-as" json:"peer-as,omitempty"`
+ // original -> bgp:local-as
+ // bgp:local-as's original type is inet:as-number.
+ // The local autonomous system number that is to be used
+ // when establishing sessions with the remote peer or peer
+ // group, if this differs from the global BGP router
+ // autonomous system number.
+ LocalAs uint32 `mapstructure:"local-as" json:"local-as,omitempty"`
+ // original -> bgp:peer-type
+ // Explicitly designate the peer or peer group as internal
+ // (iBGP) or external (eBGP).
+ PeerType PeerType `mapstructure:"peer-type" json:"peer-type,omitempty"`
+ // original -> bgp:auth-password
+ // Configures an MD5 authentication password for use with
+ // neighboring devices.
+ AuthPassword string `mapstructure:"auth-password" json:"auth-password,omitempty"`
+ // original -> bgp:remove-private-as
+ // Remove private AS numbers from updates sent to peers.
+ RemovePrivateAs RemovePrivateAsOption `mapstructure:"remove-private-as" json:"remove-private-as,omitempty"`
+ // original -> bgp:route-flap-damping
+ // bgp:route-flap-damping's original type is boolean.
+ // Enable route flap damping.
+ RouteFlapDamping bool `mapstructure:"route-flap-damping" json:"route-flap-damping,omitempty"`
+ // original -> bgp:send-community
+ // Specify which types of community should be sent to the
+ // neighbor or group. The default is to not send the
+ // community attribute.
+ SendCommunity CommunityType `mapstructure:"send-community" json:"send-community,omitempty"`
+ // original -> bgp:description
+ // An optional textual description (intended primarily for use
+ // with a peer or group.
+ Description string `mapstructure:"description" json:"description,omitempty"`
+ // original -> bgp:peer-group
+ // The peer-group with which this neighbor is associated.
+ PeerGroup string `mapstructure:"peer-group" json:"peer-group,omitempty"`
+ // original -> bgp:neighbor-address
+ // bgp:neighbor-address's original type is inet:ip-address.
+ // Address of the BGP peer, either in IPv4 or IPv6.
+ NeighborAddress string `mapstructure:"neighbor-address" json:"neighbor-address,omitempty"`
+ // original -> gobgp:admin-down
+ // gobgp:admin-down's original type is boolean.
+ // The config of administrative operation. If state, indicates the neighbor is disabled by the administrator.
+ AdminDown bool `mapstructure:"admin-down" json:"admin-down,omitempty"`
+ // original -> gobgp:neighbor-interface
+ NeighborInterface string `mapstructure:"neighbor-interface" json:"neighbor-interface,omitempty"`
+ // original -> gobgp:vrf
+ Vrf string `mapstructure:"vrf" json:"vrf,omitempty"`
+}
+
+func (lhs *NeighborConfig) Equal(rhs *NeighborConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.PeerAs != rhs.PeerAs {
+ return false
+ }
+ if lhs.LocalAs != rhs.LocalAs {
+ return false
+ }
+ if lhs.PeerType != rhs.PeerType {
+ return false
+ }
+ if lhs.AuthPassword != rhs.AuthPassword {
+ return false
+ }
+ if lhs.RemovePrivateAs != rhs.RemovePrivateAs {
+ return false
+ }
+ if lhs.RouteFlapDamping != rhs.RouteFlapDamping {
+ return false
+ }
+ if lhs.SendCommunity != rhs.SendCommunity {
+ return false
+ }
+ if lhs.Description != rhs.Description {
+ return false
+ }
+ if lhs.PeerGroup != rhs.PeerGroup {
+ return false
+ }
+ if lhs.NeighborAddress != rhs.NeighborAddress {
+ return false
+ }
+ if lhs.AdminDown != rhs.AdminDown {
+ return false
+ }
+ if lhs.NeighborInterface != rhs.NeighborInterface {
+ return false
+ }
+ if lhs.Vrf != rhs.Vrf {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp:neighbor.
+// List of BGP neighbors configured on the local system,
+// uniquely identified by peer IPv[46] address.
+type Neighbor struct {
+ // original -> bgp:neighbor-address
+ // original -> bgp:neighbor-config
+ // Configuration parameters relating to the BGP neighbor or
+ // group.
+ Config NeighborConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> bgp:neighbor-state
+ // State information relating to the BGP neighbor or group.
+ State NeighborState `mapstructure:"state" json:"state,omitempty"`
+ // original -> bgp:timers
+ // Timers related to a BGP neighbor or group.
+ Timers Timers `mapstructure:"timers" json:"timers,omitempty"`
+ // original -> bgp:transport
+ // Transport session parameters for the BGP neighbor or group.
+ Transport Transport `mapstructure:"transport" json:"transport,omitempty"`
+ // original -> bgp:error-handling
+ // Error handling parameters used for the BGP neighbor or
+ // group.
+ ErrorHandling ErrorHandling `mapstructure:"error-handling" json:"error-handling,omitempty"`
+ // original -> bgp:logging-options
+ // Logging options for events related to the BGP neighbor or
+ // group.
+ LoggingOptions LoggingOptions `mapstructure:"logging-options" json:"logging-options,omitempty"`
+ // original -> bgp:ebgp-multihop
+ // eBGP multi-hop parameters for the BGP neighbor or group.
+ EbgpMultihop EbgpMultihop `mapstructure:"ebgp-multihop" json:"ebgp-multihop,omitempty"`
+ // original -> bgp:route-reflector
+ // Route reflector parameters for the BGP neighbor or group.
+ RouteReflector RouteReflector `mapstructure:"route-reflector" json:"route-reflector,omitempty"`
+ // original -> bgp:as-path-options
+ // AS_PATH manipulation parameters for the BGP neighbor or
+ // group.
+ AsPathOptions AsPathOptions `mapstructure:"as-path-options" json:"as-path-options,omitempty"`
+ // original -> bgp:add-paths
+ // Parameters relating to the advertisement and receipt of
+ // multiple paths for a single NLRI (add-paths).
+ AddPaths AddPaths `mapstructure:"add-paths" json:"add-paths,omitempty"`
+ // original -> bgp:afi-safis
+ // Per-address-family configuration parameters associated with
+ // the neighbor or group.
+ AfiSafis []AfiSafi `mapstructure:"afi-safis" json:"afi-safis,omitempty"`
+ // original -> bgp:graceful-restart
+ // Parameters relating the graceful restart mechanism for BGP.
+ GracefulRestart GracefulRestart `mapstructure:"graceful-restart" json:"graceful-restart,omitempty"`
+ // original -> rpol:apply-policy
+ // Anchor point for routing policies in the model.
+ // Import and export policies are with respect to the local
+ // routing table, i.e., export (send) and import (receive),
+ // depending on the context.
+ ApplyPolicy ApplyPolicy `mapstructure:"apply-policy" json:"apply-policy,omitempty"`
+ // original -> bgp-mp:use-multiple-paths
+ // Parameters related to the use of multiple-paths for the same
+ // NLRI when they are received only from this neighbor.
+ UseMultiplePaths UseMultiplePaths `mapstructure:"use-multiple-paths" json:"use-multiple-paths,omitempty"`
+ // original -> gobgp:route-server
+ // Configure the local router as a route server.
+ RouteServer RouteServer `mapstructure:"route-server" json:"route-server,omitempty"`
+ // original -> gobgp:ttl-security
+ // Configure TTL Security feature.
+ TtlSecurity TtlSecurity `mapstructure:"ttl-security" json:"ttl-security,omitempty"`
+}
+
+func (lhs *Neighbor) Equal(rhs *Neighbor) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ if !lhs.Timers.Equal(&(rhs.Timers)) {
+ return false
+ }
+ if !lhs.Transport.Equal(&(rhs.Transport)) {
+ return false
+ }
+ if !lhs.ErrorHandling.Equal(&(rhs.ErrorHandling)) {
+ return false
+ }
+ if !lhs.LoggingOptions.Equal(&(rhs.LoggingOptions)) {
+ return false
+ }
+ if !lhs.EbgpMultihop.Equal(&(rhs.EbgpMultihop)) {
+ return false
+ }
+ if !lhs.RouteReflector.Equal(&(rhs.RouteReflector)) {
+ return false
+ }
+ if !lhs.AsPathOptions.Equal(&(rhs.AsPathOptions)) {
+ return false
+ }
+ if !lhs.AddPaths.Equal(&(rhs.AddPaths)) {
+ return false
+ }
+ if len(lhs.AfiSafis) != len(rhs.AfiSafis) {
+ return false
+ }
+ {
+ lmap := make(map[string]*AfiSafi)
+ for i, l := range lhs.AfiSafis {
+ lmap[mapkey(i, string(l.Config.AfiSafiName))] = &lhs.AfiSafis[i]
+ }
+ for i, r := range rhs.AfiSafis {
+ if l, y := lmap[mapkey(i, string(r.Config.AfiSafiName))]; !y {
+ return false
+ } else if !r.Equal(l) {
+ return false
+ }
+ }
+ }
+ if !lhs.GracefulRestart.Equal(&(rhs.GracefulRestart)) {
+ return false
+ }
+ if !lhs.ApplyPolicy.Equal(&(rhs.ApplyPolicy)) {
+ return false
+ }
+ if !lhs.UseMultiplePaths.Equal(&(rhs.UseMultiplePaths)) {
+ return false
+ }
+ if !lhs.RouteServer.Equal(&(rhs.RouteServer)) {
+ return false
+ }
+ if !lhs.TtlSecurity.Equal(&(rhs.TtlSecurity)) {
+ return false
+ }
+ return true
+}
+
+// struct for container gobgp:state.
+type LongLivedGracefulRestartState struct {
+ // original -> gobgp:enabled
+ // gobgp:enabled's original type is boolean.
+ Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"`
+ // original -> gobgp:received
+ // gobgp:received's original type is boolean.
+ Received bool `mapstructure:"received" json:"received,omitempty"`
+ // original -> gobgp:advertised
+ // gobgp:advertised's original type is boolean.
+ Advertised bool `mapstructure:"advertised" json:"advertised,omitempty"`
+ // original -> gobgp:peer-restart-time
+ PeerRestartTime uint32 `mapstructure:"peer-restart-time" json:"peer-restart-time,omitempty"`
+ // original -> gobgp:peer-restart-timer-expired
+ // gobgp:peer-restart-timer-expired's original type is boolean.
+ PeerRestartTimerExpired bool `mapstructure:"peer-restart-timer-expired" json:"peer-restart-timer-expired,omitempty"`
+}
+
+// struct for container gobgp:config.
+type LongLivedGracefulRestartConfig struct {
+ // original -> gobgp:enabled
+ // gobgp:enabled's original type is boolean.
+ Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"`
+ // original -> gobgp:restart-time
+ RestartTime uint32 `mapstructure:"restart-time" json:"restart-time,omitempty"`
+}
+
+func (lhs *LongLivedGracefulRestartConfig) Equal(rhs *LongLivedGracefulRestartConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.Enabled != rhs.Enabled {
+ return false
+ }
+ if lhs.RestartTime != rhs.RestartTime {
+ return false
+ }
+ return true
+}
+
+// struct for container gobgp:long-lived-graceful-restart.
+type LongLivedGracefulRestart struct {
+ // original -> gobgp:long-lived-graceful-restart-config
+ Config LongLivedGracefulRestartConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> gobgp:long-lived-graceful-restart-state
+ State LongLivedGracefulRestartState `mapstructure:"state" json:"state,omitempty"`
+}
+
+func (lhs *LongLivedGracefulRestart) Equal(rhs *LongLivedGracefulRestart) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ return true
+}
+
+// struct for container gobgp:state.
+type RouteTargetMembershipState struct {
+ // original -> gobgp:deferral-time
+ DeferralTime uint16 `mapstructure:"deferral-time" json:"deferral-time,omitempty"`
+}
+
+// struct for container gobgp:config.
+type RouteTargetMembershipConfig struct {
+ // original -> gobgp:deferral-time
+ DeferralTime uint16 `mapstructure:"deferral-time" json:"deferral-time,omitempty"`
+}
+
+func (lhs *RouteTargetMembershipConfig) Equal(rhs *RouteTargetMembershipConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.DeferralTime != rhs.DeferralTime {
+ return false
+ }
+ return true
+}
+
+// struct for container gobgp:route-target-membership.
+type RouteTargetMembership struct {
+ // original -> gobgp:route-target-membership-config
+ Config RouteTargetMembershipConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> gobgp:route-target-membership-state
+ State RouteTargetMembershipState `mapstructure:"state" json:"state,omitempty"`
+}
+
+func (lhs *RouteTargetMembership) Equal(rhs *RouteTargetMembership) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-mp:l2vpn-evpn.
+// BGP EVPN configuration options.
+type L2vpnEvpn struct {
+ // original -> bgp-mp:prefix-limit
+ // Configure the maximum number of prefixes that will be
+ // accepted from a peer.
+ PrefixLimit PrefixLimit `mapstructure:"prefix-limit" json:"prefix-limit,omitempty"`
+}
+
+func (lhs *L2vpnEvpn) Equal(rhs *L2vpnEvpn) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.PrefixLimit.Equal(&(rhs.PrefixLimit)) {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-mp:l2vpn-vpls.
+// BGP-signalled VPLS configuration options.
+type L2vpnVpls struct {
+ // original -> bgp-mp:prefix-limit
+ // Configure the maximum number of prefixes that will be
+ // accepted from a peer.
+ PrefixLimit PrefixLimit `mapstructure:"prefix-limit" json:"prefix-limit,omitempty"`
+}
+
+func (lhs *L2vpnVpls) Equal(rhs *L2vpnVpls) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.PrefixLimit.Equal(&(rhs.PrefixLimit)) {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-mp:l3vpn-ipv6-multicast.
+// Multicast IPv6 L3VPN configuration options.
+type L3vpnIpv6Multicast struct {
+ // original -> bgp-mp:prefix-limit
+ // Configure the maximum number of prefixes that will be
+ // accepted from a peer.
+ PrefixLimit PrefixLimit `mapstructure:"prefix-limit" json:"prefix-limit,omitempty"`
+}
+
+func (lhs *L3vpnIpv6Multicast) Equal(rhs *L3vpnIpv6Multicast) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.PrefixLimit.Equal(&(rhs.PrefixLimit)) {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-mp:l3vpn-ipv4-multicast.
+// Multicast IPv4 L3VPN configuration options.
+type L3vpnIpv4Multicast struct {
+ // original -> bgp-mp:prefix-limit
+ // Configure the maximum number of prefixes that will be
+ // accepted from a peer.
+ PrefixLimit PrefixLimit `mapstructure:"prefix-limit" json:"prefix-limit,omitempty"`
+}
+
+func (lhs *L3vpnIpv4Multicast) Equal(rhs *L3vpnIpv4Multicast) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.PrefixLimit.Equal(&(rhs.PrefixLimit)) {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-mp:l3vpn-ipv6-unicast.
+// Unicast IPv6 L3VPN configuration options.
+type L3vpnIpv6Unicast struct {
+ // original -> bgp-mp:prefix-limit
+ // Configure the maximum number of prefixes that will be
+ // accepted from a peer.
+ PrefixLimit PrefixLimit `mapstructure:"prefix-limit" json:"prefix-limit,omitempty"`
+}
+
+func (lhs *L3vpnIpv6Unicast) Equal(rhs *L3vpnIpv6Unicast) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.PrefixLimit.Equal(&(rhs.PrefixLimit)) {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-mp:l3vpn-ipv4-unicast.
+// Unicast IPv4 L3VPN configuration options.
+type L3vpnIpv4Unicast struct {
+ // original -> bgp-mp:prefix-limit
+ // Configure the maximum number of prefixes that will be
+ // accepted from a peer.
+ PrefixLimit PrefixLimit `mapstructure:"prefix-limit" json:"prefix-limit,omitempty"`
+}
+
+func (lhs *L3vpnIpv4Unicast) Equal(rhs *L3vpnIpv4Unicast) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.PrefixLimit.Equal(&(rhs.PrefixLimit)) {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-mp:ipv6-labelled-unicast.
+// IPv6 Labelled Unicast configuration options.
+type Ipv6LabelledUnicast struct {
+ // original -> bgp-mp:prefix-limit
+ // Configure the maximum number of prefixes that will be
+ // accepted from a peer.
+ PrefixLimit PrefixLimit `mapstructure:"prefix-limit" json:"prefix-limit,omitempty"`
+}
+
+func (lhs *Ipv6LabelledUnicast) Equal(rhs *Ipv6LabelledUnicast) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.PrefixLimit.Equal(&(rhs.PrefixLimit)) {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-mp:ipv4-labelled-unicast.
+// IPv4 Labelled Unicast configuration options.
+type Ipv4LabelledUnicast struct {
+ // original -> bgp-mp:prefix-limit
+ // Configure the maximum number of prefixes that will be
+ // accepted from a peer.
+ PrefixLimit PrefixLimit `mapstructure:"prefix-limit" json:"prefix-limit,omitempty"`
+}
+
+func (lhs *Ipv4LabelledUnicast) Equal(rhs *Ipv4LabelledUnicast) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.PrefixLimit.Equal(&(rhs.PrefixLimit)) {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-mp:state.
+// State information for common IPv4 and IPv6 unicast
+// parameters.
+type Ipv6UnicastState struct {
+ // original -> bgp-mp:send-default-route
+ // bgp-mp:send-default-route's original type is boolean.
+ // If set to true, send the default-route to the neighbour(s).
+ SendDefaultRoute bool `mapstructure:"send-default-route" json:"send-default-route,omitempty"`
+}
+
+// struct for container bgp-mp:config.
+// Configuration parameters for common IPv4 and IPv6 unicast
+// AFI-SAFI options.
+type Ipv6UnicastConfig struct {
+ // original -> bgp-mp:send-default-route
+ // bgp-mp:send-default-route's original type is boolean.
+ // If set to true, send the default-route to the neighbour(s).
+ SendDefaultRoute bool `mapstructure:"send-default-route" json:"send-default-route,omitempty"`
+}
+
+func (lhs *Ipv6UnicastConfig) Equal(rhs *Ipv6UnicastConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.SendDefaultRoute != rhs.SendDefaultRoute {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-mp:ipv6-unicast.
+// IPv6 unicast configuration options.
+type Ipv6Unicast struct {
+ // original -> bgp-mp:prefix-limit
+ // Configure the maximum number of prefixes that will be
+ // accepted from a peer.
+ PrefixLimit PrefixLimit `mapstructure:"prefix-limit" json:"prefix-limit,omitempty"`
+ // original -> bgp-mp:ipv6-unicast-config
+ // Configuration parameters for common IPv4 and IPv6 unicast
+ // AFI-SAFI options.
+ Config Ipv6UnicastConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> bgp-mp:ipv6-unicast-state
+ // State information for common IPv4 and IPv6 unicast
+ // parameters.
+ State Ipv6UnicastState `mapstructure:"state" json:"state,omitempty"`
+}
+
+func (lhs *Ipv6Unicast) Equal(rhs *Ipv6Unicast) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.PrefixLimit.Equal(&(rhs.PrefixLimit)) {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-mp:state.
+// State information for common IPv4 and IPv6 unicast
+// parameters.
+type Ipv4UnicastState struct {
+ // original -> bgp-mp:send-default-route
+ // bgp-mp:send-default-route's original type is boolean.
+ // If set to true, send the default-route to the neighbour(s).
+ SendDefaultRoute bool `mapstructure:"send-default-route" json:"send-default-route,omitempty"`
+}
+
+// struct for container bgp-mp:config.
+// Configuration parameters for common IPv4 and IPv6 unicast
+// AFI-SAFI options.
+type Ipv4UnicastConfig struct {
+ // original -> bgp-mp:send-default-route
+ // bgp-mp:send-default-route's original type is boolean.
+ // If set to true, send the default-route to the neighbour(s).
+ SendDefaultRoute bool `mapstructure:"send-default-route" json:"send-default-route,omitempty"`
+}
+
+func (lhs *Ipv4UnicastConfig) Equal(rhs *Ipv4UnicastConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.SendDefaultRoute != rhs.SendDefaultRoute {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-mp:state.
+// State information relating to the prefix-limit for the
+// AFI-SAFI.
+type PrefixLimitState struct {
+ // original -> bgp-mp:max-prefixes
+ // Maximum number of prefixes that will be accepted
+ // from the neighbour.
+ MaxPrefixes uint32 `mapstructure:"max-prefixes" json:"max-prefixes,omitempty"`
+ // original -> bgp-mp:shutdown-threshold-pct
+ // Threshold on number of prefixes that can be received
+ // from a neighbour before generation of warning messages
+ // or log entries. Expressed as a percentage of
+ // max-prefixes.
+ ShutdownThresholdPct Percentage `mapstructure:"shutdown-threshold-pct" json:"shutdown-threshold-pct,omitempty"`
+ // original -> bgp-mp:restart-timer
+ // bgp-mp:restart-timer's original type is decimal64.
+ // Time interval in seconds after which the BGP session
+ // is re-established after being torn down due to exceeding
+ // the max-prefix limit.
+ RestartTimer float64 `mapstructure:"restart-timer" json:"restart-timer,omitempty"`
+}
+
+// struct for container bgp-mp:config.
+// Configuration parameters relating to the prefix
+// limit for the AFI-SAFI.
+type PrefixLimitConfig struct {
+ // original -> bgp-mp:max-prefixes
+ // Maximum number of prefixes that will be accepted
+ // from the neighbour.
+ MaxPrefixes uint32 `mapstructure:"max-prefixes" json:"max-prefixes,omitempty"`
+ // original -> bgp-mp:shutdown-threshold-pct
+ // Threshold on number of prefixes that can be received
+ // from a neighbour before generation of warning messages
+ // or log entries. Expressed as a percentage of
+ // max-prefixes.
+ ShutdownThresholdPct Percentage `mapstructure:"shutdown-threshold-pct" json:"shutdown-threshold-pct,omitempty"`
+ // original -> bgp-mp:restart-timer
+ // bgp-mp:restart-timer's original type is decimal64.
+ // Time interval in seconds after which the BGP session
+ // is re-established after being torn down due to exceeding
+ // the max-prefix limit.
+ RestartTimer float64 `mapstructure:"restart-timer" json:"restart-timer,omitempty"`
+}
+
+func (lhs *PrefixLimitConfig) Equal(rhs *PrefixLimitConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.MaxPrefixes != rhs.MaxPrefixes {
+ return false
+ }
+ if lhs.ShutdownThresholdPct != rhs.ShutdownThresholdPct {
+ return false
+ }
+ if lhs.RestartTimer != rhs.RestartTimer {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-mp:prefix-limit.
+// Configure the maximum number of prefixes that will be
+// accepted from a peer.
+type PrefixLimit struct {
+ // original -> bgp-mp:prefix-limit-config
+ // Configuration parameters relating to the prefix
+ // limit for the AFI-SAFI.
+ Config PrefixLimitConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> bgp-mp:prefix-limit-state
+ // State information relating to the prefix-limit for the
+ // AFI-SAFI.
+ State PrefixLimitState `mapstructure:"state" json:"state,omitempty"`
+}
+
+func (lhs *PrefixLimit) Equal(rhs *PrefixLimit) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-mp:ipv4-unicast.
+// IPv4 unicast configuration options.
+type Ipv4Unicast struct {
+ // original -> bgp-mp:prefix-limit
+ // Configure the maximum number of prefixes that will be
+ // accepted from a peer.
+ PrefixLimit PrefixLimit `mapstructure:"prefix-limit" json:"prefix-limit,omitempty"`
+ // original -> bgp-mp:ipv4-unicast-config
+ // Configuration parameters for common IPv4 and IPv6 unicast
+ // AFI-SAFI options.
+ Config Ipv4UnicastConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> bgp-mp:ipv4-unicast-state
+ // State information for common IPv4 and IPv6 unicast
+ // parameters.
+ State Ipv4UnicastState `mapstructure:"state" json:"state,omitempty"`
+}
+
+func (lhs *Ipv4Unicast) Equal(rhs *Ipv4Unicast) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.PrefixLimit.Equal(&(rhs.PrefixLimit)) {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ return true
+}
+
+// struct for container rpol:state.
+// Operational state for routing policy.
+type ApplyPolicyState struct {
+ // original -> rpol:import-policy
+ // list of policy names in sequence to be applied on
+ // receiving a routing update in the current context, e.g.,
+ // for the current peer group, neighbor, address family,
+ // etc.
+ ImportPolicyList []string `mapstructure:"import-policy-list" json:"import-policy-list,omitempty"`
+ // original -> rpol:default-import-policy
+ // explicitly set a default policy if no policy definition
+ // in the import policy chain is satisfied.
+ DefaultImportPolicy DefaultPolicyType `mapstructure:"default-import-policy" json:"default-import-policy,omitempty"`
+ // original -> rpol:export-policy
+ // list of policy names in sequence to be applied on
+ // sending a routing update in the current context, e.g.,
+ // for the current peer group, neighbor, address family,
+ // etc.
+ ExportPolicyList []string `mapstructure:"export-policy-list" json:"export-policy-list,omitempty"`
+ // original -> rpol:default-export-policy
+ // explicitly set a default policy if no policy definition
+ // in the export policy chain is satisfied.
+ DefaultExportPolicy DefaultPolicyType `mapstructure:"default-export-policy" json:"default-export-policy,omitempty"`
+ // original -> gobgp:in-policy
+ // list of policy names in sequence to be applied on
+ // sending a routing update in the current context, e.g.,
+ // for the current other route server clients.
+ InPolicyList []string `mapstructure:"in-policy-list" json:"in-policy-list,omitempty"`
+ // original -> gobgp:default-in-policy
+ // explicitly set a default policy if no policy definition
+ // in the in-policy chain is satisfied.
+ DefaultInPolicy DefaultPolicyType `mapstructure:"default-in-policy" json:"default-in-policy,omitempty"`
+}
+
+// struct for container rpol:config.
+// Policy configuration data.
+type ApplyPolicyConfig struct {
+ // original -> rpol:import-policy
+ // list of policy names in sequence to be applied on
+ // receiving a routing update in the current context, e.g.,
+ // for the current peer group, neighbor, address family,
+ // etc.
+ ImportPolicyList []string `mapstructure:"import-policy-list" json:"import-policy-list,omitempty"`
+ // original -> rpol:default-import-policy
+ // explicitly set a default policy if no policy definition
+ // in the import policy chain is satisfied.
+ DefaultImportPolicy DefaultPolicyType `mapstructure:"default-import-policy" json:"default-import-policy,omitempty"`
+ // original -> rpol:export-policy
+ // list of policy names in sequence to be applied on
+ // sending a routing update in the current context, e.g.,
+ // for the current peer group, neighbor, address family,
+ // etc.
+ ExportPolicyList []string `mapstructure:"export-policy-list" json:"export-policy-list,omitempty"`
+ // original -> rpol:default-export-policy
+ // explicitly set a default policy if no policy definition
+ // in the export policy chain is satisfied.
+ DefaultExportPolicy DefaultPolicyType `mapstructure:"default-export-policy" json:"default-export-policy,omitempty"`
+ // original -> gobgp:in-policy
+ // list of policy names in sequence to be applied on
+ // sending a routing update in the current context, e.g.,
+ // for the current other route server clients.
+ InPolicyList []string `mapstructure:"in-policy-list" json:"in-policy-list,omitempty"`
+ // original -> gobgp:default-in-policy
+ // explicitly set a default policy if no policy definition
+ // in the in-policy chain is satisfied.
+ DefaultInPolicy DefaultPolicyType `mapstructure:"default-in-policy" json:"default-in-policy,omitempty"`
+}
+
+func (lhs *ApplyPolicyConfig) Equal(rhs *ApplyPolicyConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if len(lhs.ImportPolicyList) != len(rhs.ImportPolicyList) {
+ return false
+ }
+ for idx, l := range lhs.ImportPolicyList {
+ if l != rhs.ImportPolicyList[idx] {
+ return false
+ }
+ }
+ if lhs.DefaultImportPolicy != rhs.DefaultImportPolicy {
+ return false
+ }
+ if len(lhs.ExportPolicyList) != len(rhs.ExportPolicyList) {
+ return false
+ }
+ for idx, l := range lhs.ExportPolicyList {
+ if l != rhs.ExportPolicyList[idx] {
+ return false
+ }
+ }
+ if lhs.DefaultExportPolicy != rhs.DefaultExportPolicy {
+ return false
+ }
+ if len(lhs.InPolicyList) != len(rhs.InPolicyList) {
+ return false
+ }
+ for idx, l := range lhs.InPolicyList {
+ if l != rhs.InPolicyList[idx] {
+ return false
+ }
+ }
+ if lhs.DefaultInPolicy != rhs.DefaultInPolicy {
+ return false
+ }
+ return true
+}
+
+// struct for container rpol:apply-policy.
+// Anchor point for routing policies in the model.
+// Import and export policies are with respect to the local
+// routing table, i.e., export (send) and import (receive),
+// depending on the context.
+type ApplyPolicy struct {
+ // original -> rpol:apply-policy-config
+ // Policy configuration data.
+ Config ApplyPolicyConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> rpol:apply-policy-state
+ // Operational state for routing policy.
+ State ApplyPolicyState `mapstructure:"state" json:"state,omitempty"`
+}
+
+func (lhs *ApplyPolicy) Equal(rhs *ApplyPolicy) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-mp:state.
+// State information relating to the AFI-SAFI.
+type AfiSafiState struct {
+ // original -> bgp-mp:afi-safi-name
+ // AFI,SAFI.
+ AfiSafiName AfiSafiType `mapstructure:"afi-safi-name" json:"afi-safi-name,omitempty"`
+ // original -> bgp-mp:enabled
+ // bgp-mp:enabled's original type is boolean.
+ // This leaf indicates whether the IPv4 Unicast AFI,SAFI is
+ // enabled for the neighbour or group.
+ Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"`
+ // original -> bgp-op:total-paths
+ // Total number of BGP paths within the context.
+ TotalPaths uint32 `mapstructure:"total-paths" json:"total-paths,omitempty"`
+ // original -> bgp-op:total-prefixes
+ // .
+ TotalPrefixes uint32 `mapstructure:"total-prefixes" json:"total-prefixes,omitempty"`
+ // original -> gobgp:family
+ // gobgp:family's original type is route-family.
+ // Address family value of AFI-SAFI pair translated from afi-safi-name.
+ Family bgp.RouteFamily `mapstructure:"family" json:"family,omitempty"`
+}
+
+// struct for container bgp-mp:config.
+// Configuration parameters for the AFI-SAFI.
+type AfiSafiConfig struct {
+ // original -> bgp-mp:afi-safi-name
+ // AFI,SAFI.
+ AfiSafiName AfiSafiType `mapstructure:"afi-safi-name" json:"afi-safi-name,omitempty"`
+ // original -> bgp-mp:enabled
+ // bgp-mp:enabled's original type is boolean.
+ // This leaf indicates whether the IPv4 Unicast AFI,SAFI is
+ // enabled for the neighbour or group.
+ Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"`
+}
+
+func (lhs *AfiSafiConfig) Equal(rhs *AfiSafiConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.AfiSafiName != rhs.AfiSafiName {
+ return false
+ }
+ if lhs.Enabled != rhs.Enabled {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-mp:state.
+// State information for BGP graceful-restart.
+type MpGracefulRestartState struct {
+ // original -> bgp-mp:enabled
+ // bgp-mp:enabled's original type is boolean.
+ // This leaf indicates whether graceful-restart is enabled for
+ // this AFI-SAFI.
+ Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"`
+ // original -> bgp-op:received
+ // bgp-op:received's original type is boolean.
+ // This leaf indicates whether the neighbor advertised the
+ // ability to support graceful-restart for this AFI-SAFI.
+ Received bool `mapstructure:"received" json:"received,omitempty"`
+ // original -> bgp-op:advertised
+ // bgp-op:advertised's original type is boolean.
+ // This leaf indicates whether the ability to support
+ // graceful-restart has been advertised to the peer.
+ Advertised bool `mapstructure:"advertised" json:"advertised,omitempty"`
+ // original -> gobgp:end-of-rib-received
+ // gobgp:end-of-rib-received's original type is boolean.
+ EndOfRibReceived bool `mapstructure:"end-of-rib-received" json:"end-of-rib-received,omitempty"`
+ // original -> gobgp:end-of-rib-sent
+ // gobgp:end-of-rib-sent's original type is boolean.
+ EndOfRibSent bool `mapstructure:"end-of-rib-sent" json:"end-of-rib-sent,omitempty"`
+}
+
+// struct for container bgp-mp:config.
+// Configuration options for BGP graceful-restart.
+type MpGracefulRestartConfig struct {
+ // original -> bgp-mp:enabled
+ // bgp-mp:enabled's original type is boolean.
+ // This leaf indicates whether graceful-restart is enabled for
+ // this AFI-SAFI.
+ Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"`
+}
+
+func (lhs *MpGracefulRestartConfig) Equal(rhs *MpGracefulRestartConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.Enabled != rhs.Enabled {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-mp:graceful-restart.
+// Parameters relating to BGP graceful-restart.
+type MpGracefulRestart struct {
+ // original -> bgp-mp:mp-graceful-restart-config
+ // Configuration options for BGP graceful-restart.
+ Config MpGracefulRestartConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> bgp-mp:mp-graceful-restart-state
+ // State information for BGP graceful-restart.
+ State MpGracefulRestartState `mapstructure:"state" json:"state,omitempty"`
+}
+
+func (lhs *MpGracefulRestart) Equal(rhs *MpGracefulRestart) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-mp:afi-safi.
+// AFI,SAFI configuration available for the
+// neighbour or group.
+type AfiSafi struct {
+ // original -> bgp-mp:afi-safi-name
+ // original -> bgp-mp:mp-graceful-restart
+ // Parameters relating to BGP graceful-restart.
+ MpGracefulRestart MpGracefulRestart `mapstructure:"mp-graceful-restart" json:"mp-graceful-restart,omitempty"`
+ // original -> bgp-mp:afi-safi-config
+ // Configuration parameters for the AFI-SAFI.
+ Config AfiSafiConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> bgp-mp:afi-safi-state
+ // State information relating to the AFI-SAFI.
+ State AfiSafiState `mapstructure:"state" json:"state,omitempty"`
+ // original -> rpol:apply-policy
+ // Anchor point for routing policies in the model.
+ // Import and export policies are with respect to the local
+ // routing table, i.e., export (send) and import (receive),
+ // depending on the context.
+ ApplyPolicy ApplyPolicy `mapstructure:"apply-policy" json:"apply-policy,omitempty"`
+ // original -> bgp-mp:ipv4-unicast
+ // IPv4 unicast configuration options.
+ Ipv4Unicast Ipv4Unicast `mapstructure:"ipv4-unicast" json:"ipv4-unicast,omitempty"`
+ // original -> bgp-mp:ipv6-unicast
+ // IPv6 unicast configuration options.
+ Ipv6Unicast Ipv6Unicast `mapstructure:"ipv6-unicast" json:"ipv6-unicast,omitempty"`
+ // original -> bgp-mp:ipv4-labelled-unicast
+ // IPv4 Labelled Unicast configuration options.
+ Ipv4LabelledUnicast Ipv4LabelledUnicast `mapstructure:"ipv4-labelled-unicast" json:"ipv4-labelled-unicast,omitempty"`
+ // original -> bgp-mp:ipv6-labelled-unicast
+ // IPv6 Labelled Unicast configuration options.
+ Ipv6LabelledUnicast Ipv6LabelledUnicast `mapstructure:"ipv6-labelled-unicast" json:"ipv6-labelled-unicast,omitempty"`
+ // original -> bgp-mp:l3vpn-ipv4-unicast
+ // Unicast IPv4 L3VPN configuration options.
+ L3vpnIpv4Unicast L3vpnIpv4Unicast `mapstructure:"l3vpn-ipv4-unicast" json:"l3vpn-ipv4-unicast,omitempty"`
+ // original -> bgp-mp:l3vpn-ipv6-unicast
+ // Unicast IPv6 L3VPN configuration options.
+ L3vpnIpv6Unicast L3vpnIpv6Unicast `mapstructure:"l3vpn-ipv6-unicast" json:"l3vpn-ipv6-unicast,omitempty"`
+ // original -> bgp-mp:l3vpn-ipv4-multicast
+ // Multicast IPv4 L3VPN configuration options.
+ L3vpnIpv4Multicast L3vpnIpv4Multicast `mapstructure:"l3vpn-ipv4-multicast" json:"l3vpn-ipv4-multicast,omitempty"`
+ // original -> bgp-mp:l3vpn-ipv6-multicast
+ // Multicast IPv6 L3VPN configuration options.
+ L3vpnIpv6Multicast L3vpnIpv6Multicast `mapstructure:"l3vpn-ipv6-multicast" json:"l3vpn-ipv6-multicast,omitempty"`
+ // original -> bgp-mp:l2vpn-vpls
+ // BGP-signalled VPLS configuration options.
+ L2vpnVpls L2vpnVpls `mapstructure:"l2vpn-vpls" json:"l2vpn-vpls,omitempty"`
+ // original -> bgp-mp:l2vpn-evpn
+ // BGP EVPN configuration options.
+ L2vpnEvpn L2vpnEvpn `mapstructure:"l2vpn-evpn" json:"l2vpn-evpn,omitempty"`
+ // original -> bgp-mp:route-selection-options
+ // Parameters relating to options for route selection.
+ RouteSelectionOptions RouteSelectionOptions `mapstructure:"route-selection-options" json:"route-selection-options,omitempty"`
+ // original -> bgp-mp:use-multiple-paths
+ // Parameters related to the use of multiple paths for the
+ // same NLRI.
+ UseMultiplePaths UseMultiplePaths `mapstructure:"use-multiple-paths" json:"use-multiple-paths,omitempty"`
+ // original -> bgp-mp:prefix-limit
+ // Configure the maximum number of prefixes that will be
+ // accepted from a peer.
+ PrefixLimit PrefixLimit `mapstructure:"prefix-limit" json:"prefix-limit,omitempty"`
+ // original -> gobgp:route-target-membership
+ RouteTargetMembership RouteTargetMembership `mapstructure:"route-target-membership" json:"route-target-membership,omitempty"`
+ // original -> gobgp:long-lived-graceful-restart
+ LongLivedGracefulRestart LongLivedGracefulRestart `mapstructure:"long-lived-graceful-restart" json:"long-lived-graceful-restart,omitempty"`
+ // original -> gobgp:add-paths
+ // add-paths configuration options related to a particular AFI-SAFI.
+ AddPaths AddPaths `mapstructure:"add-paths" json:"add-paths,omitempty"`
+}
+
+func (lhs *AfiSafi) Equal(rhs *AfiSafi) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.MpGracefulRestart.Equal(&(rhs.MpGracefulRestart)) {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ if !lhs.ApplyPolicy.Equal(&(rhs.ApplyPolicy)) {
+ return false
+ }
+ if !lhs.Ipv4Unicast.Equal(&(rhs.Ipv4Unicast)) {
+ return false
+ }
+ if !lhs.Ipv6Unicast.Equal(&(rhs.Ipv6Unicast)) {
+ return false
+ }
+ if !lhs.Ipv4LabelledUnicast.Equal(&(rhs.Ipv4LabelledUnicast)) {
+ return false
+ }
+ if !lhs.Ipv6LabelledUnicast.Equal(&(rhs.Ipv6LabelledUnicast)) {
+ return false
+ }
+ if !lhs.L3vpnIpv4Unicast.Equal(&(rhs.L3vpnIpv4Unicast)) {
+ return false
+ }
+ if !lhs.L3vpnIpv6Unicast.Equal(&(rhs.L3vpnIpv6Unicast)) {
+ return false
+ }
+ if !lhs.L3vpnIpv4Multicast.Equal(&(rhs.L3vpnIpv4Multicast)) {
+ return false
+ }
+ if !lhs.L3vpnIpv6Multicast.Equal(&(rhs.L3vpnIpv6Multicast)) {
+ return false
+ }
+ if !lhs.L2vpnVpls.Equal(&(rhs.L2vpnVpls)) {
+ return false
+ }
+ if !lhs.L2vpnEvpn.Equal(&(rhs.L2vpnEvpn)) {
+ return false
+ }
+ if !lhs.RouteSelectionOptions.Equal(&(rhs.RouteSelectionOptions)) {
+ return false
+ }
+ if !lhs.UseMultiplePaths.Equal(&(rhs.UseMultiplePaths)) {
+ return false
+ }
+ if !lhs.PrefixLimit.Equal(&(rhs.PrefixLimit)) {
+ return false
+ }
+ if !lhs.RouteTargetMembership.Equal(&(rhs.RouteTargetMembership)) {
+ return false
+ }
+ if !lhs.LongLivedGracefulRestart.Equal(&(rhs.LongLivedGracefulRestart)) {
+ return false
+ }
+ if !lhs.AddPaths.Equal(&(rhs.AddPaths)) {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp:state.
+// State information associated with graceful-restart.
+type GracefulRestartState struct {
+ // original -> bgp:enabled
+ // bgp:enabled's original type is boolean.
+ // Enable or disable the graceful-restart capability.
+ Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"`
+ // original -> bgp:restart-time
+ // Estimated time (in seconds) for the local BGP speaker to
+ // restart a session. This value is advertise in the graceful
+ // restart BGP capability. This is a 12-bit value, referred to
+ // as Restart Time in RFC4724. Per RFC4724, the suggested
+ // default value is <= the hold-time value.
+ RestartTime uint16 `mapstructure:"restart-time" json:"restart-time,omitempty"`
+ // original -> bgp:stale-routes-time
+ // bgp:stale-routes-time's original type is decimal64.
+ // An upper-bound on the time thate stale routes will be
+ // retained by a router after a session is restarted. If an
+ // End-of-RIB (EOR) marker is received prior to this timer
+ // expiring stale-routes will be flushed upon its receipt - if
+ // no EOR is received, then when this timer expires stale paths
+ // will be purged. This timer is referred to as the
+ // Selection_Deferral_Timer in RFC4724.
+ StaleRoutesTime float64 `mapstructure:"stale-routes-time" json:"stale-routes-time,omitempty"`
+ // original -> bgp:helper-only
+ // bgp:helper-only's original type is boolean.
+ // Enable graceful-restart in helper mode only. When this
+ // leaf is set, the local system does not retain forwarding
+ // its own state during a restart, but supports procedures
+ // for the receiving speaker, as defined in RFC4724.
+ HelperOnly bool `mapstructure:"helper-only" json:"helper-only,omitempty"`
+ // original -> bgp-op:peer-restart-time
+ // The period of time (advertised by the peer) that
+ // the peer expects a restart of a BGP session to
+ // take.
+ PeerRestartTime uint16 `mapstructure:"peer-restart-time" json:"peer-restart-time,omitempty"`
+ // original -> bgp-op:peer-restarting
+ // bgp-op:peer-restarting's original type is boolean.
+ // This flag indicates whether the remote neighbor is currently
+ // in the process of restarting, and hence received routes are
+ // currently stale.
+ PeerRestarting bool `mapstructure:"peer-restarting" json:"peer-restarting,omitempty"`
+ // original -> bgp-op:local-restarting
+ // bgp-op:local-restarting's original type is boolean.
+ // This flag indicates whether the local neighbor is currently
+ // restarting. The flag is unset after all NLRI have been
+ // advertised to the peer, and the End-of-RIB (EOR) marker has
+ // been unset.
+ LocalRestarting bool `mapstructure:"local-restarting" json:"local-restarting,omitempty"`
+ // original -> bgp-op:mode
+ // Ths leaf indicates the mode of operation of BGP graceful
+ // restart with the peer.
+ Mode Mode `mapstructure:"mode" json:"mode,omitempty"`
+ // original -> gobgp:deferral-time
+ DeferralTime uint16 `mapstructure:"deferral-time" json:"deferral-time,omitempty"`
+ // original -> gobgp:notification-enabled
+ // gobgp:notification-enabled's original type is boolean.
+ NotificationEnabled bool `mapstructure:"notification-enabled" json:"notification-enabled,omitempty"`
+ // original -> gobgp:long-lived-enabled
+ // gobgp:long-lived-enabled's original type is boolean.
+ LongLivedEnabled bool `mapstructure:"long-lived-enabled" json:"long-lived-enabled,omitempty"`
+}
+
+// struct for container bgp:config.
+// Configuration parameters relating to graceful-restart.
+type GracefulRestartConfig struct {
+ // original -> bgp:enabled
+ // bgp:enabled's original type is boolean.
+ // Enable or disable the graceful-restart capability.
+ Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"`
+ // original -> bgp:restart-time
+ // Estimated time (in seconds) for the local BGP speaker to
+ // restart a session. This value is advertise in the graceful
+ // restart BGP capability. This is a 12-bit value, referred to
+ // as Restart Time in RFC4724. Per RFC4724, the suggested
+ // default value is <= the hold-time value.
+ RestartTime uint16 `mapstructure:"restart-time" json:"restart-time,omitempty"`
+ // original -> bgp:stale-routes-time
+ // bgp:stale-routes-time's original type is decimal64.
+ // An upper-bound on the time thate stale routes will be
+ // retained by a router after a session is restarted. If an
+ // End-of-RIB (EOR) marker is received prior to this timer
+ // expiring stale-routes will be flushed upon its receipt - if
+ // no EOR is received, then when this timer expires stale paths
+ // will be purged. This timer is referred to as the
+ // Selection_Deferral_Timer in RFC4724.
+ StaleRoutesTime float64 `mapstructure:"stale-routes-time" json:"stale-routes-time,omitempty"`
+ // original -> bgp:helper-only
+ // bgp:helper-only's original type is boolean.
+ // Enable graceful-restart in helper mode only. When this
+ // leaf is set, the local system does not retain forwarding
+ // its own state during a restart, but supports procedures
+ // for the receiving speaker, as defined in RFC4724.
+ HelperOnly bool `mapstructure:"helper-only" json:"helper-only,omitempty"`
+ // original -> gobgp:deferral-time
+ DeferralTime uint16 `mapstructure:"deferral-time" json:"deferral-time,omitempty"`
+ // original -> gobgp:notification-enabled
+ // gobgp:notification-enabled's original type is boolean.
+ NotificationEnabled bool `mapstructure:"notification-enabled" json:"notification-enabled,omitempty"`
+ // original -> gobgp:long-lived-enabled
+ // gobgp:long-lived-enabled's original type is boolean.
+ LongLivedEnabled bool `mapstructure:"long-lived-enabled" json:"long-lived-enabled,omitempty"`
+}
+
+func (lhs *GracefulRestartConfig) Equal(rhs *GracefulRestartConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.Enabled != rhs.Enabled {
+ return false
+ }
+ if lhs.RestartTime != rhs.RestartTime {
+ return false
+ }
+ if lhs.StaleRoutesTime != rhs.StaleRoutesTime {
+ return false
+ }
+ if lhs.HelperOnly != rhs.HelperOnly {
+ return false
+ }
+ if lhs.DeferralTime != rhs.DeferralTime {
+ return false
+ }
+ if lhs.NotificationEnabled != rhs.NotificationEnabled {
+ return false
+ }
+ if lhs.LongLivedEnabled != rhs.LongLivedEnabled {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp:graceful-restart.
+// Parameters relating the graceful restart mechanism for BGP.
+type GracefulRestart struct {
+ // original -> bgp:graceful-restart-config
+ // Configuration parameters relating to graceful-restart.
+ Config GracefulRestartConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> bgp:graceful-restart-state
+ // State information associated with graceful-restart.
+ State GracefulRestartState `mapstructure:"state" json:"state,omitempty"`
+}
+
+func (lhs *GracefulRestart) Equal(rhs *GracefulRestart) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-mp:state.
+// State information relating to iBGP multipath.
+type IbgpState struct {
+ // original -> bgp-mp:maximum-paths
+ // Maximum number of parallel paths to consider when using
+ // iBGP multipath. The default is to use a single path.
+ MaximumPaths uint32 `mapstructure:"maximum-paths" json:"maximum-paths,omitempty"`
+}
+
+// struct for container bgp-mp:config.
+// Configuration parameters relating to iBGP multipath.
+type IbgpConfig struct {
+ // original -> bgp-mp:maximum-paths
+ // Maximum number of parallel paths to consider when using
+ // iBGP multipath. The default is to use a single path.
+ MaximumPaths uint32 `mapstructure:"maximum-paths" json:"maximum-paths,omitempty"`
+}
+
+func (lhs *IbgpConfig) Equal(rhs *IbgpConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.MaximumPaths != rhs.MaximumPaths {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-mp:ibgp.
+// Multipath parameters for iBGP.
+type Ibgp struct {
+ // original -> bgp-mp:ibgp-config
+ // Configuration parameters relating to iBGP multipath.
+ Config IbgpConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> bgp-mp:ibgp-state
+ // State information relating to iBGP multipath.
+ State IbgpState `mapstructure:"state" json:"state,omitempty"`
+}
+
+func (lhs *Ibgp) Equal(rhs *Ibgp) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-mp:state.
+// State information relating to eBGP multipath.
+type EbgpState struct {
+ // original -> bgp-mp:allow-multiple-as
+ // bgp-mp:allow-multiple-as's original type is boolean.
+ // Allow multipath to use paths from different neighbouring
+ // ASes. The default is to only consider multiple paths from
+ // the same neighbouring AS.
+ AllowMultipleAs bool `mapstructure:"allow-multiple-as" json:"allow-multiple-as,omitempty"`
+ // original -> bgp-mp:maximum-paths
+ // Maximum number of parallel paths to consider when using
+ // BGP multipath. The default is use a single path.
+ MaximumPaths uint32 `mapstructure:"maximum-paths" json:"maximum-paths,omitempty"`
+}
+
+// struct for container bgp-mp:config.
+// Configuration parameters relating to eBGP multipath.
+type EbgpConfig struct {
+ // original -> bgp-mp:allow-multiple-as
+ // bgp-mp:allow-multiple-as's original type is boolean.
+ // Allow multipath to use paths from different neighbouring
+ // ASes. The default is to only consider multiple paths from
+ // the same neighbouring AS.
+ AllowMultipleAs bool `mapstructure:"allow-multiple-as" json:"allow-multiple-as,omitempty"`
+ // original -> bgp-mp:maximum-paths
+ // Maximum number of parallel paths to consider when using
+ // BGP multipath. The default is use a single path.
+ MaximumPaths uint32 `mapstructure:"maximum-paths" json:"maximum-paths,omitempty"`
+}
+
+func (lhs *EbgpConfig) Equal(rhs *EbgpConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.AllowMultipleAs != rhs.AllowMultipleAs {
+ return false
+ }
+ if lhs.MaximumPaths != rhs.MaximumPaths {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-mp:ebgp.
+// Multipath parameters for eBGP.
+type Ebgp struct {
+ // original -> bgp-mp:ebgp-config
+ // Configuration parameters relating to eBGP multipath.
+ Config EbgpConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> bgp-mp:ebgp-state
+ // State information relating to eBGP multipath.
+ State EbgpState `mapstructure:"state" json:"state,omitempty"`
+}
+
+func (lhs *Ebgp) Equal(rhs *Ebgp) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-mp:state.
+// State parameters relating to multipath.
+type UseMultiplePathsState struct {
+ // original -> bgp-mp:enabled
+ // bgp-mp:enabled's original type is boolean.
+ // Whether the use of multiple paths for the same NLRI is
+ // enabled for the neighbor. This value is overridden by
+ // any more specific configuration value.
+ Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"`
+}
+
+// struct for container bgp-mp:config.
+// Configuration parameters relating to multipath.
+type UseMultiplePathsConfig struct {
+ // original -> bgp-mp:enabled
+ // bgp-mp:enabled's original type is boolean.
+ // Whether the use of multiple paths for the same NLRI is
+ // enabled for the neighbor. This value is overridden by
+ // any more specific configuration value.
+ Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"`
+}
+
+func (lhs *UseMultiplePathsConfig) Equal(rhs *UseMultiplePathsConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.Enabled != rhs.Enabled {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-mp:use-multiple-paths.
+// Parameters related to the use of multiple paths for the
+// same NLRI.
+type UseMultiplePaths struct {
+ // original -> bgp-mp:use-multiple-paths-config
+ // Configuration parameters relating to multipath.
+ Config UseMultiplePathsConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> bgp-mp:use-multiple-paths-state
+ // State parameters relating to multipath.
+ State UseMultiplePathsState `mapstructure:"state" json:"state,omitempty"`
+ // original -> bgp-mp:ebgp
+ // Multipath parameters for eBGP.
+ Ebgp Ebgp `mapstructure:"ebgp" json:"ebgp,omitempty"`
+ // original -> bgp-mp:ibgp
+ // Multipath parameters for iBGP.
+ Ibgp Ibgp `mapstructure:"ibgp" json:"ibgp,omitempty"`
+}
+
+func (lhs *UseMultiplePaths) Equal(rhs *UseMultiplePaths) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ if !lhs.Ebgp.Equal(&(rhs.Ebgp)) {
+ return false
+ }
+ if !lhs.Ibgp.Equal(&(rhs.Ibgp)) {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp:state.
+// State information relating to the BGP confederations.
+type ConfederationState struct {
+ // original -> bgp:enabled
+ // bgp:enabled's original type is boolean.
+ // When this leaf is set to true it indicates that
+ // the local-AS is part of a BGP confederation.
+ Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"`
+ // original -> bgp:identifier
+ // bgp:identifier's original type is inet:as-number.
+ // Confederation identifier for the autonomous system.
+ Identifier uint32 `mapstructure:"identifier" json:"identifier,omitempty"`
+ // original -> bgp:member-as
+ // original type is list of inet:as-number
+ // Remote autonomous systems that are to be treated
+ // as part of the local confederation.
+ MemberAsList []uint32 `mapstructure:"member-as-list" json:"member-as-list,omitempty"`
+}
+
+// struct for container bgp:config.
+// Configuration parameters relating to BGP confederations.
+type ConfederationConfig struct {
+ // original -> bgp:enabled
+ // bgp:enabled's original type is boolean.
+ // When this leaf is set to true it indicates that
+ // the local-AS is part of a BGP confederation.
+ Enabled bool `mapstructure:"enabled" json:"enabled,omitempty"`
+ // original -> bgp:identifier
+ // bgp:identifier's original type is inet:as-number.
+ // Confederation identifier for the autonomous system.
+ Identifier uint32 `mapstructure:"identifier" json:"identifier,omitempty"`
+ // original -> bgp:member-as
+ // original type is list of inet:as-number
+ // Remote autonomous systems that are to be treated
+ // as part of the local confederation.
+ MemberAsList []uint32 `mapstructure:"member-as-list" json:"member-as-list,omitempty"`
+}
+
+func (lhs *ConfederationConfig) Equal(rhs *ConfederationConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.Enabled != rhs.Enabled {
+ return false
+ }
+ if lhs.Identifier != rhs.Identifier {
+ return false
+ }
+ if len(lhs.MemberAsList) != len(rhs.MemberAsList) {
+ return false
+ }
+ for idx, l := range lhs.MemberAsList {
+ if l != rhs.MemberAsList[idx] {
+ return false
+ }
+ }
+ return true
+}
+
+// struct for container bgp:confederation.
+// Parameters indicating whether the local system acts as part
+// of a BGP confederation.
+type Confederation struct {
+ // original -> bgp:confederation-config
+ // Configuration parameters relating to BGP confederations.
+ Config ConfederationConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> bgp:confederation-state
+ // State information relating to the BGP confederations.
+ State ConfederationState `mapstructure:"state" json:"state,omitempty"`
+}
+
+func (lhs *Confederation) Equal(rhs *Confederation) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp:state.
+// State information relating to the default route distance.
+type DefaultRouteDistanceState struct {
+ // original -> bgp:external-route-distance
+ // Administrative distance for routes learned from external
+ // BGP (eBGP).
+ ExternalRouteDistance uint8 `mapstructure:"external-route-distance" json:"external-route-distance,omitempty"`
+ // original -> bgp:internal-route-distance
+ // Administrative distance for routes learned from internal
+ // BGP (iBGP).
+ InternalRouteDistance uint8 `mapstructure:"internal-route-distance" json:"internal-route-distance,omitempty"`
+}
+
+// struct for container bgp:config.
+// Configuration parameters relating to the default route
+// distance.
+type DefaultRouteDistanceConfig struct {
+ // original -> bgp:external-route-distance
+ // Administrative distance for routes learned from external
+ // BGP (eBGP).
+ ExternalRouteDistance uint8 `mapstructure:"external-route-distance" json:"external-route-distance,omitempty"`
+ // original -> bgp:internal-route-distance
+ // Administrative distance for routes learned from internal
+ // BGP (iBGP).
+ InternalRouteDistance uint8 `mapstructure:"internal-route-distance" json:"internal-route-distance,omitempty"`
+}
+
+func (lhs *DefaultRouteDistanceConfig) Equal(rhs *DefaultRouteDistanceConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.ExternalRouteDistance != rhs.ExternalRouteDistance {
+ return false
+ }
+ if lhs.InternalRouteDistance != rhs.InternalRouteDistance {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp:default-route-distance.
+// Administrative distance (or preference) assigned to
+// routes received from different sources
+// (external, internal, and local).
+type DefaultRouteDistance struct {
+ // original -> bgp:default-route-distance-config
+ // Configuration parameters relating to the default route
+ // distance.
+ Config DefaultRouteDistanceConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> bgp:default-route-distance-state
+ // State information relating to the default route distance.
+ State DefaultRouteDistanceState `mapstructure:"state" json:"state,omitempty"`
+}
+
+func (lhs *DefaultRouteDistance) Equal(rhs *DefaultRouteDistance) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-mp:state.
+// State information for the route selection options.
+type RouteSelectionOptionsState struct {
+ // original -> bgp-mp:always-compare-med
+ // bgp-mp:always-compare-med's original type is boolean.
+ // Compare multi-exit discriminator (MED) value from
+ // different ASes when selecting the best route. The
+ // default behavior is to only compare MEDs for paths
+ // received from the same AS.
+ AlwaysCompareMed bool `mapstructure:"always-compare-med" json:"always-compare-med,omitempty"`
+ // original -> bgp-mp:ignore-as-path-length
+ // bgp-mp:ignore-as-path-length's original type is boolean.
+ // Ignore the AS path length when selecting the best path.
+ // The default is to use the AS path length and prefer paths
+ // with shorter length.
+ IgnoreAsPathLength bool `mapstructure:"ignore-as-path-length" json:"ignore-as-path-length,omitempty"`
+ // original -> bgp-mp:external-compare-router-id
+ // bgp-mp:external-compare-router-id's original type is boolean.
+ // When comparing similar routes received from external
+ // BGP peers, use the router-id as a criterion to select
+ // the active path.
+ ExternalCompareRouterId bool `mapstructure:"external-compare-router-id" json:"external-compare-router-id,omitempty"`
+ // original -> bgp-mp:advertise-inactive-routes
+ // bgp-mp:advertise-inactive-routes's original type is boolean.
+ // Advertise inactive routes to external peers. The
+ // default is to only advertise active routes.
+ AdvertiseInactiveRoutes bool `mapstructure:"advertise-inactive-routes" json:"advertise-inactive-routes,omitempty"`
+ // original -> bgp-mp:enable-aigp
+ // bgp-mp:enable-aigp's original type is boolean.
+ // Flag to enable sending / receiving accumulated IGP
+ // attribute in routing updates.
+ EnableAigp bool `mapstructure:"enable-aigp" json:"enable-aigp,omitempty"`
+ // original -> bgp-mp:ignore-next-hop-igp-metric
+ // bgp-mp:ignore-next-hop-igp-metric's original type is boolean.
+ // Ignore the IGP metric to the next-hop when calculating
+ // BGP best-path. The default is to select the route for
+ // which the metric to the next-hop is lowest.
+ IgnoreNextHopIgpMetric bool `mapstructure:"ignore-next-hop-igp-metric" json:"ignore-next-hop-igp-metric,omitempty"`
+ // original -> gobgp:disable-best-path-selection
+ // gobgp:disable-best-path-selection's original type is boolean.
+ // Disables best path selection process.
+ DisableBestPathSelection bool `mapstructure:"disable-best-path-selection" json:"disable-best-path-selection,omitempty"`
+}
+
+// struct for container bgp-mp:config.
+// Configuration parameters relating to route selection
+// options.
+type RouteSelectionOptionsConfig struct {
+ // original -> bgp-mp:always-compare-med
+ // bgp-mp:always-compare-med's original type is boolean.
+ // Compare multi-exit discriminator (MED) value from
+ // different ASes when selecting the best route. The
+ // default behavior is to only compare MEDs for paths
+ // received from the same AS.
+ AlwaysCompareMed bool `mapstructure:"always-compare-med" json:"always-compare-med,omitempty"`
+ // original -> bgp-mp:ignore-as-path-length
+ // bgp-mp:ignore-as-path-length's original type is boolean.
+ // Ignore the AS path length when selecting the best path.
+ // The default is to use the AS path length and prefer paths
+ // with shorter length.
+ IgnoreAsPathLength bool `mapstructure:"ignore-as-path-length" json:"ignore-as-path-length,omitempty"`
+ // original -> bgp-mp:external-compare-router-id
+ // bgp-mp:external-compare-router-id's original type is boolean.
+ // When comparing similar routes received from external
+ // BGP peers, use the router-id as a criterion to select
+ // the active path.
+ ExternalCompareRouterId bool `mapstructure:"external-compare-router-id" json:"external-compare-router-id,omitempty"`
+ // original -> bgp-mp:advertise-inactive-routes
+ // bgp-mp:advertise-inactive-routes's original type is boolean.
+ // Advertise inactive routes to external peers. The
+ // default is to only advertise active routes.
+ AdvertiseInactiveRoutes bool `mapstructure:"advertise-inactive-routes" json:"advertise-inactive-routes,omitempty"`
+ // original -> bgp-mp:enable-aigp
+ // bgp-mp:enable-aigp's original type is boolean.
+ // Flag to enable sending / receiving accumulated IGP
+ // attribute in routing updates.
+ EnableAigp bool `mapstructure:"enable-aigp" json:"enable-aigp,omitempty"`
+ // original -> bgp-mp:ignore-next-hop-igp-metric
+ // bgp-mp:ignore-next-hop-igp-metric's original type is boolean.
+ // Ignore the IGP metric to the next-hop when calculating
+ // BGP best-path. The default is to select the route for
+ // which the metric to the next-hop is lowest.
+ IgnoreNextHopIgpMetric bool `mapstructure:"ignore-next-hop-igp-metric" json:"ignore-next-hop-igp-metric,omitempty"`
+ // original -> gobgp:disable-best-path-selection
+ // gobgp:disable-best-path-selection's original type is boolean.
+ // Disables best path selection process.
+ DisableBestPathSelection bool `mapstructure:"disable-best-path-selection" json:"disable-best-path-selection,omitempty"`
+}
+
+func (lhs *RouteSelectionOptionsConfig) Equal(rhs *RouteSelectionOptionsConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.AlwaysCompareMed != rhs.AlwaysCompareMed {
+ return false
+ }
+ if lhs.IgnoreAsPathLength != rhs.IgnoreAsPathLength {
+ return false
+ }
+ if lhs.ExternalCompareRouterId != rhs.ExternalCompareRouterId {
+ return false
+ }
+ if lhs.AdvertiseInactiveRoutes != rhs.AdvertiseInactiveRoutes {
+ return false
+ }
+ if lhs.EnableAigp != rhs.EnableAigp {
+ return false
+ }
+ if lhs.IgnoreNextHopIgpMetric != rhs.IgnoreNextHopIgpMetric {
+ return false
+ }
+ if lhs.DisableBestPathSelection != rhs.DisableBestPathSelection {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-mp:route-selection-options.
+// Parameters relating to options for route selection.
+type RouteSelectionOptions struct {
+ // original -> bgp-mp:route-selection-options-config
+ // Configuration parameters relating to route selection
+ // options.
+ Config RouteSelectionOptionsConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> bgp-mp:route-selection-options-state
+ // State information for the route selection options.
+ State RouteSelectionOptionsState `mapstructure:"state" json:"state,omitempty"`
+}
+
+func (lhs *RouteSelectionOptions) Equal(rhs *RouteSelectionOptions) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp:state.
+// State information relating to the global BGP router.
+type GlobalState struct {
+ // original -> bgp:as
+ // bgp:as's original type is inet:as-number.
+ // Local autonomous system number of the router. Uses
+ // the 32-bit as-number type from the model in RFC 6991.
+ As uint32 `mapstructure:"as" json:"as,omitempty"`
+ // original -> bgp:router-id
+ // bgp:router-id's original type is inet:ipv4-address.
+ // Router id of the router, expressed as an
+ // 32-bit value, IPv4 address.
+ RouterId string `mapstructure:"router-id" json:"router-id,omitempty"`
+ // original -> bgp-op:total-paths
+ // Total number of BGP paths within the context.
+ TotalPaths uint32 `mapstructure:"total-paths" json:"total-paths,omitempty"`
+ // original -> bgp-op:total-prefixes
+ // .
+ TotalPrefixes uint32 `mapstructure:"total-prefixes" json:"total-prefixes,omitempty"`
+ // original -> gobgp:port
+ Port int32 `mapstructure:"port" json:"port,omitempty"`
+ // original -> gobgp:local-address
+ LocalAddressList []string `mapstructure:"local-address-list" json:"local-address-list,omitempty"`
+}
+
+// struct for container bgp:config.
+// Configuration parameters relating to the global BGP router.
+type GlobalConfig struct {
+ // original -> bgp:as
+ // bgp:as's original type is inet:as-number.
+ // Local autonomous system number of the router. Uses
+ // the 32-bit as-number type from the model in RFC 6991.
+ As uint32 `mapstructure:"as" json:"as,omitempty"`
+ // original -> bgp:router-id
+ // bgp:router-id's original type is inet:ipv4-address.
+ // Router id of the router, expressed as an
+ // 32-bit value, IPv4 address.
+ RouterId string `mapstructure:"router-id" json:"router-id,omitempty"`
+ // original -> gobgp:port
+ Port int32 `mapstructure:"port" json:"port,omitempty"`
+ // original -> gobgp:local-address
+ LocalAddressList []string `mapstructure:"local-address-list" json:"local-address-list,omitempty"`
+}
+
+func (lhs *GlobalConfig) Equal(rhs *GlobalConfig) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.As != rhs.As {
+ return false
+ }
+ if lhs.RouterId != rhs.RouterId {
+ return false
+ }
+ if lhs.Port != rhs.Port {
+ return false
+ }
+ if len(lhs.LocalAddressList) != len(rhs.LocalAddressList) {
+ return false
+ }
+ for idx, l := range lhs.LocalAddressList {
+ if l != rhs.LocalAddressList[idx] {
+ return false
+ }
+ }
+ return true
+}
+
+// struct for container bgp:global.
+// Global configuration for the BGP router.
+type Global struct {
+ // original -> bgp:global-config
+ // Configuration parameters relating to the global BGP router.
+ Config GlobalConfig `mapstructure:"config" json:"config,omitempty"`
+ // original -> bgp:global-state
+ // State information relating to the global BGP router.
+ State GlobalState `mapstructure:"state" json:"state,omitempty"`
+ // original -> bgp-mp:route-selection-options
+ // Parameters relating to options for route selection.
+ RouteSelectionOptions RouteSelectionOptions `mapstructure:"route-selection-options" json:"route-selection-options,omitempty"`
+ // original -> bgp:default-route-distance
+ // Administrative distance (or preference) assigned to
+ // routes received from different sources
+ // (external, internal, and local).
+ DefaultRouteDistance DefaultRouteDistance `mapstructure:"default-route-distance" json:"default-route-distance,omitempty"`
+ // original -> bgp:confederation
+ // Parameters indicating whether the local system acts as part
+ // of a BGP confederation.
+ Confederation Confederation `mapstructure:"confederation" json:"confederation,omitempty"`
+ // original -> bgp-mp:use-multiple-paths
+ // Parameters related to the use of multiple paths for the
+ // same NLRI.
+ UseMultiplePaths UseMultiplePaths `mapstructure:"use-multiple-paths" json:"use-multiple-paths,omitempty"`
+ // original -> bgp:graceful-restart
+ // Parameters relating the graceful restart mechanism for BGP.
+ GracefulRestart GracefulRestart `mapstructure:"graceful-restart" json:"graceful-restart,omitempty"`
+ // original -> bgp:afi-safis
+ // Address family specific configuration.
+ AfiSafis []AfiSafi `mapstructure:"afi-safis" json:"afi-safis,omitempty"`
+ // original -> rpol:apply-policy
+ // Anchor point for routing policies in the model.
+ // Import and export policies are with respect to the local
+ // routing table, i.e., export (send) and import (receive),
+ // depending on the context.
+ ApplyPolicy ApplyPolicy `mapstructure:"apply-policy" json:"apply-policy,omitempty"`
+}
+
+func (lhs *Global) Equal(rhs *Global) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Config.Equal(&(rhs.Config)) {
+ return false
+ }
+ if !lhs.RouteSelectionOptions.Equal(&(rhs.RouteSelectionOptions)) {
+ return false
+ }
+ if !lhs.DefaultRouteDistance.Equal(&(rhs.DefaultRouteDistance)) {
+ return false
+ }
+ if !lhs.Confederation.Equal(&(rhs.Confederation)) {
+ return false
+ }
+ if !lhs.UseMultiplePaths.Equal(&(rhs.UseMultiplePaths)) {
+ return false
+ }
+ if !lhs.GracefulRestart.Equal(&(rhs.GracefulRestart)) {
+ return false
+ }
+ if len(lhs.AfiSafis) != len(rhs.AfiSafis) {
+ return false
+ }
+ {
+ lmap := make(map[string]*AfiSafi)
+ for i, l := range lhs.AfiSafis {
+ lmap[mapkey(i, string(l.Config.AfiSafiName))] = &lhs.AfiSafis[i]
+ }
+ for i, r := range rhs.AfiSafis {
+ if l, y := lmap[mapkey(i, string(r.Config.AfiSafiName))]; !y {
+ return false
+ } else if !r.Equal(l) {
+ return false
+ }
+ }
+ }
+ if !lhs.ApplyPolicy.Equal(&(rhs.ApplyPolicy)) {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp:bgp.
+// Top-level configuration and state for the BGP router.
+type Bgp struct {
+ // original -> bgp:global
+ // Global configuration for the BGP router.
+ Global Global `mapstructure:"global" json:"global,omitempty"`
+ // original -> bgp:neighbors
+ // Configuration for BGP neighbors.
+ Neighbors []Neighbor `mapstructure:"neighbors" json:"neighbors,omitempty"`
+ // original -> bgp:peer-groups
+ // Configuration for BGP peer-groups.
+ PeerGroups []PeerGroup `mapstructure:"peer-groups" json:"peer-groups,omitempty"`
+ // original -> gobgp:rpki-servers
+ RpkiServers []RpkiServer `mapstructure:"rpki-servers" json:"rpki-servers,omitempty"`
+ // original -> gobgp:bmp-servers
+ BmpServers []BmpServer `mapstructure:"bmp-servers" json:"bmp-servers,omitempty"`
+ // original -> gobgp:vrfs
+ Vrfs []Vrf `mapstructure:"vrfs" json:"vrfs,omitempty"`
+ // original -> gobgp:mrt-dump
+ MrtDump []Mrt `mapstructure:"mrt-dump" json:"mrt-dump,omitempty"`
+ // original -> gobgp:zebra
+ Zebra Zebra `mapstructure:"zebra" json:"zebra,omitempty"`
+ // original -> gobgp:collector
+ Collector Collector `mapstructure:"collector" json:"collector,omitempty"`
+ // original -> gobgp:dynamic-neighbors
+ DynamicNeighbors []DynamicNeighbor `mapstructure:"dynamic-neighbors" json:"dynamic-neighbors,omitempty"`
+}
+
+func (lhs *Bgp) Equal(rhs *Bgp) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.Global.Equal(&(rhs.Global)) {
+ return false
+ }
+ if len(lhs.Neighbors) != len(rhs.Neighbors) {
+ return false
+ }
+ {
+ lmap := make(map[string]*Neighbor)
+ for i, l := range lhs.Neighbors {
+ lmap[mapkey(i, string(l.Config.NeighborAddress))] = &lhs.Neighbors[i]
+ }
+ for i, r := range rhs.Neighbors {
+ if l, y := lmap[mapkey(i, string(r.Config.NeighborAddress))]; !y {
+ return false
+ } else if !r.Equal(l) {
+ return false
+ }
+ }
+ }
+ if len(lhs.PeerGroups) != len(rhs.PeerGroups) {
+ return false
+ }
+ {
+ lmap := make(map[string]*PeerGroup)
+ for i, l := range lhs.PeerGroups {
+ lmap[mapkey(i, string(l.Config.PeerGroupName))] = &lhs.PeerGroups[i]
+ }
+ for i, r := range rhs.PeerGroups {
+ if l, y := lmap[mapkey(i, string(r.Config.PeerGroupName))]; !y {
+ return false
+ } else if !r.Equal(l) {
+ return false
+ }
+ }
+ }
+ if len(lhs.RpkiServers) != len(rhs.RpkiServers) {
+ return false
+ }
+ {
+ lmap := make(map[string]*RpkiServer)
+ for i, l := range lhs.RpkiServers {
+ lmap[mapkey(i, string(l.Config.Address))] = &lhs.RpkiServers[i]
+ }
+ for i, r := range rhs.RpkiServers {
+ if l, y := lmap[mapkey(i, string(r.Config.Address))]; !y {
+ return false
+ } else if !r.Equal(l) {
+ return false
+ }
+ }
+ }
+ if len(lhs.BmpServers) != len(rhs.BmpServers) {
+ return false
+ }
+ {
+ lmap := make(map[string]*BmpServer)
+ for i, l := range lhs.BmpServers {
+ lmap[mapkey(i, string(l.Config.Address))] = &lhs.BmpServers[i]
+ }
+ for i, r := range rhs.BmpServers {
+ if l, y := lmap[mapkey(i, string(r.Config.Address))]; !y {
+ return false
+ } else if !r.Equal(l) {
+ return false
+ }
+ }
+ }
+ if len(lhs.Vrfs) != len(rhs.Vrfs) {
+ return false
+ }
+ {
+ lmap := make(map[string]*Vrf)
+ for i, l := range lhs.Vrfs {
+ lmap[mapkey(i, string(l.Config.Name))] = &lhs.Vrfs[i]
+ }
+ for i, r := range rhs.Vrfs {
+ if l, y := lmap[mapkey(i, string(r.Config.Name))]; !y {
+ return false
+ } else if !r.Equal(l) {
+ return false
+ }
+ }
+ }
+ if len(lhs.MrtDump) != len(rhs.MrtDump) {
+ return false
+ }
+ {
+ lmap := make(map[string]*Mrt)
+ for i, l := range lhs.MrtDump {
+ lmap[mapkey(i, string(l.Config.FileName))] = &lhs.MrtDump[i]
+ }
+ for i, r := range rhs.MrtDump {
+ if l, y := lmap[mapkey(i, string(r.Config.FileName))]; !y {
+ return false
+ } else if !r.Equal(l) {
+ return false
+ }
+ }
+ }
+ if !lhs.Zebra.Equal(&(rhs.Zebra)) {
+ return false
+ }
+ if !lhs.Collector.Equal(&(rhs.Collector)) {
+ return false
+ }
+ if len(lhs.DynamicNeighbors) != len(rhs.DynamicNeighbors) {
+ return false
+ }
+ {
+ lmap := make(map[string]*DynamicNeighbor)
+ for i, l := range lhs.DynamicNeighbors {
+ lmap[mapkey(i, string(l.Config.Prefix))] = &lhs.DynamicNeighbors[i]
+ }
+ for i, r := range rhs.DynamicNeighbors {
+ if l, y := lmap[mapkey(i, string(r.Config.Prefix))]; !y {
+ return false
+ } else if !r.Equal(l) {
+ return false
+ }
+ }
+ }
+ return true
+}
+
+// struct for container gobgp:set-large-community-method.
+type SetLargeCommunityMethod struct {
+ // original -> gobgp:communities
+ CommunitiesList []string `mapstructure:"communities-list" json:"communities-list,omitempty"`
+}
+
+func (lhs *SetLargeCommunityMethod) Equal(rhs *SetLargeCommunityMethod) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if len(lhs.CommunitiesList) != len(rhs.CommunitiesList) {
+ return false
+ }
+ for idx, l := range lhs.CommunitiesList {
+ if l != rhs.CommunitiesList[idx] {
+ return false
+ }
+ }
+ return true
+}
+
+// struct for container gobgp:set-large-community.
+type SetLargeCommunity struct {
+ // original -> gobgp:set-large-community-method
+ SetLargeCommunityMethod SetLargeCommunityMethod `mapstructure:"set-large-community-method" json:"set-large-community-method,omitempty"`
+ // original -> gobgp:options
+ Options BgpSetCommunityOptionType `mapstructure:"options" json:"options,omitempty"`
+}
+
+func (lhs *SetLargeCommunity) Equal(rhs *SetLargeCommunity) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.SetLargeCommunityMethod.Equal(&(rhs.SetLargeCommunityMethod)) {
+ return false
+ }
+ if lhs.Options != rhs.Options {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-pol:set-ext-community-method.
+// Option to set communities using an inline list or
+// reference to an existing defined set.
+type SetExtCommunityMethod struct {
+ // original -> bgp-pol:communities
+ // original type is list of union
+ // Set the community values for the update inline with
+ // a list.
+ CommunitiesList []string `mapstructure:"communities-list" json:"communities-list,omitempty"`
+ // original -> bgp-pol:ext-community-set-ref
+ // References a defined extended community set by
+ // name.
+ ExtCommunitySetRef string `mapstructure:"ext-community-set-ref" json:"ext-community-set-ref,omitempty"`
+}
+
+func (lhs *SetExtCommunityMethod) Equal(rhs *SetExtCommunityMethod) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if len(lhs.CommunitiesList) != len(rhs.CommunitiesList) {
+ return false
+ }
+ for idx, l := range lhs.CommunitiesList {
+ if l != rhs.CommunitiesList[idx] {
+ return false
+ }
+ }
+ if lhs.ExtCommunitySetRef != rhs.ExtCommunitySetRef {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-pol:set-ext-community.
+// Action to set the extended community attributes of the
+// route, along with options to modify how the community is
+// modified.
+type SetExtCommunity struct {
+ // original -> bgp-pol:set-ext-community-method
+ // Option to set communities using an inline list or
+ // reference to an existing defined set.
+ SetExtCommunityMethod SetExtCommunityMethod `mapstructure:"set-ext-community-method" json:"set-ext-community-method,omitempty"`
+ // original -> bgp-pol:options
+ // bgp-pol:options's original type is bgp-set-community-option-type.
+ // options for modifying the extended community
+ // attribute with the specified values. These options
+ // apply to both methods of setting the community
+ // attribute.
+ Options string `mapstructure:"options" json:"options,omitempty"`
+}
+
+func (lhs *SetExtCommunity) Equal(rhs *SetExtCommunity) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.SetExtCommunityMethod.Equal(&(rhs.SetExtCommunityMethod)) {
+ return false
+ }
+ if lhs.Options != rhs.Options {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-pol:set-community-method.
+// Option to set communities using an inline list or
+// reference to an existing defined set.
+type SetCommunityMethod struct {
+ // original -> bgp-pol:communities
+ // original type is list of union
+ // Set the community values for the update inline with
+ // a list.
+ CommunitiesList []string `mapstructure:"communities-list" json:"communities-list,omitempty"`
+ // original -> bgp-pol:community-set-ref
+ // References a defined community set by name.
+ CommunitySetRef string `mapstructure:"community-set-ref" json:"community-set-ref,omitempty"`
+}
+
+func (lhs *SetCommunityMethod) Equal(rhs *SetCommunityMethod) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if len(lhs.CommunitiesList) != len(rhs.CommunitiesList) {
+ return false
+ }
+ for idx, l := range lhs.CommunitiesList {
+ if l != rhs.CommunitiesList[idx] {
+ return false
+ }
+ }
+ if lhs.CommunitySetRef != rhs.CommunitySetRef {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-pol:set-community.
+// action to set the community attributes of the route, along
+// with options to modify how the community is modified.
+type SetCommunity struct {
+ // original -> bgp-pol:set-community-method
+ // Option to set communities using an inline list or
+ // reference to an existing defined set.
+ SetCommunityMethod SetCommunityMethod `mapstructure:"set-community-method" json:"set-community-method,omitempty"`
+ // original -> bgp-pol:options
+ // bgp-pol:options's original type is bgp-set-community-option-type.
+ // Options for modifying the community attribute with
+ // the specified values. These options apply to both
+ // methods of setting the community attribute.
+ Options string `mapstructure:"options" json:"options,omitempty"`
+}
+
+func (lhs *SetCommunity) Equal(rhs *SetCommunity) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.SetCommunityMethod.Equal(&(rhs.SetCommunityMethod)) {
+ return false
+ }
+ if lhs.Options != rhs.Options {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-pol:set-as-path-prepend.
+// action to prepend local AS number to the AS-path a
+// specified number of times.
+type SetAsPathPrepend struct {
+ // original -> bgp-pol:repeat-n
+ // number of times to prepend the local AS
+ // number.
+ RepeatN uint8 `mapstructure:"repeat-n" json:"repeat-n,omitempty"`
+ // original -> gobgp:as
+ // gobgp:as's original type is union.
+ // autonomous system number or 'last-as' which means
+ // the leftmost as number in the AS-path to be prepended.
+ As string `mapstructure:"as" json:"as,omitempty"`
+}
+
+func (lhs *SetAsPathPrepend) Equal(rhs *SetAsPathPrepend) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.RepeatN != rhs.RepeatN {
+ return false
+ }
+ if lhs.As != rhs.As {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-pol:bgp-actions.
+// Definitions for policy action statements that
+// change BGP-specific attributes of the route.
+type BgpActions struct {
+ // original -> bgp-pol:set-as-path-prepend
+ // action to prepend local AS number to the AS-path a
+ // specified number of times.
+ SetAsPathPrepend SetAsPathPrepend `mapstructure:"set-as-path-prepend" json:"set-as-path-prepend,omitempty"`
+ // original -> bgp-pol:set-community
+ // action to set the community attributes of the route, along
+ // with options to modify how the community is modified.
+ SetCommunity SetCommunity `mapstructure:"set-community" json:"set-community,omitempty"`
+ // original -> bgp-pol:set-ext-community
+ // Action to set the extended community attributes of the
+ // route, along with options to modify how the community is
+ // modified.
+ SetExtCommunity SetExtCommunity `mapstructure:"set-ext-community" json:"set-ext-community,omitempty"`
+ // original -> bgp-pol:set-route-origin
+ // set the origin attribute to the specified
+ // value.
+ SetRouteOrigin BgpOriginAttrType `mapstructure:"set-route-origin" json:"set-route-origin,omitempty"`
+ // original -> bgp-pol:set-local-pref
+ // set the local pref attribute on the route
+ // update.
+ SetLocalPref uint32 `mapstructure:"set-local-pref" json:"set-local-pref,omitempty"`
+ // original -> bgp-pol:set-next-hop
+ // set the next-hop attribute in the route update.
+ SetNextHop BgpNextHopType `mapstructure:"set-next-hop" json:"set-next-hop,omitempty"`
+ // original -> bgp-pol:set-med
+ // set the med metric attribute in the route
+ // update.
+ SetMed BgpSetMedType `mapstructure:"set-med" json:"set-med,omitempty"`
+ // original -> gobgp:set-large-community
+ SetLargeCommunity SetLargeCommunity `mapstructure:"set-large-community" json:"set-large-community,omitempty"`
+}
+
+func (lhs *BgpActions) Equal(rhs *BgpActions) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.SetAsPathPrepend.Equal(&(rhs.SetAsPathPrepend)) {
+ return false
+ }
+ if !lhs.SetCommunity.Equal(&(rhs.SetCommunity)) {
+ return false
+ }
+ if !lhs.SetExtCommunity.Equal(&(rhs.SetExtCommunity)) {
+ return false
+ }
+ if lhs.SetRouteOrigin != rhs.SetRouteOrigin {
+ return false
+ }
+ if lhs.SetLocalPref != rhs.SetLocalPref {
+ return false
+ }
+ if lhs.SetNextHop != rhs.SetNextHop {
+ return false
+ }
+ if lhs.SetMed != rhs.SetMed {
+ return false
+ }
+ if !lhs.SetLargeCommunity.Equal(&(rhs.SetLargeCommunity)) {
+ return false
+ }
+ return true
+}
+
+// struct for container rpol:igp-actions.
+// Actions to set IGP route attributes; these actions
+// apply to multiple IGPs.
+type IgpActions struct {
+ // original -> rpol:set-tag
+ // Set the tag value for OSPF or IS-IS routes.
+ SetTag TagType `mapstructure:"set-tag" json:"set-tag,omitempty"`
+}
+
+func (lhs *IgpActions) Equal(rhs *IgpActions) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.SetTag != rhs.SetTag {
+ return false
+ }
+ return true
+}
+
+// struct for container rpol:actions.
+// Action statements for this policy
+// statement.
+type Actions struct {
+ // original -> rpol:route-disposition
+ // Select the final disposition for the route, either
+ // accept or reject.
+ RouteDisposition RouteDisposition `mapstructure:"route-disposition" json:"route-disposition,omitempty"`
+ // original -> rpol:igp-actions
+ // Actions to set IGP route attributes; these actions
+ // apply to multiple IGPs.
+ IgpActions IgpActions `mapstructure:"igp-actions" json:"igp-actions,omitempty"`
+ // original -> bgp-pol:bgp-actions
+ // Definitions for policy action statements that
+ // change BGP-specific attributes of the route.
+ BgpActions BgpActions `mapstructure:"bgp-actions" json:"bgp-actions,omitempty"`
+}
+
+func (lhs *Actions) Equal(rhs *Actions) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.RouteDisposition != rhs.RouteDisposition {
+ return false
+ }
+ if !lhs.IgpActions.Equal(&(rhs.IgpActions)) {
+ return false
+ }
+ if !lhs.BgpActions.Equal(&(rhs.BgpActions)) {
+ return false
+ }
+ return true
+}
+
+// struct for container gobgp:match-large-community-set.
+type MatchLargeCommunitySet struct {
+ // original -> gobgp:large-community-set
+ LargeCommunitySet string `mapstructure:"large-community-set" json:"large-community-set,omitempty"`
+ // original -> rpol:match-set-options
+ // Optional parameter that governs the behaviour of the
+ // match operation.
+ MatchSetOptions MatchSetOptionsType `mapstructure:"match-set-options" json:"match-set-options,omitempty"`
+}
+
+func (lhs *MatchLargeCommunitySet) Equal(rhs *MatchLargeCommunitySet) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.LargeCommunitySet != rhs.LargeCommunitySet {
+ return false
+ }
+ if lhs.MatchSetOptions != rhs.MatchSetOptions {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-pol:as-path-length.
+// Value and comparison operations for conditions based on the
+// length of the AS path in the route update.
+type AsPathLength struct {
+ // original -> ptypes:operator
+ // type of comparison to be performed.
+ Operator AttributeComparison `mapstructure:"operator" json:"operator,omitempty"`
+ // original -> ptypes:value
+ // value to compare with the community count.
+ Value uint32 `mapstructure:"value" json:"value,omitempty"`
+}
+
+func (lhs *AsPathLength) Equal(rhs *AsPathLength) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.Operator != rhs.Operator {
+ return false
+ }
+ if lhs.Value != rhs.Value {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-pol:community-count.
+// Value and comparison operations for conditions based on the
+// number of communities in the route update.
+type CommunityCount struct {
+ // original -> ptypes:operator
+ // type of comparison to be performed.
+ Operator AttributeComparison `mapstructure:"operator" json:"operator,omitempty"`
+ // original -> ptypes:value
+ // value to compare with the community count.
+ Value uint32 `mapstructure:"value" json:"value,omitempty"`
+}
+
+func (lhs *CommunityCount) Equal(rhs *CommunityCount) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.Operator != rhs.Operator {
+ return false
+ }
+ if lhs.Value != rhs.Value {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-pol:match-as-path-set.
+// Match a referenced as-path set according to the logic
+// defined in the match-set-options leaf.
+type MatchAsPathSet struct {
+ // original -> bgp-pol:as-path-set
+ // References a defined AS path set.
+ AsPathSet string `mapstructure:"as-path-set" json:"as-path-set,omitempty"`
+ // original -> rpol:match-set-options
+ // Optional parameter that governs the behaviour of the
+ // match operation.
+ MatchSetOptions MatchSetOptionsType `mapstructure:"match-set-options" json:"match-set-options,omitempty"`
+}
+
+func (lhs *MatchAsPathSet) Equal(rhs *MatchAsPathSet) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.AsPathSet != rhs.AsPathSet {
+ return false
+ }
+ if lhs.MatchSetOptions != rhs.MatchSetOptions {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-pol:match-ext-community-set.
+// Match a referenced extended community-set according to the
+// logic defined in the match-set-options leaf.
+type MatchExtCommunitySet struct {
+ // original -> bgp-pol:ext-community-set
+ // References a defined extended community set.
+ ExtCommunitySet string `mapstructure:"ext-community-set" json:"ext-community-set,omitempty"`
+ // original -> rpol:match-set-options
+ // Optional parameter that governs the behaviour of the
+ // match operation.
+ MatchSetOptions MatchSetOptionsType `mapstructure:"match-set-options" json:"match-set-options,omitempty"`
+}
+
+func (lhs *MatchExtCommunitySet) Equal(rhs *MatchExtCommunitySet) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.ExtCommunitySet != rhs.ExtCommunitySet {
+ return false
+ }
+ if lhs.MatchSetOptions != rhs.MatchSetOptions {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-pol:match-community-set.
+// Match a referenced community-set according to the logic
+// defined in the match-set-options leaf.
+type MatchCommunitySet struct {
+ // original -> bgp-pol:community-set
+ // References a defined community set.
+ CommunitySet string `mapstructure:"community-set" json:"community-set,omitempty"`
+ // original -> rpol:match-set-options
+ // Optional parameter that governs the behaviour of the
+ // match operation.
+ MatchSetOptions MatchSetOptionsType `mapstructure:"match-set-options" json:"match-set-options,omitempty"`
+}
+
+func (lhs *MatchCommunitySet) Equal(rhs *MatchCommunitySet) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.CommunitySet != rhs.CommunitySet {
+ return false
+ }
+ if lhs.MatchSetOptions != rhs.MatchSetOptions {
+ return false
+ }
+ return true
+}
+
+// struct for container bgp-pol:bgp-conditions.
+// Policy conditions for matching
+// BGP-specific defined sets or comparing BGP-specific
+// attributes.
+type BgpConditions struct {
+ // original -> bgp-pol:match-community-set
+ // Match a referenced community-set according to the logic
+ // defined in the match-set-options leaf.
+ MatchCommunitySet MatchCommunitySet `mapstructure:"match-community-set" json:"match-community-set,omitempty"`
+ // original -> bgp-pol:match-ext-community-set
+ // Match a referenced extended community-set according to the
+ // logic defined in the match-set-options leaf.
+ MatchExtCommunitySet MatchExtCommunitySet `mapstructure:"match-ext-community-set" json:"match-ext-community-set,omitempty"`
+ // original -> bgp-pol:match-as-path-set
+ // Match a referenced as-path set according to the logic
+ // defined in the match-set-options leaf.
+ MatchAsPathSet MatchAsPathSet `mapstructure:"match-as-path-set" json:"match-as-path-set,omitempty"`
+ // original -> bgp-pol:med-eq
+ // Condition to check if the received MED value is equal to
+ // the specified value.
+ MedEq uint32 `mapstructure:"med-eq" json:"med-eq,omitempty"`
+ // original -> bgp-pol:origin-eq
+ // Condition to check if the route origin is equal to the
+ // specified value.
+ OriginEq BgpOriginAttrType `mapstructure:"origin-eq" json:"origin-eq,omitempty"`
+ // original -> bgp-pol:next-hop-in
+ // original type is list of inet:ip-address
+ // List of next hop addresses to check for in the route
+ // update.
+ NextHopInList []string `mapstructure:"next-hop-in-list" json:"next-hop-in-list,omitempty"`
+ // original -> bgp-pol:afi-safi-in
+ // List of address families which the NLRI may be
+ // within.
+ AfiSafiInList []AfiSafiType `mapstructure:"afi-safi-in-list" json:"afi-safi-in-list,omitempty"`
+ // original -> bgp-pol:local-pref-eq
+ // Condition to check if the local pref attribute is equal to
+ // the specified value.
+ LocalPrefEq uint32 `mapstructure:"local-pref-eq" json:"local-pref-eq,omitempty"`
+ // original -> bgp-pol:community-count
+ // Value and comparison operations for conditions based on the
+ // number of communities in the route update.
+ CommunityCount CommunityCount `mapstructure:"community-count" json:"community-count,omitempty"`
+ // original -> bgp-pol:as-path-length
+ // Value and comparison operations for conditions based on the
+ // length of the AS path in the route update.
+ AsPathLength AsPathLength `mapstructure:"as-path-length" json:"as-path-length,omitempty"`
+ // original -> bgp-pol:route-type
+ // Condition to check the route type in the route update.
+ RouteType RouteType `mapstructure:"route-type" json:"route-type,omitempty"`
+ // original -> gobgp:rpki-validation-result
+ // specify the validation result of RPKI based on ROA as conditions.
+ RpkiValidationResult RpkiValidationResultType `mapstructure:"rpki-validation-result" json:"rpki-validation-result,omitempty"`
+ // original -> gobgp:match-large-community-set
+ MatchLargeCommunitySet MatchLargeCommunitySet `mapstructure:"match-large-community-set" json:"match-large-community-set,omitempty"`
+}
+
+func (lhs *BgpConditions) Equal(rhs *BgpConditions) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.MatchCommunitySet.Equal(&(rhs.MatchCommunitySet)) {
+ return false
+ }
+ if !lhs.MatchExtCommunitySet.Equal(&(rhs.MatchExtCommunitySet)) {
+ return false
+ }
+ if !lhs.MatchAsPathSet.Equal(&(rhs.MatchAsPathSet)) {
+ return false
+ }
+ if lhs.MedEq != rhs.MedEq {
+ return false
+ }
+ if lhs.OriginEq != rhs.OriginEq {
+ return false
+ }
+ if len(lhs.NextHopInList) != len(rhs.NextHopInList) {
+ return false
+ }
+ for idx, l := range lhs.NextHopInList {
+ if l != rhs.NextHopInList[idx] {
+ return false
+ }
+ }
+ if len(lhs.AfiSafiInList) != len(rhs.AfiSafiInList) {
+ return false
+ }
+ for idx, l := range lhs.AfiSafiInList {
+ if l != rhs.AfiSafiInList[idx] {
+ return false
+ }
+ }
+ if lhs.LocalPrefEq != rhs.LocalPrefEq {
+ return false
+ }
+ if !lhs.CommunityCount.Equal(&(rhs.CommunityCount)) {
+ return false
+ }
+ if !lhs.AsPathLength.Equal(&(rhs.AsPathLength)) {
+ return false
+ }
+ if lhs.RouteType != rhs.RouteType {
+ return false
+ }
+ if lhs.RpkiValidationResult != rhs.RpkiValidationResult {
+ return false
+ }
+ if !lhs.MatchLargeCommunitySet.Equal(&(rhs.MatchLargeCommunitySet)) {
+ return false
+ }
+ return true
+}
+
+// struct for container rpol:igp-conditions.
+// Policy conditions for IGP attributes.
+type IgpConditions struct {
+}
+
+func (lhs *IgpConditions) Equal(rhs *IgpConditions) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ return true
+}
+
+// struct for container rpol:match-tag-set.
+// Match a referenced tag set according to the logic defined
+// in the match-options-set leaf.
+type MatchTagSet struct {
+ // original -> rpol:tag-set
+ // References a defined tag set.
+ TagSet string `mapstructure:"tag-set" json:"tag-set,omitempty"`
+ // original -> rpol:match-set-options
+ // Optional parameter that governs the behaviour of the
+ // match operation. This leaf only supports matching on ANY
+ // member of the set or inverting the match. Matching on ALL is
+ // not supported).
+ MatchSetOptions MatchSetOptionsRestrictedType `mapstructure:"match-set-options" json:"match-set-options,omitempty"`
+}
+
+func (lhs *MatchTagSet) Equal(rhs *MatchTagSet) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.TagSet != rhs.TagSet {
+ return false
+ }
+ if lhs.MatchSetOptions != rhs.MatchSetOptions {
+ return false
+ }
+ return true
+}
+
+// struct for container rpol:match-neighbor-set.
+// Match a referenced neighbor set according to the logic
+// defined in the match-set-options-leaf.
+type MatchNeighborSet struct {
+ // original -> rpol:neighbor-set
+ // References a defined neighbor set.
+ NeighborSet string `mapstructure:"neighbor-set" json:"neighbor-set,omitempty"`
+ // original -> rpol:match-set-options
+ // Optional parameter that governs the behaviour of the
+ // match operation. This leaf only supports matching on ANY
+ // member of the set or inverting the match. Matching on ALL is
+ // not supported).
+ MatchSetOptions MatchSetOptionsRestrictedType `mapstructure:"match-set-options" json:"match-set-options,omitempty"`
+}
+
+func (lhs *MatchNeighborSet) Equal(rhs *MatchNeighborSet) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.NeighborSet != rhs.NeighborSet {
+ return false
+ }
+ if lhs.MatchSetOptions != rhs.MatchSetOptions {
+ return false
+ }
+ return true
+}
+
+// struct for container rpol:match-prefix-set.
+// Match a referenced prefix-set according to the logic
+// defined in the match-set-options leaf.
+type MatchPrefixSet struct {
+ // original -> rpol:prefix-set
+ // References a defined prefix set.
+ PrefixSet string `mapstructure:"prefix-set" json:"prefix-set,omitempty"`
+ // original -> rpol:match-set-options
+ // Optional parameter that governs the behaviour of the
+ // match operation. This leaf only supports matching on ANY
+ // member of the set or inverting the match. Matching on ALL is
+ // not supported).
+ MatchSetOptions MatchSetOptionsRestrictedType `mapstructure:"match-set-options" json:"match-set-options,omitempty"`
+}
+
+func (lhs *MatchPrefixSet) Equal(rhs *MatchPrefixSet) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.PrefixSet != rhs.PrefixSet {
+ return false
+ }
+ if lhs.MatchSetOptions != rhs.MatchSetOptions {
+ return false
+ }
+ return true
+}
+
+// struct for container rpol:conditions.
+// Condition statements for this
+// policy statement.
+type Conditions struct {
+ // original -> rpol:call-policy
+ // Applies the statements from the specified policy
+ // definition and then returns control the current
+ // policy statement. Note that the called policy may
+ // itself call other policies (subject to
+ // implementation limitations). This is intended to
+ // provide a policy 'subroutine' capability. The
+ // called policy should contain an explicit or a
+ // default route disposition that returns an
+ // effective true (accept-route) or false
+ // (reject-route), otherwise the behavior may be
+ // ambiguous and implementation dependent.
+ CallPolicy string `mapstructure:"call-policy" json:"call-policy,omitempty"`
+ // original -> rpol:match-prefix-set
+ // Match a referenced prefix-set according to the logic
+ // defined in the match-set-options leaf.
+ MatchPrefixSet MatchPrefixSet `mapstructure:"match-prefix-set" json:"match-prefix-set,omitempty"`
+ // original -> rpol:match-neighbor-set
+ // Match a referenced neighbor set according to the logic
+ // defined in the match-set-options-leaf.
+ MatchNeighborSet MatchNeighborSet `mapstructure:"match-neighbor-set" json:"match-neighbor-set,omitempty"`
+ // original -> rpol:match-tag-set
+ // Match a referenced tag set according to the logic defined
+ // in the match-options-set leaf.
+ MatchTagSet MatchTagSet `mapstructure:"match-tag-set" json:"match-tag-set,omitempty"`
+ // original -> rpol:install-protocol-eq
+ // Condition to check the protocol / method used to install
+ // which installed the route into the local routing table.
+ InstallProtocolEq InstallProtocolType `mapstructure:"install-protocol-eq" json:"install-protocol-eq,omitempty"`
+ // original -> rpol:igp-conditions
+ // Policy conditions for IGP attributes.
+ IgpConditions IgpConditions `mapstructure:"igp-conditions" json:"igp-conditions,omitempty"`
+ // original -> bgp-pol:bgp-conditions
+ // Policy conditions for matching
+ // BGP-specific defined sets or comparing BGP-specific
+ // attributes.
+ BgpConditions BgpConditions `mapstructure:"bgp-conditions" json:"bgp-conditions,omitempty"`
+}
+
+func (lhs *Conditions) Equal(rhs *Conditions) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.CallPolicy != rhs.CallPolicy {
+ return false
+ }
+ if !lhs.MatchPrefixSet.Equal(&(rhs.MatchPrefixSet)) {
+ return false
+ }
+ if !lhs.MatchNeighborSet.Equal(&(rhs.MatchNeighborSet)) {
+ return false
+ }
+ if !lhs.MatchTagSet.Equal(&(rhs.MatchTagSet)) {
+ return false
+ }
+ if lhs.InstallProtocolEq != rhs.InstallProtocolEq {
+ return false
+ }
+ if !lhs.IgpConditions.Equal(&(rhs.IgpConditions)) {
+ return false
+ }
+ if !lhs.BgpConditions.Equal(&(rhs.BgpConditions)) {
+ return false
+ }
+ return true
+}
+
+// struct for container rpol:statement.
+// Policy statements group conditions and actions
+// within a policy definition. They are evaluated in
+// the order specified (see the description of policy
+// evaluation at the top of this module.
+type Statement struct {
+ // original -> rpol:name
+ // name of the policy statement.
+ Name string `mapstructure:"name" json:"name,omitempty"`
+ // original -> rpol:conditions
+ // Condition statements for this
+ // policy statement.
+ Conditions Conditions `mapstructure:"conditions" json:"conditions,omitempty"`
+ // original -> rpol:actions
+ // Action statements for this policy
+ // statement.
+ Actions Actions `mapstructure:"actions" json:"actions,omitempty"`
+}
+
+func (lhs *Statement) Equal(rhs *Statement) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.Name != rhs.Name {
+ return false
+ }
+ if !lhs.Conditions.Equal(&(rhs.Conditions)) {
+ return false
+ }
+ if !lhs.Actions.Equal(&(rhs.Actions)) {
+ return false
+ }
+ return true
+}
+
+// struct for container rpol:policy-definition.
+// List of top-level policy definitions, keyed by unique
+// name. These policy definitions are expected to be
+// referenced (by name) in policy chains specified in import
+// or export configuration statements.
+type PolicyDefinition struct {
+ // original -> rpol:name
+ // Name of the top-level policy definition -- this name
+ // is used in references to the current policy.
+ Name string `mapstructure:"name" json:"name,omitempty"`
+ // original -> rpol:statements
+ // Enclosing container for policy statements.
+ Statements []Statement `mapstructure:"statements" json:"statements,omitempty"`
+}
+
+func (lhs *PolicyDefinition) Equal(rhs *PolicyDefinition) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.Name != rhs.Name {
+ return false
+ }
+ if len(lhs.Statements) != len(rhs.Statements) {
+ return false
+ }
+ {
+ lmap := make(map[string]*Statement)
+ for i, l := range lhs.Statements {
+ lmap[mapkey(i, string(l.Name))] = &lhs.Statements[i]
+ }
+ for i, r := range rhs.Statements {
+ if l, y := lmap[mapkey(i, string(r.Name))]; !y {
+ return false
+ } else if !r.Equal(l) {
+ return false
+ }
+ }
+ }
+ return true
+}
+
+// struct for container gobgp:large-community-set.
+type LargeCommunitySet struct {
+ // original -> gobgp:large-community-set-name
+ LargeCommunitySetName string `mapstructure:"large-community-set-name" json:"large-community-set-name,omitempty"`
+ // original -> gobgp:large-community
+ // extended community set member.
+ LargeCommunityList []string `mapstructure:"large-community-list" json:"large-community-list,omitempty"`
+}
+
+func (lhs *LargeCommunitySet) Equal(rhs *LargeCommunitySet) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.LargeCommunitySetName != rhs.LargeCommunitySetName {
+ return false
+ }
+ if len(lhs.LargeCommunityList) != len(rhs.LargeCommunityList) {
+ return false
+ }
+ for idx, l := range lhs.LargeCommunityList {
+ if l != rhs.LargeCommunityList[idx] {
+ return false
+ }
+ }
+ return true
+}
+
+// struct for container bgp-pol:as-path-set.
+// Definitions for AS path sets.
+type AsPathSet struct {
+ // original -> bgp-pol:as-path-set-name
+ // name of the AS path set -- this is used to reference
+ // the set in match conditions.
+ AsPathSetName string `mapstructure:"as-path-set-name" json:"as-path-set-name,omitempty"`
+ // original -> gobgp:as-path
+ // AS path expression.
+ AsPathList []string `mapstructure:"as-path-list" json:"as-path-list,omitempty"`
+}
+
+func (lhs *AsPathSet) Equal(rhs *AsPathSet) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.AsPathSetName != rhs.AsPathSetName {
+ return false
+ }
+ if len(lhs.AsPathList) != len(rhs.AsPathList) {
+ return false
+ }
+ for idx, l := range lhs.AsPathList {
+ if l != rhs.AsPathList[idx] {
+ return false
+ }
+ }
+ return true
+}
+
+// struct for container bgp-pol:ext-community-set.
+// Definitions for extended community sets.
+type ExtCommunitySet struct {
+ // original -> bgp-pol:ext-community-set-name
+ // name / label of the extended community set -- this is
+ // used to reference the set in match conditions.
+ ExtCommunitySetName string `mapstructure:"ext-community-set-name" json:"ext-community-set-name,omitempty"`
+ // original -> gobgp:ext-community
+ // extended community set member.
+ ExtCommunityList []string `mapstructure:"ext-community-list" json:"ext-community-list,omitempty"`
+}
+
+func (lhs *ExtCommunitySet) Equal(rhs *ExtCommunitySet) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.ExtCommunitySetName != rhs.ExtCommunitySetName {
+ return false
+ }
+ if len(lhs.ExtCommunityList) != len(rhs.ExtCommunityList) {
+ return false
+ }
+ for idx, l := range lhs.ExtCommunityList {
+ if l != rhs.ExtCommunityList[idx] {
+ return false
+ }
+ }
+ return true
+}
+
+// struct for container bgp-pol:community-set.
+// Definitions for community sets.
+type CommunitySet struct {
+ // original -> bgp-pol:community-set-name
+ // name / label of the community set -- this is used to
+ // reference the set in match conditions.
+ CommunitySetName string `mapstructure:"community-set-name" json:"community-set-name,omitempty"`
+ // original -> gobgp:community
+ // community set member.
+ CommunityList []string `mapstructure:"community-list" json:"community-list,omitempty"`
+}
+
+func (lhs *CommunitySet) Equal(rhs *CommunitySet) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.CommunitySetName != rhs.CommunitySetName {
+ return false
+ }
+ if len(lhs.CommunityList) != len(rhs.CommunityList) {
+ return false
+ }
+ for idx, l := range lhs.CommunityList {
+ if l != rhs.CommunityList[idx] {
+ return false
+ }
+ }
+ return true
+}
+
+// struct for container bgp-pol:bgp-defined-sets.
+// BGP-related set definitions for policy match conditions.
+type BgpDefinedSets struct {
+ // original -> bgp-pol:community-sets
+ // Enclosing container for community sets.
+ CommunitySets []CommunitySet `mapstructure:"community-sets" json:"community-sets,omitempty"`
+ // original -> bgp-pol:ext-community-sets
+ // Enclosing container for extended community sets.
+ ExtCommunitySets []ExtCommunitySet `mapstructure:"ext-community-sets" json:"ext-community-sets,omitempty"`
+ // original -> bgp-pol:as-path-sets
+ // Enclosing container for AS path sets.
+ AsPathSets []AsPathSet `mapstructure:"as-path-sets" json:"as-path-sets,omitempty"`
+ // original -> gobgp:large-community-sets
+ LargeCommunitySets []LargeCommunitySet `mapstructure:"large-community-sets" json:"large-community-sets,omitempty"`
+}
+
+func (lhs *BgpDefinedSets) Equal(rhs *BgpDefinedSets) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if len(lhs.CommunitySets) != len(rhs.CommunitySets) {
+ return false
+ }
+ {
+ lmap := make(map[string]*CommunitySet)
+ for i, l := range lhs.CommunitySets {
+ lmap[mapkey(i, string(l.CommunitySetName))] = &lhs.CommunitySets[i]
+ }
+ for i, r := range rhs.CommunitySets {
+ if l, y := lmap[mapkey(i, string(r.CommunitySetName))]; !y {
+ return false
+ } else if !r.Equal(l) {
+ return false
+ }
+ }
+ }
+ if len(lhs.ExtCommunitySets) != len(rhs.ExtCommunitySets) {
+ return false
+ }
+ {
+ lmap := make(map[string]*ExtCommunitySet)
+ for i, l := range lhs.ExtCommunitySets {
+ lmap[mapkey(i, string(l.ExtCommunitySetName))] = &lhs.ExtCommunitySets[i]
+ }
+ for i, r := range rhs.ExtCommunitySets {
+ if l, y := lmap[mapkey(i, string(r.ExtCommunitySetName))]; !y {
+ return false
+ } else if !r.Equal(l) {
+ return false
+ }
+ }
+ }
+ if len(lhs.AsPathSets) != len(rhs.AsPathSets) {
+ return false
+ }
+ {
+ lmap := make(map[string]*AsPathSet)
+ for i, l := range lhs.AsPathSets {
+ lmap[mapkey(i, string(l.AsPathSetName))] = &lhs.AsPathSets[i]
+ }
+ for i, r := range rhs.AsPathSets {
+ if l, y := lmap[mapkey(i, string(r.AsPathSetName))]; !y {
+ return false
+ } else if !r.Equal(l) {
+ return false
+ }
+ }
+ }
+ if len(lhs.LargeCommunitySets) != len(rhs.LargeCommunitySets) {
+ return false
+ }
+ {
+ lmap := make(map[string]*LargeCommunitySet)
+ for i, l := range lhs.LargeCommunitySets {
+ lmap[mapkey(i, string(l.LargeCommunitySetName))] = &lhs.LargeCommunitySets[i]
+ }
+ for i, r := range rhs.LargeCommunitySets {
+ if l, y := lmap[mapkey(i, string(r.LargeCommunitySetName))]; !y {
+ return false
+ } else if !r.Equal(l) {
+ return false
+ }
+ }
+ }
+ return true
+}
+
+// struct for container rpol:tag.
+// list of tags that are part of the tag set.
+type Tag struct {
+ // original -> rpol:value
+ // Value of the tag set member.
+ Value TagType `mapstructure:"value" json:"value,omitempty"`
+}
+
+func (lhs *Tag) Equal(rhs *Tag) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.Value != rhs.Value {
+ return false
+ }
+ return true
+}
+
+// struct for container rpol:tag-set.
+// Definitions for tag sets.
+type TagSet struct {
+ // original -> rpol:tag-set-name
+ // name / label of the tag set -- this is used to reference
+ // the set in match conditions.
+ TagSetName string `mapstructure:"tag-set-name" json:"tag-set-name,omitempty"`
+ // original -> rpol:tag
+ // list of tags that are part of the tag set.
+ TagList []Tag `mapstructure:"tag-list" json:"tag-list,omitempty"`
+}
+
+func (lhs *TagSet) Equal(rhs *TagSet) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.TagSetName != rhs.TagSetName {
+ return false
+ }
+ if len(lhs.TagList) != len(rhs.TagList) {
+ return false
+ }
+ {
+ lmap := make(map[string]*Tag)
+ for i, l := range lhs.TagList {
+ lmap[mapkey(i, string(l.Value))] = &lhs.TagList[i]
+ }
+ for i, r := range rhs.TagList {
+ if l, y := lmap[mapkey(i, string(r.Value))]; !y {
+ return false
+ } else if !r.Equal(l) {
+ return false
+ }
+ }
+ }
+ return true
+}
+
+// struct for container rpol:neighbor-set.
+// Definitions for neighbor sets.
+type NeighborSet struct {
+ // original -> rpol:neighbor-set-name
+ // name / label of the neighbor set -- this is used to
+ // reference the set in match conditions.
+ NeighborSetName string `mapstructure:"neighbor-set-name" json:"neighbor-set-name,omitempty"`
+ // original -> gobgp:neighbor-info
+ // original type is list of inet:ip-address
+ // neighbor ip address or prefix.
+ NeighborInfoList []string `mapstructure:"neighbor-info-list" json:"neighbor-info-list,omitempty"`
+}
+
+func (lhs *NeighborSet) Equal(rhs *NeighborSet) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.NeighborSetName != rhs.NeighborSetName {
+ return false
+ }
+ if len(lhs.NeighborInfoList) != len(rhs.NeighborInfoList) {
+ return false
+ }
+ for idx, l := range lhs.NeighborInfoList {
+ if l != rhs.NeighborInfoList[idx] {
+ return false
+ }
+ }
+ return true
+}
+
+// struct for container rpol:prefix.
+// List of prefix expressions that are part of the set.
+type Prefix struct {
+ // original -> rpol:ip-prefix
+ // rpol:ip-prefix's original type is inet:ip-prefix.
+ // The prefix member in CIDR notation -- while the
+ // prefix may be either IPv4 or IPv6, most
+ // implementations require all members of the prefix set
+ // to be the same address family. Mixing address types in
+ // the same prefix set is likely to cause an error.
+ IpPrefix string `mapstructure:"ip-prefix" json:"ip-prefix,omitempty"`
+ // original -> rpol:masklength-range
+ // Defines a range for the masklength, or 'exact' if
+ // the prefix has an exact length.
+ //
+ // Example: 10.3.192.0/21 through 10.3.192.0/24 would be
+ // expressed as prefix: 10.3.192.0/21,
+ // masklength-range: 21..24.
+ //
+ // Example: 10.3.192.0/21 would be expressed as
+ // prefix: 10.3.192.0/21,
+ // masklength-range: exact.
+ MasklengthRange string `mapstructure:"masklength-range" json:"masklength-range,omitempty"`
+}
+
+func (lhs *Prefix) Equal(rhs *Prefix) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.IpPrefix != rhs.IpPrefix {
+ return false
+ }
+ if lhs.MasklengthRange != rhs.MasklengthRange {
+ return false
+ }
+ return true
+}
+
+// struct for container rpol:prefix-set.
+// List of the defined prefix sets.
+type PrefixSet struct {
+ // original -> rpol:prefix-set-name
+ // name / label of the prefix set -- this is used to
+ // reference the set in match conditions.
+ PrefixSetName string `mapstructure:"prefix-set-name" json:"prefix-set-name,omitempty"`
+ // original -> rpol:prefix
+ // List of prefix expressions that are part of the set.
+ PrefixList []Prefix `mapstructure:"prefix-list" json:"prefix-list,omitempty"`
+}
+
+func (lhs *PrefixSet) Equal(rhs *PrefixSet) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if lhs.PrefixSetName != rhs.PrefixSetName {
+ return false
+ }
+ if len(lhs.PrefixList) != len(rhs.PrefixList) {
+ return false
+ }
+ {
+ lmap := make(map[string]*Prefix)
+ for i, l := range lhs.PrefixList {
+ lmap[mapkey(i, string(l.IpPrefix+l.MasklengthRange))] = &lhs.PrefixList[i]
+ }
+ for i, r := range rhs.PrefixList {
+ if l, y := lmap[mapkey(i, string(r.IpPrefix+r.MasklengthRange))]; !y {
+ return false
+ } else if !r.Equal(l) {
+ return false
+ }
+ }
+ }
+ return true
+}
+
+// struct for container rpol:defined-sets.
+// Predefined sets of attributes used in policy match
+// statements.
+type DefinedSets struct {
+ // original -> rpol:prefix-sets
+ // Enclosing container for defined prefix sets for matching.
+ PrefixSets []PrefixSet `mapstructure:"prefix-sets" json:"prefix-sets,omitempty"`
+ // original -> rpol:neighbor-sets
+ // Enclosing container for defined neighbor sets for matching.
+ NeighborSets []NeighborSet `mapstructure:"neighbor-sets" json:"neighbor-sets,omitempty"`
+ // original -> rpol:tag-sets
+ // Enclosing container for defined tag sets for matching.
+ TagSets []TagSet `mapstructure:"tag-sets" json:"tag-sets,omitempty"`
+ // original -> bgp-pol:bgp-defined-sets
+ // BGP-related set definitions for policy match conditions.
+ BgpDefinedSets BgpDefinedSets `mapstructure:"bgp-defined-sets" json:"bgp-defined-sets,omitempty"`
+}
+
+func (lhs *DefinedSets) Equal(rhs *DefinedSets) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if len(lhs.PrefixSets) != len(rhs.PrefixSets) {
+ return false
+ }
+ {
+ lmap := make(map[string]*PrefixSet)
+ for i, l := range lhs.PrefixSets {
+ lmap[mapkey(i, string(l.PrefixSetName))] = &lhs.PrefixSets[i]
+ }
+ for i, r := range rhs.PrefixSets {
+ if l, y := lmap[mapkey(i, string(r.PrefixSetName))]; !y {
+ return false
+ } else if !r.Equal(l) {
+ return false
+ }
+ }
+ }
+ if len(lhs.NeighborSets) != len(rhs.NeighborSets) {
+ return false
+ }
+ {
+ lmap := make(map[string]*NeighborSet)
+ for i, l := range lhs.NeighborSets {
+ lmap[mapkey(i, string(l.NeighborSetName))] = &lhs.NeighborSets[i]
+ }
+ for i, r := range rhs.NeighborSets {
+ if l, y := lmap[mapkey(i, string(r.NeighborSetName))]; !y {
+ return false
+ } else if !r.Equal(l) {
+ return false
+ }
+ }
+ }
+ if len(lhs.TagSets) != len(rhs.TagSets) {
+ return false
+ }
+ {
+ lmap := make(map[string]*TagSet)
+ for i, l := range lhs.TagSets {
+ lmap[mapkey(i, string(l.TagSetName))] = &lhs.TagSets[i]
+ }
+ for i, r := range rhs.TagSets {
+ if l, y := lmap[mapkey(i, string(r.TagSetName))]; !y {
+ return false
+ } else if !r.Equal(l) {
+ return false
+ }
+ }
+ }
+ if !lhs.BgpDefinedSets.Equal(&(rhs.BgpDefinedSets)) {
+ return false
+ }
+ return true
+}
+
+// struct for container rpol:routing-policy.
+// top-level container for all routing policy configuration.
+type RoutingPolicy struct {
+ // original -> rpol:defined-sets
+ // Predefined sets of attributes used in policy match
+ // statements.
+ DefinedSets DefinedSets `mapstructure:"defined-sets" json:"defined-sets,omitempty"`
+ // original -> rpol:policy-definitions
+ // Enclosing container for the list of top-level policy
+ // definitions.
+ PolicyDefinitions []PolicyDefinition `mapstructure:"policy-definitions" json:"policy-definitions,omitempty"`
+}
+
+func (lhs *RoutingPolicy) Equal(rhs *RoutingPolicy) bool {
+ if lhs == nil || rhs == nil {
+ return false
+ }
+ if !lhs.DefinedSets.Equal(&(rhs.DefinedSets)) {
+ return false
+ }
+ if len(lhs.PolicyDefinitions) != len(rhs.PolicyDefinitions) {
+ return false
+ }
+ {
+ lmap := make(map[string]*PolicyDefinition)
+ for i, l := range lhs.PolicyDefinitions {
+ lmap[mapkey(i, string(l.Name))] = &lhs.PolicyDefinitions[i]
+ }
+ for i, r := range rhs.PolicyDefinitions {
+ if l, y := lmap[mapkey(i, string(r.Name))]; !y {
+ return false
+ } else if !r.Equal(l) {
+ return false
+ }
+ }
+ }
+ return true
+}
diff --git a/internal/pkg/config/bgp_configs_test.go b/internal/pkg/config/bgp_configs_test.go
new file mode 100644
index 00000000..6d6853a7
--- /dev/null
+++ b/internal/pkg/config/bgp_configs_test.go
@@ -0,0 +1,113 @@
+// Copyright (C) 2016 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+ "bufio"
+ "os"
+ "path"
+ "runtime"
+ "strings"
+ "testing"
+
+ "github.com/spf13/viper"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestEqual(t *testing.T) {
+ assert := assert.New(t)
+ p1 := Prefix{
+ IpPrefix: "192.168.0.0",
+ MasklengthRange: "24..32",
+ }
+ p2 := Prefix{
+ IpPrefix: "192.168.0.0",
+ MasklengthRange: "24..32",
+ }
+ assert.True(p1.Equal(&p2))
+ assert.False(p1.Equal(nil))
+ var p3 *Prefix
+ assert.False(p3.Equal(&p1))
+ p3 = &Prefix{
+ IpPrefix: "192.168.0.0",
+ MasklengthRange: "24..32",
+ }
+ assert.True(p3.Equal(&p1))
+ p3.IpPrefix = "10.10.0.0"
+ assert.False(p3.Equal(&p1))
+ ps1 := PrefixSet{
+ PrefixSetName: "ps",
+ PrefixList: []Prefix{p1, p2},
+ }
+ ps2 := PrefixSet{
+ PrefixSetName: "ps",
+ PrefixList: []Prefix{p2, p1},
+ }
+ assert.True(ps1.Equal(&ps2))
+ ps2.PrefixSetName = "ps2"
+ assert.False(ps1.Equal(&ps2))
+}
+
+func extractTomlFromMarkdown(fileMd string, fileToml string) error {
+ fMd, err := os.Open(fileMd)
+ if err != nil {
+ return err
+ }
+ defer fMd.Close()
+
+ fToml, err := os.Create(fileToml)
+ if err != nil {
+ return err
+ }
+ defer fToml.Close()
+
+ isBody := false
+ scanner := bufio.NewScanner(fMd)
+ fTomlWriter := bufio.NewWriter(fToml)
+ for scanner.Scan() {
+ if curText := scanner.Text(); strings.HasPrefix(curText, "```toml") {
+ isBody = true
+ } else if strings.HasPrefix(curText, "```") {
+ isBody = false
+ } else if isBody {
+ if _, err := fTomlWriter.WriteString(curText + "\n"); err != nil {
+ return err
+ }
+ }
+ }
+
+ fTomlWriter.Flush()
+ return scanner.Err()
+}
+
+func TestConfigExample(t *testing.T) {
+ assert := assert.New(t)
+
+ _, f, _, _ := runtime.Caller(0)
+ fileMd := path.Join(path.Dir(f), "../../../docs/sources/configuration.md")
+ fileToml := "/tmp/gobgpd.example.toml"
+ assert.NoError(extractTomlFromMarkdown(fileMd, fileToml))
+ defer os.Remove(fileToml)
+
+ format := detectConfigFileType(fileToml, "")
+ c := &BgpConfigSet{}
+ v := viper.New()
+ v.SetConfigFile(fileToml)
+ v.SetConfigType(format)
+ assert.NoError(v.ReadInConfig())
+ assert.NoError(v.UnmarshalExact(c))
+ assert.NoError(setDefaultConfigValuesWithViper(v, c))
+}
diff --git a/internal/pkg/config/default.go b/internal/pkg/config/default.go
new file mode 100644
index 00000000..6ab1eddc
--- /dev/null
+++ b/internal/pkg/config/default.go
@@ -0,0 +1,524 @@
+package config
+
+import (
+ "encoding/binary"
+ "fmt"
+ "math"
+ "net"
+ "reflect"
+ "strconv"
+
+ "github.com/osrg/gobgp/pkg/packet/bgp"
+ "github.com/osrg/gobgp/pkg/packet/bmp"
+ "github.com/osrg/gobgp/pkg/packet/rtr"
+ "github.com/spf13/viper"
+)
+
+const (
+ DEFAULT_HOLDTIME = 90
+ DEFAULT_IDLE_HOLDTIME_AFTER_RESET = 30
+ DEFAULT_CONNECT_RETRY = 120
+)
+
+var forcedOverwrittenConfig = []string{
+ "neighbor.config.peer-as",
+ "neighbor.timers.config.minimum-advertisement-interval",
+}
+
+var configuredFields map[string]interface{}
+
+func RegisterConfiguredFields(addr string, n interface{}) {
+ if configuredFields == nil {
+ configuredFields = make(map[string]interface{})
+ }
+ configuredFields[addr] = n
+}
+
+func defaultAfiSafi(typ AfiSafiType, enable bool) AfiSafi {
+ return AfiSafi{
+ Config: AfiSafiConfig{
+ AfiSafiName: typ,
+ Enabled: enable,
+ },
+ State: AfiSafiState{
+ AfiSafiName: typ,
+ Family: bgp.AddressFamilyValueMap[string(typ)],
+ },
+ }
+}
+
+func SetDefaultNeighborConfigValues(n *Neighbor, pg *PeerGroup, g *Global) error {
+ // Determines this function is called against the same Neighbor struct,
+ // and if already called, returns immediately.
+ if n.State.LocalAs != 0 {
+ return nil
+ }
+
+ return setDefaultNeighborConfigValuesWithViper(nil, n, g, pg)
+}
+
+func setDefaultNeighborConfigValuesWithViper(v *viper.Viper, n *Neighbor, g *Global, pg *PeerGroup) error {
+ if n == nil {
+ return fmt.Errorf("neighbor config is nil")
+ }
+ if g == nil {
+ return fmt.Errorf("global config is nil")
+ }
+
+ if v == nil {
+ v = viper.New()
+ }
+
+ if pg != nil {
+ if err := OverwriteNeighborConfigWithPeerGroup(n, pg); err != nil {
+ return err
+ }
+ }
+
+ if n.Config.LocalAs == 0 {
+ n.Config.LocalAs = g.Config.As
+ if !g.Confederation.Config.Enabled || n.IsConfederation(g) {
+ n.Config.LocalAs = g.Config.As
+ } else {
+ n.Config.LocalAs = g.Confederation.Config.Identifier
+ }
+ }
+ n.State.LocalAs = n.Config.LocalAs
+
+ if n.Config.PeerAs != n.Config.LocalAs {
+ n.Config.PeerType = PEER_TYPE_EXTERNAL
+ n.State.PeerType = PEER_TYPE_EXTERNAL
+ n.State.RemovePrivateAs = n.Config.RemovePrivateAs
+ n.AsPathOptions.State.ReplacePeerAs = n.AsPathOptions.Config.ReplacePeerAs
+ } else {
+ n.Config.PeerType = PEER_TYPE_INTERNAL
+ n.State.PeerType = PEER_TYPE_INTERNAL
+ if string(n.Config.RemovePrivateAs) != "" {
+ return fmt.Errorf("can't set remove-private-as for iBGP peer")
+ }
+ if n.AsPathOptions.Config.ReplacePeerAs {
+ return fmt.Errorf("can't set replace-peer-as for iBGP peer")
+ }
+ }
+
+ if n.State.NeighborAddress == "" {
+ n.State.NeighborAddress = n.Config.NeighborAddress
+ }
+
+ n.State.PeerAs = n.Config.PeerAs
+ n.AsPathOptions.State.AllowOwnAs = n.AsPathOptions.Config.AllowOwnAs
+
+ if !v.IsSet("neighbor.error-handling.config.treat-as-withdraw") {
+ n.ErrorHandling.Config.TreatAsWithdraw = true
+ }
+
+ if !v.IsSet("neighbor.timers.config.connect-retry") && n.Timers.Config.ConnectRetry == 0 {
+ n.Timers.Config.ConnectRetry = float64(DEFAULT_CONNECT_RETRY)
+ }
+ if !v.IsSet("neighbor.timers.config.hold-time") && n.Timers.Config.HoldTime == 0 {
+ n.Timers.Config.HoldTime = float64(DEFAULT_HOLDTIME)
+ }
+ if !v.IsSet("neighbor.timers.config.keepalive-interval") && n.Timers.Config.KeepaliveInterval == 0 {
+ n.Timers.Config.KeepaliveInterval = n.Timers.Config.HoldTime / 3
+ }
+ if !v.IsSet("neighbor.timers.config.idle-hold-time-after-reset") && n.Timers.Config.IdleHoldTimeAfterReset == 0 {
+ n.Timers.Config.IdleHoldTimeAfterReset = float64(DEFAULT_IDLE_HOLDTIME_AFTER_RESET)
+ }
+
+ if n.Config.NeighborInterface != "" {
+ if n.RouteServer.Config.RouteServerClient {
+ return fmt.Errorf("configuring route server client as unnumbered peer is not supported")
+ }
+ addr, err := GetIPv6LinkLocalNeighborAddress(n.Config.NeighborInterface)
+ if err != nil {
+ return err
+ }
+ n.State.NeighborAddress = addr
+ }
+
+ if n.Transport.Config.LocalAddress == "" {
+ if n.State.NeighborAddress == "" {
+ return fmt.Errorf("no neighbor address/interface specified")
+ }
+ ipAddr, err := net.ResolveIPAddr("ip", n.State.NeighborAddress)
+ if err != nil {
+ return err
+ }
+ localAddress := "0.0.0.0"
+ if ipAddr.IP.To4() == nil {
+ localAddress = "::"
+ if ipAddr.Zone != "" {
+ localAddress, err = getIPv6LinkLocalAddress(ipAddr.Zone)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ n.Transport.Config.LocalAddress = localAddress
+ }
+
+ if len(n.AfiSafis) == 0 {
+ if n.Config.NeighborInterface != "" {
+ n.AfiSafis = []AfiSafi{
+ defaultAfiSafi(AFI_SAFI_TYPE_IPV4_UNICAST, true),
+ defaultAfiSafi(AFI_SAFI_TYPE_IPV6_UNICAST, true),
+ }
+ } else if ipAddr, err := net.ResolveIPAddr("ip", n.State.NeighborAddress); err != nil {
+ return fmt.Errorf("invalid neighbor address: %s", n.State.NeighborAddress)
+ } else if ipAddr.IP.To4() != nil {
+ n.AfiSafis = []AfiSafi{defaultAfiSafi(AFI_SAFI_TYPE_IPV4_UNICAST, true)}
+ } else {
+ n.AfiSafis = []AfiSafi{defaultAfiSafi(AFI_SAFI_TYPE_IPV6_UNICAST, true)}
+ }
+ for i := range n.AfiSafis {
+ n.AfiSafis[i].AddPaths.Config.Receive = n.AddPaths.Config.Receive
+ n.AfiSafis[i].AddPaths.State.Receive = n.AddPaths.Config.Receive
+ n.AfiSafis[i].AddPaths.Config.SendMax = n.AddPaths.Config.SendMax
+ n.AfiSafis[i].AddPaths.State.SendMax = n.AddPaths.Config.SendMax
+ }
+ } else {
+ afs, err := extractArray(v.Get("neighbor.afi-safis"))
+ if err != nil {
+ return err
+ }
+ for i := range n.AfiSafis {
+ vv := viper.New()
+ if len(afs) > i {
+ vv.Set("afi-safi", afs[i])
+ }
+ rf, err := bgp.GetRouteFamily(string(n.AfiSafis[i].Config.AfiSafiName))
+ if err != nil {
+ return err
+ }
+ n.AfiSafis[i].State.Family = rf
+ n.AfiSafis[i].State.AfiSafiName = n.AfiSafis[i].Config.AfiSafiName
+ if !vv.IsSet("afi-safi.config.enabled") {
+ n.AfiSafis[i].Config.Enabled = true
+ }
+ n.AfiSafis[i].MpGracefulRestart.State.Enabled = n.AfiSafis[i].MpGracefulRestart.Config.Enabled
+ if !vv.IsSet("afi-safi.add-paths.config.receive") {
+ n.AfiSafis[i].AddPaths.Config.Receive = n.AddPaths.Config.Receive
+ }
+ n.AfiSafis[i].AddPaths.State.Receive = n.AfiSafis[i].AddPaths.Config.Receive
+ if !vv.IsSet("afi-safi.add-paths.config.send-max") {
+ n.AfiSafis[i].AddPaths.Config.SendMax = n.AddPaths.Config.SendMax
+ }
+ n.AfiSafis[i].AddPaths.State.SendMax = n.AfiSafis[i].AddPaths.Config.SendMax
+ }
+ }
+
+ n.State.Description = n.Config.Description
+ n.State.AdminDown = n.Config.AdminDown
+
+ if n.GracefulRestart.Config.Enabled {
+ if !v.IsSet("neighbor.graceful-restart.config.restart-time") && n.GracefulRestart.Config.RestartTime == 0 {
+ // RFC 4724 4. Operation
+ // A suggested default for the Restart Time is a value less than or
+ // equal to the HOLDTIME carried in the OPEN.
+ n.GracefulRestart.Config.RestartTime = uint16(n.Timers.Config.HoldTime)
+ }
+ if !v.IsSet("neighbor.graceful-restart.config.deferral-time") && n.GracefulRestart.Config.DeferralTime == 0 {
+ // RFC 4724 4.1. Procedures for the Restarting Speaker
+ // The value of this timer should be large
+ // enough, so as to provide all the peers of the Restarting Speaker with
+ // enough time to send all the routes to the Restarting Speaker
+ n.GracefulRestart.Config.DeferralTime = uint16(360)
+ }
+ }
+
+ if n.EbgpMultihop.Config.Enabled {
+ if n.TtlSecurity.Config.Enabled {
+ return fmt.Errorf("ebgp-multihop and ttl-security are mututally exclusive")
+ }
+ if n.EbgpMultihop.Config.MultihopTtl == 0 {
+ n.EbgpMultihop.Config.MultihopTtl = 255
+ }
+ } else if n.TtlSecurity.Config.Enabled {
+ if n.TtlSecurity.Config.TtlMin == 0 {
+ n.TtlSecurity.Config.TtlMin = 255
+ }
+ }
+
+ if n.RouteReflector.Config.RouteReflectorClient {
+ if n.RouteReflector.Config.RouteReflectorClusterId == "" {
+ n.RouteReflector.State.RouteReflectorClusterId = RrClusterIdType(g.Config.RouterId)
+ } else {
+ id := string(n.RouteReflector.Config.RouteReflectorClusterId)
+ if ip := net.ParseIP(id).To4(); ip != nil {
+ n.RouteReflector.State.RouteReflectorClusterId = n.RouteReflector.Config.RouteReflectorClusterId
+ } else if num, err := strconv.ParseUint(id, 10, 32); err == nil {
+ ip = make(net.IP, 4)
+ binary.BigEndian.PutUint32(ip, uint32(num))
+ n.RouteReflector.State.RouteReflectorClusterId = RrClusterIdType(ip.String())
+ } else {
+ return fmt.Errorf("route-reflector-cluster-id should be specified as IPv4 address or 32-bit unsigned integer")
+ }
+ }
+ }
+
+ return nil
+}
+
+func SetDefaultGlobalConfigValues(g *Global) error {
+ if len(g.AfiSafis) == 0 {
+ g.AfiSafis = []AfiSafi{}
+ for k := range AfiSafiTypeToIntMap {
+ g.AfiSafis = append(g.AfiSafis, defaultAfiSafi(k, true))
+ }
+ }
+
+ if g.Config.Port == 0 {
+ g.Config.Port = bgp.BGP_PORT
+ }
+
+ if len(g.Config.LocalAddressList) == 0 {
+ g.Config.LocalAddressList = []string{"0.0.0.0", "::"}
+ }
+ return nil
+}
+
+func setDefaultVrfConfigValues(v *Vrf) error {
+ if v == nil {
+ return fmt.Errorf("cannot set default values for nil vrf config")
+ }
+
+ if v.Config.Name == "" {
+ return fmt.Errorf("specify vrf name")
+ }
+
+ _, err := bgp.ParseRouteDistinguisher(v.Config.Rd)
+ if err != nil {
+ return fmt.Errorf("invalid rd for vrf %s: %s", v.Config.Name, v.Config.Rd)
+ }
+
+ if len(v.Config.ImportRtList) == 0 {
+ v.Config.ImportRtList = v.Config.BothRtList
+ }
+ for _, rtString := range v.Config.ImportRtList {
+ _, err := bgp.ParseRouteTarget(rtString)
+ if err != nil {
+ return fmt.Errorf("invalid import rt for vrf %s: %s", v.Config.Name, rtString)
+ }
+ }
+
+ if len(v.Config.ExportRtList) == 0 {
+ v.Config.ExportRtList = v.Config.BothRtList
+ }
+ for _, rtString := range v.Config.ExportRtList {
+ _, err := bgp.ParseRouteTarget(rtString)
+ if err != nil {
+ return fmt.Errorf("invalid export rt for vrf %s: %s", v.Config.Name, rtString)
+ }
+ }
+
+ return nil
+}
+
+func SetDefaultConfigValues(b *BgpConfigSet) error {
+ return setDefaultConfigValuesWithViper(nil, b)
+}
+
+func setDefaultPolicyConfigValuesWithViper(v *viper.Viper, p *PolicyDefinition) error {
+ stmts, err := extractArray(v.Get("policy.statements"))
+ if err != nil {
+ return err
+ }
+ for i := range p.Statements {
+ vv := viper.New()
+ if len(stmts) > i {
+ vv.Set("statement", stmts[i])
+ }
+ if !vv.IsSet("statement.actions.route-disposition") {
+ p.Statements[i].Actions.RouteDisposition = ROUTE_DISPOSITION_NONE
+ }
+ }
+ return nil
+}
+
+func setDefaultConfigValuesWithViper(v *viper.Viper, b *BgpConfigSet) error {
+ if v == nil {
+ v = viper.New()
+ }
+
+ if err := SetDefaultGlobalConfigValues(&b.Global); err != nil {
+ return err
+ }
+
+ for idx, server := range b.BmpServers {
+ if server.Config.Port == 0 {
+ server.Config.Port = bmp.BMP_DEFAULT_PORT
+ }
+ if server.Config.RouteMonitoringPolicy == "" {
+ server.Config.RouteMonitoringPolicy = BMP_ROUTE_MONITORING_POLICY_TYPE_PRE_POLICY
+ }
+ // statistics-timeout is uint16 value and implicitly less than 65536
+ if server.Config.StatisticsTimeout != 0 && server.Config.StatisticsTimeout < 15 {
+ return fmt.Errorf("too small statistics-timeout value: %d", server.Config.StatisticsTimeout)
+ }
+ b.BmpServers[idx] = server
+ }
+
+ vrfNames := make(map[string]struct{})
+ vrfIDs := make(map[uint32]struct{})
+ for idx, vrf := range b.Vrfs {
+ if err := setDefaultVrfConfigValues(&vrf); err != nil {
+ return err
+ }
+
+ if _, ok := vrfNames[vrf.Config.Name]; ok {
+ return fmt.Errorf("duplicated vrf name: %s", vrf.Config.Name)
+ }
+ vrfNames[vrf.Config.Name] = struct{}{}
+
+ if vrf.Config.Id != 0 {
+ if _, ok := vrfIDs[vrf.Config.Id]; ok {
+ return fmt.Errorf("duplicated vrf id: %d", vrf.Config.Id)
+ }
+ vrfIDs[vrf.Config.Id] = struct{}{}
+ }
+
+ b.Vrfs[idx] = vrf
+ }
+ // Auto assign VRF identifier
+ for idx, vrf := range b.Vrfs {
+ if vrf.Config.Id == 0 {
+ for id := uint32(1); id < math.MaxUint32; id++ {
+ if _, ok := vrfIDs[id]; !ok {
+ vrf.Config.Id = id
+ vrfIDs[id] = struct{}{}
+ break
+ }
+ }
+ }
+ b.Vrfs[idx] = vrf
+ }
+
+ if b.Zebra.Config.Url == "" {
+ b.Zebra.Config.Url = "unix:/var/run/quagga/zserv.api"
+ }
+ if b.Zebra.Config.Version < 2 {
+ b.Zebra.Config.Version = 2
+ } else if b.Zebra.Config.Version > 4 {
+ b.Zebra.Config.Version = 4
+ }
+ if !v.IsSet("zebra.config.nexthop-trigger-enable") && !b.Zebra.Config.NexthopTriggerEnable && b.Zebra.Config.Version > 2 {
+ b.Zebra.Config.NexthopTriggerEnable = true
+ }
+ if b.Zebra.Config.NexthopTriggerDelay == 0 {
+ b.Zebra.Config.NexthopTriggerDelay = 5
+ }
+
+ list, err := extractArray(v.Get("neighbors"))
+ if err != nil {
+ return err
+ }
+
+ for idx, n := range b.Neighbors {
+ vv := viper.New()
+ if len(list) > idx {
+ vv.Set("neighbor", list[idx])
+ }
+
+ pg, err := b.getPeerGroup(n.Config.PeerGroup)
+ if err != nil {
+ return nil
+ }
+
+ if pg != nil {
+ identifier := vv.Get("neighbor.config.neighbor-address")
+ if identifier == nil {
+ identifier = vv.Get("neighbor.config.neighbor-interface")
+ }
+ RegisterConfiguredFields(identifier.(string), list[idx])
+ }
+
+ if err := setDefaultNeighborConfigValuesWithViper(vv, &n, &b.Global, pg); err != nil {
+ return err
+ }
+ b.Neighbors[idx] = n
+ }
+
+ for _, d := range b.DynamicNeighbors {
+ if err := d.validate(b); err != nil {
+ return err
+ }
+ }
+
+ for idx, r := range b.RpkiServers {
+ if r.Config.Port == 0 {
+ b.RpkiServers[idx].Config.Port = rtr.RPKI_DEFAULT_PORT
+ }
+ }
+
+ list, err = extractArray(v.Get("policy-definitions"))
+ if err != nil {
+ return err
+ }
+
+ for idx, p := range b.PolicyDefinitions {
+ vv := viper.New()
+ if len(list) > idx {
+ vv.Set("policy", list[idx])
+ }
+ if err := setDefaultPolicyConfigValuesWithViper(vv, &p); err != nil {
+ return err
+ }
+ b.PolicyDefinitions[idx] = p
+ }
+
+ return nil
+}
+
+func OverwriteNeighborConfigWithPeerGroup(c *Neighbor, pg *PeerGroup) error {
+ v := viper.New()
+
+ val, ok := configuredFields[c.Config.NeighborAddress]
+ if ok {
+ v.Set("neighbor", val)
+ } else {
+ v.Set("neighbor.config.peer-group", c.Config.PeerGroup)
+ }
+
+ overwriteConfig(&c.Config, &pg.Config, "neighbor.config", v)
+ overwriteConfig(&c.Timers.Config, &pg.Timers.Config, "neighbor.timers.config", v)
+ overwriteConfig(&c.Transport.Config, &pg.Transport.Config, "neighbor.transport.config", v)
+ overwriteConfig(&c.ErrorHandling.Config, &pg.ErrorHandling.Config, "neighbor.error-handling.config", v)
+ overwriteConfig(&c.LoggingOptions.Config, &pg.LoggingOptions.Config, "neighbor.logging-options.config", v)
+ overwriteConfig(&c.EbgpMultihop.Config, &pg.EbgpMultihop.Config, "neighbor.ebgp-multihop.config", v)
+ overwriteConfig(&c.RouteReflector.Config, &pg.RouteReflector.Config, "neighbor.route-reflector.config", v)
+ overwriteConfig(&c.AsPathOptions.Config, &pg.AsPathOptions.Config, "neighbor.as-path-options.config", v)
+ overwriteConfig(&c.AddPaths.Config, &pg.AddPaths.Config, "neighbor.add-paths.config", v)
+ overwriteConfig(&c.GracefulRestart.Config, &pg.GracefulRestart.Config, "neighbor.gradeful-restart.config", v)
+ overwriteConfig(&c.ApplyPolicy.Config, &pg.ApplyPolicy.Config, "neighbor.apply-policy.config", v)
+ overwriteConfig(&c.UseMultiplePaths.Config, &pg.UseMultiplePaths.Config, "neighbor.use-multiple-paths.config", v)
+ overwriteConfig(&c.RouteServer.Config, &pg.RouteServer.Config, "neighbor.route-server.config", v)
+ overwriteConfig(&c.TtlSecurity.Config, &pg.TtlSecurity.Config, "neighbor.ttl-security.config", v)
+
+ if !v.IsSet("neighbor.afi-safis") {
+ c.AfiSafis = append(c.AfiSafis, pg.AfiSafis...)
+ }
+
+ return nil
+}
+
+func overwriteConfig(c, pg interface{}, tagPrefix string, v *viper.Viper) {
+ nValue := reflect.Indirect(reflect.ValueOf(c))
+ nType := reflect.Indirect(nValue).Type()
+ pgValue := reflect.Indirect(reflect.ValueOf(pg))
+ pgType := reflect.Indirect(pgValue).Type()
+
+ for i := 0; i < pgType.NumField(); i++ {
+ field := pgType.Field(i).Name
+ tag := tagPrefix + "." + nType.Field(i).Tag.Get("mapstructure")
+ if func() bool {
+ for _, t := range forcedOverwrittenConfig {
+ if t == tag {
+ return true
+ }
+ }
+ return false
+ }() || !v.IsSet(tag) {
+ nValue.FieldByName(field).Set(pgValue.FieldByName(field))
+ }
+ }
+}
diff --git a/internal/pkg/config/default_linux.go b/internal/pkg/config/default_linux.go
new file mode 100644
index 00000000..8cfcc501
--- /dev/null
+++ b/internal/pkg/config/default_linux.go
@@ -0,0 +1,72 @@
+// Copyright (C) 2016 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// +build linux
+
+package config
+
+import (
+ "fmt"
+ "net"
+
+ "github.com/vishvananda/netlink"
+)
+
+func GetIPv6LinkLocalNeighborAddress(ifname string) (string, error) {
+ ifi, err := net.InterfaceByName(ifname)
+ if err != nil {
+ return "", err
+ }
+ neighs, err := netlink.NeighList(ifi.Index, netlink.FAMILY_V6)
+ if err != nil {
+ return "", err
+ }
+ cnt := 0
+ var addr net.IP
+ for _, neigh := range neighs {
+ local, err := isLocalLinkLocalAddress(ifi.Index, neigh.IP)
+ if err != nil {
+ return "", err
+ }
+ if neigh.State&netlink.NUD_FAILED == 0 && neigh.IP.IsLinkLocalUnicast() && !local {
+ addr = neigh.IP
+ cnt++
+ }
+ }
+
+ if cnt == 0 {
+ return "", fmt.Errorf("no ipv6 link-local neighbor found")
+ } else if cnt > 1 {
+ return "", fmt.Errorf("found %d link-local neighbors. only support p2p link", cnt)
+ }
+
+ return fmt.Sprintf("%s%%%s", addr, ifname), nil
+}
+
+func isLocalLinkLocalAddress(ifindex int, addr net.IP) (bool, error) {
+ ifi, err := net.InterfaceByIndex(ifindex)
+ if err != nil {
+ return false, err
+ }
+ addrs, err := ifi.Addrs()
+ if err != nil {
+ return false, err
+ }
+ for _, a := range addrs {
+ if ip, _, _ := net.ParseCIDR(a.String()); addr.Equal(ip) {
+ return true, nil
+ }
+ }
+ return false, nil
+}
diff --git a/internal/pkg/config/default_nonlinux.go b/internal/pkg/config/default_nonlinux.go
new file mode 100644
index 00000000..fe4705a6
--- /dev/null
+++ b/internal/pkg/config/default_nonlinux.go
@@ -0,0 +1,25 @@
+// Copyright (C) 2016 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// +build !linux
+
+package config
+
+import (
+ "fmt"
+)
+
+func GetIPv6LinkLocalNeighborAddress(ifname string) (string, error) {
+ return "", fmt.Errorf("unnumbered peering is not supported")
+}
diff --git a/internal/pkg/config/serve.go b/internal/pkg/config/serve.go
new file mode 100644
index 00000000..6b09af6e
--- /dev/null
+++ b/internal/pkg/config/serve.go
@@ -0,0 +1,159 @@
+package config
+
+import (
+ "os"
+ "os/signal"
+ "syscall"
+
+ log "github.com/sirupsen/logrus"
+ "github.com/spf13/viper"
+)
+
+type BgpConfigSet struct {
+ Global Global `mapstructure:"global"`
+ Neighbors []Neighbor `mapstructure:"neighbors"`
+ PeerGroups []PeerGroup `mapstructure:"peer-groups"`
+ RpkiServers []RpkiServer `mapstructure:"rpki-servers"`
+ BmpServers []BmpServer `mapstructure:"bmp-servers"`
+ Vrfs []Vrf `mapstructure:"vrfs"`
+ MrtDump []Mrt `mapstructure:"mrt-dump"`
+ Zebra Zebra `mapstructure:"zebra"`
+ Collector Collector `mapstructure:"collector"`
+ DefinedSets DefinedSets `mapstructure:"defined-sets"`
+ PolicyDefinitions []PolicyDefinition `mapstructure:"policy-definitions"`
+ DynamicNeighbors []DynamicNeighbor `mapstructure:"dynamic-neighbors"`
+}
+
+func ReadConfigfileServe(path, format string, configCh chan *BgpConfigSet) {
+ sigCh := make(chan os.Signal, 1)
+ signal.Notify(sigCh, syscall.SIGHUP)
+
+ // Update config file type, if detectable
+ format = detectConfigFileType(path, format)
+
+ cnt := 0
+ for {
+ c := &BgpConfigSet{}
+ v := viper.New()
+ v.SetConfigFile(path)
+ v.SetConfigType(format)
+ var err error
+ if err = v.ReadInConfig(); err != nil {
+ goto ERROR
+ }
+ if err = v.UnmarshalExact(c); err != nil {
+ goto ERROR
+ }
+ if err = setDefaultConfigValuesWithViper(v, c); err != nil {
+ goto ERROR
+ }
+ if cnt == 0 {
+ log.WithFields(log.Fields{
+ "Topic": "Config",
+ }).Info("Finished reading the config file")
+ }
+ cnt++
+ configCh <- c
+ goto NEXT
+ ERROR:
+ if cnt == 0 {
+ log.WithFields(log.Fields{
+ "Topic": "Config",
+ "Error": err,
+ }).Fatalf("Can't read config file %s", path)
+ } else {
+ log.WithFields(log.Fields{
+ "Topic": "Config",
+ "Error": err,
+ }).Warningf("Can't read config file %s", path)
+ }
+ NEXT:
+ <-sigCh
+ log.WithFields(log.Fields{
+ "Topic": "Config",
+ }).Info("Reload the config file")
+ }
+}
+
+func ConfigSetToRoutingPolicy(c *BgpConfigSet) *RoutingPolicy {
+ return &RoutingPolicy{
+ DefinedSets: c.DefinedSets,
+ PolicyDefinitions: c.PolicyDefinitions,
+ }
+}
+
+func UpdatePeerGroupConfig(curC, newC *BgpConfigSet) ([]PeerGroup, []PeerGroup, []PeerGroup) {
+ addedPg := []PeerGroup{}
+ deletedPg := []PeerGroup{}
+ updatedPg := []PeerGroup{}
+
+ for _, n := range newC.PeerGroups {
+ if idx := existPeerGroup(n.Config.PeerGroupName, curC.PeerGroups); idx < 0 {
+ addedPg = append(addedPg, n)
+ } else if !n.Equal(&curC.PeerGroups[idx]) {
+ log.WithFields(log.Fields{
+ "Topic": "Config",
+ }).Debugf("Current peer-group config:%s", curC.PeerGroups[idx])
+ log.WithFields(log.Fields{
+ "Topic": "Config",
+ }).Debugf("New peer-group config:%s", n)
+ updatedPg = append(updatedPg, n)
+ }
+ }
+
+ for _, n := range curC.PeerGroups {
+ if existPeerGroup(n.Config.PeerGroupName, newC.PeerGroups) < 0 {
+ deletedPg = append(deletedPg, n)
+ }
+ }
+ return addedPg, deletedPg, updatedPg
+}
+
+func UpdateNeighborConfig(curC, newC *BgpConfigSet) ([]Neighbor, []Neighbor, []Neighbor) {
+ added := []Neighbor{}
+ deleted := []Neighbor{}
+ updated := []Neighbor{}
+
+ for _, n := range newC.Neighbors {
+ if idx := inSlice(n, curC.Neighbors); idx < 0 {
+ added = append(added, n)
+ } else if !n.Equal(&curC.Neighbors[idx]) {
+ log.WithFields(log.Fields{
+ "Topic": "Config",
+ }).Debugf("Current neighbor config:%s", curC.Neighbors[idx])
+ log.WithFields(log.Fields{
+ "Topic": "Config",
+ }).Debugf("New neighbor config:%s", n)
+ updated = append(updated, n)
+ }
+ }
+
+ for _, n := range curC.Neighbors {
+ if inSlice(n, newC.Neighbors) < 0 {
+ deleted = append(deleted, n)
+ }
+ }
+ return added, deleted, updated
+}
+
+func CheckPolicyDifference(currentPolicy *RoutingPolicy, newPolicy *RoutingPolicy) bool {
+
+ log.WithFields(log.Fields{
+ "Topic": "Config",
+ }).Debugf("Current policy:%s", currentPolicy)
+ log.WithFields(log.Fields{
+ "Topic": "Config",
+ }).Debugf("New policy:%s", newPolicy)
+
+ var result bool
+ if currentPolicy == nil && newPolicy == nil {
+ result = false
+ } else {
+ if currentPolicy != nil && newPolicy != nil {
+ result = !currentPolicy.Equal(newPolicy)
+ } else {
+ result = true
+ }
+ }
+ return result
+}
diff --git a/internal/pkg/config/util.go b/internal/pkg/config/util.go
new file mode 100644
index 00000000..591252b4
--- /dev/null
+++ b/internal/pkg/config/util.go
@@ -0,0 +1,264 @@
+// Copyright (C) 2015 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+ "fmt"
+ "net"
+ "path/filepath"
+ "regexp"
+ "strconv"
+
+ "github.com/osrg/gobgp/pkg/packet/bgp"
+)
+
+// Returns config file type by retrieving extension from the given path.
+// If no corresponding type found, returns the given def as the default value.
+func detectConfigFileType(path, def string) string {
+ switch ext := filepath.Ext(path); ext {
+ case ".toml":
+ return "toml"
+ case ".yaml", ".yml":
+ return "yaml"
+ case ".json":
+ return "json"
+ default:
+ return def
+ }
+}
+
+// yaml is decoded as []interface{}
+// but toml is decoded as []map[string]interface{}.
+// currently, viper can't hide this difference.
+// handle the difference here.
+func extractArray(intf interface{}) ([]interface{}, error) {
+ if intf != nil {
+ list, ok := intf.([]interface{})
+ if ok {
+ return list, nil
+ }
+ l, ok := intf.([]map[string]interface{})
+ if !ok {
+ return nil, fmt.Errorf("invalid configuration: neither []interface{} nor []map[string]interface{}")
+ }
+ list = make([]interface{}, 0, len(l))
+ for _, m := range l {
+ list = append(list, m)
+ }
+ return list, nil
+ }
+ return nil, nil
+}
+
+func getIPv6LinkLocalAddress(ifname string) (string, error) {
+ ifi, err := net.InterfaceByName(ifname)
+ if err != nil {
+ return "", err
+ }
+ addrs, err := ifi.Addrs()
+ if err != nil {
+ return "", err
+ }
+ for _, addr := range addrs {
+ ip := addr.(*net.IPNet).IP
+ if ip.To4() == nil && ip.IsLinkLocalUnicast() {
+ return fmt.Sprintf("%s%%%s", ip.String(), ifname), nil
+ }
+ }
+ return "", fmt.Errorf("no ipv6 link local address for %s", ifname)
+}
+
+func (b *BgpConfigSet) getPeerGroup(n string) (*PeerGroup, error) {
+ if n == "" {
+ return nil, nil
+ }
+ for _, pg := range b.PeerGroups {
+ if n == pg.Config.PeerGroupName {
+ return &pg, nil
+ }
+ }
+ return nil, fmt.Errorf("no such peer-group: %s", n)
+}
+
+func (d *DynamicNeighbor) validate(b *BgpConfigSet) error {
+ if d.Config.PeerGroup == "" {
+ return fmt.Errorf("dynamic neighbor requires the peer group config")
+ }
+
+ if _, err := b.getPeerGroup(d.Config.PeerGroup); err != nil {
+ return err
+ }
+ if _, _, err := net.ParseCIDR(d.Config.Prefix); err != nil {
+ return fmt.Errorf("invalid dynamic neighbor prefix %s", d.Config.Prefix)
+ }
+ return nil
+}
+
+func (n *Neighbor) IsConfederationMember(g *Global) bool {
+ for _, member := range g.Confederation.Config.MemberAsList {
+ if member == n.Config.PeerAs {
+ return true
+ }
+ }
+ return false
+}
+
+func (n *Neighbor) IsConfederation(g *Global) bool {
+ if n.Config.PeerAs == g.Config.As {
+ return true
+ }
+ return n.IsConfederationMember(g)
+}
+
+func (n *Neighbor) IsEBGPPeer(g *Global) bool {
+ return n.Config.PeerAs != g.Config.As
+}
+
+func (n *Neighbor) CreateRfMap() map[bgp.RouteFamily]bgp.BGPAddPathMode {
+ rfMap := make(map[bgp.RouteFamily]bgp.BGPAddPathMode)
+ for _, af := range n.AfiSafis {
+ mode := bgp.BGP_ADD_PATH_NONE
+ if af.AddPaths.State.Receive {
+ mode |= bgp.BGP_ADD_PATH_RECEIVE
+ }
+ if af.AddPaths.State.SendMax > 0 {
+ mode |= bgp.BGP_ADD_PATH_SEND
+ }
+ rfMap[af.State.Family] = mode
+ }
+ return rfMap
+}
+
+func (n *Neighbor) GetAfiSafi(family bgp.RouteFamily) *AfiSafi {
+ for _, a := range n.AfiSafis {
+ if string(a.Config.AfiSafiName) == family.String() {
+ return &a
+ }
+ }
+ return nil
+}
+
+func (n *Neighbor) ExtractNeighborAddress() (string, error) {
+ addr := n.State.NeighborAddress
+ if addr == "" {
+ addr = n.Config.NeighborAddress
+ if addr == "" {
+ return "", fmt.Errorf("NeighborAddress is not configured")
+ }
+ }
+ return addr, nil
+}
+
+func (n *Neighbor) IsAddPathReceiveEnabled(family bgp.RouteFamily) bool {
+ for _, af := range n.AfiSafis {
+ if af.State.Family == family {
+ return af.AddPaths.State.Receive
+ }
+ }
+ return false
+}
+
+type AfiSafis []AfiSafi
+
+func (c AfiSafis) ToRfList() ([]bgp.RouteFamily, error) {
+ rfs := make([]bgp.RouteFamily, 0, len(c))
+ for _, af := range c {
+ rfs = append(rfs, af.State.Family)
+ }
+ return rfs, nil
+}
+
+func inSlice(n Neighbor, b []Neighbor) int {
+ for i, nb := range b {
+ if nb.State.NeighborAddress == n.State.NeighborAddress {
+ return i
+ }
+ }
+ return -1
+}
+
+func existPeerGroup(n string, b []PeerGroup) int {
+ for i, nb := range b {
+ if nb.Config.PeerGroupName == n {
+ return i
+ }
+ }
+ return -1
+}
+
+func isAfiSafiChanged(x, y []AfiSafi) bool {
+ if len(x) != len(y) {
+ return true
+ }
+ m := make(map[string]bool)
+ for _, e := range x {
+ m[string(e.Config.AfiSafiName)] = true
+ }
+ for _, e := range y {
+ if !m[string(e.Config.AfiSafiName)] {
+ return true
+ }
+ }
+ return false
+}
+
+func (n *Neighbor) NeedsResendOpenMessage(new *Neighbor) bool {
+ return !n.Config.Equal(&new.Config) ||
+ !n.Transport.Config.Equal(&new.Transport.Config) ||
+ !n.AddPaths.Config.Equal(&new.AddPaths.Config) ||
+ !n.GracefulRestart.Config.Equal(&new.GracefulRestart.Config) ||
+ isAfiSafiChanged(n.AfiSafis, new.AfiSafis)
+}
+
+// TODO: these regexp are duplicated in api
+var _regexpPrefixMaskLengthRange = regexp.MustCompile(`(\d+)\.\.(\d+)`)
+
+func ParseMaskLength(prefix, mask string) (int, int, error) {
+ _, ipNet, err := net.ParseCIDR(prefix)
+ if err != nil {
+ return 0, 0, fmt.Errorf("invalid prefix: %s", prefix)
+ }
+ if mask == "" {
+ l, _ := ipNet.Mask.Size()
+ return l, l, nil
+ }
+ elems := _regexpPrefixMaskLengthRange.FindStringSubmatch(mask)
+ if len(elems) != 3 {
+ return 0, 0, fmt.Errorf("invalid mask length range: %s", mask)
+ }
+ // we've already checked the range is sane by regexp
+ min, _ := strconv.ParseUint(elems[1], 10, 8)
+ max, _ := strconv.ParseUint(elems[2], 10, 8)
+ if min > max {
+ return 0, 0, fmt.Errorf("invalid mask length range: %s", mask)
+ }
+ if ipv4 := ipNet.IP.To4(); ipv4 != nil {
+ f := func(i uint64) bool {
+ return i <= 32
+ }
+ if !f(min) || !f(max) {
+ return 0, 0, fmt.Errorf("ipv4 mask length range outside scope :%s", mask)
+ }
+ } else {
+ f := func(i uint64) bool {
+ return i <= 128
+ }
+ if !f(min) || !f(max) {
+ return 0, 0, fmt.Errorf("ipv6 mask length range outside scope :%s", mask)
+ }
+ }
+ return int(min), int(max), nil
+}
diff --git a/internal/pkg/config/util_test.go b/internal/pkg/config/util_test.go
new file mode 100644
index 00000000..a34b02f4
--- /dev/null
+++ b/internal/pkg/config/util_test.go
@@ -0,0 +1,32 @@
+// Copyright (C) 2017 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestDetectConfigFileType(t *testing.T) {
+ assert := assert.New(t)
+
+ assert.Equal("toml", detectConfigFileType("bgpd.conf", "toml"))
+ assert.Equal("toml", detectConfigFileType("bgpd.toml", "xxx"))
+ assert.Equal("yaml", detectConfigFileType("bgpd.yaml", "xxx"))
+ assert.Equal("yaml", detectConfigFileType("bgpd.yml", "xxx"))
+ assert.Equal("json", detectConfigFileType("bgpd.json", "xxx"))
+}
diff --git a/internal/pkg/table/adj.go b/internal/pkg/table/adj.go
new file mode 100644
index 00000000..95fbf6af
--- /dev/null
+++ b/internal/pkg/table/adj.go
@@ -0,0 +1,186 @@
+// Copyright (C) 2015 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package table
+
+import (
+ "fmt"
+
+ "github.com/osrg/gobgp/pkg/packet/bgp"
+)
+
+type AdjRib struct {
+ accepted map[bgp.RouteFamily]int
+ table map[bgp.RouteFamily]map[string]*Path
+}
+
+func NewAdjRib(rfList []bgp.RouteFamily) *AdjRib {
+ table := make(map[bgp.RouteFamily]map[string]*Path)
+ for _, rf := range rfList {
+ table[rf] = make(map[string]*Path)
+ }
+ return &AdjRib{
+ table: table,
+ accepted: make(map[bgp.RouteFamily]int),
+ }
+}
+
+func (adj *AdjRib) Update(pathList []*Path) {
+ for _, path := range pathList {
+ if path == nil || path.IsEOR() {
+ continue
+ }
+ rf := path.GetRouteFamily()
+ key := fmt.Sprintf("%d:%s", path.GetNlri().PathIdentifier(), path.getPrefix())
+
+ old, found := adj.table[rf][key]
+ if path.IsWithdraw {
+ if found {
+ delete(adj.table[rf], key)
+ if !old.IsAsLooped() {
+ adj.accepted[rf]--
+ }
+ }
+ } else {
+ if found {
+ if old.IsAsLooped() && !path.IsAsLooped() {
+ adj.accepted[rf]++
+ } else if !old.IsAsLooped() && path.IsAsLooped() {
+ adj.accepted[rf]--
+ }
+ } else {
+ if !path.IsAsLooped() {
+ adj.accepted[rf]++
+ }
+ }
+ if found && old.Equal(path) {
+ path.setTimestamp(old.GetTimestamp())
+ }
+ adj.table[rf][key] = path
+ }
+ }
+}
+
+func (adj *AdjRib) PathList(rfList []bgp.RouteFamily, accepted bool) []*Path {
+ pathList := make([]*Path, 0, adj.Count(rfList))
+ for _, rf := range rfList {
+ for _, rr := range adj.table[rf] {
+ if accepted && rr.IsAsLooped() {
+ continue
+ }
+ pathList = append(pathList, rr)
+ }
+ }
+ return pathList
+}
+
+func (adj *AdjRib) Count(rfList []bgp.RouteFamily) int {
+ count := 0
+ for _, rf := range rfList {
+ if table, ok := adj.table[rf]; ok {
+ count += len(table)
+ }
+ }
+ return count
+}
+
+func (adj *AdjRib) Accepted(rfList []bgp.RouteFamily) int {
+ count := 0
+ for _, rf := range rfList {
+ if n, ok := adj.accepted[rf]; ok {
+ count += n
+ }
+ }
+ return count
+}
+
+func (adj *AdjRib) Drop(rfList []bgp.RouteFamily) {
+ for _, rf := range rfList {
+ if _, ok := adj.table[rf]; ok {
+ adj.table[rf] = make(map[string]*Path)
+ adj.accepted[rf] = 0
+ }
+ }
+}
+
+func (adj *AdjRib) DropStale(rfList []bgp.RouteFamily) []*Path {
+ pathList := make([]*Path, 0, adj.Count(rfList))
+ for _, rf := range rfList {
+ if table, ok := adj.table[rf]; ok {
+ for k, p := range table {
+ if p.IsStale() {
+ delete(table, k)
+ if !p.IsAsLooped() {
+ adj.accepted[rf]--
+ }
+ pathList = append(pathList, p.Clone(true))
+ }
+ }
+ }
+ }
+ return pathList
+}
+
+func (adj *AdjRib) StaleAll(rfList []bgp.RouteFamily) []*Path {
+ pathList := make([]*Path, 0)
+ for _, rf := range rfList {
+ if table, ok := adj.table[rf]; ok {
+ l := make([]*Path, 0, len(table))
+ for k, p := range table {
+ n := p.Clone(false)
+ n.MarkStale(true)
+ table[k] = n
+ l = append(l, n)
+ }
+ if len(l) > 0 {
+ pathList = append(pathList, l...)
+ }
+ }
+ }
+ return pathList
+}
+
+func (adj *AdjRib) Select(family bgp.RouteFamily, accepted bool, option ...TableSelectOption) (*Table, error) {
+ m := make(map[string][]*Path)
+ pl := adj.PathList([]bgp.RouteFamily{family}, accepted)
+ for _, path := range pl {
+ key := path.GetNlri().String()
+ if _, y := m[key]; y {
+ m[key] = append(m[key], path)
+ } else {
+ m[key] = []*Path{path}
+ }
+ }
+ d := make([]*Destination, 0, len(pl))
+ for _, l := range m {
+ d = append(d, NewDestination(l[0].GetNlri(), 0, l...))
+ }
+ tbl := NewTable(family, d...)
+ option = append(option, TableSelectOption{adj: true})
+ return tbl.Select(option...)
+}
+
+func (adj *AdjRib) TableInfo(family bgp.RouteFamily) (*TableInfo, error) {
+ if _, ok := adj.table[family]; !ok {
+ return nil, fmt.Errorf("%s unsupported", family)
+ }
+ c := adj.Count([]bgp.RouteFamily{family})
+ a := adj.Accepted([]bgp.RouteFamily{family})
+ return &TableInfo{
+ NumDestination: c,
+ NumPath: c,
+ NumAccepted: a,
+ }, nil
+}
diff --git a/internal/pkg/table/adj_test.go b/internal/pkg/table/adj_test.go
new file mode 100644
index 00000000..ac4fc5a0
--- /dev/null
+++ b/internal/pkg/table/adj_test.go
@@ -0,0 +1,52 @@
+// Copyright (C) 2018 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package table
+
+import (
+ "testing"
+ "time"
+
+ "github.com/osrg/gobgp/pkg/packet/bgp"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestStaleAll(t *testing.T) {
+ pi := &PeerInfo{}
+ attrs := []bgp.PathAttributeInterface{bgp.NewPathAttributeOrigin(0)}
+
+ nlri1 := bgp.NewIPAddrPrefix(24, "20.20.20.0")
+ nlri1.SetPathIdentifier(1)
+ p1 := NewPath(pi, nlri1, false, attrs, time.Now(), false)
+ nlri2 := bgp.NewIPAddrPrefix(24, "20.20.20.0")
+ nlri2.SetPathIdentifier(2)
+ p2 := NewPath(pi, nlri2, false, attrs, time.Now(), false)
+ family := p1.GetRouteFamily()
+ families := []bgp.RouteFamily{family}
+
+ adj := NewAdjRib(families)
+ adj.Update([]*Path{p1, p2})
+ assert.Equal(t, len(adj.table[family]), 2)
+
+ adj.StaleAll(families)
+
+ for _, p := range adj.table[family] {
+ assert.True(t, p.IsStale())
+ }
+
+ adj.DropStale(families)
+ assert.Equal(t, len(adj.table[family]), 0)
+}
diff --git a/internal/pkg/table/destination.go b/internal/pkg/table/destination.go
new file mode 100644
index 00000000..fa61572e
--- /dev/null
+++ b/internal/pkg/table/destination.go
@@ -0,0 +1,1041 @@
+// Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package table
+
+import (
+ "bytes"
+ "encoding/binary"
+ "encoding/json"
+ "fmt"
+ "net"
+ "sort"
+
+ "github.com/osrg/gobgp/internal/pkg/config"
+ "github.com/osrg/gobgp/pkg/packet/bgp"
+
+ log "github.com/sirupsen/logrus"
+)
+
+var SelectionOptions config.RouteSelectionOptionsConfig
+var UseMultiplePaths config.UseMultiplePathsConfig
+
+type BestPathReason uint8
+
+const (
+ BPR_UNKNOWN BestPathReason = iota
+ BPR_DISABLED
+ BPR_ONLY_PATH
+ BPR_REACHABLE_NEXT_HOP
+ BPR_HIGHEST_WEIGHT
+ BPR_LOCAL_PREF
+ BPR_LOCAL_ORIGIN
+ BPR_ASPATH
+ BPR_ORIGIN
+ BPR_MED
+ BPR_ASN
+ BPR_IGP_COST
+ BPR_ROUTER_ID
+ BPR_OLDER
+ BPR_NON_LLGR_STALE
+)
+
+var BestPathReasonStringMap = map[BestPathReason]string{
+ BPR_UNKNOWN: "Unknown",
+ BPR_DISABLED: "Bestpath selection disabled",
+ BPR_ONLY_PATH: "Only Path",
+ BPR_REACHABLE_NEXT_HOP: "Reachable Next Hop",
+ BPR_HIGHEST_WEIGHT: "Highest Weight",
+ BPR_LOCAL_PREF: "Local Pref",
+ BPR_LOCAL_ORIGIN: "Local Origin",
+ BPR_ASPATH: "AS Path",
+ BPR_ORIGIN: "Origin",
+ BPR_MED: "MED",
+ BPR_ASN: "ASN",
+ BPR_IGP_COST: "IGP Cost",
+ BPR_ROUTER_ID: "Router ID",
+ BPR_OLDER: "Older",
+ BPR_NON_LLGR_STALE: "no LLGR Stale",
+}
+
+func (r *BestPathReason) String() string {
+ return BestPathReasonStringMap[*r]
+}
+
+func IpToRadixkey(b []byte, max uint8) string {
+ var buffer bytes.Buffer
+ for i := 0; i < len(b) && i < int(max); i++ {
+ fmt.Fprintf(&buffer, "%08b", b[i])
+ }
+ return buffer.String()[:max]
+}
+
+func CidrToRadixkey(cidr string) string {
+ _, n, _ := net.ParseCIDR(cidr)
+ ones, _ := n.Mask.Size()
+ return IpToRadixkey(n.IP, uint8(ones))
+}
+
+func AddrToRadixkey(addr bgp.AddrPrefixInterface) string {
+ var (
+ ip net.IP
+ size uint8
+ )
+ switch T := addr.(type) {
+ case *bgp.IPAddrPrefix:
+ mask := net.CIDRMask(int(T.Length), net.IPv4len*8)
+ ip, size = T.Prefix.Mask(mask).To4(), uint8(T.Length)
+ case *bgp.IPv6AddrPrefix:
+ mask := net.CIDRMask(int(T.Length), net.IPv6len*8)
+ ip, size = T.Prefix.Mask(mask).To16(), uint8(T.Length)
+ default:
+ return CidrToRadixkey(addr.String())
+ }
+ return IpToRadixkey(ip, size)
+}
+
+type PeerInfo struct {
+ AS uint32
+ ID net.IP
+ LocalAS uint32
+ LocalID net.IP
+ Address net.IP
+ LocalAddress net.IP
+ RouteReflectorClient bool
+ RouteReflectorClusterID net.IP
+ MultihopTtl uint8
+ Confederation bool
+}
+
+func (lhs *PeerInfo) Equal(rhs *PeerInfo) bool {
+ if lhs == rhs {
+ return true
+ }
+
+ if rhs == nil {
+ return false
+ }
+
+ if (lhs.AS == rhs.AS) && lhs.ID.Equal(rhs.ID) && lhs.LocalID.Equal(rhs.LocalID) && lhs.Address.Equal(rhs.Address) {
+ return true
+ }
+ return false
+}
+
+func (i *PeerInfo) String() string {
+ if i.Address == nil {
+ return "local"
+ }
+ s := bytes.NewBuffer(make([]byte, 0, 64))
+ s.WriteString(fmt.Sprintf("{ %s | ", i.Address))
+ s.WriteString(fmt.Sprintf("as: %d", i.AS))
+ s.WriteString(fmt.Sprintf(", id: %s", i.ID))
+ if i.RouteReflectorClient {
+ s.WriteString(fmt.Sprintf(", cluster-id: %s", i.RouteReflectorClusterID))
+ }
+ s.WriteString(" }")
+ return s.String()
+}
+
+func NewPeerInfo(g *config.Global, p *config.Neighbor) *PeerInfo {
+ clusterID := net.ParseIP(string(p.RouteReflector.State.RouteReflectorClusterId)).To4()
+ // exclude zone info
+ naddr, _ := net.ResolveIPAddr("ip", p.State.NeighborAddress)
+ return &PeerInfo{
+ AS: p.Config.PeerAs,
+ LocalAS: g.Config.As,
+ LocalID: net.ParseIP(g.Config.RouterId).To4(),
+ RouteReflectorClient: p.RouteReflector.Config.RouteReflectorClient,
+ Address: naddr.IP,
+ RouteReflectorClusterID: clusterID,
+ MultihopTtl: p.EbgpMultihop.Config.MultihopTtl,
+ Confederation: p.IsConfederationMember(g),
+ }
+}
+
+type Destination struct {
+ routeFamily bgp.RouteFamily
+ nlri bgp.AddrPrefixInterface
+ knownPathList []*Path
+ localIdMap *Bitmap
+}
+
+func NewDestination(nlri bgp.AddrPrefixInterface, mapSize int, known ...*Path) *Destination {
+ d := &Destination{
+ routeFamily: bgp.AfiSafiToRouteFamily(nlri.AFI(), nlri.SAFI()),
+ nlri: nlri,
+ knownPathList: known,
+ localIdMap: NewBitmap(mapSize),
+ }
+ // the id zero means id is not allocated yet.
+ if mapSize != 0 {
+ d.localIdMap.Flag(0)
+ }
+ return d
+}
+
+func (dd *Destination) Family() bgp.RouteFamily {
+ return dd.routeFamily
+}
+
+func (dd *Destination) setRouteFamily(routeFamily bgp.RouteFamily) {
+ dd.routeFamily = routeFamily
+}
+
+func (dd *Destination) GetNlri() bgp.AddrPrefixInterface {
+ return dd.nlri
+}
+
+func (dd *Destination) setNlri(nlri bgp.AddrPrefixInterface) {
+ dd.nlri = nlri
+}
+
+func (dd *Destination) GetAllKnownPathList() []*Path {
+ return dd.knownPathList
+}
+
+func rsFilter(id string, as uint32, path *Path) bool {
+ isASLoop := func(as uint32, path *Path) bool {
+ for _, v := range path.GetAsList() {
+ if as == v {
+ return true
+ }
+ }
+ return false
+ }
+
+ if id != GLOBAL_RIB_NAME && (path.GetSource().Address.String() == id || isASLoop(as, path)) {
+ return true
+ }
+ return false
+}
+
+func (dd *Destination) GetKnownPathList(id string, as uint32) []*Path {
+ list := make([]*Path, 0, len(dd.knownPathList))
+ for _, p := range dd.knownPathList {
+ if rsFilter(id, as, p) {
+ continue
+ }
+ list = append(list, p)
+ }
+ return list
+}
+
+func getBestPath(id string, as uint32, pathList []*Path) *Path {
+ for _, p := range pathList {
+ if rsFilter(id, as, p) {
+ continue
+ }
+ return p
+ }
+ return nil
+}
+
+func (dd *Destination) GetBestPath(id string, as uint32) *Path {
+ p := getBestPath(id, as, dd.knownPathList)
+ if p == nil || p.IsNexthopInvalid {
+ return nil
+ }
+ return p
+}
+
+func (dd *Destination) GetMultiBestPath(id string) []*Path {
+ return getMultiBestPath(id, dd.knownPathList)
+}
+
+// Calculates best-path among known paths for this destination.
+//
+// Modifies destination's state related to stored paths. Removes withdrawn
+// paths from known paths. Also, adds new paths to known paths.
+func (dest *Destination) Calculate(newPath *Path) *Update {
+ oldKnownPathList := make([]*Path, len(dest.knownPathList))
+ copy(oldKnownPathList, dest.knownPathList)
+
+ if newPath.IsWithdraw {
+ p := dest.explicitWithdraw(newPath)
+ if p != nil {
+ if id := p.GetNlri().PathLocalIdentifier(); id != 0 {
+ dest.localIdMap.Unflag(uint(id))
+ }
+ }
+ } else {
+ dest.implicitWithdraw(newPath)
+ dest.knownPathList = append(dest.knownPathList, newPath)
+ }
+
+ for _, path := range dest.knownPathList {
+ if path.GetNlri().PathLocalIdentifier() == 0 {
+ id, err := dest.localIdMap.FindandSetZeroBit()
+ if err != nil {
+ dest.localIdMap.Expand()
+ id, _ = dest.localIdMap.FindandSetZeroBit()
+ }
+ path.GetNlri().SetPathLocalIdentifier(uint32(id))
+ }
+ }
+ // Compute new best path
+ dest.computeKnownBestPath()
+
+ l := make([]*Path, len(dest.knownPathList))
+ copy(l, dest.knownPathList)
+ return &Update{
+ KnownPathList: l,
+ OldKnownPathList: oldKnownPathList,
+ }
+}
+
+// Removes withdrawn paths.
+//
+// Note:
+// We may have disproportionate number of withdraws compared to know paths
+// since not all paths get installed into the table due to bgp policy and
+// we can receive withdraws for such paths and withdrawals may not be
+// stopped by the same policies.
+//
+func (dest *Destination) explicitWithdraw(withdraw *Path) *Path {
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ "Key": dest.GetNlri().String(),
+ }).Debug("Removing withdrawals")
+
+ // If we have some withdrawals and no know-paths, it means it is safe to
+ // delete these withdraws.
+ if len(dest.knownPathList) == 0 {
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ "Key": dest.GetNlri().String(),
+ }).Debug("Found withdrawals for path(s) that did not get installed")
+ return nil
+ }
+
+ // Match all withdrawals from destination paths.
+ isFound := -1
+ for i, path := range dest.knownPathList {
+ // We have a match if the source and path-id are same.
+ if path.GetSource().Equal(withdraw.GetSource()) && path.GetNlri().PathIdentifier() == withdraw.GetNlri().PathIdentifier() {
+ isFound = i
+ withdraw.GetNlri().SetPathLocalIdentifier(path.GetNlri().PathLocalIdentifier())
+ }
+ }
+
+ // We do no have any match for this withdraw.
+ if isFound == -1 {
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ "Key": dest.GetNlri().String(),
+ "Path": withdraw,
+ }).Warn("No matching path for withdraw found, may be path was not installed into table")
+ return nil
+ } else {
+ p := dest.knownPathList[isFound]
+ dest.knownPathList = append(dest.knownPathList[:isFound], dest.knownPathList[isFound+1:]...)
+ return p
+ }
+}
+
+// Identifies which of known paths are old and removes them.
+//
+// Known paths will no longer have paths whose new version is present in
+// new paths.
+func (dest *Destination) implicitWithdraw(newPath *Path) {
+ found := -1
+ for i, path := range dest.knownPathList {
+ if newPath.NoImplicitWithdraw() {
+ continue
+ }
+ // Here we just check if source is same and not check if path
+ // version num. as newPaths are implicit withdrawal of old
+ // paths and when doing RouteRefresh (not EnhancedRouteRefresh)
+ // we get same paths again.
+ if newPath.GetSource().Equal(path.GetSource()) && newPath.GetNlri().PathIdentifier() == path.GetNlri().PathIdentifier() {
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ "Key": dest.GetNlri().String(),
+ "Path": path,
+ }).Debug("Implicit withdrawal of old path, since we have learned new path from the same peer")
+
+ found = i
+ newPath.GetNlri().SetPathLocalIdentifier(path.GetNlri().PathLocalIdentifier())
+ break
+ }
+ }
+ if found != -1 {
+ dest.knownPathList = append(dest.knownPathList[:found], dest.knownPathList[found+1:]...)
+ }
+}
+
+func (dest *Destination) computeKnownBestPath() (*Path, BestPathReason, error) {
+ if SelectionOptions.DisableBestPathSelection {
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ }).Debug("computeKnownBestPath skipped")
+ return nil, BPR_DISABLED, nil
+ }
+
+ // If we do not have any paths to this destination, then we do not have
+ // new best path.
+ if len(dest.knownPathList) == 0 {
+ return nil, BPR_UNKNOWN, nil
+ }
+
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ }).Debugf("computeKnownBestPath knownPathList: %d", len(dest.knownPathList))
+
+ // We pick the first path as current best path. This helps in breaking
+ // tie between two new paths learned in one cycle for which best-path
+ // calculation steps lead to tie.
+ if len(dest.knownPathList) == 1 {
+ // If the first path has the invalidated next-hop, which evaluated by
+ // IGP, returns no path with the reason of the next-hop reachability.
+ if dest.knownPathList[0].IsNexthopInvalid {
+ return nil, BPR_REACHABLE_NEXT_HOP, nil
+ }
+ return dest.knownPathList[0], BPR_ONLY_PATH, nil
+ }
+ dest.sort()
+ newBest := dest.knownPathList[0]
+ // If the first path has the invalidated next-hop, which evaluated by IGP,
+ // returns no path with the reason of the next-hop reachability.
+ if dest.knownPathList[0].IsNexthopInvalid {
+ return nil, BPR_REACHABLE_NEXT_HOP, nil
+ }
+ return newBest, newBest.reason, nil
+}
+
+func (dst *Destination) sort() {
+ sort.SliceStable(dst.knownPathList, func(i, j int) bool {
+ //Compares given paths and returns best path.
+ //
+ //Parameters:
+ // -`path1`: first path to compare
+ // -`path2`: second path to compare
+ //
+ // Best path processing will involve following steps:
+ // 1. Select a path with a reachable next hop.
+ // 2. Select the path with the highest weight.
+ // 3. If path weights are the same, select the path with the highest
+ // local preference value.
+ // 4. Prefer locally originated routes (network routes, redistributed
+ // routes, or aggregated routes) over received routes.
+ // 5. Select the route with the shortest AS-path length.
+ // 6. If all paths have the same AS-path length, select the path based
+ // on origin: IGP is preferred over EGP; EGP is preferred over
+ // Incomplete.
+ // 7. If the origins are the same, select the path with lowest MED
+ // value.
+ // 8. If the paths have the same MED values, select the path learned
+ // via EBGP over one learned via IBGP.
+ // 9. Select the route with the lowest IGP cost to the next hop.
+ // 10. Select the route received from the peer with the lowest BGP
+ // router ID.
+ //
+ // Returns None if best-path among given paths cannot be computed else best
+ // path.
+ // Assumes paths from NC has source equal to None.
+ //
+
+ path1 := dst.knownPathList[i]
+ path2 := dst.knownPathList[j]
+
+ var better *Path
+ reason := BPR_UNKNOWN
+
+ // draft-uttaro-idr-bgp-persistence-02
+ if better == nil {
+ better = compareByLLGRStaleCommunity(path1, path2)
+ reason = BPR_NON_LLGR_STALE
+ }
+ // Follow best path calculation algorithm steps.
+ // compare by reachability
+ if better == nil {
+ better = compareByReachableNexthop(path1, path2)
+ reason = BPR_REACHABLE_NEXT_HOP
+ }
+ if better == nil {
+ better = compareByHighestWeight(path1, path2)
+ reason = BPR_HIGHEST_WEIGHT
+ }
+ if better == nil {
+ better = compareByLocalPref(path1, path2)
+ reason = BPR_LOCAL_PREF
+ }
+ if better == nil {
+ better = compareByLocalOrigin(path1, path2)
+ reason = BPR_LOCAL_ORIGIN
+ }
+ if better == nil {
+ better = compareByASPath(path1, path2)
+ reason = BPR_ASPATH
+ }
+ if better == nil {
+ better = compareByOrigin(path1, path2)
+ reason = BPR_ORIGIN
+ }
+ if better == nil {
+ better = compareByMED(path1, path2)
+ reason = BPR_MED
+ }
+ if better == nil {
+ better = compareByASNumber(path1, path2)
+ reason = BPR_ASN
+ }
+ if better == nil {
+ better = compareByIGPCost(path1, path2)
+ reason = BPR_IGP_COST
+ }
+ if better == nil {
+ better = compareByAge(path1, path2)
+ reason = BPR_OLDER
+ }
+ if better == nil {
+ var e error = nil
+ better, e = compareByRouterID(path1, path2)
+ if e != nil {
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ "Error": e,
+ }).Error("Could not get best path by comparing router ID")
+ }
+ reason = BPR_ROUTER_ID
+ }
+ if better == nil {
+ reason = BPR_UNKNOWN
+ better = path1
+ }
+
+ better.reason = reason
+
+ return better == path1
+ })
+}
+
+type Update struct {
+ KnownPathList []*Path
+ OldKnownPathList []*Path
+}
+
+func getMultiBestPath(id string, pathList []*Path) []*Path {
+ list := make([]*Path, 0, len(pathList))
+ var best *Path
+ for _, p := range pathList {
+ if !p.IsNexthopInvalid {
+ if best == nil {
+ best = p
+ list = append(list, p)
+ } else if best.Compare(p) == 0 {
+ list = append(list, p)
+ }
+ }
+ }
+ return list
+}
+
+func (u *Update) GetWithdrawnPath() []*Path {
+ if len(u.KnownPathList) == len(u.OldKnownPathList) {
+ return nil
+ }
+
+ l := make([]*Path, 0, len(u.OldKnownPathList))
+
+ for _, p := range u.OldKnownPathList {
+ y := func() bool {
+ for _, old := range u.KnownPathList {
+ if p == old {
+ return true
+ }
+ }
+ return false
+ }()
+ if !y {
+ l = append(l, p.Clone(true))
+ }
+ }
+ return l
+}
+
+func (u *Update) GetChanges(id string, as uint32, peerDown bool) (*Path, *Path, []*Path) {
+ best, old := func(id string) (*Path, *Path) {
+ old := getBestPath(id, as, u.OldKnownPathList)
+ best := getBestPath(id, as, u.KnownPathList)
+ if best != nil && best.Equal(old) {
+ // RFC4684 3.2. Intra-AS VPN Route Distribution
+ // When processing RT membership NLRIs received from internal iBGP
+ // peers, it is necessary to consider all available iBGP paths for a
+ // given RT prefix, for building the outbound route filter, and not just
+ // the best path.
+ if best.GetRouteFamily() == bgp.RF_RTC_UC {
+ return best, old
+ }
+ // For BGP Nexthop Tracking, checks if the nexthop reachability
+ // was changed or not.
+ if best.IsNexthopInvalid != old.IsNexthopInvalid {
+ // If the nexthop of the best path became unreachable, we need
+ // to withdraw that path.
+ if best.IsNexthopInvalid {
+ return best.Clone(true), old
+ }
+ return best, old
+ }
+ return nil, old
+ }
+ if best == nil {
+ if old == nil {
+ return nil, nil
+ }
+ if peerDown {
+ // withdraws were generated by peer
+ // down so paths are not in knowpath
+ // or adjin.
+ old.IsWithdraw = true
+ return old, old
+ }
+ return old.Clone(true), old
+ }
+ return best, old
+ }(id)
+
+ var multi []*Path
+
+ if id == GLOBAL_RIB_NAME && UseMultiplePaths.Enabled {
+ diff := func(lhs, rhs []*Path) bool {
+ if len(lhs) != len(rhs) {
+ return true
+ }
+ for idx, l := range lhs {
+ if !l.Equal(rhs[idx]) {
+ return true
+ }
+ }
+ return false
+ }
+ oldM := getMultiBestPath(id, u.OldKnownPathList)
+ newM := getMultiBestPath(id, u.KnownPathList)
+ if diff(oldM, newM) {
+ multi = newM
+ if len(newM) == 0 {
+ multi = []*Path{best}
+ }
+ }
+ }
+ return best, old, multi
+}
+
+func compareByLLGRStaleCommunity(path1, path2 *Path) *Path {
+ p1 := path1.IsLLGRStale()
+ p2 := path2.IsLLGRStale()
+ if p1 == p2 {
+ return nil
+ } else if p1 {
+ return path2
+ }
+ return path1
+}
+
+func compareByReachableNexthop(path1, path2 *Path) *Path {
+ // Compares given paths and selects best path based on reachable next-hop.
+ //
+ // If no path matches this criteria, return nil.
+ // For BGP Nexthop Tracking, evaluates next-hop is validated by IGP.
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ }).Debugf("enter compareByReachableNexthop -- path1: %s, path2: %s", path1, path2)
+
+ if path1.IsNexthopInvalid && !path2.IsNexthopInvalid {
+ return path2
+ } else if !path1.IsNexthopInvalid && path2.IsNexthopInvalid {
+ return path1
+ }
+
+ return nil
+}
+
+func compareByHighestWeight(path1, path2 *Path) *Path {
+ // Selects a path with highest weight.
+ //
+ // Weight is BGPS specific parameter. It is local to the router on which it
+ // is configured.
+ // Return:
+ // nil if best path among given paths cannot be decided, else best path.
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ }).Debugf("enter compareByHighestWeight -- path1: %s, path2: %s", path1, path2)
+ return nil
+}
+
+func compareByLocalPref(path1, path2 *Path) *Path {
+ // Selects a path with highest local-preference.
+ //
+ // Unlike the weight attribute, which is only relevant to the local
+ // router, local preference is an attribute that routers exchange in the
+ // same AS. Highest local-pref is preferred. If we cannot decide,
+ // we return None.
+ //
+ // # Default local-pref values is 100
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ }).Debug("enter compareByLocalPref")
+ localPref1, _ := path1.GetLocalPref()
+ localPref2, _ := path2.GetLocalPref()
+ // Highest local-preference value is preferred.
+ if localPref1 > localPref2 {
+ return path1
+ } else if localPref1 < localPref2 {
+ return path2
+ } else {
+ return nil
+ }
+}
+
+func compareByLocalOrigin(path1, path2 *Path) *Path {
+
+ // Select locally originating path as best path.
+ // Locally originating routes are network routes, redistributed routes,
+ // or aggregated routes.
+ // Returns None if given paths have same source.
+ //
+ // If both paths are from same sources we cannot compare them here.
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ }).Debug("enter compareByLocalOrigin")
+ if path1.GetSource().Equal(path2.GetSource()) {
+ return nil
+ }
+
+ // Here we consider prefix from NC as locally originating static route.
+ // Hence it is preferred.
+ if path1.IsLocal() {
+ return path1
+ }
+
+ if path2.IsLocal() {
+ return path2
+ }
+ return nil
+}
+
+func compareByASPath(path1, path2 *Path) *Path {
+ // Calculated the best-paths by comparing as-path lengths.
+ //
+ // Shortest as-path length is preferred. If both path have same lengths,
+ // we return None.
+ if SelectionOptions.IgnoreAsPathLength {
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ }).Debug("compareByASPath -- skip")
+ return nil
+ }
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ }).Debug("enter compareByASPath")
+ attribute1 := path1.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
+ attribute2 := path2.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
+
+ // With addpath support, we could compare paths from API don't
+ // AS_PATH. No need to warn here.
+ if !path1.IsLocal() && !path2.IsLocal() && (attribute1 == nil || attribute2 == nil) {
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ "Key": "compareByASPath",
+ "ASPath1": attribute1,
+ "ASPath2": attribute2,
+ }).Warn("can't compare ASPath because it's not present")
+ }
+
+ l1 := path1.GetAsPathLen()
+ l2 := path2.GetAsPathLen()
+
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ }).Debugf("compareByASPath -- l1: %d, l2: %d", l1, l2)
+ if l1 > l2 {
+ return path2
+ } else if l1 < l2 {
+ return path1
+ } else {
+ return nil
+ }
+}
+
+func compareByOrigin(path1, path2 *Path) *Path {
+ // Select the best path based on origin attribute.
+ //
+ // IGP is preferred over EGP; EGP is preferred over Incomplete.
+ // If both paths have same origin, we return None.
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ }).Debug("enter compareByOrigin")
+ attribute1 := path1.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN)
+ attribute2 := path2.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN)
+
+ if attribute1 == nil || attribute2 == nil {
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ "Key": "compareByOrigin",
+ "Origin1": attribute1,
+ "Origin2": attribute2,
+ }).Error("can't compare origin because it's not present")
+ return nil
+ }
+
+ origin1 := attribute1.(*bgp.PathAttributeOrigin).Value
+ origin2 := attribute2.(*bgp.PathAttributeOrigin).Value
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ }).Debugf("compareByOrigin -- origin1: %d, origin2: %d", origin1, origin2)
+
+ // If both paths have same origins
+ if origin1 == origin2 {
+ return nil
+ } else if origin1 < origin2 {
+ return path1
+ } else {
+ return path2
+ }
+}
+
+func compareByMED(path1, path2 *Path) *Path {
+ // Select the path based with lowest MED value.
+ //
+ // If both paths have same MED, return None.
+ // By default, a route that arrives with no MED value is treated as if it
+ // had a MED of 0, the most preferred value.
+ // RFC says lower MED is preferred over higher MED value.
+ // compare MED among not only same AS path but also all path,
+ // like bgp always-compare-med
+
+ isInternal := func() bool { return path1.GetAsPathLen() == 0 && path2.GetAsPathLen() == 0 }()
+
+ isSameAS := func() bool {
+ firstAS := func(path *Path) uint32 {
+ if asPath := path.GetAsPath(); asPath != nil {
+ for _, v := range asPath.Value {
+ segType := v.GetType()
+ asList := v.GetAS()
+ if len(asList) == 0 {
+ continue
+ }
+ switch segType {
+ case bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET, bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ:
+ continue
+ }
+ return asList[0]
+ }
+ }
+ return 0
+ }
+ return firstAS(path1) != 0 && firstAS(path1) == firstAS(path2)
+ }()
+
+ if SelectionOptions.AlwaysCompareMed || isInternal || isSameAS {
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ }).Debug("enter compareByMED")
+ getMed := func(path *Path) uint32 {
+ attribute := path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
+ if attribute == nil {
+ return 0
+ }
+ med := attribute.(*bgp.PathAttributeMultiExitDisc).Value
+ return med
+ }
+
+ med1 := getMed(path1)
+ med2 := getMed(path2)
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ }).Debugf("compareByMED -- med1: %d, med2: %d", med1, med2)
+ if med1 == med2 {
+ return nil
+ } else if med1 < med2 {
+ return path1
+ }
+ return path2
+ } else {
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ }).Debugf("skip compareByMED %v %v %v", SelectionOptions.AlwaysCompareMed, isInternal, isSameAS)
+ return nil
+ }
+}
+
+func compareByASNumber(path1, path2 *Path) *Path {
+
+ //Select the path based on source (iBGP/eBGP) peer.
+ //
+ //eBGP path is preferred over iBGP. If both paths are from same kind of
+ //peers, return None.
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ }).Debug("enter compareByASNumber")
+
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ }).Debugf("compareByASNumber -- p1Asn: %d, p2Asn: %d", path1.GetSource().AS, path2.GetSource().AS)
+ // Path from confederation member should be treated as internal (IBGP learned) path.
+ isIBGP1 := path1.GetSource().Confederation || path1.IsIBGP()
+ isIBGP2 := path2.GetSource().Confederation || path2.IsIBGP()
+ // If one path is from ibgp peer and another is from ebgp peer, take the ebgp path.
+ if isIBGP1 != isIBGP2 {
+ if isIBGP1 {
+ return path2
+ }
+ return path1
+ }
+
+ // If both paths are from ebgp or ibpg peers, we cannot decide.
+ return nil
+}
+
+func compareByIGPCost(path1, path2 *Path) *Path {
+ // Select the route with the lowest IGP cost to the next hop.
+ //
+ // Return None if igp cost is same.
+ // Currently BGPS has no concept of IGP and IGP cost.
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ }).Debugf("enter compareByIGPCost -- path1: %v, path2: %v", path1, path2)
+ return nil
+}
+
+func compareByRouterID(path1, path2 *Path) (*Path, error) {
+ // Select the route received from the peer with the lowest BGP router ID.
+ //
+ // If both paths are eBGP paths, then we do not do any tie breaking, i.e we do
+ // not pick best-path based on this criteria.
+ // RFC: http://tools.ietf.org/html/rfc5004
+ // We pick best path between two iBGP paths as usual.
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ }).Debug("enter compareByRouterID")
+
+ // If both paths are from NC we have same router Id, hence cannot compare.
+ if path1.IsLocal() && path2.IsLocal() {
+ return nil, nil
+ }
+
+ // If both paths are from eBGP peers, then according to RFC we need
+ // not tie break using router id.
+ if !SelectionOptions.ExternalCompareRouterId && !path1.IsIBGP() && !path2.IsIBGP() {
+ return nil, nil
+ }
+
+ if !SelectionOptions.ExternalCompareRouterId && path1.IsIBGP() != path2.IsIBGP() {
+ return nil, fmt.Errorf("This method does not support comparing ebgp with ibgp path")
+ }
+
+ // At least one path is not coming from NC, so we get local bgp id.
+ id1 := binary.BigEndian.Uint32(path1.GetSource().ID)
+ id2 := binary.BigEndian.Uint32(path2.GetSource().ID)
+
+ // If both router ids are same/equal we cannot decide.
+ // This case is possible since router ids are arbitrary.
+ if id1 == id2 {
+ return nil, nil
+ } else if id1 < id2 {
+ return path1, nil
+ } else {
+ return path2, nil
+ }
+}
+
+func compareByAge(path1, path2 *Path) *Path {
+ if !path1.IsIBGP() && !path2.IsIBGP() && !SelectionOptions.ExternalCompareRouterId {
+ age1 := path1.GetTimestamp().UnixNano()
+ age2 := path2.GetTimestamp().UnixNano()
+ if age1 == age2 {
+ return nil
+ } else if age1 < age2 {
+ return path1
+ }
+ return path2
+ }
+ return nil
+}
+
+func (dest *Destination) String() string {
+ return fmt.Sprintf("Destination NLRI: %s", dest.nlri.String())
+}
+
+type DestinationSelectOption struct {
+ ID string
+ AS uint32
+ VRF *Vrf
+ adj bool
+ Best bool
+ MultiPath bool
+}
+
+func (d *Destination) MarshalJSON() ([]byte, error) {
+ return json.Marshal(d.GetAllKnownPathList())
+}
+
+func (d *Destination) Select(option ...DestinationSelectOption) *Destination {
+ id := GLOBAL_RIB_NAME
+ var vrf *Vrf
+ adj := false
+ best := false
+ mp := false
+ as := uint32(0)
+ for _, o := range option {
+ if o.ID != "" {
+ id = o.ID
+ }
+ if o.VRF != nil {
+ vrf = o.VRF
+ }
+ adj = o.adj
+ best = o.Best
+ mp = o.MultiPath
+ as = o.AS
+ }
+ var paths []*Path
+ if adj {
+ paths = make([]*Path, len(d.knownPathList))
+ copy(paths, d.knownPathList)
+ } else {
+ paths = d.GetKnownPathList(id, as)
+ if vrf != nil {
+ ps := make([]*Path, 0, len(paths))
+ for _, p := range paths {
+ if CanImportToVrf(vrf, p) {
+ ps = append(ps, p.ToLocal())
+ }
+ }
+ paths = ps
+ }
+ if len(paths) == 0 {
+ return nil
+ }
+ if best {
+ if !mp {
+ paths = []*Path{paths[0]}
+ } else {
+ ps := make([]*Path, 0, len(paths))
+ var best *Path
+ for _, p := range paths {
+ if best == nil {
+ best = p
+ ps = append(ps, p)
+ } else if best.Compare(p) == 0 {
+ ps = append(ps, p)
+ }
+ }
+ paths = ps
+ }
+ }
+ }
+ return NewDestination(d.nlri, 0, paths...)
+}
diff --git a/internal/pkg/table/destination_test.go b/internal/pkg/table/destination_test.go
new file mode 100644
index 00000000..110278fb
--- /dev/null
+++ b/internal/pkg/table/destination_test.go
@@ -0,0 +1,442 @@
+// Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package table
+
+import (
+ //"fmt"
+ "fmt"
+ "net"
+ "testing"
+ "time"
+
+ "github.com/osrg/gobgp/pkg/packet/bgp"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestDestinationNewIPv4(t *testing.T) {
+ peerD := DestCreatePeer()
+ pathD := DestCreatePath(peerD)
+ ipv4d := NewDestination(pathD[0].GetNlri(), 0)
+ assert.NotNil(t, ipv4d)
+}
+func TestDestinationNewIPv6(t *testing.T) {
+ peerD := DestCreatePeer()
+ pathD := DestCreatePath(peerD)
+ ipv6d := NewDestination(pathD[0].GetNlri(), 0)
+ assert.NotNil(t, ipv6d)
+}
+
+func TestDestinationSetRouteFamily(t *testing.T) {
+ dd := &Destination{}
+ dd.setRouteFamily(bgp.RF_IPv4_UC)
+ rf := dd.Family()
+ assert.Equal(t, rf, bgp.RF_IPv4_UC)
+}
+func TestDestinationGetRouteFamily(t *testing.T) {
+ dd := &Destination{}
+ dd.setRouteFamily(bgp.RF_IPv6_UC)
+ rf := dd.Family()
+ assert.Equal(t, rf, bgp.RF_IPv6_UC)
+}
+func TestDestinationSetNlri(t *testing.T) {
+ dd := &Destination{}
+ nlri := bgp.NewIPAddrPrefix(24, "13.2.3.1")
+ dd.setNlri(nlri)
+ r_nlri := dd.GetNlri()
+ assert.Equal(t, r_nlri, nlri)
+}
+func TestDestinationGetNlri(t *testing.T) {
+ dd := &Destination{}
+ nlri := bgp.NewIPAddrPrefix(24, "10.110.123.1")
+ dd.setNlri(nlri)
+ r_nlri := dd.GetNlri()
+ assert.Equal(t, r_nlri, nlri)
+}
+
+func TestCalculate2(t *testing.T) {
+
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAs4PathParam(2, []uint32{65001})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := bgp.NewIPAddrPrefix(24, "10.10.0.0")
+
+ // peer1 sends normal update message 10.10.0.0/24
+ update1 := bgp.NewBGPUpdateMessage(nil, pathAttributes, []*bgp.IPAddrPrefix{nlri})
+ peer1 := &PeerInfo{AS: 1, Address: net.IP{1, 1, 1, 1}}
+ path1 := ProcessMessage(update1, peer1, time.Now())[0]
+
+ d := NewDestination(nlri, 0)
+ d.Calculate(path1)
+
+ // suppose peer2 sends grammaatically correct but semantically flawed update message
+ // which has a withdrawal nlri not advertised before
+ update2 := bgp.NewBGPUpdateMessage([]*bgp.IPAddrPrefix{nlri}, pathAttributes, nil)
+ peer2 := &PeerInfo{AS: 2, Address: net.IP{2, 2, 2, 2}}
+ path2 := ProcessMessage(update2, peer2, time.Now())[0]
+ assert.Equal(t, path2.IsWithdraw, true)
+
+ d.Calculate(path2)
+
+ // we have a path from peer1 here
+ assert.Equal(t, len(d.knownPathList), 1)
+
+ // after that, new update with the same nlri comes from peer2
+ update3 := bgp.NewBGPUpdateMessage(nil, pathAttributes, []*bgp.IPAddrPrefix{nlri})
+ path3 := ProcessMessage(update3, peer2, time.Now())[0]
+ assert.Equal(t, path3.IsWithdraw, false)
+
+ d.Calculate(path3)
+
+ // this time, we have paths from peer1 and peer2
+ assert.Equal(t, len(d.knownPathList), 2)
+
+ // now peer3 sends normal update message 10.10.0.0/24
+ peer3 := &PeerInfo{AS: 3, Address: net.IP{3, 3, 3, 3}}
+ update4 := bgp.NewBGPUpdateMessage(nil, pathAttributes, []*bgp.IPAddrPrefix{nlri})
+ path4 := ProcessMessage(update4, peer3, time.Now())[0]
+
+ d.Calculate(path4)
+
+ // we must have paths from peer1, peer2 and peer3
+ assert.Equal(t, len(d.knownPathList), 3)
+}
+
+func TestMedTieBreaker(t *testing.T) {
+ nlri := bgp.NewIPAddrPrefix(24, "10.10.0.0")
+
+ p0 := func() *Path {
+ aspath := bgp.NewPathAttributeAsPath([]bgp.AsPathParamInterface{bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{65001, 65002}), bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{65003, 65004})})
+ attrs := []bgp.PathAttributeInterface{aspath, bgp.NewPathAttributeMultiExitDisc(0)}
+ return NewPath(nil, nlri, false, attrs, time.Now(), false)
+ }()
+
+ p1 := func() *Path {
+ aspath := bgp.NewPathAttributeAsPath([]bgp.AsPathParamInterface{bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{65001, 65002}), bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{65003, 65005})})
+ attrs := []bgp.PathAttributeInterface{aspath, bgp.NewPathAttributeMultiExitDisc(10)}
+ return NewPath(nil, nlri, false, attrs, time.Now(), false)
+ }()
+
+ // same AS
+ assert.Equal(t, compareByMED(p0, p1), p0)
+
+ p2 := func() *Path {
+ aspath := bgp.NewPathAttributeAsPath([]bgp.AsPathParamInterface{bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{65003})})
+ attrs := []bgp.PathAttributeInterface{aspath, bgp.NewPathAttributeMultiExitDisc(10)}
+ return NewPath(nil, nlri, false, attrs, time.Now(), false)
+ }()
+
+ // different AS
+ assert.Equal(t, compareByMED(p0, p2), (*Path)(nil))
+
+ p3 := func() *Path {
+ aspath := bgp.NewPathAttributeAsPath([]bgp.AsPathParamInterface{bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ, []uint32{65003, 65004}), bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{65001, 65003})})
+ attrs := []bgp.PathAttributeInterface{aspath, bgp.NewPathAttributeMultiExitDisc(0)}
+ return NewPath(nil, nlri, false, attrs, time.Now(), false)
+ }()
+
+ p4 := func() *Path {
+ aspath := bgp.NewPathAttributeAsPath([]bgp.AsPathParamInterface{bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{65001, 65002}), bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ, []uint32{65005, 65006})})
+ attrs := []bgp.PathAttributeInterface{aspath, bgp.NewPathAttributeMultiExitDisc(10)}
+ return NewPath(nil, nlri, false, attrs, time.Now(), false)
+ }()
+
+ // ignore confed
+ assert.Equal(t, compareByMED(p3, p4), p3)
+
+ p5 := func() *Path {
+ attrs := []bgp.PathAttributeInterface{bgp.NewPathAttributeMultiExitDisc(0)}
+ return NewPath(nil, nlri, false, attrs, time.Now(), false)
+ }()
+
+ p6 := func() *Path {
+ attrs := []bgp.PathAttributeInterface{bgp.NewPathAttributeMultiExitDisc(10)}
+ return NewPath(nil, nlri, false, attrs, time.Now(), false)
+ }()
+
+ // no aspath
+ assert.Equal(t, compareByMED(p5, p6), p5)
+}
+
+func TestTimeTieBreaker(t *testing.T) {
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAs4PathParam(2, []uint32{65001})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := bgp.NewIPAddrPrefix(24, "10.10.0.0")
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, []*bgp.IPAddrPrefix{nlri})
+ peer1 := &PeerInfo{AS: 2, LocalAS: 1, Address: net.IP{1, 1, 1, 1}, ID: net.IP{1, 1, 1, 1}}
+ path1 := ProcessMessage(updateMsg, peer1, time.Now())[0]
+
+ peer2 := &PeerInfo{AS: 2, LocalAS: 1, Address: net.IP{2, 2, 2, 2}, ID: net.IP{2, 2, 2, 2}} // weaker router-id
+ path2 := ProcessMessage(updateMsg, peer2, time.Now().Add(-1*time.Hour))[0] // older than path1
+
+ d := NewDestination(nlri, 0)
+ d.Calculate(path1)
+ d.Calculate(path2)
+
+ assert.Equal(t, len(d.knownPathList), 2)
+ assert.Equal(t, true, d.GetBestPath("", 0).GetSource().ID.Equal(net.IP{2, 2, 2, 2})) // path from peer2 win
+
+ // this option disables tie breaking by age
+ SelectionOptions.ExternalCompareRouterId = true
+ d = NewDestination(nlri, 0)
+ d.Calculate(path1)
+ d.Calculate(path2)
+
+ assert.Equal(t, len(d.knownPathList), 2)
+ assert.Equal(t, true, d.GetBestPath("", 0).GetSource().ID.Equal(net.IP{1, 1, 1, 1})) // path from peer1 win
+}
+
+func DestCreatePeer() []*PeerInfo {
+ peerD1 := &PeerInfo{AS: 65000}
+ peerD2 := &PeerInfo{AS: 65001}
+ peerD3 := &PeerInfo{AS: 65002}
+ peerD := []*PeerInfo{peerD1, peerD2, peerD3}
+ return peerD
+}
+
+func DestCreatePath(peerD []*PeerInfo) []*Path {
+ bgpMsgD1 := updateMsgD1()
+ bgpMsgD2 := updateMsgD2()
+ bgpMsgD3 := updateMsgD3()
+ pathD := make([]*Path, 3)
+ for i, msg := range []*bgp.BGPMessage{bgpMsgD1, bgpMsgD2, bgpMsgD3} {
+ updateMsgD := msg.Body.(*bgp.BGPUpdate)
+ nlriList := updateMsgD.NLRI
+ pathAttributes := updateMsgD.PathAttributes
+ nlri_info := nlriList[0]
+ pathD[i] = NewPath(peerD[i], nlri_info, false, pathAttributes, time.Now(), false)
+ }
+ return pathD
+}
+
+func updateMsgD1() *bgp.BGPMessage {
+
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65000})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("192.168.50.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+
+ pathAttributes := []bgp.PathAttributeInterface{
+ origin,
+ aspath,
+ nexthop,
+ med,
+ }
+
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.10.0")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ UpdatePathAttrs4ByteAs(updateMsg.Body.(*bgp.BGPUpdate))
+ return updateMsg
+}
+
+func updateMsgD2() *bgp.BGPMessage {
+
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65100})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("192.168.100.1")
+ med := bgp.NewPathAttributeMultiExitDisc(100)
+
+ pathAttributes := []bgp.PathAttributeInterface{
+ origin,
+ aspath,
+ nexthop,
+ med,
+ }
+
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "20.20.20.0")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ UpdatePathAttrs4ByteAs(updateMsg.Body.(*bgp.BGPUpdate))
+ return updateMsg
+}
+func updateMsgD3() *bgp.BGPMessage {
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65100})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("192.168.150.1")
+ med := bgp.NewPathAttributeMultiExitDisc(100)
+
+ pathAttributes := []bgp.PathAttributeInterface{
+ origin,
+ aspath,
+ nexthop,
+ med,
+ }
+
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "30.30.30.0")}
+ w1 := bgp.NewIPAddrPrefix(23, "40.40.40.0")
+ withdrawnRoutes := []*bgp.IPAddrPrefix{w1}
+ updateMsg := bgp.NewBGPUpdateMessage(withdrawnRoutes, pathAttributes, nlri)
+ UpdatePathAttrs4ByteAs(updateMsg.Body.(*bgp.BGPUpdate))
+ return updateMsg
+}
+
+func TestRadixkey(t *testing.T) {
+ assert.Equal(t, "000010100000001100100000", CidrToRadixkey("10.3.32.0/24"))
+ assert.Equal(t, "000010100000001100100000", IpToRadixkey(net.ParseIP("10.3.32.0").To4(), 24))
+ assert.Equal(t, "000010100000001100100000", IpToRadixkey(net.ParseIP("10.3.32.0").To4(), 24))
+ assert.Equal(t, CidrToRadixkey("::ffff:0.0.0.0/96")+"000010100000001100100000", CidrToRadixkey("::ffff:10.3.32.0/120"))
+}
+
+func TestIpToRadixkey(t *testing.T) {
+ for i := byte(0); i < 255; i += 3 {
+ for y := byte(1); y < 128; y *= 2 {
+ ip := net.IPv4(i, i+2, i+3, i-y)
+ for n := uint8(16); n <= 32; n += 2 {
+ exp := CidrToRadixkey(fmt.Sprintf("%v/%d", ip.To4(), n))
+ got := IpToRadixkey(ip.To4(), n)
+ if exp != got {
+ t.Fatalf(`exp %v; got %v`, exp, got)
+ }
+ }
+ for n := uint8(116); n <= 128; n += 2 {
+ exp := CidrToRadixkey(fmt.Sprintf("::ffff:%v/%d", ip.To16(), n))
+ got := IpToRadixkey(ip.To16(), n)
+ if exp != got {
+ t.Fatalf(`exp %v; got %v`, exp, got)
+ }
+ }
+ }
+ }
+}
+
+func TestMultipath(t *testing.T) {
+ UseMultiplePaths.Enabled = true
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAs4PathParam(2, []uint32{65000})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("192.168.150.1")
+ med := bgp.NewPathAttributeMultiExitDisc(100)
+
+ pathAttributes := []bgp.PathAttributeInterface{
+ origin,
+ aspath,
+ nexthop,
+ med,
+ }
+
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.10.0")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ peer1 := &PeerInfo{AS: 1, Address: net.IP{1, 1, 1, 1}, ID: net.IP{1, 1, 1, 1}}
+ path1 := ProcessMessage(updateMsg, peer1, time.Now())[0]
+ peer2 := &PeerInfo{AS: 2, Address: net.IP{2, 2, 2, 2}, ID: net.IP{2, 2, 2, 2}}
+
+ med = bgp.NewPathAttributeMultiExitDisc(100)
+ nexthop = bgp.NewPathAttributeNextHop("192.168.150.2")
+ pathAttributes = []bgp.PathAttributeInterface{
+ origin,
+ aspath,
+ nexthop,
+ med,
+ }
+ updateMsg = bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ path2 := ProcessMessage(updateMsg, peer2, time.Now())[0]
+
+ d := NewDestination(nlri[0], 0)
+ d.Calculate(path2)
+
+ best, old, multi := d.Calculate(path1).GetChanges(GLOBAL_RIB_NAME, 0, false)
+ assert.NotNil(t, best)
+ assert.Equal(t, old, path2)
+ assert.Equal(t, len(multi), 2)
+ assert.Equal(t, len(d.GetKnownPathList(GLOBAL_RIB_NAME, 0)), 2)
+
+ path3 := path2.Clone(true)
+ dd := d.Calculate(path3)
+ best, old, multi = dd.GetChanges(GLOBAL_RIB_NAME, 0, false)
+ assert.Nil(t, best)
+ assert.Equal(t, old, path1)
+ assert.Equal(t, len(multi), 1)
+ assert.Equal(t, len(d.GetKnownPathList(GLOBAL_RIB_NAME, 0)), 1)
+
+ peer3 := &PeerInfo{AS: 3, Address: net.IP{3, 3, 3, 3}, ID: net.IP{3, 3, 3, 3}}
+ med = bgp.NewPathAttributeMultiExitDisc(50)
+ nexthop = bgp.NewPathAttributeNextHop("192.168.150.3")
+ pathAttributes = []bgp.PathAttributeInterface{
+ origin,
+ aspath,
+ nexthop,
+ med,
+ }
+ updateMsg = bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ path4 := ProcessMessage(updateMsg, peer3, time.Now())[0]
+ dd = d.Calculate(path4)
+ best, _, multi = dd.GetChanges(GLOBAL_RIB_NAME, 0, false)
+ assert.NotNil(t, best)
+ assert.Equal(t, len(multi), 1)
+ assert.Equal(t, len(d.GetKnownPathList(GLOBAL_RIB_NAME, 0)), 2)
+
+ nexthop = bgp.NewPathAttributeNextHop("192.168.150.2")
+ pathAttributes = []bgp.PathAttributeInterface{
+ origin,
+ aspath,
+ nexthop,
+ med,
+ }
+ updateMsg = bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ path5 := ProcessMessage(updateMsg, peer2, time.Now())[0]
+ best, _, multi = d.Calculate(path5).GetChanges(GLOBAL_RIB_NAME, 0, false)
+ assert.NotNil(t, best)
+ assert.Equal(t, len(multi), 2)
+ assert.Equal(t, len(d.GetKnownPathList(GLOBAL_RIB_NAME, 0)), 3)
+
+ UseMultiplePaths.Enabled = false
+}
+
+func TestIdMap(t *testing.T) {
+ d := NewDestination(bgp.NewIPAddrPrefix(24, "10.10.0.101"), 64)
+ for i := 0; ; i++ {
+ if id, err := d.localIdMap.FindandSetZeroBit(); err == nil {
+ assert.Equal(t, uint(i+1), id)
+ } else {
+ assert.Equal(t, i, 63)
+ break
+ }
+ }
+ d.localIdMap.Expand()
+ for i := 0; i < 64; i++ {
+ id, _ := d.localIdMap.FindandSetZeroBit()
+ assert.Equal(t, id, uint(64+i))
+ }
+ _, err := d.localIdMap.FindandSetZeroBit()
+ assert.NotNil(t, err)
+}
+
+func TestGetWithdrawnPath(t *testing.T) {
+ attrs := []bgp.PathAttributeInterface{
+ bgp.NewPathAttributeOrigin(0),
+ }
+ p1 := NewPath(nil, bgp.NewIPAddrPrefix(24, "13.2.3.0"), false, attrs, time.Now(), false)
+ p2 := NewPath(nil, bgp.NewIPAddrPrefix(24, "13.2.4.0"), false, attrs, time.Now(), false)
+ p3 := NewPath(nil, bgp.NewIPAddrPrefix(24, "13.2.5.0"), false, attrs, time.Now(), false)
+
+ u := &Update{
+ KnownPathList: []*Path{p2},
+ OldKnownPathList: []*Path{p1, p2, p3},
+ }
+
+ l := u.GetWithdrawnPath()
+ assert.Equal(t, len(l), 2)
+ assert.Equal(t, l[0].GetNlri(), p1.GetNlri())
+}
diff --git a/internal/pkg/table/message.go b/internal/pkg/table/message.go
new file mode 100644
index 00000000..31b90596
--- /dev/null
+++ b/internal/pkg/table/message.go
@@ -0,0 +1,502 @@
+// Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package table
+
+import (
+ "bytes"
+ "reflect"
+
+ "github.com/osrg/gobgp/pkg/packet/bgp"
+ log "github.com/sirupsen/logrus"
+)
+
+func UpdatePathAttrs2ByteAs(msg *bgp.BGPUpdate) error {
+ ps := msg.PathAttributes
+ msg.PathAttributes = make([]bgp.PathAttributeInterface, len(ps))
+ copy(msg.PathAttributes, ps)
+ var asAttr *bgp.PathAttributeAsPath
+ idx := 0
+ for i, attr := range msg.PathAttributes {
+ if a, ok := attr.(*bgp.PathAttributeAsPath); ok {
+ asAttr = a
+ idx = i
+ break
+ }
+ }
+
+ if asAttr == nil {
+ return nil
+ }
+
+ as4Params := make([]*bgp.As4PathParam, 0, len(asAttr.Value))
+ as2Params := make([]bgp.AsPathParamInterface, 0, len(asAttr.Value))
+ mkAs4 := false
+ for _, param := range asAttr.Value {
+ segType := param.GetType()
+ asList := param.GetAS()
+ as2Path := make([]uint16, 0, len(asList))
+ for _, as := range asList {
+ if as > (1<<16)-1 {
+ mkAs4 = true
+ as2Path = append(as2Path, bgp.AS_TRANS)
+ } else {
+ as2Path = append(as2Path, uint16(as))
+ }
+ }
+ as2Params = append(as2Params, bgp.NewAsPathParam(segType, as2Path))
+
+ // RFC 6793 4.2.2 Generating Updates
+ //
+ // Whenever the AS path information contains the AS_CONFED_SEQUENCE or
+ // AS_CONFED_SET path segment, the NEW BGP speaker MUST exclude such
+ // path segments from the AS4_PATH attribute being constructed.
+ switch segType {
+ case bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ, bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET:
+ // pass
+ default:
+ if as4param, ok := param.(*bgp.As4PathParam); ok {
+ as4Params = append(as4Params, as4param)
+ }
+ }
+ }
+ msg.PathAttributes[idx] = bgp.NewPathAttributeAsPath(as2Params)
+ if mkAs4 {
+ msg.PathAttributes = append(msg.PathAttributes, bgp.NewPathAttributeAs4Path(as4Params))
+ }
+ return nil
+}
+
+func UpdatePathAttrs4ByteAs(msg *bgp.BGPUpdate) error {
+ var asAttr *bgp.PathAttributeAsPath
+ var as4Attr *bgp.PathAttributeAs4Path
+ asAttrPos := 0
+ as4AttrPos := 0
+ for i, attr := range msg.PathAttributes {
+ switch attr.(type) {
+ case *bgp.PathAttributeAsPath:
+ asAttr = attr.(*bgp.PathAttributeAsPath)
+ for j, param := range asAttr.Value {
+ as2Param, ok := param.(*bgp.AsPathParam)
+ if ok {
+ asPath := make([]uint32, 0, len(as2Param.AS))
+ for _, as := range as2Param.AS {
+ asPath = append(asPath, uint32(as))
+ }
+ as4Param := bgp.NewAs4PathParam(as2Param.Type, asPath)
+ asAttr.Value[j] = as4Param
+ }
+ }
+ asAttrPos = i
+ msg.PathAttributes[i] = asAttr
+ case *bgp.PathAttributeAs4Path:
+ as4AttrPos = i
+ as4Attr = attr.(*bgp.PathAttributeAs4Path)
+ }
+ }
+
+ if as4Attr != nil {
+ msg.PathAttributes = append(msg.PathAttributes[:as4AttrPos], msg.PathAttributes[as4AttrPos+1:]...)
+ }
+
+ if asAttr == nil || as4Attr == nil {
+ return nil
+ }
+
+ asLen := 0
+ asConfedLen := 0
+ asParams := make([]bgp.AsPathParamInterface, 0, len(asAttr.Value))
+ for _, param := range asAttr.Value {
+ asLen += param.ASLen()
+ switch param.GetType() {
+ case bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET:
+ asConfedLen++
+ case bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ:
+ asConfedLen += len(param.GetAS())
+ }
+ asParams = append(asParams, param)
+ }
+
+ as4Len := 0
+ as4Params := make([]bgp.AsPathParamInterface, 0, len(as4Attr.Value))
+ if as4Attr != nil {
+ for _, p := range as4Attr.Value {
+ // RFC 6793 6. Error Handling
+ //
+ // the path segment types AS_CONFED_SEQUENCE and AS_CONFED_SET [RFC5065]
+ // MUST NOT be carried in the AS4_PATH attribute of an UPDATE message.
+ // A NEW BGP speaker that receives these path segment types in the AS4_PATH
+ // attribute of an UPDATE message from an OLD BGP speaker MUST discard
+ // these path segments, adjust the relevant attribute fields accordingly,
+ // and continue processing the UPDATE message.
+ // This case SHOULD be logged locally for analysis.
+ switch p.Type {
+ case bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ, bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET:
+ typ := "CONFED_SEQ"
+ if p.Type == bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET {
+ typ = "CONFED_SET"
+ }
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ }).Warnf("AS4_PATH contains %s segment %s. ignore", typ, p.String())
+ continue
+ }
+ as4Len += p.ASLen()
+ as4Params = append(as4Params, p)
+ }
+ }
+
+ if asLen+asConfedLen < as4Len {
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ }).Warn("AS4_PATH is longer than AS_PATH. ignore AS4_PATH")
+ return nil
+ }
+
+ keepNum := asLen + asConfedLen - as4Len
+
+ newParams := make([]bgp.AsPathParamInterface, 0, len(asAttr.Value))
+ for _, param := range asParams {
+ if keepNum-param.ASLen() >= 0 {
+ newParams = append(newParams, param)
+ keepNum -= param.ASLen()
+ } else {
+ // only SEQ param reaches here
+ newParams = append(newParams, bgp.NewAs4PathParam(param.GetType(), param.GetAS()[:keepNum]))
+ keepNum = 0
+ }
+
+ if keepNum <= 0 {
+ break
+ }
+ }
+
+ for _, param := range as4Params {
+ lastParam := newParams[len(newParams)-1]
+ lastParamAS := lastParam.GetAS()
+ paramType := param.GetType()
+ paramAS := param.GetAS()
+ if paramType == lastParam.GetType() && paramType == bgp.BGP_ASPATH_ATTR_TYPE_SEQ {
+ if len(lastParamAS)+len(paramAS) > 255 {
+ newParams[len(newParams)-1] = bgp.NewAs4PathParam(paramType, append(lastParamAS, paramAS[:255-len(lastParamAS)]...))
+ newParams = append(newParams, bgp.NewAs4PathParam(paramType, paramAS[255-len(lastParamAS):]))
+ } else {
+ newParams[len(newParams)-1] = bgp.NewAs4PathParam(paramType, append(lastParamAS, paramAS...))
+ }
+ } else {
+ newParams = append(newParams, param)
+ }
+ }
+
+ newIntfParams := make([]bgp.AsPathParamInterface, 0, len(asAttr.Value))
+ newIntfParams = append(newIntfParams, newParams...)
+
+ msg.PathAttributes[asAttrPos] = bgp.NewPathAttributeAsPath(newIntfParams)
+ return nil
+}
+
+func UpdatePathAggregator2ByteAs(msg *bgp.BGPUpdate) {
+ as := uint32(0)
+ var addr string
+ for i, attr := range msg.PathAttributes {
+ switch attr.(type) {
+ case *bgp.PathAttributeAggregator:
+ agg := attr.(*bgp.PathAttributeAggregator)
+ addr = agg.Value.Address.String()
+ if agg.Value.AS > (1<<16)-1 {
+ as = agg.Value.AS
+ msg.PathAttributes[i] = bgp.NewPathAttributeAggregator(uint16(bgp.AS_TRANS), addr)
+ } else {
+ msg.PathAttributes[i] = bgp.NewPathAttributeAggregator(uint16(agg.Value.AS), addr)
+ }
+ }
+ }
+ if as != 0 {
+ msg.PathAttributes = append(msg.PathAttributes, bgp.NewPathAttributeAs4Aggregator(as, addr))
+ }
+}
+
+func UpdatePathAggregator4ByteAs(msg *bgp.BGPUpdate) error {
+ var aggAttr *bgp.PathAttributeAggregator
+ var agg4Attr *bgp.PathAttributeAs4Aggregator
+ agg4AttrPos := 0
+ for i, attr := range msg.PathAttributes {
+ switch attr.(type) {
+ case *bgp.PathAttributeAggregator:
+ attr := attr.(*bgp.PathAttributeAggregator)
+ if attr.Value.Askind == reflect.Uint16 {
+ aggAttr = attr
+ aggAttr.Value.Askind = reflect.Uint32
+ }
+ case *bgp.PathAttributeAs4Aggregator:
+ agg4Attr = attr.(*bgp.PathAttributeAs4Aggregator)
+ agg4AttrPos = i
+ }
+ }
+ if aggAttr == nil && agg4Attr == nil {
+ return nil
+ }
+
+ if aggAttr == nil && agg4Attr != nil {
+ return bgp.NewMessageError(bgp.BGP_ERROR_UPDATE_MESSAGE_ERROR, bgp.BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "AS4 AGGREGATOR attribute exists, but AGGREGATOR doesn't")
+ }
+
+ if agg4Attr != nil {
+ msg.PathAttributes = append(msg.PathAttributes[:agg4AttrPos], msg.PathAttributes[agg4AttrPos+1:]...)
+ aggAttr.Value.AS = agg4Attr.Value.AS
+ }
+ return nil
+}
+
+type cage struct {
+ attrsBytes []byte
+ paths []*Path
+}
+
+func newCage(b []byte, path *Path) *cage {
+ return &cage{
+ attrsBytes: b,
+ paths: []*Path{path},
+ }
+}
+
+type packerInterface interface {
+ add(*Path)
+ pack(options ...*bgp.MarshallingOption) []*bgp.BGPMessage
+}
+
+type packer struct {
+ eof bool
+ family bgp.RouteFamily
+ total uint32
+}
+
+type packerMP struct {
+ packer
+ paths []*Path
+ withdrawals []*Path
+}
+
+func (p *packerMP) add(path *Path) {
+ p.packer.total++
+
+ if path.IsEOR() {
+ p.packer.eof = true
+ return
+ }
+
+ if path.IsWithdraw {
+ p.withdrawals = append(p.withdrawals, path)
+ return
+ }
+
+ p.paths = append(p.paths, path)
+}
+
+func createMPReachMessage(path *Path) *bgp.BGPMessage {
+ oattrs := path.GetPathAttrs()
+ attrs := make([]bgp.PathAttributeInterface, 0, len(oattrs))
+ for _, a := range oattrs {
+ if a.GetType() == bgp.BGP_ATTR_TYPE_MP_REACH_NLRI {
+ attrs = append(attrs, bgp.NewPathAttributeMpReachNLRI(path.GetNexthop().String(), []bgp.AddrPrefixInterface{path.GetNlri()}))
+ } else {
+ attrs = append(attrs, a)
+ }
+ }
+ return bgp.NewBGPUpdateMessage(nil, attrs, nil)
+}
+
+func (p *packerMP) pack(options ...*bgp.MarshallingOption) []*bgp.BGPMessage {
+ msgs := make([]*bgp.BGPMessage, 0, p.packer.total)
+
+ for _, path := range p.withdrawals {
+ nlris := []bgp.AddrPrefixInterface{path.GetNlri()}
+ msgs = append(msgs, bgp.NewBGPUpdateMessage(nil, []bgp.PathAttributeInterface{bgp.NewPathAttributeMpUnreachNLRI(nlris)}, nil))
+ }
+
+ for _, path := range p.paths {
+ msgs = append(msgs, createMPReachMessage(path))
+ }
+
+ if p.eof {
+ msgs = append(msgs, bgp.NewEndOfRib(p.family))
+ }
+ return msgs
+}
+
+func newPackerMP(f bgp.RouteFamily) *packerMP {
+ return &packerMP{
+ packer: packer{
+ family: f,
+ },
+ withdrawals: make([]*Path, 0),
+ paths: make([]*Path, 0),
+ }
+}
+
+type packerV4 struct {
+ packer
+ hashmap map[uint32][]*cage
+ mpPaths []*Path
+ withdrawals []*Path
+}
+
+func (p *packerV4) add(path *Path) {
+ p.packer.total++
+
+ if path.IsEOR() {
+ p.packer.eof = true
+ return
+ }
+
+ if path.IsWithdraw {
+ p.withdrawals = append(p.withdrawals, path)
+ return
+ }
+
+ if path.GetNexthop().To4() == nil {
+ // RFC 5549
+ p.mpPaths = append(p.mpPaths, path)
+ return
+ }
+
+ key := path.GetHash()
+ attrsB := bytes.NewBuffer(make([]byte, 0))
+ for _, v := range path.GetPathAttrs() {
+ b, _ := v.Serialize()
+ attrsB.Write(b)
+ }
+
+ if cages, y := p.hashmap[key]; y {
+ added := false
+ for _, c := range cages {
+ if bytes.Equal(c.attrsBytes, attrsB.Bytes()) {
+ c.paths = append(c.paths, path)
+ added = true
+ break
+ }
+ }
+ if !added {
+ p.hashmap[key] = append(p.hashmap[key], newCage(attrsB.Bytes(), path))
+ }
+ } else {
+ p.hashmap[key] = []*cage{newCage(attrsB.Bytes(), path)}
+ }
+}
+
+func (p *packerV4) pack(options ...*bgp.MarshallingOption) []*bgp.BGPMessage {
+ split := func(max int, paths []*Path) ([]*bgp.IPAddrPrefix, []*Path) {
+ nlris := make([]*bgp.IPAddrPrefix, 0, max)
+ i := 0
+ if max > len(paths) {
+ max = len(paths)
+ }
+ for ; i < max; i++ {
+ nlris = append(nlris, paths[i].GetNlri().(*bgp.IPAddrPrefix))
+ }
+ return nlris, paths[i:]
+ }
+ addpathNLRILen := 0
+ if bgp.IsAddPathEnabled(false, p.packer.family, options) {
+ addpathNLRILen = 4
+ }
+ // Header + Update (WithdrawnRoutesLen +
+ // TotalPathAttributeLen + attributes + maxlen of NLRI).
+ // the max size of NLRI is 5bytes (plus 4bytes with addpath enabled)
+ maxNLRIs := func(attrsLen int) int {
+ return (bgp.BGP_MAX_MESSAGE_LENGTH - (19 + 2 + 2 + attrsLen)) / (5 + addpathNLRILen)
+ }
+
+ loop := func(attrsLen int, paths []*Path, cb func([]*bgp.IPAddrPrefix)) {
+ max := maxNLRIs(attrsLen)
+ var nlris []*bgp.IPAddrPrefix
+ for {
+ nlris, paths = split(max, paths)
+ if len(nlris) == 0 {
+ break
+ }
+ cb(nlris)
+ }
+ }
+
+ msgs := make([]*bgp.BGPMessage, 0, p.packer.total)
+
+ loop(0, p.withdrawals, func(nlris []*bgp.IPAddrPrefix) {
+ msgs = append(msgs, bgp.NewBGPUpdateMessage(nlris, nil, nil))
+ })
+
+ for _, cages := range p.hashmap {
+ for _, c := range cages {
+ paths := c.paths
+
+ attrs := paths[0].GetPathAttrs()
+ attrsLen := 0
+ for _, a := range attrs {
+ attrsLen += a.Len()
+ }
+
+ loop(attrsLen, paths, func(nlris []*bgp.IPAddrPrefix) {
+ msgs = append(msgs, bgp.NewBGPUpdateMessage(nil, attrs, nlris))
+ })
+ }
+ }
+
+ for _, path := range p.mpPaths {
+ msgs = append(msgs, createMPReachMessage(path))
+ }
+
+ if p.eof {
+ msgs = append(msgs, bgp.NewEndOfRib(p.family))
+ }
+ return msgs
+}
+
+func newPackerV4(f bgp.RouteFamily) *packerV4 {
+ return &packerV4{
+ packer: packer{
+ family: f,
+ },
+ hashmap: make(map[uint32][]*cage),
+ withdrawals: make([]*Path, 0),
+ mpPaths: make([]*Path, 0),
+ }
+}
+
+func newPacker(f bgp.RouteFamily) packerInterface {
+ switch f {
+ case bgp.RF_IPv4_UC:
+ return newPackerV4(bgp.RF_IPv4_UC)
+ default:
+ return newPackerMP(f)
+ }
+}
+
+func CreateUpdateMsgFromPaths(pathList []*Path, options ...*bgp.MarshallingOption) []*bgp.BGPMessage {
+ msgs := make([]*bgp.BGPMessage, 0, len(pathList))
+
+ m := make(map[bgp.RouteFamily]packerInterface)
+ for _, path := range pathList {
+ f := path.GetRouteFamily()
+ if _, y := m[f]; !y {
+ m[f] = newPacker(f)
+ }
+ m[f].add(path)
+ }
+
+ for _, p := range m {
+ msgs = append(msgs, p.pack(options...)...)
+ }
+ return msgs
+}
diff --git a/internal/pkg/table/message_test.go b/internal/pkg/table/message_test.go
new file mode 100644
index 00000000..28a380fe
--- /dev/null
+++ b/internal/pkg/table/message_test.go
@@ -0,0 +1,663 @@
+// Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package table
+
+import (
+ "fmt"
+ "reflect"
+ "testing"
+ "time"
+
+ "github.com/osrg/gobgp/pkg/packet/bgp"
+ "github.com/stretchr/testify/assert"
+)
+
+// before:
+// as-path : 65000, 4000, 400000, 300000, 40001
+// expected result:
+// as-path : 65000, 4000, 23456, 23456, 40001
+// as4-path : 65000, 4000, 400000, 300000, 40001
+func TestAsPathAs2Trans1(t *testing.T) {
+ as := []uint32{65000, 4000, 400000, 300000, 40001}
+ params := []bgp.AsPathParamInterface{bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, as)}
+ aspath := bgp.NewPathAttributeAsPath(params)
+ msg := bgp.NewBGPUpdateMessage(nil, []bgp.PathAttributeInterface{aspath}, nil).Body.(*bgp.BGPUpdate)
+ UpdatePathAttrs2ByteAs(msg)
+ assert.Equal(t, len(msg.PathAttributes), 2)
+ assert.Equal(t, len(msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value), 1)
+ assert.Equal(t, len(msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.AsPathParam).AS), 5)
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.AsPathParam).AS[0], uint16(65000))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.AsPathParam).AS[1], uint16(4000))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.AsPathParam).AS[2], uint16(bgp.AS_TRANS))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.AsPathParam).AS[3], uint16(bgp.AS_TRANS))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.AsPathParam).AS[4], uint16(40001))
+ assert.Equal(t, len(msg.PathAttributes[1].(*bgp.PathAttributeAs4Path).Value), 1)
+ assert.Equal(t, len(msg.PathAttributes[1].(*bgp.PathAttributeAs4Path).Value[0].AS), 5)
+ assert.Equal(t, msg.PathAttributes[1].(*bgp.PathAttributeAs4Path).Value[0].AS[0], uint32(65000))
+ assert.Equal(t, msg.PathAttributes[1].(*bgp.PathAttributeAs4Path).Value[0].AS[1], uint32(4000))
+ assert.Equal(t, msg.PathAttributes[1].(*bgp.PathAttributeAs4Path).Value[0].AS[2], uint32(400000))
+ assert.Equal(t, msg.PathAttributes[1].(*bgp.PathAttributeAs4Path).Value[0].AS[3], uint32(300000))
+ assert.Equal(t, msg.PathAttributes[1].(*bgp.PathAttributeAs4Path).Value[0].AS[4], uint32(40001))
+}
+
+// before:
+// as-path : 65000, 4000, 40000, 30000, 40001
+// expected result:
+// as-path : 65000, 4000, 40000, 30000, 40001
+func TestAsPathAs2Trans2(t *testing.T) {
+ as := []uint32{65000, 4000, 40000, 30000, 40001}
+ params := []bgp.AsPathParamInterface{bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, as)}
+ aspath := bgp.NewPathAttributeAsPath(params)
+ msg := bgp.NewBGPUpdateMessage(nil, []bgp.PathAttributeInterface{aspath}, nil).Body.(*bgp.BGPUpdate)
+ UpdatePathAttrs2ByteAs(msg)
+ assert.Equal(t, len(msg.PathAttributes), 1)
+ assert.Equal(t, len(msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value), 1)
+ assert.Equal(t, len(msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.AsPathParam).AS), 5)
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.AsPathParam).AS[0], uint16(65000))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.AsPathParam).AS[1], uint16(4000))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.AsPathParam).AS[2], uint16(40000))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.AsPathParam).AS[3], uint16(30000))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.AsPathParam).AS[4], uint16(40001))
+}
+
+// before:
+// as-path : 65000, 4000, 23456, 23456, 40001
+// as4-path : 400000, 300000, 40001
+// expected result:
+// as-path : 65000, 4000, 400000, 300000, 40001
+func TestAsPathAs4Trans1(t *testing.T) {
+ as := []uint16{65000, 4000, bgp.AS_TRANS, bgp.AS_TRANS, 40001}
+ params := []bgp.AsPathParamInterface{bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, as)}
+ aspath := bgp.NewPathAttributeAsPath(params)
+
+ as4 := []uint32{400000, 300000, 40001}
+ param4s := []*bgp.As4PathParam{bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, as4)}
+ as4path := bgp.NewPathAttributeAs4Path(param4s)
+ msg := bgp.NewBGPUpdateMessage(nil, []bgp.PathAttributeInterface{aspath, as4path}, nil).Body.(*bgp.BGPUpdate)
+ UpdatePathAttrs4ByteAs(msg)
+ assert.Equal(t, len(msg.PathAttributes), 1)
+ assert.Equal(t, len(msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value), 1)
+ assert.Equal(t, len(msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS), 5)
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[0], uint32(65000))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[1], uint32(4000))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[2], uint32(400000))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[3], uint32(300000))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[4], uint32(40001))
+}
+
+// before:
+// as-path : 65000, 4000, {10, 20, 30}, 23456, 23456, 40001
+// as4-path : 400000, 300000, 40001
+// expected result:
+// as-path : 65000, 4000, {10, 20, 30}, 400000, 300000, 40001
+func TestAsPathAs4Trans2(t *testing.T) {
+ as1 := []uint16{65000, 4000}
+ param1 := bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, as1)
+ as2 := []uint16{10, 20, 30}
+ param2 := bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SET, as2)
+ as3 := []uint16{bgp.AS_TRANS, bgp.AS_TRANS, 40001}
+ param3 := bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, as3)
+ params := []bgp.AsPathParamInterface{param1, param2, param3}
+ aspath := bgp.NewPathAttributeAsPath(params)
+
+ as4 := []uint32{400000, 300000, 40001}
+ param4s := []*bgp.As4PathParam{bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, as4)}
+ as4path := bgp.NewPathAttributeAs4Path(param4s)
+ msg := bgp.NewBGPUpdateMessage(nil, []bgp.PathAttributeInterface{aspath, as4path}, nil).Body.(*bgp.BGPUpdate)
+ UpdatePathAttrs4ByteAs(msg)
+ assert.Equal(t, len(msg.PathAttributes), 1)
+ assert.Equal(t, len(msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value), 3)
+ assert.Equal(t, len(msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS), 2)
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[0], uint32(65000))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[1], uint32(4000))
+ assert.Equal(t, len(msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[1].(*bgp.As4PathParam).AS), 3)
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[1].(*bgp.As4PathParam).AS[0], uint32(10))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[1].(*bgp.As4PathParam).AS[1], uint32(20))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[1].(*bgp.As4PathParam).AS[2], uint32(30))
+ assert.Equal(t, len(msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[2].(*bgp.As4PathParam).AS), 3)
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[2].(*bgp.As4PathParam).AS[0], uint32(400000))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[2].(*bgp.As4PathParam).AS[1], uint32(300000))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[2].(*bgp.As4PathParam).AS[2], uint32(40001))
+}
+
+// before:
+// as-path : 65000, 4000, {10, 20, 30}, 23456, 23456, 40001
+// as4-path : 3000, 400000, 300000, 40001
+// expected result:
+// as-path : 65000, 4000, 3000, 400000, 300000, 40001
+func TestAsPathAs4Trans3(t *testing.T) {
+ as1 := []uint16{65000, 4000}
+ param1 := bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, as1)
+ as2 := []uint16{10, 20, 30}
+ param2 := bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SET, as2)
+ as3 := []uint16{bgp.AS_TRANS, bgp.AS_TRANS, 40001}
+ param3 := bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, as3)
+ params := []bgp.AsPathParamInterface{param1, param2, param3}
+ aspath := bgp.NewPathAttributeAsPath(params)
+
+ as4 := []uint32{3000, 400000, 300000, 40001}
+ param4s := []*bgp.As4PathParam{bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, as4)}
+ as4path := bgp.NewPathAttributeAs4Path(param4s)
+ msg := bgp.NewBGPUpdateMessage(nil, []bgp.PathAttributeInterface{aspath, as4path}, nil).Body.(*bgp.BGPUpdate)
+ UpdatePathAttrs4ByteAs(msg)
+ assert.Equal(t, len(msg.PathAttributes), 1)
+ assert.Equal(t, len(msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value), 1)
+ assert.Equal(t, len(msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS), 6)
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[0], uint32(65000))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[1], uint32(4000))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[2], uint32(3000))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[3], uint32(400000))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[4], uint32(300000))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[5], uint32(40001))
+}
+
+// before:
+// as-path : 65000, 4000, 23456, 23456, 40001
+// as4-path : 400000, 300000, 40001, {10, 20, 30}
+// expected result:
+// as-path : 65000, 400000, 300000, 40001, {10, 20, 30}
+func TestAsPathAs4Trans4(t *testing.T) {
+ as := []uint16{65000, 4000, bgp.AS_TRANS, bgp.AS_TRANS, 40001}
+ params := []bgp.AsPathParamInterface{bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, as)}
+ aspath := bgp.NewPathAttributeAsPath(params)
+
+ as4 := []uint32{400000, 300000, 40001}
+ as4param1 := bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, as4)
+ as5 := []uint32{10, 20, 30}
+ as4param2 := bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SET, as5)
+ param4s := []*bgp.As4PathParam{as4param1, as4param2}
+ as4path := bgp.NewPathAttributeAs4Path(param4s)
+ msg := bgp.NewBGPUpdateMessage(nil, []bgp.PathAttributeInterface{aspath, as4path}, nil).Body.(*bgp.BGPUpdate)
+ UpdatePathAttrs4ByteAs(msg)
+ assert.Equal(t, len(msg.PathAttributes), 1)
+ assert.Equal(t, len(msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value), 2)
+ assert.Equal(t, len(msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS), 4)
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[0], uint32(65000))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[1], uint32(400000))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[2], uint32(300000))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[3], uint32(40001))
+ assert.Equal(t, len(msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[1].(*bgp.As4PathParam).AS), 3)
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[1].(*bgp.As4PathParam).AS[0], uint32(10))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[1].(*bgp.As4PathParam).AS[1], uint32(20))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[1].(*bgp.As4PathParam).AS[2], uint32(30))
+}
+
+// before:
+// as-path : 65000, 4000, 23456, 23456, 40001
+// as4-path : {10, 20, 30} 400000, 300000, 40001
+// expected result:
+// as-path : 65000, {10, 20, 30}, 400000, 300000, 40001
+func TestAsPathAs4Trans5(t *testing.T) {
+ as := []uint16{65000, 4000, bgp.AS_TRANS, bgp.AS_TRANS, 40001}
+ params := []bgp.AsPathParamInterface{bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, as)}
+ aspath := bgp.NewPathAttributeAsPath(params)
+
+ as4 := []uint32{400000, 300000, 40001}
+ as4param1 := bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, as4)
+ as5 := []uint32{10, 20, 30}
+ as4param2 := bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SET, as5)
+ param4s := []*bgp.As4PathParam{as4param2, as4param1}
+ as4path := bgp.NewPathAttributeAs4Path(param4s)
+ msg := bgp.NewBGPUpdateMessage(nil, []bgp.PathAttributeInterface{aspath, as4path}, nil).Body.(*bgp.BGPUpdate)
+ UpdatePathAttrs4ByteAs(msg)
+ assert.Equal(t, len(msg.PathAttributes), 1)
+ assert.Equal(t, len(msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value), 3)
+ assert.Equal(t, len(msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS), 1)
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[0], uint32(65000))
+ assert.Equal(t, len(msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[1].(*bgp.As4PathParam).AS), 3)
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[1].(*bgp.As4PathParam).AS[0], uint32(10))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[1].(*bgp.As4PathParam).AS[1], uint32(20))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[1].(*bgp.As4PathParam).AS[2], uint32(30))
+ assert.Equal(t, len(msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[2].(*bgp.As4PathParam).AS), 3)
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[2].(*bgp.As4PathParam).AS[0], uint32(400000))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[2].(*bgp.As4PathParam).AS[1], uint32(300000))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[2].(*bgp.As4PathParam).AS[2], uint32(40001))
+}
+
+// before:
+// as-path : 65000, 4000, 23456, 23456, 40001
+// as4-path : 100000, 65000, 4000, 400000, 300000, 40001
+// expected result:
+// as-path : 65000, 4000, 23456, 23456, 40001
+func TestAsPathAs4TransInvalid1(t *testing.T) {
+ as := []uint16{65000, 4000, bgp.AS_TRANS, bgp.AS_TRANS, 40001}
+ params := []bgp.AsPathParamInterface{bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, as)}
+ aspath := bgp.NewPathAttributeAsPath(params)
+
+ as4 := []uint32{100000, 65000, 4000, 400000, 300000, 40001}
+ as4param1 := bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, as4)
+ param4s := []*bgp.As4PathParam{as4param1}
+ as4path := bgp.NewPathAttributeAs4Path(param4s)
+ msg := bgp.NewBGPUpdateMessage(nil, []bgp.PathAttributeInterface{aspath, as4path}, nil).Body.(*bgp.BGPUpdate)
+ UpdatePathAttrs4ByteAs(msg)
+ assert.Equal(t, len(msg.PathAttributes), 1)
+ assert.Equal(t, len(msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value), 1)
+ assert.Equal(t, len(msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS), 5)
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[0], uint32(65000))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[1], uint32(4000))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[2], uint32(bgp.AS_TRANS))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[3], uint32(bgp.AS_TRANS))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[4], uint32(40001))
+}
+
+// before:
+// as-path : 65000, 4000, 23456, 23456, 40001
+// as4-path : 300000, 40001
+// expected result:
+// as-path : 65000, 4000, 23456, 300000, 40001
+func TestAsPathAs4TransInvalid2(t *testing.T) {
+ as := []uint16{65000, 4000, bgp.AS_TRANS, bgp.AS_TRANS, 40001}
+ params := []bgp.AsPathParamInterface{bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, as)}
+ aspath := bgp.NewPathAttributeAsPath(params)
+
+ as4 := []uint32{300000, 40001}
+ as4param1 := bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, as4)
+ param4s := []*bgp.As4PathParam{as4param1}
+ as4path := bgp.NewPathAttributeAs4Path(param4s)
+ msg := bgp.NewBGPUpdateMessage(nil, []bgp.PathAttributeInterface{aspath, as4path}, nil).Body.(*bgp.BGPUpdate)
+ UpdatePathAttrs4ByteAs(msg)
+ assert.Equal(t, len(msg.PathAttributes), 1)
+ assert.Equal(t, len(msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value), 1)
+ assert.Equal(t, len(msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS), 5)
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[0], uint32(65000))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[1], uint32(4000))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[2], uint32(bgp.AS_TRANS))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[3], uint32(300000))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[4], uint32(40001))
+}
+
+// before:
+// as-path : 65000, 4000, 23456, 23456, 40001
+// as4-path : nil
+// expected result:
+// as-path : 65000, 4000, 23456, 23456, 40001
+func TestAsPathAs4TransInvalid3(t *testing.T) {
+ as := []uint16{65000, 4000, bgp.AS_TRANS, bgp.AS_TRANS, 40001}
+ params := []bgp.AsPathParamInterface{bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, as)}
+ aspath := bgp.NewPathAttributeAsPath(params)
+
+ msg := bgp.NewBGPUpdateMessage(nil, []bgp.PathAttributeInterface{aspath}, nil).Body.(*bgp.BGPUpdate)
+ UpdatePathAttrs4ByteAs(msg)
+ assert.Equal(t, len(msg.PathAttributes), 1)
+ assert.Equal(t, len(msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value), 1)
+ assert.Equal(t, len(msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS), 5)
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[0], uint32(65000))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[1], uint32(4000))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[2], uint32(bgp.AS_TRANS))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[3], uint32(bgp.AS_TRANS))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[4], uint32(40001))
+}
+
+// before:
+// as-path : 65000, 4000, 23456, 23456, 40001
+// as4-path : empty
+// expected result:
+// as-path : 65000, 4000, 23456, 23456, 40001
+func TestAsPathAs4TransInvalid4(t *testing.T) {
+ as := []uint16{65000, 4000, bgp.AS_TRANS, bgp.AS_TRANS, 40001}
+ params := []bgp.AsPathParamInterface{bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, as)}
+ aspath := bgp.NewPathAttributeAsPath(params)
+
+ as4 := []uint32{}
+ as4param1 := bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, as4)
+ param4s := []*bgp.As4PathParam{as4param1}
+ as4path := bgp.NewPathAttributeAs4Path(param4s)
+ msg := bgp.NewBGPUpdateMessage(nil, []bgp.PathAttributeInterface{aspath, as4path}, nil).Body.(*bgp.BGPUpdate)
+ UpdatePathAttrs4ByteAs(msg)
+ assert.Equal(t, len(msg.PathAttributes), 1)
+ assert.Equal(t, len(msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value), 1)
+ assert.Equal(t, len(msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS), 5)
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[0], uint32(65000))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[1], uint32(4000))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[2], uint32(bgp.AS_TRANS))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[3], uint32(bgp.AS_TRANS))
+ assert.Equal(t, msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value[0].(*bgp.As4PathParam).AS[4], uint32(40001))
+}
+
+func TestASPathAs4TransMultipleParams(t *testing.T) {
+ as1 := []uint16{17676, 2914, 174, 50607}
+ as2 := []uint16{bgp.AS_TRANS, bgp.AS_TRANS}
+ params := []bgp.AsPathParamInterface{bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, as1), bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, as2)}
+ aspath := bgp.NewPathAttributeAsPath(params)
+
+ as41 := []uint32{2914, 174, 50607}
+ as42 := []uint32{198035, 198035}
+ as4param1 := bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, as41)
+ as4param2 := bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, as42)
+ param4s := []*bgp.As4PathParam{as4param1, as4param2}
+ as4path := bgp.NewPathAttributeAs4Path(param4s)
+ msg := bgp.NewBGPUpdateMessage(nil, []bgp.PathAttributeInterface{aspath, as4path}, nil).Body.(*bgp.BGPUpdate)
+ UpdatePathAttrs4ByteAs(msg)
+ for _, param := range msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value {
+ p := param.(*bgp.As4PathParam)
+ assert.Equal(t, p.Num, uint8(len(p.AS)))
+ }
+}
+
+func TestASPathAs4TransMultipleLargeParams(t *testing.T) {
+ as1 := make([]uint16, 0, 255)
+ for i := 0; i < 255-5; i++ {
+ as1 = append(as1, uint16(i+1))
+ }
+ as1 = append(as1, []uint16{17676, 2914, 174, 50607}...)
+ as2 := []uint16{bgp.AS_TRANS, bgp.AS_TRANS}
+ params := []bgp.AsPathParamInterface{bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, as1), bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, as2)}
+ aspath := bgp.NewPathAttributeAsPath(params)
+
+ as41 := []uint32{2914, 174, 50607}
+ as42 := []uint32{198035, 198035}
+ as4param1 := bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, as41)
+ as4param2 := bgp.NewAs4PathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, as42)
+ param4s := []*bgp.As4PathParam{as4param1, as4param2}
+ as4path := bgp.NewPathAttributeAs4Path(param4s)
+ msg := bgp.NewBGPUpdateMessage(nil, []bgp.PathAttributeInterface{aspath, as4path}, nil).Body.(*bgp.BGPUpdate)
+ UpdatePathAttrs4ByteAs(msg)
+ for _, param := range msg.PathAttributes[0].(*bgp.PathAttributeAsPath).Value {
+ p := param.(*bgp.As4PathParam)
+ assert.Equal(t, p.Num, uint8(len(p.AS)))
+ }
+}
+
+func TestAggregator4BytesASes(t *testing.T) {
+ getAggr := func(msg *bgp.BGPUpdate) *bgp.PathAttributeAggregator {
+ for _, attr := range msg.PathAttributes {
+ switch attr.(type) {
+ case *bgp.PathAttributeAggregator:
+ return attr.(*bgp.PathAttributeAggregator)
+ }
+ }
+ return nil
+ }
+
+ getAggr4 := func(msg *bgp.BGPUpdate) *bgp.PathAttributeAs4Aggregator {
+ for _, attr := range msg.PathAttributes {
+ switch attr.(type) {
+ case *bgp.PathAttributeAs4Aggregator:
+ return attr.(*bgp.PathAttributeAs4Aggregator)
+ }
+ }
+ return nil
+ }
+
+ addr := "192.168.0.1"
+ as4 := uint32(100000)
+ as := uint32(1000)
+ msg := bgp.NewBGPUpdateMessage(nil, []bgp.PathAttributeInterface{bgp.NewPathAttributeAggregator(as4, addr)}, nil).Body.(*bgp.BGPUpdate)
+
+ // 4byte capable to 4byte capable for 4 bytes AS
+ assert.Equal(t, UpdatePathAggregator4ByteAs(msg), nil)
+ assert.Equal(t, getAggr(msg).Value.AS, as4)
+ assert.Equal(t, getAggr(msg).Value.Address.String(), addr)
+
+ // 4byte capable to 2byte capable for 4 bytes AS
+ UpdatePathAggregator2ByteAs(msg)
+ assert.Equal(t, getAggr(msg).Value.AS, uint32(bgp.AS_TRANS))
+ assert.Equal(t, getAggr(msg).Value.Askind, reflect.Uint16)
+ assert.Equal(t, getAggr4(msg).Value.AS, as4)
+ assert.Equal(t, getAggr4(msg).Value.Address.String(), addr)
+
+ msg = bgp.NewBGPUpdateMessage(nil, []bgp.PathAttributeInterface{bgp.NewPathAttributeAggregator(uint16(bgp.AS_TRANS), addr), bgp.NewPathAttributeAs4Aggregator(as4, addr)}, nil).Body.(*bgp.BGPUpdate)
+ assert.Equal(t, getAggr(msg).Value.AS, uint32(bgp.AS_TRANS))
+ assert.Equal(t, getAggr(msg).Value.Askind, reflect.Uint16)
+
+ // non 4byte capable to 4byte capable for 4 bytes AS
+ assert.Equal(t, UpdatePathAggregator4ByteAs(msg), nil)
+ assert.Equal(t, getAggr(msg).Value.AS, as4)
+ assert.Equal(t, getAggr(msg).Value.Askind, reflect.Uint32)
+ assert.Equal(t, getAggr(msg).Value.Address.String(), addr)
+ assert.Equal(t, getAggr4(msg), (*bgp.PathAttributeAs4Aggregator)(nil))
+
+ // non 4byte capable to non 4byte capable for 4 bytes AS
+ UpdatePathAggregator2ByteAs(msg)
+ assert.Equal(t, getAggr(msg).Value.AS, uint32(bgp.AS_TRANS))
+ assert.Equal(t, getAggr(msg).Value.Askind, reflect.Uint16)
+ assert.Equal(t, getAggr4(msg).Value.AS, as4)
+ assert.Equal(t, getAggr4(msg).Value.Address.String(), addr)
+
+ msg = bgp.NewBGPUpdateMessage(nil, []bgp.PathAttributeInterface{bgp.NewPathAttributeAggregator(uint32(as), addr)}, nil).Body.(*bgp.BGPUpdate)
+ // 4byte capable to 4byte capable for 2 bytes AS
+ assert.Equal(t, getAggr(msg).Value.AS, as)
+ assert.Equal(t, getAggr(msg).Value.Askind, reflect.Uint32)
+ assert.Equal(t, UpdatePathAggregator4ByteAs(msg), nil)
+ assert.Equal(t, getAggr(msg).Value.AS, as)
+ assert.Equal(t, getAggr(msg).Value.Askind, reflect.Uint32)
+
+ // 4byte capable to non 4byte capable for 2 bytes AS
+ UpdatePathAggregator2ByteAs(msg)
+ assert.Equal(t, getAggr4(msg), (*bgp.PathAttributeAs4Aggregator)(nil))
+ assert.Equal(t, getAggr(msg).Value.Askind, reflect.Uint16)
+ assert.Equal(t, getAggr(msg).Value.AS, as)
+
+ msg = bgp.NewBGPUpdateMessage(nil, []bgp.PathAttributeInterface{bgp.NewPathAttributeAggregator(uint16(as), addr)}, nil).Body.(*bgp.BGPUpdate)
+ // non 4byte capable to 4byte capable for 2 bytes AS
+ assert.Equal(t, getAggr(msg).Value.AS, as)
+ assert.Equal(t, getAggr(msg).Value.Askind, reflect.Uint16)
+ assert.Equal(t, UpdatePathAggregator4ByteAs(msg), nil)
+
+ assert.Equal(t, getAggr(msg).Value.AS, as)
+ assert.Equal(t, getAggr(msg).Value.Askind, reflect.Uint32)
+
+ // non 4byte capable to non 4byte capable for 2 bytes AS
+ UpdatePathAggregator2ByteAs(msg)
+ assert.Equal(t, getAggr(msg).Value.AS, as)
+ assert.Equal(t, getAggr(msg).Value.Askind, reflect.Uint16)
+ assert.Equal(t, getAggr4(msg), (*bgp.PathAttributeAs4Aggregator)(nil))
+}
+
+func TestBMP(t *testing.T) {
+ aspath1 := []bgp.AsPathParamInterface{
+ bgp.NewAs4PathParam(2, []uint32{1000000}),
+ bgp.NewAs4PathParam(1, []uint32{1000001, 1002}),
+ bgp.NewAs4PathParam(2, []uint32{1003, 100004}),
+ }
+ mp_nlri := []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(100,
+ "fe80:1234:1234:5667:8967:af12:8912:1023")}
+
+ p := []bgp.PathAttributeInterface{
+ bgp.NewPathAttributeOrigin(3),
+ bgp.NewPathAttributeAsPath(aspath1),
+ bgp.NewPathAttributeMpUnreachNLRI(mp_nlri),
+ }
+ w := []*bgp.IPAddrPrefix{}
+ n := []*bgp.IPAddrPrefix{}
+
+ msg := bgp.NewBGPUpdateMessage(w, p, n)
+ pList := ProcessMessage(msg, peerR1(), time.Now())
+ CreateUpdateMsgFromPaths(pList)
+}
+
+func unreachIndex(msgs []*bgp.BGPMessage) int {
+ for i, _ := range msgs {
+ for _, a := range msgs[i].Body.(*bgp.BGPUpdate).PathAttributes {
+ if a.GetType() == bgp.BGP_ATTR_TYPE_MP_UNREACH_NLRI {
+ return i
+ }
+ }
+ }
+ // should not be here
+ return -1
+}
+
+func TestMixedMPReachMPUnreach(t *testing.T) {
+ aspath1 := []bgp.AsPathParamInterface{
+ bgp.NewAs4PathParam(2, []uint32{100}),
+ }
+ nlri1 := []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(32, "2222::")}
+ nlri2 := []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(32, "1111::")}
+
+ p := []bgp.PathAttributeInterface{
+ bgp.NewPathAttributeOrigin(0),
+ bgp.NewPathAttributeAsPath(aspath1),
+ bgp.NewPathAttributeMpReachNLRI("1::1", nlri1),
+ bgp.NewPathAttributeMpUnreachNLRI(nlri2),
+ }
+ msg := bgp.NewBGPUpdateMessage(nil, p, nil)
+ pList := ProcessMessage(msg, peerR1(), time.Now())
+ assert.Equal(t, len(pList), 2)
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.Equal(t, pList[1].IsWithdraw, true)
+ msgs := CreateUpdateMsgFromPaths(pList)
+ assert.Equal(t, len(msgs), 2)
+
+ uIndex := unreachIndex(msgs)
+ rIndex := 0
+ if uIndex == 0 {
+ rIndex = 1
+ }
+ assert.Equal(t, len(msgs[uIndex].Body.(*bgp.BGPUpdate).PathAttributes), 1)
+ assert.Equal(t, len(msgs[rIndex].Body.(*bgp.BGPUpdate).PathAttributes), 3)
+}
+
+func TestMixedNLRIAndMPUnreach(t *testing.T) {
+ aspath1 := []bgp.AsPathParamInterface{
+ bgp.NewAs4PathParam(2, []uint32{100}),
+ }
+ nlri1 := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.0.0.0")}
+ nlri2 := []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(32, "1111::")}
+
+ p := []bgp.PathAttributeInterface{
+ bgp.NewPathAttributeOrigin(0),
+ bgp.NewPathAttributeAsPath(aspath1),
+ bgp.NewPathAttributeNextHop("1.1.1.1"),
+ bgp.NewPathAttributeMpUnreachNLRI(nlri2),
+ }
+ msg := bgp.NewBGPUpdateMessage(nil, p, nlri1)
+ pList := ProcessMessage(msg, peerR1(), time.Now())
+
+ assert.Equal(t, len(pList), 2)
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.Equal(t, pList[1].IsWithdraw, true)
+ msgs := CreateUpdateMsgFromPaths(pList)
+ assert.Equal(t, len(msgs), 2)
+
+ uIndex := unreachIndex(msgs)
+ rIndex := 0
+ if uIndex == 0 {
+ rIndex = 1
+ }
+ assert.Equal(t, len(msgs[uIndex].Body.(*bgp.BGPUpdate).PathAttributes), 1)
+ assert.Equal(t, len(msgs[rIndex].Body.(*bgp.BGPUpdate).PathAttributes), 3)
+}
+
+func TestMergeV4NLRIs(t *testing.T) {
+ aspath1 := []bgp.AsPathParamInterface{
+ bgp.NewAs4PathParam(2, []uint32{100}),
+ }
+ attrs := []bgp.PathAttributeInterface{
+ bgp.NewPathAttributeOrigin(0),
+ bgp.NewPathAttributeAsPath(aspath1),
+ bgp.NewPathAttributeNextHop("1.1.1.1"),
+ }
+
+ nr := 1024
+ paths := make([]*Path, 0, nr)
+ addrs := make([]string, 0, nr)
+ for i := 0; i < nr; i++ {
+ addrs = append(addrs, fmt.Sprintf("1.1.%d.%d", i>>8&0xff, i&0xff))
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(32, addrs[i])}
+ msg := bgp.NewBGPUpdateMessage(nil, attrs, nlri)
+ paths = append(paths, ProcessMessage(msg, peerR1(), time.Now())...)
+ }
+ msgs := CreateUpdateMsgFromPaths(paths)
+ assert.Equal(t, len(msgs), 2)
+
+ l := make([]*bgp.IPAddrPrefix, 0, nr)
+ for _, msg := range msgs {
+ u := msg.Body.(*bgp.BGPUpdate)
+ assert.Equal(t, len(u.PathAttributes), 3)
+ l = append(l, u.NLRI...)
+ }
+
+ assert.Equal(t, len(l), nr)
+ for i, addr := range addrs {
+ assert.Equal(t, addr, l[i].Prefix.String())
+ }
+ for _, msg := range msgs {
+ d, _ := msg.Serialize()
+ assert.True(t, len(d) < bgp.BGP_MAX_MESSAGE_LENGTH)
+ }
+}
+
+func TestNotMergeV4NLRIs(t *testing.T) {
+ paths := make([]*Path, 0, 2)
+
+ aspath1 := []bgp.AsPathParamInterface{
+ bgp.NewAs4PathParam(2, []uint32{100}),
+ }
+ attrs1 := []bgp.PathAttributeInterface{
+ bgp.NewPathAttributeOrigin(0),
+ bgp.NewPathAttributeAsPath(aspath1),
+ bgp.NewPathAttributeNextHop("1.1.1.1"),
+ }
+ nlri1 := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(32, "1.1.1.1")}
+ paths = append(paths, ProcessMessage(bgp.NewBGPUpdateMessage(nil, attrs1, nlri1), peerR1(), time.Now())...)
+
+ attrs2 := []bgp.PathAttributeInterface{
+ bgp.NewPathAttributeOrigin(0),
+ bgp.NewPathAttributeAsPath(aspath1),
+ bgp.NewPathAttributeNextHop("2.2.2.2"),
+ }
+ nlri2 := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(32, "2.2.2.2")}
+ paths = append(paths, ProcessMessage(bgp.NewBGPUpdateMessage(nil, attrs2, nlri2), peerR1(), time.Now())...)
+
+ assert.NotEmpty(t, paths[0].GetHash(), paths[1].GetHash())
+
+ msgs := CreateUpdateMsgFromPaths(paths)
+ assert.Equal(t, len(msgs), 2)
+
+ paths[1].SetHash(paths[0].GetHash())
+ msgs = CreateUpdateMsgFromPaths(paths)
+ assert.Equal(t, len(msgs), 2)
+}
+
+func TestMergeV4Withdraw(t *testing.T) {
+ nr := 1024
+ paths := make([]*Path, 0, nr)
+ addrs := make([]string, 0, nr)
+ for i := 0; i < nr; i++ {
+ addrs = append(addrs, fmt.Sprintf("1.1.%d.%d", i>>8&0xff, i&0xff))
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(32, addrs[i])}
+ // use different attribute for each nlri
+ aspath1 := []bgp.AsPathParamInterface{
+ bgp.NewAs4PathParam(2, []uint32{uint32(i)}),
+ }
+ attrs := []bgp.PathAttributeInterface{
+ bgp.NewPathAttributeOrigin(0),
+ bgp.NewPathAttributeAsPath(aspath1),
+ bgp.NewPathAttributeNextHop("1.1.1.1"),
+ }
+ msg := bgp.NewBGPUpdateMessage(nlri, attrs, nil)
+ paths = append(paths, ProcessMessage(msg, peerR1(), time.Now())...)
+ }
+ msgs := CreateUpdateMsgFromPaths(paths)
+ assert.Equal(t, len(msgs), 2)
+
+ l := make([]*bgp.IPAddrPrefix, 0, nr)
+ for _, msg := range msgs {
+ u := msg.Body.(*bgp.BGPUpdate)
+ assert.Equal(t, len(u.PathAttributes), 0)
+ l = append(l, u.WithdrawnRoutes...)
+ }
+ assert.Equal(t, len(l), nr)
+ for i, addr := range addrs {
+ assert.Equal(t, addr, l[i].Prefix.String())
+ }
+
+ for _, msg := range msgs {
+ d, _ := msg.Serialize()
+ assert.True(t, len(d) < bgp.BGP_MAX_MESSAGE_LENGTH)
+ }
+}
diff --git a/internal/pkg/table/path.go b/internal/pkg/table/path.go
new file mode 100644
index 00000000..14bbe6ae
--- /dev/null
+++ b/internal/pkg/table/path.go
@@ -0,0 +1,1179 @@
+// Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package table
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "math"
+ "net"
+ "sort"
+ "time"
+
+ "github.com/osrg/gobgp/internal/pkg/config"
+ "github.com/osrg/gobgp/pkg/packet/bgp"
+
+ log "github.com/sirupsen/logrus"
+)
+
+const (
+ DEFAULT_LOCAL_PREF = 100
+)
+
+type Bitmap struct {
+ bitmap []uint64
+}
+
+func (b *Bitmap) Flag(i uint) {
+ b.bitmap[i/64] |= 1 << uint(i%64)
+}
+
+func (b *Bitmap) Unflag(i uint) {
+ b.bitmap[i/64] &^= 1 << uint(i%64)
+}
+
+func (b *Bitmap) GetFlag(i uint) bool {
+ return b.bitmap[i/64]&(1<<uint(i%64)) > 0
+}
+
+func (b *Bitmap) FindandSetZeroBit() (uint, error) {
+ for i := 0; i < len(b.bitmap); i++ {
+ if b.bitmap[i] == math.MaxUint64 {
+ continue
+ }
+ // replace this with TrailingZero64() when gobgp drops go 1.8 support.
+ for j := 0; j < 64; j++ {
+ v := ^b.bitmap[i]
+ if v&(1<<uint64(j)) > 0 {
+ r := i*64 + j
+ b.Flag(uint(r))
+ return uint(r), nil
+ }
+ }
+ }
+ return 0, fmt.Errorf("no space")
+}
+
+func (b *Bitmap) Expand() {
+ old := b.bitmap
+ new := make([]uint64, len(old)+1)
+ for i := 0; i < len(old); i++ {
+ new[i] = old[i]
+ }
+ b.bitmap = new
+}
+
+func NewBitmap(size int) *Bitmap {
+ b := &Bitmap{}
+ if size != 0 {
+ b.bitmap = make([]uint64, (size+64-1)/64)
+ }
+ return b
+}
+
+type originInfo struct {
+ nlri bgp.AddrPrefixInterface
+ source *PeerInfo
+ timestamp int64
+ validation *Validation
+ noImplicitWithdraw bool
+ isFromExternal bool
+ eor bool
+ stale bool
+}
+
+type RpkiValidationReasonType string
+
+const (
+ RPKI_VALIDATION_REASON_TYPE_NONE RpkiValidationReasonType = "none"
+ RPKI_VALIDATION_REASON_TYPE_AS RpkiValidationReasonType = "as"
+ RPKI_VALIDATION_REASON_TYPE_LENGTH RpkiValidationReasonType = "length"
+)
+
+var RpkiValidationReasonTypeToIntMap = map[RpkiValidationReasonType]int{
+ RPKI_VALIDATION_REASON_TYPE_NONE: 0,
+ RPKI_VALIDATION_REASON_TYPE_AS: 1,
+ RPKI_VALIDATION_REASON_TYPE_LENGTH: 2,
+}
+
+func (v RpkiValidationReasonType) ToInt() int {
+ i, ok := RpkiValidationReasonTypeToIntMap[v]
+ if !ok {
+ return -1
+ }
+ return i
+}
+
+var IntToRpkiValidationReasonTypeMap = map[int]RpkiValidationReasonType{
+ 0: RPKI_VALIDATION_REASON_TYPE_NONE,
+ 1: RPKI_VALIDATION_REASON_TYPE_AS,
+ 2: RPKI_VALIDATION_REASON_TYPE_LENGTH,
+}
+
+type Validation struct {
+ Status config.RpkiValidationResultType
+ Reason RpkiValidationReasonType
+ Matched []*ROA
+ UnmatchedAs []*ROA
+ UnmatchedLength []*ROA
+}
+
+type Path struct {
+ info *originInfo
+ parent *Path
+ pathAttrs []bgp.PathAttributeInterface
+ dels []bgp.BGPAttrType
+ attrsHash uint32
+ aslooped bool
+ reason BestPathReason
+
+ // For BGP Nexthop Tracking, this field shows if nexthop is invalidated by IGP.
+ IsNexthopInvalid bool
+ IsWithdraw bool
+}
+
+func NewPath(source *PeerInfo, nlri bgp.AddrPrefixInterface, isWithdraw bool, pattrs []bgp.PathAttributeInterface, timestamp time.Time, noImplicitWithdraw bool) *Path {
+ if !isWithdraw && pattrs == nil {
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ "Key": nlri.String(),
+ }).Error("Need to provide path attributes for non-withdrawn path.")
+ return nil
+ }
+
+ return &Path{
+ info: &originInfo{
+ nlri: nlri,
+ source: source,
+ timestamp: timestamp.Unix(),
+ noImplicitWithdraw: noImplicitWithdraw,
+ },
+ IsWithdraw: isWithdraw,
+ pathAttrs: pattrs,
+ }
+}
+
+func NewEOR(family bgp.RouteFamily) *Path {
+ afi, safi := bgp.RouteFamilyToAfiSafi(family)
+ nlri, _ := bgp.NewPrefixFromRouteFamily(afi, safi)
+ return &Path{
+ info: &originInfo{
+ nlri: nlri,
+ eor: true,
+ },
+ }
+}
+
+func (path *Path) IsEOR() bool {
+ if path.info != nil && path.info.eor {
+ return true
+ }
+ return false
+}
+
+func cloneAsPath(asAttr *bgp.PathAttributeAsPath) *bgp.PathAttributeAsPath {
+ newASparams := make([]bgp.AsPathParamInterface, len(asAttr.Value))
+ for i, param := range asAttr.Value {
+ asList := param.GetAS()
+ as := make([]uint32, len(asList))
+ copy(as, asList)
+ newASparams[i] = bgp.NewAs4PathParam(param.GetType(), as)
+ }
+ return bgp.NewPathAttributeAsPath(newASparams)
+}
+
+func UpdatePathAttrs(global *config.Global, peer *config.Neighbor, info *PeerInfo, original *Path) *Path {
+ if peer.RouteServer.Config.RouteServerClient {
+ return original
+ }
+ path := original.Clone(original.IsWithdraw)
+
+ for _, a := range path.GetPathAttrs() {
+ if _, y := bgp.PathAttrFlags[a.GetType()]; !y {
+ if a.GetFlags()&bgp.BGP_ATTR_FLAG_TRANSITIVE == 0 {
+ path.delPathAttr(a.GetType())
+ }
+ } else {
+ switch a.GetType() {
+ case bgp.BGP_ATTR_TYPE_CLUSTER_LIST, bgp.BGP_ATTR_TYPE_ORIGINATOR_ID:
+ if !(peer.State.PeerType == config.PEER_TYPE_INTERNAL && peer.RouteReflector.Config.RouteReflectorClient) {
+ // send these attributes to only rr clients
+ path.delPathAttr(a.GetType())
+ }
+ }
+ }
+ }
+
+ localAddress := info.LocalAddress
+ nexthop := path.GetNexthop()
+ if peer.State.PeerType == config.PEER_TYPE_EXTERNAL {
+ // NEXTHOP handling
+ if !path.IsLocal() || nexthop.IsUnspecified() {
+ path.SetNexthop(localAddress)
+ }
+
+ // remove-private-as handling
+ path.RemovePrivateAS(peer.Config.LocalAs, peer.State.RemovePrivateAs)
+
+ // AS_PATH handling
+ confed := peer.IsConfederationMember(global)
+ path.PrependAsn(peer.Config.LocalAs, 1, confed)
+ if !confed {
+ path.removeConfedAs()
+ }
+
+ // MED Handling
+ if med := path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC); med != nil && !path.IsLocal() {
+ path.delPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
+ }
+
+ } else if peer.State.PeerType == config.PEER_TYPE_INTERNAL {
+ // NEXTHOP handling for iBGP
+ // if the path generated locally set local address as nexthop.
+ // if not, don't modify it.
+ // TODO: NEXT-HOP-SELF support
+ if path.IsLocal() && nexthop.IsUnspecified() {
+ path.SetNexthop(localAddress)
+ }
+
+ // AS_PATH handling for iBGP
+ // if the path has AS_PATH path attribute, don't modify it.
+ // if not, attach *empty* AS_PATH path attribute.
+ if nh := path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH); nh == nil {
+ path.PrependAsn(0, 0, false)
+ }
+
+ // For iBGP peers we are required to send local-pref attribute
+ // for connected or local prefixes.
+ // We set default local-pref 100.
+ if pref := path.getPathAttr(bgp.BGP_ATTR_TYPE_LOCAL_PREF); pref == nil {
+ path.setPathAttr(bgp.NewPathAttributeLocalPref(DEFAULT_LOCAL_PREF))
+ }
+
+ // RFC4456: BGP Route Reflection
+ // 8. Avoiding Routing Information Loops
+ info := path.GetSource()
+ if peer.RouteReflector.Config.RouteReflectorClient {
+ // This attribute will carry the BGP Identifier of the originator of the route in the local AS.
+ // A BGP speaker SHOULD NOT create an ORIGINATOR_ID attribute if one already exists.
+ //
+ // RFC4684 3.2 Intra-AS VPN Route Distribution
+ // When advertising RT membership NLRI to a route-reflector client,
+ // the Originator attribute shall be set to the router-id of the
+ // advertiser, and the Next-hop attribute shall be set of the local
+ // address for that session.
+ if path.GetRouteFamily() == bgp.RF_RTC_UC {
+ path.SetNexthop(localAddress)
+ path.setPathAttr(bgp.NewPathAttributeOriginatorId(info.LocalID.String()))
+ } else if path.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGINATOR_ID) == nil {
+ if path.IsLocal() {
+ path.setPathAttr(bgp.NewPathAttributeOriginatorId(global.Config.RouterId))
+ } else {
+ path.setPathAttr(bgp.NewPathAttributeOriginatorId(info.ID.String()))
+ }
+ }
+ // When an RR reflects a route, it MUST prepend the local CLUSTER_ID to the CLUSTER_LIST.
+ // If the CLUSTER_LIST is empty, it MUST create a new one.
+ clusterID := string(peer.RouteReflector.State.RouteReflectorClusterId)
+ if p := path.getPathAttr(bgp.BGP_ATTR_TYPE_CLUSTER_LIST); p == nil {
+ path.setPathAttr(bgp.NewPathAttributeClusterList([]string{clusterID}))
+ } else {
+ clusterList := p.(*bgp.PathAttributeClusterList)
+ newClusterList := make([]string, 0, len(clusterList.Value))
+ for _, ip := range clusterList.Value {
+ newClusterList = append(newClusterList, ip.String())
+ }
+ path.setPathAttr(bgp.NewPathAttributeClusterList(append([]string{clusterID}, newClusterList...)))
+ }
+ }
+
+ } else {
+ log.WithFields(log.Fields{
+ "Topic": "Peer",
+ "Key": peer.State.NeighborAddress,
+ }).Warnf("invalid peer type: %d", peer.State.PeerType)
+ }
+ return path
+}
+
+func (path *Path) GetTimestamp() time.Time {
+ return time.Unix(path.OriginInfo().timestamp, 0)
+}
+
+func (path *Path) setTimestamp(t time.Time) {
+ path.OriginInfo().timestamp = t.Unix()
+}
+
+func (path *Path) IsLocal() bool {
+ return path.GetSource().Address == nil
+}
+
+func (path *Path) IsIBGP() bool {
+ return path.GetSource().AS == path.GetSource().LocalAS
+}
+
+// create new PathAttributes
+func (path *Path) Clone(isWithdraw bool) *Path {
+ return &Path{
+ parent: path,
+ IsWithdraw: isWithdraw,
+ IsNexthopInvalid: path.IsNexthopInvalid,
+ attrsHash: path.attrsHash,
+ }
+}
+
+func (path *Path) root() *Path {
+ p := path
+ for p.parent != nil {
+ p = p.parent
+ }
+ return p
+}
+
+func (path *Path) OriginInfo() *originInfo {
+ return path.root().info
+}
+
+func (path *Path) NoImplicitWithdraw() bool {
+ return path.OriginInfo().noImplicitWithdraw
+}
+
+func (path *Path) Validation() *Validation {
+ return path.OriginInfo().validation
+}
+
+func (path *Path) ValidationStatus() config.RpkiValidationResultType {
+ if v := path.OriginInfo().validation; v != nil {
+ return v.Status
+ } else {
+ return config.RPKI_VALIDATION_RESULT_TYPE_NONE
+ }
+}
+
+func (path *Path) SetValidation(v *Validation) {
+ path.OriginInfo().validation = v
+}
+
+func (path *Path) IsFromExternal() bool {
+ return path.OriginInfo().isFromExternal
+}
+
+func (path *Path) SetIsFromExternal(y bool) {
+ path.OriginInfo().isFromExternal = y
+}
+
+func (path *Path) GetRouteFamily() bgp.RouteFamily {
+ return bgp.AfiSafiToRouteFamily(path.OriginInfo().nlri.AFI(), path.OriginInfo().nlri.SAFI())
+}
+
+func (path *Path) SetSource(source *PeerInfo) {
+ path.OriginInfo().source = source
+}
+func (path *Path) GetSource() *PeerInfo {
+ return path.OriginInfo().source
+}
+
+func (path *Path) MarkStale(s bool) {
+ path.OriginInfo().stale = s
+}
+
+func (path *Path) IsStale() bool {
+ return path.OriginInfo().stale
+}
+
+func (path *Path) IsAsLooped() bool {
+ return path.aslooped
+}
+
+func (path *Path) SetAsLooped(y bool) {
+ path.aslooped = y
+}
+
+func (path *Path) IsLLGRStale() bool {
+ for _, c := range path.GetCommunities() {
+ if c == uint32(bgp.COMMUNITY_LLGR_STALE) {
+ return true
+ }
+ }
+ return false
+}
+
+func (path *Path) GetSourceAs() uint32 {
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
+ if attr != nil {
+ asPathParam := attr.(*bgp.PathAttributeAsPath).Value
+ if len(asPathParam) == 0 {
+ return 0
+ }
+ asList := asPathParam[len(asPathParam)-1].GetAS()
+ if len(asList) == 0 {
+ return 0
+ }
+ return asList[len(asList)-1]
+ }
+ return 0
+}
+
+func (path *Path) GetNexthop() net.IP {
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP)
+ if attr != nil {
+ return attr.(*bgp.PathAttributeNextHop).Value
+ }
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI)
+ if attr != nil {
+ return attr.(*bgp.PathAttributeMpReachNLRI).Nexthop
+ }
+ return net.IP{}
+}
+
+func (path *Path) SetNexthop(nexthop net.IP) {
+ if path.GetRouteFamily() == bgp.RF_IPv4_UC && nexthop.To4() == nil {
+ path.delPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP)
+ mpreach := bgp.NewPathAttributeMpReachNLRI(nexthop.String(), []bgp.AddrPrefixInterface{path.GetNlri()})
+ path.setPathAttr(mpreach)
+ return
+ }
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP)
+ if attr != nil {
+ path.setPathAttr(bgp.NewPathAttributeNextHop(nexthop.String()))
+ }
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI)
+ if attr != nil {
+ oldNlri := attr.(*bgp.PathAttributeMpReachNLRI)
+ path.setPathAttr(bgp.NewPathAttributeMpReachNLRI(nexthop.String(), oldNlri.Value))
+ }
+}
+
+func (path *Path) GetNlri() bgp.AddrPrefixInterface {
+ return path.OriginInfo().nlri
+}
+
+type PathAttrs []bgp.PathAttributeInterface
+
+func (a PathAttrs) Len() int {
+ return len(a)
+}
+
+func (a PathAttrs) Swap(i, j int) {
+ a[i], a[j] = a[j], a[i]
+}
+
+func (a PathAttrs) Less(i, j int) bool {
+ return a[i].GetType() < a[j].GetType()
+}
+
+func (path *Path) GetPathAttrs() []bgp.PathAttributeInterface {
+ deleted := NewBitmap(math.MaxUint8)
+ modified := make(map[uint]bgp.PathAttributeInterface)
+ p := path
+ for {
+ for _, t := range p.dels {
+ deleted.Flag(uint(t))
+ }
+ if p.parent == nil {
+ list := PathAttrs(make([]bgp.PathAttributeInterface, 0, len(p.pathAttrs)))
+ // we assume that the original pathAttrs are
+ // in order, that is, other bgp speakers send
+ // attributes in order.
+ for _, a := range p.pathAttrs {
+ typ := uint(a.GetType())
+ if m, ok := modified[typ]; ok {
+ list = append(list, m)
+ delete(modified, typ)
+ } else if !deleted.GetFlag(typ) {
+ list = append(list, a)
+ }
+ }
+ if len(modified) > 0 {
+ // Huh, some attributes were newly
+ // added. So we need to sort...
+ for _, m := range modified {
+ list = append(list, m)
+ }
+ sort.Sort(list)
+ }
+ return list
+ } else {
+ for _, a := range p.pathAttrs {
+ typ := uint(a.GetType())
+ if _, ok := modified[typ]; !deleted.GetFlag(typ) && !ok {
+ modified[typ] = a
+ }
+ }
+ }
+ p = p.parent
+ }
+}
+
+func (path *Path) getPathAttr(typ bgp.BGPAttrType) bgp.PathAttributeInterface {
+ p := path
+ for {
+ for _, t := range p.dels {
+ if t == typ {
+ return nil
+ }
+ }
+ for _, a := range p.pathAttrs {
+ if a.GetType() == typ {
+ return a
+ }
+ }
+ if p.parent == nil {
+ return nil
+ }
+ p = p.parent
+ }
+}
+
+func (path *Path) setPathAttr(a bgp.PathAttributeInterface) {
+ if len(path.pathAttrs) == 0 {
+ path.pathAttrs = []bgp.PathAttributeInterface{a}
+ } else {
+ for i, b := range path.pathAttrs {
+ if a.GetType() == b.GetType() {
+ path.pathAttrs[i] = a
+ return
+ }
+ }
+ path.pathAttrs = append(path.pathAttrs, a)
+ }
+}
+
+func (path *Path) delPathAttr(typ bgp.BGPAttrType) {
+ if len(path.dels) == 0 {
+ path.dels = []bgp.BGPAttrType{typ}
+ } else {
+ path.dels = append(path.dels, typ)
+ }
+}
+
+// return Path's string representation
+func (path *Path) String() string {
+ s := bytes.NewBuffer(make([]byte, 0, 64))
+ if path.IsEOR() {
+ s.WriteString(fmt.Sprintf("{ %s EOR | src: %s }", path.GetRouteFamily(), path.GetSource()))
+ return s.String()
+ }
+ s.WriteString(fmt.Sprintf("{ %s | ", path.getPrefix()))
+ s.WriteString(fmt.Sprintf("src: %s", path.GetSource()))
+ s.WriteString(fmt.Sprintf(", nh: %s", path.GetNexthop()))
+ if path.IsNexthopInvalid {
+ s.WriteString(" (not reachable)")
+ }
+ if path.IsWithdraw {
+ s.WriteString(", withdraw")
+ }
+ s.WriteString(" }")
+ return s.String()
+}
+
+func (path *Path) getPrefix() string {
+ return path.GetNlri().String()
+}
+
+func (path *Path) GetAsPath() *bgp.PathAttributeAsPath {
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
+ if attr != nil {
+ return attr.(*bgp.PathAttributeAsPath)
+ }
+ return nil
+}
+
+// GetAsPathLen returns the number of AS_PATH
+func (path *Path) GetAsPathLen() int {
+
+ var length int = 0
+ if aspath := path.GetAsPath(); aspath != nil {
+ for _, as := range aspath.Value {
+ length += as.ASLen()
+ }
+ }
+ return length
+}
+
+func (path *Path) GetAsString() string {
+ s := bytes.NewBuffer(make([]byte, 0, 64))
+ if aspath := path.GetAsPath(); aspath != nil {
+ return bgp.AsPathString(aspath)
+ }
+ return s.String()
+}
+
+func (path *Path) GetAsList() []uint32 {
+ return path.getAsListOfSpecificType(true, true)
+
+}
+
+func (path *Path) GetAsSeqList() []uint32 {
+ return path.getAsListOfSpecificType(true, false)
+
+}
+
+func (path *Path) getAsListOfSpecificType(getAsSeq, getAsSet bool) []uint32 {
+ asList := []uint32{}
+ if aspath := path.GetAsPath(); aspath != nil {
+ for _, param := range aspath.Value {
+ segType := param.GetType()
+ if getAsSeq && segType == bgp.BGP_ASPATH_ATTR_TYPE_SEQ {
+ asList = append(asList, param.GetAS()...)
+ continue
+ }
+ if getAsSet && segType == bgp.BGP_ASPATH_ATTR_TYPE_SET {
+ asList = append(asList, param.GetAS()...)
+ } else {
+ asList = append(asList, 0)
+ }
+ }
+ }
+ return asList
+}
+
+func (path *Path) GetLabelString() string {
+ return bgp.LabelString(path.GetNlri())
+}
+
+// PrependAsn prepends AS number.
+// This function updates the AS_PATH attribute as follows.
+// (If the peer is in the confederation member AS,
+// replace AS_SEQUENCE in the following sentence with AS_CONFED_SEQUENCE.)
+// 1) if the first path segment of the AS_PATH is of type
+// AS_SEQUENCE, the local system prepends the specified AS num as
+// the last element of the sequence (put it in the left-most
+// position with respect to the position of octets in the
+// protocol message) the specified number of times.
+// If the act of prepending will cause an overflow in the AS_PATH
+// segment (i.e., more than 255 ASes),
+// it SHOULD prepend a new segment of type AS_SEQUENCE
+// and prepend its own AS number to this new segment.
+//
+// 2) if the first path segment of the AS_PATH is of other than type
+// AS_SEQUENCE, the local system prepends a new path segment of type
+// AS_SEQUENCE to the AS_PATH, including the specified AS number in
+// that segment.
+//
+// 3) if the AS_PATH is empty, the local system creates a path
+// segment of type AS_SEQUENCE, places the specified AS number
+// into that segment, and places that segment into the AS_PATH.
+func (path *Path) PrependAsn(asn uint32, repeat uint8, confed bool) {
+ var segType uint8
+ if confed {
+ segType = bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ
+ } else {
+ segType = bgp.BGP_ASPATH_ATTR_TYPE_SEQ
+ }
+
+ original := path.GetAsPath()
+
+ asns := make([]uint32, repeat)
+ for i := range asns {
+ asns[i] = asn
+ }
+
+ var asPath *bgp.PathAttributeAsPath
+ if original == nil {
+ asPath = bgp.NewPathAttributeAsPath([]bgp.AsPathParamInterface{})
+ } else {
+ asPath = cloneAsPath(original)
+ }
+
+ if len(asPath.Value) > 0 {
+ param := asPath.Value[0]
+ asList := param.GetAS()
+ if param.GetType() == segType {
+ if int(repeat)+len(asList) > 255 {
+ repeat = uint8(255 - len(asList))
+ }
+ newAsList := append(asns[:int(repeat)], asList...)
+ asPath.Value[0] = bgp.NewAs4PathParam(segType, newAsList)
+ asns = asns[int(repeat):]
+ }
+ }
+
+ if len(asns) > 0 {
+ p := bgp.NewAs4PathParam(segType, asns)
+ asPath.Value = append([]bgp.AsPathParamInterface{p}, asPath.Value...)
+ }
+ path.setPathAttr(asPath)
+}
+
+func isPrivateAS(as uint32) bool {
+ return (64512 <= as && as <= 65534) || (4200000000 <= as && as <= 4294967294)
+}
+
+func (path *Path) RemovePrivateAS(localAS uint32, option config.RemovePrivateAsOption) {
+ original := path.GetAsPath()
+ if original == nil {
+ return
+ }
+ switch option {
+ case config.REMOVE_PRIVATE_AS_OPTION_ALL, config.REMOVE_PRIVATE_AS_OPTION_REPLACE:
+ newASParams := make([]bgp.AsPathParamInterface, 0, len(original.Value))
+ for _, param := range original.Value {
+ asList := param.GetAS()
+ newASParam := make([]uint32, 0, len(asList))
+ for _, as := range asList {
+ if isPrivateAS(as) {
+ if option == config.REMOVE_PRIVATE_AS_OPTION_REPLACE {
+ newASParam = append(newASParam, localAS)
+ }
+ } else {
+ newASParam = append(newASParam, as)
+ }
+ }
+ if len(newASParam) > 0 {
+ newASParams = append(newASParams, bgp.NewAs4PathParam(param.GetType(), newASParam))
+ }
+ }
+ path.setPathAttr(bgp.NewPathAttributeAsPath(newASParams))
+ }
+}
+
+func (path *Path) removeConfedAs() {
+ original := path.GetAsPath()
+ if original == nil {
+ return
+ }
+ newAsParams := make([]bgp.AsPathParamInterface, 0, len(original.Value))
+ for _, param := range original.Value {
+ switch param.GetType() {
+ case bgp.BGP_ASPATH_ATTR_TYPE_SEQ, bgp.BGP_ASPATH_ATTR_TYPE_SET:
+ newAsParams = append(newAsParams, param)
+ }
+ }
+ path.setPathAttr(bgp.NewPathAttributeAsPath(newAsParams))
+}
+
+func (path *Path) ReplaceAS(localAS, peerAS uint32) *Path {
+ original := path.GetAsPath()
+ if original == nil {
+ return path
+ }
+ newASParams := make([]bgp.AsPathParamInterface, 0, len(original.Value))
+ changed := false
+ for _, param := range original.Value {
+ segType := param.GetType()
+ asList := param.GetAS()
+ newASParam := make([]uint32, 0, len(asList))
+ for _, as := range asList {
+ if as == peerAS {
+ as = localAS
+ changed = true
+ }
+ newASParam = append(newASParam, as)
+ }
+ newASParams = append(newASParams, bgp.NewAs4PathParam(segType, newASParam))
+ }
+ if changed {
+ path = path.Clone(path.IsWithdraw)
+ path.setPathAttr(bgp.NewPathAttributeAsPath(newASParams))
+ }
+ return path
+}
+
+func (path *Path) GetCommunities() []uint32 {
+ communityList := []uint32{}
+ if attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_COMMUNITIES); attr != nil {
+ communities := attr.(*bgp.PathAttributeCommunities)
+ communityList = append(communityList, communities.Value...)
+ }
+ return communityList
+}
+
+// SetCommunities adds or replaces communities with new ones.
+// If the length of communities is 0 and doReplace is true, it clears communities.
+func (path *Path) SetCommunities(communities []uint32, doReplace bool) {
+
+ if len(communities) == 0 && doReplace {
+ // clear communities
+ path.delPathAttr(bgp.BGP_ATTR_TYPE_COMMUNITIES)
+ return
+ }
+
+ newList := make([]uint32, 0)
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_COMMUNITIES)
+ if attr != nil {
+ c := attr.(*bgp.PathAttributeCommunities)
+ if doReplace {
+ newList = append(newList, communities...)
+ } else {
+ newList = append(newList, c.Value...)
+ newList = append(newList, communities...)
+ }
+ } else {
+ newList = append(newList, communities...)
+ }
+ path.setPathAttr(bgp.NewPathAttributeCommunities(newList))
+
+}
+
+// RemoveCommunities removes specific communities.
+// If the length of communities is 0, it does nothing.
+// If all communities are removed, it removes Communities path attribute itself.
+func (path *Path) RemoveCommunities(communities []uint32) int {
+
+ if len(communities) == 0 {
+ // do nothing
+ return 0
+ }
+
+ find := func(val uint32) bool {
+ for _, com := range communities {
+ if com == val {
+ return true
+ }
+ }
+ return false
+ }
+
+ count := 0
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_COMMUNITIES)
+ if attr != nil {
+ newList := make([]uint32, 0)
+ c := attr.(*bgp.PathAttributeCommunities)
+
+ for _, value := range c.Value {
+ if find(value) {
+ count += 1
+ } else {
+ newList = append(newList, value)
+ }
+ }
+
+ if len(newList) != 0 {
+ path.setPathAttr(bgp.NewPathAttributeCommunities(newList))
+ } else {
+ path.delPathAttr(bgp.BGP_ATTR_TYPE_COMMUNITIES)
+ }
+ }
+ return count
+}
+
+func (path *Path) GetExtCommunities() []bgp.ExtendedCommunityInterface {
+ eCommunityList := make([]bgp.ExtendedCommunityInterface, 0)
+ if attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_EXTENDED_COMMUNITIES); attr != nil {
+ eCommunities := attr.(*bgp.PathAttributeExtendedCommunities).Value
+ eCommunityList = append(eCommunityList, eCommunities...)
+ }
+ return eCommunityList
+}
+
+func (path *Path) SetExtCommunities(exts []bgp.ExtendedCommunityInterface, doReplace bool) {
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_EXTENDED_COMMUNITIES)
+ if attr != nil {
+ l := attr.(*bgp.PathAttributeExtendedCommunities).Value
+ if doReplace {
+ l = exts
+ } else {
+ l = append(l, exts...)
+ }
+ path.setPathAttr(bgp.NewPathAttributeExtendedCommunities(l))
+ } else {
+ path.setPathAttr(bgp.NewPathAttributeExtendedCommunities(exts))
+ }
+}
+
+func (path *Path) GetLargeCommunities() []*bgp.LargeCommunity {
+ if a := path.getPathAttr(bgp.BGP_ATTR_TYPE_LARGE_COMMUNITY); a != nil {
+ v := a.(*bgp.PathAttributeLargeCommunities).Values
+ ret := make([]*bgp.LargeCommunity, 0, len(v))
+ ret = append(ret, v...)
+ return ret
+ }
+ return nil
+}
+
+func (path *Path) SetLargeCommunities(cs []*bgp.LargeCommunity, doReplace bool) {
+ a := path.getPathAttr(bgp.BGP_ATTR_TYPE_LARGE_COMMUNITY)
+ if a == nil || doReplace {
+ path.setPathAttr(bgp.NewPathAttributeLargeCommunities(cs))
+ } else {
+ l := a.(*bgp.PathAttributeLargeCommunities).Values
+ path.setPathAttr(bgp.NewPathAttributeLargeCommunities(append(l, cs...)))
+ }
+}
+
+func (path *Path) GetMed() (uint32, error) {
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
+ if attr == nil {
+ return 0, fmt.Errorf("no med path attr")
+ }
+ return attr.(*bgp.PathAttributeMultiExitDisc).Value, nil
+}
+
+// SetMed replace, add or subtraction med with new ones.
+func (path *Path) SetMed(med int64, doReplace bool) error {
+ parseMed := func(orgMed uint32, med int64, doReplace bool) (*bgp.PathAttributeMultiExitDisc, error) {
+ if doReplace {
+ return bgp.NewPathAttributeMultiExitDisc(uint32(med)), nil
+ }
+
+ medVal := int64(orgMed) + med
+ if medVal < 0 {
+ return nil, fmt.Errorf("med value invalid. it's underflow threshold: %v", medVal)
+ } else if medVal > int64(math.MaxUint32) {
+ return nil, fmt.Errorf("med value invalid. it's overflow threshold: %v", medVal)
+ }
+
+ return bgp.NewPathAttributeMultiExitDisc(uint32(int64(orgMed) + med)), nil
+ }
+
+ m := uint32(0)
+ if attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC); attr != nil {
+ m = attr.(*bgp.PathAttributeMultiExitDisc).Value
+ }
+ newMed, err := parseMed(m, med, doReplace)
+ if err != nil {
+ return err
+ }
+ path.setPathAttr(newMed)
+ return nil
+}
+
+func (path *Path) RemoveLocalPref() {
+ if path.getPathAttr(bgp.BGP_ATTR_TYPE_LOCAL_PREF) != nil {
+ path.delPathAttr(bgp.BGP_ATTR_TYPE_LOCAL_PREF)
+ }
+}
+
+func (path *Path) GetOriginatorID() net.IP {
+ if attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGINATOR_ID); attr != nil {
+ return attr.(*bgp.PathAttributeOriginatorId).Value
+ }
+ return nil
+}
+
+func (path *Path) GetClusterList() []net.IP {
+ if attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_CLUSTER_LIST); attr != nil {
+ return attr.(*bgp.PathAttributeClusterList).Value
+ }
+ return nil
+}
+
+func (path *Path) GetOrigin() (uint8, error) {
+ if attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN); attr != nil {
+ return attr.(*bgp.PathAttributeOrigin).Value, nil
+ }
+ return 0, fmt.Errorf("no origin path attr")
+}
+
+func (path *Path) GetLocalPref() (uint32, error) {
+ lp := uint32(DEFAULT_LOCAL_PREF)
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_LOCAL_PREF)
+ if attr != nil {
+ lp = attr.(*bgp.PathAttributeLocalPref).Value
+ }
+ return lp, nil
+}
+
+func (lhs *Path) Equal(rhs *Path) bool {
+ if rhs == nil {
+ return false
+ }
+
+ if lhs.GetSource() != rhs.GetSource() {
+ return false
+ }
+
+ pattrs := func(arg []bgp.PathAttributeInterface) []byte {
+ ret := make([]byte, 0)
+ for _, a := range arg {
+ aa, _ := a.Serialize()
+ ret = append(ret, aa...)
+ }
+ return ret
+ }
+ return bytes.Equal(pattrs(lhs.GetPathAttrs()), pattrs(rhs.GetPathAttrs()))
+}
+
+func (path *Path) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ Nlri bgp.AddrPrefixInterface `json:"nlri"`
+ PathAttrs []bgp.PathAttributeInterface `json:"attrs"`
+ Age int64 `json:"age"`
+ Withdrawal bool `json:"withdrawal,omitempty"`
+ Validation string `json:"validation,omitempty"`
+ SourceID net.IP `json:"source-id,omitempty"`
+ NeighborIP net.IP `json:"neighbor-ip,omitempty"`
+ Stale bool `json:"stale,omitempty"`
+ UUID string `json:"uuid,omitempty"`
+ ID uint32 `json:"id,omitempty"`
+ }{
+ Nlri: path.GetNlri(),
+ PathAttrs: path.GetPathAttrs(),
+ Age: path.GetTimestamp().Unix(),
+ Withdrawal: path.IsWithdraw,
+ Validation: string(path.ValidationStatus()),
+ SourceID: path.GetSource().ID,
+ NeighborIP: path.GetSource().Address,
+ Stale: path.IsStale(),
+ ID: path.GetNlri().PathIdentifier(),
+ })
+}
+
+func (lhs *Path) Compare(rhs *Path) int {
+ if lhs.IsLocal() && !rhs.IsLocal() {
+ return 1
+ } else if !lhs.IsLocal() && rhs.IsLocal() {
+ return -1
+ }
+
+ if !lhs.IsIBGP() && rhs.IsIBGP() {
+ return 1
+ } else if lhs.IsIBGP() && !rhs.IsIBGP() {
+ return -1
+ }
+
+ lp1, _ := lhs.GetLocalPref()
+ lp2, _ := rhs.GetLocalPref()
+ if lp1 != lp2 {
+ return int(lp1 - lp2)
+ }
+
+ l1 := lhs.GetAsPathLen()
+ l2 := rhs.GetAsPathLen()
+ if l1 != l2 {
+ return int(l2 - l1)
+ }
+
+ o1, _ := lhs.GetOrigin()
+ o2, _ := rhs.GetOrigin()
+ if o1 != o2 {
+ return int(o2 - o1)
+ }
+
+ m1, _ := lhs.GetMed()
+ m2, _ := rhs.GetMed()
+ return int(m2 - m1)
+}
+
+func (v *Vrf) ToGlobalPath(path *Path) error {
+ nlri := path.GetNlri()
+ switch rf := path.GetRouteFamily(); rf {
+ case bgp.RF_IPv4_UC:
+ n := nlri.(*bgp.IPAddrPrefix)
+ pathIdentifier := path.GetNlri().PathIdentifier()
+ path.OriginInfo().nlri = bgp.NewLabeledVPNIPAddrPrefix(n.Length, n.Prefix.String(), *bgp.NewMPLSLabelStack(0), v.Rd)
+ path.GetNlri().SetPathIdentifier(pathIdentifier)
+ case bgp.RF_IPv6_UC:
+ n := nlri.(*bgp.IPv6AddrPrefix)
+ pathIdentifier := path.GetNlri().PathIdentifier()
+ path.OriginInfo().nlri = bgp.NewLabeledVPNIPv6AddrPrefix(n.Length, n.Prefix.String(), *bgp.NewMPLSLabelStack(0), v.Rd)
+ path.GetNlri().SetPathIdentifier(pathIdentifier)
+ case bgp.RF_EVPN:
+ n := nlri.(*bgp.EVPNNLRI)
+ switch n.RouteType {
+ case bgp.EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT:
+ n.RouteTypeData.(*bgp.EVPNMacIPAdvertisementRoute).RD = v.Rd
+ case bgp.EVPN_INCLUSIVE_MULTICAST_ETHERNET_TAG:
+ n.RouteTypeData.(*bgp.EVPNMulticastEthernetTagRoute).RD = v.Rd
+ }
+ default:
+ return fmt.Errorf("unsupported route family for vrf: %s", rf)
+ }
+ path.SetExtCommunities(v.ExportRt, false)
+ return nil
+}
+
+func (p *Path) ToGlobal(vrf *Vrf) *Path {
+ nlri := p.GetNlri()
+ nh := p.GetNexthop()
+ pathId := nlri.PathIdentifier()
+ switch rf := p.GetRouteFamily(); rf {
+ case bgp.RF_IPv4_UC:
+ n := nlri.(*bgp.IPAddrPrefix)
+ nlri = bgp.NewLabeledVPNIPAddrPrefix(n.Length, n.Prefix.String(), *bgp.NewMPLSLabelStack(0), vrf.Rd)
+ nlri.SetPathIdentifier(pathId)
+ case bgp.RF_IPv6_UC:
+ n := nlri.(*bgp.IPv6AddrPrefix)
+ nlri = bgp.NewLabeledVPNIPv6AddrPrefix(n.Length, n.Prefix.String(), *bgp.NewMPLSLabelStack(0), vrf.Rd)
+ nlri.SetPathIdentifier(pathId)
+ case bgp.RF_EVPN:
+ n := nlri.(*bgp.EVPNNLRI)
+ switch n.RouteType {
+ case bgp.EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT:
+ old := n.RouteTypeData.(*bgp.EVPNMacIPAdvertisementRoute)
+ new := &bgp.EVPNMacIPAdvertisementRoute{
+ RD: vrf.Rd,
+ ESI: old.ESI,
+ ETag: old.ETag,
+ MacAddressLength: old.MacAddressLength,
+ MacAddress: old.MacAddress,
+ IPAddressLength: old.IPAddressLength,
+ IPAddress: old.IPAddress,
+ Labels: old.Labels,
+ }
+ nlri = bgp.NewEVPNNLRI(n.RouteType, new)
+ case bgp.EVPN_INCLUSIVE_MULTICAST_ETHERNET_TAG:
+ old := n.RouteTypeData.(*bgp.EVPNMulticastEthernetTagRoute)
+ new := &bgp.EVPNMulticastEthernetTagRoute{
+ RD: vrf.Rd,
+ ETag: old.ETag,
+ IPAddressLength: old.IPAddressLength,
+ IPAddress: old.IPAddress,
+ }
+ nlri = bgp.NewEVPNNLRI(n.RouteType, new)
+ }
+ default:
+ return p
+ }
+ path := NewPath(p.OriginInfo().source, nlri, p.IsWithdraw, p.GetPathAttrs(), p.GetTimestamp(), false)
+ path.SetExtCommunities(vrf.ExportRt, false)
+ path.delPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP)
+ path.setPathAttr(bgp.NewPathAttributeMpReachNLRI(nh.String(), []bgp.AddrPrefixInterface{nlri}))
+ path.IsNexthopInvalid = p.IsNexthopInvalid
+ return path
+}
+
+func (p *Path) ToLocal() *Path {
+ nlri := p.GetNlri()
+ f := p.GetRouteFamily()
+ pathId := nlri.PathLocalIdentifier()
+ switch f {
+ case bgp.RF_IPv4_VPN:
+ n := nlri.(*bgp.LabeledVPNIPAddrPrefix)
+ _, c, _ := net.ParseCIDR(n.IPPrefix())
+ ones, _ := c.Mask.Size()
+ nlri = bgp.NewIPAddrPrefix(uint8(ones), c.IP.String())
+ nlri.SetPathLocalIdentifier(pathId)
+ case bgp.RF_IPv6_VPN:
+ n := nlri.(*bgp.LabeledVPNIPv6AddrPrefix)
+ _, c, _ := net.ParseCIDR(n.IPPrefix())
+ ones, _ := c.Mask.Size()
+ nlri = bgp.NewIPv6AddrPrefix(uint8(ones), c.IP.String())
+ nlri.SetPathLocalIdentifier(pathId)
+ default:
+ return p
+ }
+ path := NewPath(p.OriginInfo().source, nlri, p.IsWithdraw, p.GetPathAttrs(), p.GetTimestamp(), false)
+ path.delPathAttr(bgp.BGP_ATTR_TYPE_EXTENDED_COMMUNITIES)
+
+ if f == bgp.RF_IPv4_VPN {
+ nh := path.GetNexthop()
+ path.delPathAttr(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI)
+ path.setPathAttr(bgp.NewPathAttributeNextHop(nh.String()))
+ }
+ path.IsNexthopInvalid = p.IsNexthopInvalid
+ return path
+}
+
+func (p *Path) SetHash(v uint32) {
+ p.attrsHash = v
+}
+
+func (p *Path) GetHash() uint32 {
+ return p.attrsHash
+}
diff --git a/internal/pkg/table/path_test.go b/internal/pkg/table/path_test.go
new file mode 100644
index 00000000..449c4a8e
--- /dev/null
+++ b/internal/pkg/table/path_test.go
@@ -0,0 +1,365 @@
+// path_test.go
+package table
+
+import (
+ "testing"
+ "time"
+
+ "github.com/osrg/gobgp/internal/pkg/config"
+ "github.com/osrg/gobgp/pkg/packet/bgp"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestPathNewIPv4(t *testing.T) {
+ peerP := PathCreatePeer()
+ pathP := PathCreatePath(peerP)
+ ipv4p := NewPath(pathP[0].GetSource(), pathP[0].GetNlri(), true, pathP[0].GetPathAttrs(), time.Now(), false)
+ assert.NotNil(t, ipv4p)
+}
+
+func TestPathNewIPv6(t *testing.T) {
+ peerP := PathCreatePeer()
+ pathP := PathCreatePath(peerP)
+ ipv6p := NewPath(pathP[0].GetSource(), pathP[0].GetNlri(), true, pathP[0].GetPathAttrs(), time.Now(), false)
+ assert.NotNil(t, ipv6p)
+}
+
+func TestPathGetNlri(t *testing.T) {
+ nlri := bgp.NewIPAddrPrefix(24, "13.2.3.2")
+ pd := &Path{
+ info: &originInfo{
+ nlri: nlri,
+ },
+ }
+ r_nlri := pd.GetNlri()
+ assert.Equal(t, r_nlri, nlri)
+}
+
+func TestPathCreatePath(t *testing.T) {
+ peerP := PathCreatePeer()
+ msg := updateMsgP1()
+ updateMsgP := msg.Body.(*bgp.BGPUpdate)
+ nlriList := updateMsgP.NLRI
+ pathAttributes := updateMsgP.PathAttributes
+ nlri_info := nlriList[0]
+ path := NewPath(peerP[0], nlri_info, false, pathAttributes, time.Now(), false)
+ assert.NotNil(t, path)
+
+}
+
+func TestPathGetPrefix(t *testing.T) {
+ peerP := PathCreatePeer()
+ pathP := PathCreatePath(peerP)
+ prefix := "10.10.10.0/24"
+ r_prefix := pathP[0].getPrefix()
+ assert.Equal(t, r_prefix, prefix)
+}
+
+func TestPathGetAttribute(t *testing.T) {
+ peerP := PathCreatePeer()
+ pathP := PathCreatePath(peerP)
+ nh := "192.168.50.1"
+ pa := pathP[0].getPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP)
+ r_nh := pa.(*bgp.PathAttributeNextHop).Value.String()
+ assert.Equal(t, r_nh, nh)
+}
+
+func TestASPathLen(t *testing.T) {
+ assert := assert.New(t)
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{
+ bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint16{65001, 65002, 65003, 65004, 65004, 65004, 65004, 65004, 65005}),
+ bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SET, []uint16{65001, 65002, 65003, 65004, 65005}),
+ bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ, []uint16{65100, 65101, 65102}),
+ bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET, []uint16{65100, 65101})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("192.168.50.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+
+ pathAttributes := []bgp.PathAttributeInterface{
+ origin,
+ aspath,
+ nexthop,
+ med,
+ }
+
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.10.0")}
+ bgpmsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ update := bgpmsg.Body.(*bgp.BGPUpdate)
+ UpdatePathAttrs4ByteAs(update)
+ peer := PathCreatePeer()
+ p := NewPath(peer[0], update.NLRI[0], false, update.PathAttributes, time.Now(), false)
+ assert.Equal(10, p.GetAsPathLen())
+}
+
+func TestPathPrependAsnToExistingSeqAttr(t *testing.T) {
+ assert := assert.New(t)
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{
+ bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint16{65001, 65002, 65003, 65004, 65005}),
+ bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SET, []uint16{65001, 65002, 65003, 65004, 65005}),
+ bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ, []uint16{65100, 65101, 65102}),
+ bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET, []uint16{65100, 65101})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("192.168.50.1")
+
+ pathAttributes := []bgp.PathAttributeInterface{
+ origin,
+ aspath,
+ nexthop,
+ }
+
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.10.0")}
+ bgpmsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ update := bgpmsg.Body.(*bgp.BGPUpdate)
+ UpdatePathAttrs4ByteAs(update)
+ peer := PathCreatePeer()
+ p := NewPath(peer[0], update.NLRI[0], false, update.PathAttributes, time.Now(), false)
+
+ p.PrependAsn(65000, 1, false)
+ assert.Equal([]uint32{65000, 65001, 65002, 65003, 65004, 65005, 0, 0, 0}, p.GetAsSeqList())
+}
+
+func TestPathPrependAsnToNewAsPathAttr(t *testing.T) {
+ assert := assert.New(t)
+ origin := bgp.NewPathAttributeOrigin(0)
+ nexthop := bgp.NewPathAttributeNextHop("192.168.50.1")
+
+ pathAttributes := []bgp.PathAttributeInterface{
+ origin,
+ nexthop,
+ }
+
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.10.0")}
+ bgpmsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ update := bgpmsg.Body.(*bgp.BGPUpdate)
+ UpdatePathAttrs4ByteAs(update)
+ peer := PathCreatePeer()
+ p := NewPath(peer[0], update.NLRI[0], false, update.PathAttributes, time.Now(), false)
+
+ asn := uint32(65000)
+ p.PrependAsn(asn, 1, false)
+ assert.Equal([]uint32{asn}, p.GetAsSeqList())
+}
+
+func TestPathPrependAsnToNewAsPathSeq(t *testing.T) {
+ assert := assert.New(t)
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{
+ bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SET, []uint16{65001, 65002, 65003, 65004, 65005}),
+ bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ, []uint16{65100, 65101, 65102}),
+ bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET, []uint16{65100, 65101})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("192.168.50.1")
+
+ pathAttributes := []bgp.PathAttributeInterface{
+ origin,
+ aspath,
+ nexthop,
+ }
+
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.10.0")}
+ bgpmsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ update := bgpmsg.Body.(*bgp.BGPUpdate)
+ UpdatePathAttrs4ByteAs(update)
+ peer := PathCreatePeer()
+ p := NewPath(peer[0], update.NLRI[0], false, update.PathAttributes, time.Now(), false)
+
+ asn := uint32(65000)
+ p.PrependAsn(asn, 1, false)
+ assert.Equal([]uint32{asn, 0, 0, 0}, p.GetAsSeqList())
+}
+
+func TestPathPrependAsnToEmptyAsPathAttr(t *testing.T) {
+ assert := assert.New(t)
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{
+ bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint16{}),
+ bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SET, []uint16{65001, 65002, 65003, 65004, 65005}),
+ bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ, []uint16{65100, 65101, 65102}),
+ bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET, []uint16{65100, 65101})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("192.168.50.1")
+
+ pathAttributes := []bgp.PathAttributeInterface{
+ origin,
+ aspath,
+ nexthop,
+ }
+
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.10.0")}
+ bgpmsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ update := bgpmsg.Body.(*bgp.BGPUpdate)
+ UpdatePathAttrs4ByteAs(update)
+ peer := PathCreatePeer()
+ p := NewPath(peer[0], update.NLRI[0], false, update.PathAttributes, time.Now(), false)
+
+ asn := uint32(65000)
+ p.PrependAsn(asn, 1, false)
+ assert.Equal([]uint32{asn, 0, 0, 0}, p.GetAsSeqList())
+}
+
+func TestPathPrependAsnToFullPathAttr(t *testing.T) {
+ assert := assert.New(t)
+ origin := bgp.NewPathAttributeOrigin(0)
+
+ asns := make([]uint16, 255)
+ for i, _ := range asns {
+ asns[i] = 65000 + uint16(i)
+ }
+
+ aspathParam := []bgp.AsPathParamInterface{
+ bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, asns),
+ bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_SET, []uint16{65001, 65002, 65003, 65004, 65005}),
+ bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SEQ, []uint16{65100, 65101, 65102}),
+ bgp.NewAsPathParam(bgp.BGP_ASPATH_ATTR_TYPE_CONFED_SET, []uint16{65100, 65101})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("192.168.50.1")
+
+ pathAttributes := []bgp.PathAttributeInterface{
+ origin,
+ aspath,
+ nexthop,
+ }
+
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.10.0")}
+ bgpmsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ update := bgpmsg.Body.(*bgp.BGPUpdate)
+ UpdatePathAttrs4ByteAs(update)
+ peer := PathCreatePeer()
+ p := NewPath(peer[0], update.NLRI[0], false, update.PathAttributes, time.Now(), false)
+
+ expected := []uint32{65000, 65000}
+ for _, v := range asns {
+ expected = append(expected, uint32(v))
+ }
+ p.PrependAsn(65000, 2, false)
+ assert.Equal(append(expected, []uint32{0, 0, 0}...), p.GetAsSeqList())
+}
+
+func TestGetPathAttrs(t *testing.T) {
+ paths := PathCreatePath(PathCreatePeer())
+ path0 := paths[0]
+ path1 := path0.Clone(false)
+ path1.delPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP)
+ path2 := path1.Clone(false)
+ path2.setPathAttr(bgp.NewPathAttributeNextHop("192.168.50.1"))
+ assert.NotNil(t, path2.getPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP))
+}
+
+func PathCreatePeer() []*PeerInfo {
+ peerP1 := &PeerInfo{AS: 65000}
+ peerP2 := &PeerInfo{AS: 65001}
+ peerP3 := &PeerInfo{AS: 65002}
+ peerP := []*PeerInfo{peerP1, peerP2, peerP3}
+ return peerP
+}
+
+func PathCreatePath(peerP []*PeerInfo) []*Path {
+ bgpMsgP1 := updateMsgP1()
+ bgpMsgP2 := updateMsgP2()
+ bgpMsgP3 := updateMsgP3()
+ pathP := make([]*Path, 3)
+ for i, msg := range []*bgp.BGPMessage{bgpMsgP1, bgpMsgP2, bgpMsgP3} {
+ updateMsgP := msg.Body.(*bgp.BGPUpdate)
+ nlriList := updateMsgP.NLRI
+ pathAttributes := updateMsgP.PathAttributes
+ nlri_info := nlriList[0]
+ pathP[i] = NewPath(peerP[i], nlri_info, false, pathAttributes, time.Now(), false)
+ }
+ return pathP
+}
+
+func updateMsgP1() *bgp.BGPMessage {
+
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65000})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("192.168.50.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+
+ pathAttributes := []bgp.PathAttributeInterface{
+ origin,
+ aspath,
+ nexthop,
+ med,
+ }
+
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.10.0")}
+ return bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+}
+
+func updateMsgP2() *bgp.BGPMessage {
+
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65100})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("192.168.100.1")
+ med := bgp.NewPathAttributeMultiExitDisc(100)
+
+ pathAttributes := []bgp.PathAttributeInterface{
+ origin,
+ aspath,
+ nexthop,
+ med,
+ }
+
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "20.20.20.0")}
+ return bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+}
+
+func updateMsgP3() *bgp.BGPMessage {
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65100})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("192.168.150.1")
+ med := bgp.NewPathAttributeMultiExitDisc(100)
+
+ pathAttributes := []bgp.PathAttributeInterface{
+ origin,
+ aspath,
+ nexthop,
+ med,
+ }
+
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "30.30.30.0")}
+ w1 := bgp.NewIPAddrPrefix(23, "40.40.40.0")
+ withdrawnRoutes := []*bgp.IPAddrPrefix{w1}
+ return bgp.NewBGPUpdateMessage(withdrawnRoutes, pathAttributes, nlri)
+}
+
+func TestRemovePrivateAS(t *testing.T) {
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAs4PathParam(2, []uint32{64512, 64513, 1, 2})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nlri := bgp.NewIPAddrPrefix(24, "30.30.30.0")
+ path := NewPath(nil, nlri, false, []bgp.PathAttributeInterface{aspath}, time.Now(), false)
+ path.RemovePrivateAS(10, config.REMOVE_PRIVATE_AS_OPTION_ALL)
+ list := path.GetAsList()
+ assert.Equal(t, len(list), 2)
+ assert.Equal(t, list[0], uint32(1))
+ assert.Equal(t, list[1], uint32(2))
+
+ path = NewPath(nil, nlri, false, []bgp.PathAttributeInterface{aspath}, time.Now(), false)
+ path.RemovePrivateAS(10, config.REMOVE_PRIVATE_AS_OPTION_REPLACE)
+ list = path.GetAsList()
+ assert.Equal(t, len(list), 4)
+ assert.Equal(t, list[0], uint32(10))
+ assert.Equal(t, list[1], uint32(10))
+ assert.Equal(t, list[2], uint32(1))
+ assert.Equal(t, list[3], uint32(2))
+}
+
+func TestReplaceAS(t *testing.T) {
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAs4PathParam(2, []uint32{64512, 64513, 1, 2})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nlri := bgp.NewIPAddrPrefix(24, "30.30.30.0")
+ path := NewPath(nil, nlri, false, []bgp.PathAttributeInterface{aspath}, time.Now(), false)
+ path = path.ReplaceAS(10, 1)
+ list := path.GetAsList()
+ assert.Equal(t, len(list), 4)
+ assert.Equal(t, list[0], uint32(64512))
+ assert.Equal(t, list[1], uint32(64513))
+ assert.Equal(t, list[2], uint32(10))
+ assert.Equal(t, list[3], uint32(2))
+}
diff --git a/internal/pkg/table/policy.go b/internal/pkg/table/policy.go
new file mode 100644
index 00000000..23c2110c
--- /dev/null
+++ b/internal/pkg/table/policy.go
@@ -0,0 +1,3994 @@
+// Copyright (C) 2014-2016 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package table
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "net"
+ "reflect"
+ "regexp"
+ "sort"
+ "strconv"
+ "strings"
+ "sync"
+
+ "github.com/osrg/gobgp/internal/pkg/config"
+ "github.com/osrg/gobgp/pkg/packet/bgp"
+
+ radix "github.com/armon/go-radix"
+ log "github.com/sirupsen/logrus"
+)
+
+type PolicyOptions struct {
+ Info *PeerInfo
+ ValidationResult *Validation
+ OldNextHop net.IP
+}
+
+type DefinedType int
+
+const (
+ DEFINED_TYPE_PREFIX DefinedType = iota
+ DEFINED_TYPE_NEIGHBOR
+ DEFINED_TYPE_TAG
+ DEFINED_TYPE_AS_PATH
+ DEFINED_TYPE_COMMUNITY
+ DEFINED_TYPE_EXT_COMMUNITY
+ DEFINED_TYPE_LARGE_COMMUNITY
+ DEFINED_TYPE_NEXT_HOP
+)
+
+type RouteType int
+
+const (
+ ROUTE_TYPE_NONE RouteType = iota
+ ROUTE_TYPE_ACCEPT
+ ROUTE_TYPE_REJECT
+)
+
+func (t RouteType) String() string {
+ switch t {
+ case ROUTE_TYPE_NONE:
+ return "continue"
+ case ROUTE_TYPE_ACCEPT:
+ return "accept"
+ case ROUTE_TYPE_REJECT:
+ return "reject"
+ }
+ return fmt.Sprintf("unknown(%d)", t)
+}
+
+type PolicyDirection int
+
+const (
+ POLICY_DIRECTION_NONE PolicyDirection = iota
+ POLICY_DIRECTION_IN
+ POLICY_DIRECTION_IMPORT
+ POLICY_DIRECTION_EXPORT
+)
+
+func (d PolicyDirection) String() string {
+ switch d {
+ case POLICY_DIRECTION_IN:
+ return "in"
+ case POLICY_DIRECTION_IMPORT:
+ return "import"
+ case POLICY_DIRECTION_EXPORT:
+ return "export"
+ }
+ return fmt.Sprintf("unknown(%d)", d)
+}
+
+type MatchOption int
+
+const (
+ MATCH_OPTION_ANY MatchOption = iota
+ MATCH_OPTION_ALL
+ MATCH_OPTION_INVERT
+)
+
+func (o MatchOption) String() string {
+ switch o {
+ case MATCH_OPTION_ANY:
+ return "any"
+ case MATCH_OPTION_ALL:
+ return "all"
+ case MATCH_OPTION_INVERT:
+ return "invert"
+ default:
+ return fmt.Sprintf("MatchOption(%d)", o)
+ }
+}
+
+func (o MatchOption) ConvertToMatchSetOptionsRestrictedType() config.MatchSetOptionsRestrictedType {
+ switch o {
+ case MATCH_OPTION_ANY:
+ return config.MATCH_SET_OPTIONS_RESTRICTED_TYPE_ANY
+ case MATCH_OPTION_INVERT:
+ return config.MATCH_SET_OPTIONS_RESTRICTED_TYPE_INVERT
+ }
+ return "unknown"
+}
+
+type MedActionType int
+
+const (
+ MED_ACTION_MOD MedActionType = iota
+ MED_ACTION_REPLACE
+)
+
+var CommunityOptionNameMap = map[config.BgpSetCommunityOptionType]string{
+ config.BGP_SET_COMMUNITY_OPTION_TYPE_ADD: "add",
+ config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE: "remove",
+ config.BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE: "replace",
+}
+
+var CommunityOptionValueMap = map[string]config.BgpSetCommunityOptionType{
+ CommunityOptionNameMap[config.BGP_SET_COMMUNITY_OPTION_TYPE_ADD]: config.BGP_SET_COMMUNITY_OPTION_TYPE_ADD,
+ CommunityOptionNameMap[config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE]: config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE,
+ CommunityOptionNameMap[config.BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE]: config.BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE,
+}
+
+type ConditionType int
+
+const (
+ CONDITION_PREFIX ConditionType = iota
+ CONDITION_NEIGHBOR
+ CONDITION_AS_PATH
+ CONDITION_COMMUNITY
+ CONDITION_EXT_COMMUNITY
+ CONDITION_AS_PATH_LENGTH
+ CONDITION_RPKI
+ CONDITION_ROUTE_TYPE
+ CONDITION_LARGE_COMMUNITY
+ CONDITION_NEXT_HOP
+ CONDITION_AFI_SAFI_IN
+)
+
+type ActionType int
+
+const (
+ ACTION_ROUTING ActionType = iota
+ ACTION_COMMUNITY
+ ACTION_EXT_COMMUNITY
+ ACTION_MED
+ ACTION_AS_PATH_PREPEND
+ ACTION_NEXTHOP
+ ACTION_LOCAL_PREF
+ ACTION_LARGE_COMMUNITY
+)
+
+func NewMatchOption(c interface{}) (MatchOption, error) {
+ switch t := c.(type) {
+ case config.MatchSetOptionsType:
+ t = t.DefaultAsNeeded()
+ switch t {
+ case config.MATCH_SET_OPTIONS_TYPE_ANY:
+ return MATCH_OPTION_ANY, nil
+ case config.MATCH_SET_OPTIONS_TYPE_ALL:
+ return MATCH_OPTION_ALL, nil
+ case config.MATCH_SET_OPTIONS_TYPE_INVERT:
+ return MATCH_OPTION_INVERT, nil
+ }
+ case config.MatchSetOptionsRestrictedType:
+ t = t.DefaultAsNeeded()
+ switch t {
+ case config.MATCH_SET_OPTIONS_RESTRICTED_TYPE_ANY:
+ return MATCH_OPTION_ANY, nil
+ case config.MATCH_SET_OPTIONS_RESTRICTED_TYPE_INVERT:
+ return MATCH_OPTION_INVERT, nil
+ }
+ }
+ return MATCH_OPTION_ANY, fmt.Errorf("invalid argument to create match option: %v", c)
+}
+
+type AttributeComparison int
+
+const (
+ // "== comparison"
+ ATTRIBUTE_EQ AttributeComparison = iota
+ // ">= comparison"
+ ATTRIBUTE_GE
+ // "<= comparison"
+ ATTRIBUTE_LE
+)
+
+func (c AttributeComparison) String() string {
+ switch c {
+ case ATTRIBUTE_EQ:
+ return "="
+ case ATTRIBUTE_GE:
+ return ">="
+ case ATTRIBUTE_LE:
+ return "<="
+ }
+ return "?"
+}
+
+const (
+ ASPATH_REGEXP_MAGIC = "(^|[,{}() ]|$)"
+)
+
+type DefinedSet interface {
+ Type() DefinedType
+ Name() string
+ Append(DefinedSet) error
+ Remove(DefinedSet) error
+ Replace(DefinedSet) error
+ String() string
+ List() []string
+}
+
+type DefinedSetMap map[DefinedType]map[string]DefinedSet
+
+type DefinedSetList []DefinedSet
+
+func (l DefinedSetList) Len() int {
+ return len(l)
+}
+
+func (l DefinedSetList) Swap(i, j int) {
+ l[i], l[j] = l[j], l[i]
+}
+
+func (l DefinedSetList) Less(i, j int) bool {
+ if l[i].Type() != l[j].Type() {
+ return l[i].Type() < l[j].Type()
+ }
+ return l[i].Name() < l[j].Name()
+}
+
+type Prefix struct {
+ Prefix *net.IPNet
+ AddressFamily bgp.RouteFamily
+ MasklengthRangeMax uint8
+ MasklengthRangeMin uint8
+}
+
+func (p *Prefix) Match(path *Path) bool {
+ rf := path.GetRouteFamily()
+ if rf != p.AddressFamily {
+ return false
+ }
+
+ var pAddr net.IP
+ var pMasklen uint8
+ switch rf {
+ case bgp.RF_IPv4_UC:
+ pAddr = path.GetNlri().(*bgp.IPAddrPrefix).Prefix
+ pMasklen = path.GetNlri().(*bgp.IPAddrPrefix).Length
+ case bgp.RF_IPv6_UC:
+ pAddr = path.GetNlri().(*bgp.IPv6AddrPrefix).Prefix
+ pMasklen = path.GetNlri().(*bgp.IPv6AddrPrefix).Length
+ default:
+ return false
+ }
+
+ return (p.MasklengthRangeMin <= pMasklen && pMasklen <= p.MasklengthRangeMax) && p.Prefix.Contains(pAddr)
+}
+
+func (lhs *Prefix) Equal(rhs *Prefix) bool {
+ if lhs == rhs {
+ return true
+ }
+ if rhs == nil {
+ return false
+ }
+ return lhs.Prefix.String() == rhs.Prefix.String() && lhs.MasklengthRangeMin == rhs.MasklengthRangeMin && lhs.MasklengthRangeMax == rhs.MasklengthRangeMax
+}
+
+func (p *Prefix) PrefixString() string {
+ isZeros := func(p net.IP) bool {
+ for i := 0; i < len(p); i++ {
+ if p[i] != 0 {
+ return false
+ }
+ }
+ return true
+ }
+
+ ip := p.Prefix.IP
+ if p.AddressFamily == bgp.RF_IPv6_UC && isZeros(ip[0:10]) && ip[10] == 0xff && ip[11] == 0xff {
+ m, _ := p.Prefix.Mask.Size()
+ return fmt.Sprintf("::FFFF:%s/%d", ip.To16(), m)
+ }
+ return p.Prefix.String()
+}
+
+var _regexpPrefixRange = regexp.MustCompile(`(\d+)\.\.(\d+)`)
+
+func NewPrefix(c config.Prefix) (*Prefix, error) {
+ _, prefix, err := net.ParseCIDR(c.IpPrefix)
+ if err != nil {
+ return nil, err
+ }
+
+ rf := bgp.RF_IPv4_UC
+ if strings.Contains(c.IpPrefix, ":") {
+ rf = bgp.RF_IPv6_UC
+ }
+ p := &Prefix{
+ Prefix: prefix,
+ AddressFamily: rf,
+ }
+ maskRange := c.MasklengthRange
+
+ if maskRange == "" {
+ l, _ := prefix.Mask.Size()
+ maskLength := uint8(l)
+ p.MasklengthRangeMax = maskLength
+ p.MasklengthRangeMin = maskLength
+ return p, nil
+ }
+
+ elems := _regexpPrefixRange.FindStringSubmatch(maskRange)
+ if len(elems) != 3 {
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ "Type": "Prefix",
+ "MaskRangeFormat": maskRange,
+ }).Warn("mask length range format is invalid.")
+ return nil, fmt.Errorf("mask length range format is invalid")
+ }
+
+ // we've already checked the range is sane by regexp
+ min, _ := strconv.ParseUint(elems[1], 10, 8)
+ max, _ := strconv.ParseUint(elems[2], 10, 8)
+ p.MasklengthRangeMin = uint8(min)
+ p.MasklengthRangeMax = uint8(max)
+ return p, nil
+}
+
+type PrefixSet struct {
+ name string
+ tree *radix.Tree
+ family bgp.RouteFamily
+}
+
+func (s *PrefixSet) Name() string {
+ return s.name
+}
+
+func (s *PrefixSet) Type() DefinedType {
+ return DEFINED_TYPE_PREFIX
+}
+
+func (lhs *PrefixSet) Append(arg DefinedSet) error {
+ rhs, ok := arg.(*PrefixSet)
+ if !ok {
+ return fmt.Errorf("type cast failed")
+ }
+ // if either is empty, family can be ignored.
+ if lhs.tree.Len() != 0 && rhs.tree.Len() != 0 {
+ _, w, _ := lhs.tree.Minimum()
+ l := w.([]*Prefix)
+ _, v, _ := rhs.tree.Minimum()
+ r := v.([]*Prefix)
+ if l[0].AddressFamily != r[0].AddressFamily {
+ return fmt.Errorf("can't append different family")
+ }
+ }
+ rhs.tree.Walk(func(key string, v interface{}) bool {
+ w, ok := lhs.tree.Get(key)
+ if ok {
+ r := v.([]*Prefix)
+ l := w.([]*Prefix)
+ lhs.tree.Insert(key, append(l, r...))
+ } else {
+ lhs.tree.Insert(key, v)
+ }
+ return false
+ })
+ _, w, _ := lhs.tree.Minimum()
+ lhs.family = w.([]*Prefix)[0].AddressFamily
+ return nil
+}
+
+func (lhs *PrefixSet) Remove(arg DefinedSet) error {
+ rhs, ok := arg.(*PrefixSet)
+ if !ok {
+ return fmt.Errorf("type cast failed")
+ }
+ rhs.tree.Walk(func(key string, v interface{}) bool {
+ w, ok := lhs.tree.Get(key)
+ if !ok {
+ return false
+ }
+ r := v.([]*Prefix)
+ l := w.([]*Prefix)
+ new := make([]*Prefix, 0, len(l))
+ for _, lp := range l {
+ delete := false
+ for _, rp := range r {
+ if lp.Equal(rp) {
+ delete = true
+ break
+ }
+ }
+ if !delete {
+ new = append(new, lp)
+ }
+ }
+ if len(new) == 0 {
+ lhs.tree.Delete(key)
+ } else {
+ lhs.tree.Insert(key, new)
+ }
+ return false
+ })
+ return nil
+}
+
+func (lhs *PrefixSet) Replace(arg DefinedSet) error {
+ rhs, ok := arg.(*PrefixSet)
+ if !ok {
+ return fmt.Errorf("type cast failed")
+ }
+ lhs.tree = rhs.tree
+ lhs.family = rhs.family
+ return nil
+}
+
+func (s *PrefixSet) List() []string {
+ var list []string
+ s.tree.Walk(func(s string, v interface{}) bool {
+ ps := v.([]*Prefix)
+ for _, p := range ps {
+ list = append(list, fmt.Sprintf("%s %d..%d", p.PrefixString(), p.MasklengthRangeMin, p.MasklengthRangeMax))
+ }
+ return false
+ })
+ return list
+}
+
+func (s *PrefixSet) ToConfig() *config.PrefixSet {
+ list := make([]config.Prefix, 0, s.tree.Len())
+ s.tree.Walk(func(s string, v interface{}) bool {
+ ps := v.([]*Prefix)
+ for _, p := range ps {
+ list = append(list, config.Prefix{IpPrefix: p.PrefixString(), MasklengthRange: fmt.Sprintf("%d..%d", p.MasklengthRangeMin, p.MasklengthRangeMax)})
+ }
+ return false
+ })
+ return &config.PrefixSet{
+ PrefixSetName: s.name,
+ PrefixList: list,
+ }
+}
+
+func (s *PrefixSet) String() string {
+ return strings.Join(s.List(), "\n")
+}
+
+func (s *PrefixSet) MarshalJSON() ([]byte, error) {
+ return json.Marshal(s.ToConfig())
+}
+
+func NewPrefixSetFromApiStruct(name string, prefixes []*Prefix) (*PrefixSet, error) {
+ if name == "" {
+ return nil, fmt.Errorf("empty prefix set name")
+ }
+ tree := radix.New()
+ var family bgp.RouteFamily
+ for i, x := range prefixes {
+ if i == 0 {
+ family = x.AddressFamily
+ } else if family != x.AddressFamily {
+ return nil, fmt.Errorf("multiple families")
+ }
+ key := CidrToRadixkey(x.Prefix.String())
+ d, ok := tree.Get(key)
+ if ok {
+ ps := d.([]*Prefix)
+ tree.Insert(key, append(ps, x))
+ } else {
+ tree.Insert(key, []*Prefix{x})
+ }
+ }
+ return &PrefixSet{
+ name: name,
+ tree: tree,
+ family: family,
+ }, nil
+}
+
+func NewPrefixSet(c config.PrefixSet) (*PrefixSet, error) {
+ name := c.PrefixSetName
+ if name == "" {
+ if len(c.PrefixList) == 0 {
+ return nil, nil
+ }
+ return nil, fmt.Errorf("empty prefix set name")
+ }
+ tree := radix.New()
+ var family bgp.RouteFamily
+ for i, x := range c.PrefixList {
+ y, err := NewPrefix(x)
+ if err != nil {
+ return nil, err
+ }
+ if i == 0 {
+ family = y.AddressFamily
+ } else if family != y.AddressFamily {
+ return nil, fmt.Errorf("multiple families")
+ }
+ key := CidrToRadixkey(y.Prefix.String())
+ d, ok := tree.Get(key)
+ if ok {
+ ps := d.([]*Prefix)
+ tree.Insert(key, append(ps, y))
+ } else {
+ tree.Insert(key, []*Prefix{y})
+ }
+ }
+ return &PrefixSet{
+ name: name,
+ tree: tree,
+ family: family,
+ }, nil
+}
+
+type NextHopSet struct {
+ list []net.IPNet
+}
+
+func (s *NextHopSet) Name() string {
+ return "NextHopSet: NO NAME"
+}
+
+func (s *NextHopSet) Type() DefinedType {
+ return DEFINED_TYPE_NEXT_HOP
+}
+
+func (lhs *NextHopSet) Append(arg DefinedSet) error {
+ rhs, ok := arg.(*NextHopSet)
+ if !ok {
+ return fmt.Errorf("type cast failed")
+ }
+ lhs.list = append(lhs.list, rhs.list...)
+ return nil
+}
+
+func (lhs *NextHopSet) Remove(arg DefinedSet) error {
+ rhs, ok := arg.(*NextHopSet)
+ if !ok {
+ return fmt.Errorf("type cast failed")
+ }
+ ps := make([]net.IPNet, 0, len(lhs.list))
+ for _, x := range lhs.list {
+ found := false
+ for _, y := range rhs.list {
+ if x.String() == y.String() {
+ found = true
+ break
+ }
+ }
+ if !found {
+ ps = append(ps, x)
+ }
+ }
+ lhs.list = ps
+ return nil
+}
+
+func (lhs *NextHopSet) Replace(arg DefinedSet) error {
+ rhs, ok := arg.(*NextHopSet)
+ if !ok {
+ return fmt.Errorf("type cast failed")
+ }
+ lhs.list = rhs.list
+ return nil
+}
+
+func (s *NextHopSet) List() []string {
+ list := make([]string, 0, len(s.list))
+ for _, n := range s.list {
+ list = append(list, n.String())
+ }
+ return list
+}
+
+func (s *NextHopSet) ToConfig() []string {
+ return s.List()
+}
+
+func (s *NextHopSet) String() string {
+ return "[ " + strings.Join(s.List(), ", ") + " ]"
+}
+
+func (s *NextHopSet) MarshalJSON() ([]byte, error) {
+ return json.Marshal(s.ToConfig())
+}
+
+func NewNextHopSetFromApiStruct(name string, list []net.IPNet) (*NextHopSet, error) {
+ return &NextHopSet{
+ list: list,
+ }, nil
+}
+
+func NewNextHopSet(c []string) (*NextHopSet, error) {
+ list := make([]net.IPNet, 0, len(c))
+ for _, x := range c {
+ _, cidr, err := net.ParseCIDR(x)
+ if err != nil {
+ addr := net.ParseIP(x)
+ if addr == nil {
+ return nil, fmt.Errorf("invalid address or prefix: %s", x)
+ }
+ mask := net.CIDRMask(32, 32)
+ if addr.To4() == nil {
+ mask = net.CIDRMask(128, 128)
+ }
+ cidr = &net.IPNet{
+ IP: addr,
+ Mask: mask,
+ }
+ }
+ list = append(list, *cidr)
+ }
+ return &NextHopSet{
+ list: list,
+ }, nil
+}
+
+type NeighborSet struct {
+ name string
+ list []net.IPNet
+}
+
+func (s *NeighborSet) Name() string {
+ return s.name
+}
+
+func (s *NeighborSet) Type() DefinedType {
+ return DEFINED_TYPE_NEIGHBOR
+}
+
+func (lhs *NeighborSet) Append(arg DefinedSet) error {
+ rhs, ok := arg.(*NeighborSet)
+ if !ok {
+ return fmt.Errorf("type cast failed")
+ }
+ lhs.list = append(lhs.list, rhs.list...)
+ return nil
+}
+
+func (lhs *NeighborSet) Remove(arg DefinedSet) error {
+ rhs, ok := arg.(*NeighborSet)
+ if !ok {
+ return fmt.Errorf("type cast failed")
+ }
+ ps := make([]net.IPNet, 0, len(lhs.list))
+ for _, x := range lhs.list {
+ found := false
+ for _, y := range rhs.list {
+ if x.String() == y.String() {
+ found = true
+ break
+ }
+ }
+ if !found {
+ ps = append(ps, x)
+ }
+ }
+ lhs.list = ps
+ return nil
+}
+
+func (lhs *NeighborSet) Replace(arg DefinedSet) error {
+ rhs, ok := arg.(*NeighborSet)
+ if !ok {
+ return fmt.Errorf("type cast failed")
+ }
+ lhs.list = rhs.list
+ return nil
+}
+
+func (s *NeighborSet) List() []string {
+ list := make([]string, 0, len(s.list))
+ for _, n := range s.list {
+ list = append(list, n.String())
+ }
+ return list
+}
+
+func (s *NeighborSet) ToConfig() *config.NeighborSet {
+ return &config.NeighborSet{
+ NeighborSetName: s.name,
+ NeighborInfoList: s.List(),
+ }
+}
+
+func (s *NeighborSet) String() string {
+ return strings.Join(s.List(), "\n")
+}
+
+func (s *NeighborSet) MarshalJSON() ([]byte, error) {
+ return json.Marshal(s.ToConfig())
+}
+
+func NewNeighborSetFromApiStruct(name string, list []net.IPNet) (*NeighborSet, error) {
+ return &NeighborSet{
+ name: name,
+ list: list,
+ }, nil
+}
+
+func NewNeighborSet(c config.NeighborSet) (*NeighborSet, error) {
+ name := c.NeighborSetName
+ if name == "" {
+ if len(c.NeighborInfoList) == 0 {
+ return nil, nil
+ }
+ return nil, fmt.Errorf("empty neighbor set name")
+ }
+ list := make([]net.IPNet, 0, len(c.NeighborInfoList))
+ for _, x := range c.NeighborInfoList {
+ _, cidr, err := net.ParseCIDR(x)
+ if err != nil {
+ addr := net.ParseIP(x)
+ if addr == nil {
+ return nil, fmt.Errorf("invalid address or prefix: %s", x)
+ }
+ mask := net.CIDRMask(32, 32)
+ if addr.To4() == nil {
+ mask = net.CIDRMask(128, 128)
+ }
+ cidr = &net.IPNet{
+ IP: addr,
+ Mask: mask,
+ }
+ }
+ list = append(list, *cidr)
+ }
+ return &NeighborSet{
+ name: name,
+ list: list,
+ }, nil
+}
+
+type singleAsPathMatchMode int
+
+const (
+ INCLUDE singleAsPathMatchMode = iota
+ LEFT_MOST
+ ORIGIN
+ ONLY
+)
+
+type singleAsPathMatch struct {
+ asn uint32
+ mode singleAsPathMatchMode
+}
+
+func (lhs *singleAsPathMatch) Equal(rhs *singleAsPathMatch) bool {
+ return lhs.asn == rhs.asn && lhs.mode == rhs.mode
+}
+
+func (lhs *singleAsPathMatch) String() string {
+ switch lhs.mode {
+ case INCLUDE:
+ return fmt.Sprintf("_%d_", lhs.asn)
+ case LEFT_MOST:
+ return fmt.Sprintf("^%d_", lhs.asn)
+ case ORIGIN:
+ return fmt.Sprintf("_%d$", lhs.asn)
+ case ONLY:
+ return fmt.Sprintf("^%d$", lhs.asn)
+ }
+ return ""
+}
+
+func (m *singleAsPathMatch) Match(aspath []uint32) bool {
+ if len(aspath) == 0 {
+ return false
+ }
+ switch m.mode {
+ case INCLUDE:
+ for _, asn := range aspath {
+ if m.asn == asn {
+ return true
+ }
+ }
+ case LEFT_MOST:
+ if m.asn == aspath[0] {
+ return true
+ }
+ case ORIGIN:
+ if m.asn == aspath[len(aspath)-1] {
+ return true
+ }
+ case ONLY:
+ if len(aspath) == 1 && m.asn == aspath[0] {
+ return true
+ }
+ }
+ return false
+}
+
+var (
+ _regexpLeftMostRe = regexp.MustCompile(`$\^([0-9]+)_^`)
+ _regexpOriginRe = regexp.MustCompile(`^_([0-9]+)\$$`)
+ _regexpIncludeRe = regexp.MustCompile("^_([0-9]+)_$")
+ _regexpOnlyRe = regexp.MustCompile(`^\^([0-9]+)\$$`)
+)
+
+func NewSingleAsPathMatch(arg string) *singleAsPathMatch {
+ switch {
+ case _regexpLeftMostRe.MatchString(arg):
+ asn, _ := strconv.ParseUint(_regexpLeftMostRe.FindStringSubmatch(arg)[1], 10, 32)
+ return &singleAsPathMatch{
+ asn: uint32(asn),
+ mode: LEFT_MOST,
+ }
+ case _regexpOriginRe.MatchString(arg):
+ asn, _ := strconv.ParseUint(_regexpOriginRe.FindStringSubmatch(arg)[1], 10, 32)
+ return &singleAsPathMatch{
+ asn: uint32(asn),
+ mode: ORIGIN,
+ }
+ case _regexpIncludeRe.MatchString(arg):
+ asn, _ := strconv.ParseUint(_regexpIncludeRe.FindStringSubmatch(arg)[1], 10, 32)
+ return &singleAsPathMatch{
+ asn: uint32(asn),
+ mode: INCLUDE,
+ }
+ case _regexpOnlyRe.MatchString(arg):
+ asn, _ := strconv.ParseUint(_regexpOnlyRe.FindStringSubmatch(arg)[1], 10, 32)
+ return &singleAsPathMatch{
+ asn: uint32(asn),
+ mode: ONLY,
+ }
+ }
+ return nil
+}
+
+type AsPathSet struct {
+ typ DefinedType
+ name string
+ list []*regexp.Regexp
+ singleList []*singleAsPathMatch
+}
+
+func (s *AsPathSet) Name() string {
+ return s.name
+}
+
+func (s *AsPathSet) Type() DefinedType {
+ return s.typ
+}
+
+func (lhs *AsPathSet) Append(arg DefinedSet) error {
+ if lhs.Type() != arg.Type() {
+ return fmt.Errorf("can't append to different type of defined-set")
+ }
+ lhs.list = append(lhs.list, arg.(*AsPathSet).list...)
+ lhs.singleList = append(lhs.singleList, arg.(*AsPathSet).singleList...)
+ return nil
+}
+
+func (lhs *AsPathSet) Remove(arg DefinedSet) error {
+ if lhs.Type() != arg.Type() {
+ return fmt.Errorf("can't append to different type of defined-set")
+ }
+ newList := make([]*regexp.Regexp, 0, len(lhs.list))
+ for _, x := range lhs.list {
+ found := false
+ for _, y := range arg.(*AsPathSet).list {
+ if x.String() == y.String() {
+ found = true
+ break
+ }
+ }
+ if !found {
+ newList = append(newList, x)
+ }
+ }
+ lhs.list = newList
+ newSingleList := make([]*singleAsPathMatch, 0, len(lhs.singleList))
+ for _, x := range lhs.singleList {
+ found := false
+ for _, y := range arg.(*AsPathSet).singleList {
+ if x.Equal(y) {
+ found = true
+ break
+ }
+ }
+ if !found {
+ newSingleList = append(newSingleList, x)
+ }
+ }
+ lhs.singleList = newSingleList
+ return nil
+}
+
+func (lhs *AsPathSet) Replace(arg DefinedSet) error {
+ rhs, ok := arg.(*AsPathSet)
+ if !ok {
+ return fmt.Errorf("type cast failed")
+ }
+ lhs.list = rhs.list
+ lhs.singleList = rhs.singleList
+ return nil
+}
+
+func (s *AsPathSet) List() []string {
+ list := make([]string, 0, len(s.list)+len(s.singleList))
+ for _, exp := range s.singleList {
+ list = append(list, exp.String())
+ }
+ for _, exp := range s.list {
+ list = append(list, exp.String())
+ }
+ return list
+}
+
+func (s *AsPathSet) ToConfig() *config.AsPathSet {
+ return &config.AsPathSet{
+ AsPathSetName: s.name,
+ AsPathList: s.List(),
+ }
+}
+
+func (s *AsPathSet) String() string {
+ return strings.Join(s.List(), "\n")
+}
+
+func (s *AsPathSet) MarshalJSON() ([]byte, error) {
+ return json.Marshal(s.ToConfig())
+}
+
+func NewAsPathSet(c config.AsPathSet) (*AsPathSet, error) {
+ name := c.AsPathSetName
+ if name == "" {
+ if len(c.AsPathList) == 0 {
+ return nil, nil
+ }
+ return nil, fmt.Errorf("empty as-path set name")
+ }
+ list := make([]*regexp.Regexp, 0, len(c.AsPathList))
+ singleList := make([]*singleAsPathMatch, 0, len(c.AsPathList))
+ for _, x := range c.AsPathList {
+ if s := NewSingleAsPathMatch(x); s != nil {
+ singleList = append(singleList, s)
+ } else {
+ exp, err := regexp.Compile(strings.Replace(x, "_", ASPATH_REGEXP_MAGIC, -1))
+ if err != nil {
+ return nil, fmt.Errorf("invalid regular expression: %s", x)
+ }
+ list = append(list, exp)
+ }
+ }
+ return &AsPathSet{
+ typ: DEFINED_TYPE_AS_PATH,
+ name: name,
+ list: list,
+ singleList: singleList,
+ }, nil
+}
+
+type regExpSet struct {
+ typ DefinedType
+ name string
+ list []*regexp.Regexp
+}
+
+func (s *regExpSet) Name() string {
+ return s.name
+}
+
+func (s *regExpSet) Type() DefinedType {
+ return s.typ
+}
+
+func (lhs *regExpSet) Append(arg DefinedSet) error {
+ if lhs.Type() != arg.Type() {
+ return fmt.Errorf("can't append to different type of defined-set")
+ }
+ var list []*regexp.Regexp
+ switch lhs.Type() {
+ case DEFINED_TYPE_AS_PATH:
+ list = arg.(*AsPathSet).list
+ case DEFINED_TYPE_COMMUNITY:
+ list = arg.(*CommunitySet).list
+ case DEFINED_TYPE_EXT_COMMUNITY:
+ list = arg.(*ExtCommunitySet).list
+ case DEFINED_TYPE_LARGE_COMMUNITY:
+ list = arg.(*LargeCommunitySet).list
+ default:
+ return fmt.Errorf("invalid defined-set type: %d", lhs.Type())
+ }
+ lhs.list = append(lhs.list, list...)
+ return nil
+}
+
+func (lhs *regExpSet) Remove(arg DefinedSet) error {
+ if lhs.Type() != arg.Type() {
+ return fmt.Errorf("can't append to different type of defined-set")
+ }
+ var list []*regexp.Regexp
+ switch lhs.Type() {
+ case DEFINED_TYPE_AS_PATH:
+ list = arg.(*AsPathSet).list
+ case DEFINED_TYPE_COMMUNITY:
+ list = arg.(*CommunitySet).list
+ case DEFINED_TYPE_EXT_COMMUNITY:
+ list = arg.(*ExtCommunitySet).list
+ case DEFINED_TYPE_LARGE_COMMUNITY:
+ list = arg.(*LargeCommunitySet).list
+ default:
+ return fmt.Errorf("invalid defined-set type: %d", lhs.Type())
+ }
+ ps := make([]*regexp.Regexp, 0, len(lhs.list))
+ for _, x := range lhs.list {
+ found := false
+ for _, y := range list {
+ if x.String() == y.String() {
+ found = true
+ break
+ }
+ }
+ if !found {
+ ps = append(ps, x)
+ }
+ }
+ lhs.list = ps
+ return nil
+}
+
+func (lhs *regExpSet) Replace(arg DefinedSet) error {
+ switch c := arg.(type) {
+ case *CommunitySet:
+ lhs.list = c.list
+ case *ExtCommunitySet:
+ lhs.list = c.list
+ case *LargeCommunitySet:
+ lhs.list = c.list
+ default:
+ return fmt.Errorf("type cast failed")
+ }
+ return nil
+}
+
+type CommunitySet struct {
+ regExpSet
+}
+
+func (s *CommunitySet) List() []string {
+ list := make([]string, 0, len(s.list))
+ for _, exp := range s.list {
+ list = append(list, exp.String())
+ }
+ return list
+}
+
+func (s *CommunitySet) ToConfig() *config.CommunitySet {
+ return &config.CommunitySet{
+ CommunitySetName: s.name,
+ CommunityList: s.List(),
+ }
+}
+
+func (s *CommunitySet) String() string {
+ return strings.Join(s.List(), "\n")
+}
+
+func (s *CommunitySet) MarshalJSON() ([]byte, error) {
+ return json.Marshal(s.ToConfig())
+}
+
+var _regexpCommunity = regexp.MustCompile(`(\d+):(\d+)`)
+
+func ParseCommunity(arg string) (uint32, error) {
+ i, err := strconv.ParseUint(arg, 10, 32)
+ if err == nil {
+ return uint32(i), nil
+ }
+
+ elems := _regexpCommunity.FindStringSubmatch(arg)
+ if len(elems) == 3 {
+ fst, _ := strconv.ParseUint(elems[1], 10, 16)
+ snd, _ := strconv.ParseUint(elems[2], 10, 16)
+ return uint32(fst<<16 | snd), nil
+ }
+ for i, v := range bgp.WellKnownCommunityNameMap {
+ if arg == v {
+ return uint32(i), nil
+ }
+ }
+ return 0, fmt.Errorf("failed to parse %s as community", arg)
+}
+
+func ParseExtCommunity(arg string) (bgp.ExtendedCommunityInterface, error) {
+ var subtype bgp.ExtendedCommunityAttrSubType
+ var value string
+ elems := strings.SplitN(arg, ":", 2)
+
+ isValidationState := func(s string) bool {
+ s = strings.ToLower(s)
+ r := s == bgp.VALIDATION_STATE_VALID.String()
+ r = r || s == bgp.VALIDATION_STATE_NOT_FOUND.String()
+ return r || s == bgp.VALIDATION_STATE_INVALID.String()
+ }
+ if len(elems) < 2 && (len(elems) < 1 && !isValidationState(elems[0])) {
+ return nil, fmt.Errorf("invalid ext-community (rt|soo):<value> | valid | not-found | invalid")
+ }
+ if isValidationState(elems[0]) {
+ subtype = bgp.EC_SUBTYPE_ORIGIN_VALIDATION
+ value = elems[0]
+ } else {
+ switch strings.ToLower(elems[0]) {
+ case "rt":
+ subtype = bgp.EC_SUBTYPE_ROUTE_TARGET
+ case "soo":
+ subtype = bgp.EC_SUBTYPE_ROUTE_ORIGIN
+ default:
+ return nil, fmt.Errorf("invalid ext-community (rt|soo):<value> | valid | not-found | invalid")
+ }
+ value = elems[1]
+ }
+ return bgp.ParseExtendedCommunity(subtype, value)
+}
+
+var _regexpCommunity2 = regexp.MustCompile(`(\d+.)*\d+:\d+`)
+
+func ParseCommunityRegexp(arg string) (*regexp.Regexp, error) {
+ i, err := strconv.ParseUint(arg, 10, 32)
+ if err == nil {
+ return regexp.Compile(fmt.Sprintf("^%d:%d$", i>>16, i&0x0000ffff))
+ }
+
+ if _regexpCommunity2.MatchString(arg) {
+ return regexp.Compile(fmt.Sprintf("^%s$", arg))
+ }
+
+ for i, v := range bgp.WellKnownCommunityNameMap {
+ if strings.Replace(strings.ToLower(arg), "_", "-", -1) == v {
+ return regexp.Compile(fmt.Sprintf("^%d:%d$", i>>16, i&0x0000ffff))
+ }
+ }
+
+ return regexp.Compile(arg)
+}
+
+func ParseExtCommunityRegexp(arg string) (bgp.ExtendedCommunityAttrSubType, *regexp.Regexp, error) {
+ var subtype bgp.ExtendedCommunityAttrSubType
+ elems := strings.SplitN(arg, ":", 2)
+ if len(elems) < 2 {
+ return subtype, nil, fmt.Errorf("invalid ext-community format([rt|soo]:<value>)")
+ }
+ switch strings.ToLower(elems[0]) {
+ case "rt":
+ subtype = bgp.EC_SUBTYPE_ROUTE_TARGET
+ case "soo":
+ subtype = bgp.EC_SUBTYPE_ROUTE_ORIGIN
+ default:
+ return subtype, nil, fmt.Errorf("unknown ext-community subtype. rt, soo is supported")
+ }
+ exp, err := ParseCommunityRegexp(elems[1])
+ return subtype, exp, err
+}
+
+func NewCommunitySet(c config.CommunitySet) (*CommunitySet, error) {
+ name := c.CommunitySetName
+ if name == "" {
+ if len(c.CommunityList) == 0 {
+ return nil, nil
+ }
+ return nil, fmt.Errorf("empty community set name")
+ }
+ list := make([]*regexp.Regexp, 0, len(c.CommunityList))
+ for _, x := range c.CommunityList {
+ exp, err := ParseCommunityRegexp(x)
+ if err != nil {
+ return nil, err
+ }
+ list = append(list, exp)
+ }
+ return &CommunitySet{
+ regExpSet: regExpSet{
+ typ: DEFINED_TYPE_COMMUNITY,
+ name: name,
+ list: list,
+ },
+ }, nil
+}
+
+type ExtCommunitySet struct {
+ regExpSet
+ subtypeList []bgp.ExtendedCommunityAttrSubType
+}
+
+func (s *ExtCommunitySet) List() []string {
+ list := make([]string, 0, len(s.list))
+ f := func(idx int, arg string) string {
+ switch s.subtypeList[idx] {
+ case bgp.EC_SUBTYPE_ROUTE_TARGET:
+ return fmt.Sprintf("rt:%s", arg)
+ case bgp.EC_SUBTYPE_ROUTE_ORIGIN:
+ return fmt.Sprintf("soo:%s", arg)
+ case bgp.EC_SUBTYPE_ORIGIN_VALIDATION:
+ return arg
+ default:
+ return fmt.Sprintf("%d:%s", s.subtypeList[idx], arg)
+ }
+ }
+ for idx, exp := range s.list {
+ list = append(list, f(idx, exp.String()))
+ }
+ return list
+}
+
+func (s *ExtCommunitySet) ToConfig() *config.ExtCommunitySet {
+ return &config.ExtCommunitySet{
+ ExtCommunitySetName: s.name,
+ ExtCommunityList: s.List(),
+ }
+}
+
+func (s *ExtCommunitySet) String() string {
+ return strings.Join(s.List(), "\n")
+}
+
+func (s *ExtCommunitySet) MarshalJSON() ([]byte, error) {
+ return json.Marshal(s.ToConfig())
+}
+
+func NewExtCommunitySet(c config.ExtCommunitySet) (*ExtCommunitySet, error) {
+ name := c.ExtCommunitySetName
+ if name == "" {
+ if len(c.ExtCommunityList) == 0 {
+ return nil, nil
+ }
+ return nil, fmt.Errorf("empty ext-community set name")
+ }
+ list := make([]*regexp.Regexp, 0, len(c.ExtCommunityList))
+ subtypeList := make([]bgp.ExtendedCommunityAttrSubType, 0, len(c.ExtCommunityList))
+ for _, x := range c.ExtCommunityList {
+ subtype, exp, err := ParseExtCommunityRegexp(x)
+ if err != nil {
+ return nil, err
+ }
+ list = append(list, exp)
+ subtypeList = append(subtypeList, subtype)
+ }
+ return &ExtCommunitySet{
+ regExpSet: regExpSet{
+ typ: DEFINED_TYPE_EXT_COMMUNITY,
+ name: name,
+ list: list,
+ },
+ subtypeList: subtypeList,
+ }, nil
+}
+
+func (s *ExtCommunitySet) Append(arg DefinedSet) error {
+ err := s.regExpSet.Append(arg)
+ if err != nil {
+ return err
+ }
+ sList := arg.(*ExtCommunitySet).subtypeList
+ s.subtypeList = append(s.subtypeList, sList...)
+ return nil
+}
+
+type LargeCommunitySet struct {
+ regExpSet
+}
+
+func (s *LargeCommunitySet) List() []string {
+ list := make([]string, 0, len(s.list))
+ for _, exp := range s.list {
+ list = append(list, exp.String())
+ }
+ return list
+}
+
+func (s *LargeCommunitySet) ToConfig() *config.LargeCommunitySet {
+ return &config.LargeCommunitySet{
+ LargeCommunitySetName: s.name,
+ LargeCommunityList: s.List(),
+ }
+}
+
+func (s *LargeCommunitySet) String() string {
+ return strings.Join(s.List(), "\n")
+}
+
+func (s *LargeCommunitySet) MarshalJSON() ([]byte, error) {
+ return json.Marshal(s.ToConfig())
+}
+
+var _regexpCommunityLarge = regexp.MustCompile(`\d+:\d+:\d+`)
+
+func ParseLargeCommunityRegexp(arg string) (*regexp.Regexp, error) {
+ if _regexpCommunityLarge.MatchString(arg) {
+ return regexp.Compile(fmt.Sprintf("^%s$", arg))
+ }
+ exp, err := regexp.Compile(arg)
+ if err != nil {
+ return nil, fmt.Errorf("invalid large-community format: %v", err)
+ }
+
+ return exp, nil
+}
+
+func NewLargeCommunitySet(c config.LargeCommunitySet) (*LargeCommunitySet, error) {
+ name := c.LargeCommunitySetName
+ if name == "" {
+ if len(c.LargeCommunityList) == 0 {
+ return nil, nil
+ }
+ return nil, fmt.Errorf("empty large community set name")
+ }
+ list := make([]*regexp.Regexp, 0, len(c.LargeCommunityList))
+ for _, x := range c.LargeCommunityList {
+ exp, err := ParseLargeCommunityRegexp(x)
+ if err != nil {
+ return nil, err
+ }
+ list = append(list, exp)
+ }
+ return &LargeCommunitySet{
+ regExpSet: regExpSet{
+ typ: DEFINED_TYPE_LARGE_COMMUNITY,
+ name: name,
+ list: list,
+ },
+ }, nil
+}
+
+type Condition interface {
+ Name() string
+ Type() ConditionType
+ Evaluate(*Path, *PolicyOptions) bool
+ Set() DefinedSet
+}
+
+type NextHopCondition struct {
+ set *NextHopSet
+}
+
+func (c *NextHopCondition) Type() ConditionType {
+ return CONDITION_NEXT_HOP
+}
+
+func (c *NextHopCondition) Set() DefinedSet {
+ return c.set
+}
+
+func (c *NextHopCondition) Name() string { return "" }
+
+func (c *NextHopCondition) String() string {
+ return c.set.String()
+}
+
+// compare next-hop ipaddress of this condition and source address of path
+// and, subsequent comparisons are skipped if that matches the conditions.
+// If NextHopSet's length is zero, return true.
+func (c *NextHopCondition) Evaluate(path *Path, options *PolicyOptions) bool {
+ if len(c.set.list) == 0 {
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ }).Debug("NextHop doesn't have elements")
+ return true
+ }
+
+ nexthop := path.GetNexthop()
+
+ // In cases where we advertise routes from iBGP to eBGP, we want to filter
+ // on the "original" nexthop. The current paths' nexthop has already been
+ // set and is ready to be advertised as per:
+ // https://tools.ietf.org/html/rfc4271#section-5.1.3
+ if options != nil && options.OldNextHop != nil &&
+ !options.OldNextHop.IsUnspecified() && !options.OldNextHop.Equal(nexthop) {
+ nexthop = options.OldNextHop
+ }
+
+ if nexthop == nil {
+ return false
+ }
+
+ for _, n := range c.set.list {
+ if n.Contains(nexthop) {
+ return true
+ }
+ }
+
+ return false
+}
+
+func NewNextHopCondition(c []string) (*NextHopCondition, error) {
+ if len(c) == 0 {
+ return nil, nil
+ }
+
+ list, err := NewNextHopSet(c)
+ if err != nil {
+ return nil, nil
+ }
+
+ return &NextHopCondition{
+ set: list,
+ }, nil
+}
+
+type PrefixCondition struct {
+ set *PrefixSet
+ option MatchOption
+}
+
+func (c *PrefixCondition) Type() ConditionType {
+ return CONDITION_PREFIX
+}
+
+func (c *PrefixCondition) Set() DefinedSet {
+ return c.set
+}
+
+func (c *PrefixCondition) Option() MatchOption {
+ return c.option
+}
+
+// compare prefixes in this condition and nlri of path and
+// subsequent comparison is skipped if that matches the conditions.
+// If PrefixList's length is zero, return true.
+func (c *PrefixCondition) Evaluate(path *Path, _ *PolicyOptions) bool {
+ var key string
+ var masklen uint8
+ keyf := func(ip net.IP, ones int) string {
+ var buffer bytes.Buffer
+ for i := 0; i < len(ip) && i < ones; i++ {
+ buffer.WriteString(fmt.Sprintf("%08b", ip[i]))
+ }
+ return buffer.String()[:ones]
+ }
+ family := path.GetRouteFamily()
+ switch family {
+ case bgp.RF_IPv4_UC:
+ masklen = path.GetNlri().(*bgp.IPAddrPrefix).Length
+ key = keyf(path.GetNlri().(*bgp.IPAddrPrefix).Prefix, int(masklen))
+ case bgp.RF_IPv6_UC:
+ masklen = path.GetNlri().(*bgp.IPv6AddrPrefix).Length
+ key = keyf(path.GetNlri().(*bgp.IPv6AddrPrefix).Prefix, int(masklen))
+ default:
+ return false
+ }
+ if family != c.set.family {
+ return false
+ }
+
+ result := false
+ _, ps, ok := c.set.tree.LongestPrefix(key)
+ if ok {
+ for _, p := range ps.([]*Prefix) {
+ if p.MasklengthRangeMin <= masklen && masklen <= p.MasklengthRangeMax {
+ result = true
+ break
+ }
+ }
+ }
+
+ if c.option == MATCH_OPTION_INVERT {
+ result = !result
+ }
+
+ return result
+}
+
+func (c *PrefixCondition) Name() string { return c.set.name }
+
+func NewPrefixCondition(c config.MatchPrefixSet) (*PrefixCondition, error) {
+ if c.PrefixSet == "" {
+ return nil, nil
+ }
+ o, err := NewMatchOption(c.MatchSetOptions)
+ if err != nil {
+ return nil, err
+ }
+ return &PrefixCondition{
+ set: &PrefixSet{
+ name: c.PrefixSet,
+ },
+ option: o,
+ }, nil
+}
+
+type NeighborCondition struct {
+ set *NeighborSet
+ option MatchOption
+}
+
+func (c *NeighborCondition) Type() ConditionType {
+ return CONDITION_NEIGHBOR
+}
+
+func (c *NeighborCondition) Set() DefinedSet {
+ return c.set
+}
+
+func (c *NeighborCondition) Option() MatchOption {
+ return c.option
+}
+
+// compare neighbor ipaddress of this condition and source address of path
+// and, subsequent comparisons are skipped if that matches the conditions.
+// If NeighborList's length is zero, return true.
+func (c *NeighborCondition) Evaluate(path *Path, options *PolicyOptions) bool {
+ if len(c.set.list) == 0 {
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ }).Debug("NeighborList doesn't have elements")
+ return true
+ }
+
+ neighbor := path.GetSource().Address
+ if options != nil && options.Info != nil && options.Info.Address != nil {
+ neighbor = options.Info.Address
+ }
+
+ if neighbor == nil {
+ return false
+ }
+ result := false
+ for _, n := range c.set.list {
+ if n.Contains(neighbor) {
+ result = true
+ break
+ }
+ }
+
+ if c.option == MATCH_OPTION_INVERT {
+ result = !result
+ }
+
+ return result
+}
+
+func (c *NeighborCondition) Name() string { return c.set.name }
+
+func NewNeighborCondition(c config.MatchNeighborSet) (*NeighborCondition, error) {
+ if c.NeighborSet == "" {
+ return nil, nil
+ }
+ o, err := NewMatchOption(c.MatchSetOptions)
+ if err != nil {
+ return nil, err
+ }
+ return &NeighborCondition{
+ set: &NeighborSet{
+ name: c.NeighborSet,
+ },
+ option: o,
+ }, nil
+}
+
+type AsPathCondition struct {
+ set *AsPathSet
+ option MatchOption
+}
+
+func (c *AsPathCondition) Type() ConditionType {
+ return CONDITION_AS_PATH
+}
+
+func (c *AsPathCondition) Set() DefinedSet {
+ return c.set
+}
+
+func (c *AsPathCondition) Option() MatchOption {
+ return c.option
+}
+
+func (c *AsPathCondition) Evaluate(path *Path, _ *PolicyOptions) bool {
+ if len(c.set.singleList) > 0 {
+ aspath := path.GetAsSeqList()
+ for _, m := range c.set.singleList {
+ result := m.Match(aspath)
+ if c.option == MATCH_OPTION_ALL && !result {
+ return false
+ }
+ if c.option == MATCH_OPTION_ANY && result {
+ return true
+ }
+ if c.option == MATCH_OPTION_INVERT && result {
+ return false
+ }
+ }
+ }
+ if len(c.set.list) > 0 {
+ aspath := path.GetAsString()
+ for _, r := range c.set.list {
+ result := r.MatchString(aspath)
+ if c.option == MATCH_OPTION_ALL && !result {
+ return false
+ }
+ if c.option == MATCH_OPTION_ANY && result {
+ return true
+ }
+ if c.option == MATCH_OPTION_INVERT && result {
+ return false
+ }
+ }
+ }
+ if c.option == MATCH_OPTION_ANY {
+ return false
+ }
+ return true
+}
+
+func (c *AsPathCondition) Name() string { return c.set.name }
+
+func NewAsPathCondition(c config.MatchAsPathSet) (*AsPathCondition, error) {
+ if c.AsPathSet == "" {
+ return nil, nil
+ }
+ o, err := NewMatchOption(c.MatchSetOptions)
+ if err != nil {
+ return nil, err
+ }
+ return &AsPathCondition{
+ set: &AsPathSet{
+ name: c.AsPathSet,
+ },
+ option: o,
+ }, nil
+}
+
+type CommunityCondition struct {
+ set *CommunitySet
+ option MatchOption
+}
+
+func (c *CommunityCondition) Type() ConditionType {
+ return CONDITION_COMMUNITY
+}
+
+func (c *CommunityCondition) Set() DefinedSet {
+ return c.set
+}
+
+func (c *CommunityCondition) Option() MatchOption {
+ return c.option
+}
+
+func (c *CommunityCondition) Evaluate(path *Path, _ *PolicyOptions) bool {
+ cs := path.GetCommunities()
+ result := false
+ for _, x := range c.set.list {
+ result = false
+ for _, y := range cs {
+ if x.MatchString(fmt.Sprintf("%d:%d", y>>16, y&0x0000ffff)) {
+ result = true
+ break
+ }
+ }
+ if c.option == MATCH_OPTION_ALL && !result {
+ break
+ }
+ if (c.option == MATCH_OPTION_ANY || c.option == MATCH_OPTION_INVERT) && result {
+ break
+ }
+ }
+ if c.option == MATCH_OPTION_INVERT {
+ result = !result
+ }
+ return result
+}
+
+func (c *CommunityCondition) Name() string { return c.set.name }
+
+func NewCommunityCondition(c config.MatchCommunitySet) (*CommunityCondition, error) {
+ if c.CommunitySet == "" {
+ return nil, nil
+ }
+ o, err := NewMatchOption(c.MatchSetOptions)
+ if err != nil {
+ return nil, err
+ }
+ return &CommunityCondition{
+ set: &CommunitySet{
+ regExpSet: regExpSet{
+ name: c.CommunitySet,
+ },
+ },
+ option: o,
+ }, nil
+}
+
+type ExtCommunityCondition struct {
+ set *ExtCommunitySet
+ option MatchOption
+}
+
+func (c *ExtCommunityCondition) Type() ConditionType {
+ return CONDITION_EXT_COMMUNITY
+}
+
+func (c *ExtCommunityCondition) Set() DefinedSet {
+ return c.set
+}
+
+func (c *ExtCommunityCondition) Option() MatchOption {
+ return c.option
+}
+
+func (c *ExtCommunityCondition) Evaluate(path *Path, _ *PolicyOptions) bool {
+ es := path.GetExtCommunities()
+ result := false
+ for _, x := range es {
+ result = false
+ typ, subtype := x.GetTypes()
+ // match only with transitive community. see RFC7153
+ if typ >= 0x3f {
+ continue
+ }
+ for idx, y := range c.set.list {
+ if subtype == c.set.subtypeList[idx] && y.MatchString(x.String()) {
+ result = true
+ break
+ }
+ }
+ if c.option == MATCH_OPTION_ALL && !result {
+ break
+ }
+ if c.option == MATCH_OPTION_ANY && result {
+ break
+ }
+ }
+ if c.option == MATCH_OPTION_INVERT {
+ result = !result
+ }
+ return result
+}
+
+func (c *ExtCommunityCondition) Name() string { return c.set.name }
+
+func NewExtCommunityCondition(c config.MatchExtCommunitySet) (*ExtCommunityCondition, error) {
+ if c.ExtCommunitySet == "" {
+ return nil, nil
+ }
+ o, err := NewMatchOption(c.MatchSetOptions)
+ if err != nil {
+ return nil, err
+ }
+ return &ExtCommunityCondition{
+ set: &ExtCommunitySet{
+ regExpSet: regExpSet{
+ name: c.ExtCommunitySet,
+ },
+ },
+ option: o,
+ }, nil
+}
+
+type LargeCommunityCondition struct {
+ set *LargeCommunitySet
+ option MatchOption
+}
+
+func (c *LargeCommunityCondition) Type() ConditionType {
+ return CONDITION_LARGE_COMMUNITY
+}
+
+func (c *LargeCommunityCondition) Set() DefinedSet {
+ return c.set
+}
+
+func (c *LargeCommunityCondition) Option() MatchOption {
+ return c.option
+}
+
+func (c *LargeCommunityCondition) Evaluate(path *Path, _ *PolicyOptions) bool {
+ result := false
+ cs := path.GetLargeCommunities()
+ for _, x := range c.set.list {
+ result = false
+ for _, y := range cs {
+ if x.MatchString(y.String()) {
+ result = true
+ break
+ }
+ }
+ if c.option == MATCH_OPTION_ALL && !result {
+ break
+ }
+ if (c.option == MATCH_OPTION_ANY || c.option == MATCH_OPTION_INVERT) && result {
+ break
+ }
+ }
+ if c.option == MATCH_OPTION_INVERT {
+ result = !result
+ }
+ return result
+}
+
+func (c *LargeCommunityCondition) Name() string { return c.set.name }
+
+func NewLargeCommunityCondition(c config.MatchLargeCommunitySet) (*LargeCommunityCondition, error) {
+ if c.LargeCommunitySet == "" {
+ return nil, nil
+ }
+ o, err := NewMatchOption(c.MatchSetOptions)
+ if err != nil {
+ return nil, err
+ }
+ return &LargeCommunityCondition{
+ set: &LargeCommunitySet{
+ regExpSet: regExpSet{
+ name: c.LargeCommunitySet,
+ },
+ },
+ option: o,
+ }, nil
+}
+
+type AsPathLengthCondition struct {
+ length uint32
+ operator AttributeComparison
+}
+
+func (c *AsPathLengthCondition) Type() ConditionType {
+ return CONDITION_AS_PATH_LENGTH
+}
+
+// compare AS_PATH length in the message's AS_PATH attribute with
+// the one in condition.
+func (c *AsPathLengthCondition) Evaluate(path *Path, _ *PolicyOptions) bool {
+
+ length := uint32(path.GetAsPathLen())
+ result := false
+ switch c.operator {
+ case ATTRIBUTE_EQ:
+ result = c.length == length
+ case ATTRIBUTE_GE:
+ result = c.length <= length
+ case ATTRIBUTE_LE:
+ result = c.length >= length
+ }
+
+ return result
+}
+
+func (c *AsPathLengthCondition) Set() DefinedSet {
+ return nil
+}
+
+func (c *AsPathLengthCondition) Name() string { return "" }
+
+func (c *AsPathLengthCondition) String() string {
+ return fmt.Sprintf("%s%d", c.operator, c.length)
+}
+
+func NewAsPathLengthCondition(c config.AsPathLength) (*AsPathLengthCondition, error) {
+ if c.Value == 0 && c.Operator == "" {
+ return nil, nil
+ }
+ var op AttributeComparison
+ if i := c.Operator.ToInt(); i < 0 {
+ return nil, fmt.Errorf("invalid as path length operator: %s", c.Operator)
+ } else {
+ // take mod 3 because we have extended openconfig attribute-comparison
+ // for simple configuration. see config.AttributeComparison definition
+ op = AttributeComparison(i % 3)
+ }
+ return &AsPathLengthCondition{
+ length: c.Value,
+ operator: op,
+ }, nil
+}
+
+type RpkiValidationCondition struct {
+ result config.RpkiValidationResultType
+}
+
+func (c *RpkiValidationCondition) Type() ConditionType {
+ return CONDITION_RPKI
+}
+
+func (c *RpkiValidationCondition) Evaluate(path *Path, options *PolicyOptions) bool {
+ if options != nil && options.ValidationResult != nil {
+ return c.result == options.ValidationResult.Status
+ }
+ return false
+}
+
+func (c *RpkiValidationCondition) Set() DefinedSet {
+ return nil
+}
+
+func (c *RpkiValidationCondition) Name() string { return "" }
+
+func (c *RpkiValidationCondition) String() string {
+ return string(c.result)
+}
+
+func NewRpkiValidationCondition(c config.RpkiValidationResultType) (*RpkiValidationCondition, error) {
+ if c == config.RpkiValidationResultType("") || c == config.RPKI_VALIDATION_RESULT_TYPE_NONE {
+ return nil, nil
+ }
+ return &RpkiValidationCondition{
+ result: c,
+ }, nil
+}
+
+type RouteTypeCondition struct {
+ typ config.RouteType
+}
+
+func (c *RouteTypeCondition) Type() ConditionType {
+ return CONDITION_ROUTE_TYPE
+}
+
+func (c *RouteTypeCondition) Evaluate(path *Path, _ *PolicyOptions) bool {
+ switch c.typ {
+ case config.ROUTE_TYPE_LOCAL:
+ return path.IsLocal()
+ case config.ROUTE_TYPE_INTERNAL:
+ return !path.IsLocal() && path.IsIBGP()
+ case config.ROUTE_TYPE_EXTERNAL:
+ return !path.IsLocal() && !path.IsIBGP()
+ }
+ return false
+}
+
+func (c *RouteTypeCondition) Set() DefinedSet {
+ return nil
+}
+
+func (c *RouteTypeCondition) Name() string { return "" }
+
+func (c *RouteTypeCondition) String() string {
+ return string(c.typ)
+}
+
+func NewRouteTypeCondition(c config.RouteType) (*RouteTypeCondition, error) {
+ if string(c) == "" || c == config.ROUTE_TYPE_NONE {
+ return nil, nil
+ }
+ if err := c.Validate(); err != nil {
+ return nil, err
+ }
+ return &RouteTypeCondition{
+ typ: c,
+ }, nil
+}
+
+type AfiSafiInCondition struct {
+ routeFamilies []bgp.RouteFamily
+}
+
+func (c *AfiSafiInCondition) Type() ConditionType {
+ return CONDITION_AFI_SAFI_IN
+}
+
+func (c *AfiSafiInCondition) Evaluate(path *Path, _ *PolicyOptions) bool {
+ for _, rf := range c.routeFamilies {
+ if path.GetRouteFamily() == rf {
+ return true
+ }
+ }
+ return false
+}
+
+func (c *AfiSafiInCondition) Set() DefinedSet {
+ return nil
+}
+
+func (c *AfiSafiInCondition) Name() string { return "" }
+
+func (c *AfiSafiInCondition) String() string {
+ tmp := make([]string, 0, len(c.routeFamilies))
+ for _, afiSafi := range c.routeFamilies {
+ tmp = append(tmp, afiSafi.String())
+ }
+ return strings.Join(tmp, " ")
+}
+
+func NewAfiSafiInCondition(afiSafInConfig []config.AfiSafiType) (*AfiSafiInCondition, error) {
+ if afiSafInConfig == nil {
+ return nil, nil
+ }
+
+ routeFamilies := make([]bgp.RouteFamily, 0, len(afiSafInConfig))
+ for _, afiSafiValue := range afiSafInConfig {
+ if err := afiSafiValue.Validate(); err != nil {
+ return nil, err
+ }
+ rf, err := bgp.GetRouteFamily(string(afiSafiValue))
+ if err != nil {
+ return nil, err
+ }
+ routeFamilies = append(routeFamilies, rf)
+ }
+ return &AfiSafiInCondition{
+ routeFamilies: routeFamilies,
+ }, nil
+}
+
+type Action interface {
+ Type() ActionType
+ Apply(*Path, *PolicyOptions) *Path
+ String() string
+}
+
+type RoutingAction struct {
+ AcceptRoute bool
+}
+
+func (a *RoutingAction) Type() ActionType {
+ return ACTION_ROUTING
+}
+
+func (a *RoutingAction) Apply(path *Path, _ *PolicyOptions) *Path {
+ if a.AcceptRoute {
+ return path
+ }
+ return nil
+}
+
+func (a *RoutingAction) String() string {
+ action := "reject"
+ if a.AcceptRoute {
+ action = "accept"
+ }
+ return action
+}
+
+func NewRoutingAction(c config.RouteDisposition) (*RoutingAction, error) {
+ var accept bool
+ switch c {
+ case config.RouteDisposition(""), config.ROUTE_DISPOSITION_NONE:
+ return nil, nil
+ case config.ROUTE_DISPOSITION_ACCEPT_ROUTE:
+ accept = true
+ case config.ROUTE_DISPOSITION_REJECT_ROUTE:
+ accept = false
+ default:
+ return nil, fmt.Errorf("invalid route disposition")
+ }
+ return &RoutingAction{
+ AcceptRoute: accept,
+ }, nil
+}
+
+type CommunityAction struct {
+ action config.BgpSetCommunityOptionType
+ list []uint32
+ removeList []*regexp.Regexp
+}
+
+func RegexpRemoveCommunities(path *Path, exps []*regexp.Regexp) {
+ comms := path.GetCommunities()
+ newComms := make([]uint32, 0, len(comms))
+ for _, comm := range comms {
+ c := fmt.Sprintf("%d:%d", comm>>16, comm&0x0000ffff)
+ match := false
+ for _, exp := range exps {
+ if exp.MatchString(c) {
+ match = true
+ break
+ }
+ }
+ if !match {
+ newComms = append(newComms, comm)
+ }
+ }
+ path.SetCommunities(newComms, true)
+}
+
+func RegexpRemoveExtCommunities(path *Path, exps []*regexp.Regexp, subtypes []bgp.ExtendedCommunityAttrSubType) {
+ comms := path.GetExtCommunities()
+ newComms := make([]bgp.ExtendedCommunityInterface, 0, len(comms))
+ for _, comm := range comms {
+ match := false
+ typ, subtype := comm.GetTypes()
+ // match only with transitive community. see RFC7153
+ if typ >= 0x3f {
+ continue
+ }
+ for idx, exp := range exps {
+ if subtype == subtypes[idx] && exp.MatchString(comm.String()) {
+ match = true
+ break
+ }
+ }
+ if !match {
+ newComms = append(newComms, comm)
+ }
+ }
+ path.SetExtCommunities(newComms, true)
+}
+
+func RegexpRemoveLargeCommunities(path *Path, exps []*regexp.Regexp) {
+ comms := path.GetLargeCommunities()
+ newComms := make([]*bgp.LargeCommunity, 0, len(comms))
+ for _, comm := range comms {
+ c := comm.String()
+ match := false
+ for _, exp := range exps {
+ if exp.MatchString(c) {
+ match = true
+ break
+ }
+ }
+ if !match {
+ newComms = append(newComms, comm)
+ }
+ }
+ path.SetLargeCommunities(newComms, true)
+}
+
+func (a *CommunityAction) Type() ActionType {
+ return ACTION_COMMUNITY
+}
+
+func (a *CommunityAction) Apply(path *Path, _ *PolicyOptions) *Path {
+ switch a.action {
+ case config.BGP_SET_COMMUNITY_OPTION_TYPE_ADD:
+ path.SetCommunities(a.list, false)
+ case config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE:
+ RegexpRemoveCommunities(path, a.removeList)
+ case config.BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE:
+ path.SetCommunities(a.list, true)
+ }
+ return path
+}
+
+func (a *CommunityAction) ToConfig() *config.SetCommunity {
+ cs := make([]string, 0, len(a.list)+len(a.removeList))
+ for _, comm := range a.list {
+ c := fmt.Sprintf("%d:%d", comm>>16, comm&0x0000ffff)
+ cs = append(cs, c)
+ }
+ for _, exp := range a.removeList {
+ cs = append(cs, exp.String())
+ }
+ return &config.SetCommunity{
+ Options: string(a.action),
+ SetCommunityMethod: config.SetCommunityMethod{CommunitiesList: cs},
+ }
+}
+
+func (a *CommunityAction) MarshalJSON() ([]byte, error) {
+ return json.Marshal(a.ToConfig())
+}
+
+// TODO: this is not efficient use of regexp, probably slow
+var _regexpCommunityReplaceString = regexp.MustCompile(`[\^\$]`)
+
+func (a *CommunityAction) String() string {
+ list := a.ToConfig().SetCommunityMethod.CommunitiesList
+ l := _regexpCommunityReplaceString.ReplaceAllString(strings.Join(list, ", "), "")
+ return fmt.Sprintf("%s[%s]", a.action, l)
+}
+
+func NewCommunityAction(c config.SetCommunity) (*CommunityAction, error) {
+ a, ok := CommunityOptionValueMap[strings.ToLower(c.Options)]
+ if !ok {
+ if len(c.SetCommunityMethod.CommunitiesList) == 0 {
+ return nil, nil
+ }
+ return nil, fmt.Errorf("invalid option name: %s", c.Options)
+ }
+ var list []uint32
+ var removeList []*regexp.Regexp
+ if a == config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE {
+ removeList = make([]*regexp.Regexp, 0, len(c.SetCommunityMethod.CommunitiesList))
+ } else {
+ list = make([]uint32, 0, len(c.SetCommunityMethod.CommunitiesList))
+ }
+ for _, x := range c.SetCommunityMethod.CommunitiesList {
+ if a == config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE {
+ exp, err := ParseCommunityRegexp(x)
+ if err != nil {
+ return nil, err
+ }
+ removeList = append(removeList, exp)
+ } else {
+ comm, err := ParseCommunity(x)
+ if err != nil {
+ return nil, err
+ }
+ list = append(list, comm)
+ }
+ }
+ return &CommunityAction{
+ action: a,
+ list: list,
+ removeList: removeList,
+ }, nil
+}
+
+type ExtCommunityAction struct {
+ action config.BgpSetCommunityOptionType
+ list []bgp.ExtendedCommunityInterface
+ removeList []*regexp.Regexp
+ subtypeList []bgp.ExtendedCommunityAttrSubType
+}
+
+func (a *ExtCommunityAction) Type() ActionType {
+ return ACTION_EXT_COMMUNITY
+}
+
+func (a *ExtCommunityAction) Apply(path *Path, _ *PolicyOptions) *Path {
+ switch a.action {
+ case config.BGP_SET_COMMUNITY_OPTION_TYPE_ADD:
+ path.SetExtCommunities(a.list, false)
+ case config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE:
+ RegexpRemoveExtCommunities(path, a.removeList, a.subtypeList)
+ case config.BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE:
+ path.SetExtCommunities(a.list, true)
+ }
+ return path
+}
+
+func (a *ExtCommunityAction) ToConfig() *config.SetExtCommunity {
+ cs := make([]string, 0, len(a.list)+len(a.removeList))
+ f := func(idx int, arg string) string {
+ switch a.subtypeList[idx] {
+ case bgp.EC_SUBTYPE_ROUTE_TARGET:
+ return fmt.Sprintf("rt:%s", arg)
+ case bgp.EC_SUBTYPE_ROUTE_ORIGIN:
+ return fmt.Sprintf("soo:%s", arg)
+ case bgp.EC_SUBTYPE_ORIGIN_VALIDATION:
+ return arg
+ default:
+ return fmt.Sprintf("%d:%s", a.subtypeList[idx], arg)
+ }
+ }
+ for idx, c := range a.list {
+ cs = append(cs, f(idx, c.String()))
+ }
+ for idx, exp := range a.removeList {
+ cs = append(cs, f(idx, exp.String()))
+ }
+ return &config.SetExtCommunity{
+ Options: string(a.action),
+ SetExtCommunityMethod: config.SetExtCommunityMethod{
+ CommunitiesList: cs,
+ },
+ }
+}
+
+func (a *ExtCommunityAction) String() string {
+ list := a.ToConfig().SetExtCommunityMethod.CommunitiesList
+ l := _regexpCommunityReplaceString.ReplaceAllString(strings.Join(list, ", "), "")
+ return fmt.Sprintf("%s[%s]", a.action, l)
+}
+
+func (a *ExtCommunityAction) MarshalJSON() ([]byte, error) {
+ return json.Marshal(a.ToConfig())
+}
+
+func NewExtCommunityAction(c config.SetExtCommunity) (*ExtCommunityAction, error) {
+ a, ok := CommunityOptionValueMap[strings.ToLower(c.Options)]
+ if !ok {
+ if len(c.SetExtCommunityMethod.CommunitiesList) == 0 {
+ return nil, nil
+ }
+ return nil, fmt.Errorf("invalid option name: %s", c.Options)
+ }
+ var list []bgp.ExtendedCommunityInterface
+ var removeList []*regexp.Regexp
+ subtypeList := make([]bgp.ExtendedCommunityAttrSubType, 0, len(c.SetExtCommunityMethod.CommunitiesList))
+ if a == config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE {
+ removeList = make([]*regexp.Regexp, 0, len(c.SetExtCommunityMethod.CommunitiesList))
+ } else {
+ list = make([]bgp.ExtendedCommunityInterface, 0, len(c.SetExtCommunityMethod.CommunitiesList))
+ }
+ for _, x := range c.SetExtCommunityMethod.CommunitiesList {
+ if a == config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE {
+ subtype, exp, err := ParseExtCommunityRegexp(x)
+ if err != nil {
+ return nil, err
+ }
+ removeList = append(removeList, exp)
+ subtypeList = append(subtypeList, subtype)
+ } else {
+ comm, err := ParseExtCommunity(x)
+ if err != nil {
+ return nil, err
+ }
+ list = append(list, comm)
+ _, subtype := comm.GetTypes()
+ subtypeList = append(subtypeList, subtype)
+ }
+ }
+ return &ExtCommunityAction{
+ action: a,
+ list: list,
+ removeList: removeList,
+ subtypeList: subtypeList,
+ }, nil
+}
+
+type LargeCommunityAction struct {
+ action config.BgpSetCommunityOptionType
+ list []*bgp.LargeCommunity
+ removeList []*regexp.Regexp
+}
+
+func (a *LargeCommunityAction) Type() ActionType {
+ return ACTION_LARGE_COMMUNITY
+}
+
+func (a *LargeCommunityAction) Apply(path *Path, _ *PolicyOptions) *Path {
+ switch a.action {
+ case config.BGP_SET_COMMUNITY_OPTION_TYPE_ADD:
+ path.SetLargeCommunities(a.list, false)
+ case config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE:
+ RegexpRemoveLargeCommunities(path, a.removeList)
+ case config.BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE:
+ path.SetLargeCommunities(a.list, true)
+ }
+ return path
+}
+
+func (a *LargeCommunityAction) ToConfig() *config.SetLargeCommunity {
+ cs := make([]string, 0, len(a.list)+len(a.removeList))
+ for _, comm := range a.list {
+ cs = append(cs, comm.String())
+ }
+ for _, exp := range a.removeList {
+ cs = append(cs, exp.String())
+ }
+ return &config.SetLargeCommunity{
+ SetLargeCommunityMethod: config.SetLargeCommunityMethod{CommunitiesList: cs},
+ Options: config.BgpSetCommunityOptionType(a.action),
+ }
+}
+
+func (a *LargeCommunityAction) String() string {
+ list := a.ToConfig().SetLargeCommunityMethod.CommunitiesList
+ l := _regexpCommunityReplaceString.ReplaceAllString(strings.Join(list, ", "), "")
+ return fmt.Sprintf("%s[%s]", a.action, l)
+}
+
+func (a *LargeCommunityAction) MarshalJSON() ([]byte, error) {
+ return json.Marshal(a.ToConfig())
+}
+
+func NewLargeCommunityAction(c config.SetLargeCommunity) (*LargeCommunityAction, error) {
+ a, ok := CommunityOptionValueMap[strings.ToLower(string(c.Options))]
+ if !ok {
+ if len(c.SetLargeCommunityMethod.CommunitiesList) == 0 {
+ return nil, nil
+ }
+ return nil, fmt.Errorf("invalid option name: %s", c.Options)
+ }
+ var list []*bgp.LargeCommunity
+ var removeList []*regexp.Regexp
+ if a == config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE {
+ removeList = make([]*regexp.Regexp, 0, len(c.SetLargeCommunityMethod.CommunitiesList))
+ } else {
+ list = make([]*bgp.LargeCommunity, 0, len(c.SetLargeCommunityMethod.CommunitiesList))
+ }
+ for _, x := range c.SetLargeCommunityMethod.CommunitiesList {
+ if a == config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE {
+ exp, err := ParseLargeCommunityRegexp(x)
+ if err != nil {
+ return nil, err
+ }
+ removeList = append(removeList, exp)
+ } else {
+ comm, err := bgp.ParseLargeCommunity(x)
+ if err != nil {
+ return nil, err
+ }
+ list = append(list, comm)
+ }
+ }
+ return &LargeCommunityAction{
+ action: a,
+ list: list,
+ removeList: removeList,
+ }, nil
+
+}
+
+type MedAction struct {
+ value int64
+ action MedActionType
+}
+
+func (a *MedAction) Type() ActionType {
+ return ACTION_MED
+}
+
+func (a *MedAction) Apply(path *Path, _ *PolicyOptions) *Path {
+ var err error
+ switch a.action {
+ case MED_ACTION_MOD:
+ err = path.SetMed(a.value, false)
+ case MED_ACTION_REPLACE:
+ err = path.SetMed(a.value, true)
+ }
+
+ if err != nil {
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ "Type": "Med Action",
+ "Error": err,
+ }).Warn("Could not set Med on path")
+ }
+ return path
+}
+
+func (a *MedAction) ToConfig() config.BgpSetMedType {
+ if a.action == MED_ACTION_MOD && a.value > 0 {
+ return config.BgpSetMedType(fmt.Sprintf("+%d", a.value))
+ }
+ return config.BgpSetMedType(fmt.Sprintf("%d", a.value))
+}
+
+func (a *MedAction) String() string {
+ return string(a.ToConfig())
+}
+
+func (a *MedAction) MarshalJSON() ([]byte, error) {
+ return json.Marshal(a.ToConfig())
+}
+
+var _regexpParseMedAction = regexp.MustCompile(`^(\+|\-)?(\d+)$`)
+
+func NewMedAction(c config.BgpSetMedType) (*MedAction, error) {
+ if string(c) == "" {
+ return nil, nil
+ }
+
+ elems := _regexpParseMedAction.FindStringSubmatch(string(c))
+ if len(elems) != 3 {
+ return nil, fmt.Errorf("invalid med action format")
+ }
+ action := MED_ACTION_REPLACE
+ switch elems[1] {
+ case "+", "-":
+ action = MED_ACTION_MOD
+ }
+ value, _ := strconv.ParseInt(string(c), 10, 64)
+ return &MedAction{
+ value: value,
+ action: action,
+ }, nil
+}
+
+func NewMedActionFromApiStruct(action MedActionType, value int64) *MedAction {
+ return &MedAction{action: action, value: value}
+}
+
+type LocalPrefAction struct {
+ value uint32
+}
+
+func (a *LocalPrefAction) Type() ActionType {
+ return ACTION_LOCAL_PREF
+}
+
+func (a *LocalPrefAction) Apply(path *Path, _ *PolicyOptions) *Path {
+ path.setPathAttr(bgp.NewPathAttributeLocalPref(a.value))
+ return path
+}
+
+func (a *LocalPrefAction) ToConfig() uint32 {
+ return a.value
+}
+
+func (a *LocalPrefAction) String() string {
+ return fmt.Sprintf("%d", a.value)
+}
+
+func (a *LocalPrefAction) MarshalJSON() ([]byte, error) {
+ return json.Marshal(a.ToConfig())
+}
+
+func NewLocalPrefAction(value uint32) (*LocalPrefAction, error) {
+ if value == 0 {
+ return nil, nil
+ }
+ return &LocalPrefAction{
+ value: value,
+ }, nil
+}
+
+type AsPathPrependAction struct {
+ asn uint32
+ useLeftMost bool
+ repeat uint8
+}
+
+func (a *AsPathPrependAction) Type() ActionType {
+ return ACTION_AS_PATH_PREPEND
+}
+
+func (a *AsPathPrependAction) Apply(path *Path, option *PolicyOptions) *Path {
+ var asn uint32
+ if a.useLeftMost {
+ aspath := path.GetAsSeqList()
+ if len(aspath) == 0 {
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ "Type": "AsPathPrepend Action",
+ }).Warn("aspath length is zero.")
+ return path
+ }
+ asn = aspath[0]
+ if asn == 0 {
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ "Type": "AsPathPrepend Action",
+ }).Warn("left-most ASN is not seq")
+ return path
+ }
+ } else {
+ asn = a.asn
+ }
+
+ confed := option != nil && option.Info.Confederation
+ path.PrependAsn(asn, a.repeat, confed)
+
+ return path
+}
+
+func (a *AsPathPrependAction) ToConfig() *config.SetAsPathPrepend {
+ return &config.SetAsPathPrepend{
+ RepeatN: uint8(a.repeat),
+ As: func() string {
+ if a.useLeftMost {
+ return "last-as"
+ }
+ return fmt.Sprintf("%d", a.asn)
+ }(),
+ }
+}
+
+func (a *AsPathPrependAction) String() string {
+ c := a.ToConfig()
+ return fmt.Sprintf("prepend %s %d times", c.As, c.RepeatN)
+}
+
+func (a *AsPathPrependAction) MarshalJSON() ([]byte, error) {
+ return json.Marshal(a.ToConfig())
+}
+
+// NewAsPathPrependAction creates AsPathPrependAction object.
+// If ASN cannot be parsed, nil will be returned.
+func NewAsPathPrependAction(action config.SetAsPathPrepend) (*AsPathPrependAction, error) {
+ a := &AsPathPrependAction{
+ repeat: action.RepeatN,
+ }
+ switch action.As {
+ case "":
+ if a.repeat == 0 {
+ return nil, nil
+ }
+ return nil, fmt.Errorf("specify as to prepend")
+ case "last-as":
+ a.useLeftMost = true
+ default:
+ asn, err := strconv.ParseUint(action.As, 10, 32)
+ if err != nil {
+ return nil, fmt.Errorf("As number string invalid")
+ }
+ a.asn = uint32(asn)
+ }
+ return a, nil
+}
+
+type NexthopAction struct {
+ value net.IP
+ self bool
+}
+
+func (a *NexthopAction) Type() ActionType {
+ return ACTION_NEXTHOP
+}
+
+func (a *NexthopAction) Apply(path *Path, options *PolicyOptions) *Path {
+ if a.self {
+ if options != nil && options.Info != nil && options.Info.LocalAddress != nil {
+ path.SetNexthop(options.Info.LocalAddress)
+ }
+ return path
+ }
+ path.SetNexthop(a.value)
+ return path
+}
+
+func (a *NexthopAction) ToConfig() config.BgpNextHopType {
+ if a.self {
+ return config.BgpNextHopType("self")
+ }
+ return config.BgpNextHopType(a.value.String())
+}
+
+func (a *NexthopAction) String() string {
+ return string(a.ToConfig())
+}
+
+func (a *NexthopAction) MarshalJSON() ([]byte, error) {
+ return json.Marshal(a.ToConfig())
+}
+
+func NewNexthopAction(c config.BgpNextHopType) (*NexthopAction, error) {
+ switch strings.ToLower(string(c)) {
+ case "":
+ return nil, nil
+ case "self":
+ return &NexthopAction{
+ self: true,
+ }, nil
+ }
+ addr := net.ParseIP(string(c))
+ if addr == nil {
+ return nil, fmt.Errorf("invalid ip address format: %s", string(c))
+ }
+ return &NexthopAction{
+ value: addr,
+ }, nil
+}
+
+type Statement struct {
+ Name string
+ Conditions []Condition
+ RouteAction Action
+ ModActions []Action
+}
+
+// evaluate each condition in the statement according to MatchSetOptions
+func (s *Statement) Evaluate(p *Path, options *PolicyOptions) bool {
+ for _, c := range s.Conditions {
+ if !c.Evaluate(p, options) {
+ return false
+ }
+ }
+ return true
+}
+
+func (s *Statement) Apply(path *Path, options *PolicyOptions) (RouteType, *Path) {
+ result := s.Evaluate(path, options)
+ if result {
+ if len(s.ModActions) != 0 {
+ // apply all modification actions
+ path = path.Clone(path.IsWithdraw)
+ for _, action := range s.ModActions {
+ path = action.Apply(path, options)
+ }
+ }
+ //Routing action
+ if s.RouteAction == nil || reflect.ValueOf(s.RouteAction).IsNil() {
+ return ROUTE_TYPE_NONE, path
+ }
+ p := s.RouteAction.Apply(path, options)
+ if p == nil {
+ return ROUTE_TYPE_REJECT, path
+ }
+ return ROUTE_TYPE_ACCEPT, path
+ }
+ return ROUTE_TYPE_NONE, path
+}
+
+func (s *Statement) ToConfig() *config.Statement {
+ return &config.Statement{
+ Name: s.Name,
+ Conditions: func() config.Conditions {
+ cond := config.Conditions{}
+ for _, c := range s.Conditions {
+ switch c.(type) {
+ case *PrefixCondition:
+ v := c.(*PrefixCondition)
+ cond.MatchPrefixSet = config.MatchPrefixSet{PrefixSet: v.set.Name(), MatchSetOptions: v.option.ConvertToMatchSetOptionsRestrictedType()}
+ case *NeighborCondition:
+ v := c.(*NeighborCondition)
+ cond.MatchNeighborSet = config.MatchNeighborSet{NeighborSet: v.set.Name(), MatchSetOptions: v.option.ConvertToMatchSetOptionsRestrictedType()}
+ case *AsPathLengthCondition:
+ v := c.(*AsPathLengthCondition)
+ cond.BgpConditions.AsPathLength = config.AsPathLength{Operator: config.IntToAttributeComparisonMap[int(v.operator)], Value: v.length}
+ case *AsPathCondition:
+ v := c.(*AsPathCondition)
+ cond.BgpConditions.MatchAsPathSet = config.MatchAsPathSet{AsPathSet: v.set.Name(), MatchSetOptions: config.IntToMatchSetOptionsTypeMap[int(v.option)]}
+ case *CommunityCondition:
+ v := c.(*CommunityCondition)
+ cond.BgpConditions.MatchCommunitySet = config.MatchCommunitySet{CommunitySet: v.set.Name(), MatchSetOptions: config.IntToMatchSetOptionsTypeMap[int(v.option)]}
+ case *ExtCommunityCondition:
+ v := c.(*ExtCommunityCondition)
+ cond.BgpConditions.MatchExtCommunitySet = config.MatchExtCommunitySet{ExtCommunitySet: v.set.Name(), MatchSetOptions: config.IntToMatchSetOptionsTypeMap[int(v.option)]}
+ case *LargeCommunityCondition:
+ v := c.(*LargeCommunityCondition)
+ cond.BgpConditions.MatchLargeCommunitySet = config.MatchLargeCommunitySet{LargeCommunitySet: v.set.Name(), MatchSetOptions: config.IntToMatchSetOptionsTypeMap[int(v.option)]}
+ case *NextHopCondition:
+ v := c.(*NextHopCondition)
+ cond.BgpConditions.NextHopInList = v.set.List()
+ case *RpkiValidationCondition:
+ v := c.(*RpkiValidationCondition)
+ cond.BgpConditions.RpkiValidationResult = v.result
+ case *RouteTypeCondition:
+ v := c.(*RouteTypeCondition)
+ cond.BgpConditions.RouteType = v.typ
+ case *AfiSafiInCondition:
+ v := c.(*AfiSafiInCondition)
+ res := make([]config.AfiSafiType, 0, len(v.routeFamilies))
+ for _, rf := range v.routeFamilies {
+ res = append(res, config.AfiSafiType(rf.String()))
+ }
+ cond.BgpConditions.AfiSafiInList = res
+ }
+ }
+ return cond
+ }(),
+ Actions: func() config.Actions {
+ act := config.Actions{}
+ if s.RouteAction != nil && !reflect.ValueOf(s.RouteAction).IsNil() {
+ a := s.RouteAction.(*RoutingAction)
+ if a.AcceptRoute {
+ act.RouteDisposition = config.ROUTE_DISPOSITION_ACCEPT_ROUTE
+ } else {
+ act.RouteDisposition = config.ROUTE_DISPOSITION_REJECT_ROUTE
+ }
+ } else {
+ act.RouteDisposition = config.ROUTE_DISPOSITION_NONE
+ }
+ for _, a := range s.ModActions {
+ switch a.(type) {
+ case *AsPathPrependAction:
+ act.BgpActions.SetAsPathPrepend = *a.(*AsPathPrependAction).ToConfig()
+ case *CommunityAction:
+ act.BgpActions.SetCommunity = *a.(*CommunityAction).ToConfig()
+ case *ExtCommunityAction:
+ act.BgpActions.SetExtCommunity = *a.(*ExtCommunityAction).ToConfig()
+ case *LargeCommunityAction:
+ act.BgpActions.SetLargeCommunity = *a.(*LargeCommunityAction).ToConfig()
+ case *MedAction:
+ act.BgpActions.SetMed = a.(*MedAction).ToConfig()
+ case *LocalPrefAction:
+ act.BgpActions.SetLocalPref = a.(*LocalPrefAction).ToConfig()
+ case *NexthopAction:
+ act.BgpActions.SetNextHop = a.(*NexthopAction).ToConfig()
+ }
+ }
+ return act
+ }(),
+ }
+}
+
+func (s *Statement) MarshalJSON() ([]byte, error) {
+ return json.Marshal(s.ToConfig())
+}
+
+type opType int
+
+const (
+ ADD opType = iota
+ REMOVE
+ REPLACE
+)
+
+func (lhs *Statement) mod(op opType, rhs *Statement) error {
+ cs := make([]Condition, len(lhs.Conditions))
+ copy(cs, lhs.Conditions)
+ ra := lhs.RouteAction
+ as := make([]Action, len(lhs.ModActions))
+ copy(as, lhs.ModActions)
+ for _, x := range rhs.Conditions {
+ var c Condition
+ i := 0
+ for idx, y := range lhs.Conditions {
+ if x.Type() == y.Type() {
+ c = y
+ i = idx
+ break
+ }
+ }
+ switch op {
+ case ADD:
+ if c != nil {
+ return fmt.Errorf("condition %d is already set", x.Type())
+ }
+ if cs == nil {
+ cs = make([]Condition, 0, len(rhs.Conditions))
+ }
+ cs = append(cs, x)
+ case REMOVE:
+ if c == nil {
+ return fmt.Errorf("condition %d is not set", x.Type())
+ }
+ cs = append(cs[:i], cs[i+1:]...)
+ if len(cs) == 0 {
+ cs = nil
+ }
+ case REPLACE:
+ if c == nil {
+ return fmt.Errorf("condition %d is not set", x.Type())
+ }
+ cs[i] = x
+ }
+ }
+ if rhs.RouteAction != nil && !reflect.ValueOf(rhs.RouteAction).IsNil() {
+ switch op {
+ case ADD:
+ if lhs.RouteAction != nil && !reflect.ValueOf(lhs.RouteAction).IsNil() {
+ return fmt.Errorf("route action is already set")
+ }
+ ra = rhs.RouteAction
+ case REMOVE:
+ if lhs.RouteAction == nil || reflect.ValueOf(lhs.RouteAction).IsNil() {
+ return fmt.Errorf("route action is not set")
+ }
+ ra = nil
+ case REPLACE:
+ if lhs.RouteAction == nil || reflect.ValueOf(lhs.RouteAction).IsNil() {
+ return fmt.Errorf("route action is not set")
+ }
+ ra = rhs.RouteAction
+ }
+ }
+ for _, x := range rhs.ModActions {
+ var a Action
+ i := 0
+ for idx, y := range lhs.ModActions {
+ if x.Type() == y.Type() {
+ a = y
+ i = idx
+ break
+ }
+ }
+ switch op {
+ case ADD:
+ if a != nil {
+ return fmt.Errorf("action %d is already set", x.Type())
+ }
+ if as == nil {
+ as = make([]Action, 0, len(rhs.ModActions))
+ }
+ as = append(as, x)
+ case REMOVE:
+ if a == nil {
+ return fmt.Errorf("action %d is not set", x.Type())
+ }
+ as = append(as[:i], as[i+1:]...)
+ if len(as) == 0 {
+ as = nil
+ }
+ case REPLACE:
+ if a == nil {
+ return fmt.Errorf("action %d is not set", x.Type())
+ }
+ as[i] = x
+ }
+ }
+ lhs.Conditions = cs
+ lhs.RouteAction = ra
+ lhs.ModActions = as
+ return nil
+}
+
+func (lhs *Statement) Add(rhs *Statement) error {
+ return lhs.mod(ADD, rhs)
+}
+
+func (lhs *Statement) Remove(rhs *Statement) error {
+ return lhs.mod(REMOVE, rhs)
+}
+
+func (lhs *Statement) Replace(rhs *Statement) error {
+ return lhs.mod(REPLACE, rhs)
+}
+
+func NewStatement(c config.Statement) (*Statement, error) {
+ if c.Name == "" {
+ return nil, fmt.Errorf("empty statement name")
+ }
+ var ra Action
+ var as []Action
+ var cs []Condition
+ var err error
+ cfs := []func() (Condition, error){
+ func() (Condition, error) {
+ return NewPrefixCondition(c.Conditions.MatchPrefixSet)
+ },
+ func() (Condition, error) {
+ return NewNeighborCondition(c.Conditions.MatchNeighborSet)
+ },
+ func() (Condition, error) {
+ return NewAsPathLengthCondition(c.Conditions.BgpConditions.AsPathLength)
+ },
+ func() (Condition, error) {
+ return NewRpkiValidationCondition(c.Conditions.BgpConditions.RpkiValidationResult)
+ },
+ func() (Condition, error) {
+ return NewRouteTypeCondition(c.Conditions.BgpConditions.RouteType)
+ },
+ func() (Condition, error) {
+ return NewAsPathCondition(c.Conditions.BgpConditions.MatchAsPathSet)
+ },
+ func() (Condition, error) {
+ return NewCommunityCondition(c.Conditions.BgpConditions.MatchCommunitySet)
+ },
+ func() (Condition, error) {
+ return NewExtCommunityCondition(c.Conditions.BgpConditions.MatchExtCommunitySet)
+ },
+ func() (Condition, error) {
+ return NewLargeCommunityCondition(c.Conditions.BgpConditions.MatchLargeCommunitySet)
+ },
+ func() (Condition, error) {
+ return NewNextHopCondition(c.Conditions.BgpConditions.NextHopInList)
+ },
+ func() (Condition, error) {
+ return NewAfiSafiInCondition(c.Conditions.BgpConditions.AfiSafiInList)
+ },
+ }
+ cs = make([]Condition, 0, len(cfs))
+ for _, f := range cfs {
+ c, err := f()
+ if err != nil {
+ return nil, err
+ }
+ if !reflect.ValueOf(c).IsNil() {
+ cs = append(cs, c)
+ }
+ }
+ ra, err = NewRoutingAction(c.Actions.RouteDisposition)
+ if err != nil {
+ return nil, err
+ }
+ afs := []func() (Action, error){
+ func() (Action, error) {
+ return NewCommunityAction(c.Actions.BgpActions.SetCommunity)
+ },
+ func() (Action, error) {
+ return NewExtCommunityAction(c.Actions.BgpActions.SetExtCommunity)
+ },
+ func() (Action, error) {
+ return NewLargeCommunityAction(c.Actions.BgpActions.SetLargeCommunity)
+ },
+ func() (Action, error) {
+ return NewMedAction(c.Actions.BgpActions.SetMed)
+ },
+ func() (Action, error) {
+ return NewLocalPrefAction(c.Actions.BgpActions.SetLocalPref)
+ },
+ func() (Action, error) {
+ return NewAsPathPrependAction(c.Actions.BgpActions.SetAsPathPrepend)
+ },
+ func() (Action, error) {
+ return NewNexthopAction(c.Actions.BgpActions.SetNextHop)
+ },
+ }
+ as = make([]Action, 0, len(afs))
+ for _, f := range afs {
+ a, err := f()
+ if err != nil {
+ return nil, err
+ }
+ if !reflect.ValueOf(a).IsNil() {
+ as = append(as, a)
+ }
+ }
+ return &Statement{
+ Name: c.Name,
+ Conditions: cs,
+ RouteAction: ra,
+ ModActions: as,
+ }, nil
+}
+
+type Policy struct {
+ Name string
+ Statements []*Statement
+}
+
+// Compare path with a policy's condition in stored order in the policy.
+// If a condition match, then this function stops evaluation and
+// subsequent conditions are skipped.
+func (p *Policy) Apply(path *Path, options *PolicyOptions) (RouteType, *Path) {
+ for _, stmt := range p.Statements {
+ var result RouteType
+ result, path = stmt.Apply(path, options)
+ if result != ROUTE_TYPE_NONE {
+ return result, path
+ }
+ }
+ return ROUTE_TYPE_NONE, path
+}
+
+func (p *Policy) ToConfig() *config.PolicyDefinition {
+ ss := make([]config.Statement, 0, len(p.Statements))
+ for _, s := range p.Statements {
+ ss = append(ss, *s.ToConfig())
+ }
+ return &config.PolicyDefinition{
+ Name: p.Name,
+ Statements: ss,
+ }
+}
+
+func (p *Policy) FillUp(m map[string]*Statement) error {
+ stmts := make([]*Statement, 0, len(p.Statements))
+ for _, x := range p.Statements {
+ y, ok := m[x.Name]
+ if !ok {
+ return fmt.Errorf("not found statement %s", x.Name)
+ }
+ stmts = append(stmts, y)
+ }
+ p.Statements = stmts
+ return nil
+}
+
+func (lhs *Policy) Add(rhs *Policy) error {
+ lhs.Statements = append(lhs.Statements, rhs.Statements...)
+ return nil
+}
+
+func (lhs *Policy) Remove(rhs *Policy) error {
+ stmts := make([]*Statement, 0, len(lhs.Statements))
+ for _, x := range lhs.Statements {
+ found := false
+ for _, y := range rhs.Statements {
+ if x.Name == y.Name {
+ found = true
+ break
+ }
+ }
+ if !found {
+ stmts = append(stmts, x)
+ }
+ }
+ lhs.Statements = stmts
+ return nil
+}
+
+func (lhs *Policy) Replace(rhs *Policy) error {
+ lhs.Statements = rhs.Statements
+ return nil
+}
+
+func (p *Policy) MarshalJSON() ([]byte, error) {
+ return json.Marshal(p.ToConfig())
+}
+
+func NewPolicy(c config.PolicyDefinition) (*Policy, error) {
+ if c.Name == "" {
+ return nil, fmt.Errorf("empty policy name")
+ }
+ var st []*Statement
+ stmts := c.Statements
+ if len(stmts) != 0 {
+ st = make([]*Statement, 0, len(stmts))
+ for idx, stmt := range stmts {
+ if stmt.Name == "" {
+ stmt.Name = fmt.Sprintf("%s_stmt%d", c.Name, idx)
+ }
+ s, err := NewStatement(stmt)
+ if err != nil {
+ return nil, err
+ }
+ st = append(st, s)
+ }
+ }
+ return &Policy{
+ Name: c.Name,
+ Statements: st,
+ }, nil
+}
+
+type Policies []*Policy
+
+func (p Policies) Len() int {
+ return len(p)
+}
+
+func (p Policies) Swap(i, j int) {
+ p[i], p[j] = p[j], p[i]
+}
+
+func (p Policies) Less(i, j int) bool {
+ return p[i].Name < p[j].Name
+}
+
+type Assignment struct {
+ inPolicies []*Policy
+ defaultInPolicy RouteType
+ importPolicies []*Policy
+ defaultImportPolicy RouteType
+ exportPolicies []*Policy
+ defaultExportPolicy RouteType
+}
+
+type RoutingPolicy struct {
+ definedSetMap DefinedSetMap
+ policyMap map[string]*Policy
+ statementMap map[string]*Statement
+ assignmentMap map[string]*Assignment
+ mu sync.RWMutex
+}
+
+func (r *RoutingPolicy) ApplyPolicy(id string, dir PolicyDirection, before *Path, options *PolicyOptions) *Path {
+ r.mu.RLock()
+ defer r.mu.RUnlock()
+
+ if before == nil {
+ return nil
+ }
+
+ if before.IsWithdraw {
+ return before
+ }
+ result := ROUTE_TYPE_NONE
+ after := before
+ for _, p := range r.getPolicy(id, dir) {
+ result, after = p.Apply(after, options)
+ if result != ROUTE_TYPE_NONE {
+ break
+ }
+ }
+ if result == ROUTE_TYPE_NONE {
+ result = r.getDefaultPolicy(id, dir)
+ }
+ switch result {
+ case ROUTE_TYPE_ACCEPT:
+ return after
+ default:
+ return nil
+ }
+}
+
+func (r *RoutingPolicy) getPolicy(id string, dir PolicyDirection) []*Policy {
+ a, ok := r.assignmentMap[id]
+ if !ok {
+ return nil
+ }
+ switch dir {
+ case POLICY_DIRECTION_IN:
+ return a.inPolicies
+ case POLICY_DIRECTION_IMPORT:
+ return a.importPolicies
+ case POLICY_DIRECTION_EXPORT:
+ return a.exportPolicies
+ default:
+ return nil
+ }
+}
+
+func (r *RoutingPolicy) getDefaultPolicy(id string, dir PolicyDirection) RouteType {
+ a, ok := r.assignmentMap[id]
+ if !ok {
+ return ROUTE_TYPE_NONE
+ }
+ switch dir {
+ case POLICY_DIRECTION_IN:
+ return a.defaultInPolicy
+ case POLICY_DIRECTION_IMPORT:
+ return a.defaultImportPolicy
+ case POLICY_DIRECTION_EXPORT:
+ return a.defaultExportPolicy
+ default:
+ return ROUTE_TYPE_NONE
+ }
+
+}
+
+func (r *RoutingPolicy) setPolicy(id string, dir PolicyDirection, policies []*Policy) error {
+ a, ok := r.assignmentMap[id]
+ if !ok {
+ a = &Assignment{}
+ }
+ switch dir {
+ case POLICY_DIRECTION_IN:
+ a.inPolicies = policies
+ case POLICY_DIRECTION_IMPORT:
+ a.importPolicies = policies
+ case POLICY_DIRECTION_EXPORT:
+ a.exportPolicies = policies
+ }
+ r.assignmentMap[id] = a
+ return nil
+}
+
+func (r *RoutingPolicy) setDefaultPolicy(id string, dir PolicyDirection, typ RouteType) error {
+ a, ok := r.assignmentMap[id]
+ if !ok {
+ a = &Assignment{}
+ }
+ switch dir {
+ case POLICY_DIRECTION_IN:
+ a.defaultInPolicy = typ
+ case POLICY_DIRECTION_IMPORT:
+ a.defaultImportPolicy = typ
+ case POLICY_DIRECTION_EXPORT:
+ a.defaultExportPolicy = typ
+ }
+ r.assignmentMap[id] = a
+ return nil
+}
+
+func (r *RoutingPolicy) getAssignmentFromConfig(dir PolicyDirection, a config.ApplyPolicy) ([]*Policy, RouteType, error) {
+ var names []string
+ var cdef config.DefaultPolicyType
+ def := ROUTE_TYPE_ACCEPT
+ c := a.Config
+ switch dir {
+ case POLICY_DIRECTION_IN:
+ names = c.InPolicyList
+ cdef = c.DefaultInPolicy
+ case POLICY_DIRECTION_IMPORT:
+ names = c.ImportPolicyList
+ cdef = c.DefaultImportPolicy
+ case POLICY_DIRECTION_EXPORT:
+ names = c.ExportPolicyList
+ cdef = c.DefaultExportPolicy
+ default:
+ return nil, def, fmt.Errorf("invalid policy direction")
+ }
+ if cdef == config.DEFAULT_POLICY_TYPE_REJECT_ROUTE {
+ def = ROUTE_TYPE_REJECT
+ }
+ ps := make([]*Policy, 0, len(names))
+ seen := make(map[string]bool)
+ for _, name := range names {
+ p, ok := r.policyMap[name]
+ if !ok {
+ return nil, def, fmt.Errorf("not found policy %s", name)
+ }
+ if seen[name] {
+ return nil, def, fmt.Errorf("duplicated policy %s", name)
+ }
+ seen[name] = true
+ ps = append(ps, p)
+ }
+ return ps, def, nil
+}
+
+func (r *RoutingPolicy) validateCondition(v Condition) (err error) {
+ switch v.Type() {
+ case CONDITION_PREFIX:
+ m := r.definedSetMap[DEFINED_TYPE_PREFIX]
+ if i, ok := m[v.Name()]; !ok {
+ return fmt.Errorf("not found prefix set %s", v.Name())
+ } else {
+ c := v.(*PrefixCondition)
+ c.set = i.(*PrefixSet)
+ }
+ case CONDITION_NEIGHBOR:
+ m := r.definedSetMap[DEFINED_TYPE_NEIGHBOR]
+ if i, ok := m[v.Name()]; !ok {
+ return fmt.Errorf("not found neighbor set %s", v.Name())
+ } else {
+ c := v.(*NeighborCondition)
+ c.set = i.(*NeighborSet)
+ }
+ case CONDITION_AS_PATH:
+ m := r.definedSetMap[DEFINED_TYPE_AS_PATH]
+ if i, ok := m[v.Name()]; !ok {
+ return fmt.Errorf("not found as path set %s", v.Name())
+ } else {
+ c := v.(*AsPathCondition)
+ c.set = i.(*AsPathSet)
+ }
+ case CONDITION_COMMUNITY:
+ m := r.definedSetMap[DEFINED_TYPE_COMMUNITY]
+ if i, ok := m[v.Name()]; !ok {
+ return fmt.Errorf("not found community set %s", v.Name())
+ } else {
+ c := v.(*CommunityCondition)
+ c.set = i.(*CommunitySet)
+ }
+ case CONDITION_EXT_COMMUNITY:
+ m := r.definedSetMap[DEFINED_TYPE_EXT_COMMUNITY]
+ if i, ok := m[v.Name()]; !ok {
+ return fmt.Errorf("not found ext-community set %s", v.Name())
+ } else {
+ c := v.(*ExtCommunityCondition)
+ c.set = i.(*ExtCommunitySet)
+ }
+ case CONDITION_LARGE_COMMUNITY:
+ m := r.definedSetMap[DEFINED_TYPE_LARGE_COMMUNITY]
+ if i, ok := m[v.Name()]; !ok {
+ return fmt.Errorf("not found large-community set %s", v.Name())
+ } else {
+ c := v.(*LargeCommunityCondition)
+ c.set = i.(*LargeCommunitySet)
+ }
+ case CONDITION_NEXT_HOP:
+ case CONDITION_AFI_SAFI_IN:
+ case CONDITION_AS_PATH_LENGTH:
+ case CONDITION_RPKI:
+ }
+ return nil
+}
+
+func (r *RoutingPolicy) inUse(d DefinedSet) bool {
+ name := d.Name()
+ for _, p := range r.policyMap {
+ for _, s := range p.Statements {
+ for _, c := range s.Conditions {
+ if c.Set() != nil && c.Set().Name() == name {
+ return true
+ }
+ }
+ }
+ }
+ return false
+}
+
+func (r *RoutingPolicy) statementInUse(x *Statement) bool {
+ for _, p := range r.policyMap {
+ for _, y := range p.Statements {
+ if x.Name == y.Name {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+func (r *RoutingPolicy) reload(c config.RoutingPolicy) error {
+ dmap := make(map[DefinedType]map[string]DefinedSet)
+ dmap[DEFINED_TYPE_PREFIX] = make(map[string]DefinedSet)
+ d := c.DefinedSets
+ for _, x := range d.PrefixSets {
+ y, err := NewPrefixSet(x)
+ if err != nil {
+ return err
+ }
+ if y == nil {
+ return fmt.Errorf("empty prefix set")
+ }
+ dmap[DEFINED_TYPE_PREFIX][y.Name()] = y
+ }
+ dmap[DEFINED_TYPE_NEIGHBOR] = make(map[string]DefinedSet)
+ for _, x := range d.NeighborSets {
+ y, err := NewNeighborSet(x)
+ if err != nil {
+ return err
+ }
+ if y == nil {
+ return fmt.Errorf("empty neighbor set")
+ }
+ dmap[DEFINED_TYPE_NEIGHBOR][y.Name()] = y
+ }
+ // dmap[DEFINED_TYPE_TAG] = make(map[string]DefinedSet)
+ // for _, x := range c.DefinedSets.TagSets{
+ // y, err := NewTagSet(x)
+ // if err != nil {
+ // return nil, err
+ // }
+ // dmap[DEFINED_TYPE_TAG][y.Name()] = y
+ // }
+ bd := c.DefinedSets.BgpDefinedSets
+ dmap[DEFINED_TYPE_AS_PATH] = make(map[string]DefinedSet)
+ for _, x := range bd.AsPathSets {
+ y, err := NewAsPathSet(x)
+ if err != nil {
+ return err
+ }
+ if y == nil {
+ return fmt.Errorf("empty as path set")
+ }
+ dmap[DEFINED_TYPE_AS_PATH][y.Name()] = y
+ }
+ dmap[DEFINED_TYPE_COMMUNITY] = make(map[string]DefinedSet)
+ for _, x := range bd.CommunitySets {
+ y, err := NewCommunitySet(x)
+ if err != nil {
+ return err
+ }
+ if y == nil {
+ return fmt.Errorf("empty community set")
+ }
+ dmap[DEFINED_TYPE_COMMUNITY][y.Name()] = y
+ }
+ dmap[DEFINED_TYPE_EXT_COMMUNITY] = make(map[string]DefinedSet)
+ for _, x := range bd.ExtCommunitySets {
+ y, err := NewExtCommunitySet(x)
+ if err != nil {
+ return err
+ }
+ if y == nil {
+ return fmt.Errorf("empty ext-community set")
+ }
+ dmap[DEFINED_TYPE_EXT_COMMUNITY][y.Name()] = y
+ }
+ dmap[DEFINED_TYPE_LARGE_COMMUNITY] = make(map[string]DefinedSet)
+ for _, x := range bd.LargeCommunitySets {
+ y, err := NewLargeCommunitySet(x)
+ if err != nil {
+ return err
+ }
+ if y == nil {
+ return fmt.Errorf("empty large-community set")
+ }
+ dmap[DEFINED_TYPE_LARGE_COMMUNITY][y.Name()] = y
+ }
+
+ pmap := make(map[string]*Policy)
+ smap := make(map[string]*Statement)
+ for _, x := range c.PolicyDefinitions {
+ y, err := NewPolicy(x)
+ if err != nil {
+ return err
+ }
+ if _, ok := pmap[y.Name]; ok {
+ return fmt.Errorf("duplicated policy name. policy name must be unique.")
+ }
+ pmap[y.Name] = y
+ for _, s := range y.Statements {
+ _, ok := smap[s.Name]
+ if ok {
+ return fmt.Errorf("duplicated statement name. statement name must be unique.")
+ }
+ smap[s.Name] = s
+ }
+ }
+
+ // hacky
+ oldMap := r.definedSetMap
+ r.definedSetMap = dmap
+ for _, y := range pmap {
+ for _, s := range y.Statements {
+ for _, c := range s.Conditions {
+ if err := r.validateCondition(c); err != nil {
+ r.definedSetMap = oldMap
+ return err
+ }
+ }
+ }
+ }
+
+ r.definedSetMap = dmap
+ r.policyMap = pmap
+ r.statementMap = smap
+ r.assignmentMap = make(map[string]*Assignment)
+ // allow all routes coming in and going out by default
+ r.setDefaultPolicy(GLOBAL_RIB_NAME, POLICY_DIRECTION_IMPORT, ROUTE_TYPE_ACCEPT)
+ r.setDefaultPolicy(GLOBAL_RIB_NAME, POLICY_DIRECTION_EXPORT, ROUTE_TYPE_ACCEPT)
+ return nil
+}
+
+func (r *RoutingPolicy) GetDefinedSet(typ DefinedType, name string) (*config.DefinedSets, error) {
+ r.mu.RLock()
+
+ set, ok := r.definedSetMap[typ]
+ if !ok {
+ return nil, fmt.Errorf("invalid defined-set type: %d", typ)
+ }
+
+ var dl DefinedSetList
+ for _, s := range set {
+ dl = append(dl, s)
+ }
+ r.mu.RUnlock()
+
+ sort.Sort(dl)
+
+ sets := &config.DefinedSets{
+ PrefixSets: make([]config.PrefixSet, 0),
+ NeighborSets: make([]config.NeighborSet, 0),
+ BgpDefinedSets: config.BgpDefinedSets{
+ CommunitySets: make([]config.CommunitySet, 0),
+ ExtCommunitySets: make([]config.ExtCommunitySet, 0),
+ LargeCommunitySets: make([]config.LargeCommunitySet, 0),
+ AsPathSets: make([]config.AsPathSet, 0),
+ },
+ }
+ for _, s := range dl {
+ if name != "" && s.Name() != name {
+ continue
+ }
+ switch s.(type) {
+ case *PrefixSet:
+ sets.PrefixSets = append(sets.PrefixSets, *s.(*PrefixSet).ToConfig())
+ case *NeighborSet:
+ sets.NeighborSets = append(sets.NeighborSets, *s.(*NeighborSet).ToConfig())
+ case *CommunitySet:
+ sets.BgpDefinedSets.CommunitySets = append(sets.BgpDefinedSets.CommunitySets, *s.(*CommunitySet).ToConfig())
+ case *ExtCommunitySet:
+ sets.BgpDefinedSets.ExtCommunitySets = append(sets.BgpDefinedSets.ExtCommunitySets, *s.(*ExtCommunitySet).ToConfig())
+ case *LargeCommunitySet:
+ sets.BgpDefinedSets.LargeCommunitySets = append(sets.BgpDefinedSets.LargeCommunitySets, *s.(*LargeCommunitySet).ToConfig())
+ case *AsPathSet:
+ sets.BgpDefinedSets.AsPathSets = append(sets.BgpDefinedSets.AsPathSets, *s.(*AsPathSet).ToConfig())
+ }
+ }
+ return sets, nil
+}
+
+func (r *RoutingPolicy) AddDefinedSet(s DefinedSet) error {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ if m, ok := r.definedSetMap[s.Type()]; !ok {
+ return fmt.Errorf("invalid defined-set type: %d", s.Type())
+ } else {
+ if d, ok := m[s.Name()]; ok {
+ if err := d.Append(s); err != nil {
+ return err
+ }
+ } else {
+ m[s.Name()] = s
+ }
+ }
+ return nil
+}
+
+func (r *RoutingPolicy) DeleteDefinedSet(a DefinedSet, all bool) (err error) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ if m, ok := r.definedSetMap[a.Type()]; !ok {
+ err = fmt.Errorf("invalid defined-set type: %d", a.Type())
+ } else {
+ d, ok := m[a.Name()]
+ if !ok {
+ return fmt.Errorf("not found defined-set: %s", a.Name())
+ }
+ if all {
+ if r.inUse(d) {
+ err = fmt.Errorf("can't delete. defined-set %s is in use", a.Name())
+ } else {
+ delete(m, a.Name())
+ }
+ } else {
+ err = d.Remove(a)
+ }
+ }
+ return err
+}
+
+func (r *RoutingPolicy) ReplaceDefinedSet(a DefinedSet) (err error) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ if m, ok := r.definedSetMap[a.Type()]; !ok {
+ err = fmt.Errorf("invalid defined-set type: %d", a.Type())
+ } else {
+ if d, ok := m[a.Name()]; !ok {
+ err = fmt.Errorf("not found defined-set: %s", a.Name())
+ } else {
+ err = d.Replace(a)
+ }
+ }
+ return err
+}
+
+func (r *RoutingPolicy) GetStatement() []*config.Statement {
+ r.mu.RLock()
+ defer r.mu.RUnlock()
+
+ l := make([]*config.Statement, 0, len(r.statementMap))
+ for _, st := range r.statementMap {
+ l = append(l, st.ToConfig())
+ }
+ return l
+}
+
+func (r *RoutingPolicy) AddStatement(st *Statement) (err error) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ for _, c := range st.Conditions {
+ if err = r.validateCondition(c); err != nil {
+ return
+ }
+ }
+ m := r.statementMap
+ name := st.Name
+ if d, ok := m[name]; ok {
+ err = d.Add(st)
+ } else {
+ m[name] = st
+ }
+
+ return err
+}
+
+func (r *RoutingPolicy) DeleteStatement(st *Statement, all bool) (err error) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ m := r.statementMap
+ name := st.Name
+ if d, ok := m[name]; ok {
+ if all {
+ if r.statementInUse(d) {
+ err = fmt.Errorf("can't delete. statement %s is in use", name)
+ } else {
+ delete(m, name)
+ }
+ } else {
+ err = d.Remove(st)
+ }
+ } else {
+ err = fmt.Errorf("not found statement: %s", name)
+ }
+ return err
+}
+
+func (r *RoutingPolicy) ReplaceStatement(st *Statement) (err error) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ for _, c := range st.Conditions {
+ if err = r.validateCondition(c); err != nil {
+ return
+ }
+ }
+ m := r.statementMap
+ name := st.Name
+ if d, ok := m[name]; ok {
+ err = d.Replace(st)
+ } else {
+ err = fmt.Errorf("not found statement: %s", name)
+ }
+ return err
+}
+
+func (r *RoutingPolicy) GetAllPolicy() []*config.PolicyDefinition {
+ r.mu.RLock()
+
+ var ps Policies
+ for _, p := range r.policyMap {
+ ps = append(ps, p)
+ }
+ r.mu.RUnlock()
+
+ sort.Sort(ps)
+
+ l := make([]*config.PolicyDefinition, 0, len(ps))
+ for _, p := range ps {
+ l = append(l, p.ToConfig())
+ }
+ return l
+}
+
+func (r *RoutingPolicy) AddPolicy(x *Policy, refer bool) (err error) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ for _, st := range x.Statements {
+ for _, c := range st.Conditions {
+ if err = r.validateCondition(c); err != nil {
+ return
+ }
+ }
+ }
+
+ pMap := r.policyMap
+ sMap := r.statementMap
+ name := x.Name
+ y, ok := pMap[name]
+ if refer {
+ err = x.FillUp(sMap)
+ } else {
+ for _, st := range x.Statements {
+ if _, ok := sMap[st.Name]; ok {
+ err = fmt.Errorf("statement %s already defined", st.Name)
+ return
+ }
+ sMap[st.Name] = st
+ }
+ }
+ if ok {
+ err = y.Add(x)
+ } else {
+ pMap[name] = x
+ }
+
+ return err
+}
+
+func (r *RoutingPolicy) DeletePolicy(x *Policy, all, preserve bool, activeId []string) (err error) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ pMap := r.policyMap
+ sMap := r.statementMap
+ name := x.Name
+ y, ok := pMap[name]
+ if !ok {
+ err = fmt.Errorf("not found policy: %s", name)
+ return
+ }
+ inUse := func(ids []string) bool {
+ for _, id := range ids {
+ for _, dir := range []PolicyDirection{POLICY_DIRECTION_IN, POLICY_DIRECTION_EXPORT, POLICY_DIRECTION_EXPORT} {
+ for _, y := range r.getPolicy(id, dir) {
+ if x.Name == y.Name {
+ return true
+ }
+ }
+ }
+ }
+ return false
+ }
+
+ if all {
+ if inUse(activeId) {
+ err = fmt.Errorf("can't delete. policy %s is in use", name)
+ return
+ }
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ "Key": name,
+ }).Debug("delete policy")
+ delete(pMap, name)
+ } else {
+ err = y.Remove(x)
+ }
+ if err == nil && !preserve {
+ for _, st := range y.Statements {
+ if !r.statementInUse(st) {
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ "Key": st.Name,
+ }).Debug("delete unused statement")
+ delete(sMap, st.Name)
+ }
+ }
+ }
+ return err
+}
+
+func (r *RoutingPolicy) ReplacePolicy(x *Policy, refer, preserve bool) (err error) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ for _, st := range x.Statements {
+ for _, c := range st.Conditions {
+ if err = r.validateCondition(c); err != nil {
+ return
+ }
+ }
+ }
+
+ pMap := r.policyMap
+ sMap := r.statementMap
+ name := x.Name
+ y, ok := pMap[name]
+ if !ok {
+ err = fmt.Errorf("not found policy: %s", name)
+ return
+ }
+ if refer {
+ if err = x.FillUp(sMap); err != nil {
+ return
+ }
+ } else {
+ for _, st := range x.Statements {
+ if _, ok := sMap[st.Name]; ok {
+ err = fmt.Errorf("statement %s already defined", st.Name)
+ return
+ }
+ sMap[st.Name] = st
+ }
+ }
+
+ ys := y.Statements
+ err = y.Replace(x)
+ if err == nil && !preserve {
+ for _, st := range ys {
+ if !r.statementInUse(st) {
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ "Key": st.Name,
+ }).Debug("delete unused statement")
+ delete(sMap, st.Name)
+ }
+ }
+ }
+ return err
+}
+
+func (r *RoutingPolicy) GetPolicyAssignment(id string, dir PolicyDirection) (RouteType, []*config.PolicyDefinition, error) {
+ r.mu.RLock()
+ defer r.mu.RUnlock()
+
+ rt := r.getDefaultPolicy(id, dir)
+
+ ps := r.getPolicy(id, dir)
+ l := make([]*config.PolicyDefinition, 0, len(ps))
+ for _, p := range ps {
+ l = append(l, p.ToConfig())
+ }
+ return rt, l, nil
+}
+
+func (r *RoutingPolicy) AddPolicyAssignment(id string, dir PolicyDirection, policies []*config.PolicyDefinition, def RouteType) (err error) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ ps := make([]*Policy, 0, len(policies))
+ seen := make(map[string]bool)
+ for _, x := range policies {
+ p, ok := r.policyMap[x.Name]
+ if !ok {
+ err = fmt.Errorf("not found policy %s", x.Name)
+ return
+ }
+ if seen[x.Name] {
+ err = fmt.Errorf("duplicated policy %s", x.Name)
+ return
+ }
+ seen[x.Name] = true
+ ps = append(ps, p)
+ }
+ cur := r.getPolicy(id, dir)
+ if cur == nil {
+ err = r.setPolicy(id, dir, ps)
+ } else {
+ seen = make(map[string]bool)
+ ps = append(cur, ps...)
+ for _, x := range ps {
+ if seen[x.Name] {
+ err = fmt.Errorf("duplicated policy %s", x.Name)
+ return
+ }
+ seen[x.Name] = true
+ }
+ err = r.setPolicy(id, dir, ps)
+ }
+ if err == nil && def != ROUTE_TYPE_NONE {
+ err = r.setDefaultPolicy(id, dir, def)
+ }
+ return err
+}
+
+func (r *RoutingPolicy) DeletePolicyAssignment(id string, dir PolicyDirection, policies []*config.PolicyDefinition, all bool) (err error) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ ps := make([]*Policy, 0, len(policies))
+ seen := make(map[string]bool)
+ for _, x := range policies {
+ p, ok := r.policyMap[x.Name]
+ if !ok {
+ err = fmt.Errorf("not found policy %s", x.Name)
+ return
+ }
+ if seen[x.Name] {
+ err = fmt.Errorf("duplicated policy %s", x.Name)
+ return
+ }
+ seen[x.Name] = true
+ ps = append(ps, p)
+ }
+ cur := r.getPolicy(id, dir)
+
+ if all {
+ err = r.setPolicy(id, dir, nil)
+ if err != nil {
+ return
+ }
+ err = r.setDefaultPolicy(id, dir, ROUTE_TYPE_NONE)
+ } else {
+ l := len(cur) - len(ps)
+ if l < 0 {
+ // try to remove more than the assigned policies...
+ l = len(cur)
+ }
+ n := make([]*Policy, 0, l)
+ for _, y := range cur {
+ found := false
+ for _, x := range ps {
+ if x.Name == y.Name {
+ found = true
+ break
+ }
+ }
+ if !found {
+ n = append(n, y)
+ }
+ }
+ err = r.setPolicy(id, dir, n)
+ }
+ return err
+}
+
+func (r *RoutingPolicy) ReplacePolicyAssignment(id string, dir PolicyDirection, policies []*config.PolicyDefinition, def RouteType) (err error) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ ps := make([]*Policy, 0, len(policies))
+ seen := make(map[string]bool)
+ for _, x := range policies {
+ p, ok := r.policyMap[x.Name]
+ if !ok {
+ err = fmt.Errorf("not found policy %s", x.Name)
+ return
+ }
+ if seen[x.Name] {
+ err = fmt.Errorf("duplicated policy %s", x.Name)
+ return
+ }
+ seen[x.Name] = true
+ ps = append(ps, p)
+ }
+ r.getPolicy(id, dir)
+ err = r.setPolicy(id, dir, ps)
+ if err == nil && def != ROUTE_TYPE_NONE {
+ err = r.setDefaultPolicy(id, dir, def)
+ }
+ return err
+}
+
+func (r *RoutingPolicy) Reset(rp *config.RoutingPolicy, ap map[string]config.ApplyPolicy) error {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ if rp != nil {
+ if err := r.reload(*rp); err != nil {
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ }).Errorf("failed to create routing policy: %s", err)
+ return err
+ }
+ }
+
+ for id, c := range ap {
+ for _, dir := range []PolicyDirection{POLICY_DIRECTION_IN, POLICY_DIRECTION_IMPORT, POLICY_DIRECTION_EXPORT} {
+ ps, def, err := r.getAssignmentFromConfig(dir, c)
+ if err != nil {
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ "Dir": dir,
+ }).Errorf("failed to get policy info: %s", err)
+ continue
+ }
+ r.setDefaultPolicy(id, dir, def)
+ r.setPolicy(id, dir, ps)
+ }
+ }
+ return nil
+}
+
+func NewRoutingPolicy() *RoutingPolicy {
+ return &RoutingPolicy{
+ definedSetMap: make(map[DefinedType]map[string]DefinedSet),
+ policyMap: make(map[string]*Policy),
+ statementMap: make(map[string]*Statement),
+ assignmentMap: make(map[string]*Assignment),
+ }
+}
+
+func CanImportToVrf(v *Vrf, path *Path) bool {
+ f := func(arg []bgp.ExtendedCommunityInterface) []string {
+ ret := make([]string, 0, len(arg))
+ for _, a := range arg {
+ ret = append(ret, fmt.Sprintf("RT:%s", a.String()))
+ }
+ return ret
+ }
+ set, _ := NewExtCommunitySet(config.ExtCommunitySet{
+ ExtCommunitySetName: v.Name,
+ ExtCommunityList: f(v.ImportRt),
+ })
+ matchSet := config.MatchExtCommunitySet{
+ ExtCommunitySet: v.Name,
+ MatchSetOptions: config.MATCH_SET_OPTIONS_TYPE_ANY,
+ }
+ c, _ := NewExtCommunityCondition(matchSet)
+ c.set = set
+ return c.Evaluate(path, nil)
+}
+
+type PolicyAssignment struct {
+ Name string
+ Type PolicyDirection
+ Policies []*Policy
+ Default RouteType
+}
diff --git a/internal/pkg/table/policy_test.go b/internal/pkg/table/policy_test.go
new file mode 100644
index 00000000..7f1a1dd9
--- /dev/null
+++ b/internal/pkg/table/policy_test.go
@@ -0,0 +1,3140 @@
+// Copyright (C) 2014,2015 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package table
+
+import (
+ "fmt"
+ "math"
+ "net"
+ "strconv"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/osrg/gobgp/internal/pkg/config"
+ "github.com/osrg/gobgp/pkg/packet/bgp"
+
+ log "github.com/sirupsen/logrus"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestPrefixCalcurateNoRange(t *testing.T) {
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.0")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+ // test
+ pl1, _ := NewPrefix(config.Prefix{IpPrefix: "10.10.0.0/24", MasklengthRange: ""})
+ match1 := pl1.Match(path)
+ assert.Equal(t, true, match1)
+ pl2, _ := NewPrefix(config.Prefix{IpPrefix: "10.10.0.0/23", MasklengthRange: ""})
+ match2 := pl2.Match(path)
+ assert.Equal(t, false, match2)
+ pl3, _ := NewPrefix(config.Prefix{IpPrefix: "10.10.0.0/16", MasklengthRange: "21..24"})
+ match3 := pl3.Match(path)
+ assert.Equal(t, true, match3)
+}
+
+func TestPrefixCalcurateAddress(t *testing.T) {
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+ // test
+ pl1, _ := NewPrefix(config.Prefix{IpPrefix: "10.11.0.0/16", MasklengthRange: "21..24"})
+ match1 := pl1.Match(path)
+ assert.Equal(t, false, match1)
+ pl2, _ := NewPrefix(config.Prefix{IpPrefix: "10.10.0.0/16", MasklengthRange: "21..24"})
+ match2 := pl2.Match(path)
+ assert.Equal(t, true, match2)
+}
+
+func TestPrefixCalcurateLength(t *testing.T) {
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+ // test
+ pl1, _ := NewPrefix(config.Prefix{IpPrefix: "10.10.64.0/24", MasklengthRange: "21..24"})
+ match1 := pl1.Match(path)
+ assert.Equal(t, false, match1)
+ pl2, _ := NewPrefix(config.Prefix{IpPrefix: "10.10.64.0/16", MasklengthRange: "21..24"})
+ match2 := pl2.Match(path)
+ assert.Equal(t, true, match2)
+}
+
+func TestPrefixCalcurateLengthRange(t *testing.T) {
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+ // test
+ pl1, _ := NewPrefix(config.Prefix{IpPrefix: "10.10.0.0/16", MasklengthRange: "21..23"})
+ match1 := pl1.Match(path)
+ assert.Equal(t, false, match1)
+ pl2, _ := NewPrefix(config.Prefix{IpPrefix: "10.10.0.0/16", MasklengthRange: "25..26"})
+ match2 := pl2.Match(path)
+ assert.Equal(t, false, match2)
+ pl3, _ := NewPrefix(config.Prefix{IpPrefix: "10.10.0.0/16", MasklengthRange: "21..24"})
+ match3 := pl3.Match(path)
+ assert.Equal(t, true, match3)
+}
+
+func TestPrefixCalcurateNoRangeIPv6(t *testing.T) {
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("2001::192:168:50:1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ mpnlri := []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(64, "2001:123:123:1::")}
+ mpreach := bgp.NewPathAttributeMpReachNLRI("2001::192:168:50:1", mpnlri)
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes := []bgp.PathAttributeInterface{mpreach, origin, aspath, med}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nil)
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+ // test
+ pl1, _ := NewPrefix(config.Prefix{IpPrefix: "2001:123:123::/48", MasklengthRange: ""})
+ match1 := pl1.Match(path)
+ assert.Equal(t, false, match1)
+ pl2, _ := NewPrefix(config.Prefix{IpPrefix: "2001:123:123:1::/64", MasklengthRange: ""})
+ match2 := pl2.Match(path)
+ assert.Equal(t, true, match2)
+ pl3, _ := NewPrefix(config.Prefix{IpPrefix: "2001:123:123::/48", MasklengthRange: "64..80"})
+ match3 := pl3.Match(path)
+ assert.Equal(t, true, match3)
+}
+
+func TestPrefixCalcurateAddressIPv6(t *testing.T) {
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("2001::192:168:50:1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ mpnlri := []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(64, "2001:123:123:1::")}
+ mpreach := bgp.NewPathAttributeMpReachNLRI("2001::192:168:50:1", mpnlri)
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes := []bgp.PathAttributeInterface{mpreach, origin, aspath, med}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nil)
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+ // test
+ pl1, _ := NewPrefix(config.Prefix{IpPrefix: "2001:123:128::/48", MasklengthRange: "64..80"})
+ match1 := pl1.Match(path)
+ assert.Equal(t, false, match1)
+ pl2, _ := NewPrefix(config.Prefix{IpPrefix: "2001:123:123::/48", MasklengthRange: "64..80"})
+ match2 := pl2.Match(path)
+ assert.Equal(t, true, match2)
+}
+
+func TestPrefixCalcurateLengthIPv6(t *testing.T) {
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("2001::192:168:50:1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ mpnlri := []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(64, "2001:123:123:1::")}
+ mpreach := bgp.NewPathAttributeMpReachNLRI("2001::192:168:50:1", mpnlri)
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes := []bgp.PathAttributeInterface{mpreach, origin, aspath, med}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nil)
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+ // test
+ pl1, _ := NewPrefix(config.Prefix{IpPrefix: "2001:123:123:64::/64", MasklengthRange: "64..80"})
+ match1 := pl1.Match(path)
+ assert.Equal(t, false, match1)
+ pl2, _ := NewPrefix(config.Prefix{IpPrefix: "2001:123:123:64::/48", MasklengthRange: "64..80"})
+ match2 := pl2.Match(path)
+ assert.Equal(t, true, match2)
+}
+
+func TestPrefixCalcurateLengthRangeIPv6(t *testing.T) {
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("2001::192:168:50:1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ mpnlri := []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(64, "2001:123:123:1::")}
+ mpreach := bgp.NewPathAttributeMpReachNLRI("2001::192:168:50:1", mpnlri)
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes := []bgp.PathAttributeInterface{mpreach, origin, aspath, med}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nil)
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+ // test
+ pl1, _ := NewPrefix(config.Prefix{IpPrefix: "2001:123:123::/48", MasklengthRange: "62..63"})
+ match1 := pl1.Match(path)
+ assert.Equal(t, false, match1)
+ pl2, _ := NewPrefix(config.Prefix{IpPrefix: "2001:123:123::/48", MasklengthRange: "65..66"})
+ match2 := pl2.Match(path)
+ assert.Equal(t, false, match2)
+ pl3, _ := NewPrefix(config.Prefix{IpPrefix: "2001:123:123::/48", MasklengthRange: "63..65"})
+ match3 := pl3.Match(path)
+ assert.Equal(t, true, match3)
+}
+
+func TestPolicyNotMatch(t *testing.T) {
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+
+ // create policy
+ ps := createPrefixSet("ps1", "10.3.0.0/16", "21..24")
+ ns := createNeighborSet("ns1", "10.0.0.1")
+ ds := config.DefinedSets{}
+ ds.PrefixSets = []config.PrefixSet{ps}
+ ds.NeighborSets = []config.NeighborSet{ns}
+ s := createStatement("statement1", "ps1", "ns1", false)
+ pd := createPolicyDefinition("pd1", s)
+ pl := createRoutingPolicy(ds, pd)
+
+ r := NewRoutingPolicy()
+ err := r.reload(pl)
+ assert.Nil(t, err)
+ pType, newPath := r.policyMap["pd1"].Apply(path, nil)
+ assert.Equal(t, ROUTE_TYPE_NONE, pType)
+ assert.Equal(t, newPath, path)
+}
+
+func TestPolicyMatchAndReject(t *testing.T) {
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+ // create policy
+ ps := createPrefixSet("ps1", "10.10.0.0/16", "21..24")
+ ns := createNeighborSet("ns1", "10.0.0.1")
+ ds := config.DefinedSets{}
+ ds.PrefixSets = []config.PrefixSet{ps}
+ ds.NeighborSets = []config.NeighborSet{ns}
+
+ s := createStatement("statement1", "ps1", "ns1", false)
+ pd := createPolicyDefinition("pd1", s)
+ pl := createRoutingPolicy(ds, pd)
+
+ r := NewRoutingPolicy()
+ err := r.reload(pl)
+ assert.Nil(t, err)
+ pType, newPath := r.policyMap["pd1"].Apply(path, nil)
+ assert.Equal(t, ROUTE_TYPE_REJECT, pType)
+ assert.Equal(t, newPath, path)
+}
+
+func TestPolicyMatchAndAccept(t *testing.T) {
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+ // create policy
+ ps := createPrefixSet("ps1", "10.10.0.0/16", "21..24")
+ ns := createNeighborSet("ns1", "10.0.0.1")
+ ds := config.DefinedSets{}
+ ds.PrefixSets = []config.PrefixSet{ps}
+ ds.NeighborSets = []config.NeighborSet{ns}
+
+ s := createStatement("statement1", "ps1", "ns1", true)
+ pd := createPolicyDefinition("pd1", s)
+ pl := createRoutingPolicy(ds, pd)
+
+ //test
+ r := NewRoutingPolicy()
+ err := r.reload(pl)
+ assert.Nil(t, err)
+ pType, newPath := r.policyMap["pd1"].Apply(path, nil)
+ assert.Equal(t, ROUTE_TYPE_ACCEPT, pType)
+ assert.Equal(t, path, newPath)
+}
+
+func TestPolicyRejectOnlyPrefixSet(t *testing.T) {
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.1.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.1.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.1.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ path1 := ProcessMessage(updateMsg, peer, time.Now())[0]
+
+ peer = &PeerInfo{AS: 65002, Address: net.ParseIP("10.0.2.2")}
+ origin = bgp.NewPathAttributeOrigin(0)
+ aspathParam = []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65002})}
+ aspath = bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop = bgp.NewPathAttributeNextHop("10.0.2.2")
+ med = bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes = []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri = []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.9.2.102")}
+ updateMsg = bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ path2 := ProcessMessage(updateMsg, peer, time.Now())[0]
+
+ // create policy
+ ps := createPrefixSet("ps1", "10.10.1.0/16", "21..24")
+ ds := config.DefinedSets{}
+ ds.PrefixSets = []config.PrefixSet{ps}
+
+ s := createStatement("statement1", "ps1", "", false)
+ pd := createPolicyDefinition("pd1", s)
+ pl := createRoutingPolicy(ds, pd)
+
+ //test
+ r := NewRoutingPolicy()
+ err := r.reload(pl)
+ assert.Nil(t, err)
+ p := r.policyMap["pd1"]
+ pType, newPath := p.Apply(path1, nil)
+ assert.Equal(t, ROUTE_TYPE_REJECT, pType)
+ assert.Equal(t, newPath, path1)
+
+ pType2, newPath2 := p.Apply(path2, nil)
+ assert.Equal(t, ROUTE_TYPE_NONE, pType2)
+ assert.Equal(t, newPath2, path2)
+}
+
+func TestPolicyRejectOnlyNeighborSet(t *testing.T) {
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.1.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.1.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.1.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ path1 := ProcessMessage(updateMsg, peer, time.Now())[0]
+
+ peer = &PeerInfo{AS: 65002, Address: net.ParseIP("10.0.2.2")}
+ origin = bgp.NewPathAttributeOrigin(0)
+ aspathParam = []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65002})}
+ aspath = bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop = bgp.NewPathAttributeNextHop("10.0.2.2")
+ med = bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes = []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri = []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.2.102")}
+ updateMsg = bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ path2 := ProcessMessage(updateMsg, peer, time.Now())[0]
+
+ // create policy
+ ns := createNeighborSet("ns1", "10.0.1.1")
+ ds := config.DefinedSets{}
+ ds.NeighborSets = []config.NeighborSet{ns}
+
+ s := createStatement("statement1", "", "ns1", false)
+ pd := createPolicyDefinition("pd1", s)
+ pl := createRoutingPolicy(ds, pd)
+
+ //test
+ r := NewRoutingPolicy()
+ err := r.reload(pl)
+ assert.Nil(t, err)
+ pType, newPath := r.policyMap["pd1"].Apply(path1, nil)
+ assert.Equal(t, ROUTE_TYPE_REJECT, pType)
+ assert.Equal(t, newPath, path1)
+
+ pType2, newPath2 := r.policyMap["pd1"].Apply(path2, nil)
+ assert.Equal(t, ROUTE_TYPE_NONE, pType2)
+ assert.Equal(t, newPath2, path2)
+}
+
+func TestPolicyDifferentRoutefamilyOfPathAndPolicy(t *testing.T) {
+ // create path ipv4
+ peerIPv4 := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ originIPv4 := bgp.NewPathAttributeOrigin(0)
+ aspathParamIPv4 := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})}
+ aspathIPv4 := bgp.NewPathAttributeAsPath(aspathParamIPv4)
+ nexthopIPv4 := bgp.NewPathAttributeNextHop("10.0.0.1")
+ medIPv4 := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributesIPv4 := []bgp.PathAttributeInterface{originIPv4, aspathIPv4, nexthopIPv4, medIPv4}
+ nlriIPv4 := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsgIPv4 := bgp.NewBGPUpdateMessage(nil, pathAttributesIPv4, nlriIPv4)
+ pathIPv4 := ProcessMessage(updateMsgIPv4, peerIPv4, time.Now())[0]
+ // create path ipv6
+ peerIPv6 := &PeerInfo{AS: 65001, Address: net.ParseIP("2001::192:168:50:1")}
+ originIPv6 := bgp.NewPathAttributeOrigin(0)
+ aspathParamIPv6 := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})}
+ aspathIPv6 := bgp.NewPathAttributeAsPath(aspathParamIPv6)
+ mpnlriIPv6 := []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(64, "2001:123:123:1::")}
+ mpreachIPv6 := bgp.NewPathAttributeMpReachNLRI("2001::192:168:50:1", mpnlriIPv6)
+ medIPv6 := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributesIPv6 := []bgp.PathAttributeInterface{mpreachIPv6, originIPv6, aspathIPv6, medIPv6}
+ updateMsgIPv6 := bgp.NewBGPUpdateMessage(nil, pathAttributesIPv6, nil)
+ pathIPv6 := ProcessMessage(updateMsgIPv6, peerIPv6, time.Now())[0]
+ // create policy
+ psIPv4 := createPrefixSet("psIPv4", "10.10.0.0/16", "21..24")
+ nsIPv4 := createNeighborSet("nsIPv4", "10.0.0.1")
+
+ psIPv6 := createPrefixSet("psIPv6", "2001:123:123::/48", "64..80")
+ nsIPv6 := createNeighborSet("nsIPv6", "2001::192:168:50:1")
+
+ ds := config.DefinedSets{}
+ ds.PrefixSets = []config.PrefixSet{psIPv4, psIPv6}
+ ds.NeighborSets = []config.NeighborSet{nsIPv4, nsIPv6}
+
+ stIPv4 := createStatement("statement1", "psIPv4", "nsIPv4", false)
+ stIPv6 := createStatement("statement2", "psIPv6", "nsIPv6", false)
+
+ pd := createPolicyDefinition("pd1", stIPv4, stIPv6)
+ pl := createRoutingPolicy(ds, pd)
+
+ //test
+ r := NewRoutingPolicy()
+ err := r.reload(pl)
+ assert.Nil(t, err)
+ p := r.policyMap["pd1"]
+ pType1, newPath1 := p.Apply(pathIPv4, nil)
+ assert.Equal(t, ROUTE_TYPE_REJECT, pType1)
+ assert.Equal(t, newPath1, pathIPv4)
+
+ pType2, newPath2 := p.Apply(pathIPv6, nil)
+ assert.Equal(t, ROUTE_TYPE_REJECT, pType2)
+ assert.Equal(t, newPath2, pathIPv6)
+}
+
+func TestAsPathLengthConditionEvaluate(t *testing.T) {
+ // setup
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{
+ bgp.NewAsPathParam(2, []uint16{65001, 65000, 65004, 65005}),
+ bgp.NewAsPathParam(1, []uint16{65001, 65000, 65004, 65005}),
+ }
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ UpdatePathAttrs4ByteAs(updateMsg.Body.(*bgp.BGPUpdate))
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+
+ // create match condition
+ asPathLength := config.AsPathLength{
+ Operator: "eq",
+ Value: 5,
+ }
+ c, _ := NewAsPathLengthCondition(asPathLength)
+
+ // test
+ assert.Equal(t, true, c.Evaluate(path, nil))
+
+ // create match condition
+ asPathLength = config.AsPathLength{
+ Operator: "ge",
+ Value: 3,
+ }
+ c, _ = NewAsPathLengthCondition(asPathLength)
+
+ // test
+ assert.Equal(t, true, c.Evaluate(path, nil))
+
+ // create match condition
+ asPathLength = config.AsPathLength{
+ Operator: "le",
+ Value: 3,
+ }
+ c, _ = NewAsPathLengthCondition(asPathLength)
+
+ // test
+ assert.Equal(t, false, c.Evaluate(path, nil))
+}
+
+func TestPolicyMatchAndAcceptNextHop(t *testing.T) {
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+
+ // create policy
+ ps := createPrefixSet("ps1", "10.10.0.0/16", "21..24")
+ ns := createNeighborSet("ns1", "10.0.0.1")
+ ds := config.DefinedSets{}
+ ds.PrefixSets = []config.PrefixSet{ps}
+ ds.NeighborSets = []config.NeighborSet{ns}
+ s := createStatement("statement1", "ps1", "ns1", true)
+ s.Conditions.BgpConditions.NextHopInList = []string{"10.0.0.1/32"}
+ pd := createPolicyDefinition("pd1", s)
+ pl := createRoutingPolicy(ds, pd)
+
+ r := NewRoutingPolicy()
+ err := r.reload(pl)
+ assert.Nil(t, err)
+ pType, newPath := r.policyMap["pd1"].Apply(path, nil)
+ assert.Equal(t, ROUTE_TYPE_ACCEPT, pType)
+ assert.Equal(t, newPath, path)
+}
+
+func TestPolicyMatchAndRejectNextHop(t *testing.T) {
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+
+ // create policy
+ ps := createPrefixSet("ps1", "10.10.0.0/16", "21..24")
+ ns := createNeighborSet("ns1", "10.0.0.1")
+ ds := config.DefinedSets{}
+ ds.PrefixSets = []config.PrefixSet{ps}
+ ds.NeighborSets = []config.NeighborSet{ns}
+ s := createStatement("statement1", "ps1", "ns1", true)
+ s.Conditions.BgpConditions.NextHopInList = []string{"10.0.0.12"}
+ pd := createPolicyDefinition("pd1", s)
+ pl := createRoutingPolicy(ds, pd)
+
+ r := NewRoutingPolicy()
+ err := r.reload(pl)
+ assert.Nil(t, err)
+ pType, newPath := r.policyMap["pd1"].Apply(path, nil)
+ assert.Equal(t, ROUTE_TYPE_NONE, pType)
+ assert.Equal(t, newPath, path)
+}
+
+func TestAsPathLengthConditionWithOtherCondition(t *testing.T) {
+ // setup
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{
+ bgp.NewAsPathParam(2, []uint16{65001, 65000, 65004, 65004, 65005}),
+ bgp.NewAsPathParam(1, []uint16{65001, 65000, 65004, 65005}),
+ }
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ UpdatePathAttrs4ByteAs(updateMsg.Body.(*bgp.BGPUpdate))
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+
+ // create policy
+ ps := createPrefixSet("ps1", "10.10.1.0/16", "21..24")
+ ns := createNeighborSet("ns1", "10.0.0.1")
+
+ ds := config.DefinedSets{}
+ ds.PrefixSets = []config.PrefixSet{ps}
+ ds.NeighborSets = []config.NeighborSet{ns}
+
+ // create match condition
+ asPathLength := config.AsPathLength{
+ Operator: "le",
+ Value: 10,
+ }
+
+ s := createStatement("statement1", "ps1", "ns1", false)
+ s.Conditions.BgpConditions.AsPathLength = asPathLength
+ pd := createPolicyDefinition("pd1", s)
+ pl := createRoutingPolicy(ds, pd)
+
+ //test
+ r := NewRoutingPolicy()
+ err := r.reload(pl)
+ assert.Nil(t, err)
+ p := r.policyMap["pd1"]
+ pType, newPath := p.Apply(path, nil)
+ assert.Equal(t, ROUTE_TYPE_REJECT, pType)
+ assert.Equal(t, newPath, path)
+
+}
+
+func TestAs4PathLengthConditionEvaluate(t *testing.T) {
+ // setup
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{
+ bgp.NewAs4PathParam(2, []uint32{
+ createAs4Value("65001.1"),
+ createAs4Value("65000.1"),
+ createAs4Value("65004.1"),
+ createAs4Value("65005.1"),
+ }),
+ bgp.NewAs4PathParam(1, []uint32{
+ createAs4Value("65001.1"),
+ createAs4Value("65000.1"),
+ createAs4Value("65004.1"),
+ createAs4Value("65005.1"),
+ }),
+ }
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ UpdatePathAttrs4ByteAs(updateMsg.Body.(*bgp.BGPUpdate))
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+
+ // create match condition
+ asPathLength := config.AsPathLength{
+ Operator: "eq",
+ Value: 5,
+ }
+ c, _ := NewAsPathLengthCondition(asPathLength)
+
+ // test
+ assert.Equal(t, true, c.Evaluate(path, nil))
+
+ // create match condition
+ asPathLength = config.AsPathLength{
+ Operator: "ge",
+ Value: 3,
+ }
+ c, _ = NewAsPathLengthCondition(asPathLength)
+
+ // test
+ assert.Equal(t, true, c.Evaluate(path, nil))
+
+ // create match condition
+ asPathLength = config.AsPathLength{
+ Operator: "le",
+ Value: 3,
+ }
+ c, _ = NewAsPathLengthCondition(asPathLength)
+
+ // test
+ assert.Equal(t, false, c.Evaluate(path, nil))
+}
+
+func addPolicy(r *RoutingPolicy, x *Policy) {
+ for _, s := range x.Statements {
+ for _, c := range s.Conditions {
+ r.validateCondition(c)
+ }
+ }
+}
+
+func TestAs4PathLengthConditionWithOtherCondition(t *testing.T) {
+ // setup
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{
+ bgp.NewAs4PathParam(2, []uint32{
+ createAs4Value("65001.1"),
+ createAs4Value("65000.1"),
+ createAs4Value("65004.1"),
+ createAs4Value("65004.1"),
+ createAs4Value("65005.1"),
+ }),
+ bgp.NewAs4PathParam(1, []uint32{
+ createAs4Value("65001.1"),
+ createAs4Value("65000.1"),
+ createAs4Value("65004.1"),
+ createAs4Value("65005.1"),
+ }),
+ }
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ UpdatePathAttrs4ByteAs(updateMsg.Body.(*bgp.BGPUpdate))
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+
+ // create policy
+ ps := createPrefixSet("ps1", "10.10.1.0/16", "21..24")
+ ns := createNeighborSet("ns1", "10.0.0.1")
+
+ ds := config.DefinedSets{}
+ ds.PrefixSets = []config.PrefixSet{ps}
+ ds.NeighborSets = []config.NeighborSet{ns}
+
+ // create match condition
+ asPathLength := config.AsPathLength{
+ Operator: "le",
+ Value: 10,
+ }
+
+ s := createStatement("statement1", "ps1", "ns1", false)
+ s.Conditions.BgpConditions.AsPathLength = asPathLength
+ pd := createPolicyDefinition("pd1", s)
+ pl := createRoutingPolicy(ds, pd)
+
+ //test
+ r := NewRoutingPolicy()
+ r.reload(pl)
+ p, _ := NewPolicy(pl.PolicyDefinitions[0])
+ addPolicy(r, p)
+ pType, newPath := p.Apply(path, nil)
+ assert.Equal(t, ROUTE_TYPE_REJECT, pType)
+ assert.Equal(t, newPath, path)
+
+}
+
+func TestAsPathConditionEvaluate(t *testing.T) {
+
+ // setup
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam1 := []bgp.AsPathParamInterface{
+ bgp.NewAsPathParam(2, []uint16{65001, 65000, 65010, 65004, 65005}),
+ }
+ aspath := bgp.NewPathAttributeAsPath(aspathParam1)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg1 := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ UpdatePathAttrs4ByteAs(updateMsg1.Body.(*bgp.BGPUpdate))
+ path1 := ProcessMessage(updateMsg1, peer, time.Now())[0]
+
+ aspathParam2 := []bgp.AsPathParamInterface{
+ bgp.NewAsPathParam(2, []uint16{65010}),
+ }
+ aspath2 := bgp.NewPathAttributeAsPath(aspathParam2)
+ pathAttributes = []bgp.PathAttributeInterface{origin, aspath2, nexthop, med}
+ updateMsg2 := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ UpdatePathAttrs4ByteAs(updateMsg2.Body.(*bgp.BGPUpdate))
+ path2 := ProcessMessage(updateMsg2, peer, time.Now())[0]
+
+ // create match condition
+ asPathSet1 := config.AsPathSet{
+ AsPathSetName: "asset1",
+ AsPathList: []string{"^65001"},
+ }
+
+ asPathSet2 := config.AsPathSet{
+ AsPathSetName: "asset2",
+ AsPathList: []string{"65005$"},
+ }
+
+ asPathSet3 := config.AsPathSet{
+ AsPathSetName: "asset3",
+ AsPathList: []string{"65004", "65005$"},
+ }
+
+ asPathSet4 := config.AsPathSet{
+ AsPathSetName: "asset4",
+ AsPathList: []string{"65000$"},
+ }
+
+ asPathSet5 := config.AsPathSet{
+ AsPathSetName: "asset5",
+ AsPathList: []string{"65010"},
+ }
+
+ asPathSet6 := config.AsPathSet{
+ AsPathSetName: "asset6",
+ AsPathList: []string{"^65010$"},
+ }
+
+ m := make(map[string]DefinedSet)
+ for _, s := range []config.AsPathSet{asPathSet1, asPathSet2, asPathSet3,
+ asPathSet4, asPathSet5, asPathSet6} {
+ a, _ := NewAsPathSet(s)
+ m[s.AsPathSetName] = a
+ }
+
+ createAspathC := func(name string, option config.MatchSetOptionsType) *AsPathCondition {
+ matchSet := config.MatchAsPathSet{}
+ matchSet.AsPathSet = name
+ matchSet.MatchSetOptions = option
+ p, _ := NewAsPathCondition(matchSet)
+ if v, ok := m[name]; ok {
+ p.set = v.(*AsPathSet)
+ }
+ return p
+ }
+
+ p1 := createAspathC("asset1", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p2 := createAspathC("asset2", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p3 := createAspathC("asset3", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p4 := createAspathC("asset4", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p5 := createAspathC("asset5", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p6 := createAspathC("asset6", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p7 := createAspathC("asset3", config.MATCH_SET_OPTIONS_TYPE_ALL)
+ p8 := createAspathC("asset3", config.MATCH_SET_OPTIONS_TYPE_INVERT)
+
+ // test
+ assert.Equal(t, true, p1.Evaluate(path1, nil))
+ assert.Equal(t, true, p2.Evaluate(path1, nil))
+ assert.Equal(t, true, p3.Evaluate(path1, nil))
+ assert.Equal(t, false, p4.Evaluate(path1, nil))
+ assert.Equal(t, true, p5.Evaluate(path1, nil))
+ assert.Equal(t, false, p6.Evaluate(path1, nil))
+ assert.Equal(t, true, p6.Evaluate(path2, nil))
+ assert.Equal(t, true, p7.Evaluate(path1, nil))
+ assert.Equal(t, true, p8.Evaluate(path2, nil))
+}
+
+func TestMultipleAsPathConditionEvaluate(t *testing.T) {
+
+ // setup
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam1 := []bgp.AsPathParamInterface{
+ bgp.NewAsPathParam(2, []uint16{65001, 65000, 54000, 65004, 65005}),
+ }
+ aspath := bgp.NewPathAttributeAsPath(aspathParam1)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg1 := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ UpdatePathAttrs4ByteAs(updateMsg1.Body.(*bgp.BGPUpdate))
+ path1 := ProcessMessage(updateMsg1, peer, time.Now())[0]
+
+ // create match condition
+ asPathSet1 := config.AsPathSet{
+ AsPathSetName: "asset1",
+ AsPathList: []string{"^65001_65000"},
+ }
+
+ asPathSet2 := config.AsPathSet{
+ AsPathSetName: "asset2",
+ AsPathList: []string{"65004_65005$"},
+ }
+
+ asPathSet3 := config.AsPathSet{
+ AsPathSetName: "asset3",
+ AsPathList: []string{"65001_65000_54000"},
+ }
+
+ asPathSet4 := config.AsPathSet{
+ AsPathSetName: "asset4",
+ AsPathList: []string{"54000_65004_65005"},
+ }
+
+ asPathSet5 := config.AsPathSet{
+ AsPathSetName: "asset5",
+ AsPathList: []string{"^65001 65000 54000 65004 65005$"},
+ }
+
+ asPathSet6 := config.AsPathSet{
+ AsPathSetName: "asset6",
+ AsPathList: []string{".*_[0-9]+_65005"},
+ }
+
+ asPathSet7 := config.AsPathSet{
+ AsPathSetName: "asset7",
+ AsPathList: []string{".*_5[0-9]+_[0-9]+"},
+ }
+
+ asPathSet8 := config.AsPathSet{
+ AsPathSetName: "asset8",
+ AsPathList: []string{"6[0-9]+_6[0-9]+_5[0-9]+"},
+ }
+
+ asPathSet9 := config.AsPathSet{
+ AsPathSetName: "asset9",
+ AsPathList: []string{"6[0-9]+__6[0-9]+"},
+ }
+
+ m := make(map[string]DefinedSet)
+ for _, s := range []config.AsPathSet{asPathSet1, asPathSet2, asPathSet3,
+ asPathSet4, asPathSet5, asPathSet6, asPathSet7, asPathSet8, asPathSet9} {
+ a, _ := NewAsPathSet(s)
+ m[s.AsPathSetName] = a
+ }
+
+ createAspathC := func(name string, option config.MatchSetOptionsType) *AsPathCondition {
+ matchSet := config.MatchAsPathSet{}
+ matchSet.AsPathSet = name
+ matchSet.MatchSetOptions = option
+ p, _ := NewAsPathCondition(matchSet)
+ if v, ok := m[name]; ok {
+ p.set = v.(*AsPathSet)
+ }
+ return p
+ }
+
+ p1 := createAspathC("asset1", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p2 := createAspathC("asset2", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p3 := createAspathC("asset3", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p4 := createAspathC("asset4", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p5 := createAspathC("asset5", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p6 := createAspathC("asset6", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p7 := createAspathC("asset7", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p8 := createAspathC("asset8", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p9 := createAspathC("asset9", config.MATCH_SET_OPTIONS_TYPE_ANY)
+
+ // test
+ assert.Equal(t, true, p1.Evaluate(path1, nil))
+ assert.Equal(t, true, p2.Evaluate(path1, nil))
+ assert.Equal(t, true, p3.Evaluate(path1, nil))
+ assert.Equal(t, true, p4.Evaluate(path1, nil))
+ assert.Equal(t, true, p5.Evaluate(path1, nil))
+ assert.Equal(t, true, p6.Evaluate(path1, nil))
+ assert.Equal(t, true, p7.Evaluate(path1, nil))
+ assert.Equal(t, true, p8.Evaluate(path1, nil))
+ assert.Equal(t, false, p9.Evaluate(path1, nil))
+}
+
+func TestAsPathCondition(t *testing.T) {
+ type astest struct {
+ path *Path
+ result bool
+ }
+
+ makeTest := func(asPathAttrType uint8, ases []uint32, result bool) astest {
+ aspathParam := []bgp.AsPathParamInterface{
+ bgp.NewAs4PathParam(asPathAttrType, ases),
+ }
+ pathAttributes := []bgp.PathAttributeInterface{bgp.NewPathAttributeAsPath(aspathParam)}
+ p := NewPath(nil, nil, false, pathAttributes, time.Time{}, false)
+ return astest{
+ path: p,
+ result: result,
+ }
+ }
+
+ tests := make(map[string][]astest)
+
+ tests["^(100_)+(200_)+$"] = []astest{
+ makeTest(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{100, 200}, true),
+ makeTest(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{100, 100, 200}, true),
+ makeTest(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{100, 100, 200, 200}, true),
+ makeTest(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{100, 100, 200, 200, 300}, false),
+ }
+
+ aslen255 := func() []uint32 {
+ r := make([]uint32, 255)
+ for i := 0; i < 255; i++ {
+ r[i] = 1
+ }
+ return r
+ }()
+ tests["^([0-9]+_){0,255}$"] = []astest{
+ makeTest(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, aslen255, true),
+ makeTest(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, append(aslen255, 1), false),
+ }
+
+ tests["(_7521)$"] = []astest{
+ makeTest(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{7521}, true),
+ makeTest(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{1000, 7521}, true),
+ makeTest(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{7521, 1000}, false),
+ makeTest(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{1000, 7521, 100}, false),
+ }
+
+ tests["^65001( |_.*_)65535$"] = []astest{
+ makeTest(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{65001, 65535}, true),
+ makeTest(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{65001, 65001, 65535}, true),
+ makeTest(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{65001, 65002, 65003, 65535}, true),
+ makeTest(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{65001, 65534}, false),
+ makeTest(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{65002, 65535}, false),
+ makeTest(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{65002, 65001, 65535}, false),
+ makeTest(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{65001, 65535, 65002}, false),
+ makeTest(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{650019, 65535}, false),
+ makeTest(bgp.BGP_ASPATH_ATTR_TYPE_SEQ, []uint32{65001, 165535}, false),
+ }
+
+ for k, v := range tests {
+ s, _ := NewAsPathSet(config.AsPathSet{
+ AsPathSetName: k,
+ AsPathList: []string{k},
+ })
+ c, _ := NewAsPathCondition(config.MatchAsPathSet{
+ AsPathSet: k,
+ MatchSetOptions: config.MATCH_SET_OPTIONS_TYPE_ANY,
+ })
+ c.set = s
+ for _, a := range v {
+ result := c.Evaluate(a.path, nil)
+ if a.result != result {
+ log.WithFields(log.Fields{
+ "EXP": k,
+ "ASSTR": a.path.GetAsString(),
+ "Expected": a.result,
+ "Result": result,
+ }).Fatal("failed")
+ }
+ }
+ }
+}
+
+func TestAsPathConditionWithOtherCondition(t *testing.T) {
+
+ // setup
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{
+ bgp.NewAsPathParam(1, []uint16{65001, 65000, 65004, 65005}),
+ bgp.NewAsPathParam(2, []uint16{65001, 65000, 65004, 65004, 65005}),
+ }
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ UpdatePathAttrs4ByteAs(updateMsg.Body.(*bgp.BGPUpdate))
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+
+ // create policy
+ asPathSet := config.AsPathSet{
+ AsPathSetName: "asset1",
+ AsPathList: []string{"65005$"},
+ }
+
+ ps := createPrefixSet("ps1", "10.10.1.0/16", "21..24")
+ ns := createNeighborSet("ns1", "10.0.0.1")
+
+ ds := config.DefinedSets{}
+ ds.PrefixSets = []config.PrefixSet{ps}
+ ds.NeighborSets = []config.NeighborSet{ns}
+ ds.BgpDefinedSets.AsPathSets = []config.AsPathSet{asPathSet}
+
+ s := createStatement("statement1", "ps1", "ns1", false)
+ s.Conditions.BgpConditions.MatchAsPathSet.AsPathSet = "asset1"
+
+ pd := createPolicyDefinition("pd1", s)
+ pl := createRoutingPolicy(ds, pd)
+
+ //test
+ r := NewRoutingPolicy()
+ err := r.reload(pl)
+ assert.Nil(t, err)
+ p := r.policyMap["pd1"]
+ pType, newPath := p.Apply(path, nil)
+ assert.Equal(t, ROUTE_TYPE_REJECT, pType)
+ assert.Equal(t, newPath, path)
+
+}
+
+func TestAs4PathConditionEvaluate(t *testing.T) {
+
+ // setup
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam1 := []bgp.AsPathParamInterface{
+ bgp.NewAs4PathParam(2, []uint32{
+ createAs4Value("65001.1"),
+ createAs4Value("65000.1"),
+ createAs4Value("65010.1"),
+ createAs4Value("65004.1"),
+ createAs4Value("65005.1"),
+ })}
+
+ aspath := bgp.NewPathAttributeAsPath(aspathParam1)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg1 := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ UpdatePathAttrs4ByteAs(updateMsg1.Body.(*bgp.BGPUpdate))
+ path1 := ProcessMessage(updateMsg1, peer, time.Now())[0]
+
+ aspathParam2 := []bgp.AsPathParamInterface{
+ bgp.NewAs4PathParam(2, []uint32{
+ createAs4Value("65010.1"),
+ }),
+ }
+ aspath2 := bgp.NewPathAttributeAsPath(aspathParam2)
+ pathAttributes = []bgp.PathAttributeInterface{origin, aspath2, nexthop, med}
+ updateMsg2 := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ UpdatePathAttrs4ByteAs(updateMsg2.Body.(*bgp.BGPUpdate))
+ path2 := ProcessMessage(updateMsg2, peer, time.Now())[0]
+
+ // create match condition
+ asPathSet1 := config.AsPathSet{
+ AsPathSetName: "asset1",
+ AsPathList: []string{fmt.Sprintf("^%d", createAs4Value("65001.1"))},
+ }
+
+ asPathSet2 := config.AsPathSet{
+ AsPathSetName: "asset2",
+ AsPathList: []string{fmt.Sprintf("%d$", createAs4Value("65005.1"))},
+ }
+
+ asPathSet3 := config.AsPathSet{
+ AsPathSetName: "asset3",
+ AsPathList: []string{
+ fmt.Sprintf("%d", createAs4Value("65004.1")),
+ fmt.Sprintf("%d$", createAs4Value("65005.1")),
+ },
+ }
+
+ asPathSet4 := config.AsPathSet{
+ AsPathSetName: "asset4",
+ AsPathList: []string{
+ fmt.Sprintf("%d$", createAs4Value("65000.1")),
+ },
+ }
+
+ asPathSet5 := config.AsPathSet{
+ AsPathSetName: "asset5",
+ AsPathList: []string{
+ fmt.Sprintf("%d", createAs4Value("65010.1")),
+ },
+ }
+
+ asPathSet6 := config.AsPathSet{
+ AsPathSetName: "asset6",
+ AsPathList: []string{
+ fmt.Sprintf("%d$", createAs4Value("65010.1")),
+ },
+ }
+
+ m := make(map[string]DefinedSet)
+ for _, s := range []config.AsPathSet{asPathSet1, asPathSet2, asPathSet3,
+ asPathSet4, asPathSet5, asPathSet6} {
+ a, _ := NewAsPathSet(s)
+ m[s.AsPathSetName] = a
+ }
+
+ createAspathC := func(name string, option config.MatchSetOptionsType) *AsPathCondition {
+ matchSet := config.MatchAsPathSet{}
+ matchSet.AsPathSet = name
+ matchSet.MatchSetOptions = option
+ p, _ := NewAsPathCondition(matchSet)
+ if v, ok := m[name]; ok {
+ p.set = v.(*AsPathSet)
+ }
+ return p
+ }
+
+ p1 := createAspathC("asset1", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p2 := createAspathC("asset2", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p3 := createAspathC("asset3", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p4 := createAspathC("asset4", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p5 := createAspathC("asset5", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p6 := createAspathC("asset6", config.MATCH_SET_OPTIONS_TYPE_ANY)
+
+ p7 := createAspathC("asset3", config.MATCH_SET_OPTIONS_TYPE_ALL)
+ p8 := createAspathC("asset3", config.MATCH_SET_OPTIONS_TYPE_INVERT)
+
+ // test
+ assert.Equal(t, true, p1.Evaluate(path1, nil))
+ assert.Equal(t, true, p2.Evaluate(path1, nil))
+ assert.Equal(t, true, p3.Evaluate(path1, nil))
+ assert.Equal(t, false, p4.Evaluate(path1, nil))
+ assert.Equal(t, true, p5.Evaluate(path1, nil))
+ assert.Equal(t, false, p6.Evaluate(path1, nil))
+ assert.Equal(t, true, p6.Evaluate(path2, nil))
+
+ assert.Equal(t, true, p7.Evaluate(path1, nil))
+ assert.Equal(t, true, p8.Evaluate(path2, nil))
+}
+
+func TestMultipleAs4PathConditionEvaluate(t *testing.T) {
+
+ // setup
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam1 := []bgp.AsPathParamInterface{
+ bgp.NewAs4PathParam(2, []uint32{
+ createAs4Value("65001.1"),
+ createAs4Value("65000.1"),
+ createAs4Value("54000.1"),
+ createAs4Value("65004.1"),
+ createAs4Value("65005.1"),
+ }),
+ }
+
+ aspath := bgp.NewPathAttributeAsPath(aspathParam1)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg1 := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ UpdatePathAttrs4ByteAs(updateMsg1.Body.(*bgp.BGPUpdate))
+ path1 := ProcessMessage(updateMsg1, peer, time.Now())[0]
+
+ // create match condition
+ asPathSet1 := config.AsPathSet{
+ AsPathSetName: "asset1",
+ AsPathList: []string{
+ fmt.Sprintf("^%d_%d", createAs4Value("65001.1"), createAs4Value("65000.1")),
+ },
+ }
+
+ asPathSet2 := config.AsPathSet{
+ AsPathSetName: "asset2",
+ AsPathList: []string{
+ fmt.Sprintf("%d_%d$", createAs4Value("65004.1"), createAs4Value("65005.1")),
+ },
+ }
+
+ asPathSet3 := config.AsPathSet{
+ AsPathSetName: "asset3",
+ AsPathList: []string{
+ fmt.Sprintf("%d_%d_%d", createAs4Value("65001.1"), createAs4Value("65000.1"), createAs4Value("54000.1")),
+ },
+ }
+
+ asPathSet4 := config.AsPathSet{
+ AsPathSetName: "asset4",
+ AsPathList: []string{
+ fmt.Sprintf("%d_%d_%d", createAs4Value("54000.1"), createAs4Value("65004.1"), createAs4Value("65005.1")),
+ },
+ }
+
+ asPathSet5 := config.AsPathSet{
+ AsPathSetName: "asset5",
+ AsPathList: []string{
+ fmt.Sprintf("^%d %d %d %d %d$", createAs4Value("65001.1"), createAs4Value("65000.1"), createAs4Value("54000.1"), createAs4Value("65004.1"), createAs4Value("65005.1")),
+ },
+ }
+
+ asPathSet6 := config.AsPathSet{
+ AsPathSetName: "asset6",
+ AsPathList: []string{
+ fmt.Sprintf(".*_[0-9]+_%d", createAs4Value("65005.1")),
+ },
+ }
+
+ asPathSet7 := config.AsPathSet{
+ AsPathSetName: "asset7",
+ AsPathList: []string{".*_3[0-9]+_[0-9]+"},
+ }
+
+ asPathSet8 := config.AsPathSet{
+ AsPathSetName: "asset8",
+ AsPathList: []string{"4[0-9]+_4[0-9]+_3[0-9]+"},
+ }
+
+ asPathSet9 := config.AsPathSet{
+ AsPathSetName: "asset9",
+ AsPathList: []string{"4[0-9]+__4[0-9]+"},
+ }
+
+ m := make(map[string]DefinedSet)
+ for _, s := range []config.AsPathSet{asPathSet1, asPathSet2, asPathSet3,
+ asPathSet4, asPathSet5, asPathSet6, asPathSet7, asPathSet8, asPathSet9} {
+ a, _ := NewAsPathSet(s)
+ m[s.AsPathSetName] = a
+ }
+
+ createAspathC := func(name string, option config.MatchSetOptionsType) *AsPathCondition {
+ matchSet := config.MatchAsPathSet{}
+ matchSet.AsPathSet = name
+ matchSet.MatchSetOptions = option
+ p, _ := NewAsPathCondition(matchSet)
+ if v, ok := m[name]; ok {
+ p.set = v.(*AsPathSet)
+ }
+ return p
+ }
+
+ p1 := createAspathC("asset1", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p2 := createAspathC("asset2", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p3 := createAspathC("asset3", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p4 := createAspathC("asset4", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p5 := createAspathC("asset5", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p6 := createAspathC("asset6", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p7 := createAspathC("asset7", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p8 := createAspathC("asset8", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p9 := createAspathC("asset9", config.MATCH_SET_OPTIONS_TYPE_ANY)
+
+ // test
+ assert.Equal(t, true, p1.Evaluate(path1, nil))
+ assert.Equal(t, true, p2.Evaluate(path1, nil))
+ assert.Equal(t, true, p3.Evaluate(path1, nil))
+ assert.Equal(t, true, p4.Evaluate(path1, nil))
+ assert.Equal(t, true, p5.Evaluate(path1, nil))
+ assert.Equal(t, true, p6.Evaluate(path1, nil))
+ assert.Equal(t, true, p7.Evaluate(path1, nil))
+ assert.Equal(t, true, p8.Evaluate(path1, nil))
+ assert.Equal(t, false, p9.Evaluate(path1, nil))
+}
+
+func TestAs4PathConditionWithOtherCondition(t *testing.T) {
+
+ // setup
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{
+ bgp.NewAs4PathParam(1, []uint32{
+ createAs4Value("65001.1"),
+ createAs4Value("65000.1"),
+ createAs4Value("65004.1"),
+ createAs4Value("65005.1"),
+ }),
+ bgp.NewAs4PathParam(2, []uint32{
+ createAs4Value("65001.1"),
+ createAs4Value("65000.1"),
+ createAs4Value("65004.1"),
+ createAs4Value("65004.1"),
+ createAs4Value("65005.1"),
+ }),
+ }
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ UpdatePathAttrs4ByteAs(updateMsg.Body.(*bgp.BGPUpdate))
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+
+ // create policy
+ asPathSet := config.AsPathSet{
+ AsPathSetName: "asset1",
+ AsPathList: []string{fmt.Sprintf("%d$", createAs4Value("65005.1"))},
+ }
+
+ ps := createPrefixSet("ps1", "10.10.1.0/16", "21..24")
+ ns := createNeighborSet("ns1", "10.0.0.1")
+
+ ds := config.DefinedSets{}
+ ds.PrefixSets = []config.PrefixSet{ps}
+ ds.NeighborSets = []config.NeighborSet{ns}
+ ds.BgpDefinedSets.AsPathSets = []config.AsPathSet{asPathSet}
+
+ s := createStatement("statement1", "ps1", "ns1", false)
+ s.Conditions.BgpConditions.MatchAsPathSet.AsPathSet = "asset1"
+
+ pd := createPolicyDefinition("pd1", s)
+ pl := createRoutingPolicy(ds, pd)
+
+ //test
+ r := NewRoutingPolicy()
+ r.reload(pl)
+ p, _ := NewPolicy(pl.PolicyDefinitions[0])
+ addPolicy(r, p)
+ pType, newPath := p.Apply(path, nil)
+ assert.Equal(t, ROUTE_TYPE_REJECT, pType)
+ assert.Equal(t, newPath, path)
+
+}
+
+func TestAs4PathConditionEvaluateMixedWith2byteAS(t *testing.T) {
+
+ // setup
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam1 := []bgp.AsPathParamInterface{
+ bgp.NewAs4PathParam(2, []uint32{
+ createAs4Value("65001.1"),
+ createAs4Value("65000.1"),
+ createAs4Value("54000.1"),
+ 100,
+ 5000,
+ createAs4Value("65004.1"),
+ createAs4Value("65005.1"),
+ 4000,
+ }),
+ }
+
+ aspath := bgp.NewPathAttributeAsPath(aspathParam1)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg1 := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ UpdatePathAttrs4ByteAs(updateMsg1.Body.(*bgp.BGPUpdate))
+ path1 := ProcessMessage(updateMsg1, peer, time.Now())[0]
+
+ // create match condition
+ asPathSet1 := config.AsPathSet{
+ AsPathSetName: "asset1",
+ AsPathList: []string{fmt.Sprintf("^%d", createAs4Value("65001.1"))},
+ }
+
+ asPathSet2 := config.AsPathSet{
+ AsPathSetName: "asset2",
+ AsPathList: []string{"4000$"},
+ }
+
+ asPathSet3 := config.AsPathSet{
+ AsPathSetName: "asset3",
+ AsPathList: []string{fmt.Sprintf("%d", createAs4Value("65004.1")), "4000$"},
+ }
+
+ asPathSet4 := config.AsPathSet{
+ AsPathSetName: "asset4",
+ AsPathList: []string{fmt.Sprintf("%d_%d_%d", createAs4Value("54000.1"), 100, 5000)},
+ }
+
+ asPathSet5 := config.AsPathSet{
+ AsPathSetName: "asset5",
+ AsPathList: []string{".*_[0-9]+_100"},
+ }
+
+ asPathSet6 := config.AsPathSet{
+ AsPathSetName: "asset6",
+ AsPathList: []string{".*_3[0-9]+_[0]+"},
+ }
+
+ asPathSet7 := config.AsPathSet{
+ AsPathSetName: "asset7",
+ AsPathList: []string{".*_3[0-9]+_[1]+"},
+ }
+
+ m := make(map[string]DefinedSet)
+ for _, s := range []config.AsPathSet{asPathSet1, asPathSet2, asPathSet3,
+ asPathSet4, asPathSet5, asPathSet6, asPathSet7} {
+ a, _ := NewAsPathSet(s)
+ m[s.AsPathSetName] = a
+ }
+
+ createAspathC := func(name string, option config.MatchSetOptionsType) *AsPathCondition {
+ matchSet := config.MatchAsPathSet{}
+ matchSet.AsPathSet = name
+ matchSet.MatchSetOptions = option
+ p, _ := NewAsPathCondition(matchSet)
+ if v, ok := m[name]; ok {
+ p.set = v.(*AsPathSet)
+ }
+ return p
+ }
+
+ p1 := createAspathC("asset1", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p2 := createAspathC("asset2", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p3 := createAspathC("asset3", config.MATCH_SET_OPTIONS_TYPE_ALL)
+ p4 := createAspathC("asset4", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p5 := createAspathC("asset5", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p6 := createAspathC("asset6", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p7 := createAspathC("asset7", config.MATCH_SET_OPTIONS_TYPE_ANY)
+
+ // test
+ assert.Equal(t, true, p1.Evaluate(path1, nil))
+ assert.Equal(t, true, p2.Evaluate(path1, nil))
+ assert.Equal(t, true, p3.Evaluate(path1, nil))
+ assert.Equal(t, true, p4.Evaluate(path1, nil))
+ assert.Equal(t, true, p5.Evaluate(path1, nil))
+ assert.Equal(t, false, p6.Evaluate(path1, nil))
+ assert.Equal(t, true, p7.Evaluate(path1, nil))
+
+}
+
+func TestCommunityConditionEvaluate(t *testing.T) {
+
+ // setup
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam1 := []bgp.AsPathParamInterface{
+ bgp.NewAsPathParam(2, []uint16{65001, 65000, 65004, 65005}),
+ bgp.NewAsPathParam(1, []uint16{65001, 65010, 65004, 65005}),
+ }
+ aspath := bgp.NewPathAttributeAsPath(aspathParam1)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ communities := bgp.NewPathAttributeCommunities([]uint32{
+ stringToCommunityValue("65001:100"),
+ stringToCommunityValue("65001:200"),
+ stringToCommunityValue("65001:300"),
+ stringToCommunityValue("65001:400"),
+ 0x00000000,
+ 0xFFFFFF01,
+ 0xFFFFFF02,
+ 0xFFFFFF03})
+
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med, communities}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg1 := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ UpdatePathAttrs4ByteAs(updateMsg1.Body.(*bgp.BGPUpdate))
+ path1 := ProcessMessage(updateMsg1, peer, time.Now())[0]
+
+ communities2 := bgp.NewPathAttributeCommunities([]uint32{
+ stringToCommunityValue("65001:100"),
+ stringToCommunityValue("65001:200"),
+ stringToCommunityValue("65001:300"),
+ stringToCommunityValue("65001:400")})
+
+ pathAttributes2 := []bgp.PathAttributeInterface{origin, aspath, nexthop, med, communities2}
+ updateMsg2 := bgp.NewBGPUpdateMessage(nil, pathAttributes2, nlri)
+ UpdatePathAttrs4ByteAs(updateMsg2.Body.(*bgp.BGPUpdate))
+ path2 := ProcessMessage(updateMsg2, peer, time.Now())[0]
+
+ // create match condition
+ comSet1 := config.CommunitySet{
+ CommunitySetName: "comset1",
+ CommunityList: []string{"65001:10", "65001:50", "65001:100"},
+ }
+
+ comSet2 := config.CommunitySet{
+ CommunitySetName: "comset2",
+ CommunityList: []string{"65001:200"},
+ }
+
+ comSet3 := config.CommunitySet{
+ CommunitySetName: "comset3",
+ CommunityList: []string{"4259905936"},
+ }
+
+ comSet4 := config.CommunitySet{
+ CommunitySetName: "comset4",
+ CommunityList: []string{"^[0-9]*:300$"},
+ }
+
+ comSet5 := config.CommunitySet{
+ CommunitySetName: "comset5",
+ CommunityList: []string{"INTERNET"},
+ }
+
+ comSet6 := config.CommunitySet{
+ CommunitySetName: "comset6",
+ CommunityList: []string{"NO_EXPORT"},
+ }
+
+ comSet7 := config.CommunitySet{
+ CommunitySetName: "comset7",
+ CommunityList: []string{"NO_ADVERTISE"},
+ }
+
+ comSet8 := config.CommunitySet{
+ CommunitySetName: "comset8",
+ CommunityList: []string{"NO_EXPORT_SUBCONFED"},
+ }
+
+ comSet9 := config.CommunitySet{
+ CommunitySetName: "comset9",
+ CommunityList: []string{
+ "65001:\\d+",
+ "\\d+:\\d00",
+ },
+ }
+
+ comSet10 := config.CommunitySet{
+ CommunitySetName: "comset10",
+ CommunityList: []string{
+ "65001:1",
+ "65001:2",
+ "65001:3",
+ },
+ }
+
+ m := make(map[string]DefinedSet)
+
+ for _, c := range []config.CommunitySet{comSet1, comSet2, comSet3,
+ comSet4, comSet5, comSet6, comSet7, comSet8, comSet9, comSet10} {
+ s, _ := NewCommunitySet(c)
+ m[c.CommunitySetName] = s
+ }
+
+ createCommunityC := func(name string, option config.MatchSetOptionsType) *CommunityCondition {
+ matchSet := config.MatchCommunitySet{}
+ matchSet.CommunitySet = name
+ matchSet.MatchSetOptions = option
+ c, _ := NewCommunityCondition(matchSet)
+ if v, ok := m[name]; ok {
+ c.set = v.(*CommunitySet)
+ }
+ return c
+ }
+
+ // ANY case
+ p1 := createCommunityC("comset1", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p2 := createCommunityC("comset2", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p3 := createCommunityC("comset3", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p4 := createCommunityC("comset4", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p5 := createCommunityC("comset5", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p6 := createCommunityC("comset6", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p7 := createCommunityC("comset7", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p8 := createCommunityC("comset8", config.MATCH_SET_OPTIONS_TYPE_ANY)
+
+ // ALL case
+ p9 := createCommunityC("comset9", config.MATCH_SET_OPTIONS_TYPE_ALL)
+
+ // INVERT case
+ p10 := createCommunityC("comset10", config.MATCH_SET_OPTIONS_TYPE_INVERT)
+
+ // test
+ assert.Equal(t, true, p1.Evaluate(path1, nil))
+ assert.Equal(t, true, p2.Evaluate(path1, nil))
+ assert.Equal(t, true, p3.Evaluate(path1, nil))
+ assert.Equal(t, true, p4.Evaluate(path1, nil))
+ assert.Equal(t, true, p5.Evaluate(path1, nil))
+ assert.Equal(t, true, p6.Evaluate(path1, nil))
+ assert.Equal(t, true, p7.Evaluate(path1, nil))
+ assert.Equal(t, true, p8.Evaluate(path1, nil))
+ assert.Equal(t, true, p9.Evaluate(path2, nil))
+ assert.Equal(t, true, p10.Evaluate(path1, nil))
+
+}
+
+func TestCommunityConditionEvaluateWithOtherCondition(t *testing.T) {
+
+ // setup
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{
+ bgp.NewAsPathParam(1, []uint16{65001, 65000, 65004, 65005}),
+ bgp.NewAsPathParam(2, []uint16{65001, 65000, 65004, 65004, 65005}),
+ }
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ communities := bgp.NewPathAttributeCommunities([]uint32{
+ stringToCommunityValue("65001:100"),
+ stringToCommunityValue("65001:200"),
+ stringToCommunityValue("65001:300"),
+ stringToCommunityValue("65001:400"),
+ 0x00000000,
+ 0xFFFFFF01,
+ 0xFFFFFF02,
+ 0xFFFFFF03})
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med, communities}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ UpdatePathAttrs4ByteAs(updateMsg.Body.(*bgp.BGPUpdate))
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+
+ // create policy
+ asPathSet := config.AsPathSet{
+ AsPathSetName: "asset1",
+ AsPathList: []string{"65005$"},
+ }
+
+ comSet1 := config.CommunitySet{
+ CommunitySetName: "comset1",
+ CommunityList: []string{"65001:100", "65001:200", "65001:300"},
+ }
+
+ comSet2 := config.CommunitySet{
+ CommunitySetName: "comset2",
+ CommunityList: []string{"65050:\\d+"},
+ }
+
+ ps := createPrefixSet("ps1", "10.10.0.0/16", "21..24")
+ ns := createNeighborSet("ns1", "10.0.0.1")
+
+ ds := config.DefinedSets{}
+ ds.PrefixSets = []config.PrefixSet{ps}
+ ds.NeighborSets = []config.NeighborSet{ns}
+ ds.BgpDefinedSets.AsPathSets = []config.AsPathSet{asPathSet}
+ ds.BgpDefinedSets.CommunitySets = []config.CommunitySet{comSet1, comSet2}
+
+ s1 := createStatement("statement1", "ps1", "ns1", false)
+ s1.Conditions.BgpConditions.MatchAsPathSet.AsPathSet = "asset1"
+ s1.Conditions.BgpConditions.MatchCommunitySet.CommunitySet = "comset1"
+
+ s2 := createStatement("statement2", "ps1", "ns1", false)
+ s2.Conditions.BgpConditions.MatchAsPathSet.AsPathSet = "asset1"
+ s2.Conditions.BgpConditions.MatchCommunitySet.CommunitySet = "comset2"
+
+ pd1 := createPolicyDefinition("pd1", s1)
+ pd2 := createPolicyDefinition("pd2", s2)
+ pl := createRoutingPolicy(ds, pd1, pd2)
+
+ //test
+ r := NewRoutingPolicy()
+ err := r.reload(pl)
+ assert.Nil(t, err)
+ p := r.policyMap["pd1"]
+ pType, newPath := p.Apply(path, nil)
+ assert.Equal(t, ROUTE_TYPE_REJECT, pType)
+ assert.Equal(t, newPath, path)
+
+ p = r.policyMap["pd2"]
+ pType, newPath = p.Apply(path, nil)
+ assert.Equal(t, ROUTE_TYPE_NONE, pType)
+ assert.Equal(t, newPath, path)
+
+}
+
+func TestPolicyMatchAndAddCommunities(t *testing.T) {
+
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+ // create policy
+ ps := createPrefixSet("ps1", "10.10.0.0/16", "21..24")
+ ns := createNeighborSet("ns1", "10.0.0.1")
+
+ ds := config.DefinedSets{}
+ ds.PrefixSets = []config.PrefixSet{ps}
+ ds.NeighborSets = []config.NeighborSet{ns}
+
+ community := "65000:100"
+
+ s := createStatement("statement1", "ps1", "ns1", true)
+ s.Actions.BgpActions.SetCommunity = createSetCommunity("ADD", community)
+
+ pd := createPolicyDefinition("pd1", s)
+ pl := createRoutingPolicy(ds, pd)
+
+ //test
+ r := NewRoutingPolicy()
+ err := r.reload(pl)
+ assert.Nil(t, err)
+ p := r.policyMap["pd1"]
+
+ pType, newPath := p.Apply(path, nil)
+ assert.Equal(t, ROUTE_TYPE_ACCEPT, pType)
+ assert.NotEqual(t, nil, newPath)
+ assert.Equal(t, []uint32{stringToCommunityValue(community)}, newPath.GetCommunities())
+}
+
+func TestPolicyMatchAndReplaceCommunities(t *testing.T) {
+
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ communities := bgp.NewPathAttributeCommunities([]uint32{
+ stringToCommunityValue("65001:200"),
+ })
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med, communities}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+ // create policy
+ ps := createPrefixSet("ps1", "10.10.0.0/16", "21..24")
+ ns := createNeighborSet("ns1", "10.0.0.1")
+
+ ds := config.DefinedSets{}
+ ds.PrefixSets = []config.PrefixSet{ps}
+ ds.NeighborSets = []config.NeighborSet{ns}
+
+ community := "65000:100"
+
+ s := createStatement("statement1", "ps1", "ns1", true)
+ s.Actions.BgpActions.SetCommunity = createSetCommunity("REPLACE", community)
+
+ pd := createPolicyDefinition("pd1", s)
+ pl := createRoutingPolicy(ds, pd)
+
+ //test
+ r := NewRoutingPolicy()
+ err := r.reload(pl)
+ assert.Nil(t, err)
+ p := r.policyMap["pd1"]
+
+ pType, newPath := p.Apply(path, nil)
+ assert.Equal(t, ROUTE_TYPE_ACCEPT, pType)
+ assert.NotEqual(t, nil, newPath)
+ assert.Equal(t, []uint32{stringToCommunityValue(community)}, newPath.GetCommunities())
+}
+
+func TestPolicyMatchAndRemoveCommunities(t *testing.T) {
+
+ // create path
+ community1 := "65000:100"
+ community2 := "65000:200"
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ communities := bgp.NewPathAttributeCommunities([]uint32{
+ stringToCommunityValue(community1),
+ stringToCommunityValue(community2),
+ })
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med, communities}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+ // create policy
+ ps := createPrefixSet("ps1", "10.10.0.0/16", "21..24")
+ ns := createNeighborSet("ns1", "10.0.0.1")
+
+ ds := config.DefinedSets{}
+ ds.PrefixSets = []config.PrefixSet{ps}
+ ds.NeighborSets = []config.NeighborSet{ns}
+
+ s := createStatement("statement1", "ps1", "ns1", true)
+ s.Actions.BgpActions.SetCommunity = createSetCommunity("REMOVE", community1)
+
+ pd := createPolicyDefinition("pd1", s)
+ pl := createRoutingPolicy(ds, pd)
+
+ //test
+ r := NewRoutingPolicy()
+ err := r.reload(pl)
+ assert.Nil(t, err)
+ p := r.policyMap["pd1"]
+ pType, newPath := p.Apply(path, nil)
+ assert.Equal(t, ROUTE_TYPE_ACCEPT, pType)
+ assert.NotEqual(t, nil, newPath)
+ assert.Equal(t, []uint32{stringToCommunityValue(community2)}, newPath.GetCommunities())
+}
+
+func TestPolicyMatchAndRemoveCommunitiesRegexp(t *testing.T) {
+
+ // create path
+ community1 := "65000:100"
+ community2 := "65000:200"
+ community3 := "65100:100"
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ communities := bgp.NewPathAttributeCommunities([]uint32{
+ stringToCommunityValue(community1),
+ stringToCommunityValue(community2),
+ stringToCommunityValue(community3),
+ })
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med, communities}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+ // create policy
+ ps := createPrefixSet("ps1", "10.10.0.0/16", "21..24")
+ ns := createNeighborSet("ns1", "10.0.0.1")
+
+ ds := config.DefinedSets{}
+ ds.PrefixSets = []config.PrefixSet{ps}
+ ds.NeighborSets = []config.NeighborSet{ns}
+
+ s := createStatement("statement1", "ps1", "ns1", true)
+ s.Actions.BgpActions.SetCommunity = createSetCommunity("REMOVE", ".*:100")
+
+ pd := createPolicyDefinition("pd1", s)
+ pl := createRoutingPolicy(ds, pd)
+
+ //test
+ r := NewRoutingPolicy()
+ err := r.reload(pl)
+ assert.Nil(t, err)
+ p := r.policyMap["pd1"]
+ pType, newPath := p.Apply(path, nil)
+ assert.Equal(t, ROUTE_TYPE_ACCEPT, pType)
+ assert.NotEqual(t, nil, newPath)
+ assert.Equal(t, []uint32{stringToCommunityValue(community2)}, newPath.GetCommunities())
+}
+
+func TestPolicyMatchAndRemoveCommunitiesRegexp2(t *testing.T) {
+
+ // create path
+ community1 := "0:1"
+ community2 := "10:1"
+ community3 := "45686:2"
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ communities := bgp.NewPathAttributeCommunities([]uint32{
+ stringToCommunityValue(community1),
+ stringToCommunityValue(community2),
+ stringToCommunityValue(community3),
+ })
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med, communities}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+ // create policy
+ ps := createPrefixSet("ps1", "10.10.0.0/16", "21..24")
+ ns := createNeighborSet("ns1", "10.0.0.1")
+
+ ds := config.DefinedSets{}
+ ds.PrefixSets = []config.PrefixSet{ps}
+ ds.NeighborSets = []config.NeighborSet{ns}
+
+ s := createStatement("statement1", "ps1", "ns1", true)
+ s.Actions.BgpActions.SetCommunity = createSetCommunity("REMOVE", "^(0|45686):[0-9]+")
+
+ pd := createPolicyDefinition("pd1", s)
+ pl := createRoutingPolicy(ds, pd)
+
+ //test
+ r := NewRoutingPolicy()
+ err := r.reload(pl)
+ assert.Nil(t, err)
+ p := r.policyMap["pd1"]
+ pType, newPath := p.Apply(path, nil)
+ assert.Equal(t, ROUTE_TYPE_ACCEPT, pType)
+ assert.NotEqual(t, nil, newPath)
+ assert.Equal(t, []uint32{stringToCommunityValue(community2)}, newPath.GetCommunities())
+}
+
+func TestPolicyMatchAndClearCommunities(t *testing.T) {
+
+ // create path
+ community1 := "65000:100"
+ community2 := "65000:200"
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ communities := bgp.NewPathAttributeCommunities([]uint32{
+ stringToCommunityValue(community1),
+ stringToCommunityValue(community2),
+ })
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med, communities}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+ // create policy
+ ps := createPrefixSet("ps1", "10.10.0.0/16", "21..24")
+ ns := createNeighborSet("ns1", "10.0.0.1")
+
+ ds := config.DefinedSets{}
+ ds.PrefixSets = []config.PrefixSet{ps}
+ ds.NeighborSets = []config.NeighborSet{ns}
+
+ s := createStatement("statement1", "ps1", "ns1", true)
+ // action NULL is obsolate
+ s.Actions.BgpActions.SetCommunity.Options = "REPLACE"
+ s.Actions.BgpActions.SetCommunity.SetCommunityMethod.CommunitiesList = nil
+
+ pd := createPolicyDefinition("pd1", s)
+ pl := createRoutingPolicy(ds, pd)
+
+ //test
+ r := NewRoutingPolicy()
+ err := r.reload(pl)
+ assert.Nil(t, err)
+ p := r.policyMap["pd1"]
+
+ pType, newPath := p.Apply(path, nil)
+ assert.Equal(t, ROUTE_TYPE_ACCEPT, pType)
+ assert.NotEqual(t, nil, newPath)
+ //assert.Equal(t, []uint32{}, newPath.GetCommunities())
+}
+
+func TestExtCommunityConditionEvaluate(t *testing.T) {
+
+ // setup
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam1 := []bgp.AsPathParamInterface{
+ bgp.NewAsPathParam(2, []uint16{65001, 65000, 65004, 65005}),
+ bgp.NewAsPathParam(1, []uint16{65001, 65010, 65004, 65005}),
+ }
+ aspath := bgp.NewPathAttributeAsPath(aspathParam1)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ eComAsSpecific1 := &bgp.TwoOctetAsSpecificExtended{
+ SubType: bgp.ExtendedCommunityAttrSubType(bgp.EC_SUBTYPE_ROUTE_TARGET),
+ AS: 65001,
+ LocalAdmin: 200,
+ IsTransitive: true,
+ }
+ eComIpPrefix1 := &bgp.IPv4AddressSpecificExtended{
+ SubType: bgp.ExtendedCommunityAttrSubType(bgp.EC_SUBTYPE_ROUTE_TARGET),
+ IPv4: net.ParseIP("10.0.0.1"),
+ LocalAdmin: 300,
+ IsTransitive: true,
+ }
+ eComAs4Specific1 := &bgp.FourOctetAsSpecificExtended{
+ SubType: bgp.ExtendedCommunityAttrSubType(bgp.EC_SUBTYPE_ROUTE_TARGET),
+ AS: 65030000,
+ LocalAdmin: 200,
+ IsTransitive: true,
+ }
+ eComAsSpecific2 := &bgp.TwoOctetAsSpecificExtended{
+ SubType: bgp.ExtendedCommunityAttrSubType(bgp.EC_SUBTYPE_ROUTE_TARGET),
+ AS: 65002,
+ LocalAdmin: 200,
+ IsTransitive: false,
+ }
+ eComIpPrefix2 := &bgp.IPv4AddressSpecificExtended{
+ SubType: bgp.ExtendedCommunityAttrSubType(bgp.EC_SUBTYPE_ROUTE_TARGET),
+ IPv4: net.ParseIP("10.0.0.2"),
+ LocalAdmin: 300,
+ IsTransitive: false,
+ }
+ eComAs4Specific2 := &bgp.FourOctetAsSpecificExtended{
+ SubType: bgp.ExtendedCommunityAttrSubType(bgp.EC_SUBTYPE_ROUTE_TARGET),
+ AS: 65030001,
+ LocalAdmin: 200,
+ IsTransitive: false,
+ }
+ eComAsSpecific3 := &bgp.TwoOctetAsSpecificExtended{
+ SubType: bgp.ExtendedCommunityAttrSubType(bgp.EC_SUBTYPE_ROUTE_ORIGIN),
+ AS: 65010,
+ LocalAdmin: 300,
+ IsTransitive: true,
+ }
+ eComIpPrefix3 := &bgp.IPv4AddressSpecificExtended{
+ SubType: bgp.ExtendedCommunityAttrSubType(bgp.EC_SUBTYPE_ROUTE_ORIGIN),
+ IPv4: net.ParseIP("10.0.10.10"),
+ LocalAdmin: 400,
+ IsTransitive: true,
+ }
+ eComAs4Specific3 := &bgp.FourOctetAsSpecificExtended{
+ SubType: bgp.ExtendedCommunityAttrSubType(bgp.EC_SUBTYPE_ROUTE_TARGET),
+ AS: 65030002,
+ LocalAdmin: 500,
+ IsTransitive: true,
+ }
+ ec := []bgp.ExtendedCommunityInterface{eComAsSpecific1, eComIpPrefix1, eComAs4Specific1, eComAsSpecific2,
+ eComIpPrefix2, eComAs4Specific2, eComAsSpecific3, eComIpPrefix3, eComAs4Specific3}
+ extCommunities := bgp.NewPathAttributeExtendedCommunities(ec)
+
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med, extCommunities}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg1 := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ UpdatePathAttrs4ByteAs(updateMsg1.Body.(*bgp.BGPUpdate))
+ path1 := ProcessMessage(updateMsg1, peer, time.Now())[0]
+
+ convUintStr := func(as uint32) string {
+ upper := strconv.FormatUint(uint64(as&0xFFFF0000>>16), 10)
+ lower := strconv.FormatUint(uint64(as&0x0000FFFF), 10)
+ str := fmt.Sprintf("%s.%s", upper, lower)
+ return str
+ }
+
+ // create match condition
+ ecomSet1 := config.ExtCommunitySet{
+ ExtCommunitySetName: "ecomSet1",
+ ExtCommunityList: []string{"RT:65001:200"},
+ }
+ ecomSet2 := config.ExtCommunitySet{
+ ExtCommunitySetName: "ecomSet2",
+ ExtCommunityList: []string{"RT:10.0.0.1:300"},
+ }
+ ecomSet3 := config.ExtCommunitySet{
+ ExtCommunitySetName: "ecomSet3",
+ ExtCommunityList: []string{fmt.Sprintf("RT:%s:200", convUintStr(65030000))},
+ }
+ ecomSet4 := config.ExtCommunitySet{
+ ExtCommunitySetName: "ecomSet4",
+ ExtCommunityList: []string{"RT:65002:200"},
+ }
+ ecomSet5 := config.ExtCommunitySet{
+ ExtCommunitySetName: "ecomSet5",
+ ExtCommunityList: []string{"RT:10.0.0.2:300"},
+ }
+ ecomSet6 := config.ExtCommunitySet{
+ ExtCommunitySetName: "ecomSet6",
+ ExtCommunityList: []string{fmt.Sprintf("RT:%s:200", convUintStr(65030001))},
+ }
+ ecomSet7 := config.ExtCommunitySet{
+ ExtCommunitySetName: "ecomSet7",
+ ExtCommunityList: []string{"SoO:65010:300"},
+ }
+ ecomSet8 := config.ExtCommunitySet{
+ ExtCommunitySetName: "ecomSet8",
+ ExtCommunityList: []string{"SoO:10.0.10.10:[0-9]+"},
+ }
+ ecomSet9 := config.ExtCommunitySet{
+ ExtCommunitySetName: "ecomSet9",
+ ExtCommunityList: []string{"RT:[0-9]+:[0-9]+"},
+ }
+ ecomSet10 := config.ExtCommunitySet{
+ ExtCommunitySetName: "ecomSet10",
+ ExtCommunityList: []string{"RT:.+:\\d00", "SoO:.+:\\d00"},
+ }
+
+ ecomSet11 := config.ExtCommunitySet{
+ ExtCommunitySetName: "ecomSet11",
+ ExtCommunityList: []string{"RT:65001:2", "SoO:11.0.10.10:[0-9]+"},
+ }
+
+ m := make(map[string]DefinedSet)
+ for _, c := range []config.ExtCommunitySet{ecomSet1, ecomSet2, ecomSet3, ecomSet4, ecomSet5, ecomSet6, ecomSet7,
+ ecomSet8, ecomSet9, ecomSet10, ecomSet11} {
+ s, _ := NewExtCommunitySet(c)
+ m[s.Name()] = s
+ }
+
+ createExtCommunityC := func(name string, option config.MatchSetOptionsType) *ExtCommunityCondition {
+ matchSet := config.MatchExtCommunitySet{}
+ matchSet.ExtCommunitySet = name
+ matchSet.MatchSetOptions = option
+ c, _ := NewExtCommunityCondition(matchSet)
+ if v, ok := m[name]; ok {
+ c.set = v.(*ExtCommunitySet)
+ }
+
+ return c
+ }
+
+ p1 := createExtCommunityC("ecomSet1", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p2 := createExtCommunityC("ecomSet2", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p3 := createExtCommunityC("ecomSet3", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p4 := createExtCommunityC("ecomSet4", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p5 := createExtCommunityC("ecomSet5", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p6 := createExtCommunityC("ecomSet6", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p7 := createExtCommunityC("ecomSet7", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p8 := createExtCommunityC("ecomSet8", config.MATCH_SET_OPTIONS_TYPE_ANY)
+ p9 := createExtCommunityC("ecomSet9", config.MATCH_SET_OPTIONS_TYPE_ANY)
+
+ // ALL case
+ p10 := createExtCommunityC("ecomSet10", config.MATCH_SET_OPTIONS_TYPE_ALL)
+
+ // INVERT case
+ p11 := createExtCommunityC("ecomSet11", config.MATCH_SET_OPTIONS_TYPE_INVERT)
+
+ // test
+ assert.Equal(t, true, p1.Evaluate(path1, nil))
+ assert.Equal(t, true, p2.Evaluate(path1, nil))
+ assert.Equal(t, true, p3.Evaluate(path1, nil))
+ assert.Equal(t, false, p4.Evaluate(path1, nil))
+ assert.Equal(t, false, p5.Evaluate(path1, nil))
+ assert.Equal(t, false, p6.Evaluate(path1, nil))
+ assert.Equal(t, true, p7.Evaluate(path1, nil))
+ assert.Equal(t, true, p8.Evaluate(path1, nil))
+ assert.Equal(t, true, p9.Evaluate(path1, nil))
+ assert.Equal(t, true, p10.Evaluate(path1, nil))
+ assert.Equal(t, true, p11.Evaluate(path1, nil))
+
+}
+
+func TestExtCommunityConditionEvaluateWithOtherCondition(t *testing.T) {
+
+ // setup
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.2.1.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{
+ bgp.NewAsPathParam(1, []uint16{65001, 65000, 65004, 65005}),
+ bgp.NewAsPathParam(2, []uint16{65001, 65000, 65004, 65004, 65005}),
+ }
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.2.1.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ eComAsSpecific1 := &bgp.TwoOctetAsSpecificExtended{
+ SubType: bgp.ExtendedCommunityAttrSubType(bgp.EC_SUBTYPE_ROUTE_TARGET),
+ AS: 65001,
+ LocalAdmin: 200,
+ IsTransitive: true,
+ }
+ eComIpPrefix1 := &bgp.IPv4AddressSpecificExtended{
+ SubType: bgp.ExtendedCommunityAttrSubType(bgp.EC_SUBTYPE_ROUTE_TARGET),
+ IPv4: net.ParseIP("10.0.0.1"),
+ LocalAdmin: 300,
+ IsTransitive: true,
+ }
+ eComAs4Specific1 := &bgp.FourOctetAsSpecificExtended{
+ SubType: bgp.ExtendedCommunityAttrSubType(bgp.EC_SUBTYPE_ROUTE_TARGET),
+ AS: 65030000,
+ LocalAdmin: 200,
+ IsTransitive: true,
+ }
+ eComAsSpecific2 := &bgp.TwoOctetAsSpecificExtended{
+ SubType: bgp.ExtendedCommunityAttrSubType(bgp.EC_SUBTYPE_ROUTE_TARGET),
+ AS: 65002,
+ LocalAdmin: 200,
+ IsTransitive: false,
+ }
+ eComIpPrefix2 := &bgp.IPv4AddressSpecificExtended{
+ SubType: bgp.ExtendedCommunityAttrSubType(bgp.EC_SUBTYPE_ROUTE_TARGET),
+ IPv4: net.ParseIP("10.0.0.2"),
+ LocalAdmin: 300,
+ IsTransitive: false,
+ }
+ eComAs4Specific2 := &bgp.FourOctetAsSpecificExtended{
+ SubType: bgp.ExtendedCommunityAttrSubType(bgp.EC_SUBTYPE_ROUTE_TARGET),
+ AS: 65030001,
+ LocalAdmin: 200,
+ IsTransitive: false,
+ }
+ eComAsSpecific3 := &bgp.TwoOctetAsSpecificExtended{
+ SubType: bgp.ExtendedCommunityAttrSubType(bgp.EC_SUBTYPE_ROUTE_ORIGIN),
+ AS: 65010,
+ LocalAdmin: 300,
+ IsTransitive: true,
+ }
+ eComIpPrefix3 := &bgp.IPv4AddressSpecificExtended{
+ SubType: bgp.ExtendedCommunityAttrSubType(bgp.EC_SUBTYPE_ROUTE_ORIGIN),
+ IPv4: net.ParseIP("10.0.10.10"),
+ LocalAdmin: 400,
+ IsTransitive: true,
+ }
+ eComAs4Specific3 := &bgp.FourOctetAsSpecificExtended{
+ SubType: bgp.ExtendedCommunityAttrSubType(bgp.EC_SUBTYPE_ROUTE_TARGET),
+ AS: 65030002,
+ LocalAdmin: 500,
+ IsTransitive: true,
+ }
+ ec := []bgp.ExtendedCommunityInterface{eComAsSpecific1, eComIpPrefix1, eComAs4Specific1, eComAsSpecific2,
+ eComIpPrefix2, eComAs4Specific2, eComAsSpecific3, eComIpPrefix3, eComAs4Specific3}
+ extCommunities := bgp.NewPathAttributeExtendedCommunities(ec)
+
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med, extCommunities}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ UpdatePathAttrs4ByteAs(updateMsg.Body.(*bgp.BGPUpdate))
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+
+ // create policy
+ asPathSet := config.AsPathSet{
+ AsPathSetName: "asset1",
+ AsPathList: []string{"65005$"},
+ }
+
+ ecomSet1 := config.ExtCommunitySet{
+ ExtCommunitySetName: "ecomSet1",
+ ExtCommunityList: []string{"RT:65001:201"},
+ }
+ ecomSet2 := config.ExtCommunitySet{
+ ExtCommunitySetName: "ecomSet2",
+ ExtCommunityList: []string{"RT:[0-9]+:[0-9]+"},
+ }
+
+ ps := createPrefixSet("ps1", "10.10.1.0/16", "21..24")
+ ns := createNeighborSet("ns1", "10.2.1.1")
+
+ ds := config.DefinedSets{}
+ ds.PrefixSets = []config.PrefixSet{ps}
+ ds.NeighborSets = []config.NeighborSet{ns}
+ ds.BgpDefinedSets.AsPathSets = []config.AsPathSet{asPathSet}
+ ds.BgpDefinedSets.ExtCommunitySets = []config.ExtCommunitySet{ecomSet1, ecomSet2}
+
+ s1 := createStatement("statement1", "ps1", "ns1", false)
+ s1.Conditions.BgpConditions.MatchAsPathSet.AsPathSet = "asset1"
+ s1.Conditions.BgpConditions.MatchExtCommunitySet.ExtCommunitySet = "ecomSet1"
+
+ s2 := createStatement("statement2", "ps1", "ns1", false)
+ s2.Conditions.BgpConditions.MatchAsPathSet.AsPathSet = "asset1"
+ s2.Conditions.BgpConditions.MatchExtCommunitySet.ExtCommunitySet = "ecomSet2"
+
+ pd1 := createPolicyDefinition("pd1", s1)
+ pd2 := createPolicyDefinition("pd2", s2)
+ pl := createRoutingPolicy(ds, pd1, pd2)
+ //test
+ r := NewRoutingPolicy()
+ err := r.reload(pl)
+ assert.Nil(t, err)
+ p := r.policyMap["pd1"]
+ pType, newPath := p.Apply(path, nil)
+ assert.Equal(t, ROUTE_TYPE_NONE, pType)
+ assert.Equal(t, newPath, path)
+
+ p = r.policyMap["pd2"]
+ pType, newPath = p.Apply(path, nil)
+ assert.Equal(t, ROUTE_TYPE_REJECT, pType)
+ assert.Equal(t, newPath, path)
+
+}
+
+func TestPolicyMatchAndReplaceMed(t *testing.T) {
+
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(100)
+
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+ // create policy
+ ps := createPrefixSet("ps1", "10.10.0.0/16", "21..24")
+ ns := createNeighborSet("ns1", "10.0.0.1")
+
+ ds := config.DefinedSets{}
+ ds.PrefixSets = []config.PrefixSet{ps}
+ ds.NeighborSets = []config.NeighborSet{ns}
+
+ m := "200"
+ s := createStatement("statement1", "ps1", "ns1", true)
+ s.Actions.BgpActions.SetMed = config.BgpSetMedType(m)
+
+ pd := createPolicyDefinition("pd1", s)
+ pl := createRoutingPolicy(ds, pd)
+
+ //test
+ r := NewRoutingPolicy()
+ err := r.reload(pl)
+ assert.Nil(t, err)
+ p := r.policyMap["pd1"]
+
+ pType, newPath := p.Apply(path, nil)
+ assert.Equal(t, ROUTE_TYPE_ACCEPT, pType)
+ assert.NotEqual(t, nil, newPath)
+ v, err := newPath.GetMed()
+ assert.Nil(t, err)
+ newMed := fmt.Sprintf("%d", v)
+ assert.Equal(t, m, newMed)
+}
+
+func TestPolicyMatchAndAddingMed(t *testing.T) {
+
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(100)
+
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+ // create policy
+ ps := createPrefixSet("ps1", "10.10.0.0/16", "21..24")
+ ns := createNeighborSet("ns1", "10.0.0.1")
+
+ ds := config.DefinedSets{}
+ ds.PrefixSets = []config.PrefixSet{ps}
+ ds.NeighborSets = []config.NeighborSet{ns}
+
+ m := "+200"
+ ma := "300"
+ s := createStatement("statement1", "ps1", "ns1", true)
+ s.Actions.BgpActions.SetMed = config.BgpSetMedType(m)
+
+ pd := createPolicyDefinition("pd1", s)
+ pl := createRoutingPolicy(ds, pd)
+ //test
+ r := NewRoutingPolicy()
+ err := r.reload(pl)
+ assert.Nil(t, err)
+ p := r.policyMap["pd1"]
+ pType, newPath := p.Apply(path, nil)
+ assert.Equal(t, ROUTE_TYPE_ACCEPT, pType)
+ assert.NotEqual(t, nil, newPath)
+
+ v, err := newPath.GetMed()
+ assert.Nil(t, err)
+ newMed := fmt.Sprintf("%d", v)
+ assert.Equal(t, ma, newMed)
+}
+
+func TestPolicyMatchAndAddingMedOverFlow(t *testing.T) {
+
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(1)
+
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+ // create policy
+ ps := createPrefixSet("ps1", "10.10.0.0/16", "21..24")
+ ns := createNeighborSet("ns1", "10.0.0.1")
+
+ ds := config.DefinedSets{}
+ ds.PrefixSets = []config.PrefixSet{ps}
+ ds.NeighborSets = []config.NeighborSet{ns}
+
+ m := fmt.Sprintf("+%d", uint32(math.MaxUint32))
+ ma := "1"
+
+ s := createStatement("statement1", "ps1", "ns1", true)
+ s.Actions.BgpActions.SetMed = config.BgpSetMedType(m)
+
+ pd := createPolicyDefinition("pd1", s)
+ pl := createRoutingPolicy(ds, pd)
+ //test
+ r := NewRoutingPolicy()
+ err := r.reload(pl)
+ assert.Nil(t, err)
+ p := r.policyMap["pd1"]
+
+ pType, newPath := p.Apply(path, nil)
+ assert.Equal(t, ROUTE_TYPE_ACCEPT, pType)
+ assert.NotEqual(t, nil, newPath)
+
+ v, err := newPath.GetMed()
+ assert.Nil(t, err)
+ newMed := fmt.Sprintf("%d", v)
+ assert.Equal(t, ma, newMed)
+}
+
+func TestPolicyMatchAndSubtractMed(t *testing.T) {
+
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(100)
+
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+ // create policy
+ ps := createPrefixSet("ps1", "10.10.0.0/16", "21..24")
+ ns := createNeighborSet("ns1", "10.0.0.1")
+
+ ds := config.DefinedSets{}
+ ds.PrefixSets = []config.PrefixSet{ps}
+ ds.NeighborSets = []config.NeighborSet{ns}
+
+ m := "-50"
+ ma := "50"
+
+ s := createStatement("statement1", "ps1", "ns1", true)
+ s.Actions.BgpActions.SetMed = config.BgpSetMedType(m)
+
+ pd := createPolicyDefinition("pd1", s)
+ pl := createRoutingPolicy(ds, pd)
+ //test
+ r := NewRoutingPolicy()
+ err := r.reload(pl)
+ assert.Nil(t, err)
+ p := r.policyMap["pd1"]
+
+ pType, newPath := p.Apply(path, nil)
+ assert.Equal(t, ROUTE_TYPE_ACCEPT, pType)
+ assert.NotEqual(t, nil, newPath)
+
+ v, err := newPath.GetMed()
+ assert.Nil(t, err)
+ newMed := fmt.Sprintf("%d", v)
+ assert.Equal(t, ma, newMed)
+}
+
+func TestPolicyMatchAndSubtractMedUnderFlow(t *testing.T) {
+
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(100)
+
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+ // create policy
+ ps := createPrefixSet("ps1", "10.10.0.0/16", "21..24")
+ ns := createNeighborSet("ns1", "10.0.0.1")
+
+ ds := config.DefinedSets{}
+ ds.PrefixSets = []config.PrefixSet{ps}
+ ds.NeighborSets = []config.NeighborSet{ns}
+
+ m := "-101"
+ ma := "100"
+
+ s := createStatement("statement1", "ps1", "ns1", true)
+ s.Actions.BgpActions.SetMed = config.BgpSetMedType(m)
+
+ pd := createPolicyDefinition("pd1", s)
+ pl := createRoutingPolicy(ds, pd)
+ //test
+ r := NewRoutingPolicy()
+ err := r.reload(pl)
+ assert.Nil(t, err)
+ p := r.policyMap["pd1"]
+
+ pType, newPath := p.Apply(path, nil)
+ assert.Equal(t, ROUTE_TYPE_ACCEPT, pType)
+ assert.NotEqual(t, nil, newPath)
+
+ v, err := newPath.GetMed()
+ assert.Nil(t, err)
+ newMed := fmt.Sprintf("%d", v)
+ assert.Equal(t, ma, newMed)
+}
+
+func TestPolicyMatchWhenPathHaveNotMed(t *testing.T) {
+
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+ // create policy
+ ps := createPrefixSet("ps1", "10.10.0.0/16", "21..24")
+ ns := createNeighborSet("ns1", "10.0.0.1")
+
+ ds := config.DefinedSets{}
+ ds.PrefixSets = []config.PrefixSet{ps}
+ ds.NeighborSets = []config.NeighborSet{ns}
+
+ m := "-50"
+ s := createStatement("statement1", "ps1", "ns1", true)
+ s.Actions.BgpActions.SetMed = config.BgpSetMedType(m)
+
+ pd := createPolicyDefinition("pd1", s)
+ pl := createRoutingPolicy(ds, pd)
+ //test
+ r := NewRoutingPolicy()
+ err := r.reload(pl)
+ assert.Nil(t, err)
+ p := r.policyMap["pd1"]
+
+ pType, newPath := p.Apply(path, nil)
+ assert.Equal(t, ROUTE_TYPE_ACCEPT, pType)
+ assert.NotEqual(t, nil, newPath)
+
+ _, err = newPath.GetMed()
+ assert.NotNil(t, err)
+}
+
+func TestPolicyAsPathPrepend(t *testing.T) {
+
+ assert := assert.New(t)
+
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001, 65000})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+
+ body := updateMsg.Body.(*bgp.BGPUpdate)
+ UpdatePathAttrs4ByteAs(body)
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+
+ // create policy
+ ps := createPrefixSet("ps1", "10.10.0.0/16", "21..24")
+ ns := createNeighborSet("ns1", "10.0.0.1")
+
+ ds := config.DefinedSets{}
+ ds.PrefixSets = []config.PrefixSet{ps}
+ ds.NeighborSets = []config.NeighborSet{ns}
+
+ s := createStatement("statement1", "ps1", "ns1", true)
+ s.Actions.BgpActions.SetAsPathPrepend.As = "65002"
+ s.Actions.BgpActions.SetAsPathPrepend.RepeatN = 10
+
+ pd := createPolicyDefinition("pd1", s)
+ pl := createRoutingPolicy(ds, pd)
+ //test
+ r := NewRoutingPolicy()
+ r.reload(pl)
+ p := r.policyMap["pd1"]
+
+ pType, newPath := p.Apply(path, nil)
+ assert.Equal(ROUTE_TYPE_ACCEPT, pType)
+ assert.NotEqual(nil, newPath)
+ assert.Equal([]uint32{65002, 65002, 65002, 65002, 65002, 65002, 65002, 65002, 65002, 65002, 65001, 65000}, newPath.GetAsSeqList())
+}
+
+func TestPolicyAsPathPrependLastAs(t *testing.T) {
+
+ assert := assert.New(t)
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65002, 65001, 65000})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+
+ body := updateMsg.Body.(*bgp.BGPUpdate)
+ UpdatePathAttrs4ByteAs(body)
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+
+ // create policy
+ ps := createPrefixSet("ps1", "10.10.0.0/16", "21..24")
+ ns := createNeighborSet("ns1", "10.0.0.1")
+
+ ds := config.DefinedSets{}
+ ds.PrefixSets = []config.PrefixSet{ps}
+ ds.NeighborSets = []config.NeighborSet{ns}
+
+ s := createStatement("statement1", "ps1", "ns1", true)
+ s.Actions.BgpActions.SetAsPathPrepend.As = "last-as"
+ s.Actions.BgpActions.SetAsPathPrepend.RepeatN = 5
+
+ pd := createPolicyDefinition("pd1", s)
+ pl := createRoutingPolicy(ds, pd)
+ //test
+ r := NewRoutingPolicy()
+ r.reload(pl)
+ p := r.policyMap["pd1"]
+
+ pType, newPath := p.Apply(path, nil)
+ assert.Equal(ROUTE_TYPE_ACCEPT, pType)
+ assert.NotEqual(nil, newPath)
+ assert.Equal([]uint32{65002, 65002, 65002, 65002, 65002, 65002, 65001, 65000}, newPath.GetAsSeqList())
+}
+
+func TestPolicyAs4PathPrepend(t *testing.T) {
+
+ assert := assert.New(t)
+
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{
+ bgp.NewAs4PathParam(2, []uint32{
+ createAs4Value("65001.1"),
+ createAs4Value("65000.1"),
+ }),
+ }
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+
+ body := updateMsg.Body.(*bgp.BGPUpdate)
+ UpdatePathAttrs4ByteAs(body)
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+
+ // create policy
+ ps := createPrefixSet("ps1", "10.10.0.0/16", "21..24")
+ ns := createNeighborSet("ns1", "10.0.0.1")
+
+ ds := config.DefinedSets{}
+ ds.PrefixSets = []config.PrefixSet{ps}
+ ds.NeighborSets = []config.NeighborSet{ns}
+
+ s := createStatement("statement1", "ps1", "ns1", true)
+ s.Actions.BgpActions.SetAsPathPrepend.As = fmt.Sprintf("%d", createAs4Value("65002.1"))
+ s.Actions.BgpActions.SetAsPathPrepend.RepeatN = 10
+
+ pd := createPolicyDefinition("pd1", s)
+ pl := createRoutingPolicy(ds, pd)
+ //test
+ r := NewRoutingPolicy()
+ r.reload(pl)
+ p, err := NewPolicy(pl.PolicyDefinitions[0])
+ assert.Nil(err)
+ addPolicy(r, p)
+
+ pType, newPath := p.Apply(path, nil)
+ assert.Equal(ROUTE_TYPE_ACCEPT, pType)
+ assert.NotEqual(nil, newPath)
+ asn := createAs4Value("65002.1")
+ assert.Equal([]uint32{
+ asn, asn, asn, asn, asn, asn, asn, asn, asn, asn,
+ createAs4Value("65001.1"),
+ createAs4Value("65000.1"),
+ }, newPath.GetAsSeqList())
+}
+
+func TestPolicyAs4PathPrependLastAs(t *testing.T) {
+
+ assert := assert.New(t)
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{
+ bgp.NewAs4PathParam(2, []uint32{
+ createAs4Value("65002.1"),
+ createAs4Value("65001.1"),
+ createAs4Value("65000.1"),
+ }),
+ }
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+
+ body := updateMsg.Body.(*bgp.BGPUpdate)
+ UpdatePathAttrs4ByteAs(body)
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+
+ // create policy
+ ps := createPrefixSet("ps1", "10.10.0.0/16", "21..24")
+ ns := createNeighborSet("ns1", "10.0.0.1")
+
+ ds := config.DefinedSets{}
+ ds.PrefixSets = []config.PrefixSet{ps}
+ ds.NeighborSets = []config.NeighborSet{ns}
+
+ s := createStatement("statement1", "ps1", "ns1", true)
+ s.Actions.BgpActions.SetAsPathPrepend.As = "last-as"
+ s.Actions.BgpActions.SetAsPathPrepend.RepeatN = 5
+
+ pd := createPolicyDefinition("pd1", s)
+ pl := createRoutingPolicy(ds, pd)
+ //test
+ r := NewRoutingPolicy()
+ r.reload(pl)
+ p, _ := NewPolicy(pl.PolicyDefinitions[0])
+ addPolicy(r, p)
+
+ pType, newPath := p.Apply(path, nil)
+ assert.Equal(ROUTE_TYPE_ACCEPT, pType)
+ assert.NotEqual(nil, newPath)
+ asn := createAs4Value("65002.1")
+ assert.Equal([]uint32{
+ asn, asn, asn, asn, asn,
+ createAs4Value("65002.1"),
+ createAs4Value("65001.1"),
+ createAs4Value("65000.1"),
+ }, newPath.GetAsSeqList())
+}
+
+func TestParseCommunityRegexp(t *testing.T) {
+ exp, err := ParseCommunityRegexp("65000:1")
+ assert.Equal(t, nil, err)
+ assert.Equal(t, true, exp.MatchString("65000:1"))
+ assert.Equal(t, false, exp.MatchString("65000:100"))
+}
+
+func TestLocalPrefAction(t *testing.T) {
+ action, err := NewLocalPrefAction(10)
+ assert.Nil(t, err)
+
+ nlri := bgp.NewIPAddrPrefix(24, "10.0.0.0")
+
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{
+ bgp.NewAs4PathParam(2, []uint32{
+ createAs4Value("65002.1"),
+ createAs4Value("65001.1"),
+ createAs4Value("65000.1"),
+ }),
+ }
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+
+ attrs := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+
+ path := NewPath(nil, nlri, false, attrs, time.Now(), false)
+ p := action.Apply(path, nil)
+ assert.NotNil(t, p)
+
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_LOCAL_PREF)
+ assert.NotNil(t, attr)
+ lp := attr.(*bgp.PathAttributeLocalPref)
+ assert.Equal(t, int(lp.Value), int(10))
+}
+
+func createStatement(name, psname, nsname string, accept bool) config.Statement {
+ c := config.Conditions{
+ MatchPrefixSet: config.MatchPrefixSet{
+ PrefixSet: psname,
+ },
+ MatchNeighborSet: config.MatchNeighborSet{
+ NeighborSet: nsname,
+ },
+ }
+ rd := config.ROUTE_DISPOSITION_REJECT_ROUTE
+ if accept {
+ rd = config.ROUTE_DISPOSITION_ACCEPT_ROUTE
+ }
+ a := config.Actions{
+ RouteDisposition: rd,
+ }
+ s := config.Statement{
+ Name: name,
+ Conditions: c,
+ Actions: a,
+ }
+ return s
+}
+
+func createSetCommunity(operation string, community ...string) config.SetCommunity {
+
+ s := config.SetCommunity{
+ SetCommunityMethod: config.SetCommunityMethod{
+ CommunitiesList: community,
+ },
+ Options: operation,
+ }
+ return s
+}
+
+func stringToCommunityValue(comStr string) uint32 {
+ elem := strings.Split(comStr, ":")
+ asn, _ := strconv.ParseUint(elem[0], 10, 16)
+ val, _ := strconv.ParseUint(elem[1], 10, 16)
+ return uint32(asn<<16 | val)
+}
+
+func createPolicyDefinition(defName string, stmt ...config.Statement) config.PolicyDefinition {
+ pd := config.PolicyDefinition{
+ Name: defName,
+ Statements: []config.Statement(stmt),
+ }
+ return pd
+}
+
+func createRoutingPolicy(ds config.DefinedSets, pd ...config.PolicyDefinition) config.RoutingPolicy {
+ pl := config.RoutingPolicy{
+ DefinedSets: ds,
+ PolicyDefinitions: []config.PolicyDefinition(pd),
+ }
+ return pl
+}
+
+func createPrefixSet(name string, prefix string, maskLength string) config.PrefixSet {
+ ps := config.PrefixSet{
+ PrefixSetName: name,
+ PrefixList: []config.Prefix{
+ config.Prefix{
+ IpPrefix: prefix,
+ MasklengthRange: maskLength,
+ }},
+ }
+ return ps
+}
+
+func createNeighborSet(name string, addr string) config.NeighborSet {
+ ns := config.NeighborSet{
+ NeighborSetName: name,
+ NeighborInfoList: []string{addr},
+ }
+ return ns
+}
+
+func createAs4Value(s string) uint32 {
+ v := strings.Split(s, ".")
+ upper, _ := strconv.ParseUint(v[0], 10, 16)
+ lower, _ := strconv.ParseUint(v[1], 10, 16)
+ return uint32(upper<<16 | lower)
+}
+
+func TestPrefixSetOperation(t *testing.T) {
+ // tryp to create prefixset with multiple families
+ p1 := config.Prefix{
+ IpPrefix: "0.0.0.0/0",
+ MasklengthRange: "0..7",
+ }
+ p2 := config.Prefix{
+ IpPrefix: "0::/25",
+ MasklengthRange: "25..128",
+ }
+ _, err := NewPrefixSet(config.PrefixSet{
+ PrefixSetName: "ps1",
+ PrefixList: []config.Prefix{p1, p2},
+ })
+ assert.NotNil(t, err)
+ m1, _ := NewPrefixSet(config.PrefixSet{
+ PrefixSetName: "ps1",
+ PrefixList: []config.Prefix{p1},
+ })
+ m2, err := NewPrefixSet(config.PrefixSet{PrefixSetName: "ps2"})
+ assert.Nil(t, err)
+ err = m1.Append(m2)
+ assert.Nil(t, err)
+ err = m2.Append(m1)
+ assert.Nil(t, err)
+ assert.Equal(t, bgp.RF_IPv4_UC, m2.family)
+ p3, _ := NewPrefix(config.Prefix{IpPrefix: "10.10.0.0/24", MasklengthRange: ""})
+ p4, _ := NewPrefix(config.Prefix{IpPrefix: "0::/25", MasklengthRange: ""})
+ _, err = NewPrefixSetFromApiStruct("ps3", []*Prefix{p3, p4})
+ assert.NotNil(t, err)
+}
+
+func TestPrefixSetMatch(t *testing.T) {
+ p1 := config.Prefix{
+ IpPrefix: "0.0.0.0/0",
+ MasklengthRange: "0..7",
+ }
+ p2 := config.Prefix{
+ IpPrefix: "0.0.0.0/0",
+ MasklengthRange: "25..32",
+ }
+ ps, err := NewPrefixSet(config.PrefixSet{
+ PrefixSetName: "ps1",
+ PrefixList: []config.Prefix{p1, p2},
+ })
+ assert.Nil(t, err)
+ m := &PrefixCondition{
+ set: ps,
+ }
+
+ path := NewPath(nil, bgp.NewIPAddrPrefix(6, "0.0.0.0"), false, []bgp.PathAttributeInterface{}, time.Now(), false)
+ assert.True(t, m.Evaluate(path, nil))
+
+ path = NewPath(nil, bgp.NewIPAddrPrefix(10, "0.0.0.0"), false, []bgp.PathAttributeInterface{}, time.Now(), false)
+ assert.False(t, m.Evaluate(path, nil))
+
+ path = NewPath(nil, bgp.NewIPAddrPrefix(25, "0.0.0.0"), false, []bgp.PathAttributeInterface{}, time.Now(), false)
+ assert.True(t, m.Evaluate(path, nil))
+
+ path = NewPath(nil, bgp.NewIPAddrPrefix(30, "0.0.0.0"), false, []bgp.PathAttributeInterface{}, time.Now(), false)
+ assert.True(t, m.Evaluate(path, nil))
+
+ p3 := config.Prefix{
+ IpPrefix: "0.0.0.0/0",
+ MasklengthRange: "9..10",
+ }
+ ps2, err := NewPrefixSet(config.PrefixSet{
+ PrefixSetName: "ps2",
+ PrefixList: []config.Prefix{p3},
+ })
+ assert.Nil(t, err)
+ err = ps.Append(ps2)
+ assert.Nil(t, err)
+
+ path = NewPath(nil, bgp.NewIPAddrPrefix(10, "0.0.0.0"), false, []bgp.PathAttributeInterface{}, time.Now(), false)
+ assert.True(t, m.Evaluate(path, nil))
+
+ ps3, err := NewPrefixSet(config.PrefixSet{
+ PrefixSetName: "ps3",
+ PrefixList: []config.Prefix{p1},
+ })
+ assert.Nil(t, err)
+ err = ps.Remove(ps3)
+ assert.Nil(t, err)
+
+ path = NewPath(nil, bgp.NewIPAddrPrefix(6, "0.0.0.0"), false, []bgp.PathAttributeInterface{}, time.Now(), false)
+ assert.False(t, m.Evaluate(path, nil))
+}
+
+func TestPrefixSetMatchV4withV6Prefix(t *testing.T) {
+ p1 := config.Prefix{
+ IpPrefix: "c000::/3",
+ MasklengthRange: "3..128",
+ }
+ ps, err := NewPrefixSet(config.PrefixSet{
+ PrefixSetName: "ps1",
+ PrefixList: []config.Prefix{p1},
+ })
+ assert.Nil(t, err)
+ m := &PrefixCondition{
+ set: ps,
+ }
+
+ path := NewPath(nil, bgp.NewIPAddrPrefix(6, "192.0.0.0"), false, []bgp.PathAttributeInterface{}, time.Now(), false)
+ assert.False(t, m.Evaluate(path, nil))
+}
+
+func TestLargeCommunityMatchAction(t *testing.T) {
+ coms := []*bgp.LargeCommunity{
+ &bgp.LargeCommunity{ASN: 100, LocalData1: 100, LocalData2: 100},
+ &bgp.LargeCommunity{ASN: 100, LocalData1: 200, LocalData2: 200},
+ }
+ p := NewPath(nil, nil, false, []bgp.PathAttributeInterface{bgp.NewPathAttributeLargeCommunities(coms)}, time.Time{}, false)
+
+ c := config.LargeCommunitySet{
+ LargeCommunitySetName: "l0",
+ LargeCommunityList: []string{
+ "100:100:100",
+ "100:300:100",
+ },
+ }
+
+ set, err := NewLargeCommunitySet(c)
+ assert.Equal(t, err, nil)
+
+ m, err := NewLargeCommunityCondition(config.MatchLargeCommunitySet{
+ LargeCommunitySet: "l0",
+ })
+ assert.Equal(t, err, nil)
+ m.set = set
+
+ assert.Equal(t, m.Evaluate(p, nil), true)
+
+ a, err := NewLargeCommunityAction(config.SetLargeCommunity{
+ SetLargeCommunityMethod: config.SetLargeCommunityMethod{
+ CommunitiesList: []string{"100:100:100"},
+ },
+ Options: config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE,
+ })
+ assert.Equal(t, err, nil)
+ p = a.Apply(p, nil)
+
+ assert.Equal(t, m.Evaluate(p, nil), false)
+
+ a, err = NewLargeCommunityAction(config.SetLargeCommunity{
+ SetLargeCommunityMethod: config.SetLargeCommunityMethod{
+ CommunitiesList: []string{
+ "100:300:100",
+ "200:100:100",
+ },
+ },
+ Options: config.BGP_SET_COMMUNITY_OPTION_TYPE_ADD,
+ })
+ assert.Equal(t, err, nil)
+ p = a.Apply(p, nil)
+
+ assert.Equal(t, m.Evaluate(p, nil), true)
+
+ a, err = NewLargeCommunityAction(config.SetLargeCommunity{
+ SetLargeCommunityMethod: config.SetLargeCommunityMethod{
+ CommunitiesList: []string{"^100:"},
+ },
+ Options: config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE,
+ })
+ assert.Equal(t, err, nil)
+ p = a.Apply(p, nil)
+
+ assert.Equal(t, m.Evaluate(p, nil), false)
+
+ c = config.LargeCommunitySet{
+ LargeCommunitySetName: "l1",
+ LargeCommunityList: []string{
+ "200:",
+ },
+ }
+
+ set, err = NewLargeCommunitySet(c)
+ assert.Equal(t, err, nil)
+
+ m, err = NewLargeCommunityCondition(config.MatchLargeCommunitySet{
+ LargeCommunitySet: "l1",
+ })
+ assert.Equal(t, err, nil)
+ m.set = set
+
+ assert.Equal(t, m.Evaluate(p, nil), true)
+}
+
+func TestAfiSafiInMatchPath(t *testing.T) {
+ condition, err := NewAfiSafiInCondition([]config.AfiSafiType{config.AFI_SAFI_TYPE_L3VPN_IPV4_UNICAST, config.AFI_SAFI_TYPE_L3VPN_IPV6_UNICAST})
+ require.NoError(t, err)
+
+ rtExtCom, err := bgp.ParseExtendedCommunity(bgp.EC_SUBTYPE_ROUTE_TARGET, "100:100")
+ assert.NoError(t, err)
+
+ prefixVPNv4 := bgp.NewLabeledVPNIPAddrPrefix(0, "1.1.1.0/24", *bgp.NewMPLSLabelStack(), bgp.NewRouteDistinguisherTwoOctetAS(100, 100))
+ prefixVPNv6 := bgp.NewLabeledVPNIPv6AddrPrefix(0, "2001:0db8:85a3:0000:0000:8a2e:0370:7334", *bgp.NewMPLSLabelStack(), bgp.NewRouteDistinguisherTwoOctetAS(200, 200))
+ prefixRTC := bgp.NewRouteTargetMembershipNLRI(100, nil)
+ prefixv4 := bgp.NewIPAddrPrefix(0, "1.1.1.0/24")
+ prefixv6 := bgp.NewIPv6AddrPrefix(0, "2001:0db8:85a3:0000:0000:8a2e:0370:7334")
+
+ pathVPNv4 := NewPath(nil, prefixVPNv4, false, []bgp.PathAttributeInterface{bgp.NewPathAttributeExtendedCommunities([]bgp.ExtendedCommunityInterface{rtExtCom})}, time.Time{}, false)
+ pathVPNv6 := NewPath(nil, prefixVPNv6, false, []bgp.PathAttributeInterface{bgp.NewPathAttributeExtendedCommunities([]bgp.ExtendedCommunityInterface{rtExtCom})}, time.Time{}, false)
+ pathv4 := NewPath(nil, prefixv4, false, []bgp.PathAttributeInterface{}, time.Time{}, false)
+ pathv6 := NewPath(nil, prefixv6, false, []bgp.PathAttributeInterface{}, time.Time{}, false)
+ pathRTC := NewPath(nil, prefixRTC, false, []bgp.PathAttributeInterface{}, time.Time{}, false)
+
+ type Entry struct {
+ path *Path
+ shouldMatch bool
+ }
+
+ for _, entry := range []Entry{
+ {pathVPNv4, true},
+ {pathVPNv6, true},
+ {pathv4, false},
+ {pathv6, false},
+ {pathRTC, false},
+ } {
+ assert.Equal(t, condition.Evaluate(entry.path, nil), entry.shouldMatch)
+ }
+}
+
+func TestMultipleStatementPolicy(t *testing.T) {
+ r := NewRoutingPolicy()
+ rp := config.RoutingPolicy{
+ PolicyDefinitions: []config.PolicyDefinition{config.PolicyDefinition{
+ Name: "p1",
+ Statements: []config.Statement{
+ config.Statement{
+ Actions: config.Actions{
+ BgpActions: config.BgpActions{
+ SetMed: "+100",
+ },
+ },
+ },
+ config.Statement{
+ Actions: config.Actions{
+ BgpActions: config.BgpActions{
+ SetLocalPref: 100,
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ err := r.reload(rp)
+ assert.Nil(t, err)
+
+ nlri := bgp.NewIPAddrPrefix(24, "10.10.0.0")
+
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ pattrs := []bgp.PathAttributeInterface{origin, aspath, nexthop}
+
+ path := NewPath(nil, nlri, false, pattrs, time.Now(), false)
+
+ pType, newPath := r.policyMap["p1"].Apply(path, nil)
+ assert.Equal(t, ROUTE_TYPE_NONE, pType)
+ med, _ := newPath.GetMed()
+ assert.Equal(t, med, uint32(100))
+ localPref, _ := newPath.GetLocalPref()
+ assert.Equal(t, localPref, uint32(100))
+}
diff --git a/internal/pkg/table/roa.go b/internal/pkg/table/roa.go
new file mode 100644
index 00000000..fe08fe54
--- /dev/null
+++ b/internal/pkg/table/roa.go
@@ -0,0 +1,60 @@
+// Copyright (C) 2016 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package table
+
+import (
+ "fmt"
+ "net"
+)
+
+type IPPrefix struct {
+ Prefix net.IP
+ Length uint8
+}
+
+func (p *IPPrefix) String() string {
+ return fmt.Sprintf("%s/%d", p.Prefix, p.Length)
+}
+
+type ROA struct {
+ Family int
+ Prefix *IPPrefix
+ MaxLen uint8
+ AS uint32
+ Src string
+}
+
+func NewROA(family int, prefixByte []byte, prefixLen uint8, maxLen uint8, as uint32, src string) *ROA {
+ p := make([]byte, len(prefixByte))
+ copy(p, prefixByte)
+ return &ROA{
+ Family: family,
+ Prefix: &IPPrefix{
+ Prefix: p,
+ Length: prefixLen,
+ },
+ MaxLen: maxLen,
+ AS: as,
+ Src: src,
+ }
+}
+
+func (r *ROA) Equal(roa *ROA) bool {
+ if r.MaxLen == roa.MaxLen && r.Src == roa.Src && r.AS == roa.AS {
+ return true
+ }
+ return false
+}
diff --git a/internal/pkg/table/table.go b/internal/pkg/table/table.go
new file mode 100644
index 00000000..bcde936b
--- /dev/null
+++ b/internal/pkg/table/table.go
@@ -0,0 +1,451 @@
+// Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package table
+
+import (
+ "fmt"
+ "net"
+ "strings"
+ "unsafe"
+
+ "github.com/armon/go-radix"
+ "github.com/osrg/gobgp/pkg/packet/bgp"
+ log "github.com/sirupsen/logrus"
+)
+
+type LookupOption uint8
+
+const (
+ LOOKUP_EXACT LookupOption = iota
+ LOOKUP_LONGER
+ LOOKUP_SHORTER
+)
+
+type LookupPrefix struct {
+ Prefix string
+ LookupOption
+}
+
+type TableSelectOption struct {
+ ID string
+ AS uint32
+ LookupPrefixes []*LookupPrefix
+ VRF *Vrf
+ adj bool
+ Best bool
+ MultiPath bool
+}
+
+type Table struct {
+ routeFamily bgp.RouteFamily
+ destinations map[string]*Destination
+}
+
+func NewTable(rf bgp.RouteFamily, dsts ...*Destination) *Table {
+ t := &Table{
+ routeFamily: rf,
+ destinations: make(map[string]*Destination),
+ }
+ for _, dst := range dsts {
+ t.setDestination(dst)
+ }
+ return t
+}
+
+func (t *Table) GetRoutefamily() bgp.RouteFamily {
+ return t.routeFamily
+}
+
+func (t *Table) deletePathsByVrf(vrf *Vrf) []*Path {
+ pathList := make([]*Path, 0)
+ for _, dest := range t.destinations {
+ for _, p := range dest.knownPathList {
+ var rd bgp.RouteDistinguisherInterface
+ nlri := p.GetNlri()
+ switch nlri.(type) {
+ case *bgp.LabeledVPNIPAddrPrefix:
+ rd = nlri.(*bgp.LabeledVPNIPAddrPrefix).RD
+ case *bgp.LabeledVPNIPv6AddrPrefix:
+ rd = nlri.(*bgp.LabeledVPNIPv6AddrPrefix).RD
+ case *bgp.EVPNNLRI:
+ rd = nlri.(*bgp.EVPNNLRI).RD()
+ default:
+ return pathList
+ }
+ if p.IsLocal() && vrf.Rd.String() == rd.String() {
+ pathList = append(pathList, p.Clone(true))
+ break
+ }
+ }
+ }
+ return pathList
+}
+
+func (t *Table) deleteRTCPathsByVrf(vrf *Vrf, vrfs map[string]*Vrf) []*Path {
+ pathList := make([]*Path, 0)
+ if t.routeFamily != bgp.RF_RTC_UC {
+ return pathList
+ }
+ for _, target := range vrf.ImportRt {
+ lhs := target.String()
+ for _, dest := range t.destinations {
+ nlri := dest.GetNlri().(*bgp.RouteTargetMembershipNLRI)
+ rhs := nlri.RouteTarget.String()
+ if lhs == rhs && isLastTargetUser(vrfs, target) {
+ for _, p := range dest.knownPathList {
+ if p.IsLocal() {
+ pathList = append(pathList, p.Clone(true))
+ break
+ }
+ }
+ }
+ }
+ }
+ return pathList
+}
+
+func (t *Table) deleteDestByNlri(nlri bgp.AddrPrefixInterface) *Destination {
+ if dst := t.GetDestination(nlri); dst != nil {
+ t.deleteDest(dst)
+ return dst
+ }
+ return nil
+}
+
+func (t *Table) deleteDest(dest *Destination) {
+ destinations := t.GetDestinations()
+ delete(destinations, t.tableKey(dest.GetNlri()))
+ if len(destinations) == 0 {
+ t.destinations = make(map[string]*Destination)
+ }
+}
+
+func (t *Table) validatePath(path *Path) {
+ if path == nil {
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ "Key": t.routeFamily,
+ }).Error("path is nil")
+ }
+ if path.GetRouteFamily() != t.routeFamily {
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ "Key": t.routeFamily,
+ "Prefix": path.GetNlri().String(),
+ "ReceivedRf": path.GetRouteFamily().String(),
+ }).Error("Invalid path. RouteFamily mismatch")
+ }
+ if attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH); attr != nil {
+ pathParam := attr.(*bgp.PathAttributeAsPath).Value
+ for _, as := range pathParam {
+ _, y := as.(*bgp.As4PathParam)
+ if !y {
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ "Key": t.routeFamily,
+ "As": as,
+ }).Fatal("AsPathParam must be converted to As4PathParam")
+ }
+ }
+ }
+ if attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_AS4_PATH); attr != nil {
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ "Key": t.routeFamily,
+ }).Fatal("AS4_PATH must be converted to AS_PATH")
+ }
+ if path.GetNlri() == nil {
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ "Key": t.routeFamily,
+ }).Fatal("path's nlri is nil")
+ }
+}
+
+func (t *Table) getOrCreateDest(nlri bgp.AddrPrefixInterface) *Destination {
+ dest := t.GetDestination(nlri)
+ // If destination for given prefix does not exist we create it.
+ if dest == nil {
+ log.WithFields(log.Fields{
+ "Topic": "Table",
+ "Nlri": nlri,
+ }).Debugf("create Destination")
+ dest = NewDestination(nlri, 64)
+ t.setDestination(dest)
+ }
+ return dest
+}
+
+func (t *Table) GetDestinations() map[string]*Destination {
+ return t.destinations
+}
+func (t *Table) setDestinations(destinations map[string]*Destination) {
+ t.destinations = destinations
+}
+func (t *Table) GetDestination(nlri bgp.AddrPrefixInterface) *Destination {
+ dest, ok := t.destinations[t.tableKey(nlri)]
+ if ok {
+ return dest
+ } else {
+ return nil
+ }
+}
+
+func (t *Table) GetLongerPrefixDestinations(key string) ([]*Destination, error) {
+ results := make([]*Destination, 0, len(t.GetDestinations()))
+ switch t.routeFamily {
+ case bgp.RF_IPv4_UC, bgp.RF_IPv6_UC, bgp.RF_IPv4_MPLS, bgp.RF_IPv6_MPLS:
+ _, prefix, err := net.ParseCIDR(key)
+ if err != nil {
+ return nil, err
+ }
+ k := CidrToRadixkey(prefix.String())
+ r := radix.New()
+ for _, dst := range t.GetDestinations() {
+ r.Insert(AddrToRadixkey(dst.nlri), dst)
+ }
+ r.WalkPrefix(k, func(s string, v interface{}) bool {
+ results = append(results, v.(*Destination))
+ return false
+ })
+ default:
+ for _, dst := range t.GetDestinations() {
+ results = append(results, dst)
+ }
+ }
+ return results, nil
+}
+
+func (t *Table) GetEvpnDestinationsWithRouteType(typ string) ([]*Destination, error) {
+ var routeType uint8
+ switch strings.ToLower(typ) {
+ case "a-d":
+ routeType = bgp.EVPN_ROUTE_TYPE_ETHERNET_AUTO_DISCOVERY
+ case "macadv":
+ routeType = bgp.EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT
+ case "multicast":
+ routeType = bgp.EVPN_INCLUSIVE_MULTICAST_ETHERNET_TAG
+ case "esi":
+ routeType = bgp.EVPN_ETHERNET_SEGMENT_ROUTE
+ case "prefix":
+ routeType = bgp.EVPN_IP_PREFIX
+ default:
+ return nil, fmt.Errorf("unsupported evpn route type: %s", typ)
+ }
+ destinations := t.GetDestinations()
+ results := make([]*Destination, 0, len(destinations))
+ switch t.routeFamily {
+ case bgp.RF_EVPN:
+ for _, dst := range destinations {
+ if nlri, ok := dst.nlri.(*bgp.EVPNNLRI); !ok {
+ return nil, fmt.Errorf("invalid evpn nlri type detected: %T", dst.nlri)
+ } else if nlri.RouteType == routeType {
+ results = append(results, dst)
+ }
+ }
+ default:
+ for _, dst := range destinations {
+ results = append(results, dst)
+ }
+ }
+ return results, nil
+}
+
+func (t *Table) setDestination(dst *Destination) {
+ t.destinations[t.tableKey(dst.nlri)] = dst
+}
+
+func (t *Table) tableKey(nlri bgp.AddrPrefixInterface) string {
+ switch T := nlri.(type) {
+ case *bgp.IPAddrPrefix:
+ b := make([]byte, 5)
+ copy(b, T.Prefix.To4())
+ b[4] = T.Length
+ return *(*string)(unsafe.Pointer(&b))
+ case *bgp.IPv6AddrPrefix:
+ b := make([]byte, 17)
+ copy(b, T.Prefix.To16())
+ b[16] = T.Length
+ return *(*string)(unsafe.Pointer(&b))
+ }
+ return nlri.String()
+}
+
+func (t *Table) Bests(id string, as uint32) []*Path {
+ paths := make([]*Path, 0, len(t.destinations))
+ for _, dst := range t.destinations {
+ path := dst.GetBestPath(id, as)
+ if path != nil {
+ paths = append(paths, path)
+ }
+ }
+ return paths
+}
+
+func (t *Table) MultiBests(id string) [][]*Path {
+ paths := make([][]*Path, 0, len(t.destinations))
+ for _, dst := range t.destinations {
+ path := dst.GetMultiBestPath(id)
+ if path != nil {
+ paths = append(paths, path)
+ }
+ }
+ return paths
+}
+
+func (t *Table) GetKnownPathList(id string, as uint32) []*Path {
+ paths := make([]*Path, 0, len(t.destinations))
+ for _, dst := range t.destinations {
+ paths = append(paths, dst.GetKnownPathList(id, as)...)
+ }
+ return paths
+}
+
+func (t *Table) Select(option ...TableSelectOption) (*Table, error) {
+ id := GLOBAL_RIB_NAME
+ var vrf *Vrf
+ adj := false
+ prefixes := make([]*LookupPrefix, 0, len(option))
+ best := false
+ mp := false
+ as := uint32(0)
+ for _, o := range option {
+ if o.ID != "" {
+ id = o.ID
+ }
+ if o.VRF != nil {
+ vrf = o.VRF
+ }
+ adj = o.adj
+ prefixes = append(prefixes, o.LookupPrefixes...)
+ best = o.Best
+ mp = o.MultiPath
+ as = o.AS
+ }
+ dOption := DestinationSelectOption{ID: id, AS: as, VRF: vrf, adj: adj, Best: best, MultiPath: mp}
+ r := &Table{
+ routeFamily: t.routeFamily,
+ destinations: make(map[string]*Destination),
+ }
+
+ if len(prefixes) != 0 {
+ switch t.routeFamily {
+ case bgp.RF_IPv4_UC, bgp.RF_IPv6_UC:
+ f := func(prefixStr string) bool {
+ var nlri bgp.AddrPrefixInterface
+ if t.routeFamily == bgp.RF_IPv4_UC {
+ nlri, _ = bgp.NewPrefixFromRouteFamily(bgp.AFI_IP, bgp.SAFI_UNICAST, prefixStr)
+ } else {
+ nlri, _ = bgp.NewPrefixFromRouteFamily(bgp.AFI_IP6, bgp.SAFI_UNICAST, prefixStr)
+ }
+ if dst := t.GetDestination(nlri); dst != nil {
+ if d := dst.Select(dOption); d != nil {
+ r.setDestination(d)
+ return true
+ }
+ }
+ return false
+ }
+
+ for _, p := range prefixes {
+ key := p.Prefix
+ switch p.LookupOption {
+ case LOOKUP_LONGER:
+ ds, err := t.GetLongerPrefixDestinations(key)
+ if err != nil {
+ return nil, err
+ }
+ for _, dst := range ds {
+ if d := dst.Select(dOption); d != nil {
+ r.setDestination(d)
+ }
+ }
+ case LOOKUP_SHORTER:
+ addr, prefix, err := net.ParseCIDR(key)
+ if err != nil {
+ return nil, err
+ }
+ ones, _ := prefix.Mask.Size()
+ for i := ones; i >= 0; i-- {
+ _, prefix, _ := net.ParseCIDR(fmt.Sprintf("%s/%d", addr.String(), i))
+ f(prefix.String())
+ }
+ default:
+ if host := net.ParseIP(key); host != nil {
+ masklen := 32
+ if t.routeFamily == bgp.RF_IPv6_UC {
+ masklen = 128
+ }
+ for i := masklen; i >= 0; i-- {
+ _, prefix, err := net.ParseCIDR(fmt.Sprintf("%s/%d", key, i))
+ if err != nil {
+ return nil, err
+ }
+ if f(prefix.String()) {
+ break
+ }
+ }
+ } else {
+ f(key)
+ }
+ }
+ }
+ case bgp.RF_EVPN:
+ for _, p := range prefixes {
+ // Uses LookupPrefix.Prefix as EVPN Route Type string
+ ds, err := t.GetEvpnDestinationsWithRouteType(p.Prefix)
+ if err != nil {
+ return nil, err
+ }
+ for _, dst := range ds {
+ if d := dst.Select(dOption); d != nil {
+ r.setDestination(d)
+ }
+ }
+ }
+ default:
+ return nil, fmt.Errorf("route filtering is not supported for this family")
+ }
+ } else {
+ for _, dst := range t.GetDestinations() {
+ if d := dst.Select(dOption); d != nil {
+ r.setDestination(d)
+ }
+ }
+ }
+ return r, nil
+}
+
+type TableInfo struct {
+ NumDestination int
+ NumPath int
+ NumAccepted int
+}
+
+func (t *Table) Info(id string, as uint32) *TableInfo {
+ var numD, numP int
+ for _, d := range t.destinations {
+ ps := d.GetKnownPathList(id, as)
+ if len(ps) > 0 {
+ numD += 1
+ numP += len(ps)
+ }
+ }
+ return &TableInfo{
+ NumDestination: numD,
+ NumPath: numP,
+ }
+}
diff --git a/internal/pkg/table/table_manager.go b/internal/pkg/table/table_manager.go
new file mode 100644
index 00000000..e10f4d6a
--- /dev/null
+++ b/internal/pkg/table/table_manager.go
@@ -0,0 +1,356 @@
+// Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package table
+
+import (
+ "bytes"
+ "fmt"
+ "net"
+ "time"
+
+ "github.com/osrg/gobgp/pkg/packet/bgp"
+
+ farm "github.com/dgryski/go-farm"
+ log "github.com/sirupsen/logrus"
+)
+
+const (
+ GLOBAL_RIB_NAME = "global"
+)
+
+func ProcessMessage(m *bgp.BGPMessage, peerInfo *PeerInfo, timestamp time.Time) []*Path {
+ update := m.Body.(*bgp.BGPUpdate)
+
+ if y, f := update.IsEndOfRib(); y {
+ // this message has no normal updates or withdrawals.
+ return []*Path{NewEOR(f)}
+ }
+
+ adds := make([]bgp.AddrPrefixInterface, 0, len(update.NLRI))
+ for _, nlri := range update.NLRI {
+ adds = append(adds, nlri)
+ }
+
+ dels := make([]bgp.AddrPrefixInterface, 0, len(update.WithdrawnRoutes))
+ for _, nlri := range update.WithdrawnRoutes {
+ dels = append(dels, nlri)
+ }
+
+ attrs := make([]bgp.PathAttributeInterface, 0, len(update.PathAttributes))
+ var reach *bgp.PathAttributeMpReachNLRI
+ for _, attr := range update.PathAttributes {
+ switch a := attr.(type) {
+ case *bgp.PathAttributeMpReachNLRI:
+ reach = a
+ case *bgp.PathAttributeMpUnreachNLRI:
+ l := make([]bgp.AddrPrefixInterface, 0, len(a.Value))
+ l = append(l, a.Value...)
+ dels = append(dels, l...)
+ default:
+ attrs = append(attrs, attr)
+ }
+ }
+
+ listLen := len(adds) + len(dels)
+ if reach != nil {
+ listLen += len(reach.Value)
+ }
+
+ var hash uint32
+ if len(adds) > 0 || reach != nil {
+ total := bytes.NewBuffer(make([]byte, 0))
+ for _, a := range attrs {
+ b, _ := a.Serialize()
+ total.Write(b)
+ }
+ hash = farm.Hash32(total.Bytes())
+ }
+
+ pathList := make([]*Path, 0, listLen)
+ for _, nlri := range adds {
+ p := NewPath(peerInfo, nlri, false, attrs, timestamp, false)
+ p.SetHash(hash)
+ pathList = append(pathList, p)
+ }
+ if reach != nil {
+ reachAttrs := make([]bgp.PathAttributeInterface, len(attrs)+1)
+ copy(reachAttrs, attrs)
+ // we sort attributes when creating a bgp message from paths
+ reachAttrs[len(reachAttrs)-1] = reach
+
+ for _, nlri := range reach.Value {
+ p := NewPath(peerInfo, nlri, false, reachAttrs, timestamp, false)
+ p.SetHash(hash)
+ pathList = append(pathList, p)
+ }
+ }
+ for _, nlri := range dels {
+ p := NewPath(peerInfo, nlri, true, []bgp.PathAttributeInterface{}, timestamp, false)
+ pathList = append(pathList, p)
+ }
+ return pathList
+}
+
+type TableManager struct {
+ Tables map[bgp.RouteFamily]*Table
+ Vrfs map[string]*Vrf
+ rfList []bgp.RouteFamily
+}
+
+func NewTableManager(rfList []bgp.RouteFamily) *TableManager {
+ t := &TableManager{
+ Tables: make(map[bgp.RouteFamily]*Table),
+ Vrfs: make(map[string]*Vrf),
+ rfList: rfList,
+ }
+ for _, rf := range rfList {
+ t.Tables[rf] = NewTable(rf)
+ }
+ return t
+}
+
+func (manager *TableManager) GetRFlist() []bgp.RouteFamily {
+ return manager.rfList
+}
+
+func (manager *TableManager) AddVrf(name string, id uint32, rd bgp.RouteDistinguisherInterface, importRt, exportRt []bgp.ExtendedCommunityInterface, info *PeerInfo) ([]*Path, error) {
+ if _, ok := manager.Vrfs[name]; ok {
+ return nil, fmt.Errorf("vrf %s already exists", name)
+ }
+ log.WithFields(log.Fields{
+ "Topic": "Vrf",
+ "Key": name,
+ "Rd": rd,
+ "ImportRt": importRt,
+ "ExportRt": exportRt,
+ }).Debugf("add vrf")
+ manager.Vrfs[name] = &Vrf{
+ Name: name,
+ Id: id,
+ Rd: rd,
+ ImportRt: importRt,
+ ExportRt: exportRt,
+ }
+ msgs := make([]*Path, 0, len(importRt))
+ nexthop := "0.0.0.0"
+ for _, target := range importRt {
+ nlri := bgp.NewRouteTargetMembershipNLRI(info.AS, target)
+ pattr := make([]bgp.PathAttributeInterface, 0, 2)
+ pattr = append(pattr, bgp.NewPathAttributeOrigin(bgp.BGP_ORIGIN_ATTR_TYPE_IGP))
+ pattr = append(pattr, bgp.NewPathAttributeMpReachNLRI(nexthop, []bgp.AddrPrefixInterface{nlri}))
+ msgs = append(msgs, NewPath(info, nlri, false, pattr, time.Now(), false))
+ }
+ return msgs, nil
+}
+
+func (manager *TableManager) DeleteVrf(name string) ([]*Path, error) {
+ if _, ok := manager.Vrfs[name]; !ok {
+ return nil, fmt.Errorf("vrf %s not found", name)
+ }
+ msgs := make([]*Path, 0)
+ vrf := manager.Vrfs[name]
+ for _, t := range manager.Tables {
+ msgs = append(msgs, t.deletePathsByVrf(vrf)...)
+ }
+ log.WithFields(log.Fields{
+ "Topic": "Vrf",
+ "Key": vrf.Name,
+ "Rd": vrf.Rd,
+ "ImportRt": vrf.ImportRt,
+ "ExportRt": vrf.ExportRt,
+ }).Debugf("delete vrf")
+ delete(manager.Vrfs, name)
+ rtcTable := manager.Tables[bgp.RF_RTC_UC]
+ msgs = append(msgs, rtcTable.deleteRTCPathsByVrf(vrf, manager.Vrfs)...)
+ return msgs, nil
+}
+
+func (tm *TableManager) update(newPath *Path) *Update {
+ t := tm.Tables[newPath.GetRouteFamily()]
+ t.validatePath(newPath)
+ dst := t.getOrCreateDest(newPath.GetNlri())
+ u := dst.Calculate(newPath)
+ if len(dst.knownPathList) == 0 {
+ t.deleteDest(dst)
+ }
+ return u
+}
+
+func (manager *TableManager) GetPathListByPeer(info *PeerInfo, rf bgp.RouteFamily) []*Path {
+ if t, ok := manager.Tables[rf]; ok {
+ pathList := make([]*Path, 0, len(t.destinations))
+ for _, dst := range t.destinations {
+ for _, p := range dst.knownPathList {
+ if p.GetSource().Equal(info) {
+ pathList = append(pathList, p)
+ }
+ }
+ }
+ return pathList
+ }
+ return nil
+}
+
+func (manager *TableManager) Update(newPath *Path) []*Update {
+ if newPath == nil || newPath.IsEOR() {
+ return nil
+ }
+
+ // Except for a special case with EVPN, we'll have one destination.
+ updates := make([]*Update, 0, 1)
+ family := newPath.GetRouteFamily()
+ if _, ok := manager.Tables[family]; ok {
+ updates = append(updates, manager.update(newPath))
+
+ if family == bgp.RF_EVPN {
+ for _, p := range manager.handleMacMobility(newPath) {
+ updates = append(updates, manager.update(p))
+ }
+ }
+ }
+ return updates
+}
+
+// EVPN MAC MOBILITY HANDLING
+//
+// RFC7432 15. MAC Mobility
+//
+// A PE receiving a MAC/IP Advertisement route for a MAC address with a
+// different Ethernet segment identifier and a higher sequence number
+// than that which it had previously advertised withdraws its MAC/IP
+// Advertisement route.
+func (manager *TableManager) handleMacMobility(path *Path) []*Path {
+ pathList := make([]*Path, 0)
+ nlri := path.GetNlri().(*bgp.EVPNNLRI)
+ if path.IsWithdraw || path.IsLocal() || nlri.RouteType != bgp.EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT {
+ return nil
+ }
+ for _, path2 := range manager.GetPathList(GLOBAL_RIB_NAME, 0, []bgp.RouteFamily{bgp.RF_EVPN}) {
+ if !path2.IsLocal() || path2.GetNlri().(*bgp.EVPNNLRI).RouteType != bgp.EVPN_ROUTE_TYPE_MAC_IP_ADVERTISEMENT {
+ continue
+ }
+ f := func(p *Path) (bgp.EthernetSegmentIdentifier, net.HardwareAddr, int) {
+ nlri := p.GetNlri().(*bgp.EVPNNLRI)
+ d := nlri.RouteTypeData.(*bgp.EVPNMacIPAdvertisementRoute)
+ ecs := p.GetExtCommunities()
+ seq := -1
+ for _, ec := range ecs {
+ if t, st := ec.GetTypes(); t == bgp.EC_TYPE_EVPN && st == bgp.EC_SUBTYPE_MAC_MOBILITY {
+ seq = int(ec.(*bgp.MacMobilityExtended).Sequence)
+ break
+ }
+ }
+ return d.ESI, d.MacAddress, seq
+ }
+ e1, m1, s1 := f(path)
+ e2, m2, s2 := f(path2)
+ if bytes.Equal(m1, m2) && !bytes.Equal(e1.Value, e2.Value) && s1 > s2 {
+ pathList = append(pathList, path2.Clone(true))
+ }
+ }
+ return pathList
+}
+
+func (manager *TableManager) tables(list ...bgp.RouteFamily) []*Table {
+ l := make([]*Table, 0, len(manager.Tables))
+ if len(list) == 0 {
+ for _, v := range manager.Tables {
+ l = append(l, v)
+ }
+ return l
+ }
+ for _, f := range list {
+ if t, ok := manager.Tables[f]; ok {
+ l = append(l, t)
+ }
+ }
+ return l
+}
+
+func (manager *TableManager) getDestinationCount(rfList []bgp.RouteFamily) int {
+ count := 0
+ for _, t := range manager.tables(rfList...) {
+ count += len(t.GetDestinations())
+ }
+ return count
+}
+
+func (manager *TableManager) GetBestPathList(id string, as uint32, rfList []bgp.RouteFamily) []*Path {
+ if SelectionOptions.DisableBestPathSelection {
+ // Note: If best path selection disabled, there is no best path.
+ return nil
+ }
+ paths := make([]*Path, 0, manager.getDestinationCount(rfList))
+ for _, t := range manager.tables(rfList...) {
+ paths = append(paths, t.Bests(id, as)...)
+ }
+ return paths
+}
+
+func (manager *TableManager) GetBestMultiPathList(id string, rfList []bgp.RouteFamily) [][]*Path {
+ if !UseMultiplePaths.Enabled || SelectionOptions.DisableBestPathSelection {
+ // Note: If multi path not enabled or best path selection disabled,
+ // there is no best multi path.
+ return nil
+ }
+ paths := make([][]*Path, 0, manager.getDestinationCount(rfList))
+ for _, t := range manager.tables(rfList...) {
+ paths = append(paths, t.MultiBests(id)...)
+ }
+ return paths
+}
+
+func (manager *TableManager) GetPathList(id string, as uint32, rfList []bgp.RouteFamily) []*Path {
+ paths := make([]*Path, 0, manager.getDestinationCount(rfList))
+ for _, t := range manager.tables(rfList...) {
+ paths = append(paths, t.GetKnownPathList(id, as)...)
+ }
+ return paths
+}
+
+func (manager *TableManager) GetPathListWithNexthop(id string, rfList []bgp.RouteFamily, nexthop net.IP) []*Path {
+ paths := make([]*Path, 0, manager.getDestinationCount(rfList))
+ for _, rf := range rfList {
+ if t, ok := manager.Tables[rf]; ok {
+ for _, path := range t.GetKnownPathList(id, 0) {
+ if path.GetNexthop().Equal(nexthop) {
+ paths = append(paths, path)
+ }
+ }
+ }
+ }
+ return paths
+}
+
+func (manager *TableManager) GetDestination(path *Path) *Destination {
+ if path == nil {
+ return nil
+ }
+ family := path.GetRouteFamily()
+ t, ok := manager.Tables[family]
+ if !ok {
+ return nil
+ }
+ return t.GetDestination(path.GetNlri())
+}
+
+func (manager *TableManager) TableInfo(id string, as uint32, family bgp.RouteFamily) (*TableInfo, error) {
+ t, ok := manager.Tables[family]
+ if !ok {
+ return nil, fmt.Errorf("address family %s is not configured", family)
+ }
+ return t.Info(id, as), nil
+}
diff --git a/internal/pkg/table/table_manager_test.go b/internal/pkg/table/table_manager_test.go
new file mode 100644
index 00000000..67c26c11
--- /dev/null
+++ b/internal/pkg/table/table_manager_test.go
@@ -0,0 +1,2282 @@
+// Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package table
+
+import (
+ _ "fmt"
+ "net"
+ "testing"
+ "time"
+
+ "github.com/osrg/gobgp/pkg/packet/bgp"
+
+ "github.com/stretchr/testify/assert"
+)
+
+// process BGPUpdate message
+// this function processes only BGPUpdate
+func (manager *TableManager) ProcessUpdate(fromPeer *PeerInfo, message *bgp.BGPMessage) ([]*Path, error) {
+ pathList := make([]*Path, 0)
+ dsts := make([]*Update, 0)
+ for _, path := range ProcessMessage(message, fromPeer, time.Now()) {
+ dsts = append(dsts, manager.Update(path)...)
+ }
+ for _, d := range dsts {
+ b, _, _ := d.GetChanges(GLOBAL_RIB_NAME, 0, false)
+ pathList = append(pathList, b)
+ }
+ return pathList, nil
+}
+
+func peerR1() *PeerInfo {
+ peer := &PeerInfo{
+ AS: 65000,
+ LocalAS: 65000,
+ ID: net.ParseIP("10.0.0.3").To4(),
+ LocalID: net.ParseIP("10.0.0.1").To4(),
+ Address: net.ParseIP("10.0.0.1").To4(),
+ }
+ return peer
+}
+
+func peerR2() *PeerInfo {
+ peer := &PeerInfo{
+ AS: 65100,
+ LocalAS: 65000,
+ Address: net.ParseIP("10.0.0.2").To4(),
+ }
+ return peer
+}
+
+func peerR3() *PeerInfo {
+ peer := &PeerInfo{
+ AS: 65000,
+ LocalAS: 65000,
+ ID: net.ParseIP("10.0.0.2").To4(),
+ LocalID: net.ParseIP("10.0.0.1").To4(),
+ Address: net.ParseIP("10.0.0.3").To4(),
+ }
+ return peer
+}
+
+// test best path calculation and check the result path is from R1
+func TestProcessBGPUpdate_0_select_onlypath_ipv4(t *testing.T) {
+
+ tm := NewTableManager([]bgp.RouteFamily{bgp.RF_IPv4_UC})
+
+ bgpMessage := update_fromR1()
+ peer := peerR1()
+ pList, err := tm.ProcessUpdate(peer, bgpMessage)
+ assert.Equal(t, len(pList), 1)
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ // check type
+ path := pList[0]
+ assert.Equal(t, path.GetRouteFamily(), bgp.RF_IPv4_UC)
+
+ // check PathAttribute
+ pathAttributes := bgpMessage.Body.(*bgp.BGPUpdate).PathAttributes
+ expectedOrigin := pathAttributes[0]
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN)
+ pathOrigin := attr.(*bgp.PathAttributeOrigin)
+ assert.Equal(t, expectedOrigin, pathOrigin)
+
+ expectedAsPath := pathAttributes[1]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
+ pathAspath := attr.(*bgp.PathAttributeAsPath)
+ assert.Equal(t, expectedAsPath, pathAspath)
+
+ expectedNexthopAttr := pathAttributes[2]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP)
+ pathNexthop := attr.(*bgp.PathAttributeNextHop)
+ assert.Equal(t, expectedNexthopAttr, pathNexthop)
+
+ expectedMed := pathAttributes[3]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
+ pathMed := attr.(*bgp.PathAttributeMultiExitDisc)
+ assert.Equal(t, expectedMed, pathMed)
+
+ // check PathAttribute length
+ assert.Equal(t, 4, len(path.GetPathAttrs()))
+
+ // check destination
+ expectedPrefix := "10.10.10.0/24"
+ assert.Equal(t, expectedPrefix, path.getPrefix())
+ // check nexthop
+ expectedNexthop := "192.168.50.1"
+ assert.Equal(t, expectedNexthop, path.GetNexthop().String())
+
+}
+
+// test best path calculation and check the result path is from R1
+func TestProcessBGPUpdate_0_select_onlypath_ipv6(t *testing.T) {
+
+ tm := NewTableManager([]bgp.RouteFamily{bgp.RF_IPv6_UC})
+
+ bgpMessage := update_fromR1_ipv6()
+ peer := peerR1()
+ pList, err := tm.ProcessUpdate(peer, bgpMessage)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ // check type
+ path := pList[0]
+ assert.Equal(t, path.GetRouteFamily(), bgp.RF_IPv6_UC)
+
+ // check PathAttribute
+ pathAttributes := bgpMessage.Body.(*bgp.BGPUpdate).PathAttributes
+
+ expectedNexthopAttr := pathAttributes[0]
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI)
+ pathNexthop := attr.(*bgp.PathAttributeMpReachNLRI)
+ assert.Equal(t, expectedNexthopAttr, pathNexthop)
+
+ expectedOrigin := pathAttributes[1]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN)
+ pathOrigin := attr.(*bgp.PathAttributeOrigin)
+ assert.Equal(t, expectedOrigin, pathOrigin)
+
+ expectedAsPath := pathAttributes[2]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
+ pathAspath := attr.(*bgp.PathAttributeAsPath)
+ assert.Equal(t, expectedAsPath, pathAspath)
+
+ expectedMed := pathAttributes[3]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
+ pathMed := attr.(*bgp.PathAttributeMultiExitDisc)
+ assert.Equal(t, expectedMed, pathMed)
+
+ // check PathAttribute length
+ assert.Equal(t, 4, len(path.GetPathAttrs()))
+
+ // check destination
+ expectedPrefix := "2001:123:123:1::/64"
+ assert.Equal(t, expectedPrefix, path.getPrefix())
+ // check nexthop
+ expectedNexthop := "2001::192:168:50:1"
+ assert.Equal(t, expectedNexthop, path.GetNexthop().String())
+
+}
+
+// test: compare localpref
+func TestProcessBGPUpdate_1_select_high_localpref_ipv4(t *testing.T) {
+
+ tm := NewTableManager([]bgp.RouteFamily{bgp.RF_IPv4_UC})
+
+ // low localpref message
+ origin1 := bgp.NewPathAttributeOrigin(0)
+ aspath1 := createAsPathAttribute([]uint32{65000})
+ nexthop1 := bgp.NewPathAttributeNextHop("192.168.50.1")
+ med1 := bgp.NewPathAttributeMultiExitDisc(0)
+ localpref1 := bgp.NewPathAttributeLocalPref(100)
+
+ pathAttributes1 := []bgp.PathAttributeInterface{
+ origin1, aspath1, nexthop1, med1, localpref1,
+ }
+ nlri1 := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.10.0")}
+ bgpMessage1 := bgp.NewBGPUpdateMessage(nil, pathAttributes1, nlri1)
+
+ // high localpref message
+ origin2 := bgp.NewPathAttributeOrigin(0)
+ aspath2 := createAsPathAttribute([]uint32{65100, 65000})
+ nexthop2 := bgp.NewPathAttributeNextHop("192.168.50.1")
+ med2 := bgp.NewPathAttributeMultiExitDisc(100)
+ localpref2 := bgp.NewPathAttributeLocalPref(200)
+
+ pathAttributes2 := []bgp.PathAttributeInterface{
+ origin2, aspath2, nexthop2, med2, localpref2,
+ }
+ nlri2 := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.10.0")}
+ bgpMessage2 := bgp.NewBGPUpdateMessage(nil, pathAttributes2, nlri2)
+
+ peer1 := peerR1()
+ pList, err := tm.ProcessUpdate(peer1, bgpMessage1)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ peer2 := peerR2()
+ pList, err = tm.ProcessUpdate(peer2, bgpMessage2)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ // check type
+ path := pList[0]
+ assert.Equal(t, path.GetRouteFamily(), bgp.RF_IPv4_UC)
+
+ // check PathAttribute
+ pathAttributes := bgpMessage2.Body.(*bgp.BGPUpdate).PathAttributes
+ expectedOrigin := pathAttributes[0]
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN)
+ pathOrigin := attr.(*bgp.PathAttributeOrigin)
+ assert.Equal(t, expectedOrigin, pathOrigin)
+
+ expectedAsPath := pathAttributes[1]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
+ pathAspath := attr.(*bgp.PathAttributeAsPath)
+ assert.Equal(t, expectedAsPath, pathAspath)
+
+ expectedNexthopAttr := pathAttributes[2]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP)
+ pathNexthop := attr.(*bgp.PathAttributeNextHop)
+ assert.Equal(t, expectedNexthopAttr, pathNexthop)
+
+ expectedMed := pathAttributes[3]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
+ pathMed := attr.(*bgp.PathAttributeMultiExitDisc)
+ assert.Equal(t, expectedMed, pathMed)
+
+ // check PathAttribute length
+ assert.Equal(t, len(pathAttributes2), len(path.GetPathAttrs()))
+
+ // check destination
+ expectedPrefix := "10.10.10.0/24"
+ assert.Equal(t, expectedPrefix, path.getPrefix())
+ // check nexthop
+ expectedNexthop := "192.168.50.1"
+ assert.Equal(t, expectedNexthop, path.GetNexthop().String())
+
+}
+
+func TestProcessBGPUpdate_1_select_high_localpref_ipv6(t *testing.T) {
+
+ tm := NewTableManager([]bgp.RouteFamily{bgp.RF_IPv6_UC})
+
+ origin1 := bgp.NewPathAttributeOrigin(0)
+ aspath1 := createAsPathAttribute([]uint32{65000})
+ mp_reach1 := createMpReach("2001::192:168:50:1",
+ []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(64, "2001:123:123:1::")})
+ med1 := bgp.NewPathAttributeMultiExitDisc(100)
+ localpref1 := bgp.NewPathAttributeLocalPref(100)
+
+ pathAttributes1 := []bgp.PathAttributeInterface{
+ mp_reach1, origin1, aspath1, med1, localpref1,
+ }
+
+ bgpMessage1 := bgp.NewBGPUpdateMessage(nil, pathAttributes1, nil)
+
+ origin2 := bgp.NewPathAttributeOrigin(0)
+ aspath2 := createAsPathAttribute([]uint32{65100, 65000})
+ mp_reach2 := createMpReach("2001::192:168:100:1",
+ []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(64, "2001:123:123:1::")})
+ med2 := bgp.NewPathAttributeMultiExitDisc(100)
+ localpref2 := bgp.NewPathAttributeLocalPref(200)
+
+ pathAttributes2 := []bgp.PathAttributeInterface{
+ mp_reach2, origin2, aspath2, med2, localpref2,
+ }
+
+ bgpMessage2 := bgp.NewBGPUpdateMessage(nil, pathAttributes2, nil)
+
+ peer1 := peerR1()
+ pList, err := tm.ProcessUpdate(peer1, bgpMessage1)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ peer2 := peerR2()
+ pList, err = tm.ProcessUpdate(peer2, bgpMessage2)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ // check type
+ path := pList[0]
+ assert.Equal(t, path.GetRouteFamily(), bgp.RF_IPv6_UC)
+
+ // check PathAttribute
+ pathAttributes := bgpMessage2.Body.(*bgp.BGPUpdate).PathAttributes
+
+ expectedNexthopAttr := pathAttributes[0]
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI)
+ pathNexthop := attr.(*bgp.PathAttributeMpReachNLRI)
+ assert.Equal(t, expectedNexthopAttr, pathNexthop)
+
+ expectedOrigin := pathAttributes[1]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN)
+ pathOrigin := attr.(*bgp.PathAttributeOrigin)
+ assert.Equal(t, expectedOrigin, pathOrigin)
+
+ expectedAsPath := pathAttributes[2]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
+ pathAspath := attr.(*bgp.PathAttributeAsPath)
+ assert.Equal(t, expectedAsPath, pathAspath)
+
+ expectedMed := pathAttributes[3]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
+ pathMed := attr.(*bgp.PathAttributeMultiExitDisc)
+ assert.Equal(t, expectedMed, pathMed)
+
+ // check PathAttribute length
+ assert.Equal(t, 5, len(path.GetPathAttrs()))
+
+ // check destination
+ expectedPrefix := "2001:123:123:1::/64"
+ assert.Equal(t, expectedPrefix, path.getPrefix())
+ // check nexthop
+ expectedNexthop := "2001::192:168:100:1"
+ assert.Equal(t, expectedNexthop, path.GetNexthop().String())
+
+}
+
+// test: compare localOrigin
+func TestProcessBGPUpdate_2_select_local_origin_ipv4(t *testing.T) {
+
+ tm := NewTableManager([]bgp.RouteFamily{bgp.RF_IPv4_UC})
+
+ // low localpref message
+ origin1 := bgp.NewPathAttributeOrigin(0)
+ aspath1 := createAsPathAttribute([]uint32{65000})
+ nexthop1 := bgp.NewPathAttributeNextHop("192.168.50.1")
+ med1 := bgp.NewPathAttributeMultiExitDisc(0)
+ localpref1 := bgp.NewPathAttributeLocalPref(100)
+
+ pathAttributes1 := []bgp.PathAttributeInterface{
+ origin1, aspath1, nexthop1, med1, localpref1,
+ }
+ nlri1 := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.10.0")}
+ bgpMessage1 := bgp.NewBGPUpdateMessage(nil, pathAttributes1, nlri1)
+
+ // high localpref message
+ origin2 := bgp.NewPathAttributeOrigin(0)
+ aspath2 := createAsPathAttribute([]uint32{})
+ nexthop2 := bgp.NewPathAttributeNextHop("0.0.0.0")
+ med2 := bgp.NewPathAttributeMultiExitDisc(100)
+ localpref2 := bgp.NewPathAttributeLocalPref(100)
+
+ pathAttributes2 := []bgp.PathAttributeInterface{
+ origin2, aspath2, nexthop2, med2, localpref2,
+ }
+ nlri2 := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.10.0")}
+ bgpMessage2 := bgp.NewBGPUpdateMessage(nil, pathAttributes2, nlri2)
+
+ peer1 := peerR1()
+ pList, err := tm.ProcessUpdate(peer1, bgpMessage1)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ var peer2 *PeerInfo = &PeerInfo{
+ Address: net.ParseIP("0.0.0.0"),
+ }
+ pList, err = tm.ProcessUpdate(peer2, bgpMessage2)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ // check type
+ path := pList[0]
+ assert.Equal(t, path.GetRouteFamily(), bgp.RF_IPv4_UC)
+
+ // check PathAttribute
+ pathAttributes := bgpMessage2.Body.(*bgp.BGPUpdate).PathAttributes
+ expectedOrigin := pathAttributes[0]
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN)
+ pathOrigin := attr.(*bgp.PathAttributeOrigin)
+ assert.Equal(t, expectedOrigin, pathOrigin)
+
+ expectedAsPath := pathAttributes[1]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
+ pathAspath := attr.(*bgp.PathAttributeAsPath)
+ assert.Equal(t, expectedAsPath, pathAspath)
+
+ expectedNexthopAttr := pathAttributes[2]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP)
+ pathNexthop := attr.(*bgp.PathAttributeNextHop)
+ assert.Equal(t, expectedNexthopAttr, pathNexthop)
+
+ expectedMed := pathAttributes[3]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
+ pathMed := attr.(*bgp.PathAttributeMultiExitDisc)
+ assert.Equal(t, expectedMed, pathMed)
+
+ // check PathAttribute length
+ assert.Equal(t, len(pathAttributes2), len(path.GetPathAttrs()))
+
+ // check destination
+ expectedPrefix := "10.10.10.0/24"
+ assert.Equal(t, expectedPrefix, path.getPrefix())
+ // check nexthop
+ expectedNexthop := "0.0.0.0"
+ assert.Equal(t, expectedNexthop, path.GetNexthop().String())
+
+}
+
+func TestProcessBGPUpdate_2_select_local_origin_ipv6(t *testing.T) {
+
+ tm := NewTableManager([]bgp.RouteFamily{bgp.RF_IPv6_UC})
+
+ origin1 := bgp.NewPathAttributeOrigin(0)
+ aspath1 := createAsPathAttribute([]uint32{65000})
+ mp_reach1 := createMpReach("2001::192:168:50:1",
+ []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(64, "2001:123:123:1::")})
+ med1 := bgp.NewPathAttributeMultiExitDisc(100)
+ localpref1 := bgp.NewPathAttributeLocalPref(100)
+
+ pathAttributes1 := []bgp.PathAttributeInterface{
+ mp_reach1, origin1, aspath1, med1, localpref1,
+ }
+
+ bgpMessage1 := bgp.NewBGPUpdateMessage(nil, pathAttributes1, nil)
+
+ origin2 := bgp.NewPathAttributeOrigin(0)
+ aspath2 := createAsPathAttribute([]uint32{})
+ mp_reach2 := createMpReach("::",
+ []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(64, "2001:123:123:1::")})
+ med2 := bgp.NewPathAttributeMultiExitDisc(100)
+ localpref2 := bgp.NewPathAttributeLocalPref(100)
+
+ pathAttributes2 := []bgp.PathAttributeInterface{
+ mp_reach2, origin2, aspath2, med2, localpref2,
+ }
+
+ bgpMessage2 := bgp.NewBGPUpdateMessage(nil, pathAttributes2, nil)
+
+ peer1 := peerR1()
+ pList, err := tm.ProcessUpdate(peer1, bgpMessage1)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ var peer2 *PeerInfo = &PeerInfo{
+ Address: net.ParseIP("0.0.0.0"),
+ }
+
+ pList, err = tm.ProcessUpdate(peer2, bgpMessage2)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ // check type
+ path := pList[0]
+ assert.Equal(t, path.GetRouteFamily(), bgp.RF_IPv6_UC)
+
+ // check PathAttribute
+ pathAttributes := bgpMessage2.Body.(*bgp.BGPUpdate).PathAttributes
+
+ expectedNexthopAttr := pathAttributes[0]
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI)
+ pathNexthop := attr.(*bgp.PathAttributeMpReachNLRI)
+ assert.Equal(t, expectedNexthopAttr, pathNexthop)
+
+ expectedOrigin := pathAttributes[1]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN)
+ pathOrigin := attr.(*bgp.PathAttributeOrigin)
+ assert.Equal(t, expectedOrigin, pathOrigin)
+
+ expectedAsPath := pathAttributes[2]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
+ pathAspath := attr.(*bgp.PathAttributeAsPath)
+ assert.Equal(t, expectedAsPath, pathAspath)
+
+ expectedMed := pathAttributes[3]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
+ pathMed := attr.(*bgp.PathAttributeMultiExitDisc)
+ assert.Equal(t, expectedMed, pathMed)
+
+ // check PathAttribute length
+ assert.Equal(t, 5, len(path.GetPathAttrs()))
+
+ // check destination
+ expectedPrefix := "2001:123:123:1::/64"
+ assert.Equal(t, expectedPrefix, path.getPrefix())
+ // check nexthop
+ expectedNexthop := "::"
+ assert.Equal(t, expectedNexthop, path.GetNexthop().String())
+
+}
+
+// test: compare AS_PATH
+func TestProcessBGPUpdate_3_select_aspath_ipv4(t *testing.T) {
+
+ tm := NewTableManager([]bgp.RouteFamily{bgp.RF_IPv4_UC})
+
+ bgpMessage1 := update_fromR2viaR1()
+ peer1 := peerR1()
+ pList, err := tm.ProcessUpdate(peer1, bgpMessage1)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+ bgpMessage2 := update_fromR2()
+ peer2 := peerR2()
+ pList, err = tm.ProcessUpdate(peer2, bgpMessage2)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ // check type
+ path := pList[0]
+ assert.Equal(t, path.GetRouteFamily(), bgp.RF_IPv4_UC)
+
+ // check PathAttribute
+ pathAttributes := bgpMessage2.Body.(*bgp.BGPUpdate).PathAttributes
+ expectedOrigin := pathAttributes[0]
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN)
+ pathOrigin := attr.(*bgp.PathAttributeOrigin)
+ assert.Equal(t, expectedOrigin, pathOrigin)
+
+ expectedAsPath := pathAttributes[1]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
+ pathAspath := attr.(*bgp.PathAttributeAsPath)
+ assert.Equal(t, expectedAsPath, pathAspath)
+
+ expectedNexthopAttr := pathAttributes[2]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP)
+ pathNexthop := attr.(*bgp.PathAttributeNextHop)
+ assert.Equal(t, expectedNexthopAttr, pathNexthop)
+
+ expectedMed := pathAttributes[3]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
+ pathMed := attr.(*bgp.PathAttributeMultiExitDisc)
+ assert.Equal(t, expectedMed, pathMed)
+
+ // check PathAttribute length
+ assert.Equal(t, 4, len(path.GetPathAttrs()))
+
+ // check destination
+ expectedPrefix := "20.20.20.0/24"
+ assert.Equal(t, expectedPrefix, path.getPrefix())
+ // check nexthop
+ expectedNexthop := "192.168.100.1"
+ assert.Equal(t, expectedNexthop, path.GetNexthop().String())
+
+}
+
+func TestProcessBGPUpdate_3_select_aspath_ipv6(t *testing.T) {
+
+ tm := NewTableManager([]bgp.RouteFamily{bgp.RF_IPv6_UC})
+
+ bgpMessage1 := update_fromR2viaR1_ipv6()
+ peer1 := peerR1()
+ pList, err := tm.ProcessUpdate(peer1, bgpMessage1)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+ bgpMessage2 := update_fromR2_ipv6()
+ peer2 := peerR2()
+ pList, err = tm.ProcessUpdate(peer2, bgpMessage2)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ // check type
+ path := pList[0]
+ assert.Equal(t, path.GetRouteFamily(), bgp.RF_IPv6_UC)
+
+ // check PathAttribute
+ pathAttributes := bgpMessage2.Body.(*bgp.BGPUpdate).PathAttributes
+
+ expectedNexthopAttr := pathAttributes[0]
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI)
+ pathNexthop := attr.(*bgp.PathAttributeMpReachNLRI)
+ assert.Equal(t, expectedNexthopAttr, pathNexthop)
+
+ expectedOrigin := pathAttributes[1]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN)
+ pathOrigin := attr.(*bgp.PathAttributeOrigin)
+ assert.Equal(t, expectedOrigin, pathOrigin)
+
+ expectedAsPath := pathAttributes[2]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
+ pathAspath := attr.(*bgp.PathAttributeAsPath)
+ assert.Equal(t, expectedAsPath, pathAspath)
+
+ expectedMed := pathAttributes[3]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
+ pathMed := attr.(*bgp.PathAttributeMultiExitDisc)
+ assert.Equal(t, expectedMed, pathMed)
+
+ // check PathAttribute length
+ assert.Equal(t, 4, len(path.GetPathAttrs()))
+
+ // check destination
+ expectedPrefix := "2002:223:123:1::/64"
+ assert.Equal(t, expectedPrefix, path.getPrefix())
+ // check nexthop
+ expectedNexthop := "2001::192:168:100:1"
+ assert.Equal(t, expectedNexthop, path.GetNexthop().String())
+
+}
+
+// test: compare Origin
+func TestProcessBGPUpdate_4_select_low_origin_ipv4(t *testing.T) {
+
+ tm := NewTableManager([]bgp.RouteFamily{bgp.RF_IPv4_UC})
+
+ // low origin message
+ origin1 := bgp.NewPathAttributeOrigin(1)
+ aspath1 := createAsPathAttribute([]uint32{65200, 65000})
+ nexthop1 := bgp.NewPathAttributeNextHop("192.168.50.1")
+ med1 := bgp.NewPathAttributeMultiExitDisc(100)
+ localpref1 := bgp.NewPathAttributeLocalPref(100)
+
+ pathAttributes1 := []bgp.PathAttributeInterface{
+ origin1, aspath1, nexthop1, med1, localpref1,
+ }
+ nlri1 := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.10.0")}
+ bgpMessage1 := bgp.NewBGPUpdateMessage(nil, pathAttributes1, nlri1)
+
+ // high origin message
+ origin2 := bgp.NewPathAttributeOrigin(0)
+ aspath2 := createAsPathAttribute([]uint32{65100, 65000})
+ nexthop2 := bgp.NewPathAttributeNextHop("192.168.100.1")
+ med2 := bgp.NewPathAttributeMultiExitDisc(100)
+ localpref2 := bgp.NewPathAttributeLocalPref(100)
+
+ pathAttributes2 := []bgp.PathAttributeInterface{
+ origin2, aspath2, nexthop2, med2, localpref2,
+ }
+ nlri2 := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.10.0")}
+ bgpMessage2 := bgp.NewBGPUpdateMessage(nil, pathAttributes2, nlri2)
+
+ peer1 := peerR1()
+ pList, err := tm.ProcessUpdate(peer1, bgpMessage1)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ peer2 := peerR2()
+ pList, err = tm.ProcessUpdate(peer2, bgpMessage2)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ // check type
+ path := pList[0]
+ assert.Equal(t, path.GetRouteFamily(), bgp.RF_IPv4_UC)
+
+ // check PathAttribute
+ pathAttributes := bgpMessage2.Body.(*bgp.BGPUpdate).PathAttributes
+ expectedOrigin := pathAttributes[0]
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN)
+ pathOrigin := attr.(*bgp.PathAttributeOrigin)
+ assert.Equal(t, expectedOrigin, pathOrigin)
+
+ expectedAsPath := pathAttributes[1]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
+ pathAspath := attr.(*bgp.PathAttributeAsPath)
+ assert.Equal(t, expectedAsPath, pathAspath)
+
+ expectedNexthopAttr := pathAttributes[2]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP)
+ pathNexthop := attr.(*bgp.PathAttributeNextHop)
+ assert.Equal(t, expectedNexthopAttr, pathNexthop)
+
+ expectedMed := pathAttributes[3]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
+ pathMed := attr.(*bgp.PathAttributeMultiExitDisc)
+ assert.Equal(t, expectedMed, pathMed)
+
+ // check PathAttribute length
+ assert.Equal(t, len(pathAttributes2), len(path.GetPathAttrs()))
+
+ // check destination
+ expectedPrefix := "10.10.10.0/24"
+ assert.Equal(t, expectedPrefix, path.getPrefix())
+ // check nexthop
+ expectedNexthop := "192.168.100.1"
+ assert.Equal(t, expectedNexthop, path.GetNexthop().String())
+
+}
+
+func TestProcessBGPUpdate_4_select_low_origin_ipv6(t *testing.T) {
+
+ tm := NewTableManager([]bgp.RouteFamily{bgp.RF_IPv6_UC})
+
+ origin1 := bgp.NewPathAttributeOrigin(1)
+ aspath1 := createAsPathAttribute([]uint32{65200, 65000})
+ mp_reach1 := createMpReach("2001::192:168:50:1",
+ []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(64, "2001:123:123:1::")})
+ med1 := bgp.NewPathAttributeMultiExitDisc(100)
+ localpref1 := bgp.NewPathAttributeLocalPref(100)
+
+ pathAttributes1 := []bgp.PathAttributeInterface{
+ mp_reach1, origin1, aspath1, med1, localpref1,
+ }
+
+ bgpMessage1 := bgp.NewBGPUpdateMessage(nil, pathAttributes1, nil)
+
+ origin2 := bgp.NewPathAttributeOrigin(0)
+ aspath2 := createAsPathAttribute([]uint32{65100, 65000})
+ mp_reach2 := createMpReach("2001::192:168:100:1",
+ []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(64, "2001:123:123:1::")})
+ med2 := bgp.NewPathAttributeMultiExitDisc(100)
+ localpref2 := bgp.NewPathAttributeLocalPref(200)
+
+ pathAttributes2 := []bgp.PathAttributeInterface{
+ mp_reach2, origin2, aspath2, med2, localpref2,
+ }
+
+ bgpMessage2 := bgp.NewBGPUpdateMessage(nil, pathAttributes2, nil)
+
+ peer1 := peerR1()
+ pList, err := tm.ProcessUpdate(peer1, bgpMessage1)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ peer2 := peerR2()
+ pList, err = tm.ProcessUpdate(peer2, bgpMessage2)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ // check type
+ path := pList[0]
+ assert.Equal(t, path.GetRouteFamily(), bgp.RF_IPv6_UC)
+
+ // check PathAttribute
+ pathAttributes := bgpMessage2.Body.(*bgp.BGPUpdate).PathAttributes
+
+ expectedNexthopAttr := pathAttributes[0]
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI)
+ pathNexthop := attr.(*bgp.PathAttributeMpReachNLRI)
+ assert.Equal(t, expectedNexthopAttr, pathNexthop)
+
+ expectedOrigin := pathAttributes[1]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN)
+ pathOrigin := attr.(*bgp.PathAttributeOrigin)
+ assert.Equal(t, expectedOrigin, pathOrigin)
+
+ expectedAsPath := pathAttributes[2]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
+ pathAspath := attr.(*bgp.PathAttributeAsPath)
+ assert.Equal(t, expectedAsPath, pathAspath)
+
+ expectedMed := pathAttributes[3]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
+ pathMed := attr.(*bgp.PathAttributeMultiExitDisc)
+ assert.Equal(t, expectedMed, pathMed)
+
+ // check PathAttribute length
+ assert.Equal(t, 5, len(path.GetPathAttrs()))
+
+ // check destination
+ expectedPrefix := "2001:123:123:1::/64"
+ assert.Equal(t, expectedPrefix, path.getPrefix())
+ // check nexthop
+ expectedNexthop := "2001::192:168:100:1"
+ assert.Equal(t, expectedNexthop, path.GetNexthop().String())
+
+}
+
+// test: compare MED
+func TestProcessBGPUpdate_5_select_low_med_ipv4(t *testing.T) {
+
+ tm := NewTableManager([]bgp.RouteFamily{bgp.RF_IPv4_UC})
+
+ // low origin message
+ origin1 := bgp.NewPathAttributeOrigin(0)
+ aspath1 := createAsPathAttribute([]uint32{65200, 65000})
+ nexthop1 := bgp.NewPathAttributeNextHop("192.168.50.1")
+ med1 := bgp.NewPathAttributeMultiExitDisc(500)
+ localpref1 := bgp.NewPathAttributeLocalPref(100)
+
+ pathAttributes1 := []bgp.PathAttributeInterface{
+ origin1, aspath1, nexthop1, med1, localpref1,
+ }
+ nlri1 := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.10.0")}
+ bgpMessage1 := bgp.NewBGPUpdateMessage(nil, pathAttributes1, nlri1)
+
+ // high origin message
+ origin2 := bgp.NewPathAttributeOrigin(0)
+ aspath2 := createAsPathAttribute([]uint32{65100, 65000})
+ nexthop2 := bgp.NewPathAttributeNextHop("192.168.100.1")
+ med2 := bgp.NewPathAttributeMultiExitDisc(100)
+ localpref2 := bgp.NewPathAttributeLocalPref(100)
+
+ pathAttributes2 := []bgp.PathAttributeInterface{
+ origin2, aspath2, nexthop2, med2, localpref2,
+ }
+ nlri2 := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.10.0")}
+ bgpMessage2 := bgp.NewBGPUpdateMessage(nil, pathAttributes2, nlri2)
+
+ peer1 := peerR1()
+ pList, err := tm.ProcessUpdate(peer1, bgpMessage1)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ peer2 := peerR2()
+ pList, err = tm.ProcessUpdate(peer2, bgpMessage2)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ // check type
+ path := pList[0]
+ assert.Equal(t, path.GetRouteFamily(), bgp.RF_IPv4_UC)
+
+ // check PathAttribute
+ pathAttributes := bgpMessage2.Body.(*bgp.BGPUpdate).PathAttributes
+ expectedOrigin := pathAttributes[0]
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN)
+ pathOrigin := attr.(*bgp.PathAttributeOrigin)
+ assert.Equal(t, expectedOrigin, pathOrigin)
+
+ expectedAsPath := pathAttributes[1]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
+ pathAspath := attr.(*bgp.PathAttributeAsPath)
+ assert.Equal(t, expectedAsPath, pathAspath)
+
+ expectedNexthopAttr := pathAttributes[2]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP)
+ pathNexthop := attr.(*bgp.PathAttributeNextHop)
+ assert.Equal(t, expectedNexthopAttr, pathNexthop)
+
+ expectedMed := pathAttributes[3]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
+ pathMed := attr.(*bgp.PathAttributeMultiExitDisc)
+ assert.Equal(t, expectedMed, pathMed)
+
+ // check PathAttribute length
+ assert.Equal(t, len(pathAttributes2), len(path.GetPathAttrs()))
+
+ // check destination
+ expectedPrefix := "10.10.10.0/24"
+ assert.Equal(t, expectedPrefix, path.getPrefix())
+ // check nexthop
+ expectedNexthop := "192.168.100.1"
+ assert.Equal(t, expectedNexthop, path.GetNexthop().String())
+
+}
+
+func TestProcessBGPUpdate_5_select_low_med_ipv6(t *testing.T) {
+
+ tm := NewTableManager([]bgp.RouteFamily{bgp.RF_IPv6_UC})
+
+ origin1 := bgp.NewPathAttributeOrigin(0)
+ aspath1 := createAsPathAttribute([]uint32{65200, 65000})
+ mp_reach1 := createMpReach("2001::192:168:50:1",
+ []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(64, "2001:123:123:1::")})
+ med1 := bgp.NewPathAttributeMultiExitDisc(500)
+ localpref1 := bgp.NewPathAttributeLocalPref(100)
+
+ pathAttributes1 := []bgp.PathAttributeInterface{
+ mp_reach1, origin1, aspath1, med1, localpref1,
+ }
+
+ bgpMessage1 := bgp.NewBGPUpdateMessage(nil, pathAttributes1, nil)
+
+ origin2 := bgp.NewPathAttributeOrigin(0)
+ aspath2 := createAsPathAttribute([]uint32{65100, 65000})
+ mp_reach2 := createMpReach("2001::192:168:100:1",
+ []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(64, "2001:123:123:1::")})
+ med2 := bgp.NewPathAttributeMultiExitDisc(200)
+ localpref2 := bgp.NewPathAttributeLocalPref(100)
+
+ pathAttributes2 := []bgp.PathAttributeInterface{
+ mp_reach2, origin2, aspath2, med2, localpref2,
+ }
+
+ bgpMessage2 := bgp.NewBGPUpdateMessage(nil, pathAttributes2, nil)
+
+ peer1 := peerR1()
+ pList, err := tm.ProcessUpdate(peer1, bgpMessage1)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ peer2 := peerR2()
+ pList, err = tm.ProcessUpdate(peer2, bgpMessage2)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ // check type
+ path := pList[0]
+ assert.Equal(t, path.GetRouteFamily(), bgp.RF_IPv6_UC)
+
+ // check PathAttribute
+ pathAttributes := bgpMessage2.Body.(*bgp.BGPUpdate).PathAttributes
+
+ expectedNexthopAttr := pathAttributes[0]
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI)
+ pathNexthop := attr.(*bgp.PathAttributeMpReachNLRI)
+ assert.Equal(t, expectedNexthopAttr, pathNexthop)
+
+ expectedOrigin := pathAttributes[1]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN)
+ pathOrigin := attr.(*bgp.PathAttributeOrigin)
+ assert.Equal(t, expectedOrigin, pathOrigin)
+
+ expectedAsPath := pathAttributes[2]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
+ pathAspath := attr.(*bgp.PathAttributeAsPath)
+ assert.Equal(t, expectedAsPath, pathAspath)
+
+ expectedMed := pathAttributes[3]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
+ pathMed := attr.(*bgp.PathAttributeMultiExitDisc)
+ assert.Equal(t, expectedMed, pathMed)
+
+ // check PathAttribute length
+ assert.Equal(t, 5, len(path.GetPathAttrs()))
+
+ // check destination
+ expectedPrefix := "2001:123:123:1::/64"
+ assert.Equal(t, expectedPrefix, path.getPrefix())
+ // check nexthop
+ expectedNexthop := "2001::192:168:100:1"
+ assert.Equal(t, expectedNexthop, path.GetNexthop().String())
+
+}
+
+// test: compare AS_NUMBER(prefer eBGP path)
+func TestProcessBGPUpdate_6_select_ebgp_path_ipv4(t *testing.T) {
+
+ tm := NewTableManager([]bgp.RouteFamily{bgp.RF_IPv4_UC})
+
+ // low origin message
+ origin1 := bgp.NewPathAttributeOrigin(0)
+ aspath1 := createAsPathAttribute([]uint32{65000, 65200})
+ nexthop1 := bgp.NewPathAttributeNextHop("192.168.50.1")
+ med1 := bgp.NewPathAttributeMultiExitDisc(200)
+ localpref1 := bgp.NewPathAttributeLocalPref(100)
+
+ pathAttributes1 := []bgp.PathAttributeInterface{
+ origin1, aspath1, nexthop1, med1, localpref1,
+ }
+ nlri1 := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.10.0")}
+ bgpMessage1 := bgp.NewBGPUpdateMessage(nil, pathAttributes1, nlri1)
+
+ // high origin message
+ origin2 := bgp.NewPathAttributeOrigin(0)
+ aspath2 := createAsPathAttribute([]uint32{65100, 65000})
+ nexthop2 := bgp.NewPathAttributeNextHop("192.168.100.1")
+ med2 := bgp.NewPathAttributeMultiExitDisc(200)
+ localpref2 := bgp.NewPathAttributeLocalPref(100)
+
+ pathAttributes2 := []bgp.PathAttributeInterface{
+ origin2, aspath2, nexthop2, med2, localpref2,
+ }
+ nlri2 := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.10.0")}
+ bgpMessage2 := bgp.NewBGPUpdateMessage(nil, pathAttributes2, nlri2)
+
+ peer1 := peerR1()
+ pList, err := tm.ProcessUpdate(peer1, bgpMessage1)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ peer2 := peerR2()
+ pList, err = tm.ProcessUpdate(peer2, bgpMessage2)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ // check type
+ path := pList[0]
+ assert.Equal(t, path.GetRouteFamily(), bgp.RF_IPv4_UC)
+
+ // check PathAttribute
+ pathAttributes := bgpMessage2.Body.(*bgp.BGPUpdate).PathAttributes
+ expectedOrigin := pathAttributes[0]
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN)
+ pathOrigin := attr.(*bgp.PathAttributeOrigin)
+ assert.Equal(t, expectedOrigin, pathOrigin)
+
+ expectedAsPath := pathAttributes[1]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
+ pathAspath := attr.(*bgp.PathAttributeAsPath)
+ assert.Equal(t, expectedAsPath, pathAspath)
+
+ expectedNexthopAttr := pathAttributes[2]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP)
+ pathNexthop := attr.(*bgp.PathAttributeNextHop)
+ assert.Equal(t, expectedNexthopAttr, pathNexthop)
+
+ expectedMed := pathAttributes[3]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
+ pathMed := attr.(*bgp.PathAttributeMultiExitDisc)
+ assert.Equal(t, expectedMed, pathMed)
+
+ // check PathAttribute length
+ assert.Equal(t, len(pathAttributes2), len(path.GetPathAttrs()))
+
+ // check destination
+ expectedPrefix := "10.10.10.0/24"
+ assert.Equal(t, expectedPrefix, path.getPrefix())
+ // check nexthop
+ expectedNexthop := "192.168.100.1"
+ assert.Equal(t, expectedNexthop, path.GetNexthop().String())
+
+}
+
+func TestProcessBGPUpdate_6_select_ebgp_path_ipv6(t *testing.T) {
+
+ tm := NewTableManager([]bgp.RouteFamily{bgp.RF_IPv6_UC})
+
+ origin1 := bgp.NewPathAttributeOrigin(0)
+ aspath1 := createAsPathAttribute([]uint32{65000, 65200})
+ mp_reach1 := createMpReach("2001::192:168:50:1",
+ []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(64, "2001:123:123:1::")})
+ med1 := bgp.NewPathAttributeMultiExitDisc(200)
+ localpref1 := bgp.NewPathAttributeLocalPref(100)
+
+ pathAttributes1 := []bgp.PathAttributeInterface{
+ mp_reach1, origin1, aspath1, med1, localpref1,
+ }
+
+ bgpMessage1 := bgp.NewBGPUpdateMessage(nil, pathAttributes1, nil)
+
+ origin2 := bgp.NewPathAttributeOrigin(0)
+ aspath2 := createAsPathAttribute([]uint32{65100, 65200})
+ mp_reach2 := createMpReach("2001::192:168:100:1",
+ []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(64, "2001:123:123:1::")})
+ med2 := bgp.NewPathAttributeMultiExitDisc(200)
+ localpref2 := bgp.NewPathAttributeLocalPref(100)
+
+ pathAttributes2 := []bgp.PathAttributeInterface{
+ mp_reach2, origin2, aspath2, med2, localpref2,
+ }
+
+ bgpMessage2 := bgp.NewBGPUpdateMessage(nil, pathAttributes2, nil)
+
+ peer1 := peerR1()
+ pList, err := tm.ProcessUpdate(peer1, bgpMessage1)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ peer2 := peerR2()
+ pList, err = tm.ProcessUpdate(peer2, bgpMessage2)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ // check type
+ path := pList[0]
+ assert.Equal(t, path.GetRouteFamily(), bgp.RF_IPv6_UC)
+
+ // check PathAttribute
+ pathAttributes := bgpMessage2.Body.(*bgp.BGPUpdate).PathAttributes
+
+ expectedNexthopAttr := pathAttributes[0]
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI)
+ pathNexthop := attr.(*bgp.PathAttributeMpReachNLRI)
+ assert.Equal(t, expectedNexthopAttr, pathNexthop)
+
+ expectedOrigin := pathAttributes[1]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN)
+ pathOrigin := attr.(*bgp.PathAttributeOrigin)
+ assert.Equal(t, expectedOrigin, pathOrigin)
+
+ expectedAsPath := pathAttributes[2]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
+ pathAspath := attr.(*bgp.PathAttributeAsPath)
+ assert.Equal(t, expectedAsPath, pathAspath)
+
+ expectedMed := pathAttributes[3]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
+ pathMed := attr.(*bgp.PathAttributeMultiExitDisc)
+ assert.Equal(t, expectedMed, pathMed)
+
+ // check PathAttribute length
+ assert.Equal(t, 5, len(path.GetPathAttrs()))
+
+ // check destination
+ expectedPrefix := "2001:123:123:1::/64"
+ assert.Equal(t, expectedPrefix, path.getPrefix())
+ // check nexthop
+ expectedNexthop := "2001::192:168:100:1"
+ assert.Equal(t, expectedNexthop, path.GetNexthop().String())
+
+}
+
+// test: compare IGP cost -> N/A
+
+// test: compare Router ID
+func TestProcessBGPUpdate_7_select_low_routerid_path_ipv4(t *testing.T) {
+
+ tm := NewTableManager([]bgp.RouteFamily{bgp.RF_IPv4_UC})
+ SelectionOptions.ExternalCompareRouterId = true
+
+ // low origin message
+ origin1 := bgp.NewPathAttributeOrigin(0)
+ aspath1 := createAsPathAttribute([]uint32{65000, 65200})
+ nexthop1 := bgp.NewPathAttributeNextHop("192.168.50.1")
+ med1 := bgp.NewPathAttributeMultiExitDisc(200)
+ localpref1 := bgp.NewPathAttributeLocalPref(100)
+
+ pathAttributes1 := []bgp.PathAttributeInterface{
+ origin1, aspath1, nexthop1, med1, localpref1,
+ }
+ nlri1 := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.10.0")}
+ bgpMessage1 := bgp.NewBGPUpdateMessage(nil, pathAttributes1, nlri1)
+
+ // high origin message
+ origin2 := bgp.NewPathAttributeOrigin(0)
+ aspath2 := createAsPathAttribute([]uint32{65000, 65100})
+ nexthop2 := bgp.NewPathAttributeNextHop("192.168.100.1")
+ med2 := bgp.NewPathAttributeMultiExitDisc(200)
+ localpref2 := bgp.NewPathAttributeLocalPref(100)
+
+ pathAttributes2 := []bgp.PathAttributeInterface{
+ origin2, aspath2, nexthop2, med2, localpref2,
+ }
+ nlri2 := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.10.0")}
+ bgpMessage2 := bgp.NewBGPUpdateMessage(nil, pathAttributes2, nlri2)
+
+ peer1 := peerR1()
+ pList, err := tm.ProcessUpdate(peer1, bgpMessage1)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ peer3 := peerR3()
+ pList, err = tm.ProcessUpdate(peer3, bgpMessage2)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ // check type
+ path := pList[0]
+ assert.Equal(t, path.GetRouteFamily(), bgp.RF_IPv4_UC)
+
+ // check PathAttribute
+ pathAttributes := bgpMessage2.Body.(*bgp.BGPUpdate).PathAttributes
+ expectedOrigin := pathAttributes[0]
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN)
+ pathOrigin := attr.(*bgp.PathAttributeOrigin)
+ assert.Equal(t, expectedOrigin, pathOrigin)
+
+ expectedAsPath := pathAttributes[1]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
+ pathAspath := attr.(*bgp.PathAttributeAsPath)
+ assert.Equal(t, expectedAsPath, pathAspath)
+
+ expectedNexthopAttr := pathAttributes[2]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP)
+ pathNexthop := attr.(*bgp.PathAttributeNextHop)
+ assert.Equal(t, expectedNexthopAttr, pathNexthop)
+
+ expectedMed := pathAttributes[3]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
+ pathMed := attr.(*bgp.PathAttributeMultiExitDisc)
+ assert.Equal(t, expectedMed, pathMed)
+
+ // check PathAttribute length
+ assert.Equal(t, len(pathAttributes2), len(path.GetPathAttrs()))
+
+ // check destination
+ expectedPrefix := "10.10.10.0/24"
+ assert.Equal(t, expectedPrefix, path.getPrefix())
+ // check nexthop
+ expectedNexthop := "192.168.100.1"
+ assert.Equal(t, expectedNexthop, path.GetNexthop().String())
+
+}
+
+func TestProcessBGPUpdate_7_select_low_routerid_path_ipv6(t *testing.T) {
+
+ tm := NewTableManager([]bgp.RouteFamily{bgp.RF_IPv6_UC})
+
+ origin1 := bgp.NewPathAttributeOrigin(0)
+ aspath1 := createAsPathAttribute([]uint32{65000, 65200})
+ mp_reach1 := createMpReach("2001::192:168:50:1",
+ []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(64, "2001:123:123:1::")})
+ med1 := bgp.NewPathAttributeMultiExitDisc(200)
+ localpref1 := bgp.NewPathAttributeLocalPref(100)
+
+ pathAttributes1 := []bgp.PathAttributeInterface{
+ mp_reach1, origin1, aspath1, med1, localpref1,
+ }
+
+ bgpMessage1 := bgp.NewBGPUpdateMessage(nil, pathAttributes1, nil)
+
+ origin2 := bgp.NewPathAttributeOrigin(0)
+ aspath2 := createAsPathAttribute([]uint32{65100, 65200})
+ mp_reach2 := createMpReach("2001::192:168:100:1",
+ []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(64, "2001:123:123:1::")})
+ med2 := bgp.NewPathAttributeMultiExitDisc(200)
+ localpref2 := bgp.NewPathAttributeLocalPref(100)
+
+ pathAttributes2 := []bgp.PathAttributeInterface{
+ mp_reach2, origin2, aspath2, med2, localpref2,
+ }
+
+ bgpMessage2 := bgp.NewBGPUpdateMessage(nil, pathAttributes2, nil)
+
+ peer1 := peerR1()
+ pList, err := tm.ProcessUpdate(peer1, bgpMessage1)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ peer3 := peerR3()
+ pList, err = tm.ProcessUpdate(peer3, bgpMessage2)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ // check type
+ path := pList[0]
+ assert.Equal(t, path.GetRouteFamily(), bgp.RF_IPv6_UC)
+
+ // check PathAttribute
+ pathAttributes := bgpMessage2.Body.(*bgp.BGPUpdate).PathAttributes
+
+ expectedNexthopAttr := pathAttributes[0]
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI)
+ pathNexthop := attr.(*bgp.PathAttributeMpReachNLRI)
+ assert.Equal(t, expectedNexthopAttr, pathNexthop)
+
+ expectedOrigin := pathAttributes[1]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN)
+ pathOrigin := attr.(*bgp.PathAttributeOrigin)
+ assert.Equal(t, expectedOrigin, pathOrigin)
+
+ expectedAsPath := pathAttributes[2]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
+ pathAspath := attr.(*bgp.PathAttributeAsPath)
+ assert.Equal(t, expectedAsPath, pathAspath)
+
+ expectedMed := pathAttributes[3]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
+ pathMed := attr.(*bgp.PathAttributeMultiExitDisc)
+ assert.Equal(t, expectedMed, pathMed)
+
+ // check PathAttribute length
+ assert.Equal(t, 5, len(path.GetPathAttrs()))
+
+ // check destination
+ expectedPrefix := "2001:123:123:1::/64"
+ assert.Equal(t, expectedPrefix, path.getPrefix())
+ // check nexthop
+ expectedNexthop := "2001::192:168:100:1"
+ assert.Equal(t, expectedNexthop, path.GetNexthop().String())
+
+}
+
+// test: withdraw and mpunreach path
+func TestProcessBGPUpdate_8_withdraw_path_ipv4(t *testing.T) {
+
+ tm := NewTableManager([]bgp.RouteFamily{bgp.RF_IPv4_UC})
+
+ // path1
+ origin1 := bgp.NewPathAttributeOrigin(0)
+ aspath1 := createAsPathAttribute([]uint32{65000})
+ nexthop1 := bgp.NewPathAttributeNextHop("192.168.50.1")
+ med1 := bgp.NewPathAttributeMultiExitDisc(200)
+ localpref1 := bgp.NewPathAttributeLocalPref(100)
+
+ pathAttributes1 := []bgp.PathAttributeInterface{
+ origin1, aspath1, nexthop1, med1, localpref1,
+ }
+ nlri1 := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.10.0")}
+ bgpMessage1 := bgp.NewBGPUpdateMessage(nil, pathAttributes1, nlri1)
+
+ // path 2
+ origin2 := bgp.NewPathAttributeOrigin(0)
+ aspath2 := createAsPathAttribute([]uint32{65100, 65000})
+ nexthop2 := bgp.NewPathAttributeNextHop("192.168.100.1")
+ med2 := bgp.NewPathAttributeMultiExitDisc(200)
+ localpref2 := bgp.NewPathAttributeLocalPref(200)
+
+ pathAttributes2 := []bgp.PathAttributeInterface{
+ origin2, aspath2, nexthop2, med2, localpref2,
+ }
+ nlri2 := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.10.0")}
+ bgpMessage2 := bgp.NewBGPUpdateMessage(nil, pathAttributes2, nlri2)
+
+ peer1 := peerR1()
+ pList, err := tm.ProcessUpdate(peer1, bgpMessage1)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ peer2 := peerR2()
+ pList, err = tm.ProcessUpdate(peer2, bgpMessage2)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ // check type
+ path := pList[0]
+ assert.Equal(t, path.GetRouteFamily(), bgp.RF_IPv4_UC)
+
+ // check PathAttribute
+ checkPattr := func(expected *bgp.BGPMessage, actual *Path) {
+ pathAttributes := expected.Body.(*bgp.BGPUpdate).PathAttributes
+ expectedOrigin := pathAttributes[0]
+ attr := actual.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN)
+ pathOrigin := attr.(*bgp.PathAttributeOrigin)
+ assert.Equal(t, expectedOrigin, pathOrigin)
+
+ expectedAsPath := pathAttributes[1]
+ attr = actual.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
+ pathAspath := attr.(*bgp.PathAttributeAsPath)
+ assert.Equal(t, expectedAsPath, pathAspath)
+
+ expectedNexthopAttr := pathAttributes[2]
+ attr = actual.getPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP)
+ pathNexthop := attr.(*bgp.PathAttributeNextHop)
+ assert.Equal(t, expectedNexthopAttr, pathNexthop)
+
+ expectedMed := pathAttributes[3]
+ attr = actual.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
+ pathMed := attr.(*bgp.PathAttributeMultiExitDisc)
+ assert.Equal(t, expectedMed, pathMed)
+
+ // check PathAttribute length
+ assert.Equal(t, len(pathAttributes), len(path.GetPathAttrs()))
+ }
+ checkPattr(bgpMessage2, path)
+ // check destination
+ expectedPrefix := "10.10.10.0/24"
+ assert.Equal(t, expectedPrefix, path.getPrefix())
+ // check nexthop
+ expectedNexthop := "192.168.100.1"
+ assert.Equal(t, expectedNexthop, path.GetNexthop().String())
+
+ //withdraw path
+ w1 := bgp.NewIPAddrPrefix(24, "10.10.10.0")
+ w := []*bgp.IPAddrPrefix{w1}
+ bgpMessage3 := bgp.NewBGPUpdateMessage(w, nil, nil)
+
+ pList, err = tm.ProcessUpdate(peer2, bgpMessage3)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ path = pList[0]
+ assert.Equal(t, path.GetRouteFamily(), bgp.RF_IPv4_UC)
+
+ checkPattr(bgpMessage1, path)
+ // check destination
+ expectedPrefix = "10.10.10.0/24"
+ assert.Equal(t, expectedPrefix, path.getPrefix())
+ // check nexthop
+ expectedNexthop = "192.168.50.1"
+ assert.Equal(t, expectedNexthop, path.GetNexthop().String())
+}
+
+// TODO MP_UNREACH
+func TestProcessBGPUpdate_8_mpunreach_path_ipv6(t *testing.T) {
+
+ tm := NewTableManager([]bgp.RouteFamily{bgp.RF_IPv6_UC})
+
+ origin1 := bgp.NewPathAttributeOrigin(0)
+ aspath1 := createAsPathAttribute([]uint32{65000})
+ mp_reach1 := createMpReach("2001::192:168:50:1",
+ []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(64, "2001:123:123:1::")})
+ med1 := bgp.NewPathAttributeMultiExitDisc(200)
+ localpref1 := bgp.NewPathAttributeLocalPref(100)
+
+ pathAttributes1 := []bgp.PathAttributeInterface{
+ mp_reach1, origin1, aspath1, med1, localpref1,
+ }
+
+ bgpMessage1 := bgp.NewBGPUpdateMessage(nil, pathAttributes1, nil)
+
+ origin2 := bgp.NewPathAttributeOrigin(0)
+ aspath2 := createAsPathAttribute([]uint32{65100, 65000})
+ mp_reach2 := createMpReach("2001::192:168:100:1",
+ []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(64, "2001:123:123:1::")})
+ med2 := bgp.NewPathAttributeMultiExitDisc(200)
+ localpref2 := bgp.NewPathAttributeLocalPref(200)
+
+ pathAttributes2 := []bgp.PathAttributeInterface{
+ mp_reach2, origin2, aspath2, med2, localpref2,
+ }
+
+ bgpMessage2 := bgp.NewBGPUpdateMessage(nil, pathAttributes2, nil)
+
+ peer1 := peerR1()
+ pList, err := tm.ProcessUpdate(peer1, bgpMessage1)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ peer2 := peerR2()
+ pList, err = tm.ProcessUpdate(peer2, bgpMessage2)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ // check type
+ path := pList[0]
+ assert.Equal(t, path.GetRouteFamily(), bgp.RF_IPv6_UC)
+
+ // check PathAttribute
+ pathAttributes := bgpMessage2.Body.(*bgp.BGPUpdate).PathAttributes
+
+ expectedNexthopAttr := pathAttributes[0]
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI)
+ pathNexthop := attr.(*bgp.PathAttributeMpReachNLRI)
+ assert.Equal(t, expectedNexthopAttr, pathNexthop)
+
+ expectedOrigin := pathAttributes[1]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN)
+ pathOrigin := attr.(*bgp.PathAttributeOrigin)
+ assert.Equal(t, expectedOrigin, pathOrigin)
+
+ expectedAsPath := pathAttributes[2]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
+ pathAspath := attr.(*bgp.PathAttributeAsPath)
+ assert.Equal(t, expectedAsPath, pathAspath)
+
+ expectedMed := pathAttributes[3]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
+ pathMed := attr.(*bgp.PathAttributeMultiExitDisc)
+ assert.Equal(t, expectedMed, pathMed)
+
+ // check PathAttribute
+ checkPattr := func(expected *bgp.BGPMessage, actual *Path) {
+ pathAttributes := expected.Body.(*bgp.BGPUpdate).PathAttributes
+
+ expectedNexthopAttr := pathAttributes[0]
+ attr = actual.getPathAttr(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI)
+ pathNexthop := attr.(*bgp.PathAttributeMpReachNLRI)
+ assert.Equal(t, expectedNexthopAttr, pathNexthop)
+
+ expectedOrigin := pathAttributes[1]
+ attr = actual.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN)
+ pathOrigin := attr.(*bgp.PathAttributeOrigin)
+ assert.Equal(t, expectedOrigin, pathOrigin)
+
+ expectedAsPath := pathAttributes[2]
+ attr = actual.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
+ pathAspath := attr.(*bgp.PathAttributeAsPath)
+ assert.Equal(t, expectedAsPath, pathAspath)
+
+ expectedMed := pathAttributes[3]
+ attr = actual.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
+ pathMed := attr.(*bgp.PathAttributeMultiExitDisc)
+ assert.Equal(t, expectedMed, pathMed)
+ // check PathAttribute length
+ assert.Equal(t, len(pathAttributes), len(path.GetPathAttrs()))
+ }
+
+ checkPattr(bgpMessage2, path)
+
+ // check destination
+ expectedPrefix := "2001:123:123:1::/64"
+ assert.Equal(t, expectedPrefix, path.getPrefix())
+ // check nexthop
+ expectedNexthop := "2001::192:168:100:1"
+ assert.Equal(t, expectedNexthop, path.GetNexthop().String())
+
+ //mpunreach path
+ mp_unreach := createMpUNReach("2001:123:123:1::", 64)
+ bgpMessage3 := bgp.NewBGPUpdateMessage(nil, []bgp.PathAttributeInterface{mp_unreach}, nil)
+
+ pList, err = tm.ProcessUpdate(peer2, bgpMessage3)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ path = pList[0]
+ assert.Equal(t, path.GetRouteFamily(), bgp.RF_IPv6_UC)
+
+ checkPattr(bgpMessage1, path)
+ // check destination
+ expectedPrefix = "2001:123:123:1::/64"
+ assert.Equal(t, expectedPrefix, path.getPrefix())
+ // check nexthop
+ expectedNexthop = "2001::192:168:50:1"
+ assert.Equal(t, expectedNexthop, path.GetNexthop().String())
+
+}
+
+// handle bestpath lost
+func TestProcessBGPUpdate_bestpath_lost_ipv4(t *testing.T) {
+
+ tm := NewTableManager([]bgp.RouteFamily{bgp.RF_IPv4_UC})
+
+ // path1
+ origin1 := bgp.NewPathAttributeOrigin(0)
+ aspath1 := createAsPathAttribute([]uint32{65000})
+ nexthop1 := bgp.NewPathAttributeNextHop("192.168.50.1")
+ med1 := bgp.NewPathAttributeMultiExitDisc(200)
+ localpref1 := bgp.NewPathAttributeLocalPref(100)
+
+ pathAttributes1 := []bgp.PathAttributeInterface{
+ origin1, aspath1, nexthop1, med1, localpref1,
+ }
+ nlri1 := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.10.0")}
+ bgpMessage1 := bgp.NewBGPUpdateMessage(nil, pathAttributes1, nlri1)
+
+ // path 1 withdraw
+ w1 := bgp.NewIPAddrPrefix(24, "10.10.10.0")
+ w := []*bgp.IPAddrPrefix{w1}
+ bgpMessage1_w := bgp.NewBGPUpdateMessage(w, nil, nil)
+
+ peer1 := peerR1()
+ pList, err := tm.ProcessUpdate(peer1, bgpMessage1)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ pList, err = tm.ProcessUpdate(peer1, bgpMessage1_w)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, true)
+ assert.NoError(t, err)
+
+ // check old best path
+ path := pList[0]
+ assert.Equal(t, path.GetRouteFamily(), bgp.RF_IPv4_UC)
+
+ // check PathAttribute
+ checkPattr := func(expected *bgp.BGPMessage, actual *Path) {
+ pathAttributes := expected.Body.(*bgp.BGPUpdate).PathAttributes
+ expectedOrigin := pathAttributes[0]
+ attr := actual.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN)
+ pathOrigin := attr.(*bgp.PathAttributeOrigin)
+ assert.Equal(t, expectedOrigin, pathOrigin)
+
+ expectedAsPath := pathAttributes[1]
+ attr = actual.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
+ pathAspath := attr.(*bgp.PathAttributeAsPath)
+ assert.Equal(t, expectedAsPath, pathAspath)
+
+ expectedNexthopAttr := pathAttributes[2]
+ attr = actual.getPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP)
+ pathNexthop := attr.(*bgp.PathAttributeNextHop)
+ assert.Equal(t, expectedNexthopAttr, pathNexthop)
+
+ expectedMed := pathAttributes[3]
+ attr = actual.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
+ pathMed := attr.(*bgp.PathAttributeMultiExitDisc)
+ assert.Equal(t, expectedMed, pathMed)
+
+ // check PathAttribute length
+ assert.Equal(t, len(pathAttributes), len(path.GetPathAttrs()))
+ }
+
+ checkPattr(bgpMessage1, path)
+ // check destination
+ expectedPrefix := "10.10.10.0/24"
+ assert.Equal(t, expectedPrefix, path.getPrefix())
+}
+
+func TestProcessBGPUpdate_bestpath_lost_ipv6(t *testing.T) {
+
+ tm := NewTableManager([]bgp.RouteFamily{bgp.RF_IPv6_UC})
+
+ origin1 := bgp.NewPathAttributeOrigin(0)
+ aspath1 := createAsPathAttribute([]uint32{65000})
+ mp_reach1 := createMpReach("2001::192:168:50:1",
+ []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(64, "2001:123:123:1::")})
+ med1 := bgp.NewPathAttributeMultiExitDisc(200)
+ localpref1 := bgp.NewPathAttributeLocalPref(100)
+
+ pathAttributes1 := []bgp.PathAttributeInterface{
+ mp_reach1, origin1, aspath1, med1, localpref1,
+ }
+
+ bgpMessage1 := bgp.NewBGPUpdateMessage(nil, pathAttributes1, nil)
+
+ peer1 := peerR1()
+ pList, err := tm.ProcessUpdate(peer1, bgpMessage1)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ // path1 mpunreach
+ mp_unreach := createMpUNReach("2001:123:123:1::", 64)
+ bgpMessage1_w := bgp.NewBGPUpdateMessage(nil, []bgp.PathAttributeInterface{mp_unreach}, nil)
+
+ pList, err = tm.ProcessUpdate(peer1, bgpMessage1_w)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, true)
+ assert.NoError(t, err)
+
+ // check old best path
+ path := pList[0]
+ assert.Equal(t, path.GetRouteFamily(), bgp.RF_IPv6_UC)
+
+ // check PathAttribute
+ checkPattr := func(expected *bgp.BGPMessage, actual *Path) {
+ pathAttributes := expected.Body.(*bgp.BGPUpdate).PathAttributes
+
+ expectedNexthopAttr := pathAttributes[0]
+ attr := actual.getPathAttr(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI)
+ pathNexthop := attr.(*bgp.PathAttributeMpReachNLRI)
+ assert.Equal(t, expectedNexthopAttr, pathNexthop)
+
+ expectedOrigin := pathAttributes[1]
+ attr = actual.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN)
+ pathOrigin := attr.(*bgp.PathAttributeOrigin)
+ assert.Equal(t, expectedOrigin, pathOrigin)
+
+ expectedAsPath := pathAttributes[2]
+ attr = actual.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
+ pathAspath := attr.(*bgp.PathAttributeAsPath)
+ assert.Equal(t, expectedAsPath, pathAspath)
+
+ expectedMed := pathAttributes[3]
+ attr = actual.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
+ pathMed := attr.(*bgp.PathAttributeMultiExitDisc)
+ assert.Equal(t, expectedMed, pathMed)
+ // check PathAttribute length
+ assert.Equal(t, len(pathAttributes), len(path.GetPathAttrs()))
+ }
+
+ checkPattr(bgpMessage1, path)
+
+ // check destination
+ expectedPrefix := "2001:123:123:1::/64"
+ assert.Equal(t, expectedPrefix, path.getPrefix())
+}
+
+// test: implicit withdrawal case
+func TestProcessBGPUpdate_implicit_withdrwal_ipv4(t *testing.T) {
+
+ tm := NewTableManager([]bgp.RouteFamily{bgp.RF_IPv4_UC})
+
+ // path1
+ origin1 := bgp.NewPathAttributeOrigin(0)
+ aspath1 := createAsPathAttribute([]uint32{65000, 65100, 65200})
+ nexthop1 := bgp.NewPathAttributeNextHop("192.168.50.1")
+ med1 := bgp.NewPathAttributeMultiExitDisc(200)
+ localpref1 := bgp.NewPathAttributeLocalPref(100)
+
+ pathAttributes1 := []bgp.PathAttributeInterface{
+ origin1, aspath1, nexthop1, med1, localpref1,
+ }
+ nlri1 := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.10.0")}
+ bgpMessage1 := bgp.NewBGPUpdateMessage(nil, pathAttributes1, nlri1)
+
+ // path 1 from same peer but short AS_PATH
+ origin2 := bgp.NewPathAttributeOrigin(0)
+ aspath2 := createAsPathAttribute([]uint32{65000, 65100})
+ nexthop2 := bgp.NewPathAttributeNextHop("192.168.50.1")
+ med2 := bgp.NewPathAttributeMultiExitDisc(200)
+ localpref2 := bgp.NewPathAttributeLocalPref(100)
+
+ pathAttributes2 := []bgp.PathAttributeInterface{
+ origin2, aspath2, nexthop2, med2, localpref2,
+ }
+ nlri2 := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.10.0")}
+ bgpMessage2 := bgp.NewBGPUpdateMessage(nil, pathAttributes2, nlri2)
+
+ peer1 := peerR1()
+ pList, err := tm.ProcessUpdate(peer1, bgpMessage1)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ pList, err = tm.ProcessUpdate(peer1, bgpMessage2)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ // check type
+ path := pList[0]
+ assert.Equal(t, path.GetRouteFamily(), bgp.RF_IPv4_UC)
+
+ // check PathAttribute
+ checkPattr := func(expected *bgp.BGPMessage, actual *Path) {
+ pathAttributes := expected.Body.(*bgp.BGPUpdate).PathAttributes
+ expectedOrigin := pathAttributes[0]
+ attr := actual.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN)
+ pathOrigin := attr.(*bgp.PathAttributeOrigin)
+ assert.Equal(t, expectedOrigin, pathOrigin)
+
+ expectedAsPath := pathAttributes[1]
+ attr = actual.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
+ pathAspath := attr.(*bgp.PathAttributeAsPath)
+ assert.Equal(t, expectedAsPath, pathAspath)
+
+ expectedNexthopAttr := pathAttributes[2]
+ attr = actual.getPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP)
+ pathNexthop := attr.(*bgp.PathAttributeNextHop)
+ assert.Equal(t, expectedNexthopAttr, pathNexthop)
+
+ expectedMed := pathAttributes[3]
+ attr = actual.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
+ pathMed := attr.(*bgp.PathAttributeMultiExitDisc)
+ assert.Equal(t, expectedMed, pathMed)
+
+ // check PathAttribute length
+ assert.Equal(t, len(pathAttributes), len(path.GetPathAttrs()))
+ }
+ checkPattr(bgpMessage2, path)
+ // check destination
+ expectedPrefix := "10.10.10.0/24"
+ assert.Equal(t, expectedPrefix, path.getPrefix())
+ // check nexthop
+ expectedNexthop := "192.168.50.1"
+ assert.Equal(t, expectedNexthop, path.GetNexthop().String())
+
+}
+
+func TestProcessBGPUpdate_implicit_withdrwal_ipv6(t *testing.T) {
+
+ tm := NewTableManager([]bgp.RouteFamily{bgp.RF_IPv6_UC})
+
+ origin1 := bgp.NewPathAttributeOrigin(0)
+ aspath1 := createAsPathAttribute([]uint32{65000, 65100, 65200})
+ mp_reach1 := createMpReach("2001::192:168:50:1",
+ []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(64, "2001:123:123:1::")})
+ med1 := bgp.NewPathAttributeMultiExitDisc(200)
+ localpref1 := bgp.NewPathAttributeLocalPref(200)
+
+ pathAttributes1 := []bgp.PathAttributeInterface{
+ mp_reach1, origin1, aspath1, med1, localpref1,
+ }
+
+ bgpMessage1 := bgp.NewBGPUpdateMessage(nil, pathAttributes1, nil)
+
+ origin2 := bgp.NewPathAttributeOrigin(0)
+ aspath2 := createAsPathAttribute([]uint32{65000, 65100})
+ mp_reach2 := createMpReach("2001::192:168:50:1",
+ []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(64, "2001:123:123:1::")})
+ med2 := bgp.NewPathAttributeMultiExitDisc(200)
+ localpref2 := bgp.NewPathAttributeLocalPref(200)
+
+ pathAttributes2 := []bgp.PathAttributeInterface{
+ mp_reach2, origin2, aspath2, med2, localpref2,
+ }
+
+ bgpMessage2 := bgp.NewBGPUpdateMessage(nil, pathAttributes2, nil)
+
+ peer1 := peerR1()
+ pList, err := tm.ProcessUpdate(peer1, bgpMessage1)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ pList, err = tm.ProcessUpdate(peer1, bgpMessage2)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ // check type
+ path := pList[0]
+ assert.Equal(t, path.GetRouteFamily(), bgp.RF_IPv6_UC)
+
+ // check PathAttribute
+ pathAttributes := bgpMessage2.Body.(*bgp.BGPUpdate).PathAttributes
+
+ expectedNexthopAttr := pathAttributes[0]
+ attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI)
+ pathNexthop := attr.(*bgp.PathAttributeMpReachNLRI)
+ assert.Equal(t, expectedNexthopAttr, pathNexthop)
+
+ expectedOrigin := pathAttributes[1]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN)
+ pathOrigin := attr.(*bgp.PathAttributeOrigin)
+ assert.Equal(t, expectedOrigin, pathOrigin)
+
+ expectedAsPath := pathAttributes[2]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
+ pathAspath := attr.(*bgp.PathAttributeAsPath)
+ assert.Equal(t, expectedAsPath, pathAspath)
+
+ expectedMed := pathAttributes[3]
+ attr = path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
+ pathMed := attr.(*bgp.PathAttributeMultiExitDisc)
+ assert.Equal(t, expectedMed, pathMed)
+
+ // check PathAttribute
+ checkPattr := func(expected *bgp.BGPMessage, actual *Path) {
+ pathAttributes := expected.Body.(*bgp.BGPUpdate).PathAttributes
+
+ expectedNexthopAttr := pathAttributes[0]
+ attr := actual.getPathAttr(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI)
+ pathNexthop := attr.(*bgp.PathAttributeMpReachNLRI)
+ assert.Equal(t, expectedNexthopAttr, pathNexthop)
+
+ expectedOrigin := pathAttributes[1]
+ attr = actual.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN)
+ pathOrigin := attr.(*bgp.PathAttributeOrigin)
+ assert.Equal(t, expectedOrigin, pathOrigin)
+
+ expectedAsPath := pathAttributes[2]
+ attr = actual.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
+ pathAspath := attr.(*bgp.PathAttributeAsPath)
+ assert.Equal(t, expectedAsPath, pathAspath)
+
+ expectedMed := pathAttributes[3]
+ attr = actual.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
+ pathMed := attr.(*bgp.PathAttributeMultiExitDisc)
+ assert.Equal(t, expectedMed, pathMed)
+ // check PathAttribute length
+ assert.Equal(t, len(pathAttributes), len(path.GetPathAttrs()))
+ }
+
+ checkPattr(bgpMessage2, path)
+
+ // check destination
+ expectedPrefix := "2001:123:123:1::/64"
+ assert.Equal(t, expectedPrefix, path.getPrefix())
+ // check nexthop
+ expectedNexthop := "2001::192:168:50:1"
+ assert.Equal(t, expectedNexthop, path.GetNexthop().String())
+
+}
+
+// check multiple paths
+func TestProcessBGPUpdate_multiple_nlri_ipv4(t *testing.T) {
+
+ tm := NewTableManager([]bgp.RouteFamily{bgp.RF_IPv4_UC})
+
+ createPathAttr := func(aspaths []uint32, nh string) []bgp.PathAttributeInterface {
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspath := createAsPathAttribute(aspaths)
+ nexthop := bgp.NewPathAttributeNextHop(nh)
+ med := bgp.NewPathAttributeMultiExitDisc(200)
+ localpref := bgp.NewPathAttributeLocalPref(100)
+ pathAttr := []bgp.PathAttributeInterface{
+ origin, aspath, nexthop, med, localpref,
+ }
+ return pathAttr
+ }
+
+ // check PathAttribute
+ checkPattr := func(expected *bgp.BGPMessage, actual *Path) {
+ pathAttributes := expected.Body.(*bgp.BGPUpdate).PathAttributes
+ expectedOrigin := pathAttributes[0]
+ attr := actual.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN)
+ pathOrigin := attr.(*bgp.PathAttributeOrigin)
+ assert.Equal(t, expectedOrigin, pathOrigin)
+
+ expectedAsPath := pathAttributes[1]
+ attr = actual.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
+ pathAspath := attr.(*bgp.PathAttributeAsPath)
+ assert.Equal(t, expectedAsPath, pathAspath)
+
+ expectedNexthopAttr := pathAttributes[2]
+ attr = actual.getPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP)
+ pathNexthop := attr.(*bgp.PathAttributeNextHop)
+ assert.Equal(t, expectedNexthopAttr, pathNexthop)
+
+ expectedMed := pathAttributes[3]
+ attr = actual.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
+ pathMed := attr.(*bgp.PathAttributeMultiExitDisc)
+ assert.Equal(t, expectedMed, pathMed)
+
+ // check PathAttribute length
+ assert.Equal(t, len(pathAttributes), len(actual.GetPathAttrs()))
+ }
+
+ checkBestPathResult := func(rf bgp.RouteFamily, prefix, nexthop string, p *Path, m *bgp.BGPMessage) {
+ assert.Equal(t, p.GetRouteFamily(), rf)
+ checkPattr(m, p)
+ // check destination
+ assert.Equal(t, prefix, p.getPrefix())
+ // check nexthop
+ assert.Equal(t, nexthop, p.GetNexthop().String())
+ }
+
+ // path1
+ pathAttributes1 := createPathAttr([]uint32{65000, 65100, 65200}, "192.168.50.1")
+ nlri1 := []*bgp.IPAddrPrefix{
+ bgp.NewIPAddrPrefix(24, "10.10.10.0"),
+ bgp.NewIPAddrPrefix(24, "20.20.20.0"),
+ bgp.NewIPAddrPrefix(24, "30.30.30.0"),
+ bgp.NewIPAddrPrefix(24, "40.40.40.0"),
+ bgp.NewIPAddrPrefix(24, "50.50.50.0")}
+ bgpMessage1 := bgp.NewBGPUpdateMessage(nil, pathAttributes1, nlri1)
+
+ // path2
+ pathAttributes2 := createPathAttr([]uint32{65000, 65100, 65300}, "192.168.50.1")
+ nlri2 := []*bgp.IPAddrPrefix{
+ bgp.NewIPAddrPrefix(24, "11.11.11.0"),
+ bgp.NewIPAddrPrefix(24, "22.22.22.0"),
+ bgp.NewIPAddrPrefix(24, "33.33.33.0"),
+ bgp.NewIPAddrPrefix(24, "44.44.44.0"),
+ bgp.NewIPAddrPrefix(24, "55.55.55.0")}
+ bgpMessage2 := bgp.NewBGPUpdateMessage(nil, pathAttributes2, nlri2)
+
+ // path3
+ pathAttributes3 := createPathAttr([]uint32{65000, 65100, 65400}, "192.168.50.1")
+ nlri3 := []*bgp.IPAddrPrefix{
+ bgp.NewIPAddrPrefix(24, "77.77.77.0"),
+ bgp.NewIPAddrPrefix(24, "88.88.88.0"),
+ }
+ bgpMessage3 := bgp.NewBGPUpdateMessage(nil, pathAttributes3, nlri3)
+
+ // path4
+ pathAttributes4 := createPathAttr([]uint32{65000, 65100, 65500}, "192.168.50.1")
+ nlri4 := []*bgp.IPAddrPrefix{
+ bgp.NewIPAddrPrefix(24, "99.99.99.0"),
+ }
+ bgpMessage4 := bgp.NewBGPUpdateMessage(nil, pathAttributes4, nlri4)
+
+ peer1 := peerR1()
+ pList, err := tm.ProcessUpdate(peer1, bgpMessage1)
+ assert.Equal(t, 5, len(pList))
+ for _, p := range pList {
+ assert.Equal(t, p.IsWithdraw, false)
+ }
+ assert.NoError(t, err)
+
+ checkBestPathResult(bgp.RF_IPv4_UC, "10.10.10.0/24", "192.168.50.1", pList[0], bgpMessage1)
+ checkBestPathResult(bgp.RF_IPv4_UC, "20.20.20.0/24", "192.168.50.1", pList[1], bgpMessage1)
+ checkBestPathResult(bgp.RF_IPv4_UC, "30.30.30.0/24", "192.168.50.1", pList[2], bgpMessage1)
+ checkBestPathResult(bgp.RF_IPv4_UC, "40.40.40.0/24", "192.168.50.1", pList[3], bgpMessage1)
+ checkBestPathResult(bgp.RF_IPv4_UC, "50.50.50.0/24", "192.168.50.1", pList[4], bgpMessage1)
+
+ pList, err = tm.ProcessUpdate(peer1, bgpMessage2)
+ assert.Equal(t, 5, len(pList))
+ for _, p := range pList {
+ assert.Equal(t, p.IsWithdraw, false)
+ }
+ assert.NoError(t, err)
+
+ checkBestPathResult(bgp.RF_IPv4_UC, "11.11.11.0/24", "192.168.50.1", pList[0], bgpMessage2)
+ checkBestPathResult(bgp.RF_IPv4_UC, "22.22.22.0/24", "192.168.50.1", pList[1], bgpMessage2)
+ checkBestPathResult(bgp.RF_IPv4_UC, "33.33.33.0/24", "192.168.50.1", pList[2], bgpMessage2)
+ checkBestPathResult(bgp.RF_IPv4_UC, "44.44.44.0/24", "192.168.50.1", pList[3], bgpMessage2)
+ checkBestPathResult(bgp.RF_IPv4_UC, "55.55.55.0/24", "192.168.50.1", pList[4], bgpMessage2)
+
+ pList, err = tm.ProcessUpdate(peer1, bgpMessage3)
+ assert.Equal(t, 2, len(pList))
+ for _, p := range pList {
+ assert.Equal(t, p.IsWithdraw, false)
+ }
+ assert.NoError(t, err)
+
+ pList, err = tm.ProcessUpdate(peer1, bgpMessage4)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ // check table
+ table := tm.Tables[bgp.RF_IPv4_UC]
+ assert.Equal(t, 13, len(table.GetDestinations()))
+
+}
+
+// check multiple paths
+func TestProcessBGPUpdate_multiple_nlri_ipv6(t *testing.T) {
+
+ tm := NewTableManager([]bgp.RouteFamily{bgp.RF_IPv6_UC})
+
+ createPathAttr := func(aspaths []uint32) []bgp.PathAttributeInterface {
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspath := createAsPathAttribute(aspaths)
+ med := bgp.NewPathAttributeMultiExitDisc(100)
+ localpref := bgp.NewPathAttributeLocalPref(100)
+ pathAttr := []bgp.PathAttributeInterface{
+ origin, aspath, med, localpref,
+ }
+ return pathAttr
+ }
+
+ // check PathAttribute
+ checkPattr := func(expected *bgp.BGPMessage, actual *Path) {
+ pathAttributes := expected.Body.(*bgp.BGPUpdate).PathAttributes
+ pathNexthop := pathAttributes[4]
+ attr := actual.getPathAttr(bgp.BGP_ATTR_TYPE_MP_REACH_NLRI)
+ expectedNexthopAttr := attr.(*bgp.PathAttributeMpReachNLRI)
+ assert.Equal(t, expectedNexthopAttr, pathNexthop)
+
+ expectedOrigin := pathAttributes[0]
+ attr = actual.getPathAttr(bgp.BGP_ATTR_TYPE_ORIGIN)
+ pathOrigin := attr.(*bgp.PathAttributeOrigin)
+ assert.Equal(t, expectedOrigin, pathOrigin)
+
+ expectedAsPath := pathAttributes[1]
+ attr = actual.getPathAttr(bgp.BGP_ATTR_TYPE_AS_PATH)
+ pathAspath := attr.(*bgp.PathAttributeAsPath)
+ assert.Equal(t, expectedAsPath, pathAspath)
+
+ expectedMed := pathAttributes[2]
+ attr = actual.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
+ pathMed := attr.(*bgp.PathAttributeMultiExitDisc)
+ assert.Equal(t, expectedMed, pathMed)
+
+ expectedLocalpref := pathAttributes[3]
+ attr = actual.getPathAttr(bgp.BGP_ATTR_TYPE_LOCAL_PREF)
+ localpref := attr.(*bgp.PathAttributeLocalPref)
+ assert.Equal(t, expectedLocalpref, localpref)
+
+ // check PathAttribute length
+ assert.Equal(t, len(pathAttributes), len(actual.GetPathAttrs()))
+
+ }
+
+ checkBestPathResult := func(rf bgp.RouteFamily, prefix, nexthop string, p *Path, m *bgp.BGPMessage) {
+ assert.Equal(t, p.GetRouteFamily(), rf)
+ checkPattr(m, p)
+ // check destination
+ assert.Equal(t, prefix, p.getPrefix())
+ // check nexthop
+ assert.Equal(t, nexthop, p.GetNexthop().String())
+ }
+
+ // path1
+ pathAttributes1 := createPathAttr([]uint32{65000, 65100, 65200})
+ mpreach1 := createMpReach("2001::192:168:50:1", []bgp.AddrPrefixInterface{
+ bgp.NewIPv6AddrPrefix(64, "2001:123:1210:11::"),
+ bgp.NewIPv6AddrPrefix(64, "2001:123:1220:11::"),
+ bgp.NewIPv6AddrPrefix(64, "2001:123:1230:11::"),
+ bgp.NewIPv6AddrPrefix(64, "2001:123:1240:11::"),
+ bgp.NewIPv6AddrPrefix(64, "2001:123:1250:11::"),
+ })
+ pathAttributes1 = append(pathAttributes1, mpreach1)
+ bgpMessage1 := bgp.NewBGPUpdateMessage(nil, pathAttributes1, nil)
+
+ // path2
+ pathAttributes2 := createPathAttr([]uint32{65000, 65100, 65300})
+ mpreach2 := createMpReach("2001::192:168:50:1", []bgp.AddrPrefixInterface{
+ bgp.NewIPv6AddrPrefix(64, "2001:123:1211:11::"),
+ bgp.NewIPv6AddrPrefix(64, "2001:123:1222:11::"),
+ bgp.NewIPv6AddrPrefix(64, "2001:123:1233:11::"),
+ bgp.NewIPv6AddrPrefix(64, "2001:123:1244:11::"),
+ bgp.NewIPv6AddrPrefix(64, "2001:123:1255:11::"),
+ })
+ pathAttributes2 = append(pathAttributes2, mpreach2)
+ bgpMessage2 := bgp.NewBGPUpdateMessage(nil, pathAttributes2, nil)
+
+ // path3
+ pathAttributes3 := createPathAttr([]uint32{65000, 65100, 65400})
+ mpreach3 := createMpReach("2001::192:168:50:1", []bgp.AddrPrefixInterface{
+ bgp.NewIPv6AddrPrefix(64, "2001:123:1277:11::"),
+ bgp.NewIPv6AddrPrefix(64, "2001:123:1288:11::"),
+ })
+ pathAttributes3 = append(pathAttributes3, mpreach3)
+ bgpMessage3 := bgp.NewBGPUpdateMessage(nil, pathAttributes3, nil)
+
+ // path4
+ pathAttributes4 := createPathAttr([]uint32{65000, 65100, 65500})
+ mpreach4 := createMpReach("2001::192:168:50:1", []bgp.AddrPrefixInterface{
+ bgp.NewIPv6AddrPrefix(64, "2001:123:1299:11::"),
+ })
+ pathAttributes4 = append(pathAttributes4, mpreach4)
+ bgpMessage4 := bgp.NewBGPUpdateMessage(nil, pathAttributes4, nil)
+
+ peer1 := peerR1()
+ pList, err := tm.ProcessUpdate(peer1, bgpMessage1)
+ assert.Equal(t, 5, len(pList))
+ for _, p := range pList {
+ assert.Equal(t, p.IsWithdraw, false)
+ }
+ assert.NoError(t, err)
+
+ checkBestPathResult(bgp.RF_IPv6_UC, "2001:123:1210:11::/64", "2001::192:168:50:1", pList[0], bgpMessage1)
+ checkBestPathResult(bgp.RF_IPv6_UC, "2001:123:1220:11::/64", "2001::192:168:50:1", pList[1], bgpMessage1)
+ checkBestPathResult(bgp.RF_IPv6_UC, "2001:123:1230:11::/64", "2001::192:168:50:1", pList[2], bgpMessage1)
+ checkBestPathResult(bgp.RF_IPv6_UC, "2001:123:1240:11::/64", "2001::192:168:50:1", pList[3], bgpMessage1)
+ checkBestPathResult(bgp.RF_IPv6_UC, "2001:123:1250:11::/64", "2001::192:168:50:1", pList[4], bgpMessage1)
+
+ pList, err = tm.ProcessUpdate(peer1, bgpMessage2)
+ assert.Equal(t, 5, len(pList))
+ for _, p := range pList {
+ assert.Equal(t, p.IsWithdraw, false)
+ }
+ assert.NoError(t, err)
+
+ checkBestPathResult(bgp.RF_IPv6_UC, "2001:123:1211:11::/64", "2001::192:168:50:1", pList[0], bgpMessage2)
+ checkBestPathResult(bgp.RF_IPv6_UC, "2001:123:1222:11::/64", "2001::192:168:50:1", pList[1], bgpMessage2)
+ checkBestPathResult(bgp.RF_IPv6_UC, "2001:123:1233:11::/64", "2001::192:168:50:1", pList[2], bgpMessage2)
+ checkBestPathResult(bgp.RF_IPv6_UC, "2001:123:1244:11::/64", "2001::192:168:50:1", pList[3], bgpMessage2)
+ checkBestPathResult(bgp.RF_IPv6_UC, "2001:123:1255:11::/64", "2001::192:168:50:1", pList[4], bgpMessage2)
+
+ pList, err = tm.ProcessUpdate(peer1, bgpMessage3)
+ assert.Equal(t, 2, len(pList))
+ for _, p := range pList {
+ assert.Equal(t, p.IsWithdraw, false)
+ }
+ assert.NoError(t, err)
+
+ pList, err = tm.ProcessUpdate(peer1, bgpMessage4)
+ assert.Equal(t, 1, len(pList))
+ assert.Equal(t, pList[0].IsWithdraw, false)
+ assert.NoError(t, err)
+
+ // check table
+ table := tm.Tables[bgp.RF_IPv6_UC]
+ assert.Equal(t, 13, len(table.GetDestinations()))
+
+}
+
+func TestProcessBGPUpdate_Timestamp(t *testing.T) {
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAs4PathParam(2, []uint32{65000})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("192.168.50.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+
+ pathAttributes := []bgp.PathAttributeInterface{
+ origin,
+ aspath,
+ nexthop,
+ med,
+ }
+
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.10.0")}
+
+ adjRib := NewAdjRib([]bgp.RouteFamily{bgp.RF_IPv4_UC, bgp.RF_IPv6_UC})
+ m1 := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ peer := peerR1()
+ pList1 := ProcessMessage(m1, peer, time.Now())
+ path1 := pList1[0]
+ t1 := path1.GetTimestamp()
+ adjRib.Update(pList1)
+
+ m2 := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ pList2 := ProcessMessage(m2, peer, time.Now())
+ //path2 := pList2[0].(*IPv4Path)
+ //t2 = path2.timestamp
+ adjRib.Update(pList2)
+
+ inList := adjRib.PathList([]bgp.RouteFamily{bgp.RF_IPv4_UC}, false)
+ assert.Equal(t, len(inList), 1)
+ assert.Equal(t, inList[0].GetTimestamp(), t1)
+
+ med2 := bgp.NewPathAttributeMultiExitDisc(1)
+ pathAttributes2 := []bgp.PathAttributeInterface{
+ origin,
+ aspath,
+ nexthop,
+ med2,
+ }
+
+ m3 := bgp.NewBGPUpdateMessage(nil, pathAttributes2, nlri)
+ pList3 := ProcessMessage(m3, peer, time.Now())
+ t3 := pList3[0].GetTimestamp()
+ adjRib.Update(pList3)
+
+ inList = adjRib.PathList([]bgp.RouteFamily{bgp.RF_IPv4_UC}, false)
+ assert.Equal(t, len(inList), 1)
+ assert.Equal(t, inList[0].GetTimestamp(), t3)
+}
+
+func update_fromR1() *bgp.BGPMessage {
+
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAs4PathParam(2, []uint32{65000})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("192.168.50.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+
+ pathAttributes := []bgp.PathAttributeInterface{
+ origin,
+ aspath,
+ nexthop,
+ med,
+ }
+
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.10.0")}
+
+ return bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+}
+
+func update_fromR1_ipv6() *bgp.BGPMessage {
+
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAs4PathParam(2, []uint32{65000})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+
+ mp_nlri := []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(64, "2001:123:123:1::")}
+ mp_reach := bgp.NewPathAttributeMpReachNLRI("2001::192:168:50:1", mp_nlri)
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+
+ pathAttributes := []bgp.PathAttributeInterface{
+ mp_reach,
+ origin,
+ aspath,
+ med,
+ }
+ return bgp.NewBGPUpdateMessage(nil, pathAttributes, nil)
+}
+
+func update_fromR2() *bgp.BGPMessage {
+
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAs4PathParam(2, []uint32{65100})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("192.168.100.1")
+ med := bgp.NewPathAttributeMultiExitDisc(100)
+
+ pathAttributes := []bgp.PathAttributeInterface{
+ origin,
+ aspath,
+ nexthop,
+ med,
+ }
+
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "20.20.20.0")}
+ return bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+}
+
+func update_fromR2_ipv6() *bgp.BGPMessage {
+
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspath := createAsPathAttribute([]uint32{65100})
+ mp_reach := createMpReach("2001::192:168:100:1",
+ []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(64, "2002:223:123:1::")})
+ med := bgp.NewPathAttributeMultiExitDisc(100)
+
+ pathAttributes := []bgp.PathAttributeInterface{
+ mp_reach,
+ origin,
+ aspath,
+ med,
+ }
+ return bgp.NewBGPUpdateMessage(nil, pathAttributes, nil)
+}
+
+func createAsPathAttribute(ases []uint32) *bgp.PathAttributeAsPath {
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAs4PathParam(2, ases)}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ return aspath
+}
+
+func createMpReach(nexthop string, prefix []bgp.AddrPrefixInterface) *bgp.PathAttributeMpReachNLRI {
+ mp_reach := bgp.NewPathAttributeMpReachNLRI(nexthop, prefix)
+ return mp_reach
+}
+
+func createMpUNReach(nlri string, len uint8) *bgp.PathAttributeMpUnreachNLRI {
+ mp_nlri := []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(len, nlri)}
+ mp_unreach := bgp.NewPathAttributeMpUnreachNLRI(mp_nlri)
+ return mp_unreach
+}
+
+func update_fromR2viaR1() *bgp.BGPMessage {
+
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAs4PathParam(2, []uint32{65000, 65100})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("192.168.50.1")
+
+ pathAttributes := []bgp.PathAttributeInterface{
+ origin,
+ aspath,
+ nexthop,
+ }
+
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "20.20.20.0")}
+ return bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+}
+
+func update_fromR2viaR1_ipv6() *bgp.BGPMessage {
+
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspath := createAsPathAttribute([]uint32{65000, 65100})
+ mp_reach := createMpReach("2001::192:168:50:1",
+ []bgp.AddrPrefixInterface{bgp.NewIPv6AddrPrefix(64, "2002:223:123:1::")})
+ med := bgp.NewPathAttributeMultiExitDisc(100)
+
+ pathAttributes := []bgp.PathAttributeInterface{
+ mp_reach,
+ origin,
+ aspath,
+ med,
+ }
+ return bgp.NewBGPUpdateMessage(nil, pathAttributes, nil)
+
+}
diff --git a/internal/pkg/table/table_test.go b/internal/pkg/table/table_test.go
new file mode 100644
index 00000000..1e91aa6b
--- /dev/null
+++ b/internal/pkg/table/table_test.go
@@ -0,0 +1,180 @@
+// Copyright (C) 2014 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package table
+
+import (
+ "testing"
+ "time"
+
+ "github.com/osrg/gobgp/pkg/packet/bgp"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestTableDeleteDestByNlri(t *testing.T) {
+ peerT := TableCreatePeer()
+ pathT := TableCreatePath(peerT)
+ ipv4t := NewTable(bgp.RF_IPv4_UC)
+ for _, path := range pathT {
+ dest := NewDestination(path.GetNlri(), 0)
+ ipv4t.setDestination(dest)
+ }
+ gdest := ipv4t.GetDestination(pathT[0].GetNlri())
+ rdest := ipv4t.deleteDestByNlri(pathT[0].GetNlri())
+ assert.Equal(t, rdest, gdest)
+}
+
+func TestTableDeleteDest(t *testing.T) {
+ peerT := TableCreatePeer()
+ pathT := TableCreatePath(peerT)
+ ipv4t := NewTable(bgp.RF_IPv4_UC)
+ for _, path := range pathT {
+ dest := NewDestination(path.GetNlri(), 0)
+ ipv4t.setDestination(dest)
+ }
+ dest := NewDestination(pathT[0].GetNlri(), 0)
+ ipv4t.setDestination(dest)
+ ipv4t.deleteDest(dest)
+ gdest := ipv4t.GetDestination(pathT[0].GetNlri())
+ assert.Nil(t, gdest)
+}
+
+func TestTableGetRouteFamily(t *testing.T) {
+ ipv4t := NewTable(bgp.RF_IPv4_UC)
+ rf := ipv4t.GetRoutefamily()
+ assert.Equal(t, rf, bgp.RF_IPv4_UC)
+}
+
+func TestTableSetDestinations(t *testing.T) {
+ peerT := TableCreatePeer()
+ pathT := TableCreatePath(peerT)
+ ipv4t := NewTable(bgp.RF_IPv4_UC)
+ destinations := make(map[string]*Destination)
+ for _, path := range pathT {
+ tableKey := ipv4t.tableKey(path.GetNlri())
+ dest := NewDestination(path.GetNlri(), 0)
+ destinations[tableKey] = dest
+ }
+ ipv4t.setDestinations(destinations)
+ ds := ipv4t.GetDestinations()
+ assert.Equal(t, ds, destinations)
+}
+func TestTableGetDestinations(t *testing.T) {
+ peerT := DestCreatePeer()
+ pathT := DestCreatePath(peerT)
+ ipv4t := NewTable(bgp.RF_IPv4_UC)
+ destinations := make(map[string]*Destination)
+ for _, path := range pathT {
+ tableKey := ipv4t.tableKey(path.GetNlri())
+ dest := NewDestination(path.GetNlri(), 0)
+ destinations[tableKey] = dest
+ }
+ ipv4t.setDestinations(destinations)
+ ds := ipv4t.GetDestinations()
+ assert.Equal(t, ds, destinations)
+}
+
+func TestTableKey(t *testing.T) {
+ tb := NewTable(bgp.RF_IPv4_UC)
+ n1, _ := bgp.NewPrefixFromRouteFamily(bgp.AFI_IP, bgp.SAFI_UNICAST, "0.0.0.0/0")
+ d1 := NewDestination(n1, 0)
+ n2, _ := bgp.NewPrefixFromRouteFamily(bgp.AFI_IP, bgp.SAFI_UNICAST, "0.0.0.0/1")
+ d2 := NewDestination(n2, 0)
+ assert.Equal(t, len(tb.tableKey(d1.GetNlri())), 5)
+ tb.setDestination(d1)
+ tb.setDestination(d2)
+ assert.Equal(t, len(tb.GetDestinations()), 2)
+}
+
+func TableCreatePeer() []*PeerInfo {
+ peerT1 := &PeerInfo{AS: 65000}
+ peerT2 := &PeerInfo{AS: 65001}
+ peerT3 := &PeerInfo{AS: 65002}
+ peerT := []*PeerInfo{peerT1, peerT2, peerT3}
+ return peerT
+}
+
+func TableCreatePath(peerT []*PeerInfo) []*Path {
+ bgpMsgT1 := updateMsgT1()
+ bgpMsgT2 := updateMsgT2()
+ bgpMsgT3 := updateMsgT3()
+ pathT := make([]*Path, 3)
+ for i, msg := range []*bgp.BGPMessage{bgpMsgT1, bgpMsgT2, bgpMsgT3} {
+ updateMsgT := msg.Body.(*bgp.BGPUpdate)
+ nlriList := updateMsgT.NLRI
+ pathAttributes := updateMsgT.PathAttributes
+ nlri_info := nlriList[0]
+ pathT[i] = NewPath(peerT[i], nlri_info, false, pathAttributes, time.Now(), false)
+ }
+ return pathT
+}
+
+func updateMsgT1() *bgp.BGPMessage {
+
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65000})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("192.168.50.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+
+ pathAttributes := []bgp.PathAttributeInterface{
+ origin,
+ aspath,
+ nexthop,
+ med,
+ }
+
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.10.0")}
+ return bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+}
+
+func updateMsgT2() *bgp.BGPMessage {
+
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65100})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("192.168.100.1")
+ med := bgp.NewPathAttributeMultiExitDisc(100)
+
+ pathAttributes := []bgp.PathAttributeInterface{
+ origin,
+ aspath,
+ nexthop,
+ med,
+ }
+
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "20.20.20.0")}
+ return bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+}
+func updateMsgT3() *bgp.BGPMessage {
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65100})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("192.168.150.1")
+ med := bgp.NewPathAttributeMultiExitDisc(100)
+
+ pathAttributes := []bgp.PathAttributeInterface{
+ origin,
+ aspath,
+ nexthop,
+ med,
+ }
+
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "30.30.30.0")}
+ w1 := bgp.NewIPAddrPrefix(23, "40.40.40.0")
+ withdrawnRoutes := []*bgp.IPAddrPrefix{w1}
+ return bgp.NewBGPUpdateMessage(withdrawnRoutes, pathAttributes, nlri)
+}
diff --git a/internal/pkg/table/vrf.go b/internal/pkg/table/vrf.go
new file mode 100644
index 00000000..053f85ce
--- /dev/null
+++ b/internal/pkg/table/vrf.go
@@ -0,0 +1,53 @@
+// Copyright (C) 2014-2016 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package table
+
+import (
+ "github.com/osrg/gobgp/pkg/packet/bgp"
+)
+
+type Vrf struct {
+ Name string
+ Id uint32
+ Rd bgp.RouteDistinguisherInterface
+ ImportRt []bgp.ExtendedCommunityInterface
+ ExportRt []bgp.ExtendedCommunityInterface
+}
+
+func (v *Vrf) Clone() *Vrf {
+ f := func(rt []bgp.ExtendedCommunityInterface) []bgp.ExtendedCommunityInterface {
+ l := make([]bgp.ExtendedCommunityInterface, 0, len(rt))
+ return append(l, rt...)
+ }
+ return &Vrf{
+ Name: v.Name,
+ Id: v.Id,
+ Rd: v.Rd,
+ ImportRt: f(v.ImportRt),
+ ExportRt: f(v.ExportRt),
+ }
+}
+
+func isLastTargetUser(vrfs map[string]*Vrf, target bgp.ExtendedCommunityInterface) bool {
+ for _, vrf := range vrfs {
+ for _, rt := range vrf.ImportRt {
+ if target.String() == rt.String() {
+ return false
+ }
+ }
+ }
+ return true
+}
diff --git a/internal/pkg/zebra/afi_string.go b/internal/pkg/zebra/afi_string.go
new file mode 100644
index 00000000..6c07a09d
--- /dev/null
+++ b/internal/pkg/zebra/afi_string.go
@@ -0,0 +1,17 @@
+// Code generated by "stringer -type=AFI"; DO NOT EDIT.
+
+package zebra
+
+import "fmt"
+
+const _AFI_name = "AFI_IPAFI_IP6AFI_ETHERAFI_MAX"
+
+var _AFI_index = [...]uint8{0, 6, 13, 22, 29}
+
+func (i AFI) String() string {
+ i -= 1
+ if i >= AFI(len(_AFI_index)-1) {
+ return fmt.Sprintf("AFI(%d)", i+1)
+ }
+ return _AFI_name[_AFI_index[i]:_AFI_index[i+1]]
+}
diff --git a/internal/pkg/zebra/api_type_string.go b/internal/pkg/zebra/api_type_string.go
new file mode 100644
index 00000000..e97059b1
--- /dev/null
+++ b/internal/pkg/zebra/api_type_string.go
@@ -0,0 +1,16 @@
+// Code generated by "stringer -type=API_TYPE"; DO NOT EDIT.
+
+package zebra
+
+import "fmt"
+
+const _API_TYPE_name = "FRR_INTERFACE_ADDINTERFACE_ADDINTERFACE_DELETEINTERFACE_ADDRESS_ADDINTERFACE_ADDRESS_DELETEINTERFACE_UPINTERFACE_DOWNIPV4_ROUTE_ADDIPV4_ROUTE_DELETEIPV6_ROUTE_ADDIPV6_ROUTE_DELETEREDISTRIBUTE_ADDREDISTRIBUTE_DELETEREDISTRIBUTE_DEFAULT_ADDREDISTRIBUTE_DEFAULT_DELETEIPV4_NEXTHOP_LOOKUPIPV6_NEXTHOP_LOOKUPIPV4_IMPORT_LOOKUPIPV6_IMPORT_LOOKUPINTERFACE_RENAMEROUTER_ID_ADDROUTER_ID_DELETEROUTER_ID_UPDATEHELLOIPV4_NEXTHOP_LOOKUP_MRIBVRF_UNREGISTERINTERFACE_LINK_PARAMSNEXTHOP_REGISTERNEXTHOP_UNREGISTERNEXTHOP_UPDATEMESSAGE_MAXFRR_BFD_DEST_REPLAYFRR_REDISTRIBUTE_IPV4_ADDFRR_REDISTRIBUTE_IPV4_DELFRR_REDISTRIBUTE_IPV6_ADDFRR_REDISTRIBUTE_IPV6_DELFRR_VRF_UNREGISTERFRR_VRF_ADDFRR_VRF_DELETEFRR_INTERFACE_VRF_UPDATEFRR_BFD_CLIENT_REGISTERFRR_INTERFACE_ENABLE_RADVFRR_INTERFACE_DISABLE_RADVFRR_IPV4_NEXTHOP_LOOKUP_MRIBFRR_INTERFACE_LINK_PARAMSFRR_MPLS_LABELS_ADDFRR_MPLS_LABELS_DELETEFRR_IPV4_NEXTHOP_ADDFRR_IPV4_NEXTHOP_DELETEFRR_IPV6_NEXTHOP_ADDFRR_IPV6_NEXTHOP_DELETEFRR_IPMR_ROUTE_STATSFRR_LABEL_MANAGER_CONNECTFRR_GET_LABEL_CHUNKFRR_RELEASE_LABEL_CHUNKFRR_PW_ADDFRR_PW_DELETEFRR_PW_SETFRR_PW_UNSETFRR_PW_STATUS_UPDATE"
+
+var _API_TYPE_index = [...]uint16{0, 17, 30, 46, 67, 91, 103, 117, 131, 148, 162, 179, 195, 214, 238, 265, 284, 303, 321, 339, 355, 368, 384, 400, 405, 429, 443, 464, 480, 498, 512, 523, 542, 567, 592, 617, 642, 660, 671, 685, 709, 732, 757, 783, 811, 836, 855, 877, 897, 920, 940, 963, 983, 1008, 1027, 1050, 1060, 1073, 1083, 1095, 1115}
+
+func (i API_TYPE) String() string {
+ if i >= API_TYPE(len(_API_TYPE_index)-1) {
+ return fmt.Sprintf("API_TYPE(%d)", i)
+ }
+ return _API_TYPE_name[_API_TYPE_index[i]:_API_TYPE_index[i+1]]
+}
diff --git a/internal/pkg/zebra/link_type_string.go b/internal/pkg/zebra/link_type_string.go
new file mode 100644
index 00000000..9db8544b
--- /dev/null
+++ b/internal/pkg/zebra/link_type_string.go
@@ -0,0 +1,16 @@
+// Code generated by "stringer -type=LINK_TYPE"; DO NOT EDIT.
+
+package zebra
+
+import "fmt"
+
+const _LINK_TYPE_name = "LINK_TYPE_UNKNOWNLINK_TYPE_ETHERLINK_TYPE_EETHERLINK_TYPE_AX25LINK_TYPE_PRONETLINK_TYPE_IEEE802LINK_TYPE_ARCNETLINK_TYPE_APPLETLKLINK_TYPE_DLCILINK_TYPE_ATMLINK_TYPE_METRICOMLINK_TYPE_IEEE1394LINK_TYPE_EUI64LINK_TYPE_INFINIBANDLINK_TYPE_SLIPLINK_TYPE_CSLIPLINK_TYPE_SLIP6LINK_TYPE_CSLIP6LINK_TYPE_RSRVDLINK_TYPE_ADAPTLINK_TYPE_ROSELINK_TYPE_X25LINK_TYPE_PPPLINK_TYPE_CHDLCLINK_TYPE_LAPBLINK_TYPE_RAWHDLCLINK_TYPE_IPIPLINK_TYPE_IPIP6LINK_TYPE_FRADLINK_TYPE_SKIPLINK_TYPE_LOOPBACKLINK_TYPE_LOCALTLKLINK_TYPE_FDDILINK_TYPE_SITLINK_TYPE_IPDDPLINK_TYPE_IPGRELINK_TYPE_IP6GRELINK_TYPE_PIMREGLINK_TYPE_HIPPILINK_TYPE_ECONETLINK_TYPE_IRDALINK_TYPE_FCPPLINK_TYPE_FCALLINK_TYPE_FCPLLINK_TYPE_FCFABRICLINK_TYPE_IEEE802_TRLINK_TYPE_IEEE80211LINK_TYPE_IEEE80211_RADIOTAPLINK_TYPE_IEEE802154LINK_TYPE_IEEE802154_PHY"
+
+var _LINK_TYPE_index = [...]uint16{0, 17, 32, 48, 62, 78, 95, 111, 129, 143, 156, 174, 192, 207, 227, 241, 256, 271, 287, 302, 317, 331, 344, 357, 372, 386, 403, 417, 432, 446, 460, 478, 496, 510, 523, 538, 553, 569, 585, 600, 616, 630, 644, 658, 672, 690, 710, 729, 757, 777, 801}
+
+func (i LINK_TYPE) String() string {
+ if i >= LINK_TYPE(len(_LINK_TYPE_index)-1) {
+ return fmt.Sprintf("LINK_TYPE(%d)", i)
+ }
+ return _LINK_TYPE_name[_LINK_TYPE_index[i]:_LINK_TYPE_index[i+1]]
+}
diff --git a/internal/pkg/zebra/nexthop_flag_string.go b/internal/pkg/zebra/nexthop_flag_string.go
new file mode 100644
index 00000000..38f08b8a
--- /dev/null
+++ b/internal/pkg/zebra/nexthop_flag_string.go
@@ -0,0 +1,17 @@
+// Code generated by "stringer -type=NEXTHOP_FLAG"; DO NOT EDIT.
+
+package zebra
+
+import "fmt"
+
+const _NEXTHOP_FLAG_name = "NEXTHOP_IFINDEXNEXTHOP_IFNAMENEXTHOP_IPV4NEXTHOP_IPV4_IFINDEXNEXTHOP_IPV4_IFNAMENEXTHOP_IPV6NEXTHOP_IPV6_IFINDEXNEXTHOP_IPV6_IFNAMENEXTHOP_BLACKHOLE"
+
+var _NEXTHOP_FLAG_index = [...]uint8{0, 15, 29, 41, 61, 80, 92, 112, 131, 148}
+
+func (i NEXTHOP_FLAG) String() string {
+ i -= 1
+ if i >= NEXTHOP_FLAG(len(_NEXTHOP_FLAG_index)-1) {
+ return fmt.Sprintf("NEXTHOP_FLAG(%d)", i+1)
+ }
+ return _NEXTHOP_FLAG_name[_NEXTHOP_FLAG_index[i]:_NEXTHOP_FLAG_index[i+1]]
+}
diff --git a/internal/pkg/zebra/ptm_enable_string.go b/internal/pkg/zebra/ptm_enable_string.go
new file mode 100644
index 00000000..d750542e
--- /dev/null
+++ b/internal/pkg/zebra/ptm_enable_string.go
@@ -0,0 +1,16 @@
+// Code generated by "stringer -type=PTM_ENABLE"; DO NOT EDIT.
+
+package zebra
+
+import "fmt"
+
+const _PTM_ENABLE_name = "PTM_ENABLE_OFFPTM_ENABLE_ONPTM_ENABLE_UNSPEC"
+
+var _PTM_ENABLE_index = [...]uint8{0, 14, 27, 44}
+
+func (i PTM_ENABLE) String() string {
+ if i >= PTM_ENABLE(len(_PTM_ENABLE_index)-1) {
+ return fmt.Sprintf("PTM_ENABLE(%d)", i)
+ }
+ return _PTM_ENABLE_name[_PTM_ENABLE_index[i]:_PTM_ENABLE_index[i+1]]
+}
diff --git a/internal/pkg/zebra/ptm_status_string.go b/internal/pkg/zebra/ptm_status_string.go
new file mode 100644
index 00000000..464233b7
--- /dev/null
+++ b/internal/pkg/zebra/ptm_status_string.go
@@ -0,0 +1,16 @@
+// Code generated by "stringer -type=PTM_STATUS"; DO NOT EDIT.
+
+package zebra
+
+import "fmt"
+
+const _PTM_STATUS_name = "PTM_STATUS_DOWNPTM_STATUS_UPPTM_STATUS_UNKNOWN"
+
+var _PTM_STATUS_index = [...]uint8{0, 15, 28, 46}
+
+func (i PTM_STATUS) String() string {
+ if i >= PTM_STATUS(len(_PTM_STATUS_index)-1) {
+ return fmt.Sprintf("PTM_STATUS(%d)", i)
+ }
+ return _PTM_STATUS_name[_PTM_STATUS_index[i]:_PTM_STATUS_index[i+1]]
+}
diff --git a/internal/pkg/zebra/route_type_string.go b/internal/pkg/zebra/route_type_string.go
new file mode 100644
index 00000000..e2ad6c97
--- /dev/null
+++ b/internal/pkg/zebra/route_type_string.go
@@ -0,0 +1,16 @@
+// Code generated by "stringer -type=ROUTE_TYPE"; DO NOT EDIT.
+
+package zebra
+
+import "fmt"
+
+const _ROUTE_TYPE_name = "ROUTE_SYSTEMROUTE_KERNELROUTE_CONNECTROUTE_STATICROUTE_RIPROUTE_RIPNGROUTE_OSPFROUTE_OSPF6ROUTE_ISISROUTE_BGPROUTE_PIMROUTE_HSLSROUTE_OLSRROUTE_BABELROUTE_MAXFRR_ROUTE_VNCFRR_ROUTE_VNC_DIRECTFRR_ROUTE_VNC_DIRECT_RHFRR_ROUTE_BGP_DIRECTFRR_ROUTE_BGP_DIRECT_EXTFRR_ROUTE_ALLFRR_ROUTE_MAX"
+
+var _ROUTE_TYPE_index = [...]uint16{0, 12, 24, 37, 49, 58, 69, 79, 90, 100, 109, 118, 128, 138, 149, 158, 171, 191, 214, 234, 258, 271, 284}
+
+func (i ROUTE_TYPE) String() string {
+ if i >= ROUTE_TYPE(len(_ROUTE_TYPE_index)-1) {
+ return fmt.Sprintf("ROUTE_TYPE(%d)", i)
+ }
+ return _ROUTE_TYPE_name[_ROUTE_TYPE_index[i]:_ROUTE_TYPE_index[i+1]]
+}
diff --git a/internal/pkg/zebra/safi_string.go b/internal/pkg/zebra/safi_string.go
new file mode 100644
index 00000000..ab491cb6
--- /dev/null
+++ b/internal/pkg/zebra/safi_string.go
@@ -0,0 +1,17 @@
+// Code generated by "stringer -type=SAFI"; DO NOT EDIT.
+
+package zebra
+
+import "fmt"
+
+const _SAFI_name = "SAFI_UNICASTSAFI_MULTICASTSAFI_RESERVED_3SAFI_MPLS_VPNSAFI_MAX"
+
+var _SAFI_index = [...]uint8{0, 12, 26, 41, 54, 62}
+
+func (i SAFI) String() string {
+ i -= 1
+ if i >= SAFI(len(_SAFI_index)-1) {
+ return fmt.Sprintf("SAFI(%d)", i+1)
+ }
+ return _SAFI_name[_SAFI_index[i]:_SAFI_index[i+1]]
+}
diff --git a/internal/pkg/zebra/zapi.go b/internal/pkg/zebra/zapi.go
new file mode 100644
index 00000000..eb443d05
--- /dev/null
+++ b/internal/pkg/zebra/zapi.go
@@ -0,0 +1,1917 @@
+// Copyright (C) 2014, 2015 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package zebra
+
+import (
+ "encoding/binary"
+ "fmt"
+ "io"
+ "net"
+ "strings"
+ "syscall"
+
+ log "github.com/sirupsen/logrus"
+
+ "github.com/osrg/gobgp/pkg/packet/bgp"
+)
+
+const (
+ HEADER_MARKER = 255
+ FRR_HEADER_MARKER = 254
+ INTERFACE_NAMSIZ = 20
+)
+
+// Internal Interface Status.
+type INTERFACE_STATUS uint8
+
+const (
+ INTERFACE_ACTIVE INTERFACE_STATUS = 0x01
+ INTERFACE_SUB INTERFACE_STATUS = 0x02
+ INTERFACE_LINKDETECTION INTERFACE_STATUS = 0x04
+ INTERFACE_VRF_LOOPBACK INTERFACE_STATUS = 0x08
+)
+
+// Interface Link Layer Types.
+//go:generate stringer -type=LINK_TYPE
+type LINK_TYPE uint32
+
+const (
+ LINK_TYPE_UNKNOWN LINK_TYPE = iota
+ LINK_TYPE_ETHER
+ LINK_TYPE_EETHER
+ LINK_TYPE_AX25
+ LINK_TYPE_PRONET
+ LINK_TYPE_IEEE802
+ LINK_TYPE_ARCNET
+ LINK_TYPE_APPLETLK
+ LINK_TYPE_DLCI
+ LINK_TYPE_ATM
+ LINK_TYPE_METRICOM
+ LINK_TYPE_IEEE1394
+ LINK_TYPE_EUI64
+ LINK_TYPE_INFINIBAND
+ LINK_TYPE_SLIP
+ LINK_TYPE_CSLIP
+ LINK_TYPE_SLIP6
+ LINK_TYPE_CSLIP6
+ LINK_TYPE_RSRVD
+ LINK_TYPE_ADAPT
+ LINK_TYPE_ROSE
+ LINK_TYPE_X25
+ LINK_TYPE_PPP
+ LINK_TYPE_CHDLC
+ LINK_TYPE_LAPB
+ LINK_TYPE_RAWHDLC
+ LINK_TYPE_IPIP
+ LINK_TYPE_IPIP6
+ LINK_TYPE_FRAD
+ LINK_TYPE_SKIP
+ LINK_TYPE_LOOPBACK
+ LINK_TYPE_LOCALTLK
+ LINK_TYPE_FDDI
+ LINK_TYPE_SIT
+ LINK_TYPE_IPDDP
+ LINK_TYPE_IPGRE
+ LINK_TYPE_IP6GRE
+ LINK_TYPE_PIMREG
+ LINK_TYPE_HIPPI
+ LINK_TYPE_ECONET
+ LINK_TYPE_IRDA
+ LINK_TYPE_FCPP
+ LINK_TYPE_FCAL
+ LINK_TYPE_FCPL
+ LINK_TYPE_FCFABRIC
+ LINK_TYPE_IEEE802_TR
+ LINK_TYPE_IEEE80211
+ LINK_TYPE_IEEE80211_RADIOTAP
+ LINK_TYPE_IEEE802154
+ LINK_TYPE_IEEE802154_PHY
+)
+
+const VRF_DEFAULT = 0
+
+func HeaderSize(version uint8) uint16 {
+ switch version {
+ case 3, 4:
+ return 8
+ default:
+ return 6
+ }
+}
+
+func (t INTERFACE_STATUS) String() string {
+ ss := make([]string, 0, 3)
+ if t&INTERFACE_ACTIVE > 0 {
+ ss = append(ss, "ACTIVE")
+ }
+ if t&INTERFACE_SUB > 0 {
+ ss = append(ss, "SUB")
+ }
+ if t&INTERFACE_LINKDETECTION > 0 {
+ ss = append(ss, "LINKDETECTION")
+ }
+ if t&INTERFACE_VRF_LOOPBACK > 0 {
+ ss = append(ss, "VRF_LOOPBACK")
+ }
+ return strings.Join(ss, "|")
+}
+
+// Interface Connected Address Flags
+type INTERFACE_ADDRESS_FLAG uint8
+
+const (
+ INTERFACE_ADDRESS_SECONDARY INTERFACE_ADDRESS_FLAG = 0x01
+ INTERFACE_ADDRESS_PEER INTERFACE_ADDRESS_FLAG = 0x02
+ INTERFACE_ADDRESS_UNNUMBERED INTERFACE_ADDRESS_FLAG = 0x04
+)
+
+func (t INTERFACE_ADDRESS_FLAG) String() string {
+ ss := make([]string, 0, 3)
+ if t&INTERFACE_ADDRESS_SECONDARY > 0 {
+ ss = append(ss, "SECONDARY")
+ }
+ if t&INTERFACE_ADDRESS_PEER > 0 {
+ ss = append(ss, "PEER")
+ }
+ if t&INTERFACE_ADDRESS_UNNUMBERED > 0 {
+ ss = append(ss, "UNNUMBERED")
+ }
+ return strings.Join(ss, "|")
+}
+
+// Address Family Identifier.
+//go:generate stringer -type=AFI
+type AFI uint8
+
+const (
+ AFI_IP AFI = 1
+ AFI_IP6 AFI = 2
+ AFI_ETHER AFI = 3
+ AFI_MAX AFI = 4
+)
+
+// Subsequent Address Family Identifier.
+//go:generate stringer -type=SAFI
+type SAFI uint8
+
+const (
+ _ SAFI = iota
+ SAFI_UNICAST
+ SAFI_MULTICAST
+ SAFI_RESERVED_3
+ SAFI_MPLS_VPN
+ SAFI_MAX
+)
+
+// API Types.
+//go:generate stringer -type=API_TYPE
+type API_TYPE uint16
+
+// For Quagga.
+const (
+ _ API_TYPE = iota
+ INTERFACE_ADD
+ INTERFACE_DELETE
+ INTERFACE_ADDRESS_ADD
+ INTERFACE_ADDRESS_DELETE
+ INTERFACE_UP
+ INTERFACE_DOWN
+ IPV4_ROUTE_ADD
+ IPV4_ROUTE_DELETE
+ IPV6_ROUTE_ADD
+ IPV6_ROUTE_DELETE
+ REDISTRIBUTE_ADD
+ REDISTRIBUTE_DELETE
+ REDISTRIBUTE_DEFAULT_ADD
+ REDISTRIBUTE_DEFAULT_DELETE
+ IPV4_NEXTHOP_LOOKUP
+ IPV6_NEXTHOP_LOOKUP
+ IPV4_IMPORT_LOOKUP
+ IPV6_IMPORT_LOOKUP
+ INTERFACE_RENAME
+ ROUTER_ID_ADD
+ ROUTER_ID_DELETE
+ ROUTER_ID_UPDATE
+ HELLO
+ IPV4_NEXTHOP_LOOKUP_MRIB
+ VRF_UNREGISTER
+ INTERFACE_LINK_PARAMS
+ NEXTHOP_REGISTER
+ NEXTHOP_UNREGISTER
+ NEXTHOP_UPDATE
+ MESSAGE_MAX
+)
+
+// For FRRouting.
+const (
+ FRR_INTERFACE_ADD API_TYPE = iota
+ FRR_INTERFACE_DELETE
+ FRR_INTERFACE_ADDRESS_ADD
+ FRR_INTERFACE_ADDRESS_DELETE
+ FRR_INTERFACE_UP
+ FRR_INTERFACE_DOWN
+ FRR_IPV4_ROUTE_ADD
+ FRR_IPV4_ROUTE_DELETE
+ FRR_IPV6_ROUTE_ADD
+ FRR_IPV6_ROUTE_DELETE
+ FRR_REDISTRIBUTE_ADD
+ FRR_REDISTRIBUTE_DELETE
+ FRR_REDISTRIBUTE_DEFAULT_ADD
+ FRR_REDISTRIBUTE_DEFAULT_DELETE
+ FRR_ROUTER_ID_ADD
+ FRR_ROUTER_ID_DELETE
+ FRR_ROUTER_ID_UPDATE
+ FRR_HELLO
+ FRR_NEXTHOP_REGISTER
+ FRR_NEXTHOP_UNREGISTER
+ FRR_NEXTHOP_UPDATE
+ FRR_INTERFACE_NBR_ADDRESS_ADD
+ FRR_INTERFACE_NBR_ADDRESS_DELETE
+ FRR_INTERFACE_BFD_DEST_UPDATE
+ FRR_IMPORT_ROUTE_REGISTER
+ FRR_IMPORT_ROUTE_UNREGISTER
+ FRR_IMPORT_CHECK_UPDATE
+ FRR_IPV4_ROUTE_IPV6_NEXTHOP_ADD
+ FRR_BFD_DEST_REGISTER
+ FRR_BFD_DEST_DEREGISTER
+ FRR_BFD_DEST_UPDATE
+ FRR_BFD_DEST_REPLAY
+ FRR_REDISTRIBUTE_IPV4_ADD
+ FRR_REDISTRIBUTE_IPV4_DEL
+ FRR_REDISTRIBUTE_IPV6_ADD
+ FRR_REDISTRIBUTE_IPV6_DEL
+ FRR_VRF_UNREGISTER
+ FRR_VRF_ADD
+ FRR_VRF_DELETE
+ FRR_INTERFACE_VRF_UPDATE
+ FRR_BFD_CLIENT_REGISTER
+ FRR_INTERFACE_ENABLE_RADV
+ FRR_INTERFACE_DISABLE_RADV
+ FRR_IPV4_NEXTHOP_LOOKUP_MRIB
+ FRR_INTERFACE_LINK_PARAMS
+ FRR_MPLS_LABELS_ADD
+ FRR_MPLS_LABELS_DELETE
+ FRR_IPV4_NEXTHOP_ADD
+ FRR_IPV4_NEXTHOP_DELETE
+ FRR_IPV6_NEXTHOP_ADD
+ FRR_IPV6_NEXTHOP_DELETE
+ FRR_IPMR_ROUTE_STATS
+ FRR_LABEL_MANAGER_CONNECT
+ FRR_GET_LABEL_CHUNK
+ FRR_RELEASE_LABEL_CHUNK
+ FRR_PW_ADD
+ FRR_PW_DELETE
+ FRR_PW_SET
+ FRR_PW_UNSET
+ FRR_PW_STATUS_UPDATE
+)
+
+// Route Types.
+//go:generate stringer -type=ROUTE_TYPE
+type ROUTE_TYPE uint8
+
+// For Quagga.
+const (
+ ROUTE_SYSTEM ROUTE_TYPE = iota
+ ROUTE_KERNEL
+ ROUTE_CONNECT
+ ROUTE_STATIC
+ ROUTE_RIP
+ ROUTE_RIPNG
+ ROUTE_OSPF
+ ROUTE_OSPF6
+ ROUTE_ISIS
+ ROUTE_BGP
+ ROUTE_PIM
+ ROUTE_HSLS
+ ROUTE_OLSR
+ ROUTE_BABEL
+ ROUTE_MAX
+)
+
+// For FRRouting.
+const (
+ FRR_ROUTE_SYSTEM ROUTE_TYPE = iota
+ FRR_ROUTE_KERNEL
+ FRR_ROUTE_CONNECT
+ FRR_ROUTE_STATIC
+ FRR_ROUTE_RIP
+ FRR_ROUTE_RIPNG
+ FRR_ROUTE_OSPF
+ FRR_ROUTE_OSPF6
+ FRR_ROUTE_ISIS
+ FRR_ROUTE_BGP
+ FRR_ROUTE_PIM
+ FRR_ROUTE_HSLS
+ FRR_ROUTE_OLSR
+ FRR_ROUTE_TABLE
+ FRR_ROUTE_LDP
+ FRR_ROUTE_VNC
+ FRR_ROUTE_VNC_DIRECT
+ FRR_ROUTE_VNC_DIRECT_RH
+ FRR_ROUTE_BGP_DIRECT
+ FRR_ROUTE_BGP_DIRECT_EXT
+ FRR_ROUTE_ALL
+ FRR_ROUTE_MAX
+)
+
+var routeTypeValueMap = map[string]ROUTE_TYPE{
+ "system": ROUTE_SYSTEM,
+ "kernel": ROUTE_KERNEL,
+ "connect": ROUTE_CONNECT, // hack for backyard compatibility
+ "directly-connected": ROUTE_CONNECT,
+ "static": ROUTE_STATIC,
+ "rip": ROUTE_RIP,
+ "ripng": ROUTE_RIPNG,
+ "ospf": ROUTE_OSPF,
+ "ospf3": ROUTE_OSPF6,
+ "isis": ROUTE_ISIS,
+ "bgp": ROUTE_BGP,
+ "pim": ROUTE_PIM,
+ "hsls": ROUTE_HSLS,
+ "olsr": ROUTE_OLSR,
+ "babel": ROUTE_BABEL,
+ "table": FRR_ROUTE_TABLE,
+ "ldp": FRR_ROUTE_LDP,
+ "vnc": FRR_ROUTE_VNC,
+ "vnc-direct": FRR_ROUTE_VNC_DIRECT,
+ "vnc-direct-rh": FRR_ROUTE_VNC_DIRECT_RH,
+ "bgp-direct": FRR_ROUTE_BGP_DIRECT,
+ "bgp-direct-ext": FRR_ROUTE_BGP_DIRECT_EXT,
+ "all": FRR_ROUTE_ALL,
+}
+
+func RouteTypeFromString(typ string) (ROUTE_TYPE, error) {
+ t, ok := routeTypeValueMap[typ]
+ if ok {
+ return t, nil
+ }
+ return t, fmt.Errorf("unknown route type: %s", typ)
+}
+
+// API Message Flags.
+type MESSAGE_FLAG uint8
+
+// For Quagga.
+const (
+ MESSAGE_NEXTHOP MESSAGE_FLAG = 0x01
+ MESSAGE_IFINDEX MESSAGE_FLAG = 0x02
+ MESSAGE_DISTANCE MESSAGE_FLAG = 0x04
+ MESSAGE_METRIC MESSAGE_FLAG = 0x08
+ MESSAGE_MTU MESSAGE_FLAG = 0x10
+ MESSAGE_TAG MESSAGE_FLAG = 0x20
+)
+
+func (t MESSAGE_FLAG) String() string {
+ var ss []string
+ if t&MESSAGE_NEXTHOP > 0 {
+ ss = append(ss, "NEXTHOP")
+ }
+ if t&MESSAGE_IFINDEX > 0 {
+ ss = append(ss, "IFINDEX")
+ }
+ if t&MESSAGE_DISTANCE > 0 {
+ ss = append(ss, "DISTANCE")
+ }
+ if t&MESSAGE_METRIC > 0 {
+ ss = append(ss, "METRIC")
+ }
+ if t&MESSAGE_MTU > 0 {
+ ss = append(ss, "MTU")
+ }
+ if t&MESSAGE_TAG > 0 {
+ ss = append(ss, "TAG")
+ }
+ return strings.Join(ss, "|")
+}
+
+// For FRRouting.
+const (
+ FRR_MESSAGE_NEXTHOP MESSAGE_FLAG = 0x01
+ FRR_MESSAGE_IFINDEX MESSAGE_FLAG = 0x02
+ FRR_MESSAGE_DISTANCE MESSAGE_FLAG = 0x04
+ FRR_MESSAGE_METRIC MESSAGE_FLAG = 0x08
+ FRR_MESSAGE_TAG MESSAGE_FLAG = 0x10
+ FRR_MESSAGE_MTU MESSAGE_FLAG = 0x20
+ FRR_MESSAGE_SRCPFX MESSAGE_FLAG = 0x40
+)
+
+// Message Flags
+type FLAG uint64
+
+const (
+ FLAG_INTERNAL FLAG = 0x01
+ FLAG_SELFROUTE FLAG = 0x02
+ FLAG_BLACKHOLE FLAG = 0x04
+ FLAG_IBGP FLAG = 0x08
+ FLAG_SELECTED FLAG = 0x10
+ FLAG_CHANGED FLAG = 0x20
+ FLAG_STATIC FLAG = 0x40
+ FLAG_REJECT FLAG = 0x80
+ FLAG_SCOPE_LINK FLAG = 0x100
+ FLAG_FIB_OVERRIDE FLAG = 0x200
+)
+
+func (t FLAG) String() string {
+ var ss []string
+ if t&FLAG_INTERNAL > 0 {
+ ss = append(ss, "FLAG_INTERNAL")
+ }
+ if t&FLAG_SELFROUTE > 0 {
+ ss = append(ss, "FLAG_SELFROUTE")
+ }
+ if t&FLAG_BLACKHOLE > 0 {
+ ss = append(ss, "FLAG_BLACKHOLE")
+ }
+ if t&FLAG_IBGP > 0 {
+ ss = append(ss, "FLAG_IBGP")
+ }
+ if t&FLAG_SELECTED > 0 {
+ ss = append(ss, "FLAG_SELECTED")
+ }
+ if t&FLAG_CHANGED > 0 {
+ ss = append(ss, "FLAG_CHANGED")
+ }
+ if t&FLAG_STATIC > 0 {
+ ss = append(ss, "FLAG_STATIC")
+ }
+ if t&FLAG_REJECT > 0 {
+ ss = append(ss, "FLAG_REJECT")
+ }
+ if t&FLAG_SCOPE_LINK > 0 {
+ ss = append(ss, "FLAG_SCOPE_LINK")
+ }
+ if t&FLAG_FIB_OVERRIDE > 0 {
+ ss = append(ss, "FLAG_FIB_OVERRIDE")
+ }
+ return strings.Join(ss, "|")
+}
+
+// Nexthop Flags.
+//go:generate stringer -type=NEXTHOP_FLAG
+type NEXTHOP_FLAG uint8
+
+// For Quagga.
+const (
+ _ NEXTHOP_FLAG = iota
+ NEXTHOP_IFINDEX
+ NEXTHOP_IFNAME
+ NEXTHOP_IPV4
+ NEXTHOP_IPV4_IFINDEX
+ NEXTHOP_IPV4_IFNAME
+ NEXTHOP_IPV6
+ NEXTHOP_IPV6_IFINDEX
+ NEXTHOP_IPV6_IFNAME
+ NEXTHOP_BLACKHOLE
+)
+
+// For FRRouting.
+const (
+ _ NEXTHOP_FLAG = iota
+ FRR_NEXTHOP_IFINDEX
+ FRR_NEXTHOP_IPV4
+ FRR_NEXTHOP_IPV4_IFINDEX
+ FRR_NEXTHOP_IPV6
+ FRR_NEXTHOP_IPV6_IFINDEX
+ FRR_NEXTHOP_BLACKHOLE
+)
+
+// Interface PTM Enable Configuration.
+//go:generate stringer -type=PTM_ENABLE
+type PTM_ENABLE uint8
+
+const (
+ PTM_ENABLE_OFF PTM_ENABLE = 0
+ PTM_ENABLE_ON PTM_ENABLE = 1
+ PTM_ENABLE_UNSPEC PTM_ENABLE = 2
+)
+
+// PTM Status.
+//go:generate stringer -type=PTM_STATUS
+type PTM_STATUS uint8
+
+const (
+ PTM_STATUS_DOWN PTM_STATUS = 0
+ PTM_STATUS_UP PTM_STATUS = 1
+ PTM_STATUS_UNKNOWN PTM_STATUS = 2
+)
+
+type Client struct {
+ outgoing chan *Message
+ incoming chan *Message
+ redistDefault ROUTE_TYPE
+ conn net.Conn
+ Version uint8
+}
+
+func NewClient(network, address string, typ ROUTE_TYPE, version uint8) (*Client, error) {
+ conn, err := net.Dial(network, address)
+ if err != nil {
+ return nil, err
+ }
+ outgoing := make(chan *Message)
+ incoming := make(chan *Message, 64)
+ if version < 2 {
+ version = 2
+ } else if version > 4 {
+ version = 4
+ }
+
+ c := &Client{
+ outgoing: outgoing,
+ incoming: incoming,
+ redistDefault: typ,
+ conn: conn,
+ Version: version,
+ }
+
+ go func() {
+ for {
+ m, more := <-outgoing
+ if more {
+ b, err := m.Serialize()
+ if err != nil {
+ log.WithFields(log.Fields{
+ "Topic": "Zebra",
+ }).Warnf("failed to serialize: %v", m)
+ continue
+ }
+
+ _, err = conn.Write(b)
+ if err != nil {
+ log.WithFields(log.Fields{
+ "Topic": "Zebra",
+ }).Errorf("failed to write: %s", err)
+ close(outgoing)
+ }
+ } else {
+ log.Debug("finish outgoing loop")
+ return
+ }
+ }
+ }()
+
+ // Send HELLO/ROUTER_ID_ADD messages to negotiate the Zebra message version.
+ c.SendHello()
+ c.SendRouterIDAdd()
+
+ receiveSingleMsg := func() (*Message, error) {
+ headerBuf, err := readAll(conn, int(HeaderSize(version)))
+ if err != nil {
+ log.WithFields(log.Fields{
+ "Topic": "Zebra",
+ "Error": err,
+ }).Error("failed to read header")
+ return nil, err
+ }
+
+ hd := &Header{}
+ err = hd.DecodeFromBytes(headerBuf)
+ if err != nil {
+ log.WithFields(log.Fields{
+ "Topic": "Zebra",
+ "Data": headerBuf,
+ "Error": err,
+ }).Error("failed to decode header")
+ return nil, err
+ }
+
+ bodyBuf, err := readAll(conn, int(hd.Len-HeaderSize(version)))
+ if err != nil {
+ log.WithFields(log.Fields{
+ "Topic": "Zebra",
+ "Header": hd,
+ "Error": err,
+ }).Error("failed to read body")
+ return nil, err
+ }
+
+ m, err := ParseMessage(hd, bodyBuf)
+ if err != nil {
+ // Just outputting warnings (not error message) and ignore this
+ // error considering the case that body parser is not implemented
+ // yet.
+ log.WithFields(log.Fields{
+ "Topic": "Zebra",
+ "Header": hd,
+ "Data": bodyBuf,
+ "Error": err,
+ }).Warn("failed to decode body")
+ return nil, nil
+ }
+ log.WithFields(log.Fields{
+ "Topic": "Zebra",
+ "Message": m,
+ }).Debug("read message from zebra")
+
+ return m, nil
+ }
+
+ // Try to receive the first message from Zebra.
+ if m, err := receiveSingleMsg(); err != nil {
+ c.Close()
+ // Return error explicitly in order to retry connection.
+ return nil, err
+ } else if m != nil {
+ incoming <- m
+ }
+
+ // Start receive loop only when the first message successfully received.
+ go func() {
+ defer close(incoming)
+ for {
+ if m, err := receiveSingleMsg(); err != nil {
+ return
+ } else if m != nil {
+ incoming <- m
+ }
+ }
+ }()
+
+ return c, nil
+}
+
+func readAll(conn net.Conn, length int) ([]byte, error) {
+ buf := make([]byte, length)
+ _, err := io.ReadFull(conn, buf)
+ return buf, err
+}
+
+func (c *Client) Receive() chan *Message {
+ return c.incoming
+}
+
+func (c *Client) Send(m *Message) {
+ defer func() {
+ if err := recover(); err != nil {
+ log.WithFields(log.Fields{
+ "Topic": "Zebra",
+ }).Debugf("recovered: %s", err)
+ }
+ }()
+ log.WithFields(log.Fields{
+ "Topic": "Zebra",
+ "Header": m.Header,
+ "Body": m.Body,
+ }).Debug("send command to zebra")
+ c.outgoing <- m
+}
+
+func (c *Client) SendCommand(command API_TYPE, vrfId uint16, body Body) error {
+ var marker uint8 = HEADER_MARKER
+ if c.Version >= 4 {
+ marker = FRR_HEADER_MARKER
+ }
+ m := &Message{
+ Header: Header{
+ Len: HeaderSize(c.Version),
+ Marker: marker,
+ Version: c.Version,
+ VrfId: vrfId,
+ Command: command,
+ },
+ Body: body,
+ }
+ c.Send(m)
+ return nil
+}
+
+func (c *Client) SendHello() error {
+ if c.redistDefault > 0 {
+ command := HELLO
+ body := &HelloBody{
+ RedistDefault: c.redistDefault,
+ Instance: 0,
+ }
+ if c.Version >= 4 {
+ command = FRR_HELLO
+ }
+ return c.SendCommand(command, VRF_DEFAULT, body)
+ }
+ return nil
+}
+
+func (c *Client) SendRouterIDAdd() error {
+ command := ROUTER_ID_ADD
+ if c.Version >= 4 {
+ command = FRR_ROUTER_ID_ADD
+ }
+ return c.SendCommand(command, VRF_DEFAULT, nil)
+}
+
+func (c *Client) SendInterfaceAdd() error {
+ command := INTERFACE_ADD
+ if c.Version >= 4 {
+ command = FRR_INTERFACE_ADD
+ }
+ return c.SendCommand(command, VRF_DEFAULT, nil)
+}
+
+func (c *Client) SendRedistribute(t ROUTE_TYPE, vrfId uint16) error {
+ command := REDISTRIBUTE_ADD
+ if c.redistDefault != t {
+ bodies := make([]*RedistributeBody, 0)
+ if c.Version <= 3 {
+ bodies = append(bodies, &RedistributeBody{
+ Redist: t,
+ })
+ } else { // version >= 4
+ command = FRR_REDISTRIBUTE_ADD
+ for _, afi := range []AFI{AFI_IP, AFI_IP6} {
+ bodies = append(bodies, &RedistributeBody{
+ Afi: afi,
+ Redist: t,
+ Instance: 0,
+ })
+ }
+ }
+
+ for _, body := range bodies {
+ return c.SendCommand(command, vrfId, body)
+ }
+ }
+
+ return nil
+}
+
+func (c *Client) SendRedistributeDelete(t ROUTE_TYPE) error {
+ if t < ROUTE_MAX {
+ command := REDISTRIBUTE_DELETE
+ if c.Version >= 4 {
+ command = FRR_REDISTRIBUTE_DELETE
+ }
+ body := &RedistributeBody{
+ Redist: t,
+ }
+ return c.SendCommand(command, VRF_DEFAULT, body)
+ } else {
+ return fmt.Errorf("unknown route type: %d", t)
+ }
+}
+
+func (c *Client) SendIPRoute(vrfId uint16, body *IPRouteBody, isWithdraw bool) error {
+ command := IPV4_ROUTE_ADD
+ if c.Version <= 3 {
+ if body.Prefix.To4() != nil {
+ if isWithdraw {
+ command = IPV4_ROUTE_DELETE
+ }
+ } else {
+ if isWithdraw {
+ command = IPV6_ROUTE_DELETE
+ } else {
+ command = IPV6_ROUTE_ADD
+ }
+ }
+ } else { // version >= 4
+ if body.Prefix.To4() != nil {
+ if isWithdraw {
+ command = FRR_IPV4_ROUTE_DELETE
+ } else {
+ command = FRR_IPV4_ROUTE_ADD
+ }
+ } else {
+ if isWithdraw {
+ command = FRR_IPV6_ROUTE_DELETE
+ } else {
+ command = FRR_IPV6_ROUTE_ADD
+ }
+ }
+ }
+ return c.SendCommand(command, vrfId, body)
+}
+
+func (c *Client) SendNexthopRegister(vrfId uint16, body *NexthopRegisterBody, isWithdraw bool) error {
+ // Note: NEXTHOP_REGISTER and NEXTHOP_UNREGISTER messages are not
+ // supported in Zebra protocol version<3.
+ if c.Version < 3 {
+ return fmt.Errorf("NEXTHOP_REGISTER/NEXTHOP_UNREGISTER are not supported in version: %d", c.Version)
+ }
+ command := NEXTHOP_REGISTER
+ if c.Version == 3 {
+ if isWithdraw {
+ command = NEXTHOP_UNREGISTER
+ }
+ } else { // version >= 4
+ if isWithdraw {
+ command = FRR_NEXTHOP_UNREGISTER
+ } else {
+ command = FRR_NEXTHOP_REGISTER
+ }
+ }
+ return c.SendCommand(command, vrfId, body)
+}
+
+func (c *Client) Close() error {
+ close(c.outgoing)
+ return c.conn.Close()
+}
+
+type Header struct {
+ Len uint16
+ Marker uint8
+ Version uint8
+ VrfId uint16
+ Command API_TYPE
+}
+
+func (h *Header) Serialize() ([]byte, error) {
+ buf := make([]byte, HeaderSize(h.Version))
+ binary.BigEndian.PutUint16(buf[0:2], h.Len)
+ buf[2] = h.Marker
+ buf[3] = h.Version
+ switch h.Version {
+ case 2:
+ binary.BigEndian.PutUint16(buf[4:6], uint16(h.Command))
+ case 3, 4:
+ binary.BigEndian.PutUint16(buf[4:6], uint16(h.VrfId))
+ binary.BigEndian.PutUint16(buf[6:8], uint16(h.Command))
+ default:
+ return nil, fmt.Errorf("Unsupported ZAPI version: %d", h.Version)
+ }
+ return buf, nil
+}
+
+func (h *Header) DecodeFromBytes(data []byte) error {
+ if uint16(len(data)) < 4 {
+ return fmt.Errorf("Not all ZAPI message header")
+ }
+ h.Len = binary.BigEndian.Uint16(data[0:2])
+ h.Marker = data[2]
+ h.Version = data[3]
+ if uint16(len(data)) < HeaderSize(h.Version) {
+ return fmt.Errorf("Not all ZAPI message header")
+ }
+ switch h.Version {
+ case 2:
+ h.Command = API_TYPE(binary.BigEndian.Uint16(data[4:6]))
+ case 3, 4:
+ h.VrfId = binary.BigEndian.Uint16(data[4:6])
+ h.Command = API_TYPE(binary.BigEndian.Uint16(data[6:8]))
+ default:
+ return fmt.Errorf("Unsupported ZAPI version: %d", h.Version)
+ }
+ return nil
+}
+
+type Body interface {
+ DecodeFromBytes([]byte, uint8) error
+ Serialize(uint8) ([]byte, error)
+ String() string
+}
+
+type UnknownBody struct {
+ Data []byte
+}
+
+func (b *UnknownBody) DecodeFromBytes(data []byte, version uint8) error {
+ b.Data = data
+ return nil
+}
+
+func (b *UnknownBody) Serialize(version uint8) ([]byte, error) {
+ return b.Data, nil
+}
+
+func (b *UnknownBody) String() string {
+ return fmt.Sprintf("data: %v", b.Data)
+}
+
+type HelloBody struct {
+ RedistDefault ROUTE_TYPE
+ Instance uint16
+}
+
+func (b *HelloBody) DecodeFromBytes(data []byte, version uint8) error {
+ b.RedistDefault = ROUTE_TYPE(data[0])
+ if version >= 4 {
+ b.Instance = binary.BigEndian.Uint16(data[1:3])
+ }
+ return nil
+}
+
+func (b *HelloBody) Serialize(version uint8) ([]byte, error) {
+ if version <= 3 {
+ return []byte{uint8(b.RedistDefault)}, nil
+ } else { // version >= 4
+ buf := make([]byte, 3)
+ buf[0] = uint8(b.RedistDefault)
+ binary.BigEndian.PutUint16(buf[1:3], b.Instance)
+ return buf, nil
+
+ }
+}
+
+func (b *HelloBody) String() string {
+ return fmt.Sprintf(
+ "route_type: %s, instance :%d",
+ b.RedistDefault.String(), b.Instance)
+}
+
+type RedistributeBody struct {
+ Afi AFI
+ Redist ROUTE_TYPE
+ Instance uint16
+}
+
+func (b *RedistributeBody) DecodeFromBytes(data []byte, version uint8) error {
+ if version <= 3 {
+ b.Redist = ROUTE_TYPE(data[0])
+ } else { // version >= 4
+ b.Afi = AFI(data[0])
+ b.Redist = ROUTE_TYPE(data[1])
+ b.Instance = binary.BigEndian.Uint16(data[2:4])
+ }
+ return nil
+}
+
+func (b *RedistributeBody) Serialize(version uint8) ([]byte, error) {
+ if version <= 3 {
+ return []byte{uint8(b.Redist)}, nil
+ } else { // version >= 4
+ buf := make([]byte, 4)
+ buf[0] = uint8(b.Afi)
+ buf[1] = uint8(b.Redist)
+ binary.BigEndian.PutUint16(buf[2:4], b.Instance)
+ return buf, nil
+ }
+}
+
+func (b *RedistributeBody) String() string {
+ return fmt.Sprintf(
+ "afi: %s, route_type: %s, instance :%d",
+ b.Afi.String(), b.Redist.String(), b.Instance)
+}
+
+type InterfaceUpdateBody struct {
+ Name string
+ Index uint32
+ Status INTERFACE_STATUS
+ Flags uint64
+ PTMEnable PTM_ENABLE
+ PTMStatus PTM_STATUS
+ Metric uint32
+ Speed uint32
+ MTU uint32
+ MTU6 uint32
+ Bandwidth uint32
+ Linktype LINK_TYPE
+ HardwareAddr net.HardwareAddr
+}
+
+func (b *InterfaceUpdateBody) DecodeFromBytes(data []byte, version uint8) error {
+ if len(data) < INTERFACE_NAMSIZ+29 {
+ return fmt.Errorf("lack of bytes. need %d but %d", INTERFACE_NAMSIZ+29, len(data))
+ }
+
+ b.Name = strings.Trim(string(data[:INTERFACE_NAMSIZ]), "\u0000")
+ data = data[INTERFACE_NAMSIZ:]
+ b.Index = binary.BigEndian.Uint32(data[0:4])
+ b.Status = INTERFACE_STATUS(data[4])
+ b.Flags = binary.BigEndian.Uint64(data[5:13])
+ if version >= 4 {
+ b.PTMEnable = PTM_ENABLE(data[13])
+ b.PTMStatus = PTM_STATUS(data[14])
+ b.Metric = binary.BigEndian.Uint32(data[15:19])
+ b.Speed = binary.BigEndian.Uint32(data[19:23])
+ data = data[23:]
+ } else {
+ b.Metric = binary.BigEndian.Uint32(data[13:17])
+ data = data[17:]
+ }
+ b.MTU = binary.BigEndian.Uint32(data[0:4])
+ b.MTU6 = binary.BigEndian.Uint32(data[4:8])
+ b.Bandwidth = binary.BigEndian.Uint32(data[8:12])
+ if version >= 3 {
+ b.Linktype = LINK_TYPE(binary.BigEndian.Uint32(data[12:16]))
+ data = data[16:]
+ } else {
+ data = data[12:]
+ }
+ l := binary.BigEndian.Uint32(data[:4])
+ if l > 0 {
+ if len(data) < 4+int(l) {
+ return fmt.Errorf("lack of bytes. need %d but %d", 4+l, len(data))
+ }
+ b.HardwareAddr = data[4 : 4+l]
+ }
+ return nil
+}
+
+func (b *InterfaceUpdateBody) Serialize(version uint8) ([]byte, error) {
+ return []byte{}, nil
+}
+
+func (b *InterfaceUpdateBody) String() string {
+ s := fmt.Sprintf(
+ "name: %s, idx: %d, status: %s, flags: %s, ptm_enable: %s, ptm_status: %s, metric: %d, speed: %d, mtu: %d, mtu6: %d, bandwidth: %d, linktype: %s",
+ b.Name, b.Index, b.Status.String(), intfflag2string(b.Flags), b.PTMEnable.String(), b.PTMStatus.String(), b.Metric, b.Speed, b.MTU, b.MTU6, b.Bandwidth, b.Linktype.String())
+ if len(b.HardwareAddr) > 0 {
+ return s + fmt.Sprintf(", mac: %s", b.HardwareAddr.String())
+ }
+ return s
+}
+
+type InterfaceAddressUpdateBody struct {
+ Index uint32
+ Flags INTERFACE_ADDRESS_FLAG
+ Prefix net.IP
+ Length uint8
+ Destination net.IP
+}
+
+func (b *InterfaceAddressUpdateBody) DecodeFromBytes(data []byte, version uint8) error {
+ b.Index = binary.BigEndian.Uint32(data[:4])
+ b.Flags = INTERFACE_ADDRESS_FLAG(data[4])
+ family := data[5]
+ var addrlen int8
+ switch family {
+ case syscall.AF_INET:
+ addrlen = net.IPv4len
+ case syscall.AF_INET6:
+ addrlen = net.IPv6len
+ default:
+ return fmt.Errorf("unknown address family: %d", family)
+ }
+ b.Prefix = data[6 : 6+addrlen]
+ b.Length = data[6+addrlen]
+ b.Destination = data[7+addrlen : 7+addrlen*2]
+ return nil
+}
+
+func (b *InterfaceAddressUpdateBody) Serialize(version uint8) ([]byte, error) {
+ return []byte{}, nil
+}
+
+func (b *InterfaceAddressUpdateBody) String() string {
+ return fmt.Sprintf(
+ "idx: %d, flags: %s, addr: %s/%d",
+ b.Index, b.Flags.String(), b.Prefix.String(), b.Length)
+}
+
+type RouterIDUpdateBody struct {
+ Length uint8
+ Prefix net.IP
+}
+
+func (b *RouterIDUpdateBody) DecodeFromBytes(data []byte, version uint8) error {
+ family := data[0]
+ var addrlen int8
+ switch family {
+ case syscall.AF_INET:
+ addrlen = net.IPv4len
+ case syscall.AF_INET6:
+ addrlen = net.IPv6len
+ default:
+ return fmt.Errorf("unknown address family: %d", family)
+ }
+ b.Prefix = data[1 : 1+addrlen]
+ b.Length = data[1+addrlen]
+ return nil
+}
+
+func (b *RouterIDUpdateBody) Serialize(version uint8) ([]byte, error) {
+ return []byte{}, nil
+}
+
+func (b *RouterIDUpdateBody) String() string {
+ return fmt.Sprintf("id: %s/%d", b.Prefix.String(), b.Length)
+}
+
+type IPRouteBody struct {
+ Type ROUTE_TYPE
+ Instance uint16
+ Flags FLAG
+ Message MESSAGE_FLAG
+ SAFI SAFI
+ Prefix net.IP
+ PrefixLength uint8
+ SrcPrefix net.IP
+ SrcPrefixLength uint8
+ Nexthops []net.IP
+ Ifindexs []uint32
+ Distance uint8
+ Metric uint32
+ Mtu uint32
+ Tag uint32
+ Api API_TYPE
+}
+
+func (b *IPRouteBody) RouteFamily() bgp.RouteFamily {
+ switch b.Api {
+ case IPV4_ROUTE_ADD, IPV4_ROUTE_DELETE, FRR_REDISTRIBUTE_IPV4_ADD, FRR_REDISTRIBUTE_IPV4_DEL:
+ return bgp.RF_IPv4_UC
+ case IPV6_ROUTE_ADD, IPV6_ROUTE_DELETE, FRR_REDISTRIBUTE_IPV6_ADD, FRR_REDISTRIBUTE_IPV6_DEL:
+ return bgp.RF_IPv6_UC
+ default:
+ return bgp.RF_OPAQUE
+ }
+}
+
+func (b *IPRouteBody) IsWithdraw() bool {
+ switch b.Api {
+ case IPV4_ROUTE_DELETE, FRR_REDISTRIBUTE_IPV4_DEL, IPV6_ROUTE_DELETE, FRR_REDISTRIBUTE_IPV6_DEL:
+ return true
+ default:
+ return false
+ }
+}
+
+func (b *IPRouteBody) Serialize(version uint8) ([]byte, error) {
+
+ var buf []byte
+ nhfIPv4 := uint8(NEXTHOP_IPV4)
+ nhfIPv6 := uint8(NEXTHOP_IPV6)
+ nhfIndx := uint8(NEXTHOP_IFINDEX)
+ nhfBlkH := uint8(NEXTHOP_BLACKHOLE)
+ if version <= 3 {
+ buf = make([]byte, 5)
+ buf[0] = uint8(b.Type)
+ buf[1] = uint8(b.Flags)
+ buf[2] = uint8(b.Message)
+ binary.BigEndian.PutUint16(buf[3:5], uint16(b.SAFI))
+ } else { // version >= 4
+ buf = make([]byte, 10)
+ buf[0] = uint8(b.Type)
+ binary.BigEndian.PutUint16(buf[1:3], uint16(b.Instance))
+ binary.BigEndian.PutUint32(buf[3:7], uint32(b.Flags))
+ buf[7] = uint8(b.Message)
+ binary.BigEndian.PutUint16(buf[8:10], uint16(b.SAFI))
+ nhfIPv4 = uint8(FRR_NEXTHOP_IPV4)
+ nhfIPv6 = uint8(FRR_NEXTHOP_IPV6)
+ nhfIndx = uint8(FRR_NEXTHOP_IFINDEX)
+ nhfBlkH = uint8(FRR_NEXTHOP_BLACKHOLE)
+ }
+ byteLen := (int(b.PrefixLength) + 7) / 8
+ buf = append(buf, b.PrefixLength)
+ buf = append(buf, b.Prefix[:byteLen]...)
+ if b.Message&FRR_MESSAGE_SRCPFX > 0 {
+ byteLen = (int(b.SrcPrefixLength) + 7) / 8
+ buf = append(buf, b.SrcPrefixLength)
+ buf = append(buf, b.SrcPrefix[:byteLen]...)
+ }
+
+ if b.Message&MESSAGE_NEXTHOP > 0 {
+ if b.Flags&FLAG_BLACKHOLE > 0 {
+ buf = append(buf, []byte{1, nhfBlkH}...)
+ } else {
+ buf = append(buf, uint8(len(b.Nexthops)+len(b.Ifindexs)))
+ }
+
+ for _, v := range b.Nexthops {
+ if v.To4() != nil {
+ buf = append(buf, nhfIPv4)
+ buf = append(buf, v.To4()...)
+ } else {
+ buf = append(buf, nhfIPv6)
+ buf = append(buf, v.To16()...)
+ }
+ }
+
+ for _, v := range b.Ifindexs {
+ buf = append(buf, nhfIndx)
+ bbuf := make([]byte, 4)
+ binary.BigEndian.PutUint32(bbuf, v)
+ buf = append(buf, bbuf...)
+ }
+ }
+
+ if b.Message&MESSAGE_DISTANCE > 0 {
+ buf = append(buf, b.Distance)
+ }
+ if b.Message&MESSAGE_METRIC > 0 {
+ bbuf := make([]byte, 4)
+ binary.BigEndian.PutUint32(bbuf, b.Metric)
+ buf = append(buf, bbuf...)
+ }
+ if version <= 3 {
+ if b.Message&MESSAGE_MTU > 0 {
+ bbuf := make([]byte, 4)
+ binary.BigEndian.PutUint32(bbuf, b.Mtu)
+ buf = append(buf, bbuf...)
+ }
+ if b.Message&MESSAGE_TAG > 0 {
+ bbuf := make([]byte, 4)
+ binary.BigEndian.PutUint32(bbuf, b.Tag)
+ buf = append(buf, bbuf...)
+ }
+ } else { // version >= 4
+ if b.Message&FRR_MESSAGE_TAG > 0 {
+ bbuf := make([]byte, 4)
+ binary.BigEndian.PutUint32(bbuf, b.Tag)
+ buf = append(buf, bbuf...)
+ }
+ if b.Message&FRR_MESSAGE_MTU > 0 {
+ bbuf := make([]byte, 4)
+ binary.BigEndian.PutUint32(bbuf, b.Mtu)
+ buf = append(buf, bbuf...)
+ }
+ }
+ return buf, nil
+}
+
+func (b *IPRouteBody) DecodeFromBytes(data []byte, version uint8) error {
+ isV4 := true
+ if version <= 3 {
+ isV4 = b.Api == IPV4_ROUTE_ADD || b.Api == IPV4_ROUTE_DELETE
+ } else {
+ isV4 = b.Api == FRR_REDISTRIBUTE_IPV4_ADD || b.Api == FRR_REDISTRIBUTE_IPV4_DEL
+ }
+ var addrLen uint8 = net.IPv4len
+ if !isV4 {
+ addrLen = net.IPv6len
+ }
+
+ b.Type = ROUTE_TYPE(data[0])
+ if version <= 3 {
+ b.Flags = FLAG(data[1])
+ data = data[2:]
+ } else { // version >= 4
+ b.Instance = binary.BigEndian.Uint16(data[1:3])
+ b.Flags = FLAG(binary.BigEndian.Uint32(data[3:7]))
+ data = data[7:]
+ }
+
+ b.Message = MESSAGE_FLAG(data[0])
+ b.SAFI = SAFI(SAFI_UNICAST)
+
+ b.PrefixLength = data[1]
+ if b.PrefixLength > addrLen*8 {
+ return fmt.Errorf("prefix length is greater than %d", addrLen*8)
+ }
+ pos := 2
+ buf := make([]byte, addrLen)
+ byteLen := int((b.PrefixLength + 7) / 8)
+ copy(buf, data[pos:pos+byteLen])
+ if isV4 {
+ b.Prefix = net.IP(buf).To4()
+ } else {
+ b.Prefix = net.IP(buf).To16()
+ }
+ pos += byteLen
+
+ if b.Message&FRR_MESSAGE_SRCPFX > 0 {
+ b.SrcPrefixLength = data[pos]
+ pos += 1
+ buf = make([]byte, addrLen)
+ byteLen = int((b.SrcPrefixLength + 7) / 8)
+ copy(buf, data[pos:pos+byteLen])
+ if isV4 {
+ b.SrcPrefix = net.IP(buf).To4()
+ } else {
+ b.SrcPrefix = net.IP(buf).To16()
+ }
+ pos += byteLen
+ }
+
+ rest := 0
+ var numNexthop int
+ if b.Message&MESSAGE_NEXTHOP > 0 {
+ numNexthop = int(data[pos])
+ // rest = numNexthop(1) + (nexthop(4 or 16) + placeholder(1) + ifindex(4)) * numNexthop
+ rest += 1 + numNexthop*(int(addrLen)+5)
+ }
+ if b.Message&MESSAGE_DISTANCE > 0 {
+ // distance(1)
+ rest += 1
+ }
+ if b.Message&MESSAGE_METRIC > 0 {
+ // metric(4)
+ rest += 4
+ }
+ if version <= 3 {
+ if b.Message&MESSAGE_MTU > 0 {
+ // mtu(4)
+ rest += 4
+ }
+ if b.Message&MESSAGE_TAG > 0 {
+ // tag(4)
+ rest += 4
+ }
+ } else { // version >= 4
+ if b.Message&FRR_MESSAGE_TAG > 0 {
+ // tag(4)
+ rest += 4
+ }
+ if b.Message&FRR_MESSAGE_MTU > 0 {
+ // mtu(4)
+ rest += 4
+ }
+ }
+
+ if len(data[pos:]) != rest {
+ return fmt.Errorf("message length invalid")
+ }
+
+ b.Nexthops = []net.IP{}
+ b.Ifindexs = []uint32{}
+
+ if b.Message&MESSAGE_NEXTHOP > 0 {
+ pos += 1
+ for i := 0; i < numNexthop; i++ {
+ addr := data[pos : pos+int(addrLen)]
+ var nexthop net.IP
+ if isV4 {
+ nexthop = net.IP(addr).To4()
+ } else {
+ nexthop = net.IP(addr).To16()
+ }
+ b.Nexthops = append(b.Nexthops, nexthop)
+
+ // skip nexthop and 1byte place holder
+ pos += int(addrLen + 1)
+ ifidx := binary.BigEndian.Uint32(data[pos : pos+4])
+ b.Ifindexs = append(b.Ifindexs, ifidx)
+ pos += 4
+ }
+ }
+
+ if b.Message&MESSAGE_DISTANCE > 0 {
+ b.Distance = data[pos]
+ pos += 1
+ }
+ if b.Message&MESSAGE_METRIC > 0 {
+ b.Metric = binary.BigEndian.Uint32(data[pos : pos+4])
+ pos += 4
+ }
+ if version <= 3 {
+ if b.Message&MESSAGE_MTU > 0 {
+ b.Mtu = binary.BigEndian.Uint32(data[pos : pos+4])
+ pos += 4
+ }
+ if b.Message&MESSAGE_TAG > 0 {
+ b.Tag = binary.BigEndian.Uint32(data[pos : pos+4])
+ pos += 4
+ }
+ } else {
+ if b.Message&FRR_MESSAGE_TAG > 0 {
+ b.Tag = binary.BigEndian.Uint32(data[pos : pos+4])
+ pos += 4
+ }
+ if b.Message&FRR_MESSAGE_MTU > 0 {
+ b.Mtu = binary.BigEndian.Uint32(data[pos : pos+4])
+ pos += 4
+ }
+ }
+
+ return nil
+}
+
+func (b *IPRouteBody) String() string {
+ s := fmt.Sprintf(
+ "type: %s, instance: %d, flags: %s, message: %d, safi: %s, prefix: %s/%d, src_prefix: %s/%d",
+ b.Type.String(), b.Instance, b.Flags.String(), b.Message, b.SAFI.String(), b.Prefix.String(), b.PrefixLength, b.SrcPrefix.String(), b.SrcPrefixLength)
+ for i, nh := range b.Nexthops {
+ s += fmt.Sprintf(", nexthops[%d]: %s", i, nh.String())
+ }
+ for i, idx := range b.Ifindexs {
+ s += fmt.Sprintf(", ifindex[%d]: %d", i, idx)
+ }
+ return s + fmt.Sprintf(
+ ", distance: %d, metric: %d, mtu: %d, tag: %d",
+ b.Distance, b.Metric, b.Mtu, b.Tag)
+}
+
+type NexthopLookupBody struct {
+ Api API_TYPE
+ Addr net.IP
+ Distance uint8
+ Metric uint32
+ Nexthops []*Nexthop
+}
+
+type Nexthop struct {
+ Ifname string
+ Ifindex uint32
+ Type NEXTHOP_FLAG
+ Addr net.IP
+}
+
+func (n *Nexthop) String() string {
+ s := fmt.Sprintf(
+ "type: %s, addr: %s, ifindex: %d, ifname: %s",
+ n.Type.String(), n.Addr.String(), n.Ifindex, n.Ifname)
+ return s
+}
+
+func decodeNexthopsFromBytes(nexthops *[]*Nexthop, data []byte, isV4 bool, version uint8) (int, error) {
+ addrLen := net.IPv4len
+ if !isV4 {
+ addrLen = net.IPv6len
+ }
+ nhIfindex := NEXTHOP_IFINDEX
+ nhIfname := NEXTHOP_IFNAME
+ nhIPv4 := NEXTHOP_IPV4
+ nhIPv4Ifindex := NEXTHOP_IPV4_IFINDEX
+ nhIPv4Ifname := NEXTHOP_IPV4_IFNAME
+ nhIPv6 := NEXTHOP_IPV6
+ nhIPv6Ifindex := NEXTHOP_IPV6_IFINDEX
+ nhIPv6Ifname := NEXTHOP_IPV6_IFNAME
+ if version >= 4 {
+ nhIfindex = FRR_NEXTHOP_IFINDEX
+ nhIfname = NEXTHOP_FLAG(0)
+ nhIPv4 = FRR_NEXTHOP_IPV4
+ nhIPv4Ifindex = FRR_NEXTHOP_IPV4_IFINDEX
+ nhIPv4Ifname = NEXTHOP_FLAG(0)
+ nhIPv6 = FRR_NEXTHOP_IPV6
+ nhIPv6Ifindex = FRR_NEXTHOP_IPV6_IFINDEX
+ nhIPv6Ifname = NEXTHOP_FLAG(0)
+ }
+
+ numNexthop := int(data[0])
+ offset := 1
+
+ for i := 0; i < numNexthop; i++ {
+ nh := &Nexthop{}
+ nh.Type = NEXTHOP_FLAG(data[offset])
+ offset += 1
+
+ switch nh.Type {
+ case nhIfindex, nhIfname:
+ nh.Ifindex = binary.BigEndian.Uint32(data[offset : offset+4])
+ offset += 4
+
+ case nhIPv4, nhIPv6:
+ if isV4 {
+ nh.Addr = net.IP(data[offset : offset+addrLen]).To4()
+ } else {
+ nh.Addr = net.IP(data[offset : offset+addrLen]).To16()
+ }
+ offset += addrLen
+ if version >= 4 {
+ // On FRRouting version 3.0 or later, NEXTHOP_IPV4 and
+ // NEXTHOP_IPV6 have the same structure with
+ // NEXTHOP_TYPE_IPV4_IFINDEX and NEXTHOP_TYPE_IPV6_IFINDEX.
+ nh.Ifindex = binary.BigEndian.Uint32(data[offset : offset+4])
+ offset += 4
+ }
+
+ case nhIPv4Ifindex, nhIPv4Ifname, nhIPv6Ifindex, nhIPv6Ifname:
+ if isV4 {
+ nh.Addr = net.IP(data[offset : offset+addrLen]).To4()
+ } else {
+ nh.Addr = net.IP(data[offset : offset+addrLen]).To16()
+ }
+ offset += addrLen
+ nh.Ifindex = binary.BigEndian.Uint32(data[offset : offset+4])
+ offset += 4
+ }
+ *nexthops = append(*nexthops, nh)
+ }
+
+ return offset, nil
+}
+
+func (b *NexthopLookupBody) Serialize(version uint8) ([]byte, error) {
+ isV4 := false
+ if version <= 3 {
+ isV4 = b.Api == IPV4_NEXTHOP_LOOKUP
+ } else { // version >= 4
+ isV4 = b.Api == FRR_IPV4_NEXTHOP_LOOKUP_MRIB
+ }
+
+ buf := make([]byte, 0)
+
+ if isV4 {
+ buf = append(buf, b.Addr.To4()...)
+ } else {
+ buf = append(buf, b.Addr.To16()...)
+ }
+ return buf, nil
+}
+
+func (b *NexthopLookupBody) DecodeFromBytes(data []byte, version uint8) error {
+ isV4 := false
+ if version <= 3 {
+ isV4 = b.Api == IPV4_NEXTHOP_LOOKUP
+ } else { // version >= 4
+ isV4 = b.Api == FRR_IPV4_NEXTHOP_LOOKUP_MRIB
+ }
+ addrLen := net.IPv4len
+ if !isV4 {
+ addrLen = net.IPv6len
+ }
+
+ if len(data) < addrLen {
+ return fmt.Errorf("message length invalid")
+ }
+
+ buf := make([]byte, addrLen)
+ copy(buf, data[0:addrLen])
+ pos := addrLen
+
+ if isV4 {
+ b.Addr = net.IP(buf).To4()
+ } else {
+ b.Addr = net.IP(buf).To16()
+ }
+
+ if version >= 4 {
+ b.Distance = data[pos]
+ pos++
+ }
+
+ if len(data[pos:]) > int(1+addrLen) {
+ b.Metric = binary.BigEndian.Uint32(data[pos : pos+4])
+ pos += 4
+ b.Nexthops = []*Nexthop{}
+ if nexthopsByteLen, err := decodeNexthopsFromBytes(&b.Nexthops, data[pos:], isV4, version); err != nil {
+ return err
+ } else {
+ pos += nexthopsByteLen
+ }
+ }
+
+ return nil
+}
+
+func (b *NexthopLookupBody) String() string {
+ s := fmt.Sprintf(
+ "addr: %s, distance:%d, metric: %d",
+ b.Addr.String(), b.Distance, b.Metric)
+ if len(b.Nexthops) > 0 {
+ for _, nh := range b.Nexthops {
+ s = s + fmt.Sprintf(", nexthop:{%s}", nh.String())
+ }
+ }
+ return s
+}
+
+type ImportLookupBody struct {
+ Api API_TYPE
+ PrefixLength uint8
+ Prefix net.IP
+ Addr net.IP
+ Metric uint32
+ Nexthops []*Nexthop
+}
+
+func (b *ImportLookupBody) Serialize(version uint8) ([]byte, error) {
+ buf := make([]byte, 1)
+ buf[0] = b.PrefixLength
+ buf = append(buf, b.Addr.To4()...)
+ return buf, nil
+}
+
+func (b *ImportLookupBody) DecodeFromBytes(data []byte, version uint8) error {
+ isV4 := b.Api == IPV4_IMPORT_LOOKUP
+ addrLen := net.IPv4len
+ if !isV4 {
+ addrLen = net.IPv6len
+ }
+
+ if len(data) < addrLen {
+ return fmt.Errorf("message length invalid")
+ }
+
+ buf := make([]byte, addrLen)
+ copy(buf, data[0:addrLen])
+ pos := addrLen
+
+ b.Addr = net.IP(buf).To4()
+
+ if len(data[pos:]) > int(1+addrLen) {
+ b.Metric = binary.BigEndian.Uint32(data[pos : pos+4])
+ pos += 4
+ b.Nexthops = []*Nexthop{}
+ if nexthopsByteLen, err := decodeNexthopsFromBytes(&b.Nexthops, data[pos:], isV4, version); err != nil {
+ return err
+ } else {
+ pos += nexthopsByteLen
+ }
+ }
+
+ return nil
+}
+
+func (b *ImportLookupBody) String() string {
+ s := fmt.Sprintf(
+ "prefix: %s/%d, addr: %s, metric: %d",
+ b.Prefix.String(), b.PrefixLength, b.Addr.String(), b.Metric)
+ if len(b.Nexthops) > 0 {
+ for _, nh := range b.Nexthops {
+ s = s + fmt.Sprintf(", nexthop:{%s}", nh.String())
+ }
+ }
+ return s
+}
+
+type RegisteredNexthop struct {
+ Connected uint8
+ Family uint16
+ // Note: Ignores PrefixLength (uint8),
+ // because this field should be always:
+ // - 32 if Address Family is AF_INET
+ // - 128 if Address Family is AF_INET6
+ Prefix net.IP
+}
+
+func (n *RegisteredNexthop) Len() int {
+ // Connected (1 byte) + Address Family (2 bytes) + Prefix Length (1 byte) + Prefix (variable)
+ if n.Family == uint16(syscall.AF_INET) {
+ return 4 + net.IPv4len
+ } else {
+ return 4 + net.IPv6len
+ }
+}
+
+func (n *RegisteredNexthop) Serialize() ([]byte, error) {
+ // Connected (1 byte)
+ buf := make([]byte, 4)
+ buf[0] = byte(n.Connected)
+
+ // Address Family (2 bytes)
+ binary.BigEndian.PutUint16(buf[1:3], n.Family)
+
+ // Prefix Length (1 byte) + Prefix (variable)
+ switch n.Family {
+ case uint16(syscall.AF_INET):
+ buf[3] = byte(net.IPv4len * 8)
+ buf = append(buf, n.Prefix.To4()...)
+ case uint16(syscall.AF_INET6):
+ buf[3] = byte(net.IPv6len * 8)
+ buf = append(buf, n.Prefix.To16()...)
+ default:
+ return nil, fmt.Errorf("invalid address family: %d", n.Family)
+ }
+
+ return buf, nil
+}
+
+func (n *RegisteredNexthop) DecodeFromBytes(data []byte) error {
+ // Connected (1 byte)
+ n.Connected = uint8(data[0])
+ offset := 1
+
+ // Address Family (2 bytes)
+ n.Family = binary.BigEndian.Uint16(data[offset : offset+2])
+ isV4 := n.Family == uint16(syscall.AF_INET)
+ addrLen := int(net.IPv4len)
+ if !isV4 {
+ addrLen = net.IPv6len
+ }
+ // Note: Ignores Prefix Length (1 byte)
+ offset += 3
+
+ // Prefix (variable)
+ if isV4 {
+ n.Prefix = net.IP(data[offset : offset+addrLen]).To4()
+ } else {
+ n.Prefix = net.IP(data[offset : offset+addrLen]).To16()
+ }
+
+ return nil
+}
+
+func (n *RegisteredNexthop) String() string {
+ return fmt.Sprintf(
+ "connected: %d, family: %d, prefix: %s",
+ n.Connected, n.Family, n.Prefix.String())
+}
+
+type NexthopRegisterBody struct {
+ Api API_TYPE
+ Nexthops []*RegisteredNexthop
+}
+
+func (b *NexthopRegisterBody) Serialize(version uint8) ([]byte, error) {
+ buf := make([]byte, 0)
+
+ // List of Registered Nexthops
+ for _, nh := range b.Nexthops {
+ nhBuf, err := nh.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ buf = append(buf, nhBuf...)
+ }
+
+ return buf, nil
+}
+
+func (b *NexthopRegisterBody) DecodeFromBytes(data []byte, version uint8) error {
+ offset := 0
+
+ // List of Registered Nexthops
+ b.Nexthops = []*RegisteredNexthop{}
+ for len(data[offset:]) > 0 {
+ nh := new(RegisteredNexthop)
+ err := nh.DecodeFromBytes(data[offset:])
+ if err != nil {
+ return err
+ }
+ b.Nexthops = append(b.Nexthops, nh)
+
+ offset += nh.Len()
+ if len(data) < offset {
+ break
+ }
+ }
+
+ return nil
+}
+
+func (b *NexthopRegisterBody) String() string {
+ s := make([]string, 0)
+ for _, nh := range b.Nexthops {
+ s = append(s, fmt.Sprintf("nexthop:{%s}", nh.String()))
+ }
+ return strings.Join(s, ", ")
+}
+
+type NexthopUpdateBody struct {
+ Api API_TYPE
+ Family uint16
+ // Note: Ignores PrefixLength (uint8),
+ // because this field should be always:
+ // - 32 if Address Family is AF_INET
+ // - 128 if Address Family is AF_INET6
+ Prefix net.IP
+ Distance uint8
+ Metric uint32
+ Nexthops []*Nexthop
+}
+
+func (b *NexthopUpdateBody) Serialize(version uint8) ([]byte, error) {
+ // Address Family (2 bytes)
+ buf := make([]byte, 3)
+ binary.BigEndian.PutUint16(buf, b.Family)
+
+ // Prefix Length (1 byte) + Prefix (variable)
+ switch b.Family {
+ case uint16(syscall.AF_INET):
+ buf[2] = byte(net.IPv4len * 8)
+ buf = append(buf, b.Prefix.To4()...)
+ case uint16(syscall.AF_INET6):
+ buf[2] = byte(net.IPv6len * 8)
+ buf = append(buf, b.Prefix.To16()...)
+ default:
+ return nil, fmt.Errorf("invalid address family: %d", b.Family)
+ }
+
+ return buf, nil
+}
+
+func (b *NexthopUpdateBody) DecodeFromBytes(data []byte, version uint8) error {
+ // Address Family (2 bytes)
+ b.Family = binary.BigEndian.Uint16(data[0:2])
+ isV4 := b.Family == uint16(syscall.AF_INET)
+ addrLen := int(net.IPv4len)
+ if !isV4 {
+ addrLen = net.IPv6len
+ }
+ // Note: Ignores Prefix Length (1 byte)
+ offset := 3
+
+ // Prefix (variable)
+ if isV4 {
+ b.Prefix = net.IP(data[offset : offset+addrLen]).To4()
+ } else {
+ b.Prefix = net.IP(data[offset : offset+addrLen]).To16()
+ }
+ offset += addrLen
+
+ // Distance (1 byte) (if version>=4)
+ // Metric (4 bytes)
+ // Number of Nexthops (1 byte)
+ if version >= 4 {
+ if len(data[offset:]) < 6 {
+ return fmt.Errorf("invalid message length: missing distance(1 byte), metric(4 bytes) or nexthops(1 byte): %d<6", len(data[offset:]))
+ }
+ b.Distance = data[offset]
+ offset += 1
+ } else if len(data[offset:]) < 5 {
+ return fmt.Errorf("invalid message length: missing metric(4 bytes) or nexthops(1 byte): %d<5", len(data[offset:]))
+ }
+ b.Metric = binary.BigEndian.Uint32(data[offset : offset+4])
+ offset += 4
+
+ // List of Nexthops
+ b.Nexthops = []*Nexthop{}
+ if nexthopsByteLen, err := decodeNexthopsFromBytes(&b.Nexthops, data[offset:], isV4, version); err != nil {
+ return err
+ } else {
+ offset += nexthopsByteLen
+ }
+
+ return nil
+}
+
+func (b *NexthopUpdateBody) String() string {
+ s := fmt.Sprintf(
+ "family: %d, prefix: %s, distance: %d, metric: %d",
+ b.Family, b.Prefix.String(), b.Distance, b.Metric)
+ for _, nh := range b.Nexthops {
+ s = s + fmt.Sprintf(", nexthop:{%s}", nh.String())
+ }
+ return s
+}
+
+type Message struct {
+ Header Header
+ Body Body
+}
+
+func (m *Message) Serialize() ([]byte, error) {
+ var body []byte
+ if m.Body != nil {
+ var err error
+ body, err = m.Body.Serialize(m.Header.Version)
+ if err != nil {
+ return nil, err
+ }
+ }
+ m.Header.Len = uint16(len(body)) + HeaderSize(m.Header.Version)
+ hdr, err := m.Header.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ return append(hdr, body...), nil
+}
+
+func (m *Message) parseMessage(data []byte) error {
+ switch m.Header.Command {
+ case INTERFACE_ADD, INTERFACE_DELETE, INTERFACE_UP, INTERFACE_DOWN:
+ m.Body = &InterfaceUpdateBody{}
+ case INTERFACE_ADDRESS_ADD, INTERFACE_ADDRESS_DELETE:
+ m.Body = &InterfaceAddressUpdateBody{}
+ case ROUTER_ID_UPDATE:
+ m.Body = &RouterIDUpdateBody{}
+ case IPV4_ROUTE_ADD, IPV6_ROUTE_ADD, IPV4_ROUTE_DELETE, IPV6_ROUTE_DELETE:
+ m.Body = &IPRouteBody{Api: m.Header.Command}
+ case IPV4_NEXTHOP_LOOKUP, IPV6_NEXTHOP_LOOKUP:
+ m.Body = &NexthopLookupBody{Api: m.Header.Command}
+ case IPV4_IMPORT_LOOKUP:
+ m.Body = &ImportLookupBody{Api: m.Header.Command}
+ case NEXTHOP_UPDATE:
+ m.Body = &NexthopUpdateBody{Api: m.Header.Command}
+ default:
+ m.Body = &UnknownBody{}
+ }
+ return m.Body.DecodeFromBytes(data, m.Header.Version)
+}
+
+func (m *Message) parseFrrMessage(data []byte) error {
+ switch m.Header.Command {
+ case FRR_INTERFACE_ADD, FRR_INTERFACE_DELETE, FRR_INTERFACE_UP, FRR_INTERFACE_DOWN:
+ m.Body = &InterfaceUpdateBody{}
+ case FRR_INTERFACE_ADDRESS_ADD, FRR_INTERFACE_ADDRESS_DELETE:
+ m.Body = &InterfaceAddressUpdateBody{}
+ case FRR_ROUTER_ID_UPDATE:
+ m.Body = &RouterIDUpdateBody{}
+ case FRR_NEXTHOP_UPDATE:
+ m.Body = &NexthopUpdateBody{}
+ case FRR_INTERFACE_NBR_ADDRESS_ADD, FRR_INTERFACE_NBR_ADDRESS_DELETE:
+ // TODO
+ m.Body = &UnknownBody{}
+ case FRR_INTERFACE_BFD_DEST_UPDATE:
+ // TODO
+ m.Body = &UnknownBody{}
+ case FRR_IMPORT_CHECK_UPDATE:
+ // TODO
+ m.Body = &UnknownBody{}
+ case FRR_BFD_DEST_REPLAY:
+ // TODO
+ m.Body = &UnknownBody{}
+ case FRR_REDISTRIBUTE_IPV4_ADD, FRR_REDISTRIBUTE_IPV4_DEL, FRR_REDISTRIBUTE_IPV6_ADD, FRR_REDISTRIBUTE_IPV6_DEL:
+ m.Body = &IPRouteBody{Api: m.Header.Command}
+ case FRR_INTERFACE_VRF_UPDATE:
+ // TODO
+ m.Body = &UnknownBody{}
+ case FRR_INTERFACE_LINK_PARAMS:
+ // TODO
+ m.Body = &UnknownBody{}
+ case FRR_PW_STATUS_UPDATE:
+ // TODO
+ m.Body = &UnknownBody{}
+ default:
+ m.Body = &UnknownBody{}
+ }
+ return m.Body.DecodeFromBytes(data, m.Header.Version)
+}
+
+func ParseMessage(hdr *Header, data []byte) (m *Message, err error) {
+ m = &Message{Header: *hdr}
+ if m.Header.Version == 4 {
+ err = m.parseFrrMessage(data)
+ } else {
+ err = m.parseMessage(data)
+ }
+ if err != nil {
+ return nil, err
+ }
+ return m, nil
+}
diff --git a/internal/pkg/zebra/zapi_bsd.go b/internal/pkg/zebra/zapi_bsd.go
new file mode 100644
index 00000000..8960e796
--- /dev/null
+++ b/internal/pkg/zebra/zapi_bsd.go
@@ -0,0 +1,58 @@
+// Copyright (C) 2014, 2015 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build freebsd netbsd openbsd
+
+package zebra
+
+import (
+ "strings"
+ "syscall"
+)
+
+func intfflag2string(flag uint64) string {
+ ss := make([]string, 0, 10)
+ if flag&syscall.IFF_UP > 0 {
+ ss = append(ss, "UP")
+ }
+ if flag&syscall.IFF_BROADCAST > 0 {
+ ss = append(ss, "BROADCAST")
+ }
+ if flag&syscall.IFF_DEBUG > 0 {
+ ss = append(ss, "DEBUG")
+ }
+ if flag&syscall.IFF_LOOPBACK > 0 {
+ ss = append(ss, "LOOPBACK")
+ }
+ if flag&syscall.IFF_POINTOPOINT > 0 {
+ ss = append(ss, "POINTOPOINT")
+ }
+ if flag&syscall.IFF_RUNNING > 0 {
+ ss = append(ss, "RUNNING")
+ }
+ if flag&syscall.IFF_NOARP > 0 {
+ ss = append(ss, "NOARP")
+ }
+ if flag&syscall.IFF_PROMISC > 0 {
+ ss = append(ss, "PROMISC")
+ }
+ if flag&syscall.IFF_ALLMULTI > 0 {
+ ss = append(ss, "ALLMULTI")
+ }
+ if flag&syscall.IFF_MULTICAST > 0 {
+ ss = append(ss, "MULTICAST")
+ }
+ return strings.Join(ss, " | ")
+}
diff --git a/internal/pkg/zebra/zapi_darwin.go b/internal/pkg/zebra/zapi_darwin.go
new file mode 100644
index 00000000..a2536913
--- /dev/null
+++ b/internal/pkg/zebra/zapi_darwin.go
@@ -0,0 +1,59 @@
+// Copyright (C) 2014, 2015 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package zebra
+
+import (
+ "strings"
+ "syscall"
+)
+
+func intfflag2string(flag uint64) string {
+ ss := make([]string, 0, 10)
+ if flag&syscall.IFF_UP > 0 {
+ ss = append(ss, "UP")
+ }
+ if flag&syscall.IFF_BROADCAST > 0 {
+ ss = append(ss, "BROADCAST")
+ }
+ if flag&syscall.IFF_DEBUG > 0 {
+ ss = append(ss, "DEBUG")
+ }
+ if flag&syscall.IFF_LOOPBACK > 0 {
+ ss = append(ss, "LOOPBACK")
+ }
+ if flag&syscall.IFF_POINTOPOINT > 0 {
+ ss = append(ss, "POINTOPOINT")
+ }
+ if flag&syscall.IFF_NOTRAILERS > 0 {
+ ss = append(ss, "NOTRAILERS")
+ }
+ if flag&syscall.IFF_RUNNING > 0 {
+ ss = append(ss, "RUNNING")
+ }
+ if flag&syscall.IFF_NOARP > 0 {
+ ss = append(ss, "NOARP")
+ }
+ if flag&syscall.IFF_PROMISC > 0 {
+ ss = append(ss, "PROMISC")
+ }
+ if flag&syscall.IFF_ALLMULTI > 0 {
+ ss = append(ss, "ALLMULTI")
+ }
+ if flag&syscall.IFF_MULTICAST > 0 {
+ ss = append(ss, "MULTICAST")
+ }
+ return strings.Join(ss, " | ")
+}
diff --git a/internal/pkg/zebra/zapi_linux.go b/internal/pkg/zebra/zapi_linux.go
new file mode 100644
index 00000000..66fccb74
--- /dev/null
+++ b/internal/pkg/zebra/zapi_linux.go
@@ -0,0 +1,83 @@
+// Copyright (C) 2014, 2015 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package zebra
+
+import (
+ "strings"
+ "syscall"
+)
+
+func intfflag2string(flag uint64) string {
+ ss := make([]string, 0, 10)
+ if flag&syscall.IFF_UP > 0 {
+ ss = append(ss, "UP")
+ }
+ if flag&syscall.IFF_BROADCAST > 0 {
+ ss = append(ss, "BROADCAST")
+ }
+ if flag&syscall.IFF_DEBUG > 0 {
+ ss = append(ss, "DEBUG")
+ }
+ if flag&syscall.IFF_LOOPBACK > 0 {
+ ss = append(ss, "LOOPBACK")
+ }
+ if flag&syscall.IFF_POINTOPOINT > 0 {
+ ss = append(ss, "POINTOPOINT")
+ }
+ if flag&syscall.IFF_NOTRAILERS > 0 {
+ ss = append(ss, "NOTRAILERS")
+ }
+ if flag&syscall.IFF_RUNNING > 0 {
+ ss = append(ss, "RUNNING")
+ }
+ if flag&syscall.IFF_NOARP > 0 {
+ ss = append(ss, "NOARP")
+ }
+ if flag&syscall.IFF_PROMISC > 0 {
+ ss = append(ss, "PROMISC")
+ }
+ if flag&syscall.IFF_ALLMULTI > 0 {
+ ss = append(ss, "ALLMULTI")
+ }
+ if flag&syscall.IFF_MASTER > 0 {
+ ss = append(ss, "MASTER")
+ }
+ if flag&syscall.IFF_SLAVE > 0 {
+ ss = append(ss, "SLAVE")
+ }
+ if flag&syscall.IFF_MULTICAST > 0 {
+ ss = append(ss, "MULTICAST")
+ }
+ if flag&syscall.IFF_PORTSEL > 0 {
+ ss = append(ss, "PORTSEL")
+ }
+ if flag&syscall.IFF_AUTOMEDIA > 0 {
+ ss = append(ss, "AUTOMEDIA")
+ }
+ if flag&syscall.IFF_DYNAMIC > 0 {
+ ss = append(ss, "DYNAMIC")
+ }
+ // if flag&syscall.IFF_LOWER_UP > 0 {
+ // ss = append(ss, "LOWER_UP")
+ // }
+ // if flag&syscall.IFF_DORMANT > 0 {
+ // ss = append(ss, "DORMANT")
+ // }
+ // if flag&syscall.IFF_ECHO > 0 {
+ // ss = append(ss, "ECHO")
+ // }
+ return strings.Join(ss, " | ")
+}
diff --git a/internal/pkg/zebra/zapi_test.go b/internal/pkg/zebra/zapi_test.go
new file mode 100644
index 00000000..9fda5416
--- /dev/null
+++ b/internal/pkg/zebra/zapi_test.go
@@ -0,0 +1,531 @@
+// Copyright (C) 2014, 2015 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package zebra
+
+import (
+ "encoding/binary"
+ "net"
+ "syscall"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_Header(t *testing.T) {
+ assert := assert.New(t)
+
+ //DecodeFromBytes
+ buf := make([]byte, 6)
+ binary.BigEndian.PutUint16(buf[0:], 10)
+ buf[2] = HEADER_MARKER
+ buf[3] = 2
+ binary.BigEndian.PutUint16(buf[4:], uint16(IPV4_ROUTE_ADD))
+ h := &Header{}
+ err := h.DecodeFromBytes(buf)
+ assert.Equal(nil, err)
+
+ //Serialize
+ buf, err = h.Serialize()
+ assert.Equal(nil, err)
+ h2 := &Header{}
+ err = h2.DecodeFromBytes(buf)
+ assert.Equal(nil, err)
+ assert.Equal(h, h2)
+
+ // header_size mismatch
+ buf = make([]byte, HeaderSize(2)-1)
+ binary.BigEndian.PutUint16(buf[0:], 10)
+ buf[2] = 0xff
+ buf[3] = 0x02
+ h3 := &Header{}
+ err = h3.DecodeFromBytes(buf)
+ assert.NotEqual(nil, err)
+}
+
+func Test_InterfaceUpdateBody(t *testing.T) {
+ assert := assert.New(t)
+
+ //DecodeFromBytes
+ buf := make([]byte, INTERFACE_NAMSIZ+49)
+ pos := INTERFACE_NAMSIZ
+ binary.BigEndian.PutUint32(buf[pos:], 1)
+ pos += 4
+ buf[pos] = byte(INTERFACE_ACTIVE)
+ pos += 1
+ binary.BigEndian.PutUint64(buf[pos:], 1)
+ pos += 8 // flags
+ binary.BigEndian.PutUint32(buf[pos:], 1)
+ pos += 4 // metric
+ binary.BigEndian.PutUint32(buf[pos:], 1500)
+ pos += 4 // MTU
+ binary.BigEndian.PutUint32(buf[pos:], 1500)
+ pos += 4 // MTU6
+ binary.BigEndian.PutUint32(buf[pos:], 200)
+ pos += 4 // bandwidth
+ binary.BigEndian.PutUint32(buf[pos:], 6)
+ pos += 4 // hwaddr_len
+ mac, _ := net.ParseMAC("01:23:45:67:89:ab")
+ copy(buf[pos:pos+6], []byte(mac))
+ pos += 4
+ b := &InterfaceUpdateBody{}
+ err := b.DecodeFromBytes(buf, 2)
+ assert.Equal(nil, err)
+ assert.Equal("01:23:45:67:89:ab", b.HardwareAddr.String())
+
+ buf = make([]byte, INTERFACE_NAMSIZ+28)
+ b = &InterfaceUpdateBody{}
+ err = b.DecodeFromBytes(buf, 2)
+ assert.NotEqual(nil, err)
+}
+
+func Test_InterfaceAddressUpdateBody(t *testing.T) {
+ assert := assert.New(t)
+
+ //DecodeFromBytes
+ buf := make([]byte, 15)
+ pos := 0
+ binary.BigEndian.PutUint32(buf[pos:], 0) // index
+ pos += 4
+ buf[pos] = 0x01 // flags
+ pos += 1
+ buf[pos] = 0x2 // family
+ pos += 1
+ ip := net.ParseIP("192.168.100.1").To4() // prefix
+ copy(buf[pos:pos+4], []byte(ip))
+ pos += 4
+ buf[pos] = byte(24) // prefix len
+ pos += 1
+ dst := net.ParseIP("192.168.100.255").To4() // destination
+ copy(buf[pos:pos+4], []byte(dst))
+
+ b := &InterfaceAddressUpdateBody{}
+ err := b.DecodeFromBytes(buf, 2)
+ require.NoError(t, err)
+
+ assert.Equal(uint32(0), b.Index)
+ assert.Equal(INTERFACE_ADDRESS_FLAG(1), b.Flags)
+ assert.Equal("192.168.100.1", b.Prefix.String())
+ assert.Equal(uint8(24), b.Length)
+ assert.Equal("192.168.100.255", b.Destination.String())
+
+ // af invalid
+ buf[5] = 0x4
+ pos += 1
+ b = &InterfaceAddressUpdateBody{}
+ err = b.DecodeFromBytes(buf, 2)
+ assert.NotEqual(nil, err)
+}
+
+func Test_RouterIDUpdateBody(t *testing.T) {
+ assert := assert.New(t)
+
+ //DecodeFromBytes
+ buf := make([]byte, 6)
+ pos := 0
+ buf[pos] = 0x2
+ pos += 1
+ ip := net.ParseIP("192.168.100.1").To4()
+ copy(buf[pos:pos+4], []byte(ip))
+ pos += 4
+ buf[pos] = byte(32)
+
+ b := &RouterIDUpdateBody{}
+ err := b.DecodeFromBytes(buf, 2)
+ assert.Equal(nil, err)
+ assert.Equal("192.168.100.1", b.Prefix.String())
+ assert.Equal(uint8(32), b.Length)
+
+ // af invalid
+ buf[0] = 0x4
+ pos += 1
+ b = &RouterIDUpdateBody{}
+ err = b.DecodeFromBytes(buf, 2)
+ assert.NotEqual(nil, err)
+}
+
+func Test_IPRouteBody_IPv4(t *testing.T) {
+ assert := assert.New(t)
+
+ //DecodeFromBytes IPV4_ROUTE
+ buf := make([]byte, 26)
+ buf[0] = byte(ROUTE_CONNECT)
+ buf[1] = byte(FLAG_SELECTED)
+ buf[2] = byte(MESSAGE_NEXTHOP | MESSAGE_DISTANCE | MESSAGE_METRIC | MESSAGE_MTU)
+ buf[3] = 24
+ ip := net.ParseIP("192.168.100.0").To4()
+ copy(buf[4:7], []byte(ip))
+
+ buf[7] = 1
+ nexthop := net.ParseIP("0.0.0.0").To4()
+ copy(buf[8:12], []byte(nexthop))
+
+ buf[12] = 1
+ binary.BigEndian.PutUint32(buf[13:], 1)
+ buf[17] = 0 // distance
+ binary.BigEndian.PutUint32(buf[18:], 1)
+ binary.BigEndian.PutUint32(buf[22:], 1)
+ r := &IPRouteBody{Api: IPV4_ROUTE_ADD}
+ err := r.DecodeFromBytes(buf, 2)
+
+ assert.Equal(nil, err)
+ assert.Equal("192.168.100.0", r.Prefix.String())
+ assert.Equal(uint8(0x18), r.PrefixLength)
+ assert.Equal(MESSAGE_NEXTHOP|MESSAGE_DISTANCE|MESSAGE_METRIC|MESSAGE_MTU, r.Message)
+ assert.Equal("0.0.0.0", r.Nexthops[0].String())
+ assert.Equal(uint32(1), r.Ifindexs[0])
+ assert.Equal(uint8(0), r.Distance)
+ assert.Equal(uint32(1), r.Metric)
+ assert.Equal(uint32(1), r.Mtu)
+
+ //Serialize
+ buf, err = r.Serialize(2)
+ assert.Equal(nil, err)
+ assert.Equal([]byte{0x2, 0x10, 0x1d}, buf[0:3])
+ assert.Equal([]byte{0x0, 0x1}, buf[3:5])
+ assert.Equal(byte(24), buf[5])
+ ip = net.ParseIP("192.168.100.0").To4()
+ assert.Equal([]byte(ip)[0:3], buf[6:9])
+ assert.Equal(byte(NEXTHOP_IPV4), buf[10])
+ assert.Equal(byte(NEXTHOP_IFINDEX), buf[15])
+ assert.Equal(byte(0x0), buf[20])
+
+ bi := make([]byte, 4)
+ binary.BigEndian.PutUint32(bi, 1)
+ assert.Equal(bi, buf[21:25])
+ assert.Equal(bi, buf[25:])
+
+ // length invalid
+ buf = make([]byte, 18)
+ buf[0] = byte(ROUTE_CONNECT)
+ buf[1] = byte(FLAG_SELECTED)
+ buf[2] = byte(MESSAGE_NEXTHOP | MESSAGE_DISTANCE | MESSAGE_METRIC)
+ buf[3] = 24
+ ip = net.ParseIP("192.168.100.0").To4()
+ copy(buf[4:7], []byte(ip))
+ buf[7] = 1
+ nexthop = net.ParseIP("0.0.0.0").To4()
+ copy(buf[8:12], []byte(nexthop))
+ buf[12] = 1
+ binary.BigEndian.PutUint32(buf[13:], 1)
+
+ r = &IPRouteBody{Api: IPV4_ROUTE_ADD}
+ err = r.DecodeFromBytes(buf, 2)
+ assert.Equal("message length invalid", err.Error())
+
+ // no nexthop
+ buf = make([]byte, 12)
+ buf[0] = byte(ROUTE_CONNECT)
+ buf[1] = byte(FLAG_SELECTED)
+ buf[2] = byte(MESSAGE_DISTANCE | MESSAGE_METRIC)
+ buf[3] = 24
+ ip = net.ParseIP("192.168.100.0").To4()
+ copy(buf[4:7], []byte(ip))
+ buf[7] = 1
+ binary.BigEndian.PutUint32(buf[8:], 0)
+ r = &IPRouteBody{Api: IPV4_ROUTE_ADD}
+ err = r.DecodeFromBytes(buf, 2)
+ assert.Equal(nil, err)
+
+}
+
+func Test_IPRouteBody_IPv6(t *testing.T) {
+ assert := assert.New(t)
+
+ //DecodeFromBytes IPV6_ROUTE
+ buf := make([]byte, 43)
+ buf[0] = byte(ROUTE_CONNECT)
+ buf[1] = byte(FLAG_SELECTED)
+ buf[2] = byte(MESSAGE_NEXTHOP | MESSAGE_DISTANCE | MESSAGE_METRIC | MESSAGE_MTU)
+ buf[3] = 64
+ ip := net.ParseIP("2001:db8:0:f101::").To16()
+ copy(buf[4:12], []byte(ip))
+
+ buf[12] = 1
+ nexthop := net.ParseIP("::").To16()
+ copy(buf[13:29], []byte(nexthop))
+ // ifindex
+ buf[29] = 1
+ binary.BigEndian.PutUint32(buf[30:], 1)
+
+ buf[34] = 0 // distance
+ binary.BigEndian.PutUint32(buf[35:], 1)
+ binary.BigEndian.PutUint32(buf[39:], 1)
+ r := &IPRouteBody{Api: IPV6_ROUTE_ADD}
+ err := r.DecodeFromBytes(buf, 2)
+
+ assert.Equal(nil, err)
+ assert.Equal("2001:db8:0:f101::", r.Prefix.String())
+ assert.Equal(uint8(64), r.PrefixLength)
+ assert.Equal(MESSAGE_NEXTHOP|MESSAGE_DISTANCE|MESSAGE_METRIC|MESSAGE_MTU, r.Message)
+ assert.Equal("::", r.Nexthops[0].String())
+ assert.Equal(uint32(1), r.Ifindexs[0])
+ assert.Equal(uint8(0), r.Distance)
+ assert.Equal(uint32(1), r.Metric)
+ assert.Equal(uint32(1), r.Mtu)
+
+ //Serialize
+ buf, err = r.Serialize(2)
+ assert.Equal(nil, err)
+ assert.Equal([]byte{0x2, 0x10, 0x1d}, buf[0:3])
+ assert.Equal([]byte{0x0, 0x1}, buf[3:5])
+ assert.Equal(byte(64), buf[5])
+ ip = net.ParseIP("2001:db8:0:f101::").To16()
+ assert.Equal([]byte(ip)[0:8], buf[6:14])
+ assert.Equal(byte(2), buf[14])
+ assert.Equal(byte(NEXTHOP_IPV6), buf[15])
+ ip = net.ParseIP("::").To16()
+ assert.Equal([]byte(ip), buf[16:32])
+ assert.Equal(byte(NEXTHOP_IFINDEX), buf[32])
+ bi := make([]byte, 4)
+ binary.BigEndian.PutUint32(bi, 1)
+ assert.Equal(bi, buf[33:37])
+
+ //distance
+ assert.Equal(byte(0), buf[37])
+ bi = make([]byte, 4)
+ binary.BigEndian.PutUint32(bi, 1)
+ assert.Equal(bi, buf[38:42])
+ assert.Equal(bi, buf[42:])
+
+ // length invalid
+ buf = make([]byte, 50)
+ buf[0] = byte(ROUTE_CONNECT)
+ buf[1] = byte(FLAG_SELECTED)
+ buf[2] = byte(MESSAGE_NEXTHOP | MESSAGE_DISTANCE | MESSAGE_METRIC)
+ buf[3] = 24
+ ip = net.ParseIP("2001:db8:0:f101::").To4()
+ copy(buf[4:12], []byte(ip))
+ buf[13] = 1
+ nexthop = net.ParseIP("::").To16()
+ copy(buf[14:30], []byte(nexthop))
+ buf[31] = 1
+ binary.BigEndian.PutUint32(buf[32:], 1)
+
+ r = &IPRouteBody{Api: IPV6_ROUTE_ADD}
+ err = r.DecodeFromBytes(buf, 2)
+ assert.Equal("message length invalid", err.Error())
+
+ // no nexthop
+ buf = make([]byte, 11)
+ buf[0] = byte(ROUTE_CONNECT)
+ buf[1] = byte(FLAG_SELECTED)
+ buf[2] = byte(MESSAGE_DISTANCE | MESSAGE_METRIC)
+ buf[3] = 16
+ ip = net.ParseIP("2501::").To16()
+ copy(buf[4:6], []byte(ip))
+ buf[6] = 1
+ binary.BigEndian.PutUint32(buf[7:], 0)
+ r = &IPRouteBody{Api: IPV6_ROUTE_ADD}
+ err = r.DecodeFromBytes(buf, 2)
+ assert.Equal(nil, err)
+}
+
+func Test_NexthopLookupBody(t *testing.T) {
+ assert := assert.New(t)
+
+ //ipv4
+ //DecodeFromBytes
+ pos := 0
+ buf := make([]byte, 18)
+ ip := net.ParseIP("192.168.50.0").To4()
+ copy(buf[0:4], []byte(ip))
+ pos += 4
+ binary.BigEndian.PutUint32(buf[pos:], 10)
+ pos += 4
+ buf[pos] = byte(1)
+ pos += 1
+ buf[pos] = byte(4)
+ pos += 1
+ ip = net.ParseIP("172.16.1.101").To4()
+ copy(buf[pos:pos+4], []byte(ip))
+ pos += 4
+ binary.BigEndian.PutUint32(buf[pos:], 3)
+
+ b := &NexthopLookupBody{Api: IPV4_NEXTHOP_LOOKUP}
+ err := b.DecodeFromBytes(buf, 2)
+ assert.Equal(nil, err)
+ assert.Equal("192.168.50.0", b.Addr.String())
+ assert.Equal(uint32(10), b.Metric)
+ assert.Equal(uint32(3), b.Nexthops[0].Ifindex)
+ assert.Equal(NEXTHOP_FLAG(4), b.Nexthops[0].Type)
+ assert.Equal("172.16.1.101", b.Nexthops[0].Addr.String())
+
+ //Serialize
+ buf, err = b.Serialize(2)
+ ip = net.ParseIP("192.168.50.0").To4()
+ assert.Equal(nil, err)
+ assert.Equal([]byte(ip)[0:4], buf[0:4])
+
+ // length invalid
+ buf = make([]byte, 3)
+ b = &NexthopLookupBody{Api: IPV4_NEXTHOP_LOOKUP}
+ err = b.DecodeFromBytes(buf, 2)
+ assert.NotEqual(nil, err)
+
+ //ipv6
+ //DecodeFromBytes
+ pos = 0
+ buf = make([]byte, 46)
+ ip = net.ParseIP("2001:db8:0:f101::").To16()
+ copy(buf[0:16], []byte(ip))
+ pos += 16
+ binary.BigEndian.PutUint32(buf[pos:], 10)
+ pos += 4
+ buf[pos] = byte(1)
+ pos += 1
+ buf[pos] = byte(4)
+ pos += 1
+ ip = net.ParseIP("2001:db8:0:1111::1").To16()
+ copy(buf[pos:pos+16], []byte(ip))
+ pos += 16
+ binary.BigEndian.PutUint32(buf[pos:], 3)
+
+ b = &NexthopLookupBody{Api: IPV6_NEXTHOP_LOOKUP}
+ err = b.DecodeFromBytes(buf, 2)
+ assert.Equal(nil, err)
+ assert.Equal("2001:db8:0:f101::", b.Addr.String())
+ assert.Equal(uint32(10), b.Metric)
+ assert.Equal(uint32(3), b.Nexthops[0].Ifindex)
+ assert.Equal(NEXTHOP_FLAG(4), b.Nexthops[0].Type)
+ assert.Equal("2001:db8:0:1111::1", b.Nexthops[0].Addr.String())
+
+ //Serialize
+ buf, err = b.Serialize(2)
+ ip = net.ParseIP("2001:db8:0:f101::").To16()
+ assert.Equal(nil, err)
+ assert.Equal([]byte(ip)[0:16], buf[0:16])
+
+ // length invalid
+ buf = make([]byte, 15)
+ b = &NexthopLookupBody{Api: IPV6_NEXTHOP_LOOKUP}
+ err = b.DecodeFromBytes(buf, 2)
+ assert.NotEqual(nil, err)
+}
+
+func Test_ImportLookupBody(t *testing.T) {
+ assert := assert.New(t)
+
+ //DecodeFromBytes
+ pos := 0
+ buf := make([]byte, 18)
+ ip := net.ParseIP("192.168.50.0").To4()
+ copy(buf[0:4], []byte(ip))
+ pos += 4
+ binary.BigEndian.PutUint32(buf[pos:], 10)
+ pos += 4
+ buf[pos] = byte(1)
+ pos += 1
+ buf[pos] = byte(4)
+ pos += 1
+ ip = net.ParseIP("172.16.1.101").To4()
+ copy(buf[pos:pos+4], []byte(ip))
+ pos += 4
+ binary.BigEndian.PutUint32(buf[pos:], 3)
+
+ b := &ImportLookupBody{Api: IPV4_IMPORT_LOOKUP}
+ err := b.DecodeFromBytes(buf, 2)
+ assert.Equal(nil, err)
+ assert.Equal("192.168.50.0", b.Addr.String())
+ assert.Equal(uint32(10), b.Metric)
+ assert.Equal(uint32(3), b.Nexthops[0].Ifindex)
+ assert.Equal(NEXTHOP_FLAG(4), b.Nexthops[0].Type)
+ assert.Equal("172.16.1.101", b.Nexthops[0].Addr.String())
+
+ //Serialize
+ b.PrefixLength = uint8(24)
+ buf, err = b.Serialize(2)
+ ip = net.ParseIP("192.168.50.0").To4()
+ assert.Equal(nil, err)
+ assert.Equal(uint8(24), buf[0])
+ assert.Equal([]byte(ip)[0:4], buf[1:5])
+
+ // length invalid
+ buf = make([]byte, 3)
+ b = &ImportLookupBody{Api: IPV4_IMPORT_LOOKUP}
+ err = b.DecodeFromBytes(buf, 2)
+ assert.NotEqual(nil, err)
+}
+
+func Test_NexthopRegisterBody(t *testing.T) {
+ assert := assert.New(t)
+
+ // Input binary
+ bufIn := []byte{
+ 0x01, 0x00, 0x02, 0x20, // connected(1 byte)=1, afi(2 bytes)=AF_INET, prefix_len(1 byte)=32
+ 0xc0, 0xa8, 0x01, 0x01, // prefix(4 bytes)="192.168.1.1"
+ 0x00, 0x00, 0x0a, 0x80, // connected(1 byte)=0, afi(2 bytes)=AF_INET6, prefix_len(1 byte)=128
+ 0x20, 0x01, 0x0d, 0xb8, // prefix(16 bytes)="2001:db8:1:1::1"
+ 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01,
+ }
+ binary.BigEndian.PutUint16(bufIn[1:], syscall.AF_INET)
+ binary.BigEndian.PutUint16(bufIn[9:], syscall.AF_INET6)
+
+ // Test DecodeFromBytes()
+ b := &NexthopRegisterBody{Api: NEXTHOP_REGISTER}
+ err := b.DecodeFromBytes(bufIn, 3)
+ assert.Nil(err)
+
+ // Test decoded values
+ assert.Equal(uint8(1), b.Nexthops[0].Connected)
+ assert.Equal(uint16(syscall.AF_INET), b.Nexthops[0].Family)
+ assert.Equal(net.ParseIP("192.168.1.1").To4(), b.Nexthops[0].Prefix)
+ assert.Equal(uint8(0), b.Nexthops[1].Connected)
+ assert.Equal(uint16(syscall.AF_INET6), b.Nexthops[1].Family)
+ assert.Equal(net.ParseIP("2001:db8:1:1::1").To16(), b.Nexthops[1].Prefix)
+
+ // Test Serialize()
+ bufOut, err := b.Serialize(3)
+ assert.Nil(err)
+
+ // Test serialised value
+ assert.Equal(bufIn, bufOut)
+}
+
+func Test_NexthopUpdateBody(t *testing.T) {
+ assert := assert.New(t)
+
+ // Input binary
+ bufIn := []byte{
+ 0x00, 0x02, 0x20, // afi(2 bytes)=AF_INET, prefix_len(1 byte)=32
+ 0xc0, 0xa8, 0x01, 0x01, // prefix(4 bytes)="192.168.1.1"
+ 0x00, 0x00, 0x00, 0x01, // metric(4 bytes)=1
+ 0x01, // nexthops(1 byte)=1
+ 0x04, // nexthop_type(1 byte)=NEXTHOP_IPV4_IFINDEX
+ 0xc0, 0xa8, 0x01, 0x01, // nexthop_ip(4 bytes)="192.168.0.1"
+ 0x00, 0x00, 0x00, 0x02, // nexthop_ifindex(4 byte)=2
+ }
+
+ // Test DecodeFromBytes()
+ b := &NexthopUpdateBody{Api: NEXTHOP_UPDATE}
+ err := b.DecodeFromBytes(bufIn, 2)
+ assert.Nil(err)
+
+ // Test decoded values
+ assert.Equal(uint16(syscall.AF_INET), b.Family)
+ assert.Equal(net.ParseIP("192.168.1.1").To4(), b.Prefix)
+ assert.Equal(uint32(1), b.Metric)
+ nexthop := &Nexthop{
+ Type: NEXTHOP_FLAG(NEXTHOP_IPV4_IFINDEX),
+ Addr: net.ParseIP("192.168.1.1").To4(),
+ Ifindex: uint32(2),
+ }
+ assert.Equal(1, len(b.Nexthops))
+ assert.Equal(nexthop, b.Nexthops[0])
+}
diff --git a/internal/pkg/zebra/zapi_windows.go b/internal/pkg/zebra/zapi_windows.go
new file mode 100644
index 00000000..d55525db
--- /dev/null
+++ b/internal/pkg/zebra/zapi_windows.go
@@ -0,0 +1,38 @@
+// Copyright (C) 2014, 2015 Nippon Telegraph and Telephone Corporation.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+// implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package zebra
+
+import (
+ "strings"
+ "syscall"
+)
+
+func intfflag2string(flag uint64) string {
+ ss := make([]string, 0, 10)
+ if flag&syscall.IFF_UP > 0 {
+ ss = append(ss, "UP")
+ }
+ if flag&syscall.IFF_BROADCAST > 0 {
+ ss = append(ss, "BROADCAST")
+ }
+ if flag&syscall.IFF_LOOPBACK > 0 {
+ ss = append(ss, "LOOPBACK")
+ }
+ if flag&syscall.IFF_MULTICAST > 0 {
+ ss = append(ss, "MULTICAST")
+ }
+ return strings.Join(ss, " | ")
+}