| /* |
| * Copyright 2018-2024 Open Networking Foundation (ONF) and the ONF Contributors |
| |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package eapol |
| |
| import ( |
| "errors" |
| "fmt" |
| "net" |
| "testing" |
| |
| "github.com/google/gopacket" |
| "github.com/google/gopacket/layers" |
| "github.com/looplab/fsm" |
| "github.com/opencord/bbsim/internal/bbsim/types" |
| "github.com/opencord/voltha-protos/v5/go/openolt" |
| "google.golang.org/grpc" |
| "gotest.tools/assert" |
| ) |
| |
| // MOCKS |
| |
| var eapolStateMachine = fsm.NewFSM( |
| StateAuthStarted, |
| fsm.Events{ |
| {Name: EventStartSent, Src: []string{StateAuthStarted}, Dst: StateStartSent}, |
| {Name: EventResponseIdentitySent, Src: []string{StateStartSent}, Dst: StateResponseIdentitySent}, |
| {Name: EventResponseChallengeSent, Src: []string{StateResponseIdentitySent}, Dst: StateResponseChallengeSent}, |
| {Name: EventResponseSuccessReceived, Src: []string{StateResponseChallengeSent}, Dst: StateResponseSuccessReceived}, |
| {Name: EventAuthFailed, Src: []string{StateAuthStarted, StateStartSent, StateResponseIdentitySent, StateResponseChallengeSent}, Dst: StateAuthFailed}, |
| }, |
| fsm.Callbacks{}, |
| ) |
| |
| // params for the function under test |
| var onuId uint32 = 1 |
| var gemPortId uint32 = 1 |
| var ponPortId uint32 = 0 |
| var uniId uint32 = 0 |
| var serialNumber string = "BBSM00000001" |
| var macAddress = net.HardwareAddr{0x01, 0x80, 0xC2, 0x00, 0x00, 0x03} |
| var portNo uint32 = 16 |
| var serviceId uint32 = 0 |
| var oltId int = 0 |
| |
| type mockStream struct { |
| grpc.ServerStream |
| CallCount int |
| Calls map[int]*openolt.PacketIndication |
| fail bool |
| } |
| |
| func (s *mockStream) Send(ind *openolt.Indication) error { |
| s.CallCount++ |
| if s.fail { |
| return errors.New("fake-error") |
| } |
| s.Calls[s.CallCount] = ind.GetPktInd() |
| return nil |
| } |
| |
| // TESTS |
| |
| func TestSendEapStartSuccess(t *testing.T) { |
| eapolStateMachine.SetState(StateAuthStarted) |
| |
| stream := &mockStream{ |
| Calls: make(map[int]*openolt.PacketIndication), |
| fail: false, |
| } |
| |
| if err := SendEapStart(onuId, ponPortId, serialNumber, portNo, macAddress, gemPortId, uniId, eapolStateMachine, stream); err != nil { |
| t.Errorf("SendEapStart returned an error: %v", err) |
| t.Fail() |
| } |
| |
| assert.Equal(t, stream.CallCount, 1) |
| assert.Equal(t, stream.Calls[1].PortNo, portNo) |
| assert.Equal(t, stream.Calls[1].IntfId, ponPortId) |
| assert.Equal(t, stream.Calls[1].IntfType, "pon") |
| assert.Equal(t, stream.Calls[1].GemportId, uint32(gemPortId)) |
| |
| assert.Equal(t, eapolStateMachine.Current(), StateStartSent) |
| |
| } |
| |
| func TestSendEapStartFailStreamError(t *testing.T) { |
| |
| eapolStateMachine.SetState(StateAuthStarted) |
| |
| stream := &mockStream{ |
| Calls: make(map[int]*openolt.PacketIndication), |
| fail: true, |
| } |
| |
| err := SendEapStart(onuId, ponPortId, serialNumber, portNo, macAddress, gemPortId, uniId, eapolStateMachine, stream) |
| if err == nil { |
| t.Errorf("SendEapStart did not return an error") |
| t.Fail() |
| } |
| |
| assert.Equal(t, err.Error(), "fake-error") |
| |
| assert.Equal(t, eapolStateMachine.Current(), StateAuthFailed) |
| } |
| |
| // TODO test eapol.HandleNextPacket |
| |
| func TestUpdateAuthFailed(t *testing.T) { |
| |
| var onuId uint32 = 1 |
| var ponPortId uint32 = 0 |
| var serialNumber string = "BBSM00000001" |
| |
| eapolStateMachine.SetState(StateAuthStarted) |
| _ = updateAuthFailed(onuId, ponPortId, serialNumber, eapolStateMachine) |
| assert.Equal(t, eapolStateMachine.Current(), "auth_failed") |
| |
| eapolStateMachine.SetState(StateStartSent) |
| _ = updateAuthFailed(onuId, ponPortId, serialNumber, eapolStateMachine) |
| assert.Equal(t, eapolStateMachine.Current(), "auth_failed") |
| |
| eapolStateMachine.SetState(StateResponseIdentitySent) |
| _ = updateAuthFailed(onuId, ponPortId, serialNumber, eapolStateMachine) |
| assert.Equal(t, eapolStateMachine.Current(), "auth_failed") |
| |
| eapolStateMachine.SetState(StateResponseChallengeSent) |
| _ = updateAuthFailed(onuId, ponPortId, serialNumber, eapolStateMachine) |
| assert.Equal(t, eapolStateMachine.Current(), "auth_failed") |
| |
| eapolStateMachine.SetState(StateResponseSuccessReceived) |
| err := updateAuthFailed(onuId, ponPortId, serialNumber, eapolStateMachine) |
| if err == nil { |
| t.Errorf("updateAuthFailed did not return an error") |
| t.Fail() |
| } |
| assert.Equal(t, err.Error(), fmt.Sprintf("event %s inappropriate in current state %s", EventAuthFailed, StateResponseSuccessReceived)) |
| |
| } |
| |
| func createTestEAPOLPkt(eap *layers.EAP) gopacket.Packet { |
| bytes := createEAPOLPkt(eap, serviceId, uniId, onuId, ponPortId, oltId) |
| return gopacket.NewPacket(bytes, layers.LayerTypeEthernet, gopacket.Default) |
| } |
| |
| func handleTestEAPOLPkt(pkt gopacket.Packet, stream types.Stream) { |
| HandleNextPacket(onuId, ponPortId, gemPortId, serialNumber, portNo, uniId, serviceId, oltId, eapolStateMachine, pkt, stream, nil) |
| } |
| |
| func TestDropUnexpectedPackets(t *testing.T) { |
| stream := &mockStream{ |
| Calls: make(map[int]*openolt.PacketIndication), |
| } |
| |
| const eapId uint8 = 1 |
| |
| //Create test packets |
| identityRequest := createEAPIdentityRequest(eapId) |
| challangeRequest := createEAPChallengeRequest(eapId, []byte{0x10}) |
| success := createEAPSuccess(eapId) |
| |
| identityPkt := createTestEAPOLPkt(identityRequest) |
| challangePkt := createTestEAPOLPkt(challangeRequest) |
| successPkt := createTestEAPOLPkt(success) |
| |
| testStates := map[string]struct { |
| packets []gopacket.Packet |
| destinationState string |
| }{ |
| //All packet should be dropped in state auth_started |
| StateAuthStarted: {[]gopacket.Packet{identityPkt, challangePkt, successPkt}, StateAuthFailed}, |
| //Only the identity request packet should be handled in state eap_start_sent |
| StateStartSent: {[]gopacket.Packet{challangePkt, successPkt}, StateAuthFailed}, |
| //Only the challange request packet should be handled in state eap_response_identity_sent |
| StateResponseIdentitySent: {[]gopacket.Packet{identityPkt, successPkt}, StateAuthFailed}, |
| //Only the success packet should be handled in state eap_response_challenge_sent |
| StateResponseChallengeSent: {[]gopacket.Packet{identityPkt, challangePkt}, StateAuthFailed}, |
| //All packet should be dropped in state eap_response_success_received |
| StateResponseSuccessReceived: {[]gopacket.Packet{identityPkt, challangePkt, successPkt}, StateResponseSuccessReceived}, |
| //All packet should be dropped in state auth_failed |
| StateAuthFailed: {[]gopacket.Packet{identityPkt, challangePkt}, StateAuthFailed}, |
| } |
| |
| for s, info := range testStates { |
| for _, p := range info.packets { |
| eapolStateMachine.SetState(s) |
| handleTestEAPOLPkt(p, stream) |
| |
| //No response should be sent |
| assert.Equal(t, stream.CallCount, 0) |
| //The state machine should transition to the failed state |
| assert.Equal(t, eapolStateMachine.Current(), info.destinationState) |
| } |
| } |
| } |