diff --git a/lang/go/bindings/cne/packet.go b/lang/go/bindings/cne/packet.go index 6368050e..0a21277e 100644 --- a/lang/go/bindings/cne/packet.go +++ b/lang/go/bindings/cne/packet.go @@ -26,6 +26,8 @@ import "C" import ( "fmt" "unsafe" + "encoding/binary" + "hash/fnv" ) type Packet C.pktmbuf_t // Packet is the interface type for C.pktmbuf_t structure @@ -227,8 +229,10 @@ func GetIPv4(pkt *Packet) *IPv4Hdr { if pkt != nil { ether := GetEtherHdr(pkt) - + fmt.Println("ethernet Header", ether) + //fmt.Println("Packet Info", pkt) if ether != nil && ether.EtherType == SwapUint16(EtherTypeIPV4) { + fmt.Printf("etherType: %v %02x", ether, ether.EtherType) return (*IPv4Hdr)((unsafe.Pointer)(uintptr(unsafe.Pointer(ether)) + uintptr(EtherHdrLen))) } } @@ -285,3 +289,40 @@ func GetTCP(pkt *Packet) *TCPHdr { return nil } + +// GetHash will calculate with 3 tuples (src ip, dst ip and protocol) +func GetHash(pkt *Packet) uint32 { + var t3Tuple []byte + ipv4 := GetIPv4(pkt) + ipv6 := GetIPv6(pkt) + if ipv4 != nil { + fmt.Println("ipv4 present") + t3Tuple = make([]byte, 9) + b := make([]byte, 4) + binary.LittleEndian.PutUint32(b, uint32(ipv4.SrcAddr)) + t3Tuple = append(t3Tuple, b...) + + binary.LittleEndian.PutUint32(b, uint32(ipv4.DstAddr)) + t3Tuple = append(t3Tuple, b...) + + t3Tuple = append(t3Tuple, ipv4.NextProtoID) + var i int + for ;i<9;i++ { + fmt.Printf("%02x", t3Tuple[i]) + } + } else if ipv6 != nil { + fmt.Println("ipv6present") + t3Tuple = make([]byte, IPv6AddrLen+IPv6AddrLen+1) + copy(t3Tuple[:], ipv6.SrcAddr[:IPv6AddrLen]) + copy(t3Tuple[IPv6AddrLen:], ipv6.DstAddr[:IPv6AddrLen]) + t3Tuple = append(t3Tuple, ipv6.Proto) + } + var hash uint32 + if ipv4 != nil || ipv6 != nil { + h := fnv.New32a() + h.Write(t3Tuple) + hash = h.Sum32() + } + return hash +} + diff --git a/lang/go/bindings/examples/sampling/go.mod b/lang/go/bindings/examples/sampling/go.mod new file mode 100644 index 00000000..1e0d1af8 --- /dev/null +++ b/lang/go/bindings/examples/sampling/go.mod @@ -0,0 +1,31 @@ +module github.com/CloudNativeDataPlane/cndp/lang/go/bindings/examples/sampling + +replace github.com/CloudNativeDataPlane/cndp/lang/go/bindings/cne => ../../cne + +replace github.com/CloudNativeDataPlane/cndp/lang/go/tools/pkgs/ttylog => ../../../tools/pkgs/ttylog + +replace github.com/CloudNativeDataPlane/cndp/lang/go/tools/pkgs/etimers => ../../../tools/pkgs/etimers + +replace github.com/CloudNativeDataPlane/cndp/lang/go/tools/pkgs/colorize => ../../../tools/pkgs/colorize + +go 1.18 + +require ( + github.com/CloudNativeDataPlane/cndp/lang/go/bindings/cne v0.0.0-00010101000000-000000000000 + github.com/CloudNativeDataPlane/cndp/lang/go/tools/pkgs/colorize v0.0.0-00010101000000-000000000000 + github.com/CloudNativeDataPlane/cndp/lang/go/tools/pkgs/etimers v0.0.0-00010101000000-000000000000 + github.com/CloudNativeDataPlane/cndp/lang/go/tools/pkgs/ttylog v0.0.0-20220730140151-855a3ef0ebc1 + github.com/gdamore/tcell/v2 v2.5.2 + github.com/rivo/tview v0.0.0-20220731115447-9d32d269593e + golang.org/x/text v0.3.8 +) + +require ( + github.com/gdamore/encoding v1.0.0 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/mattn/go-runewidth v0.0.13 // indirect + github.com/rivo/uniseg v0.3.1 // indirect + github.com/tidwall/jsonc v0.3.2 // indirect + golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 // indirect + golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect +) diff --git a/lang/go/bindings/examples/sampling/go.sum b/lang/go/bindings/examples/sampling/go.sum new file mode 100644 index 00000000..6c6020f8 --- /dev/null +++ b/lang/go/bindings/examples/sampling/go.sum @@ -0,0 +1,30 @@ +github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= +github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= +github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1/go.mod h1:Az6Jt+M5idSED2YPGtwnfJV0kXohgdCBPmHGSYc1r04= +github.com/gdamore/tcell/v2 v2.5.2 h1:tKzG29kO9p2V++3oBY2W9zUjYu7IK1MENFeY/BzJSVY= +github.com/gdamore/tcell/v2 v2.5.2/go.mod h1:wSkrPaXoiIWZqW/g7Px4xc79di6FTcpB8tvaKJ6uGBo= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/rivo/tview v0.0.0-20220731115447-9d32d269593e h1:jWL4QylzqJQNk+iO3gkzbm433Akmn7nbRUEPrFU0SRA= +github.com/rivo/tview v0.0.0-20220731115447-9d32d269593e/go.mod h1:/Ve2+D+tGMTMNAlGXKCIX9ZeX2InzODYHotmtKZUUVk= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.3.1 h1:SDPP7SHNl1L7KrEFCSJslJ/DM9DT02Nq2C61XrfHMmk= +github.com/rivo/uniseg v0.3.1/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/tidwall/jsonc v0.3.2 h1:ZTKrmejRlAJYdn0kcaFqRAKlxxFIC21pYq8vLa4p2Wc= +github.com/tidwall/jsonc v0.3.2/go.mod h1:dw+3CIxqHi+t8eFSpzzMlcVYxKp08UP5CD8/uSFCyJE= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220318055525-2edf467146b5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY= +golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/lang/go/bindings/examples/sampling/run_sampling b/lang/go/bindings/examples/sampling/run_sampling new file mode 100755 index 00000000..2b7a761b --- /dev/null +++ b/lang/go/bindings/examples/sampling/run_sampling @@ -0,0 +1,25 @@ +#!/bin/bash + +cdir=$(pwd) +PROJECT_PATH="${cdir}/../../../../.." + +cmdstring="sudo -E LD_LIBRARY_PATH=$PROJECT_PATH/usr/local/lib/x86_64-linux-gnu ./sampling $*" +go env -w CGO_LDFLAGS_ALLOW='-Wl,--(?:no-)?whole-archive' + +go mod tidy +rc=$? +if [[ $rc -ne 0 ]]; then + echo "Go tidy failed" + exit $rc +fi + +go build +rc=$? +if [[ $rc -ne 0 ]]; then + echo "Go build failed" + exit $rc +fi + +$cmdstring + +stty sane diff --git a/lang/go/bindings/examples/sampling/sampling b/lang/go/bindings/examples/sampling/sampling new file mode 100755 index 00000000..0b85daf7 Binary files /dev/null and b/lang/go/bindings/examples/sampling/sampling differ diff --git a/lang/go/bindings/examples/sampling/sampling.go b/lang/go/bindings/examples/sampling/sampling.go new file mode 100644 index 00000000..827a6390 --- /dev/null +++ b/lang/go/bindings/examples/sampling/sampling.go @@ -0,0 +1,502 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright (c) 2017-2023 Intel Corporation. + */ + +package main + +import ( + "context" + "encoding/hex" + "flag" + "fmt" + "log" + "os" + "os/signal" + "syscall" + "time" + + "golang.org/x/text/language" + "golang.org/x/text/message" + + "github.com/CloudNativeDataPlane/cndp/lang/go/bindings/cne" + cz "github.com/CloudNativeDataPlane/cndp/lang/go/tools/pkgs/colorize" + "github.com/CloudNativeDataPlane/cndp/lang/go/tools/pkgs/etimers" + tlog "github.com/CloudNativeDataPlane/cndp/lang/go/tools/pkgs/ttylog" + tcell "github.com/gdamore/tcell/v2" + "github.com/rivo/tview" +) + +const ( + fwdLogID = "FwdLogID" + timerSteps = 2 +) + +type SamplingInfo struct { + handle *cne.System + ctx context.Context + stop context.CancelFunc + app *tview.Application + flex0 *tview.Flex + table *tview.Table + sigs chan os.Signal + timers *etimers.EventTimers + stats []*cne.LPortStats + redraw bool + samplingCtx map[uint32]int32 +} + +var ( + SamplingAction string = "FORWARD" + ConfigFlag string + TestFlag string + PttyFlag string + twirl int + twirlStr string = "|/-\\" +) + +func init() { + tlog.Register(fwdLogID, true) + + flag.StringVar(&ConfigFlag, "c", "", "path to configuration file") + flag.StringVar(&ConfigFlag, "config", "", "path to configuration file") + + flag.StringVar(&TestFlag, "t", "rx", "run tests - rx|tx|lb|chksum") + flag.StringVar(&TestFlag, "test", "rx", "run tests - rx|tx|lb|chksum") + + flag.StringVar(&PttyFlag, "ptty", "", "pseudo tty index value or path to /dev/pts/X") +} + +// collect the stats for each lport and store them in the SamplingInfo structure +func (f *SamplingInfo) collectStats() { + + for i, lport := range f.handle.LPortList() { + ps, err := lport.LPortStats() + if err != nil { + log.Fatalf("unable to fetch port %s stats\n", lport.Name()) + } + + f.stats[i] = ps + } +} + +// update sampling count +func (f *SamplingInfo) getSamplingAction(pkt *cne.Packet) string { + SamplingAction = "FORWARD" + + //(*C.pktmbuf_t)(unsafe.Pointer(pkt)) + hash := cne.GetHash(pkt) + if hash != 0 { + count,found := f.samplingCtx[hash] + if !found { + f.samplingCtx[hash] = 1 + } else if count > 15 { + SamplingAction = "DROP" + } else { + f.samplingCtx[hash] = (count+1) + } + } else { + SamplingAction = "FORWARD" + } + fmt.Println("Hash and Action", hash, SamplingAction) + return SamplingAction +} +// display the stats for all lports into a table +func (f *SamplingInfo) displayStats() { + + row, col := 0, 0 + f.table.SetCell(row, col, tview.NewTableCell(fmt.Sprintf("Ports %c", twirlStr[twirl&3])).SetTextColor(tcell.ColorCornsilk)) + twirl++ + f.table.SetCell(row, col+1, tview.NewTableCell(":").SetTextColor(tcell.ColorOrange)) + col += 2 + + for i, s := range f.handle.LPortList() { + f.table.SetCell(row, col+i, tview.NewTableCell(fmt.Sprintf("%14s", s.Name())).SetTextColor(tcell.ColorCornsilk)) + } + row++ + col = 0 + for _, t := range []string{"Rx Pkts/s", " TotalPkts", " MBytes", " Errors", " Missed", " Invalid"} { + f.table.SetCell(row, col, tview.NewTableCell(fmt.Sprintf("%-12s", t)).SetTextColor(tcell.ColorOrange)) + f.table.SetCell(row, col+1, tview.NewTableCell(":").SetTextColor(tcell.ColorOrange)) + row++ + } + row++ + for _, t := range []string{"Tx Pkts/s", " TotalPkts", " MBytes", " Errors", " Dropped", " Invalid"} { + f.table.SetCell(row, col, tview.NewTableCell(fmt.Sprintf("%-12s", t)).SetTextColor(tcell.ColorOrange)) + f.table.SetCell(row, col+1, tview.NewTableCell(":").SetTextColor(tcell.ColorOrange)) + row++ + } + + prt := message.NewPrinter(language.English) + for i, s := range f.stats { + row = 1 + col = 2 + i + + if s == nil { + continue + } + f.table.SetCell(row+0, col, tview.NewTableCell(prt.Sprintf("%14v", + s.InPacketRate)).SetTextColor(tcell.ColorLightCyan)) + f.table.SetCell(row+1, col, tview.NewTableCell(prt.Sprintf("%14v", + s.InPackets)).SetTextColor(tcell.ColorLightCyan)) + f.table.SetCell(row+2, col, tview.NewTableCell(prt.Sprintf("%14v", + s.InBytes/(1024*1024))).SetTextColor(tcell.ColorLightCyan)) + f.table.SetCell(row+3, col, tview.NewTableCell(prt.Sprintf("%14v", + s.InErrors)).SetTextColor(tcell.ColorLightCyan)) + f.table.SetCell(row+4, col, tview.NewTableCell(prt.Sprintf("%14v", + s.InMissed)).SetTextColor(tcell.ColorLightCyan)) + f.table.SetCell(row+5, col, tview.NewTableCell(prt.Sprintf("%14v", + s.RxInvalid)).SetTextColor(tcell.ColorLightCyan)) + + f.table.SetCell(row+7, col, tview.NewTableCell(prt.Sprintf("%14v", + s.OutPacketRate)).SetTextColor(tcell.ColorLightCyan)) + f.table.SetCell(row+8, col, tview.NewTableCell(prt.Sprintf("%14v", + s.OutPackets)).SetTextColor(tcell.ColorLightCyan)) + f.table.SetCell(row+9, col, tview.NewTableCell(prt.Sprintf("%14v", + s.OutBytes/(1024*1024))).SetTextColor(tcell.ColorLightCyan)) + f.table.SetCell(row+10, col, tview.NewTableCell(prt.Sprintf("%14v", + s.OutErrors)).SetTextColor(tcell.ColorLightCyan)) + f.table.SetCell(row+11, col, tview.NewTableCell(prt.Sprintf("%14v", + s.OutDropped)).SetTextColor(tcell.ColorLightCyan)) + f.table.SetCell(row+12, col, tview.NewTableCell(prt.Sprintf("%14v", + s.TxInvalid)).SetTextColor(tcell.ColorLightCyan)) + } +} + +// receive test routine and drop the packets on all lport attached to this thread +func (f *SamplingInfo) receivePackets(thdName string, lportNames []string) { + + lports := f.handle.LPortsByName(lportNames) + if len(lports) == 0 { + return + } + + err := f.handle.RegisterThread(thdName) + if err != nil { + return + } + defer f.handle.UnregisterThread(thdName) + + packets := make([]*cne.Packet, 256) + + var lportIds []int + for _, lport := range lports { + lportIds = append(lportIds, lport.LPortID()) + } + + for { + for _, pid := range lportIds { + select { + case <-f.ctx.Done(): + return + default: + size := cne.RxBurst(pid, packets) + if size > 0 { + cne.PktBufferFree(packets[:size]) + } + } + } + } +} + +// transmit a fixed packet buffer for all lports on this thread. +func (f *SamplingInfo) transmitPackets(thdName string, lportNames []string) { + + lports := f.handle.LPortsByName(lportNames) + if len(lports) == 0 { + return + } + + err := f.handle.RegisterThread(thdName) + if err != nil { + return + } + defer f.handle.UnregisterThread(thdName) + + txPackets := make([]*cne.Packet, 256) + + // IPv4/UDP 64 byte packet + // TTL/Port Src/Dest : 64/ 1234/ 5678 + // Pkt Type:VLAN ID : IPv4 / UDP:0001 + // IP Destination : 198.18.1.1 + // Source : 198.18.0.1/24 + // MAC Destination : 3c:fd:fe:e4:34:c0 + // Source : 3c:fd:fe:e4:38:44 + // Make sure the destination MAC address does not match + // the port the packet is being sent as the NIC will + // drop the packet. + // + // 0000 3cfd fee4 34c0 3cfd fee4 3844 0800 4500 + // 0010 002e 60ac 0000 4011 8cec c612 0001 c612 + // 0020 0101 04d2 162e 001a 93c6 6b6c 6d6e 6f70 + // 0030 7172 7374 7576 7778 797a 3031 + + data, err := hex.DecodeString( + "3cfdfee434c03cfdfee4384408004500" + + "002e60ac000040118cecc6120001c612" + + "010104d2162e001a93c66b6c6d6e6f70" + + "7172737475767778797a3031") + if err != nil { + return + } + + var lportIds []int + for _, lport := range lports { + lportIds = append(lportIds, lport.LPortID()) + } + + for { + for _, pid := range lportIds { + select { + case <-f.ctx.Done(): + return + default: + size := cne.PktBufferAlloc(pid, txPackets) + + if size != len(txPackets) { + tlog.DoPrintf("expected %d, got %d", len(txPackets), size) + } + if size > 0 { + pkts := txPackets[:size] + + if err := cne.WritePktDataList(pkts, 0, data); err != nil { + log.Fatalf("Error writing packet data list: %s\n", err.Error()) + } + nb := cne.TxBurst(pid, pkts, true) + if nb != len(pkts) { + tlog.DoPrintf("only sent %v packets out of %v\n", nb, len(pkts)) + } + } else { + tlog.DoPrintf("unable to allocate mbufs\n") + } + } + } + } +} + +// retransmit the received packet on the same lport after swapping the MAC addresses +func (f *SamplingInfo) reTransmitPackets(thdName string, lportNames []string) { + + lports := f.handle.LPortsByName(lportNames) + if len(lports) == 0 { + return + } + + err := f.handle.RegisterThread(thdName) + if err != nil { + return + } + defer f.handle.UnregisterThread(thdName) + + packets := make([]*cne.Packet, 256) + + var lportIds []int + for _, lport := range lports { + lportIds = append(lportIds, lport.LPortID()) + } + + for { + for _, pid := range lportIds { + select { + case <-f.ctx.Done(): + return + default: + size := cne.RxBurst(pid, packets) + if size > 0 { + fwdPackets := make([]*cne.Packet, 0) + pkts := packets[:size] + var i int + for ; i 0 { + cne.SwapMacAddrs(fwdPackets) + cne.TxBurst(pid, fwdPackets, true) + } + } + } + } + } +} + +// verify IPv4 checksum for each packet for each lport then free the packet. +func (f *SamplingInfo) verifyIPv4ChecksumPackets(thdName string, lportNames []string) { + + lports := f.handle.LPortsByName(lportNames) + if len(lports) == 0 { + return + } + + err := f.handle.RegisterThread(thdName) + if err != nil { + return + } + defer f.handle.UnregisterThread(thdName) + + packets := make([]*cne.Packet, 256) + + var lportIds []int + for _, lport := range lports { + lportIds = append(lportIds, lport.LPortID()) + } + + for { + for _, pid := range lportIds { + select { + case <-f.ctx.Done(): + return + default: + size := cne.RxBurst(pid, packets) + if size > 0 { + for j := 0; j < size; j++ { + ethHdr := cne.GetEtherHdr(packets[j]) + if ethHdr.EtherType != cne.SwapUint16(cne.EtherTypeIPV4) && + cne.IPv4Checksum(cne.GetIPv4(packets[j])) != 0 { + log.Println("packet ipv4Hdr checksum validation failed") + } + } + } + cne.PktBufferFree(packets[:size]) + } + } + } +} + +// setup the system signals to trap and handle shutdown +func (f *SamplingInfo) setupSignals(signals ...os.Signal) { + + sigs := make(chan os.Signal, 1) + + f.sigs = sigs + + signal.Notify(sigs, signals...) + go func() { + sig := <-sigs + + fmt.Printf("Signal: %v\n", sig) + f.stop() + + time.Sleep(time.Second) + + f.app.Stop() + os.Exit(1) + }() +} + +// setup the application SamplingInfo structure and allocate the table, etimers, ... +func samplingSetup() *SamplingInfo { + + f := &SamplingInfo{} + + handle, err := cne.OpenWithFile(ConfigFlag) + if err != nil { + log.Fatalf("error in initialization %s\n", err.Error()) + } + f.handle = handle + + f.app = tview.NewApplication() + + f.flex0 = tview.NewFlex().SetDirection(tview.FlexRow) + + f.table = tview.NewTable().SetBorders(false).SetFixed(1, 1) + f.table.SetTitleAlign(tview.AlignLeft) + f.table.SetBorder(true). + SetTitle(fmt.Sprintf(" %s TestMode: %s ", cz.Cyan("Press Esc or Q/q or Ctrl-C to quit"), + cz.Orange(TestFlag))) + + f.flex0.AddItem(f.table, 16, 1, true) + + // Shortcuts to stop application + f.app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { + switch { + case event.Rune() == 'q' || event.Rune() == 'Q' || event.Key() == tcell.KeyEscape: + f.stop() + f.app.Stop() + default: + } + return event + }) + + f.timers = etimers.New(time.Second/timerSteps, timerSteps) + + f.timers.Add("Sampling stats", func(step int, ticks uint64) { + switch step { + case 0: + f.collectStats() + + case 1: + f.app.QueueUpdateDraw(func() { + f.displayStats() + }) + } + }) + f.timers.Start() + + f.ctx, f.stop = signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) + + f.setupSignals(syscall.SIGINT, syscall.SIGTERM, syscall.SIGSEGV) + + f.stats = make([]*cne.LPortStats, len(f.handle.LPortList())) + + f.samplingCtx = make(map[uint32]int32) + + return f +} + +func main() { + flag.Parse() + + if len(ConfigFlag) == 0 { + flag.PrintDefaults() + log.Fatalf("-c option must be present\n") + } + + if len(TestFlag) == 0 { + flag.PrintDefaults() + log.Fatalf("-t option must be present\n") + } + + if len(PttyFlag) > 0 { + err := tlog.Open(PttyFlag) + if err != nil { + fmt.Printf("ttylog open failed: %s\n", err) + os.Exit(1) + } + } + + f := samplingSetup() + + defer f.handle.Close() + defer f.app.Stop() + defer f.stop() + + // For each JSON configuration thread create a Go thread and pass + // the list of LPorts attached to the thread to the test function. + for thdName, thd := range f.handle.JsonCfg().ThreadInfoMap { + if len(thd.LPorts) == 0 { + continue + } + + switch TestFlag { + case "rx": + go f.receivePackets(thdName, thd.LPorts) + case "tx": + go f.transmitPackets(thdName, thd.LPorts) + case "lb": + go f.reTransmitPackets(thdName, thd.LPorts) + case "chksum": + go f.verifyIPv4ChecksumPackets(thdName, thd.LPorts) + default: + log.Fatalf("*** invalid test option") + os.Exit(1) + } + } + + // Wait for the user to stop the process + if err := f.app.SetRoot(f.flex0, true).EnableMouse(true).Run(); err != nil { + panic(err) + } +} diff --git a/lang/go/bindings/examples/sampling/sampling.jsnoc b/lang/go/bindings/examples/sampling/sampling.jsnoc new file mode 100644 index 00000000..69346984 --- /dev/null +++ b/lang/go/bindings/examples/sampling/sampling.jsnoc @@ -0,0 +1,159 @@ +{ + // (R) - Required entry + // (O) - Optional entry + // All descriptions are optional and short form is 'desc' + // The order of the entries in this file are handled when it is parsed and the + // entries can be in any order. + + // (R) Application information + // name - (O) the name of the application + // description - (O) the description of the application + "application": { + "name": "cndpsampling-go", + "description": "A sampling application" + }, + + // (O) Default values + // bufcnt - (O) UMEM default buffer count in 1K increments + // bufsz - (O) UMEM buffer size in 1K increments + // rxdesc - (O) Number of RX ring descriptors in 1K increments + // txdesc - (O) Number of TX ring descriptors in 1K increments + // cache - (O) MBUF Pool cache size in number of entries + // mtype - (O) Memory type for mmap allocations + "defaults": { + "bufcnt": 32, + "bufsz": 2, + "rxdesc": 2, + "txdesc": 2, + "cache": 256, + "mtype": "2MB" + }, + + // List of all UMEM's to be created + // key/val - (R) The 'key' is the name of the umem for later reference. + // The 'val' is the object describing the UMEM buffer. + // Multiple umem regions can be defined. + // A UMEM can support multiple lports using the regions array. Each lports can use + // one of the regions. + // bufcnt - (R) The number of buffers in 1K increments in the UMEM space. + // bufsz - (R) The size in 1K increments of each buffer in the UMEM space. + // mtype - (O) If missing or empty string or missing means use 4KB or default system pages. + // regions - (O) Array of sizes one per region in 1K increments, total must be <= bufcnt + // rxdesc - (O) Number of RX descriptors to be allocated in 1K increments, + // if not present or zero use defaults.rxdesc, normally zero. + // txdesc - (O) Number of TX descriptors to be allocated in 1K increments, + // if not present or zero use defaults.txdesc, normally zero. + // shared_umem - (O) Set to true to use xsk_socket__create_shared() API, default false + // description | desc - (O) Description of the umem space. + "umems": { + "umem0": { + "bufcnt":64, + "bufsz": 2, + "mtype": "2MB", + "regions": [ + 32, + 32 + ], + "rxdesc": 0, + "txdesc": 0, + "description": "UMEM Description 0" + } + }, + + // List of all lports to be used in the application + // An lport is defined by a netdev/queue ID pair, which is a socket containing a Rx/Tx ring pair. + // Each queue ID is assigned to a single socket or a socket is the lport defined by netdev/qid. + // Note: A netdev can be shared between lports as the qid is unique per lport + // If netdev is not defined or empty then it must be a virtual interface and not + // associated with a netdev/queue ID. + // key/val - (R) The 'key' is the logical name e.g. 'eth0:0', 'eth1:0', ... to be used by the + // application to reference an lport. The 'val' object contains information about + // each lport. + // netdev - (R) The netdev device to be used, the part before the colon + // must reflect the netdev name + // pmd - (R) All PMDs have a name i.e. 'net_af_xdp', 'ring', ... + // qid - (R) Is the queue id to use for this lport, defined by ethtool command line + // umem - (R) The UMEM assigned to this lport + // region - (O) UMEM region index value, default region 0 + // busy_poll - (O) Enable busy polling support, true or false, default false + // busy_polling - Same as above + // busy_timeout - (O) 1-65535 or 0 - use default value, values in milliseconds + // busy_budget - (O) 0xFFFF disabled, 0 use default, >0 budget value + // inhibit_prog_load - (O) inhibit loading the BPF program if true, default false + // force_wakeup - (O) force TX wakeup calls for CVL NIC, default false + // skb_mode - (O) Enable XDP_FLAGS_SKB_MODE when creating af_xdp socket, forces copy mode, default false + // description - (O) the description, 'desc' can be used as well + "lports": { + "eno12399:0": { + "pmd": "net_af_xdp", + "qid": 0, + "umem": "umem0", + "region": 0, + "description": "LAN 0 port" + } + }, + + // (O) Define the lcore groups for each thread to run + // Can be integers or a string for a range of lcores + // e.g. [10], [10-14,16], [10-12, 14-15, 17-18, 20] + // Names of a lcore group and its lcores assigned to the group. + // The initial group is for the main thread of the application. + // The default group is special and is used if a thread if not assigned to a group. + "lcore-groups": { + "initial": [0], + "group0": [1], + "group1": [2], + "default": ["0"] + }, + + // (O) Set of common options application defined. + // The Key can be any string and value can be boolean, string, array or integer + // An array must contain only a single value type, boolean, integer, string and + // can't be a nested array. + // pkt_api - (O) Set the type of packet API xskdev, xdpdev or pktdev + // no-metrics - (O) Disable metrics gathering and thread + // no-restapi - (O) Disable RestAPI support + // cli - (O) Enable/Disable CLI supported + // mode - (O) Mode type [drop | rx-only], tx-only, [lb | loopback], fwd, acl-strict, acl-permissive + // no-color - (O) Enable/Disable vt100 colors + // burst-size - (O) Burst size for application, if zero or not defined use default 128, max 256. + // uds_path - (O) Path to unix domain socket to get xsk map fd + "options": { + "no-metrics": false, + "no-restapi": false, + "cli": true, + "mode": "l3fwd", + "l3fwd-fib-rules": [ + "198.18.0.0/24,70:b5:e8:f0:31:30,1", + "198.18.1.0/24,b4:96:91:ba:0a:de,0" + ], + "no-color": false, + "burst-size": 64 + }, + + // List of threads to start and information for that thread. Application can start + // it's own threads for any reason and are not required to be configured by this file. + // + // Key/Val - (R) A unique thread name. + // The format is [:] the ':' and identifier + // are optional if all thread names are unique + // group - (O) The lcore-group this thread belongs to. The + // lports - (O) The list of lports assigned to this thread and can not shared lports. + // description | desc - (O) The description + "threads": { + "main": { + "group": "initial", + "description": "Main Thread" + }, + "fwd:0": { + "group": "group0", + "lports": ["eno12399:0"], + "description": "Forward thread 0" + }, + "fwd:1": { + "group": "group1", + "lports": ["eno12399:0"], + "description": "Forward thread 1" + } + } +}