|  | // 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_test | 
|  |  | 
|  | import ( | 
|  | "errors" | 
|  | "fmt" | 
|  | "net" | 
|  | "reflect" | 
|  | "testing" | 
|  |  | 
|  | "golang.org/x/net/icmp" | 
|  | "golang.org/x/net/internal/iana" | 
|  | "golang.org/x/net/ipv4" | 
|  | "golang.org/x/net/ipv6" | 
|  | ) | 
|  |  | 
|  | func TestMarshalAndParseMultipartMessage(t *testing.T) { | 
|  | fn := func(t *testing.T, proto int, tm icmp.Message) error { | 
|  | b, err := tm.Marshal(nil) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | switch tm.Type { | 
|  | case ipv4.ICMPTypeExtendedEchoRequest, ipv6.ICMPTypeExtendedEchoRequest: | 
|  | default: | 
|  | switch proto { | 
|  | case iana.ProtocolICMP: | 
|  | if b[5] != 32 { | 
|  | return fmt.Errorf("got %d; want 32", b[5]) | 
|  | } | 
|  | case iana.ProtocolIPv6ICMP: | 
|  | if b[4] != 16 { | 
|  | return fmt.Errorf("got %d; want 16", b[4]) | 
|  | } | 
|  | default: | 
|  | return fmt.Errorf("unknown protocol: %d", proto) | 
|  | } | 
|  | } | 
|  | m, err := icmp.ParseMessage(proto, b) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | if m.Type != tm.Type || m.Code != tm.Code { | 
|  | return fmt.Errorf("got %v; want %v", m, &tm) | 
|  | } | 
|  | switch m.Type { | 
|  | case ipv4.ICMPTypeExtendedEchoRequest, ipv6.ICMPTypeExtendedEchoRequest: | 
|  | got, want := m.Body.(*icmp.ExtendedEchoRequest), tm.Body.(*icmp.ExtendedEchoRequest) | 
|  | if !reflect.DeepEqual(got.Extensions, want.Extensions) { | 
|  | return errors.New(dumpExtensions(got.Extensions, want.Extensions)) | 
|  | } | 
|  | case ipv4.ICMPTypeDestinationUnreachable: | 
|  | got, want := m.Body.(*icmp.DstUnreach), tm.Body.(*icmp.DstUnreach) | 
|  | if !reflect.DeepEqual(got.Extensions, want.Extensions) { | 
|  | return errors.New(dumpExtensions(got.Extensions, want.Extensions)) | 
|  | } | 
|  | if len(got.Data) != 128 { | 
|  | return fmt.Errorf("got %d; want 128", len(got.Data)) | 
|  | } | 
|  | case ipv4.ICMPTypeTimeExceeded: | 
|  | got, want := m.Body.(*icmp.TimeExceeded), tm.Body.(*icmp.TimeExceeded) | 
|  | if !reflect.DeepEqual(got.Extensions, want.Extensions) { | 
|  | return errors.New(dumpExtensions(got.Extensions, want.Extensions)) | 
|  | } | 
|  | if len(got.Data) != 128 { | 
|  | return fmt.Errorf("got %d; want 128", len(got.Data)) | 
|  | } | 
|  | case ipv4.ICMPTypeParameterProblem: | 
|  | got, want := m.Body.(*icmp.ParamProb), tm.Body.(*icmp.ParamProb) | 
|  | if !reflect.DeepEqual(got.Extensions, want.Extensions) { | 
|  | return errors.New(dumpExtensions(got.Extensions, want.Extensions)) | 
|  | } | 
|  | if len(got.Data) != 128 { | 
|  | return fmt.Errorf("got %d; want 128", len(got.Data)) | 
|  | } | 
|  | case ipv6.ICMPTypeDestinationUnreachable: | 
|  | got, want := m.Body.(*icmp.DstUnreach), tm.Body.(*icmp.DstUnreach) | 
|  | if !reflect.DeepEqual(got.Extensions, want.Extensions) { | 
|  | return errors.New(dumpExtensions(got.Extensions, want.Extensions)) | 
|  | } | 
|  | if len(got.Data) != 128 { | 
|  | return fmt.Errorf("got %d; want 128", len(got.Data)) | 
|  | } | 
|  | case ipv6.ICMPTypeTimeExceeded: | 
|  | got, want := m.Body.(*icmp.TimeExceeded), tm.Body.(*icmp.TimeExceeded) | 
|  | if !reflect.DeepEqual(got.Extensions, want.Extensions) { | 
|  | return errors.New(dumpExtensions(got.Extensions, want.Extensions)) | 
|  | } | 
|  | if len(got.Data) != 128 { | 
|  | return fmt.Errorf("got %d; want 128", len(got.Data)) | 
|  | } | 
|  | default: | 
|  | return fmt.Errorf("unknown message type: %v", m.Type) | 
|  | } | 
|  | return nil | 
|  | } | 
|  |  | 
|  | t.Run("IPv4", func(t *testing.T) { | 
|  | for i, tm := range []icmp.Message{ | 
|  | { | 
|  | Type: ipv4.ICMPTypeDestinationUnreachable, Code: 15, | 
|  | Body: &icmp.DstUnreach{ | 
|  | Data: []byte("ERROR-INVOKING-PACKET"), | 
|  | Extensions: []icmp.Extension{ | 
|  | &icmp.MPLSLabelStack{ | 
|  | Class: 1, | 
|  | Type:  1, | 
|  | Labels: []icmp.MPLSLabel{ | 
|  | { | 
|  | Label: 16014, | 
|  | TC:    0x4, | 
|  | S:     true, | 
|  | TTL:   255, | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | &icmp.InterfaceInfo{ | 
|  | Class: 2, | 
|  | Type:  0x0f, | 
|  | Interface: &net.Interface{ | 
|  | Index: 15, | 
|  | Name:  "en101", | 
|  | MTU:   8192, | 
|  | }, | 
|  | Addr: &net.IPAddr{ | 
|  | IP: net.IPv4(192, 168, 0, 1).To4(), | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | { | 
|  | Type: ipv4.ICMPTypeTimeExceeded, Code: 1, | 
|  | Body: &icmp.TimeExceeded{ | 
|  | Data: []byte("ERROR-INVOKING-PACKET"), | 
|  | Extensions: []icmp.Extension{ | 
|  | &icmp.InterfaceInfo{ | 
|  | Class: 2, | 
|  | Type:  0x0f, | 
|  | Interface: &net.Interface{ | 
|  | Index: 15, | 
|  | Name:  "en101", | 
|  | MTU:   8192, | 
|  | }, | 
|  | Addr: &net.IPAddr{ | 
|  | IP: net.IPv4(192, 168, 0, 1).To4(), | 
|  | }, | 
|  | }, | 
|  | &icmp.MPLSLabelStack{ | 
|  | Class: 1, | 
|  | Type:  1, | 
|  | Labels: []icmp.MPLSLabel{ | 
|  | { | 
|  | Label: 16014, | 
|  | TC:    0x4, | 
|  | S:     true, | 
|  | TTL:   255, | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | { | 
|  | Type: ipv4.ICMPTypeParameterProblem, Code: 2, | 
|  | Body: &icmp.ParamProb{ | 
|  | Pointer: 8, | 
|  | Data:    []byte("ERROR-INVOKING-PACKET"), | 
|  | Extensions: []icmp.Extension{ | 
|  | &icmp.MPLSLabelStack{ | 
|  | Class: 1, | 
|  | Type:  1, | 
|  | Labels: []icmp.MPLSLabel{ | 
|  | { | 
|  | Label: 16014, | 
|  | TC:    0x4, | 
|  | S:     true, | 
|  | TTL:   255, | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | &icmp.InterfaceInfo{ | 
|  | Class: 2, | 
|  | Type:  0x0f, | 
|  | Interface: &net.Interface{ | 
|  | Index: 15, | 
|  | Name:  "en101", | 
|  | MTU:   8192, | 
|  | }, | 
|  | Addr: &net.IPAddr{ | 
|  | IP: net.IPv4(192, 168, 0, 1).To4(), | 
|  | }, | 
|  | }, | 
|  | &icmp.InterfaceInfo{ | 
|  | Class: 2, | 
|  | Type:  0x2f, | 
|  | Interface: &net.Interface{ | 
|  | Index: 16, | 
|  | Name:  "en102", | 
|  | MTU:   8192, | 
|  | }, | 
|  | Addr: &net.IPAddr{ | 
|  | IP: net.IPv4(192, 168, 0, 2).To4(), | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | { | 
|  | Type: ipv4.ICMPTypeExtendedEchoRequest, Code: 0, | 
|  | Body: &icmp.ExtendedEchoRequest{ | 
|  | ID: 1, Seq: 2, Local: true, | 
|  | Extensions: []icmp.Extension{ | 
|  | &icmp.InterfaceIdent{ | 
|  | Class: 3, | 
|  | Type:  1, | 
|  | Name:  "en101", | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | { | 
|  | Type: ipv4.ICMPTypeExtendedEchoRequest, Code: 0, | 
|  | Body: &icmp.ExtendedEchoRequest{ | 
|  | ID: 1, Seq: 2, Local: true, | 
|  | Extensions: []icmp.Extension{ | 
|  | &icmp.InterfaceIdent{ | 
|  | Class: 3, | 
|  | Type:  2, | 
|  | Index: 911, | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | { | 
|  | Type: ipv4.ICMPTypeExtendedEchoRequest, Code: 0, | 
|  | Body: &icmp.ExtendedEchoRequest{ | 
|  | ID: 1, Seq: 2, | 
|  | Extensions: []icmp.Extension{ | 
|  | &icmp.InterfaceIdent{ | 
|  | Class: 3, | 
|  | Type:  3, | 
|  | AFI:   iana.AddrFamily48bitMAC, | 
|  | Addr:  []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab}, | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | } { | 
|  | if err := fn(t, iana.ProtocolICMP, tm); err != nil { | 
|  | t.Errorf("#%d: %v", i, err) | 
|  | } | 
|  | } | 
|  | }) | 
|  | t.Run("IPv6", func(t *testing.T) { | 
|  | for i, tm := range []icmp.Message{ | 
|  | { | 
|  | Type: ipv6.ICMPTypeDestinationUnreachable, Code: 6, | 
|  | Body: &icmp.DstUnreach{ | 
|  | Data: []byte("ERROR-INVOKING-PACKET"), | 
|  | Extensions: []icmp.Extension{ | 
|  | &icmp.MPLSLabelStack{ | 
|  | Class: 1, | 
|  | Type:  1, | 
|  | Labels: []icmp.MPLSLabel{ | 
|  | { | 
|  | Label: 16014, | 
|  | TC:    0x4, | 
|  | S:     true, | 
|  | TTL:   255, | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | &icmp.InterfaceInfo{ | 
|  | Class: 2, | 
|  | Type:  0x0f, | 
|  | Interface: &net.Interface{ | 
|  | Index: 15, | 
|  | Name:  "en101", | 
|  | MTU:   8192, | 
|  | }, | 
|  | Addr: &net.IPAddr{ | 
|  | IP:   net.ParseIP("fe80::1"), | 
|  | Zone: "en101", | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | { | 
|  | Type: ipv6.ICMPTypeTimeExceeded, Code: 1, | 
|  | Body: &icmp.TimeExceeded{ | 
|  | Data: []byte("ERROR-INVOKING-PACKET"), | 
|  | Extensions: []icmp.Extension{ | 
|  | &icmp.InterfaceInfo{ | 
|  | Class: 2, | 
|  | Type:  0x0f, | 
|  | Interface: &net.Interface{ | 
|  | Index: 15, | 
|  | Name:  "en101", | 
|  | MTU:   8192, | 
|  | }, | 
|  | Addr: &net.IPAddr{ | 
|  | IP:   net.ParseIP("fe80::1"), | 
|  | Zone: "en101", | 
|  | }, | 
|  | }, | 
|  | &icmp.MPLSLabelStack{ | 
|  | Class: 1, | 
|  | Type:  1, | 
|  | Labels: []icmp.MPLSLabel{ | 
|  | { | 
|  | Label: 16014, | 
|  | TC:    0x4, | 
|  | S:     true, | 
|  | TTL:   255, | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | &icmp.InterfaceInfo{ | 
|  | Class: 2, | 
|  | Type:  0x2f, | 
|  | Interface: &net.Interface{ | 
|  | Index: 16, | 
|  | Name:  "en102", | 
|  | MTU:   8192, | 
|  | }, | 
|  | Addr: &net.IPAddr{ | 
|  | IP:   net.ParseIP("fe80::1"), | 
|  | Zone: "en102", | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | { | 
|  | Type: ipv6.ICMPTypeExtendedEchoRequest, Code: 0, | 
|  | Body: &icmp.ExtendedEchoRequest{ | 
|  | ID: 1, Seq: 2, Local: true, | 
|  | Extensions: []icmp.Extension{ | 
|  | &icmp.InterfaceIdent{ | 
|  | Class: 3, | 
|  | Type:  1, | 
|  | Name:  "en101", | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | { | 
|  | Type: ipv6.ICMPTypeExtendedEchoRequest, Code: 0, | 
|  | Body: &icmp.ExtendedEchoRequest{ | 
|  | ID: 1, Seq: 2, Local: true, | 
|  | Extensions: []icmp.Extension{ | 
|  | &icmp.InterfaceIdent{ | 
|  | Class: 3, | 
|  | Type:  2, | 
|  | Index: 911, | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | { | 
|  | Type: ipv6.ICMPTypeExtendedEchoRequest, Code: 0, | 
|  | Body: &icmp.ExtendedEchoRequest{ | 
|  | ID: 1, Seq: 2, | 
|  | Extensions: []icmp.Extension{ | 
|  | &icmp.InterfaceIdent{ | 
|  | Class: 3, | 
|  | Type:  3, | 
|  | AFI:   iana.AddrFamilyIPv4, | 
|  | Addr:  []byte{192, 0, 2, 1}, | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | } { | 
|  | if err := fn(t, iana.ProtocolIPv6ICMP, tm); err != nil { | 
|  | t.Errorf("#%d: %v", i, err) | 
|  | } | 
|  | } | 
|  | }) | 
|  | } | 
|  |  | 
|  | func dumpExtensions(gotExts, wantExts []icmp.Extension) string { | 
|  | var s string | 
|  | for i, got := range gotExts { | 
|  | switch got := got.(type) { | 
|  | case *icmp.MPLSLabelStack: | 
|  | want := wantExts[i].(*icmp.MPLSLabelStack) | 
|  | if !reflect.DeepEqual(got, want) { | 
|  | s += fmt.Sprintf("#%d: got %#v; want %#v\n", i, got, want) | 
|  | } | 
|  | case *icmp.InterfaceInfo: | 
|  | want := wantExts[i].(*icmp.InterfaceInfo) | 
|  | if !reflect.DeepEqual(got, want) { | 
|  | s += fmt.Sprintf("#%d: got %#v, %#v, %#v; want %#v, %#v, %#v\n", i, got, got.Interface, got.Addr, want, want.Interface, want.Addr) | 
|  | } | 
|  | case *icmp.InterfaceIdent: | 
|  | want := wantExts[i].(*icmp.InterfaceIdent) | 
|  | if !reflect.DeepEqual(got, want) { | 
|  | s += fmt.Sprintf("#%d: got %#v; want %#v\n", i, got, want) | 
|  | } | 
|  | case *icmp.RawExtension: | 
|  | s += fmt.Sprintf("#%d: raw extension\n", i) | 
|  | } | 
|  | } | 
|  | if len(s) == 0 { | 
|  | s += "empty extension" | 
|  | } | 
|  | return s[:len(s)-1] | 
|  | } | 
|  |  | 
|  | func TestMultipartMessageBodyLen(t *testing.T) { | 
|  | for i, tt := range []struct { | 
|  | proto int | 
|  | in    icmp.MessageBody | 
|  | out   int | 
|  | }{ | 
|  | { | 
|  | iana.ProtocolICMP, | 
|  | &icmp.DstUnreach{ | 
|  | Data: make([]byte, ipv4.HeaderLen), | 
|  | }, | 
|  | 4 + ipv4.HeaderLen, // unused and original datagram | 
|  | }, | 
|  | { | 
|  | iana.ProtocolICMP, | 
|  | &icmp.TimeExceeded{ | 
|  | Data: make([]byte, ipv4.HeaderLen), | 
|  | }, | 
|  | 4 + ipv4.HeaderLen, // unused and original datagram | 
|  | }, | 
|  | { | 
|  | iana.ProtocolICMP, | 
|  | &icmp.ParamProb{ | 
|  | Data: make([]byte, ipv4.HeaderLen), | 
|  | }, | 
|  | 4 + ipv4.HeaderLen, // [pointer, unused] and original datagram | 
|  | }, | 
|  |  | 
|  | { | 
|  | iana.ProtocolICMP, | 
|  | &icmp.ParamProb{ | 
|  | Data: make([]byte, ipv4.HeaderLen), | 
|  | Extensions: []icmp.Extension{ | 
|  | &icmp.MPLSLabelStack{}, | 
|  | }, | 
|  | }, | 
|  | 4 + 4 + 4 + 0 + 128, // [pointer, length, unused], extension header, object header, object payload, original datagram | 
|  | }, | 
|  | { | 
|  | iana.ProtocolICMP, | 
|  | &icmp.ParamProb{ | 
|  | Data: make([]byte, 128), | 
|  | Extensions: []icmp.Extension{ | 
|  | &icmp.MPLSLabelStack{}, | 
|  | }, | 
|  | }, | 
|  | 4 + 4 + 4 + 0 + 128, // [pointer, length, unused], extension header, object header, object payload and original datagram | 
|  | }, | 
|  | { | 
|  | iana.ProtocolICMP, | 
|  | &icmp.ParamProb{ | 
|  | Data: make([]byte, 129), | 
|  | Extensions: []icmp.Extension{ | 
|  | &icmp.MPLSLabelStack{}, | 
|  | }, | 
|  | }, | 
|  | 4 + 4 + 4 + 0 + 132, // [pointer, length, unused], extension header, object header, object payload and original datagram | 
|  | }, | 
|  |  | 
|  | { | 
|  | iana.ProtocolIPv6ICMP, | 
|  | &icmp.DstUnreach{ | 
|  | Data: make([]byte, ipv6.HeaderLen), | 
|  | }, | 
|  | 4 + ipv6.HeaderLen, // unused and original datagram | 
|  | }, | 
|  | { | 
|  | iana.ProtocolIPv6ICMP, | 
|  | &icmp.PacketTooBig{ | 
|  | Data: make([]byte, ipv6.HeaderLen), | 
|  | }, | 
|  | 4 + ipv6.HeaderLen, // mtu and original datagram | 
|  | }, | 
|  | { | 
|  | iana.ProtocolIPv6ICMP, | 
|  | &icmp.TimeExceeded{ | 
|  | Data: make([]byte, ipv6.HeaderLen), | 
|  | }, | 
|  | 4 + ipv6.HeaderLen, // unused and original datagram | 
|  | }, | 
|  | { | 
|  | iana.ProtocolIPv6ICMP, | 
|  | &icmp.ParamProb{ | 
|  | Data: make([]byte, ipv6.HeaderLen), | 
|  | }, | 
|  | 4 + ipv6.HeaderLen, // pointer and original datagram | 
|  | }, | 
|  |  | 
|  | { | 
|  | iana.ProtocolIPv6ICMP, | 
|  | &icmp.DstUnreach{ | 
|  | Data: make([]byte, 127), | 
|  | Extensions: []icmp.Extension{ | 
|  | &icmp.MPLSLabelStack{}, | 
|  | }, | 
|  | }, | 
|  | 4 + 4 + 4 + 0 + 128, // [length, unused], extension header, object header, object payload and original datagram | 
|  | }, | 
|  | { | 
|  | iana.ProtocolIPv6ICMP, | 
|  | &icmp.DstUnreach{ | 
|  | Data: make([]byte, 128), | 
|  | Extensions: []icmp.Extension{ | 
|  | &icmp.MPLSLabelStack{}, | 
|  | }, | 
|  | }, | 
|  | 4 + 4 + 4 + 0 + 128, // [length, unused], extension header, object header, object payload and original datagram | 
|  | }, | 
|  | { | 
|  | iana.ProtocolIPv6ICMP, | 
|  | &icmp.DstUnreach{ | 
|  | Data: make([]byte, 129), | 
|  | Extensions: []icmp.Extension{ | 
|  | &icmp.MPLSLabelStack{}, | 
|  | }, | 
|  | }, | 
|  | 4 + 4 + 4 + 0 + 136, // [length, unused], extension header, object header, object payload and original datagram | 
|  | }, | 
|  |  | 
|  | { | 
|  | iana.ProtocolICMP, | 
|  | &icmp.ExtendedEchoRequest{}, | 
|  | 4, // [id, seq, l-bit] | 
|  | }, | 
|  | { | 
|  | iana.ProtocolICMP, | 
|  | &icmp.ExtendedEchoRequest{ | 
|  | Extensions: []icmp.Extension{ | 
|  | &icmp.InterfaceIdent{}, | 
|  | }, | 
|  | }, | 
|  | 4 + 4 + 4, // [id, seq, l-bit], extension header, object header | 
|  | }, | 
|  | { | 
|  | iana.ProtocolIPv6ICMP, | 
|  | &icmp.ExtendedEchoRequest{ | 
|  | Extensions: []icmp.Extension{ | 
|  | &icmp.InterfaceIdent{ | 
|  | Type: 3, | 
|  | AFI:  iana.AddrFamilyNSAP, | 
|  | Addr: []byte{0x49, 0x00, 0x01, 0xaa, 0xaa, 0xbb, 0xbb, 0xcc, 0xcc, 0x00}, | 
|  | }, | 
|  | }, | 
|  | }, | 
|  | 4 + 4 + 4 + 16, // [id, seq, l-bit], extension header, object header, object payload | 
|  | }, | 
|  | } { | 
|  | if out := tt.in.Len(tt.proto); out != tt.out { | 
|  | t.Errorf("#%d: got %d; want %d", i, out, tt.out) | 
|  | } | 
|  | } | 
|  | } |