netstack: make tcpip.Address hold a []byte

tcp_benchmark throughput increase 2-3%, but allocations go down (25% in the
download benchmark, only 2% in the upload path).

PiperOrigin-RevId: 532523146
This commit is contained in:
Kevin Krakauer
2023-05-16 11:40:49 -07:00
committed by gVisor bot
parent 27809081d4
commit 792ebbff8e
15 changed files with 104 additions and 80 deletions
+4 -2
View File
@@ -117,10 +117,11 @@ func (s *Stack) InterfaceAddrs() map[int32][]inet.InterfaceAddr {
continue
}
addrCopy := a.AddressWithPrefix.Address
addrs = append(addrs, inet.InterfaceAddr{
Family: family,
PrefixLen: uint8(a.AddressWithPrefix.PrefixLen),
Addr: a.AddressWithPrefix.Address.AsSlice(),
Addr: addrCopy.AsSlice(),
// TODO(b/68878065): Other fields.
})
}
@@ -440,6 +441,7 @@ func (s *Stack) RouteTable() []inet.Route {
continue
}
dstAddr := rt.Destination.ID()
routeTable = append(routeTable, inet.Route{
Family: family,
DstLen: uint8(rt.Destination.Prefix()), // The CIDR prefix for the destination.
@@ -453,7 +455,7 @@ func (s *Stack) RouteTable() []inet.Route {
Scope: linux.RT_SCOPE_LINK,
Type: linux.RTN_UNICAST,
DstAddr: rt.Destination.ID().AsSlice(),
DstAddr: dstAddr.AsSlice(),
OutputInterface: int32(rt.NIC),
GatewayAddr: rt.Gateway.AsSlice(),
})
+2 -19
View File
@@ -76,25 +76,8 @@ func checksumUpdate2ByteAlignedAddress(xsum uint16, old, new tcpip.Address) uint
panic(fmt.Sprintf("buffer has an odd number of bytes; got = %d", oldBytes))
}
// Try to avoid allocating.
var oldAddr []byte
var newAddr []byte
switch old.BitLen() {
case 32:
var oldAddr4 [4]byte
var newAddr4 [4]byte
oldAddr4 = old.As4()
newAddr4 = new.As4()
oldAddr = oldAddr4[:]
newAddr = newAddr4[:]
case 128:
var oldAddr16 [16]byte
var newAddr16 [16]byte
oldAddr16 = old.As16()
newAddr16 = new.As16()
oldAddr = oldAddr16[:]
newAddr = newAddr16[:]
}
oldAddr := old.AsSlice()
newAddr := new.AsSlice()
// As per RFC 1071 page 4,
// (4) Incremental Update
+2 -1
View File
@@ -437,7 +437,8 @@ func AppendOpaqueInterfaceIdentifier(buf []byte, prefix tcpip.Subnet, nicName st
// Note, we omit the optional Network_ID field.
h := sha256.New()
// h.Write never returns an error.
h.Write([]byte(prefix.ID().AsSlice()[:IIDOffsetInIPv6Address]))
prefixID := prefix.ID()
h.Write([]byte(prefixID.AsSlice()[:IIDOffsetInIPv6Address]))
h.Write([]byte(nicName))
h.Write([]byte{dadCounter})
h.Write(secretKey)
+2 -1
View File
@@ -123,7 +123,8 @@ func TestAppendOpaqueInterfaceIdentifier(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
h := sha256.New()
h.Write(test.prefix.ID().AsSlice()[:header.IIDOffsetInIPv6Address])
prefixID := test.prefix.ID()
h.Write(prefixID.AsSlice()[:header.IIDOffsetInIPv6Address])
h.Write([]byte(test.nicName))
h.Write([]byte{test.dadCounter})
if k := test.secretKey; k != nil {
+4 -3
View File
@@ -2806,11 +2806,12 @@ func hashRoute(r *stack.Route, hashIV uint32) uint32 {
// The FNV-1a was chosen because it is a fast hashing algorithm, and
// cryptographic properties are not needed here.
h := fnv.New32a()
if _, err := h.Write(r.LocalAddress().AsSlice()); err != nil {
localAddr := r.LocalAddress()
if _, err := h.Write(localAddr.AsSlice()); err != nil {
panic(fmt.Sprintf("Hash.Write: %s, but Hash' implementation of Write is not expected to ever return an error", err))
}
if _, err := h.Write(r.RemoteAddress().AsSlice()); err != nil {
remoteAddr := r.RemoteAddress()
if _, err := h.Write(remoteAddr.AsSlice()); err != nil {
panic(fmt.Sprintf("Hash.Write: %s, but Hash' implementation of Write is not expected to ever return an error", err))
}
+2 -1
View File
@@ -1182,7 +1182,8 @@ func (ndp *ndpState) generateSLAACAddr(prefix tcpip.Subnet, state *slaacPrefixSt
}
var generatedAddr tcpip.AddressWithPrefix
addrBytes := []byte(prefix.ID().AsSlice())
prefixID := prefix.ID()
addrBytes := prefixID.AsSlice()
for i := 0; ; i++ {
// If we were unable to generate an address after the maximum SLAAC address
+2 -1
View File
@@ -969,7 +969,8 @@ func TestMGPQueryMessages(t *testing.T) {
{
name: "Specified other address",
multicastAddr: func() tcpip.Address {
addrBytes := test.multicastAddr.AsSlice()
addrCopy := test.multicastAddr
addrBytes := addrCopy.AsSlice()
addrBytes[len(addrBytes)-1]++
return tcpip.AddrFromSlice(addrBytes)
}(),
+4 -2
View File
@@ -122,8 +122,10 @@ func (f *fwdTestNetworkEndpoint) WritePacket(r *Route, params NetworkHeaderParam
// Add the protocol's header to the packet and send it to the link
// endpoint.
b := pkt.NetworkHeader().Push(fwdTestNetHeaderLen)
copy(b[dstAddrOffset:], r.RemoteAddress().AsSlice())
copy(b[srcAddrOffset:], r.LocalAddress().AsSlice())
remote := r.RemoteAddress()
local := r.LocalAddress()
copy(b[dstAddrOffset:], remote.AsSlice())
copy(b[srcAddrOffset:], local.AsSlice())
b[protocolNumberOffset] = byte(params.Protocol)
pkt.NetworkProtocolNumber = fwdTestNetNumber
+4 -2
View File
@@ -607,10 +607,12 @@ func (gd *groDispatcher) bucketForPacket(ipHdr header.Network, tcpHdr header.TCP
// TODO(b/256037250): Use jenkins or checksum. Write a test to print
// distribution.
var sum int
for _, val := range ipHdr.SourceAddress().AsSlice() {
srcAddr := ipHdr.SourceAddress()
for _, val := range srcAddr.AsSlice() {
sum += int(val)
}
for _, val := range ipHdr.DestinationAddress().AsSlice() {
dstAddr := ipHdr.DestinationAddress()
for _, val := range dstAddr.AsSlice() {
sum += int(val)
}
sum += int(tcpHdr.SourcePort())
+14 -7
View File
@@ -73,7 +73,8 @@ func addrForSubnet(subnet tcpip.Subnet, linkAddr tcpip.LinkAddress) tcpip.Addres
return tcpip.AddressWithPrefix{}
}
addrBytes := subnet.ID().AsSlice()
subnetID := subnet.ID()
addrBytes := subnetID.AsSlice()
header.EthernetAdddressToModifiedEUI64IntoBuf(linkAddr, addrBytes[header.IIDOffsetInIPv6Address:])
return tcpip.AddressWithPrefix{
Address: tcpip.AddrFromSlice(addrBytes),
@@ -3214,7 +3215,8 @@ func TestMixedSLAACAddrConflictRegen(t *testing.T) {
var stableAddrsWithOpaqueIID [maxAddrs]tcpip.AddressWithPrefix
var tempAddrsWithOpaqueIID [maxAddrs]tcpip.AddressWithPrefix
var tempAddrsWithModifiedEUI64 [maxAddrs]tcpip.AddressWithPrefix
addrBytes := subnet.ID().AsSlice()
subnetID := subnet.ID()
addrBytes := subnetID.AsSlice()
for i := 0; i < maxAddrs; i++ {
stableAddrsWithOpaqueIID[i] = tcpip.AddressWithPrefix{
Address: tcpip.AddrFromSlice(header.AppendOpaqueInterfaceIdentifier(addrBytes[:header.IIDOffsetInIPv6Address], subnet, nicName, uint8(i), nil)),
@@ -4302,12 +4304,14 @@ func TestAutoGenAddrWithOpaqueIID(t *testing.T) {
// addr1 and addr2 are the addresses that are expected to be generated when
// stack.Stack is configured to generate opaque interface identifiers as
// defined by RFC 7217.
addrBytes := subnet1.ID().AsSlice()
subnetID := subnet1.ID()
addrBytes := subnetID.AsSlice()
addr1 := tcpip.AddressWithPrefix{
Address: tcpip.AddrFromSlice(header.AppendOpaqueInterfaceIdentifier(addrBytes[:header.IIDOffsetInIPv6Address], subnet1, nicName, 0, secretKey)),
PrefixLen: 64,
}
addrBytes = subnet2.ID().AsSlice()
subnetID = subnet2.ID()
addrBytes = subnetID.AsSlice()
addr2 := tcpip.AddressWithPrefix{
Address: tcpip.AddrFromSlice(header.AppendOpaqueInterfaceIdentifier(addrBytes[:header.IIDOffsetInIPv6Address], subnet2, nicName, 0, secretKey)),
PrefixLen: 64,
@@ -4394,7 +4398,8 @@ func TestAutoGenAddrInResponseToDADConflicts(t *testing.T) {
prefix, subnet, _ := prefixSubnetAddr(0, linkAddr1)
addrForSubnet := func(subnet tcpip.Subnet, dadCounter uint8) tcpip.AddressWithPrefix {
addrBytes := subnet.ID().AsSlice()
subnetID := subnet.ID()
addrBytes := subnetID.AsSlice()
return tcpip.AddressWithPrefix{
Address: tcpip.AddrFromSlice(header.AppendOpaqueInterfaceIdentifier(addrBytes[:header.IIDOffsetInIPv6Address], subnet, nicName, dadCounter, secretKey)),
PrefixLen: 64,
@@ -4676,7 +4681,8 @@ func TestAutoGenAddrWithEUI64IIDNoDADRetries(t *testing.T) {
addrType.triggerSLAACFn(e)
addrBytes := addrType.subnet.ID().AsSlice()
subnetID := addrType.subnet.ID()
addrBytes := subnetID.AsSlice()
header.EthernetAdddressToModifiedEUI64IntoBuf(linkAddr1, addrBytes[header.IIDOffsetInIPv6Address:])
addr := tcpip.AddressWithPrefix{
Address: tcpip.AddrFromSlice(addrBytes),
@@ -4762,7 +4768,8 @@ func TestAutoGenAddrContinuesLifetimesAfterRetry(t *testing.T) {
received := clock.NowMonotonic()
e.InjectInbound(header.IPv6ProtocolNumber, raBufWithPI(llAddr2, 0, prefix, true, true, lifetimeSeconds, lifetimeSeconds))
addrBytes := subnet.ID().AsSlice()
subnetID := subnet.ID()
addrBytes := subnetID.AsSlice()
addr := tcpip.AddressWithPrefix{
Address: tcpip.AddrFromSlice(header.AppendOpaqueInterfaceIdentifier(addrBytes[:header.IIDOffsetInIPv6Address], subnet, nicName, 0, secretKey)),
PrefixLen: 64,
+2 -2
View File
@@ -1381,7 +1381,7 @@ func (s *Stack) FindRoute(id tcpip.NICID, localAddr, remoteAddr tcpip.Address, n
// requirement to do this from any RFC but simply a choice made to better
// follow a strong host model which the netstack follows at the time of
// writing.
if onlyGlobalAddresses && chosenRoute == (tcpip.Route{}) && isNICForwarding(nic, netProto) {
if onlyGlobalAddresses && chosenRoute.Equal(tcpip.Route{}) && isNICForwarding(nic, netProto) {
chosenRoute = route
}
}
@@ -1391,7 +1391,7 @@ func (s *Stack) FindRoute(id tcpip.NICID, localAddr, remoteAddr tcpip.Address, n
return r, nil
}
if chosenRoute != (tcpip.Route{}) {
if !chosenRoute.Equal(tcpip.Route{}) {
// At this point we know the stack has forwarding enabled since chosenRoute is
// only set when forwarding is enabled.
nic, ok := s.nics[chosenRoute.NIC]
+5 -3
View File
@@ -180,14 +180,16 @@ func (f *fakeNetworkEndpoint) NetworkProtocolNumber() tcpip.NetworkProtocolNumbe
func (f *fakeNetworkEndpoint) WritePacket(r *stack.Route, params stack.NetworkHeaderParams, pkt stack.PacketBufferPtr) tcpip.Error {
// Increment the sent packet count in the protocol descriptor.
f.proto.sendPacketCount[int(r.RemoteAddress().AsSlice()[0])%len(f.proto.sendPacketCount)]++
remote := r.RemoteAddress()
f.proto.sendPacketCount[int(remote.AsSlice()[0])%len(f.proto.sendPacketCount)]++
// Add the protocol's header to the packet and send it to the link
// endpoint.
hdr := pkt.NetworkHeader().Push(fakeNetHeaderLen)
pkt.NetworkProtocolNumber = fakeNetNumber
copy(hdr[dstAddrOffset:], r.RemoteAddress().AsSlice())
copy(hdr[srcAddrOffset:], r.LocalAddress().AsSlice())
copy(hdr[dstAddrOffset:], remote.AsSlice())
local := r.LocalAddress()
copy(hdr[srcAddrOffset:], local.AsSlice())
hdr[protocolNumberOffset] = byte(params.Protocol)
if r.Loop()&stack.PacketLoop != 0 {
+45 -30
View File
@@ -154,12 +154,18 @@ type Timer interface {
//
// +stateify savable
type Address struct {
addr string
addr [16]byte
length int
}
// AddrFrom4 converts addr to an Address.
func AddrFrom4(addr [4]byte) Address {
return Address{addr: string(addr[:])}
ret := Address{
length: 4,
}
// It's guaranteed that copy will return 4.
copy(ret.addr[:4], addr[:])
return ret
}
// AddrFrom4Slice converts addr to an Address. It panics if len(addr) != 4.
@@ -167,12 +173,18 @@ func AddrFrom4Slice(addr []byte) Address {
if len(addr) != 4 {
panic(fmt.Sprintf("bad address length for address %v", addr))
}
// TODO(281863522): This is probalby an extra copy, just do the init here.
return AddrFrom4([4]byte(addr))
}
// AddrFrom16 converts addr to an Address.
func AddrFrom16(addr [16]byte) Address {
return Address{addr: string(addr[:])}
ret := Address{
length: 16,
}
// It's guaranteed that copy will return 16.
copy(ret.addr[:16], addr[:])
return ret
}
// AddrFrom16Slice converts addr to an Address. It panics if len(addr) != 16.
@@ -180,6 +192,7 @@ func AddrFrom16Slice(addr []byte) Address {
if len(addr) != 16 {
panic(fmt.Sprintf("bad address length for address %v", addr))
}
// TODO(281863522): This is probalby an extra copy, just do the init here.
return AddrFrom16([16]byte(addr))
}
@@ -200,7 +213,7 @@ func (a Address) As4() [4]byte {
if a.Len() != 4 {
panic(fmt.Sprintf("bad address length for address %v", a.addr))
}
return ([4]byte)([]byte(a.addr))
return [4]byte(a.addr[:4])
}
// As16 returns a as a 16 byte array. It panics if the address length is not 16.
@@ -208,13 +221,15 @@ func (a Address) As16() [16]byte {
if a.Len() != 16 {
panic(fmt.Sprintf("bad address length for address %v", a.addr))
}
return ([16]byte)([]byte(a.addr))
return [16]byte(a.addr[:16])
}
// AsSlice returns a as a byte slice. It may return a new slice or a window
// into existing memory.
func (a Address) AsSlice() []byte {
return []byte(a.addr)
// AsSlice returns a as a byte slice. Callers should be careful as it can
// return a window into existing memory.
//
// +checkescape
func (a *Address) AsSlice() []byte {
return a.addr[:a.length]
}
// BitLen returns the length in bits of a.
@@ -224,7 +239,7 @@ func (a Address) BitLen() int {
// Len returns the length in bytes of a.
func (a Address) Len() int {
return len(a.addr)
return a.length
}
// WithPrefix returns the address with a prefix that represents a point subnet.
@@ -262,7 +277,7 @@ func (a Address) MatchingPrefix(b Address) uint8 {
}
var prefix uint8
for i := range a.addr {
for i := 0; i < a.length; i++ {
aByte := a.addr[i]
bByte := b.addr[i]
@@ -308,7 +323,7 @@ func MaskFromBytes(bs []byte) AddressMask {
// String implements Stringer.
func (m AddressMask) String() string {
return fmt.Sprintf("%x", []byte(m.mask))
return fmt.Sprintf("%x", m.mask)
}
// BitLen returns the length of the mask in bits.
@@ -392,11 +407,11 @@ func (s *Subnet) Mask() AddressMask {
// Broadcast returns the subnet's broadcast address.
func (s *Subnet) Broadcast() Address {
addr := []byte(s.address.addr)
for i := range addr {
addr[i] |= ^s.mask.mask[i]
addrCopy := s.address
for i := 0; i < addrCopy.Len(); i++ {
addrCopy.addr[i] |= ^s.mask.mask[i]
}
return Address{addr: string(addr)}
return addrCopy
}
// IsBroadcast returns true if the address is considered a broadcast address.
@@ -1462,7 +1477,7 @@ type Route struct {
func (r Route) String() string {
var out strings.Builder
_, _ = fmt.Fprintf(&out, "%s", r.Destination)
if len(r.Gateway.addr) > 0 {
if r.Gateway.length > 0 {
_, _ = fmt.Fprintf(&out, " via %s", r.Gateway)
}
_, _ = fmt.Fprintf(&out, " nic %d", r.NIC)
@@ -1472,7 +1487,7 @@ func (r Route) String() string {
// Equal returns true if the given Route is equal to this Route.
func (r Route) Equal(to Route) bool {
// NOTE: This relies on the fact that r.Destination == to.Destination
return r == to
return r.Destination.Equal(to.Destination) && r.Gateway == to.Gateway && r.NIC == to.NIC
}
// TransportProtocolNumber is the number of a transport protocol.
@@ -2520,7 +2535,7 @@ func (a Address) String() string {
}
return b.String()
default:
return fmt.Sprintf("%x", []byte(a.addr))
return fmt.Sprintf("%x", a.addr[:])
}
}
@@ -2535,18 +2550,18 @@ func (a Address) To4() Address {
return a
}
if a.Len() == ipv6len &&
isZeros(Address{addr: a.addr[0:10]}) &&
isZeros(a.addr[:10]) &&
a.addr[10] == 0xff &&
a.addr[11] == 0xff {
return Address{addr: a.addr[12:16]}
return AddrFrom4Slice(a.addr[12:16])
}
return Address{}
}
// isZeros reports whether a is all zeros.
func isZeros(a Address) bool {
for i := 0; i < a.Len(); i++ {
if a.addr[i] != 0 {
// isZeros reports whether addr is all zeros.
func isZeros(addr []byte) bool {
for _, b := range addr {
if b != 0 {
return false
}
}
@@ -2606,17 +2621,17 @@ func (a AddressWithPrefix) String() string {
// Subnet converts the address and prefix into a Subnet value and returns it.
func (a AddressWithPrefix) Subnet() Subnet {
addrLen := len(a.Address.addr)
addrLen := a.Address.length
if a.PrefixLen <= 0 {
return Subnet{
address: Address{addr: strings.Repeat("\x00", addrLen)},
mask: AddressMask{mask: strings.Repeat("\x00", addrLen)},
address: AddrFromSlice(bytes.Repeat([]byte{0}, addrLen)),
mask: MaskFromBytes(bytes.Repeat([]byte{0}, addrLen)),
}
}
if a.PrefixLen >= addrLen*8 {
return Subnet{
address: a.Address,
mask: AddressMask{mask: strings.Repeat("\xff", addrLen)},
mask: MaskFromBytes(bytes.Repeat([]byte{0xff}, addrLen)),
}
}
@@ -2638,7 +2653,7 @@ func (a AddressWithPrefix) Subnet() Subnet {
// For extra caution, call NewSubnet rather than directly creating the Subnet
// value. If that fails it indicates a serious bug in this code, so panic is
// in order.
s, err := NewSubnet(Address{addr: string(sa)}, AddressMask{mask: string(sm)})
s, err := NewSubnet(AddrFromSlice(sa), MaskFromBytes(sm))
if err != nil {
panic("invalid subnet: " + err.Error())
}
+10 -5
View File
@@ -114,7 +114,8 @@ func TestLoopbackAcceptAllInSubnetUDP(t *testing.T) {
Protocol: header.IPv4ProtocolNumber,
AddressWithPrefix: utils.Ipv4Addr,
}
ipv4Bytes := ipv4ProtocolAddress.AddressWithPrefix.Address.AsSlice()
addrCopy := ipv4ProtocolAddress.AddressWithPrefix.Address
ipv4Bytes := addrCopy.AsSlice()
ipv4Bytes[len(ipv4Bytes)-1]++
otherIPv4Address := tcpip.AddrFromSlice(ipv4Bytes)
@@ -122,7 +123,8 @@ func TestLoopbackAcceptAllInSubnetUDP(t *testing.T) {
Protocol: header.IPv6ProtocolNumber,
AddressWithPrefix: utils.Ipv6Addr,
}
ipv6Bytes := utils.Ipv6Addr.Address.AsSlice()
addrCopy = utils.Ipv6Addr.Address
ipv6Bytes := addrCopy.AsSlice()
ipv6Bytes[len(ipv6Bytes)-1]++
otherIPv6Address := tcpip.AddrFromSlice(ipv6Bytes)
@@ -283,7 +285,8 @@ func TestLoopbackSubnetLifetimeBoundToAddr(t *testing.T) {
Protocol: ipv4.ProtocolNumber,
AddressWithPrefix: utils.Ipv4Addr,
}
addrBytes := utils.Ipv4Addr.Address.AsSlice()
addrCopy := utils.Ipv4Addr.Address
addrBytes := addrCopy.AsSlice()
addrBytes[len(addrBytes)-1]++
otherAddr := tcpip.AddrFromSlice(addrBytes)
@@ -352,7 +355,8 @@ func TestLoopbackAcceptAllInSubnetTCP(t *testing.T) {
AddressWithPrefix: utils.Ipv4Addr,
}
ipv4ProtocolAddress.AddressWithPrefix.PrefixLen = 8
ipv4Bytes := ipv4ProtocolAddress.AddressWithPrefix.Address.AsSlice()
addrCopy := ipv4ProtocolAddress.AddressWithPrefix.Address
ipv4Bytes := addrCopy.AsSlice()
ipv4Bytes[len(ipv4Bytes)-1]++
otherIPv4Address := tcpip.AddrFromSlice(ipv4Bytes)
@@ -360,7 +364,8 @@ func TestLoopbackAcceptAllInSubnetTCP(t *testing.T) {
Protocol: header.IPv6ProtocolNumber,
AddressWithPrefix: utils.Ipv6Addr,
}
ipv6Bytes := utils.Ipv6Addr.Address.AsSlice()
addrCopy = utils.Ipv6Addr.Address
ipv6Bytes := addrCopy.AsSlice()
ipv6Bytes[len(ipv6Bytes)-1]++
otherIPv6Address := tcpip.AddrFromSlice(ipv6Bytes)
+2 -1
View File
@@ -482,5 +482,6 @@ func ipToAddress(ip net.IP) tcpip.Address {
// ipMaskToAddressMask converts IPMask to tcpip.AddressMask, ignoring the
// protocol.
func ipMaskToAddressMask(ipMask net.IPMask) tcpip.AddressMask {
return tcpip.MaskFromBytes(ipToAddress(net.IP(ipMask)).AsSlice())
addr := ipToAddress(net.IP(ipMask))
return tcpip.MaskFromBytes(addr.AsSlice())
}