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.
22 logger "github.com/sirupsen/logrus"
24 "git.fd.io/govpp.git/api"
28 ErrNotConnected = errors.New("not connected to VPP, ignoring the request")
31 // watchRequests watches for requests on the request API channel and forwards them as messages to VPP.
32 func (c *Connection) watchRequests(ch *api.Channel, chMeta *channelMetadata) {
35 case req, ok := <-ch.ReqChan:
36 // new request on the request channel
38 // after closing the request channel, release API channel and return
39 c.releaseAPIChannel(ch, chMeta)
42 c.processRequest(ch, chMeta, req)
44 case req := <-ch.NotifSubsChan:
45 // new request on the notification subscribe channel
46 c.processNotifSubscribeRequest(ch, req)
51 // processRequest processes a single request received on the request channel.
52 func (c *Connection) processRequest(ch *api.Channel, chMeta *channelMetadata, req *api.VppRequest) error {
53 // check whether we are connected to VPP
54 if atomic.LoadUint32(&c.connected) == 0 {
55 err := ErrNotConnected
57 sendReply(ch, &api.VppReply{Error: err})
61 // retrieve message ID
62 msgID, err := c.GetMessageID(req.Message)
64 err = fmt.Errorf("unable to retrieve message ID: %v", err)
65 log.WithFields(logger.Fields{
66 "msg_name": req.Message.GetMessageName(),
67 "msg_crc": req.Message.GetCrcString(),
69 sendReply(ch, &api.VppReply{Error: err})
73 // encode the message into binary
74 data, err := c.codec.EncodeMsg(req.Message, msgID)
76 err = fmt.Errorf("unable to encode the messge: %v", err)
77 log.WithFields(logger.Fields{
81 sendReply(ch, &api.VppReply{Error: err})
85 if log.Level == logger.DebugLevel { // for performance reasons - logrus does some processing even if debugs are disabled
86 log.WithFields(logger.Fields{
89 "msg_size": len(data),
90 "msg_name": req.Message.GetMessageName(),
91 }).Debug("Sending a message to VPP.")
96 // expect multipart response
97 atomic.StoreUint32(&chMeta.multipart, 1)
100 // send the request to VPP
101 c.vpp.SendMsg(chMeta.id, data)
104 // send a control ping to determine end of the multipart response
105 pingData, _ := c.codec.EncodeMsg(msgControlPing, c.pingReqID)
107 log.WithFields(logger.Fields{
108 "context": chMeta.id,
109 "msg_id": c.pingReqID,
110 "msg_size": len(pingData),
111 }).Debug("Sending a control ping to VPP.")
113 c.vpp.SendMsg(chMeta.id, pingData)
119 // msgCallback is called whenever any binary API message comes from VPP.
120 func msgCallback(context uint32, msgID uint16, data []byte) {
122 defer connLock.RUnlock()
125 log.Warn("Already disconnected, ignoring the message.")
129 if log.Level == logger.DebugLevel { // for performance reasons - logrus does some processing even if debugs are disabled
130 log.WithFields(logger.Fields{
133 "msg_size": len(data),
134 }).Debug("Received a message from VPP.")
137 if context == 0 || conn.isNotificationMessage(msgID) {
138 // process the message as a notification
139 conn.sendNotifications(msgID, data)
143 // match ch according to the context
144 conn.channelsLock.RLock()
145 ch, ok := conn.channels[context]
146 conn.channelsLock.RUnlock()
149 log.WithFields(logger.Fields{
152 }).Error("Context ID not known, ignoring the message.")
156 chMeta := ch.Metadata().(*channelMetadata)
157 lastReplyReceived := false
158 // if this is a control ping reply and multipart request is being processed, treat this as a last part of the reply
159 if msgID == conn.pingReplyID && atomic.CompareAndSwapUint32(&chMeta.multipart, 1, 0) {
160 lastReplyReceived = true
163 // send the data to the channel
164 sendReply(ch, &api.VppReply{
167 LastReplyReceived: lastReplyReceived,
171 // sendReply sends the reply into the go channel, if it cannot be completed without blocking, otherwise
172 // it logs the error and do not send the message.
173 func sendReply(ch *api.Channel, reply *api.VppReply) {
175 case ch.ReplyChan <- reply:
176 // reply sent successfully
178 // unable to write into the channel without blocking
179 log.WithFields(logger.Fields{
181 "msg_id": reply.MessageID,
182 }).Warn("Unable to send the reply, reciever end not ready.")
186 // GetMessageID returns message identifier of given API message.
187 func (c *Connection) GetMessageID(msg api.Message) (uint16, error) {
189 return 0, errors.New("nil connection passed in")
191 return c.messageNameToID(msg.GetMessageName(), msg.GetCrcString())
194 // messageNameToID returns message ID of a message identified by its name and CRC.
195 func (c *Connection) messageNameToID(msgName string, msgCrc string) (uint16, error) {
196 msgKey := msgName + "_" + msgCrc
198 // try to get the ID from the map
200 id, ok := c.msgIDs[msgKey]
201 c.msgIDsLock.RUnlock()
206 // get the ID using VPP API
207 id, err := c.vpp.GetMsgID(msgName, msgCrc)
209 err = fmt.Errorf("unable to retrieve message ID: %v", err)
210 log.WithFields(logger.Fields{
218 c.msgIDs[msgKey] = id
219 c.msgIDsLock.Unlock()
224 // LookupByID looks up message name and crc by ID.
225 func (c *Connection) LookupByID(ID uint16) (string, error) {
227 return "", errors.New("nil connection passed in")
231 defer c.msgIDsLock.Unlock()
233 for key, id := range c.msgIDs {
239 return "", fmt.Errorf("unknown message ID: %d", ID)