Change module name to go.fd.io/govpp
[govpp.git] / extras / libmemif / examples / raw-data / raw-data.go
1 // raw-data is a basic example showing how to create a memif interface, handle
2 // events through callbacks and perform Rx/Tx of raw data. Before handling
3 // an actual packets it is important to understand the skeleton of libmemif-based
4 // applications.
5 //
6 // Since VPP expects proper packet data, it is not very useful to connect
7 // raw-data example with VPP, even though it will work, since all the received
8 // data will get dropped on the VPP side.
9 //
10 // To create a connection of two raw-data instances, run two processes
11 // concurrently:
12 //  - master memif:
13 //     $ ./raw-data
14 //  - slave memif:
15 //     $ ./raw-data --slave
16 //
17 // Every 3 seconds both sides send 3 raw-data packets to the opposite end through
18 // each queue. The received packets are printed to stdout.
19 //
20 // Stop an instance of raw-data with an interrupt signal.
21 package main
22
23 import (
24         "fmt"
25         "os"
26         "os/signal"
27         "strconv"
28         "sync"
29         "time"
30
31         "go.fd.io/govpp/extras/libmemif"
32 )
33
34 const (
35         // Socket through which the opposite memifs will establish the connection.
36         Socket = "/tmp/raw-data-example"
37
38         // Secret used to authenticate the memif connection.
39         Secret = "secret"
40
41         // ConnectionID is an identifier used to match opposite memifs.
42         ConnectionID = 1
43
44         // NumQueues is the (configured!) number of queues for both Rx & Tx.
45         // The actual number agreed during connection establishment may be smaller!
46         NumQueues uint8 = 3
47 )
48
49 // For management of go routines.
50 var wg sync.WaitGroup
51 var stopCh chan struct{}
52
53 // OnConnect is called when a memif connection gets established.
54 func OnConnect(memif *libmemif.Memif) (err error) {
55         details, err := memif.GetDetails()
56         if err != nil {
57                 fmt.Printf("libmemif.GetDetails() error: %v\n", err)
58         }
59         fmt.Printf("memif %s has been connected: %+v\n", memif.IfName, details)
60
61         stopCh = make(chan struct{})
62         // Start a separate go routine for each queue.
63         // (memif queue is a unit of parallelism for Rx/Tx)
64         // Beware: the number of queues created may be lower than what was requested
65         // in MemifConfiguration (the master makes the final decision).
66         // Use Memif.GetDetails to get the number of queues.
67         var i uint8
68         for i = 0; i < uint8(len(details.RxQueues)); i++ {
69                 wg.Add(1)
70                 go ReadAndPrintPackets(memif, i)
71         }
72         for i = 0; i < uint8(len(details.TxQueues)); i++ {
73                 wg.Add(1)
74                 go SendPackets(memif, i)
75         }
76         return nil
77 }
78
79 // OnDisconnect is called when a memif connection is lost.
80 func OnDisconnect(memif *libmemif.Memif) (err error) {
81         fmt.Printf("memif %s has been disconnected\n", memif.IfName)
82         // Stop all packet producers and consumers.
83         close(stopCh)
84         wg.Wait()
85         return nil
86 }
87
88 // ReadAndPrintPackets keeps receiving raw packet data from a selected queue
89 // and prints them to stdout.
90 func ReadAndPrintPackets(memif *libmemif.Memif, queueID uint8) {
91         defer wg.Done()
92
93         // Get channel which fires every time there are packets to read on the queue.
94         interruptCh, err := memif.GetQueueInterruptChan(queueID)
95         if err != nil {
96                 // Example of libmemif error handling code:
97                 switch err {
98                 case libmemif.ErrQueueID:
99                         fmt.Printf("libmemif.Memif.GetQueueInterruptChan() complains about invalid queue id!?")
100                 // Here you would put all the errors that need to be handled individually...
101                 default:
102                         fmt.Printf("libmemif.Memif.GetQueueInterruptChan() error: %v\n", err)
103                 }
104                 return
105         }
106
107         for {
108                 select {
109                 case <-interruptCh:
110                         // Read all packets from the queue but at most 10 at once.
111                         // Since there is only one interrupt signal sent for an entire burst
112                         // of packets, an interrupt handling routine should repeatedly call
113                         // RxBurst() until the function returns an empty slice of packets.
114                         // This way it is ensured that there are no packets left
115                         // on the queue unread when the interrupt signal is cleared.
116                         for {
117                                 packets, err := memif.RxBurst(queueID, 10)
118                                 if err != nil {
119                                         fmt.Printf("libmemif.Memif.RxBurst() error: %v\n", err)
120                                         // Skip this burst, continue with the next one 3secs later...
121                                 } else {
122                                         if len(packets) == 0 {
123                                                 // No more packets to read until the next interrupt.
124                                                 break
125                                         }
126                                         for _, packet := range packets {
127                                                 fmt.Printf("Received packet queue=%d: %v\n", queueID, string(packet[:]))
128                                         }
129                                 }
130                         }
131                 case <-stopCh:
132                         return
133                 }
134         }
135 }
136
137 // SendPackets keeps sending bursts of 3 raw-data packets every 3 seconds into
138 // the selected queue.
139 func SendPackets(memif *libmemif.Memif, queueID uint8) {
140         defer wg.Done()
141
142         counter := 0
143         for {
144                 select {
145                 case <-time.After(3 * time.Second):
146                         counter++
147                         // Prepare fake packets.
148                         packets := []libmemif.RawPacketData{
149                                 libmemif.RawPacketData("Packet #1 in burst number " + strconv.Itoa(counter)),
150                                 libmemif.RawPacketData("Packet #2 in burst number " + strconv.Itoa(counter)),
151                                 libmemif.RawPacketData("Packet #3 in burst number " + strconv.Itoa(counter)),
152                         }
153                         // Send the packets. We may not be able to do it in one burst if the ring
154                         // is (almost) full or the internal buffer cannot contain it.
155                         sent := 0
156                         for {
157                                 count, err := memif.TxBurst(queueID, packets[sent:])
158                                 if err != nil {
159                                         fmt.Printf("libmemif.Memif.TxBurst() error: %v\n", err)
160                                         break
161                                 } else {
162                                         fmt.Printf("libmemif.Memif.TxBurst() has sent %d packets.\n", count)
163                                         sent += int(count)
164                                         if sent == len(packets) {
165                                                 break
166                                         }
167                                 }
168                         }
169                 case <-stopCh:
170                         return
171                 }
172         }
173 }
174
175 func main() {
176         fmt.Println("Starting 'raw-data' example...")
177
178         // If run with the "--slave" option, create memif in the slave mode.
179         var isMaster = true
180         var appSuffix string
181         if len(os.Args) > 1 && (os.Args[1] == "--slave" || os.Args[1] == "-slave") {
182                 isMaster = false
183                 appSuffix = "-slave"
184         }
185
186         // Initialize libmemif first.
187         appName := "Raw-Data" + appSuffix
188         fmt.Println("Initializing libmemif as ", appName)
189         err := libmemif.Init(appName)
190         if err != nil {
191                 fmt.Printf("libmemif.Init() error: %v\n", err)
192                 return
193         }
194         // Schedule automatic cleanup.
195         defer libmemif.Cleanup()
196
197         // Prepare callbacks to use with the memif.
198         // The same callbacks could be used with multiple memifs.
199         // The first input argument (*libmemif.Memif) can be used to tell which
200         // memif the callback was triggered for.
201         memifCallbacks := &libmemif.MemifCallbacks{
202                 OnConnect:    OnConnect,
203                 OnDisconnect: OnDisconnect,
204         }
205
206         // Prepare memif1 configuration.
207         memifConfig := &libmemif.MemifConfig{
208                 MemifMeta: libmemif.MemifMeta{
209                         IfName:         "memif1",
210                         ConnID:         ConnectionID,
211                         SocketFilename: Socket,
212                         Secret:         Secret,
213                         IsMaster:       isMaster,
214                         Mode:           libmemif.IfModeEthernet,
215                 },
216                 MemifShmSpecs: libmemif.MemifShmSpecs{
217                         NumRxQueues:  NumQueues,
218                         NumTxQueues:  NumQueues,
219                         BufferSize:   2048,
220                         Log2RingSize: 10,
221                 },
222         }
223
224         fmt.Printf("Callbacks: %+v\n", memifCallbacks)
225         fmt.Printf("Config: %+v\n", memifConfig)
226
227         // Create memif1 interface.
228         memif, err := libmemif.CreateInterface(memifConfig, memifCallbacks)
229         if err != nil {
230                 fmt.Printf("libmemif.CreateInterface() error: %v\n", err)
231                 return
232         }
233         // Schedule automatic cleanup of the interface.
234         defer memif.Close()
235
236         // Wait until an interrupt signal is received.
237         sigChan := make(chan os.Signal, 1)
238         signal.Notify(sigChan, os.Interrupt)
239         <-sigChan
240 }