tc-health-client/sar/tcphdrnative.go (118 lines of code) (raw):

package sar /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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. */ import ( "encoding/binary" "fmt" ) // TCPHdrNative is a convenience struct containing deserialized TCP Header data. // This is more convenient but less efficient than TCPHdr. type TCPHdrNative struct { SrcPort uint16 DestPort uint16 SeqNum uint32 AckNum uint32 DataOffset uint8 // 4 bits Reserved uint8 // 4 bits CWR bool ECE bool URG bool ACK bool PSH bool RST bool SYN bool FIN bool Window uint16 Checksum uint16 // Kernel will set this if it's 0 Urgent uint16 Options []TCPHdrNativeOption } type TCPHdrNativeOption struct { Kind int Len int Data []byte } // TCPHdrToNative creates a TCPHdrNative from a TCPHdr. func TCPHdrToNative(hdr TCPHdr) (TCPHdrNative, error) { native := TCPHdrNative{} const minTCPHdrSize = 20 if len(hdr) < minTCPHdrSize { return TCPHdrNative{}, fmt.Errorf("malformed header, minimum TCP header size is %v but hdr was %v bytes", minTCPHdrSize, len(hdr)) } native.SrcPort = hdr.SrcPort() native.DestPort = hdr.DestPort() native.SeqNum = hdr.SeqNum() native.AckNum = hdr.AckNum() native.DataOffset = hdr.DataOffset() native.Reserved = hdr.Reserved() native.CWR = hdr.CWR() native.ECE = hdr.ECE() native.URG = hdr.URG() native.ACK = hdr.ACK() native.PSH = hdr.PSH() native.RST = hdr.RST() native.SYN = hdr.SYN() native.FIN = hdr.FIN() native.Window = hdr.Window() native.Checksum = hdr.Checksum() native.Urgent = hdr.Urgent() native.Options = []TCPHdrNativeOption{} if native.DataOffset < 5 { return native, nil // no options } prevOptionsSize := 0 optionNum := 0 for { // TODO add bounds checking, for malformed options option := TCPHdrNativeOption{} option.Kind = int(hdr.OptionKind(prevOptionsSize, 0)) if option.Kind == TCPHdrOptionEndOfOptionList { // EoOL is length 1, no length octet and no data octets native.Options = append(native.Options, option) prevOptionsSize += 1 optionNum++ break } if option.Kind == TCPHdrOptionNoOperation { // NoOp is length 1, no length octet and no data octets native.Options = append(native.Options, option) prevOptionsSize += 1 optionNum++ continue } option.Len = int(hdr.OptionLen(prevOptionsSize, optionNum)) option.Data = hdr.OptionData(prevOptionsSize, option.Len, optionNum) native.Options = append(native.Options, option) prevOptionsSize += option.Len optionNum++ } return native, nil } // TCPHdrFromNative creates a TCPHdr bytes from a TCPHdrNative. // The created TCPHdr is ready to send over the wire. func TCPHdrFromNative(native TCPHdrNative) (TCPHdr, error) { // TODO pre-calculate and allocate options size hdr := TCPHdr(make([]byte, 20, 20)) // allocate the mandatory 20 bytes up front binary.BigEndian.PutUint16(hdr[0:2], uint16(native.SrcPort)) binary.BigEndian.PutUint16(hdr[2:4], uint16(native.DestPort)) binary.BigEndian.PutUint32(hdr[4:8], uint32(native.SeqNum)) binary.BigEndian.PutUint32(hdr[8:12], uint32(native.AckNum)) // TODO write a single uint32 for offset+reserved+control+window? Should be faster hdr[12] = (native.DataOffset << 4) | (native.Reserved) // single byte so no endian byte order conversion. offset is 4 bits and reserved is 4 bits // TODO this is terribly inefficient. Go has no way to convert bool to int without a conditional. // The TCPHdr is designed to be efficient, and TCPHdrNative is designed to be convenient if less efficient, // But it shouldn't be *that* inefficient. Maybe we should just use uint8? boolToByte := func(b bool) byte { if b { return 1 } return 0 } cwr := boolToByte(native.CWR) ece := boolToByte(native.ECE) urg := boolToByte(native.URG) ack := boolToByte(native.ACK) psh := boolToByte(native.PSH) rst := boolToByte(native.RST) syn := boolToByte(native.SYN) fin := boolToByte(native.FIN) hdr[13] = cwr | (ece << 1) | (urg << 2) | (ack << 3) | (psh << 4) | (rst << 5) | (syn << 6) | (fin << 7) hdr[13] = (cwr << 7) | (ece << 6) | (urg << 5) | (ack << 4) | (psh << 3) | (rst << 2) | (syn << 1) | (fin) binary.BigEndian.PutUint16(hdr[14:16], native.Window) binary.BigEndian.PutUint16(hdr[16:18], native.Checksum) binary.BigEndian.PutUint16(hdr[18:20], native.Urgent) for _, opt := range native.Options { hdr = append(hdr, uint8(opt.Kind)) // uint8, no endian byte order conversion if opt.Len == 0 { continue } hdr = append(hdr, uint8(opt.Len)) hdr = append(hdr, opt.Data[:opt.Len-2]...) // -2 because Len includes the 1-octet kind and 1-octet length fields. } return hdr, nil }