github.com/vpnishe/netstack@v1.10.6/tcpip/header/checksum.go (about)

     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  
    22  	"github.com/vpnishe/netstack/tcpip"
    23  	"github.com/vpnishe/netstack/tcpip/buffer"
    24  )
    25  
    26  func calculateChecksum(buf []byte, odd bool, initial uint32) (uint16, bool) {
    27  	v := initial
    28  
    29  	if odd {
    30  		v += uint32(buf[0])
    31  		buf = buf[1:]
    32  	}
    33  
    34  	l := len(buf)
    35  	odd = l&1 != 0
    36  	if odd {
    37  		l--
    38  		v += uint32(buf[l]) << 8
    39  	}
    40  
    41  	for i := 0; i < l; i += 2 {
    42  		v += (uint32(buf[i]) << 8) + uint32(buf[i+1])
    43  	}
    44  
    45  	return ChecksumCombine(uint16(v), uint16(v>>16)), odd
    46  }
    47  
    48  // Checksum calculates the checksum (as defined in RFC 1071) of the bytes in the
    49  // given byte array.
    50  //
    51  // The initial checksum must have been computed on an even number of bytes.
    52  func Checksum(buf []byte, initial uint16) uint16 {
    53  	s, _ := calculateChecksum(buf, false, uint32(initial))
    54  	return s
    55  }
    56  
    57  // ChecksumVV calculates the checksum (as defined in RFC 1071) of the bytes in
    58  // the given VectorizedView.
    59  //
    60  // The initial checksum must have been computed on an even number of bytes.
    61  func ChecksumVV(vv buffer.VectorisedView, initial uint16) uint16 {
    62  	return ChecksumVVWithOffset(vv, initial, 0, vv.Size())
    63  }
    64  
    65  // ChecksumVVWithOffset calculates the checksum (as defined in RFC 1071) of the
    66  // bytes in the given VectorizedView.
    67  //
    68  // The initial checksum must have been computed on an even number of bytes.
    69  func ChecksumVVWithOffset(vv buffer.VectorisedView, initial uint16, off int, size int) uint16 {
    70  	odd := false
    71  	sum := initial
    72  	for _, v := range vv.Views() {
    73  		if len(v) == 0 {
    74  			continue
    75  		}
    76  
    77  		if off >= len(v) {
    78  			off -= len(v)
    79  			continue
    80  		}
    81  		v = v[off:]
    82  
    83  		l := len(v)
    84  		if l > size {
    85  			l = size
    86  		}
    87  		v = v[:l]
    88  
    89  		sum, odd = calculateChecksum(v, odd, uint32(sum))
    90  
    91  		size -= len(v)
    92  		if size == 0 {
    93  			break
    94  		}
    95  		off = 0
    96  	}
    97  	return sum
    98  }
    99  
   100  // ChecksumCombine combines the two uint16 to form their checksum. This is done
   101  // by adding them and the carry.
   102  //
   103  // Note that checksum a must have been computed on an even number of bytes.
   104  func ChecksumCombine(a, b uint16) uint16 {
   105  	v := uint32(a) + uint32(b)
   106  	return uint16(v + v>>16)
   107  }
   108  
   109  // PseudoHeaderChecksum calculates the pseudo-header checksum for the given
   110  // destination protocol and network address. Pseudo-headers are needed by
   111  // transport layers when calculating their own checksum.
   112  func PseudoHeaderChecksum(protocol tcpip.TransportProtocolNumber, srcAddr tcpip.Address, dstAddr tcpip.Address, totalLen uint16) uint16 {
   113  	xsum := Checksum([]byte(srcAddr), 0)
   114  	xsum = Checksum([]byte(dstAddr), xsum)
   115  
   116  	// Add the length portion of the checksum to the pseudo-checksum.
   117  	tmp := make([]byte, 2)
   118  	binary.BigEndian.PutUint16(tmp, totalLen)
   119  	xsum = Checksum(tmp, xsum)
   120  
   121  	return Checksum([]byte{0, uint8(protocol)}, xsum)
   122  }