blob: b3dd72fb0acccb04da126a5798ff875898556ff7 [file] [log] [blame] [edit]
// 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 (
"encoding/binary"
"net"
"strings"
"golang.org/x/net/internal/iana"
)
const (
classInterfaceInfo = 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 {
binary.BigEndian.PutUint16(b[:2], uint16(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 {
binary.BigEndian.PutUint32(b[:4], uint32(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(binary.BigEndian.Uint32(b[:4]))
return b[4:], nil
}
func (ifi *InterfaceInfo) marshalIPAddr(proto int, b []byte) []byte {
switch proto {
case iana.ProtocolICMP:
binary.BigEndian.PutUint16(b[:2], uint16(iana.AddrFamilyIPv4))
copy(b[4:4+net.IPv4len], ifi.Addr.IP.To4())
b = b[4+net.IPv4len:]
case iana.ProtocolIPv6ICMP:
binary.BigEndian.PutUint16(b[:2], uint16(iana.AddrFamilyIPv6))
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(binary.BigEndian.Uint16(b[:2]))
b = b[4:]
switch afi {
case iana.AddrFamilyIPv4:
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 iana.AddrFamilyIPv6:
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])
if l%4 != 0 || 4 > l || l > 64 {
return nil, errInvalidExtension
}
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 {
binary.BigEndian.PutUint32(b[:4], uint32(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(binary.BigEndian.Uint32(b[:4]))
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
}
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 + 4
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.PutUint32(b[4:4+4], uint32(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:]), "\x00")
case typeInterfaceByIndex:
if len(b[4:]) < 4 {
return nil, errInvalidExtension
}
ifi.Index = int(binary.BigEndian.Uint32(b[4 : 4+4]))
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
}