RADIUS Accounting View
Overview
The RADIUS_ACCOUNTING view contains session records including duration, bandwidth usage, and termination reasons.
Schema
| Column | Type | Description |
|---|---|---|
ID |
NUMBER |
Unique record identifier |
TIMESTAMP_TIMEZONE |
TIMESTAMP |
Accounting record timestamp |
CALLING_STATION_ID |
VARCHAR2 |
MAC address of endpoint |
USERNAME |
VARCHAR2 |
User identity |
NAS_IP_ADDRESS |
VARCHAR2 |
Network device IP |
NAS_PORT_ID |
VARCHAR2 |
Switch port or WLAN |
ACCT_STATUS_TYPE |
VARCHAR2 |
Start, Stop, Interim-Update |
ACCT_SESSION_ID |
VARCHAR2 |
Session identifier |
ACCT_SESSION_TIME |
NUMBER |
Session duration in seconds |
ACCT_INPUT_OCTETS |
NUMBER |
Bytes received |
ACCT_OUTPUT_OCTETS |
NUMBER |
Bytes transmitted |
ACCT_INPUT_PACKETS |
NUMBER |
Packets received |
ACCT_OUTPUT_PACKETS |
NUMBER |
Packets transmitted |
ACCT_TERMINATE_CAUSE |
VARCHAR2 |
Disconnect reason |
FRAMED_IP_ADDRESS |
VARCHAR2 |
Assigned IP address |
SERVICE_TYPE |
VARCHAR2 |
Service type (Framed, etc.) |
ISE_NODE |
VARCHAR2 |
Processing PSN hostname |
Session Queries
Active Sessions
# Currently active sessions (no Stop record yet)
netapi ise dc --format json query "
SELECT
s.CALLING_STATION_ID as mac,
s.USERNAME as identity,
s.NAS_IP_ADDRESS as nas,
s.NAS_PORT_ID as port,
s.FRAMED_IP_ADDRESS as ip,
TO_CHAR(s.TIMESTAMP_TIMEZONE, 'HH24:MI') as started,
ROUND((SYSDATE - s.TIMESTAMP_TIMEZONE) * 24 * 60, 0) as mins
FROM RADIUS_ACCOUNTING s
WHERE s.ACCT_STATUS_TYPE = 'Start'
AND s.TIMESTAMP_TIMEZONE > SYSDATE - 1
AND NOT EXISTS (
SELECT 1 FROM RADIUS_ACCOUNTING e
WHERE e.ACCT_SESSION_ID = s.ACCT_SESSION_ID
AND e.ACCT_STATUS_TYPE = 'Stop'
)
ORDER BY s.TIMESTAMP_TIMEZONE DESC
FETCH FIRST 50 ROWS ONLY
" | jq -r '
"\u001b[1m MAC Address │ Identity │ NAS │ Port │ IP │ Started\u001b[0m",
"────────────────────┼───────────────────────────┼──────────────┼──────────────────┼──────────────┼─────────────",
(.[] |
"\u001b[32m●\u001b[0m " +
"\u001b[36m" + (.mac // "N/A") + "\u001b[0m │ " +
(((.identity // "") + " ") | .[0:25]) + " │ " +
(((.nas // "") + " ") | .[0:12]) + " │ " +
"\u001b[35m" + (((.port // "") + " ") | .[0:16]) + "\u001b[0m │ " +
(((.ip // "N/A") + " ") | .[0:12]) + " │ " +
(.started // "") + " (" + ((.mins // 0) | tostring) + "m)"
)
'
Session for Specific MAC
# Session history for specific MAC
MAC="C8:5B:76:C6:59:62"
netapi ise dc --format json query "
SELECT
TO_CHAR(TIMESTAMP_TIMEZONE, 'YYYY-MM-DD HH24:MI') as timestamp,
ACCT_STATUS_TYPE as type,
ROUND(ACCT_SESSION_TIME / 60, 1) as mins,
ROUND((ACCT_INPUT_OCTETS + ACCT_OUTPUT_OCTETS) / 1024 / 1024, 2) as mb,
ACCT_TERMINATE_CAUSE as term_cause
FROM RADIUS_ACCOUNTING
WHERE CALLING_STATION_ID = '${MAC}'
AND TIMESTAMP_TIMEZONE > SYSDATE - 7
ORDER BY TIMESTAMP_TIMEZONE DESC
FETCH FIRST 50 ROWS ONLY
" | jq -r '
.[] |
(if .type == "Start" then "\u001b[32m▶\u001b[0m"
elif .type == "Stop" then "\u001b[31m■\u001b[0m"
else "\u001b[33m◆\u001b[0m" end) +
" " + .timestamp + " │ " +
(if .mins then (.mins | tostring) + " min" else "---" end) + " │ " +
(if .mb then (.mb | tostring) + " MB" else "---" end) + " │ " +
(.term_cause // "---")
'
Duration Analysis
Session Duration Distribution
# Session duration statistics with histogram
netapi ise dc --format json query "
SELECT
CASE
WHEN ACCT_SESSION_TIME < 60 THEN '< 1 min'
WHEN ACCT_SESSION_TIME < 300 THEN '1-5 min'
WHEN ACCT_SESSION_TIME < 900 THEN '5-15 min'
WHEN ACCT_SESSION_TIME < 3600 THEN '15-60 min'
WHEN ACCT_SESSION_TIME < 14400 THEN '1-4 hrs'
WHEN ACCT_SESSION_TIME < 28800 THEN '4-8 hrs'
ELSE '8+ hrs'
END as duration_bucket,
COUNT(*) as sessions,
ROUND(AVG(ACCT_INPUT_OCTETS + ACCT_OUTPUT_OCTETS) / 1024 / 1024, 2) as avg_mb
FROM RADIUS_ACCOUNTING
WHERE ACCT_STATUS_TYPE = 'Stop'
AND TIMESTAMP_TIMEZONE > SYSDATE - 7
GROUP BY CASE
WHEN ACCT_SESSION_TIME < 60 THEN '< 1 min'
WHEN ACCT_SESSION_TIME < 300 THEN '1-5 min'
WHEN ACCT_SESSION_TIME < 900 THEN '5-15 min'
WHEN ACCT_SESSION_TIME < 3600 THEN '15-60 min'
WHEN ACCT_SESSION_TIME < 14400 THEN '1-4 hrs'
WHEN ACCT_SESSION_TIME < 28800 THEN '4-8 hrs'
ELSE '8+ hrs'
END
ORDER BY MIN(ACCT_SESSION_TIME)
" | jq -r '
"\u001b[1mDuration │ │ Sessions │ Avg MB\u001b[0m",
"───────────┼──────────────────────────────┼────────────┼───────",
((map(.sessions) | max) as $max |
.[] |
(((.duration_bucket // "") + " ") | .[0:10]) + " │" +
(((.sessions // 0) / ($max | if . == 0 then 1 else . end) * 30) | floor | . as $w |
"\u001b[42m" + (" " * $w) + "\u001b[0m" + (" " * (30 - $w))) +
"│ " + ((.sessions // 0) | tostring) + " │ " +
((.avg_mb // 0) | tostring)
)
'
Termination Cause Analysis
# Session termination cause analysis
netapi ise dc --format json query "
SELECT
ACCT_TERMINATE_CAUSE as reason,
COUNT(*) as count,
ROUND(AVG(ACCT_SESSION_TIME) / 60, 1) as avg_mins,
ROUND(SUM(ACCT_INPUT_OCTETS + ACCT_OUTPUT_OCTETS) / 1024 / 1024, 0) as total_mb
FROM RADIUS_ACCOUNTING
WHERE ACCT_STATUS_TYPE = 'Stop'
AND TIMESTAMP_TIMEZONE > SYSDATE - 1
AND ACCT_TERMINATE_CAUSE IS NOT NULL
GROUP BY ACCT_TERMINATE_CAUSE
ORDER BY count DESC
" | jq -r '
"\u001b[1m Termination Cause │ Sessions │ Avg Duration │ Total MB\u001b[0m",
"────────────────────────────┼────────────┼──────────────┼──────────",
(.[] |
(if .reason == "User-Request" then "\u001b[32m●\u001b[0m"
elif .reason == "Session-Timeout" then "\u001b[33m●\u001b[0m"
elif .reason == "Admin-Reset" then "\u001b[34m●\u001b[0m"
elif .reason == "Lost-Carrier" then "\u001b[31m●\u001b[0m"
else "\u001b[37m●\u001b[0m" end) +
" " + (((.reason // "") + " ") | .[0:25]) +
" │ " + ((.count // 0) | tostring) + " │ " +
((.avg_mins // 0) | tostring) + " min │ " +
((.total_mb // 0) | tostring)
)
'
Bandwidth Analysis
Top Bandwidth Users
# Top bandwidth consumers (last 7 days)
netapi ise dc --format json query "
SELECT
USERNAME as identity,
COUNT(*) as sessions,
ROUND(SUM(ACCT_INPUT_OCTETS + ACCT_OUTPUT_OCTETS) / 1024 / 1024 / 1024, 2) as total_gb,
ROUND(SUM(ACCT_SESSION_TIME) / 3600, 1) as total_hours,
COUNT(DISTINCT CALLING_STATION_ID) as devices
FROM RADIUS_ACCOUNTING
WHERE ACCT_STATUS_TYPE = 'Stop'
AND TIMESTAMP_TIMEZONE > SYSDATE - 7
AND USERNAME IS NOT NULL
GROUP BY USERNAME
ORDER BY total_gb DESC
FETCH FIRST 15 ROWS ONLY
" | jq -r '
"┌────────────────────────────────┬──────────┬───────────┬────────────┬─────────┐",
"│ Identity │ Sessions │ Total GB │ Total Hrs │ Devices │",
"├────────────────────────────────┼──────────┼───────────┼────────────┼─────────┤",
(.[] |
"│ \u001b[33m" + (((.identity // "") + " ") | .[0:30]) + "\u001b[0m │" +
((" " + ((.sessions // 0)|tostring))[-8:]) + " │" +
((" " + ((.total_gb // 0)|tostring))[-9:]) + " │" +
((" " + ((.total_hours // 0)|tostring))[-10:]) + " │" +
((" " + ((.devices // 0)|tostring))[-7:]) + " │"
),
"└────────────────────────────────┴──────────┴───────────┴────────────┴─────────┘"
'
Traffic by Network Device
# Traffic per network device
netapi ise dc --format json query "
SELECT
NAS_IP_ADDRESS as nas,
COUNT(*) as sessions,
ROUND(SUM(ACCT_INPUT_OCTETS) / 1024 / 1024 / 1024, 2) as rx_gb,
ROUND(SUM(ACCT_OUTPUT_OCTETS) / 1024 / 1024 / 1024, 2) as tx_gb,
COUNT(DISTINCT CALLING_STATION_ID) as unique_macs
FROM RADIUS_ACCOUNTING
WHERE ACCT_STATUS_TYPE = 'Stop'
AND TIMESTAMP_TIMEZONE > SYSDATE - 1
GROUP BY NAS_IP_ADDRESS
ORDER BY (SUM(ACCT_INPUT_OCTETS) + SUM(ACCT_OUTPUT_OCTETS)) DESC
FETCH FIRST 10 ROWS ONLY
" | jq -r '
.[] |
"\u001b[36m" + .nas + "\u001b[0m │ " +
(.sessions | tostring) + " sessions │ " +
"\u001b[32m↓\u001b[0m " + (.rx_gb | tostring) + " GB │ " +
"\u001b[31m↑\u001b[0m " + (.tx_gb | tostring) + " GB │ " +
(.unique_macs | tostring) + " MACs"
'
Daily Traffic Trend
# Daily traffic trend with bar chart
netapi ise dc --format json query "
SELECT
TO_CHAR(TRUNC(TIMESTAMP_TIMEZONE), 'Dy DD-Mon') as day,
COUNT(*) as sessions,
ROUND(SUM(ACCT_INPUT_OCTETS + ACCT_OUTPUT_OCTETS) / 1024 / 1024 / 1024, 2) as total_gb
FROM RADIUS_ACCOUNTING
WHERE ACCT_STATUS_TYPE = 'Stop'
AND TIMESTAMP_TIMEZONE > SYSDATE - 7
GROUP BY TRUNC(TIMESTAMP_TIMEZONE), TO_CHAR(TRUNC(TIMESTAMP_TIMEZONE), 'Dy DD-Mon')
ORDER BY TRUNC(TIMESTAMP_TIMEZONE)
" | jq -r '
(map(.total_gb) | max) as $max |
.[] |
.day + " │" +
((.total_gb / $max * 30) | floor | . as $w |
"\u001b[46m" + (" " * $w) + "\u001b[0m" + (" " * (30 - $w))) +
"│ " + (.total_gb | tostring) + " GB │ " + (.sessions | tostring) + " sessions"
'
Advanced Analysis
Interim Update Analysis
# Interim update frequency analysis (CoA monitoring)
netapi ise dc --format json query "
SELECT
ACCT_SESSION_ID,
CALLING_STATION_ID as mac,
COUNT(*) as update_count,
MIN(TO_CHAR(TIMESTAMP_TIMEZONE, 'HH24:MI')) as first_update,
MAX(TO_CHAR(TIMESTAMP_TIMEZONE, 'HH24:MI')) as last_update,
ROUND(SUM(ACCT_INPUT_OCTETS + ACCT_OUTPUT_OCTETS) / 1024 / 1024, 2) as total_mb
FROM RADIUS_ACCOUNTING
WHERE ACCT_STATUS_TYPE = 'Interim-Update'
AND TIMESTAMP_TIMEZONE > SYSDATE - 1
GROUP BY ACCT_SESSION_ID, CALLING_STATION_ID
HAVING COUNT(*) > 5
ORDER BY update_count DESC
FETCH FIRST 20 ROWS ONLY
" | jq -r '
.[] |
"\u001b[36m" + .mac + "\u001b[0m │ " +
"\u001b[33m" + (.update_count | tostring) + " updates\u001b[0m │ " +
.first_update + "-" + .last_update + " │ " +
(.total_mb | tostring) + " MB"
'
Export
CSV Export
# Export accounting data to CSV
# Note: netapi dc doesn't have --format csv, use JSON + jq conversion
netapi ise dc --format json query "
SELECT
TO_CHAR(TIMESTAMP_TIMEZONE, 'YYYY-MM-DD HH24:MI:SS') as timestamp,
CALLING_STATION_ID as mac,
USERNAME as identity,
NAS_IP_ADDRESS as nas,
NAS_PORT_ID as port,
ACCT_STATUS_TYPE as status_type,
ACCT_SESSION_ID as session_id,
ACCT_SESSION_TIME as duration_sec,
ACCT_INPUT_OCTETS as rx_bytes,
ACCT_OUTPUT_OCTETS as tx_bytes,
ACCT_TERMINATE_CAUSE as term_cause,
FRAMED_IP_ADDRESS as ip
FROM RADIUS_ACCOUNTING
WHERE TIMESTAMP_TIMEZONE > SYSDATE - 1
ORDER BY TIMESTAMP_TIMEZONE DESC
" | jq -r '
(.[0] | keys) as $k |
($k | @csv),
(.[] | [.[$k[]]] | @csv)
' > /tmp/radius-acct-$(date +%Y%m%d).csv
echo "Exported $(wc -l < /tmp/radius-acct-$(date +%Y%m%d).csv) records"