blob: 508f45569287e9718837c60edb36aae304797577 [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"
20 "crypto/tls"
21 "github.com/golang/protobuf/ptypes/empty"
22 "github.com/google/gopacket"
23 "github.com/opencord/voltha/ponsim/v2/common"
24 "github.com/opencord/voltha/protos/go/ponsim"
25 "github.com/sirupsen/logrus"
26 "google.golang.org/grpc"
27 "google.golang.org/grpc/connectivity"
28 "google.golang.org/grpc/credentials"
29 "strconv"
30 "strings"
31 "time"
32)
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/*
126Start performs setup operations for an OLT device
127*/
128func (o *PonSimOltDevice) Start(ctx context.Context) {
129 common.Logger().Info("Starting OLT device...")
130 o.PonSimDevice.Start(ctx)
131
132 // Open network interfaces for listening
133 o.connectNetworkInterfaces()
134
135 o.outgoing = make(chan []byte, 1)
136
137 // Add INGRESS operation
138 o.AddLink(2, 0, o.forwardToLAN())
139
140 // Start PM counter logging
141 o.counterLoop = common.NewIntervalHandler(90, o.Counter.LogCounts)
142 o.counterLoop.Start()
143
144 // Start alarm simulation
145 if o.AlarmsOn {
146 common.Logger().WithFields(logrus.Fields{
147 "device": o,
148 }).Debug("Starting alarm simulation")
149
150 alarms := NewPonSimAlarm(o.InternalIf, o.VCoreEndpoint, o.forwardToLAN())
151 o.alarmLoop = common.NewIntervalHandler(o.AlarmsFreq, alarms.GenerateAlarm)
152 o.alarmLoop.Start()
153 }
154}
155
156/*
157Stop performs cleanup operations for an OLT device
158*/
159func (o *PonSimOltDevice) Stop(ctx context.Context) {
160 common.Logger().Info("Stopping OLT device...")
161
162 // Stop PM counters loop
163 o.counterLoop.Stop()
164 o.counterLoop = nil
165
166 // Stop alarm simulation
167 if o.AlarmsOn {
168 o.alarmLoop.Stop()
169 }
170 o.alarmLoop = nil
171
172 o.ingressHandler.Close()
173 o.egressHandler.Close()
174
175 o.PonSimDevice.Stop(ctx)
176}
177
178/*
179ConnectToRemoteOnu establishes communication to a remote ONU device
180*/
181func (o *PonSimOltDevice) ConnectToRemoteOnu(onu *OnuRegistree) error {
182 var err error
183
184 host := strings.Join([]string{
185 onu.Device.Address,
186 strconv.Itoa(int(onu.Device.Port)),
187 }, ":")
188
189 common.Logger().WithFields(logrus.Fields{
190 "device": o,
191 "host": host,
192 }).Debug("Formatting host address")
193
194 // TODO: make it secure
195 ta := credentials.NewTLS(&tls.Config{
196 //Certificates: []tls.Certificate{peerCert},
197 //RootCAs: caCertPool,
198 InsecureSkipVerify: true,
199 })
200
201 // GRPC communication needs to be secured
202 if onu.Conn, err = grpc.DialContext(
203 context.Background(),
204 host,
205 grpc.WithTransportCredentials(ta),
206 ); err != nil {
207 common.Logger().WithFields(logrus.Fields{
208 "device": o,
209 "error": err.Error(),
210 }).Error("Problem with client connection")
211 }
212
213 return err
214}
215
216/*
217Listen waits for incoming EGRESS data on the internal interface
218*/
219func (o *PonSimOltDevice) Listen(ctx context.Context, port int32) {
220 var reply *empty.Empty
221 var err error
222
223 // Establish a GRPC connection with the ONU
224 onu := o.GetOnu(port)
225
226 common.Logger().WithFields(logrus.Fields{
227 "onu": onu,
228 }).Debug("Connecting to remote ONU")
229
230 if onu.Client = ponsim.NewPonSimCommonClient(onu.Conn); onu.Client == nil {
231 common.Logger().WithFields(logrus.Fields{
232 "device": o,
233 }).Error("Problem establishing client connection to ONU")
234 o.RemoveOnu(ctx, port)
235 return
236 }
237
238 // Prepare stream to ONU to forward incoming data as needed
239 if onu.Stream, err = onu.Client.ProcessData(ctx); err != nil {
240 common.Logger().WithFields(logrus.Fields{
241 "device": o,
242 }).Error("Problem establishing stream to ONU")
243 o.RemoveOnu(ctx, port)
244 return
245 }
246
247 defer o.egressHandler.Close()
248 packetSource := gopacket.NewPacketSource(o.egressHandler, o.egressHandler.LinkType())
249 common.Logger().WithFields(logrus.Fields{
250 "device": o,
251 "interface": o.InternalIf,
252 }).Debug("Listening to incoming EGRESS data")
253
254 // Wait for incoming EGRESS data
255 for packet := range packetSource.Packets() {
256 if dot1q := common.GetDot1QLayer(packet); dot1q != nil {
257 common.Logger().WithFields(logrus.Fields{
258 "device": o,
259 "packet": packet,
260 }).Debug("Received EGRESS packet")
261
262 o.Forward(ctx, 2, packet)
263 }
264 }
265
266 common.Logger().WithFields(logrus.Fields{
267 "device": o,
268 }).Debug("No more packets to process")
269
270 if reply, err = onu.Stream.CloseAndRecv(); err != nil {
271 common.Logger().WithFields(logrus.Fields{
272 "device": o,
273 "error": err.Error(),
274 }).Error("A problem occurred while closing client stream")
275 } else {
276 common.Logger().WithFields(logrus.Fields{
277 "device": o,
278 "reply": reply,
279 }).Warn("Client stream closed")
280 }
281}
282
283/*
284GetOnus returns the list of registered ONU devices
285*/
286func (o *PonSimOltDevice) GetOnus() map[int32]*OnuRegistree {
287 if o.Onus == nil {
288 o.Onus = make(map[int32]*OnuRegistree)
289 }
290
291 return o.Onus
292}
293
294/*
295GetOnu return a specific registered ONU
296*/
297func (o *PonSimOltDevice) GetOnu(index int32) *OnuRegistree {
298 var onu *OnuRegistree
299 var ok bool
300
301 if onu, ok = (o.GetOnus())[index]; ok {
302 return onu
303 }
304
305 return nil
306}
307
308func (o *PonSimOltDevice) GetOutgoing() chan []byte {
309 return o.outgoing
310}
311
312/*
313nextAvailablePort returns a port that is not already used by a registered ONU
314*/
315func (o *PonSimOltDevice) nextAvailablePort() int32 {
316 var port int32 = BASE_PORT_NUMBER
317
318 if len(o.GetOnus()) < o.MaxOnuCount {
319 for {
320 if o.GetOnu(port) != nil {
321 // port is already used
322 port += 1
323 } else {
324 // port is available... use it
325 return port
326 }
327 }
328 } else {
329 // OLT has reached its max number of ONUs
330 return -1
331 }
332}
333
334/*
335AddOnu registers an ONU device and sets up all required monitoring and connections
336*/
337func (o *PonSimOltDevice) AddOnu(onu *PonSimOnuDevice) (int32, error) {
338 var portNum int32
339 ctx := context.Background()
340
341 if portNum = o.nextAvailablePort(); portNum != -1 {
342 common.Logger().WithFields(logrus.Fields{
343 "device": o,
344 "port": portNum,
345 "onu": onu,
346 }).Info("Adding ONU")
347
348 registree := &OnuRegistree{Device: onu}
349
350 // Setup GRPC communication and check if it succeeded
351 if err := o.ConnectToRemoteOnu(registree); err == nil {
352 o.GetOnus()[portNum] = registree
353
354 o.AddLink(1, int(portNum), o.forwardToONU(portNum))
355 go o.MonitorOnu(ctx, portNum)
356 go o.Listen(ctx, portNum)
357 }
358
359 } else {
360 common.Logger().WithFields(logrus.Fields{
361 "device": o,
362 }).Warn("ONU Map is full")
363 }
364
365 return int32(portNum), nil
366}
367
368/*
369RemoveOnu removes the reference to a registered ONU
370*/
371func (o *PonSimOltDevice) RemoveOnu(ctx context.Context, onuIndex int32) error {
372 onu := o.GetOnu(onuIndex)
373 if err := onu.Conn.Close(); err != nil {
374 common.Logger().WithFields(logrus.Fields{
375 "device": o,
376 "onu": onu.Device,
377 "onuIndex": onuIndex,
378 }).Error("Problem closing connection to ONU")
379 }
380
381 common.Logger().WithFields(logrus.Fields{
382 "device": o,
383 "onu": onu,
384 "onuIndex": onuIndex,
385 }).Info("Removing ONU")
386
387 delete(o.Onus, onuIndex)
388
389 // Remove link entries for this ONU
390 o.RemoveLink(1, int(onuIndex))
391
392 return nil
393}
394
395/*
396MonitorOnu verifies the connection status of a specific ONU and cleans up as necessary
397*/
398func (o *PonSimOltDevice) MonitorOnu(ctx context.Context, onuIndex int32) {
399 for {
400 if o.GetOnu(onuIndex) != nil {
401 if conn := o.GetOnu(onuIndex).Conn; conn.GetState() == connectivity.Ready {
402 // Wait for any change to occur
403 conn.WaitForStateChange(ctx, conn.GetState())
404 // We lost communication with the ONU ... remove it
405 o.RemoveOnu(ctx, onuIndex)
406 return
407 }
408 common.Logger().WithFields(logrus.Fields{
409 "device": o,
410 "ctx": ctx,
411 "onuIndex": onuIndex,
412 }).Debug("ONU is not ready")
413 time.Sleep(1 * time.Second)
414 } else {
415 return
416 }
417 }
418}