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