diff options
author | Hitoshi Irino <irino@sfc.wide.ad.jp> | 2019-03-02 13:54:38 +0900 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@gmail.com> | 2019-03-14 21:20:31 +0900 |
commit | 3a79ad3fdcbb7af0b7911a9c610c6e7af970ad6d (patch) | |
tree | 40911a6e5be205a15752aa11ea3b2e4f7335ebfa /internal/pkg | |
parent | dc4d9c6d1b253bc3a381f82941796b6bb8c71384 (diff) |
Supporting BGP/MPLS L3VPNs with Frrouting Zebra API 6
- This commit aims to solve reported problem on issues #1611, #1648 and #1912
- Partial changes of this commit duplicate with changes on PR #1587 (not merged) and PR #1766 (not merged and already closed)
- This commit is tested with only FRRouting version 6.0.2 (which uses Zebra API 6)
- This commit fixes lack of LABEL_MANAGER_CONNECT_ASYNC for ZAPI6.
(This bug is introduced on commit 2bdb76f2dcf24b891f2b6327a57c31b26463b2dd "Supporting Zebra API version 6 which is used in FRRouting version 6")
Diffstat (limited to 'internal/pkg')
-rw-r--r-- | internal/pkg/config/bgp_configs.go | 17 | ||||
-rw-r--r-- | internal/pkg/table/path.go | 38 | ||||
-rw-r--r-- | internal/pkg/table/table_manager.go | 76 | ||||
-rw-r--r-- | internal/pkg/table/vrf.go | 22 | ||||
-rw-r--r-- | internal/pkg/zebra/api_type_string.go | 4 | ||||
-rw-r--r-- | internal/pkg/zebra/lsp_type_string.go | 16 | ||||
-rw-r--r-- | internal/pkg/zebra/safi_string.go | 4 | ||||
-rw-r--r-- | internal/pkg/zebra/zapi.go | 376 |
8 files changed, 505 insertions, 48 deletions
diff --git a/internal/pkg/config/bgp_configs.go b/internal/pkg/config/bgp_configs.go index 7a533b56..d8b575b0 100644 --- a/internal/pkg/config/bgp_configs.go +++ b/internal/pkg/config/bgp_configs.go @@ -1121,13 +1121,18 @@ type ZebraState struct { // original -> gobgp:redistribute-route-type RedistributeRouteTypeList []string `mapstructure:"redistribute-route-type-list" json:"redistribute-route-type-list,omitempty"` // original -> gobgp:version - // Configure version of zebra protocol. Default is 2. Supported up to 3. + // Configure version of zebra protocol. Default is 2. + // Supported version are 2 or 3 for Quagga and 4, 5 or 6 for FRRouting. Version uint8 `mapstructure:"version" json:"version,omitempty"` // original -> gobgp:nexthop-trigger-enable // gobgp:nexthop-trigger-enable's original type is boolean. NexthopTriggerEnable bool `mapstructure:"nexthop-trigger-enable" json:"nexthop-trigger-enable,omitempty"` // original -> gobgp:nexthop-trigger-delay NexthopTriggerDelay uint8 `mapstructure:"nexthop-trigger-delay" json:"nexthop-trigger-delay,omitempty"` + // original -> gobgp:mpls-label-range-size + // Configure MPLS label range size which will be requested to + // FRR/Zebra. + MplsLabelRangeSize uint32 `mapstructure:"mpls-label-range-size" json:"mpls-label-range-size,omitempty"` } // struct for container gobgp:config. @@ -1142,13 +1147,18 @@ type ZebraConfig struct { // original -> gobgp:redistribute-route-type RedistributeRouteTypeList []string `mapstructure:"redistribute-route-type-list" json:"redistribute-route-type-list,omitempty"` // original -> gobgp:version - // Configure version of zebra protocol. Default is 2. Supported up to 3. + // Configure version of zebra protocol. Default is 2. + // Supported version are 2 or 3 for Quagga and 4, 5 or 6 for FRRouting. Version uint8 `mapstructure:"version" json:"version,omitempty"` // original -> gobgp:nexthop-trigger-enable // gobgp:nexthop-trigger-enable's original type is boolean. NexthopTriggerEnable bool `mapstructure:"nexthop-trigger-enable" json:"nexthop-trigger-enable,omitempty"` // original -> gobgp:nexthop-trigger-delay NexthopTriggerDelay uint8 `mapstructure:"nexthop-trigger-delay" json:"nexthop-trigger-delay,omitempty"` + // original -> gobgp:mpls-label-range-size + // Configure MPLS label range size which will be requested to + // FRR/Zebra. + MplsLabelRangeSize uint32 `mapstructure:"mpls-label-range-size" json:"mpls-label-range-size,omitempty"` } func (lhs *ZebraConfig) Equal(rhs *ZebraConfig) bool { @@ -1178,6 +1188,9 @@ func (lhs *ZebraConfig) Equal(rhs *ZebraConfig) bool { if lhs.NexthopTriggerDelay != rhs.NexthopTriggerDelay { return false } + if lhs.MplsLabelRangeSize != rhs.MplsLabelRangeSize { + return false + } return true } diff --git a/internal/pkg/table/path.go b/internal/pkg/table/path.go index eba0a6ec..bdeb6ff2 100644 --- a/internal/pkg/table/path.go +++ b/internal/pkg/table/path.go @@ -133,13 +133,14 @@ type Validation struct { } type Path struct { - info *originInfo - parent *Path - pathAttrs []bgp.PathAttributeInterface - dels []bgp.BGPAttrType - attrsHash uint32 - aslooped bool - reason BestPathReason + info *originInfo + parent *Path + pathAttrs []bgp.PathAttributeInterface + dels []bgp.BGPAttrType + attrsHash uint32 + aslooped bool + reason BestPathReason + receiveVrfId uint32 //VRF in which the path was received. // For BGP Nexthop Tracking, this field shows if nexthop is invalidated by IGP. IsNexthopInvalid bool @@ -167,8 +168,9 @@ func NewPath(source *PeerInfo, nlri bgp.AddrPrefixInterface, isWithdraw bool, pa timestamp: timestamp.Unix(), noImplicitWithdraw: noImplicitWithdraw, }, - IsWithdraw: isWithdraw, - pathAttrs: pattrs, + IsWithdraw: isWithdraw, + pathAttrs: pattrs, + receiveVrfId: 0, } } @@ -332,6 +334,13 @@ func (path *Path) IsIBGP() bool { return (as == path.GetSource().LocalAS) && as != 0 } +func (path *Path) ReceiveVrfId() uint32 { + return path.receiveVrfId +} +func (path *Path) SetReceiveVrfId(vrfId uint32) { + path.receiveVrfId = vrfId +} + // create new PathAttributes func (path *Path) Clone(isWithdraw bool) *Path { return &Path{ @@ -339,6 +348,7 @@ func (path *Path) Clone(isWithdraw bool) *Path { IsWithdraw: isWithdraw, IsNexthopInvalid: path.IsNexthopInvalid, attrsHash: path.attrsHash, + receiveVrfId: path.receiveVrfId, } } @@ -580,6 +590,7 @@ func (path *Path) String() string { if path.IsWithdraw { s.WriteString(", withdraw") } + s.WriteString(fmt.Sprintf(", receiveVrfId: %d", path.receiveVrfId)) s.WriteString(" }") return s.String() } @@ -1069,12 +1080,12 @@ func (v *Vrf) ToGlobalPath(path *Path) error { case bgp.RF_IPv4_UC: n := nlri.(*bgp.IPAddrPrefix) pathIdentifier := path.GetNlri().PathIdentifier() - path.OriginInfo().nlri = bgp.NewLabeledVPNIPAddrPrefix(n.Length, n.Prefix.String(), *bgp.NewMPLSLabelStack(0), v.Rd) + path.OriginInfo().nlri = bgp.NewLabeledVPNIPAddrPrefix(n.Length, n.Prefix.String(), *bgp.NewMPLSLabelStack(v.MplsLabel), v.Rd) path.GetNlri().SetPathIdentifier(pathIdentifier) case bgp.RF_IPv6_UC: n := nlri.(*bgp.IPv6AddrPrefix) pathIdentifier := path.GetNlri().PathIdentifier() - path.OriginInfo().nlri = bgp.NewLabeledVPNIPv6AddrPrefix(n.Length, n.Prefix.String(), *bgp.NewMPLSLabelStack(0), v.Rd) + path.OriginInfo().nlri = bgp.NewLabeledVPNIPv6AddrPrefix(n.Length, n.Prefix.String(), *bgp.NewMPLSLabelStack(v.MplsLabel), v.Rd) path.GetNlri().SetPathIdentifier(pathIdentifier) case bgp.RF_EVPN: n := nlri.(*bgp.EVPNNLRI) @@ -1098,11 +1109,11 @@ func (p *Path) ToGlobal(vrf *Vrf) *Path { switch rf := p.GetRouteFamily(); rf { case bgp.RF_IPv4_UC: n := nlri.(*bgp.IPAddrPrefix) - nlri = bgp.NewLabeledVPNIPAddrPrefix(n.Length, n.Prefix.String(), *bgp.NewMPLSLabelStack(0), vrf.Rd) + nlri = bgp.NewLabeledVPNIPAddrPrefix(n.Length, n.Prefix.String(), *bgp.NewMPLSLabelStack(vrf.MplsLabel), vrf.Rd) nlri.SetPathIdentifier(pathId) case bgp.RF_IPv6_UC: n := nlri.(*bgp.IPv6AddrPrefix) - nlri = bgp.NewLabeledVPNIPv6AddrPrefix(n.Length, n.Prefix.String(), *bgp.NewMPLSLabelStack(0), vrf.Rd) + nlri = bgp.NewLabeledVPNIPv6AddrPrefix(n.Length, n.Prefix.String(), *bgp.NewMPLSLabelStack(vrf.MplsLabel), vrf.Rd) nlri.SetPathIdentifier(pathId) case bgp.RF_EVPN: n := nlri.(*bgp.EVPNNLRI) @@ -1138,6 +1149,7 @@ func (p *Path) ToGlobal(vrf *Vrf) *Path { path.delPathAttr(bgp.BGP_ATTR_TYPE_NEXT_HOP) path.setPathAttr(bgp.NewPathAttributeMpReachNLRI(nh.String(), []bgp.AddrPrefixInterface{nlri})) path.IsNexthopInvalid = p.IsNexthopInvalid + path.receiveVrfId = p.receiveVrfId return path } diff --git a/internal/pkg/table/table_manager.go b/internal/pkg/table/table_manager.go index 58c09bf3..45698504 100644 --- a/internal/pkg/table/table_manager.go +++ b/internal/pkg/table/table_manager.go @@ -105,9 +105,10 @@ func ProcessMessage(m *bgp.BGPMessage, peerInfo *PeerInfo, timestamp time.Time) } type TableManager struct { - Tables map[bgp.RouteFamily]*Table - Vrfs map[string]*Vrf - rfList []bgp.RouteFamily + Tables map[bgp.RouteFamily]*Table + Vrfs map[string]*Vrf + rfList []bgp.RouteFamily + mplsLabelMaps map[uint64]*Bitmap } func NewTableManager(rfList []bgp.RouteFamily) *TableManager { @@ -122,6 +123,63 @@ func NewTableManager(rfList []bgp.RouteFamily) *TableManager { return t } +func (manager *TableManager) EnableMplsLabelAllocation() error { + if manager.mplsLabelMaps != nil { + return fmt.Errorf("label allocation already enabled") + } + manager.mplsLabelMaps = make(map[uint64]*Bitmap) + return nil +} + +func (manager *TableManager) AllocateMplsLabelRange(start, end uint32) error { + if manager.mplsLabelMaps == nil { + return fmt.Errorf("label allocation not yet enabled") + } + log.WithFields(log.Fields{ + "Topic": "Vrf", + "Start": start, + "End": end, + }).Debug("allocate new MPLS label range") + startEnd := uint64(start)<<32 | uint64(end) + manager.mplsLabelMaps[startEnd] = NewBitmap(int(end - start + 1)) + return nil +} + +func (manager *TableManager) AssignMplsLabel() (uint32, error) { + if manager.mplsLabelMaps == nil { + return 0, nil + } + var label uint32 + for startEnd, bitmap := range manager.mplsLabelMaps { + start := uint32(startEnd >> 32) + end := uint32(startEnd & 0xffffffff) + l, err := bitmap.FindandSetZeroBit() + if err == nil && start+uint32(l) <= end { + label = start + uint32(l) + break + } + } + if label == 0 { + return 0, fmt.Errorf("could not assign new MPLS label; need to allocate new MPLS label range") + } + return label, nil +} + +func (manager *TableManager) releaseMplsLabel(label uint32) { + if manager.mplsLabelMaps == nil { + return + } + for startEnd, bitmap := range manager.mplsLabelMaps { + start := uint32(startEnd >> 32) + end := uint32(startEnd & 0xffffffff) + if start <= label && label <= end { + bitmap.Unflag(uint(label - start)) + return + } + } + return +} + func (manager *TableManager) GetRFlist() []bgp.RouteFamily { return manager.rfList } @@ -166,12 +224,14 @@ func (manager *TableManager) DeleteVrf(name string) ([]*Path, error) { msgs = append(msgs, t.deletePathsByVrf(vrf)...) } log.WithFields(log.Fields{ - "Topic": "Vrf", - "Key": vrf.Name, - "Rd": vrf.Rd, - "ImportRt": vrf.ImportRt, - "ExportRt": vrf.ExportRt, + "Topic": "Vrf", + "Key": vrf.Name, + "Rd": vrf.Rd, + "ImportRt": vrf.ImportRt, + "ExportRt": vrf.ExportRt, + "MplsLabel": vrf.MplsLabel, }).Debugf("delete vrf") + manager.releaseMplsLabel(vrf.MplsLabel) delete(manager.Vrfs, name) rtcTable := manager.Tables[bgp.RF_RTC_UC] msgs = append(msgs, rtcTable.deleteRTCPathsByVrf(vrf, manager.Vrfs)...) diff --git a/internal/pkg/table/vrf.go b/internal/pkg/table/vrf.go index 053f85ce..dc98e57d 100644 --- a/internal/pkg/table/vrf.go +++ b/internal/pkg/table/vrf.go @@ -20,11 +20,12 @@ import ( ) type Vrf struct { - Name string - Id uint32 - Rd bgp.RouteDistinguisherInterface - ImportRt []bgp.ExtendedCommunityInterface - ExportRt []bgp.ExtendedCommunityInterface + Name string + Id uint32 + Rd bgp.RouteDistinguisherInterface + ImportRt []bgp.ExtendedCommunityInterface + ExportRt []bgp.ExtendedCommunityInterface + MplsLabel uint32 } func (v *Vrf) Clone() *Vrf { @@ -33,11 +34,12 @@ func (v *Vrf) Clone() *Vrf { return append(l, rt...) } return &Vrf{ - Name: v.Name, - Id: v.Id, - Rd: v.Rd, - ImportRt: f(v.ImportRt), - ExportRt: f(v.ExportRt), + Name: v.Name, + Id: v.Id, + Rd: v.Rd, + ImportRt: f(v.ImportRt), + ExportRt: f(v.ExportRt), + MplsLabel: v.MplsLabel, } } diff --git a/internal/pkg/zebra/api_type_string.go b/internal/pkg/zebra/api_type_string.go index af16317b..2c280533 100644 --- a/internal/pkg/zebra/api_type_string.go +++ b/internal/pkg/zebra/api_type_string.go @@ -4,9 +4,9 @@ package zebra import "strconv" -const _API_TYPE_name = "FRR_ZAPI6_INTERFACE_ADDFRR_ZAPI6_INTERFACE_DELETEFRR_ZAPI6_INTERFACE_ADDRESS_ADDFRR_ZAPI6_INTERFACE_ADDRESS_DELETEFRR_ZAPI6_INTERFACE_UPFRR_ZAPI6_INTERFACE_DOWNFRR_ZAPI6_INTERFACE_SET_MASTERFRR_ZAPI6_ROUTE_ADDFRR_ZAPI6_ROUTE_DELETEFRR_ZAPI6_ROUTE_NOTIFY_OWNERFRR_ZAPI6_REDISTRIBUTE_ADDFRR_ZAPI6_REDISTRIBUTE_DELETEFRR_ZAPI6_REDISTRIBUTE_DEFAULT_ADDFRR_ZAPI6_REDISTRIBUTE_DEFAULT_DELETEFRR_ZAPI6_ROUTER_ID_ADDFRR_ZAPI6_ROUTER_ID_DELETEFRR_ZAPI6_ROUTER_ID_UPDATEFRR_ZAPI6_HELLOFRR_ZAPI6_CAPABILITIESFRR_ZAPI6_NEXTHOP_REGISTERFRR_ZAPI6_NEXTHOP_UNREGISTERFRR_ZAPI6_NEXTHOP_UPDATEFRR_ZAPI6_INTERFACE_NBR_ADDRESS_ADDFRR_ZAPI6_INTERFACE_NBR_ADDRESS_DELETEFRR_ZAPI6_INTERFACE_BFD_DEST_UPDATEFRR_ZAPI6_IMPORT_ROUTE_REGISTERFRR_ZAPI6_IMPORT_ROUTE_UNREGISTERFRR_ZAPI6_IMPORT_CHECK_UPDATEFRR_ZAPI6_IPV4_ROUTE_IPV6_NEXTHOP_ADDFRR_ZAPI6_BFD_DEST_REGISTERFRR_ZAPI6_BFD_DEST_DEREGISTERFRR_ZAPI6_BFD_DEST_UPDATEFRR_ZAPI6_BFD_DEST_REPLAYFRR_ZAPI6_REDISTRIBUTE_ROUTE_ADDFRR_ZAPI6_REDISTRIBUTE_ROUTE_DELFRR_ZAPI6_VRF_UNREGISTERFRR_ZAPI6_VRF_ADDFRR_ZAPI6_VRF_DELETEFRR_ZAPI6_VRF_LABELFRR_ZAPI6_INTERFACE_VRF_UPDATEFRR_ZAPI6_BFD_CLIENT_REGISTERFRR_ZAPI6_BFD_CLIENT_DEREGISTERFRR_ZAPI6_INTERFACE_ENABLE_RADVFRR_ZAPI6_INTERFACE_DISABLE_RADVFRR_ZAPI6_IPV4_NEXTHOP_LOOKUP_MRIBFRR_ZAPI6_INTERFACE_LINK_PARAMSFRR_ZAPI6_MPLS_LABELS_ADDFRR_ZAPI6_MPLS_LABELS_DELETEFRR_ZAPI6_IPMR_ROUTE_STATSFRR_ZAPI6_LABEL_MANAGER_CONNECTFRR_ZAPI6_GET_LABEL_CHUNKFRR_ZAPI6_RELEASE_LABEL_CHUNKFRR_ZAPI6_FEC_REGISTERFRR_ZAPI6_FEC_UNREGISTERFRR_ZAPI6_FEC_UPDATEFRR_ZAPI6_ADVERTISE_DEFAULT_GWFRR_ZAPI6_ADVERTISE_SUBNETFRR_ZAPI6_ADVERTISE_ALL_VNIFRR_ZAPI6_LOCAL_ES_ADDFRR_ZAPI6_LOCAL_ES_DELFRR_ZAPI6_VNI_ADDFRR_ZAPI6_VNI_DELFRR_ZAPI6_L3VNI_ADDFRR_ZAPI6_L3VNI_DELFRR_ZAPI6_REMOTE_VTEP_ADDFRR_ZAPI6_REMOTE_VTEP_DELFRR_ZAPI6_MACIP_ADDFRR_ZAPI6_MACIP_DELFRR_ZAPI6_IP_PREFIX_ROUTE_ADDFRR_ZAPI6_IP_PREFIX_ROUTE_DELFRR_ZAPI6_REMOTE_MACIP_ADDFRR_ZAPI6_REMOTE_MACIP_DELFRR_ZAPI6_PW_ADDFRR_ZAPI6_PW_DELETEFRR_ZAPI6_PW_SETFRR_ZAPI6_PW_UNSETFRR_ZAPI6_PW_STATUS_UPDATEFRR_ZAPI6_RULE_ADDFRR_ZAPI6_RULE_DELETEFRR_ZAPI6_RULE_NOTIFY_OWNERFRR_ZAPI6_TABLE_MANAGER_CONNECTFRR_ZAPI6_GET_TABLE_CHUNKFRR_ZAPI6_RELEASE_TABLE_CHUNKFRR_ZAPI6_IPSET_CREATEFRR_ZAPI6_IPSET_DESTROYFRR_ZAPI6_IPSET_ENTRY_ADDFRR_ZAPI6_IPSET_ENTRY_DELETEFRR_ZAPI6_IPSET_NOTIFY_OWNERFRR_ZAPI6_IPSET_ENTRY_NOTIFY_OWNERFRR_ZAPI6_IPTABLE_ADDFRR_ZAPI6_IPTABLE_DELETEFRR_ZAPI6_IPTABLE_NOTIFY_OWNERFRR_ZAPI5_IPTABLE_DELETEFRR_ZAPI5_IPTABLE_NOTIFY_OWNER" +const _API_TYPE_name = "FRR_ZAPI6_INTERFACE_ADDFRR_ZAPI6_INTERFACE_DELETEFRR_ZAPI6_INTERFACE_ADDRESS_ADDFRR_ZAPI6_INTERFACE_ADDRESS_DELETEFRR_ZAPI6_INTERFACE_UPFRR_ZAPI6_INTERFACE_DOWNFRR_ZAPI6_INTERFACE_SET_MASTERFRR_ZAPI6_ROUTE_ADDFRR_ZAPI6_ROUTE_DELETEFRR_ZAPI6_ROUTE_NOTIFY_OWNERFRR_ZAPI6_REDISTRIBUTE_ADDFRR_ZAPI6_REDISTRIBUTE_DELETEFRR_ZAPI6_REDISTRIBUTE_DEFAULT_ADDFRR_ZAPI6_REDISTRIBUTE_DEFAULT_DELETEFRR_ZAPI6_ROUTER_ID_ADDFRR_ZAPI6_ROUTER_ID_DELETEFRR_ZAPI6_ROUTER_ID_UPDATEFRR_ZAPI6_HELLOFRR_ZAPI6_CAPABILITIESFRR_ZAPI6_NEXTHOP_REGISTERFRR_ZAPI6_NEXTHOP_UNREGISTERFRR_ZAPI6_NEXTHOP_UPDATEFRR_ZAPI6_INTERFACE_NBR_ADDRESS_ADDFRR_ZAPI6_INTERFACE_NBR_ADDRESS_DELETEFRR_ZAPI6_INTERFACE_BFD_DEST_UPDATEFRR_ZAPI6_IMPORT_ROUTE_REGISTERFRR_ZAPI6_IMPORT_ROUTE_UNREGISTERFRR_ZAPI6_IMPORT_CHECK_UPDATEFRR_ZAPI6_IPV4_ROUTE_IPV6_NEXTHOP_ADDFRR_ZAPI6_BFD_DEST_REGISTERFRR_ZAPI6_BFD_DEST_DEREGISTERFRR_ZAPI6_BFD_DEST_UPDATEFRR_ZAPI6_BFD_DEST_REPLAYFRR_ZAPI6_REDISTRIBUTE_ROUTE_ADDFRR_ZAPI6_REDISTRIBUTE_ROUTE_DELFRR_ZAPI6_VRF_UNREGISTERFRR_ZAPI6_VRF_ADDFRR_ZAPI6_VRF_DELETEFRR_ZAPI6_VRF_LABELFRR_ZAPI6_INTERFACE_VRF_UPDATEFRR_ZAPI6_BFD_CLIENT_REGISTERFRR_ZAPI6_BFD_CLIENT_DEREGISTERFRR_ZAPI6_INTERFACE_ENABLE_RADVFRR_ZAPI6_INTERFACE_DISABLE_RADVFRR_ZAPI6_IPV4_NEXTHOP_LOOKUP_MRIBFRR_ZAPI6_INTERFACE_LINK_PARAMSFRR_ZAPI6_MPLS_LABELS_ADDFRR_ZAPI6_MPLS_LABELS_DELETEFRR_ZAPI6_IPMR_ROUTE_STATSFRR_ZAPI6_LABEL_MANAGER_CONNECTFRR_ZAPI6_LABEL_MANAGER_CONNECT_ASYNCFRR_ZAPI6_GET_LABEL_CHUNKFRR_ZAPI6_RELEASE_LABEL_CHUNKFRR_ZAPI6_FEC_REGISTERFRR_ZAPI6_FEC_UNREGISTERFRR_ZAPI6_FEC_UPDATEFRR_ZAPI6_ADVERTISE_DEFAULT_GWFRR_ZAPI6_ADVERTISE_SUBNETFRR_ZAPI6_ADVERTISE_ALL_VNIFRR_ZAPI6_LOCAL_ES_ADDFRR_ZAPI6_LOCAL_ES_DELFRR_ZAPI6_VNI_ADDFRR_ZAPI6_VNI_DELFRR_ZAPI6_L3VNI_ADDFRR_ZAPI6_L3VNI_DELFRR_ZAPI6_REMOTE_VTEP_ADDFRR_ZAPI6_REMOTE_VTEP_DELFRR_ZAPI6_MACIP_ADDFRR_ZAPI6_MACIP_DELFRR_ZAPI6_IP_PREFIX_ROUTE_ADDFRR_ZAPI6_IP_PREFIX_ROUTE_DELFRR_ZAPI6_REMOTE_MACIP_ADDFRR_ZAPI6_REMOTE_MACIP_DELFRR_ZAPI6_PW_ADDFRR_ZAPI6_PW_DELETEFRR_ZAPI6_PW_SETFRR_ZAPI6_PW_UNSETFRR_ZAPI6_PW_STATUS_UPDATEFRR_ZAPI6_RULE_ADDFRR_ZAPI6_RULE_DELETEFRR_ZAPI6_RULE_NOTIFY_OWNERFRR_ZAPI6_TABLE_MANAGER_CONNECTFRR_ZAPI6_GET_TABLE_CHUNKFRR_ZAPI6_RELEASE_TABLE_CHUNKFRR_ZAPI6_IPSET_CREATEFRR_ZAPI6_IPSET_DESTROYFRR_ZAPI6_IPSET_ENTRY_ADDFRR_ZAPI6_IPSET_ENTRY_DELETEFRR_ZAPI6_IPSET_NOTIFY_OWNERFRR_ZAPI6_IPSET_ENTRY_NOTIFY_OWNERFRR_ZAPI6_IPTABLE_ADDFRR_ZAPI6_IPTABLE_DELETEFRR_ZAPI6_IPTABLE_NOTIFY_OWNERFRR_ZAPI5_IPTABLE_NOTIFY_OWNER" -var _API_TYPE_index = [...]uint16{0, 23, 49, 80, 114, 136, 160, 190, 209, 231, 259, 285, 314, 348, 385, 408, 434, 460, 475, 497, 523, 551, 575, 610, 648, 683, 714, 747, 776, 813, 840, 869, 894, 919, 951, 983, 1007, 1024, 1044, 1063, 1093, 1122, 1153, 1184, 1216, 1250, 1281, 1306, 1334, 1360, 1391, 1416, 1445, 1467, 1491, 1511, 1541, 1567, 1594, 1616, 1638, 1655, 1672, 1691, 1710, 1735, 1760, 1779, 1798, 1827, 1856, 1882, 1908, 1924, 1943, 1959, 1977, 2003, 2021, 2042, 2069, 2100, 2125, 2154, 2176, 2199, 2224, 2252, 2280, 2314, 2335, 2359, 2389, 2413, 2443} +var _API_TYPE_index = [...]uint16{0, 23, 49, 80, 114, 136, 160, 190, 209, 231, 259, 285, 314, 348, 385, 408, 434, 460, 475, 497, 523, 551, 575, 610, 648, 683, 714, 747, 776, 813, 840, 869, 894, 919, 951, 983, 1007, 1024, 1044, 1063, 1093, 1122, 1153, 1184, 1216, 1250, 1281, 1306, 1334, 1360, 1391, 1428, 1453, 1482, 1504, 1528, 1548, 1578, 1604, 1631, 1653, 1675, 1692, 1709, 1728, 1747, 1772, 1797, 1816, 1835, 1864, 1893, 1919, 1945, 1961, 1980, 1996, 2014, 2040, 2058, 2079, 2106, 2137, 2162, 2191, 2213, 2236, 2261, 2289, 2317, 2351, 2372, 2396, 2426, 2456} func (i API_TYPE) String() string { if i >= API_TYPE(len(_API_TYPE_index)-1) { diff --git a/internal/pkg/zebra/lsp_type_string.go b/internal/pkg/zebra/lsp_type_string.go new file mode 100644 index 00000000..318c4800 --- /dev/null +++ b/internal/pkg/zebra/lsp_type_string.go @@ -0,0 +1,16 @@ +// Code generated by "stringer -type=LSP_TYPE"; DO NOT EDIT. + +package zebra + +import "strconv" + +const _LSP_TYPE_name = "LSP_NONELSP_STATICLSP_LDPLSP_BGPLSP_SRLSP_SHARP" + +var _LSP_TYPE_index = [...]uint8{0, 8, 18, 25, 32, 38, 47} + +func (i LSP_TYPE) String() string { + if i >= LSP_TYPE(len(_LSP_TYPE_index)-1) { + return "LSP_TYPE(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _LSP_TYPE_name[_LSP_TYPE_index[i]:_LSP_TYPE_index[i+1]] +} diff --git a/internal/pkg/zebra/safi_string.go b/internal/pkg/zebra/safi_string.go index 8199a0e3..c8d469e4 100644 --- a/internal/pkg/zebra/safi_string.go +++ b/internal/pkg/zebra/safi_string.go @@ -4,9 +4,9 @@ package zebra import "strconv" -const _SAFI_name = "SAFI_UNICASTSAFI_MULTICASTSAFI_RESERVED_3SAFI_MPLS_VPNSAFI_MAX" +const _SAFI_name = "FRR_ZAPI5_SAFI_UNICASTFRR_ZAPI5_SAFI_MULTICASTFRR_ZAPI5_SAFI_MPLS_VPNFRR_ZAPI5_SAFI_ENCAPFRR_ZAPI5_SAFI_EVPNFRR_ZAPI5_SAFI_LABELED_UNICASTFRR_ZAPI5_SAFI_FLOWSPECFRR_ZAPI5_SAFI_MAX" -var _SAFI_index = [...]uint8{0, 12, 26, 41, 54, 62} +var _SAFI_index = [...]uint8{0, 22, 46, 69, 89, 108, 138, 161, 179} func (i SAFI) String() string { i -= 1 diff --git a/internal/pkg/zebra/zapi.go b/internal/pkg/zebra/zapi.go index 6bbe6a06..9659fa97 100644 --- a/internal/pkg/zebra/zapi.go +++ b/internal/pkg/zebra/zapi.go @@ -178,12 +178,29 @@ const ( //go:generate stringer -type=SAFI type SAFI uint8 +// SAFI definition in Zebra of FRRouting 4.x, 5.x, 6.x +const ( + _ SAFI = iota + FRR_ZAPI5_SAFI_UNICAST + FRR_ZAPI5_SAFI_MULTICAST + FRR_ZAPI5_SAFI_MPLS_VPN + FRR_ZAPI5_SAFI_ENCAP + FRR_ZAPI5_SAFI_EVPN + FRR_ZAPI5_SAFI_LABELED_UNICAST + FRR_ZAPI5_SAFI_FLOWSPEC + FRR_ZAPI5_SAFI_MAX +) + +// SAFI definition in Zebra of Quagga and FRRouting 3.x const ( _ SAFI = iota SAFI_UNICAST SAFI_MULTICAST - SAFI_RESERVED_3 - SAFI_MPLS_VPN + FRR_SAFI_MPLS_VPN // SAFI_RESERVED_3 in quagga + SAFI_MPLS_VPN // SAFI_RESERVED_4 in FRRouting 3.x + FRR_SAFI_ENCAP + FRR_SAFI_EVPN + SAFI_ENCAP // SAFI_MAX in FRRouting 3.x SAFI_MAX ) @@ -243,6 +260,7 @@ const ( FRR_ZAPI6_MPLS_LABELS_DELETE FRR_ZAPI6_IPMR_ROUTE_STATS FRR_ZAPI6_LABEL_MANAGER_CONNECT + FRR_ZAPI6_LABEL_MANAGER_CONNECT_ASYNC FRR_ZAPI6_GET_LABEL_CHUNK FRR_ZAPI6_RELEASE_LABEL_CHUNK FRR_ZAPI6_FEC_REGISTER @@ -1020,6 +1038,9 @@ func NewClient(network, address string, typ ROUTE_TYPE, version uint8) (*Client, // Send HELLO/ROUTER_ID_ADD messages to negotiate the Zebra message version. c.SendHello() c.SendRouterIDAdd() + if version >= 4 { + c.SendLabelManagerConnect() + } receiveSingleMsg := func() (*Message, error) { headerBuf, err := readAll(conn, int(HeaderSize(version))) @@ -1241,6 +1262,10 @@ func (c *Client) SendRedistributeDelete(t ROUTE_TYPE) error { } func (c *Client) SendIPRoute(vrfId uint32, body *IPRouteBody, isWithdraw bool) error { + routeFamily := body.RouteFamily(c.Version) + if vrfId == VRF_DEFAULT && (routeFamily == bgp.RF_IPv4_VPN || routeFamily == bgp.RF_IPv6_VPN) { + return fmt.Errorf("RF_IPv4_VPN or RF_IPv6_VPN are not suitable for VPN_DEFAULT(default forwarding table).") + } command := IPV4_ROUTE_ADD if c.Version <= 3 { if body.Prefix.Prefix.To4() != nil { @@ -1311,6 +1336,58 @@ func (c *Client) SendNexthopRegister(vrfId uint32, body *NexthopRegisterBody, is return c.SendCommand(command, vrfId, body) } +// Reference: zread_label_manager_connect function in zebra/zserv.c of FRR3.x (ZAPI) +// Reference: zread_label_manager_connect function in zebra/zapi_msg.c of FRR5.x and 6.x (ZAPI5 and 6) + +func (c *Client) SendLabelManagerConnect() error { + if c.Version < 4 { + return fmt.Errorf("LABEL_MANAGER_CONNECT is not supported in version: %d", c.Version) + } + command := FRR_LABEL_MANAGER_CONNECT + proto := FRR_ROUTE_BGP + if c.Version == 5 { + command = FRR_ZAPI5_LABEL_MANAGER_CONNECT + proto = FRR_ZAPI5_ROUTE_BGP + } else if c.Version == 6 { + command = FRR_ZAPI6_LABEL_MANAGER_CONNECT + proto = FRR_ZAPI6_ROUTE_BGP + } + return c.SendCommand( + command, 0, + &LabelManagerConnectBody{ + RedistDefault: proto, + Instance: 0, + }) +} +func (c *Client) SendGetLabelChunk(body *GetLabelChunkBody) error { + if c.Version < 4 { + return fmt.Errorf("GET_LABEL_CHUNK is not supported in version: %d", c.Version) + } + command := FRR_GET_LABEL_CHUNK + body.Instance = 0 + if c.Version == 5 { + body.Proto = uint8(FRR_ZAPI5_ROUTE_BGP) + command = FRR_ZAPI5_GET_LABEL_CHUNK + } else if c.Version == 6 { + body.Proto = uint8(FRR_ZAPI6_ROUTE_BGP) + command = FRR_ZAPI6_GET_LABEL_CHUNK + } + return c.SendCommand(command, 0, body) +} + +func (c *Client) SendVrfLabel(label uint32, vrfId uint32) error { + body := &VrfLabelBody{ + Label: label, + Afi: AFI_IP, + LabelType: LSP_BGP, + } + command := FRR_ZAPI5_VRF_LABEL + if c.Version == 6 { + command = FRR_ZAPI6_VRF_LABEL + } + return c.SendCommand(command, vrfId, body) +} + // for avoiding double close func ChannelClose(ch chan *Message) bool { select { @@ -1735,11 +1812,78 @@ func (b *IPRouteBody) RouteFamily(version uint8) bgp.RouteFamily { family = syscall.AF_INET6 } } - switch family { - case syscall.AF_INET: - return bgp.RF_IPv4_UC - case syscall.AF_INET6: - return bgp.RF_IPv6_UC + if version < 5 { + switch family { + case syscall.AF_INET: + switch b.SAFI { + case SAFI_UNICAST: + return bgp.RF_IPv4_UC + case SAFI_MULTICAST: + return bgp.RF_IPv4_MC + case SAFI_MPLS_VPN, FRR_SAFI_MPLS_VPN: + return bgp.RF_IPv4_VPN + case FRR_SAFI_ENCAP, SAFI_ENCAP: + return bgp.RF_IPv4_ENCAP + } + case syscall.AF_INET6: + switch b.SAFI { + case SAFI_UNICAST: + return bgp.RF_IPv6_UC + case SAFI_MULTICAST: + return bgp.RF_IPv6_MC + case SAFI_MPLS_VPN, FRR_SAFI_MPLS_VPN: + return bgp.RF_IPv6_VPN + case FRR_SAFI_ENCAP, SAFI_ENCAP: + return bgp.RF_IPv6_ENCAP + } + default: + switch b.SAFI { + case FRR_SAFI_EVPN: + return bgp.RF_EVPN + default: + return bgp.RF_OPAQUE + } + } + } else { + switch family { + case syscall.AF_INET: + switch b.SAFI { + case FRR_ZAPI5_SAFI_UNICAST: + return bgp.RF_IPv4_UC + case FRR_ZAPI5_SAFI_MULTICAST: + return bgp.RF_IPv4_MC + case FRR_ZAPI5_SAFI_MPLS_VPN: + return bgp.RF_IPv4_VPN + case FRR_ZAPI5_SAFI_ENCAP: + return bgp.RF_IPv4_ENCAP + case FRR_ZAPI5_SAFI_LABELED_UNICAST: + return bgp.RF_IPv4_MPLS + case FRR_ZAPI5_SAFI_FLOWSPEC: + return bgp.RF_FS_IPv4_UC + } + case syscall.AF_INET6: + switch b.SAFI { + case FRR_ZAPI5_SAFI_UNICAST: + return bgp.RF_IPv6_UC + case FRR_ZAPI5_SAFI_MULTICAST: + return bgp.RF_IPv6_MC + case FRR_ZAPI5_SAFI_MPLS_VPN: + return bgp.RF_IPv6_VPN + case FRR_ZAPI5_SAFI_ENCAP: + return bgp.RF_IPv6_ENCAP + case FRR_ZAPI5_SAFI_LABELED_UNICAST: + return bgp.RF_IPv6_MPLS + case FRR_ZAPI5_SAFI_FLOWSPEC: + return bgp.RF_FS_IPv6_UC + } + default: + switch b.SAFI { + case FRR_ZAPI5_SAFI_EVPN: + return bgp.RF_EVPN + default: + return bgp.RF_OPAQUE + } + } } return bgp.RF_OPAQUE } @@ -1897,9 +2041,15 @@ func (b *IPRouteBody) Serialize(version uint8) ([]byte, error) { } if version >= 5 && b.Message&FRR_ZAPI5_MESSAGE_LABEL > 0 { buf = append(buf, nexthop.LabelNum) - bbuf := make([]byte, 4) - binary.BigEndian.PutUint32(bbuf, nexthop.MplsLabels[0]) - buf = append(buf, bbuf...) + for i := uint8(0); i < nexthop.LabelNum; i++ { + bbuf := make([]byte, 4) + /* Frr uses stream_put for mpls label array. + stream_put is unaware of byteorder coversion. + Therefore LittleEndian is used instead of BigEndian. */ + binary.LittleEndian.PutUint32(bbuf, nexthop.MplsLabels[i]) + buf = append(buf, bbuf...) + + } } } if (version <= 3 && b.Message&MESSAGE_DISTANCE > 0) || @@ -2240,7 +2390,10 @@ func decodeNexthopsFromBytes(nexthops *[]Nexthop, data []byte, family uint8, ver } nexthop.MplsLabels = make([]uint32, nexthop.LabelNum) for n := uint8(0); n < nexthop.LabelNum; n++ { - nexthop.MplsLabels[n] = binary.BigEndian.Uint32(data[offset : offset+4]) + /* Frr uses stream_put for mpls label array. + stream_put is unaware of byteorder coversion. + Therefore LittleEndian is used instead of BigEndian. */ + nexthop.MplsLabels[n] = binary.LittleEndian.Uint32(data[offset : offset+4]) offset += 4 } } @@ -2612,6 +2765,177 @@ func (b *NexthopUpdateBody) String() string { return s } +type LabelManagerConnectBody struct { + RedistDefault ROUTE_TYPE + Instance uint16 + // The followings are used in response from Zebra + Result uint8 // 0 means success +} + +// Reference: lm_label_manager_connect in lib/zclient.c of FRR +func (b *LabelManagerConnectBody) Serialize(version uint8) ([]byte, error) { + buf := make([]byte, 3) + buf[0] = uint8(b.RedistDefault) + binary.BigEndian.PutUint16(buf[1:3], b.Instance) + return buf, nil +} + +func (b *LabelManagerConnectBody) DecodeFromBytes(data []byte, version uint8) error { + size := 1 + // FRR v4 may work incorrectly. It returns result only although it uses ZAPI v5. + if version >= 4 { + size = 4 + } + if len(data) < size { + return fmt.Errorf("invalid message length for LABEL_MANAGER_CONNECT response: %d<%d", + len(data), size) + } + if version > 4 { + b.RedistDefault = ROUTE_TYPE(data[0]) + b.Instance = binary.BigEndian.Uint16(data[1:3]) + data = data[3:] + } + b.Result = data[0] + return nil +} + +func (b *LabelManagerConnectBody) String() string { + return fmt.Sprintf( + "route_type: %s, instance: %d, result: %d", + b.RedistDefault.String(), b.Instance, b.Result) +} + +// Reference: zsend_assign_label_chunk_response in zebra/zserv.c of FRR3.x +// Reference: zsend_assign_label_chunk_response in zebra/zapi_msg.c of FRR5.x and 6.x +type GetLabelChunkBody struct { + Proto uint8 // it is appeared in FRR5.x and 6.x + Instance uint16 // it is appeared in FRR5.x and 6.x + Keep uint8 + ChunkSize uint32 + // The followings are used in response from Zebra + Start uint32 + End uint32 +} + +// Reference: zread_get_label_chunk in zebra/zserv.c of FRR3.x +// Reference: zread_get_label_chunk in zebra/zapi_msg.c of FRR5.x and 6.x +func (b *GetLabelChunkBody) Serialize(version uint8) ([]byte, error) { + buf := make([]byte, 8) + pos := 0 + if version > 4 { + buf[pos] = b.Proto + binary.BigEndian.PutUint16(buf[pos+1:pos+3], b.Instance) + pos += 3 + } + buf[pos] = b.Keep + binary.BigEndian.PutUint32(buf[pos+1:pos+5], b.ChunkSize) + pos += 5 + return buf[0:pos], nil +} + +// Reference: zsend_assign_label_chunk_response in zebra/zserv.c of FRR3.x +// Reference: zsend_assign_label_chunk_response in zebra/zapi_msg.c of FRR5.x and 6.x +func (b *GetLabelChunkBody) DecodeFromBytes(data []byte, version uint8) error { + size := 9 + if version > 4 { + size = 12 + } + if len(data) < size { + return fmt.Errorf("invalid message length for GET_LABEL_CHUNK response: %d<%d", + len(data), size) + } + if version > 4 { + b.Proto = data[0] + b.Instance = binary.BigEndian.Uint16(data[1:3]) + data = data[3:] + } + b.Keep = data[0] + b.Start = binary.BigEndian.Uint32(data[1:5]) + b.End = binary.BigEndian.Uint32(data[5:9]) + return nil +} + +func (b *GetLabelChunkBody) String() string { + return fmt.Sprintf( + "keep: %d, chunk_size: %d, start: %d, end: %d", + b.Keep, b.ChunkSize, b.Start, b.End) +} + +type ReleaseLabelChunkBody struct { + Proto uint8 // it is appeared in FRR5.x and 6.x + Instance uint16 // it is appeared in FRR5.x and 6.x + Start uint32 + End uint32 +} + +func (b *ReleaseLabelChunkBody) Serialize(version uint8) ([]byte, error) { + buf := make([]byte, 11) + pos := 0 + if version > 4 { + buf[pos] = b.Proto + binary.BigEndian.PutUint16(buf[pos+1:pos+3], b.Instance) + pos += 3 + } + binary.BigEndian.PutUint32(buf[pos:pos+4], b.Start) + binary.BigEndian.PutUint32(buf[pos+4:pos+8], b.End) + pos += 8 + return buf[0:pos], nil +} + +func (b *ReleaseLabelChunkBody) DecodeFromBytes(data []byte, version uint8) error { + // No response from Zebra + return nil +} + +func (b *ReleaseLabelChunkBody) String() string { + return fmt.Sprintf( + "start: %d, end: %d", + b.Start, b.End) +} + +//go:generate stringer -type=LSP_TYPE +type LSP_TYPE uint8 + +const ( + LSP_NONE LSP_TYPE = iota //defined in FRR3 and over + LSP_STATIC //defined in FRR3 and over + LSP_LDP //defined in FRR3 and over + LSP_BGP //defined in FRR4 and over + LSP_SR //defined in FRR4 and over + LSP_SHARP //defined in FRR5 and over +) + +type VrfLabelBody struct { + Label uint32 + Afi AFI + LabelType LSP_TYPE +} + +// Reference: zclient_send_vrf_label in lib/zclient.c of FRR 5.x and 6.x +func (b *VrfLabelBody) Serialize(version uint8) ([]byte, error) { + buf := make([]byte, 6) + binary.BigEndian.PutUint32(buf[0:4], b.Label) + buf[4] = uint8(b.Afi) + buf[5] = uint8(b.LabelType) + return buf, nil +} + +func (b *VrfLabelBody) DecodeFromBytes(data []byte, version uint8) error { + if len(data) < 6 { + return fmt.Errorf("invalid message length for VRF_LABEL message: %d<6", len(data)) + } + b.Label = binary.BigEndian.Uint32(data[0:4]) + b.Afi = AFI(data[4]) + b.LabelType = LSP_TYPE(data[5]) + return nil +} + +func (b *VrfLabelBody) String() string { + return fmt.Sprintf( + "label: %d, AFI: %s LSP_type: %s", + b.Label, b.Afi, b.LabelType) +} + type Message struct { Header Header Body Body @@ -2689,6 +3013,15 @@ func (m *Message) parseFrrMessage(data []byte) error { case FRR_PW_STATUS_UPDATE: // TODO m.Body = &UnknownBody{} + case FRR_LABEL_MANAGER_CONNECT: + // Note: Synchronous message + m.Body = &LabelManagerConnectBody{} + case FRR_GET_LABEL_CHUNK: + // Note: Synchronous message + m.Body = &GetLabelChunkBody{} + case FRR_RELEASE_LABEL_CHUNK: + // Note: Synchronous message + m.Body = &ReleaseLabelChunkBody{} default: m.Body = &UnknownBody{} } @@ -2728,6 +3061,17 @@ func (m *Message) parseFrrZapi5Message(data []byte) error { case FRR_ZAPI5_PW_STATUS_UPDATE: // TODO m.Body = &UnknownBody{} + case FRR_ZAPI5_LABEL_MANAGER_CONNECT: + // Note: Synchronous message + m.Body = &LabelManagerConnectBody{} + case FRR_ZAPI5_GET_LABEL_CHUNK: + // Note: Synchronous message + m.Body = &GetLabelChunkBody{} + case FRR_ZAPI5_RELEASE_LABEL_CHUNK: + // Note: Synchronous message + m.Body = &ReleaseLabelChunkBody{} + case FRR_ZAPI5_VRF_LABEL: + m.Body = &VrfLabelBody{} default: m.Body = &UnknownBody{} } @@ -2767,6 +3111,16 @@ func (m *Message) parseFrrZapi6Message(data []byte) error { case FRR_ZAPI6_PW_STATUS_UPDATE: // TODO m.Body = &UnknownBody{} + case FRR_ZAPI6_LABEL_MANAGER_CONNECT: + // Note: Synchronous message + m.Body = &LabelManagerConnectBody{} + case FRR_ZAPI6_GET_LABEL_CHUNK: + // Note: Synchronous message + m.Body = &GetLabelChunkBody{} + case FRR_ZAPI6_RELEASE_LABEL_CHUNK: + // Note: Synchronous message + case FRR_ZAPI6_VRF_LABEL: + m.Body = &VrfLabelBody{} default: m.Body = &UnknownBody{} } |