summaryrefslogtreecommitdiffhomepage
path: root/server/zclient.go
diff options
context:
space:
mode:
Diffstat (limited to 'server/zclient.go')
-rw-r--r--server/zclient.go450
1 files changed, 0 insertions, 450 deletions
diff --git a/server/zclient.go b/server/zclient.go
deleted file mode 100644
index 37281f7f..00000000
--- a/server/zclient.go
+++ /dev/null
@@ -1,450 +0,0 @@
-// Copyright (C) 2015 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 server
-
-import (
- "fmt"
- "math"
- "net"
- "strconv"
- "strings"
- "syscall"
- "time"
-
- log "github.com/sirupsen/logrus"
-
- "github.com/osrg/gobgp/packet/bgp"
- "github.com/osrg/gobgp/table"
- "github.com/osrg/gobgp/zebra"
-)
-
-// nexthopStateCache stores a map of nexthop IP to metric value. Especially,
-// the metric value of math.MaxUint32 means the nexthop is unreachable.
-type nexthopStateCache map[string]uint32
-
-func (m nexthopStateCache) applyToPathList(paths []*table.Path) []*table.Path {
- updated := make([]*table.Path, 0, len(paths))
- for _, path := range paths {
- if path == nil || path.IsWithdraw {
- continue
- }
- metric, ok := m[path.GetNexthop().String()]
- if !ok {
- continue
- }
- isNexthopInvalid := metric == math.MaxUint32
- med, err := path.GetMed()
- if err == nil && med == metric && path.IsNexthopInvalid == isNexthopInvalid {
- // If the nexthop state of the given path is already up to date,
- // skips this path.
- continue
- }
- newPath := path.Clone(false)
- if isNexthopInvalid {
- newPath.IsNexthopInvalid = true
- } else {
- newPath.IsNexthopInvalid = false
- newPath.SetMed(int64(metric), true)
- }
- updated = append(updated, newPath)
- }
- return updated
-}
-
-func (m nexthopStateCache) updateByNexthopUpdate(body *zebra.NexthopUpdateBody) (updated bool) {
- if len(body.Nexthops) == 0 {
- // If NEXTHOP_UPDATE message does not contain any nexthop, the given
- // nexthop is unreachable.
- if _, ok := m[body.Prefix.String()]; !ok {
- // Zebra will send an empty NEXTHOP_UPDATE message as the fist
- // response for the NEXTHOP_REGISTER message. Here ignores it.
- return false
- }
- m[body.Prefix.String()] = math.MaxUint32 // means unreachable
- } else {
- m[body.Prefix.String()] = body.Metric
- }
- return true
-}
-
-func (m nexthopStateCache) filterPathToRegister(paths []*table.Path) []*table.Path {
- filteredPaths := make([]*table.Path, 0, len(paths))
- for _, path := range paths {
- // Here filters out:
- // - Nil path
- // - Withdrawn path
- // - External path (advertised from Zebra) in order avoid sending back
- // - Unspecified nexthop address
- // - Already registered nexthop
- if path == nil || path.IsWithdraw || path.IsFromExternal() {
- continue
- } else if nexthop := path.GetNexthop(); nexthop.IsUnspecified() {
- continue
- } else if _, ok := m[nexthop.String()]; ok {
- continue
- }
- filteredPaths = append(filteredPaths, path)
- }
- return filteredPaths
-}
-
-func filterOutExternalPath(paths []*table.Path) []*table.Path {
- filteredPaths := make([]*table.Path, 0, len(paths))
- for _, path := range paths {
- // Here filters out:
- // - Nil path
- // - External path (advertised from Zebra) in order avoid sending back
- // - Unreachable path because invalidated by Zebra
- if path == nil || path.IsFromExternal() || path.IsNexthopInvalid {
- continue
- }
- filteredPaths = append(filteredPaths, path)
- }
- return filteredPaths
-}
-
-func newIPRouteBody(dst []*table.Path) (body *zebra.IPRouteBody, isWithdraw bool) {
- paths := filterOutExternalPath(dst)
- if len(paths) == 0 {
- return nil, false
- }
- path := paths[0]
-
- l := strings.SplitN(path.GetNlri().String(), "/", 2)
- var prefix net.IP
- nexthops := make([]net.IP, 0, len(paths))
- switch path.GetRouteFamily() {
- case bgp.RF_IPv4_UC, bgp.RF_IPv4_VPN:
- if path.GetRouteFamily() == bgp.RF_IPv4_UC {
- prefix = path.GetNlri().(*bgp.IPAddrPrefix).IPAddrPrefixDefault.Prefix.To4()
- } else {
- prefix = path.GetNlri().(*bgp.LabeledVPNIPAddrPrefix).IPAddrPrefixDefault.Prefix.To4()
- }
- for _, p := range paths {
- nexthops = append(nexthops, p.GetNexthop().To4())
- }
- case bgp.RF_IPv6_UC, bgp.RF_IPv6_VPN:
- if path.GetRouteFamily() == bgp.RF_IPv6_UC {
- prefix = path.GetNlri().(*bgp.IPv6AddrPrefix).IPAddrPrefixDefault.Prefix.To16()
- } else {
- prefix = path.GetNlri().(*bgp.LabeledVPNIPv6AddrPrefix).IPAddrPrefixDefault.Prefix.To16()
- }
- for _, p := range paths {
- nexthops = append(nexthops, p.GetNexthop().To16())
- }
- default:
- return nil, false
- }
- msgFlags := zebra.MESSAGE_NEXTHOP
- plen, _ := strconv.ParseUint(l[1], 10, 8)
- med, err := path.GetMed()
- if err == nil {
- msgFlags |= zebra.MESSAGE_METRIC
- }
- var flags zebra.FLAG
- info := path.GetSource()
- if info.AS == info.LocalAS {
- flags = zebra.FLAG_IBGP | zebra.FLAG_INTERNAL
- } else if info.MultihopTtl > 0 {
- flags = zebra.FLAG_INTERNAL
- }
- return &zebra.IPRouteBody{
- Type: zebra.ROUTE_BGP,
- Flags: flags,
- SAFI: zebra.SAFI_UNICAST,
- Message: msgFlags,
- Prefix: prefix,
- PrefixLength: uint8(plen),
- Nexthops: nexthops,
- Metric: med,
- }, path.IsWithdraw
-}
-
-func newNexthopRegisterBody(paths []*table.Path, nexthopCache nexthopStateCache) *zebra.NexthopRegisterBody {
- paths = nexthopCache.filterPathToRegister(paths)
- if len(paths) == 0 {
- return nil
- }
- path := paths[0]
-
- family := path.GetRouteFamily()
- nexthops := make([]*zebra.RegisteredNexthop, 0, len(paths))
- for _, p := range paths {
- nexthop := p.GetNexthop()
- var nh *zebra.RegisteredNexthop
- switch family {
- case bgp.RF_IPv4_UC, bgp.RF_IPv4_VPN:
- nh = &zebra.RegisteredNexthop{
- Family: syscall.AF_INET,
- Prefix: nexthop.To4(),
- }
- case bgp.RF_IPv6_UC, bgp.RF_IPv6_VPN:
- nh = &zebra.RegisteredNexthop{
- Family: syscall.AF_INET6,
- Prefix: nexthop.To16(),
- }
- default:
- continue
- }
- nexthops = append(nexthops, nh)
- }
-
- // If no nexthop needs to be registered or unregistered, skips to send
- // message.
- if len(nexthops) == 0 {
- return nil
- }
-
- return &zebra.NexthopRegisterBody{
- Nexthops: nexthops,
- }
-}
-
-func newNexthopUnregisterBody(family uint16, prefix net.IP) *zebra.NexthopRegisterBody {
- return &zebra.NexthopRegisterBody{
- Nexthops: []*zebra.RegisteredNexthop{{
- Family: family,
- Prefix: prefix,
- }},
- }
-}
-
-func newPathFromIPRouteMessage(m *zebra.Message) *table.Path {
- header := m.Header
- body := m.Body.(*zebra.IPRouteBody)
- family := body.RouteFamily()
- isWithdraw := body.IsWithdraw()
-
- var nlri bgp.AddrPrefixInterface
- pattr := make([]bgp.PathAttributeInterface, 0)
- origin := bgp.NewPathAttributeOrigin(bgp.BGP_ORIGIN_ATTR_TYPE_IGP)
- pattr = append(pattr, origin)
-
- log.WithFields(log.Fields{
- "Topic": "Zebra",
- "RouteType": body.Type.String(),
- "Flag": body.Flags.String(),
- "Message": body.Message,
- "Prefix": body.Prefix,
- "PrefixLength": body.PrefixLength,
- "Nexthop": body.Nexthops,
- "IfIndex": body.Ifindexs,
- "Metric": body.Metric,
- "Distance": body.Distance,
- "Mtu": body.Mtu,
- "api": header.Command.String(),
- }).Debugf("create path from ip route message.")
-
- switch family {
- case bgp.RF_IPv4_UC:
- nlri = bgp.NewIPAddrPrefix(body.PrefixLength, body.Prefix.String())
- if len(body.Nexthops) > 0 {
- pattr = append(pattr, bgp.NewPathAttributeNextHop(body.Nexthops[0].String()))
- }
- case bgp.RF_IPv6_UC:
- nlri = bgp.NewIPv6AddrPrefix(body.PrefixLength, body.Prefix.String())
- nexthop := ""
- if len(body.Nexthops) > 0 {
- nexthop = body.Nexthops[0].String()
- }
- pattr = append(pattr, bgp.NewPathAttributeMpReachNLRI(nexthop, []bgp.AddrPrefixInterface{nlri}))
- default:
- log.WithFields(log.Fields{
- "Topic": "Zebra",
- }).Errorf("unsupport address family: %s", family)
- return nil
- }
-
- med := bgp.NewPathAttributeMultiExitDisc(body.Metric)
- pattr = append(pattr, med)
-
- path := table.NewPath(nil, nlri, isWithdraw, pattr, time.Now(), false)
- path.SetIsFromExternal(true)
- return path
-}
-
-type zebraClient struct {
- client *zebra.Client
- server *BgpServer
- nexthopCache nexthopStateCache
- dead chan struct{}
-}
-
-func (z *zebraClient) getPathListWithNexthopUpdate(body *zebra.NexthopUpdateBody) []*table.Path {
- rib := &table.TableManager{
- Tables: make(map[bgp.RouteFamily]*table.Table),
- }
-
- var rfList []bgp.RouteFamily
- switch body.Family {
- case uint16(syscall.AF_INET):
- rfList = []bgp.RouteFamily{bgp.RF_IPv4_UC, bgp.RF_IPv4_VPN}
- case uint16(syscall.AF_INET6):
- rfList = []bgp.RouteFamily{bgp.RF_IPv6_UC, bgp.RF_IPv6_VPN}
- }
-
- for _, rf := range rfList {
- tbl, _, err := z.server.GetRib("", rf, nil)
- if err != nil {
- log.WithFields(log.Fields{
- "Topic": "Zebra",
- "Family": rf.String(),
- "Error": err,
- }).Error("failed to get global rib")
- continue
- }
- rib.Tables[rf] = tbl
- }
-
- return rib.GetPathListWithNexthop(table.GLOBAL_RIB_NAME, rfList, body.Prefix)
-}
-
-func (z *zebraClient) updatePathByNexthopCache(paths []*table.Path) {
- paths = z.nexthopCache.applyToPathList(paths)
- if len(paths) > 0 {
- if err := z.server.UpdatePath("", paths); err != nil {
- log.WithFields(log.Fields{
- "Topic": "Zebra",
- "PathList": paths,
- }).Error("failed to update nexthop reachability")
- }
- }
-}
-
-func (z *zebraClient) loop() {
- w := z.server.Watch([]WatchOption{
- WatchBestPath(true),
- WatchPostUpdate(true),
- }...)
- defer w.Stop()
-
- for {
- select {
- case <-z.dead:
- return
- case msg := <-z.client.Receive():
- switch body := msg.Body.(type) {
- case *zebra.IPRouteBody:
- if path := newPathFromIPRouteMessage(msg); path != nil {
- if _, err := z.server.AddPath("", []*table.Path{path}); err != nil {
- log.WithFields(log.Fields{
- "Topic": "Zebra",
- "Path": path,
- "Error": err,
- }).Error("failed to add path from zebra")
- }
- }
- case *zebra.NexthopUpdateBody:
- if updated := z.nexthopCache.updateByNexthopUpdate(body); !updated {
- continue
- }
- paths := z.getPathListWithNexthopUpdate(body)
- if len(paths) == 0 {
- // If there is no path bound for the given nexthop, send
- // NEXTHOP_UNREGISTER message.
- z.client.SendNexthopRegister(msg.Header.VrfId, newNexthopUnregisterBody(body.Family, body.Prefix), true)
- delete(z.nexthopCache, body.Prefix.String())
- }
- z.updatePathByNexthopCache(paths)
- }
- case ev := <-w.Event():
- switch msg := ev.(type) {
- case *WatchEventBestPath:
- if table.UseMultiplePaths.Enabled {
- for _, paths := range msg.MultiPathList {
- z.updatePathByNexthopCache(paths)
- if body, isWithdraw := newIPRouteBody(paths); body != nil {
- z.client.SendIPRoute(0, body, isWithdraw)
- }
- if body := newNexthopRegisterBody(paths, z.nexthopCache); body != nil {
- z.client.SendNexthopRegister(0, body, false)
- }
- }
- } else {
- z.updatePathByNexthopCache(msg.PathList)
- for _, path := range msg.PathList {
- vrfs := []uint16{0}
- if msg.Vrf != nil {
- if v, ok := msg.Vrf[path.GetNlri().String()]; ok {
- vrfs = append(vrfs, v)
- }
- }
- for _, i := range vrfs {
- if body, isWithdraw := newIPRouteBody([]*table.Path{path}); body != nil {
- z.client.SendIPRoute(i, body, isWithdraw)
- }
- if body := newNexthopRegisterBody([]*table.Path{path}, z.nexthopCache); body != nil {
- z.client.SendNexthopRegister(i, body, false)
- }
- }
- }
- }
- case *WatchEventUpdate:
- if body := newNexthopRegisterBody(msg.PathList, z.nexthopCache); body != nil {
- vrfID := uint16(0)
- for _, vrf := range z.server.GetVrf() {
- if vrf.Name == msg.Neighbor.Config.Vrf {
- vrfID = uint16(vrf.Id)
- }
- }
- z.client.SendNexthopRegister(vrfID, body, false)
- }
- }
- }
- }
-}
-
-func newZebraClient(s *BgpServer, url string, protos []string, version uint8, nhtEnable bool, nhtDelay uint8) (*zebraClient, error) {
- l := strings.SplitN(url, ":", 2)
- if len(l) != 2 {
- return nil, fmt.Errorf("unsupported url: %s", url)
- }
- var cli *zebra.Client
- var err error
- for _, ver := range []uint8{version, 2, 3, 4} {
- cli, err = zebra.NewClient(l[0], l[1], zebra.ROUTE_BGP, ver)
- if err == nil {
- break
- }
- // Retry with another Zebra message version
- log.WithFields(log.Fields{
- "Topic": "Zebra",
- }).Warnf("cannot connect to Zebra with message version %d. going to retry another version...", ver)
- }
- if cli == nil {
- return nil, err
- }
- // Note: HELLO/ROUTER_ID_ADD messages are automatically sent to negotiate
- // the Zebra message version in zebra.NewClient().
- // cli.SendHello()
- // cli.SendRouterIDAdd()
- cli.SendInterfaceAdd()
- for _, typ := range protos {
- t, err := zebra.RouteTypeFromString(typ)
- if err != nil {
- return nil, err
- }
- cli.SendRedistribute(t, zebra.VRF_DEFAULT)
- }
- w := &zebraClient{
- client: cli,
- server: s,
- nexthopCache: make(nexthopStateCache),
- dead: make(chan struct{}),
- }
- go w.loop()
- return w, nil
-}