mirror of
https://github.com/containers/gvisor-tap-vsock.git
synced 2026-04-22 16:17:07 +08:00
Add ICMP forwarding support
The current implementation supports forwarding only ICMP Echo(ping) packets, as OS not allow to send arbitrary packets without escalating privileges. All other ICMP packets are dropped(ignored) Changes in 'icmp_packet.go' and 'icmp.go' was mostly maded by cursor. Signed-off-by: Yevhen Vydolob <yvydolob@redhat.com>
This commit is contained in:
committed by
Christophe Fergeau
parent
6f646c44e5
commit
cf0e470794
@@ -25,6 +25,7 @@ require (
|
||||
github.com/vishvananda/netlink v1.3.1
|
||||
golang.org/x/crypto v0.50.0
|
||||
golang.org/x/mod v0.35.0
|
||||
golang.org/x/net v0.52.0
|
||||
golang.org/x/sync v0.20.0
|
||||
golang.org/x/sys v0.43.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
@@ -45,7 +46,6 @@ require (
|
||||
github.com/vishvananda/netns v0.0.5 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
|
||||
golang.org/x/net v0.52.0 // indirect
|
||||
golang.org/x/text v0.36.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
golang.org/x/tools v0.43.0 // indirect
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package forwarder
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
type Forwarder struct {
|
||||
handler func(request *ICMPForwarderRequest)
|
||||
stack *stack.Stack
|
||||
}
|
||||
|
||||
func ICMP(s *stack.Stack, nat map[tcpip.Address]tcpip.Address, natLock *sync.Mutex) *Forwarder {
|
||||
return NewForwarder(s, func(r *ICMPForwarderRequest) {
|
||||
localAddress := r.ID().LocalAddress
|
||||
|
||||
// Skip forwarding for addresses that should be handled locally
|
||||
if header.IsV4LoopbackAddress(localAddress) || localAddress == header.IPv4Broadcast {
|
||||
return
|
||||
}
|
||||
|
||||
// Apply NAT translation if needed
|
||||
natLock.Lock()
|
||||
if replaced, ok := nat[localAddress]; ok {
|
||||
localAddress = replaced
|
||||
}
|
||||
natLock.Unlock()
|
||||
|
||||
pkt := r.Packet()
|
||||
if pkt == nil {
|
||||
log.Warningf("Dropping ICMP packet from VM (no packet data)")
|
||||
return
|
||||
}
|
||||
|
||||
// Check if this is an ICMP Echo Request (PING)
|
||||
transportHeader := pkt.TransportHeader().Slice()
|
||||
if len(transportHeader) < header.ICMPv4MinimumSize {
|
||||
log.Warningf("Dropping ICMP packet from VM (packet too short)")
|
||||
return
|
||||
}
|
||||
|
||||
icmpHeader := header.ICMPv4(transportHeader)
|
||||
if icmpHeader.Type() != header.ICMPv4Echo {
|
||||
// Not a PING, drop it
|
||||
log.Warningf("Dropping ICMP packet from VM (type %d, not Echo Request)", icmpHeader.Type())
|
||||
return
|
||||
}
|
||||
|
||||
// This is a PING request - forward it using unprivileged ICMP sockets
|
||||
go handlePingRequest(s, r, localAddress, icmpHeader, pkt)
|
||||
})
|
||||
}
|
||||
|
||||
// HandlePacket handles all packets.
|
||||
//
|
||||
// This function is expected to be passed as an argument to the
|
||||
// stack.SetTransportProtocolHandler function.
|
||||
func (f *Forwarder) HandlePacket(id stack.TransportEndpointID, pkt *stack.PacketBuffer) bool {
|
||||
f.handler(NewICMPForwarderRequest(f.stack, id, pkt.IncRef()))
|
||||
return true
|
||||
}
|
||||
|
||||
// NewForwarder allocates and initializes a new forwarder.
|
||||
func NewForwarder(s *stack.Stack, handler func(*ICMPForwarderRequest)) *Forwarder {
|
||||
return &Forwarder{
|
||||
stack: s,
|
||||
handler: handler,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package forwarder
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
)
|
||||
|
||||
// ICMPForwarderRequest represents a request to forward an ICMP packet.
|
||||
type ICMPForwarderRequest struct {
|
||||
stack *stack.Stack
|
||||
id stack.TransportEndpointID
|
||||
pkt *stack.PacketBuffer
|
||||
}
|
||||
|
||||
// NewICMPForwarderRequest creates a new ICMP forwarder request.
|
||||
func NewICMPForwarderRequest(s *stack.Stack, id stack.TransportEndpointID, pkt *stack.PacketBuffer) *ICMPForwarderRequest {
|
||||
return &ICMPForwarderRequest{
|
||||
stack: s,
|
||||
id: id,
|
||||
pkt: pkt,
|
||||
}
|
||||
}
|
||||
|
||||
// ID returns the 4-tuple (src address, src port, dst address, dst port) that
|
||||
// represents the connection request.
|
||||
func (f *ICMPForwarderRequest) ID() stack.TransportEndpointID {
|
||||
return f.id
|
||||
}
|
||||
|
||||
// Packet returns the packet buffer associated with this forwarder request.
|
||||
func (f *ICMPForwarderRequest) Packet() *stack.PacketBuffer {
|
||||
return f.pkt
|
||||
}
|
||||
|
||||
// CreateEndpoint creates a new endpoint for this forwarder request.
|
||||
func (f *ICMPForwarderRequest) CreateEndpoint(s *stack.Stack, wq *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
|
||||
// Use the stack's public NewEndpoint API instead of linkname
|
||||
return s.NewEndpoint(f.pkt.TransportProtocolNumber, f.pkt.NetworkProtocolNumber, wq)
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
package forwarder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
netIcmp "golang.org/x/net/icmp"
|
||||
"golang.org/x/net/ipv4"
|
||||
"gvisor.dev/gvisor/pkg/buffer"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/checksum"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
// echoRequestDetails contains the extracted details from an ICMP echo request.
|
||||
type echoRequestDetails struct {
|
||||
ident uint16
|
||||
seq uint16
|
||||
payload []byte
|
||||
srcAddr tcpip.Address
|
||||
dataBuf buffer.Buffer
|
||||
}
|
||||
|
||||
// safeUint16 safely converts an int to uint16, clamping to valid range.
|
||||
// ICMP ID and sequence numbers are 16-bit values, so values outside this range
|
||||
// are invalid and will be clamped.
|
||||
func safeUint16(v int) uint16 {
|
||||
if v < 0 {
|
||||
return 0
|
||||
}
|
||||
if v > 0xFFFF {
|
||||
return 0xFFFF
|
||||
}
|
||||
return uint16(v)
|
||||
}
|
||||
|
||||
// handlePingRequest handles forwarding an ICMP echo request (PING) from the VM
|
||||
// to the external network and injecting the reply back into the VM.
|
||||
func handlePingRequest(s *stack.Stack, r *ICMPForwarderRequest, destAddr tcpip.Address, icmpHeader header.ICMPv4, pkt *stack.PacketBuffer) {
|
||||
defer pkt.DecRef()
|
||||
|
||||
// Extract ICMP echo request details
|
||||
details, err := extractEchoRequestDetails(r, icmpHeader, pkt)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer details.dataBuf.Release()
|
||||
|
||||
// Create ICMP connection
|
||||
conn, err := createICMPConnection()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
// Send the echo request
|
||||
if err := sendEchoRequest(conn, destAddr, details.ident, details.seq, details.payload); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Receive and parse the echo reply
|
||||
echoReply, err := receiveEchoReply(conn)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Validate the reply matches our request
|
||||
if !validateEchoReply(echoReply, details.ident, details.seq) {
|
||||
return
|
||||
}
|
||||
|
||||
// Forward the reply back to the VM's network stack
|
||||
// Safely convert int to uint16 (ICMP ID and Seq are 16-bit values)
|
||||
forwardEchoReply(s, r, details.srcAddr, destAddr, safeUint16(echoReply.ID), safeUint16(echoReply.Seq), echoReply.Data)
|
||||
}
|
||||
|
||||
// extractEchoRequestDetails extracts the identifier, sequence, payload, and source address
|
||||
// from an ICMP echo request packet.
|
||||
func extractEchoRequestDetails(r *ICMPForwarderRequest, icmpHeader header.ICMPv4, pkt *stack.PacketBuffer) (*echoRequestDetails, error) {
|
||||
ident := icmpHeader.Ident()
|
||||
seq := icmpHeader.Sequence()
|
||||
|
||||
// Extract payload data
|
||||
dataBuf := pkt.Data().ToBuffer()
|
||||
dataSize := int(dataBuf.Size())
|
||||
payload := make([]byte, dataSize)
|
||||
if dataSize > 0 {
|
||||
_, _ = dataBuf.ReadAt(payload, 0)
|
||||
}
|
||||
|
||||
// Get source address from the request
|
||||
srcAddr := r.ID().RemoteAddress
|
||||
|
||||
return &echoRequestDetails{
|
||||
ident: ident,
|
||||
seq: seq,
|
||||
payload: payload,
|
||||
srcAddr: srcAddr,
|
||||
dataBuf: dataBuf,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// sendEchoRequest creates and sends an ICMP echo request message.
|
||||
func sendEchoRequest(conn *netIcmp.PacketConn, destAddr tcpip.Address, ident, seq uint16, payload []byte) error {
|
||||
// Create ICMP echo request message
|
||||
msg := &netIcmp.Message{
|
||||
Type: ipv4.ICMPTypeEcho,
|
||||
Code: 0,
|
||||
Body: &netIcmp.Echo{
|
||||
ID: int(ident),
|
||||
Seq: int(seq),
|
||||
Data: payload,
|
||||
},
|
||||
}
|
||||
|
||||
// Marshal the message
|
||||
msgBytes, err := msg.Marshal(nil)
|
||||
if err != nil {
|
||||
log.Debugf("Failed to marshal ICMP message: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Parse destination address
|
||||
dstIP := net.ParseIP(destAddr.String())
|
||||
if dstIP == nil {
|
||||
log.Debugf("Failed to parse destination address: %s", destAddr)
|
||||
return fmt.Errorf("failed to parse destination address: %s", destAddr)
|
||||
}
|
||||
|
||||
// Create destination address based on platform
|
||||
dst := createDestinationAddr(dstIP)
|
||||
|
||||
// Send the ping request
|
||||
_, err = conn.WriteTo(msgBytes, dst)
|
||||
if err != nil {
|
||||
log.Debugf("Failed to send ICMP echo request: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// receiveEchoReply reads and parses an ICMP echo reply from the connection.
|
||||
func receiveEchoReply(conn *netIcmp.PacketConn) (*netIcmp.Echo, error) {
|
||||
// Read the reply
|
||||
replyBytes := make([]byte, 1500)
|
||||
n, _, err := conn.ReadFrom(replyBytes)
|
||||
if err != nil {
|
||||
log.Debugf("Failed to receive ICMP echo reply: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Extract ICMP data (skip IP header on Windows)
|
||||
replyData, err := extractICMPData(replyBytes[:n])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Parse the reply
|
||||
replyMsg, err := netIcmp.ParseMessage(ipv4.ICMPTypeEchoReply.Protocol(), replyData)
|
||||
if err != nil {
|
||||
log.Debugf("Failed to parse ICMP reply: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check if it's an echo reply
|
||||
if replyMsg.Type != ipv4.ICMPTypeEchoReply {
|
||||
log.Debugf("Received ICMP message type %v, expected Echo Reply", replyMsg.Type)
|
||||
return nil, fmt.Errorf("unexpected ICMP message type: %v", replyMsg.Type)
|
||||
}
|
||||
|
||||
echoReply, ok := replyMsg.Body.(*netIcmp.Echo)
|
||||
if !ok {
|
||||
log.Debugf("ICMP reply body is not an Echo")
|
||||
return nil, fmt.Errorf("ICMP reply body is not an Echo")
|
||||
}
|
||||
|
||||
return echoReply, nil
|
||||
}
|
||||
|
||||
// validateEchoReply verifies that the echo reply matches the original request.
|
||||
func validateEchoReply(echoReply *netIcmp.Echo, expectedIdent, expectedSeq uint16) bool {
|
||||
if echoReply.ID != int(expectedIdent) || echoReply.Seq != int(expectedSeq) {
|
||||
log.Debugf("ICMP reply ID/Seq mismatch: got ID=%d Seq=%d, expected ID=%d Seq=%d",
|
||||
echoReply.ID, echoReply.Seq, expectedIdent, expectedSeq)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// forwardEchoReply creates an ICMP echo reply packet and forwards it back to the VM.
|
||||
func forwardEchoReply(s *stack.Stack, r *ICMPForwarderRequest, dstAddr tcpip.Address, srcAddr tcpip.Address, ident, seq uint16, data []byte) {
|
||||
// Create ICMP echo reply header
|
||||
icmpHeaderSize := header.ICMPv4MinimumSize
|
||||
icmpBuf := make([]byte, icmpHeaderSize+len(data))
|
||||
icmpHdr := header.ICMPv4(icmpBuf)
|
||||
|
||||
icmpHdr.SetType(header.ICMPv4EchoReply)
|
||||
icmpHdr.SetCode(0)
|
||||
icmpHdr.SetIdent(ident)
|
||||
icmpHdr.SetSequence(seq)
|
||||
|
||||
// Copy data
|
||||
if len(data) > 0 {
|
||||
copy(icmpBuf[icmpHeaderSize:], data)
|
||||
}
|
||||
|
||||
// Calculate checksum
|
||||
icmpHdr.SetChecksum(0)
|
||||
icmpHdr.SetChecksum(^checksum.Checksum(icmpBuf, 0))
|
||||
|
||||
// Get the original packet's network info
|
||||
origPkt := r.Packet()
|
||||
if origPkt == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Find route to send the reply back
|
||||
// Use srcAddr (the address we pinged) as the local address so the reply appears
|
||||
// to come from the address the VM originally pinged, not from the gateway
|
||||
route, err := s.FindRoute(origPkt.NICID, srcAddr, dstAddr, header.IPv4ProtocolNumber, false)
|
||||
if err != nil {
|
||||
log.Debugf("Failed to find route for ICMP reply: %v", err)
|
||||
return
|
||||
}
|
||||
defer route.Release()
|
||||
|
||||
// Create packet buffer with ICMP reply
|
||||
payload := buffer.MakeWithData(icmpBuf)
|
||||
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
ReserveHeaderBytes: int(route.MaxHeaderLength()),
|
||||
Payload: payload,
|
||||
})
|
||||
defer pkt.DecRef()
|
||||
|
||||
pkt.NetworkProtocolNumber = header.IPv4ProtocolNumber
|
||||
pkt.TransportProtocolNumber = header.ICMPv4ProtocolNumber
|
||||
|
||||
// Write the packet
|
||||
params := stack.NetworkHeaderParams{
|
||||
Protocol: header.ICMPv4ProtocolNumber,
|
||||
TTL: 64,
|
||||
TOS: 0,
|
||||
}
|
||||
if err := route.WritePacket(params, pkt); err != nil {
|
||||
log.Debugf("Failed to forward ICMP echo reply: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Debugf("Successfully forwarded ICMP echo reply to %s", dstAddr)
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
//go:build !windows
|
||||
|
||||
package forwarder
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
netIcmp "golang.org/x/net/icmp"
|
||||
)
|
||||
|
||||
// createICMPConnection creates an ICMP connection using unprivileged ICMP sockets (udp4) on Linux/macOS.
|
||||
func createICMPConnection() (*netIcmp.PacketConn, error) {
|
||||
conn, err := netIcmp.ListenPacket("udp4", "0.0.0.0")
|
||||
if err != nil {
|
||||
log.Debugf("Failed to create ICMP connection: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set read deadline
|
||||
if err := conn.SetReadDeadline(time.Now().Add(5 * time.Second)); err != nil {
|
||||
conn.Close()
|
||||
log.Debugf("Failed to set read deadline: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// createDestinationAddr creates a destination address for Unix unprivileged sockets.
|
||||
func createDestinationAddr(dstIP net.IP) net.Addr {
|
||||
// Linux/macOS use net.UDPAddr for unprivileged sockets
|
||||
return &net.UDPAddr{IP: dstIP, Port: 0}
|
||||
}
|
||||
|
||||
// extractICMPData extracts ICMP data from the received bytes.
|
||||
// On Linux/macOS unprivileged sockets, it returns the data as-is.
|
||||
func extractICMPData(replyBytes []byte) ([]byte, error) {
|
||||
// Linux/macOS unprivileged sockets return just the ICMP data
|
||||
return replyBytes, nil
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
//go:build windows
|
||||
|
||||
package forwarder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
netIcmp "golang.org/x/net/icmp"
|
||||
)
|
||||
|
||||
// createICMPConnection creates an ICMP connection using privileged raw sockets (ip4:icmp) on Windows.
|
||||
func createICMPConnection() (*netIcmp.PacketConn, error) {
|
||||
conn, err := netIcmp.ListenPacket("ip4:icmp", "0.0.0.0")
|
||||
if err != nil {
|
||||
log.Debugf("Failed to create ICMP connection: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set read deadline
|
||||
if err := conn.SetReadDeadline(time.Now().Add(5 * time.Second)); err != nil {
|
||||
conn.Close()
|
||||
log.Debugf("Failed to set read deadline: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// createDestinationAddr creates a destination address for Windows raw sockets.
|
||||
func createDestinationAddr(dstIP net.IP) net.Addr {
|
||||
// Windows requires net.IPAddr for raw sockets
|
||||
return &net.IPAddr{IP: dstIP}
|
||||
}
|
||||
|
||||
// extractICMPData extracts ICMP data from the received bytes.
|
||||
// On Windows with raw sockets, it skips the IP header.
|
||||
func extractICMPData(replyBytes []byte) ([]byte, error) {
|
||||
// Raw sockets on Windows include the IP header, so we need to skip it
|
||||
if len(replyBytes) < 20 {
|
||||
log.Debugf("Reply packet too short: %d bytes", len(replyBytes))
|
||||
return nil, fmt.Errorf("reply packet too short: %d bytes", len(replyBytes))
|
||||
}
|
||||
|
||||
// Check if it's IPv4 (first byte: version and IHL)
|
||||
version := (replyBytes[0] >> 4) & 0x0F
|
||||
if version != 4 {
|
||||
log.Debugf("Unexpected IP version: %d", version)
|
||||
return nil, fmt.Errorf("unexpected IP version: %d", version)
|
||||
}
|
||||
|
||||
// Get IP header length (IHL is in the lower 4 bits of first byte, in 4-byte units)
|
||||
ihl := int(replyBytes[0]&0x0F) * 4
|
||||
if ihl < 20 || ihl > len(replyBytes) {
|
||||
log.Debugf("Invalid IP header length: %d", ihl)
|
||||
return nil, fmt.Errorf("invalid IP header length: %d", ihl)
|
||||
}
|
||||
|
||||
return replyBytes[ihl:], nil
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/icmp"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
|
||||
)
|
||||
@@ -28,6 +29,8 @@ func addServices(configuration *types.Configuration, s *stack.Stack, ipPool *tap
|
||||
s.SetTransportProtocolHandler(tcp.ProtocolNumber, tcpForwarder.HandlePacket)
|
||||
udpForwarder := forwarder.UDP(s, translation, &natLock)
|
||||
s.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket)
|
||||
icmpForwarder := forwarder.ICMP(s, translation, &natLock)
|
||||
s.SetTransportProtocolHandler(icmp.ProtocolNumber4, icmpForwarder.HandlePacket)
|
||||
|
||||
dnsMux, err := dnsServer(configuration, s)
|
||||
if err != nil {
|
||||
|
||||
@@ -124,4 +124,9 @@ var _ = ginkgo.Describe("ping with gvproxy and vfkit", func() {
|
||||
log.Infof("ping: %s", out)
|
||||
gomega.Expect(err).To(gomega.HaveOccurred())
|
||||
})
|
||||
ginkgo.It("should succeed to ping an localhost", func() {
|
||||
out, err := sshExec("ping -w2 127.0.0.1")
|
||||
log.Infof("ping: %s", out)
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
// Copyright 2014 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
|
||||
|
||||
import (
|
||||
"golang.org/x/net/internal/iana"
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
)
|
||||
|
||||
// A DstUnreach represents an ICMP destination unreachable message
|
||||
// body.
|
||||
type DstUnreach struct {
|
||||
Data []byte // data, known as original datagram field
|
||||
Extensions []Extension // extensions
|
||||
}
|
||||
|
||||
// Len implements the Len method of MessageBody interface.
|
||||
func (p *DstUnreach) Len(proto int) int {
|
||||
if p == nil {
|
||||
return 0
|
||||
}
|
||||
l, _ := multipartMessageBodyDataLen(proto, true, p.Data, p.Extensions)
|
||||
return l
|
||||
}
|
||||
|
||||
// Marshal implements the Marshal method of MessageBody interface.
|
||||
func (p *DstUnreach) Marshal(proto int) ([]byte, error) {
|
||||
var typ Type
|
||||
switch proto {
|
||||
case iana.ProtocolICMP:
|
||||
typ = ipv4.ICMPTypeDestinationUnreachable
|
||||
case iana.ProtocolIPv6ICMP:
|
||||
typ = ipv6.ICMPTypeDestinationUnreachable
|
||||
default:
|
||||
return nil, errInvalidProtocol
|
||||
}
|
||||
if !validExtensions(typ, p.Extensions) {
|
||||
return nil, errInvalidExtension
|
||||
}
|
||||
return marshalMultipartMessageBody(proto, true, p.Data, p.Extensions)
|
||||
}
|
||||
|
||||
// parseDstUnreach parses b as an ICMP destination unreachable message
|
||||
// body.
|
||||
func parseDstUnreach(proto int, typ Type, b []byte) (MessageBody, error) {
|
||||
if len(b) < 4 {
|
||||
return nil, errMessageTooShort
|
||||
}
|
||||
p := &DstUnreach{}
|
||||
var err error
|
||||
p.Data, p.Extensions, err = parseMultipartMessageBody(proto, typ, b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
// 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
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"golang.org/x/net/internal/iana"
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
)
|
||||
|
||||
// An Echo represents an ICMP echo request or reply message body.
|
||||
type Echo struct {
|
||||
ID int // identifier
|
||||
Seq int // sequence number
|
||||
Data []byte // data
|
||||
}
|
||||
|
||||
// Len implements the Len method of MessageBody interface.
|
||||
func (p *Echo) Len(proto int) int {
|
||||
if p == nil {
|
||||
return 0
|
||||
}
|
||||
return 4 + len(p.Data)
|
||||
}
|
||||
|
||||
// Marshal implements the Marshal method of MessageBody interface.
|
||||
func (p *Echo) Marshal(proto int) ([]byte, error) {
|
||||
b := make([]byte, 4+len(p.Data))
|
||||
binary.BigEndian.PutUint16(b[:2], uint16(p.ID))
|
||||
binary.BigEndian.PutUint16(b[2:4], uint16(p.Seq))
|
||||
copy(b[4:], p.Data)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// parseEcho parses b as an ICMP echo request or reply message body.
|
||||
func parseEcho(proto int, _ Type, b []byte) (MessageBody, error) {
|
||||
bodyLen := len(b)
|
||||
if bodyLen < 4 {
|
||||
return nil, errMessageTooShort
|
||||
}
|
||||
p := &Echo{ID: int(binary.BigEndian.Uint16(b[:2])), Seq: int(binary.BigEndian.Uint16(b[2:4]))}
|
||||
if bodyLen > 4 {
|
||||
p.Data = make([]byte, bodyLen-4)
|
||||
copy(p.Data, b[4:])
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// An ExtendedEchoRequest represents an ICMP extended echo request
|
||||
// message body.
|
||||
type ExtendedEchoRequest struct {
|
||||
ID int // identifier
|
||||
Seq int // sequence number
|
||||
Local bool // must be true when identifying by name or index
|
||||
Extensions []Extension // extensions
|
||||
}
|
||||
|
||||
// Len implements the Len method of MessageBody interface.
|
||||
func (p *ExtendedEchoRequest) Len(proto int) int {
|
||||
if p == nil {
|
||||
return 0
|
||||
}
|
||||
l, _ := multipartMessageBodyDataLen(proto, false, nil, p.Extensions)
|
||||
return l
|
||||
}
|
||||
|
||||
// Marshal implements the Marshal method of MessageBody interface.
|
||||
func (p *ExtendedEchoRequest) Marshal(proto int) ([]byte, error) {
|
||||
var typ Type
|
||||
switch proto {
|
||||
case iana.ProtocolICMP:
|
||||
typ = ipv4.ICMPTypeExtendedEchoRequest
|
||||
case iana.ProtocolIPv6ICMP:
|
||||
typ = ipv6.ICMPTypeExtendedEchoRequest
|
||||
default:
|
||||
return nil, errInvalidProtocol
|
||||
}
|
||||
if !validExtensions(typ, p.Extensions) {
|
||||
return nil, errInvalidExtension
|
||||
}
|
||||
b, err := marshalMultipartMessageBody(proto, false, nil, p.Extensions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
binary.BigEndian.PutUint16(b[:2], uint16(p.ID))
|
||||
b[2] = byte(p.Seq)
|
||||
if p.Local {
|
||||
b[3] |= 0x01
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// parseExtendedEchoRequest parses b as an ICMP extended echo request
|
||||
// message body.
|
||||
func parseExtendedEchoRequest(proto int, typ Type, b []byte) (MessageBody, error) {
|
||||
if len(b) < 4 {
|
||||
return nil, errMessageTooShort
|
||||
}
|
||||
p := &ExtendedEchoRequest{ID: int(binary.BigEndian.Uint16(b[:2])), Seq: int(b[2])}
|
||||
if b[3]&0x01 != 0 {
|
||||
p.Local = true
|
||||
}
|
||||
var err error
|
||||
_, p.Extensions, err = parseMultipartMessageBody(proto, typ, b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// An ExtendedEchoReply represents an ICMP extended echo reply message
|
||||
// body.
|
||||
type ExtendedEchoReply struct {
|
||||
ID int // identifier
|
||||
Seq int // sequence number
|
||||
State int // 3-bit state working together with Message.Code
|
||||
Active bool // probed interface is active
|
||||
IPv4 bool // probed interface runs IPv4
|
||||
IPv6 bool // probed interface runs IPv6
|
||||
}
|
||||
|
||||
// Len implements the Len method of MessageBody interface.
|
||||
func (p *ExtendedEchoReply) Len(proto int) int {
|
||||
if p == nil {
|
||||
return 0
|
||||
}
|
||||
return 4
|
||||
}
|
||||
|
||||
// Marshal implements the Marshal method of MessageBody interface.
|
||||
func (p *ExtendedEchoReply) Marshal(proto int) ([]byte, error) {
|
||||
b := make([]byte, 4)
|
||||
binary.BigEndian.PutUint16(b[:2], uint16(p.ID))
|
||||
b[2] = byte(p.Seq)
|
||||
b[3] = byte(p.State<<5) & 0xe0
|
||||
if p.Active {
|
||||
b[3] |= 0x04
|
||||
}
|
||||
if p.IPv4 {
|
||||
b[3] |= 0x02
|
||||
}
|
||||
if p.IPv6 {
|
||||
b[3] |= 0x01
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// parseExtendedEchoReply parses b as an ICMP extended echo reply
|
||||
// message body.
|
||||
func parseExtendedEchoReply(proto int, _ Type, b []byte) (MessageBody, error) {
|
||||
if len(b) < 4 {
|
||||
return nil, errMessageTooShort
|
||||
}
|
||||
p := &ExtendedEchoReply{
|
||||
ID: int(binary.BigEndian.Uint16(b[:2])),
|
||||
Seq: int(b[2]),
|
||||
State: int(b[3]) >> 5,
|
||||
}
|
||||
if b[3]&0x04 != 0 {
|
||||
p.Active = true
|
||||
}
|
||||
if b[3]&0x02 != 0 {
|
||||
p.IPv4 = true
|
||||
}
|
||||
if b[3]&0x01 != 0 {
|
||||
p.IPv6 = true
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
// Copyright 2014 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
|
||||
|
||||
import (
|
||||
"net"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
)
|
||||
|
||||
var _ net.PacketConn = &PacketConn{}
|
||||
|
||||
// A PacketConn represents a packet network endpoint that uses either
|
||||
// ICMPv4 or ICMPv6.
|
||||
type PacketConn struct {
|
||||
c net.PacketConn
|
||||
p4 *ipv4.PacketConn
|
||||
p6 *ipv6.PacketConn
|
||||
}
|
||||
|
||||
func (c *PacketConn) ok() bool { return c != nil && c.c != nil }
|
||||
|
||||
// IPv4PacketConn returns the ipv4.PacketConn of c.
|
||||
// It returns nil when c is not created as the endpoint for ICMPv4.
|
||||
func (c *PacketConn) IPv4PacketConn() *ipv4.PacketConn {
|
||||
if !c.ok() {
|
||||
return nil
|
||||
}
|
||||
return c.p4
|
||||
}
|
||||
|
||||
// IPv6PacketConn returns the ipv6.PacketConn of c.
|
||||
// It returns nil when c is not created as the endpoint for ICMPv6.
|
||||
func (c *PacketConn) IPv6PacketConn() *ipv6.PacketConn {
|
||||
if !c.ok() {
|
||||
return nil
|
||||
}
|
||||
return c.p6
|
||||
}
|
||||
|
||||
// ReadFrom reads an ICMP message from the connection.
|
||||
func (c *PacketConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
||||
if !c.ok() {
|
||||
return 0, nil, errInvalidConn
|
||||
}
|
||||
// Please be informed that ipv4.NewPacketConn enables
|
||||
// IP_STRIPHDR option by default on Darwin.
|
||||
// See golang.org/issue/9395 for further information.
|
||||
if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && c.p4 != nil {
|
||||
n, _, peer, err := c.p4.ReadFrom(b)
|
||||
return n, peer, err
|
||||
}
|
||||
return c.c.ReadFrom(b)
|
||||
}
|
||||
|
||||
// WriteTo writes the ICMP message b to dst.
|
||||
// The provided dst must be net.UDPAddr when c is a non-privileged
|
||||
// datagram-oriented ICMP endpoint.
|
||||
// Otherwise it must be net.IPAddr.
|
||||
func (c *PacketConn) WriteTo(b []byte, dst net.Addr) (int, error) {
|
||||
if !c.ok() {
|
||||
return 0, errInvalidConn
|
||||
}
|
||||
return c.c.WriteTo(b, dst)
|
||||
}
|
||||
|
||||
// Close closes the endpoint.
|
||||
func (c *PacketConn) Close() error {
|
||||
if !c.ok() {
|
||||
return errInvalidConn
|
||||
}
|
||||
return c.c.Close()
|
||||
}
|
||||
|
||||
// LocalAddr returns the local network address.
|
||||
func (c *PacketConn) LocalAddr() net.Addr {
|
||||
if !c.ok() {
|
||||
return nil
|
||||
}
|
||||
return c.c.LocalAddr()
|
||||
}
|
||||
|
||||
// SetDeadline sets the read and write deadlines associated with the
|
||||
// endpoint.
|
||||
func (c *PacketConn) SetDeadline(t time.Time) error {
|
||||
if !c.ok() {
|
||||
return errInvalidConn
|
||||
}
|
||||
return c.c.SetDeadline(t)
|
||||
}
|
||||
|
||||
// SetReadDeadline sets the read deadline associated with the
|
||||
// endpoint.
|
||||
func (c *PacketConn) SetReadDeadline(t time.Time) error {
|
||||
if !c.ok() {
|
||||
return errInvalidConn
|
||||
}
|
||||
return c.c.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
// SetWriteDeadline sets the write deadline associated with the
|
||||
// endpoint.
|
||||
func (c *PacketConn) SetWriteDeadline(t time.Time) error {
|
||||
if !c.ok() {
|
||||
return errInvalidConn
|
||||
}
|
||||
return c.c.SetWriteDeadline(t)
|
||||
}
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
// Copyright 2015 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
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
)
|
||||
|
||||
// An Extension represents an ICMP extension.
|
||||
type Extension interface {
|
||||
// Len returns the length of ICMP extension.
|
||||
// The provided proto must be either the ICMPv4 or ICMPv6
|
||||
// protocol number.
|
||||
Len(proto int) int
|
||||
|
||||
// Marshal returns the binary encoding of ICMP extension.
|
||||
// The provided proto must be either the ICMPv4 or ICMPv6
|
||||
// protocol number.
|
||||
Marshal(proto int) ([]byte, error)
|
||||
}
|
||||
|
||||
const extensionVersion = 2
|
||||
|
||||
func validExtensionHeader(b []byte) bool {
|
||||
v := int(b[0]&0xf0) >> 4
|
||||
s := binary.BigEndian.Uint16(b[2:4])
|
||||
if s != 0 {
|
||||
s = checksum(b)
|
||||
}
|
||||
if v != extensionVersion || s != 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// parseExtensions parses b as a list of ICMP extensions.
|
||||
// The length attribute l must be the length attribute field in
|
||||
// received icmp messages.
|
||||
//
|
||||
// It will return a list of ICMP extensions and an adjusted length
|
||||
// attribute that represents the length of the padded original
|
||||
// datagram field. Otherwise, it returns an error.
|
||||
func parseExtensions(typ Type, b []byte, l int) ([]Extension, int, error) {
|
||||
// Still a lot of non-RFC 4884 compliant implementations are
|
||||
// out there. Set the length attribute l to 128 when it looks
|
||||
// inappropriate for backwards compatibility.
|
||||
//
|
||||
// A minimal extension at least requires 8 octets; 4 octets
|
||||
// for an extension header, and 4 octets for a single object
|
||||
// header.
|
||||
//
|
||||
// See RFC 4884 for further information.
|
||||
switch typ {
|
||||
case ipv4.ICMPTypeExtendedEchoRequest, ipv6.ICMPTypeExtendedEchoRequest:
|
||||
if len(b) < 8 || !validExtensionHeader(b) {
|
||||
return nil, -1, errNoExtension
|
||||
}
|
||||
l = 0
|
||||
default:
|
||||
if 128 > l || l+8 > len(b) {
|
||||
l = 128
|
||||
}
|
||||
if l+8 > len(b) {
|
||||
return nil, -1, errNoExtension
|
||||
}
|
||||
if !validExtensionHeader(b[l:]) {
|
||||
if l == 128 {
|
||||
return nil, -1, errNoExtension
|
||||
}
|
||||
l = 128
|
||||
if !validExtensionHeader(b[l:]) {
|
||||
return nil, -1, errNoExtension
|
||||
}
|
||||
}
|
||||
}
|
||||
var exts []Extension
|
||||
for b = b[l+4:]; len(b) >= 4; {
|
||||
ol := int(binary.BigEndian.Uint16(b[:2]))
|
||||
if 4 > ol || ol > len(b) {
|
||||
break
|
||||
}
|
||||
switch b[2] {
|
||||
case classMPLSLabelStack:
|
||||
ext, err := parseMPLSLabelStack(b[:ol])
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
exts = append(exts, ext)
|
||||
case classInterfaceInfo:
|
||||
ext, err := parseInterfaceInfo(b[:ol])
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
exts = append(exts, ext)
|
||||
case classInterfaceIdent:
|
||||
ext, err := parseInterfaceIdent(b[:ol])
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
exts = append(exts, ext)
|
||||
default:
|
||||
ext := &RawExtension{Data: make([]byte, ol)}
|
||||
copy(ext.Data, b[:ol])
|
||||
exts = append(exts, ext)
|
||||
}
|
||||
b = b[ol:]
|
||||
}
|
||||
return exts, l, nil
|
||||
}
|
||||
|
||||
func validExtensions(typ Type, exts []Extension) bool {
|
||||
switch typ {
|
||||
case ipv4.ICMPTypeDestinationUnreachable, ipv4.ICMPTypeTimeExceeded, ipv4.ICMPTypeParameterProblem,
|
||||
ipv6.ICMPTypeDestinationUnreachable, ipv6.ICMPTypeTimeExceeded:
|
||||
for i := range exts {
|
||||
switch exts[i].(type) {
|
||||
case *MPLSLabelStack, *InterfaceInfo, *RawExtension:
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case ipv4.ICMPTypeExtendedEchoRequest, ipv6.ICMPTypeExtendedEchoRequest:
|
||||
var n int
|
||||
for i := range exts {
|
||||
switch exts[i].(type) {
|
||||
case *InterfaceIdent:
|
||||
n++
|
||||
case *RawExtension:
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Not a single InterfaceIdent object or a combo of
|
||||
// RawExtension and InterfaceIdent objects is not
|
||||
// allowed.
|
||||
if n == 1 && len(exts) > 1 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// A RawExtension represents a raw extension.
|
||||
//
|
||||
// A raw extension is excluded from message processing and can be used
|
||||
// to construct applications such as protocol conformance testing.
|
||||
type RawExtension struct {
|
||||
Data []byte // data
|
||||
}
|
||||
|
||||
// Len implements the Len method of Extension interface.
|
||||
func (p *RawExtension) Len(proto int) int {
|
||||
if p == nil {
|
||||
return 0
|
||||
}
|
||||
return len(p.Data)
|
||||
}
|
||||
|
||||
// Marshal implements the Marshal method of Extension interface.
|
||||
func (p *RawExtension) Marshal(proto int) ([]byte, error) {
|
||||
return p.Data, nil
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
// Copyright 2014 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.
|
||||
|
||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows
|
||||
|
||||
package icmp
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func sockaddr(family int, address string) (syscall.Sockaddr, error) {
|
||||
switch family {
|
||||
case syscall.AF_INET:
|
||||
a, err := net.ResolveIPAddr("ip4", address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(a.IP) == 0 {
|
||||
a.IP = net.IPv4zero
|
||||
}
|
||||
if a.IP = a.IP.To4(); a.IP == nil {
|
||||
return nil, net.InvalidAddrError("non-ipv4 address")
|
||||
}
|
||||
sa := &syscall.SockaddrInet4{}
|
||||
copy(sa.Addr[:], a.IP)
|
||||
return sa, nil
|
||||
case syscall.AF_INET6:
|
||||
a, err := net.ResolveIPAddr("ip6", address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(a.IP) == 0 {
|
||||
a.IP = net.IPv6unspecified
|
||||
}
|
||||
if a.IP.Equal(net.IPv4zero) {
|
||||
a.IP = net.IPv6unspecified
|
||||
}
|
||||
if a.IP = a.IP.To16(); a.IP == nil || a.IP.To4() != nil {
|
||||
return nil, net.InvalidAddrError("non-ipv6 address")
|
||||
}
|
||||
sa := &syscall.SockaddrInet6{ZoneId: zoneToUint32(a.Zone)}
|
||||
copy(sa.Addr[:], a.IP)
|
||||
return sa, nil
|
||||
default:
|
||||
return nil, net.InvalidAddrError("unexpected family")
|
||||
}
|
||||
}
|
||||
|
||||
func zoneToUint32(zone string) uint32 {
|
||||
if zone == "" {
|
||||
return 0
|
||||
}
|
||||
if ifi, err := net.InterfaceByName(zone); err == nil {
|
||||
return uint32(ifi.Index)
|
||||
}
|
||||
n, err := strconv.Atoi(zone)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return uint32(n)
|
||||
}
|
||||
|
||||
func last(s string, b byte) int {
|
||||
i := len(s)
|
||||
for i--; i >= 0; i-- {
|
||||
if s[i] == b {
|
||||
break
|
||||
}
|
||||
}
|
||||
return i
|
||||
}
|
||||
+322
@@ -0,0 +1,322 @@
|
||||
// Copyright 2015 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
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/internal/iana"
|
||||
)
|
||||
|
||||
const (
|
||||
classInterfaceInfo = 2
|
||||
)
|
||||
|
||||
const (
|
||||
attrMTU = 1 << iota
|
||||
attrName
|
||||
attrIPAddr
|
||||
attrIfIndex
|
||||
)
|
||||
|
||||
// An InterfaceInfo represents interface and next-hop identification.
|
||||
type InterfaceInfo struct {
|
||||
Class int // extension object class number
|
||||
Type int // extension object sub-type
|
||||
Interface *net.Interface
|
||||
Addr *net.IPAddr
|
||||
}
|
||||
|
||||
func (ifi *InterfaceInfo) nameLen() int {
|
||||
if len(ifi.Interface.Name) > 63 {
|
||||
return 64
|
||||
}
|
||||
l := 1 + len(ifi.Interface.Name)
|
||||
return (l + 3) &^ 3
|
||||
}
|
||||
|
||||
func (ifi *InterfaceInfo) attrsAndLen(proto int) (attrs, l int) {
|
||||
l = 4
|
||||
if ifi.Interface != nil && ifi.Interface.Index > 0 {
|
||||
attrs |= attrIfIndex
|
||||
l += 4
|
||||
if len(ifi.Interface.Name) > 0 {
|
||||
attrs |= attrName
|
||||
l += ifi.nameLen()
|
||||
}
|
||||
if ifi.Interface.MTU > 0 {
|
||||
attrs |= attrMTU
|
||||
l += 4
|
||||
}
|
||||
}
|
||||
if ifi.Addr != nil {
|
||||
switch proto {
|
||||
case iana.ProtocolICMP:
|
||||
if ifi.Addr.IP.To4() != nil {
|
||||
attrs |= attrIPAddr
|
||||
l += 4 + net.IPv4len
|
||||
}
|
||||
case iana.ProtocolIPv6ICMP:
|
||||
if ifi.Addr.IP.To16() != nil && ifi.Addr.IP.To4() == nil {
|
||||
attrs |= attrIPAddr
|
||||
l += 4 + net.IPv6len
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Len implements the Len method of Extension interface.
|
||||
func (ifi *InterfaceInfo) Len(proto int) int {
|
||||
_, l := ifi.attrsAndLen(proto)
|
||||
return l
|
||||
}
|
||||
|
||||
// Marshal implements the Marshal method of Extension interface.
|
||||
func (ifi *InterfaceInfo) Marshal(proto int) ([]byte, error) {
|
||||
attrs, l := ifi.attrsAndLen(proto)
|
||||
b := make([]byte, l)
|
||||
if err := ifi.marshal(proto, b, attrs, l); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (ifi *InterfaceInfo) marshal(proto int, b []byte, attrs, l int) error {
|
||||
binary.BigEndian.PutUint16(b[:2], uint16(l))
|
||||
b[2], b[3] = classInterfaceInfo, byte(ifi.Type)
|
||||
for b = b[4:]; len(b) > 0 && attrs != 0; {
|
||||
switch {
|
||||
case attrs&attrIfIndex != 0:
|
||||
b = ifi.marshalIfIndex(proto, b)
|
||||
attrs &^= attrIfIndex
|
||||
case attrs&attrIPAddr != 0:
|
||||
b = ifi.marshalIPAddr(proto, b)
|
||||
attrs &^= attrIPAddr
|
||||
case attrs&attrName != 0:
|
||||
b = ifi.marshalName(proto, b)
|
||||
attrs &^= attrName
|
||||
case attrs&attrMTU != 0:
|
||||
b = ifi.marshalMTU(proto, b)
|
||||
attrs &^= attrMTU
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ifi *InterfaceInfo) marshalIfIndex(proto int, b []byte) []byte {
|
||||
binary.BigEndian.PutUint32(b[:4], uint32(ifi.Interface.Index))
|
||||
return b[4:]
|
||||
}
|
||||
|
||||
func (ifi *InterfaceInfo) parseIfIndex(b []byte) ([]byte, error) {
|
||||
if len(b) < 4 {
|
||||
return nil, errMessageTooShort
|
||||
}
|
||||
ifi.Interface.Index = int(binary.BigEndian.Uint32(b[:4]))
|
||||
return b[4:], nil
|
||||
}
|
||||
|
||||
func (ifi *InterfaceInfo) marshalIPAddr(proto int, b []byte) []byte {
|
||||
switch proto {
|
||||
case iana.ProtocolICMP:
|
||||
binary.BigEndian.PutUint16(b[:2], uint16(iana.AddrFamilyIPv4))
|
||||
copy(b[4:4+net.IPv4len], ifi.Addr.IP.To4())
|
||||
b = b[4+net.IPv4len:]
|
||||
case iana.ProtocolIPv6ICMP:
|
||||
binary.BigEndian.PutUint16(b[:2], uint16(iana.AddrFamilyIPv6))
|
||||
copy(b[4:4+net.IPv6len], ifi.Addr.IP.To16())
|
||||
b = b[4+net.IPv6len:]
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (ifi *InterfaceInfo) parseIPAddr(b []byte) ([]byte, error) {
|
||||
if len(b) < 4 {
|
||||
return nil, errMessageTooShort
|
||||
}
|
||||
afi := int(binary.BigEndian.Uint16(b[:2]))
|
||||
b = b[4:]
|
||||
switch afi {
|
||||
case iana.AddrFamilyIPv4:
|
||||
if len(b) < net.IPv4len {
|
||||
return nil, errMessageTooShort
|
||||
}
|
||||
ifi.Addr.IP = make(net.IP, net.IPv4len)
|
||||
copy(ifi.Addr.IP, b[:net.IPv4len])
|
||||
b = b[net.IPv4len:]
|
||||
case iana.AddrFamilyIPv6:
|
||||
if len(b) < net.IPv6len {
|
||||
return nil, errMessageTooShort
|
||||
}
|
||||
ifi.Addr.IP = make(net.IP, net.IPv6len)
|
||||
copy(ifi.Addr.IP, b[:net.IPv6len])
|
||||
b = b[net.IPv6len:]
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (ifi *InterfaceInfo) marshalName(proto int, b []byte) []byte {
|
||||
l := byte(ifi.nameLen())
|
||||
b[0] = l
|
||||
copy(b[1:], []byte(ifi.Interface.Name))
|
||||
return b[l:]
|
||||
}
|
||||
|
||||
func (ifi *InterfaceInfo) parseName(b []byte) ([]byte, error) {
|
||||
if 4 > len(b) || len(b) < int(b[0]) {
|
||||
return nil, errMessageTooShort
|
||||
}
|
||||
l := int(b[0])
|
||||
if l%4 != 0 || 4 > l || l > 64 {
|
||||
return nil, errInvalidExtension
|
||||
}
|
||||
var name [63]byte
|
||||
copy(name[:], b[1:l])
|
||||
ifi.Interface.Name = strings.Trim(string(name[:]), "\000")
|
||||
return b[l:], nil
|
||||
}
|
||||
|
||||
func (ifi *InterfaceInfo) marshalMTU(proto int, b []byte) []byte {
|
||||
binary.BigEndian.PutUint32(b[:4], uint32(ifi.Interface.MTU))
|
||||
return b[4:]
|
||||
}
|
||||
|
||||
func (ifi *InterfaceInfo) parseMTU(b []byte) ([]byte, error) {
|
||||
if len(b) < 4 {
|
||||
return nil, errMessageTooShort
|
||||
}
|
||||
ifi.Interface.MTU = int(binary.BigEndian.Uint32(b[:4]))
|
||||
return b[4:], nil
|
||||
}
|
||||
|
||||
func parseInterfaceInfo(b []byte) (Extension, error) {
|
||||
ifi := &InterfaceInfo{
|
||||
Class: int(b[2]),
|
||||
Type: int(b[3]),
|
||||
}
|
||||
if ifi.Type&(attrIfIndex|attrName|attrMTU) != 0 {
|
||||
ifi.Interface = &net.Interface{}
|
||||
}
|
||||
if ifi.Type&attrIPAddr != 0 {
|
||||
ifi.Addr = &net.IPAddr{}
|
||||
}
|
||||
attrs := ifi.Type & (attrIfIndex | attrIPAddr | attrName | attrMTU)
|
||||
for b = b[4:]; len(b) > 0 && attrs != 0; {
|
||||
var err error
|
||||
switch {
|
||||
case attrs&attrIfIndex != 0:
|
||||
b, err = ifi.parseIfIndex(b)
|
||||
attrs &^= attrIfIndex
|
||||
case attrs&attrIPAddr != 0:
|
||||
b, err = ifi.parseIPAddr(b)
|
||||
attrs &^= attrIPAddr
|
||||
case attrs&attrName != 0:
|
||||
b, err = ifi.parseName(b)
|
||||
attrs &^= attrName
|
||||
case attrs&attrMTU != 0:
|
||||
b, err = ifi.parseMTU(b)
|
||||
attrs &^= attrMTU
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if ifi.Interface != nil && ifi.Interface.Name != "" && ifi.Addr != nil && ifi.Addr.IP.To16() != nil && ifi.Addr.IP.To4() == nil {
|
||||
ifi.Addr.Zone = ifi.Interface.Name
|
||||
}
|
||||
return ifi, nil
|
||||
}
|
||||
|
||||
const (
|
||||
classInterfaceIdent = 3
|
||||
typeInterfaceByName = 1
|
||||
typeInterfaceByIndex = 2
|
||||
typeInterfaceByAddress = 3
|
||||
)
|
||||
|
||||
// An InterfaceIdent represents interface identification.
|
||||
type InterfaceIdent struct {
|
||||
Class int // extension object class number
|
||||
Type int // extension object sub-type
|
||||
Name string // interface name
|
||||
Index int // interface index
|
||||
AFI int // address family identifier; see address family numbers in IANA registry
|
||||
Addr []byte // address
|
||||
}
|
||||
|
||||
// Len implements the Len method of Extension interface.
|
||||
func (ifi *InterfaceIdent) Len(_ int) int {
|
||||
switch ifi.Type {
|
||||
case typeInterfaceByName:
|
||||
l := len(ifi.Name)
|
||||
if l > 255 {
|
||||
l = 255
|
||||
}
|
||||
return 4 + (l+3)&^3
|
||||
case typeInterfaceByIndex:
|
||||
return 4 + 4
|
||||
case typeInterfaceByAddress:
|
||||
return 4 + 4 + (len(ifi.Addr)+3)&^3
|
||||
default:
|
||||
return 4
|
||||
}
|
||||
}
|
||||
|
||||
// Marshal implements the Marshal method of Extension interface.
|
||||
func (ifi *InterfaceIdent) Marshal(proto int) ([]byte, error) {
|
||||
b := make([]byte, ifi.Len(proto))
|
||||
if err := ifi.marshal(proto, b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (ifi *InterfaceIdent) marshal(proto int, b []byte) error {
|
||||
l := ifi.Len(proto)
|
||||
binary.BigEndian.PutUint16(b[:2], uint16(l))
|
||||
b[2], b[3] = classInterfaceIdent, byte(ifi.Type)
|
||||
switch ifi.Type {
|
||||
case typeInterfaceByName:
|
||||
copy(b[4:], ifi.Name)
|
||||
case typeInterfaceByIndex:
|
||||
binary.BigEndian.PutUint32(b[4:4+4], uint32(ifi.Index))
|
||||
case typeInterfaceByAddress:
|
||||
binary.BigEndian.PutUint16(b[4:4+2], uint16(ifi.AFI))
|
||||
b[4+2] = byte(len(ifi.Addr))
|
||||
copy(b[4+4:], ifi.Addr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseInterfaceIdent(b []byte) (Extension, error) {
|
||||
ifi := &InterfaceIdent{
|
||||
Class: int(b[2]),
|
||||
Type: int(b[3]),
|
||||
}
|
||||
switch ifi.Type {
|
||||
case typeInterfaceByName:
|
||||
ifi.Name = strings.Trim(string(b[4:]), "\x00")
|
||||
case typeInterfaceByIndex:
|
||||
if len(b[4:]) < 4 {
|
||||
return nil, errInvalidExtension
|
||||
}
|
||||
ifi.Index = int(binary.BigEndian.Uint32(b[4 : 4+4]))
|
||||
case typeInterfaceByAddress:
|
||||
if len(b[4:]) < 4 {
|
||||
return nil, errInvalidExtension
|
||||
}
|
||||
ifi.AFI = int(binary.BigEndian.Uint16(b[4 : 4+2]))
|
||||
l := int(b[4+2])
|
||||
if len(b[4+4:]) < l {
|
||||
return nil, errInvalidExtension
|
||||
}
|
||||
ifi.Addr = make([]byte, l)
|
||||
copy(ifi.Addr, b[4+4:])
|
||||
}
|
||||
return ifi, nil
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
// Copyright 2014 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
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"net"
|
||||
"runtime"
|
||||
|
||||
"golang.org/x/net/ipv4"
|
||||
)
|
||||
|
||||
// freebsdVersion is set in sys_freebsd.go.
|
||||
// See http://www.freebsd.org/doc/en/books/porters-handbook/freebsd-versions.html.
|
||||
var freebsdVersion uint32
|
||||
|
||||
// ParseIPv4Header returns the IPv4 header of the IPv4 packet that
|
||||
// triggered an ICMP error message.
|
||||
// This is found in the Data field of the ICMP error message body.
|
||||
//
|
||||
// The provided b must be in the format used by a raw ICMP socket on
|
||||
// the local system.
|
||||
// This may differ from the wire format, and the format used by a raw
|
||||
// IP socket, depending on the system.
|
||||
//
|
||||
// To parse an IPv6 header, use ipv6.ParseHeader.
|
||||
func ParseIPv4Header(b []byte) (*ipv4.Header, error) {
|
||||
if len(b) < ipv4.HeaderLen {
|
||||
return nil, errHeaderTooShort
|
||||
}
|
||||
hdrlen := int(b[0]&0x0f) << 2
|
||||
if hdrlen > len(b) {
|
||||
return nil, errBufferTooShort
|
||||
}
|
||||
h := &ipv4.Header{
|
||||
Version: int(b[0] >> 4),
|
||||
Len: hdrlen,
|
||||
TOS: int(b[1]),
|
||||
ID: int(binary.BigEndian.Uint16(b[4:6])),
|
||||
FragOff: int(binary.BigEndian.Uint16(b[6:8])),
|
||||
TTL: int(b[8]),
|
||||
Protocol: int(b[9]),
|
||||
Checksum: int(binary.BigEndian.Uint16(b[10:12])),
|
||||
Src: net.IPv4(b[12], b[13], b[14], b[15]),
|
||||
Dst: net.IPv4(b[16], b[17], b[18], b[19]),
|
||||
}
|
||||
switch runtime.GOOS {
|
||||
case "darwin", "ios":
|
||||
h.TotalLen = int(binary.NativeEndian.Uint16(b[2:4]))
|
||||
case "freebsd":
|
||||
if freebsdVersion >= 1000000 {
|
||||
h.TotalLen = int(binary.BigEndian.Uint16(b[2:4]))
|
||||
} else {
|
||||
h.TotalLen = int(binary.NativeEndian.Uint16(b[2:4]))
|
||||
}
|
||||
default:
|
||||
h.TotalLen = int(binary.BigEndian.Uint16(b[2:4]))
|
||||
}
|
||||
h.Flags = ipv4.HeaderFlags(h.FragOff&0xe000) >> 13
|
||||
h.FragOff = h.FragOff & 0x1fff
|
||||
if hdrlen-ipv4.HeaderLen > 0 {
|
||||
h.Options = make([]byte, hdrlen-ipv4.HeaderLen)
|
||||
copy(h.Options, b[ipv4.HeaderLen:])
|
||||
}
|
||||
return h, nil
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// Copyright 2013 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
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"golang.org/x/net/internal/iana"
|
||||
)
|
||||
|
||||
const ipv6PseudoHeaderLen = 2*net.IPv6len + 8
|
||||
|
||||
// IPv6PseudoHeader returns an IPv6 pseudo header for checksum
|
||||
// calculation.
|
||||
func IPv6PseudoHeader(src, dst net.IP) []byte {
|
||||
b := make([]byte, ipv6PseudoHeaderLen)
|
||||
copy(b, src.To16())
|
||||
copy(b[net.IPv6len:], dst.To16())
|
||||
b[len(b)-1] = byte(iana.ProtocolIPv6ICMP)
|
||||
return b
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
// Copyright 2014 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.
|
||||
|
||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || windows
|
||||
|
||||
package icmp
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/net/internal/iana"
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
)
|
||||
|
||||
const sysIP_STRIPHDR = 0x17 // for now only darwin supports this option
|
||||
|
||||
// ListenPacket listens for incoming ICMP packets addressed to
|
||||
// address. See net.Dial for the syntax of address.
|
||||
//
|
||||
// For non-privileged datagram-oriented ICMP endpoints, network must
|
||||
// be "udp4" or "udp6". The endpoint allows to read, write a few
|
||||
// limited ICMP messages such as echo request and echo reply.
|
||||
// Currently only Darwin and Linux support this.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// ListenPacket("udp4", "192.168.0.1")
|
||||
// ListenPacket("udp4", "0.0.0.0")
|
||||
// ListenPacket("udp6", "fe80::1%en0")
|
||||
// ListenPacket("udp6", "::")
|
||||
//
|
||||
// For privileged raw ICMP endpoints, network must be "ip4" or "ip6"
|
||||
// followed by a colon and an ICMP protocol number or name.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// ListenPacket("ip4:icmp", "192.168.0.1")
|
||||
// ListenPacket("ip4:1", "0.0.0.0")
|
||||
// ListenPacket("ip6:ipv6-icmp", "fe80::1%en0")
|
||||
// ListenPacket("ip6:58", "::")
|
||||
func ListenPacket(network, address string) (*PacketConn, error) {
|
||||
var family, proto int
|
||||
switch network {
|
||||
case "udp4":
|
||||
family, proto = syscall.AF_INET, iana.ProtocolICMP
|
||||
case "udp6":
|
||||
family, proto = syscall.AF_INET6, iana.ProtocolIPv6ICMP
|
||||
default:
|
||||
i := last(network, ':')
|
||||
if i < 0 {
|
||||
i = len(network)
|
||||
}
|
||||
switch network[:i] {
|
||||
case "ip4":
|
||||
proto = iana.ProtocolICMP
|
||||
case "ip6":
|
||||
proto = iana.ProtocolIPv6ICMP
|
||||
}
|
||||
}
|
||||
var cerr error
|
||||
var c net.PacketConn
|
||||
switch family {
|
||||
case syscall.AF_INET, syscall.AF_INET6:
|
||||
s, err := syscall.Socket(family, syscall.SOCK_DGRAM, proto)
|
||||
if err != nil {
|
||||
return nil, os.NewSyscallError("socket", err)
|
||||
}
|
||||
if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && family == syscall.AF_INET {
|
||||
if err := syscall.SetsockoptInt(s, iana.ProtocolIP, sysIP_STRIPHDR, 1); err != nil {
|
||||
syscall.Close(s)
|
||||
return nil, os.NewSyscallError("setsockopt", err)
|
||||
}
|
||||
}
|
||||
sa, err := sockaddr(family, address)
|
||||
if err != nil {
|
||||
syscall.Close(s)
|
||||
return nil, err
|
||||
}
|
||||
if err := syscall.Bind(s, sa); err != nil {
|
||||
syscall.Close(s)
|
||||
return nil, os.NewSyscallError("bind", err)
|
||||
}
|
||||
f := os.NewFile(uintptr(s), "datagram-oriented icmp")
|
||||
c, cerr = net.FilePacketConn(f)
|
||||
f.Close()
|
||||
default:
|
||||
c, cerr = net.ListenPacket(network, address)
|
||||
}
|
||||
if cerr != nil {
|
||||
return nil, cerr
|
||||
}
|
||||
switch proto {
|
||||
case iana.ProtocolICMP:
|
||||
return &PacketConn{c: c, p4: ipv4.NewPacketConn(c)}, nil
|
||||
case iana.ProtocolIPv6ICMP:
|
||||
return &PacketConn{c: c, p6: ipv6.NewPacketConn(c)}, nil
|
||||
default:
|
||||
return &PacketConn{c: c}, nil
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
// Copyright 2014 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.
|
||||
|
||||
//go:build !aix && !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows
|
||||
|
||||
package icmp
|
||||
|
||||
// ListenPacket listens for incoming ICMP packets addressed to
|
||||
// address. See net.Dial for the syntax of address.
|
||||
//
|
||||
// For non-privileged datagram-oriented ICMP endpoints, network must
|
||||
// be "udp4" or "udp6". The endpoint allows to read, write a few
|
||||
// limited ICMP messages such as echo request and echo reply.
|
||||
// Currently only Darwin and Linux support this.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// ListenPacket("udp4", "192.168.0.1")
|
||||
// ListenPacket("udp4", "0.0.0.0")
|
||||
// ListenPacket("udp6", "fe80::1%en0")
|
||||
// ListenPacket("udp6", "::")
|
||||
//
|
||||
// For privileged raw ICMP endpoints, network must be "ip4" or "ip6"
|
||||
// followed by a colon and an ICMP protocol number or name.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// ListenPacket("ip4:icmp", "192.168.0.1")
|
||||
// ListenPacket("ip4:1", "0.0.0.0")
|
||||
// ListenPacket("ip6:ipv6-icmp", "fe80::1%en0")
|
||||
// ListenPacket("ip6:58", "::")
|
||||
func ListenPacket(network, address string) (*PacketConn, error) {
|
||||
return nil, errNotImplemented
|
||||
}
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
// 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
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
// 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
|
||||
|
||||
// A MessageBody represents an ICMP message body.
|
||||
type MessageBody interface {
|
||||
// Len returns the length of ICMP message body.
|
||||
// The provided proto must be either the ICMPv4 or ICMPv6
|
||||
// protocol number.
|
||||
Len(proto int) int
|
||||
|
||||
// Marshal returns the binary encoding of ICMP message body.
|
||||
// The provided proto must be either the ICMPv4 or ICMPv6
|
||||
// protocol number.
|
||||
Marshal(proto int) ([]byte, error)
|
||||
}
|
||||
|
||||
// A RawBody represents a raw message body.
|
||||
//
|
||||
// A raw message body is excluded from message processing and can be
|
||||
// used to construct applications such as protocol conformance
|
||||
// testing.
|
||||
type RawBody struct {
|
||||
Data []byte // data
|
||||
}
|
||||
|
||||
// Len implements the Len method of MessageBody interface.
|
||||
func (p *RawBody) Len(proto int) int {
|
||||
if p == nil {
|
||||
return 0
|
||||
}
|
||||
return len(p.Data)
|
||||
}
|
||||
|
||||
// Marshal implements the Marshal method of MessageBody interface.
|
||||
func (p *RawBody) Marshal(proto int) ([]byte, error) {
|
||||
return p.Data, nil
|
||||
}
|
||||
|
||||
// parseRawBody parses b as an ICMP message body.
|
||||
func parseRawBody(proto int, b []byte) (MessageBody, error) {
|
||||
p := &RawBody{Data: make([]byte, len(b))}
|
||||
copy(p.Data, b)
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// A DefaultMessageBody represents the default message body.
|
||||
//
|
||||
// Deprecated: Use RawBody instead.
|
||||
type DefaultMessageBody = RawBody
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
// Copyright 2015 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
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
// MPLSLabel represents an MPLS label stack entry.
|
||||
type MPLSLabel struct {
|
||||
Label int // label value
|
||||
TC int // traffic class; formerly experimental use
|
||||
S bool // bottom of stack
|
||||
TTL int // time to live
|
||||
}
|
||||
|
||||
const (
|
||||
classMPLSLabelStack = 1
|
||||
typeIncomingMPLSLabelStack = 1
|
||||
)
|
||||
|
||||
// MPLSLabelStack represents an MPLS label stack.
|
||||
type MPLSLabelStack struct {
|
||||
Class int // extension object class number
|
||||
Type int // extension object sub-type
|
||||
Labels []MPLSLabel
|
||||
}
|
||||
|
||||
// Len implements the Len method of Extension interface.
|
||||
func (ls *MPLSLabelStack) Len(proto int) int {
|
||||
return 4 + (4 * len(ls.Labels))
|
||||
}
|
||||
|
||||
// Marshal implements the Marshal method of Extension interface.
|
||||
func (ls *MPLSLabelStack) Marshal(proto int) ([]byte, error) {
|
||||
b := make([]byte, ls.Len(proto))
|
||||
if err := ls.marshal(proto, b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (ls *MPLSLabelStack) marshal(proto int, b []byte) error {
|
||||
l := ls.Len(proto)
|
||||
binary.BigEndian.PutUint16(b[:2], uint16(l))
|
||||
b[2], b[3] = classMPLSLabelStack, typeIncomingMPLSLabelStack
|
||||
off := 4
|
||||
for _, ll := range ls.Labels {
|
||||
b[off], b[off+1], b[off+2] = byte(ll.Label>>12), byte(ll.Label>>4&0xff), byte(ll.Label<<4&0xf0)
|
||||
b[off+2] |= byte(ll.TC << 1 & 0x0e)
|
||||
if ll.S {
|
||||
b[off+2] |= 0x1
|
||||
}
|
||||
b[off+3] = byte(ll.TTL)
|
||||
off += 4
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseMPLSLabelStack(b []byte) (Extension, error) {
|
||||
ls := &MPLSLabelStack{
|
||||
Class: int(b[2]),
|
||||
Type: int(b[3]),
|
||||
}
|
||||
for b = b[4:]; len(b) >= 4; b = b[4:] {
|
||||
ll := MPLSLabel{
|
||||
Label: int(b[0])<<12 | int(b[1])<<4 | int(b[2])>>4,
|
||||
TC: int(b[2]&0x0e) >> 1,
|
||||
TTL: int(b[3]),
|
||||
}
|
||||
if b[2]&0x1 != 0 {
|
||||
ll.S = true
|
||||
}
|
||||
ls.Labels = append(ls.Labels, ll)
|
||||
}
|
||||
return ls, nil
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
// Copyright 2015 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
|
||||
|
||||
import "golang.org/x/net/internal/iana"
|
||||
|
||||
// multipartMessageBodyDataLen takes b as an original datagram and
|
||||
// exts as extensions, and returns a required length for message body
|
||||
// and a required length for a padded original datagram in wire
|
||||
// format.
|
||||
func multipartMessageBodyDataLen(proto int, withOrigDgram bool, b []byte, exts []Extension) (bodyLen, dataLen int) {
|
||||
bodyLen = 4 // length of leading octets
|
||||
var extLen int
|
||||
var rawExt bool // raw extension may contain an empty object
|
||||
for _, ext := range exts {
|
||||
extLen += ext.Len(proto)
|
||||
if _, ok := ext.(*RawExtension); ok {
|
||||
rawExt = true
|
||||
}
|
||||
}
|
||||
if extLen > 0 && withOrigDgram {
|
||||
dataLen = multipartMessageOrigDatagramLen(proto, b)
|
||||
} else {
|
||||
dataLen = len(b)
|
||||
}
|
||||
if extLen > 0 || rawExt {
|
||||
bodyLen += 4 // length of extension header
|
||||
}
|
||||
bodyLen += dataLen + extLen
|
||||
return bodyLen, dataLen
|
||||
}
|
||||
|
||||
// multipartMessageOrigDatagramLen takes b as an original datagram,
|
||||
// and returns a required length for a padded original datagram in wire
|
||||
// format.
|
||||
func multipartMessageOrigDatagramLen(proto int, b []byte) int {
|
||||
roundup := func(b []byte, align int) int {
|
||||
// According to RFC 4884, the padded original datagram
|
||||
// field must contain at least 128 octets.
|
||||
if len(b) < 128 {
|
||||
return 128
|
||||
}
|
||||
r := len(b)
|
||||
return (r + align - 1) &^ (align - 1)
|
||||
}
|
||||
switch proto {
|
||||
case iana.ProtocolICMP:
|
||||
return roundup(b, 4)
|
||||
case iana.ProtocolIPv6ICMP:
|
||||
return roundup(b, 8)
|
||||
default:
|
||||
return len(b)
|
||||
}
|
||||
}
|
||||
|
||||
// marshalMultipartMessageBody takes data as an original datagram and
|
||||
// exts as extesnsions, and returns a binary encoding of message body.
|
||||
// It can be used for non-multipart message bodies when exts is nil.
|
||||
func marshalMultipartMessageBody(proto int, withOrigDgram bool, data []byte, exts []Extension) ([]byte, error) {
|
||||
bodyLen, dataLen := multipartMessageBodyDataLen(proto, withOrigDgram, data, exts)
|
||||
b := make([]byte, bodyLen)
|
||||
copy(b[4:], data)
|
||||
if len(exts) > 0 {
|
||||
b[4+dataLen] = byte(extensionVersion << 4)
|
||||
off := 4 + dataLen + 4 // leading octets, data, extension header
|
||||
for _, ext := range exts {
|
||||
switch ext := ext.(type) {
|
||||
case *MPLSLabelStack:
|
||||
if err := ext.marshal(proto, b[off:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
off += ext.Len(proto)
|
||||
case *InterfaceInfo:
|
||||
attrs, l := ext.attrsAndLen(proto)
|
||||
if err := ext.marshal(proto, b[off:], attrs, l); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
off += ext.Len(proto)
|
||||
case *InterfaceIdent:
|
||||
if err := ext.marshal(proto, b[off:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
off += ext.Len(proto)
|
||||
case *RawExtension:
|
||||
copy(b[off:], ext.Data)
|
||||
off += ext.Len(proto)
|
||||
}
|
||||
}
|
||||
s := checksum(b[4+dataLen:])
|
||||
b[4+dataLen+2] ^= byte(s)
|
||||
b[4+dataLen+3] ^= byte(s >> 8)
|
||||
if withOrigDgram {
|
||||
switch proto {
|
||||
case iana.ProtocolICMP:
|
||||
b[1] = byte(dataLen / 4)
|
||||
case iana.ProtocolIPv6ICMP:
|
||||
b[0] = byte(dataLen / 8)
|
||||
}
|
||||
}
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// parseMultipartMessageBody parses b as either a non-multipart
|
||||
// message body or a multipart message body.
|
||||
func parseMultipartMessageBody(proto int, typ Type, b []byte) ([]byte, []Extension, error) {
|
||||
var l int
|
||||
switch proto {
|
||||
case iana.ProtocolICMP:
|
||||
l = 4 * int(b[1])
|
||||
case iana.ProtocolIPv6ICMP:
|
||||
l = 8 * int(b[0])
|
||||
}
|
||||
if len(b) == 4 {
|
||||
return nil, nil, nil
|
||||
}
|
||||
exts, l, err := parseExtensions(typ, b[4:], l)
|
||||
if err != nil {
|
||||
l = len(b) - 4
|
||||
}
|
||||
var data []byte
|
||||
if l > 0 {
|
||||
data = make([]byte, l)
|
||||
copy(data, b[4:])
|
||||
}
|
||||
return data, exts, nil
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
// Copyright 2014 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
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
// A PacketTooBig represents an ICMP packet too big message body.
|
||||
type PacketTooBig struct {
|
||||
MTU int // maximum transmission unit of the nexthop link
|
||||
Data []byte // data, known as original datagram field
|
||||
}
|
||||
|
||||
// Len implements the Len method of MessageBody interface.
|
||||
func (p *PacketTooBig) Len(proto int) int {
|
||||
if p == nil {
|
||||
return 0
|
||||
}
|
||||
return 4 + len(p.Data)
|
||||
}
|
||||
|
||||
// Marshal implements the Marshal method of MessageBody interface.
|
||||
func (p *PacketTooBig) Marshal(proto int) ([]byte, error) {
|
||||
b := make([]byte, 4+len(p.Data))
|
||||
binary.BigEndian.PutUint32(b[:4], uint32(p.MTU))
|
||||
copy(b[4:], p.Data)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// parsePacketTooBig parses b as an ICMP packet too big message body.
|
||||
func parsePacketTooBig(proto int, _ Type, b []byte) (MessageBody, error) {
|
||||
bodyLen := len(b)
|
||||
if bodyLen < 4 {
|
||||
return nil, errMessageTooShort
|
||||
}
|
||||
p := &PacketTooBig{MTU: int(binary.BigEndian.Uint32(b[:4]))}
|
||||
if bodyLen > 4 {
|
||||
p.Data = make([]byte, bodyLen-4)
|
||||
copy(p.Data, b[4:])
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
// Copyright 2014 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
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"golang.org/x/net/internal/iana"
|
||||
"golang.org/x/net/ipv4"
|
||||
)
|
||||
|
||||
// A ParamProb represents an ICMP parameter problem message body.
|
||||
type ParamProb struct {
|
||||
Pointer uintptr // offset within the data where the error was detected
|
||||
Data []byte // data, known as original datagram field
|
||||
Extensions []Extension // extensions
|
||||
}
|
||||
|
||||
// Len implements the Len method of MessageBody interface.
|
||||
func (p *ParamProb) Len(proto int) int {
|
||||
if p == nil {
|
||||
return 0
|
||||
}
|
||||
l, _ := multipartMessageBodyDataLen(proto, true, p.Data, p.Extensions)
|
||||
return l
|
||||
}
|
||||
|
||||
// Marshal implements the Marshal method of MessageBody interface.
|
||||
func (p *ParamProb) Marshal(proto int) ([]byte, error) {
|
||||
switch proto {
|
||||
case iana.ProtocolICMP:
|
||||
if !validExtensions(ipv4.ICMPTypeParameterProblem, p.Extensions) {
|
||||
return nil, errInvalidExtension
|
||||
}
|
||||
b, err := marshalMultipartMessageBody(proto, true, p.Data, p.Extensions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b[0] = byte(p.Pointer)
|
||||
return b, nil
|
||||
case iana.ProtocolIPv6ICMP:
|
||||
b := make([]byte, p.Len(proto))
|
||||
binary.BigEndian.PutUint32(b[:4], uint32(p.Pointer))
|
||||
copy(b[4:], p.Data)
|
||||
return b, nil
|
||||
default:
|
||||
return nil, errInvalidProtocol
|
||||
}
|
||||
}
|
||||
|
||||
// parseParamProb parses b as an ICMP parameter problem message body.
|
||||
func parseParamProb(proto int, typ Type, b []byte) (MessageBody, error) {
|
||||
if len(b) < 4 {
|
||||
return nil, errMessageTooShort
|
||||
}
|
||||
p := &ParamProb{}
|
||||
if proto == iana.ProtocolIPv6ICMP {
|
||||
p.Pointer = uintptr(binary.BigEndian.Uint32(b[:4]))
|
||||
p.Data = make([]byte, len(b)-4)
|
||||
copy(p.Data, b[4:])
|
||||
return p, nil
|
||||
}
|
||||
p.Pointer = uintptr(b[0])
|
||||
var err error
|
||||
p.Data, p.Extensions, err = parseMultipartMessageBody(proto, typ, b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// Copyright 2014 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
|
||||
|
||||
import "syscall"
|
||||
|
||||
func init() {
|
||||
freebsdVersion, _ = syscall.SysctlUint32("kern.osreldate")
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
// Copyright 2014 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
|
||||
|
||||
import (
|
||||
"golang.org/x/net/internal/iana"
|
||||
"golang.org/x/net/ipv4"
|
||||
"golang.org/x/net/ipv6"
|
||||
)
|
||||
|
||||
// A TimeExceeded represents an ICMP time exceeded message body.
|
||||
type TimeExceeded struct {
|
||||
Data []byte // data, known as original datagram field
|
||||
Extensions []Extension // extensions
|
||||
}
|
||||
|
||||
// Len implements the Len method of MessageBody interface.
|
||||
func (p *TimeExceeded) Len(proto int) int {
|
||||
if p == nil {
|
||||
return 0
|
||||
}
|
||||
l, _ := multipartMessageBodyDataLen(proto, true, p.Data, p.Extensions)
|
||||
return l
|
||||
}
|
||||
|
||||
// Marshal implements the Marshal method of MessageBody interface.
|
||||
func (p *TimeExceeded) Marshal(proto int) ([]byte, error) {
|
||||
var typ Type
|
||||
switch proto {
|
||||
case iana.ProtocolICMP:
|
||||
typ = ipv4.ICMPTypeTimeExceeded
|
||||
case iana.ProtocolIPv6ICMP:
|
||||
typ = ipv6.ICMPTypeTimeExceeded
|
||||
default:
|
||||
return nil, errInvalidProtocol
|
||||
}
|
||||
if !validExtensions(typ, p.Extensions) {
|
||||
return nil, errInvalidExtension
|
||||
}
|
||||
return marshalMultipartMessageBody(proto, true, p.Data, p.Extensions)
|
||||
}
|
||||
|
||||
// parseTimeExceeded parses b as an ICMP time exceeded message body.
|
||||
func parseTimeExceeded(proto int, typ Type, b []byte) (MessageBody, error) {
|
||||
if len(b) < 4 {
|
||||
return nil, errMessageTooShort
|
||||
}
|
||||
p := &TimeExceeded{}
|
||||
var err error
|
||||
p.Data, p.Extensions, err = parseMultipartMessageBody(proto, typ, b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
Vendored
+1
@@ -175,6 +175,7 @@ golang.org/x/net/bpf
|
||||
golang.org/x/net/html
|
||||
golang.org/x/net/html/atom
|
||||
golang.org/x/net/html/charset
|
||||
golang.org/x/net/icmp
|
||||
golang.org/x/net/internal/iana
|
||||
golang.org/x/net/internal/socket
|
||||
golang.org/x/net/ipv4
|
||||
|
||||
Reference in New Issue
Block a user