blob: 822ec6b55b633fcbae79a143603c57a0c65854c5 [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 Scandolo40e067f2019-10-16 16:59:41 -070020 "context"
Matteo Scandolo618a6582020-09-09 12:21:29 -070021 "encoding/hex"
Matteo Scandolo3bc73742019-08-20 14:04:04 -070022 "fmt"
Matteo Scandolof9d43412021-01-12 11:11:34 -080023 pb "github.com/opencord/bbsim/api/bbsim"
24 "github.com/opencord/bbsim/internal/bbsim/alarmsim"
25 bbsim "github.com/opencord/bbsim/internal/bbsim/types"
26 me "github.com/opencord/omci-lib-go/generated"
27 "strconv"
28
Matteo Scandolo4a036262020-08-17 15:56:13 -070029 "github.com/opencord/bbsim/internal/bbsim/packetHandlers"
30 "github.com/opencord/bbsim/internal/bbsim/responders/dhcp"
31 "github.com/opencord/bbsim/internal/bbsim/responders/eapol"
Zdravko Bozakov681364d2019-11-10 14:28:46 +010032 "net"
Zdravko Bozakov681364d2019-11-10 14:28:46 +010033 "time"
34
Matteo Scandolo3bc73742019-08-20 14:04:04 -070035 "github.com/google/gopacket/layers"
Matteo Scandoloeb6b5af2020-06-24 16:23:58 -070036 "github.com/jpillora/backoff"
Matteo Scandolo4747d292019-08-05 11:50:18 -070037 "github.com/looplab/fsm"
Matteo Scandolo40e067f2019-10-16 16:59:41 -070038 "github.com/opencord/bbsim/internal/common"
39 omcilib "github.com/opencord/bbsim/internal/common/omci"
Matteo Scandolof9d43412021-01-12 11:11:34 -080040 "github.com/opencord/omci-lib-go"
Matteo Scandolo4f4ac792020-10-01 16:33:21 -070041 "github.com/opencord/voltha-protos/v4/go/openolt"
42 "github.com/opencord/voltha-protos/v4/go/tech_profile"
Matteo Scandolo4747d292019-08-05 11:50:18 -070043 log "github.com/sirupsen/logrus"
44)
45
Matteo Scandolo9a3518c2019-08-13 14:36:01 -070046var onuLogger = log.WithFields(log.Fields{
47 "module": "ONU",
48})
49
Pragya Arya8bdb4532020-03-02 17:08:09 +053050type FlowKey struct {
Matteo Scandolo4f4ac792020-10-01 16:33:21 -070051 ID uint64
Pragya Arya8bdb4532020-03-02 17:08:09 +053052 Direction string
53}
54
Matteo Scandolo86e8ce62019-10-11 12:03:10 -070055type Onu struct {
Matteo Scandoloe811ae92019-12-10 17:50:14 -080056 ID uint32
57 PonPortID uint32
Matteo Scandoloeb6b5af2020-06-24 16:23:58 -070058 PonPort *PonPort
Matteo Scandoloe811ae92019-12-10 17:50:14 -080059 InternalState *fsm.FSM
Pragya Arya2225f202020-01-29 18:05:01 +053060 DiscoveryRetryDelay time.Duration // this is the time between subsequent Discovery Indication
61 DiscoveryDelay time.Duration // this is the time to send the first Discovery Indication
Matteo Scandolo4a036262020-08-17 15:56:13 -070062
63 Services []ServiceIf
64
65 Backoff *backoff.Backoff
Matteo Scandoloe811ae92019-12-10 17:50:14 -080066 // ONU State
Matteo Scandolo27428702019-10-11 16:21:16 -070067 // PortNo comes with flows and it's used when sending packetIndications,
68 // There is one PortNo per UNI Port, for now we're only storing the first one
Matteo Scandolo47ef64b2020-04-20 14:16:07 -070069 // FIXME add support for multiple UNIs (each UNI has a different PortNo)
Matteo Scandolo75ed5b92020-09-03 09:03:16 -070070 PortNo uint32
71 // deprecated (gemPort is on a Service basis)
Matteo Scandolo4a036262020-08-17 15:56:13 -070072 GemPortAdded bool
73 Flows []FlowKey
Matteo Scandolo4f4ac792020-10-01 16:33:21 -070074 FlowIds []uint64 // keep track of the flows we currently have in the ONU
Matteo Scandolo99f18462019-10-28 14:14:28 -070075
Matteo Scandolo86e8ce62019-10-11 12:03:10 -070076 OperState *fsm.FSM
77 SerialNumber *openolt.SerialNumber
78
Matteo Scandolof9d43412021-01-12 11:11:34 -080079 Channel chan bbsim.Message // this Channel is to track state changes OMCI messages, EAPOL and DHCP packets
Matteo Scandolo40e067f2019-10-16 16:59:41 -070080
81 // OMCI params
Matteo Scandoloeb6b5af2020-06-24 16:23:58 -070082 tid uint16
83 hpTid uint16
84 seqNumber uint16
Matteo Scandolo40e067f2019-10-16 16:59:41 -070085
Anand S Katti09541352020-01-29 15:54:01 +053086 DoneChannel chan bool // this channel is used to signal once the onu is complete (when the struct is used by BBR)
87 TrafficSchedulers *tech_profile.TrafficSchedulers
Matteo Scandolo86e8ce62019-10-11 12:03:10 -070088}
89
Matteo Scandolo99f18462019-10-28 14:14:28 -070090func (o *Onu) Sn() string {
Matteo Scandolo40e067f2019-10-16 16:59:41 -070091 return common.OnuSnToString(o.SerialNumber)
Matteo Scandolo86e8ce62019-10-11 12:03:10 -070092}
93
Matteo Scandolo4a036262020-08-17 15:56:13 -070094func CreateONU(olt *OltDevice, pon *PonPort, id uint32, delay time.Duration, isMock bool) *Onu {
Shrey Baidf8abccc2020-06-15 19:41:22 +053095 b := &backoff.Backoff{
96 //These are the defaults
Matteo Scandoloeb6b5af2020-06-24 16:23:58 -070097 Min: 5 * time.Second,
Shrey Baidf8abccc2020-06-15 19:41:22 +053098 Max: 35 * time.Second,
99 Factor: 1.5,
100 Jitter: false,
101 }
Matteo Scandolo4747d292019-08-05 11:50:18 -0700102
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700103 o := Onu{
Matteo Scandolo4a036262020-08-17 15:56:13 -0700104 ID: id,
Matteo Scandoloe811ae92019-12-10 17:50:14 -0800105 PonPortID: pon.ID,
106 PonPort: pon,
Matteo Scandoloe811ae92019-12-10 17:50:14 -0800107 PortNo: 0,
108 tid: 0x1,
109 hpTid: 0x8000,
110 seqNumber: 0,
111 DoneChannel: make(chan bool, 1),
Matteo Scandolo5ff80082019-12-20 13:20:57 -0800112 GemPortAdded: false,
Matteo Scandoloe811ae92019-12-10 17:50:14 -0800113 DiscoveryRetryDelay: 60 * time.Second, // this is used to send OnuDiscoveryIndications until an activate call is received
Pragya Arya8bdb4532020-03-02 17:08:09 +0530114 Flows: []FlowKey{},
Pragya Arya2225f202020-01-29 18:05:01 +0530115 DiscoveryDelay: delay,
Shrey Baidf8abccc2020-06-15 19:41:22 +0530116 Backoff: b,
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700117 }
Pragya Arya2225f202020-01-29 18:05:01 +0530118 o.SerialNumber = o.NewSN(olt.ID, pon.ID, id)
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700119 // NOTE this state machine is used to track the operational
120 // state as requested by VOLTHA
121 o.OperState = getOperStateFSM(func(e *fsm.Event) {
122 onuLogger.WithFields(log.Fields{
123 "ID": o.ID,
124 }).Debugf("Changing ONU OperState from %s to %s", e.Src, e.Dst)
125 })
126
127 // NOTE this state machine is used to activate the OMCI, EAPOL and DHCP clients
128 o.InternalState = fsm.NewFSM(
129 "created",
130 fsm.Events{
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700131 // DEVICE Lifecycle
Hardik Windlassad790cb2020-06-17 21:26:22 +0530132 {Name: "initialize", Src: []string{"created", "disabled", "pon_disabled"}, Dst: "initialized"},
133 {Name: "discover", Src: []string{"initialized"}, Dst: "discovered"},
134 {Name: "enable", Src: []string{"discovered", "pon_disabled"}, Dst: "enabled"},
Zdravko Bozakov681364d2019-11-10 14:28:46 +0100135 // NOTE should disabled state be different for oper_disabled (emulating an error) and admin_disabled (received a disabled call via VOLTHA)?
Matteo Scandolof380a972020-09-11 12:09:40 -0700136 {Name: "disable", Src: []string{"enabled", "pon_disabled"}, Dst: "disabled"},
Pragya Arya6a708d62020-01-01 17:17:20 +0530137 // ONU state when PON port is disabled but ONU is power ON(more states should be added in src?)
Matteo Scandolof380a972020-09-11 12:09:40 -0700138 {Name: "pon_disabled", Src: []string{"enabled"}, Dst: "pon_disabled"},
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700139 // BBR States
140 // TODO add start OMCI state
Zdravko Bozakov681364d2019-11-10 14:28:46 +0100141 {Name: "send_eapol_flow", Src: []string{"initialized"}, Dst: "eapol_flow_sent"},
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700142 {Name: "send_dhcp_flow", Src: []string{"eapol_flow_sent"}, Dst: "dhcp_flow_sent"},
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700143 },
144 fsm.Callbacks{
145 "enter_state": func(e *fsm.Event) {
146 o.logStateChange(e.Src, e.Dst)
Matteo Scandolo4747d292019-08-05 11:50:18 -0700147 },
Zdravko Bozakov681364d2019-11-10 14:28:46 +0100148 "enter_initialized": func(e *fsm.Event) {
149 // create new channel for ProcessOnuMessages Go routine
Matteo Scandolof9d43412021-01-12 11:11:34 -0800150 o.Channel = make(chan bbsim.Message, 2048)
Matteo Scandolod7cc6d32020-02-26 16:51:12 -0800151
152 if err := o.OperState.Event("enable"); err != nil {
153 onuLogger.WithFields(log.Fields{
154 "OnuId": o.ID,
155 "IntfId": o.PonPortID,
156 "OnuSn": o.Sn(),
157 }).Errorf("Cannot change ONU OperState to up: %s", err.Error())
158 }
159
Pragya Arya1cbefa42020-01-13 12:15:29 +0530160 if !isMock {
161 // start ProcessOnuMessages Go routine
Matteo Scandolo4a036262020-08-17 15:56:13 -0700162 go o.ProcessOnuMessages(olt.enableContext, olt.OpenoltStream, nil)
Pragya Arya1cbefa42020-01-13 12:15:29 +0530163 }
Zdravko Bozakov681364d2019-11-10 14:28:46 +0100164 },
165 "enter_discovered": func(e *fsm.Event) {
Matteo Scandolof9d43412021-01-12 11:11:34 -0800166 msg := bbsim.Message{
167 Type: bbsim.OnuDiscIndication,
168 Data: bbsim.OnuDiscIndicationMessage{
169 OperState: bbsim.UP,
Zdravko Bozakov681364d2019-11-10 14:28:46 +0100170 },
171 }
172 o.Channel <- msg
173 },
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700174 "enter_enabled": func(event *fsm.Event) {
Matteo Scandolof9d43412021-01-12 11:11:34 -0800175 msg := bbsim.Message{
176 Type: bbsim.OnuIndication,
177 Data: bbsim.OnuIndicationMessage{
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700178 OnuSN: o.SerialNumber,
179 PonPortID: o.PonPortID,
Matteo Scandolof9d43412021-01-12 11:11:34 -0800180 OperState: bbsim.UP,
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700181 },
182 }
183 o.Channel <- msg
Matteo Scandolo75ed5b92020-09-03 09:03:16 -0700184
185 // Once the ONU is enabled start listening for packets
186 for _, s := range o.Services {
Matteo Scandoloadc72a82020-09-08 18:46:08 -0700187 s.Initialize(o.PonPort.Olt.OpenoltStream)
Matteo Scandolo75ed5b92020-09-03 09:03:16 -0700188 }
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700189 },
190 "enter_disabled": func(event *fsm.Event) {
Matteo Scandolo47ef64b2020-04-20 14:16:07 -0700191
192 // clean the ONU state
Matteo Scandolo328c5f02020-06-26 14:16:39 -0700193 o.GemPortAdded = false
Matteo Scandolo47ef64b2020-04-20 14:16:07 -0700194 o.PortNo = 0
195 o.Flows = []FlowKey{}
196
Matteo Scandolo75ed5b92020-09-03 09:03:16 -0700197 // set the OperState to disabled
Matteo Scandolod7cc6d32020-02-26 16:51:12 -0800198 if err := o.OperState.Event("disable"); err != nil {
199 onuLogger.WithFields(log.Fields{
200 "OnuId": o.ID,
201 "IntfId": o.PonPortID,
202 "OnuSn": o.Sn(),
203 }).Errorf("Cannot change ONU OperState to down: %s", err.Error())
204 }
Matteo Scandolo47ef64b2020-04-20 14:16:07 -0700205
206 // send the OnuIndication DOWN event
Matteo Scandolof9d43412021-01-12 11:11:34 -0800207 msg := bbsim.Message{
208 Type: bbsim.OnuIndication,
209 Data: bbsim.OnuIndicationMessage{
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700210 OnuSN: o.SerialNumber,
211 PonPortID: o.PonPortID,
Matteo Scandolof9d43412021-01-12 11:11:34 -0800212 OperState: bbsim.DOWN,
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700213 },
214 }
215 o.Channel <- msg
Hardik Windlass7b3405b2020-07-08 15:10:05 +0530216
217 // verify all the flows removes are handled and
Zdravko Bozakov681364d2019-11-10 14:28:46 +0100218 // terminate the ONU's ProcessOnuMessages Go routine
Hardik Windlass7b3405b2020-07-08 15:10:05 +0530219 if len(o.FlowIds) == 0 {
220 close(o.Channel)
221 }
Matteo Scandolo75ed5b92020-09-03 09:03:16 -0700222
223 for _, s := range o.Services {
224 s.Disable()
225 }
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700226 },
Matteo Scandolo4a036262020-08-17 15:56:13 -0700227 // BBR states
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700228 "enter_eapol_flow_sent": func(e *fsm.Event) {
Matteo Scandolof9d43412021-01-12 11:11:34 -0800229 msg := bbsim.Message{
230 Type: bbsim.SendEapolFlow,
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700231 }
232 o.Channel <- msg
233 },
234 "enter_dhcp_flow_sent": func(e *fsm.Event) {
Matteo Scandolof9d43412021-01-12 11:11:34 -0800235 msg := bbsim.Message{
236 Type: bbsim.SendDhcpFlow,
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700237 }
238 o.Channel <- msg
239 },
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700240 },
241 )
Zdravko Bozakov681364d2019-11-10 14:28:46 +0100242
Matteo Scandolo27428702019-10-11 16:21:16 -0700243 return &o
Matteo Scandolo4747d292019-08-05 11:50:18 -0700244}
245
William Kurkian0418bc82019-11-06 12:16:24 -0500246func (o *Onu) logStateChange(src string, dst string) {
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700247 onuLogger.WithFields(log.Fields{
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700248 "OnuId": o.ID,
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700249 "IntfId": o.PonPortID,
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700250 "OnuSn": o.Sn(),
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700251 }).Debugf("Changing ONU InternalState from %s to %s", src, dst)
252}
253
Zdravko Bozakov681364d2019-11-10 14:28:46 +0100254// ProcessOnuMessages starts indication channel for each ONU
David Bainbridge103cf022019-12-16 20:11:35 +0000255func (o *Onu) ProcessOnuMessages(ctx context.Context, stream openolt.Openolt_EnableIndicationServer, client openolt.OpenoltClient) {
Matteo Scandolo9a3518c2019-08-13 14:36:01 -0700256 onuLogger.WithFields(log.Fields{
Zdravko Bozakov681364d2019-11-10 14:28:46 +0100257 "onuID": o.ID,
258 "onuSN": o.Sn(),
259 "ponPort": o.PonPortID,
260 }).Debug("Starting ONU Indication Channel")
Matteo Scandolo9a3518c2019-08-13 14:36:01 -0700261
David Bainbridge103cf022019-12-16 20:11:35 +0000262loop:
263 for {
264 select {
265 case <-ctx.Done():
266 onuLogger.WithFields(log.Fields{
267 "onuID": o.ID,
268 "onuSN": o.Sn(),
269 }).Tracef("ONU message handling canceled via context")
270 break loop
271 case message, ok := <-o.Channel:
272 if !ok || ctx.Err() != nil {
273 onuLogger.WithFields(log.Fields{
274 "onuID": o.ID,
275 "onuSN": o.Sn(),
276 }).Tracef("ONU message handling canceled via channel close")
277 break loop
Matteo Scandolo075b1892019-10-07 12:11:07 -0700278 }
David Bainbridge103cf022019-12-16 20:11:35 +0000279 onuLogger.WithFields(log.Fields{
280 "onuID": o.ID,
281 "onuSN": o.Sn(),
282 "messageType": message.Type,
283 }).Tracef("Received message on ONU Channel")
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700284
David Bainbridge103cf022019-12-16 20:11:35 +0000285 switch message.Type {
Matteo Scandolof9d43412021-01-12 11:11:34 -0800286 case bbsim.OnuDiscIndication:
287 msg, _ := message.Data.(bbsim.OnuDiscIndicationMessage)
David Bainbridge103cf022019-12-16 20:11:35 +0000288 // NOTE we need to slow down and send ONU Discovery Indication in batches to better emulate a real scenario
Pragya Arya2225f202020-01-29 18:05:01 +0530289 time.Sleep(o.DiscoveryDelay)
David Bainbridge103cf022019-12-16 20:11:35 +0000290 o.sendOnuDiscIndication(msg, stream)
Matteo Scandolof9d43412021-01-12 11:11:34 -0800291 case bbsim.OnuIndication:
292 msg, _ := message.Data.(bbsim.OnuIndicationMessage)
David Bainbridge103cf022019-12-16 20:11:35 +0000293 o.sendOnuIndication(msg, stream)
Matteo Scandolof9d43412021-01-12 11:11:34 -0800294 case bbsim.OMCI:
295 msg, _ := message.Data.(bbsim.OmciMessage)
296 o.handleOmciRequest(msg, stream)
297 case bbsim.UniStatusAlarm:
298 msg, _ := message.Data.(bbsim.UniStatusAlarmMessage)
299 pkt := omcilib.CreateUniStatusAlarm(msg.AdminState, msg.EntityID)
300 if err := o.sendOmciIndication(pkt, 0, stream); err != nil {
301 onuLogger.WithFields(log.Fields{
302 "IntfId": o.PonPortID,
303 "SerialNumber": o.Sn(),
304 "omciPacket": pkt,
305 "adminState": msg.AdminState,
306 "entityID": msg.EntityID,
307 }).Errorf("failed-to-send-UNI-Link-Alarm: %v", err)
308 }
309 onuLogger.WithFields(log.Fields{
310 "IntfId": o.PonPortID,
311 "SerialNumber": o.Sn(),
312 "omciPacket": pkt,
313 "adminState": msg.AdminState,
314 "entityID": msg.EntityID,
315 }).Trace("UNI-Link-alarm-sent")
316 case bbsim.FlowAdd:
317 msg, _ := message.Data.(bbsim.OnuFlowUpdateMessage)
Matteo Scandoloeb6b5af2020-06-24 16:23:58 -0700318 o.handleFlowAdd(msg)
Matteo Scandolof9d43412021-01-12 11:11:34 -0800319 case bbsim.FlowRemoved:
320 msg, _ := message.Data.(bbsim.OnuFlowUpdateMessage)
Matteo Scandoloeb6b5af2020-06-24 16:23:58 -0700321 o.handleFlowRemove(msg)
Matteo Scandolof9d43412021-01-12 11:11:34 -0800322 case bbsim.OnuPacketOut:
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700323
Matteo Scandolof9d43412021-01-12 11:11:34 -0800324 msg, _ := message.Data.(bbsim.OnuPacketMessage)
David Bainbridge103cf022019-12-16 20:11:35 +0000325
Matteo Scandoloadc72a82020-09-08 18:46:08 -0700326 onuLogger.WithFields(log.Fields{
David Bainbridge103cf022019-12-16 20:11:35 +0000327 "IntfId": msg.IntfId,
328 "OnuId": msg.OnuId,
329 "pktType": msg.Type,
330 }).Trace("Received OnuPacketOut Message")
331
Matteo Scandolo618a6582020-09-09 12:21:29 -0700332 if msg.Type == packetHandlers.EAPOL || msg.Type == packetHandlers.DHCP {
333
334 service, err := o.findServiceByMacAddress(msg.MacAddress)
335 if err != nil {
336 onuLogger.WithFields(log.Fields{
337 "IntfId": msg.IntfId,
338 "OnuId": msg.OnuId,
339 "pktType": msg.Type,
340 "MacAddress": msg.MacAddress,
341 "Pkt": hex.EncodeToString(msg.Packet.Data()),
342 "OnuSn": o.Sn(),
343 }).Error("Cannot find Service associated with packet")
344 return
345 }
346 service.PacketCh <- msg
347 } else if msg.Type == packetHandlers.IGMP {
348 // if it's an IGMP packet we assume we have a single IGMP service
349 for _, s := range o.Services {
350 service := s.(*Service)
351
352 if service.NeedsIgmp {
353 service.PacketCh <- msg
354 }
355 }
David Bainbridge103cf022019-12-16 20:11:35 +0000356 }
Matteo Scandolo4a036262020-08-17 15:56:13 -0700357
Matteo Scandolof9d43412021-01-12 11:11:34 -0800358 case bbsim.OnuPacketIn:
David Bainbridge103cf022019-12-16 20:11:35 +0000359 // NOTE we only receive BBR packets here.
360 // Eapol.HandleNextPacket can handle both BBSim and BBr cases so the call is the same
361 // in the DHCP case VOLTHA only act as a proxy, the behaviour is completely different thus we have a dhcp.HandleNextBbrPacket
Matteo Scandolof9d43412021-01-12 11:11:34 -0800362 msg, _ := message.Data.(bbsim.OnuPacketMessage)
David Bainbridge103cf022019-12-16 20:11:35 +0000363
364 log.WithFields(log.Fields{
365 "IntfId": msg.IntfId,
366 "OnuId": msg.OnuId,
367 "pktType": msg.Type,
368 }).Trace("Received OnuPacketIn Message")
369
370 if msg.Type == packetHandlers.EAPOL {
Matteo Scandolo4a036262020-08-17 15:56:13 -0700371 eapol.HandleNextPacket(msg.OnuId, msg.IntfId, msg.GemPortId, o.Sn(), o.PortNo, o.InternalState, msg.Packet, stream, client)
David Bainbridge103cf022019-12-16 20:11:35 +0000372 } else if msg.Type == packetHandlers.DHCP {
Matteo Scandolo4a036262020-08-17 15:56:13 -0700373 _ = dhcp.HandleNextBbrPacket(o.ID, o.PonPortID, o.Sn(), o.DoneChannel, msg.Packet, client)
David Bainbridge103cf022019-12-16 20:11:35 +0000374 }
Matteo Scandolo7f656fb2020-09-08 15:18:15 -0700375 // BBR specific messages
Matteo Scandolof9d43412021-01-12 11:11:34 -0800376 case bbsim.OmciIndication:
377 msg, _ := message.Data.(bbsim.OmciIndicationMessage)
378 o.handleOmciResponse(msg, client)
379 case bbsim.SendEapolFlow:
David Bainbridge103cf022019-12-16 20:11:35 +0000380 o.sendEapolFlow(client)
Matteo Scandolof9d43412021-01-12 11:11:34 -0800381 case bbsim.SendDhcpFlow:
David Bainbridge103cf022019-12-16 20:11:35 +0000382 o.sendDhcpFlow(client)
383 default:
384 onuLogger.Warnf("Received unknown message data %v for type %v in OLT Channel", message.Data, message.Type)
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700385 }
Matteo Scandolo9a3518c2019-08-13 14:36:01 -0700386 }
387 }
Zdravko Bozakov681364d2019-11-10 14:28:46 +0100388 onuLogger.WithFields(log.Fields{
389 "onuID": o.ID,
390 "onuSN": o.Sn(),
391 }).Debug("Stopped handling ONU Indication Channel")
Matteo Scandolo4747d292019-08-05 11:50:18 -0700392}
393
Zdravko Bozakov681364d2019-11-10 14:28:46 +0100394func (o Onu) NewSN(oltid int, intfid uint32, onuid uint32) *openolt.SerialNumber {
Matteo Scandolo4747d292019-08-05 11:50:18 -0700395
396 sn := new(openolt.SerialNumber)
397
Matteo Scandolo47e69bb2019-08-28 15:41:12 -0700398 //sn = new(openolt.SerialNumber)
Matteo Scandolo4747d292019-08-05 11:50:18 -0700399 sn.VendorId = []byte("BBSM")
400 sn.VendorSpecific = []byte{0, byte(oltid % 256), byte(intfid), byte(onuid)}
401
402 return sn
403}
404
Matteo Scandolof9d43412021-01-12 11:11:34 -0800405func (o *Onu) sendOnuDiscIndication(msg bbsim.OnuDiscIndicationMessage, stream openolt.Openolt_EnableIndicationServer) {
Matteo Scandolo4747d292019-08-05 11:50:18 -0700406 discoverData := &openolt.Indication_OnuDiscInd{OnuDiscInd: &openolt.OnuDiscIndication{
Matteo Scandolof9d43412021-01-12 11:11:34 -0800407 IntfId: o.PonPortID,
408 SerialNumber: o.SerialNumber,
Matteo Scandolo4747d292019-08-05 11:50:18 -0700409 }}
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700410
Matteo Scandolo4747d292019-08-05 11:50:18 -0700411 if err := stream.Send(&openolt.Indication{Data: discoverData}); err != nil {
Matteo Scandolo11006992019-08-28 11:29:46 -0700412 log.Errorf("Failed to send Indication_OnuDiscInd: %v", err)
Matteo Scandolo99f18462019-10-28 14:14:28 -0700413 return
Matteo Scandolo4747d292019-08-05 11:50:18 -0700414 }
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700415
Matteo Scandolo9a3518c2019-08-13 14:36:01 -0700416 onuLogger.WithFields(log.Fields{
Matteo Scandolof9d43412021-01-12 11:11:34 -0800417 "IntfId": o.PonPortID,
418 "OnuSn": o.Sn(),
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700419 "OnuId": o.ID,
Matteo Scandolo4747d292019-08-05 11:50:18 -0700420 }).Debug("Sent Indication_OnuDiscInd")
Matteo Scandolof9d43412021-01-12 11:11:34 -0800421 publishEvent("ONU-discovery-indication-sent", int32(o.PonPortID), int32(o.ID), o.Sn())
Matteo Scandoloe811ae92019-12-10 17:50:14 -0800422
423 // after DiscoveryRetryDelay check if the state is the same and in case send a new OnuDiscIndication
424 go func(delay time.Duration) {
Matteo Scandolo569e7172019-12-20 11:51:51 -0800425 time.Sleep(delay)
Matteo Scandoloe811ae92019-12-10 17:50:14 -0800426 if o.InternalState.Current() == "discovered" {
Matteo Scandoloe811ae92019-12-10 17:50:14 -0800427 o.sendOnuDiscIndication(msg, stream)
428 }
429 }(o.DiscoveryRetryDelay)
Matteo Scandolo4747d292019-08-05 11:50:18 -0700430}
431
Matteo Scandolof9d43412021-01-12 11:11:34 -0800432func (o *Onu) sendOnuIndication(msg bbsim.OnuIndicationMessage, stream openolt.Openolt_EnableIndicationServer) {
Matteo Scandolo4747d292019-08-05 11:50:18 -0700433 // NOTE voltha returns an ID, but if we use that ID then it complains:
434 // expected_onu_id: 1, received_onu_id: 1024, event: ONU-id-mismatch, can happen if both voltha and the olt rebooted
435 // so we're using the internal ID that is 1
436 // o.ID = msg.OnuID
Matteo Scandolo4747d292019-08-05 11:50:18 -0700437
438 indData := &openolt.Indication_OnuInd{OnuInd: &openolt.OnuIndication{
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700439 IntfId: o.PonPortID,
440 OnuId: o.ID,
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700441 OperState: msg.OperState.String(),
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700442 AdminState: o.OperState.Current(),
Matteo Scandolo4747d292019-08-05 11:50:18 -0700443 SerialNumber: o.SerialNumber,
444 }}
445 if err := stream.Send(&openolt.Indication{Data: indData}); err != nil {
Matteo Scandolod7cc6d32020-02-26 16:51:12 -0800446 // NOTE do we need to transition to a broken state?
Matteo Scandolo11006992019-08-28 11:29:46 -0700447 log.Errorf("Failed to send Indication_OnuInd: %v", err)
Matteo Scandolo75ed5b92020-09-03 09:03:16 -0700448 return
Matteo Scandolo4747d292019-08-05 11:50:18 -0700449 }
Matteo Scandolo9a3518c2019-08-13 14:36:01 -0700450 onuLogger.WithFields(log.Fields{
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700451 "IntfId": o.PonPortID,
452 "OnuId": o.ID,
453 "OperState": msg.OperState.String(),
Matteo Scandolo4747d292019-08-05 11:50:18 -0700454 "AdminState": msg.OperState.String(),
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700455 "OnuSn": o.Sn(),
Matteo Scandolo4747d292019-08-05 11:50:18 -0700456 }).Debug("Sent Indication_OnuInd")
Matteo Scandolo10f965c2019-09-24 10:40:46 -0700457
Matteo Scandoloc559ef12019-08-20 13:24:21 -0700458}
459
Matteo Scandolof9d43412021-01-12 11:11:34 -0800460func (o *Onu) HandleShutdownONU() error {
461
462 dyingGasp := pb.ONUAlarmRequest{
463 AlarmType: "DYING_GASP",
464 SerialNumber: o.Sn(),
465 Status: "on",
466 }
467
468 if err := alarmsim.SimulateOnuAlarm(&dyingGasp, o.ID, o.PonPortID, o.PonPort.Olt.channel); err != nil {
469 onuLogger.WithFields(log.Fields{
470 "OnuId": o.ID,
471 "IntfId": o.PonPortID,
472 "OnuSn": o.Sn(),
473 }).Errorf("Cannot send Dying Gasp: %s", err.Error())
474 return err
475 }
476
477 losReq := pb.ONUAlarmRequest{
478 AlarmType: "ONU_ALARM_LOS",
479 SerialNumber: o.Sn(),
480 Status: "on",
481 }
482
483 if err := alarmsim.SimulateOnuAlarm(&losReq, o.ID, o.PonPortID, o.PonPort.Olt.channel); err != nil {
484 onuLogger.WithFields(log.Fields{
485 "OnuId": o.ID,
486 "IntfId": o.PonPortID,
487 "OnuSn": o.Sn(),
488 }).Errorf("Cannot send LOS: %s", err.Error())
489
490 return err
491 }
492
493 // TODO if it's the last ONU on the PON, then send a PON LOS
494
495 if err := o.InternalState.Event("disable"); err != nil {
496 onuLogger.WithFields(log.Fields{
497 "OnuId": o.ID,
498 "IntfId": o.PonPortID,
499 "OnuSn": o.Sn(),
500 }).Errorf("Cannot shutdown ONU: %s", err.Error())
501 return err
502 }
503
504 return nil
505}
506
507func (o *Onu) HandlePowerOnONU() error {
508 intitalState := o.InternalState.Current()
509
510 // initialize the ONU
511 if intitalState == "created" || intitalState == "disabled" {
512 if err := o.InternalState.Event("initialize"); err != nil {
513 onuLogger.WithFields(log.Fields{
514 "OnuId": o.ID,
515 "IntfId": o.PonPortID,
516 "OnuSn": o.Sn(),
517 }).Errorf("Cannot poweron ONU: %s", err.Error())
518 return err
519 }
520 }
521
522 // turn off the LOS Alarm
523 losReq := pb.ONUAlarmRequest{
524 AlarmType: "ONU_ALARM_LOS",
525 SerialNumber: o.Sn(),
526 Status: "off",
527 }
528
529 if err := alarmsim.SimulateOnuAlarm(&losReq, o.ID, o.PonPortID, o.PonPort.Olt.channel); err != nil {
530 onuLogger.WithFields(log.Fields{
531 "OnuId": o.ID,
532 "IntfId": o.PonPortID,
533 "OnuSn": o.Sn(),
534 }).Errorf("Cannot send LOS: %s", err.Error())
535 return err
536 }
537
538 // Send a ONU Discovery indication
539 if err := o.InternalState.Event("discover"); err != nil {
540 onuLogger.WithFields(log.Fields{
541 "OnuId": o.ID,
542 "IntfId": o.PonPortID,
543 "OnuSn": o.Sn(),
544 }).Errorf("Cannot poweron ONU: %s", err.Error())
545 return err
546 }
547
548 // move o directly to enable state only when its a powercycle case
549 // in case of first time o poweron o will be moved to enable on
550 // receiving ActivateOnu request from openolt adapter
551 if intitalState == "disabled" {
552 if err := o.InternalState.Event("enable"); err != nil {
553 onuLogger.WithFields(log.Fields{
554 "OnuId": o.ID,
555 "IntfId": o.PonPortID,
556 "OnuSn": o.Sn(),
557 }).Errorf("Cannot enable ONU: %s", err.Error())
558 return err
559 }
560 }
561
562 return nil
563}
564
565func (o *Onu) SetAlarm(alarmType string, status string) error {
566 alarmReq := pb.ONUAlarmRequest{
567 AlarmType: alarmType,
568 SerialNumber: o.Sn(),
569 Status: status,
570 }
571
572 err := alarmsim.SimulateOnuAlarm(&alarmReq, o.ID, o.PonPortID, o.PonPort.Olt.channel)
573 if err != nil {
574 return err
575 }
576 return nil
577}
578
579func (o *Onu) publishOmciEvent(msg bbsim.OmciMessage) {
Pragya Arya324337e2020-02-20 14:35:08 +0530580 if olt.PublishEvents {
Matteo Scandolof9d43412021-01-12 11:11:34 -0800581 _, omciMsg, err := omcilib.ParseOpenOltOmciPacket(msg.OmciMsg.Pkt)
Pragya Arya324337e2020-02-20 14:35:08 +0530582 if err != nil {
583 log.Errorf("error in getting msgType %v", err)
584 return
585 }
Matteo Scandolof9d43412021-01-12 11:11:34 -0800586 if omciMsg.MessageType == omci.MibUploadRequestType {
Pragya Arya324337e2020-02-20 14:35:08 +0530587 o.seqNumber = 0
588 publishEvent("MIB-upload-received", int32(o.PonPortID), int32(o.ID), common.OnuSnToString(o.SerialNumber))
Matteo Scandolof9d43412021-01-12 11:11:34 -0800589 } else if omciMsg.MessageType == omci.MibUploadNextRequestType {
Pragya Arya324337e2020-02-20 14:35:08 +0530590 o.seqNumber++
591 if o.seqNumber > 290 {
592 publishEvent("MIB-upload-done", int32(o.PonPortID), int32(o.ID), common.OnuSnToString(o.SerialNumber))
593 }
594 }
595 }
596}
597
Scott Bakerb90c4312020-03-12 21:33:25 -0700598// Create a TestResponse packet and send it
Matteo Scandolof9d43412021-01-12 11:11:34 -0800599func (o *Onu) sendTestResult(msg bbsim.OmciMessage, stream openolt.Openolt_EnableIndicationServer) error {
600 resp, err := omcilib.BuildTestResult(msg.OmciMsg.Pkt)
Scott Bakerb90c4312020-03-12 21:33:25 -0700601 if err != nil {
602 return err
603 }
604
605 var omciInd openolt.OmciIndication
606 omciInd.IntfId = o.PonPortID
607 omciInd.OnuId = o.ID
608 omciInd.Pkt = resp
609
610 omci := &openolt.Indication_OmciInd{OmciInd: &omciInd}
611 if err := stream.Send(&openolt.Indication{Data: omci}); err != nil {
Scott Bakerb90c4312020-03-12 21:33:25 -0700612 return err
613 }
614 onuLogger.WithFields(log.Fields{
615 "IntfId": o.PonPortID,
616 "SerialNumber": o.Sn(),
617 "omciPacket": omciInd.Pkt,
618 }).Tracef("Sent TestResult OMCI message")
619
620 return nil
621}
622
Matteo Scandolof9d43412021-01-12 11:11:34 -0800623// handleOmciRequest is responsible to parse the OMCI packets received from the openolt adapter
624// and generate the appropriate response to it
625func (o *Onu) handleOmciRequest(msg bbsim.OmciMessage, stream openolt.Openolt_EnableIndicationServer) {
626
627 omciPkt, omciMsg, err := omcilib.ParseOpenOltOmciPacket(msg.OmciMsg.Pkt)
628 if err != nil {
629 log.WithFields(log.Fields{
630 "IntfId": o.PonPortID,
631 "SerialNumber": o.Sn(),
632 "omciPacket": msg.OmciMsg.Pkt,
633 }).Error("cannot-parse-OMCI-packet")
634 }
Matteo Scandoloc559ef12019-08-20 13:24:21 -0700635
636 onuLogger.WithFields(log.Fields{
Matteo Scandolof9d43412021-01-12 11:11:34 -0800637 "omciMsgType": omciMsg.MessageType,
638 "transCorrId": strconv.FormatInt(int64(omciMsg.TransactionID), 16),
639 "DeviceIdent": omciMsg.DeviceIdentifier,
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700640 "IntfId": o.PonPortID,
Matteo Scandolo27428702019-10-11 16:21:16 -0700641 "SerialNumber": o.Sn(),
Matteo Scandolof9d43412021-01-12 11:11:34 -0800642 }).Trace("omci-message-decoded")
643
644 var responsePkt []byte
645 switch omciMsg.MessageType {
646 case omci.MibResetRequestType:
647 responsePkt, _ = omcilib.CreateMibResetResponse(omciMsg.TransactionID)
648 case omci.MibUploadRequestType:
649 responsePkt, _ = omcilib.CreateMibUploadResponse(omciMsg.TransactionID)
650 case omci.MibUploadNextRequestType:
651 responsePkt, _ = omcilib.CreateMibUploadNextResponse(omciPkt, omciMsg)
652 case omci.GetRequestType:
653 responsePkt, _ = omcilib.CreateGetResponse(omciPkt, omciMsg)
654 case omci.SetRequestType:
655 responsePkt, _ = omcilib.CreateSetResponse(omciPkt, omciMsg)
656
657 msgObj, _ := omcilib.ParseSetRequest(omciPkt)
658 switch msgObj.EntityClass {
659 case me.PhysicalPathTerminationPointEthernetUniClassID:
660 // if we're Setting a PPTP state
661 // we need to send the appropriate alarm
662
663 if msgObj.EntityInstance == 257 {
664 // for now we're only caring about the first UNI
665 // NOTE that the EntityID for the UNI port is for now hardcoded in
666 // omci/mibpackets.go where the PhysicalPathTerminationPointEthernetUni
667 // are reported during the MIB Upload sequence
668 adminState := msgObj.Attributes["AdministrativeState"].(uint8)
669 msg := bbsim.Message{
670 Type: bbsim.UniStatusAlarm,
671 Data: bbsim.UniStatusAlarmMessage{
672 OnuSN: o.SerialNumber,
673 OnuID: o.ID,
674 AdminState: adminState,
675 EntityID: msgObj.EntityInstance,
676 },
677 }
678 o.Channel <- msg
679 }
680 }
681 case omci.CreateRequestType:
682 responsePkt, _ = omcilib.CreateCreateResponse(omciPkt, omciMsg)
683 case omci.DeleteRequestType:
684 responsePkt, _ = omcilib.CreateDeleteResponse(omciPkt, omciMsg)
685 case omci.RebootRequestType:
686
687 responsePkt, _ = omcilib.CreateRebootResponse(omciPkt, omciMsg)
688
689 // powercycle the ONU
690 go func() {
691 // we run this in a separate goroutine so that
692 // the RebootRequestResponse is sent to VOLTHA
693 onuLogger.WithFields(log.Fields{
694 "IntfId": o.PonPortID,
695 "SerialNumber": o.Sn(),
696 }).Debug("shutting-down-onu-for-omci-reboot")
697 _ = o.HandleShutdownONU()
698 time.Sleep(10 * time.Second)
699 onuLogger.WithFields(log.Fields{
700 "IntfId": o.PonPortID,
701 "SerialNumber": o.Sn(),
702 }).Debug("power-on-onu-for-omci-reboot")
703 _ = o.HandlePowerOnONU()
704 }()
705 case omci.TestRequestType:
706
707 // Test message is special, it requires sending two packets:
708 // first packet: TestResponse, says whether test was started successully, handled by omci-sim
709 // second packet, TestResult, reports the result of running the self-test
710 // TestResult can come some time after a TestResponse
711 // TODO: Implement some delay between the TestResponse and the TestResult
712 isTest, err := omcilib.IsTestRequest(msg.OmciMsg.Pkt)
713 if (err == nil) && (isTest) {
714 if sendErr := o.sendTestResult(msg, stream); sendErr != nil {
715 onuLogger.WithFields(log.Fields{
716 "IntfId": o.PonPortID,
717 "SerialNumber": o.Sn(),
718 "omciPacket": msg.OmciMsg.Pkt,
719 "msg": msg,
720 "err": sendErr,
721 }).Error("send-TestResult-indication-failed")
722 }
723 }
724
725 default:
726 log.WithFields(log.Fields{
727 "omciMsgType": omciMsg.MessageType,
728 "transCorrId": omciMsg.TransactionID,
729 "IntfId": o.PonPortID,
730 "SerialNumber": o.Sn(),
731 }).Warnf("OMCI-message-not-supported")
732 }
733
734 if responsePkt != nil {
735 if err := o.sendOmciIndication(responsePkt, omciMsg.TransactionID, stream); err != nil {
736 onuLogger.WithFields(log.Fields{
737 "IntfId": o.PonPortID,
738 "SerialNumber": o.Sn(),
739 "omciPacket": responsePkt,
740 "omciMsgType": omciMsg.MessageType,
741 "transCorrId": omciMsg.TransactionID,
742 }).Errorf("failed-to-send-omci-message: %v", err)
743 }
744 }
Matteo Scandoloc559ef12019-08-20 13:24:21 -0700745
Pragya Arya324337e2020-02-20 14:35:08 +0530746 o.publishOmciEvent(msg)
Matteo Scandolof9d43412021-01-12 11:11:34 -0800747}
Pragya Arya324337e2020-02-20 14:35:08 +0530748
Matteo Scandolof9d43412021-01-12 11:11:34 -0800749// sendOmciIndication takes an OMCI packet and sends it up to VOLTHA
750func (o *Onu) sendOmciIndication(responsePkt []byte, txId uint16, stream bbsim.Stream) error {
751 indication := &openolt.Indication_OmciInd{
752 OmciInd: &openolt.OmciIndication{
753 IntfId: o.PonPortID,
754 OnuId: o.ID,
755 Pkt: responsePkt,
756 },
Matteo Scandoloc559ef12019-08-20 13:24:21 -0700757 }
Matteo Scandolof9d43412021-01-12 11:11:34 -0800758 if err := stream.Send(&openolt.Indication{Data: indication}); err != nil {
759 return fmt.Errorf("failed-to-send-omci-message: %v", err)
Matteo Scandoloc559ef12019-08-20 13:24:21 -0700760 }
761 onuLogger.WithFields(log.Fields{
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700762 "IntfId": o.PonPortID,
Matteo Scandolo27428702019-10-11 16:21:16 -0700763 "SerialNumber": o.Sn(),
Matteo Scandolof9d43412021-01-12 11:11:34 -0800764 "omciPacket": indication.OmciInd.Pkt,
765 "transCorrId": txId,
766 }).Trace("omci-message-sent")
767 return nil
Matteo Scandoloc559ef12019-08-20 13:24:21 -0700768}
769
Matteo Scandolo27428702019-10-11 16:21:16 -0700770func (o *Onu) storePortNumber(portNo uint32) {
Matteo Scandolo813402b2019-10-23 19:24:52 -0700771 // NOTE this needed only as long as we don't support multiple UNIs
Matteo Scandolo27428702019-10-11 16:21:16 -0700772 // we need to add support for multiple UNIs
773 // the action plan is:
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700774 // - refactor the omcisim-sim library to use https://github.com/cboling/omci instead of canned messages
Matteo Scandolo27428702019-10-11 16:21:16 -0700775 // - change the library so that it reports a single UNI and remove this workaroung
776 // - add support for multiple UNIs in BBSim
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700777 if o.PortNo == 0 || portNo < o.PortNo {
Matteo Scandolo813402b2019-10-23 19:24:52 -0700778 onuLogger.WithFields(log.Fields{
779 "IntfId": o.PonPortID,
780 "OnuId": o.ID,
781 "SerialNumber": o.Sn(),
782 "OnuPortNo": o.PortNo,
783 "FlowPortNo": portNo,
784 }).Debug("Storing ONU portNo")
Matteo Scandolo27428702019-10-11 16:21:16 -0700785 o.PortNo = portNo
786 }
787}
788
William Kurkian0418bc82019-11-06 12:16:24 -0500789func (o *Onu) SetID(id uint32) {
Matteo Scandolo583f17d2020-02-13 10:35:17 -0800790 onuLogger.WithFields(log.Fields{
791 "IntfId": o.PonPortID,
792 "OnuId": id,
793 "SerialNumber": o.Sn(),
794 }).Debug("Storing OnuId ")
William Kurkian0418bc82019-11-06 12:16:24 -0500795 o.ID = id
796}
797
Matteo Scandolof9d43412021-01-12 11:11:34 -0800798func (o *Onu) handleFlowAdd(msg bbsim.OnuFlowUpdateMessage) {
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700799 onuLogger.WithFields(log.Fields{
Matteo Scandoloedf30c72020-09-18 18:15:28 -0700800 "Cookie": msg.Flow.Cookie,
801 "DstPort": msg.Flow.Classifier.DstPort,
802 "FlowId": msg.Flow.FlowId,
803 "FlowType": msg.Flow.FlowType,
804 "GemportId": msg.Flow.GemportId,
805 "InnerVlan": msg.Flow.Classifier.IVid,
806 "IntfId": msg.Flow.AccessIntfId,
807 "IpProto": msg.Flow.Classifier.IpProto,
808 "OnuId": msg.Flow.OnuId,
809 "OnuSn": o.Sn(),
810 "OuterVlan": msg.Flow.Classifier.OVid,
811 "PortNo": msg.Flow.PortNo,
812 "SrcPort": msg.Flow.Classifier.SrcPort,
813 "UniID": msg.Flow.UniId,
814 "ClassifierEthType": fmt.Sprintf("%x", msg.Flow.Classifier.EthType),
815 "ClassifierOPbits": msg.Flow.Classifier.OPbits,
816 "ClassifierIVid": msg.Flow.Classifier.IVid,
817 "ClassifierOVid": msg.Flow.Classifier.OVid,
Matteo Scandolo4f4ac792020-10-01 16:33:21 -0700818 "ReplicateFlow": msg.Flow.ReplicateFlow,
819 "PbitToGemport": msg.Flow.PbitToGemport,
Matteo Scandoloedf30c72020-09-18 18:15:28 -0700820 }).Debug("OLT receives FlowAdd for ONU")
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700821
Matteo Scandolo813402b2019-10-23 19:24:52 -0700822 if msg.Flow.UniId != 0 {
823 // as of now BBSim only support a single UNI, so ignore everything that is not targeted to it
824 onuLogger.WithFields(log.Fields{
825 "IntfId": o.PonPortID,
826 "OnuId": o.ID,
827 "SerialNumber": o.Sn(),
828 }).Debug("Ignoring flow as it's not for the first UNI")
829 return
830 }
831
Matteo Scandoloeb6b5af2020-06-24 16:23:58 -0700832 o.FlowIds = append(o.FlowIds, msg.Flow.FlowId)
Matteo Scandolo4f4ac792020-10-01 16:33:21 -0700833
834 var gemPortId uint32
835 if msg.Flow.ReplicateFlow {
836 // This means that the OLT should replicate the flow for each PBIT, for BBSim it's enough to use the
837 // first available gemport (we only need to send one packet)
838 // NOTE different TP may create different mapping between PBits and GemPorts, this may require some changes
839 gemPortId = msg.Flow.PbitToGemport[0]
840 } else {
841 // if replicateFlows is false, then the flow is carrying the correct GemPortId
842 gemPortId = uint32(msg.Flow.GemportId)
843 }
844 o.addGemPortToService(gemPortId, msg.Flow.Classifier.EthType, msg.Flow.Classifier.OVid, msg.Flow.Classifier.IVid)
Matteo Scandoloeb6b5af2020-06-24 16:23:58 -0700845
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700846 if msg.Flow.Classifier.EthType == uint32(layers.EthernetTypeEAPOL) && msg.Flow.Classifier.OVid == 4091 {
Matteo Scandolo27428702019-10-11 16:21:16 -0700847 // NOTE storing the PortNO, it's needed when sending PacketIndications
Matteo Scandolo813402b2019-10-23 19:24:52 -0700848 o.storePortNumber(uint32(msg.Flow.PortNo))
Matteo Scandolo4a036262020-08-17 15:56:13 -0700849
850 for _, s := range o.Services {
Matteo Scandoloadc72a82020-09-08 18:46:08 -0700851 s.HandleAuth()
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700852 }
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700853 } else if msg.Flow.Classifier.EthType == uint32(layers.EthernetTypeIPv4) &&
854 msg.Flow.Classifier.SrcPort == uint32(68) &&
Matteo Scandolobd875b32020-09-18 17:46:31 -0700855 msg.Flow.Classifier.DstPort == uint32(67) {
Matteo Scandolo99f18462019-10-28 14:14:28 -0700856
Matteo Scandolo4a036262020-08-17 15:56:13 -0700857 for _, s := range o.Services {
Matteo Scandolobd875b32020-09-18 17:46:31 -0700858 s.HandleDhcp(uint8(msg.Flow.Classifier.OPbits), int(msg.Flow.Classifier.OVid))
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700859 }
Matteo Scandolo3bc73742019-08-20 14:04:04 -0700860 }
861}
862
Matteo Scandolof9d43412021-01-12 11:11:34 -0800863func (o *Onu) handleFlowRemove(msg bbsim.OnuFlowUpdateMessage) {
Matteo Scandoloeb6b5af2020-06-24 16:23:58 -0700864 onuLogger.WithFields(log.Fields{
865 "IntfId": o.PonPortID,
866 "OnuId": o.ID,
867 "SerialNumber": o.Sn(),
868 "FlowId": msg.Flow.FlowId,
869 "FlowType": msg.Flow.FlowType,
870 }).Debug("ONU receives FlowRemove")
871
872 for idx, flow := range o.FlowIds {
873 // If the gemport is found, delete it from local cache.
874 if flow == msg.Flow.FlowId {
875 o.FlowIds = append(o.FlowIds[:idx], o.FlowIds[idx+1:]...)
876 break
877 }
878 }
879
880 if len(o.FlowIds) == 0 {
881 onuLogger.WithFields(log.Fields{
882 "IntfId": o.PonPortID,
883 "OnuId": o.ID,
884 "SerialNumber": o.Sn(),
885 }).Info("Resetting GemPort")
886 o.GemPortAdded = false
887
Hardik Windlass7b3405b2020-07-08 15:10:05 +0530888 // check if ONU delete is performed and
889 // terminate the ONU's ProcessOnuMessages Go routine
890 if o.InternalState.Current() == "disabled" {
891 close(o.Channel)
892 }
Matteo Scandoloeb6b5af2020-06-24 16:23:58 -0700893 }
894}
895
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700896// BBR methods
897
898func sendOmciMsg(pktBytes []byte, intfId uint32, onuId uint32, sn *openolt.SerialNumber, msgType string, client openolt.OpenoltClient) {
899 omciMsg := openolt.OmciMsg{
900 IntfId: intfId,
901 OnuId: onuId,
902 Pkt: pktBytes,
903 }
904
905 if _, err := client.OmciMsgOut(context.Background(), &omciMsg); err != nil {
906 log.WithFields(log.Fields{
907 "IntfId": intfId,
908 "OnuId": onuId,
909 "SerialNumber": common.OnuSnToString(sn),
910 "Pkt": omciMsg.Pkt,
911 }).Fatalf("Failed to send MIB Reset")
912 }
913 log.WithFields(log.Fields{
914 "IntfId": intfId,
915 "OnuId": onuId,
916 "SerialNumber": common.OnuSnToString(sn),
917 "Pkt": omciMsg.Pkt,
918 }).Tracef("Sent OMCI message %s", msgType)
919}
920
921func (onu *Onu) getNextTid(highPriority ...bool) uint16 {
922 var next uint16
923 if len(highPriority) > 0 && highPriority[0] {
924 next = onu.hpTid
925 onu.hpTid += 1
926 if onu.hpTid < 0x8000 {
927 onu.hpTid = 0x8000
928 }
929 } else {
930 next = onu.tid
931 onu.tid += 1
932 if onu.tid >= 0x8000 {
933 onu.tid = 1
934 }
935 }
936 return next
937}
938
939// TODO move this method in responders/omcisim
940func (o *Onu) StartOmci(client openolt.OpenoltClient) {
941 mibReset, _ := omcilib.CreateMibResetRequest(o.getNextTid(false))
942 sendOmciMsg(mibReset, o.PonPortID, o.ID, o.SerialNumber, "mibReset", client)
943}
944
Matteo Scandolof9d43412021-01-12 11:11:34 -0800945// handleOmciResponse is used in BBR to generate the OMCI packets the openolt-adapter would send to the device
946func (o *Onu) handleOmciResponse(msg bbsim.OmciIndicationMessage, client openolt.OpenoltClient) {
947
948 // we need to encode the packet in HEX
949 pkt := make([]byte, len(msg.OmciInd.Pkt)*2)
950 hex.Encode(pkt, msg.OmciInd.Pkt)
951 packet, omciMsg, err := omcilib.ParseOpenOltOmciPacket(pkt)
952 if err != nil {
953 log.WithFields(log.Fields{
954 "IntfId": o.PonPortID,
955 "SerialNumber": o.Sn(),
956 "omciPacket": msg.OmciInd.Pkt,
957 }).Error("BBR Cannot parse OMCI packet")
958 }
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700959
960 log.WithFields(log.Fields{
961 "IntfId": msg.OmciInd.IntfId,
962 "OnuId": msg.OmciInd.OnuId,
Matteo Scandolof9d43412021-01-12 11:11:34 -0800963 "OnuSn": o.Sn(),
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700964 "Pkt": msg.OmciInd.Pkt,
Matteo Scandolof9d43412021-01-12 11:11:34 -0800965 "msgType": omciMsg.MessageType,
Anand S Katti09541352020-01-29 15:54:01 +0530966 }).Trace("ONU Receives OMCI Msg")
Matteo Scandolof9d43412021-01-12 11:11:34 -0800967 switch omciMsg.MessageType {
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700968 default:
Matteo Scandolo813402b2019-10-23 19:24:52 -0700969 log.WithFields(log.Fields{
970 "IntfId": msg.OmciInd.IntfId,
971 "OnuId": msg.OmciInd.OnuId,
Matteo Scandolof9d43412021-01-12 11:11:34 -0800972 "OnuSn": o.Sn(),
Matteo Scandolo813402b2019-10-23 19:24:52 -0700973 "Pkt": msg.OmciInd.Pkt,
Matteo Scandolof9d43412021-01-12 11:11:34 -0800974 "msgType": omciMsg.MessageType,
Matteo Scandolo813402b2019-10-23 19:24:52 -0700975 }).Fatalf("unexpected frame: %v", packet)
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700976 case omci.MibResetResponseType:
977 mibUpload, _ := omcilib.CreateMibUploadRequest(o.getNextTid(false))
978 sendOmciMsg(mibUpload, o.PonPortID, o.ID, o.SerialNumber, "mibUpload", client)
979 case omci.MibUploadResponseType:
980 mibUploadNext, _ := omcilib.CreateMibUploadNextRequest(o.getNextTid(false), o.seqNumber)
981 sendOmciMsg(mibUploadNext, o.PonPortID, o.ID, o.SerialNumber, "mibUploadNext", client)
982 case omci.MibUploadNextResponseType:
983 o.seqNumber++
984
985 if o.seqNumber > 290 {
986 // NOTE we are done with the MIB Upload (290 is the number of messages the omci-sim library will respond to)
987 galEnet, _ := omcilib.CreateGalEnetRequest(o.getNextTid(false))
988 sendOmciMsg(galEnet, o.PonPortID, o.ID, o.SerialNumber, "CreateGalEnetRequest", client)
989 } else {
990 mibUploadNext, _ := omcilib.CreateMibUploadNextRequest(o.getNextTid(false), o.seqNumber)
991 sendOmciMsg(mibUploadNext, o.PonPortID, o.ID, o.SerialNumber, "mibUploadNext", client)
992 }
993 case omci.CreateResponseType:
994 // NOTE Creating a GemPort,
995 // BBsim actually doesn't care about the values, so we can do we want with the parameters
996 // In the same way we can create a GemPort even without setting up UNIs/TConts/...
997 // but we need the GemPort to trigger the state change
998
Matteo Scandoloeb6b5af2020-06-24 16:23:58 -0700999 if !o.GemPortAdded {
Matteo Scandolo40e067f2019-10-16 16:59:41 -07001000 // NOTE this sends a CreateRequestType and BBSim replies with a CreateResponseType
1001 // thus we send this request only once
1002 gemReq, _ := omcilib.CreateGemPortRequest(o.getNextTid(false))
1003 sendOmciMsg(gemReq, o.PonPortID, o.ID, o.SerialNumber, "CreateGemPortRequest", client)
Matteo Scandoloeb6b5af2020-06-24 16:23:58 -07001004 o.GemPortAdded = true
Matteo Scandolo40e067f2019-10-16 16:59:41 -07001005 } else {
1006 if err := o.InternalState.Event("send_eapol_flow"); err != nil {
1007 onuLogger.WithFields(log.Fields{
1008 "OnuId": o.ID,
1009 "IntfId": o.PonPortID,
1010 "OnuSn": o.Sn(),
1011 }).Errorf("Error while transitioning ONU State %v", err)
1012 }
1013 }
Matteo Scandolo40e067f2019-10-16 16:59:41 -07001014 }
1015}
1016
1017func (o *Onu) sendEapolFlow(client openolt.OpenoltClient) {
1018
1019 classifierProto := openolt.Classifier{
1020 EthType: uint32(layers.EthernetTypeEAPOL),
1021 OVid: 4091,
1022 }
1023
1024 actionProto := openolt.Action{}
1025
1026 downstreamFlow := openolt.Flow{
1027 AccessIntfId: int32(o.PonPortID),
1028 OnuId: int32(o.ID),
Matteo Scandolo813402b2019-10-23 19:24:52 -07001029 UniId: int32(0), // NOTE do not hardcode this, we need to support multiple UNIs
Matteo Scandolo4f4ac792020-10-01 16:33:21 -07001030 FlowId: uint64(o.ID),
Matteo Scandolo40e067f2019-10-16 16:59:41 -07001031 FlowType: "downstream",
1032 AllocId: int32(0),
1033 NetworkIntfId: int32(0),
1034 GemportId: int32(1), // FIXME use the same value as CreateGemPortRequest PortID, do not hardcode
1035 Classifier: &classifierProto,
1036 Action: &actionProto,
1037 Priority: int32(100),
1038 Cookie: uint64(o.ID),
1039 PortNo: uint32(o.ID), // NOTE we are using this to map an incoming packetIndication to an ONU
1040 }
1041
1042 if _, err := client.FlowAdd(context.Background(), &downstreamFlow); err != nil {
1043 log.WithFields(log.Fields{
1044 "IntfId": o.PonPortID,
1045 "OnuId": o.ID,
1046 "FlowId": downstreamFlow.FlowId,
1047 "PortNo": downstreamFlow.PortNo,
1048 "SerialNumber": common.OnuSnToString(o.SerialNumber),
Matteo Scandolob0e3e622020-04-23 17:00:29 -07001049 }).Fatalf("Failed to add EAPOL Flow")
Matteo Scandolo40e067f2019-10-16 16:59:41 -07001050 }
1051 log.WithFields(log.Fields{
1052 "IntfId": o.PonPortID,
1053 "OnuId": o.ID,
1054 "FlowId": downstreamFlow.FlowId,
1055 "PortNo": downstreamFlow.PortNo,
1056 "SerialNumber": common.OnuSnToString(o.SerialNumber),
1057 }).Info("Sent EAPOL Flow")
1058}
1059
1060func (o *Onu) sendDhcpFlow(client openolt.OpenoltClient) {
Matteo Scandolo4a036262020-08-17 15:56:13 -07001061
1062 // BBR only works with a single service (ATT HSIA)
1063 hsia := o.Services[0].(*Service)
1064
Matteo Scandolo40e067f2019-10-16 16:59:41 -07001065 classifierProto := openolt.Classifier{
1066 EthType: uint32(layers.EthernetTypeIPv4),
1067 SrcPort: uint32(68),
1068 DstPort: uint32(67),
Matteo Scandolo4a036262020-08-17 15:56:13 -07001069 OVid: uint32(hsia.CTag),
Matteo Scandolo40e067f2019-10-16 16:59:41 -07001070 }
1071
1072 actionProto := openolt.Action{}
1073
1074 downstreamFlow := openolt.Flow{
1075 AccessIntfId: int32(o.PonPortID),
1076 OnuId: int32(o.ID),
Matteo Scandolo813402b2019-10-23 19:24:52 -07001077 UniId: int32(0), // FIXME do not hardcode this
Matteo Scandolo4f4ac792020-10-01 16:33:21 -07001078 FlowId: uint64(o.ID),
Matteo Scandolo40e067f2019-10-16 16:59:41 -07001079 FlowType: "downstream",
1080 AllocId: int32(0),
1081 NetworkIntfId: int32(0),
1082 GemportId: int32(1), // FIXME use the same value as CreateGemPortRequest PortID, do not hardcode
1083 Classifier: &classifierProto,
1084 Action: &actionProto,
1085 Priority: int32(100),
1086 Cookie: uint64(o.ID),
1087 PortNo: uint32(o.ID), // NOTE we are using this to map an incoming packetIndication to an ONU
1088 }
1089
1090 if _, err := client.FlowAdd(context.Background(), &downstreamFlow); err != nil {
1091 log.WithFields(log.Fields{
1092 "IntfId": o.PonPortID,
1093 "OnuId": o.ID,
1094 "FlowId": downstreamFlow.FlowId,
1095 "PortNo": downstreamFlow.PortNo,
1096 "SerialNumber": common.OnuSnToString(o.SerialNumber),
1097 }).Fatalf("Failed to send DHCP Flow")
1098 }
1099 log.WithFields(log.Fields{
1100 "IntfId": o.PonPortID,
1101 "OnuId": o.ID,
1102 "FlowId": downstreamFlow.FlowId,
1103 "PortNo": downstreamFlow.PortNo,
1104 "SerialNumber": common.OnuSnToString(o.SerialNumber),
1105 }).Info("Sent DHCP Flow")
1106}
Pragya Arya8bdb4532020-03-02 17:08:09 +05301107
1108// DeleteFlow method search and delete flowKey from the onu flows slice
1109func (onu *Onu) DeleteFlow(key FlowKey) {
1110 for pos, flowKey := range onu.Flows {
1111 if flowKey == key {
1112 // delete the flowKey by shifting all flowKeys by one
1113 onu.Flows = append(onu.Flows[:pos], onu.Flows[pos+1:]...)
1114 t := make([]FlowKey, len(onu.Flows))
1115 copy(t, onu.Flows)
1116 onu.Flows = t
1117 break
1118 }
1119 }
1120}
Hardik Windlass7b3405b2020-07-08 15:10:05 +05301121
1122func (onu *Onu) ReDiscoverOnu() {
1123 // Wait for few seconds to be sure of the cleanup
1124 time.Sleep(5 * time.Second)
1125
1126 onuLogger.WithFields(log.Fields{
1127 "IntfId": onu.PonPortID,
1128 "OnuId": onu.ID,
1129 "OnuSn": onu.Sn(),
1130 }).Debug("Send ONU Re-Discovery")
1131
1132 // ONU Re-Discovery
1133 if err := onu.InternalState.Event("initialize"); err != nil {
1134 log.WithFields(log.Fields{
1135 "IntfId": onu.PonPortID,
1136 "OnuSn": onu.Sn(),
1137 "OnuId": onu.ID,
1138 }).Infof("Failed to transition ONU to initialized state: %s", err.Error())
1139 }
1140
1141 if err := onu.InternalState.Event("discover"); err != nil {
1142 log.WithFields(log.Fields{
1143 "IntfId": onu.PonPortID,
1144 "OnuSn": onu.Sn(),
1145 "OnuId": onu.ID,
1146 }).Infof("Failed to transition ONU to discovered state: %s", err.Error())
1147 }
1148}
Matteo Scandolo4a036262020-08-17 15:56:13 -07001149
1150func (onu *Onu) addGemPortToService(gemport uint32, ethType uint32, oVlan uint32, iVlan uint32) {
1151 for _, s := range onu.Services {
1152 if service, ok := s.(*Service); ok {
1153 // EAPOL is a strange case, as packets are untagged
1154 // but we assume we will have a single service requiring EAPOL
1155 if ethType == uint32(layers.EthernetTypeEAPOL) && service.NeedsEapol {
1156 service.GemPort = gemport
1157 }
1158
1159 // For DHCP services we single tag the outgoing packets,
1160 // thus the flow only contains the CTag and we can use that to match the service
1161 if ethType == uint32(layers.EthernetTypeIPv4) && service.NeedsDhcp && service.CTag == int(oVlan) {
1162 service.GemPort = gemport
1163 }
1164
1165 // for dataplane services match both C and S tags
1166 if service.CTag == int(iVlan) && service.STag == int(oVlan) {
1167 service.GemPort = gemport
1168 }
1169 }
1170 }
1171}
1172
1173func (onu *Onu) findServiceByMacAddress(macAddress net.HardwareAddr) (*Service, error) {
1174 for _, s := range onu.Services {
1175 service := s.(*Service)
1176 if service.HwAddress.String() == macAddress.String() {
1177 return service, nil
1178 }
1179 }
1180 return nil, fmt.Errorf("cannot-find-service-with-mac-address-%s", macAddress.String())
1181}