blob: ac3a02ae75c17720c64381c2d48d78a3ff951c22 [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 "sync"
23 "time"
24
Stephane Barbarie35595062018-02-08 08:34:39 -050025 "github.com/golang/protobuf/ptypes/empty"
26 "github.com/google/gopacket"
27 "github.com/google/uuid"
28 "github.com/opencord/voltha/ponsim/v2/common"
29 "github.com/opencord/voltha/protos/go/ponsim"
30 "github.com/sirupsen/logrus"
31 "google.golang.org/grpc"
Stephane Barbarie35595062018-02-08 08:34:39 -050032)
33
34// TODO: Cleanup GRPC security config
35// TODO: Pass-in the certificate information as a structure parameter
36
37/*
38PonSimOnuDevice is the structure responsible for the handling of an ONU device
39*/
40type PonSimOnuDevice struct {
41 PonSimDevice
42
43 ParentAddress string
44 ParentPort int32
45 AssignedPort int32
46 Conn *grpc.ClientConn
47
48 oltClient ponsim.PonSimCommonClient
49 stream ponsim.PonSimCommon_ProcessDataClient
50 monitor chan PonSimDeviceState
51 state PonSimDeviceState
52}
53
54/*
55NewPonSimOnuDevice instantiates a new ONU device structure
56*/
57func NewPonSimOnuDevice(device PonSimDevice) *PonSimOnuDevice {
58 onu := &PonSimOnuDevice{PonSimDevice: device}
59
60 return onu
61}
62
63/*
64forwardToOLT defines a INGRESS function to forward a packet to the parent OLT
65*/
66func (o *PonSimOnuDevice) forwardToOLT() func(int, gopacket.Packet) {
67 return func(port int, frame gopacket.Packet) {
68 ipAddress := common.GetInterfaceIP(o.InternalIf)
69 incoming := &ponsim.IncomingData{
70 Id: "INGRESS.ONU." + ipAddress,
71 Address: ipAddress,
72 Port: int32(port),
73 Payload: frame.Data(),
74 }
75 common.Logger().WithFields(logrus.Fields{
76 "device": o,
77 "port": port,
78 "frame": frame,
79 "frameDump": frame.Dump(),
80 "incoming": incoming,
81 }).Debug("Forwarding to OLT")
82
83 // Forward packet to OLT
84 if err := o.stream.Send(incoming); err != nil {
85 common.Logger().WithFields(logrus.Fields{
86 "device": o,
87 "port": port,
88 "frameDump": frame.Dump(),
89 "incoming": incoming,
90 }).Fatal("A problem occurred while forwarding to OLT")
91 }
92 }
93}
94
95/*
96forwardToWAN defines a EGRESS function to forward a packet to the world
97*/
98func (o *PonSimOnuDevice) forwardToWAN() func(int, gopacket.Packet) {
99 return func(port int, frame gopacket.Packet) {
100 var err error
101 common.Logger().WithFields(logrus.Fields{
102 "device": o,
103 "port": port,
104 "frame": frame,
105 }).Debug("Forwarding packet to world")
106 if err = o.ingressHandler.WritePacketData(frame.Data()); err != nil {
107 common.Logger().WithFields(logrus.Fields{
108 "device": o,
109 "port": port,
110 "frame": frame,
111 }).Fatal("Problem while forwarding packet to world")
112 } else {
113 common.Logger().WithFields(logrus.Fields{
114 "device": o,
115 "port": port,
116 "frame": frame,
117 }).Debug("Forwarded packet to world")
118 }
119 }
120}
121
122/*
123Start performs setup operations for an ONU device
124*/
125func (o *PonSimOnuDevice) Start(ctx context.Context) {
126 // Initialize the parent
127 o.PonSimDevice.Start(ctx)
128
129 // Setup flow behaviours
130 // ONU -> OLT
131 o.AddLink(1, 0, o.forwardToOLT())
132 // ONU -> World
133 o.AddLink(2, 0, o.forwardToWAN())
134
135 go o.MonitorConnection(ctx)
136}
137
138/*
139Stop performs cleanup operations for an ONU device
140*/
141func (o *PonSimOnuDevice) Stop(ctx context.Context) {
142 common.Logger().WithFields(logrus.Fields{
143 "device": o,
144 }).Debug("Stopping ONU")
145
146 o.RemoveLink(1, 0)
147 o.RemoveLink(2, 0)
148
149 o.PonSimDevice.Stop(ctx)
150}
151
152/*
153Listen waits for incoming INGRESS data on the external interface
154*/
155func (o *PonSimOnuDevice) Listen(ctx context.Context) {
156 var reply *empty.Empty
157 var err error
158
159 if o.oltClient = ponsim.NewPonSimCommonClient(o.Conn); o.oltClient == nil {
160 common.Logger().WithFields(logrus.Fields{
161 "device": o,
162 }).Fatal("Problem establishing client connection to OLT")
163 panic("Problem establishing client connection to OLT")
164 }
165
166 // Establish GRPC connection with OLT
167 if o.stream, err = o.oltClient.ProcessData(ctx); err != nil {
168 common.Logger().WithFields(logrus.Fields{
169 "device": o,
170 "error": err.Error(),
171 }).Fatal("Problem establishing stream")
172 panic(err)
173 }
174
175 defer o.ingressHandler.Close()
176 packetSource := gopacket.NewPacketSource(o.ingressHandler, o.ingressHandler.LinkType())
177 common.Logger().WithFields(logrus.Fields{
178 "device": o,
179 "interface": o.ExternalIf,
180 }).Debug("Listening to incoming ONU data")
181
182 for packet := range packetSource.Packets() {
183 common.Logger().WithFields(logrus.Fields{
184 "device": o,
185 "packet": packet,
186 }).Debug("Received INGRESS packet")
187
188 o.Forward(ctx, 2, packet)
189 }
190
191 common.Logger().WithFields(logrus.Fields{
192 "device": o,
193 }).Debug("No more packets to process")
194
195 if reply, err = o.stream.CloseAndRecv(); err != nil {
196 common.Logger().Fatal("A problem occurred while closing Ingress stream", err.Error())
197 } else {
198 common.Logger().Info("Ingress stream closed", reply)
199 }
200}
201
202/*
203Register sends a registration request to the remote OLT
204*/
205func (o *PonSimOnuDevice) Register(ctx context.Context) error {
206 var err error
207 var rreq *ponsim.RegistrationRequest
208 var rrep *ponsim.RegistrationReply
209 var client ponsim.PonSimOltClient
210
211 if o.Conn != nil {
212 if client = ponsim.NewPonSimOltClient(o.Conn); client != nil {
213 rreq = &ponsim.RegistrationRequest{
214 Id: uuid.New().String(),
215 Address: common.GetInterfaceIP(o.InternalIf),
216 Port: o.Port,
217 }
218 common.Logger().Printf("Request details %+v\n", rreq)
219
220 // TODO: Loop registration until an OLT becomes available??
221
222 rrep, err = client.Register(ctx, rreq)
223 if err != nil {
224 common.Logger().Printf("Problem with registration", err.Error())
225 } else {
226 // Save OLT address details
227 o.ParentAddress = rrep.GetParentAddress()
228 o.ParentPort = rrep.GetParentPort()
229 o.AssignedPort = rrep.GetAssignedPort()
230
231 common.Logger().Printf("Registration details - %+v\n", rrep)
232
233 o.monitor <- REGISTERED_WITH_OLT
234 }
235
236 } else {
237 common.Logger().Info("Client is NIL")
238 }
239 }
240
241 return err
242}
243
244/*
245MonitorConnection verifies the communication with the OLT
246*/
247func (o *PonSimOnuDevice) MonitorConnection(ctx context.Context) {
248 for {
249 if o.state == DISCONNECTED_FROM_PON {
250 // Establish communication with OLT
251 o.Connect(ctx)
252 }
253
254 if o.state == CONNECTED_TO_PON {
255 // Just stay idle while the ONU-OLT connection is up
256 o.Conn.WaitForStateChange(ctx, o.Conn.GetState())
257
258 // The ONU-OLT connection was lost... need to cleanup
259 o.Disconnect(ctx)
260 }
261
262 time.Sleep(1 * time.Second)
263 }
264}
265
266/*
267Connect sets up communication and monitoring with remote OLT
268*/
269func (o *PonSimOnuDevice) Connect(ctx context.Context) {
270 o.monitor = make(chan PonSimDeviceState, 1)
271
272 // Define a waitgroup to block the current routine until
273 // a CONNECTED state is reached
274 wg := sync.WaitGroup{}
275 wg.Add(1)
276
277 go o.MonitorState(ctx, &wg)
278
279 o.ConnectToRemoteOlt()
280
281 // Wait until we establish a connection to the remote PON
282 wg.Wait()
283}
284
285/*
286Disconnect tears down communication and monitoring with remote OLT
287*/
288func (o *PonSimOnuDevice) Disconnect(ctx context.Context) {
289 if o.egressHandler != nil {
290 o.egressHandler.Close()
291 o.egressHandler = nil
292 }
293
294 if o.Conn != nil {
295 o.Conn.Close()
296 o.Conn = nil
297 }
298
299 if o.monitor != nil {
300 close(o.monitor)
301 o.monitor = nil
302 o.state = DISCONNECTED_FROM_PON
303 }
304}
305
306/*
307MonitorState follows the progress of the OLT connection
308*/
309func (o *PonSimOnuDevice) MonitorState(ctx context.Context, wg *sync.WaitGroup) {
310 // Start a concurrent routine to handle ONU state changes
311 var ok bool
312 for {
313 select {
314 case o.state, ok = <-o.monitor:
315 if ok {
316 common.Logger().WithFields(logrus.Fields{
317 "device": o,
318 "state": o.state,
319 }).Info("Received monitoring state")
320
321 switch o.state {
322 case CONNECTED_TO_PON:
323 // We have successfully connected to the OLT
324 // proceed with registration
325 wg.Done()
326
327 if err := o.Register(ctx); err != nil {
328 o.Disconnect(ctx)
329 }
330
331 case DISCONNECTED_FROM_PON:
332 // Connection to remote OLT was lost... exit
333 common.Logger().WithFields(logrus.Fields{
334 "device": o,
335 }).Warn("Exiting due to disconnection")
336 return
337
338 case REGISTERED_WITH_OLT:
339 // Start listening on network interfaces
340 o.connectNetworkInterfaces()
341 o.monitor <- CONNECTED_IO_INTERFACE
342
343 case CONNECTED_IO_INTERFACE:
344 // Start listening on local interfaces
345 go o.Listen(ctx)
346 }
347 } else {
348 common.Logger().WithFields(logrus.Fields{
349 "device": o,
350 }).Warn("Monitoring channel has closed")
351 return
352 }
353 case <-ctx.Done():
354 common.Logger().WithFields(logrus.Fields{
355 "device": o,
356 }).Warn("Received a cancellation notification")
357
358 return
359 }
360 }
361}
362
363/*
364ConnectToRemoteOlt establishes GRPC communication with the remote OLT
365*/
366func (o *PonSimOnuDevice) ConnectToRemoteOlt() {
367 common.Logger().WithFields(logrus.Fields{
368 "device": o,
369 }).Debug("Connecting to remote device")
370
371 var err error
372
373 host := strings.Join([]string{
374 o.ParentAddress,
375 strconv.Itoa(int(o.ParentPort)),
376 }, ":")
377
Stephane Barbarie35595062018-02-08 08:34:39 -0500378 if o.Conn, err = grpc.DialContext(
Jeff70e8b2d2018-07-24 13:37:29 -0700379 context.Background(), host, grpc.WithInsecure(), grpc.WithBlock(),
Stephane Barbarie35595062018-02-08 08:34:39 -0500380 ); err != nil {
381 common.Logger().WithFields(logrus.Fields{
382 "device": o,
383 "error": err.Error(),
384 }).Error("Problem establishing connection")
385 } else {
386 // We are now connected
387 // time to move on
388 common.Logger().WithFields(logrus.Fields{
389 "device": o,
390 }).Info("Connected to OLT")
391 }
392
393 o.monitor <- CONNECTED_TO_PON
394}