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
17 // Package vppapiclient is the default VPP adapter being used for the connection with VPP via shared memory.
18 // It is based on the communication with the vppapiclient VPP library written in C via CGO.
22 #cgo CFLAGS: -DPNG_DEBUG=1
23 #cgo LDFLAGS: -lvppapiclient
28 #include <arpa/inet.h>
29 #include <vpp-api/client/vppapiclient.h>
31 extern void go_msg_callback(uint16_t, uint32_t, void*, size_t);
33 typedef struct __attribute__((__packed__)) _req_header {
35 uint32_t client_index;
39 typedef struct __attribute__((__packed__)) _reply_header {
45 govpp_msg_callback (unsigned char *data, int size)
47 reply_header_t *header = ((reply_header_t *)data);
48 go_msg_callback(ntohs(header->msg_id), ntohl(header->context), data, size);
54 return vac_connect("govpp", NULL, govpp_msg_callback, 32);
60 return vac_disconnect();
64 govpp_send(uint32_t context, void *data, size_t size)
66 req_header_t *header = ((req_header_t *)data);
67 header->context = htonl(context);
68 return vac_write(data, size);
72 govpp_get_msg_index(char *name_and_crc)
74 return vac_get_msg_index(name_and_crc);
86 "git.fd.io/govpp.git/adapter"
87 "github.com/fsnotify/fsnotify"
91 // watchedFolder is a folder where vpp's shared memory is supposed to be created.
92 // File system events are monitored in this folder.
93 watchedFolder = "/dev/shm/"
94 // watchedFile is a name of the file in the watchedFolder. Once the file is present
95 // the vpp is ready to accept a new connection.
96 watchedFile = watchedFolder + "vpe-api"
99 // vppAPIClientAdapter is the opaque context of the adapter.
100 type vppAPIClientAdapter struct {
101 callback func(context uint32, msgId uint16, data []byte)
104 var vppClient *vppAPIClientAdapter // global vpp API client adapter context
106 // NewVppAdapter returns a new vpp API client adapter.
107 func NewVppAdapter() adapter.VppAdapter {
108 return &vppAPIClientAdapter{}
111 // Connect connects the process to VPP.
112 func (a *vppAPIClientAdapter) Connect() error {
114 rc := C.govpp_connect()
116 return fmt.Errorf("unable to connect to VPP (error=%d)", rc)
121 // Disconnect disconnects the process from VPP.
122 func (a *vppAPIClientAdapter) Disconnect() {
126 // GetMsgID returns a runtime message ID for the given message name and CRC.
127 func (a *vppAPIClientAdapter) GetMsgID(msgName string, msgCrc string) (uint16, error) {
128 nameAndCrc := C.CString(fmt.Sprintf("%s_%s", msgName, msgCrc))
129 defer C.free(unsafe.Pointer(nameAndCrc))
131 msgID := uint16(C.govpp_get_msg_index(nameAndCrc))
132 if msgID == ^uint16(0) {
133 return msgID, errors.New("unkonwn message")
139 // SendMsg sends a binary-encoded message to VPP.
140 func (a *vppAPIClientAdapter) SendMsg(clientID uint32, data []byte) error {
141 rc := C.govpp_send(C.uint32_t(clientID), unsafe.Pointer(&data[0]), C.size_t(len(data)))
143 return fmt.Errorf("unable to send the message (error=%d)", rc)
148 // SetMsgCallback sets a callback function that will be called by the adapter whenever a message comes from VPP.
149 func (a *vppAPIClientAdapter) SetMsgCallback(cb func(context uint32, msgID uint16, data []byte)) {
153 // WaitReady returns func which blocks until shared memory
154 // for sending bin api calls is present on the file system.
155 func (a *vppAPIClientAdapter) WaitReady() func() error {
156 return func() error {
157 watcher, err := fsnotify.NewWatcher()
161 defer watcher.Close()
163 err = watcher.Add(watchedFolder)
168 if fileExists(watchedFile) {
173 ev := <-watcher.Events
174 if ev.Name == watchedFile && (ev.Op&fsnotify.Create) == fsnotify.Create {
182 func fileExists(name string) bool {
183 if _, err := os.Stat(name); err != nil {
184 if os.IsNotExist(err) {
191 //export go_msg_callback
192 func go_msg_callback(msgID C.uint16_t, context C.uint32_t, data unsafe.Pointer, size C.size_t) {
193 // convert unsafe.Pointer to byte slice
194 slice := &reflect.SliceHeader{Data: uintptr(data), Len: int(size), Cap: int(size)}
195 byteArr := *(*[]byte)(unsafe.Pointer(slice))
197 vppClient.callback(uint32(context), uint16(msgID), byteArr)