github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/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  // PROBE: A utility for probing interfaces is defined in RFC 8335.
    15  package icmp // import "github.com/hxx258456/ccgo/net/icmp"
    16  
    17  import (
    18  	"encoding/binary"
    19  	"errors"
    20  	"net"
    21  	"runtime"
    22  
    23  	"github.com/hxx258456/ccgo/net/internal/iana"
    24  	"github.com/hxx258456/ccgo/net/ipv4"
    25  	"github.com/hxx258456/ccgo/net/ipv6"
    26  )
    27  
    28  // BUG(mikio): This package is not implemented on JS, NaCl and Plan 9.
    29  
    30  var (
    31  	errInvalidConn      = errors.New("invalid connection")
    32  	errInvalidProtocol  = errors.New("invalid protocol")
    33  	errMessageTooShort  = errors.New("message too short")
    34  	errHeaderTooShort   = errors.New("header too short")
    35  	errBufferTooShort   = errors.New("buffer too short")
    36  	errInvalidBody      = errors.New("invalid body")
    37  	errNoExtension      = errors.New("no extension")
    38  	errInvalidExtension = errors.New("invalid extension")
    39  	errNotImplemented   = errors.New("not implemented on " + runtime.GOOS + "/" + runtime.GOARCH)
    40  )
    41  
    42  func checksum(b []byte) uint16 {
    43  	csumcv := len(b) - 1 // checksum coverage
    44  	s := uint32(0)
    45  	for i := 0; i < csumcv; i += 2 {
    46  		s += uint32(b[i+1])<<8 | uint32(b[i])
    47  	}
    48  	if csumcv&1 == 0 {
    49  		s += uint32(b[csumcv])
    50  	}
    51  	s = s>>16 + s&0xffff
    52  	s = s + s>>16
    53  	return ^uint16(s)
    54  }
    55  
    56  // A Type represents an ICMP message type.
    57  type Type interface {
    58  	Protocol() int
    59  }
    60  
    61  // A Message represents an ICMP message.
    62  type Message struct {
    63  	Type     Type        // type, either ipv4.ICMPType or ipv6.ICMPType
    64  	Code     int         // code
    65  	Checksum int         // checksum
    66  	Body     MessageBody // body
    67  }
    68  
    69  // Marshal returns the binary encoding of the ICMP message m.
    70  //
    71  // For an ICMPv4 message, the returned message always contains the
    72  // calculated checksum field.
    73  //
    74  // For an ICMPv6 message, the returned message contains the calculated
    75  // checksum field when psh is not nil, otherwise the kernel will
    76  // compute the checksum field during the message transmission.
    77  // When psh is not nil, it must be the pseudo header for IPv6.
    78  func (m *Message) Marshal(psh []byte) ([]byte, error) {
    79  	var mtype byte
    80  	switch typ := m.Type.(type) {
    81  	case ipv4.ICMPType:
    82  		mtype = byte(typ)
    83  	case ipv6.ICMPType:
    84  		mtype = byte(typ)
    85  	default:
    86  		return nil, errInvalidProtocol
    87  	}
    88  	b := []byte{mtype, byte(m.Code), 0, 0}
    89  	proto := m.Type.Protocol()
    90  	if proto == iana.ProtocolIPv6ICMP && psh != nil {
    91  		b = append(psh, b...)
    92  	}
    93  	if m.Body != nil && m.Body.Len(proto) != 0 {
    94  		mb, err := m.Body.Marshal(proto)
    95  		if err != nil {
    96  			return nil, err
    97  		}
    98  		b = append(b, mb...)
    99  	}
   100  	if proto == iana.ProtocolIPv6ICMP {
   101  		if psh == nil { // cannot calculate checksum here
   102  			return b, nil
   103  		}
   104  		off, l := 2*net.IPv6len, len(b)-len(psh)
   105  		binary.BigEndian.PutUint32(b[off:off+4], uint32(l))
   106  	}
   107  	s := checksum(b)
   108  	// Place checksum back in header; using ^= avoids the
   109  	// assumption the checksum bytes are zero.
   110  	b[len(psh)+2] ^= byte(s)
   111  	b[len(psh)+3] ^= byte(s >> 8)
   112  	return b[len(psh):], nil
   113  }
   114  
   115  var parseFns = map[Type]func(int, Type, []byte) (MessageBody, error){
   116  	ipv4.ICMPTypeDestinationUnreachable: parseDstUnreach,
   117  	ipv4.ICMPTypeTimeExceeded:           parseTimeExceeded,
   118  	ipv4.ICMPTypeParameterProblem:       parseParamProb,
   119  
   120  	ipv4.ICMPTypeEcho:                parseEcho,
   121  	ipv4.ICMPTypeEchoReply:           parseEcho,
   122  	ipv4.ICMPTypeExtendedEchoRequest: parseExtendedEchoRequest,
   123  	ipv4.ICMPTypeExtendedEchoReply:   parseExtendedEchoReply,
   124  
   125  	ipv6.ICMPTypeDestinationUnreachable: parseDstUnreach,
   126  	ipv6.ICMPTypePacketTooBig:           parsePacketTooBig,
   127  	ipv6.ICMPTypeTimeExceeded:           parseTimeExceeded,
   128  	ipv6.ICMPTypeParameterProblem:       parseParamProb,
   129  
   130  	ipv6.ICMPTypeEchoRequest:         parseEcho,
   131  	ipv6.ICMPTypeEchoReply:           parseEcho,
   132  	ipv6.ICMPTypeExtendedEchoRequest: parseExtendedEchoRequest,
   133  	ipv6.ICMPTypeExtendedEchoReply:   parseExtendedEchoReply,
   134  }
   135  
   136  // ParseMessage parses b as an ICMP message.
   137  // The provided proto must be either the ICMPv4 or ICMPv6 protocol
   138  // number.
   139  func ParseMessage(proto int, b []byte) (*Message, error) {
   140  	if len(b) < 4 {
   141  		return nil, errMessageTooShort
   142  	}
   143  	var err error
   144  	m := &Message{Code: int(b[1]), Checksum: int(binary.BigEndian.Uint16(b[2:4]))}
   145  	switch proto {
   146  	case iana.ProtocolICMP:
   147  		m.Type = ipv4.ICMPType(b[0])
   148  	case iana.ProtocolIPv6ICMP:
   149  		m.Type = ipv6.ICMPType(b[0])
   150  	default:
   151  		return nil, errInvalidProtocol
   152  	}
   153  	if fn, ok := parseFns[m.Type]; !ok {
   154  		m.Body, err = parseRawBody(proto, b[4:])
   155  	} else {
   156  		m.Body, err = fn(proto, m.Type, b[4:])
   157  	}
   158  	if err != nil {
   159  		return nil, err
   160  	}
   161  	return m, nil
   162  }