mysite/content/posts/house_upgrade.md

685 lines
32 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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**. 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.
```ini
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.
```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), 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.
2. 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.
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 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**
```bash
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:
```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
- Whats 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:<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)
```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 >}}