Merge pull request #597 from evidolob/add-icmp-forwarder

Add icmp forwarder
This commit is contained in:
openshift-merge-bot[bot]
2026-04-16 15:28:40 +00:00
committed by GitHub
210 changed files with 8317 additions and 2249 deletions
+4
View File
@@ -2,3 +2,7 @@ bin/
capture.pcap
tmp/
test/qcon.log
.idea/
.vscode/
test-qemu/qcon.log
test-qemu/ovmf_vars.fd
+5 -4
View File
@@ -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
)
+8 -6
View File
@@ -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=
+73
View File
@@ -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,
}
}
+40
View File
@@ -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)
}
+242
View File
@@ -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
}
+4 -3
View File
@@ -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
})
}
+3
View File
@@ -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 {
+32
View File
@@ -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())
})
})
+28
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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++
}
+31 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
+51
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
+14
View File
@@ -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)
}
+44
View File
@@ -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
@@ -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
)
+79
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
}
-26
View File
@@ -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
@@ -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
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
}
}
}
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+2 -1
View File
@@ -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
+2 -1
View File
@@ -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
View File
@@ -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
+2 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
@@ -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
)
@@ -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
)
+3 -2
View File
@@ -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
View File
@@ -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
@@ -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.
+7 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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.
//
+1 -1
View File
@@ -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
View File
@@ -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)
+65
View File
@@ -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
View File
@@ -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
View File
@@ -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