diff --git a/Dockerfile b/Dockerfile index 96c84f70..b6fe71a4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,7 +21,7 @@ RUN apt-get -q update && apt-get -q -y install \ libmnl-dev \ libibverbs-dev -RUN cd /opt && curl -L -s https://dl.google.com/go/go1.11.4.linux-amd64.tar.gz | tar zx +RUN cd /opt && curl -L -s https://dl.google.com/go/go1.11.5.linux-amd64.tar.gz | tar zx RUN mkdir -p ${NFF_GO} COPY . ${NFF_GO} diff --git a/common/common.go b/common/common.go index 959c4293..50631c97 100644 --- a/common/common.go +++ b/common/common.go @@ -7,332 +7,9 @@ package common import ( - "fmt" - "io" - "log" - "math" - "os" "strconv" - - "github.com/pkg/errors" -) - -// Max array length for type conversions -const MaxLength = math.MaxInt32 - -// Length of addresses. -const ( - EtherAddrLen = 6 - IPv4AddrLen = 4 - IPv6AddrLen = 16 -) - -// Supported EtherType for L2 -const ( - IPV4Number = 0x0800 - ARPNumber = 0x0806 - VLANNumber = 0x8100 - MPLSNumber = 0x8847 - IPV6Number = 0x86dd - - SwapIPV4Number = 0x0008 - SwapARPNumber = 0x0608 - SwapVLANNumber = 0x0081 - SwapMPLSNumber = 0x4788 - SwapIPV6Number = 0xdd86 -) - -// Supported L4 types -const ( - ICMPNumber = 0x01 - IPNumber = 0x04 - TCPNumber = 0x06 - UDPNumber = 0x11 - ICMPv6Number = 0x3a - NoNextHeader = 0x3b -) - -// Supported ICMP Types -const ( - ICMPTypeEchoRequest uint8 = 8 - ICMPTypeEchoResponse uint8 = 0 - ICMPv6TypeEchoRequest uint8 = 128 - ICMPv6TypeEchoResponse uint8 = 129 - ICMPv6NeighborSolicitation uint8 = 135 - ICMPv6NeighborAdvertisement uint8 = 136 -) - -// These constants keep length of supported headers in bytes. -// -// IPv6Len - minimum length of IPv6 header in bytes. It can be higher and it -// is not determined inside packet. Only default minimum size is used. -// -// IPv4MinLen and TCPMinLen are used only in packet generation functions. -// -// In parsing we take actual length of TCP header from DataOff field and length of -// IPv4 take from Ihl field. -const ( - EtherLen = 14 - VLANLen = 4 - MPLSLen = 4 - IPv4MinLen = 20 - IPv6Len = 40 - ICMPLen = 8 - TCPMinLen = 20 - UDPLen = 8 - ARPLen = 28 - GTPMinLen = 8 -) - -const ( - TCPMinDataOffset = 0x50 // minimal tcp data offset - IPv4VersionIhl = 0x45 // IPv4, IHL = 5 (min header len) - IPv6VtcFlow = 0x60 // IPv6 version -) - -// LogType - type of logging, used in flow package -type LogType uint8 - -const ( - // No - no output even after fatal errors - No LogType = 1 << iota - // Initialization - output during system initialization - Initialization = 2 - // Debug - output during execution one time per time period (scheduler ticks) - Debug = 4 - // Verbose - output during execution as soon as something happens. Can influence performance - Verbose = 8 -) - -// TCPFlags contains set TCP flags. -type TCPFlags uint8 - -// Constants for valuues of TCP flags. -const ( - TCPFlagFin = 0x01 - TCPFlagSyn = 0x02 - TCPFlagRst = 0x04 - TCPFlagPsh = 0x08 - TCPFlagAck = 0x10 - TCPFlagUrg = 0x20 - TCPFlagEce = 0x40 - TCPFlagCwr = 0x80 ) -// ErrorCode type for codes of errors -type ErrorCode int - -// constants with error codes -const ( - _ ErrorCode = iota - Fail - ParseCPUListErr - ReqTooManyPorts - BadArgument - UseNilFlowErr - UseClosedFlowErr - OpenedFlowAtTheEnd - PortHasNoQueues - NotAllQueuesUsed - FailToInitPort - ParseRuleJSONErr - FileErr - ParseRuleErr - IncorrectArgInRules - IncorrectRule - AllocMbufErr - PktMbufHeadRoomTooSmall - NotEnoughCores - CreatePortErr - MaxCPUExceedErr - PcapReadFail - PcapWriteFail - InvalidCPURangeErr - SetAffinityErr - MultipleReceivePort - MultipleKNIPort - WrongPort - FailToInitDPDK - FailToCreateKNI - FailToReleaseKNI -) - -// NFError is error type returned by nff-go functions -type NFError struct { - Code ErrorCode - Message string - CauseErr error -} - -type causer interface { - Cause() error -} - -// Error method to implement error interface -func (err NFError) Error() string { - return fmt.Sprintf("%s (%d)", err.Message, err.Code) -} - -// GetNFErrorCode returns value of cCode field if err is -// NFError or pointer to it and -1 otherwise. -func GetNFErrorCode(err error) ErrorCode { - if nferr := GetNFError(err); nferr != nil { - return nferr.Code - } - return -1 -} - -func checkAndGetNFErrPointer(err error) *NFError { - if err != nil { - if nferr, ok := err.(NFError); ok { - return &nferr - } else if nferr, ok := err.(*NFError); ok { - return nferr - } - } - return nil -} - -// GetNFError if error is NFerror or pointer to int -// returns pointer to NFError, otherwise returns nil. -func GetNFError(err error) (nferr *NFError) { - nferr = checkAndGetNFErrPointer(err) - if nferr == nil { - if cause, ok := err.(causer); ok { - nferr = checkAndGetNFErrPointer(cause.Cause()) - } - } - return nferr -} - -// Cause returns the underlying cause of error, if -// possible. If not, returns err itself. -func (err *NFError) Cause() error { - if err == nil { - return nil - } - if err.CauseErr != nil { - if cause, ok := err.CauseErr.(causer); ok { - return cause.Cause() - } - return err.CauseErr - } - return err -} - -// Format makes formatted printing of errors, -// the following verbs are supported: -// %s, %v print the error. If the error has a -// Cause it will be printed recursively -// %+v - extended format. Each Frame of the error's -// StackTrace will be printed in detail if possible. -func (err *NFError) Format(s fmt.State, verb rune) { - switch verb { - case 'v': - if s.Flag('+') { - if cause := err.Cause(); cause != err && cause != nil { - fmt.Fprintf(s, "%+v\n", err.Cause()) - io.WriteString(s, err.Message) - return - } - } - fallthrough - case 's', 'q': - io.WriteString(s, err.Error()) - } -} - -// WrapWithNFError returns an error annotating err with a stack trace -// at the point WrapWithNFError is called, and the next our NFError. -// If err is nil, Wrap returns nil. -func WrapWithNFError(err error, message string, code ErrorCode) error { - err = &NFError{ - CauseErr: err, - Message: message, - Code: code, - } - return errors.WithStack(err) -} - -var currentLogType = No | Initialization | Debug - -// LogFatal internal, used in all packages -func LogFatal(logType LogType, v ...interface{}) { - if logType¤tLogType != 0 { - t := fmt.Sprintln(v...) - log.Fatal("ERROR: ", t) - } - os.Exit(1) -} - -// LogFatalf is a wrapper at LogFatal which makes formatting before logger. -func LogFatalf(logType LogType, format string, v ...interface{}) { - LogFatal(logType, fmt.Sprintf(format, v...)) -} - -// LogError internal, used in all packages -func LogError(logType LogType, v ...interface{}) string { - if logType¤tLogType != 0 { - t := fmt.Sprintln(v...) - log.Print("ERROR: ", t) - return t - } - return "" -} - -// LogWarning internal, used in all packages -func LogWarning(logType LogType, v ...interface{}) { - if logType¤tLogType != 0 { - t := fmt.Sprintln(v...) - log.Print("WARNING: ", t) - } -} - -// LogDebug internal, used in all packages -func LogDebug(logType LogType, v ...interface{}) { - if logType¤tLogType != 0 { - t := fmt.Sprintln(v...) - log.Print("DEBUG: ", t) - } -} - -// LogDrop internal, used in all packages -func LogDrop(logType LogType, v ...interface{}) { - if logType¤tLogType != 0 { - t := fmt.Sprintln(v...) - log.Print("DROP: ", t) - } -} - -// LogTitle internal, used in all packages -func LogTitle(logType LogType, v ...interface{}) { - if logType¤tLogType != 0 { - log.Print(v...) - } -} - -// SetLogType internal, used in flow package -func SetLogType(logType LogType) { - log.SetFlags(0) - currentLogType = logType -} - -// GetDPDKLogLevel internal, used in flow package -func GetDPDKLogLevel() string { - switch currentLogType { - case No: - return "0" - case No | Initialization: - return "7" - case No | Initialization | Debug: - return "8" - case No | Initialization | Debug | Verbose: - return "8" - default: - return "8" - } -} - // GetDefaultCPUs returns default core list {0, 1, ..., NumCPU} func GetDefaultCPUs(cpuNumber int) []int { cpus := make([]int, cpuNumber, cpuNumber) @@ -420,3 +97,9 @@ func dropInvalidCPUs(nums []int, maxcpu int) []int { } return nums[:i] } + +// RXTXStats describes statistics for sender or receiver flow function +// node. +type RXTXStats struct { + PacketsProcessed, PacketsDropped, BytesProcessed uint64 +} diff --git a/common/error.go b/common/error.go new file mode 100644 index 00000000..df74f9ec --- /dev/null +++ b/common/error.go @@ -0,0 +1,148 @@ +// Copyright 2019 Intel Corporation. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package common + +import ( + "fmt" + "io" + + "github.com/pkg/errors" +) + +// ErrorCode type for codes of errors +type ErrorCode int + +// constants with error codes +const ( + _ ErrorCode = iota + Fail + ParseCPUListErr + ReqTooManyPorts + BadArgument + UseNilFlowErr + UseClosedFlowErr + OpenedFlowAtTheEnd + PortHasNoQueues + NotAllQueuesUsed + FailToInitPort + ParseRuleJSONErr + FileErr + ParseRuleErr + IncorrectArgInRules + IncorrectRule + AllocMbufErr + PktMbufHeadRoomTooSmall + NotEnoughCores + CreatePortErr + MaxCPUExceedErr + PcapReadFail + PcapWriteFail + InvalidCPURangeErr + SetAffinityErr + MultipleReceivePort + MultipleKNIPort + WrongPort + FailToInitDPDK + FailToCreateKNI + FailToReleaseKNI + BadSocket +) + +// NFError is error type returned by nff-go functions +type NFError struct { + Code ErrorCode + Message string + CauseErr error +} + +type causer interface { + Cause() error +} + +// Error method to implement error interface +func (err NFError) Error() string { + return fmt.Sprintf("%s (%d)", err.Message, err.Code) +} + +// GetNFErrorCode returns value of cCode field if err is +// NFError or pointer to it and -1 otherwise. +func GetNFErrorCode(err error) ErrorCode { + if nferr := GetNFError(err); nferr != nil { + return nferr.Code + } + return -1 +} + +func checkAndGetNFErrPointer(err error) *NFError { + if err != nil { + if nferr, ok := err.(NFError); ok { + return &nferr + } else if nferr, ok := err.(*NFError); ok { + return nferr + } + } + return nil +} + +// GetNFError if error is NFerror or pointer to int +// returns pointer to NFError, otherwise returns nil. +func GetNFError(err error) (nferr *NFError) { + nferr = checkAndGetNFErrPointer(err) + if nferr == nil { + if cause, ok := err.(causer); ok { + nferr = checkAndGetNFErrPointer(cause.Cause()) + } + } + return nferr +} + +// Cause returns the underlying cause of error, if +// possible. If not, returns err itself. +func (err *NFError) Cause() error { + if err == nil { + return nil + } + if err.CauseErr != nil { + if cause, ok := err.CauseErr.(causer); ok { + return cause.Cause() + } + return err.CauseErr + } + return err +} + +// Format makes formatted printing of errors, +// the following verbs are supported: +// %s, %v print the error. If the error has a +// Cause it will be printed recursively +// %+v - extended format. Each Frame of the error's +// StackTrace will be printed in detail if possible. +func (err *NFError) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + if cause := err.Cause(); cause != err && cause != nil { + fmt.Fprintf(s, "%+v\n", err.Cause()) + io.WriteString(s, err.Message) + return + } + } + fallthrough + case 's', 'q': + io.WriteString(s, err.Error()) + } +} + +// WrapWithNFError returns an error annotating err with a stack trace +// at the point WrapWithNFError is called, and the next our NFError. +// If err is nil, Wrap returns nil. +func WrapWithNFError(err error, message string, code ErrorCode) error { + err = &NFError{ + CauseErr: err, + Message: message, + Code: code, + } + return errors.WithStack(err) +} diff --git a/common/log.go b/common/log.go new file mode 100644 index 00000000..e71fadf6 --- /dev/null +++ b/common/log.go @@ -0,0 +1,104 @@ +// Copyright 2019 Intel Corporation. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package common + +import ( + "fmt" + "log" + "os" +) + +// LogType - type of logging, used in flow package +type LogType uint8 + +const ( + // No - no output even after fatal errors + No LogType = 1 << iota + // Initialization - output during system initialization + Initialization = 2 + // Debug - output during execution one time per time period (scheduler ticks) + Debug = 4 + // Verbose - output during execution as soon as something happens. Can influence performance + Verbose = 8 +) + +var currentLogType = No | Initialization | Debug + +// LogFatal internal, used in all packages +func LogFatal(logType LogType, v ...interface{}) { + if logType¤tLogType != 0 { + t := fmt.Sprintln(v...) + log.Fatal("ERROR: ", t) + } + os.Exit(1) +} + +// LogFatalf is a wrapper at LogFatal which makes formatting before logger. +func LogFatalf(logType LogType, format string, v ...interface{}) { + LogFatal(logType, fmt.Sprintf(format, v...)) +} + +// LogError internal, used in all packages +func LogError(logType LogType, v ...interface{}) string { + if logType¤tLogType != 0 { + t := fmt.Sprintln(v...) + log.Print("ERROR: ", t) + return t + } + return "" +} + +// LogWarning internal, used in all packages +func LogWarning(logType LogType, v ...interface{}) { + if logType¤tLogType != 0 { + t := fmt.Sprintln(v...) + log.Print("WARNING: ", t) + } +} + +// LogDebug internal, used in all packages +func LogDebug(logType LogType, v ...interface{}) { + if logType¤tLogType != 0 { + t := fmt.Sprintln(v...) + log.Print("DEBUG: ", t) + } +} + +// LogDrop internal, used in all packages +func LogDrop(logType LogType, v ...interface{}) { + if logType¤tLogType != 0 { + t := fmt.Sprintln(v...) + log.Print("DROP: ", t) + } +} + +// LogTitle internal, used in all packages +func LogTitle(logType LogType, v ...interface{}) { + if logType¤tLogType != 0 { + log.Print(v...) + } +} + +// SetLogType internal, used in flow package +func SetLogType(logType LogType) { + log.SetFlags(0) + currentLogType = logType +} + +// GetDPDKLogLevel internal, used in flow package +func GetDPDKLogLevel() string { + switch currentLogType { + case No: + return "0" + case No | Initialization: + return "7" + case No | Initialization | Debug: + return "8" + case No | Initialization | Debug | Verbose: + return "8" + default: + return "8" + } +} diff --git a/dpdk/dpdk b/dpdk/dpdk index 0da7f445..8b937bae 160000 --- a/dpdk/dpdk +++ b/dpdk/dpdk @@ -1 +1 @@ -Subproject commit 0da7f445df445630c794897347ee360d6fe6348b +Subproject commit 8b937bae24a306ad82c0983c83feb1be23d41f13 diff --git a/dpdk/pktgen-dpdk b/dpdk/pktgen-dpdk index 6a78a5b6..41995554 160000 --- a/dpdk/pktgen-dpdk +++ b/dpdk/pktgen-dpdk @@ -1 +1 @@ -Subproject commit 6a78a5b69a3d379c328fa3fbd5d2d259d99edd00 +Subproject commit 4199555481cd08fe08e32986c94e59836ca1cd4f diff --git a/examples/.gitignore b/examples/.gitignore index c60b8caa..e3c2f461 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -11,3 +11,5 @@ pingReplay timer netlink devbind +OSforwarding +generate diff --git a/examples/Dockerfile b/examples/Dockerfile index dd089d3d..0ab249dc 100644 --- a/examples/Dockerfile +++ b/examples/Dockerfile @@ -19,3 +19,5 @@ COPY gtpu . COPY pingReplay . COPY timer . COPY netlink . +COPY generate . +COPY OSforwarding . diff --git a/examples/Makefile b/examples/Makefile index 78fe0fa0..2a305c51 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -5,9 +5,10 @@ PATH_TO_MK = ../mk IMAGENAME = nff-go-examples EXECUTABLES = dump clonablePcapDumper kni copy errorHandling timer \ - createPacket sendFixedPktsNumber gtpu pingReplay \ - netlink gopacketParserExample devbind -SUBDIRS = tutorial antiddos demo fileReadWrite firewall forwarding ipsec + createPacket sendFixedPktsNumber gtpu pingReplay \ + netlink gopacketParserExample devbind generate \ + OSforwarding +SUBDIRS = tutorial antiddos demo fileReadWrite firewall forwarding ipsec lb .PHONY: dpi nffPktgen dpi: diff --git a/examples/OSforwarding.go b/examples/OSforwarding.go new file mode 100644 index 00000000..a845e160 --- /dev/null +++ b/examples/OSforwarding.go @@ -0,0 +1,24 @@ +// Copyright 2017 Intel Corporation. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "flag" + "github.com/intel-go/nff-go/flow" +) + +func main() { + inport := flag.String("in", "", "device for receiver") + outport := flag.String("out", "", "device for sender") + flag.Parse() + + flow.CheckFatal(flow.SystemInit(nil)) + + inputFlow, err := flow.SetReceiverOS(*inport) + flow.CheckFatal(err) + flow.CheckFatal(flow.SetSenderOS(inputFlow, *outport)) + + flow.CheckFatal(flow.SystemStart()) +} diff --git a/examples/antiddos/antiDDoS.go b/examples/antiddos/antiDDoS.go index 42d8e5e0..c01bf8eb 100644 --- a/examples/antiddos/antiDDoS.go +++ b/examples/antiddos/antiDDoS.go @@ -23,9 +23,9 @@ import ( "sync/atomic" "time" - "github.com/intel-go/nff-go/common" "github.com/intel-go/nff-go/flow" "github.com/intel-go/nff-go/packet" + "github.com/intel-go/nff-go/types" ) // Constants are taken from paper: @@ -104,11 +104,11 @@ func getPacketHash(pkt *packet.Packet) uint32 { if pktIPv4 != nil { pktTCP, pktUDP, pktICMP = pkt.ParseAllKnownL4ForIPv4() srcAddr = []byte{byte(pktIPv4.SrcAddr), byte(pktIPv4.SrcAddr >> 8), byte(pktIPv4.SrcAddr >> 16), byte(pktIPv4.SrcAddr >> 24)} - srcAddrLen = common.IPv4AddrLen + srcAddrLen = types.IPv4AddrLen } else if pktIPv6 != nil { pktTCP, pktUDP, pktICMP = pkt.ParseAllKnownL4ForIPv6() srcAddr = pktIPv6.SrcAddr[:] - srcAddrLen = common.IPv6AddrLen + srcAddrLen = types.IPv6AddrLen } if pktTCP != nil { diff --git a/examples/antiddos/generatorForAntiDDoS.go b/examples/antiddos/generatorForAntiDDoS.go index d20a93dc..4fe3bbda 100644 --- a/examples/antiddos/generatorForAntiDDoS.go +++ b/examples/antiddos/generatorForAntiDDoS.go @@ -19,6 +19,7 @@ import ( "github.com/intel-go/nff-go/flow" "github.com/intel-go/nff-go/packet" + "github.com/intel-go/nff-go/types" ) const ( @@ -43,10 +44,10 @@ var ( // addresses imitating users sending non flood // flooding user's addresses will be generated - goodAddresses = [goodDataListSize]uint32{packet.SwapBytesUint32(1235), - packet.SwapBytesUint32(980), - packet.SwapBytesUint32(2405), - packet.SwapBytesUint32(3540)} + goodAddresses = [goodDataListSize]types.IPv4Address{packet.SwapBytesIPv4Addr(1235), + packet.SwapBytesIPv4Addr(980), + packet.SwapBytesIPv4Addr(2405), + packet.SwapBytesIPv4Addr(3540)} // ports corresponding to addresses of users sending non flood goodPorts = [goodDataListSize]uint16{packet.SwapBytesUint16(1), packet.SwapBytesUint16(2), @@ -69,7 +70,7 @@ func main() { exampleDoneEvent = sync.NewCond(&sync.Mutex{}) // Create first packet flow - outputFlow, err := flow.SetFastGenerator(generatePacket, speed, nil) + outputFlow, _, err := flow.SetFastGenerator(generatePacket, speed, nil) flow.CheckFatal(err) flow.CheckFatal(flow.SetSender(outputFlow, uint16(*outPort))) inputFlow, err := flow.SetReceiver(uint16(*inPort)) @@ -115,7 +116,7 @@ func main() { // Function to use in generator func generatePacket(pkt *packet.Packet, context flow.UserContext) { - var address *uint32 + var address *types.IPv4Address var port *uint16 if pkt == nil { @@ -141,7 +142,7 @@ func generatePacket(pkt *packet.Packet, context flow.UserContext) { *port = goodPorts[indx] atomic.AddUint64(&sentNonFlood, 1) } else { - *address = packet.SwapBytesUint32(uint32(sentPackets)) + *address = packet.SwapBytesIPv4Addr(types.IPv4Address(sentPackets)) *port = packet.SwapBytesUint16(uint16(sentPackets)) } @@ -170,7 +171,7 @@ func checkInputFlow(pkt *packet.Packet, context flow.UserContext) { } } -func isSrcGood(srcAddr uint32, srcPort uint16) bool { +func isSrcGood(srcAddr types.IPv4Address, srcPort uint16) bool { for i := 0; i < goodDataListSize; i++ { if srcAddr == goodAddresses[i] && srcPort == goodPorts[i] { return true diff --git a/examples/createPacket.go b/examples/createPacket.go index db71af15..061f0474 100644 --- a/examples/createPacket.go +++ b/examples/createPacket.go @@ -30,11 +30,11 @@ func main() { flow.CheckFatal(flow.SystemInit(&config)) // Create packets with speed at least 1000 packets/s if *enablePacketFromByte == false { - firstFlow, err = flow.SetFastGenerator(generatePacket, 1000, nil) + firstFlow, _, err = flow.SetFastGenerator(generatePacket, 1000, nil) flow.CheckFatal(err) } else { buffer, _ = hex.DecodeString("00112233445501112131415108004500002ebffd00000406747a7f0000018009090504d2162e123456781234569050102000ffe60000") - firstFlow, err = flow.SetFastGenerator(generatePacketFromByte, 1000, nil) + firstFlow, _, err = flow.SetFastGenerator(generatePacketFromByte, 1000, nil) flow.CheckFatal(err) } // Send all generated packets to the output diff --git a/examples/demo/demo.go b/examples/demo/demo.go index 8791c621..e20e6ebe 100644 --- a/examples/demo/demo.go +++ b/examples/demo/demo.go @@ -28,7 +28,8 @@ func main() { // Initialize NFF-GO library at 16 cores by default config := flow.Config{ - CPUList: "0-15", + CPUList: "0-15", + NoPacketHeadChange: true, } flow.CheckFatal(flow.SystemInit(&config)) diff --git a/examples/dpi/main/handlers.go b/examples/dpi/main/handlers.go index ac066a80..53d79202 100644 --- a/examples/dpi/main/handlers.go +++ b/examples/dpi/main/handlers.go @@ -7,10 +7,10 @@ import ( "hash/fnv" "sync/atomic" - "github.com/intel-go/nff-go/common" "github.com/intel-go/nff-go/examples/dpi/pattern" "github.com/intel-go/nff-go/flow" "github.com/intel-go/nff-go/packet" + "github.com/intel-go/nff-go/types" ) type localCounters struct { @@ -83,7 +83,7 @@ func splitBy5Tuple(pkt *packet.Packet, context flow.UserContext) uint { return pattern.TotalNumFlows - 1 } if ip4 != nil { - if ip4.NextProtoID != common.TCPNumber && ip4.NextProtoID != common.UDPNumber { + if ip4.NextProtoID != types.TCPNumber && ip4.NextProtoID != types.UDPNumber { return pattern.TotalNumFlows - 1 } hash.Write([]byte{ip4.NextProtoID}) @@ -93,7 +93,7 @@ func splitBy5Tuple(pkt *packet.Packet, context flow.UserContext) uint { flow.CheckFatal(binary.Write(buf, binary.LittleEndian, ip4.DstAddr)) hash.Write(buf.Bytes()) } else if ip6 != nil { - if ip6.Proto != common.TCPNumber && ip6.Proto != common.UDPNumber { + if ip6.Proto != types.TCPNumber && ip6.Proto != types.UDPNumber { return pattern.TotalNumFlows - 1 } flow.CheckFatal(binary.Write(hash, binary.BigEndian, ip6.Proto)) diff --git a/examples/dpi/pattern/Makefile b/examples/dpi/pattern/Makefile index 56a177a8..a73799c3 100644 --- a/examples/dpi/pattern/Makefile +++ b/examples/dpi/pattern/Makefile @@ -3,7 +3,7 @@ # license that can be found in the LICENSE file. PATH_TO_MK = ../../../mk -IMAGENAME = dpi +IMAGENAME = dpi-pattern pattern: go install $(GO_COMPILE_FLAGS) diff --git a/examples/forwarding/forwarding.go b/examples/forwarding/forwarding.go index e6b9cb3a..d747d96e 100644 --- a/examples/forwarding/forwarding.go +++ b/examples/forwarding/forwarding.go @@ -1,10 +1,13 @@ -// Copyright 2017 Intel Corporation. +// Copyright 2017-2019 Intel Corporation. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( + "flag" + "net" + "github.com/intel-go/nff-go/flow" "github.com/intel-go/nff-go/packet" ) @@ -13,10 +16,23 @@ var l3Rules *packet.L3Rules // Main function for constructing packet processing graph. func main() { + inport := flag.Uint("inport", 0, "Port for receiving packets.") + numflows := flag.Uint("numflows", 5, "Number of output flows to use. First flow with number zero is used for dropped packets.") + nostats := flag.Bool("nostats", false, "Disable statics HTTP server.") + flag.Parse() + var err error + var statsServerAddres *net.TCPAddr = nil + if !*nostats { + // Set up address for stats web server + statsServerAddres = &net.TCPAddr{ + Port: 8080, + } + } + // Initialize NFF-GO library at 16 cores by default config := flow.Config{ - CPUList: "0-15", + StatsHTTPAddress: statsServerAddres, } flow.CheckFatal(flow.SystemInit(&config)) @@ -25,11 +41,11 @@ func main() { flow.CheckFatal(err) // Receive packets from zero port. Receive queue will be added automatically. - inputFlow, err := flow.SetReceiver(0) + inputFlow, err := flow.SetReceiver(uint16(*inport)) flow.CheckFatal(err) // Split packet flow based on ACL. - flowsNumber := uint16(5) + flowsNumber := uint16(*numflows) outputFlows, err := flow.SetSplitter(inputFlow, l3Splitter, uint(flowsNumber), nil) flow.CheckFatal(err) diff --git a/examples/generate.go b/examples/generate.go new file mode 100644 index 00000000..336f9f4f --- /dev/null +++ b/examples/generate.go @@ -0,0 +1,70 @@ +// Copyright 2017 Intel Corporation. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "flag" + "fmt" + "github.com/intel-go/nff-go/flow" + "github.com/intel-go/nff-go/packet" + "time" +) + +func main() { + mode := flag.Int("mode", 2, "mode of generating:\n0 - fast generate that will be slowed in a second.\n1 - time-based generate send by 32 packets.\n2 - time-based generate send by 1 packet.") + output := flag.Int("port", 1, "output port") + flag.Parse() + outputPort := uint16(*output) + + flow.SystemInit(nil) + + switch *mode { + case 0: + firstFlow, genChannel, _ := flow.SetFastGenerator(generatePacket, 3500, nil) + flow.CheckFatal(flow.SetSender(firstFlow, outputPort)) + go updateSpeed(genChannel) + flow.SystemStart() + case 1: + firstFlow := flow.SetGenerator(generatePacket1, nil) + flow.CheckFatal(flow.SetSender(firstFlow, outputPort)) + flow.SystemStart() + case 2: + temp, _ := (flow.SetReceiver(outputPort)) + flow.SetStopper(temp) + flow.SystemInitPortsAndMemory() + generatePacket2(outputPort) + } +} + +func generatePacket(pkt *packet.Packet, context flow.UserContext) { + packet.InitEmptyIPv4Packet(pkt, 1300) + pkt.Ether.DAddr = [6]uint8{0x00, 0x11, 0x22, 0x33, 0x44, 0x55} +} + +func generatePacket1(pkt *packet.Packet, context flow.UserContext) { + packet.InitEmptyIPv4Packet(pkt, 1300) + pkt.Ether.DAddr = [6]uint8{0x00, 0x11, 0x22, 0x33, 0x44, 0x55} + time.Sleep(175 * time.Microsecond) +} + +func generatePacket2(port uint16) { + for { + pkt, _ := packet.NewPacket() + packet.InitEmptyIPv4Packet(pkt, 1300) + pkt.Ether.DAddr = [6]uint8{0x00, 0x11, 0x22, 0x33, 0x44, 0x55} + pkt.SendPacket(port) + time.Sleep(175 * time.Microsecond) + } +} + +func updateSpeed(genChannel chan uint64) { + var load int + for { + // Can be file or any other source + if _, err := fmt.Scanf("%d", &load); err == nil { + genChannel <- uint64(load) + } + } +} diff --git a/examples/gtpu.go b/examples/gtpu.go index b59f4dd7..d58f407c 100644 --- a/examples/gtpu.go +++ b/examples/gtpu.go @@ -6,9 +6,9 @@ package main import ( "fmt" - "github.com/intel-go/nff-go/common" "github.com/intel-go/nff-go/flow" "github.com/intel-go/nff-go/packet" + "github.com/intel-go/nff-go/types" ) func main() { @@ -29,7 +29,7 @@ func decap(current *packet.Packet, context flow.UserContext) bool { current.ParseL3() ipv4 := current.GetIPv4() - if ipv4 == nil || ipv4.DstAddr != packet.BytesToIPv4(55, 66, 77, 88) { + if ipv4 == nil || ipv4.DstAddr != types.BytesToIPv4(55, 66, 77, 88) { // reject with wrong IP println("ERROR") return false @@ -76,10 +76,10 @@ func encap(current *packet.Packet, context flow.UserContext) bool { ipv4.FragmentOffset = 0 ipv4.TimeToLive = 64 - ipv4.TotalLength = packet.SwapBytesUint16(uint16(length - common.EtherLen)) - ipv4.NextProtoID = common.UDPNumber - ipv4.SrcAddr = packet.BytesToIPv4(11, 22, 33, 44) - ipv4.DstAddr = packet.BytesToIPv4(55, 66, 77, 88) + ipv4.TotalLength = packet.SwapBytesUint16(uint16(length - types.EtherLen)) + ipv4.NextProtoID = types.UDPNumber + ipv4.SrcAddr = types.BytesToIPv4(11, 22, 33, 44) + ipv4.DstAddr = types.BytesToIPv4(55, 66, 77, 88) ipv4.HdrChecksum = packet.SwapBytesUint16(packet.CalculateIPv4Checksum(ipv4)) current.ParseL4ForIPv4() @@ -88,7 +88,7 @@ func encap(current *packet.Packet, context flow.UserContext) bool { // construct udphdr udp.SrcPort = packet.SwapUDPPortGTPU udp.DstPort = packet.SwapUDPPortGTPU - udp.DgramLen = uint16(length - common.EtherLen - common.IPv4MinLen) + udp.DgramLen = uint16(length - types.EtherLen - types.IPv4MinLen) udp.DgramCksum = 0 return true @@ -99,8 +99,8 @@ func generate(current *packet.Packet, context flow.UserContext) { packet.InitEmptyIPv4TCPPacket(current, payload) ipv4 := current.GetIPv4NoCheck() tcp := current.GetTCPNoCheck() - ipv4.SrcAddr = packet.BytesToIPv4(1, 2, 3, 4) - ipv4.DstAddr = packet.BytesToIPv4(5, 6, 7, 8) + ipv4.SrcAddr = types.BytesToIPv4(1, 2, 3, 4) + ipv4.DstAddr = types.BytesToIPv4(5, 6, 7, 8) tcp.SrcPort = packet.SwapBytesUint16(111) tcp.DstPort = packet.SwapBytesUint16(222) } diff --git a/examples/ipsec/ipsec_kernel.go b/examples/ipsec/ipsec_kernel.go index 46e312be..1fbc361f 100644 --- a/examples/ipsec/ipsec_kernel.go +++ b/examples/ipsec/ipsec_kernel.go @@ -7,7 +7,7 @@ package ipsec import "github.com/intel-go/nff-go/packet" import "github.com/intel-go/nff-go/flow" -import "github.com/intel-go/nff-go/common" +import "github.com/intel-go/nff-go/types" import "bytes" import "unsafe" import "crypto/aes" @@ -17,8 +17,8 @@ const mode1234 = 1234 const espHeadLen = 24 const authLen = 12 const espTailLen = authLen + 2 -const etherLen = common.EtherLen -const outerIPLen = common.IPv4MinLen +const etherLen = types.EtherLen +const outerIPLen = types.IPv4MinLen type espHeader struct { SPI uint32 @@ -40,8 +40,8 @@ func Decapsulation(currentPacket *packet.Packet, context flow.UserContext) bool // Security Association switch packet.SwapBytesUint32(currentESPHeader.SPI) { case mode1234: - encryptionPart := (*[common.MaxLength]byte)(unsafe.Pointer(currentPacket.StartAtOffset(0)))[etherLen+outerIPLen+espHeadLen : length-authLen] - authPart := (*[common.MaxLength]byte)(unsafe.Pointer(currentPacket.StartAtOffset(0)))[etherLen+outerIPLen : length-authLen] + encryptionPart := (*[types.MaxLength]byte)(unsafe.Pointer(currentPacket.StartAtOffset(0)))[etherLen+outerIPLen+espHeadLen : length-authLen] + authPart := (*[types.MaxLength]byte)(unsafe.Pointer(currentPacket.StartAtOffset(0)))[etherLen+outerIPLen : length-authLen] if decapsulationSPI123(authPart, currentESPTail.Auth, currentESPHeader.IV, encryptionPart, context) == false { return false } @@ -82,8 +82,8 @@ func VectorEncapsulation(currentPackets []*packet.Packet, mask *[32]bool, notDro currentPackets[i].EncapsulateHead(etherLen, outerIPLen+espHeadLen) currentPackets[i].ParseL3() ipv4 := currentPackets[i].GetIPv4NoCheck() - ipv4.SrcAddr = packet.BytesToIPv4(111, 22, 3, 0) - ipv4.DstAddr = packet.BytesToIPv4(3, 22, 111, 0) + ipv4.SrcAddr = types.BytesToIPv4(111, 22, 3, 0) + ipv4.DstAddr = types.BytesToIPv4(3, 22, 111, 0) ipv4.VersionIhl = 0x45 ipv4.NextProtoID = esp notDrop[i] = true @@ -130,11 +130,11 @@ func vectorEncapsulationSPI123(currentPackets []*packet.Packet, n uint, context0 } } currentESPTail.paddingLen = paddingLength - currentESPTail.nextIP = common.IPNumber + currentESPTail.nextIP = types.IPNumber - context.vectorEncryptionPart[t] = (*[common.MaxLength]byte)(unsafe.Pointer(currentPackets[i+t].StartAtOffset(0)))[etherLen+outerIPLen+espHeadLen : newLength-authLen] + context.vectorEncryptionPart[t] = (*[types.MaxLength]byte)(unsafe.Pointer(currentPackets[i+t].StartAtOffset(0)))[etherLen+outerIPLen+espHeadLen : newLength-authLen] context.vectorIV[t] = currentESPHeader.IV[:] - context.vectorAuthPart[t] = (*[common.MaxLength]byte)(unsafe.Pointer(currentPackets[i+t].StartAtOffset(0)))[etherLen+outerIPLen : newLength-authLen] + context.vectorAuthPart[t] = (*[types.MaxLength]byte)(unsafe.Pointer(currentPackets[i+t].StartAtOffset(0)))[etherLen+outerIPLen : newLength-authLen] context.vectorAuthPlace[t] = currentESPTail.Auth[:] } Encrypt(context.vectorEncryptionPart, context.vectorEncryptionPart, context.vectorIV, Z, context) @@ -148,8 +148,8 @@ func ScalarEncapsulation(currentPacket *packet.Packet, context flow.UserContext) currentPacket.ParseL3() ipv4 := currentPacket.GetIPv4NoCheck() - ipv4.SrcAddr = packet.BytesToIPv4(111, 22, 3, 0) - ipv4.DstAddr = packet.BytesToIPv4(3, 22, 111, 0) + ipv4.SrcAddr = types.BytesToIPv4(111, 22, 3, 0) + ipv4.DstAddr = types.BytesToIPv4(3, 22, 111, 0) ipv4.VersionIhl = 0x45 ipv4.NextProtoID = esp @@ -180,16 +180,16 @@ func scalarEncapsulationSPI123(currentPacket *packet.Packet, context0 flow.UserC } } currentESPTail.paddingLen = paddingLength - currentESPTail.nextIP = common.IPNumber + currentESPTail.nextIP = types.IPNumber // Encryption - EncryptionPart := (*[common.MaxLength]byte)(currentPacket.StartAtOffset(0))[etherLen+outerIPLen+espHeadLen : newLength-authLen] + EncryptionPart := (*[types.MaxLength]byte)(currentPacket.StartAtOffset(0))[etherLen+outerIPLen+espHeadLen : newLength-authLen] context.modeEnc.(SetIVer).SetIV(currentESPHeader.IV[:]) context.modeEnc.CryptBlocks(EncryptionPart, EncryptionPart) // Authentication context.mac123.Reset() - AuthPart := (*[common.MaxLength]byte)(currentPacket.StartAtOffset(0))[etherLen+outerIPLen : newLength-authLen] + AuthPart := (*[types.MaxLength]byte)(currentPacket.StartAtOffset(0))[etherLen+outerIPLen : newLength-authLen] context.mac123.Write(AuthPart) copy(currentESPTail.Auth[:], context.mac123.Sum(nil)) } diff --git a/examples/ipsec/perf/Makefile b/examples/ipsec/perf/Makefile index 636e9002..7ceec28a 100644 --- a/examples/ipsec/perf/Makefile +++ b/examples/ipsec/perf/Makefile @@ -3,6 +3,6 @@ # license that can be found in the LICENSE file. PATH_TO_MK = ../../../mk -IMAGENAME = nff-go-examples +IMAGENAME = nff-go-ipsec-perf EXECUTABLES = ipsec include $(PATH_TO_MK)/leaf.mk diff --git a/examples/ipsec/stability/Makefile b/examples/ipsec/stability/Makefile index 06952023..ba3c189e 100644 --- a/examples/ipsec/stability/Makefile +++ b/examples/ipsec/stability/Makefile @@ -3,6 +3,6 @@ # license that can be found in the LICENSE file. PATH_TO_MK = ../../../mk -IMAGENAME = nff-go-examples +IMAGENAME = nff-go-ipsec-stability EXECUTABLES = stability include $(PATH_TO_MK)/leaf.mk diff --git a/examples/ipsec/stability/stability.go b/examples/ipsec/stability/stability.go index add0698c..32861850 100644 --- a/examples/ipsec/stability/stability.go +++ b/examples/ipsec/stability/stability.go @@ -4,6 +4,7 @@ package main +import "github.com/intel-go/nff-go/types" import "github.com/intel-go/nff-go/flow" import "github.com/intel-go/nff-go/packet" import "github.com/intel-go/nff-go/examples/ipsec" @@ -45,18 +46,18 @@ func main() { } func gen(pkt *packet.Packet, context flow.UserContext) { - packet.InitEmptyIPv4TCPPacket(pkt, 50) - ipv4 := pkt.GetIPv4() - tcp := pkt.GetTCPForIPv4() + packet.InitEmptyIPv4TCPPacket(pkt, 50) + ipv4 := pkt.GetIPv4() + tcp := pkt.GetTCPForIPv4() - pkt.Ether.DAddr = [6]uint8{1,2,3,4,5,6} - pkt.Ether.SAddr = [6]uint8{1,2,3,4,5,6} + pkt.Ether.DAddr = [6]uint8{1, 2, 3, 4, 5, 6} + pkt.Ether.SAddr = [6]uint8{1, 2, 3, 4, 5, 6} - ipv4.SrcAddr = packet.BytesToIPv4(111,111,111,111) - ipv4.DstAddr = packet.BytesToIPv4(222,222,222,222) + ipv4.SrcAddr = types.BytesToIPv4(111, 111, 111, 111) + ipv4.DstAddr = types.BytesToIPv4(222, 222, 222, 222) - tcp.SrcPort = packet.SwapBytesUint16(25) - tcp.DstPort = packet.SwapBytesUint16(35) + tcp.SrcPort = packet.SwapBytesUint16(25) + tcp.DstPort = packet.SwapBytesUint16(35) step++ if step == 100 { @@ -66,9 +67,9 @@ func gen(pkt *packet.Packet, context flow.UserContext) { } func dump_before(currentPacket *packet.Packet, context flow.UserContext) { - fmt.Printf("Raw bytes before=%x\n", currentPacket.GetRawPacketBytes()) + fmt.Printf("Raw bytes before=%x\n", currentPacket.GetRawPacketBytes()) } func dump_after(currentPacket *packet.Packet, context flow.UserContext) { - fmt.Printf("Raw bytes after =%x\n", currentPacket.GetRawPacketBytes()) + fmt.Printf("Raw bytes after =%x\n", currentPacket.GetRawPacketBytes()) } diff --git a/examples/kni.go b/examples/kni.go index 83a77c53..b31f556c 100644 --- a/examples/kni.go +++ b/examples/kni.go @@ -18,9 +18,9 @@ package main import ( "flag" - "github.com/intel-go/nff-go/common" "github.com/intel-go/nff-go/flow" "github.com/intel-go/nff-go/packet" + "github.com/intel-go/nff-go/types" ) var ping bool @@ -80,11 +80,11 @@ func pingSeparator(current *packet.Packet, ctx flow.UserContext) bool { if arp != nil { return false } else if ipv4 != nil { - if ipv4.NextProtoID == common.ICMPNumber { + if ipv4.NextProtoID == types.ICMPNumber { return false } } else if ipv6 != nil { - if ipv6.Proto == common.ICMPNumber { + if ipv6.Proto == types.ICMPNumber { return false } } diff --git a/examples/lb/Makefile b/examples/lb/Makefile new file mode 100644 index 00000000..4f53917c --- /dev/null +++ b/examples/lb/Makefile @@ -0,0 +1,8 @@ +# Copyright 2018 Intel Corporation. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +PATH_TO_MK = ../../mk +SUBDIRS = main + +include $(PATH_TO_MK)/intermediate.mk diff --git a/examples/lb/balancer.go b/examples/lb/balancer.go new file mode 100644 index 00000000..6aa78f00 --- /dev/null +++ b/examples/lb/balancer.go @@ -0,0 +1,163 @@ +// Copyright 2018 Intel Corporation. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package lb + +import ( + "crypto/sha256" + "fmt" + + "github.com/intel-go/nff-go/flow" + "github.com/intel-go/nff-go/packet" + "github.com/intel-go/nff-go/types" +) + +func balancer(pkt *packet.Packet, ctx flow.UserContext) bool { + pkt.ParseL3() + originalProtocol := pkt.Ether.EtherType + var worker int + + // Check packet protocol number + if originalProtocol == types.SwapARPNumber { + err := LBConfig.InputPort.neighCache.HandleIPv4ARPPacket(pkt) + if err != nil { + fmt.Println(err) + } + return false + } else if originalProtocol == types.SwapIPV4Number { + ipv4 := pkt.GetIPv4NoCheck() + if !LBConfig.TunnelSubnet.IPv4.CheckIPv4AddressWithinSubnet(ipv4.DstAddr) { + fmt.Println("Received IPv4 packet that is not targeted at balanced subnet", + LBConfig.TunnelPort.Subnet.IPv4.String(), + "it is targeted at address", ipv4.DstAddr.String(), "instead. Packet dropped.") + return false + } + worker = findWorkerIndexIPv4(pkt, ipv4) + } else if originalProtocol == types.SwapIPV6Number { + ipv6 := pkt.GetIPv6NoCheck() + if !LBConfig.TunnelSubnet.IPv6.CheckIPv6AddressWithinSubnet(ipv6.DstAddr) { + fmt.Println("Received IPv6 packet that is not targeted at balanced subnet", + LBConfig.TunnelPort.Subnet.IPv6.String(), + "it is targeted at address", ipv6.DstAddr.String(), "instead. Packet dropped.") + return false + } + worker = findWorkerIndexIPv6(pkt, ipv6) + } else { + return false + } + + workerIP := LBConfig.WorkerAddresses[worker] + workerMAC, found := LBConfig.TunnelPort.neighCache.LookupMACForIPv4(workerIP) + if !found { + fmt.Println("Not found MAC address for IP", workerIP.String()) + LBConfig.TunnelPort.neighCache.SendARPRequestForIPv4(workerIP, 0) + return false + } + + if !pkt.EncapsulateHead(types.EtherLen, types.IPv4MinLen+types.GRELen) { + fmt.Println("EncapsulateHead returned error") + return false + } + pkt.ParseL3() + + // Fill up L2 + pkt.Ether.SAddr = LBConfig.TunnelPort.macAddress + pkt.Ether.DAddr = workerMAC + pkt.Ether.EtherType = types.SwapIPV4Number + + // Fill up L3 + ipv4 := pkt.GetIPv4NoCheck() + length := pkt.GetPacketLen() + + // construct iphdr + ipv4.VersionIhl = 0x45 + ipv4.TypeOfService = 0 + ipv4.PacketID = 0x1513 + ipv4.FragmentOffset = 0 + ipv4.TimeToLive = 64 + + ipv4.TotalLength = packet.SwapBytesUint16(uint16(length - types.EtherLen)) + ipv4.NextProtoID = types.GRENumber + ipv4.SrcAddr = LBConfig.TunnelPort.Subnet.IPv4.Addr + ipv4.DstAddr = workerIP + ipv4.HdrChecksum = packet.SwapBytesUint16(packet.CalculateIPv4Checksum(ipv4)) + + // Fill up L4 + pkt.ParseL4ForIPv4() + gre := pkt.GetGREForIPv4() + gre.Flags = 0 + gre.NextProto = originalProtocol + + return true +} + +func findWorkerIndexIPv4(pkt *packet.Packet, ipv4 *packet.IPv4Hdr) int { + pkt.ParseL4ForIPv4() + hash := sha256.New() + sa := types.IPv4ToBytes(ipv4.SrcAddr) + hash.Write(sa[:]) + protocol := ipv4.NextProtoID + switch protocol { + case types.TCPNumber: + tcp := pkt.GetTCPNoCheck() + sp, dp := portsToByteSlices(tcp.SrcPort, tcp.DstPort) + hash.Write(sp) + hash.Write(dp) + case types.UDPNumber: + udp := pkt.GetUDPNoCheck() + sp, dp := portsToByteSlices(udp.SrcPort, udp.DstPort) + hash.Write(sp) + hash.Write(dp) + case types.ICMPNumber: + icmp := pkt.GetICMPNoCheck() + id, _ := portsToByteSlices(icmp.Identifier, icmp.SeqNum) + hash.Write(id) + } + hash.Write([]byte{protocol}) + da := types.IPv4ToBytes(ipv4.DstAddr) + hash.Write(da[:]) + + sum := hash.Sum(nil) + return int(sum[0]) % len(LBConfig.WorkerAddresses) +} + +func findWorkerIndexIPv6(pkt *packet.Packet, ipv6 *packet.IPv6Hdr) int { + pkt.ParseL4ForIPv6() + hash := sha256.New() + sa := ipv6.SrcAddr + hash.Write(sa[:]) + protocol := ipv6.Proto + switch protocol { + case types.TCPNumber: + tcp := pkt.GetTCPNoCheck() + sp, dp := portsToByteSlices(tcp.SrcPort, tcp.DstPort) + hash.Write(sp) + hash.Write(dp) + case types.UDPNumber: + udp := pkt.GetUDPNoCheck() + sp, dp := portsToByteSlices(udp.SrcPort, udp.DstPort) + hash.Write(sp) + hash.Write(dp) + case types.ICMPv6Number: + icmp := pkt.GetICMPNoCheck() + id, _ := portsToByteSlices(icmp.Identifier, icmp.SeqNum) + hash.Write(id) + } + hash.Write([]byte{protocol}) + da := ipv6.DstAddr + hash.Write(da[:]) + + sum := hash.Sum(nil) + return int(sum[0]) % len(LBConfig.WorkerAddresses) +} + +func portsToByteSlices(p1, p2 uint16) ([]byte, []byte) { + a1 := make([]byte, 2) + a1[0] = byte(p1 >> 8) + a1[1] = byte(p1) + a2 := make([]byte, 2) + a2[0] = byte(p2 >> 8) + a2[1] = byte(p2) + return a1, a2 +} diff --git a/examples/lb/config.go b/examples/lb/config.go new file mode 100644 index 00000000..b4042267 --- /dev/null +++ b/examples/lb/config.go @@ -0,0 +1,68 @@ +// Copyright 2018 Intel Corporation. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package lb + +import ( + "encoding/json" + "os" + + "github.com/intel-go/nff-go/flow" + "github.com/intel-go/nff-go/packet" + "github.com/intel-go/nff-go/types" +) + +type NetworkSubnet struct { + IPv4 types.IPv4Subnet `json:"ipv4"` + IPv6 types.IPv6Subnet `json:"ipv6"` +} + +type IpPort struct { + Index uint16 `json:"index"` + Subnet NetworkSubnet `json:"subnet"` + neighCache *packet.NeighboursLookupTable + macAddress types.MACAddress +} + +type LoadBalancerConfig struct { + InputPort IpPort `json:"input-port"` + TunnelPort IpPort `json:"tunnel-port"` + TunnelSubnet NetworkSubnet `json:"tunnel-subnet"` + WorkerAddresses []types.IPv4Address `json:"worker-addresses"` +} + +var LBConfig LoadBalancerConfig + +func ReadConfig(fileName string) error { + file, err := os.Open(fileName) + if err != nil { + return err + } + decoder := json.NewDecoder(file) + + err = decoder.Decode(&LBConfig) + if err != nil { + return err + } + + return nil +} + +func InitFlows() { + ioFlow, err := flow.SetReceiver(LBConfig.InputPort.Index) + flow.CheckFatal(err) + flow.CheckFatal(flow.SetHandlerDrop(ioFlow, balancer, nil)) + flow.CheckFatal(flow.SetSender(ioFlow, LBConfig.TunnelPort.Index)) + ioFlow, err = flow.SetReceiver(LBConfig.TunnelPort.Index) + flow.CheckFatal(flow.SetHandlerDrop(ioFlow, arpHandler, nil)) + flow.CheckFatal(flow.SetStopper(ioFlow)) + + LBConfig.InputPort.initPort() + LBConfig.TunnelPort.initPort() +} + +func (port *IpPort) initPort() { + port.macAddress = flow.GetPortMACAddress(port.Index) + port.neighCache = packet.NewNeighbourTable(port.Index, port.macAddress, port.Subnet.IPv4.Addr, port.Subnet.IPv6.Addr) +} diff --git a/examples/lb/main/.gitignore b/examples/lb/main/.gitignore new file mode 100644 index 00000000..aa1a077b --- /dev/null +++ b/examples/lb/main/.gitignore @@ -0,0 +1 @@ +lb diff --git a/examples/nffPktgen/perfTest/Dockerfile b/examples/lb/main/Dockerfile similarity index 86% rename from examples/nffPktgen/perfTest/Dockerfile rename to examples/lb/main/Dockerfile index 170a9213..7fb3c943 100644 --- a/examples/nffPktgen/perfTest/Dockerfile +++ b/examples/lb/main/Dockerfile @@ -1,4 +1,4 @@ -# Copyright 2017 Intel Corporation. +# Copyright 2019 Intel Corporation. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. @@ -8,4 +8,6 @@ FROM ${USER_NAME}/nff-go-base LABEL RUN docker run -it --privileged -v /sys/bus/pci/drivers:/sys/bus/pci/drivers -v /sys/kernel/mm/hugepages:/sys/kernel/mm/hugepages -v /sys/devices/system/node:/sys/devices/system/node -v /dev:/dev --name NAME -e NAME=NAME -e IMAGE=IMAGE IMAGE WORKDIR /workdir -COPY perfTest . + +COPY lb . +COPY config.json . diff --git a/examples/nffPktgen/sendGetBack/Makefile b/examples/lb/main/Makefile similarity index 63% rename from examples/nffPktgen/sendGetBack/Makefile rename to examples/lb/main/Makefile index 48b152d5..7a3f7af2 100644 --- a/examples/nffPktgen/sendGetBack/Makefile +++ b/examples/lb/main/Makefile @@ -1,9 +1,11 @@ -# Copyright 2017 Intel Corporation. +# Copyright 2018 Intel Corporation. # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. PATH_TO_MK = ../../../mk -IMAGENAME = nff-pktgen -EXECUTABLES = sendGetBack +IMAGENAME = lb +EXECUTABLES = lb + +lb: lb.go ../config.go include $(PATH_TO_MK)/leaf.mk diff --git a/examples/lb/main/config-aws.json b/examples/lb/main/config-aws.json new file mode 100644 index 00000000..06c80121 --- /dev/null +++ b/examples/lb/main/config-aws.json @@ -0,0 +1,24 @@ +{ + "input-port": { + "index": 0, + "subnet": { + "ipv4": "10.0.14.10/24", + "ipv6": "2600:1f16:80:ad14::10/64" + } + }, + "tunnel-port": { + "index": 1, + "subnet": { + "ipv4": "10.0.16.10/24", + "ipv6": "2600:1f16:80:ad16::10/64" + } + }, + "tunnel-subnet": { + "ipv4": "10.0.18.0/24", + "ipv6": "2600:1f16:80:ad18::/64" + }, + "worker-addresses": [ + "10.0.16.20", + "10.0.16.30" + ] +} diff --git a/examples/lb/main/config.json b/examples/lb/main/config.json new file mode 100644 index 00000000..e7813bd3 --- /dev/null +++ b/examples/lb/main/config.json @@ -0,0 +1,24 @@ +{ + "input-port": { + "index": 0, + "subnet": { + "ipv4": "192.168.14.1/24", + "ipv6": "fd14::1/64" + } + }, + "tunnel-port": { + "index": 1, + "subnet": { + "ipv4": "192.168.16.1/24", + "ipv6": "fd16::1/64" + } + }, + "tunnel-subnet": { + "ipv4": "192.168.18.0/24", + "ipv6": "fd18::/64" + }, + "worker-addresses": [ + "192.168.16.2", + "192.168.16.3" + ] +} diff --git a/examples/lb/main/lb.go b/examples/lb/main/lb.go new file mode 100644 index 00000000..3a448201 --- /dev/null +++ b/examples/lb/main/lb.go @@ -0,0 +1,34 @@ +// Copyright 2018 Intel Corporation. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "flag" + + "github.com/intel-go/nff-go/flow" + + "github.com/intel-go/nff-go/examples/lb" +) + +func main() { + cores := flag.String("cores", "", "Specify CPU cores to use.") + configFile := flag.String("config", "config.json", "Specify config file name.") + noscheduler := flag.Bool("no-scheduler", false, "Disable scheduler.") + dpdkLogLevel := flag.String("dpdk", "--log-level=0", "Passes an arbitrary argument to dpdk EAL.") + flag.Parse() + + // Read config + flow.CheckFatal(lb.ReadConfig(*configFile)) + + nffgoconfig := flow.Config{ + CPUList: *cores, + DPDKArgs: []string{*dpdkLogLevel}, + DisableScheduler: *noscheduler, + } + + flow.CheckFatal(flow.SystemInit(&nffgoconfig)) + lb.InitFlows() + flow.CheckFatal(flow.SystemStart()) +} diff --git a/examples/lb/util.go b/examples/lb/util.go new file mode 100644 index 00000000..0e35ce70 --- /dev/null +++ b/examples/lb/util.go @@ -0,0 +1,27 @@ +// Copyright 2018 Intel Corporation. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package lb + +import ( + "fmt" + + "github.com/intel-go/nff-go/flow" + "github.com/intel-go/nff-go/packet" + "github.com/intel-go/nff-go/types" +) + +func arpHandler(pkt *packet.Packet, ctx flow.UserContext) bool { + pkt.ParseL3() + protocol := pkt.Ether.EtherType + + if protocol == types.SwapARPNumber { + err := LBConfig.TunnelPort.neighCache.HandleIPv4ARPPacket(pkt) + if err != nil { + fmt.Println(err) + } + return false + } + return true +} diff --git a/examples/netlink.go b/examples/netlink.go index ff2a0269..b2002b72 100755 --- a/examples/netlink.go +++ b/examples/netlink.go @@ -13,6 +13,7 @@ import ( "github.com/intel-go/nff-go/common" "github.com/intel-go/nff-go/flow" "github.com/intel-go/nff-go/packet" + "github.com/intel-go/nff-go/types" "golang.org/x/sys/unix" "github.com/vishvananda/netlink" @@ -24,10 +25,10 @@ var lpm *packet.LPM // LPM binds ip with number and below // is a struct to get ip by binded value -var portToIP []uint32 +var portToIP []types.IPv4Address // length of portToIp -var lenP2IP uint32 +var lenP2IP types.IPv4Address func main() { inport := flag.Uint("inport", 0, "port for receiver") @@ -59,23 +60,23 @@ func main() { func handler(current *packet.Packet, ctx flow.UserContext) { ipv4, _, _ := current.ParseAllKnownL3() if ipv4 != nil { - var next uint32 + var next types.IPv4Address if lpm.Lookup(ipv4.DstAddr, &next) { - common.LogDebug(common.Debug, "gateway for packet: ", packet.IPv4ToBytes(portToIP[next])) + common.LogDebug(common.Debug, "gateway for packet: ", types.IPv4ToBytes(portToIP[next])) } } } -func netToNffIPv4(netIP net.IP) uint32 { +func netToNffIPv4(netIP net.IP) types.IPv4Address { if netIP == nil || len(netIP) != 4 { return 0 } - return packet.BytesToIPv4(netIP[0], netIP[1], netIP[2], netIP[3]) + return types.BytesToIPv4(netIP[0], netIP[1], netIP[2], netIP[3]) } -func getIPAndDepth(netIP *net.IPNet) (uint32, uint8, error) { +func getIPAndDepth(netIP *net.IPNet) (types.IPv4Address, uint8, error) { var ( - ip uint32 + ip types.IPv4Address depth int ) if netIP != nil { diff --git a/examples/nffPktgen/Makefile b/examples/nffPktgen/Makefile index 69366737..39d5fdcd 100644 --- a/examples/nffPktgen/Makefile +++ b/examples/nffPktgen/Makefile @@ -4,6 +4,6 @@ PATH_TO_MK = ../../mk IMAGENAME = nff-pktgen -SUBDIRS = sendGetBack perfTest +SUBDIRS = testing include $(PATH_TO_MK)/intermediate.mk diff --git a/examples/nffPktgen/README.md b/examples/nffPktgen/README.md index 8745a322..2a1d60f3 100644 --- a/examples/nffPktgen/README.md +++ b/examples/nffPktgen/README.md @@ -55,7 +55,7 @@ is a main generator nunction. Context is obligatory. // check error flow.CheckFatal(err) // set generator - outFlow, err := flow.SetFastGenerator(generator.Generate, speed, &context) + outFlow, _, err := flow.SetFastGenerator(generator.Generate, speed, &context) // check error flow.CheckFatal(err) // send diff --git a/examples/nffPktgen/generator/generator.go b/examples/nffPktgen/generator/generator.go index d416e219..809685cd 100644 --- a/examples/nffPktgen/generator/generator.go +++ b/examples/nffPktgen/generator/generator.go @@ -5,262 +5,95 @@ package generator import ( - "bytes" - "encoding/binary" "fmt" - "math" "math/rand" - "os" - "sync/atomic" "unsafe" - "github.com/intel-go/nff-go/common" - "github.com/intel-go/nff-go/flow" "github.com/intel-go/nff-go/packet" + "github.com/intel-go/nff-go/types" ) -var gen generator - -type generator struct { - count uint64 -} - -// GetGenerator returns generator struct pointer -// generator is single and created only once -func GetGenerator() *generator { - return &gen -} - -// GetGeneratedNumber returns a number of packets generated -func (g *generator) GetGeneratedNumber() uint64 { - return atomic.LoadUint64(&(g.count)) -} - -// ReadConfig function reads and parses config file. -func ReadConfig(fileName string) ([]*MixConfig, error) { - f, err := os.Open(fileName) - if err != nil { - return nil, fmt.Errorf("opening file failed with: %v ", err) - } - cfg, err := ParseConfig(f) - if err != nil { - return nil, fmt.Errorf("parsing config failed with: %v", err) - } - return cfg, nil -} - -func addAddr(a *[]byte, b []byte) { - var offset int - aLen := len(*a) - bLen := len(b) - if aLen < bLen { - add := make([]byte, bLen-aLen) - *a = append(add, *a...) - } else { - offset = aLen - bLen - } - var next byte - for i := bLen - 1; i >= 0; i-- { - add := (*a)[i+offset] + b[i] + next - if add > 255 { - next = 1 - (*a)[i+offset] = byte(255) - add - } else { - (*a)[i+offset] = add - next = 0 - } - } -} - -func getNextAddr(addr *AddrRange) []uint8 { - addAddr(&(addr.Current), addr.Incr) - // if current < min or current > max, copy min to current - if bytes.Compare(addr.Current, addr.Min) < 0 || bytes.Compare(addr.Current, addr.Max) > 0 { - copy(addr.Current, addr.Min) - } - return addr.Current -} - -func copyAddr(destination []uint8, source []uint8, size int) { - if size < len(source) { - copy(destination[:], source[len(source)-size:]) - } else { - copy(destination[size-len(source):], source[:]) +func getNextValue(addr *AddrRange) { + if addr.Inc == 0 { + return } -} - -func getNextPort(port *PortRange) (nextPort uint16) { - if port.Current < port.Min || port.Current > port.Max { - port.Current = port.Min + addr.Current += addr.Inc + if addr.Current > addr.Max { + addr.Current = addr.Min + } else if addr.Current < addr.Min { + addr.Current = addr.Max } - nextPort = port.Current - port.Current += port.Incr - return nextPort } -func getNextSeqNumber(seq *Sequence, rnd *rand.Rand) (nextSeqNum uint32) { - nextSeqNum = seq.Next +func getNextSeqNumber(seq *Sequence, rnd *rand.Rand) { if seq.Type == RANDOM { - seq.Next = rnd.Uint32() + seq.Current = rnd.Uint32() } else if seq.Type == INCREASING { - seq.Next++ + seq.Current++ } - return nextSeqNum } type dataCopier func(unsafe.Pointer, uint, *rand.Rand, unsafe.Pointer) func copyRaw(configuration unsafe.Pointer, size uint, rnd *rand.Rand, copyTo unsafe.Pointer) { - data := (*Raw)(configuration) - copy((*[1 << 30]uint8)(copyTo)[0:size], ([]uint8(data.Data))) + data := (*string)(configuration) + copy((*[1 << 30]uint8)(copyTo)[0:size], ([]uint8(*data))) } func copyRand(configuration unsafe.Pointer, size uint, rnd *rand.Rand, copyTo unsafe.Pointer) { - packetData := (*[1 << 30]byte)(copyTo)[0:size] + packetData := (*[1 << 30]uint64)(copyTo)[0 : size/8] for i := range packetData { - packetData[i] = byte(rnd.Int()) + packetData[i] = rnd.Uint64() + } + tail := (*[1 << 30]byte)(copyTo)[size-size%8 : size] + for i := range tail { + tail[i] = byte(rnd.Uint64()) } } -func getDataSizeType(configuration unsafe.Pointer, dtype DataType, rnd *rand.Rand) (uint, unsafe.Pointer, dataCopier) { - switch dtype { +func getDataSizeType(configuration *RawBytes, rnd *rand.Rand) (uint, unsafe.Pointer, dataCopier) { + switch configuration.DType { case RAWDATA: - data := (*Raw)(configuration) - return uint(len(data.Data)), configuration, copyRaw + return uint(len(configuration.Data)), unsafe.Pointer(&configuration.Data), copyRaw case RANDDATA: - data := (*RandBytes)(configuration) - maxZise := data.Size + data.Deviation + data := configuration.Rand + maxSise := data.Size + data.Deviation minSize := data.Size - data.Deviation - randSize := uint(rnd.Float64()*float64(maxZise-minSize) + float64(minSize)) - return randSize, configuration, copyRand + randSize := uint(rnd.Float64()*float64(maxSise-minSize) + float64(minSize)) + return randSize, unsafe.Pointer(uintptr(0)), copyRand case PDISTDATA: - data := (*[]PDistEntry)(configuration) prob := 0.0 - rndN := math.Abs(rnd.Float64()) - maxProb := PDistEntry{Probability: 0} - for _, item := range *data { + rndN := rnd.Float64() + for _, item := range configuration.Dist { + // Sum of all was checked to be 1 prob += item.Probability if rndN <= prob { - return getDataSizeType(item.Data, item.DType, rnd) - } - if item.Probability > maxProb.Probability { - maxProb = item + return getDataSizeType1(item.Data, item.Rand, item.DType, rnd) } } - if prob <= 0 || prob > 1 { - panic(fmt.Sprintf("sum of pdist probabilities is invalid, %f", prob)) - } - // get the variant with max prob - // if something went wrong and rand did not match any prob - // may happen if sum of prob was not 1 - return getDataSizeType(maxProb.Data, maxProb.DType, rnd) } panic(fmt.Sprintf("unknown data type")) } -func getGenerator(configuration *PacketConfig) (func(*packet.Packet, *PacketConfig, *rand.Rand), error) { - switch configuration.DType { - case ETHERHDR: - l2 := (*EtherConfig)(configuration.Data) - switch l2.DType { - case IPHDR: - l3 := (*IPConfig)(l2.Data) - switch l3.DType { - case TCPHDR: - return generateTCPIP, nil - case UDPHDR: - return generateUDPIP, nil - case ICMPHDR: - return generateICMPIP, nil - case RAWDATA, RANDDATA, PDISTDATA: - return generateIP, nil - default: - return nil, fmt.Errorf("unknown packet l4 configuration") - } - case ARPHDR: - return generateARP, nil - case RAWDATA, RANDDATA, PDISTDATA: - return generateEther, nil - default: - return nil, fmt.Errorf("unknown packet l3 configuration") - } - default: - return nil, fmt.Errorf("unknown packet l2 configuration") - } -} - -// one unit for each mix -type generatorTableUnit struct { - have, need uint32 - generatorFunc func(*packet.Packet, *PacketConfig, *rand.Rand) - config *PacketConfig -} - -func (gtu *generatorTableUnit) String() string { - return fmt.Sprintf("need: %d, config: %v\n", gtu.need, gtu.config) -} - -type genParameters struct { - table []generatorTableUnit - next uint32 - length uint32 - rnd *rand.Rand -} - -func (gp genParameters) Copy() interface{} { - ret := new(genParameters) - ret.table = make([]generatorTableUnit, len(gp.table)) - copy(ret.table, gp.table) - ret.length = gp.length - ret.rnd = rand.New(rand.NewSource(13)) - return ret -} - -func (gp genParameters) Delete() { -} - -// GetContext gets generator context according to config -func GetContext(mixConfig []*MixConfig) (*genParameters, error) { - var t []generatorTableUnit - for _, packetConfig := range mixConfig { - genFunc, err := getGenerator(packetConfig.Config) - if err != nil { - return nil, err - } - tu := generatorTableUnit{have: 0, need: packetConfig.Quantity, generatorFunc: genFunc, config: packetConfig.Config} - t = append(t, tu) - } - ret := new(genParameters) - ret.table = t - ret.length = uint32(len(t)) - ret.rnd = rand.New(rand.NewSource(13)) - return ret, nil -} - -// Generate is a main generatior func -func Generate(pkt *packet.Packet, context flow.UserContext) { - genP := context.(*genParameters) - if genP.length > 1 { - if genP.table[genP.next].have == genP.table[genP.next].need { - genP.table[genP.next].have = 0 - if genP.next+1 < genP.length { - genP.next++ - } else { - genP.next = 0 - } - } +func getDataSizeType1(Data string, Rand RandBytes, DType DataType, rnd *rand.Rand) (uint, unsafe.Pointer, dataCopier) { + switch DType { + case RAWDATA: + return uint(len(Data)), unsafe.Pointer(&Data), copyRaw + case RANDDATA: + maxSise := Rand.Size + Rand.Deviation + minSize := Rand.Size - Rand.Deviation + randSize := uint(rnd.Float64()*float64(maxSise-minSize) + float64(minSize)) + return randSize, unsafe.Pointer(uintptr(0)), copyRand } - genP.table[genP.next].generatorFunc(pkt, genP.table[genP.next].config, genP.rnd) - atomic.AddUint64(&(gen.count), 1) - genP.table[genP.next].have++ + panic(fmt.Sprintf("unknown data type")) } func generateEther(pkt *packet.Packet, config *PacketConfig, rnd *rand.Rand) { if pkt == nil { panic("Failed to create new packet") } - l2 := (*EtherConfig)(config.Data) - size, dataConfig, copyDataFunc := getDataSizeType(l2.Data, l2.DType, rnd) + l2 := &config.Ether + size, dataConfig, copyDataFunc := getDataSizeType(&l2.Bytes, rnd) if !packet.InitEmptyPacket(pkt, size) { panic(fmt.Sprintf("InitEmptyPacket failed")) @@ -269,77 +102,63 @@ func generateEther(pkt *packet.Packet, config *PacketConfig, rnd *rand.Rand) { fillEtherHdr(pkt, l2) } -func generateIP(pkt *packet.Packet, config *PacketConfig, rnd *rand.Rand) { +func generateIPv4(pkt *packet.Packet, config *PacketConfig, rnd *rand.Rand) { if pkt == nil { panic("Failed to create new packet") } - l2 := (*EtherConfig)(config.Data) - l3 := (*IPConfig)(l2.Data) - size, dataConfig, copyDataFunc := getDataSizeType(l3.Data, l3.DType, rnd) - if l3.Version == 4 { - if !packet.InitEmptyIPv4Packet(pkt, size) { - panic(fmt.Sprintf("InitEmptyIPv4Packet returned false")) - } - } else if l3.Version == 6 { - if !packet.InitEmptyIPv6Packet(pkt, size) { - panic(fmt.Sprintf("InitEmptyIPv6Packet returned false")) - } - } else { - panic(fmt.Sprintf("fillPacketl3 failed, unknovn version %d", l3.Version)) + l2 := &config.Ether + l3 := &l2.IPv4 + size, dataConfig, copyDataFunc := getDataSizeType(&l3.Bytes, rnd) + //size, _, _ := getDataSizeType(&l3.Bytes, rnd) + //size := uint(22) + if !packet.InitEmptyIPv4Packet(pkt, size) { + panic(fmt.Sprintf("InitEmptyIPv4Packet returned false")) } copyDataFunc(dataConfig, size, rnd, pkt.Data) fillEtherHdr(pkt, l2) - fillIPHdr(pkt, l3) - if l3.Version == 4 { - pktIP := (*packet.IPv4Hdr)(pkt.L3) - pktIP.HdrChecksum = packet.SwapBytesUint16(packet.CalculateIPv4Checksum(pktIP)) - } -} - -func To4(addr []byte) []byte { - if len(addr) > common.IPv4AddrLen { - return addr[len(addr)-common.IPv4AddrLen:] - } - return addr + fillIPv4Hdr(pkt, l3) + pktIP := (*packet.IPv4Hdr)(pkt.L3) + pktIP.HdrChecksum = packet.SwapBytesUint16(packet.CalculateIPv4Checksum(pktIP)) } func generateARP(pkt *packet.Packet, config *PacketConfig, rnd *rand.Rand) { if pkt == nil { panic("Failed to create new packet") } - l2 := (*EtherConfig)(config.Data) - l3 := (*ARPConfig)(l2.Data) - var SHA, THA [common.EtherAddrLen]uint8 - copyAddr(SHA[:], getNextAddr(&(l3.SHA)), common.EtherAddrLen) - SPA := binary.LittleEndian.Uint32(To4(getNextAddr(&(l3.SPA)))) - switch l3.Operation { - case packet.ARPRequest: + l2 := &config.Ether + l3 := &l2.ARP + var SHA, THA [types.EtherAddrLen]uint8 + SHA = [types.EtherAddrLen]uint8{byte(l3.SHA.Current >> 40), byte(l3.SHA.Current >> 32), byte(l3.SHA.Current >> 24), byte(l3.SHA.Current >> 16), byte(l3.SHA.Current >> 8), byte(l3.SHA.Current)} + getNextValue(&l3.SHA) + SPA := packet.SwapBytesIPv4Addr(types.IPv4Address(l3.SPA.Current)) + getNextValue(&l3.SPA) + if l3.Operation == packet.ARPRequest { if l3.Gratuitous { if !packet.InitGARPAnnouncementRequestPacket(pkt, SHA, SPA) { panic(fmt.Sprintf("InitGARPAnnouncementRequestPacket returned false")) } } else { - TPA := binary.LittleEndian.Uint32(To4(getNextAddr(&(l3.TPA)))) + TPA := packet.SwapBytesIPv4Addr(types.IPv4Address(l3.TPA.Current)) + getNextValue(&l3.TPA) if !packet.InitARPRequestPacket(pkt, SHA, SPA, TPA) { panic(fmt.Sprintf("InitARPRequestPacket returned false")) } } - case packet.ARPReply: + } else { //packet.ARPReply if l3.Gratuitous { if !packet.InitGARPAnnouncementReplyPacket(pkt, SHA, SPA) { panic(fmt.Sprintf("InitGARPAnnouncementReplyPacket returned false")) } } else { - copyAddr(THA[:], getNextAddr(&(l3.THA)), common.EtherAddrLen) - TPA := binary.LittleEndian.Uint32(To4(getNextAddr(&(l3.TPA)))) + THA = [types.EtherAddrLen]uint8{byte(l3.THA.Current >> 40), byte(l3.THA.Current >> 32), byte(l3.THA.Current >> 24), byte(l3.THA.Current >> 16), byte(l3.THA.Current >> 8), byte(l3.THA.Current)} + getNextValue(&l3.THA) + TPA := packet.SwapBytesIPv4Addr(types.IPv4Address(l3.TPA.Current)) + getNextValue(&l3.TPA) if !packet.InitARPReplyPacket(pkt, SHA, THA, SPA, TPA) { panic(fmt.Sprintf("InitARPReplyPacket returned false")) } } - default: - panic(fmt.Sprintf("unsupported operation code: %d", l3.Operation)) } - copyAddr(pkt.Ether.DAddr[:], getNextAddr(&(l2.DAddr)), common.EtherAddrLen) if l2.VLAN != nil { if !pkt.AddVLANTag(l2.VLAN.TCI) { panic("failed to add vlan tag to arp packet") @@ -347,120 +166,90 @@ func generateARP(pkt *packet.Packet, config *PacketConfig, rnd *rand.Rand) { } } -func generateTCPIP(pkt *packet.Packet, config *PacketConfig, rnd *rand.Rand) { +func generateTCPIPv4(pkt *packet.Packet, config *PacketConfig, rnd *rand.Rand) { if pkt == nil { panic("Failed to create new packet") } - l2 := (*EtherConfig)(config.Data) - l3 := (*IPConfig)(l2.Data) - l4 := (*TCPConfig)(l3.Data) - size, dataConfig, copyDataFunc := getDataSizeType(l4.Data, l4.DType, rnd) - if l3.Version == 4 { - if !packet.InitEmptyIPv4TCPPacket(pkt, size) { - panic(fmt.Sprintf("InitEmptyIPv4TCPPacket returned false")) - } - } else if l3.Version == 6 { - if !packet.InitEmptyIPv6TCPPacket(pkt, size) { - panic(fmt.Sprintf("InitEmptyIPv6TCPPacket returned false")) - } - } else { - panic(fmt.Sprintf("fill packet l4 failed, unknovn version %d", l3.Version)) + l2 := &config.Ether + l3 := &l2.IPv4 + l4 := &l3.TCP + size, dataConfig, copyDataFunc := getDataSizeType(&l4.Bytes, rnd) + if !packet.InitEmptyIPv4TCPPacket(pkt, size) { + panic(fmt.Sprintf("InitEmptyIPv4TCPPacket returned false")) } copyDataFunc(dataConfig, size, rnd, pkt.Data) fillEtherHdr(pkt, l2) - fillIPHdr(pkt, l3) + fillIPv4Hdr(pkt, l3) fillTCPHdr(pkt, l4, rnd) pktTCP := (*packet.TCPHdr)(pkt.L4) - if l3.Version == 4 { - pktIP := (*packet.IPv4Hdr)(pkt.L3) - pktIP.HdrChecksum = packet.SwapBytesUint16(packet.CalculateIPv4Checksum(pktIP)) - pktTCP.Cksum = packet.SwapBytesUint16(packet.CalculateIPv4TCPChecksum(pktIP, pktTCP, pkt.Data)) - } else if l3.Version == 6 { - pktIP := (*packet.IPv6Hdr)(pkt.L3) - pktTCP.Cksum = packet.SwapBytesUint16(packet.CalculateIPv6TCPChecksum(pktIP, pktTCP, pkt.Data)) - } + pktIP := (*packet.IPv4Hdr)(pkt.L3) + pktIP.HdrChecksum = packet.SwapBytesUint16(packet.CalculateIPv4Checksum(pktIP)) + pktTCP.Cksum = packet.SwapBytesUint16(packet.CalculateIPv4TCPChecksum(pktIP, pktTCP, pkt.Data)) } -func generateUDPIP(pkt *packet.Packet, config *PacketConfig, rnd *rand.Rand) { +func generateUDPIPv4(pkt *packet.Packet, config *PacketConfig, rnd *rand.Rand) { if pkt == nil { panic("Failed to create new packet") } - l2 := (*EtherConfig)(config.Data) - l3 := (*IPConfig)(l2.Data) - l4 := (*UDPConfig)(l3.Data) - size, dataConfig, copyDataFunc := getDataSizeType(l4.Data, l4.DType, rnd) - if l3.Version == 4 { - if !packet.InitEmptyIPv4UDPPacket(pkt, size) { - panic(fmt.Sprintf("InitEmptyIPv4UDPPacket returned false")) - } - } else if l3.Version == 6 { - if !packet.InitEmptyIPv6UDPPacket(pkt, size) { - panic(fmt.Sprintf("InitEmptyIPv6UDPPacket returned false")) - } - } else { - panic(fmt.Sprintf("fill packet l4 failed, unknovn version %d", l3.Version)) + l2 := &config.Ether + l3 := &l2.IPv4 + l4 := &l3.UDP + size, dataConfig, copyDataFunc := getDataSizeType(&l4.Bytes, rnd) + if !packet.InitEmptyIPv4UDPPacket(pkt, size) { + panic(fmt.Sprintf("InitEmptyIPv4UDPPacket returned false")) } copyDataFunc(dataConfig, size, rnd, pkt.Data) fillEtherHdr(pkt, l2) - fillIPHdr(pkt, l3) + fillIPv4Hdr(pkt, l3) fillUDPHdr(pkt, l4) pktUDP := (*packet.UDPHdr)(pkt.L4) - if l3.Version == 4 { - pktIP := (*packet.IPv4Hdr)(pkt.L3) - pktIP.HdrChecksum = packet.SwapBytesUint16(packet.CalculateIPv4Checksum(pktIP)) - pktUDP.DgramCksum = packet.SwapBytesUint16(packet.CalculateIPv4UDPChecksum(pktIP, pktUDP, pkt.Data)) - } else if l3.Version == 6 { - pktIP := (*packet.IPv6Hdr)(pkt.L3) - pktUDP.DgramCksum = packet.SwapBytesUint16(packet.CalculateIPv6UDPChecksum(pktIP, pktUDP, pkt.Data)) - } + pktIP := (*packet.IPv4Hdr)(pkt.L3) + pktIP.HdrChecksum = packet.SwapBytesUint16(packet.CalculateIPv4Checksum(pktIP)) + pktUDP.DgramCksum = packet.SwapBytesUint16(packet.CalculateIPv4UDPChecksum(pktIP, pktUDP, pkt.Data)) } -func generateICMPIP(pkt *packet.Packet, config *PacketConfig, rnd *rand.Rand) { +func generateICMPIPv4(pkt *packet.Packet, config *PacketConfig, rnd *rand.Rand) { if pkt == nil { panic("Failed to create new packet") } - l2 := (*EtherConfig)(config.Data) - l3 := (*IPConfig)(l2.Data) - l4 := (*ICMPConfig)(l3.Data) - size, dataConfig, copyDataFunc := getDataSizeType(l4.Data, l4.DType, rnd) - if l3.Version == 4 { - if !packet.InitEmptyIPv4ICMPPacket(pkt, size) { - panic(fmt.Sprintf("InitEmptyIPv4ICMPPacket returned false")) - } - } else if l3.Version == 6 { - if !packet.InitEmptyIPv6ICMPPacket(pkt, size) { - panic(fmt.Sprintf("InitEmptyIPv6ICMPPacket returned false")) - } - } else { - panic(fmt.Sprintf("fill packet l4 failed, unknovn version %d", l3.Version)) + l2 := &config.Ether + l3 := &l2.IPv4 + l4 := &l3.ICMP + size, dataConfig, copyDataFunc := getDataSizeType(&l4.Bytes, rnd) + if !packet.InitEmptyIPv4ICMPPacket(pkt, size) { + panic(fmt.Sprintf("InitEmptyIPv4ICMPPacket returned false")) } copyDataFunc(dataConfig, size, rnd, pkt.Data) fillEtherHdr(pkt, l2) - fillIPHdr(pkt, l3) + fillIPv4Hdr(pkt, l3) fillICMPHdr(pkt, l4, rnd) pktICMP := (*packet.ICMPHdr)(pkt.L4) - if l3.Version == 4 { - pktIP := (*packet.IPv4Hdr)(pkt.L3) - pktIP.HdrChecksum = packet.SwapBytesUint16(packet.CalculateIPv4Checksum(pktIP)) - pktICMP.Cksum = packet.SwapBytesUint16(packet.CalculateIPv4ICMPChecksum(pktIP, pktICMP, pkt.Data)) - } else if l3.Version == 6 { - pktIP := (*packet.IPv6Hdr)(pkt.L3) - pktICMP.Cksum = packet.SwapBytesUint16(packet.CalculateIPv6ICMPChecksum(pktIP, pktICMP, pkt.Data)) - } + pktIP := (*packet.IPv4Hdr)(pkt.L3) + pktIP.HdrChecksum = packet.SwapBytesUint16(packet.CalculateIPv4Checksum(pktIP)) + pktICMP.Cksum = packet.SwapBytesUint16(packet.CalculateIPv4ICMPChecksum(pktIP, pktICMP, pkt.Data)) } func fillTCPHdr(pkt *packet.Packet, l4 *TCPConfig, rnd *rand.Rand) { emptyPacketTCP := (*packet.TCPHdr)(pkt.L4) - emptyPacketTCP.SrcPort = packet.SwapBytesUint16(getNextPort(&(l4.SPort))) - emptyPacketTCP.DstPort = packet.SwapBytesUint16(getNextPort(&(l4.DPort))) - emptyPacketTCP.SentSeq = packet.SwapBytesUint32(getNextSeqNumber((&l4.Seq), rnd)) + emptyPacketTCP.SrcPort = packet.SwapBytesUint16(uint16(l4.SPort.Current)) + emptyPacketTCP.DstPort = packet.SwapBytesUint16(uint16(l4.DPort.Current)) + emptyPacketTCP.SentSeq = packet.SwapBytesUint32(l4.Seq.Current) emptyPacketTCP.TCPFlags = l4.Flags + if l4.DPort.Inc != 0 { + getNextValue(&l4.DPort) + } + if l4.SPort.Inc != 0 { + getNextValue(&l4.SPort) + } + getNextSeqNumber(&l4.Seq, rnd) } func fillUDPHdr(pkt *packet.Packet, l4 *UDPConfig) { emptyPacketUDP := (*packet.UDPHdr)(pkt.L4) - emptyPacketUDP.SrcPort = packet.SwapBytesUint16(getNextPort(&(l4.SPort))) - emptyPacketUDP.DstPort = packet.SwapBytesUint16(getNextPort(&(l4.DPort))) + emptyPacketUDP.SrcPort = packet.SwapBytesUint16(uint16(l4.SPort.Current)) + emptyPacketUDP.DstPort = packet.SwapBytesUint16(uint16(l4.DPort.Current)) + getNextValue(&l4.DPort) + getNextValue(&l4.SPort) } func fillICMPHdr(pkt *packet.Packet, l4 *ICMPConfig, rnd *rand.Rand) { @@ -468,27 +257,34 @@ func fillICMPHdr(pkt *packet.Packet, l4 *ICMPConfig, rnd *rand.Rand) { emptyPacketICMP.Type = l4.Type emptyPacketICMP.Code = l4.Code emptyPacketICMP.Identifier = l4.Identifier - emptyPacketICMP.SeqNum = packet.SwapBytesUint16(uint16(getNextSeqNumber(&(l4.Seq), rnd))) + emptyPacketICMP.SeqNum = packet.SwapBytesUint16(uint16(l4.Seq.Current)) + getNextSeqNumber(&l4.Seq, rnd) } -func fillIPHdr(pkt *packet.Packet, l3 *IPConfig) { - if l3.Version == 4 { - pktIP := (*packet.IPv4Hdr)(pkt.L3) - pktIP.SrcAddr = binary.LittleEndian.Uint32(To4(getNextAddr(&(l3.SAddr)))) - pktIP.DstAddr = binary.LittleEndian.Uint32(To4(getNextAddr(&(l3.DAddr)))) - return - } - pktIP := (*packet.IPv6Hdr)(pkt.L3) - copyAddr(pktIP.SrcAddr[:], getNextAddr(&(l3.SAddr)), common.IPv6AddrLen) - copyAddr(pktIP.DstAddr[:], getNextAddr(&(l3.DAddr)), common.IPv6AddrLen) +func fillIPv4Hdr(pkt *packet.Packet, l3 *IPv4Config) { + pktIP := (*packet.IPv4Hdr)(pkt.L3) + pktIP.SrcAddr = packet.SwapBytesIPv4Addr(types.IPv4Address(l3.SAddr.Current)) + pktIP.DstAddr = packet.SwapBytesIPv4Addr(types.IPv4Address(l3.DAddr.Current)) + getNextValue(&l3.DAddr) + getNextValue(&l3.SAddr) +} + +// SwapBytesUint32 swaps uint32 in Little Endian and Big Endian +func SwapBytesUint64(x uint64) uint64 { + return ((x & 0x00000000000000ff) << 40) | ((x & 0x000000000000ff00) << 24) | ((x & 0x0000000000ff0000) << 8) | + ((x & 0x00000000ff000000) >> 8) | ((x & 0x0000ff0000000000) >> 40) | ((x & 0x000000ff00000000) >> 24) } func fillEtherHdr(pkt *packet.Packet, l2 *EtherConfig) { if l2.VLAN != nil { addVLAN(pkt, l2.VLAN.TCI) } - copyAddr(pkt.Ether.DAddr[:], getNextAddr(&(l2.DAddr)), common.EtherAddrLen) - copyAddr(pkt.Ether.SAddr[:], getNextAddr(&(l2.SAddr)), common.EtherAddrLen) + t := pkt.Ether.EtherType + *(*uint64)(unsafe.Pointer(&pkt.Ether.DAddr[0])) = SwapBytesUint64(l2.DAddr.Current) + *(*uint64)(unsafe.Pointer(&pkt.Ether.SAddr[0])) = SwapBytesUint64(l2.SAddr.Current) + pkt.Ether.EtherType = t + getNextValue(&l2.DAddr) + getNextValue(&l2.SAddr) } // faster version of packet.AddVLANTag, can be used only @@ -498,7 +294,7 @@ func addVLAN(pkt *packet.Packet, tag uint16) { if !pkt.EncapsulateHead(0, 4) { panic("failed to add vlan tag, EncapsulateHead returned false") } - pkt.Ether.EtherType = packet.SwapBytesUint16(common.VLANNumber) + pkt.Ether.EtherType = packet.SwapBytesUint16(types.VLANNumber) vhdr := pkt.GetVLAN() if vhdr == nil { panic("failed to get vlan, GetVLAN returned nil") diff --git a/examples/nffPktgen/generator/ipv6.go b/examples/nffPktgen/generator/ipv6.go new file mode 100644 index 00000000..1448d1ba --- /dev/null +++ b/examples/nffPktgen/generator/ipv6.go @@ -0,0 +1,308 @@ +// Copyright 2018 Intel Corporation. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package generator + +import ( + "bytes" + "fmt" + "math/big" + "math/rand" + "net" + "strings" + + "github.com/intel-go/nff-go/packet" + "github.com/intel-go/nff-go/types" +) + +func addAddr(a *[]byte, b []byte) { + var offset int + aLen := len(*a) + bLen := len(b) + if aLen < bLen { + add := make([]byte, bLen-aLen) + *a = append(add, *a...) + } else { + offset = aLen - bLen + } + var next byte + for i := bLen - 1; i >= 0; i-- { + add := (*a)[i+offset] + b[i] + next + if add > 255 { + next = 1 + (*a)[i+offset] = byte(255) - add + } else { + (*a)[i+offset] = add + next = 0 + } + } +} + +func getNextAddr(addr *AddrIPv6Range) []uint8 { + addAddr(&(addr.Current), addr.Inc) + // if current < min or current > max, copy min to current + if bytes.Compare(addr.Current, addr.Min) < 0 || bytes.Compare(addr.Current, addr.Max) > 0 { + copy(addr.Current, addr.Min) + } + return addr.Current +} + +func copyAddr(destination []uint8, source []uint8, size int) { + if size < len(source) { + copy(destination[:], source[len(source)-size:]) + } else { + copy(destination[size-len(source):], source[:]) + } +} + +func generateIPv6(pkt *packet.Packet, config *PacketConfig, rnd *rand.Rand) { + if pkt == nil { + panic("Failed to create new packet") + } + l2 := &config.Ether + l3 := &l2.IPv6 + size, dataConfig, copyDataFunc := getDataSizeType(&l3.Bytes, rnd) + if !packet.InitEmptyIPv6Packet(pkt, size) { + panic(fmt.Sprintf("InitEmptyIPv6Packet returned false")) + } + copyDataFunc(dataConfig, size, rnd, pkt.Data) + fillEtherHdr(pkt, l2) + fillIPv6Hdr(pkt, l3) +} + +func generateTCPIPv6(pkt *packet.Packet, config *PacketConfig, rnd *rand.Rand) { + if pkt == nil { + panic("Failed to create new packet") + } + l2 := &config.Ether + l3 := &l2.IPv6 + l4 := &l3.TCP + size, dataConfig, copyDataFunc := getDataSizeType(&l4.Bytes, rnd) + if !packet.InitEmptyIPv6TCPPacket(pkt, size) { + panic(fmt.Sprintf("InitEmptyIPv6TCPPacket returned false")) + } + copyDataFunc(dataConfig, size, rnd, pkt.Data) + fillEtherHdr(pkt, l2) + fillIPv6Hdr(pkt, l3) + fillTCPHdr(pkt, l4, rnd) + pktTCP := (*packet.TCPHdr)(pkt.L4) + pktIP := (*packet.IPv6Hdr)(pkt.L3) + pktTCP.Cksum = packet.SwapBytesUint16(packet.CalculateIPv6TCPChecksum(pktIP, pktTCP, pkt.Data)) +} + +func generateUDPIPv6(pkt *packet.Packet, config *PacketConfig, rnd *rand.Rand) { + if pkt == nil { + panic("Failed to create new packet") + } + l2 := &config.Ether + l3 := &l2.IPv6 + l4 := &l3.UDP + size, dataConfig, copyDataFunc := getDataSizeType(&l4.Bytes, rnd) + if !packet.InitEmptyIPv6UDPPacket(pkt, size) { + panic(fmt.Sprintf("InitEmptyIPv6UDPPacket returned false")) + } + copyDataFunc(dataConfig, size, rnd, pkt.Data) + fillEtherHdr(pkt, l2) + fillIPv6Hdr(pkt, l3) + fillUDPHdr(pkt, l4) + pktUDP := (*packet.UDPHdr)(pkt.L4) + pktIP := (*packet.IPv6Hdr)(pkt.L3) + pktUDP.DgramCksum = packet.SwapBytesUint16(packet.CalculateIPv6UDPChecksum(pktIP, pktUDP, pkt.Data)) +} + +func generateICMPIPv6(pkt *packet.Packet, config *PacketConfig, rnd *rand.Rand) { + if pkt == nil { + panic("Failed to create new packet") + } + l2 := &config.Ether + l3 := &l2.IPv6 + l4 := &l3.ICMP + size, dataConfig, copyDataFunc := getDataSizeType(&l4.Bytes, rnd) + if !packet.InitEmptyIPv6ICMPPacket(pkt, size) { + panic(fmt.Sprintf("InitEmptyIPv6ICMPPacket returned false")) + } + copyDataFunc(dataConfig, size, rnd, pkt.Data) + fillEtherHdr(pkt, l2) + fillIPv6Hdr(pkt, l3) + fillICMPHdr(pkt, l4, rnd) + pktICMP := (*packet.ICMPHdr)(pkt.L4) + pktIP := (*packet.IPv6Hdr)(pkt.L3) + pktICMP.Cksum = packet.SwapBytesUint16(packet.CalculateIPv6ICMPChecksum(pktIP, pktICMP, pkt.Data)) +} + +func fillIPv6Hdr(pkt *packet.Packet, l3 *IPv6Config) { + pktIP := (*packet.IPv6Hdr)(pkt.L3) + copyAddr(pktIP.SrcAddr[:], getNextAddr(&(l3.SAddr)), types.IPv6AddrLen) + copyAddr(pktIP.DstAddr[:], getNextAddr(&(l3.DAddr)), types.IPv6AddrLen) +} + +// AddrRange describes range of addresses. +type AddrIPv6Range struct { + Min []byte + Max []byte + Current []byte + Inc []byte +} + +// IPv6Config configures ip header. +type IPv6Config struct { + SAddr AddrIPv6Range // source address + DAddr AddrIPv6Range // destination address + DType DataType + TCP TCPConfig + UDP UDPConfig + ICMP ICMPConfig + Bytes RawBytes +} + +func parseIPv6Hdr(in map[string]interface{}) (IPv6Config, error) { + ipHdr := IPv6Config{DAddr: AddrIPv6Range{}, SAddr: AddrIPv6Range{}} + for k, v := range in { + switch strings.ToLower(k) { + case "saddr": + saddr, err := parseIPv6Addr(v) + if err != nil { + return IPv6Config{}, fmt.Errorf("parseIPv6Addr for ip saddr returned: %v", err) + } + ipHdr.SAddr = saddr + case "daddr": + daddr, err := parseIPv6Addr(v) + if err != nil { + return IPv6Config{}, fmt.Errorf("parseIPv6Addr for ip daddr returned: %v", err) + } + ipHdr.DAddr = daddr + case "tcp": + tcpConf, err := parseTCPHdr(v.(map[string]interface{})) + if err != nil { + return IPv6Config{}, fmt.Errorf("parseTCPHdr returned %v", err) + } + ipHdr.TCP = tcpConf + ipHdr.DType = TCPHDR + break + case "udp": + udpConf, err := parseUDPHdr(v.(map[string]interface{})) + if err != nil { + return IPv6Config{}, fmt.Errorf("parseUDPHdr returned %v", err) + } + ipHdr.UDP = udpConf + ipHdr.DType = UDPHDR + break + case "icmp": + icmpConf, err := parseICMPHdr(v.(map[string]interface{})) + if err != nil { + return IPv6Config{}, fmt.Errorf("parseICMPHdr returned %v", err) + } + ipHdr.ICMP = icmpConf + ipHdr.DType = ICMPHDR + break + default: + data, err := parseData(map[string]interface{}{k: v}) + if err != nil { + return IPv6Config{}, fmt.Errorf("parseData for ip returned: %v", err) + } + ipHdr.Bytes = data + ipHdr.DType = DATA + break + } + } + return ipHdr, nil +} + +func parseRawIPv6Addr(rawAddr string) ([]uint8, error) { + addr := net.ParseIP(rawAddr) + if addr == nil { + return nil, fmt.Errorf("parsing ip addr could not parse address for %s", rawAddr) + } + return addr, nil +} + +func parseIPv6Addr(value interface{}) (AddrIPv6Range, error) { + switch v2 := value.(type) { + case map[string]interface{}: + for k, v := range v2 { + switch strings.ToLower(k) { + case "range": + addrRange, err := parseAddrIPv6Range(v.(map[string]interface{}), parseRawIPv6Addr) + if err != nil { + return AddrIPv6Range{}, fmt.Errorf("parseAddrIPv6Range returned: %v", err) + } + return addrRange, nil + default: + return AddrIPv6Range{}, fmt.Errorf("unknown key in ip addr: %s", k) + } + } + case string: + saddr, err := parseRawIPv6Addr(v2) + if err != nil { + return AddrIPv6Range{}, fmt.Errorf("parsing ip addr returned: %v", err) + } + ret := AddrIPv6Range{} + ret.Min = make([]byte, len(saddr)) + copy(ret.Min, saddr) + ret.Max = make([]byte, len(saddr)) + copy(ret.Max, saddr) + ret.Current = make([]byte, len(saddr)) + copy(ret.Current, saddr) + return ret, nil + } + return AddrIPv6Range{}, fmt.Errorf("unknown type") +} + +type fn6 func(string) ([]uint8, error) + +func parseAddrIPv6Range(in map[string]interface{}, parseFunc fn6) (AddrIPv6Range, error) { + addr := AddrIPv6Range{} + wasMin, wasMax, wasStart, wasIncr := false, false, false, false + for k, v := range in { + switch strings.ToLower(k) { + case "min": + min, err := parseFunc(v.(string)) + if err != nil { + return AddrIPv6Range{}, fmt.Errorf("parsing min returned: %v", err) + } + addr.Min = make([]byte, len(min)) + copy(addr.Min, min) + wasMin = true + case "max": + max, err := parseFunc(v.(string)) + if err != nil { + return AddrIPv6Range{}, fmt.Errorf("parsing max returned: %v", err) + } + addr.Max = make([]byte, len(max)) + copy(addr.Max, max) + wasMax = true + case "start": + start, err := parseFunc(v.(string)) + if err != nil { + return AddrIPv6Range{}, fmt.Errorf("parsing start returned: %v", err) + } + addr.Current = make([]byte, len(start)) + copy(addr.Current, start) + wasStart = true + case "incr": + addr.Inc = big.NewInt((int64)(v.(float64))).Bytes() + wasIncr = true + default: + return AddrIPv6Range{}, fmt.Errorf("unknown key %s", k) + } + } + if !wasMax || !wasMin { + return AddrIPv6Range{}, fmt.Errorf("Min and max values should be given for range") + } + if bytes.Compare(addr.Max, addr.Min) < 0 { + return AddrIPv6Range{}, fmt.Errorf("Min value should be less than Max") + } + if !wasStart { + addr.Current = make([]byte, len(addr.Min)) + copy(addr.Current, addr.Min) + } + + if bytes.Compare(addr.Current, addr.Min) < 0 || bytes.Compare(addr.Current, addr.Max) > 0 { + return AddrIPv6Range{}, fmt.Errorf(fmt.Sprintf("Start value should be between min and max: start=%v, min=%v, max=%v", addr.Current, addr.Min, addr.Max)) + } + if !wasIncr { + addr.Inc = []byte{1} + } + return addr, nil +} diff --git a/examples/nffPktgen/generator/parseConfig.go b/examples/nffPktgen/generator/parseConfig.go index 84e7090f..f80cc154 100644 --- a/examples/nffPktgen/generator/parseConfig.go +++ b/examples/nffPktgen/generator/parseConfig.go @@ -6,48 +6,24 @@ package generator import ( "bufio" - "bytes" "encoding/json" "fmt" - "math/big" "math/rand" "net" "os" "regexp" "strings" - "unsafe" - "github.com/intel-go/nff-go/common" + "github.com/intel-go/nff-go/types" ) var mixPattern = regexp.MustCompile(`^mix[0-9]*$`) -// AddrRange describes range of addresses. type AddrRange struct { - Min []byte - Max []byte - Current []byte - Incr []byte -} - -func (ar *AddrRange) String() string { - s := "addrRange:\n" - s += fmt.Sprintf("min: %v, max: %v, start: %v, incr: %d", ar.Min, ar.Max, ar.Current, ar.Incr) - return s -} - -// PortRange describes range of ports. -type PortRange struct { - Min uint16 - Max uint16 - Current uint16 - Incr uint16 -} - -func (pr *PortRange) String() string { - s := "portRange:\n" - s += fmt.Sprintf("min: %v, max: %v, start: %v, incr: %d", pr.Min, pr.Max, pr.Current, pr.Incr) - return s + Min uint64 + Max uint64 + Current uint64 + Inc uint64 } // SequenceType used in enum below. @@ -61,19 +37,22 @@ const ( // Sequence contains type and next sequence value. type Sequence struct { - Type SequenceType - Next uint32 + Type SequenceType + Current uint32 } type DataType int const ( - ETHERHDR = iota - IPHDR + NULL = iota + ETHERHDR + IPv4HDR + IPv6HDR TCPHDR UDPHDR ICMPHDR ARPHDR + DATA PDISTDATA RANDDATA RAWDATA @@ -83,17 +62,17 @@ const ( // PacketConfig configures packet type PacketConfig struct { DType DataType - Data unsafe.Pointer + Ether EtherConfig } // MixConfig contains PacketConfigs with quantity. type MixConfig struct { - Config *PacketConfig + Config PacketConfig Quantity uint32 } func (mc *MixConfig) String() string { - return fmt.Sprintf("config: %v; quantity: %v\n", *(mc.Config), mc.Quantity) + return fmt.Sprintf("config: %v; quantity: %v\n", mc.Config, mc.Quantity) } // EtherConfig configures ether header. @@ -102,7 +81,10 @@ type EtherConfig struct { SAddr AddrRange VLAN *VlanTagConfig DType DataType - Data unsafe.Pointer + IPv4 IPv4Config + IPv6 IPv6Config + ARP ARPConfig + Bytes RawBytes } // VlanTagConfig configures vlan tag @@ -110,31 +92,33 @@ type VlanTagConfig struct { TCI uint16 } -// IPConfig configures ip header. -type IPConfig struct { - Version int - SAddr AddrRange // source address - DAddr AddrRange // destination address - DType DataType - Data unsafe.Pointer +// IPv4Config configures ip header. +type IPv4Config struct { + SAddr AddrRange // source address + DAddr AddrRange // destination address + DType DataType + TCP TCPConfig + UDP UDPConfig + ICMP ICMPConfig + Bytes RawBytes } // TCPConfig configures tcp header. type TCPConfig struct { - SPort PortRange - DPort PortRange + SPort AddrRange + DPort AddrRange Seq Sequence - Flags common.TCPFlags + Flags types.TCPFlags DType DataType - Data unsafe.Pointer + Bytes RawBytes } // UDPConfig configures tcp header. type UDPConfig struct { - SPort PortRange - DPort PortRange + SPort AddrRange + DPort AddrRange DType DataType - Data unsafe.Pointer + Bytes RawBytes } // ICMPConfig configures tcp header. @@ -144,7 +128,7 @@ type ICMPConfig struct { Identifier uint16 Seq Sequence DType DataType - Data unsafe.Pointer + Bytes RawBytes } // ARPConfig configures arp header. @@ -161,7 +145,8 @@ type ARPConfig struct { type PDistEntry struct { Probability float64 DType DataType - Data unsafe.Pointer + Data string + Rand RandBytes } // RandBytes gives a payload of random bytes, of a given size. @@ -172,12 +157,15 @@ type RandBytes struct { } // Raw represents raw data -type Raw struct { - Data string +type RawBytes struct { + Data string + Rand RandBytes + Dist []PDistEntry + DType DataType } // ParseConfig parses json config and returns []*MixConfig. -func ParseConfig(f *os.File) (config []*MixConfig, err error) { +func ParseConfig(f *os.File) (config []MixConfig, err error) { r := bufio.NewReader(f) var in map[string]interface{} err = json.NewDecoder(r).Decode(&in) @@ -193,8 +181,8 @@ func ParseConfig(f *os.File) (config []*MixConfig, err error) { if err != nil { return nil, fmt.Errorf("parseEtherHdr returned: %v", err) } - pktConfig := &PacketConfig{Data: unsafe.Pointer(ðHdr), DType: ETHERHDR} - return append(config, &MixConfig{Config: pktConfig, Quantity: 1}), nil + pktConfig := PacketConfig{Ether: ethHdr, DType: ETHERHDR} + return append(config, MixConfig{Config: pktConfig, Quantity: 1}), nil case mixPattern.MatchString(key): return ParseMixConfig(in) default: @@ -206,13 +194,13 @@ func ParseConfig(f *os.File) (config []*MixConfig, err error) { } // ParseMixConfig parses json config and returns []*MixConfig. -func ParseMixConfig(in map[string]interface{}) (config []*MixConfig, err error) { +func ParseMixConfig(in map[string]interface{}) (config []MixConfig, err error) { for k, v := range in { key := strings.ToLower(k) switch { case mixPattern.MatchString(key): var ( - pktConfig *PacketConfig + pktConfig PacketConfig q uint32 ) mixUnit := v.(map[string]interface{}) @@ -224,17 +212,17 @@ func ParseMixConfig(in map[string]interface{}) (config []*MixConfig, err error) if err != nil { return nil, fmt.Errorf("parseEtherHdr returned: %v", err) } - pktConfig = &PacketConfig{Data: unsafe.Pointer(ðHdr), DType: ETHERHDR} + pktConfig = PacketConfig{Ether: ethHdr, DType: ETHERHDR} case "quantity", "q": q = uint32(vv.(float64)) default: return nil, fmt.Errorf("unexpected key: %s, expected ether and quantity or q", k) } } - if q == 0 || pktConfig == nil { + if q == 0 || pktConfig.DType == NULL { return nil, fmt.Errorf("some fields were not set") } - config = append(config, &MixConfig{Config: pktConfig, Quantity: q}) + config = append(config, MixConfig{Config: pktConfig, Quantity: q}) default: return nil, fmt.Errorf("unexpected key: %s, expected mix[0-9]*", k) } @@ -261,13 +249,21 @@ func parseEtherHdr(in map[string]interface{}) (EtherConfig, error) { case "vlan-tci": ethConfig.VLAN = &(VlanTagConfig{TCI: uint16(v.(float64))}) break - case "ip": - ipConf, err := parseIPHdr(v.(map[string]interface{})) + case "ipv4": + ipConf, err := parseIPv4Hdr(v.(map[string]interface{})) if err != nil { return EtherConfig{}, fmt.Errorf("parseIpHdr returned %v", err) } - ethConfig.Data = unsafe.Pointer(&ipConf) - ethConfig.DType = IPHDR + ethConfig.IPv4 = ipConf + ethConfig.DType = IPv4HDR + break + case "ipv6": + ipConf, err := parseIPv6Hdr(v.(map[string]interface{})) + if err != nil { + return EtherConfig{}, fmt.Errorf("parseIpHdr returned %v", err) + } + ethConfig.IPv6 = ipConf + ethConfig.DType = IPv6HDR break case "arp": arpConfig := v.(map[string]interface{}) @@ -275,104 +271,101 @@ func parseEtherHdr(in map[string]interface{}) (EtherConfig, error) { if err != nil { return EtherConfig{}, fmt.Errorf("parseARPHdr returned %v", err) } - ethConfig.Data = unsafe.Pointer(&arpConf) + ethConfig.ARP = arpConf ethConfig.DType = ARPHDR break default: - data, dataType, err := parseData(map[string]interface{}{k: v}) + data, err := parseData(map[string]interface{}{k: v}) if err != nil { return EtherConfig{}, fmt.Errorf("parseData for ether returned: %v", err) } - ethConfig.Data = data - ethConfig.DType = dataType + ethConfig.Bytes = data + ethConfig.DType = DATA break } } return ethConfig, nil } -func parseData(in map[string]interface{}) (unsafe.Pointer, DataType, error) { +func parseData(in map[string]interface{}) (ret RawBytes, err error) { for k, v := range in { switch strings.ToLower(k) { case "raw": - data, err := parseRaw(v.(map[string]interface{})) + ret.Data, err = parseRaw(v.(map[string]interface{})) + ret.DType = RAWDATA if err != nil { - return nil, NONE, fmt.Errorf("parseRaw returned %v", err) + return ret, fmt.Errorf("parseRaw returned %v", err) } - return unsafe.Pointer(&data), RAWDATA, nil + return ret, nil case "randbytes": - randBytes, err := parseRandBytes(v.(map[string]interface{})) + ret.Rand, err = parseRandBytes(v.(map[string]interface{})) + ret.DType = RANDDATA if err != nil { - return nil, NONE, fmt.Errorf("parseRandBytes returned %v", err) + return ret, fmt.Errorf("parseRandBytes returned %v", err) } - return unsafe.Pointer(&randBytes), RANDDATA, nil + return ret, nil case "pdist": - pdist, err := parsePdist(v.([]interface{})) + ret.Dist, err = parsePdist(v.([]interface{})) + ret.DType = PDISTDATA if err != nil { - return nil, NONE, fmt.Errorf("parsePdist returned %v", err) + return ret, fmt.Errorf("parsePdist returned %v", err) } - return unsafe.Pointer(&pdist), PDISTDATA, nil + return ret, nil default: fmt.Println("unknown key: ", k) break } } - return nil, NONE, fmt.Errorf("failed to parse data") + return ret, fmt.Errorf("failed to parse data") } -func parseIPHdr(in map[string]interface{}) (IPConfig, error) { - ipHdr := IPConfig{Version: 4, DAddr: AddrRange{}, SAddr: AddrRange{}} +func parseIPv4Hdr(in map[string]interface{}) (IPv4Config, error) { + ipHdr := IPv4Config{DAddr: AddrRange{}, SAddr: AddrRange{}} for k, v := range in { switch strings.ToLower(k) { - case "version": - version := (int)(v.(float64)) - if version != 4 && version != 6 { - return IPConfig{}, fmt.Errorf("ip version should be 4 or 6, got: %d", version) - } - ipHdr.Version = version case "saddr": - saddr, err := parseIPAddr(v) + saddr, err := parseIPv4Addr(v) if err != nil { - return IPConfig{}, fmt.Errorf("parseIPAddr for ip saddr returned: %v", err) + return IPv4Config{}, fmt.Errorf("parseIPv4Addr for ip saddr returned: %v", err) } ipHdr.SAddr = saddr case "daddr": - daddr, err := parseIPAddr(v) + daddr, err := parseIPv4Addr(v) if err != nil { - return IPConfig{}, fmt.Errorf("parseIPAddr for ip daddr returned: %v", err) + return IPv4Config{}, fmt.Errorf("parseIPv4Addr for ip daddr returned: %v", err) } ipHdr.DAddr = daddr case "tcp": tcpConf, err := parseTCPHdr(v.(map[string]interface{})) if err != nil { - return IPConfig{}, fmt.Errorf("parseTCPHdr returned %v", err) + return IPv4Config{}, fmt.Errorf("parseTCPHdr returned %v", err) } - ipHdr.Data = unsafe.Pointer(&tcpConf) + ipHdr.TCP = tcpConf ipHdr.DType = TCPHDR break case "udp": udpConf, err := parseUDPHdr(v.(map[string]interface{})) if err != nil { - return IPConfig{}, fmt.Errorf("parseUDPHdr returned %v", err) + return IPv4Config{}, fmt.Errorf("parseUDPHdr returned %v", err) } - ipHdr.Data = unsafe.Pointer(&udpConf) + ipHdr.UDP = udpConf ipHdr.DType = UDPHDR break case "icmp": icmpConf, err := parseICMPHdr(v.(map[string]interface{})) if err != nil { - return IPConfig{}, fmt.Errorf("parseICMPHdr returned %v", err) + return IPv4Config{}, fmt.Errorf("parseICMPHdr returned %v", err) } - ipHdr.Data = unsafe.Pointer(&icmpConf) + ipHdr.ICMP = icmpConf ipHdr.DType = ICMPHDR break default: - data, dataType, err := parseData(map[string]interface{}{k: v}) + data, err := parseData(map[string]interface{}{k: v}) if err != nil { - return IPConfig{}, fmt.Errorf("parseData for ip returned: %v", err) + return IPv4Config{}, fmt.Errorf("parseData for ip returned: %v", err) } - ipHdr.Data = data - ipHdr.DType = dataType + ipHdr.Bytes = data + ipHdr.DType = DATA break } } @@ -398,7 +391,7 @@ func parseARPHdr(in map[string]interface{}) (ARPConfig, error) { } arpHdr.SHA = sha case "spa": - spa, err := parseIPAddr(v) + spa, err := parseIPv4Addr(v) if err != nil { return ARPConfig{}, fmt.Errorf("parseIPAddr for spa returned: %v", err) } @@ -410,7 +403,7 @@ func parseARPHdr(in map[string]interface{}) (ARPConfig, error) { } arpHdr.THA = tha case "tpa": - tpa, err := parseIPAddr(v) + tpa, err := parseIPv4Addr(v) if err != nil { return ARPConfig{}, fmt.Errorf("parseIPAddr for tpa returned: %v", err) } @@ -423,7 +416,7 @@ func parseARPHdr(in map[string]interface{}) (ARPConfig, error) { } func parseTCPHdr(in map[string]interface{}) (TCPConfig, error) { - tcpHdr := TCPConfig{SPort: PortRange{}, DPort: PortRange{}} + tcpHdr := TCPConfig{SPort: AddrRange{}, DPort: AddrRange{}} for k, v := range in { switch strings.ToLower(k) { case "sport": @@ -451,12 +444,11 @@ func parseTCPHdr(in map[string]interface{}) (TCPConfig, error) { } tcpHdr.Flags = flags default: - data, dataType, err := parseData(map[string]interface{}{k: v}) + data, err := parseData(map[string]interface{}{k: v}) if err != nil { return TCPConfig{}, fmt.Errorf("parseData for tcp returned: %v", err) } - tcpHdr.Data = data - tcpHdr.DType = dataType + tcpHdr.Bytes = data break } } @@ -464,7 +456,7 @@ func parseTCPHdr(in map[string]interface{}) (TCPConfig, error) { } func parseUDPHdr(in map[string]interface{}) (UDPConfig, error) { - udpHdr := UDPConfig{SPort: PortRange{}, DPort: PortRange{}} + udpHdr := UDPConfig{SPort: AddrRange{}, DPort: AddrRange{}} for k, v := range in { switch strings.ToLower(k) { case "sport": @@ -480,12 +472,11 @@ func parseUDPHdr(in map[string]interface{}) (UDPConfig, error) { } udpHdr.DPort = dport default: - data, dataType, err := parseData(map[string]interface{}{k: v}) + data, err := parseData(map[string]interface{}{k: v}) if err != nil { return UDPConfig{}, fmt.Errorf("parseData for udp returned: %v", err) } - udpHdr.Data = data - udpHdr.DType = dataType + udpHdr.Bytes = data break } } @@ -493,7 +484,7 @@ func parseUDPHdr(in map[string]interface{}) (UDPConfig, error) { } func parseICMPHdr(in map[string]interface{}) (ICMPConfig, error) { - icmpHdr := ICMPConfig{Data: unsafe.Pointer(&Raw{Data: ""})} + icmpHdr := ICMPConfig{Bytes: RawBytes{Data: ""}} for k, v := range in { switch strings.ToLower(k) { case "type": @@ -509,38 +500,37 @@ func parseICMPHdr(in map[string]interface{}) (ICMPConfig, error) { } icmpHdr.Seq = seq default: - data, dataType, err := parseData(map[string]interface{}{k: v}) + data, err := parseData(map[string]interface{}{k: v}) if err != nil { return ICMPConfig{}, fmt.Errorf("parseData for icmp returned: %v", err) } - icmpHdr.Data = data - icmpHdr.DType = dataType + icmpHdr.Bytes = data break } } return icmpHdr, nil } -func parseTCPFlags(in []interface{}) (ret common.TCPFlags, err error) { +func parseTCPFlags(in []interface{}) (ret types.TCPFlags, err error) { ret = 0 for _, flag := range in { switch strings.ToLower(flag.(string)) { case "fin": - ret ^= common.TCPFlagFin + ret ^= types.TCPFlagFin case "syn": - ret ^= common.TCPFlagSyn + ret ^= types.TCPFlagSyn case "rst": - ret ^= common.TCPFlagRst + ret ^= types.TCPFlagRst case "psh": - ret ^= common.TCPFlagPsh + ret ^= types.TCPFlagPsh case "ack": - ret ^= common.TCPFlagAck + ret ^= types.TCPFlagAck case "urg": - ret ^= common.TCPFlagUrg + ret ^= types.TCPFlagUrg case "ece": - ret ^= common.TCPFlagEce + ret ^= types.TCPFlagEce case "cwr": - ret ^= common.TCPFlagCwr + ret ^= types.TCPFlagCwr default: return 0, fmt.Errorf("unknown flag value: %s", flag.(string)) } @@ -551,24 +541,24 @@ func parseTCPFlags(in []interface{}) (ret common.TCPFlags, err error) { func parseSeq(in string) (Sequence, error) { switch strings.ToLower(in) { case "rand", "random": - return Sequence{Type: RANDOM, Next: rand.Uint32()}, nil + return Sequence{Type: RANDOM, Current: rand.Uint32()}, nil case "incr", "increasing": - return Sequence{Type: INCREASING, Next: 0}, nil + return Sequence{Type: INCREASING, Current: 0}, nil } return Sequence{}, fmt.Errorf("unknown key for tcp sequence: %s", in) } -func parseRaw(in map[string]interface{}) (Raw, error) { +func parseRaw(in map[string]interface{}) (string, error) { for k, v := range in { switch strings.ToLower(k) { case "data": - return Raw{Data: v.(string)}, nil + return v.(string), nil default: - return Raw{}, fmt.Errorf("unknown key in 'raw' block: %s", k) + return "", fmt.Errorf("unknown key in 'raw' block: %s", k) } } - return Raw{}, fmt.Errorf("no key 'data' found inside 'raw' block") + return "", fmt.Errorf("no key 'data' found inside 'raw' block") } func parseRandBytes(in map[string]interface{}) (RandBytes, error) { @@ -595,18 +585,22 @@ func parseRandBytes(in map[string]interface{}) (RandBytes, error) { func parsePdist(in []interface{}) ([]PDistEntry, error) { pdist := []PDistEntry{} + var totalProb float64 for _, v := range in { pdistElem, err := parsePdistEntry(v.(map[string]interface{})) if err != nil { return []PDistEntry{}, fmt.Errorf("parsing pdistentry returned: %v", err) } + totalProb += pdistElem.Probability pdist = append(pdist, pdistElem) } + if totalProb != 1 { + return []PDistEntry{{}}, fmt.Errorf("Sum of probabilities of pdist elements is not equal to 1") + } return pdist, nil } -func parsePdistEntry(in map[string]interface{}) (PDistEntry, error) { - pdistElem := PDistEntry{} +func parsePdistEntry(in map[string]interface{}) (pdistElem PDistEntry, err error) { for k, v := range in { switch strings.ToLower(k) { case "probability": @@ -615,18 +609,16 @@ func parsePdistEntry(in map[string]interface{}) (PDistEntry, error) { return PDistEntry{}, fmt.Errorf("invalid probability value: %f", pdistElem.Probability) } case "raw": - data, err := parseRaw(v.(map[string]interface{})) + pdistElem.Data, err = parseRaw(v.(map[string]interface{})) if err != nil { return PDistEntry{}, fmt.Errorf("parsing pdist entry: parseRaw returned %v", err) } - pdistElem.Data = unsafe.Pointer(&data) pdistElem.DType = RAWDATA case "randbytes": - randBytes, err := parseRandBytes(v.(map[string]interface{})) + pdistElem.Rand, err = parseRandBytes(v.(map[string]interface{})) if err != nil { return PDistEntry{}, fmt.Errorf("parsing pdist entry: parseRandBytes returned %v", err) } - pdistElem.Data = unsafe.Pointer(&randBytes) pdistElem.DType = RANDDATA default: return PDistEntry{}, fmt.Errorf("unknown key %s", k) @@ -635,7 +627,7 @@ func parsePdistEntry(in map[string]interface{}) (PDistEntry, error) { return pdistElem, nil } -func parseMacAddr(value interface{}) (AddrRange, error) { +func parseMacAddr(value interface{}) (ret AddrRange, err error) { switch v2 := value.(type) { case map[string]interface{}: for k, v := range v2 { @@ -646,51 +638,43 @@ func parseMacAddr(value interface{}) (AddrRange, error) { return AddrRange{}, fmt.Errorf("parseAddrRange returned: %v", err) } return saddrRange, nil - default: return AddrRange{}, fmt.Errorf("unknown key in parse mac addr: %s", k) } } case string: - saddr, err := net.ParseMAC(v2) + ret.Current, err = parseRawMACAddr(v2) if err != nil { return AddrRange{}, fmt.Errorf("parsing mac saddr returned: %v", err) } - ret := AddrRange{} - ret.Min = make([]byte, len(saddr)) - copy(ret.Min, saddr) - ret.Max = make([]byte, len(saddr)) - copy(ret.Max, saddr) - ret.Current = make([]byte, len(saddr)) - copy(ret.Current, saddr) return ret, nil } return AddrRange{}, fmt.Errorf("unknown type") } -func parseRawIPAddr(rawAddr string) ([]uint8, error) { +func parseRawIPv4Addr(rawAddr string) (uint64, error) { addr := net.ParseIP(rawAddr) if addr == nil { - return nil, fmt.Errorf("parsing ip addr could not parse address for %s", rawAddr) + return 0, fmt.Errorf("parsing ip addr could not parse address for %s", rawAddr) } - return addr, nil + return uint64(addr[12])<<24 | uint64(addr[13])<<16 | uint64(addr[14])<<8 | uint64(addr[15]), nil } -func parseRawMACAddr(rawAddr string) ([]uint8, error) { +func parseRawMACAddr(rawAddr string) (uint64, error) { addr, err := net.ParseMAC(rawAddr) if err != nil { - return nil, fmt.Errorf("parsing mac addr returned: %v for %s", err, rawAddr) + return 0, fmt.Errorf("parsing mac addr returned: %v for %s", err, rawAddr) } - return addr, nil + return uint64(addr[0])<<40 | uint64(addr[1])<<32 | uint64(addr[2])<<24 | uint64(addr[3])<<16 | uint64(addr[4])<<8 | uint64(addr[5]), nil } -func parseIPAddr(value interface{}) (AddrRange, error) { +func parseIPv4Addr(value interface{}) (ret AddrRange, err error) { switch v2 := value.(type) { case map[string]interface{}: for k, v := range v2 { switch strings.ToLower(k) { case "range": - addrRange, err := parseAddrRange(v.(map[string]interface{}), parseRawIPAddr) + addrRange, err := parseAddrRange(v.(map[string]interface{}), parseRawIPv4Addr) if err != nil { return AddrRange{}, fmt.Errorf("parseAddrRange returned: %v", err) } @@ -700,56 +684,42 @@ func parseIPAddr(value interface{}) (AddrRange, error) { } } case string: - saddr, err := parseRawIPAddr(v2) + ret.Current, err = parseRawIPv4Addr(v2) if err != nil { return AddrRange{}, fmt.Errorf("parsing ip addr returned: %v", err) } - ret := AddrRange{} - ret.Min = make([]byte, len(saddr)) - copy(ret.Min, saddr) - ret.Max = make([]byte, len(saddr)) - copy(ret.Max, saddr) - ret.Current = make([]byte, len(saddr)) - copy(ret.Current, saddr) return ret, nil } return AddrRange{}, fmt.Errorf("unknown type") } -type fn func(string) ([]uint8, error) +type fn func(string) (uint64, error) -func parseAddrRange(in map[string]interface{}, parseFunc fn) (AddrRange, error) { - addr := AddrRange{} - wasMin, wasMax, wasStart, wasIncr := false, false, false, false +func parseAddrRange(in map[string]interface{}, parseFunc fn) (addr AddrRange, err error) { + wasMin, wasMax, wasStart, wasInc := false, false, false, false for k, v := range in { switch strings.ToLower(k) { case "min": - min, err := parseFunc(v.(string)) + addr.Min, err = parseFunc(v.(string)) if err != nil { return AddrRange{}, fmt.Errorf("parsing min returned: %v", err) } - addr.Min = make([]byte, len(min)) - copy(addr.Min, min) wasMin = true case "max": - max, err := parseFunc(v.(string)) + addr.Max, err = parseFunc(v.(string)) if err != nil { return AddrRange{}, fmt.Errorf("parsing max returned: %v", err) } - addr.Max = make([]byte, len(max)) - copy(addr.Max, max) wasMax = true case "start": - start, err := parseFunc(v.(string)) + addr.Current, err = parseFunc(v.(string)) if err != nil { return AddrRange{}, fmt.Errorf("parsing start returned: %v", err) } - addr.Current = make([]byte, len(start)) - copy(addr.Current, start) wasStart = true - case "incr": - addr.Incr = big.NewInt((int64)(v.(float64))).Bytes() - wasIncr = true + case "inc": + addr.Inc = (uint64)(v.(float64)) + wasInc = true default: return AddrRange{}, fmt.Errorf("unknown key %s", k) } @@ -757,24 +727,23 @@ func parseAddrRange(in map[string]interface{}, parseFunc fn) (AddrRange, error) if !wasMax || !wasMin { return AddrRange{}, fmt.Errorf("Min and max values should be given for range") } - if bytes.Compare(addr.Max, addr.Min) < 0 { + if addr.Max < addr.Min { return AddrRange{}, fmt.Errorf("Min value should be less than Max") } if !wasStart { - addr.Current = make([]byte, len(addr.Min)) - copy(addr.Current, addr.Min) + addr.Current = addr.Min } - if bytes.Compare(addr.Current, addr.Min) < 0 || bytes.Compare(addr.Current, addr.Max) > 0 { + if addr.Current < addr.Min || addr.Current > addr.Max { return AddrRange{}, fmt.Errorf(fmt.Sprintf("Start value should be between min and max: start=%v, min=%v, max=%v", addr.Current, addr.Min, addr.Max)) } - if !wasIncr { - addr.Incr = []byte{1} + if !wasInc { + addr.Inc = 1 } return addr, nil } -func parsePort(in interface{}) (PortRange, error) { +func parsePort(in interface{}) (AddrRange, error) { switch v2 := in.(type) { case map[string]interface{}: for k, v := range v2 { @@ -782,70 +751,70 @@ func parsePort(in interface{}) (PortRange, error) { case "range": portRng, err := parsePortRange(v.(map[string]interface{})) if err != nil { - return PortRange{}, fmt.Errorf("parseAddrRange returned: %v", err) + return AddrRange{}, fmt.Errorf("parseAddrRange returned: %v", err) } return portRng, nil default: - return PortRange{}, fmt.Errorf("unknown key in port: %s", k) + return AddrRange{}, fmt.Errorf("unknown key in port: %s", k) } } case float64: if in.(float64) < 0 { - return PortRange{}, fmt.Errorf("Port should be positive") + return AddrRange{}, fmt.Errorf("Port should be positive") } - port := (uint16)(in.(float64)) - return PortRange{Min: port, Max: port, Current: port, Incr: 0}, nil + port := (uint64)(in.(float64)) + return AddrRange{Min: port, Max: port, Current: port, Inc: 0}, nil } - return PortRange{}, fmt.Errorf("unknown type") + return AddrRange{}, fmt.Errorf("unknown type") } -func parsePortRange(in map[string]interface{}) (PortRange, error) { - portRng := PortRange{Incr: 0} - wasMin, wasMax, wasStart, wasIncr := false, false, false, false +func parsePortRange(in map[string]interface{}) (AddrRange, error) { + portRng := AddrRange{Inc: 0} + wasMin, wasMax, wasStart, wasInc := false, false, false, false for k, v := range in { switch strings.ToLower(k) { case "min": if v.(float64) < 0 { - return PortRange{}, fmt.Errorf("Min should be positive") + return AddrRange{}, fmt.Errorf("Min should be positive") } - portRng.Min = (uint16)(v.(float64)) + portRng.Min = (uint64)(v.(float64)) wasMin = true case "max": if v.(float64) < 0 { - return PortRange{}, fmt.Errorf("Max should be positive") + return AddrRange{}, fmt.Errorf("Max should be positive") } - portRng.Max = (uint16)(v.(float64)) + portRng.Max = (uint64)(v.(float64)) wasMax = true case "start": if v.(float64) < 0 { - return PortRange{}, fmt.Errorf("Start should be positive") + return AddrRange{}, fmt.Errorf("Start should be positive") } - portRng.Current = (uint16)(v.(float64)) + portRng.Current = (uint64)(v.(float64)) wasStart = true - case "incr": + case "inc": if v.(float64) < 0 { - return PortRange{}, fmt.Errorf("Incr should be positive") + return AddrRange{}, fmt.Errorf("Inc should be positive") } - portRng.Incr = (uint16)(v.(float64)) - wasIncr = true + portRng.Inc = (uint64)(v.(float64)) + wasInc = true default: - return PortRange{}, fmt.Errorf("unknown key %s", k) + return AddrRange{}, fmt.Errorf("unknown key %s", k) } } if !wasMax || !wasMin { - return PortRange{}, fmt.Errorf("Min and max values should be given for range") + return AddrRange{}, fmt.Errorf("Min and max values should be given for range") } if portRng.Max < portRng.Min { - return PortRange{}, fmt.Errorf("Min value should be <= Max value") + return AddrRange{}, fmt.Errorf("Min value should be <= Max value") } if !wasStart { portRng.Current = portRng.Min } if portRng.Current < portRng.Min || portRng.Current > portRng.Max { - return PortRange{}, fmt.Errorf("Start should be in range of min and max") + return AddrRange{}, fmt.Errorf("Start should be in range of min and max") } - if !wasIncr { - portRng.Incr = 1 + if !wasInc { + portRng.Inc = 1 } return portRng, nil } diff --git a/examples/nffPktgen/generator/utility.go b/examples/nffPktgen/generator/utility.go new file mode 100644 index 00000000..ea1071d1 --- /dev/null +++ b/examples/nffPktgen/generator/utility.go @@ -0,0 +1,156 @@ +// Copyright 2018 Intel Corporation. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package generator + +import ( + "fmt" + "math/rand" + "os" + "sync/atomic" + + "github.com/intel-go/nff-go/flow" + "github.com/intel-go/nff-go/packet" +) + +var gen generator + +type generator struct { + count uint64 +} + +// GetGenerator returns generator struct pointer +// generator is single and created only once +func GetGenerator() *generator { + return &gen +} + +// GetGeneratedNumber returns a number of packets generated +func (g *generator) GetGeneratedNumber() uint64 { + return atomic.LoadUint64(&(g.count)) +} + +// ReadConfig function reads and parses config file. +func ReadConfig(fileName string) ([]MixConfig, error) { + f, err := os.Open(fileName) + if err != nil { + return nil, fmt.Errorf("opening file failed with: %v ", err) + } + cfg, err := ParseConfig(f) + if err != nil { + return nil, fmt.Errorf("parsing config failed with: %v", err) + } + return cfg, nil +} + +func getGenerator(configuration PacketConfig) (func(*packet.Packet, *PacketConfig, *rand.Rand), error) { + switch configuration.DType { + case ETHERHDR: + l2 := configuration.Ether + switch l2.DType { + case IPv4HDR: + l3 := l2.IPv4 + switch l3.DType { + case TCPHDR: + return generateTCPIPv4, nil + case UDPHDR: + return generateUDPIPv4, nil + case ICMPHDR: + return generateICMPIPv4, nil + case DATA: + return generateIPv4, nil + default: + return nil, fmt.Errorf("unknown packet l4 configuration") + } + case IPv6HDR: + l3 := l2.IPv6 + switch l3.DType { + case TCPHDR: + return generateTCPIPv6, nil + case UDPHDR: + return generateUDPIPv6, nil + case ICMPHDR: + return generateICMPIPv6, nil + case DATA: + return generateIPv6, nil + default: + return nil, fmt.Errorf("unknown packet l4 configuration") + } + case ARPHDR: + return generateARP, nil + case DATA: + return generateEther, nil + default: + return nil, fmt.Errorf("unknown packet l3 configuration") + } + default: + return nil, fmt.Errorf("unknown packet l2 configuration") + } +} + +// one unit for each mix +type generatorTableUnit struct { + have, need uint32 + generatorFunc func(*packet.Packet, *PacketConfig, *rand.Rand) + config PacketConfig +} + +func (gtu *generatorTableUnit) String() string { + return fmt.Sprintf("need: %d, config: %v\n", gtu.need, gtu.config) +} + +type genParameters struct { + table []generatorTableUnit + next uint32 + length uint32 + rnd *rand.Rand +} + +func (gp genParameters) Copy() interface{} { + ret := new(genParameters) + ret.table = make([]generatorTableUnit, len(gp.table)) + copy(ret.table, gp.table) + ret.length = gp.length + ret.rnd = rand.New(rand.NewSource(13)) + return ret +} + +func (gp genParameters) Delete() { +} + +// GetContext gets generator context according to config +func GetContext(mixConfig []MixConfig) (*genParameters, error) { + var t []generatorTableUnit + for _, packetConfig := range mixConfig { + genFunc, err := getGenerator(packetConfig.Config) + if err != nil { + return nil, err + } + tu := generatorTableUnit{have: 0, need: packetConfig.Quantity, generatorFunc: genFunc, config: packetConfig.Config} + t = append(t, tu) + } + ret := new(genParameters) + ret.table = t + ret.length = uint32(len(t)) + ret.rnd = rand.New(rand.NewSource(13)) + return ret, nil +} + +// Generate is a main generatior func +func Generate(pkt *packet.Packet, context flow.UserContext) { + genP := context.(*genParameters) + if genP.length > 1 { + if genP.table[genP.next].have == genP.table[genP.next].need { + genP.table[genP.next].have = 0 + if genP.next+1 < genP.length { + genP.next++ + } else { + genP.next = 0 + } + } + } + genP.table[genP.next].generatorFunc(pkt, &genP.table[genP.next].config, genP.rnd) + atomic.AddUint64(&(gen.count), 1) + genP.table[genP.next].have++ +} diff --git a/examples/nffPktgen/perfTest/.gitignore b/examples/nffPktgen/perfTest/.gitignore deleted file mode 100644 index fe034ff6..00000000 --- a/examples/nffPktgen/perfTest/.gitignore +++ /dev/null @@ -1 +0,0 @@ -perfTest diff --git a/examples/nffPktgen/sendGetBack/.gitignore b/examples/nffPktgen/sendGetBack/.gitignore deleted file mode 100644 index 820c9c8e..00000000 --- a/examples/nffPktgen/sendGetBack/.gitignore +++ /dev/null @@ -1 +0,0 @@ -sendGetBack diff --git a/examples/nffPktgen/testing/.gitignore b/examples/nffPktgen/testing/.gitignore new file mode 100644 index 00000000..eb624839 --- /dev/null +++ b/examples/nffPktgen/testing/.gitignore @@ -0,0 +1,3 @@ +sendGetBack +perfTest +dump diff --git a/examples/nffPktgen/sendGetBack/Dockerfile b/examples/nffPktgen/testing/Dockerfile similarity index 93% rename from examples/nffPktgen/sendGetBack/Dockerfile rename to examples/nffPktgen/testing/Dockerfile index 83a9e9b9..fe79ce59 100644 --- a/examples/nffPktgen/sendGetBack/Dockerfile +++ b/examples/nffPktgen/testing/Dockerfile @@ -9,3 +9,5 @@ LABEL RUN docker run -it --privileged -v /sys/bus/pci/drivers:/sys/bus/pci/drive WORKDIR /workdir COPY sendGetBack . +COPY perfTesting . +COPY dump . diff --git a/examples/nffPktgen/perfTest/Makefile b/examples/nffPktgen/testing/Makefile similarity index 84% rename from examples/nffPktgen/perfTest/Makefile rename to examples/nffPktgen/testing/Makefile index 769c07ee..8e31b4fe 100644 --- a/examples/nffPktgen/perfTest/Makefile +++ b/examples/nffPktgen/testing/Makefile @@ -4,6 +4,6 @@ PATH_TO_MK = ../../../mk IMAGENAME = nff-pktgen -EXECUTABLES = perfTest +EXECUTABLES = sendGetBack perfTest dump include $(PATH_TO_MK)/leaf.mk diff --git a/examples/nffPktgen/sendGetBack/README.md b/examples/nffPktgen/testing/README.md similarity index 100% rename from examples/nffPktgen/sendGetBack/README.md rename to examples/nffPktgen/testing/README.md diff --git a/examples/nffPktgen/testing/arp.json b/examples/nffPktgen/testing/arp.json index 84db0630..0769f580 100644 --- a/examples/nffPktgen/testing/arp.json +++ b/examples/nffPktgen/testing/arp.json @@ -4,7 +4,7 @@ "opcode": 1, "gratuitous" : true, "sha": "99:25:96:FF:FE:12", - "spa": "1.1.1.1" + "spa": "1.1.1.2" } } } diff --git a/examples/nffPktgen/testing/arpVlan.json b/examples/nffPktgen/testing/arpVlan.json index 606d94c6..94eb5fba 100644 --- a/examples/nffPktgen/testing/arpVlan.json +++ b/examples/nffPktgen/testing/arpVlan.json @@ -1,12 +1,11 @@ { "ether": { "vlan-tci": 200, - "daddr": "00:25:96:FF:FE:12", "arp": { "opcode": 1, "gratuitous" : true, "sha": "99:25:96:FF:FE:12", - "spa": "1.1.1.1" + "spa": "1.1.1.2" } } } diff --git a/examples/nffPktgen/testing/config.json b/examples/nffPktgen/testing/config.json index c0f03244..36f388c0 100644 --- a/examples/nffPktgen/testing/config.json +++ b/examples/nffPktgen/testing/config.json @@ -5,12 +5,11 @@ "min": "00:25:96:FF:FE:12", "start": "00:30:00:FF:FE:12", "max": "00:FF:96:FF:FE:12", - "incr": 3 + "inc": 3 } }, "daddr": "00:FF:96:FF:FE:12", - "ip": { - "version": 4, + "ipv4": { "saddr": { "range": { "min": "1.1.127.1", @@ -40,4 +39,4 @@ } } } -} \ No newline at end of file +} diff --git a/examples/nffPktgen/testing/dump.go b/examples/nffPktgen/testing/dump.go new file mode 100644 index 00000000..43236f44 --- /dev/null +++ b/examples/nffPktgen/testing/dump.go @@ -0,0 +1,45 @@ +// Copyright 2018 Intel Corporation. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "flag" + "fmt" + "github.com/intel-go/nff-go/examples/nffPktgen/generator" + "github.com/intel-go/nff-go/flow" + "github.com/intel-go/nff-go/packet" + "os" +) + +func main() { + name := flag.String("m", "", "filename") + flag.Parse() + // Init NFF-GO system at 16 available cores + config := flow.Config{ + CPUList: "0-43", + DisableScheduler: true, + } + flow.CheckFatal(flow.SystemInit(&config)) + + configuration, err := generator.ReadConfig(*name) + flow.CheckFatal(err) + context, err1 := generator.GetContext(configuration) + flow.CheckFatal(err1) + + outFlow, _, _ := flow.SetFastGenerator(generator.Generate, 100, context) + flow.SetHandler(outFlow, handleRecv, nil) + flow.SetStopper(outFlow) + flow.CheckFatal(flow.SystemStart()) +} + +var got int + +func handleRecv(currentPacket *packet.Packet, context flow.UserContext) { + got++ + fmt.Printf("Raw bytes=%x\n", currentPacket.GetRawPacketBytes()) + if got > 25 { + os.Exit(0) + } +} diff --git a/examples/nffPktgen/testing/ether.json b/examples/nffPktgen/testing/ether.json index 3b50e306..fbce7af4 100644 --- a/examples/nffPktgen/testing/ether.json +++ b/examples/nffPktgen/testing/ether.json @@ -5,7 +5,7 @@ "min": "00:25:96:FF:FE:12", "start": "00:30:00:FF:FE:12", "max": "00:FF:96:FF:FE:12", - "incr": 10 + "inc": 10 } }, "daddr": "00:FF:96:FF:FE:12", @@ -14,4 +14,4 @@ "deviation": 300 } } -} \ No newline at end of file +} diff --git a/examples/nffPktgen/testing/ip4.json b/examples/nffPktgen/testing/ip4.json index d7de24ef..20749859 100644 --- a/examples/nffPktgen/testing/ip4.json +++ b/examples/nffPktgen/testing/ip4.json @@ -4,17 +4,16 @@ "range": { "min": "00:25:96:FF:FE:12", "max": "00:FF:96:FF:FE:12", - "incr": 10 + "inc": 10 } }, "daddr": "00:00:96:FF:00:00", - "ip": { - "version": 4, + "ipv4": { "saddr": "1.1.127.1", "daddr": { "range": { "min": "1.1.1.1", - "max": "3.3.3.3" + "max": "1.1.3.3" } }, diff --git a/examples/nffPktgen/testing/ip4icmp.json b/examples/nffPktgen/testing/ip4icmp.json index 8ee23c1c..2b80b1bd 100644 --- a/examples/nffPktgen/testing/ip4icmp.json +++ b/examples/nffPktgen/testing/ip4icmp.json @@ -5,12 +5,11 @@ "min": "00:25:96:FF:FE:12", "start": "00:30:00:FF:FE:12", "max": "00:FF:96:FF:FE:12", - "incr": 3 + "inc": 3 } }, "daddr": "00:FF:96:FF:FE:12", - "ip": { - "version": 4, + "ipv4": { "saddr": "1.1.127.1", "daddr": "1.1.127.0", "icmp": { @@ -19,20 +18,20 @@ "seq": "increasing", "pdist": [ { - "probability": 0.3, + "probability": 0.33, "randbytes": { "size": 50, "deviation": 10 } }, { - "probability": 0.3, + "probability": 0.33, "raw": { "data": "sfsfsfs" } }, { - "probability": 0.3, + "probability": 0.34, "raw": { "data": "0000000000000000000000000000000000" } @@ -41,4 +40,4 @@ } } } -} \ No newline at end of file +} diff --git a/examples/nffPktgen/testing/ip4tcp.json b/examples/nffPktgen/testing/ip4tcp.json index be567d51..c18d59e2 100644 --- a/examples/nffPktgen/testing/ip4tcp.json +++ b/examples/nffPktgen/testing/ip4tcp.json @@ -5,12 +5,11 @@ "min": "00:25:96:FF:FE:12", "start": "00:30:00:FF:FE:12", "max": "00:FF:96:FF:FE:12", - "incr": 3 + "inc": 3 } }, "daddr": "00:FF:96:FF:FE:12", - "ip": { - "version": 4, + "ipv4": { "saddr": "1.1.127.1", "daddr": "1.1.127.0", "tcp": { @@ -18,7 +17,7 @@ "range": { "min": 1, "max": 8080, - "incr": 100 + "inc": 100 } }, "dport": 2000, @@ -42,4 +41,4 @@ } } } -} \ No newline at end of file +} diff --git a/examples/nffPktgen/testing/ip4tcpVlan.json b/examples/nffPktgen/testing/ip4tcpVlan.json index 275bf074..3cfe42fd 100644 --- a/examples/nffPktgen/testing/ip4tcpVlan.json +++ b/examples/nffPktgen/testing/ip4tcpVlan.json @@ -5,13 +5,12 @@ "min": "00:25:96:FF:FE:12", "start": "00:30:00:FF:FE:12", "max": "00:FF:96:FF:FE:12", - "incr": 3 + "inc": 3 } }, "daddr": "00:FF:96:FF:FE:12", "vlan-tci": 123, - "ip": { - "version": 4, + "ipv4": { "saddr": "1.1.127.1", "daddr": "1.1.127.0", "tcp": { @@ -19,7 +18,7 @@ "range": { "min": 1, "max": 8080, - "incr": 100 + "inc": 100 } }, "dport": 2000, @@ -43,4 +42,4 @@ } } } -} \ No newline at end of file +} diff --git a/examples/nffPktgen/testing/ip4udp.json b/examples/nffPktgen/testing/ip4udp.json index 5f9f498a..d3c89221 100644 --- a/examples/nffPktgen/testing/ip4udp.json +++ b/examples/nffPktgen/testing/ip4udp.json @@ -5,12 +5,11 @@ "min": "00:25:96:FF:FE:12", "start": "00:30:00:FF:FE:12", "max": "00:FF:96:FF:FE:12", - "incr": 3 + "inc": 3 } }, "daddr": "00:FF:96:FF:FE:12", - "ip": { - "version": 4, + "ipv4": { "saddr": "1.1.127.1", "daddr": "1.1.127.0", "udp": { @@ -18,7 +17,7 @@ "range": { "min": 1, "max": 8080, - "incr": 100 + "inc": 100 } }, "dport": 2000, @@ -40,4 +39,4 @@ } } } -} \ No newline at end of file +} diff --git a/examples/nffPktgen/testing/ip4udpVlan.json b/examples/nffPktgen/testing/ip4udpVlan.json index a6c2b607..331c1910 100644 --- a/examples/nffPktgen/testing/ip4udpVlan.json +++ b/examples/nffPktgen/testing/ip4udpVlan.json @@ -5,13 +5,12 @@ "min": "00:25:96:FF:FE:12", "start": "00:30:00:FF:FE:12", "max": "00:FF:96:FF:FE:12", - "incr": 3 + "inc": 3 } }, "daddr": "00:FF:96:FF:FE:12", "vlan-tci": 123, - "ip": { - "version": 4, + "ipv4": { "saddr": "1.1.127.1", "daddr": "1.1.127.0", "udp": { @@ -19,7 +18,7 @@ "range": { "min": 1, "max": 8080, - "incr": 100 + "inc": 100 } }, "dport": 2000, @@ -41,4 +40,4 @@ } } } -} \ No newline at end of file +} diff --git a/examples/nffPktgen/testing/ip6.json b/examples/nffPktgen/testing/ip6.json index 10ba3b18..fa286b0e 100644 --- a/examples/nffPktgen/testing/ip6.json +++ b/examples/nffPktgen/testing/ip6.json @@ -4,12 +4,11 @@ "range": { "min": "00:25:96:FF:FE:12", "max": "00:FF:96:FF:FE:12", - "incr": 10 + "inc": 10 } }, "daddr": "00:00:96:FF:00:00", - "ip": { - "version": 6, + "ipv6": { "saddr": "2001:db8:a0b:12f0::", "pdist": [ { @@ -21,4 +20,4 @@ ] } } -} \ No newline at end of file +} diff --git a/examples/nffPktgen/testing/ip6icmp.json b/examples/nffPktgen/testing/ip6icmp.json index 95a45e36..955541e1 100644 --- a/examples/nffPktgen/testing/ip6icmp.json +++ b/examples/nffPktgen/testing/ip6icmp.json @@ -5,26 +5,25 @@ "min": "00:25:96:FF:FE:12", "start": "00:30:00:FF:FE:12", "max": "00:FF:96:FF:FE:12", - "incr": 3 + "inc": 3 } }, "daddr": "00:FF:96:FF:FE:12", - "ip": { - "version": 6, + "ipv6": { "icmp": { "type": 10, "code": 1, "seq": "increasing", "pdist": [ { - "probability": 0.3, + "probability": 0.5, "randbytes": { "size": 50, "deviation": 10 } }, { - "probability": 0.3, + "probability": 0.5, "raw": { "data": "0000000000000000000000000000000000" } @@ -33,4 +32,4 @@ } } } -} \ No newline at end of file +} diff --git a/examples/nffPktgen/testing/ip6tcp.json b/examples/nffPktgen/testing/ip6tcp.json index 9dcef700..374c483f 100644 --- a/examples/nffPktgen/testing/ip6tcp.json +++ b/examples/nffPktgen/testing/ip6tcp.json @@ -5,12 +5,11 @@ "min": "00:25:96:FF:FE:12", "start": "00:30:00:FF:FE:12", "max": "00:FF:96:FF:FE:12", - "incr": 3 + "inc": 3 } }, "daddr": "00:FF:96:FF:FE:12", - "ip": { - "version": 6, + "ipv6": { "saddr": "2001:db8:a0b:12f0::1", "daddr": "2001:db8:a0b:12f0::1", "tcp": { @@ -18,7 +17,7 @@ "range": { "min": 1, "max": 8080, - "incr": 100 + "inc": 100 } }, "dport": 2000, @@ -33,7 +32,7 @@ } }, { - "probability": 0.2, + "probability": 0.3, "raw": { "data": "sfsfsfs" } @@ -42,4 +41,4 @@ } } } -} \ No newline at end of file +} diff --git a/examples/nffPktgen/testing/ip6udp.json b/examples/nffPktgen/testing/ip6udp.json index 344a0cf5..7e615610 100644 --- a/examples/nffPktgen/testing/ip6udp.json +++ b/examples/nffPktgen/testing/ip6udp.json @@ -5,12 +5,11 @@ "min": "00:25:96:FF:FE:12", "start": "00:30:00:FF:FE:12", "max": "00:FF:96:FF:FE:12", - "incr": 3 + "inc": 3 } }, "daddr": "00:FF:96:FF:FE:12", - "ip": { - "version": 6, + "ipv6": { "saddr": "2001:db8:a0b:12f0::1", "daddr": "2001:db8:a0b:12f0::1", "udp": { @@ -18,7 +17,7 @@ "range": { "min": 1, "max": 8080, - "incr": 100 + "inc": 100 } }, "dport": 2000, @@ -29,4 +28,4 @@ } } } -} \ No newline at end of file +} diff --git a/examples/nffPktgen/testing/mix.json b/examples/nffPktgen/testing/mix.json index 23aa7a3b..7fc0e429 100644 --- a/examples/nffPktgen/testing/mix.json +++ b/examples/nffPktgen/testing/mix.json @@ -5,12 +5,11 @@ "range": { "min": "00:25:96:FF:FE:12", "max": "00:FF:96:FF:FE:12", - "incr": 10 + "inc": 10 } }, "daddr": "00:00:96:FF:00:00", - "ip": { - "version": 4, + "ipv4": { "saddr": "1.1.127.1", "daddr": { "range": { @@ -37,12 +36,11 @@ "range": { "min": "00:25:96:FF:FE:12", "max": "00:FF:96:FF:FE:12", - "incr": 10 + "inc": 10 } }, "daddr": "00:00:96:FF:00:00", - "ip": { - "version": 4, + "ipv4": { "saddr": "1.1.127.1", "daddr": { "range": { @@ -55,7 +53,7 @@ "range": { "min": 1, "max": 8080, - "incr": 100 + "inc": 100 } }, "dport": 2000, @@ -74,12 +72,11 @@ "range": { "min": "00:25:96:FF:FE:12", "max": "00:FF:96:FF:FE:12", - "incr": 10 + "inc": 10 } }, "daddr": "00:00:96:FF:00:00", - "ip": { - "version": 4, + "ipv4": { "saddr": "1.1.127.1", "daddr": { "range": { @@ -92,7 +89,7 @@ "range": { "min": 1, "max": 8080, - "incr": 100 + "inc": 100 } }, "dport": 2000, diff --git a/examples/nffPktgen/perfTest/perfTest.go b/examples/nffPktgen/testing/perfTest.go similarity index 73% rename from examples/nffPktgen/perfTest/perfTest.go rename to examples/nffPktgen/testing/perfTest.go index 01e4e733..abd7d390 100644 --- a/examples/nffPktgen/perfTest/perfTest.go +++ b/examples/nffPktgen/testing/perfTest.go @@ -17,9 +17,9 @@ func main() { genConfig, cores string port uint ) - flag.Uint64Var(&speed, "speed", 6000000, "speed of fast generator, Pkts/s") - flag.StringVar(&genConfig, "config", "../testing/ip4.json", "specifies config for generator") - flag.StringVar(&cores, "cores", "0-3", "specifies cores") + flag.Uint64Var(&speed, "speed", 120000000, "speed of fast generator, Pkts/s") + flag.StringVar(&genConfig, "config", "ip4.json", "specifies config for generator") + flag.StringVar(&cores, "cores", "0-2", "specifies cores") flag.UintVar(&port, "port", 1, "specifies output port") flag.Parse() @@ -35,8 +35,7 @@ func main() { } context, err := generator.GetContext(configuration) flow.CheckFatal(err) - outFlow, err := flow.SetFastGenerator(generator.Generate, speed, context) - flow.CheckFatal(err) + outFlow, _, _ := flow.SetFastGenerator(generator.Generate, speed, context) flow.CheckFatal(flow.SetSender(outFlow, uint16(port))) flow.CheckFatal(flow.SystemStart()) } diff --git a/examples/nffPktgen/testing/run.sh b/examples/nffPktgen/testing/run.sh index 38bc1bc3..ba6ce9f9 100644 --- a/examples/nffPktgen/testing/run.sh +++ b/examples/nffPktgen/testing/run.sh @@ -1,18 +1,18 @@ #!/bin/bash -../sendGetBack/sendGetBack -number 100000 -outConfig "'ether.pcap': 'ether.json'" -../sendGetBack/sendGetBack -number 10000 -outConfig "'ip4.pcap':'ip4.json'" -../sendGetBack/sendGetBack -number 1000 -outConfig "'ip6.pcap':'ip6.json'" -../sendGetBack/sendGetBack -number 100000 -outConfig "'ip4tcp.pcap':'ip4tcp.json'" -../sendGetBack/sendGetBack -number 100000 -outConfig "'ip4tcpVlan.pcap':'ip4tcpVlan.json'" -../sendGetBack/sendGetBack -number 1000000 -outConfig "'ip6tcp.pcap':'ip6tcp.json'" -../sendGetBack/sendGetBack -number 10000 -outConfig "'ip4udp.pcap':'ip4udp.json'" -../sendGetBack/sendGetBack -number 10000 -outConfig "'ip4udpVlan.pcap':'ip4udpVlan.json'" -../sendGetBack/sendGetBack -number 1000 -outConfig "'ip6udp.pcap':'ip6udp.json'" -../sendGetBack/sendGetBack -number 1000000 -outConfig "'ip4icmp.pcap':'ip4icmp.json'" -../sendGetBack/sendGetBack -number 100000 -outConfig "'ip6icmp.pcap':'ip6icmp.json'" -../sendGetBack/sendGetBack -number 1000 -outConfig "'arp.pcap':'arp.json'" -../sendGetBack/sendGetBack -number 100 -outConfig "'vlanTag.pcap':'vlanTag.json'" -../sendGetBack/sendGetBack -number 100 -outConfig "'arpVlan.pcap':'arpVlan.json'" -../sendGetBack/sendGetBack -number 100 -outConfig "'config.pcap': 'config.json'" -../sendGetBack/sendGetBack -number 100 -outConfig "'mix.pcap': 'mix.json'" \ No newline at end of file +./sendGetBack -number 100000 -outConfig "'ether.pcap': 'ether.json'" +./sendGetBack -number 10000 -outConfig "'ip4.pcap':'ip4.json'" +./sendGetBack -number 1000 -outConfig "'ip6.pcap':'ip6.json'" +./sendGetBack -number 100000 -outConfig "'ip4tcp.pcap':'ip4tcp.json'" +./sendGetBack -number 100000 -outConfig "'ip4tcpVlan.pcap':'ip4tcpVlan.json'" +./sendGetBack -number 1000000 -outConfig "'ip6tcp.pcap':'ip6tcp.json'" +./sendGetBack -number 10000 -outConfig "'ip4udp.pcap':'ip4udp.json'" +./sendGetBack -number 10000 -outConfig "'ip4udpVlan.pcap':'ip4udpVlan.json'" +./sendGetBack -number 1000 -outConfig "'ip6udp.pcap':'ip6udp.json'" +./sendGetBack -number 1000000 -outConfig "'ip4icmp.pcap':'ip4icmp.json'" +./sendGetBack -number 100000 -outConfig "'ip6icmp.pcap':'ip6icmp.json'" +./sendGetBack -number 1000 -outConfig "'arp.pcap':'arp.json'" +./sendGetBack -number 100 -outConfig "'vlanTag.pcap':'vlanTag.json'" +./sendGetBack -number 100 -outConfig "'arpVlan.pcap':'arpVlan.json'" +./sendGetBack -number 100 -outConfig "'config.pcap': 'config.json'" +./sendGetBack -number 100 -outConfig "'mix.pcap': 'mix.json'" diff --git a/examples/nffPktgen/sendGetBack/sendGetBack.go b/examples/nffPktgen/testing/sendGetBack.go similarity index 97% rename from examples/nffPktgen/sendGetBack/sendGetBack.go rename to examples/nffPktgen/testing/sendGetBack.go index 003831bd..d40b631a 100644 --- a/examples/nffPktgen/sendGetBack/sendGetBack.go +++ b/examples/nffPktgen/testing/sendGetBack.go @@ -121,7 +121,8 @@ func main() { // Init NFF-GO system at 16 available cores config := flow.Config{ - CPUList: "0-43", + CPUList: "0-43", + DisableScheduler: true, } flow.CheckFatal(flow.SystemInit(&config)) @@ -136,7 +137,7 @@ func main() { } context, err := generator.GetContext(configuration) flow.CheckFatal(err) - outFlow, err := flow.SetFastGenerator(generator.Generate, speed, context) + outFlow, _, err := flow.SetFastGenerator(generator.Generate, speed, context) flow.CheckFatal(err) switch t := key.(type) { case int: diff --git a/examples/nffPktgen/testing/vlanTag.json b/examples/nffPktgen/testing/vlanTag.json index ea6f30e2..77b53e99 100644 --- a/examples/nffPktgen/testing/vlanTag.json +++ b/examples/nffPktgen/testing/vlanTag.json @@ -4,13 +4,12 @@ "range": { "min": "00:25:96:FF:FE:12", "max": "00:FF:96:FF:FE:12", - "incr": 10 + "inc": 10 } }, "daddr": "00:00:96:FF:00:00", "vlan-tci": 123, - "ip": { - "version": 6, + "ipv6": { "saddr": "2001:db8:a0b:12f0::", "pdist": [ { @@ -22,4 +21,4 @@ ] } } -} \ No newline at end of file +} diff --git a/examples/pingReplay.go b/examples/pingReplay.go index 71053972..edabdff7 100644 --- a/examples/pingReplay.go +++ b/examples/pingReplay.go @@ -3,6 +3,7 @@ package main import ( "flag" "github.com/intel-go/nff-go/flow" + "github.com/intel-go/nff-go/types" ) func main() { @@ -14,7 +15,7 @@ func main() { inputFlow, err := flow.SetReceiver(uint16(*inport)) flow.CheckFatal(err) - flow.CheckFatal(flow.SetIPForPort(uint16(*inport), uint32(20)<<24|uint32(20)<<16|uint32(20)<<8|uint32(20))) + flow.CheckFatal(flow.SetIPForPort(uint16(*inport), types.IPv4Address(20)<<24|types.IPv4Address(20)<<16|types.IPv4Address(20)<<8|types.IPv4Address(20))) flow.CheckFatal(flow.DealARPICMP(inputFlow)) flow.CheckFatal(flow.SetStopper(inputFlow)) diff --git a/examples/tutorial/common.go b/examples/tutorial/common.go index 00eb401f..6d5e973b 100644 --- a/examples/tutorial/common.go +++ b/examples/tutorial/common.go @@ -7,16 +7,16 @@ import ( "net" "os" - "github.com/intel-go/nff-go/common" "github.com/intel-go/nff-go/flow" "github.com/intel-go/nff-go/packet" + "github.com/intel-go/nff-go/types" ) var config map[string][]string -var dstMac0 [common.EtherAddrLen]uint8 -var srcMac0 [common.EtherAddrLen]uint8 -var dstMac1 [common.EtherAddrLen]uint8 -var srcMac1 [common.EtherAddrLen]uint8 +var dstMac0 [types.EtherAddrLen]uint8 +var srcMac0 [types.EtherAddrLen]uint8 +var dstMac1 [types.EtherAddrLen]uint8 +var srcMac1 [types.EtherAddrLen]uint8 var modifyPacket = []func(pkt *packet.Packet, ctx flow.UserContext){modifyPacket0, modifyPacket1} var direct = "direct" @@ -38,7 +38,7 @@ func readConfig(fileName string) error { return nil } -func printMAC(prompt string, mac [common.EtherAddrLen]uint8) { +func printMAC(prompt string, mac [types.EtherAddrLen]uint8) { log.Printf("%s: %02x:%02x:%02x:%02x:%02x:%02x\n", prompt, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]) } diff --git a/examples/tutorial/step10.go b/examples/tutorial/step10.go index 0b33345e..88be669b 100644 --- a/examples/tutorial/step10.go +++ b/examples/tutorial/step10.go @@ -3,7 +3,7 @@ package main import "sync/atomic" import "time" import "unsafe" -import "github.com/intel-go/nff-go/common" +import "github.com/intel-go/nff-go/types" import "github.com/intel-go/nff-go/flow" import "github.com/intel-go/nff-go/packet" @@ -43,10 +43,10 @@ func myHandler(curV []*packet.Packet, mask *[vecSize]bool, ctx flow.UserContext) for i := uint(0); i < vecSize; i++ { if (*mask)[i] == true { cur := curV[i] - cur.EncapsulateHead(common.EtherLen, common.IPv4MinLen) + cur.EncapsulateHead(types.EtherLen, types.IPv4MinLen) cur.ParseL3() - cur.GetIPv4NoCheck().SrcAddr = packet.BytesToIPv4(111, 22, 3, 0) - cur.GetIPv4NoCheck().DstAddr = packet.BytesToIPv4(3, 22, 111, 0) + cur.GetIPv4NoCheck().SrcAddr = types.BytesToIPv4(111, 22, 3, 0) + cur.GetIPv4NoCheck().DstAddr = types.BytesToIPv4(3, 22, 111, 0) cur.GetIPv4NoCheck().VersionIhl = 0x45 cur.GetIPv4NoCheck().NextProtoID = 0x04 } diff --git a/examples/tutorial/step11.go b/examples/tutorial/step11.go index 71498c7c..a619e74f 100644 --- a/examples/tutorial/step11.go +++ b/examples/tutorial/step11.go @@ -3,7 +3,7 @@ package main import "sync/atomic" import "time" import "unsafe" -import "github.com/intel-go/nff-go/common" +import "github.com/intel-go/nff-go/types" import "github.com/intel-go/nff-go/flow" import "github.com/intel-go/nff-go/packet" @@ -43,10 +43,10 @@ func myHandler(curV []*packet.Packet, mask *[vecSize]bool, ctx flow.UserContext) for i := uint(0); i < vecSize; i++ { if (*mask)[i] == true { cur := curV[i] - cur.EncapsulateHead(common.EtherLen, common.IPv4MinLen) + cur.EncapsulateHead(types.EtherLen, types.IPv4MinLen) cur.ParseL3() - cur.GetIPv4NoCheck().SrcAddr = packet.BytesToIPv4(111, 22, 3, 0) - cur.GetIPv4NoCheck().DstAddr = packet.BytesToIPv4(3, 22, 111, 0) + cur.GetIPv4NoCheck().SrcAddr = types.BytesToIPv4(111, 22, 3, 0) + cur.GetIPv4NoCheck().DstAddr = types.BytesToIPv4(3, 22, 111, 0) cur.GetIPv4NoCheck().VersionIhl = 0x45 cur.GetIPv4NoCheck().NextProtoID = 0x04 } diff --git a/examples/tutorial/step9.go b/examples/tutorial/step9.go index be3aeb4e..4442bb4c 100644 --- a/examples/tutorial/step9.go +++ b/examples/tutorial/step9.go @@ -3,7 +3,7 @@ package main import "sync/atomic" import "time" import "unsafe" -import "github.com/intel-go/nff-go/common" +import "github.com/intel-go/nff-go/types" import "github.com/intel-go/nff-go/flow" import "github.com/intel-go/nff-go/packet" @@ -38,10 +38,10 @@ func mySplitter(cur *packet.Packet, ctx flow.UserContext) uint { } func myHandler(cur *packet.Packet, ctx flow.UserContext) { - cur.EncapsulateHead(common.EtherLen, common.IPv4MinLen) + cur.EncapsulateHead(types.EtherLen, types.IPv4MinLen) cur.ParseL3() - cur.GetIPv4NoCheck().SrcAddr = packet.BytesToIPv4(111, 22, 3, 0) - cur.GetIPv4NoCheck().DstAddr = packet.BytesToIPv4(3, 22, 111, 0) + cur.GetIPv4NoCheck().SrcAddr = types.BytesToIPv4(111, 22, 3, 0) + cur.GetIPv4NoCheck().DstAddr = types.BytesToIPv4(3, 22, 111, 0) cur.GetIPv4NoCheck().VersionIhl = 0x45 cur.GetIPv4NoCheck().NextProtoID = 0x04 } diff --git a/flow/counters.go b/flow/counters.go new file mode 100644 index 00000000..5de64865 --- /dev/null +++ b/flow/counters.go @@ -0,0 +1,424 @@ +// Copyright 2019 Intel Corporation. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package flow + +import ( + "encoding/json" + "fmt" + "html/template" + "net" + "net/http" + "strings" + + "github.com/intel-go/nff-go/common" + "github.com/intel-go/nff-go/low" +) + +const ( + rootText = ` +/rxtx for protocol statistics gathered on all send and +receive or /rxtx/name for individual sender/receiver port.
+/json/rxtx for JSON data structure enumerating all +ports that have statistics or /json/rxtx/name for JSON data structure with statistics +of indivitual individual sender/receiver port. +` + + statsSummaryTemplateText = ` +Select a node to see its counters +{{$name := .StatsName}} +{{range $key, $value := .Values}}{{end}}{{/* end range .Values */}} +
{{$key}}
` + + statsTemplateText = ` + + + + + + + + +

{{.NodeName}}

+ + + + + + + + + + + + + + + +
DataTotalDelta
Packets processed
no data
no data
Packets dropped
no data
no data
Bytes processed
no data
no data
+ + + + +
+ Show delta
+ Show total
+
+ + + + +
+
+ +` +) + +var ( + rxtxstats map[string]*common.RXTXStats = map[string]*common.RXTXStats{} + statsSummaryTemplate *template.Template + statsTemplate *template.Template + + countersEnabledInFramework bool = low.CountersEnabledInFramework + countersEnabledInApplication bool = false + useInterlockedCounters bool = low.UseInterlockedCounters + analyzePacketSizes bool = low.AnalyzePacketSizes +) + +func init() { + statsSummaryTemplate = template.New("summary") + statsSummaryTemplate = template.Must(statsSummaryTemplate.Parse(statsSummaryTemplateText)) + statsTemplate = template.New("node") + statsTemplate = template.Must(statsTemplate.Parse(statsTemplateText)) +} + +func handleRoot(w http.ResponseWriter, r *http.Request) { + url := strings.Split(r.URL.Path, "/") + if len(url) < 2 || url[1] == "" || url[1] == "index.html" { + w.Header().Set("Content-Type", "text/html") + fmt.Fprintf(w, rootText) + } else { + http.Error(w, "Bad root request: "+r.URL.Path, http.StatusBadRequest) + return + } +} + +func handleRXTXStats(w http.ResponseWriter, r *http.Request) { + data := struct { + StatsName string + Values map[string]*common.RXTXStats + }{ + StatsName: "rxtx", + Values: rxtxstats, + } + err := statsSummaryTemplate.Execute(w, &data) + if err != nil { + fmt.Println("Error in RXTX summary stats", err) + } +} + +func handleRXTXStatsNode(w http.ResponseWriter, r *http.Request) { + url := strings.Split(r.URL.Path, "/") + sendNodeVisualization(w, r, "rxtx", url[2], true) +} + +func sendNodeVisualization(w http.ResponseWriter, r *http.Request, statsName, nodeName string, doDropped bool) { + data := struct { + StatsName string + NodeName string + DoDropped bool + }{ + StatsName: statsName, + NodeName: nodeName, + DoDropped: doDropped, + } + err := statsTemplate.Execute(w, &data) + if err != nil { + fmt.Println("Error in", statsName, "node", nodeName, "stats", err) + } +} + +func handleJSONRXTXStats(w http.ResponseWriter, r *http.Request) { + enc := json.NewEncoder(w) + + w.Header().Set("Content-Type", "application/json") + names := make([]string, len(rxtxstats)) + index := 0 + for keys := range rxtxstats { + names[index] = keys + index++ + } + enc.Encode(names) +} + +func handleJSONRXTXStatsNode(w http.ResponseWriter, r *http.Request) { + url := strings.Split(r.URL.Path, "/") + enc := json.NewEncoder(w) + + stats, ok := rxtxstats[url[3]] + if !ok { + http.Error(w, "Bad node name: "+url[3], http.StatusBadRequest) + return + } + w.Header().Set("Content-Type", "application/json") + enc.Encode(stats) +} + +func initCounters(addr *net.TCPAddr) error { + http.HandleFunc("/", handleRoot) + http.HandleFunc("/rxtx/", handleRXTXStatsNode) + http.HandleFunc("/rxtx", handleRXTXStats) + http.HandleFunc("/json/rxtx/", handleJSONRXTXStatsNode) + http.HandleFunc("/json/rxtx", handleJSONRXTXStats) + + server := &http.Server{} + listener, err := net.ListenTCP("tcp", addr) + if err != nil { + return nil + } + + go func() { + if err := server.Serve(listener); err != nil { + common.LogWarning(common.Initialization, "Error while serving HTTP requests:", err) + server.Close() + } + }() + + low.SetCountersEnabledInApplication(true) + countersEnabledInApplication = true + + return nil +} + +func registerRXTXStatitics(s *common.RXTXStats, name string) { + rxtxstats[name] = s +} diff --git a/flow/flow.go b/flow/flow.go index ead77aa0..a5507435 100644 --- a/flow/flow.go +++ b/flow/flow.go @@ -30,6 +30,7 @@ package flow import ( + "net" "os" "runtime" "sync/atomic" @@ -39,13 +40,15 @@ import ( "github.com/intel-go/nff-go/common" "github.com/intel-go/nff-go/low" "github.com/intel-go/nff-go/packet" + "github.com/intel-go/nff-go/types" ) var openFlowsNumber = uint32(0) var createdPorts []port -var portPair map[uint32](*port) +var portPair map[types.IPv4Address](*port) var schedState *scheduler -var vEach [10][burstSize]uint8 +var vEach [10][vBurstSize]uint8 +var devices map[string]int type Timer struct { t *time.Ticker @@ -92,7 +95,7 @@ type Func struct { vHandleFunction VectorHandleFunction vSeparateFunction VectorSeparateFunction vSplitFunction VectorSplitFunction - vFunc func([]*packet.Packet, *[burstSize]bool, *[burstSize]uint8, *Func, UserContext) + vFunc func([]*packet.Packet, *[vBurstSize]bool, *[vBurstSize]uint8, *Func, UserContext) next [](*Func) bufIndex uint @@ -115,7 +118,7 @@ type VectorGenerateFunction func([]*packet.Packet, UserContext) type HandleFunction func(*packet.Packet, UserContext) // VectorHandleFunction is a function type like HandleFunction for vector handling -type VectorHandleFunction func([]*packet.Packet, *[burstSize]bool, UserContext) +type VectorHandleFunction func([]*packet.Packet, *[vBurstSize]bool, UserContext) // SeparateFunction is a function type for user defined function which separates packets // based on some rule for two flows. Functions receives a packet from flow. @@ -124,7 +127,7 @@ type VectorHandleFunction func([]*packet.Packet, *[burstSize]bool, UserContext) type SeparateFunction func(*packet.Packet, UserContext) bool // VectorSeparateFunction is a function type like SeparateFunction for vector separation -type VectorSeparateFunction func([]*packet.Packet, *[burstSize]bool, *[burstSize]bool, UserContext) +type VectorSeparateFunction func([]*packet.Packet, *[vBurstSize]bool, *[vBurstSize]bool, UserContext) // SplitFunction is a function type for user defined function which splits packets // based in some rule for multiple flows. Function receives a packet from @@ -136,7 +139,7 @@ type VectorSeparateFunction func([]*packet.Packet, *[burstSize]bool, *[burstSize type SplitFunction func(*packet.Packet, UserContext) uint // VectorSplitFunction is a function type like SplitFunction for vector splitting -type VectorSplitFunction func([]*packet.Packet, *[burstSize]bool, *[burstSize]uint8, UserContext) +type VectorSplitFunction func([]*packet.Packet, *[vBurstSize]bool, *[vBurstSize]uint8, UserContext) // Kni is a high level struct of KNI device. The device itself is stored // in C memory in low.c and is defined by its port which is equal to port @@ -149,6 +152,7 @@ type receiveParameters struct { out low.Rings port *low.Port status []int32 + stats common.RXTXStats } func addReceiver(portId uint16, out low.Rings, inIndexNumber int32) { @@ -156,7 +160,20 @@ func addReceiver(portId uint16, out low.Rings, inIndexNumber int32) { par.port = low.GetPort(portId) par.out = out par.status = make([]int32, maxRecv, maxRecv) - schedState.addFF("receiver", nil, recvRSS, nil, par, nil, receiveRSS, inIndexNumber) + schedState.addFF("receiver", nil, recvRSS, nil, par, nil, receiveRSS, inIndexNumber, &par.stats) +} + +type receiveOSParameters struct { + out low.Rings + socket int + stats common.RXTXStats +} + +func addOSReceiver(socket int, out low.Rings) { + par := new(receiveOSParameters) + par.socket = socket + par.out = out + schedState.addFF("OS receiver", nil, recvOS, nil, par, nil, sendReceiveKNI, 0, &par.stats) } type KNIParameters struct { @@ -166,6 +183,7 @@ type KNIParameters struct { recv bool send bool linuxCore bool + stats common.RXTXStats } func addKNI(portId uint16, recv bool, out low.Rings, send bool, in low.Rings, inIndexNumber int32, name string, core bool) { @@ -177,9 +195,9 @@ func addKNI(portId uint16, recv bool, out low.Rings, send bool, in low.Rings, in par.send = send par.linuxCore = core if core { - schedState.addFF(name, nil, processKNI, nil, par, nil, comboKNI, inIndexNumber) + schedState.addFF(name, nil, processKNI, nil, par, nil, comboKNI, inIndexNumber, &par.stats) } else { - schedState.addFF(name, nil, processKNI, nil, par, nil, sendReceiveKNI, inIndexNumber) + schedState.addFF(name, nil, processKNI, nil, par, nil, sendReceiveKNI, inIndexNumber, &par.stats) } } @@ -188,7 +206,9 @@ type generateParameters struct { generateFunction GenerateFunction vectorGenerateFunction VectorGenerateFunction mempool *low.Mempool + targetChannel chan uint64 targetSpeed float64 + stats common.RXTXStats } func addGenerator(out low.Rings, generateFunction GenerateFunction, context UserContext) { @@ -197,18 +217,16 @@ func addGenerator(out low.Rings, generateFunction GenerateFunction, context User par.generateFunction = generateFunction ctx := make([]UserContext, 1, 1) ctx[0] = context - schedState.addFF("generator", nil, nil, pGenerate, par, &ctx, generate, 0) + schedState.addFF("generator", nil, nil, pGenerate, par, &ctx, generate, 0, &par.stats) } func addFastGenerator(out low.Rings, generateFunction GenerateFunction, - vectorGenerateFunction VectorGenerateFunction, targetSpeed uint64, context UserContext) error { + vectorGenerateFunction VectorGenerateFunction, targetSpeed uint64, context UserContext) (chan uint64, error) { fTargetSpeed := float64(targetSpeed) - if fTargetSpeed <= 0 { - return common.WrapWithNFError(nil, "Target speed value should be > 0", common.BadArgument) - } else if fTargetSpeed/(1000 /*milleseconds*/ /float64(schedTime)) < float64(burstSize) { + if fTargetSpeed/(1000 /*milleseconds*/ /float64(schedTime)) < float64(burstSize) { // TargetSpeed per schedTime should be more than burstSize because one burstSize packets in // one schedTime seconds are out minimal scheduling part. We can't make generate speed less than this. - return common.WrapWithNFError(nil, "Target speed per schedTime should be more than burstSize", common.BadArgument) + return nil, common.WrapWithNFError(nil, "Target speed per schedTime should be more than burstSize", common.BadArgument) } par := new(generateParameters) par.out = out @@ -216,16 +234,18 @@ func addFastGenerator(out low.Rings, generateFunction GenerateFunction, par.mempool = low.CreateMempool("fast generate") par.vectorGenerateFunction = vectorGenerateFunction par.targetSpeed = fTargetSpeed + par.targetChannel = make(chan uint64, 1) ctx := make([]UserContext, 1, 1) ctx[0] = context - schedState.addFF("fast generator", nil, nil, pFastGenerate, par, &ctx, fastGenerate, 0) - return nil + schedState.addFF("fast generator", nil, nil, pFastGenerate, par, &ctx, fastGenerate, 0, &par.stats) + return par.targetChannel, nil } type sendParameters struct { in low.Rings port uint16 anyway bool + stats common.RXTXStats } func addSender(port uint16, in low.Rings, inIndexNumber int32) { @@ -233,7 +253,20 @@ func addSender(port uint16, in low.Rings, inIndexNumber int32) { par.port = port par.in = in par.anyway = schedState.anyway - schedState.addFF("sender", nil, send, nil, par, nil, sendReceiveKNI, inIndexNumber) + schedState.addFF("sender", nil, send, nil, par, nil, sendReceiveKNI, inIndexNumber, &par.stats) +} + +type sendOSParameters struct { + in low.Rings + socket int + stats common.RXTXStats +} + +func addSenderOS(socket int, in low.Rings, inIndexNumber int32) { + par := new(sendOSParameters) + par.socket = socket + par.in = in + schedState.addFF("sender OS", nil, sendOS, nil, par, nil, sendReceiveKNI, inIndexNumber, &par.stats) } type copyParameters struct { @@ -249,7 +282,7 @@ func addCopier(in low.Rings, out low.Rings, outCopy low.Rings, inIndexNumber int par.out = out par.outCopy = outCopy par.mempool = low.CreateMempool("copy") - schedState.addFF("copy", nil, nil, pcopy, par, nil, segmentCopy, inIndexNumber) + schedState.addFF("copy", nil, nil, pcopy, par, nil, segmentCopy, inIndexNumber, nil) } func makePartitioner(N uint64, M uint64) *Func { @@ -297,19 +330,21 @@ func makeHandler(handleFunction HandleFunction, vectorHandleFunction VectorHandl type writeParameters struct { in low.Rings filename string + stats common.RXTXStats } func addWriter(filename string, in low.Rings, inIndexNumber int32) { par := new(writeParameters) par.in = in par.filename = filename - schedState.addFF("writer", write, nil, nil, par, nil, readWrite, inIndexNumber) + schedState.addFF("write", write, nil, nil, par, nil, readWrite, inIndexNumber, &par.stats) } type readParameters struct { out low.Rings filename string repcount int32 + stats common.RXTXStats } func addReader(filename string, out low.Rings, repcount int32) { @@ -317,7 +352,7 @@ func addReader(filename string, out low.Rings, repcount int32) { par.out = out par.filename = filename par.repcount = repcount - schedState.addFF("reader", read, nil, nil, par, nil, readWrite, 0) + schedState.addFF("read", read, nil, nil, par, nil, readWrite, 0, &par.stats) } func makeSlice(out low.Rings, segment *processSegment) *Func { @@ -346,7 +381,7 @@ func addSegment(in low.Rings, first *Func, inIndexNumber int32) *processSegment segment.contexts = make([](UserContext), 0, 0) par.out = &segment.out par.stype = &segment.stype - schedState.addFF("segment", nil, nil, segmentProcess, par, &segment.contexts, segmentCopy, inIndexNumber) + schedState.addFF("segment", nil, nil, segmentProcess, par, &segment.contexts, segmentCopy, inIndexNumber, nil) return segment } @@ -385,7 +420,15 @@ func SetUseHWCapability(capa HWCapability, use bool) { } } +// Size of operations with internal ring buffers and NIC receive/send +// Can be changed for debug and test purposes for scalar examples, not recommended +// At i40e drivers burstSize should be >= 4 +// http://mails.dpdk.org/archives/dev/2016-December/052554.html const burstSize = 32 + +// Size of all vectors in system. Can't be changed due to asm stickiness +// Using vector functions with vBurstSize != burstSize is undefined behaviour +const vBurstSize = 32 const reportMbits = false var sizeMultiplier uint @@ -399,7 +442,7 @@ type port struct { willKNI bool // will this port has assigned KNI device KNICoreIndex int port uint16 - MAC [common.EtherAddrLen]uint8 + MAC types.MACAddress InIndex int32 sendRings low.Rings } @@ -454,9 +497,31 @@ type Config struct { MaxRecv int // Limits parallel instances. 1 for one instance, 1000 for RSS count determine instances MaxInIndex int32 - // Scheduler should clone functions even if ti can lead to reordering. + // Scheduler should clone functions even if it can lead to reordering. // This option should be switch off for all high level reassembling like TCP or HTTP RestrictedCloning bool + // If application uses EncapsulateHead or DecapsulateHead functions L2 pointers + // should be reinit every receving or generating a packet. This can be removed if + // EncapsulateHead and DecapsulateHead are not in use + NoPacketHeadChange bool + // HTTP server address to use for serving statistics and + // telemetry. Server provides different types of statistics which + // can be controlled by statistics flags. File format is + // JSON. Registered roots return statistics for all framework + // graph nodes or accept an optional argument /ID where ID is port + // number for send and receive nodes. + // + // Following are possible statistics requests: + // + // /rxtxstats for protocol statistics gathered on all send and + // receive or /rxtxstats/name for individual send/receiver node. + // + // /telemetry for all nodes names and their counters which include + // received, send, processed, lost and dropped packets. Using + // /telemetry/name returns information about individual node. + // + // If no string is specified, no HTTP server is spawned. + StatsHTTPAddress *net.TCPAddr } // SystemInit is initialization of system. This function should be always called before graph construction. @@ -542,13 +607,18 @@ func SystemInit(args *Config) error { maxInIndex = args.MaxInIndex } + NoPacketHeadChange := false + if args.NoPacketHeadChange == true { + NoPacketHeadChange = true + } + argc, argv := low.InitDPDKArguments(args.DPDKArgs) // We want to add new clone if input ring is approximately 80% full maxPacketsToClone := uint32(sizeMultiplier * burstSize / 5 * 4) // TODO all low level initialization here! Now everything is default. // Init eal common.LogTitle(common.Initialization, "------------***-------- Initializing DPDK --------***------------") - if err := low.InitDPDK(argc, argv, burstSize, mbufNumber, mbufCacheSize, needKNI); err != nil { + if err := low.InitDPDK(argc, argv, burstSize, mbufNumber, mbufCacheSize, needKNI, NoPacketHeadChange); err != nil { return err } // Init Ports @@ -561,14 +631,27 @@ func SystemInit(args *Config) error { createdPorts[i].InIndex = maxInIndex } } - portPair = make(map[uint32](*port)) + portPair = make(map[types.IPv4Address](*port)) + devices = make(map[string]int) // Init scheduler common.LogTitle(common.Initialization, "------------***------ Initializing scheduler -----***------------") - StopRing := low.CreateRings(burstSize*sizeMultiplier, maxInIndex) + StopRing := low.CreateRings(burstSize*sizeMultiplier, maxInIndex /* Maximum possible rings */) common.LogDebug(common.Initialization, "Scheduler can use cores:", cpus) schedState = newScheduler(cpus, schedulerOff, schedulerOffRemove, stopDedicatedCore, StopRing, checkTime, debugTime, maxPacketsToClone, maxRecv, anyway) - // Init packet processing + + // Set HW offloading flag in packet package packet.SetHWTXChecksumFlag(hwtxchecksum) + + // Initialize telemetry web server + if countersEnabledInFramework { + if args.StatsHTTPAddress != nil { + if err = initCounters(args.StatsHTTPAddress); err != nil { + return err + } + } + } + + // Init packet processing for i := 0; i < 10; i++ { for j := 0; j < burstSize; j++ { vEach[i][j] = uint8(i) @@ -592,7 +675,7 @@ func SystemInitPortsAndMemory() error { } } createdPorts[i].MAC = GetPortMACAddress(createdPorts[i].port) - common.LogDebug(common.Initialization, "Port", createdPorts[i].port, "MAC address:", packet.MACToString(createdPorts[i].MAC)) + common.LogDebug(common.Initialization, "Port", createdPorts[i].port, "MAC address:", createdPorts[i].MAC.String()) } common.LogTitle(common.Initialization, "------------***------ Starting FlowFunctions -----***------------") // Init low performance mempool @@ -699,6 +782,42 @@ func SetReceiver(portId uint16) (OUT *Flow, err error) { return newFlow(rings, createdPorts[portId].InIndex), nil } +// SetReceiverOS adds function receive from Linux interface to flow graph. +// Gets name of device, will return error if can't initialize socket. +// Creates RAW socket, returns new opened flow with received packets. +func SetReceiverOS(device string) (*Flow, error) { + socketID, ok := devices[device] + if !ok { + socketID = low.InitDevice(device) + if socketID == -1 { + return nil, common.WrapWithNFError(nil, "Can't initialize socket", common.BadSocket) + } + devices[device] = socketID + } + rings := low.CreateRings(burstSize*sizeMultiplier, 1) + addOSReceiver(socketID, rings) + return newFlow(rings, 1), nil +} + +// SetSenderOS adds function send from flow graph to Linux interface. +// Gets name of device, will return error if can't initialize socket. +// Creates RAW socket, sends packets, closes input flow. +func SetSenderOS(IN *Flow, device string) error { + if err := checkFlow(IN); err != nil { + return err + } + socketID, ok := devices[device] + if !ok { + socketID = low.InitDevice(device) + if socketID == -1 { + return common.WrapWithNFError(nil, "Can't initialize socket", common.BadSocket) + } + devices[device] = socketID + } + addSenderOS(socketID, finishFlow(IN), IN.inIndexNumber) + return nil +} + // SetReceiverKNI adds function receive from KNI to flow graph. // Gets KNI device from which packets will be received. // Receive queue will be added to port automatically. @@ -725,26 +844,26 @@ func SetSenderReceiverKNI(IN *Flow, kni *Kni, linuxCore bool) (OUT *Flow, err er // SetFastGenerator adds clonable generate function to flow graph. // Gets user-defined generate function, target speed of generation user wants to achieve and context. -// Returns new open flow with generated packets. +// Returns new open flow with generated packets and channel that can be used for dynamically changing target speed // Function tries to achieve target speed by cloning. -func SetFastGenerator(f GenerateFunction, targetSpeed uint64, context UserContext) (OUT *Flow, err error) { +func SetFastGenerator(f GenerateFunction, targetSpeed uint64, context UserContext) (OUT *Flow, tc chan uint64, err error) { rings := low.CreateRings(burstSize*sizeMultiplier, 1) - if err := addFastGenerator(rings, f, nil, targetSpeed, context); err != nil { - return nil, err + if tc, err = addFastGenerator(rings, f, nil, targetSpeed, context); err != nil { + return nil, nil, err } - return newFlow(rings, 1), nil + return newFlow(rings, 1), tc, nil } // SetVectorFastGenerator adds clonable vector generate function to flow graph. // Gets user-defined vector generate function, target speed of generation user wants to achieve and context. -// Returns new open flow with generated packets. +// Returns new open flow with generated packets and channel that can be used for dynamically changing target speed // Function tries to achieve target speed by cloning. -func SetVectorFastGenerator(f VectorGenerateFunction, targetSpeed uint64, context UserContext) (OUT *Flow, err error) { +func SetVectorFastGenerator(f VectorGenerateFunction, targetSpeed uint64, context UserContext) (OUT *Flow, tc chan uint64, err error) { rings := low.CreateRings(burstSize*sizeMultiplier, 1) - if err := addFastGenerator(rings, nil, f, targetSpeed, context); err != nil { - return nil, err + if tc, err = addFastGenerator(rings, nil, f, targetSpeed, context); err != nil { + return nil, nil, err } - return newFlow(rings, 1), nil + return newFlow(rings, 1), tc, nil } // SetGenerator adds non-clonable generate flow function to flow graph. @@ -1004,7 +1123,7 @@ func mergeOneFlow(IN *Flow, rings low.Rings) { } // GetPortMACAddress returns default MAC address of an Ethernet port. -func GetPortMACAddress(port uint16) [common.EtherAddrLen]uint8 { +func GetPortMACAddress(port uint16) [types.EtherAddrLen]uint8 { return low.GetPortMACAddress(port) } @@ -1029,7 +1148,7 @@ func GetNameByPort(port uint16) (string, error) { // SetIPForPort sets IP for specified port if it was created. Not thread safe. // Return error if requested port isn't exist or wasn't previously requested. -func SetIPForPort(port uint16, ip uint32) error { +func SetIPForPort(port uint16, ip types.IPv4Address) error { for i := range createdPorts { if createdPorts[i].port == port && createdPorts[i].wasRequested { portPair[ip] = &createdPorts[i] @@ -1131,11 +1250,11 @@ func segmentProcess(parameters interface{}, inIndex []int32, stopper [2]chan int tempPackets := make([]*packet.Packet, burstSize) type pair struct { f *Func - mask [burstSize]bool + mask [vBurstSize]bool } def := make([]pair, 30, 30) - var currentMask [burstSize]bool - var answers [burstSize]uint8 + var currentMask [vBurstSize]bool + var answers [vBurstSize]uint8 tick := time.NewTicker(time.Duration(schedTime) * time.Millisecond) stopper[1] <- 2 // Answer that function is ready @@ -1177,6 +1296,7 @@ func segmentProcess(parameters interface{}, inIndex []int32, stopper [2]chan int currentState.ZeroAttempts[q-1]++ continue } + if scalar { // Scalar code for i := uint(0); i < n; i++ { currentFunc := firstFunc @@ -1257,7 +1377,12 @@ func recvRSS(parameters interface{}, inIndex []int32, flag *int32, coreID int) { i-- } } - low.ReceiveRSS(uint16(srp.port.PortId), inIndex, srp.out, flag, coreID, &srp.status[index]) + low.ReceiveRSS(uint16(srp.port.PortId), inIndex, srp.out, flag, coreID, &srp.status[index], &srp.stats) +} + +func recvOS(parameters interface{}, inIndex []int32, flag *int32, coreID int) { + srp := parameters.(*receiveOSParameters) + low.ReceiveOS(srp.socket, srp.out[0], flag, coreID, &srp.stats) } func processKNI(parameters interface{}, inIndex []int32, flag *int32, coreID int) { @@ -1265,7 +1390,7 @@ func processKNI(parameters interface{}, inIndex []int32, flag *int32, coreID int if srk.linuxCore == true { coreID = schedState.cores[createdPorts[srk.port.PortId].KNICoreIndex].id } - low.SrKNI(uint16(srk.port.PortId), flag, coreID, srk.recv, srk.out, srk.send, srk.in) + low.SrKNI(uint16(srk.port.PortId), flag, coreID, srk.recv, srk.out, srk.send, srk.in, &srk.stats) } func pGenerate(parameters interface{}, inIndex []int32, stopper [2]chan int, report chan reportPair, context []UserContext) { @@ -1290,6 +1415,10 @@ func pGenerate(parameters interface{}, inIndex []int32, stopper [2]chan int, rep } generateFunction(tempPacket, context[0]) safeEnqueueOne(OUT[0], tempPacket.ToUintptr()) + + if countersEnabledInApplication { + updatePortStatsOne(&gp.stats, tempPacket) + } } } } @@ -1337,7 +1466,7 @@ func pFastGenerate(parameters interface{}, inIndex []int32, stopper [2]chan int, } if vector == false { for i := range bufs { - // TODO Maybe we need to prefetcht here? + // TODO Maybe we need to prefetch here? tempPacket = packet.ExtractPacket(bufs[i]) generateFunction(tempPacket, context[0]) if reportMbits { @@ -1350,6 +1479,9 @@ func pFastGenerate(parameters interface{}, inIndex []int32, stopper [2]chan int, } safeEnqueue(OUT[0], bufs, burstSize) currentState.V.Packets += uint64(burstSize) + if countersEnabledInApplication { + updatePortStats(&gp.stats, bufs, burstSize) + } // GO parks goroutines while Sleep. So Sleep lasts more time than our precision // we just want to slow goroutine down without parking, so loop is OK for this. // time.Now lasts approximately 70ns and this satisfies us @@ -1399,12 +1531,13 @@ func pcopy(parameters interface{}, inIndex []int32, stopper [2]chan int, report default: for q := int32(1); q < inIndex[0]+1; q++ { n := IN[inIndex[q]].DequeueBurst(bufs1, burstSize) + if n != 0 { if err := low.AllocateMbufs(bufs2, mempool, n); err != nil { common.LogFatal(common.Debug, err) } for i := uint(0); i < n; i++ { - // TODO Maybe we need to prefetcht here? + // TODO Maybe we need to prefetch here? tempPacket1 = packet.ExtractPacket(bufs1[i]) tempPacket2 = packet.ExtractPacket(bufs2[i]) packet.GeneratePacketFromByte(tempPacket2, tempPacket1.GetRawPacketBytes()) @@ -1433,7 +1566,12 @@ func pcopy(parameters interface{}, inIndex []int32, stopper [2]chan int, report func send(parameters interface{}, inIndex []int32, flag *int32, coreID int) { srp := parameters.(*sendParameters) - low.Send(srp.port, srp.in, srp.anyway, flag, coreID) + low.Send(srp.port, srp.in, srp.anyway, flag, coreID, &srp.stats) +} + +func sendOS(parameters interface{}, inIndex []int32, flag *int32, coreID int) { + srp := parameters.(*sendOSParameters) + low.SendOS(srp.socket, srp.in, flag, coreID, &srp.stats) } func merge(from low.Rings, to low.Rings) { @@ -1482,7 +1620,7 @@ func separate(packet *packet.Packet, sc *Func, ctx UserContext) uint { return uint(low.BoolToInt(sc.sSeparateFunction(packet, ctx))) } -func vSeparate(packets []*packet.Packet, mask *[burstSize]bool, answers *[burstSize]uint8, ve *Func, ctx UserContext) { +func vSeparate(packets []*packet.Packet, mask *[vBurstSize]bool, answers *[vBurstSize]uint8, ve *Func, ctx UserContext) { ve.vSeparateFunction(packets, mask, low.IntArrayToBool(answers), ctx) } @@ -1499,7 +1637,7 @@ func partition(packet *packet.Packet, sc *Func, ctx UserContext) uint { return uint(context.currentAnswer) } -func vPartition(packets []*packet.Packet, mask *[burstSize]bool, answers *[burstSize]uint8, ve *Func, ctx UserContext) { +func vPartition(packets []*packet.Packet, mask *[vBurstSize]bool, answers *[vBurstSize]uint8, ve *Func, ctx UserContext) { context := ctx.(*partitionCtx) for i := 0; i < burstSize; i++ { if (*mask)[i] { @@ -1518,7 +1656,7 @@ func split(packet *packet.Packet, sc *Func, ctx UserContext) uint { return sc.sSplitFunction(packet, ctx) } -func vSplit(packets []*packet.Packet, mask *[burstSize]bool, answers *[burstSize]uint8, ve *Func, ctx UserContext) { +func vSplit(packets []*packet.Packet, mask *[vBurstSize]bool, answers *[vBurstSize]uint8, ve *Func, ctx UserContext) { ve.vSplitFunction(packets, mask, answers, ctx) } @@ -1527,7 +1665,7 @@ func handle(packet *packet.Packet, sc *Func, ctx UserContext) uint { return 0 } -func vHandle(packets []*packet.Packet, mask *[burstSize]bool, answers *[burstSize]uint8, ve *Func, ctx UserContext) { +func vHandle(packets []*packet.Packet, mask *[vBurstSize]bool, answers *[vBurstSize]uint8, ve *Func, ctx UserContext) { ve.vHandleFunction(packets, mask, ctx) } @@ -1535,7 +1673,7 @@ func constructSlice(packet *packet.Packet, sc *Func, ctx UserContext) uint { return sc.bufIndex } -func vConstructSlice(packets []*packet.Packet, mask *[burstSize]bool, answers *[burstSize]uint8, ve *Func, ctx UserContext) { +func vConstructSlice(packets []*packet.Packet, mask *[vBurstSize]bool, answers *[vBurstSize]uint8, ve *Func, ctx UserContext) { answers[0] = uint8(ve.bufIndex) } @@ -1566,6 +1704,11 @@ func write(parameters interface{}, inIndex []int32, stopper [2]chan int) { default: for q := int32(0); q < inIndex[0]; q++ { n := IN[q].DequeueBurst(bufIn, 1) + + if countersEnabledInApplication { + updatePortStats(&wp.stats, bufIn, n) + } + if n == 0 { continue } @@ -1632,6 +1775,10 @@ func read(parameters interface{}, inIndex []int32, stopper [2]chan int) { // TODO we need packet reassembly here. However we don't // use mbuf packet_type here, so it is impossible. safeEnqueueOne(OUT[0], tempPacket.ToUintptr()) + + if countersEnabledInApplication { + updatePortStatsOne(&rp.stats, tempPacket) + } } } } @@ -1699,9 +1846,9 @@ func CreateKniDevice(portId uint16, name string) (*Kni, error) { } } -func FillSliceFromMask(input []uintptr, mask *[burstSize]bool, output []uintptr) uint8 { +func FillSliceFromMask(input []uintptr, mask *[vBurstSize]bool, output []uintptr) uint8 { count := 0 - for i := 0; i < burstSize; i++ { + for i := 0; i < vBurstSize; i++ { if (*mask)[i] != false { output[count] = input[i] count++ @@ -1757,3 +1904,40 @@ func CheckFatal(err error) { common.LogFatalf(common.No, "failed with message: %s\n", err.Error()) } } + +func updatePortStatsOne(stats *common.RXTXStats, pkt *packet.Packet) { + if useInterlockedCounters { + atomic.AddUint64(&stats.PacketsProcessed, 1) + if analyzePacketSizes { + atomic.AddUint64(&stats.BytesProcessed, uint64(pkt.GetPacketLen())) + } + } else { + stats.PacketsProcessed++ + if analyzePacketSizes { + stats.BytesProcessed += uint64(pkt.GetPacketLen()) + } + } +} + +func updatePortStats(stats *common.RXTXStats, packetPtrs []uintptr, number uint) { + if useInterlockedCounters { + atomic.AddUint64(&stats.PacketsProcessed, uint64(number)) + if analyzePacketSizes { + atomic.AddUint64(&stats.BytesProcessed, calculateSize(packetPtrs, number)) + } + } else { + stats.PacketsProcessed += uint64(number) + if analyzePacketSizes { + stats.BytesProcessed += calculateSize(packetPtrs, number) + } + } +} + +func calculateSize(packetPtrs []uintptr, number uint) uint64 { + size := uint64(0) + for i := uint(0); i < number; i++ { + tempPacket := packet.ExtractPacket(packetPtrs[i]) + size += uint64(tempPacket.GetPacketLen()) + } + return size +} diff --git a/flow/predefined.go b/flow/predefined.go index 3eea7da1..621dfd57 100644 --- a/flow/predefined.go +++ b/flow/predefined.go @@ -9,6 +9,7 @@ package flow import ( "github.com/intel-go/nff-go/common" "github.com/intel-go/nff-go/packet" + "github.com/intel-go/nff-go/types" ) func handleARPICMPRequests(current *packet.Packet, context UserContext) bool { @@ -17,11 +18,11 @@ func handleARPICMPRequests(current *packet.Packet, context UserContext) bool { // ARP can be only in IPv4. IPv6 replace it with modified ICMP if arp != nil { if packet.SwapBytesUint16(arp.Operation) != packet.ARPRequest || - arp.THA != [common.EtherAddrLen]byte{} { + arp.THA != [types.EtherAddrLen]byte{} { return false } - port := portPair[packet.ArrayToIPv4(arp.TPA)] + port := portPair[types.ArrayToIPv4(arp.TPA)] if port == nil { return false } @@ -31,7 +32,7 @@ func handleARPICMPRequests(current *packet.Packet, context UserContext) bool { if err != nil { common.LogFatal(common.Debug, err) } - packet.InitARPReplyPacket(answerPacket, port.MAC, arp.SHA, packet.ArrayToIPv4(arp.TPA), packet.ArrayToIPv4(arp.SPA)) + packet.InitARPReplyPacket(answerPacket, port.MAC, arp.SHA, types.ArrayToIPv4(arp.TPA), types.ArrayToIPv4(arp.SPA)) answerPacket.SendPacket(port.port) return false @@ -42,7 +43,7 @@ func handleARPICMPRequests(current *packet.Packet, context UserContext) bool { icmp := current.GetICMPForIPv4() if icmp != nil { // Check that received ICMP packet is echo request packet. - if icmp.Type != common.ICMPTypeEchoRequest || icmp.Code != 0 { + if icmp.Type != types.ICMPTypeEchoRequest || icmp.Code != 0 { return true } @@ -65,9 +66,9 @@ func handleARPICMPRequests(current *packet.Packet, context UserContext) bool { (answerPacket.GetIPv4NoCheck()).DstAddr = ipv4.SrcAddr (answerPacket.GetIPv4NoCheck()).SrcAddr = ipv4.DstAddr answerPacket.ParseL4ForIPv4() - (answerPacket.GetICMPNoCheck()).Type = common.ICMPTypeEchoResponse + (answerPacket.GetICMPNoCheck()).Type = types.ICMPTypeEchoResponse ipv4.HdrChecksum = packet.SwapBytesUint16(packet.CalculateIPv4Checksum(ipv4)) - answerPacket.ParseL7(common.ICMPNumber) + answerPacket.ParseL7(types.ICMPNumber) icmp.Cksum = packet.SwapBytesUint16(packet.CalculateIPv4ICMPChecksum(ipv4, icmp, answerPacket.Data)) answerPacket.SendPacket(port.port) diff --git a/flow/scheduler.go b/flow/scheduler.go index 5b4a7a05..50382311 100644 --- a/flow/scheduler.go +++ b/flow/scheduler.go @@ -128,14 +128,14 @@ type flowFunction struct { // Adding every flow function to scheduler list func (scheduler *scheduler) addFF(name string, ucfn uncloneFlowFunction, Cfn cFlowFunction, cfn cloneFlowFunction, - par interface{}, context *[]UserContext, fType ffType, inIndexNumber int32) { + par interface{}, context *[]UserContext, fType ffType, inIndexNumber int32, rxtxstats *common.RXTXStats) { ff := new(flowFunction) nameC := 1 - tName := name + tName := name + strconv.Itoa(nameC) for i := range scheduler.ff { if scheduler.ff[i].name == tName { - tName = name + strconv.Itoa(nameC) nameC++ + tName = name + strconv.Itoa(nameC) } } ff.name = tName @@ -150,6 +150,9 @@ func (scheduler *scheduler) addFF(name string, ucfn uncloneFlowFunction, Cfn cFl scheduler.maxInIndex = inIndexNumber } scheduler.ff = append(scheduler.ff, ff) + if countersEnabledInFramework && rxtxstats != nil { + registerRXTXStatitics(rxtxstats, tName) + } } type scheduler struct { @@ -221,8 +224,13 @@ func (scheduler *scheduler) systemStart() (err error) { } else { common.LogDebug(common.Initialization, "Start STOP at scheduler", core, "core") } + var stopstats *common.RXTXStats + if countersEnabledInFramework { + stopstats = new(common.RXTXStats) + registerRXTXStatitics(stopstats, "systemstop") + } go func() { - low.Stop(scheduler.StopRing, &scheduler.stopFlag, core) + low.Stop(scheduler.StopRing, &scheduler.stopFlag, core, stopstats) }() for i := range scheduler.ff { if err = scheduler.ff[i].startNewInstance(constructNewIndex(scheduler.ff[i].inIndexNumber), scheduler); err != nil { @@ -415,6 +423,19 @@ func (scheduler *scheduler) schedule(schedTime uint) { if ff.fType == segmentCopy || ff.fType == fastGenerate { ff.updateReportedState() // TODO also for debug } + if ff.fType == fastGenerate { + select { + case temp := <-(ff.Parameters.(*generateParameters)).targetChannel: + if float64(temp)/(1000 /*milliseconds*/ /float64(schedTime)) < float64(burstSize) { + // TargetSpeed per schedTime should be more than burstSize because one burstSize packets in + // one schedTime seconds are out minimal scheduling part. We can't make generate speed less than this. + common.LogWarning(common.Debug, "Target speed per schedTime should be more than burstSize - not changing") + } else { + (ff.Parameters.(*generateParameters)).targetSpeed = float64(temp) + } + default: + } + } // Firstly we check removing clones. We can remove one clone if: // 1. flow function has clones or it is fastGenerate // 2. scheduler removing is switched on diff --git a/go.sum b/go.sum index 1f307a5f..b30ef8f8 100644 --- a/go.sum +++ b/go.sum @@ -42,6 +42,7 @@ golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3 h1:ulvT7fqt0yHWzpJwI57MezWnY golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/sys v0.0.0-20181004145325-8469e314837c h1:SJ7JoQNVl3mC7EWkkONgBWgCno8LcABIJwFMkWBC+EY= golang.org/x/sys v0.0.0-20181004145325-8469e314837c/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190204203706-41f3e6584952 h1:FDfvYgoVsA7TTZSbgiqjAbfPbK47CNHdWl3h/PJtii0= golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/tools v0.0.0-20181002223833-cd09f19c2f7e h1:x8cnE8uLkl6ATwMpvL/N/wYBk/53BdeePq1JaYt1zuo= golang.org/x/tools v0.0.0-20181002223833-cd09f19c2f7e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/low/low.go b/low/low.go index 078937cb..e8e5748a 100644 --- a/low/low.go +++ b/low/low.go @@ -28,6 +28,13 @@ import ( "github.com/intel-go/nff-go/asm" "github.com/intel-go/nff-go/common" + "github.com/intel-go/nff-go/types" +) + +var ( + CountersEnabledInFramework bool = bool(C.counters_enabled_in_framework) + UseInterlockedCounters bool = bool(C.use_interlocked_counters) + AnalyzePacketSizes bool = bool(C.analyze_packet_sizes) ) var ringName = 1 @@ -76,8 +83,8 @@ func CheckRSSPacketCount(p *Port, queue int16) int64 { } // GetPortMACAddress gets MAC address of given port. -func GetPortMACAddress(port uint16) [common.EtherAddrLen]uint8 { - var mac [common.EtherAddrLen]uint8 +func GetPortMACAddress(port uint16) [types.EtherAddrLen]uint8 { + var mac [types.EtherAddrLen]uint8 var cmac C.struct_ether_addr C.rte_eth_macaddr_get(C.uint16_t(port), &cmac) @@ -489,15 +496,15 @@ func (ring *Ring) GetRingCount() uint32 { } // ReceiveRSS - get packets from port and enqueue on a Ring. -func ReceiveRSS(port uint16, inIndex []int32, OUT Rings, flag *int32, coreID int, race *int32) { +func ReceiveRSS(port uint16, inIndex []int32, OUT Rings, flag *int32, coreID int, race *int32, stats *common.RXTXStats) { if C.rte_eth_dev_socket_id(C.uint16_t(port)) != C.int(C.rte_lcore_to_socket_id(C.uint(coreID))) { common.LogWarning(common.Initialization, "Receive port", port, "is on remote NUMA node to polling thread - not optimal performance.") } C.receiveRSS(C.uint16_t(port), (*C.int32_t)(unsafe.Pointer(&(inIndex[0]))), C.extractDPDKRings((**C.struct_nff_go_ring)(unsafe.Pointer(&(OUT[0]))), C.int32_t(len(OUT))), - (*C.int)(unsafe.Pointer(flag)), C.int(coreID), (*C.int)(unsafe.Pointer(race))) + (*C.int)(unsafe.Pointer(flag)), C.int(coreID), (*C.int)(unsafe.Pointer(race)), (*C.RXTXStats)(unsafe.Pointer(stats))) } -func SrKNI(port uint16, flag *int32, coreID int, recv bool, OUT Rings, send bool, IN Rings) { +func SrKNI(port uint16, flag *int32, coreID int, recv bool, OUT Rings, send bool, IN Rings, stats *common.RXTXStats) { var nOut *C.struct_rte_ring var nIn **C.struct_rte_ring if OUT != nil { @@ -507,20 +514,21 @@ func SrKNI(port uint16, flag *int32, coreID int, recv bool, OUT Rings, send bool nIn = C.extractDPDKRings((**C.struct_nff_go_ring)(unsafe.Pointer(&(IN[0]))), C.int32_t(len(IN))) } C.nff_go_KNI(C.uint16_t(port), (*C.int)(unsafe.Pointer(flag)), C.int(coreID), - C.bool(recv), nOut, C.bool(send), nIn, C.int32_t(len(IN))) + C.bool(recv), nOut, C.bool(send), nIn, C.int32_t(len(IN)), (*C.RXTXStats)(unsafe.Pointer(stats))) } // Send - dequeue packets and send. -func Send(port uint16, IN Rings, anyway bool, flag *int32, coreID int) { +func Send(port uint16, IN Rings, anyway bool, flag *int32, coreID int, stats *common.RXTXStats) { if C.rte_eth_dev_socket_id(C.uint16_t(port)) != C.int(C.rte_lcore_to_socket_id(C.uint(coreID))) { common.LogWarning(common.Initialization, "Send port", port, "is on remote NUMA node to polling thread - not optimal performance.") } - C.nff_go_send(C.uint16_t(port), C.extractDPDKRings((**C.struct_nff_go_ring)(unsafe.Pointer(&(IN[0]))), C.int32_t(len(IN))), C.int32_t(len(IN)), C.bool(anyway), (*C.int)(unsafe.Pointer(flag)), C.int(coreID)) + C.nff_go_send(C.uint16_t(port), C.extractDPDKRings((**C.struct_nff_go_ring)(unsafe.Pointer(&(IN[0]))), C.int32_t(len(IN))), C.int32_t(len(IN)), + C.bool(anyway), (*C.int)(unsafe.Pointer(flag)), C.int(coreID), (*C.RXTXStats)(unsafe.Pointer(stats))) } // Stop - dequeue and free packets. -func Stop(IN Rings, flag *int32, coreID int) { - C.nff_go_stop(C.extractDPDKRings((**C.struct_nff_go_ring)(unsafe.Pointer(&(IN[0]))), C.int32_t(len(IN))), C.int(len(IN)), (*C.int)(unsafe.Pointer(flag)), C.int(coreID)) +func Stop(IN Rings, flag *int32, coreID int, stats *common.RXTXStats) { + C.nff_go_stop(C.extractDPDKRings((**C.struct_nff_go_ring)(unsafe.Pointer(&(IN[0]))), C.int32_t(len(IN))), C.int(len(IN)), (*C.int)(unsafe.Pointer(flag)), C.int(coreID), (*C.RXTXStats)(unsafe.Pointer(stats))) } // InitDPDKArguments allocates and initializes arguments for dpdk. @@ -537,8 +545,8 @@ func InitDPDKArguments(args []string) (C.int, **C.char) { } // InitDPDK initializes the Environment Abstraction Layer (EAL) in DPDK. -func InitDPDK(argc C.int, argv **C.char, burstSize uint, mbufNumber uint, mbufCacheSize uint, needKNI int) error { - ret := C.eal_init(argc, argv, C.uint32_t(burstSize), C.int32_t(needKNI)) +func InitDPDK(argc C.int, argv **C.char, burstSize uint, mbufNumber uint, mbufCacheSize uint, needKNI int, NoPacketHeadChange bool) error { + ret := C.eal_init(argc, argv, C.uint32_t(burstSize), C.int32_t(needKNI), C.bool(NoPacketHeadChange)) if ret < 0 { return common.WrapWithNFError(nil, "Error with EAL initialization\n", common.FailToInitDPDK) } @@ -657,7 +665,7 @@ func AllocateMbuf(mb *uintptr, mempool *Mempool) error { // WriteDataToMbuf copies data to mbuf. func WriteDataToMbuf(mb *Mbuf, data []byte) { d := unsafe.Pointer(GetPacketDataStartPointer(mb)) - slice := (*[common.MaxLength]byte)(d)[:len(data)] // copy requires slice + slice := (*[types.MaxLength]byte)(d)[:len(data)] // copy requires slice //TODO need to investigate maybe we need to use C function C.rte_memcpy here copy(slice, data) } @@ -718,12 +726,12 @@ func CreateLPM(name string, socket uint8, maxRules uint32, numberTbl8 uint32, tb } // AddLPMRule adds one rule to LPM table -func AddLPMRule(lpm unsafe.Pointer, ip uint32, depth uint8, nextHop uint32) int { +func AddLPMRule(lpm unsafe.Pointer, ip types.IPv4Address, depth uint8, nextHop types.IPv4Address) int { return int(C.lpm_add(lpm, C.uint32_t(ip), C.uint8_t(depth), C.uint32_t(nextHop))) } // DeleteLPMRule removes one rule from LPM table -func DeleteLPMRule(lpm unsafe.Pointer, ip uint32, depth uint8) int { +func DeleteLPMRule(lpm unsafe.Pointer, ip types.IPv4Address, depth uint8) int { return int(C.lpm_delete(lpm, C.uint32_t(ip), C.uint8_t(depth))) } @@ -743,3 +751,23 @@ func IntArrayToBool(value *[32]uint8) *[32]bool { func CheckHWTXChecksumCapability(port uint16) bool { return bool(C.check_hwtxchecksum_capability(C.uint16_t(port))) } + +func ReceiveOS(socket int, OUT *Ring, flag *int32, coreID int, stats *common.RXTXStats) { + m := CreateMempool("receiveOS") + C.receiveOS(C.int(socket), OUT.DPDK_ring, (*C.struct_rte_mempool)(unsafe.Pointer(m)), + (*C.int)(unsafe.Pointer(flag)), C.int(coreID), (*C.RXTXStats)(unsafe.Pointer(stats))) +} + +func SendOS(socket int, IN Rings, flag *int32, coreID int, stats *common.RXTXStats) { + C.sendOS(C.int(socket), C.extractDPDKRings((**C.struct_nff_go_ring)(unsafe.Pointer(&(IN[0]))), + C.int32_t(len(IN))), C.int32_t(len(IN)), (*C.int)(unsafe.Pointer(flag)), C.int(coreID), + (*C.RXTXStats)(unsafe.Pointer(stats))) +} + +func InitDevice(device string) int { + return int(C.initDevice(C.CString(device))) +} + +func SetCountersEnabledInApplication(enabled bool) { + C.counters_enabled_in_application = C.bool(true) +} diff --git a/low/low.h b/low/low.h index 6bbf350f..9f08ee98 100644 --- a/low/low.h +++ b/low/low.h @@ -19,6 +19,16 @@ #include #include +#include +#include // ETH_P_ALL +#include // snprintf +#include // memset +#include // malloc +#include // htons +#include // ioctl +#include // ifreq +#include // sockaddr_ll + #define process 1 #define stopRequest 2 #define wasStopped 9 @@ -35,6 +45,9 @@ // #define DEBUG // #define REASSEMBLY +#define COUNTERS_ENABLED +#define USE_INTERLOCKED_COUNTERS +#define ANALYZE_PACKETS_SIZES #define recvNotUsed 0 #define recvDone 2 @@ -46,8 +59,11 @@ // 24 offset is L2 offset and is always begining of packet // 32 offset is CMbuf offset and is initilized when mempool is created // 40 offset is Next field. Should be 0. Will be filled later if required -#define mbufInit(buf) \ -*(char **)((char *)(buf) + mbufStructSize + 24) = (char *)(buf) + defaultStart; \ +#define mbufInitL2(buf) \ +*(char **)((char *)(buf) + mbufStructSize + 24) = (char *)(buf) + defaultStart; +#define mbufInitCMbuf(buf) \ +*(char **)((char *)(buf) + mbufStructSize + 32) = (char *)(buf); +#define mbufInitNextChain(buf) \ *(char **)((char *)(buf) + mbufStructSize + 40) = 0; #ifdef REASSEMBLY @@ -56,27 +72,85 @@ struct rte_ip_frag_death_row death_row; \ death_row.cnt = 0; /* DPDK doesn't initialize this field. It is probably a bug. */ \ struct rte_ip_frag_death_row* pdeath_row = &death_row; -#else -#define REASSEMBLY_INIT \ - struct rte_ip_frag_tbl* tbl = NULL; \ - struct rte_ip_frag_death_row* pdeath_row = NULL; -#endif // Firstly we set "next" packet pointer (+40) to the packet from next mbuf // Secondly we know that followed mbufs don't contain L2 and L3 headers. We assume that they start with a data // so we assume that Data packet field (+16) should be equal to Ether packet field (+24) // (which we parse at this packet segment receiving #define mbufSetNext(buf) \ -*(char **)((char *)(buf) + mbufStructSize + 40) = (char *)(buf->next) + mbufStructSize; \ -*(char **)((char *)(buf->next) + mbufStructSize + 16) = *(char **)((char *)(buf->next) + mbufStructSize + 24) + *(char **)((char *)(buf) + mbufStructSize + 40) = (char *)(buf->next) + mbufStructSize; \ + *(char **)((char *)(buf->next) + mbufStructSize + 16) = *(char **)((char *)(buf->next) + mbufStructSize + 24) +#else +#define REASSEMBLY_INIT \ + struct rte_ip_frag_tbl* tbl = NULL; \ + struct rte_ip_frag_death_row* pdeath_row = NULL; +#endif + +#ifdef COUNTERS_ENABLED +#ifdef USE_INTERLOCKED_COUNTERS +#define UPDATE_PACKETS(packets, dropped) \ + __sync_fetch_and_add(&stats->PacketsProcessed, (packets)); \ + __sync_fetch_and_add(&stats->PacketsDropped, (dropped)); +#ifdef ANALYZE_PACKETS_SIZES +#define UPDATE_BYTES(bytes) \ + __sync_fetch_and_add(&stats->BytesProcessed, (bytes)); +#else // ANALYZE_PACKETS_SIZES +#define UPDATE_BYTES(bytes) \ + do {} while (0) +#endif // ANALYZE_PACKETS_SIZES +#else // USE_INTERLOCKED_COUNTERS +#define UPDATE_PACKETS(packets, dropped) \ + stats->PacketsProcessed += (packets); \ + stats->PacketsDropped += (dropped); +#ifdef ANALYZE_PACKETS_SIZES +#define UPDATE_BYTES(bytes) \ + stats->BytesProcessed += (bytes); +#else // ANALYZE_PACKETS_SIZES +#define UPDATE_BYTES(bytes) \ + do {} while (0) +#endif // ANALYZE_PACKETS_SIZES +#endif // USE_INTERLOCKED_COUNTERS +#define UPDATE_COUNTERS(packets, bytes, dropped) \ + if (counters_enabled_in_application) { \ + UPDATE_PACKETS(packets, dropped) \ + UPDATE_BYTES(bytes) \ + } +#else // COUNTERS_ENABLED +#define UPDATE_COUNTERS(packets, bytes, dropped) \ + do {} while (0) +#endif // COUNTERS_ENABLED + +#ifdef COUNTERS_ENABLED +bool counters_enabled_in_framework = true; +bool counters_enabled_in_application = false; +#else +bool counters_enabled_in_framework = false; +#endif + +#ifdef USE_INTERLOCKED_COUNTERS +bool use_interlocked_counters = true; +#else +bool use_interlocked_counters = false; +#endif + +#ifdef ANALYZE_PACKETS_SIZES +bool analyze_packet_sizes = true; +#else +bool analyze_packet_sizes = false; +#endif long receive_received = 0, receive_pushed = 0; long send_required = 0, send_sent = 0; long stop_freed = 0; +typedef struct { + uint64_t PacketsProcessed, PacketsDropped, BytesProcessed; +} RXTXStats; + int mbufStructSize; int headroomSize; int defaultStart; +bool L2CanBeChanged; char mempoolName[9] = "mempool1\0"; __m128 zero128 = {0, 0, 0, 0}; @@ -255,6 +329,17 @@ int port_init(uint16_t port, bool willReceive, struct rte_mempool **mbuf_pools, return 0; } +#if defined(COUNTERS_ENABLED) && defined(ANALYZE_PACKETS_SIZES) +__attribute__((always_inline)) +static inline uint64_t calculateSize(struct rte_mbuf *bufs[BURST_SIZE], uint16_t number) { + uint64_t size = 0; + for (uint32_t i = 0; i < number; i++) { + size += bufs[i]->pkt_len; + } + return size; +} +#endif + __attribute__((always_inline)) static inline void handleUnpushed(struct rte_mbuf *bufs[BURST_SIZE], uint16_t real_number, uint16_t required_number) { if (unlikely(real_number < required_number)) { @@ -266,15 +351,23 @@ static inline void handleUnpushed(struct rte_mbuf *bufs[BURST_SIZE], uint16_t re __attribute__((always_inline)) static inline uint16_t handleReceived(struct rte_mbuf *bufs[BURST_SIZE], uint16_t rx_pkts_number, struct rte_ip_frag_tbl* tbl, struct rte_ip_frag_death_row* death_row) { -#ifdef REASSEMBLY +#ifndef REASSEMBLY + if (L2CanBeChanged == true) { + for (uint16_t i = 0; i < rx_pkts_number; i++) { + // TODO prefetch + mbufInitL2(bufs[i]); + } + } +#else uint16_t temp_number = 0; uint64_t cur_tsc = rte_rdtsc(); -#endif for (uint16_t i = 0; i < rx_pkts_number; i++) { // Prefetch decreases speed here without reassembly and increases with reassembly. // Speed of this is highly influenced by size of mempool. It seems that due to caches. - mbufInit(bufs[i]); -#ifdef REASSEMBLY + if (L2CanBeChanged == true) { + mbufInitL2(bufs[i]); + } + mbufInitNextChain(bufs[i]); // TODO prefetch will give 8-10% performance in reassembly case. // However we need additional investigations about small (< 3) packet numbers. //rte_prefetch0(rte_pktmbuf_mtod(bufs[i + 3] /*PREFETCH_OFFSET*/, void *)); @@ -289,9 +382,7 @@ static inline uint16_t handleReceived(struct rte_mbuf *bufs[BURST_SIZE], uint16_ } bufs[temp_number] = bufs[i]; temp_number++; -#endif } -#ifdef REASSEMBLY rx_pkts_number = temp_number; rte_ip_frag_free_death_row(death_row, 0 /* PREFETCH_OFFSET */); #endif @@ -343,7 +434,7 @@ static inline struct rte_mbuf* reassemble(struct rte_ip_frag_tbl* tbl, struct rt return buf; } -void receiveRSS(uint16_t port, volatile int32_t *inIndex, struct rte_ring **out_rings, volatile int *flag, int coreId, volatile int *race) { +void receiveRSS(uint16_t port, volatile int32_t *inIndex, struct rte_ring **out_rings, volatile int *flag, int coreId, volatile int *race, RXTXStats *stats) { setAffinity(coreId); struct rte_mbuf *bufs[BURST_SIZE]; REASSEMBLY_INIT @@ -358,12 +449,15 @@ void receiveRSS(uint16_t port, volatile int32_t *inIndex, struct rte_ring **out_ rx_pkts_number = handleReceived(bufs, rx_pkts_number, tbl, pdeath_row); uint16_t pushed_pkts_number = rte_ring_enqueue_burst(out_rings[inIndex[q+1]], (void*)bufs, rx_pkts_number, NULL); + + UPDATE_COUNTERS(pushed_pkts_number, calculateSize(bufs, pushed_pkts_number), rx_pkts_number - pushed_pkts_number); + // Free any packets which can't be pushed to the ring. The ring is probably full. handleUnpushed((void*)bufs, pushed_pkts_number, rx_pkts_number); #ifdef DEBUG receive_received += rx_pkts_number; receive_pushed += pushed_pkts_number; -#endif +#endif // DEBUG } } free(out_rings); @@ -372,8 +466,8 @@ void receiveRSS(uint16_t port, volatile int32_t *inIndex, struct rte_ring **out_ } void nff_go_KNI(uint16_t port, volatile int *flag, int coreId, - bool recv, struct rte_ring *out_ring, - bool send, struct rte_ring **in_rings, int32_t inIndexNumber) { + bool recv, struct rte_ring *out_ring, + bool send, struct rte_ring **in_rings, int32_t inIndexNumber, RXTXStats *stats) { setAffinity(coreId); struct rte_mbuf *bufs[BURST_SIZE]; int q = 0; @@ -386,6 +480,9 @@ void nff_go_KNI(uint16_t port, volatile int *flag, int coreId, if (likely(rx_pkts_number != 0)) { rx_pkts_number = handleReceived(bufs, rx_pkts_number, tbl, pdeath_row); uint16_t pushed_pkts_number = rte_ring_enqueue_burst(out_ring, (void*)bufs, rx_pkts_number, NULL); + + UPDATE_COUNTERS(pushed_pkts_number, calculateSize(bufs, pushed_pkts_number), rx_pkts_number - pushed_pkts_number); + // Free any packets which can't be pushed to the ring. The ring is probably full. handleUnpushed(bufs, pushed_pkts_number, rx_pkts_number); } @@ -396,6 +493,9 @@ void nff_go_KNI(uint16_t port, volatile int *flag, int coreId, uint16_t pkts_for_tx_number = rte_ring_mc_dequeue_burst(in_rings[q], (void*)bufs, BURST_SIZE, NULL); if (likely(pkts_for_tx_number != 0)) { uint16_t tx_pkts_number = rte_kni_tx_burst(kni[port], bufs, pkts_for_tx_number); + + UPDATE_COUNTERS(tx_pkts_number, calculateSize(bufs, tx_pkts_number), pkts_for_tx_number - tx_pkts_number); + // Free any unsent packets handleUnpushed(bufs, tx_pkts_number, pkts_for_tx_number); } @@ -407,7 +507,7 @@ void nff_go_KNI(uint16_t port, volatile int *flag, int coreId, *flag = wasStopped; } -void nff_go_send(uint16_t port, struct rte_ring **in_rings, int32_t inIndexNumber, bool anyway, volatile int *flag, int coreId) { +void nff_go_send(uint16_t port, struct rte_ring **in_rings, int32_t inIndexNumber, bool anyway, volatile int *flag, int coreId, RXTXStats *stats) { setAffinity(coreId); struct rte_mbuf *bufs[BURST_SIZE]; @@ -429,6 +529,9 @@ void nff_go_send(uint16_t port, struct rte_ring **in_rings, int32_t inIndexNumbe if (switchQueue) { queue = !queue; } + + UPDATE_COUNTERS(tx_pkts_number, calculateSize(bufs, tx_pkts_number), pkts_for_tx_number - tx_pkts_number); + // Free any unsent packets handleUnpushed(bufs, tx_pkts_number, pkts_for_tx_number); #ifdef DEBUG @@ -441,7 +544,7 @@ void nff_go_send(uint16_t port, struct rte_ring **in_rings, int32_t inIndexNumbe *flag = wasStopped; } -void nff_go_stop(struct rte_ring **in_rings, int len, volatile int *flag, int coreId) { +void nff_go_stop(struct rte_ring **in_rings, int len, volatile int *flag, int coreId, RXTXStats *stats) { setAffinity(coreId); struct rte_mbuf *bufs[BURST_SIZE]; uint16_t buf; @@ -455,6 +558,8 @@ void nff_go_stop(struct rte_ring **in_rings, int len, volatile int *flag, int co if (unlikely(pkts_for_free_number == 0)) continue; + UPDATE_COUNTERS(pkts_for_free_number, calculateSize(bufs, pkts_for_free_number), 0); + // Free all these packets for (buf = 0; buf < pkts_for_free_number; buf++) { rte_pktmbuf_free(bufs[buf]); @@ -519,7 +624,7 @@ void statistics(float N) { } // Initialize the Environment Abstraction Layer (EAL) in DPDK. -int eal_init(int argc, char *argv[], uint32_t burstSize, int32_t needKNI) +int eal_init(int argc, char *argv[], uint32_t burstSize, int32_t needKNI, bool noPacketHeadChange) { int ret = rte_eal_init(argc, argv); if (ret < 0) @@ -532,13 +637,27 @@ int eal_init(int argc, char *argv[], uint32_t burstSize, int32_t needKNI) mbufStructSize = sizeof(struct rte_mbuf); headroomSize = RTE_PKTMBUF_HEADROOM; defaultStart = mbufStructSize + headroomSize; + L2CanBeChanged = !noPacketHeadChange; if (needKNI != 0) { rte_kni_init(MAX_KNI); } return 0; } -int allocateMbufs(struct rte_mempool *mempool, struct rte_mbuf **bufs, unsigned count); +int allocateMbufs(struct rte_mempool *mempool, struct rte_mbuf **bufs, unsigned count) { + int ret = rte_pktmbuf_alloc_bulk(mempool, bufs, count); + if (ret == 0) { + for (int i = 0; i < count; i++) { + if (L2CanBeChanged == true) { + mbufInitL2(bufs[i]); + } +#ifdef REASSEMBLY + mbufInitNextChain(bufs[i]); +#endif + } + } + return ret; +} struct rte_mempool * createMempool(uint32_t num_mbufs, uint32_t mbuf_cache_size) { struct rte_mempool *mbuf_pool; @@ -555,11 +674,13 @@ struct rte_mempool * createMempool(uint32_t num_mbufs, uint32_t mbuf_cache_size) // Put mbuf addresses in all packets. It is CMbuf GO field. struct rte_mbuf **temp; temp = malloc(sizeof(struct rte_mbuf *) * num_mbufs); - allocateMbufs(mbuf_pool, temp, num_mbufs); + rte_pktmbuf_alloc_bulk(mbuf_pool, temp, num_mbufs); // This initializes CMbuf field of packet structure stored in mbuf // All CMbuf pointers is set to point to starting of corresponding mbufs for (int i = 0; i < num_mbufs; i++) { - *(char**)((char*)(temp[i]) + mbufStructSize + 32) = (char*)(temp[i]); + mbufInitL2(temp[i]) + mbufInitCMbuf(temp[i]) + mbufInitNextChain(temp[i]) } for (int i = 0; i < num_mbufs; i++) { rte_pktmbuf_free(temp[i]); @@ -569,16 +690,6 @@ struct rte_mempool * createMempool(uint32_t num_mbufs, uint32_t mbuf_cache_size) return mbuf_pool; } -int allocateMbufs(struct rte_mempool *mempool, struct rte_mbuf **bufs, unsigned count) { - int ret = rte_pktmbuf_alloc_bulk(mempool, bufs, count); - if (ret == 0) { - for (int i = 0; i < count; i++) { - mbufInit(bufs[i]); - } - } - return ret; -} - int getMempoolSpace(struct rte_mempool * m) { return rte_mempool_in_use_count(m); } @@ -674,3 +785,92 @@ bool check_hwtxchecksum_capability(uint16_t port_id) { rte_eth_dev_info_get(port_id, &dev_info); return (dev_info.tx_offload_capa & flags) == flags; } + +int initDevice(char *name) { + int s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (s < 1) { + fprintf(stderr, "ERROR: Can't create socket for OS send/receive\n"); + return -1; + } + + struct ifreq ifr; + memset (&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", name); + if (ioctl(s, SIOCGIFINDEX, &ifr) == -1) { + fprintf(stderr, "ERROR: Can't find device %s\n", name); + return -1; + } + + struct sockaddr_ll myaddr; + memset(&myaddr, 0, sizeof(myaddr)); + myaddr.sll_family = AF_PACKET; + myaddr.sll_protocol = htons(ETH_P_ALL); + myaddr.sll_ifindex = ifr.ifr_ifindex; + if (bind(s, (struct sockaddr*)&myaddr, sizeof(myaddr)) < 0) { + fprintf(stderr, "ERROR: Can't bind socket to %s\n", name); + return -1; + } + + ioctl(s, SIOCGIFFLAGS, &ifr); + ifr.ifr_flags |= IFF_PROMISC; + if (ioctl(s, SIOCSIFFLAGS, &ifr) != 0) { + fprintf(stderr, "ERROR: Can't set up promiscuos mode on %s\n", name); + return -1; + } + + return s; +} + +void receiveOS(int socket, struct rte_ring *out_ring, struct rte_mempool *m, volatile int *flag, int coreId, RXTXStats *stats) { + setAffinity(coreId); + const int recvOSBusrst = BURST_SIZE; + struct rte_mbuf *bufs[recvOSBusrst]; + REASSEMBLY_INIT + while (*flag == process) { + // Get packets from OS + allocateMbufs(m, bufs, recvOSBusrst); + //int step = 0; + for (int i = 0; i < recvOSBusrst; i++) { + int bytes_received = recv(socket, (char *)(bufs[i]) + defaultStart, ETH_FRAME_LEN, 0); + if (unlikely(bytes_received == 0)) { + //step++; + i--; + continue; + } + rte_pktmbuf_append(bufs[i], bytes_received); + } + uint16_t rx_pkts_number = handleReceived(bufs, recvOSBusrst, tbl, pdeath_row); + uint16_t pushed_pkts_number = rte_ring_enqueue_burst(out_ring, (void*)bufs, rx_pkts_number, NULL); + + UPDATE_COUNTERS(pushed_pkts_number, calculateSize(bufs, pushed_pkts_number), rx_pkts_number - pushed_pkts_number); + + // Free any packets which can't be pushed to the ring. The ring is probably full. + handleUnpushed((void*)bufs, pushed_pkts_number, rx_pkts_number); + } + *flag = wasStopped; +} + +void sendOS(int socket, struct rte_ring **in_rings, int32_t inIndexNumber, volatile int *flag, int coreId, RXTXStats *stats) { + setAffinity(coreId); + + struct rte_mbuf *bufs[BURST_SIZE]; + uint16_t buf; + uint16_t tx_pkts_number; + while (*flag == process) { + for (int q = 0; q < inIndexNumber; q++) { + // Get packets for TX from ring + uint16_t pkts_for_tx_number = rte_ring_mc_dequeue_burst(in_rings[q], (void*)bufs, BURST_SIZE, NULL); + + for (int i = 0; i < pkts_for_tx_number; i++) { + send(socket, (char *)(bufs[i]) + defaultStart, rte_pktmbuf_pkt_len(bufs[i]), 0); + } + + UPDATE_COUNTERS(pkts_for_tx_number, calculateSize(bufs, pkts_for_tx_number), 0); + + // Free all packets + handleUnpushed(bufs, 0, pkts_for_tx_number); + } + } + free(in_rings); + *flag = wasStopped; +} diff --git a/low/low_test.go b/low/low_test.go index 7be01d0f..86cb7848 100644 --- a/low/low_test.go +++ b/low/low_test.go @@ -15,7 +15,7 @@ import ( func init() { argc, argv := InitDPDKArguments([]string{}) // Default: burstSize=32, mbufNumber=8191, mbufCacheSize=250 - if err := InitDPDK(argc, argv, 32, 8191, 250, 0); err != nil { + if err := InitDPDK(argc, argv, 32, 8191, 250, 0, false); err != nil { log.Fatalf("fail to initialize with error: %+v\n", err) } rand.Seed(time.Now().UTC().UnixNano()) diff --git a/packet/acl.go b/packet/acl.go index a38350bb..3a01dbca 100644 --- a/packet/acl.go +++ b/packet/acl.go @@ -38,6 +38,7 @@ import ( "strings" "github.com/intel-go/nff-go/common" + "github.com/intel-go/nff-go/types" ) type rawL2Rule struct { @@ -207,13 +208,13 @@ func rawL2Parse(rules *rawL2Rules, jp *L2Rules) error { jp.eth[i].ID = 0 jp.eth[i].IDMask = 0 case "ipv4", "Ipv4", "IPv4", "IPV4", "0x0800": - jp.eth[i].ID = common.IPV4Number + jp.eth[i].ID = types.IPV4Number jp.eth[i].IDMask = 0xffff case "ipv6", "Ipv6", "IPv6", "IPV6", "0x86dd": - jp.eth[i].ID = common.IPV6Number + jp.eth[i].ID = types.IPV6Number jp.eth[i].IDMask = 0xffff case "arp", "Arp", "ARP", "0x0806": - jp.eth[i].ID = common.ARPNumber + jp.eth[i].ID = types.ARPNumber jp.eth[i].IDMask = 0xffff default: return common.WrapWithNFError(nil, fmt.Sprintf("Incorrect L3 protocol ID: %v", jup[i].ID), common.IncorrectArgInRules) @@ -241,13 +242,13 @@ func rawL3Parse(rules *rawL3Rules, jp *L3Rules) error { l4temp.ID = 0 l4temp.IDMask = 0 case "tcp", "TCP", "Tcp", "0x06", "6": - l4temp.ID = common.TCPNumber + l4temp.ID = types.TCPNumber l4temp.IDMask = 0xff case "udp", "UDP", "Udp", "0x11", "17": - l4temp.ID = common.UDPNumber + l4temp.ID = types.UDPNumber l4temp.IDMask = 0xff case "icmp", "ICMP", "Icmp", "0x01", "1": - l4temp.ID = common.ICMPNumber + l4temp.ID = types.ICMPNumber l4temp.IDMask = 0xff if jup[i].SrcPort != "ANY" || jup[i].DstPort != "ANY" { return common.WrapWithNFError(nil, "Incorrect request: for ICMP rule Source port and Destination port should be ANY", common.IncorrectArgInRules) @@ -396,8 +397,8 @@ func parseRuleResult(rule string) (uint, error) { } } -func parseAddr4(addr *net.IPNet) (uint32, uint32) { - return binary.LittleEndian.Uint32(addr.IP), binary.LittleEndian.Uint32(addr.Mask) +func parseAddr4(addr *net.IPNet) (types.IPv4Address, types.IPv4Address) { + return types.IPv4Address(binary.LittleEndian.Uint32(addr.IP)), types.IPv4Address(binary.LittleEndian.Uint32(addr.Mask)) } func parseAddr6(addr *net.IPNet) ([16]uint8, [16]uint8) { @@ -413,8 +414,8 @@ type l2Rules struct { OutputNumber uint DAddrNotAny bool SAddrNotAny bool - DAddr [6]uint8 - SAddr [6]uint8 + DAddr types.MACAddress + SAddr types.MACAddress IDMask uint16 ID uint16 } @@ -431,19 +432,19 @@ type l4Rules struct { type l3Rules4 struct { OutputNumber uint - SrcAddr uint32 - DstAddr uint32 - SrcMask uint32 - DstMask uint32 + SrcAddr types.IPv4Address + DstAddr types.IPv4Address + SrcMask types.IPv4Address + DstMask types.IPv4Address L4 l4Rules } type l3Rules6 struct { OutputNumber uint - SrcAddr [16]uint8 - DstAddr [16]uint8 - SrcMask [16]uint8 - DstMask [16]uint8 + SrcAddr types.IPv6Address + DstAddr types.IPv6Address + SrcMask types.IPv6Address + DstMask types.IPv6Address L4 l4Rules } diff --git a/packet/acl_internal_test.go b/packet/acl_internal_test.go index 68375344..86e7d0c8 100644 --- a/packet/acl_internal_test.go +++ b/packet/acl_internal_test.go @@ -59,6 +59,8 @@ import ( "os" "reflect" "testing" + + "github.com/intel-go/nff-go/types" ) // Data to generate L2 rules @@ -70,12 +72,12 @@ var rulesL2Ctxt = rawL2RuleTestCtxt{ {"", 0}, // case only for ORIG }, srcs: []macAddrTest{ // {rawaddr, ground truth addr info {macaddr, addrNotAny}} - {"ANY", gtMacAddr{[6]uint8{0, 0, 0, 0, 0, 0}, false}}, + {"ANY", gtMacAddr{types.MACAddress{0, 0, 0, 0, 0, 0}, false}}, {"00:11:22:33:44:55", gtMacAddr{[6]uint8{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}, true}}, }, dsts: []macAddrTest{ // {rawaddr, ground truth addr info {macaddr, addrNotAny}} - {"ANY", gtMacAddr{[6]uint8{0, 0, 0, 0, 0, 0}, false}}, - {"01:11:21:31:41:51", gtMacAddr{[6]uint8{0x01, 0x11, 0x21, 0x31, 0x41, 0x51}, true}}, + {"ANY", gtMacAddr{types.MACAddress{0, 0, 0, 0, 0, 0}, false}}, + {"01:11:21:31:41:51", gtMacAddr{types.MACAddress{0x01, 0x11, 0x21, 0x31, 0x41, 0x51}, true}}, }, ids: []idTest{ // {rawid, ground truth {id, idmask}} {"ANY", gtID{0, 0x0000}}, @@ -98,40 +100,40 @@ var rulesL3Ctxt = rawL3RuleTestCtxt{ srcs6: []Addr6Test{ // {rawaddr, ground truth addr info {macaddr, addrNotAny}} {"ANY", gtAddr6{ - [16]uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - [16]uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + types.IPv6Address{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + types.IPv6Address{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, }, }, {"::/0", gtAddr6{ - [16]uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - [16]uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + types.IPv6Address{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + types.IPv6Address{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, }, }, {"dead::beef/16", gtAddr6{ - [16]uint8{0xde, 0xad, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - [16]uint8{0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + types.IPv6Address{0xde, 0xad, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + types.IPv6Address{0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, }, }, }, dsts6: []Addr6Test{ // {rawaddr, ground truth addr info} {"ANY", gtAddr6{ - [16]uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - [16]uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + types.IPv6Address{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + types.IPv6Address{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, }, }, {"::/0", gtAddr6{ - [16]uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - [16]uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + types.IPv6Address{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + types.IPv6Address{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, }, }, {"dead::beef/128", gtAddr6{ - [16]uint8{0xde, 0xad, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xbe, 0xef}, - [16]uint8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + types.IPv6Address{0xde, 0xad, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xbe, 0xef}, + types.IPv6Address{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, }, }, }, @@ -1342,13 +1344,13 @@ type Addr6Test struct { } type gtAddr4 struct { - addr uint32 - mask uint32 + addr types.IPv4Address + mask types.IPv4Address } type gtAddr6 struct { - addr [16]uint8 - mask [16]uint8 + addr types.IPv6Address + mask types.IPv6Address } type portTest struct { @@ -1424,19 +1426,19 @@ type IDMask16 struct { } type Addr4Mask struct { - addr uint32 - msk uint32 + addr types.IPv4Address + msk types.IPv4Address ok bool } type Addr6Mask struct { - addr [16]uint8 - msk [16]uint8 + addr types.IPv6Address + msk types.IPv6Address ok bool } type MacAddr struct { - addr [6]uint8 + addr types.MACAddress addrNotAny bool ok bool } diff --git a/packet/arp.go b/packet/arp.go index 95f4cbcc..cc6cfdd5 100644 --- a/packet/arp.go +++ b/packet/arp.go @@ -8,21 +8,22 @@ import ( "fmt" "github.com/intel-go/nff-go/common" + "github.com/intel-go/nff-go/types" ) // ARPHdr is protocol structure used in Address Resolution Protocol // for IPv4 to MAC mapping type ARPHdr struct { - HType uint16 // Hardware type, e.g. 1 for Ethernet - PType uint16 // Protocol type, e.g. 0x0800 for IPv4 - HLen uint8 // Hardware address length, e.g. 6 for MAC length - PLen uint8 // Protocol address length, e.g. 4 for IPv4 address length - Operation uint16 // Operation type, see ARP constants - SHA [common.EtherAddrLen]uint8 // Sender hardware address (sender MAC address) - SPA [common.IPv4AddrLen]uint8 // Sender protocol address (sender IPv4 address) + HType uint16 // Hardware type, e.g. 1 for Ethernet + PType uint16 // Protocol type, e.g. 0x0800 for IPv4 + HLen uint8 // Hardware address length, e.g. 6 for MAC length + PLen uint8 // Protocol address length, e.g. 4 for IPv4 address length + Operation uint16 // Operation type, see ARP constants + SHA types.MACAddress // Sender hardware address (sender MAC address) + SPA [types.IPv4AddrLen]uint8 // Sender protocol address (sender IPv4 address) // array is used to avoid alignment (compiler alignes uint32 on 4 bytes) - THA [common.EtherAddrLen]uint8 // Target hardware address (target MAC address) - TPA [common.IPv4AddrLen]uint8 // Target protocol address (target IPv4 address) + THA types.MACAddress // Target hardware address (target MAC address) + TPA [types.IPv4AddrLen]uint8 // Target protocol address (target IPv4 address) // array is used to avoid alignment (compiler alignes uint32 on 4 bytes) } @@ -63,9 +64,9 @@ func initARPCommonData(packet *Packet) bool { arp := packet.GetARPNoCheck() arp.HType = SwapBytesUint16(1) - arp.PType = SwapBytesUint16(common.IPV4Number) - arp.HLen = common.EtherAddrLen - arp.PLen = common.IPv4AddrLen + arp.PType = SwapBytesUint16(types.IPV4Number) + arp.HLen = types.EtherAddrLen + arp.PLen = types.IPv4AddrLen return true } @@ -75,19 +76,19 @@ func initARPCommonData(packet *Packet) bool { // specifies IP address for host which request is sent // for. Destination MAC address in L2 Ethernet header is set to // FF:FF:FF:FF:FF:FF (broadcast) and source address is set to SHA. -func InitARPRequestPacket(packet *Packet, SHA [common.EtherAddrLen]uint8, SPA, TPA uint32) bool { +func InitARPRequestPacket(packet *Packet, SHA types.MACAddress, SPA, TPA types.IPv4Address) bool { if !initARPCommonData(packet) { common.LogWarning(common.Debug, "InitARPRequestPacket: failed to fill common data") return false } packet.Ether.SAddr = SHA - packet.Ether.DAddr = [common.EtherAddrLen]uint8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + packet.Ether.DAddr = types.MACAddress{0xff, 0xff, 0xff, 0xff, 0xff, 0xff} arp := packet.GetARPNoCheck() arp.Operation = SwapBytesUint16(ARPRequest) arp.SHA = SHA - arp.SPA = IPv4ToBytes(SPA) - arp.THA = [common.EtherAddrLen]uint8{} - arp.TPA = IPv4ToBytes(TPA) + arp.SPA = types.IPv4ToBytes(SPA) + arp.THA = types.MACAddress{0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + arp.TPA = types.IPv4ToBytes(TPA) return true } @@ -96,7 +97,7 @@ func InitARPRequestPacket(packet *Packet, SHA [common.EtherAddrLen]uint8, SPA, T // TPA specify target MAC and IP addresses. Destination MAC address // in L2 Ethernet header is set to THA and source address is set to // SHA. -func InitARPReplyPacket(packet *Packet, SHA, THA [common.EtherAddrLen]uint8, SPA, TPA uint32) bool { +func InitARPReplyPacket(packet *Packet, SHA, THA types.MACAddress, SPA, TPA types.IPv4Address) bool { if !initARPCommonData(packet) { common.LogWarning(common.Debug, "InitARPRequestPacket: failed to fill common data") return false @@ -106,9 +107,9 @@ func InitARPReplyPacket(packet *Packet, SHA, THA [common.EtherAddrLen]uint8, SPA arp := packet.GetARPNoCheck() arp.Operation = SwapBytesUint16(ARPReply) arp.SHA = SHA - arp.SPA = IPv4ToBytes(SPA) + arp.SPA = types.IPv4ToBytes(SPA) arp.THA = THA - arp.TPA = IPv4ToBytes(TPA) + arp.TPA = types.IPv4ToBytes(TPA) return true } @@ -118,19 +119,19 @@ func InitARPReplyPacket(packet *Packet, SHA, THA [common.EtherAddrLen]uint8, SPA // SPA specify sender MAC and IP addresses, TPA is set to the value of // SPA. Destination MAC address in L2 Ethernet header is set to // FF:FF:FF:FF:FF:FF (broadcast) and source address is set to SHA. -func InitGARPAnnouncementRequestPacket(packet *Packet, SHA [common.EtherAddrLen]uint8, SPA uint32) bool { +func InitGARPAnnouncementRequestPacket(packet *Packet, SHA types.MACAddress, SPA types.IPv4Address) bool { if !initARPCommonData(packet) { common.LogWarning(common.Debug, "InitARPRequestPacket: failed to fill common data") return false } packet.Ether.SAddr = SHA - packet.Ether.DAddr = [common.EtherAddrLen]uint8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + packet.Ether.DAddr = types.MACAddress{0xff, 0xff, 0xff, 0xff, 0xff, 0xff} arp := packet.GetARPNoCheck() arp.Operation = SwapBytesUint16(ARPRequest) arp.SHA = SHA - arp.SPA = IPv4ToBytes(SPA) - arp.THA = [common.EtherAddrLen]uint8{} - arp.TPA = IPv4ToBytes(SPA) + arp.SPA = types.IPv4ToBytes(SPA) + arp.THA = types.MACAddress{0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + arp.TPA = types.IPv4ToBytes(SPA) return true } @@ -140,18 +141,18 @@ func InitGARPAnnouncementRequestPacket(packet *Packet, SHA [common.EtherAddrLen] // to zeroes (according to RFC 5227). Destination MAC address in L2 Ethernet header is set // to FF:FF:FF:FF:FF:FF (broadcast) and source address is set // to SHA. -func InitGARPAnnouncementReplyPacket(packet *Packet, SHA [common.EtherAddrLen]uint8, SPA uint32) bool { +func InitGARPAnnouncementReplyPacket(packet *Packet, SHA types.MACAddress, SPA types.IPv4Address) bool { if !initARPCommonData(packet) { common.LogWarning(common.Debug, "InitARPRequestPacket: failed to fill common data") return false } packet.Ether.SAddr = SHA - packet.Ether.DAddr = [common.EtherAddrLen]uint8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + packet.Ether.DAddr = types.MACAddress{0xff, 0xff, 0xff, 0xff, 0xff, 0xff} arp := packet.GetARPNoCheck() arp.Operation = SwapBytesUint16(ARPReply) arp.SHA = SHA - arp.SPA = IPv4ToBytes(SPA) - arp.THA = [common.EtherAddrLen]uint8{} - arp.TPA = IPv4ToBytes(SPA) + arp.SPA = types.IPv4ToBytes(SPA) + arp.THA = types.MACAddress{0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + arp.TPA = types.IPv4ToBytes(SPA) return true } diff --git a/packet/arp_test.go b/packet/arp_test.go index 06172848..88706e0d 100644 --- a/packet/arp_test.go +++ b/packet/arp_test.go @@ -11,7 +11,7 @@ import ( "testing" "unsafe" - . "github.com/intel-go/nff-go/common" + "github.com/intel-go/nff-go/types" ) func init() { @@ -20,10 +20,10 @@ func init() { // These strings were created with gopacket library. var ( - gtLineARPRequest = "ffffffffffff00070daff4540806000108000604000100070daff45418a6ac0100000000000018a6ad9f" + gtLineARPRequest = "ffffffffffff00070daff4540806000108000604000100070daff45418a6ac01ffffffffffff18a6ad9f" gtLineARPReply = "c40132580000c402326b000008060001080006040002c402326b00000a000002c401325800000a000001" - gtLineGratARPRequest = "ffffffffffff02020202020208060001080006040001020202020202c0a80101000000000000c0a80101" - gtLineGratARPReply = "ffffffffffff00000c07ac010806000108000604000200000c07ac010a0000060000000000000a000006" + gtLineGratARPRequest = "ffffffffffff02020202020208060001080006040001020202020202c0a80101ffffffffffffc0a80101" + gtLineGratARPReply = "ffffffffffff00000c07ac010806000108000604000200000c07ac010a000006ffffffffffff0a000006" gtLineEmptyARP = "000000000000000000000000080600000000000000000000000000000000000000000000000000000000" ) @@ -32,12 +32,12 @@ func TestInitARPCommonDataPacket(t *testing.T) { pkt := getPacket() initARPCommonData(pkt) - pkt.Ether.DAddr = [6]uint8{0xff, 0xff, 0xff, 0xff, 0xff, 0xff} - pkt.Ether.SAddr = [6]uint8{0x00, 0x07, 0x0d, 0xaf, 0xf4, 0x54} + pkt.Ether.DAddr = types.MACAddress{0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + pkt.Ether.SAddr = types.MACAddress{0x00, 0x07, 0x0d, 0xaf, 0xf4, 0x54} pktARP := pkt.GetARP() pktARP.Operation = SwapBytesUint16(1) - pktARP.SHA = [6]uint8{0x00, 0x07, 0x0d, 0xaf, 0xf4, 0x54} - pktARP.THA = [6]uint8{0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + pktARP.SHA = types.MACAddress{0x00, 0x07, 0x0d, 0xaf, 0xf4, 0x54} + pktARP.THA = types.MACAddress{0xff, 0xff, 0xff, 0xff, 0xff, 0xff} copy(pktARP.SPA[:], net.ParseIP("24.166.172.1").To4()[:]) copy(pktARP.TPA[:], net.ParseIP("24.166.173.159").To4()[:]) @@ -46,7 +46,7 @@ func TestInitARPCommonDataPacket(t *testing.T) { gtPkt := getPacket() GeneratePacketFromByte(gtPkt, gtBuf) - size := EtherLen + ARPLen + size := types.EtherLen + types.ARPLen buf := (*[1 << 30]byte)(unsafe.Pointer(pkt.StartAtOffset(0)))[:size] if !reflect.DeepEqual(buf, gtBuf) { t.Errorf("Incorrect result:\ngot: %x, \nwant: %x\n\n", buf, gtBuf) @@ -56,11 +56,11 @@ func TestInitARPCommonDataPacket(t *testing.T) { func TestInitARPRequestPacket(t *testing.T) { // Create empty packet, set UDP header fields pkt := getPacket() - sha := [6]uint8{0x00, 0x07, 0x0d, 0xaf, 0xf4, 0x54} + sha := types.MACAddress{0x00, 0x07, 0x0d, 0xaf, 0xf4, 0x54} srcIP := net.ParseIP("24.166.172.1").To4() dstIP := net.ParseIP("24.166.173.159").To4() - spa := BytesToIPv4(srcIP[0], srcIP[1], srcIP[2], srcIP[3]) - tpa := BytesToIPv4(dstIP[0], dstIP[1], dstIP[2], dstIP[3]) + spa := types.BytesToIPv4(srcIP[0], srcIP[1], srcIP[2], srcIP[3]) + tpa := types.BytesToIPv4(dstIP[0], dstIP[1], dstIP[2], dstIP[3]) InitARPRequestPacket(pkt, sha, spa, tpa) // Create ground truth packet @@ -68,7 +68,7 @@ func TestInitARPRequestPacket(t *testing.T) { gtPkt := getPacket() GeneratePacketFromByte(gtPkt, gtBuf) - size := EtherLen + ARPLen + size := types.EtherLen + types.ARPLen buf := (*[1 << 30]byte)(unsafe.Pointer(pkt.StartAtOffset(0)))[:size] if !reflect.DeepEqual(buf, gtBuf) { t.Errorf("Incorrect result:\ngot: %x, \nwant: %x\n\n", buf, gtBuf) @@ -78,12 +78,12 @@ func TestInitARPRequestPacket(t *testing.T) { func TestInitARPReplyPacket(t *testing.T) { // Create empty packet, set UDP header fields pkt := getPacket() - tha := [6]uint8{0xc4, 0x01, 0x32, 0x58, 0x00, 0x00} - sha := [6]uint8{0xc4, 0x02, 0x32, 0x6b, 0x00, 0x00} + tha := types.MACAddress{0xc4, 0x01, 0x32, 0x58, 0x00, 0x00} + sha := types.MACAddress{0xc4, 0x02, 0x32, 0x6b, 0x00, 0x00} srcIP := net.ParseIP("10.0.0.2").To4() dstIP := net.ParseIP("10.0.0.1").To4() - spa := BytesToIPv4(srcIP[0], srcIP[1], srcIP[2], srcIP[3]) - tpa := BytesToIPv4(dstIP[0], dstIP[1], dstIP[2], dstIP[3]) + spa := types.BytesToIPv4(srcIP[0], srcIP[1], srcIP[2], srcIP[3]) + tpa := types.BytesToIPv4(dstIP[0], dstIP[1], dstIP[2], dstIP[3]) InitARPReplyPacket(pkt, sha, tha, spa, tpa) // Create ground truth packet @@ -91,7 +91,7 @@ func TestInitARPReplyPacket(t *testing.T) { gtPkt := getPacket() GeneratePacketFromByte(gtPkt, gtBuf) - size := EtherLen + ARPLen + size := types.EtherLen + types.ARPLen buf := (*[1 << 30]byte)(unsafe.Pointer(pkt.StartAtOffset(0)))[:size] if !reflect.DeepEqual(buf, gtBuf) { t.Errorf("Incorrect result:\ngot: %x, \nwant: %x\n\n", buf, gtBuf) @@ -101,9 +101,9 @@ func TestInitARPReplyPacket(t *testing.T) { func TestInitGARPAnnouncementRequestPacket(t *testing.T) { // Create empty packet, set UDP header fields pkt := getPacket() - sha := [6]uint8{0x02, 0x02, 0x02, 0x02, 0x02, 0x02} + sha := types.MACAddress{0x02, 0x02, 0x02, 0x02, 0x02, 0x02} srcIP := net.ParseIP("192.168.1.1").To4() - spa := BytesToIPv4(srcIP[0], srcIP[1], srcIP[2], srcIP[3]) + spa := types.BytesToIPv4(srcIP[0], srcIP[1], srcIP[2], srcIP[3]) InitGARPAnnouncementRequestPacket(pkt, sha, spa) @@ -112,7 +112,7 @@ func TestInitGARPAnnouncementRequestPacket(t *testing.T) { gtPkt := getPacket() GeneratePacketFromByte(gtPkt, gtBuf) - size := EtherLen + ARPLen + size := types.EtherLen + types.ARPLen buf := (*[1 << 30]byte)(unsafe.Pointer(pkt.StartAtOffset(0)))[:size] if !reflect.DeepEqual(buf, gtBuf) { t.Errorf("Incorrect result:\ngot: %x, \nwant: %x\n\n", buf, gtBuf) @@ -122,9 +122,9 @@ func TestInitGARPAnnouncementRequestPacket(t *testing.T) { func TestInitGARPAnnouncementReplyPacket(t *testing.T) { // Create empty packet, set UDP header fields pkt := getPacket() - sha := [6]uint8{0x00, 0x00, 0x0c, 0x07, 0xac, 0x01} + sha := types.MACAddress{0x00, 0x00, 0x0c, 0x07, 0xac, 0x01} srcIP := net.ParseIP("10.0.0.6").To4() - spa := BytesToIPv4(srcIP[0], srcIP[1], srcIP[2], srcIP[3]) + spa := types.BytesToIPv4(srcIP[0], srcIP[1], srcIP[2], srcIP[3]) InitGARPAnnouncementReplyPacket(pkt, sha, spa) @@ -133,7 +133,7 @@ func TestInitGARPAnnouncementReplyPacket(t *testing.T) { gtPkt := getPacket() GeneratePacketFromByte(gtPkt, gtBuf) - size := EtherLen + ARPLen + size := types.EtherLen + types.ARPLen buf := (*[1 << 30]byte)(unsafe.Pointer(pkt.StartAtOffset(0)))[:size] if !reflect.DeepEqual(buf, gtBuf) { t.Errorf("Incorrect result:\ngot: %x, \nwant: %x\n\n", buf, gtBuf) @@ -149,7 +149,7 @@ func TestInitEmptyARPPacket(t *testing.T) { gtPkt := getPacket() GeneratePacketFromByte(gtPkt, gtBuf) - size := EtherLen + ARPLen + size := types.EtherLen + types.ARPLen buf := (*[1 << 30]byte)(unsafe.Pointer(pkt.StartAtOffset(0)))[:size] if !reflect.DeepEqual(buf, gtBuf) { t.Errorf("Incorrect result:\ngot: %x, \nwant: %x\n\n", buf, gtBuf) diff --git a/packet/checksum.go b/packet/checksum.go index b1ec78c3..a6c88814 100644 --- a/packet/checksum.go +++ b/packet/checksum.go @@ -5,9 +5,10 @@ package packet import ( - . "github.com/intel-go/nff-go/common" - "github.com/intel-go/nff-go/low" "unsafe" + + "github.com/intel-go/nff-go/low" + . "github.com/intel-go/nff-go/types" ) // Setting up flags for hardware offloading for hardware calculation of checksums diff --git a/packet/checksum_test.go b/packet/checksum_test.go index 042751b5..062f2fec 100644 --- a/packet/checksum_test.go +++ b/packet/checksum_test.go @@ -5,8 +5,7 @@ package packet import ( - "encoding/binary" - "github.com/intel-go/nff-go/common" + "github.com/intel-go/nff-go/types" "net" "testing" ) @@ -159,15 +158,15 @@ func TestCalculateIPv6ICMPChecksum(t *testing.T) { func initIPv4AddrsLocal(pkt *Packet) { ipv4 := pkt.GetIPv4() - ipv4.SrcAddr = binary.LittleEndian.Uint32(net.ParseIP("131.151.32.21").To4()) - ipv4.DstAddr = binary.LittleEndian.Uint32(net.ParseIP("131.151.32.129").To4()) + ipv4.SrcAddr = types.SliceToIPv4(net.ParseIP("131.151.32.21").To4()) + ipv4.DstAddr = types.SliceToIPv4(net.ParseIP("131.151.32.129").To4()) ipv4.HdrChecksum = 0 } func initIPv6AddrsLocal(pkt *Packet) { ipv6 := pkt.GetIPv6() - copy(ipv6.SrcAddr[:], net.ParseIP("2001:db8:0:0:1::1")[:common.IPv6AddrLen]) - copy(ipv6.DstAddr[:], net.ParseIP("2001:db8:0:0:1::12")[:common.IPv6AddrLen]) + copy(ipv6.SrcAddr[:], net.ParseIP("2001:db8:0:0:1::1")[:types.IPv6AddrLen]) + copy(ipv6.DstAddr[:], net.ParseIP("2001:db8:0:0:1::12")[:types.IPv6AddrLen]) } func initData(pkt *Packet) { diff --git a/packet/gre.go b/packet/gre.go new file mode 100644 index 00000000..be9d7458 --- /dev/null +++ b/packet/gre.go @@ -0,0 +1,34 @@ +// Copyright 2018 Intel Corporation. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "fmt" + + "github.com/intel-go/nff-go/types" +) + +type GREHdr struct { + Flags uint16 + NextProto uint16 +} + +func (hdr *GREHdr) String() string { + return fmt.Sprintf("GRE: flags = 0x%04x, embedded protocol = 0x%04x", + hdr.Flags, hdr.NextProto) +} + +// GetGREForIPv4 casts L4 pointer to *GREHdr type. +func (packet *Packet) GetGREForIPv4() *GREHdr { + if packet.GetIPv4NoCheck().NextProtoID == types.GRENumber { + return (*GREHdr)(packet.L4) + } + return nil +} + +// GetGRENoCheck casts L4 pointer to *GREHdr type. +func (packet *Packet) GetGRENoCheck() *GREHdr { + return (*GREHdr)(packet.L4) +} diff --git a/packet/gtp.go b/packet/gtp.go index fca67365..ae52fbe2 100644 --- a/packet/gtp.go +++ b/packet/gtp.go @@ -13,7 +13,7 @@ import ( "fmt" "unsafe" - . "github.com/intel-go/nff-go/common" + . "github.com/intel-go/nff-go/types" ) // GTPv1-U and GTPv1-C diff --git a/packet/icmp6.go b/packet/icmp6.go index 7b7db94e..dd7ff476 100644 --- a/packet/icmp6.go +++ b/packet/icmp6.go @@ -7,7 +7,7 @@ package packet import ( "unsafe" - "github.com/intel-go/nff-go/common" + "github.com/intel-go/nff-go/types" ) const ( @@ -38,13 +38,13 @@ var ( type ICMPv6NDSourceLinkLayerAddressOption struct { Type uint8 Length uint8 - LinkLayerAddress [common.EtherAddrLen]uint8 + LinkLayerAddress types.MACAddress } type ICMPv6NDTargetLinkLayerAddressOption struct { Type uint8 Length uint8 - LinkLayerAddress [common.EtherAddrLen]uint8 + LinkLayerAddress types.MACAddress } type ICMPv6NDPrefixInformationOption struct { @@ -55,7 +55,7 @@ type ICMPv6NDPrefixInformationOption struct { ValidLifetime uint32 PreferredLifetime uint32 Reserved2 uint32 - Prefix [common.IPv6AddrLen]uint8 + Prefix types.IPv6Address } type ICMPv6NDRedirectedHeaderOption struct { @@ -71,11 +71,11 @@ type ICMPv6NDMTUOption struct { } type ICMPv6NeighborSolicitationMessage struct { - TargetAddr [common.IPv6AddrLen]uint8 + TargetAddr types.IPv6Address } type ICMPv6NeighborAdvertisementMessage struct { - TargetAddr [common.IPv6AddrLen]uint8 + TargetAddr types.IPv6Address } // GetICMPv6NeighborSolicitationMessage returns pointer to ICMPv6 @@ -126,7 +126,7 @@ func (packet *Packet) GetICMPv6NDTargetLinkLayerAddressOption(msgLength uint) *I // CalculateIPv6LinkLocalAddrForMAC generates IPv6 link local address // based on interface MAC address. -func CalculateIPv6LinkLocalAddrForMAC(llAddr *[common.IPv6AddrLen]uint8, mac [common.EtherAddrLen]uint8) { +func CalculateIPv6LinkLocalAddrForMAC(llAddr *types.IPv6Address, mac types.MACAddress) { copy((*llAddr)[:], ipv6LinkLocalPrefix) (*llAddr)[8] = mac[0] ^ 0x02 (*llAddr)[9] = mac[1] @@ -142,14 +142,14 @@ func CalculateIPv6LinkLocalAddrForMAC(llAddr *[common.IPv6AddrLen]uint8, mac [co // that other hosts use to solicit its MAC address. This address is // used as destination for all Neighbor Solicitation ICMPv6 messages // and NAT should answer packets coming to it. -func CalculateIPv6MulticastAddrForDstIP(muticastAddr *[common.IPv6AddrLen]uint8, dstIP [common.IPv6AddrLen]uint8) { +func CalculateIPv6MulticastAddrForDstIP(muticastAddr *types.IPv6Address, dstIP types.IPv6Address) { copy((*muticastAddr)[:], ipv6LinkLocalMulticastPrefix) (*muticastAddr)[13] = dstIP[13] (*muticastAddr)[14] = dstIP[14] (*muticastAddr)[15] = dstIP[15] } -func CalculateIPv6BroadcastMACForDstMulticastIP(dstMAC *[common.EtherAddrLen]uint8, dstIP [common.IPv6AddrLen]uint8) { +func CalculateIPv6BroadcastMACForDstMulticastIP(dstMAC *types.MACAddress, dstIP types.IPv6Address) { copy((*dstMAC)[:], ipv6EtherMulticastPrefix) (*dstMAC)[2] = dstIP[12] (*dstMAC)[3] = dstIP[13] @@ -160,10 +160,10 @@ func CalculateIPv6BroadcastMACForDstMulticastIP(dstMAC *[common.EtherAddrLen]uin // InitICMPv6NeighborSolicitationPacket allocates and initializes // ICMPv6 Neighbor Solicitation request message packet with source MAC // and IPv6 address and target IPv6 address. -func InitICMPv6NeighborSolicitationPacket(packet *Packet, srcMAC [common.EtherAddrLen]uint8, srcIP, dstIP [common.IPv6AddrLen]uint8) { +func InitICMPv6NeighborSolicitationPacket(packet *Packet, srcMAC types.MACAddress, srcIP, dstIP types.IPv6Address) { InitEmptyIPv6ICMPPacket(packet, ICMPv6NeighborSolicitationMessageSize+ICMPv6NDSourceLinkLayerAddressOptionSize) - var targetMulticastAddr [common.IPv6AddrLen]uint8 + var targetMulticastAddr types.IPv6Address CalculateIPv6MulticastAddrForDstIP(&targetMulticastAddr, dstIP) // Fill up L2 @@ -177,12 +177,12 @@ func InitICMPv6NeighborSolicitationPacket(packet *Packet, srcMAC [common.EtherAd // Fill up L4 icmp := packet.GetICMPNoCheck() - icmp.Type = common.ICMPv6NeighborSolicitation + icmp.Type = types.ICMPv6NeighborSolicitation icmp.Identifier = 0 icmp.SeqNum = 0 // Fill up L7 - packet.ParseL7(common.ICMPv6Number) + packet.ParseL7(types.ICMPv6Number) msg := packet.GetICMPv6NeighborSolicitationMessage() msg.TargetAddr = dstIP option := packet.GetICMPv6NDSourceLinkLayerAddressOption(ICMPv6NeighborSolicitationMessageSize) @@ -194,7 +194,7 @@ func InitICMPv6NeighborSolicitationPacket(packet *Packet, srcMAC [common.EtherAd // InitICMPv6NeighborAdvertisementPacket allocates and initializes // ICMPv6 Neighbor Advertisement answer message packet with source MAC // and IPv6 address and target IPv6 address. -func InitICMPv6NeighborAdvertisementPacket(packet *Packet, srcMAC, dstMAC [common.EtherAddrLen]uint8, srcIP, dstIP [common.IPv6AddrLen]uint8) { +func InitICMPv6NeighborAdvertisementPacket(packet *Packet, srcMAC, dstMAC types.MACAddress, srcIP, dstIP types.IPv6Address) { InitEmptyIPv6ICMPPacket(packet, ICMPv6NeighborAdvertisementMessageSize+ICMPv6NDTargetLinkLayerAddressOptionSize) // Fill up L2 @@ -208,12 +208,12 @@ func InitICMPv6NeighborAdvertisementPacket(packet *Packet, srcMAC, dstMAC [commo // Fill up L4 icmp := packet.GetICMPNoCheck() - icmp.Type = common.ICMPv6NeighborAdvertisement + icmp.Type = types.ICMPv6NeighborAdvertisement icmp.Identifier = SwapBytesUint16(ICMPv6NDSolicitedFlag | ICMPv6NDOverrideFlag) icmp.SeqNum = 0 // Fill up L7 - packet.ParseL7(common.ICMPv6Number) + packet.ParseL7(types.ICMPv6Number) msg := packet.GetICMPv6NeighborAdvertisementMessage() msg.TargetAddr = srcIP option := packet.GetICMPv6NDTargetLinkLayerAddressOption(ICMPv6NeighborAdvertisementMessageSize) diff --git a/packet/lpm_test.go b/packet/lpm_test.go index 8413cd57..5ad3dfdf 100644 --- a/packet/lpm_test.go +++ b/packet/lpm_test.go @@ -9,6 +9,8 @@ import ( "math/rand" "testing" "time" + + "github.com/intel-go/nff-go/types" ) func init() { @@ -16,8 +18,8 @@ func init() { rand.Seed(time.Now().UTC().UnixNano()) } -var zero uint32 = 0 -var next uint32 +var zero types.IPv4Address = 0 +var next types.IPv4Address var stepM = 1000 var stepN = 10 @@ -28,13 +30,13 @@ func TestOneRule(t *testing.T) { for i := 0; i < stepM; i++ { ip_number := 1 + rand.Intn(32) mask_len := uint8(1 + rand.Intn(32)) - next_hop := uint32(rand.Intn(100)) + next_hop := types.IPv4Address(rand.Intn(100)) // Construct ip ip := constructIP(ip_number) // Construct mask mask := zero - for u := zero; u < uint32(mask_len); u++ { + for u := zero; u < types.IPv4Address(mask_len); u++ { mask = mask | (1 << (31 - u)) } @@ -132,8 +134,8 @@ func TestMultipleRules(t *testing.T) { lpm.Free() } -func constructIP(ip_number int) uint32 { - var ip uint32 +func constructIP(ip_number int) types.IPv4Address { + var ip types.IPv4Address if ip_number < 16 { ip = zero for j := 0; j < ip_number; j++ { @@ -156,15 +158,15 @@ func constructIP(ip_number int) uint32 { return ip } -func testIP(ip uint32, mask_len uint8) (int, int, []uint32, []uint32) { +func testIP(ip types.IPv4Address, mask_len uint8) (int, int, []types.IPv4Address, []types.IPv4Address) { right := int(math.Pow(2, float64(32-mask_len))) wrong := int(math.Pow(2, float64(mask_len)) - 1) rightN := rand.Intn(right)%stepK + 1 wrongN := rand.Intn(wrong)%stepK + 1 - rightIP := make([]uint32, rightN, rightN) - wrongIP := make([]uint32, wrongN, wrongN) + rightIP := make([]types.IPv4Address, rightN, rightN) + wrongIP := make([]types.IPv4Address, wrongN, wrongN) rightIP[0] = ip for i := 1; i < rightN; i++ { diff --git a/packet/mpls.go b/packet/mpls.go index 2202b499..3c0518d7 100644 --- a/packet/mpls.go +++ b/packet/mpls.go @@ -8,7 +8,7 @@ import ( "fmt" "unsafe" - . "github.com/intel-go/nff-go/common" + . "github.com/intel-go/nff-go/types" ) type MPLSHdr struct { diff --git a/packet/neigh.go b/packet/neigh.go new file mode 100644 index 00000000..48ae43f3 --- /dev/null +++ b/packet/neigh.go @@ -0,0 +1,100 @@ +// Copyright 2018 Intel Corporation. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package packet + +import ( + "fmt" + "sync" + + "github.com/intel-go/nff-go/common" + "github.com/intel-go/nff-go/types" +) + +type NeighboursLookupTable struct { + portIndex uint16 + ipv4Table sync.Map + ipv6Table sync.Map + interfaceMAC types.MACAddress + ipv4InterfaceAddr types.IPv4Address + ipv6InterfaceAddr types.IPv6Address +} + +func NewNeighbourTable(index uint16, mac types.MACAddress, ipv4 types.IPv4Address, ipv6 types.IPv6Address) *NeighboursLookupTable { + return &NeighboursLookupTable{ + portIndex: index, + interfaceMAC: mac, + ipv4InterfaceAddr: ipv4, + ipv6InterfaceAddr: ipv6, + } +} + +// HandleIPv4ARPRequest processes IPv4 ARP request and reply packets +// and sends an ARP response (if needed) to the same interface. Packet +// has to have L3 parsed. If ARP request packet has VLAN tag, VLAN tag +// is copied into reply packet. +func (table *NeighboursLookupTable) HandleIPv4ARPPacket(pkt *Packet) error { + arp := pkt.GetARPNoCheck() + + if SwapBytesUint16(arp.Operation) != ARPRequest { + // Handle ARP reply and record information in lookup table + if SwapBytesUint16(arp.Operation) == ARPReply { + ipv4 := types.ArrayToIPv4(arp.SPA) + table.ipv4Table.Store(ipv4, arp.SHA) + } + return nil + } + + // Check that someone is asking about MAC of my IP address and HW + // address is blank in request + if types.BytesToIPv4(arp.TPA[0], arp.TPA[1], arp.TPA[2], arp.TPA[3]) != table.ipv4InterfaceAddr { + return fmt.Errorf("Warning! Got an ARP packet with target IPv4 address %s different from IPv4 address on interface. Should be %s. ARP request ignored.", types.IPv4ArrayToString(arp.TPA), table.ipv4InterfaceAddr.String()) + } + if arp.THA != (types.MACAddress{}) { + return fmt.Errorf("Warning! Got an ARP packet with non-zero MAC address %s. ARP request ignored.", arp.THA.String()) + } + + // Prepare an answer to this request + answerPacket, err := NewPacket() + if err != nil { + common.LogFatal(common.Debug, err) + } + + InitARPReplyPacket(answerPacket, table.interfaceMAC, arp.SHA, types.ArrayToIPv4(arp.TPA), types.ArrayToIPv4(arp.SPA)) + vlan := pkt.GetVLAN() + if vlan != nil { + answerPacket.AddVLANTag(SwapBytesUint16(vlan.TCI)) + } + + answerPacket.SendPacket(table.portIndex) + return nil +} + +// LookupMACForIPv4 tries to find MAC address for specified IPv4 +// address. +func (table *NeighboursLookupTable) LookupMACForIPv4(ipv4 types.IPv4Address) (types.MACAddress, bool) { + v, found := table.ipv4Table.Load(ipv4) + if found { + return v.(types.MACAddress), true + } + return [types.EtherAddrLen]byte{}, false +} + +// SendARPRequestForIPv4 sends an ARP request for specified IPv4 +// address. If specified vlan tag is not zero, ARP request packet gets +// VLAN tag assigned to it. +func (table *NeighboursLookupTable) SendARPRequestForIPv4(ipv4 types.IPv4Address, vlan uint16) { + requestPacket, err := NewPacket() + if err != nil { + common.LogFatal(common.Debug, err) + } + + InitARPRequestPacket(requestPacket, table.interfaceMAC, table.ipv4InterfaceAddr, ipv4) + + if vlan != 0 { + requestPacket.AddVLANTag(vlan) + } + + requestPacket.SendPacket(table.portIndex) +} diff --git a/packet/packet.go b/packet/packet.go index 90f4a72c..2ad570e6 100644 --- a/packet/packet.go +++ b/packet/packet.go @@ -47,6 +47,7 @@ import ( . "github.com/intel-go/nff-go/common" "github.com/intel-go/nff-go/low" + "github.com/intel-go/nff-go/types" ) var mbufStructSize uintptr @@ -69,9 +70,9 @@ func init() { // EtherHdr L2 header from DPDK: lib/librte_ether/rte_ehter.h type EtherHdr struct { - DAddr [EtherAddrLen]uint8 // Destination address - SAddr [EtherAddrLen]uint8 // Source address - EtherType uint16 // Frame type + DAddr types.MACAddress // Destination address + SAddr types.MACAddress // Source address + EtherType uint16 // Frame type } func (hdr *EtherHdr) String() string { @@ -80,17 +81,17 @@ Ethernet Source: %s Ethernet Destination: %s `, hdr.EtherType, getEtherTypeName(hdr.EtherType), - MACToString(hdr.SAddr), - MACToString(hdr.DAddr)) + hdr.SAddr.String(), + hdr.DAddr.String()) } var ( etherTypeNameLookupTable = map[uint16]string{ - SwapIPV4Number: "IPv4", - SwapARPNumber: "ARP", - SwapVLANNumber: "VLAN", - SwapMPLSNumber: "MPLS", - SwapIPV6Number: "IPv6", + types.SwapIPV4Number: "IPv4", + types.SwapARPNumber: "ARP", + types.SwapVLANNumber: "VLAN", + types.SwapMPLSNumber: "MPLS", + types.SwapIPV6Number: "IPv6", } ) @@ -102,72 +103,55 @@ func getEtherTypeName(et uint16) string { return ret } -// MACToString return MAC address like string -func MACToString(mac [EtherAddrLen]uint8) string { - return fmt.Sprintf("%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]) -} - // IPv4Hdr L3 header from DPDK: lib/librte_net/rte_ip.h type IPv4Hdr struct { - VersionIhl uint8 // version and header length - TypeOfService uint8 // type of service - TotalLength uint16 // length of packet - PacketID uint16 // packet ID - FragmentOffset uint16 // fragmentation offset - TimeToLive uint8 // time to live - NextProtoID uint8 // protocol ID - HdrChecksum uint16 // header checksum - SrcAddr uint32 // source address - DstAddr uint32 // destination address -} - -func IPv4ToString(addr uint32) string { - return fmt.Sprintf("%d.%d.%d.%d", byte(addr), byte(addr>>8), byte(addr>>16), byte(addr>>24)) + VersionIhl uint8 // version and header length + TypeOfService uint8 // type of service + TotalLength uint16 // length of packet + PacketID uint16 // packet ID + FragmentOffset uint16 // fragmentation offset + TimeToLive uint8 // time to live + NextProtoID uint8 // protocol ID + HdrChecksum uint16 // header checksum + SrcAddr types.IPv4Address // source address + DstAddr types.IPv4Address // destination address } func (hdr *IPv4Hdr) String() string { r0 := " L3 protocol: IPv4\n" - r1 := " IPv4 Source: " + IPv4ToString(hdr.SrcAddr) + "\n" - r2 := " IPv4 Destination: " + IPv4ToString(hdr.DstAddr) + "\n" + r1 := " IPv4 Source: " + hdr.SrcAddr.String() + "\n" + r2 := " IPv4 Destination: " + hdr.DstAddr.String() + "\n" return r0 + r1 + r2 } // IPv6Hdr L3 header from DPDK: lib/librte_net/rte_ip.h type IPv6Hdr struct { - VtcFlow uint32 // IP version, traffic class & flow label - PayloadLen uint16 // IP packet length - includes sizeof(ip_header) - Proto uint8 // Protocol, next header - HopLimits uint8 // Hop limits - SrcAddr [IPv6AddrLen]uint8 // IP address of source host - DstAddr [IPv6AddrLen]uint8 // IP address of destination host(s) -} - -func IPv6ToString(addr [IPv6AddrLen]uint8) string { - return fmt.Sprintf("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", - addr[0], addr[1], addr[2], addr[3], - addr[4], addr[5], addr[6], addr[7], - addr[8], addr[9], addr[10], addr[11], - addr[12], addr[13], addr[14], addr[15]) + VtcFlow uint32 // IP version, traffic class & flow label + PayloadLen uint16 // IP packet length - includes sizeof(ip_header) + Proto uint8 // Protocol, next header + HopLimits uint8 // Hop limits + SrcAddr types.IPv6Address // IP address of source host + DstAddr types.IPv6Address // IP address of destination host(s) } func (hdr *IPv6Hdr) String() string { return fmt.Sprintf(` L3 protocol: IPv6 IPv6 Source: %s IPv6 Destination %s -`, IPv6ToString(hdr.SrcAddr), IPv6ToString(hdr.DstAddr)) +`, hdr.SrcAddr.String(), hdr.DstAddr.String()) } // TCPHdr L4 header from DPDK: lib/librte_net/rte_tcp.h type TCPHdr struct { - SrcPort uint16 // TCP source port - DstPort uint16 // TCP destination port - SentSeq uint32 // TX data sequence number - RecvAck uint32 // RX data acknowledgement sequence number - DataOff uint8 // Data offset - TCPFlags TCPFlags // TCP flags - RxWin uint16 // RX flow control window - Cksum uint16 // TCP checksum - TCPUrp uint16 // TCP urgent pointer, if any + SrcPort uint16 // TCP source port + DstPort uint16 // TCP destination port + SentSeq uint32 // TX data sequence number + RecvAck uint32 // RX data acknowledgement sequence number + DataOff uint8 // Data offset + TCPFlags types.TCPFlags // TCP flags + RxWin uint16 // RX flow control window + Cksum uint16 // TCP checksum + TCPUrp uint16 // TCP urgent pointer, if any } func (hdr *TCPHdr) String() string { @@ -235,7 +219,7 @@ type Packet struct { func (packet *Packet) unparsed() unsafe.Pointer { ether := unsafe.Pointer(packet.Ether) - return unsafe.Pointer(uintptr(ether) + EtherLen) + return unsafe.Pointer(uintptr(ether) + types.EtherLen) } // StartAtOffset function return pointer to first byte of packet @@ -252,7 +236,7 @@ func (packet *Packet) ParseL3() { // GetIPv4 ensures if EtherType is IPv4 and casts L3 pointer to IPv4Hdr type. func (packet *Packet) GetIPv4() *IPv4Hdr { - if packet.Ether.EtherType == SwapBytesUint16(IPV4Number) { + if packet.Ether.EtherType == SwapBytesUint16(types.IPV4Number) { return (*IPv4Hdr)(packet.L3) } return nil @@ -265,7 +249,7 @@ func (packet *Packet) GetIPv4NoCheck() *IPv4Hdr { // GetARP ensures if EtherType is ARP and casts L3 pointer to ARPHdr type. func (packet *Packet) GetARP() *ARPHdr { - if packet.Ether.EtherType == SwapBytesUint16(ARPNumber) { + if packet.Ether.EtherType == SwapBytesUint16(types.ARPNumber) { return (*ARPHdr)(packet.L3) } return nil @@ -278,7 +262,7 @@ func (packet *Packet) GetARPNoCheck() *ARPHdr { // GetIPv6 ensures if EtherType is IPv6 and cast L3 pointer to IPv6Hdr type. func (packet *Packet) GetIPv6() *IPv6Hdr { - if packet.Ether.EtherType == SwapBytesUint16(IPV6Number) { + if packet.Ether.EtherType == SwapBytesUint16(types.IPV6Number) { return (*IPv6Hdr)(packet.L3) } return nil @@ -297,12 +281,12 @@ func (packet *Packet) ParseL4ForIPv4() { // ParseL4ForIPv6 set L4 to start of L4 header, if L3 protocol is IPv6. func (packet *Packet) ParseL4ForIPv6() { - packet.L4 = unsafe.Pointer(uintptr(packet.L3) + uintptr(IPv6Len)) + packet.L4 = unsafe.Pointer(uintptr(packet.L3) + uintptr(types.IPv6Len)) } // GetTCPForIPv4 ensures if L4 type is TCP and cast L4 pointer to TCPHdr type. func (packet *Packet) GetTCPForIPv4() *TCPHdr { - if packet.GetIPv4NoCheck().NextProtoID == TCPNumber { + if packet.GetIPv4NoCheck().NextProtoID == types.TCPNumber { return (*TCPHdr)(packet.L4) } return nil @@ -315,7 +299,7 @@ func (packet *Packet) GetTCPNoCheck() *TCPHdr { // GetTCPForIPv6 ensures if L4 type is TCP and cast L4 pointer to *TCPHdr type. func (packet *Packet) GetTCPForIPv6() *TCPHdr { - if packet.GetIPv6NoCheck().Proto == TCPNumber { + if packet.GetIPv6NoCheck().Proto == types.TCPNumber { return (*TCPHdr)(packet.L4) } return nil @@ -323,7 +307,7 @@ func (packet *Packet) GetTCPForIPv6() *TCPHdr { // GetUDPForIPv4 ensures if L4 type is UDP and cast L4 pointer to *UDPHdr type. func (packet *Packet) GetUDPForIPv4() *UDPHdr { - if packet.GetIPv4NoCheck().NextProtoID == UDPNumber { + if packet.GetIPv4NoCheck().NextProtoID == types.UDPNumber { return (*UDPHdr)(packet.L4) } return nil @@ -336,7 +320,7 @@ func (packet *Packet) GetUDPNoCheck() *UDPHdr { // GetUDPForIPv6 ensures if L4 type is UDP and cast L4 pointer to *UDPHdr type. func (packet *Packet) GetUDPForIPv6() *UDPHdr { - if packet.GetIPv6NoCheck().Proto == UDPNumber { + if packet.GetIPv6NoCheck().Proto == types.UDPNumber { return (*UDPHdr)(packet.L4) } return nil @@ -345,7 +329,7 @@ func (packet *Packet) GetUDPForIPv6() *UDPHdr { // GetICMPForIPv4 ensures if L4 type is ICMP and cast L4 pointer to *ICMPHdr type. // L3 supposed to be parsed before and of IPv4 type. func (packet *Packet) GetICMPForIPv4() *ICMPHdr { - if packet.GetIPv4NoCheck().NextProtoID == ICMPNumber { + if packet.GetIPv4NoCheck().NextProtoID == types.ICMPNumber { return (*ICMPHdr)(packet.L4) } return nil @@ -359,7 +343,7 @@ func (packet *Packet) GetICMPNoCheck() *ICMPHdr { // GetICMPForIPv6 ensures if L4 type is ICMP and cast L4 pointer to *ICMPHdr type. // L3 supposed to be parsed before and of IPv6 type. func (packet *Packet) GetICMPForIPv6() *ICMPHdr { - if packet.GetIPv6NoCheck().Proto == ICMPv6Number { + if packet.GetIPv6NoCheck().Proto == types.ICMPv6Number { return (*ICMPHdr)(packet.L4) } return nil @@ -407,14 +391,14 @@ func (packet *Packet) ParseAllKnownL4ForIPv6() (*TCPHdr, *UDPHdr, *ICMPHdr) { // ParseL7 fills pointers to all supported headers and data field. func (packet *Packet) ParseL7(protocol uint) { switch protocol { - case TCPNumber: + case types.TCPNumber: packet.Data = unsafe.Pointer(uintptr(packet.L4) + uintptr(((*TCPHdr)(packet.L4)).DataOff&0xf0)>>2) - case UDPNumber: - packet.Data = unsafe.Pointer(uintptr(packet.L4) + uintptr(UDPLen)) - case ICMPNumber: + case types.UDPNumber: + packet.Data = unsafe.Pointer(uintptr(packet.L4) + uintptr(types.UDPLen)) + case types.ICMPNumber: fallthrough - case ICMPv6Number: - packet.Data = unsafe.Pointer(uintptr(packet.L4) + uintptr(ICMPLen)) + case types.ICMPv6Number: + packet.Data = unsafe.Pointer(uintptr(packet.L4) + uintptr(types.ICMPLen)) } } @@ -436,9 +420,9 @@ func (packet *Packet) ParseData() int { if pktTCP != nil { packet.Data = unsafe.Pointer(uintptr(packet.L4) + uintptr(((*TCPHdr)(packet.L4)).DataOff&0xf0)>>2) } else if pktUDP != nil { - packet.Data = unsafe.Pointer(uintptr(packet.L4) + uintptr(UDPLen)) + packet.Data = unsafe.Pointer(uintptr(packet.L4) + uintptr(types.UDPLen)) } else if pktICMP != nil { - packet.Data = unsafe.Pointer(uintptr(packet.L4) + uintptr(ICMPLen)) + packet.Data = unsafe.Pointer(uintptr(packet.L4) + uintptr(types.ICMPLen)) } else { return -1 } @@ -495,7 +479,7 @@ func GeneratePacketFromByte(packet *Packet, data []byte) bool { // InitEmptyPacket initializes input packet with preallocated plSize of bytes for payload // and init pointer to Ethernet header. func InitEmptyPacket(packet *Packet, plSize uint) bool { - bufSize := plSize + EtherLen + bufSize := plSize + types.EtherLen if low.AppendMbuf(packet.CMbuf, bufSize) == false { LogWarning(Debug, "InitEmptyPacket: Cannot append mbuf") return false @@ -505,7 +489,7 @@ func InitEmptyPacket(packet *Packet, plSize uint) bool { } func fillIPv4Default(packet *Packet, plLen uint16, nextProto uint8) { - packet.GetIPv4NoCheck().VersionIhl = IPv4VersionIhl + packet.GetIPv4NoCheck().VersionIhl = types.IPv4VersionIhl packet.GetIPv4NoCheck().TotalLength = SwapBytesUint16(plLen) packet.GetIPv4NoCheck().NextProtoID = nextProto packet.GetIPv4NoCheck().TimeToLive = 64 @@ -513,7 +497,7 @@ func fillIPv4Default(packet *Packet, plLen uint16, nextProto uint8) { func fillIPv6Default(packet *Packet, totalLen uint16, nextProto uint8) { packet.GetIPv6NoCheck().PayloadLen = SwapBytesUint16(totalLen) - packet.GetIPv6NoCheck().VtcFlow = IPv6VtcFlow + packet.GetIPv6NoCheck().VtcFlow = types.IPv6VtcFlow packet.GetIPv6NoCheck().Proto = nextProto packet.GetIPv6NoCheck().HopLimits = 255 } @@ -523,20 +507,20 @@ func fillIPv6Default(packet *Packet, totalLen uint16, nextProto uint8) { func InitEmptyIPv4Packet(packet *Packet, plSize uint) bool { // TODO After mandatory fields, IPv4 header optionally may have options of variable length // Now pre-allocate space only for mandatory fields - bufSize := plSize + EtherLen + IPv4MinLen + bufSize := plSize + types.EtherLen + types.IPv4MinLen if low.AppendMbuf(packet.CMbuf, bufSize) == false { LogWarning(Debug, "InitEmptyIPv4Packet: Cannot append mbuf") return false } - packet.Ether.EtherType = SwapBytesUint16(IPV4Number) - packet.Data = unsafe.Pointer(uintptr(packet.unparsed()) + IPv4MinLen) + packet.Ether.EtherType = SwapBytesUint16(types.IPV4Number) + packet.Data = unsafe.Pointer(uintptr(packet.unparsed()) + types.IPv4MinLen) packet.ParseL3() - fillIPv4Default(packet, uint16(IPv4MinLen+plSize), NoNextHeader) + fillIPv4Default(packet, uint16(types.IPv4MinLen+plSize), types.NoNextHeader) if hwtxchecksum { packet.GetIPv4NoCheck().HdrChecksum = 0 - low.SetTXIPv4OLFlags(packet.CMbuf, EtherLen, IPv4MinLen) + low.SetTXIPv4OLFlags(packet.CMbuf, types.EtherLen, types.IPv4MinLen) } return true } @@ -544,28 +528,28 @@ func InitEmptyIPv4Packet(packet *Packet, plSize uint) bool { // InitEmptyIPv6Packet initializes input packet with preallocated plSize of bytes for payload // and init pointers to Ethernet and IPv6 headers. func InitEmptyIPv6Packet(packet *Packet, plSize uint) bool { - bufSize := plSize + EtherLen + IPv6Len + bufSize := plSize + types.EtherLen + types.IPv6Len if low.AppendMbuf(packet.CMbuf, bufSize) == false { LogWarning(Debug, "InitEmptyIPv6Packet: Cannot append mbuf") return false } - packet.Ether.EtherType = SwapBytesUint16(IPV6Number) - packet.Data = unsafe.Pointer(uintptr(packet.unparsed()) + IPv6Len) + packet.Ether.EtherType = SwapBytesUint16(types.IPV6Number) + packet.Data = unsafe.Pointer(uintptr(packet.unparsed()) + types.IPv6Len) packet.ParseL3() - fillIPv6Default(packet, uint16(plSize), NoNextHeader) + fillIPv6Default(packet, uint16(plSize), types.NoNextHeader) return true } // InitEmptyARPPacket initializes empty ARP packet func InitEmptyARPPacket(packet *Packet) bool { - var bufSize uint = EtherLen + ARPLen + var bufSize uint = types.EtherLen + types.ARPLen if low.AppendMbuf(packet.CMbuf, bufSize) == false { LogWarning(Debug, "InitEmptyARPPacket: Cannot append mbuf") return false } - packet.Ether.EtherType = SwapBytesUint16(ARPNumber) + packet.Ether.EtherType = SwapBytesUint16(types.ARPNumber) packet.ParseL3() return true } @@ -577,22 +561,22 @@ func InitEmptyARPPacket(packet *Packet) bool { func InitEmptyIPv4TCPPacket(packet *Packet, plSize uint) bool { // Now user cannot set explicitly optional fields, so len of header is supposed to be equal to TCPMinLen // TODO support variable header length (ask header length from user) - bufSize := plSize + EtherLen + IPv4MinLen + TCPMinLen + bufSize := plSize + types.EtherLen + types.IPv4MinLen + types.TCPMinLen if low.AppendMbuf(packet.CMbuf, bufSize) == false { LogWarning(Debug, "InitEmptyPacket: Cannot append mbuf") return false } - packet.Ether.EtherType = SwapBytesUint16(IPV4Number) + packet.Ether.EtherType = SwapBytesUint16(types.IPV4Number) packet.ParseL3() - fillIPv4Default(packet, uint16(IPv4MinLen+TCPMinLen+plSize), TCPNumber) + fillIPv4Default(packet, uint16(types.IPv4MinLen+types.TCPMinLen+plSize), types.TCPNumber) packet.ParseL4ForIPv4() - packet.GetTCPNoCheck().DataOff = TCPMinDataOffset + packet.GetTCPNoCheck().DataOff = types.TCPMinDataOffset packet.Data = unsafe.Pointer(uintptr(packet.L4) + uintptr(packet.GetTCPNoCheck().DataOff&0xf0)>>2) if hwtxchecksum { packet.GetIPv4NoCheck().HdrChecksum = 0 - low.SetTXIPv4TCPOLFlags(packet.CMbuf, EtherLen, IPv4MinLen) + low.SetTXIPv4TCPOLFlags(packet.CMbuf, types.EtherLen, types.IPv4MinLen) } return true } @@ -602,22 +586,22 @@ func InitEmptyIPv4TCPPacket(packet *Packet, plSize uint) bool { // header has minimum length. In fact length can be higher due to optional fields. // Now setting optional fields explicitly is not supported. func InitEmptyIPv4UDPPacket(packet *Packet, plSize uint) bool { - bufSize := plSize + EtherLen + IPv4MinLen + UDPLen + bufSize := plSize + types.EtherLen + types.IPv4MinLen + types.UDPLen if low.AppendMbuf(packet.CMbuf, bufSize) == false { LogWarning(Debug, "InitEmptyIPv4UDPPacket: Cannot append mbuf") return false } - packet.Ether.EtherType = SwapBytesUint16(IPV4Number) - packet.Data = unsafe.Pointer(uintptr(packet.unparsed()) + IPv4MinLen + UDPLen) + packet.Ether.EtherType = SwapBytesUint16(types.IPV4Number) + packet.Data = unsafe.Pointer(uintptr(packet.unparsed()) + types.IPv4MinLen + types.UDPLen) packet.ParseL3() - fillIPv4Default(packet, uint16(IPv4MinLen+UDPLen+plSize), UDPNumber) + fillIPv4Default(packet, uint16(types.IPv4MinLen+types.UDPLen+plSize), types.UDPNumber) packet.ParseL4ForIPv4() - packet.GetUDPNoCheck().DgramLen = SwapBytesUint16(uint16(UDPLen + plSize)) + packet.GetUDPNoCheck().DgramLen = SwapBytesUint16(uint16(types.UDPLen + plSize)) if hwtxchecksum { packet.GetIPv4NoCheck().HdrChecksum = 0 - low.SetTXIPv4UDPOLFlags(packet.CMbuf, EtherLen, IPv4MinLen) + low.SetTXIPv4UDPOLFlags(packet.CMbuf, types.EtherLen, types.IPv4MinLen) } return true } @@ -627,16 +611,16 @@ func InitEmptyIPv4UDPPacket(packet *Packet, plSize uint) bool { // header has minimum length. In fact length can be higher due to optional fields. // Now setting optional fields explicitly is not supported. func InitEmptyIPv4ICMPPacket(packet *Packet, plSize uint) bool { - bufSize := plSize + EtherLen + IPv4MinLen + ICMPLen + bufSize := plSize + types.EtherLen + types.IPv4MinLen + types.ICMPLen if low.AppendMbuf(packet.CMbuf, bufSize) == false { LogWarning(Debug, "InitEmptyIPv4ICMPPacket: Cannot append mbuf") return false } - packet.Ether.EtherType = SwapBytesUint16(IPV4Number) - packet.Data = unsafe.Pointer(uintptr(packet.unparsed()) + IPv4MinLen + ICMPLen) + packet.Ether.EtherType = SwapBytesUint16(types.IPV4Number) + packet.Data = unsafe.Pointer(uintptr(packet.unparsed()) + types.IPv4MinLen + types.ICMPLen) packet.ParseL3() - fillIPv4Default(packet, uint16(IPv4MinLen+ICMPLen+plSize), ICMPNumber) + fillIPv4Default(packet, uint16(types.IPv4MinLen+types.ICMPLen+plSize), types.ICMPNumber) packet.ParseL4ForIPv4() return true } @@ -647,21 +631,21 @@ func InitEmptyIPv4ICMPPacket(packet *Packet, plSize uint) bool { // Now setting optional fields explicitly is not supported. func InitEmptyIPv6TCPPacket(packet *Packet, plSize uint) bool { // TODO support variable header length (ask header length from user) - bufSize := plSize + EtherLen + IPv6Len + TCPMinLen + bufSize := plSize + types.EtherLen + types.IPv6Len + types.TCPMinLen if low.AppendMbuf(packet.CMbuf, bufSize) == false { LogWarning(Debug, "InitEmptyIPv6TCPPacket: Cannot append mbuf") return false } - packet.Ether.EtherType = SwapBytesUint16(IPV6Number) + packet.Ether.EtherType = SwapBytesUint16(types.IPV6Number) packet.ParseL3() - fillIPv6Default(packet, uint16(TCPMinLen+plSize), TCPNumber) + fillIPv6Default(packet, uint16(types.TCPMinLen+plSize), types.TCPNumber) packet.ParseL4ForIPv6() - packet.GetTCPNoCheck().DataOff = TCPMinDataOffset + packet.GetTCPNoCheck().DataOff = types.TCPMinDataOffset packet.Data = unsafe.Pointer(uintptr(packet.L4) + uintptr(packet.GetTCPNoCheck().DataOff&0xf0)>>2) if hwtxchecksum { - low.SetTXIPv6TCPOLFlags(packet.CMbuf, EtherLen, IPv6Len) + low.SetTXIPv6TCPOLFlags(packet.CMbuf, types.EtherLen, types.IPv6Len) } return true } @@ -671,21 +655,21 @@ func InitEmptyIPv6TCPPacket(packet *Packet, plSize uint) bool { // header has minimum length. In fact length can be higher due to optional fields. // Now setting optional fields explicitly is not supported. func InitEmptyIPv6UDPPacket(packet *Packet, plSize uint) bool { - bufSize := plSize + EtherLen + IPv6Len + UDPLen + bufSize := plSize + types.EtherLen + types.IPv6Len + types.UDPLen if low.AppendMbuf(packet.CMbuf, bufSize) == false { LogWarning(Debug, "InitEmptyIPv6UDPPacket: Cannot append mbuf") return false } - packet.Ether.EtherType = SwapBytesUint16(IPV6Number) - packet.Data = unsafe.Pointer(uintptr(packet.unparsed()) + IPv6Len + UDPLen) + packet.Ether.EtherType = SwapBytesUint16(types.IPV6Number) + packet.Data = unsafe.Pointer(uintptr(packet.unparsed()) + types.IPv6Len + types.UDPLen) packet.ParseL3() - fillIPv6Default(packet, uint16(UDPLen+plSize), UDPNumber) + fillIPv6Default(packet, uint16(types.UDPLen+plSize), types.UDPNumber) packet.ParseL4ForIPv6() - packet.GetUDPNoCheck().DgramLen = SwapBytesUint16(uint16(UDPLen + plSize)) + packet.GetUDPNoCheck().DgramLen = SwapBytesUint16(uint16(types.UDPLen + plSize)) if hwtxchecksum { - low.SetTXIPv6UDPOLFlags(packet.CMbuf, EtherLen, IPv6Len) + low.SetTXIPv6UDPOLFlags(packet.CMbuf, types.EtherLen, types.IPv6Len) } return true } @@ -693,16 +677,16 @@ func InitEmptyIPv6UDPPacket(packet *Packet, plSize uint) bool { // InitEmptyIPv6ICMPPacket initializes input packet with preallocated plSize of bytes for payload // and init pointers to Ethernet, IPv6 and ICMP headers. func InitEmptyIPv6ICMPPacket(packet *Packet, plSize uint) bool { - bufSize := plSize + EtherLen + IPv6Len + ICMPLen + bufSize := plSize + types.EtherLen + types.IPv6Len + types.ICMPLen if low.AppendMbuf(packet.CMbuf, bufSize) == false { LogWarning(Debug, "InitEmptyIPv6ICMPPacket: Cannot append mbuf") return false } - packet.Ether.EtherType = SwapBytesUint16(IPV6Number) - packet.Data = unsafe.Pointer(uintptr(packet.unparsed()) + IPv6Len + ICMPLen) + packet.Ether.EtherType = SwapBytesUint16(types.IPV6Number) + packet.Data = unsafe.Pointer(uintptr(packet.unparsed()) + types.IPv6Len + types.ICMPLen) packet.ParseL3() - fillIPv6Default(packet, uint16(ICMPLen+plSize), ICMPv6Number) + fillIPv6Default(packet, uint16(types.ICMPLen+plSize), types.ICMPv6Number) packet.ParseL4ForIPv6() return true } @@ -717,6 +701,10 @@ func SwapBytesUint32(x uint32) uint32 { return ((x & 0x000000ff) << 24) | ((x & 0x0000ff00) << 8) | ((x & 0x00ff0000) >> 8) | ((x & 0xff000000) >> 24) } +func SwapBytesIPv4Addr(x types.IPv4Address) types.IPv4Address { + return ((x & 0x000000ff) << 24) | ((x & 0x0000ff00) << 8) | ((x & 0x00ff0000) >> 8) | ((x & 0xff000000) >> 24) +} + // GetRawPacketBytes returns all bytes from this packet. Not zero-copy. func (packet *Packet) GetRawPacketBytes() []byte { return low.GetRawPacketBytesMbuf(packet.CMbuf) @@ -747,6 +735,7 @@ func (packet *Packet) GetPacketPayload() ([]byte, bool) { // EncapsulateHead adds bytes to packet. start - number of beginning byte, length - number of // added bytes. This function should be used to add bytes to the first half // of packet. Return false if error. +// You must not add NoPacketHeadChange option to SystemInit for using this function safely. // TODO change this for scattered packet case (multiple mbufs) func (packet *Packet) EncapsulateHead(start uint, length uint) bool { if low.PrependMbuf(packet.CMbuf, length) == false { @@ -777,6 +766,7 @@ func (packet *Packet) EncapsulateTail(start uint, length uint) bool { // DecapsulateHead removes bytes from packet. start - number of beginning byte, length - number of // removed bytes. This function should be used to remove bytes from the first half // of packet. Return false if error. +// You must not add NoPacketHeadChange option to SystemInit for using this function safely. // TODO change this for scattered packet case (multiple mbufs) func (packet *Packet) DecapsulateHead(start uint, length uint) bool { if low.AdjMbuf(packet.CMbuf, length) == false { @@ -817,21 +807,6 @@ func (packet *Packet) PacketBytesChange(start uint, bytes []byte) bool { return true } -// BytesToIPv4 converts four element address to uint32 representation -func BytesToIPv4(a byte, b byte, c byte, d byte) uint32 { - return uint32(d)<<24 | uint32(c)<<16 | uint32(b)<<8 | uint32(a) -} - -// ArrayToIPv4 converts four element array to uint32 representation -func ArrayToIPv4(a [IPv4AddrLen]byte) uint32 { - return uint32(a[3])<<24 | uint32(a[2])<<16 | uint32(a[1])<<8 | uint32(a[0]) -} - -// IPv4ToBytes converts four element address to uint32 representation -func IPv4ToBytes(v uint32) [IPv4AddrLen]byte { - return [IPv4AddrLen]uint8{byte(v), byte(v >> 8), byte(v >> 16), byte(v >> 24)} -} - // NewPacket shouldn't be used for performance critical allocations. // Allocate mbufs one by one is very inefficient. // FastGenerate or copy functions give developer a packet from previously bulk allocated set @@ -862,8 +837,8 @@ func SetNonPerfMempool(m *low.Mempool) { } type LPM struct { - tbl24 *([MaxLength]uint32) - tbl8 *([MaxLength]uint32) + tbl24 *([types.MaxLength]types.IPv4Address) + tbl8 *([types.MaxLength]types.IPv4Address) lpm unsafe.Pointer //C.struct_rte_lpm } @@ -884,7 +859,7 @@ func CreateLPM(name string, socket uint8, maxRules uint32, numberTbl8 uint32) *L // Heavily based on DPDK rte_lpm_lookup with constants from there // No error checking (lpm == NULL or nextHop == NULL) due to performance // User should check it manually -func (lpm *LPM) Lookup(ip uint32, nextHop *uint32) bool { +func (lpm *LPM) Lookup(ip types.IPv4Address, nextHop *types.IPv4Address) bool { tbl24_index := ip >> 8 tbl_entry := (*lpm.tbl24)[tbl24_index] // Copy tbl24 entry @@ -903,13 +878,13 @@ func (lpm *LPM) Lookup(ip uint32, nextHop *uint32) bool { // Add adds longest prefix match rule with specified ip, depth and nextHop // inside LPM table. Returns 0 if success and negative value otherwise -func (lpm *LPM) Add(ip uint32, depth uint8, nextHop uint32) int { +func (lpm *LPM) Add(ip types.IPv4Address, depth uint8, nextHop types.IPv4Address) int { return low.AddLPMRule(lpm.lpm, ip, depth, nextHop) } // Delete removes longest prefix match rule with diven ip and depth from // LPM table. Returns 0 if success and negative value otherwise -func (lpm *LPM) Delete(ip uint32, depth uint8) int { +func (lpm *LPM) Delete(ip types.IPv4Address, depth uint8) int { return low.DeleteLPMRule(lpm.lpm, ip, depth) } diff --git a/packet/packet_test.go b/packet/packet_test.go index 4b11b21e..28840b3f 100644 --- a/packet/packet_test.go +++ b/packet/packet_test.go @@ -6,14 +6,13 @@ package packet import ( "bytes" - "encoding/binary" "encoding/hex" "net" "reflect" "testing" "unsafe" - . "github.com/intel-go/nff-go/common" + "github.com/intel-go/nff-go/types" ) func init() { @@ -22,44 +21,44 @@ func init() { var MacHeader = [8]EtherHdr{ { - DAddr: [6]uint8{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}, - SAddr: [6]uint8{0x01, 0x11, 0x21, 0x31, 0x41, 0x51}, - EtherType: SwapBytesUint16(IPV4Number), + DAddr: types.MACAddress{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}, + SAddr: types.MACAddress{0x01, 0x11, 0x21, 0x31, 0x41, 0x51}, + EtherType: SwapBytesUint16(types.IPV4Number), }, { - DAddr: [6]uint8{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}, - SAddr: [6]uint8{0x01, 0x11, 0x21, 0x31, 0x41, 0x51}, - EtherType: SwapBytesUint16(IPV4Number), + DAddr: types.MACAddress{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}, + SAddr: types.MACAddress{0x01, 0x11, 0x21, 0x31, 0x41, 0x51}, + EtherType: SwapBytesUint16(types.IPV4Number), }, { - DAddr: [6]uint8{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}, - SAddr: [6]uint8{0x01, 0x11, 0x21, 0x31, 0x41, 0x52}, - EtherType: SwapBytesUint16(IPV4Number), + DAddr: types.MACAddress{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}, + SAddr: types.MACAddress{0x01, 0x11, 0x21, 0x31, 0x41, 0x52}, + EtherType: SwapBytesUint16(types.IPV4Number), }, { - DAddr: [6]uint8{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}, - SAddr: [6]uint8{0x01, 0x11, 0x21, 0x31, 0x41, 0x52}, - EtherType: SwapBytesUint16(IPV4Number), + DAddr: types.MACAddress{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}, + SAddr: types.MACAddress{0x01, 0x11, 0x21, 0x31, 0x41, 0x52}, + EtherType: SwapBytesUint16(types.IPV4Number), }, { - DAddr: [6]uint8{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}, - SAddr: [6]uint8{0x01, 0x11, 0x21, 0x31, 0x41, 0x52}, - EtherType: SwapBytesUint16(IPV4Number), + DAddr: types.MACAddress{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}, + SAddr: types.MACAddress{0x01, 0x11, 0x21, 0x31, 0x41, 0x52}, + EtherType: SwapBytesUint16(types.IPV4Number), }, { - DAddr: [6]uint8{0x00, 0x12, 0x22, 0x33, 0x44, 0x55}, - SAddr: [6]uint8{0x01, 0x11, 0x21, 0x31, 0x41, 0x52}, - EtherType: SwapBytesUint16(IPV4Number), + DAddr: types.MACAddress{0x00, 0x12, 0x22, 0x33, 0x44, 0x55}, + SAddr: types.MACAddress{0x01, 0x11, 0x21, 0x31, 0x41, 0x52}, + EtherType: SwapBytesUint16(types.IPV4Number), }, { - DAddr: [6]uint8{0x10, 0x11, 0x22, 0x33, 0x44, 0x55}, - SAddr: [6]uint8{0x01, 0x11, 0x21, 0x31, 0x41, 0x51}, - EtherType: SwapBytesUint16(IPV4Number), + DAddr: types.MACAddress{0x10, 0x11, 0x22, 0x33, 0x44, 0x55}, + SAddr: types.MACAddress{0x01, 0x11, 0x21, 0x31, 0x41, 0x51}, + EtherType: SwapBytesUint16(types.IPV4Number), }, { - DAddr: [6]uint8{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}, - SAddr: [6]uint8{0x01, 0x11, 0x21, 0x31, 0x41, 0x51}, - EtherType: SwapBytesUint16(IPV4Number), + DAddr: types.MACAddress{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}, + SAddr: types.MACAddress{0x01, 0x11, 0x21, 0x31, 0x41, 0x51}, + EtherType: SwapBytesUint16(types.IPV4Number), }, } @@ -436,8 +435,8 @@ func TestInitEmptyPacket(t *testing.T) { // Create empty packet, set Ether header fields pkt := getPacket() InitEmptyPacket(pkt, 0) - pkt.Ether.DAddr = [6]uint8{0x00, 0x11, 0x22, 0x33, 0x44, 0x55} - pkt.Ether.SAddr = [6]uint8{0x01, 0x11, 0x21, 0x31, 0x41, 0x51} + pkt.Ether.DAddr = types.MACAddress{0x00, 0x11, 0x22, 0x33, 0x44, 0x55} + pkt.Ether.SAddr = types.MACAddress{0x01, 0x11, 0x21, 0x31, 0x41, 0x51} // Create ground truth packet gtBuf, _ := hex.DecodeString(gtLineEther) @@ -445,7 +444,7 @@ func TestInitEmptyPacket(t *testing.T) { gtPkt := getPacket() GeneratePacketFromByte(gtPkt, gtBuf) - buf := (*[1 << 30]byte)(pkt.StartAtOffset(0))[:EtherLen] + buf := (*[1 << 30]byte)(pkt.StartAtOffset(0))[:types.EtherLen] // DEEP equal for whole packet buffer if !reflect.DeepEqual(buf, gtBuf) { t.Errorf("Incorrect result:\ngot: %x, \nwant: %x\n\n", buf, gtBuf) @@ -458,8 +457,8 @@ func TestInitEmptyIPv4Packet(t *testing.T) { InitEmptyIPv4Packet(pkt, testPlSize) dst := net.ParseIP("128.9.9.5").To4() src := net.ParseIP("127.0.0.1").To4() - pkt.GetIPv4().DstAddr = binary.LittleEndian.Uint32([]byte(dst)) - pkt.GetIPv4().SrcAddr = binary.LittleEndian.Uint32([]byte(src)) + pkt.GetIPv4().DstAddr = types.SliceToIPv4(dst) + pkt.GetIPv4().SrcAddr = types.SliceToIPv4(src) ptrData := (*Packetdata)(pkt.Data) ptrData.F1 = 0xddff ptrData.F2 = 0xaabb @@ -470,7 +469,7 @@ func TestInitEmptyIPv4Packet(t *testing.T) { gtPkt := getPacket() GeneratePacketFromByte(gtPkt, gtBuf) - size := EtherLen + IPv4MinLen + testPlSize + size := types.EtherLen + types.IPv4MinLen + testPlSize buf := (*[1 << 30]byte)(pkt.StartAtOffset(0))[:size] if !reflect.DeepEqual(buf, gtBuf) { t.Errorf("Incorrect result:\ngot: %x, \nwant: %x\n\n", buf, gtBuf) @@ -481,7 +480,7 @@ func TestInitEmptyIPv6Packet(t *testing.T) { // Create empty packet, set IPv6 header fields pkt := getPacket() InitEmptyIPv6Packet(pkt, testPlSize) - pkt.GetIPv6().SrcAddr = [16]uint8{0xde, 0xad, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xbe, 0xef} + pkt.GetIPv6().SrcAddr = types.IPv6Address{0xde, 0xad, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xbe, 0xef} ptrData := (*Packetdata)(pkt.Data) ptrData.F1 = 0xddff ptrData.F2 = 0xaabb @@ -491,7 +490,7 @@ func TestInitEmptyIPv6Packet(t *testing.T) { gtPkt := getPacket() GeneratePacketFromByte(gtPkt, gtBuf) - size := EtherLen + IPv6Len + testPlSize + size := types.EtherLen + types.IPv6Len + testPlSize buf := (*[1 << 30]byte)(pkt.StartAtOffset(0))[:size] if !reflect.DeepEqual(buf, gtBuf) { t.Errorf("Incorrect result:\ngot: %x, \nwant: %x\n\n", buf, gtBuf) @@ -513,7 +512,7 @@ func TestInitEmptyIPv4TCPPacket(t *testing.T) { gtPkt := getPacket() GeneratePacketFromByte(gtPkt, gtBuf) - size := EtherLen + IPv4MinLen + TCPMinLen + testPlSize + size := types.EtherLen + types.IPv4MinLen + types.TCPMinLen + testPlSize buf := (*[1 << 30]byte)(pkt.StartAtOffset(0))[:size] if !reflect.DeepEqual(buf, gtBuf) { t.Errorf("Incorrect result:\ngot: %x, \nwant: %x\n\n", buf, gtBuf) @@ -535,7 +534,7 @@ func TestInitEmptyIPv4UDPPacket(t *testing.T) { gtPkt := getPacket() GeneratePacketFromByte(gtPkt, gtBuf) - size := EtherLen + IPv4MinLen + UDPLen + testPlSize + size := types.EtherLen + types.IPv4MinLen + types.UDPLen + testPlSize buf := (*[1 << 30]byte)(pkt.StartAtOffset(0))[:size] if !reflect.DeepEqual(buf, gtBuf) { t.Errorf("Incorrect result:\ngot: %x, \nwant: %x\n\n", buf, gtBuf) @@ -557,7 +556,7 @@ func TestInitEmptyIPv6TCPPacket(t *testing.T) { gtPkt := getPacket() GeneratePacketFromByte(gtPkt, gtBuf) - size := EtherLen + IPv6Len + TCPMinLen + testPlSize + size := types.EtherLen + types.IPv6Len + types.TCPMinLen + testPlSize buf := (*[1 << 30]byte)(pkt.StartAtOffset(0))[:size] if !reflect.DeepEqual(buf, gtBuf) { t.Errorf("Incorrect result:\ngot: %x, \nwant: %x\n\n", buf, gtBuf) @@ -579,7 +578,7 @@ func TestInitEmptyIPv6UDPPacket(t *testing.T) { gtPkt := getPacket() GeneratePacketFromByte(gtPkt, gtBuf) - size := EtherLen + IPv6Len + UDPLen + testPlSize + size := types.EtherLen + types.IPv6Len + types.UDPLen + testPlSize buf := (*[1 << 30]byte)(pkt.StartAtOffset(0))[:size] if !reflect.DeepEqual(buf, gtBuf) { t.Errorf("Incorrect result:\ngot: %x, \nwant: %x\n\n", buf, gtBuf) diff --git a/packet/utils_for_test.go b/packet/utils_for_test.go index bb8c5e70..022afc49 100644 --- a/packet/utils_for_test.go +++ b/packet/utils_for_test.go @@ -3,14 +3,13 @@ package packet import ( - "encoding/binary" "fmt" "log" "net" "os" - "github.com/intel-go/nff-go/common" "github.com/intel-go/nff-go/low" + "github.com/intel-go/nff-go/types" ) // isInit is common for all tests @@ -23,7 +22,7 @@ func tInitDPDK() { if isInit != true { argc, argv := low.InitDPDKArguments([]string{}) // burstSize=32, mbufNumber=8191, mbufCacheSize=250 - if err := low.InitDPDK(argc, argv, 32, 8191, 250, 0); err != nil { + if err := low.InitDPDK(argc, argv, 32, 8191, 250, 0, false); err != nil { log.Fatal(err) } nonPerfMempool = low.CreateMempool("Test") @@ -96,27 +95,27 @@ func getIPv6ICMPTestPacket() *Packet { func getARPRequestTestPacket() *Packet { pkt := getPacket() - sha := [common.EtherAddrLen]byte{0x01, 0x11, 0x21, 0x31, 0x41, 0x51} - spa := binary.LittleEndian.Uint32(net.ParseIP("127.0.0.1").To4()) - tpa := binary.LittleEndian.Uint32(net.ParseIP("128.9.9.5").To4()) + sha := types.MACAddress{0x01, 0x11, 0x21, 0x31, 0x41, 0x51} + spa := types.SliceToIPv4(net.ParseIP("127.0.0.1").To4()) + tpa := types.SliceToIPv4(net.ParseIP("128.9.9.5").To4()) InitARPRequestPacket(pkt, sha, spa, tpa) return pkt } func initEtherAddrs(pkt *Packet) { - pkt.Ether.SAddr = [common.EtherAddrLen]byte{0x01, 0x11, 0x21, 0x31, 0x41, 0x51} - pkt.Ether.DAddr = [common.EtherAddrLen]byte{0x0, 0x11, 0x22, 0x33, 0x44, 0x55} + pkt.Ether.SAddr = types.MACAddress{0x01, 0x11, 0x21, 0x31, 0x41, 0x51} + pkt.Ether.DAddr = types.MACAddress{0x0, 0x11, 0x22, 0x33, 0x44, 0x55} } func initIPv4Addrs(pkt *Packet) { - pkt.GetIPv4().SrcAddr = binary.LittleEndian.Uint32(net.ParseIP("127.0.0.1").To4()) - pkt.GetIPv4().DstAddr = binary.LittleEndian.Uint32(net.ParseIP("128.9.9.5").To4()) + pkt.GetIPv4().SrcAddr = types.SliceToIPv4(net.ParseIP("127.0.0.1").To4()) + pkt.GetIPv4().DstAddr = types.SliceToIPv4(net.ParseIP("128.9.9.5").To4()) } func initIPv6Addrs(pkt *Packet) { - copy(pkt.GetIPv6().SrcAddr[:], net.ParseIP("dead::beaf")[:common.IPv6AddrLen]) - copy(pkt.GetIPv6().DstAddr[:], net.ParseIP("dead::beaf")[:common.IPv6AddrLen]) + copy(pkt.GetIPv6().SrcAddr[:], net.ParseIP("dead::beaf")[:types.IPv6AddrLen]) + copy(pkt.GetIPv6().DstAddr[:], net.ParseIP("dead::beaf")[:types.IPv6AddrLen]) } func initPorts(pkt *Packet) { diff --git a/packet/vlan.go b/packet/vlan.go index 038455bc..6d397850 100644 --- a/packet/vlan.go +++ b/packet/vlan.go @@ -8,7 +8,7 @@ import ( "fmt" "unsafe" - . "github.com/intel-go/nff-go/common" + . "github.com/intel-go/nff-go/types" ) // VLANHdr 802.1Q VLAN header. We interpret it as an addition after diff --git a/packet/vlan_test.go b/packet/vlan_test.go index a553da08..102123ed 100644 --- a/packet/vlan_test.go +++ b/packet/vlan_test.go @@ -5,14 +5,13 @@ package packet import ( - "encoding/binary" "encoding/hex" "net" "reflect" "testing" "unsafe" - . "github.com/intel-go/nff-go/common" + "github.com/intel-go/nff-go/types" ) func init() { @@ -24,34 +23,34 @@ var ( gtLineIPv4TCPVLAN = "00400540ef240060089fb1f3810000200800450000288a1b00004006000083972015839720811770048a0000000100000d9550107c7000000000" MacHeaderVLAN = EtherHdr{ - DAddr: [6]uint8{0x00, 0x40, 0x05, 0x40, 0xef, 0x24}, - SAddr: [6]uint8{0x00, 0x60, 0x08, 0x9f, 0xb1, 0xf3}, - EtherType: SwapBytesUint16(VLANNumber), + DAddr: types.MACAddress{0x00, 0x40, 0x05, 0x40, 0xef, 0x24}, + SAddr: types.MACAddress{0x00, 0x60, 0x08, 0x9f, 0xb1, 0xf3}, + EtherType: SwapBytesUint16(types.VLANNumber), } VlanTag = VLANHdr{ TCI: SwapBytesUint16(32), - EtherType: SwapBytesUint16(IPV4Number), + EtherType: SwapBytesUint16(types.IPV4Number), } IPv4HeaderVLAN = IPv4Hdr{ VersionIhl: 0x45, PacketID: SwapBytesUint16(35355), TimeToLive: uint8(64), - NextProtoID: TCPNumber, + NextProtoID: types.TCPNumber, HdrChecksum: 0, - SrcAddr: binary.LittleEndian.Uint32([]byte(net.ParseIP("131.151.32.21").To4())), - DstAddr: binary.LittleEndian.Uint32([]byte(net.ParseIP("131.151.32.129").To4())), - TotalLength: SwapBytesUint16(uint16(IPv4MinLen + TCPMinLen)), + SrcAddr: types.SliceToIPv4([]byte(net.ParseIP("131.151.32.21").To4())), + DstAddr: types.SliceToIPv4([]byte(net.ParseIP("131.151.32.129").To4())), + TotalLength: SwapBytesUint16(uint16(types.IPv4MinLen + types.TCPMinLen)), } IPv6HeaderVLAN = IPv6Hdr{ VtcFlow: SwapBytesUint32(0x60 << 24), HopLimits: 255, - Proto: NoNextHeader, - SrcAddr: [16]byte{0x26, 0x07, 0xf2, 0xc0, 0xf0, 0x0f, + Proto: types.NoNextHeader, + SrcAddr: types.IPv6Address{0x26, 0x07, 0xf2, 0xc0, 0xf0, 0x0f, 0xb0, 0x01, 0x00, 0x00, 0x00, 0x00, 0xfa, 0xce, 0xb0, 0x0c}, - DstAddr: [16]byte{0x26, 0x07, 0xf2, 0xc0, 0xf0, 0x0f, + DstAddr: types.IPv6Address{0x26, 0x07, 0xf2, 0xc0, 0xf0, 0x0f, 0xb0, 0x01, 0x00, 0x00, 0x00, 0x00, 0xfa, 0xce, 0xb0, 0x0c}, } @@ -60,7 +59,7 @@ var ( DstPort: SwapBytesUint16(1162), SentSeq: SwapBytesUint32(1), RecvAck: SwapBytesUint32(3477), - TCPFlags: TCPFlagAck, + TCPFlags: types.TCPFlagAck, RxWin: SwapBytesUint16(31856), Cksum: 0, DataOff: 0x50, @@ -68,14 +67,14 @@ var ( ARPHeaderVLAN = ARPHdr{ HType: SwapBytesUint16(1), - PType: SwapBytesUint16(IPV4Number), - HLen: EtherAddrLen, - PLen: IPv4AddrLen, + PType: SwapBytesUint16(types.IPV4Number), + HLen: types.EtherAddrLen, + PLen: types.IPv4AddrLen, Operation: SwapBytesUint16(ARPRequest), SHA: MacHeaderVLAN.SAddr, - SPA: IPv4ToBytes(IPv4HeaderVLAN.SrcAddr), - THA: [EtherAddrLen]uint8{}, - TPA: IPv4ToBytes(IPv4HeaderVLAN.DstAddr), + SPA: types.IPv4ToBytes(IPv4HeaderVLAN.SrcAddr), + THA: types.MACAddress{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + TPA: types.IPv4ToBytes(IPv4HeaderVLAN.DstAddr), } ) @@ -119,7 +118,7 @@ func TestAddVLANTag(t *testing.T) { gtPkt := getPacket() GeneratePacketFromByte(gtPkt, gtBuf) - size := EtherLen + VLANLen + IPv4MinLen + TCPMinLen + size := types.EtherLen + types.VLANLen + types.IPv4MinLen + types.TCPMinLen buf := (*[1 << 30]byte)(unsafe.Pointer(pkt.StartAtOffset(0)))[:size] if !reflect.DeepEqual(buf, gtBuf) { t.Errorf("Incorrect result:\ngot: %x, \nwant: %x\n\n", buf, gtBuf) @@ -165,24 +164,24 @@ func TestGetEtherType(t *testing.T) { InitEmptyIPv4Packet(pkt, 0) etherType := pkt.GetEtherType() - if etherType != IPV4Number { + if etherType != types.IPV4Number { t.Errorf("Incorrect GetEtherType result:\ngot: %x, \nwant: %x\n\n", - etherType, IPV4Number) + etherType, types.IPV4Number) t.FailNow() } pkt.AddVLANTag(12) etherType = pkt.GetEtherType() - if etherType != IPV4Number { + if etherType != types.IPV4Number { t.Errorf("Incorrect GetEtherType result after vlan add:\ngot: %x, \nwant: %x\n\n", - etherType, IPV4Number) + etherType, types.IPV4Number) t.FailNow() } etherType = SwapBytesUint16(pkt.Ether.EtherType) - if etherType != VLANNumber { + if etherType != types.VLANNumber { t.Errorf("Incorrect pkt.Ether.EtherType after vlan add:\ngot: %x, \nwant: %x\n\n", - etherType, VLANNumber) + etherType, types.VLANNumber) t.FailNow() } } diff --git a/test/framework/main/report_compare.go b/test/framework/main/report_compare.go index 364af5cc..0cdb7b52 100644 --- a/test/framework/main/report_compare.go +++ b/test/framework/main/report_compare.go @@ -18,11 +18,21 @@ import ( ) var ( - user = "" - password = "" + user = "" + password = "" + substringFilter = "" ) -type inputReportsArray []*test.TestsuiteReport +type inputReport struct { + report *test.TestsuiteReport + fileName string +} + +func (ir *inputReport) String() string { + return ir.report.Timestamp + "(" + ir.fileName + ")" +} + +type inputReportsArray []inputReport func (ira *inputReportsArray) String() string { return fmt.Sprint(*ira) @@ -66,18 +76,18 @@ func (ira *inputReportsArray) Set(value string) error { } else if report.Timestamp == "" { return fmt.Errorf("Bad report from %s contains no timestamp string", value) } - *ira = append(*ira, &report) + *ira = append(*ira, inputReport{&report, value}) return nil } type reportProcessorResult interface { // compare returns true if this report is better than another report - compare(another reportProcessorResult) (bool, error) + compare(another reportProcessorResult, threshold float64) (bool, error) String() string } type reportProcessor interface { - process(*test.TestsuiteReport, bool) reportProcessorResult + process(*inputReport, bool) reportProcessorResult } type comparisonMode struct { @@ -148,6 +158,11 @@ error if any of new reports is worse than base report. `) flag.StringVar(&user, "u", "", "User name to authenticate with for HTTP(S) connection") flag.StringVar(&password, "p", "", "Password to authenticate with for HTTP(S) connection") + flag.StringVar(&substringFilter, "s", "", "Substring filter for test names, e.g. \"-s _off_\".") + threshold := flag.Float64("t", 5.0, `Threshold in percents by which +values in new report may be below values in original report. +E.g. "-t 5" specifies threshold of 5%. If new report value +shows -5% or below, it is considered worse than original.`) verbose := flag.Bool("v", false, "Verbose mode") flag.Parse() @@ -158,22 +173,25 @@ error if any of new reports is worse than base report. values := make([]reportProcessorResult, len(reports)) for iii := range reports { - values[iii] = processMode.process(reports[iii], *verbose) - fmt.Printf("Test report from %s: %s\n", reports[iii].Timestamp, values[iii].String()) + values[iii] = processMode.process(&reports[iii], *verbose) + fmt.Printf("Test report %s: %s\n", reports[iii].String(), values[iii].String()) } fmt.Printf("\n") exitCode := 0 for iii := 1; iii < len(reports); iii++ { - result, err := values[0].compare(values[iii]) + result, err := values[0].compare(values[iii], *threshold) if err != nil { fmt.Print(err) exitCode = 1 break } else if result { - fmt.Printf("New report %s is worse than base report %s\n", - reports[iii].Timestamp, reports[0].Timestamp) + fmt.Printf("With threshold %.2f%% new report %s is WORSE than base report %s.\n", + *threshold, reports[iii].String(), reports[0].String()) exitCode = 1 + } else { + fmt.Printf("With threshold %.2f%% new report %s is OK compared to base report %s.\n", + *threshold, reports[iii].String(), reports[0].String()) } } os.Exit(exitCode) @@ -181,6 +199,7 @@ error if any of new reports is worse than base report. type geomeanProcessorResult struct { reportProcessorResult + report *inputReport geomeanPkts, geomeanMbits, geomeanCores float64 } @@ -198,32 +217,42 @@ type geomeanProcessorResultMbits struct { } // compare by average packets received to ensure transitivity -func (gp geomeanProcessorResultPkts) compare(another reportProcessorResult) (bool, error) { +func (gp geomeanProcessorResultPkts) compare(another reportProcessorResult, threshold float64) (bool, error) { a := another.(geomeanProcessorResultPkts) - return gp.geomeanPkts > a.geomeanPkts, nil + percent := (float64(a.geomeanPkts) - float64(gp.geomeanPkts)) / float64(gp.geomeanPkts) * 100.0 + fmt.Printf("Report %s is %.2f%% of report %s\n", a.report.String(), percent, gp.report.String()) + return percent+threshold < 0.0, nil } // compare by average packets received to ensure transitivity -func (gp geomeanProcessorResultMbits) compare(another reportProcessorResult) (bool, error) { +func (gp geomeanProcessorResultMbits) compare(another reportProcessorResult, threshold float64) (bool, error) { a := another.(geomeanProcessorResultMbits) - return gp.geomeanMbits > a.geomeanMbits, nil + percent := (float64(a.geomeanMbits) - float64(gp.geomeanMbits)) / float64(gp.geomeanMbits) * 100.0 + fmt.Printf("Report %s is %.2f%% of report %s\n", a.report.String(), percent, gp.report.String()) + return percent+threshold < 0.0, nil } type geomeanProcessor struct { reportProcessor } -func (gp geomeanProcessor) process(tr *test.TestsuiteReport, verbose bool) reportProcessorResult { +func (gp geomeanProcessor) process(ir *inputReport, verbose bool) reportProcessorResult { if verbose { - fmt.Printf("Parsing report %s\n", tr.Timestamp) + fmt.Printf("Parsing report %s\n", ir.report.Timestamp) } sumlogPkts := 0.0 sumlogMbits := 0.0 sumlogCores := 0.0 - for iii := range tr.Tests { - ttt := tr.Tests[iii] + for iii := range ir.report.Tests { + ttt := ir.report.Tests[iii] + if strings.Index(ttt.TestName, substringFilter) == -1 { + if verbose { + fmt.Printf("Test %s skipped\n", ttt.TestName) + } + continue + } cores := 0 if ttt.CoresStats != nil { @@ -248,9 +277,10 @@ func (gp geomeanProcessor) process(tr *test.TestsuiteReport, verbose bool) repor } return geomeanProcessorResult{ - geomeanPkts: math.Exp2(sumlogPkts / float64(len(tr.Tests))), - geomeanMbits: math.Exp2(sumlogMbits / float64(len(tr.Tests))), - geomeanCores: math.Exp2(sumlogCores / float64(len(tr.Tests))), + report: ir, + geomeanPkts: math.Exp2(sumlogPkts / float64(len(ir.report.Tests))), + geomeanMbits: math.Exp2(sumlogMbits / float64(len(ir.report.Tests))), + geomeanCores: math.Exp2(sumlogCores / float64(len(ir.report.Tests))), } } @@ -262,15 +292,15 @@ type geomeanProcessorMbits struct { geomeanProcessor } -func (gp geomeanProcessorPkts) process(tr *test.TestsuiteReport, verbose bool) reportProcessorResult { +func (gp geomeanProcessorPkts) process(ir *inputReport, verbose bool) reportProcessorResult { return geomeanProcessorResultPkts{ - geomeanProcessorResult: gp.geomeanProcessor.process(tr, verbose).(geomeanProcessorResult), + geomeanProcessorResult: gp.geomeanProcessor.process(ir, verbose).(geomeanProcessorResult), } } -func (gp geomeanProcessorMbits) process(tr *test.TestsuiteReport, verbose bool) reportProcessorResult { +func (gp geomeanProcessorMbits) process(ir *inputReport, verbose bool) reportProcessorResult { return geomeanProcessorResultMbits{ - geomeanProcessorResult: gp.geomeanProcessor.process(tr, verbose).(geomeanProcessorResult), + geomeanProcessorResult: gp.geomeanProcessor.process(ir, verbose).(geomeanProcessorResult), } } @@ -287,8 +317,8 @@ func (tr *testResult) String() string { type sbsProcessorResult struct { reportProcessorResult - suiteName string - results []testResult + report *inputReport + results []testResult } const indentString = " " @@ -298,18 +328,18 @@ const mbitsHeader = " Mbit/s " const coresHeader = " cores" const percents = " pkts/s Mbit/s cores" -func (spr sbsProcessorResult) compare(another reportProcessorResult) (bool, error) { +func (spr sbsProcessorResult) compare(another reportProcessorResult, threshold float64) (bool, error) { a := another.(sbsProcessorResult) if len(spr.results) != len(a.results) { - return false, fmt.Errorf("Reports %s and %s have different number of tests.", spr.suiteName, a.suiteName) + return false, fmt.Errorf("Reports %s and %s have different number of tests.", spr.report.String(), a.report.String()) } col1Len := 0 for iii := range spr.results { if spr.results[iii].name != a.results[iii].name { return false, fmt.Errorf("Reports %s has test named %s while report %s has test named %s. Cannot compare them.", - spr.suiteName, spr.results[iii].name, a.suiteName, a.results[iii].name) + spr.report.String(), spr.results[iii].name, a.report.String(), a.results[iii].name) } if col1Len < len(spr.results[iii].name) { col1Len = len(spr.results[iii].name) @@ -318,23 +348,23 @@ func (spr sbsProcessorResult) compare(another reportProcessorResult) (bool, erro sep2Len := 1 col2Len := len(pktsHeader) + len(mbitsHeader) + len(coresHeader) + sep2Len - if len(spr.suiteName) > col2Len { - sep2Len += len(spr.suiteName) - col2Len - col2Len = len(spr.suiteName) + if len(spr.report.report.Timestamp) > col2Len { + sep2Len += len(spr.report.report.Timestamp) - col2Len + col2Len = len(spr.report.report.Timestamp) } sep3Len := 1 col3Len := len(pktsHeader) + len(mbitsHeader) + len(coresHeader) + sep3Len - if len(a.suiteName) > col2Len { - sep3Len += len(a.suiteName) - col3Len - col3Len = len(a.suiteName) + if len(a.report.report.Timestamp) > col2Len { + sep3Len += len(a.report.report.Timestamp) - col3Len + col3Len = len(a.report.report.Timestamp) } // Print header with report names fmt.Printf("%*s | %*s | %*s\n", col1Len, "", - col2Len, spr.suiteName, - col3Len, a.suiteName) + col2Len, spr.report.report.Timestamp, + col3Len, a.report.report.Timestamp) // Print header with units fmt.Printf("%*s | %s%*s%s%s | %s%*s%s%s | %s\n", col1Len, "", @@ -371,15 +401,16 @@ type sbsProcessorResultMbits struct { } // Return true if at least one test result is better than another result -func (spr sbsProcessorResultPkts) compare(another reportProcessorResult) (bool, error) { +func (spr sbsProcessorResultPkts) compare(another reportProcessorResult, threshold float64) (bool, error) { a := another.(sbsProcessorResultPkts) - _, err := spr.sbsProcessorResult.compare(a.sbsProcessorResult) + _, err := spr.sbsProcessorResult.compare(a.sbsProcessorResult, threshold) if err != nil { return false, err } for iii := range spr.results { - if spr.results[iii].pkts > a.results[iii].pkts { + percent := (float64(a.results[iii].pkts) - float64(spr.results[iii].pkts)) / float64(spr.results[iii].pkts) * 100.0 + if percent+threshold < 0.0 { return true, nil } } @@ -387,15 +418,16 @@ func (spr sbsProcessorResultPkts) compare(another reportProcessorResult) (bool, } // Return true if at least one test result is better than another result -func (spr sbsProcessorResultMbits) compare(another reportProcessorResult) (bool, error) { +func (spr sbsProcessorResultMbits) compare(another reportProcessorResult, threshold float64) (bool, error) { a := another.(sbsProcessorResultMbits) - _, err := spr.sbsProcessorResult.compare(a.sbsProcessorResult) + _, err := spr.sbsProcessorResult.compare(a.sbsProcessorResult, threshold) if err != nil { return false, err } for iii := range spr.results { - if spr.results[iii].mbits > a.results[iii].mbits { + percent := (float64(a.results[iii].mbits) - float64(spr.results[iii].mbits)) / float64(spr.results[iii].mbits) * 100.0 + if percent+threshold < 0.0 { return true, nil } } @@ -406,11 +438,18 @@ type sbsProcessor struct { reportProcessor } -func (sp *sbsProcessor) process(tr *test.TestsuiteReport, verbose bool) reportProcessorResult { - out := make([]testResult, len(tr.Tests)) +func (sp *sbsProcessor) process(ir *inputReport, verbose bool) reportProcessorResult { + out := []testResult{} + + for iii := range ir.report.Tests { + ttt := ir.report.Tests[iii] + if strings.Index(ttt.TestName, substringFilter) == -1 { + if verbose { + fmt.Printf("Test %s skipped\n", ttt.TestName) + } + continue + } - for iii := range tr.Tests { - ttt := tr.Tests[iii] pgbd := ttt.PktgenBenchdata pkts := int64(0) mbits := int64(0) @@ -429,17 +468,17 @@ func (sp *sbsProcessor) process(tr *test.TestsuiteReport, verbose bool) reportPr fmt.Printf("Test %s: pkts: %d, mbits: %d, cores: %d\n", ttt.TestName, pkts, mbits, cores) } } - out[iii] = testResult{ + out = append(out, testResult{ name: ttt.TestName, pkts: pkts, mbits: mbits, cores: cores, - } + }) } return sbsProcessorResult{ - suiteName: tr.Timestamp, - results: out, + report: ir, + results: out, } } @@ -451,14 +490,14 @@ type sbsProcessorMbits struct { sbsProcessor } -func (sp sbsProcessorPkts) process(tr *test.TestsuiteReport, verbose bool) reportProcessorResult { +func (sp sbsProcessorPkts) process(ir *inputReport, verbose bool) reportProcessorResult { return sbsProcessorResultPkts{ - sbsProcessorResult: sp.sbsProcessor.process(tr, verbose).(sbsProcessorResult), + sbsProcessorResult: sp.sbsProcessor.process(ir, verbose).(sbsProcessorResult), } } -func (sp sbsProcessorMbits) process(tr *test.TestsuiteReport, verbose bool) reportProcessorResult { +func (sp sbsProcessorMbits) process(ir *inputReport, verbose bool) reportProcessorResult { return sbsProcessorResultMbits{ - sbsProcessorResult: sp.sbsProcessor.process(tr, verbose).(sbsProcessorResult), + sbsProcessorResult: sp.sbsProcessor.process(ir, verbose).(sbsProcessorResult), } } diff --git a/test/framework/main/tf.go b/test/framework/main/tf.go index 873454fc..25a8b70d 100644 --- a/test/framework/main/tf.go +++ b/test/framework/main/tf.go @@ -28,6 +28,7 @@ func main() { flag.StringVar(&configFile, "config", "config.json", "Name of config file to use") flag.StringVar(&directory, "directory", "", "Use `directory` to output log files instead of timestamp") flag.BoolVar(&test.NoDeleteContainersOnExit, "nodelete", false, "Do not remove containers after tests finish") + repeatFailed := flag.Int("repeat-failed", 1, "Number of times to repeat a test which failed. This includes tests that timed out.") flag.Parse() // Read config @@ -46,6 +47,6 @@ func main() { } // Start test execution - status := config.RunAllTests(directory, tl) + status := config.RunAllTests(directory, tl, *repeatFailed) os.Exit(status) } diff --git a/test/framework/testsuite.go b/test/framework/testsuite.go index c5a8cc7a..e066f8d3 100644 --- a/test/framework/testsuite.go +++ b/test/framework/testsuite.go @@ -223,7 +223,7 @@ func isTestInList(test *TestConfig, tl TestsList) bool { } // RunAllTests launches all tests. -func (config *TestsuiteConfig) RunAllTests(logdir string, tl TestsList) int { +func (config *TestsuiteConfig) RunAllTests(logdir string, tl TestsList, repeatCount int) int { report := StartReport(logdir) if report == nil { return 255 @@ -233,19 +233,38 @@ func (config *TestsuiteConfig) RunAllTests(logdir string, tl TestsList) int { sichan := make(chan os.Signal, 1) signal.Notify(sichan, os.Interrupt) - var totalTests, passedTests, failedTests []string + type TestInfo struct { + TestName string + Repeated int + } + + var totalTests, passedTests, failedTests []TestInfo + for iii := range config.Tests { test := &config.Tests[iii] if isTestInList(test, tl) { - tr := config.executeOneTest(test, logdir, sichan) + var tr *TestcaseReportInfo + ti := TestInfo{ + TestName: test.Name, + Repeated: 0, + } + + for ti.Repeated < repeatCount { + tr = config.executeOneTest(test, logdir, sichan) + ti.Repeated++ + if tr.Status == TestReportedPassed || tr.Status == TestInterrupted { + break + } + } + report.AddTestResult(tr) - totalTests = append(totalTests, test.Name) + totalTests = append(totalTests, ti) if tr.Status == TestReportedPassed { - passedTests = append(passedTests, test.Name) + passedTests = append(passedTests, ti) } else { - failedTests = append(failedTests, test.Name) + failedTests = append(failedTests, ti) } if tr.Status == TestInterrupted { @@ -256,9 +275,9 @@ func (config *TestsuiteConfig) RunAllTests(logdir string, tl TestsList) int { report.FinishReport() - LogInfo("EXECUTED TEST NAMES:", totalTests) - LogInfo("PASSED TEST NAMES:", passedTests) - LogInfo("FAILED TEST NAMES:", failedTests) + LogInfo("EXECUTED TEST NAMES AND REPEAT COUNT:", totalTests) + LogInfo("PASSED TEST NAMES AND REPEAT COUNT:", passedTests) + LogInfo("FAILED TEST NAMES AND REPEAT COUNT:", failedTests) LogInfo("TESTS EXECUTED:", len(totalTests)) LogInfo("PASSED:", len(passedTests)) LogInfo("FAILED:", len(failedTests)) diff --git a/test/performance/ipsec.go b/test/performance/ipsec.go index cf7cb9ec..b6ef07bf 100644 --- a/test/performance/ipsec.go +++ b/test/performance/ipsec.go @@ -11,9 +11,9 @@ import ( "hash" "unsafe" - "github.com/intel-go/nff-go/common" "github.com/intel-go/nff-go/flow" "github.com/intel-go/nff-go/packet" + "github.com/intel-go/nff-go/types" ) func main() { @@ -67,8 +67,8 @@ const mode1230 = 1230 const espHeadLen = 24 const authLen = 12 const espTailLen = authLen + 2 -const etherLen = common.EtherLen -const outerIPLen = common.IPv4MinLen +const etherLen = types.EtherLen +const outerIPLen = types.IPv4MinLen type espHeader struct { SPI uint32 @@ -90,8 +90,8 @@ func decapsulation(currentPacket *packet.Packet, context flow.UserContext) bool // Security Association switch packet.SwapBytesUint32(currentESPHeader.SPI) { case mode1230: - encryptionPart := (*[common.MaxLength]byte)(unsafe.Pointer(currentPacket.StartAtOffset(0)))[etherLen+outerIPLen+espHeadLen : length-authLen] - authPart := (*[common.MaxLength]byte)(unsafe.Pointer(currentPacket.StartAtOffset(0)))[etherLen+outerIPLen : length-authLen] + encryptionPart := (*[types.MaxLength]byte)(unsafe.Pointer(currentPacket.StartAtOffset(0)))[etherLen+outerIPLen+espHeadLen : length-authLen] + authPart := (*[types.MaxLength]byte)(unsafe.Pointer(currentPacket.StartAtOffset(0)))[etherLen+outerIPLen : length-authLen] if decapsulationSPI123(authPart, currentESPTail.Auth, currentESPHeader.IV, encryptionPart, context) == false { return false } @@ -157,16 +157,16 @@ func encapsulationSPI123(currentPacket *packet.Packet, context0 flow.UserContext currentESPTail := (*espTail)(currentPacket.StartAtOffset(uintptr(newLength) - espTailLen)) currentESPTail.paddingLen = paddingLength - currentESPTail.nextIP = common.IPNumber + currentESPTail.nextIP = types.IPNumber // Encryption - EncryptionPart := (*[common.MaxLength]byte)(currentPacket.StartAtOffset(0))[etherLen+outerIPLen+espHeadLen : newLength-authLen] + EncryptionPart := (*[types.MaxLength]byte)(currentPacket.StartAtOffset(0))[etherLen+outerIPLen+espHeadLen : newLength-authLen] context.modeEnc.(setIVer).SetIV(currentESPHeader.IV[:]) context.modeEnc.CryptBlocks(EncryptionPart, EncryptionPart) // Authentication context.mac123.Reset() - AuthPart := (*[common.MaxLength]byte)(currentPacket.StartAtOffset(0))[etherLen+outerIPLen : newLength-authLen] + AuthPart := (*[types.MaxLength]byte)(currentPacket.StartAtOffset(0))[etherLen+outerIPLen : newLength-authLen] context.mac123.Write(AuthPart) copy(currentESPTail.Auth[:], context.mac123.Sum(nil)) } diff --git a/test/performance/latency.go b/test/performance/latency.go index 5bed3d07..00ff6449 100644 --- a/test/performance/latency.go +++ b/test/performance/latency.go @@ -14,9 +14,9 @@ import ( "sync/atomic" "time" - "github.com/intel-go/nff-go/common" "github.com/intel-go/nff-go/flow" "github.com/intel-go/nff-go/packet" + "github.com/intel-go/nff-go/types" ) // This is 1 part of latency test @@ -35,7 +35,7 @@ var ( passedLimit uint64 = 80 packetSize uint64 = 128 - servDataSize uint64 = common.EtherLen + common.IPv4MinLen + common.UDPLen + crcLen + servDataSize uint64 = types.EtherLen + types.IPv4MinLen + types.UDPLen + crcLen outport uint16 inport uint16 @@ -107,7 +107,7 @@ func main() { payloadSize = packetSize - servDataSize // Create packet flow - outputFlow, err := flow.SetFastGenerator(generatePackets, speed, nil) + outputFlow, _, err := flow.SetFastGenerator(generatePackets, speed, nil) flow.CheckFatal(err) flow.CheckFatal(flow.SetSender(outputFlow, uint16(*outport))) diff --git a/test/performance/perf_gen.go b/test/performance/perf_gen.go index 66889b34..ddaf51a0 100644 --- a/test/performance/perf_gen.go +++ b/test/performance/perf_gen.go @@ -6,9 +6,9 @@ package main import ( "flag" - . "github.com/intel-go/nff-go/common" "github.com/intel-go/nff-go/flow" "github.com/intel-go/nff-go/packet" + "github.com/intel-go/nff-go/types" ) var size uint @@ -33,12 +33,12 @@ func main() { flag.Parse() pkts := uint64(speed * 1000 * 1000 / 8 / (size + 20)) - size = size - EtherLen - IPv4MinLen - TCPMinLen - 4 /* Ethernet checksum length*/ + size = size - types.EtherLen - types.IPv4MinLen - types.TCPMinLen - 4 /* Ethernet checksum length*/ flow.SystemInit(nil) - a, _ := flow.SetFastGenerator(generatePacket, pkts/2, *new(ctx)) - b, _ := flow.SetFastGenerator(generatePacket, pkts/2, *new(ctx)) + a, _, _ := flow.SetFastGenerator(generatePacket, pkts/2, *new(ctx)) + b, _, _ := flow.SetFastGenerator(generatePacket, pkts/2, *new(ctx)) flow.SetSender(a, 0) flow.SetSender(b, 0) @@ -54,8 +54,8 @@ func generatePacket(pkt *packet.Packet, context flow.UserContext) { ipv4 := pkt.GetIPv4() tcp := pkt.GetTCPForIPv4() - ipv4.DstAddr = packet.SwapBytesUint32(uint32(r)) - ipv4.SrcAddr = packet.SwapBytesUint32(uint32(r + 15)) + ipv4.DstAddr = packet.SwapBytesIPv4Addr(types.IPv4Address(r)) + ipv4.SrcAddr = packet.SwapBytesIPv4Addr(types.IPv4Address(r + 15)) tcp.DstPort = packet.SwapBytesUint16(r + 25) tcp.SrcPort = packet.SwapBytesUint16(r + 35) diff --git a/test/performance/perf_light.go b/test/performance/perf_light.go index 67b2a07c..7400f3b9 100644 --- a/test/performance/perf_light.go +++ b/test/performance/perf_light.go @@ -23,9 +23,10 @@ func main() { // Initialize NFF-GO library config := flow.Config{ - DisableScheduler: *noscheduler, - DPDKArgs: []string{*dpdkLogLevel}, - CPUList: *cores, + DisableScheduler: *noscheduler, + DPDKArgs: []string{*dpdkLogLevel}, + CPUList: *cores, + NoPacketHeadChange: true, } flow.CheckFatal(flow.SystemInit(&config)) diff --git a/test/performance/perf_main.go b/test/performance/perf_main.go index 0098a1ce..5cc83cf5 100644 --- a/test/performance/perf_main.go +++ b/test/performance/perf_main.go @@ -9,6 +9,7 @@ import ( "github.com/intel-go/nff-go/flow" "github.com/intel-go/nff-go/packet" + "github.com/intel-go/nff-go/types" ) var ( @@ -29,9 +30,10 @@ func main() { // Initialize NFF-GO library config := flow.Config{ - DisableScheduler: *noscheduler, - DPDKArgs: []string{*dpdkLogLevel}, - CPUList: *cores, + DisableScheduler: *noscheduler, + DPDKArgs: []string{*dpdkLogLevel}, + CPUList: *cores, + NoPacketHeadChange: true, } flow.CheckFatal(flow.SystemInit(&config)) @@ -52,10 +54,10 @@ func heavyFunc(currentPacket *packet.Packet, context flow.UserContext) { ipv4 := currentPacket.GetIPv4() if ipv4 != nil { T := ipv4.DstAddr - for j := uint32(0); j < uint32(load); j++ { + for j := types.IPv4Address(0); j < types.IPv4Address(load); j++ { T += j } - for i := uint32(0); i < uint32(loadRW); i++ { + for i := types.IPv4Address(0); i < types.IPv4Address(loadRW); i++ { ipv4.DstAddr = ipv4.SrcAddr + i } ipv4.SrcAddr = 263 + (T) diff --git a/test/performance/perf_seq.go b/test/performance/perf_seq.go index 06714f6c..3c91159a 100644 --- a/test/performance/perf_seq.go +++ b/test/performance/perf_seq.go @@ -9,6 +9,7 @@ import ( "github.com/intel-go/nff-go/flow" "github.com/intel-go/nff-go/packet" + "github.com/intel-go/nff-go/types" ) var ( @@ -29,9 +30,10 @@ func main() { // Initialize NFF-GO library config := flow.Config{ - DisableScheduler: *noscheduler, - DPDKArgs: []string{*dpdkLogLevel}, - CPUList: *cores, + DisableScheduler: *noscheduler, + DPDKArgs: []string{*dpdkLogLevel}, + CPUList: *cores, + NoPacketHeadChange: true, } flow.CheckFatal(flow.SystemInit(&config)) @@ -82,7 +84,7 @@ func heavyFunc(currentPacket *packet.Packet, context flow.UserContext) { if ipv4 != nil { T := (ipv4.DstAddr) for j := uint(0); j < load; j++ { - T += uint32(j) + T += types.IPv4Address(j) } ipv4.SrcAddr = 263 + (T) } diff --git a/test/stability/stabilityCommon/common.go b/test/stability/stabilityCommon/common.go index cba5930c..53ae4dd2 100644 --- a/test/stability/stabilityCommon/common.go +++ b/test/stability/stabilityCommon/common.go @@ -2,23 +2,24 @@ package stabilityCommon import ( "encoding/json" - "github.com/intel-go/nff-go/common" - "github.com/intel-go/nff-go/flow" - "github.com/intel-go/nff-go/packet" "log" "net" "os" + + "github.com/intel-go/nff-go/flow" + "github.com/intel-go/nff-go/packet" + "github.com/intel-go/nff-go/types" ) var ( config map[string][]string - dstMac0 [common.EtherAddrLen]uint8 - srcMac0 [common.EtherAddrLen]uint8 - dstMac1 [common.EtherAddrLen]uint8 - srcMac1 [common.EtherAddrLen]uint8 + dstMac0 [types.EtherAddrLen]uint8 + srcMac0 [types.EtherAddrLen]uint8 + dstMac1 [types.EtherAddrLen]uint8 + srcMac1 [types.EtherAddrLen]uint8 // First byte in MAC address has to be even because otherwise it // means multicast address which cannot be source address. - stubMac = [common.EtherAddrLen]uint8{0x10, 0x22, 0x33, 0x44, 0x55, 0x66} + stubMac = [types.EtherAddrLen]uint8{0x10, 0x22, 0x33, 0x44, 0x55, 0x66} // ModifyPacket is used to set src and dst MAC addresses for outgoing packets. ModifyPacket = []interface{}{modifyPacket0, modifyPacket1} ) @@ -26,7 +27,7 @@ var ( // ShouldBeSkipped return true for packets which are not expected to receive by test. // Return false only for expected IPv4 UDP packets. func ShouldBeSkipped(pkt *packet.Packet) bool { - if packet.SwapBytesUint16(pkt.Ether.EtherType) != common.IPV4Number { + if packet.SwapBytesUint16(pkt.Ether.EtherType) != types.IPV4Number { println("Not IPv4 packet, skip") return true } @@ -103,7 +104,7 @@ func readConfig(fileName string) error { return nil } -func printMAC(prompt string, mac [common.EtherAddrLen]uint8) { +func printMAC(prompt string, mac [types.EtherAddrLen]uint8) { log.Printf("%s: %02x:%02x:%02x:%02x:%02x:%02x\n", prompt, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]) } diff --git a/test/stability/testCksum/testCksum.go b/test/stability/testCksum/testCksum.go index 0c96702a..9eaebd05 100644 --- a/test/stability/testCksum/testCksum.go +++ b/test/stability/testCksum/testCksum.go @@ -14,9 +14,9 @@ import ( "time" "unsafe" - "github.com/intel-go/nff-go/common" "github.com/intel-go/nff-go/flow" "github.com/intel-go/nff-go/packet" + "github.com/intel-go/nff-go/types" "github.com/intel-go/nff-go/test/stability/stabilityCommon" "github.com/intel-go/nff-go/test/stability/testCksum/testCksumCommon" @@ -242,7 +242,10 @@ func executeTest(testScenario uint, shouldUseIPv4, shouldUseIPv6, shouldUseUDP, testDoneEvent = sync.NewCond(&m) // Create packet flow - generatedFlow, err := flow.SetFastGenerator(generatePacket, speed, initGenerator(13)) + generatedFlow, _, err := flow.SetFastGenerator(generatePacket, speed, initGenerator(13)) + if err != nil { + return err + } var finalFlow *flow.Flow if testScenario == generatePart { @@ -457,16 +460,16 @@ func initPacketCommon(emptyPacket *packet.Packet, length uint16, rnd *rand.Rand) func initPacketIPv4(emptyPacket *packet.Packet) { // Initialize IPv4 addresses emptyPacketIPv4 := emptyPacket.GetIPv4() - emptyPacketIPv4.SrcAddr = packet.SwapBytesUint32((192 << 24) | (168 << 16) | (1 << 8) | 1) - emptyPacketIPv4.DstAddr = packet.SwapBytesUint32((192 << 24) | (168 << 16) | (1 << 8) | 2) + emptyPacketIPv4.SrcAddr = packet.SwapBytesIPv4Addr((192 << 24) | (168 << 16) | (1 << 8) | 1) + emptyPacketIPv4.DstAddr = packet.SwapBytesIPv4Addr((192 << 24) | (168 << 16) | (1 << 8) | 2) emptyPacketIPv4.TimeToLive = 100 } func initPacketIPv6(emptyPacket *packet.Packet) { // Initialize IPv6 addresses emptyPacketIPv6 := emptyPacket.GetIPv6() - emptyPacketIPv6.SrcAddr = [common.IPv6AddrLen]uint8{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} - emptyPacketIPv6.DstAddr = [common.IPv6AddrLen]uint8{17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32} + emptyPacketIPv6.SrcAddr = types.IPv6Address{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} + emptyPacketIPv6.DstAddr = types.IPv6Address{17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32} } func initPacketUDP(emptyPacket *packet.Packet) { @@ -483,7 +486,7 @@ func initPacketTCP(emptyPacket *packet.Packet) { func initPacketICMP(emptyPacket *packet.Packet) { emptyPacketICMP := (*packet.ICMPHdr)(emptyPacket.L4) - emptyPacketICMP.Type = common.ICMPTypeEchoRequest + emptyPacketICMP.Type = types.ICMPTypeEchoRequest emptyPacketICMP.Identifier = 0xdead emptyPacketICMP.SeqNum = 0xbeef } @@ -535,12 +538,12 @@ func generateIPv4ICMP(emptyPacket *packet.Packet, rnd *rand.Rand) { pICMP := emptyPacket.GetICMPForIPv4() if hwol { pIPv4.HdrChecksum = 0 - emptyPacket.SetTXIPv4OLFlags(common.EtherLen, common.IPv4MinLen) + emptyPacket.SetTXIPv4OLFlags(types.EtherLen, types.IPv4MinLen) } else { pIPv4.HdrChecksum = packet.SwapBytesUint16(packet.CalculateIPv4Checksum(pIPv4)) } pICMP.Cksum = packet.SwapBytesUint16(packet.CalculateIPv4ICMPChecksum(pIPv4, pICMP, - unsafe.Pointer(uintptr(unsafe.Pointer(pICMP))+common.ICMPLen))) + unsafe.Pointer(uintptr(unsafe.Pointer(pICMP))+types.ICMPLen))) } func generateIPv6UDP(emptyPacket *packet.Packet, rnd *rand.Rand) { diff --git a/test/stability/testCksum/testCksumCommon/testCksumCommon.go b/test/stability/testCksum/testCksumCommon/testCksumCommon.go index 7d11d0bd..ae7da58a 100644 --- a/test/stability/testCksum/testCksumCommon/testCksumCommon.go +++ b/test/stability/testCksum/testCksumCommon/testCksumCommon.go @@ -5,8 +5,8 @@ package testCksumCommon import ( - "github.com/intel-go/nff-go/common" "github.com/intel-go/nff-go/packet" + "github.com/intel-go/nff-go/types" ) // Packetdata is a structure for packet pointer cast. @@ -23,7 +23,7 @@ var ( func CheckPacketChecksums(p *packet.Packet) bool { status := false - if p.GetEtherType() == common.IPV4Number { + if p.GetEtherType() == types.IPV4Number { pIPv4 := p.GetIPv4CheckVLAN() csum := packet.CalculateIPv4Checksum(pIPv4) l3status := true @@ -31,7 +31,7 @@ func CheckPacketChecksums(p *packet.Packet) bool { println("IPv4 checksum mismatch", packet.SwapBytesUint16(pIPv4.HdrChecksum), "should be", csum) l3status = false } - if pIPv4.NextProtoID == common.UDPNumber { + if pIPv4.NextProtoID == types.UDPNumber { pUDP := p.GetUDPForIPv4() csum := packet.CalculateIPv4UDPChecksum(pIPv4, pUDP, p.Data) if packet.SwapBytesUint16(pUDP.DgramCksum) != csum { @@ -44,7 +44,7 @@ func CheckPacketChecksums(p *packet.Packet) bool { } else { status = l3status } - } else if pIPv4.NextProtoID == common.TCPNumber { + } else if pIPv4.NextProtoID == types.TCPNumber { pTCP := p.GetTCPForIPv4() csum := packet.CalculateIPv4TCPChecksum(pIPv4, pTCP, p.Data) if packet.SwapBytesUint16(pTCP.Cksum) != csum { @@ -57,7 +57,7 @@ func CheckPacketChecksums(p *packet.Packet) bool { } else { status = l3status } - } else if pIPv4.NextProtoID == common.ICMPNumber { + } else if pIPv4.NextProtoID == types.ICMPNumber { pICMP := p.GetICMPForIPv4() csum := packet.CalculateIPv4ICMPChecksum(pIPv4, pICMP, p.Data) if packet.SwapBytesUint16(pICMP.Cksum) != csum { @@ -68,9 +68,9 @@ func CheckPacketChecksums(p *packet.Packet) bool { } else { println("Unknown IPv4 protocol number", pIPv4.NextProtoID) } - } else if p.GetEtherType() == common.IPV6Number { + } else if p.GetEtherType() == types.IPV6Number { pIPv6 := p.GetIPv6CheckVLAN() - if pIPv6.Proto == common.UDPNumber { + if pIPv6.Proto == types.UDPNumber { pUDP := p.GetUDPForIPv6() csum := packet.CalculateIPv6UDPChecksum(pIPv6, pUDP, p.Data) if packet.SwapBytesUint16(pUDP.DgramCksum) != csum { @@ -83,7 +83,7 @@ func CheckPacketChecksums(p *packet.Packet) bool { } else { status = true } - } else if pIPv6.Proto == common.TCPNumber { + } else if pIPv6.Proto == types.TCPNumber { pTCP := p.GetTCPForIPv6() csum := packet.CalculateIPv6TCPChecksum(pIPv6, pTCP, p.Data) if packet.SwapBytesUint16(pTCP.Cksum) != csum { @@ -96,7 +96,7 @@ func CheckPacketChecksums(p *packet.Packet) bool { } else { status = true } - } else if pIPv6.Proto == common.ICMPv6Number { + } else if pIPv6.Proto == types.ICMPv6Number { pICMP := p.GetICMPForIPv6() csum := packet.CalculateIPv6ICMPChecksum(pIPv6, pICMP, p.Data) if packet.SwapBytesUint16(pICMP.Cksum) != csum { @@ -116,32 +116,32 @@ func CheckPacketChecksums(p *packet.Packet) bool { // CalculateChecksum calculates checksum and writes to fields of packet. func CalculateChecksum(p *packet.Packet) { - if p.GetEtherType() == common.IPV4Number { + if p.GetEtherType() == types.IPV4Number { pIPv4 := p.GetIPv4NoCheck() pIPv4.HdrChecksum = packet.SwapBytesUint16(packet.CalculateIPv4Checksum(pIPv4)) - if pIPv4.NextProtoID == common.UDPNumber { + if pIPv4.NextProtoID == types.UDPNumber { pUDP := p.GetUDPForIPv4() pUDP.DgramCksum = packet.SwapBytesUint16(packet.CalculateIPv4UDPChecksum(pIPv4, pUDP, p.Data)) - } else if pIPv4.NextProtoID == common.TCPNumber { + } else if pIPv4.NextProtoID == types.TCPNumber { pTCP := p.GetTCPForIPv4() pTCP.Cksum = packet.SwapBytesUint16(packet.CalculateIPv4TCPChecksum(pIPv4, pTCP, p.Data)) - } else if pIPv4.NextProtoID == common.ICMPNumber { + } else if pIPv4.NextProtoID == types.ICMPNumber { pICMP := p.GetICMPForIPv4() pICMP.Cksum = packet.SwapBytesUint16(packet.CalculateIPv4ICMPChecksum(pIPv4, pICMP, p.Data)) } else { println("Unknown IPv4 protocol number", pIPv4.NextProtoID) println("TEST FAILED") } - } else if p.GetEtherType() == common.IPV6Number { + } else if p.GetEtherType() == types.IPV6Number { pIPv6 := p.GetIPv6NoCheck() - if pIPv6.Proto == common.UDPNumber { + if pIPv6.Proto == types.UDPNumber { pUDP := p.GetUDPForIPv6() pUDP.DgramCksum = packet.SwapBytesUint16(packet.CalculateIPv6UDPChecksum(pIPv6, pUDP, p.Data)) - } else if pIPv6.Proto == common.TCPNumber { + } else if pIPv6.Proto == types.TCPNumber { pTCP := p.GetTCPForIPv6() pTCP.Cksum = packet.SwapBytesUint16(packet.CalculateIPv6TCPChecksum(pIPv6, pTCP, p.Data)) - } else if pIPv6.Proto == common.ICMPv6Number { + } else if pIPv6.Proto == types.ICMPv6Number { pICMP := p.GetICMPForIPv6() pICMP.Cksum = packet.SwapBytesUint16(packet.CalculateIPv6ICMPChecksum(pIPv6, pICMP, p.Data)) } else { diff --git a/test/stability/testMerge/testMerge.go b/test/stability/testMerge/testMerge.go index 952cf723..49af5457 100644 --- a/test/stability/testMerge/testMerge.go +++ b/test/stability/testMerge/testMerge.go @@ -15,6 +15,7 @@ import ( "github.com/intel-go/nff-go/flow" "github.com/intel-go/nff-go/packet" "github.com/intel-go/nff-go/test/stability/stabilityCommon" + "github.com/intel-go/nff-go/types" ) // Test with testScenario=1: @@ -63,8 +64,8 @@ var ( // Usually when writing multibyte fields to packet, we should make // sure that byte order in packet buffer is correct and swap bytes if needed. // Here for testing purposes we use addresses with bytes swapped by hand. - ipv4addr1 uint32 = 0x0100007f // 127.0.0.1 - ipv4addr2 uint32 = 0x05090980 // 128.9.9.5 + ipv4addr1 types.IPv4Address = 0x0100007f // 127.0.0.1 + ipv4addr2 types.IPv4Address = 0x05090980 // 128.9.9.5 testDoneEvent *sync.Cond progStart time.Time @@ -135,9 +136,9 @@ func setGetter(scenario getScenario, inputSource uint) (finalFlow *flow.Flow, er finalFlow, err = flow.SetReceiver(uint16(inputSource)) case fastGenerate: if inputSource == useGenerator1 { - finalFlow, err = flow.SetFastGenerator(generatePacketGroup, speed, &generatorParameters{isGroup1: true}) + finalFlow, _, err = flow.SetFastGenerator(generatePacketGroup, speed, &generatorParameters{isGroup1: true}) } else if inputSource == useGenerator2 { - finalFlow, err = flow.SetFastGenerator(generatePacketGroup, speed, &generatorParameters{isGroup1: false}) + finalFlow, _, err = flow.SetFastGenerator(generatePacketGroup, speed, &generatorParameters{isGroup1: false}) } else { return nil, errors.New(" setGetter: unknown generator type") } diff --git a/test/stability/testSingleWorkingFF/testSingleWorkingFF.go b/test/stability/testSingleWorkingFF/testSingleWorkingFF.go index edddb471..0b485d5c 100644 --- a/test/stability/testSingleWorkingFF/testSingleWorkingFF.go +++ b/test/stability/testSingleWorkingFF/testSingleWorkingFF.go @@ -197,7 +197,7 @@ func executeTest(configFile, target string, testScenario uint, testType uint) er testDoneEvent = sync.NewCond(&m) // Create packet flow - generatedFlow, err := flow.SetFastGenerator(generatePacket, speed, nil) + generatedFlow, _, err := flow.SetFastGenerator(generatePacket, speed, nil) if err != nil { return err } diff --git a/types/Makefile b/types/Makefile new file mode 100644 index 00000000..bf306e09 --- /dev/null +++ b/types/Makefile @@ -0,0 +1,15 @@ +# Copyright 2019 Intel Corporation. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +PATH_TO_MK = ../mk +include $(PATH_TO_MK)/include.mk + +.PHONY: testing +testing: check-pktgen + go test + +.PHONY: coverage +coverage: + go test -cover -coverprofile=c.out + go tool cover -html=c.out -o common_coverage.html diff --git a/types/const.go b/types/const.go new file mode 100644 index 00000000..ca686404 --- /dev/null +++ b/types/const.go @@ -0,0 +1,99 @@ +// Copyright 2019 Intel Corporation. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types + +import ( + "math" +) + +// Max array length for type conversions +const MaxLength = math.MaxInt32 + +// Length of addresses. +const ( + EtherAddrLen = 6 + IPv4AddrLen = 4 + IPv6AddrLen = 16 +) + +// Supported EtherType for L2 +const ( + IPV4Number = 0x0800 + ARPNumber = 0x0806 + VLANNumber = 0x8100 + MPLSNumber = 0x8847 + IPV6Number = 0x86dd + + SwapIPV4Number = 0x0008 + SwapARPNumber = 0x0608 + SwapVLANNumber = 0x0081 + SwapMPLSNumber = 0x4788 + SwapIPV6Number = 0xdd86 +) + +// Supported L4 types +const ( + ICMPNumber = 0x01 + IPNumber = 0x04 + TCPNumber = 0x06 + UDPNumber = 0x11 + GRENumber = 0x2f + ICMPv6Number = 0x3a + NoNextHeader = 0x3b +) + +// Supported ICMP Types +const ( + ICMPTypeEchoRequest uint8 = 8 + ICMPTypeEchoResponse uint8 = 0 + ICMPv6TypeEchoRequest uint8 = 128 + ICMPv6TypeEchoResponse uint8 = 129 + ICMPv6NeighborSolicitation uint8 = 135 + ICMPv6NeighborAdvertisement uint8 = 136 +) + +// These constants keep length of supported headers in bytes. +// +// IPv6Len - minimum length of IPv6 header in bytes. It can be higher and it +// is not determined inside packet. Only default minimum size is used. +// +// IPv4MinLen and TCPMinLen are used only in packet generation functions. +// +// In parsing we take actual length of TCP header from DataOff field and length of +// IPv4 take from Ihl field. +const ( + EtherLen = 14 + VLANLen = 4 + MPLSLen = 4 + IPv4MinLen = 20 + IPv6Len = 40 + ICMPLen = 8 + TCPMinLen = 20 + UDPLen = 8 + ARPLen = 28 + GTPMinLen = 8 + GRELen = 4 +) + +const ( + TCPMinDataOffset = 0x50 // minimal tcp data offset + IPv4VersionIhl = 0x45 // IPv4, IHL = 5 (min header len) + IPv6VtcFlow = 0x60 // IPv6 version +) + +// TCPFlags contains set TCP flags. +type TCPFlags uint8 + +// Constants for valuues of TCP flags. +const ( + TCPFlagFin = 0x01 + TCPFlagSyn = 0x02 + TCPFlagRst = 0x04 + TCPFlagPsh = 0x08 + TCPFlagAck = 0x10 + TCPFlagUrg = 0x20 + TCPFlagEce = 0x40 + TCPFlagCwr = 0x80 +) diff --git a/types/ipv4.go b/types/ipv4.go new file mode 100644 index 00000000..fea89bb0 --- /dev/null +++ b/types/ipv4.go @@ -0,0 +1,59 @@ +// Copyright 2019 Intel Corporation. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types + +import ( + "encoding/json" + "fmt" + "net" +) + +type IPv4Address uint32 + +// BytesToIPv4 converts four element address to IPv4Address representation +func BytesToIPv4(a byte, b byte, c byte, d byte) IPv4Address { + return IPv4Address(d)<<24 | IPv4Address(c)<<16 | IPv4Address(b)<<8 | IPv4Address(a) +} + +// ArrayToIPv4 converts four element array to IPv4Address representation +func ArrayToIPv4(a [IPv4AddrLen]byte) IPv4Address { + return IPv4Address(a[3])<<24 | IPv4Address(a[2])<<16 | IPv4Address(a[1])<<8 | IPv4Address(a[0]) +} + +// SliceToIPv4 converts four element slice to IPv4Address representation +func SliceToIPv4(s []byte) IPv4Address { + return IPv4Address(s[3])<<24 | IPv4Address(s[2])<<16 | IPv4Address(s[1])<<8 | IPv4Address(s[0]) +} + +// IPv4ToBytes converts four element address to IPv4Address representation +func IPv4ToBytes(v IPv4Address) [IPv4AddrLen]byte { + return [IPv4AddrLen]uint8{byte(v), byte(v >> 8), byte(v >> 16), byte(v >> 24)} +} + +func (addr IPv4Address) String() string { + return fmt.Sprintf("%d.%d.%d.%d", byte(addr), byte(addr>>8), byte(addr>>16), byte(addr>>24)) +} + +func IPv4ArrayToString(addr [IPv4AddrLen]uint8) string { + return fmt.Sprintf("%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]) +} + +// UnmarshalJSON parses IPv4 address. +func (out *IPv4Address) UnmarshalJSON(b []byte) error { + var s string + if err := json.Unmarshal(b, &s); err != nil { + return err + } + + if ip := net.ParseIP(s); ip != nil { + ipv4 := ip.To4() + if ipv4 == nil { + return fmt.Errorf("Bad IPv4 address %s", s) + } + *out = BytesToIPv4(ipv4[0], ipv4[1], ipv4[2], ipv4[3]) + return nil + } + return fmt.Errorf("Failed to parse address %s", s) +} diff --git a/types/ipv6.go b/types/ipv6.go new file mode 100644 index 00000000..21a62aa8 --- /dev/null +++ b/types/ipv6.go @@ -0,0 +1,39 @@ +// Copyright 2019 Intel Corporation. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types + +import ( + "encoding/json" + "fmt" + "net" +) + +type IPv6Address [IPv6AddrLen]uint8 + +func (addr IPv6Address) String() string { + return fmt.Sprintf("[%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x]", + addr[0], addr[1], addr[2], addr[3], + addr[4], addr[5], addr[6], addr[7], + addr[8], addr[9], addr[10], addr[11], + addr[12], addr[13], addr[14], addr[15]) +} + +// UnmarshalJSON parses IPv6 address. +func (out *IPv6Address) UnmarshalJSON(b []byte) error { + var s string + if err := json.Unmarshal(b, &s); err != nil { + return err + } + + if ip := net.ParseIP(s); ip != nil { + ipv6 := ip.To16() + if ipv6 == nil { + return fmt.Errorf("Bad IPv6 address %s", s) + } + copy((*out)[:], ipv6) + return nil + } + return fmt.Errorf("Failed to parse address %s", s) +} diff --git a/types/mac.go b/types/mac.go new file mode 100644 index 00000000..27eaea23 --- /dev/null +++ b/types/mac.go @@ -0,0 +1,16 @@ +// Copyright 2019 Intel Corporation. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types + +import ( + "fmt" +) + +type MACAddress [EtherAddrLen]uint8 + +// MACToString return MAC address like string +func (mac MACAddress) String() string { + return fmt.Sprintf("%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]) +} diff --git a/types/subnet.go b/types/subnet.go new file mode 100644 index 00000000..bf4fa309 --- /dev/null +++ b/types/subnet.go @@ -0,0 +1,129 @@ +// Copyright 2018 Intel Corporation. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types + +import ( + "encoding/json" + "fmt" + "net" + "strconv" +) + +type IPv4Subnet struct { + Addr IPv4Address + Mask IPv4Address +} + +func (sn *IPv4Subnet) String() string { + // Count most significant set bits + mask := IPv4Address(1) << 31 + i := 0 + for ; i <= 32; i++ { + if sn.Mask&mask == 0 { + break + } + mask >>= 1 + } + return sn.Addr.String() + "/" + strconv.Itoa(i) +} + +// UnmarshalJSON parses ipv 4 subnet details. +func (out *IPv4Subnet) UnmarshalJSON(b []byte) error { + var s string + if err := json.Unmarshal(b, &s); err != nil { + return err + } + + convertIPv4 := func(in []byte, str string) (IPv4Address, error) { + if in == nil || len(in) > 4 { + return 0, fmt.Errorf("Bad IPv4 address: ", str) + } + + return BytesToIPv4(in[0], in[1], in[2], in[3]), nil + } + + if ip, ipnet, err := net.ParseCIDR(s); err == nil { + if out.Addr, err = convertIPv4(ip.To4(), s); err != nil { + return err + } + if out.Mask, err = convertIPv4(ipnet.Mask, s); err != nil { + return err + } + return nil + } + + if ip := net.ParseIP(s); ip != nil { + var err error + if out.Addr, err = convertIPv4(ip.To4(), s); err != nil { + return err + } + out.Mask = 0xffffffff + return nil + } + return fmt.Errorf("Failed to parse address ", s) +} + +// CheckIPv4AddressWithinSubnet returns true if IPv4 addr is inside of specified subnet. +func (sn *IPv4Subnet) CheckIPv4AddressWithinSubnet(addr IPv4Address) bool { + return addr&sn.Mask == sn.Addr&sn.Mask +} + +type IPv6Subnet struct { + Addr IPv6Address + Mask IPv6Address +} + +func (sn *IPv6Subnet) String() string { + // Count most significant set bits + i := 0 + for ; i <= 128; i++ { + mask := uint8(1) << uint(7-(i&7)) + if i == 128 || sn.Mask[i>>3]&mask == 0 { + break + } + } + return sn.Addr.String() + "/" + strconv.Itoa(i) +} + +// UnmarshalJSON parses ipv 6 subnet details. +func (out *IPv6Subnet) UnmarshalJSON(b []byte) error { + var s string + if err := json.Unmarshal(b, &s); err != nil { + return err + } + + if ip, ipnet, err := net.ParseCIDR(s); err == nil { + if ip.To16() == nil { + return fmt.Errorf("Bad IPv6 address: %s", s) + } + copy(out.Addr[:], ip.To16()) + copy(out.Mask[:], ipnet.Mask) + return nil + } + + if ip := net.ParseIP(s); ip != nil { + if ip.To16() == nil { + return fmt.Errorf("Bad IPv6 address: %s", s) + } + copy(out.Addr[:], ip.To16()) + out.Mask = IPv6Address{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} + return nil + } + return fmt.Errorf("Failed to parse address ", s) +} + +// AndIPv6Mask performs bit AND operation for IPv6 address and mask. +func (sn *IPv6Subnet) AndIPv6Mask(addr IPv6Address) IPv6Address { + var result IPv6Address + for i := range addr { + result[i] = addr[i] & sn.Mask[i] + } + return result +} + +// CheckIPv6AddressWithinSubnet returns true if IPv6 addr is inside of specified subnet. +func (sn *IPv6Subnet) CheckIPv6AddressWithinSubnet(addr IPv6Address) bool { + return sn.AndIPv6Mask(addr) == sn.AndIPv6Mask(sn.Addr) +} diff --git a/vagrant/Vagrantfile b/vagrant/Vagrantfile index c373d107..9b7e6c96 100644 --- a/vagrant/Vagrantfile +++ b/vagrant/Vagrantfile @@ -126,7 +126,7 @@ echo Reassigning "${syscon}" interface to system name sudo nmcli c mod "${syscon}" connection.id 'System connection' echo Unpacking Go language into /opt -(cd /opt; sudo sh -c 'curl -L -s https://dl.google.com/go/go1.11.4.linux-amd64.tar.gz | tar zx') +(cd /opt; sudo sh -c 'curl -L -s https://dl.google.com/go/go1.11.5.linux-amd64.tar.gz | tar zx') mkdir go chmod +x ~/scripts.sh . ~/scripts.sh