--- title: "I Beat My 8-Device Wi-Fi Limit with a Raspberry Pi (and Made an IoT Village)" date: 2025-11-23 draft: false tags: ["raspberry pi", "wifi", "iot", "networking", "hostapd", "dnsmasq"] description: "Real-world guide: hardware choices, reasons, setup details, performance, and how many devices you can realistically support." author: "Iman Alipour" ShowToc: true TocOpen: true --- ## The Day My Wi-Fi Said “Eight Is Enough” My apartment Wi-Fi (ASK4) has a hard cap of **8 devices**. That’s 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 building’s 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 Pi’s 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. - **16–32 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 building’s **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 I’m 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. ```ini interface=wlx00c0cab7ab29 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= ``` ### 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. ```bash 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: ```bash 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: ```bash [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), 50–70 devices is completely reasonable. The ALFA’s radio and hostapd handle concurrent associations fine; I’ve 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. 2. What speeds should I expect? - LAN (IoT device ↔ Pi) on 2.4 GHz, 20 MHz channel, WPA2: ~20–60 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 Pi’s upstream is Wi-Fi, which in my case is, the bottleneck is that link; expect ~30–70 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. 3. 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 building’s 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** → ```bash sudo systemctl unmask hostapd && sudo systemctl enable --now hostapd ``` - `dnsmasq` can’t 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 don’t show up across subnets → ensure Avahi reflector is enabled and UDP/5353 (mDNS) is allowed: - `/etc/avahi/avahi-daemon.conf` has: ```bash [server] allow-interfaces=wlan0,wlx00c0cab7ab29 [reflector] enable-reflector=yes ``` - firewall allows: ```bash 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: ```bash sudo sysctl net.ipv4.ip_forward ``` If it's 0, enable it permanently and reload ```bash 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) ```bash sudo nft list ruleset | sed -n '/table ip nat/,$p' | sed -n '1,80p' ``` Add it if missing ```bash 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) ```bash sudo sh -c 'nft list ruleset > /etc/nftables.conf' sudo systemctl enable --now nftables ``` ## Quick service sanity checks ### hostapd (AP) ```bash 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) ```bash 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) ```bash 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? ```bash ip addr show ip route cat /etc/resolv.conf ``` ### can you reach the Pi and the internet? ```bash ping -c 3 192.168.50.1 ping -c 3 1.1.1.1 ping -c 3 google.com ``` ### DNS works end-to-end? ```bash 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 ```bash 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 ```bash 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) ```bash sudo tcpdump -ni wlx00c0cab7ab29 udp port 5353 -vvv ``` (in another terminal:) ```bash sudo tcpdump -ni wlan0 udp port 5353 -vvv ``` ## Throughput + airtime sanity (optional) ### simple iperf3 (install on Pi and a client) ```bash 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) ```bash 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) ```bash beacon_int=100 dtim_period=2 wmm_enabled=1 ap_isolate=0 max_num_sta=256 # logical cap; real limit is airtime/driver ``` ```bash 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? ```bash ip route | grep default ``` ### NAT counters are increasing when clients surf? ```bash sudo nft list chain ip nat postrouting -a ``` ### connections tracked? ```bash sudo conntrack -L -o extended | head -n 40 ``` ### last resort: bounce the pieces ```bash 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) ```bash 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) ```bash 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) ```bash 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) ```bash 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 - What’s inside? ```bash 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?) ```bash ❯ 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?) ```bash ❯ tshark -r iot_ap_120s.pcap -q -z conv,ip ================================================================================ IPv4 Conversations 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) ```bash ❯ 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?) ```bash # 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?) ```bash ❯ 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) ```bash ❯ 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 >}}