High-performance asynchronous NTP proxy that intercepts NTP requests from specified clients (via ipset) and forwards them to a backend NTP server while maintaining the original destination IP as the source address in responses.
- 🚀 Asynchronous event-driven architecture (epoll)
- 🌐 Full IPv4 and IPv6 client support
- ⚡ High throughput with thread pool design
- 🎯 Selective interception using ipset
- 🔄 Automatic timeout handling
- 📊 Query tracking and drop monitoring
- Linux kernel with netfilter support
libnetfilter-queue-devipsetiptablesandip6tables- GCC compiler
# Debian/Ubuntu
apt-get update
apt-get install -y libnetfilter-queue-dev ipset iptables build-essential
# RHEL/CentOS
yum install -y libnetfilter_queue-devel ipset iptables gcc makeEdit ntp.c and change the NTP server IP address:
#define NTP_SERVER "pool.ntp.org" // Change this to your NTP server
#define NTP_PORT 123gcc -o ntp ntp.c -lnetfilter_queue -lpthread -O2sudo install -m 755 ntp /usr/local/bin/ntp-proxy# Create IPv4 ipset
ipset create myntp4 hash:ip family inet
# Create IPv6 ipset
ipset create myntp6 hash:ip family inet6# Add IPv4 addresses
ipset add myntp4 192.168.1.100
ipset add myntp4 10.0.0.0/24
# Add IPv6 addresses
ipset add myntp6 2001:db8::1
ipset add myntp6 fd00::/64ipset list myntp4
ipset list myntp6# Save to file
ipset save > /etc/ipset.conf
# Restore on boot (add to /etc/rc.local or systemd)
ipset restore < /etc/ipset.conf# IPv4 - Intercept NTP requests from myntp4 clients
iptables -t mangle -A PREROUTING -p udp --dport 123 -m set --match-set myntp4 src -j NFQUEUE --queue-num 123
# IPv6 - Intercept NTP requests from myntp6 clients
ip6tables -t mangle -A PREROUTING -p udp --dport 123 -m set --match-set myntp6 src -j NFQUEUE --queue-num 123apt-get install iptables-persistent
netfilter-persistent saveCreate /etc/systemd/system/ntp-proxy-firewall.service:
[Unit]
Description=NTP Proxy Firewall Rules
Before=ntp-proxy.service
After=network.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/bash -c 'ipset restore < /etc/ipset.conf || true'
ExecStart=/sbin/iptables -t mangle -A PREROUTING -p udp --dport 123 -m set --match-set myntp4 src -j NFQUEUE --queue-num 123
ExecStart=/sbin/ip6tables -t mangle -A PREROUTING -p udp --dport 123 -m set --match-set myntp6 src -j NFQUEUE --queue-num 123
ExecStop=/sbin/iptables -t mangle -D PREROUTING -p udp --dport 123 -m set --match-set myntp4 src -j NFQUEUE --queue-num 123
ExecStop=/sbin/ip6tables -t mangle -D PREROUTING -p udp --dport 123 -m set --match-set myntp6 src -j NFQUEUE --queue-num 123
[Install]
WantedBy=multi-user.targetEnable it:
systemctl enable ntp-proxy-firewall.service
systemctl start ntp-proxy-firewall.serviceCreate /etc/systemd/system/ntp-proxy.service:
[Unit]
Description=NTP Proxy - Dual Stack IPv4/IPv6
After=network.target ntp-proxy-firewall.service
Wants=ntp-proxy-firewall.service
[Service]
Type=simple
ExecStart=/usr/local/bin/ntp-proxy
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
# Security hardening
NoNewPrivileges=false
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/log
# Resource limits
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target# Reload systemd
systemctl daemon-reload
# Enable auto-start at boot
systemctl enable ntp-proxy.service
# Start the service
systemctl start ntp-proxy.service
# Check status
systemctl status ntp-proxy.service
# View logs
journalctl -u ntp-proxy.service -f# Restart service
systemctl restart ntp-proxy.service
# Stop service
systemctl stop ntp-proxy.service
# Disable auto-start
systemctl disable ntp-proxy.service# 1. Install dependencies
apt-get update && apt-get install -y libnetfilter-queue-dev ipset iptables build-essential
# 2. Compile
gcc -o ntp ntp.c -lnetfilter_queue -lpthread -O2
# 3. Install binary
sudo install -m 755 ntp /usr/local/bin/ntp-proxy
# 4. Create ipsets
ipset create myntp4 hash:ip family inet
ipset create myntp6 hash:ip family inet6
# 5. Add some IPs
ipset add myntp4 192.168.1.0/24
ipset add myntp6 2001:db8::/64
# 6. Save ipsets
ipset save > /etc/ipset.conf
# 7. Create firewall service
cat > /etc/systemd/system/ntp-proxy-firewall.service << 'EOF'
[Unit]
Description=NTP Proxy Firewall Rules
Before=ntp-proxy.service
After=network.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/bash -c 'ipset restore < /etc/ipset.conf || true'
ExecStart=/sbin/iptables -t mangle -A PREROUTING -p udp --dport 123 -m set --match-set myntp4 src -j NFQUEUE --queue-num 123
ExecStart=/sbin/ip6tables -t mangle -A PREROUTING -p udp --dport 123 -m set --match-set myntp6 src -j NFQUEUE --queue-num 123
ExecStop=/sbin/iptables -t mangle -D PREROUTING -p udp --dport 123 -m set --match-set myntp4 src -j NFQUEUE --queue-num 123
ExecStop=/sbin/ip6tables -t mangle -D PREROUTING -p udp --dport 123 -m set --match-set myntp6 src -j NFQUEUE --queue-num 123
[Install]
WantedBy=multi-user.target
EOF
# 8. Create ntp-proxy service
cat > /etc/systemd/system/ntp-proxy.service << 'EOF'
[Unit]
Description=NTP Proxy - Dual Stack IPv4/IPv6
After=network.target ntp-proxy-firewall.service
Wants=ntp-proxy-firewall.service
[Service]
Type=simple
ExecStart=/usr/local/bin/ntp-proxy
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
NoNewPrivileges=false
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/log
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
# 9. Enable and start services
systemctl daemon-reload
systemctl enable ntp-proxy-firewall.service ntp-proxy.service
systemctl start ntp-proxy-firewall.service ntp-proxy.service
# 10. Verify
systemctl status ntp-proxy.service
journalctl -u ntp-proxy.service -fsystemctl status ntp-proxy.servicejournalctl -u ntp-proxy.service -f# Check NFQUEUE stats
cat /proc/net/netfilter/nfnetlink_queue# Check if an IP is in the set
ipset test myntp4 192.168.1.100
ipset test myntp6 2001:db8::1# Test NTP query from a client in the ipset
ntpdate -q pool.ntp.org
# Check system time sync status
timedatectl status# Check for errors
journalctl -u ntp-proxy.service -n 50
# Verify binary exists
ls -l /usr/local/bin/ntp-proxy
# Check permissions
sudo /usr/local/bin/ntp-proxy# Verify iptables rules are active
iptables -t mangle -L PREROUTING -n -v
ip6tables -t mangle -L PREROUTING -n -v
# Check if packets are hitting the queue
cat /proc/net/netfilter/nfnetlink_queue
# Verify ipsets exist
ipset list
# Test NTP connectivity to backend server
ntpdate -q your.ntp.server# Check IPv6 is enabled
sysctl net.ipv6.conf.all.disable_ipv6
# Verify IPv6 firewall rules
ip6tables -t mangle -L PREROUTING -n -v
# Test IPv6 connectivity
ping6 2001:4860:4860::8888# Check if NTP packets are being intercepted
tcpdump -i any udp port 123 -vv
# Verify backend NTP server is reachable
ntpdate -q <NTP_SERVER>
# Check for clock drift
ntpq -pAdjust these values in ntp.c before compiling:
#define MAX_PENDING 1000 // Maximum concurrent queries
#define NTP_TIMEOUT_MS 300 // Query timeout in milliseconds
#define THREAD_POOL_SIZE 20 // Event loop thread count- This proxy requires root privileges to use raw sockets and NFQUEUE
- Only intercepts NTP requests from IPs in the ipset lists
- Responses are spoofed to appear from the original destination IP
- Enable
NoNewPrivileges=truein systemd if not using raw sockets - Consider using authenticated NTP (NTPsec) for production environments
- Centralized time source management for client networks
- NTP traffic inspection and logging
- Time synchronization in isolated network segments
- Testing and development of NTP-dependent applications
- Network time protocol monitoring and analysis
This project is provided as-is for educational and infrastructure purposes.
Client (IPv4/IPv6) → iptables/ip6tables → NFQUEUE(123) → ntp-proxy
↓
Backend NTP Server
↓
ntp-proxy → Client
(spoofed source)
The proxy:
- Intercepts NTP requests via NFQUEUE
- Forwards them to the backend NTP server (IPv4)
- Receives responses
- Sends spoofed responses back to clients with original destination IP as source