.examples
.github
alerting
client
config
controller
core
docs
jsonpath
metrics
pattern
security
storage
test
util
vendor
github.com
golang.org
x
crypto
image
mod
net
bpf
context
icmp
dstunreach.go
echo.go
endpoint.go
extension.go
helper_posix.go
interface.go
ipv4.go
ipv6.go
listen_posix.go
listen_stub.go
message.go
messagebody.go
mpls.go
multipart.go
packettoobig.go
paramprob.go
sys_freebsd.go
timeexceeded.go
internal
ipv4
ipv6
LICENSE
PATENTS
oauth2
sync
sys
tools
xerrors
google.golang.org
gopkg.in
lukechampine.com
modernc.org
modules.txt
watchdog
web
.dockerignore
.gitattributes
.gitignore
Dockerfile
LICENSE
Makefile
README.md
config.yaml
go.mod
go.sum
main.go
163 lines
4.9 KiB
Go
163 lines
4.9 KiB
Go
// 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
|
|
}
|