mirror of
https://github.com/BishopFox/sliver.git
synced 2026-11-07 17:08:13 -10:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
211cd7750a | ||
|
|
93225d07a4 | ||
|
|
a46257c2c4 | ||
|
|
2f0b953210 |
No files matched your search
@@ -57,7 +57,7 @@ require (
|
||||
gorm.io/driver/postgres v1.0.5
|
||||
gorm.io/driver/sqlite v1.1.3
|
||||
gorm.io/gorm v1.21.14
|
||||
inet.af/netstack v0.0.0-20210317161235-a1bf4e56ef22
|
||||
inet.af/netstack v0.0.0-20220214151720-7585b01ddccf
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -65,6 +65,7 @@ require (
|
||||
github.com/shirou/gopsutil/v3 v3.21.10
|
||||
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1
|
||||
golang.zx2c4.com/go118/netip v0.0.0-20211111135330-a4a02eeacf9d
|
||||
modernc.org/sqlite v1.14.3
|
||||
)
|
||||
|
||||
|
||||
@@ -595,7 +595,6 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210217105451-b926d437f341/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210309040221-94ec62e08169/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210316164454-77fc1eacc6aa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -655,6 +654,8 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.zx2c4.com/go118/netip v0.0.0-20211111135330-a4a02eeacf9d h1:9+v0G0naRhLPOJEeJOL6NuXTtAHHwmkyZlgQJ0XcQ8I=
|
||||
golang.zx2c4.com/go118/netip v0.0.0-20211111135330-a4a02eeacf9d/go.mod h1:5yyfuiqVIJ7t+3MqrpTQ+QqRkMWiESiyDvPNvKYCecg=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20210311162910-5f0c8b942d93 h1:nb+w1m5cgug4cfeqcWJx4lcj8cYA+wWAz7aGI96Wp/Q=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20210311162910-5f0c8b942d93/go.mod h1:a057zjmoc00UN7gVkaJt2sXVK523kMJcogDTEvPIasg=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200609130330-bd2cb7843e1b h1:l4mBVCYinjzZuR5DtxHuBD6wyd4348TGiavJ5vLrhEc=
|
||||
@@ -744,8 +745,8 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
inet.af/netstack v0.0.0-20210317161235-a1bf4e56ef22 h1:DNtszwGa6w76qlIr+PbPEnlBJdiRV8SaxeigOy0q1gg=
|
||||
inet.af/netstack v0.0.0-20210317161235-a1bf4e56ef22/go.mod h1:GVx+5OZtbG4TVOW5ilmyRZAZXr1cNwfqUEkTOtWK0PM=
|
||||
inet.af/netstack v0.0.0-20220214151720-7585b01ddccf h1:kwr6UZ4fMnzVJfpnjIl4ej23vfByfAUVLaiIr69326s=
|
||||
inet.af/netstack v0.0.0-20220214151720-7585b01ddccf/go.mod h1:KOJdAzQzMLKzwFEdOOnrnSrLIhaFVB+NQoME/e5wllA=
|
||||
lukechampine.com/uint128 v1.1.1 h1:pnxCASz787iMf+02ssImqk6OLt+Z5QHMoZyUXR4z6JU=
|
||||
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||
modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
|
||||
+329
-89
@@ -15,10 +15,12 @@ import (
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.zx2c4.com/go118/netip"
|
||||
"golang.zx2c4.com/wireguard/tun"
|
||||
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
@@ -29,8 +31,10 @@ import (
|
||||
"inet.af/netstack/tcpip/network/ipv4"
|
||||
"inet.af/netstack/tcpip/network/ipv6"
|
||||
"inet.af/netstack/tcpip/stack"
|
||||
"inet.af/netstack/tcpip/transport/icmp"
|
||||
"inet.af/netstack/tcpip/transport/tcp"
|
||||
"inet.af/netstack/tcpip/transport/udp"
|
||||
"inet.af/netstack/waiter"
|
||||
)
|
||||
|
||||
type netTun struct {
|
||||
@@ -39,11 +43,14 @@ type netTun struct {
|
||||
events chan tun.Event
|
||||
incomingPacket chan buffer.VectorisedView
|
||||
mtu int
|
||||
dnsServers []net.IP
|
||||
dnsServers []netip.Addr
|
||||
hasV4, hasV6 bool
|
||||
}
|
||||
type endpoint netTun
|
||||
type Net netTun
|
||||
|
||||
type (
|
||||
endpoint netTun
|
||||
Net netTun
|
||||
)
|
||||
|
||||
func (e *endpoint) Attach(dispatcher stack.NetworkDispatcher) {
|
||||
e.dispatcher = dispatcher
|
||||
@@ -75,12 +82,16 @@ func (*endpoint) LinkAddress() tcpip.LinkAddress {
|
||||
|
||||
func (*endpoint) Wait() {}
|
||||
|
||||
func (e *endpoint) WritePacket(_ stack.RouteInfo, _ *stack.GSO, _ tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) tcpip.Error {
|
||||
func (e *endpoint) WritePacket(_ stack.RouteInfo, _ tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) tcpip.Error {
|
||||
e.incomingPacket <- buffer.NewVectorisedView(pkt.Size(), pkt.Views())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *endpoint) WritePackets(stack.RouteInfo, *stack.GSO, stack.PacketBufferList, tcpip.NetworkProtocolNumber) (int, tcpip.Error) {
|
||||
func (e *endpoint) WritePackets(stack.RouteInfo, stack.PacketBufferList, tcpip.NetworkProtocolNumber) (int, tcpip.Error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (e *endpoint) WriteRawPacket(*stack.PacketBuffer) tcpip.Error {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
@@ -91,10 +102,10 @@ func (*endpoint) ARPHardwareType() header.ARPHardwareType {
|
||||
func (e *endpoint) AddHeader(tcpip.LinkAddress, tcpip.LinkAddress, tcpip.NetworkProtocolNumber, *stack.PacketBuffer) {
|
||||
}
|
||||
|
||||
func CreateNetTUN(localAddresses, dnsServers []net.IP, mtu int) (tun.Device, *Net, error) {
|
||||
func CreateNetTUN(localAddresses, dnsServers []netip.Addr, mtu int) (tun.Device, *Net, error) {
|
||||
opts := stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol},
|
||||
TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol},
|
||||
TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol, icmp.NewProtocol6, icmp.NewProtocol4},
|
||||
HandleLocal: true,
|
||||
}
|
||||
dev := &netTun{
|
||||
@@ -109,33 +120,31 @@ func CreateNetTUN(localAddresses, dnsServers []net.IP, mtu int) (tun.Device, *Ne
|
||||
return nil, nil, fmt.Errorf("CreateNIC: %v", tcpipErr)
|
||||
}
|
||||
for _, ip := range localAddresses {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
tcpipErr = dev.stack.AddAddress(1, ipv4.ProtocolNumber, tcpip.Address(ip4))
|
||||
if tcpipErr != nil {
|
||||
return nil, nil, fmt.Errorf("AddAddress(%v): %v", ip4, tcpipErr)
|
||||
}
|
||||
var protoNumber tcpip.NetworkProtocolNumber
|
||||
if ip.Is4() {
|
||||
protoNumber = ipv4.ProtocolNumber
|
||||
} else if ip.Is6() {
|
||||
protoNumber = ipv6.ProtocolNumber
|
||||
}
|
||||
protoAddr := tcpip.ProtocolAddress{
|
||||
Protocol: protoNumber,
|
||||
AddressWithPrefix: tcpip.Address(ip.AsSlice()).WithPrefix(),
|
||||
}
|
||||
tcpipErr := dev.stack.AddProtocolAddress(1, protoAddr, stack.AddressProperties{})
|
||||
if tcpipErr != nil {
|
||||
return nil, nil, fmt.Errorf("AddProtocolAddress(%v): %v", ip, tcpipErr)
|
||||
}
|
||||
if ip.Is4() {
|
||||
dev.hasV4 = true
|
||||
} else {
|
||||
tcpipErr = dev.stack.AddAddress(1, ipv6.ProtocolNumber, tcpip.Address(ip))
|
||||
if tcpipErr != nil {
|
||||
return nil, nil, fmt.Errorf("AddAddress(%v): %v", ip4, tcpipErr)
|
||||
}
|
||||
} else if ip.Is6() {
|
||||
dev.hasV6 = true
|
||||
}
|
||||
}
|
||||
if dev.hasV4 {
|
||||
dev.stack.AddRoute(tcpip.Route{Destination: header.IPv4EmptySubnet, NIC: 1})
|
||||
tcpipErr = dev.stack.SetForwarding(header.IPv4ProtocolNumber, true)
|
||||
if tcpipErr != nil {
|
||||
return nil, nil, fmt.Errorf("SetForwarding(): %v", tcpipErr)
|
||||
}
|
||||
}
|
||||
if dev.hasV6 {
|
||||
dev.stack.AddRoute(tcpip.Route{Destination: header.IPv6EmptySubnet, NIC: 1})
|
||||
tcpipErr = dev.stack.SetForwarding(header.IPv6ProtocolNumber, true)
|
||||
if tcpipErr != nil {
|
||||
return nil, nil, fmt.Errorf("SetForwarding(): %v", tcpipErr)
|
||||
}
|
||||
}
|
||||
|
||||
dev.events <- tun.EventUp
|
||||
@@ -199,62 +208,286 @@ func (tun *netTun) MTU() (int, error) {
|
||||
return tun.mtu, nil
|
||||
}
|
||||
|
||||
func convertToFullAddr(ip net.IP, port int) (tcpip.FullAddress, tcpip.NetworkProtocolNumber) {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
return tcpip.FullAddress{
|
||||
NIC: 1,
|
||||
Addr: tcpip.Address(ip4),
|
||||
Port: uint16(port),
|
||||
}, ipv4.ProtocolNumber
|
||||
func convertToFullAddr(endpoint netip.AddrPort) (tcpip.FullAddress, tcpip.NetworkProtocolNumber) {
|
||||
var protoNumber tcpip.NetworkProtocolNumber
|
||||
if endpoint.Addr().Is4() {
|
||||
protoNumber = ipv4.ProtocolNumber
|
||||
} else {
|
||||
return tcpip.FullAddress{
|
||||
NIC: 1,
|
||||
Addr: tcpip.Address(ip),
|
||||
Port: uint16(port),
|
||||
}, ipv6.ProtocolNumber
|
||||
protoNumber = ipv6.ProtocolNumber
|
||||
}
|
||||
return tcpip.FullAddress{
|
||||
NIC: 1,
|
||||
Addr: tcpip.Address(endpoint.Addr().AsSlice()),
|
||||
Port: endpoint.Port(),
|
||||
}, protoNumber
|
||||
}
|
||||
|
||||
func (net *Net) DialContextTCPAddrPort(ctx context.Context, addr netip.AddrPort) (*gonet.TCPConn, error) {
|
||||
fa, pn := convertToFullAddr(addr)
|
||||
return gonet.DialContextTCP(ctx, net.stack, fa, pn)
|
||||
}
|
||||
|
||||
func (net *Net) DialContextTCP(ctx context.Context, addr *net.TCPAddr) (*gonet.TCPConn, error) {
|
||||
if addr == nil {
|
||||
panic("todo: deal with auto addr semantics for nil addr")
|
||||
return net.DialContextTCPAddrPort(ctx, netip.AddrPort{})
|
||||
}
|
||||
fa, pn := convertToFullAddr(addr.IP, addr.Port)
|
||||
return gonet.DialContextTCP(ctx, net.stack, fa, pn)
|
||||
return net.DialContextTCPAddrPort(ctx, netip.AddrPortFrom(netip.AddrFromSlice(addr.IP), uint16(addr.Port)))
|
||||
}
|
||||
|
||||
func (net *Net) DialTCPAddrPort(addr netip.AddrPort) (*gonet.TCPConn, error) {
|
||||
fa, pn := convertToFullAddr(addr)
|
||||
return gonet.DialTCP(net.stack, fa, pn)
|
||||
}
|
||||
|
||||
func (net *Net) DialTCP(addr *net.TCPAddr) (*gonet.TCPConn, error) {
|
||||
if addr == nil {
|
||||
panic("todo: deal with auto addr semantics for nil addr")
|
||||
return net.DialTCPAddrPort(netip.AddrPort{})
|
||||
}
|
||||
fa, pn := convertToFullAddr(addr.IP, addr.Port)
|
||||
return gonet.DialTCP(net.stack, fa, pn)
|
||||
return net.DialTCPAddrPort(netip.AddrPortFrom(netip.AddrFromSlice(addr.IP), uint16(addr.Port)))
|
||||
}
|
||||
|
||||
func (net *Net) ListenTCPAddrPort(addr netip.AddrPort) (*gonet.TCPListener, error) {
|
||||
fa, pn := convertToFullAddr(addr)
|
||||
return gonet.ListenTCP(net.stack, fa, pn)
|
||||
}
|
||||
|
||||
func (net *Net) ListenTCP(addr *net.TCPAddr) (*gonet.TCPListener, error) {
|
||||
if addr == nil {
|
||||
panic("todo: deal with auto addr semantics for nil addr")
|
||||
return net.ListenTCPAddrPort(netip.AddrPort{})
|
||||
}
|
||||
fa, pn := convertToFullAddr(addr.IP, addr.Port)
|
||||
return gonet.ListenTCP(net.stack, fa, pn)
|
||||
return net.ListenTCPAddrPort(netip.AddrPortFrom(netip.AddrFromSlice(addr.IP), uint16(addr.Port)))
|
||||
}
|
||||
|
||||
func (net *Net) DialUDP(laddr, raddr *net.UDPAddr) (*gonet.UDPConn, error) {
|
||||
func (net *Net) DialUDPAddrPort(laddr, raddr netip.AddrPort) (*gonet.UDPConn, error) {
|
||||
var lfa, rfa *tcpip.FullAddress
|
||||
var pn tcpip.NetworkProtocolNumber
|
||||
if laddr != nil {
|
||||
if laddr.IsValid() || laddr.Port() > 0 {
|
||||
var addr tcpip.FullAddress
|
||||
addr, pn = convertToFullAddr(laddr.IP, laddr.Port)
|
||||
addr, pn = convertToFullAddr(laddr)
|
||||
lfa = &addr
|
||||
}
|
||||
if raddr != nil {
|
||||
if raddr.IsValid() || raddr.Port() > 0 {
|
||||
var addr tcpip.FullAddress
|
||||
addr, pn = convertToFullAddr(raddr.IP, raddr.Port)
|
||||
addr, pn = convertToFullAddr(raddr)
|
||||
rfa = &addr
|
||||
}
|
||||
return gonet.DialUDP(net.stack, lfa, rfa, pn)
|
||||
}
|
||||
|
||||
func (net *Net) ListenUDPAddrPort(laddr netip.AddrPort) (*gonet.UDPConn, error) {
|
||||
return net.DialUDPAddrPort(laddr, netip.AddrPort{})
|
||||
}
|
||||
|
||||
func (net *Net) DialUDP(laddr, raddr *net.UDPAddr) (*gonet.UDPConn, error) {
|
||||
var la, ra netip.AddrPort
|
||||
if laddr != nil {
|
||||
la = netip.AddrPortFrom(netip.AddrFromSlice(laddr.IP), uint16(laddr.Port))
|
||||
}
|
||||
if raddr != nil {
|
||||
ra = netip.AddrPortFrom(netip.AddrFromSlice(raddr.IP), uint16(raddr.Port))
|
||||
}
|
||||
return net.DialUDPAddrPort(la, ra)
|
||||
}
|
||||
|
||||
func (net *Net) ListenUDP(laddr *net.UDPAddr) (*gonet.UDPConn, error) {
|
||||
return net.DialUDP(laddr, nil)
|
||||
}
|
||||
|
||||
type PingConn struct {
|
||||
laddr PingAddr
|
||||
raddr PingAddr
|
||||
wq waiter.Queue
|
||||
ep tcpip.Endpoint
|
||||
deadline *time.Timer
|
||||
}
|
||||
|
||||
type PingAddr struct{ addr netip.Addr }
|
||||
|
||||
func (ia PingAddr) String() string {
|
||||
return ia.addr.String()
|
||||
}
|
||||
|
||||
func (ia PingAddr) Network() string {
|
||||
if ia.addr.Is4() {
|
||||
return "ping4"
|
||||
} else if ia.addr.Is6() {
|
||||
return "ping6"
|
||||
}
|
||||
return "ping"
|
||||
}
|
||||
|
||||
func (ia PingAddr) Addr() netip.Addr {
|
||||
return ia.addr
|
||||
}
|
||||
|
||||
func PingAddrFromAddr(addr netip.Addr) *PingAddr {
|
||||
return &PingAddr{addr}
|
||||
}
|
||||
|
||||
func (net *Net) DialPingAddr(laddr, raddr netip.Addr) (*PingConn, error) {
|
||||
if !laddr.IsValid() && !raddr.IsValid() {
|
||||
return nil, errors.New("ping dial: invalid address")
|
||||
}
|
||||
v6 := laddr.Is6() || raddr.Is6()
|
||||
bind := laddr.IsValid()
|
||||
if !bind {
|
||||
if v6 {
|
||||
laddr = netip.IPv6Unspecified()
|
||||
} else {
|
||||
laddr = netip.IPv4Unspecified()
|
||||
}
|
||||
}
|
||||
|
||||
tn := icmp.ProtocolNumber4
|
||||
pn := ipv4.ProtocolNumber
|
||||
if v6 {
|
||||
tn = icmp.ProtocolNumber6
|
||||
pn = ipv6.ProtocolNumber
|
||||
}
|
||||
|
||||
pc := &PingConn{
|
||||
laddr: PingAddr{laddr},
|
||||
deadline: time.NewTimer(time.Hour << 10),
|
||||
}
|
||||
pc.deadline.Stop()
|
||||
|
||||
ep, tcpipErr := net.stack.NewEndpoint(tn, pn, &pc.wq)
|
||||
if tcpipErr != nil {
|
||||
return nil, fmt.Errorf("ping socket: endpoint: %s", tcpipErr)
|
||||
}
|
||||
pc.ep = ep
|
||||
|
||||
if bind {
|
||||
fa, _ := convertToFullAddr(netip.AddrPortFrom(laddr, 0))
|
||||
if tcpipErr = pc.ep.Bind(fa); tcpipErr != nil {
|
||||
return nil, fmt.Errorf("ping bind: %s", tcpipErr)
|
||||
}
|
||||
}
|
||||
|
||||
if raddr.IsValid() {
|
||||
pc.raddr = PingAddr{raddr}
|
||||
fa, _ := convertToFullAddr(netip.AddrPortFrom(raddr, 0))
|
||||
if tcpipErr = pc.ep.Connect(fa); tcpipErr != nil {
|
||||
return nil, fmt.Errorf("ping connect: %s", tcpipErr)
|
||||
}
|
||||
}
|
||||
|
||||
return pc, nil
|
||||
}
|
||||
|
||||
func (net *Net) ListenPingAddr(laddr netip.Addr) (*PingConn, error) {
|
||||
return net.DialPingAddr(laddr, netip.Addr{})
|
||||
}
|
||||
|
||||
func (net *Net) DialPing(laddr, raddr *PingAddr) (*PingConn, error) {
|
||||
var la, ra netip.Addr
|
||||
if laddr != nil {
|
||||
la = laddr.addr
|
||||
}
|
||||
if raddr != nil {
|
||||
ra = raddr.addr
|
||||
}
|
||||
return net.DialPingAddr(la, ra)
|
||||
}
|
||||
|
||||
func (net *Net) ListenPing(laddr *PingAddr) (*PingConn, error) {
|
||||
var la netip.Addr
|
||||
if laddr != nil {
|
||||
la = laddr.addr
|
||||
}
|
||||
return net.ListenPingAddr(la)
|
||||
}
|
||||
|
||||
func (pc *PingConn) LocalAddr() net.Addr {
|
||||
return pc.laddr
|
||||
}
|
||||
|
||||
func (pc *PingConn) RemoteAddr() net.Addr {
|
||||
return pc.raddr
|
||||
}
|
||||
|
||||
func (pc *PingConn) Close() error {
|
||||
pc.deadline.Reset(0)
|
||||
pc.ep.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pc *PingConn) SetWriteDeadline(t time.Time) error {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (pc *PingConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
var na netip.Addr
|
||||
switch v := addr.(type) {
|
||||
case *PingAddr:
|
||||
na = v.addr
|
||||
case *net.IPAddr:
|
||||
na = netip.AddrFromSlice(v.IP)
|
||||
default:
|
||||
return 0, fmt.Errorf("ping write: wrong net.Addr type")
|
||||
}
|
||||
if !((na.Is4() && pc.laddr.addr.Is4()) || (na.Is6() && pc.laddr.addr.Is6())) {
|
||||
return 0, fmt.Errorf("ping write: mismatched protocols")
|
||||
}
|
||||
|
||||
buf := buffer.NewViewFromBytes(p)
|
||||
rdr := buf.Reader()
|
||||
rfa, _ := convertToFullAddr(netip.AddrPortFrom(na, 0))
|
||||
// won't block, no deadlines
|
||||
n64, tcpipErr := pc.ep.Write(&rdr, tcpip.WriteOptions{
|
||||
To: &rfa,
|
||||
})
|
||||
if tcpipErr != nil {
|
||||
return int(n64), fmt.Errorf("ping write: %s", tcpipErr)
|
||||
}
|
||||
|
||||
return int(n64), nil
|
||||
}
|
||||
|
||||
func (pc *PingConn) Write(p []byte) (n int, err error) {
|
||||
return pc.WriteTo(p, &pc.raddr)
|
||||
}
|
||||
|
||||
func (pc *PingConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
e, notifyCh := waiter.NewChannelEntry(nil)
|
||||
pc.wq.EventRegister(&e, waiter.EventIn)
|
||||
defer pc.wq.EventUnregister(&e)
|
||||
|
||||
select {
|
||||
case <-pc.deadline.C:
|
||||
return 0, nil, os.ErrDeadlineExceeded
|
||||
case <-notifyCh:
|
||||
}
|
||||
|
||||
w := tcpip.SliceWriter(p)
|
||||
|
||||
res, tcpipErr := pc.ep.Read(&w, tcpip.ReadOptions{
|
||||
NeedRemoteAddr: true,
|
||||
})
|
||||
if tcpipErr != nil {
|
||||
return 0, nil, fmt.Errorf("ping read: %s", tcpipErr)
|
||||
}
|
||||
|
||||
addr = &PingAddr{netip.AddrFromSlice([]byte(res.RemoteAddr.Addr))}
|
||||
return res.Count, addr, nil
|
||||
}
|
||||
|
||||
func (pc *PingConn) Read(p []byte) (n int, err error) {
|
||||
n, _, err = pc.ReadFrom(p)
|
||||
return
|
||||
}
|
||||
|
||||
func (pc *PingConn) SetDeadline(t time.Time) error {
|
||||
// pc.SetWriteDeadline is unimplemented
|
||||
|
||||
return pc.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (pc *PingConn) SetReadDeadline(t time.Time) error {
|
||||
pc.deadline.Reset(t.Sub(time.Now()))
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
errNoSuchHost = errors.New("no such host")
|
||||
errLameReferral = errors.New("lame referral")
|
||||
@@ -430,7 +663,7 @@ func dnsStreamRoundTrip(c net.Conn, id uint16, query dnsmessage.Question, b []by
|
||||
return p, h, nil
|
||||
}
|
||||
|
||||
func (tnet *Net) exchange(ctx context.Context, server net.IP, q dnsmessage.Question, timeout time.Duration) (dnsmessage.Parser, dnsmessage.Header, error) {
|
||||
func (tnet *Net) exchange(ctx context.Context, server netip.Addr, q dnsmessage.Question, timeout time.Duration) (dnsmessage.Parser, dnsmessage.Header, error) {
|
||||
q.Class = dnsmessage.ClassINET
|
||||
id, udpReq, tcpReq, err := newRequest(q)
|
||||
if err != nil {
|
||||
@@ -444,9 +677,9 @@ func (tnet *Net) exchange(ctx context.Context, server net.IP, q dnsmessage.Quest
|
||||
var c net.Conn
|
||||
var err error
|
||||
if useUDP {
|
||||
c, err = tnet.DialUDP(nil, &net.UDPAddr{IP: server, Port: 53})
|
||||
c, err = tnet.DialUDPAddrPort(netip.AddrPort{}, netip.AddrPortFrom(server, 53))
|
||||
} else {
|
||||
c, err = tnet.DialContextTCP(ctx, &net.TCPAddr{IP: server, Port: 53})
|
||||
c, err = tnet.DialContextTCPAddrPort(ctx, netip.AddrPortFrom(server, 53))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -597,8 +830,8 @@ func (tnet *Net) LookupContextHost(ctx context.Context, host string) ([]string,
|
||||
zlen = zidx
|
||||
}
|
||||
}
|
||||
if ip := net.ParseIP(host[:zlen]); ip != nil {
|
||||
return []string{host[:zlen]}, nil
|
||||
if ip, err := netip.ParseAddr(host[:zlen]); err == nil {
|
||||
return []string{ip.String()}, nil
|
||||
}
|
||||
|
||||
if !isDomainName(host) {
|
||||
@@ -609,7 +842,7 @@ func (tnet *Net) LookupContextHost(ctx context.Context, host string) ([]string,
|
||||
server string
|
||||
error
|
||||
}
|
||||
var addrsV4, addrsV6 []net.IP
|
||||
var addrsV4, addrsV6 []netip.Addr
|
||||
lanes := 0
|
||||
if tnet.hasV4 {
|
||||
lanes++
|
||||
@@ -664,7 +897,7 @@ func (tnet *Net) LookupContextHost(ctx context.Context, host string) ([]string,
|
||||
}
|
||||
break loop
|
||||
}
|
||||
addrsV4 = append(addrsV4, net.IP(a.A[:]))
|
||||
addrsV4 = append(addrsV4, netip.AddrFrom4(a.A))
|
||||
|
||||
case dnsmessage.TypeAAAA:
|
||||
aaaa, err := result.p.AAAAResource()
|
||||
@@ -676,7 +909,7 @@ func (tnet *Net) LookupContextHost(ctx context.Context, host string) ([]string,
|
||||
}
|
||||
break loop
|
||||
}
|
||||
addrsV6 = append(addrsV6, net.IP(aaaa.AAAA[:]))
|
||||
addrsV6 = append(addrsV6, netip.AddrFrom16(aaaa.AAAA))
|
||||
|
||||
default:
|
||||
if err := result.p.SkipAnswer(); err != nil {
|
||||
@@ -692,7 +925,7 @@ func (tnet *Net) LookupContextHost(ctx context.Context, host string) ([]string,
|
||||
}
|
||||
}
|
||||
// We don't do RFC6724. Instead just put V6 addresess first if an IPv6 address is enabled
|
||||
var addrs []net.IP
|
||||
var addrs []netip.Addr
|
||||
if tnet.hasV6 {
|
||||
addrs = append(addrsV6, addrsV4...)
|
||||
} else {
|
||||
@@ -729,44 +962,48 @@ func partialDeadline(now, deadline time.Time, addrsRemaining int) (time.Time, er
|
||||
return now.Add(timeout), nil
|
||||
}
|
||||
|
||||
var protoSplitter = regexp.MustCompile(`^(tcp|udp|ping)(4|6)?$`)
|
||||
|
||||
func (tnet *Net) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
if ctx == nil {
|
||||
panic("nil context")
|
||||
}
|
||||
var acceptV4, acceptV6, useUDP bool
|
||||
if len(network) == 3 {
|
||||
var acceptV4, acceptV6 bool
|
||||
matches := protoSplitter.FindStringSubmatch(network)
|
||||
if matches == nil {
|
||||
return nil, &net.OpError{Op: "dial", Err: net.UnknownNetworkError(network)}
|
||||
} else if len(matches[2]) == 0 {
|
||||
acceptV4 = true
|
||||
acceptV6 = true
|
||||
} else if len(network) == 4 {
|
||||
acceptV4 = network[3] == '4'
|
||||
acceptV6 = network[3] == '6'
|
||||
} else {
|
||||
acceptV4 = matches[2][0] == '4'
|
||||
acceptV6 = !acceptV4
|
||||
}
|
||||
if !acceptV4 && !acceptV6 {
|
||||
return nil, &net.OpError{Op: "dial", Err: net.UnknownNetworkError(network)}
|
||||
}
|
||||
if network[:3] == "udp" {
|
||||
useUDP = true
|
||||
} else if network[:3] != "tcp" {
|
||||
return nil, &net.OpError{Op: "dial", Err: net.UnknownNetworkError(network)}
|
||||
}
|
||||
host, sport, err := net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
return nil, &net.OpError{Op: "dial", Err: err}
|
||||
}
|
||||
port, err := strconv.Atoi(sport)
|
||||
if err != nil || port < 0 || port > 65535 {
|
||||
return nil, &net.OpError{Op: "dial", Err: errNumericPort}
|
||||
var host string
|
||||
var port int
|
||||
if matches[1] == "ping" {
|
||||
host = address
|
||||
} else {
|
||||
var sport string
|
||||
var err error
|
||||
host, sport, err = net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
return nil, &net.OpError{Op: "dial", Err: err}
|
||||
}
|
||||
port, err = strconv.Atoi(sport)
|
||||
if err != nil || port < 0 || port > 65535 {
|
||||
return nil, &net.OpError{Op: "dial", Err: errNumericPort}
|
||||
}
|
||||
}
|
||||
allAddr, err := tnet.LookupContextHost(ctx, host)
|
||||
if err != nil {
|
||||
return nil, &net.OpError{Op: "dial", Err: err}
|
||||
}
|
||||
var addrs []net.IP
|
||||
var addrs []netip.AddrPort
|
||||
for _, addr := range allAddr {
|
||||
if strings.IndexByte(addr, ':') != -1 && acceptV6 {
|
||||
addrs = append(addrs, net.ParseIP(addr))
|
||||
} else if strings.IndexByte(addr, '.') != -1 && acceptV4 {
|
||||
addrs = append(addrs, net.ParseIP(addr))
|
||||
ip, err := netip.ParseAddr(addr)
|
||||
if err == nil && ((ip.Is4() && acceptV4) || (ip.Is6() && acceptV6)) {
|
||||
addrs = append(addrs, netip.AddrPortFrom(ip, uint16(port)))
|
||||
}
|
||||
}
|
||||
if len(addrs) == 0 && len(allAddr) != 0 {
|
||||
@@ -804,10 +1041,13 @@ func (tnet *Net) DialContext(ctx context.Context, network, address string) (net.
|
||||
}
|
||||
|
||||
var c net.Conn
|
||||
if useUDP {
|
||||
c, err = tnet.DialUDP(nil, &net.UDPAddr{IP: addr, Port: port})
|
||||
} else {
|
||||
c, err = tnet.DialContextTCP(dialCtx, &net.TCPAddr{IP: addr, Port: port})
|
||||
switch matches[1] {
|
||||
case "tcp":
|
||||
c, err = tnet.DialContextTCPAddrPort(dialCtx, addr)
|
||||
case "udp":
|
||||
c, err = tnet.DialUDPAddrPort(netip.AddrPort{}, addr)
|
||||
case "ping":
|
||||
c, err = tnet.DialPingAddr(netip.Addr{}, addr.Addr())
|
||||
}
|
||||
if err == nil {
|
||||
return c, nil
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
## `net/netip` for Go < 1.18
|
||||
|
||||
This is an unmodified extraction of the `net/netip` package from Go 1.18's
|
||||
standard library, for use in Go 1.17. When Go 1.18 is released, this repository
|
||||
will be deleted.
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package bytealg
|
||||
|
||||
import "strings"
|
||||
|
||||
func IndexByteString(s string, c byte) int {
|
||||
return strings.IndexByte(s, c)
|
||||
}
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package intern lets you make smaller comparable values by boxing
|
||||
// a larger comparable value (such as a 16 byte string header) down
|
||||
// into a globally unique 8 byte pointer.
|
||||
//
|
||||
// The globally unique pointers are garbage collected with weak
|
||||
// references and finalizers. This package hides that.
|
||||
package intern
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// A Value pointer is the handle to an underlying comparable value.
|
||||
// See func Get for how Value pointers may be used.
|
||||
type Value struct {
|
||||
_ [0]func() // prevent people from accidentally using value type as comparable
|
||||
cmpVal interface{}
|
||||
// resurrected is guarded by mu (for all instances of Value).
|
||||
// It is set true whenever v is synthesized from a uintptr.
|
||||
resurrected bool
|
||||
}
|
||||
|
||||
// Get returns the comparable value passed to the Get func
|
||||
// that returned v.
|
||||
func (v *Value) Get() interface{} { return v.cmpVal }
|
||||
|
||||
// key is a key in our global value map.
|
||||
// It contains type-specialized fields to avoid allocations
|
||||
// when converting common types to empty interfaces.
|
||||
type key struct {
|
||||
s string
|
||||
cmpVal interface{}
|
||||
// isString reports whether key contains a string.
|
||||
// Without it, the zero value of key is ambiguous.
|
||||
isString bool
|
||||
}
|
||||
|
||||
// keyFor returns a key to use with cmpVal.
|
||||
func keyFor(cmpVal interface{}) key {
|
||||
if s, ok := cmpVal.(string); ok {
|
||||
return key{s: s, isString: true}
|
||||
}
|
||||
return key{cmpVal: cmpVal}
|
||||
}
|
||||
|
||||
// Value returns a *Value built from k.
|
||||
func (k key) Value() *Value {
|
||||
if k.isString {
|
||||
return &Value{cmpVal: k.s}
|
||||
}
|
||||
return &Value{cmpVal: k.cmpVal}
|
||||
}
|
||||
|
||||
var (
|
||||
// mu guards valMap, a weakref map of *Value by underlying value.
|
||||
// It also guards the resurrected field of all *Values.
|
||||
mu sync.Mutex
|
||||
valMap = map[key]uintptr{} // to uintptr(*Value)
|
||||
valSafe = safeMap() // non-nil in safe+leaky mode
|
||||
)
|
||||
|
||||
// safeMap returns a non-nil map if we're in safe-but-leaky mode,
|
||||
// as controlled by GODEBUG=intern=leaky
|
||||
func safeMap() map[key]*Value {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns a pointer representing the comparable value cmpVal.
|
||||
//
|
||||
// The returned pointer will be the same for Get(v) and Get(v2)
|
||||
// if and only if v == v2, and can be used as a map key.
|
||||
func Get(cmpVal interface{}) *Value {
|
||||
return get(keyFor(cmpVal))
|
||||
}
|
||||
|
||||
// GetByString is identical to Get, except that it is specialized for strings.
|
||||
// This avoids an allocation from putting a string into an interface{}
|
||||
// to pass as an argument to Get.
|
||||
func GetByString(s string) *Value {
|
||||
return get(key{s: s, isString: true})
|
||||
}
|
||||
|
||||
// We play unsafe games that violate Go's rules (and assume a non-moving
|
||||
// collector). So we quiet Go here.
|
||||
// See the comment below Get for more implementation details.
|
||||
//go:nocheckptr
|
||||
func get(k key) *Value {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
var v *Value
|
||||
if valSafe != nil {
|
||||
v = valSafe[k]
|
||||
} else if addr, ok := valMap[k]; ok {
|
||||
v = (*Value)(unsafe.Pointer(addr))
|
||||
v.resurrected = true
|
||||
}
|
||||
if v != nil {
|
||||
return v
|
||||
}
|
||||
v = k.Value()
|
||||
if valSafe != nil {
|
||||
valSafe[k] = v
|
||||
} else {
|
||||
// SetFinalizer before uintptr conversion (theoretical concern;
|
||||
// see https://github.com/go4org/intern/issues/13)
|
||||
runtime.SetFinalizer(v, finalize)
|
||||
valMap[k] = uintptr(unsafe.Pointer(v))
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func finalize(v *Value) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if v.resurrected {
|
||||
// We lost the race. Somebody resurrected it while we
|
||||
// were about to finalize it. Try again next round.
|
||||
v.resurrected = false
|
||||
runtime.SetFinalizer(v, finalize)
|
||||
return
|
||||
}
|
||||
delete(valMap, keyFor(v.cmpVal))
|
||||
}
|
||||
|
||||
// Interning is simple if you don't require that unused values be
|
||||
// garbage collectable. But we do require that; we don't want to be
|
||||
// DOS vector. We do this by using a uintptr to hide the pointer from
|
||||
// the garbage collector, and using a finalizer to eliminate the
|
||||
// pointer when no other code is using it.
|
||||
//
|
||||
// The obvious implementation of this is to use a
|
||||
// map[interface{}]uintptr-of-*interface{}, and set up a finalizer to
|
||||
// delete from the map. Unfortunately, this is racy. Because pointers
|
||||
// are being created in violation of Go's unsafety rules, it's
|
||||
// possible to create a pointer to a value concurrently with the GC
|
||||
// concluding that the value can be collected. There are other races
|
||||
// that break the equality invariant as well, but the use-after-free
|
||||
// will cause a runtime crash.
|
||||
//
|
||||
// To make this work, the finalizer needs to know that no references
|
||||
// have been unsafely created since the finalizer was set up. To do
|
||||
// this, values carry a "resurrected" sentinel, which gets set
|
||||
// whenever a pointer is unsafely created. If the finalizer encounters
|
||||
// the sentinel, it clears the sentinel and delays collection for one
|
||||
// additional GC cycle, by re-installing itself as finalizer. This
|
||||
// ensures that the unsafely created pointer is visible to the GC, and
|
||||
// will correctly prevent collection.
|
||||
//
|
||||
// This technique does mean that interned values that get reused take
|
||||
// at least 3 GC cycles to fully collect (1 to clear the sentinel, 1
|
||||
// to clean up the unsafe map, 1 to be actually deleted).
|
||||
//
|
||||
// @ianlancetaylor commented in
|
||||
// https://github.com/golang/go/issues/41303#issuecomment-717401656
|
||||
// that it is possible to implement weak references in terms of
|
||||
// finalizers without unsafe. Unfortunately, the approach he outlined
|
||||
// does not work here, for two reasons. First, there is no way to
|
||||
// construct a strong pointer out of a weak pointer; our map stores
|
||||
// weak pointers, but we must return strong pointers to callers.
|
||||
// Second, and more fundamentally, we must return not just _a_ strong
|
||||
// pointer to callers, but _the same_ strong pointer to callers. In
|
||||
// order to return _the same_ strong pointer to callers, we must track
|
||||
// it, which is exactly what we cannot do with strong pointers.
|
||||
//
|
||||
// See https://github.com/inetaf/netaddr/issues/53 for more
|
||||
// discussion, and https://github.com/go4org/intern/issues/2 for an
|
||||
// illustration of the subtleties at play.
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package itoa
|
||||
|
||||
import "strconv"
|
||||
|
||||
func Itoa(i int) string {
|
||||
return strconv.Itoa(i)
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Stuff that exists in std, but we can't use due to being a dependency
|
||||
// of net, for go/build deps_test policy reasons.
|
||||
|
||||
package netip
|
||||
|
||||
func stringsLastIndexByte(s string, b byte) int {
|
||||
for i := len(s) - 1; i >= 0; i-- {
|
||||
if s[i] == b {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func beUint64(b []byte) uint64 {
|
||||
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
|
||||
return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
|
||||
uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
|
||||
}
|
||||
|
||||
func bePutUint64(b []byte, v uint64) {
|
||||
_ = b[7] // early bounds check to guarantee safety of writes below
|
||||
b[0] = byte(v >> 56)
|
||||
b[1] = byte(v >> 48)
|
||||
b[2] = byte(v >> 40)
|
||||
b[3] = byte(v >> 32)
|
||||
b[4] = byte(v >> 24)
|
||||
b[5] = byte(v >> 16)
|
||||
b[6] = byte(v >> 8)
|
||||
b[7] = byte(v)
|
||||
}
|
||||
|
||||
func bePutUint32(b []byte, v uint32) {
|
||||
_ = b[3] // early bounds check to guarantee safety of writes below
|
||||
b[0] = byte(v >> 24)
|
||||
b[1] = byte(v >> 16)
|
||||
b[2] = byte(v >> 8)
|
||||
b[3] = byte(v)
|
||||
}
|
||||
|
||||
func leUint16(b []byte) uint16 {
|
||||
_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
|
||||
return uint16(b[0]) | uint16(b[1])<<8
|
||||
}
|
||||
|
||||
func lePutUint16(b []byte, v uint16) {
|
||||
_ = b[1] // early bounds check to guarantee safety of writes below
|
||||
b[0] = byte(v)
|
||||
b[1] = byte(v >> 8)
|
||||
}
|
||||
+1469
File diff suppressed because it is too large.
Load diff
+92
@@ -0,0 +1,92 @@
|
||||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package netip
|
||||
|
||||
import "math/bits"
|
||||
|
||||
// uint128 represents a uint128 using two uint64s.
|
||||
//
|
||||
// When the methods below mention a bit number, bit 0 is the most
|
||||
// significant bit (in hi) and bit 127 is the lowest (lo&1).
|
||||
type uint128 struct {
|
||||
hi uint64
|
||||
lo uint64
|
||||
}
|
||||
|
||||
// mask6 returns a uint128 bitmask with the topmost n bits of a
|
||||
// 128-bit number.
|
||||
func mask6(n int) uint128 {
|
||||
return uint128{^(^uint64(0) >> n), ^uint64(0) << (128 - n)}
|
||||
}
|
||||
|
||||
// isZero reports whether u == 0.
|
||||
//
|
||||
// It's faster than u == (uint128{}) because the compiler (as of Go
|
||||
// 1.15/1.16b1) doesn't do this trick and instead inserts a branch in
|
||||
// its eq alg's generated code.
|
||||
func (u uint128) isZero() bool { return u.hi|u.lo == 0 }
|
||||
|
||||
// and returns the bitwise AND of u and m (u&m).
|
||||
func (u uint128) and(m uint128) uint128 {
|
||||
return uint128{u.hi & m.hi, u.lo & m.lo}
|
||||
}
|
||||
|
||||
// xor returns the bitwise XOR of u and m (u^m).
|
||||
func (u uint128) xor(m uint128) uint128 {
|
||||
return uint128{u.hi ^ m.hi, u.lo ^ m.lo}
|
||||
}
|
||||
|
||||
// or returns the bitwise OR of u and m (u|m).
|
||||
func (u uint128) or(m uint128) uint128 {
|
||||
return uint128{u.hi | m.hi, u.lo | m.lo}
|
||||
}
|
||||
|
||||
// not returns the bitwise NOT of u.
|
||||
func (u uint128) not() uint128 {
|
||||
return uint128{^u.hi, ^u.lo}
|
||||
}
|
||||
|
||||
// subOne returns u - 1.
|
||||
func (u uint128) subOne() uint128 {
|
||||
lo, borrow := bits.Sub64(u.lo, 1, 0)
|
||||
return uint128{u.hi - borrow, lo}
|
||||
}
|
||||
|
||||
// addOne returns u + 1.
|
||||
func (u uint128) addOne() uint128 {
|
||||
lo, carry := bits.Add64(u.lo, 1, 0)
|
||||
return uint128{u.hi + carry, lo}
|
||||
}
|
||||
|
||||
func u64CommonPrefixLen(a, b uint64) uint8 {
|
||||
return uint8(bits.LeadingZeros64(a ^ b))
|
||||
}
|
||||
|
||||
func (u uint128) commonPrefixLen(v uint128) (n uint8) {
|
||||
if n = u64CommonPrefixLen(u.hi, v.hi); n == 64 {
|
||||
n += u64CommonPrefixLen(u.lo, v.lo)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// halves returns the two uint64 halves of the uint128.
|
||||
//
|
||||
// Logically, think of it as returning two uint64s.
|
||||
// It only returns pointers for inlining reasons on 32-bit platforms.
|
||||
func (u *uint128) halves() [2]*uint64 {
|
||||
return [2]*uint64{&u.hi, &u.lo}
|
||||
}
|
||||
|
||||
// bitsSetFrom returns a copy of u with the given bit
|
||||
// and all subsequent ones set.
|
||||
func (u uint128) bitsSetFrom(bit uint8) uint128 {
|
||||
return u.or(mask6(int(bit)).not())
|
||||
}
|
||||
|
||||
// bitsClearedFrom returns a copy of u with the given bit
|
||||
// and all subsequent ones cleared.
|
||||
func (u uint128) bitsClearedFrom(bit uint8) uint128 {
|
||||
return u.and(mask6(int(bit)))
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
// 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.
|
||||
|
||||
//go:build arm || mips || mipsle || 386
|
||||
// +build arm mips mipsle 386
|
||||
|
||||
package atomicbitops
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// AlignedAtomicInt64 is an atomic int64 that is guaranteed to be 64-bit
|
||||
// aligned, even on 32-bit systems.
|
||||
//
|
||||
// Per https://golang.org/pkg/sync/atomic/#pkg-note-BUG:
|
||||
//
|
||||
// "On ARM, 386, and 32-bit MIPS, it is the caller's responsibility to arrange
|
||||
// for 64-bit alignment of 64-bit words accessed atomically. The first word in
|
||||
// a variable or in an allocated struct, array, or slice can be relied upon to
|
||||
// be 64-bit aligned."
|
||||
//
|
||||
// +stateify savable
|
||||
type AlignedAtomicInt64 struct {
|
||||
value int64
|
||||
value32 int32
|
||||
}
|
||||
|
||||
func (aa *AlignedAtomicInt64) ptr() *int64 {
|
||||
// On 32-bit systems, aa.value is guaranteed to be 32-bit aligned. It means
|
||||
// that in the 12-byte aa.value, there are guaranteed to be 8 contiguous bytes
|
||||
// with 64-bit alignment.
|
||||
return (*int64)(unsafe.Pointer((uintptr(unsafe.Pointer(&aa.value)) + 4) &^ 7))
|
||||
}
|
||||
|
||||
// Load is analagous to atomic.LoadInt64.
|
||||
func (aa *AlignedAtomicInt64) Load() int64 {
|
||||
return atomic.LoadInt64(aa.ptr())
|
||||
}
|
||||
|
||||
// Store is analagous to atomic.StoreInt64.
|
||||
func (aa *AlignedAtomicInt64) Store(v int64) {
|
||||
atomic.StoreInt64(aa.ptr(), v)
|
||||
}
|
||||
|
||||
// Add is analagous to atomic.AddInt64.
|
||||
func (aa *AlignedAtomicInt64) Add(v int64) int64 {
|
||||
return atomic.AddInt64(aa.ptr(), v)
|
||||
}
|
||||
|
||||
// AlignedAtomicUint64 is an atomic uint64 that is guaranteed to be 64-bit
|
||||
// aligned, even on 32-bit systems.
|
||||
//
|
||||
// Per https://golang.org/pkg/sync/atomic/#pkg-note-BUG:
|
||||
//
|
||||
// "On ARM, 386, and 32-bit MIPS, it is the caller's responsibility to arrange
|
||||
// for 64-bit alignment of 64-bit words accessed atomically. The first word in
|
||||
// a variable or in an allocated struct, array, or slice can be relied upon to
|
||||
// be 64-bit aligned."
|
||||
//
|
||||
// +stateify savable
|
||||
type AlignedAtomicUint64 struct {
|
||||
value uint64
|
||||
value32 uint32
|
||||
}
|
||||
|
||||
func (aa *AlignedAtomicUint64) ptr() *uint64 {
|
||||
// On 32-bit systems, aa.value is guaranteed to be 32-bit aligned. It means
|
||||
// that in the 12-byte aa.value, there are guaranteed to be 8 contiguous bytes
|
||||
// with 64-bit alignment.
|
||||
return (*uint64)(unsafe.Pointer((uintptr(unsafe.Pointer(&aa.value)) + 4) &^ 7))
|
||||
}
|
||||
|
||||
// Load is analagous to atomic.LoadUint64.
|
||||
func (aa *AlignedAtomicUint64) Load() uint64 {
|
||||
return atomic.LoadUint64(aa.ptr())
|
||||
}
|
||||
|
||||
// Store is analagous to atomic.StoreUint64.
|
||||
func (aa *AlignedAtomicUint64) Store(v uint64) {
|
||||
atomic.StoreUint64(aa.ptr(), v)
|
||||
}
|
||||
|
||||
// Add is analagous to atomic.AddUint64.
|
||||
func (aa *AlignedAtomicUint64) Add(v uint64) uint64 {
|
||||
return atomic.AddUint64(aa.ptr(), v)
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// 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.
|
||||
|
||||
//go:build !arm && !mips && !mipsle && !386
|
||||
// +build !arm,!mips,!mipsle,!386
|
||||
|
||||
package atomicbitops
|
||||
|
||||
import "sync/atomic"
|
||||
|
||||
// AlignedAtomicInt64 is an atomic int64 that is guaranteed to be 64-bit
|
||||
// aligned, even on 32-bit systems. On most architectures, it's just a regular
|
||||
// int64.
|
||||
//
|
||||
// See aligned_unsafe.go in this directory for justification.
|
||||
//
|
||||
// +stateify savable
|
||||
type AlignedAtomicInt64 struct {
|
||||
value int64
|
||||
}
|
||||
|
||||
// Load is analagous to atomic.LoadInt64.
|
||||
func (aa *AlignedAtomicInt64) Load() int64 {
|
||||
return atomic.LoadInt64(&aa.value)
|
||||
}
|
||||
|
||||
// Store is analagous to atomic.StoreInt64.
|
||||
func (aa *AlignedAtomicInt64) Store(v int64) {
|
||||
atomic.StoreInt64(&aa.value, v)
|
||||
}
|
||||
|
||||
// Add is analagous to atomic.AddInt64.
|
||||
func (aa *AlignedAtomicInt64) Add(v int64) int64 {
|
||||
return atomic.AddInt64(&aa.value, v)
|
||||
}
|
||||
|
||||
// AlignedAtomicUint64 is an atomic uint64 that is guaranteed to be 64-bit
|
||||
// aligned, even on 32-bit systems. On most architectures, it's just a regular
|
||||
// uint64.
|
||||
//
|
||||
// See aligned_unsafe.go in this directory for justification.
|
||||
//
|
||||
// +stateify savable
|
||||
type AlignedAtomicUint64 struct {
|
||||
value uint64
|
||||
}
|
||||
|
||||
// Load is analagous to atomic.LoadUint64.
|
||||
func (aa *AlignedAtomicUint64) Load() uint64 {
|
||||
return atomic.LoadUint64(&aa.value)
|
||||
}
|
||||
|
||||
// Store is analagous to atomic.StoreUint64.
|
||||
func (aa *AlignedAtomicUint64) Store(v uint64) {
|
||||
atomic.StoreUint64(&aa.value, v)
|
||||
}
|
||||
|
||||
// Add is analagous to atomic.AddUint64.
|
||||
func (aa *AlignedAtomicUint64) Add(v uint64) uint64 {
|
||||
return atomic.AddUint64(&aa.value, v)
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// 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.
|
||||
|
||||
//go:build amd64 || arm64
|
||||
// +build amd64 arm64
|
||||
|
||||
// Package atomicbitops provides extensions to the sync/atomic package.
|
||||
//
|
||||
// All read-modify-write operations implemented by this package have
|
||||
// acquire-release memory ordering (like sync/atomic).
|
||||
package atomicbitops
|
||||
|
||||
// AndUint32 atomically applies bitwise AND operation to *addr with val.
|
||||
func AndUint32(addr *uint32, val uint32)
|
||||
|
||||
// OrUint32 atomically applies bitwise OR operation to *addr with val.
|
||||
func OrUint32(addr *uint32, val uint32)
|
||||
|
||||
// XorUint32 atomically applies bitwise XOR operation to *addr with val.
|
||||
func XorUint32(addr *uint32, val uint32)
|
||||
|
||||
// CompareAndSwapUint32 is like sync/atomic.CompareAndSwapUint32, but returns
|
||||
// the value previously stored at addr.
|
||||
func CompareAndSwapUint32(addr *uint32, old, new uint32) uint32
|
||||
|
||||
// AndUint64 atomically applies bitwise AND operation to *addr with val.
|
||||
func AndUint64(addr *uint64, val uint64)
|
||||
|
||||
// OrUint64 atomically applies bitwise OR operation to *addr with val.
|
||||
func OrUint64(addr *uint64, val uint64)
|
||||
|
||||
// XorUint64 atomically applies bitwise XOR operation to *addr with val.
|
||||
func XorUint64(addr *uint64, val uint64)
|
||||
|
||||
// CompareAndSwapUint64 is like sync/atomic.CompareAndSwapUint64, but returns
|
||||
// the value previously stored at addr.
|
||||
func CompareAndSwapUint64(addr *uint64, old, new uint64) uint64
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
// automatically generated by stateify.
|
||||
|
||||
//go:build arm || mips || mipsle || 386
|
||||
// +build arm mips mipsle 386
|
||||
|
||||
package atomicbitops
|
||||
|
||||
import (
|
||||
"inet.af/netstack/state"
|
||||
)
|
||||
|
||||
func (aa *AlignedAtomicInt64) StateTypeName() string {
|
||||
return "pkg/atomicbitops.AlignedAtomicInt64"
|
||||
}
|
||||
|
||||
func (aa *AlignedAtomicInt64) StateFields() []string {
|
||||
return []string{
|
||||
"value",
|
||||
"value32",
|
||||
}
|
||||
}
|
||||
|
||||
func (aa *AlignedAtomicInt64) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (aa *AlignedAtomicInt64) StateSave(stateSinkObject state.Sink) {
|
||||
aa.beforeSave()
|
||||
stateSinkObject.Save(0, &aa.value)
|
||||
stateSinkObject.Save(1, &aa.value32)
|
||||
}
|
||||
|
||||
func (aa *AlignedAtomicInt64) afterLoad() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (aa *AlignedAtomicInt64) StateLoad(stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &aa.value)
|
||||
stateSourceObject.Load(1, &aa.value32)
|
||||
}
|
||||
|
||||
func (aa *AlignedAtomicUint64) StateTypeName() string {
|
||||
return "pkg/atomicbitops.AlignedAtomicUint64"
|
||||
}
|
||||
|
||||
func (aa *AlignedAtomicUint64) StateFields() []string {
|
||||
return []string{
|
||||
"value",
|
||||
"value32",
|
||||
}
|
||||
}
|
||||
|
||||
func (aa *AlignedAtomicUint64) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (aa *AlignedAtomicUint64) StateSave(stateSinkObject state.Sink) {
|
||||
aa.beforeSave()
|
||||
stateSinkObject.Save(0, &aa.value)
|
||||
stateSinkObject.Save(1, &aa.value32)
|
||||
}
|
||||
|
||||
func (aa *AlignedAtomicUint64) afterLoad() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (aa *AlignedAtomicUint64) StateLoad(stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &aa.value)
|
||||
stateSourceObject.Load(1, &aa.value32)
|
||||
}
|
||||
|
||||
func init() {
|
||||
state.Register((*AlignedAtomicInt64)(nil))
|
||||
state.Register((*AlignedAtomicUint64)(nil))
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
// automatically generated by stateify.
|
||||
|
||||
//go:build !arm && !mips && !mipsle && !386
|
||||
// +build !arm,!mips,!mipsle,!386
|
||||
|
||||
package atomicbitops
|
||||
|
||||
import (
|
||||
"inet.af/netstack/state"
|
||||
)
|
||||
|
||||
func (aa *AlignedAtomicInt64) StateTypeName() string {
|
||||
return "pkg/atomicbitops.AlignedAtomicInt64"
|
||||
}
|
||||
|
||||
func (aa *AlignedAtomicInt64) StateFields() []string {
|
||||
return []string{
|
||||
"value",
|
||||
}
|
||||
}
|
||||
|
||||
func (aa *AlignedAtomicInt64) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (aa *AlignedAtomicInt64) StateSave(stateSinkObject state.Sink) {
|
||||
aa.beforeSave()
|
||||
stateSinkObject.Save(0, &aa.value)
|
||||
}
|
||||
|
||||
func (aa *AlignedAtomicInt64) afterLoad() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (aa *AlignedAtomicInt64) StateLoad(stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &aa.value)
|
||||
}
|
||||
|
||||
func (aa *AlignedAtomicUint64) StateTypeName() string {
|
||||
return "pkg/atomicbitops.AlignedAtomicUint64"
|
||||
}
|
||||
|
||||
func (aa *AlignedAtomicUint64) StateFields() []string {
|
||||
return []string{
|
||||
"value",
|
||||
}
|
||||
}
|
||||
|
||||
func (aa *AlignedAtomicUint64) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (aa *AlignedAtomicUint64) StateSave(stateSinkObject state.Sink) {
|
||||
aa.beforeSave()
|
||||
stateSinkObject.Save(0, &aa.value)
|
||||
}
|
||||
|
||||
func (aa *AlignedAtomicUint64) afterLoad() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (aa *AlignedAtomicUint64) StateLoad(stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &aa.value)
|
||||
}
|
||||
|
||||
func init() {
|
||||
state.Register((*AlignedAtomicInt64)(nil))
|
||||
state.Register((*AlignedAtomicUint64)(nil))
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
// 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.
|
||||
|
||||
// +build amd64
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
TEXT ·AndUint32(SB),NOSPLIT,$0-12
|
||||
MOVQ addr+0(FP), BX
|
||||
MOVL val+8(FP), AX
|
||||
LOCK
|
||||
ANDL AX, 0(BX)
|
||||
RET
|
||||
|
||||
TEXT ·OrUint32(SB),NOSPLIT,$0-12
|
||||
MOVQ addr+0(FP), BX
|
||||
MOVL val+8(FP), AX
|
||||
LOCK
|
||||
ORL AX, 0(BX)
|
||||
RET
|
||||
|
||||
TEXT ·XorUint32(SB),NOSPLIT,$0-12
|
||||
MOVQ addr+0(FP), BX
|
||||
MOVL val+8(FP), AX
|
||||
LOCK
|
||||
XORL AX, 0(BX)
|
||||
RET
|
||||
|
||||
TEXT ·CompareAndSwapUint32(SB),NOSPLIT,$0-20
|
||||
MOVQ addr+0(FP), DI
|
||||
MOVL old+8(FP), AX
|
||||
MOVL new+12(FP), DX
|
||||
LOCK
|
||||
CMPXCHGL DX, 0(DI)
|
||||
MOVL AX, ret+16(FP)
|
||||
RET
|
||||
|
||||
TEXT ·AndUint64(SB),NOSPLIT,$0-16
|
||||
MOVQ addr+0(FP), BX
|
||||
MOVQ val+8(FP), AX
|
||||
LOCK
|
||||
ANDQ AX, 0(BX)
|
||||
RET
|
||||
|
||||
TEXT ·OrUint64(SB),NOSPLIT,$0-16
|
||||
MOVQ addr+0(FP), BX
|
||||
MOVQ val+8(FP), AX
|
||||
LOCK
|
||||
ORQ AX, 0(BX)
|
||||
RET
|
||||
|
||||
TEXT ·XorUint64(SB),NOSPLIT,$0-16
|
||||
MOVQ addr+0(FP), BX
|
||||
MOVQ val+8(FP), AX
|
||||
LOCK
|
||||
XORQ AX, 0(BX)
|
||||
RET
|
||||
|
||||
TEXT ·CompareAndSwapUint64(SB),NOSPLIT,$0-32
|
||||
MOVQ addr+0(FP), DI
|
||||
MOVQ old+8(FP), AX
|
||||
MOVQ new+16(FP), DX
|
||||
LOCK
|
||||
CMPXCHGQ DX, 0(DI)
|
||||
MOVQ AX, ret+24(FP)
|
||||
RET
|
||||
@@ -0,0 +1,105 @@
|
||||
// Copyright 2019 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.
|
||||
|
||||
// +build arm64
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
TEXT ·AndUint32(SB),NOSPLIT,$0-12
|
||||
MOVD ptr+0(FP), R0
|
||||
MOVW val+8(FP), R1
|
||||
again:
|
||||
LDAXRW (R0), R2
|
||||
ANDW R1, R2
|
||||
STLXRW R2, (R0), R3
|
||||
CBNZ R3, again
|
||||
RET
|
||||
|
||||
TEXT ·OrUint32(SB),NOSPLIT,$0-12
|
||||
MOVD ptr+0(FP), R0
|
||||
MOVW val+8(FP), R1
|
||||
again:
|
||||
LDAXRW (R0), R2
|
||||
ORRW R1, R2
|
||||
STLXRW R2, (R0), R3
|
||||
CBNZ R3, again
|
||||
RET
|
||||
|
||||
TEXT ·XorUint32(SB),NOSPLIT,$0-12
|
||||
MOVD ptr+0(FP), R0
|
||||
MOVW val+8(FP), R1
|
||||
again:
|
||||
LDAXRW (R0), R2
|
||||
EORW R1, R2
|
||||
STLXRW R2, (R0), R3
|
||||
CBNZ R3, again
|
||||
RET
|
||||
|
||||
TEXT ·CompareAndSwapUint32(SB),NOSPLIT,$0-20
|
||||
MOVD addr+0(FP), R0
|
||||
MOVW old+8(FP), R1
|
||||
MOVW new+12(FP), R2
|
||||
again:
|
||||
LDAXRW (R0), R3
|
||||
CMPW R1, R3
|
||||
BNE done
|
||||
STLXRW R2, (R0), R4
|
||||
CBNZ R4, again
|
||||
done:
|
||||
MOVW R3, prev+16(FP)
|
||||
RET
|
||||
|
||||
TEXT ·AndUint64(SB),NOSPLIT,$0-16
|
||||
MOVD ptr+0(FP), R0
|
||||
MOVD val+8(FP), R1
|
||||
again:
|
||||
LDAXR (R0), R2
|
||||
AND R1, R2
|
||||
STLXR R2, (R0), R3
|
||||
CBNZ R3, again
|
||||
RET
|
||||
|
||||
TEXT ·OrUint64(SB),NOSPLIT,$0-16
|
||||
MOVD ptr+0(FP), R0
|
||||
MOVD val+8(FP), R1
|
||||
again:
|
||||
LDAXR (R0), R2
|
||||
ORR R1, R2
|
||||
STLXR R2, (R0), R3
|
||||
CBNZ R3, again
|
||||
RET
|
||||
|
||||
TEXT ·XorUint64(SB),NOSPLIT,$0-16
|
||||
MOVD ptr+0(FP), R0
|
||||
MOVD val+8(FP), R1
|
||||
again:
|
||||
LDAXR (R0), R2
|
||||
EOR R1, R2
|
||||
STLXR R2, (R0), R3
|
||||
CBNZ R3, again
|
||||
RET
|
||||
|
||||
TEXT ·CompareAndSwapUint64(SB),NOSPLIT,$0-32
|
||||
MOVD addr+0(FP), R0
|
||||
MOVD old+8(FP), R1
|
||||
MOVD new+16(FP), R2
|
||||
again:
|
||||
LDAXR (R0), R3
|
||||
CMP R1, R3
|
||||
BNE done
|
||||
STLXR R2, (R0), R4
|
||||
CBNZ R4, again
|
||||
done:
|
||||
MOVD R3, prev+24(FP)
|
||||
RET
|
||||
@@ -0,0 +1,114 @@
|
||||
// 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.
|
||||
|
||||
//go:build !amd64 && !arm64
|
||||
// +build !amd64,!arm64
|
||||
|
||||
package atomicbitops
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
//go:nosplit
|
||||
func AndUint32(addr *uint32, val uint32) {
|
||||
for {
|
||||
o := atomic.LoadUint32(addr)
|
||||
n := o & val
|
||||
if atomic.CompareAndSwapUint32(addr, o, n) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func OrUint32(addr *uint32, val uint32) {
|
||||
for {
|
||||
o := atomic.LoadUint32(addr)
|
||||
n := o | val
|
||||
if atomic.CompareAndSwapUint32(addr, o, n) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func XorUint32(addr *uint32, val uint32) {
|
||||
for {
|
||||
o := atomic.LoadUint32(addr)
|
||||
n := o ^ val
|
||||
if atomic.CompareAndSwapUint32(addr, o, n) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func CompareAndSwapUint32(addr *uint32, old, new uint32) (prev uint32) {
|
||||
for {
|
||||
prev = atomic.LoadUint32(addr)
|
||||
if prev != old {
|
||||
return
|
||||
}
|
||||
if atomic.CompareAndSwapUint32(addr, old, new) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func AndUint64(addr *uint64, val uint64) {
|
||||
for {
|
||||
o := atomic.LoadUint64(addr)
|
||||
n := o & val
|
||||
if atomic.CompareAndSwapUint64(addr, o, n) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func OrUint64(addr *uint64, val uint64) {
|
||||
for {
|
||||
o := atomic.LoadUint64(addr)
|
||||
n := o | val
|
||||
if atomic.CompareAndSwapUint64(addr, o, n) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func XorUint64(addr *uint64, val uint64) {
|
||||
for {
|
||||
o := atomic.LoadUint64(addr)
|
||||
n := o ^ val
|
||||
if atomic.CompareAndSwapUint64(addr, o, n) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func CompareAndSwapUint64(addr *uint64, old, new uint64) (prev uint64) {
|
||||
for {
|
||||
prev = atomic.LoadUint64(addr)
|
||||
if prev != old {
|
||||
return
|
||||
}
|
||||
if atomic.CompareAndSwapUint64(addr, old, new) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// automatically generated by stateify.
|
||||
|
||||
//go:build (amd64 || arm64) && !amd64 && !arm64
|
||||
// +build amd64 arm64
|
||||
// +build !amd64
|
||||
// +build !arm64
|
||||
|
||||
package atomicbitops
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
// 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 buffer provides the implementation of a buffer view.
|
||||
//
|
||||
// A view is an flexible buffer, supporting the safecopy operations natively as
|
||||
// well as the ability to grow via either prepend or append, as well as shrink.
|
||||
package buffer
|
||||
|
||||
// buffer encapsulates a queueable byte buffer.
|
||||
//
|
||||
// +stateify savable
|
||||
type buffer struct {
|
||||
data []byte
|
||||
read int
|
||||
write int
|
||||
bufferEntry
|
||||
}
|
||||
|
||||
// init performs in-place initialization for zero value.
|
||||
func (b *buffer) init(size int) {
|
||||
b.data = make([]byte, size)
|
||||
}
|
||||
|
||||
// initWithData initializes b with data, taking ownership.
|
||||
func (b *buffer) initWithData(data []byte) {
|
||||
b.data = data
|
||||
b.read = 0
|
||||
b.write = len(data)
|
||||
}
|
||||
|
||||
// Reset resets read and write locations, effectively emptying the buffer.
|
||||
func (b *buffer) Reset() {
|
||||
b.read = 0
|
||||
b.write = 0
|
||||
}
|
||||
|
||||
// Remove removes r from the unread portion. It returns false if r does not
|
||||
// fully reside in b.
|
||||
func (b *buffer) Remove(r Range) bool {
|
||||
sz := b.ReadSize()
|
||||
switch {
|
||||
case r.Len() != r.Intersect(Range{end: sz}).Len():
|
||||
return false
|
||||
case r.Len() == 0:
|
||||
// Noop
|
||||
case r.begin == 0:
|
||||
b.read += r.end
|
||||
case r.end == sz:
|
||||
b.write -= r.Len()
|
||||
default:
|
||||
// Remove from the middle of b.data.
|
||||
copy(b.data[b.read+r.begin:], b.data[b.read+r.end:b.write])
|
||||
b.write -= r.Len()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Full indicates the buffer is full.
|
||||
//
|
||||
// This indicates there is no capacity left to write.
|
||||
func (b *buffer) Full() bool {
|
||||
return b.write == len(b.data)
|
||||
}
|
||||
|
||||
// ReadSize returns the number of bytes available for reading.
|
||||
func (b *buffer) ReadSize() int {
|
||||
return b.write - b.read
|
||||
}
|
||||
|
||||
// ReadMove advances the read index by the given amount.
|
||||
func (b *buffer) ReadMove(n int) {
|
||||
b.read += n
|
||||
}
|
||||
|
||||
// ReadSlice returns the read slice for this buffer.
|
||||
func (b *buffer) ReadSlice() []byte {
|
||||
return b.data[b.read:b.write]
|
||||
}
|
||||
|
||||
// WriteSize returns the number of bytes available for writing.
|
||||
func (b *buffer) WriteSize() int {
|
||||
return len(b.data) - b.write
|
||||
}
|
||||
|
||||
// WriteMove advances the write index by the given amount.
|
||||
func (b *buffer) WriteMove(n int) {
|
||||
b.write += n
|
||||
}
|
||||
|
||||
// WriteSlice returns the write slice for this buffer.
|
||||
func (b *buffer) WriteSlice() []byte {
|
||||
return b.data[b.write:]
|
||||
}
|
||||
+221
@@ -0,0 +1,221 @@
|
||||
package buffer
|
||||
|
||||
// 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 bufferElementMapper struct{}
|
||||
|
||||
// linkerFor maps an Element to a Linker.
|
||||
//
|
||||
// This default implementation should be inlined.
|
||||
//
|
||||
//go:nosplit
|
||||
func (bufferElementMapper) linkerFor(elem *buffer) *buffer { 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 bufferList struct {
|
||||
head *buffer
|
||||
tail *buffer
|
||||
}
|
||||
|
||||
// Reset resets list l to the empty state.
|
||||
func (l *bufferList) Reset() {
|
||||
l.head = nil
|
||||
l.tail = nil
|
||||
}
|
||||
|
||||
// Empty returns true iff the list is empty.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *bufferList) Empty() bool {
|
||||
return l.head == nil
|
||||
}
|
||||
|
||||
// Front returns the first element of list l or nil.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *bufferList) Front() *buffer {
|
||||
return l.head
|
||||
}
|
||||
|
||||
// Back returns the last element of list l or nil.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *bufferList) Back() *buffer {
|
||||
return l.tail
|
||||
}
|
||||
|
||||
// Len returns the number of elements in the list.
|
||||
//
|
||||
// NOTE: This is an O(n) operation.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *bufferList) Len() (count int) {
|
||||
for e := l.Front(); e != nil; e = (bufferElementMapper{}.linkerFor(e)).Next() {
|
||||
count++
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// PushFront inserts the element e at the front of list l.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *bufferList) PushFront(e *buffer) {
|
||||
linker := bufferElementMapper{}.linkerFor(e)
|
||||
linker.SetNext(l.head)
|
||||
linker.SetPrev(nil)
|
||||
if l.head != nil {
|
||||
bufferElementMapper{}.linkerFor(l.head).SetPrev(e)
|
||||
} else {
|
||||
l.tail = e
|
||||
}
|
||||
|
||||
l.head = e
|
||||
}
|
||||
|
||||
// PushBack inserts the element e at the back of list l.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *bufferList) PushBack(e *buffer) {
|
||||
linker := bufferElementMapper{}.linkerFor(e)
|
||||
linker.SetNext(nil)
|
||||
linker.SetPrev(l.tail)
|
||||
if l.tail != nil {
|
||||
bufferElementMapper{}.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 *bufferList) PushBackList(m *bufferList) {
|
||||
if l.head == nil {
|
||||
l.head = m.head
|
||||
l.tail = m.tail
|
||||
} else if m.head != nil {
|
||||
bufferElementMapper{}.linkerFor(l.tail).SetNext(m.head)
|
||||
bufferElementMapper{}.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 *bufferList) InsertAfter(b, e *buffer) {
|
||||
bLinker := bufferElementMapper{}.linkerFor(b)
|
||||
eLinker := bufferElementMapper{}.linkerFor(e)
|
||||
|
||||
a := bLinker.Next()
|
||||
|
||||
eLinker.SetNext(a)
|
||||
eLinker.SetPrev(b)
|
||||
bLinker.SetNext(e)
|
||||
|
||||
if a != nil {
|
||||
bufferElementMapper{}.linkerFor(a).SetPrev(e)
|
||||
} else {
|
||||
l.tail = e
|
||||
}
|
||||
}
|
||||
|
||||
// InsertBefore inserts e before a.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *bufferList) InsertBefore(a, e *buffer) {
|
||||
aLinker := bufferElementMapper{}.linkerFor(a)
|
||||
eLinker := bufferElementMapper{}.linkerFor(e)
|
||||
|
||||
b := aLinker.Prev()
|
||||
eLinker.SetNext(a)
|
||||
eLinker.SetPrev(b)
|
||||
aLinker.SetPrev(e)
|
||||
|
||||
if b != nil {
|
||||
bufferElementMapper{}.linkerFor(b).SetNext(e)
|
||||
} else {
|
||||
l.head = e
|
||||
}
|
||||
}
|
||||
|
||||
// Remove removes e from l.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *bufferList) Remove(e *buffer) {
|
||||
linker := bufferElementMapper{}.linkerFor(e)
|
||||
prev := linker.Prev()
|
||||
next := linker.Next()
|
||||
|
||||
if prev != nil {
|
||||
bufferElementMapper{}.linkerFor(prev).SetNext(next)
|
||||
} else if l.head == e {
|
||||
l.head = next
|
||||
}
|
||||
|
||||
if next != nil {
|
||||
bufferElementMapper{}.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 bufferEntry struct {
|
||||
next *buffer
|
||||
prev *buffer
|
||||
}
|
||||
|
||||
// Next returns the entry that follows e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *bufferEntry) Next() *buffer {
|
||||
return e.next
|
||||
}
|
||||
|
||||
// Prev returns the entry that precedes e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *bufferEntry) Prev() *buffer {
|
||||
return e.prev
|
||||
}
|
||||
|
||||
// SetNext assigns 'entry' as the entry that follows e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *bufferEntry) SetNext(elem *buffer) {
|
||||
e.next = elem
|
||||
}
|
||||
|
||||
// SetPrev assigns 'entry' as the entry that precedes e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *bufferEntry) SetPrev(elem *buffer) {
|
||||
e.prev = elem
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
// automatically generated by stateify.
|
||||
|
||||
package buffer
|
||||
|
||||
import (
|
||||
"inet.af/netstack/state"
|
||||
)
|
||||
|
||||
func (b *buffer) StateTypeName() string {
|
||||
return "pkg/buffer.buffer"
|
||||
}
|
||||
|
||||
func (b *buffer) StateFields() []string {
|
||||
return []string{
|
||||
"data",
|
||||
"read",
|
||||
"write",
|
||||
"bufferEntry",
|
||||
}
|
||||
}
|
||||
|
||||
func (b *buffer) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (b *buffer) StateSave(stateSinkObject state.Sink) {
|
||||
b.beforeSave()
|
||||
stateSinkObject.Save(0, &b.data)
|
||||
stateSinkObject.Save(1, &b.read)
|
||||
stateSinkObject.Save(2, &b.write)
|
||||
stateSinkObject.Save(3, &b.bufferEntry)
|
||||
}
|
||||
|
||||
func (b *buffer) afterLoad() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (b *buffer) StateLoad(stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &b.data)
|
||||
stateSourceObject.Load(1, &b.read)
|
||||
stateSourceObject.Load(2, &b.write)
|
||||
stateSourceObject.Load(3, &b.bufferEntry)
|
||||
}
|
||||
|
||||
func (l *bufferList) StateTypeName() string {
|
||||
return "pkg/buffer.bufferList"
|
||||
}
|
||||
|
||||
func (l *bufferList) StateFields() []string {
|
||||
return []string{
|
||||
"head",
|
||||
"tail",
|
||||
}
|
||||
}
|
||||
|
||||
func (l *bufferList) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (l *bufferList) StateSave(stateSinkObject state.Sink) {
|
||||
l.beforeSave()
|
||||
stateSinkObject.Save(0, &l.head)
|
||||
stateSinkObject.Save(1, &l.tail)
|
||||
}
|
||||
|
||||
func (l *bufferList) afterLoad() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (l *bufferList) StateLoad(stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &l.head)
|
||||
stateSourceObject.Load(1, &l.tail)
|
||||
}
|
||||
|
||||
func (e *bufferEntry) StateTypeName() string {
|
||||
return "pkg/buffer.bufferEntry"
|
||||
}
|
||||
|
||||
func (e *bufferEntry) StateFields() []string {
|
||||
return []string{
|
||||
"next",
|
||||
"prev",
|
||||
}
|
||||
}
|
||||
|
||||
func (e *bufferEntry) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *bufferEntry) StateSave(stateSinkObject state.Sink) {
|
||||
e.beforeSave()
|
||||
stateSinkObject.Save(0, &e.next)
|
||||
stateSinkObject.Save(1, &e.prev)
|
||||
}
|
||||
|
||||
func (e *bufferEntry) afterLoad() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *bufferEntry) StateLoad(stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &e.next)
|
||||
stateSourceObject.Load(1, &e.prev)
|
||||
}
|
||||
|
||||
func (p *pool) StateTypeName() string {
|
||||
return "pkg/buffer.pool"
|
||||
}
|
||||
|
||||
func (p *pool) StateFields() []string {
|
||||
return []string{
|
||||
"bufferSize",
|
||||
"embeddedStorage",
|
||||
}
|
||||
}
|
||||
|
||||
func (p *pool) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (p *pool) StateSave(stateSinkObject state.Sink) {
|
||||
p.beforeSave()
|
||||
stateSinkObject.Save(0, &p.bufferSize)
|
||||
stateSinkObject.Save(1, &p.embeddedStorage)
|
||||
}
|
||||
|
||||
// +checklocksignore
|
||||
func (p *pool) StateLoad(stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &p.bufferSize)
|
||||
stateSourceObject.LoadWait(1, &p.embeddedStorage)
|
||||
stateSourceObject.AfterLoad(p.afterLoad)
|
||||
}
|
||||
|
||||
func (v *View) StateTypeName() string {
|
||||
return "pkg/buffer.View"
|
||||
}
|
||||
|
||||
func (v *View) StateFields() []string {
|
||||
return []string{
|
||||
"data",
|
||||
"size",
|
||||
"pool",
|
||||
}
|
||||
}
|
||||
|
||||
func (v *View) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (v *View) StateSave(stateSinkObject state.Sink) {
|
||||
v.beforeSave()
|
||||
stateSinkObject.Save(0, &v.data)
|
||||
stateSinkObject.Save(1, &v.size)
|
||||
stateSinkObject.Save(2, &v.pool)
|
||||
}
|
||||
|
||||
func (v *View) afterLoad() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (v *View) StateLoad(stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &v.data)
|
||||
stateSourceObject.Load(1, &v.size)
|
||||
stateSourceObject.Load(2, &v.pool)
|
||||
}
|
||||
|
||||
func init() {
|
||||
state.Register((*buffer)(nil))
|
||||
state.Register((*bufferList)(nil))
|
||||
state.Register((*bufferEntry)(nil))
|
||||
state.Register((*pool)(nil))
|
||||
state.Register((*View)(nil))
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
// automatically generated by stateify.
|
||||
|
||||
package buffer
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
// 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 buffer
|
||||
|
||||
const (
|
||||
// embeddedCount is the number of buffer structures embedded in the pool. It
|
||||
// is also the number for overflow allocations.
|
||||
embeddedCount = 8
|
||||
|
||||
// defaultBufferSize is the default size for each underlying storage buffer.
|
||||
//
|
||||
// It is slightly less than two pages. This is done intentionally to ensure
|
||||
// that the buffer object aligns with runtime internals. This two page size
|
||||
// will effectively minimize internal fragmentation, but still have a large
|
||||
// enough chunk to limit excessive segmentation.
|
||||
defaultBufferSize = 8144
|
||||
)
|
||||
|
||||
// pool allocates buffer.
|
||||
//
|
||||
// It contains an embedded buffer storage for fast path when the number of
|
||||
// buffers needed is small.
|
||||
//
|
||||
// +stateify savable
|
||||
type pool struct {
|
||||
bufferSize int
|
||||
avail []buffer `state:"nosave"`
|
||||
embeddedStorage [embeddedCount]buffer `state:"wait"`
|
||||
}
|
||||
|
||||
// get gets a new buffer from p.
|
||||
func (p *pool) get() *buffer {
|
||||
buf := p.getNoInit()
|
||||
buf.init(p.bufferSize)
|
||||
return buf
|
||||
}
|
||||
|
||||
// get gets a new buffer from p without initializing it.
|
||||
func (p *pool) getNoInit() *buffer {
|
||||
if p.avail == nil {
|
||||
p.avail = p.embeddedStorage[:]
|
||||
}
|
||||
if len(p.avail) == 0 {
|
||||
p.avail = make([]buffer, embeddedCount)
|
||||
}
|
||||
if p.bufferSize <= 0 {
|
||||
p.bufferSize = defaultBufferSize
|
||||
}
|
||||
buf := &p.avail[0]
|
||||
p.avail = p.avail[1:]
|
||||
return buf
|
||||
}
|
||||
|
||||
// put releases buf.
|
||||
func (p *pool) put(buf *buffer) {
|
||||
// Remove reference to the underlying storage, allowing it to be garbage
|
||||
// collected.
|
||||
buf.data = nil
|
||||
buf.Reset()
|
||||
}
|
||||
|
||||
// setBufferSize sets the size of underlying storage buffer for future
|
||||
// allocations. It can be called at any time.
|
||||
func (p *pool) setBufferSize(size int) {
|
||||
p.bufferSize = size
|
||||
}
|
||||
|
||||
// afterLoad is invoked by stateify.
|
||||
func (p *pool) afterLoad() {
|
||||
// S/R does not save subslice into embeddedStorage correctly. Restore
|
||||
// available portion of embeddedStorage manually. Restore as nil if none used.
|
||||
for i := len(p.embeddedStorage); i > 0; i-- {
|
||||
if p.embeddedStorage[i-1].data != nil {
|
||||
p.avail = p.embeddedStorage[i:]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
+580
@@ -0,0 +1,580 @@
|
||||
// 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 buffer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Buffer is an alias to View.
|
||||
type Buffer = View
|
||||
|
||||
// View is a non-linear buffer.
|
||||
//
|
||||
// All methods are thread compatible.
|
||||
//
|
||||
// +stateify savable
|
||||
type View struct {
|
||||
data bufferList
|
||||
size int64
|
||||
pool pool
|
||||
}
|
||||
|
||||
// TrimFront removes the first count bytes from the buffer.
|
||||
func (v *View) TrimFront(count int64) {
|
||||
if count >= v.size {
|
||||
v.advanceRead(v.size)
|
||||
} else {
|
||||
v.advanceRead(count)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove deletes data at specified location in v. It returns false if specified
|
||||
// range does not fully reside in v.
|
||||
func (v *View) Remove(offset, length int) bool {
|
||||
if offset < 0 || length < 0 {
|
||||
return false
|
||||
}
|
||||
tgt := Range{begin: offset, end: offset + length}
|
||||
if tgt.Len() != tgt.Intersect(Range{end: int(v.size)}).Len() {
|
||||
return false
|
||||
}
|
||||
|
||||
// Scan through each buffer and remove intersections.
|
||||
var curr Range
|
||||
for buf := v.data.Front(); buf != nil; {
|
||||
origLen := buf.ReadSize()
|
||||
curr.end = curr.begin + origLen
|
||||
|
||||
if x := curr.Intersect(tgt); x.Len() > 0 {
|
||||
if !buf.Remove(x.Offset(-curr.begin)) {
|
||||
panic("buf.Remove() failed")
|
||||
}
|
||||
if buf.ReadSize() == 0 {
|
||||
// buf fully removed, removing it from the list.
|
||||
oldBuf := buf
|
||||
buf = buf.Next()
|
||||
v.data.Remove(oldBuf)
|
||||
v.pool.put(oldBuf)
|
||||
} else {
|
||||
// Only partial data intersects, moving on to next one.
|
||||
buf = buf.Next()
|
||||
}
|
||||
v.size -= int64(x.Len())
|
||||
} else {
|
||||
// This buffer is not in range, moving on to next one.
|
||||
buf = buf.Next()
|
||||
}
|
||||
|
||||
curr.begin += origLen
|
||||
if curr.begin >= tgt.end {
|
||||
break
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ReadAt implements io.ReaderAt.ReadAt.
|
||||
func (v *View) ReadAt(p []byte, offset int64) (int, error) {
|
||||
var (
|
||||
skipped int64
|
||||
done int64
|
||||
)
|
||||
for buf := v.data.Front(); buf != nil && done < int64(len(p)); buf = buf.Next() {
|
||||
needToSkip := int(offset - skipped)
|
||||
if sz := buf.ReadSize(); sz <= needToSkip {
|
||||
skipped += int64(sz)
|
||||
continue
|
||||
}
|
||||
|
||||
// Actually read data.
|
||||
n := copy(p[done:], buf.ReadSlice()[needToSkip:])
|
||||
skipped += int64(needToSkip)
|
||||
done += int64(n)
|
||||
}
|
||||
if int(done) < len(p) || offset+done == v.size {
|
||||
return int(done), io.EOF
|
||||
}
|
||||
return int(done), nil
|
||||
}
|
||||
|
||||
// advanceRead advances the view's read index.
|
||||
//
|
||||
// Precondition: there must be sufficient bytes in the buffer.
|
||||
func (v *View) advanceRead(count int64) {
|
||||
for buf := v.data.Front(); buf != nil && count > 0; {
|
||||
sz := int64(buf.ReadSize())
|
||||
if sz > count {
|
||||
// There is still data for reading.
|
||||
buf.ReadMove(int(count))
|
||||
v.size -= count
|
||||
count = 0
|
||||
break
|
||||
}
|
||||
|
||||
// Consume the whole buffer.
|
||||
oldBuf := buf
|
||||
buf = buf.Next() // Iterate.
|
||||
v.data.Remove(oldBuf)
|
||||
v.pool.put(oldBuf)
|
||||
|
||||
// Update counts.
|
||||
count -= sz
|
||||
v.size -= sz
|
||||
}
|
||||
if count > 0 {
|
||||
panic(fmt.Sprintf("advanceRead still has %d bytes remaining", count))
|
||||
}
|
||||
}
|
||||
|
||||
// Truncate truncates the view to the given bytes.
|
||||
//
|
||||
// This will not grow the view, only shrink it. If a length is passed that is
|
||||
// greater than the current size of the view, then nothing will happen.
|
||||
//
|
||||
// Precondition: length must be >= 0.
|
||||
func (v *View) Truncate(length int64) {
|
||||
if length < 0 {
|
||||
panic("negative length provided")
|
||||
}
|
||||
if length >= v.size {
|
||||
return // Nothing to do.
|
||||
}
|
||||
for buf := v.data.Back(); buf != nil && v.size > length; buf = v.data.Back() {
|
||||
sz := int64(buf.ReadSize())
|
||||
if after := v.size - sz; after < length {
|
||||
// Truncate the buffer locally.
|
||||
left := (length - after)
|
||||
buf.write = buf.read + int(left)
|
||||
v.size = length
|
||||
break
|
||||
}
|
||||
|
||||
// Drop the buffer completely; see above.
|
||||
v.data.Remove(buf)
|
||||
v.pool.put(buf)
|
||||
v.size -= sz
|
||||
}
|
||||
}
|
||||
|
||||
// Grow grows the given view to the number of bytes, which will be appended. If
|
||||
// zero is true, all these bytes will be zero. If zero is false, then this is
|
||||
// the caller's responsibility.
|
||||
//
|
||||
// Precondition: length must be >= 0.
|
||||
func (v *View) Grow(length int64, zero bool) {
|
||||
if length < 0 {
|
||||
panic("negative length provided")
|
||||
}
|
||||
for v.size < length {
|
||||
buf := v.data.Back()
|
||||
|
||||
// Is there some space in the last buffer?
|
||||
if buf == nil || buf.Full() {
|
||||
buf = v.pool.get()
|
||||
v.data.PushBack(buf)
|
||||
}
|
||||
|
||||
// Write up to length bytes.
|
||||
sz := buf.WriteSize()
|
||||
if int64(sz) > length-v.size {
|
||||
sz = int(length - v.size)
|
||||
}
|
||||
|
||||
// Zero the written section; note that this pattern is
|
||||
// specifically recognized and optimized by the compiler.
|
||||
if zero {
|
||||
for i := buf.write; i < buf.write+sz; i++ {
|
||||
buf.data[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Advance the index.
|
||||
buf.WriteMove(sz)
|
||||
v.size += int64(sz)
|
||||
}
|
||||
}
|
||||
|
||||
// Prepend prepends the given data.
|
||||
func (v *View) Prepend(data []byte) {
|
||||
// Is there any space in the first buffer?
|
||||
if buf := v.data.Front(); buf != nil && buf.read > 0 {
|
||||
// Fill up before the first write.
|
||||
avail := buf.read
|
||||
bStart := 0
|
||||
dStart := len(data) - avail
|
||||
if avail > len(data) {
|
||||
bStart = avail - len(data)
|
||||
dStart = 0
|
||||
}
|
||||
n := copy(buf.data[bStart:], data[dStart:])
|
||||
data = data[:dStart]
|
||||
v.size += int64(n)
|
||||
buf.read -= n
|
||||
}
|
||||
|
||||
for len(data) > 0 {
|
||||
// Do we need an empty buffer?
|
||||
buf := v.pool.get()
|
||||
v.data.PushFront(buf)
|
||||
|
||||
// The buffer is empty; copy last chunk.
|
||||
avail := len(buf.data)
|
||||
bStart := 0
|
||||
dStart := len(data) - avail
|
||||
if avail > len(data) {
|
||||
bStart = avail - len(data)
|
||||
dStart = 0
|
||||
}
|
||||
|
||||
// We have to put the data at the end of the current
|
||||
// buffer in order to ensure that the next prepend will
|
||||
// correctly fill up the beginning of this buffer.
|
||||
n := copy(buf.data[bStart:], data[dStart:])
|
||||
data = data[:dStart]
|
||||
v.size += int64(n)
|
||||
buf.read = len(buf.data) - n
|
||||
buf.write = len(buf.data)
|
||||
}
|
||||
}
|
||||
|
||||
// Append appends the given data.
|
||||
func (v *View) Append(data []byte) {
|
||||
for done := 0; done < len(data); {
|
||||
buf := v.data.Back()
|
||||
|
||||
// Ensure there's a buffer with space.
|
||||
if buf == nil || buf.Full() {
|
||||
buf = v.pool.get()
|
||||
v.data.PushBack(buf)
|
||||
}
|
||||
|
||||
// Copy in to the given buffer.
|
||||
n := copy(buf.WriteSlice(), data[done:])
|
||||
done += n
|
||||
buf.WriteMove(n)
|
||||
v.size += int64(n)
|
||||
}
|
||||
}
|
||||
|
||||
// AppendOwned takes ownership of data and appends it to v.
|
||||
func (v *View) AppendOwned(data []byte) {
|
||||
if len(data) > 0 {
|
||||
buf := v.pool.getNoInit()
|
||||
buf.initWithData(data)
|
||||
v.data.PushBack(buf)
|
||||
v.size += int64(len(data))
|
||||
}
|
||||
}
|
||||
|
||||
// PullUp makes the specified range contiguous and returns the backing memory.
|
||||
func (v *View) PullUp(offset, length int) ([]byte, bool) {
|
||||
if length == 0 {
|
||||
return nil, true
|
||||
}
|
||||
tgt := Range{begin: offset, end: offset + length}
|
||||
if tgt.Intersect(Range{end: int(v.size)}).Len() != length {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
curr := Range{}
|
||||
buf := v.data.Front()
|
||||
for ; buf != nil; buf = buf.Next() {
|
||||
origLen := buf.ReadSize()
|
||||
curr.end = curr.begin + origLen
|
||||
|
||||
if x := curr.Intersect(tgt); x.Len() == tgt.Len() {
|
||||
// buf covers the whole requested target range.
|
||||
sub := x.Offset(-curr.begin)
|
||||
return buf.ReadSlice()[sub.begin:sub.end], true
|
||||
} else if x.Len() > 0 {
|
||||
// buf is pointing at the starting buffer we want to merge.
|
||||
break
|
||||
}
|
||||
|
||||
curr.begin += origLen
|
||||
}
|
||||
|
||||
// Calculate the total merged length.
|
||||
totLen := 0
|
||||
for n := buf; n != nil; n = n.Next() {
|
||||
totLen += n.ReadSize()
|
||||
if curr.begin+totLen >= tgt.end {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Merge the buffers.
|
||||
data := make([]byte, totLen)
|
||||
off := 0
|
||||
for n := buf; n != nil && off < totLen; {
|
||||
copy(data[off:], n.ReadSlice())
|
||||
off += n.ReadSize()
|
||||
|
||||
// Remove buffers except for the first one, which will be reused.
|
||||
if n == buf {
|
||||
n = n.Next()
|
||||
} else {
|
||||
old := n
|
||||
n = n.Next()
|
||||
v.data.Remove(old)
|
||||
v.pool.put(old)
|
||||
}
|
||||
}
|
||||
|
||||
// Update the first buffer with merged data.
|
||||
buf.initWithData(data)
|
||||
|
||||
r := tgt.Offset(-curr.begin)
|
||||
return buf.data[r.begin:r.end], true
|
||||
}
|
||||
|
||||
// Flatten returns a flattened copy of this data.
|
||||
//
|
||||
// This method should not be used in any performance-sensitive paths. It may
|
||||
// allocate a fresh byte slice sufficiently large to contain all the data in
|
||||
// the buffer. This is principally for debugging.
|
||||
//
|
||||
// N.B. Tee data still belongs to this view, as if there is a single buffer
|
||||
// present, then it will be returned directly. This should be used for
|
||||
// temporary use only, and a reference to the given slice should not be held.
|
||||
func (v *View) Flatten() []byte {
|
||||
if buf := v.data.Front(); buf == nil {
|
||||
return nil // No data at all.
|
||||
} else if buf.Next() == nil {
|
||||
return buf.ReadSlice() // Only one buffer.
|
||||
}
|
||||
data := make([]byte, 0, v.size) // Need to flatten.
|
||||
for buf := v.data.Front(); buf != nil; buf = buf.Next() {
|
||||
// Copy to the allocated slice.
|
||||
data = append(data, buf.ReadSlice()...)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// Size indicates the total amount of data available in this view.
|
||||
func (v *View) Size() int64 {
|
||||
return v.size
|
||||
}
|
||||
|
||||
// Copy makes a strict copy of this view.
|
||||
func (v *View) Copy() (other View) {
|
||||
for buf := v.data.Front(); buf != nil; buf = buf.Next() {
|
||||
other.Append(buf.ReadSlice())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Clone makes a more shallow copy compared to Copy. The underlying payload
|
||||
// slice (buffer.data) is shared but the buffers themselves are copied.
|
||||
func (v *View) Clone() *View {
|
||||
other := &View{
|
||||
size: v.size,
|
||||
}
|
||||
for buf := v.data.Front(); buf != nil; buf = buf.Next() {
|
||||
newBuf := other.pool.getNoInit()
|
||||
*newBuf = *buf
|
||||
other.data.PushBack(newBuf)
|
||||
}
|
||||
return other
|
||||
}
|
||||
|
||||
// Apply applies the given function across all valid data.
|
||||
func (v *View) Apply(fn func([]byte)) {
|
||||
for buf := v.data.Front(); buf != nil; buf = buf.Next() {
|
||||
fn(buf.ReadSlice())
|
||||
}
|
||||
}
|
||||
|
||||
// SubApply applies fn to a given range of data in v. Any part of the range
|
||||
// outside of v is ignored.
|
||||
func (v *View) SubApply(offset, length int, fn func([]byte)) {
|
||||
for buf := v.data.Front(); length > 0 && buf != nil; buf = buf.Next() {
|
||||
d := buf.ReadSlice()
|
||||
if offset >= len(d) {
|
||||
offset -= len(d)
|
||||
continue
|
||||
}
|
||||
if offset > 0 {
|
||||
d = d[offset:]
|
||||
offset = 0
|
||||
}
|
||||
if length < len(d) {
|
||||
d = d[:length]
|
||||
}
|
||||
fn(d)
|
||||
length -= len(d)
|
||||
}
|
||||
}
|
||||
|
||||
// Merge merges the provided View with this one.
|
||||
//
|
||||
// The other view will be appended to v, and other will be empty after this
|
||||
// operation completes.
|
||||
func (v *View) Merge(other *View) {
|
||||
// Copy over all buffers.
|
||||
for buf := other.data.Front(); buf != nil; buf = other.data.Front() {
|
||||
other.data.Remove(buf)
|
||||
v.data.PushBack(buf)
|
||||
}
|
||||
|
||||
// Adjust sizes.
|
||||
v.size += other.size
|
||||
other.size = 0
|
||||
}
|
||||
|
||||
// WriteFromReader writes to the buffer from an io.Reader.
|
||||
//
|
||||
// A minimum read size equal to unsafe.Sizeof(unintptr) is enforced,
|
||||
// provided that count is greater than or equal to unsafe.Sizeof(uintptr).
|
||||
func (v *View) WriteFromReader(r io.Reader, count int64) (int64, error) {
|
||||
var (
|
||||
done int64
|
||||
n int
|
||||
err error
|
||||
)
|
||||
for done < count {
|
||||
buf := v.data.Back()
|
||||
|
||||
// Ensure we have an empty buffer.
|
||||
if buf == nil || buf.Full() {
|
||||
buf = v.pool.get()
|
||||
v.data.PushBack(buf)
|
||||
}
|
||||
|
||||
// Is this less than the minimum batch?
|
||||
if buf.WriteSize() < minBatch && (count-done) >= int64(minBatch) {
|
||||
tmp := make([]byte, minBatch)
|
||||
n, err = r.Read(tmp)
|
||||
v.Append(tmp[:n])
|
||||
done += int64(n)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Limit the read, if necessary.
|
||||
sz := buf.WriteSize()
|
||||
if left := count - done; int64(sz) > left {
|
||||
sz = int(left)
|
||||
}
|
||||
|
||||
// Pass the relevant portion of the buffer.
|
||||
n, err = r.Read(buf.WriteSlice()[:sz])
|
||||
buf.WriteMove(n)
|
||||
done += int64(n)
|
||||
v.size += int64(n)
|
||||
if err == io.EOF {
|
||||
err = nil // Short write allowed.
|
||||
break
|
||||
} else if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
return done, err
|
||||
}
|
||||
|
||||
// ReadToWriter reads from the buffer into an io.Writer.
|
||||
//
|
||||
// N.B. This does not consume the bytes read. TrimFront should
|
||||
// be called appropriately after this call in order to do so.
|
||||
//
|
||||
// A minimum write size equal to unsafe.Sizeof(unintptr) is enforced,
|
||||
// provided that count is greater than or equal to unsafe.Sizeof(uintptr).
|
||||
func (v *View) ReadToWriter(w io.Writer, count int64) (int64, error) {
|
||||
var (
|
||||
done int64
|
||||
n int
|
||||
err error
|
||||
)
|
||||
offset := 0 // Spill-over for batching.
|
||||
for buf := v.data.Front(); buf != nil && done < count; buf = buf.Next() {
|
||||
// Has this been consumed? Skip it.
|
||||
sz := buf.ReadSize()
|
||||
if sz <= offset {
|
||||
offset -= sz
|
||||
continue
|
||||
}
|
||||
sz -= offset
|
||||
|
||||
// Is this less than the minimum batch?
|
||||
left := count - done
|
||||
if sz < minBatch && left >= int64(minBatch) && (v.size-done) >= int64(minBatch) {
|
||||
tmp := make([]byte, minBatch)
|
||||
n, err = v.ReadAt(tmp, done)
|
||||
w.Write(tmp[:n])
|
||||
done += int64(n)
|
||||
offset = n - sz // Reset below.
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Limit the write if necessary.
|
||||
if int64(sz) >= left {
|
||||
sz = int(left)
|
||||
}
|
||||
|
||||
// Perform the actual write.
|
||||
n, err = w.Write(buf.ReadSlice()[offset : offset+sz])
|
||||
done += int64(n)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
// Reset spill-over.
|
||||
offset = 0
|
||||
}
|
||||
return done, err
|
||||
}
|
||||
|
||||
// A Range specifies a range of buffer.
|
||||
type Range struct {
|
||||
begin int
|
||||
end int
|
||||
}
|
||||
|
||||
// Intersect returns the intersection of x and y.
|
||||
func (x Range) Intersect(y Range) Range {
|
||||
if x.begin < y.begin {
|
||||
x.begin = y.begin
|
||||
}
|
||||
if x.end > y.end {
|
||||
x.end = y.end
|
||||
}
|
||||
if x.begin >= x.end {
|
||||
return Range{}
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// Offset returns x offset by off.
|
||||
func (x Range) Offset(off int) Range {
|
||||
x.begin += off
|
||||
x.end += off
|
||||
return x
|
||||
}
|
||||
|
||||
// Len returns the length of x.
|
||||
func (x Range) Len() int {
|
||||
l := x.end - x.begin
|
||||
if l < 0 {
|
||||
l = 0
|
||||
}
|
||||
return l
|
||||
}
|
||||
+7
-11
@@ -12,18 +12,14 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tcp
|
||||
package buffer
|
||||
|
||||
import (
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// saveXmitTime is invoked by stateify.
|
||||
func (rc *rackControl) saveXmitTime() unixTime {
|
||||
return unixTime{rc.xmitTime.Unix(), rc.xmitTime.UnixNano()}
|
||||
}
|
||||
|
||||
// loadXmitTime is invoked by stateify.
|
||||
func (rc *rackControl) loadXmitTime(unix unixTime) {
|
||||
rc.xmitTime = time.Unix(unix.second, unix.nano)
|
||||
}
|
||||
// minBatch is the smallest Read or Write operation that the
|
||||
// WriteFromReader and ReadToWriter functions will use.
|
||||
//
|
||||
// This is defined as the size of a native pointer.
|
||||
const minBatch = int(unsafe.Sizeof(uintptr(0)))
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
// 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 context defines an internal context type.
|
||||
//
|
||||
// The given Context conforms to the standard Go context, but mandates
|
||||
// additional methods that are specific to the kernel internals. Note however,
|
||||
// that the Context described by this package carries additional constraints
|
||||
// regarding concurrent access and retaining beyond the scope of a call.
|
||||
//
|
||||
// See the Context type for complete details.
|
||||
package context
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"inet.af/netstack/log"
|
||||
)
|
||||
|
||||
// A Context represents a thread of execution (hereafter "goroutine" to reflect
|
||||
// Go idiosyncrasy). It carries state associated with the goroutine across API
|
||||
// boundaries.
|
||||
//
|
||||
// While Context exists for essentially the same reasons as Go's standard
|
||||
// context.Context, the standard type represents the state of an operation
|
||||
// rather than that of a goroutine. This is a critical distinction:
|
||||
//
|
||||
// - Unlike context.Context, which "may be passed to functions running in
|
||||
// different goroutines", it is *not safe* to use the same Context in multiple
|
||||
// concurrent goroutines.
|
||||
//
|
||||
// - It is *not safe* to retain a Context passed to a function beyond the scope
|
||||
// of that function call.
|
||||
//
|
||||
// In both cases, values extracted from the Context should be used instead.
|
||||
type Context interface {
|
||||
log.Logger
|
||||
context.Context
|
||||
|
||||
ChannelSleeper
|
||||
|
||||
// UninterruptibleSleepStart indicates the beginning of an uninterruptible
|
||||
// sleep state (equivalent to Linux's TASK_UNINTERRUPTIBLE). If deactivate
|
||||
// is true and the Context represents a Task, the Task's AddressSpace is
|
||||
// deactivated.
|
||||
UninterruptibleSleepStart(deactivate bool)
|
||||
|
||||
// UninterruptibleSleepFinish indicates the end of an uninterruptible sleep
|
||||
// state that was begun by a previous call to UninterruptibleSleepStart. If
|
||||
// activate is true and the Context represents a Task, the Task's
|
||||
// AddressSpace is activated. Normally activate is the same value as the
|
||||
// deactivate parameter passed to UninterruptibleSleepStart.
|
||||
UninterruptibleSleepFinish(activate bool)
|
||||
}
|
||||
|
||||
// A ChannelSleeper represents a goroutine that may sleep interruptibly, where
|
||||
// interruption is indicated by a channel becoming readable.
|
||||
type ChannelSleeper interface {
|
||||
// SleepStart is called before going to sleep interruptibly. If SleepStart
|
||||
// returns a non-nil channel and that channel becomes ready for receiving
|
||||
// while the goroutine is sleeping, the goroutine should be woken, and
|
||||
// SleepFinish(false) should be called. Otherwise, SleepFinish(true) should
|
||||
// be called after the goroutine stops sleeping.
|
||||
SleepStart() <-chan struct{}
|
||||
|
||||
// SleepFinish is called after an interruptibly-sleeping goroutine stops
|
||||
// sleeping, as documented by SleepStart.
|
||||
SleepFinish(success bool)
|
||||
|
||||
// Interrupted returns true if the channel returned by SleepStart is
|
||||
// ready for receiving.
|
||||
Interrupted() bool
|
||||
}
|
||||
|
||||
// NoopSleeper is a noop implementation of ChannelSleeper and
|
||||
// Context.UninterruptibleSleep* methods for anonymous embedding in other types
|
||||
// that do not implement special behavior around sleeps.
|
||||
type NoopSleeper struct{}
|
||||
|
||||
// SleepStart implements ChannelSleeper.SleepStart.
|
||||
func (NoopSleeper) SleepStart() <-chan struct{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SleepFinish implements ChannelSleeper.SleepFinish.
|
||||
func (NoopSleeper) SleepFinish(success bool) {}
|
||||
|
||||
// Interrupted implements ChannelSleeper.Interrupted.
|
||||
func (NoopSleeper) Interrupted() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// UninterruptibleSleepStart implements Context.UninterruptibleSleepStart.
|
||||
func (NoopSleeper) UninterruptibleSleepStart(deactivate bool) {}
|
||||
|
||||
// UninterruptibleSleepFinish implements Context.UninterruptibleSleepFinish.
|
||||
func (NoopSleeper) UninterruptibleSleepFinish(activate bool) {}
|
||||
|
||||
// Deadline implements context.Context.Deadline.
|
||||
func (NoopSleeper) Deadline() (time.Time, bool) {
|
||||
return time.Time{}, false
|
||||
}
|
||||
|
||||
// Done implements context.Context.Done.
|
||||
func (NoopSleeper) Done() <-chan struct{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Err returns context.Context.Err.
|
||||
func (NoopSleeper) Err() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// logContext implements basic logging.
|
||||
type logContext struct {
|
||||
log.Logger
|
||||
NoopSleeper
|
||||
}
|
||||
|
||||
// Value implements Context.Value.
|
||||
func (logContext) Value(key interface{}) interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// bgContext is the context returned by context.Background.
|
||||
var bgContext = &logContext{Logger: log.Log()}
|
||||
|
||||
// Background returns an empty context using the default logger.
|
||||
// Generally, one should use the Task as their context when available, or avoid
|
||||
// having to use a context in places where a Task is unavailable.
|
||||
//
|
||||
// Using a Background context for tests is fine, as long as no values are
|
||||
// needed from the context in the tested code paths.
|
||||
func Background() Context {
|
||||
return bgContext
|
||||
}
|
||||
|
||||
// WithValue returns a copy of parent in which the value associated with key is
|
||||
// val.
|
||||
func WithValue(parent Context, key, val interface{}) Context {
|
||||
return &withValue{
|
||||
Context: parent,
|
||||
key: key,
|
||||
val: val,
|
||||
}
|
||||
}
|
||||
|
||||
type withValue struct {
|
||||
Context
|
||||
key interface{}
|
||||
val interface{}
|
||||
}
|
||||
|
||||
// Value implements Context.Value.
|
||||
func (ctx *withValue) Value(key interface{}) interface{} {
|
||||
if key == ctx.key {
|
||||
return ctx.val
|
||||
}
|
||||
return ctx.Context.Value(key)
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
// automatically generated by stateify.
|
||||
|
||||
package context
|
||||
+23
-3
@@ -12,10 +12,16 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// +build go1.13
|
||||
// +build !go1.18
|
||||
//go:build go1.13 && !go1.19
|
||||
// +build go1.13,!go1.19
|
||||
|
||||
// Check type signatures when updating Go version.
|
||||
// //go:linkname directives type-checked by checklinkname. Any other
|
||||
// non-linkname assumptions outside the Go 1 compatibility guarantee should
|
||||
// have an accompanied vet check or version guard build tag.
|
||||
|
||||
// Check type signatures and Noescape when updating Go version.
|
||||
//
|
||||
// TODO(b/165820485): add these checks to checklinkname.
|
||||
|
||||
// Package gohacks contains utilities for subverting the Go compiler.
|
||||
package gohacks
|
||||
@@ -75,3 +81,17 @@ func StringFromImmutableBytes(bs []byte) string {
|
||||
// strings.Builder.String().
|
||||
return *(*string)(unsafe.Pointer(&bs))
|
||||
}
|
||||
|
||||
// Note that go:linkname silently doesn't work if the local name is exported,
|
||||
// necessitating an indirection for exported functions.
|
||||
|
||||
// Memmove is runtime.memmove, exported for SeqAtomicLoad/SeqAtomicTryLoad<T>.
|
||||
//
|
||||
//go:nosplit
|
||||
func Memmove(to, from unsafe.Pointer, n uintptr) {
|
||||
memmove(to, from, n)
|
||||
}
|
||||
|
||||
//go:linkname memmove runtime.memmove
|
||||
//go:noescape
|
||||
func memmove(to, from unsafe.Pointer, n uintptr)
|
||||
Vendored
+2
-2
@@ -12,8 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// +build go1.12
|
||||
// +build !go1.18
|
||||
//go:build go1.12 && !go1.19
|
||||
// +build go1.12,!go1.19
|
||||
|
||||
// Check type signatures when updating Go version.
|
||||
|
||||
|
||||
Vendored
+1
@@ -12,6 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !linux
|
||||
// +build !linux
|
||||
|
||||
// Package rand implements a cryptographically secure pseudorandom number
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// automatically generated by stateify.
|
||||
|
||||
//go:build !linux
|
||||
// +build !linux
|
||||
|
||||
package rand
|
||||
+530
@@ -0,0 +1,530 @@
|
||||
// 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 refs defines an interface for reference counted objects. It
|
||||
// also provides a drop-in implementation called AtomicRefCount.
|
||||
package refs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
|
||||
"inet.af/netstack/context"
|
||||
"inet.af/netstack/log"
|
||||
"inet.af/netstack/sync"
|
||||
)
|
||||
|
||||
// RefCounter is the interface to be implemented by objects that are reference
|
||||
// counted.
|
||||
//
|
||||
// TODO(gvisor.dev/issue/1624): Get rid of most of this package and replace it
|
||||
// with refsvfs2.
|
||||
type RefCounter interface {
|
||||
// IncRef increments the reference counter on the object.
|
||||
IncRef()
|
||||
|
||||
// DecRef decrements the reference counter on the object.
|
||||
//
|
||||
// Note that AtomicRefCounter.DecRef() does not support destructors.
|
||||
// If a type has a destructor, it must implement its own DecRef()
|
||||
// method and call AtomicRefCounter.DecRefWithDestructor(destructor).
|
||||
DecRef(ctx context.Context)
|
||||
|
||||
// TryIncRef attempts to increase the reference counter on the object,
|
||||
// but may fail if all references have already been dropped. This
|
||||
// should be used only in special circumstances, such as WeakRefs.
|
||||
TryIncRef() bool
|
||||
|
||||
// addWeakRef adds the given weak reference. Note that you should have a
|
||||
// reference to the object when calling this method.
|
||||
addWeakRef(*WeakRef)
|
||||
|
||||
// dropWeakRef drops the given weak reference. Note that you should have
|
||||
// a reference to the object when calling this method.
|
||||
dropWeakRef(*WeakRef)
|
||||
}
|
||||
|
||||
// A WeakRefUser is notified when the last non-weak reference is dropped.
|
||||
type WeakRefUser interface {
|
||||
// WeakRefGone is called when the last non-weak reference is dropped.
|
||||
WeakRefGone(ctx context.Context)
|
||||
}
|
||||
|
||||
// WeakRef is a weak reference.
|
||||
//
|
||||
// +stateify savable
|
||||
type WeakRef struct {
|
||||
weakRefEntry `state:"nosave"`
|
||||
|
||||
// obj is an atomic value that points to the refCounter.
|
||||
obj atomic.Value `state:".(savedReference)"`
|
||||
|
||||
// user is notified when the weak ref is zapped by the object getting
|
||||
// destroyed.
|
||||
user WeakRefUser
|
||||
}
|
||||
|
||||
// weakRefPool is a pool of weak references to avoid allocations on the hot path.
|
||||
var weakRefPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &WeakRef{}
|
||||
},
|
||||
}
|
||||
|
||||
// NewWeakRef acquires a weak reference for the given object.
|
||||
//
|
||||
// An optional user will be notified when the last non-weak reference is
|
||||
// dropped.
|
||||
//
|
||||
// Note that you must hold a reference to the object prior to getting a weak
|
||||
// reference. (But you may drop the non-weak reference after that.)
|
||||
func NewWeakRef(rc RefCounter, u WeakRefUser) *WeakRef {
|
||||
w := weakRefPool.Get().(*WeakRef)
|
||||
w.init(rc, u)
|
||||
return w
|
||||
}
|
||||
|
||||
// get attempts to get a normal reference to the underlying object, and returns
|
||||
// the object. If this weak reference has already been zapped (the object has
|
||||
// been destroyed) then false is returned. If the object still exists, then
|
||||
// true is returned.
|
||||
func (w *WeakRef) get() (RefCounter, bool) {
|
||||
rc := w.obj.Load().(RefCounter)
|
||||
if v := reflect.ValueOf(rc); v == reflect.Zero(v.Type()) {
|
||||
// This pointer has already been zapped by zap() below. We do
|
||||
// this to ensure that the GC can collect the underlying
|
||||
// RefCounter objects and they don't hog resources.
|
||||
return nil, false
|
||||
}
|
||||
if !rc.TryIncRef() {
|
||||
return nil, true
|
||||
}
|
||||
return rc, true
|
||||
}
|
||||
|
||||
// Get attempts to get a normal reference to the underlying object, and returns
|
||||
// the object. If this fails (the object no longer exists), then nil will be
|
||||
// returned instead.
|
||||
func (w *WeakRef) Get() RefCounter {
|
||||
rc, _ := w.get()
|
||||
return rc
|
||||
}
|
||||
|
||||
// Drop drops this weak reference. You should always call drop when you are
|
||||
// finished with the weak reference. You may not use this object after calling
|
||||
// drop.
|
||||
func (w *WeakRef) Drop(ctx context.Context) {
|
||||
rc, ok := w.get()
|
||||
if !ok {
|
||||
// We've been zapped already. When the refcounter has called
|
||||
// zap, we're guaranteed it's not holding references.
|
||||
weakRefPool.Put(w)
|
||||
return
|
||||
}
|
||||
if rc == nil {
|
||||
// The object is in the process of being destroyed. We can't
|
||||
// remove this from the object's list, nor can we return this
|
||||
// object to the pool. It'll just be garbage collected. This is
|
||||
// a rare edge case, so it's not a big deal.
|
||||
return
|
||||
}
|
||||
|
||||
// At this point, we have a reference on the object. So destruction
|
||||
// of the object (and zapping this weak reference) can't race here.
|
||||
rc.dropWeakRef(w)
|
||||
|
||||
// And now aren't on the object's list of weak references. So it won't
|
||||
// zap us if this causes the reference count to drop to zero.
|
||||
rc.DecRef(ctx)
|
||||
|
||||
// Return to the pool.
|
||||
weakRefPool.Put(w)
|
||||
}
|
||||
|
||||
// init initializes this weak reference.
|
||||
func (w *WeakRef) init(rc RefCounter, u WeakRefUser) {
|
||||
// Reset the contents of the weak reference.
|
||||
// This is important because we are reseting the atomic value type.
|
||||
// Otherwise, we could panic here if obj is different than what it was
|
||||
// the last time this was used.
|
||||
*w = WeakRef{}
|
||||
w.user = u
|
||||
w.obj.Store(rc)
|
||||
|
||||
// In the load path, we may already have a nil value. So we need to
|
||||
// check whether or not that is the case before calling addWeakRef.
|
||||
if v := reflect.ValueOf(rc); v != reflect.Zero(v.Type()) {
|
||||
rc.addWeakRef(w)
|
||||
}
|
||||
}
|
||||
|
||||
// zap zaps this weak reference.
|
||||
func (w *WeakRef) zap() {
|
||||
// We need to be careful about types here.
|
||||
// So reflect is involved. But it's not that bad.
|
||||
rc := w.obj.Load()
|
||||
typ := reflect.TypeOf(rc)
|
||||
w.obj.Store(reflect.Zero(typ).Interface())
|
||||
}
|
||||
|
||||
// AtomicRefCount keeps a reference count using atomic operations and calls the
|
||||
// destructor when the count reaches zero.
|
||||
//
|
||||
// Do not use AtomicRefCount for new ref-counted objects! It is deprecated in
|
||||
// favor of the refsvfs2 package.
|
||||
//
|
||||
// N.B. To allow the zero-object to be initialized, the count is offset by
|
||||
// 1, that is, when refCount is n, there are really n+1 references.
|
||||
//
|
||||
// +stateify savable
|
||||
type AtomicRefCount struct {
|
||||
// refCount is composed of two fields:
|
||||
//
|
||||
// [32-bit speculative references]:[32-bit real references]
|
||||
//
|
||||
// Speculative references are used for TryIncRef, to avoid a
|
||||
// CompareAndSwap loop. See IncRef, DecRef and TryIncRef for details of
|
||||
// how these fields are used.
|
||||
refCount int64
|
||||
|
||||
// name is the name of the type which owns this ref count.
|
||||
//
|
||||
// name is immutable after EnableLeakCheck is called.
|
||||
name string
|
||||
|
||||
// stack optionally records the caller of EnableLeakCheck.
|
||||
//
|
||||
// stack is immutable after EnableLeakCheck is called.
|
||||
stack []uintptr
|
||||
|
||||
// mu protects the list below.
|
||||
mu sync.Mutex `state:"nosave"`
|
||||
|
||||
// weakRefs is our collection of weak references.
|
||||
weakRefs weakRefList `state:"nosave"`
|
||||
}
|
||||
|
||||
// LeakMode configures the leak checker.
|
||||
type LeakMode uint32
|
||||
|
||||
// TODO(gvisor.dev/issue/1624): Simplify down to two modes (on/off) once vfs1
|
||||
// ref counting is gone.
|
||||
const (
|
||||
// UninitializedLeakChecking indicates that the leak checker has not yet been initialized.
|
||||
UninitializedLeakChecking LeakMode = iota
|
||||
|
||||
// NoLeakChecking indicates that no effort should be made to check for
|
||||
// leaks.
|
||||
NoLeakChecking
|
||||
|
||||
// LeaksLogWarning indicates that a warning should be logged when leaks
|
||||
// are found.
|
||||
LeaksLogWarning
|
||||
|
||||
// LeaksLogTraces indicates that a trace collected during allocation
|
||||
// should be logged when leaks are found.
|
||||
LeaksLogTraces
|
||||
)
|
||||
|
||||
// Set implements flag.Value.
|
||||
func (l *LeakMode) Set(v string) error {
|
||||
switch v {
|
||||
case "disabled":
|
||||
*l = NoLeakChecking
|
||||
case "log-names":
|
||||
*l = LeaksLogWarning
|
||||
case "log-traces":
|
||||
*l = LeaksLogTraces
|
||||
default:
|
||||
return fmt.Errorf("invalid ref leak mode %q", v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get implements flag.Value.
|
||||
func (l *LeakMode) Get() interface{} {
|
||||
return *l
|
||||
}
|
||||
|
||||
// String implements flag.Value.
|
||||
func (l LeakMode) String() string {
|
||||
switch l {
|
||||
case UninitializedLeakChecking:
|
||||
return "uninitialized"
|
||||
case NoLeakChecking:
|
||||
return "disabled"
|
||||
case LeaksLogWarning:
|
||||
return "log-names"
|
||||
case LeaksLogTraces:
|
||||
return "log-traces"
|
||||
}
|
||||
panic(fmt.Sprintf("invalid ref leak mode %d", l))
|
||||
}
|
||||
|
||||
// leakMode stores the current mode for the reference leak checker.
|
||||
//
|
||||
// Values must be one of the LeakMode values.
|
||||
//
|
||||
// leakMode must be accessed atomically.
|
||||
var leakMode uint32
|
||||
|
||||
// SetLeakMode configures the reference leak checker.
|
||||
func SetLeakMode(mode LeakMode) {
|
||||
atomic.StoreUint32(&leakMode, uint32(mode))
|
||||
}
|
||||
|
||||
// GetLeakMode returns the current leak mode.
|
||||
func GetLeakMode() LeakMode {
|
||||
return LeakMode(atomic.LoadUint32(&leakMode))
|
||||
}
|
||||
|
||||
const maxStackFrames = 40
|
||||
|
||||
type fileLine struct {
|
||||
file string
|
||||
line int
|
||||
}
|
||||
|
||||
// A stackKey is a representation of a stack frame for use as a map key.
|
||||
//
|
||||
// The fileLine type is used as PC values seem to vary across collections, even
|
||||
// for the same call stack.
|
||||
type stackKey [maxStackFrames]fileLine
|
||||
|
||||
var stackCache = struct {
|
||||
sync.Mutex
|
||||
entries map[stackKey][]uintptr
|
||||
}{entries: map[stackKey][]uintptr{}}
|
||||
|
||||
func makeStackKey(pcs []uintptr) stackKey {
|
||||
frames := runtime.CallersFrames(pcs)
|
||||
var key stackKey
|
||||
keySlice := key[:0]
|
||||
for {
|
||||
frame, more := frames.Next()
|
||||
keySlice = append(keySlice, fileLine{frame.File, frame.Line})
|
||||
|
||||
if !more || len(keySlice) == len(key) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
// RecordStack constructs and returns the PCs on the current stack.
|
||||
func RecordStack() []uintptr {
|
||||
pcs := make([]uintptr, maxStackFrames)
|
||||
n := runtime.Callers(1, pcs)
|
||||
if n == 0 {
|
||||
// No pcs available. Stop now.
|
||||
//
|
||||
// This can happen if the first argument to runtime.Callers
|
||||
// is large.
|
||||
return nil
|
||||
}
|
||||
pcs = pcs[:n]
|
||||
key := makeStackKey(pcs)
|
||||
stackCache.Lock()
|
||||
v, ok := stackCache.entries[key]
|
||||
if !ok {
|
||||
// Reallocate to prevent pcs from escaping.
|
||||
v = append([]uintptr(nil), pcs...)
|
||||
stackCache.entries[key] = v
|
||||
}
|
||||
stackCache.Unlock()
|
||||
return v
|
||||
}
|
||||
|
||||
// FormatStack converts the given stack into a readable format.
|
||||
func FormatStack(pcs []uintptr) string {
|
||||
frames := runtime.CallersFrames(pcs)
|
||||
var trace bytes.Buffer
|
||||
for {
|
||||
frame, more := frames.Next()
|
||||
fmt.Fprintf(&trace, "%s:%d: %s\n", frame.File, frame.Line, frame.Function)
|
||||
|
||||
if !more {
|
||||
break
|
||||
}
|
||||
}
|
||||
return trace.String()
|
||||
}
|
||||
|
||||
func (r *AtomicRefCount) finalize() {
|
||||
var note string
|
||||
switch LeakMode(atomic.LoadUint32(&leakMode)) {
|
||||
case NoLeakChecking:
|
||||
return
|
||||
case UninitializedLeakChecking:
|
||||
note = "(Leak checker uninitialized): "
|
||||
}
|
||||
if n := r.ReadRefs(); n != 0 {
|
||||
msg := fmt.Sprintf("%sAtomicRefCount %p owned by %q garbage collected with ref count of %d (want 0)", note, r, r.name, n)
|
||||
if len(r.stack) != 0 {
|
||||
msg += ":\nCaller:\n" + FormatStack(r.stack)
|
||||
} else {
|
||||
msg += " (enable trace logging to debug)"
|
||||
}
|
||||
log.Warningf(msg)
|
||||
}
|
||||
}
|
||||
|
||||
// EnableLeakCheck checks for reference leaks when the AtomicRefCount gets
|
||||
// garbage collected.
|
||||
//
|
||||
// This function adds a finalizer to the AtomicRefCount, so the AtomicRefCount
|
||||
// must be at the beginning of its parent.
|
||||
//
|
||||
// name is a friendly name that will be listed as the owner of the
|
||||
// AtomicRefCount in logs. It should be the name of the parent type, including
|
||||
// package.
|
||||
func (r *AtomicRefCount) EnableLeakCheck(name string) {
|
||||
if name == "" {
|
||||
panic("invalid name")
|
||||
}
|
||||
switch LeakMode(atomic.LoadUint32(&leakMode)) {
|
||||
case NoLeakChecking:
|
||||
return
|
||||
case LeaksLogTraces:
|
||||
r.stack = RecordStack()
|
||||
}
|
||||
r.name = name
|
||||
runtime.SetFinalizer(r, (*AtomicRefCount).finalize)
|
||||
}
|
||||
|
||||
// ReadRefs returns the current number of references. The returned count is
|
||||
// inherently racy and is unsafe to use without external synchronization.
|
||||
func (r *AtomicRefCount) ReadRefs() int64 {
|
||||
// Account for the internal -1 offset on refcounts.
|
||||
return atomic.LoadInt64(&r.refCount) + 1
|
||||
}
|
||||
|
||||
// IncRef increments this object's reference count. While the count is kept
|
||||
// greater than zero, the destructor doesn't get called.
|
||||
//
|
||||
// The sanity check here is limited to real references, since if they have
|
||||
// dropped beneath zero then the object should have been destroyed.
|
||||
//
|
||||
//go:nosplit
|
||||
func (r *AtomicRefCount) IncRef() {
|
||||
if v := atomic.AddInt64(&r.refCount, 1); v <= 0 {
|
||||
panic("Incrementing non-positive ref count")
|
||||
}
|
||||
}
|
||||
|
||||
// TryIncRef attempts to increment the reference count, *unless the count has
|
||||
// already reached zero*. If false is returned, then the object has already
|
||||
// been destroyed, and the weak reference is no longer valid. If true if
|
||||
// returned then a valid reference is now held on the object.
|
||||
//
|
||||
// To do this safely without a loop, a speculative reference is first acquired
|
||||
// on the object. This allows multiple concurrent TryIncRef calls to
|
||||
// distinguish other TryIncRef calls from genuine references held.
|
||||
//
|
||||
//go:nosplit
|
||||
func (r *AtomicRefCount) TryIncRef() bool {
|
||||
const speculativeRef = 1 << 32
|
||||
v := atomic.AddInt64(&r.refCount, speculativeRef)
|
||||
if int32(v) < 0 {
|
||||
// This object has already been freed.
|
||||
atomic.AddInt64(&r.refCount, -speculativeRef)
|
||||
return false
|
||||
}
|
||||
|
||||
// Turn into a real reference.
|
||||
atomic.AddInt64(&r.refCount, -speculativeRef+1)
|
||||
return true
|
||||
}
|
||||
|
||||
// addWeakRef adds the given weak reference.
|
||||
func (r *AtomicRefCount) addWeakRef(w *WeakRef) {
|
||||
r.mu.Lock()
|
||||
r.weakRefs.PushBack(w)
|
||||
r.mu.Unlock()
|
||||
}
|
||||
|
||||
// dropWeakRef drops the given weak reference.
|
||||
func (r *AtomicRefCount) dropWeakRef(w *WeakRef) {
|
||||
r.mu.Lock()
|
||||
r.weakRefs.Remove(w)
|
||||
r.mu.Unlock()
|
||||
}
|
||||
|
||||
// DecRefWithDestructor decrements the object's reference count. If the
|
||||
// resulting count is negative and the destructor is not nil, then the
|
||||
// destructor will be called.
|
||||
//
|
||||
// Note that speculative references are counted here. Since they were added
|
||||
// prior to real references reaching zero, they will successfully convert to
|
||||
// real references. In other words, we see speculative references only in the
|
||||
// following case:
|
||||
//
|
||||
// A: TryIncRef [speculative increase => sees non-negative references]
|
||||
// B: DecRef [real decrease]
|
||||
// A: TryIncRef [transform speculative to real]
|
||||
//
|
||||
//go:nosplit
|
||||
func (r *AtomicRefCount) DecRefWithDestructor(ctx context.Context, destroy func(context.Context)) {
|
||||
switch v := atomic.AddInt64(&r.refCount, -1); {
|
||||
case v < -1:
|
||||
panic("Decrementing non-positive ref count")
|
||||
|
||||
case v == -1:
|
||||
// Zap weak references. Note that at this point, all weak
|
||||
// references are already invalid. That is, TryIncRef() will
|
||||
// return false due to the reference count check.
|
||||
r.mu.Lock()
|
||||
for !r.weakRefs.Empty() {
|
||||
w := r.weakRefs.Front()
|
||||
// Capture the callback because w cannot be touched
|
||||
// after it's zapped -- the owner is free it reuse it
|
||||
// after that.
|
||||
user := w.user
|
||||
r.weakRefs.Remove(w)
|
||||
w.zap()
|
||||
|
||||
if user != nil {
|
||||
r.mu.Unlock()
|
||||
user.WeakRefGone(ctx)
|
||||
r.mu.Lock()
|
||||
}
|
||||
}
|
||||
r.mu.Unlock()
|
||||
|
||||
// Call the destructor.
|
||||
if destroy != nil {
|
||||
destroy(ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DecRef decrements this object's reference count.
|
||||
//
|
||||
//go:nosplit
|
||||
func (r *AtomicRefCount) DecRef(ctx context.Context) {
|
||||
r.DecRefWithDestructor(ctx, nil)
|
||||
}
|
||||
|
||||
// OnExit is called on sandbox exit. It runs GC to enqueue refcount finalizers,
|
||||
// which check for reference leaks. There is no way to guarantee that every
|
||||
// finalizer will run before exiting, but this at least ensures that they will
|
||||
// be discovered/enqueued by GC.
|
||||
func OnExit() {
|
||||
if LeakMode(atomic.LoadUint32(&leakMode)) != NoLeakChecking {
|
||||
runtime.GC()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// 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 refs
|
||||
|
||||
// +stateify savable
|
||||
type savedReference struct {
|
||||
obj interface{}
|
||||
}
|
||||
|
||||
func (w *WeakRef) saveObj() savedReference {
|
||||
// We load the object directly, because it is typed. This will be
|
||||
// serialized and loaded as a typed value.
|
||||
return savedReference{w.obj.Load()}
|
||||
}
|
||||
|
||||
func (w *WeakRef) loadObj(v savedReference) {
|
||||
// See note above. This will be serialized and loaded typed. So we're okay
|
||||
// as long as refs aren't changing during save and load (which they should
|
||||
// not be).
|
||||
//
|
||||
// w.user is loaded before loadObj is called.
|
||||
w.init(v.obj.(RefCounter), w.user)
|
||||
}
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
// automatically generated by stateify.
|
||||
|
||||
package refs
|
||||
|
||||
import (
|
||||
"inet.af/netstack/state"
|
||||
)
|
||||
|
||||
func (w *WeakRef) StateTypeName() string {
|
||||
return "pkg/refs.WeakRef"
|
||||
}
|
||||
|
||||
func (w *WeakRef) StateFields() []string {
|
||||
return []string{
|
||||
"obj",
|
||||
"user",
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WeakRef) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (w *WeakRef) StateSave(stateSinkObject state.Sink) {
|
||||
w.beforeSave()
|
||||
var objValue savedReference
|
||||
objValue = w.saveObj()
|
||||
stateSinkObject.SaveValue(0, objValue)
|
||||
stateSinkObject.Save(1, &w.user)
|
||||
}
|
||||
|
||||
func (w *WeakRef) afterLoad() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (w *WeakRef) StateLoad(stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(1, &w.user)
|
||||
stateSourceObject.LoadValue(0, new(savedReference), func(y interface{}) { w.loadObj(y.(savedReference)) })
|
||||
}
|
||||
|
||||
func (r *AtomicRefCount) StateTypeName() string {
|
||||
return "pkg/refs.AtomicRefCount"
|
||||
}
|
||||
|
||||
func (r *AtomicRefCount) StateFields() []string {
|
||||
return []string{
|
||||
"refCount",
|
||||
"name",
|
||||
"stack",
|
||||
}
|
||||
}
|
||||
|
||||
func (r *AtomicRefCount) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (r *AtomicRefCount) StateSave(stateSinkObject state.Sink) {
|
||||
r.beforeSave()
|
||||
stateSinkObject.Save(0, &r.refCount)
|
||||
stateSinkObject.Save(1, &r.name)
|
||||
stateSinkObject.Save(2, &r.stack)
|
||||
}
|
||||
|
||||
func (r *AtomicRefCount) afterLoad() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (r *AtomicRefCount) StateLoad(stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &r.refCount)
|
||||
stateSourceObject.Load(1, &r.name)
|
||||
stateSourceObject.Load(2, &r.stack)
|
||||
}
|
||||
|
||||
func (s *savedReference) StateTypeName() string {
|
||||
return "pkg/refs.savedReference"
|
||||
}
|
||||
|
||||
func (s *savedReference) StateFields() []string {
|
||||
return []string{
|
||||
"obj",
|
||||
}
|
||||
}
|
||||
|
||||
func (s *savedReference) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (s *savedReference) StateSave(stateSinkObject state.Sink) {
|
||||
s.beforeSave()
|
||||
stateSinkObject.Save(0, &s.obj)
|
||||
}
|
||||
|
||||
func (s *savedReference) afterLoad() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (s *savedReference) StateLoad(stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &s.obj)
|
||||
}
|
||||
|
||||
func (l *weakRefList) StateTypeName() string {
|
||||
return "pkg/refs.weakRefList"
|
||||
}
|
||||
|
||||
func (l *weakRefList) StateFields() []string {
|
||||
return []string{
|
||||
"head",
|
||||
"tail",
|
||||
}
|
||||
}
|
||||
|
||||
func (l *weakRefList) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (l *weakRefList) StateSave(stateSinkObject state.Sink) {
|
||||
l.beforeSave()
|
||||
stateSinkObject.Save(0, &l.head)
|
||||
stateSinkObject.Save(1, &l.tail)
|
||||
}
|
||||
|
||||
func (l *weakRefList) afterLoad() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (l *weakRefList) StateLoad(stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &l.head)
|
||||
stateSourceObject.Load(1, &l.tail)
|
||||
}
|
||||
|
||||
func (e *weakRefEntry) StateTypeName() string {
|
||||
return "pkg/refs.weakRefEntry"
|
||||
}
|
||||
|
||||
func (e *weakRefEntry) StateFields() []string {
|
||||
return []string{
|
||||
"next",
|
||||
"prev",
|
||||
}
|
||||
}
|
||||
|
||||
func (e *weakRefEntry) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *weakRefEntry) StateSave(stateSinkObject state.Sink) {
|
||||
e.beforeSave()
|
||||
stateSinkObject.Save(0, &e.next)
|
||||
stateSinkObject.Save(1, &e.prev)
|
||||
}
|
||||
|
||||
func (e *weakRefEntry) afterLoad() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *weakRefEntry) StateLoad(stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &e.next)
|
||||
stateSourceObject.Load(1, &e.prev)
|
||||
}
|
||||
|
||||
func init() {
|
||||
state.Register((*WeakRef)(nil))
|
||||
state.Register((*AtomicRefCount)(nil))
|
||||
state.Register((*savedReference)(nil))
|
||||
state.Register((*weakRefList)(nil))
|
||||
state.Register((*weakRefEntry)(nil))
|
||||
}
|
||||
+221
@@ -0,0 +1,221 @@
|
||||
package refs
|
||||
|
||||
// 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 weakRefElementMapper struct{}
|
||||
|
||||
// linkerFor maps an Element to a Linker.
|
||||
//
|
||||
// This default implementation should be inlined.
|
||||
//
|
||||
//go:nosplit
|
||||
func (weakRefElementMapper) linkerFor(elem *WeakRef) *WeakRef { 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 weakRefList struct {
|
||||
head *WeakRef
|
||||
tail *WeakRef
|
||||
}
|
||||
|
||||
// Reset resets list l to the empty state.
|
||||
func (l *weakRefList) Reset() {
|
||||
l.head = nil
|
||||
l.tail = nil
|
||||
}
|
||||
|
||||
// Empty returns true iff the list is empty.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *weakRefList) Empty() bool {
|
||||
return l.head == nil
|
||||
}
|
||||
|
||||
// Front returns the first element of list l or nil.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *weakRefList) Front() *WeakRef {
|
||||
return l.head
|
||||
}
|
||||
|
||||
// Back returns the last element of list l or nil.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *weakRefList) Back() *WeakRef {
|
||||
return l.tail
|
||||
}
|
||||
|
||||
// Len returns the number of elements in the list.
|
||||
//
|
||||
// NOTE: This is an O(n) operation.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *weakRefList) Len() (count int) {
|
||||
for e := l.Front(); e != nil; e = (weakRefElementMapper{}.linkerFor(e)).Next() {
|
||||
count++
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// PushFront inserts the element e at the front of list l.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *weakRefList) PushFront(e *WeakRef) {
|
||||
linker := weakRefElementMapper{}.linkerFor(e)
|
||||
linker.SetNext(l.head)
|
||||
linker.SetPrev(nil)
|
||||
if l.head != nil {
|
||||
weakRefElementMapper{}.linkerFor(l.head).SetPrev(e)
|
||||
} else {
|
||||
l.tail = e
|
||||
}
|
||||
|
||||
l.head = e
|
||||
}
|
||||
|
||||
// PushBack inserts the element e at the back of list l.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *weakRefList) PushBack(e *WeakRef) {
|
||||
linker := weakRefElementMapper{}.linkerFor(e)
|
||||
linker.SetNext(nil)
|
||||
linker.SetPrev(l.tail)
|
||||
if l.tail != nil {
|
||||
weakRefElementMapper{}.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 *weakRefList) PushBackList(m *weakRefList) {
|
||||
if l.head == nil {
|
||||
l.head = m.head
|
||||
l.tail = m.tail
|
||||
} else if m.head != nil {
|
||||
weakRefElementMapper{}.linkerFor(l.tail).SetNext(m.head)
|
||||
weakRefElementMapper{}.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 *weakRefList) InsertAfter(b, e *WeakRef) {
|
||||
bLinker := weakRefElementMapper{}.linkerFor(b)
|
||||
eLinker := weakRefElementMapper{}.linkerFor(e)
|
||||
|
||||
a := bLinker.Next()
|
||||
|
||||
eLinker.SetNext(a)
|
||||
eLinker.SetPrev(b)
|
||||
bLinker.SetNext(e)
|
||||
|
||||
if a != nil {
|
||||
weakRefElementMapper{}.linkerFor(a).SetPrev(e)
|
||||
} else {
|
||||
l.tail = e
|
||||
}
|
||||
}
|
||||
|
||||
// InsertBefore inserts e before a.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *weakRefList) InsertBefore(a, e *WeakRef) {
|
||||
aLinker := weakRefElementMapper{}.linkerFor(a)
|
||||
eLinker := weakRefElementMapper{}.linkerFor(e)
|
||||
|
||||
b := aLinker.Prev()
|
||||
eLinker.SetNext(a)
|
||||
eLinker.SetPrev(b)
|
||||
aLinker.SetPrev(e)
|
||||
|
||||
if b != nil {
|
||||
weakRefElementMapper{}.linkerFor(b).SetNext(e)
|
||||
} else {
|
||||
l.head = e
|
||||
}
|
||||
}
|
||||
|
||||
// Remove removes e from l.
|
||||
//
|
||||
//go:nosplit
|
||||
func (l *weakRefList) Remove(e *WeakRef) {
|
||||
linker := weakRefElementMapper{}.linkerFor(e)
|
||||
prev := linker.Prev()
|
||||
next := linker.Next()
|
||||
|
||||
if prev != nil {
|
||||
weakRefElementMapper{}.linkerFor(prev).SetNext(next)
|
||||
} else if l.head == e {
|
||||
l.head = next
|
||||
}
|
||||
|
||||
if next != nil {
|
||||
weakRefElementMapper{}.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 weakRefEntry struct {
|
||||
next *WeakRef
|
||||
prev *WeakRef
|
||||
}
|
||||
|
||||
// Next returns the entry that follows e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *weakRefEntry) Next() *WeakRef {
|
||||
return e.next
|
||||
}
|
||||
|
||||
// Prev returns the entry that precedes e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *weakRefEntry) Prev() *WeakRef {
|
||||
return e.prev
|
||||
}
|
||||
|
||||
// SetNext assigns 'entry' as the entry that follows e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *weakRefEntry) SetNext(elem *WeakRef) {
|
||||
e.next = elem
|
||||
}
|
||||
|
||||
// SetPrev assigns 'entry' as the entry that precedes e in the list.
|
||||
//
|
||||
//go:nosplit
|
||||
func (e *weakRefEntry) SetPrev(elem *WeakRef) {
|
||||
e.prev = elem
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
// 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 refsvfs2 defines an interface for a reference-counted object.
|
||||
package refsvfs2
|
||||
|
||||
import (
|
||||
"inet.af/netstack/context"
|
||||
)
|
||||
|
||||
// RefCounter is the interface to be implemented by objects that are reference
|
||||
// counted.
|
||||
type RefCounter interface {
|
||||
// IncRef increments the reference counter on the object.
|
||||
IncRef()
|
||||
|
||||
// DecRef decrements the object's reference count. Users of refs_template.Refs
|
||||
// may specify a destructor to be called once the reference count reaches zero.
|
||||
DecRef(ctx context.Context)
|
||||
}
|
||||
|
||||
// TryRefCounter is like RefCounter but allow the ref increment to be tried.
|
||||
type TryRefCounter interface {
|
||||
RefCounter
|
||||
|
||||
// TryIncRef attempts to increment the reference count, but may fail if all
|
||||
// references have already been dropped, in which case it returns false. If
|
||||
// true is returned, then a valid reference is now held on the object.
|
||||
TryIncRef() bool
|
||||
}
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
// 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 refsvfs2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"inet.af/netstack/log"
|
||||
refs_vfs1 "inet.af/netstack/refs"
|
||||
"inet.af/netstack/sync"
|
||||
)
|
||||
|
||||
var (
|
||||
// liveObjects is a global map of reference-counted objects. Objects are
|
||||
// inserted when leak check is enabled, and they are removed when they are
|
||||
// destroyed. It is protected by liveObjectsMu.
|
||||
liveObjects map[CheckedObject]struct{}
|
||||
liveObjectsMu sync.Mutex
|
||||
)
|
||||
|
||||
// CheckedObject represents a reference-counted object with an informative
|
||||
// leak detection message.
|
||||
type CheckedObject interface {
|
||||
// RefType is the type of the reference-counted object.
|
||||
RefType() string
|
||||
|
||||
// LeakMessage supplies a warning to be printed upon leak detection.
|
||||
LeakMessage() string
|
||||
|
||||
// LogRefs indicates whether reference-related events should be logged.
|
||||
LogRefs() bool
|
||||
}
|
||||
|
||||
func init() {
|
||||
liveObjects = make(map[CheckedObject]struct{})
|
||||
}
|
||||
|
||||
// leakCheckEnabled returns whether leak checking is enabled. The following
|
||||
// functions should only be called if it returns true.
|
||||
func leakCheckEnabled() bool {
|
||||
return refs_vfs1.GetLeakMode() != refs_vfs1.NoLeakChecking
|
||||
}
|
||||
|
||||
// Register adds obj to the live object map.
|
||||
func Register(obj CheckedObject) {
|
||||
if leakCheckEnabled() {
|
||||
liveObjectsMu.Lock()
|
||||
if _, ok := liveObjects[obj]; ok {
|
||||
panic(fmt.Sprintf("Unexpected entry in leak checking map: reference %p already added", obj))
|
||||
}
|
||||
liveObjects[obj] = struct{}{}
|
||||
liveObjectsMu.Unlock()
|
||||
if leakCheckEnabled() && obj.LogRefs() {
|
||||
logEvent(obj, "registered")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unregister removes obj from the live object map.
|
||||
func Unregister(obj CheckedObject) {
|
||||
if leakCheckEnabled() {
|
||||
liveObjectsMu.Lock()
|
||||
defer liveObjectsMu.Unlock()
|
||||
if _, ok := liveObjects[obj]; !ok {
|
||||
panic(fmt.Sprintf("Expected to find entry in leak checking map for reference %p", obj))
|
||||
}
|
||||
delete(liveObjects, obj)
|
||||
if leakCheckEnabled() && obj.LogRefs() {
|
||||
logEvent(obj, "unregistered")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LogIncRef logs a reference increment.
|
||||
func LogIncRef(obj CheckedObject, refs int64) {
|
||||
if leakCheckEnabled() && obj.LogRefs() {
|
||||
logEvent(obj, fmt.Sprintf("IncRef to %d", refs))
|
||||
}
|
||||
}
|
||||
|
||||
// LogTryIncRef logs a successful TryIncRef call.
|
||||
func LogTryIncRef(obj CheckedObject, refs int64) {
|
||||
if leakCheckEnabled() && obj.LogRefs() {
|
||||
logEvent(obj, fmt.Sprintf("TryIncRef to %d", refs))
|
||||
}
|
||||
}
|
||||
|
||||
// LogDecRef logs a reference decrement.
|
||||
func LogDecRef(obj CheckedObject, refs int64) {
|
||||
if leakCheckEnabled() && obj.LogRefs() {
|
||||
logEvent(obj, fmt.Sprintf("DecRef to %d", refs))
|
||||
}
|
||||
}
|
||||
|
||||
// logEvent logs a message for the given reference-counted object.
|
||||
//
|
||||
// obj.LogRefs() should be checked before calling logEvent, in order to avoid
|
||||
// calling any text processing needed to evaluate msg.
|
||||
func logEvent(obj CheckedObject, msg string) {
|
||||
log.Infof("[%s %p] %s:\n%s", obj.RefType(), obj, msg, refs_vfs1.FormatStack(refs_vfs1.RecordStack()))
|
||||
}
|
||||
|
||||
// checkOnce makes sure that leak checking is only done once. DoLeakCheck is
|
||||
// called from multiple places (which may overlap) to cover different sandbox
|
||||
// exit scenarios.
|
||||
var checkOnce sync.Once
|
||||
|
||||
// DoLeakCheck iterates through the live object map and logs a message for each
|
||||
// object. It is called once no reference-counted objects should be reachable
|
||||
// anymore, at which point anything left in the map is considered a leak.
|
||||
func DoLeakCheck() {
|
||||
if leakCheckEnabled() {
|
||||
checkOnce.Do(func() {
|
||||
liveObjectsMu.Lock()
|
||||
defer liveObjectsMu.Unlock()
|
||||
leaked := len(liveObjects)
|
||||
if leaked > 0 {
|
||||
msg := fmt.Sprintf("Leak checking detected %d leaked objects:\n", leaked)
|
||||
for obj := range liveObjects {
|
||||
msg += obj.LeakMessage() + "\n"
|
||||
}
|
||||
log.Warningf(msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
// automatically generated by stateify.
|
||||
|
||||
package refsvfs2
|
||||
+45
-63
@@ -37,15 +37,15 @@
|
||||
//
|
||||
// // One time set-up.
|
||||
// s := sleep.Sleeper{}
|
||||
// s.AddWaker(&w1, constant1)
|
||||
// s.AddWaker(&w2, constant2)
|
||||
// s.AddWaker(&w1)
|
||||
// s.AddWaker(&w2)
|
||||
//
|
||||
// // Called repeatedly.
|
||||
// for {
|
||||
// switch id, _ := s.Fetch(true); id {
|
||||
// case constant1:
|
||||
// switch s.Fetch(true) {
|
||||
// case &w1:
|
||||
// // Do work triggered by w1 being asserted.
|
||||
// case constant2:
|
||||
// case &w2:
|
||||
// // Do work triggered by w2 being asserted.
|
||||
// }
|
||||
// }
|
||||
@@ -119,13 +119,18 @@ type Sleeper struct {
|
||||
waitingG uintptr
|
||||
}
|
||||
|
||||
// AddWaker associates the given waker to the sleeper. id is the value to be
|
||||
// returned when the sleeper is woken by the given waker.
|
||||
func (s *Sleeper) AddWaker(w *Waker, id int) {
|
||||
// AddWaker associates the given waker to the sleeper.
|
||||
func (s *Sleeper) AddWaker(w *Waker) {
|
||||
if w.allWakersNext != nil {
|
||||
panic("waker has non-nil allWakersNext; owned by another sleeper?")
|
||||
}
|
||||
if w.next != nil {
|
||||
panic("waker has non-nil next; queued in another sleeper?")
|
||||
}
|
||||
|
||||
// Add the waker to the list of all wakers.
|
||||
w.allWakersNext = s.allWakers
|
||||
s.allWakers = w
|
||||
w.id = id
|
||||
|
||||
// Try to associate the waker with the sleeper. If it's already
|
||||
// asserted, we simply enqueue it in the "ready" list.
|
||||
@@ -213,28 +218,26 @@ func commitSleep(g uintptr, waitingG unsafe.Pointer) bool {
|
||||
return sync.RaceUncheckedAtomicCompareAndSwapUintptr((*uintptr)(waitingG), preparingG, g)
|
||||
}
|
||||
|
||||
// Fetch fetches the next wake-up notification. If a notification is immediately
|
||||
// available, it is returned right away. Otherwise, the behavior depends on the
|
||||
// value of 'block': if true, the current goroutine blocks until a notification
|
||||
// arrives, then returns it; if false, returns 'ok' as false.
|
||||
//
|
||||
// When 'ok' is true, the value of 'id' corresponds to the id associated with
|
||||
// the waker; when 'ok' is false, 'id' is undefined.
|
||||
// Fetch fetches the next wake-up notification. If a notification is
|
||||
// immediately available, the asserted waker is returned immediately.
|
||||
// Otherwise, the behavior depends on the value of 'block': if true, the
|
||||
// current goroutine blocks until a notification arrives and returns the
|
||||
// asserted waker; if false, nil will be returned.
|
||||
//
|
||||
// N.B. This method is *not* thread-safe. Only one goroutine at a time is
|
||||
// allowed to call this method.
|
||||
func (s *Sleeper) Fetch(block bool) (id int, ok bool) {
|
||||
func (s *Sleeper) Fetch(block bool) *Waker {
|
||||
for {
|
||||
w := s.nextWaker(block)
|
||||
if w == nil {
|
||||
return -1, false
|
||||
return nil
|
||||
}
|
||||
|
||||
// Reassociate the waker with the sleeper. If the waker was
|
||||
// still asserted we can return it, otherwise try the next one.
|
||||
old := (*Sleeper)(atomic.SwapPointer(&w.s, usleeper(s)))
|
||||
if old == &assertedSleeper {
|
||||
return w.id, true
|
||||
return w
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -243,51 +246,34 @@ func (s *Sleeper) Fetch(block bool) (id int, ok bool) {
|
||||
// removes the association with all wakers so that they can be safely reused
|
||||
// by another sleeper after Done() returns.
|
||||
func (s *Sleeper) Done() {
|
||||
// Remove all associations that we can, and build a list of the ones
|
||||
// we could not. An association can be removed right away from waker w
|
||||
// if w.s has a pointer to the sleeper, that is, the waker is not
|
||||
// asserted yet. By atomically switching w.s to nil, we guarantee that
|
||||
// subsequent calls to Assert() on the waker will not result in it being
|
||||
// queued to this sleeper.
|
||||
var pending *Waker
|
||||
w := s.allWakers
|
||||
for w != nil {
|
||||
next := w.allWakersNext
|
||||
for {
|
||||
t := atomic.LoadPointer(&w.s)
|
||||
if t != usleeper(s) {
|
||||
w.allWakersNext = pending
|
||||
pending = w
|
||||
break
|
||||
}
|
||||
|
||||
if atomic.CompareAndSwapPointer(&w.s, t, nil) {
|
||||
break
|
||||
}
|
||||
// Remove all associations that we can, and build a list of the ones we
|
||||
// could not. An association can be removed right away from waker w if
|
||||
// w.s has a pointer to the sleeper, that is, the waker is not asserted
|
||||
// yet. By atomically switching w.s to nil, we guarantee that
|
||||
// subsequent calls to Assert() on the waker will not result in it
|
||||
// being queued.
|
||||
for w := s.allWakers; w != nil; w = s.allWakers {
|
||||
next := w.allWakersNext // Before zapping.
|
||||
if atomic.CompareAndSwapPointer(&w.s, usleeper(s), nil) {
|
||||
w.allWakersNext = nil
|
||||
w.next = nil
|
||||
s.allWakers = next // Move ahead.
|
||||
continue
|
||||
}
|
||||
w = next
|
||||
}
|
||||
|
||||
// The associations that we could not remove are either asserted, or in
|
||||
// the process of being asserted, or have been asserted and cleared
|
||||
// before being pulled from the sleeper lists. We must wait for them all
|
||||
// to make it to the sleeper lists, so that we know that the wakers
|
||||
// won't do any more work towards waking this sleeper up.
|
||||
for pending != nil {
|
||||
pulled := s.nextWaker(true)
|
||||
|
||||
// Remove the waker we just pulled from the list of associated
|
||||
// wakers.
|
||||
prev := &pending
|
||||
for w := *prev; w != nil; w = *prev {
|
||||
if pulled == w {
|
||||
*prev = w.allWakersNext
|
||||
break
|
||||
// Dequeue exactly one waiter from the list, it may not be
|
||||
// this one but we know this one is in the process. We must
|
||||
// leave it in the asserted state but drop it from our lists.
|
||||
if w := s.nextWaker(true); w != nil {
|
||||
prev := &s.allWakers
|
||||
for *prev != w {
|
||||
prev = &((*prev).allWakersNext)
|
||||
}
|
||||
prev = &w.allWakersNext
|
||||
*prev = (*prev).allWakersNext
|
||||
w.allWakersNext = nil
|
||||
w.next = nil
|
||||
}
|
||||
}
|
||||
s.allWakers = nil
|
||||
}
|
||||
|
||||
// enqueueAssertedWaker enqueues an asserted waker to the "ready" circular list
|
||||
@@ -349,10 +335,6 @@ type Waker struct {
|
||||
// allWakersNext is used to form a linked list of all wakers associated
|
||||
// to a given sleeper.
|
||||
allWakersNext *Waker
|
||||
|
||||
// id is the value to be returned to sleepers when they wake up due to
|
||||
// this waker being asserted.
|
||||
id int
|
||||
}
|
||||
|
||||
// Assert moves the waker to an asserted state, if it isn't asserted yet. When
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !race
|
||||
// +build !race
|
||||
|
||||
package state
|
||||
|
||||
+1
@@ -12,6 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build race
|
||||
// +build race
|
||||
|
||||
package state
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !checklocks
|
||||
// +build !checklocks
|
||||
|
||||
package sync
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build checklocks
|
||||
// +build checklocks
|
||||
|
||||
package sync
|
||||
|
||||
+2
-2
@@ -3,8 +3,8 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.13
|
||||
// +build !go1.14
|
||||
//go:build go1.13 && !go1.14
|
||||
// +build go1.13,!go1.14
|
||||
|
||||
package sync
|
||||
|
||||
|
||||
+4
-2
@@ -3,10 +3,12 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.14
|
||||
// +build go1.14
|
||||
// +build !go1.18
|
||||
|
||||
// Check go:linkname function signatures when updating Go version.
|
||||
// //go:linkname directives type-checked by checklinkname. Any other
|
||||
// non-linkname assumptions outside the Go 1 compatibility guarantee should
|
||||
// have an accompanied vet check or version guard build tag.
|
||||
|
||||
package sync
|
||||
|
||||
|
||||
+16
-2
@@ -3,8 +3,8 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.13
|
||||
// +build !go1.18
|
||||
//go:build go1.13 && !go1.19
|
||||
// +build go1.13,!go1.19
|
||||
|
||||
// When updating the build constraint (above), check that syncMutex matches the
|
||||
// standard library sync.Mutex definition.
|
||||
@@ -32,6 +32,18 @@ func (m *CrossGoroutineMutex) state() *int32 {
|
||||
return &(*syncMutex)(unsafe.Pointer(&m.Mutex)).state
|
||||
}
|
||||
|
||||
// Lock locks the underlying Mutex.
|
||||
// +checklocksignore
|
||||
func (m *CrossGoroutineMutex) Lock() {
|
||||
m.Mutex.Lock()
|
||||
}
|
||||
|
||||
// Unlock unlocks the underlying Mutex.
|
||||
// +checklocksignore
|
||||
func (m *CrossGoroutineMutex) Unlock() {
|
||||
m.Mutex.Unlock()
|
||||
}
|
||||
|
||||
const (
|
||||
mutexUnlocked = 0
|
||||
mutexLocked = 1
|
||||
@@ -62,6 +74,7 @@ type Mutex struct {
|
||||
|
||||
// Lock locks m. If the lock is already in use, the calling goroutine blocks
|
||||
// until the mutex is available.
|
||||
// +checklocksignore
|
||||
func (m *Mutex) Lock() {
|
||||
noteLock(unsafe.Pointer(m))
|
||||
m.m.Lock()
|
||||
@@ -80,6 +93,7 @@ func (m *Mutex) Unlock() {
|
||||
|
||||
// TryLock tries to acquire the mutex. It returns true if it succeeds and false
|
||||
// otherwise. TryLock does not block.
|
||||
// +checklocksignore
|
||||
func (m *Mutex) TryLock() bool {
|
||||
// Note lock first to enforce proper locking even if unsuccessful.
|
||||
noteLock(unsafe.Pointer(m))
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !race
|
||||
// +build !race
|
||||
|
||||
package sync
|
||||
|
||||
+2
-2
@@ -12,8 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// +build race
|
||||
// +build amd64
|
||||
//go:build race && amd64
|
||||
// +build race,amd64
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
|
||||
+2
-2
@@ -12,8 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// +build race
|
||||
// +build arm64
|
||||
//go:build race && arm64
|
||||
// +build race,arm64
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
|
||||
+1
@@ -3,6 +3,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build race
|
||||
// +build race
|
||||
|
||||
package sync
|
||||
|
||||
+11
-20
@@ -3,11 +3,16 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.13
|
||||
// +build !go1.18
|
||||
//go:build go1.13 && !go1.19
|
||||
// +build go1.13,!go1.19
|
||||
|
||||
// Check go:linkname function signatures, type definitions, and constants when
|
||||
// updating Go version.
|
||||
// //go:linkname directives type-checked by checklinkname. Any other
|
||||
// non-linkname assumptions outside the Go 1 compatibility guarantee should
|
||||
// have an accompanied vet check or version guard build tag.
|
||||
|
||||
// Check type definitions and constants when updating Go version.
|
||||
//
|
||||
// TODO(b/165820485): add these checks to checklinkname.
|
||||
|
||||
package sync
|
||||
|
||||
@@ -17,20 +22,6 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Note that go:linkname silently doesn't work if the local name is exported,
|
||||
// necessitating an indirection for exported functions.
|
||||
|
||||
// Memmove is runtime.memmove, exported for SeqAtomicLoad/SeqAtomicTryLoad<T>.
|
||||
//
|
||||
//go:nosplit
|
||||
func Memmove(to, from unsafe.Pointer, n uintptr) {
|
||||
memmove(to, from, n)
|
||||
}
|
||||
|
||||
//go:linkname memmove runtime.memmove
|
||||
//go:noescape
|
||||
func memmove(to, from unsafe.Pointer, n uintptr)
|
||||
|
||||
// Gopark is runtime.gopark. Gopark calls unlockf(pointer to runtime.g, lock);
|
||||
// if unlockf returns true, Gopark blocks until Goready(pointer to runtime.g)
|
||||
// is called. unlockf and its callees must be nosplit and norace, since stack
|
||||
@@ -123,10 +114,10 @@ type maptype struct {
|
||||
// These functions are only used within the sync package.
|
||||
|
||||
//go:linkname semacquire sync.runtime_Semacquire
|
||||
func semacquire(s *uint32)
|
||||
func semacquire(addr *uint32)
|
||||
|
||||
//go:linkname semrelease sync.runtime_Semrelease
|
||||
func semrelease(s *uint32, handoff bool, skipframes int)
|
||||
func semrelease(addr *uint32, handoff bool, skipframes int)
|
||||
|
||||
//go:linkname canSpin sync.runtime_canSpin
|
||||
func canSpin(i int) bool
|
||||
|
||||
@@ -37,6 +37,7 @@ const rwmutexMaxReaders = 1 << 30
|
||||
|
||||
// TryRLock locks rw for reading. It returns true if it succeeds and false
|
||||
// otherwise. It does not block.
|
||||
// +checklocksignore
|
||||
func (rw *CrossGoroutineRWMutex) TryRLock() bool {
|
||||
if RaceEnabled {
|
||||
RaceDisable()
|
||||
@@ -65,6 +66,7 @@ func (rw *CrossGoroutineRWMutex) TryRLock() bool {
|
||||
// It should not be used for recursive read locking; a blocked Lock call
|
||||
// excludes new readers from acquiring the lock. See the documentation on the
|
||||
// RWMutex type.
|
||||
// +checklocksignore
|
||||
func (rw *CrossGoroutineRWMutex) RLock() {
|
||||
if RaceEnabled {
|
||||
RaceDisable()
|
||||
@@ -83,6 +85,7 @@ func (rw *CrossGoroutineRWMutex) RLock() {
|
||||
//
|
||||
// Preconditions:
|
||||
// * rw is locked for reading.
|
||||
// +checklocksignore
|
||||
func (rw *CrossGoroutineRWMutex) RUnlock() {
|
||||
if RaceEnabled {
|
||||
RaceReleaseMerge(unsafe.Pointer(&rw.writerSem))
|
||||
@@ -134,6 +137,7 @@ func (rw *CrossGoroutineRWMutex) TryLock() bool {
|
||||
|
||||
// Lock locks rw for writing. If the lock is already locked for reading or
|
||||
// writing, Lock blocks until the lock is available.
|
||||
// +checklocksignore
|
||||
func (rw *CrossGoroutineRWMutex) Lock() {
|
||||
if RaceEnabled {
|
||||
RaceDisable()
|
||||
@@ -228,6 +232,7 @@ type RWMutex struct {
|
||||
|
||||
// TryRLock locks rw for reading. It returns true if it succeeds and false
|
||||
// otherwise. It does not block.
|
||||
// +checklocksignore
|
||||
func (rw *RWMutex) TryRLock() bool {
|
||||
// Note lock first to enforce proper locking even if unsuccessful.
|
||||
noteLock(unsafe.Pointer(rw))
|
||||
@@ -243,6 +248,7 @@ func (rw *RWMutex) TryRLock() bool {
|
||||
// It should not be used for recursive read locking; a blocked Lock call
|
||||
// excludes new readers from acquiring the lock. See the documentation on the
|
||||
// RWMutex type.
|
||||
// +checklocksignore
|
||||
func (rw *RWMutex) RLock() {
|
||||
noteLock(unsafe.Pointer(rw))
|
||||
rw.m.RLock()
|
||||
@@ -261,6 +267,7 @@ func (rw *RWMutex) RUnlock() {
|
||||
|
||||
// TryLock locks rw for writing. It returns true if it succeeds and false
|
||||
// otherwise. It does not block.
|
||||
// +checklocksignore
|
||||
func (rw *RWMutex) TryLock() bool {
|
||||
// Note lock first to enforce proper locking even if unsuccessful.
|
||||
noteLock(unsafe.Pointer(rw))
|
||||
@@ -273,6 +280,7 @@ func (rw *RWMutex) TryLock() bool {
|
||||
|
||||
// Lock locks rw for writing. If the lock is already locked for reading or
|
||||
// writing, Lock blocks until the lock is available.
|
||||
// +checklocksignore
|
||||
func (rw *RWMutex) Lock() {
|
||||
noteLock(unsafe.Pointer(rw))
|
||||
rw.m.Lock()
|
||||
|
||||
+29
-16
@@ -19,6 +19,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"time"
|
||||
@@ -250,8 +251,8 @@ func (l *TCPListener) Accept() (net.Conn, error) {
|
||||
|
||||
if _, ok := err.(*tcpip.ErrWouldBlock); ok {
|
||||
// Create wait queue entry that notifies a channel.
|
||||
waitEntry, notifyCh := waiter.NewChannelEntry(nil)
|
||||
l.wq.EventRegister(&waitEntry, waiter.EventIn)
|
||||
waitEntry, notifyCh := waiter.NewChannelEntry(waiter.ReadableEvents)
|
||||
l.wq.EventRegister(&waitEntry)
|
||||
defer l.wq.EventUnregister(&waitEntry)
|
||||
|
||||
for {
|
||||
@@ -300,8 +301,8 @@ func commonRead(b []byte, ep tcpip.Endpoint, wq *waiter.Queue, deadline <-chan s
|
||||
|
||||
if _, ok := err.(*tcpip.ErrWouldBlock); ok {
|
||||
// Create wait queue entry that notifies a channel.
|
||||
waitEntry, notifyCh := waiter.NewChannelEntry(nil)
|
||||
wq.EventRegister(&waitEntry, waiter.EventIn)
|
||||
waitEntry, notifyCh := waiter.NewChannelEntry(waiter.ReadableEvents)
|
||||
wq.EventRegister(&waitEntry)
|
||||
defer wq.EventUnregister(&waitEntry)
|
||||
for {
|
||||
res, err = ep.Read(&w, opts)
|
||||
@@ -380,9 +381,8 @@ func (c *TCPConn) Write(b []byte) (int, error) {
|
||||
case nil:
|
||||
case *tcpip.ErrWouldBlock:
|
||||
if ch == nil {
|
||||
entry, ch = waiter.NewChannelEntry(nil)
|
||||
|
||||
c.wq.EventRegister(&entry, waiter.EventOut)
|
||||
entry, ch = waiter.NewChannelEntry(waiter.WritableEvents)
|
||||
c.wq.EventRegister(&entry)
|
||||
defer c.wq.EventUnregister(&entry)
|
||||
} else {
|
||||
// Don't wait immediately after registration in case more data
|
||||
@@ -471,9 +471,9 @@ func DialTCP(s *stack.Stack, addr tcpip.FullAddress, network tcpip.NetworkProtoc
|
||||
return DialContextTCP(context.Background(), s, addr, network)
|
||||
}
|
||||
|
||||
// DialContextTCP creates a new TCPConn connected to the specified address
|
||||
// with the option of adding cancellation and timeouts.
|
||||
func DialContextTCP(ctx context.Context, s *stack.Stack, addr tcpip.FullAddress, network tcpip.NetworkProtocolNumber) (*TCPConn, error) {
|
||||
// DialTCPWithBind creates a new TCPConn connected to the specified
|
||||
// remoteAddress with its local address bound to localAddr.
|
||||
func DialTCPWithBind(ctx context.Context, s *stack.Stack, localAddr, remoteAddr tcpip.FullAddress, network tcpip.NetworkProtocolNumber) (*TCPConn, error) {
|
||||
// Create TCP endpoint, then connect.
|
||||
var wq waiter.Queue
|
||||
ep, err := s.NewEndpoint(tcp.ProtocolNumber, network, &wq)
|
||||
@@ -484,8 +484,8 @@ func DialContextTCP(ctx context.Context, s *stack.Stack, addr tcpip.FullAddress,
|
||||
// Create wait queue entry that notifies a channel.
|
||||
//
|
||||
// We do this unconditionally as Connect will always return an error.
|
||||
waitEntry, notifyCh := waiter.NewChannelEntry(nil)
|
||||
wq.EventRegister(&waitEntry, waiter.EventOut)
|
||||
waitEntry, notifyCh := waiter.NewChannelEntry(waiter.WritableEvents)
|
||||
wq.EventRegister(&waitEntry)
|
||||
defer wq.EventUnregister(&waitEntry)
|
||||
|
||||
select {
|
||||
@@ -494,7 +494,14 @@ func DialContextTCP(ctx context.Context, s *stack.Stack, addr tcpip.FullAddress,
|
||||
default:
|
||||
}
|
||||
|
||||
err = ep.Connect(addr)
|
||||
// Bind before connect if requested.
|
||||
if localAddr != (tcpip.FullAddress{}) {
|
||||
if err = ep.Bind(localAddr); err != nil {
|
||||
return nil, fmt.Errorf("ep.Bind(%+v) = %s", localAddr, err)
|
||||
}
|
||||
}
|
||||
|
||||
err = ep.Connect(remoteAddr)
|
||||
if _, ok := err.(*tcpip.ErrConnectStarted); ok {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@@ -510,7 +517,7 @@ func DialContextTCP(ctx context.Context, s *stack.Stack, addr tcpip.FullAddress,
|
||||
return nil, &net.OpError{
|
||||
Op: "connect",
|
||||
Net: "tcp",
|
||||
Addr: fullToTCPAddr(addr),
|
||||
Addr: fullToTCPAddr(remoteAddr),
|
||||
Err: errors.New(err.String()),
|
||||
}
|
||||
}
|
||||
@@ -518,6 +525,12 @@ func DialContextTCP(ctx context.Context, s *stack.Stack, addr tcpip.FullAddress,
|
||||
return NewTCPConn(&wq, ep), nil
|
||||
}
|
||||
|
||||
// DialContextTCP creates a new TCPConn connected to the specified address
|
||||
// with the option of adding cancellation and timeouts.
|
||||
func DialContextTCP(ctx context.Context, s *stack.Stack, addr tcpip.FullAddress, network tcpip.NetworkProtocolNumber) (*TCPConn, error) {
|
||||
return DialTCPWithBind(ctx, s, tcpip.FullAddress{} /* localAddr */, addr /* remoteAddr */, network)
|
||||
}
|
||||
|
||||
// A UDPConn is a wrapper around a UDP tcpip.Endpoint that implements
|
||||
// net.Conn and net.PacketConn.
|
||||
type UDPConn struct {
|
||||
@@ -651,8 +664,8 @@ func (c *UDPConn) WriteTo(b []byte, addr net.Addr) (int, error) {
|
||||
n, err := c.ep.Write(&r, writeOptions)
|
||||
if _, ok := err.(*tcpip.ErrWouldBlock); ok {
|
||||
// Create wait queue entry that notifies a channel.
|
||||
waitEntry, notifyCh := waiter.NewChannelEntry(nil)
|
||||
c.wq.EventRegister(&waitEntry, waiter.EventOut)
|
||||
waitEntry, notifyCh := waiter.NewChannelEntry(waiter.WritableEvents)
|
||||
c.wq.EventRegister(&waitEntry)
|
||||
defer c.wq.EventUnregister(&waitEntry)
|
||||
for {
|
||||
select {
|
||||
|
||||
@@ -19,6 +19,7 @@ func (vv *VectorisedView) StateFields() []string {
|
||||
|
||||
func (vv *VectorisedView) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (vv *VectorisedView) StateSave(stateSinkObject state.Sink) {
|
||||
vv.beforeSave()
|
||||
stateSinkObject.Save(0, &vv.views)
|
||||
@@ -27,6 +28,7 @@ func (vv *VectorisedView) StateSave(stateSinkObject state.Sink) {
|
||||
|
||||
func (vv *VectorisedView) afterLoad() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (vv *VectorisedView) StateLoad(stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &vv.views)
|
||||
stateSourceObject.Load(1, &vv.size)
|
||||
|
||||
+10
-10
@@ -42,26 +42,26 @@ func (s *Sum32) Reset() { *s = 0 }
|
||||
|
||||
// Sum32 returns the hash value
|
||||
func (s *Sum32) Sum32() uint32 {
|
||||
hash := *s
|
||||
sCopy := *s
|
||||
|
||||
hash += (hash << 3)
|
||||
hash ^= hash >> 11
|
||||
hash += hash << 15
|
||||
sCopy += sCopy << 3
|
||||
sCopy ^= sCopy >> 11
|
||||
sCopy += sCopy << 15
|
||||
|
||||
return uint32(hash)
|
||||
return uint32(sCopy)
|
||||
}
|
||||
|
||||
// Write adds more data to the running hash.
|
||||
//
|
||||
// It never returns an error.
|
||||
func (s *Sum32) Write(data []byte) (int, error) {
|
||||
hash := *s
|
||||
sCopy := *s
|
||||
for _, b := range data {
|
||||
hash += Sum32(b)
|
||||
hash += hash << 10
|
||||
hash ^= hash >> 6
|
||||
sCopy += Sum32(b)
|
||||
sCopy += sCopy << 10
|
||||
sCopy ^= sCopy >> 6
|
||||
}
|
||||
*s = hash
|
||||
*s = sCopy
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ package header
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"inet.af/netstack/tcpip"
|
||||
"inet.af/netstack/tcpip/buffer"
|
||||
@@ -234,3 +235,64 @@ func PseudoHeaderChecksum(protocol tcpip.TransportProtocolNumber, srcAddr tcpip.
|
||||
|
||||
return Checksum([]byte{0, uint8(protocol)}, xsum)
|
||||
}
|
||||
|
||||
// checksumUpdate2ByteAlignedUint16 updates a uint16 value in a calculated
|
||||
// checksum.
|
||||
//
|
||||
// The value MUST begin at a 2-byte boundary in the original buffer.
|
||||
func checksumUpdate2ByteAlignedUint16(xsum, old, new uint16) uint16 {
|
||||
// As per RFC 1071 page 4,
|
||||
// (4) Incremental Update
|
||||
//
|
||||
// ...
|
||||
//
|
||||
// To update the checksum, simply add the differences of the
|
||||
// sixteen bit integers that have been changed. To see why this
|
||||
// works, observe that every 16-bit integer has an additive inverse
|
||||
// and that addition is associative. From this it follows that
|
||||
// given the original value m, the new value m', and the old
|
||||
// checksum C, the new checksum C' is:
|
||||
//
|
||||
// C' = C + (-m) + m' = C + (m' - m)
|
||||
return ChecksumCombine(xsum, ChecksumCombine(new, ^old))
|
||||
}
|
||||
|
||||
// checksumUpdate2ByteAlignedAddress updates an address in a calculated
|
||||
// checksum.
|
||||
//
|
||||
// The addresses must have the same length and must contain an even number
|
||||
// of bytes. The address MUST begin at a 2-byte boundary in the original buffer.
|
||||
func checksumUpdate2ByteAlignedAddress(xsum uint16, old, new tcpip.Address) uint16 {
|
||||
const uint16Bytes = 2
|
||||
|
||||
if len(old) != len(new) {
|
||||
panic(fmt.Sprintf("buffer lengths are different; old = %d, new = %d", len(old), len(new)))
|
||||
}
|
||||
|
||||
if len(old)%uint16Bytes != 0 {
|
||||
panic(fmt.Sprintf("buffer has an odd number of bytes; got = %d", len(old)))
|
||||
}
|
||||
|
||||
// As per RFC 1071 page 4,
|
||||
// (4) Incremental Update
|
||||
//
|
||||
// ...
|
||||
//
|
||||
// To update the checksum, simply add the differences of the
|
||||
// sixteen bit integers that have been changed. To see why this
|
||||
// works, observe that every 16-bit integer has an additive inverse
|
||||
// and that addition is associative. From this it follows that
|
||||
// given the original value m, the new value m', and the old
|
||||
// checksum C, the new checksum C' is:
|
||||
//
|
||||
// C' = C + (-m) + m' = C + (m' - m)
|
||||
for len(old) != 0 {
|
||||
// Convert the 2 byte sequences to uint16 values then apply the increment
|
||||
// update.
|
||||
xsum = checksumUpdate2ByteAlignedUint16(xsum, (uint16(old[0])<<8)+uint16(old[1]), (uint16(new[0])<<8)+uint16(new[1]))
|
||||
old = old[uint16Bytes:]
|
||||
new = new[uint16Bytes:]
|
||||
}
|
||||
|
||||
return xsum
|
||||
}
|
||||
+3
-3
@@ -49,9 +49,9 @@ const (
|
||||
// EthernetAddressSize is the size, in bytes, of an ethernet address.
|
||||
EthernetAddressSize = 6
|
||||
|
||||
// unspecifiedEthernetAddress is the unspecified ethernet address
|
||||
// UnspecifiedEthernetAddress is the unspecified ethernet address
|
||||
// (all bits set to 0).
|
||||
unspecifiedEthernetAddress = tcpip.LinkAddress("\x00\x00\x00\x00\x00\x00")
|
||||
UnspecifiedEthernetAddress = tcpip.LinkAddress("\x00\x00\x00\x00\x00\x00")
|
||||
|
||||
// EthernetBroadcastAddress is an ethernet address that addresses every node
|
||||
// on a local link.
|
||||
@@ -134,7 +134,7 @@ func IsValidUnicastEthernetAddress(addr tcpip.LinkAddress) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
if addr == unspecifiedEthernetAddress {
|
||||
if addr == UnspecifiedEthernetAddress {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ func (r *SACKBlock) StateFields() []string {
|
||||
|
||||
func (r *SACKBlock) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (r *SACKBlock) StateSave(stateSinkObject state.Sink) {
|
||||
r.beforeSave()
|
||||
stateSinkObject.Save(0, &r.Start)
|
||||
@@ -27,6 +28,7 @@ func (r *SACKBlock) StateSave(stateSinkObject state.Sink) {
|
||||
|
||||
func (r *SACKBlock) afterLoad() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (r *SACKBlock) StateLoad(stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &r.Start)
|
||||
stateSourceObject.Load(1, &r.End)
|
||||
@@ -47,6 +49,7 @@ func (t *TCPOptions) StateFields() []string {
|
||||
|
||||
func (t *TCPOptions) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (t *TCPOptions) StateSave(stateSinkObject state.Sink) {
|
||||
t.beforeSave()
|
||||
stateSinkObject.Save(0, &t.TS)
|
||||
@@ -57,6 +60,7 @@ func (t *TCPOptions) StateSave(stateSinkObject state.Sink) {
|
||||
|
||||
func (t *TCPOptions) afterLoad() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (t *TCPOptions) StateLoad(stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &t.TS)
|
||||
stateSourceObject.Load(1, &t.TSVal)
|
||||
|
||||
@@ -53,6 +53,31 @@ type Transport interface {
|
||||
Payload() []byte
|
||||
}
|
||||
|
||||
// ChecksummableTransport is a Transport that supports checksumming.
|
||||
type ChecksummableTransport interface {
|
||||
Transport
|
||||
|
||||
// SetSourcePortWithChecksumUpdate sets the source port and updates
|
||||
// the checksum.
|
||||
//
|
||||
// The receiver's checksum must be a fully calculated checksum.
|
||||
SetSourcePortWithChecksumUpdate(port uint16)
|
||||
|
||||
// SetDestinationPortWithChecksumUpdate sets the destination port and updates
|
||||
// the checksum.
|
||||
//
|
||||
// The receiver's checksum must be a fully calculated checksum.
|
||||
SetDestinationPortWithChecksumUpdate(port uint16)
|
||||
|
||||
// UpdateChecksumPseudoHeaderAddress updates the checksum to reflect an
|
||||
// updated address in the pseudo header.
|
||||
//
|
||||
// If fullChecksum is true, the receiver's checksum field is assumed to hold a
|
||||
// fully calculated checksum. Otherwise, it is assumed to hold a partially
|
||||
// calculated checksum which only reflects the pseudo header.
|
||||
UpdateChecksumPseudoHeaderAddress(old, new tcpip.Address, fullChecksum bool)
|
||||
}
|
||||
|
||||
// Network offers generic methods to query and/or update the fields of the
|
||||
// header of a network protocol buffer.
|
||||
type Network interface {
|
||||
@@ -90,3 +115,16 @@ type Network interface {
|
||||
// SetTOS sets the values of the "type of service" and "flow label" fields.
|
||||
SetTOS(t uint8, l uint32)
|
||||
}
|
||||
|
||||
// ChecksummableNetwork is a Network that supports checksumming.
|
||||
type ChecksummableNetwork interface {
|
||||
Network
|
||||
|
||||
// SetSourceAddressAndChecksum sets the source address and updates the
|
||||
// checksum to reflect the new address.
|
||||
SetSourceAddressWithChecksumUpdate(tcpip.Address)
|
||||
|
||||
// SetDestinationAddressAndChecksum sets the destination address and
|
||||
// updates the checksum to reflect the new address.
|
||||
SetDestinationAddressWithChecksumUpdate(tcpip.Address)
|
||||
}
|
||||
+91
-8
@@ -17,6 +17,7 @@ package header
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"inet.af/netstack/tcpip"
|
||||
)
|
||||
@@ -178,6 +179,26 @@ const (
|
||||
IPv4FlagDontFragment
|
||||
)
|
||||
|
||||
// ipv4LinkLocalUnicastSubnet is the IPv4 link local unicast subnet as defined
|
||||
// by RFC 3927 section 1.
|
||||
var ipv4LinkLocalUnicastSubnet = func() tcpip.Subnet {
|
||||
subnet, err := tcpip.NewSubnet("\xa9\xfe\x00\x00", "\xff\xff\x00\x00")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return subnet
|
||||
}()
|
||||
|
||||
// ipv4LinkLocalMulticastSubnet is the IPv4 link local multicast subnet as
|
||||
// defined by RFC 5771 section 4.
|
||||
var ipv4LinkLocalMulticastSubnet = func() tcpip.Subnet {
|
||||
subnet, err := tcpip.NewSubnet("\xe0\x00\x00\x00", "\xff\xff\xff\x00")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return subnet
|
||||
}()
|
||||
|
||||
// IPv4EmptySubnet is the empty IPv4 subnet.
|
||||
var IPv4EmptySubnet = func() tcpip.Subnet {
|
||||
subnet, err := tcpip.NewSubnet(IPv4Any, tcpip.AddressMask(IPv4Any))
|
||||
@@ -187,6 +208,15 @@ var IPv4EmptySubnet = func() tcpip.Subnet {
|
||||
return subnet
|
||||
}()
|
||||
|
||||
// IPv4LoopbackSubnet is the loopback subnet for IPv4.
|
||||
var IPv4LoopbackSubnet = func() tcpip.Subnet {
|
||||
subnet, err := tcpip.NewSubnet(tcpip.Address("\x7f\x00\x00\x00"), tcpip.AddressMask("\xff\x00\x00\x00"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return subnet
|
||||
}()
|
||||
|
||||
// IPVersion returns the version of IP used in the given packet. It returns -1
|
||||
// if the packet is not large enough to contain the version field.
|
||||
func IPVersion(b []byte) int {
|
||||
@@ -284,6 +314,18 @@ func (b IPv4) DestinationAddress() tcpip.Address {
|
||||
return tcpip.Address(b[dstAddr : dstAddr+IPv4AddressSize])
|
||||
}
|
||||
|
||||
// SetSourceAddressWithChecksumUpdate implements ChecksummableNetwork.
|
||||
func (b IPv4) SetSourceAddressWithChecksumUpdate(new tcpip.Address) {
|
||||
b.SetChecksum(^checksumUpdate2ByteAlignedAddress(^b.Checksum(), b.SourceAddress(), new))
|
||||
b.SetSourceAddress(new)
|
||||
}
|
||||
|
||||
// SetDestinationAddressWithChecksumUpdate implements ChecksummableNetwork.
|
||||
func (b IPv4) SetDestinationAddressWithChecksumUpdate(new tcpip.Address) {
|
||||
b.SetChecksum(^checksumUpdate2ByteAlignedAddress(^b.Checksum(), b.DestinationAddress(), new))
|
||||
b.SetDestinationAddress(new)
|
||||
}
|
||||
|
||||
// padIPv4OptionsLength returns the total length for IPv4 options of length l
|
||||
// after applying padding according to RFC 791:
|
||||
// The internet header padding is used to ensure that the internet
|
||||
@@ -423,6 +465,44 @@ func (b IPv4) IsValid(pktSize int) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// IsV4LinkLocalUnicastAddress determines if the provided address is an IPv4
|
||||
// link-local unicast address.
|
||||
func IsV4LinkLocalUnicastAddress(addr tcpip.Address) bool {
|
||||
return ipv4LinkLocalUnicastSubnet.Contains(addr)
|
||||
}
|
||||
|
||||
// IsV4LinkLocalMulticastAddress determines if the provided address is an IPv4
|
||||
// link-local multicast address.
|
||||
func IsV4LinkLocalMulticastAddress(addr tcpip.Address) bool {
|
||||
return ipv4LinkLocalMulticastSubnet.Contains(addr)
|
||||
}
|
||||
|
||||
// IsChecksumValid returns true iff the IPv4 header's checksum is valid.
|
||||
func (b IPv4) IsChecksumValid() bool {
|
||||
// There has been some confusion regarding verifying checksums. We need
|
||||
// just look for negative 0 (0xffff) as the checksum, as it's not possible to
|
||||
// get positive 0 (0) for the checksum. Some bad implementations could get it
|
||||
// when doing entry replacement in the early days of the Internet,
|
||||
// however the lore that one needs to check for both persists.
|
||||
//
|
||||
// RFC 1624 section 1 describes the source of this confusion as:
|
||||
// [the partial recalculation method described in RFC 1071] computes a
|
||||
// result for certain cases that differs from the one obtained from
|
||||
// scratch (one's complement of one's complement sum of the original
|
||||
// fields).
|
||||
//
|
||||
// However RFC 1624 section 5 clarifies that if using the verification method
|
||||
// "recommended by RFC 1071, it does not matter if an intermediate system
|
||||
// generated a -0 instead of +0".
|
||||
//
|
||||
// RFC1071 page 1 specifies the verification method as:
|
||||
// (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.
|
||||
return b.CalculateChecksum() == 0xffff
|
||||
}
|
||||
|
||||
// IsV4MulticastAddress determines if the provided address is an IPv4 multicast
|
||||
// address (range 224.0.0.0 to 239.255.255.255). The four most significant bits
|
||||
// will be 1110 = 0xe0.
|
||||
@@ -514,7 +594,7 @@ func (o *IPv4OptionGeneric) Type() IPv4OptionType {
|
||||
func (o *IPv4OptionGeneric) Size() uint8 { return uint8(len(*o)) }
|
||||
|
||||
// Contents implements IPv4Option.
|
||||
func (o *IPv4OptionGeneric) Contents() []byte { return []byte(*o) }
|
||||
func (o *IPv4OptionGeneric) Contents() []byte { return *o }
|
||||
|
||||
// IPv4OptionIterator is an iterator pointing to a specific IP option
|
||||
// at any point of time. It also holds information as to a new options buffer
|
||||
@@ -552,7 +632,7 @@ func (i *IPv4OptionIterator) InitReplacement(option IPv4Option) IPv4Options {
|
||||
// RemainingBuffer returns the remaining (unused) part of the new option buffer,
|
||||
// into which a new option may be written.
|
||||
func (i *IPv4OptionIterator) RemainingBuffer() IPv4Options {
|
||||
return IPv4Options(i.newOptions[i.writePoint:])
|
||||
return i.newOptions[i.writePoint:]
|
||||
}
|
||||
|
||||
// ConsumeBuffer marks a portion of the new buffer as used.
|
||||
@@ -755,9 +835,12 @@ const (
|
||||
|
||||
// ipv4TimestampTime provides the current time as specified in RFC 791.
|
||||
func ipv4TimestampTime(clock tcpip.Clock) uint32 {
|
||||
const millisecondsPerDay = 24 * 3600 * 1000
|
||||
const nanoPerMilli = 1000000
|
||||
return uint32((clock.NowNanoseconds() / nanoPerMilli) % millisecondsPerDay)
|
||||
// Per RFC 791 page 21:
|
||||
// The Timestamp is a right-justified, 32-bit timestamp in
|
||||
// milliseconds since midnight UT.
|
||||
now := clock.Now().UTC()
|
||||
midnight := now.Truncate(24 * time.Hour)
|
||||
return uint32(now.Sub(midnight).Milliseconds())
|
||||
}
|
||||
|
||||
// IP Timestamp option fields.
|
||||
@@ -785,7 +868,7 @@ func (ts *IPv4OptionTimestamp) Type() IPv4OptionType { return IPv4OptionTimestam
|
||||
func (ts *IPv4OptionTimestamp) Size() uint8 { return uint8(len(*ts)) }
|
||||
|
||||
// Contents implements IPv4Option.
|
||||
func (ts *IPv4OptionTimestamp) Contents() []byte { return []byte(*ts) }
|
||||
func (ts *IPv4OptionTimestamp) Contents() []byte { return *ts }
|
||||
|
||||
// Pointer returns the pointer field in the IP Timestamp option.
|
||||
func (ts *IPv4OptionTimestamp) Pointer() uint8 {
|
||||
@@ -889,7 +972,7 @@ func (rr *IPv4OptionRecordRoute) Type() IPv4OptionType { return IPv4OptionRecord
|
||||
func (rr *IPv4OptionRecordRoute) Size() uint8 { return uint8(len(*rr)) }
|
||||
|
||||
// Contents implements IPv4Option.
|
||||
func (rr *IPv4OptionRecordRoute) Contents() []byte { return []byte(*rr) }
|
||||
func (rr *IPv4OptionRecordRoute) Contents() []byte { return *rr }
|
||||
|
||||
// Router Alert option specific related constants.
|
||||
//
|
||||
@@ -934,7 +1017,7 @@ func (*IPv4OptionRouterAlert) Type() IPv4OptionType { return IPv4OptionRouterAle
|
||||
func (ra *IPv4OptionRouterAlert) Size() uint8 { return uint8(len(*ra)) }
|
||||
|
||||
// Contents implements IPv4Option.
|
||||
func (ra *IPv4OptionRouterAlert) Contents() []byte { return []byte(*ra) }
|
||||
func (ra *IPv4OptionRouterAlert) Contents() []byte { return *ra }
|
||||
|
||||
// Value returns the value of the IPv4OptionRouterAlert.
|
||||
func (ra *IPv4OptionRouterAlert) Value() uint16 {
|
||||
|
||||
+70
-17
@@ -98,12 +98,27 @@ const (
|
||||
// The address is ff02::1.
|
||||
IPv6AllNodesMulticastAddress tcpip.Address = "\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"
|
||||
|
||||
// IPv6AllRoutersMulticastAddress is a link-local multicast group that
|
||||
// all IPv6 routers MUST join, as per RFC 4291, section 2.8. Packets
|
||||
// IPv6AllRoutersInterfaceLocalMulticastAddress is an interface-local
|
||||
// multicast group that all IPv6 routers MUST join, as per RFC 4291, section
|
||||
// 2.8. Packets destined to this address will reach the router on an
|
||||
// interface.
|
||||
//
|
||||
// The address is ff01::2.
|
||||
IPv6AllRoutersInterfaceLocalMulticastAddress tcpip.Address = "\xff\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02"
|
||||
|
||||
// IPv6AllRoutersLinkLocalMulticastAddress is a link-local multicast group
|
||||
// that all IPv6 routers MUST join, as per RFC 4291, section 2.8. Packets
|
||||
// destined to this address will reach all routers on a link.
|
||||
//
|
||||
// The address is ff02::2.
|
||||
IPv6AllRoutersMulticastAddress tcpip.Address = "\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02"
|
||||
IPv6AllRoutersLinkLocalMulticastAddress tcpip.Address = "\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02"
|
||||
|
||||
// IPv6AllRoutersSiteLocalMulticastAddress is a site-local multicast group
|
||||
// that all IPv6 routers MUST join, as per RFC 4291, section 2.8. Packets
|
||||
// destined to this address will reach all routers in a site.
|
||||
//
|
||||
// The address is ff05::2.
|
||||
IPv6AllRoutersSiteLocalMulticastAddress tcpip.Address = "\xff\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02"
|
||||
|
||||
// IPv6MinimumMTU is the minimum MTU required by IPv6, per RFC 8200,
|
||||
// section 5:
|
||||
@@ -142,11 +157,6 @@ const (
|
||||
// ipv6MulticastAddressScopeMask is the mask for the scope (scop) field,
|
||||
// within the byte holding the field, as per RFC 4291 section 2.7.
|
||||
ipv6MulticastAddressScopeMask = 0xF
|
||||
|
||||
// ipv6LinkLocalMulticastScope is the value of the scope (scop) field within
|
||||
// a multicast IPv6 address that indicates the address has link-local scope,
|
||||
// as per RFC 4291 section 2.7.
|
||||
ipv6LinkLocalMulticastScope = 2
|
||||
)
|
||||
|
||||
// IPv6EmptySubnet is the empty IPv6 subnet. It may also be known as the
|
||||
@@ -381,25 +391,25 @@ func LinkLocalAddr(linkAddr tcpip.LinkAddress) tcpip.Address {
|
||||
return tcpip.Address(lladdrb[:])
|
||||
}
|
||||
|
||||
// IsV6LinkLocalAddress determines if the provided address is an IPv6
|
||||
// link-local address (fe80::/10).
|
||||
func IsV6LinkLocalAddress(addr tcpip.Address) bool {
|
||||
// IsV6LinkLocalUnicastAddress returns true iff the provided address is an IPv6
|
||||
// link-local unicast address, as defined by RFC 4291 section 2.5.6.
|
||||
func IsV6LinkLocalUnicastAddress(addr tcpip.Address) bool {
|
||||
if len(addr) != IPv6AddressSize {
|
||||
return false
|
||||
}
|
||||
return addr[0] == 0xfe && (addr[1]&0xc0) == 0x80
|
||||
}
|
||||
|
||||
// IsV6LoopbackAddress determines if the provided address is an IPv6 loopback
|
||||
// address.
|
||||
// IsV6LoopbackAddress returns true iff the provided address is an IPv6 loopback
|
||||
// address, as defined by RFC 4291 section 2.5.3.
|
||||
func IsV6LoopbackAddress(addr tcpip.Address) bool {
|
||||
return addr == IPv6Loopback
|
||||
}
|
||||
|
||||
// IsV6LinkLocalMulticastAddress determines if the provided address is an IPv6
|
||||
// link-local multicast address.
|
||||
// IsV6LinkLocalMulticastAddress returns true iff the provided address is an
|
||||
// IPv6 link-local multicast address, as defined by RFC 4291 section 2.7.
|
||||
func IsV6LinkLocalMulticastAddress(addr tcpip.Address) bool {
|
||||
return IsV6MulticastAddress(addr) && addr[ipv6MulticastAddressScopeByteIdx]&ipv6MulticastAddressScopeMask == ipv6LinkLocalMulticastScope
|
||||
return IsV6MulticastAddress(addr) && V6MulticastScope(addr) == IPv6LinkLocalMulticastScope
|
||||
}
|
||||
|
||||
// AppendOpaqueInterfaceIdentifier appends a 64 bit opaque interface identifier
|
||||
@@ -462,7 +472,7 @@ func ScopeForIPv6Address(addr tcpip.Address) (IPv6AddressScope, tcpip.Error) {
|
||||
case IsV6LinkLocalMulticastAddress(addr):
|
||||
return LinkLocalScope, nil
|
||||
|
||||
case IsV6LinkLocalAddress(addr):
|
||||
case IsV6LinkLocalUnicastAddress(addr):
|
||||
return LinkLocalScope, nil
|
||||
|
||||
default:
|
||||
@@ -520,3 +530,46 @@ func GenerateTempIPv6SLAACAddr(tempIIDHistory []byte, stableAddr tcpip.Address)
|
||||
PrefixLen: IIDOffsetInIPv6Address * 8,
|
||||
}
|
||||
}
|
||||
|
||||
// IPv6MulticastScope is the scope of a multicast IPv6 address, as defined by
|
||||
// RFC 7346 section 2.
|
||||
type IPv6MulticastScope uint8
|
||||
|
||||
// The various values for IPv6 multicast scopes, as per RFC 7346 section 2:
|
||||
//
|
||||
// +------+--------------------------+-------------------------+
|
||||
// | scop | NAME | REFERENCE |
|
||||
// +------+--------------------------+-------------------------+
|
||||
// | 0 | Reserved | [RFC4291], RFC 7346 |
|
||||
// | 1 | Interface-Local scope | [RFC4291], RFC 7346 |
|
||||
// | 2 | Link-Local scope | [RFC4291], RFC 7346 |
|
||||
// | 3 | Realm-Local scope | [RFC4291], RFC 7346 |
|
||||
// | 4 | Admin-Local scope | [RFC4291], RFC 7346 |
|
||||
// | 5 | Site-Local scope | [RFC4291], RFC 7346 |
|
||||
// | 6 | Unassigned | |
|
||||
// | 7 | Unassigned | |
|
||||
// | 8 | Organization-Local scope | [RFC4291], RFC 7346 |
|
||||
// | 9 | Unassigned | |
|
||||
// | A | Unassigned | |
|
||||
// | B | Unassigned | |
|
||||
// | C | Unassigned | |
|
||||
// | D | Unassigned | |
|
||||
// | E | Global scope | [RFC4291], RFC 7346 |
|
||||
// | F | Reserved | [RFC4291], RFC 7346 |
|
||||
// +------+--------------------------+-------------------------+
|
||||
const (
|
||||
IPv6Reserved0MulticastScope = IPv6MulticastScope(0x0)
|
||||
IPv6InterfaceLocalMulticastScope = IPv6MulticastScope(0x1)
|
||||
IPv6LinkLocalMulticastScope = IPv6MulticastScope(0x2)
|
||||
IPv6RealmLocalMulticastScope = IPv6MulticastScope(0x3)
|
||||
IPv6AdminLocalMulticastScope = IPv6MulticastScope(0x4)
|
||||
IPv6SiteLocalMulticastScope = IPv6MulticastScope(0x5)
|
||||
IPv6OrganizationLocalMulticastScope = IPv6MulticastScope(0x8)
|
||||
IPv6GlobalMulticastScope = IPv6MulticastScope(0xE)
|
||||
IPv6ReservedFMulticastScope = IPv6MulticastScope(0xF)
|
||||
)
|
||||
|
||||
// V6MulticastScope returns the scope of a multicast address.
|
||||
func V6MulticastScope(addr tcpip.Address) IPv6MulticastScope {
|
||||
return IPv6MulticastScope(addr[ipv6MulticastAddressScopeByteIdx] & ipv6MulticastAddressScopeMask)
|
||||
}
|
||||
+148
-8
@@ -77,12 +77,12 @@ const (
|
||||
|
||||
// ndpPrefixInformationOnLinkFlagMask is the mask of the On-Link Flag
|
||||
// field in the flags byte within an NDPPrefixInformation.
|
||||
ndpPrefixInformationOnLinkFlagMask = (1 << 7)
|
||||
ndpPrefixInformationOnLinkFlagMask = 1 << 7
|
||||
|
||||
// ndpPrefixInformationAutoAddrConfFlagMask is the mask of the
|
||||
// Autonomous Address-Configuration flag field in the flags byte within
|
||||
// an NDPPrefixInformation.
|
||||
ndpPrefixInformationAutoAddrConfFlagMask = (1 << 6)
|
||||
ndpPrefixInformationAutoAddrConfFlagMask = 1 << 6
|
||||
|
||||
// ndpPrefixInformationReserved1FlagsMask is the mask of the Reserved1
|
||||
// field in the flags byte within an NDPPrefixInformation.
|
||||
@@ -148,15 +148,10 @@ const (
|
||||
// NDP option. That is, the length field for NDP options is in units of
|
||||
// 8 octets, as per RFC 4861 section 4.6.
|
||||
lengthByteUnits = 8
|
||||
)
|
||||
|
||||
var (
|
||||
// NDPInfiniteLifetime is a value that represents infinity for the
|
||||
// 4-byte lifetime fields found in various NDP options. Its value is
|
||||
// (2^32 - 1)s = 4294967295s.
|
||||
//
|
||||
// This is a variable instead of a constant so that tests can change
|
||||
// this value to a smaller value. It should only be modified by tests.
|
||||
NDPInfiniteLifetime = time.Second * math.MaxUint32
|
||||
)
|
||||
|
||||
@@ -238,6 +233,17 @@ func (i *NDPOptionIterator) Next() (NDPOption, bool, error) {
|
||||
case ndpNonceOptionType:
|
||||
return NDPNonceOption(body), false, nil
|
||||
|
||||
case ndpRouteInformationType:
|
||||
if numBodyBytes > ndpRouteInformationMaxLength {
|
||||
return nil, true, fmt.Errorf("got %d bytes for NDP Route Information option's body, expected at max %d bytes: %w", numBodyBytes, ndpRouteInformationMaxLength, ErrNDPOptMalformedBody)
|
||||
}
|
||||
opt := NDPRouteInformation(body)
|
||||
if err := opt.hasError(); err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
|
||||
return opt, false, nil
|
||||
|
||||
case ndpPrefixInformationType:
|
||||
// Make sure the length of a Prefix Information option
|
||||
// body is ndpPrefixInformationLength, as per RFC 4861
|
||||
@@ -451,7 +457,7 @@ func (o NDPNonceOption) String() string {
|
||||
|
||||
// Nonce returns the nonce value this option holds.
|
||||
func (o NDPNonceOption) Nonce() []byte {
|
||||
return []byte(o)
|
||||
return o
|
||||
}
|
||||
|
||||
// NDPSourceLinkLayerAddressOption is the NDP Source Link Layer Option
|
||||
@@ -935,3 +941,137 @@ func isUpperLetter(b byte) bool {
|
||||
func isDigit(b byte) bool {
|
||||
return b >= '0' && b <= '9'
|
||||
}
|
||||
|
||||
// As per RFC 4191 section 2.3,
|
||||
//
|
||||
// 2.3. Route Information Option
|
||||
//
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Type | Length | Prefix Length |Resvd|Prf|Resvd|
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Route Lifetime |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Prefix (Variable Length) |
|
||||
// . .
|
||||
// . .
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
//
|
||||
// Fields:
|
||||
//
|
||||
// Type 24
|
||||
//
|
||||
//
|
||||
// Length 8-bit unsigned integer. The length of the option
|
||||
// (including the Type and Length fields) in units of 8
|
||||
// octets. The Length field is 1, 2, or 3 depending on the
|
||||
// Prefix Length. If Prefix Length is greater than 64, then
|
||||
// Length must be 3. If Prefix Length is greater than 0,
|
||||
// then Length must be 2 or 3. If Prefix Length is zero,
|
||||
// then Length must be 1, 2, or 3.
|
||||
const (
|
||||
ndpRouteInformationType = ndpOptionIdentifier(24)
|
||||
ndpRouteInformationMaxLength = 22
|
||||
|
||||
ndpRouteInformationPrefixLengthIdx = 0
|
||||
ndpRouteInformationFlagsIdx = 1
|
||||
ndpRouteInformationPrfShift = 3
|
||||
ndpRouteInformationPrfMask = 3 << ndpRouteInformationPrfShift
|
||||
ndpRouteInformationRouteLifetimeIdx = 2
|
||||
ndpRouteInformationRoutePrefixIdx = 6
|
||||
)
|
||||
|
||||
// NDPRouteInformation is the NDP Router Information option, as defined by
|
||||
// RFC 4191 section 2.3.
|
||||
type NDPRouteInformation []byte
|
||||
|
||||
func (NDPRouteInformation) kind() ndpOptionIdentifier {
|
||||
return ndpRouteInformationType
|
||||
}
|
||||
|
||||
func (o NDPRouteInformation) length() int {
|
||||
return len(o)
|
||||
}
|
||||
|
||||
func (o NDPRouteInformation) serializeInto(b []byte) int {
|
||||
return copy(b, o)
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (o NDPRouteInformation) String() string {
|
||||
return fmt.Sprintf("%T", o)
|
||||
}
|
||||
|
||||
// PrefixLength returns the length of the prefix.
|
||||
func (o NDPRouteInformation) PrefixLength() uint8 {
|
||||
return o[ndpRouteInformationPrefixLengthIdx]
|
||||
}
|
||||
|
||||
// RoutePreference returns the preference of the route over other routes to the
|
||||
// same destination but through a different router.
|
||||
func (o NDPRouteInformation) RoutePreference() NDPRoutePreference {
|
||||
return NDPRoutePreference((o[ndpRouteInformationFlagsIdx] & ndpRouteInformationPrfMask) >> ndpRouteInformationPrfShift)
|
||||
}
|
||||
|
||||
// RouteLifetime returns the lifetime of the route.
|
||||
//
|
||||
// Note, a value of 0 implies the route is now invalid and a value of
|
||||
// infinity/forever is represented by NDPInfiniteLifetime.
|
||||
func (o NDPRouteInformation) RouteLifetime() time.Duration {
|
||||
return time.Second * time.Duration(binary.BigEndian.Uint32(o[ndpRouteInformationRouteLifetimeIdx:]))
|
||||
}
|
||||
|
||||
// Prefix returns the prefix of the destination subnet this route is for.
|
||||
func (o NDPRouteInformation) Prefix() (tcpip.Subnet, error) {
|
||||
prefixLength := int(o.PrefixLength())
|
||||
if max := IPv6AddressSize * 8; prefixLength > max {
|
||||
return tcpip.Subnet{}, fmt.Errorf("got prefix length = %d, want <= %d", prefixLength, max)
|
||||
}
|
||||
|
||||
prefix := o[ndpRouteInformationRoutePrefixIdx:]
|
||||
var addrBytes [IPv6AddressSize]byte
|
||||
if n := copy(addrBytes[:], prefix); n != len(prefix) {
|
||||
panic(fmt.Sprintf("got copy(addrBytes, prefix) = %d, want = %d", n, len(prefix)))
|
||||
}
|
||||
|
||||
return tcpip.AddressWithPrefix{
|
||||
Address: tcpip.Address(addrBytes[:]),
|
||||
PrefixLen: prefixLength,
|
||||
}.Subnet(), nil
|
||||
}
|
||||
|
||||
func (o NDPRouteInformation) hasError() error {
|
||||
l := len(o)
|
||||
if l < ndpRouteInformationRoutePrefixIdx {
|
||||
return fmt.Errorf("%T too small, got = %d bytes: %w", o, l, ErrNDPOptMalformedBody)
|
||||
}
|
||||
|
||||
prefixLength := int(o.PrefixLength())
|
||||
if max := IPv6AddressSize * 8; prefixLength > max {
|
||||
return fmt.Errorf("got prefix length = %d, want <= %d: %w", prefixLength, max, ErrNDPOptMalformedBody)
|
||||
}
|
||||
|
||||
// Length 8-bit unsigned integer. The length of the option
|
||||
// (including the Type and Length fields) in units of 8
|
||||
// octets. The Length field is 1, 2, or 3 depending on the
|
||||
// Prefix Length. If Prefix Length is greater than 64, then
|
||||
// Length must be 3. If Prefix Length is greater than 0,
|
||||
// then Length must be 2 or 3. If Prefix Length is zero,
|
||||
// then Length must be 1, 2, or 3.
|
||||
l += 2 // Add 2 bytes for the type and length bytes.
|
||||
lengthField := l / lengthByteUnits
|
||||
if prefixLength > 64 {
|
||||
if lengthField != 3 {
|
||||
return fmt.Errorf("Length field must be 3 when Prefix Length (%d) is > 64 (got = %d): %w", prefixLength, lengthField, ErrNDPOptMalformedBody)
|
||||
}
|
||||
} else if prefixLength > 0 {
|
||||
if lengthField != 2 && lengthField != 3 {
|
||||
return fmt.Errorf("Length field must be 2 or 3 when Prefix Length (%d) is between 0 and 64 (got = %d): %w", prefixLength, lengthField, ErrNDPOptMalformedBody)
|
||||
}
|
||||
} else if lengthField == 0 || lengthField > 3 {
|
||||
return fmt.Errorf("Length field must be 1, 2, or 3 when Prefix Length is zero (got = %d): %w", lengthField, ErrNDPOptMalformedBody)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+93
-1
@@ -16,15 +16,94 @@ package header
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
var _ fmt.Stringer = NDPRoutePreference(0)
|
||||
|
||||
// NDPRoutePreference is the preference values for default routers or
|
||||
// more-specific routes.
|
||||
//
|
||||
// As per RFC 4191 section 2.1,
|
||||
//
|
||||
// Default router preferences and preferences for more-specific routes
|
||||
// are encoded the same way.
|
||||
//
|
||||
// Preference values are encoded as a two-bit signed integer, as
|
||||
// follows:
|
||||
//
|
||||
// 01 High
|
||||
// 00 Medium (default)
|
||||
// 11 Low
|
||||
// 10 Reserved - MUST NOT be sent
|
||||
//
|
||||
// Note that implementations can treat the value as a two-bit signed
|
||||
// integer.
|
||||
//
|
||||
// Having just three values reinforces that they are not metrics and
|
||||
// more values do not appear to be necessary for reasonable scenarios.
|
||||
type NDPRoutePreference uint8
|
||||
|
||||
const (
|
||||
// HighRoutePreference indicates a high preference, as per
|
||||
// RFC 4191 section 2.1.
|
||||
HighRoutePreference NDPRoutePreference = 0b01
|
||||
|
||||
// MediumRoutePreference indicates a medium preference, as per
|
||||
// RFC 4191 section 2.1.
|
||||
//
|
||||
// This is the default preference value.
|
||||
MediumRoutePreference = 0b00
|
||||
|
||||
// LowRoutePreference indicates a low preference, as per
|
||||
// RFC 4191 section 2.1.
|
||||
LowRoutePreference = 0b11
|
||||
|
||||
// ReservedRoutePreference is a reserved preference value, as per
|
||||
// RFC 4191 section 2.1.
|
||||
//
|
||||
// It MUST NOT be sent.
|
||||
ReservedRoutePreference = 0b10
|
||||
)
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (p NDPRoutePreference) String() string {
|
||||
switch p {
|
||||
case HighRoutePreference:
|
||||
return "HighRoutePreference"
|
||||
case MediumRoutePreference:
|
||||
return "MediumRoutePreference"
|
||||
case LowRoutePreference:
|
||||
return "LowRoutePreference"
|
||||
case ReservedRoutePreference:
|
||||
return "ReservedRoutePreference"
|
||||
default:
|
||||
return fmt.Sprintf("NDPRoutePreference(%d)", p)
|
||||
}
|
||||
}
|
||||
|
||||
// NDPRouterAdvert is an NDP Router Advertisement message. It will only contain
|
||||
// the body of an ICMPv6 packet.
|
||||
//
|
||||
// See RFC 4861 section 4.2 for more details.
|
||||
// See RFC 4861 section 4.2 and RFC 4191 section 2.2 for more details.
|
||||
type NDPRouterAdvert []byte
|
||||
|
||||
// As per RFC 4191 section 2.2,
|
||||
//
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Type | Code | Checksum |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Cur Hop Limit |M|O|H|Prf|Resvd| Router Lifetime |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Reachable Time |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Retrans Timer |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Options ...
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-
|
||||
const (
|
||||
// NDPRAMinimumSize is the minimum size of a valid NDP Router
|
||||
// Advertisement message (body of an ICMPv6 packet).
|
||||
@@ -47,6 +126,14 @@ const (
|
||||
// within the bit-field/flags byte of an NDPRouterAdvert.
|
||||
ndpRAOtherConfFlagMask = (1 << 6)
|
||||
|
||||
// ndpDefaultRouterPreferenceShift is the shift of the Prf (Default Router
|
||||
// Preference) field within the flags byte of an NDPRouterAdvert.
|
||||
ndpDefaultRouterPreferenceShift = 3
|
||||
|
||||
// ndpDefaultRouterPreferenceMask is the mask of the Prf (Default Router
|
||||
// Preference) field within the flags byte of an NDPRouterAdvert.
|
||||
ndpDefaultRouterPreferenceMask = (0b11 << ndpDefaultRouterPreferenceShift)
|
||||
|
||||
// ndpRARouterLifetimeOffset is the start of the 2-byte Router Lifetime
|
||||
// field within an NDPRouterAdvert.
|
||||
ndpRARouterLifetimeOffset = 2
|
||||
@@ -80,6 +167,11 @@ func (b NDPRouterAdvert) OtherConfFlag() bool {
|
||||
return b[ndpRAFlagsOffset]&ndpRAOtherConfFlagMask != 0
|
||||
}
|
||||
|
||||
// DefaultRouterPreference returns the Default Router Preference field.
|
||||
func (b NDPRouterAdvert) DefaultRouterPreference() NDPRoutePreference {
|
||||
return NDPRoutePreference((b[ndpRAFlagsOffset] & ndpDefaultRouterPreferenceMask) >> ndpDefaultRouterPreferenceShift)
|
||||
}
|
||||
|
||||
// RouterLifetime returns the lifetime associated with the default router. A
|
||||
// value of 0 means the source of the Router Advertisement is not a default
|
||||
// router and SHOULD NOT appear on the default router list. Note, a value of 0
|
||||
|
||||
+73
-1
@@ -60,9 +60,13 @@ func IPv4(pkt *stack.PacketBuffer) bool {
|
||||
return false
|
||||
}
|
||||
ipHdr = header.IPv4(hdr)
|
||||
length := int(ipHdr.TotalLength()) - len(hdr)
|
||||
if length < 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
pkt.NetworkProtocolNumber = header.IPv4ProtocolNumber
|
||||
pkt.Data().CapLength(int(ipHdr.TotalLength()) - len(hdr))
|
||||
pkt.Data().CapLength(length)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -106,6 +110,16 @@ traverseExtensions:
|
||||
|
||||
switch extHdr := extHdr.(type) {
|
||||
case header.IPv6FragmentExtHdr:
|
||||
if extHdr.IsAtomic() {
|
||||
// This fragment extension header indicates that this packet is an
|
||||
// atomic fragment. An atomic fragment is a fragment that contains
|
||||
// all the data required to reassemble a full packet. As per RFC 6946,
|
||||
// atomic fragments must not interfere with "normal" fragmented traffic
|
||||
// so we skip processing the fragment instead of feeding it through the
|
||||
// reassembly process below.
|
||||
continue
|
||||
}
|
||||
|
||||
if fragID == 0 && fragOffset == 0 && !fragMore {
|
||||
fragID = extHdr.ID()
|
||||
fragOffset = extHdr.FragmentOffset()
|
||||
@@ -171,3 +185,61 @@ func TCP(pkt *stack.PacketBuffer) bool {
|
||||
pkt.TransportProtocolNumber = header.TCPProtocolNumber
|
||||
return ok
|
||||
}
|
||||
|
||||
// ICMPv4 populates the packet buffer's transport header with an ICMPv4 header,
|
||||
// if present.
|
||||
//
|
||||
// Returns true if an ICMPv4 header was successfully parsed.
|
||||
func ICMPv4(pkt *stack.PacketBuffer) bool {
|
||||
if _, ok := pkt.TransportHeader().Consume(header.ICMPv4MinimumSize); ok {
|
||||
pkt.TransportProtocolNumber = header.ICMPv4ProtocolNumber
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ICMPv6 populates the packet buffer's transport header with an ICMPv4 header,
|
||||
// if present.
|
||||
//
|
||||
// Returns true if an ICMPv6 header was successfully parsed.
|
||||
func ICMPv6(pkt *stack.PacketBuffer) bool {
|
||||
hdr, ok := pkt.Data().PullUp(header.ICMPv6MinimumSize)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
h := header.ICMPv6(hdr)
|
||||
switch h.Type() {
|
||||
case header.ICMPv6RouterSolicit,
|
||||
header.ICMPv6RouterAdvert,
|
||||
header.ICMPv6NeighborSolicit,
|
||||
header.ICMPv6NeighborAdvert,
|
||||
header.ICMPv6RedirectMsg:
|
||||
size := pkt.Data().Size()
|
||||
if _, ok := pkt.TransportHeader().Consume(size); !ok {
|
||||
panic(fmt.Sprintf("expected to consume the full data of size = %d bytes into transport header", size))
|
||||
}
|
||||
case header.ICMPv6MulticastListenerQuery,
|
||||
header.ICMPv6MulticastListenerReport,
|
||||
header.ICMPv6MulticastListenerDone:
|
||||
size := header.ICMPv6HeaderSize + header.MLDMinimumSize
|
||||
if _, ok := pkt.TransportHeader().Consume(size); !ok {
|
||||
return false
|
||||
}
|
||||
case header.ICMPv6DstUnreachable,
|
||||
header.ICMPv6PacketTooBig,
|
||||
header.ICMPv6TimeExceeded,
|
||||
header.ICMPv6ParamProblem,
|
||||
header.ICMPv6EchoRequest,
|
||||
header.ICMPv6EchoReply:
|
||||
fallthrough
|
||||
default:
|
||||
if _, ok := pkt.TransportHeader().Consume(header.ICMPv6MinimumSize); !ok {
|
||||
// Checked above if the packet buffer holds at least the minimum size for
|
||||
// an ICMPv6 packet.
|
||||
panic(fmt.Sprintf("expected to consume %d bytes", header.ICMPv6MinimumSize))
|
||||
}
|
||||
}
|
||||
pkt.TransportProtocolNumber = header.ICMPv6ProtocolNumber
|
||||
return true
|
||||
}
|
||||
+69
-23
@@ -48,6 +48,16 @@ const (
|
||||
// TCPFlags is the dedicated type for TCP flags.
|
||||
type TCPFlags uint8
|
||||
|
||||
// Intersects returns true iff there are flags common to both f and o.
|
||||
func (f TCPFlags) Intersects(o TCPFlags) bool {
|
||||
return f&o != 0
|
||||
}
|
||||
|
||||
// Contains returns true iff all the flags in o are contained within f.
|
||||
func (f TCPFlags) Contains(o TCPFlags) bool {
|
||||
return f&o == o
|
||||
}
|
||||
|
||||
// String implements Stringer.String.
|
||||
func (f TCPFlags) String() string {
|
||||
flagsStr := []byte("FSRPAU")
|
||||
@@ -216,104 +226,104 @@ const (
|
||||
TCPDefaultMSS = 536
|
||||
)
|
||||
|
||||
// SourcePort returns the "source port" field of the tcp header.
|
||||
// SourcePort returns the "source port" field of the TCP header.
|
||||
func (b TCP) SourcePort() uint16 {
|
||||
return binary.BigEndian.Uint16(b[TCPSrcPortOffset:])
|
||||
}
|
||||
|
||||
// DestinationPort returns the "destination port" field of the tcp header.
|
||||
// DestinationPort returns the "destination port" field of the TCP header.
|
||||
func (b TCP) DestinationPort() uint16 {
|
||||
return binary.BigEndian.Uint16(b[TCPDstPortOffset:])
|
||||
}
|
||||
|
||||
// SequenceNumber returns the "sequence number" field of the tcp header.
|
||||
// SequenceNumber returns the "sequence number" field of the TCP header.
|
||||
func (b TCP) SequenceNumber() uint32 {
|
||||
return binary.BigEndian.Uint32(b[TCPSeqNumOffset:])
|
||||
}
|
||||
|
||||
// AckNumber returns the "ack number" field of the tcp header.
|
||||
// AckNumber returns the "ack number" field of the TCP header.
|
||||
func (b TCP) AckNumber() uint32 {
|
||||
return binary.BigEndian.Uint32(b[TCPAckNumOffset:])
|
||||
}
|
||||
|
||||
// DataOffset returns the "data offset" field of the tcp header. The return
|
||||
// DataOffset returns the "data offset" field of the TCP header. The return
|
||||
// value is the length of the TCP header in bytes.
|
||||
func (b TCP) DataOffset() uint8 {
|
||||
return (b[TCPDataOffset] >> 4) * 4
|
||||
}
|
||||
|
||||
// Payload returns the data in the tcp packet.
|
||||
// Payload returns the data in the TCP packet.
|
||||
func (b TCP) Payload() []byte {
|
||||
return b[b.DataOffset():]
|
||||
}
|
||||
|
||||
// Flags returns the flags field of the tcp header.
|
||||
// Flags returns the flags field of the TCP header.
|
||||
func (b TCP) Flags() TCPFlags {
|
||||
return TCPFlags(b[TCPFlagsOffset])
|
||||
}
|
||||
|
||||
// WindowSize returns the "window size" field of the tcp header.
|
||||
// WindowSize returns the "window size" field of the TCP header.
|
||||
func (b TCP) WindowSize() uint16 {
|
||||
return binary.BigEndian.Uint16(b[TCPWinSizeOffset:])
|
||||
}
|
||||
|
||||
// Checksum returns the "checksum" field of the tcp header.
|
||||
// Checksum returns the "checksum" field of the TCP header.
|
||||
func (b TCP) Checksum() uint16 {
|
||||
return binary.BigEndian.Uint16(b[TCPChecksumOffset:])
|
||||
}
|
||||
|
||||
// UrgentPointer returns the "urgent pointer" field of the tcp header.
|
||||
// UrgentPointer returns the "urgent pointer" field of the TCP header.
|
||||
func (b TCP) UrgentPointer() uint16 {
|
||||
return binary.BigEndian.Uint16(b[TCPUrgentPtrOffset:])
|
||||
}
|
||||
|
||||
// SetSourcePort sets the "source port" field of the tcp header.
|
||||
// SetSourcePort sets the "source port" field of the TCP header.
|
||||
func (b TCP) SetSourcePort(port uint16) {
|
||||
binary.BigEndian.PutUint16(b[TCPSrcPortOffset:], port)
|
||||
}
|
||||
|
||||
// SetDestinationPort sets the "destination port" field of the tcp header.
|
||||
// SetDestinationPort sets the "destination port" field of the TCP header.
|
||||
func (b TCP) SetDestinationPort(port uint16) {
|
||||
binary.BigEndian.PutUint16(b[TCPDstPortOffset:], port)
|
||||
}
|
||||
|
||||
// SetChecksum sets the checksum field of the tcp header.
|
||||
// SetChecksum sets the checksum field of the TCP header.
|
||||
func (b TCP) SetChecksum(checksum uint16) {
|
||||
binary.BigEndian.PutUint16(b[TCPChecksumOffset:], checksum)
|
||||
}
|
||||
|
||||
// SetDataOffset sets the data offset field of the tcp header. headerLen should
|
||||
// SetDataOffset sets the data offset field of the TCP header. headerLen should
|
||||
// be the length of the TCP header in bytes.
|
||||
func (b TCP) SetDataOffset(headerLen uint8) {
|
||||
b[TCPDataOffset] = (headerLen / 4) << 4
|
||||
}
|
||||
|
||||
// SetSequenceNumber sets the sequence number field of the tcp header.
|
||||
// SetSequenceNumber sets the sequence number field of the TCP header.
|
||||
func (b TCP) SetSequenceNumber(seqNum uint32) {
|
||||
binary.BigEndian.PutUint32(b[TCPSeqNumOffset:], seqNum)
|
||||
}
|
||||
|
||||
// SetAckNumber sets the ack number field of the tcp header.
|
||||
// SetAckNumber sets the ack number field of the TCP header.
|
||||
func (b TCP) SetAckNumber(ackNum uint32) {
|
||||
binary.BigEndian.PutUint32(b[TCPAckNumOffset:], ackNum)
|
||||
}
|
||||
|
||||
// SetFlags sets the flags field of the tcp header.
|
||||
// SetFlags sets the flags field of the TCP header.
|
||||
func (b TCP) SetFlags(flags uint8) {
|
||||
b[TCPFlagsOffset] = flags
|
||||
}
|
||||
|
||||
// SetWindowSize sets the window size field of the tcp header.
|
||||
// SetWindowSize sets the window size field of the TCP header.
|
||||
func (b TCP) SetWindowSize(rcvwnd uint16) {
|
||||
binary.BigEndian.PutUint16(b[TCPWinSizeOffset:], rcvwnd)
|
||||
}
|
||||
|
||||
// SetUrgentPoiner sets the window size field of the tcp header.
|
||||
// SetUrgentPoiner sets the window size field of the TCP header.
|
||||
func (b TCP) SetUrgentPoiner(urgentPointer uint16) {
|
||||
binary.BigEndian.PutUint16(b[TCPUrgentPtrOffset:], urgentPointer)
|
||||
}
|
||||
|
||||
// CalculateChecksum calculates the checksum of the tcp segment.
|
||||
// CalculateChecksum calculates the checksum of the TCP segment.
|
||||
// partialChecksum is the checksum of the network-layer pseudo-header
|
||||
// and the checksum of the segment data.
|
||||
func (b TCP) CalculateChecksum(partialChecksum uint16) uint16 {
|
||||
@@ -321,6 +331,13 @@ func (b TCP) CalculateChecksum(partialChecksum uint16) uint16 {
|
||||
return Checksum(b[:b.DataOffset()], partialChecksum)
|
||||
}
|
||||
|
||||
// IsChecksumValid returns true iff the TCP header's checksum is valid.
|
||||
func (b TCP) IsChecksumValid(src, dst tcpip.Address, payloadChecksum, payloadLength uint16) bool {
|
||||
xsum := PseudoHeaderChecksum(TCPProtocolNumber, src, dst, uint16(b.DataOffset())+payloadLength)
|
||||
xsum = ChecksumCombine(xsum, payloadChecksum)
|
||||
return b.CalculateChecksum(xsum) == 0xffff
|
||||
}
|
||||
|
||||
// Options returns a slice that holds the unparsed TCP options in the segment.
|
||||
func (b TCP) Options() []byte {
|
||||
return b[TCPMinimumSize:b.DataOffset()]
|
||||
@@ -340,7 +357,7 @@ func (b TCP) encodeSubset(seq, ack uint32, flags TCPFlags, rcvwnd uint16) {
|
||||
binary.BigEndian.PutUint16(b[TCPWinSizeOffset:], rcvwnd)
|
||||
}
|
||||
|
||||
// Encode encodes all the fields of the tcp header.
|
||||
// Encode encodes all the fields of the TCP header.
|
||||
func (b TCP) Encode(t *TCPFields) {
|
||||
b.encodeSubset(t.SeqNum, t.AckNum, t.Flags, t.WindowSize)
|
||||
binary.BigEndian.PutUint16(b[TCPSrcPortOffset:], t.SrcPort)
|
||||
@@ -350,7 +367,7 @@ func (b TCP) Encode(t *TCPFields) {
|
||||
binary.BigEndian.PutUint16(b[TCPUrgentPtrOffset:], t.UrgentPointer)
|
||||
}
|
||||
|
||||
// EncodePartial updates a subset of the fields of the tcp header. It is useful
|
||||
// EncodePartial updates a subset of the fields of the TCP header. It is useful
|
||||
// in cases when similar segments are produced.
|
||||
func (b TCP) EncodePartial(partialChecksum, length uint16, seqnum, acknum uint32, flags TCPFlags, rcvwnd uint16) {
|
||||
// Add the total length and "flags" field contributions to the checksum.
|
||||
@@ -373,8 +390,37 @@ func (b TCP) EncodePartial(partialChecksum, length uint16, seqnum, acknum uint32
|
||||
b.SetChecksum(^checksum)
|
||||
}
|
||||
|
||||
// SetSourcePortWithChecksumUpdate implements ChecksummableTransport.
|
||||
func (b TCP) SetSourcePortWithChecksumUpdate(new uint16) {
|
||||
old := b.SourcePort()
|
||||
b.SetSourcePort(new)
|
||||
b.SetChecksum(^checksumUpdate2ByteAlignedUint16(^b.Checksum(), old, new))
|
||||
}
|
||||
|
||||
// SetDestinationPortWithChecksumUpdate implements ChecksummableTransport.
|
||||
func (b TCP) SetDestinationPortWithChecksumUpdate(new uint16) {
|
||||
old := b.DestinationPort()
|
||||
b.SetDestinationPort(new)
|
||||
b.SetChecksum(^checksumUpdate2ByteAlignedUint16(^b.Checksum(), old, new))
|
||||
}
|
||||
|
||||
// UpdateChecksumPseudoHeaderAddress implements ChecksummableTransport.
|
||||
func (b TCP) UpdateChecksumPseudoHeaderAddress(old, new tcpip.Address, fullChecksum bool) {
|
||||
xsum := b.Checksum()
|
||||
if fullChecksum {
|
||||
xsum = ^xsum
|
||||
}
|
||||
|
||||
xsum = checksumUpdate2ByteAlignedAddress(xsum, old, new)
|
||||
if fullChecksum {
|
||||
xsum = ^xsum
|
||||
}
|
||||
|
||||
b.SetChecksum(xsum)
|
||||
}
|
||||
|
||||
// ParseSynOptions parses the options received in a SYN segment and returns the
|
||||
// relevant ones. opts should point to the option part of the TCP Header.
|
||||
// relevant ones. opts should point to the option part of the TCP header.
|
||||
func ParseSynOptions(opts []byte, isAck bool) TCPSynOptions {
|
||||
limit := len(opts)
|
||||
|
||||
|
||||
+46
-10
@@ -64,17 +64,17 @@ const (
|
||||
UDPProtocolNumber tcpip.TransportProtocolNumber = 17
|
||||
)
|
||||
|
||||
// SourcePort returns the "source port" field of the udp header.
|
||||
// SourcePort returns the "source port" field of the UDP header.
|
||||
func (b UDP) SourcePort() uint16 {
|
||||
return binary.BigEndian.Uint16(b[udpSrcPort:])
|
||||
}
|
||||
|
||||
// DestinationPort returns the "destination port" field of the udp header.
|
||||
// DestinationPort returns the "destination port" field of the UDP header.
|
||||
func (b UDP) DestinationPort() uint16 {
|
||||
return binary.BigEndian.Uint16(b[udpDstPort:])
|
||||
}
|
||||
|
||||
// Length returns the "length" field of the udp header.
|
||||
// Length returns the "length" field of the UDP header.
|
||||
func (b UDP) Length() uint16 {
|
||||
return binary.BigEndian.Uint16(b[udpLength:])
|
||||
}
|
||||
@@ -84,42 +84,78 @@ func (b UDP) Payload() []byte {
|
||||
return b[UDPMinimumSize:]
|
||||
}
|
||||
|
||||
// Checksum returns the "checksum" field of the udp header.
|
||||
// Checksum returns the "checksum" field of the UDP header.
|
||||
func (b UDP) Checksum() uint16 {
|
||||
return binary.BigEndian.Uint16(b[udpChecksum:])
|
||||
}
|
||||
|
||||
// SetSourcePort sets the "source port" field of the udp header.
|
||||
// SetSourcePort sets the "source port" field of the UDP header.
|
||||
func (b UDP) SetSourcePort(port uint16) {
|
||||
binary.BigEndian.PutUint16(b[udpSrcPort:], port)
|
||||
}
|
||||
|
||||
// SetDestinationPort sets the "destination port" field of the udp header.
|
||||
// SetDestinationPort sets the "destination port" field of the UDP header.
|
||||
func (b UDP) SetDestinationPort(port uint16) {
|
||||
binary.BigEndian.PutUint16(b[udpDstPort:], port)
|
||||
}
|
||||
|
||||
// SetChecksum sets the "checksum" field of the udp header.
|
||||
// SetChecksum sets the "checksum" field of the UDP header.
|
||||
func (b UDP) SetChecksum(checksum uint16) {
|
||||
binary.BigEndian.PutUint16(b[udpChecksum:], checksum)
|
||||
}
|
||||
|
||||
// SetLength sets the "length" field of the udp header.
|
||||
// SetLength sets the "length" field of the UDP header.
|
||||
func (b UDP) SetLength(length uint16) {
|
||||
binary.BigEndian.PutUint16(b[udpLength:], length)
|
||||
}
|
||||
|
||||
// CalculateChecksum calculates the checksum of the udp packet, given the
|
||||
// CalculateChecksum calculates the checksum of the UDP packet, given the
|
||||
// checksum of the network-layer pseudo-header and the checksum of the payload.
|
||||
func (b UDP) CalculateChecksum(partialChecksum uint16) uint16 {
|
||||
// Calculate the rest of the checksum.
|
||||
return Checksum(b[:UDPMinimumSize], partialChecksum)
|
||||
}
|
||||
|
||||
// Encode encodes all the fields of the udp header.
|
||||
// IsChecksumValid returns true iff the UDP header's checksum is valid.
|
||||
func (b UDP) IsChecksumValid(src, dst tcpip.Address, payloadChecksum uint16) bool {
|
||||
xsum := PseudoHeaderChecksum(UDPProtocolNumber, dst, src, b.Length())
|
||||
xsum = ChecksumCombine(xsum, payloadChecksum)
|
||||
return b.CalculateChecksum(xsum) == 0xffff
|
||||
}
|
||||
|
||||
// Encode encodes all the fields of the UDP header.
|
||||
func (b UDP) Encode(u *UDPFields) {
|
||||
binary.BigEndian.PutUint16(b[udpSrcPort:], u.SrcPort)
|
||||
binary.BigEndian.PutUint16(b[udpDstPort:], u.DstPort)
|
||||
binary.BigEndian.PutUint16(b[udpLength:], u.Length)
|
||||
binary.BigEndian.PutUint16(b[udpChecksum:], u.Checksum)
|
||||
}
|
||||
|
||||
// SetSourcePortWithChecksumUpdate implements ChecksummableTransport.
|
||||
func (b UDP) SetSourcePortWithChecksumUpdate(new uint16) {
|
||||
old := b.SourcePort()
|
||||
b.SetSourcePort(new)
|
||||
b.SetChecksum(^checksumUpdate2ByteAlignedUint16(^b.Checksum(), old, new))
|
||||
}
|
||||
|
||||
// SetDestinationPortWithChecksumUpdate implements ChecksummableTransport.
|
||||
func (b UDP) SetDestinationPortWithChecksumUpdate(new uint16) {
|
||||
old := b.DestinationPort()
|
||||
b.SetDestinationPort(new)
|
||||
b.SetChecksum(^checksumUpdate2ByteAlignedUint16(^b.Checksum(), old, new))
|
||||
}
|
||||
|
||||
// UpdateChecksumPseudoHeaderAddress implements ChecksummableTransport.
|
||||
func (b UDP) UpdateChecksumPseudoHeaderAddress(old, new tcpip.Address, fullChecksum bool) {
|
||||
xsum := b.Checksum()
|
||||
if fullChecksum {
|
||||
xsum = ^xsum
|
||||
}
|
||||
|
||||
xsum = checksumUpdate2ByteAlignedAddress(xsum, old, new)
|
||||
if fullChecksum {
|
||||
xsum = ^xsum
|
||||
}
|
||||
|
||||
b.SetChecksum(xsum)
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// 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 tcp contains internal type definitions that are not expected to be
|
||||
// used by anyone else outside pkg/tcpip.
|
||||
package tcp
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"inet.af/netstack/tcpip"
|
||||
)
|
||||
|
||||
// TSOffset is an offset applied to the value of the TSVal field in the TCP
|
||||
// Timestamp option.
|
||||
//
|
||||
// +stateify savable
|
||||
type TSOffset struct {
|
||||
milliseconds uint32
|
||||
}
|
||||
|
||||
// NewTSOffset creates a new TSOffset from milliseconds.
|
||||
func NewTSOffset(milliseconds uint32) TSOffset {
|
||||
return TSOffset{
|
||||
milliseconds: milliseconds,
|
||||
}
|
||||
}
|
||||
|
||||
// TSVal applies the offset to now and returns the timestamp in milliseconds.
|
||||
func (offset TSOffset) TSVal(now tcpip.MonotonicTime) uint32 {
|
||||
return uint32(now.Sub(tcpip.MonotonicTime{}).Milliseconds()) + offset.milliseconds
|
||||
}
|
||||
|
||||
// Elapsed calculates the elapsed time given now and the echoed back timestamp.
|
||||
func (offset TSOffset) Elapsed(now tcpip.MonotonicTime, tsEcr uint32) time.Duration {
|
||||
return time.Duration(offset.TSVal(now)-tsEcr) * time.Millisecond
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// automatically generated by stateify.
|
||||
|
||||
package tcp
|
||||
|
||||
import (
|
||||
"inet.af/netstack/state"
|
||||
)
|
||||
|
||||
func (offset *TSOffset) StateTypeName() string {
|
||||
return "pkg/tcpip/internal/tcp.TSOffset"
|
||||
}
|
||||
|
||||
func (offset *TSOffset) StateFields() []string {
|
||||
return []string{
|
||||
"milliseconds",
|
||||
}
|
||||
}
|
||||
|
||||
func (offset *TSOffset) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (offset *TSOffset) StateSave(stateSinkObject state.Sink) {
|
||||
offset.beforeSave()
|
||||
stateSinkObject.Save(0, &offset.milliseconds)
|
||||
}
|
||||
|
||||
func (offset *TSOffset) afterLoad() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (offset *TSOffset) StateLoad(stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &offset.milliseconds)
|
||||
}
|
||||
|
||||
func init() {
|
||||
state.Register((*TSOffset)(nil))
|
||||
}
|
||||
+1
-1
@@ -251,7 +251,7 @@ func (f *Fragmentation) releaseReassemblersLocked() {
|
||||
// The list is empty.
|
||||
break
|
||||
}
|
||||
elapsed := time.Duration(now-r.creationTime) * time.Nanosecond
|
||||
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.
|
||||
|
||||
Vendored
+4
@@ -19,6 +19,7 @@ func (l *reassemblerList) StateFields() []string {
|
||||
|
||||
func (l *reassemblerList) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (l *reassemblerList) StateSave(stateSinkObject state.Sink) {
|
||||
l.beforeSave()
|
||||
stateSinkObject.Save(0, &l.head)
|
||||
@@ -27,6 +28,7 @@ func (l *reassemblerList) StateSave(stateSinkObject state.Sink) {
|
||||
|
||||
func (l *reassemblerList) afterLoad() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (l *reassemblerList) StateLoad(stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &l.head)
|
||||
stateSourceObject.Load(1, &l.tail)
|
||||
@@ -45,6 +47,7 @@ func (e *reassemblerEntry) StateFields() []string {
|
||||
|
||||
func (e *reassemblerEntry) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *reassemblerEntry) StateSave(stateSinkObject state.Sink) {
|
||||
e.beforeSave()
|
||||
stateSinkObject.Save(0, &e.next)
|
||||
@@ -53,6 +56,7 @@ func (e *reassemblerEntry) StateSave(stateSinkObject state.Sink) {
|
||||
|
||||
func (e *reassemblerEntry) afterLoad() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *reassemblerEntry) StateLoad(stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &e.next)
|
||||
stateSourceObject.Load(1, &e.prev)
|
||||
|
||||
+14
-13
@@ -35,21 +35,21 @@ type hole struct {
|
||||
|
||||
type reassembler struct {
|
||||
reassemblerEntry
|
||||
id FragmentID
|
||||
memSize int
|
||||
proto uint8
|
||||
mu sync.Mutex
|
||||
holes []hole
|
||||
filled int
|
||||
done bool
|
||||
creationTime int64
|
||||
pkt *stack.PacketBuffer
|
||||
id FragmentID
|
||||
memSize int
|
||||
proto uint8
|
||||
mu sync.Mutex
|
||||
holes []hole
|
||||
filled int
|
||||
done bool
|
||||
createdAt tcpip.MonotonicTime
|
||||
pkt *stack.PacketBuffer
|
||||
}
|
||||
|
||||
func newReassembler(id FragmentID, clock tcpip.Clock) *reassembler {
|
||||
r := &reassembler{
|
||||
id: id,
|
||||
creationTime: clock.NowMonotonic(),
|
||||
id: id,
|
||||
createdAt: clock.NowMonotonic(),
|
||||
}
|
||||
r.holes = append(r.holes, hole{
|
||||
first: 0,
|
||||
@@ -149,6 +149,7 @@ func (r *reassembler) process(first, last uint16, more bool, proto uint8, pkt *s
|
||||
r.proto = proto
|
||||
}
|
||||
|
||||
pkt.IncRef()
|
||||
break
|
||||
}
|
||||
if !holeFound {
|
||||
@@ -166,9 +167,9 @@ func (r *reassembler) process(first, last uint16, more bool, proto uint8, pkt *s
|
||||
})
|
||||
|
||||
resPkt := r.holes[0].pkt
|
||||
resPkt.DecRef()
|
||||
for i := 1; i < len(r.holes); i++ {
|
||||
fragData := r.holes[i].pkt.Data()
|
||||
resPkt.Data().ReadFromData(fragData, fragData.Size())
|
||||
stack.MergeFragment(resPkt, r.holes[i].pkt)
|
||||
}
|
||||
return resPkt, r.proto, true, memConsumed, nil
|
||||
}
|
||||
|
||||
+2
@@ -83,6 +83,8 @@ func (d *DAD) Init(protocolMU sync.Locker, configs stack.DADConfigurations, opts
|
||||
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,
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
// 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"
|
||||
|
||||
"inet.af/netstack/tcpip"
|
||||
)
|
||||
|
||||
// ForwardingError represents an error that occured 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" }
|
||||
|
||||
// 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" }
|
||||
|
||||
// ErrLinkLocalSourceAddress indicates the received packet had a link-local
|
||||
// source address.
|
||||
type ErrLinkLocalSourceAddress struct{}
|
||||
|
||||
func (*ErrLinkLocalSourceAddress) isForwardingError() {}
|
||||
|
||||
func (*ErrLinkLocalSourceAddress) String() string { return "link local destination 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" }
|
||||
|
||||
// ErrNoRoute indicates that a route for the received packet couldn't be found.
|
||||
type ErrNoRoute struct{}
|
||||
|
||||
func (*ErrNoRoute) isForwardingError() {}
|
||||
|
||||
func (*ErrNoRoute) String() string { return "no route" }
|
||||
|
||||
// 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" }
|
||||
|
||||
// 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) }
|
||||
+8
-52
@@ -12,7 +12,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package ip holds IPv4/IPv6 common utilities.
|
||||
package ip
|
||||
|
||||
import (
|
||||
@@ -156,14 +155,6 @@ type GenericMulticastProtocolOptions struct {
|
||||
//
|
||||
// Unsolicited reports are transmitted when a group is newly joined.
|
||||
MaxUnsolicitedReportDelay time.Duration
|
||||
|
||||
// AllNodesAddress is a multicast address that all nodes on a network should
|
||||
// be a member of.
|
||||
//
|
||||
// This address will not have the generic multicast protocol performed on it;
|
||||
// it will be left in the non member/listener state, and packets will never
|
||||
// be sent for it.
|
||||
AllNodesAddress tcpip.Address
|
||||
}
|
||||
|
||||
// MulticastGroupProtocol is a multicast group protocol whose core state machine
|
||||
@@ -188,6 +179,10 @@ type MulticastGroupProtocol interface {
|
||||
|
||||
// SendLeave sends a multicast leave for the specified group address.
|
||||
SendLeave(groupAddress tcpip.Address) tcpip.Error
|
||||
|
||||
// ShouldPerformProtocol returns true iff the protocol should be performed for
|
||||
// the specified group.
|
||||
ShouldPerformProtocol(tcpip.Address) bool
|
||||
}
|
||||
|
||||
// GenericMulticastProtocolState is the per interface generic multicast protocol
|
||||
@@ -455,20 +450,7 @@ func (g *GenericMulticastProtocolState) initializeNewMemberLocked(groupAddress t
|
||||
|
||||
info.lastToSendReport = false
|
||||
|
||||
if groupAddress == g.opts.AllNodesAddress {
|
||||
// As per RFC 2236 section 6 page 10 (for IGMPv2),
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// As per RFC 2710 section 5 page 10 (for MLDv1),
|
||||
//
|
||||
// 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.
|
||||
if !g.opts.Protocol.ShouldPerformProtocol(groupAddress) {
|
||||
info.state = idleMember
|
||||
return
|
||||
}
|
||||
@@ -537,20 +519,7 @@ func (g *GenericMulticastProtocolState) maybeSendLeave(groupAddress tcpip.Addres
|
||||
return
|
||||
}
|
||||
|
||||
if groupAddress == g.opts.AllNodesAddress {
|
||||
// As per RFC 2236 section 6 page 10 (for IGMPv2),
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// As per RFC 2710 section 5 page 10 (for MLDv1),
|
||||
//
|
||||
// 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.
|
||||
if !g.opts.Protocol.ShouldPerformProtocol(groupAddress) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -627,20 +596,7 @@ func (g *GenericMulticastProtocolState) setDelayTimerForAddressRLocked(groupAddr
|
||||
return
|
||||
}
|
||||
|
||||
if groupAddress == g.opts.AllNodesAddress {
|
||||
// As per RFC 2236 section 6 page 10 (for IGMPv2),
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// As per RFC 2710 section 5 page 10 (for MLDv1),
|
||||
//
|
||||
// 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.
|
||||
if !g.opts.Protocol.ShouldPerformProtocol(groupAddress) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -655,7 +611,7 @@ func (g *GenericMulticastProtocolState) setDelayTimerForAddressRLocked(groupAddr
|
||||
// If a timer for any address is already running, it is reset to the new
|
||||
// random value only if the requested Maximum Response Delay is less than
|
||||
// the remaining value of the running timer.
|
||||
now := time.Unix(0 /* seconds */, g.opts.Clock.NowNanoseconds())
|
||||
now := g.opts.Clock.Now()
|
||||
if info.state == delayingMember {
|
||||
if info.delayedReportJobFiresAt.IsZero() {
|
||||
panic(fmt.Sprintf("delayed report unscheduled while in the delaying member state; group = %s", groupAddress))
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
// automatically generated by stateify.
|
||||
|
||||
package ip
|
||||
|
||||
import (
|
||||
"inet.af/netstack/state"
|
||||
)
|
||||
|
||||
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() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *ErrMessageTooLong) StateLoad(stateSourceObject state.Source) {
|
||||
}
|
||||
|
||||
func init() {
|
||||
state.Register((*ErrMessageTooLong)(nil))
|
||||
}
|
||||
+96
-20
@@ -16,77 +16,150 @@ package ip
|
||||
|
||||
import "inet.af/netstack/tcpip"
|
||||
|
||||
// LINT.IfChange(MultiCounterIPForwardingStats)
|
||||
|
||||
// MultiCounterIPForwardingStats holds IP forwarding statistics. Each counter
|
||||
// may have several versions.
|
||||
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
|
||||
|
||||
// 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
|
||||
|
||||
// Errors is the number of IP packets received which could not be
|
||||
// successfully forwarded.
|
||||
Errors 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.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)
|
||||
}
|
||||
|
||||
// LINT.ThenChange(:MultiCounterIPForwardingStats, ../../../tcpip.go:IPForwardingStats)
|
||||
|
||||
// LINT.IfChange(MultiCounterIPStats)
|
||||
|
||||
// MultiCounterIPStats holds IP statistics, each counter may have several
|
||||
// versions.
|
||||
type MultiCounterIPStats struct {
|
||||
// PacketsReceived is the total number of IP packets received from the link
|
||||
// PacketsReceived is the number of IP packets received from the link
|
||||
// layer.
|
||||
PacketsReceived tcpip.MultiCounterStat
|
||||
|
||||
// DisabledPacketsReceived is the total number of IP packets received from the
|
||||
// link layer when the IP layer is disabled.
|
||||
// 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 total number of IP packets
|
||||
// InvalidDestinationAddressesReceived is the number of IP packets
|
||||
// received with an unknown or invalid destination address.
|
||||
InvalidDestinationAddressesReceived tcpip.MultiCounterStat
|
||||
|
||||
// InvalidSourceAddressesReceived is the total number of IP packets received
|
||||
// with a source address that should never have been received on the wire.
|
||||
// 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 total number of incoming IP packets that are
|
||||
// successfully delivered to the transport layer.
|
||||
// PacketsDelivered is the number of incoming IP packets successfully
|
||||
// delivered to the transport layer.
|
||||
PacketsDelivered tcpip.MultiCounterStat
|
||||
|
||||
// PacketsSent is the total number of IP packets sent via WritePacket.
|
||||
// PacketsSent is the number of IP packets sent via WritePacket.
|
||||
PacketsSent tcpip.MultiCounterStat
|
||||
|
||||
// OutgoingPacketErrors is the total number of IP packets which failed to
|
||||
// OutgoingPacketErrors is the number of IP packets which failed to
|
||||
// write to a link-layer endpoint.
|
||||
OutgoingPacketErrors tcpip.MultiCounterStat
|
||||
|
||||
// MalformedPacketsReceived is the total number of IP Packets that were
|
||||
// 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 total number of IP Fragments that were
|
||||
// MalformedFragmentsReceived is the number of IP Fragments that were
|
||||
// dropped due to the fragment failing validation checks.
|
||||
MalformedFragmentsReceived tcpip.MultiCounterStat
|
||||
|
||||
// IPTablesPreroutingDropped is the total number of IP packets dropped in the
|
||||
// IPTablesPreroutingDropped is the number of IP packets dropped in the
|
||||
// Prerouting chain.
|
||||
IPTablesPreroutingDropped tcpip.MultiCounterStat
|
||||
|
||||
// IPTablesInputDropped is the total number of IP packets dropped in the Input
|
||||
// chain.
|
||||
// IPTablesInputDropped is the number of IP packets dropped in the
|
||||
// Input chain.
|
||||
IPTablesInputDropped tcpip.MultiCounterStat
|
||||
|
||||
// IPTablesOutputDropped is the total number of IP packets dropped in the
|
||||
// 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
|
||||
|
||||
// TODO(https://gvisor.dev/issues/5529): Move the IPv4-only option stats out
|
||||
// of IPStats.
|
||||
// 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 is the number of Record Route options
|
||||
// seen.
|
||||
OptionRecordRouteReceived tcpip.MultiCounterStat
|
||||
|
||||
// OptionRouterAlertReceived is the number of Router Alert options seen.
|
||||
// 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)
|
||||
@@ -97,11 +170,14 @@ func (m *MultiCounterIPStats) Init(a, b *tcpip.IPStats) {
|
||||
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(:MultiCounterIPStats, ../../../tcpip.go:IPStats)
|
||||
+123
-74
@@ -163,26 +163,26 @@ func (e *endpoint) handleControl(errInfo stack.TransportError, pkt *stack.Packet
|
||||
return
|
||||
}
|
||||
|
||||
// Skip the ip header, then deliver the error.
|
||||
pkt.Data().TrimFront(hlen)
|
||||
// Keep needed information before trimming header.
|
||||
p := hdr.TransportProtocol()
|
||||
e.dispatcher.DeliverTransportError(srcAddr, hdr.DestinationAddress(), ProtocolNumber, p, errInfo, pkt)
|
||||
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
|
||||
// TODO(gvisor.dev/issue/170): ICMP packets don't have their
|
||||
// TransportHeader fields set. See icmp/protocol.go:protocol.Parse for a
|
||||
// full explanation.
|
||||
v, ok := pkt.Data().PullUp(header.ICMPv4MinimumSize)
|
||||
if !ok {
|
||||
h := header.ICMPv4(pkt.TransportHeader().View())
|
||||
if len(h) < header.ICMPv4MinimumSize {
|
||||
received.invalid.Increment()
|
||||
return
|
||||
}
|
||||
h := header.ICMPv4(v)
|
||||
|
||||
// Only do in-stack processing if the checksum is correct.
|
||||
if pkt.Data().AsRange().Checksum() != 0xffff {
|
||||
if header.Checksum(h, pkt.Data().AsRange().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
|
||||
@@ -220,7 +220,6 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer) {
|
||||
_ = e.protocol.returnError(&icmpReasonParamProblem{
|
||||
pointer: optProblem.Pointer,
|
||||
}, pkt)
|
||||
e.protocol.stack.Stats().MalformedRcvdPackets.Increment()
|
||||
e.stats.ip.MalformedPacketsReceived.Increment()
|
||||
}
|
||||
return
|
||||
@@ -240,20 +239,15 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer) {
|
||||
case header.ICMPv4Echo:
|
||||
received.echoRequest.Increment()
|
||||
|
||||
sent := e.stats.icmp.packetsSent
|
||||
if !e.protocol.stack.AllowICMPMessage() {
|
||||
sent.rateLimited.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
// DeliverTransportPacket will take ownership of pkt so don't use it beyond
|
||||
// DeliverTransportPacket may modify pkt so don't use it beyond
|
||||
// this point. Make a deep copy of the data before pkt gets sent as we will
|
||||
// be modifying fields.
|
||||
// be modifying fields. Both the ICMP header (with its type modified to
|
||||
// EchoReply) and payload are reused in the reply packet.
|
||||
//
|
||||
// TODO(gvisor.dev/issue/4399): The copy may not be needed if there are no
|
||||
// waiting endpoints. Consider moving responsibility for doing the copy to
|
||||
// DeliverTransportPacket so that is is only done when needed.
|
||||
replyData := pkt.Data().AsRange().ToOwnedView()
|
||||
replyData := stack.PayloadSince(pkt.TransportHeader())
|
||||
ipHdr := header.IPv4(pkt.NetworkHeader().View())
|
||||
localAddressBroadcast := pkt.NetworkPacketInfo.LocalAddressBroadcast
|
||||
|
||||
@@ -281,6 +275,12 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer) {
|
||||
}
|
||||
defer r.Release()
|
||||
|
||||
sent := e.stats.icmp.packetsSent
|
||||
if !e.protocol.allowICMPReply(header.ICMPv4EchoReply, header.ICMPv4UnusedCode) {
|
||||
sent.rateLimited.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(gvisor.dev/issue/3810:) When adding protocol numbers into the
|
||||
// header information, we may have to change this code to handle the
|
||||
// ICMP header no longer being in the data buffer.
|
||||
@@ -305,8 +305,8 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer) {
|
||||
// We need to produce the entire packet in the data segment in order to
|
||||
// use WriteHeaderIncludedPacket(). WriteHeaderIncludedPacket sets the
|
||||
// total length and the header checksum so we don't need to set those here.
|
||||
replyIPHdr.SetSourceAddress(r.LocalAddress)
|
||||
replyIPHdr.SetDestinationAddress(r.RemoteAddress)
|
||||
replyIPHdr.SetSourceAddress(r.LocalAddress())
|
||||
replyIPHdr.SetDestinationAddress(r.RemoteAddress())
|
||||
replyIPHdr.SetTTL(r.DefaultTTL())
|
||||
|
||||
replyICMPHdr := header.ICMPv4(replyData)
|
||||
@@ -320,6 +320,7 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer) {
|
||||
ReserveHeaderBytes: int(r.MaxHeaderLength()),
|
||||
Data: replyVV,
|
||||
})
|
||||
defer replyPkt.DecRef()
|
||||
replyPkt.TransportProtocolNumber = header.ICMPv4ProtocolNumber
|
||||
|
||||
if err := r.WriteHeaderIncludedPacket(replyPkt); err != nil {
|
||||
@@ -331,19 +332,22 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer) {
|
||||
case header.ICMPv4EchoReply:
|
||||
received.echoReply.Increment()
|
||||
|
||||
// ICMP sockets expect the ICMP header to be present, so we don't consume
|
||||
// the ICMP header.
|
||||
e.dispatcher.DeliverTransportPacket(header.ICMPv4ProtocolNumber, pkt)
|
||||
|
||||
case header.ICMPv4DstUnreachable:
|
||||
received.dstUnreachable.Increment()
|
||||
|
||||
pkt.Data().TrimFront(header.ICMPv4MinimumSize)
|
||||
switch h.Code() {
|
||||
mtu := h.MTU()
|
||||
code := h.Code()
|
||||
switch code {
|
||||
case header.ICMPv4HostUnreachable:
|
||||
e.handleControl(&icmpv4DestinationHostUnreachableSockError{}, pkt)
|
||||
case header.ICMPv4PortUnreachable:
|
||||
e.handleControl(&icmpv4DestinationPortUnreachableSockError{}, pkt)
|
||||
case header.ICMPv4FragmentationNeeded:
|
||||
networkMTU, err := calculateNetworkMTU(uint32(h.MTU()), header.IPv4MinimumSize)
|
||||
networkMTU, err := calculateNetworkMTU(uint32(mtu), header.IPv4MinimumSize)
|
||||
if err != nil {
|
||||
networkMTU = 0
|
||||
}
|
||||
@@ -383,6 +387,8 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer) {
|
||||
// icmpReason is a marker interface for IPv4 specific ICMP errors.
|
||||
type icmpReason interface {
|
||||
isICMPReason()
|
||||
// isForwarding indicates whether or not the error arose while attempting to
|
||||
// forward a packet.
|
||||
isForwarding() bool
|
||||
}
|
||||
|
||||
@@ -442,6 +448,55 @@ func (r *icmpReasonParamProblem) isForwarding() bool {
|
||||
return r.forwarding
|
||||
}
|
||||
|
||||
// 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() {}
|
||||
func (*icmpReasonNetworkUnreachable) isForwarding() bool {
|
||||
// If we hit a Net Unreachable error, then we know we are operating as
|
||||
// a router. As per RFC 792 page 5, Destination Unreachable Message,
|
||||
//
|
||||
// If, according to the information in the gateway's routing tables,
|
||||
// the network specified in the internet destination field of a
|
||||
// datagram is unreachable, e.g., the distance to the network is
|
||||
// infinity, the gateway may send a destination unreachable message to
|
||||
// the internet source host of the datagram.
|
||||
return true
|
||||
}
|
||||
|
||||
// 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() {}
|
||||
func (*icmpReasonFragmentationNeeded) isForwarding() bool {
|
||||
// If we hit a Don't Fragment error, then we know we are operating as a router.
|
||||
// As per RFC 792 page 4, Destination Unreachable Message,
|
||||
//
|
||||
// Another case is when a datagram must be fragmented to be forwarded by a
|
||||
// gateway yet the Don't Fragment flag is on. In this case the gateway must
|
||||
// discard the datagram and may return a destination unreachable message.
|
||||
return true
|
||||
}
|
||||
|
||||
// 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() {}
|
||||
func (*icmpReasonHostUnreachable) isForwarding() bool {
|
||||
// If we hit a Host Unreachable error, then we know we are operating as a
|
||||
// router. As per RFC 792 page 5, Destination Unreachable Message,
|
||||
//
|
||||
// In addition, in some networks, the gateway may be able to determine
|
||||
// if the internet destination host is unreachable. Gateways in these
|
||||
// networks may send destination unreachable messages to the source host
|
||||
// when the destination host is unreachable.
|
||||
return true
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -498,42 +553,30 @@ func (p *protocol) returnError(reason icmpReason, pkt *stack.PacketBuffer) tcpip
|
||||
defer route.Release()
|
||||
|
||||
p.mu.Lock()
|
||||
netEP, ok := p.mu.eps[pkt.NICID]
|
||||
// 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.mu.eps[route.NICID()]
|
||||
p.mu.Unlock()
|
||||
if !ok {
|
||||
return &tcpip.ErrNotConnected{}
|
||||
}
|
||||
|
||||
sent := netEP.stats.icmp.packetsSent
|
||||
|
||||
if !p.stack.AllowICMPMessage() {
|
||||
sent.rateLimited.Increment()
|
||||
return nil
|
||||
}
|
||||
|
||||
transportHeader := pkt.TransportHeader().View()
|
||||
|
||||
// Don't respond to icmp error packets.
|
||||
if origIPHdr.Protocol() == uint8(header.ICMPv4ProtocolNumber) {
|
||||
// TODO(gvisor.dev/issue/3810):
|
||||
// Unfortunately the current stack pretty much always has ICMPv4 headers
|
||||
// in the Data section of the packet but there is no guarantee that is the
|
||||
// case. If this is the case grab the header to make it like all other
|
||||
// packet types. When this is cleaned up the Consume should be removed.
|
||||
if transportHeader.IsEmpty() {
|
||||
var ok bool
|
||||
transportHeader, ok = pkt.TransportHeader().Consume(header.ICMPv4MinimumSize)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
} else if transportHeader.Size() < header.ICMPv4MinimumSize {
|
||||
return nil
|
||||
}
|
||||
// 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,
|
||||
@@ -548,6 +591,35 @@ func (p *protocol) returnError(reason icmpReason, pkt *stack.PacketBuffer) tcpip
|
||||
}
|
||||
}
|
||||
|
||||
sent := netEP.stats.icmp.packetsSent
|
||||
icmpType, icmpCode, counter, pointer := func() (header.ICMPv4Type, header.ICMPv4Code, tcpip.MultiCounterStat, byte) {
|
||||
switch reason := reason.(type) {
|
||||
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
|
||||
//
|
||||
@@ -596,40 +668,17 @@ func (p *protocol) returnError(reason icmpReason, pkt *stack.PacketBuffer) tcpip
|
||||
ReserveHeaderBytes: int(route.MaxHeaderLength()) + header.ICMPv4MinimumSize,
|
||||
Data: payload,
|
||||
})
|
||||
defer icmpPkt.DecRef()
|
||||
|
||||
icmpPkt.TransportProtocolNumber = header.ICMPv4ProtocolNumber
|
||||
|
||||
icmpHdr := header.ICMPv4(icmpPkt.TransportHeader().Push(header.ICMPv4MinimumSize))
|
||||
var counter tcpip.MultiCounterStat
|
||||
switch reason := reason.(type) {
|
||||
case *icmpReasonPortUnreachable:
|
||||
icmpHdr.SetType(header.ICMPv4DstUnreachable)
|
||||
icmpHdr.SetCode(header.ICMPv4PortUnreachable)
|
||||
counter = sent.dstUnreachable
|
||||
case *icmpReasonProtoUnreachable:
|
||||
icmpHdr.SetType(header.ICMPv4DstUnreachable)
|
||||
icmpHdr.SetCode(header.ICMPv4ProtoUnreachable)
|
||||
counter = sent.dstUnreachable
|
||||
case *icmpReasonTTLExceeded:
|
||||
icmpHdr.SetType(header.ICMPv4TimeExceeded)
|
||||
icmpHdr.SetCode(header.ICMPv4TTLExceeded)
|
||||
counter = sent.timeExceeded
|
||||
case *icmpReasonReassemblyTimeout:
|
||||
icmpHdr.SetType(header.ICMPv4TimeExceeded)
|
||||
icmpHdr.SetCode(header.ICMPv4ReassemblyTimeout)
|
||||
counter = sent.timeExceeded
|
||||
case *icmpReasonParamProblem:
|
||||
icmpHdr.SetType(header.ICMPv4ParamProblem)
|
||||
icmpHdr.SetCode(header.ICMPv4UnusedCode)
|
||||
icmpHdr.SetPointer(reason.pointer)
|
||||
counter = sent.paramProblem
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported ICMP type %T", reason))
|
||||
}
|
||||
icmpHdr.SetCode(icmpCode)
|
||||
icmpHdr.SetType(icmpType)
|
||||
icmpHdr.SetPointer(pointer)
|
||||
icmpHdr.SetChecksum(header.ICMPv4Checksum(icmpHdr, icmpPkt.Data().AsRange().Checksum()))
|
||||
|
||||
if err := route.WritePacket(
|
||||
nil, /* gso */
|
||||
stack.NetworkHeaderParams{
|
||||
Protocol: header.ICMPv4ProtocolNumber,
|
||||
TTL: route.DefaultTTL(),
|
||||
|
||||
+14
-3
@@ -126,6 +126,17 @@ func (igmp *igmpState) SendLeave(groupAddress tcpip.Address) tcpip.Error {
|
||||
return err
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// init sets up an igmpState struct, and is required to be called before using
|
||||
// a new igmpState.
|
||||
//
|
||||
@@ -137,10 +148,9 @@ func (igmp *igmpState) init(ep *endpoint) {
|
||||
Clock: ep.protocol.stack.Clock(),
|
||||
Protocol: igmp,
|
||||
MaxUnsolicitedReportDelay: UnsolicitedReportIntervalMax,
|
||||
AllNodesAddress: header.IPv4AllSystems,
|
||||
})
|
||||
igmp.igmpV1Present = igmpV1PresentDefault
|
||||
igmp.igmpV1Job = ep.protocol.stack.NewJob(&ep.mu, func() {
|
||||
igmp.igmpV1Job = tcpip.NewJob(ep.protocol.stack.Clock(), &ep.mu, func() {
|
||||
igmp.setV1Present(false)
|
||||
})
|
||||
}
|
||||
@@ -312,6 +322,7 @@ func (igmp *igmpState) writePacket(destAddress tcpip.Address, groupAddress tcpip
|
||||
ReserveHeaderBytes: int(igmp.ep.MaxHeaderLength()),
|
||||
Data: buffer.View(igmpData).ToVectorisedView(),
|
||||
})
|
||||
defer pkt.DecRef()
|
||||
|
||||
addressEndpoint := igmp.ep.acquireOutgoingPrimaryAddressRLocked(destAddress, false /* allowExpired */)
|
||||
if addressEndpoint == nil {
|
||||
@@ -331,7 +342,7 @@ func (igmp *igmpState) writePacket(destAddress tcpip.Address, groupAddress tcpip
|
||||
}
|
||||
|
||||
sentStats := igmp.ep.stats.igmp.packetsSent
|
||||
if err := igmp.ep.nic.WritePacketToRemote(header.EthernetAddressFromMulticastIPv4Address(destAddress), nil /* gso */, ProtocolNumber, pkt); err != nil {
|
||||
if err := igmp.ep.nic.WritePacketToRemote(header.EthernetAddressFromMulticastIPv4Address(destAddress), ProtocolNumber, pkt); err != nil {
|
||||
sentStats.dropped.Increment()
|
||||
return false, err
|
||||
}
|
||||
|
||||
+391
-176
File diff suppressed because it is too large.
Load diff
@@ -16,12 +16,14 @@ func (i *icmpv4DestinationUnreachableSockError) StateFields() []string {
|
||||
|
||||
func (i *icmpv4DestinationUnreachableSockError) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (i *icmpv4DestinationUnreachableSockError) StateSave(stateSinkObject state.Sink) {
|
||||
i.beforeSave()
|
||||
}
|
||||
|
||||
func (i *icmpv4DestinationUnreachableSockError) afterLoad() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (i *icmpv4DestinationUnreachableSockError) StateLoad(stateSourceObject state.Source) {
|
||||
}
|
||||
|
||||
@@ -37,6 +39,7 @@ func (i *icmpv4DestinationHostUnreachableSockError) StateFields() []string {
|
||||
|
||||
func (i *icmpv4DestinationHostUnreachableSockError) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (i *icmpv4DestinationHostUnreachableSockError) StateSave(stateSinkObject state.Sink) {
|
||||
i.beforeSave()
|
||||
stateSinkObject.Save(0, &i.icmpv4DestinationUnreachableSockError)
|
||||
@@ -44,6 +47,7 @@ func (i *icmpv4DestinationHostUnreachableSockError) StateSave(stateSinkObject st
|
||||
|
||||
func (i *icmpv4DestinationHostUnreachableSockError) afterLoad() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (i *icmpv4DestinationHostUnreachableSockError) StateLoad(stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &i.icmpv4DestinationUnreachableSockError)
|
||||
}
|
||||
@@ -60,6 +64,7 @@ func (i *icmpv4DestinationPortUnreachableSockError) StateFields() []string {
|
||||
|
||||
func (i *icmpv4DestinationPortUnreachableSockError) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (i *icmpv4DestinationPortUnreachableSockError) StateSave(stateSinkObject state.Sink) {
|
||||
i.beforeSave()
|
||||
stateSinkObject.Save(0, &i.icmpv4DestinationUnreachableSockError)
|
||||
@@ -67,6 +72,7 @@ func (i *icmpv4DestinationPortUnreachableSockError) StateSave(stateSinkObject st
|
||||
|
||||
func (i *icmpv4DestinationPortUnreachableSockError) afterLoad() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (i *icmpv4DestinationPortUnreachableSockError) StateLoad(stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &i.icmpv4DestinationUnreachableSockError)
|
||||
}
|
||||
@@ -84,6 +90,7 @@ func (e *icmpv4FragmentationNeededSockError) StateFields() []string {
|
||||
|
||||
func (e *icmpv4FragmentationNeededSockError) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *icmpv4FragmentationNeededSockError) StateSave(stateSinkObject state.Sink) {
|
||||
e.beforeSave()
|
||||
stateSinkObject.Save(0, &e.icmpv4DestinationUnreachableSockError)
|
||||
@@ -92,6 +99,7 @@ func (e *icmpv4FragmentationNeededSockError) StateSave(stateSinkObject state.Sin
|
||||
|
||||
func (e *icmpv4FragmentationNeededSockError) afterLoad() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *icmpv4FragmentationNeededSockError) StateLoad(stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &e.icmpv4DestinationUnreachableSockError)
|
||||
stateSourceObject.Load(1, &e.mtu)
|
||||
|
||||
+205
-141
@@ -181,10 +181,15 @@ func (e *endpoint) handleControl(transErr stack.TransportError, pkt *stack.Packe
|
||||
return
|
||||
}
|
||||
|
||||
// Keep needed information before trimming header.
|
||||
p := hdr.TransportProtocol()
|
||||
dstAddr := hdr.DestinationAddress()
|
||||
|
||||
// Skip the IP header, then handle the fragmentation header if there
|
||||
// is one.
|
||||
pkt.Data().TrimFront(header.IPv6MinimumSize)
|
||||
p := hdr.TransportProtocol()
|
||||
if _, ok := pkt.Data().Consume(header.IPv6MinimumSize); !ok {
|
||||
panic("could not consume IPv6MinimumSize bytes")
|
||||
}
|
||||
if p == header.IPv6FragmentHeader {
|
||||
f, ok := pkt.Data().PullUp(header.IPv6FragmentHeaderSize)
|
||||
if !ok {
|
||||
@@ -196,14 +201,16 @@ func (e *endpoint) handleControl(transErr stack.TransportError, pkt *stack.Packe
|
||||
// because they don't have the transport headers.
|
||||
return
|
||||
}
|
||||
p = fragHdr.TransportProtocol()
|
||||
|
||||
// Skip fragmentation header and find out the actual protocol
|
||||
// number.
|
||||
pkt.Data().TrimFront(header.IPv6FragmentHeaderSize)
|
||||
p = fragHdr.TransportProtocol()
|
||||
if _, ok := pkt.Data().Consume(header.IPv6FragmentHeaderSize); !ok {
|
||||
panic("could not consume IPv6FragmentHeaderSize bytes")
|
||||
}
|
||||
}
|
||||
|
||||
e.dispatcher.DeliverTransportError(srcAddr, hdr.DestinationAddress(), ProtocolNumber, p, transErr, pkt)
|
||||
e.dispatcher.DeliverTransportError(srcAddr, dstAddr, ProtocolNumber, p, transErr, pkt)
|
||||
}
|
||||
|
||||
// getLinkAddrOption searches NDP options for a given link address option using
|
||||
@@ -267,13 +274,13 @@ func isMLDValid(pkt *stack.PacketBuffer, iph header.IPv6, routerAlert *header.IP
|
||||
if routerAlert == nil || routerAlert.Value != header.IPv6RouterAlertMLD {
|
||||
return false
|
||||
}
|
||||
if pkt.Data().Size() < header.ICMPv6HeaderSize+header.MLDMinimumSize {
|
||||
if pkt.TransportHeader().View().Size() < header.ICMPv6HeaderSize+header.MLDMinimumSize {
|
||||
return false
|
||||
}
|
||||
if iph.HopLimit() != header.MLDHopLimit {
|
||||
return false
|
||||
}
|
||||
if !header.IsV6LinkLocalAddress(iph.SourceAddress()) {
|
||||
if !header.IsV6LinkLocalUnicastAddress(iph.SourceAddress()) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
@@ -282,20 +289,17 @@ func isMLDValid(pkt *stack.PacketBuffer, iph header.IPv6, routerAlert *header.IP
|
||||
func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool, routerAlert *header.IPv6RouterAlertOption) {
|
||||
sent := e.stats.icmp.packetsSent
|
||||
received := e.stats.icmp.packetsReceived
|
||||
// TODO(gvisor.dev/issue/170): ICMP packets don't have their TransportHeader
|
||||
// fields set. See icmp/protocol.go:protocol.Parse for a full explanation.
|
||||
v, ok := pkt.Data().PullUp(header.ICMPv6HeaderSize)
|
||||
if !ok {
|
||||
h := header.ICMPv6(pkt.TransportHeader().View())
|
||||
if len(h) < header.ICMPv6MinimumSize {
|
||||
received.invalid.Increment()
|
||||
return
|
||||
}
|
||||
h := header.ICMPv6(v)
|
||||
iph := header.IPv6(pkt.NetworkHeader().View())
|
||||
srcAddr := iph.SourceAddress()
|
||||
dstAddr := iph.DestinationAddress()
|
||||
|
||||
// Validate ICMPv6 checksum before processing the packet.
|
||||
payload := pkt.Data().AsRange().SubRange(len(h))
|
||||
payload := pkt.Data().AsRange()
|
||||
if got, want := h.Checksum(), header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
|
||||
Header: h,
|
||||
Src: srcAddr,
|
||||
@@ -322,13 +326,7 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool, r
|
||||
switch icmpType := h.Type(); icmpType {
|
||||
case header.ICMPv6PacketTooBig:
|
||||
received.packetTooBig.Increment()
|
||||
hdr, ok := pkt.Data().PullUp(header.ICMPv6PacketTooBigMinimumSize)
|
||||
if !ok {
|
||||
received.invalid.Increment()
|
||||
return
|
||||
}
|
||||
pkt.Data().TrimFront(header.ICMPv6PacketTooBigMinimumSize)
|
||||
networkMTU, err := calculateNetworkMTU(header.ICMPv6(hdr).MTU(), header.IPv6MinimumSize)
|
||||
networkMTU, err := calculateNetworkMTU(h.MTU(), header.IPv6MinimumSize)
|
||||
if err != nil {
|
||||
networkMTU = 0
|
||||
}
|
||||
@@ -336,13 +334,7 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool, r
|
||||
|
||||
case header.ICMPv6DstUnreachable:
|
||||
received.dstUnreachable.Increment()
|
||||
hdr, ok := pkt.Data().PullUp(header.ICMPv6DstUnreachableMinimumSize)
|
||||
if !ok {
|
||||
received.invalid.Increment()
|
||||
return
|
||||
}
|
||||
pkt.Data().TrimFront(header.ICMPv6DstUnreachableMinimumSize)
|
||||
switch header.ICMPv6(hdr).Code() {
|
||||
switch h.Code() {
|
||||
case header.ICMPv6NetworkUnreachable:
|
||||
e.handleControl(&icmpv6DestinationNetworkUnreachableSockError{}, pkt)
|
||||
case header.ICMPv6PortUnreachable:
|
||||
@@ -350,16 +342,12 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool, r
|
||||
}
|
||||
case header.ICMPv6NeighborSolicit:
|
||||
received.neighborSolicit.Increment()
|
||||
if !isNDPValid() || pkt.Data().Size() < header.ICMPv6NeighborSolicitMinimumSize {
|
||||
if !isNDPValid() || len(h) < header.ICMPv6NeighborSolicitMinimumSize {
|
||||
received.invalid.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
// The remainder of payload must be only the neighbor solicitation, so
|
||||
// payload.AsView() always returns the solicitation. Per RFC 6980 section 5,
|
||||
// NDP messages cannot be fragmented. Also note that in the common case NDP
|
||||
// datagrams are very small and AsView() will not incur allocations.
|
||||
ns := header.NDPNeighborSolicit(payload.AsView())
|
||||
ns := header.NDPNeighborSolicit(h.MessageBody())
|
||||
targetAddr := ns.TargetAddress()
|
||||
|
||||
// As per RFC 4861 section 4.3, the Target Address MUST NOT be a multicast
|
||||
@@ -537,6 +525,7 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool, r
|
||||
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
ReserveHeaderBytes: int(r.MaxHeaderLength()) + neighborAdvertSize,
|
||||
})
|
||||
defer pkt.DecRef()
|
||||
pkt.TransportProtocolNumber = header.ICMPv6ProtocolNumber
|
||||
packet := header.ICMPv6(pkt.TransportHeader().Push(neighborAdvertSize))
|
||||
packet.SetType(header.ICMPv6NeighborAdvert)
|
||||
@@ -554,8 +543,8 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool, r
|
||||
na.Options().Serialize(optsSerializer)
|
||||
packet.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
|
||||
Header: packet,
|
||||
Src: r.LocalAddress,
|
||||
Dst: r.RemoteAddress,
|
||||
Src: r.LocalAddress(),
|
||||
Dst: r.RemoteAddress(),
|
||||
}))
|
||||
|
||||
// RFC 4861 Neighbor Discovery for IP version 6 (IPv6)
|
||||
@@ -564,7 +553,7 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool, r
|
||||
//
|
||||
// The IP Hop Limit field has a value of 255, i.e., the packet
|
||||
// could not possibly have been forwarded by a router.
|
||||
if err := r.WritePacket(nil /* gso */, stack.NetworkHeaderParams{Protocol: header.ICMPv6ProtocolNumber, TTL: header.NDPHopLimit, TOS: stack.DefaultTOS}, pkt); err != nil {
|
||||
if err := r.WritePacket(stack.NetworkHeaderParams{Protocol: header.ICMPv6ProtocolNumber, TTL: header.NDPHopLimit, TOS: stack.DefaultTOS}, pkt); err != nil {
|
||||
sent.dropped.Increment()
|
||||
return
|
||||
}
|
||||
@@ -572,16 +561,12 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool, r
|
||||
|
||||
case header.ICMPv6NeighborAdvert:
|
||||
received.neighborAdvert.Increment()
|
||||
if !isNDPValid() || pkt.Data().Size() < header.ICMPv6NeighborAdvertMinimumSize {
|
||||
if !isNDPValid() || len(h) < header.ICMPv6NeighborAdvertMinimumSize {
|
||||
received.invalid.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
// The remainder of payload must be only the neighbor advertisement, so
|
||||
// payload.AsView() always returns the advertisement. Per RFC 6980 section
|
||||
// 5, NDP messages cannot be fragmented. Also note that in the common case
|
||||
// NDP datagrams are very small and AsView() will not incur allocations.
|
||||
na := header.NDPNeighborAdvert(payload.AsView())
|
||||
na := header.NDPNeighborAdvert(h.MessageBody())
|
||||
|
||||
it, err := na.Options().Iter(false /* check */)
|
||||
if err != nil {
|
||||
@@ -668,12 +653,6 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool, r
|
||||
|
||||
case header.ICMPv6EchoRequest:
|
||||
received.echoRequest.Increment()
|
||||
icmpHdr, ok := pkt.TransportHeader().Consume(header.ICMPv6EchoMinimumSize)
|
||||
if !ok {
|
||||
received.invalid.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
// As per RFC 4291 section 2.7, multicast addresses must not be used as
|
||||
// source addresses in IPv6 packets.
|
||||
localAddr := dstAddr
|
||||
@@ -688,23 +667,29 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool, r
|
||||
}
|
||||
defer r.Release()
|
||||
|
||||
if !e.protocol.allowICMPReply(header.ICMPv6EchoReply) {
|
||||
sent.rateLimited.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
replyPkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
ReserveHeaderBytes: int(r.MaxHeaderLength()) + header.ICMPv6EchoMinimumSize,
|
||||
Data: pkt.Data().ExtractVV(),
|
||||
})
|
||||
defer replyPkt.DecRef()
|
||||
icmp := header.ICMPv6(replyPkt.TransportHeader().Push(header.ICMPv6EchoMinimumSize))
|
||||
pkt.TransportProtocolNumber = header.ICMPv6ProtocolNumber
|
||||
copy(icmp, icmpHdr)
|
||||
copy(icmp, h)
|
||||
icmp.SetType(header.ICMPv6EchoReply)
|
||||
dataRange := replyPkt.Data().AsRange()
|
||||
icmp.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
|
||||
Header: icmp,
|
||||
Src: r.LocalAddress,
|
||||
Dst: r.RemoteAddress,
|
||||
Src: r.LocalAddress(),
|
||||
Dst: r.RemoteAddress(),
|
||||
PayloadCsum: dataRange.Checksum(),
|
||||
PayloadLen: dataRange.Size(),
|
||||
}))
|
||||
if err := r.WritePacket(nil /* gso */, stack.NetworkHeaderParams{
|
||||
if err := r.WritePacket(stack.NetworkHeaderParams{
|
||||
Protocol: header.ICMPv6ProtocolNumber,
|
||||
TTL: r.DefaultTTL(),
|
||||
TOS: stack.DefaultTOS,
|
||||
@@ -716,7 +701,7 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool, r
|
||||
|
||||
case header.ICMPv6EchoReply:
|
||||
received.echoReply.Increment()
|
||||
if pkt.Data().Size() < header.ICMPv6EchoMinimumSize {
|
||||
if len(h) < header.ICMPv6EchoMinimumSize {
|
||||
received.invalid.Increment()
|
||||
return
|
||||
}
|
||||
@@ -736,23 +721,17 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool, r
|
||||
//
|
||||
|
||||
// Is the NDP payload of sufficient size to hold a Router Solictation?
|
||||
if !isNDPValid() || pkt.Data().Size()-header.ICMPv6HeaderSize < header.NDPRSMinimumSize {
|
||||
if !isNDPValid() || len(h)-header.ICMPv6HeaderSize < header.NDPRSMinimumSize {
|
||||
received.invalid.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
stack := e.protocol.stack
|
||||
|
||||
// Is the networking stack operating as a router?
|
||||
if !stack.Forwarding(ProtocolNumber) {
|
||||
// ... No, silently drop the packet.
|
||||
if !e.Forwarding() {
|
||||
received.routerOnlyPacketsDroppedByHost.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
// Note that in the common case NDP datagrams are very small and AsView()
|
||||
// will not incur allocations.
|
||||
rs := header.NDPRouterSolicit(payload.AsView())
|
||||
rs := header.NDPRouterSolicit(h.MessageBody())
|
||||
it, err := rs.Options().Iter(false /* check */)
|
||||
if err != nil {
|
||||
// Options are not valid as per the wire format, silently drop the packet.
|
||||
@@ -796,7 +775,7 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool, r
|
||||
//
|
||||
|
||||
// Is the NDP payload of sufficient size to hold a Router Advertisement?
|
||||
if !isNDPValid() || pkt.Data().Size()-header.ICMPv6HeaderSize < header.NDPRAMinimumSize {
|
||||
if !isNDPValid() || len(h)-header.ICMPv6HeaderSize < header.NDPRAMinimumSize {
|
||||
received.invalid.Increment()
|
||||
return
|
||||
}
|
||||
@@ -804,15 +783,13 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool, r
|
||||
routerAddr := srcAddr
|
||||
|
||||
// Is the IP Source Address a link-local address?
|
||||
if !header.IsV6LinkLocalAddress(routerAddr) {
|
||||
if !header.IsV6LinkLocalUnicastAddress(routerAddr) {
|
||||
// ...No, silently drop the packet.
|
||||
received.invalid.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
// Note that in the common case NDP datagrams are very small and AsView()
|
||||
// will not incur allocations.
|
||||
ra := header.NDPRouterAdvert(payload.AsView())
|
||||
ra := header.NDPRouterAdvert(h.MessageBody())
|
||||
it, err := ra.Options().Iter(false /* check */)
|
||||
if err != nil {
|
||||
// Options are not valid as per the wire format, silently drop the packet.
|
||||
@@ -890,11 +867,11 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool, r
|
||||
switch icmpType {
|
||||
case header.ICMPv6MulticastListenerQuery:
|
||||
e.mu.Lock()
|
||||
e.mu.mld.handleMulticastListenerQuery(header.MLD(payload.AsView()))
|
||||
e.mu.mld.handleMulticastListenerQuery(header.MLD(h.MessageBody()))
|
||||
e.mu.Unlock()
|
||||
case header.ICMPv6MulticastListenerReport:
|
||||
e.mu.Lock()
|
||||
e.mu.mld.handleMulticastListenerReport(header.MLD(payload.AsView()))
|
||||
e.mu.mld.handleMulticastListenerReport(header.MLD(h.MessageBody()))
|
||||
e.mu.Unlock()
|
||||
case header.ICMPv6MulticastListenerDone:
|
||||
default:
|
||||
@@ -951,6 +928,19 @@ func (*endpoint) ResolveStaticAddress(addr tcpip.Address) (tcpip.LinkAddress, bo
|
||||
// icmpReason is a marker interface for IPv6 specific ICMP errors.
|
||||
type icmpReason interface {
|
||||
isICMPReason()
|
||||
// isForwarding indicates whether or not the error arose while attempting to
|
||||
// forward a packet.
|
||||
isForwarding() bool
|
||||
// respondToMulticast indicates whether this error falls under the exception
|
||||
// outlined by RFC 4443 section 2.4 point e.3 exception 2:
|
||||
//
|
||||
// (e.3) A packet destined to an IPv6 multicast address. (There are two
|
||||
// exceptions to this rule: (1) the Packet Too Big Message (Section 3.2) to
|
||||
// allow Path MTU discovery to work for IPv6 multicast, and (2) the Parameter
|
||||
// Problem Message, Code 2 (Section 3.4) reporting an unrecognized IPv6
|
||||
// option (see Section 4.2 of [IPv6]) that has the Option Type highest-
|
||||
// order two bits set to 10).
|
||||
respondsToMulticast() bool
|
||||
}
|
||||
|
||||
// icmpReasonParameterProblem is an error during processing of extension headers
|
||||
@@ -958,18 +948,6 @@ type icmpReason interface {
|
||||
type icmpReasonParameterProblem struct {
|
||||
code header.ICMPv6Code
|
||||
|
||||
// respondToMulticast indicates that we are sending a packet that falls under
|
||||
// the exception outlined by RFC 4443 section 2.4 point e.3 exception 2:
|
||||
//
|
||||
// (e.3) A packet destined to an IPv6 multicast address. (There are
|
||||
// two exceptions to this rule: (1) the Packet Too Big Message
|
||||
// (Section 3.2) to allow Path MTU discovery to work for IPv6
|
||||
// multicast, and (2) the Parameter Problem Message, Code 2
|
||||
// (Section 3.4) reporting an unrecognized IPv6 option (see
|
||||
// Section 4.2 of [IPv6]) that has the Option Type highest-
|
||||
// order two bits set to 10).
|
||||
respondToMulticast bool
|
||||
|
||||
// pointer is defined in the RFC 4443 setion 3.4 which reads:
|
||||
//
|
||||
// Pointer Identifies the octet offset within the invoking packet
|
||||
@@ -979,9 +957,20 @@ type icmpReasonParameterProblem struct {
|
||||
// packet if the field in error is beyond what can fit
|
||||
// in the maximum size of an ICMPv6 error message.
|
||||
pointer uint32
|
||||
|
||||
forwarding bool
|
||||
|
||||
respondToMulticast bool
|
||||
}
|
||||
|
||||
func (*icmpReasonParameterProblem) isICMPReason() {}
|
||||
func (p *icmpReasonParameterProblem) isForwarding() bool {
|
||||
return p.forwarding
|
||||
}
|
||||
|
||||
func (p *icmpReasonParameterProblem) respondsToMulticast() bool {
|
||||
return p.respondToMulticast
|
||||
}
|
||||
|
||||
// icmpReasonPortUnreachable is an error where the transport protocol has no
|
||||
// listener and no alternative means to inform the sender.
|
||||
@@ -989,12 +978,96 @@ type icmpReasonPortUnreachable struct{}
|
||||
|
||||
func (*icmpReasonPortUnreachable) isICMPReason() {}
|
||||
|
||||
func (*icmpReasonPortUnreachable) isForwarding() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (*icmpReasonPortUnreachable) respondsToMulticast() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// icmpReasonNetUnreachable is an error where no route can be found to the
|
||||
// network of the final destination.
|
||||
type icmpReasonNetUnreachable struct{}
|
||||
|
||||
func (*icmpReasonNetUnreachable) isICMPReason() {}
|
||||
|
||||
func (*icmpReasonNetUnreachable) isForwarding() bool {
|
||||
// If we hit a Network Unreachable error, then we also know we are
|
||||
// operating as a router. As per RFC 4443 section 3.1:
|
||||
//
|
||||
// If the reason for the failure to deliver is lack of a matching
|
||||
// entry in the forwarding node's routing table, the Code field is
|
||||
// set to 0 (Network Unreachable).
|
||||
return true
|
||||
}
|
||||
|
||||
func (*icmpReasonNetUnreachable) respondsToMulticast() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// 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() {}
|
||||
func (*icmpReasonHostUnreachable) isForwarding() bool {
|
||||
// If we hit a Host Unreachable error, then we know we are operating as a
|
||||
// router. As per RFC 4443 page 8, Destination Unreachable Message,
|
||||
//
|
||||
// If the reason for the failure to deliver cannot be mapped to any of
|
||||
// other codes, the Code field is set to 3. Example of such cases are
|
||||
// an inability to resolve the IPv6 destination address into a
|
||||
// corresponding link address, or a link-specific problem of some sort.
|
||||
return true
|
||||
}
|
||||
|
||||
func (*icmpReasonHostUnreachable) respondsToMulticast() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// icmpReasonFragmentationNeeded is an error where a packet is to big to be sent
|
||||
// out through the outgoing MTU, as per RFC 4443 page 9, Packet Too Big Message.
|
||||
type icmpReasonPacketTooBig struct{}
|
||||
|
||||
func (*icmpReasonPacketTooBig) isICMPReason() {}
|
||||
|
||||
func (*icmpReasonPacketTooBig) isForwarding() bool {
|
||||
// If we hit a Packet Too Big error, then we know we are operating as a router.
|
||||
// As per RFC 4443 section 3.2:
|
||||
//
|
||||
// A Packet Too Big MUST be sent by a router in response to a packet that it
|
||||
// cannot forward because the packet is larger than the MTU of the outgoing
|
||||
// link.
|
||||
return true
|
||||
}
|
||||
|
||||
func (*icmpReasonPacketTooBig) respondsToMulticast() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// icmpReasonHopLimitExceeded is an error where a packet's hop limit exceeded in
|
||||
// transit to its final destination, as per RFC 4443 section 3.3.
|
||||
type icmpReasonHopLimitExceeded struct{}
|
||||
|
||||
func (*icmpReasonHopLimitExceeded) isICMPReason() {}
|
||||
|
||||
func (*icmpReasonHopLimitExceeded) isForwarding() bool {
|
||||
// If we hit a Hop Limit Exceeded error, then we know we are operating
|
||||
// as a router. As per RFC 4443 section 3.3:
|
||||
//
|
||||
// If a router receives a packet with a Hop Limit of zero, or if a
|
||||
// router decrements a packet's Hop Limit to zero, it MUST discard
|
||||
// the packet and originate an ICMPv6 Time Exceeded message with Code
|
||||
// 0 to the source of the packet. This indicates either a routing
|
||||
// loop or too small an initial Hop Limit value.
|
||||
return true
|
||||
}
|
||||
|
||||
func (*icmpReasonHopLimitExceeded) respondsToMulticast() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// 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.
|
||||
@@ -1002,6 +1075,14 @@ type icmpReasonReassemblyTimeout struct{}
|
||||
|
||||
func (*icmpReasonReassemblyTimeout) isICMPReason() {}
|
||||
|
||||
func (*icmpReasonReassemblyTimeout) isForwarding() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (*icmpReasonReassemblyTimeout) respondsToMulticast() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// returnError takes an error descriptor and generates the appropriate ICMP
|
||||
// error packet for IPv6 and sends it.
|
||||
func (p *protocol) returnError(reason icmpReason, pkt *stack.PacketBuffer) tcpip.Error {
|
||||
@@ -1030,25 +1111,12 @@ func (p *protocol) returnError(reason icmpReason, pkt *stack.PacketBuffer) tcpip
|
||||
// Section 4.2 of [IPv6]) that has the Option Type highest-
|
||||
// order two bits set to 10).
|
||||
//
|
||||
var allowResponseToMulticast bool
|
||||
if reason, ok := reason.(*icmpReasonParameterProblem); ok {
|
||||
allowResponseToMulticast = reason.respondToMulticast
|
||||
}
|
||||
|
||||
allowResponseToMulticast := reason.respondsToMulticast()
|
||||
isOrigDstMulticast := header.IsV6MulticastAddress(origIPHdrDst)
|
||||
if (!allowResponseToMulticast && isOrigDstMulticast) || origIPHdrSrc == header.IPv6Any {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If we hit a Hop Limit Exceeded error, then we know we are operating as a
|
||||
// router. As per RFC 4443 section 3.3:
|
||||
//
|
||||
// If a router receives a packet with a Hop Limit of zero, or if a
|
||||
// router decrements a packet's Hop Limit to zero, it MUST discard the
|
||||
// packet and originate an ICMPv6 Time Exceeded message with Code 0 to
|
||||
// the source of the packet. This indicates either a routing loop or
|
||||
// too small an initial Hop Limit value.
|
||||
//
|
||||
// If we are operating as a router, do not use the packet's destination
|
||||
// address as the response's source address as we should not own the
|
||||
// destination address of a packet we are forwarding.
|
||||
@@ -1058,7 +1126,7 @@ func (p *protocol) returnError(reason icmpReason, pkt *stack.PacketBuffer) tcpip
|
||||
// packet as "multicast addresses must not be used as source addresses in IPv6
|
||||
// packets", as per RFC 4291 section 2.7.
|
||||
localAddr := origIPHdrDst
|
||||
if _, ok := reason.(*icmpReasonHopLimitExceeded); ok || isOrigDstMulticast {
|
||||
if reason.isForwarding() || isOrigDstMulticast {
|
||||
localAddr = ""
|
||||
}
|
||||
// Even if we were able to receive a packet from some remote, we may not have
|
||||
@@ -1072,34 +1140,48 @@ func (p *protocol) returnError(reason icmpReason, pkt *stack.PacketBuffer) tcpip
|
||||
defer route.Release()
|
||||
|
||||
p.mu.Lock()
|
||||
netEP, ok := p.mu.eps[pkt.NICID]
|
||||
// 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.mu.eps[route.NICID()]
|
||||
p.mu.Unlock()
|
||||
if !ok {
|
||||
return &tcpip.ErrNotConnected{}
|
||||
}
|
||||
|
||||
sent := netEP.stats.icmp.packetsSent
|
||||
|
||||
if !p.stack.AllowICMPMessage() {
|
||||
sent.rateLimited.Increment()
|
||||
return nil
|
||||
if pkt.TransportProtocolNumber == header.ICMPv6ProtocolNumber {
|
||||
if typ := header.ICMPv6(pkt.TransportHeader().View()).Type(); typ.IsErrorType() || typ == header.ICMPv6RedirectMsg {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if pkt.TransportProtocolNumber == header.ICMPv6ProtocolNumber {
|
||||
// TODO(gvisor.dev/issues/3810): Sort this out when ICMP headers are stored.
|
||||
// Unfortunately at this time ICMP Packets do not have a transport
|
||||
// header separated out. It is in the Data part so we need to
|
||||
// separate it out now. We will just pretend it is a minimal length
|
||||
// ICMP packet as we don't really care if any later bits of a
|
||||
// larger ICMP packet are in the header view or in the Data view.
|
||||
transport, ok := pkt.TransportHeader().Consume(header.ICMPv6MinimumSize)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
typ := header.ICMPv6(transport).Type()
|
||||
if typ.IsErrorType() || typ == header.ICMPv6RedirectMsg {
|
||||
return nil
|
||||
sent := netEP.stats.icmp.packetsSent
|
||||
icmpType, icmpCode, counter, typeSpecific := func() (header.ICMPv6Type, header.ICMPv6Code, tcpip.MultiCounterStat, uint32) {
|
||||
switch reason := reason.(type) {
|
||||
case *icmpReasonParameterProblem:
|
||||
return header.ICMPv6ParamProblem, reason.code, sent.paramProblem, reason.pointer
|
||||
case *icmpReasonPortUnreachable:
|
||||
return header.ICMPv6DstUnreachable, header.ICMPv6PortUnreachable, sent.dstUnreachable, 0
|
||||
case *icmpReasonNetUnreachable:
|
||||
return header.ICMPv6DstUnreachable, header.ICMPv6NetworkUnreachable, sent.dstUnreachable, 0
|
||||
case *icmpReasonHostUnreachable:
|
||||
return header.ICMPv6DstUnreachable, header.ICMPv6AddressUnreachable, sent.dstUnreachable, 0
|
||||
case *icmpReasonPacketTooBig:
|
||||
return header.ICMPv6PacketTooBig, header.ICMPv6UnusedCode, sent.packetTooBig, 0
|
||||
case *icmpReasonHopLimitExceeded:
|
||||
return header.ICMPv6TimeExceeded, header.ICMPv6HopLimitExceeded, sent.timeExceeded, 0
|
||||
case *icmpReasonReassemblyTimeout:
|
||||
return header.ICMPv6TimeExceeded, header.ICMPv6ReassemblyTimeout, sent.timeExceeded, 0
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported ICMP type %T", reason))
|
||||
}
|
||||
}()
|
||||
|
||||
if !p.allowICMPReply(icmpType) {
|
||||
sent.rateLimited.Increment()
|
||||
return nil
|
||||
}
|
||||
|
||||
network, transport := pkt.NetworkHeader().View(), pkt.TransportHeader().View()
|
||||
@@ -1133,41 +1215,23 @@ func (p *protocol) returnError(reason icmpReason, pkt *stack.PacketBuffer) tcpip
|
||||
ReserveHeaderBytes: int(route.MaxHeaderLength()) + header.ICMPv6ErrorHeaderSize,
|
||||
Data: payload,
|
||||
})
|
||||
defer newPkt.DecRef()
|
||||
newPkt.TransportProtocolNumber = header.ICMPv6ProtocolNumber
|
||||
|
||||
icmpHdr := header.ICMPv6(newPkt.TransportHeader().Push(header.ICMPv6DstUnreachableMinimumSize))
|
||||
var counter tcpip.MultiCounterStat
|
||||
switch reason := reason.(type) {
|
||||
case *icmpReasonParameterProblem:
|
||||
icmpHdr.SetType(header.ICMPv6ParamProblem)
|
||||
icmpHdr.SetCode(reason.code)
|
||||
icmpHdr.SetTypeSpecific(reason.pointer)
|
||||
counter = sent.paramProblem
|
||||
case *icmpReasonPortUnreachable:
|
||||
icmpHdr.SetType(header.ICMPv6DstUnreachable)
|
||||
icmpHdr.SetCode(header.ICMPv6PortUnreachable)
|
||||
counter = sent.dstUnreachable
|
||||
case *icmpReasonHopLimitExceeded:
|
||||
icmpHdr.SetType(header.ICMPv6TimeExceeded)
|
||||
icmpHdr.SetCode(header.ICMPv6HopLimitExceeded)
|
||||
counter = sent.timeExceeded
|
||||
case *icmpReasonReassemblyTimeout:
|
||||
icmpHdr.SetType(header.ICMPv6TimeExceeded)
|
||||
icmpHdr.SetCode(header.ICMPv6ReassemblyTimeout)
|
||||
counter = sent.timeExceeded
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported ICMP type %T", reason))
|
||||
}
|
||||
icmpHdr.SetType(icmpType)
|
||||
icmpHdr.SetCode(icmpCode)
|
||||
icmpHdr.SetTypeSpecific(typeSpecific)
|
||||
|
||||
dataRange := newPkt.Data().AsRange()
|
||||
icmpHdr.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
|
||||
Header: icmpHdr,
|
||||
Src: route.LocalAddress,
|
||||
Dst: route.RemoteAddress,
|
||||
Src: route.LocalAddress(),
|
||||
Dst: route.RemoteAddress(),
|
||||
PayloadCsum: dataRange.Checksum(),
|
||||
PayloadLen: dataRange.Size(),
|
||||
}))
|
||||
if err := route.WritePacket(
|
||||
nil, /* gso */
|
||||
stack.NetworkHeaderParams{
|
||||
Protocol: header.ICMPv6ProtocolNumber,
|
||||
TTL: route.DefaultTTL(),
|
||||
|
||||
+474
-221
File diff suppressed because it is too large.
Load diff
@@ -16,12 +16,14 @@ func (i *icmpv6DestinationUnreachableSockError) StateFields() []string {
|
||||
|
||||
func (i *icmpv6DestinationUnreachableSockError) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (i *icmpv6DestinationUnreachableSockError) StateSave(stateSinkObject state.Sink) {
|
||||
i.beforeSave()
|
||||
}
|
||||
|
||||
func (i *icmpv6DestinationUnreachableSockError) afterLoad() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (i *icmpv6DestinationUnreachableSockError) StateLoad(stateSourceObject state.Source) {
|
||||
}
|
||||
|
||||
@@ -37,6 +39,7 @@ func (i *icmpv6DestinationNetworkUnreachableSockError) StateFields() []string {
|
||||
|
||||
func (i *icmpv6DestinationNetworkUnreachableSockError) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (i *icmpv6DestinationNetworkUnreachableSockError) StateSave(stateSinkObject state.Sink) {
|
||||
i.beforeSave()
|
||||
stateSinkObject.Save(0, &i.icmpv6DestinationUnreachableSockError)
|
||||
@@ -44,6 +47,7 @@ func (i *icmpv6DestinationNetworkUnreachableSockError) StateSave(stateSinkObject
|
||||
|
||||
func (i *icmpv6DestinationNetworkUnreachableSockError) afterLoad() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (i *icmpv6DestinationNetworkUnreachableSockError) StateLoad(stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &i.icmpv6DestinationUnreachableSockError)
|
||||
}
|
||||
@@ -60,6 +64,7 @@ func (i *icmpv6DestinationPortUnreachableSockError) StateFields() []string {
|
||||
|
||||
func (i *icmpv6DestinationPortUnreachableSockError) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (i *icmpv6DestinationPortUnreachableSockError) StateSave(stateSinkObject state.Sink) {
|
||||
i.beforeSave()
|
||||
stateSinkObject.Save(0, &i.icmpv6DestinationUnreachableSockError)
|
||||
@@ -67,6 +72,7 @@ func (i *icmpv6DestinationPortUnreachableSockError) StateSave(stateSinkObject st
|
||||
|
||||
func (i *icmpv6DestinationPortUnreachableSockError) afterLoad() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (i *icmpv6DestinationPortUnreachableSockError) StateLoad(stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &i.icmpv6DestinationUnreachableSockError)
|
||||
}
|
||||
@@ -83,6 +89,7 @@ func (i *icmpv6DestinationAddressUnreachableSockError) StateFields() []string {
|
||||
|
||||
func (i *icmpv6DestinationAddressUnreachableSockError) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (i *icmpv6DestinationAddressUnreachableSockError) StateSave(stateSinkObject state.Sink) {
|
||||
i.beforeSave()
|
||||
stateSinkObject.Save(0, &i.icmpv6DestinationUnreachableSockError)
|
||||
@@ -90,6 +97,7 @@ func (i *icmpv6DestinationAddressUnreachableSockError) StateSave(stateSinkObject
|
||||
|
||||
func (i *icmpv6DestinationAddressUnreachableSockError) afterLoad() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (i *icmpv6DestinationAddressUnreachableSockError) StateLoad(stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &i.icmpv6DestinationUnreachableSockError)
|
||||
}
|
||||
@@ -106,6 +114,7 @@ func (e *icmpv6PacketTooBigSockError) StateFields() []string {
|
||||
|
||||
func (e *icmpv6PacketTooBigSockError) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *icmpv6PacketTooBigSockError) StateSave(stateSinkObject state.Sink) {
|
||||
e.beforeSave()
|
||||
stateSinkObject.Save(0, &e.mtu)
|
||||
@@ -113,6 +122,7 @@ func (e *icmpv6PacketTooBigSockError) StateSave(stateSinkObject state.Sink) {
|
||||
|
||||
func (e *icmpv6PacketTooBigSockError) afterLoad() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (e *icmpv6PacketTooBigSockError) StateLoad(stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &e.mtu)
|
||||
}
|
||||
|
||||
+22
-3
@@ -76,10 +76,29 @@ func (mld *mldState) SendReport(groupAddress tcpip.Address) (bool, tcpip.Error)
|
||||
//
|
||||
// Precondition: mld.ep.mu must be read locked.
|
||||
func (mld *mldState) SendLeave(groupAddress tcpip.Address) tcpip.Error {
|
||||
_, err := mld.writePacket(header.IPv6AllRoutersMulticastAddress, groupAddress, header.ICMPv6MulticastListenerDone)
|
||||
_, 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
|
||||
}
|
||||
|
||||
// init sets up an mldState struct, and is required to be called before using
|
||||
// a new mldState.
|
||||
//
|
||||
@@ -91,7 +110,6 @@ func (mld *mldState) init(ep *endpoint) {
|
||||
Clock: ep.protocol.stack.Clock(),
|
||||
Protocol: mld,
|
||||
MaxUnsolicitedReportDelay: UnsolicitedReportIntervalMax,
|
||||
AllNodesAddress: header.IPv6AllNodesMulticastAddress,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -252,6 +270,7 @@ func (mld *mldState) writePacket(destAddress, groupAddress tcpip.Address, mldTyp
|
||||
ReserveHeaderBytes: int(mld.ep.MaxHeaderLength()) + extensionHeaders.Length(),
|
||||
Data: buffer.View(icmp).ToVectorisedView(),
|
||||
})
|
||||
defer pkt.DecRef()
|
||||
|
||||
if err := addIPHeader(localAddress, destAddress, pkt, stack.NetworkHeaderParams{
|
||||
Protocol: header.ICMPv6ProtocolNumber,
|
||||
@@ -259,7 +278,7 @@ func (mld *mldState) writePacket(destAddress, groupAddress tcpip.Address, mldTyp
|
||||
}, extensionHeaders); err != nil {
|
||||
panic(fmt.Sprintf("failed to add IP header: %s", err))
|
||||
}
|
||||
if err := mld.ep.nic.WritePacketToRemote(header.EthernetAddressFromMulticastIPv6Address(destAddress), nil /* gso */, ProtocolNumber, pkt); err != nil {
|
||||
if err := mld.ep.nic.WritePacketToRemote(header.EthernetAddressFromMulticastIPv6Address(destAddress), ProtocolNumber, pkt); err != nil {
|
||||
sentStats.dropped.Increment()
|
||||
return false, err
|
||||
}
|
||||
|
||||
+282
-186
@@ -16,7 +16,6 @@ package ipv6
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"inet.af/netstack/sync"
|
||||
@@ -48,13 +47,18 @@ const (
|
||||
|
||||
// defaultHandleRAs is the default configuration for whether or not to
|
||||
// handle incoming Router Advertisements as a host.
|
||||
defaultHandleRAs = true
|
||||
defaultHandleRAs = HandlingRAsEnabledWhenForwardingDisabled
|
||||
|
||||
// defaultDiscoverDefaultRouters is the default configuration for
|
||||
// whether or not to discover default routers from incoming Router
|
||||
// Advertisements, as a host.
|
||||
defaultDiscoverDefaultRouters = true
|
||||
|
||||
// defaultDiscoverMoreSpecificRoutes is the default configuration for
|
||||
// whether or not to discover more-specific routes from incoming Router
|
||||
// Advertisements, as a host.
|
||||
defaultDiscoverMoreSpecificRoutes = true
|
||||
|
||||
// defaultDiscoverOnLinkPrefixes is the default configuration for
|
||||
// whether or not to discover on-link prefixes from incoming Router
|
||||
// Advertisements' Prefix Information option, as a host.
|
||||
@@ -79,13 +83,13 @@ const (
|
||||
// we cannot have a negative delay.
|
||||
minimumMaxRtrSolicitationDelay = 0
|
||||
|
||||
// MaxDiscoveredDefaultRouters is the maximum number of discovered
|
||||
// default routers. The stack should stop discovering new routers after
|
||||
// discovering MaxDiscoveredDefaultRouters routers.
|
||||
// MaxDiscoveredOffLinkRoutes is the maximum number of discovered off-link
|
||||
// routes. The stack should stop discovering new off-link routes after
|
||||
// this limit is reached.
|
||||
//
|
||||
// This value MUST be at minimum 2 as per RFC 4861 section 6.3.4, and
|
||||
// SHOULD be more.
|
||||
MaxDiscoveredDefaultRouters = 10
|
||||
MaxDiscoveredOffLinkRoutes = 10
|
||||
|
||||
// MaxDiscoveredOnLinkPrefixes is the maximum number of discovered
|
||||
// on-link prefixes. The stack should stop discovering new on-link
|
||||
@@ -128,25 +132,17 @@ const (
|
||||
// maxSLAACAddrLocalRegenAttempts is the maximum number of times to attempt
|
||||
// SLAAC address regenerations in response to an IPv6 endpoint-local conflict.
|
||||
maxSLAACAddrLocalRegenAttempts = 10
|
||||
)
|
||||
|
||||
var (
|
||||
// MinPrefixInformationValidLifetimeForUpdate is the minimum Valid
|
||||
// Lifetime to update the valid lifetime of a generated address by
|
||||
// SLAAC.
|
||||
//
|
||||
// This is exported as a variable (instead of a constant) so tests
|
||||
// can update it to a smaller value.
|
||||
//
|
||||
// Min = 2hrs.
|
||||
MinPrefixInformationValidLifetimeForUpdate = 2 * time.Hour
|
||||
|
||||
// MaxDesyncFactor is the upper bound for the preferred lifetime's desync
|
||||
// factor for temporary SLAAC addresses.
|
||||
//
|
||||
// This is exported as a variable (instead of a constant) so tests
|
||||
// can update it to a smaller value.
|
||||
//
|
||||
// Must be greater than 0.
|
||||
//
|
||||
// Max = 10m (from RFC 4941 section 5).
|
||||
@@ -155,9 +151,6 @@ var (
|
||||
// MinMaxTempAddrPreferredLifetime is the minimum value allowed for the
|
||||
// maximum preferred lifetime for temporary SLAAC addresses.
|
||||
//
|
||||
// This is exported as a variable (instead of a constant) so tests
|
||||
// can update it to a smaller value.
|
||||
//
|
||||
// This value guarantees that a temporary address is preferred for at
|
||||
// least 1hr if the SLAAC prefix is valid for at least that time.
|
||||
MinMaxTempAddrPreferredLifetime = defaultRegenAdvanceDuration + MaxDesyncFactor + time.Hour
|
||||
@@ -165,9 +158,6 @@ var (
|
||||
// MinMaxTempAddrValidLifetime is the minimum value allowed for the
|
||||
// maximum valid lifetime for temporary SLAAC addresses.
|
||||
//
|
||||
// This is exported as a variable (instead of a constant) so tests
|
||||
// can update it to a smaller value.
|
||||
//
|
||||
// This value guarantees that a temporary address is valid for at least
|
||||
// 2hrs if the SLAAC prefix is valid for at least that time.
|
||||
MinMaxTempAddrValidLifetime = 2 * time.Hour
|
||||
@@ -215,28 +205,23 @@ type NDPDispatcher interface {
|
||||
// is also not permitted to call into the stack.
|
||||
OnDuplicateAddressDetectionResult(tcpip.NICID, tcpip.Address, stack.DADResult)
|
||||
|
||||
// OnDefaultRouterDiscovered is called when a new default router is
|
||||
// discovered. Implementations must return true if the newly discovered
|
||||
// router should be remembered.
|
||||
// OnOffLinkRouteUpdated is called when an off-link route is updated.
|
||||
//
|
||||
// This function is not permitted to block indefinitely. This function
|
||||
// is also not permitted to call into the stack.
|
||||
OnDefaultRouterDiscovered(tcpip.NICID, tcpip.Address) bool
|
||||
OnOffLinkRouteUpdated(tcpip.NICID, tcpip.Subnet, tcpip.Address, header.NDPRoutePreference)
|
||||
|
||||
// OnDefaultRouterInvalidated is called when a discovered default router that
|
||||
// was remembered is invalidated.
|
||||
// OnOffLinkRouteInvalidated is called when an off-link route is invalidated.
|
||||
//
|
||||
// This function is not permitted to block indefinitely. This function
|
||||
// is also not permitted to call into the stack.
|
||||
OnDefaultRouterInvalidated(tcpip.NICID, tcpip.Address)
|
||||
OnOffLinkRouteInvalidated(tcpip.NICID, tcpip.Subnet, tcpip.Address)
|
||||
|
||||
// OnOnLinkPrefixDiscovered is called when a new on-link prefix is discovered.
|
||||
// Implementations must return true if the newly discovered on-link prefix
|
||||
// should be remembered.
|
||||
//
|
||||
// This function is not permitted to block indefinitely. This function
|
||||
// is also not permitted to call into the stack.
|
||||
OnOnLinkPrefixDiscovered(tcpip.NICID, tcpip.Subnet) bool
|
||||
OnOnLinkPrefixDiscovered(tcpip.NICID, tcpip.Subnet)
|
||||
|
||||
// OnOnLinkPrefixInvalidated is called when a discovered on-link prefix that
|
||||
// was remembered is invalidated.
|
||||
@@ -246,13 +231,11 @@ type NDPDispatcher interface {
|
||||
OnOnLinkPrefixInvalidated(tcpip.NICID, tcpip.Subnet)
|
||||
|
||||
// OnAutoGenAddress is called when a new prefix with its autonomous address-
|
||||
// configuration flag set is received and SLAAC was performed. Implementations
|
||||
// may prevent the stack from assigning the address to the NIC by returning
|
||||
// false.
|
||||
// configuration flag set is received and SLAAC was performed.
|
||||
//
|
||||
// This function is not permitted to block indefinitely. It must not
|
||||
// call functions on the stack itself.
|
||||
OnAutoGenAddress(tcpip.NICID, tcpip.AddressWithPrefix) bool
|
||||
OnAutoGenAddress(tcpip.NICID, tcpip.AddressWithPrefix)
|
||||
|
||||
// OnAutoGenAddressDeprecated is called when an auto-generated address (SLAAC)
|
||||
// is deprecated, but is still considered valid. Note, if an address is
|
||||
@@ -301,10 +284,60 @@ type NDPDispatcher interface {
|
||||
OnDHCPv6Configuration(tcpip.NICID, DHCPv6ConfigurationFromNDPRA)
|
||||
}
|
||||
|
||||
var _ fmt.Stringer = HandleRAsConfiguration(0)
|
||||
|
||||
// HandleRAsConfiguration enumerates when RAs may be handled.
|
||||
type HandleRAsConfiguration int
|
||||
|
||||
const (
|
||||
// HandlingRAsDisabled indicates that Router Advertisements will not be
|
||||
// handled.
|
||||
HandlingRAsDisabled HandleRAsConfiguration = iota
|
||||
|
||||
// HandlingRAsEnabledWhenForwardingDisabled indicates that router
|
||||
// advertisements will only be handled when forwarding is disabled.
|
||||
HandlingRAsEnabledWhenForwardingDisabled
|
||||
|
||||
// HandlingRAsAlwaysEnabled indicates that Router Advertisements will always
|
||||
// be handled, even when forwarding is enabled.
|
||||
HandlingRAsAlwaysEnabled
|
||||
)
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (c HandleRAsConfiguration) String() string {
|
||||
switch c {
|
||||
case HandlingRAsDisabled:
|
||||
return "HandlingRAsDisabled"
|
||||
case HandlingRAsEnabledWhenForwardingDisabled:
|
||||
return "HandlingRAsEnabledWhenForwardingDisabled"
|
||||
case HandlingRAsAlwaysEnabled:
|
||||
return "HandlingRAsAlwaysEnabled"
|
||||
default:
|
||||
return fmt.Sprintf("HandleRAsConfiguration(%d)", c)
|
||||
}
|
||||
}
|
||||
|
||||
// enabled returns true iff Router Advertisements may be handled given the
|
||||
// specified forwarding status.
|
||||
func (c HandleRAsConfiguration) enabled(forwarding bool) bool {
|
||||
switch c {
|
||||
case HandlingRAsDisabled:
|
||||
return false
|
||||
case HandlingRAsEnabledWhenForwardingDisabled:
|
||||
return !forwarding
|
||||
case HandlingRAsAlwaysEnabled:
|
||||
return true
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled HandleRAsConfiguration = %d", c))
|
||||
}
|
||||
}
|
||||
|
||||
// NDPConfigurations is the NDP configurations for the netstack.
|
||||
type NDPConfigurations struct {
|
||||
// The number of Router Solicitation messages to send when the IPv6 endpoint
|
||||
// becomes enabled.
|
||||
//
|
||||
// Ignored unless configured to handle Router Advertisements.
|
||||
MaxRtrSolicitations uint8
|
||||
|
||||
// The amount of time between transmitting Router Solicitation messages.
|
||||
@@ -318,17 +351,24 @@ type NDPConfigurations struct {
|
||||
// Must be greater than or equal to 0s.
|
||||
MaxRtrSolicitationDelay time.Duration
|
||||
|
||||
// HandleRAs determines whether or not Router Advertisements are processed.
|
||||
HandleRAs bool
|
||||
// HandleRAs is the configuration for when Router Advertisements should be
|
||||
// handled.
|
||||
HandleRAs HandleRAsConfiguration
|
||||
|
||||
// DiscoverDefaultRouters determines whether or not default routers are
|
||||
// discovered from Router Advertisements, as per RFC 4861 section 6. This
|
||||
// configuration is ignored if HandleRAs is false.
|
||||
// configuration is ignored if RAs will not be processed (see HandleRAs).
|
||||
DiscoverDefaultRouters bool
|
||||
|
||||
// DiscoverMoreSpecificRoutes determines whether or not more specific routes
|
||||
// are discovered from Router Advertisements, as per RFC 4191. This
|
||||
// configuration is ignored if RAs will not be processed (see HandleRAs).
|
||||
DiscoverMoreSpecificRoutes bool
|
||||
|
||||
// DiscoverOnLinkPrefixes determines whether or not on-link prefixes are
|
||||
// discovered from Router Advertisements' Prefix Information option, as per
|
||||
// RFC 4861 section 6. This configuration is ignored if HandleRAs is false.
|
||||
// RFC 4861 section 6. This configuration is ignored if RAs will not be
|
||||
// processed (see HandleRAs).
|
||||
DiscoverOnLinkPrefixes bool
|
||||
|
||||
// AutoGenGlobalAddresses determines whether or not an IPv6 endpoint performs
|
||||
@@ -379,6 +419,7 @@ func DefaultNDPConfigurations() NDPConfigurations {
|
||||
MaxRtrSolicitationDelay: defaultMaxRtrSolicitationDelay,
|
||||
HandleRAs: defaultHandleRAs,
|
||||
DiscoverDefaultRouters: defaultDiscoverDefaultRouters,
|
||||
DiscoverMoreSpecificRoutes: defaultDiscoverMoreSpecificRoutes,
|
||||
DiscoverOnLinkPrefixes: defaultDiscoverOnLinkPrefixes,
|
||||
AutoGenGlobalAddresses: defaultAutoGenGlobalAddresses,
|
||||
AutoGenTempGlobalAddresses: defaultAutoGenTempGlobalAddresses,
|
||||
@@ -419,6 +460,11 @@ type timer struct {
|
||||
timer tcpip.Timer
|
||||
}
|
||||
|
||||
type offLinkRoute struct {
|
||||
dest tcpip.Subnet
|
||||
router tcpip.Address
|
||||
}
|
||||
|
||||
// ndpState is the per-Interface NDP state.
|
||||
type ndpState struct {
|
||||
// Do not allow overwriting this state.
|
||||
@@ -433,8 +479,8 @@ type ndpState struct {
|
||||
// The DAD timers to send the next NS message, or resolve the address.
|
||||
dad ip.DAD
|
||||
|
||||
// The default routers discovered through Router Advertisements.
|
||||
defaultRouters map[tcpip.Address]defaultRouterState
|
||||
// The off-link routes discovered through Router Advertisements.
|
||||
offLinkRoutes map[offLinkRoute]offLinkRouteState
|
||||
|
||||
// rtrSolicitTimer is the timer used to send the next router solicitation
|
||||
// message.
|
||||
@@ -462,10 +508,12 @@ type ndpState struct {
|
||||
temporaryAddressDesyncFactor time.Duration
|
||||
}
|
||||
|
||||
// defaultRouterState holds data associated with a default router discovered by
|
||||
// offLinkRouteState holds data associated with an off-link route discovered by
|
||||
// a Router Advertisement (RA).
|
||||
type defaultRouterState struct {
|
||||
// Job to invalidate the default router.
|
||||
type offLinkRouteState struct {
|
||||
prf header.NDPRoutePreference
|
||||
|
||||
// Job to invalidate the route.
|
||||
//
|
||||
// Must not be nil.
|
||||
invalidationJob *tcpip.Job
|
||||
@@ -498,7 +546,7 @@ type tempSLAACAddrState struct {
|
||||
// Must not be nil.
|
||||
regenJob *tcpip.Job
|
||||
|
||||
createdAt time.Time
|
||||
createdAt tcpip.MonotonicTime
|
||||
|
||||
// The address's endpoint.
|
||||
//
|
||||
@@ -521,11 +569,11 @@ type slaacPrefixState struct {
|
||||
// Must not be nil.
|
||||
invalidationJob *tcpip.Job
|
||||
|
||||
// Nonzero only when the address is not valid forever.
|
||||
validUntil time.Time
|
||||
// nil iff the address is valid forever.
|
||||
validUntil *tcpip.MonotonicTime
|
||||
|
||||
// Nonzero only when the address is not preferred forever.
|
||||
preferredUntil time.Time
|
||||
// nil iff the address is preferred forever.
|
||||
preferredUntil *tcpip.MonotonicTime
|
||||
|
||||
// State associated with the stable address generated for the prefix.
|
||||
stableAddr struct {
|
||||
@@ -654,7 +702,8 @@ func (ndp *ndpState) handleRA(ip tcpip.Address, ra header.NDPRouterAdvert) {
|
||||
// per-interface basis; it is a protocol-wide configuration, so we check the
|
||||
// protocol's forwarding flag to determine if the IPv6 endpoint is forwarding
|
||||
// packets.
|
||||
if !ndp.configs.HandleRAs || ndp.ep.protocol.Forwarding() {
|
||||
if !ndp.configs.HandleRAs.enabled(ndp.ep.Forwarding()) {
|
||||
ndp.ep.stats.localStats.UnhandledRouterAdvertisements.Increment()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -682,30 +731,22 @@ func (ndp *ndpState) handleRA(ip tcpip.Address, ra header.NDPRouterAdvert) {
|
||||
|
||||
// Is the IPv6 endpoint configured to discover default routers?
|
||||
if ndp.configs.DiscoverDefaultRouters {
|
||||
rtr, ok := ndp.defaultRouters[ip]
|
||||
rl := ra.RouterLifetime()
|
||||
switch {
|
||||
case !ok && rl != 0:
|
||||
// This is a new default router we are discovering.
|
||||
prf := ra.DefaultRouterPreference()
|
||||
if prf == header.ReservedRoutePreference {
|
||||
// As per RFC 4191 section 2.2,
|
||||
//
|
||||
// Only remember it if we currently know about less than
|
||||
// MaxDiscoveredDefaultRouters routers.
|
||||
if len(ndp.defaultRouters) < MaxDiscoveredDefaultRouters {
|
||||
ndp.rememberDefaultRouter(ip, rl)
|
||||
}
|
||||
|
||||
case ok && rl != 0:
|
||||
// This is an already discovered default router. Update
|
||||
// the invalidation job.
|
||||
rtr.invalidationJob.Cancel()
|
||||
rtr.invalidationJob.Schedule(rl)
|
||||
ndp.defaultRouters[ip] = rtr
|
||||
|
||||
case ok && rl == 0:
|
||||
// We know about the router but it is no longer to be
|
||||
// used as a default router so invalidate it.
|
||||
ndp.invalidateDefaultRouter(ip)
|
||||
// Prf (Default Router Preference)
|
||||
//
|
||||
// If the Reserved (10) value is received, the receiver MUST treat the
|
||||
// value as if it were (00).
|
||||
//
|
||||
// Note that the value 00 is the medium (default) router preference value.
|
||||
prf = header.MediumRoutePreference
|
||||
}
|
||||
|
||||
// We represent default routers with a default (off-link) route through the
|
||||
// router.
|
||||
ndp.handleOffLinkRouteDiscovery(offLinkRoute{dest: header.IPv6EmptySubnet, router: ip}, ra.RouterLifetime(), prf)
|
||||
}
|
||||
|
||||
// TODO(b/141556115): Do (RetransTimer, ReachableTime)) Parameter
|
||||
@@ -737,7 +778,7 @@ func (ndp *ndpState) handleRA(ip tcpip.Address, ra header.NDPRouterAdvert) {
|
||||
prefix := opt.Subnet()
|
||||
|
||||
// Is the prefix a link-local?
|
||||
if header.IsV6LinkLocalAddress(prefix.ID()) {
|
||||
if header.IsV6LinkLocalUnicastAddress(prefix.ID()) {
|
||||
// ...Yes, skip as per RFC 4861 section 6.3.4,
|
||||
// and RFC 4862 section 5.5.3.b (for SLAAC).
|
||||
continue
|
||||
@@ -757,61 +798,107 @@ func (ndp *ndpState) handleRA(ip tcpip.Address, ra header.NDPRouterAdvert) {
|
||||
if opt.AutonomousAddressConfigurationFlag() {
|
||||
ndp.handleAutonomousPrefixInformation(opt)
|
||||
}
|
||||
|
||||
case header.NDPRouteInformation:
|
||||
if !ndp.configs.DiscoverMoreSpecificRoutes {
|
||||
continue
|
||||
}
|
||||
|
||||
dest, err := opt.Prefix()
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("%T.Prefix(): %s", opt, err))
|
||||
}
|
||||
|
||||
prf := opt.RoutePreference()
|
||||
if prf == header.ReservedRoutePreference {
|
||||
// As per RFC 4191 section 2.3,
|
||||
//
|
||||
// Prf (Route Preference)
|
||||
// 2-bit signed integer. The Route Preference indicates
|
||||
// whether to prefer the router associated with this prefix
|
||||
// over others, when multiple identical prefixes (for
|
||||
// different routers) have been received. If the Reserved
|
||||
// (10) value is received, the Route Information Option MUST
|
||||
// be ignored.
|
||||
continue
|
||||
}
|
||||
|
||||
ndp.handleOffLinkRouteDiscovery(offLinkRoute{dest: dest, router: ip}, opt.RouteLifetime(), prf)
|
||||
}
|
||||
|
||||
// TODO(b/141556115): Do (MTU) Parameter Discovery.
|
||||
}
|
||||
}
|
||||
|
||||
// invalidateDefaultRouter invalidates a discovered default router.
|
||||
// invalidateOffLinkRoute invalidates a discovered off-link route.
|
||||
//
|
||||
// The IPv6 endpoint that ndp belongs to MUST be locked.
|
||||
func (ndp *ndpState) invalidateDefaultRouter(ip tcpip.Address) {
|
||||
rtr, ok := ndp.defaultRouters[ip]
|
||||
|
||||
// Is the router still discovered?
|
||||
func (ndp *ndpState) invalidateOffLinkRoute(route offLinkRoute) {
|
||||
state, ok := ndp.offLinkRoutes[route]
|
||||
if !ok {
|
||||
// ...Nope, do nothing further.
|
||||
return
|
||||
}
|
||||
|
||||
rtr.invalidationJob.Cancel()
|
||||
delete(ndp.defaultRouters, ip)
|
||||
state.invalidationJob.Cancel()
|
||||
delete(ndp.offLinkRoutes, route)
|
||||
|
||||
// Let the integrator know a discovered default router is invalidated.
|
||||
// Let the integrator know a discovered off-link route is invalidated.
|
||||
if ndpDisp := ndp.ep.protocol.options.NDPDisp; ndpDisp != nil {
|
||||
ndpDisp.OnDefaultRouterInvalidated(ndp.ep.nic.ID(), ip)
|
||||
ndpDisp.OnOffLinkRouteInvalidated(ndp.ep.nic.ID(), route.dest, route.router)
|
||||
}
|
||||
}
|
||||
|
||||
// rememberDefaultRouter remembers a newly discovered default router with IPv6
|
||||
// link-local address ip with lifetime rl.
|
||||
// handleOffLinkRouteDiscovery handles the discovery of an off-link route.
|
||||
//
|
||||
// The router identified by ip MUST NOT already be known by the IPv6 endpoint.
|
||||
//
|
||||
// The IPv6 endpoint that ndp belongs to MUST be locked.
|
||||
func (ndp *ndpState) rememberDefaultRouter(ip tcpip.Address, rl time.Duration) {
|
||||
// Precondition: ndp.ep.mu must be locked.
|
||||
func (ndp *ndpState) handleOffLinkRouteDiscovery(route offLinkRoute, lifetime time.Duration, prf header.NDPRoutePreference) {
|
||||
ndpDisp := ndp.ep.protocol.options.NDPDisp
|
||||
if ndpDisp == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Inform the integrator when we discovered a default router.
|
||||
if !ndpDisp.OnDefaultRouterDiscovered(ndp.ep.nic.ID(), ip) {
|
||||
// Informed by the integrator to not remember the router, do
|
||||
// nothing further.
|
||||
return
|
||||
state, ok := ndp.offLinkRoutes[route]
|
||||
switch {
|
||||
case !ok && lifetime != 0:
|
||||
// This is a new route we are discovering.
|
||||
//
|
||||
// Only remember it if we currently know about less than
|
||||
// MaxDiscoveredOffLinkRoutes routers.
|
||||
if len(ndp.offLinkRoutes) < MaxDiscoveredOffLinkRoutes {
|
||||
// Inform the integrator when we discovered an off-link route.
|
||||
ndpDisp.OnOffLinkRouteUpdated(ndp.ep.nic.ID(), route.dest, route.router, prf)
|
||||
|
||||
state := offLinkRouteState{
|
||||
prf: prf,
|
||||
invalidationJob: tcpip.NewJob(ndp.ep.protocol.stack.Clock(), &ndp.ep.mu, func() {
|
||||
ndp.invalidateOffLinkRoute(route)
|
||||
}),
|
||||
}
|
||||
|
||||
state.invalidationJob.Schedule(lifetime)
|
||||
|
||||
ndp.offLinkRoutes[route] = state
|
||||
}
|
||||
|
||||
case ok && lifetime != 0:
|
||||
// This is an already discovered off-link route. Update the lifetime.
|
||||
state.invalidationJob.Cancel()
|
||||
state.invalidationJob.Schedule(lifetime)
|
||||
|
||||
if prf != state.prf {
|
||||
state.prf = prf
|
||||
|
||||
// Inform the integrator about route preference updates.
|
||||
ndpDisp.OnOffLinkRouteUpdated(ndp.ep.nic.ID(), route.dest, route.router, prf)
|
||||
}
|
||||
|
||||
ndp.offLinkRoutes[route] = state
|
||||
|
||||
case ok && lifetime == 0:
|
||||
// The already discovered off-link route is no longer considered valid so we
|
||||
// invalidate it immediately.
|
||||
ndp.invalidateOffLinkRoute(route)
|
||||
}
|
||||
|
||||
state := defaultRouterState{
|
||||
invalidationJob: ndp.ep.protocol.stack.NewJob(&ndp.ep.mu, func() {
|
||||
ndp.invalidateDefaultRouter(ip)
|
||||
}),
|
||||
}
|
||||
|
||||
state.invalidationJob.Schedule(rl)
|
||||
|
||||
ndp.defaultRouters[ip] = state
|
||||
}
|
||||
|
||||
// rememberOnLinkPrefix remembers a newly discovered on-link prefix with IPv6
|
||||
@@ -827,14 +914,10 @@ func (ndp *ndpState) rememberOnLinkPrefix(prefix tcpip.Subnet, l time.Duration)
|
||||
}
|
||||
|
||||
// Inform the integrator when we discovered an on-link prefix.
|
||||
if !ndpDisp.OnOnLinkPrefixDiscovered(ndp.ep.nic.ID(), prefix) {
|
||||
// Informed by the integrator to not remember the prefix, do
|
||||
// nothing further.
|
||||
return
|
||||
}
|
||||
ndpDisp.OnOnLinkPrefixDiscovered(ndp.ep.nic.ID(), prefix)
|
||||
|
||||
state := onLinkPrefixState{
|
||||
invalidationJob: ndp.ep.protocol.stack.NewJob(&ndp.ep.mu, func() {
|
||||
invalidationJob: tcpip.NewJob(ndp.ep.protocol.stack.Clock(), &ndp.ep.mu, func() {
|
||||
ndp.invalidateOnLinkPrefix(prefix)
|
||||
}),
|
||||
}
|
||||
@@ -979,7 +1062,7 @@ func (ndp *ndpState) doSLAAC(prefix tcpip.Subnet, pl, vl time.Duration) {
|
||||
}
|
||||
|
||||
state := slaacPrefixState{
|
||||
deprecationJob: ndp.ep.protocol.stack.NewJob(&ndp.ep.mu, func() {
|
||||
deprecationJob: tcpip.NewJob(ndp.ep.protocol.stack.Clock(), &ndp.ep.mu, func() {
|
||||
state, ok := ndp.slaacPrefixes[prefix]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("ndp: must have a slaacPrefixes entry for the deprecated SLAAC prefix %s", prefix))
|
||||
@@ -987,7 +1070,7 @@ func (ndp *ndpState) doSLAAC(prefix tcpip.Subnet, pl, vl time.Duration) {
|
||||
|
||||
ndp.deprecateSLAACAddress(state.stableAddr.addressEndpoint)
|
||||
}),
|
||||
invalidationJob: ndp.ep.protocol.stack.NewJob(&ndp.ep.mu, func() {
|
||||
invalidationJob: tcpip.NewJob(ndp.ep.protocol.stack.Clock(), &ndp.ep.mu, func() {
|
||||
state, ok := ndp.slaacPrefixes[prefix]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("ndp: must have a slaacPrefixes entry for the invalidated SLAAC prefix %s", prefix))
|
||||
@@ -999,12 +1082,13 @@ func (ndp *ndpState) doSLAAC(prefix tcpip.Subnet, pl, vl time.Duration) {
|
||||
maxGenerationAttempts: ndp.configs.AutoGenAddressConflictRetries + 1,
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
now := ndp.ep.protocol.stack.Clock().NowMonotonic()
|
||||
|
||||
// The time an address is preferred until is needed to properly generate the
|
||||
// address.
|
||||
if pl < header.NDPInfiniteLifetime {
|
||||
state.preferredUntil = now.Add(pl)
|
||||
t := now.Add(pl)
|
||||
state.preferredUntil = &t
|
||||
}
|
||||
|
||||
if !ndp.generateSLAACAddr(prefix, &state) {
|
||||
@@ -1022,7 +1106,8 @@ func (ndp *ndpState) doSLAAC(prefix tcpip.Subnet, pl, vl time.Duration) {
|
||||
|
||||
if vl < header.NDPInfiniteLifetime {
|
||||
state.invalidationJob.Schedule(vl)
|
||||
state.validUntil = now.Add(vl)
|
||||
t := now.Add(vl)
|
||||
state.validUntil = &t
|
||||
}
|
||||
|
||||
// If the address is assigned (DAD resolved), generate a temporary address.
|
||||
@@ -1045,16 +1130,17 @@ func (ndp *ndpState) addAndAcquireSLAACAddr(addr tcpip.AddressWithPrefix, config
|
||||
return nil
|
||||
}
|
||||
|
||||
if !ndpDisp.OnAutoGenAddress(ndp.ep.nic.ID(), addr) {
|
||||
// Informed by the integrator not to add the address.
|
||||
return nil
|
||||
}
|
||||
|
||||
addressEndpoint, err := ndp.ep.addAndAcquirePermanentAddressLocked(addr, stack.FirstPrimaryEndpoint, configType, deprecated)
|
||||
addressEndpoint, err := ndp.ep.addAndAcquirePermanentAddressLocked(addr, stack.AddressProperties{
|
||||
PEB: stack.FirstPrimaryEndpoint,
|
||||
ConfigType: configType,
|
||||
Deprecated: deprecated,
|
||||
})
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("ndp: error when adding SLAAC address %+v: %s", addr, err))
|
||||
}
|
||||
|
||||
ndpDisp.OnAutoGenAddress(ndp.ep.nic.ID(), addr)
|
||||
|
||||
return addressEndpoint
|
||||
}
|
||||
|
||||
@@ -1130,7 +1216,8 @@ func (ndp *ndpState) generateSLAACAddr(prefix tcpip.Subnet, state *slaacPrefixSt
|
||||
state.stableAddr.localGenerationFailures++
|
||||
}
|
||||
|
||||
if addressEndpoint := ndp.addAndAcquireSLAACAddr(generatedAddr, stack.AddressConfigSlaac, time.Since(state.preferredUntil) >= 0 /* deprecated */); addressEndpoint != nil {
|
||||
deprecated := state.preferredUntil != nil && !state.preferredUntil.After(ndp.ep.protocol.stack.Clock().NowMonotonic())
|
||||
if addressEndpoint := ndp.addAndAcquireSLAACAddr(generatedAddr, stack.AddressConfigSlaac, deprecated); addressEndpoint != nil {
|
||||
state.stableAddr.addressEndpoint = addressEndpoint
|
||||
state.generationAttempts++
|
||||
return true
|
||||
@@ -1185,13 +1272,13 @@ func (ndp *ndpState) generateTempSLAACAddr(prefix tcpip.Subnet, prefixState *sla
|
||||
}
|
||||
|
||||
stableAddr := prefixState.stableAddr.addressEndpoint.AddressWithPrefix().Address
|
||||
now := time.Now()
|
||||
now := ndp.ep.protocol.stack.Clock().NowMonotonic()
|
||||
|
||||
// As per RFC 4941 section 3.3 step 4, the valid lifetime of a temporary
|
||||
// address is the lower of the valid lifetime of the stable address or the
|
||||
// maximum temporary address valid lifetime.
|
||||
vl := ndp.configs.MaxTempAddrValidLifetime
|
||||
if prefixState.validUntil != (time.Time{}) {
|
||||
if prefixState.validUntil != nil {
|
||||
if prefixVL := prefixState.validUntil.Sub(now); vl > prefixVL {
|
||||
vl = prefixVL
|
||||
}
|
||||
@@ -1207,7 +1294,7 @@ func (ndp *ndpState) generateTempSLAACAddr(prefix tcpip.Subnet, prefixState *sla
|
||||
// maximum temporary address preferred lifetime - the temporary address desync
|
||||
// factor.
|
||||
pl := ndp.configs.MaxTempAddrPreferredLifetime - ndp.temporaryAddressDesyncFactor
|
||||
if prefixState.preferredUntil != (time.Time{}) {
|
||||
if prefixState.preferredUntil != nil {
|
||||
if prefixPL := prefixState.preferredUntil.Sub(now); pl > prefixPL {
|
||||
// Respect the preferred lifetime of the prefix, as per RFC 4941 section
|
||||
// 3.3 step 4.
|
||||
@@ -1248,7 +1335,7 @@ func (ndp *ndpState) generateTempSLAACAddr(prefix tcpip.Subnet, prefixState *sla
|
||||
}
|
||||
|
||||
state := tempSLAACAddrState{
|
||||
deprecationJob: ndp.ep.protocol.stack.NewJob(&ndp.ep.mu, func() {
|
||||
deprecationJob: tcpip.NewJob(ndp.ep.protocol.stack.Clock(), &ndp.ep.mu, func() {
|
||||
prefixState, ok := ndp.slaacPrefixes[prefix]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("ndp: must have a slaacPrefixes entry for %s to deprecate temporary address %s", prefix, generatedAddr))
|
||||
@@ -1261,7 +1348,7 @@ func (ndp *ndpState) generateTempSLAACAddr(prefix tcpip.Subnet, prefixState *sla
|
||||
|
||||
ndp.deprecateSLAACAddress(tempAddrState.addressEndpoint)
|
||||
}),
|
||||
invalidationJob: ndp.ep.protocol.stack.NewJob(&ndp.ep.mu, func() {
|
||||
invalidationJob: tcpip.NewJob(ndp.ep.protocol.stack.Clock(), &ndp.ep.mu, func() {
|
||||
prefixState, ok := ndp.slaacPrefixes[prefix]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("ndp: must have a slaacPrefixes entry for %s to invalidate temporary address %s", prefix, generatedAddr))
|
||||
@@ -1274,7 +1361,7 @@ func (ndp *ndpState) generateTempSLAACAddr(prefix tcpip.Subnet, prefixState *sla
|
||||
|
||||
ndp.invalidateTempSLAACAddr(prefixState.tempAddrs, generatedAddr.Address, tempAddrState)
|
||||
}),
|
||||
regenJob: ndp.ep.protocol.stack.NewJob(&ndp.ep.mu, func() {
|
||||
regenJob: tcpip.NewJob(ndp.ep.protocol.stack.Clock(), &ndp.ep.mu, func() {
|
||||
prefixState, ok := ndp.slaacPrefixes[prefix]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("ndp: must have a slaacPrefixes entry for %s to regenerate temporary address after %s", prefix, generatedAddr))
|
||||
@@ -1342,16 +1429,17 @@ func (ndp *ndpState) refreshSLAACPrefixLifetimes(prefix tcpip.Subnet, prefixStat
|
||||
// deprecation job so it can be reset.
|
||||
prefixState.deprecationJob.Cancel()
|
||||
|
||||
now := time.Now()
|
||||
now := ndp.ep.protocol.stack.Clock().NowMonotonic()
|
||||
|
||||
// Schedule the deprecation job if prefix has a finite preferred lifetime.
|
||||
if pl < header.NDPInfiniteLifetime {
|
||||
if !deprecated {
|
||||
prefixState.deprecationJob.Schedule(pl)
|
||||
}
|
||||
prefixState.preferredUntil = now.Add(pl)
|
||||
t := now.Add(pl)
|
||||
prefixState.preferredUntil = &t
|
||||
} else {
|
||||
prefixState.preferredUntil = time.Time{}
|
||||
prefixState.preferredUntil = nil
|
||||
}
|
||||
|
||||
// As per RFC 4862 section 5.5.3.e, update the valid lifetime for prefix:
|
||||
@@ -1369,17 +1457,17 @@ func (ndp *ndpState) refreshSLAACPrefixLifetimes(prefix tcpip.Subnet, prefixStat
|
||||
// Handle the infinite valid lifetime separately as we do not schedule a
|
||||
// job in this case.
|
||||
prefixState.invalidationJob.Cancel()
|
||||
prefixState.validUntil = time.Time{}
|
||||
prefixState.validUntil = nil
|
||||
} else {
|
||||
var effectiveVl time.Duration
|
||||
var rl time.Duration
|
||||
|
||||
// If the prefix was originally set to be valid forever, assume the
|
||||
// remaining time to be the maximum possible value.
|
||||
if prefixState.validUntil == (time.Time{}) {
|
||||
if prefixState.validUntil == nil {
|
||||
rl = header.NDPInfiniteLifetime
|
||||
} else {
|
||||
rl = time.Until(prefixState.validUntil)
|
||||
rl = prefixState.validUntil.Sub(now)
|
||||
}
|
||||
|
||||
if vl > MinPrefixInformationValidLifetimeForUpdate || vl > rl {
|
||||
@@ -1391,7 +1479,8 @@ func (ndp *ndpState) refreshSLAACPrefixLifetimes(prefix tcpip.Subnet, prefixStat
|
||||
if effectiveVl != 0 {
|
||||
prefixState.invalidationJob.Cancel()
|
||||
prefixState.invalidationJob.Schedule(effectiveVl)
|
||||
prefixState.validUntil = now.Add(effectiveVl)
|
||||
t := now.Add(effectiveVl)
|
||||
prefixState.validUntil = &t
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1411,8 +1500,8 @@ func (ndp *ndpState) refreshSLAACPrefixLifetimes(prefix tcpip.Subnet, prefixStat
|
||||
// maximum temporary address valid lifetime. Note, the valid lifetime of a
|
||||
// temporary address is relative to the address's creation time.
|
||||
validUntil := tempAddrState.createdAt.Add(ndp.configs.MaxTempAddrValidLifetime)
|
||||
if prefixState.validUntil != (time.Time{}) && validUntil.Sub(prefixState.validUntil) > 0 {
|
||||
validUntil = prefixState.validUntil
|
||||
if prefixState.validUntil != nil && prefixState.validUntil.Before(validUntil) {
|
||||
validUntil = *prefixState.validUntil
|
||||
}
|
||||
|
||||
// If the address is no longer valid, invalidate it immediately. Otherwise,
|
||||
@@ -1431,14 +1520,15 @@ func (ndp *ndpState) refreshSLAACPrefixLifetimes(prefix tcpip.Subnet, prefixStat
|
||||
// desync factor. Note, the preferred lifetime of a temporary address is
|
||||
// relative to the address's creation time.
|
||||
preferredUntil := tempAddrState.createdAt.Add(ndp.configs.MaxTempAddrPreferredLifetime - ndp.temporaryAddressDesyncFactor)
|
||||
if prefixState.preferredUntil != (time.Time{}) && preferredUntil.Sub(prefixState.preferredUntil) > 0 {
|
||||
preferredUntil = prefixState.preferredUntil
|
||||
if prefixState.preferredUntil != nil && prefixState.preferredUntil.Before(preferredUntil) {
|
||||
preferredUntil = *prefixState.preferredUntil
|
||||
}
|
||||
|
||||
// If the address is no longer preferred, deprecate it immediately.
|
||||
// Otherwise, schedule the deprecation job again.
|
||||
newPreferredLifetime := preferredUntil.Sub(now)
|
||||
tempAddrState.deprecationJob.Cancel()
|
||||
|
||||
if newPreferredLifetime <= 0 {
|
||||
ndp.deprecateSLAACAddress(tempAddrState.addressEndpoint)
|
||||
} else {
|
||||
@@ -1609,44 +1699,16 @@ func (ndp *ndpState) cleanupTempSLAACAddrResourcesAndNotifyInner(tempAddrs map[t
|
||||
delete(tempAddrs, tempAddr)
|
||||
}
|
||||
|
||||
// removeSLAACAddresses removes all SLAAC addresses.
|
||||
//
|
||||
// If keepLinkLocal is false, the SLAAC generated link-local address is removed.
|
||||
//
|
||||
// The IPv6 endpoint that ndp belongs to MUST be locked.
|
||||
func (ndp *ndpState) removeSLAACAddresses(keepLinkLocal bool) {
|
||||
linkLocalSubnet := header.IPv6LinkLocalPrefix.Subnet()
|
||||
var linkLocalPrefixes int
|
||||
for prefix, state := range ndp.slaacPrefixes {
|
||||
// RFC 4862 section 5 states that routers are also expected to generate a
|
||||
// link-local address so we do not invalidate them if we are cleaning up
|
||||
// host-only state.
|
||||
if keepLinkLocal && prefix == linkLocalSubnet {
|
||||
linkLocalPrefixes++
|
||||
continue
|
||||
}
|
||||
|
||||
ndp.invalidateSLAACPrefix(prefix, state)
|
||||
}
|
||||
|
||||
if got := len(ndp.slaacPrefixes); got != linkLocalPrefixes {
|
||||
panic(fmt.Sprintf("ndp: still have non-linklocal SLAAC prefixes after cleaning up; found = %d prefixes, of which %d are link-local", got, linkLocalPrefixes))
|
||||
}
|
||||
}
|
||||
|
||||
// cleanupState cleans up ndp's state.
|
||||
//
|
||||
// If hostOnly is true, then only host-specific state is cleaned up.
|
||||
//
|
||||
// This function invalidates all discovered on-link prefixes, discovered
|
||||
// routers, and auto-generated addresses.
|
||||
//
|
||||
// If hostOnly is true, then the link-local auto-generated address aren't
|
||||
// invalidated as routers are also expected to generate a link-local address.
|
||||
//
|
||||
// The IPv6 endpoint that ndp belongs to MUST be locked.
|
||||
func (ndp *ndpState) cleanupState(hostOnly bool) {
|
||||
ndp.removeSLAACAddresses(hostOnly /* keepLinkLocal */)
|
||||
func (ndp *ndpState) cleanupState() {
|
||||
for prefix, state := range ndp.slaacPrefixes {
|
||||
ndp.invalidateSLAACPrefix(prefix, state)
|
||||
}
|
||||
|
||||
for prefix := range ndp.onLinkPrefixes {
|
||||
ndp.invalidateOnLinkPrefix(prefix)
|
||||
@@ -1656,12 +1718,12 @@ func (ndp *ndpState) cleanupState(hostOnly bool) {
|
||||
panic(fmt.Sprintf("ndp: still have discovered on-link prefixes after cleaning up; found = %d", got))
|
||||
}
|
||||
|
||||
for router := range ndp.defaultRouters {
|
||||
ndp.invalidateDefaultRouter(router)
|
||||
for route := range ndp.offLinkRoutes {
|
||||
ndp.invalidateOffLinkRoute(route)
|
||||
}
|
||||
|
||||
if got := len(ndp.defaultRouters); got != 0 {
|
||||
panic(fmt.Sprintf("ndp: still have discovered default routers after cleaning up; found = %d", got))
|
||||
if got := len(ndp.offLinkRoutes); got != 0 {
|
||||
panic(fmt.Sprintf("ndp: still have discovered off-link routes after cleaning up; found = %d", got))
|
||||
}
|
||||
|
||||
ndp.dhcpv6Configuration = 0
|
||||
@@ -1670,6 +1732,10 @@ func (ndp *ndpState) cleanupState(hostOnly bool) {
|
||||
// startSolicitingRouters starts soliciting routers, as per RFC 4861 section
|
||||
// 6.3.7. If routers are already being solicited, this function does nothing.
|
||||
//
|
||||
// If ndp is not configured to handle Router Advertisements, routers will not
|
||||
// be solicited as there is no point soliciting routers if we don't handle their
|
||||
// advertisements.
|
||||
//
|
||||
// The IPv6 endpoint that ndp belongs to MUST be locked.
|
||||
func (ndp *ndpState) startSolicitingRouters() {
|
||||
if ndp.rtrSolicitTimer.timer != nil {
|
||||
@@ -1682,11 +1748,15 @@ func (ndp *ndpState) startSolicitingRouters() {
|
||||
return
|
||||
}
|
||||
|
||||
if !ndp.configs.HandleRAs.enabled(ndp.ep.Forwarding()) {
|
||||
return
|
||||
}
|
||||
|
||||
// Calculate the random delay before sending our first RS, as per RFC
|
||||
// 4861 section 6.3.7.
|
||||
var delay time.Duration
|
||||
if ndp.configs.MaxRtrSolicitationDelay > 0 {
|
||||
delay = time.Duration(rand.Int63n(int64(ndp.configs.MaxRtrSolicitationDelay)))
|
||||
delay = time.Duration(ndp.ep.protocol.stack.Rand().Int63n(int64(ndp.configs.MaxRtrSolicitationDelay)))
|
||||
}
|
||||
|
||||
// Protected by ndp.ep.mu.
|
||||
@@ -1703,7 +1773,7 @@ func (ndp *ndpState) startSolicitingRouters() {
|
||||
// the unspecified address if no address is assigned
|
||||
// to the sending interface.
|
||||
localAddr := header.IPv6Any
|
||||
if addressEndpoint := ndp.ep.AcquireOutgoingPrimaryAddress(header.IPv6AllRoutersMulticastAddress, false); addressEndpoint != nil {
|
||||
if addressEndpoint := ndp.ep.AcquireOutgoingPrimaryAddress(header.IPv6AllRoutersLinkLocalMulticastAddress, false); addressEndpoint != nil {
|
||||
localAddr = addressEndpoint.AddressWithPrefix().Address
|
||||
addressEndpoint.DecRef()
|
||||
}
|
||||
@@ -1722,7 +1792,7 @@ func (ndp *ndpState) startSolicitingRouters() {
|
||||
header.NDPSourceLinkLayerAddressOption(linkAddress),
|
||||
}
|
||||
}
|
||||
payloadSize := header.ICMPv6HeaderSize + header.NDPRSMinimumSize + int(optsSerializer.Length())
|
||||
payloadSize := header.ICMPv6HeaderSize + header.NDPRSMinimumSize + optsSerializer.Length()
|
||||
icmpData := header.ICMPv6(buffer.NewView(payloadSize))
|
||||
icmpData.SetType(header.ICMPv6RouterSolicit)
|
||||
rs := header.NDPRouterSolicit(icmpData.MessageBody())
|
||||
@@ -1730,23 +1800,24 @@ func (ndp *ndpState) startSolicitingRouters() {
|
||||
icmpData.SetChecksum(header.ICMPv6Checksum(header.ICMPv6ChecksumParams{
|
||||
Header: icmpData,
|
||||
Src: localAddr,
|
||||
Dst: header.IPv6AllRoutersMulticastAddress,
|
||||
Dst: header.IPv6AllRoutersLinkLocalMulticastAddress,
|
||||
}))
|
||||
|
||||
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
ReserveHeaderBytes: int(ndp.ep.MaxHeaderLength()),
|
||||
Data: buffer.View(icmpData).ToVectorisedView(),
|
||||
})
|
||||
defer pkt.DecRef()
|
||||
|
||||
sent := ndp.ep.stats.icmp.packetsSent
|
||||
if err := addIPHeader(localAddr, header.IPv6AllRoutersMulticastAddress, pkt, stack.NetworkHeaderParams{
|
||||
if err := addIPHeader(localAddr, header.IPv6AllRoutersLinkLocalMulticastAddress, pkt, stack.NetworkHeaderParams{
|
||||
Protocol: header.ICMPv6ProtocolNumber,
|
||||
TTL: header.NDPHopLimit,
|
||||
}, nil /* extensionHeaders */); err != nil {
|
||||
panic(fmt.Sprintf("failed to add IP header: %s", err))
|
||||
}
|
||||
|
||||
if err := ndp.ep.nic.WritePacketToRemote(header.EthernetAddressFromMulticastIPv6Address(header.IPv6AllRoutersMulticastAddress), nil /* gso */, ProtocolNumber, pkt); err != nil {
|
||||
if err := ndp.ep.nic.WritePacketToRemote(header.EthernetAddressFromMulticastIPv6Address(header.IPv6AllRoutersLinkLocalMulticastAddress), ProtocolNumber, pkt); err != nil {
|
||||
sent.dropped.Increment()
|
||||
// Don't send any more messages if we had an error.
|
||||
remaining = 0
|
||||
@@ -1774,6 +1845,32 @@ func (ndp *ndpState) startSolicitingRouters() {
|
||||
}
|
||||
}
|
||||
|
||||
// forwardingChanged handles a change in forwarding configuration.
|
||||
//
|
||||
// If transitioning to a host, router solicitation will be started. Otherwise,
|
||||
// router solicitation will be stopped if NDP is not configured to handle RAs
|
||||
// as a router.
|
||||
//
|
||||
// Precondition: ndp.ep.mu must be locked.
|
||||
func (ndp *ndpState) forwardingChanged(forwarding bool) {
|
||||
if forwarding {
|
||||
if ndp.configs.HandleRAs.enabled(forwarding) {
|
||||
return
|
||||
}
|
||||
|
||||
ndp.stopSolicitingRouters()
|
||||
return
|
||||
}
|
||||
|
||||
// Solicit routers when transitioning to a host.
|
||||
//
|
||||
// If the endpoint is not currently enabled, routers will be solicited when
|
||||
// the endpoint becomes enabled (if it is still a host).
|
||||
if ndp.ep.Enabled() {
|
||||
ndp.startSolicitingRouters()
|
||||
}
|
||||
}
|
||||
|
||||
// stopSolicitingRouters stops soliciting routers. If routers are not currently
|
||||
// being solicited, this function does nothing.
|
||||
//
|
||||
@@ -1790,21 +1887,19 @@ func (ndp *ndpState) stopSolicitingRouters() {
|
||||
}
|
||||
|
||||
func (ndp *ndpState) init(ep *endpoint, dadOptions ip.DADOptions) {
|
||||
if ndp.defaultRouters != nil {
|
||||
if ndp.offLinkRoutes != nil {
|
||||
panic("attempted to initialize NDP state twice")
|
||||
}
|
||||
|
||||
ndp.ep = ep
|
||||
ndp.configs = ep.protocol.options.NDPConfigs
|
||||
ndp.dad.Init(&ndp.ep.mu, ep.protocol.options.DADConfigs, dadOptions)
|
||||
ndp.defaultRouters = make(map[tcpip.Address]defaultRouterState)
|
||||
ndp.offLinkRoutes = make(map[offLinkRoute]offLinkRouteState)
|
||||
ndp.onLinkPrefixes = make(map[tcpip.Subnet]onLinkPrefixState)
|
||||
ndp.slaacPrefixes = make(map[tcpip.Subnet]slaacPrefixState)
|
||||
|
||||
header.InitialTempIID(ndp.temporaryIIDHistory[:], ndp.ep.protocol.options.TempIIDSeed, ndp.ep.nic.ID())
|
||||
if MaxDesyncFactor != 0 {
|
||||
ndp.temporaryAddressDesyncFactor = time.Duration(rand.Int63n(int64(MaxDesyncFactor)))
|
||||
}
|
||||
ndp.temporaryAddressDesyncFactor = time.Duration(ep.protocol.stack.Rand().Int63n(int64(MaxDesyncFactor)))
|
||||
}
|
||||
|
||||
func (ndp *ndpState) SendDADMessage(addr tcpip.Address, nonce []byte) tcpip.Error {
|
||||
@@ -1830,6 +1925,7 @@ func (e *endpoint) sendNDPNS(srcAddr, dstAddr, targetAddr tcpip.Address, remoteL
|
||||
ReserveHeaderBytes: int(e.MaxHeaderLength()),
|
||||
Data: buffer.View(icmp).ToVectorisedView(),
|
||||
})
|
||||
defer pkt.DecRef()
|
||||
|
||||
if err := addIPHeader(srcAddr, dstAddr, pkt, stack.NetworkHeaderParams{
|
||||
Protocol: header.ICMPv6ProtocolNumber,
|
||||
@@ -1839,7 +1935,7 @@ func (e *endpoint) sendNDPNS(srcAddr, dstAddr, targetAddr tcpip.Address, remoteL
|
||||
}
|
||||
|
||||
sent := e.stats.icmp.packetsSent
|
||||
err := e.nic.WritePacketToRemote(remoteLinkAddr, nil /* gso */, ProtocolNumber, pkt)
|
||||
err := e.nic.WritePacketToRemote(remoteLinkAddr, ProtocolNumber, pkt)
|
||||
if err != nil {
|
||||
sent.dropped.Increment()
|
||||
} else {
|
||||
|
||||
@@ -29,6 +29,10 @@ type Stats struct {
|
||||
|
||||
// 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.
|
||||
|
||||
+45
-29
@@ -17,14 +17,19 @@
|
||||
package ports
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/rand"
|
||||
"sync/atomic"
|
||||
|
||||
"inet.af/netstack/sync"
|
||||
"inet.af/netstack/tcpip"
|
||||
"inet.af/netstack/tcpip/header"
|
||||
)
|
||||
|
||||
const anyIPAddress tcpip.Address = ""
|
||||
const (
|
||||
firstEphemeral = 16000
|
||||
anyIPAddress tcpip.Address = ""
|
||||
)
|
||||
|
||||
// Reservation describes a port reservation.
|
||||
type Reservation struct {
|
||||
@@ -97,7 +102,7 @@ func (dc destToCounter) intersectionFlags(res Reservation) (BitFlags, int) {
|
||||
// Wildcard destinations affect all destinations for TupleOnly.
|
||||
if dest.addr == anyIPAddress || res.Dest.Addr == anyIPAddress {
|
||||
// Only bitwise and the TupleOnlyFlag.
|
||||
intersection &= ((^TupleOnlyFlag) | counter.SharedFlags())
|
||||
intersection &= (^TupleOnlyFlag) | counter.SharedFlags()
|
||||
count++
|
||||
}
|
||||
}
|
||||
@@ -118,7 +123,7 @@ type deviceToDest map[tcpip.NICID]destToCounter
|
||||
// If either of the port reuse flags is enabled on any of the nodes, all nodes
|
||||
// sharing a port must share at least one reuse flag. This matches Linux's
|
||||
// behavior.
|
||||
func (dd deviceToDest) isAvailable(res Reservation) bool {
|
||||
func (dd deviceToDest) isAvailable(res Reservation, portSpecified bool) bool {
|
||||
flagBits := res.Flags.Bits()
|
||||
if res.BindToDevice == 0 {
|
||||
intersection := FlagMask
|
||||
@@ -134,6 +139,9 @@ func (dd deviceToDest) isAvailable(res Reservation) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if !portSpecified && res.Transport == header.TCPProtocolNumber {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -142,16 +150,26 @@ func (dd deviceToDest) isAvailable(res Reservation) bool {
|
||||
if dests, ok := dd[0]; ok {
|
||||
var count int
|
||||
intersection, count = dests.intersectionFlags(res)
|
||||
if count > 0 && intersection&flagBits == 0 {
|
||||
return false
|
||||
if count > 0 {
|
||||
if intersection&flagBits == 0 {
|
||||
return false
|
||||
}
|
||||
if !portSpecified && res.Transport == header.TCPProtocolNumber {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if dests, ok := dd[res.BindToDevice]; ok {
|
||||
flags, count := dests.intersectionFlags(res)
|
||||
intersection &= flags
|
||||
if count > 0 && intersection&flagBits == 0 {
|
||||
return false
|
||||
if count > 0 {
|
||||
if intersection&flagBits == 0 {
|
||||
return false
|
||||
}
|
||||
if !portSpecified && res.Transport == header.TCPProtocolNumber {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,12 +182,12 @@ type addrToDevice map[tcpip.Address]deviceToDest
|
||||
// isAvailable checks whether an IP address is available to bind to. If the
|
||||
// address is the "any" address, check all other addresses. Otherwise, just
|
||||
// check against the "any" address and the provided address.
|
||||
func (ad addrToDevice) isAvailable(res Reservation) bool {
|
||||
func (ad addrToDevice) isAvailable(res Reservation, portSpecified bool) bool {
|
||||
if res.Addr == anyIPAddress {
|
||||
// If binding to the "any" address then check that there are no
|
||||
// conflicts with all addresses.
|
||||
for _, devices := range ad {
|
||||
if !devices.isAvailable(res) {
|
||||
if !devices.isAvailable(res, portSpecified) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -178,14 +196,14 @@ func (ad addrToDevice) isAvailable(res Reservation) bool {
|
||||
|
||||
// Check that there is no conflict with the "any" address.
|
||||
if devices, ok := ad[anyIPAddress]; ok {
|
||||
if !devices.isAvailable(res) {
|
||||
if !devices.isAvailable(res, portSpecified) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Check that this is no conflict with the provided address.
|
||||
if devices, ok := ad[res.Addr]; ok {
|
||||
if !devices.isAvailable(res) {
|
||||
if !devices.isAvailable(res, portSpecified) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -220,10 +238,8 @@ type PortManager struct {
|
||||
func NewPortManager() *PortManager {
|
||||
return &PortManager{
|
||||
allocatedPorts: make(map[portDescriptor]addrToDevice),
|
||||
// Match Linux's default ephemeral range. See:
|
||||
// https://github.com/torvalds/linux/blob/e54937963fa249595824439dc839c948188dea83/net/ipv4/af_inet.c#L1842
|
||||
firstEphemeral: 32768,
|
||||
numEphemeral: 28232,
|
||||
firstEphemeral: firstEphemeral,
|
||||
numEphemeral: math.MaxUint16 - firstEphemeral + 1,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,19 +252,19 @@ type PortTester func(port uint16) (good bool, err tcpip.Error)
|
||||
// possible ephemeral ports, allowing the caller to decide whether a given port
|
||||
// is suitable for its needs, and stopping when a port is found or an error
|
||||
// occurs.
|
||||
func (pm *PortManager) PickEphemeralPort(testPort PortTester) (port uint16, err tcpip.Error) {
|
||||
func (pm *PortManager) PickEphemeralPort(rng *rand.Rand, testPort PortTester) (port uint16, err tcpip.Error) {
|
||||
pm.ephemeralMu.RLock()
|
||||
firstEphemeral := pm.firstEphemeral
|
||||
numEphemeral := pm.numEphemeral
|
||||
pm.ephemeralMu.RUnlock()
|
||||
|
||||
offset := uint16(rand.Int31n(int32(numEphemeral)))
|
||||
offset := uint32(rng.Int31n(int32(numEphemeral)))
|
||||
return pickEphemeralPort(offset, firstEphemeral, numEphemeral, testPort)
|
||||
}
|
||||
|
||||
// portHint atomically reads and returns the pm.hint value.
|
||||
func (pm *PortManager) portHint() uint16 {
|
||||
return uint16(atomic.LoadUint32(&pm.hint))
|
||||
func (pm *PortManager) portHint() uint32 {
|
||||
return atomic.LoadUint32(&pm.hint)
|
||||
}
|
||||
|
||||
// incPortHint atomically increments pm.hint by 1.
|
||||
@@ -260,7 +276,7 @@ func (pm *PortManager) incPortHint() {
|
||||
// iterates over all ephemeral ports, allowing the caller to decide whether a
|
||||
// given port is suitable for its needs and stopping when a port is found or an
|
||||
// error occurs.
|
||||
func (pm *PortManager) PickEphemeralPortStable(offset uint16, testPort PortTester) (port uint16, err tcpip.Error) {
|
||||
func (pm *PortManager) PickEphemeralPortStable(offset uint32, testPort PortTester) (port uint16, err tcpip.Error) {
|
||||
pm.ephemeralMu.RLock()
|
||||
firstEphemeral := pm.firstEphemeral
|
||||
numEphemeral := pm.numEphemeral
|
||||
@@ -277,9 +293,9 @@ func (pm *PortManager) PickEphemeralPortStable(offset uint16, testPort PortTeste
|
||||
// and iterates over the number of ports specified by count and allows the
|
||||
// caller to decide whether a given port is suitable for its needs, and stopping
|
||||
// when a port is found or an error occurs.
|
||||
func pickEphemeralPort(offset, first, count uint16, testPort PortTester) (port uint16, err tcpip.Error) {
|
||||
for i := uint16(0); i < count; i++ {
|
||||
port = first + (offset+i)%count
|
||||
func pickEphemeralPort(offset uint32, first, count uint16, testPort PortTester) (port uint16, err tcpip.Error) {
|
||||
for i := uint32(0); i < uint32(count); i++ {
|
||||
port := uint16(uint32(first) + (offset+i)%uint32(count))
|
||||
ok, err := testPort(port)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@@ -301,14 +317,14 @@ func pickEphemeralPort(offset, first, count uint16, testPort PortTester) (port u
|
||||
// An optional PortTester can be passed in which if provided will be used to
|
||||
// test if the picked port can be used. The function should return true if the
|
||||
// port is safe to use, false otherwise.
|
||||
func (pm *PortManager) ReservePort(res Reservation, testPort PortTester) (reservedPort uint16, err tcpip.Error) {
|
||||
func (pm *PortManager) ReservePort(rng *rand.Rand, res Reservation, testPort PortTester) (reservedPort uint16, err tcpip.Error) {
|
||||
pm.mu.Lock()
|
||||
defer pm.mu.Unlock()
|
||||
|
||||
// If a port is specified, just try to reserve it for all network
|
||||
// protocols.
|
||||
if res.Port != 0 {
|
||||
if !pm.reserveSpecificPortLocked(res) {
|
||||
if !pm.reserveSpecificPortLocked(res, true /* portSpecified */) {
|
||||
return 0, &tcpip.ErrPortInUse{}
|
||||
}
|
||||
if testPort != nil {
|
||||
@@ -326,9 +342,9 @@ func (pm *PortManager) ReservePort(res Reservation, testPort PortTester) (reserv
|
||||
}
|
||||
|
||||
// A port wasn't specified, so try to find one.
|
||||
return pm.PickEphemeralPort(func(p uint16) (bool, tcpip.Error) {
|
||||
return pm.PickEphemeralPort(rng, func(p uint16) (bool, tcpip.Error) {
|
||||
res.Port = p
|
||||
if !pm.reserveSpecificPortLocked(res) {
|
||||
if !pm.reserveSpecificPortLocked(res, false /* portSpecified */) {
|
||||
return false, nil
|
||||
}
|
||||
if testPort != nil {
|
||||
@@ -348,12 +364,12 @@ func (pm *PortManager) ReservePort(res Reservation, testPort PortTester) (reserv
|
||||
|
||||
// reserveSpecificPortLocked tries to reserve the given port on all given
|
||||
// protocols.
|
||||
func (pm *PortManager) reserveSpecificPortLocked(res Reservation) bool {
|
||||
func (pm *PortManager) reserveSpecificPortLocked(res Reservation, portSpecified bool) bool {
|
||||
// Make sure the port is available.
|
||||
for _, network := range res.Networks {
|
||||
desc := portDescriptor{network, res.Transport, res.Port}
|
||||
if addrs, ok := pm.allocatedPorts[desc]; ok {
|
||||
if !addrs.isAvailable(res) {
|
||||
if !addrs.isAvailable(res, portSpecified) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ func (f *Flags) StateFields() []string {
|
||||
|
||||
func (f *Flags) beforeSave() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (f *Flags) StateSave(stateSinkObject state.Sink) {
|
||||
f.beforeSave()
|
||||
stateSinkObject.Save(0, &f.MostRecent)
|
||||
@@ -29,6 +30,7 @@ func (f *Flags) StateSave(stateSinkObject state.Sink) {
|
||||
|
||||
func (f *Flags) afterLoad() {}
|
||||
|
||||
// +checklocksignore
|
||||
func (f *Flags) StateLoad(stateSourceObject state.Source) {
|
||||
stateSourceObject.Load(0, &f.MostRecent)
|
||||
stateSourceObject.Load(1, &f.LoadBalanced)
|
||||
|
||||
+80
-38
@@ -15,16 +15,12 @@
|
||||
package tcpip
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sync/atomic"
|
||||
|
||||
"inet.af/netstack/atomicbitops"
|
||||
"inet.af/netstack/sync"
|
||||
)
|
||||
|
||||
// PacketOverheadFactor is used to multiply the value provided by the user on a
|
||||
// SetSockOpt for setting the send/receive buffer sizes sockets.
|
||||
const PacketOverheadFactor = 2
|
||||
|
||||
// SocketOptionsHandler holds methods that help define endpoint specific
|
||||
// behavior for socket level socket options. These must be implemented by
|
||||
// endpoints to get notified when socket level options are set.
|
||||
@@ -58,6 +54,14 @@ type SocketOptionsHandler interface {
|
||||
// changed. The handler is invoked with the new value for the socket send
|
||||
// buffer size. It also returns the newly set value.
|
||||
OnSetSendBufferSize(v int64) (newSz int64)
|
||||
|
||||
// OnSetReceiveBufferSize is invoked by SO_RCVBUF and SO_RCVBUFFORCE.
|
||||
OnSetReceiveBufferSize(v, oldSz int64) (newSz int64)
|
||||
|
||||
// WakeupWriters is invoked when the send buffer size for an endpoint is
|
||||
// changed. The handler notifies the writers if the send buffer size is
|
||||
// increased with setsockopt(2) for TCP endpoints.
|
||||
WakeupWriters()
|
||||
}
|
||||
|
||||
// DefaultSocketOptionsHandler is an embeddable type that implements no-op
|
||||
@@ -99,6 +103,14 @@ func (*DefaultSocketOptionsHandler) OnSetSendBufferSize(v int64) (newSz int64) {
|
||||
return v
|
||||
}
|
||||
|
||||
// WakeupWriters implements SocketOptionsHandler.WakeupWriters.
|
||||
func (*DefaultSocketOptionsHandler) WakeupWriters() {}
|
||||
|
||||
// OnSetReceiveBufferSize implements SocketOptionsHandler.OnSetReceiveBufferSize.
|
||||
func (*DefaultSocketOptionsHandler) OnSetReceiveBufferSize(v, oldSz int64) (newSz int64) {
|
||||
return v
|
||||
}
|
||||
|
||||
// StackHandler holds methods to access the stack options. These must be
|
||||
// implemented by the stack.
|
||||
type StackHandler interface {
|
||||
@@ -158,10 +170,14 @@ type SocketOptions struct {
|
||||
// message is passed with incoming packets.
|
||||
receiveTClassEnabled uint32
|
||||
|
||||
// receivePacketInfoEnabled is used to specify if more inforamtion is
|
||||
// provided with incoming packets such as interface index and address.
|
||||
// receivePacketInfoEnabled is used to specify if more information is
|
||||
// provided with incoming IPv4 packets.
|
||||
receivePacketInfoEnabled uint32
|
||||
|
||||
// receivePacketInfoEnabled is used to specify if more information is
|
||||
// provided with incoming IPv6 packets.
|
||||
receiveIPv6PacketInfoEnabled uint32
|
||||
|
||||
// hdrIncludeEnabled is used to indicate for a raw endpoint that all packets
|
||||
// being written have an IP header and the endpoint should not attach an IP
|
||||
// header.
|
||||
@@ -205,7 +221,15 @@ type SocketOptions struct {
|
||||
getSendBufferLimits GetSendBufferLimits `state:"manual"`
|
||||
|
||||
// sendBufferSize determines the send buffer size for this socket.
|
||||
sendBufferSize int64
|
||||
sendBufferSize atomicbitops.AlignedAtomicInt64
|
||||
|
||||
// getReceiveBufferLimits provides the handler to get the min, default and
|
||||
// max size for receive buffer. It is initialized at the creation time and
|
||||
// will not change.
|
||||
getReceiveBufferLimits GetReceiveBufferLimits `state:"manual"`
|
||||
|
||||
// receiveBufferSize determines the receive buffer size for this socket.
|
||||
receiveBufferSize atomicbitops.AlignedAtomicInt64
|
||||
|
||||
// mu protects the access to the below fields.
|
||||
mu sync.Mutex `state:"nosave"`
|
||||
@@ -217,10 +241,11 @@ type SocketOptions struct {
|
||||
|
||||
// InitHandler initializes the handler. This must be called before using the
|
||||
// socket options utility.
|
||||
func (so *SocketOptions) InitHandler(handler SocketOptionsHandler, stack StackHandler, getSendBufferLimits GetSendBufferLimits) {
|
||||
func (so *SocketOptions) InitHandler(handler SocketOptionsHandler, stack StackHandler, getSendBufferLimits GetSendBufferLimits, getReceiveBufferLimits GetReceiveBufferLimits) {
|
||||
so.handler = handler
|
||||
so.stackHandler = stack
|
||||
so.getSendBufferLimits = getSendBufferLimits
|
||||
so.getReceiveBufferLimits = getReceiveBufferLimits
|
||||
}
|
||||
|
||||
func storeAtomicBool(addr *uint32, v bool) {
|
||||
@@ -339,6 +364,16 @@ func (so *SocketOptions) SetReceivePacketInfo(v bool) {
|
||||
storeAtomicBool(&so.receivePacketInfoEnabled, v)
|
||||
}
|
||||
|
||||
// GetIPv6ReceivePacketInfo gets value for IPV6_RECVPKTINFO option.
|
||||
func (so *SocketOptions) GetIPv6ReceivePacketInfo() bool {
|
||||
return atomic.LoadUint32(&so.receiveIPv6PacketInfoEnabled) != 0
|
||||
}
|
||||
|
||||
// SetIPv6ReceivePacketInfo sets value for IPV6_RECVPKTINFO option.
|
||||
func (so *SocketOptions) SetIPv6ReceivePacketInfo(v bool) {
|
||||
storeAtomicBool(&so.receiveIPv6PacketInfoEnabled, v)
|
||||
}
|
||||
|
||||
// GetHeaderIncluded gets value for IP_HDRINCL option.
|
||||
func (so *SocketOptions) GetHeaderIncluded() bool {
|
||||
return atomic.LoadUint32(&so.hdrIncludedEnabled) != 0
|
||||
@@ -583,9 +618,10 @@ func (so *SocketOptions) GetBindToDevice() int32 {
|
||||
return atomic.LoadInt32(&so.bindToDevice)
|
||||
}
|
||||
|
||||
// SetBindToDevice sets value for SO_BINDTODEVICE option.
|
||||
// SetBindToDevice sets value for SO_BINDTODEVICE option. If bindToDevice is
|
||||
// zero, the socket device binding is removed.
|
||||
func (so *SocketOptions) SetBindToDevice(bindToDevice int32) Error {
|
||||
if !so.handler.HasNIC(bindToDevice) {
|
||||
if bindToDevice != 0 && !so.handler.HasNIC(bindToDevice) {
|
||||
return &ErrUnknownDevice{}
|
||||
}
|
||||
|
||||
@@ -595,40 +631,46 @@ func (so *SocketOptions) SetBindToDevice(bindToDevice int32) Error {
|
||||
|
||||
// GetSendBufferSize gets value for SO_SNDBUF option.
|
||||
func (so *SocketOptions) GetSendBufferSize() int64 {
|
||||
return atomic.LoadInt64(&so.sendBufferSize)
|
||||
return so.sendBufferSize.Load()
|
||||
}
|
||||
|
||||
// SendBufferLimits returns the [min, max) range of allowable send buffer
|
||||
// sizes.
|
||||
func (so *SocketOptions) SendBufferLimits() (min, max int64) {
|
||||
limits := so.getSendBufferLimits(so.stackHandler)
|
||||
return int64(limits.Min), int64(limits.Max)
|
||||
}
|
||||
|
||||
// SetSendBufferSize sets value for SO_SNDBUF option. notify indicates if the
|
||||
// stack handler should be invoked to set the send buffer size.
|
||||
func (so *SocketOptions) SetSendBufferSize(sendBufferSize int64, notify bool) {
|
||||
v := sendBufferSize
|
||||
|
||||
if !notify {
|
||||
atomic.StoreInt64(&so.sendBufferSize, v)
|
||||
return
|
||||
if notify {
|
||||
sendBufferSize = so.handler.OnSetSendBufferSize(sendBufferSize)
|
||||
}
|
||||
|
||||
// Make sure the send buffer size is within the min and max
|
||||
// allowed.
|
||||
ss := so.getSendBufferLimits(so.stackHandler)
|
||||
min := int64(ss.Min)
|
||||
max := int64(ss.Max)
|
||||
// Validate the send buffer size with min and max values.
|
||||
// Multiply it by factor of 2.
|
||||
if v > max {
|
||||
v = max
|
||||
so.sendBufferSize.Store(sendBufferSize)
|
||||
if notify {
|
||||
so.handler.WakeupWriters()
|
||||
}
|
||||
}
|
||||
|
||||
if v < math.MaxInt32/PacketOverheadFactor {
|
||||
v *= PacketOverheadFactor
|
||||
if v < min {
|
||||
v = min
|
||||
}
|
||||
} else {
|
||||
v = math.MaxInt32
|
||||
// GetReceiveBufferSize gets value for SO_RCVBUF option.
|
||||
func (so *SocketOptions) GetReceiveBufferSize() int64 {
|
||||
return so.receiveBufferSize.Load()
|
||||
}
|
||||
|
||||
// ReceiveBufferLimits returns the [min, max) range of allowable receive buffer
|
||||
// sizes.
|
||||
func (so *SocketOptions) ReceiveBufferLimits() (min, max int64) {
|
||||
limits := so.getReceiveBufferLimits(so.stackHandler)
|
||||
return int64(limits.Min), int64(limits.Max)
|
||||
}
|
||||
|
||||
// SetReceiveBufferSize sets the value of the SO_RCVBUF option, optionally
|
||||
// notifying the owning endpoint.
|
||||
func (so *SocketOptions) SetReceiveBufferSize(receiveBufferSize int64, notify bool) {
|
||||
if notify {
|
||||
oldSz := so.receiveBufferSize.Load()
|
||||
receiveBufferSize = so.handler.OnSetReceiveBufferSize(receiveBufferSize, oldSz)
|
||||
}
|
||||
|
||||
// Notify endpoint about change in buffer size.
|
||||
newSz := so.handler.OnSetSendBufferSize(v)
|
||||
atomic.StoreInt64(&so.sendBufferSize, newSz)
|
||||
so.receiveBufferSize.Store(receiveBufferSize)
|
||||
}
|
||||
@@ -117,10 +117,10 @@ func (a *AddressableEndpointState) releaseAddressStateLocked(addrState *addressS
|
||||
}
|
||||
|
||||
// AddAndAcquirePermanentAddress implements AddressableEndpoint.
|
||||
func (a *AddressableEndpointState) AddAndAcquirePermanentAddress(addr tcpip.AddressWithPrefix, peb PrimaryEndpointBehavior, configType AddressConfigType, deprecated bool) (AddressEndpoint, tcpip.Error) {
|
||||
func (a *AddressableEndpointState) AddAndAcquirePermanentAddress(addr tcpip.AddressWithPrefix, properties AddressProperties) (AddressEndpoint, tcpip.Error) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
ep, err := a.addAndAcquireAddressLocked(addr, peb, configType, deprecated, true /* permanent */)
|
||||
ep, err := a.addAndAcquireAddressLocked(addr, properties, true /* permanent */)
|
||||
// From https://golang.org/doc/faq#nil_error:
|
||||
//
|
||||
// Under the covers, interfaces are implemented as two elements, a type T and
|
||||
@@ -149,7 +149,7 @@ func (a *AddressableEndpointState) AddAndAcquirePermanentAddress(addr tcpip.Addr
|
||||
func (a *AddressableEndpointState) AddAndAcquireTemporaryAddress(addr tcpip.AddressWithPrefix, peb PrimaryEndpointBehavior) (AddressEndpoint, tcpip.Error) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
ep, err := a.addAndAcquireAddressLocked(addr, peb, AddressConfigStatic, false /* deprecated */, false /* permanent */)
|
||||
ep, err := a.addAndAcquireAddressLocked(addr, AddressProperties{PEB: peb}, false /* permanent */)
|
||||
// From https://golang.org/doc/faq#nil_error:
|
||||
//
|
||||
// Under the covers, interfaces are implemented as two elements, a type T and
|
||||
@@ -180,7 +180,7 @@ func (a *AddressableEndpointState) AddAndAcquireTemporaryAddress(addr tcpip.Addr
|
||||
// returned, regardless the kind of address that is being added.
|
||||
//
|
||||
// Precondition: a.mu must be write locked.
|
||||
func (a *AddressableEndpointState) addAndAcquireAddressLocked(addr tcpip.AddressWithPrefix, peb PrimaryEndpointBehavior, configType AddressConfigType, deprecated, permanent bool) (*addressState, tcpip.Error) {
|
||||
func (a *AddressableEndpointState) addAndAcquireAddressLocked(addr tcpip.AddressWithPrefix, properties AddressProperties, permanent bool) (*addressState, tcpip.Error) {
|
||||
// attemptAddToPrimary is false when the address is already in the primary
|
||||
// address list.
|
||||
attemptAddToPrimary := true
|
||||
@@ -208,7 +208,7 @@ func (a *AddressableEndpointState) addAndAcquireAddressLocked(addr tcpip.Address
|
||||
// We now promote the address.
|
||||
for i, s := range a.mu.primary {
|
||||
if s == addrState {
|
||||
switch peb {
|
||||
switch properties.PEB {
|
||||
case CanBePrimaryEndpoint:
|
||||
// The address is already in the primary address list.
|
||||
attemptAddToPrimary = false
|
||||
@@ -222,7 +222,7 @@ func (a *AddressableEndpointState) addAndAcquireAddressLocked(addr tcpip.Address
|
||||
case NeverPrimaryEndpoint:
|
||||
a.mu.primary = append(a.mu.primary[:i], a.mu.primary[i+1:]...)
|
||||
default:
|
||||
panic(fmt.Sprintf("unrecognized primary endpoint behaviour = %d", peb))
|
||||
panic(fmt.Sprintf("unrecognized primary endpoint behaviour = %d", properties.PEB))
|
||||
}
|
||||
break
|
||||
}
|
||||
@@ -249,7 +249,7 @@ func (a *AddressableEndpointState) addAndAcquireAddressLocked(addr tcpip.Address
|
||||
// or we are adding a new temporary or permanent address.
|
||||
//
|
||||
// The address MUST be write locked at this point.
|
||||
defer addrState.mu.Unlock()
|
||||
defer addrState.mu.Unlock() // +checklocksforce
|
||||
|
||||
if permanent {
|
||||
if addrState.mu.kind.IsPermanent() {
|
||||
@@ -262,11 +262,11 @@ func (a *AddressableEndpointState) addAndAcquireAddressLocked(addr tcpip.Address
|
||||
}
|
||||
// Acquire the address before returning it.
|
||||
addrState.mu.refs++
|
||||
addrState.mu.deprecated = deprecated
|
||||
addrState.mu.configType = configType
|
||||
addrState.mu.deprecated = properties.Deprecated
|
||||
addrState.mu.configType = properties.ConfigType
|
||||
|
||||
if attemptAddToPrimary {
|
||||
switch peb {
|
||||
switch properties.PEB {
|
||||
case NeverPrimaryEndpoint:
|
||||
case CanBePrimaryEndpoint:
|
||||
a.mu.primary = append(a.mu.primary, addrState)
|
||||
@@ -285,7 +285,7 @@ func (a *AddressableEndpointState) addAndAcquireAddressLocked(addr tcpip.Address
|
||||
a.mu.primary[0] = addrState
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unrecognized primary endpoint behaviour = %d", peb))
|
||||
panic(fmt.Sprintf("unrecognized primary endpoint behaviour = %d", properties.PEB))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -440,41 +440,63 @@ func (a *AddressableEndpointState) acquirePrimaryAddressRLocked(isValid func(*ad
|
||||
// Regardless how the address was obtained, it will be acquired before it is
|
||||
// returned.
|
||||
func (a *AddressableEndpointState) AcquireAssignedAddressOrMatching(localAddr tcpip.Address, f func(AddressEndpoint) bool, allowTemp bool, tempPEB PrimaryEndpointBehavior) AddressEndpoint {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
lookup := func() *addressState {
|
||||
if addrState, ok := a.mu.endpoints[localAddr]; ok {
|
||||
if !addrState.IsAssigned(allowTemp) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if addrState, ok := a.mu.endpoints[localAddr]; ok {
|
||||
if !addrState.IsAssigned(allowTemp) {
|
||||
return nil
|
||||
if !addrState.IncRef() {
|
||||
panic(fmt.Sprintf("failed to increase the reference count for address = %s", addrState.addr))
|
||||
}
|
||||
|
||||
return addrState
|
||||
}
|
||||
|
||||
if !addrState.IncRef() {
|
||||
panic(fmt.Sprintf("failed to increase the reference count for address = %s", addrState.addr))
|
||||
}
|
||||
|
||||
return addrState
|
||||
}
|
||||
|
||||
if f != nil {
|
||||
for _, addrState := range a.mu.endpoints {
|
||||
if addrState.IsAssigned(allowTemp) && f(addrState) && addrState.IncRef() {
|
||||
return addrState
|
||||
if f != nil {
|
||||
for _, addrState := range a.mu.endpoints {
|
||||
if addrState.IsAssigned(allowTemp) && f(addrState) && addrState.IncRef() {
|
||||
return addrState
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// Avoid exclusive lock on mu unless we need to add a new address.
|
||||
a.mu.RLock()
|
||||
ep := lookup()
|
||||
a.mu.RUnlock()
|
||||
|
||||
if ep != nil {
|
||||
return ep
|
||||
}
|
||||
|
||||
if !allowTemp {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Acquire state lock in exclusive mode as we need to add a new temporary
|
||||
// endpoint.
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
// Do the lookup again in case another goroutine added the address in the time
|
||||
// we released and acquired the lock.
|
||||
ep = lookup()
|
||||
if ep != nil {
|
||||
return ep
|
||||
}
|
||||
|
||||
// Proceed to add a new temporary endpoint.
|
||||
addr := localAddr.WithPrefix()
|
||||
ep, err := a.addAndAcquireAddressLocked(addr, tempPEB, AddressConfigStatic, false /* deprecated */, false /* permanent */)
|
||||
ep, err := a.addAndAcquireAddressLocked(addr, AddressProperties{PEB: tempPEB}, false /* permanent */)
|
||||
if err != nil {
|
||||
// addAndAcquireAddressLocked only returns an error if the address is
|
||||
// already assigned but we just checked above if the address exists so we
|
||||
// expect no error.
|
||||
panic(fmt.Sprintf("a.addAndAcquireAddressLocked(%s, %d, %d, false, false): %s", addr, tempPEB, AddressConfigStatic, err))
|
||||
panic(fmt.Sprintf("a.addAndAcquireAddressLocked(%s, AddressProperties{PEB: %s}, false): %s", addr, tempPEB, err))
|
||||
}
|
||||
|
||||
// From https://golang.org/doc/faq#nil_error:
|
||||
//
|
||||
// Under the covers, interfaces are implemented as two elements, a type T and
|
||||
|
||||
+708
-349
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,41 @@
|
||||
// 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.
|
||||
|
||||
// Code generated by "stringer -type Hook ."; DO NOT EDIT.
|
||||
|
||||
package stack
|
||||
|
||||
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[Prerouting-0]
|
||||
_ = x[Input-1]
|
||||
_ = x[Forward-2]
|
||||
_ = x[Output-3]
|
||||
_ = x[Postrouting-4]
|
||||
_ = x[NumHooks-5]
|
||||
}
|
||||
|
||||
const _Hook_name = "PreroutingInputForwardOutputPostroutingNumHooks"
|
||||
|
||||
var _Hook_index = [...]uint8{0, 10, 15, 22, 28, 39, 47}
|
||||
|
||||
func (i Hook) String() string {
|
||||
if i >= Hook(len(_Hook_index)-1) {
|
||||
return "Hook(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _Hook_name[_Hook_index[i]:_Hook_index[i+1]]
|
||||
}
|
||||
+35
-4
@@ -16,6 +16,7 @@ package stack
|
||||
|
||||
import (
|
||||
"golang.org/x/time/rate"
|
||||
"inet.af/netstack/tcpip"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -31,11 +32,41 @@ const (
|
||||
// ICMPRateLimiter is a global rate limiter that controls the generation of
|
||||
// ICMP messages generated by the stack.
|
||||
type ICMPRateLimiter struct {
|
||||
*rate.Limiter
|
||||
limiter *rate.Limiter
|
||||
clock tcpip.Clock
|
||||
}
|
||||
|
||||
// NewICMPRateLimiter returns a global rate limiter for controlling the rate
|
||||
// at which ICMP messages are generated by the stack.
|
||||
func NewICMPRateLimiter() *ICMPRateLimiter {
|
||||
return &ICMPRateLimiter{Limiter: rate.NewLimiter(icmpLimit, icmpBurst)}
|
||||
// at which ICMP messages are generated by the stack. The returned limiter
|
||||
// does not apply limits to any ICMP types by default.
|
||||
func NewICMPRateLimiter(clock tcpip.Clock) *ICMPRateLimiter {
|
||||
return &ICMPRateLimiter{
|
||||
clock: clock,
|
||||
limiter: rate.NewLimiter(icmpLimit, icmpBurst),
|
||||
}
|
||||
}
|
||||
|
||||
// SetLimit sets a new Limit for the limiter.
|
||||
func (l *ICMPRateLimiter) SetLimit(limit rate.Limit) {
|
||||
l.limiter.SetLimitAt(l.clock.Now(), limit)
|
||||
}
|
||||
|
||||
// Limit returns the maximum overall event rate.
|
||||
func (l *ICMPRateLimiter) Limit() rate.Limit {
|
||||
return l.limiter.Limit()
|
||||
}
|
||||
|
||||
// SetBurst sets a new burst size for the limiter.
|
||||
func (l *ICMPRateLimiter) SetBurst(burst int) {
|
||||
l.limiter.SetBurstAt(l.clock.Now(), burst)
|
||||
}
|
||||
|
||||
// Burst returns the maximum burst size.
|
||||
func (l *ICMPRateLimiter) Burst() int {
|
||||
return l.limiter.Burst()
|
||||
}
|
||||
|
||||
// Allow reports whether one ICMP message may be sent now.
|
||||
func (l *ICMPRateLimiter) Allow() bool {
|
||||
return l.limiter.AllowN(l.clock.Now(), 1)
|
||||
}
|
||||
+286
-102
@@ -16,6 +16,7 @@ package stack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"inet.af/netstack/tcpip"
|
||||
@@ -42,7 +43,7 @@ const reaperDelay = 5 * time.Second
|
||||
|
||||
// DefaultTables returns a default set of tables. Each chain is set to accept
|
||||
// all packets.
|
||||
func DefaultTables() *IPTables {
|
||||
func DefaultTables(clock tcpip.Clock, rand *rand.Rand) *IPTables {
|
||||
return &IPTables{
|
||||
v4Tables: [NumTables]Table{
|
||||
NATID: {
|
||||
@@ -174,13 +175,10 @@ func DefaultTables() *IPTables {
|
||||
},
|
||||
},
|
||||
},
|
||||
priorities: [NumHooks][]TableID{
|
||||
Prerouting: {MangleID, NATID},
|
||||
Input: {NATID, FilterID},
|
||||
Output: {MangleID, NATID, FilterID},
|
||||
},
|
||||
connections: ConnTrack{
|
||||
seed: generateRandUint32(),
|
||||
seed: rand.Uint32(),
|
||||
clock: clock,
|
||||
rand: rand,
|
||||
},
|
||||
reaperDone: make(chan struct{}, 1),
|
||||
}
|
||||
@@ -254,7 +252,7 @@ const (
|
||||
// chainAccept indicates the packet should continue through netstack.
|
||||
chainAccept chainVerdict = iota
|
||||
|
||||
// chainAccept indicates the packet should be dropped.
|
||||
// chainDrop indicates the packet should be dropped.
|
||||
chainDrop
|
||||
|
||||
// chainReturn indicates the packet should return to the calling chain
|
||||
@@ -262,91 +260,260 @@ const (
|
||||
chainReturn
|
||||
)
|
||||
|
||||
// Check runs pkt through the rules for hook. It returns true when the packet
|
||||
// should continue traversing the network stack and false when it should be
|
||||
// dropped.
|
||||
// CheckPrerouting performs the prerouting hook on the packet.
|
||||
//
|
||||
// TODO(gvisor.dev/issue/170): PacketBuffer should hold the GSO and route, from
|
||||
// which address can be gathered. Currently, address is only needed for
|
||||
// prerouting.
|
||||
// Returns true iff the packet may continue traversing the stack; the packet
|
||||
// must be dropped if false is returned.
|
||||
//
|
||||
// Precondition: pkt.NetworkHeader is set.
|
||||
func (it *IPTables) Check(hook Hook, pkt *PacketBuffer, gso *GSO, r *Route, preroutingAddr tcpip.Address, inNicName, outNicName string) bool {
|
||||
if pkt.NetworkProtocolNumber != header.IPv4ProtocolNumber && pkt.NetworkProtocolNumber != header.IPv6ProtocolNumber {
|
||||
return true
|
||||
}
|
||||
// Many users never configure iptables. Spare them the cost of rule
|
||||
// traversal if rules have never been set.
|
||||
// Precondition: The packet's network and transport header must be set.
|
||||
func (it *IPTables) CheckPrerouting(pkt *PacketBuffer, addressEP AddressableEndpoint, inNicName string) bool {
|
||||
it.mu.RLock()
|
||||
defer it.mu.RUnlock()
|
||||
if !it.modified {
|
||||
|
||||
if it.shouldSkipRLocked(pkt.NetworkProtocolNumber) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Packets are manipulated only if connection and matching
|
||||
// NAT rule exists.
|
||||
shouldTrack := it.connections.handlePacket(pkt, hook, gso, r)
|
||||
pkt.tuple = it.connections.getConnAndUpdate(pkt)
|
||||
|
||||
// Go through each table containing the hook.
|
||||
priorities := it.priorities[hook]
|
||||
for _, tableID := range priorities {
|
||||
// If handlePacket already NATed the packet, we don't need to
|
||||
// check the NAT table.
|
||||
if tableID == NATID && pkt.NatDone {
|
||||
continue
|
||||
}
|
||||
var table Table
|
||||
if pkt.NetworkProtocolNumber == header.IPv6ProtocolNumber {
|
||||
table = it.v6Tables[tableID]
|
||||
} else {
|
||||
table = it.v4Tables[tableID]
|
||||
}
|
||||
ruleIdx := table.BuiltinChains[hook]
|
||||
switch verdict := it.checkChain(hook, pkt, table, ruleIdx, gso, r, preroutingAddr, inNicName, outNicName); verdict {
|
||||
// If the table returns Accept, move on to the next table.
|
||||
case chainAccept:
|
||||
continue
|
||||
// The Drop verdict is final.
|
||||
case chainDrop:
|
||||
for _, check := range [...]checkTableFn{
|
||||
it.checkMangleRLocked,
|
||||
it.checkNATRLocked,
|
||||
} {
|
||||
if !check(Prerouting, pkt, nil /* route */, addressEP, inNicName, "" /* outNicName */) {
|
||||
return false
|
||||
case chainReturn:
|
||||
// Any Return from a built-in chain means we have to
|
||||
// call the underflow.
|
||||
underflow := table.Rules[table.Underflows[hook]]
|
||||
switch v, _ := underflow.Target.Action(pkt, &it.connections, hook, gso, r, preroutingAddr); v {
|
||||
case RuleAccept:
|
||||
continue
|
||||
case RuleDrop:
|
||||
return false
|
||||
case RuleJump, RuleReturn:
|
||||
panic("Underflows should only return RuleAccept or RuleDrop.")
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown verdict: %d", v))
|
||||
}
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown verdict %v.", verdict))
|
||||
}
|
||||
}
|
||||
|
||||
// If this connection should be tracked, try to add an entry for it. If
|
||||
// traversing the nat table didn't end in adding an entry,
|
||||
// maybeInsertNoop will add a no-op entry for the connection. This is
|
||||
// needeed when establishing connections so that the SYN/ACK reply to an
|
||||
// outgoing SYN is delivered to the correct endpoint rather than being
|
||||
// redirected by a prerouting rule.
|
||||
//
|
||||
// From the iptables documentation: "If there is no rule, a `null'
|
||||
// binding is created: this usually does not map the packet, but exists
|
||||
// to ensure we don't map another stream over an existing one."
|
||||
if shouldTrack {
|
||||
it.connections.maybeInsertNoop(pkt, hook)
|
||||
return true
|
||||
}
|
||||
|
||||
// CheckInput performs the input hook on the packet.
|
||||
//
|
||||
// Returns true iff the packet may continue traversing the stack; the packet
|
||||
// must be dropped if false is returned.
|
||||
//
|
||||
// Precondition: The packet's network and transport header must be set.
|
||||
func (it *IPTables) CheckInput(pkt *PacketBuffer, inNicName string) bool {
|
||||
it.mu.RLock()
|
||||
defer it.mu.RUnlock()
|
||||
|
||||
if it.shouldSkipRLocked(pkt.NetworkProtocolNumber) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Every table returned Accept.
|
||||
for _, check := range [...]checkTableFn{
|
||||
it.checkNATRLocked,
|
||||
it.checkFilterRLocked,
|
||||
} {
|
||||
if !check(Input, pkt, nil /* route */, nil /* addressEP */, inNicName, "" /* outNicName */) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if t := pkt.tuple; t != nil {
|
||||
pkt.tuple = nil
|
||||
return t.conn.finalize()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// CheckForward performs the forward hook on the packet.
|
||||
//
|
||||
// Returns true iff the packet may continue traversing the stack; the packet
|
||||
// must be dropped if false is returned.
|
||||
//
|
||||
// Precondition: The packet's network and transport header must be set.
|
||||
func (it *IPTables) CheckForward(pkt *PacketBuffer, inNicName, outNicName string) bool {
|
||||
it.mu.RLock()
|
||||
defer it.mu.RUnlock()
|
||||
|
||||
if it.shouldSkipRLocked(pkt.NetworkProtocolNumber) {
|
||||
return true
|
||||
}
|
||||
|
||||
return it.checkFilterRLocked(Forward, pkt, nil /* route */, nil /* addressEP */, inNicName, outNicName)
|
||||
}
|
||||
|
||||
// CheckOutput performs the output hook on the packet.
|
||||
//
|
||||
// Returns true iff the packet may continue traversing the stack; the packet
|
||||
// must be dropped if false is returned.
|
||||
//
|
||||
// Precondition: The packet's network and transport header must be set.
|
||||
func (it *IPTables) CheckOutput(pkt *PacketBuffer, r *Route, outNicName string) bool {
|
||||
it.mu.RLock()
|
||||
defer it.mu.RUnlock()
|
||||
|
||||
if it.shouldSkipRLocked(pkt.NetworkProtocolNumber) {
|
||||
return true
|
||||
}
|
||||
|
||||
pkt.tuple = it.connections.getConnAndUpdate(pkt)
|
||||
|
||||
for _, check := range [...]checkTableFn{
|
||||
it.checkMangleRLocked,
|
||||
it.checkNATRLocked,
|
||||
it.checkFilterRLocked,
|
||||
} {
|
||||
if !check(Output, pkt, r, nil /* addressEP */, "" /* inNicName */, outNicName) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// CheckPostrouting performs the postrouting hook on the packet.
|
||||
//
|
||||
// Returns true iff the packet may continue traversing the stack; the packet
|
||||
// must be dropped if false is returned.
|
||||
//
|
||||
// Precondition: The packet's network and transport header must be set.
|
||||
func (it *IPTables) CheckPostrouting(pkt *PacketBuffer, r *Route, addressEP AddressableEndpoint, outNicName string) bool {
|
||||
it.mu.RLock()
|
||||
defer it.mu.RUnlock()
|
||||
|
||||
if it.shouldSkipRLocked(pkt.NetworkProtocolNumber) {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, check := range [...]checkTableFn{
|
||||
it.checkMangleRLocked,
|
||||
it.checkNATRLocked,
|
||||
} {
|
||||
if !check(Postrouting, pkt, r, addressEP, "" /* inNicName */, outNicName) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if t := pkt.tuple; t != nil {
|
||||
pkt.tuple = nil
|
||||
return t.conn.finalize()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// +checklocksread:it.mu
|
||||
func (it *IPTables) shouldSkipRLocked(netProto tcpip.NetworkProtocolNumber) bool {
|
||||
switch netProto {
|
||||
case header.IPv4ProtocolNumber, header.IPv6ProtocolNumber:
|
||||
default:
|
||||
// IPTables only supports IPv4/IPv6.
|
||||
return true
|
||||
}
|
||||
|
||||
// Many users never configure iptables. Spare them the cost of rule
|
||||
// traversal if rules have never been set.
|
||||
return !it.modified
|
||||
}
|
||||
|
||||
type checkTableFn func(hook Hook, pkt *PacketBuffer, r *Route, addressEP AddressableEndpoint, inNicName, outNicName string) bool
|
||||
|
||||
// checkMangleRLocked runs the packet through the mangle table.
|
||||
//
|
||||
// See checkRLocked.
|
||||
//
|
||||
// +checklocksread:it.mu
|
||||
func (it *IPTables) checkMangleRLocked(hook Hook, pkt *PacketBuffer, r *Route, addressEP AddressableEndpoint, inNicName, outNicName string) bool {
|
||||
return it.checkRLocked(MangleID, hook, pkt, r, addressEP, inNicName, outNicName)
|
||||
}
|
||||
|
||||
// checkNATRLocked runs the packet through the NAT table.
|
||||
//
|
||||
// See checkRLocked.
|
||||
//
|
||||
// +checklocksread:it.mu
|
||||
func (it *IPTables) checkNATRLocked(hook Hook, pkt *PacketBuffer, r *Route, addressEP AddressableEndpoint, inNicName, outNicName string) bool {
|
||||
t := pkt.tuple
|
||||
if t != nil && t.conn.handlePacket(pkt, hook, r) {
|
||||
return true
|
||||
}
|
||||
|
||||
if !it.checkRLocked(NATID, hook, pkt, r, addressEP, inNicName, outNicName) {
|
||||
return false
|
||||
}
|
||||
|
||||
if t == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
var dnat bool
|
||||
var natDone *bool
|
||||
switch hook {
|
||||
case Prerouting, Output:
|
||||
dnat = true
|
||||
natDone = &pkt.DNATDone
|
||||
case Input, Postrouting:
|
||||
dnat = false
|
||||
natDone = &pkt.SNATDone
|
||||
case Forward:
|
||||
panic("should not attempt NAT in forwarding")
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled hook = %d", hook))
|
||||
}
|
||||
|
||||
// Make sure the connection is NATed.
|
||||
//
|
||||
// If the packet was already NATed, the connection must be NATed.
|
||||
if !*natDone {
|
||||
t.conn.maybePerformNoopNAT(dnat)
|
||||
_ = t.conn.handlePacket(pkt, hook, r)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// checkFilterRLocked runs the packet through the filter table.
|
||||
//
|
||||
// See checkRLocked.
|
||||
//
|
||||
// +checklocksread:it.mu
|
||||
func (it *IPTables) checkFilterRLocked(hook Hook, pkt *PacketBuffer, r *Route, addressEP AddressableEndpoint, inNicName, outNicName string) bool {
|
||||
return it.checkRLocked(FilterID, hook, pkt, r, addressEP, inNicName, outNicName)
|
||||
}
|
||||
|
||||
// checkRLocked runs the packet through the rules in the specified table for the
|
||||
// hook. It returns true if the packet should continue to traverse through the
|
||||
// network stack or tables, or false when it must be dropped.
|
||||
//
|
||||
// Precondition: The packet's network and transport header must be set.
|
||||
//
|
||||
// +checklocksread:it.mu
|
||||
func (it *IPTables) checkRLocked(tableID TableID, hook Hook, pkt *PacketBuffer, r *Route, addressEP AddressableEndpoint, inNicName, outNicName string) bool {
|
||||
var table Table
|
||||
if pkt.NetworkProtocolNumber == header.IPv6ProtocolNumber {
|
||||
table = it.v6Tables[tableID]
|
||||
} else {
|
||||
table = it.v4Tables[tableID]
|
||||
}
|
||||
ruleIdx := table.BuiltinChains[hook]
|
||||
switch verdict := it.checkChain(hook, pkt, table, ruleIdx, r, addressEP, inNicName, outNicName); verdict {
|
||||
// If the table returns Accept, move on to the next table.
|
||||
case chainAccept:
|
||||
return true
|
||||
// The Drop verdict is final.
|
||||
case chainDrop:
|
||||
return false
|
||||
case chainReturn:
|
||||
// Any Return from a built-in chain means we have to
|
||||
// call the underflow.
|
||||
underflow := table.Rules[table.Underflows[hook]]
|
||||
switch v, _ := underflow.Target.Action(pkt, hook, r, addressEP); v {
|
||||
case RuleAccept:
|
||||
return true
|
||||
case RuleDrop:
|
||||
return false
|
||||
case RuleJump, RuleReturn:
|
||||
panic("Underflows should only return RuleAccept or RuleDrop.")
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown verdict: %d", v))
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("Unknown verdict %v.", verdict))
|
||||
}
|
||||
}
|
||||
|
||||
// beforeSave is invoked by stateify.
|
||||
func (it *IPTables) beforeSave() {
|
||||
// Ensure the reaper exits cleanly.
|
||||
@@ -369,6 +536,7 @@ func (it *IPTables) startReaper(interval time.Duration) {
|
||||
select {
|
||||
case <-it.reaperDone:
|
||||
return
|
||||
// TODO(gvisor.dev/issue/5939): do not use the ambient clock.
|
||||
case <-time.After(interval):
|
||||
bucket, interval = it.connections.reapUnused(bucket, interval)
|
||||
}
|
||||
@@ -376,30 +544,46 @@ func (it *IPTables) startReaper(interval time.Duration) {
|
||||
}()
|
||||
}
|
||||
|
||||
// CheckPackets runs pkts through the rules for hook and returns a map of packets that
|
||||
// should not go forward.
|
||||
// CheckOutputPackets performs the output hook on the packets.
|
||||
//
|
||||
// Preconditions:
|
||||
// * pkt is a IPv4 packet of at least length header.IPv4MinimumSize.
|
||||
// * pkt.NetworkHeader is not nil.
|
||||
// Returns a map of packets that must be dropped.
|
||||
//
|
||||
// NOTE: unlike the Check API the returned map contains packets that should be
|
||||
// dropped.
|
||||
func (it *IPTables) CheckPackets(hook Hook, pkts PacketBufferList, gso *GSO, r *Route, inNicName, outNicName string) (drop map[*PacketBuffer]struct{}, natPkts map[*PacketBuffer]struct{}) {
|
||||
// Precondition: The packets' network and transport header must be set.
|
||||
func (it *IPTables) CheckOutputPackets(pkts PacketBufferList, r *Route, outNicName string) (drop map[*PacketBuffer]struct{}, natPkts map[*PacketBuffer]struct{}) {
|
||||
return checkPackets(pkts, func(pkt *PacketBuffer) bool {
|
||||
return it.CheckOutput(pkt, r, outNicName)
|
||||
}, true /* dnat */)
|
||||
}
|
||||
|
||||
// CheckPostroutingPackets performs the postrouting hook on the packets.
|
||||
//
|
||||
// Returns a map of packets that must be dropped.
|
||||
//
|
||||
// Precondition: The packets' network and transport header must be set.
|
||||
func (it *IPTables) CheckPostroutingPackets(pkts PacketBufferList, r *Route, addressEP AddressableEndpoint, outNicName string) (drop map[*PacketBuffer]struct{}, natPkts map[*PacketBuffer]struct{}) {
|
||||
return checkPackets(pkts, func(pkt *PacketBuffer) bool {
|
||||
return it.CheckPostrouting(pkt, r, addressEP, outNicName)
|
||||
}, false /* dnat */)
|
||||
}
|
||||
|
||||
func checkPackets(pkts PacketBufferList, f func(*PacketBuffer) bool, dnat bool) (drop map[*PacketBuffer]struct{}, natPkts map[*PacketBuffer]struct{}) {
|
||||
for pkt := pkts.Front(); pkt != nil; pkt = pkt.Next() {
|
||||
if !pkt.NatDone {
|
||||
if ok := it.Check(hook, pkt, gso, r, "", inNicName, outNicName); !ok {
|
||||
if drop == nil {
|
||||
drop = make(map[*PacketBuffer]struct{})
|
||||
}
|
||||
drop[pkt] = struct{}{}
|
||||
natDone := &pkt.SNATDone
|
||||
if dnat {
|
||||
natDone = &pkt.DNATDone
|
||||
}
|
||||
|
||||
if ok := f(pkt); !ok {
|
||||
if drop == nil {
|
||||
drop = make(map[*PacketBuffer]struct{})
|
||||
}
|
||||
if pkt.NatDone {
|
||||
if natPkts == nil {
|
||||
natPkts = make(map[*PacketBuffer]struct{})
|
||||
}
|
||||
natPkts[pkt] = struct{}{}
|
||||
drop[pkt] = struct{}{}
|
||||
}
|
||||
if *natDone {
|
||||
if natPkts == nil {
|
||||
natPkts = make(map[*PacketBuffer]struct{})
|
||||
}
|
||||
natPkts[pkt] = struct{}{}
|
||||
}
|
||||
}
|
||||
return drop, natPkts
|
||||
@@ -408,11 +592,11 @@ func (it *IPTables) CheckPackets(hook Hook, pkts PacketBufferList, gso *GSO, r *
|
||||
// Preconditions:
|
||||
// * pkt is a IPv4 packet of at least length header.IPv4MinimumSize.
|
||||
// * pkt.NetworkHeader is not nil.
|
||||
func (it *IPTables) checkChain(hook Hook, pkt *PacketBuffer, table Table, ruleIdx int, gso *GSO, r *Route, preroutingAddr tcpip.Address, inNicName, outNicName string) chainVerdict {
|
||||
func (it *IPTables) checkChain(hook Hook, pkt *PacketBuffer, table Table, ruleIdx int, r *Route, addressEP AddressableEndpoint, inNicName, outNicName string) chainVerdict {
|
||||
// Start from ruleIdx and walk the list of rules until a rule gives us
|
||||
// a verdict.
|
||||
for ruleIdx < len(table.Rules) {
|
||||
switch verdict, jumpTo := it.checkRule(hook, pkt, table, ruleIdx, gso, r, preroutingAddr, inNicName, outNicName); verdict {
|
||||
switch verdict, jumpTo := it.checkRule(hook, pkt, table, ruleIdx, r, addressEP, inNicName, outNicName); verdict {
|
||||
case RuleAccept:
|
||||
return chainAccept
|
||||
|
||||
@@ -429,7 +613,7 @@ func (it *IPTables) checkChain(hook Hook, pkt *PacketBuffer, table Table, ruleId
|
||||
ruleIdx++
|
||||
continue
|
||||
}
|
||||
switch verdict := it.checkChain(hook, pkt, table, jumpTo, gso, r, preroutingAddr, inNicName, outNicName); verdict {
|
||||
switch verdict := it.checkChain(hook, pkt, table, jumpTo, r, addressEP, inNicName, outNicName); verdict {
|
||||
case chainAccept:
|
||||
return chainAccept
|
||||
case chainDrop:
|
||||
@@ -455,7 +639,7 @@ func (it *IPTables) checkChain(hook Hook, pkt *PacketBuffer, table Table, ruleId
|
||||
// Preconditions:
|
||||
// * pkt is a IPv4 packet of at least length header.IPv4MinimumSize.
|
||||
// * pkt.NetworkHeader is not nil.
|
||||
func (it *IPTables) checkRule(hook Hook, pkt *PacketBuffer, table Table, ruleIdx int, gso *GSO, r *Route, preroutingAddr tcpip.Address, inNicName, outNicName string) (RuleVerdict, int) {
|
||||
func (it *IPTables) checkRule(hook Hook, pkt *PacketBuffer, table Table, ruleIdx int, r *Route, addressEP AddressableEndpoint, inNicName, outNicName string) (RuleVerdict, int) {
|
||||
rule := table.Rules[ruleIdx]
|
||||
|
||||
// Check whether the packet matches the IP header filter.
|
||||
@@ -478,16 +662,16 @@ func (it *IPTables) checkRule(hook Hook, pkt *PacketBuffer, table Table, ruleIdx
|
||||
}
|
||||
|
||||
// All the matchers matched, so run the target.
|
||||
return rule.Target.Action(pkt, &it.connections, hook, gso, r, preroutingAddr)
|
||||
return rule.Target.Action(pkt, hook, r, addressEP)
|
||||
}
|
||||
|
||||
// OriginalDst returns the original destination of redirected connections. It
|
||||
// returns an error if the connection doesn't exist or isn't redirected.
|
||||
func (it *IPTables) OriginalDst(epID TransportEndpointID, netProto tcpip.NetworkProtocolNumber) (tcpip.Address, uint16, tcpip.Error) {
|
||||
func (it *IPTables) OriginalDst(epID TransportEndpointID, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber) (tcpip.Address, uint16, tcpip.Error) {
|
||||
it.mu.RLock()
|
||||
defer it.mu.RUnlock()
|
||||
if !it.modified {
|
||||
return "", 0, &tcpip.ErrNotConnected{}
|
||||
}
|
||||
return it.connections.originalDst(epID, netProto)
|
||||
return it.connections.originalDst(epID, netProto, transProto)
|
||||
}
|
||||
+195
-59
@@ -16,6 +16,7 @@ package stack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"inet.af/netstack/log"
|
||||
"inet.af/netstack/tcpip"
|
||||
@@ -29,7 +30,7 @@ type AcceptTarget struct {
|
||||
}
|
||||
|
||||
// Action implements Target.Action.
|
||||
func (*AcceptTarget) Action(*PacketBuffer, *ConnTrack, Hook, *GSO, *Route, tcpip.Address) (RuleVerdict, int) {
|
||||
func (*AcceptTarget) Action(*PacketBuffer, Hook, *Route, AddressableEndpoint) (RuleVerdict, int) {
|
||||
return RuleAccept, 0
|
||||
}
|
||||
|
||||
@@ -40,7 +41,7 @@ type DropTarget struct {
|
||||
}
|
||||
|
||||
// Action implements Target.Action.
|
||||
func (*DropTarget) Action(*PacketBuffer, *ConnTrack, Hook, *GSO, *Route, tcpip.Address) (RuleVerdict, int) {
|
||||
func (*DropTarget) Action(*PacketBuffer, Hook, *Route, AddressableEndpoint) (RuleVerdict, int) {
|
||||
return RuleDrop, 0
|
||||
}
|
||||
|
||||
@@ -52,7 +53,7 @@ type ErrorTarget struct {
|
||||
}
|
||||
|
||||
// Action implements Target.Action.
|
||||
func (*ErrorTarget) Action(*PacketBuffer, *ConnTrack, Hook, *GSO, *Route, tcpip.Address) (RuleVerdict, int) {
|
||||
func (*ErrorTarget) Action(*PacketBuffer, Hook, *Route, AddressableEndpoint) (RuleVerdict, int) {
|
||||
log.Debugf("ErrorTarget triggered.")
|
||||
return RuleDrop, 0
|
||||
}
|
||||
@@ -67,7 +68,7 @@ type UserChainTarget struct {
|
||||
}
|
||||
|
||||
// Action implements Target.Action.
|
||||
func (*UserChainTarget) Action(*PacketBuffer, *ConnTrack, Hook, *GSO, *Route, tcpip.Address) (RuleVerdict, int) {
|
||||
func (*UserChainTarget) Action(*PacketBuffer, Hook, *Route, AddressableEndpoint) (RuleVerdict, int) {
|
||||
panic("UserChainTarget should never be called.")
|
||||
}
|
||||
|
||||
@@ -79,17 +80,53 @@ type ReturnTarget struct {
|
||||
}
|
||||
|
||||
// Action implements Target.Action.
|
||||
func (*ReturnTarget) Action(*PacketBuffer, *ConnTrack, Hook, *GSO, *Route, tcpip.Address) (RuleVerdict, int) {
|
||||
func (*ReturnTarget) Action(*PacketBuffer, Hook, *Route, AddressableEndpoint) (RuleVerdict, int) {
|
||||
return RuleReturn, 0
|
||||
}
|
||||
|
||||
// DNATTarget modifies the destination port/IP of packets.
|
||||
type DNATTarget struct {
|
||||
// The new destination address for packets.
|
||||
//
|
||||
// Immutable.
|
||||
Addr tcpip.Address
|
||||
|
||||
// The new destination port for packets.
|
||||
//
|
||||
// Immutable.
|
||||
Port uint16
|
||||
|
||||
// NetworkProtocol is the network protocol the target is used with.
|
||||
//
|
||||
// Immutable.
|
||||
NetworkProtocol tcpip.NetworkProtocolNumber
|
||||
}
|
||||
|
||||
// Action implements Target.Action.
|
||||
func (rt *DNATTarget) Action(pkt *PacketBuffer, hook Hook, r *Route, addressEP AddressableEndpoint) (RuleVerdict, int) {
|
||||
// Sanity check.
|
||||
if rt.NetworkProtocol != pkt.NetworkProtocolNumber {
|
||||
panic(fmt.Sprintf(
|
||||
"DNATTarget.Action with NetworkProtocol %d called on packet with NetworkProtocolNumber %d",
|
||||
rt.NetworkProtocol, pkt.NetworkProtocolNumber))
|
||||
}
|
||||
|
||||
switch hook {
|
||||
case Prerouting, Output:
|
||||
case Input, Forward, Postrouting:
|
||||
panic(fmt.Sprintf("%s not supported for DNAT", hook))
|
||||
default:
|
||||
panic(fmt.Sprintf("%s unrecognized", hook))
|
||||
}
|
||||
|
||||
return dnatAction(pkt, hook, r, rt.Port, rt.Addr)
|
||||
|
||||
}
|
||||
|
||||
// RedirectTarget redirects the packet to this machine by modifying the
|
||||
// destination port/IP. Outgoing packets are redirected to the loopback device,
|
||||
// and incoming packets are redirected to the incoming interface (rather than
|
||||
// forwarded).
|
||||
//
|
||||
// TODO(gvisor.dev/issue/170): Other flags need to be added after we support
|
||||
// them.
|
||||
type RedirectTarget struct {
|
||||
// Port indicates port used to redirect. It is immutable.
|
||||
Port uint16
|
||||
@@ -100,10 +137,7 @@ type RedirectTarget struct {
|
||||
}
|
||||
|
||||
// Action implements Target.Action.
|
||||
// TODO(gvisor.dev/issue/170): Parse headers without copying. The current
|
||||
// implementation only works for Prerouting and calls pkt.Clone(), neither
|
||||
// of which should be the case.
|
||||
func (rt *RedirectTarget) Action(pkt *PacketBuffer, ct *ConnTrack, hook Hook, gso *GSO, r *Route, address tcpip.Address) (RuleVerdict, int) {
|
||||
func (rt *RedirectTarget) Action(pkt *PacketBuffer, hook Hook, r *Route, addressEP AddressableEndpoint) (RuleVerdict, int) {
|
||||
// Sanity check.
|
||||
if rt.NetworkProtocol != pkt.NetworkProtocolNumber {
|
||||
panic(fmt.Sprintf(
|
||||
@@ -111,18 +145,9 @@ func (rt *RedirectTarget) Action(pkt *PacketBuffer, ct *ConnTrack, hook Hook, gs
|
||||
rt.NetworkProtocol, pkt.NetworkProtocolNumber))
|
||||
}
|
||||
|
||||
// Packet is already manipulated.
|
||||
if pkt.NatDone {
|
||||
return RuleAccept, 0
|
||||
}
|
||||
|
||||
// Drop the packet if network and transport header are not set.
|
||||
if pkt.NetworkHeader().View().IsEmpty() || pkt.TransportHeader().View().IsEmpty() {
|
||||
return RuleDrop, 0
|
||||
}
|
||||
|
||||
// Change the address to loopback (127.0.0.1 or ::1) in Output and to
|
||||
// the primary address of the incoming interface in Prerouting.
|
||||
var address tcpip.Address
|
||||
switch hook {
|
||||
case Output:
|
||||
if pkt.NetworkProtocolNumber == header.IPv4ProtocolNumber {
|
||||
@@ -131,54 +156,165 @@ func (rt *RedirectTarget) Action(pkt *PacketBuffer, ct *ConnTrack, hook Hook, gs
|
||||
address = header.IPv6Loopback
|
||||
}
|
||||
case Prerouting:
|
||||
// No-op, as address is already set correctly.
|
||||
// addressEP is expected to be set for the prerouting hook.
|
||||
address = addressEP.MainAddress().Address
|
||||
default:
|
||||
panic("redirect target is supported only on output and prerouting hooks")
|
||||
}
|
||||
|
||||
// TODO(gvisor.dev/issue/170): Check Flags in RedirectTarget if
|
||||
// we need to change dest address (for OUTPUT chain) or ports.
|
||||
switch protocol := pkt.TransportProtocolNumber; protocol {
|
||||
case header.UDPProtocolNumber:
|
||||
udpHeader := header.UDP(pkt.TransportHeader().View())
|
||||
udpHeader.SetDestinationPort(rt.Port)
|
||||
return dnatAction(pkt, hook, r, rt.Port, address)
|
||||
}
|
||||
|
||||
// Calculate UDP checksum and set it.
|
||||
if hook == Output {
|
||||
udpHeader.SetChecksum(0)
|
||||
netHeader := pkt.Network()
|
||||
netHeader.SetDestinationAddress(address)
|
||||
// SNATTarget modifies the source port/IP in the outgoing packets.
|
||||
type SNATTarget struct {
|
||||
Addr tcpip.Address
|
||||
Port uint16
|
||||
|
||||
// Only calculate the checksum if offloading isn't supported.
|
||||
if r.RequiresTXTransportChecksum() {
|
||||
length := uint16(pkt.Size()) - uint16(len(pkt.NetworkHeader().View()))
|
||||
xsum := header.PseudoHeaderChecksum(protocol, netHeader.SourceAddress(), netHeader.DestinationAddress(), length)
|
||||
xsum = header.ChecksumCombine(xsum, pkt.Data().AsRange().Checksum())
|
||||
udpHeader.SetChecksum(^udpHeader.CalculateChecksum(xsum))
|
||||
}
|
||||
// NetworkProtocol is the network protocol the target is used with. It
|
||||
// is immutable.
|
||||
NetworkProtocol tcpip.NetworkProtocolNumber
|
||||
}
|
||||
|
||||
func dnatAction(pkt *PacketBuffer, hook Hook, r *Route, port uint16, address tcpip.Address) (RuleVerdict, int) {
|
||||
return natAction(pkt, hook, r, portRange{start: port, size: 1}, address, true /* dnat */)
|
||||
}
|
||||
|
||||
func snatAction(pkt *PacketBuffer, hook Hook, r *Route, port uint16, address tcpip.Address) (RuleVerdict, int) {
|
||||
ports := portRange{start: port, size: 1}
|
||||
if port == 0 {
|
||||
// As per iptables(8),
|
||||
//
|
||||
// If no port range is specified, then source ports below 512 will be
|
||||
// mapped to other ports below 512: those between 512 and 1023 inclusive
|
||||
// will be mapped to ports below 1024, and other ports will be mapped to
|
||||
// 1024 or above.
|
||||
switch protocol := pkt.TransportProtocolNumber; protocol {
|
||||
case header.UDPProtocolNumber:
|
||||
port = header.UDP(pkt.TransportHeader().View()).SourcePort()
|
||||
case header.TCPProtocolNumber:
|
||||
port = header.TCP(pkt.TransportHeader().View()).SourcePort()
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported transport protocol = %d", pkt.TransportProtocolNumber))
|
||||
}
|
||||
|
||||
// After modification, IPv4 packets need a valid checksum.
|
||||
if pkt.NetworkProtocolNumber == header.IPv4ProtocolNumber {
|
||||
netHeader := header.IPv4(pkt.NetworkHeader().View())
|
||||
netHeader.SetChecksum(0)
|
||||
netHeader.SetChecksum(^netHeader.CalculateChecksum())
|
||||
}
|
||||
pkt.NatDone = true
|
||||
case header.TCPProtocolNumber:
|
||||
if ct == nil {
|
||||
return RuleAccept, 0
|
||||
switch {
|
||||
case port < 512:
|
||||
ports = portRange{start: 1, size: 511}
|
||||
case port < 1024:
|
||||
ports = portRange{start: 1, size: 1023}
|
||||
default:
|
||||
ports = portRange{start: 1024, size: math.MaxUint16 - 1023}
|
||||
}
|
||||
}
|
||||
|
||||
// Set up conection for matching NAT rule. Only the first
|
||||
// packet of the connection comes here. Other packets will be
|
||||
// manipulated in connection tracking.
|
||||
if conn := ct.insertRedirectConn(pkt, hook, rt.Port, address); conn != nil {
|
||||
ct.handlePacket(pkt, hook, gso, r)
|
||||
}
|
||||
default:
|
||||
return natAction(pkt, hook, r, ports, address, false /* dnat */)
|
||||
}
|
||||
|
||||
func natAction(pkt *PacketBuffer, hook Hook, r *Route, ports portRange, address tcpip.Address, dnat bool) (RuleVerdict, int) {
|
||||
// Drop the packet if network and transport header are not set.
|
||||
if pkt.NetworkHeader().View().IsEmpty() || pkt.TransportHeader().View().IsEmpty() {
|
||||
return RuleDrop, 0
|
||||
}
|
||||
|
||||
return RuleAccept, 0
|
||||
if t := pkt.tuple; t != nil {
|
||||
t.conn.performNAT(pkt, hook, r, ports, address, dnat)
|
||||
return RuleAccept, 0
|
||||
}
|
||||
|
||||
return RuleDrop, 0
|
||||
}
|
||||
|
||||
// Action implements Target.Action.
|
||||
func (st *SNATTarget) Action(pkt *PacketBuffer, hook Hook, r *Route, _ AddressableEndpoint) (RuleVerdict, int) {
|
||||
// Sanity check.
|
||||
if st.NetworkProtocol != pkt.NetworkProtocolNumber {
|
||||
panic(fmt.Sprintf(
|
||||
"SNATTarget.Action with NetworkProtocol %d called on packet with NetworkProtocolNumber %d",
|
||||
st.NetworkProtocol, pkt.NetworkProtocolNumber))
|
||||
}
|
||||
|
||||
switch hook {
|
||||
case Postrouting, Input:
|
||||
case Prerouting, Output, Forward:
|
||||
panic(fmt.Sprintf("%s not supported", hook))
|
||||
default:
|
||||
panic(fmt.Sprintf("%s unrecognized", hook))
|
||||
}
|
||||
|
||||
return snatAction(pkt, hook, r, st.Port, st.Addr)
|
||||
}
|
||||
|
||||
// MasqueradeTarget modifies the source port/IP in the outgoing packets.
|
||||
type MasqueradeTarget struct {
|
||||
// NetworkProtocol is the network protocol the target is used with. It
|
||||
// is immutable.
|
||||
NetworkProtocol tcpip.NetworkProtocolNumber
|
||||
}
|
||||
|
||||
// Action implements Target.Action.
|
||||
func (mt *MasqueradeTarget) Action(pkt *PacketBuffer, hook Hook, r *Route, addressEP AddressableEndpoint) (RuleVerdict, int) {
|
||||
// Sanity check.
|
||||
if mt.NetworkProtocol != pkt.NetworkProtocolNumber {
|
||||
panic(fmt.Sprintf(
|
||||
"MasqueradeTarget.Action with NetworkProtocol %d called on packet with NetworkProtocolNumber %d",
|
||||
mt.NetworkProtocol, pkt.NetworkProtocolNumber))
|
||||
}
|
||||
|
||||
switch hook {
|
||||
case Postrouting:
|
||||
case Prerouting, Input, Forward, Output:
|
||||
panic(fmt.Sprintf("masquerade target is supported only on postrouting hook; hook = %d", hook))
|
||||
default:
|
||||
panic(fmt.Sprintf("%s unrecognized", hook))
|
||||
}
|
||||
|
||||
// addressEP is expected to be set for the postrouting hook.
|
||||
ep := addressEP.AcquireOutgoingPrimaryAddress(pkt.Network().DestinationAddress(), false /* allowExpired */)
|
||||
if ep == nil {
|
||||
// No address exists that we can use as a source address.
|
||||
return RuleDrop, 0
|
||||
}
|
||||
|
||||
address := ep.AddressWithPrefix().Address
|
||||
ep.DecRef()
|
||||
return snatAction(pkt, hook, r, 0 /* port */, address)
|
||||
}
|
||||
|
||||
func rewritePacket(n header.Network, t header.ChecksummableTransport, updateSRCFields, fullChecksum, updatePseudoHeader bool, newPort uint16, newAddr tcpip.Address) {
|
||||
if updateSRCFields {
|
||||
if fullChecksum {
|
||||
t.SetSourcePortWithChecksumUpdate(newPort)
|
||||
} else {
|
||||
t.SetSourcePort(newPort)
|
||||
}
|
||||
} else {
|
||||
if fullChecksum {
|
||||
t.SetDestinationPortWithChecksumUpdate(newPort)
|
||||
} else {
|
||||
t.SetDestinationPort(newPort)
|
||||
}
|
||||
}
|
||||
|
||||
if updatePseudoHeader {
|
||||
var oldAddr tcpip.Address
|
||||
if updateSRCFields {
|
||||
oldAddr = n.SourceAddress()
|
||||
} else {
|
||||
oldAddr = n.DestinationAddress()
|
||||
}
|
||||
|
||||
t.UpdateChecksumPseudoHeaderAddress(oldAddr, newAddr, fullChecksum)
|
||||
}
|
||||
|
||||
if checksummableNetHeader, ok := n.(header.ChecksummableNetwork); ok {
|
||||
if updateSRCFields {
|
||||
checksummableNetHeader.SetSourceAddressWithChecksumUpdate(newAddr)
|
||||
} else {
|
||||
checksummableNetHeader.SetDestinationAddressWithChecksumUpdate(newAddr)
|
||||
}
|
||||
} else if updateSRCFields {
|
||||
n.SetSourceAddress(newAddr)
|
||||
} else {
|
||||
n.SetDestinationAddress(newAddr)
|
||||
}
|
||||
}
|
||||
+27
-21
@@ -81,26 +81,25 @@ const (
|
||||
//
|
||||
// +stateify savable
|
||||
type IPTables struct {
|
||||
// mu protects v4Tables, v6Tables, and modified.
|
||||
mu sync.RWMutex
|
||||
// v4Tables and v6tables map tableIDs to tables. They hold builtin
|
||||
// tables only, not user tables. mu must be locked for accessing.
|
||||
v4Tables [NumTables]Table
|
||||
v6Tables [NumTables]Table
|
||||
// modified is whether tables have been modified at least once. It is
|
||||
// used to elide the iptables performance overhead for workloads that
|
||||
// don't utilize iptables.
|
||||
modified bool
|
||||
|
||||
// priorities maps each hook to a list of table names. The order of the
|
||||
// list is the order in which each table should be visited for that
|
||||
// hook. It is immutable.
|
||||
priorities [NumHooks][]TableID
|
||||
|
||||
connections ConnTrack
|
||||
|
||||
// reaperDone can be signaled to stop the reaper goroutine.
|
||||
reaperDone chan struct{}
|
||||
|
||||
mu sync.RWMutex
|
||||
// v4Tables and v6tables map tableIDs to tables. They hold builtin
|
||||
// tables only, not user tables.
|
||||
//
|
||||
// +checklocks:mu
|
||||
v4Tables [NumTables]Table
|
||||
// +checklocks:mu
|
||||
v6Tables [NumTables]Table
|
||||
// modified is whether tables have been modified at least once. It is
|
||||
// used to elide the iptables performance overhead for workloads that
|
||||
// don't utilize iptables.
|
||||
//
|
||||
// +checklocks:mu
|
||||
modified bool
|
||||
}
|
||||
|
||||
// VisitTargets traverses all the targets of all tables and replaces each with
|
||||
@@ -242,7 +241,6 @@ type IPHeaderFilter struct {
|
||||
func (fl IPHeaderFilter) match(pkt *PacketBuffer, hook Hook, inNicName, outNicName string) bool {
|
||||
// Extract header fields.
|
||||
var (
|
||||
// TODO(gvisor.dev/issue/170): Support other filter fields.
|
||||
transProto tcpip.TransportProtocolNumber
|
||||
dstAddr tcpip.Address
|
||||
srcAddr tcpip.Address
|
||||
@@ -280,9 +278,17 @@ func (fl IPHeaderFilter) match(pkt *PacketBuffer, hook Hook, inNicName, outNicNa
|
||||
return matchIfName(inNicName, fl.InputInterface, fl.InputInterfaceInvert)
|
||||
case Output:
|
||||
return matchIfName(outNicName, fl.OutputInterface, fl.OutputInterfaceInvert)
|
||||
case Forward, Postrouting:
|
||||
// TODO(gvisor.dev/issue/170): Add the check for FORWARD and POSTROUTING
|
||||
// hooks after supported.
|
||||
case Forward:
|
||||
if !matchIfName(inNicName, fl.InputInterface, fl.InputInterfaceInvert) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !matchIfName(outNicName, fl.OutputInterface, fl.OutputInterfaceInvert) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
case Postrouting:
|
||||
return true
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown hook: %d", hook))
|
||||
@@ -345,5 +351,5 @@ type Target interface {
|
||||
// Action takes an action on the packet and returns a verdict on how
|
||||
// traversal should (or should not) continue. If the return value is
|
||||
// Jump, it also returns the index of the rule to jump to.
|
||||
Action(packet *PacketBuffer, connections *ConnTrack, hook Hook, gso *GSO, r *Route, address tcpip.Address) (RuleVerdict, int)
|
||||
Action(*PacketBuffer, Hook, *Route, AddressableEndpoint) (RuleVerdict, int)
|
||||
}
|
||||
+1
-1
@@ -310,7 +310,7 @@ func (n *neighborCache) handleUpperLevelConfirmation(addr tcpip.Address) {
|
||||
func (n *neighborCache) init(nic *nic, r LinkAddressResolver) {
|
||||
*n = neighborCache{
|
||||
nic: nic,
|
||||
state: NewNUDState(nic.stack.nudConfigs, nic.stack.randomGenerator),
|
||||
state: NewNUDState(nic.stack.nudConfigs, nic.stack.clock, nic.stack.randomGenerator),
|
||||
linkRes: r,
|
||||
}
|
||||
n.mu.Lock()
|
||||
|
||||
+24
-18
@@ -31,10 +31,10 @@ const (
|
||||
|
||||
// NeighborEntry describes a neighboring device in the local network.
|
||||
type NeighborEntry struct {
|
||||
Addr tcpip.Address
|
||||
LinkAddr tcpip.LinkAddress
|
||||
State NeighborState
|
||||
UpdatedAtNanos int64
|
||||
Addr tcpip.Address
|
||||
LinkAddr tcpip.LinkAddress
|
||||
State NeighborState
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
// NeighborState defines the state of a NeighborEntry within the Neighbor
|
||||
@@ -138,10 +138,10 @@ func newNeighborEntry(cache *neighborCache, remoteAddr tcpip.Address, nudState *
|
||||
// calling `setStateLocked`.
|
||||
func newStaticNeighborEntry(cache *neighborCache, addr tcpip.Address, linkAddr tcpip.LinkAddress, state *NUDState) *neighborEntry {
|
||||
entry := NeighborEntry{
|
||||
Addr: addr,
|
||||
LinkAddr: linkAddr,
|
||||
State: Static,
|
||||
UpdatedAtNanos: cache.nic.stack.clock.NowNanoseconds(),
|
||||
Addr: addr,
|
||||
LinkAddr: linkAddr,
|
||||
State: Static,
|
||||
UpdatedAt: cache.nic.stack.clock.Now(),
|
||||
}
|
||||
n := &neighborEntry{
|
||||
cache: cache,
|
||||
@@ -166,14 +166,20 @@ func (e *neighborEntry) notifyCompletionLocked(err tcpip.Error) {
|
||||
if ch := e.mu.done; ch != nil {
|
||||
close(ch)
|
||||
e.mu.done = nil
|
||||
// Dequeue the pending packets in a new goroutine to not hold up the current
|
||||
// Dequeue the pending packets asynchronously to not hold up the current
|
||||
// goroutine as writing packets may be a costly operation.
|
||||
//
|
||||
// At the time of writing, when writing packets, a neighbor's link address
|
||||
// is resolved (which ends up obtaining the entry's lock) while holding the
|
||||
// link resolution queue's lock. Dequeuing packets in a new goroutine avoids
|
||||
// a lock ordering violation.
|
||||
go e.cache.nic.linkResQueue.dequeue(ch, e.mu.neigh.LinkAddr, err)
|
||||
// link resolution queue's lock. Dequeuing packets asynchronously avoids a
|
||||
// lock ordering violation.
|
||||
//
|
||||
// NB: this is equivalent to spawning a goroutine directly using the go
|
||||
// keyword but allows tests that use manual clocks to deterministically
|
||||
// wait for this work to complete.
|
||||
e.cache.nic.stack.clock.AfterFunc(0, func() {
|
||||
e.cache.nic.linkResQueue.dequeue(ch, e.mu.neigh.LinkAddr, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,7 +230,7 @@ func (e *neighborEntry) cancelTimerLocked() {
|
||||
//
|
||||
// Precondition: e.mu MUST be locked.
|
||||
func (e *neighborEntry) removeLocked() {
|
||||
e.mu.neigh.UpdatedAtNanos = e.cache.nic.stack.clock.NowNanoseconds()
|
||||
e.mu.neigh.UpdatedAt = e.cache.nic.stack.clock.Now()
|
||||
e.dispatchRemoveEventLocked()
|
||||
e.cancelTimerLocked()
|
||||
// TODO(https://gvisor.dev/issues/5583): test the case where this function is
|
||||
@@ -246,7 +252,7 @@ func (e *neighborEntry) setStateLocked(next NeighborState) {
|
||||
|
||||
prev := e.mu.neigh.State
|
||||
e.mu.neigh.State = next
|
||||
e.mu.neigh.UpdatedAtNanos = e.cache.nic.stack.clock.NowNanoseconds()
|
||||
e.mu.neigh.UpdatedAt = e.cache.nic.stack.clock.Now()
|
||||
config := e.nudState.Config()
|
||||
|
||||
switch next {
|
||||
@@ -307,7 +313,7 @@ func (e *neighborEntry) setStateLocked(next NeighborState) {
|
||||
// a shared lock.
|
||||
e.mu.timer = timer{
|
||||
done: &done,
|
||||
timer: e.cache.nic.stack.Clock().AfterFunc(0, func() {
|
||||
timer: e.cache.nic.stack.Clock().AfterFunc(immediateDuration, func() {
|
||||
var err tcpip.Error = &tcpip.ErrTimeout{}
|
||||
if remaining != 0 {
|
||||
err = e.cache.linkRes.LinkAddressRequest(addr, "" /* localAddr */, linkAddr)
|
||||
@@ -354,14 +360,14 @@ func (e *neighborEntry) handlePacketQueuedLocked(localAddr tcpip.Address) {
|
||||
case Unknown, Unreachable:
|
||||
prev := e.mu.neigh.State
|
||||
e.mu.neigh.State = Incomplete
|
||||
e.mu.neigh.UpdatedAtNanos = e.cache.nic.stack.clock.NowNanoseconds()
|
||||
e.mu.neigh.UpdatedAt = e.cache.nic.stack.clock.Now()
|
||||
|
||||
switch prev {
|
||||
case Unknown:
|
||||
e.dispatchAddEventLocked()
|
||||
case Unreachable:
|
||||
e.dispatchChangeEventLocked()
|
||||
e.cache.nic.stats.Neighbor.UnreachableEntryLookups.Increment()
|
||||
e.cache.nic.stats.neighbor.unreachableEntryLookups.Increment()
|
||||
}
|
||||
|
||||
config := e.nudState.Config()
|
||||
@@ -378,7 +384,7 @@ func (e *neighborEntry) handlePacketQueuedLocked(localAddr tcpip.Address) {
|
||||
// a shared lock.
|
||||
e.mu.timer = timer{
|
||||
done: &done,
|
||||
timer: e.cache.nic.stack.Clock().AfterFunc(0, func() {
|
||||
timer: e.cache.nic.stack.Clock().AfterFunc(immediateDuration, func() {
|
||||
var err tcpip.Error = &tcpip.ErrTimeout{}
|
||||
if remaining != 0 {
|
||||
// As per RFC 4861 section 7.2.2:
|
||||
|
||||
Loaded 100 of 169 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user