ODPM 266: Go-libmemif + 2 examples.
[govpp.git] / extras / libmemif / adapter.go
1 // Copyright (c) 2017 Cisco and/or its affiliates.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at:
6 //
7 //       http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 // +build !windows,!darwin
16
17 package libmemif
18
19 import (
20         "encoding/binary"
21         "os"
22         "sync"
23         "syscall"
24         "unsafe"
25
26         logger "github.com/Sirupsen/logrus"
27 )
28
29 /*
30 #cgo LDFLAGS: -lmemif
31
32 #include <unistd.h>
33 #include <stdlib.h>
34 #include <stdint.h>
35 #include <string.h>
36 #include <sys/eventfd.h>
37 #include <libmemif.h>
38
39 // Feature tests.
40 #ifndef MEMIF_HAVE_CANCEL_POLL_EVENT
41 // memif_cancel_poll_event that simply returns ErrUnsupported.
42 static int
43 memif_cancel_poll_event ()
44 {
45         return 102; // ErrUnsupported
46 }
47 #endif
48
49 // govpp_memif_conn_args_t replaces fixed sized arrays with C-strings which
50 // are much easier to work with in cgo.
51 typedef struct
52 {
53         char *socket_filename;
54         char *secret;
55         uint8_t num_s2m_rings;
56         uint8_t num_m2s_rings;
57         uint16_t buffer_size;
58         memif_log2_ring_size_t log2_ring_size;
59         uint8_t is_master;
60         memif_interface_id_t interface_id;
61         char *interface_name;
62         char *instance_name;
63         memif_interface_mode_t mode;
64 } govpp_memif_conn_args_t;
65
66 // govpp_memif_details_t replaces strings represented with (uint8_t *)
67 // to the standard and easy to work with in cgo: (char *)
68 typedef struct
69 {
70         char *if_name;
71         char *inst_name;
72         char *remote_if_name;
73         char *remote_inst_name;
74         uint32_t id;
75         char *secret;
76         uint8_t role;
77         uint8_t mode;
78         char *socket_filename;
79         uint8_t rx_queues_num;
80         uint8_t tx_queues_num;
81         memif_queue_details_t *rx_queues;
82         memif_queue_details_t *tx_queues;
83         uint8_t link_up_down;
84 } govpp_memif_details_t;
85
86 extern int go_on_connect_callback(void *privateCtx);
87 extern int go_on_disconnect_callback(void *privateCtx);
88
89 // Callbacks strip the connection handle away.
90
91 static int
92 govpp_on_connect_callback(memif_conn_handle_t conn, void *private_ctx)
93 {
94         return go_on_connect_callback(private_ctx);
95 }
96
97 static int
98 govpp_on_disconnect_callback(memif_conn_handle_t conn, void *private_ctx)
99 {
100         return go_on_disconnect_callback(private_ctx);
101 }
102
103 // govpp_memif_create uses govpp_memif_conn_args_t.
104 static int
105 govpp_memif_create (memif_conn_handle_t *conn, govpp_memif_conn_args_t *go_args,
106                     void *private_ctx)
107 {
108         memif_conn_args_t args;
109         memset (&args, 0, sizeof (args));
110         args.socket_filename = (char *)go_args->socket_filename;
111         if (go_args->secret != NULL)
112         {
113                 strncpy ((char *)args.secret, go_args->secret,
114                                  sizeof (args.secret) - 1);
115         }
116         args.num_s2m_rings = go_args->num_s2m_rings;
117         args.num_m2s_rings = go_args->num_m2s_rings;
118         args.buffer_size = go_args->buffer_size;
119         args.log2_ring_size = go_args->log2_ring_size;
120         args.is_master = go_args->is_master;
121         args.interface_id = go_args->interface_id;
122         if (go_args->interface_name != NULL)
123         {
124                 strncpy ((char *)args.interface_name, go_args->interface_name,
125                                  sizeof(args.interface_name) - 1);
126         }
127         if (go_args->instance_name != NULL)
128         {
129                 strncpy ((char *)args.instance_name, go_args->instance_name,
130                                  sizeof (args.instance_name) - 1);
131         }
132         args.mode = go_args->mode;
133
134         return memif_create(conn, &args, govpp_on_connect_callback,
135                                                 govpp_on_disconnect_callback, NULL,
136                                                 private_ctx);
137 }
138
139 // govpp_memif_get_details keeps reallocating buffer until it is large enough.
140 // The buffer is returned to be deallocated when it is no longer needed.
141 static int
142 govpp_memif_get_details (memif_conn_handle_t conn, govpp_memif_details_t *govpp_md,
143                          char **buf)
144 {
145         int rv = 0;
146         size_t buflen = 1 << 7;
147         char *buffer = NULL, *new_buffer = NULL;
148         memif_details_t md = {0};
149
150         do {
151                 // initial malloc (256 bytes) or realloc
152                 buflen <<= 1;
153                 new_buffer = realloc(buffer, buflen);
154                 if (new_buffer == NULL)
155                 {
156                         free(buffer);
157                         return MEMIF_ERR_NOMEM;
158                 }
159                 buffer = new_buffer;
160                 // try to get details
161                 rv = memif_get_details(conn, &md, buffer, buflen);
162         } while (rv == MEMIF_ERR_NOBUF_DET);
163
164         if (rv == 0)
165         {
166                 *buf = buffer;
167                 govpp_md->if_name = (char *)md.if_name;
168                 govpp_md->inst_name = (char *)md.inst_name;
169                 govpp_md->remote_if_name = (char *)md.remote_if_name;
170                 govpp_md->remote_inst_name = (char *)md.remote_inst_name;
171                 govpp_md->id = md.id;
172                 govpp_md->secret = (char *)md.secret;
173                 govpp_md->role = md.role;
174                 govpp_md->mode = md.mode;
175                 govpp_md->socket_filename = (char *)md.socket_filename;
176                 govpp_md->rx_queues_num = md.rx_queues_num;
177                 govpp_md->tx_queues_num = md.tx_queues_num;
178                 govpp_md->rx_queues = md.rx_queues;
179                 govpp_md->tx_queues = md.tx_queues;
180                 govpp_md->link_up_down = md.link_up_down;
181         }
182         else
183                 free(buffer);
184         return rv;
185 }
186
187 // Used to avoid cumbersome tricks that use unsafe.Pointer() + unsafe.Sizeof()
188 // or even cast C-array directly into Go-slice.
189 static memif_queue_details_t
190 govpp_get_rx_queue_details (govpp_memif_details_t *md, int index)
191 {
192         return md->rx_queues[index];
193 }
194
195 // Used to avoid cumbersome tricks that use unsafe.Pointer() + unsafe.Sizeof()
196 // or even cast C-array directly into Go-slice.
197 static memif_queue_details_t
198 govpp_get_tx_queue_details (govpp_memif_details_t *md, int index)
199 {
200         return md->tx_queues[index];
201 }
202
203 // Copy packet data into the selected buffer.
204 static void
205 govpp_copy_packet_data(memif_buffer_t *buffers, int index, void *data, uint32_t size)
206 {
207         buffers[index].data_len = (size > buffers[index].buffer_len ? buffers[index].buffer_len : size);
208         memcpy(buffers[index].data, data, (size_t)buffers[index].data_len);
209 }
210
211 // Get packet data from the selected buffer.
212 // Used to avoid an ugly unsafe.Pointer() + unsafe.Sizeof().
213 static void *
214 govpp_get_packet_data(memif_buffer_t *buffers, int index, int *size)
215 {
216         *size = (int)buffers[index].data_len;
217         return buffers[index].data;
218 }
219
220 */
221 import "C"
222
223 // IfMode represents the mode (layer/behaviour) in which the interface operates.
224 type IfMode int
225
226 const (
227         // IfModeEthernet tells memif to operate on the L2 layer.
228         IfModeEthernet IfMode = iota
229
230         // IfModeIP tells memif to operate on the L3 layer.
231         IfModeIP
232
233         // IfModePuntInject tells memif to behave as Inject/Punt interface.
234         IfModePuntInject
235 )
236
237 // RxMode is used to switch between polling and interrupt for RX.
238 type RxMode int
239
240 const (
241         // RxModeInterrupt tells libmemif to send interrupt signal when data are available.
242         RxModeInterrupt RxMode = iota
243
244         // RxModePolling means that the user needs to explicitly poll for data on RX
245         // queues.
246         RxModePolling
247 )
248
249 // RawPacketData represents raw packet data. libmemif doesn't care what the
250 // actual content is, it only manipulates with raw bytes.
251 type RawPacketData []byte
252
253 // MemifMeta is used to store a basic memif metadata needed for identification
254 // and connection establishment.
255 type MemifMeta struct {
256         // IfName is the interface name. Has to be unique across all created memifs.
257         // Interface name is truncated if needed to have no more than 32 characters.
258         IfName string
259
260         // InstanceName identifies the endpoint. If omitted, the application
261         // name passed to Init() will be used instead.
262         // Instance name is truncated if needed to have no more than 32 characters.
263         InstanceName string
264
265         // ConnID is a connection ID used to match opposite sides of the memif
266         // connection.
267         ConnID uint32
268
269         // SocketFilename is the filename of the AF_UNIX socket through which
270         // the connection is established.
271         // The string is truncated if neede to fit into sockaddr_un.sun_path
272         // (108 characters on Linux).
273         SocketFilename string
274
275         // Secret must be the same on both sides for the authentication to succeed.
276         // Empty string is allowed.
277         // The secret is truncated if needed to have no more than 24 characters.
278         Secret string
279
280         // IsMaster is set to true if memif operates in the Master mode.
281         IsMaster bool
282
283         // Mode is the mode (layer/behaviour) in which the memif operates.
284         Mode IfMode
285 }
286
287 // MemifShmSpecs is used to store the specification of the shared memory segment
288 // used by memif to send/receive packets.
289 type MemifShmSpecs struct {
290         // NumRxQueues is the number of Rx queues.
291         // Default is 1 (used if the value is 0).
292         NumRxQueues uint8
293
294         // NumTxQueues is the number of Tx queues.
295         // Default is 1 (used if the value is 0).
296         NumTxQueues uint8
297
298         // BufferSize is the size of the buffer to hold one packet, or a single
299         // fragment of a jumbo frame. Default is 2048 (used if the value is 0).
300         BufferSize uint16
301
302         // Log2RingSize is the number of items in the ring represented through
303         // the logarithm base 2.
304         // Default is 10 (used if the value is 0).
305         Log2RingSize uint8
306 }
307
308 // MemifConfig is the memif configuration.
309 // Used as the input argument to CreateInterface().
310 // It is the slave's config that mostly decides the parameters of the connection,
311 // but master may limit some of the quantities if needed (based on the memif
312 // protocol or master's configuration)
313 type MemifConfig struct {
314         MemifMeta
315         MemifShmSpecs
316 }
317
318 // ConnUpdateCallback is a callback type declaration used with callbacks
319 // related to connection status changes.
320 type ConnUpdateCallback func(memif *Memif) (err error)
321
322 // MemifCallbacks is a container for all callbacks provided by memif.
323 // Any callback can be nil, in which case it will be simply skipped.
324 // Important: Do not call CreateInterface() or Memif.Close() from within a callback
325 // or a deadlock will occur. Instead send signal through a channel to another
326 // go routine which will be able to create/remove memif interface(s).
327 type MemifCallbacks struct {
328         // OnConnect is triggered when a connection for a given memif was established.
329         OnConnect ConnUpdateCallback
330
331         // OnDisconnect is triggered when a connection for a given memif was lost.
332         OnDisconnect ConnUpdateCallback
333 }
334
335 // Memif represents a single memif interface. It provides methods to send/receive
336 // packets in bursts in either the polling mode or in the interrupt mode with
337 // the help of golang channels.
338 type Memif struct {
339         MemifMeta
340
341         // Per-library references
342         ifIndex int                   // index used in the Go-libmemif context (Context.memifs)
343         cHandle C.memif_conn_handle_t // handle used in C-libmemif
344
345         // Callbacks
346         callbacks *MemifCallbacks
347
348         // Interrupt
349         intCh      chan uint8      // memif-global interrupt channel (value = queue ID)
350         queueIntCh []chan struct{} // per RX queue interrupt channel
351
352         // Rx/Tx queues
353         stopQPollFd int              // event file descriptor used to stop pollRxQueue-s
354         wg          sync.WaitGroup   // wait group for all pollRxQueue-s
355         rxQueueBufs []CPacketBuffers // an array of C-libmemif packet buffers for each RX queue
356         txQueueBufs []CPacketBuffers // an array of C-libmemif packet buffers for each TX queue
357 }
358
359 // MemifDetails provides a detailed runtime information about a memif interface.
360 type MemifDetails struct {
361         MemifMeta
362         MemifConnDetails
363 }
364
365 // MemifConnDetails provides a detailed runtime information about a memif
366 // connection.
367 type MemifConnDetails struct {
368         // RemoteIfName is the name of the memif on the opposite side.
369         RemoteIfName string
370         // RemoteInstanceName is the name of the endpoint on the opposite side.
371         RemoteInstanceName string
372         // HasLink is true if the connection has link (= is established and functional).
373         HasLink bool
374         // RxQueues contains details for each Rx queue.
375         RxQueues []MemifQueueDetails
376         // TxQueues contains details for each Tx queue.
377         TxQueues []MemifQueueDetails
378 }
379
380 // MemifQueueDetails provides a detailed runtime information about a memif queue.
381 // Queue = Ring + the associated buffers (one directional).
382 type MemifQueueDetails struct {
383         // QueueID is the ID of the queue.
384         QueueID uint8
385         // RingSize is the number of slots in the ring (not logarithmic).
386         RingSize uint32
387         // BufferSize is the size of each buffer pointed to from the ring slots.
388         BufferSize uint16
389         /* Further ring information TO-BE-ADDED when C-libmemif supports them. */
390 }
391
392 // CPacketBuffers stores an array of memif buffers for use with TxBurst or RxBurst.
393 type CPacketBuffers struct {
394         buffers *C.memif_buffer_t
395         count   int
396 }
397
398 // Context is a global Go-libmemif runtime context.
399 type Context struct {
400         lock           sync.RWMutex
401         initialized    bool
402         memifs         map[int] /* ifIndex */ *Memif /* slice of all active memif interfaces */
403         nextMemifIndex int
404
405         wg sync.WaitGroup /* wait-group for pollEvents() */
406 }
407
408 var (
409         // logger used by the adapter.
410         log *logger.Logger
411
412         // Global Go-libmemif context.
413         context = &Context{initialized: false}
414 )
415
416 // init initializes global logger, which logs debug level messages to stdout.
417 func init() {
418         log = logger.New()
419         log.Out = os.Stdout
420         log.Level = logger.DebugLevel
421 }
422
423 // SetLogger changes the logger for Go-libmemif to the provided one.
424 // The logger is not used for logging of C-libmemif.
425 func SetLogger(l *logger.Logger) {
426         log = l
427 }
428
429 // Init initializes the libmemif library. Must by called exactly once and before
430 // any libmemif functions. Do not forget to call Cleanup() before exiting
431 // your application.
432 // <appName> should be a human-readable string identifying your application.
433 // For example, VPP returns the version information ("show version" from VPP CLI).
434 func Init(appName string) error {
435         context.lock.Lock()
436         defer context.lock.Unlock()
437
438         if context.initialized {
439                 return ErrAlreadyInit
440         }
441
442         log.Debug("Initializing libmemif library")
443
444         // Initialize C-libmemif.
445         var errCode int
446         if appName == "" {
447                 errCode = int(C.memif_init(nil, nil))
448         } else {
449                 appName := C.CString(appName)
450                 defer C.free(unsafe.Pointer(appName))
451                 errCode = int(C.memif_init(nil, appName))
452         }
453         err := getMemifError(errCode)
454         if err != nil {
455                 return err
456         }
457
458         // Initialize the map of memory interfaces.
459         context.memifs = make(map[int]*Memif)
460
461         // Start event polling.
462         context.wg.Add(1)
463         go pollEvents()
464
465         context.initialized = true
466         log.Debug("libmemif library was initialized")
467         return err
468 }
469
470 // Cleanup cleans up all the resources allocated by libmemif.
471 func Cleanup() error {
472         context.lock.Lock()
473         defer context.lock.Unlock()
474
475         if !context.initialized {
476                 return ErrNotInit
477         }
478
479         log.Debug("Closing libmemif library")
480
481         // Delete all active interfaces.
482         for _, memif := range context.memifs {
483                 memif.Close()
484         }
485
486         // Stop the event loop (if supported by C-libmemif).
487         errCode := C.memif_cancel_poll_event()
488         err := getMemifError(int(errCode))
489         if err == nil {
490                 log.Debug("Waiting for pollEvents() to stop...")
491                 context.wg.Wait()
492                 log.Debug("pollEvents() has stopped...")
493         } else {
494                 log.WithField("err", err).Debug("NOT Waiting for pollEvents to stop...")
495         }
496
497         // Run cleanup for C-libmemif.
498         err = getMemifError(int(C.memif_cleanup()))
499         if err == nil {
500                 context.initialized = false
501                 log.Debug("libmemif library was closed")
502         }
503         return err
504 }
505
506 // CreateInterface creates a new memif interface with the given configuration.
507 // The same callbacks can be used with multiple memifs. The first callback input
508 // argument (*Memif) can be used to tell which memif the callback was triggered for.
509 // The method is thread-safe.
510 func CreateInterface(config *MemifConfig, callbacks *MemifCallbacks) (memif *Memif, err error) {
511         context.lock.Lock()
512         defer context.lock.Unlock()
513
514         if !context.initialized {
515                 return nil, ErrNotInit
516         }
517
518         log.WithField("ifName", config.IfName).Debug("Creating a new memif interface")
519
520         // Create memif-wrapper for Go-libmemif.
521         memif = &Memif{
522                 MemifMeta: config.MemifMeta,
523                 callbacks: &MemifCallbacks{},
524                 ifIndex:   context.nextMemifIndex,
525         }
526
527         // Initialize memif callbacks.
528         if callbacks != nil {
529                 memif.callbacks.OnConnect = callbacks.OnConnect
530                 memif.callbacks.OnDisconnect = callbacks.OnDisconnect
531         }
532
533         // Initialize memif-global interrupt channel.
534         memif.intCh = make(chan uint8, 1<<6)
535
536         // Initialize event file descriptor for stopping Rx/Tx queue polling.
537         memif.stopQPollFd = int(C.eventfd(0, C.EFD_NONBLOCK))
538         if memif.stopQPollFd < 0 {
539                 return nil, ErrSyscall
540         }
541
542         // Initialize memif input arguments.
543         args := &C.govpp_memif_conn_args_t{}
544         // - socket file name
545         if config.SocketFilename != "" {
546                 args.socket_filename = C.CString(config.SocketFilename)
547                 defer C.free(unsafe.Pointer(args.socket_filename))
548         }
549         // - interface ID
550         args.interface_id = C.memif_interface_id_t(config.ConnID)
551         // - interface name
552         if config.IfName != "" {
553                 args.interface_name = C.CString(config.IfName)
554                 defer C.free(unsafe.Pointer(args.interface_name))
555         }
556         // - instance name
557         if config.InstanceName != "" {
558                 args.instance_name = C.CString(config.InstanceName)
559                 defer C.free(unsafe.Pointer(args.instance_name))
560         }
561         // - mode
562         switch config.Mode {
563         case IfModeEthernet:
564                 args.mode = C.MEMIF_INTERFACE_MODE_ETHERNET
565         case IfModeIP:
566                 args.mode = C.MEMIF_INTERFACE_MODE_IP
567         case IfModePuntInject:
568                 args.mode = C.MEMIF_INTERFACE_MODE_PUNT_INJECT
569         default:
570                 args.mode = C.MEMIF_INTERFACE_MODE_ETHERNET
571         }
572         // - secret
573         if config.Secret != "" {
574                 args.secret = C.CString(config.Secret)
575                 defer C.free(unsafe.Pointer(args.secret))
576         }
577         // - master/slave flag + number of Rx/Tx queues
578         if config.IsMaster {
579                 args.num_s2m_rings = C.uint8_t(config.NumRxQueues)
580                 args.num_m2s_rings = C.uint8_t(config.NumTxQueues)
581                 args.is_master = C.uint8_t(1)
582         } else {
583                 args.num_s2m_rings = C.uint8_t(config.NumTxQueues)
584                 args.num_m2s_rings = C.uint8_t(config.NumRxQueues)
585                 args.is_master = C.uint8_t(0)
586         }
587         // - buffer size
588         args.buffer_size = C.uint16_t(config.BufferSize)
589         // - log_2(ring size)
590         args.log2_ring_size = C.memif_log2_ring_size_t(config.Log2RingSize)
591
592         // Create memif in C-libmemif.
593         errCode := C.govpp_memif_create(&memif.cHandle, args, unsafe.Pointer(uintptr(memif.ifIndex)))
594         err = getMemifError(int(errCode))
595         if err != nil {
596                 return nil, err
597         }
598
599         // Register the new memif.
600         context.memifs[memif.ifIndex] = memif
601         context.nextMemifIndex++
602         log.WithField("ifName", config.IfName).Debug("A new memif interface was created")
603
604         return memif, nil
605 }
606
607 // GetInterruptChan returns a channel which is continuously being filled with
608 // IDs of queues with data ready to be received.
609 // Since there is only one interrupt signal sent for an entire burst of packets,
610 // an interrupt handling routine should repeatedly call RxBurst() until
611 // the function returns an empty slice of packets. This way it is ensured
612 // that there are no packets left on the queue unread when the interrupt signal
613 // is cleared.
614 // The method is thread-safe.
615 func (memif *Memif) GetInterruptChan() (ch <-chan uint8 /* queue ID */) {
616         return memif.intCh
617 }
618
619 // GetQueueInterruptChan returns an empty-data channel which fires every time
620 // there are data to read on a given queue.
621 // It is only valid to call this function if memif is in the connected state.
622 // Channel is automatically closed when the connection goes down (but after
623 // the user provided callback OnDisconnect has executed).
624 // Since there is only one interrupt signal sent for an entire burst of packets,
625 // an interrupt handling routine should repeatedly call RxBurst() until
626 // the function returns an empty slice of packets. This way it is ensured
627 // that there are no packets left on the queue unread when the interrupt signal
628 // is cleared.
629 // The method is thread-safe.
630 func (memif *Memif) GetQueueInterruptChan(queueID uint8) (ch <-chan struct{}, err error) {
631         if int(queueID) >= len(memif.queueIntCh) {
632                 return nil, ErrQueueID
633         }
634         return memif.queueIntCh[queueID], nil
635 }
636
637 // SetRxMode allows to switch between the interrupt and the polling mode for Rx.
638 // The method is thread-safe.
639 func (memif *Memif) SetRxMode(queueID uint8, rxMode RxMode) (err error) {
640         var cRxMode C.memif_rx_mode_t
641         switch rxMode {
642         case RxModeInterrupt:
643                 cRxMode = C.MEMIF_RX_MODE_INTERRUPT
644         case RxModePolling:
645                 cRxMode = C.MEMIF_RX_MODE_POLLING
646         default:
647                 cRxMode = C.MEMIF_RX_MODE_INTERRUPT
648         }
649         errCode := C.memif_set_rx_mode(memif.cHandle, cRxMode, C.uint16_t(queueID))
650         return getMemifError(int(errCode))
651 }
652
653 // GetDetails returns a detailed runtime information about this memif.
654 // The method is thread-safe.
655 func (memif *Memif) GetDetails() (details *MemifDetails, err error) {
656         cDetails := C.govpp_memif_details_t{}
657         var buf *C.char
658
659         // Get memif details from C-libmemif.
660         errCode := C.govpp_memif_get_details(memif.cHandle, &cDetails, &buf)
661         err = getMemifError(int(errCode))
662         if err != nil {
663                 return nil, err
664         }
665         defer C.free(unsafe.Pointer(buf))
666
667         // Convert details from C to Go.
668         details = &MemifDetails{}
669         // - metadata:
670         details.IfName = C.GoString(cDetails.if_name)
671         details.InstanceName = C.GoString(cDetails.inst_name)
672         details.ConnID = uint32(cDetails.id)
673         details.SocketFilename = C.GoString(cDetails.socket_filename)
674         if cDetails.secret != nil {
675                 details.Secret = C.GoString(cDetails.secret)
676         }
677         details.IsMaster = cDetails.role == C.uint8_t(0)
678         switch cDetails.mode {
679         case C.MEMIF_INTERFACE_MODE_ETHERNET:
680                 details.Mode = IfModeEthernet
681         case C.MEMIF_INTERFACE_MODE_IP:
682                 details.Mode = IfModeIP
683         case C.MEMIF_INTERFACE_MODE_PUNT_INJECT:
684                 details.Mode = IfModePuntInject
685         default:
686                 details.Mode = IfModeEthernet
687         }
688         // - connection details:
689         details.RemoteIfName = C.GoString(cDetails.remote_if_name)
690         details.RemoteInstanceName = C.GoString(cDetails.remote_inst_name)
691         details.HasLink = cDetails.link_up_down == C.uint8_t(1)
692         // - RX queues:
693         var i uint8
694         for i = 0; i < uint8(cDetails.rx_queues_num); i++ {
695                 cRxQueue := C.govpp_get_rx_queue_details(&cDetails, C.int(i))
696                 queueDetails := MemifQueueDetails{
697                         QueueID:    uint8(cRxQueue.qid),
698                         RingSize:   uint32(cRxQueue.ring_size),
699                         BufferSize: uint16(cRxQueue.buffer_size),
700                 }
701                 details.RxQueues = append(details.RxQueues, queueDetails)
702         }
703         // - TX queues:
704         for i = 0; i < uint8(cDetails.tx_queues_num); i++ {
705                 cTxQueue := C.govpp_get_tx_queue_details(&cDetails, C.int(i))
706                 queueDetails := MemifQueueDetails{
707                         QueueID:    uint8(cTxQueue.qid),
708                         RingSize:   uint32(cTxQueue.ring_size),
709                         BufferSize: uint16(cTxQueue.buffer_size),
710                 }
711                 details.TxQueues = append(details.TxQueues, queueDetails)
712         }
713
714         return details, nil
715 }
716
717 // TxBurst is used to send multiple packets in one call into a selected queue.
718 // The actual number of packets sent may be smaller and is returned as <count>.
719 // The method is non-blocking even if the ring is full and no packet can be sent.
720 // It is only valid to call this function if memif is in the connected state.
721 // Multiple TxBurst-s can run concurrently provided that each targets a different
722 // TX queue.
723 func (memif *Memif) TxBurst(queueID uint8, packets []RawPacketData) (count uint16, err error) {
724         var sentCount C.uint16_t
725         var allocated C.uint16_t
726         var bufSize int
727
728         if len(packets) == 0 {
729                 return 0, nil
730         }
731
732         if int(queueID) >= len(memif.txQueueBufs) {
733                 return 0, ErrQueueID
734         }
735
736         // The largest packet in the set determines the packet buffer size.
737         for _, packet := range packets {
738                 if len(packet) > int(bufSize) {
739                         bufSize = len(packet)
740                 }
741         }
742
743         // Reallocate Tx buffers if needed to fit the input packets.
744         pb := memif.txQueueBufs[queueID]
745         bufCount := len(packets)
746         if pb.count < bufCount {
747                 newBuffers := C.realloc(unsafe.Pointer(pb.buffers), C.size_t(bufCount*int(C.sizeof_memif_buffer_t)))
748                 if newBuffers == nil {
749                         // Realloc failed, <count> will be less than len(packets).
750                         bufCount = pb.count
751                 } else {
752                         pb.buffers = (*C.memif_buffer_t)(newBuffers)
753                         pb.count = bufCount
754                 }
755         }
756
757         // Allocate ring slots.
758         cQueueID := C.uint16_t(queueID)
759         errCode := C.memif_buffer_alloc(memif.cHandle, cQueueID, pb.buffers, C.uint16_t(bufCount),
760                 &allocated, C.uint16_t(bufSize))
761         err = getMemifError(int(errCode))
762         if err == ErrNoBufRing {
763                 // Not enough ring slots, <count> will be less than bufCount.
764                 err = nil
765         }
766         if err != nil {
767                 return 0, err
768         }
769
770         // Copy packet data into the buffers.
771         for i := 0; i < int(allocated); i++ {
772                 packetData := unsafe.Pointer(&packets[i][0])
773                 C.govpp_copy_packet_data(pb.buffers, C.int(i), packetData, C.uint32_t(len(packets[i])))
774         }
775
776         errCode = C.memif_tx_burst(memif.cHandle, cQueueID, pb.buffers, allocated, &sentCount)
777         err = getMemifError(int(errCode))
778         if err != nil {
779                 return 0, err
780         }
781         count = uint16(sentCount)
782
783         return count, nil
784 }
785
786 // RxBurst is used to receive multiple packets in one call from a selected queue.
787 // <count> is the number of packets to receive. The actual number of packets
788 // received may be smaller. <count> effectively limits the maximum number
789 // of packets to receive in one burst (for a flat, predictable memory usage).
790 // The method is non-blocking even if there are no packets to receive.
791 // It is only valid to call this function if memif is in the connected state.
792 // Multiple RxBurst-s can run concurrently provided that each targets a different
793 // Rx queue.
794 func (memif *Memif) RxBurst(queueID uint8, count uint16) (packets []RawPacketData, err error) {
795         var recvCount C.uint16_t
796         var freed C.uint16_t
797
798         if count == 0 {
799                 return packets, nil
800         }
801
802         if int(queueID) >= len(memif.rxQueueBufs) {
803                 return packets, ErrQueueID
804         }
805
806         // Reallocate Rx buffers if needed to fit the output packets.
807         pb := memif.rxQueueBufs[queueID]
808         bufCount := int(count)
809         if pb.count < bufCount {
810                 newBuffers := C.realloc(unsafe.Pointer(pb.buffers), C.size_t(bufCount*int(C.sizeof_memif_buffer_t)))
811                 if newBuffers == nil {
812                         // Realloc failed, len(<packets>) will be certainly less than <count>.
813                         bufCount = pb.count
814                 } else {
815                         pb.buffers = (*C.memif_buffer_t)(newBuffers)
816                         pb.count = bufCount
817                 }
818         }
819
820         cQueueID := C.uint16_t(queueID)
821         errCode := C.memif_rx_burst(memif.cHandle, cQueueID, pb.buffers, C.uint16_t(bufCount), &recvCount)
822         err = getMemifError(int(errCode))
823         if err == ErrNoBuf {
824                 // More packets to read - the user is expected to run RxBurst() until there
825                 // are no more packets to receive.
826                 err = nil
827         }
828         if err != nil {
829                 return packets, err
830         }
831
832         // Copy packet data into the instances of RawPacketData.
833         for i := 0; i < int(recvCount); i++ {
834                 var packetSize C.int
835                 packetData := C.govpp_get_packet_data(pb.buffers, C.int(i), &packetSize)
836                 packets = append(packets, C.GoBytes(packetData, packetSize))
837         }
838
839         errCode = C.memif_buffer_free(memif.cHandle, cQueueID, pb.buffers, recvCount, &freed)
840         err = getMemifError(int(errCode))
841         if err != nil {
842                 // Throw away packets to avoid duplicities.
843                 packets = nil
844         }
845
846         return packets, err
847 }
848
849 // Close removes the memif interface. If the memif is in the connected state,
850 // the connection is first properly closed.
851 // Do not access memif after it is closed, let garbage collector to remove it.
852 func (memif *Memif) Close() error {
853         log.WithField("ifName", memif.IfName).Debug("Closing the memif interface")
854
855         // Delete memif from C-libmemif.
856         err := getMemifError(int(C.memif_delete(&memif.cHandle)))
857
858         if err != nil {
859                 // Close memif-global interrupt channel.
860                 close(memif.intCh)
861                 // Close file descriptor stopQPollFd.
862                 C.close(C.int(memif.stopQPollFd))
863         }
864
865         context.lock.Lock()
866         defer context.lock.Unlock()
867         // Unregister the interface from the context.
868         delete(context.memifs, memif.ifIndex)
869         log.WithField("ifName", memif.IfName).Debug("memif interface was closed")
870
871         return err
872 }
873
874 // initQueues allocates resources associated with Rx/Tx queues.
875 func (memif *Memif) initQueues() error {
876         // Get Rx/Tx queues count.
877         details, err := memif.GetDetails()
878         if err != nil {
879                 return err
880         }
881
882         log.WithFields(logger.Fields{
883                 "ifName":   memif.IfName,
884                 "Rx-count": len(details.RxQueues),
885                 "Tx-count": len(details.TxQueues),
886         }).Debug("Initializing Rx/Tx queues.")
887
888         // Initialize interrupt channels.
889         var i int
890         for i = 0; i < len(details.RxQueues); i++ {
891                 queueIntCh := make(chan struct{}, 1)
892                 memif.queueIntCh = append(memif.queueIntCh, queueIntCh)
893         }
894
895         // Initialize Rx/Tx packet buffers.
896         for i = 0; i < len(details.RxQueues); i++ {
897                 memif.rxQueueBufs = append(memif.rxQueueBufs, CPacketBuffers{})
898         }
899         for i = 0; i < len(details.TxQueues); i++ {
900                 memif.txQueueBufs = append(memif.txQueueBufs, CPacketBuffers{})
901         }
902
903         return nil
904 }
905
906 // closeQueues deallocates all resources associated with Rx/Tx queues.
907 func (memif *Memif) closeQueues() {
908         log.WithFields(logger.Fields{
909                 "ifName":   memif.IfName,
910                 "Rx-count": len(memif.rxQueueBufs),
911                 "Tx-count": len(memif.txQueueBufs),
912         }).Debug("Closing Rx/Tx queues.")
913
914         // Close interrupt channels.
915         for _, ch := range memif.queueIntCh {
916                 close(ch)
917         }
918         memif.queueIntCh = nil
919
920         // Deallocate Rx/Tx packet buffers.
921         for _, pb := range memif.rxQueueBufs {
922                 C.free(unsafe.Pointer(pb.buffers))
923         }
924         memif.rxQueueBufs = nil
925         for _, pb := range memif.txQueueBufs {
926                 C.free(unsafe.Pointer(pb.buffers))
927         }
928         memif.txQueueBufs = nil
929 }
930
931 // pollEvents repeatedly polls for a libmemif event.
932 func pollEvents() {
933         defer context.wg.Done()
934         for {
935                 errCode := C.memif_poll_event(C.int(-1))
936                 err := getMemifError(int(errCode))
937                 if err == ErrPollCanceled {
938                         return
939                 }
940         }
941 }
942
943 // pollRxQueue repeatedly polls an Rx queue for interrupts.
944 func pollRxQueue(memif *Memif, queueID uint8) {
945         defer memif.wg.Done()
946
947         log.WithFields(logger.Fields{
948                 "ifName":   memif.IfName,
949                 "queue-ID": queueID,
950         }).Debug("Started queue interrupt polling.")
951
952         var qfd C.int
953         errCode := C.memif_get_queue_efd(memif.cHandle, C.uint16_t(queueID), &qfd)
954         err := getMemifError(int(errCode))
955         if err != nil {
956                 log.WithField("err", err).Error("memif_get_queue_efd() failed")
957                 return
958         }
959
960         // Create epoll file descriptor.
961         var event [1]syscall.EpollEvent
962         epFd, err := syscall.EpollCreate1(0)
963         if err != nil {
964                 log.WithField("err", err).Error("epoll_create1() failed")
965                 return
966         }
967         defer syscall.Close(epFd)
968
969         // Add Rx queue interrupt file descriptor.
970         event[0].Events = syscall.EPOLLIN
971         event[0].Fd = int32(qfd)
972         if err = syscall.EpollCtl(epFd, syscall.EPOLL_CTL_ADD, int(qfd), &event[0]); err != nil {
973                 log.WithField("err", err).Error("epoll_ctl() failed")
974                 return
975         }
976
977         // Add file descriptor used to stop this go routine.
978         event[0].Events = syscall.EPOLLIN
979         event[0].Fd = int32(memif.stopQPollFd)
980         if err = syscall.EpollCtl(epFd, syscall.EPOLL_CTL_ADD, memif.stopQPollFd, &event[0]); err != nil {
981                 log.WithField("err", err).Error("epoll_ctl() failed")
982                 return
983         }
984
985         // Poll for interrupts.
986         for {
987                 _, err := syscall.EpollWait(epFd, event[:], -1)
988                 if err != nil {
989                         log.WithField("err", err).Error("epoll_wait() failed")
990                         return
991                 }
992
993                 // Handle Rx Interrupt.
994                 if event[0].Fd == int32(qfd) {
995                         // Consume the interrupt event.
996                         buf := make([]byte, 8)
997                         _, err = syscall.Read(int(qfd), buf[:])
998                         if err != nil {
999                                 log.WithField("err", err).Warn("read() failed")
1000                         }
1001
1002                         // Send signal to memif-global interrupt channel.
1003                         select {
1004                         case memif.intCh <- queueID:
1005                                 break
1006                         default:
1007                                 break
1008                         }
1009
1010                         // Send signal to queue-specific interrupt channel.
1011                         select {
1012                         case memif.queueIntCh[queueID] <- struct{}{}:
1013                                 break
1014                         default:
1015                                 break
1016                         }
1017                 }
1018
1019                 // Stop the go routine if requested.
1020                 if event[0].Fd == int32(memif.stopQPollFd) {
1021                         log.WithFields(logger.Fields{
1022                                 "ifName":   memif.IfName,
1023                                 "queue-ID": queueID,
1024                         }).Debug("Stopped queue interrupt polling.")
1025                         return
1026                 }
1027         }
1028 }
1029
1030 //export go_on_connect_callback
1031 func go_on_connect_callback(privateCtx unsafe.Pointer) C.int {
1032         log.Debug("go_on_connect_callback BEGIN")
1033         defer log.Debug("go_on_connect_callback END")
1034         context.lock.RLock()
1035         defer context.lock.RUnlock()
1036
1037         // Get memif reference.
1038         ifIndex := int(uintptr(privateCtx))
1039         memif, exists := context.memifs[ifIndex]
1040         if !exists {
1041                 return C.int(ErrNoConn.Code())
1042         }
1043
1044         // Initialize Rx/Tx queues.
1045         err := memif.initQueues()
1046         if err != nil {
1047                 if memifErr, ok := err.(*MemifError); ok {
1048                         return C.int(memifErr.Code())
1049                 }
1050                 return C.int(ErrUnknown.Code())
1051         }
1052
1053         // Call the user callback.
1054         if memif.callbacks.OnConnect != nil {
1055                 memif.callbacks.OnConnect(memif)
1056         }
1057
1058         // Start polling the RX queues for interrupts.
1059         for i := 0; i < len(memif.queueIntCh); i++ {
1060                 memif.wg.Add(1)
1061                 go pollRxQueue(memif, uint8(i))
1062         }
1063
1064         return C.int(0)
1065 }
1066
1067 //export go_on_disconnect_callback
1068 func go_on_disconnect_callback(privateCtx unsafe.Pointer) C.int {
1069         log.Debug("go_on_disconnect_callback BEGIN")
1070         defer log.Debug("go_on_disconnect_callback END")
1071         context.lock.RLock()
1072         defer context.lock.RUnlock()
1073
1074         // Get memif reference.
1075         ifIndex := int(uintptr(privateCtx))
1076         memif, exists := context.memifs[ifIndex]
1077         if !exists {
1078                 // Already closed.
1079                 return C.int(0)
1080         }
1081
1082         // Stop polling the RX queues for interrupts.
1083         buf := make([]byte, 8)
1084         binary.PutUvarint(buf, 1)
1085         // - add an event
1086         _, err := syscall.Write(memif.stopQPollFd, buf[:])
1087         if err != nil {
1088                 return C.int(ErrSyscall.Code())
1089         }
1090         // - wait
1091         memif.wg.Wait()
1092         // - remove the event
1093         _, err = syscall.Read(memif.stopQPollFd, buf[:])
1094         if err != nil {
1095                 return C.int(ErrSyscall.Code())
1096         }
1097
1098         // Call the user callback.
1099         if memif.callbacks.OnDisconnect != nil {
1100                 memif.callbacks.OnDisconnect(memif)
1101         }
1102
1103         // Close Rx/Tx queues.
1104         memif.closeQueues()
1105
1106         return C.int(0)
1107 }