Fix dial-up modem path end-to-end: answer tone, PPP runtime, transparent V.22bis data - #26
Closed
nicolasumaras wants to merge 6 commits into
Closed
Fix dial-up modem path end-to-end: answer tone, PPP runtime, transparent V.22bis data#26nicolasumaras wants to merge 6 commits into
nicolasumaras wants to merge 6 commits into
Conversation
`modem_connect_tone` in spandsp's `v8_parms_t` is a `MODEM_CONNECT_TONES_*` enum, not a boolean. The worker set it to `true` (== 1 == MODEM_CONNECT_TONES_FAX_CNG), so as the V.8 answerer it emitted the fax calling tone (1100 Hz, 0.5s on / 3s off) instead of a modem answer tone. Calling modems never received a valid answer tone, so V.8 always failed and the worker fell back to V.21, holding a 1650 Hz mark the caller could not train against. Captured RTP confirmed the 1100 Hz CNG cadence outbound and near-total silence inbound (the modem never engaged). Set it to MODEM_CONNECT_TONES_ANSAM_PR (2100 Hz ANSam with phase reversals), the standard V.8 answer tone. Verified offline (worker now emits continuous 2100 Hz ANSam) and on a live Windows dial-up call: V.8 now succeeds and selects V.22bis at 2400 baud (v8-v22bis-selected) instead of failing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
V.22bis is a synchronous modem, but Windows dial-up carries asynchronous PPP (8N1). The worker fed the raw V.22bis bit stream straight into an 8-bit accumulator with no start/stop handling, so characters never aligned to PPP HDLC flags and `decodedBytes` stayed 0 (PPP could never come up). The V.21 path already handled this via spandsp's FSK async framing; V.22bis did not. Insert spandsp async_rx/async_tx (use_v14=TRUE) between the V.22bis bits and the existing HDLC byte framing: rx: v22bis bits -> async_rx (strip start/stop) -> hdlc_rx_byte -> pty tx: pty -> hdlc bytes -> async_tx (add start/stop) -> v22bis bits Also fix v22bis_status: spandsp SIG_STATUS_* codes are all negative, so the old `status < 0` test could never report carrier-up or training-succeeded. Decode the specific codes instead. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Several fixes needed to carry real async PPP and to negotiate cleanly: - Transparent data path: pppd runs async HDLC itself, so stop re-framing. async_put_byte() now writes the recovered octet stream (0x7E flags, byte-stuffing, FCS intact) straight to the pty, and poll_pty() queues the raw pty octets for V.14 transmission instead of HDLC-framing them. Without this, pppd saw de-framed payloads with no frame delimiters and LCP never ran. - V.8 handoff use-after-free: v8_result() runs synchronously inside v8_rx(), which keeps using the v8 context afterwards. Freeing v8 and allocating the v22bis/async state there corrupted that context (the new state often reused the freed block). Now the callback only records the decision; main() does the v8_free()/data-modem start after v8_rx/v8_tx return (per spandsp's tests/v8_tests.c pattern). Verified with a spandsp loopback harness. - V.8 modulation choice now masks the caller's offer with what we advertised, so we don't get pulled into V.22bis when only V.21 was offered. - Forced-mode start (SIPFAX_MODEM_START_MODE) defers entry to the first input frame, so the operator side is wired before pty-opened fires (start-up race that intermittently prevented pppd from starting).
pppd has no command-line option to select a secrets file; it always reads
/etc/ppp/{chap,pap}-secrets. The supervisor wrapped pppd in a shell that
appended a bogus `chap-secrets <tmpfile>` option, which pppd rejects
("unrecognized option 'chap-secrets'"), so it exited before LCP and the
caller saw error 721.
Render the per-call credentials to the standard path before launch (single
active call), spawn pppd directly (no wrapper, no bogus option), and clear
the file in place on teardown -- we own the file but not the root-owned
/etc/ppp directory, so truncate rather than unlink.
Standalone spandsp loopback (caller <-> answerer) that runs full V.8 negotiation and the V.22bis handoff, in both forced and V.8 modes, with an optional echo model. Used to prove the handoff logic is correct independent of the real modem/channel. cc -O2 -o v8handoff_test tests/v8handoff_test.c -lspandsp -lm ./v8handoff_test forced # PASS ./v8handoff_test v8 # PASS (handoff trains s2s) ECHO_GAIN=0.3 ECHO_DELAY=64 ./v8handoff_test v8 # PASS with echo
PPP never worked on the first VM because the runtime was never set up: the ppp package was absent, the cloud kernel has no PPP modules, and the hardened systemd unit blocks setuid pppd. Add the systemd drop-in that grants pppd the privileges it needs (without privileging the node process) and a runbook section covering the ppp package, generic kernel, the drop-in, and the pre-created /etc/ppp secrets files. Also document why the live service forces V.22bis instead of relying on V.8 negotiation.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Gets a real Windows XP dial-up client all the way to an authenticated PPP/IP connection over the spandsp soft-modem.
Code fixes
modem_connect_tone = true→FAX_CNG) instead ofANSAM_PR; no modem could ever train against it.v8_rxcallback; now deferred to the sample loop (spandspv8_tests.cpattern). Verified by a new loopback test.chap-secretsoption (→ error 721); now renders/etc/ppp/chap-secretsand launches pppd directly.Deploy
ppppackage, a kernel with PPP (cloud kernel has none → generic kernel), a systemd drop-in so the hardened unit can run setuid pppd, and pre-created secrets files.Known limitation
SIPFAX_MODEM_START_MODE=v22bis. Standards V.8 negotiation selects V.22bis but the real modem won't complete training (timing/interop; not reproducible in the spandsp loopback). Forced V.22bis trains reliably.🤖 Generated with Claude Code