snapshot: sagernet/gvisor v0.0.0-20250811.0-sing-box-mod.1
Содержимое пина, зафиксированного в go.mod sing-box-lx, одним коммитом без истории. Полная история SagerNet/gvisor — 1.45 ГБ и клонируется в каждой CI-джобе; наша дельта — одна вставка в одну функцию, история для неё не нужна. Module path github.com/sagernet/gvisor сохранён намеренно: на него опирается replace-директива суперпроекта. Патч поверх — отдельным коммитом, чтобы дельта читалась одним git show и переносилась на новый пин копированием. SPECS/TASKS/048-GVISOR_HANDSHAKE_NIL_CRASH
This commit is contained in:
commit
2c4ae3b0a4
712 changed files with 185689 additions and 0 deletions
416
pkg/tcpip/network/arp/arp.go
Normal file
416
pkg/tcpip/network/arp/arp.go
Normal file
|
|
@ -0,0 +1,416 @@
|
|||
// 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 arp implements the ARP network protocol. It is used to resolve
|
||||
// IPv4 addresses into link-local MAC addresses, and advertises IPv4
|
||||
// addresses of its stack with the local network.
|
||||
package arp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/atomicbitops"
|
||||
"github.com/sagernet/gvisor/pkg/sync"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header/parse"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/network/internal/ip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
const (
|
||||
// ProtocolNumber is the ARP protocol number.
|
||||
ProtocolNumber = header.ARPProtocolNumber
|
||||
)
|
||||
|
||||
var (
|
||||
_ stack.DuplicateAddressDetector = (*endpoint)(nil)
|
||||
_ stack.LinkAddressResolver = (*endpoint)(nil)
|
||||
_ ip.DADProtocol = (*endpoint)(nil)
|
||||
)
|
||||
|
||||
// ARP endpoints need to implement stack.NetworkEndpoint because the stack
|
||||
// considers the layer above the link-layer a network layer; the only
|
||||
// facility provided by the stack to deliver packets to a layer above
|
||||
// the link-layer is via stack.NetworkEndpoint.HandlePacket.
|
||||
var _ stack.NetworkEndpoint = (*endpoint)(nil)
|
||||
|
||||
// +stateify savable
|
||||
type endpoint struct {
|
||||
protocol *protocol
|
||||
|
||||
// enabled is set to 1 when the NIC is enabled and 0 when it is disabled.
|
||||
enabled atomicbitops.Uint32
|
||||
|
||||
nic stack.NetworkInterface
|
||||
stats sharedStats
|
||||
|
||||
// mu protects annotated fields below.
|
||||
mu sync.Mutex `state:"nosave"`
|
||||
|
||||
// +checklocks:mu
|
||||
dad ip.DAD
|
||||
}
|
||||
|
||||
// CheckDuplicateAddress implements stack.DuplicateAddressDetector.
|
||||
func (e *endpoint) CheckDuplicateAddress(addr tcpip.Address, h stack.DADCompletionHandler) stack.DADCheckAddressDisposition {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
return e.dad.CheckDuplicateAddressLocked(addr, h)
|
||||
}
|
||||
|
||||
// SetDADConfigurations implements stack.DuplicateAddressDetector.
|
||||
func (e *endpoint) SetDADConfigurations(c stack.DADConfigurations) {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
e.dad.SetConfigsLocked(c)
|
||||
}
|
||||
|
||||
// DuplicateAddressProtocol implements stack.DuplicateAddressDetector.
|
||||
func (*endpoint) DuplicateAddressProtocol() tcpip.NetworkProtocolNumber {
|
||||
return header.IPv4ProtocolNumber
|
||||
}
|
||||
|
||||
// SendDADMessage implements ip.DADProtocol.
|
||||
func (e *endpoint) SendDADMessage(addr tcpip.Address, _ []byte) tcpip.Error {
|
||||
return e.sendARPRequest(header.IPv4Any, addr, header.EthernetBroadcastAddress)
|
||||
}
|
||||
|
||||
func (e *endpoint) Enable() tcpip.Error {
|
||||
if !e.nic.Enabled() {
|
||||
return &tcpip.ErrNotPermitted{}
|
||||
}
|
||||
|
||||
e.setEnabled(true)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *endpoint) Enabled() bool {
|
||||
return e.nic.Enabled() && e.isEnabled()
|
||||
}
|
||||
|
||||
// isEnabled returns true if the endpoint is enabled, regardless of the
|
||||
// enabled status of the NIC.
|
||||
func (e *endpoint) isEnabled() bool {
|
||||
return e.enabled.Load() == 1
|
||||
}
|
||||
|
||||
// setEnabled sets the enabled status for the endpoint.
|
||||
func (e *endpoint) setEnabled(v bool) {
|
||||
if v {
|
||||
e.enabled.Store(1)
|
||||
} else {
|
||||
e.enabled.Store(0)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *endpoint) Disable() {
|
||||
e.setEnabled(false)
|
||||
}
|
||||
|
||||
// DefaultTTL is unused for ARP. It implements stack.NetworkEndpoint.
|
||||
func (*endpoint) DefaultTTL() uint8 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (e *endpoint) MTU() uint32 {
|
||||
lmtu := e.nic.MTU()
|
||||
return lmtu - uint32(e.MaxHeaderLength())
|
||||
}
|
||||
|
||||
func (e *endpoint) MaxHeaderLength() uint16 {
|
||||
return e.nic.MaxHeaderLength() + header.ARPSize
|
||||
}
|
||||
|
||||
func (*endpoint) Close() {}
|
||||
|
||||
func (*endpoint) WritePacket(*stack.Route, stack.NetworkHeaderParams, *stack.PacketBuffer) tcpip.Error {
|
||||
return &tcpip.ErrNotSupported{}
|
||||
}
|
||||
|
||||
// NetworkProtocolNumber implements stack.NetworkEndpoint.NetworkProtocolNumber.
|
||||
func (*endpoint) NetworkProtocolNumber() tcpip.NetworkProtocolNumber {
|
||||
return ProtocolNumber
|
||||
}
|
||||
|
||||
func (*endpoint) WriteHeaderIncludedPacket(*stack.Route, *stack.PacketBuffer) tcpip.Error {
|
||||
return &tcpip.ErrNotSupported{}
|
||||
}
|
||||
|
||||
func (e *endpoint) HandlePacket(pkt *stack.PacketBuffer) {
|
||||
stats := e.stats.arp
|
||||
stats.packetsReceived.Increment()
|
||||
|
||||
if !e.isEnabled() {
|
||||
stats.disabledPacketsReceived.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
if _, _, ok := e.protocol.Parse(pkt); !ok {
|
||||
stats.malformedPacketsReceived.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
h := header.ARP(pkt.NetworkHeader().Slice())
|
||||
if !h.IsValid() {
|
||||
stats.malformedPacketsReceived.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
switch h.Op() {
|
||||
case header.ARPRequest:
|
||||
stats.requestsReceived.Increment()
|
||||
localAddr := tcpip.AddrFrom4Slice(h.ProtocolAddressTarget())
|
||||
|
||||
if !e.nic.CheckLocalAddress(header.IPv4ProtocolNumber, localAddr) {
|
||||
stats.requestsReceivedUnknownTargetAddress.Increment()
|
||||
return // we have no useful answer, ignore the request
|
||||
}
|
||||
|
||||
remoteAddr := tcpip.AddrFrom4Slice(h.ProtocolAddressSender())
|
||||
remoteLinkAddr := tcpip.LinkAddress(h.HardwareAddressSender())
|
||||
|
||||
switch err := e.nic.HandleNeighborProbe(header.IPv4ProtocolNumber, remoteAddr, remoteLinkAddr); err.(type) {
|
||||
case nil:
|
||||
case *tcpip.ErrNotSupported:
|
||||
// The stack may support ARP but the NIC may not need link resolution.
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected error when informing NIC of neighbor probe message: %s", err))
|
||||
}
|
||||
|
||||
respPkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
ReserveHeaderBytes: int(e.nic.MaxHeaderLength()) + header.ARPSize,
|
||||
})
|
||||
defer respPkt.DecRef()
|
||||
packet := header.ARP(respPkt.NetworkHeader().Push(header.ARPSize))
|
||||
respPkt.NetworkProtocolNumber = ProtocolNumber
|
||||
packet.SetIPv4OverEthernet()
|
||||
packet.SetOp(header.ARPReply)
|
||||
// TODO(gvisor.dev/issue/4582): check copied length once TAP devices have a
|
||||
// link address.
|
||||
_ = copy(packet.HardwareAddressSender(), e.nic.LinkAddress())
|
||||
if n := copy(packet.ProtocolAddressSender(), h.ProtocolAddressTarget()); n != header.IPv4AddressSize {
|
||||
panic(fmt.Sprintf("copied %d bytes, expected %d bytes", n, header.IPv4AddressSize))
|
||||
}
|
||||
origSender := h.HardwareAddressSender()
|
||||
if n := copy(packet.HardwareAddressTarget(), origSender); n != header.EthernetAddressSize {
|
||||
panic(fmt.Sprintf("copied %d bytes, expected %d bytes", n, header.EthernetAddressSize))
|
||||
}
|
||||
if n := copy(packet.ProtocolAddressTarget(), h.ProtocolAddressSender()); n != header.IPv4AddressSize {
|
||||
panic(fmt.Sprintf("copied %d bytes, expected %d bytes", n, header.IPv4AddressSize))
|
||||
}
|
||||
|
||||
// As per RFC 826, under Packet Reception:
|
||||
// Swap hardware and protocol fields, putting the local hardware and
|
||||
// protocol addresses in the sender fields.
|
||||
//
|
||||
// Send the packet to the (new) target hardware address on the same
|
||||
// hardware on which the request was received.
|
||||
if err := e.nic.WritePacketToRemote(tcpip.LinkAddress(origSender), respPkt); err != nil {
|
||||
stats.outgoingRepliesDropped.Increment()
|
||||
} else {
|
||||
stats.outgoingRepliesSent.Increment()
|
||||
}
|
||||
|
||||
case header.ARPReply:
|
||||
stats.repliesReceived.Increment()
|
||||
addr := tcpip.AddrFrom4Slice(h.ProtocolAddressSender())
|
||||
linkAddr := tcpip.LinkAddress(h.HardwareAddressSender())
|
||||
|
||||
e.mu.Lock()
|
||||
e.dad.StopLocked(addr, &stack.DADDupAddrDetected{HolderLinkAddress: linkAddr})
|
||||
e.mu.Unlock()
|
||||
|
||||
switch err := e.nic.HandleNeighborConfirmation(header.IPv4ProtocolNumber, addr, linkAddr, stack.ReachabilityConfirmationFlags{
|
||||
// Only unicast ARP replies are considered solicited. Broadcast replies
|
||||
// are gratuitous ARP replies and should not move neighbor entries to the
|
||||
// reachable state.
|
||||
Solicited: pkt.PktType == tcpip.PacketHost,
|
||||
// If a different link address is received than the one cached, the entry
|
||||
// should always go to Stale.
|
||||
Override: false,
|
||||
// ARP does not distinguish between router and non-router hosts.
|
||||
IsRouter: false,
|
||||
}); err.(type) {
|
||||
case nil:
|
||||
case *tcpip.ErrNotSupported:
|
||||
// The stack may support ARP but the NIC may not need link resolution.
|
||||
default:
|
||||
panic(fmt.Sprintf("unexpected error when informing NIC of neighbor confirmation message: %s", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stats implements stack.NetworkEndpoint.
|
||||
func (e *endpoint) Stats() stack.NetworkEndpointStats {
|
||||
return &e.stats.localStats
|
||||
}
|
||||
|
||||
var _ stack.NetworkProtocol = (*protocol)(nil)
|
||||
|
||||
// +stateify savable
|
||||
type protocol struct {
|
||||
stack *stack.Stack
|
||||
options Options
|
||||
}
|
||||
|
||||
func (p *protocol) Number() tcpip.NetworkProtocolNumber { return ProtocolNumber }
|
||||
func (p *protocol) MinimumPacketSize() int { return header.ARPSize }
|
||||
|
||||
func (*protocol) ParseAddresses([]byte) (src, dst tcpip.Address) {
|
||||
return tcpip.Address{}, tcpip.Address{}
|
||||
}
|
||||
|
||||
func (p *protocol) NewEndpoint(nic stack.NetworkInterface, _ stack.TransportDispatcher) stack.NetworkEndpoint {
|
||||
e := &endpoint{
|
||||
protocol: p,
|
||||
nic: nic,
|
||||
}
|
||||
|
||||
e.mu.Lock()
|
||||
e.dad.Init(&e.mu, p.options.DADConfigs, ip.DADOptions{
|
||||
Clock: p.stack.Clock(),
|
||||
SecureRNG: p.stack.SecureRNG().Reader,
|
||||
// ARP does not support sending nonce values.
|
||||
NonceSize: 0,
|
||||
Protocol: e,
|
||||
NICID: nic.ID(),
|
||||
})
|
||||
e.mu.Unlock()
|
||||
|
||||
tcpip.InitStatCounters(reflect.ValueOf(&e.stats.localStats).Elem())
|
||||
|
||||
stackStats := p.stack.Stats()
|
||||
e.stats.arp.init(&e.stats.localStats.ARP, &stackStats.ARP)
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// LinkAddressProtocol implements stack.LinkAddressResolver.LinkAddressProtocol.
|
||||
func (*endpoint) LinkAddressProtocol() tcpip.NetworkProtocolNumber {
|
||||
return header.IPv4ProtocolNumber
|
||||
}
|
||||
|
||||
// LinkAddressRequest implements stack.LinkAddressResolver.LinkAddressRequest.
|
||||
func (e *endpoint) LinkAddressRequest(targetAddr, localAddr tcpip.Address, remoteLinkAddr tcpip.LinkAddress) tcpip.Error {
|
||||
stats := e.stats.arp
|
||||
|
||||
if len(remoteLinkAddr) == 0 {
|
||||
remoteLinkAddr = header.EthernetBroadcastAddress
|
||||
}
|
||||
|
||||
if localAddr.BitLen() == 0 {
|
||||
addr, err := e.nic.PrimaryAddress(header.IPv4ProtocolNumber)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if addr.Address.BitLen() == 0 {
|
||||
stats.outgoingRequestInterfaceHasNoLocalAddressErrors.Increment()
|
||||
return &tcpip.ErrNetworkUnreachable{}
|
||||
}
|
||||
|
||||
localAddr = addr.Address
|
||||
} else if !e.nic.CheckLocalAddress(header.IPv4ProtocolNumber, localAddr) {
|
||||
stats.outgoingRequestBadLocalAddressErrors.Increment()
|
||||
return &tcpip.ErrBadLocalAddress{}
|
||||
}
|
||||
|
||||
return e.sendARPRequest(localAddr, targetAddr, remoteLinkAddr)
|
||||
}
|
||||
|
||||
func (e *endpoint) sendARPRequest(localAddr, targetAddr tcpip.Address, remoteLinkAddr tcpip.LinkAddress) tcpip.Error {
|
||||
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
ReserveHeaderBytes: int(e.MaxHeaderLength()),
|
||||
})
|
||||
defer pkt.DecRef()
|
||||
h := header.ARP(pkt.NetworkHeader().Push(header.ARPSize))
|
||||
pkt.NetworkProtocolNumber = ProtocolNumber
|
||||
h.SetIPv4OverEthernet()
|
||||
h.SetOp(header.ARPRequest)
|
||||
// TODO(gvisor.dev/issue/4582): check copied length once TAP devices have a
|
||||
// link address.
|
||||
_ = copy(h.HardwareAddressSender(), e.nic.LinkAddress())
|
||||
if n := copy(h.ProtocolAddressSender(), localAddr.AsSlice()); n != header.IPv4AddressSize {
|
||||
panic(fmt.Sprintf("copied %d bytes, expected %d bytes", n, header.IPv4AddressSize))
|
||||
}
|
||||
if n := copy(h.ProtocolAddressTarget(), targetAddr.AsSlice()); n != header.IPv4AddressSize {
|
||||
panic(fmt.Sprintf("copied %d bytes, expected %d bytes", n, header.IPv4AddressSize))
|
||||
}
|
||||
|
||||
stats := e.stats.arp
|
||||
if err := e.nic.WritePacketToRemote(remoteLinkAddr, pkt); err != nil {
|
||||
stats.outgoingRequestsDropped.Increment()
|
||||
return err
|
||||
}
|
||||
stats.outgoingRequestsSent.Increment()
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResolveStaticAddress implements stack.LinkAddressResolver.ResolveStaticAddress.
|
||||
func (*endpoint) ResolveStaticAddress(addr tcpip.Address) (tcpip.LinkAddress, bool) {
|
||||
if addr == header.IPv4Broadcast {
|
||||
return header.EthernetBroadcastAddress, true
|
||||
}
|
||||
if header.IsV4MulticastAddress(addr) {
|
||||
return header.EthernetAddressFromMulticastIPv4Address(addr), true
|
||||
}
|
||||
return tcpip.LinkAddress([]byte(nil)), false
|
||||
}
|
||||
|
||||
// SetOption implements stack.NetworkProtocol.SetOption.
|
||||
func (*protocol) SetOption(tcpip.SettableNetworkProtocolOption) tcpip.Error {
|
||||
return &tcpip.ErrUnknownProtocolOption{}
|
||||
}
|
||||
|
||||
// Option implements stack.NetworkProtocol.Option.
|
||||
func (*protocol) Option(tcpip.GettableNetworkProtocolOption) tcpip.Error {
|
||||
return &tcpip.ErrUnknownProtocolOption{}
|
||||
}
|
||||
|
||||
// Close implements stack.TransportProtocol.Close.
|
||||
func (*protocol) Close() {}
|
||||
|
||||
// Wait implements stack.TransportProtocol.Wait.
|
||||
func (*protocol) Wait() {}
|
||||
|
||||
// Parse implements stack.NetworkProtocol.Parse.
|
||||
func (*protocol) Parse(pkt *stack.PacketBuffer) (proto tcpip.TransportProtocolNumber, hasTransportHdr bool, ok bool) {
|
||||
return 0, false, parse.ARP(pkt)
|
||||
}
|
||||
|
||||
// Options holds options to configure a protocol.
|
||||
//
|
||||
// +stateify savable
|
||||
type Options struct {
|
||||
// DADConfigs is the default DAD configurations used by ARP endpoints.
|
||||
DADConfigs stack.DADConfigurations
|
||||
}
|
||||
|
||||
// NewProtocolWithOptions returns an ARP network protocol factory that
|
||||
// will return an ARP network protocol with the provided options.
|
||||
func NewProtocolWithOptions(opts Options) stack.NetworkProtocolFactory {
|
||||
return func(s *stack.Stack) stack.NetworkProtocol {
|
||||
return &protocol{
|
||||
stack: s,
|
||||
options: opts,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NewProtocol returns an ARP network protocol.
|
||||
func NewProtocol(s *stack.Stack) stack.NetworkProtocol {
|
||||
return NewProtocolWithOptions(Options{})(s)
|
||||
}
|
||||
219
pkg/tcpip/network/arp/arp_state_autogen.go
Normal file
219
pkg/tcpip/network/arp/arp_state_autogen.go
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
// automatically generated by stateify.
|
||||
|
||||
package arp
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/state"
|
||||
)
|
||||
|
||||
func (e *endpoint) StateTypeName() string {
|
||||
return "pkg/tcpip/network/arp.endpoint"
|
||||
}
|
||||
|
||||
func (e *endpoint) StateFields() []string {
|
||||
return []string{
|
||||
"protocol",
|
||||
"enabled",
|
||||
"nic",
|
||||
"stats",
|
||||
"dad",
|
||||
}
|
||||
}
|
||||
|
||||
func (e *endpoint) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *endpoint) StateSave(stateSinkObject state.Sink) {
|
||||
e.beforeSave()
|
||||
stateSinkObject.Save(0, &e.protocol)
|
||||
stateSinkObject.Save(1, &e.enabled)
|
||||
stateSinkObject.Save(2, &e.nic)
|
||||
stateSinkObject.Save(3, &e.stats)
|
||||
stateSinkObject.Save(4, &e.dad)
|
||||
}
|
||||
|
||||
func (e *endpoint) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *endpoint) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &e.protocol)
|
||||
stateSourceObject.Load(1, &e.enabled)
|
||||
stateSourceObject.Load(2, &e.nic)
|
||||
stateSourceObject.Load(3, &e.stats)
|
||||
stateSourceObject.Load(4, &e.dad)
|
||||
}
|
||||
|
||||
func (p *protocol) StateTypeName() string {
|
||||
return "pkg/tcpip/network/arp.protocol"
|
||||
}
|
||||
|
||||
func (p *protocol) StateFields() []string {
|
||||
return []string{
|
||||
"stack",
|
||||
"options",
|
||||
}
|
||||
}
|
||||
|
||||
func (p *protocol) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (p *protocol) StateSave(stateSinkObject state.Sink) {
|
||||
p.beforeSave()
|
||||
stateSinkObject.Save(0, &p.stack)
|
||||
stateSinkObject.Save(1, &p.options)
|
||||
}
|
||||
|
||||
func (p *protocol) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (p *protocol) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &p.stack)
|
||||
stateSourceObject.Load(1, &p.options)
|
||||
}
|
||||
|
||||
func (o *Options) StateTypeName() string {
|
||||
return "pkg/tcpip/network/arp.Options"
|
||||
}
|
||||
|
||||
func (o *Options) StateFields() []string {
|
||||
return []string{
|
||||
"DADConfigs",
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Options) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (o *Options) StateSave(stateSinkObject state.Sink) {
|
||||
o.beforeSave()
|
||||
stateSinkObject.Save(0, &o.DADConfigs)
|
||||
}
|
||||
|
||||
func (o *Options) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (o *Options) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &o.DADConfigs)
|
||||
}
|
||||
|
||||
func (s *Stats) StateTypeName() string {
|
||||
return "pkg/tcpip/network/arp.Stats"
|
||||
}
|
||||
|
||||
func (s *Stats) StateFields() []string {
|
||||
return []string{
|
||||
"ARP",
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Stats) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (s *Stats) StateSave(stateSinkObject state.Sink) {
|
||||
s.beforeSave()
|
||||
stateSinkObject.Save(0, &s.ARP)
|
||||
}
|
||||
|
||||
func (s *Stats) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (s *Stats) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &s.ARP)
|
||||
}
|
||||
|
||||
func (s *sharedStats) StateTypeName() string {
|
||||
return "pkg/tcpip/network/arp.sharedStats"
|
||||
}
|
||||
|
||||
func (s *sharedStats) StateFields() []string {
|
||||
return []string{
|
||||
"localStats",
|
||||
"arp",
|
||||
}
|
||||
}
|
||||
|
||||
func (s *sharedStats) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (s *sharedStats) StateSave(stateSinkObject state.Sink) {
|
||||
s.beforeSave()
|
||||
stateSinkObject.Save(0, &s.localStats)
|
||||
stateSinkObject.Save(1, &s.arp)
|
||||
}
|
||||
|
||||
func (s *sharedStats) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (s *sharedStats) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &s.localStats)
|
||||
stateSourceObject.Load(1, &s.arp)
|
||||
}
|
||||
|
||||
func (m *multiCounterARPStats) StateTypeName() string {
|
||||
return "pkg/tcpip/network/arp.multiCounterARPStats"
|
||||
}
|
||||
|
||||
func (m *multiCounterARPStats) StateFields() []string {
|
||||
return []string{
|
||||
"packetsReceived",
|
||||
"disabledPacketsReceived",
|
||||
"malformedPacketsReceived",
|
||||
"requestsReceived",
|
||||
"requestsReceivedUnknownTargetAddress",
|
||||
"outgoingRequestInterfaceHasNoLocalAddressErrors",
|
||||
"outgoingRequestBadLocalAddressErrors",
|
||||
"outgoingRequestsDropped",
|
||||
"outgoingRequestsSent",
|
||||
"repliesReceived",
|
||||
"outgoingRepliesDropped",
|
||||
"outgoingRepliesSent",
|
||||
}
|
||||
}
|
||||
|
||||
func (m *multiCounterARPStats) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (m *multiCounterARPStats) StateSave(stateSinkObject state.Sink) {
|
||||
m.beforeSave()
|
||||
stateSinkObject.Save(0, &m.packetsReceived)
|
||||
stateSinkObject.Save(1, &m.disabledPacketsReceived)
|
||||
stateSinkObject.Save(2, &m.malformedPacketsReceived)
|
||||
stateSinkObject.Save(3, &m.requestsReceived)
|
||||
stateSinkObject.Save(4, &m.requestsReceivedUnknownTargetAddress)
|
||||
stateSinkObject.Save(5, &m.outgoingRequestInterfaceHasNoLocalAddressErrors)
|
||||
stateSinkObject.Save(6, &m.outgoingRequestBadLocalAddressErrors)
|
||||
stateSinkObject.Save(7, &m.outgoingRequestsDropped)
|
||||
stateSinkObject.Save(8, &m.outgoingRequestsSent)
|
||||
stateSinkObject.Save(9, &m.repliesReceived)
|
||||
stateSinkObject.Save(10, &m.outgoingRepliesDropped)
|
||||
stateSinkObject.Save(11, &m.outgoingRepliesSent)
|
||||
}
|
||||
|
||||
func (m *multiCounterARPStats) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (m *multiCounterARPStats) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &m.packetsReceived)
|
||||
stateSourceObject.Load(1, &m.disabledPacketsReceived)
|
||||
stateSourceObject.Load(2, &m.malformedPacketsReceived)
|
||||
stateSourceObject.Load(3, &m.requestsReceived)
|
||||
stateSourceObject.Load(4, &m.requestsReceivedUnknownTargetAddress)
|
||||
stateSourceObject.Load(5, &m.outgoingRequestInterfaceHasNoLocalAddressErrors)
|
||||
stateSourceObject.Load(6, &m.outgoingRequestBadLocalAddressErrors)
|
||||
stateSourceObject.Load(7, &m.outgoingRequestsDropped)
|
||||
stateSourceObject.Load(8, &m.outgoingRequestsSent)
|
||||
stateSourceObject.Load(9, &m.repliesReceived)
|
||||
stateSourceObject.Load(10, &m.outgoingRepliesDropped)
|
||||
stateSourceObject.Load(11, &m.outgoingRepliesSent)
|
||||
}
|
||||
|
||||
func init() {
|
||||
state.Register((*endpoint)(nil))
|
||||
state.Register((*protocol)(nil))
|
||||
state.Register((*Options)(nil))
|
||||
state.Register((*Stats)(nil))
|
||||
state.Register((*sharedStats)(nil))
|
||||
state.Register((*multiCounterARPStats)(nil))
|
||||
}
|
||||
74
pkg/tcpip/network/arp/stats.go
Normal file
74
pkg/tcpip/network/arp/stats.go
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright 2021 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 arp
|
||||
|
||||
import (
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
var _ stack.NetworkEndpointStats = (*Stats)(nil)
|
||||
|
||||
// Stats holds statistics related to ARP.
|
||||
//
|
||||
// +stateify savable
|
||||
type Stats struct {
|
||||
// ARP holds ARP statistics.
|
||||
ARP tcpip.ARPStats
|
||||
}
|
||||
|
||||
// IsNetworkEndpointStats implements stack.NetworkEndpointStats.
|
||||
func (*Stats) IsNetworkEndpointStats() {}
|
||||
|
||||
// +stateify savable
|
||||
type sharedStats struct {
|
||||
localStats Stats
|
||||
arp multiCounterARPStats
|
||||
}
|
||||
|
||||
// LINT.IfChange(multiCounterARPStats)
|
||||
|
||||
// +stateify savable
|
||||
type multiCounterARPStats struct {
|
||||
packetsReceived tcpip.MultiCounterStat
|
||||
disabledPacketsReceived tcpip.MultiCounterStat
|
||||
malformedPacketsReceived tcpip.MultiCounterStat
|
||||
requestsReceived tcpip.MultiCounterStat
|
||||
requestsReceivedUnknownTargetAddress tcpip.MultiCounterStat
|
||||
outgoingRequestInterfaceHasNoLocalAddressErrors tcpip.MultiCounterStat
|
||||
outgoingRequestBadLocalAddressErrors tcpip.MultiCounterStat
|
||||
outgoingRequestsDropped tcpip.MultiCounterStat
|
||||
outgoingRequestsSent tcpip.MultiCounterStat
|
||||
repliesReceived tcpip.MultiCounterStat
|
||||
outgoingRepliesDropped tcpip.MultiCounterStat
|
||||
outgoingRepliesSent tcpip.MultiCounterStat
|
||||
}
|
||||
|
||||
func (m *multiCounterARPStats) init(a, b *tcpip.ARPStats) {
|
||||
m.packetsReceived.Init(a.PacketsReceived, b.PacketsReceived)
|
||||
m.disabledPacketsReceived.Init(a.DisabledPacketsReceived, b.DisabledPacketsReceived)
|
||||
m.malformedPacketsReceived.Init(a.MalformedPacketsReceived, b.MalformedPacketsReceived)
|
||||
m.requestsReceived.Init(a.RequestsReceived, b.RequestsReceived)
|
||||
m.requestsReceivedUnknownTargetAddress.Init(a.RequestsReceivedUnknownTargetAddress, b.RequestsReceivedUnknownTargetAddress)
|
||||
m.outgoingRequestInterfaceHasNoLocalAddressErrors.Init(a.OutgoingRequestInterfaceHasNoLocalAddressErrors, b.OutgoingRequestInterfaceHasNoLocalAddressErrors)
|
||||
m.outgoingRequestBadLocalAddressErrors.Init(a.OutgoingRequestBadLocalAddressErrors, b.OutgoingRequestBadLocalAddressErrors)
|
||||
m.outgoingRequestsDropped.Init(a.OutgoingRequestsDropped, b.OutgoingRequestsDropped)
|
||||
m.outgoingRequestsSent.Init(a.OutgoingRequestsSent, b.OutgoingRequestsSent)
|
||||
m.repliesReceived.Init(a.RepliesReceived, b.RepliesReceived)
|
||||
m.outgoingRepliesDropped.Init(a.OutgoingRepliesDropped, b.OutgoingRepliesDropped)
|
||||
m.outgoingRepliesSent.Init(a.OutgoingRepliesSent, b.OutgoingRepliesSent)
|
||||
}
|
||||
|
||||
// LINT.ThenChange(../../tcpip.go:ARPStats)
|
||||
93
pkg/tcpip/network/hash/hash.go
Normal file
93
pkg/tcpip/network/hash/hash.go
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
// 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 hash contains utility functions for hashing.
|
||||
package hash
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/rand"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||
)
|
||||
|
||||
var hashIV = RandN32(1)[0]
|
||||
|
||||
// RandN32 generates a slice of n cryptographic random 32-bit numbers.
|
||||
func RandN32(n int) []uint32 {
|
||||
b := make([]byte, 4*n)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
panic("unable to get random numbers: " + err.Error())
|
||||
}
|
||||
r := make([]uint32, n)
|
||||
for i := range r {
|
||||
r[i] = binary.LittleEndian.Uint32(b[4*i : (4*i + 4)])
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Hash3Words calculates the Jenkins hash of 3 32-bit words. This is adapted
|
||||
// from linux.
|
||||
func Hash3Words(a, b, c, initval uint32) uint32 {
|
||||
const iv = 0xdeadbeef + (3 << 2)
|
||||
initval += iv
|
||||
|
||||
a += initval
|
||||
b += initval
|
||||
c += initval
|
||||
|
||||
c ^= b
|
||||
c -= rol32(b, 14)
|
||||
a ^= c
|
||||
a -= rol32(c, 11)
|
||||
b ^= a
|
||||
b -= rol32(a, 25)
|
||||
c ^= b
|
||||
c -= rol32(b, 16)
|
||||
a ^= c
|
||||
a -= rol32(c, 4)
|
||||
b ^= a
|
||||
b -= rol32(a, 14)
|
||||
c ^= b
|
||||
c -= rol32(b, 24)
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// IPv4FragmentHash computes the hash of the IPv4 fragment as suggested in RFC 791.
|
||||
func IPv4FragmentHash(h header.IPv4) uint32 {
|
||||
x := uint32(h.ID())<<16 | uint32(h.Protocol())
|
||||
t := h.SourceAddress().As4()
|
||||
y := uint32(t[0]) | uint32(t[1])<<8 | uint32(t[2])<<16 | uint32(t[3])<<24
|
||||
t = h.DestinationAddress().As4()
|
||||
z := uint32(t[0]) | uint32(t[1])<<8 | uint32(t[2])<<16 | uint32(t[3])<<24
|
||||
return Hash3Words(x, y, z, hashIV)
|
||||
}
|
||||
|
||||
// IPv6FragmentHash computes the hash of the ipv6 fragment.
|
||||
// Unlike IPv4, the protocol is not used to compute the hash.
|
||||
// RFC 2640 (sec 4.5) is not very sharp on this aspect.
|
||||
// As a reference, also Linux ignores the protocol to compute
|
||||
// the hash (inet6_hash_frag).
|
||||
func IPv6FragmentHash(h header.IPv6, id uint32) uint32 {
|
||||
t := h.SourceAddress().As16()
|
||||
y := uint32(t[0]) | uint32(t[1])<<8 | uint32(t[2])<<16 | uint32(t[3])<<24
|
||||
t = h.DestinationAddress().As16()
|
||||
z := uint32(t[0]) | uint32(t[1])<<8 | uint32(t[2])<<16 | uint32(t[3])<<24
|
||||
return Hash3Words(id, y, z, hashIV)
|
||||
}
|
||||
|
||||
func rol32(v, shift uint32) uint32 {
|
||||
return (v << shift) | (v >> ((-shift) & 31))
|
||||
}
|
||||
3
pkg/tcpip/network/hash/hash_state_autogen.go
Normal file
3
pkg/tcpip/network/hash/hash_state_autogen.go
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
// automatically generated by stateify.
|
||||
|
||||
package hash
|
||||
375
pkg/tcpip/network/internal/fragmentation/fragmentation.go
Normal file
375
pkg/tcpip/network/internal/fragmentation/fragmentation.go
Normal file
|
|
@ -0,0 +1,375 @@
|
|||
// 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 fragmentation contains the implementation of IP fragmentation.
|
||||
// It is based on RFC 791, RFC 815 and RFC 8200.
|
||||
package fragmentation
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/buffer"
|
||||
"github.com/sagernet/gvisor/pkg/log"
|
||||
"github.com/sagernet/gvisor/pkg/sync"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
const (
|
||||
// HighFragThreshold is the threshold at which we start trimming old
|
||||
// fragmented packets. Linux uses a default value of 4 MB. See
|
||||
// net.ipv4.ipfrag_high_thresh for more information.
|
||||
HighFragThreshold = 4 << 20 // 4MB
|
||||
|
||||
// LowFragThreshold is the threshold we reach to when we start dropping
|
||||
// older fragmented packets. It's important that we keep enough room for newer
|
||||
// packets to be re-assembled. Hence, this needs to be lower than
|
||||
// HighFragThreshold enough. Linux uses a default value of 3 MB. See
|
||||
// net.ipv4.ipfrag_low_thresh for more information.
|
||||
LowFragThreshold = 3 << 20 // 3MB
|
||||
|
||||
// minBlockSize is the minimum block size for fragments.
|
||||
minBlockSize = 1
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrInvalidArgs indicates to the caller that an invalid argument was
|
||||
// provided.
|
||||
ErrInvalidArgs = errors.New("invalid args")
|
||||
|
||||
// ErrFragmentOverlap indicates that, during reassembly, a fragment overlaps
|
||||
// with another one.
|
||||
ErrFragmentOverlap = errors.New("overlapping fragments")
|
||||
|
||||
// ErrFragmentConflict indicates that, during reassembly, some fragments are
|
||||
// in conflict with one another.
|
||||
ErrFragmentConflict = errors.New("conflicting fragments")
|
||||
)
|
||||
|
||||
// FragmentID is the identifier for a fragment.
|
||||
//
|
||||
// +stateify savable
|
||||
type FragmentID struct {
|
||||
// Source is the source address of the fragment.
|
||||
Source tcpip.Address
|
||||
|
||||
// Destination is the destination address of the fragment.
|
||||
Destination tcpip.Address
|
||||
|
||||
// ID is the identification value of the fragment.
|
||||
//
|
||||
// This is a uint32 because IPv6 uses a 32-bit identification value.
|
||||
ID uint32
|
||||
|
||||
// The protocol for the packet.
|
||||
Protocol uint8
|
||||
}
|
||||
|
||||
// Fragmentation is the main structure that other modules
|
||||
// of the stack should use to implement IP Fragmentation.
|
||||
//
|
||||
// +stateify savable
|
||||
type Fragmentation struct {
|
||||
mu sync.Mutex `state:"nosave"`
|
||||
highLimit int
|
||||
lowLimit int
|
||||
reassemblers map[FragmentID]*reassembler
|
||||
rList reassemblerList
|
||||
memSize int
|
||||
timeout time.Duration
|
||||
blockSize uint16
|
||||
clock tcpip.Clock
|
||||
releaseJob *tcpip.Job
|
||||
timeoutHandler TimeoutHandler
|
||||
}
|
||||
|
||||
// TimeoutHandler is consulted if a packet reassembly has timed out.
|
||||
type TimeoutHandler interface {
|
||||
// OnReassemblyTimeout will be called with the first fragment (or nil, if the
|
||||
// first fragment has not been received) of a packet whose reassembly has
|
||||
// timed out.
|
||||
OnReassemblyTimeout(pkt *stack.PacketBuffer)
|
||||
}
|
||||
|
||||
// NewFragmentation creates a new Fragmentation.
|
||||
//
|
||||
// blockSize specifies the fragment block size, in bytes.
|
||||
//
|
||||
// highMemoryLimit specifies the limit on the memory consumed
|
||||
// by the fragments stored by Fragmentation (overhead of internal data-structures
|
||||
// is not accounted). Fragments are dropped when the limit is reached.
|
||||
//
|
||||
// lowMemoryLimit specifies the limit on which we will reach by dropping
|
||||
// fragments after reaching highMemoryLimit.
|
||||
//
|
||||
// reassemblingTimeout specifies the maximum time allowed to reassemble a packet.
|
||||
// Fragments are lazily evicted only when a new a packet with an
|
||||
// already existing fragmentation-id arrives after the timeout.
|
||||
func NewFragmentation(blockSize uint16, highMemoryLimit, lowMemoryLimit int, reassemblingTimeout time.Duration, clock tcpip.Clock, timeoutHandler TimeoutHandler) *Fragmentation {
|
||||
if lowMemoryLimit >= highMemoryLimit {
|
||||
lowMemoryLimit = highMemoryLimit
|
||||
}
|
||||
|
||||
if lowMemoryLimit < 0 {
|
||||
lowMemoryLimit = 0
|
||||
}
|
||||
|
||||
if blockSize < minBlockSize {
|
||||
blockSize = minBlockSize
|
||||
}
|
||||
|
||||
f := &Fragmentation{
|
||||
reassemblers: make(map[FragmentID]*reassembler),
|
||||
highLimit: highMemoryLimit,
|
||||
lowLimit: lowMemoryLimit,
|
||||
timeout: reassemblingTimeout,
|
||||
blockSize: blockSize,
|
||||
clock: clock,
|
||||
timeoutHandler: timeoutHandler,
|
||||
}
|
||||
f.releaseJob = tcpip.NewJob(f.clock, &f.mu, f.releaseReassemblersLocked)
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
// Process processes an incoming fragment belonging to an ID and returns a
|
||||
// complete packet and its protocol number when all the packets belonging to
|
||||
// that ID have been received.
|
||||
//
|
||||
// [first, last] is the range of the fragment bytes.
|
||||
//
|
||||
// first must be a multiple of the block size f is configured with. The size
|
||||
// of the fragment data must be a multiple of the block size, unless there are
|
||||
// no fragments following this fragment (more set to false).
|
||||
//
|
||||
// proto is the protocol number marked in the fragment being processed. It has
|
||||
// to be given here outside of the FragmentID struct because IPv6 should not use
|
||||
// the protocol to identify a fragment.
|
||||
func (f *Fragmentation) Process(
|
||||
id FragmentID, first, last uint16, more bool, proto uint8, pkt *stack.PacketBuffer) (
|
||||
*stack.PacketBuffer, uint8, bool, error,
|
||||
) {
|
||||
if first > last {
|
||||
return nil, 0, false, fmt.Errorf("first=%d is greater than last=%d: %w", first, last, ErrInvalidArgs)
|
||||
}
|
||||
|
||||
if first%f.blockSize != 0 {
|
||||
return nil, 0, false, fmt.Errorf("first=%d is not a multiple of block size=%d: %w", first, f.blockSize, ErrInvalidArgs)
|
||||
}
|
||||
|
||||
fragmentSize := last - first + 1
|
||||
if more && fragmentSize%f.blockSize != 0 {
|
||||
return nil, 0, false, fmt.Errorf("fragment size=%d bytes is not a multiple of block size=%d on non-final fragment: %w", fragmentSize, f.blockSize, ErrInvalidArgs)
|
||||
}
|
||||
|
||||
if l := pkt.Data().Size(); l != int(fragmentSize) {
|
||||
return nil, 0, false, fmt.Errorf("got fragment size=%d bytes not equal to the expected fragment size=%d bytes (first=%d last=%d): %w", l, fragmentSize, first, last, ErrInvalidArgs)
|
||||
}
|
||||
|
||||
f.mu.Lock()
|
||||
if f.reassemblers == nil {
|
||||
return nil, 0, false, fmt.Errorf("Release() called before fragmentation processing could finish")
|
||||
}
|
||||
|
||||
r, ok := f.reassemblers[id]
|
||||
if !ok {
|
||||
r = newReassembler(id, f.clock)
|
||||
f.reassemblers[id] = r
|
||||
wasEmpty := f.rList.Empty()
|
||||
f.rList.PushFront(r)
|
||||
if wasEmpty {
|
||||
// If we have just pushed a first reassembler into an empty list, we
|
||||
// should kickstart the release job. The release job will keep
|
||||
// rescheduling itself until the list becomes empty.
|
||||
f.releaseReassemblersLocked()
|
||||
}
|
||||
}
|
||||
f.mu.Unlock()
|
||||
|
||||
resPkt, firstFragmentProto, done, memConsumed, err := r.process(first, last, more, proto, pkt)
|
||||
if err != nil {
|
||||
// We probably got an invalid sequence of fragments. Just
|
||||
// discard the reassembler and move on.
|
||||
f.mu.Lock()
|
||||
f.release(r, false /* timedOut */)
|
||||
f.mu.Unlock()
|
||||
return nil, 0, false, fmt.Errorf("fragmentation processing error: %w", err)
|
||||
}
|
||||
f.mu.Lock()
|
||||
f.memSize += memConsumed
|
||||
if done {
|
||||
f.release(r, false /* timedOut */)
|
||||
}
|
||||
// Evict reassemblers if we are consuming more memory than highLimit until
|
||||
// we reach lowLimit.
|
||||
if f.memSize > f.highLimit {
|
||||
for f.memSize > f.lowLimit {
|
||||
tail := f.rList.Back()
|
||||
if tail == nil {
|
||||
break
|
||||
}
|
||||
f.release(tail, false /* timedOut */)
|
||||
}
|
||||
}
|
||||
f.mu.Unlock()
|
||||
return resPkt, firstFragmentProto, done, nil
|
||||
}
|
||||
|
||||
// Release releases all underlying resources.
|
||||
func (f *Fragmentation) Release() {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
for _, r := range f.reassemblers {
|
||||
f.release(r, false /* timedOut */)
|
||||
}
|
||||
f.reassemblers = nil
|
||||
}
|
||||
|
||||
func (f *Fragmentation) release(r *reassembler, timedOut bool) {
|
||||
// Before releasing a fragment we need to check if r is already marked as done.
|
||||
// Otherwise, we would delete it twice.
|
||||
if r.checkDoneOrMark() {
|
||||
return
|
||||
}
|
||||
|
||||
delete(f.reassemblers, r.id)
|
||||
f.rList.Remove(r)
|
||||
f.memSize -= r.memSize
|
||||
if f.memSize < 0 {
|
||||
log.Warningf("memory counter < 0 (%d), this is an accounting bug that requires investigation", f.memSize)
|
||||
f.memSize = 0
|
||||
}
|
||||
|
||||
if h := f.timeoutHandler; timedOut && h != nil {
|
||||
h.OnReassemblyTimeout(r.pkt)
|
||||
}
|
||||
if r.pkt != nil {
|
||||
r.pkt.DecRef()
|
||||
r.pkt = nil
|
||||
}
|
||||
for _, h := range r.holes {
|
||||
if h.pkt != nil {
|
||||
h.pkt.DecRef()
|
||||
h.pkt = nil
|
||||
}
|
||||
}
|
||||
r.holes = nil
|
||||
}
|
||||
|
||||
// releaseReassemblersLocked releases already-expired reassemblers, then
|
||||
// schedules the job to call back itself for the remaining reassemblers if
|
||||
// any. This function must be called with f.mu locked.
|
||||
func (f *Fragmentation) releaseReassemblersLocked() {
|
||||
now := f.clock.NowMonotonic()
|
||||
for {
|
||||
// The reassembler at the end of the list is the oldest.
|
||||
r := f.rList.Back()
|
||||
if r == nil {
|
||||
// The list is empty.
|
||||
break
|
||||
}
|
||||
elapsed := now.Sub(r.createdAt)
|
||||
if f.timeout > elapsed {
|
||||
// If the oldest reassembler has not expired, schedule the release
|
||||
// job so that this function is called back when it has expired.
|
||||
f.releaseJob.Schedule(f.timeout - elapsed)
|
||||
break
|
||||
}
|
||||
// If the oldest reassembler has already expired, release it.
|
||||
f.release(r, true /* timedOut*/)
|
||||
}
|
||||
}
|
||||
|
||||
// PacketFragmenter is the book-keeping struct for packet fragmentation.
|
||||
type PacketFragmenter struct {
|
||||
transportHeader []byte
|
||||
data buffer.Buffer
|
||||
reserve int
|
||||
fragmentPayloadLen int
|
||||
fragmentCount int
|
||||
currentFragment int
|
||||
fragmentOffset int
|
||||
}
|
||||
|
||||
// MakePacketFragmenter prepares the struct needed for packet fragmentation.
|
||||
//
|
||||
// pkt is the packet to be fragmented.
|
||||
//
|
||||
// fragmentPayloadLen is the maximum number of bytes of fragmentable data a fragment can
|
||||
// have.
|
||||
//
|
||||
// reserve is the number of bytes that should be reserved for the headers in
|
||||
// each generated fragment.
|
||||
func MakePacketFragmenter(pkt *stack.PacketBuffer, fragmentPayloadLen uint32, reserve int) PacketFragmenter {
|
||||
// As per RFC 8200 Section 4.5, some IPv6 extension headers should not be
|
||||
// repeated in each fragment. However we do not currently support any header
|
||||
// of that kind yet, so the following computation is valid for both IPv4 and
|
||||
// IPv6.
|
||||
// TODO(gvisor.dev/issue/3912): Once Authentication or ESP Headers are
|
||||
// supported for outbound packets, the fragmentable data should not include
|
||||
// these headers.
|
||||
var fragmentableData buffer.Buffer
|
||||
fragmentableData.Append(pkt.TransportHeader().View())
|
||||
pktBuf := pkt.Data().ToBuffer()
|
||||
fragmentableData.Merge(&pktBuf)
|
||||
fragmentCount := (uint32(fragmentableData.Size()) + fragmentPayloadLen - 1) / fragmentPayloadLen
|
||||
|
||||
return PacketFragmenter{
|
||||
data: fragmentableData,
|
||||
reserve: reserve,
|
||||
fragmentPayloadLen: int(fragmentPayloadLen),
|
||||
fragmentCount: int(fragmentCount),
|
||||
}
|
||||
}
|
||||
|
||||
// BuildNextFragment returns a packet with the payload of the next fragment,
|
||||
// along with the fragment's offset, the number of bytes copied and a boolean
|
||||
// indicating if there are more fragments left or not. If this function is
|
||||
// called again after it indicated that no more fragments were left, it will
|
||||
// panic.
|
||||
//
|
||||
// Note that the returned packet will not have its network and link headers
|
||||
// populated, but space for them will be reserved. The transport header will be
|
||||
// stored in the packet's data.
|
||||
func (pf *PacketFragmenter) BuildNextFragment() (*stack.PacketBuffer, int, int, bool) {
|
||||
if pf.currentFragment >= pf.fragmentCount {
|
||||
panic("BuildNextFragment should not be called again after the last fragment was returned")
|
||||
}
|
||||
|
||||
fragPkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
ReserveHeaderBytes: pf.reserve,
|
||||
})
|
||||
|
||||
// Copy data for the fragment.
|
||||
copied := fragPkt.Data().ReadFrom(&pf.data, pf.fragmentPayloadLen)
|
||||
|
||||
offset := pf.fragmentOffset
|
||||
pf.fragmentOffset += copied
|
||||
pf.currentFragment++
|
||||
more := pf.currentFragment != pf.fragmentCount
|
||||
|
||||
return fragPkt, offset, copied, more
|
||||
}
|
||||
|
||||
// RemainingFragmentCount returns the number of fragments left to be built.
|
||||
func (pf *PacketFragmenter) RemainingFragmentCount() int {
|
||||
return pf.fragmentCount - pf.currentFragment
|
||||
}
|
||||
|
||||
// Release frees resources owned by the packet fragmenter.
|
||||
func (pf *PacketFragmenter) Release() {
|
||||
pf.data.Release()
|
||||
}
|
||||
|
|
@ -0,0 +1,246 @@
|
|||
// automatically generated by stateify.
|
||||
|
||||
package fragmentation
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/state"
|
||||
)
|
||||
|
||||
func (f *FragmentID) StateTypeName() string {
|
||||
return "pkg/tcpip/network/internal/fragmentation.FragmentID"
|
||||
}
|
||||
|
||||
func (f *FragmentID) StateFields() []string {
|
||||
return []string{
|
||||
"Source",
|
||||
"Destination",
|
||||
"ID",
|
||||
"Protocol",
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FragmentID) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (f *FragmentID) StateSave(stateSinkObject state.Sink) {
|
||||
f.beforeSave()
|
||||
stateSinkObject.Save(0, &f.Source)
|
||||
stateSinkObject.Save(1, &f.Destination)
|
||||
stateSinkObject.Save(2, &f.ID)
|
||||
stateSinkObject.Save(3, &f.Protocol)
|
||||
}
|
||||
|
||||
func (f *FragmentID) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (f *FragmentID) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &f.Source)
|
||||
stateSourceObject.Load(1, &f.Destination)
|
||||
stateSourceObject.Load(2, &f.ID)
|
||||
stateSourceObject.Load(3, &f.Protocol)
|
||||
}
|
||||
|
||||
func (f *Fragmentation) StateTypeName() string {
|
||||
return "pkg/tcpip/network/internal/fragmentation.Fragmentation"
|
||||
}
|
||||
|
||||
func (f *Fragmentation) StateFields() []string {
|
||||
return []string{
|
||||
"highLimit",
|
||||
"lowLimit",
|
||||
"reassemblers",
|
||||
"rList",
|
||||
"memSize",
|
||||
"timeout",
|
||||
"blockSize",
|
||||
"clock",
|
||||
"releaseJob",
|
||||
"timeoutHandler",
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Fragmentation) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (f *Fragmentation) StateSave(stateSinkObject state.Sink) {
|
||||
f.beforeSave()
|
||||
stateSinkObject.Save(0, &f.highLimit)
|
||||
stateSinkObject.Save(1, &f.lowLimit)
|
||||
stateSinkObject.Save(2, &f.reassemblers)
|
||||
stateSinkObject.Save(3, &f.rList)
|
||||
stateSinkObject.Save(4, &f.memSize)
|
||||
stateSinkObject.Save(5, &f.timeout)
|
||||
stateSinkObject.Save(6, &f.blockSize)
|
||||
stateSinkObject.Save(7, &f.clock)
|
||||
stateSinkObject.Save(8, &f.releaseJob)
|
||||
stateSinkObject.Save(9, &f.timeoutHandler)
|
||||
}
|
||||
|
||||
func (f *Fragmentation) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (f *Fragmentation) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &f.highLimit)
|
||||
stateSourceObject.Load(1, &f.lowLimit)
|
||||
stateSourceObject.Load(2, &f.reassemblers)
|
||||
stateSourceObject.Load(3, &f.rList)
|
||||
stateSourceObject.Load(4, &f.memSize)
|
||||
stateSourceObject.Load(5, &f.timeout)
|
||||
stateSourceObject.Load(6, &f.blockSize)
|
||||
stateSourceObject.Load(7, &f.clock)
|
||||
stateSourceObject.Load(8, &f.releaseJob)
|
||||
stateSourceObject.Load(9, &f.timeoutHandler)
|
||||
}
|
||||
|
||||
func (h *hole) StateTypeName() string {
|
||||
return "pkg/tcpip/network/internal/fragmentation.hole"
|
||||
}
|
||||
|
||||
func (h *hole) StateFields() []string {
|
||||
return []string{
|
||||
"first",
|
||||
"last",
|
||||
"filled",
|
||||
"final",
|
||||
"pkt",
|
||||
}
|
||||
}
|
||||
|
||||
func (h *hole) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (h *hole) StateSave(stateSinkObject state.Sink) {
|
||||
h.beforeSave()
|
||||
stateSinkObject.Save(0, &h.first)
|
||||
stateSinkObject.Save(1, &h.last)
|
||||
stateSinkObject.Save(2, &h.filled)
|
||||
stateSinkObject.Save(3, &h.final)
|
||||
stateSinkObject.Save(4, &h.pkt)
|
||||
}
|
||||
|
||||
func (h *hole) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (h *hole) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &h.first)
|
||||
stateSourceObject.Load(1, &h.last)
|
||||
stateSourceObject.Load(2, &h.filled)
|
||||
stateSourceObject.Load(3, &h.final)
|
||||
stateSourceObject.Load(4, &h.pkt)
|
||||
}
|
||||
|
||||
func (r *reassembler) StateTypeName() string {
|
||||
return "pkg/tcpip/network/internal/fragmentation.reassembler"
|
||||
}
|
||||
|
||||
func (r *reassembler) StateFields() []string {
|
||||
return []string{
|
||||
"reassemblerEntry",
|
||||
"id",
|
||||
"memSize",
|
||||
"proto",
|
||||
"holes",
|
||||
"filled",
|
||||
"done",
|
||||
"createdAt",
|
||||
"pkt",
|
||||
}
|
||||
}
|
||||
|
||||
func (r *reassembler) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (r *reassembler) StateSave(stateSinkObject state.Sink) {
|
||||
r.beforeSave()
|
||||
stateSinkObject.Save(0, &r.reassemblerEntry)
|
||||
stateSinkObject.Save(1, &r.id)
|
||||
stateSinkObject.Save(2, &r.memSize)
|
||||
stateSinkObject.Save(3, &r.proto)
|
||||
stateSinkObject.Save(4, &r.holes)
|
||||
stateSinkObject.Save(5, &r.filled)
|
||||
stateSinkObject.Save(6, &r.done)
|
||||
stateSinkObject.Save(7, &r.createdAt)
|
||||
stateSinkObject.Save(8, &r.pkt)
|
||||
}
|
||||
|
||||
func (r *reassembler) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (r *reassembler) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &r.reassemblerEntry)
|
||||
stateSourceObject.Load(1, &r.id)
|
||||
stateSourceObject.Load(2, &r.memSize)
|
||||
stateSourceObject.Load(3, &r.proto)
|
||||
stateSourceObject.Load(4, &r.holes)
|
||||
stateSourceObject.Load(5, &r.filled)
|
||||
stateSourceObject.Load(6, &r.done)
|
||||
stateSourceObject.Load(7, &r.createdAt)
|
||||
stateSourceObject.Load(8, &r.pkt)
|
||||
}
|
||||
|
||||
func (l *reassemblerList) StateTypeName() string {
|
||||
return "pkg/tcpip/network/internal/fragmentation.reassemblerList"
|
||||
}
|
||||
|
||||
func (l *reassemblerList) StateFields() []string {
|
||||
return []string{
|
||||
"head",
|
||||
"tail",
|
||||
}
|
||||
}
|
||||
|
||||
func (l *reassemblerList) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (l *reassemblerList) StateSave(stateSinkObject state.Sink) {
|
||||
l.beforeSave()
|
||||
stateSinkObject.Save(0, &l.head)
|
||||
stateSinkObject.Save(1, &l.tail)
|
||||
}
|
||||
|
||||
func (l *reassemblerList) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (l *reassemblerList) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &l.head)
|
||||
stateSourceObject.Load(1, &l.tail)
|
||||
}
|
||||
|
||||
func (e *reassemblerEntry) StateTypeName() string {
|
||||
return "pkg/tcpip/network/internal/fragmentation.reassemblerEntry"
|
||||
}
|
||||
|
||||
func (e *reassemblerEntry) StateFields() []string {
|
||||
return []string{
|
||||
"next",
|
||||
"prev",
|
||||
}
|
||||
}
|
||||
|
||||
func (e *reassemblerEntry) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *reassemblerEntry) StateSave(stateSinkObject state.Sink) {
|
||||
e.beforeSave()
|
||||
stateSinkObject.Save(0, &e.next)
|
||||
stateSinkObject.Save(1, &e.prev)
|
||||
}
|
||||
|
||||
func (e *reassemblerEntry) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *reassemblerEntry) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &e.next)
|
||||
stateSourceObject.Load(1, &e.prev)
|
||||
}
|
||||
|
||||
func init() {
|
||||
state.Register((*FragmentID)(nil))
|
||||
state.Register((*Fragmentation)(nil))
|
||||
state.Register((*hole)(nil))
|
||||
state.Register((*reassembler)(nil))
|
||||
state.Register((*reassemblerList)(nil))
|
||||
state.Register((*reassemblerEntry)(nil))
|
||||
}
|
||||
185
pkg/tcpip/network/internal/fragmentation/reassembler.go
Normal file
185
pkg/tcpip/network/internal/fragmentation/reassembler.go
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
// 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 fragmentation
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/sync"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
// +stateify savable
|
||||
type hole struct {
|
||||
first uint16
|
||||
last uint16
|
||||
filled bool
|
||||
final bool
|
||||
// pkt is the fragment packet if hole is filled. We keep the whole pkt rather
|
||||
// than the fragmented payload to prevent binding to specific buffer types.
|
||||
pkt *stack.PacketBuffer
|
||||
}
|
||||
|
||||
// +stateify savable
|
||||
type reassembler struct {
|
||||
reassemblerEntry
|
||||
id FragmentID
|
||||
memSize int
|
||||
proto uint8
|
||||
mu sync.Mutex `state:"nosave"`
|
||||
holes []hole
|
||||
filled int
|
||||
done bool
|
||||
createdAt tcpip.MonotonicTime
|
||||
pkt *stack.PacketBuffer
|
||||
}
|
||||
|
||||
func newReassembler(id FragmentID, clock tcpip.Clock) *reassembler {
|
||||
r := &reassembler{
|
||||
id: id,
|
||||
createdAt: clock.NowMonotonic(),
|
||||
}
|
||||
r.holes = append(r.holes, hole{
|
||||
first: 0,
|
||||
last: math.MaxUint16,
|
||||
filled: false,
|
||||
final: true,
|
||||
})
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *reassembler) process(first, last uint16, more bool, proto uint8, pkt *stack.PacketBuffer) (*stack.PacketBuffer, uint8, bool, int, error) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
if r.done {
|
||||
// A concurrent goroutine might have already reassembled
|
||||
// the packet and emptied the heap while this goroutine
|
||||
// was waiting on the mutex. We don't have to do anything in this case.
|
||||
return nil, 0, false, 0, nil
|
||||
}
|
||||
|
||||
var holeFound bool
|
||||
var memConsumed int
|
||||
for i := range r.holes {
|
||||
currentHole := &r.holes[i]
|
||||
|
||||
if last < currentHole.first || currentHole.last < first {
|
||||
continue
|
||||
}
|
||||
// For IPv6, overlaps with an existing fragment are explicitly forbidden by
|
||||
// RFC 8200 section 4.5:
|
||||
// If any of the fragments being reassembled overlap with any other
|
||||
// fragments being reassembled for the same packet, reassembly of that
|
||||
// packet must be abandoned and all the fragments that have been received
|
||||
// for that packet must be discarded, and no ICMP error messages should be
|
||||
// sent.
|
||||
//
|
||||
// It is not explicitly forbidden for IPv4, but to keep parity with Linux we
|
||||
// disallow it as well:
|
||||
// https://github.com/torvalds/linux/blob/38525c6/net/ipv4/inet_fragment.c#L349
|
||||
if first < currentHole.first || currentHole.last < last {
|
||||
// Incoming fragment only partially fits in the free hole.
|
||||
return nil, 0, false, 0, ErrFragmentOverlap
|
||||
}
|
||||
if !more {
|
||||
if !currentHole.final || currentHole.filled && currentHole.last != last {
|
||||
// We have another final fragment, which does not perfectly overlap.
|
||||
return nil, 0, false, 0, ErrFragmentConflict
|
||||
}
|
||||
}
|
||||
|
||||
holeFound = true
|
||||
if currentHole.filled {
|
||||
// Incoming fragment is a duplicate.
|
||||
continue
|
||||
}
|
||||
|
||||
// We are populating the current hole with the payload and creating a new
|
||||
// hole for any unfilled ranges on either end.
|
||||
if first > currentHole.first {
|
||||
r.holes = append(r.holes, hole{
|
||||
first: currentHole.first,
|
||||
last: first - 1,
|
||||
filled: false,
|
||||
final: false,
|
||||
})
|
||||
}
|
||||
if last < currentHole.last && more {
|
||||
r.holes = append(r.holes, hole{
|
||||
first: last + 1,
|
||||
last: currentHole.last,
|
||||
filled: false,
|
||||
final: currentHole.final,
|
||||
})
|
||||
currentHole.final = false
|
||||
}
|
||||
memConsumed = pkt.MemSize()
|
||||
r.memSize += memConsumed
|
||||
// Update the current hole to precisely match the incoming fragment.
|
||||
r.holes[i] = hole{
|
||||
first: first,
|
||||
last: last,
|
||||
filled: true,
|
||||
final: currentHole.final,
|
||||
pkt: pkt.Clone(),
|
||||
}
|
||||
r.filled++
|
||||
// For IPv6, it is possible to have different Protocol values between
|
||||
// fragments of a packet (because, unlike IPv4, the Protocol is not used to
|
||||
// identify a fragment). In this case, only the Protocol of the first
|
||||
// fragment must be used as per RFC 8200 Section 4.5.
|
||||
//
|
||||
// TODO(gvisor.dev/issue/3648): During reassembly of an IPv6 packet, IP
|
||||
// options received in the first fragment should be used - and they should
|
||||
// override options from following fragments.
|
||||
if first == 0 {
|
||||
if r.pkt != nil {
|
||||
r.pkt.DecRef()
|
||||
}
|
||||
r.pkt = pkt.Clone()
|
||||
r.proto = proto
|
||||
}
|
||||
break
|
||||
}
|
||||
if !holeFound {
|
||||
// Incoming fragment is beyond end.
|
||||
return nil, 0, false, 0, ErrFragmentConflict
|
||||
}
|
||||
|
||||
// Check if all the holes have been filled and we are ready to reassemble.
|
||||
if r.filled < len(r.holes) {
|
||||
return nil, 0, false, memConsumed, nil
|
||||
}
|
||||
|
||||
sort.Slice(r.holes, func(i, j int) bool {
|
||||
return r.holes[i].first < r.holes[j].first
|
||||
})
|
||||
|
||||
resPkt := r.holes[0].pkt.Clone()
|
||||
for i := 1; i < len(r.holes); i++ {
|
||||
stack.MergeFragment(resPkt, r.holes[i].pkt)
|
||||
}
|
||||
return resPkt, r.proto, true /* done */, memConsumed, nil
|
||||
}
|
||||
|
||||
func (r *reassembler) checkDoneOrMark() bool {
|
||||
r.mu.Lock()
|
||||
prev := r.done
|
||||
r.done = true
|
||||
r.mu.Unlock()
|
||||
return prev
|
||||
}
|
||||
239
pkg/tcpip/network/internal/fragmentation/reassembler_list.go
Normal file
239
pkg/tcpip/network/internal/fragmentation/reassembler_list.go
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
package fragmentation
|
||||
|
||||
// ElementMapper provides an identity mapping by default.
|
||||
//
|
||||
// This can be replaced to provide a struct that maps elements to linker
|
||||
// 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 reassemblerElementMapper struct{}
|
||||
|
||||
// linkerFor maps an Element to a Linker.
|
||||
//
|
||||
// This default implementation should be inlined.
|
||||
//
|
||||
//go:nosplit
|
||||
func (reassemblerElementMapper) linkerFor(elem *reassembler) *reassembler { 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.
|
||||
//
|
||||
// The zero value for List is an empty list ready to use.
|
||||
//
|
||||
// To iterate over a list (where l is a List):
|
||||
//
|
||||
// for e := l.Front(); e != nil; e = e.Next() {
|
||||
// // do something with e.
|
||||
// }
|
||||
//
|
||||
// +stateify savable
|
||||
type reassemblerList struct {
|
||||
head *reassembler
|
||||
tail *reassembler
|
||||
}
|
||||
|
||||
// Reset resets list l to the empty state.
|
||||
func (l *reassemblerList) Reset() {
|
||||
l.head = nil
|
||||
l.tail = nil
|
||||
}
|
||||
|
||||
// Empty returns true iff the list is empty.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *reassemblerList) Empty() bool {
|
||||
return l.head == nil
|
||||
}
|
||||
|
||||
// Front returns the first element of list l or nil.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *reassemblerList) Front() *reassembler {
|
||||
return l.head
|
||||
}
|
||||
|
||||
// Back returns the last element of list l or nil.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *reassemblerList) Back() *reassembler {
|
||||
return l.tail
|
||||
}
|
||||
|
||||
// Len returns the number of elements in the list.
|
||||
//
|
||||
// NOTE: This is an O(n) operation.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *reassemblerList) Len() (count int) {
|
||||
for e := l.Front(); e != nil; e = (reassemblerElementMapper{}.linkerFor(e)).Next() {
|
||||
count++
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// PushFront inserts the element e at the front of list l.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *reassemblerList) PushFront(e *reassembler) {
|
||||
linker := reassemblerElementMapper{}.linkerFor(e)
|
||||
linker.SetNext(l.head)
|
||||
linker.SetPrev(nil)
|
||||
if l.head != nil {
|
||||
reassemblerElementMapper{}.linkerFor(l.head).SetPrev(e)
|
||||
} else {
|
||||
l.tail = e
|
||||
}
|
||||
|
||||
l.head = e
|
||||
}
|
||||
|
||||
// PushFrontList inserts list m at the start of list l, emptying m.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *reassemblerList) PushFrontList(m *reassemblerList) {
|
||||
if l.head == nil {
|
||||
l.head = m.head
|
||||
l.tail = m.tail
|
||||
} else if m.head != nil {
|
||||
reassemblerElementMapper{}.linkerFor(l.head).SetPrev(m.tail)
|
||||
reassemblerElementMapper{}.linkerFor(m.tail).SetNext(l.head)
|
||||
|
||||
l.head = m.head
|
||||
}
|
||||
m.head = nil
|
||||
m.tail = nil
|
||||
}
|
||||
|
||||
// PushBack inserts the element e at the back of list l.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *reassemblerList) PushBack(e *reassembler) {
|
||||
linker := reassemblerElementMapper{}.linkerFor(e)
|
||||
linker.SetNext(nil)
|
||||
linker.SetPrev(l.tail)
|
||||
if l.tail != nil {
|
||||
reassemblerElementMapper{}.linkerFor(l.tail).SetNext(e)
|
||||
} else {
|
||||
l.head = e
|
||||
}
|
||||
|
||||
l.tail = e
|
||||
}
|
||||
|
||||
// PushBackList inserts list m at the end of list l, emptying m.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *reassemblerList) PushBackList(m *reassemblerList) {
|
||||
if l.head == nil {
|
||||
l.head = m.head
|
||||
l.tail = m.tail
|
||||
} else if m.head != nil {
|
||||
reassemblerElementMapper{}.linkerFor(l.tail).SetNext(m.head)
|
||||
reassemblerElementMapper{}.linkerFor(m.head).SetPrev(l.tail)
|
||||
|
||||
l.tail = m.tail
|
||||
}
|
||||
m.head = nil
|
||||
m.tail = nil
|
||||
}
|
||||
|
||||
// InsertAfter inserts e after b.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *reassemblerList) InsertAfter(b, e *reassembler) {
|
||||
bLinker := reassemblerElementMapper{}.linkerFor(b)
|
||||
eLinker := reassemblerElementMapper{}.linkerFor(e)
|
||||
|
||||
a := bLinker.Next()
|
||||
|
||||
eLinker.SetNext(a)
|
||||
eLinker.SetPrev(b)
|
||||
bLinker.SetNext(e)
|
||||
|
||||
if a != nil {
|
||||
reassemblerElementMapper{}.linkerFor(a).SetPrev(e)
|
||||
} else {
|
||||
l.tail = e
|
||||
}
|
||||
}
|
||||
|
||||
// InsertBefore inserts e before a.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *reassemblerList) InsertBefore(a, e *reassembler) {
|
||||
aLinker := reassemblerElementMapper{}.linkerFor(a)
|
||||
eLinker := reassemblerElementMapper{}.linkerFor(e)
|
||||
|
||||
b := aLinker.Prev()
|
||||
eLinker.SetNext(a)
|
||||
eLinker.SetPrev(b)
|
||||
aLinker.SetPrev(e)
|
||||
|
||||
if b != nil {
|
||||
reassemblerElementMapper{}.linkerFor(b).SetNext(e)
|
||||
} else {
|
||||
l.head = e
|
||||
}
|
||||
}
|
||||
|
||||
// Remove removes e from l.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *reassemblerList) Remove(e *reassembler) {
|
||||
linker := reassemblerElementMapper{}.linkerFor(e)
|
||||
prev := linker.Prev()
|
||||
next := linker.Next()
|
||||
|
||||
if prev != nil {
|
||||
reassemblerElementMapper{}.linkerFor(prev).SetNext(next)
|
||||
} else if l.head == e {
|
||||
l.head = next
|
||||
}
|
||||
|
||||
if next != nil {
|
||||
reassemblerElementMapper{}.linkerFor(next).SetPrev(prev)
|
||||
} else if l.tail == e {
|
||||
l.tail = prev
|
||||
}
|
||||
|
||||
linker.SetNext(nil)
|
||||
linker.SetPrev(nil)
|
||||
}
|
||||
|
||||
// Entry is a default implementation of Linker. Users can add anonymous fields
|
||||
// of this type to their structs to make them automatically implement the
|
||||
// methods needed by List.
|
||||
//
|
||||
// +stateify savable
|
||||
type reassemblerEntry struct {
|
||||
next *reassembler
|
||||
prev *reassembler
|
||||
}
|
||||
|
||||
// Next returns the entry that follows e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *reassemblerEntry) Next() *reassembler {
|
||||
return e.next
|
||||
}
|
||||
|
||||
// Prev returns the entry that precedes e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *reassemblerEntry) Prev() *reassembler {
|
||||
return e.prev
|
||||
}
|
||||
|
||||
// SetNext assigns 'entry' as the entry that follows e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *reassemblerEntry) SetNext(elem *reassembler) {
|
||||
e.next = elem
|
||||
}
|
||||
|
||||
// SetPrev assigns 'entry' as the entry that precedes e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *reassemblerEntry) SetPrev(elem *reassembler) {
|
||||
e.prev = elem
|
||||
}
|
||||
304
pkg/tcpip/network/internal/ip/duplicate_address_detection.go
Normal file
304
pkg/tcpip/network/internal/ip/duplicate_address_detection.go
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
// Copyright 2021 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 ip holds IPv4/IPv6 common utilities.
|
||||
package ip
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/sync"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
type extendRequest int
|
||||
|
||||
const (
|
||||
notRequested extendRequest = iota
|
||||
requested
|
||||
extended
|
||||
)
|
||||
|
||||
// +stateify savable
|
||||
type dadState struct {
|
||||
nonce []byte
|
||||
extendRequest extendRequest
|
||||
|
||||
done *bool
|
||||
timer tcpip.Timer
|
||||
|
||||
completionHandlers []stack.DADCompletionHandler
|
||||
}
|
||||
|
||||
// DADProtocol is a protocol whose core state machine can be represented by DAD.
|
||||
type DADProtocol interface {
|
||||
// SendDADMessage attempts to send a DAD probe message.
|
||||
SendDADMessage(tcpip.Address, []byte) tcpip.Error
|
||||
}
|
||||
|
||||
// DADOptions holds options for DAD.
|
||||
//
|
||||
// +stateify savable
|
||||
type DADOptions struct {
|
||||
Clock tcpip.Clock
|
||||
// TODO(b/341946753): Restore when netstack is savable.
|
||||
SecureRNG io.Reader `state:"nosave"`
|
||||
NonceSize uint8
|
||||
ExtendDADTransmits uint8
|
||||
Protocol DADProtocol
|
||||
NICID tcpip.NICID
|
||||
}
|
||||
|
||||
// DAD performs duplicate address detection for addresses.
|
||||
//
|
||||
// +stateify savable
|
||||
type DAD struct {
|
||||
opts DADOptions
|
||||
configs stack.DADConfigurations
|
||||
|
||||
protocolMU sync.Locker `state:"nosave"`
|
||||
addresses map[tcpip.Address]dadState
|
||||
}
|
||||
|
||||
// Init initializes the DAD state.
|
||||
//
|
||||
// Must only be called once for the lifetime of d; Init will panic if it is
|
||||
// called twice.
|
||||
//
|
||||
// The lock will only be taken when timers fire.
|
||||
func (d *DAD) Init(protocolMU sync.Locker, configs stack.DADConfigurations, opts DADOptions) {
|
||||
if d.addresses != nil {
|
||||
panic("attempted to initialize DAD state twice")
|
||||
}
|
||||
|
||||
if opts.NonceSize != 0 && opts.ExtendDADTransmits == 0 {
|
||||
panic(fmt.Sprintf("given a non-zero value for NonceSize (%d) but zero for ExtendDADTransmits", opts.NonceSize))
|
||||
}
|
||||
|
||||
configs.Validate()
|
||||
|
||||
*d = DAD{
|
||||
opts: opts,
|
||||
configs: configs,
|
||||
protocolMU: protocolMU,
|
||||
addresses: make(map[tcpip.Address]dadState),
|
||||
}
|
||||
}
|
||||
|
||||
// CheckDuplicateAddressLocked performs DAD for an address, calling the
|
||||
// completion handler once DAD resolves.
|
||||
//
|
||||
// If DAD is already performing for the provided address, h will be called when
|
||||
// the currently running process completes.
|
||||
//
|
||||
// Precondition: d.protocolMU must be locked.
|
||||
func (d *DAD) CheckDuplicateAddressLocked(addr tcpip.Address, h stack.DADCompletionHandler) stack.DADCheckAddressDisposition {
|
||||
if d.configs.DupAddrDetectTransmits == 0 {
|
||||
return stack.DADDisabled
|
||||
}
|
||||
|
||||
ret := stack.DADAlreadyRunning
|
||||
s, ok := d.addresses[addr]
|
||||
if !ok {
|
||||
ret = stack.DADStarting
|
||||
|
||||
remaining := d.configs.DupAddrDetectTransmits
|
||||
|
||||
// Protected by d.protocolMU.
|
||||
done := false
|
||||
|
||||
s = dadState{
|
||||
done: &done,
|
||||
timer: d.opts.Clock.AfterFunc(0, func() {
|
||||
dadDone := remaining == 0
|
||||
|
||||
nonce, earlyReturn := func() ([]byte, bool) {
|
||||
d.protocolMU.Lock()
|
||||
defer d.protocolMU.Unlock()
|
||||
|
||||
if done {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
s, ok := d.addresses[addr]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("dad: timer fired but missing state for %s on NIC(%d)", addr, d.opts.NICID))
|
||||
}
|
||||
|
||||
// As per RFC 7527 section 4
|
||||
//
|
||||
// If any probe is looped back within RetransTimer milliseconds
|
||||
// after having sent DupAddrDetectTransmits NS(DAD) messages, the
|
||||
// interface continues with another MAX_MULTICAST_SOLICIT number of
|
||||
// NS(DAD) messages transmitted RetransTimer milliseconds apart.
|
||||
if dadDone && s.extendRequest == requested {
|
||||
dadDone = false
|
||||
remaining = d.opts.ExtendDADTransmits
|
||||
s.extendRequest = extended
|
||||
}
|
||||
|
||||
if !dadDone && d.opts.NonceSize != 0 {
|
||||
if s.nonce == nil {
|
||||
s.nonce = make([]byte, d.opts.NonceSize)
|
||||
}
|
||||
|
||||
if n, err := io.ReadFull(d.opts.SecureRNG, s.nonce); err != nil {
|
||||
panic(fmt.Sprintf("SecureRNG.Read(...): %s", err))
|
||||
} else if n != len(s.nonce) {
|
||||
panic(fmt.Sprintf("expected to read %d bytes from secure RNG, only read %d bytes", len(s.nonce), n))
|
||||
}
|
||||
}
|
||||
|
||||
d.addresses[addr] = s
|
||||
return s.nonce, false
|
||||
}()
|
||||
if earlyReturn {
|
||||
return
|
||||
}
|
||||
|
||||
var err tcpip.Error
|
||||
if !dadDone {
|
||||
err = d.opts.Protocol.SendDADMessage(addr, nonce)
|
||||
}
|
||||
|
||||
d.protocolMU.Lock()
|
||||
defer d.protocolMU.Unlock()
|
||||
|
||||
if done {
|
||||
return
|
||||
}
|
||||
|
||||
s, ok := d.addresses[addr]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("dad: timer fired but missing state for %s on NIC(%d)", addr, d.opts.NICID))
|
||||
}
|
||||
|
||||
if !dadDone && err == nil {
|
||||
remaining--
|
||||
s.timer.Reset(d.configs.RetransmitTimer)
|
||||
return
|
||||
}
|
||||
|
||||
// At this point we know that either DAD has resolved or we hit an error
|
||||
// sending the last DAD message. Either way, clear the DAD state.
|
||||
done = false
|
||||
s.timer.Stop()
|
||||
delete(d.addresses, addr)
|
||||
|
||||
var res stack.DADResult = &stack.DADSucceeded{}
|
||||
if err != nil {
|
||||
res = &stack.DADError{Err: err}
|
||||
}
|
||||
for _, h := range s.completionHandlers {
|
||||
h(res)
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
s.completionHandlers = append(s.completionHandlers, h)
|
||||
d.addresses[addr] = s
|
||||
return ret
|
||||
}
|
||||
|
||||
// ExtendIfNonceEqualLockedDisposition enumerates the possible results from
|
||||
// ExtendIfNonceEqualLocked.
|
||||
type ExtendIfNonceEqualLockedDisposition int
|
||||
|
||||
const (
|
||||
// Extended indicates that the DAD process was extended.
|
||||
Extended ExtendIfNonceEqualLockedDisposition = iota
|
||||
|
||||
// AlreadyExtended indicates that the DAD process was already extended.
|
||||
AlreadyExtended
|
||||
|
||||
// NoDADStateFound indicates that DAD state was not found for the address.
|
||||
NoDADStateFound
|
||||
|
||||
// NonceDisabled indicates that nonce values are not sent with DAD messages.
|
||||
NonceDisabled
|
||||
|
||||
// NonceNotEqual indicates that the nonce value passed and the nonce in the
|
||||
// last send DAD message are not equal.
|
||||
NonceNotEqual
|
||||
)
|
||||
|
||||
// ExtendIfNonceEqualLocked extends the DAD process if the provided nonce is the
|
||||
// same as the nonce sent in the last DAD message.
|
||||
//
|
||||
// Precondition: d.protocolMU must be locked.
|
||||
func (d *DAD) ExtendIfNonceEqualLocked(addr tcpip.Address, nonce []byte) ExtendIfNonceEqualLockedDisposition {
|
||||
s, ok := d.addresses[addr]
|
||||
if !ok {
|
||||
return NoDADStateFound
|
||||
}
|
||||
|
||||
if d.opts.NonceSize == 0 {
|
||||
return NonceDisabled
|
||||
}
|
||||
|
||||
if s.extendRequest != notRequested {
|
||||
return AlreadyExtended
|
||||
}
|
||||
|
||||
// As per RFC 7527 section 4
|
||||
//
|
||||
// If any probe is looped back within RetransTimer milliseconds after having
|
||||
// sent DupAddrDetectTransmits NS(DAD) messages, the interface continues
|
||||
// with another MAX_MULTICAST_SOLICIT number of NS(DAD) messages transmitted
|
||||
// RetransTimer milliseconds apart.
|
||||
//
|
||||
// If a DAD message has already been sent and the nonce value we observed is
|
||||
// the same as the nonce value we last sent, then we assume our probe was
|
||||
// looped back and request an extension to the DAD process.
|
||||
//
|
||||
// Note, the first DAD message is sent asynchronously so we need to make sure
|
||||
// that we sent a DAD message by checking if we have a nonce value set.
|
||||
if s.nonce != nil && bytes.Equal(s.nonce, nonce) {
|
||||
s.extendRequest = requested
|
||||
d.addresses[addr] = s
|
||||
return Extended
|
||||
}
|
||||
|
||||
return NonceNotEqual
|
||||
}
|
||||
|
||||
// StopLocked stops a currently running DAD process.
|
||||
//
|
||||
// Precondition: d.protocolMU must be locked.
|
||||
func (d *DAD) StopLocked(addr tcpip.Address, reason stack.DADResult) {
|
||||
s, ok := d.addresses[addr]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
*s.done = true
|
||||
s.timer.Stop()
|
||||
delete(d.addresses, addr)
|
||||
|
||||
for _, h := range s.completionHandlers {
|
||||
h(reason)
|
||||
}
|
||||
}
|
||||
|
||||
// SetConfigsLocked sets the DAD configurations.
|
||||
//
|
||||
// Precondition: d.protocolMU must be locked.
|
||||
func (d *DAD) SetConfigsLocked(c stack.DADConfigurations) {
|
||||
c.Validate()
|
||||
d.configs = c
|
||||
}
|
||||
129
pkg/tcpip/network/internal/ip/errors.go
Normal file
129
pkg/tcpip/network/internal/ip/errors.go
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
// Copyright 2021 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 ip
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
)
|
||||
|
||||
// ForwardingError represents an error that occurred while trying to forward
|
||||
// a packet.
|
||||
type ForwardingError interface {
|
||||
isForwardingError()
|
||||
fmt.Stringer
|
||||
}
|
||||
|
||||
// ErrTTLExceeded indicates that the received packet's TTL has been exceeded.
|
||||
type ErrTTLExceeded struct{}
|
||||
|
||||
func (*ErrTTLExceeded) isForwardingError() {}
|
||||
|
||||
func (*ErrTTLExceeded) String() string { return "ttl exceeded" }
|
||||
|
||||
// ErrOutgoingDeviceNoBufferSpace indicates that the outgoing device does not
|
||||
// have enough space to hold a buffer.
|
||||
type ErrOutgoingDeviceNoBufferSpace struct{}
|
||||
|
||||
func (*ErrOutgoingDeviceNoBufferSpace) isForwardingError() {}
|
||||
|
||||
func (*ErrOutgoingDeviceNoBufferSpace) String() string { return "no device buffer space" }
|
||||
|
||||
// ErrParameterProblem indicates the received packet had a problem with an IP
|
||||
// parameter.
|
||||
type ErrParameterProblem struct{}
|
||||
|
||||
func (*ErrParameterProblem) isForwardingError() {}
|
||||
|
||||
func (*ErrParameterProblem) String() string { return "parameter problem" }
|
||||
|
||||
// ErrInitializingSourceAddress indicates the received packet had a source
|
||||
// address that may only be used on the local network as part of initialization
|
||||
// work.
|
||||
type ErrInitializingSourceAddress struct{}
|
||||
|
||||
func (*ErrInitializingSourceAddress) isForwardingError() {}
|
||||
|
||||
func (*ErrInitializingSourceAddress) String() string { return "initializing source address" }
|
||||
|
||||
// ErrLinkLocalSourceAddress indicates the received packet had a link-local
|
||||
// source address.
|
||||
type ErrLinkLocalSourceAddress struct{}
|
||||
|
||||
func (*ErrLinkLocalSourceAddress) isForwardingError() {}
|
||||
|
||||
func (*ErrLinkLocalSourceAddress) String() string { return "link local source address" }
|
||||
|
||||
// ErrLinkLocalDestinationAddress indicates the received packet had a link-local
|
||||
// destination address.
|
||||
type ErrLinkLocalDestinationAddress struct{}
|
||||
|
||||
func (*ErrLinkLocalDestinationAddress) isForwardingError() {}
|
||||
|
||||
func (*ErrLinkLocalDestinationAddress) String() string { return "link local destination address" }
|
||||
|
||||
// ErrHostUnreachable indicates that the destination host could not be reached.
|
||||
type ErrHostUnreachable struct{}
|
||||
|
||||
func (*ErrHostUnreachable) isForwardingError() {}
|
||||
|
||||
func (*ErrHostUnreachable) String() string { return "no route to host" }
|
||||
|
||||
// ErrMessageTooLong indicates the packet was too big for the outgoing MTU.
|
||||
//
|
||||
// +stateify savable
|
||||
type ErrMessageTooLong struct{}
|
||||
|
||||
func (*ErrMessageTooLong) isForwardingError() {}
|
||||
|
||||
func (*ErrMessageTooLong) String() string { return "message too long" }
|
||||
|
||||
// ErrNoMulticastPendingQueueBufferSpace indicates that a multicast packet
|
||||
// could not be added to the pending packet queue due to insufficient buffer
|
||||
// space.
|
||||
//
|
||||
// +stateify savable
|
||||
type ErrNoMulticastPendingQueueBufferSpace struct{}
|
||||
|
||||
func (*ErrNoMulticastPendingQueueBufferSpace) isForwardingError() {}
|
||||
|
||||
func (*ErrNoMulticastPendingQueueBufferSpace) String() string { return "no buffer space" }
|
||||
|
||||
// ErrUnexpectedMulticastInputInterface indicates that the interface that the
|
||||
// packet arrived on did not match the routes expected input interface.
|
||||
type ErrUnexpectedMulticastInputInterface struct{}
|
||||
|
||||
func (*ErrUnexpectedMulticastInputInterface) isForwardingError() {}
|
||||
|
||||
func (*ErrUnexpectedMulticastInputInterface) String() string { return "unexpected input interface" }
|
||||
|
||||
// ErrUnknownOutputEndpoint indicates that the output endpoint associated with
|
||||
// a route could not be found.
|
||||
type ErrUnknownOutputEndpoint struct{}
|
||||
|
||||
func (*ErrUnknownOutputEndpoint) isForwardingError() {}
|
||||
|
||||
func (*ErrUnknownOutputEndpoint) String() string { return "unknown endpoint" }
|
||||
|
||||
// ErrOther indicates the packet coould not be forwarded for a reason
|
||||
// captured by the contained error.
|
||||
type ErrOther struct {
|
||||
Err tcpip.Error
|
||||
}
|
||||
|
||||
func (*ErrOther) isForwardingError() {}
|
||||
|
||||
func (e *ErrOther) String() string { return fmt.Sprintf("other tcpip error: %s", e.Err) }
|
||||
1192
pkg/tcpip/network/internal/ip/generic_multicast_protocol.go
Normal file
1192
pkg/tcpip/network/internal/ip/generic_multicast_protocol.go
Normal file
File diff suppressed because it is too large
Load diff
435
pkg/tcpip/network/internal/ip/ip_state_autogen.go
Normal file
435
pkg/tcpip/network/internal/ip/ip_state_autogen.go
Normal file
|
|
@ -0,0 +1,435 @@
|
|||
// automatically generated by stateify.
|
||||
|
||||
package ip
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/state"
|
||||
)
|
||||
|
||||
func (d *dadState) StateTypeName() string {
|
||||
return "pkg/tcpip/network/internal/ip.dadState"
|
||||
}
|
||||
|
||||
func (d *dadState) StateFields() []string {
|
||||
return []string{
|
||||
"nonce",
|
||||
"extendRequest",
|
||||
"done",
|
||||
"timer",
|
||||
"completionHandlers",
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dadState) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (d *dadState) StateSave(stateSinkObject state.Sink) {
|
||||
d.beforeSave()
|
||||
stateSinkObject.Save(0, &d.nonce)
|
||||
stateSinkObject.Save(1, &d.extendRequest)
|
||||
stateSinkObject.Save(2, &d.done)
|
||||
stateSinkObject.Save(3, &d.timer)
|
||||
stateSinkObject.Save(4, &d.completionHandlers)
|
||||
}
|
||||
|
||||
func (d *dadState) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (d *dadState) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &d.nonce)
|
||||
stateSourceObject.Load(1, &d.extendRequest)
|
||||
stateSourceObject.Load(2, &d.done)
|
||||
stateSourceObject.Load(3, &d.timer)
|
||||
stateSourceObject.Load(4, &d.completionHandlers)
|
||||
}
|
||||
|
||||
func (d *DADOptions) StateTypeName() string {
|
||||
return "pkg/tcpip/network/internal/ip.DADOptions"
|
||||
}
|
||||
|
||||
func (d *DADOptions) StateFields() []string {
|
||||
return []string{
|
||||
"Clock",
|
||||
"NonceSize",
|
||||
"ExtendDADTransmits",
|
||||
"Protocol",
|
||||
"NICID",
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DADOptions) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (d *DADOptions) StateSave(stateSinkObject state.Sink) {
|
||||
d.beforeSave()
|
||||
stateSinkObject.Save(0, &d.Clock)
|
||||
stateSinkObject.Save(1, &d.NonceSize)
|
||||
stateSinkObject.Save(2, &d.ExtendDADTransmits)
|
||||
stateSinkObject.Save(3, &d.Protocol)
|
||||
stateSinkObject.Save(4, &d.NICID)
|
||||
}
|
||||
|
||||
func (d *DADOptions) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (d *DADOptions) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &d.Clock)
|
||||
stateSourceObject.Load(1, &d.NonceSize)
|
||||
stateSourceObject.Load(2, &d.ExtendDADTransmits)
|
||||
stateSourceObject.Load(3, &d.Protocol)
|
||||
stateSourceObject.Load(4, &d.NICID)
|
||||
}
|
||||
|
||||
func (d *DAD) StateTypeName() string {
|
||||
return "pkg/tcpip/network/internal/ip.DAD"
|
||||
}
|
||||
|
||||
func (d *DAD) StateFields() []string {
|
||||
return []string{
|
||||
"opts",
|
||||
"configs",
|
||||
"addresses",
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DAD) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (d *DAD) StateSave(stateSinkObject state.Sink) {
|
||||
d.beforeSave()
|
||||
stateSinkObject.Save(0, &d.opts)
|
||||
stateSinkObject.Save(1, &d.configs)
|
||||
stateSinkObject.Save(2, &d.addresses)
|
||||
}
|
||||
|
||||
func (d *DAD) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (d *DAD) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &d.opts)
|
||||
stateSourceObject.Load(1, &d.configs)
|
||||
stateSourceObject.Load(2, &d.addresses)
|
||||
}
|
||||
|
||||
func (e *ErrMessageTooLong) StateTypeName() string {
|
||||
return "pkg/tcpip/network/internal/ip.ErrMessageTooLong"
|
||||
}
|
||||
|
||||
func (e *ErrMessageTooLong) StateFields() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (e *ErrMessageTooLong) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *ErrMessageTooLong) StateSave(stateSinkObject state.Sink) {
|
||||
e.beforeSave()
|
||||
}
|
||||
|
||||
func (e *ErrMessageTooLong) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *ErrMessageTooLong) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
}
|
||||
|
||||
func (e *ErrNoMulticastPendingQueueBufferSpace) StateTypeName() string {
|
||||
return "pkg/tcpip/network/internal/ip.ErrNoMulticastPendingQueueBufferSpace"
|
||||
}
|
||||
|
||||
func (e *ErrNoMulticastPendingQueueBufferSpace) StateFields() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (e *ErrNoMulticastPendingQueueBufferSpace) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *ErrNoMulticastPendingQueueBufferSpace) StateSave(stateSinkObject state.Sink) {
|
||||
e.beforeSave()
|
||||
}
|
||||
|
||||
func (e *ErrNoMulticastPendingQueueBufferSpace) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *ErrNoMulticastPendingQueueBufferSpace) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
}
|
||||
|
||||
func (m *multicastGroupState) StateTypeName() string {
|
||||
return "pkg/tcpip/network/internal/ip.multicastGroupState"
|
||||
}
|
||||
|
||||
func (m *multicastGroupState) StateFields() []string {
|
||||
return []string{
|
||||
"joins",
|
||||
"transmissionLeft",
|
||||
"lastToSendReport",
|
||||
"delayedReportJob",
|
||||
"queriedIncludeSources",
|
||||
"deleteScheduled",
|
||||
}
|
||||
}
|
||||
|
||||
func (m *multicastGroupState) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (m *multicastGroupState) StateSave(stateSinkObject state.Sink) {
|
||||
m.beforeSave()
|
||||
stateSinkObject.Save(0, &m.joins)
|
||||
stateSinkObject.Save(1, &m.transmissionLeft)
|
||||
stateSinkObject.Save(2, &m.lastToSendReport)
|
||||
stateSinkObject.Save(3, &m.delayedReportJob)
|
||||
stateSinkObject.Save(4, &m.queriedIncludeSources)
|
||||
stateSinkObject.Save(5, &m.deleteScheduled)
|
||||
}
|
||||
|
||||
func (m *multicastGroupState) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (m *multicastGroupState) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &m.joins)
|
||||
stateSourceObject.Load(1, &m.transmissionLeft)
|
||||
stateSourceObject.Load(2, &m.lastToSendReport)
|
||||
stateSourceObject.Load(3, &m.delayedReportJob)
|
||||
stateSourceObject.Load(4, &m.queriedIncludeSources)
|
||||
stateSourceObject.Load(5, &m.deleteScheduled)
|
||||
}
|
||||
|
||||
func (g *GenericMulticastProtocolOptions) StateTypeName() string {
|
||||
return "pkg/tcpip/network/internal/ip.GenericMulticastProtocolOptions"
|
||||
}
|
||||
|
||||
func (g *GenericMulticastProtocolOptions) StateFields() []string {
|
||||
return []string{
|
||||
"Clock",
|
||||
"Protocol",
|
||||
"MaxUnsolicitedReportDelay",
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GenericMulticastProtocolOptions) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (g *GenericMulticastProtocolOptions) StateSave(stateSinkObject state.Sink) {
|
||||
g.beforeSave()
|
||||
stateSinkObject.Save(0, &g.Clock)
|
||||
stateSinkObject.Save(1, &g.Protocol)
|
||||
stateSinkObject.Save(2, &g.MaxUnsolicitedReportDelay)
|
||||
}
|
||||
|
||||
func (g *GenericMulticastProtocolOptions) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (g *GenericMulticastProtocolOptions) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &g.Clock)
|
||||
stateSourceObject.Load(1, &g.Protocol)
|
||||
stateSourceObject.Load(2, &g.MaxUnsolicitedReportDelay)
|
||||
}
|
||||
|
||||
func (g *GenericMulticastProtocolState) StateTypeName() string {
|
||||
return "pkg/tcpip/network/internal/ip.GenericMulticastProtocolState"
|
||||
}
|
||||
|
||||
func (g *GenericMulticastProtocolState) StateFields() []string {
|
||||
return []string{
|
||||
"opts",
|
||||
"memberships",
|
||||
"robustnessVariable",
|
||||
"queryInterval",
|
||||
"mode",
|
||||
"modeTimer",
|
||||
"generalQueryV2Timer",
|
||||
"stateChangedReportV2Timer",
|
||||
"stateChangedReportV2TimerSet",
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GenericMulticastProtocolState) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (g *GenericMulticastProtocolState) StateSave(stateSinkObject state.Sink) {
|
||||
g.beforeSave()
|
||||
stateSinkObject.Save(0, &g.opts)
|
||||
stateSinkObject.Save(1, &g.memberships)
|
||||
stateSinkObject.Save(2, &g.robustnessVariable)
|
||||
stateSinkObject.Save(3, &g.queryInterval)
|
||||
stateSinkObject.Save(4, &g.mode)
|
||||
stateSinkObject.Save(5, &g.modeTimer)
|
||||
stateSinkObject.Save(6, &g.generalQueryV2Timer)
|
||||
stateSinkObject.Save(7, &g.stateChangedReportV2Timer)
|
||||
stateSinkObject.Save(8, &g.stateChangedReportV2TimerSet)
|
||||
}
|
||||
|
||||
func (g *GenericMulticastProtocolState) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (g *GenericMulticastProtocolState) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &g.opts)
|
||||
stateSourceObject.Load(1, &g.memberships)
|
||||
stateSourceObject.Load(2, &g.robustnessVariable)
|
||||
stateSourceObject.Load(3, &g.queryInterval)
|
||||
stateSourceObject.Load(4, &g.mode)
|
||||
stateSourceObject.Load(5, &g.modeTimer)
|
||||
stateSourceObject.Load(6, &g.generalQueryV2Timer)
|
||||
stateSourceObject.Load(7, &g.stateChangedReportV2Timer)
|
||||
stateSourceObject.Load(8, &g.stateChangedReportV2TimerSet)
|
||||
}
|
||||
|
||||
func (m *MultiCounterIPForwardingStats) StateTypeName() string {
|
||||
return "pkg/tcpip/network/internal/ip.MultiCounterIPForwardingStats"
|
||||
}
|
||||
|
||||
func (m *MultiCounterIPForwardingStats) StateFields() []string {
|
||||
return []string{
|
||||
"Unrouteable",
|
||||
"ExhaustedTTL",
|
||||
"InitializingSource",
|
||||
"LinkLocalSource",
|
||||
"LinkLocalDestination",
|
||||
"PacketTooBig",
|
||||
"HostUnreachable",
|
||||
"ExtensionHeaderProblem",
|
||||
"UnexpectedMulticastInputInterface",
|
||||
"UnknownOutputEndpoint",
|
||||
"NoMulticastPendingQueueBufferSpace",
|
||||
"OutgoingDeviceNoBufferSpace",
|
||||
"Errors",
|
||||
"OutgoingDeviceClosedForSend",
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MultiCounterIPForwardingStats) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (m *MultiCounterIPForwardingStats) StateSave(stateSinkObject state.Sink) {
|
||||
m.beforeSave()
|
||||
stateSinkObject.Save(0, &m.Unrouteable)
|
||||
stateSinkObject.Save(1, &m.ExhaustedTTL)
|
||||
stateSinkObject.Save(2, &m.InitializingSource)
|
||||
stateSinkObject.Save(3, &m.LinkLocalSource)
|
||||
stateSinkObject.Save(4, &m.LinkLocalDestination)
|
||||
stateSinkObject.Save(5, &m.PacketTooBig)
|
||||
stateSinkObject.Save(6, &m.HostUnreachable)
|
||||
stateSinkObject.Save(7, &m.ExtensionHeaderProblem)
|
||||
stateSinkObject.Save(8, &m.UnexpectedMulticastInputInterface)
|
||||
stateSinkObject.Save(9, &m.UnknownOutputEndpoint)
|
||||
stateSinkObject.Save(10, &m.NoMulticastPendingQueueBufferSpace)
|
||||
stateSinkObject.Save(11, &m.OutgoingDeviceNoBufferSpace)
|
||||
stateSinkObject.Save(12, &m.Errors)
|
||||
stateSinkObject.Save(13, &m.OutgoingDeviceClosedForSend)
|
||||
}
|
||||
|
||||
func (m *MultiCounterIPForwardingStats) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (m *MultiCounterIPForwardingStats) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &m.Unrouteable)
|
||||
stateSourceObject.Load(1, &m.ExhaustedTTL)
|
||||
stateSourceObject.Load(2, &m.InitializingSource)
|
||||
stateSourceObject.Load(3, &m.LinkLocalSource)
|
||||
stateSourceObject.Load(4, &m.LinkLocalDestination)
|
||||
stateSourceObject.Load(5, &m.PacketTooBig)
|
||||
stateSourceObject.Load(6, &m.HostUnreachable)
|
||||
stateSourceObject.Load(7, &m.ExtensionHeaderProblem)
|
||||
stateSourceObject.Load(8, &m.UnexpectedMulticastInputInterface)
|
||||
stateSourceObject.Load(9, &m.UnknownOutputEndpoint)
|
||||
stateSourceObject.Load(10, &m.NoMulticastPendingQueueBufferSpace)
|
||||
stateSourceObject.Load(11, &m.OutgoingDeviceNoBufferSpace)
|
||||
stateSourceObject.Load(12, &m.Errors)
|
||||
stateSourceObject.Load(13, &m.OutgoingDeviceClosedForSend)
|
||||
}
|
||||
|
||||
func (m *MultiCounterIPStats) StateTypeName() string {
|
||||
return "pkg/tcpip/network/internal/ip.MultiCounterIPStats"
|
||||
}
|
||||
|
||||
func (m *MultiCounterIPStats) StateFields() []string {
|
||||
return []string{
|
||||
"PacketsReceived",
|
||||
"ValidPacketsReceived",
|
||||
"DisabledPacketsReceived",
|
||||
"InvalidDestinationAddressesReceived",
|
||||
"InvalidSourceAddressesReceived",
|
||||
"PacketsDelivered",
|
||||
"PacketsSent",
|
||||
"OutgoingPacketErrors",
|
||||
"MalformedPacketsReceived",
|
||||
"MalformedFragmentsReceived",
|
||||
"IPTablesPreroutingDropped",
|
||||
"IPTablesInputDropped",
|
||||
"IPTablesForwardDropped",
|
||||
"IPTablesOutputDropped",
|
||||
"IPTablesPostroutingDropped",
|
||||
"OptionTimestampReceived",
|
||||
"OptionRecordRouteReceived",
|
||||
"OptionRouterAlertReceived",
|
||||
"OptionUnknownReceived",
|
||||
"Forwarding",
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MultiCounterIPStats) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (m *MultiCounterIPStats) StateSave(stateSinkObject state.Sink) {
|
||||
m.beforeSave()
|
||||
stateSinkObject.Save(0, &m.PacketsReceived)
|
||||
stateSinkObject.Save(1, &m.ValidPacketsReceived)
|
||||
stateSinkObject.Save(2, &m.DisabledPacketsReceived)
|
||||
stateSinkObject.Save(3, &m.InvalidDestinationAddressesReceived)
|
||||
stateSinkObject.Save(4, &m.InvalidSourceAddressesReceived)
|
||||
stateSinkObject.Save(5, &m.PacketsDelivered)
|
||||
stateSinkObject.Save(6, &m.PacketsSent)
|
||||
stateSinkObject.Save(7, &m.OutgoingPacketErrors)
|
||||
stateSinkObject.Save(8, &m.MalformedPacketsReceived)
|
||||
stateSinkObject.Save(9, &m.MalformedFragmentsReceived)
|
||||
stateSinkObject.Save(10, &m.IPTablesPreroutingDropped)
|
||||
stateSinkObject.Save(11, &m.IPTablesInputDropped)
|
||||
stateSinkObject.Save(12, &m.IPTablesForwardDropped)
|
||||
stateSinkObject.Save(13, &m.IPTablesOutputDropped)
|
||||
stateSinkObject.Save(14, &m.IPTablesPostroutingDropped)
|
||||
stateSinkObject.Save(15, &m.OptionTimestampReceived)
|
||||
stateSinkObject.Save(16, &m.OptionRecordRouteReceived)
|
||||
stateSinkObject.Save(17, &m.OptionRouterAlertReceived)
|
||||
stateSinkObject.Save(18, &m.OptionUnknownReceived)
|
||||
stateSinkObject.Save(19, &m.Forwarding)
|
||||
}
|
||||
|
||||
func (m *MultiCounterIPStats) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (m *MultiCounterIPStats) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &m.PacketsReceived)
|
||||
stateSourceObject.Load(1, &m.ValidPacketsReceived)
|
||||
stateSourceObject.Load(2, &m.DisabledPacketsReceived)
|
||||
stateSourceObject.Load(3, &m.InvalidDestinationAddressesReceived)
|
||||
stateSourceObject.Load(4, &m.InvalidSourceAddressesReceived)
|
||||
stateSourceObject.Load(5, &m.PacketsDelivered)
|
||||
stateSourceObject.Load(6, &m.PacketsSent)
|
||||
stateSourceObject.Load(7, &m.OutgoingPacketErrors)
|
||||
stateSourceObject.Load(8, &m.MalformedPacketsReceived)
|
||||
stateSourceObject.Load(9, &m.MalformedFragmentsReceived)
|
||||
stateSourceObject.Load(10, &m.IPTablesPreroutingDropped)
|
||||
stateSourceObject.Load(11, &m.IPTablesInputDropped)
|
||||
stateSourceObject.Load(12, &m.IPTablesForwardDropped)
|
||||
stateSourceObject.Load(13, &m.IPTablesOutputDropped)
|
||||
stateSourceObject.Load(14, &m.IPTablesPostroutingDropped)
|
||||
stateSourceObject.Load(15, &m.OptionTimestampReceived)
|
||||
stateSourceObject.Load(16, &m.OptionRecordRouteReceived)
|
||||
stateSourceObject.Load(17, &m.OptionRouterAlertReceived)
|
||||
stateSourceObject.Load(18, &m.OptionUnknownReceived)
|
||||
stateSourceObject.Load(19, &m.Forwarding)
|
||||
}
|
||||
|
||||
func init() {
|
||||
state.Register((*dadState)(nil))
|
||||
state.Register((*DADOptions)(nil))
|
||||
state.Register((*DAD)(nil))
|
||||
state.Register((*ErrMessageTooLong)(nil))
|
||||
state.Register((*ErrNoMulticastPendingQueueBufferSpace)(nil))
|
||||
state.Register((*multicastGroupState)(nil))
|
||||
state.Register((*GenericMulticastProtocolOptions)(nil))
|
||||
state.Register((*GenericMulticastProtocolState)(nil))
|
||||
state.Register((*MultiCounterIPForwardingStats)(nil))
|
||||
state.Register((*MultiCounterIPStats)(nil))
|
||||
}
|
||||
219
pkg/tcpip/network/internal/ip/stats.go
Normal file
219
pkg/tcpip/network/internal/ip/stats.go
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
// 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.
|
||||
|
||||
package ip
|
||||
|
||||
import "github.com/sagernet/gvisor/pkg/tcpip"
|
||||
|
||||
// LINT.IfChange(MultiCounterIPForwardingStats)
|
||||
|
||||
// MultiCounterIPForwardingStats holds IP forwarding statistics. Each counter
|
||||
// may have several versions.
|
||||
//
|
||||
// +stateify savable
|
||||
type MultiCounterIPForwardingStats struct {
|
||||
// Unrouteable is the number of IP packets received which were dropped
|
||||
// because the netstack could not construct a route to their
|
||||
// destination.
|
||||
Unrouteable tcpip.MultiCounterStat
|
||||
|
||||
// ExhaustedTTL is the number of IP packets received which were dropped
|
||||
// because their TTL was exhausted.
|
||||
ExhaustedTTL tcpip.MultiCounterStat
|
||||
|
||||
// InitializingSource is the number of IP packets which were dropped
|
||||
// because they contained a source address that may only be used on the local
|
||||
// network as part of initialization work.
|
||||
InitializingSource tcpip.MultiCounterStat
|
||||
|
||||
// LinkLocalSource is the number of IP packets which were dropped
|
||||
// because they contained a link-local source address.
|
||||
LinkLocalSource tcpip.MultiCounterStat
|
||||
|
||||
// LinkLocalDestination is the number of IP packets which were dropped
|
||||
// because they contained a link-local destination address.
|
||||
LinkLocalDestination tcpip.MultiCounterStat
|
||||
|
||||
// PacketTooBig is the number of IP packets which were dropped because they
|
||||
// were too big for the outgoing MTU.
|
||||
PacketTooBig tcpip.MultiCounterStat
|
||||
|
||||
// HostUnreachable is the number of IP packets received which could not be
|
||||
// successfully forwarded due to an unresolvable next hop.
|
||||
HostUnreachable tcpip.MultiCounterStat
|
||||
|
||||
// ExtensionHeaderProblem is the number of IP packets which were dropped
|
||||
// because of a problem encountered when processing an IPv6 extension
|
||||
// header.
|
||||
ExtensionHeaderProblem tcpip.MultiCounterStat
|
||||
|
||||
// UnexpectedMulticastInputInterface is the number of multicast packets that
|
||||
// were received on an interface that did not match the corresponding route's
|
||||
// expected input interface.
|
||||
UnexpectedMulticastInputInterface tcpip.MultiCounterStat
|
||||
|
||||
// UnknownOutputEndpoint is the number of packets that could not be forwarded
|
||||
// because the output endpoint could not be found.
|
||||
UnknownOutputEndpoint tcpip.MultiCounterStat
|
||||
|
||||
// NoMulticastPendingQueueBufferSpace is the number of multicast packets that
|
||||
// were dropped due to insufficient buffer space in the pending packet queue.
|
||||
NoMulticastPendingQueueBufferSpace tcpip.MultiCounterStat
|
||||
|
||||
// OutgoingDeviceNoBufferSpace is the number of packets that were dropped due
|
||||
// to insufficient space in the outgoing device.
|
||||
OutgoingDeviceNoBufferSpace tcpip.MultiCounterStat
|
||||
|
||||
// Errors is the number of IP packets received which could not be
|
||||
// successfully forwarded.
|
||||
Errors tcpip.MultiCounterStat
|
||||
|
||||
// OutgoingDeviceClosedForSend is the number of packets that were dropped due
|
||||
// to the outgoing device being closed for send.
|
||||
OutgoingDeviceClosedForSend tcpip.MultiCounterStat
|
||||
}
|
||||
|
||||
// Init sets internal counters to track a and b counters.
|
||||
func (m *MultiCounterIPForwardingStats) Init(a, b *tcpip.IPForwardingStats) {
|
||||
m.Unrouteable.Init(a.Unrouteable, b.Unrouteable)
|
||||
m.Errors.Init(a.Errors, b.Errors)
|
||||
m.InitializingSource.Init(a.InitializingSource, b.InitializingSource)
|
||||
m.LinkLocalSource.Init(a.LinkLocalSource, b.LinkLocalSource)
|
||||
m.LinkLocalDestination.Init(a.LinkLocalDestination, b.LinkLocalDestination)
|
||||
m.ExtensionHeaderProblem.Init(a.ExtensionHeaderProblem, b.ExtensionHeaderProblem)
|
||||
m.PacketTooBig.Init(a.PacketTooBig, b.PacketTooBig)
|
||||
m.ExhaustedTTL.Init(a.ExhaustedTTL, b.ExhaustedTTL)
|
||||
m.HostUnreachable.Init(a.HostUnreachable, b.HostUnreachable)
|
||||
m.UnexpectedMulticastInputInterface.Init(a.UnexpectedMulticastInputInterface, b.UnexpectedMulticastInputInterface)
|
||||
m.UnknownOutputEndpoint.Init(a.UnknownOutputEndpoint, b.UnknownOutputEndpoint)
|
||||
m.NoMulticastPendingQueueBufferSpace.Init(a.NoMulticastPendingQueueBufferSpace, b.NoMulticastPendingQueueBufferSpace)
|
||||
m.OutgoingDeviceNoBufferSpace.Init(a.OutgoingDeviceNoBufferSpace, b.OutgoingDeviceNoBufferSpace)
|
||||
m.OutgoingDeviceClosedForSend.Init(a.OutgoingDeviceClosedForSend, b.OutgoingDeviceClosedForSend)
|
||||
}
|
||||
|
||||
// LINT.ThenChange(../../../tcpip.go:IPForwardingStats)
|
||||
|
||||
// LINT.IfChange(MultiCounterIPStats)
|
||||
|
||||
// MultiCounterIPStats holds IP statistics, each counter may have several
|
||||
// versions.
|
||||
//
|
||||
// +stateify savable
|
||||
type MultiCounterIPStats struct {
|
||||
// PacketsReceived is the number of IP packets received from the link
|
||||
// layer.
|
||||
PacketsReceived tcpip.MultiCounterStat
|
||||
|
||||
// ValidPacketsReceived is the number of valid IP packets that reached the IP
|
||||
// layer.
|
||||
ValidPacketsReceived tcpip.MultiCounterStat
|
||||
|
||||
// DisabledPacketsReceived is the number of IP packets received from
|
||||
// the link layer when the IP layer is disabled.
|
||||
DisabledPacketsReceived tcpip.MultiCounterStat
|
||||
|
||||
// InvalidDestinationAddressesReceived is the number of IP packets
|
||||
// received with an unknown or invalid destination address.
|
||||
InvalidDestinationAddressesReceived tcpip.MultiCounterStat
|
||||
|
||||
// InvalidSourceAddressesReceived is the number of IP packets received
|
||||
// with a source address that should never have been received on the
|
||||
// wire.
|
||||
InvalidSourceAddressesReceived tcpip.MultiCounterStat
|
||||
|
||||
// PacketsDelivered is the number of incoming IP packets successfully
|
||||
// delivered to the transport layer.
|
||||
PacketsDelivered tcpip.MultiCounterStat
|
||||
|
||||
// PacketsSent is the number of IP packets sent via WritePacket.
|
||||
PacketsSent tcpip.MultiCounterStat
|
||||
|
||||
// OutgoingPacketErrors is the number of IP packets which failed to
|
||||
// write to a link-layer endpoint.
|
||||
OutgoingPacketErrors tcpip.MultiCounterStat
|
||||
|
||||
// MalformedPacketsReceived is the number of IP Packets that were
|
||||
// dropped due to the IP packet header failing validation checks.
|
||||
MalformedPacketsReceived tcpip.MultiCounterStat
|
||||
|
||||
// MalformedFragmentsReceived is the number of IP Fragments that were
|
||||
// dropped due to the fragment failing validation checks.
|
||||
MalformedFragmentsReceived tcpip.MultiCounterStat
|
||||
|
||||
// IPTablesPreroutingDropped is the number of IP packets dropped in the
|
||||
// Prerouting chain.
|
||||
IPTablesPreroutingDropped tcpip.MultiCounterStat
|
||||
|
||||
// IPTablesInputDropped is the number of IP packets dropped in the
|
||||
// Input chain.
|
||||
IPTablesInputDropped tcpip.MultiCounterStat
|
||||
|
||||
// IPTablesForwardDropped is the number of IP packets dropped in the
|
||||
// Forward chain.
|
||||
IPTablesForwardDropped tcpip.MultiCounterStat
|
||||
|
||||
// IPTablesOutputDropped is the number of IP packets dropped in the
|
||||
// Output chain.
|
||||
IPTablesOutputDropped tcpip.MultiCounterStat
|
||||
|
||||
// IPTablesPostroutingDropped is the number of IP packets dropped in
|
||||
// the Postrouting chain.
|
||||
IPTablesPostroutingDropped tcpip.MultiCounterStat
|
||||
|
||||
// TODO(https://gvisor.dev/issues/5529): Move the IPv4-only option
|
||||
// stats out of IPStats.
|
||||
|
||||
// OptionTimestampReceived is the number of Timestamp options seen.
|
||||
OptionTimestampReceived tcpip.MultiCounterStat
|
||||
|
||||
// OptionRecordRouteReceived is the number of Record Route options
|
||||
// seen.
|
||||
OptionRecordRouteReceived tcpip.MultiCounterStat
|
||||
|
||||
// OptionRouterAlertReceived is the number of Router Alert options
|
||||
// seen.
|
||||
OptionRouterAlertReceived tcpip.MultiCounterStat
|
||||
|
||||
// OptionUnknownReceived is the number of unknown IP options seen.
|
||||
OptionUnknownReceived tcpip.MultiCounterStat
|
||||
|
||||
// Forwarding collects stats related to IP forwarding.
|
||||
Forwarding MultiCounterIPForwardingStats
|
||||
}
|
||||
|
||||
// Init sets internal counters to track a and b counters.
|
||||
func (m *MultiCounterIPStats) Init(a, b *tcpip.IPStats) {
|
||||
m.PacketsReceived.Init(a.PacketsReceived, b.PacketsReceived)
|
||||
m.ValidPacketsReceived.Init(a.ValidPacketsReceived, b.ValidPacketsReceived)
|
||||
m.DisabledPacketsReceived.Init(a.DisabledPacketsReceived, b.DisabledPacketsReceived)
|
||||
m.InvalidDestinationAddressesReceived.Init(a.InvalidDestinationAddressesReceived, b.InvalidDestinationAddressesReceived)
|
||||
m.InvalidSourceAddressesReceived.Init(a.InvalidSourceAddressesReceived, b.InvalidSourceAddressesReceived)
|
||||
m.PacketsDelivered.Init(a.PacketsDelivered, b.PacketsDelivered)
|
||||
m.PacketsSent.Init(a.PacketsSent, b.PacketsSent)
|
||||
m.OutgoingPacketErrors.Init(a.OutgoingPacketErrors, b.OutgoingPacketErrors)
|
||||
m.MalformedPacketsReceived.Init(a.MalformedPacketsReceived, b.MalformedPacketsReceived)
|
||||
m.MalformedFragmentsReceived.Init(a.MalformedFragmentsReceived, b.MalformedFragmentsReceived)
|
||||
m.IPTablesPreroutingDropped.Init(a.IPTablesPreroutingDropped, b.IPTablesPreroutingDropped)
|
||||
m.IPTablesInputDropped.Init(a.IPTablesInputDropped, b.IPTablesInputDropped)
|
||||
m.IPTablesForwardDropped.Init(a.IPTablesForwardDropped, b.IPTablesForwardDropped)
|
||||
m.IPTablesOutputDropped.Init(a.IPTablesOutputDropped, b.IPTablesOutputDropped)
|
||||
m.IPTablesPostroutingDropped.Init(a.IPTablesPostroutingDropped, b.IPTablesPostroutingDropped)
|
||||
m.OptionTimestampReceived.Init(a.OptionTimestampReceived, b.OptionTimestampReceived)
|
||||
m.OptionRecordRouteReceived.Init(a.OptionRecordRouteReceived, b.OptionRecordRouteReceived)
|
||||
m.OptionRouterAlertReceived.Init(a.OptionRouterAlertReceived, b.OptionRouterAlertReceived)
|
||||
m.OptionUnknownReceived.Init(a.OptionUnknownReceived, b.OptionUnknownReceived)
|
||||
m.Forwarding.Init(&a.Forwarding, &b.Forwarding)
|
||||
}
|
||||
|
||||
// LINT.ThenChange(../../../tcpip.go:IPStats)
|
||||
137
pkg/tcpip/network/internal/multicast/multicast_state_autogen.go
Normal file
137
pkg/tcpip/network/internal/multicast/multicast_state_autogen.go
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
// automatically generated by stateify.
|
||||
|
||||
package multicast
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/state"
|
||||
)
|
||||
|
||||
func (r *RouteTable) StateTypeName() string {
|
||||
return "pkg/tcpip/network/internal/multicast.RouteTable"
|
||||
}
|
||||
|
||||
func (r *RouteTable) StateFields() []string {
|
||||
return []string{
|
||||
"installedRoutes",
|
||||
"pendingRoutes",
|
||||
"cleanupPendingRoutesTimer",
|
||||
"isCleanupRoutineRunning",
|
||||
"config",
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RouteTable) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (r *RouteTable) StateSave(stateSinkObject state.Sink) {
|
||||
r.beforeSave()
|
||||
stateSinkObject.Save(0, &r.installedRoutes)
|
||||
stateSinkObject.Save(1, &r.pendingRoutes)
|
||||
stateSinkObject.Save(2, &r.cleanupPendingRoutesTimer)
|
||||
stateSinkObject.Save(3, &r.isCleanupRoutineRunning)
|
||||
stateSinkObject.Save(4, &r.config)
|
||||
}
|
||||
|
||||
func (r *RouteTable) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (r *RouteTable) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &r.installedRoutes)
|
||||
stateSourceObject.Load(1, &r.pendingRoutes)
|
||||
stateSourceObject.Load(2, &r.cleanupPendingRoutesTimer)
|
||||
stateSourceObject.Load(3, &r.isCleanupRoutineRunning)
|
||||
stateSourceObject.Load(4, &r.config)
|
||||
}
|
||||
|
||||
func (r *InstalledRoute) StateTypeName() string {
|
||||
return "pkg/tcpip/network/internal/multicast.InstalledRoute"
|
||||
}
|
||||
|
||||
func (r *InstalledRoute) StateFields() []string {
|
||||
return []string{
|
||||
"MulticastRoute",
|
||||
"lastUsedTimestamp",
|
||||
}
|
||||
}
|
||||
|
||||
func (r *InstalledRoute) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (r *InstalledRoute) StateSave(stateSinkObject state.Sink) {
|
||||
r.beforeSave()
|
||||
stateSinkObject.Save(0, &r.MulticastRoute)
|
||||
stateSinkObject.Save(1, &r.lastUsedTimestamp)
|
||||
}
|
||||
|
||||
func (r *InstalledRoute) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (r *InstalledRoute) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &r.MulticastRoute)
|
||||
stateSourceObject.Load(1, &r.lastUsedTimestamp)
|
||||
}
|
||||
|
||||
func (p *PendingRoute) StateTypeName() string {
|
||||
return "pkg/tcpip/network/internal/multicast.PendingRoute"
|
||||
}
|
||||
|
||||
func (p *PendingRoute) StateFields() []string {
|
||||
return []string{
|
||||
"packets",
|
||||
"expiration",
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PendingRoute) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (p *PendingRoute) StateSave(stateSinkObject state.Sink) {
|
||||
p.beforeSave()
|
||||
stateSinkObject.Save(0, &p.packets)
|
||||
stateSinkObject.Save(1, &p.expiration)
|
||||
}
|
||||
|
||||
func (p *PendingRoute) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (p *PendingRoute) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &p.packets)
|
||||
stateSourceObject.Load(1, &p.expiration)
|
||||
}
|
||||
|
||||
func (c *Config) StateTypeName() string {
|
||||
return "pkg/tcpip/network/internal/multicast.Config"
|
||||
}
|
||||
|
||||
func (c *Config) StateFields() []string {
|
||||
return []string{
|
||||
"MaxPendingQueueSize",
|
||||
"Clock",
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Config) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (c *Config) StateSave(stateSinkObject state.Sink) {
|
||||
c.beforeSave()
|
||||
stateSinkObject.Save(0, &c.MaxPendingQueueSize)
|
||||
stateSinkObject.Save(1, &c.Clock)
|
||||
}
|
||||
|
||||
func (c *Config) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (c *Config) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &c.MaxPendingQueueSize)
|
||||
stateSourceObject.Load(1, &c.Clock)
|
||||
}
|
||||
|
||||
func init() {
|
||||
state.Register((*RouteTable)(nil))
|
||||
state.Register((*InstalledRoute)(nil))
|
||||
state.Register((*PendingRoute)(nil))
|
||||
state.Register((*Config)(nil))
|
||||
}
|
||||
446
pkg/tcpip/network/internal/multicast/route_table.go
Normal file
446
pkg/tcpip/network/internal/multicast/route_table.go
Normal file
|
|
@ -0,0 +1,446 @@
|
|||
// Copyright 2022 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 multicast contains utilities for supporting multicast routing.
|
||||
package multicast
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
// RouteTable represents a multicast routing table.
|
||||
//
|
||||
// +stateify savable
|
||||
type RouteTable struct {
|
||||
// Internally, installed and pending routes are stored and locked separately
|
||||
// A couple of reasons for structuring the table this way:
|
||||
//
|
||||
// 1. We can avoid write locking installed routes when pending packets are
|
||||
// being queued. In other words, the happy path of reading installed
|
||||
// routes doesn't require an exclusive lock.
|
||||
// 2. The cleanup process for expired routes only needs to operate on pending
|
||||
// routes. Like above, a write lock on the installed routes can be
|
||||
// avoided.
|
||||
// 3. This structure is similar to the Linux implementation:
|
||||
// https://github.com/torvalds/linux/blob/cffb2b72d3e/include/linux/mroute_base.h#L250
|
||||
|
||||
// The installedMu lock should typically be acquired before the pendingMu
|
||||
// lock. This ensures that installed routes can continue to be read even when
|
||||
// the pending routes are write locked.
|
||||
|
||||
installedMu sync.RWMutex `state:"nosave"`
|
||||
// Maintaining pointers ensures that the installed routes are exclusively
|
||||
// locked only when a route is being installed.
|
||||
// +checklocks:installedMu
|
||||
installedRoutes map[stack.UnicastSourceAndMulticastDestination]*InstalledRoute
|
||||
|
||||
pendingMu sync.RWMutex `state:"nosave"`
|
||||
// +checklocks:pendingMu
|
||||
pendingRoutes map[stack.UnicastSourceAndMulticastDestination]PendingRoute
|
||||
// cleanupPendingRoutesTimer is a timer that triggers a routine to remove
|
||||
// pending routes that are expired.
|
||||
// +checklocks:pendingMu
|
||||
cleanupPendingRoutesTimer tcpip.Timer
|
||||
// +checklocks:pendingMu
|
||||
isCleanupRoutineRunning bool
|
||||
|
||||
config Config
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrNoBufferSpace indicates that no buffer space is available in the
|
||||
// pending route packet queue.
|
||||
ErrNoBufferSpace = errors.New("unable to queue packet, no buffer space available")
|
||||
|
||||
// ErrMissingClock indicates that a clock was not provided as part of the
|
||||
// Config, but is required.
|
||||
ErrMissingClock = errors.New("clock must not be nil")
|
||||
|
||||
// ErrAlreadyInitialized indicates that RouteTable.Init was already invoked.
|
||||
ErrAlreadyInitialized = errors.New("table is already initialized")
|
||||
)
|
||||
|
||||
// InstalledRoute represents a route that is in the installed state.
|
||||
//
|
||||
// If a route is in the installed state, then it may be used to forward
|
||||
// multicast packets.
|
||||
//
|
||||
// +stateify savable
|
||||
type InstalledRoute struct {
|
||||
stack.MulticastRoute
|
||||
|
||||
lastUsedTimestampMu sync.RWMutex `state:"nosave"`
|
||||
// +checklocks:lastUsedTimestampMu
|
||||
lastUsedTimestamp tcpip.MonotonicTime
|
||||
}
|
||||
|
||||
// LastUsedTimestamp returns a monotonic timestamp that corresponds to the last
|
||||
// time the route was used or updated.
|
||||
func (r *InstalledRoute) LastUsedTimestamp() tcpip.MonotonicTime {
|
||||
r.lastUsedTimestampMu.RLock()
|
||||
defer r.lastUsedTimestampMu.RUnlock()
|
||||
|
||||
return r.lastUsedTimestamp
|
||||
}
|
||||
|
||||
// SetLastUsedTimestamp sets the time that the route was last used.
|
||||
//
|
||||
// The timestamp is only updated if it occurs after the currently set
|
||||
// timestamp. Callers should invoke this anytime the route is used to forward a
|
||||
// packet.
|
||||
func (r *InstalledRoute) SetLastUsedTimestamp(monotonicTime tcpip.MonotonicTime) {
|
||||
r.lastUsedTimestampMu.Lock()
|
||||
defer r.lastUsedTimestampMu.Unlock()
|
||||
|
||||
if monotonicTime.After(r.lastUsedTimestamp) {
|
||||
r.lastUsedTimestamp = monotonicTime
|
||||
}
|
||||
}
|
||||
|
||||
// PendingRoute represents a route that is in the "pending" state.
|
||||
//
|
||||
// A route is in the pending state if an installed route does not yet exist
|
||||
// for the entry. For such routes, packets are added to an expiring queue until
|
||||
// a route is installed.
|
||||
//
|
||||
// +stateify savable
|
||||
type PendingRoute struct {
|
||||
packets []*stack.PacketBuffer
|
||||
|
||||
// expiration is the timestamp at which the pending route should be expired.
|
||||
//
|
||||
// If this value is before the current time, then this pending route will
|
||||
// be dropped.
|
||||
expiration tcpip.MonotonicTime
|
||||
}
|
||||
|
||||
func (p *PendingRoute) releasePackets() {
|
||||
for _, pkt := range p.packets {
|
||||
pkt.DecRef()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PendingRoute) isExpired(currentTime tcpip.MonotonicTime) bool {
|
||||
return currentTime.After(p.expiration)
|
||||
}
|
||||
|
||||
const (
|
||||
// DefaultMaxPendingQueueSize corresponds to the number of elements that can
|
||||
// be in the packet queue for a pending route.
|
||||
//
|
||||
// Matches the Linux default queue size:
|
||||
// https://github.com/torvalds/linux/blob/26291c54e11/net/ipv6/ip6mr.c#L1186
|
||||
DefaultMaxPendingQueueSize uint8 = 3
|
||||
|
||||
// DefaultPendingRouteExpiration is the default maximum lifetime of a pending
|
||||
// route.
|
||||
//
|
||||
// Matches the Linux default:
|
||||
// https://github.com/torvalds/linux/blob/26291c54e11/net/ipv6/ip6mr.c#L991
|
||||
DefaultPendingRouteExpiration time.Duration = 10 * time.Second
|
||||
|
||||
// DefaultCleanupInterval is the default frequency of the routine that
|
||||
// expires pending routes.
|
||||
//
|
||||
// Matches the Linux default:
|
||||
// https://github.com/torvalds/linux/blob/26291c54e11/net/ipv6/ip6mr.c#L793
|
||||
DefaultCleanupInterval time.Duration = 10 * time.Second
|
||||
)
|
||||
|
||||
// Config represents the options for configuring a RouteTable.
|
||||
//
|
||||
// +stateify savable
|
||||
type Config struct {
|
||||
// MaxPendingQueueSize corresponds to the maximum number of queued packets
|
||||
// for a pending route.
|
||||
//
|
||||
// If the caller attempts to queue a packet and the queue already contains
|
||||
// MaxPendingQueueSize elements, then the packet will be rejected and should
|
||||
// not be forwarded.
|
||||
MaxPendingQueueSize uint8
|
||||
|
||||
// Clock represents the clock that should be used to obtain the current time.
|
||||
//
|
||||
// This field is required and must have a non-nil value.
|
||||
Clock tcpip.Clock
|
||||
}
|
||||
|
||||
// DefaultConfig returns the default configuration for the table.
|
||||
func DefaultConfig(clock tcpip.Clock) Config {
|
||||
return Config{
|
||||
MaxPendingQueueSize: DefaultMaxPendingQueueSize,
|
||||
Clock: clock,
|
||||
}
|
||||
}
|
||||
|
||||
// Init initializes the RouteTable with the provided config.
|
||||
//
|
||||
// An error is returned if the config is not valid.
|
||||
//
|
||||
// Must be called before any other function on the table.
|
||||
func (r *RouteTable) Init(config Config) error {
|
||||
r.installedMu.Lock()
|
||||
defer r.installedMu.Unlock()
|
||||
r.pendingMu.Lock()
|
||||
defer r.pendingMu.Unlock()
|
||||
|
||||
if r.installedRoutes != nil {
|
||||
return ErrAlreadyInitialized
|
||||
}
|
||||
|
||||
if config.Clock == nil {
|
||||
return ErrMissingClock
|
||||
}
|
||||
|
||||
r.config = config
|
||||
r.installedRoutes = make(map[stack.UnicastSourceAndMulticastDestination]*InstalledRoute)
|
||||
r.pendingRoutes = make(map[stack.UnicastSourceAndMulticastDestination]PendingRoute)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close cleans up resources held by the table.
|
||||
//
|
||||
// Calling this will stop the cleanup routine and release any packets owned by
|
||||
// the table.
|
||||
func (r *RouteTable) Close() {
|
||||
r.pendingMu.Lock()
|
||||
defer r.pendingMu.Unlock()
|
||||
|
||||
if r.cleanupPendingRoutesTimer != nil {
|
||||
r.cleanupPendingRoutesTimer.Stop()
|
||||
}
|
||||
|
||||
for key, route := range r.pendingRoutes {
|
||||
delete(r.pendingRoutes, key)
|
||||
route.releasePackets()
|
||||
}
|
||||
}
|
||||
|
||||
// maybeStopCleanupRoutine stops the pending routes cleanup routine if no
|
||||
// pending routes exist.
|
||||
//
|
||||
// Returns true if the timer is not running. Otherwise, returns false.
|
||||
//
|
||||
// +checklocks:r.pendingMu
|
||||
func (r *RouteTable) maybeStopCleanupRoutineLocked() bool {
|
||||
if !r.isCleanupRoutineRunning {
|
||||
return true
|
||||
}
|
||||
|
||||
if len(r.pendingRoutes) == 0 {
|
||||
r.cleanupPendingRoutesTimer.Stop()
|
||||
r.isCleanupRoutineRunning = false
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *RouteTable) cleanupPendingRoutes() {
|
||||
currentTime := r.config.Clock.NowMonotonic()
|
||||
r.pendingMu.Lock()
|
||||
defer r.pendingMu.Unlock()
|
||||
|
||||
for key, route := range r.pendingRoutes {
|
||||
if route.isExpired(currentTime) {
|
||||
delete(r.pendingRoutes, key)
|
||||
route.releasePackets()
|
||||
}
|
||||
}
|
||||
|
||||
if stopped := r.maybeStopCleanupRoutineLocked(); !stopped {
|
||||
r.cleanupPendingRoutesTimer.Reset(DefaultCleanupInterval)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RouteTable) newPendingRoute() PendingRoute {
|
||||
return PendingRoute{
|
||||
packets: make([]*stack.PacketBuffer, 0, r.config.MaxPendingQueueSize),
|
||||
expiration: r.config.Clock.NowMonotonic().Add(DefaultPendingRouteExpiration),
|
||||
}
|
||||
}
|
||||
|
||||
// NewInstalledRoute instantiates an installed route for the table.
|
||||
func (r *RouteTable) NewInstalledRoute(route stack.MulticastRoute) *InstalledRoute {
|
||||
return &InstalledRoute{
|
||||
MulticastRoute: route,
|
||||
lastUsedTimestamp: r.config.Clock.NowMonotonic(),
|
||||
}
|
||||
}
|
||||
|
||||
// GetRouteResult represents the result of calling GetRouteOrInsertPending.
|
||||
type GetRouteResult struct {
|
||||
// GetRouteResultState signals the result of calling GetRouteOrInsertPending.
|
||||
GetRouteResultState GetRouteResultState
|
||||
|
||||
// InstalledRoute represents the existing installed route. This field will
|
||||
// only be populated if the GetRouteResultState is InstalledRouteFound.
|
||||
InstalledRoute *InstalledRoute
|
||||
}
|
||||
|
||||
// GetRouteResultState signals the result of calling GetRouteOrInsertPending.
|
||||
type GetRouteResultState uint8
|
||||
|
||||
const (
|
||||
// InstalledRouteFound indicates that an InstalledRoute was found.
|
||||
InstalledRouteFound GetRouteResultState = iota
|
||||
|
||||
// PacketQueuedInPendingRoute indicates that the packet was queued in an
|
||||
// existing pending route.
|
||||
PacketQueuedInPendingRoute
|
||||
|
||||
// NoRouteFoundAndPendingInserted indicates that no route was found and that
|
||||
// a pending route was newly inserted into the RouteTable.
|
||||
NoRouteFoundAndPendingInserted
|
||||
)
|
||||
|
||||
func (e GetRouteResultState) String() string {
|
||||
switch e {
|
||||
case InstalledRouteFound:
|
||||
return "InstalledRouteFound"
|
||||
case PacketQueuedInPendingRoute:
|
||||
return "PacketQueuedInPendingRoute"
|
||||
case NoRouteFoundAndPendingInserted:
|
||||
return "NoRouteFoundAndPendingInserted"
|
||||
default:
|
||||
return fmt.Sprintf("%d", uint8(e))
|
||||
}
|
||||
}
|
||||
|
||||
// GetRouteOrInsertPending attempts to fetch the installed route that matches
|
||||
// the provided key.
|
||||
//
|
||||
// If no matching installed route is found, then the pkt is cloned and queued
|
||||
// in a pending route. The GetRouteResult.GetRouteResultState will indicate
|
||||
// whether the pkt was queued in a new pending route or an existing one.
|
||||
//
|
||||
// If the relevant pending route queue is at max capacity, then returns false.
|
||||
// Otherwise, returns true.
|
||||
func (r *RouteTable) GetRouteOrInsertPending(key stack.UnicastSourceAndMulticastDestination, pkt *stack.PacketBuffer) (GetRouteResult, bool) {
|
||||
r.installedMu.RLock()
|
||||
defer r.installedMu.RUnlock()
|
||||
|
||||
if route, ok := r.installedRoutes[key]; ok {
|
||||
return GetRouteResult{GetRouteResultState: InstalledRouteFound, InstalledRoute: route}, true
|
||||
}
|
||||
|
||||
r.pendingMu.Lock()
|
||||
defer r.pendingMu.Unlock()
|
||||
|
||||
pendingRoute, getRouteResultState := r.getOrCreatePendingRouteRLocked(key)
|
||||
if len(pendingRoute.packets) >= int(r.config.MaxPendingQueueSize) {
|
||||
// The incoming packet is rejected if the pending queue is already at max
|
||||
// capacity. This behavior matches the Linux implementation:
|
||||
// https://github.com/torvalds/linux/blob/ae085d7f936/net/ipv4/ipmr.c#L1147
|
||||
return GetRouteResult{}, false
|
||||
}
|
||||
pendingRoute.packets = append(pendingRoute.packets, pkt.Clone())
|
||||
r.pendingRoutes[key] = pendingRoute
|
||||
|
||||
if !r.isCleanupRoutineRunning {
|
||||
// The cleanup routine isn't running, but should be. Start it.
|
||||
if r.cleanupPendingRoutesTimer == nil {
|
||||
r.cleanupPendingRoutesTimer = r.config.Clock.AfterFunc(DefaultCleanupInterval, r.cleanupPendingRoutes)
|
||||
} else {
|
||||
r.cleanupPendingRoutesTimer.Reset(DefaultCleanupInterval)
|
||||
}
|
||||
r.isCleanupRoutineRunning = true
|
||||
}
|
||||
|
||||
return GetRouteResult{GetRouteResultState: getRouteResultState, InstalledRoute: nil}, true
|
||||
}
|
||||
|
||||
// +checklocks:r.pendingMu
|
||||
func (r *RouteTable) getOrCreatePendingRouteRLocked(key stack.UnicastSourceAndMulticastDestination) (PendingRoute, GetRouteResultState) {
|
||||
if pendingRoute, ok := r.pendingRoutes[key]; ok {
|
||||
return pendingRoute, PacketQueuedInPendingRoute
|
||||
}
|
||||
return r.newPendingRoute(), NoRouteFoundAndPendingInserted
|
||||
}
|
||||
|
||||
// AddInstalledRoute adds the provided route to the table.
|
||||
//
|
||||
// Packets that were queued while the route was in the pending state are
|
||||
// returned. The caller assumes ownership of these packets and is responsible
|
||||
// for forwarding and releasing them. If an installed route already exists for
|
||||
// the provided key, then it is overwritten.
|
||||
func (r *RouteTable) AddInstalledRoute(key stack.UnicastSourceAndMulticastDestination, route *InstalledRoute) []*stack.PacketBuffer {
|
||||
r.installedMu.Lock()
|
||||
defer r.installedMu.Unlock()
|
||||
r.installedRoutes[key] = route
|
||||
|
||||
r.pendingMu.Lock()
|
||||
pendingRoute, ok := r.pendingRoutes[key]
|
||||
delete(r.pendingRoutes, key)
|
||||
// No need to reset the timer here. The cleanup routine is responsible for
|
||||
// doing so.
|
||||
_ = r.maybeStopCleanupRoutineLocked()
|
||||
r.pendingMu.Unlock()
|
||||
|
||||
// Ignore the pending route if it is expired. It may be in this state since
|
||||
// the cleanup process is only run periodically.
|
||||
if !ok || pendingRoute.isExpired(r.config.Clock.NowMonotonic()) {
|
||||
pendingRoute.releasePackets()
|
||||
return nil
|
||||
}
|
||||
|
||||
return pendingRoute.packets
|
||||
}
|
||||
|
||||
// RemoveInstalledRoute deletes any installed route that matches the provided
|
||||
// key.
|
||||
//
|
||||
// Returns true if a route was removed. Otherwise returns false.
|
||||
func (r *RouteTable) RemoveInstalledRoute(key stack.UnicastSourceAndMulticastDestination) bool {
|
||||
r.installedMu.Lock()
|
||||
defer r.installedMu.Unlock()
|
||||
|
||||
if _, ok := r.installedRoutes[key]; ok {
|
||||
delete(r.installedRoutes, key)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// RemoveAllInstalledRoutes removes all installed routes from the table.
|
||||
func (r *RouteTable) RemoveAllInstalledRoutes() {
|
||||
r.installedMu.Lock()
|
||||
defer r.installedMu.Unlock()
|
||||
|
||||
for key := range r.installedRoutes {
|
||||
delete(r.installedRoutes, key)
|
||||
}
|
||||
}
|
||||
|
||||
// GetLastUsedTimestamp returns a monotonic timestamp that represents the last
|
||||
// time the route that matches the provided key was used or updated.
|
||||
//
|
||||
// Returns true if a matching route was found. Otherwise returns false.
|
||||
func (r *RouteTable) GetLastUsedTimestamp(key stack.UnicastSourceAndMulticastDestination) (tcpip.MonotonicTime, bool) {
|
||||
r.installedMu.RLock()
|
||||
defer r.installedMu.RUnlock()
|
||||
|
||||
if route, ok := r.installedRoutes[key]; ok {
|
||||
return route.LastUsedTimestamp(), true
|
||||
}
|
||||
return tcpip.MonotonicTime{}, false
|
||||
}
|
||||
709
pkg/tcpip/network/ipv4/icmp.go
Normal file
709
pkg/tcpip/network/ipv4/icmp.go
Normal file
|
|
@ -0,0 +1,709 @@
|
|||
// Copyright 2021 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 ipv4
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/buffer"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/checksum"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
// icmpv4DestinationUnreachableSockError is a general ICMPv4 Destination
|
||||
// Unreachable error.
|
||||
//
|
||||
// +stateify savable
|
||||
type icmpv4DestinationUnreachableSockError struct{}
|
||||
|
||||
// Origin implements tcpip.SockErrorCause.
|
||||
func (*icmpv4DestinationUnreachableSockError) Origin() tcpip.SockErrOrigin {
|
||||
return tcpip.SockExtErrorOriginICMP
|
||||
}
|
||||
|
||||
// Type implements tcpip.SockErrorCause.
|
||||
func (*icmpv4DestinationUnreachableSockError) Type() uint8 {
|
||||
return uint8(header.ICMPv4DstUnreachable)
|
||||
}
|
||||
|
||||
// Info implements tcpip.SockErrorCause.
|
||||
func (*icmpv4DestinationUnreachableSockError) Info() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
var _ stack.TransportError = (*icmpv4DestinationHostUnreachableSockError)(nil)
|
||||
|
||||
// icmpv4DestinationHostUnreachableSockError is an ICMPv4 Destination Host
|
||||
// Unreachable error.
|
||||
//
|
||||
// It indicates that a packet was not able to reach the destination host.
|
||||
//
|
||||
// +stateify savable
|
||||
type icmpv4DestinationHostUnreachableSockError struct {
|
||||
icmpv4DestinationUnreachableSockError
|
||||
}
|
||||
|
||||
// Code implements tcpip.SockErrorCause.
|
||||
func (*icmpv4DestinationHostUnreachableSockError) Code() uint8 {
|
||||
return uint8(header.ICMPv4HostUnreachable)
|
||||
}
|
||||
|
||||
// Kind implements stack.TransportError.
|
||||
func (*icmpv4DestinationHostUnreachableSockError) Kind() stack.TransportErrorKind {
|
||||
return stack.DestinationHostUnreachableTransportError
|
||||
}
|
||||
|
||||
var _ stack.TransportError = (*icmpv4DestinationNetUnreachableSockError)(nil)
|
||||
|
||||
// icmpv4DestinationNetUnreachableSockError is an ICMPv4 Destination Net
|
||||
// Unreachable error.
|
||||
//
|
||||
// It indicates that a packet was not able to reach the destination network.
|
||||
//
|
||||
// +stateify savable
|
||||
type icmpv4DestinationNetUnreachableSockError struct {
|
||||
icmpv4DestinationUnreachableSockError
|
||||
}
|
||||
|
||||
// Code implements tcpip.SockErrorCause.
|
||||
func (*icmpv4DestinationNetUnreachableSockError) Code() uint8 {
|
||||
return uint8(header.ICMPv4NetUnreachable)
|
||||
}
|
||||
|
||||
// Kind implements stack.TransportError.
|
||||
func (*icmpv4DestinationNetUnreachableSockError) Kind() stack.TransportErrorKind {
|
||||
return stack.DestinationNetworkUnreachableTransportError
|
||||
}
|
||||
|
||||
var _ stack.TransportError = (*icmpv4DestinationPortUnreachableSockError)(nil)
|
||||
|
||||
// icmpv4DestinationPortUnreachableSockError is an ICMPv4 Destination Port
|
||||
// Unreachable error.
|
||||
//
|
||||
// It indicates that a packet reached the destination host, but the transport
|
||||
// protocol was not active on the destination port.
|
||||
//
|
||||
// +stateify savable
|
||||
type icmpv4DestinationPortUnreachableSockError struct {
|
||||
icmpv4DestinationUnreachableSockError
|
||||
}
|
||||
|
||||
// Code implements tcpip.SockErrorCause.
|
||||
func (*icmpv4DestinationPortUnreachableSockError) Code() uint8 {
|
||||
return uint8(header.ICMPv4PortUnreachable)
|
||||
}
|
||||
|
||||
// Kind implements stack.TransportError.
|
||||
func (*icmpv4DestinationPortUnreachableSockError) Kind() stack.TransportErrorKind {
|
||||
return stack.DestinationPortUnreachableTransportError
|
||||
}
|
||||
|
||||
var _ stack.TransportError = (*icmpv4DestinationProtoUnreachableSockError)(nil)
|
||||
|
||||
// icmpv4DestinationProtoUnreachableSockError is an ICMPv4 Destination Protocol
|
||||
// Unreachable error.
|
||||
//
|
||||
// It indicates that a packet reached the destination host, but the transport
|
||||
// protocol was not reachable
|
||||
//
|
||||
// +stateify savable
|
||||
type icmpv4DestinationProtoUnreachableSockError struct {
|
||||
icmpv4DestinationUnreachableSockError
|
||||
}
|
||||
|
||||
// Code implements tcpip.SockErrorCause.
|
||||
func (*icmpv4DestinationProtoUnreachableSockError) Code() uint8 {
|
||||
return uint8(header.ICMPv4ProtoUnreachable)
|
||||
}
|
||||
|
||||
// Kind implements stack.TransportError.
|
||||
func (*icmpv4DestinationProtoUnreachableSockError) Kind() stack.TransportErrorKind {
|
||||
return stack.DestinationProtoUnreachableTransportError
|
||||
}
|
||||
|
||||
var _ stack.TransportError = (*icmpv4SourceRouteFailedSockError)(nil)
|
||||
|
||||
// icmpv4SourceRouteFailedSockError is an ICMPv4 Destination Unreachable error
|
||||
// due to source route failed.
|
||||
//
|
||||
// +stateify savable
|
||||
type icmpv4SourceRouteFailedSockError struct {
|
||||
icmpv4DestinationUnreachableSockError
|
||||
}
|
||||
|
||||
// Code implements tcpip.SockErrorCause.
|
||||
func (*icmpv4SourceRouteFailedSockError) Code() uint8 {
|
||||
return uint8(header.ICMPv4SourceRouteFailed)
|
||||
}
|
||||
|
||||
// Kind implements stack.TransportError.
|
||||
func (*icmpv4SourceRouteFailedSockError) Kind() stack.TransportErrorKind {
|
||||
return stack.SourceRouteFailedTransportError
|
||||
}
|
||||
|
||||
var _ stack.TransportError = (*icmpv4SourceHostIsolatedSockError)(nil)
|
||||
|
||||
// icmpv4SourceHostIsolatedSockError is an ICMPv4 Destination Unreachable error
|
||||
// due to source host isolated (not on the network).
|
||||
//
|
||||
// +stateify savable
|
||||
type icmpv4SourceHostIsolatedSockError struct {
|
||||
icmpv4DestinationUnreachableSockError
|
||||
}
|
||||
|
||||
// Code implements tcpip.SockErrorCause.
|
||||
func (*icmpv4SourceHostIsolatedSockError) Code() uint8 {
|
||||
return uint8(header.ICMPv4SourceHostIsolated)
|
||||
}
|
||||
|
||||
// Kind implements stack.TransportError.
|
||||
func (*icmpv4SourceHostIsolatedSockError) Kind() stack.TransportErrorKind {
|
||||
return stack.SourceHostIsolatedTransportError
|
||||
}
|
||||
|
||||
var _ stack.TransportError = (*icmpv4DestinationHostUnknownSockError)(nil)
|
||||
|
||||
// icmpv4DestinationHostUnknownSockError is an ICMPv4 Destination Unreachable
|
||||
// error due to destination host unknown/down.
|
||||
//
|
||||
// +stateify savable
|
||||
type icmpv4DestinationHostUnknownSockError struct {
|
||||
icmpv4DestinationUnreachableSockError
|
||||
}
|
||||
|
||||
// Code implements tcpip.SockErrorCause.
|
||||
func (*icmpv4DestinationHostUnknownSockError) Code() uint8 {
|
||||
return uint8(header.ICMPv4DestinationHostUnknown)
|
||||
}
|
||||
|
||||
// Kind implements stack.TransportError.
|
||||
func (*icmpv4DestinationHostUnknownSockError) Kind() stack.TransportErrorKind {
|
||||
return stack.DestinationHostDownTransportError
|
||||
}
|
||||
|
||||
var _ stack.TransportError = (*icmpv4FragmentationNeededSockError)(nil)
|
||||
|
||||
// icmpv4FragmentationNeededSockError is an ICMPv4 Destination Unreachable error
|
||||
// due to fragmentation being required but the packet was set to not be
|
||||
// fragmented.
|
||||
//
|
||||
// It indicates that a link exists on the path to the destination with an MTU
|
||||
// that is too small to carry the packet.
|
||||
//
|
||||
// +stateify savable
|
||||
type icmpv4FragmentationNeededSockError struct {
|
||||
icmpv4DestinationUnreachableSockError
|
||||
|
||||
mtu uint32
|
||||
}
|
||||
|
||||
// Code implements tcpip.SockErrorCause.
|
||||
func (*icmpv4FragmentationNeededSockError) Code() uint8 {
|
||||
return uint8(header.ICMPv4FragmentationNeeded)
|
||||
}
|
||||
|
||||
// Info implements tcpip.SockErrorCause.
|
||||
func (e *icmpv4FragmentationNeededSockError) Info() uint32 {
|
||||
return e.mtu
|
||||
}
|
||||
|
||||
// Kind implements stack.TransportError.
|
||||
func (*icmpv4FragmentationNeededSockError) Kind() stack.TransportErrorKind {
|
||||
return stack.PacketTooBigTransportError
|
||||
}
|
||||
|
||||
func (e *endpoint) checkLocalAddress(addr tcpip.Address) bool {
|
||||
if e.nic.Spoofing() {
|
||||
return true
|
||||
}
|
||||
|
||||
if addressEndpoint := e.AcquireAssignedAddress(addr, false, stack.NeverPrimaryEndpoint, true /* readOnly */); addressEndpoint != nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// handleControl handles the case when an ICMP error packet contains the headers
|
||||
// of the original packet that caused the ICMP one to be sent. This information
|
||||
// is used to find out which transport endpoint must be notified about the ICMP
|
||||
// packet. We only expect the payload, not the enclosing ICMP packet.
|
||||
func (e *endpoint) handleControl(errInfo stack.TransportError, pkt *stack.PacketBuffer) {
|
||||
h, ok := pkt.Data().PullUp(header.IPv4MinimumSize)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
hdr := header.IPv4(h)
|
||||
|
||||
// We don't use IsValid() here because ICMP only requires that the IP
|
||||
// header plus 8 bytes of the transport header be included. So it's
|
||||
// likely that it is truncated, which would cause IsValid to return
|
||||
// false.
|
||||
//
|
||||
// Drop packet if it doesn't have the basic IPv4 header or if the
|
||||
// original source address doesn't match an address we own.
|
||||
srcAddr := hdr.SourceAddress()
|
||||
if !e.checkLocalAddress(srcAddr) {
|
||||
return
|
||||
}
|
||||
|
||||
hlen := int(hdr.HeaderLength())
|
||||
if pkt.Data().Size() < hlen || hdr.FragmentOffset() != 0 {
|
||||
// We won't be able to handle this if it doesn't contain the
|
||||
// full IPv4 header, or if it's a fragment not at offset 0
|
||||
// (because it won't have the transport header).
|
||||
return
|
||||
}
|
||||
|
||||
// Keep needed information before trimming header.
|
||||
p := hdr.TransportProtocol()
|
||||
dstAddr := hdr.DestinationAddress()
|
||||
// Skip the ip header, then deliver the error.
|
||||
if _, ok := pkt.Data().Consume(hlen); !ok {
|
||||
panic(fmt.Sprintf("could not consume the IP header of %d bytes", hlen))
|
||||
}
|
||||
e.dispatcher.DeliverTransportError(srcAddr, dstAddr, ProtocolNumber, p, errInfo, pkt)
|
||||
}
|
||||
|
||||
func (e *endpoint) handleICMP(pkt *stack.PacketBuffer) {
|
||||
received := e.stats.icmp.packetsReceived
|
||||
h := header.ICMPv4(pkt.TransportHeader().Slice())
|
||||
if len(h) < header.ICMPv4MinimumSize {
|
||||
received.invalid.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
// Only do in-stack processing if the checksum is correct.
|
||||
if checksum.Checksum(h, pkt.Data().Checksum()) != 0xffff {
|
||||
received.invalid.Increment()
|
||||
// It's possible that a raw socket expects to receive this regardless
|
||||
// of checksum errors. If it's an echo request we know it's safe because
|
||||
// we are the only handler, however other types do not cope well with
|
||||
// packets with checksum errors.
|
||||
switch h.Type() {
|
||||
case header.ICMPv4Echo:
|
||||
e.dispatcher.DeliverTransportPacket(header.ICMPv4ProtocolNumber, pkt)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
iph := header.IPv4(pkt.NetworkHeader().Slice())
|
||||
var newOptions header.IPv4Options
|
||||
if opts := iph.Options(); len(opts) != 0 {
|
||||
// RFC 1122 section 3.2.2.6 (page 43) (and similar for other round trip
|
||||
// type ICMP packets):
|
||||
// If a Record Route and/or Time Stamp option is received in an
|
||||
// ICMP Echo Request, this option (these options) SHOULD be
|
||||
// updated to include the current host and included in the IP
|
||||
// header of the Echo Reply message, without "truncation".
|
||||
// Thus, the recorded route will be for the entire round trip.
|
||||
//
|
||||
// So we need to let the option processor know how it should handle them.
|
||||
var op optionsUsage
|
||||
if h.Type() == header.ICMPv4Echo {
|
||||
op = &optionUsageEcho{}
|
||||
} else {
|
||||
op = &optionUsageReceive{}
|
||||
}
|
||||
var optProblem *header.IPv4OptParameterProblem
|
||||
newOptions, _, optProblem = e.processIPOptions(pkt, opts, op)
|
||||
if optProblem != nil {
|
||||
if optProblem.NeedICMP {
|
||||
_ = e.protocol.returnError(&icmpReasonParamProblem{
|
||||
pointer: optProblem.Pointer,
|
||||
}, pkt, true /* deliveredLocally */)
|
||||
e.stats.ip.MalformedPacketsReceived.Increment()
|
||||
}
|
||||
return
|
||||
}
|
||||
copied := copy(opts, newOptions)
|
||||
if copied != len(newOptions) {
|
||||
panic(fmt.Sprintf("copied %d bytes of new options, expected %d bytes", copied, len(newOptions)))
|
||||
}
|
||||
for i := copied; i < len(opts); i++ {
|
||||
// Pad with 0 (EOL). RFC 791 page 23 says "The padding is zero".
|
||||
opts[i] = byte(header.IPv4OptionListEndType)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(b/112892170): Meaningfully handle all ICMP types.
|
||||
switch h.Type() {
|
||||
case header.ICMPv4Echo:
|
||||
received.echoRequest.Increment()
|
||||
e.dispatcher.DeliverTransportPacket(header.ICMPv4ProtocolNumber, pkt)
|
||||
case header.ICMPv4EchoReply:
|
||||
received.echoReply.Increment()
|
||||
e.dispatcher.DeliverTransportPacket(header.ICMPv4ProtocolNumber, pkt)
|
||||
case header.ICMPv4DstUnreachable:
|
||||
received.dstUnreachable.Increment()
|
||||
|
||||
mtu := h.MTU()
|
||||
code := h.Code()
|
||||
switch code {
|
||||
case header.ICMPv4NetUnreachable,
|
||||
header.ICMPv4DestinationNetworkUnknown,
|
||||
header.ICMPv4NetUnreachableForTos,
|
||||
header.ICMPv4NetProhibited:
|
||||
e.handleControl(&icmpv4DestinationNetUnreachableSockError{}, pkt)
|
||||
case header.ICMPv4HostUnreachable,
|
||||
header.ICMPv4HostProhibited,
|
||||
header.ICMPv4AdminProhibited,
|
||||
header.ICMPv4HostUnreachableForTos,
|
||||
header.ICMPv4HostPrecedenceViolation,
|
||||
header.ICMPv4PrecedenceCutInEffect:
|
||||
e.handleControl(&icmpv4DestinationHostUnreachableSockError{}, pkt)
|
||||
case header.ICMPv4PortUnreachable:
|
||||
e.handleControl(&icmpv4DestinationPortUnreachableSockError{}, pkt)
|
||||
case header.ICMPv4FragmentationNeeded:
|
||||
networkMTU, err := calculateNetworkMTU(uint32(mtu), header.IPv4MinimumSize)
|
||||
if err != nil {
|
||||
networkMTU = 0
|
||||
}
|
||||
e.handleControl(&icmpv4FragmentationNeededSockError{mtu: networkMTU}, pkt)
|
||||
case header.ICMPv4ProtoUnreachable:
|
||||
e.handleControl(&icmpv4DestinationProtoUnreachableSockError{}, pkt)
|
||||
case header.ICMPv4SourceRouteFailed:
|
||||
e.handleControl(&icmpv4SourceRouteFailedSockError{}, pkt)
|
||||
case header.ICMPv4SourceHostIsolated:
|
||||
e.handleControl(&icmpv4SourceHostIsolatedSockError{}, pkt)
|
||||
case header.ICMPv4DestinationHostUnknown:
|
||||
e.handleControl(&icmpv4DestinationHostUnknownSockError{}, pkt)
|
||||
}
|
||||
case header.ICMPv4SrcQuench:
|
||||
received.srcQuench.Increment()
|
||||
|
||||
case header.ICMPv4Redirect:
|
||||
received.redirect.Increment()
|
||||
|
||||
case header.ICMPv4TimeExceeded:
|
||||
received.timeExceeded.Increment()
|
||||
|
||||
case header.ICMPv4ParamProblem:
|
||||
received.paramProblem.Increment()
|
||||
|
||||
case header.ICMPv4Timestamp:
|
||||
received.timestamp.Increment()
|
||||
|
||||
case header.ICMPv4TimestampReply:
|
||||
received.timestampReply.Increment()
|
||||
|
||||
case header.ICMPv4InfoRequest:
|
||||
received.infoRequest.Increment()
|
||||
|
||||
case header.ICMPv4InfoReply:
|
||||
received.infoReply.Increment()
|
||||
|
||||
default:
|
||||
received.invalid.Increment()
|
||||
}
|
||||
}
|
||||
|
||||
// ======= ICMP Error packet generation =========
|
||||
|
||||
// icmpReason is a marker interface for IPv4 specific ICMP errors.
|
||||
type icmpReason interface {
|
||||
isICMPReason()
|
||||
}
|
||||
|
||||
// icmpReasonNetworkProhibited is an error where the destination network is
|
||||
// prohibited.
|
||||
type icmpReasonNetworkProhibited struct{}
|
||||
|
||||
func (*icmpReasonNetworkProhibited) isICMPReason() {}
|
||||
|
||||
// icmpReasonHostProhibited is an error where the destination host is
|
||||
// prohibited.
|
||||
type icmpReasonHostProhibited struct{}
|
||||
|
||||
func (*icmpReasonHostProhibited) isICMPReason() {}
|
||||
|
||||
// icmpReasonAdministrativelyProhibited is an error where the destination is
|
||||
// administratively prohibited.
|
||||
type icmpReasonAdministrativelyProhibited struct{}
|
||||
|
||||
func (*icmpReasonAdministrativelyProhibited) isICMPReason() {}
|
||||
|
||||
// icmpReasonPortUnreachable is an error where the transport protocol has no
|
||||
// listener and no alternative means to inform the sender.
|
||||
type icmpReasonPortUnreachable struct{}
|
||||
|
||||
func (*icmpReasonPortUnreachable) isICMPReason() {}
|
||||
|
||||
// icmpReasonProtoUnreachable is an error where the transport protocol is
|
||||
// not supported.
|
||||
type icmpReasonProtoUnreachable struct{}
|
||||
|
||||
func (*icmpReasonProtoUnreachable) isICMPReason() {}
|
||||
|
||||
// icmpReasonTTLExceeded is an error where a packet's time to live exceeded in
|
||||
// transit to its final destination, as per RFC 792 page 6, Time Exceeded
|
||||
// Message.
|
||||
type icmpReasonTTLExceeded struct{}
|
||||
|
||||
func (*icmpReasonTTLExceeded) isICMPReason() {}
|
||||
|
||||
// icmpReasonReassemblyTimeout is an error where insufficient fragments are
|
||||
// received to complete reassembly of a packet within a configured time after
|
||||
// the reception of the first-arriving fragment of that packet.
|
||||
type icmpReasonReassemblyTimeout struct{}
|
||||
|
||||
func (*icmpReasonReassemblyTimeout) isICMPReason() {}
|
||||
|
||||
// icmpReasonParamProblem is an error to use to request a Parameter Problem
|
||||
// message to be sent.
|
||||
type icmpReasonParamProblem struct {
|
||||
pointer byte
|
||||
}
|
||||
|
||||
func (*icmpReasonParamProblem) isICMPReason() {}
|
||||
|
||||
// icmpReasonNetworkUnreachable is an error in which the network specified in
|
||||
// the internet destination field of the datagram is unreachable.
|
||||
type icmpReasonNetworkUnreachable struct{}
|
||||
|
||||
func (*icmpReasonNetworkUnreachable) isICMPReason() {}
|
||||
|
||||
// icmpReasonFragmentationNeeded is an error where a packet requires
|
||||
// fragmentation while also having the Don't Fragment flag set, as per RFC 792
|
||||
// page 3, Destination Unreachable Message.
|
||||
type icmpReasonFragmentationNeeded struct{}
|
||||
|
||||
func (*icmpReasonFragmentationNeeded) isICMPReason() {}
|
||||
|
||||
// icmpReasonHostUnreachable is an error in which the host specified in the
|
||||
// internet destination field of the datagram is unreachable.
|
||||
type icmpReasonHostUnreachable struct{}
|
||||
|
||||
func (*icmpReasonHostUnreachable) isICMPReason() {}
|
||||
|
||||
// returnError takes an error descriptor and generates the appropriate ICMP
|
||||
// error packet for IPv4 and sends it back to the remote device that sent
|
||||
// the problematic packet. It incorporates as much of that packet as
|
||||
// possible as well as any error metadata as is available. returnError
|
||||
// expects pkt to hold a valid IPv4 packet as per the wire format.
|
||||
func (p *protocol) returnError(reason icmpReason, pkt *stack.PacketBuffer, deliveredLocally bool) tcpip.Error {
|
||||
origIPHdr := header.IPv4(pkt.NetworkHeader().Slice())
|
||||
origIPHdrSrc := origIPHdr.SourceAddress()
|
||||
origIPHdrDst := origIPHdr.DestinationAddress()
|
||||
|
||||
// We check we are responding only when we are allowed to.
|
||||
// See RFC 1812 section 4.3.2.7 (shown below).
|
||||
//
|
||||
// =========
|
||||
// 4.3.2.7 When Not to Send ICMP Errors
|
||||
//
|
||||
// An ICMP error message MUST NOT be sent as the result of receiving:
|
||||
//
|
||||
// o An ICMP error message, or
|
||||
//
|
||||
// o A packet which fails the IP header validation tests described in
|
||||
// Section [5.2.2] (except where that section specifically permits
|
||||
// the sending of an ICMP error message), or
|
||||
//
|
||||
// o A packet destined to an IP broadcast or IP multicast address, or
|
||||
//
|
||||
// o A packet sent as a Link Layer broadcast or multicast, or
|
||||
//
|
||||
// o Any fragment of a datagram other then the first fragment (i.e., a
|
||||
// packet for which the fragment offset in the IP header is nonzero).
|
||||
//
|
||||
// TODO(gvisor.dev/issues/4058): Make sure we don't send ICMP errors in
|
||||
// response to a non-initial fragment, but it currently can not happen.
|
||||
if pkt.NetworkPacketInfo.LocalAddressBroadcast || header.IsV4MulticastAddress(origIPHdrDst) || origIPHdrSrc == header.IPv4Any {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If the packet wasn't delivered locally, do not use the packet's destination
|
||||
// address as the response's source address as we should not not own the
|
||||
// destination address of a packet we are forwarding.
|
||||
localAddr := origIPHdrDst
|
||||
if !deliveredLocally {
|
||||
localAddr = tcpip.Address{}
|
||||
}
|
||||
|
||||
// Even if we were able to receive a packet from some remote, we may not have
|
||||
// a route to it - the remote may be blocked via routing rules. We must always
|
||||
// consult our routing table and find a route to the remote before sending any
|
||||
// packet.
|
||||
route, err := p.stack.FindRoute(pkt.NICID, localAddr, origIPHdrSrc, ProtocolNumber, false /* multicastLoop */)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer route.Release()
|
||||
|
||||
p.mu.Lock()
|
||||
// We retrieve an endpoint using the newly constructed route's NICID rather
|
||||
// than the packet's NICID. The packet's NICID corresponds to the NIC on
|
||||
// which it arrived, which isn't necessarily the same as the NIC on which it
|
||||
// will be transmitted. On the other hand, the route's NIC *is* guaranteed
|
||||
// to be the NIC on which the packet will be transmitted.
|
||||
netEP, ok := p.eps[route.NICID()]
|
||||
p.mu.Unlock()
|
||||
if !ok {
|
||||
return &tcpip.ErrNotConnected{}
|
||||
}
|
||||
|
||||
transportHeader := pkt.TransportHeader().Slice()
|
||||
|
||||
// Don't respond to icmp error packets.
|
||||
if origIPHdr.Protocol() == uint8(header.ICMPv4ProtocolNumber) {
|
||||
// We need to decide to explicitly name the packets we can respond to or
|
||||
// the ones we can not respond to. The decision is somewhat arbitrary and
|
||||
// if problems arise this could be reversed. It was judged less of a breach
|
||||
// of protocol to not respond to unknown non-error packets than to respond
|
||||
// to unknown error packets so we take the first approach.
|
||||
if len(transportHeader) < header.ICMPv4MinimumSize {
|
||||
// The packet is malformed.
|
||||
return nil
|
||||
}
|
||||
switch header.ICMPv4(transportHeader).Type() {
|
||||
case
|
||||
header.ICMPv4EchoReply,
|
||||
header.ICMPv4Echo,
|
||||
header.ICMPv4Timestamp,
|
||||
header.ICMPv4TimestampReply,
|
||||
header.ICMPv4InfoRequest,
|
||||
header.ICMPv4InfoReply:
|
||||
default:
|
||||
// Assume any type we don't know about may be an error type.
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
sent := netEP.stats.icmp.packetsSent
|
||||
icmpType, icmpCode, counter, pointer := func() (header.ICMPv4Type, header.ICMPv4Code, tcpip.MultiCounterStat, byte) {
|
||||
switch reason := reason.(type) {
|
||||
case *icmpReasonNetworkProhibited:
|
||||
return header.ICMPv4DstUnreachable, header.ICMPv4NetProhibited, sent.dstUnreachable, 0
|
||||
case *icmpReasonHostProhibited:
|
||||
return header.ICMPv4DstUnreachable, header.ICMPv4HostProhibited, sent.dstUnreachable, 0
|
||||
case *icmpReasonAdministrativelyProhibited:
|
||||
return header.ICMPv4DstUnreachable, header.ICMPv4AdminProhibited, sent.dstUnreachable, 0
|
||||
case *icmpReasonPortUnreachable:
|
||||
return header.ICMPv4DstUnreachable, header.ICMPv4PortUnreachable, sent.dstUnreachable, 0
|
||||
case *icmpReasonProtoUnreachable:
|
||||
return header.ICMPv4DstUnreachable, header.ICMPv4ProtoUnreachable, sent.dstUnreachable, 0
|
||||
case *icmpReasonNetworkUnreachable:
|
||||
return header.ICMPv4DstUnreachable, header.ICMPv4NetUnreachable, sent.dstUnreachable, 0
|
||||
case *icmpReasonHostUnreachable:
|
||||
return header.ICMPv4DstUnreachable, header.ICMPv4HostUnreachable, sent.dstUnreachable, 0
|
||||
case *icmpReasonFragmentationNeeded:
|
||||
return header.ICMPv4DstUnreachable, header.ICMPv4FragmentationNeeded, sent.dstUnreachable, 0
|
||||
case *icmpReasonTTLExceeded:
|
||||
return header.ICMPv4TimeExceeded, header.ICMPv4TTLExceeded, sent.timeExceeded, 0
|
||||
case *icmpReasonReassemblyTimeout:
|
||||
return header.ICMPv4TimeExceeded, header.ICMPv4ReassemblyTimeout, sent.timeExceeded, 0
|
||||
case *icmpReasonParamProblem:
|
||||
return header.ICMPv4ParamProblem, header.ICMPv4UnusedCode, sent.paramProblem, reason.pointer
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported ICMP type %T", reason))
|
||||
}
|
||||
}()
|
||||
|
||||
if !p.allowICMPReply(icmpType, icmpCode) {
|
||||
sent.rateLimited.Increment()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Now work out how much of the triggering packet we should return.
|
||||
// As per RFC 1812 Section 4.3.2.3
|
||||
//
|
||||
// ICMP datagram SHOULD contain as much of the original
|
||||
// datagram as possible without the length of the ICMP
|
||||
// datagram exceeding 576 bytes.
|
||||
//
|
||||
// NOTE: The above RFC referenced is different from the original
|
||||
// recommendation in RFC 1122 and RFC 792 where it mentioned that at
|
||||
// least 8 bytes of the payload must be included. Today linux and other
|
||||
// systems implement the RFC 1812 definition and not the original
|
||||
// requirement. We treat 8 bytes as the minimum but will try send more.
|
||||
mtu := int(route.MTU())
|
||||
const maxIPData = header.IPv4MinimumProcessableDatagramSize - header.IPv4MinimumSize
|
||||
if mtu > maxIPData {
|
||||
mtu = maxIPData
|
||||
}
|
||||
available := mtu - header.ICMPv4MinimumSize
|
||||
|
||||
if available < len(origIPHdr)+header.ICMPv4MinimumErrorPayloadSize {
|
||||
return nil
|
||||
}
|
||||
|
||||
payloadLen := len(origIPHdr) + len(transportHeader) + pkt.Data().Size()
|
||||
if payloadLen > available {
|
||||
payloadLen = available
|
||||
}
|
||||
|
||||
// The buffers used by pkt may be used elsewhere in the system.
|
||||
// For example, an AF_RAW or AF_PACKET socket may use what the transport
|
||||
// protocol considers an unreachable destination. Thus we deep copy pkt to
|
||||
// prevent multiple ownership and SR errors. The new copy is a vectorized
|
||||
// view with the entire incoming IP packet reassembled and truncated as
|
||||
// required. This is now the payload of the new ICMP packet and no longer
|
||||
// considered a packet in its own right.
|
||||
|
||||
payload := buffer.MakeWithView(pkt.NetworkHeader().View())
|
||||
payload.Append(pkt.TransportHeader().View())
|
||||
if dataCap := payloadLen - int(payload.Size()); dataCap > 0 {
|
||||
buf := pkt.Data().ToBuffer()
|
||||
buf.Truncate(int64(dataCap))
|
||||
payload.Merge(&buf)
|
||||
} else {
|
||||
payload.Truncate(int64(payloadLen))
|
||||
}
|
||||
|
||||
icmpPkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
ReserveHeaderBytes: int(route.MaxHeaderLength()) + header.ICMPv4MinimumSize,
|
||||
Payload: payload,
|
||||
})
|
||||
defer icmpPkt.DecRef()
|
||||
|
||||
icmpPkt.TransportProtocolNumber = header.ICMPv4ProtocolNumber
|
||||
|
||||
icmpHdr := header.ICMPv4(icmpPkt.TransportHeader().Push(header.ICMPv4MinimumSize))
|
||||
icmpHdr.SetCode(icmpCode)
|
||||
icmpHdr.SetType(icmpType)
|
||||
icmpHdr.SetPointer(pointer)
|
||||
icmpHdr.SetChecksum(header.ICMPv4Checksum(icmpHdr, icmpPkt.Data().Checksum()))
|
||||
|
||||
if err := route.WritePacket(
|
||||
stack.NetworkHeaderParams{
|
||||
Protocol: header.ICMPv4ProtocolNumber,
|
||||
TTL: route.DefaultTTL(),
|
||||
TOS: stack.DefaultTOS,
|
||||
},
|
||||
icmpPkt,
|
||||
); err != nil {
|
||||
sent.dropped.Increment()
|
||||
return err
|
||||
}
|
||||
counter.Increment()
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnReassemblyTimeout implements fragmentation.TimeoutHandler.
|
||||
func (p *protocol) OnReassemblyTimeout(pkt *stack.PacketBuffer) {
|
||||
// OnReassemblyTimeout sends a Time Exceeded Message, as per RFC 792:
|
||||
//
|
||||
// If a host reassembling a fragmented datagram cannot complete the
|
||||
// reassembly due to missing fragments within its time limit it discards the
|
||||
// datagram, and it may send a time exceeded message.
|
||||
//
|
||||
// If fragment zero is not available then no time exceeded need be sent at
|
||||
// all.
|
||||
if pkt != nil {
|
||||
p.returnError(&icmpReasonReassemblyTimeout{}, pkt, true /* deliveredLocally */)
|
||||
}
|
||||
}
|
||||
654
pkg/tcpip/network/ipv4/igmp.go
Normal file
654
pkg/tcpip/network/ipv4/igmp.go
Normal file
|
|
@ -0,0 +1,654 @@
|
|||
// 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.
|
||||
|
||||
package ipv4
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/buffer"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/network/internal/ip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
const (
|
||||
// v1RouterPresentTimeout from RFC 2236 Section 8.11, Page 18
|
||||
// See note on igmpState.igmpV1Present for more detail.
|
||||
v1RouterPresentTimeout = 400 * time.Second
|
||||
|
||||
// v1MaxRespTime from RFC 2236 Section 4, Page 5. "The IGMPv1 router
|
||||
// will send General Queries with the Max Response Time set to 0. This MUST
|
||||
// be interpreted as a value of 100 (10 seconds)."
|
||||
//
|
||||
// Note that the Max Response Time field is a value in units of deciseconds.
|
||||
v1MaxRespTime = 10 * time.Second
|
||||
|
||||
// UnsolicitedReportIntervalMax is the maximum delay between sending
|
||||
// unsolicited IGMP reports.
|
||||
//
|
||||
// Obtained from RFC 2236 Section 8.10, Page 19.
|
||||
UnsolicitedReportIntervalMax = 10 * time.Second
|
||||
)
|
||||
|
||||
type protocolMode int
|
||||
|
||||
const (
|
||||
protocolModeV2OrV3 protocolMode = iota
|
||||
protocolModeV1
|
||||
// protocolModeV1Compatibility is for maintaining compatibility with IGMPv1
|
||||
// Routers.
|
||||
//
|
||||
// Per RFC 2236 Section 4 Page 6: "The IGMPv1 router expects Version 1
|
||||
// Membership Reports in response to its Queries, and will not pay
|
||||
// attention to Version 2 Membership Reports. Therefore, a state variable
|
||||
// MUST be kept for each interface, describing whether the multicast
|
||||
// Querier on that interface is running IGMPv1 or IGMPv2. This variable
|
||||
// MUST be based upon whether or not an IGMPv1 query was heard in the last
|
||||
// [Version 1 Router Present Timeout] seconds".
|
||||
protocolModeV1Compatibility
|
||||
)
|
||||
|
||||
// IGMPVersion is the forced version of IGMP.
|
||||
type IGMPVersion int
|
||||
|
||||
const (
|
||||
_ IGMPVersion = iota
|
||||
// IGMPVersion1 indicates IGMPv1.
|
||||
IGMPVersion1
|
||||
// IGMPVersion2 indicates IGMPv2. Note that IGMP may still fallback to V1
|
||||
// compatibility mode as required by IGMPv2.
|
||||
IGMPVersion2
|
||||
// IGMPVersion3 indicates IGMPv3. Note that IGMP may still fallback to V2
|
||||
// compatibility mode as required by IGMPv3.
|
||||
IGMPVersion3
|
||||
)
|
||||
|
||||
// IGMPEndpoint is a network endpoint that supports IGMP.
|
||||
type IGMPEndpoint interface {
|
||||
// SetIGMPVersion sets the IGMP version.
|
||||
//
|
||||
// Returns the previous IGMP version.
|
||||
SetIGMPVersion(IGMPVersion) IGMPVersion
|
||||
|
||||
// GetIGMPVersion returns the IGMP version.
|
||||
GetIGMPVersion() IGMPVersion
|
||||
}
|
||||
|
||||
// IGMPOptions holds options for IGMP.
|
||||
//
|
||||
// +stateify savable
|
||||
type IGMPOptions struct {
|
||||
// Enabled indicates whether IGMP will be performed.
|
||||
//
|
||||
// When enabled, IGMP may transmit IGMP report and leave messages when
|
||||
// joining and leaving multicast groups respectively, and handle incoming
|
||||
// IGMP packets.
|
||||
//
|
||||
// This field is ignored and is always assumed to be false for interfaces
|
||||
// without neighbouring nodes (e.g. loopback).
|
||||
Enabled bool
|
||||
}
|
||||
|
||||
var _ ip.MulticastGroupProtocol = (*igmpState)(nil)
|
||||
|
||||
// igmpState is the per-interface IGMP state.
|
||||
//
|
||||
// igmpState.init() MUST be called after creating an IGMP state.
|
||||
//
|
||||
// +stateify savable
|
||||
type igmpState struct {
|
||||
// The IPv4 endpoint this igmpState is for.
|
||||
ep *endpoint
|
||||
|
||||
genericMulticastProtocol ip.GenericMulticastProtocolState
|
||||
|
||||
// mode is used to configure the version of IGMP to perform.
|
||||
mode protocolMode
|
||||
|
||||
// igmpV1Job is scheduled when this interface receives an IGMPv1 style
|
||||
// message, upon expiration the igmpV1Present flag is cleared.
|
||||
// igmpV1Job may not be nil once igmpState is initialized.
|
||||
igmpV1Job *tcpip.Job
|
||||
}
|
||||
|
||||
// Enabled implements ip.MulticastGroupProtocol.
|
||||
func (igmp *igmpState) Enabled() bool {
|
||||
// No need to perform IGMP on loopback interfaces since they don't have
|
||||
// neighbouring nodes.
|
||||
return igmp.ep.protocol.options.IGMP.Enabled && !igmp.ep.nic.IsLoopback() && igmp.ep.Enabled()
|
||||
}
|
||||
|
||||
// SendReport implements ip.MulticastGroupProtocol.
|
||||
//
|
||||
// +checklocksread:igmp.ep.mu
|
||||
func (igmp *igmpState) SendReport(groupAddress tcpip.Address) (bool, tcpip.Error) {
|
||||
igmpType := header.IGMPv2MembershipReport
|
||||
switch igmp.mode {
|
||||
case protocolModeV2OrV3:
|
||||
case protocolModeV1, protocolModeV1Compatibility:
|
||||
igmpType = header.IGMPv1MembershipReport
|
||||
default:
|
||||
panic(fmt.Sprintf("unrecognized mode = %d", igmp.mode))
|
||||
}
|
||||
return igmp.writePacket(groupAddress, groupAddress, igmpType)
|
||||
}
|
||||
|
||||
// SendLeave implements ip.MulticastGroupProtocol.
|
||||
//
|
||||
// +checklocksread:igmp.ep.mu
|
||||
func (igmp *igmpState) SendLeave(groupAddress tcpip.Address) tcpip.Error {
|
||||
// As per RFC 2236 Section 6, Page 8: "If the interface state says the
|
||||
// Querier is running IGMPv1, this action SHOULD be skipped. If the flag
|
||||
// saying we were the last host to report is cleared, this action MAY be
|
||||
// skipped."
|
||||
switch igmp.mode {
|
||||
case protocolModeV2OrV3:
|
||||
_, err := igmp.writePacket(header.IPv4AllRoutersGroup, groupAddress, header.IGMPLeaveGroup)
|
||||
return err
|
||||
case protocolModeV1, protocolModeV1Compatibility:
|
||||
return nil
|
||||
default:
|
||||
panic(fmt.Sprintf("unrecognized mode = %d", igmp.mode))
|
||||
}
|
||||
}
|
||||
|
||||
// ShouldPerformProtocol implements ip.MulticastGroupProtocol.
|
||||
func (igmp *igmpState) ShouldPerformProtocol(groupAddress tcpip.Address) bool {
|
||||
// As per RFC 2236 section 6 page 10,
|
||||
//
|
||||
// The all-systems group (address 224.0.0.1) is handled as a special
|
||||
// case. The host starts in Idle Member state for that group on every
|
||||
// interface, never transitions to another state, and never sends a
|
||||
// report for that group.
|
||||
return groupAddress != header.IPv4AllSystems
|
||||
}
|
||||
|
||||
type igmpv3ReportBuilder struct {
|
||||
igmp *igmpState
|
||||
|
||||
records []header.IGMPv3ReportGroupAddressRecordSerializer
|
||||
}
|
||||
|
||||
// AddRecord implements ip.MulticastGroupProtocolV2ReportBuilder.
|
||||
func (b *igmpv3ReportBuilder) AddRecord(genericRecordType ip.MulticastGroupProtocolV2ReportRecordType, groupAddress tcpip.Address) {
|
||||
var recordType header.IGMPv3ReportRecordType
|
||||
switch genericRecordType {
|
||||
case ip.MulticastGroupProtocolV2ReportRecordModeIsInclude:
|
||||
recordType = header.IGMPv3ReportRecordModeIsInclude
|
||||
case ip.MulticastGroupProtocolV2ReportRecordModeIsExclude:
|
||||
recordType = header.IGMPv3ReportRecordModeIsExclude
|
||||
case ip.MulticastGroupProtocolV2ReportRecordChangeToIncludeMode:
|
||||
recordType = header.IGMPv3ReportRecordChangeToIncludeMode
|
||||
case ip.MulticastGroupProtocolV2ReportRecordChangeToExcludeMode:
|
||||
recordType = header.IGMPv3ReportRecordChangeToExcludeMode
|
||||
case ip.MulticastGroupProtocolV2ReportRecordAllowNewSources:
|
||||
recordType = header.IGMPv3ReportRecordAllowNewSources
|
||||
case ip.MulticastGroupProtocolV2ReportRecordBlockOldSources:
|
||||
recordType = header.IGMPv3ReportRecordBlockOldSources
|
||||
default:
|
||||
panic(fmt.Sprintf("unrecognied genericRecordType = %d", genericRecordType))
|
||||
}
|
||||
|
||||
b.records = append(b.records, header.IGMPv3ReportGroupAddressRecordSerializer{
|
||||
RecordType: recordType,
|
||||
GroupAddress: groupAddress,
|
||||
Sources: nil,
|
||||
})
|
||||
}
|
||||
|
||||
// Send implements ip.MulticastGroupProtocolV2ReportBuilder.
|
||||
//
|
||||
// +checklocksread:b.igmp.ep.mu
|
||||
func (b *igmpv3ReportBuilder) Send() (sent bool, err tcpip.Error) {
|
||||
if len(b.records) == 0 {
|
||||
return false, err
|
||||
}
|
||||
|
||||
options := header.IPv4OptionsSerializer{
|
||||
&header.IPv4SerializableRouterAlertOption{},
|
||||
}
|
||||
mtu := int(b.igmp.ep.MTU()) - int(options.Length())
|
||||
|
||||
allSentWithSpecifiedAddress := true
|
||||
var firstErr tcpip.Error
|
||||
for records := b.records; len(records) != 0; {
|
||||
spaceLeft := mtu
|
||||
maxRecords := 0
|
||||
|
||||
for ; maxRecords < len(records); maxRecords++ {
|
||||
tmp := spaceLeft - records[maxRecords].Length()
|
||||
if tmp > 0 {
|
||||
spaceLeft = tmp
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
serializer := header.IGMPv3ReportSerializer{Records: records[:maxRecords]}
|
||||
records = records[maxRecords:]
|
||||
|
||||
icmpView := buffer.NewViewSize(serializer.Length())
|
||||
serializer.SerializeInto(icmpView.AsSlice())
|
||||
if sentWithSpecifiedAddress, err := b.igmp.writePacketInner(
|
||||
icmpView,
|
||||
b.igmp.ep.stats.igmp.packetsSent.v3MembershipReport,
|
||||
options,
|
||||
header.IGMPv3RoutersAddress,
|
||||
); err != nil {
|
||||
if firstErr != nil {
|
||||
firstErr = nil
|
||||
}
|
||||
allSentWithSpecifiedAddress = false
|
||||
} else if !sentWithSpecifiedAddress {
|
||||
allSentWithSpecifiedAddress = false
|
||||
}
|
||||
}
|
||||
|
||||
return allSentWithSpecifiedAddress, firstErr
|
||||
}
|
||||
|
||||
// NewReportV2Builder implements ip.MulticastGroupProtocol.
|
||||
func (igmp *igmpState) NewReportV2Builder() ip.MulticastGroupProtocolV2ReportBuilder {
|
||||
return &igmpv3ReportBuilder{igmp: igmp}
|
||||
}
|
||||
|
||||
// V2QueryMaxRespCodeToV2Delay implements ip.MulticastGroupProtocol.
|
||||
func (*igmpState) V2QueryMaxRespCodeToV2Delay(code uint16) time.Duration {
|
||||
if code > math.MaxUint8 {
|
||||
panic(fmt.Sprintf("got IGMPv3 MaxRespCode = %d, want <= %d", code, math.MaxUint8))
|
||||
}
|
||||
return header.IGMPv3MaximumResponseDelay(uint8(code))
|
||||
}
|
||||
|
||||
// V2QueryMaxRespCodeToV1Delay implements ip.MulticastGroupProtocol.
|
||||
func (*igmpState) V2QueryMaxRespCodeToV1Delay(code uint16) time.Duration {
|
||||
return time.Duration(code) * time.Millisecond
|
||||
}
|
||||
|
||||
// init sets up an igmpState struct, and is required to be called before using
|
||||
// a new igmpState.
|
||||
//
|
||||
// Must only be called once for the lifetime of igmp.
|
||||
func (igmp *igmpState) init(ep *endpoint) {
|
||||
igmp.ep = ep
|
||||
igmp.genericMulticastProtocol.Init(&ep.mu, ip.GenericMulticastProtocolOptions{
|
||||
Rand: ep.protocol.stack.InsecureRNG(),
|
||||
Clock: ep.protocol.stack.Clock(),
|
||||
Protocol: igmp,
|
||||
MaxUnsolicitedReportDelay: UnsolicitedReportIntervalMax,
|
||||
})
|
||||
// As per RFC 2236 Page 9 says "No IGMPv1 Router Present ... is
|
||||
// the initial state.
|
||||
igmp.mode = protocolModeV2OrV3
|
||||
igmp.igmpV1Job = tcpip.NewJob(ep.protocol.stack.Clock(), &ep.mu, func() {
|
||||
igmp.mode = protocolModeV2OrV3
|
||||
})
|
||||
}
|
||||
|
||||
// +checklocks:igmp.ep.mu
|
||||
func (igmp *igmpState) isSourceIPValidLocked(src tcpip.Address, messageType header.IGMPType) bool {
|
||||
if messageType == header.IGMPMembershipQuery {
|
||||
// RFC 2236 does not require the IGMP implementation to check the source IP
|
||||
// for Membership Query messages.
|
||||
return true
|
||||
}
|
||||
|
||||
// As per RFC 2236 section 10,
|
||||
//
|
||||
// Ignore the Report if you cannot identify the source address of the
|
||||
// packet as belonging to a subnet assigned to the interface on which the
|
||||
// packet was received.
|
||||
//
|
||||
// Ignore the Leave message if you cannot identify the source address of
|
||||
// the packet as belonging to a subnet assigned to the interface on which
|
||||
// the packet was received.
|
||||
//
|
||||
// Note: this rule applies to both V1 and V2 Membership Reports.
|
||||
var isSourceIPValid bool
|
||||
igmp.ep.addressableEndpointState.ForEachPrimaryEndpoint(func(addressEndpoint stack.AddressEndpoint) bool {
|
||||
if subnet := addressEndpoint.Subnet(); subnet.Contains(src) {
|
||||
isSourceIPValid = true
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return isSourceIPValid
|
||||
}
|
||||
|
||||
// +checklocks:igmp.ep.mu
|
||||
func (igmp *igmpState) isPacketValidLocked(pkt *stack.PacketBuffer, messageType header.IGMPType, hasRouterAlertOption bool) bool {
|
||||
// We can safely assume that the IP header is valid if we got this far.
|
||||
iph := header.IPv4(pkt.NetworkHeader().Slice())
|
||||
|
||||
// As per RFC 2236 section 2,
|
||||
//
|
||||
// All IGMP messages described in this document are sent with IP TTL 1, and
|
||||
// contain the IP Router Alert option [RFC 2113] in their IP header.
|
||||
if !hasRouterAlertOption || iph.TTL() != header.IGMPTTL {
|
||||
return false
|
||||
}
|
||||
|
||||
return igmp.isSourceIPValidLocked(iph.SourceAddress(), messageType)
|
||||
}
|
||||
|
||||
// handleIGMP handles an IGMP packet.
|
||||
//
|
||||
// +checklocks:igmp.ep.mu
|
||||
func (igmp *igmpState) handleIGMP(pkt *stack.PacketBuffer, hasRouterAlertOption bool) {
|
||||
received := igmp.ep.stats.igmp.packetsReceived
|
||||
hdr, ok := pkt.Data().PullUp(pkt.Data().Size())
|
||||
if !ok {
|
||||
received.invalid.Increment()
|
||||
return
|
||||
}
|
||||
h := header.IGMP(hdr)
|
||||
if len(h) < header.IGMPMinimumSize {
|
||||
received.invalid.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
// As per RFC 1071 section 1.3,
|
||||
//
|
||||
// To check a checksum, the 1's complement sum is computed over the
|
||||
// same set of octets, including the checksum field. If the result
|
||||
// is all 1 bits (-0 in 1's complement arithmetic), the check
|
||||
// succeeds.
|
||||
if pkt.Data().Checksum() != 0xFFFF {
|
||||
received.checksumErrors.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
isValid := func(minimumSize int) bool {
|
||||
return len(hdr) >= minimumSize && igmp.isPacketValidLocked(pkt, h.Type(), hasRouterAlertOption)
|
||||
}
|
||||
|
||||
switch h.Type() {
|
||||
case header.IGMPMembershipQuery:
|
||||
received.membershipQuery.Increment()
|
||||
if len(h) >= header.IGMPv3QueryMinimumSize {
|
||||
if isValid(header.IGMPv3QueryMinimumSize) {
|
||||
igmp.handleMembershipQueryV3(header.IGMPv3Query(h))
|
||||
} else {
|
||||
received.invalid.Increment()
|
||||
}
|
||||
return
|
||||
} else if !isValid(header.IGMPQueryMinimumSize) {
|
||||
received.invalid.Increment()
|
||||
return
|
||||
}
|
||||
igmp.handleMembershipQuery(h.GroupAddress(), h.MaxRespTime())
|
||||
case header.IGMPv1MembershipReport:
|
||||
received.v1MembershipReport.Increment()
|
||||
if !isValid(header.IGMPReportMinimumSize) {
|
||||
received.invalid.Increment()
|
||||
return
|
||||
}
|
||||
igmp.handleMembershipReport(h.GroupAddress())
|
||||
case header.IGMPv2MembershipReport:
|
||||
received.v2MembershipReport.Increment()
|
||||
if !isValid(header.IGMPReportMinimumSize) {
|
||||
received.invalid.Increment()
|
||||
return
|
||||
}
|
||||
igmp.handleMembershipReport(h.GroupAddress())
|
||||
case header.IGMPLeaveGroup:
|
||||
received.leaveGroup.Increment()
|
||||
if !isValid(header.IGMPLeaveMessageMinimumSize) {
|
||||
received.invalid.Increment()
|
||||
return
|
||||
}
|
||||
// As per RFC 2236 Section 6, Page 7: "IGMP messages other than Query or
|
||||
// Report, are ignored in all states"
|
||||
|
||||
default:
|
||||
// As per RFC 2236 Section 2.1 Page 3: "Unrecognized message types should
|
||||
// be silently ignored. New message types may be used by newer versions of
|
||||
// IGMP, by multicast routing protocols, or other uses."
|
||||
received.unrecognized.Increment()
|
||||
}
|
||||
}
|
||||
|
||||
func (igmp *igmpState) resetV1Present() {
|
||||
igmp.igmpV1Job.Cancel()
|
||||
switch igmp.mode {
|
||||
case protocolModeV2OrV3, protocolModeV1:
|
||||
case protocolModeV1Compatibility:
|
||||
igmp.mode = protocolModeV2OrV3
|
||||
default:
|
||||
panic(fmt.Sprintf("unrecognized mode = %d", igmp.mode))
|
||||
}
|
||||
}
|
||||
|
||||
// handleMembershipQuery handles a membership query.
|
||||
//
|
||||
// +checklocks:igmp.ep.mu
|
||||
func (igmp *igmpState) handleMembershipQuery(groupAddress tcpip.Address, maxRespTime time.Duration) {
|
||||
// As per RFC 2236 Section 6, Page 10: If the maximum response time is zero
|
||||
// then change the state to note that an IGMPv1 router is present and
|
||||
// schedule the query received Job.
|
||||
if maxRespTime == 0 && igmp.Enabled() {
|
||||
switch igmp.mode {
|
||||
case protocolModeV2OrV3, protocolModeV1Compatibility:
|
||||
igmp.igmpV1Job.Cancel()
|
||||
igmp.igmpV1Job.Schedule(v1RouterPresentTimeout)
|
||||
igmp.mode = protocolModeV1Compatibility
|
||||
case protocolModeV1:
|
||||
default:
|
||||
panic(fmt.Sprintf("unrecognized mode = %d", igmp.mode))
|
||||
}
|
||||
|
||||
maxRespTime = v1MaxRespTime
|
||||
}
|
||||
|
||||
igmp.genericMulticastProtocol.HandleQueryLocked(groupAddress, maxRespTime)
|
||||
}
|
||||
|
||||
// handleMembershipQueryV3 handles a membership query.
|
||||
//
|
||||
// +checklocks:igmp.ep.mu
|
||||
func (igmp *igmpState) handleMembershipQueryV3(igmpHdr header.IGMPv3Query) {
|
||||
sources, ok := igmpHdr.Sources()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
igmp.genericMulticastProtocol.HandleQueryV2Locked(
|
||||
igmpHdr.GroupAddress(),
|
||||
uint16(igmpHdr.MaximumResponseCode()),
|
||||
sources,
|
||||
igmpHdr.QuerierRobustnessVariable(),
|
||||
igmpHdr.QuerierQueryInterval(),
|
||||
)
|
||||
}
|
||||
|
||||
// handleMembershipReport handles a membership report.
|
||||
//
|
||||
// +checklocks:igmp.ep.mu
|
||||
func (igmp *igmpState) handleMembershipReport(groupAddress tcpip.Address) {
|
||||
igmp.genericMulticastProtocol.HandleReportLocked(groupAddress)
|
||||
}
|
||||
|
||||
// writePacket assembles and sends an IGMP packet.
|
||||
//
|
||||
// +checklocksread:igmp.ep.mu
|
||||
func (igmp *igmpState) writePacket(destAddress tcpip.Address, groupAddress tcpip.Address, igmpType header.IGMPType) (bool, tcpip.Error) {
|
||||
igmpView := buffer.NewViewSize(header.IGMPReportMinimumSize)
|
||||
igmpData := header.IGMP(igmpView.AsSlice())
|
||||
igmpData.SetType(igmpType)
|
||||
igmpData.SetGroupAddress(groupAddress)
|
||||
igmpData.SetChecksum(header.IGMPCalculateChecksum(igmpData))
|
||||
|
||||
var reportType tcpip.MultiCounterStat
|
||||
sentStats := igmp.ep.stats.igmp.packetsSent
|
||||
switch igmpType {
|
||||
case header.IGMPv1MembershipReport:
|
||||
reportType = sentStats.v1MembershipReport
|
||||
case header.IGMPv2MembershipReport:
|
||||
reportType = sentStats.v2MembershipReport
|
||||
case header.IGMPLeaveGroup:
|
||||
reportType = sentStats.leaveGroup
|
||||
default:
|
||||
panic(fmt.Sprintf("unrecognized igmp type = %d", igmpType))
|
||||
}
|
||||
|
||||
return igmp.writePacketInner(
|
||||
igmpView,
|
||||
reportType,
|
||||
header.IPv4OptionsSerializer{
|
||||
&header.IPv4SerializableRouterAlertOption{},
|
||||
},
|
||||
destAddress,
|
||||
)
|
||||
}
|
||||
|
||||
// +checklocksread:igmp.ep.mu
|
||||
func (igmp *igmpState) writePacketInner(buf *buffer.View, reportStat tcpip.MultiCounterStat, options header.IPv4OptionsSerializer, destAddress tcpip.Address) (bool, tcpip.Error) {
|
||||
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
ReserveHeaderBytes: int(igmp.ep.MaxHeaderLength()),
|
||||
Payload: buffer.MakeWithView(buf),
|
||||
})
|
||||
defer pkt.DecRef()
|
||||
|
||||
addressEndpoint := igmp.ep.acquireOutgoingPrimaryAddressRLocked(destAddress, tcpip.Address{} /* srcHint */, false /* allowExpired */)
|
||||
if addressEndpoint == nil {
|
||||
return false, nil
|
||||
}
|
||||
localAddr := addressEndpoint.AddressWithPrefix().Address
|
||||
addressEndpoint.DecRef()
|
||||
addressEndpoint = nil
|
||||
if err := igmp.ep.addIPHeader(localAddr, destAddress, pkt, stack.NetworkHeaderParams{
|
||||
Protocol: header.IGMPProtocolNumber,
|
||||
TTL: header.IGMPTTL,
|
||||
TOS: stack.DefaultTOS,
|
||||
}, options); err != nil {
|
||||
panic(fmt.Sprintf("failed to add IP header: %s", err))
|
||||
}
|
||||
|
||||
sentStats := igmp.ep.stats.igmp.packetsSent
|
||||
if err := igmp.ep.nic.WritePacketToRemote(header.EthernetAddressFromMulticastIPv4Address(destAddress), pkt); err != nil {
|
||||
sentStats.dropped.Increment()
|
||||
return false, err
|
||||
}
|
||||
reportStat.Increment()
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// joinGroup handles adding a new group to the membership map, setting up the
|
||||
// IGMP state for the group, and sending and scheduling the required
|
||||
// messages.
|
||||
//
|
||||
// If the group already exists in the membership map, returns
|
||||
// *tcpip.ErrDuplicateAddress.
|
||||
//
|
||||
// +checklocks:igmp.ep.mu
|
||||
func (igmp *igmpState) joinGroup(groupAddress tcpip.Address) {
|
||||
igmp.genericMulticastProtocol.JoinGroupLocked(groupAddress)
|
||||
}
|
||||
|
||||
// isInGroup returns true if the specified group has been joined locally.
|
||||
//
|
||||
// +checklocksread:igmp.ep.mu
|
||||
func (igmp *igmpState) isInGroup(groupAddress tcpip.Address) bool {
|
||||
return igmp.genericMulticastProtocol.IsLocallyJoinedRLocked(groupAddress)
|
||||
}
|
||||
|
||||
// leaveGroup handles removing the group from the membership map, cancels any
|
||||
// delay timers associated with that group, and sends the Leave Group message
|
||||
// if required.
|
||||
//
|
||||
// +checklocks:igmp.ep.mu
|
||||
func (igmp *igmpState) leaveGroup(groupAddress tcpip.Address) tcpip.Error {
|
||||
// LeaveGroup returns false only if the group was not joined.
|
||||
if igmp.genericMulticastProtocol.LeaveGroupLocked(groupAddress) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &tcpip.ErrBadLocalAddress{}
|
||||
}
|
||||
|
||||
// softLeaveAll leaves all groups from the perspective of IGMP, but remains
|
||||
// joined locally.
|
||||
//
|
||||
// +checklocks:igmp.ep.mu
|
||||
func (igmp *igmpState) softLeaveAll() {
|
||||
igmp.genericMulticastProtocol.MakeAllNonMemberLocked()
|
||||
}
|
||||
|
||||
// initializeAll attempts to initialize the IGMP state for each group that has
|
||||
// been joined locally.
|
||||
//
|
||||
// +checklocks:igmp.ep.mu
|
||||
func (igmp *igmpState) initializeAll() {
|
||||
igmp.genericMulticastProtocol.InitializeGroupsLocked()
|
||||
}
|
||||
|
||||
// sendQueuedReports attempts to send any reports that are queued for sending.
|
||||
//
|
||||
// +checklocks:igmp.ep.mu
|
||||
func (igmp *igmpState) sendQueuedReports() {
|
||||
igmp.genericMulticastProtocol.SendQueuedReportsLocked()
|
||||
}
|
||||
|
||||
// setVersion sets the IGMP version.
|
||||
//
|
||||
// +checklocks:igmp.ep.mu
|
||||
func (igmp *igmpState) setVersion(v IGMPVersion) IGMPVersion {
|
||||
prev := igmp.mode
|
||||
igmp.igmpV1Job.Cancel()
|
||||
|
||||
var prevGenericModeV1 bool
|
||||
switch v {
|
||||
case IGMPVersion3:
|
||||
prevGenericModeV1 = igmp.genericMulticastProtocol.SetV1ModeLocked(false)
|
||||
igmp.mode = protocolModeV2OrV3
|
||||
case IGMPVersion2:
|
||||
// IGMPv1 and IGMPv2 map to V1 of the generic multicast protocol.
|
||||
prevGenericModeV1 = igmp.genericMulticastProtocol.SetV1ModeLocked(true)
|
||||
igmp.mode = protocolModeV2OrV3
|
||||
case IGMPVersion1:
|
||||
// IGMPv1 and IGMPv2 map to V1 of the generic multicast protocol.
|
||||
prevGenericModeV1 = igmp.genericMulticastProtocol.SetV1ModeLocked(true)
|
||||
igmp.mode = protocolModeV1
|
||||
default:
|
||||
panic(fmt.Sprintf("unrecognized version = %d", v))
|
||||
}
|
||||
|
||||
return toIGMPVersion(prev, prevGenericModeV1)
|
||||
}
|
||||
|
||||
func toIGMPVersion(mode protocolMode, genericV1 bool) IGMPVersion {
|
||||
switch mode {
|
||||
case protocolModeV2OrV3, protocolModeV1Compatibility:
|
||||
if genericV1 {
|
||||
return IGMPVersion2
|
||||
}
|
||||
return IGMPVersion3
|
||||
case protocolModeV1:
|
||||
return IGMPVersion1
|
||||
default:
|
||||
panic(fmt.Sprintf("unrecognized mode = %d", mode))
|
||||
}
|
||||
}
|
||||
|
||||
// getVersion returns the IGMP version.
|
||||
//
|
||||
// +checklocksread:igmp.ep.mu
|
||||
func (igmp *igmpState) getVersion() IGMPVersion {
|
||||
return toIGMPVersion(igmp.mode, igmp.genericMulticastProtocol.GetV1ModeLocked())
|
||||
}
|
||||
2405
pkg/tcpip/network/ipv4/ipv4.go
Normal file
2405
pkg/tcpip/network/ipv4/ipv4.go
Normal file
File diff suppressed because it is too large
Load diff
14
pkg/tcpip/network/ipv4/ipv4_export.go
Normal file
14
pkg/tcpip/network/ipv4/ipv4_export.go
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package ipv4
|
||||
|
||||
import (
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
type ExportedEndpoint interface {
|
||||
WritePacketDirect(r *stack.Route, pkt *stack.PacketBuffer) tcpip.Error
|
||||
}
|
||||
|
||||
func (e *endpoint) WritePacketDirect(r *stack.Route, pkt *stack.PacketBuffer) tcpip.Error {
|
||||
return e.writePacket(r, pkt)
|
||||
}
|
||||
785
pkg/tcpip/network/ipv4/ipv4_state_autogen.go
Normal file
785
pkg/tcpip/network/ipv4/ipv4_state_autogen.go
Normal file
|
|
@ -0,0 +1,785 @@
|
|||
// automatically generated by stateify.
|
||||
|
||||
package ipv4
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/state"
|
||||
)
|
||||
|
||||
func (i *icmpv4DestinationUnreachableSockError) StateTypeName() string {
|
||||
return "pkg/tcpip/network/ipv4.icmpv4DestinationUnreachableSockError"
|
||||
}
|
||||
|
||||
func (i *icmpv4DestinationUnreachableSockError) StateFields() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (i *icmpv4DestinationUnreachableSockError) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (i *icmpv4DestinationUnreachableSockError) StateSave(stateSinkObject state.Sink) {
|
||||
i.beforeSave()
|
||||
}
|
||||
|
||||
func (i *icmpv4DestinationUnreachableSockError) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (i *icmpv4DestinationUnreachableSockError) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
}
|
||||
|
||||
func (i *icmpv4DestinationHostUnreachableSockError) StateTypeName() string {
|
||||
return "pkg/tcpip/network/ipv4.icmpv4DestinationHostUnreachableSockError"
|
||||
}
|
||||
|
||||
func (i *icmpv4DestinationHostUnreachableSockError) StateFields() []string {
|
||||
return []string{
|
||||
"icmpv4DestinationUnreachableSockError",
|
||||
}
|
||||
}
|
||||
|
||||
func (i *icmpv4DestinationHostUnreachableSockError) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (i *icmpv4DestinationHostUnreachableSockError) StateSave(stateSinkObject state.Sink) {
|
||||
i.beforeSave()
|
||||
stateSinkObject.Save(0, &i.icmpv4DestinationUnreachableSockError)
|
||||
}
|
||||
|
||||
func (i *icmpv4DestinationHostUnreachableSockError) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (i *icmpv4DestinationHostUnreachableSockError) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &i.icmpv4DestinationUnreachableSockError)
|
||||
}
|
||||
|
||||
func (i *icmpv4DestinationNetUnreachableSockError) StateTypeName() string {
|
||||
return "pkg/tcpip/network/ipv4.icmpv4DestinationNetUnreachableSockError"
|
||||
}
|
||||
|
||||
func (i *icmpv4DestinationNetUnreachableSockError) StateFields() []string {
|
||||
return []string{
|
||||
"icmpv4DestinationUnreachableSockError",
|
||||
}
|
||||
}
|
||||
|
||||
func (i *icmpv4DestinationNetUnreachableSockError) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (i *icmpv4DestinationNetUnreachableSockError) StateSave(stateSinkObject state.Sink) {
|
||||
i.beforeSave()
|
||||
stateSinkObject.Save(0, &i.icmpv4DestinationUnreachableSockError)
|
||||
}
|
||||
|
||||
func (i *icmpv4DestinationNetUnreachableSockError) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (i *icmpv4DestinationNetUnreachableSockError) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &i.icmpv4DestinationUnreachableSockError)
|
||||
}
|
||||
|
||||
func (i *icmpv4DestinationPortUnreachableSockError) StateTypeName() string {
|
||||
return "pkg/tcpip/network/ipv4.icmpv4DestinationPortUnreachableSockError"
|
||||
}
|
||||
|
||||
func (i *icmpv4DestinationPortUnreachableSockError) StateFields() []string {
|
||||
return []string{
|
||||
"icmpv4DestinationUnreachableSockError",
|
||||
}
|
||||
}
|
||||
|
||||
func (i *icmpv4DestinationPortUnreachableSockError) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (i *icmpv4DestinationPortUnreachableSockError) StateSave(stateSinkObject state.Sink) {
|
||||
i.beforeSave()
|
||||
stateSinkObject.Save(0, &i.icmpv4DestinationUnreachableSockError)
|
||||
}
|
||||
|
||||
func (i *icmpv4DestinationPortUnreachableSockError) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (i *icmpv4DestinationPortUnreachableSockError) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &i.icmpv4DestinationUnreachableSockError)
|
||||
}
|
||||
|
||||
func (i *icmpv4DestinationProtoUnreachableSockError) StateTypeName() string {
|
||||
return "pkg/tcpip/network/ipv4.icmpv4DestinationProtoUnreachableSockError"
|
||||
}
|
||||
|
||||
func (i *icmpv4DestinationProtoUnreachableSockError) StateFields() []string {
|
||||
return []string{
|
||||
"icmpv4DestinationUnreachableSockError",
|
||||
}
|
||||
}
|
||||
|
||||
func (i *icmpv4DestinationProtoUnreachableSockError) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (i *icmpv4DestinationProtoUnreachableSockError) StateSave(stateSinkObject state.Sink) {
|
||||
i.beforeSave()
|
||||
stateSinkObject.Save(0, &i.icmpv4DestinationUnreachableSockError)
|
||||
}
|
||||
|
||||
func (i *icmpv4DestinationProtoUnreachableSockError) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (i *icmpv4DestinationProtoUnreachableSockError) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &i.icmpv4DestinationUnreachableSockError)
|
||||
}
|
||||
|
||||
func (i *icmpv4SourceRouteFailedSockError) StateTypeName() string {
|
||||
return "pkg/tcpip/network/ipv4.icmpv4SourceRouteFailedSockError"
|
||||
}
|
||||
|
||||
func (i *icmpv4SourceRouteFailedSockError) StateFields() []string {
|
||||
return []string{
|
||||
"icmpv4DestinationUnreachableSockError",
|
||||
}
|
||||
}
|
||||
|
||||
func (i *icmpv4SourceRouteFailedSockError) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (i *icmpv4SourceRouteFailedSockError) StateSave(stateSinkObject state.Sink) {
|
||||
i.beforeSave()
|
||||
stateSinkObject.Save(0, &i.icmpv4DestinationUnreachableSockError)
|
||||
}
|
||||
|
||||
func (i *icmpv4SourceRouteFailedSockError) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (i *icmpv4SourceRouteFailedSockError) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &i.icmpv4DestinationUnreachableSockError)
|
||||
}
|
||||
|
||||
func (i *icmpv4SourceHostIsolatedSockError) StateTypeName() string {
|
||||
return "pkg/tcpip/network/ipv4.icmpv4SourceHostIsolatedSockError"
|
||||
}
|
||||
|
||||
func (i *icmpv4SourceHostIsolatedSockError) StateFields() []string {
|
||||
return []string{
|
||||
"icmpv4DestinationUnreachableSockError",
|
||||
}
|
||||
}
|
||||
|
||||
func (i *icmpv4SourceHostIsolatedSockError) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (i *icmpv4SourceHostIsolatedSockError) StateSave(stateSinkObject state.Sink) {
|
||||
i.beforeSave()
|
||||
stateSinkObject.Save(0, &i.icmpv4DestinationUnreachableSockError)
|
||||
}
|
||||
|
||||
func (i *icmpv4SourceHostIsolatedSockError) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (i *icmpv4SourceHostIsolatedSockError) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &i.icmpv4DestinationUnreachableSockError)
|
||||
}
|
||||
|
||||
func (i *icmpv4DestinationHostUnknownSockError) StateTypeName() string {
|
||||
return "pkg/tcpip/network/ipv4.icmpv4DestinationHostUnknownSockError"
|
||||
}
|
||||
|
||||
func (i *icmpv4DestinationHostUnknownSockError) StateFields() []string {
|
||||
return []string{
|
||||
"icmpv4DestinationUnreachableSockError",
|
||||
}
|
||||
}
|
||||
|
||||
func (i *icmpv4DestinationHostUnknownSockError) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (i *icmpv4DestinationHostUnknownSockError) StateSave(stateSinkObject state.Sink) {
|
||||
i.beforeSave()
|
||||
stateSinkObject.Save(0, &i.icmpv4DestinationUnreachableSockError)
|
||||
}
|
||||
|
||||
func (i *icmpv4DestinationHostUnknownSockError) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (i *icmpv4DestinationHostUnknownSockError) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &i.icmpv4DestinationUnreachableSockError)
|
||||
}
|
||||
|
||||
func (e *icmpv4FragmentationNeededSockError) StateTypeName() string {
|
||||
return "pkg/tcpip/network/ipv4.icmpv4FragmentationNeededSockError"
|
||||
}
|
||||
|
||||
func (e *icmpv4FragmentationNeededSockError) StateFields() []string {
|
||||
return []string{
|
||||
"icmpv4DestinationUnreachableSockError",
|
||||
"mtu",
|
||||
}
|
||||
}
|
||||
|
||||
func (e *icmpv4FragmentationNeededSockError) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *icmpv4FragmentationNeededSockError) StateSave(stateSinkObject state.Sink) {
|
||||
e.beforeSave()
|
||||
stateSinkObject.Save(0, &e.icmpv4DestinationUnreachableSockError)
|
||||
stateSinkObject.Save(1, &e.mtu)
|
||||
}
|
||||
|
||||
func (e *icmpv4FragmentationNeededSockError) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *icmpv4FragmentationNeededSockError) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &e.icmpv4DestinationUnreachableSockError)
|
||||
stateSourceObject.Load(1, &e.mtu)
|
||||
}
|
||||
|
||||
func (i *IGMPOptions) StateTypeName() string {
|
||||
return "pkg/tcpip/network/ipv4.IGMPOptions"
|
||||
}
|
||||
|
||||
func (i *IGMPOptions) StateFields() []string {
|
||||
return []string{
|
||||
"Enabled",
|
||||
}
|
||||
}
|
||||
|
||||
func (i *IGMPOptions) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (i *IGMPOptions) StateSave(stateSinkObject state.Sink) {
|
||||
i.beforeSave()
|
||||
stateSinkObject.Save(0, &i.Enabled)
|
||||
}
|
||||
|
||||
func (i *IGMPOptions) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (i *IGMPOptions) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &i.Enabled)
|
||||
}
|
||||
|
||||
func (igmp *igmpState) StateTypeName() string {
|
||||
return "pkg/tcpip/network/ipv4.igmpState"
|
||||
}
|
||||
|
||||
func (igmp *igmpState) StateFields() []string {
|
||||
return []string{
|
||||
"ep",
|
||||
"genericMulticastProtocol",
|
||||
"mode",
|
||||
"igmpV1Job",
|
||||
}
|
||||
}
|
||||
|
||||
func (igmp *igmpState) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (igmp *igmpState) StateSave(stateSinkObject state.Sink) {
|
||||
igmp.beforeSave()
|
||||
stateSinkObject.Save(0, &igmp.ep)
|
||||
stateSinkObject.Save(1, &igmp.genericMulticastProtocol)
|
||||
stateSinkObject.Save(2, &igmp.mode)
|
||||
stateSinkObject.Save(3, &igmp.igmpV1Job)
|
||||
}
|
||||
|
||||
func (igmp *igmpState) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (igmp *igmpState) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &igmp.ep)
|
||||
stateSourceObject.Load(1, &igmp.genericMulticastProtocol)
|
||||
stateSourceObject.Load(2, &igmp.mode)
|
||||
stateSourceObject.Load(3, &igmp.igmpV1Job)
|
||||
}
|
||||
|
||||
func (e *endpoint) StateTypeName() string {
|
||||
return "pkg/tcpip/network/ipv4.endpoint"
|
||||
}
|
||||
|
||||
func (e *endpoint) StateFields() []string {
|
||||
return []string{
|
||||
"nic",
|
||||
"dispatcher",
|
||||
"protocol",
|
||||
"stats",
|
||||
"enabled",
|
||||
"forwarding",
|
||||
"multicastForwarding",
|
||||
"addressableEndpointState",
|
||||
"igmp",
|
||||
}
|
||||
}
|
||||
|
||||
func (e *endpoint) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *endpoint) StateSave(stateSinkObject state.Sink) {
|
||||
e.beforeSave()
|
||||
stateSinkObject.Save(0, &e.nic)
|
||||
stateSinkObject.Save(1, &e.dispatcher)
|
||||
stateSinkObject.Save(2, &e.protocol)
|
||||
stateSinkObject.Save(3, &e.stats)
|
||||
stateSinkObject.Save(4, &e.enabled)
|
||||
stateSinkObject.Save(5, &e.forwarding)
|
||||
stateSinkObject.Save(6, &e.multicastForwarding)
|
||||
stateSinkObject.Save(7, &e.addressableEndpointState)
|
||||
stateSinkObject.Save(8, &e.igmp)
|
||||
}
|
||||
|
||||
func (e *endpoint) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *endpoint) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &e.nic)
|
||||
stateSourceObject.Load(1, &e.dispatcher)
|
||||
stateSourceObject.Load(2, &e.protocol)
|
||||
stateSourceObject.Load(3, &e.stats)
|
||||
stateSourceObject.Load(4, &e.enabled)
|
||||
stateSourceObject.Load(5, &e.forwarding)
|
||||
stateSourceObject.Load(6, &e.multicastForwarding)
|
||||
stateSourceObject.Load(7, &e.addressableEndpointState)
|
||||
stateSourceObject.Load(8, &e.igmp)
|
||||
}
|
||||
|
||||
func (p *protocol) StateTypeName() string {
|
||||
return "pkg/tcpip/network/ipv4.protocol"
|
||||
}
|
||||
|
||||
func (p *protocol) StateFields() []string {
|
||||
return []string{
|
||||
"stack",
|
||||
"eps",
|
||||
"icmpRateLimitedTypes",
|
||||
"defaultTTL",
|
||||
"ids",
|
||||
"hashIV",
|
||||
"idTS",
|
||||
"fragmentation",
|
||||
"options",
|
||||
"multicastRouteTable",
|
||||
"multicastForwardingDisp",
|
||||
}
|
||||
}
|
||||
|
||||
func (p *protocol) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (p *protocol) StateSave(stateSinkObject state.Sink) {
|
||||
p.beforeSave()
|
||||
stateSinkObject.Save(0, &p.stack)
|
||||
stateSinkObject.Save(1, &p.eps)
|
||||
stateSinkObject.Save(2, &p.icmpRateLimitedTypes)
|
||||
stateSinkObject.Save(3, &p.defaultTTL)
|
||||
stateSinkObject.Save(4, &p.ids)
|
||||
stateSinkObject.Save(5, &p.hashIV)
|
||||
stateSinkObject.Save(6, &p.idTS)
|
||||
stateSinkObject.Save(7, &p.fragmentation)
|
||||
stateSinkObject.Save(8, &p.options)
|
||||
stateSinkObject.Save(9, &p.multicastRouteTable)
|
||||
stateSinkObject.Save(10, &p.multicastForwardingDisp)
|
||||
}
|
||||
|
||||
func (p *protocol) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (p *protocol) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &p.stack)
|
||||
stateSourceObject.Load(1, &p.eps)
|
||||
stateSourceObject.Load(2, &p.icmpRateLimitedTypes)
|
||||
stateSourceObject.Load(3, &p.defaultTTL)
|
||||
stateSourceObject.Load(4, &p.ids)
|
||||
stateSourceObject.Load(5, &p.hashIV)
|
||||
stateSourceObject.Load(6, &p.idTS)
|
||||
stateSourceObject.Load(7, &p.fragmentation)
|
||||
stateSourceObject.Load(8, &p.options)
|
||||
stateSourceObject.Load(9, &p.multicastRouteTable)
|
||||
stateSourceObject.Load(10, &p.multicastForwardingDisp)
|
||||
}
|
||||
|
||||
func (o *Options) StateTypeName() string {
|
||||
return "pkg/tcpip/network/ipv4.Options"
|
||||
}
|
||||
|
||||
func (o *Options) StateFields() []string {
|
||||
return []string{
|
||||
"IGMP",
|
||||
"AllowExternalLoopbackTraffic",
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Options) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (o *Options) StateSave(stateSinkObject state.Sink) {
|
||||
o.beforeSave()
|
||||
stateSinkObject.Save(0, &o.IGMP)
|
||||
stateSinkObject.Save(1, &o.AllowExternalLoopbackTraffic)
|
||||
}
|
||||
|
||||
func (o *Options) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (o *Options) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &o.IGMP)
|
||||
stateSourceObject.Load(1, &o.AllowExternalLoopbackTraffic)
|
||||
}
|
||||
|
||||
func (s *Stats) StateTypeName() string {
|
||||
return "pkg/tcpip/network/ipv4.Stats"
|
||||
}
|
||||
|
||||
func (s *Stats) StateFields() []string {
|
||||
return []string{
|
||||
"IP",
|
||||
"IGMP",
|
||||
"ICMP",
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Stats) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (s *Stats) StateSave(stateSinkObject state.Sink) {
|
||||
s.beforeSave()
|
||||
stateSinkObject.Save(0, &s.IP)
|
||||
stateSinkObject.Save(1, &s.IGMP)
|
||||
stateSinkObject.Save(2, &s.ICMP)
|
||||
}
|
||||
|
||||
func (s *Stats) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (s *Stats) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &s.IP)
|
||||
stateSourceObject.Load(1, &s.IGMP)
|
||||
stateSourceObject.Load(2, &s.ICMP)
|
||||
}
|
||||
|
||||
func (s *sharedStats) StateTypeName() string {
|
||||
return "pkg/tcpip/network/ipv4.sharedStats"
|
||||
}
|
||||
|
||||
func (s *sharedStats) StateFields() []string {
|
||||
return []string{
|
||||
"localStats",
|
||||
"ip",
|
||||
"icmp",
|
||||
"igmp",
|
||||
}
|
||||
}
|
||||
|
||||
func (s *sharedStats) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (s *sharedStats) StateSave(stateSinkObject state.Sink) {
|
||||
s.beforeSave()
|
||||
stateSinkObject.Save(0, &s.localStats)
|
||||
stateSinkObject.Save(1, &s.ip)
|
||||
stateSinkObject.Save(2, &s.icmp)
|
||||
stateSinkObject.Save(3, &s.igmp)
|
||||
}
|
||||
|
||||
func (s *sharedStats) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (s *sharedStats) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &s.localStats)
|
||||
stateSourceObject.Load(1, &s.ip)
|
||||
stateSourceObject.Load(2, &s.icmp)
|
||||
stateSourceObject.Load(3, &s.igmp)
|
||||
}
|
||||
|
||||
func (m *multiCounterICMPv4PacketStats) StateTypeName() string {
|
||||
return "pkg/tcpip/network/ipv4.multiCounterICMPv4PacketStats"
|
||||
}
|
||||
|
||||
func (m *multiCounterICMPv4PacketStats) StateFields() []string {
|
||||
return []string{
|
||||
"echoRequest",
|
||||
"echoReply",
|
||||
"dstUnreachable",
|
||||
"srcQuench",
|
||||
"redirect",
|
||||
"timeExceeded",
|
||||
"paramProblem",
|
||||
"timestamp",
|
||||
"timestampReply",
|
||||
"infoRequest",
|
||||
"infoReply",
|
||||
}
|
||||
}
|
||||
|
||||
func (m *multiCounterICMPv4PacketStats) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (m *multiCounterICMPv4PacketStats) StateSave(stateSinkObject state.Sink) {
|
||||
m.beforeSave()
|
||||
stateSinkObject.Save(0, &m.echoRequest)
|
||||
stateSinkObject.Save(1, &m.echoReply)
|
||||
stateSinkObject.Save(2, &m.dstUnreachable)
|
||||
stateSinkObject.Save(3, &m.srcQuench)
|
||||
stateSinkObject.Save(4, &m.redirect)
|
||||
stateSinkObject.Save(5, &m.timeExceeded)
|
||||
stateSinkObject.Save(6, &m.paramProblem)
|
||||
stateSinkObject.Save(7, &m.timestamp)
|
||||
stateSinkObject.Save(8, &m.timestampReply)
|
||||
stateSinkObject.Save(9, &m.infoRequest)
|
||||
stateSinkObject.Save(10, &m.infoReply)
|
||||
}
|
||||
|
||||
func (m *multiCounterICMPv4PacketStats) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (m *multiCounterICMPv4PacketStats) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &m.echoRequest)
|
||||
stateSourceObject.Load(1, &m.echoReply)
|
||||
stateSourceObject.Load(2, &m.dstUnreachable)
|
||||
stateSourceObject.Load(3, &m.srcQuench)
|
||||
stateSourceObject.Load(4, &m.redirect)
|
||||
stateSourceObject.Load(5, &m.timeExceeded)
|
||||
stateSourceObject.Load(6, &m.paramProblem)
|
||||
stateSourceObject.Load(7, &m.timestamp)
|
||||
stateSourceObject.Load(8, &m.timestampReply)
|
||||
stateSourceObject.Load(9, &m.infoRequest)
|
||||
stateSourceObject.Load(10, &m.infoReply)
|
||||
}
|
||||
|
||||
func (m *multiCounterICMPv4SentPacketStats) StateTypeName() string {
|
||||
return "pkg/tcpip/network/ipv4.multiCounterICMPv4SentPacketStats"
|
||||
}
|
||||
|
||||
func (m *multiCounterICMPv4SentPacketStats) StateFields() []string {
|
||||
return []string{
|
||||
"multiCounterICMPv4PacketStats",
|
||||
"dropped",
|
||||
"rateLimited",
|
||||
}
|
||||
}
|
||||
|
||||
func (m *multiCounterICMPv4SentPacketStats) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (m *multiCounterICMPv4SentPacketStats) StateSave(stateSinkObject state.Sink) {
|
||||
m.beforeSave()
|
||||
stateSinkObject.Save(0, &m.multiCounterICMPv4PacketStats)
|
||||
stateSinkObject.Save(1, &m.dropped)
|
||||
stateSinkObject.Save(2, &m.rateLimited)
|
||||
}
|
||||
|
||||
func (m *multiCounterICMPv4SentPacketStats) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (m *multiCounterICMPv4SentPacketStats) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &m.multiCounterICMPv4PacketStats)
|
||||
stateSourceObject.Load(1, &m.dropped)
|
||||
stateSourceObject.Load(2, &m.rateLimited)
|
||||
}
|
||||
|
||||
func (m *multiCounterICMPv4ReceivedPacketStats) StateTypeName() string {
|
||||
return "pkg/tcpip/network/ipv4.multiCounterICMPv4ReceivedPacketStats"
|
||||
}
|
||||
|
||||
func (m *multiCounterICMPv4ReceivedPacketStats) StateFields() []string {
|
||||
return []string{
|
||||
"multiCounterICMPv4PacketStats",
|
||||
"invalid",
|
||||
}
|
||||
}
|
||||
|
||||
func (m *multiCounterICMPv4ReceivedPacketStats) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (m *multiCounterICMPv4ReceivedPacketStats) StateSave(stateSinkObject state.Sink) {
|
||||
m.beforeSave()
|
||||
stateSinkObject.Save(0, &m.multiCounterICMPv4PacketStats)
|
||||
stateSinkObject.Save(1, &m.invalid)
|
||||
}
|
||||
|
||||
func (m *multiCounterICMPv4ReceivedPacketStats) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (m *multiCounterICMPv4ReceivedPacketStats) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &m.multiCounterICMPv4PacketStats)
|
||||
stateSourceObject.Load(1, &m.invalid)
|
||||
}
|
||||
|
||||
func (m *multiCounterICMPv4Stats) StateTypeName() string {
|
||||
return "pkg/tcpip/network/ipv4.multiCounterICMPv4Stats"
|
||||
}
|
||||
|
||||
func (m *multiCounterICMPv4Stats) StateFields() []string {
|
||||
return []string{
|
||||
"packetsSent",
|
||||
"packetsReceived",
|
||||
}
|
||||
}
|
||||
|
||||
func (m *multiCounterICMPv4Stats) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (m *multiCounterICMPv4Stats) StateSave(stateSinkObject state.Sink) {
|
||||
m.beforeSave()
|
||||
stateSinkObject.Save(0, &m.packetsSent)
|
||||
stateSinkObject.Save(1, &m.packetsReceived)
|
||||
}
|
||||
|
||||
func (m *multiCounterICMPv4Stats) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (m *multiCounterICMPv4Stats) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &m.packetsSent)
|
||||
stateSourceObject.Load(1, &m.packetsReceived)
|
||||
}
|
||||
|
||||
func (m *multiCounterIGMPPacketStats) StateTypeName() string {
|
||||
return "pkg/tcpip/network/ipv4.multiCounterIGMPPacketStats"
|
||||
}
|
||||
|
||||
func (m *multiCounterIGMPPacketStats) StateFields() []string {
|
||||
return []string{
|
||||
"membershipQuery",
|
||||
"v1MembershipReport",
|
||||
"v2MembershipReport",
|
||||
"v3MembershipReport",
|
||||
"leaveGroup",
|
||||
}
|
||||
}
|
||||
|
||||
func (m *multiCounterIGMPPacketStats) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (m *multiCounterIGMPPacketStats) StateSave(stateSinkObject state.Sink) {
|
||||
m.beforeSave()
|
||||
stateSinkObject.Save(0, &m.membershipQuery)
|
||||
stateSinkObject.Save(1, &m.v1MembershipReport)
|
||||
stateSinkObject.Save(2, &m.v2MembershipReport)
|
||||
stateSinkObject.Save(3, &m.v3MembershipReport)
|
||||
stateSinkObject.Save(4, &m.leaveGroup)
|
||||
}
|
||||
|
||||
func (m *multiCounterIGMPPacketStats) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (m *multiCounterIGMPPacketStats) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &m.membershipQuery)
|
||||
stateSourceObject.Load(1, &m.v1MembershipReport)
|
||||
stateSourceObject.Load(2, &m.v2MembershipReport)
|
||||
stateSourceObject.Load(3, &m.v3MembershipReport)
|
||||
stateSourceObject.Load(4, &m.leaveGroup)
|
||||
}
|
||||
|
||||
func (m *multiCounterIGMPSentPacketStats) StateTypeName() string {
|
||||
return "pkg/tcpip/network/ipv4.multiCounterIGMPSentPacketStats"
|
||||
}
|
||||
|
||||
func (m *multiCounterIGMPSentPacketStats) StateFields() []string {
|
||||
return []string{
|
||||
"multiCounterIGMPPacketStats",
|
||||
"dropped",
|
||||
}
|
||||
}
|
||||
|
||||
func (m *multiCounterIGMPSentPacketStats) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (m *multiCounterIGMPSentPacketStats) StateSave(stateSinkObject state.Sink) {
|
||||
m.beforeSave()
|
||||
stateSinkObject.Save(0, &m.multiCounterIGMPPacketStats)
|
||||
stateSinkObject.Save(1, &m.dropped)
|
||||
}
|
||||
|
||||
func (m *multiCounterIGMPSentPacketStats) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (m *multiCounterIGMPSentPacketStats) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &m.multiCounterIGMPPacketStats)
|
||||
stateSourceObject.Load(1, &m.dropped)
|
||||
}
|
||||
|
||||
func (m *multiCounterIGMPReceivedPacketStats) StateTypeName() string {
|
||||
return "pkg/tcpip/network/ipv4.multiCounterIGMPReceivedPacketStats"
|
||||
}
|
||||
|
||||
func (m *multiCounterIGMPReceivedPacketStats) StateFields() []string {
|
||||
return []string{
|
||||
"multiCounterIGMPPacketStats",
|
||||
"invalid",
|
||||
"checksumErrors",
|
||||
"unrecognized",
|
||||
}
|
||||
}
|
||||
|
||||
func (m *multiCounterIGMPReceivedPacketStats) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (m *multiCounterIGMPReceivedPacketStats) StateSave(stateSinkObject state.Sink) {
|
||||
m.beforeSave()
|
||||
stateSinkObject.Save(0, &m.multiCounterIGMPPacketStats)
|
||||
stateSinkObject.Save(1, &m.invalid)
|
||||
stateSinkObject.Save(2, &m.checksumErrors)
|
||||
stateSinkObject.Save(3, &m.unrecognized)
|
||||
}
|
||||
|
||||
func (m *multiCounterIGMPReceivedPacketStats) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (m *multiCounterIGMPReceivedPacketStats) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &m.multiCounterIGMPPacketStats)
|
||||
stateSourceObject.Load(1, &m.invalid)
|
||||
stateSourceObject.Load(2, &m.checksumErrors)
|
||||
stateSourceObject.Load(3, &m.unrecognized)
|
||||
}
|
||||
|
||||
func (m *multiCounterIGMPStats) StateTypeName() string {
|
||||
return "pkg/tcpip/network/ipv4.multiCounterIGMPStats"
|
||||
}
|
||||
|
||||
func (m *multiCounterIGMPStats) StateFields() []string {
|
||||
return []string{
|
||||
"packetsSent",
|
||||
"packetsReceived",
|
||||
}
|
||||
}
|
||||
|
||||
func (m *multiCounterIGMPStats) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (m *multiCounterIGMPStats) StateSave(stateSinkObject state.Sink) {
|
||||
m.beforeSave()
|
||||
stateSinkObject.Save(0, &m.packetsSent)
|
||||
stateSinkObject.Save(1, &m.packetsReceived)
|
||||
}
|
||||
|
||||
func (m *multiCounterIGMPStats) afterLoad(context.Context) {}
|
||||
|
||||
// +checklocksignore
|
||||
func (m *multiCounterIGMPStats) StateLoad(ctx context.Context, stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &m.packetsSent)
|
||||
stateSourceObject.Load(1, &m.packetsReceived)
|
||||
}
|
||||
|
||||
func init() {
|
||||
state.Register((*icmpv4DestinationUnreachableSockError)(nil))
|
||||
state.Register((*icmpv4DestinationHostUnreachableSockError)(nil))
|
||||
state.Register((*icmpv4DestinationNetUnreachableSockError)(nil))
|
||||
state.Register((*icmpv4DestinationPortUnreachableSockError)(nil))
|
||||
state.Register((*icmpv4DestinationProtoUnreachableSockError)(nil))
|
||||
state.Register((*icmpv4SourceRouteFailedSockError)(nil))
|
||||
state.Register((*icmpv4SourceHostIsolatedSockError)(nil))
|
||||
state.Register((*icmpv4DestinationHostUnknownSockError)(nil))
|
||||
state.Register((*icmpv4FragmentationNeededSockError)(nil))
|
||||
state.Register((*IGMPOptions)(nil))
|
||||
state.Register((*igmpState)(nil))
|
||||
state.Register((*endpoint)(nil))
|
||||
state.Register((*protocol)(nil))
|
||||
state.Register((*Options)(nil))
|
||||
state.Register((*Stats)(nil))
|
||||
state.Register((*sharedStats)(nil))
|
||||
state.Register((*multiCounterICMPv4PacketStats)(nil))
|
||||
state.Register((*multiCounterICMPv4SentPacketStats)(nil))
|
||||
state.Register((*multiCounterICMPv4ReceivedPacketStats)(nil))
|
||||
state.Register((*multiCounterICMPv4Stats)(nil))
|
||||
state.Register((*multiCounterIGMPPacketStats)(nil))
|
||||
state.Register((*multiCounterIGMPSentPacketStats)(nil))
|
||||
state.Register((*multiCounterIGMPReceivedPacketStats)(nil))
|
||||
state.Register((*multiCounterIGMPStats)(nil))
|
||||
}
|
||||
203
pkg/tcpip/network/ipv4/stats.go
Normal file
203
pkg/tcpip/network/ipv4/stats.go
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
// 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.
|
||||
|
||||
package ipv4
|
||||
|
||||
import (
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/network/internal/ip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
var _ stack.IPNetworkEndpointStats = (*Stats)(nil)
|
||||
|
||||
// Stats holds statistics related to the IPv4 protocol family.
|
||||
//
|
||||
// +stateify savable
|
||||
type Stats struct {
|
||||
// IP holds IPv4 statistics.
|
||||
IP tcpip.IPStats
|
||||
|
||||
// IGMP holds IGMP statistics.
|
||||
IGMP tcpip.IGMPStats
|
||||
|
||||
// ICMP holds ICMPv4 statistics.
|
||||
ICMP tcpip.ICMPv4Stats
|
||||
}
|
||||
|
||||
// IsNetworkEndpointStats implements stack.NetworkEndpointStats.
|
||||
func (*Stats) IsNetworkEndpointStats() {}
|
||||
|
||||
// IPStats implements stack.IPNetworkEndointStats
|
||||
func (s *Stats) IPStats() *tcpip.IPStats {
|
||||
return &s.IP
|
||||
}
|
||||
|
||||
// +stateify savable
|
||||
type sharedStats struct {
|
||||
localStats Stats
|
||||
ip ip.MultiCounterIPStats
|
||||
icmp multiCounterICMPv4Stats
|
||||
igmp multiCounterIGMPStats
|
||||
}
|
||||
|
||||
// LINT.IfChange(multiCounterICMPv4PacketStats)
|
||||
|
||||
// +stateify savable
|
||||
type multiCounterICMPv4PacketStats struct {
|
||||
echoRequest tcpip.MultiCounterStat
|
||||
echoReply tcpip.MultiCounterStat
|
||||
dstUnreachable tcpip.MultiCounterStat
|
||||
srcQuench tcpip.MultiCounterStat
|
||||
redirect tcpip.MultiCounterStat
|
||||
timeExceeded tcpip.MultiCounterStat
|
||||
paramProblem tcpip.MultiCounterStat
|
||||
timestamp tcpip.MultiCounterStat
|
||||
timestampReply tcpip.MultiCounterStat
|
||||
infoRequest tcpip.MultiCounterStat
|
||||
infoReply tcpip.MultiCounterStat
|
||||
}
|
||||
|
||||
func (m *multiCounterICMPv4PacketStats) init(a, b *tcpip.ICMPv4PacketStats) {
|
||||
m.echoRequest.Init(a.EchoRequest, b.EchoRequest)
|
||||
m.echoReply.Init(a.EchoReply, b.EchoReply)
|
||||
m.dstUnreachable.Init(a.DstUnreachable, b.DstUnreachable)
|
||||
m.srcQuench.Init(a.SrcQuench, b.SrcQuench)
|
||||
m.redirect.Init(a.Redirect, b.Redirect)
|
||||
m.timeExceeded.Init(a.TimeExceeded, b.TimeExceeded)
|
||||
m.paramProblem.Init(a.ParamProblem, b.ParamProblem)
|
||||
m.timestamp.Init(a.Timestamp, b.Timestamp)
|
||||
m.timestampReply.Init(a.TimestampReply, b.TimestampReply)
|
||||
m.infoRequest.Init(a.InfoRequest, b.InfoRequest)
|
||||
m.infoReply.Init(a.InfoReply, b.InfoReply)
|
||||
}
|
||||
|
||||
// LINT.ThenChange(../../tcpip.go:ICMPv4PacketStats)
|
||||
|
||||
// LINT.IfChange(multiCounterICMPv4SentPacketStats)
|
||||
|
||||
// +stateify savable
|
||||
type multiCounterICMPv4SentPacketStats struct {
|
||||
multiCounterICMPv4PacketStats
|
||||
dropped tcpip.MultiCounterStat
|
||||
rateLimited tcpip.MultiCounterStat
|
||||
}
|
||||
|
||||
func (m *multiCounterICMPv4SentPacketStats) init(a, b *tcpip.ICMPv4SentPacketStats) {
|
||||
m.multiCounterICMPv4PacketStats.init(&a.ICMPv4PacketStats, &b.ICMPv4PacketStats)
|
||||
m.dropped.Init(a.Dropped, b.Dropped)
|
||||
m.rateLimited.Init(a.RateLimited, b.RateLimited)
|
||||
}
|
||||
|
||||
// LINT.ThenChange(../../tcpip.go:ICMPv4SentPacketStats)
|
||||
|
||||
// LINT.IfChange(multiCounterICMPv4ReceivedPacketStats)
|
||||
|
||||
// +stateify savable
|
||||
type multiCounterICMPv4ReceivedPacketStats struct {
|
||||
multiCounterICMPv4PacketStats
|
||||
invalid tcpip.MultiCounterStat
|
||||
}
|
||||
|
||||
func (m *multiCounterICMPv4ReceivedPacketStats) init(a, b *tcpip.ICMPv4ReceivedPacketStats) {
|
||||
m.multiCounterICMPv4PacketStats.init(&a.ICMPv4PacketStats, &b.ICMPv4PacketStats)
|
||||
m.invalid.Init(a.Invalid, b.Invalid)
|
||||
}
|
||||
|
||||
// LINT.ThenChange(../../tcpip.go:ICMPv4ReceivedPacketStats)
|
||||
|
||||
// LINT.IfChange(multiCounterICMPv4Stats)
|
||||
|
||||
// +stateify savable
|
||||
type multiCounterICMPv4Stats struct {
|
||||
packetsSent multiCounterICMPv4SentPacketStats
|
||||
packetsReceived multiCounterICMPv4ReceivedPacketStats
|
||||
}
|
||||
|
||||
func (m *multiCounterICMPv4Stats) init(a, b *tcpip.ICMPv4Stats) {
|
||||
m.packetsSent.init(&a.PacketsSent, &b.PacketsSent)
|
||||
m.packetsReceived.init(&a.PacketsReceived, &b.PacketsReceived)
|
||||
}
|
||||
|
||||
// LINT.ThenChange(../../tcpip.go:ICMPv4Stats)
|
||||
|
||||
// LINT.IfChange(multiCounterIGMPPacketStats)
|
||||
|
||||
// +stateify savable
|
||||
type multiCounterIGMPPacketStats struct {
|
||||
membershipQuery tcpip.MultiCounterStat
|
||||
v1MembershipReport tcpip.MultiCounterStat
|
||||
v2MembershipReport tcpip.MultiCounterStat
|
||||
v3MembershipReport tcpip.MultiCounterStat
|
||||
leaveGroup tcpip.MultiCounterStat
|
||||
}
|
||||
|
||||
func (m *multiCounterIGMPPacketStats) init(a, b *tcpip.IGMPPacketStats) {
|
||||
m.membershipQuery.Init(a.MembershipQuery, b.MembershipQuery)
|
||||
m.v1MembershipReport.Init(a.V1MembershipReport, b.V1MembershipReport)
|
||||
m.v2MembershipReport.Init(a.V2MembershipReport, b.V2MembershipReport)
|
||||
m.v3MembershipReport.Init(a.V3MembershipReport, b.V3MembershipReport)
|
||||
m.leaveGroup.Init(a.LeaveGroup, b.LeaveGroup)
|
||||
}
|
||||
|
||||
// LINT.ThenChange(../../tcpip.go:IGMPPacketStats)
|
||||
|
||||
// LINT.IfChange(multiCounterIGMPSentPacketStats)
|
||||
|
||||
// +stateify savable
|
||||
type multiCounterIGMPSentPacketStats struct {
|
||||
multiCounterIGMPPacketStats
|
||||
dropped tcpip.MultiCounterStat
|
||||
}
|
||||
|
||||
func (m *multiCounterIGMPSentPacketStats) init(a, b *tcpip.IGMPSentPacketStats) {
|
||||
m.multiCounterIGMPPacketStats.init(&a.IGMPPacketStats, &b.IGMPPacketStats)
|
||||
m.dropped.Init(a.Dropped, b.Dropped)
|
||||
}
|
||||
|
||||
// LINT.ThenChange(../../tcpip.go:IGMPSentPacketStats)
|
||||
|
||||
// LINT.IfChange(multiCounterIGMPReceivedPacketStats)
|
||||
|
||||
// +stateify savable
|
||||
type multiCounterIGMPReceivedPacketStats struct {
|
||||
multiCounterIGMPPacketStats
|
||||
invalid tcpip.MultiCounterStat
|
||||
checksumErrors tcpip.MultiCounterStat
|
||||
unrecognized tcpip.MultiCounterStat
|
||||
}
|
||||
|
||||
func (m *multiCounterIGMPReceivedPacketStats) init(a, b *tcpip.IGMPReceivedPacketStats) {
|
||||
m.multiCounterIGMPPacketStats.init(&a.IGMPPacketStats, &b.IGMPPacketStats)
|
||||
m.invalid.Init(a.Invalid, b.Invalid)
|
||||
m.checksumErrors.Init(a.ChecksumErrors, b.ChecksumErrors)
|
||||
m.unrecognized.Init(a.Unrecognized, b.Unrecognized)
|
||||
}
|
||||
|
||||
// LINT.ThenChange(../../tcpip.go:IGMPReceivedPacketStats)
|
||||
|
||||
// LINT.IfChange(multiCounterIGMPStats)
|
||||
|
||||
// +stateify savable
|
||||
type multiCounterIGMPStats struct {
|
||||
packetsSent multiCounterIGMPSentPacketStats
|
||||
packetsReceived multiCounterIGMPReceivedPacketStats
|
||||
}
|
||||
|
||||
func (m *multiCounterIGMPStats) init(a, b *tcpip.IGMPStats) {
|
||||
m.packetsSent.init(&a.PacketsSent, &b.PacketsSent)
|
||||
m.packetsReceived.init(&a.PacketsReceived, &b.PacketsReceived)
|
||||
}
|
||||
|
||||
// LINT.ThenChange(../../tcpip.go:IGMPStats)
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
// 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.
|
||||
|
||||
// Code generated by "stringer -type DHCPv6ConfigurationFromNDPRA"; DO NOT EDIT.
|
||||
|
||||
package ipv6
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[DHCPv6NoConfiguration-1]
|
||||
_ = x[DHCPv6ManagedAddress-2]
|
||||
_ = x[DHCPv6OtherConfigurations-3]
|
||||
}
|
||||
|
||||
const _DHCPv6ConfigurationFromNDPRA_name = "DHCPv6NoConfigurationDHCPv6ManagedAddressDHCPv6OtherConfigurations"
|
||||
|
||||
var _DHCPv6ConfigurationFromNDPRA_index = [...]uint8{0, 21, 41, 66}
|
||||
|
||||
func (i DHCPv6ConfigurationFromNDPRA) String() string {
|
||||
i -= 1
|
||||
if i < 0 || i >= DHCPv6ConfigurationFromNDPRA(len(_DHCPv6ConfigurationFromNDPRA_index)-1) {
|
||||
return "DHCPv6ConfigurationFromNDPRA(" + strconv.FormatInt(int64(i+1), 10) + ")"
|
||||
}
|
||||
return _DHCPv6ConfigurationFromNDPRA_name[_DHCPv6ConfigurationFromNDPRA_index[i]:_DHCPv6ConfigurationFromNDPRA_index[i+1]]
|
||||
}
|
||||
1184
pkg/tcpip/network/ipv6/icmp.go
Normal file
1184
pkg/tcpip/network/ipv6/icmp.go
Normal file
File diff suppressed because it is too large
Load diff
2875
pkg/tcpip/network/ipv6/ipv6.go
Normal file
2875
pkg/tcpip/network/ipv6/ipv6.go
Normal file
File diff suppressed because it is too large
Load diff
14
pkg/tcpip/network/ipv6/ipv6_export.go
Normal file
14
pkg/tcpip/network/ipv6/ipv6_export.go
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package ipv6
|
||||
|
||||
import (
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
type ExportedEndpoint interface {
|
||||
WritePacketDirect(r *stack.Route, pkt *stack.PacketBuffer) tcpip.Error
|
||||
}
|
||||
|
||||
func (e *endpoint) WritePacketDirect(r *stack.Route, pkt *stack.PacketBuffer) tcpip.Error {
|
||||
return e.writePacket(r, pkt, pkt.TransportProtocolNumber, true)
|
||||
}
|
||||
1049
pkg/tcpip/network/ipv6/ipv6_state_autogen.go
Normal file
1049
pkg/tcpip/network/ipv6/ipv6_state_autogen.go
Normal file
File diff suppressed because it is too large
Load diff
478
pkg/tcpip/network/ipv6/mld.go
Normal file
478
pkg/tcpip/network/ipv6/mld.go
Normal file
|
|
@ -0,0 +1,478 @@
|
|||
// 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.
|
||||
|
||||
package ipv6
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/gvisor/pkg/buffer"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/header"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/network/internal/ip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
const (
|
||||
// UnsolicitedReportIntervalMax is the maximum delay between sending
|
||||
// unsolicited MLD reports.
|
||||
//
|
||||
// Obtained from RFC 2710 Section 7.10.
|
||||
UnsolicitedReportIntervalMax = 10 * time.Second
|
||||
)
|
||||
|
||||
// MLDVersion is the forced version of MLD.
|
||||
type MLDVersion int
|
||||
|
||||
const (
|
||||
_ MLDVersion = iota
|
||||
// MLDVersion1 indicates MLDv1.
|
||||
MLDVersion1
|
||||
// MLDVersion2 indicates MLDv2. Note that MLD may still fallback to V1
|
||||
// compatibility mode as required by MLDv2.
|
||||
MLDVersion2
|
||||
)
|
||||
|
||||
// MLDEndpoint is a network endpoint that supports MLD.
|
||||
type MLDEndpoint interface {
|
||||
// SetMLDVersions sets the MLD version.
|
||||
//
|
||||
// Returns the previous MLD version.
|
||||
SetMLDVersion(MLDVersion) MLDVersion
|
||||
|
||||
// GetMLDVersion returns the MLD version.
|
||||
GetMLDVersion() MLDVersion
|
||||
}
|
||||
|
||||
// MLDOptions holds options for MLD.
|
||||
//
|
||||
// +stateify savable
|
||||
type MLDOptions struct {
|
||||
// Enabled indicates whether MLD will be performed.
|
||||
//
|
||||
// When enabled, MLD may transmit MLD report and done messages when
|
||||
// joining and leaving multicast groups respectively, and handle incoming
|
||||
// MLD packets.
|
||||
//
|
||||
// This field is ignored and is always assumed to be false for interfaces
|
||||
// without neighbouring nodes (e.g. loopback).
|
||||
Enabled bool
|
||||
}
|
||||
|
||||
var _ ip.MulticastGroupProtocol = (*mldState)(nil)
|
||||
|
||||
// mldState is the per-interface MLD state.
|
||||
//
|
||||
// mldState.init MUST be called to initialize the MLD state.
|
||||
//
|
||||
// +stateify savable
|
||||
type mldState struct {
|
||||
// The IPv6 endpoint this mldState is for.
|
||||
ep *endpoint
|
||||
|
||||
genericMulticastProtocol ip.GenericMulticastProtocolState
|
||||
}
|
||||
|
||||
// Enabled implements ip.MulticastGroupProtocol.
|
||||
func (mld *mldState) Enabled() bool {
|
||||
// No need to perform MLD on loopback interfaces since they don't have
|
||||
// neighbouring nodes.
|
||||
return mld.ep.protocol.options.MLD.Enabled && !mld.ep.nic.IsLoopback() && mld.ep.Enabled()
|
||||
}
|
||||
|
||||
// SendReport implements ip.MulticastGroupProtocol.
|
||||
//
|
||||
// Precondition: mld.ep.mu must be read locked.
|
||||
func (mld *mldState) SendReport(groupAddress tcpip.Address) (bool, tcpip.Error) {
|
||||
return mld.writePacket(groupAddress, groupAddress, header.ICMPv6MulticastListenerReport)
|
||||
}
|
||||
|
||||
// SendLeave implements ip.MulticastGroupProtocol.
|
||||
//
|
||||
// Precondition: mld.ep.mu must be read locked.
|
||||
func (mld *mldState) SendLeave(groupAddress tcpip.Address) tcpip.Error {
|
||||
_, err := mld.writePacket(header.IPv6AllRoutersLinkLocalMulticastAddress, groupAddress, header.ICMPv6MulticastListenerDone)
|
||||
return err
|
||||
}
|
||||
|
||||
// ShouldPerformProtocol implements ip.MulticastGroupProtocol.
|
||||
func (mld *mldState) ShouldPerformProtocol(groupAddress tcpip.Address) bool {
|
||||
// As per RFC 2710 section 5 page 10,
|
||||
//
|
||||
// The link-scope all-nodes address (FF02::1) is handled as a special
|
||||
// case. The node starts in Idle Listener state for that address on
|
||||
// every interface, never transitions to another state, and never sends
|
||||
// a Report or Done for that address.
|
||||
//
|
||||
// MLD messages are never sent for multicast addresses whose scope is 0
|
||||
// (reserved) or 1 (node-local).
|
||||
if groupAddress == header.IPv6AllNodesMulticastAddress {
|
||||
return false
|
||||
}
|
||||
|
||||
scope := header.V6MulticastScope(groupAddress)
|
||||
return scope != header.IPv6Reserved0MulticastScope && scope != header.IPv6InterfaceLocalMulticastScope
|
||||
}
|
||||
|
||||
type mldv2ReportBuilder struct {
|
||||
mld *mldState
|
||||
|
||||
records []header.MLDv2ReportMulticastAddressRecordSerializer
|
||||
}
|
||||
|
||||
// AddRecord implements ip.MulticastGroupProtocolV2ReportBuilder.
|
||||
func (b *mldv2ReportBuilder) AddRecord(genericRecordType ip.MulticastGroupProtocolV2ReportRecordType, groupAddress tcpip.Address) {
|
||||
var recordType header.MLDv2ReportRecordType
|
||||
switch genericRecordType {
|
||||
case ip.MulticastGroupProtocolV2ReportRecordModeIsInclude:
|
||||
recordType = header.MLDv2ReportRecordModeIsInclude
|
||||
case ip.MulticastGroupProtocolV2ReportRecordModeIsExclude:
|
||||
recordType = header.MLDv2ReportRecordModeIsExclude
|
||||
case ip.MulticastGroupProtocolV2ReportRecordChangeToIncludeMode:
|
||||
recordType = header.MLDv2ReportRecordChangeToIncludeMode
|
||||
case ip.MulticastGroupProtocolV2ReportRecordChangeToExcludeMode:
|
||||
recordType = header.MLDv2ReportRecordChangeToExcludeMode
|
||||
case ip.MulticastGroupProtocolV2ReportRecordAllowNewSources:
|
||||
recordType = header.MLDv2ReportRecordAllowNewSources
|
||||
case ip.MulticastGroupProtocolV2ReportRecordBlockOldSources:
|
||||
recordType = header.MLDv2ReportRecordBlockOldSources
|
||||
default:
|
||||
panic(fmt.Sprintf("unrecognied genericRecordType = %d", genericRecordType))
|
||||
}
|
||||
|
||||
b.records = append(b.records, header.MLDv2ReportMulticastAddressRecordSerializer{
|
||||
RecordType: recordType,
|
||||
MulticastAddress: groupAddress,
|
||||
Sources: nil,
|
||||
})
|
||||
}
|
||||
|
||||
// Send implements ip.MulticastGroupProtocolV2ReportBuilder.
|
||||
func (b *mldv2ReportBuilder) Send() (sent bool, err tcpip.Error) {
|
||||
if len(b.records) == 0 {
|
||||
return false, err
|
||||
}
|
||||
|
||||
extensionHeaders := header.IPv6ExtHdrSerializer{
|
||||
header.IPv6SerializableHopByHopExtHdr{
|
||||
&header.IPv6RouterAlertOption{Value: header.IPv6RouterAlertMLD},
|
||||
},
|
||||
}
|
||||
mtu := int(b.mld.ep.MTU()) - extensionHeaders.Length()
|
||||
|
||||
allSentWithSpecifiedAddress := true
|
||||
var firstErr tcpip.Error
|
||||
for records := b.records; len(records) != 0; {
|
||||
spaceLeft := mtu
|
||||
maxRecords := 0
|
||||
|
||||
for ; maxRecords < len(records); maxRecords++ {
|
||||
tmp := spaceLeft - records[maxRecords].Length()
|
||||
if tmp > 0 {
|
||||
spaceLeft = tmp
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
serializer := header.MLDv2ReportSerializer{Records: records[:maxRecords]}
|
||||
records = records[maxRecords:]
|
||||
|
||||
icmpView := buffer.NewViewSize(header.ICMPv6HeaderSize + serializer.Length())
|
||||
icmp := header.ICMPv6(icmpView.AsSlice())
|
||||
serializer.SerializeInto(icmp.MessageBody())
|
||||
if sentWithSpecifiedAddress, err := b.mld.writePacketInner(
|
||||
icmpView,
|
||||
header.ICMPv6MulticastListenerV2Report,
|
||||
b.mld.ep.stats.icmp.packetsSent.multicastListenerReportV2,
|
||||
extensionHeaders,
|
||||
header.MLDv2RoutersAddress,
|
||||
); err != nil {
|
||||
if firstErr != nil {
|
||||
firstErr = nil
|
||||
}
|
||||
allSentWithSpecifiedAddress = false
|
||||
} else if !sentWithSpecifiedAddress {
|
||||
allSentWithSpecifiedAddress = false
|
||||
}
|
||||
}
|
||||
|
||||
return allSentWithSpecifiedAddress, firstErr
|
||||
}
|
||||
|
||||
// NewReportV2Builder implements ip.MulticastGroupProtocol.
|
||||
func (mld *mldState) NewReportV2Builder() ip.MulticastGroupProtocolV2ReportBuilder {
|
||||
return &mldv2ReportBuilder{mld: mld}
|
||||
}
|
||||
|
||||
// V2QueryMaxRespCodeToV2Delay implements ip.MulticastGroupProtocol.
|
||||
func (*mldState) V2QueryMaxRespCodeToV2Delay(code uint16) time.Duration {
|
||||
return header.MLDv2MaximumResponseDelay(code)
|
||||
}
|
||||
|
||||
// V2QueryMaxRespCodeToV1Delay implements ip.MulticastGroupProtocol.
|
||||
func (*mldState) V2QueryMaxRespCodeToV1Delay(code uint16) time.Duration {
|
||||
return time.Duration(code) * time.Millisecond
|
||||
}
|
||||
|
||||
// init sets up an mldState struct, and is required to be called before using
|
||||
// a new mldState.
|
||||
//
|
||||
// Must only be called once for the lifetime of mld.
|
||||
func (mld *mldState) init(ep *endpoint) {
|
||||
mld.ep = ep
|
||||
mld.genericMulticastProtocol.Init(&ep.mu.RWMutex, ip.GenericMulticastProtocolOptions{
|
||||
Rand: ep.protocol.stack.InsecureRNG(),
|
||||
Clock: ep.protocol.stack.Clock(),
|
||||
Protocol: mld,
|
||||
MaxUnsolicitedReportDelay: UnsolicitedReportIntervalMax,
|
||||
})
|
||||
}
|
||||
|
||||
// handleMulticastListenerQuery handles a query message.
|
||||
//
|
||||
// Precondition: mld.ep.mu must be locked.
|
||||
func (mld *mldState) handleMulticastListenerQuery(mldHdr header.MLD) {
|
||||
mld.genericMulticastProtocol.HandleQueryLocked(mldHdr.MulticastAddress(), mldHdr.MaximumResponseDelay())
|
||||
}
|
||||
|
||||
// handleMulticastListenerQueryV2 handles a V2 query message.
|
||||
//
|
||||
// Precondition: mld.ep.mu must be locked.
|
||||
func (mld *mldState) handleMulticastListenerQueryV2(mldHdr header.MLDv2Query) {
|
||||
sources, ok := mldHdr.Sources()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
mld.genericMulticastProtocol.HandleQueryV2Locked(
|
||||
mldHdr.MulticastAddress(),
|
||||
mldHdr.MaximumResponseCode(),
|
||||
sources,
|
||||
mldHdr.QuerierRobustnessVariable(),
|
||||
mldHdr.QuerierQueryInterval(),
|
||||
)
|
||||
}
|
||||
|
||||
// handleMulticastListenerReport handles a report message.
|
||||
//
|
||||
// Precondition: mld.ep.mu must be locked.
|
||||
func (mld *mldState) handleMulticastListenerReport(mldHdr header.MLD) {
|
||||
mld.genericMulticastProtocol.HandleReportLocked(mldHdr.MulticastAddress())
|
||||
}
|
||||
|
||||
// joinGroup handles joining a new group and sending and scheduling the required
|
||||
// messages.
|
||||
//
|
||||
// If the group is already joined, returns *tcpip.ErrDuplicateAddress.
|
||||
//
|
||||
// Precondition: mld.ep.mu must be locked.
|
||||
func (mld *mldState) joinGroup(groupAddress tcpip.Address) {
|
||||
mld.genericMulticastProtocol.JoinGroupLocked(groupAddress)
|
||||
}
|
||||
|
||||
// isInGroup returns true if the specified group has been joined locally.
|
||||
//
|
||||
// Precondition: mld.ep.mu must be read locked.
|
||||
func (mld *mldState) isInGroup(groupAddress tcpip.Address) bool {
|
||||
return mld.genericMulticastProtocol.IsLocallyJoinedRLocked(groupAddress)
|
||||
}
|
||||
|
||||
// leaveGroup handles removing the group from the membership map, cancels any
|
||||
// delay timers associated with that group, and sends the Done message, if
|
||||
// required.
|
||||
//
|
||||
// Precondition: mld.ep.mu must be locked.
|
||||
func (mld *mldState) leaveGroup(groupAddress tcpip.Address) tcpip.Error {
|
||||
// LeaveGroup returns false only if the group was not joined.
|
||||
if mld.genericMulticastProtocol.LeaveGroupLocked(groupAddress) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &tcpip.ErrBadLocalAddress{}
|
||||
}
|
||||
|
||||
// softLeaveAll leaves all groups from the perspective of MLD, but remains
|
||||
// joined locally.
|
||||
//
|
||||
// Precondition: mld.ep.mu must be locked.
|
||||
func (mld *mldState) softLeaveAll() {
|
||||
mld.genericMulticastProtocol.MakeAllNonMemberLocked()
|
||||
}
|
||||
|
||||
// initializeAll attempts to initialize the MLD state for each group that has
|
||||
// been joined locally.
|
||||
//
|
||||
// Precondition: mld.ep.mu must be locked.
|
||||
func (mld *mldState) initializeAll() {
|
||||
mld.genericMulticastProtocol.InitializeGroupsLocked()
|
||||
}
|
||||
|
||||
// sendQueuedReports attempts to send any reports that are queued for sending.
|
||||
//
|
||||
// Precondition: mld.ep.mu must be locked.
|
||||
func (mld *mldState) sendQueuedReports() {
|
||||
mld.genericMulticastProtocol.SendQueuedReportsLocked()
|
||||
}
|
||||
|
||||
// setVersion sets the MLD version.
|
||||
//
|
||||
// Precondition: mld.ep.mu must be locked.
|
||||
func (mld *mldState) setVersion(v MLDVersion) MLDVersion {
|
||||
var prev bool
|
||||
switch v {
|
||||
case MLDVersion2:
|
||||
prev = mld.genericMulticastProtocol.SetV1ModeLocked(false)
|
||||
case MLDVersion1:
|
||||
prev = mld.genericMulticastProtocol.SetV1ModeLocked(true)
|
||||
default:
|
||||
panic(fmt.Sprintf("unrecognized version = %d", v))
|
||||
}
|
||||
|
||||
return toMLDVersion(prev)
|
||||
}
|
||||
|
||||
func toMLDVersion(v1Generic bool) MLDVersion {
|
||||
if v1Generic {
|
||||
return MLDVersion1
|
||||
}
|
||||
return MLDVersion2
|
||||
}
|
||||
|
||||
// getVersion returns the MLD version.
|
||||
//
|
||||
// Precondition: mld.ep.mu must be read locked.
|
||||
func (mld *mldState) getVersion() MLDVersion {
|
||||
return toMLDVersion(mld.genericMulticastProtocol.GetV1ModeLocked())
|
||||
}
|
||||
|
||||
// writePacket assembles and sends an MLD packet.
|
||||
//
|
||||
// Precondition: mld.ep.mu must be read locked.
|
||||
func (mld *mldState) writePacket(destAddress, groupAddress tcpip.Address, mldType header.ICMPv6Type) (bool, tcpip.Error) {
|
||||
sentStats := mld.ep.stats.icmp.packetsSent
|
||||
var mldStat tcpip.MultiCounterStat
|
||||
switch mldType {
|
||||
case header.ICMPv6MulticastListenerReport:
|
||||
mldStat = sentStats.multicastListenerReport
|
||||
case header.ICMPv6MulticastListenerDone:
|
||||
mldStat = sentStats.multicastListenerDone
|
||||
default:
|
||||
panic(fmt.Sprintf("unrecognized mld type = %d", mldType))
|
||||
}
|
||||
|
||||
icmpView := buffer.NewViewSize(header.ICMPv6HeaderSize + header.MLDMinimumSize)
|
||||
|
||||
icmp := header.ICMPv6(icmpView.AsSlice())
|
||||
header.MLD(icmp.MessageBody()).SetMulticastAddress(groupAddress)
|
||||
extensionHeaders := header.IPv6ExtHdrSerializer{
|
||||
header.IPv6SerializableHopByHopExtHdr{
|
||||
&header.IPv6RouterAlertOption{Value: header.IPv6RouterAlertMLD},
|
||||
},
|
||||
}
|
||||
|
||||
return mld.writePacketInner(
|
||||
icmpView,
|
||||
mldType,
|
||||
mldStat,
|
||||
extensionHeaders,
|
||||
destAddress,
|
||||
)
|
||||
}
|
||||
|
||||
func (mld *mldState) writePacketInner(buf *buffer.View, mldType header.ICMPv6Type, reportStat tcpip.MultiCounterStat, extensionHeaders header.IPv6ExtHdrSerializer, destAddress tcpip.Address) (bool, tcpip.Error) {
|
||||
icmp := header.ICMPv6(buf.AsSlice())
|
||||
icmp.SetType(mldType)
|
||||
|
||||
// As per RFC 2710 section 3,
|
||||
//
|
||||
// All MLD messages described in this document are sent with a link-local
|
||||
// IPv6 Source Address, an IPv6 Hop Limit of 1, and an IPv6 Router Alert
|
||||
// option in a Hop-by-Hop Options header.
|
||||
//
|
||||
// However, this would cause problems with Duplicate Address Detection with
|
||||
// the first address as MLD snooping switches may not send multicast traffic
|
||||
// that DAD depends on to the node performing DAD without the MLD report, as
|
||||
// documented in RFC 4816:
|
||||
//
|
||||
// Note that when a node joins a multicast address, it typically sends a
|
||||
// Multicast Listener Discovery (MLD) report message [RFC2710] [RFC3810]
|
||||
// for the multicast address. In the case of Duplicate Address
|
||||
// Detection, the MLD report message is required in order to inform MLD-
|
||||
// snooping switches, rather than routers, to forward multicast packets.
|
||||
// In the above description, the delay for joining the multicast address
|
||||
// thus means delaying transmission of the corresponding MLD report
|
||||
// message. Since the MLD specifications do not request a random delay
|
||||
// to avoid race conditions, just delaying Neighbor Solicitation would
|
||||
// cause congestion by the MLD report messages. The congestion would
|
||||
// then prevent the MLD-snooping switches from working correctly and, as
|
||||
// a result, prevent Duplicate Address Detection from working. The
|
||||
// requirement to include the delay for the MLD report in this case
|
||||
// avoids this scenario. [RFC3590] also talks about some interaction
|
||||
// issues between Duplicate Address Detection and MLD, and specifies
|
||||
// which source address should be used for the MLD report in this case.
|
||||
//
|
||||
// As per RFC 3590 section 4, we should still send out MLD reports with an
|
||||
// unspecified source address if we do not have an assigned link-local
|
||||
// address to use as the source address to ensure DAD works as expected on
|
||||
// networks with MLD snooping switches:
|
||||
//
|
||||
// MLD Report and Done messages are sent with a link-local address as
|
||||
// the IPv6 source address, if a valid address is available on the
|
||||
// interface. If a valid link-local address is not available (e.g., one
|
||||
// has not been configured), the message is sent with the unspecified
|
||||
// address (::) as the IPv6 source address.
|
||||
//
|
||||
// Once a valid link-local address is available, a node SHOULD generate
|
||||
// new MLD Report messages for all multicast addresses joined on the
|
||||
// interface.
|
||||
//
|
||||
// Routers receiving an MLD Report or Done message with the unspecified
|
||||
// address as the IPv6 source address MUST silently discard the packet
|
||||
// without taking any action on the packets contents.
|
||||
//
|
||||
// Snooping switches MUST manage multicast forwarding state based on MLD
|
||||
// Report and Done messages sent with the unspecified address as the
|
||||
// IPv6 source address.
|
||||
localAddress := mld.ep.getLinkLocalAddressRLocked()
|
||||
if localAddress.BitLen() == 0 {
|
||||
localAddress = header.IPv6Any
|
||||
}
|
||||
|
||||
icmp.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
|
||||
Header: icmp,
|
||||
Src: localAddress,
|
||||
Dst: destAddress,
|
||||
}))
|
||||
|
||||
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
ReserveHeaderBytes: int(mld.ep.MaxHeaderLength()) + extensionHeaders.Length(),
|
||||
Payload: buffer.MakeWithView(buf),
|
||||
})
|
||||
defer pkt.DecRef()
|
||||
|
||||
if err := addIPHeader(localAddress, destAddress, pkt, stack.NetworkHeaderParams{
|
||||
Protocol: header.ICMPv6ProtocolNumber,
|
||||
TTL: header.MLDHopLimit,
|
||||
}, extensionHeaders); err != nil {
|
||||
panic(fmt.Sprintf("failed to add IP header: %s", err))
|
||||
}
|
||||
if err := mld.ep.nic.WritePacketToRemote(header.EthernetAddressFromMulticastIPv6Address(destAddress), pkt); err != nil {
|
||||
mld.ep.stats.icmp.packetsSent.dropped.Increment()
|
||||
return false, err
|
||||
}
|
||||
reportStat.Increment()
|
||||
return localAddress != header.IPv6Any, nil
|
||||
}
|
||||
2033
pkg/tcpip/network/ipv6/ndp.go
Normal file
2033
pkg/tcpip/network/ipv6/ndp.go
Normal file
File diff suppressed because it is too large
Load diff
145
pkg/tcpip/network/ipv6/stats.go
Normal file
145
pkg/tcpip/network/ipv6/stats.go
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
// 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.
|
||||
|
||||
package ipv6
|
||||
|
||||
import (
|
||||
"github.com/sagernet/gvisor/pkg/tcpip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/network/internal/ip"
|
||||
"github.com/sagernet/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
var _ stack.IPNetworkEndpointStats = (*Stats)(nil)
|
||||
|
||||
// Stats holds statistics related to the IPv6 protocol family.
|
||||
//
|
||||
// +stateify savable
|
||||
type Stats struct {
|
||||
// IP holds IPv6 statistics.
|
||||
IP tcpip.IPStats
|
||||
|
||||
// ICMP holds ICMPv6 statistics.
|
||||
ICMP tcpip.ICMPv6Stats
|
||||
|
||||
// UnhandledRouterAdvertisements is the number of Router Advertisements that
|
||||
// were observed but not handled.
|
||||
UnhandledRouterAdvertisements *tcpip.StatCounter
|
||||
}
|
||||
|
||||
// IsNetworkEndpointStats implements stack.NetworkEndpointStats.
|
||||
func (*Stats) IsNetworkEndpointStats() {}
|
||||
|
||||
// IPStats implements stack.IPNetworkEndointStats
|
||||
func (s *Stats) IPStats() *tcpip.IPStats {
|
||||
return &s.IP
|
||||
}
|
||||
|
||||
// +stateify savable
|
||||
type sharedStats struct {
|
||||
localStats Stats
|
||||
ip ip.MultiCounterIPStats
|
||||
icmp multiCounterICMPv6Stats
|
||||
}
|
||||
|
||||
// LINT.IfChange(multiCounterICMPv6PacketStats)
|
||||
|
||||
// +stateify savable
|
||||
type multiCounterICMPv6PacketStats struct {
|
||||
echoRequest tcpip.MultiCounterStat
|
||||
echoReply tcpip.MultiCounterStat
|
||||
dstUnreachable tcpip.MultiCounterStat
|
||||
packetTooBig tcpip.MultiCounterStat
|
||||
timeExceeded tcpip.MultiCounterStat
|
||||
paramProblem tcpip.MultiCounterStat
|
||||
routerSolicit tcpip.MultiCounterStat
|
||||
routerAdvert tcpip.MultiCounterStat
|
||||
neighborSolicit tcpip.MultiCounterStat
|
||||
neighborAdvert tcpip.MultiCounterStat
|
||||
redirectMsg tcpip.MultiCounterStat
|
||||
multicastListenerQuery tcpip.MultiCounterStat
|
||||
multicastListenerReport tcpip.MultiCounterStat
|
||||
multicastListenerReportV2 tcpip.MultiCounterStat
|
||||
multicastListenerDone tcpip.MultiCounterStat
|
||||
}
|
||||
|
||||
func (m *multiCounterICMPv6PacketStats) init(a, b *tcpip.ICMPv6PacketStats) {
|
||||
m.echoRequest.Init(a.EchoRequest, b.EchoRequest)
|
||||
m.echoReply.Init(a.EchoReply, b.EchoReply)
|
||||
m.dstUnreachable.Init(a.DstUnreachable, b.DstUnreachable)
|
||||
m.packetTooBig.Init(a.PacketTooBig, b.PacketTooBig)
|
||||
m.timeExceeded.Init(a.TimeExceeded, b.TimeExceeded)
|
||||
m.paramProblem.Init(a.ParamProblem, b.ParamProblem)
|
||||
m.routerSolicit.Init(a.RouterSolicit, b.RouterSolicit)
|
||||
m.routerAdvert.Init(a.RouterAdvert, b.RouterAdvert)
|
||||
m.neighborSolicit.Init(a.NeighborSolicit, b.NeighborSolicit)
|
||||
m.neighborAdvert.Init(a.NeighborAdvert, b.NeighborAdvert)
|
||||
m.redirectMsg.Init(a.RedirectMsg, b.RedirectMsg)
|
||||
m.multicastListenerQuery.Init(a.MulticastListenerQuery, b.MulticastListenerQuery)
|
||||
m.multicastListenerReport.Init(a.MulticastListenerReport, b.MulticastListenerReport)
|
||||
m.multicastListenerReportV2.Init(a.MulticastListenerReportV2, b.MulticastListenerReportV2)
|
||||
m.multicastListenerDone.Init(a.MulticastListenerDone, b.MulticastListenerDone)
|
||||
}
|
||||
|
||||
// LINT.ThenChange(../../tcpip.go:ICMPv6PacketStats)
|
||||
|
||||
// LINT.IfChange(multiCounterICMPv6SentPacketStats)
|
||||
|
||||
// +stateify savable
|
||||
type multiCounterICMPv6SentPacketStats struct {
|
||||
multiCounterICMPv6PacketStats
|
||||
dropped tcpip.MultiCounterStat
|
||||
rateLimited tcpip.MultiCounterStat
|
||||
}
|
||||
|
||||
func (m *multiCounterICMPv6SentPacketStats) init(a, b *tcpip.ICMPv6SentPacketStats) {
|
||||
m.multiCounterICMPv6PacketStats.init(&a.ICMPv6PacketStats, &b.ICMPv6PacketStats)
|
||||
m.dropped.Init(a.Dropped, b.Dropped)
|
||||
m.rateLimited.Init(a.RateLimited, b.RateLimited)
|
||||
}
|
||||
|
||||
// LINT.ThenChange(../../tcpip.go:ICMPv6SentPacketStats)
|
||||
|
||||
// LINT.IfChange(multiCounterICMPv6ReceivedPacketStats)
|
||||
|
||||
// +stateify savable
|
||||
type multiCounterICMPv6ReceivedPacketStats struct {
|
||||
multiCounterICMPv6PacketStats
|
||||
unrecognized tcpip.MultiCounterStat
|
||||
invalid tcpip.MultiCounterStat
|
||||
routerOnlyPacketsDroppedByHost tcpip.MultiCounterStat
|
||||
}
|
||||
|
||||
func (m *multiCounterICMPv6ReceivedPacketStats) init(a, b *tcpip.ICMPv6ReceivedPacketStats) {
|
||||
m.multiCounterICMPv6PacketStats.init(&a.ICMPv6PacketStats, &b.ICMPv6PacketStats)
|
||||
m.unrecognized.Init(a.Unrecognized, b.Unrecognized)
|
||||
m.invalid.Init(a.Invalid, b.Invalid)
|
||||
m.routerOnlyPacketsDroppedByHost.Init(a.RouterOnlyPacketsDroppedByHost, b.RouterOnlyPacketsDroppedByHost)
|
||||
}
|
||||
|
||||
// LINT.ThenChange(../../tcpip.go:ICMPv6ReceivedPacketStats)
|
||||
|
||||
// LINT.IfChange(multiCounterICMPv6Stats)
|
||||
|
||||
// +stateify savable
|
||||
type multiCounterICMPv6Stats struct {
|
||||
packetsSent multiCounterICMPv6SentPacketStats
|
||||
packetsReceived multiCounterICMPv6ReceivedPacketStats
|
||||
}
|
||||
|
||||
func (m *multiCounterICMPv6Stats) init(a, b *tcpip.ICMPv6Stats) {
|
||||
m.packetsSent.init(&a.PacketsSent, &b.PacketsSent)
|
||||
m.packetsReceived.init(&a.PacketsReceived, &b.PacketsReceived)
|
||||
}
|
||||
|
||||
// LINT.ThenChange(../../tcpip.go:ICMPv6Stats)
|
||||
Loading…
Add table
Add a link
Reference in a new issue