diff --git a/.gitignore b/.gitignore index c294e4dc..ad41b44a 100644 --- a/.gitignore +++ b/.gitignore @@ -26,4 +26,4 @@ cscope.* # Debug *.log -*.pcap +*.pcap \ No newline at end of file diff --git a/go.mod b/go.mod index 659c5c7f..4fd98007 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/free5gc/smf -go 1.21 +go 1.21.0 require ( github.com/antihax/optional v1.0.0 diff --git a/internal/pfcp/handler/handler_test.go b/internal/pfcp/handler/handler_test.go index 0410ee18..82b769bb 100644 --- a/internal/pfcp/handler/handler_test.go +++ b/internal/pfcp/handler/handler_test.go @@ -1,11 +1,165 @@ package handler_test import ( + "bytes" + "fmt" + "io" + "net" + "regexp" "testing" + + "github.com/sirupsen/logrus" + . "github.com/smartystreets/goconvey/convey" + + "github.com/free5gc/pfcp" + "github.com/free5gc/pfcp/pfcpType" + "github.com/free5gc/pfcp/pfcpUdp" + "github.com/free5gc/smf/internal/logger" + "github.com/free5gc/smf/internal/pfcp/handler" ) +type LogCapture struct { + buffer bytes.Buffer +} + +func (lc *LogCapture) Write(p []byte) (n int, err error) { + return lc.buffer.Write(p) +} + +func (lc *LogCapture) String() string { + return lc.buffer.String() +} + +// func TestHandlePfcpHeartbeatRequest(t *testing.T) { +// } + +func TestHandlePfcpManagementRequest(t *testing.T) { + re := regexp.MustCompile(`(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{9}Z)(.*)`) + Convey("Test log message", t, func() { + remoteAddr := &net.UDPAddr{} + testPfcpReq := &pfcp.Message{} + msg := pfcpUdp.NewMessage(remoteAddr, testPfcpReq) + logCapture := &LogCapture{} + logger.Log.SetOutput(io.MultiWriter(logCapture, logrus.StandardLogger().Out)) + handler.HandlePfcpPfdManagementRequest(msg) + capturedLogs := re.FindStringSubmatch(logCapture.String()) + + logCaptureExp := &LogCapture{} + logger.Log.SetOutput(io.MultiWriter(logCaptureExp)) + logger.PfcpLog.Warnf("PFCP PFD Management Request handling is not implemented") + capturedLogsExp := re.FindStringSubmatch(logCaptureExp.String()) + So(capturedLogs[2], ShouldEqual, capturedLogsExp[2]) + }) +} + func TestHandlePfcpAssociationSetupRequest(t *testing.T) { + re := regexp.MustCompile(`(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{9}Z)(.*)`) + re2 := regexp.MustCompile(`(.*)\n(.*)`) + Convey("Test if NodeID is Nil", t, func() { + remoteAddr := &net.UDPAddr{ + IP: net.ParseIP("192.168.1.1"), + Port: 12345, + } + + testPfcpReq := &pfcp.Message{ + Header: pfcp.Header{ + Version: 1, + MP: 0, + S: 0, + MessageType: pfcp.PFCP_ASSOCIATION_SETUP_REQUEST, + MessageLength: 9, + SEID: 0, + SequenceNumber: 1, + MessagePriority: 0, + }, + Body: pfcp.PFCPAssociationSetupRequest{ + NodeID: nil, + }, + } + + logCapture := &LogCapture{} + logger.Log.SetOutput(io.MultiWriter(logCapture, logrus.StandardLogger().Out)) + + msg := pfcpUdp.NewMessage(remoteAddr, testPfcpReq) + handler.HandlePfcpAssociationSetupRequest(msg) + capturedLogs := re.FindStringSubmatch(logCapture.String()) + + logCaptureExp := &LogCapture{} + logger.Log.SetOutput(io.MultiWriter(logCaptureExp)) + logger.PfcpLog.Errorln("pfcp association needs NodeID") + logger.PfcpLog.Infof("Handle PFCP Association Setup Request with NodeID") + ExpLogs := re.FindStringSubmatch(logCaptureExp.String()) + fmt.Println(ExpLogs) + if len(capturedLogs) <= 2 || len(ExpLogs) <= 2 { + t.Errorf("The extracted log is not as expected.") + } + So(capturedLogs[2], ShouldEqual, ExpLogs[2]) + }) + Convey("Test if NodeID is NotNil, upf is Nil", t, func() { + remoteAddr := &net.UDPAddr{ + IP: net.ParseIP("192.168.1.1"), + Port: 12345, + } + + testPfcpReq := &pfcp.Message{ + Header: pfcp.Header{ + Version: 1, + MP: 0, + S: 0, + MessageType: pfcp.PFCP_ASSOCIATION_SETUP_REQUEST, + MessageLength: 9, + SEID: 0, + SequenceNumber: 1, + MessagePriority: 0, + }, + Body: pfcp.PFCPAssociationSetupRequest{ + NodeID: &pfcpType.NodeID{ + NodeIdType: pfcpType.NodeIdTypeIpv4Address, + IP: net.ParseIP("192.168.1.1").To4(), + }, + }, + } + + logCapture := &LogCapture{} + logger.Log.SetOutput(io.MultiWriter(logCapture, logrus.StandardLogger().Out)) + + msg := pfcpUdp.NewMessage(remoteAddr, testPfcpReq) + handler.HandlePfcpAssociationSetupRequest(msg) + capturedLogs := re.FindStringSubmatch(re2.FindStringSubmatch(logCapture.String())[1]) + + logCaptureExp := &LogCapture{} + logger.Log.SetOutput(io.MultiWriter(logCaptureExp)) + logger.PfcpLog.Infof("Handle PFCP Association Setup Request with NodeID[%s]", "192.168.1.1") + logger.PfcpLog.Errorf("can't find UPF[%s]", "192.168.1.1") + ExpLogs := re.FindStringSubmatch(re2.FindStringSubmatch(logCaptureExp.String())[1]) + if len(capturedLogs) <= 2 || len(ExpLogs) <= 2 { + t.Errorf("The extracted log is not as expected.") + } + So(capturedLogs[2], ShouldEqual, ExpLogs[2]) + capturedLogs = re.FindStringSubmatch(re2.FindStringSubmatch(logCapture.String())[2]) + ExpLogs = re.FindStringSubmatch(re2.FindStringSubmatch(logCaptureExp.String())[2]) + So(capturedLogs[2], ShouldEqual, ExpLogs[2]) + }) } -func TestHandlePfcpAssociationReleaseRequest(t *testing.T) { +func TestHandlePfcpAssociationUpdateRequest(t *testing.T) { + re := regexp.MustCompile(`(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{9}Z)(.*)`) + Convey("Test logger message", t, func() { + remoteAddr := &net.UDPAddr{} + testPfcpReq := &pfcp.Message{} + msg := pfcpUdp.NewMessage(remoteAddr, testPfcpReq) + logCapture := &LogCapture{} + logger.Log.SetOutput(io.MultiWriter(logCapture, logrus.StandardLogger().Out)) + handler.HandlePfcpAssociationUpdateRequest(msg) + capturedLogs := re.FindStringSubmatch(logCapture.String()) + + logCaptureExp := &LogCapture{} + logger.Log.SetOutput(io.MultiWriter(logCaptureExp)) + logger.PfcpLog.Warnf("PFCP Association Update Request handling is not implemented") + capturedLogsExp := re.FindStringSubmatch(logCaptureExp.String()) + So(capturedLogs[2], ShouldEqual, capturedLogsExp[2]) + }) } + +// func TestHandlePfcpAssociationReleaseRequest(t *testing.T) { +// } diff --git a/internal/pfcp/message/build_test.go b/internal/pfcp/message/build_test.go new file mode 100644 index 00000000..834bcb16 --- /dev/null +++ b/internal/pfcp/message/build_test.go @@ -0,0 +1,268 @@ +package message_test + +import ( + "net" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/free5gc/pfcp/pfcpType" + "github.com/free5gc/smf/internal/context" + "github.com/free5gc/smf/internal/pfcp/message" + "github.com/free5gc/smf/internal/pfcp/udp" + "github.com/free5gc/smf/pkg/factory" +) + +var testConfig = factory.Config{ + Info: &factory.Info{ + Version: "1.0.0", + Description: "SMF procdeure test configuration", + }, + Configuration: &factory.Configuration{ + Sbi: &factory.Sbi{ + Scheme: "http", + RegisterIPv4: "127.0.0.1", + BindingIPv4: "127.0.0.1", + Port: 8000, + }, + PFCP: &factory.PFCP{ + NodeID: "10.4.0.1", + }, + }, +} + +var testNodeID = &pfcpType.NodeID{ + NodeIdType: pfcpType.NodeIdTypeIpv4Address, + IP: net.ParseIP("10.4.0.1").To4(), +} + +func initSmfContext() { + context.InitSmfContext(&testConfig) +} + +func initRuleList() ([]*context.PDR, []*context.FAR, []*context.BAR, + []*context.QER, []*context.URR, +) { + testPDR := &context.PDR{ + PDRID: uint16(1), + State: context.RULE_INITIAL, + OuterHeaderRemoval: &pfcpType.OuterHeaderRemoval{ + OuterHeaderRemovalDescription: (1), + }, + FAR: &context.FAR{}, + URR: []*context.URR{}, + QER: []*context.QER{}, + } + + testFAR := &context.FAR{ + FARID: uint32(123), + // State Can be RULE_INITIAL or RULE_UPDATE or RULE_REMOVE + State: context.RULE_INITIAL, + ApplyAction: pfcpType.ApplyAction{ + Forw: true, + }, + ForwardingParameters: &context.ForwardingParameters{}, + BAR: &context.BAR{}, + } + + testBAR := &context.BAR{ + BARID: uint8(124), + // State Can be RULE_INITIAL or RULE_UPDATE or RULE_REMOVE + State: context.RULE_INITIAL, + } + + testQER := &context.QER{ + QERID: uint32(123), + // State Can be RULE_INITIAL or RULE_UPDATE or RULE_REMOVE + State: context.RULE_INITIAL, + } + + testURR := &context.URR{ + URRID: uint32(123), + // State Can be RULE_INITIAL or RULE_UPDATE or RULE_REMOVE + State: context.RULE_INITIAL, + } + pdrList := make([]*context.PDR, 0) + farList := make([]*context.FAR, 0) + barList := make([]*context.BAR, 0) + qerList := make([]*context.QER, 0) + urrList := make([]*context.URR, 0) + pdrList = append(pdrList, testPDR) + farList = append(farList, testFAR) + barList = append(barList, testBAR) + qerList = append(qerList, testQER) + urrList = append(urrList, testURR) + return pdrList, farList, barList, qerList, urrList +} + +func TestBuildPfcpAssociationSetupRequest(t *testing.T) { + emptyReq, err := message.BuildPfcpAssociationSetupRequest() + if err != nil { + t.Errorf("TestBuildPfcpAssociationSetupRequest failed: %v", err) + } + + // BuildPfcpAssociationSetupRequest buila a empty template of pfcp.PFCPAssociationSetupRequest + assert.Equal(t, uint8(0), emptyReq.NodeID.NodeIdType) + assert.Equal(t, net.IP(nil), emptyReq.NodeID.IP) + assert.Equal(t, "", emptyReq.NodeID.FQDN) + + assert.Equal(t, + udp.ServerStartTime, + emptyReq.RecoveryTimeStamp.RecoveryTimeStamp) + assert.Nil(t, + emptyReq.UPFunctionFeatures) + assert.Equal(t, + pfcpType.CPFunctionFeatures{SupportedFeatures: 0}, + *emptyReq.CPFunctionFeatures) +} + +func TestBuildPfcpAssociationSetupResponse(t *testing.T) { + cause := pfcpType.Cause{CauseValue: pfcpType.CauseRequestAccepted} + rsp, err := message.BuildPfcpAssociationSetupResponse(cause) + if err != nil { + t.Errorf("TestBuildPfcpAssociationSetupResponse failed: %v", err) + } + + assert.Equal(t, uint8(0), rsp.NodeID.NodeIdType) + assert.Equal(t, cause, *rsp.Cause) + + assert.Nil(t, + rsp.UPFunctionFeatures) + assert.Equal(t, + pfcpType.CPFunctionFeatures{SupportedFeatures: 0}, + *rsp.CPFunctionFeatures) +} + +func TestBuildPfcpAssociationReleaseRequest(t *testing.T) { + emptyReq, err := message.BuildPfcpAssociationReleaseRequest() + if err != nil { + t.Errorf("TestBuildPfcpAssociationReleaseRequest failed: %v", err) + } + + assert.Equal(t, uint8(0), emptyReq.NodeID.NodeIdType) +} + +func TestBuildPfcpAssociationReleaseResponse(t *testing.T) { + cause := pfcpType.Cause{CauseValue: pfcpType.CauseRequestAccepted} + rsp, err := message.BuildPfcpAssociationReleaseResponse(cause) + if err != nil { + t.Errorf("TestBuildPfcpAssociationReleaseResponse failed: %v", err) + } + + assert.Equal(t, uint8(0), rsp.NodeID.NodeIdType) + assert.Equal(t, cause, *rsp.Cause) +} + +func TestBuildPfcpSessionEstablishmentRequest(t *testing.T) { + initSmfContext() + smctx := context.NewSMContext("imsi-208930000000001", 10) + pdrList, farList, barList, qerList, urrList := initRuleList() + smctx.PFCPContext["10.4.0.1"] = &context.PFCPSessionContext{} + + req, err := message.BuildPfcpSessionEstablishmentRequest( + *testNodeID, "10.4.0.1", smctx, pdrList, farList, barList, qerList, urrList) + if err != nil { + t.Errorf("TestBuildPfcpSessionEstablishmentRequest failed: %v", err) + } + // assert.Equal(t, uint8(0), req.NodeID.NodeIdType) + assert.Equal(t, testNodeID, req.NodeID) + assert.Equal(t, &pfcpType.PDNType{PdnType: pfcpType.PDNTypeIpv4}, req.PDNType) + assert.Equal(t, len(req.CreatePDR), 1) + assert.Equal(t, len(req.CreateFAR), 1) + assert.Equal(t, len(req.CreateBAR), 1) + assert.Equal(t, len(req.CreateQER), 1) + assert.Equal(t, len(req.CreateURR), 1) + assert.Equal(t, pdrList[0].State, context.RULE_CREATE) + assert.Equal(t, farList[0].State, context.RULE_CREATE) + assert.Equal(t, barList[0].State, context.RULE_CREATE) + assert.Equal(t, qerList[0].State, context.RULE_CREATE) + assert.Equal(t, urrList[0].State, context.RULE_CREATE) + + req2, err2 := message.BuildPfcpSessionEstablishmentRequest( + *testNodeID, "10.4.0.1", smctx, nil, nil, nil, nil, nil) + if err2 != nil { + t.Errorf("TestBuildPfcpSessionEstablishmentRequest failed: %v", err2) + } + assert.NotEqual(t, req2, req) + assert.Equal(t, len(req2.CreatePDR), 0) + assert.Equal(t, len(req2.CreateFAR), 0) + assert.Equal(t, len(req2.CreateBAR), 0) + assert.Equal(t, len(req2.CreateQER), 0) + assert.Equal(t, len(req2.CreateURR), 0) +} + +// hsien +func TestBuildPfcpSessionEstablishmentResponse(t *testing.T) { + initSmfContext() + rsp, err := message.BuildPfcpSessionEstablishmentResponse() + if err != nil { + t.Errorf("TestBuildPfcpSessionEstablishmentResponse failed: %v", err) + } + assert.Equal(t, rsp.NodeID, testNodeID) + assert.Equal(t, uint8(0), rsp.NodeID.NodeIdType) + assert.Equal(t, pfcpType.CauseRequestAccepted, rsp.Cause.CauseValue) + assert.NotNil(t, rsp.UPFSEID) + assert.NotNil(t, rsp.CreatedPDR) +} + +func TestBuildPfcpSessionModificationRequest(t *testing.T) { + initSmfContext() + smctx := context.NewSMContext("imsi-208930000000001", 10) + pdrList, farList, barList, qerList, urrList := initRuleList() + smctx.PFCPContext["10.4.0.1"] = &context.PFCPSessionContext{} + + req, err := message.BuildPfcpSessionModificationRequest( + *testNodeID, "10.4.0.1", smctx, pdrList, farList, barList, qerList, urrList) + if err != nil { + t.Errorf("TestBuildPfcpSessionModificationRequest failed: %v", err) + } + + assert.Equal(t, context.RULE_CREATE, pdrList[0].State) + assert.Equal(t, context.RULE_CREATE, farList[0].State) + assert.Equal(t, context.RULE_INITIAL, barList[0].State) + assert.Equal(t, context.RULE_CREATE, qerList[0].State) + assert.Equal(t, context.RULE_CREATE, urrList[0].State) + + assert.Equal(t, len(req.CreatePDR), 1) + assert.Equal(t, len(req.CreateFAR), 1) + assert.Equal(t, len(req.CreateBAR), 1) + assert.Equal(t, len(req.CreateQER), 1) + assert.Equal(t, len(req.CreateURR), 1) +} + +func TestBuildPfcpSessionModificationResponse(t *testing.T) { + initSmfContext() + rsp, err := message.BuildPfcpSessionEstablishmentResponse() + if err != nil { + t.Errorf("BuildPfcpSessionModificationResponse failed: %v", err) + } + assert.Equal(t, rsp.NodeID, testNodeID) + assert.Equal(t, pfcpType.CauseRequestAccepted, rsp.Cause.CauseValue) + assert.NotNil(t, rsp.OffendingIE) + assert.NotNil(t, rsp.CreatedPDR) +} + +func TestBuildPfcpSessionDeletionResponse(t *testing.T) { + _, err := message.BuildPfcpSessionDeletionResponse() + if err != nil { + t.Errorf("TestBuildPfcpSessionDeletionResponse failed: %v", err) + } +} + +func TestBuildPfcpSessionReportResponse(t *testing.T) { + cause := pfcpType.Cause{CauseValue: pfcpType.CauseRequestAccepted} + rsp, err := message.BuildPfcpSessionReportResponse(cause) + if err != nil { + t.Errorf("TestBuildPfcpSessionReportResponse failed: %v", err) + } + assert.Equal(t, cause, *rsp.Cause) +} + +func TestBuildPfcpHeartbeatRequest(t *testing.T) { + rsq, err := message.BuildPfcpHeartbeatRequest() + if err != nil { + t.Errorf("TestBuildPfcpHeartbeatRequest failed: %v", err) + } + + assert.Equal(t, udp.ServerStartTime, rsq.RecoveryTimeStamp.RecoveryTimeStamp) +} diff --git a/internal/pfcp/udp/udp_test.go b/internal/pfcp/udp/udp_test.go index 07f084d5..6a929bf4 100644 --- a/internal/pfcp/udp/udp_test.go +++ b/internal/pfcp/udp/udp_test.go @@ -1,7 +1,6 @@ package udp_test import ( - "context" "net" "testing" "time" @@ -11,7 +10,7 @@ import ( "github.com/free5gc/pfcp" "github.com/free5gc/pfcp/pfcpType" "github.com/free5gc/pfcp/pfcpUdp" - smf_context "github.com/free5gc/smf/internal/context" + "github.com/free5gc/smf/internal/context" smf_pfcp "github.com/free5gc/smf/internal/pfcp" "github.com/free5gc/smf/internal/pfcp/udp" ) @@ -21,16 +20,13 @@ const testPfcpClientPort = 12345 func TestRun(t *testing.T) { // Set SMF Node ID - smfContext := smf_context.GetSelf() - - smfContext.CPNodeID = pfcpType.NodeID{ + context.GetSelf().CPNodeID = pfcpType.NodeID{ NodeIdType: pfcpType.NodeIdTypeIpv4Address, IP: net.ParseIP("127.0.0.1").To4(), } - smfContext.ExternalAddr = "127.0.0.1" - smfContext.ListenAddr = "127.0.0.1" + context.GetSelf().ExternalAddr = "127.0.0.1" + context.GetSelf().ListenAddr = "127.0.0.1" - smfContext.PfcpContext, smfContext.PfcpCancelFunc = context.WithCancel(context.Background()) udp.Run(smf_pfcp.Dispatch) testPfcpReq := pfcp.Message{ @@ -64,7 +60,7 @@ func TestRun(t *testing.T) { err := pfcpUdp.SendPfcpMessage(testPfcpReq, srcAddr, dstAddr) require.Nil(t, err) - err = udp.ClosePfcp() + err = udp.Server.Close() require.NoError(t, err) time.Sleep(300 * time.Millisecond)