mirror of
https://github.com/containers/gvisor-tap-vsock.git
synced 2026-04-23 00:27:09 +08:00
Merge pull request #597 from evidolob/add-icmp-forwarder
Add icmp forwarder
This commit is contained in:
@@ -2,3 +2,7 @@ bin/
|
||||
capture.pcap
|
||||
tmp/
|
||||
test/qcon.log
|
||||
.idea/
|
||||
.vscode/
|
||||
test-qemu/qcon.log
|
||||
test-qemu/ovmf_vars.fd
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module github.com/containers/gvisor-tap-vsock
|
||||
|
||||
go 1.25.0
|
||||
go 1.25.5
|
||||
|
||||
require (
|
||||
github.com/Microsoft/go-winio v0.6.2
|
||||
@@ -25,10 +25,11 @@ 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
|
||||
gvisor.dev/gvisor v0.0.0-20240916094835-a174eb65023f
|
||||
gvisor.dev/gvisor v0.0.0-20260413194555-9680d69bf798
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -44,9 +45,9 @@ require (
|
||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
|
||||
github.com/vishvananda/netns v0.0.5 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/net v0.52.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
|
||||
golang.org/x/text v0.36.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
golang.org/x/tools v0.43.0 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
)
|
||||
|
||||
@@ -112,6 +112,8 @@ golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf
|
||||
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
|
||||
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
|
||||
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
|
||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
|
||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
@@ -185,8 +187,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
|
||||
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
@@ -207,8 +209,8 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A=
|
||||
google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
|
||||
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
@@ -219,5 +221,5 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gvisor.dev/gvisor v0.0.0-20240916094835-a174eb65023f h1:O2w2DymsOlM/nv2pLNWCMCYOldgBBMkD7H0/prN5W2k=
|
||||
gvisor.dev/gvisor v0.0.0-20240916094835-a174eb65023f/go.mod h1:sxc3Uvk/vHcd3tj7/DHVBoR5wvWT/MmRq2pj7HRJnwU=
|
||||
gvisor.dev/gvisor v0.0.0-20260413194555-9680d69bf798 h1:7h21uRYtzPJjXp9W/BZLx68z8TwW8+h5BPdI4P5ZkJ0=
|
||||
gvisor.dev/gvisor v0.0.0-20260413194555-9680d69bf798/go.mod h1:xQ2PWgHmWJA/Ph4i1q1jBm39BKhc3W0DXqWoDSyuBOY=
|
||||
|
||||
@@ -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,242 @@
|
||||
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
|
||||
}
|
||||
|
||||
// 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 (on Linux, kernel uses socket port as echo ID)
|
||||
expectedIdent := getExpectedReplyIdent(conn, details.ident)
|
||||
if !validateEchoReply(echoReply, expectedIdent, details.seq) {
|
||||
return
|
||||
}
|
||||
|
||||
// Forward the reply back to the VM's network stack. Use the VM's original
|
||||
// ident/seq so the VM's ping process can match the reply to its request
|
||||
// (on Linux the host reply has kernel-assigned ID; we must rewrite to VM's).
|
||||
forwardEchoReply(s, r, details.srcAddr, destAddr, details.ident, details.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,63 @@
|
||||
//go:build !windows
|
||||
|
||||
package forwarder
|
||||
|
||||
import (
|
||||
"net"
|
||||
"runtime"
|
||||
"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
|
||||
}
|
||||
|
||||
// getExpectedReplyIdent returns the ICMP echo identifier to expect in the reply.
|
||||
// On Linux, the kernel overwrites the echo ID with the socket's local port for
|
||||
// unprivileged ICMP sockets, so we must use that for validation. On macOS the
|
||||
// kernel preserves the ID we send.
|
||||
func getExpectedReplyIdent(conn *netIcmp.PacketConn, sentIdent uint16) uint16 {
|
||||
if runtime.GOOS != "linux" {
|
||||
return sentIdent
|
||||
}
|
||||
addr := conn.LocalAddr()
|
||||
udpAddr, ok := addr.(*net.UDPAddr)
|
||||
if !ok || udpAddr == nil {
|
||||
return sentIdent
|
||||
}
|
||||
port := udpAddr.Port
|
||||
if port < 0 || port > 0xFFFF {
|
||||
return sentIdent
|
||||
}
|
||||
return uint16(port)
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
//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.
|
||||
// Some Windows versions return IPv4 with the IP header; others return ICMP only (e.g. echo
|
||||
// reply type 0 makes the first byte 0x00, which is not IPv4 version 4). If the buffer
|
||||
// starts with an IPv4 header, strip it; otherwise return the payload as-is.
|
||||
func extractICMPData(replyBytes []byte) ([]byte, error) {
|
||||
if len(replyBytes) == 0 {
|
||||
return nil, fmt.Errorf("reply packet empty")
|
||||
}
|
||||
version := (replyBytes[0] >> 4) & 0x0F
|
||||
if version != 4 {
|
||||
return replyBytes, nil
|
||||
}
|
||||
if len(replyBytes) < 20 {
|
||||
return nil, fmt.Errorf("reply packet too short for IPv4: %d bytes", len(replyBytes))
|
||||
}
|
||||
ihl := int(replyBytes[0]&0x0F) * 4
|
||||
if ihl < 20 || ihl > len(replyBytes) {
|
||||
return nil, fmt.Errorf("invalid IP header length: %d", ihl)
|
||||
}
|
||||
return replyBytes[ihl:], nil
|
||||
}
|
||||
|
||||
// getExpectedReplyIdent returns the ICMP echo identifier to expect in the reply.
|
||||
// On Windows (raw sockets) the kernel preserves the ID we send.
|
||||
func getExpectedReplyIdent(_ *netIcmp.PacketConn, sentIdent uint16) uint16 {
|
||||
return sentIdent
|
||||
}
|
||||
@@ -15,11 +15,11 @@ import (
|
||||
)
|
||||
|
||||
func UDP(s *stack.Stack, nat map[tcpip.Address]tcpip.Address, natLock *sync.Mutex) *udp.Forwarder {
|
||||
return udp.NewForwarder(s, func(r *udp.ForwarderRequest) {
|
||||
return udp.NewForwarder(s, func(r *udp.ForwarderRequest) bool {
|
||||
localAddress := r.ID().LocalAddress
|
||||
|
||||
if linkLocal().Contains(localAddress) || localAddress == header.IPv4Broadcast {
|
||||
return
|
||||
return true
|
||||
}
|
||||
|
||||
natLock.Lock()
|
||||
@@ -37,7 +37,7 @@ func UDP(s *stack.Stack, nat map[tcpip.Address]tcpip.Address, natLock *sync.Mute
|
||||
} else {
|
||||
log.Errorf("r.CreateEndpoint() = %v", tcpErr)
|
||||
}
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
p, _ := NewUDPProxy(&autoStoppingListener{underlying: gonet.NewUDPConn(&wq, ep)}, func() (net.Conn, error) {
|
||||
@@ -51,5 +51,6 @@ func UDP(s *stack.Stack, nat map[tcpip.Address]tcpip.Address, natLock *sync.Mute
|
||||
// forwarder request.
|
||||
ep.Close()
|
||||
}()
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
e2e "github.com/containers/gvisor-tap-vsock/test"
|
||||
"github.com/onsi/ginkgo"
|
||||
"github.com/onsi/gomega"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var _ = ginkgo.Describe("connectivity with qemu", func() {
|
||||
@@ -52,3 +53,34 @@ var _ = ginkgo.Describe("command-line format", func() {
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
var _ = ginkgo.Describe("ping with gvproxy", func() {
|
||||
ginkgo.It("should succeed to ping a known domain", func() {
|
||||
ginkgo.Skip("this test is not run in CI, because on Azure ICMP is blocked by default")
|
||||
out, err := sshExec("ping -w2 crc.dev")
|
||||
log.Infof("ping: %s", out)
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
})
|
||||
ginkgo.It("should succeed to ping a known IP", func() {
|
||||
ginkgo.Skip("this test is not run in CI, because on Azure ICMP is blocked by default")
|
||||
out, err := sshExec("ping -w2 1.1.1.1")
|
||||
log.Infof("ping: %s", out)
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
})
|
||||
|
||||
ginkgo.It("should fail to ping an unknown domain", func() {
|
||||
out, err := sshExec("ping -w2 unknown.crc.dev")
|
||||
log.Infof("ping: %s", out)
|
||||
gomega.Expect(err).To(gomega.HaveOccurred())
|
||||
})
|
||||
ginkgo.It("should fail to ping an unknown IP", func() {
|
||||
out, err := sshExec("ping -w2 7.7.7.7")
|
||||
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())
|
||||
})
|
||||
})
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
e2e "github.com/containers/gvisor-tap-vsock/test"
|
||||
"github.com/onsi/ginkgo"
|
||||
"github.com/onsi/gomega"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var _ = ginkgo.Describe("connectivity with vfkit", func() {
|
||||
@@ -102,3 +103,30 @@ var _ = ginkgo.Describe("upload and download with vfkit", func() {
|
||||
tmpDir = dlTmpDir
|
||||
})
|
||||
})
|
||||
var _ = ginkgo.Describe("ping with gvproxy and vfkit", func() {
|
||||
ginkgo.It("should succeed to ping a known domain", func() {
|
||||
out, err := sshExec("ping -w2 crc.dev")
|
||||
log.Infof("ping: %s", out)
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
})
|
||||
ginkgo.It("should fail to ping an unknown domain", func() {
|
||||
out, err := sshExec("ping -w2 unknown.crc.dev")
|
||||
log.Infof("ping: %s", out)
|
||||
gomega.Expect(err).To(gomega.HaveOccurred())
|
||||
})
|
||||
ginkgo.It("should succeed to ping a known IP", func() {
|
||||
out, err := sshExec("ping -w2 1.1.1.1")
|
||||
log.Infof("ping: %s", out)
|
||||
gomega.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
})
|
||||
ginkgo.It("should fail to ping an unknown IP", func() {
|
||||
out, err := sshExec("ping -w2 7.7.7.7")
|
||||
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())
|
||||
})
|
||||
})
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
// Copyright 2021 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 constraints defines a set of useful constraints to be used
|
||||
// with type parameters.
|
||||
package constraints
|
||||
|
||||
// Signed is a constraint that permits any signed integer type.
|
||||
// If future releases of Go add new predeclared signed integer types,
|
||||
// this constraint will be modified to include them.
|
||||
type Signed interface {
|
||||
~int | ~int8 | ~int16 | ~int32 | ~int64
|
||||
}
|
||||
|
||||
// Unsigned is a constraint that permits any unsigned integer type.
|
||||
// If future releases of Go add new predeclared unsigned integer types,
|
||||
// this constraint will be modified to include them.
|
||||
type Unsigned interface {
|
||||
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
|
||||
}
|
||||
|
||||
// Integer is a constraint that permits any integer type.
|
||||
// If future releases of Go add new predeclared integer types,
|
||||
// this constraint will be modified to include them.
|
||||
type Integer interface {
|
||||
Signed | Unsigned
|
||||
}
|
||||
|
||||
// Float is a constraint that permits any floating-point type.
|
||||
// If future releases of Go add new predeclared floating-point types,
|
||||
// this constraint will be modified to include them.
|
||||
type Float interface {
|
||||
~float32 | ~float64
|
||||
}
|
||||
|
||||
// Complex is a constraint that permits any complex numeric type.
|
||||
// If future releases of Go add new predeclared complex numeric types,
|
||||
// this constraint will be modified to include them.
|
||||
type Complex interface {
|
||||
~complex64 | ~complex128
|
||||
}
|
||||
|
||||
// Ordered is a constraint that permits any ordered type: any type
|
||||
// that supports the operators < <= >= >.
|
||||
// If future releases of Go add new ordered types,
|
||||
// this constraint will be modified to include them.
|
||||
type Ordered interface {
|
||||
Integer | Float | ~string
|
||||
}
|
||||
+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
|
||||
}
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
Copyright 2009 The Go Authors.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
@@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer.
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
* Neither the name of Google LLC nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
|
||||
+21
-24
@@ -85,7 +85,7 @@ func (lim *Limiter) Burst() int {
|
||||
// TokensAt returns the number of tokens available at time t.
|
||||
func (lim *Limiter) TokensAt(t time.Time) float64 {
|
||||
lim.mu.Lock()
|
||||
_, tokens := lim.advance(t) // does not mutate lim
|
||||
tokens := lim.advance(t) // does not mutate lim
|
||||
lim.mu.Unlock()
|
||||
return tokens
|
||||
}
|
||||
@@ -99,8 +99,9 @@ func (lim *Limiter) Tokens() float64 {
|
||||
// bursts of at most b tokens.
|
||||
func NewLimiter(r Limit, b int) *Limiter {
|
||||
return &Limiter{
|
||||
limit: r,
|
||||
burst: b,
|
||||
limit: r,
|
||||
burst: b,
|
||||
tokens: float64(b),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +186,7 @@ func (r *Reservation) CancelAt(t time.Time) {
|
||||
return
|
||||
}
|
||||
// advance time to now
|
||||
t, tokens := r.lim.advance(t)
|
||||
tokens := r.lim.advance(t)
|
||||
// calculate new number of tokens
|
||||
tokens += restoreTokens
|
||||
if burst := float64(r.lim.burst); tokens > burst {
|
||||
@@ -306,7 +307,7 @@ func (lim *Limiter) SetLimitAt(t time.Time, newLimit Limit) {
|
||||
lim.mu.Lock()
|
||||
defer lim.mu.Unlock()
|
||||
|
||||
t, tokens := lim.advance(t)
|
||||
tokens := lim.advance(t)
|
||||
|
||||
lim.last = t
|
||||
lim.tokens = tokens
|
||||
@@ -323,7 +324,7 @@ func (lim *Limiter) SetBurstAt(t time.Time, newBurst int) {
|
||||
lim.mu.Lock()
|
||||
defer lim.mu.Unlock()
|
||||
|
||||
t, tokens := lim.advance(t)
|
||||
tokens := lim.advance(t)
|
||||
|
||||
lim.last = t
|
||||
lim.tokens = tokens
|
||||
@@ -344,21 +345,9 @@ func (lim *Limiter) reserveN(t time.Time, n int, maxFutureReserve time.Duration)
|
||||
tokens: n,
|
||||
timeToAct: t,
|
||||
}
|
||||
} else if lim.limit == 0 {
|
||||
var ok bool
|
||||
if lim.burst >= n {
|
||||
ok = true
|
||||
lim.burst -= n
|
||||
}
|
||||
return Reservation{
|
||||
ok: ok,
|
||||
lim: lim,
|
||||
tokens: lim.burst,
|
||||
timeToAct: t,
|
||||
}
|
||||
}
|
||||
|
||||
t, tokens := lim.advance(t)
|
||||
tokens := lim.advance(t)
|
||||
|
||||
// Calculate the remaining number of tokens resulting from the request.
|
||||
tokens -= float64(n)
|
||||
@@ -391,10 +380,11 @@ func (lim *Limiter) reserveN(t time.Time, n int, maxFutureReserve time.Duration)
|
||||
return r
|
||||
}
|
||||
|
||||
// advance calculates and returns an updated state for lim resulting from the passage of time.
|
||||
// advance calculates and returns an updated number of tokens for lim
|
||||
// resulting from the passage of time.
|
||||
// lim is not changed.
|
||||
// advance requires that lim.mu is held.
|
||||
func (lim *Limiter) advance(t time.Time) (newT time.Time, newTokens float64) {
|
||||
func (lim *Limiter) advance(t time.Time) (newTokens float64) {
|
||||
last := lim.last
|
||||
if t.Before(last) {
|
||||
last = t
|
||||
@@ -407,7 +397,7 @@ func (lim *Limiter) advance(t time.Time) (newT time.Time, newTokens float64) {
|
||||
if burst := float64(lim.burst); tokens > burst {
|
||||
tokens = burst
|
||||
}
|
||||
return t, tokens
|
||||
return tokens
|
||||
}
|
||||
|
||||
// durationFromTokens is a unit conversion function from the number of tokens to the duration
|
||||
@@ -416,8 +406,15 @@ func (limit Limit) durationFromTokens(tokens float64) time.Duration {
|
||||
if limit <= 0 {
|
||||
return InfDuration
|
||||
}
|
||||
seconds := tokens / float64(limit)
|
||||
return time.Duration(float64(time.Second) * seconds)
|
||||
|
||||
duration := (tokens / float64(limit)) * float64(time.Second)
|
||||
|
||||
// Cap the duration to the maximum representable int64 value, to avoid overflow.
|
||||
if duration > float64(math.MaxInt64) {
|
||||
return InfDuration
|
||||
}
|
||||
|
||||
return time.Duration(duration)
|
||||
}
|
||||
|
||||
// tokensFromDuration is a unit conversion function from a time duration to the number of tokens
|
||||
|
||||
+3
-1
@@ -61,7 +61,9 @@ func (s *Sometimes) Do(f func()) {
|
||||
(s.Every > 0 && s.count%s.Every == 0) ||
|
||||
(s.Interval > 0 && time.Since(s.last) >= s.Interval) {
|
||||
f()
|
||||
s.last = time.Now()
|
||||
if s.Interval > 0 {
|
||||
s.last = time.Now()
|
||||
}
|
||||
}
|
||||
s.count++
|
||||
}
|
||||
|
||||
Vendored
+31
-1
@@ -221,4 +221,34 @@ Some files carry the following license, noted at the top of each file:
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
THE SOFTWARE.
|
||||
|
||||
------------------
|
||||
|
||||
Some files carry the "BSD" license, noted at the top of each file:
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of Google Inc. nor the names of its contributors may be
|
||||
used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
package bits
|
||||
|
||||
// IsOn returns true if *all* bits set in 'bits' are set in 'mask'.
|
||||
func IsOn32(mask, bits uint32) bool {
|
||||
return mask&bits == bits
|
||||
}
|
||||
|
||||
// IsAnyOn returns true if *any* bit set in 'bits' is set in 'mask'.
|
||||
func IsAnyOn32(mask, bits uint32) bool {
|
||||
return mask&bits != 0
|
||||
}
|
||||
|
||||
// Mask returns a T with all of the given bits set.
|
||||
func Mask32(is ...int) uint32 {
|
||||
ret := uint32(0)
|
||||
for _, i := range is {
|
||||
ret |= MaskOf32(i)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// MaskOf is like Mask, but sets only a single bit (more efficiently).
|
||||
func MaskOf32(i int) uint32 {
|
||||
return uint32(1) << uint32(i)
|
||||
}
|
||||
|
||||
// IsPowerOfTwo returns true if v is power of 2.
|
||||
func IsPowerOfTwo32(v uint32) bool {
|
||||
if v == 0 {
|
||||
return false
|
||||
}
|
||||
return v&(v-1) == 0
|
||||
}
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
package bits
|
||||
|
||||
// IsOn returns true if *all* bits set in 'bits' are set in 'mask'.
|
||||
func IsOn64(mask, bits uint64) bool {
|
||||
return mask&bits == bits
|
||||
}
|
||||
|
||||
// IsAnyOn returns true if *any* bit set in 'bits' is set in 'mask'.
|
||||
func IsAnyOn64(mask, bits uint64) bool {
|
||||
return mask&bits != 0
|
||||
}
|
||||
|
||||
// Mask returns a T with all of the given bits set.
|
||||
func Mask64(is ...int) uint64 {
|
||||
ret := uint64(0)
|
||||
for _, i := range is {
|
||||
ret |= MaskOf64(i)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// MaskOf is like Mask, but sets only a single bit (more efficiently).
|
||||
func MaskOf64(i int) uint64 {
|
||||
return uint64(1) << uint64(i)
|
||||
}
|
||||
|
||||
// IsPowerOfTwo returns true if v is power of 2.
|
||||
func IsPowerOfTwo64(v uint64) bool {
|
||||
if v == 0 {
|
||||
return false
|
||||
}
|
||||
return v&(v-1) == 0
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright 2018 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package bits
|
||||
|
||||
import "golang.org/x/exp/constraints"
|
||||
|
||||
// Non-atomic bit operations on integral types.
|
||||
|
||||
// IsOn returns true if *all* bits set in 'bits' are set in 'mask'.
|
||||
func IsOn[T constraints.Integer](mask, bits T) bool {
|
||||
return mask&bits == bits
|
||||
}
|
||||
|
||||
// IsAnyOn returns true if *any* bit set in 'bits' is set in 'mask'.
|
||||
func IsAnyOn[T constraints.Integer](mask, bits T) bool {
|
||||
return mask&bits != 0
|
||||
}
|
||||
|
||||
// Mask returns a T with all of the given bits set.
|
||||
func Mask[T constraints.Integer](is ...int) T {
|
||||
ret := T(0)
|
||||
for _, i := range is {
|
||||
ret |= MaskOf[T](i)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// MaskOf is like Mask, but sets only a single bit (more efficiently).
|
||||
func MaskOf[T constraints.Integer](i int) T {
|
||||
return T(1) << T(i)
|
||||
}
|
||||
|
||||
// IsPowerOfTwo returns true if v is power of 2.
|
||||
func IsPowerOfTwo[T constraints.Integer](v T) bool {
|
||||
if v == 0 {
|
||||
return false
|
||||
}
|
||||
return v&(v-1) == 0
|
||||
}
|
||||
+1
-1
@@ -32,6 +32,6 @@ func ForEachSetBit64(x uint64, f func(i int)) {
|
||||
for x != 0 {
|
||||
i := TrailingZeros64(x)
|
||||
f(i)
|
||||
x &^= MaskOf64(i)
|
||||
x &^= MaskOf[uint64](i)
|
||||
}
|
||||
}
|
||||
|
||||
+5
-2
@@ -318,8 +318,11 @@ func (b *Buffer) PullUp(offset, length int) (View, bool) {
|
||||
if x := curr.Intersect(tgt); x.Len() == tgt.Len() {
|
||||
// buf covers the whole requested target range.
|
||||
sub := x.Offset(-curr.begin)
|
||||
// Don't increment the reference count of the underlying chunk. Views
|
||||
// returned by PullUp are explicitly unowned and read only
|
||||
if v.sharesChunk() {
|
||||
old := v.chunk
|
||||
v.chunk = v.chunk.Clone()
|
||||
old.DecRef()
|
||||
}
|
||||
new := View{
|
||||
read: v.read + sub.begin,
|
||||
write: v.read + sub.end,
|
||||
|
||||
@@ -24,8 +24,8 @@ func (b *Buffer) beforeSave() {}
|
||||
// +checklocksignore
|
||||
func (b *Buffer) StateSave(stateSinkObject state.Sink) {
|
||||
b.beforeSave()
|
||||
var dataValue []byte
|
||||
dataValue = b.saveData()
|
||||
dataValue := b.saveData()
|
||||
_ = ([]byte)(dataValue)
|
||||
stateSinkObject.SaveValue(0, dataValue)
|
||||
stateSinkObject.Save(1, &b.size)
|
||||
}
|
||||
|
||||
+1
-3
@@ -15,12 +15,10 @@
|
||||
package buffer
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// BasePtr returns a pointer to the view's chunk.
|
||||
func (v *View) BasePtr() *byte {
|
||||
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&v.chunk.data))
|
||||
return (*byte)(unsafe.Pointer(hdr.Data))
|
||||
return unsafe.SliceData(v.chunk.data)
|
||||
}
|
||||
|
||||
+55
-27
@@ -43,6 +43,9 @@ type Blocker interface {
|
||||
// Interrupted notes whether this context is Interrupted.
|
||||
Interrupted() bool
|
||||
|
||||
// Killed returns true if this context is interrupted by a fatal signal.
|
||||
Killed() bool
|
||||
|
||||
// BlockOn blocks until one of the previously registered events occurs,
|
||||
// or some external interrupt (cancellation).
|
||||
//
|
||||
@@ -55,25 +58,32 @@ type Blocker interface {
|
||||
// is interrupted.
|
||||
Block(C <-chan struct{}) error
|
||||
|
||||
// BlockWithTimeout blocks until an event is received from C, the timeout
|
||||
// has elapsed (only if haveTimeout is true), or some external interrupt.
|
||||
//
|
||||
// It returns:
|
||||
// - The remaining timeout, which is guaranteed to be 0 if the timeout
|
||||
// expired, and is unspecified if haveTimeout is false.
|
||||
// - An error which if the timeout expired or if interrupted.
|
||||
BlockWithTimeout(C chan struct{}, haveTimeout bool, timeout time.Duration) (time.Duration, error)
|
||||
|
||||
// BlockWithTimeoutOn blocks until either the conditions of Block are
|
||||
// satisfied, or the timeout is hit. Note that deadlines are not supported
|
||||
// since the notion of "with respect to what clock" is not resolved.
|
||||
//
|
||||
// The return value is per BlockOn.
|
||||
// It returns:
|
||||
// - The remaining timeout, which is guaranteed to be 0 if the timeout
|
||||
// expired.
|
||||
// - Boolean as per BlockOn return value.
|
||||
BlockWithTimeoutOn(waiter.Waitable, waiter.EventMask, time.Duration) (time.Duration, bool)
|
||||
|
||||
// UninterruptibleSleepStart indicates the beginning of an uninterruptible
|
||||
// sleep state (equivalent to Linux's TASK_UNINTERRUPTIBLE). If deactivate
|
||||
// is true and the Context represents a Task, the Task's AddressSpace is
|
||||
// deactivated.
|
||||
UninterruptibleSleepStart(deactivate bool)
|
||||
// sleep state (equivalent to Linux's TASK_UNINTERRUPTIBLE).
|
||||
UninterruptibleSleepStart()
|
||||
|
||||
// UninterruptibleSleepFinish indicates the end of an uninterruptible sleep
|
||||
// state that was begun by a previous call to UninterruptibleSleepStart. If
|
||||
// activate is true and the Context represents a Task, the Task's
|
||||
// AddressSpace is activated. Normally activate is the same value as the
|
||||
// deactivate parameter passed to UninterruptibleSleepStart.
|
||||
UninterruptibleSleepFinish(activate bool)
|
||||
// state that was begun by a previous call to UninterruptibleSleepStart.
|
||||
UninterruptibleSleepFinish()
|
||||
}
|
||||
|
||||
// NoTask is an implementation of Blocker that does not block.
|
||||
@@ -91,7 +101,12 @@ func (nt *NoTask) Interrupt() {
|
||||
|
||||
// Interrupted implements Blocker.Interrupted.
|
||||
func (nt *NoTask) Interrupted() bool {
|
||||
return nt.cancel != nil && len(nt.cancel) > 0
|
||||
return len(nt.cancel) > 0
|
||||
}
|
||||
|
||||
// Killed implements Blocker.Killed.
|
||||
func (nt *NoTask) Killed() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Block implements Blocker.Block.
|
||||
@@ -123,34 +138,47 @@ func (nt *NoTask) BlockOn(w waiter.Waitable, mask waiter.EventMask) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// BlockWithTimeoutOn implements Blocker.BlockWithTimeoutOn.
|
||||
func (nt *NoTask) BlockWithTimeoutOn(w waiter.Waitable, mask waiter.EventMask, duration time.Duration) (time.Duration, bool) {
|
||||
// BlockWithTimeout implements Blocker.BlockWithTimeout.
|
||||
func (nt *NoTask) BlockWithTimeout(C chan struct{}, haveTimeout bool, timeout time.Duration) (time.Duration, error) {
|
||||
if !haveTimeout {
|
||||
return timeout, nt.Block(C)
|
||||
}
|
||||
|
||||
if nt.cancel == nil {
|
||||
nt.cancel = make(chan struct{}, 1)
|
||||
}
|
||||
e, ch := waiter.NewChannelEntry(mask)
|
||||
w.EventRegister(&e)
|
||||
defer w.EventUnregister(&e)
|
||||
start := time.Now() // In system time.
|
||||
t := time.AfterFunc(duration, func() { ch <- struct{}{} })
|
||||
remainingTimeout := func() time.Duration {
|
||||
rt := timeout - time.Since(start)
|
||||
if rt < 0 {
|
||||
rt = 0
|
||||
}
|
||||
return rt
|
||||
}
|
||||
select {
|
||||
case <-nt.cancel:
|
||||
return time.Since(start), false // Interrupted.
|
||||
case _, ok := <-ch:
|
||||
if ok && t.Stop() {
|
||||
// Timer never fired.
|
||||
return time.Since(start), ok
|
||||
}
|
||||
// Timer fired, remain is zero.
|
||||
return time.Duration(0), ok
|
||||
return remainingTimeout(), errors.New("interrupted system call") // Interrupted.
|
||||
case <-C:
|
||||
return remainingTimeout(), nil
|
||||
case <-time.After(timeout):
|
||||
return 0, errors.New("timeout expired")
|
||||
}
|
||||
}
|
||||
|
||||
// BlockWithTimeoutOn implements Blocker.BlockWithTimeoutOn.
|
||||
func (nt *NoTask) BlockWithTimeoutOn(w waiter.Waitable, mask waiter.EventMask, timeout time.Duration) (time.Duration, bool) {
|
||||
e, ch := waiter.NewChannelEntry(mask)
|
||||
w.EventRegister(&e)
|
||||
defer w.EventUnregister(&e)
|
||||
left, err := nt.BlockWithTimeout(ch, true, timeout)
|
||||
return left, err == nil
|
||||
}
|
||||
|
||||
// UninterruptibleSleepStart implmenents Blocker.UninterruptedSleepStart.
|
||||
func (*NoTask) UninterruptibleSleepStart(bool) {}
|
||||
func (*NoTask) UninterruptibleSleepStart() {}
|
||||
|
||||
// UninterruptibleSleepFinish implmenents Blocker.UninterruptibleSleepFinish.
|
||||
func (*NoTask) UninterruptibleSleepFinish(bool) {}
|
||||
func (*NoTask) UninterruptibleSleepFinish() {}
|
||||
|
||||
// Context represents a thread of execution (hereafter "goroutine" to reflect
|
||||
// Go idiosyncrasy). It carries state associated with the goroutine across API
|
||||
|
||||
+4
-3
@@ -233,9 +233,10 @@ func readHWCap(auxvFilepath string) (hwCap, error) {
|
||||
for i := 0; i < l; i++ {
|
||||
tag := binary.LittleEndian.Uint64(auxv[i*16:])
|
||||
val := binary.LittleEndian.Uint64(auxv[i*16+8:])
|
||||
if tag == _AT_HWCAP {
|
||||
switch tag {
|
||||
case _AT_HWCAP:
|
||||
c.hwCap1 = val
|
||||
} else if tag == _AT_HWCAP2 {
|
||||
case _AT_HWCAP2:
|
||||
c.hwCap2 = val
|
||||
}
|
||||
|
||||
@@ -249,7 +250,7 @@ func readHWCap(auxvFilepath string) (hwCap, error) {
|
||||
func initHWCap() {
|
||||
c, err := readHWCap("/proc/self/auxv")
|
||||
if err != nil {
|
||||
log.Warningf("cpuid HWCap not initialized: %w", err)
|
||||
log.Warningf("cpuid HWCap not initialized: %v", err)
|
||||
} else {
|
||||
hostFeatureSet.hwCap = c
|
||||
}
|
||||
|
||||
@@ -480,3 +480,17 @@ func (fs FeatureSet) archCheckHostCompatible(hfs FeatureSet) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AllowedHWCap1 returns the HWCAP1 bits that the guest is allowed to depend
|
||||
// on.
|
||||
func (fs FeatureSet) AllowedHWCap1() uint64 {
|
||||
// HWCAPS are not supported on amd64.
|
||||
return 0
|
||||
}
|
||||
|
||||
// AllowedHWCap2 returns the HWCAP2 bits that the guest is allowed to depend
|
||||
// on.
|
||||
func (fs FeatureSet) AllowedHWCap2() uint64 {
|
||||
// HWCAPS are not supported on amd64.
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// automatically generated by stateify.
|
||||
|
||||
//go:build amd64 && amd64 && amd64 && amd64
|
||||
// +build amd64,amd64,amd64,amd64
|
||||
//go:build amd64 && amd64 && amd64 && amd64 && amd64
|
||||
// +build amd64,amd64,amd64,amd64,amd64
|
||||
|
||||
package cpuid
|
||||
|
||||
@@ -27,8 +27,8 @@ func (fs *FeatureSet) beforeSave() {}
|
||||
// +checklocksignore
|
||||
func (fs *FeatureSet) StateSave(stateSinkObject state.Sink) {
|
||||
fs.beforeSave()
|
||||
var FunctionValue Static
|
||||
FunctionValue = fs.saveFunction()
|
||||
FunctionValue := fs.saveFunction()
|
||||
_ = (Static)(FunctionValue)
|
||||
stateSinkObject.SaveValue(0, FunctionValue)
|
||||
stateSinkObject.Save(1, &fs.hwCap)
|
||||
}
|
||||
|
||||
@@ -108,3 +108,47 @@ func (fs FeatureSet) WriteCPUInfoTo(cpu, numCPU uint, w io.Writer) {
|
||||
func (FeatureSet) archCheckHostCompatible(FeatureSet) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AllowedHWCap1 returns the HWCAP1 bits that the guest is allowed to depend
|
||||
// on.
|
||||
func (fs FeatureSet) AllowedHWCap1() uint64 {
|
||||
// Pick a set of safe HWCAPS to expose. These do not rely on cpu state
|
||||
// that gvisor does not restore after a context switch.
|
||||
allowed := HWCAP_AES |
|
||||
HWCAP_ASIMD |
|
||||
HWCAP_ASIMDDP |
|
||||
HWCAP_ASIMDFHM |
|
||||
HWCAP_ASIMDHP |
|
||||
HWCAP_ASIMDRDM |
|
||||
HWCAP_ATOMICS |
|
||||
HWCAP_CRC32 |
|
||||
HWCAP_DCPOP |
|
||||
HWCAP_DIT |
|
||||
HWCAP_EVTSTRM |
|
||||
HWCAP_FCMA |
|
||||
HWCAP_FLAGM |
|
||||
HWCAP_FP |
|
||||
HWCAP_FPHP |
|
||||
HWCAP_ILRCPC |
|
||||
HWCAP_JSCVT |
|
||||
HWCAP_LRCPC |
|
||||
HWCAP_PMULL |
|
||||
HWCAP_SHA1 |
|
||||
HWCAP_SHA2 |
|
||||
HWCAP_SHA3 |
|
||||
HWCAP_SHA512 |
|
||||
HWCAP_SM3 |
|
||||
HWCAP_SM4 |
|
||||
HWCAP_USCAT
|
||||
return fs.hwCap.hwCap1 & uint64(allowed)
|
||||
}
|
||||
|
||||
// AllowedHWCap2 returns the HWCAP2 bits that the guest is allowed to depend
|
||||
// on.
|
||||
func (fs FeatureSet) AllowedHWCap2() uint64 {
|
||||
// We don't expose anything here yet, but this could be expanded to
|
||||
// include features do not rely on cpu state that is not restored after
|
||||
// a context switch.
|
||||
allowed := 0
|
||||
return fs.hwCap.hwCap2 & uint64(allowed)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// automatically generated by stateify.
|
||||
|
||||
//go:build arm64 && arm64 && arm64
|
||||
// +build arm64,arm64,arm64
|
||||
//go:build arm64 && arm64 && arm64 && arm64
|
||||
// +build arm64,arm64,arm64,arm64
|
||||
|
||||
package cpuid
|
||||
|
||||
|
||||
+9
-11
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020 The gVisor Authors.
|
||||
// Copyright 2024 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -12,15 +12,13 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !go1.23
|
||||
//go:build amd64
|
||||
// +build amd64
|
||||
|
||||
#include "textflag.h"
|
||||
package cpuid
|
||||
|
||||
#define GOID_OFFSET 152 // +checkoffset runtime g.goid
|
||||
|
||||
// func goid() int64
|
||||
TEXT ·goid(SB),NOSPLIT|NOFRAME,$0-8
|
||||
MOVQ (TLS), R14
|
||||
MOVQ GOID_OFFSET(R14), R14
|
||||
MOVQ R14, ret+0(FP)
|
||||
RET
|
||||
// See arch/x86/include/uapi/asm/hwcap2.h
|
||||
const (
|
||||
HWCAP2_RING3MWAIT = 1 << 0
|
||||
HWCAP2_FSGSBASE = 1 << 1
|
||||
)
|
||||
@@ -0,0 +1,79 @@
|
||||
// Copyright 2024 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build arm64
|
||||
// +build arm64
|
||||
|
||||
package cpuid
|
||||
|
||||
// See arch/arm64/include/uapi/asm/hwcap.h
|
||||
const (
|
||||
// HWCAP flags for AT_HWCAP.
|
||||
HWCAP_FP = 1 << 0
|
||||
HWCAP_ASIMD = 1 << 1
|
||||
HWCAP_EVTSTRM = 1 << 2
|
||||
HWCAP_AES = 1 << 3
|
||||
HWCAP_PMULL = 1 << 4
|
||||
HWCAP_SHA1 = 1 << 5
|
||||
HWCAP_SHA2 = 1 << 6
|
||||
HWCAP_CRC32 = 1 << 7
|
||||
HWCAP_ATOMICS = 1 << 8
|
||||
HWCAP_FPHP = 1 << 9
|
||||
HWCAP_ASIMDHP = 1 << 10
|
||||
HWCAP_CPUID = 1 << 11
|
||||
HWCAP_ASIMDRDM = 1 << 12
|
||||
HWCAP_JSCVT = 1 << 13
|
||||
HWCAP_FCMA = 1 << 14
|
||||
HWCAP_LRCPC = 1 << 15
|
||||
HWCAP_DCPOP = 1 << 16
|
||||
HWCAP_SHA3 = 1 << 17
|
||||
HWCAP_SM3 = 1 << 18
|
||||
HWCAP_SM4 = 1 << 19
|
||||
HWCAP_ASIMDDP = 1 << 20
|
||||
HWCAP_SHA512 = 1 << 21
|
||||
HWCAP_SVE = 1 << 22
|
||||
HWCAP_ASIMDFHM = 1 << 23
|
||||
HWCAP_DIT = 1 << 24
|
||||
HWCAP_USCAT = 1 << 25
|
||||
HWCAP_ILRCPC = 1 << 26
|
||||
HWCAP_FLAGM = 1 << 27
|
||||
HWCAP_SSBS = 1 << 28
|
||||
HWCAP_SB = 1 << 29
|
||||
HWCAP_PACA = 1 << 30
|
||||
HWCAP_PACG = 1 << 31
|
||||
|
||||
// HWCAP2 flags for AT_HWCAP2.
|
||||
HWCAP2_DCPODP = 1 << 0
|
||||
HWCAP2_SVE2 = 1 << 1
|
||||
HWCAP2_SVEAES = 1 << 2
|
||||
HWCAP2_SVEPMULL = 1 << 3
|
||||
HWCAP2_SVEBITPERM = 1 << 4
|
||||
HWCAP2_SVESHA3 = 1 << 5
|
||||
HWCAP2_SVESM4 = 1 << 6
|
||||
HWCAP2_FLAGM2 = 1 << 7
|
||||
HWCAP2_FRINT = 1 << 8
|
||||
HWCAP2_SVEI8MM = 1 << 9
|
||||
HWCAP2_SVEF32MM = 1 << 10
|
||||
HWCAP2_SVEF64MM = 1 << 11
|
||||
HWCAP2_SVEBF16 = 1 << 12
|
||||
HWCAP2_I8MM = 1 << 13
|
||||
HWCAP2_BF16 = 1 << 14
|
||||
HWCAP2_DGH = 1 << 15
|
||||
HWCAP2_RNG = 1 << 16
|
||||
HWCAP2_BTI = 1 << 17
|
||||
HWCAP2_MTE = 1 << 18
|
||||
HWCAP2_ECV = 1 << 19
|
||||
HWCAP2_AFP = 1 << 20
|
||||
HWCAP2_RPRES = 1 << 21
|
||||
)
|
||||
+37
-14
@@ -18,9 +18,10 @@
|
||||
package cpuid
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"bufio"
|
||||
"bytes"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/log"
|
||||
)
|
||||
@@ -162,6 +163,23 @@ func (fs FeatureSet) query(fn cpuidFunction) (uint32, uint32, uint32, uint32) {
|
||||
return out.Eax, out.Ebx, out.Ecx, out.Edx
|
||||
}
|
||||
|
||||
// Intersect returns the intersection of features between self and allowedFeatures.
|
||||
func (fs FeatureSet) Intersect(allowedFeatures map[Feature]struct{}) (FeatureSet, error) {
|
||||
hs := fs.ToStatic()
|
||||
|
||||
// only keep features inside allowedFeatures.
|
||||
for f := range allFeatures {
|
||||
if fs.HasFeature(f) {
|
||||
if _, ok := allowedFeatures[f]; !ok {
|
||||
log.Infof("Removing CPU feature %v as it is not allowed.", f)
|
||||
hs.Remove(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hs.ToFeatureSet(), nil
|
||||
}
|
||||
|
||||
var hostFeatureSet FeatureSet
|
||||
|
||||
// HostFeatureSet returns a host CPUID.
|
||||
@@ -180,39 +198,44 @@ var (
|
||||
// filter installation. This value is used to create the fake /proc/cpuinfo
|
||||
// from a FeatureSet.
|
||||
func readMaxCPUFreq() {
|
||||
cpuinfob, err := ioutil.ReadFile("/proc/cpuinfo")
|
||||
cpuinfoFile, err := os.Open("/proc/cpuinfo")
|
||||
if err != nil {
|
||||
// Leave it as 0... the VDSO bails out in the same way.
|
||||
log.Warningf("Could not read /proc/cpuinfo: %v", err)
|
||||
log.Warningf("Could not open /proc/cpuinfo: %v", err)
|
||||
return
|
||||
}
|
||||
cpuinfo := string(cpuinfob)
|
||||
defer cpuinfoFile.Close()
|
||||
|
||||
// We get the value straight from host /proc/cpuinfo. On machines with
|
||||
// frequency scaling enabled, this will only get the current value
|
||||
// which will likely be inaccurate. This is fine on machines with
|
||||
// frequency scaling disabled.
|
||||
for _, line := range strings.Split(cpuinfo, "\n") {
|
||||
if strings.Contains(line, "cpu MHz") {
|
||||
splitMHz := strings.Split(line, ":")
|
||||
s := bufio.NewScanner(cpuinfoFile)
|
||||
for s.Scan() {
|
||||
line := s.Bytes()
|
||||
if bytes.Contains(line, []byte("cpu MHz")) {
|
||||
splitMHz := bytes.Split(line, []byte(":"))
|
||||
if len(splitMHz) < 2 {
|
||||
log.Warningf("Could not read /proc/cpuinfo: malformed cpu MHz line")
|
||||
log.Warningf("Could not parse /proc/cpuinfo: malformed cpu MHz line: %q", line)
|
||||
return
|
||||
}
|
||||
|
||||
// If there was a problem, leave cpuFreqMHz as 0.
|
||||
var err error
|
||||
cpuFreqMHz, err = strconv.ParseFloat(strings.TrimSpace(splitMHz[1]), 64)
|
||||
splitMHzStr := string(bytes.TrimSpace(splitMHz[1]))
|
||||
f64MHz, err := strconv.ParseFloat(splitMHzStr, 64)
|
||||
if err != nil {
|
||||
log.Warningf("Could not parse cpu MHz value %v: %v", splitMHz[1], err)
|
||||
cpuFreqMHz = 0
|
||||
log.Warningf("Could not parse cpu MHz value %q: %v", splitMHzStr, err)
|
||||
return
|
||||
}
|
||||
cpuFreqMHz = f64MHz
|
||||
return
|
||||
}
|
||||
}
|
||||
if err := s.Err(); err != nil {
|
||||
log.Warningf("Could not read /proc/cpuinfo: %v", err)
|
||||
return
|
||||
}
|
||||
log.Warningf("Could not parse /proc/cpuinfo, it is empty or does not contain cpu MHz")
|
||||
|
||||
}
|
||||
|
||||
// xgetbv reads an extended control register.
|
||||
|
||||
+10
-2
@@ -18,7 +18,8 @@
|
||||
package cpuid
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -41,6 +42,13 @@ func (fs FeatureSet) Fixed() FeatureSet {
|
||||
return fs
|
||||
}
|
||||
|
||||
// Intersect returns the intersection of features between self and allowedFeatures.
|
||||
//
|
||||
// Just return error as there is no ARM64 equivalent to cpuid.Static.Remove().
|
||||
func (fs FeatureSet) Intersect(allowedFeatures map[Feature]struct{}) (FeatureSet, error) {
|
||||
return FeatureSet{}, fmt.Errorf("FeatureSet intersection is not supported on ARM64")
|
||||
}
|
||||
|
||||
// Reads CPU information from host /proc/cpuinfo.
|
||||
//
|
||||
// Must run before syscall filter installation. This value is used to create
|
||||
@@ -51,7 +59,7 @@ func initCPUInfo() {
|
||||
// warn about them not existing.
|
||||
return
|
||||
}
|
||||
cpuinfob, err := ioutil.ReadFile("/proc/cpuinfo")
|
||||
cpuinfob, err := os.ReadFile("/proc/cpuinfo")
|
||||
if err != nil {
|
||||
// Leave everything at 0, nothing can be done.
|
||||
log.Warningf("Could not read /proc/cpuinfo: %v", err)
|
||||
|
||||
+5
-3
@@ -26,7 +26,9 @@ type Static map[In]Out
|
||||
|
||||
// Fixed converts the FeatureSet to a fixed set.
|
||||
func (fs FeatureSet) Fixed() FeatureSet {
|
||||
return fs.ToStatic().ToFeatureSet()
|
||||
sfs := fs.ToStatic().ToFeatureSet()
|
||||
sfs.hwCap = fs.hwCap
|
||||
return sfs
|
||||
}
|
||||
|
||||
// ToStatic converts a FeatureSet to a Static function.
|
||||
@@ -103,8 +105,8 @@ func (s Static) normalize() {
|
||||
if fs.HasFeature(X86FeatureXSAVE) {
|
||||
in := In{Eax: uint32(xSaveInfo)}
|
||||
out := s[in]
|
||||
out.Ecx = maxXsaveSize
|
||||
out.Ebx = xsaveSize
|
||||
out.Ecx = max(out.Ecx, maxXsaveSize)
|
||||
out.Ebx = max(out.Ebx, xsaveSize)
|
||||
s[in] = out
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !go1.23
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
#define GOID_OFFSET 152 // +checkoffset runtime g.goid
|
||||
|
||||
// func goid() int64
|
||||
TEXT ·goid(SB),NOSPLIT,$0-8
|
||||
MOVD g, R0 // g
|
||||
MOVD GOID_OFFSET(R0), R0
|
||||
MOVD R0, ret+0(FP)
|
||||
RET
|
||||
+2
-2
@@ -12,11 +12,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build go1.23
|
||||
//go:build amd64
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
#define GOID_OFFSET 160 // +checkoffset runtime g.goid
|
||||
#define GOID_OFFSET 152
|
||||
|
||||
// func goid() int64
|
||||
TEXT ·goid(SB),NOSPLIT|NOFRAME,$0-8
|
||||
+2
-2
@@ -12,11 +12,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build go1.23
|
||||
//go:build arm64
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
#define GOID_OFFSET 160 // +checkoffset runtime g.goid
|
||||
#define GOID_OFFSET 152
|
||||
|
||||
// func goid() int64
|
||||
TEXT ·goid(SB),NOSPLIT,$0-8
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
// Copyright 2025 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
)
|
||||
|
||||
// This file contains helper functions analogous to the Linux kernel's WARN*
|
||||
// macros. Should be used for non-fatal errors that should be treated as bugs
|
||||
// none the less.
|
||||
|
||||
const (
|
||||
warnFmtStr = "WARNING: BUG on %s:%d\n"
|
||||
warnUnknownLineStr = "WARNING: BUG on unknown line\n"
|
||||
catchAllMagic = "runtime.Caller failed"
|
||||
)
|
||||
|
||||
//go:noinline
|
||||
func reportBugErr(caller int, err error) {
|
||||
reportBug(caller+1, err.Error(), nil)
|
||||
}
|
||||
|
||||
func reportBug(caller int, msg string, vars []any) {
|
||||
var b strings.Builder
|
||||
if _, file, line, ok := runtime.Caller(caller); ok {
|
||||
fmt.Fprintf(&b, warnFmtStr, file, line)
|
||||
} else {
|
||||
b.WriteString(warnUnknownLineStr)
|
||||
}
|
||||
b.WriteByte('\n')
|
||||
if len(msg) > 0 {
|
||||
if len(vars) > 0 {
|
||||
fmt.Fprintf(&b, msg, vars...)
|
||||
} else {
|
||||
b.WriteString(msg)
|
||||
}
|
||||
b.WriteByte('\n')
|
||||
}
|
||||
TracebackAll(b.String())
|
||||
}
|
||||
|
||||
var (
|
||||
// warnedMu protects the variables below.
|
||||
warnedMu sync.Mutex
|
||||
// warnedSet is used to keep track of which WarnOnOnce calls have fired.
|
||||
warnedSet map[string]struct{}
|
||||
)
|
||||
|
||||
//go:noinline
|
||||
func reportBugErrOnce(caller int, err error) {
|
||||
reportBugOnce(caller+1, err.Error(), nil)
|
||||
}
|
||||
|
||||
func reportBugOnce(caller int, msg string, vars []any) {
|
||||
var b strings.Builder
|
||||
if _, file, line, ok := runtime.Caller(caller); ok {
|
||||
key := fmt.Sprintf("%s:%d", file, line)
|
||||
|
||||
warnedMu.Lock()
|
||||
defer warnedMu.Unlock()
|
||||
|
||||
if _, ok = warnedSet[key]; !ok {
|
||||
fmt.Fprintf(&b, warnFmtStr, file, line)
|
||||
b.WriteByte('\n')
|
||||
if len(msg) > 0 {
|
||||
if len(vars) > 0 {
|
||||
fmt.Fprintf(&b, msg, vars...)
|
||||
} else {
|
||||
b.WriteString(msg)
|
||||
}
|
||||
b.WriteByte('\n')
|
||||
}
|
||||
|
||||
TracebackAll(b.String())
|
||||
warnedSet[key] = struct{}{}
|
||||
}
|
||||
} else {
|
||||
warnedMu.Lock()
|
||||
defer warnedMu.Unlock()
|
||||
|
||||
// Use const string as a catch-all when runtime.Caller fails,
|
||||
// so as to avoid log-spam since that's the point of WARN_ONCE.
|
||||
if _, ok := warnedSet[catchAllMagic]; !ok {
|
||||
b.WriteString(warnUnknownLineStr)
|
||||
b.WriteByte('\n')
|
||||
if len(msg) > 0 {
|
||||
if len(vars) > 0 {
|
||||
fmt.Fprintf(&b, msg, vars...)
|
||||
} else {
|
||||
b.WriteString(msg)
|
||||
}
|
||||
b.WriteByte('\n')
|
||||
}
|
||||
|
||||
TracebackAll(b.String())
|
||||
warnedSet[catchAllMagic] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BugTraceback will report a bug with a traceback of all goroutines if the
|
||||
// error isn't nil. Use it for reporting abnormal bugs encountered at runtime
|
||||
// that should be fixed.
|
||||
//
|
||||
// Do not use this for bad user input. Errors reported by this function should
|
||||
// not be fatal.
|
||||
func BugTraceback(err error) {
|
||||
if err != nil {
|
||||
reportBugErr(2, err)
|
||||
}
|
||||
}
|
||||
|
||||
// BugTracebackf will report a bug with a traceback of all goroutines.
|
||||
// Use it for reporting abnormal bugs encountered at runtime that should be
|
||||
// fixed.
|
||||
//
|
||||
// Do not use this for bad user input. Errors reported by this function should
|
||||
// not be fatal.
|
||||
func BugTracebackf(s string, a ...any) {
|
||||
reportBug(2, s, a)
|
||||
}
|
||||
|
||||
// BugTracebackOnce will report a bug with a traceback of all goroutines if the
|
||||
// error isn't nil. Use it for reporting abnormal bugs encountered at runtime
|
||||
// that should be fixed. If called multiple time from same invocation, will only
|
||||
// print once.
|
||||
//
|
||||
// Do not use this for bad user input. Errors reported by this function should
|
||||
// not be fatal.
|
||||
func BugTracebackOnce(err error) {
|
||||
if err != nil {
|
||||
reportBugErrOnce(2, err)
|
||||
}
|
||||
}
|
||||
|
||||
// BugTracebackfOnce will report a bug with a traceback of all goroutines.
|
||||
// Use it for reporting abnormal bugs encountered at runtime that should be
|
||||
// fixed. If called multiple time from same invocation, will only print once.
|
||||
//
|
||||
// Do not use this for bad user input. Errors reported by this function should
|
||||
// not be fatal.
|
||||
func BugTracebackfOnce(s string, a ...any) {
|
||||
reportBugOnce(2, s, a)
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
// Copyright 2026 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// FileOpts contains options for creating a log file.
|
||||
type FileOpts interface {
|
||||
// Build constructs the log file path based on the given pattern.
|
||||
Build(logPattern string) string
|
||||
}
|
||||
|
||||
// DefaultFileOpts is the default implementation of FileOpts which supports no
|
||||
// variable substitution.
|
||||
type DefaultFileOpts struct{}
|
||||
|
||||
// Build implements FileOpts.Build.
|
||||
func (f *DefaultFileOpts) Build(logPattern string) string {
|
||||
return logPattern
|
||||
}
|
||||
|
||||
// OpenFile opens a log file using the specified flags. It uses `opts` to
|
||||
// construct the log file path based on the given `logPattern`.
|
||||
func OpenFile(logPattern string, flags int, opts FileOpts) (*os.File, error) {
|
||||
if len(logPattern) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Replace variables in the log pattern.
|
||||
logPath := opts.Build(logPattern)
|
||||
|
||||
// Create parent directory if it doesn't exist.
|
||||
dir := filepath.Dir(logPath)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return nil, fmt.Errorf("error creating dir %q: %v", dir, err)
|
||||
}
|
||||
|
||||
// Open file with the specified flags.
|
||||
f, err := os.OpenFile(logPath, flags, 0644)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error opening file %q: %v", logPath, err)
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
+3
-1
@@ -327,7 +327,7 @@ func Stacks(all bool) []byte {
|
||||
}
|
||||
|
||||
// stackRegexp matches one level within a stack trace.
|
||||
var stackRegexp = regexp.MustCompile("(?m)^\\S+\\(.*\\)$\\r?\\n^\\t\\S+:\\d+.*$\\r?\\n")
|
||||
var stackRegexp = regexp.MustCompile(`(?m)^\S+\(.*\)$\r?\n^\t\S+:\d+.*$\r?\n`)
|
||||
|
||||
// LocalStack returns the local goroutine stack, excluding the top N entries.
|
||||
// LocalStack's own entry is excluded by default and does not need to be counted in excludeTopN.
|
||||
@@ -396,4 +396,6 @@ func CopyStandardLogTo(l Level) error {
|
||||
func init() {
|
||||
// Store the initial value for the log.
|
||||
log.Store(&BasicLogger{Level: Info, Emitter: GoogleEmitter{&Writer{Next: os.Stderr}}})
|
||||
|
||||
warnedSet = make(map[string]struct{})
|
||||
}
|
||||
|
||||
+2
-2
@@ -162,7 +162,7 @@ func doLeakCheck() {
|
||||
skip = o.LeakCheckDisabled()
|
||||
}
|
||||
if skip {
|
||||
log.Debugf(obj.LeakMessage())
|
||||
log.Debugf("%s", obj.LeakMessage())
|
||||
continue
|
||||
}
|
||||
msg += obj.LeakMessage() + "\n"
|
||||
@@ -174,6 +174,6 @@ func doLeakCheck() {
|
||||
if leakCheckPanicEnabled() {
|
||||
panic(msg)
|
||||
}
|
||||
log.Warningf(msg)
|
||||
log.Warningf("%s", msg)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ func (s *Sleeper) beforeSave() {}
|
||||
// +checklocksignore
|
||||
func (s *Sleeper) StateSave(stateSinkObject state.Sink) {
|
||||
s.beforeSave()
|
||||
var sharedListValue *Waker
|
||||
sharedListValue = s.saveSharedList()
|
||||
sharedListValue := s.saveSharedList()
|
||||
_ = (*Waker)(sharedListValue)
|
||||
stateSinkObject.SaveValue(0, sharedListValue)
|
||||
stateSinkObject.Save(1, &s.localList)
|
||||
stateSinkObject.Save(2, &s.allWakers)
|
||||
@@ -58,8 +58,8 @@ func (w *Waker) beforeSave() {}
|
||||
// +checklocksignore
|
||||
func (w *Waker) StateSave(stateSinkObject state.Sink) {
|
||||
w.beforeSave()
|
||||
var sValue wakerState
|
||||
sValue = w.saveS()
|
||||
sValue := w.saveS()
|
||||
_ = (wakerState)(sValue)
|
||||
stateSinkObject.SaveValue(0, sValue)
|
||||
stateSinkObject.Save(1, &w.next)
|
||||
stateSinkObject.Save(2, &w.allWakersNext)
|
||||
|
||||
+71
-24
@@ -384,7 +384,7 @@ func (s *addrSet) InsertWithoutMergingUnchecked(gap addrGapIterator, r addrRange
|
||||
if splitMaxGap {
|
||||
gap.node.updateMaxGapLeaf()
|
||||
}
|
||||
return addrIterator{gap.node, gap.index}
|
||||
return addrIterator(gap)
|
||||
}
|
||||
|
||||
// InsertRange inserts the given segment into the set. If the new segment can
|
||||
@@ -513,7 +513,7 @@ func (s *addrSet) Remove(seg addrIterator) addrGapIterator {
|
||||
if addrtrackGaps != 0 {
|
||||
seg.node.updateMaxGapLeaf()
|
||||
}
|
||||
return seg.node.rebalanceAfterRemove(addrGapIterator{seg.node, seg.index})
|
||||
return seg.node.rebalanceAfterRemove(addrGapIterator(seg))
|
||||
}
|
||||
|
||||
// RemoveAll removes all segments from the set. All existing iterators are
|
||||
@@ -530,21 +530,52 @@ func (s *addrSet) RemoveAll() {
|
||||
// if the caller needs to do additional work before removing each segment,
|
||||
// iterate segments and call Remove in a loop instead.
|
||||
func (s *addrSet) RemoveRange(r addrRange) addrGapIterator {
|
||||
seg, gap := s.Find(r.Start)
|
||||
if seg.Ok() {
|
||||
seg = s.Isolate(seg, r)
|
||||
gap = s.Remove(seg)
|
||||
}
|
||||
for seg = gap.NextSegment(); seg.Ok() && seg.Start() < r.End; seg = gap.NextSegment() {
|
||||
seg = s.SplitAfter(seg, r.End)
|
||||
gap = s.Remove(seg)
|
||||
}
|
||||
return gap
|
||||
return s.RemoveRangeWith(r, nil)
|
||||
}
|
||||
|
||||
// RemoveFullRange is equivalent to RemoveRange, except that if any key in the
|
||||
// given range does not correspond to a segment, RemoveFullRange panics.
|
||||
func (s *addrSet) RemoveFullRange(r addrRange) addrGapIterator {
|
||||
return s.RemoveFullRangeWith(r, nil)
|
||||
}
|
||||
|
||||
// RemoveRangeWith removes all segments in the given range. An iterator to the
|
||||
// newly formed gap is returned, and all existing iterators are invalidated.
|
||||
//
|
||||
// The function f is applied to each segment immediately before it is removed,
|
||||
// in order of ascending keys. Segments that lie partially outside r are split
|
||||
// before f is called, such that f only observes segments entirely within r.
|
||||
// Non-empty gaps between segments are skipped.
|
||||
//
|
||||
// RemoveRangeWith searches the set to find segments to remove. If the caller
|
||||
// already has an iterator to either end of the range of segments to remove, or
|
||||
// if the caller needs to do additional work before removing each segment,
|
||||
// iterate segments and call Remove in a loop instead.
|
||||
//
|
||||
// N.B. f must not invalidate iterators into s.
|
||||
func (s *addrSet) RemoveRangeWith(r addrRange, f func(seg addrIterator)) addrGapIterator {
|
||||
seg, gap := s.Find(r.Start)
|
||||
if seg.Ok() {
|
||||
seg = s.Isolate(seg, r)
|
||||
if f != nil {
|
||||
f(seg)
|
||||
}
|
||||
gap = s.Remove(seg)
|
||||
}
|
||||
for seg = gap.NextSegment(); seg.Ok() && seg.Start() < r.End; seg = gap.NextSegment() {
|
||||
seg = s.SplitAfter(seg, r.End)
|
||||
if f != nil {
|
||||
f(seg)
|
||||
}
|
||||
gap = s.Remove(seg)
|
||||
}
|
||||
return gap
|
||||
}
|
||||
|
||||
// RemoveFullRangeWith is equivalent to RemoveRangeWith, except that if any key
|
||||
// in the given range does not correspond to a segment, RemoveFullRangeWith
|
||||
// panics.
|
||||
func (s *addrSet) RemoveFullRangeWith(r addrRange, f func(seg addrIterator)) addrGapIterator {
|
||||
seg := s.FindSegment(r.Start)
|
||||
if !seg.Ok() {
|
||||
panic(fmt.Sprintf("missing segment at %v", r.Start))
|
||||
@@ -552,6 +583,9 @@ func (s *addrSet) RemoveFullRange(r addrRange) addrGapIterator {
|
||||
seg = s.SplitBefore(seg, r.Start)
|
||||
for {
|
||||
seg = s.SplitAfter(seg, r.End)
|
||||
if f != nil {
|
||||
f(seg)
|
||||
}
|
||||
end := seg.End()
|
||||
gap := s.Remove(seg)
|
||||
if r.End <= end {
|
||||
@@ -564,6 +598,19 @@ func (s *addrSet) RemoveFullRange(r addrRange) addrGapIterator {
|
||||
}
|
||||
}
|
||||
|
||||
// MoveFrom moves all segments from s2 to s, replacing all existing segments in
|
||||
// s and leaving s2 empty.
|
||||
func (s *addrSet) MoveFrom(s2 *addrSet) {
|
||||
*s = *s2
|
||||
for _, child := range s.root.children {
|
||||
if child == nil {
|
||||
break
|
||||
}
|
||||
child.parent = &s.root
|
||||
}
|
||||
s2.RemoveAll()
|
||||
}
|
||||
|
||||
// Merge attempts to merge two neighboring segments. If successful, Merge
|
||||
// returns an iterator to the merged segment, and all existing iterators are
|
||||
// invalidated. Otherwise, Merge returns a terminal iterator.
|
||||
@@ -817,11 +864,11 @@ func (s *addrSet) Isolate(seg addrIterator, r addrRange) addrIterator {
|
||||
// LowerBoundSegmentSplitBefore provides an iterator to the first segment to be
|
||||
// mutated, suitable as the initial value for a loop variable.
|
||||
func (s *addrSet) LowerBoundSegmentSplitBefore(min uintptr) addrIterator {
|
||||
seg := s.LowerBoundSegment(min)
|
||||
seg, gap := s.Find(min)
|
||||
if seg.Ok() {
|
||||
seg = s.SplitBefore(seg, min)
|
||||
return s.SplitBefore(seg, min)
|
||||
}
|
||||
return seg
|
||||
return gap.NextSegment()
|
||||
}
|
||||
|
||||
// UpperBoundSegmentSplitAfter combines UpperBoundSegment and SplitAfter.
|
||||
@@ -831,11 +878,11 @@ func (s *addrSet) LowerBoundSegmentSplitBefore(min uintptr) addrIterator {
|
||||
// UpperBoundSegmentSplitAfter provides an iterator to the first segment to be
|
||||
// mutated, suitable as the initial value for a loop variable.
|
||||
func (s *addrSet) UpperBoundSegmentSplitAfter(max uintptr) addrIterator {
|
||||
seg := s.UpperBoundSegment(max)
|
||||
seg, gap := s.Find(max)
|
||||
if seg.Ok() {
|
||||
seg = s.SplitAfter(seg, max)
|
||||
return s.SplitAfter(seg, max)
|
||||
}
|
||||
return seg
|
||||
return gap.PrevSegment()
|
||||
}
|
||||
|
||||
// VisitRange applies the function f to all segments intersecting the range r,
|
||||
@@ -1575,7 +1622,7 @@ func (seg addrIterator) PrevGap() addrGapIterator {
|
||||
|
||||
return seg.node.children[seg.index].lastSegment().NextGap()
|
||||
}
|
||||
return addrGapIterator{seg.node, seg.index}
|
||||
return addrGapIterator(seg)
|
||||
}
|
||||
|
||||
// NextGap returns the gap immediately after the iterated segment.
|
||||
@@ -1867,26 +1914,26 @@ func (n *addrnode) String() string {
|
||||
func (n *addrnode) writeDebugString(buf *bytes.Buffer, prefix string) {
|
||||
if n.hasChildren != (n.nrSegments > 0 && n.children[0] != nil) {
|
||||
buf.WriteString(prefix)
|
||||
buf.WriteString(fmt.Sprintf("WARNING: inconsistent value of hasChildren: got %v, want %v\n", n.hasChildren, !n.hasChildren))
|
||||
fmt.Fprintf(buf, "WARNING: inconsistent value of hasChildren: got %v, want %v\n", n.hasChildren, !n.hasChildren)
|
||||
}
|
||||
for i := 0; i < n.nrSegments; i++ {
|
||||
if child := n.children[i]; child != nil {
|
||||
cprefix := fmt.Sprintf("%s- % 3d ", prefix, i)
|
||||
if child.parent != n || child.parentIndex != i {
|
||||
buf.WriteString(cprefix)
|
||||
buf.WriteString(fmt.Sprintf("WARNING: inconsistent linkage to parent: got (%p, %d), want (%p, %d)\n", child.parent, child.parentIndex, n, i))
|
||||
fmt.Fprintf(buf, "WARNING: inconsistent linkage to parent: got (%p, %d), want (%p, %d)\n", child.parent, child.parentIndex, n, i)
|
||||
}
|
||||
child.writeDebugString(buf, fmt.Sprintf("%s- % 3d ", prefix, i))
|
||||
}
|
||||
buf.WriteString(prefix)
|
||||
if n.hasChildren {
|
||||
if addrtrackGaps != 0 {
|
||||
buf.WriteString(fmt.Sprintf("- % 3d: %v => %v, maxGap: %d\n", i, n.keys[i], n.values[i], n.maxGap.Get()))
|
||||
fmt.Fprintf(buf, "- % 3d: %v => %v, maxGap: %d\n", i, n.keys[i], n.values[i], n.maxGap.Get())
|
||||
} else {
|
||||
buf.WriteString(fmt.Sprintf("- % 3d: %v => %v\n", i, n.keys[i], n.values[i]))
|
||||
fmt.Fprintf(buf, "- % 3d: %v => %v\n", i, n.keys[i], n.values[i])
|
||||
}
|
||||
} else {
|
||||
buf.WriteString(fmt.Sprintf("- % 3d: %v => %v\n", i, n.keys[i], n.values[i]))
|
||||
fmt.Fprintf(buf, "- % 3d: %v => %v\n", i, n.keys[i], n.values[i])
|
||||
}
|
||||
}
|
||||
if child := n.children[n.nrSegments]; child != nil {
|
||||
|
||||
+56
-49
@@ -32,7 +32,7 @@ type internalCallback interface {
|
||||
source() *objectDecodeState
|
||||
|
||||
// callbackRun executes the callback.
|
||||
callbackRun()
|
||||
callbackRun(ds *decodeState)
|
||||
}
|
||||
|
||||
// userCallback is an implementation of internalCallback.
|
||||
@@ -44,7 +44,7 @@ func (userCallback) source() *objectDecodeState {
|
||||
}
|
||||
|
||||
// callbackRun implements internalCallback.callbackRun.
|
||||
func (uc userCallback) callbackRun() {
|
||||
func (uc userCallback) callbackRun(*decodeState) {
|
||||
uc()
|
||||
}
|
||||
|
||||
@@ -84,7 +84,13 @@ type objectDecodeState struct {
|
||||
// callbacks is a set of callbacks to execute on load.
|
||||
callbacks []internalCallback
|
||||
|
||||
completeEntry
|
||||
pendingEntry odsListElem
|
||||
leafEntry odsListElem
|
||||
}
|
||||
|
||||
type odsListElem struct {
|
||||
ods *objectDecodeState
|
||||
odsEntry
|
||||
}
|
||||
|
||||
// addCallback adds a callback to the objectDecodeState.
|
||||
@@ -122,8 +128,13 @@ func (ods *objectDecodeState) source() *objectDecodeState {
|
||||
}
|
||||
|
||||
// callbackRun implements internalCallback.callbackRun.
|
||||
func (ods *objectDecodeState) callbackRun() {
|
||||
func (ods *objectDecodeState) callbackRun(ds *decodeState) {
|
||||
ods.blockedBy--
|
||||
if ods.blockedBy == 0 {
|
||||
ds.leaves.PushBack(&ods.leafEntry)
|
||||
} else if ods.blockedBy < 0 {
|
||||
Failf("object %d has negative blockedBy: %d", ods.id, ods.blockedBy)
|
||||
}
|
||||
}
|
||||
|
||||
// decodeState is a graph of objects in the process of being decoded.
|
||||
@@ -155,7 +166,11 @@ type decodeState struct {
|
||||
deferred map[objectID]wire.Object
|
||||
|
||||
// pending is the set of objects that are not yet complete.
|
||||
pending completeList
|
||||
pending odsList
|
||||
|
||||
// leaves is the set of objects that have no dependencies (blockedBy == 0).
|
||||
// leaves are consumed from the front and appended to the back.
|
||||
leaves odsList
|
||||
|
||||
// stats tracks time data.
|
||||
stats Stats
|
||||
@@ -185,20 +200,12 @@ func (ds *decodeState) checkComplete(ods *objectDecodeState) bool {
|
||||
|
||||
// Fire all callbacks.
|
||||
for _, ic := range ods.callbacks {
|
||||
ic.callbackRun()
|
||||
ic.callbackRun(ds)
|
||||
}
|
||||
|
||||
// Mark completed.
|
||||
cbs := ods.callbacks
|
||||
ods.callbacks = nil
|
||||
ds.pending.Remove(ods)
|
||||
|
||||
// Recursively check others.
|
||||
for _, ic := range cbs {
|
||||
if other := ic.source(); other != nil && other.blockedBy == 0 {
|
||||
ds.checkComplete(other)
|
||||
}
|
||||
}
|
||||
ds.pending.Remove(&ods.pendingEntry)
|
||||
|
||||
return true // All set.
|
||||
}
|
||||
@@ -223,6 +230,9 @@ func (ds *decodeState) wait(waiter *objectDecodeState, id objectID, callback fun
|
||||
|
||||
// Mark as blocked.
|
||||
waiter.blockedBy++
|
||||
if waiter.blockedBy == 1 {
|
||||
ds.leaves.Remove(&waiter.leafEntry)
|
||||
}
|
||||
|
||||
// No nil can be returned here.
|
||||
other := ds.lookup(id)
|
||||
@@ -280,6 +290,26 @@ func walkChild(path []wire.Dot, obj reflect.Value) reflect.Value {
|
||||
return obj
|
||||
}
|
||||
|
||||
func (ds *decodeState) growObjectsByID(id objectID) {
|
||||
if len(ds.objectsByID) < int(id) {
|
||||
ds.objectsByID = append(ds.objectsByID, make([]*objectDecodeState, int(id)-len(ds.objectsByID))...)
|
||||
}
|
||||
}
|
||||
|
||||
func (ds *decodeState) addObject(id objectID, obj reflect.Value) *objectDecodeState {
|
||||
ods := &objectDecodeState{
|
||||
id: id,
|
||||
obj: obj,
|
||||
}
|
||||
ods.pendingEntry.ods = ods
|
||||
ods.leafEntry.ods = ods
|
||||
ds.growObjectsByID(id)
|
||||
ds.objectsByID[id-1] = ods
|
||||
ds.pending.PushBack(&ods.pendingEntry)
|
||||
ds.leaves.PushBack(&ods.leafEntry)
|
||||
return ods
|
||||
}
|
||||
|
||||
// register registers a decode with a type.
|
||||
//
|
||||
// This type is only used to instantiate a new object if it has not been
|
||||
@@ -288,11 +318,9 @@ func walkChild(path []wire.Dot, obj reflect.Value) reflect.Value {
|
||||
func (ds *decodeState) register(r *wire.Ref, typ reflect.Type) reflect.Value {
|
||||
// Grow the objectsByID slice.
|
||||
id := objectID(r.Root)
|
||||
if len(ds.objectsByID) < int(id) {
|
||||
ds.objectsByID = append(ds.objectsByID, make([]*objectDecodeState, int(id)-len(ds.objectsByID))...)
|
||||
}
|
||||
|
||||
// Does this object already exist?
|
||||
ds.growObjectsByID(id)
|
||||
ods := ds.objectsByID[id-1]
|
||||
if ods != nil {
|
||||
return walkChild(r.Dots, ods.obj)
|
||||
@@ -303,12 +331,7 @@ func (ds *decodeState) register(r *wire.Ref, typ reflect.Type) reflect.Value {
|
||||
typ = ds.findType(r.Type)
|
||||
}
|
||||
v := reflect.New(typ)
|
||||
ods = &objectDecodeState{
|
||||
id: id,
|
||||
obj: v.Elem(),
|
||||
}
|
||||
ds.objectsByID[id-1] = ods
|
||||
ds.pending.PushBack(ods)
|
||||
ods = ds.addObject(id, v.Elem())
|
||||
|
||||
// Process any deferred objects & callbacks.
|
||||
if encoded, ok := ds.deferred[id]; ok {
|
||||
@@ -581,13 +604,8 @@ func (ds *decodeState) Load(obj reflect.Value) {
|
||||
return ds.types.LookupName(id)
|
||||
})
|
||||
|
||||
// Create the root object.
|
||||
rootOds := &objectDecodeState{
|
||||
id: 1,
|
||||
obj: obj,
|
||||
}
|
||||
ds.objectsByID = append(ds.objectsByID, rootOds)
|
||||
ds.pending.PushBack(rootOds)
|
||||
// Add the root object with ID 1.
|
||||
_ = ds.addObject(1, obj)
|
||||
|
||||
// Read the number of objects.
|
||||
numObjects, object, err := ReadHeader(&ds.r)
|
||||
@@ -603,7 +621,6 @@ func (ds *decodeState) Load(obj reflect.Value) {
|
||||
encoded wire.Object
|
||||
ods *objectDecodeState
|
||||
id objectID
|
||||
tid = typeID(1)
|
||||
)
|
||||
if err := safely(func() {
|
||||
// Decode all objects in the stream.
|
||||
@@ -616,7 +633,6 @@ func (ds *decodeState) Load(obj reflect.Value) {
|
||||
switch we := encoded.(type) {
|
||||
case *wire.Type:
|
||||
ds.types.Register(we)
|
||||
tid++
|
||||
encoded = nil
|
||||
continue
|
||||
case wire.Uint:
|
||||
@@ -673,22 +689,13 @@ func (ds *decodeState) Load(obj reflect.Value) {
|
||||
// objects become complete (there is a dependency cycle).
|
||||
//
|
||||
// Note that we iterate backwards here, because there will be a strong
|
||||
// tendendcy for blocking relationships to go from earlier objects to
|
||||
// tendency for blocking relationships to go from earlier objects to
|
||||
// later (deeper) objects in the graph. This will reduce the number of
|
||||
// iterations required to finish all objects.
|
||||
if err := safely(func() {
|
||||
for ds.pending.Back() != nil {
|
||||
thisCycle := false
|
||||
for ods = ds.pending.Back(); ods != nil; {
|
||||
if ds.checkComplete(ods) {
|
||||
thisCycle = true
|
||||
break
|
||||
}
|
||||
ods = ods.Prev()
|
||||
}
|
||||
if !thisCycle {
|
||||
break
|
||||
}
|
||||
for elem := ds.leaves.Front(); elem != nil; elem = elem.Next() {
|
||||
ods = elem.ods
|
||||
ds.checkComplete(elem.ods)
|
||||
}
|
||||
}); err != nil {
|
||||
Failf("error executing callbacks: %w\nfor object %#v", err, ods.obj.Interface())
|
||||
@@ -696,9 +703,9 @@ func (ds *decodeState) Load(obj reflect.Value) {
|
||||
|
||||
// Check if we have any remaining dependency cycles. If there are any
|
||||
// objects left in the pending list, then it must be due to a cycle.
|
||||
if ods := ds.pending.Front(); ods != nil {
|
||||
if elem := ds.pending.Front(); elem != nil {
|
||||
// This must be the result of a dependency cycle.
|
||||
cycle := ods.findCycle()
|
||||
cycle := elem.ods.findCycle()
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString("dependency cycle: {")
|
||||
for i, cycleOS := range cycle {
|
||||
@@ -708,7 +715,7 @@ func (ds *decodeState) Load(obj reflect.Value) {
|
||||
fmt.Fprintf(&buf, "%q", cycleOS.obj.Type())
|
||||
}
|
||||
buf.WriteString("}")
|
||||
Failf("incomplete graph: %s", string(buf.Bytes()))
|
||||
Failf("incomplete graph: %s", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+8
-3
@@ -802,14 +802,19 @@ func (es *encodeState) Save(obj reflect.Value) {
|
||||
})
|
||||
for _, id := range ids {
|
||||
// Encode the id.
|
||||
oes = nil
|
||||
wire.Save(&es.w, wire.Uint(id))
|
||||
// Marshal the object.
|
||||
oes := es.pending[id]
|
||||
oes = es.pending[id]
|
||||
wire.Save(&es.w, oes.encoded)
|
||||
}
|
||||
}); err != nil {
|
||||
// Include the object and the error.
|
||||
Failf("error serializing object %#v: %w", oes.encoded, err)
|
||||
if oes != nil {
|
||||
// Include the object and the error.
|
||||
Failf("error serializing object %#v: %w", oes.encoded, err)
|
||||
} else {
|
||||
Failf("error serializing type or ID: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
vendor/gvisor.dev/gvisor/pkg/state/complete_list.go → vendor/gvisor.dev/gvisor/pkg/state/ods_list.go
Vendored
+42
-42
@@ -6,14 +6,14 @@ package state
|
||||
// objects, if they are not the same. An ElementMapper is not typically
|
||||
// required if: Linker is left as is, Element is left as is, or Linker and
|
||||
// Element are the same type.
|
||||
type completeElementMapper struct{}
|
||||
type odsElementMapper struct{}
|
||||
|
||||
// linkerFor maps an Element to a Linker.
|
||||
//
|
||||
// This default implementation should be inlined.
|
||||
//
|
||||
//go:nosplit
|
||||
func (completeElementMapper) linkerFor(elem *objectDecodeState) *objectDecodeState { return elem }
|
||||
func (odsElementMapper) linkerFor(elem *odsListElem) *odsListElem { return elem }
|
||||
|
||||
// List is an intrusive list. Entries can be added to or removed from the list
|
||||
// in O(1) time and with no additional memory allocations.
|
||||
@@ -27,13 +27,13 @@ func (completeElementMapper) linkerFor(elem *objectDecodeState) *objectDecodeSta
|
||||
// }
|
||||
//
|
||||
// +stateify savable
|
||||
type completeList struct {
|
||||
head *objectDecodeState
|
||||
tail *objectDecodeState
|
||||
type odsList struct {
|
||||
head *odsListElem
|
||||
tail *odsListElem
|
||||
}
|
||||
|
||||
// Reset resets list l to the empty state.
|
||||
func (l *completeList) Reset() {
|
||||
func (l *odsList) Reset() {
|
||||
l.head = nil
|
||||
l.tail = nil
|
||||
}
|
||||
@@ -41,21 +41,21 @@ func (l *completeList) Reset() {
|
||||
// Empty returns true iff the list is empty.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *completeList) Empty() bool {
|
||||
func (l *odsList) Empty() bool {
|
||||
return l.head == nil
|
||||
}
|
||||
|
||||
// Front returns the first element of list l or nil.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *completeList) Front() *objectDecodeState {
|
||||
func (l *odsList) Front() *odsListElem {
|
||||
return l.head
|
||||
}
|
||||
|
||||
// Back returns the last element of list l or nil.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *completeList) Back() *objectDecodeState {
|
||||
func (l *odsList) Back() *odsListElem {
|
||||
return l.tail
|
||||
}
|
||||
|
||||
@@ -64,8 +64,8 @@ func (l *completeList) Back() *objectDecodeState {
|
||||
// NOTE: This is an O(n) operation.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *completeList) Len() (count int) {
|
||||
for e := l.Front(); e != nil; e = (completeElementMapper{}.linkerFor(e)).Next() {
|
||||
func (l *odsList) Len() (count int) {
|
||||
for e := l.Front(); e != nil; e = (odsElementMapper{}.linkerFor(e)).Next() {
|
||||
count++
|
||||
}
|
||||
return count
|
||||
@@ -74,12 +74,12 @@ func (l *completeList) Len() (count int) {
|
||||
// PushFront inserts the element e at the front of list l.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *completeList) PushFront(e *objectDecodeState) {
|
||||
linker := completeElementMapper{}.linkerFor(e)
|
||||
func (l *odsList) PushFront(e *odsListElem) {
|
||||
linker := odsElementMapper{}.linkerFor(e)
|
||||
linker.SetNext(l.head)
|
||||
linker.SetPrev(nil)
|
||||
if l.head != nil {
|
||||
completeElementMapper{}.linkerFor(l.head).SetPrev(e)
|
||||
odsElementMapper{}.linkerFor(l.head).SetPrev(e)
|
||||
} else {
|
||||
l.tail = e
|
||||
}
|
||||
@@ -90,13 +90,13 @@ func (l *completeList) PushFront(e *objectDecodeState) {
|
||||
// PushFrontList inserts list m at the start of list l, emptying m.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *completeList) PushFrontList(m *completeList) {
|
||||
func (l *odsList) PushFrontList(m *odsList) {
|
||||
if l.head == nil {
|
||||
l.head = m.head
|
||||
l.tail = m.tail
|
||||
} else if m.head != nil {
|
||||
completeElementMapper{}.linkerFor(l.head).SetPrev(m.tail)
|
||||
completeElementMapper{}.linkerFor(m.tail).SetNext(l.head)
|
||||
odsElementMapper{}.linkerFor(l.head).SetPrev(m.tail)
|
||||
odsElementMapper{}.linkerFor(m.tail).SetNext(l.head)
|
||||
|
||||
l.head = m.head
|
||||
}
|
||||
@@ -107,12 +107,12 @@ func (l *completeList) PushFrontList(m *completeList) {
|
||||
// PushBack inserts the element e at the back of list l.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *completeList) PushBack(e *objectDecodeState) {
|
||||
linker := completeElementMapper{}.linkerFor(e)
|
||||
func (l *odsList) PushBack(e *odsListElem) {
|
||||
linker := odsElementMapper{}.linkerFor(e)
|
||||
linker.SetNext(nil)
|
||||
linker.SetPrev(l.tail)
|
||||
if l.tail != nil {
|
||||
completeElementMapper{}.linkerFor(l.tail).SetNext(e)
|
||||
odsElementMapper{}.linkerFor(l.tail).SetNext(e)
|
||||
} else {
|
||||
l.head = e
|
||||
}
|
||||
@@ -123,13 +123,13 @@ func (l *completeList) PushBack(e *objectDecodeState) {
|
||||
// PushBackList inserts list m at the end of list l, emptying m.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *completeList) PushBackList(m *completeList) {
|
||||
func (l *odsList) PushBackList(m *odsList) {
|
||||
if l.head == nil {
|
||||
l.head = m.head
|
||||
l.tail = m.tail
|
||||
} else if m.head != nil {
|
||||
completeElementMapper{}.linkerFor(l.tail).SetNext(m.head)
|
||||
completeElementMapper{}.linkerFor(m.head).SetPrev(l.tail)
|
||||
odsElementMapper{}.linkerFor(l.tail).SetNext(m.head)
|
||||
odsElementMapper{}.linkerFor(m.head).SetPrev(l.tail)
|
||||
|
||||
l.tail = m.tail
|
||||
}
|
||||
@@ -140,9 +140,9 @@ func (l *completeList) PushBackList(m *completeList) {
|
||||
// InsertAfter inserts e after b.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *completeList) InsertAfter(b, e *objectDecodeState) {
|
||||
bLinker := completeElementMapper{}.linkerFor(b)
|
||||
eLinker := completeElementMapper{}.linkerFor(e)
|
||||
func (l *odsList) InsertAfter(b, e *odsListElem) {
|
||||
bLinker := odsElementMapper{}.linkerFor(b)
|
||||
eLinker := odsElementMapper{}.linkerFor(e)
|
||||
|
||||
a := bLinker.Next()
|
||||
|
||||
@@ -151,7 +151,7 @@ func (l *completeList) InsertAfter(b, e *objectDecodeState) {
|
||||
bLinker.SetNext(e)
|
||||
|
||||
if a != nil {
|
||||
completeElementMapper{}.linkerFor(a).SetPrev(e)
|
||||
odsElementMapper{}.linkerFor(a).SetPrev(e)
|
||||
} else {
|
||||
l.tail = e
|
||||
}
|
||||
@@ -160,9 +160,9 @@ func (l *completeList) InsertAfter(b, e *objectDecodeState) {
|
||||
// InsertBefore inserts e before a.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *completeList) InsertBefore(a, e *objectDecodeState) {
|
||||
aLinker := completeElementMapper{}.linkerFor(a)
|
||||
eLinker := completeElementMapper{}.linkerFor(e)
|
||||
func (l *odsList) InsertBefore(a, e *odsListElem) {
|
||||
aLinker := odsElementMapper{}.linkerFor(a)
|
||||
eLinker := odsElementMapper{}.linkerFor(e)
|
||||
|
||||
b := aLinker.Prev()
|
||||
eLinker.SetNext(a)
|
||||
@@ -170,7 +170,7 @@ func (l *completeList) InsertBefore(a, e *objectDecodeState) {
|
||||
aLinker.SetPrev(e)
|
||||
|
||||
if b != nil {
|
||||
completeElementMapper{}.linkerFor(b).SetNext(e)
|
||||
odsElementMapper{}.linkerFor(b).SetNext(e)
|
||||
} else {
|
||||
l.head = e
|
||||
}
|
||||
@@ -179,19 +179,19 @@ func (l *completeList) InsertBefore(a, e *objectDecodeState) {
|
||||
// Remove removes e from l.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *completeList) Remove(e *objectDecodeState) {
|
||||
linker := completeElementMapper{}.linkerFor(e)
|
||||
func (l *odsList) Remove(e *odsListElem) {
|
||||
linker := odsElementMapper{}.linkerFor(e)
|
||||
prev := linker.Prev()
|
||||
next := linker.Next()
|
||||
|
||||
if prev != nil {
|
||||
completeElementMapper{}.linkerFor(prev).SetNext(next)
|
||||
odsElementMapper{}.linkerFor(prev).SetNext(next)
|
||||
} else if l.head == e {
|
||||
l.head = next
|
||||
}
|
||||
|
||||
if next != nil {
|
||||
completeElementMapper{}.linkerFor(next).SetPrev(prev)
|
||||
odsElementMapper{}.linkerFor(next).SetPrev(prev)
|
||||
} else if l.tail == e {
|
||||
l.tail = prev
|
||||
}
|
||||
@@ -205,35 +205,35 @@ func (l *completeList) Remove(e *objectDecodeState) {
|
||||
// methods needed by List.
|
||||
//
|
||||
// +stateify savable
|
||||
type completeEntry struct {
|
||||
next *objectDecodeState
|
||||
prev *objectDecodeState
|
||||
type odsEntry struct {
|
||||
next *odsListElem
|
||||
prev *odsListElem
|
||||
}
|
||||
|
||||
// Next returns the entry that follows e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *completeEntry) Next() *objectDecodeState {
|
||||
func (e *odsEntry) Next() *odsListElem {
|
||||
return e.next
|
||||
}
|
||||
|
||||
// Prev returns the entry that precedes e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *completeEntry) Prev() *objectDecodeState {
|
||||
func (e *odsEntry) Prev() *odsListElem {
|
||||
return e.prev
|
||||
}
|
||||
|
||||
// SetNext assigns 'entry' as the entry that follows e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *completeEntry) SetNext(elem *objectDecodeState) {
|
||||
func (e *odsEntry) SetNext(elem *odsListElem) {
|
||||
e.next = elem
|
||||
}
|
||||
|
||||
// SetPrev assigns 'entry' as the entry that precedes e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *completeEntry) SetPrev(elem *objectDecodeState) {
|
||||
func (e *odsEntry) SetPrev(elem *odsListElem) {
|
||||
e.prev = elem
|
||||
}
|
||||
+1
-1
@@ -188,7 +188,7 @@ func (s Sink) Context() context.Context {
|
||||
// Type is an interface that must be implemented by Struct objects. This allows
|
||||
// these objects to be serialized while minimizing runtime reflection required.
|
||||
//
|
||||
// All these methods can be automatically generated by the go_statify tool.
|
||||
// All these methods can be automatically generated by the go_stateify tool.
|
||||
type Type interface {
|
||||
// StateTypeName returns the type's name.
|
||||
//
|
||||
|
||||
+6
-6
@@ -124,7 +124,7 @@ func (s *Stats) String() string {
|
||||
total time.Duration
|
||||
)
|
||||
buf.WriteString("\n")
|
||||
buf.WriteString(fmt.Sprintf("% 16s | % 8s | % 16s | %s\n", "total", "count", "per", "type"))
|
||||
fmt.Fprintf(&buf, "% 16s | % 8s | % 16s | %s\n", "total", "count", "per", "type")
|
||||
buf.WriteString("-----------------+----------+------------------+----------------\n")
|
||||
for _, se := range ss {
|
||||
if se.entry.count == 0 {
|
||||
@@ -135,11 +135,11 @@ func (s *Stats) String() string {
|
||||
count += se.entry.count
|
||||
total += se.entry.total
|
||||
per := se.entry.total / time.Duration(se.entry.count)
|
||||
buf.WriteString(fmt.Sprintf("% 16s | %8d | % 16s | %s\n",
|
||||
se.entry.total, se.entry.count, per, se.name))
|
||||
fmt.Fprintf(&buf, "% 16s | %8d | % 16s | %s\n",
|
||||
se.entry.total, se.entry.count, per, se.name)
|
||||
}
|
||||
buf.WriteString("-----------------+----------+------------------+----------------\n")
|
||||
buf.WriteString(fmt.Sprintf("% 16s | % 8d | % 16s | [all]",
|
||||
total, count, total/time.Duration(count)))
|
||||
return string(buf.Bytes())
|
||||
fmt.Fprintf(&buf, "% 16s | % 8d | % 16s | [all]",
|
||||
total, count, total/time.Duration(count))
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
+17
-17
@@ -295,23 +295,23 @@ const interfaceType = "interface"
|
||||
var primitiveTypeDatabase = func() map[string]reflect.Type {
|
||||
r := make(map[string]reflect.Type)
|
||||
for _, t := range []reflect.Type{
|
||||
reflect.TypeOf(false),
|
||||
reflect.TypeOf(int(0)),
|
||||
reflect.TypeOf(int8(0)),
|
||||
reflect.TypeOf(int16(0)),
|
||||
reflect.TypeOf(int32(0)),
|
||||
reflect.TypeOf(int64(0)),
|
||||
reflect.TypeOf(uint(0)),
|
||||
reflect.TypeOf(uintptr(0)),
|
||||
reflect.TypeOf(uint8(0)),
|
||||
reflect.TypeOf(uint16(0)),
|
||||
reflect.TypeOf(uint32(0)),
|
||||
reflect.TypeOf(uint64(0)),
|
||||
reflect.TypeOf(""),
|
||||
reflect.TypeOf(float32(0.0)),
|
||||
reflect.TypeOf(float64(0.0)),
|
||||
reflect.TypeOf(complex64(0.0)),
|
||||
reflect.TypeOf(complex128(0.0)),
|
||||
reflect.TypeFor[bool](),
|
||||
reflect.TypeFor[int](),
|
||||
reflect.TypeFor[int8](),
|
||||
reflect.TypeFor[int16](),
|
||||
reflect.TypeFor[int32](),
|
||||
reflect.TypeFor[int64](),
|
||||
reflect.TypeFor[uint](),
|
||||
reflect.TypeFor[uintptr](),
|
||||
reflect.TypeFor[uint8](),
|
||||
reflect.TypeFor[uint16](),
|
||||
reflect.TypeFor[uint32](),
|
||||
reflect.TypeFor[uint64](),
|
||||
reflect.TypeFor[string](),
|
||||
reflect.TypeFor[float32](),
|
||||
reflect.TypeFor[float64](),
|
||||
reflect.TypeFor[complex64](),
|
||||
reflect.TypeFor[complex128](),
|
||||
} {
|
||||
r[t.Name()] = t
|
||||
}
|
||||
|
||||
+2
-1
@@ -1,7 +1,8 @@
|
||||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd.
|
||||
|
||||
package sync
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd.
|
||||
|
||||
//go:build !checklocks
|
||||
// +build !checklocks
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd.
|
||||
|
||||
//go:build checklocks
|
||||
// +build checklocks
|
||||
|
||||
+5
-4
@@ -110,8 +110,8 @@ func (g *Gate) leaveClosed() {
|
||||
if atomic.LoadUintptr(&g.closingG) == 0 {
|
||||
return
|
||||
}
|
||||
if g := atomic.SwapUintptr(&g.closingG, 0); g > preparingG {
|
||||
goready(g, 0)
|
||||
if cG := atomic.SwapUintptr(&g.closingG, 0); cG > preparingG {
|
||||
goready(cG, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,8 +133,8 @@ func (g *Gate) Close() {
|
||||
panic("concurrent Close of sync.Gate")
|
||||
}
|
||||
|
||||
if g := atomic.SwapUintptr(&g.closingG, preparingG); g != 0 {
|
||||
panic(fmt.Sprintf("invalid sync.Gate.closingG during Close: %#x", g))
|
||||
if cG := atomic.SwapUintptr(&g.closingG, preparingG); cG != 0 {
|
||||
panic(fmt.Sprintf("invalid sync.Gate.closingG during Close: %#x", cG))
|
||||
}
|
||||
if atomic.LoadInt32(&g.userCount) == math.MinInt32 {
|
||||
// The last call to Leave arrived while we were setting up closingG.
|
||||
@@ -142,6 +142,7 @@ func (g *Gate) Close() {
|
||||
}
|
||||
// WaitReasonSemacquire/TraceBlockSync are consistent with WaitGroup.
|
||||
gopark(gateCommit, gohacks.Noescape(unsafe.Pointer(&g.closingG)), WaitReasonSemacquire, TraceBlockSync, 0)
|
||||
RaceAcquire(unsafe.Pointer(&g.closingG))
|
||||
}
|
||||
|
||||
//go:norace
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd.
|
||||
|
||||
//go:build go1.13 && !go1.14
|
||||
// +build go1.13,!go1.14
|
||||
|
||||
+2
-1
@@ -1,7 +1,8 @@
|
||||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd.
|
||||
|
||||
//go:build go1.14
|
||||
// +build go1.14
|
||||
|
||||
+2
-1
@@ -1,7 +1,8 @@
|
||||
// Copyright 2019 The gVisor Authors.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd.
|
||||
|
||||
package sync
|
||||
|
||||
|
||||
+2
-1
@@ -1,7 +1,8 @@
|
||||
// Copyright 2019 The gVisor Authors.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd.
|
||||
|
||||
//go:build !race
|
||||
// +build !race
|
||||
|
||||
+2
-1
@@ -1,7 +1,8 @@
|
||||
// Copyright 2019 The gVisor Authors.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd.
|
||||
|
||||
//go:build race
|
||||
// +build race
|
||||
|
||||
+2
-1
@@ -1,7 +1,8 @@
|
||||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd.
|
||||
|
||||
//go:build amd64
|
||||
|
||||
|
||||
+3
-3
@@ -16,7 +16,7 @@ package sync
|
||||
|
||||
// Values for the reason argument to gopark, from Go's src/runtime/runtime2.go.
|
||||
const (
|
||||
WaitReasonSelect uint8 = 9 // +checkconst runtime waitReasonSelect
|
||||
WaitReasonChanReceive uint8 = 14 // +checkconst runtime waitReasonChanReceive
|
||||
WaitReasonSemacquire uint8 = 18 // +checkconst runtime waitReasonSemacquire
|
||||
WaitReasonSelect uint8 = 9
|
||||
WaitReasonChanReceive uint8 = 14
|
||||
WaitReasonSemacquire uint8 = 19
|
||||
)
|
||||
+2
-2
@@ -16,6 +16,6 @@ package sync
|
||||
|
||||
// TraceBlockReason constants, from Go's src/runtime/trace2runtime.go.
|
||||
const (
|
||||
TraceBlockSelect TraceBlockReason = 3 // +checkconst runtime traceBlockSelect
|
||||
TraceBlockSync TraceBlockReason = 5 // +checkconst runtime traceBlockSync
|
||||
TraceBlockSelect TraceBlockReason = 3
|
||||
TraceBlockSync TraceBlockReason = 5
|
||||
)
|
||||
@@ -1,9 +1,10 @@
|
||||
// Copyright 2023 The gVisor Authors.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd.
|
||||
|
||||
//go:build go1.21
|
||||
//go:build go1.21 && !go1.24
|
||||
|
||||
package sync
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2024 The gVisor Authors.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd.
|
||||
|
||||
// https://go.dev/cl/691596 (1.26) renames the internal map type which nogo relies on.
|
||||
//go:build go1.24 && !go1.26
|
||||
|
||||
package sync
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// Use checkoffset to assert that maptype.hasher (the only field we use) has
|
||||
// the correct offset.
|
||||
const maptypeHasherOffset = unsafe.Offsetof(maptype{}.Hasher) // +checkoffset internal/abi SwissMapType.Hasher
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2024 The gVisor Authors.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd.
|
||||
|
||||
// https://go.dev/cl/691596 (1.26) renames the internal map type which nogo relies on.
|
||||
//go:build go1.26
|
||||
|
||||
package sync
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// Use checkoffset to assert that maptype.hasher (the only field we use) has
|
||||
// the correct offset.
|
||||
const maptypeHasherOffset = unsafe.Offsetof(maptype{}.Hasher) // +checkoffset internal/abi MapType.Hasher
|
||||
@@ -1,7 +1,8 @@
|
||||
// Copyright 2023 The gVisor Authors.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd.
|
||||
|
||||
// runtime.maptype is moved to internal/abi.MapType in Go 1.21.
|
||||
//
|
||||
|
||||
+2
-1
@@ -1,7 +1,8 @@
|
||||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd.
|
||||
|
||||
//go:build !amd64
|
||||
// +build !amd64
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
#define NMSPINNING_OFFSET 92 // +checkoffset runtime schedt.nmspinning
|
||||
#define NMSPINNING_OFFSET 100
|
||||
|
||||
TEXT ·addrOfSpinning(SB),NOSPLIT|NOFRAME,$0-8
|
||||
LEAQ runtime·sched(SB), AX
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright 2023 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !amd64
|
||||
|
||||
// This file is intentionally left blank. arm64 doesn't use
|
||||
// addrOfSpinning, but we still need an input to the nogo template rule.
|
||||
@@ -15,4 +15,10 @@
|
||||
//go:build !amd64
|
||||
|
||||
// This file is intentionally left blank. Other arches don't use
|
||||
// addrOfSpinning, but we still need an input to the nogo template rule.
|
||||
// addrOfSpinning, but because this package is partially used in Netstack, we
|
||||
// should support arches that aren't amd64 or arm64. Having this file here
|
||||
// ensures that `go build` doesn't compile the package with the `-complete`
|
||||
// flag, because the package isn't made up of just '.go' files.
|
||||
// This allows Netstack to use the architecture-independent portions of this
|
||||
// package, because the architecture-dependent portions are never compiled in
|
||||
// the first place.
|
||||
|
||||
+2
-1
@@ -1,7 +1,8 @@
|
||||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd.
|
||||
|
||||
// //go:linkname directives type-checked by checklinkname.
|
||||
// Runtime type copies checked by checkoffset.
|
||||
|
||||
+3
-1
@@ -1,7 +1,9 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Copyright 2019 The gVisor Authors.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd.
|
||||
|
||||
// This is mostly copied from the standard library's sync/rwmutex.go.
|
||||
//
|
||||
|
||||
+2
-1
@@ -1,7 +1,8 @@
|
||||
// Copyright 2019 The gVisor Authors.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd.
|
||||
|
||||
package sync
|
||||
|
||||
|
||||
+2
-1
@@ -1,7 +1,8 @@
|
||||
// Copyright 2019 The gVisor Authors.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd.
|
||||
|
||||
// Package sync provides synchronization primitives.
|
||||
//
|
||||
|
||||
@@ -183,7 +183,7 @@ func (d *deadlineTimer) setDeadline(cancelCh *chan struct{}, timer **time.Timer,
|
||||
return
|
||||
}
|
||||
|
||||
timeout := t.Sub(time.Now())
|
||||
timeout := time.Until(t)
|
||||
if timeout <= 0 {
|
||||
close(*cancelCh)
|
||||
return
|
||||
|
||||
+18
@@ -620,4 +620,22 @@ func (*ErrMulticastInputCannotBeOutput) IgnoreStats() bool {
|
||||
}
|
||||
func (*ErrMulticastInputCannotBeOutput) String() string { return "output cannot contain input" }
|
||||
|
||||
// ErrEndpointBusy indicates that the operation cannot be completed because the
|
||||
// endpoint is busy.
|
||||
//
|
||||
// +stateify savable
|
||||
type ErrEndpointBusy struct{}
|
||||
|
||||
// isError implements Error.
|
||||
func (*ErrEndpointBusy) isError() {}
|
||||
|
||||
// IgnoreStats implements Error.
|
||||
func (*ErrEndpointBusy) IgnoreStats() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (*ErrEndpointBusy) String() string {
|
||||
return "operation cannot be completed because the endpoint is busy"
|
||||
}
|
||||
|
||||
// LINT.ThenChange(../syserr/netstack.go)
|
||||
|
||||
@@ -603,6 +603,9 @@ const (
|
||||
// IPv4OptionTimestampType is the option type for the Timestamp option.
|
||||
IPv4OptionTimestampType IPv4OptionType = 68
|
||||
|
||||
// IPv4OptionExperimentType is the option type for the Experiment option.
|
||||
IPv4OptionExperimentType IPv4OptionType = 30
|
||||
|
||||
// ipv4OptionTypeOffset is the offset in an option of its type field.
|
||||
ipv4OptionTypeOffset = 0
|
||||
|
||||
@@ -800,6 +803,17 @@ func (i *IPv4OptionIterator) Next() (IPv4Option, bool, *IPv4OptParameterProblem)
|
||||
}
|
||||
retval := IPv4OptionRouterAlert(optionBody)
|
||||
return &retval, false, nil
|
||||
|
||||
case IPv4OptionExperimentType:
|
||||
if optLen != IPv4OptionExperimentLength {
|
||||
i.ErrCursor++
|
||||
return nil, false, &IPv4OptParameterProblem{
|
||||
Pointer: i.ErrCursor,
|
||||
NeedICMP: true,
|
||||
}
|
||||
}
|
||||
retval := IPv4OptionExperiment(optionBody)
|
||||
return &retval, false, nil
|
||||
}
|
||||
retval := IPv4OptionGeneric(optionBody)
|
||||
return &retval, false, nil
|
||||
@@ -1074,6 +1088,35 @@ func (ra *IPv4OptionRouterAlert) Value() uint16 {
|
||||
return binary.BigEndian.Uint16(ra.Contents()[IPv4OptionRouterAlertValueOffset:])
|
||||
}
|
||||
|
||||
// Experiment option specific related constants.
|
||||
const (
|
||||
// IPv4OptionExperimentLength is the length of an Experiment option.
|
||||
IPv4OptionExperimentLength = 4
|
||||
|
||||
// IPv4OptionExperimentValueOffset is the offset for the value of an
|
||||
// Experiment option.
|
||||
IPv4OptionExperimentValueOffset = 2
|
||||
)
|
||||
|
||||
var _ IPv4Option = (*IPv4OptionExperiment)(nil)
|
||||
|
||||
// IPv4OptionExperiment is an IPv4 option defined by RFC 4727.
|
||||
type IPv4OptionExperiment []byte
|
||||
|
||||
// Type implements IPv4Option.
|
||||
func (*IPv4OptionExperiment) Type() IPv4OptionType { return IPv4OptionExperimentType }
|
||||
|
||||
// Size implements IPv4Option.
|
||||
func (*IPv4OptionExperiment) Size() uint8 { return uint8(IPv4OptionExperimentLength) }
|
||||
|
||||
// Contents implements IPv4Option.
|
||||
func (ex *IPv4OptionExperiment) Contents() []byte { return *ex }
|
||||
|
||||
// Value returns the value of the IPv4OptionRouterAlert.
|
||||
func (ex *IPv4OptionExperiment) Value() uint16 {
|
||||
return binary.BigEndian.Uint16(ex.Contents()[IPv4OptionExperimentValueOffset:])
|
||||
}
|
||||
|
||||
// IPv4SerializableOption is an interface to represent serializable IPv4 option
|
||||
// types.
|
||||
type IPv4SerializableOption interface {
|
||||
@@ -1179,6 +1222,28 @@ func (o *IPv4SerializableRouterAlertOption) serializeInto(buffer []byte) uint8 {
|
||||
return o.length()
|
||||
}
|
||||
|
||||
var _ IPv4SerializableOptionPayload = (*IPv4SerializableExperimentOption)(nil)
|
||||
var _ IPv4SerializableOption = (*IPv4SerializableExperimentOption)(nil)
|
||||
|
||||
// IPv4SerializableExperimentOption provides serialization for the IPv4
|
||||
// Experiment option.
|
||||
type IPv4SerializableExperimentOption struct {
|
||||
Tag uint16
|
||||
}
|
||||
|
||||
func (*IPv4SerializableExperimentOption) optionType() IPv4OptionType {
|
||||
return IPv4OptionExperimentType
|
||||
}
|
||||
|
||||
func (*IPv4SerializableExperimentOption) length() uint8 {
|
||||
return IPv4OptionExperimentLength - IPv4OptionExperimentValueOffset
|
||||
}
|
||||
|
||||
func (o *IPv4SerializableExperimentOption) serializeInto(buffer []byte) uint8 {
|
||||
binary.BigEndian.PutUint16(buffer, o.Tag)
|
||||
return o.length()
|
||||
}
|
||||
|
||||
var _ IPv4SerializableOption = (*IPv4SerializableNOPOption)(nil)
|
||||
|
||||
// IPv4SerializableNOPOption provides serialization for the IPv4 no-op option.
|
||||
|
||||
@@ -49,6 +49,10 @@ const (
|
||||
// of an IPv6 payload, as per RFC 8200 section 4.7.
|
||||
IPv6NoNextHeaderIdentifier IPv6ExtensionHeaderIdentifier = 59
|
||||
|
||||
// IPv6ExperimentExtHdrIdentifier is the header identifier of an Experiment
|
||||
// extension header, as per RFC 4727 section 3.3.
|
||||
IPv6ExperimentExtHdrIdentifier IPv6ExtensionHeaderIdentifier = 253
|
||||
|
||||
// IPv6UnknownExtHdrIdentifier is reserved by IANA.
|
||||
// https://www.iana.org/assignments/ipv6-parameters/ipv6-parameters.xhtml#extension-header
|
||||
// "254 Use for experimentation and testing [RFC3692][RFC4727]"
|
||||
@@ -411,6 +415,17 @@ type IPv6DestinationOptionsExtHdr struct {
|
||||
// isIPv6PayloadHeader implements IPv6PayloadHeader.isIPv6PayloadHeader.
|
||||
func (IPv6DestinationOptionsExtHdr) isIPv6PayloadHeader() {}
|
||||
|
||||
// IPv6ExperimentExtHdr is a buffer holding the Experiment extension header.
|
||||
type IPv6ExperimentExtHdr struct {
|
||||
Value uint16
|
||||
}
|
||||
|
||||
// Release implements IPv6PayloadHeader.Release.
|
||||
func (IPv6ExperimentExtHdr) Release() {}
|
||||
|
||||
// isIPv6PayloadHeader implements IPv6PayloadHeader.isIPv6PayloadHeader.
|
||||
func (IPv6ExperimentExtHdr) isIPv6PayloadHeader() {}
|
||||
|
||||
// IPv6RoutingExtHdr is a buffer holding the Routing extension header specific
|
||||
// data as outlined in RFC 8200 section 4.4.
|
||||
type IPv6RoutingExtHdr struct {
|
||||
@@ -580,7 +595,7 @@ func (i *IPv6PayloadIterator) Next() (IPv6PayloadHeader, bool, error) {
|
||||
// Is the header we are parsing a known extension header?
|
||||
switch i.nextHdrIdentifier {
|
||||
case IPv6HopByHopOptionsExtHdrIdentifier:
|
||||
nextHdrIdentifier, view, err := i.nextHeaderData(false /* fragmentHdr */, nil)
|
||||
nextHdrIdentifier, view, err := i.nextHeaderData(false /* ignoreLength */, nil)
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
@@ -588,7 +603,7 @@ func (i *IPv6PayloadIterator) Next() (IPv6PayloadHeader, bool, error) {
|
||||
i.nextHdrIdentifier = nextHdrIdentifier
|
||||
return IPv6HopByHopOptionsExtHdr{ipv6OptionsExtHdr{view}}, false, nil
|
||||
case IPv6RoutingExtHdrIdentifier:
|
||||
nextHdrIdentifier, view, err := i.nextHeaderData(false /* fragmentHdr */, nil)
|
||||
nextHdrIdentifier, view, err := i.nextHeaderData(false /* ignoreLength */, nil)
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
@@ -599,7 +614,7 @@ func (i *IPv6PayloadIterator) Next() (IPv6PayloadHeader, bool, error) {
|
||||
var data [6]byte
|
||||
// We ignore the returned bytes because we know the fragment extension
|
||||
// header specific data will fit in data.
|
||||
nextHdrIdentifier, _, err := i.nextHeaderData(true /* fragmentHdr */, data[:])
|
||||
nextHdrIdentifier, _, err := i.nextHeaderData(true /* ignoreLength */, data[:])
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
@@ -618,13 +633,24 @@ func (i *IPv6PayloadIterator) Next() (IPv6PayloadHeader, bool, error) {
|
||||
i.nextHdrIdentifier = nextHdrIdentifier
|
||||
return fragmentExtHdr, false, nil
|
||||
case IPv6DestinationOptionsExtHdrIdentifier:
|
||||
nextHdrIdentifier, view, err := i.nextHeaderData(false /* fragmentHdr */, nil)
|
||||
nextHdrIdentifier, view, err := i.nextHeaderData(false /* ignoreLength */, nil)
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
|
||||
i.nextHdrIdentifier = nextHdrIdentifier
|
||||
return IPv6DestinationOptionsExtHdr{ipv6OptionsExtHdr{view}}, false, nil
|
||||
case IPv6ExperimentExtHdrIdentifier:
|
||||
var data [IPv6ExperimentHdrLength - ipv6ExperimentHdrValueOffset]byte
|
||||
nextHdrIdentifier, _, err := i.nextHeaderData(true /* ignoreLength */, data[:])
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
i.nextHdrIdentifier = nextHdrIdentifier
|
||||
hdr := IPv6ExperimentExtHdr{
|
||||
Value: binary.BigEndian.Uint16(data[:ipv6ExperimentHdrTagLength]),
|
||||
}
|
||||
return hdr, false, nil
|
||||
case IPv6NoNextHeaderIdentifier:
|
||||
// This indicates the end of the IPv6 payload.
|
||||
return nil, true, nil
|
||||
@@ -644,14 +670,14 @@ func (i *IPv6PayloadIterator) NextHeaderIdentifier() IPv6ExtensionHeaderIdentifi
|
||||
|
||||
// nextHeaderData returns the extension header's Next Header field and raw data.
|
||||
//
|
||||
// fragmentHdr indicates that the extension header being parsed is the Fragment
|
||||
// extension header so the Length field should be ignored as it is Reserved
|
||||
// for the Fragment extension header.
|
||||
// ignoreLength indicates that the extension header being parsed should ignore
|
||||
// the Length field as it is reserved. This is for the Fragment and Experiment
|
||||
// extension headers.
|
||||
//
|
||||
// If bytes is not nil, extension header specific data will be read into bytes
|
||||
// if it has enough capacity. If bytes is provided but does not have enough
|
||||
// capacity for the data, nextHeaderData will panic.
|
||||
func (i *IPv6PayloadIterator) nextHeaderData(fragmentHdr bool, bytes []byte) (IPv6ExtensionHeaderIdentifier, *buffer.View, error) {
|
||||
func (i *IPv6PayloadIterator) nextHeaderData(ignoreLength bool, bytes []byte) (IPv6ExtensionHeaderIdentifier, *buffer.View, error) {
|
||||
// We ignore the number of bytes read because we know we will only ever read
|
||||
// at max 1 bytes since rune has a length of 1. If we read 0 bytes, the Read
|
||||
// would return io.EOF to indicate that io.Reader has reached the end of the
|
||||
@@ -667,13 +693,13 @@ func (i *IPv6PayloadIterator) nextHeaderData(fragmentHdr bool, bytes []byte) (IP
|
||||
length, err = rdr.ReadByte()
|
||||
|
||||
if err != nil {
|
||||
if fragmentHdr {
|
||||
if ignoreLength {
|
||||
return 0, nil, fmt.Errorf("error when reading the Length field for extension header with id = %d: %w", i.nextHdrIdentifier, err)
|
||||
}
|
||||
|
||||
return 0, nil, fmt.Errorf("error when reading the Reserved field for extension header with id = %d: %w", i.nextHdrIdentifier, err)
|
||||
}
|
||||
if fragmentHdr {
|
||||
if ignoreLength {
|
||||
length = 0
|
||||
}
|
||||
|
||||
@@ -689,7 +715,7 @@ func (i *IPv6PayloadIterator) nextHeaderData(fragmentHdr bool, bytes []byte) (IP
|
||||
i.nextOffset += uint32((length + 1) * ipv6ExtHdrLenBytesPerUnit)
|
||||
|
||||
bytesLen := int(length)*ipv6ExtHdrLenBytesPerUnit + ipv6ExtHdrLenBytesExcluded
|
||||
if fragmentHdr {
|
||||
if ignoreLength {
|
||||
if n := len(bytes); n < bytesLen {
|
||||
panic(fmt.Sprintf("bytes only has space for %d bytes but need space for %d bytes (length = %d) for extension header with id = %d", n, bytesLen, length, i.nextHdrIdentifier))
|
||||
}
|
||||
@@ -735,6 +761,36 @@ type IPv6SerializableExtHdr interface {
|
||||
serializeInto(nextHeader uint8, b []byte) int
|
||||
}
|
||||
|
||||
// ipv6RouterAlertPayloadLength is the length of the Router Alert payload
|
||||
// as defined in RFC 4727 section 3.3.
|
||||
const (
|
||||
IPv6ExperimentHdrLength = 8
|
||||
ipv6ExperimentNextHeaderOffset = 0
|
||||
ipv6ExperimentLengthOffset = 1
|
||||
ipv6ExperimentHdrValueOffset = 2
|
||||
ipv6ExperimentHdrTagLength = 2
|
||||
)
|
||||
|
||||
var _ IPv6SerializableExtHdr = (*IPv6ExperimentExtHdr)(nil)
|
||||
|
||||
// identifier implements IPv6SerializableExtHdr.
|
||||
func (h IPv6ExperimentExtHdr) identifier() IPv6ExtensionHeaderIdentifier {
|
||||
return IPv6ExperimentExtHdrIdentifier
|
||||
}
|
||||
|
||||
// length implements IPv6SerializableExtHdr.
|
||||
func (h IPv6ExperimentExtHdr) length() int {
|
||||
return IPv6ExperimentHdrLength
|
||||
}
|
||||
|
||||
// serializeInto implements IPv6SerializableExtHdr.
|
||||
func (h IPv6ExperimentExtHdr) serializeInto(nextHeader uint8, b []byte) int {
|
||||
b[ipv6ExperimentNextHeaderOffset] = nextHeader
|
||||
b[ipv6ExperimentLengthOffset] = (IPv6ExperimentHdrLength / ipv6ExtHdrLenBytesPerUnit) - 1
|
||||
binary.BigEndian.PutUint16(b[ipv6ExperimentHdrValueOffset:][:ipv6ExperimentHdrTagLength], uint16(h.Value))
|
||||
return IPv6ExperimentHdrLength
|
||||
}
|
||||
|
||||
var _ IPv6SerializableExtHdr = (*IPv6SerializableHopByHopExtHdr)(nil)
|
||||
|
||||
// IPv6SerializableHopByHopExtHdr implements serialization of the Hop by Hop
|
||||
|
||||
+2
-9
@@ -17,7 +17,6 @@ package header
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/google/btree"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/checksum"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/seqnum"
|
||||
@@ -175,11 +174,6 @@ type SACKBlock struct {
|
||||
End seqnum.Value
|
||||
}
|
||||
|
||||
// Less returns true if r.Start < b.Start.
|
||||
func (r SACKBlock) Less(b btree.Item) bool {
|
||||
return r.Start.LessThan(b.(SACKBlock).Start)
|
||||
}
|
||||
|
||||
// Contains returns true if b is completely contained in r.
|
||||
func (r SACKBlock) Contains(b SACKBlock) bool {
|
||||
return r.Start.LessThanEq(b.Start) && b.End.LessThanEq(r.End)
|
||||
@@ -219,9 +213,8 @@ const (
|
||||
// TCPTotalHeaderMaximumSize is the maximum size of headers from all layers in
|
||||
// a TCP packet. It analogous to MAX_TCP_HEADER in Linux.
|
||||
//
|
||||
// TODO(b/319936470): Investigate why this needs to be at least 140 bytes. In
|
||||
// Linux this value is at least 160, but in theory we should be able to use
|
||||
// 138. In practice anything less than 140 starts to break GSO on gVNIC
|
||||
// Note: In Linux this value is at least 160, but in theory we should be able
|
||||
// to use 138. In practice anything less than 140 starts to break GSO on gVNIC
|
||||
// hardware.
|
||||
TCPTotalHeaderMaximumSize = 160
|
||||
|
||||
|
||||
+4
-4
@@ -87,8 +87,8 @@ func (v VirtioNetHeader) CSumOffset() uint16 {
|
||||
func (v VirtioNetHeader) Encode(f *VirtioNetHeaderFields) {
|
||||
v[flags] = uint8(f.Flags)
|
||||
v[gsoType] = uint8(f.GSOType)
|
||||
binary.BigEndian.PutUint16(v[hdrLen:], f.HdrLen)
|
||||
binary.BigEndian.PutUint16(v[gsoSize:], f.GSOSize)
|
||||
binary.BigEndian.PutUint16(v[csumStart:], f.CSumStart)
|
||||
binary.BigEndian.PutUint16(v[csumOffset:], f.CSumOffset)
|
||||
binary.LittleEndian.PutUint16(v[hdrLen:], f.HdrLen)
|
||||
binary.LittleEndian.PutUint16(v[gsoSize:], f.GSOSize)
|
||||
binary.LittleEndian.PutUint16(v[csumStart:], f.CSumStart)
|
||||
binary.LittleEndian.PutUint16(v[csumOffset:], f.CSumOffset)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user