diff --git a/config/isis.go b/config/isis.go index 0ec4c5f05f6fb0c3b9301c2b85fd7a6701f434c0..59b2e5f8d3f580a6ec11e3177ae8803131b0dfc0 100644 --- a/config/isis.go +++ b/config/isis.go @@ -1,16 +1,17 @@ package config import ( - "time" "fmt" + "time" "github.com/bio-routing/bio-rd/protocols/isis/types" ) type ISISConfig struct { - NETs []NET - Interfaces []ISISInterfaceConfig + NETs []NET + Interfaces []ISISInterfaceConfig MinLSPTransmissionInterval time.Duration + TrafficEngineeringRouterID [4]byte } type ISISInterfaceConfig struct { diff --git a/examples/isis/main.go b/examples/isis/main.go index 1dcf63b003b09e41e703ff0f55501ffcac4072a3..2e5e7090bcbc8b3b8ea2af9dac127c66f7211c6d 100644 --- a/examples/isis/main.go +++ b/examples/isis/main.go @@ -33,8 +33,15 @@ func main() { Priority: 0, }, }, + { + Name: "lo", + Passive: true, + P2P: true, + ISISLevel2Config: &config.ISISLevelConfig{}, + }, }, MinLSPTransmissionInterval: 100, + TrafficEngineeringRouterID: [4]byte{10, 20, 30, 40}, } ds, err := device.New() diff --git a/protocols/isis/packet/csnp.go b/protocols/isis/packet/csnp.go index 76d982e6a863a80d877d7b62d026bbb3779bfe7c..7b6e649a51e19b34fbb32428d17fa14e694f189c 100644 --- a/protocols/isis/packet/csnp.go +++ b/protocols/isis/packet/csnp.go @@ -25,7 +25,6 @@ type CSNP struct { StartLSPID LSPID EndLSPID LSPID TLVs []TLV - //LSPEntries []LSPEntry } func compareLSPIDs(lspIDA, lspIDB LSPID) bool { @@ -89,7 +88,8 @@ func NewCSNPs(sourceID types.SourceID, lspEntries []*LSPEntry, maxPDULen int) [] res[0].StartLSPID = LSPID{} res[len(res)-1].EndLSPID = LSPID{ SystemID: types.SystemID{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, - PseudonodeID: 0xffff, + PseudonodeID: 0xff, + LSPNumber: 0xff, } return res diff --git a/protocols/isis/packet/csnp_test.go b/protocols/isis/packet/csnp_test.go index 653a0767e8b677d9fa3d1819198d070db9c285e4..0bf6aaca36526cb2c0fb72812c21a21a3b44001c 100644 --- a/protocols/isis/packet/csnp_test.go +++ b/protocols/isis/packet/csnp_test.go @@ -85,7 +85,8 @@ func TestNewCSNPs(t *testing.T) { StartLSPID: LSPID{}, EndLSPID: LSPID{ SystemID: types.SystemID{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, - PseudonodeID: 0xffff, + PseudonodeID: 0xff, + LSPNumber: 0xff, }, TLVs: []TLV{ &LSPEntriesTLV{ @@ -176,7 +177,8 @@ func TestNewCSNPs(t *testing.T) { }, EndLSPID: LSPID{ SystemID: types.SystemID{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, - PseudonodeID: 0xffff, + PseudonodeID: 0xff, + LSPNumber: 0xff, }, TLVs: []TLV{ &LSPEntriesTLV{ @@ -268,7 +270,8 @@ func TestNewCSNPs(t *testing.T) { }, EndLSPID: LSPID{ SystemID: types.SystemID{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, - PseudonodeID: 0xffff, + PseudonodeID: 0xff, + LSPNumber: 0xff, }, TLVs: []TLV{ &LSPEntriesTLV{ @@ -313,12 +316,14 @@ func TestCSNPSerialize(t *testing.T) { CircuitID: 0, }, StartLSPID: LSPID{ - SystemID: types.SystemID{11, 22, 33, 44, 55, 66}, - PseudonodeID: 256, + SystemID: types.SystemID{0x11, 0x22, 0x33, 0x44, 0x55, 0x66}, + PseudonodeID: 0, + LSPNumber: 0, }, EndLSPID: LSPID{ - SystemID: types.SystemID{11, 22, 33, 44, 55, 67}, - PseudonodeID: 255, + SystemID: types.SystemID{0x11, 0x22, 0x33, 0x44, 0x55, 0x67}, + PseudonodeID: 0xff, + LSPNumber: 0, }, TLVs: []TLV{ &LSPEntriesTLV{ @@ -326,29 +331,30 @@ func TestCSNPSerialize(t *testing.T) { TLVLength: 16, LSPEntries: []*LSPEntry{ { - SequenceNumber: 123, RemainingLifetime: 255, - LSPChecksum: 111, LSPID: LSPID{ SystemID: types.SystemID{10, 20, 30, 40, 50, 61}, PseudonodeID: 11, + LSPNumber: 0, }, + SequenceNumber: 123, + LSPChecksum: 111, }, }, }, }, }, expected: []byte{ - 0, 43, - 10, 20, 30, 40, 50, 60, 0, - 11, 22, 33, 44, 55, 66, 1, 0, - 11, 22, 33, 44, 55, 67, 0, 255, - 9, - 16, - 0, 255, - 10, 20, 30, 40, 50, 61, 0, 11, - 0, 0, 0, 123, - 0, 111, + 0, 43, // Length + 10, 20, 30, 40, 50, 60, 0, // SourceID + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0, 0, // Start LDP ID + 0x11, 0x22, 0x33, 0x44, 0x55, 0x67, 0xff, 0, // End LSP ID + 9, // TLV Type + 16, // TLV Length + 0, 255, // Remaining Lifetime + 10, 20, 30, 40, 50, 61, 11, 0, // LSP ID + 0, 0, 0, 123, // Seq. Nr. + 0, 111, // Checksum }, }, } diff --git a/protocols/isis/packet/hello.go b/protocols/isis/packet/hello.go index 7e2680261b6edfc443a2558973dc31eea5df8259..31cccb420c28bb85505154647e67ad6d4106a311 100644 --- a/protocols/isis/packet/hello.go +++ b/protocols/isis/packet/hello.go @@ -75,6 +75,18 @@ func (h *P2PHello) GetP2PAdjTLV() *P2PAdjacencyStateTLV { return nil } +// GetIPInterfaceAddressesesTLV gets the IP Interface Addresses TLV +func (h *P2PHello) GetIPInterfaceAddressesesTLV() *IPInterfaceAddressesTLV { + for _, tlv := range h.TLVs { + if tlv.Type() != IPInterfaceAddressesTLVType { + continue + } + + return tlv.(*IPInterfaceAddressesTLV) + } + return nil +} + // Serialize serializes a P2P Hello func (h *P2PHello) Serialize(buf *bytes.Buffer) { buf.WriteByte(h.CircuitType) diff --git a/protocols/isis/packet/hello_test.go b/protocols/isis/packet/hello_test.go index cb0f7840e948f38766ec9d630500d193ff2a65f4..b921f96b9945a4b9afe590051bf545dfcf3491bc 100644 --- a/protocols/isis/packet/hello_test.go +++ b/protocols/isis/packet/hello_test.go @@ -8,6 +8,40 @@ import ( "github.com/stretchr/testify/assert" ) +func TestGetIPInterfaceAddressesesTLV(t *testing.T) { + tests := []struct { + name string + hello *P2PHello + expected *IPInterfaceAddressesTLV + }{ + { + name: "Test #1", + hello: &P2PHello{ + TLVs: []TLV{ + NewIPInterfaceAddressesTLV([]uint32{111}), + }, + }, + expected: &IPInterfaceAddressesTLV{ + TLVType: IPInterfaceAddressesTLVType, + TLVLength: 4, + IPv4Addresses: []uint32{111}, + }, + }, + { + name: "Test #1", + hello: &P2PHello{ + TLVs: []TLV{}, + }, + expected: nil, + }, + } + + for _, test := range tests { + ret := test.hello.GetIPInterfaceAddressesesTLV() + assert.Equalf(t, test.expected, ret, "Test %q", test.name) + } +} + func TestGetP2PAdjTLV(t *testing.T) { tests := []struct { name string @@ -274,10 +308,10 @@ func TestDecodeISISHello(t *testing.T) { TLVLength: 2, NetworkLayerProtocolIDs: []byte{0xcc, 0x8e}, }, - &IPInterfaceAddressTLV{ - TLVType: 132, - TLVLength: 4, - IPv4Address: 167772160, + &IPInterfaceAddressesTLV{ + TLVType: 132, + TLVLength: 4, + IPv4Addresses: []uint32{167772160}, }, &AreaAddressesTLV{ TLVType: 1, diff --git a/protocols/isis/packet/isis_test.go b/protocols/isis/packet/isis_test.go index 4da6163a2ab758d49d5986e583caceef8ddd57ea..31f8038a5a63f63664df2e9ff93db8f04bd26fba 100644 --- a/protocols/isis/packet/isis_test.go +++ b/protocols/isis/packet/isis_test.go @@ -76,10 +76,10 @@ func TestDecode(t *testing.T) { TLVLength: 2, NetworkLayerProtocolIDs: []uint8{0xcc, 0x8e}, }, - &IPInterfaceAddressTLV{ - TLVType: 132, - TLVLength: 4, - IPv4Address: 3232235776, + &IPInterfaceAddressesTLV{ + TLVType: 132, + TLVLength: 4, + IPv4Addresses: []uint32{3232235776}, }, &AreaAddressesTLV{ TLVType: 1, diff --git a/protocols/isis/packet/lsp.go b/protocols/isis/packet/lsp.go index 24717949eb10ed5500ecaf46fe106cdeed2b240e..dc42e2281efc3e3e41b8aa6ff108d552cac72b89 100644 --- a/protocols/isis/packet/lsp.go +++ b/protocols/isis/packet/lsp.go @@ -19,7 +19,8 @@ const ( // LSPID represents a Link State Packet ID type LSPID struct { SystemID types.SystemID - PseudonodeID uint16 + PseudonodeID uint8 + LSPNumber uint8 } func (l *LSPID) String() string { @@ -29,7 +30,8 @@ func (l *LSPID) String() string { // Serialize serializes an LSPID func (l *LSPID) Serialize(buf *bytes.Buffer) { buf.Write(l.SystemID[:]) - buf.Write(convert.Uint16Byte(l.PseudonodeID)) + buf.WriteByte(l.PseudonodeID) + buf.WriteByte(l.LSPNumber) } // Compare returns 1 if l is bigger m, 0 if they are equal, else -1 diff --git a/protocols/isis/packet/lsp_entry.go b/protocols/isis/packet/lsp_entry.go index c8d8cd5626898fcba7ddcd9f6c8208e5792b1b52..8c1d259e1de3808b89973c6f24629c549ef0efbf 100644 --- a/protocols/isis/packet/lsp_entry.go +++ b/protocols/isis/packet/lsp_entry.go @@ -15,10 +15,10 @@ const ( // LSPEntry represents an LSP entry in a CSNP PDU type LSPEntry struct { - SequenceNumber uint32 RemainingLifetime uint16 - LSPChecksum uint16 LSPID LSPID + SequenceNumber uint32 + LSPChecksum uint16 } // Serialize serializes an LSPEntry diff --git a/protocols/isis/packet/lsp_test.go b/protocols/isis/packet/lsp_test.go index f8979b7ef6159c28f55f052a5dad485cea6403d0..878ca80054e6bda55a02dd2e21c203d452daddcb 100644 --- a/protocols/isis/packet/lsp_test.go +++ b/protocols/isis/packet/lsp_test.go @@ -266,6 +266,7 @@ func TestSetChecksum(t *testing.T) { LSPID: LSPID{ SystemID: types.SystemID{10, 20, 30, 40, 50, 60}, PseudonodeID: 0, + LSPNumber: 0, }, SequenceNumber: 1, TypeBlock: 3, diff --git a/protocols/isis/packet/tlv.go b/protocols/isis/packet/tlv.go index 3d5b6c88b2d01aeae6707352331e62cc97587e44..3dc3ece2175a29d9828775947889cf860143f2ae 100644 --- a/protocols/isis/packet/tlv.go +++ b/protocols/isis/packet/tlv.go @@ -56,8 +56,8 @@ func readTLVs(buf *bytes.Buffer) ([]TLV, error) { tlv, err = readChecksumTLV(buf, tlvType, tlvLength) case ProtocolsSupportedTLVType: tlv, err = readProtocolsSupportedTLV(buf, tlvType, tlvLength) - case IPInterfaceAddressTLVType: - tlv, err = readIPInterfaceAddressTLV(buf, tlvType, tlvLength) + case IPInterfaceAddressesTLVType: + tlv, err = readIPInterfaceAddressesTLV(buf, tlvType, tlvLength) case AreaAddressesTLVType: tlv, err = readAreaAddressesTLV(buf, tlvType, tlvLength) case P2PAdjacencyStateTLVType: diff --git a/protocols/isis/packet/tlv_dynamic_hostname.go b/protocols/isis/packet/tlv_dynamic_hostname.go index 2b5114e948aef784d74bdf51642619d582722433..1a2b74977718b3e9c99fc0bd6677956897b89bd6 100644 --- a/protocols/isis/packet/tlv_dynamic_hostname.go +++ b/protocols/isis/packet/tlv_dynamic_hostname.go @@ -32,6 +32,15 @@ func (d *DynamicHostNameTLV) Value() interface{} { return d } +// NewDynamicHostnameTLV creates a new dynamic hostname TLV +func NewDynamicHostnameTLV(name []byte) *DynamicHostNameTLV { + return &DynamicHostNameTLV{ + TLVType: DynamicHostNameTLVType, + TLVLength: uint8(len(name)), + Hostname: name, + } +} + func readDynamicHostnameTLV(buf *bytes.Buffer, tlvType uint8, tlvLength uint8) (*DynamicHostNameTLV, error) { pdu := &DynamicHostNameTLV{ TLVType: tlvType, diff --git a/protocols/isis/packet/tlv_extended_is_reachability.go b/protocols/isis/packet/tlv_extended_is_reachability.go index 20e77188a21fcee35f72427629ac78d4f27e42f5..20aa2d7ee8ace8485bbb00d29154d0044fa87192 100644 --- a/protocols/isis/packet/tlv_extended_is_reachability.go +++ b/protocols/isis/packet/tlv_extended_is_reachability.go @@ -1,12 +1,185 @@ package packet -const ExtendedISReachabilityType = 22 +import ( + "bytes" + "github.com/bio-routing/bio-rd/protocols/isis/types" + "github.com/taktv6/tflow2/convert" +) + +const ( + // ExtendedISReachabilityType is the type value of an Extended IS Reachability TLV + ExtendedISReachabilityType = 22 + + // LinkLocalRemoteIdentifiersSubTLVType is the type value of an Link Local/Remote Indentifiers Sub TLV + LinkLocalRemoteIdentifiersSubTLVType = 4 + + // IPv4InterfaceAddressSubTLVType is the type value of an IPv4 interface address sub TLV + IPv4InterfaceAddressSubTLVType = 6 + + // IPv4NeighborAddressSubTLVType is the type value of an IPv4 neighbor address sub TLV + IPv4NeighborAddressSubTLVType = 8 +) + +// ExtendedISReachabilityTLV is an Extended IS Reachability TLV type ExtendedISReachabilityTLV struct { - TLVType uint8 - TLVLength uint8 - SystemID [7]byte - WideMetrics [3]byte + TLVType uint8 + TLVLength uint8 + Neighbors []*ExtendedISReachabilityNeighbor +} + +// Type gets the type of the TLV +func (e *ExtendedISReachabilityTLV) Type() uint8 { + return e.TLVType +} + +// Length gets the length of the TLV +func (e *ExtendedISReachabilityTLV) Length() uint8 { + return e.TLVLength +} + +// Value returns the TLV itself +func (e *ExtendedISReachabilityTLV) Value() interface{} { + return e +} + +// Serialize serializes an ExtendedISReachabilityTLV +func (e *ExtendedISReachabilityTLV) Serialize(buf *bytes.Buffer) { + buf.WriteByte(e.TLVType) + buf.WriteByte(e.TLVLength) + for i := range e.Neighbors { + e.Neighbors[i].Serialize(buf) + } +} + +// ExtendedISReachabilityNeighbor is an extended IS Reachability Neighbor +type ExtendedISReachabilityNeighbor struct { + NeighborID types.SourceID + Metric [3]byte SubTLVLength uint8 - SubTLVs []interface{} + SubTLVs []TLV +} + +// Serialize serializes an ExtendedISReachabilityNeighbor +func (e *ExtendedISReachabilityNeighbor) Serialize(buf *bytes.Buffer) { + buf.Write(e.NeighborID.Serialize()) + buf.Write(e.Metric[:]) + buf.WriteByte(e.SubTLVLength) + for i := range e.SubTLVs { + e.SubTLVs[i].Serialize(buf) + } +} + +// NewExtendedISReachabilityNeighbor creates a new ExtendedISReachabilityNeighbor +func NewExtendedISReachabilityNeighbor(neighborID types.SourceID, metric [3]byte) *ExtendedISReachabilityNeighbor { + return &ExtendedISReachabilityNeighbor{ + NeighborID: neighborID, + Metric: metric, + SubTLVs: make([]TLV, 0), + } +} + +// NewExtendedISReachabilityTLV creates a new Extended IS Reachability TLV +func NewExtendedISReachabilityTLV() *ExtendedISReachabilityTLV { + e := &ExtendedISReachabilityTLV{ + TLVType: ExtendedISReachabilityType, + } + + return e +} + +// AddSubTLV adds a sub TLV to the ExtendedISReachabilityNeighbor +func (e *ExtendedISReachabilityNeighbor) AddSubTLV(tlv TLV) { + e.SubTLVLength += tlv.Length() + 2 + e.SubTLVs = append(e.SubTLVs, tlv) +} + +// LinkLocalRemoteIdentifiersSubTLV is an Link Local/Remote Identifiers Sub TLV +type LinkLocalRemoteIdentifiersSubTLV struct { + TLVType uint8 + TLVLength uint8 + Local uint32 + Remote uint32 +} + +// Type gets the type of the TLV +func (l *LinkLocalRemoteIdentifiersSubTLV) Type() uint8 { + return l.TLVType +} + +// Length gets the length of the TLV +func (l *LinkLocalRemoteIdentifiersSubTLV) Length() uint8 { + return l.TLVLength +} + +// Value returns the TLV itself +func (l *LinkLocalRemoteIdentifiersSubTLV) Value() interface{} { + return l +} + +// Serialize serializes an IPv4 address sub TLV +func (l *LinkLocalRemoteIdentifiersSubTLV) Serialize(buf *bytes.Buffer) { + buf.WriteByte(l.TLVType) + buf.WriteByte(l.TLVLength) + buf.Write(convert.Uint32Byte(l.Local)) + buf.Write(convert.Uint32Byte(l.Remote)) +} + +// NewLinkLocalRemoteIdentifiersSubTLV creates a new LinkLocalRemoteIdentifiersSubTLV +func NewLinkLocalRemoteIdentifiersSubTLV(local uint32, remote uint32) *LinkLocalRemoteIdentifiersSubTLV { + return &LinkLocalRemoteIdentifiersSubTLV{ + TLVType: LinkLocalRemoteIdentifiersSubTLVType, + TLVLength: 8, + Local: local, + Remote: remote, + } +} + +// IPv4AddressSubTLV is an IPv4 Address Sub TLV (used for both interface and neighbor) +type IPv4AddressSubTLV struct { + TLVType uint8 + TLVLength uint8 + Address uint32 +} + +// Type gets the type of the TLV +func (s *IPv4AddressSubTLV) Type() uint8 { + return s.TLVType +} + +// Length gets the length of the TLV +func (s *IPv4AddressSubTLV) Length() uint8 { + return s.TLVLength +} + +// Value returns the TLV itself +func (s *IPv4AddressSubTLV) Value() interface{} { + return s +} + +// Serialize serializes an IPv4 address sub TLV +func (s *IPv4AddressSubTLV) Serialize(buf *bytes.Buffer) { + buf.WriteByte(s.TLVType) + buf.WriteByte(s.TLVLength) + buf.Write(convert.Uint32Byte(s.Address)) +} + +// NewIPv4InterfaceAddressSubTLV creates a new IPv4 Interface Address Sub TLV +func NewIPv4InterfaceAddressSubTLV(addr uint32) *IPv4AddressSubTLV { + return newIPv4AddressSubTLV(IPv4InterfaceAddressSubTLVType, addr) +} + +// NewIPv4NeighborAddressSubTLV creates a new IPv4 Neighbor Address Sub TLV +func NewIPv4NeighborAddressSubTLV(addr uint32) *IPv4AddressSubTLV { + return newIPv4AddressSubTLV(IPv4NeighborAddressSubTLVType, addr) +} + +func newIPv4AddressSubTLV(tlvType uint8, addr uint32) *IPv4AddressSubTLV { + tlv := &IPv4AddressSubTLV{ + TLVType: tlvType, + TLVLength: 4, + Address: addr, + } + + return tlv } diff --git a/protocols/isis/packet/tlv_extended_is_reachability_test.go b/protocols/isis/packet/tlv_extended_is_reachability_test.go new file mode 100644 index 0000000000000000000000000000000000000000..bea8f12c77ae5ac58f48aeb17eb4fcf8306513e4 --- /dev/null +++ b/protocols/isis/packet/tlv_extended_is_reachability_test.go @@ -0,0 +1,101 @@ +package packet + +import ( + "bytes" + "testing" + + "github.com/bio-routing/bio-rd/protocols/isis/types" + "github.com/stretchr/testify/assert" +) + +func TestExtendedISReachabilityTLVSerialize(t *testing.T) { + +} + +func TestExtendedISReachabilityNeighborAddSubTLV(t *testing.T) { + tests := []struct { + name string + neighbor *ExtendedISReachabilityNeighbor + addTLV TLV + expected *ExtendedISReachabilityNeighbor + }{ + { + name: "Test #1", + neighbor: NewExtendedISReachabilityNeighbor(types.NewSourceID( + types.SystemID{1, 2, 3, 4, 5, 6}, + 0, + ), [3]byte{1, 2, 3}), + addTLV: &IPv4AddressSubTLV{ + TLVType: 6, + TLVLength: 4, + Address: 111, + }, + expected: &ExtendedISReachabilityNeighbor{ + NeighborID: types.NewSourceID( + types.SystemID{1, 2, 3, 4, 5, 6}, + 0, + ), + Metric: [3]byte{1, 2, 3}, + SubTLVLength: 6, + SubTLVs: []TLV{ + &IPv4AddressSubTLV{ + TLVType: 6, + TLVLength: 4, + Address: 111, + }, + }, + }, + }, + } + + for _, test := range tests { + test.neighbor.AddSubTLV(test.addTLV) + assert.Equal(t, test.expected, test.neighbor, test.name) + } +} + +func TestIPv4AddressSubTLVSerialize(t *testing.T) { + tests := []struct { + name string + tlv *IPv4AddressSubTLV + expected []byte + }{ + { + name: "Test #1", + tlv: &IPv4AddressSubTLV{ + TLVType: IPv4InterfaceAddressSubTLVType, + TLVLength: 4, + Address: 111, + }, + expected: []byte{6, 4, 0, 0, 0, 111}, + }, + } + + for _, test := range tests { + buf := bytes.NewBuffer(nil) + test.tlv.Serialize(buf) + assert.Equal(t, test.expected, buf.Bytes(), test.name) + } +} + +func TestNewIPv4InterfaceAddressSubTLV(t *testing.T) { + tlv := NewIPv4InterfaceAddressSubTLV(111) + expectred := &IPv4AddressSubTLV{ + TLVType: 6, + TLVLength: 4, + Address: 111, + } + + assert.Equal(t, expectred, tlv) +} + +func TestNewIPv4NeighborAddressSubTLV(t *testing.T) { + tlv := NewIPv4NeighborAddressSubTLV(111) + expectred := &IPv4AddressSubTLV{ + TLVType: 8, + TLVLength: 4, + Address: 111, + } + + assert.Equal(t, expectred, tlv) +} diff --git a/protocols/isis/packet/tlv_ip_interface_address.go b/protocols/isis/packet/tlv_ip_interface_address.go deleted file mode 100644 index 012688a8b9e049513fa4398e1897b288a22fc5b4..0000000000000000000000000000000000000000 --- a/protocols/isis/packet/tlv_ip_interface_address.go +++ /dev/null @@ -1,68 +0,0 @@ -package packet - -import ( - "bytes" - "fmt" - - "github.com/bio-routing/bio-rd/util/decode" - "github.com/taktv6/tflow2/convert" -) - -// IPInterfaceAddressTLVType is the type value of an IP interface address TLV -const IPInterfaceAddressTLVType = 132 - -// IPInterfaceAddressTLV represents an IP interface TLV -type IPInterfaceAddressTLV struct { - TLVType uint8 - TLVLength uint8 - IPv4Address uint32 -} - -// NewIPInterfaceAddressTLV creates a new IPInterfaceAddressTLV -func NewIPInterfaceAddressTLV(addr uint32) *IPInterfaceAddressTLV { - return &IPInterfaceAddressTLV{ - TLVType: IPInterfaceAddressTLVType, - TLVLength: 4, - IPv4Address: addr, - } -} - -func readIPInterfaceAddressTLV(buf *bytes.Buffer, tlvType uint8, tlvLength uint8) (*IPInterfaceAddressTLV, error) { - pdu := &IPInterfaceAddressTLV{ - TLVType: tlvType, - TLVLength: tlvLength, - } - - fields := []interface{}{ - &pdu.IPv4Address, - } - - err := decode.Decode(buf, fields) - if err != nil { - return nil, fmt.Errorf("Unable to decode fields: %v", err) - } - - return pdu, nil -} - -// Type returns the type of the TLV -func (i IPInterfaceAddressTLV) Type() uint8 { - return i.TLVType -} - -// Length returns the length of the TLV -func (i IPInterfaceAddressTLV) Length() uint8 { - return i.TLVLength -} - -// Value gets the TLV itself -func (i IPInterfaceAddressTLV) Value() interface{} { - return i -} - -// Serialize serializes an IP interfaces address TLV -func (i IPInterfaceAddressTLV) Serialize(buf *bytes.Buffer) { - buf.WriteByte(i.TLVType) - buf.WriteByte(i.TLVLength) - buf.Write(convert.Uint32Byte(i.IPv4Address)) -} diff --git a/protocols/isis/packet/tlv_ip_interface_addresses.go b/protocols/isis/packet/tlv_ip_interface_addresses.go new file mode 100644 index 0000000000000000000000000000000000000000..b68ba48766410c1663ab0662880898a34b98f4e2 --- /dev/null +++ b/protocols/isis/packet/tlv_ip_interface_addresses.go @@ -0,0 +1,72 @@ +package packet + +import ( + "bytes" + "fmt" + + "github.com/bio-routing/bio-rd/util/decode" + "github.com/taktv6/tflow2/convert" +) + +// IPInterfaceAddressesTLVType is the type value of an IP interface address TLV +const IPInterfaceAddressesTLVType = 132 + +// IPInterfaceAddressesTLV represents an IP interface TLV +type IPInterfaceAddressesTLV struct { + TLVType uint8 + TLVLength uint8 + IPv4Addresses []uint32 +} + +// NewIPInterfaceAddressesTLV creates a new IPInterfaceAddressesTLV +func NewIPInterfaceAddressesTLV(addrs []uint32) *IPInterfaceAddressesTLV { + return &IPInterfaceAddressesTLV{ + TLVType: IPInterfaceAddressesTLVType, + TLVLength: 4, + IPv4Addresses: addrs, + } +} + +func readIPInterfaceAddressesTLV(buf *bytes.Buffer, tlvType uint8, tlvLength uint8) (*IPInterfaceAddressesTLV, error) { + pdu := &IPInterfaceAddressesTLV{ + TLVType: tlvType, + TLVLength: tlvLength, + IPv4Addresses: make([]uint32, tlvLength/4), + } + + fields := make([]interface{}, len(pdu.IPv4Addresses)) + for i := range pdu.IPv4Addresses { + fields[i] = &pdu.IPv4Addresses[i] + } + + err := decode.Decode(buf, fields) + if err != nil { + return nil, fmt.Errorf("Unable to decode fields: %v", err) + } + + return pdu, nil +} + +// Type returns the type of the TLV +func (i IPInterfaceAddressesTLV) Type() uint8 { + return i.TLVType +} + +// Length returns the length of the TLV +func (i IPInterfaceAddressesTLV) Length() uint8 { + return i.TLVLength +} + +// Value gets the TLV itself +func (i IPInterfaceAddressesTLV) Value() interface{} { + return i +} + +// Serialize serializes an IP interfaces address TLV +func (i IPInterfaceAddressesTLV) Serialize(buf *bytes.Buffer) { + buf.WriteByte(i.TLVType) + buf.WriteByte(i.TLVLength) + for j := range i.IPv4Addresses { + buf.Write(convert.Uint32Byte(i.IPv4Addresses[j])) + } +} diff --git a/protocols/isis/packet/tlv_ip_interface_address_test.go b/protocols/isis/packet/tlv_ip_interface_addresses_test.go similarity index 67% rename from protocols/isis/packet/tlv_ip_interface_address_test.go rename to protocols/isis/packet/tlv_ip_interface_addresses_test.go index 432437ce0b943adec0947b3d247afdad411d15d2..b9e51e3d7f6366bd472cf65f7cff8fd8695f730c 100644 --- a/protocols/isis/packet/tlv_ip_interface_address_test.go +++ b/protocols/isis/packet/tlv_ip_interface_addresses_test.go @@ -10,22 +10,22 @@ import ( func TestNewIPInterfaceAddressTLV(t *testing.T) { tests := []struct { name string - addr uint32 - expected *IPInterfaceAddressTLV + addrs []uint32 + expected *IPInterfaceAddressesTLV }{ { - name: "Test #1", - addr: 100, - expected: &IPInterfaceAddressTLV{ - TLVType: 132, - TLVLength: 4, - IPv4Address: 100, + name: "Test #1", + addrs: []uint32{100}, + expected: &IPInterfaceAddressesTLV{ + TLVType: 132, + TLVLength: 4, + IPv4Addresses: []uint32{100}, }, }, } for _, test := range tests { - tlv := NewIPInterfaceAddressTLV(test.addr) + tlv := NewIPInterfaceAddressesTLV(test.addrs) assert.Equalf(t, test.expected, tlv, "Test %q", test.name) } } @@ -36,7 +36,7 @@ func TestReadIPInterfaceAddressTLV(t *testing.T) { input []byte tlvLength uint8 wantFail bool - expected *IPInterfaceAddressTLV + expected *IPInterfaceAddressesTLV }{ { name: "Full", @@ -44,10 +44,10 @@ func TestReadIPInterfaceAddressTLV(t *testing.T) { 0, 0, 0, 100, }, tlvLength: 4, - expected: &IPInterfaceAddressTLV{ - TLVType: 132, - TLVLength: 4, - IPv4Address: 100, + expected: &IPInterfaceAddressesTLV{ + TLVType: 132, + TLVLength: 4, + IPv4Addresses: []uint32{100}, }, }, { @@ -62,7 +62,7 @@ func TestReadIPInterfaceAddressTLV(t *testing.T) { for _, test := range tests { buf := bytes.NewBuffer(test.input) - tlv, err := readIPInterfaceAddressTLV(buf, 132, test.tlvLength) + tlv, err := readIPInterfaceAddressesTLV(buf, 132, test.tlvLength) if err != nil { if test.wantFail { diff --git a/protocols/isis/packet/tlv_traffic_engineering_router_id.go b/protocols/isis/packet/tlv_traffic_engineering_router_id.go new file mode 100644 index 0000000000000000000000000000000000000000..0720607b586fe9b22a70990c1d945b571971ae3f --- /dev/null +++ b/protocols/isis/packet/tlv_traffic_engineering_router_id.go @@ -0,0 +1,69 @@ +package packet + +import ( + "bytes" + "fmt" + + "github.com/bio-routing/bio-rd/util/decode" +) + +const ( + // TrafficEngineeringRouterIDTLVType is the type value of an Traffic Engineering Router ID TLV + TrafficEngineeringRouterIDTLVType = 134 +) + +// TrafficEngineeringRouterIDTLV is a Traffic Engineering Router ID TLV +type TrafficEngineeringRouterIDTLV struct { + TLVType uint8 + TLVLength uint8 + Address [4]byte +} + +// NewTrafficEngineeringRouterIDTLV creates a new TrafficEngineeringRouterIDTLV +func NewTrafficEngineeringRouterIDTLV(addr [4]byte) *TrafficEngineeringRouterIDTLV { + return &TrafficEngineeringRouterIDTLV{ + TLVType: TrafficEngineeringRouterIDTLVType, + TLVLength: 4, + Address: addr, + } +} + +// Serialize serializes a TrafficEngineeringRouterIDTLV +func (t *TrafficEngineeringRouterIDTLV) Serialize(buf *bytes.Buffer) { + buf.WriteByte(t.TLVType) + buf.WriteByte(t.TLVLength) + buf.Write(t.Address[:]) +} + +func readTrafficEngineeringRouterIDTLV(buf *bytes.Buffer, tlvType uint8, tlvLength uint8) (*TrafficEngineeringRouterIDTLV, error) { + pdu := &TrafficEngineeringRouterIDTLV{ + TLVType: tlvType, + TLVLength: tlvLength, + } + + fields := []interface{}{ + pdu.Address[:], + } + + err := decode.Decode(buf, fields) + if err != nil { + return nil, fmt.Errorf("Unable to decode fields: %v", err) + } + + return pdu, nil +} + +// Type gets the type of the TLV +func (t TrafficEngineeringRouterIDTLV) Type() uint8 { + return t.TLVType +} + +// Length gets the length of the TLV +func (t TrafficEngineeringRouterIDTLV) Length() uint8 { + return t.TLVLength +} + +// Value gets the TLV itself +func (t TrafficEngineeringRouterIDTLV) Value() interface{} { + return t +} diff --git a/protocols/isis/packet/tlv_traffic_engineering_router_id_test.go b/protocols/isis/packet/tlv_traffic_engineering_router_id_test.go new file mode 100644 index 0000000000000000000000000000000000000000..51875f6a4f146fd7d637505d295386b6f020612b --- /dev/null +++ b/protocols/isis/packet/tlv_traffic_engineering_router_id_test.go @@ -0,0 +1,100 @@ +package packet + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNewTrafficEngineeringRouterIDTLV(t *testing.T) { + tests := []struct { + name string + addr [4]byte + expected *TrafficEngineeringRouterIDTLV + }{ + { + name: "Test #1", + addr: [4]byte{10, 20, 30, 40}, + expected: &TrafficEngineeringRouterIDTLV{ + TLVType: 134, + TLVLength: 4, + Address: [4]byte{10, 20, 30, 40}, + }, + }, + } + + for _, test := range tests { + tlv := NewTrafficEngineeringRouterIDTLV(test.addr) + assert.Equal(t, test.expected, tlv, test.name) + } +} + +func TestTrafficEngineeringRouterIDTLVSerialize(t *testing.T) { + tests := []struct { + name string + tlv *TrafficEngineeringRouterIDTLV + expected []byte + }{ + { + name: "Test #1", + tlv: &TrafficEngineeringRouterIDTLV{ + TLVType: 134, + TLVLength: 4, + Address: [4]byte{10, 0, 0, 123}, + }, + expected: []byte{134, 4, 10, 0, 0, 123}, + }, + } + + for _, test := range tests { + buf := bytes.NewBuffer(nil) + test.tlv.Serialize(buf) + assert.Equal(t, test.expected, buf.Bytes(), test.name) + } +} + +func TestReadTrafficEngineeringRouterIDTLV(t *testing.T) { + tests := []struct { + name string + tlvType uint8 + tlvLength uint8 + pkt []byte + expected *TrafficEngineeringRouterIDTLV + wantFail bool + }{ + { + name: "Normal packet", + tlvType: 134, + tlvLength: 4, + pkt: []byte{ + 1, 2, 3, 4, + }, + expected: &TrafficEngineeringRouterIDTLV{ + TLVType: 134, + TLVLength: 4, + Address: [4]byte{1, 2, 3, 4}, + }, + }, + } + + for _, test := range tests { + buf := bytes.NewBuffer(test.pkt) + tlv, err := readTrafficEngineeringRouterIDTLV(buf, test.tlvType, test.tlvLength) + 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) + continue + } + + assert.Equal(t, test.expected, tlv, test.name) + } +} diff --git a/protocols/isis/packet/tlv_unknown.go b/protocols/isis/packet/tlv_unknown.go index 9488ac1ff700e6e15ee8c06256b4b16af206f2a9..13a469d63ba311b5bd5c2f87398a442ca52fdd2d 100644 --- a/protocols/isis/packet/tlv_unknown.go +++ b/protocols/isis/packet/tlv_unknown.go @@ -25,7 +25,7 @@ func readUnknownTLV(buf *bytes.Buffer, tlvType uint8, tlvLength uint8) (*Unknown } if n != int(tlvLength) { - return nil, fmt.Errorf("Read incomplete") + return nil, fmt.Errorf("Read of TLVType %d incomplete", pdu.TLVType) } return pdu, nil diff --git a/protocols/isis/server/interface.go b/protocols/isis/server/interface.go index 97064fe2db801813a588f07e1361c7da31c5b4af..5049a44415422219fd81f884cd5080ea40c5fc23 100644 --- a/protocols/isis/server/interface.go +++ b/protocols/isis/server/interface.go @@ -7,6 +7,7 @@ import ( "time" "github.com/bio-routing/bio-rd/protocols/device" + "github.com/bio-routing/tflow2/convert" "github.com/bio-routing/bio-rd/config" "github.com/bio-routing/bio-rd/protocols/isis/packet" @@ -51,6 +52,11 @@ type level struct { neighborsMu sync.RWMutex } +func (l *level) get3ByteMetric() [3]byte { + bytes := convert.Uint32Byte(l.Metric) + return [3]byte{bytes[1], bytes[2], bytes[3]} +} + func (ifa *netIf) DeviceUpdate(d *device.Device) { fmt.Printf("ISIS: DeviceUpdate() called\n") ifa.deviceMu.Lock() @@ -229,6 +235,13 @@ func (ifa *netIf) processIngressP2PHello(pkt *packet.ISISPacket, src types.MACAd if _, ok := ifa.l2.neighbors[src]; !ok { p2pAdjTLV := hello.GetP2PAdjTLV() if p2pAdjTLV == nil { + log.Warningf("Received a P2P hello PDU without P2P Adjacency TLV on %s", ifa.name) + return + } + + ipIfAddrTLV := hello.GetIPInterfaceAddressesesTLV() + if ipIfAddrTLV == nil { + log.Warningf("Received a P2P hello PDU without IP Interface Addresses TLV on %s", ifa.name) return } @@ -238,6 +251,7 @@ func (ifa *netIf) processIngressP2PHello(pkt *packet.ISISPacket, src types.MACAd holdingTime: hello.HoldingTimer, localCircuitID: hello.LocalCircuitID, extendedLocalCircuitID: p2pAdjTLV.ExtendedLocalCircuitID, + ipInterfaceAddresses: ipIfAddrTLV.IPv4Addresses, } fmt.Printf("DEBUG: extendedLocalCircuitID: %v\n", p2pAdjTLV.ExtendedLocalCircuitID) @@ -340,7 +354,7 @@ func (ifa *netIf) p2pHelloTLVs() []packet.TLV { protocolsSupportedTLV := packet.NewProtocolsSupportedTLV(ifa.supportedProtocols) areaAddressesTLV := packet.NewAreaAddressesTLV(ifa.isisServer.getAreas()) - ipInterfaceAddressesTLV := packet.NewIPInterfaceAddressTLV(3232235523) //FIXME: Insert address automatically + ipInterfaceAddressesTLV := packet.NewIPInterfaceAddressesTLV([]uint32{3232235523}) //FIXME: Insert address automatically return []packet.TLV{ p2pAdjStateTLV, diff --git a/protocols/isis/server/interface_test.go b/protocols/isis/server/interface_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a573c91d97020a1f37f28a2539c1f19ce0bc9a8b --- /dev/null +++ b/protocols/isis/server/interface_test.go @@ -0,0 +1,35 @@ +package server + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGet3ByteMetric(t *testing.T) { + tests := []struct { + name string + level *level + expected [3]byte + }{ + { + name: "Test #1", + level: &level{ + Metric: 512, + }, + expected: [3]byte{0, 2, 0}, + }, + { + name: "Test #2", + level: &level{ + Metric: 513, + }, + expected: [3]byte{0, 2, 1}, + }, + } + + for _, test := range tests { + res := test.level.get3ByteMetric() + assert.Equal(t, test.expected, res, test.name) + } +} diff --git a/protocols/isis/server/lsdb.go b/protocols/isis/server/lsdb.go index 9dacc716c21ebb4b5e70f23b1d33f2a9f0a3120c..7e48bdbc5de0d48a62cce9529b143be3c73d0aec 100644 --- a/protocols/isis/server/lsdb.go +++ b/protocols/isis/server/lsdb.go @@ -2,6 +2,7 @@ package server import ( "fmt" + "os" "sync" "github.com/bio-routing/bio-rd/protocols/isis/packet" @@ -55,6 +56,15 @@ func newLSDB(server *ISISServer) *lsdb { protoSupportedTLV := packet.NewProtocolsSupportedTLV([]uint8{0xcc, 0x8e}) localLSPDU.TLVs = append(localLSPDU.TLVs, protoSupportedTLV) + hostname, err := os.Hostname() + if err == nil { + hostnameTLV := packet.NewDynamicHostnameTLV([]byte(hostname)) + localLSPDU.TLVs = append(localLSPDU.TLVs, hostnameTLV) + } + + ipInterfaceAddrTLV := packet.NewIPInterfaceAddressesTLV([]uint32{3232235520}) + localLSPDU.TLVs = append(localLSPDU.TLVs, ipInterfaceAddrTLV) + localLSPDU.SetChecksum() lsdb.lsps[localLSPID] = newLSDBEntry(localLSPDU) return lsdb @@ -246,7 +256,8 @@ func (lsdb *lsdb) getCSNP() *packet.CSNP { StartLSPID: packet.LSPID{}, EndLSPID: packet.LSPID{ SystemID: types.SystemID{255, 255, 255, 255, 255, 255}, - PseudonodeID: 65535, + PseudonodeID: 255, + LSPNumber: 255, }, TLVs: make([]packet.TLV, 0, 1), } diff --git a/protocols/isis/server/lsp.go b/protocols/isis/server/lsp.go new file mode 100644 index 0000000000000000000000000000000000000000..b6d107a5d8130ccde1af358b9318a7f0915305b7 --- /dev/null +++ b/protocols/isis/server/lsp.go @@ -0,0 +1,63 @@ +package server + +import ( + "os" + + "github.com/bio-routing/bio-rd/protocols/isis/packet" +) + +func (s *ISISServer) createLSPDU() *packet.LSPDU { + lspID := packet.LSPID{ + SystemID: s.config.NETs[0].SystemID, + PseudonodeID: 0x00, + } + + lspdu := &packet.LSPDU{ + Length: packet.LSPDUMinLen, + RemainingLifetime: 3600, + LSPID: lspID, + SequenceNumber: 1, + Checksum: 0, + TypeBlock: 0x03, + TLVs: make([]packet.TLV, 0), + } + + areaAddressesTLV := packet.NewAreaAddressesTLV(s.getAreas()) + lspdu.TLVs = append(lspdu.TLVs, areaAddressesTLV) + + protoSupportedTLV := packet.NewProtocolsSupportedTLV([]uint8{0xcc, 0x8e}) + lspdu.TLVs = append(lspdu.TLVs, protoSupportedTLV) + + hostname, err := os.Hostname() + if err == nil { + hostnameTLV := packet.NewDynamicHostnameTLV([]byte(hostname)) + lspdu.TLVs = append(lspdu.TLVs, hostnameTLV) + } + + ipInterfaceAddrTLV := packet.NewIPInterfaceAddressesTLV([]uint32{3232235520}) + lspdu.TLVs = append(lspdu.TLVs, ipInterfaceAddrTLV) + + teRouterIDTLV := packet.NewTrafficEngineeringRouterIDTLV(s.config.TrafficEngineeringRouterID) + lspdu.TLVs = append(lspdu.TLVs, teRouterIDTLV) + + extISReachTLV := s.createExtendedISReachabilityTLV() + lspdu.TLVs = append(lspdu.TLVs, extISReachTLV) + + lspdu.SetChecksum() + return lspdu +} + +func (s *ISISServer) createExtendedISReachabilityTLV() *packet.ExtendedISReachabilityTLV { + tlv := packet.NewExtendedISReachabilityTLV() + + s.interfacesMu.RLock() + defer s.interfacesMu.RUnlock() + for i := range s.interfaces { + for j := range s.interfaces[i].l2.neighbors { + n := s.interfaces[i].l2.neighbors[j].getExtendedISReachabilityNeighbor() + tlv.Neighbors = append(tlv.Neighbors, n) + } + } + + return tlv +} diff --git a/protocols/isis/server/neighbor.go b/protocols/isis/server/neighbor.go index 33ed7bb8637a79d6d839c985c6b84aa1b4358845..fe1752f5c932127f7280a439500cda87e50f1e37 100644 --- a/protocols/isis/server/neighbor.go +++ b/protocols/isis/server/neighbor.go @@ -1,6 +1,7 @@ package server import ( + "github.com/bio-routing/bio-rd/protocols/isis/packet" "github.com/bio-routing/bio-rd/protocols/isis/types" ) @@ -11,6 +12,7 @@ type neighbor struct { localCircuitID uint8 extendedLocalCircuitID uint32 fsm *FSM + ipInterfaceAddresses []uint32 } func newNeighbor(sysID types.SystemID, ifa *netIf, extendedLocalCircuitID uint32) *neighbor { @@ -18,5 +20,31 @@ func newNeighbor(sysID types.SystemID, ifa *netIf, extendedLocalCircuitID uint32 systemID: sysID, ifa: ifa, extendedLocalCircuitID: extendedLocalCircuitID, + ipInterfaceAddresses: make([]uint32, 0), } } + +func (n *neighbor) getExtendedISReachabilityNeighbor() *packet.ExtendedISReachabilityNeighbor { + eirn := packet.NewExtendedISReachabilityNeighbor( + types.NewSourceID( + n.systemID, + n.localCircuitID, + ), + n.ifa.l2.get3ByteMetric(), + ) + + for i := range n.ifa.device.Addrs { + if !n.ifa.device.Addrs[i].Addr().IsIPv4() { + continue + } + + eirn.AddSubTLV(packet.NewIPv4InterfaceAddressSubTLV(n.ifa.device.Addrs[i].Addr().ToUint32())) + } + + for i := range n.ipInterfaceAddresses { + eirn.AddSubTLV(packet.NewIPv4NeighborAddressSubTLV(n.ipInterfaceAddresses[i])) + } + + eirn.AddSubTLV(packet.NewLinkLocalRemoteIdentifiersSubTLV(uint32(n.ifa.device.Index), n.extendedLocalCircuitID)) + return eirn +}