1 // Copyright 2018 The gVisor Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 15 // Package header provides the implementation of the encoding and decoding of
16 // network protocol headers.
17 package header
18 19 import (
20 "encoding/binary"
21 "fmt"
22 23 "gvisor.dev/gvisor/pkg/tcpip"
24 "gvisor.dev/gvisor/pkg/tcpip/checksum"
25 )
26 27 // PseudoHeaderChecksum calculates the pseudo-header checksum for the given
28 // destination protocol and network address. Pseudo-headers are needed by
29 // transport layers when calculating their own checksum.
30 func PseudoHeaderChecksum(protocol tcpip.TransportProtocolNumber, srcAddr tcpip.Address, dstAddr tcpip.Address, totalLen uint16) uint16 {
31 xsum := checksum.Checksum(srcAddr.AsSlice(), 0)
32 xsum = checksum.Checksum(dstAddr.AsSlice(), xsum)
33 34 // Add the length portion of the checksum to the pseudo-checksum.
35 var tmp [2]byte
36 binary.BigEndian.PutUint16(tmp[:], totalLen)
37 xsum = checksum.Checksum(tmp[:], xsum)
38 39 return checksum.Checksum([]byte{0, uint8(protocol)}, xsum)
40 }
41 42 // checksumUpdate2ByteAlignedUint16 updates a uint16 value in a calculated
43 // checksum.
44 //
45 // The value MUST begin at a 2-byte boundary in the original buffer.
46 func checksumUpdate2ByteAlignedUint16(xsum, old, new uint16) uint16 {
47 // As per RFC 1071 page 4,
48 // (4) Incremental Update
49 //
50 // ...
51 //
52 // To update the checksum, simply add the differences of the
53 // sixteen bit integers that have been changed. To see why this
54 // works, observe that every 16-bit integer has an additive inverse
55 // and that addition is associative. From this it follows that
56 // given the original value m, the new value m', and the old
57 // checksum C, the new checksum C' is:
58 //
59 // C' = C + (-m) + m' = C + (m' - m)
60 if old == new {
61 return xsum
62 }
63 return checksum.Combine(xsum, checksum.Combine(new, ^old))
64 }
65 66 // checksumUpdate2ByteAlignedAddress updates an address in a calculated
67 // checksum.
68 //
69 // The addresses must have the same length and must contain an even number
70 // of bytes. The address MUST begin at a 2-byte boundary in the original buffer.
71 func checksumUpdate2ByteAlignedAddress(xsum uint16, old, new tcpip.Address) uint16 {
72 const uint16Bytes = 2
73 74 if old.BitLen() != new.BitLen() {
75 panic(fmt.Sprintf("buffer lengths are different; old = %d, new = %d", old.BitLen()/8, new.BitLen()/8))
76 }
77 78 if oldBytes := old.BitLen() % 16; oldBytes != 0 {
79 panic(fmt.Sprintf("buffer has an odd number of bytes; got = %d", oldBytes))
80 }
81 82 oldAddr := old.AsSlice()
83 newAddr := new.AsSlice()
84 85 // As per RFC 1071 page 4,
86 // (4) Incremental Update
87 //
88 // ...
89 //
90 // To update the checksum, simply add the differences of the
91 // sixteen bit integers that have been changed. To see why this
92 // works, observe that every 16-bit integer has an additive inverse
93 // and that addition is associative. From this it follows that
94 // given the original value m, the new value m', and the old
95 // checksum C, the new checksum C' is:
96 //
97 // C' = C + (-m) + m' = C + (m' - m)
98 for len(oldAddr) != 0 {
99 // Convert the 2 byte sequences to uint16 values then apply the increment
100 // update.
101 xsum = checksumUpdate2ByteAlignedUint16(xsum, (uint16(oldAddr[0])<<8)+uint16(oldAddr[1]), (uint16(newAddr[0])<<8)+uint16(newAddr[1]))
102 oldAddr = oldAddr[uint16Bytes:]
103 newAddr = newAddr[uint16Bytes:]
104 }
105 106 return xsum
107 }
108