In Part 1 we followed a VoWiFi subscriber through IKE_SA_INIT, IKE_AUTH, and the EAP-AKA / EAP-AKA' authentication flow that bridges the UE, the AAA server, and the HSS. By the end of that exchange both endpoints had cryptographic confidence in each other’s identity, plus a Master Session Key to use for subsequent key derivation.
What the UE did not yet have was anything resembling actual connectivity. No IP address, no bearer to the mobile core, no path to the IMS. This article covers what happens in the seconds between EAP-Success and the first SIP REGISTER reaching the P-CSCF.
The Big Picture for Operators
Authentication is just the first half. Between “the subscriber is who they say they are” and “the call connects”, the ePDG has to verify they are authorized to use VoWiFi, request a bearer from the PGW, and program the kernel to actually move encrypted packets. The volte.io ePDG does all three in parallel and converts every failure mode into a specific signal the handset can act on – which is what makes VoWiFi feel reliable to the subscriber.
What Happens Between EAP-Success and the First Packet
Three things happen, more or less in parallel, the moment EAP-AKA' succeeds:
1. S6b authorization
The ePDG asks the AAA server whether this subscriber is allowed to use VoWiFi on this APN through this PGW – a different question from authentication, answered over Diameter.
2. S2b bearer establishment
The ePDG asks the PGW, over GTPv2-C, to create a session and hand back the resources the UE needs – IP address, P-CSCF, DNS, QoS limits.
3. IPsec Child SAs in the kernel
The ePDG programs the Linux kernel’s XFRM subsystem with the inbound and outbound Security Associations and policies that will encrypt and decrypt user-plane traffic.
4. Final IKE_AUTH response to the UE
Once those three are in place, the ePDG returns the UE’s IP address, the P-CSCF address, and the DNS servers. The handset can now register with the IMS.
1. S6b: Authorization Before Connectivity
Authentication and authorization are different questions. Authentication asks who is this subscriber? – answered by EAP-AKA / EAP-AKA' against the HSS. Authorization asks is this subscriber allowed to use VoWiFi on this APN through this PGW? – answered over the S6b interface between the ePDG and the 3GPP AAA server, defined in TS 29.273 §9.
S6b uses Diameter and is conceptually straightforward. The ePDG sends an AA-Request (AAR) carrying the subscriber’s NAI, the requested APN (typically ims for VoWiFi), and the identity of the PGW it intends to use. The AAA server checks this against the subscriber’s profile – fetched from the HSS during the authentication phase – and returns an AA-Answer (AAA) with the result.
A successful AAA carries more than just an OK. The interesting AVPs include:
APN-Configuration– the QoS profile, the AMBR limits, and the list of permitted APNs for this subscriber.MIP6-Agent-Info– the PGW the AAA wants this session anchored to, useful in roaming scenarios where the home AAA selects the home PGW.3GPP-Charging-Characteristics– how the ePDG and PGW should bill this session.Trace-Info– optional trace activation, used for diagnostic capture of subscriber-specific signaling.
If the AAA returns a non-success result code – common reasons include APN not provisioned, subscriber barred for non-3GPP access, or a missing roaming agreement – the ePDG terminates the IKE exchange with an AUTHORIZATION_FAILED notification. The UE typically responds by falling back to LTE, which is the right behavior: a subscriber who cannot use VoWiFi should not have the symptom appear as “VoWiFi sometimes works”.
S6b Sessions Are Long-Lived
The S6b session persists for the entire VoWiFi attach. The AAA can send Re-Auth-Request (RAR) or Abort-Session-Request (ASR) at any point to revoke authorization mid-session. Production deployments see this most often during subscriber profile changes – a service downgrade at midnight, for example, propagates through the AAA and lands as an ASR on every active session. The ePDG must handle these gracefully, tearing down the IKE SA and releasing the bearer when instructed.
2. S2b: Asking the PGW for a Bearer
While S6b is in flight (or immediately after, depending on the implementation), the ePDG initiates an S2b session toward the PGW. S2b is GTPv2-C, defined in TS 29.274 with VoWiFi-specific behavior in TS 23.402 §7.
The ePDG sends a Create Session Request to the PGW. The mandatory information elements include:
- IMSI – the subscriber’s 15-digit identifier, recovered from the NAI used during authentication.
- APN – the access point name the UE requested, almost always
imsfor VoWiFi. - PDN Type – IPv4, IPv6, or IPv4v6. Modern handsets request IPv4v6 and let the PGW decide.
- PAA (PDN Address Allocation) – the UE’s preferred address, or a request to allocate dynamically.
- Bearer Context – the EPS Bearer ID, the requested QoS, and the F-TEID (Fully Qualified Tunnel Endpoint Identifier) for the user plane on the ePDG side.
- Sender F-TEID for Control Plane – the ePDG’s GTP-C endpoint, where the PGW will send subsequent control messages.
- APCO (Additional Protocol Configuration Options) – the modern container for what used to be PCO, used to request P-CSCF address, DNS servers, and IPv6 prefix delegation parameters.
The F-TEID is worth a moment of attention. It is a 4-byte integer that uniquely identifies a GTP tunnel endpoint at a specific IP address, and the ePDG and PGW each pick their own. Once the session is established, the ePDG knows the PGW’s F-TEID for both control and user plane, and the PGW knows the ePDG’s. Every GTP-U packet carries the F-TEID of the receiver in its header, so each side can identify which subscriber’s session a packet belongs to – without parsing the inner IP header.
The Create Session Response
The PGW responds with a Create Session Response carrying everything the UE needs to function:
- The allocated IPv4 address and / or IPv6 prefix for the UE.
- The PGW’s F-TEID for the user plane, which the ePDG will use as the GTP-U destination for uplink packets.
- The PGW’s F-TEID for the control plane, for subsequent modifications and the eventual session deletion.
- APCO containing the P-CSCF IPv4 / IPv6 address(es) and DNS server addresses – the items the UE will need to reach the IMS.
- Bearer-level QoS – the negotiated AMBR and the bearer’s QCI and ARP.
At this point the ePDG has everything it needs to tell the UE what its network looks like. But there is one more thing to do first: install the kernel state that will let traffic actually move.
3. Installing the IPsec Child SAs
The IKE_AUTH exchange in Part 1 ended with mutual authentication and a Master Session Key. It did not yet result in any actual IPsec Security Associations – those are negotiated separately, alongside the EAP exchange but completed only at the end.
A pair of Child SAs is needed: one for inbound traffic from the UE to the ePDG (decryption), one for outbound traffic from the ePDG to the UE (encryption). Each Child SA has its own SPI, its own keys derived from the IKE SA’s SK_d plus fresh nonces, and its own selectors specifying which traffic it covers.
3.1 Key Derivation for the Child SAs
The Child SA keys come from SK_d (the derivation key established during IKE_AUTH) combined with fresh nonces from the Child SA negotiation. RFC 7296 §2.17 defines the construction:
KEYMAT = prf+(SK_d, Ni | Nr)
The output is sliced into the per-direction keys. For AES-GCM-256 that means a 32-byte encryption key plus a 4-byte salt per direction; for AES-CBC-256 with HMAC-SHA-256-128 it means a 32-byte encryption key plus a 32-byte integrity key per direction. The slicing order is precisely defined by RFC 7296 §2.17:
- Encryption key for initiator-to-responder traffic.
- Authentication key for initiator-to-responder traffic.
- Encryption key for responder-to-initiator traffic.
- Authentication key for responder-to-initiator traffic.
For an ePDG, “initiator-to-responder” is uplink (UE to ePDG), and “responder-to-initiator” is downlink (ePDG to UE). Getting the slicing right matters: keys that match the spec but are assigned to the wrong direction produce ESP packets that look correct on the wire but fail integrity checks on the peer – a class of bug that is invisible until it isn’t.
SK_d and the fresh nonces are expanded by prf+ into per-direction encryption and authentication keys.3.2 Programming the Kernel
The Linux kernel’s XFRM subsystem is what actually performs ESP encryption and decryption. The ePDG userspace process talks to it through the netlink-based XFRM API (or its pfkey equivalent), installing four pieces of state per session:
- An inbound SA – covering ESP packets arriving from the UE’s IP address, decrypting them with the inbound key, and producing plaintext IP packets.
- An inbound policy – telling the kernel that any packet matching the UE’s traffic selectors must arrive via that inbound SA, and dropping packets that bypass it.
- An outbound SA – covering plaintext IP packets destined for the UE, encrypting them with the outbound key, and producing ESP packets sent toward the UE.
- An outbound policy – directing matching plaintext packets through the outbound SA before they leave the host.
For a NAT-traversed session – which, as covered in our NAT-T article, is the common case in Kubernetes – the SAs additionally specify UDP encapsulation on port 4500 per RFC 3948.
Once the four pieces of state are in place, the kernel handles every subsequent ESP packet without involving the userspace ePDG process at all. On hardware that supports IPsec offload, the kernel further delegates the cryptographic operations to the NIC, which handles ESP transformation, encryption, integrity checking, and NAT-T encapsulation inline.
3.3 Why This Architecture Matters
Keeping the data plane in the kernel – rather than implementing ESP in userspace – is the architectural decision that makes the volte.io ePDG suitable for production traffic loads. A single CPU core handling AES-NI-accelerated XFRM can comfortably push gigabits per second of ESP traffic. The userspace ePDG stays out of the data path entirely, which means user-plane throughput is bounded by the NIC and the kernel scheduler, not by the speed at which an Erlang process can shovel packets.
4. The Final IKE_AUTH Response
With the Child SAs installed and the bearer established, the ePDG can now reply to the UE’s last IKE_AUTH request. The response carries:
- EAP-Success, completing the EAP exchange that ran inside the IKE_AUTH messages.
- The AUTH payload, signed with the ePDG’s certificate, covering the responder’s contribution to mutual authentication.
- Configuration Payload (CFG) carrying the UE’s allocated IP address, internal DNS servers, and P-CSCF address – sourced directly from the APCO returned by the PGW.
- Traffic Selectors (TSi, TSr) specifying which traffic the just-installed Child SA covers – typically “all IP traffic from the UE’s allocated address to anywhere” on the inbound side, mirrored on the outbound.
- SA payload confirming the Child SA proposal accepted by the ePDG.
The UE receives this message, installs its own side of the Child SAs in its IPsec stack, and considers the tunnel up. Within milliseconds it begins sending its first packets – typically a SIP REGISTER toward the P-CSCF address it just learned.
5. What Failure Looks Like
Each of the three preceding exchanges can fail, and the failure modes propagate back to the UE in distinct ways.
S6b failure
For example, the AAA returning DIAMETER_ERROR_USER_UNKNOWN because the subscriber profile is missing – produces an AUTHORIZATION_FAILED IKE notification. The UE typically retries shortly, then falls back to cellular.
S2b failure
For example, the PGW returning APN access denied – no subscription because the requested APN is not in the subscriber’s profile – produces an IKE notification carrying the GTP cause as a private notification type. Modern UEs interpret this and stop retrying for that APN until the subscriber’s profile is refreshed.
Child SA installation failure
For example, the kernel rejecting an XFRM policy because of an address conflict with an existing tunnel – produces an internal error that the ePDG converts into an INTERNAL_ADDRESS_FAILURE notification. Rare in practice, and almost always indicates an operational problem rather than a subscriber issue.
The common thread: the ePDG converts every failure mode into a specific IKE notification with a meaningful type. UE implementations rely on this to decide whether to retry, fall back, or report failure to the user. A vague or generic failure response leaves the UE in a state where it does not know what to do next – and that, more than any specific bug, is what makes a VoWiFi solution feel unreliable to the subscriber.
Coming Up in Part 3
At this point the tunnel is up, the bearer is established, the kernel has installed the IPsec state, and the UE has its IP address and the IMS server addresses it needs. From the subscriber’s point of view, VoWiFi is now working – calls can be made, messages can be sent.
What we have not yet examined is what actually happens to a packet as it traverses the ePDG. Part 3 follows a single uplink packet from the moment the UE places it on the air, through ESP decryption in the kernel, into the GTP-U encapsulation that carries it to the PGW, and back the other way for the downlink. We will look at how kernel XFRM and the GTP-U userspace path interact, why this architecture is fast, and what the volte.io ePDG does that is specific to running this setup at scale.
Why This Matters Commercially
Bearer establishment is invisible to the subscriber when it works and instantly noticed when it doesn’t. The work the ePDG does in these few seconds – correctly authorizing, correctly negotiating with the PGW, correctly programming the kernel, and correctly translating every failure into something the handset understands – is what separates a VoWiFi service that subscribers rely on from one they learn to switch off. The volte.io ePDG is built around this principle, and the rest of our stack follows the same pattern.