| // 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. |
| // The provided proto must be either the ICMPv4 or ICMPv6 |
| // protocol number. |
| Len(proto int) int |
| |
| // Marshal returns the binary encoding of ICMP extension. |
| // The provided 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) |
| default: |
| ext := &RawExtension{Data: make([]byte, ol)} |
| copy(ext.Data, b[:ol]) |
| exts = append(exts, ext) |
| } |
| b = b[ol:] |
| } |
| return exts, l, nil |
| } |
| |
| func validExtensions(typ Type, exts []Extension) bool { |
| switch typ { |
| case ipv4.ICMPTypeDestinationUnreachable, ipv4.ICMPTypeTimeExceeded, ipv4.ICMPTypeParameterProblem, |
| ipv6.ICMPTypeDestinationUnreachable, ipv6.ICMPTypeTimeExceeded: |
| for i := range exts { |
| switch exts[i].(type) { |
| case *MPLSLabelStack, *InterfaceInfo, *RawExtension: |
| default: |
| return false |
| } |
| } |
| return true |
| case ipv4.ICMPTypeExtendedEchoRequest, ipv6.ICMPTypeExtendedEchoRequest: |
| var n int |
| for i := range exts { |
| switch exts[i].(type) { |
| case *InterfaceIdent: |
| n++ |
| case *RawExtension: |
| default: |
| return false |
| } |
| } |
| // Not a single InterfaceIdent object or a combo of |
| // RawExtension and InterfaceIdent objects is not |
| // allowed. |
| if n == 1 && len(exts) > 1 { |
| return false |
| } |
| return true |
| default: |
| return false |
| } |
| } |
| |
| // A RawExtension represents a raw extension. |
| // |
| // A raw extension is excluded from message processing and can be used |
| // to construct applications such as protocol conformance testing. |
| type RawExtension struct { |
| Data []byte // data |
| } |
| |
| // Len implements the Len method of Extension interface. |
| func (p *RawExtension) Len(proto int) int { |
| if p == nil { |
| return 0 |
| } |
| return len(p.Data) |
| } |
| |
| // Marshal implements the Marshal method of Extension interface. |
| func (p *RawExtension) Marshal(proto int) ([]byte, error) { |
| return p.Data, nil |
| } |