blob: 9ab8abb81104add035b934f6b56ad3f45acdcb34 [file] [log] [blame]
Matteo Scandolo813402b2019-10-23 19:24:52 -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 (
Matteo Scandolo813402b2019-10-23 19:24:52 -070020 "github.com/google/gopacket/layers"
21 "github.com/looplab/fsm"
Matteo Scandolo3de9de02019-11-14 13:40:03 -080022 "github.com/opencord/voltha-protos/v2/go/openolt"
Matteo Scandolo813402b2019-10-23 19:24:52 -070023 "gotest.tools/assert"
Matteo Scandolo5ff80082019-12-20 13:20:57 -080024 "sync"
Matteo Scandolo813402b2019-10-23 19:24:52 -070025 "testing"
Matteo Scandolo5ff80082019-12-20 13:20:57 -080026 "time"
Matteo Scandolo813402b2019-10-23 19:24:52 -070027)
28
Matteo Scandolo813402b2019-10-23 19:24:52 -070029func Test_Onu_SendEapolFlow(t *testing.T) {
Matteo Scandoloc1147092019-10-29 09:38:33 -070030 onu := createMockOnu(1, 1, 900, 900, false, false)
Matteo Scandolo813402b2019-10-23 19:24:52 -070031
32 client := &mockClient{
33 FlowAddSpy: FlowAddSpy{
34 Calls: make(map[int]*openolt.Flow),
35 },
36 fail: false,
37 }
38
39 onu.sendEapolFlow(client)
40 assert.Equal(t, client.FlowAddSpy.CallCount, 1)
41
42 assert.Equal(t, client.FlowAddSpy.Calls[1].AccessIntfId, int32(onu.PonPortID))
43 assert.Equal(t, client.FlowAddSpy.Calls[1].OnuId, int32(onu.ID))
44 assert.Equal(t, client.FlowAddSpy.Calls[1].UniId, int32(0))
45 assert.Equal(t, client.FlowAddSpy.Calls[1].FlowId, onu.ID)
46 assert.Equal(t, client.FlowAddSpy.Calls[1].FlowType, "downstream")
47 assert.Equal(t, client.FlowAddSpy.Calls[1].PortNo, onu.ID)
48}
49
50// validates that when an ONU receives an EAPOL flow for UNI 0
Matteo Scandolo5ff80082019-12-20 13:20:57 -080051// and the GemPort has already been configured
Matteo Scandolo813402b2019-10-23 19:24:52 -070052// it transition to auth_started state
Matteo Scandolo5ff80082019-12-20 13:20:57 -080053func Test_HandleFlowUpdateEapolWithGem(t *testing.T) {
Matteo Scandolo813402b2019-10-23 19:24:52 -070054
Matteo Scandoloc1147092019-10-29 09:38:33 -070055 onu := createMockOnu(1, 1, 900, 900, true, false)
Matteo Scandolo813402b2019-10-23 19:24:52 -070056
57 onu.InternalState = fsm.NewFSM(
Matteo Scandolo5ff80082019-12-20 13:20:57 -080058 "enabled",
Matteo Scandolo813402b2019-10-23 19:24:52 -070059 fsm.Events{
Matteo Scandolo5ff80082019-12-20 13:20:57 -080060 {Name: "start_auth", Src: []string{"enabled"}, Dst: "auth_started"},
Matteo Scandolo813402b2019-10-23 19:24:52 -070061 },
62 fsm.Callbacks{},
63 )
64
65 flow := openolt.Flow{
66 AccessIntfId: int32(onu.PonPortID),
67 OnuId: int32(onu.ID),
68 UniId: int32(0),
69 FlowId: uint32(onu.ID),
70 FlowType: "downstream",
71 AllocId: int32(0),
72 NetworkIntfId: int32(0),
73 Classifier: &openolt.Classifier{
74 EthType: uint32(layers.EthernetTypeEAPOL),
75 OVid: 4091,
76 },
77 Action: &openolt.Action{},
78 Priority: int32(100),
79 PortNo: uint32(onu.ID), // NOTE we are using this to map an incoming packetIndication to an ONU
80 }
81
82 msg := OnuFlowUpdateMessage{
83 PonPortID: 1,
84 OnuID: 1,
85 Flow: &flow,
86 }
87
88 onu.handleFlowUpdate(msg)
89 assert.Equal(t, onu.InternalState.Current(), "auth_started")
90}
91
92// validates that when an ONU receives an EAPOL flow for UNI that is not 0
Matteo Scandolo5ff80082019-12-20 13:20:57 -080093// no action is taken (this is independent of GemPort status
94func Test_HandleFlowUpdateEapolWrongUNI(t *testing.T) {
Matteo Scandolo813402b2019-10-23 19:24:52 -070095
Matteo Scandolo5ff80082019-12-20 13:20:57 -080096 onu := createMockOnu(1, 1, 900, 900, true, false)
Matteo Scandolo813402b2019-10-23 19:24:52 -070097
98 onu.InternalState = fsm.NewFSM(
99 "enabled",
100 fsm.Events{
Matteo Scandolo5ff80082019-12-20 13:20:57 -0800101 {Name: "start_auth", Src: []string{"enabled"}, Dst: "auth_started"},
Matteo Scandolo813402b2019-10-23 19:24:52 -0700102 },
103 fsm.Callbacks{},
104 )
105
106 flow := openolt.Flow{
107 AccessIntfId: int32(onu.PonPortID),
108 OnuId: int32(onu.ID),
109 UniId: int32(1),
110 FlowId: uint32(onu.ID),
111 FlowType: "downstream",
112 AllocId: int32(0),
113 NetworkIntfId: int32(0),
114 Classifier: &openolt.Classifier{
115 EthType: uint32(layers.EthernetTypeEAPOL),
116 OVid: 4091,
117 },
118 Action: &openolt.Action{},
119 Priority: int32(100),
120 PortNo: uint32(onu.ID), // NOTE we are using this to map an incoming packetIndication to an ONU
121 }
122
123 msg := OnuFlowUpdateMessage{
124 PonPortID: 1,
125 OnuID: 1,
126 Flow: &flow,
127 }
128
129 onu.handleFlowUpdate(msg)
130 assert.Equal(t, onu.InternalState.Current(), "enabled")
131}
132
Matteo Scandoloc1147092019-10-29 09:38:33 -0700133// validates that when an ONU receives an EAPOL flow for UNI 0
Matteo Scandolo5ff80082019-12-20 13:20:57 -0800134// and the GemPort has not yet been configured
135// it transition to auth_started state
136func Test_HandleFlowUpdateEapolWithoutGem(t *testing.T) {
137
138 onu := createMockOnu(1, 1, 900, 900, true, false)
139 onu.GemPortAdded = false
Matteo Scandoloc1147092019-10-29 09:38:33 -0700140
141 onu.InternalState = fsm.NewFSM(
Matteo Scandolo5ff80082019-12-20 13:20:57 -0800142 "enabled",
Matteo Scandoloc1147092019-10-29 09:38:33 -0700143 fsm.Events{
Matteo Scandolo5ff80082019-12-20 13:20:57 -0800144 {Name: "start_auth", Src: []string{"enabled"}, Dst: "auth_started"},
Matteo Scandoloc1147092019-10-29 09:38:33 -0700145 },
146 fsm.Callbacks{},
147 )
148
149 flow := openolt.Flow{
150 AccessIntfId: int32(onu.PonPortID),
151 OnuId: int32(onu.ID),
152 UniId: int32(0),
153 FlowId: uint32(onu.ID),
154 FlowType: "downstream",
155 AllocId: int32(0),
156 NetworkIntfId: int32(0),
157 Classifier: &openolt.Classifier{
158 EthType: uint32(layers.EthernetTypeEAPOL),
159 OVid: 4091,
160 },
161 Action: &openolt.Action{},
162 Priority: int32(100),
163 PortNo: uint32(onu.ID), // NOTE we are using this to map an incoming packetIndication to an ONU
164 }
165
166 msg := OnuFlowUpdateMessage{
167 PonPortID: 1,
168 OnuID: 1,
169 Flow: &flow,
170 }
171
172 onu.handleFlowUpdate(msg)
Matteo Scandolo5ff80082019-12-20 13:20:57 -0800173
174 wg := sync.WaitGroup{}
175 wg.Add(1)
176 go func(wg *sync.WaitGroup) {
177 defer wg.Done()
178 time.Sleep(100 * time.Millisecond)
179
180 // emulate the addition of a GemPort
181 for _, ch := range onu.GemPortChannels {
182 ch <- true
183 }
184
185 time.Sleep(100 * time.Millisecond)
186 assert.Equal(t, onu.InternalState.Current(), "auth_started")
187 }(&wg)
188 wg.Wait()
189
Matteo Scandoloc1147092019-10-29 09:38:33 -0700190}
191
Matteo Scandolo5ff80082019-12-20 13:20:57 -0800192// validates that when an ONU receives an EAPOL flow for UNI 0
193// but the noAuth bit is set no action is taken
194func Test_HandleFlowUpdateEapolNoAuth(t *testing.T) {
195 onu := createMockOnu(1, 1, 900, 900, false, false)
196
197 onu.InternalState = fsm.NewFSM(
198 "enabled",
199 fsm.Events{
200 {Name: "start_auth", Src: []string{"enabled"}, Dst: "auth_started"},
201 },
202 fsm.Callbacks{},
203 )
204
205 flow := openolt.Flow{
206 AccessIntfId: int32(onu.PonPortID),
207 OnuId: int32(onu.ID),
208 UniId: int32(0),
209 FlowId: uint32(onu.ID),
210 FlowType: "downstream",
211 AllocId: int32(0),
212 NetworkIntfId: int32(0),
213 Classifier: &openolt.Classifier{
214 EthType: uint32(layers.EthernetTypeEAPOL),
215 OVid: 4091,
216 },
217 Action: &openolt.Action{},
218 Priority: int32(100),
219 PortNo: uint32(onu.ID), // NOTE we are using this to map an incoming packetIndication to an ONU
220 }
221
222 msg := OnuFlowUpdateMessage{
223 PonPortID: 1,
224 OnuID: 1,
225 Flow: &flow,
226 }
227
228 onu.handleFlowUpdate(msg)
229 assert.Equal(t, onu.InternalState.Current(), "enabled")
230}
231
232// validates that when an ONU receives a DHCP flow for UNI 0 and pbit 0
233// and the GemPort has already been configured
234// it transition to dhcp_started state
Matteo Scandoloc1147092019-10-29 09:38:33 -0700235func Test_HandleFlowUpdateDhcp(t *testing.T) {
236 onu := createMockOnu(1, 1, 900, 900, false, true)
237
238 onu.InternalState = fsm.NewFSM(
239 "eap_response_success_received",
240 fsm.Events{
241 {Name: "start_dhcp", Src: []string{"eap_response_success_received"}, Dst: "dhcp_started"},
242 },
243 fsm.Callbacks{},
244 )
245
246 flow := openolt.Flow{
247 AccessIntfId: int32(onu.PonPortID),
248 OnuId: int32(onu.ID),
249 UniId: int32(0),
250 FlowId: uint32(onu.ID),
251 FlowType: "downstream",
252 AllocId: int32(0),
253 NetworkIntfId: int32(0),
254 Classifier: &openolt.Classifier{
255 EthType: uint32(layers.EthernetTypeIPv4),
256 SrcPort: uint32(68),
257 DstPort: uint32(67),
Matteo Scandolo5ff80082019-12-20 13:20:57 -0800258 OPbits: 0,
Matteo Scandoloc1147092019-10-29 09:38:33 -0700259 },
260 Action: &openolt.Action{},
261 Priority: int32(100),
262 PortNo: uint32(onu.ID), // NOTE we are using this to map an incoming packetIndication to an ONU
263 }
264
265 msg := OnuFlowUpdateMessage{
266 PonPortID: 1,
267 OnuID: 1,
268 Flow: &flow,
269 }
270
271 onu.handleFlowUpdate(msg)
272 assert.Equal(t, onu.InternalState.Current(), "dhcp_started")
273 assert.Equal(t, onu.DhcpFlowReceived, true)
274}
275
Matteo Scandolo5ff80082019-12-20 13:20:57 -0800276// validates that when an ONU receives a DHCP flow for UNI 0 and pbit 255
277// and the GemPort has already been configured
278// it transition to dhcp_started state
Matteo Scandolod74abba2020-04-16 16:36:44 -0700279func Test_HandleFlowUpdateDhcpPBit255(t *testing.T) {
280 onu := createMockOnu(1, 1, 900, 900, false, true)
281
282 onu.InternalState = fsm.NewFSM(
283 "eap_response_success_received",
284 fsm.Events{
285 {Name: "start_dhcp", Src: []string{"eap_response_success_received"}, Dst: "dhcp_started"},
286 },
287 fsm.Callbacks{},
288 )
289
290 flow := openolt.Flow{
291 AccessIntfId: int32(onu.PonPortID),
292 OnuId: int32(onu.ID),
293 UniId: int32(0),
294 FlowId: uint32(onu.ID),
295 FlowType: "downstream",
296 AllocId: int32(0),
297 NetworkIntfId: int32(0),
298 Classifier: &openolt.Classifier{
299 EthType: uint32(layers.EthernetTypeIPv4),
300 SrcPort: uint32(68),
301 DstPort: uint32(67),
Matteo Scandolo5ff80082019-12-20 13:20:57 -0800302 OPbits: 255,
Matteo Scandolod74abba2020-04-16 16:36:44 -0700303 },
304 Action: &openolt.Action{},
305 Priority: int32(100),
306 PortNo: uint32(onu.ID), // NOTE we are using this to map an incoming packetIndication to an ONU
307 }
308
309 msg := OnuFlowUpdateMessage{
310 PonPortID: 1,
311 OnuID: 1,
312 Flow: &flow,
313 }
314
315 onu.handleFlowUpdate(msg)
316 assert.Equal(t, onu.InternalState.Current(), "dhcp_started")
317 assert.Equal(t, onu.DhcpFlowReceived, true)
318}
319
Matteo Scandolo5ff80082019-12-20 13:20:57 -0800320// validates that when an ONU receives a DHCP flow for UNI 0 and pbit not 0 or 255
321// and the GemPort has already been configured
322// it ignores the message
Matteo Scandolod74abba2020-04-16 16:36:44 -0700323func Test_HandleFlowUpdateDhcpIgnoreByPbit(t *testing.T) {
324 onu := createMockOnu(1, 1, 900, 900, false, true)
325
326 onu.InternalState = fsm.NewFSM(
327 "eap_response_success_received",
328 fsm.Events{
329 {Name: "start_dhcp", Src: []string{"eap_response_success_received"}, Dst: "dhcp_started"},
330 },
331 fsm.Callbacks{},
332 )
333
334 flow := openolt.Flow{
335 AccessIntfId: int32(onu.PonPortID),
336 OnuId: int32(onu.ID),
337 UniId: int32(0),
338 FlowId: uint32(onu.ID),
339 FlowType: "downstream",
340 AllocId: int32(0),
341 NetworkIntfId: int32(0),
342 Classifier: &openolt.Classifier{
343 EthType: uint32(layers.EthernetTypeIPv4),
344 SrcPort: uint32(68),
345 DstPort: uint32(67),
Matteo Scandolo5ff80082019-12-20 13:20:57 -0800346 OPbits: 1,
Matteo Scandolod74abba2020-04-16 16:36:44 -0700347 },
348 Action: &openolt.Action{},
349 Priority: int32(100),
350 PortNo: uint32(onu.ID), // NOTE we are using this to map an incoming packetIndication to an ONU
351 }
352
353 msg := OnuFlowUpdateMessage{
354 PonPortID: 1,
355 OnuID: 1,
356 Flow: &flow,
357 }
358
359 onu.handleFlowUpdate(msg)
360 assert.Equal(t, onu.InternalState.Current(), "eap_response_success_received")
361 assert.Equal(t, onu.DhcpFlowReceived, false)
362}
363
Matteo Scandolo5ff80082019-12-20 13:20:57 -0800364// validates that when an ONU receives a DHCP flow for UNI 0
365// but the noDchp bit is set no action is taken
Matteo Scandoloc1147092019-10-29 09:38:33 -0700366func Test_HandleFlowUpdateDhcpNoDhcp(t *testing.T) {
367 onu := createMockOnu(1, 1, 900, 900, false, false)
368
369 onu.InternalState = fsm.NewFSM(
370 "eap_response_success_received",
371 fsm.Events{
372 {Name: "start_dhcp", Src: []string{"eap_response_success_received"}, Dst: "dhcp_started"},
373 },
374 fsm.Callbacks{},
375 )
376
377 flow := openolt.Flow{
378 AccessIntfId: int32(onu.PonPortID),
379 OnuId: int32(onu.ID),
380 UniId: int32(0),
381 FlowId: uint32(onu.ID),
382 FlowType: "downstream",
383 AllocId: int32(0),
384 NetworkIntfId: int32(0),
385 Classifier: &openolt.Classifier{
386 EthType: uint32(layers.EthernetTypeIPv4),
387 SrcPort: uint32(68),
388 DstPort: uint32(67),
389 },
390 Action: &openolt.Action{},
391 Priority: int32(100),
392 PortNo: uint32(onu.ID), // NOTE we are using this to map an incoming packetIndication to an ONU
393 }
394
395 msg := OnuFlowUpdateMessage{
396 PonPortID: 1,
397 OnuID: 1,
398 Flow: &flow,
399 }
400
401 onu.handleFlowUpdate(msg)
402 assert.Equal(t, onu.InternalState.Current(), "eap_response_success_received")
Matteo Scandolod74abba2020-04-16 16:36:44 -0700403 assert.Equal(t, onu.DhcpFlowReceived, false)
Matteo Scandoloc1147092019-10-29 09:38:33 -0700404}
Matteo Scandolo5ff80082019-12-20 13:20:57 -0800405
406// validates that when an ONU receives a DHCP flow for UNI 0 and pbit not 0 or 255
407// and the GemPort has not already been configured
408// it transition to dhcp_started state
409func Test_HandleFlowUpdateDhcpWithoutGem(t *testing.T) {
410 // NOTE that this feature is required when we do DHCP with no eapol
411 // as the DHCP flow will be the first one received
412 onu := createMockOnu(1, 1, 900, 900, false, true)
413
414 onu.GemPortAdded = false
415
416 onu.InternalState = fsm.NewFSM(
417 "enabled",
418 fsm.Events{
419 {Name: "start_dhcp", Src: []string{"enabled"}, Dst: "dhcp_started"},
420 },
421 fsm.Callbacks{},
422 )
423
424 flow := openolt.Flow{
425 AccessIntfId: int32(onu.PonPortID),
426 OnuId: int32(onu.ID),
427 UniId: int32(0),
428 FlowId: uint32(onu.ID),
429 FlowType: "downstream",
430 AllocId: int32(0),
431 NetworkIntfId: int32(0),
432 Classifier: &openolt.Classifier{
433 EthType: uint32(layers.EthernetTypeIPv4),
434 SrcPort: uint32(68),
435 DstPort: uint32(67),
436 OPbits: 0,
437 },
438 Action: &openolt.Action{},
439 Priority: int32(100),
440 PortNo: uint32(onu.ID), // NOTE we are using this to map an incoming packetIndication to an ONU
441 }
442
443 msg := OnuFlowUpdateMessage{
444 PonPortID: 1,
445 OnuID: 1,
446 Flow: &flow,
447 }
448
449 onu.handleFlowUpdate(msg)
450
451 wg := sync.WaitGroup{}
452 wg.Add(1)
453 go func(wg *sync.WaitGroup) {
454 defer wg.Done()
455 time.Sleep(100 * time.Millisecond)
456
457 // emulate the addition of a GemPort
458 for _, ch := range onu.GemPortChannels {
459 ch <- true
460 }
461
462 time.Sleep(100 * time.Millisecond)
463 assert.Equal(t, onu.InternalState.Current(), "dhcp_started")
464 assert.Equal(t, onu.DhcpFlowReceived, true)
465 }(&wg)
466 wg.Wait()
467}