blob: ab884da3cde311e89d55abdd2e728ad3f0ee966a [file] [log] [blame]
David K. Bainbridge157bdab2020-01-16 14:38:05 -08001/*
2 Copyright 2020 the original author or authors.
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15*/
16
17package openflow
18
19import (
David K. Bainbridge55376262020-01-22 23:28:27 -080020 "bufio"
David K. Bainbridge157bdab2020-01-16 14:38:05 -080021 "context"
22 "encoding/binary"
23 "encoding/json"
24 "errors"
David K. Bainbridge157bdab2020-01-16 14:38:05 -080025 "io"
26 "net"
27 "time"
David Bainbridge20e34e72020-04-08 12:49:41 -070028
29 "github.com/donNewtonAlpha/goloxi"
30 ofp "github.com/donNewtonAlpha/goloxi/of13"
31 "github.com/opencord/ofagent-go/internal/pkg/holder"
32 "github.com/opencord/voltha-lib-go/v3/pkg/log"
33 "github.com/opencord/voltha-protos/v3/go/voltha"
David K. Bainbridge157bdab2020-01-16 14:38:05 -080034)
35
David K. Bainbridge9cb404e2020-01-28 14:32:29 -080036var NoVolthaConnectionError = errors.New("no-voltha-connection")
David K. Bainbridge157bdab2020-01-16 14:38:05 -080037
38type ofcEvent byte
39type ofcState byte
40
41const (
42 ofcEventStart = ofcEvent(iota)
David K. Bainbridge55376262020-01-22 23:28:27 -080043 ofcEventConnect
44 ofcEventDisconnect
45 ofcEventStop
David K. Bainbridge157bdab2020-01-16 14:38:05 -080046
David K. Bainbridge55376262020-01-22 23:28:27 -080047 ofcStateCreated = ofcState(iota)
48 ofcStateStarted
49 ofcStateConnected
David K. Bainbridge157bdab2020-01-16 14:38:05 -080050 ofcStateDisconnected
David K. Bainbridge55376262020-01-22 23:28:27 -080051 ofcStateStopped
David K. Bainbridge157bdab2020-01-16 14:38:05 -080052)
53
David K. Bainbridge55376262020-01-22 23:28:27 -080054func (e ofcEvent) String() string {
55 switch e {
56 case ofcEventStart:
57 return "ofc-event-start"
58 case ofcEventConnect:
59 return "ofc-event-connected"
60 case ofcEventDisconnect:
61 return "ofc-event-disconnected"
62 case ofcEventStop:
63 return "ofc-event-stop"
64 default:
65 return "ofc-event-unknown"
66 }
67}
68
69func (s ofcState) String() string {
70 switch s {
71 case ofcStateCreated:
72 return "ofc-state-created"
73 case ofcStateStarted:
74 return "ofc-state-started"
75 case ofcStateConnected:
76 return "ofc-state-connected"
77 case ofcStateDisconnected:
78 return "ofc-state-disconnected"
79 case ofcStateStopped:
80 return "ofc-state-stopped"
81 default:
82 return "ofc-state-unknown"
83 }
84}
85
86// OFClient the configuration and operational state of a connection to an
87// openflow controller
David K. Bainbridge157bdab2020-01-16 14:38:05 -080088type OFClient struct {
89 OFControllerEndPoint string
90 Port uint16
91 DeviceID string
David Bainbridge20e34e72020-04-08 12:49:41 -070092 VolthaClient *holder.VolthaServiceClientHolder
David K. Bainbridge157bdab2020-01-16 14:38:05 -080093 PacketOutChannel chan *voltha.PacketOut
94 ConnectionMaxRetries int
95 ConnectionRetryDelay time.Duration
96 conn net.Conn
97
98 // expirimental
99 events chan ofcEvent
100 sendChannel chan Message
101 lastUnsentMessage Message
102}
103
David K. Bainbridge55376262020-01-22 23:28:27 -0800104// NewClient returns an initialized OFClient instance based on the configuration
105// specified
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800106func NewOFClient(config *OFClient) *OFClient {
107
108 ofc := OFClient{
109 DeviceID: config.DeviceID,
110 OFControllerEndPoint: config.OFControllerEndPoint,
111 VolthaClient: config.VolthaClient,
112 PacketOutChannel: config.PacketOutChannel,
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800113 ConnectionMaxRetries: config.ConnectionMaxRetries,
114 ConnectionRetryDelay: config.ConnectionRetryDelay,
115 events: make(chan ofcEvent, 10),
116 sendChannel: make(chan Message, 100),
117 }
118
119 if ofc.ConnectionRetryDelay <= 0 {
120 logger.Warnw("connection retry delay not valid, setting to default",
121 log.Fields{
122 "device-id": ofc.DeviceID,
123 "value": ofc.ConnectionRetryDelay.String(),
124 "default": (3 * time.Second).String()})
125 ofc.ConnectionRetryDelay = 3 * time.Second
126 }
127 return &ofc
128}
129
David K. Bainbridge55376262020-01-22 23:28:27 -0800130// Stop initiates a shutdown of the OFClient
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800131func (ofc *OFClient) Stop() {
David K. Bainbridge55376262020-01-22 23:28:27 -0800132 ofc.events <- ofcEventStop
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800133}
134
135func (ofc *OFClient) peekAtOFHeader(buf []byte) (ofp.IHeader, error) {
136 header := ofp.Header{}
137 header.Version = uint8(buf[0])
138 header.Type = uint8(buf[1])
139 header.Length = binary.BigEndian.Uint16(buf[2:4])
140 header.Xid = binary.BigEndian.Uint32(buf[4:8])
141
142 // TODO: add minimal validation of version and type
143
144 return &header, nil
145}
146
147func (ofc *OFClient) establishConnectionToController() error {
148 if ofc.conn != nil {
David K. Bainbridge55376262020-01-22 23:28:27 -0800149 logger.Debugw("closing-of-connection-to-reconnect",
150 log.Fields{"device-id": ofc.DeviceID})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800151 ofc.conn.Close()
152 ofc.conn = nil
153 }
154 try := 1
155 for ofc.ConnectionMaxRetries == 0 || try < ofc.ConnectionMaxRetries {
156 if raddr, err := net.ResolveTCPAddr("tcp", ofc.OFControllerEndPoint); err != nil {
157 logger.Debugw("openflow-client unable to resolve endpoint",
158 log.Fields{
159 "device-id": ofc.DeviceID,
160 "endpoint": ofc.OFControllerEndPoint})
161 } else {
162 if connection, err := net.DialTCP("tcp", nil, raddr); err == nil {
163 ofc.conn = connection
164 ofc.sayHello()
David K. Bainbridge55376262020-01-22 23:28:27 -0800165 ofc.events <- ofcEventConnect
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800166 return nil
167 } else {
168 logger.Warnw("openflow-client-connect-error",
169 log.Fields{
170 "device-id": ofc.DeviceID,
171 "endpoint": ofc.OFControllerEndPoint})
172 }
173 }
174 if ofc.ConnectionMaxRetries == 0 || try < ofc.ConnectionMaxRetries {
175 if ofc.ConnectionMaxRetries != 0 {
176 try += 1
177 }
178 time.Sleep(ofc.ConnectionRetryDelay)
179 }
180 }
181 return errors.New("failed-to-connect-to-of-controller")
182}
183
David K. Bainbridge55376262020-01-22 23:28:27 -0800184// Run implementes the state machine for the OF client reacting to state change
185// events and invoking actions as a reaction to those state changes
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800186func (ofc *OFClient) Run(ctx context.Context) {
187
188 var ofCtx context.Context
189 var ofDone func()
David K. Bainbridge55376262020-01-22 23:28:27 -0800190 state := ofcStateCreated
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800191 ofc.events <- ofcEventStart
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800192top:
193 for {
194 select {
195 case <-ctx.Done():
David K. Bainbridge55376262020-01-22 23:28:27 -0800196 state = ofcStateStopped
197 logger.Debugw("state-transition-context-done",
198 log.Fields{"device-id": ofc.DeviceID})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800199 break top
200 case event := <-ofc.events:
David K. Bainbridge55376262020-01-22 23:28:27 -0800201 previous := state
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800202 switch event {
203 case ofcEventStart:
David K. Bainbridge55376262020-01-22 23:28:27 -0800204 logger.Debugw("ofc-event-start",
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800205 log.Fields{"device-id": ofc.DeviceID})
David K. Bainbridge55376262020-01-22 23:28:27 -0800206 if state == ofcStateCreated {
207 state = ofcStateStarted
208 logger.Debug("STARTED MORE THAN ONCE")
David K. Bainbridgecac73ac2020-02-19 07:00:12 -0800209 go func() {
210 if err := ofc.establishConnectionToController(); err != nil {
211 logger.Errorw("controller-connection-failed", log.Fields{"error": err})
212 panic(err)
213 }
214 }()
David K. Bainbridge55376262020-01-22 23:28:27 -0800215 } else {
216 logger.Errorw("illegal-state-transition",
217 log.Fields{
218 "device-id": ofc.DeviceID,
219 "current-state": state.String(),
220 "event": event.String()})
221 }
222 case ofcEventConnect:
223 logger.Debugw("ofc-event-connected",
224 log.Fields{"device-id": ofc.DeviceID})
225 if state == ofcStateStarted || state == ofcStateDisconnected {
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800226 state = ofcStateConnected
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800227 ofCtx, ofDone = context.WithCancel(context.Background())
228 go ofc.messageSender(ofCtx)
229 go ofc.processOFStream(ofCtx)
David K. Bainbridge55376262020-01-22 23:28:27 -0800230 } else {
231 logger.Errorw("illegal-state-transition",
232 log.Fields{
233 "device-id": ofc.DeviceID,
234 "current-state": state.String(),
235 "event": event.String()})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800236 }
David K. Bainbridge55376262020-01-22 23:28:27 -0800237 case ofcEventDisconnect:
238 logger.Debugw("ofc-event-disconnected",
239 log.Fields{
240 "device-id": ofc.DeviceID,
241 "state": state.String()})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800242 if state == ofcStateConnected {
243 state = ofcStateDisconnected
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800244 if ofDone != nil {
245 ofDone()
246 ofDone = nil
247 }
David K. Bainbridgecac73ac2020-02-19 07:00:12 -0800248 go func() {
249 if err := ofc.establishConnectionToController(); err != nil {
Girish Kumare9d76172020-03-20 20:26:04 +0000250 logger.Errorw("controller-connection-failed", log.Fields{"error": err})
David K. Bainbridgecac73ac2020-02-19 07:00:12 -0800251 panic(err)
252 }
253 }()
David K. Bainbridge55376262020-01-22 23:28:27 -0800254 } else {
255 logger.Errorw("illegal-state-transition",
256 log.Fields{
257 "device-id": ofc.DeviceID,
258 "current-state": state.String(),
259 "event": event.String()})
260 }
261 case ofcEventStop:
262 logger.Debugw("ofc-event-stop",
263 log.Fields{"device-id": ofc.DeviceID})
264 if state == ofcStateCreated || state == ofcStateConnected || state == ofcStateDisconnected {
265 state = ofcStateStopped
266 break top
267 } else {
268 logger.Errorw("illegal-state-transition",
269 log.Fields{
270 "device-id": ofc.DeviceID,
271 "current-state": state.String(),
272 "event": event.String()})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800273 }
274 }
David K. Bainbridge55376262020-01-22 23:28:27 -0800275 logger.Debugw("state-transition",
276 log.Fields{
277 "device-id": ofc.DeviceID,
278 "previous-state": previous.String(),
279 "current-state": state.String(),
280 "event": event.String()})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800281 }
282 }
283
David K. Bainbridge55376262020-01-22 23:28:27 -0800284 // If the child context exists, then cancel it
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800285 if ofDone != nil {
Girish Kumare9d76172020-03-20 20:26:04 +0000286 logger.Debugw("closing-child-processes",
David K. Bainbridge55376262020-01-22 23:28:27 -0800287 log.Fields{"device-id": ofc.DeviceID})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800288 ofDone()
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800289 }
290
David K. Bainbridge55376262020-01-22 23:28:27 -0800291 // If the connection is open, then close it
292 if ofc.conn != nil {
Girish Kumare9d76172020-03-20 20:26:04 +0000293 logger.Debugw("closing-of-connection",
David K. Bainbridge55376262020-01-22 23:28:27 -0800294 log.Fields{"device-id": ofc.DeviceID})
295 ofc.conn.Close()
296 ofc.conn = nil
297 }
Girish Kumare9d76172020-03-20 20:26:04 +0000298 logger.Debugw("state-machine-finished",
David K. Bainbridge55376262020-01-22 23:28:27 -0800299 log.Fields{"device-id": ofc.DeviceID})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800300}
301
David K. Bainbridge55376262020-01-22 23:28:27 -0800302// processOFStream processes the OF connection from the controller and invokes
303// the appropriate handler methods for each message.
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800304func (ofc *OFClient) processOFStream(ctx context.Context) {
David K. Bainbridge55376262020-01-22 23:28:27 -0800305 fromController := bufio.NewReader(ofc.conn)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800306
307 /*
David K. Bainbridge55376262020-01-22 23:28:27 -0800308 * We have a read buffer of a max size of 4096, so if we ever have
309 * a message larger than this then we will have issues
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800310 */
David K. Bainbridgee6d95f62020-01-28 11:11:47 -0800311 headerBuf := make([]byte, 8)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800312
313top:
314 // Continue until we are told to stop
David K. Bainbridge55376262020-01-22 23:28:27 -0800315 for {
316 select {
317 case <-ctx.Done():
318 logger.Error("of-loop-ending-context-done")
319 break top
320 default:
321 // Read 8 bytes, the standard OF header
David K. Bainbridgee6d95f62020-01-28 11:11:47 -0800322 read, err := io.ReadFull(fromController, headerBuf)
David K. Bainbridge55376262020-01-22 23:28:27 -0800323 if err != nil {
324 logger.Errorw("bad-of-header",
325 log.Fields{
326 "byte-count": read,
327 "device-id": ofc.DeviceID,
328 "error": err})
329 break top
330 }
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800331
David K. Bainbridge55376262020-01-22 23:28:27 -0800332 // Decode the header
David K. Bainbridgee6d95f62020-01-28 11:11:47 -0800333 peek, err := ofc.peekAtOFHeader(headerBuf)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800334 if err != nil {
335 /*
336 * Header is bad, assume stream is corrupted
337 * and needs to be restarted
338 */
339 logger.Errorw("bad-of-packet",
340 log.Fields{
341 "device-id": ofc.DeviceID,
342 "error": err})
343 break top
344 }
345
David K. Bainbridge55376262020-01-22 23:28:27 -0800346 // Calculate the size of the rest of the packet and read it
347 need := int(peek.GetLength())
David K. Bainbridgee6d95f62020-01-28 11:11:47 -0800348 messageBuf := make([]byte, need)
349 copy(messageBuf, headerBuf)
350 read, err = io.ReadFull(fromController, messageBuf[8:])
David K. Bainbridge55376262020-01-22 23:28:27 -0800351 if err != nil {
352 logger.Errorw("bad-of-packet",
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800353 log.Fields{
David K. Bainbridge55376262020-01-22 23:28:27 -0800354 "byte-count": read,
355 "device-id": ofc.DeviceID,
356 "error": err})
357 break top
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800358 }
359
360 // Decode and process the packet
David K. Bainbridgee6d95f62020-01-28 11:11:47 -0800361 decoder := goloxi.NewDecoder(messageBuf)
David K. Bainbridge0b3f6482020-01-27 19:32:39 -0800362 msg, err := ofp.DecodeHeader(decoder)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800363 if err != nil {
David K. Bainbridgecac73ac2020-02-19 07:00:12 -0800364 // nolint: staticcheck
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800365 js, _ := json.Marshal(decoder)
366 logger.Errorw("failed-to-decode",
367 log.Fields{
368 "device-id": ofc.DeviceID,
369 "decoder": js,
370 "error": err})
371 break top
372 }
373 if logger.V(log.DebugLevel) {
David K. Bainbridge0b3f6482020-01-27 19:32:39 -0800374 js, _ := json.Marshal(msg)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800375 logger.Debugw("packet-header",
376 log.Fields{
377 "device-id": ofc.DeviceID,
378 "header": js})
379 }
David K. Bainbridge0b3f6482020-01-27 19:32:39 -0800380 ofc.parseHeader(msg)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800381 }
382 }
David K. Bainbridge55376262020-01-22 23:28:27 -0800383 logger.Debugw("end-of-stream",
384 log.Fields{"device-id": ofc.DeviceID})
385 ofc.events <- ofcEventDisconnect
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800386}
387
388func (ofc *OFClient) sayHello() {
389 hello := ofp.NewHello()
390 hello.Xid = uint32(GetXid())
391 elem := ofp.NewHelloElemVersionbitmap()
392 elem.SetType(ofp.OFPHETVersionbitmap)
393 elem.SetLength(8)
Kent Hagerman3243ee52020-02-26 12:11:55 -0500394 elem.SetBitmaps([]*ofp.Uint32{{Value: 16}})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800395 hello.SetElements([]ofp.IHelloElem{elem})
396 if logger.V(log.DebugLevel) {
397 js, _ := json.Marshal(hello)
398 logger.Debugw("sayHello Called",
399 log.Fields{
400 "device-id": ofc.DeviceID,
401 "hello-message": js})
402 }
403 if err := ofc.SendMessage(hello); err != nil {
404 logger.Fatalw("Failed saying hello to Openflow Server, unable to proceed",
405 log.Fields{
406 "device-id": ofc.DeviceID,
407 "error": err})
408 }
409}
410
411func (ofc *OFClient) parseHeader(header ofp.IHeader) {
Andrea Campanella91c4e4e2020-03-05 16:52:06 +0100412 headerType := header.GetType()
413 logger.Debugw("packet-header-type",
414 log.Fields{
415 "header-type": ofp.Type(headerType).String()})
416 switch headerType {
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800417 case ofp.OFPTHello:
418 //x := header.(*ofp.Hello)
419 case ofp.OFPTError:
420 go ofc.handleErrMsg(header.(*ofp.ErrorMsg))
421 case ofp.OFPTEchoRequest:
422 go ofc.handleEchoRequest(header.(*ofp.EchoRequest))
423 case ofp.OFPTEchoReply:
424 case ofp.OFPTExperimenter:
425 case ofp.OFPTFeaturesRequest:
David K. Bainbridgecac73ac2020-02-19 07:00:12 -0800426 go func() {
427 if err := ofc.handleFeatureRequest(header.(*ofp.FeaturesRequest)); err != nil {
428 logger.Errorw("handle-feature-request", log.Fields{"error": err})
429 }
430 }()
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800431 case ofp.OFPTFeaturesReply:
432 case ofp.OFPTGetConfigRequest:
433 go ofc.handleGetConfigRequest(header.(*ofp.GetConfigRequest))
434 case ofp.OFPTGetConfigReply:
435 case ofp.OFPTSetConfig:
436 go ofc.handleSetConfig(header.(*ofp.SetConfig))
437 case ofp.OFPTPacketIn:
438 case ofp.OFPTFlowRemoved:
439 case ofp.OFPTPortStatus:
440 case ofp.OFPTPacketOut:
441 go ofc.handlePacketOut(header.(*ofp.PacketOut))
442 case ofp.OFPTFlowMod:
443 /*
444 * Not using go routine to handle flow* messages or barrier requests
445 * onos typically issues barrier requests just before a flow* message.
446 * by handling in this thread I ensure all flow* are handled when barrier
447 * request is issued.
448 */
449 switch header.(ofp.IFlowMod).GetCommand() {
450 case ofp.OFPFCAdd:
451 ofc.handleFlowAdd(header.(*ofp.FlowAdd))
452 case ofp.OFPFCModify:
453 ofc.handleFlowMod(header.(*ofp.FlowMod))
454 case ofp.OFPFCModifyStrict:
455 ofc.handleFlowModStrict(header.(*ofp.FlowModifyStrict))
456 case ofp.OFPFCDelete:
457 ofc.handleFlowDelete(header.(*ofp.FlowDelete))
458 case ofp.OFPFCDeleteStrict:
459 ofc.handleFlowDeleteStrict(header.(*ofp.FlowDeleteStrict))
460 }
461 case ofp.OFPTStatsRequest:
David K. Bainbridgecac73ac2020-02-19 07:00:12 -0800462 go func() {
463 if err := ofc.handleStatsRequest(header, header.(ofp.IStatsRequest).GetStatsType()); err != nil {
464 logger.Errorw("ofpt-stats-request", log.Fields{"error": err})
465 }
466 }()
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800467 case ofp.OFPTBarrierRequest:
468 /* See note above at case ofp.OFPTFlowMod:*/
469 ofc.handleBarrierRequest(header.(*ofp.BarrierRequest))
470 case ofp.OFPTRoleRequest:
471 go ofc.handleRoleRequest(header.(*ofp.RoleRequest))
472 case ofp.OFPTMeterMod:
Don Newtonac0455d2020-01-23 11:52:26 -0500473 ofc.handleMeterModRequest(header.(*ofp.MeterMod))
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800474 }
475}
476
David K. Bainbridge55376262020-01-22 23:28:27 -0800477// Message interface that represents an open flow message and enables for a
478// unified implementation of SendMessage
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800479type Message interface {
480 Serialize(encoder *goloxi.Encoder) error
481}
482
483func (ofc *OFClient) doSend(msg Message) error {
484 if ofc.conn == nil {
485 return errors.New("no-connection")
486 }
487 enc := goloxi.NewEncoder()
David K. Bainbridgecac73ac2020-02-19 07:00:12 -0800488 if err := msg.Serialize(enc); err != nil {
489 return err
490 }
491
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800492 bytes := enc.Bytes()
493 if _, err := ofc.conn.Write(bytes); err != nil {
David K. Bainbridge55376262020-01-22 23:28:27 -0800494 logger.Errorw("unable-to-send-message-to-controller",
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800495 log.Fields{
496 "device-id": ofc.DeviceID,
497 "message": msg,
498 "error": err})
499 return err
500 }
501 return nil
502}
503
504func (ofc *OFClient) messageSender(ctx context.Context) {
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800505 // first process last fail if it exists
506 if ofc.lastUnsentMessage != nil {
507 if err := ofc.doSend(ofc.lastUnsentMessage); err != nil {
David K. Bainbridge55376262020-01-22 23:28:27 -0800508 ofc.events <- ofcEventDisconnect
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800509 return
510 }
511 ofc.lastUnsentMessage = nil
512 }
513top:
514 for {
515 select {
516 case <-ctx.Done():
517 break top
518 case msg := <-ofc.sendChannel:
David K. Bainbridge55376262020-01-22 23:28:27 -0800519 if err := ofc.doSend(msg); err != nil {
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800520 ofc.lastUnsentMessage = msg
David K. Bainbridge55376262020-01-22 23:28:27 -0800521 ofc.events <- ofcEventDisconnect
David K. Bainbridge9cb404e2020-01-28 14:32:29 -0800522 logger.Debugw("message-sender-error",
523 log.Fields{
524 "device-id": ofc.DeviceID,
525 "error": err.Error()})
526 break top
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800527 }
David K. Bainbridge9cb404e2020-01-28 14:32:29 -0800528 logger.Debugw("message-sender-send",
529 log.Fields{
530 "device-id": ofc.DeviceID})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800531 ofc.lastUnsentMessage = nil
532 }
533 }
David K. Bainbridge9cb404e2020-01-28 14:32:29 -0800534
535 logger.Debugw("message-sender-finished",
536 log.Fields{
537 "device-id": ofc.DeviceID})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800538}
539
David K. Bainbridge55376262020-01-22 23:28:27 -0800540// SendMessage queues a message to be sent to the openflow controller
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800541func (ofc *OFClient) SendMessage(message Message) error {
David K. Bainbridge9cb404e2020-01-28 14:32:29 -0800542 logger.Debug("queuing-message")
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800543 ofc.sendChannel <- message
544 return nil
545}