diff --git a/config/isis.go b/config/isis.go
new file mode 100644
index 0000000000000000000000000000000000000000..c0c5de5a773dac856ea466e0244e56149b40f838
--- /dev/null
+++ b/config/isis.go
@@ -0,0 +1,70 @@
+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
+}
diff --git a/config/isis_test.go b/config/isis_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..602606495b3b1f4a60986113e880cbbcf1cd679a
--- /dev/null
+++ b/config/isis_test.go
@@ -0,0 +1,67 @@
+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)
+
+	}
+}
diff --git a/protocols/device/device.go b/protocols/device/device.go
index 8939ca531a6530cb576aea4b93c25f7f14057327..d0eafe7549c3c90cede52c720f139ac81ac8c478 100644
--- a/protocols/device/device.go
+++ b/protocols/device/device.go
@@ -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
diff --git a/protocols/device/server.go b/protocols/device/server.go
index 00e29266830a87543505c8e0592d724afdbd9ba2..f01a82c6e7c33638134c2528474c8913fb2e0d96 100644
--- a/protocols/device/server.go
+++ b/protocols/device/server.go
@@ -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()
diff --git a/protocols/device/server_darwin.go b/protocols/device/server_darwin.go
index 364843f7dd729f08a114d6e24e6b62a432024ec0..803b9a1a7420f87f09ef33443f3e57af49242ed3 100644
--- a/protocols/device/server_darwin.go
+++ b/protocols/device/server_darwin.go
@@ -1,14 +1,32 @@
 package device
 
-import "fmt"
+import (
+	"fmt"
+
+	"github.com/pkg/errors"
+)
+
+func (ds *Server) loadAdapter() error {
+	a, err := newOSAdapterLinux(ds)
+	if err != nil {
+		return errors.Wrap(err, "Unable to create linux adapter")
+	}
+
+	ds.osAdapter = a
+	return nil
+}
 
 type osAdapterDarwin struct {
 }
 
-func newOSAdapterDarwin(srv *Server) (*osAdapterDarwin, error) {
-	return nil, nil
+func newOSAdapterLinux(srv *Server) (*osAdapterDarwin, error) {
+	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")
+}
diff --git a/protocols/device/server_mock.go b/protocols/device/server_mock.go
new file mode 100644
index 0000000000000000000000000000000000000000..19cc6e5048cbc5422d4176a6c45530673e340b0a
--- /dev/null
+++ b/protocols/device/server_mock.go
@@ -0,0 +1,19 @@
+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
+}
diff --git a/protocols/device/server_test.go b/protocols/device/server_test.go
index 87a0f70e2bbaff47682b964e787a0865c560a08b..1e3e5b0070a35b825810f11bb2f000be86e021ac 100644
--- a/protocols/device/server_test.go
+++ b/protocols/device/server_test.go
@@ -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)
+	}
+}
diff --git a/protocols/isis/packet/isis.go b/protocols/isis/packet/isis.go
index 8e1438c253f00310c3395c2cb94479dac6f154f8..b7edb627cb73dd9d6724ed981fc53d2c9ded51ef 100644
--- a/protocols/isis/packet/isis.go
+++ b/protocols/isis/packet/isis.go
@@ -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
diff --git a/protocols/isis/packet/tlv_protocols_supported.go b/protocols/isis/packet/tlv_protocols_supported.go
index a9597e155d539245a2c0e7fc158bb81311ac8c45..648ee36a0e1589620fe9602e2d88476114771dfa 100644
--- a/protocols/isis/packet/tlv_protocols_supported.go
+++ b/protocols/isis/packet/tlv_protocols_supported.go
@@ -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 {
diff --git a/protocols/isis/server/device.go b/protocols/isis/server/device.go
new file mode 100644
index 0000000000000000000000000000000000000000..1df586558224a7deca49aa3bd4692dadeef08181
--- /dev/null
+++ b/protocols/isis/server/device.go
@@ -0,0 +1,123 @@
+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
+}
diff --git a/protocols/isis/server/device_test.go b/protocols/isis/server/device_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..4b67c879f74d42623e46b5f76d0af49090cdf15d
--- /dev/null
+++ b/protocols/isis/server/device_test.go
@@ -0,0 +1,141 @@
+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)
+	}
+}
diff --git a/protocols/isis/server/devices.go b/protocols/isis/server/devices.go
new file mode 100644
index 0000000000000000000000000000000000000000..c0ec70dcc6d365f902b115e5907ba17ec6f53665
--- /dev/null
+++ b/protocols/isis/server/devices.go
@@ -0,0 +1,55 @@
+package server
+
+import (
+	"fmt"
+	"sync"
+
+	"github.com/bio-routing/bio-rd/config"
+	"github.com/pkg/errors"
+)
+
+type devices struct {
+	srv  *Server
+	db   map[string]*dev
+	dbMu sync.RWMutex
+}
+
+func newDevices(srv *Server) *devices {
+	return &devices{
+		srv: srv,
+		db:  make(map[string]*dev),
+	}
+}
+
+func (db *devices) addDevice(ifcfg *config.ISISInterfaceConfig) error {
+	db.dbMu.Lock()
+	defer db.dbMu.Unlock()
+
+	if _, ok := db.db[ifcfg.Name]; ok {
+		return fmt.Errorf("Interface exists already")
+	}
+
+	d := newDev(db.srv, ifcfg)
+	db.db[ifcfg.Name] = d
+
+	db.srv.ds.Subscribe(d, d.name)
+	return nil
+}
+
+func (db *devices) removeDevice(name string) error {
+	db.dbMu.Lock()
+	defer db.dbMu.Unlock()
+
+	if _, ok := db.db[name]; !ok {
+		return fmt.Errorf("Interface not found")
+	}
+
+	db.srv.ds.Unsubscribe(db.db[name], name)
+	err := db.db[name].disable()
+	if err != nil {
+		return errors.Wrap(err, "Unable to disable interface")
+	}
+
+	delete(db.db, name)
+	return nil
+}
diff --git a/protocols/isis/server/devices_test.go b/protocols/isis/server/devices_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..0aaec1dabb680bbbf2df43f6b8f715a5f6e0b0f6
--- /dev/null
+++ b/protocols/isis/server/devices_test.go
@@ -0,0 +1,250 @@
+package server
+
+import (
+	"testing"
+
+	"github.com/bio-routing/bio-rd/config"
+	"github.com/bio-routing/bio-rd/protocols/device"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestRemoveDevice(t *testing.T) {
+	tests := []struct {
+		name           string
+		db             *devices
+		removeName     string
+		wantFail       bool
+		expected       *devices
+		wantUnregister bool
+	}{
+		{
+			name: "Remove existing",
+			db: &devices{
+				srv: &Server{
+					ds: &device.MockServer{},
+				},
+				db: map[string]*dev{
+					"foobar": {
+						done: make(chan struct{}),
+						sys:  &mockSys{},
+						name: "foobar",
+					},
+				},
+			},
+			removeName: "foobar",
+			expected: &devices{
+				srv: &Server{
+					ds: &device.MockServer{
+						UnsubscribeCalled: true,
+						UnsubscribeName:   "foobar",
+					},
+				},
+				db: map[string]*dev{},
+			},
+			wantUnregister: true,
+		},
+		{
+			name: "Remove non-existing",
+			db: &devices{
+				srv: &Server{
+					ds: &device.MockServer{},
+				},
+				db: map[string]*dev{
+					"foobar": {
+						done: make(chan struct{}),
+						sys:  &mockSys{},
+						name: "foobar",
+					},
+				},
+			},
+			removeName: "baz",
+			expected: &devices{
+				srv: &Server{
+					ds: &device.MockServer{},
+				},
+				db: map[string]*dev{
+					"foobar": {
+						done: make(chan struct{}),
+						sys:  &mockSys{},
+						name: "foobar",
+					},
+				},
+			},
+			wantUnregister: false,
+			wantFail:       true,
+		},
+		{
+			name: "Remove existing - disable fails",
+			db: &devices{
+				srv: &Server{
+					ds: &device.MockServer{},
+				},
+				db: map[string]*dev{
+					"foobar": {
+						done: make(chan struct{}),
+						sys: &mockSys{
+							wantFailClosedPacketSocket: true,
+						},
+						name: "foobar",
+					},
+				},
+			},
+			removeName: "foobar",
+			expected: &devices{
+				srv: &Server{
+					ds: &device.MockServer{
+						UnsubscribeCalled: true,
+						UnsubscribeName:   "foobar",
+					},
+				},
+				db: map[string]*dev{},
+			},
+			wantUnregister: true,
+			wantFail:       true,
+		},
+	}
+
+	for _, test := range tests {
+		err := test.db.removeDevice(test.removeName)
+
+		assert.Equal(t, test.wantUnregister, test.db.srv.ds.(*device.MockServer).UnsubscribeCalled, test.name)
+
+		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)
+		}
+
+		// Ignore some attributes
+		for i := range test.db.db {
+			test.db.db[i].srv = nil
+			test.db.db[i].helloMethod = nil
+			test.db.db[i].receiverMethod = nil
+			test.db.db[i].done = nil
+		}
+
+		assert.Equal(t, test.expected, test.db, test.name)
+	}
+}
+
+func TestDeviceAddDevice(t *testing.T) {
+	tests := []struct {
+		name         string
+		db           *devices
+		addIfCfg     *config.ISISInterfaceConfig
+		wantFail     bool
+		expected     *devices
+		wantRegister bool
+	}{
+		{
+			name: "Test #1",
+			db: &devices{
+				srv: &Server{
+					ds: &device.MockServer{},
+				},
+				db: map[string]*dev{
+					"foobar": {
+						name: "foobar",
+					},
+				},
+			},
+			addIfCfg: &config.ISISInterfaceConfig{
+				Name:    "baz",
+				Passive: true,
+				ISISLevel2Config: &config.ISISLevelConfig{
+					HelloInterval: 5,
+				},
+			},
+			expected: &devices{
+				srv: &Server{
+					ds: &device.MockServer{
+						Called: true,
+						Name:   "baz",
+					},
+				},
+				db: map[string]*dev{
+					"foobar": {
+						name: "foobar",
+					},
+					"baz": {
+						name:               "baz",
+						passive:            true,
+						supportedProtocols: []uint8{0xcc, 0x8e},
+						level2: &level{
+							HelloInterval: 5,
+						},
+					},
+				},
+			},
+			wantRegister: true,
+		},
+		{
+			name: "Test #2",
+			db: &devices{
+				srv: &Server{
+					ds: &device.MockServer{},
+				},
+				db: map[string]*dev{
+					"foobar": {
+						name: "foobar",
+					},
+				},
+			},
+			addIfCfg: &config.ISISInterfaceConfig{
+				Name:    "foobar",
+				Passive: true,
+			},
+			expected: &devices{
+				srv: &Server{
+					ds: &device.MockServer{
+						Called: true,
+						Name:   "baz",
+					},
+				},
+				db: map[string]*dev{
+					"foobar": {
+						name: "foobar",
+					},
+				},
+			},
+			wantRegister: false,
+			wantFail:     true,
+		},
+	}
+
+	for _, test := range tests {
+		err := test.db.addDevice(test.addIfCfg)
+
+		assert.Equal(t, test.wantRegister, test.db.srv.ds.(*device.MockServer).Called, test.name)
+
+		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)
+		}
+
+		// Ignore some attributes
+		for i := range test.db.db {
+			test.db.db[i].srv = nil
+			test.db.db[i].helloMethod = nil
+			test.db.db[i].receiverMethod = nil
+			test.db.db[i].done = nil
+		}
+
+		assert.Equal(t, test.expected, test.db, test.name)
+	}
+}
diff --git a/protocols/isis/server/lsdb.go b/protocols/isis/server/lsdb.go
new file mode 100644
index 0000000000000000000000000000000000000000..0bf96060ae54b15e9eaceb4d6793196eb859bc86
--- /dev/null
+++ b/protocols/isis/server/lsdb.go
@@ -0,0 +1,71 @@
+package server
+
+import (
+	"sync"
+
+	"github.com/bio-routing/bio-rd/protocols/isis/packet"
+	btime "github.com/bio-routing/bio-rd/util/time"
+)
+
+type lsdb struct {
+	srv    *Server
+	lsps   map[packet.LSPID]*lsdbEntry
+	lspsMu sync.RWMutex
+	done   chan struct{}
+	wg     sync.WaitGroup
+}
+
+type lsdbEntry struct {
+	lspdu    *packet.LSPDU
+	srmFlags map[*dev]struct{}
+	ssnFlags map[*dev]struct{}
+}
+
+func newLSDB(s *Server) *lsdb {
+	return &lsdb{
+		srv:  s,
+		done: make(chan struct{}),
+	}
+}
+
+func (l *lsdb) dispose() {
+	l.stop()
+	l.srv = nil
+}
+
+func (l *lsdb) start(t btime.Ticker) {
+	l.wg.Add(1)
+	go l.decrementRemainingLifetimesRoutine(t)
+}
+
+func (l *lsdb) stop() {
+	close(l.done)
+	l.wg.Wait()
+}
+
+func (l *lsdb) decrementRemainingLifetimesRoutine(t btime.Ticker) {
+	defer l.wg.Done()
+
+	for {
+		select {
+		case <-t.C():
+			l.decrementRemainingLifetimes()
+		case <-l.done:
+			return
+		}
+	}
+}
+
+func (l *lsdb) decrementRemainingLifetimes() {
+	l.lspsMu.Lock()
+	defer l.lspsMu.Unlock()
+
+	for lspid, lspdbEntry := range l.lsps {
+		if lspdbEntry.lspdu.RemainingLifetime <= 1 {
+			delete(l.lsps, lspid)
+			continue
+		}
+
+		lspdbEntry.lspdu.RemainingLifetime--
+	}
+}
diff --git a/protocols/isis/server/lsdb_test.go b/protocols/isis/server/lsdb_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..6c6c27aed5d2e5566a54e7b04904542f905b1b59
--- /dev/null
+++ b/protocols/isis/server/lsdb_test.go
@@ -0,0 +1,135 @@
+package server
+
+import (
+	"testing"
+
+	"github.com/bio-routing/bio-rd/protocols/isis/packet"
+	"github.com/bio-routing/bio-rd/protocols/isis/types"
+	btime "github.com/bio-routing/bio-rd/util/time"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestLSDPDispose(t *testing.T) {
+	l := newLSDB(&Server{})
+	l.dispose()
+
+	if l.srv != nil {
+		t.Errorf("srv reference not cleared")
+	}
+}
+
+func TestDecrementRemainingLifetimes(t *testing.T) {
+	tests := []struct {
+		name     string
+		lsdb     *lsdb
+		expected *lsdb
+	}{
+		{
+			name: "Test #1",
+			lsdb: &lsdb{
+				lsps: map[packet.LSPID]*lsdbEntry{
+					{
+						SystemID:     types.SystemID{10, 20, 30, 40, 50, 60},
+						PseudonodeID: 0x00,
+						LSPNumber:    1,
+					}: {
+						lspdu: &packet.LSPDU{
+							RemainingLifetime: 5,
+						},
+					},
+					{
+						SystemID:     types.SystemID{11, 22, 33, 44, 55, 66},
+						PseudonodeID: 0x00,
+						LSPNumber:    1,
+					}: {
+						lspdu: &packet.LSPDU{
+							RemainingLifetime: 1,
+						},
+					},
+				},
+			},
+			expected: &lsdb{
+				lsps: map[packet.LSPID]*lsdbEntry{
+					{
+						SystemID:     types.SystemID{10, 20, 30, 40, 50, 60},
+						PseudonodeID: 0x00,
+						LSPNumber:    1,
+					}: {
+						lspdu: &packet.LSPDU{
+							RemainingLifetime: 4,
+						},
+					},
+				},
+			},
+		},
+	}
+
+	for _, test := range tests {
+		test.lsdb.decrementRemainingLifetimes()
+		assert.Equal(t, test.expected, test.lsdb)
+	}
+}
+
+func TestStartStop(t *testing.T) {
+	db := &lsdb{
+		done: make(chan struct{}),
+		lsps: map[packet.LSPID]*lsdbEntry{
+			{
+				SystemID:     types.SystemID{10, 20, 30, 40, 50, 60},
+				PseudonodeID: 0x00,
+				LSPNumber:    1,
+			}: {
+				lspdu: &packet.LSPDU{
+					RemainingLifetime: 5,
+				},
+			},
+			{
+				SystemID:     types.SystemID{11, 22, 33, 44, 55, 66},
+				PseudonodeID: 0x00,
+				LSPNumber:    1,
+			}: {
+				lspdu: &packet.LSPDU{
+					RemainingLifetime: 1,
+				},
+			},
+		},
+	}
+	expected := &lsdb{
+		done: make(chan struct{}),
+		lsps: map[packet.LSPID]*lsdbEntry{
+			{
+				SystemID:     types.SystemID{10, 20, 30, 40, 50, 60},
+				PseudonodeID: 0x00,
+				LSPNumber:    1,
+			}: {
+				lspdu: &packet.LSPDU{
+					RemainingLifetime: 5,
+				},
+			},
+			{
+				SystemID:     types.SystemID{11, 22, 33, 44, 55, 66},
+				PseudonodeID: 0x00,
+				LSPNumber:    1,
+			}: {
+				lspdu: &packet.LSPDU{
+					RemainingLifetime: 1,
+				},
+			},
+		},
+	}
+	ticker := btime.NewMockTicker()
+	db.start(ticker)
+
+	expected.decrementRemainingLifetimes()
+	expected.decrementRemainingLifetimes()
+	expected.decrementRemainingLifetimes()
+	expected.decrementRemainingLifetimes()
+
+	ticker.Tick()
+	ticker.Tick()
+	ticker.Tick()
+	ticker.Tick()
+
+	db.stop()
+	assert.Equal(t, db.lsps, expected.lsps)
+}
diff --git a/protocols/isis/server/neighbor.go b/protocols/isis/server/neighbor.go
new file mode 100644
index 0000000000000000000000000000000000000000..62982b32d8938c80e4b978888fb80efb73a169ff
--- /dev/null
+++ b/protocols/isis/server/neighbor.go
@@ -0,0 +1,13 @@
+package server
+
+import "github.com/bio-routing/bio-rd/protocols/isis/types"
+
+type neighbor struct {
+	systemID               types.SystemID
+	dev                    *dev
+	holdingTime            uint16
+	localCircuitID         uint8
+	extendedLocalCircuitID uint32
+	ipInterfaceAddresses   []uint32
+	//fsm                    *FSM
+}
diff --git a/protocols/isis/server/neighbors.go b/protocols/isis/server/neighbors.go
new file mode 100644
index 0000000000000000000000000000000000000000..fcfb1549d2791cf744629f3ca01730db29ff1874
--- /dev/null
+++ b/protocols/isis/server/neighbors.go
@@ -0,0 +1,16 @@
+package server
+
+import (
+	"sync"
+
+	"github.com/bio-routing/bio-rd/protocols/isis/types"
+)
+
+type neighbors struct {
+	db   map[types.MACAddress]*neighbor
+	dbMu sync.RWMutex
+}
+
+func newNeighbors() *neighbors {
+	return nil
+}
diff --git a/protocols/isis/server/server.go b/protocols/isis/server/server.go
new file mode 100644
index 0000000000000000000000000000000000000000..fbac57bec5300f634f09a40fe49639b3e9948f30
--- /dev/null
+++ b/protocols/isis/server/server.go
@@ -0,0 +1,51 @@
+package server
+
+import (
+	"time"
+
+	"github.com/bio-routing/bio-rd/config"
+	"github.com/bio-routing/bio-rd/protocols/device"
+	btime "github.com/bio-routing/bio-rd/util/time"
+)
+
+//Server represents an ISIS server
+type Server struct {
+	config         *config.ISISConfig
+	sequenceNumber uint32
+	devices        *devices
+	lsdb           *lsdb
+	stop           chan struct{}
+	ds             device.Updater
+}
+
+func New(cfg *config.ISISConfig, ds device.Updater) *Server {
+	s := &Server{
+		config:         cfg,
+		ds:             ds,
+		sequenceNumber: 1,
+		stop:           make(chan struct{}),
+	}
+
+	s.devices = newDevices(s)
+	s.lsdb = newLSDB(s)
+	return s
+}
+
+func (s *Server) start() {
+	s.lsdb.start(btime.NewBIOTicker(time.Second))
+}
+
+func (s *Server) dispose() {
+	s.lsdb.dispose()
+	s.lsdb = nil
+}
+
+// AddInterface adds an interface to the ISIS Server
+func (s *Server) AddInterface(ifcfg *config.ISISInterfaceConfig) {
+	s.devices.addDevice(ifcfg)
+}
+
+// RemoveInterface removes an interface from the ISIS Server
+func (s *Server) RemoveInterface(name string) {
+	s.devices.removeDevice(name)
+}
diff --git a/protocols/isis/server/server_test.go b/protocols/isis/server/server_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..abb4e431abd516750a5a1e5e2b77073c236b8f9e
--- /dev/null
+++ b/protocols/isis/server/server_test.go
@@ -0,0 +1 @@
+package server
diff --git a/protocols/isis/server/sys.go b/protocols/isis/server/sys.go
new file mode 100644
index 0000000000000000000000000000000000000000..400542cf87a0f0efec827e1f711e2dc2ea482bb5
--- /dev/null
+++ b/protocols/isis/server/sys.go
@@ -0,0 +1,71 @@
+package server
+
+import (
+	"fmt"
+
+	"github.com/bio-routing/bio-rd/protocols/device"
+	"github.com/bio-routing/bio-rd/protocols/isis/types"
+)
+
+type sys interface {
+	openPacketSocket() error
+	closePacketSocket() error
+	mcastJoin(addr [6]byte) error
+	sendPacket(pkt []byte, dst [6]byte) error
+	recvPacket() (pkt []byte, src types.MACAddress, err error)
+}
+
+type bioSys struct {
+	socket int
+	device *device.Device
+}
+
+type mockSys struct {
+	wantFailOpenPacketSocket   bool
+	wantFailClosedPacketSocket bool
+	wantFailMcastJoin          bool
+	wantFailSendPacket         bool
+	wantFailRecvPacket         bool
+	closePacketSocketCalled    bool
+}
+
+func (m *mockSys) openPacketSocket() error {
+	if m.wantFailOpenPacketSocket {
+		return fmt.Errorf("Fail")
+	}
+
+	return nil
+}
+
+func (m *mockSys) closePacketSocket() error {
+	m.closePacketSocketCalled = true
+	if m.wantFailClosedPacketSocket {
+		return fmt.Errorf("Fail")
+	}
+
+	return nil
+}
+
+func (m *mockSys) mcastJoin(addr [6]byte) error {
+	if m.wantFailMcastJoin {
+		return fmt.Errorf("Fail")
+	}
+
+	return nil
+}
+
+func (m *mockSys) sendPacket(pkt []byte, dst [6]byte) error {
+	if m.wantFailSendPacket {
+		return fmt.Errorf("Fail")
+	}
+
+	return nil
+}
+
+func (m *mockSys) recvPacket() (pkt []byte, src types.MACAddress, err error) {
+	if m.wantFailRecvPacket {
+		return nil, [6]byte{}, fmt.Errorf("Fail")
+	}
+
+	return []byte{1, 2, 3}, [6]byte{10, 20, 30, 40, 50, 60}, nil
+}
diff --git a/protocols/isis/server/sys_darwin.go b/protocols/isis/server/sys_darwin.go
new file mode 100644
index 0000000000000000000000000000000000000000..eff533dd801c3ba378b7614c0bd3fc882a503dd1
--- /dev/null
+++ b/protocols/isis/server/sys_darwin.go
@@ -0,0 +1,27 @@
+package server
+
+import (
+	"fmt"
+
+	"github.com/bio-routing/bio-rd/protocols/isis/types"
+)
+
+func (b *bioSys) openPacketSocket() error {
+	return fmt.Errorf("Unsupported platform")
+}
+
+func (b *bioSys) closePacketSocket() error {
+	return fmt.Errorf("Unsupported platform")
+}
+
+func (b *bioSys) mcastJoin(addr [6]byte) error {
+	return fmt.Errorf("Unsupported platform")
+}
+
+func (b *bioSys) sendPacket(pkt []byte, dst [6]byte) error {
+	return fmt.Errorf("Unsupported platform")
+}
+
+func (b *bioSys) recvPacket() (pkt []byte, src types.MACAddress, err error) {
+	return nil, types.MACAddress{}, fmt.Errorf("Unsupported platform")
+}
diff --git a/protocols/isis/server/sys_linux.go b/protocols/isis/server/sys_linux.go
new file mode 100644
index 0000000000000000000000000000000000000000..bfe690cf6e036c459c324da9dbd74501f3027445
--- /dev/null
+++ b/protocols/isis/server/sys_linux.go
@@ -0,0 +1,78 @@
+package server
+
+import (
+	"fmt"
+	"syscall"
+
+	"github.com/bio-routing/bio-rd/protocols/isis/types"
+	"github.com/bio-routing/bio-rd/syscallwrappers"
+)
+
+func (b *bioSys) openPacketSocket() error {
+	socket, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_DGRAM, syscall.ETH_P_ALL)
+	if err != nil {
+		return fmt.Errorf("socket() failed: %v", err)
+	}
+	b.socket = socket
+
+	if syscallwrappers.SetBPFFilter(b.socket) != 0 {
+		return fmt.Errorf("Unable to set BPF filter")
+	}
+
+	if syscallwrappers.BindToInterface(b.socket, int(b.device.Index)) != 0 {
+		return fmt.Errorf("Unable to bind to interface")
+	}
+
+	return nil
+}
+
+func (b *bioSys) closePacketSocket() error {
+	return syscall.Close(b.socket)
+}
+
+func (b *bioSys) mcastJoin(addr [6]byte) error {
+	if syscallwrappers.JoinISISMcast(b.socket, int(b.device.Index)) != 0 {
+		return fmt.Errorf("setsockopt failed")
+	}
+
+	return nil
+}
+
+func (b *bioSys) recvPacket() (pkt []byte, src types.MACAddress, err error) {
+	buf := make([]byte, 1500)
+	nBytes, from, err := syscall.Recvfrom(b.socket, buf, 0)
+	if err != nil {
+		return nil, types.MACAddress{}, fmt.Errorf("recvfrom failed: %v", err)
+	}
+
+	ll := from.(*syscall.SockaddrLinklayer)
+	copy(src[:], ll.Addr[:6])
+
+	return buf[:nBytes], src, nil
+}
+
+func (b *bioSys) sendPacket(pkt []byte, dst [6]byte) error {
+	ll := syscall.SockaddrLinklayer{
+		Ifindex: int(b.device.Index),
+		Halen:   6, // MAC address length
+	}
+
+	for i := uint8(0); i < ll.Halen; i++ {
+		ll.Addr[i] = dst[i]
+	}
+
+	newPkt := []byte{
+		0xfe, 0xfe, 0x03,
+	}
+
+	newPkt = append(newPkt, pkt...)
+
+	ll.Protocol = uint16(len(newPkt))
+
+	err := syscall.Sendto(b.socket, newPkt, 0, &ll)
+	if err != nil {
+		return fmt.Errorf("sendto failed: %v", err)
+	}
+
+	return nil
+}
diff --git a/syscallwrappers/syscalls_linux.go b/syscallwrappers/syscalls_linux.go
new file mode 100644
index 0000000000000000000000000000000000000000..9649e2c3b75ce7e623bf7ce0e827e235f8d9c00b
--- /dev/null
+++ b/syscallwrappers/syscalls_linux.go
@@ -0,0 +1,85 @@
+package syscallwrappers
+
+/*
+#cgo CFLAGS: -I/usr/include
+#cgo LDFLAGS: -L/usr/lib
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <linux/if_packet.h>
+#include <linux/filter.h>
+#include <net/ethernet.h>
+#include <string.h>
+#include <arpa/inet.h>
+int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
+uint8_t ALL_L1_ISS[6] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x14};
+uint8_t ALL_L2_ISS[6] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x15};
+uint8_t ALL_P2P_ISS[6] = {0x09, 0x00, 0x2b, 0x00, 0x00, 0x5b};
+uint8_t ALL_ISS[6] = {0x09, 0x00, 0x2B, 0x00, 0x00, 0x05};
+uint8_t ALL_ESS[6] = {0x09, 0x00, 0x2B, 0x00, 0x00, 0x04};
+static struct sock_filter isisfilter[] = {
+	//{ 0x28, 0, 0, 0x0000000c }, { 0x25, 5, 0, 0x000005dc },
+	{ 0x28, 0, 0, 0x0000000e - 14 }, { 0x15, 0, 3, 0x0000fefe },
+	{ 0x30, 0, 0, 0x00000011 - 14 }, { 0x15, 0, 1, 0x00000083 },
+	{ 0x6, 0, 0, 0x00040000 }, { 0x6, 0, 0, 0x00000000 },
+};
+static struct sock_fprog bpf = {
+	.len = 6,
+	.filter = isisfilter,
+};
+int reg_bpf(int fd) {
+	return setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf));
+}
+int bind_to_interface(int fd, int ifindex) {
+	struct sockaddr_ll s_addr;
+	memset(&s_addr, 0, sizeof(struct sockaddr_ll));
+	s_addr.sll_family = AF_PACKET;
+	s_addr.sll_protocol = htons(ETH_P_ALL);
+	s_addr.sll_ifindex = ifindex;
+	return bind(fd, (struct sockaddr *)(&s_addr), sizeof(struct sockaddr_ll));
+}
+int isis_multicast_join(int fd, int registerto, int ifindex)
+{
+	struct packet_mreq mreq;
+	memset(&mreq, 0, sizeof(mreq));
+	mreq.mr_ifindex = ifindex;
+	if (registerto) {
+		mreq.mr_type = PACKET_MR_MULTICAST;
+		mreq.mr_alen = ETH_ALEN;
+		if (registerto == 1)
+			memcpy(&mreq.mr_address, ALL_L1_ISS, ETH_ALEN);
+		else if (registerto == 2)
+			memcpy(&mreq.mr_address, ALL_L2_ISS, ETH_ALEN);
+		else if (registerto == 3)
+			memcpy(&mreq.mr_address, ALL_ISS, ETH_ALEN);
+		else if (registerto == 4)
+			memcpy(&mreq.mr_address, ALL_P2P_ISS, ETH_ALEN);
+		else
+			memcpy(&mreq.mr_address, ALL_ESS, ETH_ALEN);
+	} else {
+		mreq.mr_type = PACKET_MR_ALLMULTI;
+	}
+	return setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(struct packet_mreq));
+}
+*/
+import "C"
+
+import (
+	"unsafe"
+)
+
+func SetBPFFilter(sockfd int) int {
+	return int(C.reg_bpf(C.int(sockfd)))
+}
+
+func SetSockOpt(sockfd int, level int, optName int, optVal uintptr, optLen int) int {
+	ptr := unsafe.Pointer(optVal)
+	return int(C.setsockopt(C.int(sockfd), C.int(level), C.int(optName), ptr, C.uint(optLen)))
+}
+
+func JoinISISMcast(sockfd int, ifIndex int) int {
+	return int(C.isis_multicast_join(C.int(sockfd), 4, C.int(ifIndex)))
+}
+
+func BindToInterface(sockfd int, ifIndex int) int {
+	return int(C.bind_to_interface(C.int(sockfd), C.int(ifIndex)))
+}
diff --git a/util/time/ticker.go b/util/time/ticker.go
new file mode 100644
index 0000000000000000000000000000000000000000..9bfb6cda97f3d7cbe3dfc65410f06d408ad4db78
--- /dev/null
+++ b/util/time/ticker.go
@@ -0,0 +1,64 @@
+package time
+
+import (
+	gotime "time"
+)
+
+// Ticker is a ticker interface that allows mocking tickers
+type Ticker interface {
+	C() <-chan gotime.Time
+	Stop()
+}
+
+// BIOTicker is a wrapper for time.Ticker
+type BIOTicker struct {
+	t  *gotime.Ticker
+	ch <-chan gotime.Time
+}
+
+// NewBIOTicker creates a new BIO ticker
+func NewBIOTicker(interval gotime.Duration) *BIOTicker {
+	bt := &BIOTicker{
+		t: gotime.NewTicker(interval),
+	}
+
+	bt.ch = bt.t.C
+	return bt
+}
+
+// C returns the channel
+func (bt *BIOTicker) C() <-chan gotime.Time {
+	return bt.ch
+}
+
+// Stop stops the ticker
+func (bt *BIOTicker) Stop() {
+	bt.t.Stop()
+}
+
+// MockTicker os a mocked ticker
+type MockTicker struct {
+	ch chan gotime.Time
+}
+
+// NewMockTicker creates a new mock ticker
+func NewMockTicker() *MockTicker {
+	return &MockTicker{
+		ch: make(chan gotime.Time),
+	}
+}
+
+// C gets the channel of the ticker
+func (m *MockTicker) C() <-chan gotime.Time {
+	return m.ch
+}
+
+// Stop is here to fulfill an interface
+func (m *MockTicker) Stop() {
+
+}
+
+// Tick lets the mock ticker tick
+func (m *MockTicker) Tick() {
+	m.ch <- gotime.Now()
+}