blob: d92140080e29ce8fc5a8c7ac63d0fb79e9339c26 [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"
25 "github.com/donNewtonAlpha/goloxi"
26 ofp "github.com/donNewtonAlpha/goloxi/of13"
David K. Bainbridgeaea73cd2020-01-27 10:44:50 -080027 "github.com/opencord/voltha-lib-go/v3/pkg/log"
28 "github.com/opencord/voltha-protos/v3/go/voltha"
David K. Bainbridge157bdab2020-01-16 14:38:05 -080029 "io"
30 "net"
31 "time"
32)
33
34var logger, _ = log.AddPackage(log.JSON, log.DebugLevel, nil)
David K. Bainbridge9cb404e2020-01-28 14:32:29 -080035var NoVolthaConnectionError = errors.New("no-voltha-connection")
David K. Bainbridge157bdab2020-01-16 14:38:05 -080036
37type ofcEvent byte
38type ofcState byte
39
40const (
41 ofcEventStart = ofcEvent(iota)
David K. Bainbridge55376262020-01-22 23:28:27 -080042 ofcEventConnect
43 ofcEventDisconnect
44 ofcEventStop
David K. Bainbridge157bdab2020-01-16 14:38:05 -080045
David K. Bainbridge55376262020-01-22 23:28:27 -080046 ofcStateCreated = ofcState(iota)
47 ofcStateStarted
48 ofcStateConnected
David K. Bainbridge157bdab2020-01-16 14:38:05 -080049 ofcStateDisconnected
David K. Bainbridge55376262020-01-22 23:28:27 -080050 ofcStateStopped
David K. Bainbridge157bdab2020-01-16 14:38:05 -080051)
52
David K. Bainbridge55376262020-01-22 23:28:27 -080053func (e ofcEvent) String() string {
54 switch e {
55 case ofcEventStart:
56 return "ofc-event-start"
57 case ofcEventConnect:
58 return "ofc-event-connected"
59 case ofcEventDisconnect:
60 return "ofc-event-disconnected"
61 case ofcEventStop:
62 return "ofc-event-stop"
63 default:
64 return "ofc-event-unknown"
65 }
66}
67
68func (s ofcState) String() string {
69 switch s {
70 case ofcStateCreated:
71 return "ofc-state-created"
72 case ofcStateStarted:
73 return "ofc-state-started"
74 case ofcStateConnected:
75 return "ofc-state-connected"
76 case ofcStateDisconnected:
77 return "ofc-state-disconnected"
78 case ofcStateStopped:
79 return "ofc-state-stopped"
80 default:
81 return "ofc-state-unknown"
82 }
83}
84
85// OFClient the configuration and operational state of a connection to an
86// openflow controller
David K. Bainbridge157bdab2020-01-16 14:38:05 -080087type OFClient struct {
88 OFControllerEndPoint string
89 Port uint16
90 DeviceID string
David K. Bainbridge157bdab2020-01-16 14:38:05 -080091 VolthaClient voltha.VolthaServiceClient
92 PacketOutChannel chan *voltha.PacketOut
93 ConnectionMaxRetries int
94 ConnectionRetryDelay time.Duration
95 conn net.Conn
96
97 // expirimental
98 events chan ofcEvent
99 sendChannel chan Message
100 lastUnsentMessage Message
101}
102
David K. Bainbridge55376262020-01-22 23:28:27 -0800103// NewClient returns an initialized OFClient instance based on the configuration
104// specified
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800105func NewOFClient(config *OFClient) *OFClient {
106
107 ofc := OFClient{
108 DeviceID: config.DeviceID,
109 OFControllerEndPoint: config.OFControllerEndPoint,
110 VolthaClient: config.VolthaClient,
111 PacketOutChannel: config.PacketOutChannel,
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800112 ConnectionMaxRetries: config.ConnectionMaxRetries,
113 ConnectionRetryDelay: config.ConnectionRetryDelay,
114 events: make(chan ofcEvent, 10),
115 sendChannel: make(chan Message, 100),
116 }
117
118 if ofc.ConnectionRetryDelay <= 0 {
119 logger.Warnw("connection retry delay not valid, setting to default",
120 log.Fields{
121 "device-id": ofc.DeviceID,
122 "value": ofc.ConnectionRetryDelay.String(),
123 "default": (3 * time.Second).String()})
124 ofc.ConnectionRetryDelay = 3 * time.Second
125 }
126 return &ofc
127}
128
David K. Bainbridge55376262020-01-22 23:28:27 -0800129// Stop initiates a shutdown of the OFClient
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800130func (ofc *OFClient) Stop() {
David K. Bainbridge55376262020-01-22 23:28:27 -0800131 ofc.events <- ofcEventStop
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800132}
133
134func (ofc *OFClient) peekAtOFHeader(buf []byte) (ofp.IHeader, error) {
135 header := ofp.Header{}
136 header.Version = uint8(buf[0])
137 header.Type = uint8(buf[1])
138 header.Length = binary.BigEndian.Uint16(buf[2:4])
139 header.Xid = binary.BigEndian.Uint32(buf[4:8])
140
141 // TODO: add minimal validation of version and type
142
143 return &header, nil
144}
145
146func (ofc *OFClient) establishConnectionToController() error {
147 if ofc.conn != nil {
David K. Bainbridge55376262020-01-22 23:28:27 -0800148 logger.Debugw("closing-of-connection-to-reconnect",
149 log.Fields{"device-id": ofc.DeviceID})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800150 ofc.conn.Close()
151 ofc.conn = nil
152 }
153 try := 1
154 for ofc.ConnectionMaxRetries == 0 || try < ofc.ConnectionMaxRetries {
155 if raddr, err := net.ResolveTCPAddr("tcp", ofc.OFControllerEndPoint); err != nil {
156 logger.Debugw("openflow-client unable to resolve endpoint",
157 log.Fields{
158 "device-id": ofc.DeviceID,
159 "endpoint": ofc.OFControllerEndPoint})
160 } else {
161 if connection, err := net.DialTCP("tcp", nil, raddr); err == nil {
162 ofc.conn = connection
163 ofc.sayHello()
David K. Bainbridge55376262020-01-22 23:28:27 -0800164 ofc.events <- ofcEventConnect
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800165 return nil
166 } else {
167 logger.Warnw("openflow-client-connect-error",
168 log.Fields{
169 "device-id": ofc.DeviceID,
170 "endpoint": ofc.OFControllerEndPoint})
171 }
172 }
173 if ofc.ConnectionMaxRetries == 0 || try < ofc.ConnectionMaxRetries {
174 if ofc.ConnectionMaxRetries != 0 {
175 try += 1
176 }
177 time.Sleep(ofc.ConnectionRetryDelay)
178 }
179 }
180 return errors.New("failed-to-connect-to-of-controller")
181}
182
David K. Bainbridge55376262020-01-22 23:28:27 -0800183// Run implementes the state machine for the OF client reacting to state change
184// events and invoking actions as a reaction to those state changes
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800185func (ofc *OFClient) Run(ctx context.Context) {
186
187 var ofCtx context.Context
188 var ofDone func()
David K. Bainbridge55376262020-01-22 23:28:27 -0800189 state := ofcStateCreated
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800190 ofc.events <- ofcEventStart
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800191top:
192 for {
193 select {
194 case <-ctx.Done():
David K. Bainbridge55376262020-01-22 23:28:27 -0800195 state = ofcStateStopped
196 logger.Debugw("state-transition-context-done",
197 log.Fields{"device-id": ofc.DeviceID})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800198 break top
199 case event := <-ofc.events:
David K. Bainbridge55376262020-01-22 23:28:27 -0800200 previous := state
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800201 switch event {
202 case ofcEventStart:
David K. Bainbridge55376262020-01-22 23:28:27 -0800203 logger.Debugw("ofc-event-start",
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800204 log.Fields{"device-id": ofc.DeviceID})
David K. Bainbridge55376262020-01-22 23:28:27 -0800205 if state == ofcStateCreated {
206 state = ofcStateStarted
207 logger.Debug("STARTED MORE THAN ONCE")
David K. Bainbridgecac73ac2020-02-19 07:00:12 -0800208 go func() {
209 if err := ofc.establishConnectionToController(); err != nil {
210 logger.Errorw("controller-connection-failed", log.Fields{"error": err})
211 panic(err)
212 }
213 }()
David K. Bainbridge55376262020-01-22 23:28:27 -0800214 } else {
215 logger.Errorw("illegal-state-transition",
216 log.Fields{
217 "device-id": ofc.DeviceID,
218 "current-state": state.String(),
219 "event": event.String()})
220 }
221 case ofcEventConnect:
222 logger.Debugw("ofc-event-connected",
223 log.Fields{"device-id": ofc.DeviceID})
224 if state == ofcStateStarted || state == ofcStateDisconnected {
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800225 state = ofcStateConnected
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800226 ofCtx, ofDone = context.WithCancel(context.Background())
227 go ofc.messageSender(ofCtx)
228 go ofc.processOFStream(ofCtx)
David K. Bainbridge55376262020-01-22 23:28:27 -0800229 } else {
230 logger.Errorw("illegal-state-transition",
231 log.Fields{
232 "device-id": ofc.DeviceID,
233 "current-state": state.String(),
234 "event": event.String()})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800235 }
David K. Bainbridge55376262020-01-22 23:28:27 -0800236 case ofcEventDisconnect:
237 logger.Debugw("ofc-event-disconnected",
238 log.Fields{
239 "device-id": ofc.DeviceID,
240 "state": state.String()})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800241 if state == ofcStateConnected {
242 state = ofcStateDisconnected
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800243 if ofDone != nil {
244 ofDone()
245 ofDone = nil
246 }
David K. Bainbridgecac73ac2020-02-19 07:00:12 -0800247 go func() {
248 if err := ofc.establishConnectionToController(); err != nil {
249 log.Errorw("controller-connection-failed", log.Fields{"error": err})
250 panic(err)
251 }
252 }()
David K. Bainbridge55376262020-01-22 23:28:27 -0800253 } else {
254 logger.Errorw("illegal-state-transition",
255 log.Fields{
256 "device-id": ofc.DeviceID,
257 "current-state": state.String(),
258 "event": event.String()})
259 }
260 case ofcEventStop:
261 logger.Debugw("ofc-event-stop",
262 log.Fields{"device-id": ofc.DeviceID})
263 if state == ofcStateCreated || state == ofcStateConnected || state == ofcStateDisconnected {
264 state = ofcStateStopped
265 break top
266 } else {
267 logger.Errorw("illegal-state-transition",
268 log.Fields{
269 "device-id": ofc.DeviceID,
270 "current-state": state.String(),
271 "event": event.String()})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800272 }
273 }
David K. Bainbridge55376262020-01-22 23:28:27 -0800274 logger.Debugw("state-transition",
275 log.Fields{
276 "device-id": ofc.DeviceID,
277 "previous-state": previous.String(),
278 "current-state": state.String(),
279 "event": event.String()})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800280 }
281 }
282
David K. Bainbridge55376262020-01-22 23:28:27 -0800283 // If the child context exists, then cancel it
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800284 if ofDone != nil {
David K. Bainbridge55376262020-01-22 23:28:27 -0800285 log.Debugw("closing-child-processes",
286 log.Fields{"device-id": ofc.DeviceID})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800287 ofDone()
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800288 }
289
David K. Bainbridge55376262020-01-22 23:28:27 -0800290 // If the connection is open, then close it
291 if ofc.conn != nil {
292 log.Debugw("closing-of-connection",
293 log.Fields{"device-id": ofc.DeviceID})
294 ofc.conn.Close()
295 ofc.conn = nil
296 }
297 log.Debugw("state-machine-finished",
298 log.Fields{"device-id": ofc.DeviceID})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800299}
300
David K. Bainbridge55376262020-01-22 23:28:27 -0800301// processOFStream processes the OF connection from the controller and invokes
302// the appropriate handler methods for each message.
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800303func (ofc *OFClient) processOFStream(ctx context.Context) {
David K. Bainbridge55376262020-01-22 23:28:27 -0800304 fromController := bufio.NewReader(ofc.conn)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800305
306 /*
David K. Bainbridge55376262020-01-22 23:28:27 -0800307 * We have a read buffer of a max size of 4096, so if we ever have
308 * a message larger than this then we will have issues
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800309 */
David K. Bainbridgee6d95f62020-01-28 11:11:47 -0800310 headerBuf := make([]byte, 8)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800311
312top:
313 // Continue until we are told to stop
David K. Bainbridge55376262020-01-22 23:28:27 -0800314 for {
315 select {
316 case <-ctx.Done():
317 logger.Error("of-loop-ending-context-done")
318 break top
319 default:
320 // Read 8 bytes, the standard OF header
David K. Bainbridgee6d95f62020-01-28 11:11:47 -0800321 read, err := io.ReadFull(fromController, headerBuf)
David K. Bainbridge55376262020-01-22 23:28:27 -0800322 if err != nil {
323 logger.Errorw("bad-of-header",
324 log.Fields{
325 "byte-count": read,
326 "device-id": ofc.DeviceID,
327 "error": err})
328 break top
329 }
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800330
David K. Bainbridge55376262020-01-22 23:28:27 -0800331 // Decode the header
David K. Bainbridgee6d95f62020-01-28 11:11:47 -0800332 peek, err := ofc.peekAtOFHeader(headerBuf)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800333 if err != nil {
334 /*
335 * Header is bad, assume stream is corrupted
336 * and needs to be restarted
337 */
338 logger.Errorw("bad-of-packet",
339 log.Fields{
340 "device-id": ofc.DeviceID,
341 "error": err})
342 break top
343 }
344
David K. Bainbridge55376262020-01-22 23:28:27 -0800345 // Calculate the size of the rest of the packet and read it
346 need := int(peek.GetLength())
David K. Bainbridgee6d95f62020-01-28 11:11:47 -0800347 messageBuf := make([]byte, need)
348 copy(messageBuf, headerBuf)
349 read, err = io.ReadFull(fromController, messageBuf[8:])
David K. Bainbridge55376262020-01-22 23:28:27 -0800350 if err != nil {
351 logger.Errorw("bad-of-packet",
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800352 log.Fields{
David K. Bainbridge55376262020-01-22 23:28:27 -0800353 "byte-count": read,
354 "device-id": ofc.DeviceID,
355 "error": err})
356 break top
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800357 }
358
359 // Decode and process the packet
David K. Bainbridgee6d95f62020-01-28 11:11:47 -0800360 decoder := goloxi.NewDecoder(messageBuf)
David K. Bainbridge0b3f6482020-01-27 19:32:39 -0800361 msg, err := ofp.DecodeHeader(decoder)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800362 if err != nil {
David K. Bainbridgecac73ac2020-02-19 07:00:12 -0800363 // nolint: staticcheck
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800364 js, _ := json.Marshal(decoder)
365 logger.Errorw("failed-to-decode",
366 log.Fields{
367 "device-id": ofc.DeviceID,
368 "decoder": js,
369 "error": err})
370 break top
371 }
372 if logger.V(log.DebugLevel) {
David K. Bainbridge0b3f6482020-01-27 19:32:39 -0800373 js, _ := json.Marshal(msg)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800374 logger.Debugw("packet-header",
375 log.Fields{
376 "device-id": ofc.DeviceID,
377 "header": js})
378 }
David K. Bainbridge0b3f6482020-01-27 19:32:39 -0800379 ofc.parseHeader(msg)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800380 }
381 }
David K. Bainbridge55376262020-01-22 23:28:27 -0800382 logger.Debugw("end-of-stream",
383 log.Fields{"device-id": ofc.DeviceID})
384 ofc.events <- ofcEventDisconnect
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800385}
386
387func (ofc *OFClient) sayHello() {
388 hello := ofp.NewHello()
389 hello.Xid = uint32(GetXid())
390 elem := ofp.NewHelloElemVersionbitmap()
391 elem.SetType(ofp.OFPHETVersionbitmap)
392 elem.SetLength(8)
393 elem.SetBitmaps([]*ofp.Uint32{&ofp.Uint32{Value: 16}})
394 hello.SetElements([]ofp.IHelloElem{elem})
395 if logger.V(log.DebugLevel) {
396 js, _ := json.Marshal(hello)
397 logger.Debugw("sayHello Called",
398 log.Fields{
399 "device-id": ofc.DeviceID,
400 "hello-message": js})
401 }
402 if err := ofc.SendMessage(hello); err != nil {
403 logger.Fatalw("Failed saying hello to Openflow Server, unable to proceed",
404 log.Fields{
405 "device-id": ofc.DeviceID,
406 "error": err})
407 }
408}
409
410func (ofc *OFClient) parseHeader(header ofp.IHeader) {
411 switch header.GetType() {
412 case ofp.OFPTHello:
413 //x := header.(*ofp.Hello)
414 case ofp.OFPTError:
415 go ofc.handleErrMsg(header.(*ofp.ErrorMsg))
416 case ofp.OFPTEchoRequest:
417 go ofc.handleEchoRequest(header.(*ofp.EchoRequest))
418 case ofp.OFPTEchoReply:
419 case ofp.OFPTExperimenter:
420 case ofp.OFPTFeaturesRequest:
David K. Bainbridgecac73ac2020-02-19 07:00:12 -0800421 go func() {
422 if err := ofc.handleFeatureRequest(header.(*ofp.FeaturesRequest)); err != nil {
423 logger.Errorw("handle-feature-request", log.Fields{"error": err})
424 }
425 }()
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800426 case ofp.OFPTFeaturesReply:
427 case ofp.OFPTGetConfigRequest:
428 go ofc.handleGetConfigRequest(header.(*ofp.GetConfigRequest))
429 case ofp.OFPTGetConfigReply:
430 case ofp.OFPTSetConfig:
431 go ofc.handleSetConfig(header.(*ofp.SetConfig))
432 case ofp.OFPTPacketIn:
433 case ofp.OFPTFlowRemoved:
434 case ofp.OFPTPortStatus:
435 case ofp.OFPTPacketOut:
436 go ofc.handlePacketOut(header.(*ofp.PacketOut))
437 case ofp.OFPTFlowMod:
438 /*
439 * Not using go routine to handle flow* messages or barrier requests
440 * onos typically issues barrier requests just before a flow* message.
441 * by handling in this thread I ensure all flow* are handled when barrier
442 * request is issued.
443 */
444 switch header.(ofp.IFlowMod).GetCommand() {
445 case ofp.OFPFCAdd:
446 ofc.handleFlowAdd(header.(*ofp.FlowAdd))
447 case ofp.OFPFCModify:
448 ofc.handleFlowMod(header.(*ofp.FlowMod))
449 case ofp.OFPFCModifyStrict:
450 ofc.handleFlowModStrict(header.(*ofp.FlowModifyStrict))
451 case ofp.OFPFCDelete:
452 ofc.handleFlowDelete(header.(*ofp.FlowDelete))
453 case ofp.OFPFCDeleteStrict:
454 ofc.handleFlowDeleteStrict(header.(*ofp.FlowDeleteStrict))
455 }
456 case ofp.OFPTStatsRequest:
David K. Bainbridgecac73ac2020-02-19 07:00:12 -0800457 go func() {
458 if err := ofc.handleStatsRequest(header, header.(ofp.IStatsRequest).GetStatsType()); err != nil {
459 logger.Errorw("ofpt-stats-request", log.Fields{"error": err})
460 }
461 }()
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800462 case ofp.OFPTBarrierRequest:
463 /* See note above at case ofp.OFPTFlowMod:*/
464 ofc.handleBarrierRequest(header.(*ofp.BarrierRequest))
465 case ofp.OFPTRoleRequest:
466 go ofc.handleRoleRequest(header.(*ofp.RoleRequest))
467 case ofp.OFPTMeterMod:
Don Newtonac0455d2020-01-23 11:52:26 -0500468 ofc.handleMeterModRequest(header.(*ofp.MeterMod))
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800469 }
470}
471
David K. Bainbridge55376262020-01-22 23:28:27 -0800472// Message interface that represents an open flow message and enables for a
473// unified implementation of SendMessage
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800474type Message interface {
475 Serialize(encoder *goloxi.Encoder) error
476}
477
478func (ofc *OFClient) doSend(msg Message) error {
479 if ofc.conn == nil {
480 return errors.New("no-connection")
481 }
482 enc := goloxi.NewEncoder()
David K. Bainbridgecac73ac2020-02-19 07:00:12 -0800483 if err := msg.Serialize(enc); err != nil {
484 return err
485 }
486
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800487 bytes := enc.Bytes()
488 if _, err := ofc.conn.Write(bytes); err != nil {
David K. Bainbridge55376262020-01-22 23:28:27 -0800489 logger.Errorw("unable-to-send-message-to-controller",
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800490 log.Fields{
491 "device-id": ofc.DeviceID,
492 "message": msg,
493 "error": err})
494 return err
495 }
496 return nil
497}
498
499func (ofc *OFClient) messageSender(ctx context.Context) {
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800500 // first process last fail if it exists
501 if ofc.lastUnsentMessage != nil {
502 if err := ofc.doSend(ofc.lastUnsentMessage); err != nil {
David K. Bainbridge55376262020-01-22 23:28:27 -0800503 ofc.events <- ofcEventDisconnect
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800504 return
505 }
506 ofc.lastUnsentMessage = nil
507 }
508top:
509 for {
510 select {
511 case <-ctx.Done():
512 break top
513 case msg := <-ofc.sendChannel:
David K. Bainbridge55376262020-01-22 23:28:27 -0800514 if err := ofc.doSend(msg); err != nil {
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800515 ofc.lastUnsentMessage = msg
David K. Bainbridge55376262020-01-22 23:28:27 -0800516 ofc.events <- ofcEventDisconnect
David K. Bainbridge9cb404e2020-01-28 14:32:29 -0800517 logger.Debugw("message-sender-error",
518 log.Fields{
519 "device-id": ofc.DeviceID,
520 "error": err.Error()})
521 break top
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800522 }
David K. Bainbridge9cb404e2020-01-28 14:32:29 -0800523 logger.Debugw("message-sender-send",
524 log.Fields{
525 "device-id": ofc.DeviceID})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800526 ofc.lastUnsentMessage = nil
527 }
528 }
David K. Bainbridge9cb404e2020-01-28 14:32:29 -0800529
530 logger.Debugw("message-sender-finished",
531 log.Fields{
532 "device-id": ofc.DeviceID})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800533}
534
David K. Bainbridge55376262020-01-22 23:28:27 -0800535// SendMessage queues a message to be sent to the openflow controller
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800536func (ofc *OFClient) SendMessage(message Message) error {
David K. Bainbridge9cb404e2020-01-28 14:32:29 -0800537 logger.Debug("queuing-message")
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800538 ofc.sendChannel <- message
539 return nil
540}