Skip to content

Commit

Permalink
Fix Forwarding Policy and NAT error reporting
Browse files Browse the repository at this point in the history
Fix error handling during session establishment and modification.
Update tests to verify error messages in PFCP responses
  • Loading branch information
ivan4th authored and korroot committed Oct 25, 2022
1 parent e7a3289 commit 74e0a86
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 50 deletions.
77 changes: 61 additions & 16 deletions test/e2e/pfcp/pfcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,10 @@ var (
)

type PFCPServerError struct {
Cause uint8
SEID SEID
Cause uint8
SEID SEID
FailedRuleID uint32
Message string
}

func (e *PFCPServerError) Error() string {
Expand All @@ -163,6 +165,10 @@ func (e *PFCPServerError) Error() string {
return fmt.Sprintf("<bad cause value %d>", e.Cause)
}

if e.Message != "" {
return fmt.Sprintf("server error, cause: %s: %s", s, e.Message)
}

return fmt.Sprintf("server error, cause: %s", s)
}

Expand Down Expand Up @@ -1254,35 +1260,61 @@ func (s *pfcpSession) getMeasurement(m message.Message) (*PFCPMeasurement, error
return r, err
}

func getCauseIE(m message.Message) *ie.IE {
func getCauseIE(m message.Message) (causeIE, failedRuleIDIE *ie.IE, otherIEs []*ie.IE) {
switch r := m.(type) {
case *message.AssociationSetupResponse:
return r.Cause
return r.Cause, nil, r.IEs
case *message.AssociationReleaseResponse:
return r.Cause
return r.Cause, nil, r.IEs
case *message.AssociationUpdateResponse:
return r.Cause
return r.Cause, nil, r.IEs
case *message.NodeReportResponse:
return r.Cause
return r.Cause, nil, r.IEs
case *message.PFDManagementResponse:
return r.Cause
return r.Cause, nil, r.IEs
case *message.SessionEstablishmentResponse:
return r.Cause
return r.Cause, r.FailedRuleID, r.IEs
case *message.SessionDeletionResponse:
return r.Cause
return r.Cause, nil, r.IEs
case *message.SessionModificationResponse:
return r.Cause
return r.Cause, r.FailedRuleID, r.IEs
case *message.SessionReportResponse:
return r.Cause
return r.Cause, nil, r.IEs
case *message.SessionSetDeletionResponse:
return r.Cause
return r.Cause, nil, r.IEs
default:
return nil
return nil, nil, nil
}
}

const (
TPErrorReportEnterpriseID = 0x48f9
TPErrorReportIEID = 0x8006
TPErrorMessageIEID = 0x8007
)

func parseTPErrorReport(ies []*ie.IE) (string, error) {
for _, cur := range ies {
if cur.EnterpriseID != TPErrorReportEnterpriseID || cur.Type != TPErrorReportIEID {
continue
}

for len(cur.Payload) != 0 {
var child ie.IE
if err := child.UnmarshalBinary(cur.Payload); err != nil {
return "", errors.Wrap(err, "error parsing child IE")
}
if child.EnterpriseID == TPErrorReportEnterpriseID || cur.Type == TPErrorMessageIEID {
return string(child.Payload), nil
}
}
}

return "", nil
}

func verifyCause(m message.Message, seid SEID) error {
causeIE := getCauseIE(m)
causeIE, failedRuleIDIE, otherIEs := getCauseIE(m)
if causeIE == nil {
return errors.New("no cause")
}
Expand All @@ -1299,7 +1331,20 @@ func verifyCause(m message.Message, seid SEID) error {
if m.SEID() != 0 {
seid = SEID(m.SEID())
}
return &PFCPServerError{Cause: cause, SEID: seid}

var failedRuleID uint32
if failedRuleIDIE != nil {
failedRuleID, err = failedRuleIDIE.FailedRuleID()
if err != nil {
return errors.Wrap(err, "bad Failed Rule ID IE")
}
}

msg, err := parseTPErrorReport(otherIEs)
if err != nil {
return errors.Wrap(err, "failed to find/parse error report")
}
return &PFCPServerError{Cause: cause, SEID: seid, FailedRuleID: failedRuleID, Message: msg}
}

func requestToEventType(m message.Message) (pfcpEventType, bool) {
Expand Down
100 changes: 74 additions & 26 deletions test/e2e/upg_e2e.go
Original file line number Diff line number Diff line change
Expand Up @@ -837,7 +837,7 @@ var _ = ginkgo.Describe("UPG Binary API", func() {
gomega.Expect(err).To(gomega.BeNil())
hbGetRequest := &upf.UpfPfcpHeartbeatsGet{}
hbGetReply := &upf.UpfPfcpHeartbeatsGetReply{}
err = f.VPP.ApiChannel.SendRequest(hbGetRequest).ReceiveReply(hbGetReply)
err = f.VPP.ApiChannel.SendRequest(hbGetRequest).ReceiveReply(hbGetReply)
gomega.Expect(err).To(gomega.BeNil())
gomega.Expect(hbGetReply.Timeout).To(gomega.Equal(uint32(5)))
gomega.Expect(hbGetReply.Retries).To(gomega.Equal(uint32(15)))
Expand Down Expand Up @@ -1402,15 +1402,10 @@ var _ = ginkgo.Describe("Multiple PFCP Sessions", func() {
if err == nil {
unexpectedSuccess = true
} else {
var serverErr *pfcp.PFCPServerError
gomega.Expect(errors.As(err, &serverErr)).To(gomega.BeTrue())
framework.ExpectEqual(newSEID, serverErr.SEID)
framework.ExpectEqual(serverErr.Cause, ie.CauseRuleCreationModificationFailure)
framework.Logf("Server error (expected): %v", err)
// TODO: decode and verify TP error report
verifyPFCPError(err, ie.CauseRuleCreationModificationFailure, newSEID, 2,
"PDR ID 2, duplicate UE IP")
break
}

}
gomega.Expect(unexpectedSuccess).To(gomega.BeFalse(), "EstablishSession succeeded unexpectedly")
var m message.Message
Expand All @@ -1435,13 +1430,8 @@ var _ = ginkgo.Describe("Multiple PFCP Sessions", func() {
var newSEID pfcp.SEID
newSEID = f.PFCP.NewSEID()
_, err = f.PFCP.EstablishSession(f.Context, newSEID, sessionCfg.SessionIEs()...)
framework.ExpectError(err)

var serverErr *pfcp.PFCPServerError
gomega.Expect(errors.As(err, &serverErr)).To(gomega.BeTrue())
framework.ExpectEqual(newSEID, serverErr.SEID)
framework.ExpectEqual(serverErr.Cause, ie.CauseRuleCreationModificationFailure)
framework.Logf("Server error (expected): %v", err)
verifyPFCPError(err, ie.CauseRuleCreationModificationFailure, newSEID, 2,
"PDR ID 2, duplicate UE IP")

var m message.Message
gomega.Eventually(reportCh, 10*time.Second, 50*time.Millisecond).Should(gomega.Receive(&m))
Expand Down Expand Up @@ -1480,14 +1470,7 @@ var _ = ginkgo.Describe("Multiple PFCP Sessions", func() {
f.PFCP.ForgetSession(seid)
sessionCfg.UEIP = f.AddUEIP()
_, err = f.PFCP.EstablishSession(f.Context, seid, sessionCfg.SessionIEs()...)
framework.ExpectError(err)

var serverErr *pfcp.PFCPServerError
gomega.Expect(errors.As(err, &serverErr)).To(gomega.BeTrue())
gomega.Expect(serverErr.SEID).To(gomega.Equal(seid))
framework.ExpectEqual(serverErr.Cause, ie.CauseRequestRejected)
framework.Logf("Server error (expected): %v", err)
// TODO: decode and verify TP error report
verifyPFCPError(err, ie.CauseRequestRejected, seid, 0, "Duplicate SEID")
})
})

Expand Down Expand Up @@ -1518,9 +1501,8 @@ var _ = ginkgo.Describe("Multiple PFCP Sessions", func() {
SGWIP: f.VPPCfg.GetNamespaceAddress("grx").IP,
}
_, err = f.PFCP.EstablishSession(f.Context, 0, sessionCfg.SessionIEs()...)
var serverErr *pfcp.PFCPServerError
gomega.Expect(errors.As(err, &serverErr)).To(gomega.BeTrue())
framework.ExpectEqual(serverErr.Cause, ie.CauseRuleCreationModificationFailure)
verifyPFCPError(err, ie.CauseRuleCreationModificationFailure, 0, 1,
"PDR ID 1, can't handle F-TEID")

sessionCfg.TEIDPGWs5u += 10
seid1, err := f.PFCP.EstablishSession(f.Context, 0, sessionCfg.SessionIEs()...)
Expand All @@ -1530,6 +1512,57 @@ var _ = ginkgo.Describe("Multiple PFCP Sessions", func() {
})
})

var _ = ginkgo.Describe("Error handling", func() {
f := framework.NewDefaultFramework(framework.UPGModeTDF, framework.UPGIPModeV4)

ginkgo.It("should be done correctly with unknown Forwarding Policy when creating a session", func() {
sessionCfg := &framework.SessionConfig{
IdBase: 1,
UEIP: f.UEIP(),
Mode: f.Mode,
ForwardingPolicyID: "nosuchpolicy",
}
seid := f.PFCP.NewSEID()
_, err := f.PFCP.EstablishSession(f.Context, seid, sessionCfg.SessionIEs()...)
verifyPFCPError(err, ie.CauseInvalidForwardingPolicy, seid, 1,
"FAR ID 1, forwarding policy 'nosuchpolicy' not configured")
})

ginkgo.It("should be done correctly with unknown Forwarding Policy when modifying a session", func() {
sessionCfg := &framework.SessionConfig{
IdBase: 1,
UEIP: f.UEIP(),
Mode: f.Mode,
}
seid := f.PFCP.NewSEID()
_, err := f.PFCP.EstablishSession(f.Context, seid, sessionCfg.SessionIEs()...)
gomega.Expect(err).NotTo(gomega.HaveOccurred())

modIE := ie.NewUpdateFAR(
ie.NewFARID(1),
ie.NewUpdateForwardingParameters(
ie.NewDestinationInterface(ie.DstInterfaceSGiLANN6LAN),
ie.NewNetworkInstance(framework.EncodeAPN("sgi")),
ie.NewForwardingPolicy("nosuchpolicy")))
_, err = f.PFCP.ModifySession(f.Context, seid, modIE)
verifyPFCPError(err, ie.CauseInvalidForwardingPolicy, seid, 1,
"FAR ID 1, forwarding policy 'nosuchpolicy' not configured")
})

ginkgo.It("should be done correctly for NAT", func() {
sessionCfg := &framework.SessionConfig{
IdBase: 1,
UEIP: f.UEIP(),
Mode: f.Mode,
NatPoolName: "nosuchpool",
}
seid := f.PFCP.NewSEID()
_, err := f.PFCP.EstablishSession(f.Context, seid, sessionCfg.SessionIEs()...)
verifyPFCPError(err, ie.CauseRuleCreationModificationFailure, seid, 1,
"FAR ID 1, Error creating NAT binding for pool 'nosuchpool'")
})
})

func describeMTU(mode framework.UPGMode, ipMode framework.UPGIPMode) {
ginkgo.Describe("[MTU corner cases]", func() {
var seid pfcp.SEID
Expand Down Expand Up @@ -2097,3 +2130,18 @@ func setupNAT(f *framework.Framework) {
f.VPP.Ctl("upf nat pool 144.0.0.20 - 144.0.0.120 block_size 512 nwi sgi name testing")
f.VPP.Ctl("nat44 controlled enable")
}

func verifyPFCPError(err error, cause uint8, seid pfcp.SEID, failedRuleID uint32, message string) {
framework.Logf("Server error (expected to occur): %v", err)
framework.ExpectError(err, "expected PFCP error")
var serverErr *pfcp.PFCPServerError
gomega.Expect(errors.As(err, &serverErr)).To(gomega.BeTrue())
if seid != 0 {
framework.ExpectEqual(seid, serverErr.SEID, "SEID")
}
framework.ExpectEqual(serverErr.Cause, cause, "Cause")
if failedRuleID != 0 {
framework.ExpectEqual(serverErr.FailedRuleID, failedRuleID, "FailedRuleID")
}
framework.ExpectEqual(serverErr.Message, message, "Message")
}
22 changes: 14 additions & 8 deletions upf/upf_pfcp_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -1488,7 +1488,12 @@ handle_create_far (upf_session_t * sx, pfcp_create_far_t * create_far,
rc = handle_nat_binding_creation (sx, pool_name, response);
vec_free (pool_name);
if (rc)
goto out_error;
{
far_error (response, far,
"Error creating NAT binding for pool '%v'",
far->forwarding_parameters.nat_port_block);
goto out_error;
}
}

if (ISSET_BIT (far->forwarding_parameters.grp.fields,
Expand Down Expand Up @@ -1548,9 +1553,10 @@ handle_create_far (upf_session_t * sx, pfcp_create_far_t * create_far,
}
else
{
upf_debug
("###### Forwarding policy id %v is not preconfigured at UPF ######",
far->forwarding_parameters.forwarding_policy.identifier);
far_error (response, far,
"forwarding policy '%v' not configured",
far->forwarding_parameters.
forwarding_policy.identifier);
response->cause = PFCP_CAUSE_INVALID_FORWARDING_POLICY;
goto out_cause_set;
}
Expand Down Expand Up @@ -1708,10 +1714,10 @@ handle_update_far (upf_session_t * sx, pfcp_update_far_t * update_far,
}
else
{
upf_debug
("###### Forwarding policy id %v is not preconfigured at UPF ######",
far->update_forwarding_parameters.
forwarding_policy.identifier);
far_error (response, far,
"forwarding policy '%v' not configured",
far->update_forwarding_parameters.
forwarding_policy.identifier);
response->cause = PFCP_CAUSE_INVALID_FORWARDING_POLICY;
goto out_cause_set;
}
Expand Down

0 comments on commit 74e0a86

Please sign in to comment.