mysite/public-onion/sources/posts/house_upgrade.md

32 KiB
Raw Blame History

title date draft tags description author ShowToc TocOpen
I Beat My 8-Device Wi-Fi Limit with a Raspberry Pi (and Made an IoT Village) 2025-11-23 false
raspberry pi
wifi
iot
networking
hostapd
dnsmasq
Real-world guide: hardware choices, reasons, setup details, performance, and how many devices you can realistically support. Iman Alipour true true

The Day My Wi-Fi Said “Eight Is Enough”

My apartment Wi-Fi (ASK4) has a hard cap of 8 devices. Thats adorable until you own a phone, a laptop, a tablet, a TV, a smart speaker, and even a couple of smart plugs. My solution: put all the IoT stuff behind a Raspberry Pi that shows up as one device to the buildings network, but acts like a full-blown Wi-Fi for everything else I own.

This post is everything I did—no corporate firewall access, no exotic gear—just a Pi, a USB Wi-Fi adapter, and a little patience.


Hardware: what I picked and why

  • Raspberry Pi 4 (4GB)
    It has enough CPU to route/NAT comfortably, USB 3 for fast Wi-Fi dongles, and solid Linux support. A Pi 3 works in a pinch, but the 4 stays cooler and handles bursts better.

  • ALFA AWUS036ACHM (USB Wi-Fi)
    Reliable, AP-mode friendly on Linux, and happy with hostapd. I used it to broadcast my IoT network (2.4 GHz, 20 MHz channel) while the Pis built-in Wi-Fi connects upstream. For better range, use a short USB 3 extension so the antenna sits away from the Pi (USB 3 noise can hurt 2.4 GHz).

  • Fast USB-C power supply (official Pi 4 or 5V/3A equivalent)
    Wi-Fi spikes current draw; brown-outs cause random gremlins.

  • 1632 GB microSD, heatsinks, ventilated case
    The Pi 4 appreciates a bit of airflow.

  • (Optional) Ethernet cable
    If you can wire the Pi to the wall/router for upstream instead of using onboard Wi-Fi, do it. Wired upstream = higher, steadier throughput and frees the onboard radio.


What this setup actually does

  • The Pi connects to the buildings ASK4 Wi-Fi as a client (wlan0).
  • The ALFA dongle creates a new access point called IoT_hub (wlx… interface) with password Iman8118.
  • Devices join IoT_hub. The Pi does NAT, so upstream sees one MAC address (the Pi), not dozens of gadgets. Device limit: defeated.
  • Avahi mDNS reflector lets services like AirPlay/HomeKit be discoverable across the two subnets without touching the managed router.
  • If Im on the building Wi-Fi and want to control IoT gear without switching, I can also use an optional overlay (e.g., Tailscale) to reach 192.168.50.0/24 from anywhere.

Setup details (the friendly version)

1) Access point with hostapd

I broadcast a simple WPA2 network on 2.4 GHz (best compatibility). Channel 1/6/11—pick the cleanest. 20 MHz channel width keeps legacy and low-power devices happy.

interface=wlx00c0cab7ab29
ssid=<A_NICE_SSID>
country_code=DE
hw_mode=g
channel=6
ieee80211n=1
wmm_enabled=1
# Don't require HT or you'll reject some tiny IoT clients:
require_ht=0
wpa=2
wpa_key_mgmt=WPA-PSK
rsn_pairwise=CCMP
wpa_passphrase=<STRONG_PASSWORD>

2) DHCP on the IoT side (dnsmasq)

Give out addresses on 192.168.50.0/24 and let systemd-resolved handle DNS to the internet.

interface=wlx00c0cab7ab29
bind-interfaces
dhcp-range=192.168.50.100,192.168.50.240,255.255.255.0,12h
dhcp-option=3,192.168.50.1
dhcp-option=6,1.1.1.1,8.8.8.8
port=0  # leave port 53 to systemd-resolved
log-dhcp

3) Routing + NAT (nftables)

This makes every downstream device appear as the Pi upstream:

table inet filter {
  chain input {
    type filter hook input priority filter; policy accept;
    ct state established,related accept
    iif "lo" accept
    iif "wlan0" accept
    iif "wlx00c0cab7ab29" accept
    udp dport 5353 accept  # mDNS
    counter drop
  }
  chain forward {
    type filter hook forward priority filter; policy accept;
    ct state established,related accept
    iif "wlx00c0cab7ab29" oif "wlan0" accept
    iif "wlan0" oif "wlx00c0cab7ab29" accept
  }
  chain output { type filter hook output priority filter; policy accept; }
}
table ip nat {
  chain postrouting {
    type nat hook postrouting priority srcnat; policy accept;
    oif "wlan0" masquerade
  }
}

4) mDNS across subnets (Avahi)

The reflector bridges Bonjour so AirPlay/HomeKit can find each other across IoT and ASK4:

[server]
allow-interfaces=wlan0,wlx00c0cab7ab29
[reflector]
enable-reflector=yes

I wanted to be able to control my homepod without switching networks, but without setting a custom route on ASK4 WiFi it was not possible, hence the reason I chose Tailscale

Iet up Tailscale, advertise 192.168.50.0/24 from the Pi. Now I can log in with a passkey from my devices and control my homepod without switching networks, well, kinda. Boom! Secure access to IoT without leaving ASK4.

Now some random questions that I looked up in internet:

  1. How many devices can this really support?

Short answer: a lot—for IoT. Long answer: the DHCP pool here has 141 IPs (.100 to .240). Wi-Fi airtime is the real limit, not IPs. With a 2.4 GHz AP and mostly low-bandwidth gadgets (sensors, plugs, bulbs, ESPs), 5070 devices is completely reasonable. The ALFAs radio and hostapd handle concurrent associations fine; Ive watched dozens attach, chatter, and renew leases without drama.

If you plan camera-heavy traffic or bulk transfers, either split heavy gear onto 5 GHz or wire those devices. For small, chatty IoT traffic, this Pi/AP combo is very comfortable.

  1. What speeds should I expect?
  • LAN (IoT device ↔ Pi) on 2.4 GHz, 20 MHz channel, WPA2: ~2060 Mbps real throughput per device is typical. IoT stuff barely needs 1 Mbps, so I'm golden! ^^

  • Internet (IoT device → upstream via Pi): If the Pis upstream is Wi-Fi, which in my case is, the bottleneck is that link; expect ~3070 Mbps stable. If upstream is Ethernet, the Pi 4 can NAT hundreds of Mbps, and my AP radio becomes the limit.

  • Latency: Adding one NAT hop adds a little delay (a few ms). For HomePod/AirPlay, mDNS reflection + local Wi-Fi keeps it snappy.

  1. Why these choices?
  • 2.4 GHz is where tiny IoT radios live (bulbs, plugs, ESP/ESP32). It trades speed for range and compatibility.

  • WPA2-PSK keeps things simple; WPA3 is fine if all your clients support it.

  • 20 MHz channel prevents older or power-saving devices from flaking out.

  • Avahi reflector avoids any changes to the buildings router and still lets discovery work.

  • NAT collapses dozens of gadgets into a single upstream “seat,” sidestepping the 8-device limit entirely.

Troubleshooting

  • hostapd masked/disabled

    sudo systemctl unmask hostapd && sudo systemctl enable --now hostapd
    
  • dnsmasq cant start because of port 53
    set port=0 in /etc/dnsmasq.d/*.conf and let systemd-resolved keep DNS.

  • “Station does not support mandatory HT PHY” in hostapd logs →
    add require_ht=0 (and keep ieee80211n=1) in your hostapd.conf.

  • AirPlay/HomeKit dont show up across subnets →
    ensure Avahi reflector is enabled and UDP/5353 (mDNS) is allowed:

    • /etc/avahi/avahi-daemon.conf has:
      [server]
      allow-interfaces=wlan0,wlx00c0cab7ab29
      [reflector]
      enable-reflector=yes
      
    • firewall allows:
    udp dport 5353 accept
    
  • Weak signal / flaky joins →
    use a short USB 3 extension to move the ALFA away from the Pi and cables;
    scan and switch to a cleaner 2.4 GHz channel (1/6/11), keep width at 20 MHz.

  • Clients connect but no internet →
    verify IP forward & NAT:

sudo sysctl net.ipv4.ip_forward

If it's 0, enable it permanently and reload

echo 'net.ipv4.ip_forward=1' | sudo tee /etc/sysctl.d/99-iot.conf
sudo sysctl --system

Verify the nftables NAT rule exists (masquerade out via wlan0)

sudo nft list ruleset | sed -n '/table ip nat/,$p' | sed -n '1,80p'

Add it if missing

sudo nft add table ip nat
sudo nft 'add chain ip nat postrouting { type nat hook postrouting priority srcnat; policy accept; }'
sudo nft 'add rule ip nat postrouting oif "wlan0" masquerade'

Persist nftables (Ubuntu)

sudo sh -c 'nft list ruleset > /etc/nftables.conf'
sudo systemctl enable --now nftables

Quick service sanity checks

hostapd (AP)

sudo systemctl status hostapd --no-pager
sudo journalctl -u hostapd -n 60 --no-pager
iw dev wlx00c0cab7ab29 info
iw dev wlx00c0cab7ab29 station dump

dnsmasq (DHCP on IoT side)

sudo systemctl status dnsmasq --no-pager
sudo journalctl -u dnsmasq -n 80 --no-pager
grep -E 'interface=|dhcp-range' /etc/dnsmasq.d/*.conf || true
tail -n 50 /var/lib/misc/dnsmasq.leases

Avahi (mDNS reflector)

grep -E 'enable-reflector|allow-interfaces' /etc/avahi/avahi-daemon.conf
sudo systemctl status avahi-daemon --no-pager
sudo journalctl -u avahi-daemon -n 60 --no-pager

Basic connectivity tests from a client on IoT_hub

got DHCP?

ip addr show
ip route
cat /etc/resolv.conf

can you reach the Pi and the internet?

ping -c 3 192.168.50.1
ping -c 3 1.1.1.1
ping -c 3 google.com

DNS works end-to-end?


dig +short _airplay._tcp.local @224.0.0.251 -p 5353

(Or use a Bonjour browser app to confirm the HomePod is discoverable.)

If AirPlay/HomeKit discovery is flaky across subnets

make sure mDNS is allowed both directions on the Pi


sudo nft add rule inet filter input udp dport 5353 accept
sudo nft add rule inet filter forward udp dport 5353 accept
sudo sh -c 'nft list ruleset > /etc/nftables.conf'

restart Avahi cleanly and re-announce

sudo systemctl restart avahi-daemon
sudo journalctl -u avahi-daemon -n 40 --no-pager

from the Pi, watch mDNS traffic on both sides (you should see queries/answers)

sudo tcpdump -ni wlx00c0cab7ab29 udp port 5353 -vvv

(in another terminal:)

sudo tcpdump -ni wlan0 udp port 5353 -vvv

Throughput + airtime sanity (optional)

simple iperf3 (install on Pi and a client)

sudo apt-get update && sudo apt-get install -y iperf3

(on the Pi:) iperf3 -s (on a client:) iperf3 -c 192.168.50.1 -P 3 -t 10

check channel utilization/interference (from Pi)

sudo iw dev wlx00c0cab7ab29 survey dump | sed -n '1,200p'

(if busy, try channel 1 or 11 in hostapd.conf and restart hostapd)

Scaling tips (lots of tiny IoT devices)

hostapd tweaks (add to hostapd.conf, then restart)

(reduce management overhead slightly; keep conservative for compatibility)

beacon_int=100
dtim_period=2
wmm_enabled=1
ap_isolate=0
max_num_sta=256      # logical cap; real limit is airtime/driver
sudo systemctl restart hostapd

if some ESP/low-power clients are sticky or nap too hard:

(device-side) disable aggressive power saving if supported
(AP-side) keep channel width at 20 MHz and require_ht=0 for legacy clients

“Clients connect but no internet” quick checklist

Pi has a default route via upstream?

ip route | grep default

NAT counters are increasing when clients surf?

sudo nft list chain ip nat postrouting -a

connections tracked?

sudo conntrack -L -o extended | head -n 40

last resort: bounce the pieces

sudo systemctl restart hostapd dnsmasq avahi-daemon
sudo systemctl restart NetworkManager || true
sudo systemctl restart nftables

Network Traffic (embedded from captures)

For fun, and curiousity!

1) DHCP leases (who joined the IoT network)

1763936213 6a:12:f6:62:40:88 192.168.50.216 * 01:6a:12:f6:62:40:88
1763935691 72:00:a6:33:e5:7a 192.168.50.222 * 01:72:00:a6:33:e5:7a
1763935629 e8:06:90:69:66:7c 192.168.50.189 Aidot 01:e8:06:90:69:66:7c
1763936452 ac:bc:b5:e5:38:29 192.168.50.202 Main-Bedroom-2 01:ac:bc:b5:e5:38:29
1763935618 e8:06:90:68:06:2c 192.168.50.196 * 01:e8:06:90:68:06:2c
1763935618 48:e1:e9:bc:f6:91 192.168.50.131 * *
1763935618 48:e1:e9:bd:08:0d 192.168.50.118 * *
1763935615 48:e1:e9:bd:0b:a7 192.168.50.233 * *
1763935618 7c:f1:7e:ff:e1:b0 192.168.50.104 KH100 *
1763935607 cc:b5:d1:31:a4:cc 192.168.50.134 espressif 01:cc:b5:d1:31:a4:cc

2) Top talkers by bytes (hosts table)

192.168.50.233,7346
192.168.50.118,7216
192.168.50.131,7216
17.253.53.203,7040
54.217.122.41,6271
192.168.50.216,5392
192.168.50.104,3058
192.168.50.189,2278
17.242.218.132,1802
192.168.50.134,1750
192.168.50.196,1653
3.122.71.119,639
18.196.19.65,570
161.117.178.131,391
17.253.53.201,148
17.57.146.25,90
10.172.72.196,66
224.0.0.251,0
224.0.0.2,0
255.255.255.255,0

3) Heaviest flows (src, dst, proto, dport, bytes)

192.168.50.202,192.168.50.131,TCP,59903,402
192.168.50.202,192.168.50.118,TCP,59807,402
161.117.178.131,192.168.50.134,TCP,58246,391
192.168.50.216,192.168.50.202,UDP,5353,336
192.168.50.202,192.168.50.189,UDP,5353,312
192.168.50.202,192.168.50.196,UDP,5353,234
17.253.53.203,192.168.50.202,TCP,64726,222
192.168.50.216,17.57.146.25,TCP,5223,172
192.168.50.216,192.168.50.233,UDP,5353,162
192.168.50.202,192.168.50.104,UDP,5353,156
17.253.53.201,192.168.50.202,TCP,64727,148
192.168.50.202,17.253.53.201,TCP,443,132
192.168.50.216,224.0.0.251,IP,0,92
192.168.50.216,224.0.0.2,IP,0,92
17.57.146.25,192.168.50.216,TCP,64544,90
192.168.50.216,192.168.50.118,UDP,5353,75
192.168.50.216,192.168.50.131,UDP,5353,75
192.168.50.216,192.168.50.134,UDP,5353,75
192.168.50.216,10.172.72.196,TCP,59593,70
10.172.72.196,192.168.50.216,TCP,51638,66

4) Conntrack snapshot (active connections inventory)

ipv4     2 tcp      6 48 TIME_WAIT src=10.172.71.98 dst=185.125.190.39 sport=55012 dport=80 src=185.125.190.39 dst=10.172.71.98 sport=80 dport=55012 [ASSURED] mark=0 use=1
ipv4     2 udp      17 100 src=10.172.71.98 dst=185.40.234.113 sport=41641 dport=3478 src=185.40.234.113 dst=10.172.71.98 sport=3478 dport=41641 [ASSURED] mark=0 use=1
ipv4     2 tcp      6 431665 ESTABLISHED src=192.168.50.216 dst=17.57.146.25 sport=64544 dport=5223 src=17.57.146.25 dst=10.172.71.98 sport=5223 dport=64544 [ASSURED] mark=0 use=1
ipv4     2 udp      17 27 src=10.172.72.205 dst=224.0.0.251 sport=5353 dport=5353 [UNREPLIED] src=224.0.0.251 dst=10.172.72.205 sport=5353 dport=5353 mark=0 use=1
ipv4     2 udp      17 17 src=10.172.72.196 dst=10.172.79.255 sport=137 dport=137 [UNREPLIED] src=10.172.79.255 dst=10.172.72.196 sport=137 dport=137 mark=0 use=1
ipv4     2 tcp      6 431999 ESTABLISHED src=192.168.50.196 dst=18.196.19.65 sport=55164 dport=8883 src=18.196.19.65 dst=10.172.71.98 sport=8883 dport=55164 [ASSURED] mark=0 use=1
ipv4     2 udp      17 16 src=127.0.0.1 dst=127.0.0.53 sport=41796 dport=53 src=127.0.0.53 dst=127.0.0.1 sport=53 dport=41796 mark=0 use=1
ipv4     2 unknown  2 265 src=192.168.50.216 dst=224.0.0.251 [UNREPLIED] src=224.0.0.251 dst=192.168.50.216 mark=0 use=1
ipv4     2 udp      17 5 src=192.168.50.104 dst=1.1.1.1 sport=23791 dport=53 src=1.1.1.1 dst=10.172.71.98 sport=53 dport=23791 mark=0 use=1
ipv4     2 udp      17 119 src=10.172.72.205 dst=10.172.71.98 sport=41641 dport=41641 src=10.172.71.98 dst=10.172.72.205 sport=41641 dport=41641 [ASSURED] mark=0 use=1
ipv4     2 udp      17 27 src=10.172.71.98 dst=224.0.0.251 sport=5353 dport=5353 [UNREPLIED] src=224.0.0.251 dst=10.172.71.98 sport=5353 dport=5353 mark=0 use=1
ipv4     2 tcp      6 431946 ESTABLISHED src=192.168.50.202 dst=17.242.218.132 sport=64686 dport=5223 src=17.242.218.132 dst=10.172.71.98 sport=5223 dport=64686 [ASSURED] mark=0 use=1
ipv4     2 udp      17 10 src=10.172.71.98 dst=239.255.255.250 sport=40234 dport=1900 [UNREPLIED] src=239.255.255.250 dst=10.172.71.98 sport=1900 dport=40234 mark=0 use=1
ipv4     2 tcp      6 96 TIME_WAIT src=192.168.50.104 dst=3.217.113.91 sport=49707 dport=443 src=3.217.113.91 dst=10.172.71.98 sport=443 dport=49707 [ASSURED] mark=0 use=1
ipv4     2 udp      17 10 src=10.172.71.98 dst=10.172.64.1 sport=40234 dport=1900 [UNREPLIED] src=10.172.64.1 dst=10.172.71.98 sport=1900 dport=40234 mark=0 use=1
ipv4     2 tcp      6 431812 ESTABLISHED src=100.69.238.103 dst=192.168.50.202 sport=55514 dport=49395 src=192.168.50.202 dst=192.168.50.1 sport=49395 dport=55514 [ASSURED] mark=0 use=1
ipv4     2 tcp      6 431666 ESTABLISHED src=192.168.50.216 dst=10.172.72.196 sport=51638 dport=59593 src=10.172.72.196 dst=10.172.71.98 sport=59593 dport=51638 [ASSURED] mark=0 use=1
ipv4     2 tcp      6 431998 ESTABLISHED src=100.69.238.103 dst=192.168.50.202 sport=55510 dport=7000 src=192.168.50.202 dst=192.168.50.1 sport=7000 dport=55510 [ASSURED] mark=0 use=1
ipv4     2 udp      17 14 src=192.168.50.131 dst=224.0.0.251 sport=5353 dport=5353 [UNREPLIED] src=224.0.0.251 dst=192.168.50.131 sport=5353 dport=5353 mark=0 use=1
ipv4     2 unknown  2 596 src=10.172.72.205 dst=224.0.0.251 [UNREPLIED] src=224.0.0.251 dst=10.172.72.205 mark=0 use=1
ipv4     2 udp      17 27 src=192.168.50.1 dst=224.0.0.251 sport=5353 dport=5353 [UNREPLIED] src=224.0.0.251 dst=192.168.50.1 sport=5353 dport=5353 mark=0 use=8
ipv4     2 udp      17 27 src=10.172.72.205 dst=10.172.71.98 sport=5353 dport=5353 [UNREPLIED] src=10.172.71.98 dst=10.172.72.205 sport=5353 dport=5353 mark=0 use=1
ipv4     2 udp      17 15 src=192.168.50.118 dst=224.0.0.251 sport=5353 dport=5353 [UNREPLIED] src=224.0.0.251 dst=192.168.50.118 sport=5353 dport=5353 mark=0 use=1
ipv4     2 udp      17 88 src=10.172.71.98 dst=109.43.114.250 sport=41641 dport=41641 src=109.43.114.250 dst=10.172.71.98 sport=41641 dport=41641 [ASSURED] mark=0 use=1
ipv4     2 tcp      6 431997 ESTABLISHED src=192.168.50.104 dst=18.234.9.109 sport=49700 dport=443 src=18.234.9.109 dst=10.172.71.98 sport=443 dport=49700 [ASSURED] mark=0 use=1
ipv4     2 tcp      6 431998 ESTABLISHED src=10.172.71.98 dst=199.165.136.101 sport=45248 dport=443 src=199.165.136.101 dst=10.172.71.98 sport=443 dport=45248 [ASSURED] mark=0 use=1
ipv4     2 tcp      6 431992 ESTABLISHED src=10.172.72.196 dst=10.172.71.98 sport=63953 dport=22 src=10.172.71.98 dst=10.172.72.196 sport=22 dport=63953 [ASSURED] mark=0 use=1
ipv4     2 tcp      6 431995 ESTABLISHED src=192.168.50.189 dst=3.122.71.119 sport=50422 dport=8883 src=3.122.71.119 dst=10.172.71.98 sport=8883 dport=50422 [ASSURED] mark=0 use=1
ipv4     2 udp      17 100 src=10.172.71.98 dst=176.58.90.104 sport=41641 dport=3478 src=176.58.90.104 dst=10.172.71.98 sport=3478 dport=41641 [ASSURED] mark=0 use=1
ipv4     2 tcp      6 107 TIME_WAIT src=10.172.71.98 dst=185.125.190.36 sport=50914 dport=80 src=185.125.190.36 dst=10.172.71.98 sport=80 dport=50914 [ASSURED] mark=0 use=1
ipv4     2 udp      17 26 src=192.168.50.202 dst=192.168.50.1 sport=5353 dport=5353 [UNREPLIED] src=192.168.50.1 dst=192.168.50.202 sport=5353 dport=5353 mark=0 use=1
ipv4     2 udp      17 16 src=127.0.0.1 dst=127.0.0.53 sport=33321 dport=53 src=127.0.0.53 dst=127.0.0.1 sport=53 dport=33321 mark=0 use=1
ipv4     2 tcp      6 431975 ESTABLISHED src=192.168.50.134 dst=161.117.178.131 sport=58246 dport=11883 src=161.117.178.131 dst=10.172.71.98 sport=11883 dport=58246 [ASSURED] mark=0 use=1
ipv4     2 udp      17 16 src=10.172.71.98 dst=10.172.64.1 sport=35510 dport=53 src=10.172.64.1 dst=10.172.71.98 sport=53 dport=35510 mark=0 use=1
ipv4     2 udp      17 100 src=10.172.71.98 dst=176.58.93.154 sport=41641 dport=3478 src=176.58.93.154 dst=10.172.71.98 sport=3478 dport=41641 [ASSURED] mark=0 use=1
ipv4     2 tcp      6 431652 ESTABLISHED src=100.69.238.103 dst=192.168.50.202 sport=55513 dport=49394 src=192.168.50.202 dst=192.168.50.1 sport=49394 dport=55513 [ASSURED] mark=0 use=1
ipv4     2 udp      17 23 src=192.168.50.104 dst=255.255.255.255 sport=21112 dport=20002 [UNREPLIED] src=255.255.255.255 dst=192.168.50.104 sport=20002 dport=21112 mark=0 use=1
ipv4     2 udp      17 10 src=10.172.71.98 dst=10.172.64.1 sport=40234 dport=5351 [UNREPLIED] src=10.172.64.1 dst=10.172.71.98 sport=5351 dport=40234 mark=0 use=1
ipv4     2 udp      17 10 src=10.172.71.98 dst=10.172.64.1 sport=34331 dport=5351 [UNREPLIED] src=10.172.64.1 dst=10.172.71.98 sport=5351 dport=34331 mark=0 use=1
ipv4     2 tcp      6 431975 ESTABLISHED src=10.172.71.98 dst=192.200.0.115 sport=48100 dport=443 src=192.200.0.115 dst=10.172.71.98 sport=443 dport=48100 [ASSURED] mark=0 use=1
ipv4     2 tcp      6 431999 ESTABLISHED src=10.172.71.98 dst=176.58.93.154 sport=58128 dport=443 src=176.58.93.154 dst=10.172.71.98 sport=443 dport=58128 [ASSURED] mark=0 use=1
ipv4     2 tcp      6 431997 ESTABLISHED src=100.69.238.103 dst=192.168.50.202 sport=55515 dport=49154 src=192.168.50.202 dst=192.168.50.1 sport=49154 dport=55515 [ASSURED] mark=0 use=1
ipv4     2 tcp      6 118 TIME_WAIT src=192.168.50.104 dst=3.217.113.91 sport=49708 dport=443 src=3.217.113.91 dst=10.172.71.98 sport=443 dport=49708 [ASSURED] mark=0 use=1

5) Part of Raw packet capture

  • Whats inside?
tshark -r iot_ap_120s.pcap -q -z io,stat,60


==================================
| IO Statistics                  |
|                                |
| Duration: 119.773534 secs      |
| Interval:  60 secs             |
|                                |
| Col 1: Frames and bytes        |
|--------------------------------|
|            |1                  |
| Interval   | Frames |   Bytes  |
|--------------------------------|
|   0 <>  60 |    400 |    60449 |
|  60 <> Dur |  24672 | 30179911 |
==================================
  • Protocol hierarchy (what kinds of traffic?)
 tshark -r iot_ap_120s.pcap -q -z io,phs

===================================================================
Protocol Hierarchy Statistics
Filter: 

eth                                      frames:25072 bytes:30240360
  ip                                     frames:24706 bytes:30195993
    udp                                  frames:349 bytes:94583
      mdns                               frames:241 bytes:60693
      data                               frames:32 bytes:2240
      quic                               frames:76 bytes:31650
        quic                             frames:2 bytes:2484
    tcp                                  frames:24353 bytes:30101226
      tls                                frames:2413 bytes:3493680
        tcp.segments                     frames:2256 bytes:3378795
          tls                            frames:2222 bytes:3353799
      data                               frames:147 bytes:71717
      tcp.segments                       frames:5 bytes:7550
    igmp                                 frames:4 bytes:184
  arp                                    frames:150 bytes:6300
  ipv6                                   frames:216 bytes:38067
    icmpv6                               frames:124 bytes:10704
    udp                                  frames:79 bytes:25415
      data                               frames:2 bytes:132
      mdns                               frames:77 bytes:25283
    tcp                                  frames:13 bytes:1948
      data                               frames:1 bytes:90
===================================================================
  • Top IP conversations (who talked to whom?)
 tshark -r iot_ap_120s.pcap  -q -z conv,ip
================================================================================
IPv4 Conversations
Filter:<No Filter>
                                               |       <-      | |       ->      | |     Total     |    Relative    |   Duration   |
                                               | Frames  Bytes | | Frames  Bytes | | Frames  Bytes |      Start     |              |
192.168.50.202       <-> 17.253.37.195          19588 29 MB        3912 286 kB      23500 29 MB        72.555738000        21.8560
192.168.50.1         <-> 192.168.50.202           239 74 kB         238 46 kB         477 120 kB        0.293866000       119.4797
192.168.50.1         <-> 224.0.0.251                0 0 bytes       118 28 kB         118 28 kB         0.000000000       119.7310
192.168.50.202       <-> 1.1.1.1                   40 20 kB          36 10 kB          76 31 kB        72.033321000        30.3367
192.168.50.202       <-> 2.19.120.151              47 44 kB          29 8,460 bytes      76 53 kB        72.062807000        14.8525
192.168.50.202       <-> 17.56.138.35              27 7,602 bytes      33 13 kB          60 20 kB        72.107270000        15.0196
192.168.50.202       <-> 17.253.53.203             17 7,040 bytes      23 9,256 bytes      40 16 kB        72.326823000        10.5130
192.168.50.202       <-> 54.217.122.41             19 6,271 bytes      18 6,122 bytes      37 12 kB       102.352119000         0.3678
192.168.50.202       <-> 17.253.53.202             19 22 kB          17 3,099 bytes      36 25 kB        81.940717000         0.3208
192.168.50.202       <-> 23.58.105.122             19 9,936 bytes      16 5,759 bytes      35 15 kB        72.225431000         9.6434
192.168.50.104       <-> 255.255.255.255            0 0 bytes        32 2,240 bytes      32 2,240 bytes    50.628126000        65.2664
17.242.218.132       <-> 192.168.50.202            12 1,701 bytes      13 1,802 bytes      25 3,503 bytes    20.967857000        62.0463
192.168.50.189       <-> 3.122.71.119               8 639 bytes      13 978 bytes      21 1,617 bytes     1.112174000       108.8174
192.168.50.131       <-> 224.0.0.251                0 0 bytes        20 6,092 bytes      20 6,092 bytes    19.033978000        79.6932
192.168.50.118       <-> 224.0.0.251                0 0 bytes        20 6,092 bytes      20 6,092 bytes    79.030835000        19.7664
192.168.50.233       <-> 224.0.0.251                0 0 bytes        20 6,052 bytes      20 6,052 bytes    79.031250000        19.7814
192.168.50.196       <-> 18.196.19.65               8 570 bytes      10 678 bytes      18 1,248 bytes     4.963201000       107.5242
192.168.50.216       <-> 224.0.0.251                0 0 bytes        12 3,880 bytes      12 3,880 bytes    98.049857000         4.2949
192.168.50.134       <-> 161.117.178.131            5 391 bytes       4 960 bytes       9 1,351 bytes    15.392290000        60.5936
192.168.50.202       <-> 192.168.50.189             4 1,300 bytes       4 312 bytes       8 1,612 bytes    26.274469000        93.2919
192.168.50.202       <-> 192.168.50.118             2 406 bytes       4 402 bytes       6 808 bytes    17.999757000        60.1593
192.168.50.202       <-> 192.168.50.131             2 406 bytes       4 402 bytes       6 808 bytes    17.999812000        60.1593
192.168.50.202       <-> 192.168.50.233             2 406 bytes       4 402 bytes       6 808 bytes    17.999825000        60.1592
192.168.50.202       <-> 192.168.50.196             3 975 bytes       3 234 bytes       6 1,209 bytes    30.223646000        89.3442
192.168.50.216       <-> 192.168.50.233             4 888 bytes       2 162 bytes       6 1,050 bytes    98.322145000         0.1456
192.168.50.216       <-> 192.168.50.202             1 1,092 bytes       4 336 bytes       5 1,428 bytes    98.319211000         0.2939
192.168.50.216       <-> 192.168.50.1               0 0 bytes         5 455 bytes       5 455 bytes    98.319329000         0.2505
192.168.50.202       <-> 17.253.53.201              2 148 bytes       2 132 bytes       4 280 bytes    81.716483000         1.0465
192.168.50.202       <-> 192.168.50.104             2 818 bytes       2 156 bytes       4 974 bytes    87.030561000         0.0107
192.168.50.216       <-> 192.168.50.131             3 718 bytes       1 75 bytes        4 793 bytes    98.321918000         0.1421
192.168.50.216       <-> 192.168.50.118             3 718 bytes       1 75 bytes        4 793 bytes    98.322266000         0.1420
192.168.50.216       <-> 17.57.146.25               1 90 bytes        2 172 bytes       3 262 bytes    98.048979000         0.0384
192.168.50.216       <-> 192.168.50.134             2 790 bytes       1 75 bytes        3 865 bytes    98.321779000         0.2550
192.168.50.216       <-> 224.0.0.2                  0 0 bytes         2 92 bytes        2 92 bytes     98.049757000         0.0001
192.168.50.216       <-> 10.172.72.196              1 66 bytes        1 70 bytes        2 136 bytes    98.664295000         0.0060
================================================================================
  • Fast “top talkers” table (by source IP)
 tshark -r iot_ap_120s.pcap  -T fields -e ip.src -e frame.len \
  | awk 'NF==2{b[$1]+=$2} END{for (h in b) printf "%-16s %12d\n", h, b[h]}' \
  | sort -k2,2nr | head -n 20
17.253.37.195        29540349
192.168.50.202         422414
192.168.50.1            75521
2.19.120.151            44554
17.253.53.202           22016
1.1.1.1                 20675
23.58.105.122            9936
17.56.138.35             7602
192.168.50.233           7346
192.168.50.118           7216
192.168.50.131           7216
17.253.53.203            7040
54.217.122.41            6271
192.168.50.216           5392
192.168.50.104           3058
192.168.50.189           2278
17.242.218.132           1802
192.168.50.134           1750
192.168.50.196           1653
3.122.71.119              639
  • DNS & mDNS highlights (what names show up?)
# DNS queries (unicast)
 tshark -r iot_ap_120s.pcap -Y "dns.flags.response==0" \
  -T fields -e frame.time -e ip.src -e dns.qry.name | head -n 20
Nov 23, 2025 11:48:38.131162000 CET	192.168.50.1	_hap._tcp.local,_rdlink._tcp.local,_companion-link._tcp.local,_hap._udp.local
Nov 23, 2025 11:48:42.961690000 CET	192.168.50.1	_hap._tcp.local
Nov 23, 2025 11:48:42.962245000 CET	192.168.50.1	_companion-link._tcp.local
Nov 23, 2025 11:48:42.962911000 CET	192.168.50.1	_hap._udp.local,_rdlink._tcp.local
Nov 23, 2025 11:48:43.098378000 CET	192.168.50.1	MSS110-f691._hap._tcp.local
Nov 23, 2025 11:48:43.158641000 CET	192.168.50.1	Qingping Air Monitor Lite._hap._tcp.local
Nov 23, 2025 11:48:43.201440000 CET	192.168.50.202	_companion-link._tcp.local
Nov 23, 2025 11:48:43.532623000 CET	192.168.50.1	MSS110-080d._hap._tcp.local
Nov 23, 2025 11:48:43.962673000 CET	192.168.50.1	_hap._tcp.local,MSS110-0ba7._hap._tcp.local,Main Bedroom (3)._companion-link._tcp.local,HomePodSensor 185277._hap._tcp.local,_rdlink._tcp.local,_companion-link._tcp.local,_hap._udp.local
Nov 23, 2025 11:48:49.110167000 CET	192.168.50.1	_hap._tcp.local
Nov 23, 2025 11:48:49.110760000 CET	192.168.50.1	_companion-link._tcp.local
Nov 23, 2025 11:48:49.111395000 CET	192.168.50.1	_hap._udp.local,_rdlink._tcp.local
Nov 23, 2025 11:48:49.272920000 CET	192.168.50.1	Qingping Air Monitor Lite._hap._tcp.local
Nov 23, 2025 11:48:49.311002000 CET	192.168.50.1	MSS110-0ba7._hap._tcp.local
Nov 23, 2025 11:48:49.619376000 CET	192.168.50.1	Main Bedroom (3)._companion-link._tcp.local,MSS110-f691._hap._tcp.local,MSS110-080d._hap._tcp.local,HomePodSensor 185277._hap._tcp.local
Nov 23, 2025 11:48:50.115506000 CET	192.168.50.1	_hap._tcp.local,_rdlink._tcp.local,_companion-link._tcp.local,_hap._udp.local
Nov 23, 2025 11:48:55.037844000 CET	192.168.50.1	_hap._tcp.local
Nov 23, 2025 11:48:55.038380000 CET	192.168.50.1	_companion-link._tcp.local
Nov 23, 2025 11:48:55.038926000 CET	192.168.50.1	_hap._udp.local,_rdlink._tcp.local
Nov 23, 2025 11:48:55.060503000 CET	192.168.50.202	_companion-link._tcp.local

# mDNS service announcements (AirPlay/HomeKit live here)
 tshark -r iot_ap_120s.pcap  -Y "udp.port==5353 && dns" \
  -T fields -e frame.time -e ip.src -e dns.qry.name -e dns.ptr.domain_name \
  | head -n 30

The latter had no output as I wasn't able to make that work with a static route and relied on Tailscale. (?)

  • TLS SNI (which cloud endpoints?)
 tshark -r iot_ap_120s.pcap -Y "tls.handshake.extensions_server_name" \
  -T fields -e ip.dst -e tls.handshake.extensions_server_name \
  | sort | uniq -c | sort -k1,1nr | head -n 20
   1 1.1.1.1	one.one.one.one
   1 17.253.37.195	streamingaudio.itunes.apple.com
   1 17.253.53.202	pancake.apple.com
   1 17.253.53.203	radio-activity.itunes.apple.com
   1 17.56.138.35	cma.itunes.apple.com
   1 2.19.120.151	play.itunes.apple.com
   1 23.58.105.122	librarydaap.itunes.apple.com
   1 54.217.122.41	guzzoni.apple.com
  • Ports in use (quick heat check)
 tshark -r iot_ap_120s.pcap -T fields -e _ws.col.Protocol -e tcp.dstport -e udp.dstport \
  | awk '{for(i=1;i<=NF;i++) if($i~/^[0-9]+$/) p[$i]++} END{for(k in p) printf "%-8s %8d\n", k, p[k]}' \
  | sort -k2,2nr | head -n 20
64725       19588
443          4086
5353          318
49395         109
7000          109
55514         102
55510         101
64721          47
54806          40
20002          32
64722          27
8883           23
49154          21
55515          21
64723          19
64728          19
64729          19
5223           14
64724          14
64686          13

Epilogue

I've been having so so much trouble with setting up a iot-hub for my house where I can connect all my IoT devices to, and without putting a phone on hotspot. Now I finally did it, and I even treated myself by allowing mDNS routing! Let's cross our fingers and hope this setup finally works nicely.


{{< sigdl >}}