EST. 2026 — Notes from real server work SYSADMIN · SECURITY · AI · DIRECTADMIN Issue 01
THE SYSADMIN ALMANAC
Home / Network / Read Device Uptime Without SNMP/SSH/API Using TCP Timestamps
Network

Read Device Uptime Without SNMP/SSH/API Using TCP Timestamps

by Ton-Or ·20 Aug 2026·7 min read
Read Device Uptime Without SNMP/SSH/API Using TCP Timestamps

A device that reboots itself in the middle of the night without anyone noticing is one of the hardest problems to gather evidence for, because everything looks normal again by morning. This method gives me its actual boot time without a password, an SDK, or any physical access to the device.

I had to track down why a device on the network kept dropping out intermittently and coming back as if nothing had happened. My first hypothesis was that it was rebooting itself, but how could I prove it when that firmware version had no SNMP to enable, no SSH access, no uptime field on the web interface, and no command for querying it in the manufacturer's API? So I reached for an old OS fingerprinting technique — reading uptime from the TCP timestamp option, which the device already returns every time I open a connection.

The only requirement is that it runs a Linux kernel (or BSD) and has TCP timestamps enabled. Almost all embedded devices these days qualify because they run Linux internally, and the kernel default is to enable them.

All IP addresses in this article are made-up addresses in 192.168.10.0/24. The commands and output are real.

The principle: TSval is jiffies since boot

TCP timestamps are an option defined in RFC 1323 that lets TCP measure RTT more accurately and protect against wrapped sequence numbers (PAWS). When I send a SYN, the destination replies with a SYN+ACK containing a TSval value in the options.

What makes this interesting is that Linux puts the jiffies value in directly — jiffies is the kernel's tick counter, which starts at zero at boot and keeps increasing at a fixed frequency (CONFIG_HZ, usually 100, or 250 or 1000 in some kernels). That means if I know TSval and HZ, dividing one by the other immediately gives me uptime in seconds.

How do I find HZ without access to the device? The answer is to send two packets some time apart and measure it. If TSval increases by 100 in 1 second, that simply means HZ = 100.

From SYN to boot timeSend SYNto an open port→SYN+ACKTSval = jiffies→Send againMeasure ticks/second = HZ→TSval / HZ= uptime→now - uptime= boot time

In practice: two lines of hping3

hping3 handles the whole process: sending repeat probes, estimating HZ, and converting the result to text:

apt-get install -y hping3
hping3 -c 2 -S -p 443 --tcp-timestamp 192.168.10.8

The output:

HPING 192.168.10.8 (eth0 192.168.10.8): S set, 40 headers + 0 data bytes
len=46 ip=192.168.10.8 ttl=63 DF id=0 sport=443 flags=SA seq=0 win=29200 rtt=2.1 ms
  TCP timestamp: tcpts=3308374

len=46 ip=192.168.10.8 ttl=63 DF id=0 sport=443 flags=SA seq=1 win=29200 rtt=1.9 ms
  TCP timestamp: tcpts=3308474
  HZ seems hz=100
  System uptime seems: 0 days, 9 hours, 11 minutes, 23 seconds

Reading it line by line — tcpts increases from 3308374 to 3308474, a rise of 100 in one second, so it concludes that hz=100. Dividing 3308374 by 100 gives 33083 seconds = 9 hours 11 minutes. That is the device's uptime. Subtract it from the current time to get the boot time immediately. In my case, it matched the moment the power flickered — case closed, no more arguing.

Boot time from two TSval values (from the hping3 example)Send SYN 1SYN+ACK tcpts = 3308374Send SYN 2, 1 second laterSYN+ACK tcpts = 3308474Up 100 in 1 second = hz 100At least 2 packets needed3308374 / 100 = 33083 secondsTSval is jiffies since bootuptime 9 hr 11 min 23 secCurrent time - uptime = boot time
Using the hping3 output above, TSval increases from 3308374 to 3308474 in 1 second, giving hz 100. Dividing TSval by hz gives uptime.

Some practical notes:

  1. It must run as root because it uses a raw socket to assemble packets itself.
  2. Any open port on the device will do — 80 / 443 / 22 / 8080 / 554, anything that returns a SYN+ACK. No login or password needed.
  3. -c 2 is enough. At least 2 packets are needed to calculate HZ. Sending more gives a slightly more stable value, but it is not worth the time.
  4. If a firewall filters the chosen port, there will be no SYN+ACK at all — try another port before concluding that this does not work.
2Packets to send
hz=100Ticks per second
497 daysBefore the counter wraps
0Passwords used

The script I actually use — loop over devices and calculate boot times

hping3 alone works when investigating an incident, but integrating it into monitoring requires converting the output to numbers first. So I wrote a short wrapper:

#!/usr/bin/env python3
# device_uptime.py — อ่าน uptime อุปกรณ์ผ่าน TCP timestamp (ต้องรันเป็น root)
import re, subprocess, sys, time
from datetime import datetime

PAT = re.compile(
    r'System uptime seems:\s*(\d+)\s*days?,\s*(\d+)\s*hours?,'
    r'\s*(\d+)\s*minutes?,\s*(\d+)\s*seconds?')

def uptime_sec(ip, port=443, timeout=8):
    """คืนค่า uptime เป็นวินาที หรือ None ถ้าอ่านไม่ได้"""
    try:
        r = subprocess.run(
            ['hping3', '-c', '2', '-S', '-p', str(port), '--tcp-timestamp', ip],
            capture_output=True, text=True, timeout=timeout)
    except (subprocess.TimeoutExpired, FileNotFoundError):
        return None
    m = PAT.search(r.stdout + r.stderr)   # hping3 พ่นบางส่วนออก stderr
    if not m:
        return None
    d, h, mi, s = map(int, m.groups())
    return d * 86400 + h * 3600 + mi * 60 + s

for ip in sys.argv[1:]:
    up = uptime_sec(ip)
    if up is None:
        print(f'{ip:<16} อ่านไม่ได้ (ปิด timestamp / เป็น Windows / โดน NAT rewrite)')
        continue
    boot = datetime.fromtimestamp(time.time() - up)
    print(f'{ip:<16} uptime {up // 86400}d '
          f'{up % 86400 // 3600:02d}:{up % 3600 // 60:02d}  '
          f'boot {boot:%Y-%m-%d %H:%M}')

Running it gives this:

sudo ./device_uptime.py 192.168.10.8 192.168.10.21 192.168.10.50
192.168.10.8     uptime 0d 09:11  boot 2026-08-15 01:04
192.168.10.21    uptime 63d 04:37  boot 2026-06-13 06:18
192.168.10.50    อ่านไม่ได้ (ปิด timestamp / เป็น Windows / โดน NAT rewrite)

What I find most valuable is not the uptime number, but the boot column — after collecting it every hour, a change means the device has rebooted. I can immediately compare it with power logs or switch logs to see which event it matches. I store it in a table and alert only when the boot value changes. That gives me a system for catching "devices quietly rebooting themselves" without relying on any cooperation from the devices.

What it works with, and what it does not

Works

Anything running Linux/BSD inside

  • IP cameras and door controllers whose firmware has no SNMP
  • Routers / switches / embedded IoT devices
  • Ordinary Linux servers with timestamps still enabled
  • NAS and storage appliances with at least one open port
Does not work

Disabled, or rewritten along the way

  • Windows — TCP timestamps have been disabled by default since Vista
  • Machines explicitly configured with net.ipv4.tcp_timestamps=0
  • Anything behind a NAT / proxy / load balancer that rewrites TCP options
  • Destinations that return no SYN+ACK on any port
The value belongs to "whatever replies," not necessarily the device I have in mind

When probing through a load balancer or reverse proxy, the value is the uptime of the frontend, not the backend machine. And if the device sits behind a NAT that rewrites TCP options, the value can be wrong without any obvious sign — trust it only when probing the device directly on the same network. Another point is that the counter wraps when jiffies reaches 2^32, which at hz=100 is around 497 days, but if the kernel uses hz=1000, that drops to around 49 days. If a machine's uptime jumps back to zero without a reboot, suspect this first.

Alternatives if this method does not work

  • nmap -O -Pn <ip> provides an "Uptime guess" line. It uses the same mechanism but needs to scan several ports before reaching a conclusion, so it is much slower. The advantage is that it comes with nmap, which is already installed on many machines, so there is no need to install hping3.
  • scapy if I want to avoid calling a subprocess: sr1(IP(dst=ip)/TCP(dport=443, flags='S', options=[('Timestamp',(0,0))])), then read TSval from the returned option — but I have to collect two samples and calculate HZ myself, which means rewriting everything hping3 already does. I choose this route only when I want raw TSval values for further use.
  • ICMP timestamp (ping -T tsonly) gives the destination's clock time, not uptime, but it is useful for catching "device clock is wrong" cases, and many devices block it.
  • Continuous ordinary ping does not report uptime, but if I record it every minute, the gap shows when the device was gone — less precise, but it works with every device without exception. I always use both, because they answer different questions: ping tells me "when did it disappear," while TCP timestamps tell me "when did it reboot," which are sometimes different times.
  • syslog from the device is the most direct option if the device can send it, and it should be the first thing to set up. The TCP timestamp method is only for devices that cannot send syslog.
The other side of the coin — my server's uptime can be read too

If this technique works on other people's devices, anyone can read my server's uptime too. That is useful information for someone looking for a target — a machine with 800 days of uptime is a machine whose kernel has not been patched for 800 days. This can be disabled with net.ipv4.tcp_timestamps=0 in sysctl, but doing so also loses PAWS and accurate RTT measurements. Personally, I leave it enabled and reboot promptly after kernel updates instead. That addresses the root cause more directly.

This method is not accurate to the second and does not work on every device, but it answers one of the most costly questions in system administration — "did this thing reboot itself?" — without a password, without installing anything on the device, and without waiting for the manufacturer to release new firmware. If there is a device on your network that you cannot access like this, try probing one. It takes less than a minute to find out whether it works. If I have left anything out or made a typo, I will come back and fix it later.

Want us to set this up?

Get help with server setup, configuration, migration, or troubleshooting. Save time for the work that matters.

Get a quote

Hit something like this? Stuck somewhere?

Come trade notes on the LinuxThai board

→ Continue on the board

Comments 0

No comments yet — be the first