aboutsummaryrefslogtreecommitdiffstats
path: root/extras/libmemif/examples/raw-data/raw-data.go
diff options
context:
space:
mode:
Diffstat (limited to 'extras/libmemif/examples/raw-data/raw-data.go')
-rw-r--r--extras/libmemif/examples/raw-data/raw-data.go240
1 files changed, 240 insertions, 0 deletions
diff --git a/extras/libmemif/examples/raw-data/raw-data.go b/extras/libmemif/examples/raw-data/raw-data.go
new file mode 100644
index 0000000..f8a6aad
--- /dev/null
+++ b/extras/libmemif/examples/raw-data/raw-data.go
@@ -0,0 +1,240 @@
+// raw-data is a basic example showing how to create a memif interface, handle
+// events through callbacks and perform Rx/Tx of raw data. Before handling
+// an actual packets it is important to understand the skeleton of libmemif-based
+// applications.
+//
+// Since VPP expects proper packet data, it is not very useful to connect
+// raw-data example with VPP, even though it will work, since all the received
+// data will get dropped on the VPP side.
+//
+// To create a connection of two raw-data instances, run two processes
+// concurrently:
+// - master memif:
+// $ ./raw-data
+// - slave memif:
+// $ ./raw-data --slave
+//
+// Every 3 seconds both sides send 3 raw-data packets to the opposite end through
+// each queue. The received packets are printed to stdout.
+//
+// Stop an instance of raw-data with an interrupt signal.
+package main
+
+import (
+ "fmt"
+ "os"
+ "os/signal"
+ "strconv"
+ "sync"
+ "time"
+
+ "git.fd.io/govpp.git/extras/libmemif"
+)
+
+const (
+ // Socket through which the opposite memifs will establish the connection.
+ Socket = "/tmp/raw-data-example"
+
+ // Secret used to authenticate the memif connection.
+ Secret = "secret"
+
+ // ConnectionID is an identifier used to match opposite memifs.
+ ConnectionID = 1
+
+ // NumQueues is the (configured!) number of queues for both Rx & Tx.
+ // The actual number agreed during connection establishment may be smaller!
+ NumQueues uint8 = 3
+)
+
+// For management of go routines.
+var wg sync.WaitGroup
+var stopCh chan struct{}
+
+// OnConnect is called when a memif connection gets established.
+func OnConnect(memif *libmemif.Memif) (err error) {
+ details, err := memif.GetDetails()
+ if err != nil {
+ fmt.Printf("libmemif.GetDetails() error: %v\n", err)
+ }
+ fmt.Printf("memif %s has been connected: %+v\n", memif.IfName, details)
+
+ stopCh = make(chan struct{})
+ // Start a separate go routine for each queue.
+ // (memif queue is a unit of parallelism for Rx/Tx)
+ // Beware: the number of queues created may be lower than what was requested
+ // in MemifConfiguration (the master makes the final decision).
+ // Use Memif.GetDetails to get the number of queues.
+ var i uint8
+ for i = 0; i < uint8(len(details.RxQueues)); i++ {
+ wg.Add(1)
+ go ReadAndPrintPackets(memif, i)
+ }
+ for i = 0; i < uint8(len(details.TxQueues)); i++ {
+ wg.Add(1)
+ go SendPackets(memif, i)
+ }
+ return nil
+}
+
+// OnDisconnect is called when a memif connection is lost.
+func OnDisconnect(memif *libmemif.Memif) (err error) {
+ fmt.Printf("memif %s has been disconnected\n", memif.IfName)
+ // Stop all packet producers and consumers.
+ close(stopCh)
+ wg.Wait()
+ return nil
+}
+
+// ReadAndPrintPackets keeps receiving raw packet data from a selected queue
+// and prints them to stdout.
+func ReadAndPrintPackets(memif *libmemif.Memif, queueID uint8) {
+ defer wg.Done()
+
+ // Get channel which fires every time there are packets to read on the queue.
+ interruptCh, err := memif.GetQueueInterruptChan(queueID)
+ if err != nil {
+ // Example of libmemif error handling code:
+ switch err {
+ case libmemif.ErrQueueID:
+ fmt.Printf("libmemif.Memif.GetQueueInterruptChan() complains about invalid queue id!?")
+ // Here you would put all the errors that need to be handled individually...
+ default:
+ fmt.Printf("libmemif.Memif.GetQueueInterruptChan() error: %v\n", err)
+ }
+ return
+ }
+
+ for {
+ select {
+ case <-interruptCh:
+ // Read all packets from the queue but at most 10 at once.
+ // Since there is only one interrupt signal sent for an entire burst
+ // of packets, an interrupt handling routine should repeatedly call
+ // RxBurst() until the function returns an empty slice of packets.
+ // This way it is ensured that there are no packets left
+ // on the queue unread when the interrupt signal is cleared.
+ for {
+ packets, err := memif.RxBurst(queueID, 10)
+ if err != nil {
+ fmt.Printf("libmemif.Memif.RxBurst() error: %v\n", err)
+ // Skip this burst, continue with the next one 3secs later...
+ } else {
+ if len(packets) == 0 {
+ // No more packets to read until the next interrupt.
+ break
+ }
+ for _, packet := range packets {
+ fmt.Printf("Received packet queue=%d: %v\n", queueID, string(packet[:]))
+ }
+ }
+ }
+ case <-stopCh:
+ return
+ }
+ }
+}
+
+// SendPackets keeps sending bursts of 3 raw-data packets every 3 seconds into
+// the selected queue.
+func SendPackets(memif *libmemif.Memif, queueID uint8) {
+ defer wg.Done()
+
+ counter := 0
+ for {
+ select {
+ case <-time.After(3 * time.Second):
+ counter++
+ // Prepare fake packets.
+ packets := []libmemif.RawPacketData{
+ libmemif.RawPacketData("Packet #1 in burst number " + strconv.Itoa(counter)),
+ libmemif.RawPacketData("Packet #2 in burst number " + strconv.Itoa(counter)),
+ libmemif.RawPacketData("Packet #3 in burst number " + strconv.Itoa(counter)),
+ }
+ // Send the packets. We may not be able to do it in one burst if the ring
+ // is (almost) full or the internal buffer cannot contain it.
+ sent := 0
+ for {
+ count, err := memif.TxBurst(queueID, packets[sent:])
+ if err != nil {
+ fmt.Printf("libmemif.Memif.TxBurst() error: %v\n", err)
+ break
+ } else {
+ fmt.Printf("libmemif.Memif.TxBurst() has sent %d packets.\n", count)
+ sent += int(count)
+ if sent == len(packets) {
+ break
+ }
+ }
+ }
+ case <-stopCh:
+ return
+ }
+ }
+}
+
+func main() {
+ fmt.Println("Starting 'raw-data' example...")
+
+ // If run with the "--slave" option, create memif in the slave mode.
+ var isMaster = true
+ var appSuffix string
+ if len(os.Args) > 1 && (os.Args[1] == "--slave" || os.Args[1] == "-slave") {
+ isMaster = false
+ appSuffix = "-slave"
+ }
+
+ // Initialize libmemif first.
+ appName := "Raw-Data" + appSuffix
+ fmt.Println("Initializing libmemif as ", appName)
+ err := libmemif.Init(appName)
+ if err != nil {
+ fmt.Printf("libmemif.Init() error: %v\n", err)
+ return
+ }
+ // Schedule automatic cleanup.
+ defer libmemif.Cleanup()
+
+ // Prepare callbacks to use with the memif.
+ // The same callbacks could be used with multiple memifs.
+ // The first input argument (*libmemif.Memif) can be used to tell which
+ // memif the callback was triggered for.
+ memifCallbacks := &libmemif.MemifCallbacks{
+ OnConnect: OnConnect,
+ OnDisconnect: OnDisconnect,
+ }
+
+ // Prepare memif1 configuration.
+ memifConfig := &libmemif.MemifConfig{
+ MemifMeta: libmemif.MemifMeta{
+ IfName: "memif1",
+ ConnID: ConnectionID,
+ SocketFilename: Socket,
+ Secret: Secret,
+ IsMaster: isMaster,
+ Mode: libmemif.IfModeEthernet,
+ },
+ MemifShmSpecs: libmemif.MemifShmSpecs{
+ NumRxQueues: NumQueues,
+ NumTxQueues: NumQueues,
+ BufferSize: 2048,
+ Log2RingSize: 10,
+ },
+ }
+
+ fmt.Printf("Callbacks: %+v\n", memifCallbacks)
+ fmt.Printf("Config: %+v\n", memifConfig)
+
+ // Create memif1 interface.
+ memif, err := libmemif.CreateInterface(memifConfig, memifCallbacks)
+ if err != nil {
+ fmt.Printf("libmemif.CreateInterface() error: %v\n", err)
+ return
+ }
+ // Schedule automatic cleanup of the interface.
+ defer memif.Close()
+
+ // Wait until an interrupt signal is received.
+ sigChan := make(chan os.Signal, 1)
+ signal.Notify(sigChan, os.Interrupt)
+ <-sigChan
+}