diff --git a/protocols/isis/packet/header_test.go b/protocols/isis/packet/header_test.go new file mode 100644 index 0000000000000000000000000000000000000000..2d39eb688a0fcab0f9d5be0c55b463a7761b6340 --- /dev/null +++ b/protocols/isis/packet/header_test.go @@ -0,0 +1,46 @@ +package packet + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestHeaderEncode(t *testing.T) { + tests := []struct { + name string + input *ISISHeader + expected []byte + }{ + { + name: "Test #1", + input: &ISISHeader{ + ProtoDiscriminator: 0x83, + LengthIndicator: 27, + ProtocolIDExtension: 0, + IDLength: 0, + PDUType: 16, + Version: 1, + MaxAreaAddresses: 0, + }, + expected: []byte{ + 0x83, + 27, + 0, + 0, + 16, + 1, + 0, + 0, + }, + }, + } + + for _, test := range tests { + buf := bytes.NewBuffer(nil) + test.input.Serialize(buf) + res := buf.Bytes() + assert.Equalf(t, test.expected, res, "%s failed", test.name) + } +} diff --git a/protocols/isis/packet/hello.go b/protocols/isis/packet/hello.go index 5d3494a90186c7d51117b22d48d8bd79e973bc3f..d40d4383ce4b9931843c27e2e06109f93cc3d544 100644 --- a/protocols/isis/packet/hello.go +++ b/protocols/isis/packet/hello.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" + "github.com/bio-routing/bio-rd/protocols/isis/types" "github.com/taktv6/tflow2/convert" ) @@ -19,7 +20,7 @@ type L2Hello struct { type P2PHello struct { CircuitType uint8 - SystemID [6]byte + SystemID types.SystemID HoldingTimer uint16 PDULength uint16 LocalCircuitID uint8 @@ -28,7 +29,7 @@ type P2PHello struct { const ( P2PHelloMinSize = 20 - ISISHeaderSize = 8 + ISISHeaderSize = 8 L2CircuitType = 2 ) diff --git a/protocols/isis/packet/isis_test.go b/protocols/isis/packet/isis_test.go index 2d39eb688a0fcab0f9d5be0c55b463a7761b6340..21a9495c506dc991bb5b9650162b870940214520 100644 --- a/protocols/isis/packet/isis_test.go +++ b/protocols/isis/packet/isis_test.go @@ -4,43 +4,161 @@ import ( "bytes" "testing" + "github.com/bio-routing/bio-rd/protocols/isis/types" "github.com/stretchr/testify/assert" ) -func TestHeaderEncode(t *testing.T) { +func TestDecode(t *testing.T) { tests := []struct { name string - input *ISISHeader - expected []byte + input []byte + wantFail bool + expected *ISISPacket }{ { - name: "Test #1", - input: &ISISHeader{ - ProtoDiscriminator: 0x83, - LengthIndicator: 27, - ProtocolIDExtension: 0, - IDLength: 0, - PDUType: 16, - Version: 1, - MaxAreaAddresses: 0, + name: "P2P Hello", + input: []byte{ + // LLC + 0xfe, // DSAP + 0xfe, // SSAP + 0x03, // Control Fields + + // Header + 0x83, + 20, + 1, + 0, + 17, // PDU Type P2P Hello + 1, + 0, + 0, + + // P2P Hello + 02, + 0, 0, 0, 0, 0, 2, + 0, 27, + 0, 50, + 1, + + //TLVs + 240, 5, 0x02, 0x00, 0x00, 0x01, 0x4b, + 129, 2, 0xcc, 0x8e, + 132, 4, 192, 168, 1, 0, + 1, 6, 0x05, 0x49, 0x00, 0x01, 0x00, 0x10, + 211, 3, 0, 0, 0, }, - expected: []byte{ + wantFail: false, + expected: &ISISPacket{ + Header: &ISISHeader{ + ProtoDiscriminator: 0x83, + LengthIndicator: 20, + ProtocolIDExtension: 1, + IDLength: 0, + PDUType: 17, + Version: 1, + MaxAreaAddresses: 0, + }, + Body: &P2PHello{ + CircuitType: 2, + SystemID: types.SystemID{0, 0, 0, 0, 0, 2}, + HoldingTimer: 27, + PDULength: 50, + LocalCircuitID: 1, + TLVs: []TLV{ + &P2PAdjacencyStateTLV{ + TLVType: 240, + TLVLength: 5, + AdjacencyState: 2, + ExtendedLocalCircuitID: 0x0000014b, + }, + &ProtocolsSupportedTLV{ + TLVType: 129, + TLVLength: 2, + NerworkLayerProtocolIDs: []uint8{0xcc, 0x8e}, + }, + &IPInterfaceAddressTLV{ + TLVType: 132, + TLVLength: 4, + IPv4Address: 3232235776, + }, + &AreaAddressesTLV{ + TLVType: 1, + TLVLength: 6, + AreaIDs: []types.AreaID{ + { + 0x49, 0x00, 0x01, 0x00, 0x10, + }, + }, + }, + &UnknownTLV{ + TLVType: 211, + TLVLength: 3, + TLVValue: []byte{0, 0, 0}, + }, + }, + }, + }, + }, + { + name: "Incomplete header", + input: []byte{ + // LLC + 0xfe, // DSAP + 0xfe, // SSAP + 0x03, // Control Fields + + // Header 0x83, - 27, + 20, + 1, 0, + }, + wantFail: true, + }, + { + name: "Incomplete P2P Hello", + input: []byte{ + // LLC + 0xfe, // DSAP + 0xfe, // SSAP + 0x03, // Control Fields + + // Header + 0x83, + 20, + 1, 0, - 16, + 17, // PDU Type P2P Hello 1, 0, 0, + + // P2P Hello + 02, + 0, 0, 0, 0, 0, 2, + 0, 27, }, + wantFail: true, }, } for _, test := range tests { - buf := bytes.NewBuffer(nil) - test.input.Serialize(buf) - res := buf.Bytes() - assert.Equalf(t, test.expected, res, "%s failed", test.name) + buf := bytes.NewBuffer(test.input) + pkt, err := Decode(buf) + + 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.Equalf(t, test.expected, pkt, "Test %q", test.name) } } diff --git a/protocols/isis/packet/tlv.go b/protocols/isis/packet/tlv.go index 4499391df08e0da84401f1c1c1068005c4f0414e..6a545c48009f4f1f656dd5df609f434c0c4b37b8 100644 --- a/protocols/isis/packet/tlv.go +++ b/protocols/isis/packet/tlv.go @@ -3,8 +3,6 @@ package packet import ( "bytes" "fmt" - - log "github.com/sirupsen/logrus" ) // TLV is an interface that all TLVs must fulfill @@ -67,13 +65,7 @@ func readTLVs(buf *bytes.Buffer) ([]TLV, error) { case P2PAdjacencyStateTLVType: tlv, _, err = readP2PAdjacencyStateTLV(buf, tlvType, tlvLength) default: - log.Warningf("Unknown type: %d", tlvType) - nirvana := make([]byte, tlvLength) - _, err = buf.Read(nirvana) - if err != nil { - return nil, fmt.Errorf("Unable to read: %v", err) - } - continue + tlv, err = readUnknownTLV(buf, tlvType, tlvLength) } if err != nil { diff --git a/protocols/isis/packet/tlv_unknown.go b/protocols/isis/packet/tlv_unknown.go new file mode 100644 index 0000000000000000000000000000000000000000..b89b7f3bae13cab6727ed3ac1ec98cf9e713d1f2 --- /dev/null +++ b/protocols/isis/packet/tlv_unknown.go @@ -0,0 +1,54 @@ +package packet + +import ( + "bytes" + "fmt" +) + +// UnknownTLV represents an unknown TLV +type UnknownTLV struct { + TLVType uint8 + TLVLength uint8 + TLVValue []byte +} + +func readUnknownTLV(buf *bytes.Buffer, tlvType uint8, tlvLength uint8) (*UnknownTLV, error) { + pdu := &UnknownTLV{ + TLVType: tlvType, + TLVLength: tlvLength, + TLVValue: make([]byte, tlvLength), + } + + n, err := buf.Read(pdu.TLVValue) + if err != nil { + return nil, fmt.Errorf("Unable to read: %v", err) + } + + if n != int(tlvLength) { + return nil, fmt.Errorf("Read incomplete") + } + + return pdu, nil +} + +// Type gets the type of the TLV +func (u UnknownTLV) Type() uint8 { + return u.TLVType +} + +// Length gets the length of the TLV +func (u UnknownTLV) Length() uint8 { + return u.TLVLength +} + +// Value gets the TLV itself +func (u *UnknownTLV) Value() interface{} { + return u +} + +// Serialize serializes a protocols supported TLV +func (u UnknownTLV) Serialize(buf *bytes.Buffer) { + buf.WriteByte(u.TLVType) + buf.WriteByte(u.TLVLength) + buf.Write(u.TLVValue) +} diff --git a/protocols/isis/packet/tlv_unknown_test.go b/protocols/isis/packet/tlv_unknown_test.go new file mode 100644 index 0000000000000000000000000000000000000000..5b0f76dca6c0654cc243b9d71cbd141a75ad870c --- /dev/null +++ b/protocols/isis/packet/tlv_unknown_test.go @@ -0,0 +1,85 @@ +package packet + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestReadUnknownTLV(t *testing.T) { + tests := []struct { + name string + input []byte + tlvType uint8 + tlvLength uint8 + wantFail bool + expected *UnknownTLV + }{ + { + name: "Full", + input: []byte{1, 1, 1}, + tlvType: 100, + tlvLength: 3, + wantFail: false, + expected: &UnknownTLV{ + TLVType: 100, + TLVLength: 3, + TLVValue: []byte{1, 1, 1}, + }, + }, + { + name: "Incomplete", + input: []byte{1, 1}, + tlvType: 100, + tlvLength: 3, + wantFail: true, + }, + } + + for _, test := range tests { + buf := bytes.NewBuffer(test.input) + tlv, err := readUnknownTLV(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.Equalf(t, test.expected, tlv, "Test %q", test.name) + } +} + +func TestUnknownTLVSerialize(t *testing.T) { + tests := []struct { + name string + input *UnknownTLV + expected []byte + }{ + { + name: "Full", + input: &UnknownTLV{ + TLVType: 100, + TLVLength: 3, + TLVValue: []byte{1, 2, 3}, + }, + expected: []byte{ + 100, 3, 1, 2, 3, + }, + }, + } + + for _, test := range tests { + buf := bytes.NewBuffer(nil) + test.input.Serialize(buf) + assert.Equalf(t, test.expected, buf.Bytes(), "Test %q", test.name) + } +}