Loading learning content...
An IPSec implementation doesn't maintain just one or two Security Associations—a busy VPN gateway might manage thousands of active SAs simultaneously. Each SA must be immediately retrievable when a packet needs processing, and the system must quickly determine which packets require protection and which can pass through unprotected.
To solve this management challenge, IPSec defines two essential databases:
These databases work together: the SPD determines the security policy for traffic, and the SAD provides the SA instances that implement those policies. Understanding both databases is essential for anyone configuring, troubleshooting, or implementing IPSec systems.
By the end of this page, you will understand the structure and operation of both the SPD and SAD, including how selectors work, how lookups are performed, and how the databases coordinate to process packets efficiently.
The division between SPD and SAD reflects a fundamental separation of concerns in IPSec's architecture:
This separation allows policies to be defined independently from the specific cryptographic instances that implement them. You can define a policy saying "all traffic between networks A and B must be encrypted" without specifying which keys or algorithms are used—that's the SA's job.
For outbound traffic, SPD is consulted FIRST to determine if protection is needed. For inbound IPSec traffic, SAD is consulted FIRST (using the SPI) to decrypt, then SPD is checked to verify the protection matched the required policy. This prevents an attacker from sending unprotected traffic that should have been protected.
The Security Policy Database is an ordered list of policy entries that dictate how traffic should be handled. Each entry contains selectors (matching criteria) and an action (what to do with matching traffic).
SPD Entry Structure:
| Component | Purpose | Example Values |
|---|---|---|
| Name/ID | Human-readable policy identifier | Branch_Office_VPN |
| Selectors | Traffic matching criteria | Source IP, Dest IP, Protocol, Ports |
| Action | What to do with matching traffic | PROTECT, BYPASS, DISCARD |
| SA Specification | Requirements for protection (if PROTECT) | ESP-TUNNEL, AES-GCM, etc. |
| Priority/Order | Which policy takes precedence | Lower number = higher priority |
The Three SPD Actions:
Selectors — Traffic Matching Criteria:
Selectors are the patterns that determine which packets match a policy entry. IPSec defines a rich set of selectors:
| Selector | Description | Matching Options |
|---|---|---|
| Local IP Address | IP address(es) of the local endpoint | Single, Range, Subnet, ANY |
| Remote IP Address | IP address(es) of the remote endpoint | Single, Range, Subnet, ANY |
| Next Layer Protocol | Transport protocol number | TCP (6), UDP (17), ICMP (1), ANY |
| Local Port | Local port number(s) | Single, Range, ANY (ignored for ICMP) |
| Remote Port | Remote port number(s) | Single, Range, ANY (ignored for ICMP) |
| ICMP Type/Code | ICMP message type and code | Type, Type+Code, ANY |
| Name | FQDN or User ID (for remote access) | user@example.com, host.example.com |
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
# Example SPD Entries (Conceptual Representation) # Policy 1: Protect all traffic between corporate networksSPD_Entry { name: "Corporate_Site_to_Site" priority: 100 selectors: { local_address: 10.1.0.0/16 # Headquarters remote_address: 10.2.0.0/16 # Branch office protocol: ANY } action: PROTECT sa_specification: { protocol: ESP mode: TUNNEL encryption: AES-256-GCM integrity: (included in GCM) }} # Policy 2: Allow IKE traffic to pass (required for negotiation)SPD_Entry { name: "IKE_Bypass" priority: 50 # Higher priority (lower number) selectors: { local_address: ANY remote_address: ANY protocol: UDP local_port: 500 remote_port: 500 } action: BYPASS} # Policy 3: Block all traffic to known malicious networkSPD_Entry { name: "Block_Malicious" priority: 10 # Highest priority selectors: { local_address: ANY remote_address: 198.51.100.0/24 # Malicious network protocol: ANY } action: DISCARD} # Policy 4: Default - bypass all other trafficSPD_Entry { name: "Default_Bypass" priority: 1000 # Lowest priority (catch-all) selectors: { local_address: ANY remote_address: ANY protocol: ANY } action: BYPASS}SPD entries are evaluated in priority order—first match wins. A misconfigured priority can cause traffic to bypass protection or be blocked unexpectedly. Always place more specific policies before general policies, and verify IKE/IKEv2 bypass policies are high priority to ensure key exchange can occur.
RFC 4301 defines a conceptual split of the SPD for clarity and efficiency:
SPD-S (SPD-Static): The administratively configured policies. This is the "source of truth" that administrators edit. Entries here are templates that specify what protection is required.
SPD-I (SPD-Inbound): A cache derived from SPD-S entries for inbound traffic processing. Contains additional state linking policies to active inbound SAs.
SPD-O (SPD-Outbound): A cache derived from SPD-S entries for outbound traffic processing. Links policies to active outbound SAs.
Why separate caches?
Looking up the full SPD for every packet would be prohibitively expensive. The caches provide fast lookups by pre-computing connections between policies and active SAs:
Cache Invalidation:
When SAs are created, rekeyed, or deleted, the corresponding SPD cache entries must be updated. This linkage is what enables fast packet processing—instead of a full selector match, the cache provides direct SA references.
The SPD-S/SPD-I/SPD-O split is conceptual. Many implementations combine these or use different caching strategies. What matters is the behavior: policies determine actions, and active SAs are quickly accessible. How this is internally organized is implementation-dependent.
The Security Association Database stores all currently active Security Associations. Each SAD entry contains the complete parameter set we explored in Page 2, plus operational state like sequence number counters.
SAD Entry Contents:
An SAD entry contains everything needed to process packets for that SA:
| Field Category | Specific Fields | Purpose |
|---|---|---|
| Identification | SPI, Destination Address, Protocol (AH/ESP) | Unique SA identifier for lookups |
| Sequence Counter | Current sequence number, ESN high bits | Track packets sent, anti-replay |
| Anti-Replay Window | Window bitmap, right edge position | Detect replayed packets |
| AH Info | Algorithm, Key, Key Length | AH processing parameters |
| ESP Info | Encryption alg, Auth alg, Keys, IVs | ESP processing parameters |
| Lifetime State | Creation time, Byte count, Packet count | Track usage against limits |
| Mode | Tunnel or Transport | How to encapsulate/decapsulate |
| Tunnel Headers | Source/Dest for outer IP (tunnel mode) | Build outer headers |
| Path MTU | Maximum transmission unit | Fragmentation handling |
| DSCP/ECN | Differentiated services handling | QoS preservation |
| Flags | Don't Fragment, ESN enabled, etc. | Behavioral modifiers |
SAD Lookup Mechanisms:
Different lookup methods are used depending on the processing direction:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
// Conceptual SAD Entry Structure interface SADEntry { // === Identification === spi: number; // 32-bit SPI destAddress: IPAddress; // Destination IP protocol: 'AH' | 'ESP'; // Security protocol // === Sequence Number State === sequenceCounter: bigint; // 64-bit for ESN esnEnabled: boolean; // Extended Sequence Numbers sequenceOverflowAllow: boolean; // === Anti-Replay Window === antiReplayEnabled: boolean; replayWindow: { size: number; // e.g., 64, 128, 256 bitmap: bigint; // Window bitmap rightEdge: bigint; // Highest seen sequence }; // === Cryptographic Parameters === encryption: { algorithm: string; // e.g., 'AES-256-GCM' key: Uint8Array; // Encryption key salt: Uint8Array; // For GCM/CCM nonce ivMode: 'explicit' | 'implicit' | 'counter'; } | null; authentication: { algorithm: string; // e.g., 'HMAC-SHA-256-128' key: Uint8Array; // Integrity key icvLength: number; // Truncation length in bytes } | null; // === Mode and Tunnel Parameters === mode: 'tunnel' | 'transport'; tunnelHeader: { sourceAddress: IPAddress; destAddress: IPAddress; dscp: number; dontFragment: boolean; } | null; // === Lifetime Tracking === lifetime: { createdAt: Date; softTimeLimit: number; // seconds hardTimeLimit: number; // seconds softByteLimit: bigint; hardByteLimit: bigint; bytesProcessed: bigint; packetsProcessed: bigint; }; // === Linkage === spdEntryRef: SPDEntry; // Link to controlling policy peerAddress: IPAddress; // IKE peer (for rekeying)} // SAD as hash table for O(1) inbound lookupclass SecurityAssociationDatabase { private entries: Map<string, SADEntry> = new Map(); private makeKey(spi: number, dest: IPAddress, proto: string): string { return `${spi}:${dest}:${proto}`; } lookupInbound(spi: number, dest: IPAddress, proto: string): SADEntry | null { return this.entries.get(this.makeKey(spi, dest, proto)) || null; } add(entry: SADEntry): void { const key = this.makeKey(entry.spi, entry.destAddress, entry.protocol); this.entries.set(key, entry); } delete(spi: number, dest: IPAddress, proto: string): void { this.entries.delete(this.makeKey(spi, dest, proto)); }}The SPD and SAD must work together seamlessly. The SPD defines requirements; the SAD provides implementations. This coordination involves several key operations:
SA Creation Flow:
When outbound traffic matches an SPD PROTECT entry but no suitable SA exists:
Inbound Verification:
After decrypting inbound traffic, the system must verify the protection matched requirements:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
// Inbound Traffic Processing with SPD Verification function processInboundIPSec(packet: IPSecPacket): Result { // Step 1: Extract identification from outer headers const spi = packet.espHeader.spi; const destIP = packet.ipHeader.destination; const protocol = 'ESP'; // Step 2: SAD lookup const sa = sad.lookupInbound(spi, destIP, protocol); if (!sa) { log.error("No SA found for SPI", spi); return DROP; // Unknown SA } // Step 3: Anti-replay check if (sa.antiReplayEnabled) { if (!checkReplayWindow(sa, packet.espHeader.sequenceNumber)) { log.warn("Replay detected or packet too old"); return DROP; } } // Step 4: Decrypt and verify integrity const decrypted = decryptESP(packet, sa); if (!decrypted.integrityValid) { log.error("Authentication failed"); return DROP; // Tampered packet } // Step 5: Extract inner packet const innerPacket = decrypted.payload; // Step 6: SPD VERIFICATION - Critical security check! // Verify that the protection matches what policy REQUIRED const policyRequired = spd.findMatchingPolicy(innerPacket); if (policyRequired.action !== 'PROTECT') { // Traffic arrived protected but wasn't required to be // This is okay - proceed log.debug("Received protected traffic where BYPASS was acceptable"); } if (policyRequired.action === 'PROTECT') { // Verify the SA used matches policy requirements if (!policyMatches(policyRequired.saSpecification, sa)) { log.error("SA doesn't meet policy requirements"); return DROP; // Protection insufficient } } if (policyRequired.action === 'DISCARD') { // This traffic should have been blocked, not protected log.error("Received traffic that should be discarded"); return DROP; } // Step 7: Update SA state updateReplayWindow(sa, packet.espHeader.sequenceNumber); sa.lifetime.bytesProcessed += BigInt(packet.length); sa.lifetime.packetsProcessed += 1n; // Step 8: Pass decrypted packet to upper layers return ACCEPT(innerPacket);}The post-decryption SPD check prevents a critical attack: an adversary sending unprotected traffic that should have been protected. Without this check, an attacker could inject plaintext packets into traffic flows that the policy mandates must be encrypted. This is why inbound processing ALWAYS includes SPD verification.
Both SPD and SAD require ongoing maintenance to ensure correct operation. This includes handling lifetime expirations, rekeying, administrative changes, and error conditions.
SAD Maintenance Tasks:
| Operation | Trigger | Actions |
|---|---|---|
| Soft Lifetime Expiry | Time or bytes threshold reached | Signal IKE to initiate rekey; SA remains usable |
| Hard Lifetime Expiry | Absolute limit reached | Delete SA immediately; notify IKE; update SPD cache |
| Manual Deletion | Administrative command | Delete SA; update SPD cache; notify peer via IKE DELETE |
| Rekey Completion | New SA negotiated | Install new SA; mark old SA for deletion; update SPD linkage |
| Error Conditions | Decryption failure, auth failure | Log error; potentially delete SA if repeated failures |
| Peer Deletion | IKE DELETE message received | Delete indicated SA; update SPD cache |
SPD Maintenance Tasks:
| Operation | Trigger | Actions |
|---|---|---|
| Policy Addition | Administrator adds rule | Insert in SPD-S; invalidate affected SPD-I/SPD-O caches |
| Policy Deletion | Administrator removes rule | Remove from SPD-S; delete linked SAs; update caches |
| Policy Modification | Administrator changes rule | Update SPD-S; may require SA renegotiation if requirements change |
| Cache Refresh | SA creation/deletion | Update SPD-O/SPD-I links to reflect current SAs |
| Priority Reorder | Administrator changes order | Reorder SPD-S; reverify active traffic against new order |
Database modifications should be atomic to prevent race conditions. During an SPD policy change, you don't want packets being processed against a partially-updated policy. Most implementations use locking or copy-on-write strategies to ensure consistency.
While not strictly part of the SPD/SAD pair, the Peer Authorization Database (PAD) is a third database defined in RFC 4301 that controls IKE peer authentication and authorization.
PAD Purpose:
The PAD links:
This prevents a valid IKE peer from negotiating SAs for traffic it shouldn't have access to.
| Field | Description | Example |
|---|---|---|
| ID Type | Type of peer identity | Distinguished Name, Email, FQDN, IP Address |
| ID Value | Actual identity value | CN=vpn.example.com or user@example.com |
| Authentication Method | How to authenticate this peer | Pre-shared key, RSA signature, EAP |
| Authentication Data | Credentials for authentication | PSK value, certificate CA, EAP config |
| Child SA Authorization | What SAs this peer can create | Link to SPD entries or traffic selectors |
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253
# PAD Configuration Examples # Entry 1: Site-to-site VPN peer with certificate authenticationPAD_Entry { id_type: DISTINGUISHED_NAME id_value: "CN=branch-office.example.com, O=Example Corp" authentication: { method: RSA_SIGNATURE trusted_ca: "CN=Example Corp CA, O=Example Corp" cert_verification: FULL_CHAIN } child_sa_auth: { # This peer can only create SAs for branch office traffic allowed_local: 10.1.0.0/16 # HQ network allowed_remote: 10.2.0.0/16 # Branch network }} # Entry 2: Remote access VPN user with EAP authenticationPAD_Entry { id_type: RFC822_EMAIL id_value: "*@example.com" # Wildcard for all employees authentication: { method: EAP eap_type: EAP-TLS radius_server: "radius.example.com" } child_sa_auth: { # Remote users can access internal networks only allowed_local: 10.0.0.0/8 allowed_remote: ASSIGNED_ADDRESS # Whatever DHCP assigns }} # Entry 3: Generic peer with pre-shared key (less secure)PAD_Entry { id_type: IP_ADDRESS id_value: "203.0.113.1" authentication: { method: PRE_SHARED_KEY psk: "ComplexSecretKeyHere123!" } child_sa_auth: { # Only specific traffic policy spd_entries: ["Partner_VPN_Policy"] }}Many IPSec implementations don't expose a separate PAD interface. Instead, PAD-like information is embedded in IKE peer configurations. The conceptual PAD still exists—authentication credentials and child SA authorization—but the interface may differ from the RFC model.
We've explored the database architecture that underpins all IPSec operations. These databases transform abstract security requirements into concrete packet processing decisions.
What's Next:
With the database architecture understood, the next page explores the Internet Key Exchange (IKE) protocol—the automated mechanism that negotiates SAs, derives keys, and populates the SAD. IKE transforms IPSec from a manual, pre-shared-key system into a scalable, automated security infrastructure.
You now understand the SPD and SAD—the two databases that govern all IPSec packet processing. This knowledge is essential for configuring IPSec policies, debugging connectivity issues, and understanding how security decisions are made in real-time.