blob: 77e912c3c22900f3e66940e5aeb53d164f969aef [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 Scandolo3bc73742019-08-20 14:04:04 -070021 "github.com/google/gopacket/layers"
Matteo Scandolo4747d292019-08-05 11:50:18 -070022 "github.com/looplab/fsm"
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -070023 "github.com/opencord/bbsim/internal/bbsim/responders/dhcp"
24 "github.com/opencord/bbsim/internal/bbsim/responders/eapol"
25 bbsim "github.com/opencord/bbsim/internal/bbsim/types"
Matteo Scandoloc559ef12019-08-20 13:24:21 -070026 omci "github.com/opencord/omci-sim"
Matteo Scandolo3bc73742019-08-20 14:04:04 -070027 "github.com/opencord/voltha-protos/go/openolt"
Matteo Scandolo4747d292019-08-05 11:50:18 -070028 log "github.com/sirupsen/logrus"
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -070029 "net"
Matteo Scandolo4747d292019-08-05 11:50:18 -070030)
31
Matteo Scandolo9a3518c2019-08-13 14:36:01 -070032var onuLogger = log.WithFields(log.Fields{
33 "module": "ONU",
34})
35
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -070036func CreateONU(olt OltDevice, pon PonPort, id uint32, sTag int, cTag int) Onu {
Matteo Scandolo4747d292019-08-05 11:50:18 -070037
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -070038 o := Onu{
39 ID: id,
40 PonPortID: pon.ID,
41 PonPort: pon,
42 STag: sTag,
43 CTag: cTag,
44 HwAddress: net.HardwareAddr{0x2e, 0x60, 0x70, 0x13, byte(pon.ID), byte(id)},
45 // NOTE can we combine everything in a single channel?
46 channel: make(chan Message, 2048),
47 eapolPktOutCh: make(chan *bbsim.ByteMsg, 1024),
48 dhcpPktOutCh: make(chan *bbsim.ByteMsg, 1024),
49 }
50 o.SerialNumber = o.NewSN(olt.ID, pon.ID, o.ID)
Matteo Scandolo9a3518c2019-08-13 14:36:01 -070051
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -070052 // NOTE this state machine is used to track the operational
53 // state as requested by VOLTHA
54 o.OperState = getOperStateFSM(func(e *fsm.Event) {
55 onuLogger.WithFields(log.Fields{
56 "ID": o.ID,
57 }).Debugf("Changing ONU OperState from %s to %s", e.Src, e.Dst)
58 })
59
60 // NOTE this state machine is used to activate the OMCI, EAPOL and DHCP clients
61 o.InternalState = fsm.NewFSM(
62 "created",
63 fsm.Events{
64 // DEVICE Activation
65 {Name: "discover", Src: []string{"created"}, Dst: "discovered"},
66 {Name: "enable", Src: []string{"discovered"}, Dst: "enabled"},
67 {Name: "receive_eapol_flow", Src: []string{"enabled", "gem_port_added"}, Dst: "eapol_flow_received"},
68 {Name: "add_gem_port", Src: []string{"enabled", "eapol_flow_received"}, Dst: "gem_port_added"},
69 // EAPOL
70 {Name: "start_auth", Src: []string{"eapol_flow_received", "gem_port_added"}, Dst: "auth_started"},
71 {Name: "eap_start_sent", Src: []string{"auth_started"}, Dst: "eap_start_sent"},
72 {Name: "eap_response_identity_sent", Src: []string{"eap_start_sent"}, Dst: "eap_response_identity_sent"},
73 {Name: "eap_response_challenge_sent", Src: []string{"eap_response_identity_sent"}, Dst: "eap_response_challenge_sent"},
74 {Name: "eap_response_success_received", Src: []string{"eap_response_challenge_sent"}, Dst: "eap_response_success_received"},
75 {Name: "auth_failed", Src: []string{"auth_started", "eap_start_sent", "eap_response_identity_sent", "eap_response_challenge_sent"}, Dst: "auth_failed"},
76 // DHCP
77 {Name: "start_dhcp", Src: []string{"eap_response_success_received"}, Dst: "dhcp_started"},
78 {Name: "dhcp_discovery_sent", Src: []string{"dhcp_started"}, Dst: "dhcp_discovery_sent"},
79 {Name: "dhcp_request_sent", Src: []string{"dhcp_discovery_sent"}, Dst: "dhcp_request_sent"},
80 {Name: "dhcp_ack_received", Src: []string{"dhcp_request_sent"}, Dst: "dhcp_ack_received"},
81 {Name: "dhcp_failed", Src: []string{"dhcp_started", "dhcp_discovery_sent", "dhcp_request_sent"}, Dst: "dhcp_failed"},
82 },
83 fsm.Callbacks{
84 "enter_state": func(e *fsm.Event) {
85 o.logStateChange(e.Src, e.Dst)
Matteo Scandolo4747d292019-08-05 11:50:18 -070086 },
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -070087 "enter_auth_started": func(e *fsm.Event) {
88 o.logStateChange(e.Src, e.Dst)
89 msg := Message{
90 Type: StartEAPOL,
91 Data: PacketMessage{
92 PonPortID: o.PonPortID,
93 OnuID: o.ID,
94 },
95 }
96 o.channel <- msg
Matteo Scandolo4747d292019-08-05 11:50:18 -070097 },
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -070098 "enter_auth_failed": func(e *fsm.Event) {
99 onuLogger.WithFields(log.Fields{
100 "OnuId": o.ID,
101 "IntfId": o.PonPortID,
102 "OnuSn": o.Sn(),
103 }).Errorf("ONU failed to authenticate!")
104 },
105 "enter_dhcp_started": func(e *fsm.Event) {
106 msg := Message{
107 Type: StartDHCP,
108 Data: PacketMessage{
109 PonPortID: o.PonPortID,
110 OnuID: o.ID,
111 },
112 }
113 o.channel <- msg
114 },
115 "enter_dhcp_failed": func(e *fsm.Event) {
116 onuLogger.WithFields(log.Fields{
117 "OnuId": o.ID,
118 "IntfId": o.PonPortID,
119 "OnuSn": o.Sn(),
120 }).Errorf("ONU failed to DHCP!")
121 },
122 },
123 )
124 return o
Matteo Scandolo4747d292019-08-05 11:50:18 -0700125}
126
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700127func (o Onu) logStateChange(src string, dst string) {
128 onuLogger.WithFields(log.Fields{
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700129 "OnuId": o.ID,
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700130 "IntfId": o.PonPortID,
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700131 "OnuSn": o.Sn(),
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700132 }).Debugf("Changing ONU InternalState from %s to %s", src, dst)
133}
134
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700135func (o Onu) processOnuMessages(stream openolt.Openolt_EnableIndicationServer) {
Matteo Scandolo9a3518c2019-08-13 14:36:01 -0700136 onuLogger.WithFields(log.Fields{
Matteo Scandolo4747d292019-08-05 11:50:18 -0700137 "onuID": o.ID,
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700138 "onuSN": o.Sn(),
Matteo Scandolo9a3518c2019-08-13 14:36:01 -0700139 }).Debug("Started ONU Indication Channel")
140
141 for message := range o.channel {
142 onuLogger.WithFields(log.Fields{
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700143 "onuID": o.ID,
144 "onuSN": o.SerialNumber,
Matteo Scandolo9a3518c2019-08-13 14:36:01 -0700145 "messageType": message.Type,
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700146 }).Tracef("Received message on ONU Channel")
Matteo Scandolo9a3518c2019-08-13 14:36:01 -0700147
148 switch message.Type {
149 case OnuDiscIndication:
150 msg, _ := message.Data.(OnuDiscIndicationMessage)
151 o.sendOnuDiscIndication(msg, stream)
152 case OnuIndication:
153 msg, _ := message.Data.(OnuIndicationMessage)
154 o.sendOnuIndication(msg, stream)
155 case OMCI:
Matteo Scandoloc559ef12019-08-20 13:24:21 -0700156 msg, _ := message.Data.(OmciMessage)
Matteo Scandoloc559ef12019-08-20 13:24:21 -0700157 o.handleOmciMessage(msg, stream)
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700158 case FlowUpdate:
159 msg, _ := message.Data.(OnuFlowUpdateMessage)
160 o.handleFlowUpdate(msg, stream)
161 case StartEAPOL:
162 log.Infof("Receive StartEAPOL message on ONU channel")
163 go func() {
Matteo Scandolo47e69bb2019-08-28 15:41:12 -0700164 // TODO kill this thread
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700165 eapol.CreateWPASupplicant(
166 o.ID,
167 o.PonPortID,
168 o.Sn(),
169 o.InternalState,
170 stream,
171 o.eapolPktOutCh,
172 )
173 }()
174 case StartDHCP:
175 log.Infof("Receive StartDHCP message on ONU channel")
176 go func() {
177 // TODO kill this thread
178 dhcp.CreateDHCPClient(
179 o.ID,
180 o.PonPortID,
181 o.Sn(),
182 o.HwAddress,
183 o.CTag,
184 o.InternalState,
185 stream,
186 o.dhcpPktOutCh,
187 )
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700188 }()
Matteo Scandolo9a3518c2019-08-13 14:36:01 -0700189 default:
190 onuLogger.Warnf("Received unknown message data %v for type %v in OLT channel", message.Data, message.Type)
191 }
192 }
Matteo Scandolo4747d292019-08-05 11:50:18 -0700193}
194
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700195func (o Onu) processOmciMessages(stream openolt.Openolt_EnableIndicationServer) {
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700196 ch := omci.GetChannel()
197
198 onuLogger.WithFields(log.Fields{
199 "onuID": o.ID,
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700200 "onuSN": o.Sn(),
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700201 }).Debug("Started OMCI Indication Channel")
202
203 for message := range ch {
204 switch message.Type {
205 case omci.GemPortAdded:
206 log.WithFields(log.Fields{
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700207 "OnuId": message.Data.OnuId,
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700208 "IntfId": message.Data.IntfId,
209 }).Infof("GemPort Added")
210
211 // NOTE if we receive the GemPort but we don't have EAPOL flows
212 // go an intermediate state, otherwise start auth
213 if o.InternalState.Is("enabled") {
214 if err := o.InternalState.Event("add_gem_port"); err != nil {
215 log.Errorf("Can't go to gem_port_added: %v", err)
216 }
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700217 } else if o.InternalState.Is("eapol_flow_received") {
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700218 if err := o.InternalState.Event("start_auth"); err != nil {
219 log.Errorf("Can't go to auth_started: %v", err)
220 }
221 }
222 }
223 }
224}
225
Matteo Scandolo4747d292019-08-05 11:50:18 -0700226func (o Onu) NewSN(oltid int, intfid uint32, onuid uint32) *openolt.SerialNumber {
227
228 sn := new(openolt.SerialNumber)
229
Matteo Scandolo47e69bb2019-08-28 15:41:12 -0700230 //sn = new(openolt.SerialNumber)
Matteo Scandolo4747d292019-08-05 11:50:18 -0700231 sn.VendorId = []byte("BBSM")
232 sn.VendorSpecific = []byte{0, byte(oltid % 256), byte(intfid), byte(onuid)}
233
234 return sn
235}
236
237func (o Onu) sendOnuDiscIndication(msg OnuDiscIndicationMessage, stream openolt.Openolt_EnableIndicationServer) {
238 discoverData := &openolt.Indication_OnuDiscInd{OnuDiscInd: &openolt.OnuDiscIndication{
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700239 IntfId: msg.Onu.PonPortID,
Matteo Scandolo4747d292019-08-05 11:50:18 -0700240 SerialNumber: msg.Onu.SerialNumber,
241 }}
242 if err := stream.Send(&openolt.Indication{Data: discoverData}); err != nil {
Matteo Scandolo11006992019-08-28 11:29:46 -0700243 log.Errorf("Failed to send Indication_OnuDiscInd: %v", err)
Matteo Scandolo4747d292019-08-05 11:50:18 -0700244 }
Matteo Scandolo9a3518c2019-08-13 14:36:01 -0700245 o.InternalState.Event("discover")
246 onuLogger.WithFields(log.Fields{
Matteo Scandolo4747d292019-08-05 11:50:18 -0700247 "IntfId": msg.Onu.PonPortID,
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700248 "OnuSn": msg.Onu.Sn(),
249 "OnuId": o.ID,
Matteo Scandolo4747d292019-08-05 11:50:18 -0700250 }).Debug("Sent Indication_OnuDiscInd")
251}
252
253func (o Onu) sendOnuIndication(msg OnuIndicationMessage, stream openolt.Openolt_EnableIndicationServer) {
254 // NOTE voltha returns an ID, but if we use that ID then it complains:
255 // expected_onu_id: 1, received_onu_id: 1024, event: ONU-id-mismatch, can happen if both voltha and the olt rebooted
256 // so we're using the internal ID that is 1
257 // o.ID = msg.OnuID
Matteo Scandolo9a3518c2019-08-13 14:36:01 -0700258 o.OperState.Event("enable")
Matteo Scandolo4747d292019-08-05 11:50:18 -0700259
260 indData := &openolt.Indication_OnuInd{OnuInd: &openolt.OnuIndication{
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700261 IntfId: o.PonPortID,
262 OnuId: o.ID,
263 OperState: o.OperState.Current(),
264 AdminState: o.OperState.Current(),
Matteo Scandolo4747d292019-08-05 11:50:18 -0700265 SerialNumber: o.SerialNumber,
266 }}
267 if err := stream.Send(&openolt.Indication{Data: indData}); err != nil {
Matteo Scandolo11006992019-08-28 11:29:46 -0700268 log.Errorf("Failed to send Indication_OnuInd: %v", err)
Matteo Scandolo4747d292019-08-05 11:50:18 -0700269 }
Matteo Scandolo9a3518c2019-08-13 14:36:01 -0700270 o.InternalState.Event("enable")
271 onuLogger.WithFields(log.Fields{
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700272 "IntfId": o.PonPortID,
273 "OnuId": o.ID,
274 "OperState": msg.OperState.String(),
Matteo Scandolo4747d292019-08-05 11:50:18 -0700275 "AdminState": msg.OperState.String(),
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700276 "OnuSn": o.Sn(),
Matteo Scandolo4747d292019-08-05 11:50:18 -0700277 }).Debug("Sent Indication_OnuInd")
Matteo Scandoloc559ef12019-08-20 13:24:21 -0700278}
279
280func (o Onu) handleOmciMessage(msg OmciMessage, stream openolt.Openolt_EnableIndicationServer) {
281
282 onuLogger.WithFields(log.Fields{
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700283 "IntfId": o.PonPortID,
Matteo Scandoloc559ef12019-08-20 13:24:21 -0700284 "SerialNumber": o.SerialNumber,
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700285 "omciPacket": msg.omciMsg.Pkt,
Matteo Scandoloc559ef12019-08-20 13:24:21 -0700286 }).Tracef("Received OMCI message")
287
288 var omciInd openolt.OmciIndication
289 respPkt, err := omci.OmciSim(o.PonPortID, o.ID, HexDecode(msg.omciMsg.Pkt))
290 if err != nil {
291 onuLogger.Errorf("Error handling OMCI message %v", msg)
292 }
293
294 omciInd.IntfId = o.PonPortID
295 omciInd.OnuId = o.ID
296 omciInd.Pkt = respPkt
297
298 omci := &openolt.Indication_OmciInd{OmciInd: &omciInd}
299 if err := stream.Send(&openolt.Indication{Data: omci}); err != nil {
Matteo Scandolo11006992019-08-28 11:29:46 -0700300 onuLogger.Errorf("send omci indication failed: %v", err)
Matteo Scandoloc559ef12019-08-20 13:24:21 -0700301 }
302 onuLogger.WithFields(log.Fields{
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700303 "IntfId": o.PonPortID,
Matteo Scandoloc559ef12019-08-20 13:24:21 -0700304 "SerialNumber": o.SerialNumber,
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700305 "omciPacket": omciInd.Pkt,
Matteo Scandoloc559ef12019-08-20 13:24:21 -0700306 }).Tracef("Sent OMCI message")
307}
308
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700309func (o Onu) handleFlowUpdate(msg OnuFlowUpdateMessage, stream openolt.Openolt_EnableIndicationServer) {
310 onuLogger.WithFields(log.Fields{
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700311 "DstPort": msg.Flow.Classifier.DstPort,
312 "EthType": fmt.Sprintf("%x", msg.Flow.Classifier.EthType),
313 "FlowId": msg.Flow.FlowId,
314 "FlowType": msg.Flow.FlowType,
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700315 "InnerVlan": msg.Flow.Classifier.IVid,
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700316 "IntfId": msg.Flow.AccessIntfId,
317 "IpProto": msg.Flow.Classifier.IpProto,
318 "OnuId": msg.Flow.OnuId,
319 "OnuSn": o.Sn(),
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700320 "OuterVlan": msg.Flow.Classifier.OVid,
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700321 "PortNo": msg.Flow.PortNo,
322 "SrcPort": msg.Flow.Classifier.SrcPort,
323 "UniID": msg.Flow.UniId,
324 }).Debug("ONU receives Flow")
325
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700326 if msg.Flow.Classifier.EthType == uint32(layers.EthernetTypeEAPOL) && msg.Flow.Classifier.OVid == 4091 {
327 // NOTE if we receive the EAPOL flows but we don't have GemPorts
328 // go an intermediate state, otherwise start auth
329 if o.InternalState.Is("enabled") {
330 if err := o.InternalState.Event("receive_eapol_flow"); err != nil {
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700331 log.Warnf("Can't go to eapol_flow_received: %v", err)
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700332 }
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700333 } else if o.InternalState.Is("gem_port_added") {
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700334 if err := o.InternalState.Event("start_auth"); err != nil {
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700335 log.Warnf("Can't go to auth_started: %v", err)
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700336 }
337 }
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700338 } else if msg.Flow.Classifier.EthType == uint32(layers.EthernetTypeIPv4) &&
339 msg.Flow.Classifier.SrcPort == uint32(68) &&
340 msg.Flow.Classifier.DstPort == uint32(67) {
341 // NOTE we are receiving mulitple DHCP flows but we shouldn't call the transition multiple times
342 if err := o.InternalState.Event("start_dhcp"); err != nil {
343 log.Warnf("Can't go to dhcp_started: %v", err)
344 }
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700345 }
346}
347
Matteo Scandoloc559ef12019-08-20 13:24:21 -0700348// HexDecode converts the hex encoding to binary
349func HexDecode(pkt []byte) []byte {
350 p := make([]byte, len(pkt)/2)
351 for i, j := 0, 0; i < len(pkt); i, j = i+2, j+1 {
352 // Go figure this ;)
353 u := (pkt[i] & 15) + (pkt[i]>>6)*9
354 l := (pkt[i+1] & 15) + (pkt[i+1]>>6)*9
355 p[j] = u<<4 + l
356 }
357 onuLogger.Tracef("Omci decoded: %x.", p)
358 return p
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700359}