Commit 6078986f authored by Mikio Hara's avatar Mikio Hara

icmp: add support for extended echo request and echo reply messages

This change implements support for extended echo request and reply
messages, and interface identification object that can be used to test
the status of a probed interface via a proxy interface from a probing
interface as defined in RFC 8335.

It's package user's responsibility to make a complete RFC-compliant
PROBE initiator implementation using ipv4, ipv6 and icmp packages of
x/net repository.

Fixes golang/go#24440.

Change-Id: I87ab8a7581c4d41a0c579805b0e3043e48ac85f0
Reviewed-on: https://go-review.googlesource.com/63999Reviewed-by: 's avatarIan Lance Taylor <iant@golang.org>
parent 92b859f3
...@@ -71,8 +71,7 @@ func TestDiag(t *testing.T) { ...@@ -71,8 +71,7 @@ func TestDiag(t *testing.T) {
}) })
t.Run("Ping/Privileged", func(t *testing.T) { t.Run("Ping/Privileged", func(t *testing.T) {
if m, ok := nettest.SupportsRawIPSocket(); !ok { if m, ok := nettest.SupportsRawIPSocket(); !ok {
t.Log(m) t.Skip(m)
return
} }
for i, dt := range []diagTest{ for i, dt := range []diagTest{
{ {
...@@ -102,6 +101,50 @@ func TestDiag(t *testing.T) { ...@@ -102,6 +101,50 @@ func TestDiag(t *testing.T) {
} }
} }
}) })
t.Run("Probe/Privileged", func(t *testing.T) {
if m, ok := nettest.SupportsRawIPSocket(); !ok {
t.Skip(m)
}
for i, dt := range []diagTest{
{
"ip4:icmp", "0.0.0.0", iana.ProtocolICMP,
icmp.Message{
Type: ipv4.ICMPTypeExtendedEchoRequest, Code: 0,
Body: &icmp.ExtendedEchoRequest{
ID: os.Getpid() & 0xffff,
Local: true,
Extensions: []icmp.Extension{
&icmp.InterfaceIdent{
Class: 3, Type: 1,
Name: "doesnotexist",
},
},
},
},
},
{
"ip6:ipv6-icmp", "::", iana.ProtocolIPv6ICMP,
icmp.Message{
Type: ipv6.ICMPTypeExtendedEchoRequest, Code: 0,
Body: &icmp.ExtendedEchoRequest{
ID: os.Getpid() & 0xffff,
Local: true,
Extensions: []icmp.Extension{
&icmp.InterfaceIdent{
Class: 3, Type: 1,
Name: "doesnotexist",
},
},
},
},
},
} {
if err := doDiag(dt, i); err != nil {
t.Error(err)
}
}
})
} }
func doDiag(dt diagTest, seq int) error { func doDiag(dt diagTest, seq int) error {
...@@ -124,6 +167,7 @@ func doDiag(dt diagTest, seq int) error { ...@@ -124,6 +167,7 @@ func doDiag(dt diagTest, seq int) error {
f.Accept(ipv6.ICMPTypeTimeExceeded) f.Accept(ipv6.ICMPTypeTimeExceeded)
f.Accept(ipv6.ICMPTypeParameterProblem) f.Accept(ipv6.ICMPTypeParameterProblem)
f.Accept(ipv6.ICMPTypeEchoReply) f.Accept(ipv6.ICMPTypeEchoReply)
f.Accept(ipv6.ICMPTypeExtendedEchoReply)
if err := c.IPv6PacketConn().SetICMPFilter(&f); err != nil { if err := c.IPv6PacketConn().SetICMPFilter(&f); err != nil {
return err return err
} }
...@@ -132,6 +176,8 @@ func doDiag(dt diagTest, seq int) error { ...@@ -132,6 +176,8 @@ func doDiag(dt diagTest, seq int) error {
switch m := dt.m.Body.(type) { switch m := dt.m.Body.(type) {
case *icmp.Echo: case *icmp.Echo:
m.Seq = 1 << uint(seq) m.Seq = 1 << uint(seq)
case *icmp.ExtendedEchoRequest:
m.Seq = 1 << uint(seq)
} }
wb, err := dt.m.Marshal(nil) wb, err := dt.m.Marshal(nil)
if err != nil { if err != nil {
...@@ -159,9 +205,13 @@ func doDiag(dt diagTest, seq int) error { ...@@ -159,9 +205,13 @@ func doDiag(dt diagTest, seq int) error {
case dt.m.Type == ipv4.ICMPTypeEcho && rm.Type == ipv4.ICMPTypeEchoReply: case dt.m.Type == ipv4.ICMPTypeEcho && rm.Type == ipv4.ICMPTypeEchoReply:
fallthrough fallthrough
case dt.m.Type == ipv6.ICMPTypeEchoRequest && rm.Type == ipv6.ICMPTypeEchoReply: case dt.m.Type == ipv6.ICMPTypeEchoRequest && rm.Type == ipv6.ICMPTypeEchoReply:
fallthrough
case dt.m.Type == ipv4.ICMPTypeExtendedEchoRequest && rm.Type == ipv4.ICMPTypeExtendedEchoReply:
fallthrough
case dt.m.Type == ipv6.ICMPTypeExtendedEchoRequest && rm.Type == ipv6.ICMPTypeExtendedEchoReply:
return nil return nil
default: default:
return fmt.Errorf("got %+v from %v; want echo reply", rm, peer) return fmt.Errorf("got %+v from %v; want echo reply or extended echo reply", rm, peer)
} }
} }
......
...@@ -16,24 +16,24 @@ func (p *DstUnreach) Len(proto int) int { ...@@ -16,24 +16,24 @@ func (p *DstUnreach) Len(proto int) int {
if p == nil { if p == nil {
return 0 return 0
} }
l, _ := multipartMessageBodyDataLen(proto, p.Data, p.Extensions) l, _ := multipartMessageBodyDataLen(proto, true, p.Data, p.Extensions)
return 4 + l return 4 + l
} }
// Marshal implements the Marshal method of MessageBody interface. // Marshal implements the Marshal method of MessageBody interface.
func (p *DstUnreach) Marshal(proto int) ([]byte, error) { func (p *DstUnreach) Marshal(proto int) ([]byte, error) {
return marshalMultipartMessageBody(proto, p.Data, p.Extensions) return marshalMultipartMessageBody(proto, true, p.Data, p.Extensions)
} }
// parseDstUnreach parses b as an ICMP destination unreachable message // parseDstUnreach parses b as an ICMP destination unreachable message
// body. // body.
func parseDstUnreach(proto int, b []byte) (MessageBody, error) { func parseDstUnreach(proto int, typ Type, b []byte) (MessageBody, error) {
if len(b) < 4 { if len(b) < 4 {
return nil, errMessageTooShort return nil, errMessageTooShort
} }
p := &DstUnreach{} p := &DstUnreach{}
var err error var err error
p.Data, p.Extensions, err = parseMultipartMessageBody(proto, b) p.Data, p.Extensions, err = parseMultipartMessageBody(proto, typ, b)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
...@@ -31,7 +31,7 @@ func (p *Echo) Marshal(proto int) ([]byte, error) { ...@@ -31,7 +31,7 @@ func (p *Echo) Marshal(proto int) ([]byte, error) {
} }
// parseEcho parses b as an ICMP echo request or reply message body. // parseEcho parses b as an ICMP echo request or reply message body.
func parseEcho(proto int, b []byte) (MessageBody, error) { func parseEcho(proto int, _ Type, b []byte) (MessageBody, error) {
bodyLen := len(b) bodyLen := len(b)
if bodyLen < 4 { if bodyLen < 4 {
return nil, errMessageTooShort return nil, errMessageTooShort
...@@ -43,3 +43,115 @@ func parseEcho(proto int, b []byte) (MessageBody, error) { ...@@ -43,3 +43,115 @@ func parseEcho(proto int, b []byte) (MessageBody, error) {
} }
return p, nil return p, nil
} }
// An ExtendedEchoRequest represents an ICMP extended echo request
// message body.
type ExtendedEchoRequest struct {
ID int // identifier
Seq int // sequence number
Local bool // must be true when identifying by name or index
Extensions []Extension // extensions
}
// Len implements the Len method of MessageBody interface.
func (p *ExtendedEchoRequest) Len(proto int) int {
if p == nil {
return 0
}
l, _ := multipartMessageBodyDataLen(proto, false, nil, p.Extensions)
return 4 + l
}
// Marshal implements the Marshal method of MessageBody interface.
func (p *ExtendedEchoRequest) Marshal(proto int) ([]byte, error) {
b, err := marshalMultipartMessageBody(proto, false, nil, p.Extensions)
if err != nil {
return nil, err
}
bb := make([]byte, 4)
binary.BigEndian.PutUint16(bb[:2], uint16(p.ID))
bb[2] = byte(p.Seq)
if p.Local {
bb[3] |= 0x01
}
bb = append(bb, b...)
return bb, nil
}
// parseExtendedEchoRequest parses b as an ICMP extended echo request
// message body.
func parseExtendedEchoRequest(proto int, typ Type, b []byte) (MessageBody, error) {
if len(b) < 4+4 {
return nil, errMessageTooShort
}
p := &ExtendedEchoRequest{ID: int(binary.BigEndian.Uint16(b[:2])), Seq: int(b[2])}
if b[3]&0x01 != 0 {
p.Local = true
}
var err error
_, p.Extensions, err = parseMultipartMessageBody(proto, typ, b[4:])
if err != nil {
return nil, err
}
return p, nil
}
// An ExtendedEchoReply represents an ICMP extended echo reply message
// body.
type ExtendedEchoReply struct {
ID int // identifier
Seq int // sequence number
State int // 3-bit state working together with Message.Code
Active bool // probed interface is active
IPv4 bool // probed interface runs IPv4
IPv6 bool // probed interface runs IPv6
}
// Len implements the Len method of MessageBody interface.
func (p *ExtendedEchoReply) Len(proto int) int {
if p == nil {
return 0
}
return 4
}
// Marshal implements the Marshal method of MessageBody interface.
func (p *ExtendedEchoReply) Marshal(proto int) ([]byte, error) {
b := make([]byte, 4)
binary.BigEndian.PutUint16(b[:2], uint16(p.ID))
b[2] = byte(p.Seq)
b[3] = byte(p.State<<5) & 0xe0
if p.Active {
b[3] |= 0x04
}
if p.IPv4 {
b[3] |= 0x02
}
if p.IPv6 {
b[3] |= 0x01
}
return b, nil
}
// parseExtendedEchoReply parses b as an ICMP extended echo reply
// message body.
func parseExtendedEchoReply(proto int, _ Type, b []byte) (MessageBody, error) {
if len(b) < 4 {
return nil, errMessageTooShort
}
p := &ExtendedEchoReply{
ID: int(binary.BigEndian.Uint16(b[:2])),
Seq: int(b[2]),
State: int(b[3]) >> 5,
}
if b[3]&0x04 != 0 {
p.Active = true
}
if b[3]&0x02 != 0 {
p.IPv4 = true
}
if b[3]&0x01 != 0 {
p.IPv6 = true
}
return p, nil
}
...@@ -4,7 +4,12 @@ ...@@ -4,7 +4,12 @@
package icmp package icmp
import "encoding/binary" import (
"encoding/binary"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
)
// An Extension represents an ICMP extension. // An Extension represents an ICMP extension.
type Extension interface { type Extension interface {
...@@ -38,7 +43,7 @@ func validExtensionHeader(b []byte) bool { ...@@ -38,7 +43,7 @@ func validExtensionHeader(b []byte) bool {
// It will return a list of ICMP extensions and an adjusted length // It will return a list of ICMP extensions and an adjusted length
// attribute that represents the length of the padded original // attribute that represents the length of the padded original
// datagram field. Otherwise, it returns an error. // datagram field. Otherwise, it returns an error.
func parseExtensions(b []byte, l int) ([]Extension, int, error) { func parseExtensions(typ Type, b []byte, l int) ([]Extension, int, error) {
// Still a lot of non-RFC 4884 compliant implementations are // Still a lot of non-RFC 4884 compliant implementations are
// out there. Set the length attribute l to 128 when it looks // out there. Set the length attribute l to 128 when it looks
// inappropriate for backwards compatibility. // inappropriate for backwards compatibility.
...@@ -48,20 +53,28 @@ func parseExtensions(b []byte, l int) ([]Extension, int, error) { ...@@ -48,20 +53,28 @@ func parseExtensions(b []byte, l int) ([]Extension, int, error) {
// header. // header.
// //
// See RFC 4884 for further information. // See RFC 4884 for further information.
if 128 > l || l+8 > len(b) { switch typ {
l = 128 case ipv4.ICMPTypeExtendedEchoRequest, ipv6.ICMPTypeExtendedEchoRequest:
} if len(b) < 8 || !validExtensionHeader(b) {
if l+8 > len(b) {
return nil, -1, errNoExtension
}
if !validExtensionHeader(b[l:]) {
if l == 128 {
return nil, -1, errNoExtension return nil, -1, errNoExtension
} }
l = 128 l = 0
if !validExtensionHeader(b[l:]) { default:
if 128 > l || l+8 > len(b) {
l = 128
}
if l+8 > len(b) {
return nil, -1, errNoExtension return nil, -1, errNoExtension
} }
if !validExtensionHeader(b[l:]) {
if l == 128 {
return nil, -1, errNoExtension
}
l = 128
if !validExtensionHeader(b[l:]) {
return nil, -1, errNoExtension
}
}
} }
var exts []Extension var exts []Extension
for b = b[l+4:]; len(b) >= 4; { for b = b[l+4:]; len(b) >= 4; {
...@@ -82,6 +95,12 @@ func parseExtensions(b []byte, l int) ([]Extension, int, error) { ...@@ -82,6 +95,12 @@ func parseExtensions(b []byte, l int) ([]Extension, int, error) {
return nil, -1, err return nil, -1, err
} }
exts = append(exts, ext) exts = append(exts, ext)
case classInterfaceIdent:
ext, err := parseInterfaceIdent(b[:ol])
if err != nil {
return nil, -1, err
}
exts = append(exts, ext)
} }
b = b[ol:] b = b[ol:]
} }
......
...@@ -11,10 +11,12 @@ import ( ...@@ -11,10 +11,12 @@ import (
"testing" "testing"
"golang.org/x/net/internal/iana" "golang.org/x/net/internal/iana"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
) )
func TestMarshalAndParseExtension(t *testing.T) { func TestMarshalAndParseExtension(t *testing.T) {
fn := func(t *testing.T, proto int, hdr, obj []byte, te Extension) error { fn := func(t *testing.T, proto int, typ Type, hdr, obj []byte, te Extension) error {
b, err := te.Marshal(proto) b, err := te.Marshal(proto)
if err != nil { if err != nil {
return err return err
...@@ -22,39 +24,53 @@ func TestMarshalAndParseExtension(t *testing.T) { ...@@ -22,39 +24,53 @@ func TestMarshalAndParseExtension(t *testing.T) {
if !reflect.DeepEqual(b, obj) { if !reflect.DeepEqual(b, obj) {
return fmt.Errorf("got %#v; want %#v", b, obj) return fmt.Errorf("got %#v; want %#v", b, obj)
} }
for i, wire := range []struct { switch typ {
data []byte // original datagram case ipv4.ICMPTypeExtendedEchoRequest, ipv6.ICMPTypeExtendedEchoRequest:
inlattr int // length of padded original datagram, a hint exts, l, err := parseExtensions(typ, append(hdr, obj...), 0)
outlattr int // length of padded original datagram, a want if err != nil {
err error return err
}{
{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(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 { if l != 0 {
return fmt.Errorf("#%d: got %d; want %d", i, l, wire.outlattr) return fmt.Errorf("got %d; want 0", l)
} }
if !reflect.DeepEqual(exts, []Extension{te}) { if !reflect.DeepEqual(exts, []Extension{te}) {
return fmt.Errorf("#%d: got %#v; want %#v", i, exts[0], 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 return nil
...@@ -63,6 +79,7 @@ func TestMarshalAndParseExtension(t *testing.T) { ...@@ -63,6 +79,7 @@ func TestMarshalAndParseExtension(t *testing.T) {
t.Run("MPLSLabelStack", func(t *testing.T) { t.Run("MPLSLabelStack", func(t *testing.T) {
for _, et := range []struct { for _, et := range []struct {
proto int proto int
typ Type
hdr []byte hdr []byte
obj []byte obj []byte
ext Extension ext Extension
...@@ -70,6 +87,7 @@ func TestMarshalAndParseExtension(t *testing.T) { ...@@ -70,6 +87,7 @@ func TestMarshalAndParseExtension(t *testing.T) {
// MPLS label stack with no label // MPLS label stack with no label
{ {
proto: iana.ProtocolICMP, proto: iana.ProtocolICMP,
typ: ipv4.ICMPTypeDestinationUnreachable,
hdr: []byte{ hdr: []byte{
0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
}, },
...@@ -84,6 +102,7 @@ func TestMarshalAndParseExtension(t *testing.T) { ...@@ -84,6 +102,7 @@ func TestMarshalAndParseExtension(t *testing.T) {
// MPLS label stack with a single label // MPLS label stack with a single label
{ {
proto: iana.ProtocolIPv6ICMP, proto: iana.ProtocolIPv6ICMP,
typ: ipv6.ICMPTypeDestinationUnreachable,
hdr: []byte{ hdr: []byte{
0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
}, },
...@@ -107,6 +126,7 @@ func TestMarshalAndParseExtension(t *testing.T) { ...@@ -107,6 +126,7 @@ func TestMarshalAndParseExtension(t *testing.T) {
// MPLS label stack with multiple labels // MPLS label stack with multiple labels
{ {
proto: iana.ProtocolICMP, proto: iana.ProtocolICMP,
typ: ipv4.ICMPTypeDestinationUnreachable,
hdr: []byte{ hdr: []byte{
0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
}, },
...@@ -135,7 +155,7 @@ func TestMarshalAndParseExtension(t *testing.T) { ...@@ -135,7 +155,7 @@ func TestMarshalAndParseExtension(t *testing.T) {
}, },
}, },
} { } {
if err := fn(t, et.proto, et.hdr, et.obj, et.ext); err != nil { if err := fn(t, et.proto, et.typ, et.hdr, et.obj, et.ext); err != nil {
t.Error(err) t.Error(err)
} }
} }
...@@ -143,6 +163,7 @@ func TestMarshalAndParseExtension(t *testing.T) { ...@@ -143,6 +163,7 @@ func TestMarshalAndParseExtension(t *testing.T) {
t.Run("InterfaceInfo", func(t *testing.T) { t.Run("InterfaceInfo", func(t *testing.T) {
for _, et := range []struct { for _, et := range []struct {
proto int proto int
typ Type
hdr []byte hdr []byte
obj []byte obj []byte
ext Extension ext Extension
...@@ -150,6 +171,7 @@ func TestMarshalAndParseExtension(t *testing.T) { ...@@ -150,6 +171,7 @@ func TestMarshalAndParseExtension(t *testing.T) {
// Interface information with no attribute // Interface information with no attribute
{ {
proto: iana.ProtocolICMP, proto: iana.ProtocolICMP,
typ: ipv4.ICMPTypeDestinationUnreachable,
hdr: []byte{ hdr: []byte{
0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
}, },
...@@ -163,6 +185,7 @@ func TestMarshalAndParseExtension(t *testing.T) { ...@@ -163,6 +185,7 @@ func TestMarshalAndParseExtension(t *testing.T) {
// Interface information with ifIndex and name // Interface information with ifIndex and name
{ {
proto: iana.ProtocolICMP, proto: iana.ProtocolICMP,
typ: ipv4.ICMPTypeDestinationUnreachable,
hdr: []byte{ hdr: []byte{
0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
}, },
...@@ -184,6 +207,7 @@ func TestMarshalAndParseExtension(t *testing.T) { ...@@ -184,6 +207,7 @@ func TestMarshalAndParseExtension(t *testing.T) {
// Interface information with ifIndex, IPAddr, name and MTU // Interface information with ifIndex, IPAddr, name and MTU
{ {
proto: iana.ProtocolIPv6ICMP, proto: iana.ProtocolIPv6ICMP,
typ: ipv6.ICMPTypeDestinationUnreachable,
hdr: []byte{ hdr: []byte{
0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
}, },
...@@ -214,7 +238,77 @@ func TestMarshalAndParseExtension(t *testing.T) { ...@@ -214,7 +238,77 @@ func TestMarshalAndParseExtension(t *testing.T) {
}, },
}, },
} { } {
if err := fn(t, et.proto, et.hdr, et.obj, et.ext); err != nil { 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) t.Error(err)
} }
} }
......
...@@ -14,9 +14,6 @@ import ( ...@@ -14,9 +14,6 @@ import (
const ( const (
classInterfaceInfo = 2 classInterfaceInfo = 2
afiIPv4 = 1
afiIPv6 = 2
) )
const ( const (
...@@ -127,11 +124,11 @@ func (ifi *InterfaceInfo) parseIfIndex(b []byte) ([]byte, error) { ...@@ -127,11 +124,11 @@ func (ifi *InterfaceInfo) parseIfIndex(b []byte) ([]byte, error) {
func (ifi *InterfaceInfo) marshalIPAddr(proto int, b []byte) []byte { func (ifi *InterfaceInfo) marshalIPAddr(proto int, b []byte) []byte {
switch proto { switch proto {
case iana.ProtocolICMP: case iana.ProtocolICMP:
binary.BigEndian.PutUint16(b[:2], uint16(afiIPv4)) binary.BigEndian.PutUint16(b[:2], uint16(iana.AddrFamilyIPv4))
copy(b[4:4+net.IPv4len], ifi.Addr.IP.To4()) copy(b[4:4+net.IPv4len], ifi.Addr.IP.To4())
b = b[4+net.IPv4len:] b = b[4+net.IPv4len:]
case iana.ProtocolIPv6ICMP: case iana.ProtocolIPv6ICMP:
binary.BigEndian.PutUint16(b[:2], uint16(afiIPv6)) binary.BigEndian.PutUint16(b[:2], uint16(iana.AddrFamilyIPv6))
copy(b[4:4+net.IPv6len], ifi.Addr.IP.To16()) copy(b[4:4+net.IPv6len], ifi.Addr.IP.To16())
b = b[4+net.IPv6len:] b = b[4+net.IPv6len:]
} }
...@@ -145,14 +142,14 @@ func (ifi *InterfaceInfo) parseIPAddr(b []byte) ([]byte, error) { ...@@ -145,14 +142,14 @@ func (ifi *InterfaceInfo) parseIPAddr(b []byte) ([]byte, error) {
afi := int(binary.BigEndian.Uint16(b[:2])) afi := int(binary.BigEndian.Uint16(b[:2]))
b = b[4:] b = b[4:]
switch afi { switch afi {
case afiIPv4: case iana.AddrFamilyIPv4:
if len(b) < net.IPv4len { if len(b) < net.IPv4len {
return nil, errMessageTooShort return nil, errMessageTooShort
} }
ifi.Addr.IP = make(net.IP, net.IPv4len) ifi.Addr.IP = make(net.IP, net.IPv4len)
copy(ifi.Addr.IP, b[:net.IPv4len]) copy(ifi.Addr.IP, b[:net.IPv4len])
b = b[net.IPv4len:] b = b[net.IPv4len:]
case afiIPv6: case iana.AddrFamilyIPv6:
if len(b) < net.IPv6len { if len(b) < net.IPv6len {
return nil, errMessageTooShort return nil, errMessageTooShort
} }
...@@ -234,3 +231,92 @@ func parseInterfaceInfo(b []byte) (Extension, error) { ...@@ -234,3 +231,92 @@ func parseInterfaceInfo(b []byte) (Extension, error) {
} }
return ifi, nil return ifi, nil
} }
const (
classInterfaceIdent = 3
typeInterfaceByName = 1
typeInterfaceByIndex = 2
typeInterfaceByAddress = 3
)
// An InterfaceIdent represents interface identification.
type InterfaceIdent struct {
Class int // extension object class number
Type int // extension object sub-type
Name string // interface name
Index int // interface index
AFI int // address family identifier; see address family numbers in IANA registry
Addr []byte // address
}
// Len implements the Len method of Extension interface.
func (ifi *InterfaceIdent) Len(_ int) int {
switch ifi.Type {
case typeInterfaceByName:
l := len(ifi.Name)
if l > 255 {
l = 255
}
return 4 + (l+3)&^3
case typeInterfaceByIndex:
return 4 + 8
case typeInterfaceByAddress:
return 4 + 4 + (len(ifi.Addr)+3)&^3
default:
return 4
}
}
// Marshal implements the Marshal method of Extension interface.
func (ifi *InterfaceIdent) Marshal(proto int) ([]byte, error) {
b := make([]byte, ifi.Len(proto))
if err := ifi.marshal(proto, b); err != nil {
return nil, err
}
return b, nil
}
func (ifi *InterfaceIdent) marshal(proto int, b []byte) error {
l := ifi.Len(proto)
binary.BigEndian.PutUint16(b[:2], uint16(l))
b[2], b[3] = classInterfaceIdent, byte(ifi.Type)
switch ifi.Type {
case typeInterfaceByName:
copy(b[4:], ifi.Name)
case typeInterfaceByIndex:
binary.BigEndian.PutUint64(b[4:4+8], uint64(ifi.Index))
case typeInterfaceByAddress:
binary.BigEndian.PutUint16(b[4:4+2], uint16(ifi.AFI))
b[4+2] = byte(len(ifi.Addr))
copy(b[4+4:], ifi.Addr)
}
return nil
}
func parseInterfaceIdent(b []byte) (Extension, error) {
ifi := &InterfaceIdent{
Class: int(b[2]),
Type: int(b[3]),
}
switch ifi.Type {
case typeInterfaceByName:
ifi.Name = strings.Trim(string(b[4:]), string(0))
case typeInterfaceByIndex:
if len(b[4:]) < 8 {
return nil, errInvalidExtension
}
ifi.Index = int(binary.BigEndian.Uint64(b[4 : 4+8]))
case typeInterfaceByAddress:
if len(b[4:]) < 4 {
return nil, errInvalidExtension
}
ifi.AFI = int(binary.BigEndian.Uint16(b[4 : 4+2]))
l := int(b[4+2])
if len(b[4+4:]) < l {
return nil, errInvalidExtension
}
ifi.Addr = make([]byte, l)
copy(ifi.Addr, b[4+4:])
}
return ifi, nil
}
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
// ICMP extensions for MPLS are defined in RFC 4950. // ICMP extensions for MPLS are defined in RFC 4950.
// ICMP extensions for interface and next-hop identification are // ICMP extensions for interface and next-hop identification are
// defined in RFC 5837. // defined in RFC 5837.
// PROBE: A utility for probing interfaces is defined in RFC 8335.
package icmp // import "golang.org/x/net/icmp" package icmp // import "golang.org/x/net/icmp"
import ( import (
...@@ -107,21 +108,25 @@ func (m *Message) Marshal(psh []byte) ([]byte, error) { ...@@ -107,21 +108,25 @@ func (m *Message) Marshal(psh []byte) ([]byte, error) {
return b[len(psh):], nil return b[len(psh):], nil
} }
var parseFns = map[Type]func(int, []byte) (MessageBody, error){ var parseFns = map[Type]func(int, Type, []byte) (MessageBody, error){
ipv4.ICMPTypeDestinationUnreachable: parseDstUnreach, ipv4.ICMPTypeDestinationUnreachable: parseDstUnreach,
ipv4.ICMPTypeTimeExceeded: parseTimeExceeded, ipv4.ICMPTypeTimeExceeded: parseTimeExceeded,
ipv4.ICMPTypeParameterProblem: parseParamProb, ipv4.ICMPTypeParameterProblem: parseParamProb,
ipv4.ICMPTypeEcho: parseEcho, ipv4.ICMPTypeEcho: parseEcho,
ipv4.ICMPTypeEchoReply: parseEcho, ipv4.ICMPTypeEchoReply: parseEcho,
ipv4.ICMPTypeExtendedEchoRequest: parseExtendedEchoRequest,
ipv4.ICMPTypeExtendedEchoReply: parseExtendedEchoReply,
ipv6.ICMPTypeDestinationUnreachable: parseDstUnreach, ipv6.ICMPTypeDestinationUnreachable: parseDstUnreach,
ipv6.ICMPTypePacketTooBig: parsePacketTooBig, ipv6.ICMPTypePacketTooBig: parsePacketTooBig,
ipv6.ICMPTypeTimeExceeded: parseTimeExceeded, ipv6.ICMPTypeTimeExceeded: parseTimeExceeded,
ipv6.ICMPTypeParameterProblem: parseParamProb, ipv6.ICMPTypeParameterProblem: parseParamProb,
ipv6.ICMPTypeEchoRequest: parseEcho, ipv6.ICMPTypeEchoRequest: parseEcho,
ipv6.ICMPTypeEchoReply: parseEcho, ipv6.ICMPTypeEchoReply: parseEcho,
ipv6.ICMPTypeExtendedEchoRequest: parseExtendedEchoRequest,
ipv6.ICMPTypeExtendedEchoReply: parseExtendedEchoReply,
} }
// ParseMessage parses b as an ICMP message. // ParseMessage parses b as an ICMP message.
...@@ -143,7 +148,7 @@ func ParseMessage(proto int, b []byte) (*Message, error) { ...@@ -143,7 +148,7 @@ func ParseMessage(proto int, b []byte) (*Message, error) {
if fn, ok := parseFns[m.Type]; !ok { if fn, ok := parseFns[m.Type]; !ok {
m.Body, err = parseDefaultMessageBody(proto, b[4:]) m.Body, err = parseDefaultMessageBody(proto, b[4:])
} else { } else {
m.Body, err = fn(proto, b[4:]) m.Body, err = fn(proto, m.Type, b[4:])
} }
if err != nil { if err != nil {
return nil, err return nil, err
......
...@@ -76,6 +76,18 @@ func TestMarshalAndParseMessage(t *testing.T) { ...@@ -76,6 +76,18 @@ func TestMarshalAndParseMessage(t *testing.T) {
Data: []byte("HELLO-R-U-THERE"), Data: []byte("HELLO-R-U-THERE"),
}, },
}, },
{
Type: ipv4.ICMPTypeExtendedEchoRequest, Code: 0,
Body: &icmp.ExtendedEchoRequest{
ID: 1, Seq: 2,
},
},
{
Type: ipv4.ICMPTypeExtendedEchoReply, Code: 0,
Body: &icmp.ExtendedEchoReply{
State: 4 /* Delay */, Active: true, IPv4: true,
},
},
{ {
Type: ipv4.ICMPTypePhoturis, Type: ipv4.ICMPTypePhoturis,
Body: &icmp.DefaultMessageBody{ Body: &icmp.DefaultMessageBody{
...@@ -120,6 +132,18 @@ func TestMarshalAndParseMessage(t *testing.T) { ...@@ -120,6 +132,18 @@ func TestMarshalAndParseMessage(t *testing.T) {
Data: []byte("HELLO-R-U-THERE"), Data: []byte("HELLO-R-U-THERE"),
}, },
}, },
{
Type: ipv6.ICMPTypeExtendedEchoRequest, Code: 0,
Body: &icmp.ExtendedEchoRequest{
ID: 1, Seq: 2,
},
},
{
Type: ipv6.ICMPTypeExtendedEchoReply, Code: 0,
Body: &icmp.ExtendedEchoReply{
State: 5 /* Probe */, Active: true, IPv6: true,
},
},
{ {
Type: ipv6.ICMPTypeDuplicateAddressConfirmation, Type: ipv6.ICMPTypeDuplicateAddressConfirmation,
Body: &icmp.DefaultMessageBody{ Body: &icmp.DefaultMessageBody{
......
...@@ -10,12 +10,14 @@ import "golang.org/x/net/internal/iana" ...@@ -10,12 +10,14 @@ import "golang.org/x/net/internal/iana"
// exts as extensions, and returns a required length for message body // exts as extensions, and returns a required length for message body
// and a required length for a padded original datagram in wire // and a required length for a padded original datagram in wire
// format. // format.
func multipartMessageBodyDataLen(proto int, b []byte, exts []Extension) (bodyLen, dataLen int) { func multipartMessageBodyDataLen(proto int, withOrigDgram bool, b []byte, exts []Extension) (bodyLen, dataLen int) {
for _, ext := range exts { for _, ext := range exts {
bodyLen += ext.Len(proto) bodyLen += ext.Len(proto)
} }
if bodyLen > 0 { if bodyLen > 0 {
dataLen = multipartMessageOrigDatagramLen(proto, b) if withOrigDgram {
dataLen = multipartMessageOrigDatagramLen(proto, b)
}
bodyLen += 4 // length of extension header bodyLen += 4 // length of extension header
} else { } else {
dataLen = len(b) dataLen = len(b)
...@@ -50,8 +52,8 @@ func multipartMessageOrigDatagramLen(proto int, b []byte) int { ...@@ -50,8 +52,8 @@ func multipartMessageOrigDatagramLen(proto int, b []byte) int {
// marshalMultipartMessageBody takes data as an original datagram and // marshalMultipartMessageBody takes data as an original datagram and
// exts as extesnsions, and returns a binary encoding of message body. // exts as extesnsions, and returns a binary encoding of message body.
// It can be used for non-multipart message bodies when exts is nil. // It can be used for non-multipart message bodies when exts is nil.
func marshalMultipartMessageBody(proto int, data []byte, exts []Extension) ([]byte, error) { func marshalMultipartMessageBody(proto int, withOrigDgram bool, data []byte, exts []Extension) ([]byte, error) {
bodyLen, dataLen := multipartMessageBodyDataLen(proto, data, exts) bodyLen, dataLen := multipartMessageBodyDataLen(proto, withOrigDgram, data, exts)
b := make([]byte, 4+bodyLen) b := make([]byte, 4+bodyLen)
copy(b[4:], data) copy(b[4:], data)
off := dataLen + 4 off := dataLen + 4
...@@ -71,16 +73,23 @@ func marshalMultipartMessageBody(proto int, data []byte, exts []Extension) ([]by ...@@ -71,16 +73,23 @@ func marshalMultipartMessageBody(proto int, data []byte, exts []Extension) ([]by
return nil, err return nil, err
} }
off += ext.Len(proto) off += ext.Len(proto)
case *InterfaceIdent:
if err := ext.marshal(proto, b[off:]); err != nil {
return nil, err
}
off += ext.Len(proto)
} }
} }
s := checksum(b[dataLen+4:]) s := checksum(b[dataLen+4:])
b[dataLen+4+2] ^= byte(s) b[dataLen+4+2] ^= byte(s)
b[dataLen+4+3] ^= byte(s >> 8) b[dataLen+4+3] ^= byte(s >> 8)
switch proto { if withOrigDgram {
case iana.ProtocolICMP: switch proto {
b[1] = byte(dataLen / 4) case iana.ProtocolICMP:
case iana.ProtocolIPv6ICMP: b[1] = byte(dataLen / 4)
b[0] = byte(dataLen / 8) case iana.ProtocolIPv6ICMP:
b[0] = byte(dataLen / 8)
}
} }
} }
return b, nil return b, nil
...@@ -88,7 +97,7 @@ func marshalMultipartMessageBody(proto int, data []byte, exts []Extension) ([]by ...@@ -88,7 +97,7 @@ func marshalMultipartMessageBody(proto int, data []byte, exts []Extension) ([]by
// parseMultipartMessageBody parses b as either a non-multipart // parseMultipartMessageBody parses b as either a non-multipart
// message body or a multipart message body. // message body or a multipart message body.
func parseMultipartMessageBody(proto int, b []byte) ([]byte, []Extension, error) { func parseMultipartMessageBody(proto int, typ Type, b []byte) ([]byte, []Extension, error) {
var l int var l int
switch proto { switch proto {
case iana.ProtocolICMP: case iana.ProtocolICMP:
...@@ -99,11 +108,14 @@ func parseMultipartMessageBody(proto int, b []byte) ([]byte, []Extension, error) ...@@ -99,11 +108,14 @@ func parseMultipartMessageBody(proto int, b []byte) ([]byte, []Extension, error)
if len(b) == 4 { if len(b) == 4 {
return nil, nil, nil return nil, nil, nil
} }
exts, l, err := parseExtensions(b[4:], l) exts, l, err := parseExtensions(typ, b[4:], l)
if err != nil { if err != nil {
l = len(b) - 4 l = len(b) - 4
} }
data := make([]byte, l) var data []byte
copy(data, b[4:]) if l > 0 {
data = make([]byte, l)
copy(data, b[4:])
}
return data, exts, nil return data, exts, nil
} }
...@@ -23,17 +23,21 @@ func TestMarshalAndParseMultipartMessage(t *testing.T) { ...@@ -23,17 +23,21 @@ func TestMarshalAndParseMultipartMessage(t *testing.T) {
if err != nil { if err != nil {
return err return err
} }
switch proto { switch tm.Type {
case iana.ProtocolICMP: case ipv4.ICMPTypeExtendedEchoRequest, ipv6.ICMPTypeExtendedEchoRequest:
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: default:
return fmt.Errorf("unknown protocol: %d", proto) 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) m, err := icmp.ParseMessage(proto, b)
if err != nil { if err != nil {
...@@ -43,6 +47,11 @@ func TestMarshalAndParseMultipartMessage(t *testing.T) { ...@@ -43,6 +47,11 @@ func TestMarshalAndParseMultipartMessage(t *testing.T) {
return fmt.Errorf("got %v; want %v", m, &tm) return fmt.Errorf("got %v; want %v", m, &tm)
} }
switch m.Type { 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: case ipv4.ICMPTypeDestinationUnreachable:
got, want := m.Body.(*icmp.DstUnreach), tm.Body.(*icmp.DstUnreach) got, want := m.Body.(*icmp.DstUnreach), tm.Body.(*icmp.DstUnreach)
if !reflect.DeepEqual(got.Extensions, want.Extensions) { if !reflect.DeepEqual(got.Extensions, want.Extensions) {
...@@ -200,6 +209,51 @@ func TestMarshalAndParseMultipartMessage(t *testing.T) { ...@@ -200,6 +209,51 @@ func TestMarshalAndParseMultipartMessage(t *testing.T) {
}, },
}, },
}, },
{
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,
},
&icmp.InterfaceIdent{
Class: 3,
Type: 1,
Name: "en101",
},
},
},
},
{
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 { if err := fn(t, iana.ProtocolICMP, tm); err != nil {
t.Errorf("#%d: %v", i, err) t.Errorf("#%d: %v", i, err)
...@@ -287,6 +341,51 @@ func TestMarshalAndParseMultipartMessage(t *testing.T) { ...@@ -287,6 +341,51 @@ func TestMarshalAndParseMultipartMessage(t *testing.T) {
}, },
}, },
}, },
{
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: 1,
Name: "en101",
},
&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 { if err := fn(t, iana.ProtocolIPv6ICMP, tm); err != nil {
t.Errorf("#%d: %v", i, err) t.Errorf("#%d: %v", i, err)
...@@ -309,6 +408,11 @@ func dumpExtensions(gotExts, wantExts []icmp.Extension) string { ...@@ -309,6 +408,11 @@ func dumpExtensions(gotExts, wantExts []icmp.Extension) string {
if !reflect.DeepEqual(got, want) { 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) 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)
}
} }
} }
if len(s) == 0 { if len(s) == 0 {
...@@ -435,6 +539,34 @@ func TestMultipartMessageBodyLen(t *testing.T) { ...@@ -435,6 +539,34 @@ func TestMultipartMessageBodyLen(t *testing.T) {
}, },
4 + 4 + 4 + 0 + 136, // [length, unused], extension header, object header, object payload and original datagram 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 { if out := tt.in.Len(tt.proto); out != tt.out {
t.Errorf("#%d: got %d; want %d", i, out, tt.out) t.Errorf("#%d: got %d; want %d", i, out, tt.out)
......
...@@ -29,7 +29,7 @@ func (p *PacketTooBig) Marshal(proto int) ([]byte, error) { ...@@ -29,7 +29,7 @@ func (p *PacketTooBig) Marshal(proto int) ([]byte, error) {
} }
// parsePacketTooBig parses b as an ICMP packet too big message body. // parsePacketTooBig parses b as an ICMP packet too big message body.
func parsePacketTooBig(proto int, b []byte) (MessageBody, error) { func parsePacketTooBig(proto int, _ Type, b []byte) (MessageBody, error) {
bodyLen := len(b) bodyLen := len(b)
if bodyLen < 4 { if bodyLen < 4 {
return nil, errMessageTooShort return nil, errMessageTooShort
......
...@@ -21,7 +21,7 @@ func (p *ParamProb) Len(proto int) int { ...@@ -21,7 +21,7 @@ func (p *ParamProb) Len(proto int) int {
if p == nil { if p == nil {
return 0 return 0
} }
l, _ := multipartMessageBodyDataLen(proto, p.Data, p.Extensions) l, _ := multipartMessageBodyDataLen(proto, true, p.Data, p.Extensions)
return 4 + l return 4 + l
} }
...@@ -33,7 +33,7 @@ func (p *ParamProb) Marshal(proto int) ([]byte, error) { ...@@ -33,7 +33,7 @@ func (p *ParamProb) Marshal(proto int) ([]byte, error) {
copy(b[4:], p.Data) copy(b[4:], p.Data)
return b, nil return b, nil
} }
b, err := marshalMultipartMessageBody(proto, p.Data, p.Extensions) b, err := marshalMultipartMessageBody(proto, true, p.Data, p.Extensions)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -42,7 +42,7 @@ func (p *ParamProb) Marshal(proto int) ([]byte, error) { ...@@ -42,7 +42,7 @@ func (p *ParamProb) Marshal(proto int) ([]byte, error) {
} }
// parseParamProb parses b as an ICMP parameter problem message body. // parseParamProb parses b as an ICMP parameter problem message body.
func parseParamProb(proto int, b []byte) (MessageBody, error) { func parseParamProb(proto int, typ Type, b []byte) (MessageBody, error) {
if len(b) < 4 { if len(b) < 4 {
return nil, errMessageTooShort return nil, errMessageTooShort
} }
...@@ -55,7 +55,7 @@ func parseParamProb(proto int, b []byte) (MessageBody, error) { ...@@ -55,7 +55,7 @@ func parseParamProb(proto int, b []byte) (MessageBody, error) {
} }
p.Pointer = uintptr(b[0]) p.Pointer = uintptr(b[0])
var err error var err error
p.Data, p.Extensions, err = parseMultipartMessageBody(proto, b) p.Data, p.Extensions, err = parseMultipartMessageBody(proto, typ, b)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
...@@ -15,23 +15,23 @@ func (p *TimeExceeded) Len(proto int) int { ...@@ -15,23 +15,23 @@ func (p *TimeExceeded) Len(proto int) int {
if p == nil { if p == nil {
return 0 return 0
} }
l, _ := multipartMessageBodyDataLen(proto, p.Data, p.Extensions) l, _ := multipartMessageBodyDataLen(proto, true, p.Data, p.Extensions)
return 4 + l return 4 + l
} }
// Marshal implements the Marshal method of MessageBody interface. // Marshal implements the Marshal method of MessageBody interface.
func (p *TimeExceeded) Marshal(proto int) ([]byte, error) { func (p *TimeExceeded) Marshal(proto int) ([]byte, error) {
return marshalMultipartMessageBody(proto, p.Data, p.Extensions) return marshalMultipartMessageBody(proto, true, p.Data, p.Extensions)
} }
// parseTimeExceeded parses b as an ICMP time exceeded message body. // parseTimeExceeded parses b as an ICMP time exceeded message body.
func parseTimeExceeded(proto int, b []byte) (MessageBody, error) { func parseTimeExceeded(proto int, typ Type, b []byte) (MessageBody, error) {
if len(b) < 4 { if len(b) < 4 {
return nil, errMessageTooShort return nil, errMessageTooShort
} }
p := &TimeExceeded{} p := &TimeExceeded{}
var err error var err error
p.Data, p.Extensions, err = parseMultipartMessageBody(proto, b) p.Data, p.Extensions, err = parseMultipartMessageBody(proto, typ, b)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment