blob: ccceb4401d85206e3da38b95e61392a81f7df256 [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. Bainbridge55376262020-01-22 23:28:27 -0800348 decoder := goloxi.NewDecoder(buf[:need])
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800349 header, err := ofp.DecodeHeader(decoder)
350 if err != nil {
351 js, _ := json.Marshal(decoder)
352 logger.Errorw("failed-to-decode",
353 log.Fields{
354 "device-id": ofc.DeviceID,
355 "decoder": js,
356 "error": err})
357 break top
358 }
359 if logger.V(log.DebugLevel) {
360 js, _ := json.Marshal(header)
361 logger.Debugw("packet-header",
362 log.Fields{
363 "device-id": ofc.DeviceID,
364 "header": js})
365 }
366 ofc.parseHeader(header)
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800367 }
368 }
David K. Bainbridge55376262020-01-22 23:28:27 -0800369 logger.Debugw("end-of-stream",
370 log.Fields{"device-id": ofc.DeviceID})
371 ofc.events <- ofcEventDisconnect
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800372}
373
374func (ofc *OFClient) sayHello() {
375 hello := ofp.NewHello()
376 hello.Xid = uint32(GetXid())
377 elem := ofp.NewHelloElemVersionbitmap()
378 elem.SetType(ofp.OFPHETVersionbitmap)
379 elem.SetLength(8)
380 elem.SetBitmaps([]*ofp.Uint32{&ofp.Uint32{Value: 16}})
381 hello.SetElements([]ofp.IHelloElem{elem})
382 if logger.V(log.DebugLevel) {
383 js, _ := json.Marshal(hello)
384 logger.Debugw("sayHello Called",
385 log.Fields{
386 "device-id": ofc.DeviceID,
387 "hello-message": js})
388 }
389 if err := ofc.SendMessage(hello); err != nil {
390 logger.Fatalw("Failed saying hello to Openflow Server, unable to proceed",
391 log.Fields{
392 "device-id": ofc.DeviceID,
393 "error": err})
394 }
395}
396
397func (ofc *OFClient) parseHeader(header ofp.IHeader) {
398 switch header.GetType() {
399 case ofp.OFPTHello:
400 //x := header.(*ofp.Hello)
401 case ofp.OFPTError:
402 go ofc.handleErrMsg(header.(*ofp.ErrorMsg))
403 case ofp.OFPTEchoRequest:
404 go ofc.handleEchoRequest(header.(*ofp.EchoRequest))
405 case ofp.OFPTEchoReply:
406 case ofp.OFPTExperimenter:
407 case ofp.OFPTFeaturesRequest:
408 go ofc.handleFeatureRequest(header.(*ofp.FeaturesRequest))
409 case ofp.OFPTFeaturesReply:
410 case ofp.OFPTGetConfigRequest:
411 go ofc.handleGetConfigRequest(header.(*ofp.GetConfigRequest))
412 case ofp.OFPTGetConfigReply:
413 case ofp.OFPTSetConfig:
414 go ofc.handleSetConfig(header.(*ofp.SetConfig))
415 case ofp.OFPTPacketIn:
416 case ofp.OFPTFlowRemoved:
417 case ofp.OFPTPortStatus:
418 case ofp.OFPTPacketOut:
419 go ofc.handlePacketOut(header.(*ofp.PacketOut))
420 case ofp.OFPTFlowMod:
421 /*
422 * Not using go routine to handle flow* messages or barrier requests
423 * onos typically issues barrier requests just before a flow* message.
424 * by handling in this thread I ensure all flow* are handled when barrier
425 * request is issued.
426 */
427 switch header.(ofp.IFlowMod).GetCommand() {
428 case ofp.OFPFCAdd:
429 ofc.handleFlowAdd(header.(*ofp.FlowAdd))
430 case ofp.OFPFCModify:
431 ofc.handleFlowMod(header.(*ofp.FlowMod))
432 case ofp.OFPFCModifyStrict:
433 ofc.handleFlowModStrict(header.(*ofp.FlowModifyStrict))
434 case ofp.OFPFCDelete:
435 ofc.handleFlowDelete(header.(*ofp.FlowDelete))
436 case ofp.OFPFCDeleteStrict:
437 ofc.handleFlowDeleteStrict(header.(*ofp.FlowDeleteStrict))
438 }
439 case ofp.OFPTStatsRequest:
440 go ofc.handleStatsRequest(header, header.(ofp.IStatsRequest).GetStatsType())
441 case ofp.OFPTBarrierRequest:
442 /* See note above at case ofp.OFPTFlowMod:*/
443 ofc.handleBarrierRequest(header.(*ofp.BarrierRequest))
444 case ofp.OFPTRoleRequest:
445 go ofc.handleRoleRequest(header.(*ofp.RoleRequest))
446 case ofp.OFPTMeterMod:
Don Newtonac0455d2020-01-23 11:52:26 -0500447 ofc.handleMeterModRequest(header.(*ofp.MeterMod))
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800448 }
449}
450
David K. Bainbridge55376262020-01-22 23:28:27 -0800451// Message interface that represents an open flow message and enables for a
452// unified implementation of SendMessage
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800453type Message interface {
454 Serialize(encoder *goloxi.Encoder) error
455}
456
457func (ofc *OFClient) doSend(msg Message) error {
458 if ofc.conn == nil {
459 return errors.New("no-connection")
460 }
461 enc := goloxi.NewEncoder()
462 msg.Serialize(enc)
463 bytes := enc.Bytes()
464 if _, err := ofc.conn.Write(bytes); err != nil {
David K. Bainbridge55376262020-01-22 23:28:27 -0800465 logger.Errorw("unable-to-send-message-to-controller",
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800466 log.Fields{
467 "device-id": ofc.DeviceID,
468 "message": msg,
469 "error": err})
470 return err
471 }
472 return nil
473}
474
475func (ofc *OFClient) messageSender(ctx context.Context) {
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800476 // first process last fail if it exists
477 if ofc.lastUnsentMessage != nil {
478 if err := ofc.doSend(ofc.lastUnsentMessage); err != nil {
David K. Bainbridge55376262020-01-22 23:28:27 -0800479 ofc.events <- ofcEventDisconnect
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800480 return
481 }
482 ofc.lastUnsentMessage = nil
483 }
484top:
485 for {
486 select {
487 case <-ctx.Done():
488 break top
489 case msg := <-ofc.sendChannel:
David K. Bainbridge55376262020-01-22 23:28:27 -0800490 if err := ofc.doSend(msg); err != nil {
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800491 ofc.lastUnsentMessage = msg
David K. Bainbridge55376262020-01-22 23:28:27 -0800492 ofc.events <- ofcEventDisconnect
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800493 return
494 }
495 ofc.lastUnsentMessage = nil
496 }
497 }
498}
499
David K. Bainbridge55376262020-01-22 23:28:27 -0800500// SendMessage queues a message to be sent to the openflow controller
David K. Bainbridge157bdab2020-01-16 14:38:05 -0800501func (ofc *OFClient) SendMessage(message Message) error {
502 ofc.sendChannel <- message
503 return nil
504}