Skip to content

feat(smf): add Nsmf event exposure create/delete support#225

Draft
haha39 wants to merge 1 commit into
free5gc:mainfrom
Intelligent-Systems-Lab:feat/smf-event-exposure
Draft

feat(smf): add Nsmf event exposure create/delete support#225
haha39 wants to merge 1 commit into
free5gc:mainfrom
Intelligent-Systems-Lab:feat/smf-event-exposure

Conversation

@haha39

@haha39 haha39 commented Jun 21, 2026

Copy link
Copy Markdown

Draft / Stacked Dependency

This is a Draft stacked PR blocked by free5gc/openapi#77.

It depends on free5gc/openapi#77 for the Nsmf and Nupf Event Exposure models,
the Nupf Create/Delete client, and the configurable redirect policy used to
reject Nupf 307/308 responses without replay.

Local development and review used free5gc/openapi commit d357875 through an
external temporary go.work. This PR does not include a committed go.work,
go.work.sum, local replace, fork-only module wiring, duplicate Nupf wire
models, or duplicate Nupf client code.

At the time this draft was opened, free5gc/openapi#77 did not yet provide a
canonical upstream dependency revision. Final upstream-ready validation with
GOWORK=off has not completed. This PR is therefore not yet claiming final
upstream readiness.

Summary

This PR adds the initial SMF-side Nsmf Event Exposure Create/Delete path for
UPF user data usage subscriptions.

The SMF accepts a focused Nsmf Event Exposure profile, resolves the selected
active PDU session and UPF, creates the corresponding Nupf Event Exposure
subscription on that UPF, stores local linkage state, and deletes the downstream
UPF subscription when the Nsmf subscription is deleted.

Supported Profile

The initial profile supports:

  • POST /nsmf-event-exposure/v1/subscriptions
  • DELETE /nsmf-event-exposure/v1/subscriptions/{subId}
  • single SUPI subscriptions
  • UPF_EVENT
  • Nupf USER_DATA_USAGE_MEASURES
  • VOLUME_MEASUREMENT and THROUGHPUT_MEASUREMENT
  • PER_SESSION
  • PERIODIC
  • one session-selected UPF per subscription

Unsupported operations such as GET and replace return 501 ProblemDetails.

Changes

  • Register the standard /nsmf-event-exposure/v1 route when
    nsmf-event-exposure is present in serviceNameList.
  • Add route-local strict JSON decoding for the initial Nsmf Event Exposure
    profile.
  • Require supi, notifId, notifUri, bundledEventNotifyUri, one
    eventSubs entry, one upfEvents entry, supported measurement types,
    PER_SESSION, and positive repPeriod.
  • Preserve omission versus explicit zero for supported selectors, including
    pduSeId: 0.
  • Reject unsupported controls such as present bundlingAllowed or bundleId
    with 400 ProblemDetails.
  • Resolve the target from:
    supi -> active SM context -> UE IP -> selected UPF -> nupfEeApiRoot.
  • Add optional per-UPF nupfEeApiRoot configuration, valid only on UPF nodes.
  • Add an in-memory mutex-protected subscription repository with immutable copy
    semantics and atomic Delete claim.
  • Add an Nupf Event Exposure consumer using the generated OpenAPI client from
    feat: extend Nsmf_EventExposure models and add Nupf_EventExposure client openapi#77.
  • Configure Nupf Event Exposure with openapi.RejectRedirects so Create/Delete
    307/308 responses are not replayed.
  • Validate Nupf Create success by checking the typed subscription ID and
    Location header against the actual Create request URI and configured UPF
    service base URL.
  • Map downstream Create failures to a sanitized 502 Bad Gateway response.
  • Delete uses the stored validated target snapshot and UPF subscription ID; it
    does not rerun SUPI/session resolution or treat stored Location as a URL to
    call.

Nsmf to Nupf Mapping

Nsmf value Nupf value
notifId notifyCorrelationId
bundledEventNotifyUri eventNotifyUri
selected session UE IP ueIpAddress
accepted measurement types eventList[].measurementTypes
PER_SESSION eventList[].granularityOfMeasurement
positive repPeriod eventReportingMode.repPeriod
SMF NF instance ID nfId

The Nupf Create request uses the required outer subscription wrapper.

Lifecycle and Error Behavior

Create commits no local state before Nupf Create succeeds.

If Nupf Create succeeds but local Store fails, the SMF returns 500 and logs a
bounded orphan warning. It does not attempt an automatic compensating Delete.

Delete atomically claims and removes local state first, then attempts downstream
Nupf Delete using the stored target snapshot and UPF subscription ID. Downstream
Delete failure, redirect, or token acquisition failure is logged as a bounded
warning and the Nsmf Delete still returns 204.

Unknown local Delete returns 404 before any token acquisition or Nupf request.

Intentional Limitations

The initial PR does not implement:

  • subscription modification
  • UPF notification relay through SMF
  • group UE or any-UE subscriptions
  • additional event types or granularities
  • persistent subscription storage
  • restart reconciliation
  • shutdown cleanup of remaining UPF subscriptions
  • UPF relocation or UE release reconciliation
  • NRF endpoint discovery for Nupf Event Exposure
  • custom Nupf TLS, OAuth, retry, redirect, or response-reader framework

The implementation reuses existing free5GC OAuth, generated-client, shared HTTP
client, and metrics conventions.

Validation

Local validation used Go 1.26.2, free5gc/openapi commit d357875, and an
external temporary go.work.

Passed:

git diff --check
gofmt -l <changed Go files>
GOWORK=<external temporary go.work> GOPROXY=off go build -v ./...
GOWORK=<external temporary go.work> GOPROXY=off go vet ./...
GOWORK=<external temporary go.work> GOPROXY=off go test -count=1 -run 'EventExposure|NupfEeApiRoot|ValidateNupf|HTTPCreateIndividualSubcription' ./internal/context ./internal/sbi ./internal/sbi/consumer ./internal/sbi/processor ./pkg/factory
GOWORK=<external temporary go.work> GOPROXY=off go test -race -count=1 -run 'EventExposure|NupfEeApiRoot|ValidateNupf|HTTPCreateIndividualSubcription' ./internal/context ./internal/sbi ./internal/sbi/consumer ./internal/sbi/processor ./pkg/factory
docker run --rm ... golangci/golangci-lint:v2.11.4 golangci-lint run ./...

The Docker lint run reported:

0 issues.

Repository-wide go test -count=1 ./... was also run. Event Exposure related
packages passed, but the full run is not green because of existing unrelated
internal/pfcp/handler log extraction panics at:

handler_test.go:51
handler_test.go:96
handler_test.go:138
handler_test.go:160

Final upstream-ready validation with GOWORK=off remains blocked until
free5gc/openapi#77 has a canonical dependency revision.

Related PRs

Depends on free5gc/openapi#77.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant