blob: 15ca2e73fa8f299a97b87a14158b1da48ab110f7 [file] [log] [blame]
Zack Williams41513bf2018-07-07 20:08:35 -07001/*
2 * Copyright 2017-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 */
Stephane Barbarie35595062018-02-08 08:34:39 -050016package core
17
18import (
19 "context"
Jeff70e8b2d2018-07-24 13:37:29 -070020 "strconv"
21 "strings"
22 "time"
23
Stephane Barbarie35595062018-02-08 08:34:39 -050024 "github.com/golang/protobuf/ptypes/empty"
25 "github.com/google/gopacket"
26 "github.com/opencord/voltha/ponsim/v2/common"
Jeff55a2f132018-08-05 21:10:41 -070027 "github.com/opencord/voltha/protos/go/openflow_13"
Stephane Barbarie35595062018-02-08 08:34:39 -050028 "github.com/opencord/voltha/protos/go/ponsim"
29 "github.com/sirupsen/logrus"
30 "google.golang.org/grpc"
31 "google.golang.org/grpc/connectivity"
Stephane Barbarie35595062018-02-08 08:34:39 -050032)
33
34// TODO: Pass-in the certificate information as a structure parameter
35
36/*
37PonSimOltDevice is the structure responsible for the handling of an OLT device
38*/
39type PonSimOltDevice struct {
40 PonSimDevice `json:pon_device`
41 VCoreEndpoint string `json:vcore_ep`
42 MaxOnuCount int `json:max_onu`
43 Onus map[int32]*OnuRegistree `json:onu_registrees`
44 outgoing chan []byte
45
46 counterLoop *common.IntervalHandler
47 alarmLoop *common.IntervalHandler
48}
49
50/*
51
52 */
53type OnuRegistree struct {
54 Device *PonSimOnuDevice `json:onu_device`
55 Conn *grpc.ClientConn `json:grpc_conn`
56 Client ponsim.PonSimCommonClient `json:client`
57 Stream ponsim.PonSimCommon_ProcessDataClient `json:stream`
58}
59
60const (
61 BASE_PORT_NUMBER = 128
62)
63
64/*
65NewPonSimOltDevice instantiates a new OLT device structure
66*/
67func NewPonSimOltDevice(device PonSimDevice) *PonSimOltDevice {
68 olt := &PonSimOltDevice{PonSimDevice: device}
69 return olt
70}
71
72/*
73forwardToONU defines a EGRESS function to forward a packet to a specific ONU
74*/
75func (o *PonSimOltDevice) forwardToONU(onuPort int32) func(int, gopacket.Packet) {
76 return func(port int, frame gopacket.Packet) {
77 ipAddress := common.GetInterfaceIP(o.ExternalIf)
78 incoming := &ponsim.IncomingData{
79 Id: "EGRESS.OLT." + ipAddress,
80 Address: ipAddress,
81 Port: int32(port),
82 Payload: frame.Data(),
83 }
84 common.Logger().WithFields(logrus.Fields{
85 "device": o,
86 "port": port,
87 "frame": frame,
88 }).Debug("Forwarding to ONU")
89
90 // Forward packet to ONU
91 if err := o.GetOnu(onuPort).Stream.Send(incoming); err != nil {
92 common.Logger().WithFields(logrus.Fields{
93 "device": o,
94 "frameDump": frame.Dump(),
95 "incoming": incoming,
96 "error": err.Error(),
97 }).Error("A problem occurred while forwarding to ONU")
98 }
99
100 }
101}
102
103/*
104forwardToLAN defines an INGRESS function to forward a packet to VOLTHA
105*/
106func (o *PonSimOltDevice) forwardToLAN() func(int, gopacket.Packet) {
107 return func(port int, frame gopacket.Packet) {
108 common.Logger().WithFields(logrus.Fields{
109 "frame": frame.Dump(),
110 }).Info("Sending packet")
111
112 select {
113 case o.outgoing <- frame.Data():
114 common.Logger().WithFields(logrus.Fields{
115 "frame": frame.Dump(),
116 }).Info("Sent packet")
117 default:
118 common.Logger().WithFields(logrus.Fields{
119 "frame": frame.Dump(),
120 }).Warn("Unable to send packet")
121 }
122 }
123}
124
125/*
Jeff55a2f132018-08-05 21:10:41 -0700126forwardToNNI defines function to forward a packet to the NNI interface
127*/
128func (o *PonSimOltDevice) forwardToNNI() func(int, gopacket.Packet) {
129 return func(port int, frame gopacket.Packet) {
130 var err error
131 common.Logger().WithFields(logrus.Fields{
132 "device": o,
133 "port": port,
134 "frame": frame,
135 }).Debug("Forwarding packet to NNI")
136 if err = o.egressHandler.WritePacketData(frame.Data()); err != nil {
137 common.Logger().WithFields(logrus.Fields{
138 "device": o,
139 "port": port,
140 "frame": frame,
141 }).Fatal("Problem while forwarding packet to NNI")
142 } else {
143 common.Logger().WithFields(logrus.Fields{
144 "device": o,
145 "port": port,
146 "frame": frame,
147 }).Debug("Forwarded packet to NNI")
148 }
149 }
150}
151
152/*
Stephane Barbarie35595062018-02-08 08:34:39 -0500153Start performs setup operations for an OLT device
154*/
155func (o *PonSimOltDevice) Start(ctx context.Context) {
156 common.Logger().Info("Starting OLT device...")
157 o.PonSimDevice.Start(ctx)
158
159 // Open network interfaces for listening
160 o.connectNetworkInterfaces()
161
162 o.outgoing = make(chan []byte, 1)
163
164 // Add INGRESS operation
Jeff55a2f132018-08-05 21:10:41 -0700165 o.AddLink(int(openflow_13.OfpPortNo_OFPP_CONTROLLER), 0, o.forwardToLAN())
166
167 // Add Data-Plane Forwarding operation
168 o.AddLink(2, 0, o.forwardToNNI())
Stephane Barbarie35595062018-02-08 08:34:39 -0500169
170 // Start PM counter logging
171 o.counterLoop = common.NewIntervalHandler(90, o.Counter.LogCounts)
172 o.counterLoop.Start()
173
174 // Start alarm simulation
175 if o.AlarmsOn {
176 common.Logger().WithFields(logrus.Fields{
177 "device": o,
178 }).Debug("Starting alarm simulation")
179
180 alarms := NewPonSimAlarm(o.InternalIf, o.VCoreEndpoint, o.forwardToLAN())
181 o.alarmLoop = common.NewIntervalHandler(o.AlarmsFreq, alarms.GenerateAlarm)
182 o.alarmLoop.Start()
183 }
184}
185
186/*
187Stop performs cleanup operations for an OLT device
188*/
189func (o *PonSimOltDevice) Stop(ctx context.Context) {
190 common.Logger().Info("Stopping OLT device...")
191
192 // Stop PM counters loop
193 o.counterLoop.Stop()
194 o.counterLoop = nil
195
196 // Stop alarm simulation
197 if o.AlarmsOn {
198 o.alarmLoop.Stop()
199 }
200 o.alarmLoop = nil
201
202 o.ingressHandler.Close()
203 o.egressHandler.Close()
204
205 o.PonSimDevice.Stop(ctx)
206}
207
208/*
209ConnectToRemoteOnu establishes communication to a remote ONU device
210*/
211func (o *PonSimOltDevice) ConnectToRemoteOnu(onu *OnuRegistree) error {
212 var err error
213
214 host := strings.Join([]string{
215 onu.Device.Address,
216 strconv.Itoa(int(onu.Device.Port)),
217 }, ":")
218
219 common.Logger().WithFields(logrus.Fields{
220 "device": o,
221 "host": host,
222 }).Debug("Formatting host address")
223
Stephane Barbarie35595062018-02-08 08:34:39 -0500224 // GRPC communication needs to be secured
225 if onu.Conn, err = grpc.DialContext(
226 context.Background(),
227 host,
Jeff70e8b2d2018-07-24 13:37:29 -0700228 grpc.WithInsecure(),
Stephane Barbarie35595062018-02-08 08:34:39 -0500229 ); err != nil {
230 common.Logger().WithFields(logrus.Fields{
231 "device": o,
232 "error": err.Error(),
233 }).Error("Problem with client connection")
234 }
235
236 return err
237}
238
239/*
240Listen waits for incoming EGRESS data on the internal interface
241*/
242func (o *PonSimOltDevice) Listen(ctx context.Context, port int32) {
243 var reply *empty.Empty
244 var err error
245
246 // Establish a GRPC connection with the ONU
247 onu := o.GetOnu(port)
248
249 common.Logger().WithFields(logrus.Fields{
250 "onu": onu,
251 }).Debug("Connecting to remote ONU")
252
253 if onu.Client = ponsim.NewPonSimCommonClient(onu.Conn); onu.Client == nil {
254 common.Logger().WithFields(logrus.Fields{
255 "device": o,
256 }).Error("Problem establishing client connection to ONU")
257 o.RemoveOnu(ctx, port)
258 return
259 }
260
261 // Prepare stream to ONU to forward incoming data as needed
262 if onu.Stream, err = onu.Client.ProcessData(ctx); err != nil {
263 common.Logger().WithFields(logrus.Fields{
264 "device": o,
265 }).Error("Problem establishing stream to ONU")
266 o.RemoveOnu(ctx, port)
267 return
268 }
269
270 defer o.egressHandler.Close()
271 packetSource := gopacket.NewPacketSource(o.egressHandler, o.egressHandler.LinkType())
272 common.Logger().WithFields(logrus.Fields{
273 "device": o,
274 "interface": o.InternalIf,
275 }).Debug("Listening to incoming EGRESS data")
276
277 // Wait for incoming EGRESS data
278 for packet := range packetSource.Packets() {
279 if dot1q := common.GetDot1QLayer(packet); dot1q != nil {
280 common.Logger().WithFields(logrus.Fields{
281 "device": o,
282 "packet": packet,
283 }).Debug("Received EGRESS packet")
284
285 o.Forward(ctx, 2, packet)
286 }
287 }
288
289 common.Logger().WithFields(logrus.Fields{
290 "device": o,
291 }).Debug("No more packets to process")
292
293 if reply, err = onu.Stream.CloseAndRecv(); err != nil {
294 common.Logger().WithFields(logrus.Fields{
295 "device": o,
296 "error": err.Error(),
297 }).Error("A problem occurred while closing client stream")
298 } else {
299 common.Logger().WithFields(logrus.Fields{
300 "device": o,
301 "reply": reply,
302 }).Warn("Client stream closed")
303 }
304}
305
306/*
307GetOnus returns the list of registered ONU devices
308*/
309func (o *PonSimOltDevice) GetOnus() map[int32]*OnuRegistree {
310 if o.Onus == nil {
311 o.Onus = make(map[int32]*OnuRegistree)
312 }
313
314 return o.Onus
315}
316
317/*
318GetOnu return a specific registered ONU
319*/
320func (o *PonSimOltDevice) GetOnu(index int32) *OnuRegistree {
321 var onu *OnuRegistree
322 var ok bool
323
324 if onu, ok = (o.GetOnus())[index]; ok {
325 return onu
326 }
327
328 return nil
329}
330
331func (o *PonSimOltDevice) GetOutgoing() chan []byte {
332 return o.outgoing
333}
334
335/*
336nextAvailablePort returns a port that is not already used by a registered ONU
337*/
338func (o *PonSimOltDevice) nextAvailablePort() int32 {
339 var port int32 = BASE_PORT_NUMBER
340
341 if len(o.GetOnus()) < o.MaxOnuCount {
342 for {
343 if o.GetOnu(port) != nil {
344 // port is already used
345 port += 1
346 } else {
347 // port is available... use it
348 return port
349 }
350 }
351 } else {
352 // OLT has reached its max number of ONUs
353 return -1
354 }
355}
356
357/*
358AddOnu registers an ONU device and sets up all required monitoring and connections
359*/
360func (o *PonSimOltDevice) AddOnu(onu *PonSimOnuDevice) (int32, error) {
361 var portNum int32
362 ctx := context.Background()
363
364 if portNum = o.nextAvailablePort(); portNum != -1 {
365 common.Logger().WithFields(logrus.Fields{
366 "device": o,
367 "port": portNum,
368 "onu": onu,
369 }).Info("Adding ONU")
370
371 registree := &OnuRegistree{Device: onu}
372
373 // Setup GRPC communication and check if it succeeded
374 if err := o.ConnectToRemoteOnu(registree); err == nil {
375 o.GetOnus()[portNum] = registree
376
377 o.AddLink(1, int(portNum), o.forwardToONU(portNum))
Jeff55a2f132018-08-05 21:10:41 -0700378 common.Logger().WithFields(logrus.Fields{
379 "port": portNum,
380 "onu": onu,
381 }).Info("Connected ONU")
Stephane Barbarie35595062018-02-08 08:34:39 -0500382 go o.MonitorOnu(ctx, portNum)
383 go o.Listen(ctx, portNum)
384 }
385
386 } else {
387 common.Logger().WithFields(logrus.Fields{
388 "device": o,
389 }).Warn("ONU Map is full")
390 }
391
392 return int32(portNum), nil
393}
394
395/*
396RemoveOnu removes the reference to a registered ONU
397*/
398func (o *PonSimOltDevice) RemoveOnu(ctx context.Context, onuIndex int32) error {
399 onu := o.GetOnu(onuIndex)
400 if err := onu.Conn.Close(); err != nil {
401 common.Logger().WithFields(logrus.Fields{
402 "device": o,
403 "onu": onu.Device,
404 "onuIndex": onuIndex,
405 }).Error("Problem closing connection to ONU")
406 }
407
408 common.Logger().WithFields(logrus.Fields{
409 "device": o,
410 "onu": onu,
411 "onuIndex": onuIndex,
412 }).Info("Removing ONU")
413
414 delete(o.Onus, onuIndex)
415
416 // Remove link entries for this ONU
417 o.RemoveLink(1, int(onuIndex))
418
419 return nil
420}
421
422/*
423MonitorOnu verifies the connection status of a specific ONU and cleans up as necessary
424*/
425func (o *PonSimOltDevice) MonitorOnu(ctx context.Context, onuIndex int32) {
426 for {
427 if o.GetOnu(onuIndex) != nil {
428 if conn := o.GetOnu(onuIndex).Conn; conn.GetState() == connectivity.Ready {
429 // Wait for any change to occur
430 conn.WaitForStateChange(ctx, conn.GetState())
431 // We lost communication with the ONU ... remove it
432 o.RemoveOnu(ctx, onuIndex)
433 return
434 }
435 common.Logger().WithFields(logrus.Fields{
436 "device": o,
437 "ctx": ctx,
438 "onuIndex": onuIndex,
439 }).Debug("ONU is not ready")
440 time.Sleep(1 * time.Second)
441 } else {
442 return
443 }
444 }
445}