package ethernet import ( "bytes" "io" "net" "reflect" "testing" ) func TestFrameMarshalBinary(t *testing.T) { tests := []struct { desc string f *Frame b []byte err error }{ { desc: "S-VLAN, no C-VLAN", f: &Frame{ // Contents don't matter. ServiceVLAN: &VLAN{}, }, err: ErrInvalidVLAN, }, { desc: "IPv4, no VLANs", f: &Frame{ Destination: net.HardwareAddr{0, 1, 0, 1, 0, 1}, Source: net.HardwareAddr{1, 0, 1, 0, 1, 0}, EtherType: EtherTypeIPv4, Payload: bytes.Repeat([]byte{0}, 50), }, b: append([]byte{ 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0x08, 0x00, }, bytes.Repeat([]byte{0}, 50)...), }, { desc: "IPv6, C-VLAN: (PRI 1, ID 101)", f: &Frame{ Destination: net.HardwareAddr{1, 0, 1, 0, 1, 0}, Source: net.HardwareAddr{0, 1, 0, 1, 0, 1}, VLAN: &VLAN{ Priority: 1, ID: 101, }, EtherType: EtherTypeIPv6, Payload: bytes.Repeat([]byte{0}, 50), }, b: append([]byte{ 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0x81, 0x00, 0x20, 0x65, 0x86, 0xDD, }, bytes.Repeat([]byte{0}, 50)...), }, { desc: "ARP, S-VLAN: (PRI 0, DROP, ID 100), C-VLAN: (PRI 1, ID 101)", f: &Frame{ Destination: Broadcast, Source: net.HardwareAddr{0, 1, 0, 1, 0, 1}, ServiceVLAN: &VLAN{ DropEligible: true, ID: 100, }, VLAN: &VLAN{ Priority: 1, ID: 101, }, EtherType: EtherTypeARP, Payload: bytes.Repeat([]byte{0}, 50), }, b: append([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 1, 0, 1, 0, 1, 0x88, 0xa8, 0x10, 0x64, 0x81, 0x00, 0x20, 0x65, 0x08, 0x06, }, bytes.Repeat([]byte{0}, 50)...), }, } for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { b, err := tt.f.MarshalBinary() if err != nil { if want, got := tt.err, err; want != got { t.Fatalf("unexpected error: %v != %v", want, got) } return } if want, got := tt.b, b; !bytes.Equal(want, got) { t.Fatalf("unexpected Frame bytes:\n- want: %v\n- got: %v", want, got) } }) } } func TestFrameMarshalFCS(t *testing.T) { tests := []struct { desc string f *Frame b []byte err error }{ { desc: "IPv4, no VLANs", f: &Frame{ Destination: net.HardwareAddr{0, 1, 0, 1, 0, 1}, Source: net.HardwareAddr{1, 0, 1, 0, 1, 0}, EtherType: EtherTypeIPv4, Payload: bytes.Repeat([]byte{0}, 50), }, b: append( append( []byte{ 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0x08, 0x00, }, bytes.Repeat([]byte{0}, 50)..., ), []byte{159, 205, 24, 60}..., ), }, } for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { b, err := tt.f.MarshalFCS() if err != nil { if want, got := tt.err, err; want != got { t.Fatalf("unexpected error: %v != %v", want, got) } return } if want, got := tt.b, b; !bytes.Equal(want, got) { t.Fatalf("unexpected Frame bytes:\n- want: %v\n- got: %v", want, got) } }) } } func TestFrameUnmarshalBinary(t *testing.T) { tests := []struct { desc string b []byte f *Frame err error }{ { desc: "nil buffer", err: io.ErrUnexpectedEOF, }, { desc: "short buffer", b: bytes.Repeat([]byte{0}, 13), err: io.ErrUnexpectedEOF, }, { desc: "1 short S-VLAN", b: []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x88, 0xa8, 0x00, }, err: io.ErrUnexpectedEOF, }, { desc: "1 short C-VLAN", b: []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x81, 0x00, 0x00, }, err: io.ErrUnexpectedEOF, }, { desc: "VLAN ID too large", b: []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x81, 0x00, 0xff, 0xff, 0x00, 0x00, }, err: ErrInvalidVLAN, }, { desc: "no C-VLAN after S-VLAN", b: []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x88, 0xa8, 0x20, 0x65, 0x08, 0x06, 0x00, 0x00, 0x00, 0x00, }, err: ErrInvalidVLAN, }, { desc: "short C-VLAN after S-VLAN", b: []byte{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x88, 0xa8, 0x20, 0x65, 0x81, 0x00, 0x00, 0x00, }, err: io.ErrUnexpectedEOF, }, { desc: "go-fuzz crasher: VLAN tag without enough bytes for trailing EtherType", b: []byte("190734863281\x81\x0032"), err: io.ErrUnexpectedEOF, }, { desc: "0 VLANs detected, but 1 may have been present", b: bytes.Repeat([]byte{0}, 56), f: &Frame{ Destination: net.HardwareAddr{0, 0, 0, 0, 0, 0}, Source: net.HardwareAddr{0, 0, 0, 0, 0, 0}, Payload: bytes.Repeat([]byte{0}, 42), }, }, { desc: "IPv4, no VLANs", b: append([]byte{ 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0x08, 0x00, }, bytes.Repeat([]byte{0}, 50)...), f: &Frame{ Destination: net.HardwareAddr{0, 1, 0, 1, 0, 1}, Source: net.HardwareAddr{1, 0, 1, 0, 1, 0}, EtherType: EtherTypeIPv4, Payload: bytes.Repeat([]byte{0}, 50), }, }, { desc: "IPv6, C-VLAN: (PRI 1, ID 101)", b: append([]byte{ 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0x81, 0x00, 0x20, 0x65, 0x86, 0xDD, }, bytes.Repeat([]byte{0}, 50)...), f: &Frame{ Destination: net.HardwareAddr{1, 0, 1, 0, 1, 0}, Source: net.HardwareAddr{0, 1, 0, 1, 0, 1}, VLAN: &VLAN{ Priority: 1, ID: 101, }, EtherType: EtherTypeIPv6, Payload: bytes.Repeat([]byte{0}, 50), }, }, { desc: "ARP, S-VLAN: (PRI 0, DROP, ID 100), C-VLAN: (PRI 1, ID 101)", b: append([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 1, 0, 1, 0, 1, 0x88, 0xa8, 0x10, 0x64, 0x81, 0x00, 0x20, 0x65, 0x08, 0x06, }, bytes.Repeat([]byte{0}, 50)...), f: &Frame{ Destination: Broadcast, Source: net.HardwareAddr{0, 1, 0, 1, 0, 1}, ServiceVLAN: &VLAN{ DropEligible: true, ID: 100, }, VLAN: &VLAN{ Priority: 1, ID: 101, }, EtherType: EtherTypeARP, Payload: bytes.Repeat([]byte{0}, 50), }, }, } for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { f := new(Frame) if err := f.UnmarshalBinary(tt.b); err != nil { if want, got := tt.err, err; want != got { t.Fatalf("unexpected error: %v != %v", want, got) } return } if want, got := tt.f, f; !reflect.DeepEqual(want, got) { t.Fatalf("unexpected Frame:\n- want: %v\n- got: %v", want, got) } }) } } func TestFrameUnmarshalFCS(t *testing.T) { tests := []struct { desc string b []byte f *Frame err error }{ { desc: "too short for FCS", b: []byte{1, 2, 3}, err: io.ErrUnexpectedEOF, }, { desc: "invalid FCS", b: []byte{1, 2, 3, 4}, err: ErrInvalidFCS, }, { desc: "IPv4, no VLANs", b: append( append( []byte{ 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0x08, 0x00, }, bytes.Repeat([]byte{0}, 50)..., ), []byte{159, 205, 24, 60}..., ), f: &Frame{ Destination: net.HardwareAddr{0, 1, 0, 1, 0, 1}, Source: net.HardwareAddr{1, 0, 1, 0, 1, 0}, EtherType: EtherTypeIPv4, Payload: bytes.Repeat([]byte{0}, 50), }, }, } for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { f := new(Frame) if err := f.UnmarshalFCS(tt.b); err != nil { if want, got := tt.err, err; want != got { t.Fatalf("unexpected error: %v != %v", want, got) } return } if want, got := tt.f, f; !reflect.DeepEqual(want, got) { t.Fatalf("unexpected Frame:\n- want: %v\n- got: %v", want, got) } }) } } // Benchmarks for Frame.MarshalBinary with varying VLAN tags and payloads func BenchmarkFrameMarshalBinary(b *testing.B) { f := &Frame{ Payload: []byte{0, 1, 2, 3, 4}, } benchmarkFrameMarshalBinary(b, f) } func BenchmarkFrameMarshalBinaryCVLAN(b *testing.B) { f := &Frame{ VLAN: &VLAN{ Priority: PriorityBackground, ID: 10, }, Payload: []byte{0, 1, 2, 3, 4}, } benchmarkFrameMarshalBinary(b, f) } func BenchmarkFrameMarshalBinarySVLANCVLAN(b *testing.B) { f := &Frame{ ServiceVLAN: &VLAN{ Priority: PriorityBackground, ID: 10, }, VLAN: &VLAN{ Priority: PriorityBestEffort, ID: 20, }, Payload: []byte{0, 1, 2, 3, 4}, } benchmarkFrameMarshalBinary(b, f) } func BenchmarkFrameMarshalBinaryJumboPayload(b *testing.B) { f := &Frame{ Payload: make([]byte, 8192), } benchmarkFrameMarshalBinary(b, f) } func benchmarkFrameMarshalBinary(b *testing.B, f *Frame) { f.Destination = net.HardwareAddr{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad} f.Source = net.HardwareAddr{0xad, 0xbe, 0xef, 0xde, 0xad, 0xde} b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { if _, err := f.MarshalBinary(); err != nil { b.Fatal(err) } } } // Benchmarks for Frame.MarshalFCS func BenchmarkFrameMarshalFCS(b *testing.B) { f := &Frame{ Payload: []byte{0, 1, 2, 3, 4}, } benchmarkFrameMarshalFCS(b, f) } func benchmarkFrameMarshalFCS(b *testing.B, f *Frame) { f.Destination = net.HardwareAddr{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad} f.Source = net.HardwareAddr{0xad, 0xbe, 0xef, 0xde, 0xad, 0xde} b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { if _, err := f.MarshalFCS(); err != nil { b.Fatal(err) } } } // Benchmarks for Frame.UnmarshalBinary with varying VLAN tags and payloads func BenchmarkFrameUnmarshalBinary(b *testing.B) { f := &Frame{ Payload: []byte{0, 1, 2, 3, 4}, } benchmarkFrameUnmarshalBinary(b, f) } func BenchmarkFrameUnmarshalBinaryCVLAN(b *testing.B) { f := &Frame{ VLAN: &VLAN{ Priority: PriorityBackground, ID: 10, }, Payload: []byte{0, 1, 2, 3, 4}, } benchmarkFrameUnmarshalBinary(b, f) } func BenchmarkFrameUnmarshalBinarySVLANCVLAN(b *testing.B) { f := &Frame{ ServiceVLAN: &VLAN{ Priority: PriorityBackground, ID: 10, }, VLAN: &VLAN{ Priority: PriorityBestEffort, ID: 20, }, Payload: []byte{0, 1, 2, 3, 4}, } benchmarkFrameUnmarshalBinary(b, f) } func BenchmarkFrameUnmarshalBinaryJumboPayload(b *testing.B) { f := &Frame{ Payload: make([]byte, 8192), } benchmarkFrameUnmarshalBinary(b, f) } func benchmarkFrameUnmarshalBinary(b *testing.B, f *Frame) { f.Destination = net.HardwareAddr{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad} f.Source = net.HardwareAddr{0xad, 0xbe, 0xef, 0xde, 0xad, 0xde} fb, err := f.MarshalBinary() if err != nil { b.Fatal(err) } b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { if err := f.UnmarshalBinary(fb); err != nil { b.Fatal(err) } } } // Benchmarks for Frame.UnmarshalFCS func BenchmarkFrameUnmarshalFCS(b *testing.B) { f := &Frame{ Payload: []byte{0, 1, 2, 3, 4}, } benchmarkFrameUnmarshalFCS(b, f) } func benchmarkFrameUnmarshalFCS(b *testing.B, f *Frame) { f.Destination = net.HardwareAddr{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad} f.Source = net.HardwareAddr{0xad, 0xbe, 0xef, 0xde, 0xad, 0xde} fb, err := f.MarshalFCS() if err != nil { b.Fatal(err) } b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { if err := f.UnmarshalFCS(fb); err != nil { b.Fatal(err) } } }