mirror of
https://github.com/buger/goreplay.git
synced 2024-04-21 12:32:02 +00:00
The focus here was to **reduce allocation in TCP parser** but speed may have hopeful improved too! pool no longer use map's key of **string** it uses **uint64** **Benchmarks** was revamped to be more clear if you want to compare these results copy the benchmark in tcp/bench_test.go@reduce-allocation to tcp/bench_test.go@master: **before(master)**: ``` BenchmarkPacketParseAndSort-4 1000000 1006 ns/op 64 B/op 2 allocs/op BenchmarkMessageParserWithoutHint-4 625 1772309 ns/op 1000 packets/op 419096 B/op 10045 allocs/op BenchmarkMessageParserWithHint-4 74 14969926 ns/op 1000 chunks/op 1002 packets/op 450992 B/op 10126 allocs/op ``` **After(this branch)**: ``` BenchmarkPacketParseAndSort-4 1267662 941 ns/op 64 B/op 2 allocs/op BenchmarkMessageParserWithoutHint-4 2256 523474 ns/op 1000 packets/op 243530 B/op 1037 allocs/op BenchmarkMessageParserWithHint-4 80 13990955 ns/op 1000 chunks/op 1002 packets/op 268609 B/op 1099 allocs/op ```
198 lines
3.8 KiB
Go
198 lines
3.8 KiB
Go
package tcp
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"net"
|
|
"time"
|
|
|
|
"github.com/google/gopacket"
|
|
"github.com/google/gopacket/layers"
|
|
)
|
|
|
|
/*
|
|
Packet represent data and layers of packet.
|
|
parser extracts information from pcap Packet. functions of *Packet doesn't validate if packet is nil,
|
|
calllers must make sure that ParsePacket has'nt returned any error before calling any other
|
|
function.
|
|
*/
|
|
type Packet struct {
|
|
// Link layer
|
|
gopacket.LinkLayer
|
|
|
|
// IP Header
|
|
Version uint8 // Ip version
|
|
SrcIP net.IP
|
|
DstIP net.IP
|
|
IHL uint8
|
|
Length uint16
|
|
|
|
// TCP Segment Header
|
|
*layers.TCP
|
|
|
|
// Data info
|
|
Lost uint16
|
|
Timestamp time.Time
|
|
}
|
|
|
|
// ParsePacket parse raw packets
|
|
func ParsePacket(packet gopacket.Packet) (pckt *Packet, err error) {
|
|
// early check of error
|
|
if packet == nil {
|
|
return
|
|
}
|
|
defer func() {
|
|
if packet.ErrorLayer() != nil {
|
|
err = packet.ErrorLayer().Error()
|
|
return
|
|
}
|
|
}()
|
|
|
|
// initialization
|
|
pckt = new(Packet)
|
|
pckt.Timestamp = packet.Metadata().Timestamp
|
|
if pckt.Timestamp.IsZero() {
|
|
pckt.Timestamp = time.Now()
|
|
}
|
|
|
|
// parsing link layer
|
|
pckt.LinkLayer = packet.LinkLayer()
|
|
|
|
// parsing network layer
|
|
if net4, ok := packet.NetworkLayer().(*layers.IPv4); ok {
|
|
pckt.Version = 4
|
|
pckt.SrcIP = net4.SrcIP
|
|
pckt.DstIP = net4.DstIP
|
|
pckt.IHL = net4.IHL * 4
|
|
pckt.Length = net4.Length
|
|
} else if net6, ok := packet.NetworkLayer().(*layers.IPv6); ok {
|
|
pckt.Version = 6
|
|
pckt.SrcIP = net6.SrcIP
|
|
pckt.DstIP = net6.DstIP
|
|
pckt.IHL = 40
|
|
pckt.Length = net6.Length
|
|
} else {
|
|
pckt = nil
|
|
return
|
|
}
|
|
|
|
// parsing tcp header(transportation layer)
|
|
if tcp, ok := packet.TransportLayer().(*layers.TCP); ok {
|
|
pckt.TCP = tcp
|
|
} else {
|
|
pckt = nil
|
|
return
|
|
}
|
|
pckt.DataOffset *= 4
|
|
|
|
// calculating lost data
|
|
headerSize := int(uint32(pckt.DataOffset) + uint32(pckt.IHL))
|
|
if pckt.Version == 6 {
|
|
headerSize -= 40 // in ipv6 the length of payload doesn't include the IPheader size
|
|
}
|
|
pckt.Lost = pckt.Length - uint16(headerSize+len(pckt.Payload))
|
|
|
|
return
|
|
}
|
|
|
|
// Src returns the source socket of a packet
|
|
func (pckt *Packet) Src() string {
|
|
return fmt.Sprintf("%s:%d", pckt.SrcIP, pckt.SrcPort)
|
|
}
|
|
|
|
// Dst returns destination socket
|
|
func (pckt *Packet) Dst() string {
|
|
return fmt.Sprintf("%s:%d", pckt.DstIP, pckt.DstPort)
|
|
}
|
|
|
|
// SYNOptions returns MSS and windowscale of syn packets
|
|
func (pckt *Packet) SYNOptions() (mss uint16, windowscale byte) {
|
|
if !pckt.SYN {
|
|
return
|
|
}
|
|
for _, v := range pckt.Options {
|
|
if v.OptionType == layers.TCPOptionKindMSS {
|
|
mss = binary.BigEndian.Uint16(v.OptionData)
|
|
continue
|
|
}
|
|
if v.OptionType == layers.TCPOptionKindWindowScale {
|
|
if v.OptionLength > 0 {
|
|
windowscale = v.OptionData[0]
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// LinkInfo returns info about the link layer
|
|
func (pckt *Packet) LinkInfo() string {
|
|
if l, ok := pckt.LinkLayer.(*layers.Ethernet); ok {
|
|
return fmt.Sprintf(
|
|
"Source Mac: %s\nDestination Mac: %s\nProtocol: %s",
|
|
l.SrcMAC,
|
|
l.DstMAC,
|
|
l.EthernetType,
|
|
)
|
|
}
|
|
return "<Not Ethernet>"
|
|
}
|
|
|
|
// Flag returns formatted tcp flags
|
|
func (pckt *Packet) Flag() (flag string) {
|
|
if pckt.FIN {
|
|
flag += "FIN, "
|
|
}
|
|
if pckt.SYN {
|
|
flag += "SYN, "
|
|
}
|
|
if pckt.RST {
|
|
flag += "RST, "
|
|
}
|
|
if pckt.PSH {
|
|
flag += "PSH, "
|
|
}
|
|
if pckt.ACK {
|
|
flag += "ACK, "
|
|
}
|
|
if pckt.URG {
|
|
flag += "URG, "
|
|
}
|
|
if len(flag) != 0 {
|
|
return flag[:len(flag)-2]
|
|
}
|
|
return flag
|
|
}
|
|
|
|
// String output for a TCP Packet
|
|
func (pckt *Packet) String() string {
|
|
return fmt.Sprintf(`Time: %s
|
|
%s
|
|
Source: %s
|
|
Destination: %s
|
|
IHL: %d
|
|
Total Length: %d
|
|
Sequence: %d
|
|
Acknowledgment: %d
|
|
DataOffset: %d
|
|
Window: %d
|
|
Flag: %s
|
|
Options: %s
|
|
Data Size: %d
|
|
Lost Data: %d`,
|
|
pckt.Timestamp.Format(time.StampNano),
|
|
pckt.LinkInfo(),
|
|
pckt.Src(),
|
|
pckt.Dst(),
|
|
pckt.IHL,
|
|
pckt.Length,
|
|
pckt.Seq,
|
|
pckt.Ack,
|
|
pckt.DataOffset,
|
|
pckt.Window,
|
|
pckt.Flag(),
|
|
pckt.Options,
|
|
len(pckt.Payload),
|
|
pckt.Lost,
|
|
)
|
|
}
|