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