Skip to content

Commit

Permalink
zclinet: Enable to request MPLS label range to FRRouting
Browse files Browse the repository at this point in the history
With FRRouting, MPLS label allocation is maintained on Zebra daemon, so
GoBGP need to request the label range allocation to Zebra daemon in
order to allocate a label to VRFs with MPLS VPN.

Example of Configuration:

```toml
[zebra.config]
  enabled = true
  url = "unix:/var/run/frr/zserv.api"
  redistribute-route-type-list = ["connect"]
  version = 4
  mpls-label-range-size = 100
```

Note: "version >= 4" is required to use this feature.

Signed-off-by: IWASE Yusuke <iwase.yusuke0@gmail.com>
  • Loading branch information
iwaseyusuke committed Jun 5, 2018
1 parent a6e0d00 commit 418dcb0
Show file tree
Hide file tree
Showing 8 changed files with 295 additions and 26 deletions.
17 changes: 15 additions & 2 deletions config/bgp_configs.go
Original file line number Diff line number Diff line change
Expand Up @@ -1116,13 +1116,18 @@ type ZebraState struct {
// original -> gobgp:redistribute-route-type
RedistributeRouteTypeList []InstallProtocolType `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 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.
Expand All @@ -1137,13 +1142,18 @@ type ZebraConfig struct {
// original -> gobgp:redistribute-route-type
RedistributeRouteTypeList []InstallProtocolType `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 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 {
Expand Down Expand Up @@ -1173,6 +1183,9 @@ func (lhs *ZebraConfig) Equal(rhs *ZebraConfig) bool {
if lhs.NexthopTriggerDelay != rhs.NexthopTriggerDelay {
return false
}
if lhs.MplsLabelRangeSize != rhs.MplsLabelRangeSize {
return false
}
return true
}

Expand Down
34 changes: 29 additions & 5 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -1394,8 +1394,17 @@ func (s *BgpServer) StartZebraClient(c *config.ZebraConfig) error {
protos = append(protos, string(p))
}
var err error
s.zclient, err = newZebraClient(s, c.Url, protos, c.Version, c.NexthopTriggerEnable, c.NexthopTriggerDelay)
return err
s.zclient, err = newZebraClient(s, c.Url, protos, c.Version, c.NexthopTriggerEnable, c.NexthopTriggerDelay, c.MplsLabelRangeSize)
if err != nil {
return err
}
if s.globalRib != nil && s.zclient.client.Version >= 4 && c.MplsLabelRangeSize > 0 {
if err := s.globalRib.EnableMplsLabelAllocation(); err != nil {
s.zclient.stop()
return err
}
}
return nil
}, false)
}

Expand Down Expand Up @@ -1708,9 +1717,24 @@ func (s *BgpServer) AddVrf(name string, id uint32, rd bgp.RouteDistinguisherInte
AS: s.bgpConfig.Global.Config.As,
LocalID: net.ParseIP(s.bgpConfig.Global.Config.RouterId).To4(),
}
if pathList, e := s.globalRib.AddVrf(name, id, rd, im, ex, pi); e != nil {
return e
} else if len(pathList) > 0 {
pathList, err := s.globalRib.AddVrf(name, id, rd, im, ex, pi)
if err != nil {
if _, ok := err.(*table.MplsLabelRangeFullError); ok {
var start, end uint32
if start, end, err = s.zclient.requestMplsLabelAllocation(); err != nil {
return err
}
if err = s.globalRib.AllocateMplsLabelRange(start, end); err != nil {
return err
}
if pathList, err = s.globalRib.AddVrf(name, id, rd, im, ex, pi); err != nil {
return err
}
} else {
return err
}
}
if len(pathList) > 0 {
s.propagateUpdate(nil, pathList)
}
return nil
Expand Down
37 changes: 28 additions & 9 deletions server/zclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,16 +277,27 @@ func newPathFromIPRouteMessage(m *zebra.Message) *table.Path {
}

type zebraClient struct {
client *zebra.Client
server *BgpServer
nexthopCache nexthopStateCache
dead chan struct{}
client *zebra.Client
server *BgpServer
nexthopCache nexthopStateCache
mplsLabelRangeCh chan [2]uint32 // {start, end}
mplsLabelRangeSize uint32
dead chan struct{}
}

func (z *zebraClient) stop() {
close(z.dead)
}

func (z *zebraClient) requestMplsLabelAllocation() (uint32, uint32, error) {
if z.mplsLabelRangeCh == nil {
return 0, 0, fmt.Errorf("no mpls label range channel")
}
z.client.SendGetLabelChunk(&zebra.GetLabelChunkBody{ChunkSize: z.mplsLabelRangeSize})
labels := <-z.mplsLabelRangeCh
return labels[0], labels[1], nil
}

func (z *zebraClient) getPathListWithNexthopUpdate(body *zebra.NexthopUpdateBody) []*table.Path {
rib := &table.TableManager{
Tables: make(map[bgp.RouteFamily]*table.Table),
Expand Down Expand Up @@ -336,6 +347,11 @@ func (z *zebraClient) loop() {
}...)
defer w.Stop()

if z.mplsLabelRangeSize > 0 {
z.mplsLabelRangeCh = make(chan [2]uint32)
defer close(z.mplsLabelRangeCh)
}

for {
select {
case <-z.dead:
Expand Down Expand Up @@ -364,6 +380,8 @@ func (z *zebraClient) loop() {
delete(z.nexthopCache, body.Prefix.String())
}
z.updatePathByNexthopCache(paths)
case *zebra.GetLabelChunkBody:
z.mplsLabelRangeCh <- [2]uint32{body.Start, body.End}
}
case ev := <-w.Event():
switch msg := ev.(type) {
Expand Down Expand Up @@ -412,7 +430,7 @@ func (z *zebraClient) loop() {
}
}

func newZebraClient(s *BgpServer, url string, protos []string, version uint8, nhtEnable bool, nhtDelay uint8) (*zebraClient, error) {
func newZebraClient(s *BgpServer, url string, protos []string, version uint8, nhtEnable bool, nhtDelay uint8, mplsLabelRangeSize uint32) (*zebraClient, error) {
l := strings.SplitN(url, ":", 2)
if len(l) != 2 {
return nil, fmt.Errorf("unsupported url: %s", url)
Expand Down Expand Up @@ -445,10 +463,11 @@ func newZebraClient(s *BgpServer, url string, protos []string, version uint8, nh
cli.SendRedistribute(t, zebra.VRF_DEFAULT)
}
w := &zebraClient{
client: cli,
server: s,
nexthopCache: make(nexthopStateCache),
dead: make(chan struct{}),
client: cli,
server: s,
nexthopCache: make(nexthopStateCache),
mplsLabelRangeSize: mplsLabelRangeSize,
dead: make(chan struct{}),
}
go w.loop()
return w, nil
Expand Down
8 changes: 4 additions & 4 deletions table/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -1128,12 +1128,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)
Expand All @@ -1157,11 +1157,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)
Expand Down
89 changes: 84 additions & 5 deletions table/table_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ const (
GLOBAL_RIB_NAME = "global"
)

type MplsLabelRangeFullError struct{}

func (e *MplsLabelRangeFullError) Error() string {
return "could not assign new MPLS label; need to allocate new MPLS label range"
}

func NewMplsLabelRangeFullError() *MplsLabelRangeFullError {
return &MplsLabelRangeFullError{}
}

func ProcessMessage(m *bgp.BGPMessage, peerInfo *PeerInfo, timestamp time.Time) []*Path {
update := m.Body.(*bgp.BGPUpdate)

Expand Down Expand Up @@ -107,9 +117,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 {
Expand All @@ -128,23 +139,89 @@ func (manager *TableManager) GetRFlist() []bgp.RouteFamily {
return manager.rfList
}

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, NewMplsLabelRangeFullError()
}
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) AddVrf(name string, id uint32, rd bgp.RouteDistinguisherInterface, importRt, exportRt []bgp.ExtendedCommunityInterface, info *PeerInfo) ([]*Path, error) {
if _, ok := manager.Vrfs[name]; ok {
return nil, fmt.Errorf("vrf %s already exists", name)
}
mplsLabel, err := manager.assignMplsLabel()
if err != nil {
return nil, err
}
option := &vrfOption{
mplsLabel: mplsLabel,
}
log.WithFields(log.Fields{
"Topic": "Vrf",
"Key": name,
"Rd": rd,
"ImportRt": importRt,
"ExportRt": exportRt,
}).Debugf("add vrf")
"option": fmt.Sprintf("%+v", option),
}).Debug("add vrf")
manager.Vrfs[name] = &Vrf{
Name: name,
Id: id,
Rd: rd,
ImportRt: importRt,
ExportRt: exportRt,
option: option,
}
msgs := make([]*Path, 0, len(importRt))
nexthop := "0.0.0.0"
Expand Down Expand Up @@ -173,7 +250,9 @@ func (manager *TableManager) DeleteVrf(name string) ([]*Path, error) {
"Rd": vrf.Rd,
"ImportRt": vrf.ImportRt,
"ExportRt": vrf.ExportRt,
}).Debugf("delete vrf")
"option": fmt.Sprintf("%+v", vrf.option),
}).Debug("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)...)
Expand Down
14 changes: 14 additions & 0 deletions table/vrf.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,24 @@ import (
"github.com/osrg/gobgp/packet/bgp"
)

type vrfOption struct {
mplsLabel uint32
}

type Vrf struct {
Name string
Id uint32
Rd bgp.RouteDistinguisherInterface
ImportRt []bgp.ExtendedCommunityInterface
ExportRt []bgp.ExtendedCommunityInterface
option *vrfOption
}

func (v *Vrf) MplsLabel() uint32 {
if v.option == nil {
return 0
}
return v.option.mplsLabel
}

func (v *Vrf) Clone() *Vrf {
Expand All @@ -35,12 +47,14 @@ func (v *Vrf) Clone() *Vrf {
}
return l
}
option := *v.option
return &Vrf{
Name: v.Name,
Id: v.Id,
Rd: v.Rd,
ImportRt: f(v.ImportRt),
ExportRt: f(v.ExportRt),
option: &option,
}
}

Expand Down
Loading

0 comments on commit 418dcb0

Please sign in to comment.