334 lines
8 KiB
Go
334 lines
8 KiB
Go
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||
|
// Use of this source code is governed by a BSD-style
|
||
|
// license that can be found in the LICENSE file.
|
||
|
|
||
|
package icmp
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"net"
|
||
|
"reflect"
|
||
|
"testing"
|
||
|
|
||
|
"golang.org/x/net/internal/iana"
|
||
|
"golang.org/x/net/ipv4"
|
||
|
"golang.org/x/net/ipv6"
|
||
|
)
|
||
|
|
||
|
func TestMarshalAndParseExtension(t *testing.T) {
|
||
|
fn := func(t *testing.T, proto int, typ Type, hdr, obj []byte, te Extension) error {
|
||
|
b, err := te.Marshal(proto)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if !reflect.DeepEqual(b, obj) {
|
||
|
return fmt.Errorf("got %#v; want %#v", b, obj)
|
||
|
}
|
||
|
switch typ {
|
||
|
case ipv4.ICMPTypeExtendedEchoRequest, ipv6.ICMPTypeExtendedEchoRequest:
|
||
|
exts, l, err := parseExtensions(typ, append(hdr, obj...), 0)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if l != 0 {
|
||
|
return fmt.Errorf("got %d; want 0", l)
|
||
|
}
|
||
|
if !reflect.DeepEqual(exts, []Extension{te}) {
|
||
|
return fmt.Errorf("got %#v; want %#v", exts[0], te)
|
||
|
}
|
||
|
default:
|
||
|
for i, wire := range []struct {
|
||
|
data []byte // original datagram
|
||
|
inlattr int // length of padded original datagram, a hint
|
||
|
outlattr int // length of padded original datagram, a want
|
||
|
err error
|
||
|
}{
|
||
|
{nil, 0, -1, errNoExtension},
|
||
|
{make([]byte, 127), 128, -1, errNoExtension},
|
||
|
|
||
|
{make([]byte, 128), 127, -1, errNoExtension},
|
||
|
{make([]byte, 128), 128, -1, errNoExtension},
|
||
|
{make([]byte, 128), 129, -1, errNoExtension},
|
||
|
|
||
|
{append(make([]byte, 128), append(hdr, obj...)...), 127, 128, nil},
|
||
|
{append(make([]byte, 128), append(hdr, obj...)...), 128, 128, nil},
|
||
|
{append(make([]byte, 128), append(hdr, obj...)...), 129, 128, nil},
|
||
|
|
||
|
{append(make([]byte, 512), append(hdr, obj...)...), 511, -1, errNoExtension},
|
||
|
{append(make([]byte, 512), append(hdr, obj...)...), 512, 512, nil},
|
||
|
{append(make([]byte, 512), append(hdr, obj...)...), 513, -1, errNoExtension},
|
||
|
} {
|
||
|
exts, l, err := parseExtensions(typ, wire.data, wire.inlattr)
|
||
|
if err != wire.err {
|
||
|
return fmt.Errorf("#%d: got %v; want %v", i, err, wire.err)
|
||
|
}
|
||
|
if wire.err != nil {
|
||
|
continue
|
||
|
}
|
||
|
if l != wire.outlattr {
|
||
|
return fmt.Errorf("#%d: got %d; want %d", i, l, wire.outlattr)
|
||
|
}
|
||
|
if !reflect.DeepEqual(exts, []Extension{te}) {
|
||
|
return fmt.Errorf("#%d: got %#v; want %#v", i, exts[0], te)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
t.Run("MPLSLabelStack", func(t *testing.T) {
|
||
|
for _, et := range []struct {
|
||
|
proto int
|
||
|
typ Type
|
||
|
hdr []byte
|
||
|
obj []byte
|
||
|
ext Extension
|
||
|
}{
|
||
|
// MPLS label stack with no label
|
||
|
{
|
||
|
proto: iana.ProtocolICMP,
|
||
|
typ: ipv4.ICMPTypeDestinationUnreachable,
|
||
|
hdr: []byte{
|
||
|
0x20, 0x00, 0x00, 0x00,
|
||
|
},
|
||
|
obj: []byte{
|
||
|
0x00, 0x04, 0x01, 0x01,
|
||
|
},
|
||
|
ext: &MPLSLabelStack{
|
||
|
Class: classMPLSLabelStack,
|
||
|
Type: typeIncomingMPLSLabelStack,
|
||
|
},
|
||
|
},
|
||
|
// MPLS label stack with a single label
|
||
|
{
|
||
|
proto: iana.ProtocolIPv6ICMP,
|
||
|
typ: ipv6.ICMPTypeDestinationUnreachable,
|
||
|
hdr: []byte{
|
||
|
0x20, 0x00, 0x00, 0x00,
|
||
|
},
|
||
|
obj: []byte{
|
||
|
0x00, 0x08, 0x01, 0x01,
|
||
|
0x03, 0xe8, 0xe9, 0xff,
|
||
|
},
|
||
|
ext: &MPLSLabelStack{
|
||
|
Class: classMPLSLabelStack,
|
||
|
Type: typeIncomingMPLSLabelStack,
|
||
|
Labels: []MPLSLabel{
|
||
|
{
|
||
|
Label: 16014,
|
||
|
TC: 0x4,
|
||
|
S: true,
|
||
|
TTL: 255,
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
// MPLS label stack with multiple labels
|
||
|
{
|
||
|
proto: iana.ProtocolICMP,
|
||
|
typ: ipv4.ICMPTypeDestinationUnreachable,
|
||
|
hdr: []byte{
|
||
|
0x20, 0x00, 0x00, 0x00,
|
||
|
},
|
||
|
obj: []byte{
|
||
|
0x00, 0x0c, 0x01, 0x01,
|
||
|
0x03, 0xe8, 0xde, 0xfe,
|
||
|
0x03, 0xe8, 0xe1, 0xff,
|
||
|
},
|
||
|
ext: &MPLSLabelStack{
|
||
|
Class: classMPLSLabelStack,
|
||
|
Type: typeIncomingMPLSLabelStack,
|
||
|
Labels: []MPLSLabel{
|
||
|
{
|
||
|
Label: 16013,
|
||
|
TC: 0x7,
|
||
|
S: false,
|
||
|
TTL: 254,
|
||
|
},
|
||
|
{
|
||
|
Label: 16014,
|
||
|
TC: 0,
|
||
|
S: true,
|
||
|
TTL: 255,
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
} {
|
||
|
if err := fn(t, et.proto, et.typ, et.hdr, et.obj, et.ext); err != nil {
|
||
|
t.Error(err)
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
t.Run("InterfaceInfo", func(t *testing.T) {
|
||
|
for _, et := range []struct {
|
||
|
proto int
|
||
|
typ Type
|
||
|
hdr []byte
|
||
|
obj []byte
|
||
|
ext Extension
|
||
|
}{
|
||
|
// Interface information with no attribute
|
||
|
{
|
||
|
proto: iana.ProtocolICMP,
|
||
|
typ: ipv4.ICMPTypeDestinationUnreachable,
|
||
|
hdr: []byte{
|
||
|
0x20, 0x00, 0x00, 0x00,
|
||
|
},
|
||
|
obj: []byte{
|
||
|
0x00, 0x04, 0x02, 0x00,
|
||
|
},
|
||
|
ext: &InterfaceInfo{
|
||
|
Class: classInterfaceInfo,
|
||
|
},
|
||
|
},
|
||
|
// Interface information with ifIndex and name
|
||
|
{
|
||
|
proto: iana.ProtocolICMP,
|
||
|
typ: ipv4.ICMPTypeDestinationUnreachable,
|
||
|
hdr: []byte{
|
||
|
0x20, 0x00, 0x00, 0x00,
|
||
|
},
|
||
|
obj: []byte{
|
||
|
0x00, 0x10, 0x02, 0x0a,
|
||
|
0x00, 0x00, 0x00, 0x10,
|
||
|
0x08, byte('e'), byte('n'), byte('1'),
|
||
|
byte('0'), byte('1'), 0x00, 0x00,
|
||
|
},
|
||
|
ext: &InterfaceInfo{
|
||
|
Class: classInterfaceInfo,
|
||
|
Type: 0x0a,
|
||
|
Interface: &net.Interface{
|
||
|
Index: 16,
|
||
|
Name: "en101",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
// Interface information with ifIndex, IPAddr, name and MTU
|
||
|
{
|
||
|
proto: iana.ProtocolIPv6ICMP,
|
||
|
typ: ipv6.ICMPTypeDestinationUnreachable,
|
||
|
hdr: []byte{
|
||
|
0x20, 0x00, 0x00, 0x00,
|
||
|
},
|
||
|
obj: []byte{
|
||
|
0x00, 0x28, 0x02, 0x0f,
|
||
|
0x00, 0x00, 0x00, 0x0f,
|
||
|
0x00, 0x02, 0x00, 0x00,
|
||
|
0xfe, 0x80, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x00, 0x01,
|
||
|
0x08, byte('e'), byte('n'), byte('1'),
|
||
|
byte('0'), byte('1'), 0x00, 0x00,
|
||
|
0x00, 0x00, 0x20, 0x00,
|
||
|
},
|
||
|
ext: &InterfaceInfo{
|
||
|
Class: classInterfaceInfo,
|
||
|
Type: 0x0f,
|
||
|
Interface: &net.Interface{
|
||
|
Index: 15,
|
||
|
Name: "en101",
|
||
|
MTU: 8192,
|
||
|
},
|
||
|
Addr: &net.IPAddr{
|
||
|
IP: net.ParseIP("fe80::1"),
|
||
|
Zone: "en101",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
} {
|
||
|
if err := fn(t, et.proto, et.typ, et.hdr, et.obj, et.ext); err != nil {
|
||
|
t.Error(err)
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
t.Run("InterfaceIdent", func(t *testing.T) {
|
||
|
for _, et := range []struct {
|
||
|
proto int
|
||
|
typ Type
|
||
|
hdr []byte
|
||
|
obj []byte
|
||
|
ext Extension
|
||
|
}{
|
||
|
// Interface identification by name
|
||
|
{
|
||
|
proto: iana.ProtocolICMP,
|
||
|
typ: ipv4.ICMPTypeExtendedEchoRequest,
|
||
|
hdr: []byte{
|
||
|
0x20, 0x00, 0x00, 0x00,
|
||
|
},
|
||
|
obj: []byte{
|
||
|
0x00, 0x0c, 0x03, 0x01,
|
||
|
byte('e'), byte('n'), byte('1'), byte('0'),
|
||
|
byte('1'), 0x00, 0x00, 0x00,
|
||
|
},
|
||
|
ext: &InterfaceIdent{
|
||
|
Class: classInterfaceIdent,
|
||
|
Type: typeInterfaceByName,
|
||
|
Name: "en101",
|
||
|
},
|
||
|
},
|
||
|
// Interface identification by index
|
||
|
{
|
||
|
proto: iana.ProtocolIPv6ICMP,
|
||
|
typ: ipv6.ICMPTypeExtendedEchoRequest,
|
||
|
hdr: []byte{
|
||
|
0x20, 0x00, 0x00, 0x00,
|
||
|
},
|
||
|
obj: []byte{
|
||
|
0x00, 0x0c, 0x03, 0x02,
|
||
|
0x00, 0x00, 0x00, 0x00,
|
||
|
0x00, 0x00, 0x03, 0x8f,
|
||
|
},
|
||
|
ext: &InterfaceIdent{
|
||
|
Class: classInterfaceIdent,
|
||
|
Type: typeInterfaceByIndex,
|
||
|
Index: 911,
|
||
|
},
|
||
|
},
|
||
|
// Interface identification by address
|
||
|
{
|
||
|
proto: iana.ProtocolICMP,
|
||
|
typ: ipv4.ICMPTypeExtendedEchoRequest,
|
||
|
hdr: []byte{
|
||
|
0x20, 0x00, 0x00, 0x00,
|
||
|
},
|
||
|
obj: []byte{
|
||
|
0x00, 0x10, 0x03, 0x03,
|
||
|
byte(iana.AddrFamily48bitMAC >> 8), byte(iana.AddrFamily48bitMAC & 0x0f), 0x06, 0x00,
|
||
|
0x01, 0x23, 0x45, 0x67,
|
||
|
0x89, 0xab, 0x00, 0x00,
|
||
|
},
|
||
|
ext: &InterfaceIdent{
|
||
|
Class: classInterfaceIdent,
|
||
|
Type: typeInterfaceByAddress,
|
||
|
AFI: iana.AddrFamily48bitMAC,
|
||
|
Addr: []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab},
|
||
|
},
|
||
|
},
|
||
|
} {
|
||
|
if err := fn(t, et.proto, et.typ, et.hdr, et.obj, et.ext); err != nil {
|
||
|
t.Error(err)
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func TestParseInterfaceName(t *testing.T) {
|
||
|
ifi := InterfaceInfo{Interface: &net.Interface{}}
|
||
|
for i, tt := range []struct {
|
||
|
b []byte
|
||
|
error
|
||
|
}{
|
||
|
{[]byte{0, 'e', 'n', '0'}, errInvalidExtension},
|
||
|
{[]byte{4, 'e', 'n', '0'}, nil},
|
||
|
{[]byte{7, 'e', 'n', '0', 0xff, 0xff, 0xff, 0xff}, errInvalidExtension},
|
||
|
{[]byte{8, 'e', 'n', '0', 0xff, 0xff, 0xff}, errMessageTooShort},
|
||
|
} {
|
||
|
if _, err := ifi.parseName(tt.b); err != tt.error {
|
||
|
t.Errorf("#%d: got %v; want %v", i, err, tt.error)
|
||
|
}
|
||
|
}
|
||
|
}
|