summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--server/zclient.go139
1 files changed, 132 insertions, 7 deletions
diff --git a/server/zclient.go b/server/zclient.go
index 5f81cfcf..622a0c2a 100644
--- a/server/zclient.go
+++ b/server/zclient.go
@@ -24,17 +24,61 @@ import (
"net"
"strconv"
"strings"
+ "syscall"
"time"
)
-func newIPRouteMessage(dst []*table.Path, version uint8, vrfId uint16) *zebra.Message {
- paths := make([]*table.Path, 0, len(dst))
- for _, path := range dst {
+type pathList []*table.Path
+
+type registeredNexthopCache []*net.IP
+
+func newRegisteredNexthopCache() *registeredNexthopCache {
+ cache := make(registeredNexthopCache, 0)
+ return &cache
+}
+
+func (c *registeredNexthopCache) isRegistered(nexthop net.IP) bool {
+ for _, cached := range *c {
+ if cached.Equal(nexthop) {
+ return true
+ }
+ }
+ return false
+}
+
+func (c *registeredNexthopCache) register(nexthop net.IP) bool {
+ cache := *c
+ if c.isRegistered(nexthop) {
+ return false
+ }
+ *c = append(cache, &nexthop)
+ return true
+}
+
+func filterOutNilPath(paths pathList) pathList {
+ filteredPaths := make(pathList, 0, len(paths))
+ for _, path := range paths {
+ if path == nil {
+ continue
+ }
+ filteredPaths = append(filteredPaths, path)
+ }
+ return filteredPaths
+}
+
+func filterOutExternalPath(paths pathList) pathList {
+ filteredPaths := make(pathList, 0, len(paths))
+ for _, path := range paths {
if path == nil || path.IsFromExternal() {
continue
}
- paths = append(paths, path)
+ filteredPaths = append(filteredPaths, path)
}
+ return filteredPaths
+}
+
+func newIPRouteMessage(dst pathList, version uint8, vrfId uint16) *zebra.Message {
+ paths := filterOutExternalPath(dst)
if len(paths) == 0 {
return nil
}
@@ -110,6 +154,80 @@ func newIPRouteMessage(dst []*table.Path, version uint8, vrfId uint16) *zebra.Me
}
}
+func newNexthopRegisterMessage(dst pathList, version uint8, vrfId uint16, nexthopCache *registeredNexthopCache) *zebra.Message {
+ // Note: NEXTHOP_REGISTER and NEXTHOP_UNREGISTER messages are not
+ // supported in Zebra protocol version<3.
+ if version < 3 {
+ return nil
+ }
+
+ paths := filterOutNilPath(dst)
+ if len(paths) == 0 {
+ return nil
+ }
+
+ route_family := paths[0].GetRouteFamily()
+ command := zebra.NEXTHOP_REGISTER
+ if paths[0].IsWithdraw == true {
+ // TODO:
+ // Send NEXTHOP_UNREGISTER message if the given nexthop is no longer
+ // referred by any path. Currently, do not send NEXTHOP_UNREGISTER
+ // message to simplify the implementation.
+ //command = zebra.NEXTHOP_UNREGISTER
+ return nil
+ }
+
+ nexthops := make([]*zebra.RegisteredNexthop, 0, len(paths))
+ for _, p := range paths {
+ nexthop := p.GetNexthop()
+ // Skips to register or unregister the given nexthop
+ // when the nexthop is:
+ // - already registered
+ // - already invalidated
+ // - an unspecified address
+ if nexthopCache.isRegistered(nexthop) || p.IsNexthopInvalid || nexthop.IsUnspecified() {
+ continue
+ }
+
+ var nh *zebra.RegisteredNexthop
+ switch route_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:
+ return nil
+ }
+ nexthops = append(nexthops, nh)
+ nexthopCache.register(nexthop)
+ }
+
+ // If no nexthop needs to be registered or unregistered,
+ // skips to send message.
+ if len(nexthops) == 0 {
+ return nil
+ }
+
+ return &zebra.Message{
+ Header: zebra.Header{
+ Len: zebra.HeaderSize(version),
+ Marker: zebra.HEADER_MARKER,
+ Version: version,
+ Command: command,
+ VrfId: vrfId,
+ },
+ Body: &zebra.NexthopRegisterBody{
+ Nexthops: nexthops,
+ },
+ }
+}
+
func createPathFromIPRouteMessage(m *zebra.Message) *table.Path {
header := m.Header
@@ -180,6 +298,8 @@ func (z *zebraClient) loop() {
w := z.server.Watch(WatchBestPath(true))
defer func() { w.Stop() }()
+ nexthopCache := newRegisteredNexthopCache()
+
for {
select {
case <-z.dead:
@@ -188,7 +308,7 @@ func (z *zebraClient) loop() {
switch msg.Body.(type) {
case *zebra.IPRouteBody:
if p := createPathFromIPRouteMessage(msg); p != nil {
- if _, err := z.server.AddPath("", []*table.Path{p}); err != nil {
+ if _, err := z.server.AddPath("", pathList{p}); err != nil {
log.Errorf("failed to add path from zebra: %s", p)
}
}
@@ -200,15 +320,20 @@ func (z *zebraClient) loop() {
if m := newIPRouteMessage(dst, z.client.Version, 0); m != nil {
z.client.Send(m)
}
+ if m := newNexthopRegisterMessage(dst, z.client.Version, 0, nexthopCache); m != nil {
+ z.client.Send(m)
+ }
}
} else {
for _, path := range msg.PathList {
if len(path.VrfIds) == 0 {
path.VrfIds = []uint16{0}
}
-
for _, i := range path.VrfIds {
- if m := newIPRouteMessage([]*table.Path{path}, z.client.Version, i); m != nil {
+ if m := newIPRouteMessage(pathList{path}, z.client.Version, i); m != nil {
+ z.client.Send(m)
+ }
+ if m := newNexthopRegisterMessage(pathList{path}, z.client.Version, i, nexthopCache); m != nil {
z.client.Send(m)
}
}