The Reply That Never Came Back: Auditing Inbound SMS on a NAT-Trapped DIY Gateway
Photo: www.digitalpush.net, CC BY 4.0, via Wikimedia Commons
Outbound SMS is a terrible liar. It will fire alerts, OTPs, and status pings all day while the return path is completely hosed. Then a customer texts STOP, a user replies YES, or an upstream provider sends a callback, and nothing shows up. The instinct is to open a carrier ticket. Before you do, draw the inbound path on paper. In a DIY gateway, that path is often shorter, stranger, and more fragile than you think.
First, Name the Inbound You Actually Need
There are two inbound SMS paths in most DIY setups, and they fail for different reasons. If the SIM lives in your modem, the message arrives over the air interface. Your gateway reads it from the modem or SIM storage. Carrier-grade NAT is irrelevant to that path. If the modem has signal and the SIM is provisioned, an inbound SMS does not need a port forward.
If your gateway relies on an HTTP callback from an SMS provider, an aggregator, or a carrier portal, that is a different animal. That callback is an inbound TCP connection to your public endpoint. CGNAT can absolutely swallow it. Many racks use both paths: a local modem for one campaign, a provider API for another, and a webhook for delivery receipts. Audit each path separately. Otherwise you will spend a week proving the wrong thing.
The Private WAN Tell
Log into the LTE router and look at its WAN address. If it sits in 100.64.0.0/10, you are on carrier-grade NAT shared address space. If it starts with 10., 172.16 through 172.31, or 192.168, you are behind private addressing too. Then run a quick public-IP check from the gateway itself and compare. If the router WAN address and the public address disagree, your port-forwarding rule is decoration.
Even when the router reports something that looks public, do not trust the label. Cheap LTE routers are famous for optimistic status pages. US mobile ISPs commonly put consumer and small-business LTE lines behind CGNAT. Some business APNs offer a static public IPv4 address for a fee, and some hand out public IPv6 while leaving IPv4 behind NAT. If your gateway only listens on IPv4, that IPv6 gift does not help you.
Port Forwarding Is a Claim, Not a Proof
A port-forwarding rule says the router intends to send traffic to your gateway. It does not prove the carrier will let that traffic in. Test from outside your LAN. Use a small US VPS or a remote shell on a different network. Run nc -vz your-public-ip your-port for TCP. For UDP, use a matching UDP listener and a client that can send a known payload. Watch tcpdump on the gateway while you test. If the packets never arrive, stop tuning the app and fix the path.
Check for double NAT too. An LTE router plugged into another router is a classic DIY setup, and the second NAT layer is easy to forget. Confirm the forward points at the gateway's current LAN address, not last month's DHCP lease. Confirm the host firewall allows the port. Confirm the service is actually listening on the interface you think it is. These are boring checks, but they save you from arguing with a carrier about a rule that never had a chance.
Keepalive Tests That Mean Something
TCP keepalive on the gateway does not create an inbound mapping on the carrier side. It keeps your side of a connection alive, which is not the same thing. If you depend on a NAT mapping staying open, test it after real idle time. Set an external probe every minute for 24 hours, log successes and failures, then send a canary SMS from a phone you control. If the probe works but the SMS callback never lands, your problem is upstream or application-level. If both go silent after 30 to 60 minutes of idle, you have a mapping or carrier filtering issue.
For webhook traffic, put a unique token in the callback URL and log every request at the edge. Ask the provider for delivery receipts. A receipt tells you the provider accepted the message. It does not tell you your gateway received the callback. Those are different failure points, and mixing them up is how people end up replacing modems for no reason.
Fallbacks That Keep STOP Messages From Vanishing
Do not make inbound SMS depend on an inbound IP address if you can avoid it. If your provider exposes an inbox API, poll it from the gateway over outbound HTTPS. If you need remote access to the rack, use a reverse tunnel or a WireGuard link to a public VPS instead of exposing ports on an LTE router. If you must receive provider callbacks, put a relay on a public VPS and forward them to the gateway over the tunnel. That gives you one public door to defend and one private path to the rack.
For SIM-based STOP handling, process the message on the modem and sync the result outbound. Keep a canary SIM that sends a known reply on a schedule. Alert when the expected inbound message does not appear. In the US, a vanished STOP is not just a monitoring gap. It is a compliance problem wearing a networking costume. Treat inbound SMS as a first-class service with its own health check, not as a bonus feature of outbound traffic.
A Monthly Audit You Can Actually Finish
- Print the inbound path: carrier, APN, router WAN address, NAT layer, gateway listener, application handler.
- Compare the router WAN address with a public-IP check from the gateway.
- Test the public endpoint from a remote US VPS, not from the office LAN.
- Run a 24-hour idle keepalive probe and a canary SMS test.
- Confirm the fallback path works with the primary path disabled.
- Document the result in the same place you keep SIM inventory and carrier contacts.
The uncomfortable truth is that inbound SMS usually breaks quietly. Nobody notices until a reply, an opt-out, or a two-factor confirmation goes missing. Map the path before that happens. The carrier may still be guilty, but at least you will know which door the message was supposed to come through.