| // 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 |
| |
| 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 { |
| 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(afiIPv4)) |
| copy(b[4:4+net.IPv4len], ifi.Addr.IP.To4()) |
| b = b[4+net.IPv4len:] |
| case iana.ProtocolIPv6ICMP: |
| binary.BigEndian.PutUint16(b[:2], uint16(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(binary.BigEndian.Uint16(b[:2])) |
| 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]) |
| 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 |
| } |