// 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" "golang.org/x/net/ipv4" "golang.org/x/net/ipv6" ) // An Extension represents an ICMP extension. type Extension interface { // Len returns the length of ICMP extension. // Proto must be either the ICMPv4 or ICMPv6 protocol number. Len(proto int) int // Marshal returns the binary encoding of ICMP extension. // Proto must be either the ICMPv4 or ICMPv6 protocol number. Marshal(proto int) ([]byte, error) } const extensionVersion = 2 func validExtensionHeader(b []byte) bool { v := int(b[0]&0xf0) >> 4 s := binary.BigEndian.Uint16(b[2:4]) if s != 0 { s = checksum(b) } if v != extensionVersion || s != 0 { return false } return true } // parseExtensions parses b as a list of ICMP extensions. // The length attribute l must be the length attribute field in // received icmp messages. // // It will return a list of ICMP extensions and an adjusted length // attribute that represents the length of the padded original // datagram field. Otherwise, it returns an error. func parseExtensions(typ Type, b []byte, l int) ([]Extension, int, error) { // Still a lot of non-RFC 4884 compliant implementations are // out there. Set the length attribute l to 128 when it looks // inappropriate for backwards compatibility. // // A minimal extension at least requires 8 octets; 4 octets // for an extension header, and 4 octets for a single object // header. // // See RFC 4884 for further information. switch typ { case ipv4.ICMPTypeExtendedEchoRequest, ipv6.ICMPTypeExtendedEchoRequest: if len(b) < 8 || !validExtensionHeader(b) { return nil, -1, errNoExtension } l = 0 default: if 128 > l || l+8 > len(b) { l = 128 } if l+8 > len(b) { 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 for b = b[l+4:]; len(b) >= 4; { ol := int(binary.BigEndian.Uint16(b[:2])) if 4 > ol || ol > len(b) { break } switch b[2] { case classMPLSLabelStack: ext, err := parseMPLSLabelStack(b[:ol]) if err != nil { return nil, -1, err } exts = append(exts, ext) case classInterfaceInfo: ext, err := parseInterfaceInfo(b[:ol]) if err != nil { return nil, -1, err } 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:] } return exts, l, nil }