blob: 6e832e538dd071a6a67a255c30bac2e0bcfbc03a [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)
35
36type ofcEvent byte
37type ofcState byte
38
39const (
40 ofcEventStart = ofcEvent(iota)
David K. Bainbridge55376262020-01-22 23:28:27 -080041 ofcEventConnect
42 ofcEventDisconnect
43 ofcEventStop
David K. Bainbridge157bdab2020-01-16 14:38:05 -080044
David K. Bainbridge55376262020-01-22 23:28:27 -080045 ofcStateCreated = ofcState(iota)
46 ofcStateStarted
47 ofcStateConnected
David K. Bainbridge157bdab2020-01-16 14:38:05 -080048 ofcStateDisconnected
David K. Bainbridge55376262020-01-22 23:28:27 -080049 ofcStateStopped
David K. Bainbridge157bdab2020-01-16 14:38:05 -080050)
51
David K. Bainbridge55376262020-01-22 23:28:27 -080052func (e ofcEvent) String() string {
53 switch e {
54 case ofcEventStart:
55 return "ofc-event-start"
56 case ofcEventConnect:
57 return "ofc-event-connected"
58 case ofcEventDisconnect:
59 return "ofc-event-disconnected"
60 case ofcEventStop:
61 return "ofc-event-stop"
62 default:
63 return "ofc-event-unknown"
64 }
65}
66
67func (s ofcState) String() string {
68 switch s {
69 case ofcStateCreated:
70 return "ofc-state-created"
71 case ofcStateStarted:
72 return "ofc-state-started"
73 case ofcStateConnected:
74 return "ofc-state-connected"
75 case ofcStateDisconnected:
76 return "ofc-state-disconnected"
77 case ofcStateStopped:
78 return "ofc-state-stopped"
79 default:
80 return "ofc-state-unknown"
81 }
82}
83
84// OFClient the configuration and operational state of a connection to an
85// openflow controller
David K. Bainbridge157bdab2020-01-16 14:38:05 -080086type OFClient struct {
87 OFControllerEndPoint string
88 Port uint16
89 DeviceID string
David K. Bainbridge157bdab2020-01-16 14:38:05 -080090 VolthaClient voltha.VolthaServiceClient
91 PacketOutChannel chan *voltha.PacketOut
92 ConnectionMaxRetries int
93 ConnectionRetryDelay time.Duration
94 conn net.Conn
95
96 // expirimental
97 events chan ofcEvent
98 sendChannel chan Message
99 lastUnsentMessage Message
100}
101
David K. Bainbridge55376262020-01-22 23:28:27 -0800102// NewClient returns an initialized OFClient instance based on the configuration
103// specified
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800104func NewOFClient(config *OFClient) *OFClient {
105
106 ofc := OFClient{
107 DeviceID: config.DeviceID,
108 OFControllerEndPoint: config.OFControllerEndPoint,
109 VolthaClient: config.VolthaClient,
110 PacketOutChannel: config.PacketOutChannel,
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800111 ConnectionMaxRetries: config.ConnectionMaxRetries,
112 ConnectionRetryDelay: config.ConnectionRetryDelay,
113 events: make(chan ofcEvent, 10),
114 sendChannel: make(chan Message, 100),
115 }
116
117 if ofc.ConnectionRetryDelay <= 0 {
118 logger.Warnw("connection retry delay not valid, setting to default",
119 log.Fields{
120 "device-id": ofc.DeviceID,
121 "value": ofc.ConnectionRetryDelay.String(),
122 "default": (3 * time.Second).String()})
123 ofc.ConnectionRetryDelay = 3 * time.Second
124 }
125 return &ofc
126}
127
David K. Bainbridge55376262020-01-22 23:28:27 -0800128// Stop initiates a shutdown of the OFClient
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800129func (ofc *OFClient) Stop() {
David K. Bainbridge55376262020-01-22 23:28:27 -0800130 ofc.events <- ofcEventStop
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800131}
132
133func (ofc *OFClient) peekAtOFHeader(buf []byte) (ofp.IHeader, error) {
134 header := ofp.Header{}
135 header.Version = uint8(buf[0])
136 header.Type = uint8(buf[1])
137 header.Length = binary.BigEndian.Uint16(buf[2:4])
138 header.Xid = binary.BigEndian.Uint32(buf[4:8])
139
140 // TODO: add minimal validation of version and type
141
142 return &header, nil
143}
144
145func (ofc *OFClient) establishConnectionToController() error {
146 if ofc.conn != nil {
David K. Bainbridge55376262020-01-22 23:28:27 -0800147 logger.Debugw("closing-of-connection-to-reconnect",
148 log.Fields{"device-id": ofc.DeviceID})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800149 ofc.conn.Close()
150 ofc.conn = nil
151 }
152 try := 1
153 for ofc.ConnectionMaxRetries == 0 || try < ofc.ConnectionMaxRetries {
154 if raddr, err := net.ResolveTCPAddr("tcp", ofc.OFControllerEndPoint); err != nil {
155 logger.Debugw("openflow-client unable to resolve endpoint",
156 log.Fields{
157 "device-id": ofc.DeviceID,
158 "endpoint": ofc.OFControllerEndPoint})
159 } else {
160 if connection, err := net.DialTCP("tcp", nil, raddr); err == nil {
161 ofc.conn = connection
162 ofc.sayHello()
David K. Bainbridge55376262020-01-22 23:28:27 -0800163 ofc.events <- ofcEventConnect
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800164 return nil
165 } else {
166 logger.Warnw("openflow-client-connect-error",
167 log.Fields{
168 "device-id": ofc.DeviceID,
169 "endpoint": ofc.OFControllerEndPoint})
170 }
171 }
172 if ofc.ConnectionMaxRetries == 0 || try < ofc.ConnectionMaxRetries {
173 if ofc.ConnectionMaxRetries != 0 {
174 try += 1
175 }
176 time.Sleep(ofc.ConnectionRetryDelay)
177 }
178 }
179 return errors.New("failed-to-connect-to-of-controller")
180}
181
David K. Bainbridge55376262020-01-22 23:28:27 -0800182// Run implementes the state machine for the OF client reacting to state change
183// events and invoking actions as a reaction to those state changes
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800184func (ofc *OFClient) Run(ctx context.Context) {
185
186 var ofCtx context.Context
187 var ofDone func()
David K. Bainbridge55376262020-01-22 23:28:27 -0800188 state := ofcStateCreated
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800189 ofc.events <- ofcEventStart
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800190top:
191 for {
192 select {
193 case <-ctx.Done():
David K. Bainbridge55376262020-01-22 23:28:27 -0800194 state = ofcStateStopped
195 logger.Debugw("state-transition-context-done",
196 log.Fields{"device-id": ofc.DeviceID})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800197 break top
198 case event := <-ofc.events:
David K. Bainbridge55376262020-01-22 23:28:27 -0800199 previous := state
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800200 switch event {
201 case ofcEventStart:
David K. Bainbridge55376262020-01-22 23:28:27 -0800202 logger.Debugw("ofc-event-start",
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800203 log.Fields{"device-id": ofc.DeviceID})
David K. Bainbridge55376262020-01-22 23:28:27 -0800204 if state == ofcStateCreated {
205 state = ofcStateStarted
206 logger.Debug("STARTED MORE THAN ONCE")
207 go ofc.establishConnectionToController()
208 } else {
209 logger.Errorw("illegal-state-transition",
210 log.Fields{
211 "device-id": ofc.DeviceID,
212 "current-state": state.String(),
213 "event": event.String()})
214 }
215 case ofcEventConnect:
216 logger.Debugw("ofc-event-connected",
217 log.Fields{"device-id": ofc.DeviceID})
218 if state == ofcStateStarted || state == ofcStateDisconnected {
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800219 state = ofcStateConnected
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800220 ofCtx, ofDone = context.WithCancel(context.Background())
221 go ofc.messageSender(ofCtx)
222 go ofc.processOFStream(ofCtx)
David K. Bainbridge55376262020-01-22 23:28:27 -0800223 } else {
224 logger.Errorw("illegal-state-transition",
225 log.Fields{
226 "device-id": ofc.DeviceID,
227 "current-state": state.String(),
228 "event": event.String()})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800229 }
David K. Bainbridge55376262020-01-22 23:28:27 -0800230 case ofcEventDisconnect:
231 logger.Debugw("ofc-event-disconnected",
232 log.Fields{
233 "device-id": ofc.DeviceID,
234 "state": state.String()})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800235 if state == ofcStateConnected {
236 state = ofcStateDisconnected
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800237 if ofDone != nil {
238 ofDone()
239 ofDone = nil
240 }
241 go ofc.establishConnectionToController()
David K. Bainbridge55376262020-01-22 23:28:27 -0800242 } else {
243 logger.Errorw("illegal-state-transition",
244 log.Fields{
245 "device-id": ofc.DeviceID,
246 "current-state": state.String(),
247 "event": event.String()})
248 }
249 case ofcEventStop:
250 logger.Debugw("ofc-event-stop",
251 log.Fields{"device-id": ofc.DeviceID})
252 if state == ofcStateCreated || state == ofcStateConnected || state == ofcStateDisconnected {
253 state = ofcStateStopped
254 break top
255 } else {
256 logger.Errorw("illegal-state-transition",
257 log.Fields{
258 "device-id": ofc.DeviceID,
259 "current-state": state.String(),
260 "event": event.String()})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800261 }
262 }
David K. Bainbridge55376262020-01-22 23:28:27 -0800263 logger.Debugw("state-transition",
264 log.Fields{
265 "device-id": ofc.DeviceID,
266 "previous-state": previous.String(),
267 "current-state": state.String(),
268 "event": event.String()})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800269 }
270 }
271
David K. Bainbridge55376262020-01-22 23:28:27 -0800272 // If the child context exists, then cancel it
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800273 if ofDone != nil {
David K. Bainbridge55376262020-01-22 23:28:27 -0800274 log.Debugw("closing-child-processes",
275 log.Fields{"device-id": ofc.DeviceID})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800276 ofDone()
277 ofDone = nil
278 }
279
David K. Bainbridge55376262020-01-22 23:28:27 -0800280 // If the connection is open, then close it
281 if ofc.conn != nil {
282 log.Debugw("closing-of-connection",
283 log.Fields{"device-id": ofc.DeviceID})
284 ofc.conn.Close()
285 ofc.conn = nil
286 }
287 log.Debugw("state-machine-finished",
288 log.Fields{"device-id": ofc.DeviceID})
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800289}
290
David K. Bainbridge55376262020-01-22 23:28:27 -0800291// processOFStream processes the OF connection from the controller and invokes
292// the appropriate handler methods for each message.
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800293func (ofc *OFClient) processOFStream(ctx context.Context) {
David K. Bainbridge55376262020-01-22 23:28:27 -0800294 fromController := bufio.NewReader(ofc.conn)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800295
296 /*
David K. Bainbridge55376262020-01-22 23:28:27 -0800297 * We have a read buffer of a max size of 4096, so if we ever have
298 * a message larger than this then we will have issues
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800299 */
David K. Bainbridge55376262020-01-22 23:28:27 -0800300 buf := make([]byte, 4096)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800301
302top:
303 // Continue until we are told to stop
David K. Bainbridge55376262020-01-22 23:28:27 -0800304 for {
305 select {
306 case <-ctx.Done():
307 logger.Error("of-loop-ending-context-done")
308 break top
309 default:
310 // Read 8 bytes, the standard OF header
311 read, err := io.ReadFull(fromController, buf[:8])
312 if err != nil {
313 logger.Errorw("bad-of-header",
314 log.Fields{
315 "byte-count": read,
316 "device-id": ofc.DeviceID,
317 "error": err})
318 break top
319 }
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800320
David K. Bainbridge55376262020-01-22 23:28:27 -0800321 // Decode the header
322 peek, err := ofc.peekAtOFHeader(buf[:8])
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800323 if err != nil {
324 /*
325 * Header is bad, assume stream is corrupted
326 * and needs to be restarted
327 */
328 logger.Errorw("bad-of-packet",
329 log.Fields{
330 "device-id": ofc.DeviceID,
331 "error": err})
332 break top
333 }
334
David K. Bainbridge55376262020-01-22 23:28:27 -0800335 // Calculate the size of the rest of the packet and read it
336 need := int(peek.GetLength())
337 read, err = io.ReadFull(fromController, buf[8:need])
338 if err != nil {
339 logger.Errorw("bad-of-packet",
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800340 log.Fields{
David K. Bainbridge55376262020-01-22 23:28:27 -0800341 "byte-count": read,
342 "device-id": ofc.DeviceID,
343 "error": err})
344 break top
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800345 }
346
347 // Decode and process the packet
David K. Bainbridge0b3f6482020-01-27 19:32:39 -0800348 msgbuf := make([]byte, need)
349 copy(msgbuf, buf[:need])
350 decoder := goloxi.NewDecoder(msgbuf)
351 msg, err := ofp.DecodeHeader(decoder)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800352 if err != nil {
353 js, _ := json.Marshal(decoder)
354 logger.Errorw("failed-to-decode",
355 log.Fields{
356 "device-id": ofc.DeviceID,
357 "decoder": js,
358 "error": err})
359 break top
360 }
361 if logger.V(log.DebugLevel) {
David K. Bainbridge0b3f6482020-01-27 19:32:39 -0800362 js, _ := json.Marshal(msg)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800363 logger.Debugw("packet-header",
364 log.Fields{
365 "device-id": ofc.DeviceID,
366 "header": js})
367 }
David K. Bainbridge0b3f6482020-01-27 19:32:39 -0800368 ofc.parseHeader(msg)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800369 }
370 }
David K. Bainbridge55376262020-01-22 23:28:27 -0800371 logger.Debugw("end-of-stream",
372 log.Fields{"device-id": ofc.DeviceID})
373 ofc.events <- ofcEventDisconnect
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800374}
375
376func (ofc *OFClient) sayHello() {
377 hello := ofp.NewHello()
378 hello.Xid = uint32(GetXid())
379 elem := ofp.NewHelloElemVersionbitmap()
380 elem.SetType(ofp.OFPHETVersionbitmap)
381 elem.SetLength(8)
382 elem.SetBitmaps([]*ofp.Uint32{&ofp.Uint32{Value: 16}})
383 hello.SetElements([]ofp.IHelloElem{elem})
384 if logger.V(log.DebugLevel) {
385 js, _ := json.Marshal(hello)
386 logger.Debugw("sayHello Called",
387 log.Fields{
388 "device-id": ofc.DeviceID,
389 "hello-message": js})
390 }
391 if err := ofc.SendMessage(hello); err != nil {
392 logger.Fatalw("Failed saying hello to Openflow Server, unable to proceed",
393 log.Fields{
394 "device-id": ofc.DeviceID,
395 "error": err})
396 }
397}
398
399func (ofc *OFClient) parseHeader(header ofp.IHeader) {
400 switch header.GetType() {
401 case ofp.OFPTHello:
402 //x := header.(*ofp.Hello)
403 case ofp.OFPTError:
404 go ofc.handleErrMsg(header.(*ofp.ErrorMsg))
405 case ofp.OFPTEchoRequest:
406 go ofc.handleEchoRequest(header.(*ofp.EchoRequest))
407 case ofp.OFPTEchoReply:
408 case ofp.OFPTExperimenter:
409 case ofp.OFPTFeaturesRequest:
410 go ofc.handleFeatureRequest(header.(*ofp.FeaturesRequest))
411 case ofp.OFPTFeaturesReply:
412 case ofp.OFPTGetConfigRequest:
413 go ofc.handleGetConfigRequest(header.(*ofp.GetConfigRequest))
414 case ofp.OFPTGetConfigReply:
415 case ofp.OFPTSetConfig:
416 go ofc.handleSetConfig(header.(*ofp.SetConfig))
417 case ofp.OFPTPacketIn:
418 case ofp.OFPTFlowRemoved:
419 case ofp.OFPTPortStatus:
420 case ofp.OFPTPacketOut:
421 go ofc.handlePacketOut(header.(*ofp.PacketOut))
422 case ofp.OFPTFlowMod:
423 /*
424 * Not using go routine to handle flow* messages or barrier requests
425 * onos typically issues barrier requests just before a flow* message.
426 * by handling in this thread I ensure all flow* are handled when barrier
427 * request is issued.
428 */
429 switch header.(ofp.IFlowMod).GetCommand() {
430 case ofp.OFPFCAdd:
431 ofc.handleFlowAdd(header.(*ofp.FlowAdd))
432 case ofp.OFPFCModify:
433 ofc.handleFlowMod(header.(*ofp.FlowMod))
434 case ofp.OFPFCModifyStrict:
435 ofc.handleFlowModStrict(header.(*ofp.FlowModifyStrict))
436 case ofp.OFPFCDelete:
437 ofc.handleFlowDelete(header.(*ofp.FlowDelete))
438 case ofp.OFPFCDeleteStrict:
439 ofc.handleFlowDeleteStrict(header.(*ofp.FlowDeleteStrict))
440 }
441 case ofp.OFPTStatsRequest:
442 go ofc.handleStatsRequest(header, header.(ofp.IStatsRequest).GetStatsType())
443 case ofp.OFPTBarrierRequest:
444 /* See note above at case ofp.OFPTFlowMod:*/
445 ofc.handleBarrierRequest(header.(*ofp.BarrierRequest))
446 case ofp.OFPTRoleRequest:
447 go ofc.handleRoleRequest(header.(*ofp.RoleRequest))
448 case ofp.OFPTMeterMod:
Don Newtonac0455d2020-01-23 11:52:26 -0500449 ofc.handleMeterModRequest(header.(*ofp.MeterMod))
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800450 }
451}
452
David K. Bainbridge55376262020-01-22 23:28:27 -0800453// Message interface that represents an open flow message and enables for a
454// unified implementation of SendMessage
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800455type Message interface {
456 Serialize(encoder *goloxi.Encoder) error
457}
458
459func (ofc *OFClient) doSend(msg Message) error {
460 if ofc.conn == nil {
461 return errors.New("no-connection")
462 }
463 enc := goloxi.NewEncoder()
464 msg.Serialize(enc)
465 bytes := enc.Bytes()
466 if _, err := ofc.conn.Write(bytes); err != nil {
David K. Bainbridge55376262020-01-22 23:28:27 -0800467 logger.Errorw("unable-to-send-message-to-controller",
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800468 log.Fields{
469 "device-id": ofc.DeviceID,
470 "message": msg,
471 "error": err})
472 return err
473 }
474 return nil
475}
476
477func (ofc *OFClient) messageSender(ctx context.Context) {
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800478 // first process last fail if it exists
479 if ofc.lastUnsentMessage != nil {
480 if err := ofc.doSend(ofc.lastUnsentMessage); err != nil {
David K. Bainbridge55376262020-01-22 23:28:27 -0800481 ofc.events <- ofcEventDisconnect
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800482 return
483 }
484 ofc.lastUnsentMessage = nil
485 }
486top:
487 for {
488 select {
489 case <-ctx.Done():
490 break top
491 case msg := <-ofc.sendChannel:
David K. Bainbridge55376262020-01-22 23:28:27 -0800492 if err := ofc.doSend(msg); err != nil {
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800493 ofc.lastUnsentMessage = msg
David K. Bainbridge55376262020-01-22 23:28:27 -0800494 ofc.events <- ofcEventDisconnect
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800495 return
496 }
497 ofc.lastUnsentMessage = nil
498 }
499 }
500}
501
David K. Bainbridge55376262020-01-22 23:28:27 -0800502// SendMessage queues a message to be sent to the openflow controller
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800503func (ofc *OFClient) SendMessage(message Message) error {
504 ofc.sendChannel <- message
505 return nil
506}