Skip to content
Snippets Groups Projects
path_attributes_test.go 40.2 KiB
Newer Older
  • Learn to ignore specific revisions
  • Oliver Herms's avatar
    Oliver Herms committed
    package packet
    
    import (
    	"bytes"
    	"testing"
    
    	"github.com/stretchr/testify/assert"
    )
    
    func TestDecodePathAttrs(t *testing.T) {
    	tests := []struct {
    		name     string
    		input    []byte
    		wantFail bool
    		expected *PathAttribute
    	}{
    		{
    			name: "Valid attribute set",
    			input: []byte{
    				0,              // Attr. Flags
    				1,              // Attr. Type Code
    				1,              // Attr. Length
    				1,              // EGP
    				0,              // Attr. Flags
    				3,              // Next Hop
    				4,              // Attr. Length
    				10, 20, 30, 40, // IP-Address
    			},
    			wantFail: false,
    			expected: &PathAttribute{
    				TypeCode: 1,
    				Length:   1,
    				Value:    uint8(1),
    				Next: &PathAttribute{
    					TypeCode: 3,
    					Length:   4,
    
    					Value:    strAddr("10.20.30.40"),
    
    Oliver Herms's avatar
    Oliver Herms committed
    				},
    			},
    		},
    		{
    			name: "Incomplete data",
    			input: []byte{
    				0, // Attr. Flags
    				1, // Attr. Type Code
    				1, // Attr. Length
    			},
    			wantFail: true,
    		},
    	}
    
    	for _, test := range tests {
    		res, err := decodePathAttrs(bytes.NewBuffer(test.input), uint16(len(test.input)))
    
    		if test.wantFail && err == nil {
    			t.Errorf("Expected error did not happen for test %q", test.name)
    			continue
    		}
    
    		if !test.wantFail && err != nil {
    			t.Errorf("Unexpected failure for test %q: %v", test.name, err)
    			continue
    		}
    
    
    		assert.Equalf(t, test.expected, res, "%s", test.name)
    
    Oliver Herms's avatar
    Oliver Herms committed
    	}
    }
    
    func TestDecodePathAttr(t *testing.T) {
    	tests := []struct {
    		name     string
    		input    []byte
    		wantFail bool
    		expected *PathAttribute
    	}{
    		{
    			name: "Valid origin",
    			input: []byte{
    				0, // Attr. Flags
    				1, // Attr. Type Code
    				1, // Attr. Length
    				1, // EGP
    			},
    			wantFail: false,
    			expected: &PathAttribute{
    				Length:         1,
    				Optional:       false,
    				Transitive:     false,
    				Partial:        false,
    				ExtendedLength: false,
    				TypeCode:       OriginAttr,
    				Value:          uint8(1),
    			},
    		},
    		{
    			name: "Missing TypeCode",
    			input: []byte{
    				0, // Attr. Flags
    			},
    			wantFail: true,
    		},
    		{
    			name: "Missing Length",
    			input: []byte{
    				0, // Attr. Flags
    				1, // Attr. Type Code
    			},
    			wantFail: true,
    		},
    		{
    			name: "Missing Value ORIGIN",
    			input: []byte{
    				0, // Attr. Flags
    				1, // Attr. Type Code
    				1, // Attr. Length
    			},
    			wantFail: true,
    		},
    		{
    			name: "Missing value AS_PATH",
    			input: []byte{
    				0, // Attr. Flags
    				2, // Attr. Type Code
    				8, // Attr. Length
    			},
    			wantFail: true,
    		},
    		{
    			name: "Missing value NextHop",
    			input: []byte{
    				0, // Attr. Flags
    				3, // Attr. Type Code
    				4, // Attr. Length
    			},
    			wantFail: true,
    		},
    		{
    			name: "Missing value MED",
    			input: []byte{
    				0, // Attr. Flags
    				4, // Attr. Type Code
    				4, // Attr. Length
    			},
    			wantFail: true,
    		},
    		{
    			name: "Missing value LocalPref",
    			input: []byte{
    				0, // Attr. Flags
    				5, // Attr. Type Code
    				4, // Attr. Length
    			},
    			wantFail: true,
    		},
    		{
    			name: "Missing value AGGREGATOR",
    			input: []byte{
    				0, // Attr. Flags
    				7, // Attr. Type Code
    				4, // Attr. Length
    			},
    			wantFail: true,
    		},
    		{
    			name: "Not supported attribute",
    			input: []byte{
    				0,   // Attr. Flags
    				111, // Attr. Type Code
    				4,   // Attr. Length
    			},
    			wantFail: true,
    		},
    	}
    
    	for _, test := range tests {
    		res, _, err := decodePathAttr(bytes.NewBuffer(test.input))
    
    		if test.wantFail && err == nil {
    			t.Errorf("Expected error did not happen for test %q", test.name)
    			continue
    		}
    
    		if !test.wantFail && err != nil {
    			t.Errorf("Unexpected failure for test %q: %v", test.name, err)
    			continue
    		}
    
    		assert.Equal(t, test.expected, res)
    	}
    }
    
    func TestDecodeOrigin(t *testing.T) {
    	tests := []struct {
    		name     string
    		input    []byte
    		wantFail bool
    		expected *PathAttribute
    	}{
    		{
    			name: "Test #1",
    			input: []byte{
    				0, // Origin: IGP
    			},
    			wantFail: false,
    			expected: &PathAttribute{
    				Value:  uint8(IGP),
    				Length: 1,
    			},
    		},
    		{
    			name: "Test #2",
    			input: []byte{
    				1, // Origin: EGP
    			},
    			wantFail: false,
    			expected: &PathAttribute{
    				Value:  uint8(EGP),
    				Length: 1,
    			},
    		},
    		{
    			name: "Test #3",
    			input: []byte{
    				2, // Origin: INCOMPLETE
    			},
    			wantFail: false,
    			expected: &PathAttribute{
    				Value:  uint8(INCOMPLETE),
    				Length: 1,
    			},
    		},
    		{
    			name:     "Test #4",
    			input:    []byte{},
    			wantFail: true,
    		},
    	}
    
    	for _, test := range tests {
    		pa := &PathAttribute{
    			Length: uint16(len(test.input)),
    		}
    		err := pa.decodeOrigin(bytes.NewBuffer(test.input))
    
    		if test.wantFail && err == nil {
    			t.Errorf("Expected error did not happen for test %q", test.name)
    		}
    
    		if !test.wantFail && err != nil {
    			t.Errorf("Unexpected failure for test %q: %v", test.name, err)
    		}
    
    		if err != nil {
    			continue
    		}
    
    		assert.Equal(t, test.expected, pa)
    	}
    }
    
    func TestDecodeASPath(t *testing.T) {
    	tests := []struct {
    		name           string
    		input          []byte
    		wantFail       bool
    		explicitLength uint16
    		expected       *PathAttribute
    	}{
    		{
    			name: "Test #1",
    			input: []byte{
    				2, // AS_SEQUENCE
    				4, // Path Length
    				0, 100, 0, 200, 0, 222, 0, 240,
    			},
    			wantFail: false,
    			expected: &PathAttribute{
    				Length: 10,
    				Value: ASPath{
    					ASPathSegment{
    						Type:  2,
    						Count: 4,
    						ASNs: []uint32{
    							100, 200, 222, 240,
    						},
    					},
    				},
    			},
    		},
    		{
    			name: "Test #2",
    			input: []byte{
    				1, // AS_SEQUENCE
    				3, // Path Length
    				0, 100, 0, 222, 0, 240,
    			},
    			wantFail: false,
    			expected: &PathAttribute{
    				Length: 8,
    				Value: ASPath{
    					ASPathSegment{
    						Type:  1,
    						Count: 3,
    						ASNs: []uint32{
    							100, 222, 240,
    						},
    					},
    				},
    			},
    		},
    		{
    			name:           "Empty input",
    			input:          []byte{},
    			explicitLength: 5,
    			wantFail:       true,
    		},
    		{
    			name: "Incomplete AS_PATH",
    			input: []byte{
    				1, // AS_SEQUENCE
    				3, // Path Length
    				0, 100, 0, 222,
    			},
    			wantFail: true,
    		},
    	}
    
    	for _, test := range tests {
    		l := uint16(len(test.input))
    		if test.explicitLength != 0 {
    			l = test.explicitLength
    		}
    		pa := &PathAttribute{
    			Length: l,
    		}
    		err := pa.decodeASPath(bytes.NewBuffer(test.input))
    
    		if test.wantFail && err == nil {
    			t.Errorf("Expected error did not happen for test %q", test.name)
    		}
    
    		if !test.wantFail && err != nil {
    			t.Errorf("Unexpected failure for test %q: %v", test.name, err)
    		}
    
    		if err != nil {
    			continue
    		}
    
    		assert.Equal(t, test.expected, pa)
    	}
    }
    
    func TestDecodeNextHop(t *testing.T) {
    	tests := []struct {
    		name           string
    		input          []byte
    		wantFail       bool
    		explicitLength uint16
    		expected       *PathAttribute
    	}{
    		{
    			name: "Test #1",
    			input: []byte{
    				10, 20, 30, 40,
    			},
    			wantFail: false,
    			expected: &PathAttribute{
    				Length: 4,
    
    				Value:  strAddr("10.20.30.40"),
    
    Oliver Herms's avatar
    Oliver Herms committed
    			},
    		},
    		{
    			name:           "Test #2",
    			input:          []byte{},
    			explicitLength: 5,
    			wantFail:       true,
    		},
    		{
    			name:           "Incomplete IP-Address",
    			input:          []byte{10, 20, 30},
    			explicitLength: 5,
    			wantFail:       true,
    		},
    	}
    
    	for _, test := range tests {
    		l := uint16(len(test.input))
    		if test.explicitLength != 0 {
    			l = test.explicitLength
    		}
    		pa := &PathAttribute{
    			Length: l,
    		}
    		err := pa.decodeNextHop(bytes.NewBuffer(test.input))
    
    		if test.wantFail && err == nil {
    			t.Errorf("Expected error did not happen for test %q", test.name)
    		}
    
    		if !test.wantFail && err != nil {
    			t.Errorf("Unexpected failure for test %q: %v", test.name, err)
    		}
    
    		if err != nil {
    			continue
    		}
    
    		assert.Equal(t, test.expected, pa)
    	}
    }
    
    func TestDecodeMED(t *testing.T) {
    	tests := []struct {
    		name           string
    		input          []byte
    		wantFail       bool
    		explicitLength uint16
    		expected       *PathAttribute
    	}{
    		{
    			name: "Test #1",
    			input: []byte{
    				0, 0, 3, 232,
    			},
    			wantFail: false,
    			expected: &PathAttribute{
    				Length: 4,
    				Value:  uint32(1000),
    			},
    		},
    		{
    			name:           "Test #2",
    			input:          []byte{},
    			explicitLength: 5,
    			wantFail:       true,
    		},
    	}
    
    	for _, test := range tests {
    		l := uint16(len(test.input))
    		if test.explicitLength != 0 {
    			l = test.explicitLength
    		}
    		pa := &PathAttribute{
    			Length: l,
    		}
    		err := pa.decodeMED(bytes.NewBuffer(test.input))
    
    		if test.wantFail && err == nil {
    			t.Errorf("Expected error did not happen for test %q", test.name)
    		}
    
    		if !test.wantFail && err != nil {
    			t.Errorf("Unexpected failure for test %q: %v", test.name, err)
    		}
    
    		if err != nil {
    			continue
    		}
    
    		assert.Equal(t, test.expected, pa)
    	}
    }
    
    func TestDecodeLocalPref(t *testing.T) {
    	tests := []struct {
    		name           string
    		input          []byte
    		wantFail       bool
    		explicitLength uint16
    		expected       *PathAttribute
    	}{
    		{
    			name: "Test #1",
    			input: []byte{
    				0, 0, 3, 232,
    			},
    			wantFail: false,
    			expected: &PathAttribute{
    				Length: 4,
    				Value:  uint32(1000),
    			},
    		},
    		{
    			name:           "Test #2",
    			input:          []byte{},
    			explicitLength: 5,
    			wantFail:       true,
    		},
    	}
    
    	for _, test := range tests {
    		l := uint16(len(test.input))
    		if test.explicitLength != 0 {
    			l = test.explicitLength
    		}
    		pa := &PathAttribute{
    			Length: l,
    		}
    		err := pa.decodeLocalPref(bytes.NewBuffer(test.input))
    
    		if test.wantFail {
    			if err != nil {
    				continue
    			}
    			t.Errorf("Expected error did not happen for test %q", test.name)
    			continue
    		}
    
    		if err != nil {
    			t.Errorf("Unexpected failure for test %q: %v", test.name, err)
    			continue
    		}
    
    		assert.Equal(t, test.expected, pa)
    	}
    }
    
    func TestDecodeAggregator(t *testing.T) {
    	tests := []struct {
    		name           string
    		input          []byte
    		wantFail       bool
    		explicitLength uint16
    		expected       *PathAttribute
    	}{
    		{
    			name: "Valid aggregator",
    			input: []byte{
    				0, 222, // ASN
    				10, 20, 30, 40, // Aggregator IP
    			},
    			wantFail: false,
    			expected: &PathAttribute{
    				Length: 6,
    				Value: Aggretator{
    					ASN:  222,
    
    Oliver Herms's avatar
    Oliver Herms committed
    				},
    			},
    		},
    		{
    			name: "Incomplete Address",
    			input: []byte{
    				0, 222, // ASN
    				10, 20, // Aggregator IP
    			},
    			wantFail: true,
    		},
    		{
    			name: "Missing Address",
    			input: []byte{
    				0, 222, // ASN
    			},
    			wantFail: true,
    		},
    		{
    			name:     "Empty input",
    			input:    []byte{},
    			wantFail: true,
    		},
    	}
    
    	for _, test := range tests {
    		l := uint16(len(test.input))
    		if test.explicitLength != 0 {
    			l = test.explicitLength
    		}
    		pa := &PathAttribute{
    			Length: l,
    		}
    		err := pa.decodeAggregator(bytes.NewBuffer(test.input))
    
    		if test.wantFail {
    			if err != nil {
    				continue
    			}
    			t.Errorf("Expected error did not happen for test %q", test.name)
    			continue
    		}
    
    		if err != nil {
    			t.Errorf("Unexpected failure for test %q: %v", test.name, err)
    			continue
    		}
    
    		assert.Equal(t, test.expected, pa)
    	}
    }
    
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    func TestDecodeLargeCommunity(t *testing.T) {
    	tests := []struct {
    		name           string
    		input          []byte
    		wantFail       bool
    		explicitLength uint16
    		expected       *PathAttribute
    	}{
    		{
    			name: "two valid large communities",
    			input: []byte{
    				0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6, // (1, 2, 3), (4, 5, 6)
    			},
    			wantFail: false,
    			expected: &PathAttribute{
    				Length: 24,
    				Value: []LargeCommunity{
    					{
    						GlobalAdministrator: 1,
    						DataPart1:           2,
    						DataPart2:           3,
    					},
    					{
    						GlobalAdministrator: 4,
    						DataPart1:           5,
    						DataPart2:           6,
    					},
    				},
    			},
    		},
    		{
    			name:     "Empty input",
    			input:    []byte{},
    			wantFail: false,
    			expected: &PathAttribute{
    				Length: 0,
    				Value:  []LargeCommunity{},
    			},
    		},
    	}
    
    	for _, test := range tests {
    		l := uint16(len(test.input))
    		if test.explicitLength != 0 {
    			l = test.explicitLength
    		}
    		pa := &PathAttribute{
    			Length: l,
    		}
    		err := pa.decodeLargeCommunities(bytes.NewBuffer(test.input))
    
    
    		if test.wantFail {
    			if err != nil {
    				continue
    			}
    			t.Errorf("Expected error did not happen for test %q", test.name)
    			continue
    		}
    
    		if err != nil {
    			t.Errorf("Unexpected failure for test %q: %v", test.name, err)
    			continue
    		}
    
    		assert.Equal(t, test.expected, pa)
    	}
    }
    
    func TestDecodeCommunity(t *testing.T) {
    	tests := []struct {
    		name           string
    		input          []byte
    		wantFail       bool
    		explicitLength uint16
    		expected       *PathAttribute
    	}{
    		{
    			name: "two valid communities",
    			input: []byte{
    				0, 2, 0, 8, 1, 0, 4, 1, // (2,8), (256,1025)
    			},
    			wantFail: false,
    			expected: &PathAttribute{
    				Length: 8,
    				Value: []uint32{
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    					131080, 16778241,
    
    				},
    			},
    		},
    		{
    			name:     "Empty input",
    			input:    []byte{},
    			wantFail: false,
    			expected: &PathAttribute{
    				Length: 0,
    				Value:  []uint32{},
    			},
    		},
    	}
    
    	for _, test := range tests {
    		l := uint16(len(test.input))
    		if test.explicitLength != 0 {
    			l = test.explicitLength
    		}
    		pa := &PathAttribute{
    			Length: l,
    		}
    		err := pa.decodeCommunities(bytes.NewBuffer(test.input))
    
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    		if test.wantFail {
    			if err != nil {
    				continue
    			}
    			t.Errorf("Expected error did not happen for test %q", test.name)
    			continue
    		}
    
    		if err != nil {
    			t.Errorf("Unexpected failure for test %q: %v", test.name, err)
    			continue
    		}
    
    		assert.Equal(t, test.expected, pa)
    	}
    }
    
    
    Oliver Herms's avatar
    Oliver Herms committed
    func TestSetLength(t *testing.T) {
    	tests := []struct {
    		name             string
    		input            []byte
    		ExtendedLength   bool
    		wantFail         bool
    		expected         *PathAttribute
    		expectedConsumed int
    	}{
    		{
    			name:           "Valid input",
    			ExtendedLength: false,
    			input:          []byte{22},
    			expected: &PathAttribute{
    				ExtendedLength: false,
    				Length:         22,
    			},
    			expectedConsumed: 1,
    		},
    		{
    			name:           "Valid input (extended)",
    			ExtendedLength: true,
    			input:          []byte{1, 1},
    			expected: &PathAttribute{
    				ExtendedLength: true,
    				Length:         257,
    			},
    			expectedConsumed: 2,
    		},
    		{
    			name:           "Invalid input",
    			ExtendedLength: true,
    			input:          []byte{},
    			wantFail:       true,
    		},
    		{
    			name:           "Invalid input (extended)",
    			ExtendedLength: true,
    			input:          []byte{1},
    			wantFail:       true,
    		},
    	}
    
    	for _, test := range tests {
    		pa := &PathAttribute{
    			ExtendedLength: test.ExtendedLength,
    		}
    		consumed, err := pa.setLength(bytes.NewBuffer(test.input))
    
    		if test.wantFail {
    			if err != nil {
    				continue
    			}
    			t.Errorf("Expected error did not happen for test %q", test.name)
    			continue
    		}
    
    		if err != nil {
    			t.Errorf("Unexpected failure for test %q: %v", test.name, err)
    			continue
    		}
    
    		assert.Equal(t, test.expected, pa)
    		assert.Equal(t, test.expectedConsumed, consumed)
    	}
    }
    
    
    func TestDecodeUint32(t *testing.T) {
    
    Oliver Herms's avatar
    Oliver Herms committed
    	tests := []struct {
    		name           string
    		input          []byte
    		wantFail       bool
    		explicitLength uint16
    		expected       uint32
    	}{
    		{
    			name:     "Valid input",
    			input:    []byte{0, 0, 1, 1},
    			expected: 257,
    		},
    		{
    			name:     "Valid input with additional crap",
    			input:    []byte{0, 0, 1, 1, 200},
    			expected: 257,
    		},
    		{
    			name:           "Valid input with additional crap and invalid length",
    			input:          []byte{0, 0, 1, 1, 200},
    			explicitLength: 8,
    			wantFail:       true,
    		},
    		{
    			name:     "Invalid input",
    			input:    []byte{0, 0, 1},
    			wantFail: true,
    		},
    	}
    
    	for _, test := range tests {
    		l := uint16(len(test.input))
    		if test.explicitLength > 0 {
    			l = test.explicitLength
    		}
    		pa := &PathAttribute{
    			Length: l,
    		}
    
    		err := pa.decodeUint32(bytes.NewBuffer(test.input), "test")
    
    Oliver Herms's avatar
    Oliver Herms committed
    
    		if test.wantFail {
    			if err != nil {
    				continue
    			}
    			t.Errorf("Expected error did not happen for test %q", test.name)
    			continue
    		}
    
    		if err != nil {
    			t.Errorf("Unexpected failure for test %q: %v", test.name, err)
    			continue
    		}
    
    
    		assert.Equal(t, test.expected, pa.Value)
    
    Oliver Herms's avatar
    Oliver Herms committed
    	}
    }
    
    func TestASPathString(t *testing.T) {
    	tests := []struct {
    		name     string
    		pa       *PathAttribute
    		expected string
    	}{
    		{
    			name: "Test #1",
    			pa: &PathAttribute{
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    				Value: ASPath{
    
    Oliver Herms's avatar
    Oliver Herms committed
    					{
    						Type: ASSequence,
    						ASNs: []uint32{10, 20, 30},
    					},
    				},
    			},
    			expected: "10 20 30",
    		},
    		{
    			name: "Test #2",
    			pa: &PathAttribute{
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    				Value: ASPath{
    
    Oliver Herms's avatar
    Oliver Herms committed
    					{
    						Type: ASSequence,
    						ASNs: []uint32{10, 20, 30},
    					},
    					{
    						Type: ASSet,
    						ASNs: []uint32{200, 300},
    					},
    				},
    			},
    			expected: "10 20 30 (200 300)",
    		},
    	}
    
    	for _, test := range tests {
    		res := test.pa.ASPathString()
    		assert.Equal(t, test.expected, res)
    	}
    }
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    func TestLargeCommunityString(t *testing.T) {
    	tests := []struct {
    		name     string
    		pa       *PathAttribute
    		expected string
    	}{
    		{
    			name: "two attributes",
    			pa: &PathAttribute{
    				Value: []LargeCommunity{
    					{
    						GlobalAdministrator: 1,
    						DataPart1:           2,
    						DataPart2:           3,
    					},
    					{
    						GlobalAdministrator: 4,
    						DataPart1:           5,
    						DataPart2:           6,
    					},
    				},
    			},
    			expected: "(1,2,3) (4,5,6)",
    		},
    	}
    
    	for _, test := range tests {
    		t.Run(test.name, func(te *testing.T) {
    			res := test.pa.LargeCommunityString()
    			assert.Equal(te, test.expected, res)
    
    Daniel Czerwonk's avatar
    Daniel Czerwonk committed
    		})
    	}
    }
    
    func TestCommunityString(t *testing.T) {
    	tests := []struct {
    		name     string
    		pa       *PathAttribute
    		expected string
    	}{
    		{
    			name: "two attributes",
    			pa: &PathAttribute{
    				Value: []uint32{
    					131080, 16778241,
    				},
    			},
    			expected: "(2,8) (256,1025)",
    		},
    	}
    
    	for _, test := range tests {
    		t.Run(test.name, func(te *testing.T) {
    			res := test.pa.CommunityString()
    			assert.Equal(te, test.expected, res)
    
    func TestSetOptional(t *testing.T) {
    	tests := []struct {
    		name     string
    		input    uint8
    		expected uint8
    	}{
    		{
    			name:     "Test #1",
    			input:    0,
    			expected: 128,
    		},
    	}
    
    	for _, test := range tests {
    		res := setOptional(test.input)
    		if res != test.expected {
    			t.Errorf("Unexpected result for test %q: %d", test.name, res)
    		}
    	}
    }
    
    func TestSetTransitive(t *testing.T) {
    	tests := []struct {
    		name     string
    		input    uint8
    		expected uint8
    	}{
    		{
    			name:     "Test #1",
    			input:    0,
    			expected: 64,
    		},
    	}
    
    	for _, test := range tests {
    		res := setTransitive(test.input)
    		if res != test.expected {
    			t.Errorf("Unexpected result for test %q: %d", test.name, res)
    		}
    	}
    }
    
    func TestSetPartial(t *testing.T) {
    	tests := []struct {
    		name     string
    		input    uint8
    		expected uint8
    	}{
    		{
    			name:     "Test #1",
    			input:    0,
    			expected: 32,
    		},
    	}
    
    	for _, test := range tests {