123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162 |
- // Copyright 2012 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 provides basic functions for the manipulation of
- // messages used in the Internet Control Message Protocols,
- // ICMPv4 and ICMPv6.
- //
- // ICMPv4 and ICMPv6 are defined in RFC 792 and RFC 4443.
- // Multi-part message support for ICMP is defined in RFC 4884.
- // ICMP extensions for MPLS are defined in RFC 4950.
- // ICMP extensions for interface and next-hop identification are
- // defined in RFC 5837.
- // PROBE: A utility for probing interfaces is defined in RFC 8335.
- package icmp // import "golang.org/x/net/icmp"
- import (
- "encoding/binary"
- "errors"
- "net"
- "runtime"
- "golang.org/x/net/internal/iana"
- "golang.org/x/net/ipv4"
- "golang.org/x/net/ipv6"
- )
- // BUG(mikio): This package is not implemented on JS, NaCl and Plan 9.
- var (
- errInvalidConn = errors.New("invalid connection")
- errInvalidProtocol = errors.New("invalid protocol")
- errMessageTooShort = errors.New("message too short")
- errHeaderTooShort = errors.New("header too short")
- errBufferTooShort = errors.New("buffer too short")
- errInvalidBody = errors.New("invalid body")
- errNoExtension = errors.New("no extension")
- errInvalidExtension = errors.New("invalid extension")
- errNotImplemented = errors.New("not implemented on " + runtime.GOOS + "/" + runtime.GOARCH)
- )
- func checksum(b []byte) uint16 {
- csumcv := len(b) - 1 // checksum coverage
- s := uint32(0)
- for i := 0; i < csumcv; i += 2 {
- s += uint32(b[i+1])<<8 | uint32(b[i])
- }
- if csumcv&1 == 0 {
- s += uint32(b[csumcv])
- }
- s = s>>16 + s&0xffff
- s = s + s>>16
- return ^uint16(s)
- }
- // A Type represents an ICMP message type.
- type Type interface {
- Protocol() int
- }
- // A Message represents an ICMP message.
- type Message struct {
- Type Type // type, either ipv4.ICMPType or ipv6.ICMPType
- Code int // code
- Checksum int // checksum
- Body MessageBody // body
- }
- // Marshal returns the binary encoding of the ICMP message m.
- //
- // For an ICMPv4 message, the returned message always contains the
- // calculated checksum field.
- //
- // For an ICMPv6 message, the returned message contains the calculated
- // checksum field when psh is not nil, otherwise the kernel will
- // compute the checksum field during the message transmission.
- // When psh is not nil, it must be the pseudo header for IPv6.
- func (m *Message) Marshal(psh []byte) ([]byte, error) {
- var mtype byte
- switch typ := m.Type.(type) {
- case ipv4.ICMPType:
- mtype = byte(typ)
- case ipv6.ICMPType:
- mtype = byte(typ)
- default:
- return nil, errInvalidProtocol
- }
- b := []byte{mtype, byte(m.Code), 0, 0}
- proto := m.Type.Protocol()
- if proto == iana.ProtocolIPv6ICMP && psh != nil {
- b = append(psh, b...)
- }
- if m.Body != nil && m.Body.Len(proto) != 0 {
- mb, err := m.Body.Marshal(proto)
- if err != nil {
- return nil, err
- }
- b = append(b, mb...)
- }
- if proto == iana.ProtocolIPv6ICMP {
- if psh == nil { // cannot calculate checksum here
- return b, nil
- }
- off, l := 2*net.IPv6len, len(b)-len(psh)
- binary.BigEndian.PutUint32(b[off:off+4], uint32(l))
- }
- s := checksum(b)
- // Place checksum back in header; using ^= avoids the
- // assumption the checksum bytes are zero.
- b[len(psh)+2] ^= byte(s)
- b[len(psh)+3] ^= byte(s >> 8)
- return b[len(psh):], nil
- }
- var parseFns = map[Type]func(int, Type, []byte) (MessageBody, error){
- ipv4.ICMPTypeDestinationUnreachable: parseDstUnreach,
- ipv4.ICMPTypeTimeExceeded: parseTimeExceeded,
- ipv4.ICMPTypeParameterProblem: parseParamProb,
- ipv4.ICMPTypeEcho: parseEcho,
- ipv4.ICMPTypeEchoReply: parseEcho,
- ipv4.ICMPTypeExtendedEchoRequest: parseExtendedEchoRequest,
- ipv4.ICMPTypeExtendedEchoReply: parseExtendedEchoReply,
- ipv6.ICMPTypeDestinationUnreachable: parseDstUnreach,
- ipv6.ICMPTypePacketTooBig: parsePacketTooBig,
- ipv6.ICMPTypeTimeExceeded: parseTimeExceeded,
- ipv6.ICMPTypeParameterProblem: parseParamProb,
- ipv6.ICMPTypeEchoRequest: parseEcho,
- ipv6.ICMPTypeEchoReply: parseEcho,
- ipv6.ICMPTypeExtendedEchoRequest: parseExtendedEchoRequest,
- ipv6.ICMPTypeExtendedEchoReply: parseExtendedEchoReply,
- }
- // ParseMessage parses b as an ICMP message.
- // The provided proto must be either the ICMPv4 or ICMPv6 protocol
- // number.
- func ParseMessage(proto int, b []byte) (*Message, error) {
- if len(b) < 4 {
- return nil, errMessageTooShort
- }
- var err error
- m := &Message{Code: int(b[1]), Checksum: int(binary.BigEndian.Uint16(b[2:4]))}
- switch proto {
- case iana.ProtocolICMP:
- m.Type = ipv4.ICMPType(b[0])
- case iana.ProtocolIPv6ICMP:
- m.Type = ipv6.ICMPType(b[0])
- default:
- return nil, errInvalidProtocol
- }
- if fn, ok := parseFns[m.Type]; !ok {
- m.Body, err = parseRawBody(proto, b[4:])
- } else {
- m.Body, err = fn(proto, m.Type, b[4:])
- }
- if err != nil {
- return nil, err
- }
- return m, nil
- }
|