Diameter Security
OmniDRA provides a comprehensive suite of Diameter security modules. They align with GSMA FS.19 (Diameter Interconnect Security, v10.0) and GSMA FS.21 (Interconnect Signalling Security Recommendations, v12.0). These modules protect the network against signalling-level attacks on Diameter interconnect interfaces.
You configure each module independently. You can enable or disable each module without an effect on the others. Each module emits its own telemetry events for monitoring and alerting.
Architecture Overview
Processing Order
Inbound Diameter requests pass through the security modules in the following order. If a stage rejects a message, the DRA never forwards it to the later stages.
| Order | Module | Direction | Purpose |
|---|---|---|---|
| 1 | Rate Limiter | Inbound | Volumetric flood protection |
| 2 | Diameter Firewall | Inbound | FS.19 protocol and content filtering |
| 3 | AVP Sanitizer | Inbound | AVP validation and stripping |
| 4 | Routing Engine | - | Standard Diameter routing |
| 5 | Topology Hiding | Outbound | Network topology concealment |
Actions
All security modules support configurable actions when they detect a violation. You configure the actions per-module, and per-category for the Diameter Firewall.
| Action | Behaviour | Diameter Result |
|---|---|---|
{:error, 3002} | Respond with a Diameter error answer | DIAMETER_UNABLE_TO_DELIVER |
{:error, 3004} | Respond with a Diameter error answer | DIAMETER_TOO_BUSY |
{:error, 3007} | Respond with a Diameter error answer | DIAMETER_APPLICATION_UNSUPPORTED |
:drop | Discard the message | No response sent |
:log_only | Log the violation but allow the message through | Message continues |
Diameter Firewall
The Diameter Firewall implements the four filtering layers defined in GSMA FS.19 Section 3.3. It also implements the protocol-agnostic packet categorisation from FS.21 Section 7. The firewall evaluates messages through each layer in order. A violation at any layer stops further processing.
Low-Layer Format Filtering
GSMA Reference: FS.19 Section 3.3.4, FS.21 Section 7.3.1
Low-layer filtering detects protocol-level violations and basic anti-spoofing attempts. It does not need to understand upper-layer application semantics. This layer catches malformed messages before they reach deeper inspection.
Checks performed:
| Check | FS.19 Reference | Description |
|---|---|---|
| AVP Duplication | Section 3.3.4, 4.8.1 | Detects duplicate instances of AVPs that must appear at most once (for example Origin-Host, Origin-Realm). Prevents AVP doubling evasion attacks. |
| Session-Id Ordering | Section 3.3.4 | Confirms that Session-Id (AVP 263) is the first AVP in the message, per RFC 6733 Section 8.8. |
| Destination-Host in Answers | Section 3.3.4 | Rejects an answer message that contains a Destination-Host AVP. This is a protocol violation that may indicate filter evasion. |
| Mandatory AVP Validation | Section 3.3.5 | Confirms that the required AVPs are present for each command code. For example, a ULR must contain User-Name and Visited-PLMN-Id per 3GPP TS 29.272 Section 7.2.3. |
low_layer: %{
enabled: true,
action: {:error, 3002},
# AVP codes that must not appear more than once (FS.19 Section 3.3.4)
# 264 = Origin-Host, 296 = Origin-Realm, 283 = Destination-Realm, 293 = Destination-Host
single_instance_avps: [264, 296, 283, 293],
# Session-Id must be the first AVP (RFC 6733 Section 8.8)
enforce_session_id_first: true,
# Answer messages must not contain Destination-Host (FS.19 Section 3.3.4)
reject_destination_host_in_answers: true,
# Mandatory AVPs per command code (3GPP TS 29.272)
# 1 = User-Name (IMSI), 1407 = Visited-PLMN-Id, 264 = Origin-Host, 296 = Origin-Realm
mandatory_avps: %{
316 => [1, 1407, 264, 296],
318 => [1, 1407, 264, 296]
}
}
Category 1 - Interface-Unauthorised Packet Filtering
GSMA Reference: FS.19 Section 3.3.5, FS.21 Section 7.2.1
Category 1 filtering accepts only authorised Diameter Application IDs and Command Codes on each interconnect interface. This prevents external access to internal-only interfaces. For example, it blocks Sh commands over an S6a interface. It also enforces that a roaming partner sends only the message types that its roaming agreement covers.
The whitelist approach follows the FS.19 recommendation: block all Diameter messages except those that a given interface requires.
You can configure whitelists globally, which applies them to all peers, or per-peer. A per-peer whitelist lets different roaming partners have different permitted message sets.
category_1: %{
enabled: true,
action: {:error, 3007},
whitelists: %{
# Per-peer whitelist - restrict this partner to ULR and AIR only (FS.19 Section 3.3.5)
"restricted-partner.roaming.com" => %{
16_777_251 => [316, 318]
},
# Default whitelist - standard S6a commands permitted for all other peers
all: %{
# S6a/S6d (FS.19 Section 3.3.5, Table 2)
16_777_251 => [316, 317, 318, 319, 320, 321, 323],
# S13 - ME Identity Check (FS.19 Section 3.3.5)
16_777_252 => [324],
# S6c - SMS via HSS (FS.19 Section 3.3.5.1)
16_777_312 => [8388647, 8388648],
# SGd - SMS via MME (FS.19 Section 3.3.5.2)
16_777_313 => [8388645, 8388646]
}
}
}
Common S6a/S6d Whitelist:
| Command Code | Name | Direction | Reference |
|---|---|---|---|
| 316 | Update-Location-Request/Answer | MME → HSS | 3GPP TS 29.272 §7.2.3 |
| 317 | Cancel-Location-Request/Answer | HSS → MME | 3GPP TS 29.272 §7.2.7 |
| 318 | Authentication-Information-Request/Answer | MME → HSS | 3GPP TS 29.272 §7.2.5 |
| 319 | Insert-Subscriber-Data-Request/Answer | HSS → MME | 3GPP TS 29.272 §7.2.9 |
| 320 | Delete-Subscriber-Data-Request/Answer | HSS → MME | 3GPP TS 29.272 §7.2.11 |
| 321 | Purge-UE-Request/Answer | MME → HSS | 3GPP TS 29.272 §7.2.13 |
| 323 | Notify-Request/Answer | MME → HSS | 3GPP TS 29.272 §7.2.15 |
Common Application IDs for roaming interconnect (FS.19 Section 3.3.5):
| Application ID | Interface | Reference |
|---|---|---|
| 16777251 | S6a/S6d | 3GPP TS 29.272 |
| 16777252 | S13 | 3GPP TS 29.272 |
| 16777312 | S6c | 3GPP TS 29.338 |
| 16777313 | SGd | 3GPP TS 29.338 |
| 16777255 | SLg | 3GPP TS 29.172 |
| 16777267 | S9 | 3GPP TS 29.215 |
Category 2 - Home-Network Packet Filtering
GSMA Reference: FS.19 Section 3.3.6, FS.21 Section 7.2.2
Category 2 filtering protects home subscribers against messages that target them from the interconnect. The firewall inspects messages on protected command codes for subscriber identity (IMSI in the User-Name AVP, MSISDN in AVP 701). If the identity matches a home subscriber prefix, the firewall rejects the message. Legitimate traffic for home subscribers originates from within the home network, not from external peers.
This prevents attacks where an external entity sends location updates, subscriber data requests, or authentication queries that target the operator's own subscribers.
# Top-level: define home subscriber prefixes
home_imsi_prefixes: ["31338", "31339"],
home_msisdn_prefixes: ["+1313"],
category_2: %{
enabled: true,
action: {:error, 3002},
# S6a command codes that carry subscriber identity (FS.19 Section 3.3.6, Table 12)
protected_command_codes: [316, 317, 318, 319, 320, 321, 323]
}
Category 3 - Plausible-Network Packet Filtering
GSMA Reference: FS.19 Section 3.3.7, FS.21 Section 7.2.3
Category 3 filtering detects implausible location changes. It tracks the last-seen network for each subscriber (IMSI). When an Update-Location-Request arrives from a different visited network than the previous one, the firewall compares the elapsed time against a configurable threshold. A realm change that occurs faster than is physically possible indicates a potential attack, such as a spoofed location update.
This module maintains stateful per-IMSI tracking in an ETS table. It cleans up stale entries automatically. It removes entries older than 24 hours periodically.
Checks performed:
| Check | FS.19 Reference | Description |
|---|---|---|
| Previous Location Check | Section 3.3.7.1 | Compares the Origin-Realm of the current ULR with the last-seen Origin-Realm for that IMSI |
| Velocity/Time Check | Section 3.3.7.2 | Flags a realm change that occurs within a configurable minimum time window |
category_3: %{
enabled: true,
# Start with log_only to observe patterns before enforcing (FS.19 Section 3.3.7)
action: :log_only,
# Maximum plausible velocity in km/h (FS.19 Section 3.3.7.2)
max_velocity_kmh: 1200,
# Minimum seconds between ULRs from different realms for the same IMSI
min_time_between_updates_seconds: 2
}
Configuration
Enable the Diameter Firewall at the top level. Configure each filtering layer independently. Combine the config snippets from each category section above under a single module_diameter_firewall key:
config :dra,
module_diameter_firewall: %{
enabled: true,
home_imsi_prefixes: ["31338", "31339"],
home_msisdn_prefixes: ["+1313"],
low_layer: %{ ... }, # See Low-Layer Format Filtering above
category_1: %{ ... }, # See Category 1 above
category_2: %{ ... }, # See Category 2 above
category_3: %{ ... } # See Category 3 above
}
Top-Level Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
enabled | Boolean | Yes | false | Enable or disable the entire Diameter Firewall module. When false, all messages pass through without inspection. |
children | List | No | [] | Processes that the DRA supervisor starts when the module is enabled. For the firewall, set this to [DRA.Router.Modules.DiameterFirewall.LocationTracker]. Category 3 stateful (velocity) tracking requires it. |
home_imsi_prefixes | List | No | [] | List of IMSI prefix strings that identify home subscribers. Category 2 filtering uses it. Example: ["31338", "31339"]. |
home_msisdn_prefixes | List | No | [] | List of MSISDN prefix strings that identify home subscribers. Category 2 filtering uses it. Example: ["+1313"]. |
Low-Layer Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
enabled | Boolean | No | true | Enable or disable low-layer format filtering. |
action | Action | No | {:error, 3002} | Action to take when the firewall detects a low-layer violation. |
single_instance_avps | List | No | [264, 296, 283, 293] | AVP codes that must not appear more than once in a message. The default covers Origin-Host (264), Origin-Realm (296), Destination-Realm (283), and Destination-Host (293). |
enforce_session_id_first | Boolean | No | true | Reject a message where Session-Id is not the first AVP. |
reject_destination_host_in_answers | Boolean | No | true | Reject an answer message that contains a Destination-Host AVP. |
mandatory_avps | Map | No | %{} | Map of command code to list of required AVP codes. Example: %{316 => [1, 1407, 264, 296]} requires User-Name, Visited-PLMN-Id, Origin-Host, and Origin-Realm in ULR messages. |
Category 1 Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
enabled | Boolean | No | true | Enable or disable Category 1 filtering. |
action | Action | No | {:error, 3007} | Action to take when the firewall detects a Category 1 violation. The default responds with DIAMETER_APPLICATION_UNSUPPORTED. |
whitelists | Map | Yes | - | Map of peer hostname (or :all) to permitted Application ID / Command Code combinations. The :all key provides a default whitelist. Peer-specific entries take priority. |
Category 2 Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
enabled | Boolean | No | true | Enable or disable Category 2 filtering. |
action | Action | No | {:error, 3002} | Action to take when a message targets a home subscriber from the interconnect. |
protected_command_codes | List | No | [] | Command codes that trigger home subscriber identity checks. Typically the S6a command codes that carry subscriber identity. |
Category 3 Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
enabled | Boolean | No | false | Enable or disable Category 3 filtering. It requires the LocationTracker process. The supervisor starts this process automatically when the module is enabled. |
action | Action | No | :log_only | Action to take when a location plausibility check fails. Start with :log_only to tune the thresholds before you enforce. |
max_velocity_kmh | Integer | No | 1200 | Maximum plausible velocity in km/h. Reserved for future geo-distance calculations. |
min_time_between_updates_seconds | Integer | No | 2 | Minimum time in seconds between location updates from different realms for the same IMSI. The firewall flags a realm change that is faster than this. |
Telemetry Events
| Event | Description |
|---|---|
[:diameter, :firewall, :low_layer, :block] | The firewall detected and blocked a low-layer format violation |
[:diameter, :firewall, :category_1, :block] | The firewall detected and blocked a Category 1 violation |
[:diameter, :firewall, :category_2, :block] | The firewall detected and blocked a Category 2 violation |
[:diameter, :firewall, :category_3, :block] | The firewall detected and blocked a Category 3 violation |
[:diameter, :firewall, :pass, :count] | The message passed all firewall checks |
All events include this metadata: origin_host, application_id, command_code, application_name, command_name, and reason.
Topology Hiding
GSMA Reference: FS.19 Section 2.4, Section 3.4; FS.21 Section 3.6
The Topology Hiding module prevents exposure of the internal network topology to external peers. When Diameter messages traverse multiple internal nodes, they accumulate Route-Record AVPs. They also carry Origin-Host/Origin-Realm values that reveal internal hostnames, network structure, and node counts. An attacker can use this information to map the internal network for targeted attacks.
Topology hiding operates on the outbound path. It runs after the DRA makes routing decisions and before it forwards messages to the destination peer.
Features
| Feature | FS.19 Reference | Description |
|---|---|---|
| Route-Record Stripping | Section 2.4 | Removes all Route-Record AVPs (282) that reveal internal routing paths and node hostnames |
| Origin-Host Rewriting | Section 3.4 | Replaces the Origin-Host AVP with the DRA's own identity (or a custom value) on answer messages |
| Origin-Realm Rewriting | Section 3.4 | Optionally replaces the Origin-Realm AVP to hide the internal realm structure |
| Per-Peer Control | - | Apply topology hiding selectively, only to external peers or to all peers |
module_topology_hiding: %{
enabled: true,
# Strip Route-Record AVPs revealing internal paths (FS.19 Section 2.4)
strip_route_records: true,
# Rewrite Origin-Host on answers to hide internal node names (FS.19 Section 3.4)
rewrite_origin_host: %{enabled: true, replacement: :self},
# Optionally hide internal realm structure
rewrite_origin_realm: %{enabled: false, replacement: :self},
# Apply to all external peers, or list specific hostnames
external_peers: :all
}
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
enabled | Boolean | Yes | false | Enable or disable the Topology Hiding module. |
strip_route_records | Boolean | No | true | Remove all Route-Record AVPs (code 282) from messages before the DRA forwards them to external peers. |
rewrite_origin_host | Map | No | See below | Controls Origin-Host rewriting on answer messages. |
rewrite_origin_realm | Map | No | See below | Controls Origin-Realm rewriting on answer messages. |
external_peers | :all or List | No | :all | Peers that the module treats as external. The module applies topology hiding only to messages that are destined for these peers. Use :all for interconnect deployments. Use a list of hostnames for selective application. |
Rewrite Parameters (apply to both rewrite_origin_host and rewrite_origin_realm):
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
enabled | Boolean | No | varies | Enable rewriting for this AVP. Default is true for Origin-Host, false for Origin-Realm. |
replacement | :self or String | No | :self | Replacement value. :self uses the DRA's own identity from the diameter config (host.realm). The module uses a string value as-is. |
Telemetry Events
| Event | Description |
|---|---|
[:diameter, :topology_hiding, :route_record, :stripped] | The module removed Route-Record AVPs. The measurement includes the count of stripped AVPs. |
[:diameter, :topology_hiding, :origin_host, :rewritten] | The module rewrote the Origin-Host AVP |
[:diameter, :topology_hiding, :origin_realm, :rewritten] | The module rewrote the Origin-Realm AVP |
Rate Limiter
GSMA Reference: FS.19 Section 3.4 (Availability / DoS Protection)
The Rate Limiter enforces per-peer message rate limits. It protects against volumetric attacks and message flooding. It operates as the first security check in the pipeline, before any message parsing or content inspection. This sheds excess load as early as possible.
The Rate Limiter tracks rate limits per-peer with a sliding window counter. Each peer has an independent counter that resets every second.
module_rate_limiter: %{
enabled: true,
# Default limit for all peers (FS.19 Section 3.4)
default_max_requests_per_second: 1000,
default_action: {:error, 3004},
# Per-peer overrides
peer_limits: %{
"high-volume-partner.roaming.com" => %{max_requests_per_second: 5000, action: {:error, 3004}},
"restricted-peer.roaming.com" => %{max_requests_per_second: 100, action: :drop}
}
}
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
enabled | Boolean | Yes | false | Enable or disable the Rate Limiter module. |
children | List | No | [] | Processes that the DRA supervisor starts when enabled. Set this to [DRA.Router.Modules.RateLimiter] so the per-peer rate-counter process runs. |
default_max_requests_per_second | Integer | No | 1000 | Default maximum requests per second from any single peer. |
default_action | Action | No | {:error, 3004} | Default action when a peer exceeds its rate limit. {:error, 3004} responds with DIAMETER_TOO_BUSY. |
peer_limits | Map | No | %{} | Map of peer hostname to override configuration. Peers not listed use the defaults. |
Per-Peer Override Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
max_requests_per_second | Integer | No | Inherits default | Maximum requests per second for this specific peer. |
action | Action | No | Inherits default | Action when this peer exceeds its rate limit. |
Telemetry Events
| Event | Description |
|---|---|
[:diameter, :rate_limiter, :throttled] | The module rate limited a message. The measurements include current_rate and limit. |
[:diameter, :rate_limiter, :allowed] | A message was within the rate limits. |
AVP Sanitizer
GSMA Reference: FS.19 Section 3.3.4, 4.8.1, 4.8.2; FS.21 Section 3.6
The AVP Sanitizer validates and sanitizes Diameter AVPs at the interconnect boundary. It addresses the FS.21 Section 3.6 recommendations. These recommendations handle protocol-level manipulation attacks that operate below the level of the FS.19 filtering categories.
Features
| Feature | GSMA Reference | Description |
|---|---|---|
| Unknown Vendor AVP Stripping | FS.21 Section 3.6 | Removes AVPs from non-whitelisted vendors. Prevents injection of proprietary AVPs that may trigger unintended behaviour in backend nodes. |
| Grouped AVP Nesting Depth | FS.21 Section 3.6 | Enforces a maximum nesting depth for grouped AVPs. Prevents stack overflow attacks that use deeply nested AVP structures. |
module_avp_sanitizer: %{
enabled: true,
action: {:error, 3002},
# Only allow standard vendor AVPs on interconnect (FS.21 Section 3.6)
# 0 = IETF, 10415 = 3GPP, 13019 = ETSI, 5535 = 3GPP2
allowed_vendor_ids: [0, 10415, 13019, 5535],
strip_unknown_vendor_avps: true,
# Prevent stack overflow via deeply nested grouped AVPs (FS.21 Section 3.6)
max_avp_nesting_depth: 10
}
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
enabled | Boolean | Yes | false | Enable or disable the AVP Sanitizer module. |
action | Action | No | {:error, 3002} | Action to take when the module detects a nesting depth violation. Vendor stripping removes the offending AVPs and does not block the message. |
allowed_vendor_ids | List | No | [0, 10415, 13019, 5535] | List of vendor IDs permitted on interconnect. The module strips AVPs from other vendors. |
strip_unknown_vendor_avps | Boolean | No | true | Enable stripping of AVPs from vendors that are not in the allowed_vendor_ids list. |
max_avp_nesting_depth | Integer | No | 10 | Maximum permitted depth of grouped AVP nesting. The configured action applies to a message that exceeds this depth. |
Common Vendor IDs:
| Vendor ID | Organisation | Notes |
|---|---|---|
| 0 | IETF | Standard Diameter AVPs defined in RFCs |
| 10415 | 3GPP | All 3GPP-defined AVPs (S6a, Gx, Rx, etc.) |
| 13019 | ETSI | ETSI-defined AVPs |
| 5535 | 3GPP2 | 3GPP2-defined AVPs (CDMA interworking) |
Telemetry Events
| Event | Description |
|---|---|
[:diameter, :avp_sanitizer, :unknown_vendor, :stripped] | The module stripped one or more AVPs from unknown vendors |
[:diameter, :avp_sanitizer, :nesting_depth, :violation] | A message exceeded the maximum AVP nesting depth |
[:diameter, :avp_sanitizer, :pass, :count] | The message passed all sanitization checks |
Deployment Recommendations
Recommended Enablement Order
When you deploy the security modules for the first time, enable them one at a time. This avoids disruption to live traffic:
- Rate Limiter - Start with generous limits and monitor traffic patterns. Tighten the limits after you understand the baseline rates.
- AVP Sanitizer - Low risk of false positives. Enable the stripping and nesting checks.
- Diameter Firewall (Category 1) - Define whitelists based on roaming agreements. Start with known-good Application ID / Command Code combinations.
- Diameter Firewall (Low-Layer) - Enable the protocol conformance checks.
- Diameter Firewall (Category 2) - Configure the home IMSI/MSISDN prefixes.
- Topology Hiding - Enable Route-Record stripping first, then Origin-Host rewriting.
- Diameter Firewall (Category 3) - Enable it with the
:log_onlyaction. Observe location patterns before you enforce.
Defence in Depth
These modules implement the defence in depth principle from FS.19 Section 3.4. Each layer addresses a different class of attack:
| Attack Class | Primary Defence | Secondary Defence |
|---|---|---|
| Volumetric DoS | Rate Limiter | Diameter Firewall (all categories) |
| Interface Abuse | Category 1 | AVP Sanitizer |
| Home Subscriber Targeting | Category 2 | Category 1 (interface restriction) |
| Location Spoofing | Category 3 | Category 2 (home sub check) |
| Topology Discovery | Topology Hiding | - |
| AVP Injection | AVP Sanitizer | Low-Layer Filtering |
| Protocol Evasion (AVP Doubling) | Low-Layer Filtering | AVP Sanitizer |
GSMA Document Cross-Reference
| Module / Feature | FS.19 v10.0 | FS.21 v12.0 |
|---|---|---|
| Low-Layer Format Filtering | Section 3.3.4 | Section 7.3.1 |
| Category 1 Filtering | Section 3.3.5, Annex B.3.3 | Section 7.2.1 |
| Category 2 Filtering | Section 3.3.6, Annex B.3.4 | Section 7.2.2, Section 16 |
| Category 3 Filtering | Section 3.3.7, Annex B.3.5 | Section 7.2.3 |
| Topology Hiding | Section 2.4, Section 3.4 | Section 3.6 |
| Rate Limiting | Section 3.4 | - |
| AVP Sanitization | Section 4.8.1, 4.8.2 | Section 3.6 |
| Defence in Depth | Section 3.4 | Section 3.15 |
| Filtering Categories (Protocol-Agnostic) | Annex A | Section 7 |