github.com/osrg/gobgp@v2.0.0+incompatible/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/internal/pkg/table"
    28  	"github.com/osrg/gobgp/internal/pkg/zebra"
    29  	"github.com/osrg/gobgp/pkg/packet/bgp"
    30  
    31  	log "github.com/sirupsen/logrus"
    32  )
    33  
    34  // nexthopStateCache stores a map of nexthop IP to metric value. Especially,
    35  // the metric value of math.MaxUint32 means the nexthop is unreachable.
    36  type nexthopStateCache map[string]uint32
    37  
    38  func (m nexthopStateCache) applyToPathList(paths []*table.Path) []*table.Path {
    39  	updated := make([]*table.Path, 0, len(paths))
    40  	for _, path := range paths {
    41  		if path == nil || path.IsWithdraw {
    42  			continue
    43  		}
    44  		metric, ok := m[path.GetNexthop().String()]
    45  		if !ok {
    46  			continue
    47  		}
    48  		isNexthopInvalid := metric == math.MaxUint32
    49  		med, err := path.GetMed()
    50  		if err == nil && med == metric && path.IsNexthopInvalid == isNexthopInvalid {
    51  			// If the nexthop state of the given path is already up to date,
    52  			// skips this path.
    53  			continue
    54  		}
    55  		newPath := path.Clone(false)
    56  		if isNexthopInvalid {
    57  			newPath.IsNexthopInvalid = true
    58  		} else {
    59  			newPath.IsNexthopInvalid = false
    60  			newPath.SetMed(int64(metric), true)
    61  		}
    62  		updated = append(updated, newPath)
    63  	}
    64  	return updated
    65  }
    66  
    67  func (m nexthopStateCache) updateByNexthopUpdate(body *zebra.NexthopUpdateBody) (updated bool) {
    68  	if len(body.Nexthops) == 0 {
    69  		// If NEXTHOP_UPDATE message does not contain any nexthop, the given
    70  		// nexthop is unreachable.
    71  		if _, ok := m[body.Prefix.Prefix.String()]; !ok {
    72  			// Zebra will send an empty NEXTHOP_UPDATE message as the fist
    73  			// response for the NEXTHOP_REGISTER message. Here ignores it.
    74  			return false
    75  		}
    76  		m[body.Prefix.Prefix.String()] = math.MaxUint32 // means unreachable
    77  	} else {
    78  		m[body.Prefix.Prefix.String()] = body.Metric
    79  	}
    80  	return true
    81  }
    82  
    83  func (m nexthopStateCache) filterPathToRegister(paths []*table.Path) []*table.Path {
    84  	filteredPaths := make([]*table.Path, 0, len(paths))
    85  	for _, path := range paths {
    86  		// Here filters out:
    87  		// - Nil path
    88  		// - Withdrawn path
    89  		// - External path (advertised from Zebra) in order avoid sending back
    90  		// - Unspecified nexthop address
    91  		// - Already registered nexthop
    92  		if path == nil || path.IsWithdraw || path.IsFromExternal() {
    93  			continue
    94  		} else if nexthop := path.GetNexthop(); nexthop.IsUnspecified() {
    95  			continue
    96  		} else if _, ok := m[nexthop.String()]; ok {
    97  			continue
    98  		}
    99  		filteredPaths = append(filteredPaths, path)
   100  	}
   101  	return filteredPaths
   102  }
   103  
   104  func filterOutExternalPath(paths []*table.Path) []*table.Path {
   105  	filteredPaths := make([]*table.Path, 0, len(paths))
   106  	for _, path := range paths {
   107  		// Here filters out:
   108  		// - Nil path
   109  		// - External path (advertised from Zebra) in order avoid sending back
   110  		// - Unreachable path because invalidated by Zebra
   111  		if path == nil || path.IsFromExternal() || path.IsNexthopInvalid {
   112  			continue
   113  		}
   114  		filteredPaths = append(filteredPaths, path)
   115  	}
   116  	return filteredPaths
   117  }
   118  
   119  func newIPRouteBody(dst []*table.Path) (body *zebra.IPRouteBody, isWithdraw bool) {
   120  	paths := filterOutExternalPath(dst)
   121  	if len(paths) == 0 {
   122  		return nil, false
   123  	}
   124  	path := paths[0]
   125  
   126  	l := strings.SplitN(path.GetNlri().String(), "/", 2)
   127  	var prefix net.IP
   128  	//nexthops := make([]net.IP, 0, len(paths))
   129  	var nexthop zebra.Nexthop
   130  	nexthops := make([]zebra.Nexthop, 0, len(paths))
   131  	switch path.GetRouteFamily() {
   132  	case bgp.RF_IPv4_UC, bgp.RF_IPv4_VPN:
   133  		if path.GetRouteFamily() == bgp.RF_IPv4_UC {
   134  			prefix = path.GetNlri().(*bgp.IPAddrPrefix).IPAddrPrefixDefault.Prefix.To4()
   135  		} else {
   136  			prefix = path.GetNlri().(*bgp.LabeledVPNIPAddrPrefix).IPAddrPrefixDefault.Prefix.To4()
   137  		}
   138  		for _, p := range paths {
   139  			nexthop.Gate = p.GetNexthop().To4()
   140  			nexthops = append(nexthops, nexthop)
   141  		}
   142  	case bgp.RF_IPv6_UC, bgp.RF_IPv6_VPN:
   143  		if path.GetRouteFamily() == bgp.RF_IPv6_UC {
   144  			prefix = path.GetNlri().(*bgp.IPv6AddrPrefix).IPAddrPrefixDefault.Prefix.To16()
   145  		} else {
   146  			prefix = path.GetNlri().(*bgp.LabeledVPNIPv6AddrPrefix).IPAddrPrefixDefault.Prefix.To16()
   147  		}
   148  		for _, p := range paths {
   149  			nexthop.Gate = p.GetNexthop().To16()
   150  			nexthops = append(nexthops, nexthop)
   151  		}
   152  	default:
   153  		return nil, false
   154  	}
   155  	msgFlags := zebra.MESSAGE_NEXTHOP
   156  	plen, _ := strconv.ParseUint(l[1], 10, 8)
   157  	med, err := path.GetMed()
   158  	if err == nil {
   159  		msgFlags |= zebra.MESSAGE_METRIC
   160  	}
   161  	var flags zebra.FLAG
   162  	if path.IsIBGP() {
   163  		flags = zebra.FLAG_IBGP | zebra.FLAG_INTERNAL
   164  	} else if path.GetSource().MultihopTtl > 0 {
   165  		flags = zebra.FLAG_INTERNAL
   166  	}
   167  	return &zebra.IPRouteBody{
   168  		Type:    zebra.ROUTE_BGP,
   169  		Flags:   flags,
   170  		SAFI:    zebra.SAFI_UNICAST,
   171  		Message: msgFlags,
   172  		Prefix: zebra.Prefix{
   173  			Prefix:    prefix,
   174  			PrefixLen: uint8(plen),
   175  		},
   176  		Nexthops: nexthops,
   177  		Metric:   med,
   178  	}, path.IsWithdraw
   179  }
   180  
   181  func newNexthopRegisterBody(paths []*table.Path, nexthopCache nexthopStateCache) *zebra.NexthopRegisterBody {
   182  	paths = nexthopCache.filterPathToRegister(paths)
   183  	if len(paths) == 0 {
   184  		return nil
   185  	}
   186  	path := paths[0]
   187  
   188  	family := path.GetRouteFamily()
   189  	nexthops := make([]*zebra.RegisteredNexthop, 0, len(paths))
   190  	for _, p := range paths {
   191  		nexthop := p.GetNexthop()
   192  		var nh *zebra.RegisteredNexthop
   193  		switch family {
   194  		case bgp.RF_IPv4_UC, bgp.RF_IPv4_VPN:
   195  			nh = &zebra.RegisteredNexthop{
   196  				Family: syscall.AF_INET,
   197  				Prefix: nexthop.To4(),
   198  			}
   199  		case bgp.RF_IPv6_UC, bgp.RF_IPv6_VPN:
   200  			nh = &zebra.RegisteredNexthop{
   201  				Family: syscall.AF_INET6,
   202  				Prefix: nexthop.To16(),
   203  			}
   204  		default:
   205  			continue
   206  		}
   207  		nexthops = append(nexthops, nh)
   208  	}
   209  
   210  	// If no nexthop needs to be registered or unregistered, skips to send
   211  	// message.
   212  	if len(nexthops) == 0 {
   213  		return nil
   214  	}
   215  
   216  	return &zebra.NexthopRegisterBody{
   217  		Nexthops: nexthops,
   218  	}
   219  }
   220  
   221  func newNexthopUnregisterBody(family uint16, prefix net.IP) *zebra.NexthopRegisterBody {
   222  	return &zebra.NexthopRegisterBody{
   223  		Nexthops: []*zebra.RegisteredNexthop{{
   224  			Family: family,
   225  			Prefix: prefix,
   226  		}},
   227  	}
   228  }
   229  
   230  func newPathFromIPRouteMessage(m *zebra.Message, version uint8) *table.Path {
   231  	header := m.Header
   232  	body := m.Body.(*zebra.IPRouteBody)
   233  	family := body.RouteFamily(version)
   234  	isWithdraw := body.IsWithdraw(version)
   235  
   236  	var nlri bgp.AddrPrefixInterface
   237  	pattr := make([]bgp.PathAttributeInterface, 0)
   238  	origin := bgp.NewPathAttributeOrigin(bgp.BGP_ORIGIN_ATTR_TYPE_IGP)
   239  	pattr = append(pattr, origin)
   240  
   241  	log.WithFields(log.Fields{
   242  		"Topic":        "Zebra",
   243  		"RouteType":    body.Type.String(),
   244  		"Flag":         body.Flags.String(),
   245  		"Message":      body.Message,
   246  		"Family":       body.Prefix.Family,
   247  		"Prefix":       body.Prefix.Prefix,
   248  		"PrefixLength": body.Prefix.PrefixLen,
   249  		"Nexthop":      body.Nexthops,
   250  		"Metric":       body.Metric,
   251  		"Distance":     body.Distance,
   252  		"Mtu":          body.Mtu,
   253  		"api":          header.Command.String(),
   254  	}).Debugf("create path from ip route message.")
   255  
   256  	switch family {
   257  	case bgp.RF_IPv4_UC:
   258  		nlri = bgp.NewIPAddrPrefix(body.Prefix.PrefixLen, body.Prefix.Prefix.String())
   259  		if len(body.Nexthops) > 0 {
   260  			pattr = append(pattr, bgp.NewPathAttributeNextHop(body.Nexthops[0].Gate.String()))
   261  		}
   262  	case bgp.RF_IPv6_UC:
   263  		nlri = bgp.NewIPv6AddrPrefix(body.Prefix.PrefixLen, body.Prefix.Prefix.String())
   264  		nexthop := ""
   265  		if len(body.Nexthops) > 0 {
   266  			nexthop = body.Nexthops[0].Gate.String()
   267  		}
   268  		pattr = append(pattr, bgp.NewPathAttributeMpReachNLRI(nexthop, []bgp.AddrPrefixInterface{nlri}))
   269  	default:
   270  		log.WithFields(log.Fields{
   271  			"Topic": "Zebra",
   272  		}).Errorf("unsupport address family: %s", family)
   273  		return nil
   274  	}
   275  
   276  	med := bgp.NewPathAttributeMultiExitDisc(body.Metric)
   277  	pattr = append(pattr, med)
   278  
   279  	path := table.NewPath(nil, nlri, isWithdraw, pattr, time.Now(), false)
   280  	path.SetIsFromExternal(true)
   281  	return path
   282  }
   283  
   284  type zebraClient struct {
   285  	client       *zebra.Client
   286  	server       *BgpServer
   287  	nexthopCache nexthopStateCache
   288  	dead         chan struct{}
   289  }
   290  
   291  func (z *zebraClient) getPathListWithNexthopUpdate(body *zebra.NexthopUpdateBody) []*table.Path {
   292  	rib := &table.TableManager{
   293  		Tables: make(map[bgp.RouteFamily]*table.Table),
   294  	}
   295  
   296  	var rfList []bgp.RouteFamily
   297  	switch body.Prefix.Family {
   298  	case syscall.AF_INET:
   299  		rfList = []bgp.RouteFamily{bgp.RF_IPv4_UC, bgp.RF_IPv4_VPN}
   300  	case syscall.AF_INET6:
   301  		rfList = []bgp.RouteFamily{bgp.RF_IPv6_UC, bgp.RF_IPv6_VPN}
   302  	}
   303  
   304  	for _, rf := range rfList {
   305  		tbl, _, err := z.server.getRib("", rf, nil)
   306  		if err != nil {
   307  			log.WithFields(log.Fields{
   308  				"Topic":  "Zebra",
   309  				"Family": rf.String(),
   310  				"Error":  err,
   311  			}).Error("failed to get global rib")
   312  			continue
   313  		}
   314  		rib.Tables[rf] = tbl
   315  	}
   316  
   317  	return rib.GetPathListWithNexthop(table.GLOBAL_RIB_NAME, rfList, body.Prefix.Prefix)
   318  }
   319  
   320  func (z *zebraClient) updatePathByNexthopCache(paths []*table.Path) {
   321  	paths = z.nexthopCache.applyToPathList(paths)
   322  	if len(paths) > 0 {
   323  		if err := z.server.updatePath("", paths); err != nil {
   324  			log.WithFields(log.Fields{
   325  				"Topic":    "Zebra",
   326  				"PathList": paths,
   327  			}).Error("failed to update nexthop reachability")
   328  		}
   329  	}
   330  }
   331  
   332  func (z *zebraClient) loop() {
   333  	w := z.server.watch([]watchOption{
   334  		watchBestPath(true),
   335  		watchPostUpdate(true),
   336  	}...)
   337  	defer w.Stop()
   338  
   339  	for {
   340  		select {
   341  		case <-z.dead:
   342  			return
   343  		case msg := <-z.client.Receive():
   344  			if msg == nil {
   345  				break
   346  			}
   347  			switch body := msg.Body.(type) {
   348  			case *zebra.IPRouteBody:
   349  				if path := newPathFromIPRouteMessage(msg, z.client.Version); path != nil {
   350  					if err := z.server.addPathList("", []*table.Path{path}); err != nil {
   351  						log.WithFields(log.Fields{
   352  							"Topic": "Zebra",
   353  							"Path":  path,
   354  							"Error": err,
   355  						}).Error("failed to add path from zebra")
   356  					}
   357  				}
   358  			case *zebra.NexthopUpdateBody:
   359  				if updated := z.nexthopCache.updateByNexthopUpdate(body); !updated {
   360  					continue
   361  				}
   362  				paths := z.getPathListWithNexthopUpdate(body)
   363  				if len(paths) == 0 {
   364  					// If there is no path bound for the given nexthop, send
   365  					// NEXTHOP_UNREGISTER message.
   366  					z.client.SendNexthopRegister(msg.Header.VrfId, newNexthopUnregisterBody(uint16(body.Prefix.Family), body.Prefix.Prefix), true)
   367  					delete(z.nexthopCache, body.Prefix.Prefix.String())
   368  				}
   369  				z.updatePathByNexthopCache(paths)
   370  			}
   371  		case ev := <-w.Event():
   372  			switch msg := ev.(type) {
   373  			case *watchEventBestPath:
   374  				if table.UseMultiplePaths.Enabled {
   375  					for _, paths := range msg.MultiPathList {
   376  						z.updatePathByNexthopCache(paths)
   377  						if body, isWithdraw := newIPRouteBody(paths); body != nil {
   378  							z.client.SendIPRoute(0, body, isWithdraw)
   379  						}
   380  						if body := newNexthopRegisterBody(paths, z.nexthopCache); body != nil {
   381  							z.client.SendNexthopRegister(0, body, false)
   382  						}
   383  					}
   384  				} else {
   385  					z.updatePathByNexthopCache(msg.PathList)
   386  					for _, path := range msg.PathList {
   387  						vrfs := []uint32{0}
   388  						if msg.Vrf != nil {
   389  							if v, ok := msg.Vrf[path.GetNlri().String()]; ok {
   390  								vrfs = append(vrfs, v)
   391  							}
   392  						}
   393  						for _, i := range vrfs {
   394  							if body, isWithdraw := newIPRouteBody([]*table.Path{path}); body != nil {
   395  								z.client.SendIPRoute(i, body, isWithdraw)
   396  							}
   397  							if body := newNexthopRegisterBody([]*table.Path{path}, z.nexthopCache); body != nil {
   398  								z.client.SendNexthopRegister(i, body, false)
   399  							}
   400  						}
   401  					}
   402  				}
   403  			case *watchEventUpdate:
   404  				if body := newNexthopRegisterBody(msg.PathList, z.nexthopCache); body != nil {
   405  					vrfID := uint32(0)
   406  					for _, vrf := range z.server.listVrf() {
   407  						if vrf.Name == msg.Neighbor.Config.Vrf {
   408  							vrfID = uint32(vrf.Id)
   409  						}
   410  					}
   411  					z.client.SendNexthopRegister(vrfID, body, false)
   412  				}
   413  			}
   414  		}
   415  	}
   416  }
   417  
   418  func newZebraClient(s *BgpServer, url string, protos []string, version uint8, nhtEnable bool, nhtDelay uint8) (*zebraClient, error) {
   419  	l := strings.SplitN(url, ":", 2)
   420  	if len(l) != 2 {
   421  		return nil, fmt.Errorf("unsupported url: %s", url)
   422  	}
   423  	var cli *zebra.Client
   424  	var err error
   425  	var usingVersion uint8
   426  	var zapivers [zebra.MaxZapiVer - zebra.MinZapiVer + 1]uint8
   427  	zapivers[0] = version
   428  	for elem, ver := 1, zebra.MinZapiVer; elem < len(zapivers) && ver <= zebra.MaxZapiVer; elem++ {
   429  		if version == ver && ver < zebra.MaxZapiVer {
   430  			ver++
   431  		}
   432  		zapivers[elem] = ver
   433  		ver++
   434  	}
   435  	for elem, ver := range zapivers {
   436  		cli, err = zebra.NewClient(l[0], l[1], zebra.ROUTE_BGP, ver)
   437  		if cli != nil && err == nil {
   438  			usingVersion = ver
   439  			break
   440  		}
   441  		// Retry with another Zebra message version
   442  		log.WithFields(log.Fields{
   443  			"Topic": "Zebra",
   444  		}).Warnf("cannot connect to Zebra with message version %d.", ver)
   445  		if elem < len(zapivers)-1 {
   446  			log.WithFields(log.Fields{
   447  				"Topic": "Zebra",
   448  			}).Warnf("going to retry another version %d.", zapivers[elem+1])
   449  		}
   450  	}
   451  	if cli == nil || err != nil {
   452  		return nil, err
   453  	}
   454  	log.WithFields(log.Fields{
   455  		"Topic": "Zebra",
   456  	}).Infof("success to connect to Zebra with message version %d.", usingVersion)
   457  
   458  	// Note: HELLO/ROUTER_ID_ADD messages are automatically sent to negotiate
   459  	// the Zebra message version in zebra.NewClient().
   460  	// cli.SendHello()
   461  	// cli.SendRouterIDAdd()
   462  	cli.SendInterfaceAdd()
   463  	for _, typ := range protos {
   464  		t, err := zebra.RouteTypeFromString(typ, version)
   465  		if err != nil {
   466  			return nil, err
   467  		}
   468  		cli.SendRedistribute(t, zebra.VRF_DEFAULT)
   469  	}
   470  	w := &zebraClient{
   471  		client:       cli,
   472  		server:       s,
   473  		nexthopCache: make(nexthopStateCache),
   474  		dead:         make(chan struct{}),
   475  	}
   476  	go w.loop()
   477  	return w, nil
   478  }