// 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) } } }