2017-03-22 18:09:21 +01:00
|
|
|
// 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.
|
2019-03-17 20:19:56 +01:00
|
|
|
// PROBE: A utility for probing interfaces is defined in RFC 8335.
|
2017-03-22 18:09:21 +01:00
|
|
|
package icmp // import "golang.org/x/net/icmp"
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/binary"
|
|
|
|
"errors"
|
|
|
|
"net"
|
2019-03-17 20:19:56 +01:00
|
|
|
"runtime"
|
2017-03-22 18:09:21 +01:00
|
|
|
|
|
|
|
"golang.org/x/net/internal/iana"
|
|
|
|
"golang.org/x/net/ipv4"
|
|
|
|
"golang.org/x/net/ipv6"
|
|
|
|
)
|
|
|
|
|
2019-03-17 20:19:56 +01:00
|
|
|
// BUG(mikio): This package is not implemented on AIX, JS, NaCl and
|
|
|
|
// Plan 9.
|
2017-03-22 18:09:21 +01:00
|
|
|
|
|
|
|
var (
|
2019-03-17 20:19:56 +01:00
|
|
|
errInvalidConn = errors.New("invalid connection")
|
|
|
|
errInvalidProtocol = errors.New("invalid protocol")
|
2017-03-22 18:09:21 +01:00
|
|
|
errMessageTooShort = errors.New("message too short")
|
|
|
|
errHeaderTooShort = errors.New("header too short")
|
|
|
|
errBufferTooShort = errors.New("buffer too short")
|
2019-03-17 20:19:56 +01:00
|
|
|
errInvalidBody = errors.New("invalid body")
|
2017-03-22 18:09:21 +01:00
|
|
|
errNoExtension = errors.New("no extension")
|
|
|
|
errInvalidExtension = errors.New("invalid extension")
|
2019-03-17 20:19:56 +01:00
|
|
|
errNotImplemented = errors.New("not implemented on " + runtime.GOOS + "/" + runtime.GOARCH)
|
2017-03-22 18:09:21 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
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) {
|
2019-03-17 20:19:56 +01:00
|
|
|
var mtype byte
|
2017-03-22 18:09:21 +01:00
|
|
|
switch typ := m.Type.(type) {
|
|
|
|
case ipv4.ICMPType:
|
2019-03-17 20:19:56 +01:00
|
|
|
mtype = byte(typ)
|
2017-03-22 18:09:21 +01:00
|
|
|
case ipv6.ICMPType:
|
2019-03-17 20:19:56 +01:00
|
|
|
mtype = byte(typ)
|
2017-03-22 18:09:21 +01:00
|
|
|
default:
|
2019-03-17 20:19:56 +01:00
|
|
|
return nil, errInvalidProtocol
|
2017-03-22 18:09:21 +01:00
|
|
|
}
|
2019-03-17 20:19:56 +01:00
|
|
|
b := []byte{mtype, byte(m.Code), 0, 0}
|
|
|
|
proto := m.Type.Protocol()
|
|
|
|
if proto == iana.ProtocolIPv6ICMP && psh != nil {
|
2017-03-22 18:09:21 +01:00
|
|
|
b = append(psh, b...)
|
|
|
|
}
|
2019-03-17 20:19:56 +01:00
|
|
|
if m.Body != nil && m.Body.Len(proto) != 0 {
|
|
|
|
mb, err := m.Body.Marshal(proto)
|
2017-03-22 18:09:21 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
b = append(b, mb...)
|
|
|
|
}
|
2019-03-17 20:19:56 +01:00
|
|
|
if proto == iana.ProtocolIPv6ICMP {
|
2017-03-22 18:09:21 +01:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2019-03-17 20:19:56 +01:00
|
|
|
var parseFns = map[Type]func(int, Type, []byte) (MessageBody, error){
|
2017-03-22 18:09:21 +01:00
|
|
|
ipv4.ICMPTypeDestinationUnreachable: parseDstUnreach,
|
|
|
|
ipv4.ICMPTypeTimeExceeded: parseTimeExceeded,
|
|
|
|
ipv4.ICMPTypeParameterProblem: parseParamProb,
|
|
|
|
|
2019-03-17 20:19:56 +01:00
|
|
|
ipv4.ICMPTypeEcho: parseEcho,
|
|
|
|
ipv4.ICMPTypeEchoReply: parseEcho,
|
|
|
|
ipv4.ICMPTypeExtendedEchoRequest: parseExtendedEchoRequest,
|
|
|
|
ipv4.ICMPTypeExtendedEchoReply: parseExtendedEchoReply,
|
2017-03-22 18:09:21 +01:00
|
|
|
|
|
|
|
ipv6.ICMPTypeDestinationUnreachable: parseDstUnreach,
|
|
|
|
ipv6.ICMPTypePacketTooBig: parsePacketTooBig,
|
|
|
|
ipv6.ICMPTypeTimeExceeded: parseTimeExceeded,
|
|
|
|
ipv6.ICMPTypeParameterProblem: parseParamProb,
|
|
|
|
|
2019-03-17 20:19:56 +01:00
|
|
|
ipv6.ICMPTypeEchoRequest: parseEcho,
|
|
|
|
ipv6.ICMPTypeEchoReply: parseEcho,
|
|
|
|
ipv6.ICMPTypeExtendedEchoRequest: parseExtendedEchoRequest,
|
|
|
|
ipv6.ICMPTypeExtendedEchoReply: parseExtendedEchoReply,
|
2017-03-22 18:09:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// ParseMessage parses b as an ICMP message.
|
2019-03-17 20:19:56 +01:00
|
|
|
// The provided proto must be either the ICMPv4 or ICMPv6 protocol
|
|
|
|
// number.
|
2017-03-22 18:09:21 +01:00
|
|
|
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:
|
2019-03-17 20:19:56 +01:00
|
|
|
return nil, errInvalidProtocol
|
2017-03-22 18:09:21 +01:00
|
|
|
}
|
|
|
|
if fn, ok := parseFns[m.Type]; !ok {
|
2019-03-17 20:19:56 +01:00
|
|
|
m.Body, err = parseRawBody(proto, b[4:])
|
2017-03-22 18:09:21 +01:00
|
|
|
} else {
|
2019-03-17 20:19:56 +01:00
|
|
|
m.Body, err = fn(proto, m.Type, b[4:])
|
2017-03-22 18:09:21 +01:00
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return m, nil
|
|
|
|
}
|