Repository navigation
Name the configured static UE address in the rule the UPF installs - #711
Merged
gab-arrobo merged 2 commits intoOct 26, 2026
Merged
Conversation
A subscriber given an address in staticIpInfo does not get it when the user plane
allocates UE addresses, which is the reference deployment's default
(enable_ue_ip_alloc: true on bess-upf). Nothing reports it. The SMF selects the
configured address and logs it, the PDR it then builds carries CHV4 because the
UPF advertises UEIP, the UPF allocates from its own pool and returns its choice,
and the SMF adopts that address and releases the configured one. Observed on a
cluster as imsi-001010100007510, configured for 192.168.100.100, holding
192.168.100.15; the only trace of the configuration was the SMF's own
"Release IP[192.168.100.100]". Other subscribers were unaffected, so the feature
looks like it works until the one address an operator cares about is checked.
The three places that build the UE IP Address IE now share one function, and for
a session holding a configured address it names that address instead of asking
the UPF to choose. TS 29.244 clause 8.2.62 provides for either -- V4 and CHV4 are
mutually exclusive -- and a UPF that allocates still honours a named address: it
allocates only for a PDI whose CHV4 is set, and returns an address in the Created
PDR only for one it allocated for, so the SMF has nothing to adopt and nothing to
release. Every other session is untouched and still left to the UPF's allocator.
Which address was configured is recorded on the session, as UeIpAddr.Static, when
the address is allocated. Asking the allocator at the time the rule is built
would not answer for a session that outlived the SMF: an IPAllocator has only
unexported fields, so an SMContext read back from the database carries one that
marshalled as {} and knows no configuration at all, and a rule rebuilt for such a
session through ActivateTunnelAndPDR -- adding a PDU session anchor or a ULCL
rule, which the restoration path's pinUeAddress does not cover -- would carry
CHV4 and lose the address again. The field is new and untagged, so a session
written by an older SMF decodes with Static false and behaves as it does today.
HasStaticUeIpAddr answers for the address the session holds now, not for what was
allocated: UpfProvided overrides Static, and the 0.0.0.0 ReleaseUeIpAddr writes
back is not an address. Without the first, a UPF that allocated over a static
address -- the misconfiguration below -- would have its own choice reported and
logged as the configured one. Without the second, a rule could name 0.0.0.0,
which the user plane reads as every address; producer/callback.go's
sessionUEAddress guards the same sentinel.
StaticIpForImsi replaces the copy of the static lookup that Allocate and Release
each carried, so the allocation site can ask what Allocate just did.
Naming a static address leaves the SMF and the UPF drawing on one subnet with
nothing to reconcile them, and the doc comment and the log line both say so: the
UPF's pool knows nothing of staticIpInfo, so a configured address inside that
pool is handed to a second session once the pool reaches it. Where the UPF
allocates, the configured addresses have to be kept out of its pool -- which is
where their route and NAT rule have to be arranged anyway. The SMF cannot check
it, because the UPF's pool is not signalled over N4, and PFCP offers no way to
reserve an address there.
Tests: five new test functions in context/datapath_test.go, beside the
ActivateUpLinkPdr and ActivateDlLinkPdr tests already there. Four fail without
the change: the uplink and the downlink rule must name the configured address
when the UPF advertises UEIP, a rule rebuilt for a session restored without its
allocator must name it too, and neither an address the UPF allocated over it nor
the released-address sentinel may be named. The fifth pins what must not change:
a session with no configured address is still left to the UPF's allocator, with
CHV4 set and V4 clear.
Signed-off-by: Benjamin Grewell <bgrewell@gmail.com>
gab-arrobo
force-pushed
the
fix/static-ue-ip-with-upf-alloc
branch
from
October 26, 2026 19:08
baf6d7c to
5de959c
Compare
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Releasing a static-address session after database restoration can dereference an uninitialized allocator and panic.
1 open issue
What changed in this PR
This PR makes the SMF name configured static UE addresses in UPF rules instead of requesting a replacement address, while preserving dynamic allocation behavior.
Changes:
- Record static-address ownership on the session so it survives persistence.
- Consolidate UE address rule construction and log static-address overrides.
- Add regression tests for static, dynamic, restored, released, and UPF-overridden addresses.
| File | Description |
|---|---|
| producer/pdu_session.go | Record whether the allocated session address is static. |
| context/sm_context.go | Store static ownership and identify currently held static addresses. |
| context/ip_allocator.go | Share static-address lookup across allocation and release. |
| context/datapath.go | Build consistent address rules and log static overrides. |
| context/datapath_test.go | Cover address selection and session-state edge cases. |
Review effort: Balanced
The two cases this change added use "default" as a PDR map key in a composite literal, which brought the count goconst sees in the package to four and failed the lint job: context/datapath_test.go:30:5: string `default` has 4 occurrences, make it a constant (goconst). The key gets a constant beside testDnn and every use in the file goes through it. Signed-off-by: Benjamin Grewell <bgrewell@gmail.com>
gab-arrobo
approved these changes
Oct 26, 2026
This was referenced Oct 26, 2026
Open
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Why this is needed
A subscriber given a fixed address in
staticIpInfodoes not get it, and nothing says so.The configuration is accepted, the SMF selects the address, and the session establishes. The UE then holds an address out of the user plane's pool instead. On a lab core,
imsi-001010100007510configured for192.168.100.100came up on192.168.100.15; every other subscriber was unaffected, so the feature looks like it works until the one address an operator cares about is checked. The only trace of the configuration anywhere was the SMF's ownRelease IP[192.168.100.100]— a log line that reads like ordinary teardown.This is the reference deployment's default, not a corner case.
aether-onrampruns bess-upf withenable_ue_ip_alloc: true, so the UPF advertises the UEIP feature and the condition holds for every session on every such cluster. Static addressing is used for the things that have to be reachable at a known address — a fixed-wireless CPE, a camera, a sensor gateway reached by a fleet controller — so the failure lands on the subscribers whose address is load-bearing.What is broken
context/datapath.gobuilds the UE IP Address IE for a PDI in three places, each of them identically:A UPF that advertises UEIP is asked to choose the address, whatever address the session already holds. The static configuration is consulted earlier, in the allocator (
context/ip_allocator.go), and is lost here.What follows is ordinary SMF behaviour, which is why it is silent:
Allocatereturns the configured address for the IMSI and the session holds it.CHV4, so the UPF allocates from its own pool (pfcpiface/parse_pdr.go,needAllocIP) and returns its choice in the Created PDR.HandlePfcpSessionEstablishmentResponseadopts that address, callsReleaseUeIpAddron the configured one, and setsUpfProvided.No step is in error on its own, and no log line reports that a configured address was dropped.
How it is fixed
The three copies become one function, and for a session holding a configured address it names that address rather than asking the UPF to choose:
Naming the address is what the interface is for
TS 29.244 clause 8.2.62 makes
V4andCHV4mutually exclusive: the control plane either assigns the address or asks the user plane to. Both are normal. A UPF that can allocate is not obliged to, and bess-upf does not —needAllocIPallocates only for a PDI whoseCHV4is set, and a Created PDR carries a UE IP Address only for a PDR it allocated for. So with the address named there is nothing for the SMF to adopt and nothing to release, and the rest of the establishment path needs no change at all.Every session without a configured address is untouched:
CHV4as today, so the UE keeps getting an address out of the pool the user plane routes and NATs for.The fact is recorded on the session, not re-derived
UeIpAddrgainsStatic, set when the address is allocated.Asking the allocator at the time the rule is built does not answer for a session that outlived the SMF.
IPAllocatorhas only unexported fields, so anSMContextread back from the database carries one that marshalled as{}and knows no configuration at all. A rule rebuilt for such a session — adding a PDU session anchor or a ULCL rule, which reachesActivateTunnelAndPDRwithout going through the restoration path'spinUeAddress— would carryCHV4again, the UPF would allocate, and the configured address would be dropped a second time.The field is new and untagged, so an
SMContextwritten by an older SMF decodes withStaticfalse and behaves exactly as it does today.HasStaticUeIpAddranswers for the address the session holds nowNot for what was allocated:
UpfProvidedoverridesStatic. A UPF that allocated over a static address — the misconfiguration described below — leavesStaticset while the address the session holds is the UPF's. Without this, the UPF's own choice would be reported and logged as the configured one, concealing the precondition the log line exists to surface.0.0.0.0is rejected.ReleaseUeIpAddrwrites it back to mean "no address", and the user plane reads the unspecified address as any address, so naming it would emit a rule matching every subscriber.producer/callback.go'ssessionUEAddressguards the same sentinel for the same reason.Two allocators on one subnet, and nothing to reconcile them
Naming the address leaves the SMF placing one session on an address while the UPF allocates for every other session on the same UPF, out of the same subnet. Nothing reconciles the two: the UPF's
ue_ip_poolknows nothing ofstaticIpInfo(pfcpiface/ip_pool.go'sNewIPPoolenumerates the whole subnet minus network and broadcast, andneedAllocIPhonours a named address without marking it used), and the SMF withholds the configured addresses from its own pool only. A configured address inside the UPF's pool is handed to a second session once the pool reaches it, and both sessions then carry rules for the same UE address.So where the UPF allocates, the configured addresses have to be kept out of its pool — which is where their route and NAT rule have to be arranged anyway. The SMF cannot check it: the UPF's pool is not signalled over N4, and PFCP offers no way to reserve an address there. Both the doc comment and the log line say so.
The override is logged, with the precondition
One line at INFO per node, from
ActivateTunnelAndPDR(the only caller ofActivateUpLinkPdrandActivateDlLinkPdr), naming the UPF, the subscriber, the address, and that the UPF's own pool must not contain it. The defect it replaces was invisible, and a statically addressed subscriber is by nature a small number of sessions. The IE builder itself stays pure.StaticIpForImsiThe static lookup that
AllocateandReleaseeach carried a copy of becomes one method, so the allocation site can ask whatAllocatejust did.What this does not fix
Deliberately out of scope — each is a defect this change does not cause and does not make worse, and each is better raised on its own:
staticIpInfois not validated or merged.ReserveStaticIpsreplaces its map rather than merging, andreserveStaticIpsIfNeededcalls it once per entry matching the DNN, so with more than one entry all but the last are withheld from the dynamic pool and then never handed to the subscriber configured for them. Nothing rejects an entry that cannot be a UE address either. Both are independent of this change: such a subscriber gets a dynamic address and aCHV4rule, exactly as before.ueSubnet, or an invalid one, is stored with no allocator (context/context.goonly warns), so the first PDU session on it panics the SMF inAllocate;ReleaseUeIpAddrreaches the same deref for a session restored from the database. Both predate this change, which adds no new call site on those paths.ULCLSupportset the ULCL path can still move a live UE's address. Telling an established session from one at establishment needs to know whether its rules are installed, andSMContextStatecannot answer it —ChangeStatewrites it withoutSMLock, which is whycreateInFlightexists for the restoration sweep.Testing
5 new test functions in
context/datapath_test.go, beside theActivateUpLinkPdr/ActivateDlLinkPdrtests already there. Four fail without the change:CHV4 set: the UPF is asked to choose an address although 192.168.100.100 is configured for imsi-001010100007510);192.168.100.3 is reported as imsi-001010100007510's configured address 192.168.100.100);rule names 0.0.0.0, which matches every subscriber).The fifth pins what must not change: a session with no configured address is still left to the UPF's allocator, with
CHV4set andV4clear.Existing tests are unmodified.
go build ./...,go vet,gofmt,gofumpt,gci,staticcheck ./...andgo test -race ./...are clean.