Move checksum to its own package and optimize bufferv2 checksumming.

Checksum capabilities are logically separate from tcpip header definitions
and operations. It makes sense to extract this logic into its own package.
This is also necessary to avoid circular dependencies with bufferv2.

PiperOrigin-RevId: 473096480
This commit is contained in:
Lucas Manning
2022-09-08 15:17:00 -07:00
committed by gVisor bot
parent f7855b28f0
commit 22ca20c0f1
62 changed files with 611 additions and 471 deletions
+2
View File
@@ -47,6 +47,7 @@ go_library(
"//pkg/pool",
"//pkg/refsvfs2",
"//pkg/sync",
"//pkg/tcpip/checksum",
],
)
@@ -59,6 +60,7 @@ go_test(
library = ":bufferv2",
deps = [
"//pkg/state",
"//pkg/tcpip/checksum",
"@com_github_google_go_cmp//cmp:go_default_library",
],
)
+20
View File
@@ -20,6 +20,8 @@ package bufferv2
import (
"fmt"
"io"
"gvisor.dev/gvisor/pkg/tcpip/checksum"
)
// Buffer is a non-linear buffer.
@@ -443,6 +445,24 @@ func (b *Buffer) SubApply(offset, length int, fn func(*View)) {
}
}
// Checksum calculates a checksum over the buffer's payload starting at offset.
func (b *Buffer) Checksum(offset int) uint16 {
if offset >= int(b.size) {
return 0
}
var v *View
for v = b.data.Front(); v != nil && offset >= v.Size(); v = v.Next() {
offset -= v.Size()
}
var cs checksum.Checksumer
cs.Add(v.AsSlice()[offset:])
for v = v.Next(); v != nil; v = v.Next() {
cs.Add(v.AsSlice())
}
return cs.Checksum()
}
// Merge merges the provided Buffer with this one.
//
// The other Buffer will be appended to v, and other will be empty after this
+22
View File
@@ -19,11 +19,13 @@ import (
"context"
"fmt"
"io"
"math/rand"
"reflect"
"strings"
"testing"
"gvisor.dev/gvisor/pkg/state"
"gvisor.dev/gvisor/pkg/tcpip/checksum"
)
func BenchmarkReadAt(b *testing.B) {
@@ -876,3 +878,23 @@ func TestRangeLen(t *testing.T) {
}
}
}
func TestChecksum(t *testing.T) {
data := make([]byte, 100)
rand.Read(data)
b := MakeWithData(data[:30])
b.appendOwned(NewViewWithData(data[30:70]))
b.appendOwned(NewViewWithData(data[70:]))
for offset := 0; offset < 100; offset++ {
var cs checksum.Checksumer
cs.Add(data[offset:])
dataChecksum := cs.Checksum()
bufChecksum := b.Checksum(offset)
if dataChecksum != bufChecksum {
t.Errorf("(%#v).Checksum(%d) = %d, want %d", b, offset, bufChecksum, dataChecksum)
}
}
}
+1
View File
@@ -10,6 +10,7 @@ go_library(
deps = [
"//pkg/bufferv2",
"//pkg/tcpip",
"//pkg/tcpip/checksum",
"//pkg/tcpip/header",
"//pkg/tcpip/seqnum",
"@com_github_google_go_cmp//cmp:go_default_library",
+3 -2
View File
@@ -25,6 +25,7 @@ import (
"github.com/google/go-cmp/cmp"
"gvisor.dev/gvisor/pkg/bufferv2"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/checksum"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/seqnum"
)
@@ -506,7 +507,7 @@ func TCP(checkers ...TransportChecker) NetworkChecker {
tcp := header.TCP(last.Payload())
payload := tcp.Payload()
payloadChecksum := header.Checksum(payload, 0)
payloadChecksum := checksum.Checksum(payload, 0)
if !tcp.IsChecksumValid(first.SourceAddress(), first.DestinationAddress(), payloadChecksum, uint16(len(payload))) {
t.Errorf("Bad checksum, got = %d", tcp.Checksum())
}
@@ -1042,7 +1043,7 @@ func ICMPv4Checksum() TransportChecker {
}
heldChecksum := icmpv4.Checksum()
icmpv4.SetChecksum(0)
newChecksum := ^header.Checksum(icmpv4, 0)
newChecksum := ^checksum.Checksum(icmpv4, 0)
icmpv4.SetChecksum(heldChecksum)
if heldChecksum != newChecksum {
t.Errorf("unexpected ICMP checksum, got = %d, want = %d", heldChecksum, newChecksum)
+15
View File
@@ -0,0 +1,15 @@
load("//tools:defs.bzl", "go_library", "go_test")
package(licenses = ["notice"])
go_test(
name = "checksum_test",
srcs = ["checksum_test.go"],
library = ":checksum",
)
go_library(
name = "checksum",
srcs = ["checksum.go"],
visibility = ["//visibility:public"],
)
+216
View File
@@ -0,0 +1,216 @@
// 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 checksum provides the implementation of the encoding and decoding of
// network protocol headers.
package checksum
import (
"encoding/binary"
)
// Size is the size of a checksum.
//
// The checksum is held in a uint16 which is 2 bytes.
const Size = 2
// Put puts the checksum in the provided byte slice.
func Put(b []byte, xsum uint16) {
binary.BigEndian.PutUint16(b, xsum)
}
func calculateChecksum(buf []byte, odd bool, initial uint32) (uint16, bool) {
v := initial
if odd {
v += uint32(buf[0])
buf = buf[1:]
}
l := len(buf)
odd = l&1 != 0
if odd {
l--
v += uint32(buf[l]) << 8
}
for i := 0; i < l; i += 2 {
v += (uint32(buf[i]) << 8) + uint32(buf[i+1])
}
return Combine(uint16(v), uint16(v>>16)), odd
}
func unrolledCalculateChecksum(buf []byte, odd bool, initial uint32) (uint16, bool) {
v := initial
if odd {
v += uint32(buf[0])
buf = buf[1:]
}
l := len(buf)
odd = l&1 != 0
if odd {
l--
v += uint32(buf[l]) << 8
}
for (l - 64) >= 0 {
i := 0
v += (uint32(buf[i]) << 8) + uint32(buf[i+1])
v += (uint32(buf[i+2]) << 8) + uint32(buf[i+3])
v += (uint32(buf[i+4]) << 8) + uint32(buf[i+5])
v += (uint32(buf[i+6]) << 8) + uint32(buf[i+7])
v += (uint32(buf[i+8]) << 8) + uint32(buf[i+9])
v += (uint32(buf[i+10]) << 8) + uint32(buf[i+11])
v += (uint32(buf[i+12]) << 8) + uint32(buf[i+13])
v += (uint32(buf[i+14]) << 8) + uint32(buf[i+15])
i += 16
v += (uint32(buf[i]) << 8) + uint32(buf[i+1])
v += (uint32(buf[i+2]) << 8) + uint32(buf[i+3])
v += (uint32(buf[i+4]) << 8) + uint32(buf[i+5])
v += (uint32(buf[i+6]) << 8) + uint32(buf[i+7])
v += (uint32(buf[i+8]) << 8) + uint32(buf[i+9])
v += (uint32(buf[i+10]) << 8) + uint32(buf[i+11])
v += (uint32(buf[i+12]) << 8) + uint32(buf[i+13])
v += (uint32(buf[i+14]) << 8) + uint32(buf[i+15])
i += 16
v += (uint32(buf[i]) << 8) + uint32(buf[i+1])
v += (uint32(buf[i+2]) << 8) + uint32(buf[i+3])
v += (uint32(buf[i+4]) << 8) + uint32(buf[i+5])
v += (uint32(buf[i+6]) << 8) + uint32(buf[i+7])
v += (uint32(buf[i+8]) << 8) + uint32(buf[i+9])
v += (uint32(buf[i+10]) << 8) + uint32(buf[i+11])
v += (uint32(buf[i+12]) << 8) + uint32(buf[i+13])
v += (uint32(buf[i+14]) << 8) + uint32(buf[i+15])
i += 16
v += (uint32(buf[i]) << 8) + uint32(buf[i+1])
v += (uint32(buf[i+2]) << 8) + uint32(buf[i+3])
v += (uint32(buf[i+4]) << 8) + uint32(buf[i+5])
v += (uint32(buf[i+6]) << 8) + uint32(buf[i+7])
v += (uint32(buf[i+8]) << 8) + uint32(buf[i+9])
v += (uint32(buf[i+10]) << 8) + uint32(buf[i+11])
v += (uint32(buf[i+12]) << 8) + uint32(buf[i+13])
v += (uint32(buf[i+14]) << 8) + uint32(buf[i+15])
buf = buf[64:]
l = l - 64
}
if (l - 32) >= 0 {
i := 0
v += (uint32(buf[i]) << 8) + uint32(buf[i+1])
v += (uint32(buf[i+2]) << 8) + uint32(buf[i+3])
v += (uint32(buf[i+4]) << 8) + uint32(buf[i+5])
v += (uint32(buf[i+6]) << 8) + uint32(buf[i+7])
v += (uint32(buf[i+8]) << 8) + uint32(buf[i+9])
v += (uint32(buf[i+10]) << 8) + uint32(buf[i+11])
v += (uint32(buf[i+12]) << 8) + uint32(buf[i+13])
v += (uint32(buf[i+14]) << 8) + uint32(buf[i+15])
i += 16
v += (uint32(buf[i]) << 8) + uint32(buf[i+1])
v += (uint32(buf[i+2]) << 8) + uint32(buf[i+3])
v += (uint32(buf[i+4]) << 8) + uint32(buf[i+5])
v += (uint32(buf[i+6]) << 8) + uint32(buf[i+7])
v += (uint32(buf[i+8]) << 8) + uint32(buf[i+9])
v += (uint32(buf[i+10]) << 8) + uint32(buf[i+11])
v += (uint32(buf[i+12]) << 8) + uint32(buf[i+13])
v += (uint32(buf[i+14]) << 8) + uint32(buf[i+15])
buf = buf[32:]
l = l - 32
}
if (l - 16) >= 0 {
i := 0
v += (uint32(buf[i]) << 8) + uint32(buf[i+1])
v += (uint32(buf[i+2]) << 8) + uint32(buf[i+3])
v += (uint32(buf[i+4]) << 8) + uint32(buf[i+5])
v += (uint32(buf[i+6]) << 8) + uint32(buf[i+7])
v += (uint32(buf[i+8]) << 8) + uint32(buf[i+9])
v += (uint32(buf[i+10]) << 8) + uint32(buf[i+11])
v += (uint32(buf[i+12]) << 8) + uint32(buf[i+13])
v += (uint32(buf[i+14]) << 8) + uint32(buf[i+15])
buf = buf[16:]
l = l - 16
}
if (l - 8) >= 0 {
i := 0
v += (uint32(buf[i]) << 8) + uint32(buf[i+1])
v += (uint32(buf[i+2]) << 8) + uint32(buf[i+3])
v += (uint32(buf[i+4]) << 8) + uint32(buf[i+5])
v += (uint32(buf[i+6]) << 8) + uint32(buf[i+7])
buf = buf[8:]
l = l - 8
}
if (l - 4) >= 0 {
i := 0
v += (uint32(buf[i]) << 8) + uint32(buf[i+1])
v += (uint32(buf[i+2]) << 8) + uint32(buf[i+3])
buf = buf[4:]
l = l - 4
}
// At this point since l was even before we started unrolling
// there can be only two bytes left to add.
if l != 0 {
v += (uint32(buf[0]) << 8) + uint32(buf[1])
}
return Combine(uint16(v), uint16(v>>16)), odd
}
// Old calculates the checksum (as defined in RFC 1071) of the bytes in
// the given byte array. This function uses a non-optimized implementation. Its
// only retained for reference and to use as a benchmark/test. Most code should
// use the header.Checksum function.
//
// The initial checksum must have been computed on an even number of bytes.
func Old(buf []byte, initial uint16) uint16 {
s, _ := calculateChecksum(buf, false, uint32(initial))
return s
}
// Checksum calculates the checksum (as defined in RFC 1071) of the bytes in the
// given byte array. This function uses an optimized unrolled version of the
// checksum algorithm.
//
// The initial checksum must have been computed on an even number of bytes.
func Checksum(buf []byte, initial uint16) uint16 {
s, _ := unrolledCalculateChecksum(buf, false, uint32(initial))
return s
}
// Checksumer calculates checksum defined in RFC 1071.
type Checksumer struct {
sum uint16
odd bool
}
// Add adds b to checksum.
func (c *Checksumer) Add(b []byte) {
if len(b) > 0 {
c.sum, c.odd = unrolledCalculateChecksum(b, c.odd, uint32(c.sum))
}
}
// Checksum returns the latest checksum value.
func (c *Checksumer) Checksum() uint16 {
return c.sum
}
// Combine combines the two uint16 to form their checksum. This is done
// by adding them and the carry.
//
// Note that checksum a must have been computed on an even number of bytes.
func Combine(a, b uint16) uint16 {
v := uint32(a) + uint32(b)
return uint16(v + v>>16)
}
+155
View File
@@ -0,0 +1,155 @@
// 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.
// Package header provides the implementation of the encoding and decoding of
// network protocol headers.
package checksum
import (
"bytes"
"fmt"
"math/rand"
"testing"
)
func TestChecksumer(t *testing.T) {
testCases := []struct {
name string
data [][]byte
want uint16
}{
{
name: "empty",
want: 0,
},
{
name: "OneOddView",
data: [][]byte{
{1, 9, 0, 5, 4},
},
want: 1294,
},
{
name: "TwoOddViews",
data: [][]byte{
{1, 9, 0, 5, 4},
{4, 3, 7, 1, 2, 123},
},
want: 33819,
},
{
name: "OneEvenView",
data: [][]byte{
{1, 9, 0, 5},
},
want: 270,
},
{
name: "TwoEvenViews",
data: [][]byte{
[]byte{98, 1, 9, 0},
[]byte{9, 0, 5, 4},
},
want: 30981,
},
{
name: "ThreeViews",
data: [][]byte{
{77, 11, 33, 0, 55, 44},
{98, 1, 9, 0, 5, 4},
{4, 3, 7, 1, 2, 123, 99},
},
want: 34236,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
var all bytes.Buffer
var c Checksumer
for _, b := range tc.data {
c.Add(b)
// Append to the buffer. We will check the checksum as a whole later.
if _, err := all.Write(b); err != nil {
t.Fatalf("all.Write(b) = _, %s; want _, nil", err)
}
}
if got, want := c.Checksum(), tc.want; got != want {
t.Errorf("c.Checksum() = %d, want %d", got, want)
}
if got, want := Checksum(all.Bytes(), 0 /* initial */), tc.want; got != want {
t.Errorf("Checksum(flatten tc.data) = %d, want %d", got, want)
}
})
}
}
func TestChecksum(t *testing.T) {
var bufSizes = []int{0, 1, 2, 3, 4, 7, 8, 15, 16, 31, 32, 63, 64, 127, 128, 255, 256, 257, 1023, 1024}
type testCase struct {
buf []byte
initial uint16
csumOrig uint16
csumNew uint16
}
testCases := make([]testCase, 100000)
// Ensure same buffer generation for test consistency.
rnd := rand.New(rand.NewSource(42))
for i := range testCases {
testCases[i].buf = make([]byte, bufSizes[i%len(bufSizes)])
testCases[i].initial = uint16(rnd.Intn(65536))
rnd.Read(testCases[i].buf)
}
for i := range testCases {
testCases[i].csumOrig = Old(testCases[i].buf, testCases[i].initial)
testCases[i].csumNew = Checksum(testCases[i].buf, testCases[i].initial)
if got, want := testCases[i].csumNew, testCases[i].csumOrig; got != want {
t.Fatalf("new checksum for (buf = %x, initial = %d) does not match old got: %d, want: %d", testCases[i].buf, testCases[i].initial, got, want)
}
}
}
func BenchmarkChecksum(b *testing.B) {
var bufSizes = []int{64, 128, 256, 512, 1024, 1500, 2048, 4096, 8192, 16384, 32767, 32768, 65535, 65536}
checkSumImpls := []struct {
fn func([]byte, uint16) uint16
name string
}{
{Old, fmt.Sprintf("checksum_old")},
{Checksum, fmt.Sprintf("checksum")},
}
for _, csumImpl := range checkSumImpls {
// Ensure same buffer generation for test consistency.
rnd := rand.New(rand.NewSource(42))
for _, bufSz := range bufSizes {
b.Run(fmt.Sprintf("%s_%d", csumImpl.name, bufSz), func(b *testing.B) {
tc := struct {
buf []byte
initial uint16
csum uint16
}{
buf: make([]byte, bufSz),
initial: uint16(rnd.Intn(65536)),
}
rnd.Read(tc.buf)
b.ResetTimer()
for i := 0; i < b.N; i++ {
tc.csum = csumImpl.fn(tc.buf, tc.initial)
}
})
}
}
}
+2
View File
@@ -32,6 +32,7 @@ go_library(
deps = [
"//pkg/bufferv2",
"//pkg/tcpip",
"//pkg/tcpip/checksum",
"//pkg/tcpip/seqnum",
"@com_github_google_btree//:go_default_library",
],
@@ -53,6 +54,7 @@ go_test(
"//pkg/bufferv2",
"//pkg/rand",
"//pkg/tcpip",
"//pkg/tcpip/checksum",
"//pkg/tcpip/prependable",
"//pkg/tcpip/testutil",
"@com_github_google_go_cmp//cmp:go_default_library",
+6 -213
View File
@@ -20,230 +20,23 @@ import (
"encoding/binary"
"fmt"
"gvisor.dev/gvisor/pkg/bufferv2"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/checksum"
)
// ChecksumSize is the size of a checksum.
//
// The checksum is held in a uint16 which is 2 bytes.
const ChecksumSize = 2
// PutChecksum puts the checksum in the provided byte slice.
func PutChecksum(b []byte, xsum uint16) {
binary.BigEndian.PutUint16(b, xsum)
}
func calculateChecksum(buf []byte, odd bool, initial uint32) (uint16, bool) {
v := initial
if odd {
v += uint32(buf[0])
buf = buf[1:]
}
l := len(buf)
odd = l&1 != 0
if odd {
l--
v += uint32(buf[l]) << 8
}
for i := 0; i < l; i += 2 {
v += (uint32(buf[i]) << 8) + uint32(buf[i+1])
}
return ChecksumCombine(uint16(v), uint16(v>>16)), odd
}
func unrolledCalculateChecksum(buf []byte, odd bool, initial uint32) (uint16, bool) {
v := initial
if odd {
v += uint32(buf[0])
buf = buf[1:]
}
l := len(buf)
odd = l&1 != 0
if odd {
l--
v += uint32(buf[l]) << 8
}
for (l - 64) >= 0 {
i := 0
v += (uint32(buf[i]) << 8) + uint32(buf[i+1])
v += (uint32(buf[i+2]) << 8) + uint32(buf[i+3])
v += (uint32(buf[i+4]) << 8) + uint32(buf[i+5])
v += (uint32(buf[i+6]) << 8) + uint32(buf[i+7])
v += (uint32(buf[i+8]) << 8) + uint32(buf[i+9])
v += (uint32(buf[i+10]) << 8) + uint32(buf[i+11])
v += (uint32(buf[i+12]) << 8) + uint32(buf[i+13])
v += (uint32(buf[i+14]) << 8) + uint32(buf[i+15])
i += 16
v += (uint32(buf[i]) << 8) + uint32(buf[i+1])
v += (uint32(buf[i+2]) << 8) + uint32(buf[i+3])
v += (uint32(buf[i+4]) << 8) + uint32(buf[i+5])
v += (uint32(buf[i+6]) << 8) + uint32(buf[i+7])
v += (uint32(buf[i+8]) << 8) + uint32(buf[i+9])
v += (uint32(buf[i+10]) << 8) + uint32(buf[i+11])
v += (uint32(buf[i+12]) << 8) + uint32(buf[i+13])
v += (uint32(buf[i+14]) << 8) + uint32(buf[i+15])
i += 16
v += (uint32(buf[i]) << 8) + uint32(buf[i+1])
v += (uint32(buf[i+2]) << 8) + uint32(buf[i+3])
v += (uint32(buf[i+4]) << 8) + uint32(buf[i+5])
v += (uint32(buf[i+6]) << 8) + uint32(buf[i+7])
v += (uint32(buf[i+8]) << 8) + uint32(buf[i+9])
v += (uint32(buf[i+10]) << 8) + uint32(buf[i+11])
v += (uint32(buf[i+12]) << 8) + uint32(buf[i+13])
v += (uint32(buf[i+14]) << 8) + uint32(buf[i+15])
i += 16
v += (uint32(buf[i]) << 8) + uint32(buf[i+1])
v += (uint32(buf[i+2]) << 8) + uint32(buf[i+3])
v += (uint32(buf[i+4]) << 8) + uint32(buf[i+5])
v += (uint32(buf[i+6]) << 8) + uint32(buf[i+7])
v += (uint32(buf[i+8]) << 8) + uint32(buf[i+9])
v += (uint32(buf[i+10]) << 8) + uint32(buf[i+11])
v += (uint32(buf[i+12]) << 8) + uint32(buf[i+13])
v += (uint32(buf[i+14]) << 8) + uint32(buf[i+15])
buf = buf[64:]
l = l - 64
}
if (l - 32) >= 0 {
i := 0
v += (uint32(buf[i]) << 8) + uint32(buf[i+1])
v += (uint32(buf[i+2]) << 8) + uint32(buf[i+3])
v += (uint32(buf[i+4]) << 8) + uint32(buf[i+5])
v += (uint32(buf[i+6]) << 8) + uint32(buf[i+7])
v += (uint32(buf[i+8]) << 8) + uint32(buf[i+9])
v += (uint32(buf[i+10]) << 8) + uint32(buf[i+11])
v += (uint32(buf[i+12]) << 8) + uint32(buf[i+13])
v += (uint32(buf[i+14]) << 8) + uint32(buf[i+15])
i += 16
v += (uint32(buf[i]) << 8) + uint32(buf[i+1])
v += (uint32(buf[i+2]) << 8) + uint32(buf[i+3])
v += (uint32(buf[i+4]) << 8) + uint32(buf[i+5])
v += (uint32(buf[i+6]) << 8) + uint32(buf[i+7])
v += (uint32(buf[i+8]) << 8) + uint32(buf[i+9])
v += (uint32(buf[i+10]) << 8) + uint32(buf[i+11])
v += (uint32(buf[i+12]) << 8) + uint32(buf[i+13])
v += (uint32(buf[i+14]) << 8) + uint32(buf[i+15])
buf = buf[32:]
l = l - 32
}
if (l - 16) >= 0 {
i := 0
v += (uint32(buf[i]) << 8) + uint32(buf[i+1])
v += (uint32(buf[i+2]) << 8) + uint32(buf[i+3])
v += (uint32(buf[i+4]) << 8) + uint32(buf[i+5])
v += (uint32(buf[i+6]) << 8) + uint32(buf[i+7])
v += (uint32(buf[i+8]) << 8) + uint32(buf[i+9])
v += (uint32(buf[i+10]) << 8) + uint32(buf[i+11])
v += (uint32(buf[i+12]) << 8) + uint32(buf[i+13])
v += (uint32(buf[i+14]) << 8) + uint32(buf[i+15])
buf = buf[16:]
l = l - 16
}
if (l - 8) >= 0 {
i := 0
v += (uint32(buf[i]) << 8) + uint32(buf[i+1])
v += (uint32(buf[i+2]) << 8) + uint32(buf[i+3])
v += (uint32(buf[i+4]) << 8) + uint32(buf[i+5])
v += (uint32(buf[i+6]) << 8) + uint32(buf[i+7])
buf = buf[8:]
l = l - 8
}
if (l - 4) >= 0 {
i := 0
v += (uint32(buf[i]) << 8) + uint32(buf[i+1])
v += (uint32(buf[i+2]) << 8) + uint32(buf[i+3])
buf = buf[4:]
l = l - 4
}
// At this point since l was even before we started unrolling
// there can be only two bytes left to add.
if l != 0 {
v += (uint32(buf[0]) << 8) + uint32(buf[1])
}
return ChecksumCombine(uint16(v), uint16(v>>16)), odd
}
// ChecksumOld calculates the checksum (as defined in RFC 1071) of the bytes in
// the given byte array. This function uses a non-optimized implementation. Its
// only retained for reference and to use as a benchmark/test. Most code should
// use the header.Checksum function.
//
// The initial checksum must have been computed on an even number of bytes.
func ChecksumOld(buf []byte, initial uint16) uint16 {
s, _ := calculateChecksum(buf, false, uint32(initial))
return s
}
// Checksum calculates the checksum (as defined in RFC 1071) of the bytes in the
// given byte array. This function uses an optimized unrolled version of the
// checksum algorithm.
//
// The initial checksum must have been computed on an even number of bytes.
func Checksum(buf []byte, initial uint16) uint16 {
s, _ := unrolledCalculateChecksum(buf, false, uint32(initial))
return s
}
// ChecksumBuffer calculates the checksum (as defined in RFC 1071) of the
// bytes in the given Buffer.
//
// The initial checksum must have been computed on an even number of bytes.
func ChecksumBuffer(buf bufferv2.Buffer, initial uint16) uint16 {
var c Checksumer
buf.Apply(func(v *bufferv2.View) {
c.Add(v.AsSlice())
})
return ChecksumCombine(initial, c.Checksum())
}
// Checksumer calculates checksum defined in RFC 1071.
type Checksumer struct {
sum uint16
odd bool
}
// Add adds b to checksum.
func (c *Checksumer) Add(b []byte) {
if len(b) > 0 {
c.sum, c.odd = unrolledCalculateChecksum(b, c.odd, uint32(c.sum))
}
}
// Checksum returns the latest checksum value.
func (c *Checksumer) Checksum() uint16 {
return c.sum
}
// ChecksumCombine combines the two uint16 to form their checksum. This is done
// by adding them and the carry.
//
// Note that checksum a must have been computed on an even number of bytes.
func ChecksumCombine(a, b uint16) uint16 {
v := uint32(a) + uint32(b)
return uint16(v + v>>16)
}
// PseudoHeaderChecksum calculates the pseudo-header checksum for the given
// destination protocol and network address. Pseudo-headers are needed by
// transport layers when calculating their own checksum.
func PseudoHeaderChecksum(protocol tcpip.TransportProtocolNumber, srcAddr tcpip.Address, dstAddr tcpip.Address, totalLen uint16) uint16 {
xsum := Checksum([]byte(srcAddr), 0)
xsum = Checksum([]byte(dstAddr), xsum)
xsum := checksum.Checksum([]byte(srcAddr), 0)
xsum = checksum.Checksum([]byte(dstAddr), xsum)
// Add the length portion of the checksum to the pseudo-checksum.
tmp := make([]byte, 2)
binary.BigEndian.PutUint16(tmp, totalLen)
xsum = Checksum(tmp, xsum)
xsum = checksum.Checksum(tmp, xsum)
return Checksum([]byte{0, uint8(protocol)}, xsum)
return checksum.Checksum([]byte{0, uint8(protocol)}, xsum)
}
// checksumUpdate2ByteAlignedUint16 updates a uint16 value in a calculated
@@ -264,7 +57,7 @@ func checksumUpdate2ByteAlignedUint16(xsum, old, new uint16) uint16 {
// checksum C, the new checksum C' is:
//
// C' = C + (-m) + m' = C + (m' - m)
return ChecksumCombine(xsum, ChecksumCombine(new, ^old))
return checksum.Combine(xsum, checksum.Combine(new, ^old))
}
// checksumUpdate2ByteAlignedAddress updates an address in a calculated
+7 -138
View File
@@ -17,7 +17,6 @@
package header_test
import (
"bytes"
"fmt"
"math/rand"
"sync"
@@ -25,140 +24,10 @@ import (
"gvisor.dev/gvisor/pkg/bufferv2"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/checksum"
"gvisor.dev/gvisor/pkg/tcpip/header"
)
func TestChecksumer(t *testing.T) {
testCases := []struct {
name string
data [][]byte
want uint16
}{
{
name: "empty",
want: 0,
},
{
name: "OneOddView",
data: [][]byte{
{1, 9, 0, 5, 4},
},
want: 1294,
},
{
name: "TwoOddViews",
data: [][]byte{
{1, 9, 0, 5, 4},
{4, 3, 7, 1, 2, 123},
},
want: 33819,
},
{
name: "OneEvenView",
data: [][]byte{
{1, 9, 0, 5},
},
want: 270,
},
{
name: "TwoEvenViews",
data: [][]byte{
[]byte{98, 1, 9, 0},
[]byte{9, 0, 5, 4},
},
want: 30981,
},
{
name: "ThreeViews",
data: [][]byte{
{77, 11, 33, 0, 55, 44},
{98, 1, 9, 0, 5, 4},
{4, 3, 7, 1, 2, 123, 99},
},
want: 34236,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
var all bytes.Buffer
var c header.Checksumer
for _, b := range tc.data {
c.Add(b)
// Append to the buffer. We will check the checksum as a whole later.
if _, err := all.Write(b); err != nil {
t.Fatalf("all.Write(b) = _, %s; want _, nil", err)
}
}
if got, want := c.Checksum(), tc.want; got != want {
t.Errorf("c.Checksum() = %d, want %d", got, want)
}
if got, want := header.Checksum(all.Bytes(), 0 /* initial */), tc.want; got != want {
t.Errorf("Checksum(flatten tc.data) = %d, want %d", got, want)
}
})
}
}
func TestChecksum(t *testing.T) {
var bufSizes = []int{0, 1, 2, 3, 4, 7, 8, 15, 16, 31, 32, 63, 64, 127, 128, 255, 256, 257, 1023, 1024}
type testCase struct {
buf []byte
initial uint16
csumOrig uint16
csumNew uint16
}
testCases := make([]testCase, 100000)
// Ensure same buffer generation for test consistency.
rnd := rand.New(rand.NewSource(42))
for i := range testCases {
testCases[i].buf = make([]byte, bufSizes[i%len(bufSizes)])
testCases[i].initial = uint16(rnd.Intn(65536))
rnd.Read(testCases[i].buf)
}
for i := range testCases {
testCases[i].csumOrig = header.ChecksumOld(testCases[i].buf, testCases[i].initial)
testCases[i].csumNew = header.Checksum(testCases[i].buf, testCases[i].initial)
if got, want := testCases[i].csumNew, testCases[i].csumOrig; got != want {
t.Fatalf("new checksum for (buf = %x, initial = %d) does not match old got: %d, want: %d", testCases[i].buf, testCases[i].initial, got, want)
}
}
}
func BenchmarkChecksum(b *testing.B) {
var bufSizes = []int{64, 128, 256, 512, 1024, 1500, 2048, 4096, 8192, 16384, 32767, 32768, 65535, 65536}
checkSumImpls := []struct {
fn func([]byte, uint16) uint16
name string
}{
{header.ChecksumOld, fmt.Sprintf("checksum_old")},
{header.Checksum, fmt.Sprintf("checksum")},
}
for _, csumImpl := range checkSumImpls {
// Ensure same buffer generation for test consistency.
rnd := rand.New(rand.NewSource(42))
for _, bufSz := range bufSizes {
b.Run(fmt.Sprintf("%s_%d", csumImpl.name, bufSz), func(b *testing.B) {
tc := struct {
buf []byte
initial uint16
csum uint16
}{
buf: make([]byte, bufSz),
initial: uint16(rnd.Intn(65536)),
}
rnd.Read(tc.buf)
b.ResetTimer()
for i := 0; i < b.N; i++ {
tc.csum = csumImpl.fn(tc.buf, tc.initial)
}
})
}
}
}
func testICMPChecksum(t *testing.T, headerChecksum func() uint16, icmpChecksum func() uint16, want uint16, pktStr string) {
// icmpChecksum should not do any modifications of the header to
// calculate its checksum. Let's call it from a few go-routines and the
@@ -211,12 +80,12 @@ func TestICMPv4Checksum(t *testing.T) {
b := bufferv2.MakeWithData(buf[:5])
b.Append(bufferv2.NewViewWithData(buf[5:]))
want := header.Checksum(b.Flatten(), 0)
want = ^header.Checksum(h, want)
want := checksum.Checksum(b.Flatten(), 0)
want = ^checksum.Checksum(h, want)
h.SetChecksum(want)
testICMPChecksum(t, h.Checksum, func() uint16 {
return header.ICMPv4Checksum(h, header.ChecksumBuffer(b, 0))
return header.ICMPv4Checksum(h, b.Checksum(0))
}, want, fmt.Sprintf("header: {% x} data {% x}", h, b.Flatten()))
}
@@ -241,8 +110,8 @@ func TestICMPv6Checksum(t *testing.T) {
src := header.IPv6Loopback
want := header.PseudoHeaderChecksum(header.ICMPv6ProtocolNumber, src, dst, uint16(len(h)+int(b.Size())))
want = header.Checksum(b.Flatten(), want)
want = ^header.Checksum(h, want)
want = checksum.Checksum(b.Flatten(), want)
want = ^checksum.Checksum(h, want)
h.SetChecksum(want)
testICMPChecksum(t, h.Checksum, func() uint16 {
@@ -250,7 +119,7 @@ func TestICMPv6Checksum(t *testing.T) {
Header: h,
Src: src,
Dst: dst,
PayloadCsum: header.ChecksumBuffer(b, 0),
PayloadCsum: b.Checksum(0),
PayloadLen: int(b.Size()),
})
}, want, fmt.Sprintf("header: {% x} data {% x}", h, b.Flatten()))
+5 -4
View File
@@ -18,6 +18,7 @@ import (
"encoding/binary"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/checksum"
)
// ICMPv4 represents an ICMPv4 header stored in a byte array.
@@ -142,8 +143,8 @@ func (b ICMPv4) Checksum() uint16 {
}
// SetChecksum sets the ICMP checksum field.
func (b ICMPv4) SetChecksum(checksum uint16) {
PutChecksum(b[icmpv4ChecksumOffset:], checksum)
func (b ICMPv4) SetChecksum(cs uint16) {
checksum.Put(b[icmpv4ChecksumOffset:], cs)
}
// SourcePort implements Transport.SourcePort.
@@ -212,8 +213,8 @@ func ICMPv4Checksum(h ICMPv4, payloadCsum uint16) uint16 {
xsum := payloadCsum
// h[2:4] is the checksum itself, skip it to avoid checksumming the checksum.
xsum = Checksum(h[:2], xsum)
xsum = Checksum(h[4:], xsum)
xsum = checksum.Checksum(h[:2], xsum)
xsum = checksum.Checksum(h[4:], xsum)
return ^xsum
}
+6 -5
View File
@@ -18,6 +18,7 @@ import (
"encoding/binary"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/checksum"
)
// ICMPv6 represents an ICMPv6 header stored in a byte array.
@@ -198,8 +199,8 @@ func (b ICMPv6) Checksum() uint16 {
}
// SetChecksum sets the ICMP checksum field.
func (b ICMPv6) SetChecksum(checksum uint16) {
PutChecksum(b[ICMPv6ChecksumOffset:], checksum)
func (b ICMPv6) SetChecksum(cs uint16) {
checksum.Put(b[ICMPv6ChecksumOffset:], cs)
}
// SourcePort implements Transport.SourcePort.
@@ -283,11 +284,11 @@ func ICMPv6Checksum(params ICMPv6ChecksumParams) uint16 {
h := params.Header
xsum := PseudoHeaderChecksum(ICMPv6ProtocolNumber, params.Src, params.Dst, uint16(len(h)+params.PayloadLen))
xsum = ChecksumCombine(xsum, params.PayloadCsum)
xsum = checksum.Combine(xsum, params.PayloadCsum)
// h[2:4] is the checksum itself, skip it to avoid checksumming the checksum.
xsum = Checksum(h[:2], xsum)
xsum = Checksum(h[4:], xsum)
xsum = checksum.Checksum(h[:2], xsum)
xsum = checksum.Checksum(h[4:], xsum)
return ^xsum
}
+2 -1
View File
@@ -20,6 +20,7 @@ import (
"time"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/checksum"
)
// IGMP represents an IGMP header stored in a byte array.
@@ -169,7 +170,7 @@ func IGMPCalculateChecksum(h IGMP) uint16 {
// the checksum and replace it afterwards.
existingXsum := h.Checksum()
h.SetChecksum(0)
xsum := ^Checksum(h, 0)
xsum := ^checksum.Checksum(h, 0)
h.SetChecksum(existingXsum)
return xsum
}
+4 -3
View File
@@ -18,6 +18,7 @@ import (
"testing"
"time"
"gvisor.dev/gvisor/pkg/tcpip/checksum"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/testutil"
)
@@ -94,11 +95,11 @@ func TestIGMPChecksum(t *testing.T) {
// to avoid checksumming the checksum.
initialChecksum := igmpHeader.Checksum()
igmpHeader.SetChecksum(0)
checksum := ^header.Checksum(b, 0)
xsum := ^checksum.Checksum(b, 0)
igmpHeader.SetChecksum(initialChecksum)
if got := header.IGMPCalculateChecksum(igmpHeader); got != checksum {
t.Errorf("got IGMPCalculateChecksum = %x, want %x", got, checksum)
if got := header.IGMPCalculateChecksum(igmpHeader); got != xsum {
t.Errorf("got IGMPCalculateChecksum = %x, want %x", got, xsum)
}
}
+7 -6
View File
@@ -20,6 +20,7 @@ import (
"time"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/checksum"
)
// RFC 971 defines the fields of the IPv4 header on page 11 using the following
@@ -50,7 +51,7 @@ const (
flagsFO = 6
ttl = 8
protocol = 9
checksum = 10
xsum = 10
srcAddr = 12
dstAddr = 16
options = 20
@@ -301,7 +302,7 @@ func (b IPv4) TotalLength() uint16 {
// Checksum returns the checksum field of the IPv4 header.
func (b IPv4) Checksum() uint16 {
return binary.BigEndian.Uint16(b[checksum:])
return binary.BigEndian.Uint16(b[xsum:])
}
// SourceAddress returns the "source address" field of the IPv4 header.
@@ -382,7 +383,7 @@ func (b IPv4) SetTotalLength(totalLength uint16) {
// SetChecksum sets the checksum field of the IPv4 header.
func (b IPv4) SetChecksum(v uint16) {
PutChecksum(b[checksum:], v)
checksum.Put(b[xsum:], v)
}
// SetFlagsFragmentOffset sets the "flags" and "fragment offset" fields of the
@@ -410,7 +411,7 @@ func (b IPv4) SetDestinationAddress(addr tcpip.Address) {
// CalculateChecksum calculates the checksum of the IPv4 header.
func (b IPv4) CalculateChecksum() uint16 {
return Checksum(b[:b.HeaderLength()], 0)
return checksum.Checksum(b[:b.HeaderLength()], 0)
}
// Encode encodes all the fields of the IPv4 header.
@@ -444,8 +445,8 @@ func (b IPv4) Encode(i *IPv4Fields) {
// packets are produced.
func (b IPv4) EncodePartial(partialChecksum, totalLength uint16) {
b.SetTotalLength(totalLength)
checksum := Checksum(b[IPv4TotalLenOffset:IPv4TotalLenOffset+2], partialChecksum)
b.SetChecksum(^checksum)
xsum := checksum.Checksum(b[IPv4TotalLenOffset:IPv4TotalLenOffset+2], partialChecksum)
b.SetChecksum(^xsum)
}
// IsValid performs basic validation on the packet.
+9 -8
View File
@@ -19,6 +19,7 @@ import (
"github.com/google/btree"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/checksum"
"gvisor.dev/gvisor/pkg/tcpip/seqnum"
)
@@ -300,8 +301,8 @@ func (b TCP) SetDestinationPort(port uint16) {
}
// SetChecksum sets the checksum field of the TCP header.
func (b TCP) SetChecksum(checksum uint16) {
PutChecksum(b[TCPChecksumOffset:], checksum)
func (b TCP) SetChecksum(xsum uint16) {
checksum.Put(b[TCPChecksumOffset:], xsum)
}
// SetDataOffset sets the data offset field of the TCP header. headerLen should
@@ -340,13 +341,13 @@ func (b TCP) SetUrgentPointer(urgentPointer uint16) {
// and the checksum of the segment data.
func (b TCP) CalculateChecksum(partialChecksum uint16) uint16 {
// Calculate the rest of the checksum.
return Checksum(b[:b.DataOffset()], partialChecksum)
return checksum.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)
xsum = checksum.Combine(xsum, payloadChecksum)
return b.CalculateChecksum(xsum) == 0xffff
}
@@ -389,17 +390,17 @@ func (b TCP) EncodePartial(partialChecksum, length uint16, seqnum, acknum uint32
tmp := make([]byte, 4)
binary.BigEndian.PutUint16(tmp, length)
binary.BigEndian.PutUint16(tmp[2:], uint16(flags))
checksum := Checksum(tmp, partialChecksum)
xsum := checksum.Checksum(tmp, partialChecksum)
// Encode the passed-in fields.
b.encodeSubset(seqnum, acknum, flags, rcvwnd)
// Add the contributions of the passed-in fields to the checksum.
checksum = Checksum(b[TCPSeqNumOffset:TCPSeqNumOffset+8], checksum)
checksum = Checksum(b[TCPWinSizeOffset:TCPWinSizeOffset+2], checksum)
xsum = checksum.Checksum(b[TCPSeqNumOffset:TCPSeqNumOffset+8], xsum)
xsum = checksum.Checksum(b[TCPWinSizeOffset:TCPWinSizeOffset+2], xsum)
// Encode the checksum.
b.SetChecksum(^checksum)
b.SetChecksum(^xsum)
}
// SetSourcePortWithChecksumUpdate implements ChecksummableTransport.
+5 -4
View File
@@ -19,6 +19,7 @@ import (
"math"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/checksum"
)
const (
@@ -100,8 +101,8 @@ func (b UDP) SetDestinationPort(port uint16) {
}
// SetChecksum sets the "checksum" field of the UDP header.
func (b UDP) SetChecksum(checksum uint16) {
PutChecksum(b[udpChecksum:], checksum)
func (b UDP) SetChecksum(xsum uint16) {
checksum.Put(b[udpChecksum:], xsum)
}
// SetLength sets the "length" field of the UDP header.
@@ -113,13 +114,13 @@ func (b UDP) SetLength(length uint16) {
// 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)
return checksum.Checksum(b[:UDPMinimumSize], partialChecksum)
}
// 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)
xsum = checksum.Combine(xsum, payloadChecksum)
return b.CalculateChecksum(xsum) == 0xffff
}
+1
View File
@@ -17,6 +17,7 @@ go_test(
"//pkg/sync",
"//pkg/tcpip",
"//pkg/tcpip/checker",
"//pkg/tcpip/checksum",
"//pkg/tcpip/faketime",
"//pkg/tcpip/header",
"//pkg/tcpip/link/channel",
+4 -3
View File
@@ -26,6 +26,7 @@ import (
"gvisor.dev/gvisor/pkg/sync"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/checker"
"gvisor.dev/gvisor/pkg/tcpip/checksum"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/link/channel"
"gvisor.dev/gvisor/pkg/tcpip/link/loopback"
@@ -381,7 +382,7 @@ func TestSourceAddressValidation(t *testing.T) {
pkt.SetType(header.ICMPv4Echo)
pkt.SetCode(0)
pkt.SetChecksum(0)
pkt.SetChecksum(^header.Checksum(pkt, 0))
pkt.SetChecksum(^checksum.Checksum(pkt, 0))
ip := header.IPv4(hdr.Prepend(header.IPv4MinimumSize))
ip.Encode(&header.IPv4Fields{
TotalLength: uint16(totalLen),
@@ -916,8 +917,8 @@ func TestIPv4ReceiveControl(t *testing.T) {
}
icmp.SetChecksum(0)
checksum := ^header.Checksum(icmp, 0 /* initial */)
icmp.SetChecksum(checksum)
xsum := ^checksum.Checksum(icmp, 0 /* initial */)
icmp.SetChecksum(xsum)
// Give packet to IPv4 endpoint, dispatcher will validate that
// it's ok.

Some files were not shown because too many files have changed in this diff Show More