Skip to content
Snippets Groups Projects
Unverified Commit 308262d5 authored by Daniel Czerwonk's avatar Daniel Czerwonk Committed by GitHub
Browse files

Merge branch 'master' into fix/proto_script_for_mac

parents 55ec9f48 8f926bba
No related branches found
No related tags found
No related merge requests found
Showing
with 1213 additions and 17 deletions
package config
import (
"fmt"
"github.com/bio-routing/bio-rd/protocols/isis/types"
)
type ISISConfig struct {
NETs []NET
Interfaces []ISISInterfaceConfig
TrafficEngineeringRouterID [4]byte
}
type ISISInterfaceConfig struct {
Name string
Passive bool
P2P bool
ISISLevel1Config *ISISLevelConfig
ISISLevel2Config *ISISLevelConfig
}
type ISISLevelConfig struct {
HelloInterval uint16
HoldTime uint16
Metric uint32
Priority uint8
}
// NET represents an ISO network entity title
type NET struct {
AFI byte
AreaID types.AreaID
SystemID types.SystemID
SEL byte
}
func parseNET(addr []byte) (*NET, error) {
l := len(addr)
if l < 8 {
return nil, fmt.Errorf("NET too short")
}
if l > 20 {
return nil, fmt.Errorf("NET too long")
}
areaID := []byte{}
for i := 0; i < l-8; i++ {
areaID = append(areaID, addr[i+1])
}
systemID := types.SystemID{
addr[l-7],
addr[l-6],
addr[l-5],
addr[l-4],
addr[l-3],
addr[l-2],
}
return &NET{
AFI: addr[0],
AreaID: areaID,
SystemID: systemID,
SEL: addr[l-1],
}, nil
}
package config
import (
"testing"
"github.com/bio-routing/bio-rd/protocols/isis/types"
"github.com/stretchr/testify/assert"
)
func TestParseNET(t *testing.T) {
tests := []struct {
name string
input []byte
wantFail bool
expected *NET
}{
{
name: "Too short",
input: []byte{49, 1, 2, 3, 4, 5, 0},
wantFail: true,
},
{
name: "Too long",
input: []byte{0x49, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 0, 0},
wantFail: true,
},
{
name: "Max area ID length",
input: []byte{0x49, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 0},
expected: &NET{
AFI: 0x49,
AreaID: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12},
SystemID: types.SystemID{1, 2, 3, 4, 5, 6},
SEL: 0x00,
},
},
{
name: "No Area ID",
input: []byte{0x49, 1, 2, 3, 4, 5, 6, 0},
expected: &NET{
AFI: 0x49,
AreaID: []byte{},
SystemID: types.SystemID{1, 2, 3, 4, 5, 6},
SEL: 0x00,
},
},
}
for _, test := range tests {
NET, err := parseNET(test.input)
if err != nil {
if test.wantFail {
continue
}
t.Errorf("Unexpected error for test %q: %v", test.name, err)
continue
}
if test.wantFail {
t.Errorf("Unexpected success for test %q", test.name)
}
assert.Equalf(t, test.expected, NET, "Test: %q", test.name)
}
}
......@@ -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,6 +7,16 @@ import (
bnet "github.com/bio-routing/bio-rd/net"
)
const (
IfOperUnknown = 0
IfOperNotPresent = 1
IfOperDown = 2
IfOperLowerLayerDown = 3
IfOperTesting = 4
IfOperDormant = 5
IfOperUp = 6
)
// Device represents a network device
type Device struct {
Name string
......
......@@ -6,6 +6,12 @@ import (
"github.com/pkg/errors"
)
// Updater is a device updater interface
type Updater interface {
Subscribe(Client, string)
Unsubscribe(Client, string)
}
// Server represents a device server
type Server struct {
devices map[uint64]*Device
......@@ -67,8 +73,8 @@ func (ds *Server) Subscribe(client Client, devName string) {
client.DeviceUpdate(d)
}
ds.clientsByDeviceMu.RLock()
defer ds.clientsByDeviceMu.RUnlock()
ds.clientsByDeviceMu.Lock()
defer ds.clientsByDeviceMu.Unlock()
if _, ok := ds.clientsByDevice[devName]; !ok {
ds.clientsByDevice[devName] = make([]Client, 0)
......@@ -77,6 +83,25 @@ func (ds *Server) Subscribe(client Client, devName string) {
ds.clientsByDevice[devName] = append(ds.clientsByDevice[devName], client)
}
// Unsubscribe unsubscribes a client
func (ds *Server) Unsubscribe(client Client, devName string) {
ds.clientsByDeviceMu.Lock()
defer ds.clientsByDeviceMu.Unlock()
if _, ok := ds.clientsByDevice[devName]; !ok {
return
}
for i := range ds.clientsByDevice[devName] {
if ds.clientsByDevice[devName][i] != client {
continue
}
ds.clientsByDevice[devName] = append(ds.clientsByDevice[devName][:i], ds.clientsByDevice[devName][i+1:]...)
return
}
}
func (ds *Server) addDevice(d *Device) {
ds.devicesMu.Lock()
defer ds.devicesMu.Unlock()
......
package device
import "fmt"
import (
"fmt"
"github.com/pkg/errors"
)
func (ds *Server) loadAdapter() error {
a, err := newOSAdapterDarwin(ds)
if err != nil {
return errors.Wrap(err, "Unable to create OS X adapter")
}
ds.osAdapter = a
return nil
}
type osAdapterDarwin struct {
}
func newOSAdapterDarwin(srv *Server) (*osAdapterDarwin, error) {
return nil, nil
return nil, fmt.Errorf("Not implemented")
}
func (o *osAdapterDarwin) start() error {
return fmt.Errorf("Not implemented")
}
func (o *osAdapterDarwin) init() error {
return fmt.Errorf("Not implemented")
}
package device
type MockServer struct {
Called bool
UnsubscribeCalled bool
C Client
Name string
UnsubscribeName string
}
func (ms *MockServer) Subscribe(c Client, n string) {
ms.Called = true
ms.Name = n
}
func (ms *MockServer) Unsubscribe(c Client, n string) {
ms.UnsubscribeCalled = true
ms.UnsubscribeName = n
}
......@@ -81,6 +81,7 @@ func TestStop(t *testing.T) {
type mockClient struct {
deviceUpdateCalled uint
name string
}
func (m *mockClient) DeviceUpdate(d *Device) {
......@@ -117,3 +118,134 @@ func TestNotify(t *testing.T) {
s.notify(101)
assert.Equal(t, uint(2), mc.deviceUpdateCalled)
}
func TestUnsubscribe(t *testing.T) {
tests := []struct {
name string
ds *Server
unsubscribeDev string
unsubscribeClient int
expected *Server
}{
{
name: "Remove single",
ds: &Server{
clientsByDevice: map[string][]Client{
"eth0": {
&mockClient{
name: "foo",
},
},
},
},
unsubscribeDev: "eth0",
unsubscribeClient: 0,
expected: &Server{
clientsByDevice: map[string][]Client{
"eth0": {},
},
},
},
{
name: "Remove middle",
ds: &Server{
clientsByDevice: map[string][]Client{
"eth0": {
&mockClient{
name: "foo",
},
&mockClient{
name: "bar",
},
&mockClient{
name: "baz",
},
},
},
},
unsubscribeDev: "eth0",
unsubscribeClient: 1,
expected: &Server{
clientsByDevice: map[string][]Client{
"eth0": {
&mockClient{
name: "foo",
},
&mockClient{
name: "baz",
},
},
},
},
},
{
name: "Remove first",
ds: &Server{
clientsByDevice: map[string][]Client{
"eth0": {
&mockClient{
name: "foo",
},
&mockClient{
name: "bar",
},
&mockClient{
name: "baz",
},
},
},
},
unsubscribeDev: "eth0",
unsubscribeClient: 0,
expected: &Server{
clientsByDevice: map[string][]Client{
"eth0": {
&mockClient{
name: "bar",
},
&mockClient{
name: "baz",
},
},
},
},
},
{
name: "Remove last",
ds: &Server{
clientsByDevice: map[string][]Client{
"eth0": {
&mockClient{
name: "foo",
},
&mockClient{
name: "bar",
},
&mockClient{
name: "baz",
},
},
},
},
unsubscribeDev: "eth0",
unsubscribeClient: 2,
expected: &Server{
clientsByDevice: map[string][]Client{
"eth0": {
&mockClient{
name: "foo",
},
&mockClient{
name: "bar",
},
},
},
},
},
}
for _, test := range tests {
test.ds.Unsubscribe(test.ds.clientsByDevice[test.unsubscribeDev][test.unsubscribeClient], test.unsubscribeDev)
assert.Equal(t, test.expected, test.ds, test.name)
}
}
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"
......
......@@ -21,6 +21,14 @@ const (
UP_STATE = 0
)
var (
AllL1ISS = [6]byte{0x01, 0x80, 0xC2, 0x00, 0x00, 0x14}
AllL2ISS = [6]byte{0x01, 0x80, 0xC2, 0x00, 0x00, 0x15}
AllP2PISS = [6]byte{0x09, 0x00, 0x2b, 0x00, 0x00, 0x05}
AllISS = [6]byte{0x09, 0x00, 0x2B, 0x00, 0x00, 0x05}
AllESS = [6]byte{0x09, 0x00, 0x2B, 0x00, 0x00, 0x04}
)
// ISISPacket represents an ISIS packet
type ISISPacket struct {
Header *ISISHeader
......
......@@ -7,8 +7,16 @@ import (
"github.com/bio-routing/bio-rd/util/decode"
)
// ProtocolsSupportedTLVType is the type value of an protocols supported TLV
const ProtocolsSupportedTLVType = 129
const (
// ProtocolsSupportedTLVType is the type value of an protocols supported TLV
ProtocolsSupportedTLVType = 129
// NLPIDIPv4 is the Network Layer Protocol ID for IPv4
NLPIDIPv4 = uint8(0xcc)
// NLPIDIPv6 is the Network Layer Protocol ID for IPv6
NLPIDIPv6 = uint8(0x8e)
)
// ProtocolsSupportedTLV represents a protocols supported TLV
type ProtocolsSupportedTLV struct {
......
package server
import (
"fmt"
"sync"
"github.com/bio-routing/bio-rd/config"
"github.com/bio-routing/bio-rd/protocols/device"
"github.com/bio-routing/bio-rd/protocols/isis/packet"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)
type dev struct {
name string
srv *Server
sys sys
up bool
passive bool
p2p bool
level2 *level
supportedProtocols []uint8
phy *device.Device
done chan struct{}
wg sync.WaitGroup
helloMethod func()
receiverMethod func()
}
type level struct {
HelloInterval uint16
HoldTime uint16
Metric uint32
neighbors *neighbors
}
func newDev(srv *Server, ifcfg *config.ISISInterfaceConfig) *dev {
d := &dev{
name: ifcfg.Name,
srv: srv,
passive: ifcfg.Passive,
p2p: ifcfg.P2P,
supportedProtocols: []uint8{packet.NLPIDIPv4, packet.NLPIDIPv6},
done: make(chan struct{}),
}
d.helloMethod = d.helloRoutine
d.receiverMethod = d.receiverRoutine
if ifcfg.ISISLevel2Config != nil {
d.level2 = &level{}
d.level2.HelloInterval = ifcfg.ISISLevel2Config.HelloInterval
d.level2.HoldTime = ifcfg.ISISLevel2Config.HoldTime
d.level2.Metric = ifcfg.ISISLevel2Config.Metric
d.level2.neighbors = newNeighbors()
}
return d
}
// DeviceUpdate receives interface status information and manages ISIS interface state
func (d *dev) DeviceUpdate(phy *device.Device) {
d.phy = phy
if d.phy.OperState == device.IfOperUp {
err := d.enable()
if err != nil {
log.Errorf("Unable to enable ISIS on %q: %v", d.name, err)
}
return
}
err := d.disable()
if err != nil {
log.Errorf("Unable to disable ISIS on %q: %v", d.name, err)
return
}
}
func (d *dev) enable() error {
err := d.sys.openPacketSocket()
if err != nil {
return fmt.Errorf("Failed to open packet socket: %v", err)
}
err = d.sys.mcastJoin(packet.AllP2PISS)
if err != nil {
return fmt.Errorf("Failed to join multicast group: %v", err)
}
d.done = make(chan struct{})
d.wg.Add(1)
go d.receiverMethod()
d.wg.Add(1)
go d.helloMethod()
log.Infof("ISIS: Interface %q is now up", d.name)
d.up = true
return nil
}
func (d *dev) disable() error {
close(d.done)
err := d.sys.closePacketSocket()
if err != nil {
return errors.Wrap(err, "Unable to close socket")
}
d.wg.Wait()
d.up = false
return nil
}
func (d *dev) receiverRoutine() {
// To be implemented
}
func (d *dev) helloRoutine() {
// To be implemented
}
package server
import (
"testing"
"github.com/bio-routing/bio-rd/protocols/device"
"github.com/stretchr/testify/assert"
)
func (d *dev) mockRecv() {
<-d.done
d.wg.Done()
}
func (d *dev) mockHello() {
<-d.done
d.wg.Done()
}
func TestEnableDisable(t *testing.T) {
tests := []struct {
name string
dev *dev
wantFail bool
}{
{
name: "Failed open() for socket",
dev: &dev{
sys: &mockSys{
wantFailOpenPacketSocket: true,
},
},
wantFail: true,
},
{
name: "Failed mcast join",
dev: &dev{
sys: &mockSys{
wantFailMcastJoin: true,
},
},
wantFail: true,
},
{
name: "Success",
dev: &dev{
sys: &mockSys{},
},
wantFail: false,
},
}
for _, test := range tests {
test.dev.receiverMethod = test.dev.mockRecv
test.dev.helloMethod = test.dev.mockHello
err := test.dev.enable()
if err != nil {
if test.wantFail {
continue
}
t.Errorf("Unexpected failure for test %q: %v", test.name, err)
continue
}
if test.wantFail {
t.Errorf("Unexpected success for test %q", test.name)
}
err = test.dev.disable()
if err != nil {
if test.wantFail {
continue
}
t.Errorf("Unexpected failure for test %q: %v", test.name, err)
continue
}
if test.wantFail {
t.Errorf("Unexpected success for test %q", test.name)
}
assert.Equal(t, true, test.dev.sys.(*mockSys).closePacketSocketCalled)
}
}
func TestDeviceUpdate(t *testing.T) {
tests := []struct {
name string
dev *dev
update *device.Device
expected bool
}{
{
name: "Enable",
dev: &dev{
up: false,
sys: &mockSys{},
},
update: &device.Device{
OperState: device.IfOperUp,
},
expected: true,
},
{
name: "Disable #1",
dev: &dev{
done: make(chan struct{}),
up: true,
sys: &mockSys{},
},
update: &device.Device{
OperState: device.IfOperLowerLayerDown,
},
expected: false,
},
{
name: "Disable #2",
dev: &dev{
done: make(chan struct{}),
up: true,
sys: &mockSys{},
},
update: &device.Device{
OperState: device.IfOperDown,
},
expected: false,
},
}
for _, test := range tests {
test.dev.receiverMethod = test.dev.mockRecv
test.dev.helloMethod = test.dev.mockHello
test.dev.DeviceUpdate(test.update)
assert.Equal(t, test.expected, test.dev.up, test.name)
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment