blob: fac7ce17a5e1eeb44978d77a42e31747fdeb5f0e [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"
Matteo Scandolo47e69bb2019-08-28 15:41:12 -070021 "github.com/opencord/bbsim/internal/bbsim/responders/eapol"
Matteo Scandolo3bc73742019-08-20 14:04:04 -070022 "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 Scandolo47e69bb2019-08-28 15:41:12 -070062 {Name: "eap_start_sent", Src: []string{"auth_started"}, Dst: "eap_start_sent"},
63 {Name: "eap_resonse_identity_sent", Src: []string{"eap_start_sent"}, Dst: "eap_resonse_identity_sent"},
64 {Name: "eap_resonse_challenge_sent", Src: []string{"eap_resonse_identity_sent"}, Dst: "eap_resonse_challenge_sent"},
65 {Name: "eap_resonse_success_received", Src: []string{"eap_resonse_challenge_sent"}, Dst: "eap_resonse_success_received"},
66 {Name: "auth_failed", Src: []string{"auth_started", "eap_start_sent", "eap_resonse_identity_sent", "eap_resonse_challenge_sent"}, Dst: "auth_failed"},
Matteo Scandolo4747d292019-08-05 11:50:18 -070067 },
68 fsm.Callbacks{
69 "enter_state": func(e *fsm.Event) {
Matteo Scandolo3bc73742019-08-20 14:04:04 -070070 o.logStateChange(e.Src, e.Dst)
71 },
72 "enter_eapol_flow_received": func(e *fsm.Event) {
73 o.logStateChange(e.Src, e.Dst)
74 if e.Src == "enter_gem_port_added" {
75 if err := o.InternalState.Event("start_auth"); err != nil {
76 log.Infof("Transitioning to StartAuth")
77 onuLogger.WithFields(log.Fields{
78 "OnuId": o.ID,
79 "IntfId": o.PonPortID,
80 "OnuSn": o.SerialNumber,
81 }).Errorf("Error while transitioning ONU State")
82 }
83 }
84 },
85 "enter_gem_port_added": func(e *fsm.Event) {
86 o.logStateChange(e.Src, e.Dst)
87 if e.Src == "eapol_flow_received" {
88 log.Infof("Transitioning to StartAuth")
89 if err := o.InternalState.Event("start_auth"); err != nil {
90 onuLogger.WithFields(log.Fields{
91 "OnuId": o.ID,
92 "IntfId": o.PonPortID,
93 "OnuSn": o.SerialNumber,
94 }).Errorf("Error while transitioning ONU State")
95 }
96 }
97 },
98 "enter_auth_started": func(e *fsm.Event) {
99 o.logStateChange(e.Src, e.Dst)
100 msg := Message{
101 Type: StartEAPOL,
102 Data: EapStartMessage{
103 PonPortID: o.PonPortID,
104 OnuID: o.ID,
105 },
106 }
107 go func(msg Message){
108 // you can only send a value on an unbuffered channel without blocking
109 o.channel <- msg
110 }(msg)
111
Matteo Scandolo4747d292019-08-05 11:50:18 -0700112 },
Matteo Scandolo47e69bb2019-08-28 15:41:12 -0700113 "enter_eap_resonse_success_received": func(e *fsm.Event) {
114 o.logStateChange(e.Src, e.Dst)
115 onuLogger.WithFields(log.Fields{
116 "OnuId": o.ID,
117 "IntfId": o.PonPortID,
118 "OnuSn": o.SerialNumber,
119 }).Warnf("TODO start DHCP request")
120 },
Matteo Scandolo4747d292019-08-05 11:50:18 -0700121 },
122 )
123 return o
124}
125
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700126func (o Onu) logStateChange(src string, dst string) {
127 onuLogger.WithFields(log.Fields{
128 "OnuId": o.ID,
129 "IntfId": o.PonPortID,
130 "OnuSn": o.SerialNumber,
131 }).Debugf("Changing ONU InternalState from %s to %s", src, dst)
132}
133
Matteo Scandolo9a3518c2019-08-13 14:36:01 -0700134func (o Onu) processOnuMessages(stream openolt.Openolt_EnableIndicationServer) {
135 onuLogger.WithFields(log.Fields{
Matteo Scandolo4747d292019-08-05 11:50:18 -0700136 "onuID": o.ID,
137 "onuSN": o.SerialNumber,
Matteo Scandolo9a3518c2019-08-13 14:36:01 -0700138 }).Debug("Started ONU Indication Channel")
139
140 for message := range o.channel {
141 onuLogger.WithFields(log.Fields{
142 "onuID": o.ID,
143 "onuSN": o.SerialNumber,
144 "messageType": message.Type,
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700145 }).Tracef("Received message on ONU Channel")
Matteo Scandolo9a3518c2019-08-13 14:36:01 -0700146
147 switch message.Type {
148 case OnuDiscIndication:
149 msg, _ := message.Data.(OnuDiscIndicationMessage)
150 o.sendOnuDiscIndication(msg, stream)
151 case OnuIndication:
152 msg, _ := message.Data.(OnuIndicationMessage)
153 o.sendOnuIndication(msg, stream)
154 case OMCI:
Matteo Scandoloc559ef12019-08-20 13:24:21 -0700155 msg, _ := message.Data.(OmciMessage)
Matteo Scandoloc559ef12019-08-20 13:24:21 -0700156 o.handleOmciMessage(msg, stream)
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700157 case FlowUpdate:
158 msg, _ := message.Data.(OnuFlowUpdateMessage)
159 o.handleFlowUpdate(msg, stream)
160 case StartEAPOL:
161 log.Infof("Receive StartEAPOL message on ONU channel")
162 go func() {
Matteo Scandolo47e69bb2019-08-28 15:41:12 -0700163 // TODO kill this thread
164 eapol.CreateWPASupplicant(o.ID, o.PonPortID, o.SerialNumber, o.InternalState, stream, o.eapolPktOutCh)
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700165 }()
Matteo Scandolo9a3518c2019-08-13 14:36:01 -0700166 default:
167 onuLogger.Warnf("Received unknown message data %v for type %v in OLT channel", message.Data, message.Type)
168 }
169 }
Matteo Scandolo4747d292019-08-05 11:50:18 -0700170}
171
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700172func (o Onu) processOmciMessages(stream openolt.Openolt_EnableIndicationServer) {
173 ch := omci.GetChannel()
174
175 onuLogger.WithFields(log.Fields{
176 "onuID": o.ID,
177 "onuSN": o.SerialNumber,
178 }).Debug("Started OMCI Indication Channel")
179
180 for message := range ch {
181 switch message.Type {
182 case omci.GemPortAdded:
183 log.WithFields(log.Fields{
184 "OnuId": message.Data.OnuId,
185 "IntfId": message.Data.IntfId,
186 }).Infof("GemPort Added")
187
188 // NOTE if we receive the GemPort but we don't have EAPOL flows
189 // go an intermediate state, otherwise start auth
190 if o.InternalState.Is("enabled") {
191 if err := o.InternalState.Event("add_gem_port"); err != nil {
192 log.Errorf("Can't go to gem_port_added: %v", err)
193 }
194 } else if o.InternalState.Is("eapol_flow_received"){
195 if err := o.InternalState.Event("start_auth"); err != nil {
196 log.Errorf("Can't go to auth_started: %v", err)
197 }
198 }
199 }
200 }
201}
202
Matteo Scandolo4747d292019-08-05 11:50:18 -0700203func (o Onu) NewSN(oltid int, intfid uint32, onuid uint32) *openolt.SerialNumber {
204
205 sn := new(openolt.SerialNumber)
206
Matteo Scandolo47e69bb2019-08-28 15:41:12 -0700207 //sn = new(openolt.SerialNumber)
Matteo Scandolo4747d292019-08-05 11:50:18 -0700208 sn.VendorId = []byte("BBSM")
209 sn.VendorSpecific = []byte{0, byte(oltid % 256), byte(intfid), byte(onuid)}
210
211 return sn
212}
213
214func (o Onu) sendOnuDiscIndication(msg OnuDiscIndicationMessage, stream openolt.Openolt_EnableIndicationServer) {
215 discoverData := &openolt.Indication_OnuDiscInd{OnuDiscInd: &openolt.OnuDiscIndication{
216 IntfId: msg.Onu.PonPortID,
217 SerialNumber: msg.Onu.SerialNumber,
218 }}
219 if err := stream.Send(&openolt.Indication{Data: discoverData}); err != nil {
Matteo Scandolo11006992019-08-28 11:29:46 -0700220 log.Errorf("Failed to send Indication_OnuDiscInd: %v", err)
Matteo Scandolo4747d292019-08-05 11:50:18 -0700221 }
Matteo Scandolo9a3518c2019-08-13 14:36:01 -0700222 o.InternalState.Event("discover")
223 onuLogger.WithFields(log.Fields{
Matteo Scandolo4747d292019-08-05 11:50:18 -0700224 "IntfId": msg.Onu.PonPortID,
225 "SerialNumber": msg.Onu.SerialNumber,
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700226 "OnuId": o.ID,
Matteo Scandolo4747d292019-08-05 11:50:18 -0700227 }).Debug("Sent Indication_OnuDiscInd")
228}
229
230func (o Onu) sendOnuIndication(msg OnuIndicationMessage, stream openolt.Openolt_EnableIndicationServer) {
231 // NOTE voltha returns an ID, but if we use that ID then it complains:
232 // expected_onu_id: 1, received_onu_id: 1024, event: ONU-id-mismatch, can happen if both voltha and the olt rebooted
233 // so we're using the internal ID that is 1
234 // o.ID = msg.OnuID
Matteo Scandolo9a3518c2019-08-13 14:36:01 -0700235 o.OperState.Event("enable")
Matteo Scandolo4747d292019-08-05 11:50:18 -0700236
237 indData := &openolt.Indication_OnuInd{OnuInd: &openolt.OnuIndication{
238 IntfId: o.PonPortID,
239 OnuId: o.ID,
Matteo Scandolo9a3518c2019-08-13 14:36:01 -0700240 OperState: o.OperState.Current(),
241 AdminState: o.OperState.Current(),
Matteo Scandolo4747d292019-08-05 11:50:18 -0700242 SerialNumber: o.SerialNumber,
243 }}
244 if err := stream.Send(&openolt.Indication{Data: indData}); err != nil {
Matteo Scandolo11006992019-08-28 11:29:46 -0700245 log.Errorf("Failed to send Indication_OnuInd: %v", err)
Matteo Scandolo4747d292019-08-05 11:50:18 -0700246 }
Matteo Scandolo9a3518c2019-08-13 14:36:01 -0700247 o.InternalState.Event("enable")
248 onuLogger.WithFields(log.Fields{
Matteo Scandolo4747d292019-08-05 11:50:18 -0700249 "IntfId": o.PonPortID,
250 "OnuId": o.ID,
251 "OperState": msg.OperState.String(),
252 "AdminState": msg.OperState.String(),
253 "SerialNumber": o.SerialNumber,
254 }).Debug("Sent Indication_OnuInd")
Matteo Scandoloc559ef12019-08-20 13:24:21 -0700255}
256
257func (o Onu) handleOmciMessage(msg OmciMessage, stream openolt.Openolt_EnableIndicationServer) {
258
259 onuLogger.WithFields(log.Fields{
260 "IntfId": o.PonPortID,
261 "SerialNumber": o.SerialNumber,
262 "omciPacket": msg.omciMsg.Pkt,
263 }).Tracef("Received OMCI message")
264
265 var omciInd openolt.OmciIndication
266 respPkt, err := omci.OmciSim(o.PonPortID, o.ID, HexDecode(msg.omciMsg.Pkt))
267 if err != nil {
268 onuLogger.Errorf("Error handling OMCI message %v", msg)
269 }
270
271 omciInd.IntfId = o.PonPortID
272 omciInd.OnuId = o.ID
273 omciInd.Pkt = respPkt
274
275 omci := &openolt.Indication_OmciInd{OmciInd: &omciInd}
276 if err := stream.Send(&openolt.Indication{Data: omci}); err != nil {
Matteo Scandolo11006992019-08-28 11:29:46 -0700277 onuLogger.Errorf("send omci indication failed: %v", err)
Matteo Scandoloc559ef12019-08-20 13:24:21 -0700278 }
279 onuLogger.WithFields(log.Fields{
280 "IntfId": o.PonPortID,
281 "SerialNumber": o.SerialNumber,
282 "omciPacket": omciInd.Pkt,
283 }).Tracef("Sent OMCI message")
284}
285
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700286func (o Onu) handleFlowUpdate(msg OnuFlowUpdateMessage, stream openolt.Openolt_EnableIndicationServer) {
287 onuLogger.WithFields(log.Fields{
288 "IntfId": msg.Flow.AccessIntfId,
289 "OnuId": msg.Flow.OnuId,
290 "EthType": fmt.Sprintf("%x", msg.Flow.Classifier.EthType),
291 "InnerVlan": msg.Flow.Classifier.IVid,
292 "OuterVlan": msg.Flow.Classifier.OVid,
293 "FlowType": msg.Flow.FlowType,
294 "FlowId": msg.Flow.FlowId,
295 "UniID": msg.Flow.UniId,
296 "PortNo": msg.Flow.PortNo,
297 }).Infof("ONU receives Flow")
298 if msg.Flow.Classifier.EthType == uint32(layers.EthernetTypeEAPOL) && msg.Flow.Classifier.OVid == 4091 {
299 // NOTE if we receive the EAPOL flows but we don't have GemPorts
300 // go an intermediate state, otherwise start auth
301 if o.InternalState.Is("enabled") {
302 if err := o.InternalState.Event("receive_eapol_flow"); err != nil {
303 log.Errorf("Can't go to eapol_flow_received: %v", err)
304 }
305 } else if o.InternalState.Is("gem_port_added"){
306 if err := o.InternalState.Event("start_auth"); err != nil {
307 log.Errorf("Can't go to auth_started: %v", err)
308 }
309 }
310 }
311}
312
Matteo Scandoloc559ef12019-08-20 13:24:21 -0700313// HexDecode converts the hex encoding to binary
314func HexDecode(pkt []byte) []byte {
315 p := make([]byte, len(pkt)/2)
316 for i, j := 0, 0; i < len(pkt); i, j = i+2, j+1 {
317 // Go figure this ;)
318 u := (pkt[i] & 15) + (pkt[i]>>6)*9
319 l := (pkt[i+1] & 15) + (pkt[i+1]>>6)*9
320 p[j] = u<<4 + l
321 }
322 onuLogger.Tracef("Omci decoded: %x.", p)
323 return p
Matteo Scandolo4747d292019-08-05 11:50:18 -0700324}