diff options
author | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2018-07-08 00:00:47 +0900 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2018-07-08 21:32:32 +0900 |
commit | 07f47b7d370e4a2197cf34e3847b2f867b97a40a (patch) | |
tree | ae89ab07ea53d9bc34c443efcb5a980d7f3ac625 /internal/pkg/apiutil | |
parent | 26aed14b48dc8afce6a3d2faa20f5a8ce95494b6 (diff) |
remove package dependency except for grpc in api/
Nothing except for protobuf IDL files and files generated by protobuf in api/.
Try to make the APIs portable to any languages.
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
Diffstat (limited to 'internal/pkg/apiutil')
-rw-r--r-- | internal/pkg/apiutil/attribute.go | 1302 | ||||
-rw-r--r-- | internal/pkg/apiutil/attribute_test.go | 1493 | ||||
-rw-r--r-- | internal/pkg/apiutil/capability.go | 244 | ||||
-rw-r--r-- | internal/pkg/apiutil/capability_test.go | 251 | ||||
-rw-r--r-- | internal/pkg/apiutil/util.go | 114 |
5 files changed, 3404 insertions, 0 deletions
diff --git a/internal/pkg/apiutil/attribute.go b/internal/pkg/apiutil/attribute.go new file mode 100644 index 00000000..e149edbb --- /dev/null +++ b/internal/pkg/apiutil/attribute.go @@ -0,0 +1,1302 @@ +// 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 apiutil + +import ( + "errors" + "fmt" + "net" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "github.com/golang/protobuf/ptypes/any" + api "github.com/osrg/gobgp/api" + "github.com/osrg/gobgp/pkg/packet/bgp" + log "github.com/sirupsen/logrus" +) + +func UnmarshalAttribute(an *any.Any) (bgp.PathAttributeInterface, error) { + var value ptypes.DynamicAny + if err := ptypes.UnmarshalAny(an, &value); err != nil { + return nil, fmt.Errorf("failed to unmarshal route distinguisher: %s", err) + } + switch a := value.Message.(type) { + case *api.OriginAttribute: + return bgp.NewPathAttributeOrigin(uint8(a.Origin)), nil + case *api.AsPathAttribute: + params := make([]bgp.AsPathParamInterface, 0, len(a.Segments)) + for _, segment := range a.Segments { + params = append(params, bgp.NewAs4PathParam(uint8(segment.Type), segment.Numbers)) + } + return bgp.NewPathAttributeAsPath(params), nil + case *api.NextHopAttribute: + nexthop := net.ParseIP(a.NextHop).To4() + if nexthop == nil { + return nil, fmt.Errorf("invalid nexthop address: %s", a.NextHop) + } + return bgp.NewPathAttributeNextHop(a.NextHop), nil + case *api.MultiExitDiscAttribute: + return bgp.NewPathAttributeMultiExitDisc(a.Med), nil + case *api.LocalPrefAttribute: + return bgp.NewPathAttributeLocalPref(a.LocalPref), nil + case *api.AtomicAggregateAttribute: + return bgp.NewPathAttributeAtomicAggregate(), nil + case *api.AggregatorAttribute: + if net.ParseIP(a.Address).To4() == nil { + return nil, fmt.Errorf("invalid aggregator address: %s", a.Address) + } + return bgp.NewPathAttributeAggregator(a.As, a.Address), nil + case *api.CommunitiesAttribute: + return bgp.NewPathAttributeCommunities(a.Communities), nil + case *api.OriginatorIdAttribute: + if net.ParseIP(a.Id).To4() == nil { + return nil, fmt.Errorf("invalid originator id: %s", a.Id) + } + return bgp.NewPathAttributeOriginatorId(a.Id), nil + case *api.ClusterListAttribute: + for _, id := range a.Ids { + if net.ParseIP(id).To4() == nil { + return nil, fmt.Errorf("invalid cluster list: %s", a.Ids) + } + } + return bgp.NewPathAttributeClusterList(a.Ids), nil + } + return nil, errors.New("unexpected object") +} + +func NewOriginAttributeFromNative(a *bgp.PathAttributeOrigin) *api.OriginAttribute { + return &api.OriginAttribute{ + Origin: uint32(a.Value), + } +} + +func NewAsPathAttributeFromNative(a *bgp.PathAttributeAsPath) *api.AsPathAttribute { + segments := make([]*api.AsSegment, 0, len(a.Value)) + for _, param := range a.Value { + segments = append(segments, &api.AsSegment{ + Type: uint32(param.GetType()), + Numbers: param.GetAS(), + }) + } + return &api.AsPathAttribute{ + Segments: segments, + } +} + +func NewNextHopAttributeFromNative(a *bgp.PathAttributeNextHop) *api.NextHopAttribute { + return &api.NextHopAttribute{ + NextHop: a.Value.String(), + } +} + +func NewMultiExitDiscAttributeFromNative(a *bgp.PathAttributeMultiExitDisc) *api.MultiExitDiscAttribute { + return &api.MultiExitDiscAttribute{ + Med: a.Value, + } +} + +func NewLocalPrefAttributeFromNative(a *bgp.PathAttributeLocalPref) *api.LocalPrefAttribute { + return &api.LocalPrefAttribute{ + LocalPref: a.Value, + } +} + +func NewAtomicAggregateAttributeFromNative(a *bgp.PathAttributeAtomicAggregate) *api.AtomicAggregateAttribute { + return &api.AtomicAggregateAttribute{} +} + +func NewAggregatorAttributeFromNative(a *bgp.PathAttributeAggregator) *api.AggregatorAttribute { + return &api.AggregatorAttribute{ + As: a.Value.AS, + Address: a.Value.Address.String(), + } +} + +func NewCommunitiesAttributeFromNative(a *bgp.PathAttributeCommunities) *api.CommunitiesAttribute { + return &api.CommunitiesAttribute{ + Communities: a.Value, + } +} + +func NewOriginatorIdAttributeFromNative(a *bgp.PathAttributeOriginatorId) *api.OriginatorIdAttribute { + return &api.OriginatorIdAttribute{ + Id: a.Value.String(), + } +} + +func NewClusterListAttributeFromNative(a *bgp.PathAttributeClusterList) *api.ClusterListAttribute { + ids := make([]string, 0, len(a.Value)) + for _, id := range a.Value { + ids = append(ids, id.String()) + } + return &api.ClusterListAttribute{ + Ids: ids, + } +} + +func MarshalRD(rd bgp.RouteDistinguisherInterface) *any.Any { + var r proto.Message + switch v := rd.(type) { + case *bgp.RouteDistinguisherTwoOctetAS: + r = &api.RouteDistinguisherTwoOctetAS{ + Admin: uint32(v.Admin), + Assigned: v.Assigned, + } + case *bgp.RouteDistinguisherIPAddressAS: + r = &api.RouteDistinguisherIPAddress{ + Admin: v.Admin.String(), + Assigned: uint32(v.Assigned), + } + case *bgp.RouteDistinguisherFourOctetAS: + r = &api.RouteDistinguisherFourOctetAS{ + Admin: v.Admin, + Assigned: uint32(v.Assigned), + } + default: + log.WithFields(log.Fields{ + "Topic": "protobuf", + "RD": rd, + }).Warn("invalid rd type to marshal") + return nil + } + a, _ := ptypes.MarshalAny(r) + return a +} + +func UnmarshalRD(a *any.Any) (bgp.RouteDistinguisherInterface, error) { + var value ptypes.DynamicAny + if err := ptypes.UnmarshalAny(a, &value); err != nil { + return nil, fmt.Errorf("failed to unmarshal route distinguisher: %s", err) + } + switch v := value.Message.(type) { + case *api.RouteDistinguisherTwoOctetAS: + return bgp.NewRouteDistinguisherTwoOctetAS(uint16(v.Admin), v.Assigned), nil + case *api.RouteDistinguisherIPAddress: + rd := bgp.NewRouteDistinguisherIPAddressAS(v.Admin, uint16(v.Assigned)) + if rd == nil { + return nil, fmt.Errorf("invalid address for route distinguisher: %s", v.Admin) + } + return rd, nil + case *api.RouteDistinguisherFourOctetAS: + return bgp.NewRouteDistinguisherFourOctetAS(v.Admin, uint16(v.Assigned)), nil + } + return nil, fmt.Errorf("invalid route distinguisher type: %s", a.TypeUrl) +} + +func NewEthernetSegmentIdentifierFromNative(a *bgp.EthernetSegmentIdentifier) *api.EthernetSegmentIdentifier { + return &api.EthernetSegmentIdentifier{ + Type: uint32(a.Type), + Value: a.Value, + } +} + +func unmarshalESI(a *api.EthernetSegmentIdentifier) (*bgp.EthernetSegmentIdentifier, error) { + return &bgp.EthernetSegmentIdentifier{ + Type: bgp.ESIType(a.Type), + Value: a.Value, + }, nil +} + +func MarshalFlowSpecRules(values []bgp.FlowSpecComponentInterface) []*any.Any { + rules := make([]*any.Any, 0, len(values)) + for _, value := range values { + var rule proto.Message + switch v := value.(type) { + case *bgp.FlowSpecDestinationPrefix: + rule = &api.FlowSpecIPPrefix{ + Type: uint32(bgp.FLOW_SPEC_TYPE_DST_PREFIX), + PrefixLen: uint32(v.Prefix.(*bgp.IPAddrPrefix).Length), + Prefix: v.Prefix.(*bgp.IPAddrPrefix).Prefix.String(), + } + case *bgp.FlowSpecSourcePrefix: + rule = &api.FlowSpecIPPrefix{ + Type: uint32(bgp.FLOW_SPEC_TYPE_SRC_PREFIX), + PrefixLen: uint32(v.Prefix.(*bgp.IPAddrPrefix).Length), + Prefix: v.Prefix.(*bgp.IPAddrPrefix).Prefix.String(), + } + case *bgp.FlowSpecDestinationPrefix6: + rule = &api.FlowSpecIPPrefix{ + Type: uint32(bgp.FLOW_SPEC_TYPE_DST_PREFIX), + PrefixLen: uint32(v.Prefix.(*bgp.IPv6AddrPrefix).Length), + Prefix: v.Prefix.(*bgp.IPv6AddrPrefix).Prefix.String(), + Offset: uint32(v.Offset), + } + case *bgp.FlowSpecSourcePrefix6: + rule = &api.FlowSpecIPPrefix{ + Type: uint32(bgp.FLOW_SPEC_TYPE_SRC_PREFIX), + PrefixLen: uint32(v.Prefix.(*bgp.IPv6AddrPrefix).Length), + Prefix: v.Prefix.(*bgp.IPv6AddrPrefix).Prefix.String(), + Offset: uint32(v.Offset), + } + case *bgp.FlowSpecSourceMac: + rule = &api.FlowSpecMAC{ + Type: uint32(bgp.FLOW_SPEC_TYPE_SRC_MAC), + Address: v.Mac.String(), + } + case *bgp.FlowSpecDestinationMac: + rule = &api.FlowSpecMAC{ + Type: uint32(bgp.FLOW_SPEC_TYPE_DST_MAC), + Address: v.Mac.String(), + } + case *bgp.FlowSpecComponent: + items := make([]*api.FlowSpecComponentItem, 0, len(v.Items)) + for _, i := range v.Items { + items = append(items, &api.FlowSpecComponentItem{ + Op: uint32(i.Op), + Value: i.Value, + }) + } + rule = &api.FlowSpecComponent{ + Type: uint32(v.Type()), + Items: items, + } + } + a, _ := ptypes.MarshalAny(rule) + rules = append(rules, a) + } + return rules +} + +func UnmarshalFlowSpecRules(values []*any.Any) ([]bgp.FlowSpecComponentInterface, error) { + rules := make([]bgp.FlowSpecComponentInterface, 0, len(values)) + for _, an := range values { + var rule bgp.FlowSpecComponentInterface + var value ptypes.DynamicAny + if err := ptypes.UnmarshalAny(an, &value); err != nil { + return nil, fmt.Errorf("failed to unmarshal flow spec component: %s", err) + } + switch v := value.Message.(type) { + case *api.FlowSpecIPPrefix: + typ := bgp.BGPFlowSpecType(v.Type) + isIPv4 := net.ParseIP(v.Prefix).To4() != nil + switch { + case typ == bgp.FLOW_SPEC_TYPE_DST_PREFIX && isIPv4: + rule = bgp.NewFlowSpecDestinationPrefix(bgp.NewIPAddrPrefix(uint8(v.PrefixLen), v.Prefix)) + case typ == bgp.FLOW_SPEC_TYPE_SRC_PREFIX && isIPv4: + rule = bgp.NewFlowSpecSourcePrefix(bgp.NewIPAddrPrefix(uint8(v.PrefixLen), v.Prefix)) + case typ == bgp.FLOW_SPEC_TYPE_DST_PREFIX && !isIPv4: + rule = bgp.NewFlowSpecDestinationPrefix6(bgp.NewIPv6AddrPrefix(uint8(v.PrefixLen), v.Prefix), uint8(v.Offset)) + case typ == bgp.FLOW_SPEC_TYPE_SRC_PREFIX && !isIPv4: + rule = bgp.NewFlowSpecSourcePrefix6(bgp.NewIPv6AddrPrefix(uint8(v.PrefixLen), v.Prefix), uint8(v.Offset)) + } + case *api.FlowSpecMAC: + typ := bgp.BGPFlowSpecType(v.Type) + mac, err := net.ParseMAC(v.Address) + if err != nil { + return nil, fmt.Errorf("invalid mac address for %s flow spec component: %s", typ.String(), v.Address) + } + switch typ { + case bgp.FLOW_SPEC_TYPE_SRC_MAC: + rule = bgp.NewFlowSpecSourceMac(mac) + case bgp.FLOW_SPEC_TYPE_DST_MAC: + rule = bgp.NewFlowSpecDestinationMac(mac) + } + case *api.FlowSpecComponent: + items := make([]*bgp.FlowSpecComponentItem, 0, len(v.Items)) + for _, item := range v.Items { + items = append(items, bgp.NewFlowSpecComponentItem(uint8(item.Op), item.Value)) + } + rule = bgp.NewFlowSpecComponent(bgp.BGPFlowSpecType(v.Type), items) + } + if rule == nil { + return nil, fmt.Errorf("invalid flow spec component: %v", value.Message) + } + rules = append(rules, rule) + } + return rules, nil +} + +func MarshalNLRI(value bgp.AddrPrefixInterface) *any.Any { + var nlri proto.Message + + switch v := value.(type) { + case *bgp.IPAddrPrefix: + nlri = &api.IPAddressPrefix{ + PrefixLen: uint32(v.Length), + Prefix: v.Prefix.String(), + } + case *bgp.IPv6AddrPrefix: + nlri = &api.IPAddressPrefix{ + PrefixLen: uint32(v.Length), + Prefix: v.Prefix.String(), + } + case *bgp.LabeledIPAddrPrefix: + nlri = &api.LabeledIPAddressPrefix{ + Labels: v.Labels.Labels, + PrefixLen: uint32(v.IPPrefixLen()), + Prefix: v.Prefix.String(), + } + case *bgp.LabeledIPv6AddrPrefix: + nlri = &api.LabeledIPAddressPrefix{ + Labels: v.Labels.Labels, + PrefixLen: uint32(v.IPPrefixLen()), + Prefix: v.Prefix.String(), + } + case *bgp.EncapNLRI: + nlri = &api.EncapsulationNLRI{ + Address: v.String(), + } + case *bgp.Encapv6NLRI: + nlri = &api.EncapsulationNLRI{ + Address: v.String(), + } + case *bgp.EVPNNLRI: + switch r := v.RouteTypeData.(type) { + case *bgp.EVPNEthernetAutoDiscoveryRoute: + nlri = &api.EVPNEthernetAutoDiscoveryRoute{ + Rd: MarshalRD(r.RD), + Esi: NewEthernetSegmentIdentifierFromNative(&r.ESI), + EthernetTag: r.ETag, + Label: r.Label, + } + case *bgp.EVPNMacIPAdvertisementRoute: + nlri = &api.EVPNMACIPAdvertisementRoute{ + Rd: MarshalRD(r.RD), + Esi: NewEthernetSegmentIdentifierFromNative(&r.ESI), + EthernetTag: r.ETag, + MacAddress: r.MacAddress.String(), + IpAddress: r.IPAddress.String(), + Labels: r.Labels, + } + case *bgp.EVPNMulticastEthernetTagRoute: + nlri = &api.EVPNInclusiveMulticastEthernetTagRoute{ + Rd: MarshalRD(r.RD), + EthernetTag: r.ETag, + IpAddress: r.IPAddress.String(), + } + case *bgp.EVPNEthernetSegmentRoute: + nlri = &api.EVPNEthernetSegmentRoute{ + Rd: MarshalRD(r.RD), + Esi: NewEthernetSegmentIdentifierFromNative(&r.ESI), + IpAddress: r.IPAddress.String(), + } + case *bgp.EVPNIPPrefixRoute: + nlri = &api.EVPNIPPrefixRoute{ + Rd: MarshalRD(r.RD), + Esi: NewEthernetSegmentIdentifierFromNative(&r.ESI), + EthernetTag: r.ETag, + IpPrefix: r.IPPrefix.String(), + IpPrefixLen: uint32(r.IPPrefixLength), + Label: r.Label, + } + } + case *bgp.LabeledVPNIPAddrPrefix: + nlri = &api.LabeledVPNIPAddressPrefix{ + Labels: v.Labels.Labels, + Rd: MarshalRD(v.RD), + PrefixLen: uint32(v.IPPrefixLen()), + Prefix: v.Prefix.String(), + } + case *bgp.LabeledVPNIPv6AddrPrefix: + nlri = &api.LabeledVPNIPAddressPrefix{ + Labels: v.Labels.Labels, + Rd: MarshalRD(v.RD), + PrefixLen: uint32(v.IPPrefixLen()), + Prefix: v.Prefix.String(), + } + case *bgp.RouteTargetMembershipNLRI: + nlri = &api.RouteTargetMembershipNLRI{ + As: v.AS, + Rt: MarshalRT(v.RouteTarget), + } + case *bgp.FlowSpecIPv4Unicast: + nlri = &api.FlowSpecNLRI{ + Rules: MarshalFlowSpecRules(v.Value), + } + case *bgp.FlowSpecIPv6Unicast: + nlri = &api.FlowSpecNLRI{ + Rules: MarshalFlowSpecRules(v.Value), + } + case *bgp.FlowSpecIPv4VPN: + nlri = &api.VPNFlowSpecNLRI{ + Rd: MarshalRD(v.RD()), + Rules: MarshalFlowSpecRules(v.Value), + } + case *bgp.FlowSpecIPv6VPN: + nlri = &api.VPNFlowSpecNLRI{ + Rd: MarshalRD(v.RD()), + Rules: MarshalFlowSpecRules(v.Value), + } + case *bgp.FlowSpecL2VPN: + nlri = &api.VPNFlowSpecNLRI{ + Rd: MarshalRD(v.RD()), + Rules: MarshalFlowSpecRules(v.Value), + } + } + + an, _ := ptypes.MarshalAny(nlri) + return an +} + +func MarshalNLRIs(values []bgp.AddrPrefixInterface) []*any.Any { + nlris := make([]*any.Any, 0, len(values)) + for _, value := range values { + nlris = append(nlris, MarshalNLRI(value)) + } + return nlris +} + +func UnmarshalNLRI(rf bgp.RouteFamily, an *any.Any) (bgp.AddrPrefixInterface, error) { + var nlri bgp.AddrPrefixInterface + + var value ptypes.DynamicAny + if err := ptypes.UnmarshalAny(an, &value); err != nil { + return nil, fmt.Errorf("failed to unmarshal nlri: %s", err) + } + + switch v := value.Message.(type) { + case *api.IPAddressPrefix: + switch rf { + case bgp.RF_IPv4_UC: + nlri = bgp.NewIPAddrPrefix(uint8(v.PrefixLen), v.Prefix) + case bgp.RF_IPv6_UC: + nlri = bgp.NewIPv6AddrPrefix(uint8(v.PrefixLen), v.Prefix) + } + case *api.LabeledIPAddressPrefix: + switch rf { + case bgp.RF_IPv4_MPLS: + nlri = bgp.NewLabeledIPAddrPrefix(uint8(v.PrefixLen), v.Prefix, *bgp.NewMPLSLabelStack(v.Labels...)) + case bgp.RF_IPv6_MPLS: + nlri = bgp.NewLabeledIPv6AddrPrefix(uint8(v.PrefixLen), v.Prefix, *bgp.NewMPLSLabelStack(v.Labels...)) + } + case *api.EncapsulationNLRI: + switch rf { + case bgp.RF_IPv4_ENCAP: + nlri = bgp.NewEncapNLRI(v.Address) + case bgp.RF_IPv6_ENCAP: + nlri = bgp.NewEncapv6NLRI(v.Address) + } + case *api.EVPNEthernetAutoDiscoveryRoute: + if rf == bgp.RF_EVPN { + rd, err := UnmarshalRD(v.Rd) + if err != nil { + return nil, err + } + esi, err := unmarshalESI(v.Esi) + if err != nil { + return nil, err + } + nlri = bgp.NewEVPNEthernetAutoDiscoveryRoute(rd, *esi, v.EthernetTag, v.Label) + } + case *api.EVPNMACIPAdvertisementRoute: + if rf == bgp.RF_EVPN { + rd, err := UnmarshalRD(v.Rd) + if err != nil { + return nil, err + } + esi, err := unmarshalESI(v.Esi) + if err != nil { + return nil, err + } + nlri = bgp.NewEVPNMacIPAdvertisementRoute(rd, *esi, v.EthernetTag, v.MacAddress, v.IpAddress, v.Labels) + } + case *api.EVPNInclusiveMulticastEthernetTagRoute: + if rf == bgp.RF_EVPN { + rd, err := UnmarshalRD(v.Rd) + if err != nil { + return nil, err + } + nlri = bgp.NewEVPNMulticastEthernetTagRoute(rd, v.EthernetTag, v.IpAddress) + } + case *api.EVPNEthernetSegmentRoute: + if rf == bgp.RF_EVPN { + rd, err := UnmarshalRD(v.Rd) + if err != nil { + return nil, err + } + esi, err := unmarshalESI(v.Esi) + if err != nil { + return nil, err + } + nlri = bgp.NewEVPNEthernetSegmentRoute(rd, *esi, v.IpAddress) + } + case *api.EVPNIPPrefixRoute: + if rf == bgp.RF_EVPN { + rd, err := UnmarshalRD(v.Rd) + if err != nil { + return nil, err + } + esi, err := unmarshalESI(v.Esi) + if err != nil { + return nil, err + } + nlri = bgp.NewEVPNIPPrefixRoute(rd, *esi, v.EthernetTag, uint8(v.IpPrefixLen), v.IpPrefix, v.GwAddress, v.Label) + } + case *api.LabeledVPNIPAddressPrefix: + rd, err := UnmarshalRD(v.Rd) + if err != nil { + return nil, err + } + switch rf { + case bgp.RF_IPv4_VPN: + nlri = bgp.NewLabeledVPNIPAddrPrefix(uint8(v.PrefixLen), v.Prefix, *bgp.NewMPLSLabelStack(v.Labels...), rd) + case bgp.RF_IPv6_VPN: + nlri = bgp.NewLabeledVPNIPv6AddrPrefix(uint8(v.PrefixLen), v.Prefix, *bgp.NewMPLSLabelStack(v.Labels...), rd) + } + case *api.RouteTargetMembershipNLRI: + rt, err := UnmarshalRT(v.Rt) + if err != nil { + return nil, err + } + nlri = bgp.NewRouteTargetMembershipNLRI(v.As, rt) + case *api.FlowSpecNLRI: + rules, err := UnmarshalFlowSpecRules(v.Rules) + if err != nil { + return nil, err + } + switch rf { + case bgp.RF_FS_IPv4_UC: + nlri = bgp.NewFlowSpecIPv4Unicast(rules) + case bgp.RF_FS_IPv6_UC: + nlri = bgp.NewFlowSpecIPv6Unicast(rules) + } + case *api.VPNFlowSpecNLRI: + rd, err := UnmarshalRD(v.Rd) + if err != nil { + return nil, err + } + rules, err := UnmarshalFlowSpecRules(v.Rules) + if err != nil { + return nil, err + } + switch rf { + case bgp.RF_FS_IPv4_VPN: + nlri = bgp.NewFlowSpecIPv4VPN(rd, rules) + case bgp.RF_FS_IPv6_VPN: + nlri = bgp.NewFlowSpecIPv6VPN(rd, rules) + case bgp.RF_FS_L2_VPN: + nlri = bgp.NewFlowSpecL2VPN(rd, rules) + } + } + + if nlri == nil { + return nil, fmt.Errorf("invalid nlri for %s family: %s", rf.String(), value.Message) + } + + return nlri, nil +} + +func UnmarshalNLRIs(rf bgp.RouteFamily, values []*any.Any) ([]bgp.AddrPrefixInterface, error) { + nlris := make([]bgp.AddrPrefixInterface, 0, len(values)) + for _, an := range values { + nlri, err := UnmarshalNLRI(rf, an) + if err != nil { + return nil, err + } + nlris = append(nlris, nlri) + } + return nlris, nil +} + +func NewMpReachNLRIAttributeFromNative(a *bgp.PathAttributeMpReachNLRI) *api.MpReachNLRIAttribute { + var nexthops []string + if a.SAFI == bgp.SAFI_FLOW_SPEC_UNICAST || a.SAFI == bgp.SAFI_FLOW_SPEC_VPN { + nexthops = nil + } else { + nexthops = []string{a.Nexthop.String()} + if a.LinkLocalNexthop != nil { + nexthops = append(nexthops, a.LinkLocalNexthop.String()) + } + } + return &api.MpReachNLRIAttribute{ + Family: uint32(bgp.AfiSafiToRouteFamily(a.AFI, a.SAFI)), + NextHops: nexthops, + Nlris: MarshalNLRIs(a.Value), + } +} + +func NewMpUnreachNLRIAttributeFromNative(a *bgp.PathAttributeMpUnreachNLRI) *api.MpUnreachNLRIAttribute { + return &api.MpUnreachNLRIAttribute{ + Family: uint32(bgp.AfiSafiToRouteFamily(a.AFI, a.SAFI)), + Nlris: MarshalNLRIs(a.Value), + } +} + +func MarshalRT(rt bgp.ExtendedCommunityInterface) *any.Any { + var r proto.Message + switch v := rt.(type) { + case *bgp.TwoOctetAsSpecificExtended: + r = &api.TwoOctetAsSpecificExtended{ + IsTransitive: true, + SubType: uint32(bgp.EC_SUBTYPE_ROUTE_TARGET), + As: uint32(v.AS), + LocalAdmin: uint32(v.LocalAdmin), + } + case *bgp.IPv4AddressSpecificExtended: + r = &api.IPv4AddressSpecificExtended{ + IsTransitive: true, + SubType: uint32(bgp.EC_SUBTYPE_ROUTE_TARGET), + Address: v.IPv4.String(), + LocalAdmin: uint32(v.LocalAdmin), + } + case *bgp.FourOctetAsSpecificExtended: + r = &api.FourOctetAsSpecificExtended{ + IsTransitive: true, + SubType: uint32(bgp.EC_SUBTYPE_ROUTE_TARGET), + As: uint32(v.AS), + LocalAdmin: uint32(v.LocalAdmin), + } + default: + log.WithFields(log.Fields{ + "Topic": "protobuf", + "RT": rt, + }).Warn("invalid rt type to marshal") + return nil + } + a, _ := ptypes.MarshalAny(r) + return a +} + +func MarshalRTs(values []bgp.ExtendedCommunityInterface) []*any.Any { + rts := make([]*any.Any, 0, len(values)) + for _, rt := range values { + rts = append(rts, MarshalRT(rt)) + } + return rts +} + +func UnmarshalRT(a *any.Any) (bgp.ExtendedCommunityInterface, error) { + var value ptypes.DynamicAny + if err := ptypes.UnmarshalAny(a, &value); err != nil { + return nil, fmt.Errorf("failed to unmarshal route target: %s", err) + } + switch v := value.Message.(type) { + case *api.TwoOctetAsSpecificExtended: + return bgp.NewTwoOctetAsSpecificExtended(bgp.ExtendedCommunityAttrSubType(v.SubType), uint16(v.As), v.LocalAdmin, v.IsTransitive), nil + case *api.IPv4AddressSpecificExtended: + rt := bgp.NewIPv4AddressSpecificExtended(bgp.ExtendedCommunityAttrSubType(v.SubType), v.Address, uint16(v.LocalAdmin), v.IsTransitive) + if rt == nil { + return nil, fmt.Errorf("invalid address for ipv4 address specific route target: %s", v.Address) + } + return rt, nil + case *api.FourOctetAsSpecificExtended: + return bgp.NewFourOctetAsSpecificExtended(bgp.ExtendedCommunityAttrSubType(v.SubType), v.As, uint16(v.LocalAdmin), v.IsTransitive), nil + } + return nil, fmt.Errorf("invalid route target type: %s", a.TypeUrl) +} + +func UnmarshalRTs(values []*any.Any) ([]bgp.ExtendedCommunityInterface, error) { + rts := make([]bgp.ExtendedCommunityInterface, 0, len(values)) + for _, an := range values { + rt, err := UnmarshalRT(an) + if err != nil { + return nil, err + } + rts = append(rts, rt) + } + return rts, nil +} + +func NewExtendedCommunitiesAttributeFromNative(a *bgp.PathAttributeExtendedCommunities) *api.ExtendedCommunitiesAttribute { + communities := make([]*any.Any, 0, len(a.Value)) + for _, value := range a.Value { + var community proto.Message + switch v := value.(type) { + case *bgp.TwoOctetAsSpecificExtended: + community = &api.TwoOctetAsSpecificExtended{ + IsTransitive: v.IsTransitive, + SubType: uint32(v.SubType), + As: uint32(v.AS), + LocalAdmin: uint32(v.LocalAdmin), + } + case *bgp.IPv4AddressSpecificExtended: + community = &api.IPv4AddressSpecificExtended{ + IsTransitive: v.IsTransitive, + SubType: uint32(v.SubType), + Address: v.IPv4.String(), + LocalAdmin: uint32(v.LocalAdmin), + } + case *bgp.FourOctetAsSpecificExtended: + community = &api.FourOctetAsSpecificExtended{ + IsTransitive: v.IsTransitive, + SubType: uint32(v.SubType), + As: uint32(v.AS), + LocalAdmin: uint32(v.LocalAdmin), + } + case *bgp.ValidationExtended: + community = &api.ValidationExtended{ + State: uint32(v.State), + } + case *bgp.ColorExtended: + community = &api.ColorExtended{ + Color: v.Color, + } + case *bgp.EncapExtended: + community = &api.EncapExtended{ + TunnelType: uint32(v.TunnelType), + } + case *bgp.DefaultGatewayExtended: + community = &api.DefaultGatewayExtended{} + case *bgp.OpaqueExtended: + community = &api.OpaqueExtended{ + IsTransitive: v.IsTransitive, + Value: v.Value, + } + case *bgp.ESILabelExtended: + community = &api.ESILabelExtended{ + IsSingleActive: v.IsSingleActive, + Label: v.Label, + } + case *bgp.ESImportRouteTarget: + community = &api.ESImportRouteTarget{ + EsImport: v.ESImport.String(), + } + case *bgp.MacMobilityExtended: + community = &api.MacMobilityExtended{ + IsSticky: v.IsSticky, + SequenceNum: v.Sequence, + } + case *bgp.RouterMacExtended: + community = &api.RouterMacExtended{ + Mac: v.Mac.String(), + } + case *bgp.TrafficRateExtended: + community = &api.TrafficRateExtended{ + As: uint32(v.AS), + Rate: v.Rate, + } + case *bgp.TrafficActionExtended: + community = &api.TrafficActionExtended{ + Terminal: v.Terminal, + Sample: v.Sample, + } + case *bgp.RedirectTwoOctetAsSpecificExtended: + community = &api.RedirectTwoOctetAsSpecificExtended{ + As: uint32(v.AS), + LocalAdmin: v.LocalAdmin, + } + case *bgp.RedirectIPv4AddressSpecificExtended: + community = &api.RedirectIPv4AddressSpecificExtended{ + Address: v.IPv4.String(), + LocalAdmin: uint32(v.LocalAdmin), + } + case *bgp.RedirectFourOctetAsSpecificExtended: + community = &api.RedirectFourOctetAsSpecificExtended{ + As: v.AS, + LocalAdmin: uint32(v.LocalAdmin), + } + case *bgp.TrafficRemarkExtended: + community = &api.TrafficRemarkExtended{ + Dscp: uint32(v.DSCP), + } + case *bgp.UnknownExtended: + community = &api.UnknownExtended{ + Type: uint32(v.Type), + Value: v.Value, + } + default: + log.WithFields(log.Fields{ + "Topic": "protobuf", + "Community": value, + }).Warn("unsupported extended community") + return nil + } + an, _ := ptypes.MarshalAny(community) + communities = append(communities, an) + } + return &api.ExtendedCommunitiesAttribute{ + Communities: communities, + } +} + +func unmarshalExComm(a *api.ExtendedCommunitiesAttribute) (*bgp.PathAttributeExtendedCommunities, error) { + communities := make([]bgp.ExtendedCommunityInterface, 0, len(a.Communities)) + for _, an := range a.Communities { + var community bgp.ExtendedCommunityInterface + var value ptypes.DynamicAny + if err := ptypes.UnmarshalAny(an, &value); err != nil { + return nil, fmt.Errorf("failed to unmarshal extended community: %s", err) + } + switch v := value.Message.(type) { + case *api.TwoOctetAsSpecificExtended: + community = bgp.NewTwoOctetAsSpecificExtended(bgp.ExtendedCommunityAttrSubType(v.SubType), uint16(v.As), v.LocalAdmin, v.IsTransitive) + case *api.IPv4AddressSpecificExtended: + community = bgp.NewIPv4AddressSpecificExtended(bgp.ExtendedCommunityAttrSubType(v.SubType), v.Address, uint16(v.LocalAdmin), v.IsTransitive) + case *api.FourOctetAsSpecificExtended: + community = bgp.NewFourOctetAsSpecificExtended(bgp.ExtendedCommunityAttrSubType(v.SubType), v.As, uint16(v.LocalAdmin), v.IsTransitive) + case *api.ValidationExtended: + community = bgp.NewValidationExtended(bgp.ValidationState(v.State)) + case *api.ColorExtended: + community = bgp.NewColorExtended(v.Color) + case *api.EncapExtended: + community = bgp.NewEncapExtended(bgp.TunnelType(v.TunnelType)) + case *api.DefaultGatewayExtended: + community = bgp.NewDefaultGatewayExtended() + case *api.OpaqueExtended: + community = bgp.NewOpaqueExtended(v.IsTransitive, v.Value) + case *api.ESILabelExtended: + community = bgp.NewESILabelExtended(v.Label, v.IsSingleActive) + case *api.ESImportRouteTarget: + community = bgp.NewESImportRouteTarget(v.EsImport) + case *api.MacMobilityExtended: + community = bgp.NewMacMobilityExtended(v.SequenceNum, v.IsSticky) + case *api.RouterMacExtended: + community = bgp.NewRoutersMacExtended(v.Mac) + case *api.TrafficRateExtended: + community = bgp.NewTrafficRateExtended(uint16(v.As), v.Rate) + case *api.TrafficActionExtended: + community = bgp.NewTrafficActionExtended(v.Terminal, v.Sample) + case *api.RedirectTwoOctetAsSpecificExtended: + community = bgp.NewRedirectTwoOctetAsSpecificExtended(uint16(v.As), v.LocalAdmin) + case *api.RedirectIPv4AddressSpecificExtended: + community = bgp.NewRedirectIPv4AddressSpecificExtended(v.Address, uint16(v.LocalAdmin)) + case *api.RedirectFourOctetAsSpecificExtended: + community = bgp.NewRedirectFourOctetAsSpecificExtended(v.As, uint16(v.LocalAdmin)) + case *api.TrafficRemarkExtended: + community = bgp.NewTrafficRemarkExtended(uint8(v.Dscp)) + case *api.UnknownExtended: + community = bgp.NewUnknownExtended(bgp.ExtendedCommunityAttrType(v.Type), v.Value) + } + if community == nil { + return nil, fmt.Errorf("invalid extended community: %v", value.Message) + } + communities = append(communities, community) + } + return bgp.NewPathAttributeExtendedCommunities(communities), nil +} + +func NewAs4PathAttributeFromNative(a *bgp.PathAttributeAs4Path) *api.As4PathAttribute { + segments := make([]*api.AsSegment, 0, len(a.Value)) + for _, param := range a.Value { + segments = append(segments, &api.AsSegment{ + Type: uint32(param.Type), + Numbers: param.AS, + }) + } + return &api.As4PathAttribute{ + Segments: segments, + } +} + +func NewAs4AggregatorAttributeFromNative(a *bgp.PathAttributeAs4Aggregator) *api.As4AggregatorAttribute { + return &api.As4AggregatorAttribute{ + As: a.Value.AS, + Address: a.Value.Address.String(), + } +} + +func NewPmsiTunnelAttributeFromNative(a *bgp.PathAttributePmsiTunnel) *api.PmsiTunnelAttribute { + var flags uint32 + if a.IsLeafInfoRequired { + flags |= 0x01 + } + id, _ := a.TunnelID.Serialize() + return &api.PmsiTunnelAttribute{ + Flags: flags, + Type: uint32(a.TunnelType), + Label: a.Label, + Id: id, + } +} + +func NewTunnelEncapAttributeFromNative(a *bgp.PathAttributeTunnelEncap) *api.TunnelEncapAttribute { + tlvs := make([]*api.TunnelEncapTLV, 0, len(a.Value)) + for _, v := range a.Value { + subTlvs := make([]*any.Any, 0, len(v.Value)) + for _, s := range v.Value { + var subTlv proto.Message + switch sv := s.(type) { + case *bgp.TunnelEncapSubTLVEncapsulation: + subTlv = &api.TunnelEncapSubTLVEncapsulation{ + Key: sv.Key, + Cookie: sv.Cookie, + } + case *bgp.TunnelEncapSubTLVProtocol: + subTlv = &api.TunnelEncapSubTLVProtocol{ + Protocol: uint32(sv.Protocol), + } + case *bgp.TunnelEncapSubTLVColor: + subTlv = &api.TunnelEncapSubTLVColor{ + Color: sv.Color, + } + case *bgp.TunnelEncapSubTLVUnknown: + subTlv = &api.TunnelEncapSubTLVUnknown{ + Type: uint32(sv.Type), + Value: sv.Value, + } + } + an, _ := ptypes.MarshalAny(subTlv) + subTlvs = append(subTlvs, an) + } + tlvs = append(tlvs, &api.TunnelEncapTLV{ + Type: uint32(v.Type), + Tlvs: subTlvs, + }) + } + return &api.TunnelEncapAttribute{ + Tlvs: tlvs, + } +} + +func NewIP6ExtendedCommunitiesAttributeFromNative(a *bgp.PathAttributeIP6ExtendedCommunities) *api.IP6ExtendedCommunitiesAttribute { + communities := make([]*any.Any, 0, len(a.Value)) + for _, value := range a.Value { + var community proto.Message + switch v := value.(type) { + case *bgp.IPv6AddressSpecificExtended: + community = &api.IPv6AddressSpecificExtended{ + IsTransitive: v.IsTransitive, + SubType: uint32(v.SubType), + Address: v.IPv6.String(), + LocalAdmin: uint32(v.LocalAdmin), + } + case *bgp.RedirectIPv6AddressSpecificExtended: + community = &api.RedirectIPv6AddressSpecificExtended{ + Address: v.IPv6.String(), + LocalAdmin: uint32(v.LocalAdmin), + } + default: + log.WithFields(log.Fields{ + "Topic": "protobuf", + "Attribute": value, + }).Warn("invalid ipv6 extended community") + return nil + } + an, _ := ptypes.MarshalAny(community) + communities = append(communities, an) + } + return &api.IP6ExtendedCommunitiesAttribute{ + Communities: communities, + } +} + +func NewAigpAttributeFromNative(a *bgp.PathAttributeAigp) *api.AigpAttribute { + tlvs := make([]*any.Any, 0, len(a.Values)) + for _, value := range a.Values { + var tlv proto.Message + switch v := value.(type) { + case *bgp.AigpTLVIgpMetric: + tlv = &api.AigpTLVIGPMetric{ + Metric: v.Metric, + } + case *bgp.AigpTLVDefault: + tlv = &api.AigpTLVUnknown{ + Type: uint32(v.Type()), + Value: v.Value, + } + } + an, _ := ptypes.MarshalAny(tlv) + tlvs = append(tlvs, an) + } + return &api.AigpAttribute{ + Tlvs: tlvs, + } +} + +func NewLargeCommunitiesAttributeFromNative(a *bgp.PathAttributeLargeCommunities) *api.LargeCommunitiesAttribute { + communities := make([]*api.LargeCommunity, 0, len(a.Values)) + for _, v := range a.Values { + communities = append(communities, &api.LargeCommunity{ + GlobalAdmin: v.ASN, + LocalData1: v.LocalData1, + LocalData2: v.LocalData2, + }) + } + return &api.LargeCommunitiesAttribute{ + Communities: communities, + } +} + +func NewUnknownAttributeFromNative(a *bgp.PathAttributeUnknown) *api.UnknownAttribute { + return &api.UnknownAttribute{ + Flags: uint32(a.Flags), + Type: uint32(a.Type), + Value: a.Value, + } +} + +func MarshalPathAttributes(attrList []bgp.PathAttributeInterface) []*any.Any { + anyList := make([]*any.Any, 0, len(attrList)) + for _, attr := range attrList { + switch a := attr.(type) { + case *bgp.PathAttributeOrigin: + n, _ := ptypes.MarshalAny(NewOriginAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeAsPath: + n, _ := ptypes.MarshalAny(NewAsPathAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeNextHop: + n, _ := ptypes.MarshalAny(NewNextHopAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeMultiExitDisc: + n, _ := ptypes.MarshalAny(NewMultiExitDiscAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeLocalPref: + n, _ := ptypes.MarshalAny(NewLocalPrefAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeAtomicAggregate: + n, _ := ptypes.MarshalAny(NewAtomicAggregateAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeAggregator: + n, _ := ptypes.MarshalAny(NewAggregatorAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeCommunities: + n, _ := ptypes.MarshalAny(NewCommunitiesAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeOriginatorId: + n, _ := ptypes.MarshalAny(NewOriginatorIdAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeClusterList: + n, _ := ptypes.MarshalAny(NewClusterListAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeMpReachNLRI: + n, _ := ptypes.MarshalAny(NewMpReachNLRIAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeMpUnreachNLRI: + n, _ := ptypes.MarshalAny(NewMpUnreachNLRIAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeExtendedCommunities: + n, _ := ptypes.MarshalAny(NewExtendedCommunitiesAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeAs4Path: + n, _ := ptypes.MarshalAny(NewAs4PathAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeAs4Aggregator: + n, _ := ptypes.MarshalAny(NewAs4AggregatorAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributePmsiTunnel: + n, _ := ptypes.MarshalAny(NewPmsiTunnelAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeTunnelEncap: + n, _ := ptypes.MarshalAny(NewTunnelEncapAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeIP6ExtendedCommunities: + n, _ := ptypes.MarshalAny(NewIP6ExtendedCommunitiesAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeAigp: + n, _ := ptypes.MarshalAny(NewAigpAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeLargeCommunities: + n, _ := ptypes.MarshalAny(NewLargeCommunitiesAttributeFromNative(a)) + anyList = append(anyList, n) + case *bgp.PathAttributeUnknown: + n, _ := ptypes.MarshalAny(NewUnknownAttributeFromNative(a)) + anyList = append(anyList, n) + } + } + return anyList +} + +func UnmarshalPathAttributes(values []*any.Any) ([]bgp.PathAttributeInterface, error) { + attrList := make([]bgp.PathAttributeInterface, 0, len(values)) + typeMap := make(map[bgp.BGPAttrType]struct{}) + for _, an := range values { + attr, err := unmarshalAttribute(an) + if err != nil { + return nil, err + } + if _, ok := typeMap[attr.GetType()]; ok { + return nil, fmt.Errorf("duplicated path attribute type: %d", attr.GetType()) + } + typeMap[attr.GetType()] = struct{}{} + attrList = append(attrList, attr) + } + return attrList, nil +} + +func unmarshalAttribute(an *any.Any) (bgp.PathAttributeInterface, error) { + var value ptypes.DynamicAny + if err := ptypes.UnmarshalAny(an, &value); err != nil { + return nil, fmt.Errorf("failed to unmarshal route distinguisher: %s", err) + } + switch a := value.Message.(type) { + case *api.OriginAttribute: + return bgp.NewPathAttributeOrigin(uint8(a.Origin)), nil + case *api.AsPathAttribute: + params := make([]bgp.AsPathParamInterface, 0, len(a.Segments)) + for _, segment := range a.Segments { + params = append(params, bgp.NewAs4PathParam(uint8(segment.Type), segment.Numbers)) + } + return bgp.NewPathAttributeAsPath(params), nil + case *api.NextHopAttribute: + nexthop := net.ParseIP(a.NextHop).To4() + if nexthop == nil { + return nil, fmt.Errorf("invalid nexthop address: %s", a.NextHop) + } + return bgp.NewPathAttributeNextHop(a.NextHop), nil + case *api.MultiExitDiscAttribute: + return bgp.NewPathAttributeMultiExitDisc(a.Med), nil + case *api.LocalPrefAttribute: + return bgp.NewPathAttributeLocalPref(a.LocalPref), nil + case *api.AtomicAggregateAttribute: + return bgp.NewPathAttributeAtomicAggregate(), nil + case *api.AggregatorAttribute: + if net.ParseIP(a.Address).To4() == nil { + return nil, fmt.Errorf("invalid aggregator address: %s", a.Address) + } + return bgp.NewPathAttributeAggregator(a.As, a.Address), nil + case *api.CommunitiesAttribute: + return bgp.NewPathAttributeCommunities(a.Communities), nil + case *api.OriginatorIdAttribute: + if net.ParseIP(a.Id).To4() == nil { + return nil, fmt.Errorf("invalid originator id: %s", a.Id) + } + return bgp.NewPathAttributeOriginatorId(a.Id), nil + case *api.ClusterListAttribute: + for _, id := range a.Ids { + if net.ParseIP(id).To4() == nil { + return nil, fmt.Errorf("invalid cluster list: %s", a.Ids) + } + } + return bgp.NewPathAttributeClusterList(a.Ids), nil + case *api.MpReachNLRIAttribute: + rf := bgp.RouteFamily(a.Family) + nlris, err := UnmarshalNLRIs(rf, a.Nlris) + if err != nil { + return nil, err + } + afi, safi := bgp.RouteFamilyToAfiSafi(rf) + nexthop := "0.0.0.0" + var linkLocalNexthop net.IP + if afi == bgp.AFI_IP6 { + nexthop = "::" + if len(a.NextHops) > 1 { + linkLocalNexthop = net.ParseIP(a.NextHops[1]).To16() + if linkLocalNexthop == nil { + return nil, fmt.Errorf("invalid nexthop: %s", a.NextHops[1]) + } + } + } + if safi == bgp.SAFI_FLOW_SPEC_UNICAST || safi == bgp.SAFI_FLOW_SPEC_VPN { + nexthop = "" + } else if len(a.NextHops) > 0 { + nexthop = a.NextHops[0] + if net.ParseIP(nexthop) == nil { + return nil, fmt.Errorf("invalid nexthop: %s", nexthop) + } + } + attr := bgp.NewPathAttributeMpReachNLRI(nexthop, nlris) + attr.LinkLocalNexthop = linkLocalNexthop + return attr, nil + case *api.MpUnreachNLRIAttribute: + rf := bgp.RouteFamily(a.Family) + nlris, err := UnmarshalNLRIs(rf, a.Nlris) + if err != nil { + return nil, err + } + return bgp.NewPathAttributeMpUnreachNLRI(nlris), nil + case *api.ExtendedCommunitiesAttribute: + return unmarshalExComm(a) + case *api.As4PathAttribute: + params := make([]*bgp.As4PathParam, 0, len(a.Segments)) + for _, segment := range a.Segments { + params = append(params, bgp.NewAs4PathParam(uint8(segment.Type), segment.Numbers)) + } + return bgp.NewPathAttributeAs4Path(params), nil + case *api.As4AggregatorAttribute: + if net.ParseIP(a.Address).To4() == nil { + return nil, fmt.Errorf("invalid as4 aggregator address: %s", a.Address) + } + return bgp.NewPathAttributeAs4Aggregator(a.As, a.Address), nil + case *api.PmsiTunnelAttribute: + typ := bgp.PmsiTunnelType(a.Type) + var isLeafInfoRequired bool + if a.Flags&0x01 > 0 { + isLeafInfoRequired = true + } + var id bgp.PmsiTunnelIDInterface + switch typ { + case bgp.PMSI_TUNNEL_TYPE_INGRESS_REPL: + ip := net.IP(a.Id) + if ip.To4() == nil && ip.To16() == nil { + return nil, fmt.Errorf("invalid pmsi tunnel identifier: %s", a.Id) + } + id = bgp.NewIngressReplTunnelID(ip.String()) + default: + id = bgp.NewDefaultPmsiTunnelID(a.Id) + } + return bgp.NewPathAttributePmsiTunnel(typ, isLeafInfoRequired, a.Label, id), nil + case *api.TunnelEncapAttribute: + tlvs := make([]*bgp.TunnelEncapTLV, 0, len(a.Tlvs)) + for _, tlv := range a.Tlvs { + subTlvs := make([]bgp.TunnelEncapSubTLVInterface, 0, len(tlv.Tlvs)) + for _, an := range tlv.Tlvs { + var subTlv bgp.TunnelEncapSubTLVInterface + var subValue ptypes.DynamicAny + if err := ptypes.UnmarshalAny(an, &subValue); err != nil { + return nil, fmt.Errorf("failed to unmarshal tunnel encapsulation attribute sub tlv: %s", err) + } + switch sv := subValue.Message.(type) { + case *api.TunnelEncapSubTLVEncapsulation: + subTlv = bgp.NewTunnelEncapSubTLVEncapsulation(sv.Key, sv.Cookie) + case *api.TunnelEncapSubTLVProtocol: + subTlv = bgp.NewTunnelEncapSubTLVProtocol(uint16(sv.Protocol)) + case *api.TunnelEncapSubTLVColor: + subTlv = bgp.NewTunnelEncapSubTLVColor(sv.Color) + case *api.TunnelEncapSubTLVUnknown: + subTlv = bgp.NewTunnelEncapSubTLVUnknown(bgp.EncapSubTLVType(sv.Type), sv.Value) + default: + return nil, fmt.Errorf("invalid tunnel encapsulation attribute sub tlv: %v", subValue.Message) + } + subTlvs = append(subTlvs, subTlv) + } + tlvs = append(tlvs, bgp.NewTunnelEncapTLV(bgp.TunnelType(tlv.Type), subTlvs)) + } + return bgp.NewPathAttributeTunnelEncap(tlvs), nil + case *api.IP6ExtendedCommunitiesAttribute: + communities := make([]bgp.ExtendedCommunityInterface, 0, len(a.Communities)) + for _, an := range a.Communities { + var community bgp.ExtendedCommunityInterface + var value ptypes.DynamicAny + if err := ptypes.UnmarshalAny(an, &value); err != nil { + return nil, fmt.Errorf("failed to unmarshal ipv6 extended community: %s", err) + } + switch v := value.Message.(type) { + case *api.IPv6AddressSpecificExtended: + community = bgp.NewIPv6AddressSpecificExtended(bgp.ExtendedCommunityAttrSubType(v.SubType), v.Address, uint16(v.LocalAdmin), v.IsTransitive) + case *api.RedirectIPv6AddressSpecificExtended: + community = bgp.NewRedirectIPv6AddressSpecificExtended(v.Address, uint16(v.LocalAdmin)) + } + if community == nil { + return nil, fmt.Errorf("invalid ipv6 extended community: %v", value.Message) + } + communities = append(communities, community) + } + return bgp.NewPathAttributeIP6ExtendedCommunities(communities), nil + + case *api.AigpAttribute: + tlvs := make([]bgp.AigpTLVInterface, 0, len(a.Tlvs)) + for _, an := range a.Tlvs { + var tlv bgp.AigpTLVInterface + var value ptypes.DynamicAny + if err := ptypes.UnmarshalAny(an, &value); err != nil { + return nil, fmt.Errorf("failed to unmarshal aigp attribute tlv: %s", err) + } + switch v := value.Message.(type) { + case *api.AigpTLVIGPMetric: + tlv = bgp.NewAigpTLVIgpMetric(v.Metric) + case *api.AigpTLVUnknown: + tlv = bgp.NewAigpTLVDefault(bgp.AigpTLVType(v.Type), v.Value) + } + if tlv == nil { + return nil, fmt.Errorf("invalid aigp attribute tlv: %v", value.Message) + } + tlvs = append(tlvs, tlv) + } + return bgp.NewPathAttributeAigp(tlvs), nil + + case *api.LargeCommunitiesAttribute: + communities := make([]*bgp.LargeCommunity, 0, len(a.Communities)) + for _, c := range a.Communities { + communities = append(communities, bgp.NewLargeCommunity(c.GlobalAdmin, c.LocalData1, c.LocalData2)) + } + return bgp.NewPathAttributeLargeCommunities(communities), nil + + case *api.UnknownAttribute: + return bgp.NewPathAttributeUnknown(bgp.BGPAttrFlag(a.Flags), bgp.BGPAttrType(a.Type), a.Value), nil + } + return nil, errors.New("unknown path attribute") +} diff --git a/internal/pkg/apiutil/attribute_test.go b/internal/pkg/apiutil/attribute_test.go new file mode 100644 index 00000000..df86427f --- /dev/null +++ b/internal/pkg/apiutil/attribute_test.go @@ -0,0 +1,1493 @@ +// 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 apiutil + +import ( + "net" + "testing" + + "github.com/golang/protobuf/ptypes" + "github.com/golang/protobuf/ptypes/any" + api "github.com/osrg/gobgp/api" + "github.com/osrg/gobgp/pkg/packet/bgp" + "github.com/stretchr/testify/assert" +) + +func Test_OriginAttribute(t *testing.T) { + assert := assert.New(t) + + input := &api.OriginAttribute{ + Origin: 0, // IGP + } + a, err := ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewOriginAttributeFromNative(n.(*bgp.PathAttributeOrigin)) + assert.Equal(input.Origin, output.Origin) +} + +func Test_AsPathAttribute(t *testing.T) { + assert := assert.New(t) + + input := &api.AsPathAttribute{ + Segments: []*api.AsSegment{ + { + Type: 1, // SET + Numbers: []uint32{100, 200}, + }, + { + Type: 2, // SEQ + Numbers: []uint32{300, 400}, + }, + }, + } + + a, err := ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewAsPathAttributeFromNative(n.(*bgp.PathAttributeAsPath)) + assert.Equal(2, len(output.Segments)) + assert.Equal(input.Segments, output.Segments) +} + +func Test_NextHopAttribute(t *testing.T) { + assert := assert.New(t) + + input := &api.NextHopAttribute{ + NextHop: "192.168.0.1", + } + + a, err := ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewNextHopAttributeFromNative(n.(*bgp.PathAttributeNextHop)) + assert.Equal(input.NextHop, output.NextHop) +} + +func Test_MultiExitDiscAttribute(t *testing.T) { + assert := assert.New(t) + + input := &api.MultiExitDiscAttribute{ + Med: 100, + } + + a, err := ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewMultiExitDiscAttributeFromNative(n.(*bgp.PathAttributeMultiExitDisc)) + assert.Equal(input.Med, output.Med) +} + +func Test_LocalPrefAttribute(t *testing.T) { + assert := assert.New(t) + + input := &api.LocalPrefAttribute{ + LocalPref: 100, + } + + a, err := ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewLocalPrefAttributeFromNative(n.(*bgp.PathAttributeLocalPref)) + assert.Equal(input.LocalPref, output.LocalPref) +} + +func Test_AtomicAggregateAttribute(t *testing.T) { + assert := assert.New(t) + + input := &api.AtomicAggregateAttribute{} + + a, err := ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewAtomicAggregateAttributeFromNative(n.(*bgp.PathAttributeAtomicAggregate)) + // AtomicAggregateAttribute has no value + assert.NotNil(output) +} + +func Test_AggregatorAttribute(t *testing.T) { + assert := assert.New(t) + + input := &api.AggregatorAttribute{ + As: 65000, + Address: "1.1.1.1", + } + + a, err := ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewAggregatorAttributeFromNative(n.(*bgp.PathAttributeAggregator)) + assert.Equal(input.As, output.As) + assert.Equal(input.Address, output.Address) +} + +func Test_CommunitiesAttribute(t *testing.T) { + assert := assert.New(t) + + input := &api.CommunitiesAttribute{ + Communities: []uint32{100, 200}, + } + + a, err := ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewCommunitiesAttributeFromNative(n.(*bgp.PathAttributeCommunities)) + assert.Equal(input.Communities, output.Communities) +} + +func Test_OriginatorIdAttribute(t *testing.T) { + assert := assert.New(t) + + input := &api.OriginatorIdAttribute{ + Id: "1.1.1.1", + } + + a, err := ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewOriginatorIdAttributeFromNative(n.(*bgp.PathAttributeOriginatorId)) + assert.Equal(input.Id, output.Id) +} + +func Test_ClusterListAttribute(t *testing.T) { + assert := assert.New(t) + + input := &api.ClusterListAttribute{ + Ids: []string{"1.1.1.1", "2.2.2.2"}, + } + + a, err := ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewClusterListAttributeFromNative(n.(*bgp.PathAttributeClusterList)) + assert.Equal(input.Ids, output.Ids) +} + +func Test_MpReachNLRIAttribute_IPv4_UC(t *testing.T) { + assert := assert.New(t) + + nlris := make([]*any.Any, 0, 2) + a, err := ptypes.MarshalAny(&api.IPAddressPrefix{ + PrefixLen: 24, + Prefix: "192.168.101.0", + }) + assert.Nil(err) + nlris = append(nlris, a) + a, err = ptypes.MarshalAny(&api.IPAddressPrefix{ + PrefixLen: 24, + Prefix: "192.168.201.0", + }) + assert.Nil(err) + nlris = append(nlris, a) + + input := &api.MpReachNLRIAttribute{ + Family: uint32(bgp.RF_IPv4_UC), + NextHops: []string{"192.168.1.1"}, + Nlris: nlris, + } + + a, err = ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) + assert.Equal(input.Family, output.Family) + assert.Equal(input.NextHops, output.NextHops) + assert.Equal(2, len(output.Nlris)) + for idx, inputNLRI := range input.Nlris { + outputNLRI := output.Nlris[idx] + assert.Equal(inputNLRI.TypeUrl, outputNLRI.TypeUrl) + assert.Equal(inputNLRI.Value, outputNLRI.Value) + } +} + +func Test_MpReachNLRIAttribute_IPv6_UC(t *testing.T) { + assert := assert.New(t) + + nlris := make([]*any.Any, 0, 2) + a, err := ptypes.MarshalAny(&api.IPAddressPrefix{ + PrefixLen: 64, + Prefix: "2001:db8:1::", + }) + assert.Nil(err) + nlris = append(nlris, a) + a, err = ptypes.MarshalAny(&api.IPAddressPrefix{ + PrefixLen: 64, + Prefix: "2001:db8:2::", + }) + assert.Nil(err) + nlris = append(nlris, a) + + input := &api.MpReachNLRIAttribute{ + Family: uint32(bgp.RF_IPv6_UC), + NextHops: []string{"2001:db8::1", "2001:db8::2"}, + Nlris: nlris, + } + + a, err = ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) + assert.Equal(input.Family, output.Family) + assert.Equal(input.NextHops, output.NextHops) + assert.Equal(2, len(output.Nlris)) + for idx, inputNLRI := range input.Nlris { + outputNLRI := output.Nlris[idx] + assert.Equal(inputNLRI.TypeUrl, outputNLRI.TypeUrl) + assert.Equal(inputNLRI.Value, outputNLRI.Value) + } +} + +func Test_MpReachNLRIAttribute_IPv4_MPLS(t *testing.T) { + assert := assert.New(t) + + nlris := make([]*any.Any, 0, 2) + a, err := ptypes.MarshalAny(&api.LabeledIPAddressPrefix{ + Labels: []uint32{100}, + PrefixLen: 24, + Prefix: "192.168.101.0", + }) + assert.Nil(err) + nlris = append(nlris, a) + a, err = ptypes.MarshalAny(&api.LabeledIPAddressPrefix{ + Labels: []uint32{200}, + PrefixLen: 24, + Prefix: "192.168.201.0", + }) + assert.Nil(err) + nlris = append(nlris, a) + + input := &api.MpReachNLRIAttribute{ + Family: uint32(bgp.RF_IPv4_MPLS), + NextHops: []string{"192.168.1.1"}, + Nlris: nlris, + } + + a, err = ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) + assert.Equal(input.Family, output.Family) + assert.Equal(input.NextHops, output.NextHops) + assert.Equal(2, len(output.Nlris)) + for idx, inputNLRI := range input.Nlris { + outputNLRI := output.Nlris[idx] + assert.Equal(inputNLRI.TypeUrl, outputNLRI.TypeUrl) + assert.Equal(inputNLRI.Value, outputNLRI.Value) + } +} + +func Test_MpReachNLRIAttribute_IPv6_MPLS(t *testing.T) { + assert := assert.New(t) + + nlris := make([]*any.Any, 0, 2) + a, err := ptypes.MarshalAny(&api.LabeledIPAddressPrefix{ + Labels: []uint32{100}, + PrefixLen: 64, + Prefix: "2001:db8:1::", + }) + assert.Nil(err) + nlris = append(nlris, a) + a, err = ptypes.MarshalAny(&api.LabeledIPAddressPrefix{ + Labels: []uint32{200}, + PrefixLen: 64, + Prefix: "2001:db8:2::", + }) + assert.Nil(err) + nlris = append(nlris, a) + + input := &api.MpReachNLRIAttribute{ + Family: uint32(bgp.RF_IPv6_MPLS), + NextHops: []string{"2001:db8::1"}, + Nlris: nlris, + } + + a, err = ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) + assert.Equal(input.Family, output.Family) + assert.Equal(input.NextHops, output.NextHops) + assert.Equal(2, len(output.Nlris)) + for idx, inputNLRI := range input.Nlris { + outputNLRI := output.Nlris[idx] + assert.Equal(inputNLRI.TypeUrl, outputNLRI.TypeUrl) + assert.Equal(inputNLRI.Value, outputNLRI.Value) + } +} + +func Test_MpReachNLRIAttribute_IPv4_ENCAP(t *testing.T) { + assert := assert.New(t) + + nlris := make([]*any.Any, 0, 2) + a, err := ptypes.MarshalAny(&api.EncapsulationNLRI{ + Address: "192.168.101.1", + }) + assert.Nil(err) + nlris = append(nlris, a) + a, err = ptypes.MarshalAny(&api.EncapsulationNLRI{ + Address: "192.168.201.1", + }) + assert.Nil(err) + nlris = append(nlris, a) + + input := &api.MpReachNLRIAttribute{ + Family: uint32(bgp.RF_IPv4_ENCAP), + NextHops: []string{"192.168.1.1"}, + Nlris: nlris, + } + + a, err = ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) + assert.Equal(input.Family, output.Family) + assert.Equal(input.NextHops, output.NextHops) + assert.Equal(2, len(output.Nlris)) + for idx, inputNLRI := range input.Nlris { + outputNLRI := output.Nlris[idx] + assert.Equal(inputNLRI.TypeUrl, outputNLRI.TypeUrl) + assert.Equal(inputNLRI.Value, outputNLRI.Value) + } +} + +func Test_MpReachNLRIAttribute_IPv6_ENCAP(t *testing.T) { + assert := assert.New(t) + + nlris := make([]*any.Any, 0, 2) + a, err := ptypes.MarshalAny(&api.EncapsulationNLRI{ + Address: "2001:db8:1::1", + }) + assert.Nil(err) + nlris = append(nlris, a) + a, err = ptypes.MarshalAny(&api.EncapsulationNLRI{ + Address: "2001:db8:2::1", + }) + assert.Nil(err) + nlris = append(nlris, a) + + input := &api.MpReachNLRIAttribute{ + Family: uint32(bgp.RF_IPv6_ENCAP), + NextHops: []string{"2001:db8::1"}, + Nlris: nlris, + } + + a, err = ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) + assert.Equal(input.Family, output.Family) + assert.Equal(input.NextHops, output.NextHops) + assert.Equal(2, len(output.Nlris)) + for idx, inputNLRI := range input.Nlris { + outputNLRI := output.Nlris[idx] + assert.Equal(inputNLRI.TypeUrl, outputNLRI.TypeUrl) + assert.Equal(inputNLRI.Value, outputNLRI.Value) + } +} + +func Test_MpReachNLRIAttribute_EVPN_AD_Route(t *testing.T) { + assert := assert.New(t) + + nlris := make([]*any.Any, 0, 1) + rd, err := ptypes.MarshalAny(&api.RouteDistinguisherTwoOctetAS{ + Admin: 65000, + Assigned: 100, + }) + assert.Nil(err) + esi := &api.EthernetSegmentIdentifier{ + Type: 0, + Value: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}, + } + a, err := ptypes.MarshalAny(&api.EVPNEthernetAutoDiscoveryRoute{ + Rd: rd, + Esi: esi, + EthernetTag: 100, + Label: 200, + }) + assert.Nil(err) + nlris = append(nlris, a) + + input := &api.MpReachNLRIAttribute{ + Family: uint32(bgp.RF_EVPN), + NextHops: []string{"192.168.1.1"}, + Nlris: nlris, + } + + a, err = ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) + assert.Equal(input.Family, output.Family) + assert.Equal(input.NextHops, output.NextHops) + assert.Equal(1, len(output.Nlris)) + for idx, inputNLRI := range input.Nlris { + outputNLRI := output.Nlris[idx] + assert.Equal(inputNLRI.TypeUrl, outputNLRI.TypeUrl) + assert.Equal(inputNLRI.Value, outputNLRI.Value) + } +} + +func Test_MpReachNLRIAttribute_EVPN_MAC_IP_Route(t *testing.T) { + assert := assert.New(t) + + nlris := make([]*any.Any, 0, 1) + rd, err := ptypes.MarshalAny(&api.RouteDistinguisherIPAddress{ + Admin: "1.1.1.1", + Assigned: 100, + }) + assert.Nil(err) + esi := &api.EthernetSegmentIdentifier{ + Type: 0, + Value: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}, + } + a, err := ptypes.MarshalAny(&api.EVPNMACIPAdvertisementRoute{ + Rd: rd, + Esi: esi, + EthernetTag: 100, + MacAddress: "aa:bb:cc:dd:ee:ff", + IpAddress: "192.168.101.1", + Labels: []uint32{200}, + }) + assert.Nil(err) + nlris = append(nlris, a) + + input := &api.MpReachNLRIAttribute{ + Family: uint32(bgp.RF_EVPN), + NextHops: []string{"192.168.1.1"}, + Nlris: nlris, + } + + a, err = ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) + assert.Equal(input.Family, output.Family) + assert.Equal(input.NextHops, output.NextHops) + assert.Equal(1, len(output.Nlris)) + for idx, inputNLRI := range input.Nlris { + outputNLRI := output.Nlris[idx] + assert.Equal(inputNLRI.TypeUrl, outputNLRI.TypeUrl) + assert.Equal(inputNLRI.Value, outputNLRI.Value) + } +} + +func Test_MpReachNLRIAttribute_EVPN_MC_Route(t *testing.T) { + assert := assert.New(t) + + nlris := make([]*any.Any, 0, 1) + rd, err := ptypes.MarshalAny(&api.RouteDistinguisherFourOctetAS{ + Admin: 65000, + Assigned: 100, + }) + assert.Nil(err) + a, err := ptypes.MarshalAny(&api.EVPNInclusiveMulticastEthernetTagRoute{ + Rd: rd, + EthernetTag: 100, + IpAddress: "192.168.101.1", + }) + assert.Nil(err) + nlris = append(nlris, a) + + input := &api.MpReachNLRIAttribute{ + Family: uint32(bgp.RF_EVPN), + NextHops: []string{"192.168.1.1"}, + Nlris: nlris, + } + + a, err = ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) + assert.Equal(input.Family, output.Family) + assert.Equal(input.NextHops, output.NextHops) + assert.Equal(1, len(output.Nlris)) + for idx, inputNLRI := range input.Nlris { + outputNLRI := output.Nlris[idx] + assert.Equal(inputNLRI.TypeUrl, outputNLRI.TypeUrl) + assert.Equal(inputNLRI.Value, outputNLRI.Value) + } +} + +func Test_MpReachNLRIAttribute_EVPN_ES_Route(t *testing.T) { + assert := assert.New(t) + + nlris := make([]*any.Any, 0, 1) + rd, err := ptypes.MarshalAny(&api.RouteDistinguisherIPAddress{ + Admin: "1.1.1.1", + Assigned: 100, + }) + assert.Nil(err) + esi := &api.EthernetSegmentIdentifier{ + Type: 0, + Value: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}, + } + a, err := ptypes.MarshalAny(&api.EVPNEthernetSegmentRoute{ + Rd: rd, + Esi: esi, + IpAddress: "192.168.101.1", + }) + assert.Nil(err) + nlris = append(nlris, a) + + input := &api.MpReachNLRIAttribute{ + Family: uint32(bgp.RF_EVPN), + NextHops: []string{"192.168.1.1"}, + Nlris: nlris, + } + + a, err = ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) + assert.Equal(input.Family, output.Family) + assert.Equal(input.NextHops, output.NextHops) + assert.Equal(1, len(output.Nlris)) + for idx, inputNLRI := range input.Nlris { + outputNLRI := output.Nlris[idx] + assert.Equal(inputNLRI.TypeUrl, outputNLRI.TypeUrl) + assert.Equal(inputNLRI.Value, outputNLRI.Value) + } +} + +func Test_MpReachNLRIAttribute_EVPN_Prefix_Route(t *testing.T) { + assert := assert.New(t) + + nlris := make([]*any.Any, 0, 1) + rd, err := ptypes.MarshalAny(&api.RouteDistinguisherIPAddress{ + Admin: "1.1.1.1", + Assigned: 100, + }) + assert.Nil(err) + esi := &api.EthernetSegmentIdentifier{ + Type: 0, + Value: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}, + } + a, err := ptypes.MarshalAny(&api.EVPNIPPrefixRoute{ + Rd: rd, + Esi: esi, + EthernetTag: 100, + IpPrefixLen: 24, + IpPrefix: "192.168.101.0", + Label: 200, + }) + assert.Nil(err) + nlris = append(nlris, a) + + input := &api.MpReachNLRIAttribute{ + Family: uint32(bgp.RF_EVPN), + NextHops: []string{"192.168.1.1"}, + Nlris: nlris, + } + + a, err = ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) + assert.Equal(input.Family, output.Family) + assert.Equal(input.NextHops, output.NextHops) + assert.Equal(1, len(output.Nlris)) + for idx, inputNLRI := range input.Nlris { + outputNLRI := output.Nlris[idx] + assert.Equal(inputNLRI.TypeUrl, outputNLRI.TypeUrl) + assert.Equal(inputNLRI.Value, outputNLRI.Value) + } +} + +func Test_MpReachNLRIAttribute_IPv4_VPN(t *testing.T) { + assert := assert.New(t) + + nlris := make([]*any.Any, 0, 1) + rd, err := ptypes.MarshalAny(&api.RouteDistinguisherIPAddress{ + Admin: "1.1.1.1", + Assigned: 100, + }) + assert.Nil(err) + a, err := ptypes.MarshalAny(&api.LabeledVPNIPAddressPrefix{ + Labels: []uint32{100, 200}, + Rd: rd, + PrefixLen: 24, + Prefix: "192.168.101.0", + }) + assert.Nil(err) + nlris = append(nlris, a) + + input := &api.MpReachNLRIAttribute{ + Family: uint32(bgp.RF_IPv4_VPN), + NextHops: []string{"192.168.1.1"}, + Nlris: nlris, + } + + a, err = ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) + assert.Equal(input.Family, output.Family) + assert.Equal(input.NextHops, output.NextHops) + assert.Equal(1, len(output.Nlris)) + for idx, inputNLRI := range input.Nlris { + outputNLRI := output.Nlris[idx] + assert.Equal(inputNLRI.TypeUrl, outputNLRI.TypeUrl) + assert.Equal(inputNLRI.Value, outputNLRI.Value) + } +} + +func Test_MpReachNLRIAttribute_IPv6_VPN(t *testing.T) { + assert := assert.New(t) + + nlris := make([]*any.Any, 0, 1) + rd, err := ptypes.MarshalAny(&api.RouteDistinguisherIPAddress{ + Admin: "1.1.1.1", + Assigned: 100, + }) + assert.Nil(err) + a, err := ptypes.MarshalAny(&api.LabeledVPNIPAddressPrefix{ + Labels: []uint32{100, 200}, + Rd: rd, + PrefixLen: 64, + Prefix: "2001:db8:1::", + }) + assert.Nil(err) + nlris = append(nlris, a) + + input := &api.MpReachNLRIAttribute{ + Family: uint32(bgp.RF_IPv6_VPN), + NextHops: []string{"2001:db8::1"}, + Nlris: nlris, + } + + a, err = ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) + assert.Equal(input.Family, output.Family) + assert.Equal(input.NextHops, output.NextHops) + assert.Equal(1, len(output.Nlris)) + for idx, inputNLRI := range input.Nlris { + outputNLRI := output.Nlris[idx] + assert.Equal(inputNLRI.TypeUrl, outputNLRI.TypeUrl) + assert.Equal(inputNLRI.Value, outputNLRI.Value) + } +} + +func Test_MpReachNLRIAttribute_RTC_UC(t *testing.T) { + assert := assert.New(t) + + nlris := make([]*any.Any, 0, 1) + rt, err := ptypes.MarshalAny(&api.IPv4AddressSpecificExtended{ + IsTransitive: true, + SubType: 0x02, // Route Target + Address: "1.1.1.1", + LocalAdmin: 100, + }) + assert.Nil(err) + a, err := ptypes.MarshalAny(&api.RouteTargetMembershipNLRI{ + As: 65000, + Rt: rt, + }) + assert.Nil(err) + nlris = append(nlris, a) + + input := &api.MpReachNLRIAttribute{ + Family: uint32(bgp.RF_RTC_UC), + NextHops: []string{"192.168.1.1"}, + Nlris: nlris, + } + + a, err = ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) + assert.Equal(input.Family, output.Family) + assert.Equal(input.NextHops, output.NextHops) + assert.Equal(1, len(output.Nlris)) + for idx, inputNLRI := range input.Nlris { + outputNLRI := output.Nlris[idx] + assert.Equal(inputNLRI.TypeUrl, outputNLRI.TypeUrl) + assert.Equal(inputNLRI.Value, outputNLRI.Value) + } +} + +func Test_MpReachNLRIAttribute_FS_IPv4_UC(t *testing.T) { + assert := assert.New(t) + + rules := make([]*any.Any, 0, 3) + rule, err := ptypes.MarshalAny(&api.FlowSpecIPPrefix{ + Type: 1, // Destination Prefix + PrefixLen: 24, + Prefix: "192.168.101.0", + }) + assert.Nil(err) + rules = append(rules, rule) + rule, err = ptypes.MarshalAny(&api.FlowSpecIPPrefix{ + Type: 2, // Source Prefix + PrefixLen: 24, + Prefix: "192.168.201.0", + }) + assert.Nil(err) + rules = append(rules, rule) + rule, err = ptypes.MarshalAny(&api.FlowSpecComponent{ + Type: 3, // IP Protocol + Items: []*api.FlowSpecComponentItem{ + { + Op: 0x80 | 0x01, // End, EQ + Value: 6, // TCP + }, + }, + }) + assert.Nil(err) + rules = append(rules, rule) + + nlris := make([]*any.Any, 0, 1) + a, err := ptypes.MarshalAny(&api.FlowSpecNLRI{ + Rules: rules, + }) + assert.Nil(err) + nlris = append(nlris, a) + + input := &api.MpReachNLRIAttribute{ + Family: uint32(bgp.RF_FS_IPv4_UC), + // NextHops: // No nexthop required + Nlris: nlris, + } + + a, err = ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) + assert.Equal(input.Family, output.Family) + assert.Equal(input.NextHops, output.NextHops) + assert.Equal(1, len(output.Nlris)) + for idx, inputNLRI := range input.Nlris { + outputNLRI := output.Nlris[idx] + assert.Equal(inputNLRI.TypeUrl, outputNLRI.TypeUrl) + assert.Equal(inputNLRI.Value, outputNLRI.Value) + } +} + +func Test_MpReachNLRIAttribute_FS_IPv4_VPN(t *testing.T) { + assert := assert.New(t) + + rd, err := ptypes.MarshalAny(&api.RouteDistinguisherIPAddress{ + Admin: "1.1.1.1", + Assigned: 100, + }) + assert.Nil(err) + + rules := make([]*any.Any, 0, 3) + rule, err := ptypes.MarshalAny(&api.FlowSpecIPPrefix{ + Type: 1, // Destination Prefix + PrefixLen: 24, + Prefix: "192.168.101.0", + }) + assert.Nil(err) + rules = append(rules, rule) + rule, err = ptypes.MarshalAny(&api.FlowSpecIPPrefix{ + Type: 2, // Source Prefix + PrefixLen: 24, + Prefix: "192.168.201.0", + }) + assert.Nil(err) + rules = append(rules, rule) + rule, err = ptypes.MarshalAny(&api.FlowSpecComponent{ + Type: 3, // IP Protocol + Items: []*api.FlowSpecComponentItem{ + { + Op: 0x80 | 0x01, // End, EQ + Value: 6, // TCP + }, + }, + }) + assert.Nil(err) + rules = append(rules, rule) + + nlris := make([]*any.Any, 0, 1) + a, err := ptypes.MarshalAny(&api.VPNFlowSpecNLRI{ + Rd: rd, + Rules: rules, + }) + assert.Nil(err) + nlris = append(nlris, a) + + input := &api.MpReachNLRIAttribute{ + Family: uint32(bgp.RF_FS_IPv4_VPN), + // NextHops: // No nexthop required + Nlris: nlris, + } + + a, err = ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) + assert.Equal(input.Family, output.Family) + assert.Equal(input.NextHops, output.NextHops) + assert.Equal(1, len(output.Nlris)) + for idx, inputNLRI := range input.Nlris { + outputNLRI := output.Nlris[idx] + assert.Equal(inputNLRI.TypeUrl, outputNLRI.TypeUrl) + assert.Equal(inputNLRI.Value, outputNLRI.Value) + } +} + +func Test_MpReachNLRIAttribute_FS_IPv6_UC(t *testing.T) { + assert := assert.New(t) + + rules := make([]*any.Any, 0, 3) + rule, err := ptypes.MarshalAny(&api.FlowSpecIPPrefix{ + Type: 1, // Destination Prefix + PrefixLen: 64, + Prefix: "2001:db8:1::", + }) + assert.Nil(err) + rules = append(rules, rule) + rule, err = ptypes.MarshalAny(&api.FlowSpecIPPrefix{ + Type: 2, // Source Prefix + PrefixLen: 64, + Prefix: "2001:db8:2::", + }) + assert.Nil(err) + rules = append(rules, rule) + rule, err = ptypes.MarshalAny(&api.FlowSpecComponent{ + Type: 3, // Next Header + Items: []*api.FlowSpecComponentItem{ + { + Op: 0x80 | 0x01, // End, EQ + Value: 6, // TCP + }, + }, + }) + assert.Nil(err) + rules = append(rules, rule) + + nlris := make([]*any.Any, 0, 1) + a, err := ptypes.MarshalAny(&api.FlowSpecNLRI{ + Rules: rules, + }) + assert.Nil(err) + nlris = append(nlris, a) + + input := &api.MpReachNLRIAttribute{ + Family: uint32(bgp.RF_FS_IPv6_UC), + // NextHops: // No nexthop required + Nlris: nlris, + } + + a, err = ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) + assert.Equal(input.Family, output.Family) + assert.Equal(input.NextHops, output.NextHops) + assert.Equal(1, len(output.Nlris)) + for idx, inputNLRI := range input.Nlris { + outputNLRI := output.Nlris[idx] + assert.Equal(inputNLRI.TypeUrl, outputNLRI.TypeUrl) + assert.Equal(inputNLRI.Value, outputNLRI.Value) + } +} + +func Test_MpReachNLRIAttribute_FS_IPv6_VPN(t *testing.T) { + assert := assert.New(t) + + rd, err := ptypes.MarshalAny(&api.RouteDistinguisherIPAddress{ + Admin: "1.1.1.1", + Assigned: 100, + }) + assert.Nil(err) + + rules := make([]*any.Any, 0, 3) + rule, err := ptypes.MarshalAny(&api.FlowSpecIPPrefix{ + Type: 1, // Destination Prefix + PrefixLen: 64, + Prefix: "2001:db8:1::", + }) + assert.Nil(err) + rules = append(rules, rule) + rule, err = ptypes.MarshalAny(&api.FlowSpecIPPrefix{ + Type: 2, // Source Prefix + PrefixLen: 64, + Prefix: "2001:db8:2::", + }) + assert.Nil(err) + rules = append(rules, rule) + rule, err = ptypes.MarshalAny(&api.FlowSpecComponent{ + Type: 3, // Next Header + Items: []*api.FlowSpecComponentItem{ + { + Op: 0x80 | 0x01, // End, EQ + Value: 6, // TCP + }, + }, + }) + assert.Nil(err) + rules = append(rules, rule) + + nlris := make([]*any.Any, 0, 1) + a, err := ptypes.MarshalAny(&api.VPNFlowSpecNLRI{ + Rd: rd, + Rules: rules, + }) + assert.Nil(err) + nlris = append(nlris, a) + + input := &api.MpReachNLRIAttribute{ + Family: uint32(bgp.RF_FS_IPv6_VPN), + // NextHops: // No nexthop required + Nlris: nlris, + } + + a, err = ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) + assert.Equal(input.Family, output.Family) + assert.Equal(input.NextHops, output.NextHops) + assert.Equal(1, len(output.Nlris)) + for idx, inputNLRI := range input.Nlris { + outputNLRI := output.Nlris[idx] + assert.Equal(inputNLRI.TypeUrl, outputNLRI.TypeUrl) + assert.Equal(inputNLRI.Value, outputNLRI.Value) + } +} + +func Test_MpReachNLRIAttribute_FS_L2_VPN(t *testing.T) { + assert := assert.New(t) + + rd, err := ptypes.MarshalAny(&api.RouteDistinguisherIPAddress{ + Admin: "1.1.1.1", + Assigned: 100, + }) + assert.Nil(err) + + rules := make([]*any.Any, 0, 3) + rule, err := ptypes.MarshalAny(&api.FlowSpecMAC{ + Type: 15, // Source MAC + Address: "aa:bb:cc:11:22:33", + }) + assert.Nil(err) + rules = append(rules, rule) + rule, err = ptypes.MarshalAny(&api.FlowSpecMAC{ + Type: 16, // Destination MAC + Address: "dd:ee:ff:11:22:33", + }) + assert.Nil(err) + rules = append(rules, rule) + rule, err = ptypes.MarshalAny(&api.FlowSpecComponent{ + Type: 21, // VLAN ID + Items: []*api.FlowSpecComponentItem{ + { + Op: 0x80 | 0x01, // End, EQ + Value: 100, + }, + }, + }) + assert.Nil(err) + rules = append(rules, rule) + + nlris := make([]*any.Any, 0, 1) + a, err := ptypes.MarshalAny(&api.VPNFlowSpecNLRI{ + Rd: rd, + Rules: rules, + }) + assert.Nil(err) + nlris = append(nlris, a) + + input := &api.MpReachNLRIAttribute{ + Family: uint32(bgp.RF_FS_L2_VPN), + // NextHops: // No nexthop required + Nlris: nlris, + } + + a, err = ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewMpReachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpReachNLRI)) + assert.Equal(input.Family, output.Family) + assert.Equal(input.NextHops, output.NextHops) + assert.Equal(1, len(output.Nlris)) + for idx, inputNLRI := range input.Nlris { + outputNLRI := output.Nlris[idx] + assert.Equal(inputNLRI.TypeUrl, outputNLRI.TypeUrl) + assert.Equal(inputNLRI.Value, outputNLRI.Value) + } +} + +func Test_MpUnreachNLRIAttribute_IPv4_UC(t *testing.T) { + assert := assert.New(t) + + nlris := make([]*any.Any, 0, 2) + a, err := ptypes.MarshalAny(&api.IPAddressPrefix{ + PrefixLen: 24, + Prefix: "192.168.101.0", + }) + assert.Nil(err) + nlris = append(nlris, a) + a, err = ptypes.MarshalAny(&api.IPAddressPrefix{ + PrefixLen: 24, + Prefix: "192.168.201.0", + }) + assert.Nil(err) + nlris = append(nlris, a) + + input := &api.MpUnreachNLRIAttribute{ + Family: uint32(bgp.RF_IPv4_UC), + Nlris: nlris, + } + + a, err = ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewMpUnreachNLRIAttributeFromNative(n.(*bgp.PathAttributeMpUnreachNLRI)) + assert.Equal(input.Family, output.Family) + assert.Equal(2, len(output.Nlris)) + for idx, inputNLRI := range input.Nlris { + outputNLRI := output.Nlris[idx] + assert.Equal(inputNLRI.TypeUrl, outputNLRI.TypeUrl) + assert.Equal(inputNLRI.Value, outputNLRI.Value) + } +} + +func Test_ExtendedCommunitiesAttribute(t *testing.T) { + assert := assert.New(t) + + communities := make([]*any.Any, 0, 19) + a, err := ptypes.MarshalAny(&api.TwoOctetAsSpecificExtended{ + IsTransitive: true, + SubType: 0x02, // ROUTE_TARGET + As: 65001, + LocalAdmin: 100, + }) + assert.Nil(err) + communities = append(communities, a) + a, err = ptypes.MarshalAny(&api.IPv4AddressSpecificExtended{ + IsTransitive: true, + SubType: 0x02, // ROUTE_TARGET + Address: "2.2.2.2", + LocalAdmin: 200, + }) + assert.Nil(err) + communities = append(communities, a) + a, err = ptypes.MarshalAny(&api.FourOctetAsSpecificExtended{ + IsTransitive: true, + SubType: 0x02, // ROUTE_TARGET + As: 65003, + LocalAdmin: 300, + }) + assert.Nil(err) + communities = append(communities, a) + a, err = ptypes.MarshalAny(&api.ValidationExtended{ + State: 0, // VALID + }) + assert.Nil(err) + communities = append(communities, a) + a, err = ptypes.MarshalAny(&api.ColorExtended{ + Color: 400, + }) + assert.Nil(err) + communities = append(communities, a) + a, err = ptypes.MarshalAny(&api.EncapExtended{ + TunnelType: 8, // VXLAN + }) + assert.Nil(err) + communities = append(communities, a) + a, err = ptypes.MarshalAny(&api.DefaultGatewayExtended{ + // No value + }) + assert.Nil(err) + communities = append(communities, a) + a, err = ptypes.MarshalAny(&api.OpaqueExtended{ + IsTransitive: true, + Value: []byte{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}, + }) + assert.Nil(err) + communities = append(communities, a) + a, err = ptypes.MarshalAny(&api.ESILabelExtended{ + IsSingleActive: true, + Label: 500, + }) + assert.Nil(err) + communities = append(communities, a) + a, err = ptypes.MarshalAny(&api.ESImportRouteTarget{ + EsImport: "aa:bb:cc:dd:ee:ff", + }) + assert.Nil(err) + communities = append(communities, a) + a, err = ptypes.MarshalAny(&api.MacMobilityExtended{ + IsSticky: true, + SequenceNum: 1, + }) + assert.Nil(err) + communities = append(communities, a) + a, err = ptypes.MarshalAny(&api.RouterMacExtended{ + Mac: "ff:ee:dd:cc:bb:aa", + }) + assert.Nil(err) + communities = append(communities, a) + a, err = ptypes.MarshalAny(&api.TrafficRateExtended{ + As: 65004, + Rate: 100.0, + }) + assert.Nil(err) + communities = append(communities, a) + a, err = ptypes.MarshalAny(&api.TrafficActionExtended{ + Terminal: true, + Sample: false, + }) + assert.Nil(err) + communities = append(communities, a) + a, err = ptypes.MarshalAny(&api.RedirectTwoOctetAsSpecificExtended{ + As: 65005, + LocalAdmin: 500, + }) + assert.Nil(err) + communities = append(communities, a) + a, err = ptypes.MarshalAny(&api.RedirectIPv4AddressSpecificExtended{ + Address: "6.6.6.6", + LocalAdmin: 600, + }) + assert.Nil(err) + communities = append(communities, a) + a, err = ptypes.MarshalAny(&api.RedirectFourOctetAsSpecificExtended{ + As: 65007, + LocalAdmin: 700, + }) + assert.Nil(err) + communities = append(communities, a) + a, err = ptypes.MarshalAny(&api.TrafficRemarkExtended{ + Dscp: 0x0a, // AF11 + }) + assert.Nil(err) + communities = append(communities, a) + a, err = ptypes.MarshalAny(&api.UnknownExtended{ + Type: 0xff, // Max of uint8 + Value: []byte{1, 2, 3, 4, 5, 6, 7}, + }) + assert.Nil(err) + communities = append(communities, a) + + input := &api.ExtendedCommunitiesAttribute{ + Communities: communities, + } + + a, err = ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewExtendedCommunitiesAttributeFromNative(n.(*bgp.PathAttributeExtendedCommunities)) + assert.Equal(19, len(output.Communities)) + for idx, inputCommunity := range input.Communities { + outputCommunity := output.Communities[idx] + assert.Equal(inputCommunity.TypeUrl, outputCommunity.TypeUrl) + assert.Equal(inputCommunity.Value, outputCommunity.Value) + } +} + +func Test_As4PathAttribute(t *testing.T) { + assert := assert.New(t) + + input := &api.As4PathAttribute{ + Segments: []*api.AsSegment{ + { + Type: 1, // SET + Numbers: []uint32{100, 200}, + }, + { + Type: 2, // SEQ + Numbers: []uint32{300, 400}, + }, + }, + } + + a, err := ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewAs4PathAttributeFromNative(n.(*bgp.PathAttributeAs4Path)) + assert.Equal(2, len(output.Segments)) + assert.Equal(input.Segments, output.Segments) +} + +func Test_As4AggregatorAttribute(t *testing.T) { + assert := assert.New(t) + + input := &api.As4AggregatorAttribute{ + As: 65000, + Address: "1.1.1.1", + } + + a, err := ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewAs4AggregatorAttributeFromNative(n.(*bgp.PathAttributeAs4Aggregator)) + assert.Equal(input.As, output.As) + assert.Equal(input.Address, output.Address) +} + +func Test_PmsiTunnelAttribute(t *testing.T) { + assert := assert.New(t) + + input := &api.PmsiTunnelAttribute{ + Flags: 0x01, // IsLeafInfoRequired = true + Type: 6, // INGRESS_REPL + Label: 100, + Id: net.ParseIP("1.1.1.1").To4(), // IngressReplTunnelID with IPv4 + } + + a, err := ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewPmsiTunnelAttributeFromNative(n.(*bgp.PathAttributePmsiTunnel)) + assert.Equal(input.Flags, output.Flags) + assert.Equal(input.Type, output.Type) + assert.Equal(input.Label, output.Label) + assert.Equal(input.Id, output.Id) +} + +func Test_TunnelEncapAttribute(t *testing.T) { + assert := assert.New(t) + + subTlvs := make([]*any.Any, 0, 4) + a, err := ptypes.MarshalAny(&api.TunnelEncapSubTLVEncapsulation{ + Key: 100, + Cookie: []byte{0x11, 0x22, 0x33, 0x44}, + }) + assert.Nil(err) + subTlvs = append(subTlvs, a) + a, err = ptypes.MarshalAny(&api.TunnelEncapSubTLVProtocol{ + Protocol: 200, + }) + assert.Nil(err) + subTlvs = append(subTlvs, a) + a, err = ptypes.MarshalAny(&api.TunnelEncapSubTLVColor{ + Color: 300, + }) + assert.Nil(err) + subTlvs = append(subTlvs, a) + a, err = ptypes.MarshalAny(&api.TunnelEncapSubTLVUnknown{ + Type: 0xff, // Max of uint8 + Value: []byte{0x55, 0x66, 0x77, 0x88}, + }) + assert.Nil(err) + subTlvs = append(subTlvs, a) + + input := &api.TunnelEncapAttribute{ + Tlvs: []*api.TunnelEncapTLV{ + { + Type: 8, // VXLAN + Tlvs: subTlvs, + }, + }, + } + + a, err = ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewTunnelEncapAttributeFromNative(n.(*bgp.PathAttributeTunnelEncap)) + assert.Equal(1, len(output.Tlvs)) + assert.Equal(input.Tlvs[0].Type, output.Tlvs[0].Type) + assert.Equal(len(output.Tlvs[0].Tlvs), len(output.Tlvs[0].Tlvs)) + for idx, inputSubTlv := range input.Tlvs[0].Tlvs { + outputSubTlv := output.Tlvs[0].Tlvs[idx] + assert.Equal(inputSubTlv.TypeUrl, outputSubTlv.TypeUrl) + assert.Equal(inputSubTlv.Value, outputSubTlv.Value) + } +} + +func Test_IP6ExtendedCommunitiesAttribute(t *testing.T) { + assert := assert.New(t) + + communities := make([]*any.Any, 0, 2) + a, err := ptypes.MarshalAny(&api.IPv6AddressSpecificExtended{ + IsTransitive: true, + SubType: 0xff, // Max of uint8 + Address: "2001:db8:1::1", + LocalAdmin: 100, + }) + assert.Nil(err) + communities = append(communities, a) + a, err = ptypes.MarshalAny(&api.RedirectIPv6AddressSpecificExtended{ + Address: "2001:db8:2::1", + LocalAdmin: 200, + }) + assert.Nil(err) + communities = append(communities, a) + + input := &api.IP6ExtendedCommunitiesAttribute{ + Communities: communities, + } + + a, err = ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewIP6ExtendedCommunitiesAttributeFromNative(n.(*bgp.PathAttributeIP6ExtendedCommunities)) + assert.Equal(2, len(output.Communities)) + for idx, inputCommunity := range input.Communities { + outputCommunity := output.Communities[idx] + assert.Equal(inputCommunity.TypeUrl, outputCommunity.TypeUrl) + assert.Equal(inputCommunity.Value, outputCommunity.Value) + } +} + +func Test_AigpAttribute(t *testing.T) { + assert := assert.New(t) + + tlvs := make([]*any.Any, 0, 2) + a, err := ptypes.MarshalAny(&api.AigpTLVIGPMetric{ + Metric: 50, + }) + assert.Nil(err) + tlvs = append(tlvs, a) + a, err = ptypes.MarshalAny(&api.AigpTLVUnknown{ + Type: 0xff, // Max of uint8 + Value: []byte{0x11, 0x22, 0x33, 0x44}, + }) + assert.Nil(err) + tlvs = append(tlvs, a) + + input := &api.AigpAttribute{ + Tlvs: tlvs, + } + + a, err = ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewAigpAttributeFromNative(n.(*bgp.PathAttributeAigp)) + assert.Equal(2, len(output.Tlvs)) + for idx, inputTlv := range input.Tlvs { + outputTlv := output.Tlvs[idx] + assert.Equal(inputTlv.TypeUrl, outputTlv.TypeUrl) + assert.Equal(inputTlv.Value, outputTlv.Value) + } +} + +func Test_LargeCommunitiesAttribute(t *testing.T) { + assert := assert.New(t) + + input := &api.LargeCommunitiesAttribute{ + Communities: []*api.LargeCommunity{ + { + GlobalAdmin: 65001, + LocalData1: 100, + LocalData2: 200, + }, + { + GlobalAdmin: 65002, + LocalData1: 300, + LocalData2: 400, + }, + }, + } + + a, err := ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewLargeCommunitiesAttributeFromNative(n.(*bgp.PathAttributeLargeCommunities)) + assert.Equal(2, len(output.Communities)) + assert.Equal(input.Communities, output.Communities) +} + +func Test_UnknownAttribute(t *testing.T) { + assert := assert.New(t) + + input := &api.UnknownAttribute{ + Flags: (1 << 6) | (1 << 7), // OPTIONAL and TRANSITIVE + Type: 0xff, + Value: []byte{0x11, 0x22, 0x33, 0x44}, + } + + a, err := ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalAttribute(a) + assert.Nil(err) + + output := NewUnknownAttributeFromNative(n.(*bgp.PathAttributeUnknown)) + assert.Equal(input.Flags, output.Flags) + assert.Equal(input.Type, output.Type) + assert.Equal(input.Value, output.Value) +} diff --git a/internal/pkg/apiutil/capability.go b/internal/pkg/apiutil/capability.go new file mode 100644 index 00000000..0e3b4c0b --- /dev/null +++ b/internal/pkg/apiutil/capability.go @@ -0,0 +1,244 @@ +// 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 apiutil + +import ( + "fmt" + + proto "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/ptypes" + "github.com/golang/protobuf/ptypes/any" + api "github.com/osrg/gobgp/api" + "github.com/osrg/gobgp/pkg/packet/bgp" +) + +func NewMultiProtocolCapability(a *bgp.CapMultiProtocol) *api.MultiProtocolCapability { + return &api.MultiProtocolCapability{ + Family: api.Family(a.CapValue), + } +} + +func NewRouteRefreshCapability(a *bgp.CapRouteRefresh) *api.RouteRefreshCapability { + return &api.RouteRefreshCapability{} +} + +func NewCarryingLabelInfoCapability(a *bgp.CapCarryingLabelInfo) *api.CarryingLabelInfoCapability { + return &api.CarryingLabelInfoCapability{} +} + +func NewExtendedNexthopCapability(a *bgp.CapExtendedNexthop) *api.ExtendedNexthopCapability { + tuples := make([]*api.ExtendedNexthopCapabilityTuple, 0, len(a.Tuples)) + for _, t := range a.Tuples { + tuples = append(tuples, &api.ExtendedNexthopCapabilityTuple{ + NlriFamily: api.Family(bgp.AfiSafiToRouteFamily(t.NLRIAFI, uint8(t.NLRISAFI))), + NexthopFamily: api.Family(bgp.AfiSafiToRouteFamily(t.NexthopAFI, bgp.SAFI_UNICAST)), + }) + } + return &api.ExtendedNexthopCapability{ + Tuples: tuples, + } +} + +func NewGracefulRestartCapability(a *bgp.CapGracefulRestart) *api.GracefulRestartCapability { + tuples := make([]*api.GracefulRestartCapabilityTuple, 0, len(a.Tuples)) + for _, t := range a.Tuples { + tuples = append(tuples, &api.GracefulRestartCapabilityTuple{ + Family: api.Family(bgp.AfiSafiToRouteFamily(t.AFI, uint8(t.SAFI))), + Flags: uint32(t.Flags), + }) + } + return &api.GracefulRestartCapability{ + Flags: uint32(a.Flags), + Time: uint32(a.Time), + Tuples: tuples, + } +} + +func NewFourOctetASNumberCapability(a *bgp.CapFourOctetASNumber) *api.FourOctetASNumberCapability { + return &api.FourOctetASNumberCapability{ + As: a.CapValue, + } +} + +func NewAddPathCapability(a *bgp.CapAddPath) *api.AddPathCapability { + tuples := make([]*api.AddPathCapabilityTuple, 0, len(a.Tuples)) + for _, t := range a.Tuples { + tuples = append(tuples, &api.AddPathCapabilityTuple{ + Family: api.Family(t.RouteFamily), + Mode: api.AddPathMode(t.Mode), + }) + } + return &api.AddPathCapability{ + Tuples: tuples, + } +} + +func NewEnhancedRouteRefreshCapability(a *bgp.CapEnhancedRouteRefresh) *api.EnhancedRouteRefreshCapability { + return &api.EnhancedRouteRefreshCapability{} +} + +func NewLongLivedGracefulRestartCapability(a *bgp.CapLongLivedGracefulRestart) *api.LongLivedGracefulRestartCapability { + tuples := make([]*api.LongLivedGracefulRestartCapabilityTuple, 0, len(a.Tuples)) + for _, t := range a.Tuples { + tuples = append(tuples, &api.LongLivedGracefulRestartCapabilityTuple{ + Family: api.Family(bgp.AfiSafiToRouteFamily(t.AFI, uint8(t.SAFI))), + Flags: uint32(t.Flags), + Time: t.RestartTime, + }) + } + return &api.LongLivedGracefulRestartCapability{ + Tuples: tuples, + } +} + +func NewRouteRefreshCiscoCapability(a *bgp.CapRouteRefreshCisco) *api.RouteRefreshCiscoCapability { + return &api.RouteRefreshCiscoCapability{} +} + +func NewUnknownCapability(a *bgp.CapUnknown) *api.UnknownCapability { + return &api.UnknownCapability{ + Code: uint32(a.CapCode), + Value: a.CapValue, + } +} + +func MarshalCapability(value bgp.ParameterCapabilityInterface) (*any.Any, error) { + var m proto.Message + switch n := value.(type) { + case *bgp.CapMultiProtocol: + m = NewMultiProtocolCapability(n) + case *bgp.CapRouteRefresh: + m = NewRouteRefreshCapability(n) + case *bgp.CapCarryingLabelInfo: + m = NewCarryingLabelInfoCapability(n) + case *bgp.CapExtendedNexthop: + m = NewExtendedNexthopCapability(n) + case *bgp.CapGracefulRestart: + m = NewGracefulRestartCapability(n) + case *bgp.CapFourOctetASNumber: + m = NewFourOctetASNumberCapability(n) + case *bgp.CapAddPath: + m = NewAddPathCapability(n) + case *bgp.CapEnhancedRouteRefresh: + m = NewEnhancedRouteRefreshCapability(n) + case *bgp.CapLongLivedGracefulRestart: + m = NewLongLivedGracefulRestartCapability(n) + case *bgp.CapRouteRefreshCisco: + m = NewRouteRefreshCiscoCapability(n) + case *bgp.CapUnknown: + m = NewUnknownCapability(n) + default: + return nil, fmt.Errorf("invalid capability type to marshal: %+v", value) + } + return ptypes.MarshalAny(m) +} + +func MarshalCapabilities(values []bgp.ParameterCapabilityInterface) ([]*any.Any, error) { + caps := make([]*any.Any, 0, len(values)) + for _, value := range values { + a, err := MarshalCapability(value) + if err != nil { + return nil, err + } + caps = append(caps, a) + } + return caps, nil +} + +func unmarshalCapability(a *any.Any) (bgp.ParameterCapabilityInterface, error) { + var value ptypes.DynamicAny + if err := ptypes.UnmarshalAny(a, &value); err != nil { + return nil, fmt.Errorf("failed to unmarshal capability: %s", err) + } + switch a := value.Message.(type) { + case *api.MultiProtocolCapability: + return bgp.NewCapMultiProtocol(bgp.RouteFamily(a.Family)), nil + case *api.RouteRefreshCapability: + return bgp.NewCapRouteRefresh(), nil + case *api.CarryingLabelInfoCapability: + return bgp.NewCapCarryingLabelInfo(), nil + case *api.ExtendedNexthopCapability: + tuples := make([]*bgp.CapExtendedNexthopTuple, 0, len(a.Tuples)) + for _, t := range a.Tuples { + var nhAfi uint16 + switch t.NexthopFamily { + case api.Family_IPv4: + nhAfi = bgp.AFI_IP + case api.Family_IPv6: + nhAfi = bgp.AFI_IP6 + default: + return nil, fmt.Errorf("invalid address family for nexthop afi in extended nexthop capability: %s", t.NexthopFamily) + } + tuples = append(tuples, bgp.NewCapExtendedNexthopTuple(bgp.RouteFamily(t.NlriFamily), nhAfi)) + } + return bgp.NewCapExtendedNexthop(tuples), nil + case *api.GracefulRestartCapability: + tuples := make([]*bgp.CapGracefulRestartTuple, 0, len(a.Tuples)) + for _, t := range a.Tuples { + var forward bool + if t.Flags&0x80 > 0 { + forward = true + } + tuples = append(tuples, bgp.NewCapGracefulRestartTuple(bgp.RouteFamily(t.Family), forward)) + } + var restarting bool + if a.Flags&0x08 > 0 { + restarting = true + } + var notification bool + if a.Flags&0x04 > 0 { + notification = true + } + return bgp.NewCapGracefulRestart(restarting, notification, uint16(a.Time), tuples), nil + case *api.FourOctetASNumberCapability: + return bgp.NewCapFourOctetASNumber(a.As), nil + case *api.AddPathCapability: + tuples := make([]*bgp.CapAddPathTuple, 0, len(a.Tuples)) + for _, t := range a.Tuples { + tuples = append(tuples, bgp.NewCapAddPathTuple(bgp.RouteFamily(t.Family), bgp.BGPAddPathMode(t.Mode))) + } + return bgp.NewCapAddPath(tuples), nil + case *api.EnhancedRouteRefreshCapability: + return bgp.NewCapEnhancedRouteRefresh(), nil + case *api.LongLivedGracefulRestartCapability: + tuples := make([]*bgp.CapLongLivedGracefulRestartTuple, 0, len(a.Tuples)) + for _, t := range a.Tuples { + var forward bool + if t.Flags&0x80 > 0 { + forward = true + } + tuples = append(tuples, bgp.NewCapLongLivedGracefulRestartTuple(bgp.RouteFamily(t.Family), forward, t.Time)) + } + return bgp.NewCapLongLivedGracefulRestart(tuples), nil + case *api.RouteRefreshCiscoCapability: + return bgp.NewCapRouteRefreshCisco(), nil + case *api.UnknownCapability: + return bgp.NewCapUnknown(bgp.BGPCapabilityCode(a.Code), a.Value), nil + } + return nil, fmt.Errorf("invalid capability type to unmarshal: %s", a.TypeUrl) +} + +func UnmarshalCapabilities(values []*any.Any) ([]bgp.ParameterCapabilityInterface, error) { + caps := make([]bgp.ParameterCapabilityInterface, 0, len(values)) + for _, value := range values { + c, err := unmarshalCapability(value) + if err != nil { + return nil, err + } + caps = append(caps, c) + } + return caps, nil +} diff --git a/internal/pkg/apiutil/capability_test.go b/internal/pkg/apiutil/capability_test.go new file mode 100644 index 00000000..0872ed58 --- /dev/null +++ b/internal/pkg/apiutil/capability_test.go @@ -0,0 +1,251 @@ +// 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 apiutil + +import ( + "testing" + + "github.com/golang/protobuf/ptypes" + api "github.com/osrg/gobgp/api" + "github.com/osrg/gobgp/pkg/packet/bgp" + "github.com/stretchr/testify/assert" +) + +func Test_MultiProtocolCapability(t *testing.T) { + assert := assert.New(t) + + input := &api.MultiProtocolCapability{ + Family: api.Family_IPv4, + } + + a, err := ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalCapability(a) + assert.Nil(err) + c := n.(*bgp.CapMultiProtocol) + assert.Equal(bgp.RF_IPv4_UC, c.CapValue) + + output := NewMultiProtocolCapability(c) + assert.Equal(input, output) +} + +func Test_RouteRefreshCapability(t *testing.T) { + assert := assert.New(t) + + input := &api.RouteRefreshCapability{} + + a, err := ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalCapability(a) + assert.Nil(err) + + output := NewRouteRefreshCapability(n.(*bgp.CapRouteRefresh)) + assert.Equal(input, output) +} + +func Test_CarryingLabelInfoCapability(t *testing.T) { + assert := assert.New(t) + + input := &api.CarryingLabelInfoCapability{} + + a, err := ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalCapability(a) + assert.Nil(err) + + output := NewCarryingLabelInfoCapability(n.(*bgp.CapCarryingLabelInfo)) + assert.Equal(input, output) +} + +func Test_ExtendedNexthopCapability(t *testing.T) { + assert := assert.New(t) + + input := &api.ExtendedNexthopCapability{ + Tuples: []*api.ExtendedNexthopCapabilityTuple{ + { + NlriFamily: api.Family_IPv4, + NexthopFamily: api.Family_IPv6, + }, + }, + } + + a, err := ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalCapability(a) + assert.Nil(err) + c := n.(*bgp.CapExtendedNexthop) + assert.Equal(1, len(c.Tuples)) + assert.Equal(uint16(bgp.AFI_IP), c.Tuples[0].NLRIAFI) + assert.Equal(uint16(bgp.SAFI_UNICAST), c.Tuples[0].NLRISAFI) + assert.Equal(uint16(bgp.AFI_IP6), c.Tuples[0].NexthopAFI) + + output := NewExtendedNexthopCapability(c) + assert.Equal(input, output) +} + +func Test_GracefulRestartCapability(t *testing.T) { + assert := assert.New(t) + + input := &api.GracefulRestartCapability{ + Flags: 0x08 | 0x04, // restarting|notification + Time: 90, + Tuples: []*api.GracefulRestartCapabilityTuple{ + { + Family: api.Family_IPv4, + Flags: 0x80, // forward + }, + }, + } + + a, err := ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalCapability(a) + assert.Nil(err) + + c := n.(*bgp.CapGracefulRestart) + assert.Equal(1, len(c.Tuples)) + assert.Equal(uint8(0x08|0x04), c.Flags) + assert.Equal(uint16(90), c.Time) + assert.Equal(uint16(bgp.AFI_IP), c.Tuples[0].AFI) + assert.Equal(uint8(bgp.SAFI_UNICAST), c.Tuples[0].SAFI) + assert.Equal(uint8(0x80), c.Tuples[0].Flags) + + output := NewGracefulRestartCapability(c) + assert.Equal(input, output) +} + +func Test_FourOctetASNumberCapability(t *testing.T) { + assert := assert.New(t) + + input := &api.FourOctetASNumberCapability{ + As: 100, + } + + a, err := ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalCapability(a) + assert.Nil(err) + + c := n.(*bgp.CapFourOctetASNumber) + assert.Equal(uint32(100), c.CapValue) + + output := NewFourOctetASNumberCapability(c) + assert.Equal(input, output) +} + +func Test_AddPathCapability(t *testing.T) { + assert := assert.New(t) + + input := &api.AddPathCapability{ + Tuples: []*api.AddPathCapabilityTuple{ + { + Family: api.Family_IPv4, + Mode: api.AddPathMode_MODE_BOTH, + }, + }, + } + + a, err := ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalCapability(a) + assert.Nil(err) + + c := n.(*bgp.CapAddPath) + assert.Equal(1, len(c.Tuples)) + assert.Equal(bgp.RF_IPv4_UC, c.Tuples[0].RouteFamily) + assert.Equal(bgp.BGP_ADD_PATH_BOTH, c.Tuples[0].Mode) + + output := NewAddPathCapability(c) + assert.Equal(input, output) +} + +func Test_EnhancedRouteRefreshCapability(t *testing.T) { + assert := assert.New(t) + + input := &api.EnhancedRouteRefreshCapability{} + + a, err := ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalCapability(a) + assert.Nil(err) + + output := NewEnhancedRouteRefreshCapability(n.(*bgp.CapEnhancedRouteRefresh)) + assert.Equal(input, output) +} + +func Test_LongLivedGracefulRestartCapability(t *testing.T) { + assert := assert.New(t) + + input := &api.LongLivedGracefulRestartCapability{ + Tuples: []*api.LongLivedGracefulRestartCapabilityTuple{ + { + Family: api.Family_IPv4, + Flags: 0x80, // forward + Time: 90, + }, + }, + } + + a, err := ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalCapability(a) + assert.Nil(err) + + c := n.(*bgp.CapLongLivedGracefulRestart) + assert.Equal(1, len(c.Tuples)) + assert.Equal(uint16(bgp.AFI_IP), c.Tuples[0].AFI) + assert.Equal(uint8(bgp.SAFI_UNICAST), c.Tuples[0].SAFI) + assert.Equal(uint8(0x80), c.Tuples[0].Flags) + assert.Equal(uint32(90), c.Tuples[0].RestartTime) + + output := NewLongLivedGracefulRestartCapability(c) + assert.Equal(input, output) +} + +func Test_RouteRefreshCiscoCapability(t *testing.T) { + assert := assert.New(t) + + input := &api.RouteRefreshCiscoCapability{} + + a, err := ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalCapability(a) + assert.Nil(err) + + output := NewRouteRefreshCiscoCapability(n.(*bgp.CapRouteRefreshCisco)) + assert.Equal(input, output) +} + +func Test_UnknownCapability(t *testing.T) { + assert := assert.New(t) + + input := &api.UnknownCapability{ + Code: 0xff, + Value: []byte{0x11, 0x22, 0x33, 0x44}, + } + + a, err := ptypes.MarshalAny(input) + assert.Nil(err) + n, err := unmarshalCapability(a) + assert.Nil(err) + + c := n.(*bgp.CapUnknown) + assert.Equal(bgp.BGPCapabilityCode(0xff), c.CapCode) + assert.Equal([]byte{0x11, 0x22, 0x33, 0x44}, c.CapValue) + + output := NewUnknownCapability(c) + assert.Equal(input, output) +} diff --git a/internal/pkg/apiutil/util.go b/internal/pkg/apiutil/util.go new file mode 100644 index 00000000..45cf1a04 --- /dev/null +++ b/internal/pkg/apiutil/util.go @@ -0,0 +1,114 @@ +// 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 apiutil + +import ( + "encoding/json" + "net" + "time" + + api "github.com/osrg/gobgp/api" + "github.com/osrg/gobgp/pkg/packet/bgp" +) + +// workaround. This for the json format compatibility. Once we update senario tests, we can remove this. +type Path struct { + Nlri bgp.AddrPrefixInterface `json:"nlri"` + Age int64 `json:"age"` + Best bool `json:"best"` + Attrs []bgp.PathAttributeInterface `json:"attrs"` + Stale bool `json:"stale"` + Withdrawal bool `json:"withdrawal,omitempty"` + SourceID net.IP `json:"source-id,omitempty"` + NeighborIP net.IP `json:"neighbor-ip,omitempty"` +} + +type Destination struct { + Paths []*Path +} + +func (d *Destination) MarshalJSON() ([]byte, error) { + return json.Marshal(d.Paths) +} + +func NewDestination(dst *api.Destination) *Destination { + l := make([]*Path, 0, len(dst.Paths)) + for _, p := range dst.Paths { + nlri, _ := GetNativeNlri(p) + attrs, _ := GetNativePathAttributes(p) + l = append(l, &Path{ + Nlri: nlri, + Age: p.Age, + Best: p.Best, + Attrs: attrs, + Stale: p.Stale, + Withdrawal: p.IsWithdraw, + SourceID: net.ParseIP(p.SourceId), + NeighborIP: net.ParseIP(p.NeighborIp), + }) + } + return &Destination{Paths: l} +} + +func NewPath(nlri bgp.AddrPrefixInterface, isWithdraw bool, attrs []bgp.PathAttributeInterface, age time.Time) *api.Path { + return &api.Path{ + AnyNlri: MarshalNLRI(nlri), + AnyPattrs: MarshalPathAttributes(attrs), + Age: age.Unix(), + IsWithdraw: isWithdraw, + Family: uint32(bgp.AfiSafiToRouteFamily(nlri.AFI(), nlri.SAFI())), + Identifier: nlri.PathIdentifier(), + } +} + +func getNLRI(family bgp.RouteFamily, buf []byte) (bgp.AddrPrefixInterface, error) { + afi, safi := bgp.RouteFamilyToAfiSafi(family) + nlri, err := bgp.NewPrefixFromRouteFamily(afi, safi) + if err != nil { + return nil, err + } + if err := nlri.DecodeFromBytes(buf); err != nil { + return nil, err + } + return nlri, nil +} + +func GetNativeNlri(p *api.Path) (bgp.AddrPrefixInterface, error) { + if len(p.Nlri) > 0 { + return getNLRI(bgp.RouteFamily(p.Family), p.Nlri) + } + return UnmarshalNLRI(bgp.RouteFamily(p.Family), p.AnyNlri) +} + +func GetNativePathAttributes(p *api.Path) ([]bgp.PathAttributeInterface, error) { + pattrsLen := len(p.Pattrs) + if pattrsLen > 0 { + pattrs := make([]bgp.PathAttributeInterface, 0, pattrsLen) + for _, attr := range p.Pattrs { + a, err := bgp.GetPathAttribute(attr) + if err != nil { + return nil, err + } + err = a.DecodeFromBytes(attr) + if err != nil { + return nil, err + } + pattrs = append(pattrs, a) + } + return pattrs, nil + } + return UnmarshalPathAttributes(p.AnyPattrs) +} |