1 // Copyright (c) 2017 Cisco and/or its affiliates.
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:
7 // http://www.apache.org/licenses/LICENSE-2.0
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.
15 // +build !windows,!darwin
20 #cgo CFLAGS: -DPNG_DEBUG=1
21 #cgo LDFLAGS: -lvppapiclient
26 #include <arpa/inet.h>
27 #include <vpp-api/client/vppapiclient.h>
29 extern void go_msg_callback(uint16_t msg_id, void* data, size_t size);
31 typedef struct __attribute__((__packed__)) _req_header {
33 uint32_t client_index;
37 typedef struct __attribute__((__packed__)) _reply_header {
42 govpp_msg_callback(unsigned char *data, int size)
44 reply_header_t *header = ((reply_header_t *)data);
45 go_msg_callback(ntohs(header->msg_id), data, size);
49 govpp_send(uint32_t context, void *data, size_t size)
51 req_header_t *header = ((req_header_t *)data);
52 header->context = htonl(context);
53 return vac_write(data, size);
57 govpp_connect(char *shm, int rx_qlen)
59 return vac_connect("govpp", shm, govpp_msg_callback, rx_qlen);
65 return vac_disconnect();
69 govpp_get_msg_index(char *name_and_crc)
71 return vac_get_msg_index(name_and_crc);
84 "git.fd.io/govpp.git/adapter"
85 "github.com/fsnotify/fsnotify"
89 // MaxWaitReady defines maximum duration before waiting for shared memory
91 MaxWaitReady = time.Second * 15
95 // shmDir is a directory where shared memory is supposed to be created.
97 // vppShmFile is a default name of the file in the shmDir.
98 vppShmFile = "vpe-api"
101 // global VPP binary API client, library vppapiclient only supports
102 // single connection at a time
103 var globalVppClient *vppClient
105 // stubVppClient is the default implementation of the VppAPI.
106 type vppClient struct {
108 msgCallback adapter.MsgCallback
109 inputQueueSize uint16
112 // NewVppClient returns a new VPP binary API client.
113 func NewVppClient(shmPrefix string) adapter.VppAPI {
114 return NewVppClientWithInputQueueSize(shmPrefix, 32)
117 // NewVppClientWithInputQueueSize returns a new VPP binary API client with a custom input queue size.
118 func NewVppClientWithInputQueueSize(shmPrefix string, inputQueueSize uint16) adapter.VppAPI {
120 shmPrefix: shmPrefix,
121 inputQueueSize: inputQueueSize,
125 // Connect connects the process to VPP.
126 func (a *vppClient) Connect() error {
127 if globalVppClient != nil {
128 return fmt.Errorf("already connected to binary API, disconnect first")
131 rxQlen := C.int(a.inputQueueSize)
133 if a.shmPrefix == "" {
134 rc = C.govpp_connect(nil, rxQlen)
136 shm := C.CString(a.shmPrefix)
137 rc = C.govpp_connect(shm, rxQlen)
140 return fmt.Errorf("connecting to VPP binary API failed (rc=%v)", rc)
147 // Disconnect disconnects the process from VPP.
148 func (a *vppClient) Disconnect() error {
149 globalVppClient = nil
151 rc := C.govpp_disconnect()
153 return fmt.Errorf("disconnecting from VPP binary API failed (rc=%v)", rc)
159 // GetMsgID returns a runtime message ID for the given message name and CRC.
160 func (a *vppClient) GetMsgID(msgName string, msgCrc string) (uint16, error) {
161 nameAndCrc := C.CString(msgName + "_" + msgCrc)
162 defer C.free(unsafe.Pointer(nameAndCrc))
164 msgID := uint16(C.govpp_get_msg_index(nameAndCrc))
165 if msgID == ^uint16(0) {
166 // VPP does not know this message
167 return msgID, fmt.Errorf("unknown message: %v (crc: %v)", msgName, msgCrc)
173 // SendMsg sends a binary-encoded message to VPP.
174 func (a *vppClient) SendMsg(context uint32, data []byte) error {
175 rc := C.govpp_send(C.uint32_t(context), unsafe.Pointer(&data[0]), C.size_t(len(data)))
177 return fmt.Errorf("unable to send the message (rc=%v)", rc)
182 // SetMsgCallback sets a callback function that will be called by the adapter
183 // whenever a message comes from VPP.
184 func (a *vppClient) SetMsgCallback(cb adapter.MsgCallback) {
188 // WaitReady blocks until shared memory for sending
189 // binary api calls is present on the file system.
190 func (a *vppClient) WaitReady() error {
191 // join the path to the shared memory segment
193 if a.shmPrefix == "" {
194 path = filepath.Join(shmDir, vppShmFile)
196 path = filepath.Join(shmDir, a.shmPrefix+"-"+vppShmFile)
199 // check if file at the path already exists
200 if _, err := os.Stat(path); err == nil {
201 // file exists, we are ready
203 } else if !os.IsNotExist(err) {
207 // file does not exist, start watching folder
208 watcher, err := fsnotify.NewWatcher()
212 defer watcher.Close()
214 // start watching directory
215 if err := watcher.Add(shmDir); err != nil {
221 case <-time.After(MaxWaitReady):
222 return fmt.Errorf("waiting for shared memory segment timed out (%s)", MaxWaitReady)
223 case e := <-watcher.Errors:
225 case ev := <-watcher.Events:
227 if (ev.Op & fsnotify.Create) == fsnotify.Create {
228 // file was created, we are ready
236 //export go_msg_callback
237 func go_msg_callback(msgID C.uint16_t, data unsafe.Pointer, size C.size_t) {
238 // convert unsafe.Pointer to byte slice
239 sliceHeader := &reflect.SliceHeader{Data: uintptr(data), Len: int(size), Cap: int(size)}
240 byteSlice := *(*[]byte)(unsafe.Pointer(sliceHeader))
242 globalVppClient.msgCallback(uint16(msgID), byteSlice)