2018-09-11 08:13:54 +05:30

451 lines
13 KiB
Go

// 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
}