Skip to content

Conversation

@pranav-kumar-hex
Copy link
Member

@pranav-kumar-hex pranav-kumar-hex commented Sep 28, 2025

Summary by CodeRabbit

  • New Features
    • Added 5G GMM state machine with atomic state handling and a consolidated NAS context.
    • Introduced NAS registration request handling that populates UE identifiers and advances state.
    • Added client-side service discovery with caching and automatic OAuth token injection.
  • Refactor
    • Streamlined NAS interfaces and module structure; updated NF client API to accept target services/types.
  • Performance
    • Replaced read/write locks with a lightweight sequential lock for context scheduling.
  • Chores
    • Added workspace dependencies to support atomic enums and socket operations.

@coderabbitai
Copy link

coderabbitai bot commented Sep 28, 2025

Walkthrough

Adds atomic state management and NAS context structures; introduces a new atomic-handle crate; refactors UE/NAS handling to a state-machine trait with registration handling; switches NGAP context scheduling to a SeqLock-based queue; adds service discovery and OAuth layers to the client; updates NRF token handling; adjusts module paths and workspace dependencies.

Changes

Cohort / File(s) Summary
Workspace dependencies
Cargo.toml, lightning-nf/omnipath/app/Cargo.toml
Added atomic_enum and socket2 to workspace deps; registered new workspace member and path dep atomic-handle.
New crate: atomic-handle
utils/atomic-handle/Cargo.toml, utils/atomic-handle/src/lib.rs
Introduces AtomicOperation trait and AtomicHandle<S, Field> for pointer-stable access to an AtomicUsize inside Arc<S> with Send/Sync guarantees and tests.
Context: GMM state and NAS context
.../context/mod.rs, .../context/state/mod.rs, .../context/state/gmm_state.rs, .../context/state/atomic_gmm_state.rs, .../context/nas_context.rs
Adds GmmState enum and AtomicGmmState wrapper; introduces NasContext with identifiers; re-exports new types from context modules.
UE and gNB context updates
.../context/ue_context.rs, .../context/gnb_context.rs
Replaces multiple NAS/GMM fields with state: AtomicGmmState and nas_context: NasContext; adds GmmStateField marker and AtomicOperation impl; GnbContext now tracks idle UEs and atomic UE state handles.
NAS interface and flow (new)
.../nas/interface.rs, .../nas/registration_req/mod.rs, .../nas/registration_req/error.rs, .../nas/mod.rs, .../nas/ue_actions.rs
Defines NasMessageHandle trait and InvalidStateTransition; implements Registration Request handler on UeContext, updating NasContext and transitioning states; reshapes NAS module exports; removes old handle_nas impl file.
Legacy NAS moved under nas_old
.../app/src/lib.rs, .../nas_old/mod.rs, .../nas_old/builders/*, .../nas_old/handlers/*, .../nas_old/ue_actions.rs
Adds pub mod nas_old with legacy NasHandler/NasBuilder; updates imports in old handlers/builders to nas_old; adds commented scaffold in ue_actions.
NGAP engine and manager adjustments
.../ngap/engine/mod.rs, .../ngap/engine/controller.rs
Renames interfacesinterface and updates imports/exports.
NGAP context scheduling to SeqLock
.../ngap/manager/context_manager.rs, .../ngap/manager/context_queue.rs
Switches scheduling closures to take Arc<SeqLock<T>> instead of OwnedRwLockWriteGuard<T>; internal queue holds Arc<SeqLock<T>>; updates method signatures accordingly.
NGAP Initial UE Message flow
.../ngap/core/initial_ue_message.rs
Adds create_new_context; constructs UeContext with AtomicGmmState and NasContext; uses SeqLock and unsafe access in scheduled closure to invoke NAS handling.
Utils: SeqLock
.../utils/mod.rs, .../utils/seq_lock.rs
Adds SeqLock<T> (unsafe cell wrapper) and re-exports; ensures utils::models is public.
Client: feature flag
utils/client/src/lib.rs
Enables nightly generic_const_exprs.
Client: NFClient refactor with layers
utils/client/src/nf_clients/mod.rs
Adds generics over TARGET_TYPE, introduces OAuthTokenLayer and ServiceDiscoveryLayer; reworks construction to layered pipeline and PhantomData controller.
Client: OAuth token middleware
utils/client/src/nf_clients/oauth_service.rs
Adds OAuthTokenService and OAuthTokenLayer to inject tokens via NrfClient before forwarding requests.
Client: Service discovery
utils/client/src/nf_clients/service_discovery/mod.rs, .../service_discovery/future.rs
Adds cached NF profile, discovery layer/service, URL rewrite logic, error types, and ResponseFuture.
NRF client token handling
utils/client/src/nrf_client.rs
Refactors token application to operate on HeaderMap; adds create_token_from_access_token; checks expiry before using cached tokens.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor R as RAN/UE
  participant NGAP as NGAP Controller
  participant CtxMgr as ContextManager<UE>
  participant UE as UeContext (SeqLock)
  participant NAS as NasMessageHandle (Registration)

  rect rgba(230,240,255,0.5)
    R->>NGAP: InitialUEMessage (NAS PDU, FiveGSTmsi?)
    NGAP->>CtxMgr: create_new_context(...)
    CtxMgr-->>NGAP: Arc<SeqLock<UeContext>>
    NGAP->>UE: schedule closure with NAS PDU
    note right of UE: Unsafe SeqLock access within single-threaded queue
    UE->>NAS: state_transition(from_state, request)
    NAS-->>UE: (next_state, Optional<NgapPdu>)
  end
Loading
sequenceDiagram
  autonumber
  participant Client as NFClient<T, APP, TARGET>
  participant SD as ServiceDiscoveryLayer
  participant OA as OAuthTokenLayer
  participant HTTP as HttpClient
  participant NRF as NrfClient

  rect rgba(240,255,240,0.5)
    Client->>SD: request
    alt cache invalid
      SD->>NRF: discover TARGET services
      NRF-->>SD: NfProfile / error
      SD->>SD: update cache / error
    end
    SD->>OA: rewritten request (base URL)
    OA->>NRF: get/set OAuth token (headers)
    OA->>HTTP: forward request
    HTTP-->>Client: response
  end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Poem

In burrows of bytes where states now gleam,
I thump my paws at a lock-free dream.
SeqLocks squeak, atoms align,
Tokens hop on each request line.
Discovery scents the service track—
Carrots cached, we never backtrack. 🥕✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The provided title accurately highlights the primary intent of this pull request, namely the introduction of both a client module architecture and a new NAS handling architecture. It is concise and directly reflects the most significant change in the changeset without unnecessary detail. Team members reviewing PR history will understand at a glance that the update concerns architectural additions for the client and NAS functionality. Therefore, it meets the criteria for a clear, focused title.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch wip

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 23

🧹 Nitpick comments (25)
Cargo.toml (1)

76-76: Fix the typo in this comment.

"Depenedency" should be spelled "Dependency" to keep the note clear.

-socket2 = "0.4" # Depenedency of tokio-sctp
+socket2 = "0.4" # Dependency of tokio-sctp
lightning-nf/omnipath/app/src/nas_old/ue_actions.rs (1)

1-1: Drop the unused UeContext import

Line 1 pulls in UeContext, but every reference remains commented out, so the compiler emits an unused-import warning. Unless we plan to resurrect that block immediately, please remove the import (or add an allow) to keep the module warning-free.

lightning-nf/omnipath/app/src/context/ue_context.rs (1)

17-19: Restrict state and nas_context visibility to avoid unsound external mutation

Both fields are public, enabling mutation outside the scheduling/serialization guarantees of the context manager. Make them private or pub(crate) and expose intentional accessors/methods.

 pub struct UeContext {
   pub ran_ue_ngap_id: RanUeNgapId,
   pub amf_ue_ngap_id: AmfUeNgapId,
   pub gnb_context: Arc<GnbContext>,
-  pub state: AtomicGmmState,
-  pub nas_context: NasContext,
+  pub(crate) state: AtomicGmmState,
+  pub(crate) nas_context: NasContext,
 }
lightning-nf/omnipath/app/src/nas_old/mod.rs (2)

1-2: Remove unused Arc import

Arc is not used in this module.

-use std::{future::Future, sync::Arc};
+use std::future::Future;

4-4: Explicitly import the intended NasContext
Change the import to reference the canonical type and avoid shadowing:

-use nas_context::NasContext;
+use crate::context::NasContext;

Confirm all usages in nas_old/mod.rs now refer to crate::context::NasContext, not the local nas_old::nas_context::NasContext.

lightning-nf/omnipath/app/src/ngap/core/initial_ue_message.rs (2)

5-5: Drop unused RwLock imports

OwnedRwLockWriteGuard and RwLock are no longer used after switching to SeqLock.

-use tokio::sync::{OwnedRwLockWriteGuard, RwLock};
+// (imports removed)

30-51: Existing‑context path returns an NGAP failure by design — confirm this matches expected behavior

Previously, implementations often schedule onto the existing context. If failing fast here is intentional, confirm AMF behavior/spec alignment for re‑entrant InitialUE when registration is in progress.

lightning-nf/omnipath/app/src/context/state/gmm_state.rs (2)

7-8: Derive Copy/Clone for ergonomic value semantics.

These states are small repr(u8) values and frequently copied; deriving Copy, Clone reduces churn and borrow issues.

Apply:

-#[derive(Eq, PartialEq, Hash)]
+#[derive(Copy, Clone, Eq, PartialEq, Hash)]

94-107: next_state for Registered: clarify or encode the common-procedure hop.

Comment says “Final state or can go to CommonProcedureInitiated” but next_state returns None. Consider returning Some(CommonProcedureInitiated) or clarifying that next_state encodes only the registration linear flow.

lightning-nf/omnipath/app/src/nas/registration_req/mod.rs (2)

35-37: Emit structured log for NoIdentity.

Convert TODO into a warning so operators can see malformed/empty IDs.

Apply:

-            MobileIdentity::NoIdentity(_no_identity) => {
-                // Todo push some logging here
-            }
+            MobileIdentity::NoIdentity(_no_identity) => {
+                tracing::warn!("NAS RegistrationRequest without a mobile identity");
+            }

26-63: Persist UE security capability from the request.

Prior handler logic populated ue_security_capability or errored; this impl omits it. Persisting this is necessary for subsequent security mode decisions.

Apply near the end of the function (before Ok(...)):

+        if let Some(sec) = &req.nas_ue_security_capability {
+            self.nas_context.ue_security_capability = Some(sec.clone());
+        } else {
+            tracing::warn!("Missing UE security capability in RegistrationRequest");
+        }
lightning-nf/omnipath/app/src/ngap/manager/context_queue.rs (3)

17-26: Docs mention RwLock—update to SeqLock.

The module docs still reference RwLock; adjust to reflect the SeqLock‑based design to prevent confusion.


202-209: Avoid unwrap on oneshot receive.

A drop/panic in the worker will panic callers. Return Result or map the error.

Apply:

-        rx.await.unwrap()
+        rx.await.expect("ContextQueue: operation task dropped or panicked")

Or better, change the API to return Result<O, oneshot::error::RecvError>.


136-140: Unnecessary Sync bound on FnOnce closures.

Requiring Sync for FnOnce is superfluous and restricts callers; remove it unless there’s a concrete need.

Apply:

-            + Send
-            + Sync
+            + Send

Do the same in schedule_and_wait.

Also applies to: 192-196

utils/client/src/nf_clients/oauth_service.rs (3)

19-23: Use Arc<[ServiceName]> instead of Vec to avoid per-call clones and shrink captures

Replace Vec with Arc<[ServiceName]> in the service and layer; accept Into<Arc<[ServiceName]>> in constructors to keep call sites ergonomic. This removes an allocation and makes .clone() cheap in call().

@@
-pub struct OAuthTokenService<S, const TARGET_NF_TYPE: NfType> {
+pub struct OAuthTokenService<S, const TARGET_NF_TYPE: NfType> {
     inner: S,
     nrf_client: Arc<NrfClient>,
-    service_names: Vec<ServiceName>,
+    service_names: Arc<[ServiceName]>,
 }
@@
-    pub fn new(
+    pub fn new(
         inner: S,
         nrf_client: Arc<NrfClient>,
-        service_names: Vec<ServiceName>,
+        service_names: impl Into<Arc<[ServiceName]>>,
     ) -> Self {
         Self {
             inner,
             nrf_client,
-            service_names,
+            service_names: service_names.into(),
         }
     }
@@
-    pub fn service_names(&self) -> &[ServiceName] {
+    pub fn service_names(&self) -> &[ServiceName] {
         &self.service_names
     }
@@
-pub struct OAuthTokenLayer<const TARGET_NF_TYPE: NfType> {
+pub struct OAuthTokenLayer<const TARGET_NF_TYPE: NfType> {
     nrf_client: Arc<NrfClient>,
-    service_names: Vec<ServiceName>,
+    service_names: Arc<[ServiceName]>,
 }
@@
-    pub fn new(
+    pub fn new(
         nrf_client: Arc<NrfClient>,
-        service_names: Vec<ServiceName>,
+        service_names: impl Into<Arc<[ServiceName]>>,
     ) -> Self {
         Self {
             nrf_client,
-            service_names,
+            service_names: service_names.into(),
         }
     }
@@
-    pub fn with_service(
+    pub fn with_service(
         nrf_client: Arc<NrfClient>,
         service_name: ServiceName,
     ) -> Self {
         Self {
             nrf_client,
-            service_names: vec![service_name],
+            service_names: vec![service_name].into(),
         }
     }
@@
-    fn layer(
+    fn layer(
         &self,
         inner: S,
     ) -> Self::Service {
-        OAuthTokenService::new(inner, self.nrf_client.clone(), self.service_names.clone())
+        OAuthTokenService::new(inner, self.nrf_client.clone(), self.service_names.clone())
     }

Also applies to: 30-37, 55-57, 112-114, 119-137, 150-159, 89-92


150-159: Minor: make the layer self-sufficient wrt sensitive headers

Today, SetSensitiveRequestHeadersLayer is applied by the caller (see utils/client/src/nf_clients/mod.rs). Consider documenting this requirement here, or optionally adding a tiny layer wrapper that always marks AUTHORIZATION as sensitive to prevent accidental leakage in other stacks.


175-180: Unit test is a stub; provide a real test via a mock NrfClient

Add a lightweight NrfClient test-double that sets a fixed Authorization header so we can assert header injection.

I can scaffold a minimal trait/adapter around set_auth_token for easy mocking and add a test if helpful.

utils/atomic-handle/src/lib.rs (2)

1-5: Prefer NonNull over raw pointer; derive Clone; tighten debug load ordering

Using NonNull avoids accidental nulls and makes unsafe blocks smaller; deriving Clone simplifies code; Relaxed is enough for debug printing.

@@
-use std::{
-    marker::PhantomData, ops::Deref, sync::{
-        atomic::{AtomicUsize, Ordering}, Arc
-    }
-};
+use std::{
+    marker::PhantomData,
+    ops::Deref,
+    ptr::NonNull,
+    sync::{atomic::{AtomicUsize, Ordering}, Arc},
+};
@@
-pub struct AtomicHandle<S, Field = ()>
+#[derive(Clone)]
+pub struct AtomicHandle<S, Field = ()>
 where
     S: AtomicOperation<Field> + Send + Sync ,
 {
     /// Keeps the original Arc<S> alive to prevent memory deallocation
     _owner: Arc<S>,
     /// Raw pointer to the specific atomic field within the struct
-    atomic_ptr: *const AtomicUsize,
+    atomic_ptr: NonNull<AtomicUsize>,
     /// Zero-sized marker to track which field this handle represents
     _phantom: PhantomData<Field>,
 }
@@
-    pub fn new(owner: Arc<S>) -> Self {
-        let atomic_ptr = owner.get_atomic() as *const AtomicUsize;
+    pub fn new(owner: Arc<S>) -> Self {
+        let atomic_ptr = NonNull::from(owner.get_atomic());
         Self {
             _owner: owner,
             atomic_ptr,
             _phantom: PhantomData,
         }
     }
@@
-    pub fn get(&self) -> &AtomicUsize {
-        unsafe { &*self.atomic_ptr }
+    pub fn get(&self) -> &AtomicUsize {
+        // Safety: NonNull points at a field inside `_owner`, whose lifetime we hold via Arc.
+        unsafe { self.atomic_ptr.as_ref() }
     }
@@
-// Manual implementation of Clone.
-impl<S, Field> Clone for AtomicHandle<S, Field>
-where
-    S: AtomicOperation<Field> + Send + Sync,
-{
-    fn clone(&self) -> Self {
-        Self {
-            _owner: Arc::clone(&self._owner),
-            atomic_ptr: self.atomic_ptr,
-            _phantom: PhantomData,
-        }
-    }
-}
+// (Clone derived above)
@@
         f.debug_struct("AtomicHandle")
-            .field("current_value", &self.load(Ordering::SeqCst))
+            .field("current_value", &self.load(Ordering::Relaxed))
             .field("field_type", &std::any::type_name::<Field>())
             .field("struct_type", &std::any::type_name::<S>())
             .finish()
     }

Also applies to: 32-42, 95-103, 108-111, 114-123, 125-137


8-15: Document safety invariants for implementors of AtomicOperation

Callers must ensure the returned &AtomicUsize has a stable address for the lifetime of Arc and is not inside buffers that may reallocate/move. Add explicit safety notes to prevent unsound impls.

@@
 /// The generic parameter `Field` allows the same type to implement
 /// this trait multiple times for different atomic fields.
 ///
 pub trait AtomicOperation<Field = ()> {
-    /// Returns a reference to the atomic field identified by the Field type
-    /// parameter
+    /// Returns a reference to the atomic field identified by the `Field` type parameter.
+    ///
+    /// Safety contract:
+    /// - The returned reference must point to a field whose memory address remains
+    ///   stable for as long as any `Arc<S>` clones exist (e.g., the field is inside `S`,
+    ///   not inside a relocatable buffer).
+    /// - It must not be freed while any `AtomicHandle<S, Field>` derived from the same
+    ///   owner `Arc<S>` is alive.
     fn get_atomic(&self) -> &AtomicUsize;
 }
lightning-nf/omnipath/app/src/nas/interface.rs (2)

17-21: Avoid logging full request Debug in error message (PII risk)

Printing the entire request on invalid transitions may leak identifiers. Remove it from Display; keep fields on the struct for programmatic access.

-#[error("Invalid state transition from {state:?} with request: {request:?}")]
+#[error("Invalid state transition from {state:?}")]
 pub struct InvalidStateTransition<R: Debug> {

212-226: Test covers spawn bounds; expand with a case that mutates self

Add a handler that updates internal state inside an async block to ensure the relaxed (no Sync) bounds compile in real scenarios.

utils/client/src/nf_clients/mod.rs (1)

162-171: Remove unused generics from request signature (H, Q, B).

They’re unused and create noise. Keep only Resp.

-pub async fn request<H, Q, B, Resp>(
+pub async fn request<Resp>(
 		&self,
 		req: HttpRequest<Body>,
 	) -> Result<(StatusCode, Resp), GenericClientError>
-	where
-		Q: Serialize,
-		H: Serialize,
-		B: Serialize,
-		Resp: DeserResponse,
+	where
+		Resp: DeserResponse,
utils/client/src/nf_clients/service_discovery/mod.rs (3)

84-93: Guard against negative validity periods to avoid cast pitfalls.

Casting negative i64 to u64 can mis-evaluate freshness. Treat non-positive as invalid.

 	pub fn is_valid(&self) -> bool {
 		if let Some(validity_seconds) = self.validity_period {
-			SystemTime::now()
+			if validity_seconds <= 0 {
+				return false;
+			}
+			SystemTime::now()
 				.duration_since(self.cached_at)
 				.unwrap_or(Duration::MAX)
-				.as_secs() < validity_seconds as u64
+				.as_secs() < validity_seconds as u64
 		} else {
 			self.nf_profile.is_some() // Valid if we have a profile and no expiry
 		}
 	}

95-99: Prefer service-specific URL selection using configured service_names.

get_base_url() picks an arbitrary URL from a HashMap. If multiple NF services exist, use service_names priority order to choose a base URL deterministically.

Minimal change approach:

  • Pass &[ServiceName] into update_request_url.
  • Choose the first matching service URL; fallback to get_base_url().
-	fn update_request_url(
-		request: &mut HttpRequest<Body>,
-		cached_profile: &CachedNfProfile,
-	) -> Result<(), ServiceDiscoveryError> {
-		match cached_profile.get_base_url() {
+	fn update_request_url(
+		request: &mut HttpRequest<Body>,
+		cached_profile: &CachedNfProfile,
+		service_names: &[ServiceName],
+	) -> Result<(), ServiceDiscoveryError> {
+		let preferred = service_names
+			.iter()
+			.find_map(|n| cached_profile.get_first_service_url(n));
+		match preferred.or_else(|| cached_profile.get_base_url()) {
 			Some(base_url) => {

And call site:

-				match Self::update_request_url(&mut request, &cached_profile) {
+				match Self::update_request_url(&mut request, &cached_profile, &self.layer.service_names) {

Also applies to: 347-373, 377-397


200-218: Cache reset on discovery failure is fine; consider exponential backoff later.

Current strategy invalidates cache to retry on next request. Looks good for now.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dbaa2fb and 8d4d9bd.

⛔ Files ignored due to path filters (1)
  • lightning-nf/omnipath/app/src/nas_old/gmm_state_machine.png is excluded by !**/*.png
📒 Files selected for processing (35)
  • Cargo.toml (1 hunks)
  • lightning-nf/omnipath/app/Cargo.toml (1 hunks)
  • lightning-nf/omnipath/app/src/context/gnb_context.rs (2 hunks)
  • lightning-nf/omnipath/app/src/context/mod.rs (1 hunks)
  • lightning-nf/omnipath/app/src/context/nas_context.rs (1 hunks)
  • lightning-nf/omnipath/app/src/context/state/atomic_gmm_state.rs (1 hunks)
  • lightning-nf/omnipath/app/src/context/state/gmm_state.rs (1 hunks)
  • lightning-nf/omnipath/app/src/context/state/mod.rs (1 hunks)
  • lightning-nf/omnipath/app/src/context/ue_context.rs (2 hunks)
  • lightning-nf/omnipath/app/src/lib.rs (1 hunks)
  • lightning-nf/omnipath/app/src/nas/interface.rs (1 hunks)
  • lightning-nf/omnipath/app/src/nas/mod.rs (1 hunks)
  • lightning-nf/omnipath/app/src/nas/registration_req/error.rs (1 hunks)
  • lightning-nf/omnipath/app/src/nas/registration_req/mod.rs (1 hunks)
  • lightning-nf/omnipath/app/src/nas/ue_actions.rs (0 hunks)
  • lightning-nf/omnipath/app/src/nas_old/builders/authentication_request.rs (1 hunks)
  • lightning-nf/omnipath/app/src/nas_old/handlers/authentication_response.rs (1 hunks)
  • lightning-nf/omnipath/app/src/nas_old/handlers/registration_request.rs (1 hunks)
  • lightning-nf/omnipath/app/src/nas_old/mod.rs (1 hunks)
  • lightning-nf/omnipath/app/src/nas_old/ue_actions.rs (1 hunks)
  • lightning-nf/omnipath/app/src/ngap/core/initial_ue_message.rs (2 hunks)
  • lightning-nf/omnipath/app/src/ngap/engine/controller.rs (1 hunks)
  • lightning-nf/omnipath/app/src/ngap/engine/mod.rs (1 hunks)
  • lightning-nf/omnipath/app/src/ngap/manager/context_manager.rs (2 hunks)
  • lightning-nf/omnipath/app/src/ngap/manager/context_queue.rs (5 hunks)
  • lightning-nf/omnipath/app/src/utils/mod.rs (1 hunks)
  • lightning-nf/omnipath/app/src/utils/seq_lock.rs (1 hunks)
  • utils/atomic-handle/Cargo.toml (1 hunks)
  • utils/atomic-handle/src/lib.rs (1 hunks)
  • utils/client/src/lib.rs (1 hunks)
  • utils/client/src/nf_clients/mod.rs (5 hunks)
  • utils/client/src/nf_clients/oauth_service.rs (1 hunks)
  • utils/client/src/nf_clients/service_discovery/future.rs (1 hunks)
  • utils/client/src/nf_clients/service_discovery/mod.rs (1 hunks)
  • utils/client/src/nrf_client.rs (4 hunks)
💤 Files with no reviewable changes (1)
  • lightning-nf/omnipath/app/src/nas/ue_actions.rs
🧰 Additional context used
🧬 Code graph analysis (24)
lightning-nf/omnipath/app/src/nas_old/builders/authentication_request.rs (4)
lightning-nf/omnipath/app/src/nas/builders/mod.rs (1)
  • authentication_request (2-2)
lightning-nf/omnipath/app/src/nas/builders/authentication_request.rs (2)
  • NasBuilder (7-16)
  • build (9-15)
lightning-nf/omnipath/app/src/nas/gmm.rs (1)
  • NasContext (24-97)
lightning-nf/omnipath/app/src/nas/nas_context.rs (1)
  • NasContext (8-13)
lightning-nf/omnipath/app/src/nas_old/ue_actions.rs (2)
lightning-nf/omnipath/app/src/nas/ue_actions.rs (2)
  • UeContext (3-25)
  • handle_nas (4-24)
lightning-nf/omnipath/app/src/nas/nas_context.rs (1)
  • NasContext (8-13)
lightning-nf/omnipath/app/src/nas/registration_req/error.rs (2)
lightning-nf/omnipath/app/src/nas/handlers/registration_request.rs (1)
  • NasHandler (54-72)
lightning-nf/omnipath/app/src/nas/nas_context.rs (1)
  • NasContext (8-13)
lightning-nf/omnipath/app/Cargo.toml (1)
lightning-nf/omnipath/app/src/context/app_context.rs (1)
  • update_config (126-133)
lightning-nf/omnipath/app/src/utils/seq_lock.rs (1)
lightning-nf/omnipath/app/src/ngap/manager/context_queue.rs (2)
  • new (43-49)
  • into_inner (54-57)
utils/client/src/nf_clients/service_discovery/future.rs (1)
utils/client/src/nf_clients/service_discovery/mod.rs (3)
  • new (46-82)
  • new (146-160)
  • new (270-279)
lightning-nf/omnipath/app/src/context/nas_context.rs (2)
lightning-nf/omnipath/app/src/nas/nas_context.rs (1)
  • NasContext (8-13)
lightning-nf/omnipath/app/src/nas/ue_actions.rs (1)
  • UeContext (3-25)
lightning-nf/omnipath/app/src/context/gnb_context.rs (3)
lightning-nf/omnipath/app/src/ngap/manager/context_manager.rs (1)
  • new (54-58)
utils/atomic-handle/src/lib.rs (1)
  • new (95-102)
lightning-nf/omnipath/app/src/context/ngap_context.rs (2)
  • new (28-44)
  • NgapContext (16-25)
lightning-nf/omnipath/app/src/nas/interface.rs (2)
lightning-nf/omnipath/app/src/context/state/gmm_state.rs (3)
  • fmt (168-184)
  • fmt (188-193)
  • from (154-156)
lightning-nf/omnipath/app/src/nas/registration_req/mod.rs (1)
  • pre_comp_state_change (17-24)
lightning-nf/omnipath/app/src/context/state/atomic_gmm_state.rs (3)
utils/atomic-handle/src/lib.rs (6)
  • std (149-149)
  • std (150-150)
  • fmt (143-152)
  • get (108-110)
  • deref (120-122)
  • clone (130-136)
lightning-nf/omnipath/app/src/context/state/gmm_state.rs (5)
  • fmt (168-184)
  • fmt (188-193)
  • can_access_services (77-79)
  • is_transitional (82-92)
  • default (148-150)
lightning-nf/omnipath/app/src/context/ue_context.rs (1)
  • fmt (30-39)
lightning-nf/omnipath/app/src/context/state/gmm_state.rs (3)
lightning-nf/omnipath/app/src/context/state/atomic_gmm_state.rs (5)
  • fmt (211-217)
  • can_access_services (170-172)
  • is_transitional (176-178)
  • default (183-185)
  • from (205-207)
lightning-nf/omnipath/app/src/context/ue_context.rs (1)
  • fmt (30-39)
lightning-nf/omnipath/app/src/nas/nas_context.rs (1)
  • GmmState (21-24)
lightning-nf/omnipath/app/src/context/mod.rs (1)
lightning-nf/omnipath/app/src/nas/handlers/mod.rs (1)
  • gmm_status (3-3)
lightning-nf/omnipath/app/src/nas_old/mod.rs (3)
lightning-nf/omnipath/app/src/nas_old/handlers/authentication_response.rs (1)
  • handle (8-15)
lightning-nf/omnipath/app/src/nas_old/handlers/registration_request.rs (1)
  • handle (56-70)
lightning-nf/omnipath/app/src/nas_old/builders/authentication_request.rs (1)
  • build (9-15)
utils/client/src/nf_clients/oauth_service.rs (3)
utils/client/src/nf_clients/mod.rs (1)
  • new (127-160)
utils/client/src/nf_clients/service_discovery/mod.rs (7)
  • new (46-82)
  • new (146-160)
  • new (270-279)
  • into_inner (292-294)
  • poll_ready (308-345)
  • call (347-374)
  • layer (233-242)
utils/client/src/nrf_client.rs (2)
  • new (127-140)
  • from (549-557)
lightning-nf/omnipath/app/src/nas/registration_req/mod.rs (3)
lightning-nf/omnipath/app/src/nas/interface.rs (3)
  • pre_comp_state_change (51-57)
  • state_transition_deregistered (90-95)
  • new (24-29)
lightning-nf/omnipath/app/src/nas/handlers/registration_request.rs (2)
  • initial_registration_handler (13-52)
  • NasHandler (54-72)
lightning-nf/omnipath/app/src/nas/gmm.rs (4)
  • NasContext (24-97)
  • deregistered (68-96)
  • registration_initiated (53-65)
  • unauthenticated (28-39)
lightning-nf/omnipath/app/src/ngap/core/initial_ue_message.rs (5)
lightning-nf/omnipath/app/src/context/state/atomic_gmm_state.rs (2)
  • new (20-24)
  • from (205-207)
lightning-nf/omnipath/app/src/ngap/manager/context_manager.rs (1)
  • new (54-58)
lightning-nf/omnipath/app/src/ngap/manager/context_queue.rs (1)
  • new (43-49)
lightning-nf/omnipath/app/src/utils/seq_lock.rs (1)
  • new (11-15)
lightning-nf/omnipath/app/src/nas/ue_actions.rs (2)
  • UeContext (3-25)
  • handle_nas (4-24)
lightning-nf/omnipath/app/src/context/ue_context.rs (3)
utils/atomic-handle/src/lib.rs (8)
  • std (149-149)
  • std (150-150)
  • new (95-102)
  • get_atomic (14-14)
  • get_atomic (177-179)
  • get_atomic (183-185)
  • get_atomic (189-191)
  • get_atomic (196-198)
lightning-nf/omnipath/app/src/context/state/atomic_gmm_state.rs (1)
  • new (20-24)
lightning-nf/omnipath/app/src/nas/nas_context.rs (2)
  • GmmState (21-24)
  • NasContext (8-13)
utils/client/src/nrf_client.rs (1)
utils/client/src/nf_clients/mod.rs (1)
  • request (162-180)
lightning-nf/omnipath/app/src/ngap/engine/mod.rs (1)
lightning-nf/omnipath/app/src/ngap/mod.rs (4)
  • models (8-10)
  • core (4-4)
  • engine (3-3)
  • network (6-6)
lightning-nf/omnipath/app/src/nas/mod.rs (2)
lightning-nf/omnipath/app/src/nas/handlers/registration_request.rs (1)
  • NasHandler (54-72)
lightning-nf/omnipath/app/src/nas/handlers/authentication_response.rs (1)
  • NasHandler (6-16)
Cargo.toml (1)
lightning-nf/omnipath/app/src/config/mod.rs (1)
  • SCTP (178-183)
utils/atomic-handle/src/lib.rs (2)
lightning-nf/omnipath/app/src/context/ue_context.rs (1)
  • get_atomic (24-26)
lightning-nf/omnipath/app/src/context/state/atomic_gmm_state.rs (5)
  • new (20-24)
  • get (146-148)
  • deref (191-193)
  • clone (199-201)
  • load (28-44)
utils/client/src/nf_clients/service_discovery/mod.rs (4)
utils/client/src/nf_clients/oauth_service.rs (11)
  • nrf_client (60-62)
  • nrf_client (95-96)
  • nrf_client (145-147)
  • new (27-37)
  • new (118-126)
  • service_names (55-57)
  • service_names (140-142)
  • layer (153-158)
  • inner (40-42)
  • poll_ready (78-83)
  • call (85-106)
utils/client/src/nf_clients/mod.rs (3)
  • new (127-160)
  • request (162-180)
  • base_url (67-67)
utils/client/src/nf_clients/service_discovery/future.rs (2)
  • new (33-37)
  • error (40-44)
utils/client/src/nrf_client.rs (2)
  • new (127-140)
  • from (549-557)
utils/client/src/nf_clients/mod.rs (3)
utils/client/src/nf_clients/oauth_service.rs (5)
  • new (27-37)
  • new (118-126)
  • nrf_client (60-62)
  • nrf_client (95-96)
  • nrf_client (145-147)
utils/client/src/nf_clients/service_discovery/future.rs (1)
  • new (33-37)
utils/client/src/nf_clients/service_discovery/mod.rs (3)
  • new (46-82)
  • new (146-160)
  • new (270-279)
🔇 Additional comments (13)
lightning-nf/omnipath/app/src/ngap/engine/controller.rs (1)

10-14: Import path aligns with module rename.

Confirming the interface rename is reflected here; everything builds against the new module structure cleanly.

lightning-nf/omnipath/app/src/ngap/engine/mod.rs (1)

2-7: Interface module rename looks good.

mod interface; plus the re-export keeps downstream imports stable after the rename.

lightning-nf/omnipath/app/src/context/ue_context.rs (1)

37-37: Debug output is concise and safe

Printing gmm via state.get() is a good, stable choice for logs.

lightning-nf/omnipath/app/src/nas_old/mod.rs (1)

25-30: Builder trait looks fine

The NasBuilder signature is clear and composable.

lightning-nf/omnipath/app/src/ngap/core/initial_ue_message.rs (1)

61-77: No change needed for GmmState variant
The GmmState in this file is imported from crate::context, whose enum defines Deregistered (not GmmDeregistered), so AtomicGmmState::new(GmmState::Deregistered) is correct.

lightning-nf/omnipath/app/src/context/state/gmm_state.rs (2)

138-140: Self→self transitions: confirm intentional.

Allowing identity transitions can mask no‑ops where callers expect a change. If not required, disallow to catch bugs; otherwise add a short comment stating why it’s allowed.


5-35: Ignore legacy GmmState in nas_old—only one active GmmState remains
The only GmmState used by the build is the enum in context/state/gmm_state.rs; the one in src/nas_old/nas_context.rs lives in an unused directory and has no imports in active code.

Likely an incorrect or invalid review comment.

utils/client/src/nf_clients/oauth_service.rs (2)

93-101: Fail-open behavior when OAuth is disabled or temporarily unavailable

If OAuth is disabled or token retrieval is transiently failing, returning Err here will make all requests fail. Confirm NrfClient::set_auth_token returns Ok(()) when disabled and uses cached tokens to avoid per-request NRF calls.


65-73: Verify ADT const-generic setup for NfType

  • Confirm the crate root (e.g. lib.rs or main.rs) includes #![feature(adt_const_params)] to enable const TARGET_NF_TYPE: NfType.
  • Ensure the NfType enum derives all traits required for a const‐generic parameter (e.g. Copy, Eq).

Applies to oauth_service.rs lines 65–73 and 85–106

lightning-nf/omnipath/app/src/nas/interface.rs (2)

32-37: Trait bounds look good

Reasonable defaults, explicit decode hook, and dispatcher structure are clear.


1-1: No + Sync bounds found in any NasMessageHandle impl return types. All existing implementations rely on the trait’s default handle (which still includes + Send + Sync), and none override it—therefore there are no + Sync bounds in impls to remove.

utils/client/src/nf_clients/service_discovery/mod.rs (1)

227-244: Layer implementation is correct and clone-safe.

Layer<S> for ServiceDiscoveryLayer returns a fresh ServiceDiscovery with no shared future; safe and idiomatic.

utils/client/src/nf_clients/mod.rs (1)

146-154: Layer order confirmed; approving.
Pipeline matches TowerReqwestClient alias (ServiceDiscovery → OAuth → SetSensitive → Trace → HttpClient).

Comment on lines +188 to +194
impl Deref for AtomicGmmState {
type Target = AtomicUsize;

fn deref(&self) -> &Self::Target {
&self.state
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Deref leaks the raw AtomicUsize and breaks your invariants

By exposing &AtomicUsize, callers can invoke operations like fetch_add or store with arbitrary values, producing invalid discriminants and triggering UB once load() transmutes them. This defeats the entire safety contract of AtomicGmmState. Please drop the Deref impl (or expose only constrained helpers) so the raw atomic ops remain encapsulated.

-impl Deref for AtomicGmmState {
-	type Target = AtomicUsize;
-
-	fn deref(&self) -> &Self::Target {
-		&self.state	
-	}
-}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
impl Deref for AtomicGmmState {
type Target = AtomicUsize;
fn deref(&self) -> &Self::Target {
&self.state
}
}
🤖 Prompt for AI Agents
In lightning-nf/omnipath/app/src/context/state/atomic_gmm_state.rs around lines
188-194, the Deref impl returns &AtomicUsize which leaks the raw atomic and
allows callers to call fetch_add/store with arbitrary values (violating
invariants and risking UB when you transmute load results); remove the Deref
implementation and instead keep the AtomicUsize private, then add controlled
accessor/mutator methods on AtomicGmmState (e.g., safe load(),
compare_exchange/validated_store(), increment_if_valid(), etc.) that enforce
valid discriminants and encapsulate all atomic operations so external code
cannot perform raw atomic ops.

Comment on lines +23 to +26
impl AtomicOperation<GmmStateField> for UeContext {
fn get_atomic(&self) -> &AtomicUsize {
&self.state
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

get_atomic relies on implicit Deref — make the dependency explicit (or add AsRef)

Returning &self.state assumes AtomicGmmState: Deref<Target = AtomicUsize>. Prefer an explicit projection to the inner AtomicUsize to avoid brittle coupling.

 impl AtomicOperation<GmmStateField> for UeContext {
-  fn get_atomic(&self) -> &AtomicUsize {
-    &self.state
-  }
+  fn get_atomic(&self) -> &AtomicUsize {
+    // Prefer an explicit projection, e.g., via AsRef or a dedicated accessor
+    self.state.as_ref()
+  }
 }

If AsRef<AtomicUsize> is not implemented for AtomicGmmState, add:

// in context/state/atomic_gmm_state.rs
impl AsRef<std::sync::atomic::AtomicUsize> for AtomicGmmState {
    fn as_ref(&self) -> &std::sync::atomic::AtomicUsize { &self.state }
}

Run to verify Deref/AsRef existence and call sites:


🏁 Script executed:

#!/bin/bash
rg -nP 'struct\s+AtomicGmmState\b|impl\s+Deref<\s*Target\s*=\s*AtomicUsize\s*>\s+for\s+AtomicGmmState|impl\s+AsRef<\s*AtomicUsize\s*>\s+for\s+AtomicGmmState' -C2

Length of output: 478


Make the AtomicUsize projection explicit — implement AsRef or return the inner AtomicUsize

AtomicGmmState wraps an AtomicUsize and no Deref/AsRef exists; returning &self.state depends on an implicit conversion. Replace the implicit projection with an explicit one.

  • Change in lightning-nf/omnipath/app/src/context/ue_context.rs:
    fn get_atomic(&self) -> &AtomicUsize { self.state.as_ref() }

  • If AsRef is missing, add in lightning-nf/omnipath/app/src/context/state/atomic_gmm_state.rs:
    impl AsRefstd::sync::atomic::AtomicUsize for AtomicGmmState {
    fn as_ref(&self) -> &std::sync::atomic::AtomicUsize { &self.state }
    }

🤖 Prompt for AI Agents
In lightning-nf/omnipath/app/src/context/ue_context.rs around lines 23 to 26,
the method get_atomic currently returns &self.state relying on an implicit
projection; update it to call the explicit AsRef conversion (e.g.
self.state.as_ref()) or otherwise return the inner AtomicUsize reference
explicitly. If AsRef is not yet implemented, add impl
AsRef<std::sync::atomic::AtomicUsize> for AtomicGmmState in
lightning-nf/omnipath/app/src/context/state/atomic_gmm_state.rs with as_ref
returning &self.state, then change fn get_atomic(&self) -> &AtomicUsize {
self.state.as_ref() } to use that explicit projection.

Comment on lines +16 to +22
pub trait NasHandler {
fn handle(
&self,
nas_context: &mut NasContext,
ue_context: &mut UeContext,
) -> impl Future<Output = Result<(), NasHandlerError>> + Send;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

impl Future in trait is not stable here — switch to GAT associated future or async-trait

Returning impl Future in trait methods (RPITIT) is not usable in stable Rust across broad MSRV ranges. Use a GAT associated type or async_trait.

GAT option (zero allocation, idiomatic on stable):

-pub trait NasHandler {
-    fn handle(
-        &self,
-        nas_context: &mut NasContext,
-        ue_context: &mut UeContext,
-    ) -> impl Future<Output = Result<(), NasHandlerError>> + Send;
-}
+pub trait NasHandler {
+    type Fut<'a>: Future<Output = Result<(), NasHandlerError>> + Send
+    where
+        Self: 'a;
+
+    fn handle<'a>(
+        &'a self,
+        nas_context: &'a mut NasContext,
+        ue_context: &'a mut UeContext,
+    ) -> Self::Fut<'a>;
+}

Alternatively:

use async_trait::async_trait;

#[async_trait]
pub trait NasHandler {
    async fn handle(
        &self,
        nas_context: &mut NasContext,
        ue_context: &mut UeContext,
    ) -> Result<(), NasHandlerError>;
}

Update implementors accordingly (builders/handlers).

🤖 Prompt for AI Agents
In lightning-nf/omnipath/app/src/nas_old/mod.rs around lines 16–22, the trait
currently returns an `impl Future` which is unstable; replace it by either (A) a
GAT-style associated future: add an associated generic Future type on the trait
(e.g. Future<'a> that implements Future<Output = Result<(), NasHandlerError>> +
Send with the appropriate lifetime bound) and change `handle` to take
lifetime-parameterized references and return that associated Future type, then
update all implementors to return that concrete future type; or (B) use the
async-trait crate: annotate the trait with the async_trait attribute and make
`handle` an async fn returning `Result<(), NasHandlerError>`, then update all
implementors to use async functions — pick one approach and update all
implementations and imports accordingly.

Comment on lines +64 to +65
) -> impl Future<Output = Result<(State, Option<NgapPdu>), Self::Error>> + Send + Sync {
async move {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Overly strict Sync bound on returned futures blocks common implementations

Requiring Future + Sync prevents handlers that mutate self inside async blocks (capturing &mut self makes futures !Sync). Drop Sync and keep Send; tokio::spawn only needs Send.

-    ) -> impl Future<Output = Result<(State, Option<NgapPdu>), Self::Error>> + Send + Sync {
+    ) -> impl Future<Output = Result<(State, Option<NgapPdu>), Self::Error>> + Send {
@@
-    ) -> impl Future<Output = Result<(State, Option<NgapPdu>), Self::Error>> + Send + Sync {
+    ) -> impl Future<Output = Result<(State, Option<NgapPdu>), Self::Error>> + Send {
@@
-    ) -> impl std::future::Future<Output = Result<(GmmState, Option<NgapPdu>), Self::Error>>
-        + Send
-        + Sync {
+    ) -> impl std::future::Future<Output = Result<(GmmState, Option<NgapPdu>), Self::Error>> + Send {

Apply the same -Sync change to each per-state handler signature.

Also applies to: 93-96, 100-103, 107-110, 114-117, 121-124, 126-130, 132-136, 138-143, 145-150, 191-199

🤖 Prompt for AI Agents
In lightning-nf/omnipath/app/src/nas/interface.rs around lines 64-65 (and also
update the same change at ranges 93-96, 100-103, 107-110, 114-117, 121-124,
126-130, 132-136, 138-143, 145-150, 191-199), the handler signatures currently
return futures bounded by Send + Sync which prevents common implementations that
capture &mut self (making futures !Sync); remove the Sync bound and keep only
Send on the returned Future types so the signatures become impl Future<...> +
Send (not + Sync), and apply this exact -Sync change consistently to each
per-state handler signature listed.

Comment on lines +145 to +150
fn state_transition_irrecoverable(
&mut self,
req: Self::Request,
) -> impl Future<Output = Result<(State, Option<NgapPdu>), Self::Error>> + Send + Sync {
async move { Err(InvalidStateTransition::new(GmmState::CommonProcedureInitiated, req).into()) }
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Bug: wrong state reported for Irrecoverable default transition

Should use GmmState::Irrecoverable, not CommonProcedureInitiated.

-    async move { Err(InvalidStateTransition::new(GmmState::CommonProcedureInitiated, req).into()) }
+    async move { Err(InvalidStateTransition::new(GmmState::Irrecoverable, req).into()) }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fn state_transition_irrecoverable(
&mut self,
req: Self::Request,
) -> impl Future<Output = Result<(State, Option<NgapPdu>), Self::Error>> + Send + Sync {
async move { Err(InvalidStateTransition::new(GmmState::CommonProcedureInitiated, req).into()) }
}
fn state_transition_irrecoverable(
&mut self,
req: Self::Request,
) -> impl Future<Output = Result<(State, Option<NgapPdu>), Self::Error>> + Send + Sync {
async move { Err(InvalidStateTransition::new(GmmState::Irrecoverable, req).into()) }
}
🤖 Prompt for AI Agents
In lightning-nf/omnipath/app/src/nas/interface.rs around lines 145 to 150, the
default irrecoverable state transition currently constructs an
InvalidStateTransition with GmmState::CommonProcedureInitiated; change that to
GmmState::Irrecoverable so the error reports the correct target state. Update
the InvalidStateTransition::new call to pass GmmState::Irrecoverable and keep
the rest of the async return signature and Err(...) wrapping unchanged.

Comment on lines +63 to +65
let url_str = format!("{}://{:?}:{}", scheme, host, port);
Url::parse(&url_str).ok()
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Broken URL construction: using {:?} for host yields invalid URLs.

Debug formatting inserts quotes; parsing may fail. Use {}.

-							let url_str = format!("{}://{:?}:{}", scheme, host, port);
+							let url_str = format!("{}://{}:{}", scheme, host, port);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let url_str = format!("{}://{:?}:{}", scheme, host, port);
Url::parse(&url_str).ok()
})
let url_str = format!("{}://{}:{}", scheme, host, port);
Url::parse(&url_str).ok()
})
🤖 Prompt for AI Agents
In utils/client/src/nf_clients/service_discovery/mod.rs around lines 63–65, the
URL is being built with debug formatting "{:?}" for the host which inserts
quotes and produces invalid URLs; change the formatter to use "{}", ensuring the
host is formatted via Display (e.g., replace the "{:?}" usage with "{}"),
rebuild and run tests to confirm Url::parse succeeds with the corrected string.

Comment on lines +181 to +185
self: Self
) -> Result<Arc<CachedNfProfile>, ServiceDiscoveryError> {
// Acquire a permit. This will pause if another task is already updating.
let _permit = self.discovery_semaphore.acquire().await.unwrap();

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Use acquire_owned() to keep the future Send; avoid unwrap().

Semaphore::acquire() returns a non-Send permit, making the async future !Send, but you store it in a Pin<Box<dyn Future + Send>>. Also, unwrap() can panic.

-		let _permit = self.discovery_semaphore.acquire().await.unwrap();
+		let _permit = self
+			.discovery_semaphore
+			.clone()
+			.acquire_owned()
+			.await
+			.map_err(|_| ServiceDiscoveryError::SemaphoreClosed)?;

Add an error variant:

 #[derive(thiserror::Error, Debug)]
 pub enum ServiceDiscoveryError {
+	#[error("Discovery semaphore closed")]
+	SemaphoreClosed,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
self: Self
) -> Result<Arc<CachedNfProfile>, ServiceDiscoveryError> {
// Acquire a permit. This will pause if another task is already updating.
let _permit = self.discovery_semaphore.acquire().await.unwrap();
++ b/utils/client/src/nf_clients/service_discovery/mod.rs
@@ -181,7 +181,12 @@ impl ServiceDiscoveryClient {
// Acquire a permit. This will pause if another task is already updating.
let _permit = self
.discovery_semaphore
.clone()
.acquire_owned()
.await
.map_err(|_| ServiceDiscoveryError::SemaphoreClosed)?;
Suggested change
self: Self
) -> Result<Arc<CachedNfProfile>, ServiceDiscoveryError> {
// Acquire a permit. This will pause if another task is already updating.
let _permit = self.discovery_semaphore.acquire().await.unwrap();
++ b/utils/client/src/nf_clients/service_discovery/mod.rs
@@ -10,6 +10,9 @@
#[derive(thiserror::Error, Debug)]
pub enum ServiceDiscoveryError {
#[error("Discovery semaphore closed")]
SemaphoreClosed,
// ... existing variants ...
}
🤖 Prompt for AI Agents
In utils/client/src/nf_clients/service_discovery/mod.rs around lines 181 to 185,
the code uses self.discovery_semaphore.acquire().await.unwrap(), which yields a
non-Send permit and can panic; change this to use
self.discovery_semaphore.acquire_owned().await and map the error into a new
ServiceDiscoveryError variant instead of unwrap-ing, keep the owned permit bound
for the duration of the update to ensure it remains Send, and add an appropriate
enum variant (e.g., SemaphoreClosed or PermitAcquisitionFailed) to
ServiceDiscoveryError and return that variant on acquisition failure.

Comment on lines +308 to +345
fn poll_ready(
&mut self,
cx: &mut Context<'_>,
) -> Poll<Result<(), Self::Error>> {
// First check if we have a valid cache (fast path)
if self.layer.is_cache_valid() {
// Cache is valid, just check if inner service is ready
return self.inner.poll_ready(cx).map_err(Into::into);
}

// Cache is stale, we need to discover
loop {
if let Some(ref mut discovery_fut) = self.discovery_future {
// We have an ongoing discovery, poll it
match discovery_fut.as_mut().poll(cx) {
Poll::Ready(Ok(_cached_profile)) => {
// Discovery completed successfully
self.discovery_future = None;
// Now check if inner service is ready
return self.inner.poll_ready(cx).map_err(Into::into);
}
Poll::Ready(Err(e)) => {
// Discovery failed
self.discovery_future = None;
return Poll::Ready(Err(Box::new(e)));
}
Poll::Pending => {
// Discovery is still in progress
return Poll::Pending;
}
}
} else {
// No ongoing discovery, start one
self.discovery_future = Some(Box::pin(ServiceDiscoveryLayer::discover_and_update_cache(self.layer.clone())));
// Continue the loop to poll the new future
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

poll_ready state machine is solid; single in-flight discovery per service.

The double-check after acquiring and proper error propagation are good.

Run quick greps to ensure no non-Send permits remain and bad URL formatting is gone:


🏁 Script executed:

#!/bin/bash
rg -nP 'acquire\(' utils/client/src/nf_clients/service_discovery
rg -nP 'format!\([^)]*{:?\}' utils/client/src/nf_clients/service_discovery

Length of output: 245


🏁 Script executed:

#!/bin/bash
sed -n '150,200p' utils/client/src/nf_clients/service_discovery/mod.rs

Length of output: 1573


🏁 Script executed:

#!/bin/bash
sed -n '280,380p' utils/client/src/nf_clients/service_discovery/mod.rs

Length of output: 2949


poll_ready logic is solid and semaphore permits are Send. Fix the URL formatting
Line 63 still uses a debug specifier—replace

let url_str = format!("{}://{:?}:{}", scheme, host, port);

with

let url_str = format!("{}://{}:{}", scheme, host, port);
🤖 Prompt for AI Agents
In utils/client/src/nf_clients/service_discovery/mod.rs around lines 308 to 345,
the URL string is being built with a debug formatter for the host which produces
extra quotes/braces; replace the debug specifier with the display specifier so
the host is formatted as a plain string (i.e., change the format call to use {}
for host instead of {:?}); ensure the surrounding variables (scheme, host, port)
remain in the same order and types implement Display.

Comment on lines 501 to 505
AccessTokenRspTokenType::Bearer => {
let mut string = "Bearer ".to_owned();
let mut string = "bearer ".to_owned();
string.push_str(token);
string
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Authorization scheme must use canonical “Bearer” casing

OAuth 2.0 access tokens require the Authorization header to start with Bearer (RFC 6750). Emitting bearer <token> breaks interoperability with servers that enforce the scheme’s case. Please restore the capitalized form.

-			let mut string = "bearer ".to_owned();
+			let mut string = "Bearer ".to_owned();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
AccessTokenRspTokenType::Bearer => {
let mut string = "Bearer ".to_owned();
let mut string = "bearer ".to_owned();
string.push_str(token);
string
}
AccessTokenRspTokenType::Bearer => {
let mut string = "Bearer ".to_owned();
string.push_str(token);
string
}
🤖 Prompt for AI Agents
In utils/client/src/nrf_client.rs around lines 501 to 505, the Authorization
header value is constructed using lowercase "bearer " which violates RFC 6750;
change the prefix to "Bearer " (capital B) when building the string so the
header is emitted as "Bearer <token>" to ensure correct OAuth2 scheme casing and
interoperability.

Comment on lines +521 to +538
fn is_token_expired(token: &AccessTokenRsp) -> bool {
// Get current time
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
// SAFETY: As now system time should not be before UNIX_EPOCH
.unwrap_or_default()
.as_secs();
// Check if token has expires_in field
if let Some(expires_in) = token.expires_in {
// Calculate expiry time (assuming the token was issued recently)
// In a real implementation, you might want to store the issue time
// For now, we assume the token was just issued
let buffer_seconds = 30; // 30 second buffer before expiry
let expires_at = now + expires_in as u64;
let expires_with_buffer = expires_at.saturating_sub(buffer_seconds);

now >= expires_with_buffer
} else {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Token expiry check never invalidates cached tokens

is_token_expired adds expires_in to the current timestamp, so the comparison becomes now >= now + expires_in - buffer, which never trips (unless expires_in ≤ buffer). As a result, cached tokens are treated as valid forever and we keep sending expired credentials to NRF once the real TTL elapses. Please derive the expiry using the moment the token was issued/stored (e.g., issued_at + expires_in) or track an absolute expiry at insertion time so stale tokens are evicted before reuse.

🤖 Prompt for AI Agents
In utils/client/src/nrf_client.rs around lines 521 to 538, the expiry check
incorrectly adds expires_in to the current time (now) which makes the token
never appear expired; change the logic to compute expiry from the token's
issuance time or from an absolute expiry stored when caching: either (a)
add/require an issued_at (unix seconds) on AccessTokenRsp and compute expires_at
= issued_at + expires_in then compare if now >=
expires_at.saturating_sub(buffer) or (b) when inserting the token into the cache
compute and store expires_at = now + expires_in and then compare now >=
expires_at.saturating_sub(buffer); ensure buffer is applied by subtracting it
from expires_at and handle missing fields gracefully.

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.

2 participants