github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/net/icmp/message.go (about)

     1  // Copyright 2012 The Go Authors.  All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package icmp provides basic functions for the manipulation of
     6  // messages used in the Internet Control Message Protocols,
     7  // ICMPv4 and ICMPv6.
     8  //
     9  // ICMPv4 and ICMPv6 are defined in RFC 792 and RFC 4443.
    10  // Multi-part message support for ICMP is defined in RFC 4884.
    11  // ICMP extensions for MPLS are defined in RFC 4950.
    12  // ICMP extensions for interface and next-hop identification are
    13  // defined in RFC 5837.
    14  package icmp // import "golang.org/x/net/icmp"
    15  
    16  import (
    17  	"errors"
    18  	"net"
    19  	"syscall"
    20  
    21  	"golang.org/x/net/internal/iana"
    22  	"golang.org/x/net/ipv4"
    23  	"golang.org/x/net/ipv6"
    24  )
    25  
    26  var (
    27  	errMessageTooShort  = errors.New("message too short")
    28  	errHeaderTooShort   = errors.New("header too short")
    29  	errBufferTooShort   = errors.New("buffer too short")
    30  	errOpNoSupport      = errors.New("operation not supported")
    31  	errNoExtension      = errors.New("no extension")
    32  	errInvalidExtension = errors.New("invalid extension")
    33  )
    34  
    35  func checksum(b []byte) uint16 {
    36  	csumcv := len(b) - 1 // checksum coverage
    37  	s := uint32(0)
    38  	for i := 0; i < csumcv; i += 2 {
    39  		s += uint32(b[i+1])<<8 | uint32(b[i])
    40  	}
    41  	if csumcv&1 == 0 {
    42  		s += uint32(b[csumcv])
    43  	}
    44  	s = s>>16 + s&0xffff
    45  	s = s + s>>16
    46  	return ^uint16(s)
    47  }
    48  
    49  // A Type represents an ICMP message type.
    50  type Type interface {
    51  	Protocol() int
    52  }
    53  
    54  // A Message represents an ICMP message.
    55  type Message struct {
    56  	Type     Type        // type, either ipv4.ICMPType or ipv6.ICMPType
    57  	Code     int         // code
    58  	Checksum int         // checksum
    59  	Body     MessageBody // body
    60  }
    61  
    62  // Marshal returns the binary enconding of the ICMP message m.
    63  //
    64  // For an ICMPv4 message, the returned message always contains the
    65  // calculated checksum field.
    66  //
    67  // For an ICMPv6 message, the returned message contains the calculated
    68  // checksum field when psh is not nil, otherwise the kernel will
    69  // compute the checksum field during the message transmission.
    70  // When psh is not nil, it must be the pseudo header for IPv6.
    71  func (m *Message) Marshal(psh []byte) ([]byte, error) {
    72  	var mtype int
    73  	switch typ := m.Type.(type) {
    74  	case ipv4.ICMPType:
    75  		mtype = int(typ)
    76  	case ipv6.ICMPType:
    77  		mtype = int(typ)
    78  	default:
    79  		return nil, syscall.EINVAL
    80  	}
    81  	b := []byte{byte(mtype), byte(m.Code), 0, 0}
    82  	if m.Type.Protocol() == iana.ProtocolIPv6ICMP && psh != nil {
    83  		b = append(psh, b...)
    84  	}
    85  	if m.Body != nil && m.Body.Len(m.Type.Protocol()) != 0 {
    86  		mb, err := m.Body.Marshal(m.Type.Protocol())
    87  		if err != nil {
    88  			return nil, err
    89  		}
    90  		b = append(b, mb...)
    91  	}
    92  	if m.Type.Protocol() == iana.ProtocolIPv6ICMP {
    93  		if psh == nil { // cannot calculate checksum here
    94  			return b, nil
    95  		}
    96  		off, l := 2*net.IPv6len, len(b)-len(psh)
    97  		b[off], b[off+1], b[off+2], b[off+3] = byte(l>>24), byte(l>>16), byte(l>>8), byte(l)
    98  	}
    99  	s := checksum(b)
   100  	// Place checksum back in header; using ^= avoids the
   101  	// assumption the checksum bytes are zero.
   102  	b[len(psh)+2] ^= byte(s)
   103  	b[len(psh)+3] ^= byte(s >> 8)
   104  	return b[len(psh):], nil
   105  }
   106  
   107  var parseFns = map[Type]func(int, []byte) (MessageBody, error){
   108  	ipv4.ICMPTypeDestinationUnreachable: parseDstUnreach,
   109  	ipv4.ICMPTypeTimeExceeded:           parseTimeExceeded,
   110  	ipv4.ICMPTypeParameterProblem:       parseParamProb,
   111  
   112  	ipv4.ICMPTypeEcho:      parseEcho,
   113  	ipv4.ICMPTypeEchoReply: parseEcho,
   114  
   115  	ipv6.ICMPTypeDestinationUnreachable: parseDstUnreach,
   116  	ipv6.ICMPTypePacketTooBig:           parsePacketTooBig,
   117  	ipv6.ICMPTypeTimeExceeded:           parseTimeExceeded,
   118  	ipv6.ICMPTypeParameterProblem:       parseParamProb,
   119  
   120  	ipv6.ICMPTypeEchoRequest: parseEcho,
   121  	ipv6.ICMPTypeEchoReply:   parseEcho,
   122  }
   123  
   124  // ParseMessage parses b as an ICMP message.
   125  // Proto must be either the ICMPv4 or ICMPv6 protocol number.
   126  func ParseMessage(proto int, b []byte) (*Message, error) {
   127  	if len(b) < 4 {
   128  		return nil, errMessageTooShort
   129  	}
   130  	var err error
   131  	m := &Message{Code: int(b[1]), Checksum: int(b[2])<<8 | int(b[3])}
   132  	switch proto {
   133  	case iana.ProtocolICMP:
   134  		m.Type = ipv4.ICMPType(b[0])
   135  	case iana.ProtocolIPv6ICMP:
   136  		m.Type = ipv6.ICMPType(b[0])
   137  	default:
   138  		return nil, syscall.EINVAL
   139  	}
   140  	if fn, ok := parseFns[m.Type]; !ok {
   141  		m.Body, err = parseDefaultMessageBody(proto, b[4:])
   142  	} else {
   143  		m.Body, err = fn(proto, b[4:])
   144  	}
   145  	if err != nil {
   146  		return nil, err
   147  	}
   148  	return m, nil
   149  }