aboutsummaryrefslogtreecommitdiffstats
path: root/adapter/vppapiclient/vppapiclient.go
diff options
context:
space:
mode:
Diffstat (limited to 'adapter/vppapiclient/vppapiclient.go')
-rw-r--r--adapter/vppapiclient/vppapiclient.go223
1 files changed, 223 insertions, 0 deletions
diff --git a/adapter/vppapiclient/vppapiclient.go b/adapter/vppapiclient/vppapiclient.go
new file mode 100644
index 0000000..34ad199
--- /dev/null
+++ b/adapter/vppapiclient/vppapiclient.go
@@ -0,0 +1,223 @@
+// Copyright (c) 2017 Cisco and/or its affiliates.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// +build !windows,!darwin
+
+package vppapiclient
+
+/*
+#cgo CFLAGS: -DPNG_DEBUG=1
+#cgo LDFLAGS: -lvppapiclient
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+#include <vpp-api/client/vppapiclient.h>
+
+extern void go_msg_callback(uint16_t msg_id, void* data, size_t size);
+
+typedef struct __attribute__((__packed__)) _req_header {
+ uint16_t msg_id;
+ uint32_t client_index;
+ uint32_t context;
+} req_header_t;
+
+typedef struct __attribute__((__packed__)) _reply_header {
+ uint16_t msg_id;
+} reply_header_t;
+
+static void
+govpp_msg_callback(unsigned char *data, int size)
+{
+ reply_header_t *header = ((reply_header_t *)data);
+ go_msg_callback(ntohs(header->msg_id), data, size);
+}
+
+static int
+govpp_send(uint32_t context, void *data, size_t size)
+{
+ req_header_t *header = ((req_header_t *)data);
+ header->context = htonl(context);
+ return vac_write(data, size);
+}
+
+static int
+govpp_connect(char *shm)
+{
+ return vac_connect("govpp", shm, govpp_msg_callback, 32);
+}
+
+static int
+govpp_disconnect()
+{
+ return vac_disconnect();
+}
+
+static uint32_t
+govpp_get_msg_index(char *name_and_crc)
+{
+ return vac_get_msg_index(name_and_crc);
+}
+*/
+import "C"
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+ "reflect"
+ "unsafe"
+
+ "git.fd.io/govpp.git/adapter"
+ "github.com/fsnotify/fsnotify"
+)
+
+const (
+ // shmDir is a directory where shared memory is supposed to be created.
+ shmDir = "/dev/shm/"
+ // vppShmFile is a default name of the file in the shmDir.
+ vppShmFile = "vpe-api"
+)
+
+// global VPP binary API client adapter context
+var client *VppClient
+
+// VppClient is the default implementation of the VppAPI.
+type VppClient struct {
+ shmPrefix string
+ msgCallback adapter.MsgCallback
+}
+
+// NewVppClient returns a new VPP binary API client.
+func NewVppClient(shmPrefix string) *VppClient {
+ return &VppClient{
+ shmPrefix: shmPrefix,
+ }
+}
+
+// Connect connects the process to VPP.
+func (a *VppClient) Connect() error {
+ if client != nil {
+ return fmt.Errorf("already connected to binary API, disconnect first")
+ }
+
+ var rc _Ctype_int
+ if a.shmPrefix == "" {
+ rc = C.govpp_connect(nil)
+ } else {
+ shm := C.CString(a.shmPrefix)
+ rc = C.govpp_connect(shm)
+ }
+ if rc != 0 {
+ return fmt.Errorf("connecting to VPP binary API failed (rc=%v)", rc)
+ }
+
+ client = a
+ return nil
+}
+
+// Disconnect disconnects the process from VPP.
+func (a *VppClient) Disconnect() error {
+ client = nil
+
+ rc := C.govpp_disconnect()
+ if rc != 0 {
+ return fmt.Errorf("disconnecting from VPP binary API failed (rc=%v)", rc)
+ }
+
+ return nil
+}
+
+// GetMsgID returns a runtime message ID for the given message name and CRC.
+func (a *VppClient) GetMsgID(msgName string, msgCrc string) (uint16, error) {
+ nameAndCrc := C.CString(msgName + "_" + msgCrc)
+ defer C.free(unsafe.Pointer(nameAndCrc))
+
+ msgID := uint16(C.govpp_get_msg_index(nameAndCrc))
+ if msgID == ^uint16(0) {
+ // VPP does not know this message
+ return msgID, fmt.Errorf("unknown message: %v (crc: %v)", msgName, msgCrc)
+ }
+
+ return msgID, nil
+}
+
+// SendMsg sends a binary-encoded message to VPP.
+func (a *VppClient) SendMsg(context uint32, data []byte) error {
+ rc := C.govpp_send(C.uint32_t(context), unsafe.Pointer(&data[0]), C.size_t(len(data)))
+ if rc != 0 {
+ return fmt.Errorf("unable to send the message (rc=%v)", rc)
+ }
+ return nil
+}
+
+// SetMsgCallback sets a callback function that will be called by the adapter
+// whenever a message comes from VPP.
+func (a *VppClient) SetMsgCallback(cb adapter.MsgCallback) {
+ a.msgCallback = cb
+}
+
+// WaitReady blocks until shared memory for sending
+// binary api calls is present on the file system.
+func (a *VppClient) WaitReady() error {
+ var path string
+
+ // join the path to the shared memory segment
+ if a.shmPrefix == "" {
+ path = filepath.Join(shmDir, vppShmFile)
+ } else {
+ path = filepath.Join(shmDir, a.shmPrefix+"-"+vppShmFile)
+ }
+
+ // check if file at the path exists
+ if _, err := os.Stat(path); err == nil {
+ // file exists, we are ready
+ return nil
+ } else if !os.IsNotExist(err) {
+ return err
+ }
+
+ // file does not exist, start watching folder
+ watcher, err := fsnotify.NewWatcher()
+ if err != nil {
+ return err
+ }
+ defer watcher.Close()
+
+ if err := watcher.Add(shmDir); err != nil {
+ return err
+ }
+
+ for {
+ ev := <-watcher.Events
+ if ev.Name == path {
+ if (ev.Op & fsnotify.Create) == fsnotify.Create {
+ // file was created, we are ready
+ break
+ }
+ }
+ }
+
+ return nil
+}
+
+//export go_msg_callback
+func go_msg_callback(msgID C.uint16_t, data unsafe.Pointer, size C.size_t) {
+ // convert unsafe.Pointer to byte slice
+ sliceHeader := &reflect.SliceHeader{Data: uintptr(data), Len: int(size), Cap: int(size)}
+ byteSlice := *(*[]byte)(unsafe.Pointer(sliceHeader))
+
+ client.msgCallback(uint16(msgID), byteSlice)
+}