blob: 94e07052db17b13e8914966fc7ed18ab34b8aff6 [file] [log] [blame]
Matteo Scandolo11006992019-08-28 11:29:46 -07001/*
2 * Copyright 2018-present Open Networking Foundation
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
Matteo Scandolo4747d292019-08-05 11:50:18 -070017package devices
18
19import (
Matteo Scandolo3bc73742019-08-20 14:04:04 -070020 "fmt"
21 "github.com/opencord/bbsim/internal/bbsim/responders"
22 "github.com/google/gopacket/layers"
Matteo Scandolo4747d292019-08-05 11:50:18 -070023 "github.com/looplab/fsm"
Matteo Scandoloc559ef12019-08-20 13:24:21 -070024 omci "github.com/opencord/omci-sim"
Matteo Scandolo3bc73742019-08-20 14:04:04 -070025 "github.com/opencord/voltha-protos/go/openolt"
Matteo Scandolo4747d292019-08-05 11:50:18 -070026 log "github.com/sirupsen/logrus"
Matteo Scandolo3bc73742019-08-20 14:04:04 -070027 bbsim "github.com/opencord/bbsim/internal/bbsim/types"
Matteo Scandolo4747d292019-08-05 11:50:18 -070028)
29
Matteo Scandolo9a3518c2019-08-13 14:36:01 -070030var onuLogger = log.WithFields(log.Fields{
31 "module": "ONU",
32})
33
Matteo Scandolo4747d292019-08-05 11:50:18 -070034func CreateONU(olt OltDevice, pon PonPort, id uint32) Onu {
35 o := Onu{
36 ID: id,
Matteo Scandolo4747d292019-08-05 11:50:18 -070037 PonPortID: pon.ID,
38 PonPort: pon,
Matteo Scandolo3bc73742019-08-20 14:04:04 -070039 // NOTE can we combine everything in a single channel?
Matteo Scandolo9a3518c2019-08-13 14:36:01 -070040 channel: make(chan Message),
Matteo Scandolo3bc73742019-08-20 14:04:04 -070041 eapolPktOutCh: make(chan *bbsim.ByteMsg, 1024),
Matteo Scandolo4747d292019-08-05 11:50:18 -070042 }
43 o.SerialNumber = o.NewSN(olt.ID, pon.ID, o.ID)
44
Matteo Scandolo9a3518c2019-08-13 14:36:01 -070045 // NOTE this state machine is used to track the operational
46 // state as requested by VOLTHA
47 o.OperState = getOperStateFSM(func(e *fsm.Event) {
48 onuLogger.WithFields(log.Fields{
49 "ID": o.ID,
50 }).Debugf("Changing ONU OperState from %s to %s", e.Src, e.Dst)
51 })
52
53 // NOTE this state machine is used to activate the OMCI, EAPOL and DHCP clients
Matteo Scandolo4747d292019-08-05 11:50:18 -070054 o.InternalState = fsm.NewFSM(
55 "created",
56 fsm.Events{
57 {Name: "discover", Src: []string{"created"}, Dst: "discovered"},
58 {Name: "enable", Src: []string{"discovered"}, Dst: "enabled"},
Matteo Scandolo3bc73742019-08-20 14:04:04 -070059 {Name: "receive_eapol_flow", Src: []string{"enabled", "gem_port_added"}, Dst: "eapol_flow_received"},
60 {Name: "add_gem_port", Src: []string{"enabled", "eapol_flow_received"}, Dst: "gem_port_added"},
61 {Name: "start_auth", Src: []string{"eapol_flow_received", "gem_port_added"}, Dst: "auth_started"},
Matteo Scandolo4747d292019-08-05 11:50:18 -070062 },
63 fsm.Callbacks{
64 "enter_state": func(e *fsm.Event) {
Matteo Scandolo3bc73742019-08-20 14:04:04 -070065 o.logStateChange(e.Src, e.Dst)
66 },
67 "enter_eapol_flow_received": func(e *fsm.Event) {
68 o.logStateChange(e.Src, e.Dst)
69 if e.Src == "enter_gem_port_added" {
70 if err := o.InternalState.Event("start_auth"); err != nil {
71 log.Infof("Transitioning to StartAuth")
72 onuLogger.WithFields(log.Fields{
73 "OnuId": o.ID,
74 "IntfId": o.PonPortID,
75 "OnuSn": o.SerialNumber,
76 }).Errorf("Error while transitioning ONU State")
77 }
78 }
79 },
80 "enter_gem_port_added": func(e *fsm.Event) {
81 o.logStateChange(e.Src, e.Dst)
82 if e.Src == "eapol_flow_received" {
83 log.Infof("Transitioning to StartAuth")
84 if err := o.InternalState.Event("start_auth"); err != nil {
85 onuLogger.WithFields(log.Fields{
86 "OnuId": o.ID,
87 "IntfId": o.PonPortID,
88 "OnuSn": o.SerialNumber,
89 }).Errorf("Error while transitioning ONU State")
90 }
91 }
92 },
93 "enter_auth_started": func(e *fsm.Event) {
94 o.logStateChange(e.Src, e.Dst)
95 msg := Message{
96 Type: StartEAPOL,
97 Data: EapStartMessage{
98 PonPortID: o.PonPortID,
99 OnuID: o.ID,
100 },
101 }
102 go func(msg Message){
103 // you can only send a value on an unbuffered channel without blocking
104 o.channel <- msg
105 }(msg)
106
Matteo Scandolo4747d292019-08-05 11:50:18 -0700107 },
108 },
109 )
110 return o
111}
112
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700113func (o Onu) logStateChange(src string, dst string) {
114 onuLogger.WithFields(log.Fields{
115 "OnuId": o.ID,
116 "IntfId": o.PonPortID,
117 "OnuSn": o.SerialNumber,
118 }).Debugf("Changing ONU InternalState from %s to %s", src, dst)
119}
120
Matteo Scandolo9a3518c2019-08-13 14:36:01 -0700121func (o Onu) processOnuMessages(stream openolt.Openolt_EnableIndicationServer) {
122 onuLogger.WithFields(log.Fields{
Matteo Scandolo4747d292019-08-05 11:50:18 -0700123 "onuID": o.ID,
124 "onuSN": o.SerialNumber,
Matteo Scandolo9a3518c2019-08-13 14:36:01 -0700125 }).Debug("Started ONU Indication Channel")
126
127 for message := range o.channel {
128 onuLogger.WithFields(log.Fields{
129 "onuID": o.ID,
130 "onuSN": o.SerialNumber,
131 "messageType": message.Type,
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700132 }).Tracef("Received message on ONU Channel")
Matteo Scandolo9a3518c2019-08-13 14:36:01 -0700133
134 switch message.Type {
135 case OnuDiscIndication:
136 msg, _ := message.Data.(OnuDiscIndicationMessage)
137 o.sendOnuDiscIndication(msg, stream)
138 case OnuIndication:
139 msg, _ := message.Data.(OnuIndicationMessage)
140 o.sendOnuIndication(msg, stream)
141 case OMCI:
Matteo Scandoloc559ef12019-08-20 13:24:21 -0700142 msg, _ := message.Data.(OmciMessage)
Matteo Scandoloc559ef12019-08-20 13:24:21 -0700143 o.handleOmciMessage(msg, stream)
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700144 case FlowUpdate:
145 msg, _ := message.Data.(OnuFlowUpdateMessage)
146 o.handleFlowUpdate(msg, stream)
147 case StartEAPOL:
148 log.Infof("Receive StartEAPOL message on ONU channel")
149 go func() {
150
151 responders.StartWPASupplicant(o.ID, o.PonPortID, o.SerialNumber, stream, o.eapolPktOutCh)
152 }()
Matteo Scandolo9a3518c2019-08-13 14:36:01 -0700153 default:
154 onuLogger.Warnf("Received unknown message data %v for type %v in OLT channel", message.Data, message.Type)
155 }
156 }
Matteo Scandolo4747d292019-08-05 11:50:18 -0700157}
158
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700159func (o Onu) processOmciMessages(stream openolt.Openolt_EnableIndicationServer) {
160 ch := omci.GetChannel()
161
162 onuLogger.WithFields(log.Fields{
163 "onuID": o.ID,
164 "onuSN": o.SerialNumber,
165 }).Debug("Started OMCI Indication Channel")
166
167 for message := range ch {
168 switch message.Type {
169 case omci.GemPortAdded:
170 log.WithFields(log.Fields{
171 "OnuId": message.Data.OnuId,
172 "IntfId": message.Data.IntfId,
173 }).Infof("GemPort Added")
174
175 // NOTE if we receive the GemPort but we don't have EAPOL flows
176 // go an intermediate state, otherwise start auth
177 if o.InternalState.Is("enabled") {
178 if err := o.InternalState.Event("add_gem_port"); err != nil {
179 log.Errorf("Can't go to gem_port_added: %v", err)
180 }
181 } else if o.InternalState.Is("eapol_flow_received"){
182 if err := o.InternalState.Event("start_auth"); err != nil {
183 log.Errorf("Can't go to auth_started: %v", err)
184 }
185 }
186 }
187 }
188}
189
Matteo Scandolo4747d292019-08-05 11:50:18 -0700190func (o Onu) NewSN(oltid int, intfid uint32, onuid uint32) *openolt.SerialNumber {
191
192 sn := new(openolt.SerialNumber)
193
194 sn = new(openolt.SerialNumber)
195 sn.VendorId = []byte("BBSM")
196 sn.VendorSpecific = []byte{0, byte(oltid % 256), byte(intfid), byte(onuid)}
197
198 return sn
199}
200
201func (o Onu) sendOnuDiscIndication(msg OnuDiscIndicationMessage, stream openolt.Openolt_EnableIndicationServer) {
202 discoverData := &openolt.Indication_OnuDiscInd{OnuDiscInd: &openolt.OnuDiscIndication{
203 IntfId: msg.Onu.PonPortID,
204 SerialNumber: msg.Onu.SerialNumber,
205 }}
206 if err := stream.Send(&openolt.Indication{Data: discoverData}); err != nil {
Matteo Scandolo11006992019-08-28 11:29:46 -0700207 log.Errorf("Failed to send Indication_OnuDiscInd: %v", err)
Matteo Scandolo4747d292019-08-05 11:50:18 -0700208 }
Matteo Scandolo9a3518c2019-08-13 14:36:01 -0700209 o.InternalState.Event("discover")
210 onuLogger.WithFields(log.Fields{
Matteo Scandolo4747d292019-08-05 11:50:18 -0700211 "IntfId": msg.Onu.PonPortID,
212 "SerialNumber": msg.Onu.SerialNumber,
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700213 "OnuId": o.ID,
Matteo Scandolo4747d292019-08-05 11:50:18 -0700214 }).Debug("Sent Indication_OnuDiscInd")
215}
216
217func (o Onu) sendOnuIndication(msg OnuIndicationMessage, stream openolt.Openolt_EnableIndicationServer) {
218 // NOTE voltha returns an ID, but if we use that ID then it complains:
219 // expected_onu_id: 1, received_onu_id: 1024, event: ONU-id-mismatch, can happen if both voltha and the olt rebooted
220 // so we're using the internal ID that is 1
221 // o.ID = msg.OnuID
Matteo Scandolo9a3518c2019-08-13 14:36:01 -0700222 o.OperState.Event("enable")
Matteo Scandolo4747d292019-08-05 11:50:18 -0700223
224 indData := &openolt.Indication_OnuInd{OnuInd: &openolt.OnuIndication{
225 IntfId: o.PonPortID,
226 OnuId: o.ID,
Matteo Scandolo9a3518c2019-08-13 14:36:01 -0700227 OperState: o.OperState.Current(),
228 AdminState: o.OperState.Current(),
Matteo Scandolo4747d292019-08-05 11:50:18 -0700229 SerialNumber: o.SerialNumber,
230 }}
231 if err := stream.Send(&openolt.Indication{Data: indData}); err != nil {
Matteo Scandolo11006992019-08-28 11:29:46 -0700232 log.Errorf("Failed to send Indication_OnuInd: %v", err)
Matteo Scandolo4747d292019-08-05 11:50:18 -0700233 }
Matteo Scandolo9a3518c2019-08-13 14:36:01 -0700234 o.InternalState.Event("enable")
235 onuLogger.WithFields(log.Fields{
Matteo Scandolo4747d292019-08-05 11:50:18 -0700236 "IntfId": o.PonPortID,
237 "OnuId": o.ID,
238 "OperState": msg.OperState.String(),
239 "AdminState": msg.OperState.String(),
240 "SerialNumber": o.SerialNumber,
241 }).Debug("Sent Indication_OnuInd")
Matteo Scandoloc559ef12019-08-20 13:24:21 -0700242}
243
244func (o Onu) handleOmciMessage(msg OmciMessage, stream openolt.Openolt_EnableIndicationServer) {
245
246 onuLogger.WithFields(log.Fields{
247 "IntfId": o.PonPortID,
248 "SerialNumber": o.SerialNumber,
249 "omciPacket": msg.omciMsg.Pkt,
250 }).Tracef("Received OMCI message")
251
252 var omciInd openolt.OmciIndication
253 respPkt, err := omci.OmciSim(o.PonPortID, o.ID, HexDecode(msg.omciMsg.Pkt))
254 if err != nil {
255 onuLogger.Errorf("Error handling OMCI message %v", msg)
256 }
257
258 omciInd.IntfId = o.PonPortID
259 omciInd.OnuId = o.ID
260 omciInd.Pkt = respPkt
261
262 omci := &openolt.Indication_OmciInd{OmciInd: &omciInd}
263 if err := stream.Send(&openolt.Indication{Data: omci}); err != nil {
Matteo Scandolo11006992019-08-28 11:29:46 -0700264 onuLogger.Errorf("send omci indication failed: %v", err)
Matteo Scandoloc559ef12019-08-20 13:24:21 -0700265 }
266 onuLogger.WithFields(log.Fields{
267 "IntfId": o.PonPortID,
268 "SerialNumber": o.SerialNumber,
269 "omciPacket": omciInd.Pkt,
270 }).Tracef("Sent OMCI message")
271}
272
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700273func (o Onu) handleFlowUpdate(msg OnuFlowUpdateMessage, stream openolt.Openolt_EnableIndicationServer) {
274 onuLogger.WithFields(log.Fields{
275 "IntfId": msg.Flow.AccessIntfId,
276 "OnuId": msg.Flow.OnuId,
277 "EthType": fmt.Sprintf("%x", msg.Flow.Classifier.EthType),
278 "InnerVlan": msg.Flow.Classifier.IVid,
279 "OuterVlan": msg.Flow.Classifier.OVid,
280 "FlowType": msg.Flow.FlowType,
281 "FlowId": msg.Flow.FlowId,
282 "UniID": msg.Flow.UniId,
283 "PortNo": msg.Flow.PortNo,
284 }).Infof("ONU receives Flow")
285 if msg.Flow.Classifier.EthType == uint32(layers.EthernetTypeEAPOL) && msg.Flow.Classifier.OVid == 4091 {
286 // NOTE if we receive the EAPOL flows but we don't have GemPorts
287 // go an intermediate state, otherwise start auth
288 if o.InternalState.Is("enabled") {
289 if err := o.InternalState.Event("receive_eapol_flow"); err != nil {
290 log.Errorf("Can't go to eapol_flow_received: %v", err)
291 }
292 } else if o.InternalState.Is("gem_port_added"){
293 if err := o.InternalState.Event("start_auth"); err != nil {
294 log.Errorf("Can't go to auth_started: %v", err)
295 }
296 }
297 }
298}
299
Matteo Scandoloc559ef12019-08-20 13:24:21 -0700300// HexDecode converts the hex encoding to binary
301func HexDecode(pkt []byte) []byte {
302 p := make([]byte, len(pkt)/2)
303 for i, j := 0, 0; i < len(pkt); i, j = i+2, j+1 {
304 // Go figure this ;)
305 u := (pkt[i] & 15) + (pkt[i]>>6)*9
306 l := (pkt[i+1] & 15) + (pkt[i+1]>>6)*9
307 p[j] = u<<4 + l
308 }
309 onuLogger.Tracef("Omci decoded: %x.", p)
310 return p
Matteo Scandolo4747d292019-08-05 11:50:18 -0700311}