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