diff --git a/Dockerfile b/Dockerfile index b6fe71a4..43267906 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:cosmic +FROM ubuntu:disco ARG MAKEFLAGS=-j2 @@ -11,7 +11,6 @@ RUN apt-get -q update && apt-get -q -y install \ git \ curl \ wget \ - golang-${GO_VERSION} \ libpcap-dev \ libelf-dev \ hugepages \ @@ -21,7 +20,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.5.linux-amd64.tar.gz | tar zx +RUN cd /opt && curl -L -s https://dl.google.com/go/go1.12.5.linux-amd64.tar.gz | tar zx RUN mkdir -p ${NFF_GO} COPY . ${NFF_GO} diff --git a/Makefile b/Makefile index 4d6782d0..074dd070 100644 --- a/Makefile +++ b/Makefile @@ -4,8 +4,7 @@ PATH_TO_MK = mk SUBDIRS = nff-go-base dpdk test examples -DOC_TARGETS = flow packet -CI_TESTING_TARGETS = packet low common +CI_TESTING_TARGETS = packet internal/low common TESTING_TARGETS = $(CI_TESTING_TARGETS) test/stability all: $(SUBDIRS) @@ -33,9 +32,4 @@ citesting: $(CI_TESTING_TARGETS) $(TESTING_TARGETS): $(MAKE) -C $@ testing -.PHONY: doc -doc: $(DOC_TARGETS) - mkdir doc - $(foreach package,$(DOC_TARGETS),godoc -analysis=type -analysis=pointer -html github.com/intel-go/nff-go/$(package) > doc/$(package).html;) - include $(PATH_TO_MK)/intermediate.mk diff --git a/README.md b/README.md index 3bababfa..15d2ee3d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -[![Go Report Card](https://goreportcard.com/badge/github.com/intel-go/yanff)](https://goreportcard.com/report/github.com/intel-go/yanff) -[![GoDoc](https://godoc.org/github.com/intel-go/yanff?status.svg)](https://godoc.org/github.com/intel-go/yanff) +[![Go Report Card](https://goreportcard.com/badge/github.com/intel-go/nff-go)](https://goreportcard.com/report/github.com/intel-go/nff-go) +[![GoDoc](https://godoc.org/github.com/intel-go/nff-go?status.svg)](https://godoc.org/github.com/intel-go/nff-go) [![Dev chat at https://gitter.im/intel-yanff/Lobby](https://img.shields.io/badge/gitter-developer_chat-46bc99.svg)](https://gitter.im/intel-yanff/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://travis-ci.org/intel-go/nff-go.svg?branch=develop)](https://travis-ci.org/intel-go/nff-go) # Network Function Framework for Go (former YANFF) @@ -84,9 +84,15 @@ Requirements in the DPDK Getting Started Guide for Linux](http://dpdk.org/doc/guides/linux_gsg/sys_reqs.html) for more information. -Since NFF-Go is build with Mellanox cards support out of the box you +By default NFF-Go is build with Mellanox cards support out of the box you need to install additional dependencies required for MLX network -drivers. On Ubuntu they are called `libmnl-dev` and `libibverbs-dev`. +drivers. On Ubuntu they are called `libmnl-dev` and +`libibverbs-dev`. For more details see MLX drivers respective pages +for [MLX4](https://doc.dpdk.org/guides/nics/mlx4.html) and +[MLX5](https://doc.dpdk.org/guides/nics/mlx5.html). If these +dependencies cannot be satisfied, and Mellanox drivers are not needed, +you can set variable `NFF_GO_NO_MLX_DRIVERS` to some unempty value to +disable MLX drivers compilation. Additional dependencies are required for pktgen, especially if you are running RedHat or CentOS Linux distributions. See [this @@ -133,18 +139,9 @@ when it is run for the first time, but may be quite slow. ## Documentation -Use: - - make doc - -to generate full documentation. Alternatively, you can do: - - godoc -http=:6060 - -and browse the following URLs: - -* http://localhost:6060/pkg/nff-go/flow/ -* http://localhost:6060/pkg/nff-go/packet/ +Online API documentation is available on [godoc.org +site](https://godoc.org/github.com/intel-go/nff-go). API usage is +explained on our [Wiki pages](https://github.com/intel-go/nff-go/wiki). ## Tests @@ -187,7 +184,7 @@ deployed images listed in NFF_GO_HOSTS, use the **make cleanall** command. ## Contributing If you want to contribute to NFF-Go, check our [Contributing -guide](https://github.com/intel-go/yanff/blob/master/CONTRIBUTING.md). We also +guide](https://github.com/intel-go/nff-go/blob/master/CONTRIBUTING.md). We also recommend checking the bugs with 'help-wanted' or 'easyfix' in our list of open issues; these bugs can be solved without an extensive knowledge of NFF-Go. We would love to help you start contributing. diff --git a/devices/consts.go b/devices/consts.go index 4d66a872..87240031 100644 --- a/devices/consts.go +++ b/devices/consts.go @@ -75,7 +75,7 @@ var ( var ( pathSysClassNetDeviceDriver stringBuilder = PathSysClassNet + "/%s/device/driver" - pathSysClassNetDevice stringBuilder = PathSysClassNet + "/%s/device" + pathSysClassNetDevice stringBuilder = PathSysClassNet + "/%s" ) var ( @@ -107,6 +107,7 @@ func init() { rVmbusDriver = regexp.MustCompile("/sys/bus/vmbus/drivers/(\\S+)/") - IsPciID = regexp.MustCompile("^\\d{4}:\\d{2}:\\d{2}.\\d$") + // domains are numbered from 0 to ffff), bus (0 to ff), slot (0 to 1f) and function (0 to 7) + IsPciID = regexp.MustCompile("^[[:xdigit:]]{4}:[[:xdigit:]]{2}:[0-1][[:xdigit:]].[0-7]$") IsUUID = regexp.MustCompile("^[[:xdigit:]]{8}-[[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12}$") } diff --git a/devices/misc.go b/devices/misc.go index e8e80bb3..ee6b14e0 100644 --- a/devices/misc.go +++ b/devices/misc.go @@ -34,8 +34,18 @@ func FindDefaultDpdkDriver(nicName string) string { // GetDeviceID returns the device ID of given NIC name. func GetDeviceID(nicName string) (string, error) { - // DEV_ID=$(basename $(readlink /sys/class/net//device)) - return readlinkBaseCmd(pathSysClassNetDevice.With(nicName)) + // DEV_ID=$(basename $(readlink /sys/class/net/)) + raw, err := readlinkCmd(pathSysClassNetDevice.With(nicName)) + if err != nil { + return "", err + } + // raw should be like /sys/devices/pci0002:00/0000:00:08.0/virtio2/net/ens8 + raws := strings.Split(raw, "/") + if len(raws) < 5 { + return "", fmt.Errorf("path not correct") + } + return raws[4], nil + } // IsModuleLoaded checks if the kernel has already loaded the driver or not. @@ -69,12 +79,20 @@ func writeToTargetWithData(sysfs string, flag int, mode os.FileMode, data string return nil } -func readlinkBaseCmd(path string) (string, error) { - output, err := cmdOutputWithTimeout(defaultTimeoutLimitation, "readlink", path) +func readlinkCmd(path string) (string, error) { + output, err := cmdOutputWithTimeout(defaultTimeoutLimitation, "readlink", "-f", path) if err != nil { return "", fmt.Errorf("Cmd Execute readlink failed: %s", err.Error()) } outputStr := strings.Trim(string(output), "\n") + return outputStr, nil +} + +func readlinkBaseCmd(path string) (string, error) { + outputStr, err := readlinkCmd(path) + if err != nil { + return "", fmt.Errorf("Cmd Execute readlink failed: %s", err.Error()) + } result := filepath.Base(outputStr) return result, nil } diff --git a/dpdk/Makefile b/dpdk/Makefile index a3b10ec6..0c79ae16 100644 --- a/dpdk/Makefile +++ b/dpdk/Makefile @@ -23,7 +23,12 @@ all: pktgen $(DPDK_DIR)/$(DPDK_INSTALL_DIR): $(MAKE) -C $(DPDK_DIR) config T=$(RTE_TARGET) - sed -ri 's,(MLX._PMD=)n,\1y,' $(DPDK_DIR)/build/.config + @if [ -z '${NFF_GO_NO_MLX_DRIVERS}' ]; then \ + echo BUILDING DPDK WITH MLX DRIVERS; \ + sed -ri 's,(MLX._PMD=)n,\1y,' $(DPDK_DIR)/build/.config; \ + else \ + echo BUILDING DPDK WITHOUT MLX DRIVERS; \ + fi $(MAKE) -C $(DPDK_DIR) $(MAKE) -C $(DPDK_DIR) install DESTDIR=$(DPDK_INSTALL_DIR) diff --git a/dpdk/dpdk b/dpdk/dpdk index 8b937bae..07efd6dd 160000 --- a/dpdk/dpdk +++ b/dpdk/dpdk @@ -1 +1 @@ -Subproject commit 8b937bae24a306ad82c0983c83feb1be23d41f13 +Subproject commit 07efd6ddc0499688eb11ae4866d3532295d6db2b diff --git a/dpdk/pktgen-dpdk b/dpdk/pktgen-dpdk index 41995554..ae5a88bf 160000 --- a/dpdk/pktgen-dpdk +++ b/dpdk/pktgen-dpdk @@ -1 +1 @@ -Subproject commit 4199555481cd08fe08e32986c94e59836ca1cd4f +Subproject commit ae5a88bf89e421ea84d680a7e5613af959243a4b diff --git a/examples/.gitignore b/examples/.gitignore index e3c2f461..eda6c703 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -13,3 +13,5 @@ netlink devbind OSforwarding generate +jumbo +decrementTTL diff --git a/examples/Dockerfile b/examples/Dockerfile index 0ab249dc..4d612694 100644 --- a/examples/Dockerfile +++ b/examples/Dockerfile @@ -21,3 +21,5 @@ COPY timer . COPY netlink . COPY generate . COPY OSforwarding . +COPY jumbo . +COPY decrementTTL . diff --git a/examples/Makefile b/examples/Makefile index 2a305c51..3aaaef21 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -7,7 +7,8 @@ IMAGENAME = nff-go-examples EXECUTABLES = dump clonablePcapDumper kni copy errorHandling timer \ createPacket sendFixedPktsNumber gtpu pingReplay \ netlink gopacketParserExample devbind generate \ - OSforwarding + OSforwarding jumbo decrementTTL + SUBDIRS = tutorial antiddos demo fileReadWrite firewall forwarding ipsec lb .PHONY: dpi nffPktgen diff --git a/examples/decrementTTL.go b/examples/decrementTTL.go new file mode 100644 index 00000000..cd2708a9 --- /dev/null +++ b/examples/decrementTTL.go @@ -0,0 +1,40 @@ +// 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" + "github.com/intel-go/nff-go/packet" +) + +// Main function for constructing packet processing graph. +func main() { + inPort := flag.Uint("inPort", 0, "port for receiver") + outPort := flag.Uint("outPort", 1, "port for sender") + flag.Parse() + + flow.SystemInit(nil) + inputFlow, _ := flow.SetReceiver(uint16(*inPort)) + flow.SetHandlerDrop(inputFlow, decrementTTL, nil) + flow.SetSender(inputFlow, uint16(*outPort)) + flow.SystemStart() +} + +func decrementTTL(current *packet.Packet, c flow.UserContext) bool { + current.ParseL3() // must parse before header can be read + header := current.GetIPv4() + if header == nil { // not IPv4 + return false + } + + header.TimeToLive-- + if header.TimeToLive == 0 { // TTL exceeded, drop + return false + } else { + return true + } +} diff --git a/examples/dpi/Makefile b/examples/dpi/Makefile index dafe4ca5..f4b27078 100644 --- a/examples/dpi/Makefile +++ b/examples/dpi/Makefile @@ -5,6 +5,6 @@ PATH_TO_MK = ../../mk SUBDIRS = main pattern -export GO_COMPILE_FLAGS += -tags hyperscan_v4 +export GO_BUILD_TAGS += hyperscan_v4 include $(PATH_TO_MK)/intermediate.mk diff --git a/examples/dpi/main/Makefile b/examples/dpi/main/Makefile index 65919230..3e2625ee 100644 --- a/examples/dpi/main/Makefile +++ b/examples/dpi/main/Makefile @@ -9,7 +9,7 @@ EXECUTABLES = dpi dpi: ../pattern gohs gohs: - go get $(GO_COMPILE_FLAGS) -v github.com/flier/gohs/hyperscan + go get -tags hyperscan_v4 -v github.com/flier/gohs/hyperscan COMMON_FILES = handlers.go diff --git a/examples/dpi/pattern/Makefile b/examples/dpi/pattern/Makefile index a73799c3..bd4f7e18 100644 --- a/examples/dpi/pattern/Makefile +++ b/examples/dpi/pattern/Makefile @@ -6,4 +6,4 @@ PATH_TO_MK = ../../../mk IMAGENAME = dpi-pattern pattern: - go install $(GO_COMPILE_FLAGS) + go install -tags hyperscan_v4 diff --git a/examples/jumbo.go b/examples/jumbo.go new file mode 100644 index 00000000..8f8723fa --- /dev/null +++ b/examples/jumbo.go @@ -0,0 +1,100 @@ +// 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 main + +import ( + "flag" + "fmt" + "github.com/intel-go/nff-go/flow" + "github.com/intel-go/nff-go/packet" + "time" +) + +// You need to enable memory jumbo for maxPacketSegment more than 2014. +// Explanation: internal packet buffers are 2048 bytes, first packet will have +// ethernet and ipv4 headers = 14 + 2 = 34 bytes. +// Buffer will overflows if maxPacketSegment > 2048 - 34 = 2014 bytes + +// Note: usage of 1484 < maxPacketSegment < 2014 without memory jumbo can work +// on some NICs, but is treated as underfined behaviour +// Explanation: in this situation packet will be fit in internal buffers +// however packet will be more than 1518 bytes - Ethernet max non-jumbo frame +const maxPacketSegment = 1484 + +// You need to enale chained jumbo for requiredPacketLength > maxPacketSegment +// Explanation: if required length is more than segment length packet will consist of +// several chained segments. You should enable chained jumbo to correctly handle them + +// Note: usage of maxPacketSegment > 2014 and also requiredPacketLength > maxPacketSegment +// is forbidden. If you decicde to chain packets you shouldn't chain non-standart packets +// Explanation: in this situation you will need to enable both +// memory and chained jumbo and this is forbidden +const requiredPacketLength = 7000 + +func main() { + chained := flag.Bool("chained", false, "enable chained jumbo") + memory := flag.Bool("memory", false, "enalbe memory jumbo") + flag.Parse() + + // Initialize NFF-GO library at 10 available cores + config := flow.Config{ + ChainedJumbo: *chained, + MemoryJumbo: *memory, + } + + flow.CheckFatal(flow.SystemInit(&config)) + + generated := flow.SetGenerator(generate, nil) + flow.SetHandler(generated, dump, nil) + flow.SetSender(generated, 0) + + received, _ := flow.SetReceiver(0) + flow.SetHandler(received, dump, nil) + flow.SetStopper(received) + flow.SystemStart() +} + +func generate(pkt *packet.Packet, context flow.UserContext) { + remain := requiredPacketLength + c := 0 + diff, remain := m(maxPacketSegment, remain) + if packet.InitEmptyIPv4Packet(pkt, diff) == false { + fmt.Printf("maxPacketSegment %d is more than 2014 bytes without enabling memory jumbo\n", maxPacketSegment) + time.Sleep(5 * time.Second) + return + } + pkt.Ether.DAddr = [6]uint8{0x00, 0x11, 0x22, 0x33, 0x44, 0x55} + for i := 0; i < maxPacketSegment; i++ { + (*(*[maxPacketSegment]byte)(pkt.Data))[i] = byte(c % 10) + c++ + } + for remain != 0 { + diff, remain = m(maxPacketSegment, remain) + pkt = packet.InitNextPacket(diff, pkt) + for i := 0; i < maxPacketSegment; i++ { + (*(*[maxPacketSegment]byte)(pkt.Data))[i] = byte(c % 10) + c++ + } + } + time.Sleep(1 * time.Second) +} + +func m(max int, remain int) (uint, int) { + if remain > max { + return uint(max), remain - max + } else { + return uint(remain), 0 + } +} + +func dump(currentPacket *packet.Packet, context flow.UserContext) { + fmt.Println("NEW PACKET") + fmt.Printf("BLOCK\n%x\nEND_BLOCK\n", currentPacket.GetRawPacketBytes()) + for currentPacket.Next != nil { + currentPacket = currentPacket.Next + fmt.Printf("BLOCK\n%x\nEND_BLOCK\n", currentPacket.GetRawPacketBytes()) + } + fmt.Println("END PACKET") +} diff --git a/examples/lb/balancer.go b/examples/lb/balancer.go index 6aa78f00..44a95b56 100644 --- a/examples/lb/balancer.go +++ b/examples/lb/balancer.go @@ -51,7 +51,7 @@ func balancer(pkt *packet.Packet, ctx flow.UserContext) bool { 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) + LBConfig.TunnelPort.neighCache.SendARPRequestForIPv4(workerIP, LBConfig.TunnelPort.Subnet.IPv4.Addr, 0) return false } diff --git a/examples/lb/config.go b/examples/lb/config.go index b4042267..2d1d73f9 100644 --- a/examples/lb/config.go +++ b/examples/lb/config.go @@ -64,5 +64,11 @@ func InitFlows() { 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) + port.neighCache = packet.NewNeighbourTable(port.Index, port.macAddress, + func(ipv4 types.IPv4Address) bool { + return ipv4 == port.Subnet.IPv4.Addr + }, + func(ipv6 types.IPv6Address) bool { + return ipv6 == port.Subnet.IPv6.Addr + }) } diff --git a/examples/nffPktgen/Makefile b/examples/nffPktgen/Makefile index 39d5fdcd..d92c7877 100644 --- a/examples/nffPktgen/Makefile +++ b/examples/nffPktgen/Makefile @@ -4,6 +4,6 @@ PATH_TO_MK = ../../mk IMAGENAME = nff-pktgen -SUBDIRS = testing +SUBDIRS = testing gtp-u include $(PATH_TO_MK)/intermediate.mk diff --git a/examples/nffPktgen/generator/generator.go b/examples/nffPktgen/generator/generator.go index 809685cd..911e2f91 100644 --- a/examples/nffPktgen/generator/generator.go +++ b/examples/nffPktgen/generator/generator.go @@ -99,7 +99,7 @@ func generateEther(pkt *packet.Packet, config *PacketConfig, rnd *rand.Rand) { panic(fmt.Sprintf("InitEmptyPacket failed")) } copyDataFunc(dataConfig, size, rnd, pkt.Data) - fillEtherHdr(pkt, l2) + FillEtherHdr(pkt, l2) } func generateIPv4(pkt *packet.Packet, config *PacketConfig, rnd *rand.Rand) { @@ -115,8 +115,8 @@ func generateIPv4(pkt *packet.Packet, config *PacketConfig, rnd *rand.Rand) { panic(fmt.Sprintf("InitEmptyIPv4Packet returned false")) } copyDataFunc(dataConfig, size, rnd, pkt.Data) - fillEtherHdr(pkt, l2) - fillIPv4Hdr(pkt, l3) + FillEtherHdr(pkt, l2) + FillIPv4Hdr(pkt, l3) pktIP := (*packet.IPv4Hdr)(pkt.L3) pktIP.HdrChecksum = packet.SwapBytesUint16(packet.CalculateIPv4Checksum(pktIP)) } @@ -178,9 +178,9 @@ func generateTCPIPv4(pkt *packet.Packet, config *PacketConfig, rnd *rand.Rand) { panic(fmt.Sprintf("InitEmptyIPv4TCPPacket returned false")) } copyDataFunc(dataConfig, size, rnd, pkt.Data) - fillEtherHdr(pkt, l2) - fillIPv4Hdr(pkt, l3) - fillTCPHdr(pkt, l4, rnd) + FillEtherHdr(pkt, l2) + FillIPv4Hdr(pkt, l3) + FillTCPHdr(pkt, l4, rnd) pktTCP := (*packet.TCPHdr)(pkt.L4) pktIP := (*packet.IPv4Hdr)(pkt.L3) pktIP.HdrChecksum = packet.SwapBytesUint16(packet.CalculateIPv4Checksum(pktIP)) @@ -199,9 +199,9 @@ func generateUDPIPv4(pkt *packet.Packet, config *PacketConfig, rnd *rand.Rand) { panic(fmt.Sprintf("InitEmptyIPv4UDPPacket returned false")) } copyDataFunc(dataConfig, size, rnd, pkt.Data) - fillEtherHdr(pkt, l2) - fillIPv4Hdr(pkt, l3) - fillUDPHdr(pkt, l4) + FillEtherHdr(pkt, l2) + FillIPv4Hdr(pkt, l3) + FillUDPHdr(pkt, l4) pktUDP := (*packet.UDPHdr)(pkt.L4) pktIP := (*packet.IPv4Hdr)(pkt.L3) pktIP.HdrChecksum = packet.SwapBytesUint16(packet.CalculateIPv4Checksum(pktIP)) @@ -220,16 +220,16 @@ func generateICMPIPv4(pkt *packet.Packet, config *PacketConfig, rnd *rand.Rand) panic(fmt.Sprintf("InitEmptyIPv4ICMPPacket returned false")) } copyDataFunc(dataConfig, size, rnd, pkt.Data) - fillEtherHdr(pkt, l2) - fillIPv4Hdr(pkt, l3) - fillICMPHdr(pkt, l4, rnd) + FillEtherHdr(pkt, l2) + FillIPv4Hdr(pkt, l3) + FillICMPHdr(pkt, l4, rnd) pktICMP := (*packet.ICMPHdr)(pkt.L4) 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) { +func FillTCPHdr(pkt *packet.Packet, l4 *TCPConfig, rnd *rand.Rand) { emptyPacketTCP := (*packet.TCPHdr)(pkt.L4) emptyPacketTCP.SrcPort = packet.SwapBytesUint16(uint16(l4.SPort.Current)) emptyPacketTCP.DstPort = packet.SwapBytesUint16(uint16(l4.DPort.Current)) @@ -244,7 +244,7 @@ func fillTCPHdr(pkt *packet.Packet, l4 *TCPConfig, rnd *rand.Rand) { getNextSeqNumber(&l4.Seq, rnd) } -func fillUDPHdr(pkt *packet.Packet, l4 *UDPConfig) { +func FillUDPHdr(pkt *packet.Packet, l4 *UDPConfig) { emptyPacketUDP := (*packet.UDPHdr)(pkt.L4) emptyPacketUDP.SrcPort = packet.SwapBytesUint16(uint16(l4.SPort.Current)) emptyPacketUDP.DstPort = packet.SwapBytesUint16(uint16(l4.DPort.Current)) @@ -252,7 +252,7 @@ func fillUDPHdr(pkt *packet.Packet, l4 *UDPConfig) { getNextValue(&l4.SPort) } -func fillICMPHdr(pkt *packet.Packet, l4 *ICMPConfig, rnd *rand.Rand) { +func FillICMPHdr(pkt *packet.Packet, l4 *ICMPConfig, rnd *rand.Rand) { emptyPacketICMP := (*packet.ICMPHdr)(pkt.L4) emptyPacketICMP.Type = l4.Type emptyPacketICMP.Code = l4.Code @@ -261,7 +261,7 @@ func fillICMPHdr(pkt *packet.Packet, l4 *ICMPConfig, rnd *rand.Rand) { getNextSeqNumber(&l4.Seq, rnd) } -func fillIPv4Hdr(pkt *packet.Packet, l3 *IPv4Config) { +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)) @@ -275,7 +275,7 @@ func SwapBytesUint64(x uint64) uint64 { ((x & 0x00000000ff000000) >> 8) | ((x & 0x0000ff0000000000) >> 40) | ((x & 0x000000ff00000000) >> 24) } -func fillEtherHdr(pkt *packet.Packet, l2 *EtherConfig) { +func FillEtherHdr(pkt *packet.Packet, l2 *EtherConfig) { if l2.VLAN != nil { addVLAN(pkt, l2.VLAN.TCI) } diff --git a/examples/nffPktgen/generator/ipv6.go b/examples/nffPktgen/generator/ipv6.go index 1448d1ba..0b4f6cec 100644 --- a/examples/nffPktgen/generator/ipv6.go +++ b/examples/nffPktgen/generator/ipv6.go @@ -6,6 +6,7 @@ package generator import ( "bytes" + "encoding/json" "fmt" "math/big" "math/rand" @@ -67,8 +68,8 @@ func generateIPv6(pkt *packet.Packet, config *PacketConfig, rnd *rand.Rand) { panic(fmt.Sprintf("InitEmptyIPv6Packet returned false")) } copyDataFunc(dataConfig, size, rnd, pkt.Data) - fillEtherHdr(pkt, l2) - fillIPv6Hdr(pkt, l3) + FillEtherHdr(pkt, l2) + FillIPv6Hdr(pkt, l3) } func generateTCPIPv6(pkt *packet.Packet, config *PacketConfig, rnd *rand.Rand) { @@ -83,9 +84,9 @@ func generateTCPIPv6(pkt *packet.Packet, config *PacketConfig, rnd *rand.Rand) { panic(fmt.Sprintf("InitEmptyIPv6TCPPacket returned false")) } copyDataFunc(dataConfig, size, rnd, pkt.Data) - fillEtherHdr(pkt, l2) - fillIPv6Hdr(pkt, l3) - fillTCPHdr(pkt, l4, rnd) + 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)) @@ -103,9 +104,9 @@ func generateUDPIPv6(pkt *packet.Packet, config *PacketConfig, rnd *rand.Rand) { panic(fmt.Sprintf("InitEmptyIPv6UDPPacket returned false")) } copyDataFunc(dataConfig, size, rnd, pkt.Data) - fillEtherHdr(pkt, l2) - fillIPv6Hdr(pkt, l3) - fillUDPHdr(pkt, l4) + 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)) @@ -123,15 +124,15 @@ func generateICMPIPv6(pkt *packet.Packet, config *PacketConfig, rnd *rand.Rand) panic(fmt.Sprintf("InitEmptyIPv6ICMPPacket returned false")) } copyDataFunc(dataConfig, size, rnd, pkt.Data) - fillEtherHdr(pkt, l2) - fillIPv6Hdr(pkt, l3) - fillICMPHdr(pkt, l4, rnd) + 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) { +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) @@ -156,6 +157,16 @@ type IPv6Config struct { Bytes RawBytes } +func (ipv6 *IPv6Config) UnmarshalJSON(data []byte) error { + var in map[string]interface{} + err := json.Unmarshal(data, &in) + if err != nil { + return err + } + *ipv6, err = parseIPv6Hdr(in) + return err +} + func parseIPv6Hdr(in map[string]interface{}) (IPv6Config, error) { ipHdr := IPv6Config{DAddr: AddrIPv6Range{}, SAddr: AddrIPv6Range{}} for k, v := range in { diff --git a/examples/nffPktgen/generator/parseConfig.go b/examples/nffPktgen/generator/parseConfig.go index f80cc154..4690522a 100644 --- a/examples/nffPktgen/generator/parseConfig.go +++ b/examples/nffPktgen/generator/parseConfig.go @@ -164,14 +164,32 @@ type RawBytes struct { DType DataType } -// ParseConfig parses json config and returns []*MixConfig. -func ParseConfig(f *os.File) (config []MixConfig, err error) { +type GeneratorConfig []MixConfig + +// ParseConfigFile parses json config file and returns GeneratorConfig. +func ParseConfigFile(f *os.File) (config GeneratorConfig, err error) { r := bufio.NewReader(f) - var in map[string]interface{} + var in GeneratorConfig err = json.NewDecoder(r).Decode(&in) if err != nil { - return nil, fmt.Errorf("decoding input from file returned: %v", err) + return nil, err + } + return in, nil +} + +// UnmarshalJSON implements Unmarshaller interface for GeneratorConfig. +func (mca *GeneratorConfig) UnmarshalJSON(data []byte) error { + var in map[string]interface{} + err := json.Unmarshal(data, &in) + if err != nil { + return err } + *mca, err = ParseConfig(in) + return err +} + +// ParseConfig parses json config and returns GeneratorConfig. +func ParseConfig(in map[string]interface{}) (config GeneratorConfig, err error) { for k, v := range in { key := strings.ToLower(k) switch { @@ -184,7 +202,7 @@ func ParseConfig(f *os.File) (config []MixConfig, err error) { pktConfig := PacketConfig{Ether: ethHdr, DType: ETHERHDR} return append(config, MixConfig{Config: pktConfig, Quantity: 1}), nil case mixPattern.MatchString(key): - return ParseMixConfig(in) + return ParseGeneratorConfig(in) default: return nil, fmt.Errorf("unexpected key: %s, expected mix[0-9]* or ether", k) } @@ -193,8 +211,7 @@ func ParseConfig(f *os.File) (config []MixConfig, err error) { return nil, fmt.Errorf("expected 'ether' key , but did not get") } -// ParseMixConfig parses json config and returns []*MixConfig. -func ParseMixConfig(in map[string]interface{}) (config []MixConfig, err error) { +func ParseGeneratorConfig(in map[string]interface{}) (config GeneratorConfig, err error) { for k, v := range in { key := strings.ToLower(k) switch { @@ -319,6 +336,16 @@ func parseData(in map[string]interface{}) (ret RawBytes, err error) { return ret, fmt.Errorf("failed to parse data") } +func (ipv4 *IPv4Config) UnmarshalJSON(data []byte) error { + var in map[string]interface{} + err := json.Unmarshal(data, &in) + if err != nil { + return err + } + *ipv4, err = parseIPv4Hdr(in) + return err +} + func parseIPv4Hdr(in map[string]interface{}) (IPv4Config, error) { ipHdr := IPv4Config{DAddr: AddrRange{}, SAddr: AddrRange{}} for k, v := range in { diff --git a/examples/nffPktgen/generator/utility.go b/examples/nffPktgen/generator/utility.go index ea1071d1..4f7fae0f 100644 --- a/examples/nffPktgen/generator/utility.go +++ b/examples/nffPktgen/generator/utility.go @@ -32,12 +32,12 @@ func (g *generator) GetGeneratedNumber() uint64 { } // ReadConfig function reads and parses config file. -func ReadConfig(fileName string) ([]MixConfig, error) { +func ReadConfig(fileName string) (GeneratorConfig, error) { f, err := os.Open(fileName) if err != nil { return nil, fmt.Errorf("opening file failed with: %v ", err) } - cfg, err := ParseConfig(f) + cfg, err := ParseConfigFile(f) if err != nil { return nil, fmt.Errorf("parsing config failed with: %v", err) } @@ -120,7 +120,7 @@ func (gp genParameters) Delete() { } // GetContext gets generator context according to config -func GetContext(mixConfig []MixConfig) (*genParameters, error) { +func GetContext(mixConfig GeneratorConfig) (*genParameters, error) { var t []generatorTableUnit for _, packetConfig := range mixConfig { genFunc, err := getGenerator(packetConfig.Config) diff --git a/examples/nffPktgen/gtp-u/.gitignore b/examples/nffPktgen/gtp-u/.gitignore new file mode 100644 index 00000000..2abfe307 --- /dev/null +++ b/examples/nffPktgen/gtp-u/.gitignore @@ -0,0 +1 @@ +trafficgen diff --git a/examples/nffPktgen/gtp-u/Makefile b/examples/nffPktgen/gtp-u/Makefile new file mode 100644 index 00000000..7ba08ca3 --- /dev/null +++ b/examples/nffPktgen/gtp-u/Makefile @@ -0,0 +1,9 @@ +# 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 +IMAGENAME = gtp-u-pktgen +EXECUTABLES = trafficgen + +include $(PATH_TO_MK)/leaf.mk diff --git a/examples/nffPktgen/gtp-u/gen-ip4gtp-u.json b/examples/nffPktgen/gtp-u/gen-ip4gtp-u.json new file mode 100644 index 00000000..36b0f3eb --- /dev/null +++ b/examples/nffPktgen/gtp-u/gen-ip4gtp-u.json @@ -0,0 +1,41 @@ +{ + "s1u-port": { + "index": 0, + "dst-mac": "52:54:00:90:D4:39", + "speed": 100000, + "traffic-config": { + "ether": { + "ipv4": { + "saddr": { + "range": { + "min": "16.0.0.1", + "max": "16.7.161.32" + } + }, + "daddr": "13.7.1.110", + "udp": { + "sport": 1234, + "dport": 5678, + "randbytes": { + "size": 128, + "deviation": 0 + } + } + } + } + }, + "gtp-u-config": { + "ipv4": { + "saddr": { + "range": { + "min": "11.7.1.101", + "max": "11.7.1.180" + } + }, + "daddr": "11.7.1.93" + }, + "start-teid": 4026531840, + "max-teids": 500000 + } + } +} diff --git a/examples/nffPktgen/gtp-u/ip4gtp-u-sarp.json b/examples/nffPktgen/gtp-u/ip4gtp-u-sarp.json new file mode 100644 index 00000000..0873b3ad --- /dev/null +++ b/examples/nffPktgen/gtp-u/ip4gtp-u-sarp.json @@ -0,0 +1,67 @@ +{ + "s1u-port": { + "index": 0, + "dst-mac": "52:54:00:90:D4:39", + "speed": 100000, + "traffic-config": { + "ether": { + "ipv4": { + "saddr": { + "range": { + "min": "16.0.0.1", + "max": "16.7.161.32" + } + }, + "daddr": "13.7.1.110", + "udp": { + "sport": 1234, + "dport": 5678, + "randbytes": { + "size": 128, + "deviation": 0 + } + } + } + } + }, + "gtp-u-config": { + "ipv4": { + "saddr": { + "range": { + "min": "11.7.1.101", + "max": "11.7.1.180" + } + }, + "daddr": "11.7.1.93" + }, + "start-teid": 4026531840, + "max-teids": 500000 + } + }, + "sgi-port": { + "index": 1, + "dst-mac": "52:54:00:B9:32:8B", + "speed": 100000, + "traffic-config": { + "ether": { + "ipv4": { + "saddr": "13.7.1.110", + "daddr": { + "range": { + "min": "16.0.0.1", + "max": "16.7.161.32" + } + }, + "udp": { + "sport": 5678, + "dport": 1234, + "randbytes": { + "size": 128, + "deviation": 0 + } + } + } + } + } + } +} diff --git a/examples/nffPktgen/gtp-u/ip4gtp-u.json b/examples/nffPktgen/gtp-u/ip4gtp-u.json new file mode 100644 index 00000000..104209ed --- /dev/null +++ b/examples/nffPktgen/gtp-u/ip4gtp-u.json @@ -0,0 +1,66 @@ +{ + "s1u-port": { + "index": 0, + "speed": 100000, + "traffic-config": { + "ether": { + "ipv4": { + "saddr": { + "range": { + "min": "16.0.0.1", + "max": "16.7.161.32" + } + }, + "daddr": "13.7.1.110", + "udp": { + "sport": 1234, + "dport": 5678, + "randbytes": { + "size": 128, + "deviation": 0 + } + } + } + } + }, + "gtp-u-config": { + "ipv4": { + "saddr": { + "range": { + "min": "11.7.1.101", + "max": "11.7.1.180" + } + }, + "daddr": "11.7.1.93" + }, + "start-teid": 4026531840, + "max-teids": 500000 + } + }, + "sgi-port": { + "index": 1, + "speed": 100000, + "traffic-config": { + "ether": { + "ipv4": { + "saddr": "13.7.1.110", + "daddr": { + "range": { + "min": "16.0.0.1", + "max": "16.7.161.32" + } + }, + "udp": { + "sport": 5678, + "dport": 1234, + "randbytes": { + "size": 128, + "deviation": 0 + } + } + } + } + }, + "dut-sgi-ipv4": "13.7.1.93" + } +} diff --git a/examples/nffPktgen/gtp-u/rec-ip4gtp-u.json b/examples/nffPktgen/gtp-u/rec-ip4gtp-u.json new file mode 100644 index 00000000..05f39e3b --- /dev/null +++ b/examples/nffPktgen/gtp-u/rec-ip4gtp-u.json @@ -0,0 +1,28 @@ +{ + "sgi-port": { + "index": 1, + "dst-mac": "52:54:00:B9:32:8B", + "speed": 100000, + "traffic-config": { + "ether": { + "ipv4": { + "saddr": "13.7.1.110", + "daddr": { + "range": { + "min": "16.0.0.1", + "max": "16.7.161.32" + } + }, + "udp": { + "sport": 5678, + "dport": 1234, + "randbytes": { + "size": 128, + "deviation": 0 + } + } + } + } + } + } +} diff --git a/examples/nffPktgen/gtp-u/trafficgen.go b/examples/nffPktgen/gtp-u/trafficgen.go new file mode 100644 index 00000000..2f55de0e --- /dev/null +++ b/examples/nffPktgen/gtp-u/trafficgen.go @@ -0,0 +1,332 @@ +// 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 main + +import ( + "encoding/json" + "flag" + "fmt" + "os" + "os/signal" + "sync/atomic" + "time" + + "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/examples/nffPktgen/generator" +) + +type GTPUConfig struct { + IPv4 generator.IPv4Config `json:"ipv4"` + StartTEID uint32 `json:"start-teid"` + MaxTEIDs uint32 `json:"max-teids"` +} + +type IpPort struct { + Index uint16 `json:"index"` + DstMacAddress types.MACAddress `json:"dst-mac"` + Speed uint64 `json:"speed"` + TrafficConfig generator.GeneratorConfig `json:"traffic-config"` + // GTPUConfig is ignored for SGi port + GTPUConfig *GTPUConfig `json:"gtp-u-config"` + // DUTSGiIPv4 is ignored for S1u port + DUTSGiIPv4 types.IPv4Address `json:"dut-sgi-ipv4"` + + staticARP bool + neighCache *packet.NeighboursLookupTable + macAddress types.MACAddress + teidCount uint32 + packetCount uint64 + bytesCount uint64 +} + +type GenConfig struct { + S1uPort *IpPort `json:"s1u-port"` + SgiPort *IpPort `json:"sgi-port"` +} + +type HandlerContext struct { + port *IpPort +} + +func (hc HandlerContext) Copy() interface{} { + return HandlerContext{ + port: hc.port, + } +} + +func (hc HandlerContext) Delete() { +} + +func main() { + genConfig := flag.String("c", "config.json", "specifies config for generator") + testTime := flag.Uint("t", 30, "run generator for specified period of time in seconds, use zero to run forever") + statInterval := flag.Uint("s", 2, "statistics update interval in seconds, use zero to disable it") + flag.Parse() + + file, err := os.Open(*genConfig) + if err != nil { + panic(err) + } + decoder := json.NewDecoder(file) + var gc GenConfig + err = decoder.Decode(&gc) + if err != nil { + panic(fmt.Errorf("%s config reading failed: %v", *genConfig, err)) + } + err = validateConfig(&gc) + if err != nil { + panic(err) + } + + config := flow.Config{} + flow.CheckFatal(flow.SystemInit(&config)) + + if gc.S1uPort != nil { + initPortFlows(gc.S1uPort, gc.S1uPort.GTPUConfig.IPv4.SAddr, true) + } + if gc.SgiPort != nil { + initPortFlows(gc.SgiPort, gc.SgiPort.TrafficConfig[0].Config.Ether.IPv4.SAddr, false) + } + + go func() { + flow.CheckFatal(flow.SystemStart()) + }() + + // Set up finish channels + interruptChannel := make(chan os.Signal, 1) + signal.Notify(interruptChannel, os.Interrupt) + + var finishChannel, statsChannel <-chan time.Time + if *testTime > 0 { + finishChannel = time.NewTicker(time.Duration(*testTime) * time.Second).C + } + if *statInterval > 0 { + statsChannel = time.NewTicker(time.Duration(*statInterval) * time.Second).C + } + + started := time.Now() +out: + for { + select { + case sig := <-interruptChannel: + fmt.Printf("Received signal %v, finishing.\n", sig) + break out + case <-finishChannel: + fmt.Println("Test timeout reached") + break out + case <-statsChannel: + printStats(&gc, started) + } + } + printStats(&gc, started) + printTotals(&gc, started) +} + +func validateConfig(gc *GenConfig) error { + if gc.S1uPort != nil { + if len(gc.S1uPort.TrafficConfig) > 1 { + return fmt.Errorf("Currently suppotted mix only of one traffic configuration for S1u interface. Configured %d instead", len(gc.S1uPort.TrafficConfig)) + } + if gc.S1uPort.GTPUConfig == nil { + return fmt.Errorf("GTP config should be specified for S1u") + } + if gc.S1uPort.GTPUConfig.MaxTEIDs == 0 { + return fmt.Errorf("GTP configuration should specify non-zero maximum number of possible TEIDs") + } + if gc.S1uPort.TrafficConfig[0].Config.DType != generator.ETHERHDR { + return fmt.Errorf("Only \"ether\" type of traffic is supported for l2") + } + if gc.S1uPort.TrafficConfig[0].Config.Ether.DType != generator.IPv4HDR { + return fmt.Errorf("Only \"ipv4\" type of traffic is supported for l3") + } + } + if gc.SgiPort != nil { + if len(gc.S1uPort.TrafficConfig) > 1 { + return fmt.Errorf("Currently suppotted mix only of one traffic configuration for SGi interface. Configured %d instead", len(gc.SgiPort.TrafficConfig)) + } + if gc.SgiPort.GTPUConfig != nil { + return fmt.Errorf("GTP config cannot be specified for SGi interface") + } + if gc.SgiPort.TrafficConfig[0].Config.DType != generator.ETHERHDR { + return fmt.Errorf("Only \"ether\" type of traffic is supported for l2") + } + if gc.SgiPort.TrafficConfig[0].Config.Ether.DType != generator.IPv4HDR { + return fmt.Errorf("Only \"ipv4\" type of traffic is supported for l3") + } + } + if gc.S1uPort == nil && gc.SgiPort == nil { + return fmt.Errorf("Configuration should specify settings either for S1u interface or Sgi interface or both") + } + return nil +} + +func initPortFlows(port *IpPort, myIPs generator.AddrRange, addEncapsulation bool) { + port.macAddress = flow.GetPortMACAddress(port.Index) + port.staticARP = port.DstMacAddress != types.MACAddress{0, 0, 0, 0, 0, 0} + port.teidCount = 0 + // myIPs is caputred inside this lambda + myV4Checker := func(ip types.IPv4Address) bool { + return (myIPs.Inc != 0 && myIPs.Min <= uint64(packet.SwapBytesIPv4Addr(ip)) && myIPs.Max >= uint64(packet.SwapBytesIPv4Addr(ip))) || + (myIPs.Inc == 0 && myIPs.Current == uint64(packet.SwapBytesIPv4Addr(ip))) + } + port.neighCache = packet.NewNeighbourTable(port.Index, port.macAddress, myV4Checker, nil) + + // Output flow + context, err := generator.GetContext(port.TrafficConfig) + flow.CheckFatal(err) + outFlow, _, err := flow.SetFastGenerator(generator.Generate, port.Speed, context) + flow.CheckFatal(err) + hc := HandlerContext{ + port: port, + } + if addEncapsulation { + flow.CheckFatal(flow.SetHandlerDrop(outFlow, encapsulateGTP, hc)) + } else { + flow.CheckFatal(flow.SetHandlerDrop(outFlow, setCorrectL2, hc)) + } + flow.CheckFatal(flow.SetSender(outFlow, uint16(port.Index))) + // Input flow + inFlow, err := flow.SetReceiver(port.Index) + flow.CheckFatal(flow.SetHandlerDrop(inFlow, receiveHandler, hc)) + flow.CheckFatal(flow.SetStopper(inFlow)) +} + +func encapsulateGTP(pkt *packet.Packet, ctx flow.UserContext) bool { + hc := ctx.(HandlerContext) + + // Get destination IP address and compare it with minimal value + pkt.ParseL3() + ipv4 := pkt.GetIPv4NoCheck() + if hc.port.teidCount == hc.port.GTPUConfig.MaxTEIDs { + hc.port.teidCount = 0 + } else { + hc.port.teidCount++ + } + + // Add new IPv4, UDP and GTP headers to the packet + if !pkt.EncapsulateIPv4GTP(hc.port.teidCount + hc.port.GTPUConfig.StartTEID) { + fmt.Println("EncapsulateHead returned error") + return false + } + pkt.ParseL3() + + // Fill new IPv4 header with addresses according to configuration + generator.FillIPv4Hdr(pkt, &hc.port.GTPUConfig.IPv4) + ipv4 = pkt.GetIPv4NoCheck() + + // Fill L2 + pkt.Ether.EtherType = types.SwapIPV4Number + pkt.Ether.SAddr = hc.port.macAddress + if hc.port.staticARP { + pkt.Ether.DAddr = hc.port.DstMacAddress + } else { + // Find l2 addresses for new destionation IP in ARP cache + targetIP := ipv4.DstAddr + targetMAC, found := hc.port.neighCache.LookupMACForIPv4(targetIP) + if !found { + // fmt.Println("Not found MAC address for IP", targetIP.String()) + hc.port.neighCache.SendARPRequestForIPv4(targetIP, ipv4.SrcAddr, 0) + return false + } + pkt.Ether.DAddr = targetMAC + } + + // Fill up l3 + ipv4.VersionIhl = 0x45 + ipv4.TypeOfService = 0 + ipv4.PacketID = 0x1513 + ipv4.FragmentOffset = 0 + ipv4.TimeToLive = 64 + + length := pkt.GetPacketLen() + ipv4.TotalLength = packet.SwapBytesUint16(uint16(length - types.EtherLen)) + ipv4.NextProtoID = types.UDPNumber + ipv4.HdrChecksum = packet.SwapBytesUint16(packet.CalculateIPv4Checksum(ipv4)) + + // Fill up L4 + pkt.ParseL4ForIPv4() + udp := pkt.GetUDPForIPv4() + udp.SrcPort = packet.SwapBytesUint16(packet.UDPPortGTPU) + udp.DstPort = packet.SwapBytesUint16(packet.UDPPortGTPU) + udp.DgramLen = packet.SwapBytesUint16(uint16(length - types.EtherLen - types.IPv4MinLen)) + pkt.ParseL7(types.UDPNumber) + // Calculate checksums + ipv4.HdrChecksum = packet.SwapBytesUint16(packet.CalculateIPv4Checksum(ipv4)) + udp.DgramCksum = packet.SwapBytesUint16(packet.CalculateIPv4UDPChecksum(ipv4, udp, pkt.Data)) + + return true +} + +func setCorrectL2(pkt *packet.Packet, ctx flow.UserContext) bool { + hc := ctx.(HandlerContext) + + pkt.ParseL3() + + // Fill L2 + pkt.Ether.SAddr = hc.port.macAddress + if hc.port.staticARP { + pkt.Ether.DAddr = hc.port.DstMacAddress + } else { + // Set destination MAC address to DUT's SGi port, so it + // effectively routes a packet to GW IP SGi address + targetMAC, found := hc.port.neighCache.LookupMACForIPv4(hc.port.DUTSGiIPv4) + if !found { + // fmt.Println("Not found MAC address for IP", targetIP.String()) + hc.port.neighCache.SendARPRequestForIPv4(hc.port.DUTSGiIPv4, pkt.GetIPv4NoCheck().SrcAddr, 0) + return false + } + pkt.Ether.DAddr = targetMAC + } + return true +} + +func receiveHandler(pkt *packet.Packet, ctx flow.UserContext) bool { + hc := ctx.(HandlerContext) + + pkt.ParseL3() + protocol := pkt.Ether.EtherType + + if protocol == types.SwapARPNumber { + err := hc.port.neighCache.HandleIPv4ARPPacket(pkt) + if err != nil { + fmt.Println(err) + } + return false + } + atomic.AddUint64(&hc.port.packetCount, 1) + atomic.AddUint64(&hc.port.bytesCount, uint64(pkt.GetPacketLen())) + return true +} + +func printStats(gc *GenConfig, started time.Time) { + fmt.Printf("%v: ", time.Since(started)) + if gc.S1uPort != nil { + fmt.Printf("S1u received %vpkts, %vkB", gc.S1uPort.packetCount, gc.S1uPort.bytesCount/1000) + } + if gc.SgiPort != nil { + if gc.S1uPort != nil { + fmt.Print(". ") + } + fmt.Printf("Sgi received %vpkts, %vkB", gc.SgiPort.packetCount, gc.SgiPort.bytesCount/1000) + } + fmt.Println() +} + +func printTotals(gc *GenConfig, started time.Time) { + runtime := time.Since(started) + runtimeint := uint64(runtime) / uint64(time.Second) + fmt.Printf("\nTest executed for %v\n", runtime) + if gc.S1uPort != nil { + fmt.Printf("S1u pkts/s: %v\n", gc.S1uPort.packetCount/runtimeint) + fmt.Printf("S1u kB/s: %v\n", gc.S1uPort.bytesCount/runtimeint/1000) + } + if gc.SgiPort != nil { + fmt.Printf("Sgi pkts/s: %v\n", gc.SgiPort.packetCount/runtimeint) + fmt.Printf("Sgi kB/s: %v\n", gc.SgiPort.bytesCount/runtimeint/1000) + } +} diff --git a/examples/tutorial/.gitignore b/examples/tutorial/.gitignore index 209bc141..da47fdf5 100644 --- a/examples/tutorial/.gitignore +++ b/examples/tutorial/.gitignore @@ -1,13 +1,13 @@ genscripts -step1 -step2 -step3 -step4 -step5 -step6 -step7 -step8 -step9 +step01 +step02 +step03 +step04 +step05 +step06 +step07 +step08 +step09 step10 step11 nat.json diff --git a/examples/tutorial/Dockerfile b/examples/tutorial/Dockerfile index 35fa98e1..d6b311e1 100644 --- a/examples/tutorial/Dockerfile +++ b/examples/tutorial/Dockerfile @@ -9,15 +9,15 @@ LABEL RUN docker run -it --privileged -v /sys/bus/pci/drivers:/sys/bus/pci/drive WORKDIR /workdir -COPY step1 . -COPY step2 . -COPY step3 . -COPY step4 . -COPY step5 . -COPY step6 . -COPY step7 . -COPY step8 . -COPY step9 . +COPY step01 . +COPY step02 . +COPY step03 . +COPY step04 . +COPY step05 . +COPY step06 . +COPY step07 . +COPY step08 . +COPY step09 . COPY step10 . COPY step11 . COPY rules1.conf . diff --git a/examples/tutorial/Makefile b/examples/tutorial/Makefile index 9d7df2b6..5cd6245d 100644 --- a/examples/tutorial/Makefile +++ b/examples/tutorial/Makefile @@ -4,7 +4,7 @@ PATH_TO_MK = ../../mk IMAGENAME = steps -EXECUTABLES = genscripts step1 step2 step3 step4 step5 step6 step7 step8 step9 step10 step11 +EXECUTABLES = genscripts step01 step02 step03 step04 step05 step06 step07 step08 step09 step10 step11 COMMON_FILES = common.go include $(PATH_TO_MK)/leaf.mk diff --git a/examples/tutorial/NFF-Go tutorial.pdf b/examples/tutorial/NFF-Go tutorial.pdf new file mode 100644 index 00000000..b96c0961 Binary files /dev/null and b/examples/tutorial/NFF-Go tutorial.pdf differ diff --git a/examples/tutorial/YANFF tutorial.pdf b/examples/tutorial/YANFF tutorial.pdf deleted file mode 100644 index 20380272..00000000 Binary files a/examples/tutorial/YANFF tutorial.pdf and /dev/null differ diff --git a/examples/tutorial/genscripts.go b/examples/tutorial/genscripts.go index 1674a9a8..7e211254 100644 --- a/examples/tutorial/genscripts.go +++ b/examples/tutorial/genscripts.go @@ -138,9 +138,9 @@ func main() { _, needWO := workaroundTargets[*target] if *target != "" { - genScript(*pktgenDir, []string{"step2.pg", "step3.pg"}, script1, config[*target][0], config[*target][1], needWO) - genScript(*pktgenDir, []string{"step4.pg"}, script2, config[*target][0], config[*target][1], needWO) - genScript(*pktgenDir, []string{"step5.pg", "step6.pg", "step7.pg", "step8.pg", "step9.pg", "step10.pg", "step11.pg"}, + genScript(*pktgenDir, []string{"step02.pg", "step03.pg"}, script1, config[*target][0], config[*target][1], needWO) + genScript(*pktgenDir, []string{"step04.pg"}, script2, config[*target][0], config[*target][1], needWO) + genScript(*pktgenDir, []string{"step05.pg", "step06.pg", "step07.pg", "step08.pg", "step09.pg", "step10.pg", "step11.pg"}, script3, config[*target][0], config[*target][1], needWO) genScript(*pktgenDir, []string{"nat.pg"}, scriptNat, config[*target][0], config[*target][1], needWO) } diff --git a/examples/tutorial/step1.go b/examples/tutorial/step01.go similarity index 100% rename from examples/tutorial/step1.go rename to examples/tutorial/step01.go diff --git a/examples/tutorial/step2.go b/examples/tutorial/step02.go similarity index 100% rename from examples/tutorial/step2.go rename to examples/tutorial/step02.go diff --git a/examples/tutorial/step3.go b/examples/tutorial/step03.go similarity index 100% rename from examples/tutorial/step3.go rename to examples/tutorial/step03.go diff --git a/examples/tutorial/step4.go b/examples/tutorial/step04.go similarity index 100% rename from examples/tutorial/step4.go rename to examples/tutorial/step04.go diff --git a/examples/tutorial/step5.go b/examples/tutorial/step05.go similarity index 100% rename from examples/tutorial/step5.go rename to examples/tutorial/step05.go diff --git a/examples/tutorial/step6.go b/examples/tutorial/step06.go similarity index 100% rename from examples/tutorial/step6.go rename to examples/tutorial/step06.go diff --git a/examples/tutorial/step7.go b/examples/tutorial/step07.go similarity index 100% rename from examples/tutorial/step7.go rename to examples/tutorial/step07.go diff --git a/examples/tutorial/step8.go b/examples/tutorial/step08.go similarity index 100% rename from examples/tutorial/step8.go rename to examples/tutorial/step08.go diff --git a/examples/tutorial/step9.go b/examples/tutorial/step09.go similarity index 100% rename from examples/tutorial/step9.go rename to examples/tutorial/step09.go diff --git a/flow/counters.go b/flow/counters.go index 5de64865..f91490aa 100644 --- a/flow/counters.go +++ b/flow/counters.go @@ -13,7 +13,7 @@ import ( "strings" "github.com/intel-go/nff-go/common" - "github.com/intel-go/nff-go/low" + "github.com/intel-go/nff-go/internal/low" ) const ( diff --git a/flow/flow.go b/flow/flow.go index a5507435..ffe20cd0 100644 --- a/flow/flow.go +++ b/flow/flow.go @@ -38,7 +38,7 @@ import ( "github.com/intel-go/nff-go/asm" "github.com/intel-go/nff-go/common" - "github.com/intel-go/nff-go/low" + "github.com/intel-go/nff-go/internal/low" "github.com/intel-go/nff-go/packet" "github.com/intel-go/nff-go/types" ) @@ -389,6 +389,7 @@ type HWCapability int const ( HWTXChecksumCapability HWCapability = iota + HWRXPacketsTimestamp ) const ( @@ -397,18 +398,23 @@ const ( recvDone ) -// CheckHWCapability return true if hardware offloading capability -// present in all ports. Otherwise it returns false. -func CheckHWCapability(capa HWCapability, ports []uint16) bool { +// CheckHWCapability returns array of booleans for every requested +// port. An element of this array is set to true if hardware +// offloading capability is supported on corresponding port, otherwise +// it is set to false. +func CheckHWCapability(capa HWCapability, ports []uint16) []bool { + ret := make([]bool, len(ports)) for p := range ports { switch capa { case HWTXChecksumCapability: - if !low.CheckHWTXChecksumCapability(ports[p]) { - return false - } + ret[p] = low.CheckHWTXChecksumCapability(ports[p]) + case HWRXPacketsTimestamp: + ret[p] = low.CheckHWRXPacketsTimestamp(ports[p]) + default: + ret[p] = false } } - return true + return ret } // SetUseHWCapability enables or disables using a hardware offloading @@ -433,7 +439,7 @@ const reportMbits = false var sizeMultiplier uint var schedTime uint -var hwtxchecksum bool +var hwtxchecksum, hwrxpacketstimestamp bool var maxRecv int type port struct { @@ -522,6 +528,16 @@ type Config struct { // // If no string is specified, no HTTP server is spawned. StatsHTTPAddress *net.TCPAddr + // Enables possibility of IP reassembly via chaining packets + ChainedReassembly bool + // Enables possibility of handling jumbo frames via chaining packets + ChainedJumbo bool + // Enables possibility of handling jumbo frames via making huge packets + // Will require big amount of memory + MemoryJumbo bool + // Enables hardware assisted timestamps in packet mbufs. These + // timestamps can be accessed with GetPacketTimestamp function. + HWRXPacketsTimestamp bool } // SystemInit is initialization of system. This function should be always called before graph construction. @@ -544,6 +560,7 @@ func SystemInit(args *Config) error { schedulerOffRemove := args.PersistentClones stopDedicatedCore := args.StopOnDedicatedCore hwtxchecksum = args.HWTXChecksum + hwrxpacketstimestamp = args.HWRXPacketsTimestamp anyway := !args.RestrictedCloning mbufNumber := uint(8191) @@ -612,13 +629,29 @@ func SystemInit(args *Config) error { NoPacketHeadChange = true } + needChainedReassembly := false + if args.ChainedReassembly == true { + needChainedReassembly = true + } + + needChainedJumbo := false + if args.ChainedJumbo == true { + needChainedJumbo = true + } + + needMemoryJumbo := false + if args.MemoryJumbo == true { + needMemoryJumbo = 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, NoPacketHeadChange); err != nil { + if err := low.InitDPDK(argc, argv, burstSize, mbufNumber, mbufCacheSize, needKNI, + NoPacketHeadChange, needChainedReassembly, needChainedJumbo, needMemoryJumbo); err != nil { return err } // Init Ports @@ -670,7 +703,7 @@ func SystemInitPortsAndMemory() error { for i := range createdPorts { if createdPorts[i].wasRequested { if err := low.CreatePort(createdPorts[i].port, createdPorts[i].willReceive, - true, hwtxchecksum, createdPorts[i].InIndex); err != nil { + true, hwtxchecksum, hwrxpacketstimestamp, createdPorts[i].InIndex); err != nil { return err } } @@ -1462,20 +1495,20 @@ func pFastGenerate(parameters interface{}, inIndex []int32, stopper [2]chan int, err := low.AllocateMbufs(bufs, mempool, burstSize) if err != nil { low.ReportMempoolsState() - common.LogFatal(common.Debug, err) - } - if vector == false { - for i := range bufs { - // TODO Maybe we need to prefetch here? - tempPacket = packet.ExtractPacket(bufs[i]) - generateFunction(tempPacket, context[0]) - if reportMbits { - currentState.V.Bytes += uint64(tempPacket.GetPacketLen()) + } else { + if vector == false { + for i := range bufs { + // TODO Maybe we need to prefetch here? + tempPacket = packet.ExtractPacket(bufs[i]) + generateFunction(tempPacket, context[0]) + if reportMbits { + currentState.V.Bytes += uint64(tempPacket.GetPacketLen()) + } } + } else { + packet.ExtractPackets(tempPackets, bufs, burstSize) + vectorGenerateFunction(tempPackets, context[0]) } - } else { - packet.ExtractPackets(tempPackets, bufs, burstSize) - vectorGenerateFunction(tempPackets, context[0]) } safeEnqueue(OUT[0], bufs, burstSize) currentState.V.Packets += uint64(burstSize) diff --git a/flow/scheduler.go b/flow/scheduler.go index 50382311..6390ea42 100644 --- a/flow/scheduler.go +++ b/flow/scheduler.go @@ -26,7 +26,7 @@ import ( "time" "github.com/intel-go/nff-go/common" - "github.com/intel-go/nff-go/low" + "github.com/intel-go/nff-go/internal/low" ) const generatePauseStep = 0.1 diff --git a/low/Makefile b/internal/low/Makefile similarity index 91% rename from low/Makefile rename to internal/low/Makefile index f2d34ca8..d310b737 100644 --- a/low/Makefile +++ b/internal/low/Makefile @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -PATH_TO_MK = ../mk +PATH_TO_MK = ../../mk include $(PATH_TO_MK)/include.mk .PHONY: testing diff --git a/low/low.go b/internal/low/low.go similarity index 96% rename from low/low.go rename to internal/low/low.go index e8e5748a..9ef31035 100644 --- a/low/low.go +++ b/internal/low/low.go @@ -13,7 +13,6 @@ package low // it increases executable size and build time. /* -#cgo LDFLAGS: -lrte_distributor -lrte_reorder -lrte_kni -lrte_pipeline -lrte_table -lrte_port -lrte_timer -lrte_jobstats -lrte_lpm -lrte_power -lrte_acl -lrte_meter -lrte_sched -lrte_vhost -lrte_ip_frag -lrte_cfgfile -Wl,--whole-archive -Wl,--start-group -lrte_kvargs -lrte_mbuf -lrte_hash -lrte_ethdev -lrte_mempool -lrte_ring -lrte_mempool_ring -lrte_eal -lrte_cmdline -lrte_net -lrte_bus_pci -lrte_pci -lrte_bus_vdev -lrte_timer -lrte_pmd_bond -lrte_pmd_vmxnet3_uio -lrte_pmd_virtio -lrte_pmd_cxgbe -lrte_pmd_enic -lrte_pmd_i40e -lrte_pmd_fm10k -lrte_pmd_ixgbe -lrte_pmd_e1000 -lrte_pmd_ena -lrte_pmd_ring -lrte_pmd_af_packet -lrte_pmd_null -libverbs -lmnl -lmlx4 -lmlx5 -lrte_pmd_mlx4 -lrte_pmd_mlx5 -Wl,--end-group -Wl,--no-whole-archive -lrt -lm -ldl -lnuma #include "low.h" */ import "C" @@ -545,8 +544,13 @@ 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, NoPacketHeadChange bool) error { - ret := C.eal_init(argc, argv, C.uint32_t(burstSize), C.int32_t(needKNI), C.bool(NoPacketHeadChange)) +func InitDPDK(argc C.int, argv **C.char, burstSize uint, mbufNumber uint, mbufCacheSize uint, needKNI int, + NoPacketHeadChange bool, needChainedReassembly bool, needChainedJumbo bool, needMemoryJumbo bool) error { + if needChainedReassembly && needChainedJumbo || needChainedReassembly && needMemoryJumbo || needChainedJumbo && needMemoryJumbo { + return common.WrapWithNFError(nil, "Memory jumbo, chained jumbo or IP reassembly is unsupported together\n", common.FailToInitDPDK) + } + ret := C.eal_init(argc, argv, C.uint32_t(burstSize), C.int32_t(needKNI), + C.bool(NoPacketHeadChange), C.bool(needChainedReassembly), C.bool(needChainedJumbo), C.bool(needMemoryJumbo)) if ret < 0 { return common.WrapWithNFError(nil, "Error with EAL initialization\n", common.FailToInitDPDK) } @@ -590,7 +594,7 @@ func CheckPortRSS(port uint16) int32 { } // CreatePort initializes a new port using global settings and parameters. -func CreatePort(port uint16, willReceive bool, promiscuous bool, hwtxchecksum bool, inIndex int32) error { +func CreatePort(port uint16, willReceive bool, promiscuous bool, hwtxchecksum, hwrxpacketstimestamp bool, inIndex int32) error { var mempools **C.struct_rte_mempool if willReceive { m := CreateMempools("receive", inIndex) @@ -598,8 +602,8 @@ func CreatePort(port uint16, willReceive bool, promiscuous bool, hwtxchecksum bo } else { mempools = nil } - if C.port_init(C.uint16_t(port), C.bool(willReceive), - mempools, C._Bool(promiscuous), C._Bool(hwtxchecksum), C.int32_t(inIndex)) != 0 { + if C.port_init(C.uint16_t(port), C.bool(willReceive), mempools, + C._Bool(promiscuous), C._Bool(hwtxchecksum), C._Bool(hwrxpacketstimestamp), C.int32_t(inIndex)) != 0 { msg := common.LogError(common.Initialization, "Cannot init port ", port, "!") return common.WrapWithNFError(nil, msg, common.FailToInitPort) } @@ -752,6 +756,10 @@ func CheckHWTXChecksumCapability(port uint16) bool { return bool(C.check_hwtxchecksum_capability(C.uint16_t(port))) } +func CheckHWRXPacketsTimestamp(port uint16) bool { + return bool(C.check_hwrxpackets_timestamp_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)), @@ -771,3 +779,15 @@ func InitDevice(device string) int { func SetCountersEnabledInApplication(enabled bool) { C.counters_enabled_in_application = C.bool(true) } + +func SetNextMbuf(next *Mbuf, prev *Mbuf) { + prev.next = (*C.struct_rte_mbuf)(next) +} + +func GetPacketOffloadFlags(mb *Mbuf) uint64 { + return uint64(mb.ol_flags) +} + +func GetPacketTimestamp(mb *Mbuf) uint64 { + return uint64(mb.timestamp) +} diff --git a/low/low.h b/internal/low/low.h similarity index 91% rename from low/low.h rename to internal/low/low.h index 9f08ee98..af656d0b 100644 --- a/low/low.h +++ b/internal/low/low.h @@ -43,8 +43,9 @@ #define APP_RETA_SIZE_MAX (ETH_RSS_RETA_SIZE_512 / RTE_RETA_GROUP_SIZE) +#define MAX_JUMBO_PKT_LEN 9600 // from most DPDK examples. Only for MEMORY_JUMBO. + // #define DEBUG -// #define REASSEMBLY #define COUNTERS_ENABLED #define USE_INTERLOCKED_COUNTERS #define ANALYZE_PACKETS_SIZES @@ -66,13 +67,6 @@ #define mbufInitNextChain(buf) \ *(char **)((char *)(buf) + mbufStructSize + 40) = 0; -#ifdef REASSEMBLY -#define REASSEMBLY_INIT \ - struct rte_ip_frag_tbl* tbl = create_reassemble_table(); \ - 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; - // 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) @@ -80,11 +74,16 @@ #define mbufSetNext(buf) \ *(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 + struct rte_ip_frag_death_row* pdeath_row = NULL; \ + if (CHAINED_REASSEMBLY) { \ + tbl = create_reassemble_table(); \ + struct rte_ip_frag_death_row death_row; \ + death_row.cnt = 0; \ + pdeath_row = &death_row; \ + } #ifdef COUNTERS_ENABLED #ifdef USE_INTERLOCKED_COUNTERS @@ -169,6 +168,11 @@ static int KNI_config_mac_address(uint16_t port_id, uint8_t mac_addr[]); static int KNI_config_promiscusity(uint16_t port_id, uint8_t to_on); uint32_t BURST_SIZE; +bool CHAINED_REASSEMBLY; +bool CHAINED_JUMBO; +bool MEMORY_JUMBO; +bool JUMBO; +bool CHAINED; struct cPort { uint16_t PortId; @@ -255,7 +259,7 @@ int check_port_tx(uint16_t port) { // Initializes a given port using global settings and with the RX buffers // coming from the mbuf_pool passed as a parameter. -int port_init(uint16_t port, bool willReceive, struct rte_mempool **mbuf_pools, bool promiscuous, bool hwtxchecksum, int32_t inIndex) { +int port_init(uint16_t port, bool willReceive, struct rte_mempool **mbuf_pools, bool promiscuous, bool hwtxchecksum, bool hwrxpacketstimestamp, int32_t inIndex) { uint16_t rx_rings, tx_rings = TX_QUEUE_NUMBER; struct rte_eth_dev_info dev_info; @@ -283,11 +287,24 @@ int port_init(uint16_t port, bool willReceive, struct rte_mempool **mbuf_pools, .rx_adv_conf.rss_conf.rss_hf = dev_info.flow_type_rss_offloads }; + if (JUMBO) { + port_conf_default.rxmode.max_rx_pkt_len = dev_info.max_rx_pktlen; + port_conf_default.rxmode.offloads = DEV_RX_OFFLOAD_JUMBO_FRAME; + } + if (CHAINED) { + port_conf_default.txmode.offloads = DEV_TX_OFFLOAD_MULTI_SEGS; + } + if (hwtxchecksum) { /* Enable everything that is supported by hardware */ port_conf_default.txmode.offloads = dev_info.tx_offload_capa; } + if (hwrxpacketstimestamp) { + /* Enable hardware timestamping */ + port_conf_default.rxmode.offloads |= dev_info.rx_offload_capa & DEV_RX_OFFLOAD_TIMESTAMP; + } + /* Configure the Ethernet device. */ int retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf_default); if (retval != 0) @@ -349,43 +366,46 @@ static inline void handleUnpushed(struct rte_mbuf *bufs[BURST_SIZE], uint16_t re } } +static inline struct rte_mbuf* reassemble(struct rte_ip_frag_tbl*, struct rte_mbuf*, struct rte_ip_frag_death_row*, uint64_t); + __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) { -#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(); - 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. - if (L2CanBeChanged == true) { - mbufInitL2(bufs[i]); + if (CHAINED) { + uint16_t temp_number = 0; + uint64_t cur_tsc; + if (CHAINED_REASSEMBLY) { + cur_tsc = rte_rdtsc(); + } + for (uint16_t i = 0; i < rx_pkts_number; i++) { + mbufInitNextChain(bufs[i]); + if (CHAINED_REASSEMBLY) { + // 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 *)); + bufs[i] = reassemble(tbl, bufs[i], death_row, cur_tsc); + if (bufs[i] == NULL) { + continue; + } + } + struct rte_mbuf *temp = bufs[i]; + while (temp->next != NULL) { + mbufSetNext(temp); + temp = temp->next; + } + bufs[temp_number] = bufs[i]; + temp_number++; + } + rx_pkts_number = temp_number; + if (CHAINED_REASSEMBLY) { + rte_ip_frag_free_death_row(death_row, 0 /* PREFETCH_OFFSET */); } - 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 *)); - bufs[i] = reassemble(tbl, bufs[i], death_row, cur_tsc); - if (bufs[i] == NULL) { - continue; - } - struct rte_mbuf *temp = bufs[i]; - while (temp->next != NULL) { - mbufSetNext(temp); - temp = temp->next; - } - bufs[temp_number] = bufs[i]; - temp_number++; } - rx_pkts_number = temp_number; - rte_ip_frag_free_death_row(death_row, 0 /* PREFETCH_OFFSET */); -#endif return rx_pkts_number; } @@ -624,8 +644,8 @@ 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, bool noPacketHeadChange) -{ +int eal_init(int argc, char *argv[], uint32_t burstSize, int32_t needKNI, bool noPacketHeadChange, + bool needChainedReassembly, bool needChainedJumbo, bool needMemoryJumbo) { int ret = rte_eal_init(argc, argv); if (ret < 0) return -1; @@ -641,6 +661,11 @@ int eal_init(int argc, char *argv[], uint32_t burstSize, int32_t needKNI, bool n if (needKNI != 0) { rte_kni_init(MAX_KNI); } + CHAINED_REASSEMBLY = needChainedReassembly; + CHAINED_JUMBO = needChainedJumbo; + MEMORY_JUMBO = needMemoryJumbo; + JUMBO = MEMORY_JUMBO || CHAINED_JUMBO; + CHAINED = CHAINED_REASSEMBLY || CHAINED_JUMBO; return 0; } @@ -651,9 +676,9 @@ int allocateMbufs(struct rte_mempool *mempool, struct rte_mbuf **bufs, unsigned if (L2CanBeChanged == true) { mbufInitL2(bufs[i]); } -#ifdef REASSEMBLY - mbufInitNextChain(bufs[i]); -#endif + if (CHAINED) { + mbufInitNextChain(bufs[i]); + } } } return ret; @@ -662,9 +687,14 @@ int allocateMbufs(struct rte_mempool *mempool, struct rte_mbuf **bufs, unsigned struct rte_mempool * createMempool(uint32_t num_mbufs, uint32_t mbuf_cache_size) { struct rte_mempool *mbuf_pool; + int mbufSize = RTE_MBUF_DEFAULT_BUF_SIZE; + if (MEMORY_JUMBO) { + mbufSize = MAX_JUMBO_PKT_LEN; + } + /* Creates a new mempool in memory to hold the mbufs. */ mbuf_pool = rte_pktmbuf_pool_create(mempoolName, num_mbufs, - mbuf_cache_size, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id()); + mbuf_cache_size, 0, mbufSize, rte_socket_id()); mempoolName[7]++; @@ -786,6 +816,18 @@ bool check_hwtxchecksum_capability(uint16_t port_id) { return (dev_info.tx_offload_capa & flags) == flags; } +bool check_hwrxpackets_timestamp_capability(uint16_t port_id) { + uint64_t flags = DEV_RX_OFFLOAD_TIMESTAMP; + struct rte_eth_dev_info dev_info; + + if (port_id >= rte_eth_dev_count()) + return false; + + memset(&dev_info, 0, sizeof(dev_info)); + rte_eth_dev_info_get(port_id, &dev_info); + return (dev_info.rx_offload_capa & flags) == flags; +} + int initDevice(char *name) { int s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (s < 1) { diff --git a/internal/low/low_mlx.go b/internal/low/low_mlx.go new file mode 100644 index 00000000..17f4d2b6 --- /dev/null +++ b/internal/low/low_mlx.go @@ -0,0 +1,12 @@ +// Copyright 2019 Intel Corporation. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build mlx + +package low + +/* +#cgo LDFLAGS: -lrte_distributor -lrte_reorder -lrte_kni -lrte_pipeline -lrte_table -lrte_port -lrte_timer -lrte_jobstats -lrte_lpm -lrte_power -lrte_acl -lrte_meter -lrte_sched -lrte_vhost -lrte_ip_frag -lrte_cfgfile -Wl,--whole-archive -Wl,--start-group -lrte_kvargs -lrte_mbuf -lrte_hash -lrte_ethdev -lrte_mempool -lrte_ring -lrte_mempool_ring -lrte_eal -lrte_cmdline -lrte_net -lrte_bus_pci -lrte_pci -lrte_bus_vdev -lrte_timer -lrte_pmd_bond -lrte_pmd_vmxnet3_uio -lrte_pmd_virtio -lrte_pmd_cxgbe -lrte_pmd_enic -lrte_pmd_i40e -lrte_pmd_fm10k -lrte_pmd_ixgbe -lrte_pmd_e1000 -lrte_pmd_ena -lrte_pmd_ring -lrte_pmd_af_packet -lrte_pmd_null -libverbs -lmnl -lmlx4 -lmlx5 -lrte_pmd_mlx4 -lrte_pmd_mlx5 -Wl,--end-group -Wl,--no-whole-archive -lrt -lm -ldl -lnuma +*/ +import "C" diff --git a/internal/low/low_no_mlx.go b/internal/low/low_no_mlx.go new file mode 100644 index 00000000..4460b908 --- /dev/null +++ b/internal/low/low_no_mlx.go @@ -0,0 +1,12 @@ +// Copyright 2019 Intel Corporation. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !mlx + +package low + +/* +#cgo LDFLAGS: -lrte_distributor -lrte_reorder -lrte_kni -lrte_pipeline -lrte_table -lrte_port -lrte_timer -lrte_jobstats -lrte_lpm -lrte_power -lrte_acl -lrte_meter -lrte_sched -lrte_vhost -lrte_ip_frag -lrte_cfgfile -Wl,--whole-archive -Wl,--start-group -lrte_kvargs -lrte_mbuf -lrte_hash -lrte_ethdev -lrte_mempool -lrte_ring -lrte_mempool_ring -lrte_eal -lrte_cmdline -lrte_net -lrte_bus_pci -lrte_pci -lrte_bus_vdev -lrte_timer -lrte_pmd_bond -lrte_pmd_vmxnet3_uio -lrte_pmd_virtio -lrte_pmd_cxgbe -lrte_pmd_enic -lrte_pmd_i40e -lrte_pmd_fm10k -lrte_pmd_ixgbe -lrte_pmd_e1000 -lrte_pmd_ena -lrte_pmd_ring -lrte_pmd_af_packet -lrte_pmd_null -Wl,--end-group -Wl,--no-whole-archive -lrt -lm -ldl -lnuma +*/ +import "C" diff --git a/low/low_test.go b/internal/low/low_test.go similarity index 95% rename from low/low_test.go rename to internal/low/low_test.go index 86cb7848..f56e626a 100644 --- a/low/low_test.go +++ b/internal/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, false); err != nil { + if err := InitDPDK(argc, argv, 32, 8191, 250, 0, false, false, false, false); err != nil { log.Fatalf("fail to initialize with error: %+v\n", err) } rand.Seed(time.Now().UTC().UnixNano()) diff --git a/mk/leaf.mk b/mk/leaf.mk index 72e4f59a..1e98c6b8 100644 --- a/mk/leaf.mk +++ b/mk/leaf.mk @@ -10,13 +10,19 @@ include $(PATH_TO_MK)/include.mk # Build all .PHONY: clean +ifndef NFF_GO_NO_MLX_DRIVERS +ifeq (,$(findstring mlx,$(GO_BUILD_TAGS))) +export GO_BUILD_TAGS += mlx +endif +endif + ifdef NFF_GO_DEBUG # Flags to build Go files without optimizations export GO_COMPILE_FLAGS += -gcflags=all='-N -l' endif $(EXECUTABLES) : % : %.go - go build $(GO_COMPILE_FLAGS) $< $(COMMON_FILES) + go build $(GO_COMPILE_FLAGS) -tags "${GO_BUILD_TAGS}" $< $(COMMON_FILES) ifndef NOCHECK_PKTGEN all: check-pktgen diff --git a/nff-go-base/Makefile b/nff-go-base/Makefile index e7b1dd21..bfaebf29 100644 --- a/nff-go-base/Makefile +++ b/nff-go-base/Makefile @@ -22,7 +22,7 @@ Fedora: Makefile Dockerfile: Makefile echo 'ARG USER_NAME' > Dockerfile - echo 'FROM ubuntu:cosmic' >> Dockerfile + echo 'FROM ubuntu:disco' >> Dockerfile if [ -n '${http_proxy}' ]; then \ echo 'ENV http_proxy ${http_proxy}' >> Dockerfile; \ echo 'ENV https_proxy ${https_proxy}' >> Dockerfile; \ diff --git a/packet/checksum.go b/packet/checksum.go index a6c88814..abca58ca 100644 --- a/packet/checksum.go +++ b/packet/checksum.go @@ -7,7 +7,7 @@ package packet import ( "unsafe" - "github.com/intel-go/nff-go/low" + "github.com/intel-go/nff-go/internal/low" . "github.com/intel-go/nff-go/types" ) diff --git a/packet/gtp.go b/packet/gtp.go index ae52fbe2..7fd3e362 100644 --- a/packet/gtp.go +++ b/packet/gtp.go @@ -36,7 +36,7 @@ const ( ) type UDPPort struct { - Lenght uint8 // in 4 octets, here always 0x01 + Length uint8 // in 4 octets, here always 0x01 UDPPortNumber uint16 NextExtensionHeader uint8 } diff --git a/packet/neigh.go b/packet/neigh.go index 48ae43f3..6fda1723 100644 --- a/packet/neigh.go +++ b/packet/neigh.go @@ -7,26 +7,37 @@ package packet import ( "fmt" "sync" + "time" "github.com/intel-go/nff-go/common" "github.com/intel-go/nff-go/types" ) +const ( + arpRequestsRepeatInterval = 1 * time.Second +) + type NeighboursLookupTable struct { - portIndex uint16 - ipv4Table sync.Map - ipv6Table sync.Map - interfaceMAC types.MACAddress - ipv4InterfaceAddr types.IPv4Address - ipv6InterfaceAddr types.IPv6Address + portIndex uint16 + ipv4Table sync.Map + ipv6Table sync.Map + ipv4SentRequestTable sync.Map + ipv6SentRequestTable sync.Map + interfaceMAC types.MACAddress + // Should return true if IPv4 address belongs to interface + checkv4 func(ipv4 types.IPv4Address) bool + // Should return true if IPv6 address belongs to interface + checkv6 func(ipv6 types.IPv6Address) bool } -func NewNeighbourTable(index uint16, mac types.MACAddress, ipv4 types.IPv4Address, ipv6 types.IPv6Address) *NeighboursLookupTable { +func NewNeighbourTable(index uint16, mac types.MACAddress, + checkv4 func(ipv4 types.IPv4Address) bool, + checkv6 func(ipv6 types.IPv6Address) bool) *NeighboursLookupTable { return &NeighboursLookupTable{ - portIndex: index, - interfaceMAC: mac, - ipv4InterfaceAddr: ipv4, - ipv6InterfaceAddr: ipv6, + portIndex: index, + interfaceMAC: mac, + checkv4: checkv4, + checkv6: checkv6, } } @@ -48,17 +59,15 @@ func (table *NeighboursLookupTable) HandleIPv4ARPPacket(pkt *Packet) error { // 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()) + targetIP := types.BytesToIPv4(arp.TPA[0], arp.TPA[1], arp.TPA[2], arp.TPA[3]) + if !table.checkv4(targetIP) { + return fmt.Errorf("Warning! Got an ARP packet with target IPv4 address %s different from IPv4 address on interface. ARP request ignored.", types.IPv4ArrayToString(arp.TPA)) } // Prepare an answer to this request answerPacket, err := NewPacket() if err != nil { - common.LogFatal(common.Debug, err) + return err } InitARPReplyPacket(answerPacket, table.interfaceMAC, arp.SHA, types.ArrayToIPv4(arp.TPA), types.ArrayToIPv4(arp.SPA)) @@ -84,17 +93,28 @@ func (table *NeighboursLookupTable) LookupMACForIPv4(ipv4 types.IPv4Address) (ty // 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) { +func (table *NeighboursLookupTable) SendARPRequestForIPv4(ipv4, myIPv4Address types.IPv4Address, vlan uint16) { + v, found := table.ipv4SentRequestTable.Load(ipv4) + if found { + lastsent := v.(time.Time) + if time.Since(lastsent) < arpRequestsRepeatInterval { + // Another ARP request has beep sent recently, we're still + // waiting for reply + return + } + } + requestPacket, err := NewPacket() if err != nil { common.LogFatal(common.Debug, err) } - InitARPRequestPacket(requestPacket, table.interfaceMAC, table.ipv4InterfaceAddr, ipv4) + InitARPRequestPacket(requestPacket, table.interfaceMAC, myIPv4Address, ipv4) if vlan != 0 { requestPacket.AddVLANTag(vlan) } requestPacket.SendPacket(table.portIndex) + table.ipv4SentRequestTable.Store(ipv4, time.Now()) } diff --git a/packet/packet.go b/packet/packet.go index 2ad570e6..4805f0f1 100644 --- a/packet/packet.go +++ b/packet/packet.go @@ -46,7 +46,7 @@ import ( "unsafe" . "github.com/intel-go/nff-go/common" - "github.com/intel-go/nff-go/low" + "github.com/intel-go/nff-go/internal/low" "github.com/intel-go/nff-go/types" ) @@ -488,6 +488,24 @@ func InitEmptyPacket(packet *Packet, plSize uint) bool { return true } +// InitNextPacket creates new packet with plSize bytes, +// packet is treated as one of segments: +// Data pointer is set to the beginning of packet +// new packet is attached to Next pointer of prev packet +// Return new packet or nil if error +// Function is not performance efficient due to use of single packet allocation +func InitNextPacket(plSize uint, prev *Packet) *Packet { + packet, err := NewPacket() + if err != nil || low.AppendMbuf(packet.CMbuf, plSize) == false { + LogWarning(Debug, "InitNextPacket: Cannot allocate new packet") + return nil + } + packet.Data = unsafe.Pointer(packet.Ether) + prev.Next = packet + low.SetNextMbuf(packet.CMbuf, prev.CMbuf) + return packet +} + func fillIPv4Default(packet *Packet, plLen uint16, nextProto uint8) { packet.GetIPv4NoCheck().VersionIhl = types.IPv4VersionIhl packet.GetIPv4NoCheck().TotalLength = SwapBytesUint16(plLen) @@ -820,7 +838,7 @@ func NewPacket() (*Packet, error) { return pkt, nil } -// SendPacket immidiately sends packet to specified port via calling C function. +// SendPacket immediately sends packet to specified port via calling C function. // Packet is freed. Function return true if packet was actually sent. // Port should be initialized. Packet is sent to zero queue (is always present). // Sending simultaneously to one port is permitted in DPDK. @@ -869,8 +887,8 @@ func (lpm *LPM) Lookup(ip types.IPv4Address, nextHop *types.IPv4Address) bool { tbl_entry = (*lpm.tbl8)[tbl8_index] } - *nextHop = tbl_entry & 0x00FFFFFF if tbl_entry&low.RteLpmLookupSuccess != 0 { + *nextHop = tbl_entry & 0x00FFFFFF return true } return false @@ -892,3 +910,15 @@ func (lpm *LPM) Delete(ip types.IPv4Address, depth uint8) int { func (lpm *LPM) Free() { low.FreeLPM(lpm.lpm) } + +// GetPacketOffloadFlags returns ol_flags field of packet mbuf +func (pkt *Packet) GetPacketOffloadFlags() uint64 { + return low.GetPacketOffloadFlags(pkt.CMbuf) +} + +// GetPacketTimestamp returns timestamp field of packet mbuf. Check +// that flag PKT_RX_TIMESTAMP (1ULL << 17) is set in value returned by +// GetPacketOffloadFlags. +func (pkt *Packet) GetPacketTimestamp() uint64 { + return low.GetPacketTimestamp(pkt.CMbuf) +} diff --git a/packet/pcap_utils.go b/packet/pcap_utils.go index 41000662..f5806214 100644 --- a/packet/pcap_utils.go +++ b/packet/pcap_utils.go @@ -10,7 +10,7 @@ import ( "time" "github.com/intel-go/nff-go/common" - "github.com/intel-go/nff-go/low" + "github.com/intel-go/nff-go/internal/low" ) type nowFuncT func() time.Time diff --git a/packet/utils_for_test.go b/packet/utils_for_test.go index 022afc49..d18b07f0 100644 --- a/packet/utils_for_test.go +++ b/packet/utils_for_test.go @@ -8,7 +8,7 @@ import ( "net" "os" - "github.com/intel-go/nff-go/low" + "github.com/intel-go/nff-go/internal/low" "github.com/intel-go/nff-go/types" ) @@ -22,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, false); err != nil { + if err := low.InitDPDK(argc, argv, 32, 8191, 250, 0, false, false, false, false); err != nil { log.Fatal(err) } nonPerfMempool = low.CreateMempool("Test") diff --git a/test/stability/stabilityCommon/common.go b/test/stability/stabilityCommon/common.go index 53ae4dd2..7fa7b283 100644 --- a/test/stability/stabilityCommon/common.go +++ b/test/stability/stabilityCommon/common.go @@ -129,7 +129,7 @@ func InitCommonState(configFile, target string) { return } - // Get destination MAC addressess for port 0 and 1 from config file + // Get destination MAC addresses for port 0 and 1 from config file if hw, err := net.ParseMAC(config[target][0]); err == nil { copy(dstMac0[:], hw) } else { diff --git a/test/stability/testCksum/checksum_test.go b/test/stability/testCksum/checksum_test.go index a649b346..a5169a1b 100644 --- a/test/stability/testCksum/checksum_test.go +++ b/test/stability/testCksum/checksum_test.go @@ -15,9 +15,7 @@ func init() { } } -// TODO unfortunately this test can't be executed as go test right now due to https://github.com/intel-go/nff-go/issues/301 -// If you want to test it via go test you should firstly comment FreeMempools function at flow/flow.go -// You will have multiple mempools however test will pass. +// If it will fail due to strange mempool behaviour feel free to reopen https://github.com/intel-go/nff-go/issues/301 func TestChecksum(t *testing.T) { variants := []struct{ useUDP, useTCP, useICMP bool }{ {false, false, false}, diff --git a/test/stability/testCksum/testCksum.go b/test/stability/testCksum/testCksum.go index 9eaebd05..4229d4f8 100644 --- a/test/stability/testCksum/testCksum.go +++ b/test/stability/testCksum/testCksum.go @@ -124,7 +124,11 @@ func configTest(shouldUseIPv4, shouldUseIPv6, shouldUseUDP, shouldUseTCP, should ports := []uint16{uint16(inport), uint16(outport)} offloadingAvailable := flow.CheckHWCapability(flow.HWTXChecksumCapability, ports) - if hwol && !offloadingAvailable { + offloadingAvailableOnAllPorts := offloadingAvailable[0] + for i := 1; offloadingAvailableOnAllPorts && i < len(offloadingAvailable); i++ { + offloadingAvailableOnAllPorts = offloadingAvailableOnAllPorts && offloadingAvailable[i] + } + if hwol && !offloadingAvailableOnAllPorts { println("Warning! Requested hardware offloading is not available on all ports. Falling back to software checksum calculation.") hwol = false flow.SetUseHWCapability(flow.HWTXChecksumCapability, false) diff --git a/test/stability/testMerge/merge_test.go b/test/stability/testMerge/merge_test.go index 9d7bd599..291a8a87 100644 --- a/test/stability/testMerge/merge_test.go +++ b/test/stability/testMerge/merge_test.go @@ -15,9 +15,7 @@ func init() { } } -// TODO unfortunately this test can't be executed as go test right now due to https://github.com/intel-go/nff-go/issues/301 -// If you want to test it via go test you should firstly comment FreeMempools function at flow/flow.go -// You will have multiple mempools however test will pass. +// If it will fail due to strange mempool behaviour feel free to reopen https://github.com/intel-go/nff-go/issues/301 func TestMerge(t *testing.T) { scenarios := []pipelineScenario{getGet, segmGet, multipleSegm, singleSegm, getCopy, copyCopy, segmCopy} addSegmVariants := []bool{true, false} diff --git a/test/stability/testSingleWorkingFF/singleWorkingFF_test.go b/test/stability/testSingleWorkingFF/singleWorkingFF_test.go index 2f3c3bb7..f92e4b76 100644 --- a/test/stability/testSingleWorkingFF/singleWorkingFF_test.go +++ b/test/stability/testSingleWorkingFF/singleWorkingFF_test.go @@ -9,9 +9,6 @@ import ( "testing" ) -// TODO unfortunately this test can't be executed as go test right now due to https://github.com/intel-go/nff-go/issues/301 -// If you want to test it via go test you should firstly comment FreeMempools function at flow/flow.go -// You will have multiple mempools however test will pass. func init() { if err := initDPDK(); err != nil { log.Fatalf("fail: %+v\n", err) diff --git a/types/mac.go b/types/mac.go index 27eaea23..e25aac02 100644 --- a/types/mac.go +++ b/types/mac.go @@ -5,7 +5,9 @@ package types import ( + "encoding/json" "fmt" + "net" ) type MACAddress [EtherAddrLen]uint8 @@ -14,3 +16,27 @@ type MACAddress [EtherAddrLen]uint8 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]) } + +// StringToMACAddress parses a string which contains a MAC address. +func StringToMACAddress(str string) (MACAddress, error) { + hw, err := net.ParseMAC(str) + if err != nil { + return MACAddress{}, err + } + var out MACAddress + copy(out[:], hw) + return out, nil +} + +// UnmarshalJSON parses JSON element which contains string which +// contains MAC address. +func (out *MACAddress) UnmarshalJSON(b []byte) error { + var s string + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + + *out, err = StringToMACAddress(s) + return err +} diff --git a/vagrant/Vagrantfile b/vagrant/Vagrantfile index 9b7e6c96..de9d615f 100644 --- a/vagrant/Vagrantfile +++ b/vagrant/Vagrantfile @@ -19,7 +19,7 @@ Vagrant.configure(2) do |config| vm_port_base = ENV.fetch("VM_TUNNEL_PORT_BASE", 12345).to_i vm_second_port_base = vm_port_base + (vm_links_number + 1) * vm_total_number - config.vm.box = "generic/ubuntu1810" + config.vm.box = "generic/ubuntu1904" # config.vm.box = "fedora/28-cloud-base" # Docker server port @@ -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.5.linux-amd64.tar.gz | tar zx') +(cd /opt; sudo sh -c 'curl -L -s https://dl.google.com/go/go1.12.5.linux-amd64.tar.gz | tar zx') mkdir go chmod +x ~/scripts.sh . ~/scripts.sh