Skip to content
Snippets Groups Projects
Commit 8f926bba authored by takt's avatar takt Committed by Daniel Czerwonk
Browse files

Refactor Netlink into FIB and Netlink module (#177)

parent 0fc25fd7
Branches isissrv
Tags
No related merge requests found
......@@ -7,7 +7,7 @@ import (
"github.com/bio-routing/bio-rd/config"
"github.com/bio-routing/bio-rd/protocols/bgp/server"
"github.com/bio-routing/bio-rd/protocols/netlink"
"github.com/bio-routing/bio-rd/protocols/fib"
"github.com/bio-routing/bio-rd/routingtable/locRIB"
log "github.com/sirupsen/logrus"
......@@ -43,8 +43,8 @@ func main() {
b := server.NewBgpServer()
startBGPServer(b, rib, cfg)
// Netlink communication
n := protocolnetlink.NewNetlink(&config.Netlink{
// FIB communication
n := fib.NewFIB(&config.Netlink{
HoldTime: time.Second * 15,
UpdateInterval: time.Second * 15,
RoutingTable: config.RtMain,
......
File moved
File moved
......@@ -10,7 +10,7 @@ require (
github.com/vishvananda/netlink v1.0.0
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc // indirect
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 // indirect
golang.org/x/net v0.0.0-20181220203305-927f97764cc3 // indirect
golang.org/x/net v0.0.0-20181220203305-927f97764cc3
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 // indirect
golang.org/x/sys v0.0.0-20181228120256-c6cbdbf9e68a // indirect
google.golang.org/genproto v0.0.0-20181221175505-bd9b4fb69e2f // indirect
......
......@@ -7,9 +7,9 @@ import (
)
func (ds *Server) loadAdapter() error {
a, err := newOSAdapterLinux(ds)
a, err := newOSAdapterDarwin(ds)
if err != nil {
return errors.Wrap(err, "Unable to create linux adapter")
return errors.Wrap(err, "Unable to create OS X adapter")
}
ds.osAdapter = a
......@@ -19,7 +19,7 @@ func (ds *Server) loadAdapter() error {
type osAdapterDarwin struct {
}
func newOSAdapterLinux(srv *Server) (*osAdapterDarwin, error) {
func newOSAdapterDarwin(srv *Server) (*osAdapterDarwin, error) {
return nil, fmt.Errorf("Not implemented")
}
......
package protocolnetlink
package fib
import (
"github.com/bio-routing/bio-rd/config"
"github.com/bio-routing/bio-rd/routingtable"
"github.com/bio-routing/bio-rd/routingtable/locRIB"
)
// Netlink is the netlink module which handles the entire NetlinkCommunication
type Netlink struct {
// FIB is forwarding information base
type FIB struct {
locRib *locRIB.LocRIB
writer *NetlinkWriter
reader *NetlinkReader
//writer *NetlinkWriter
//reader *NetlinkReader
}
// NewNetlink creates a new Netlink object and returns the pointer to it
func NewNetlink(options *config.Netlink, locRib *locRIB.LocRIB) *Netlink {
// NewFIB creates a new Netlink object and returns the pointer to it
func NewFIB(options *config.Netlink, locRib *locRIB.LocRIB) *FIB {
n := &Netlink{
n := &FIB{
locRib: locRib,
writer: NewNetlinkWriter(options),
reader: NewNetlinkReader(options),
//writer: NewNetlinkWriter(options),
//reader: NewNetlinkReader(options),
}
return n
}
// Start the Netlink module
func (n *Netlink) Start() {
func (f *FIB) Start() {
// connect all RIBs
options := routingtable.ClientOptions{
/*options := routingtable.ClientOptions{
BestOnly: false,
EcmpOnly: false,
MaxPaths: ^uint(0), // max int
}
}*/
// 1. from locRib to Kernel
n.locRib.RegisterWithOptions(n.writer, options)
//f.locRib.RegisterWithOptions(n.writer, options)
// 2. from Kernel to locRib
n.reader.clientManager.RegisterWithOptions(n.locRib, options)
//f.reader.clientManager.RegisterWithOptions(n.locRib, options)
// Listen for new routes from kernel
go n.reader.Read()
//go n.reader.Read()
}
package route
package fib
import (
"fmt"
bnet "github.com/bio-routing/bio-rd/net"
"github.com/bio-routing/bio-rd/route"
"github.com/vishvananda/netlink"
)
......@@ -19,29 +20,56 @@ const (
ProtoBio = 45 // bio
)
// NetlinkPath represents a path learned via Netlink of a route
type NetlinkPath struct {
Src bnet.IP
NextHop bnet.IP // GW
Priority int
Protocol int
Type int
Table int
Kernel bool // True if the route is already installed in the kernel
// NetlinkRouteDiff gets the list of elements contained by a but not b
func NetlinkRouteDiff(a, b []netlink.Route) []netlink.Route {
ret := make([]netlink.Route, 0)
for _, pa := range a {
if !netlinkRoutesContains(pa, b) {
ret = append(ret, pa)
}
}
return ret
}
// NewNlPathFromBgpPath creates a new NetlinkPath object from a BGPPath object
func NewNlPathFromBgpPath(p *BGPPath) *NetlinkPath {
return &NetlinkPath{
Src: p.Source,
NextHop: p.NextHop,
Protocol: ProtoBio,
Kernel: false,
func netlinkRoutesContains(needle netlink.Route, haystack []netlink.Route) bool {
for i := range haystack {
if netlinkRouteEquals(&needle, &haystack[i]) {
return true
}
}
return false
}
func netlinkRouteEquals(a, b *netlink.Route) bool {
aMaskSize, aMaskBits := a.Dst.Mask.Size()
bMaskSize, bMaskBits := b.Dst.Mask.Size()
return a.LinkIndex == b.LinkIndex &&
a.ILinkIndex == b.ILinkIndex &&
a.Scope == b.Scope &&
a.Dst.IP.Equal(b.Dst.IP) &&
aMaskSize == bMaskSize &&
aMaskBits == bMaskBits &&
a.Src.Equal(b.Src) &&
a.Gw.Equal(b.Gw) &&
a.Protocol == b.Protocol &&
a.Priority == b.Priority &&
a.Table == b.Table &&
a.Type == b.Type &&
a.Tos == b.Tos &&
a.Flags == b.Flags &&
a.MTU == b.MTU &&
a.AdvMSS == b.AdvMSS
}
// NewNlPathFromRoute creates a new NetlinkPath object from a netlink.Route object
func NewPathsFromNlRoute(r netlink.Route, kernel bool) (bnet.Prefix, []*Path, error) {
// NewNlPathFromRoute creates a new route.FIBPath object from a netlink.Route object
func NewPathsFromNlRoute(r netlink.Route, kernel bool) (bnet.Prefix, []*route.Path, error) {
var src bnet.IP
var dst bnet.Prefix
......@@ -72,14 +100,14 @@ func NewPathsFromNlRoute(r netlink.Route, kernel bool) (bnet.Prefix, []*Path, er
dst = bnet.NewPfxFromIPNet(r.Dst)
}
paths := make([]*Path, 0)
paths := make([]*route.Path, 0)
if len(r.MultiPath) > 0 {
for _, multiPath := range r.MultiPath {
nextHop, _ := bnet.IPFromBytes(multiPath.Gw)
paths = append(paths, &Path{
Type: NetlinkPathType,
NetlinkPath: &NetlinkPath{
paths = append(paths, &route.Path{
Type: route.FIBPathType,
FIBPath: &route.FIBPath{
Src: src,
NextHop: nextHop,
Priority: r.Priority,
......@@ -92,9 +120,9 @@ func NewPathsFromNlRoute(r netlink.Route, kernel bool) (bnet.Prefix, []*Path, er
}
} else {
nextHop, _ := bnet.IPFromBytes(r.Gw)
paths = append(paths, &Path{
Type: NetlinkPathType,
NetlinkPath: &NetlinkPath{
paths = append(paths, &route.Path{
Type: route.FIBPathType,
FIBPath: &route.FIBPath{
Src: src,
NextHop: nextHop,
Priority: r.Priority,
......@@ -108,85 +136,3 @@ func NewPathsFromNlRoute(r netlink.Route, kernel bool) (bnet.Prefix, []*Path, er
return dst, paths, nil
}
// Select compares s with t and returns negative if s < t, 0 if paths are equal, positive if s > t
func (s *NetlinkPath) Select(t *NetlinkPath) int8 {
if s.NextHop.Compare(t.NextHop) < 0 {
return -1
}
if s.NextHop.Compare(t.NextHop) > 0 {
return 1
}
if s.Src.Compare(t.Src) < 0 {
return -1
}
if s.Src.Compare(t.Src) > 0 {
return 1
}
if s.Priority < t.Priority {
return -1
}
if s.Priority > t.Priority {
return 1
}
if s.Protocol < t.Protocol {
return -1
}
if s.Protocol > t.Protocol {
return 1
}
if s.Table < t.Table {
return -1
}
if s.Table > t.Table {
return 1
}
return 0
}
// ECMP determines if path s and t are equal in terms of ECMP
func (s *NetlinkPath) ECMP(t *NetlinkPath) bool {
return s.Src == t.Src && s.Priority == t.Priority && s.Protocol == t.Protocol && s.Type == t.Type && s.Table == t.Table
}
// Copy duplicates the current object
func (s *NetlinkPath) Copy() *NetlinkPath {
if s == nil {
return nil
}
cp := *s
return &cp
}
// Print all known information about a route in logfile friendly format
func (s *NetlinkPath) String() string {
ret := fmt.Sprintf("Source: %s, ", s.Src.String())
ret += fmt.Sprintf("NextHop: %s, ", s.NextHop.String())
ret += fmt.Sprintf("Priority: %d, ", s.Priority)
ret += fmt.Sprintf("Type: %d, ", s.Type)
ret += fmt.Sprintf("Table: %d", s.Table)
return ret
}
// Print all known information about a route in human readable form
func (s *NetlinkPath) Print() string {
ret := fmt.Sprintf("\t\tSource: %s\n", s.Src.String())
ret += fmt.Sprintf("\t\tNextHop: %s\n", s.NextHop.String())
ret += fmt.Sprintf("\t\tPriority: %d\n", s.Priority)
ret += fmt.Sprintf("\t\tType: %d\n", s.Type)
ret += fmt.Sprintf("\t\tTable: %d\n", s.Table)
return ret
}
package fib
import (
"net"
"testing"
bnet "github.com/bio-routing/bio-rd/net"
"github.com/bio-routing/bio-rd/route"
"github.com/stretchr/testify/assert"
"github.com/vishvananda/netlink"
)
func TestNetlinkRouteDiff(t *testing.T) {
tests := []struct {
name string
left []netlink.Route
right []netlink.Route
expected []netlink.Route
}{
{
name: "Equal",
left: []netlink.Route{
{
Dst: &net.IPNet{
IP: net.IPv4(10, 0, 0, 1),
Mask: net.IPv4Mask(255, 0, 0, 0),
},
Table: 1,
},
{
Dst: &net.IPNet{
IP: net.IPv4(20, 0, 0, 1),
Mask: net.IPv4Mask(255, 0, 0, 0),
},
Table: 2,
},
},
right: []netlink.Route{
{
Dst: &net.IPNet{
IP: net.IPv4(10, 0, 0, 1),
Mask: net.IPv4Mask(255, 0, 0, 0),
},
Table: 1,
},
{
Dst: &net.IPNet{
IP: net.IPv4(20, 0, 0, 1),
Mask: net.IPv4Mask(255, 0, 0, 0),
},
Table: 2,
},
},
expected: []netlink.Route{},
}, {
name: "Left empty",
left: []netlink.Route{},
right: []netlink.Route{
{
Dst: &net.IPNet{
IP: net.IPv4(10, 0, 0, 1),
Mask: net.IPv4Mask(255, 0, 0, 0),
},
Table: 1,
},
{
Dst: &net.IPNet{
IP: net.IPv4(20, 0, 0, 1),
Mask: net.IPv4Mask(255, 0, 0, 0),
},
Table: 2,
},
},
expected: []netlink.Route{},
}, {
name: "Right empty",
left: []netlink.Route{
{
Dst: &net.IPNet{
IP: net.IPv4(10, 0, 0, 1),
Mask: net.IPv4Mask(255, 0, 0, 0),
},
Table: 1,
},
{
Dst: &net.IPNet{
IP: net.IPv4(20, 0, 0, 1),
Mask: net.IPv4Mask(255, 0, 0, 0),
},
Table: 2,
},
},
right: []netlink.Route{},
expected: []netlink.Route{
{
Dst: &net.IPNet{
IP: net.IPv4(10, 0, 0, 1),
Mask: net.IPv4Mask(255, 0, 0, 0),
},
Table: 1,
},
{
Dst: &net.IPNet{
IP: net.IPv4(20, 0, 0, 1),
Mask: net.IPv4Mask(255, 0, 0, 0),
},
Table: 2,
},
},
}, {
name: "Diff",
left: []netlink.Route{
{
Dst: &net.IPNet{
IP: net.IPv4(10, 0, 0, 1),
Mask: net.IPv4Mask(255, 0, 0, 0),
},
Table: 1,
},
{
Dst: &net.IPNet{
IP: net.IPv4(20, 0, 0, 1),
Mask: net.IPv4Mask(255, 0, 0, 0),
},
Table: 2,
},
},
right: []netlink.Route{
{
Dst: &net.IPNet{
IP: net.IPv4(10, 0, 0, 1),
Mask: net.IPv4Mask(255, 0, 0, 0),
},
Table: 1,
},
},
expected: []netlink.Route{
{
Dst: &net.IPNet{
IP: net.IPv4(20, 0, 0, 1),
Mask: net.IPv4Mask(255, 0, 0, 0),
},
Table: 2,
},
},
},
}
for _, test := range tests {
res := NetlinkRouteDiff(test.left, test.right)
assert.Equal(t, test.expected, res)
}
}
func TestNewPathsFromNetlinkRoute(t *testing.T) {
tests := []struct {
name string
source netlink.Route
expectedPfx bnet.Prefix
expectedPaths []*route.Path
expectError bool
}{
{
name: "Simple",
source: netlink.Route{
Dst: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8).GetIPNet(),
Src: bnet.IPv4(456).Bytes(),
Gw: bnet.IPv4(789).Bytes(),
Protocol: ProtoKernel,
Priority: 1,
Table: 254,
Type: 1,
},
expectedPfx: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8),
expectedPaths: []*route.Path{
{
Type: route.FIBPathType,
FIBPath: &route.FIBPath{
Src: bnet.IPv4(456),
NextHop: bnet.IPv4(789),
Protocol: ProtoKernel,
Priority: 1,
Table: 254,
Type: 1,
Kernel: true,
},
},
},
expectError: false,
},
{
name: "Multiple nexthop",
source: netlink.Route{
Dst: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8).GetIPNet(),
Src: bnet.IPv4(456).Bytes(),
MultiPath: []*netlink.NexthopInfo{
{
LinkIndex: 1,
Hops: 1,
Gw: bnet.IPv4(123).Bytes(),
Flags: 0,
NewDst: nil,
Encap: nil,
}, {
LinkIndex: 2,
Hops: 1,
Gw: bnet.IPv4(345).Bytes(),
Flags: 0,
NewDst: nil,
Encap: nil,
},
},
Protocol: ProtoKernel,
Priority: 1,
Table: 254,
Type: 1,
},
expectedPfx: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8),
expectedPaths: []*route.Path{
{
Type: route.FIBPathType,
FIBPath: &route.FIBPath{
Src: bnet.IPv4(456),
NextHop: bnet.IPv4(123),
Protocol: ProtoKernel,
Priority: 1,
Table: 254,
Type: 1,
Kernel: true,
},
}, {
Type: route.FIBPathType,
FIBPath: &route.FIBPath{
Src: bnet.IPv4(456),
NextHop: bnet.IPv4(345),
Protocol: ProtoKernel,
Priority: 1,
Table: 254,
Type: 1,
Kernel: true,
},
},
},
expectError: false,
},
{
name: "No source but destination",
source: netlink.Route{
Dst: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8).GetIPNet(),
Gw: bnet.IPv4(789).Bytes(),
Protocol: ProtoKernel,
Priority: 1,
Table: 254,
Type: 1,
},
expectedPfx: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8),
expectedPaths: []*route.Path{
{
Type: route.FIBPathType,
FIBPath: &route.FIBPath{
Src: bnet.IPv4(0),
NextHop: bnet.IPv4(789),
Protocol: ProtoKernel,
Priority: 1,
Table: 254,
Type: 1,
Kernel: true,
},
},
},
expectError: false,
},
{
name: "Source but no destination",
source: netlink.Route{
Src: bnet.IPv4(456).Bytes(),
Gw: bnet.IPv4(789).Bytes(),
Protocol: ProtoKernel,
Priority: 1,
Table: 254,
Type: 1,
},
expectedPfx: bnet.NewPfx(bnet.IPv4FromOctets(0, 0, 0, 0), 0),
expectedPaths: []*route.Path{
{
Type: route.FIBPathType,
FIBPath: &route.FIBPath{
Src: bnet.IPv4(456),
NextHop: bnet.IPv4(789),
Protocol: ProtoKernel,
Priority: 1,
Table: 254,
Type: 1,
Kernel: true,
},
},
},
expectError: false,
},
{
name: "No source but no destination",
source: netlink.Route{
Gw: bnet.IPv4(789).Bytes(),
Protocol: ProtoKernel,
Priority: 1,
Table: 254,
Type: 1,
},
expectedPfx: bnet.Prefix{},
expectedPaths: []*route.Path{},
expectError: true,
},
{
name: "No source but destination IPv6",
source: netlink.Route{
Dst: bnet.NewPfx(bnet.IPv6(2001, 0), 48).GetIPNet(),
Gw: bnet.IPv6(2001, 123).Bytes(),
Protocol: ProtoKernel,
Priority: 1,
Table: 254,
Type: 1,
},
expectedPfx: bnet.NewPfx(bnet.IPv6(2001, 0), 48),
expectedPaths: []*route.Path{
{
Type: route.FIBPathType,
FIBPath: &route.FIBPath{
Src: bnet.IPv6(0, 0),
NextHop: bnet.IPv6(2001, 123),
Protocol: ProtoKernel,
Priority: 1,
Table: 254,
Type: 1,
Kernel: true,
},
},
},
expectError: false,
},
{
name: "Source but no destination IPv6",
source: netlink.Route{
Src: bnet.IPv6(2001, 456).Bytes(),
Gw: bnet.IPv6(2001, 789).Bytes(),
Protocol: ProtoKernel,
Priority: 1,
Table: 254,
Type: 1,
},
expectedPfx: bnet.NewPfx(bnet.IPv6(0, 0), 0),
expectedPaths: []*route.Path{
{
Type: route.FIBPathType,
FIBPath: &route.FIBPath{
Src: bnet.IPv6(2001, 456),
NextHop: bnet.IPv6(2001, 789),
Protocol: ProtoKernel,
Priority: 1,
Table: 254,
Type: 1,
Kernel: true,
},
},
},
expectError: false,
},
{
name: "no source no destination",
source: netlink.Route{
Gw: bnet.IPv4(123).Bytes(),
Protocol: ProtoKernel,
Priority: 1,
Table: 254,
Type: 1,
},
expectedPfx: bnet.NewPfx(bnet.IPv4(0), 0),
expectedPaths: []*route.Path{{}},
expectError: true,
},
}
for _, test := range tests {
pfx, paths, err := NewPathsFromNlRoute(test.source, true)
if test.expectError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
assert.Equalf(t, test.expectedPaths, paths, test.name)
assert.Equalf(t, test.expectedPfx, pfx, test.name)
}
}
}
package protocolnetlink
package fib
import (
"fmt"
......@@ -59,7 +59,7 @@ func (nr *NetlinkReader) Read() {
nr.mu.Lock()
nr.routes = routes
log.Debugf("NetlinkRouteDiff: %d", len(route.NetlinkRouteDiff(nr.routes, routes)))
log.Debugf("NetlinkRouteDiff: %d", len(NetlinkRouteDiff(nr.routes, routes)))
nr.mu.Unlock()
time.Sleep(nr.options.UpdateInterval)
......@@ -81,7 +81,7 @@ func (nr *NetlinkReader) addPathsToClients(routes []netlink.Route) {
// only advertise changed routes
nr.mu.RLock()
advertise := route.NetlinkRouteDiff(routes, nr.routes)
advertise := NetlinkRouteDiff(routes, nr.routes)
nr.mu.RUnlock()
for _, client := range nr.clientManager.Clients() {
......@@ -92,7 +92,7 @@ func (nr *NetlinkReader) addPathsToClients(routes []netlink.Route) {
}
// create pfx and path from route
pfx, paths, err := route.NewPathsFromNlRoute(r, true)
pfx, paths, err := NewPathsFromNlRoute(r, true)
if err != nil {
log.WithError(err).WithFields(log.Fields{
"prefix": pfx.String(),
......@@ -135,7 +135,7 @@ func (nr *NetlinkReader) removePathsFromClients(routes []netlink.Route) {
}
// only withdraw changed routes
withdraw := route.NetlinkRouteDiff(nr.routes, routes)
withdraw := NetlinkRouteDiff(nr.routes, routes)
nr.mu.RUnlock()
for _, client := range nr.clientManager.Clients() {
......@@ -146,7 +146,7 @@ func (nr *NetlinkReader) removePathsFromClients(routes []netlink.Route) {
}
// create pfx and path from route
pfx, paths, err := route.NewPathsFromNlRoute(r, true)
pfx, paths, err := NewPathsFromNlRoute(r, true)
if err != nil {
log.WithError(err).WithFields(log.Fields{
"prefix": pfx.String(),
......
package protocolnetlink
package fib
import (
"fmt"
......
package route
import (
"fmt"
bnet "github.com/bio-routing/bio-rd/net"
)
const (
ProtoBio = 45 // bio
)
// FIBPath represents a path learned via Netlink of a route
type FIBPath struct {
Src bnet.IP
NextHop bnet.IP // GW
Priority int
Protocol int
Type int
Table int
Kernel bool // True if the route is already installed in the kernel
}
// NewNlPathFromBgpPath creates a new FIBPath object from a BGPPath object
func NewNlPathFromBgpPath(p *BGPPath) *FIBPath {
return &FIBPath{
Src: p.Source,
NextHop: p.NextHop,
Protocol: ProtoBio,
Kernel: false,
}
}
// Select compares s with t and returns negative if s < t, 0 if paths are equal, positive if s > t
func (s *FIBPath) Select(t *FIBPath) int8 {
if s.NextHop.Compare(t.NextHop) < 0 {
return -1
}
if s.NextHop.Compare(t.NextHop) > 0 {
return 1
}
if s.Src.Compare(t.Src) < 0 {
return -1
}
if s.Src.Compare(t.Src) > 0 {
return 1
}
if s.Priority < t.Priority {
return -1
}
if s.Priority > t.Priority {
return 1
}
if s.Protocol < t.Protocol {
return -1
}
if s.Protocol > t.Protocol {
return 1
}
if s.Table < t.Table {
return -1
}
if s.Table > t.Table {
return 1
}
return 0
}
// ECMP determines if path s and t are equal in terms of ECMP
func (s *FIBPath) ECMP(t *FIBPath) bool {
return s.Src == t.Src && s.Priority == t.Priority && s.Protocol == t.Protocol && s.Type == t.Type && s.Table == t.Table
}
// Copy duplicates the current object
func (s *FIBPath) Copy() *FIBPath {
if s == nil {
return nil
}
cp := *s
return &cp
}
// Print all known information about a route in logfile friendly format
func (s *FIBPath) String() string {
ret := fmt.Sprintf("Source: %s, ", s.Src.String())
ret += fmt.Sprintf("NextHop: %s, ", s.NextHop.String())
ret += fmt.Sprintf("Priority: %d, ", s.Priority)
ret += fmt.Sprintf("Type: %d, ", s.Type)
ret += fmt.Sprintf("Table: %d", s.Table)
return ret
}
// Print all known information about a route in human readable form
func (s *FIBPath) Print() string {
ret := fmt.Sprintf("\t\tSource: %s\n", s.Src.String())
ret += fmt.Sprintf("\t\tNextHop: %s\n", s.NextHop.String())
ret += fmt.Sprintf("\t\tPriority: %d\n", s.Priority)
ret += fmt.Sprintf("\t\tType: %d\n", s.Type)
ret += fmt.Sprintf("\t\tTable: %d\n", s.Table)
return ret
}
......@@ -8,10 +8,10 @@ import (
// Path represents a network path
type Path struct {
Type uint8
StaticPath *StaticPath
BGPPath *BGPPath
NetlinkPath *NetlinkPath
Type uint8
StaticPath *StaticPath
BGPPath *BGPPath
FIBPath *FIBPath
}
// Select returns negative if p < q, 0 if paths are equal, positive if p > q
......@@ -39,8 +39,8 @@ func (p *Path) Select(q *Path) int8 {
return p.BGPPath.Select(q.BGPPath)
case StaticPathType:
return p.StaticPath.Select(q.StaticPath)
case NetlinkPathType:
return p.NetlinkPath.Select(q.NetlinkPath)
case FIBPathType:
return p.FIBPath.Select(q.FIBPath)
}
panic("Unknown path type")
......@@ -53,8 +53,8 @@ func (p *Path) ECMP(q *Path) bool {
return p.BGPPath.ECMP(q.BGPPath)
case StaticPathType:
return p.StaticPath.ECMP(q.StaticPath)
case NetlinkPathType:
return p.NetlinkPath.ECMP(q.NetlinkPath)
case FIBPathType:
return p.FIBPath.ECMP(q.FIBPath)
}
panic("Unknown path type")
......@@ -110,8 +110,8 @@ func (p *Path) String() string {
return "not implemented yet"
case BGPPathType:
return p.BGPPath.String()
case NetlinkPathType:
return p.NetlinkPath.String()
case FIBPathType:
return p.FIBPath.String()
default:
return "Unknown paty type. Probably not implemented yet"
}
......@@ -125,7 +125,7 @@ func (p *Path) Print() string {
protocol = "static"
case BGPPathType:
protocol = "BGP"
case NetlinkPathType:
case FIBPathType:
protocol = "Netlink"
}
......@@ -135,8 +135,8 @@ func (p *Path) Print() string {
ret += "Not implemented yet"
case BGPPathType:
ret += p.BGPPath.Print()
case NetlinkPathType:
ret += p.NetlinkPath.Print()
case FIBPathType:
ret += p.FIBPath.Print()
}
return ret
......@@ -162,8 +162,8 @@ func (p *Path) NextHop() bnet.IP {
return p.BGPPath.NextHop
case StaticPathType:
return p.StaticPath.NextHop
case NetlinkPathType:
return p.NetlinkPath.NextHop
case FIBPathType:
return p.FIBPath.NextHop
}
panic("Unknown path type")
......
......@@ -6,7 +6,6 @@ import (
bnet "github.com/bio-routing/bio-rd/net"
"github.com/stretchr/testify/assert"
"github.com/vishvananda/netlink"
)
func TestPathNextHop(t *testing.T) {
......@@ -38,8 +37,8 @@ func TestPathNextHop(t *testing.T) {
{
name: "Netlink Path",
p: &Path{
Type: NetlinkPathType,
NetlinkPath: &NetlinkPath{
Type: FIBPathType,
FIBPath: &FIBPath{
NextHop: bnet.IPv4(1000),
},
},
......@@ -151,12 +150,12 @@ func TestSelect(t *testing.T) {
{
name: "Netlink",
p: &Path{
Type: NetlinkPathType,
NetlinkPath: &NetlinkPath{},
Type: FIBPathType,
FIBPath: &FIBPath{},
},
q: &Path{
Type: NetlinkPathType,
NetlinkPath: &NetlinkPath{},
Type: FIBPathType,
FIBPath: &FIBPath{},
},
expected: 0,
},
......@@ -328,7 +327,7 @@ func TestNewNlPath(t *testing.T) {
tests := []struct {
name string
source *Path
expected *NetlinkPath
expected *FIBPath
}{
{
name: "BGPPath",
......@@ -338,7 +337,7 @@ func TestNewNlPath(t *testing.T) {
NextHop: bnet.IPv4(123),
},
},
expected: &NetlinkPath{
expected: &FIBPath{
NextHop: bnet.IPv4(123),
Protocol: ProtoBio,
},
......@@ -346,7 +345,7 @@ func TestNewNlPath(t *testing.T) {
}
for _, test := range tests {
var converted *NetlinkPath
var converted *FIBPath
switch test.source.Type {
case BGPPathType:
......@@ -360,245 +359,6 @@ func TestNewNlPath(t *testing.T) {
}
}
func TestNewPathsFromNetlinkRoute(t *testing.T) {
tests := []struct {
name string
source netlink.Route
expectedPfx bnet.Prefix
expectedPaths []*Path
expectError bool
}{
{
name: "Simple",
source: netlink.Route{
Dst: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8).GetIPNet(),
Src: bnet.IPv4(456).Bytes(),
Gw: bnet.IPv4(789).Bytes(),
Protocol: ProtoKernel,
Priority: 1,
Table: 254,
Type: 1,
},
expectedPfx: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8),
expectedPaths: []*Path{
{
Type: NetlinkPathType,
NetlinkPath: &NetlinkPath{
Src: bnet.IPv4(456),
NextHop: bnet.IPv4(789),
Protocol: ProtoKernel,
Priority: 1,
Table: 254,
Type: 1,
Kernel: true,
},
},
},
expectError: false,
},
{
name: "Multiple nexthop",
source: netlink.Route{
Dst: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8).GetIPNet(),
Src: bnet.IPv4(456).Bytes(),
MultiPath: []*netlink.NexthopInfo{
{
LinkIndex: 1,
Hops: 1,
Gw: bnet.IPv4(123).Bytes(),
Flags: 0,
NewDst: nil,
Encap: nil,
}, {
LinkIndex: 2,
Hops: 1,
Gw: bnet.IPv4(345).Bytes(),
Flags: 0,
NewDst: nil,
Encap: nil,
},
},
Protocol: ProtoKernel,
Priority: 1,
Table: 254,
Type: 1,
},
expectedPfx: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8),
expectedPaths: []*Path{
{
Type: NetlinkPathType,
NetlinkPath: &NetlinkPath{
Src: bnet.IPv4(456),
NextHop: bnet.IPv4(123),
Protocol: ProtoKernel,
Priority: 1,
Table: 254,
Type: 1,
Kernel: true,
},
}, {
Type: NetlinkPathType,
NetlinkPath: &NetlinkPath{
Src: bnet.IPv4(456),
NextHop: bnet.IPv4(345),
Protocol: ProtoKernel,
Priority: 1,
Table: 254,
Type: 1,
Kernel: true,
},
},
},
expectError: false,
},
{
name: "No source but destination",
source: netlink.Route{
Dst: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8).GetIPNet(),
Gw: bnet.IPv4(789).Bytes(),
Protocol: ProtoKernel,
Priority: 1,
Table: 254,
Type: 1,
},
expectedPfx: bnet.NewPfx(bnet.IPv4FromOctets(10, 0, 0, 0), 8),
expectedPaths: []*Path{
{
Type: NetlinkPathType,
NetlinkPath: &NetlinkPath{
Src: bnet.IPv4(0),
NextHop: bnet.IPv4(789),
Protocol: ProtoKernel,
Priority: 1,
Table: 254,
Type: 1,
Kernel: true,
},
},
},
expectError: false,
},
{
name: "Source but no destination",
source: netlink.Route{
Src: bnet.IPv4(456).Bytes(),
Gw: bnet.IPv4(789).Bytes(),
Protocol: ProtoKernel,
Priority: 1,
Table: 254,
Type: 1,
},
expectedPfx: bnet.NewPfx(bnet.IPv4FromOctets(0, 0, 0, 0), 0),
expectedPaths: []*Path{
{
Type: NetlinkPathType,
NetlinkPath: &NetlinkPath{
Src: bnet.IPv4(456),
NextHop: bnet.IPv4(789),
Protocol: ProtoKernel,
Priority: 1,
Table: 254,
Type: 1,
Kernel: true,
},
},
},
expectError: false,
},
{
name: "No source but no destination",
source: netlink.Route{
Gw: bnet.IPv4(789).Bytes(),
Protocol: ProtoKernel,
Priority: 1,
Table: 254,
Type: 1,
},
expectedPfx: bnet.Prefix{},
expectedPaths: []*Path{},
expectError: true,
},
{
name: "No source but destination IPv6",
source: netlink.Route{
Dst: bnet.NewPfx(bnet.IPv6(2001, 0), 48).GetIPNet(),
Gw: bnet.IPv6(2001, 123).Bytes(),
Protocol: ProtoKernel,
Priority: 1,
Table: 254,
Type: 1,
},
expectedPfx: bnet.NewPfx(bnet.IPv6(2001, 0), 48),
expectedPaths: []*Path{
{
Type: NetlinkPathType,
NetlinkPath: &NetlinkPath{
Src: bnet.IPv6(0, 0),
NextHop: bnet.IPv6(2001, 123),
Protocol: ProtoKernel,
Priority: 1,
Table: 254,
Type: 1,
Kernel: true,
},
},
},
expectError: false,
},
{
name: "Source but no destination IPv6",
source: netlink.Route{
Src: bnet.IPv6(2001, 456).Bytes(),
Gw: bnet.IPv6(2001, 789).Bytes(),
Protocol: ProtoKernel,
Priority: 1,
Table: 254,
Type: 1,
},
expectedPfx: bnet.NewPfx(bnet.IPv6(0, 0), 0),
expectedPaths: []*Path{
{
Type: NetlinkPathType,
NetlinkPath: &NetlinkPath{
Src: bnet.IPv6(2001, 456),
NextHop: bnet.IPv6(2001, 789),
Protocol: ProtoKernel,
Priority: 1,
Table: 254,
Type: 1,
Kernel: true,
},
},
},
expectError: false,
},
{
name: "no source no destination",
source: netlink.Route{
Gw: bnet.IPv4(123).Bytes(),
Protocol: ProtoKernel,
Priority: 1,
Table: 254,
Type: 1,
},
expectedPfx: bnet.NewPfx(bnet.IPv4(0), 0),
expectedPaths: []*Path{{}},
expectError: true,
},
}
for _, test := range tests {
pfx, paths, err := NewPathsFromNlRoute(test.source, true)
if test.expectError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
assert.Equalf(t, test.expectedPaths, paths, test.name)
assert.Equalf(t, test.expectedPfx, pfx, test.name)
}
}
}
func TestECMP(t *testing.T) {
tests := []struct {
name string
......@@ -648,11 +408,12 @@ func TestECMP(t *testing.T) {
},
},
ecmp: false,
}, {
},
{
name: "Netlink Path ecmp",
left: &Path{
Type: NetlinkPathType,
NetlinkPath: &NetlinkPath{
Type: FIBPathType,
FIBPath: &FIBPath{
Src: bnet.IPv4(123),
Priority: 1,
Protocol: 1,
......@@ -661,8 +422,8 @@ func TestECMP(t *testing.T) {
},
},
right: &Path{
Type: NetlinkPathType,
NetlinkPath: &NetlinkPath{
Type: FIBPathType,
FIBPath: &FIBPath{
Src: bnet.IPv4(123),
Priority: 1,
Protocol: 1,
......@@ -674,8 +435,8 @@ func TestECMP(t *testing.T) {
}, {
name: "Netlink Path not ecmp",
left: &Path{
Type: NetlinkPathType,
NetlinkPath: &NetlinkPath{
Type: FIBPathType,
FIBPath: &FIBPath{
Src: bnet.IPv4(123),
Priority: 1,
Protocol: 1,
......@@ -684,8 +445,8 @@ func TestECMP(t *testing.T) {
},
},
right: &Path{
Type: NetlinkPathType,
NetlinkPath: &NetlinkPath{
Type: FIBPathType,
FIBPath: &FIBPath{
Src: bnet.IPv4(123),
Priority: 2,
Protocol: 1,
......@@ -734,23 +495,23 @@ func TestECMP(t *testing.T) {
}
}
func TestNetlinkPathSelect(t *testing.T) {
func TestFIBPathSelect(t *testing.T) {
tests := []struct {
name string
left *NetlinkPath
right *NetlinkPath
left *FIBPath
right *FIBPath
expected int8
}{
{
name: "equal",
left: &NetlinkPath{
left: &FIBPath{
NextHop: bnet.IPv4(123),
Src: bnet.IPv4(234),
Priority: 1,
Protocol: 1,
Table: 1,
},
right: &NetlinkPath{
right: &FIBPath{
NextHop: bnet.IPv4(123),
Src: bnet.IPv4(234),
Priority: 1,
......@@ -761,100 +522,100 @@ func TestNetlinkPathSelect(t *testing.T) {
},
{
name: "nextHop smaller",
left: &NetlinkPath{
left: &FIBPath{
NextHop: bnet.IPv4(1),
},
right: &NetlinkPath{
right: &FIBPath{
NextHop: bnet.IPv4(2),
},
expected: -1,
},
{
name: "nextHop bigger",
left: &NetlinkPath{
left: &FIBPath{
NextHop: bnet.IPv4(2),
},
right: &NetlinkPath{
right: &FIBPath{
NextHop: bnet.IPv4(1),
},
expected: 1,
},
{
name: "src smaller",
left: &NetlinkPath{
left: &FIBPath{
Src: bnet.IPv4(1),
},
right: &NetlinkPath{
right: &FIBPath{
Src: bnet.IPv4(2),
},
expected: -1,
},
{
name: "src bigger",
left: &NetlinkPath{
left: &FIBPath{
Src: bnet.IPv4(2),
},
right: &NetlinkPath{
right: &FIBPath{
Src: bnet.IPv4(1),
},
expected: 1,
},
{
name: "priority smaller",
left: &NetlinkPath{
left: &FIBPath{
Priority: 1,
},
right: &NetlinkPath{
right: &FIBPath{
Priority: 2,
},
expected: -1,
},
{
name: "priority bigger",
left: &NetlinkPath{
left: &FIBPath{
Priority: 2,
},
right: &NetlinkPath{
right: &FIBPath{
Priority: 1,
},
expected: 1,
},
{
name: "protocol smaller",
left: &NetlinkPath{
left: &FIBPath{
Protocol: 1,
},
right: &NetlinkPath{
right: &FIBPath{
Protocol: 2,
},
expected: -1,
},
{
name: "protocol bigger",
left: &NetlinkPath{
left: &FIBPath{
Protocol: 2,
},
right: &NetlinkPath{
right: &FIBPath{
Protocol: 1,
},
expected: 1,
},
{
name: "table smaller",
left: &NetlinkPath{
left: &FIBPath{
Table: 1,
},
right: &NetlinkPath{
right: &FIBPath{
Table: 2,
},
expected: -1,
},
{
name: "table bigger",
left: &NetlinkPath{
left: &FIBPath{
Table: 2,
},
right: &NetlinkPath{
right: &FIBPath{
Table: 1,
},
expected: 1,
......
......@@ -6,7 +6,6 @@ import (
"sync"
"github.com/bio-routing/bio-rd/net"
"github.com/vishvananda/netlink"
)
const (
......@@ -24,8 +23,8 @@ const (
// ISISPathType indicates a path is an ISIS path
ISISPathType
// NetlinkPathType indicates a path is an Netlink/Kernel path
NetlinkPathType
// FIBPathType indicates a path is a FIB path
FIBPathType
)
// Route links a prefix to paths
......@@ -278,51 +277,3 @@ func (r *Route) Print() string {
return ret
}
// NetlinkRouteDiff gets the list of elements contained by a but not b
func NetlinkRouteDiff(a, b []netlink.Route) []netlink.Route {
ret := make([]netlink.Route, 0)
for _, pa := range a {
if !netlinkRoutesContains(pa, b) {
ret = append(ret, pa)
}
}
return ret
}
func netlinkRoutesContains(needle netlink.Route, haystack []netlink.Route) bool {
for i := range haystack {
if netlinkRouteEquals(&needle, &haystack[i]) {
return true
}
}
return false
}
func netlinkRouteEquals(a, b *netlink.Route) bool {
aMaskSize, aMaskBits := a.Dst.Mask.Size()
bMaskSize, bMaskBits := b.Dst.Mask.Size()
return a.LinkIndex == b.LinkIndex &&
a.ILinkIndex == b.ILinkIndex &&
a.Scope == b.Scope &&
a.Dst.IP.Equal(b.Dst.IP) &&
aMaskSize == bMaskSize &&
aMaskBits == bMaskBits &&
a.Src.Equal(b.Src) &&
a.Gw.Equal(b.Gw) &&
a.Protocol == b.Protocol &&
a.Priority == b.Priority &&
a.Table == b.Table &&
a.Type == b.Type &&
a.Tos == b.Tos &&
a.Flags == b.Flags &&
a.MTU == b.MTU &&
a.AdvMSS == b.AdvMSS
}
package route
import (
"net"
"testing"
"github.com/stretchr/testify/assert"
"github.com/vishvananda/netlink"
bnet "github.com/bio-routing/bio-rd/net"
)
func TestNetlinkRouteDiff(t *testing.T) {
tests := []struct {
name string
left []netlink.Route
right []netlink.Route
expected []netlink.Route
}{
{
name: "Equal",
left: []netlink.Route{
{
Dst: &net.IPNet{
IP: net.IPv4(10, 0, 0, 1),
Mask: net.IPv4Mask(255, 0, 0, 0),
},
Table: 1,
},
{
Dst: &net.IPNet{
IP: net.IPv4(20, 0, 0, 1),
Mask: net.IPv4Mask(255, 0, 0, 0),
},
Table: 2,
},
},
right: []netlink.Route{
{
Dst: &net.IPNet{
IP: net.IPv4(10, 0, 0, 1),
Mask: net.IPv4Mask(255, 0, 0, 0),
},
Table: 1,
},
{
Dst: &net.IPNet{
IP: net.IPv4(20, 0, 0, 1),
Mask: net.IPv4Mask(255, 0, 0, 0),
},
Table: 2,
},
},
expected: []netlink.Route{},
}, {
name: "Left empty",
left: []netlink.Route{},
right: []netlink.Route{
{
Dst: &net.IPNet{
IP: net.IPv4(10, 0, 0, 1),
Mask: net.IPv4Mask(255, 0, 0, 0),
},
Table: 1,
},
{
Dst: &net.IPNet{
IP: net.IPv4(20, 0, 0, 1),
Mask: net.IPv4Mask(255, 0, 0, 0),
},
Table: 2,
},
},
expected: []netlink.Route{},
}, {
name: "Right empty",
left: []netlink.Route{
{
Dst: &net.IPNet{
IP: net.IPv4(10, 0, 0, 1),
Mask: net.IPv4Mask(255, 0, 0, 0),
},
Table: 1,
},
{
Dst: &net.IPNet{
IP: net.IPv4(20, 0, 0, 1),
Mask: net.IPv4Mask(255, 0, 0, 0),
},
Table: 2,
},
},
right: []netlink.Route{},
expected: []netlink.Route{
{
Dst: &net.IPNet{
IP: net.IPv4(10, 0, 0, 1),
Mask: net.IPv4Mask(255, 0, 0, 0),
},
Table: 1,
},
{
Dst: &net.IPNet{
IP: net.IPv4(20, 0, 0, 1),
Mask: net.IPv4Mask(255, 0, 0, 0),
},
Table: 2,
},
},
}, {
name: "Diff",
left: []netlink.Route{
{
Dst: &net.IPNet{
IP: net.IPv4(10, 0, 0, 1),
Mask: net.IPv4Mask(255, 0, 0, 0),
},
Table: 1,
},
{
Dst: &net.IPNet{
IP: net.IPv4(20, 0, 0, 1),
Mask: net.IPv4Mask(255, 0, 0, 0),
},
Table: 2,
},
},
right: []netlink.Route{
{
Dst: &net.IPNet{
IP: net.IPv4(10, 0, 0, 1),
Mask: net.IPv4Mask(255, 0, 0, 0),
},
Table: 1,
},
},
expected: []netlink.Route{
{
Dst: &net.IPNet{
IP: net.IPv4(20, 0, 0, 1),
Mask: net.IPv4Mask(255, 0, 0, 0),
},
Table: 2,
},
},
},
}
for _, test := range tests {
res := NetlinkRouteDiff(test.left, test.right)
assert.Equal(t, test.expected, res)
}
}
func TestNewRoute(t *testing.T) {
tests := []struct {
name string
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment