github.com/osrg/gobgp/v3@v3.30.0/pkg/server/zclient.go (about)

     1  // Copyright (C) 2015 Nippon Telegraph and Telephone Corporation.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    12  // implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  package server
    17  
    18  import (
    19  	"fmt"
    20  	"math"
    21  	"net"
    22  	"strconv"
    23  	"strings"
    24  	"syscall"
    25  	"time"
    26  
    27  	"github.com/osrg/gobgp/v3/internal/pkg/table"
    28  	"github.com/osrg/gobgp/v3/pkg/log"
    29  	"github.com/osrg/gobgp/v3/pkg/packet/bgp"
    30  	"github.com/osrg/gobgp/v3/pkg/zebra"
    31  )
    32  
    33  // nexthopStateCache stores a map of nexthop IP to metric value. Especially,
    34  // the metric value of math.MaxUint32 means the nexthop is unreachable.
    35  type nexthopStateCache map[string]uint32
    36  
    37  func (m nexthopStateCache) applyToPathList(paths []*table.Path) []*table.Path {
    38  	updated := make([]*table.Path, 0, len(paths))
    39  	for _, path := range paths {
    40  		if path == nil || path.IsWithdraw {
    41  			continue
    42  		}
    43  		metric, ok := m[path.GetNexthop().String()]
    44  		if !ok {
    45  			continue
    46  		}
    47  		isNexthopInvalid := metric == math.MaxUint32
    48  		med, err := path.GetMed()
    49  		if err == nil && med == metric && path.IsNexthopInvalid == isNexthopInvalid {
    50  			// If the nexthop state of the given path is already up to date,
    51  			// skips this path.
    52  			continue
    53  		}
    54  		newPath := path.Clone(false)
    55  		if isNexthopInvalid {
    56  			newPath.IsNexthopInvalid = true
    57  		} else {
    58  			newPath.IsNexthopInvalid = false
    59  			newPath.SetMed(int64(metric), true)
    60  		}
    61  		updated = append(updated, newPath)
    62  	}
    63  	return updated
    64  }
    65  
    66  func (m nexthopStateCache) updateByNexthopUpdate(body *zebra.NexthopUpdateBody) (updated bool) {
    67  	if len(body.Nexthops) == 0 {
    68  		// If NEXTHOP_UPDATE message does not contain any nexthop, the given
    69  		// nexthop is unreachable.
    70  		if _, ok := m[body.Prefix.Prefix.String()]; !ok {
    71  			// Zebra will send an empty NEXTHOP_UPDATE message as the fist
    72  			// response for the NEXTHOP_REGISTER message. Here ignores it.
    73  			return false
    74  		}
    75  		m[body.Prefix.Prefix.String()] = math.MaxUint32 // means unreachable
    76  	} else {
    77  		m[body.Prefix.Prefix.String()] = body.Metric
    78  	}
    79  	return true
    80  }
    81  
    82  func (m nexthopStateCache) filterPathToRegister(paths []*table.Path) []*table.Path {
    83  	filteredPaths := make([]*table.Path, 0, len(paths))
    84  	for _, path := range paths {
    85  		// Here filters out:
    86  		// - Nil path
    87  		// - Withdrawn path
    88  		// - External path (advertised from Zebra) in order avoid sending back
    89  		// - Unspecified nexthop address
    90  		// - Already registered nexthop
    91  		if path == nil || path.IsWithdraw || path.IsFromExternal() {
    92  			continue
    93  		} else if nexthop := path.GetNexthop(); nexthop.IsUnspecified() {
    94  			continue
    95  		} else if _, ok := m[nexthop.String()]; ok {
    96  			continue
    97  		}
    98  		filteredPaths = append(filteredPaths, path)
    99  	}
   100  	return filteredPaths
   101  }
   102  
   103  func filterOutExternalPath(paths []*table.Path) []*table.Path {
   104  	filteredPaths := make([]*table.Path, 0, len(paths))
   105  	for _, path := range paths {
   106  		// Here filters out:
   107  		// - Nil path
   108  		// - External path (advertised from Zebra) in order avoid sending back
   109  		// - Unreachable path because invalidated by Zebra
   110  		if path == nil || path.IsFromExternal() || path.IsNexthopInvalid {
   111  			continue
   112  		}
   113  		filteredPaths = append(filteredPaths, path)
   114  	}
   115  	return filteredPaths
   116  }
   117  
   118  func addLabelToNexthop(path *table.Path, z *zebraClient, msgFlags *zebra.MessageFlag, nexthop *zebra.Nexthop) {
   119  	rf := path.GetRouteFamily()
   120  	if rf == bgp.RF_IPv4_VPN || rf == bgp.RF_IPv6_VPN {
   121  		z.client.SetLabelFlag(msgFlags, nexthop)
   122  		switch rf {
   123  		case bgp.RF_IPv4_VPN:
   124  			for _, label := range path.GetNlri().(*bgp.LabeledVPNIPAddrPrefix).Labels.Labels {
   125  				nexthop.LabelNum++
   126  				nexthop.MplsLabels = append(nexthop.MplsLabels, label)
   127  			}
   128  		case bgp.RF_IPv6_VPN:
   129  			for _, label := range path.GetNlri().(*bgp.LabeledVPNIPv6AddrPrefix).Labels.Labels {
   130  				nexthop.LabelNum++
   131  				nexthop.MplsLabels = append(nexthop.MplsLabels, label)
   132  			}
   133  		}
   134  	}
   135  }
   136  
   137  func newIPRouteBody(dst []*table.Path, vrfID uint32, z *zebraClient) (body *zebra.IPRouteBody, isWithdraw bool) {
   138  	version := z.client.Version
   139  	paths := filterOutExternalPath(dst)
   140  	if len(paths) == 0 {
   141  		return nil, false
   142  	}
   143  	path := paths[0]
   144  
   145  	l := strings.SplitN(path.GetPrefix(), "/", 2)
   146  	var prefix net.IP
   147  	var nexthop zebra.Nexthop
   148  	nexthops := make([]zebra.Nexthop, 0, len(paths))
   149  	msgFlags := zebra.MessageNexthop
   150  	switch path.GetRouteFamily() {
   151  	case bgp.RF_IPv4_UC:
   152  		prefix = path.GetNlri().(*bgp.IPAddrPrefix).IPAddrPrefixDefault.Prefix.To4()
   153  	case bgp.RF_IPv4_VPN:
   154  		prefix = path.GetNlri().(*bgp.LabeledVPNIPAddrPrefix).IPAddrPrefixDefault.Prefix.To4()
   155  	case bgp.RF_IPv6_UC:
   156  		prefix = path.GetNlri().(*bgp.IPv6AddrPrefix).IPAddrPrefixDefault.Prefix.To16()
   157  	case bgp.RF_IPv6_VPN:
   158  		prefix = path.GetNlri().(*bgp.LabeledVPNIPv6AddrPrefix).IPAddrPrefixDefault.Prefix.To16()
   159  	default:
   160  		return nil, false
   161  	}
   162  	nhVrfID := uint32(zebra.DefaultVrf)
   163  	for vrfPath, pathVrfID := range z.pathVrfMap {
   164  		if path.Equal(vrfPath) {
   165  			nhVrfID = pathVrfID
   166  			break
   167  		} else {
   168  			continue
   169  		}
   170  	}
   171  	for _, p := range paths {
   172  		nexthop.Gate = p.GetNexthop()
   173  		nexthop.VrfID = nhVrfID
   174  		if nhVrfID != vrfID {
   175  			addLabelToNexthop(path, z, &msgFlags, &nexthop)
   176  		}
   177  		nexthops = append(nexthops, nexthop)
   178  	}
   179  	plen, _ := strconv.ParseUint(l[1], 10, 8)
   180  	med, err := path.GetMed()
   181  	if err == nil {
   182  		msgFlags |= zebra.MessageMetric.ToEach(version, z.client.Software)
   183  	}
   184  	var flags zebra.Flag
   185  	if path.IsIBGP() {
   186  		flags = zebra.FlagIBGP.ToEach(z.client.Version, z.client.Software) | zebra.FlagAllowRecursion
   187  	} else if path.GetSource().MultihopTtl > 0 {
   188  		flags = zebra.FlagAllowRecursion // 0x01
   189  	}
   190  	return &zebra.IPRouteBody{
   191  		Type:    zebra.RouteBGP,
   192  		Flags:   flags,
   193  		Safi:    zebra.SafiUnicast,
   194  		Message: msgFlags,
   195  		Prefix: zebra.Prefix{
   196  			Prefix:    prefix,
   197  			PrefixLen: uint8(plen),
   198  		},
   199  		Nexthops: nexthops,
   200  		Metric:   med,
   201  	}, path.IsWithdraw
   202  }
   203  
   204  func newNexthopRegisterBody(paths []*table.Path, nexthopCache nexthopStateCache) *zebra.NexthopRegisterBody {
   205  	paths = nexthopCache.filterPathToRegister(paths)
   206  	if len(paths) == 0 {
   207  		return nil
   208  	}
   209  	path := paths[0]
   210  
   211  	family := path.GetRouteFamily()
   212  	nexthops := make([]*zebra.RegisteredNexthop, 0, len(paths))
   213  	for _, p := range paths {
   214  		nexthop := p.GetNexthop()
   215  		var nh *zebra.RegisteredNexthop
   216  		switch family {
   217  		case bgp.RF_IPv4_UC, bgp.RF_IPv4_VPN:
   218  			nh = &zebra.RegisteredNexthop{
   219  				Family: syscall.AF_INET,
   220  				Prefix: nexthop.To4(),
   221  			}
   222  		case bgp.RF_IPv6_UC, bgp.RF_IPv6_VPN:
   223  			nh = &zebra.RegisteredNexthop{
   224  				Family: syscall.AF_INET6,
   225  				Prefix: nexthop.To16(),
   226  			}
   227  		default:
   228  			continue
   229  		}
   230  		nexthops = append(nexthops, nh)
   231  	}
   232  
   233  	// If no nexthop needs to be registered or unregistered, skips to send
   234  	// message.
   235  	if len(nexthops) == 0 {
   236  		return nil
   237  	}
   238  
   239  	return &zebra.NexthopRegisterBody{
   240  		Nexthops: nexthops,
   241  	}
   242  }
   243  
   244  func newNexthopUnregisterBody(family uint16, prefix net.IP) *zebra.NexthopRegisterBody {
   245  	return &zebra.NexthopRegisterBody{
   246  		Nexthops: []*zebra.RegisteredNexthop{{
   247  			Family: family,
   248  			Prefix: prefix,
   249  		}},
   250  	}
   251  }
   252  
   253  func newPathFromIPRouteMessage(logger log.Logger, m *zebra.Message, version uint8, software zebra.Software) *table.Path {
   254  	header := m.Header
   255  	body := m.Body.(*zebra.IPRouteBody)
   256  	family := body.RouteFamily(logger, version, software)
   257  	isWithdraw := body.IsWithdraw(version, software)
   258  
   259  	var nlri bgp.AddrPrefixInterface
   260  	pattr := make([]bgp.PathAttributeInterface, 0)
   261  	origin := bgp.NewPathAttributeOrigin(bgp.BGP_ORIGIN_ATTR_TYPE_IGP)
   262  	pattr = append(pattr, origin)
   263  
   264  	logger.Debug("create path from ip route message",
   265  		log.Fields{
   266  			"Topic":        "Zebra",
   267  			"RouteType":    body.Type.String(),
   268  			"Flag":         body.Flags.String(version, software),
   269  			"Message":      body.Message,
   270  			"Family":       body.Prefix.Family,
   271  			"Prefix":       body.Prefix.Prefix,
   272  			"PrefixLength": body.Prefix.PrefixLen,
   273  			"Nexthop":      body.Nexthops,
   274  			"Metric":       body.Metric,
   275  			"Distance":     body.Distance,
   276  			"Mtu":          body.Mtu,
   277  			"api":          header.Command.String()})
   278  
   279  	switch family {
   280  	case bgp.RF_IPv4_UC:
   281  		nlri = bgp.NewIPAddrPrefix(body.Prefix.PrefixLen, body.Prefix.Prefix.String())
   282  		if len(body.Nexthops) > 0 {
   283  			pattr = append(pattr, bgp.NewPathAttributeNextHop(body.Nexthops[0].Gate.String()))
   284  		}
   285  	case bgp.RF_IPv6_UC:
   286  		nlri = bgp.NewIPv6AddrPrefix(body.Prefix.PrefixLen, body.Prefix.Prefix.String())
   287  		nexthop := ""
   288  		if len(body.Nexthops) > 0 {
   289  			nexthop = body.Nexthops[0].Gate.String()
   290  		}
   291  		pattr = append(pattr, bgp.NewPathAttributeMpReachNLRI(nexthop, []bgp.AddrPrefixInterface{nlri}))
   292  	default:
   293  		logger.Error("unsupport address family",
   294  			log.Fields{
   295  				"Topic":  "Zebra",
   296  				"Family": family})
   297  		return nil
   298  	}
   299  
   300  	med := bgp.NewPathAttributeMultiExitDisc(body.Metric)
   301  	pattr = append(pattr, med)
   302  
   303  	path := table.NewPath(nil, nlri, isWithdraw, pattr, time.Now(), false)
   304  	path.SetIsFromExternal(true)
   305  	return path
   306  }
   307  
   308  type mplsLabelParameter struct {
   309  	rangeSize     uint32
   310  	maps          map[uint64]*table.Bitmap
   311  	unassignedVrf []*table.Vrf //Vrfs which are not assigned MPLS label
   312  }
   313  
   314  type zebraClient struct {
   315  	client       *zebra.Client
   316  	server       *BgpServer
   317  	nexthopCache nexthopStateCache
   318  	pathVrfMap   map[*table.Path]uint32 //vpn paths and nexthop vpn id
   319  	mplsLabel    mplsLabelParameter
   320  	dead         chan struct{}
   321  }
   322  
   323  func (z *zebraClient) getPathListWithNexthopUpdate(body *zebra.NexthopUpdateBody) []*table.Path {
   324  	rib := &table.TableManager{
   325  		Tables: make(map[bgp.RouteFamily]*table.Table),
   326  	}
   327  
   328  	var rfList []bgp.RouteFamily
   329  	switch body.Prefix.Family {
   330  	case syscall.AF_INET:
   331  		rfList = []bgp.RouteFamily{bgp.RF_IPv4_UC, bgp.RF_IPv4_VPN}
   332  	case syscall.AF_INET6:
   333  		rfList = []bgp.RouteFamily{bgp.RF_IPv6_UC, bgp.RF_IPv6_VPN}
   334  	}
   335  
   336  	for _, rf := range rfList {
   337  		tbl, _, err := z.server.getRib("", rf, nil)
   338  		if err != nil {
   339  			z.server.logger.Error("failed to get global rib",
   340  				log.Fields{
   341  					"Topic":  "Zebra",
   342  					"Family": rf.String(),
   343  					"Error":  err})
   344  			continue
   345  		}
   346  		rib.Tables[rf] = tbl
   347  	}
   348  
   349  	return rib.GetPathListWithNexthop(table.GLOBAL_RIB_NAME, rfList, body.Prefix.Prefix)
   350  }
   351  
   352  func (z *zebraClient) updatePathByNexthopCache(paths []*table.Path) {
   353  	paths = z.nexthopCache.applyToPathList(paths)
   354  	if len(paths) > 0 {
   355  		if err := z.server.updatePath("", paths); err != nil {
   356  			z.server.logger.Error("failed to update nexthop reachability",
   357  				log.Fields{
   358  					"Topic":    "Zebra",
   359  					"PathList": paths})
   360  		}
   361  	}
   362  }
   363  
   364  func (z *zebraClient) loop() {
   365  	w := z.server.watch([]watchOption{
   366  		watchBestPath(true),
   367  		watchPostUpdate(true, "", ""),
   368  	}...)
   369  	defer w.Stop()
   370  
   371  	for {
   372  		select {
   373  		case <-z.dead:
   374  			return
   375  		case msg := <-z.client.Receive():
   376  			if msg == nil {
   377  				break
   378  			}
   379  			switch body := msg.Body.(type) {
   380  			case *zebra.IPRouteBody:
   381  				if path := newPathFromIPRouteMessage(z.server.logger, msg, z.client.Version, z.client.Software); path != nil {
   382  					if err := z.server.addPathList("", []*table.Path{path}); err != nil {
   383  						z.server.logger.Error("failed to add path from zebra",
   384  							log.Fields{
   385  								"Topic": "Zebra",
   386  								"Path":  path,
   387  								"Error": err})
   388  					}
   389  				}
   390  			case *zebra.NexthopUpdateBody:
   391  				if updated := z.nexthopCache.updateByNexthopUpdate(body); !updated {
   392  					continue
   393  				}
   394  				paths := z.getPathListWithNexthopUpdate(body)
   395  				if len(paths) == 0 {
   396  					// If there is no path bound for the given nexthop, send
   397  					// NEXTHOP_UNREGISTER message.
   398  					z.client.SendNexthopRegister(msg.Header.VrfID, newNexthopUnregisterBody(uint16(body.Prefix.Family), body.Prefix.Prefix), true)
   399  					delete(z.nexthopCache, body.Prefix.Prefix.String())
   400  				}
   401  				z.updatePathByNexthopCache(paths)
   402  			case *zebra.GetLabelChunkBody:
   403  				z.server.logger.Debug("zebra GetLabelChunkBody is received",
   404  					log.Fields{
   405  						"Topic": "Zebra",
   406  						"Start": body.Start,
   407  						"End":   body.End})
   408  				startEnd := uint64(body.Start)<<32 | uint64(body.End)
   409  				z.mplsLabel.maps[startEnd] = table.NewBitmap(int(body.End - body.Start + 1))
   410  				for _, vrf := range z.mplsLabel.unassignedVrf {
   411  					if err := z.assignAndSendVrfMplsLabel(vrf); err != nil {
   412  						z.server.logger.Error("zebra failed to assign and send vrf mpls label",
   413  							log.Fields{
   414  								"Topic": "Zebra",
   415  								"Error": err})
   416  					}
   417  				}
   418  				z.mplsLabel.unassignedVrf = nil
   419  			}
   420  		case ev := <-w.Event():
   421  			switch msg := ev.(type) {
   422  			case *watchEventBestPath:
   423  				if table.UseMultiplePaths.Enabled {
   424  					for _, paths := range msg.MultiPathList {
   425  						z.updatePathByNexthopCache(paths)
   426  						for i := range msg.Vrf {
   427  							if body, isWithdraw := newIPRouteBody(paths, i, z); body != nil {
   428  								z.client.SendIPRoute(i, body, isWithdraw)
   429  							}
   430  							if body := newNexthopRegisterBody(paths, z.nexthopCache); body != nil {
   431  								z.client.SendNexthopRegister(i, body, false)
   432  							}
   433  						}
   434  					}
   435  				} else {
   436  					z.updatePathByNexthopCache(msg.PathList)
   437  					for _, path := range msg.PathList {
   438  						for i := range msg.Vrf {
   439  							if body, isWithdraw := newIPRouteBody([]*table.Path{path}, i, z); body != nil {
   440  								err := z.client.SendIPRoute(i, body, isWithdraw)
   441  								if err != nil {
   442  									continue
   443  								}
   444  							}
   445  							if body := newNexthopRegisterBody([]*table.Path{path}, z.nexthopCache); body != nil {
   446  								z.client.SendNexthopRegister(i, body, false)
   447  							}
   448  						}
   449  					}
   450  				}
   451  			case *watchEventUpdate:
   452  				if body := newNexthopRegisterBody(msg.PathList, z.nexthopCache); body != nil {
   453  					vrfID := uint32(0)
   454  					for _, vrf := range z.server.listVrf() {
   455  						if vrf.Name == msg.Neighbor.Config.Vrf {
   456  							vrfID = uint32(vrf.Id)
   457  						}
   458  					}
   459  					z.client.SendNexthopRegister(vrfID, body, false)
   460  				}
   461  			}
   462  		}
   463  	}
   464  }
   465  
   466  func newZebraClient(s *BgpServer, url string, protos []string, version uint8, nhtEnable bool, nhtDelay uint8, mplsLabelRangeSize uint32, software zebra.Software) (*zebraClient, error) {
   467  	l := strings.SplitN(url, ":", 2)
   468  	if len(l) != 2 {
   469  		return nil, fmt.Errorf("unsupported url: %s", url)
   470  	}
   471  	var cli *zebra.Client
   472  	var err error
   473  	var usingVersion uint8
   474  	var zapivers [zebra.MaxZapiVer - zebra.MinZapiVer + 1]uint8
   475  	zapivers[0] = version
   476  	for elem, ver := 1, zebra.MinZapiVer; elem < len(zapivers) && ver <= zebra.MaxZapiVer; elem++ {
   477  		if version == ver && ver < zebra.MaxZapiVer {
   478  			ver++
   479  		}
   480  		zapivers[elem] = ver
   481  		ver++
   482  	}
   483  	for elem, ver := range zapivers {
   484  		cli, err = zebra.NewClient(s.logger, l[0], l[1], zebra.RouteBGP, ver, software, mplsLabelRangeSize)
   485  		if cli != nil && err == nil {
   486  			usingVersion = ver
   487  			break
   488  		}
   489  		// Retry with another Zebra message version
   490  		s.logger.Warn("cannot connect to Zebra with message version",
   491  			log.Fields{
   492  				"Topic":   "Zebra",
   493  				"Version": ver})
   494  		if elem < len(zapivers)-1 {
   495  			s.logger.Warn("going to retry another version",
   496  				log.Fields{
   497  					"Topic":   "Zebra",
   498  					"Version": zapivers[elem+1]})
   499  		}
   500  	}
   501  	if cli == nil || err != nil {
   502  		return nil, err
   503  	}
   504  	s.logger.Info("success to connect to Zebra",
   505  		log.Fields{
   506  			"Topic":   "Zebra",
   507  			"Version": usingVersion})
   508  
   509  	// Note: HELLO/ROUTER_ID_ADD messages are automatically sent to negotiate
   510  	// the Zebra message version in zebra.NewClient().
   511  	// cli.SendHello()
   512  	// cli.SendRouterIDAdd()
   513  	cli.SendInterfaceAdd()
   514  	for _, typ := range protos {
   515  		t, err := zebra.RouteTypeFromString(typ, version, software)
   516  		if err != nil {
   517  			return nil, err
   518  		}
   519  		cli.SendRedistribute(t, zebra.DefaultVrf)
   520  	}
   521  	w := &zebraClient{
   522  		client:       cli,
   523  		server:       s,
   524  		nexthopCache: make(nexthopStateCache),
   525  		pathVrfMap:   make(map[*table.Path]uint32),
   526  		mplsLabel: mplsLabelParameter{
   527  			rangeSize: mplsLabelRangeSize,
   528  			maps:      make(map[uint64]*table.Bitmap),
   529  		},
   530  		dead: make(chan struct{}),
   531  	}
   532  	go w.loop()
   533  	if mplsLabelRangeSize > 0 && cli.SupportMpls() {
   534  		if err = cli.SendGetLabelChunk(&zebra.GetLabelChunkBody{ChunkSize: mplsLabelRangeSize}); err != nil {
   535  			return nil, err
   536  		}
   537  	}
   538  	return w, nil
   539  }
   540  
   541  func (z *zebraClient) assignMplsLabel() (uint32, error) {
   542  	if z.mplsLabel.maps == nil {
   543  		return 0, nil
   544  	}
   545  	var label uint32
   546  	for startEnd, bitmap := range z.mplsLabel.maps {
   547  		start := uint32(startEnd >> 32)
   548  		end := uint32(startEnd & 0xffffffff)
   549  		l, err := bitmap.FindandSetZeroBit()
   550  		if err == nil && start+uint32(l) <= end {
   551  			label = start + uint32(l)
   552  			break
   553  		}
   554  	}
   555  	if label == 0 {
   556  		return 0, fmt.Errorf("failed to assign new MPLS label")
   557  	}
   558  	return label, nil
   559  }
   560  
   561  func (z *zebraClient) assignAndSendVrfMplsLabel(vrf *table.Vrf) error {
   562  	var err error
   563  	if vrf.MplsLabel, err = z.assignMplsLabel(); vrf.MplsLabel > 0 { // success
   564  		if err = z.client.SendVrfLabel(vrf.MplsLabel, vrf.Id); err != nil {
   565  			return err
   566  		}
   567  	} else if vrf.MplsLabel == 0 { // GetLabelChunk is not performed
   568  		z.mplsLabel.unassignedVrf = append(z.mplsLabel.unassignedVrf, vrf)
   569  	}
   570  	return err
   571  }
   572  
   573  func (z *zebraClient) releaseMplsLabel(label uint32) {
   574  	if z.mplsLabel.maps == nil {
   575  		return
   576  	}
   577  	for startEnd, bitmap := range z.mplsLabel.maps {
   578  		start := uint32(startEnd >> 32)
   579  		end := uint32(startEnd & 0xffffffff)
   580  		if start <= label && label <= end {
   581  			bitmap.Unflag(uint(label - start))
   582  			return
   583  		}
   584  	}
   585  }