blob: 35b5697558d2921464301965ec6cb18acecce0ed [file] [log] [blame]
Matteo Scandolo40e067f2019-10-16 16:59:41 -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
17package devices
18
19import (
20 "context"
Matteo Scandolo4a036262020-08-17 15:56:13 -070021 "encoding/hex"
Matteo Scandolo40e067f2019-10-16 16:59:41 -070022 "fmt"
Shrey Baid688b4242020-07-10 20:40:10 +053023 "io"
24 "reflect"
25 "time"
26
Matteo Scandolo40e067f2019-10-16 16:59:41 -070027 "github.com/google/gopacket"
28 "github.com/google/gopacket/layers"
29 "github.com/opencord/bbsim/internal/bbsim/devices"
30 "github.com/opencord/bbsim/internal/bbsim/packetHandlers"
31 "github.com/opencord/bbsim/internal/common"
Matteo Scandolo3de9de02019-11-14 13:40:03 -080032 "github.com/opencord/voltha-protos/v2/go/openolt"
Matteo Scandolo40e067f2019-10-16 16:59:41 -070033 log "github.com/sirupsen/logrus"
34 "google.golang.org/grpc"
Matteo Scandolo40e067f2019-10-16 16:59:41 -070035)
36
37type OltMock struct {
Matteo Scandolo583f17d2020-02-13 10:35:17 -080038 LastUsedOnuId map[uint32]uint32
39 Olt *devices.OltDevice
40 BBSimIp string
41 BBSimPort string
42 BBSimApiPort string
Matteo Scandolo40e067f2019-10-16 16:59:41 -070043
44 conn *grpc.ClientConn
45
46 TargetOnus int
47 CompletedOnus int // Number of ONUs that have received a DHCPAck
48}
49
50// trigger an enable call and start the same listeners on the gRPC stream that VOLTHA would create
51// this method is blocking
52func (o *OltMock) Start() {
53 log.Info("Starting Mock OLT")
54
55 for _, pon := range o.Olt.Pons {
56 for _, onu := range pon.Onus {
Matteo Scandolod32c3822019-11-26 15:57:46 -070057 if err := onu.InternalState.Event("initialize"); err != nil {
58 log.Fatalf("Error initializing ONU: %v", err)
59 }
Matteo Scandolo4a036262020-08-17 15:56:13 -070060 log.Debugf("Created ONU: %s", onu.Sn())
Matteo Scandolo40e067f2019-10-16 16:59:41 -070061 }
62 }
63
64 client, conn := Connect(o.BBSimIp, o.BBSimPort)
65 o.conn = conn
66 defer conn.Close()
67
68 deviceInfo, err := o.getDeviceInfo(client)
69
70 if err != nil {
71 log.WithFields(log.Fields{
72 "error": err,
73 }).Fatal("Can't read device info")
74 }
75
76 log.WithFields(log.Fields{
77 "Vendor": deviceInfo.Vendor,
78 "Model": deviceInfo.Model,
79 "DeviceSerialNumber": deviceInfo.DeviceSerialNumber,
80 "PonPorts": deviceInfo.PonPorts,
81 }).Info("Retrieved device info")
82
83 o.readIndications(client)
84
85}
86
87func (o *OltMock) getDeviceInfo(client openolt.OpenoltClient) (*openolt.DeviceInfo, error) {
88 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
89 defer cancel()
90
91 return client.GetDeviceInfo(ctx, new(openolt.Empty))
92}
93
Matteo Scandolo40e067f2019-10-16 16:59:41 -070094func (o *OltMock) readIndications(client openolt.OpenoltClient) {
95 defer func() {
96 log.Info("OLT readIndications done")
97 }()
98
99 // Tell the OLT to start sending indications
100 indications, err := client.EnableIndication(context.Background(), new(openolt.Empty))
101 if err != nil {
102 log.WithFields(log.Fields{
103 "error": err,
104 }).Error("Failed to enable indication stream")
105 return
106 }
107
108 // listen for indications
109 for {
110 indication, err := indications.Recv()
111 if err == io.EOF {
112 break
113 }
114 if err != nil {
115
116 // the connection is closed once we have sent the DHCP_ACK packet to all of the ONUs
117 // it means BBR completed, it's not an error
118
119 log.WithFields(log.Fields{
120 "error": err,
121 }).Debug("Failed to read from indications")
122 break
123 }
124
125 o.handleIndication(client, indication)
126 }
127}
128
129func (o *OltMock) handleIndication(client openolt.OpenoltClient, indication *openolt.Indication) {
130 switch indication.Data.(type) {
131 case *openolt.Indication_OltInd:
132 log.Info("Received Indication_OltInd")
133 case *openolt.Indication_IntfInd:
134 log.Info("Received Indication_IntfInd")
135 case *openolt.Indication_IntfOperInd:
136 log.Info("Received Indication_IntfOperInd")
137 case *openolt.Indication_OnuDiscInd:
138 onuDiscInd := indication.GetOnuDiscInd()
139 o.handleOnuDiscIndication(client, onuDiscInd)
140 case *openolt.Indication_OnuInd:
141 onuInd := indication.GetOnuInd()
142 o.handleOnuIndication(client, onuInd)
143 case *openolt.Indication_OmciInd:
144 omciIndication := indication.GetOmciInd()
145 o.handleOmciIndication(client, omciIndication)
146 case *openolt.Indication_PktInd:
147 pktIndication := indication.GetPktInd()
148 o.handlePktIndication(client, pktIndication)
149 case *openolt.Indication_PortStats:
150 case *openolt.Indication_FlowStats:
151 case *openolt.Indication_AlarmInd:
152 default:
153 log.WithFields(log.Fields{
154 "data": indication.Data,
155 "type": reflect.TypeOf(indication.Data),
156 }).Warn("Indication unsupported")
157 }
158}
159
160func (o *OltMock) handleOnuDiscIndication(client openolt.OpenoltClient, onuDiscInd *openolt.OnuDiscIndication) {
161 log.WithFields(log.Fields{
162 "IntfId": onuDiscInd.IntfId,
163 "SerialNumber": common.OnuSnToString(onuDiscInd.SerialNumber),
164 }).Info("Received Onu discovery indication")
165
166 onu, err := o.Olt.FindOnuBySn(common.OnuSnToString(onuDiscInd.SerialNumber))
167
168 if err != nil {
169 log.WithFields(log.Fields{
170 "IntfId": onuDiscInd.IntfId,
171 "SerialNumber": common.OnuSnToString(onuDiscInd.SerialNumber),
Matteo Scandolo583f17d2020-02-13 10:35:17 -0800172 "Err": err,
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700173 }).Fatal("Cannot find ONU")
174 }
175
Matteo Scandolo583f17d2020-02-13 10:35:17 -0800176 // creating and storing ONU IDs
177 id := o.LastUsedOnuId[onuDiscInd.IntfId] + 1
178 o.LastUsedOnuId[onuDiscInd.IntfId] = o.LastUsedOnuId[onuDiscInd.IntfId] + 1
179 onu.SetID(id)
180
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700181 var pir uint32 = 1000000
182 Onu := openolt.Onu{
183 IntfId: onu.PonPortID,
Matteo Scandolo583f17d2020-02-13 10:35:17 -0800184 OnuId: id,
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700185 SerialNumber: onu.SerialNumber,
186 Pir: pir,
187 }
188
189 if _, err := client.ActivateOnu(context.Background(), &Onu); err != nil {
190 log.WithFields(log.Fields{
191 "IntfId": onuDiscInd.IntfId,
192 "SerialNumber": common.OnuSnToString(onuDiscInd.SerialNumber),
193 }).Error("Failed to activate ONU")
194 }
195}
196
197func (o *OltMock) handleOnuIndication(client openolt.OpenoltClient, onuInd *openolt.OnuIndication) {
198 log.WithFields(log.Fields{
199 "IntfId": onuInd.IntfId,
200 "SerialNumber": common.OnuSnToString(onuInd.SerialNumber),
201 }).Info("Received Onu indication")
202
203 onu, err := o.Olt.FindOnuBySn(common.OnuSnToString(onuInd.SerialNumber))
204
205 if err != nil {
206 log.WithFields(log.Fields{
207 "IntfId": onuInd.IntfId,
208 "SerialNumber": common.OnuSnToString(onuInd.SerialNumber),
209 }).Fatal("Cannot find ONU")
210 }
211
David Bainbridge103cf022019-12-16 20:11:35 +0000212 ctx, cancel := context.WithCancel(context.TODO())
213 go onu.ProcessOnuMessages(ctx, nil, client)
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700214
215 go func() {
216
217 defer func() {
218 log.WithFields(log.Fields{
219 "onuSn": common.OnuSnToString(onuInd.SerialNumber),
220 "CompletedOnus": o.CompletedOnus,
221 "TargetOnus": o.TargetOnus,
222 }).Debugf("Onu done")
223
Matteo Scandolo569e7172019-12-20 11:51:51 -0800224 // close the ONU channel
225 cancel()
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700226 }()
227
228 for message := range onu.DoneChannel {
Shrey Baid688b4242020-07-10 20:40:10 +0530229 if message {
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700230 o.CompletedOnus++
231 if o.CompletedOnus == o.TargetOnus {
232 // NOTE once all the ONUs are completed, exit
233 // closing the connection is not the most elegant way,
234 // but I haven't found any other way to stop
235 // the indications.Recv() infinite loop
236 log.Info("Simulation Done")
237 ValidateAndClose(o)
238 }
239
240 break
241 }
242 }
243
244 }()
245
246 // TODO change the state instead of calling an ONU method from here
247 onu.StartOmci(client)
248}
249
250func (o *OltMock) handleOmciIndication(client openolt.OpenoltClient, omciInd *openolt.OmciIndication) {
251
252 pon, err := o.Olt.GetPonById(omciInd.IntfId)
253 if err != nil {
254 log.WithFields(log.Fields{
255 "OnuId": omciInd.OnuId,
256 "IntfId": omciInd.IntfId,
257 "err": err,
258 }).Fatal("Can't find PonPort")
259 }
260 onu, _ := pon.GetOnuById(omciInd.OnuId)
261 if err != nil {
262 log.WithFields(log.Fields{
263 "OnuId": omciInd.OnuId,
264 "IntfId": omciInd.IntfId,
265 "err": err,
266 }).Fatal("Can't find Onu")
267 }
268
269 log.WithFields(log.Fields{
270 "IntfId": onu.PonPortID,
271 "OnuId": onu.ID,
272 "OnuSn": onu.Sn(),
273 "Pkt": omciInd.Pkt,
274 }).Trace("Received Onu omci indication")
275
276 msg := devices.Message{
277 Type: devices.OmciIndication,
278 Data: devices.OmciIndicationMessage{
279 OnuSN: onu.SerialNumber,
280 OnuID: onu.ID,
281 OmciInd: omciInd,
282 },
283 }
284 onu.Channel <- msg
285}
286
287func (o *OltMock) handlePktIndication(client openolt.OpenoltClient, pktIndication *openolt.PacketIndication) {
288
289 pkt := gopacket.NewPacket(pktIndication.Pkt, layers.LayerTypeEthernet, gopacket.Default)
290
291 pktType, err := packetHandlers.IsEapolOrDhcp(pkt)
292
293 if err != nil {
294 log.Warnf("Ignoring packet as it's neither EAPOL or DHCP")
295 return
296 }
297
298 log.WithFields(log.Fields{
299 "IntfType": pktIndication.IntfType,
300 "IntfId": pktIndication.IntfId,
301 "GemportId": pktIndication.GemportId,
302 "FlowId": pktIndication.FlowId,
303 "PortNo": pktIndication.PortNo,
304 "Cookie": pktIndication.Cookie,
305 "pktType": pktType,
306 }).Trace("Received PktIndication")
307
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700308 if pktIndication.IntfType == "nni" {
309 // This is an packet that is arriving from the NNI and needs to be sent to an ONU
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700310
Matteo Scandolo4a036262020-08-17 15:56:13 -0700311 onuMac, err := packetHandlers.GetDstMacAddressFromPacket(pkt)
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700312
313 if err != nil {
314 log.WithFields(log.Fields{
Matteo Scandolo4a036262020-08-17 15:56:13 -0700315 "IntfType": "nni",
316 "Pkt": hex.EncodeToString(pkt.Data()),
317 }).Fatal("Can't find Dst MacAddress in packet")
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700318 }
319
Matteo Scandolo4a036262020-08-17 15:56:13 -0700320 s, err := o.Olt.FindServiceByMacAddress(onuMac)
321 if err != nil {
322 log.WithFields(log.Fields{
323 "IntfType": "nni",
324 "Pkt": hex.EncodeToString(pkt.Data()),
325 "MacAddress": onuMac.String(),
326 }).Fatal("Can't find ONU with MacAddress")
327 }
328
329 service := s.(*devices.Service)
330 onu := service.Onu
331
Shrey Baid688b4242020-07-10 20:40:10 +0530332 msg := devices.Message{
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700333 Type: devices.OnuPacketIn,
334 Data: devices.OnuPacketMessage{
Matteo Scandolo4a036262020-08-17 15:56:13 -0700335 IntfId: pktIndication.IntfId,
336 OnuId: onu.ID,
337 Packet: pkt,
338 Type: pktType,
339 GemPortId: pktIndication.GemportId,
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700340 },
341 }
342 // NOTE we send it on the ONU channel so that is handled as all the others packets in a separate thread
343 onu.Channel <- msg
344 } else {
345 // TODO a very similar construct is used in many places,
346 // abstract this in an OLT method
347 pon, err := o.Olt.GetPonById(pktIndication.IntfId)
348 if err != nil {
349 log.WithFields(log.Fields{
350 "OnuId": pktIndication.PortNo,
351 "IntfId": pktIndication.IntfId,
352 "err": err,
353 }).Fatal("Can't find PonPort")
354 }
355 onu, err := pon.GetOnuById(pktIndication.PortNo)
356 if err != nil {
357 log.WithFields(log.Fields{
358 "OnuId": pktIndication.PortNo,
359 "IntfId": pktIndication.IntfId,
360 "err": err,
361 }).Fatal("Can't find Onu")
362 }
363 // NOTE when we push the EAPOL flow we set the PortNo = OnuId for convenience sake
364 // BBsim responds setting the port number that was sent with the flow
Shrey Baid688b4242020-07-10 20:40:10 +0530365 msg := devices.Message{
Matteo Scandolo40e067f2019-10-16 16:59:41 -0700366 Type: devices.OnuPacketIn,
367 Data: devices.OnuPacketMessage{
368 IntfId: pktIndication.IntfId,
369 OnuId: pktIndication.PortNo,
370 Packet: pkt,
371 Type: pktType,
372 },
373 }
374 onu.Channel <- msg
375 }
376}
377
378// TODO Move in a different file
379func Connect(ip string, port string) (openolt.OpenoltClient, *grpc.ClientConn) {
380 server := fmt.Sprintf("%s:%s", ip, port)
381 conn, err := grpc.Dial(server, grpc.WithInsecure())
382
383 if err != nil {
384 log.Fatalf("did not connect: %v", err)
385 return nil, conn
386 }
387 return openolt.NewOpenoltClient(conn), conn
388}