Commit c84eff70 authored by Mikio Hara's avatar Mikio Hara

icmp: add extensions for interface and next-hop identification

This change implements ICMP extensions for interface and next-hop
identification which are used for route trace applications as described
in RFC 5837.

Change-Id: I2435109b5e766e743b894b0280a537324975489d
Reviewed-on: https://go-review.googlesource.com/3112Reviewed-by: 's avatarIan Lance Taylor <iant@golang.org>
parent d534621e
...@@ -74,6 +74,12 @@ func parseExtensions(b []byte, l int) ([]Extension, int, error) { ...@@ -74,6 +74,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 classInterfaceInfo:
ext, err := parseInterfaceInfo(b[:ol])
if err != nil {
return nil, -1, err
}
exts = append(exts, ext)
} }
b = b[ol:] b = b[ol:]
} }
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package icmp package icmp
import ( import (
"net"
"reflect" "reflect"
"testing" "testing"
...@@ -90,6 +91,78 @@ var marshalAndParseExtensionTests = []struct { ...@@ -90,6 +91,78 @@ var marshalAndParseExtensionTests = []struct {
}, },
}, },
}, },
// Interface information with no attribute
{
proto: iana.ProtocolICMP,
hdr: []byte{
0x20, 0x00, 0x00, 0x00,
},
obj: []byte{
0x00, 0x04, 0x02, 0x00,
},
exts: []Extension{
&InterfaceInfo{
Class: classInterfaceInfo,
},
},
},
// Interface information with ifIndex and name
{
proto: iana.ProtocolICMP,
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,
},
exts: []Extension{
&InterfaceInfo{
Class: classInterfaceInfo,
Type: 0x0a,
Interface: &net.Interface{
Index: 16,
Name: "en101",
},
},
},
},
// Interface information with ifIndex, IPAddr, name and MTU
{
proto: iana.ProtocolIPv6ICMP,
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,
},
exts: []Extension{
&InterfaceInfo{
Class: classInterfaceInfo,
Type: 0x0f,
Interface: &net.Interface{
Index: 15,
Name: "en101",
MTU: 8192,
},
Addr: &net.IPAddr{
IP: net.ParseIP("fe80::1"),
Zone: "en101",
},
},
},
},
} }
func TestMarshalAndParseExtension(t *testing.T) { func TestMarshalAndParseExtension(t *testing.T) {
...@@ -104,6 +177,12 @@ func TestMarshalAndParseExtension(t *testing.T) { ...@@ -104,6 +177,12 @@ func TestMarshalAndParseExtension(t *testing.T) {
t.Errorf("#%v/%v: %v", i, j, err) t.Errorf("#%v/%v: %v", i, j, err)
continue continue
} }
case *InterfaceInfo:
b, err = ext.Marshal(tt.proto)
if err != nil {
t.Errorf("#%v/%v: %v", i, j, err)
continue
}
} }
if !reflect.DeepEqual(b, tt.obj) { if !reflect.DeepEqual(b, tt.obj) {
t.Errorf("#%v/%v: got %#v; want %#v", i, j, b, tt.obj) t.Errorf("#%v/%v: got %#v; want %#v", i, j, b, tt.obj)
...@@ -149,6 +228,9 @@ func TestMarshalAndParseExtension(t *testing.T) { ...@@ -149,6 +228,9 @@ func TestMarshalAndParseExtension(t *testing.T) {
case *MPLSLabelStack: case *MPLSLabelStack:
want := tt.exts[j].(*MPLSLabelStack) want := tt.exts[j].(*MPLSLabelStack)
t.Errorf("#%v/%v: got %#v; want %#v", i, j, ext, want) t.Errorf("#%v/%v: got %#v; want %#v", i, j, ext, want)
case *InterfaceInfo:
want := tt.exts[j].(*InterfaceInfo)
t.Errorf("#%v/%v: got %#v; want %#v", i, j, ext, want)
} }
} }
continue continue
......
// 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 (
"net"
"strings"
"golang.org/x/net/internal/iana"
)
const (
classInterfaceInfo = 2
afiIPv4 = 1
afiIPv6 = 2
)
const (
attrMTU = 1 << iota
attrName
attrIPAddr
attrIfIndex
)
// An InterfaceInfo represents interface and next-hop identification.
type InterfaceInfo struct {
Class int // extension object class number
Type int // extension object sub-type
Interface *net.Interface
Addr *net.IPAddr
}
func (ifi *InterfaceInfo) nameLen() int {
if len(ifi.Interface.Name) > 63 {
return 64
}
l := 1 + len(ifi.Interface.Name)
return (l + 3) &^ 3
}
func (ifi *InterfaceInfo) attrsAndLen(proto int) (attrs, l int) {
l = 4
if ifi.Interface != nil && ifi.Interface.Index > 0 {
attrs |= attrIfIndex
l += 4
if len(ifi.Interface.Name) > 0 {
attrs |= attrName
l += ifi.nameLen()
}
if ifi.Interface.MTU > 0 {
attrs |= attrMTU
l += 4
}
}
if ifi.Addr != nil {
switch proto {
case iana.ProtocolICMP:
if ifi.Addr.IP.To4() != nil {
attrs |= attrIPAddr
l += 4 + net.IPv4len
}
case iana.ProtocolIPv6ICMP:
if ifi.Addr.IP.To16() != nil && ifi.Addr.IP.To4() == nil {
attrs |= attrIPAddr
l += 4 + net.IPv6len
}
}
}
return
}
// Len implements the Len method of Extension interface.
func (ifi *InterfaceInfo) Len(proto int) int {
_, l := ifi.attrsAndLen(proto)
return l
}
// Marshal implements the Marshal method of Extension interface.
func (ifi *InterfaceInfo) Marshal(proto int) ([]byte, error) {
attrs, l := ifi.attrsAndLen(proto)
b := make([]byte, l)
if err := ifi.marshal(proto, b, attrs, l); err != nil {
return nil, err
}
return b, nil
}
func (ifi *InterfaceInfo) marshal(proto int, b []byte, attrs, l int) error {
b[0], b[1] = byte(l>>8), byte(l)
b[2], b[3] = classInterfaceInfo, byte(ifi.Type)
for b = b[4:]; len(b) > 0 && attrs != 0; {
switch {
case attrs&attrIfIndex != 0:
b = ifi.marshalIfIndex(proto, b)
attrs &^= attrIfIndex
case attrs&attrIPAddr != 0:
b = ifi.marshalIPAddr(proto, b)
attrs &^= attrIPAddr
case attrs&attrName != 0:
b = ifi.marshalName(proto, b)
attrs &^= attrName
case attrs&attrMTU != 0:
b = ifi.marshalMTU(proto, b)
attrs &^= attrMTU
}
}
return nil
}
func (ifi *InterfaceInfo) marshalIfIndex(proto int, b []byte) []byte {
b[0], b[1], b[2], b[3] = byte(ifi.Interface.Index>>24), byte(ifi.Interface.Index>>16), byte(ifi.Interface.Index>>8), byte(ifi.Interface.Index)
return b[4:]
}
func (ifi *InterfaceInfo) parseIfIndex(b []byte) ([]byte, error) {
if len(b) < 4 {
return nil, errMessageTooShort
}
ifi.Interface.Index = int(b[0])<<24 | int(b[1])<<16 | int(b[2])<<8 | int(b[3])
return b[4:], nil
}
func (ifi *InterfaceInfo) marshalIPAddr(proto int, b []byte) []byte {
switch proto {
case iana.ProtocolICMP:
b[0], b[1] = byte(afiIPv4>>8), byte(afiIPv4)
copy(b[4:4+net.IPv4len], ifi.Addr.IP.To4())
b = b[4+net.IPv4len:]
case iana.ProtocolIPv6ICMP:
b[0], b[1] = byte(afiIPv6>>8), byte(afiIPv6)
copy(b[4:4+net.IPv6len], ifi.Addr.IP.To16())
b = b[4+net.IPv6len:]
}
return b
}
func (ifi *InterfaceInfo) parseIPAddr(b []byte) ([]byte, error) {
if len(b) < 4 {
return nil, errMessageTooShort
}
afi := int(b[0])<<8 | int(b[1])
b = b[4:]
switch afi {
case afiIPv4:
if len(b) < net.IPv4len {
return nil, errMessageTooShort
}
ifi.Addr.IP = make(net.IP, net.IPv4len)
copy(ifi.Addr.IP, b[:net.IPv4len])
b = b[net.IPv4len:]
case afiIPv6:
if len(b) < net.IPv6len {
return nil, errMessageTooShort
}
ifi.Addr.IP = make(net.IP, net.IPv6len)
copy(ifi.Addr.IP, b[:net.IPv6len])
b = b[net.IPv6len:]
}
return b, nil
}
func (ifi *InterfaceInfo) marshalName(proto int, b []byte) []byte {
l := byte(ifi.nameLen())
b[0] = l
copy(b[1:], []byte(ifi.Interface.Name))
return b[l:]
}
func (ifi *InterfaceInfo) parseName(b []byte) ([]byte, error) {
if 4 > len(b) || len(b) < int(b[0]) {
return nil, errMessageTooShort
}
l := int(b[0])
var name [63]byte
copy(name[:], b[1:l])
ifi.Interface.Name = strings.Trim(string(name[:]), "\000")
return b[l:], nil
}
func (ifi *InterfaceInfo) marshalMTU(proto int, b []byte) []byte {
b[0], b[1], b[2], b[3] = byte(ifi.Interface.MTU>>24), byte(ifi.Interface.MTU>>16), byte(ifi.Interface.MTU>>8), byte(ifi.Interface.MTU)
return b[4:]
}
func (ifi *InterfaceInfo) parseMTU(b []byte) ([]byte, error) {
if len(b) < 4 {
return nil, errMessageTooShort
}
ifi.Interface.MTU = int(b[0])<<24 | int(b[1])<<16 | int(b[2])<<8 | int(b[3])
return b[4:], nil
}
func parseInterfaceInfo(b []byte) (Extension, error) {
ifi := &InterfaceInfo{
Class: int(b[2]),
Type: int(b[3]),
}
if ifi.Type&(attrIfIndex|attrName|attrMTU) != 0 {
ifi.Interface = &net.Interface{}
}
if ifi.Type&attrIPAddr != 0 {
ifi.Addr = &net.IPAddr{}
}
attrs := ifi.Type & (attrIfIndex | attrIPAddr | attrName | attrMTU)
for b = b[4:]; len(b) > 0 && attrs != 0; {
var err error
switch {
case attrs&attrIfIndex != 0:
b, err = ifi.parseIfIndex(b)
attrs &^= attrIfIndex
case attrs&attrIPAddr != 0:
b, err = ifi.parseIPAddr(b)
attrs &^= attrIPAddr
case attrs&attrName != 0:
b, err = ifi.parseName(b)
attrs &^= attrName
case attrs&attrMTU != 0:
b, err = ifi.parseMTU(b)
attrs &^= attrMTU
}
if err != nil {
return nil, err
}
}
if ifi.Interface != nil && ifi.Interface.Name != "" && ifi.Addr != nil && ifi.Addr.IP.To16() != nil && ifi.Addr.IP.To4() == nil {
ifi.Addr.Zone = ifi.Interface.Name
}
return ifi, nil
}
...@@ -9,6 +9,8 @@ ...@@ -9,6 +9,8 @@
// ICMPv4 and ICMPv6 are defined in RFC 792 and RFC 4443. // ICMPv4 and ICMPv6 are defined in RFC 792 and RFC 4443.
// Multi-part message support for ICMP is defined in RFC 4884. // Multi-part message support for ICMP is defined in RFC 4884.
// 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
// defined in RFC 5837.
package icmp // import "golang.org/x/net/icmp" package icmp // import "golang.org/x/net/icmp"
import ( import (
......
...@@ -65,6 +65,12 @@ func marshalMultipartMessageBody(proto int, data []byte, exts []Extension) ([]by ...@@ -65,6 +65,12 @@ func marshalMultipartMessageBody(proto int, data []byte, exts []Extension) ([]by
return nil, err return nil, err
} }
off += ext.Len(proto) off += ext.Len(proto)
case *InterfaceInfo:
attrs, l := ext.attrsAndLen(proto)
if err := ext.marshal(proto, b[off:], attrs, l); err != nil {
return nil, err
}
off += ext.Len(proto)
} }
} }
s := checksum(b[dataLen+4:]) s := checksum(b[dataLen+4:])
......
...@@ -34,6 +34,18 @@ var marshalAndParseMultipartMessageForIPv4Tests = []icmp.Message{ ...@@ -34,6 +34,18 @@ var marshalAndParseMultipartMessageForIPv4Tests = []icmp.Message{
}, },
}, },
}, },
&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(),
},
},
}, },
}, },
}, },
...@@ -42,6 +54,18 @@ var marshalAndParseMultipartMessageForIPv4Tests = []icmp.Message{ ...@@ -42,6 +54,18 @@ var marshalAndParseMultipartMessageForIPv4Tests = []icmp.Message{
Body: &icmp.TimeExceeded{ Body: &icmp.TimeExceeded{
Data: []byte("ERROR-INVOKING-PACKET"), Data: []byte("ERROR-INVOKING-PACKET"),
Extensions: []icmp.Extension{ 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{ &icmp.MPLSLabelStack{
Class: 1, Class: 1,
Type: 1, Type: 1,
...@@ -75,6 +99,30 @@ var marshalAndParseMultipartMessageForIPv4Tests = []icmp.Message{ ...@@ -75,6 +99,30 @@ var marshalAndParseMultipartMessageForIPv4Tests = []icmp.Message{
}, },
}, },
}, },
&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(),
},
},
}, },
}, },
}, },
...@@ -100,7 +148,7 @@ func TestMarshalAndParseMultipartMessageForIPv4(t *testing.T) { ...@@ -100,7 +148,7 @@ func TestMarshalAndParseMultipartMessageForIPv4(t *testing.T) {
case ipv4.ICMPTypeDestinationUnreachable: case ipv4.ICMPTypeDestinationUnreachable:
got, want := m.Body.(*icmp.DstUnreach), tt.Body.(*icmp.DstUnreach) got, want := m.Body.(*icmp.DstUnreach), tt.Body.(*icmp.DstUnreach)
if !reflect.DeepEqual(got.Extensions, want.Extensions) { if !reflect.DeepEqual(got.Extensions, want.Extensions) {
t.Errorf("#%v: got %#v; want %#v", i, got.Extensions, want.Extensions) t.Error(dumpExtensions(i, got.Extensions, want.Extensions))
} }
if len(got.Data) != 128 { if len(got.Data) != 128 {
t.Errorf("#%v: got %v; want 128", i, len(got.Data)) t.Errorf("#%v: got %v; want 128", i, len(got.Data))
...@@ -143,6 +191,19 @@ var marshalAndParseMultipartMessageForIPv6Tests = []icmp.Message{ ...@@ -143,6 +191,19 @@ var marshalAndParseMultipartMessageForIPv6Tests = []icmp.Message{
}, },
}, },
}, },
&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",
},
},
}, },
}, },
}, },
...@@ -151,6 +212,19 @@ var marshalAndParseMultipartMessageForIPv6Tests = []icmp.Message{ ...@@ -151,6 +212,19 @@ var marshalAndParseMultipartMessageForIPv6Tests = []icmp.Message{
Body: &icmp.TimeExceeded{ Body: &icmp.TimeExceeded{
Data: []byte("ERROR-INVOKING-PACKET"), Data: []byte("ERROR-INVOKING-PACKET"),
Extensions: []icmp.Extension{ 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{ &icmp.MPLSLabelStack{
Class: 1, Class: 1,
Type: 1, Type: 1,
...@@ -163,6 +237,19 @@ var marshalAndParseMultipartMessageForIPv6Tests = []icmp.Message{ ...@@ -163,6 +237,19 @@ var marshalAndParseMultipartMessageForIPv6Tests = []icmp.Message{
}, },
}, },
}, },
&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",
},
},
}, },
}, },
}, },
...@@ -217,6 +304,11 @@ func dumpExtensions(i int, gotExts, wantExts []icmp.Extension) string { ...@@ -217,6 +304,11 @@ func dumpExtensions(i int, gotExts, wantExts []icmp.Extension) string {
if !reflect.DeepEqual(got, want) { if !reflect.DeepEqual(got, want) {
s += fmt.Sprintf("#%v/%v: got %#v; want %#v\n", i, j, got, want) s += fmt.Sprintf("#%v/%v: got %#v; want %#v\n", i, j, got, want)
} }
case *icmp.InterfaceInfo:
want := wantExts[j].(*icmp.InterfaceInfo)
if !reflect.DeepEqual(got, want) {
s += fmt.Sprintf("#%v/%v: got %#v, %#v, %#v; want %#v, %#v, %#v\n", i, j, got, got.Interface, got.Addr, want, want.Interface, want.Addr)
}
} }
} }
return s[:len(s)-1] return s[:len(s)-1]
......
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