Complete Guide for RTNetlink Interface Query System
📋 Overview
This tool provides comprehensive network interface monitoring and analysis using Linux RTNetlink and Generic Netlink protocols. It leverages CFFI (C Foreign Function Interface) to directly interface with kernel networking subsystems, offering detailed insights into network configurations, addresses, and specialized interface types.
Key Capabilities:
Real-time network interface status monitoring
Complete IP address information including IPv6 DAD states
Bridge configuration and STP details
WireGuard VPN interface monitoring via Generic Netlink
VLAN, veth, and GENEVE virtual interface support
Hardware offload capabilities detection
DPLL (Digital Phase-Locked Loop) pin synchronization info
Why This Tool?
Traditional tools like ip and ifconfig provide basic interface information, but this tool offers:
Programmatic access: JSON output for automation and integration
Deep inspection: Access to all kernel-provided interface attributes
WireGuard insights: Detailed peer information without external tools
Address readiness: IPv6 DAD state and address operational status
Bridge topology: Complete STP and port state information
⚠️ Python 3.12+ Notice:
Python 3.12 and higher require setuptools to be explicitly installed for CFFI to work properly. The script will check for this and provide a clear error message if it's missing.
File Setup
Save the script as device_info.py
Make it executable:
$ chmod +x device_info.py
Verify it runs:
$ sudo python3 device_info.py --help
🚀 Usage
Basic Usage Patterns
1. Full JSON Output (Default)
Get complete interface information in JSON format:
$ sudo python3 device_info.py
This outputs a comprehensive JSON structure containing all detected interfaces, their configurations, addresses, and special attributes.
2. Extended Interface Details
Show human-readable interface information with extensive details:
$ sudo python3 device_info.py --extended
Output includes:
Interface name, index, and type
MAC address and MTU
Operational and administrative state
Carrier status and change counts
Hardware offload capabilities
Bridge, VLAN, or WireGuard specific configuration
3. Detailed Address Information
Focus on IP address configuration and readiness:
$ sudo python3 device_info.py --addresses
Shows address-specific details:
Readiness status (✓=ready, ⋯=pending, ✗=failed, ○=down)
IPv4 and IPv6 addresses with prefix lengths
Network, netmask, and broadcast information
Address scope and flags
IPv6 DAD (Duplicate Address Detection) state
Address lifetime information
Configuration protocol (manual, SLAAC, kernel)
4. WireGuard Interfaces Only
Filter output to show only WireGuard interfaces with peer details:
$ sudo python3 device_info.py --wireguard
Displays:
WireGuard interface configuration
Listen port and fwmark settings
Public key information
Peer count and individual peer details
Allowed IPs per peer
Latest handshake times
Transfer statistics per peer
5. Summary View
Condensed view of special interfaces (bridges, VLANs, WireGuard, veth):
$ sudo python3 device_info.py --summary
💡 Pro Tip: Pipe JSON output to jq for advanced filtering:
# Check if IPv6 addresses have completed DAD
$ sudo python3 device_info.py --addresses | grep -A 10 "ipv6"
# Get readiness status of all addresses
$ sudo python3 device_info.py | jq '.[] | .addresses[] | {addr: .address, ready: .readiness}'
Bridge Management
# View all bridge configurations
$ sudo python3 device_info.py --summary | grep -i bridge
# Get detailed bridge STP status
$ sudo python3 device_info.py --extended | grep -A 20 "Bridge Master"
When upgrading your kernel, verify that new attributes are properly supported:
# Check kernel version
$ uname -r
# Test the script
$ sudo python3 device_info.py --extended
# Look for "unknown_attrs" in output
$ sudo python3 device_info.py | jq '.[] | select(.unknown_attrs != null)'
3. Monitor for Deprecation Warnings
# Run with warnings enabled
$ python3 -W all device_info.py > /dev/null
Extending the Tool
Adding New IFLA Attributes
When new kernel versions introduce new IFLA_* constants:
Find the constant definition in /usr/include/linux/if_link.h
Add it to the C code's IFLA_ATTR_NAMES array (around line 300)
Implement decoding logic in parse_ifla_attributes() (around line 1500)
Update the Python post-processing if needed (around line 5200)
Document it in the reference guide
Example for a hypothetical IFLA_NEW_FEATURE (65):
// In C_SOURCE - Add to IFLA_ATTR_NAMES
[65] = "IFLA_NEW_FEATURE",
// In parse_ifla_attributes() - Add decode case
case 65: // IFLA_NEW_FEATURE
if (rta_len >= sizeof(uint32_t)) {
uint32_t value = *(uint32_t*)RTA_DATA(rta);
append_comma(info_json);
append_to_buffer(info_json, "\"new_feature\": %u", value);
}
break;
Adding New Interface Types
To support a new interface kind (e.g., "newtype"):
Add kind detection in IFLA_LINKINFO parsing (around line 2000)
Create a type-specific attribute parser
Add Python-side processing if needed
Update output formatters
Debugging New Features
Enable verbose debugging during development:
# Add debug output in C code (temporary)
fprintf(stderr, "DEBUG: Processing attribute type %d, len %d\n",
rta_type, rta_len);
# Or use Python logging
import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
logger.debug(f"Interface: {if_name}, Data: {data}")
Code Organization Best Practices
⚠️ Important Guidelines:
Keep C and Python synchronized: Struct definitions must match between C code and CFFI cdef
Handle padding: Use __attribute__((packed)) for structs read directly from kernel
Version guards: Check kernel version before using newer attributes
Error handling: Always check return values from C functions
Memory safety: Free allocated memory before returning from C functions
Testing Strategy
Unit Testing
Create test scenarios for different interface configurations:
#!/bin/bash
# test_script.sh - Comprehensive testing
# Test 1: Basic execution
echo "Test 1: Basic execution"
sudo python3 device_info.py > /tmp/test1.json || exit 1
# Test 2: Validate JSON
echo "Test 2: JSON validation"
jq empty /tmp/test1.json || exit 1
# Test 3: Check for required fields
echo "Test 3: Required fields"
jq -e '.lo.ifname == "lo"' /tmp/test1.json || exit 1
# Test 4: All output modes
for mode in "--extended" "--addresses" "--summary"; do
echo "Test 4: Mode $mode"
sudo python3 device_info.py $mode > /dev/null || exit 1
done
echo "All tests passed!"
Integration Testing
Test with various interface configurations:
# Create test VLAN
sudo ip link add link eth0 name eth0.100 type vlan id 100
sudo python3 device_info.py | jq '.["eth0.100"]'
sudo ip link del eth0.100
# Create test bridge
sudo ip link add br0 type bridge
sudo python3 device_info.py --summary | grep br0
sudo ip link del br0
# Create veth pair
sudo ip link add veth0 type veth peer name veth1
sudo python3 device_info.py | jq '.veth0.veth'
sudo ip link del veth0
Performance Monitoring
Track execution time and resource usage:
# Time the execution
$ time sudo python3 device_info.py > /dev/null
# Monitor memory usage
$ /usr/bin/time -v sudo python3 device_info.py > /dev/null 2>&1 | \
grep -E "(Maximum resident set size|User time|System time)"
# Profile the script
$ sudo python3 -m cProfile -o profile.stats device_info.py
$ python3 -c "import pstats; p = pstats.Stats('profile.stats'); p.sort_stats('cumulative').print_stats(20)"
Documentation Updates
When modifying the tool, update:
Docstring at the top of the file
This guide (HTML)
IFLA_IFA_REFERENCE.md for new attributes
Usage examples for new features
Change log with version notes
🔍 Troubleshooting
Common Issues and Solutions
Issue: Permission Denied
Error:OSError: [Errno 13] Permission denied
Solution: The script requires root access to create Netlink sockets:
Error:RuntimeError: Python 3.12+ requires setuptools for CFFI
Solution: Install setuptools:
$ pip install setuptools
Issue: Old Python Version
Error:RuntimeError: Python 3.8 or higher is required
Solution: Upgrade Python or use a virtual environment:
# Check your Python version
$ python3 --version
# Use a newer Python if available
$ python3.10 device_info.py
# Or install a newer Python version
$ sudo apt-get install python3.10 # Debian/Ubuntu
$ sudo yum install python310 # RHEL/CentOS
Issue: Compilation Errors
Error:CompileError: command 'gcc' failed
Solution: Install build essentials and kernel headers:
Netlink socket timeout: Increase timeout in C code (SOCKET_TIMEOUT)
Buffer overflow: Increase INITIAL_BUFFER_SIZE or MAX_BUFFER_SIZE
Kernel version too old: Some attributes require newer kernels
Namespace isolation: Run in appropriate network namespace
# Check for kernel version requirements
$ uname -r
$ grep -A 5 "IFLA_.*Min Kernel" IFLA_IFA_REFERENCE.md
# Run in specific namespace
$ sudo ip netns exec myns python3 device_info.py
Issue: WireGuard Information Not Showing
Checklist:
Verify WireGuard module is loaded: lsmod | grep wireguard
Check if WireGuard interfaces exist: wg show
Ensure Generic Netlink family is registered: genl-ctrl-list | grep wireguard
Use --wireguard flag to isolate WireGuard output
# Load WireGuard module
$ sudo modprobe wireguard
# Create test interface
$ sudo ip link add dev wg0 type wireguard
# Run script
$ sudo python3 device_info.py --wireguard
Issue: Unknown Attributes Appearing
If you see unknown_attrs in the output, it means the kernel is providing attributes that aren't decoded by the script.
Investigation steps:
Note the attribute numbers
Check /usr/include/linux/if_link.h for the constant definition
Add the attribute to IFLA_ATTR_NAMES in the C code
Modify the C code temporarily to add debug prints:
// Add at the beginning of problematic function
#define DEBUG 1
#ifdef DEBUG
fprintf(stderr, "DEBUG: Entering function with param=%d\n", param);
#endif
# Compare interface list
$ ip -j link show | jq -r '.[].ifname' | sort > /tmp/ip_output.txt
$ sudo python3 device_info.py | jq -r 'keys[]' | sort > /tmp/script_output.txt
$ diff /tmp/ip_output.txt /tmp/script_output.txt
# Compare addresses
$ ip -j addr show dev eth0 | jq '.[] | .addr_info'
$ sudo python3 device_info.py | jq '.eth0.addresses'
Getting Help
If you encounter issues not covered here:
Check kernel logs: sudo dmesg | tail -50
Verify system requirements are met
Test with minimal configuration (only loopback interface)
Check for conflicting network management tools
Review recent kernel or distribution updates
📚 API Reference
Complete reference for IFLA and IFA constants used in Linux RTNetlink.
IFLA Constants (Interface Link Attributes)
These constants are defined in <linux/if_link.h> and used for querying/configuring network interface properties.
Basic Interface Properties (0-10)
Constant
Value
Description
IFLA_UNSPEC
0
Unspecified/invalid attribute
IFLA_ADDRESS
1
Hardware (MAC) address
IFLA_BROADCAST
2
Hardware broadcast address
IFLA_IFNAME
3
Interface name (string)
IFLA_MTU
4
Maximum Transmission Unit
IFLA_LINK
5
Link to parent device (for VLANs, etc.)
IFLA_QDISC
6
Queueing discipline name
IFLA_STATS
7
Interface statistics (legacy, 32-bit)
IFLA_MASTER
10
Master device for bonding/bridging
Operational State and Configuration (11-20)
Constant
Value
Description
IFLA_TXQLEN
13
Transmit queue length
IFLA_OPERSTATE
16
Operational state (up/down/dormant)
IFLA_LINKMODE
17
Link mode (default/dormant)
IFLA_LINKINFO
18
Link type specific info (nested)
IFLA_IFALIAS
20
Interface alias (description)
Statistics and Monitoring (21-35)
Constant
Value
Description
IFLA_STATS64
23
Interface statistics (64-bit, preferred)
IFLA_AF_SPEC
26
Address family specific attributes
IFLA_PROMISCUITY
30
Promiscuity count
IFLA_CARRIER
33
Physical carrier status
IFLA_CARRIER_CHANGES
35
Number of carrier changes
Hardware Offload Properties (40-43)
Constant
Value
Description
IFLA_GSO_MAX_SEGS
40
GSO max segments
IFLA_GSO_MAX_SIZE
41
Generic Segmentation Offload max size
IFLA_XDP
43
eXpress Data Path (XDP) configuration
Carrier Event Tracking (47-49)
Constant
Value
Description
IFLA_CARRIER_UP_COUNT
47
Carrier up event count
IFLA_CARRIER_DOWN_COUNT
48
Carrier down event count
IFLA_NEW_IFINDEX
49
New interface index
Recent Additions (52-64) - Kernel 5.0+
Constant
Value
Min Kernel
Description
IFLA_ALT_IFNAME
53
5.5
Alternative interface names
IFLA_PERM_ADDRESS
54
5.6
Permanent hardware address
IFLA_PARENT_DEV_NAME
56
5.10
Parent device name
IFLA_GRO_MAX_SIZE
58
5.15
GRO max size
IFLA_TSO_MAX_SIZE
59
5.18
TSO max size
IFLA_TSO_MAX_SEGS
60
5.18
TSO max segments
IFLA_GSO_IPV4_MAX_SIZE
63
6.2
GSO IPv4 max size
IFLA_GRO_IPV4_MAX_SIZE
64
6.2
GRO IPv4 max size
IFA Constants (Interface Address Attributes)
These constants are defined in <linux/if_addr.h> and used for querying/configuring IP addresses.
Basic Address Properties (0-8)
Constant
Value
Description
IFA_UNSPEC
0
Unspecified/invalid attribute
IFA_ADDRESS
1
Interface address (IPv4/IPv6)
IFA_LOCAL
2
Local address (for point-to-point)
IFA_LABEL
3
Address label/alias
IFA_BROADCAST
4
Broadcast address (IPv4 only)
IFA_ANYCAST
5
Anycast address (IPv6 only)
IFA_CACHEINFO
6
Address lifetime information
IFA_FLAGS
8
Extended address flags (32-bit)
Recent Additions (9-11) - Kernel 4.20+
Constant
Value
Min Kernel
Description
IFA_RT_PRIORITY
9
4.20
Route priority for this address
IFA_TARGET_NETNSID
10
5.0
Target network namespace ID
IFA_PROTO
11
5.1
Address protocol (how configured)
Address Flags (IFA_F_*)
These are flag bits used within the IFA_FLAGS attribute:
Flag
Hex Value
Description
IFA_F_SECONDARY
0x01
Secondary/alias address (IPv4)
IFA_F_TEMPORARY
0x01
Temporary address (IPv6 privacy)
IFA_F_NODAD
0x02
Skip Duplicate Address Detection
IFA_F_OPTIMISTIC
0x04
Optimistic DAD (usable immediately)
IFA_F_DADFAILED
0x08
DAD failed
IFA_F_DEPRECATED
0x20
Deprecated address
IFA_F_TENTATIVE
0x40
Tentative (DAD in progress)
IFA_F_PERMANENT
0x80
Permanent address
IFA_F_MANAGETEMPADDR
0x100
Manage temporary addresses
IFA_F_NOPREFIXROUTE
0x200
Don't create prefix route
IFA_F_STABLE_PRIVACY
0x800
Stable privacy address
Address Readiness States
The tool computes an address "readiness" state based on interface state and address flags:
State
Symbol
Condition
Meaning
ready
✓
Interface UP, no DAD flags
Address is fully operational
pending
⋯
TENTATIVE or OPTIMISTIC flag, or link DORMANT
Configuration in progress
failed
✗
DADFAILED flag set
Duplicate address detected
down
○
Interface DOWN or LOWERLAYERDOWN
Interface not operational
Operational States (IF_OPER_*)
Value
Name
Description
0
UNKNOWN
Operational state unknown
1
NOTPRESENT
Interface not present
2
DOWN
Interface is down
3
LOWERLAYERDOWN
Lower layer is down
4
TESTING
Interface in testing mode
5
DORMANT
Interface dormant
6
UP
Interface is up
Bridge STP States
Value
Name
Description
0
DISABLED
STP disabled
1
LISTENING
Port is listening
2
LEARNING
Port is learning
3
FORWARDING
Port is forwarding
4
BLOCKING
Port is blocking
📖 Full Reference:
For complete details on all 64+ IFLA and 11+ IFA constants, including kernel version compatibility and usage examples, refer to the included IFLA_IFA_REFERENCE.md document.
💡 Advanced Examples
Scripting and Automation
Monitor for New Interfaces
#!/bin/bash
# Watch for interface additions/removals
LAST_COUNT=0
while true; do
CURRENT_COUNT=$(sudo python3 device_info.py | jq 'length')
if [ "$CURRENT_COUNT" -ne "$LAST_COUNT" ]; then
echo "$(date): Interface count changed: $LAST_COUNT → $CURRENT_COUNT"
sudo python3 device_info.py --summary
LAST_COUNT=$CURRENT_COUNT
fi
sleep 2
done
Alert on Carrier Loss
#!/bin/bash
# Alert when critical interfaces lose carrier
CRITICAL_IFACES="eth0 eth1"
for iface in $CRITICAL_IFACES; do
CARRIER=$(sudo python3 device_info.py | \
jq -r ".\"$iface\".carrier // false")
if [ "$CARRIER" = "false" ]; then
echo "ALERT: $iface has no carrier!"
# Send notification, email, etc.
fi
done
#!/usr/bin/env python3
# Export metrics in Prometheus format
import subprocess
import json
import sys
def main():
result = subprocess.run(
['sudo', 'python3', 'device_info.py'],
capture_output=True,
text=True
)
if result.returncode != 0:
sys.exit(1)
interfaces = json.loads(result.stdout)
# Export carrier status
for name, info in interfaces.items():
carrier = 1 if info.get('carrier', False) else 0
print(f'interface_carrier{{interface="{name}"}} {carrier}')
# Export MTU
for name, info in interfaces.items():
if 'mtu' in info:
print(f'interface_mtu{{interface="{name}"}} {info["mtu"]}')
# Export address count
for name, info in interfaces.items():
addr_count = len(info.get('addresses', []))
print(f'interface_addresses{{interface="{name}"}} {addr_count}')
if __name__ == '__main__':
main()
Nagios/Icinga Check Plugin
#!/bin/bash
# Nagios plugin to check interface status
CRITICAL_IFACES="$1"
WARNING=""
CRITICAL=""
for iface in $CRITICAL_IFACES; do
DATA=$(sudo python3 device_info.py | jq -r ".\"$iface\"")
if [ "$DATA" = "null" ]; then
CRITICAL="$CRITICAL Interface $iface not found."
continue
fi
OPERSTATE=$(echo "$DATA" | jq -r '.operstate')
CARRIER=$(echo "$DATA" | jq -r '.carrier // false')
if [ "$OPERSTATE" != "UP" ]; then
CRITICAL="$CRITICAL $iface is $OPERSTATE."
elif [ "$CARRIER" = "false" ]; then
WARNING="$WARNING $iface has no carrier."
fi
done
if [ -n "$CRITICAL" ]; then
echo "CRITICAL:$CRITICAL"
exit 2
elif [ -n "$WARNING" ]; then
echo "WARNING:$WARNING"
exit 1
else
echo "OK: All interfaces operational"
exit 0
fi