<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Voip on Omar Karim Chtioui</title>
    <link>https://blog.okch.pw/tags/voip/</link>
    <description>Recent content in Voip on Omar Karim Chtioui</description>
    <generator>Hugo</generator>
    <language>en-us</language>
    <lastBuildDate>Wed, 01 Jul 2026 08:00:00 +0200</lastBuildDate>
    <atom:link href="https://blog.okch.pw/tags/voip/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>My Sky phone line kept dying for days. The fix was one SIP flag.</title>
      <link>https://blog.okch.pw/posts/keenetic-sky-phone-503/</link>
      <pubDate>Wed, 01 Jul 2026 08:00:00 +0200</pubDate>
      <guid>https://blog.okch.pw/posts/keenetic-sky-phone-503/</guid>
      <description>Sky Phone on my Keenetic would drop and throw a 503, then stay dead for days. The tempting fix was a nightly reboot. The real fix was a single SIP option: 100rel.</description>
      <content:encoded><![CDATA[<p>Same setup as <a href="../instagram-slow-wifi/">the Wi-Fi story</a>: Sky Wifi FTTH in
Italy, a Keenetic Titan (KN-1811) plugged straight into the ONT and speaking
MAP-T natively. This time the problem wasn&rsquo;t the internet — it was the phone.
Sky Phone (Sky&rsquo;s VoIP line) would work fine, then drop with a <strong>503</strong>, and
then stay dead. Not for minutes. Sometimes for <em>days</em>, until a reboot or a
DHCP renewal happened to knock it back to life.</p>
<p>The obvious instinct with a phone line that &ldquo;just needs a kick&rdquo; is to
schedule the kick. That was my plan. It turned out to be the wrong fix for
the right symptom — the actual cause was one SIP option that Keenetic doesn&rsquo;t
enable by default for Sky&rsquo;s Italian VoIP.</p>
<p>Here&rsquo;s the whole path.</p>
<h2 id="the-error-503-no-answer-record-in-the-dns-response">The error: 503, &ldquo;No answer record in the DNS response&rdquo;</h2>
<p>Keenetic&rsquo;s IP Telephony module doesn&rsquo;t register to a fixed IP. It registers
to Sky&rsquo;s SIP proxy by <em>name</em> — something like <code>voip.glb.it.isp.sky</code> — and to
get there it does two lookups in sequence:</p>
<ol>
<li>A DNS <strong>SRV</strong> lookup to find the SIP service host.</li>
<li>An <strong>AAAA</strong> (IPv6) lookup to resolve that host, because Sky&rsquo;s VoIP backend
(the SBC) is reached over IPv6.</li>
</ol>
<p>If either lookup fails at the exact moment the router tries to re-register,
Keenetic eventually gives up with <code>503 — No answer record in the DNS response</code>, and the line stays down until registration succeeds again.</p>
<p>So the first hypothesis was the same family of suspect as the Instagram
post: <strong>IPv6</strong>. On a MAP-T network, VoIP leans on IPv6 even when ordinary
browsing is happily riding IPv4. If the MAP-T session flaps or the ISP
profile re-negotiates badly, AAAA resolution for the SBC host fails, the
phone module can&rsquo;t recover on its own, and — because some firmware versions
don&rsquo;t retry aggressively — it sits broken until something external kicks it.
That &ldquo;no automatic recovery&rdquo; part is what turns a momentary hiccup into a
multi-day outage.</p>
<p>If you want to catch it in the act, resolve the Sky names directly from a
machine behind the router, during a working window and (if you can) during a
failure:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">nslookup -type<span class="o">=</span>SRV _sip._udp.voip.glb.it.isp.sky
</span></span><span class="line"><span class="cl">nslookup -type<span class="o">=</span>AAAA &lt;hostname the SRV record returns&gt;
</span></span></code></pre></div><p>If those fail while <code>google.com</code> resolves fine, you&rsquo;ve confirmed it&rsquo;s
specifically the Sky SBC name failing — which is exactly the evidence support
wants.</p>
<h2 id="the-tempting-fix-bounce-just-the-phone-nightly">The tempting fix: bounce just the phone, nightly</h2>
<p>My first plan was a workaround, not a cure: if the line only needs a nudge to
re-resolve and re-register, schedule the nudge before it gets stuck.</p>
<p>Keenetic&rsquo;s scheduler isn&rsquo;t reboot-specific. You define a named time window,
then attach a command to fire in that window — the same pattern people use for
a nightly router reboot. And there <em>is</em> a narrower target than a full reboot:
the Phone Station toggle deactivates just the telephone exchange
(deregistering every line, powering down the handsets) without touching Wi-Fi
or routing.</p>
<p>Honest caveat, and it&rsquo;s the same discipline as diagnosing with data: I could
confirm that toggle exists and that the scheduler can drive arbitrary
commands, but I could <strong>not</strong> verify Keenetic&rsquo;s exact CLI keyword for that
specific switch. I&rsquo;m not going to hand myself a guessed command for something
that runs unattended on my only phone line. The way to nail it down is to let
the CLI tell you:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl"># SSH/Telnet in, enter config mode, then:
</span></span><span class="line"><span class="cl">nvox sip 0     # your line id, then press Tab to list every valid keyword
</span></span></code></pre></div><p>Tab-completion lists the real options (<code>keep-alive-extended</code>, <code>deny-pickup</code>,
and friends), so you toggle the right one, confirm in the Web UI that the line
drops to <em>Disabled</em> then returns to <em>Registered</em>, and only then wrap it in a
schedule block.</p>
<p>The fully-confirmed, blunter fallback is a scheduled full reboot at a quiet
hour:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">schedule nightlyreboot
</span></span><span class="line"><span class="cl"> action start 0 4 *
</span></span><span class="line"><span class="cl"> action stop 1 4 *
</span></span><span class="line"><span class="cl">exit
</span></span><span class="line"><span class="cl">system reboot schedule nightlyreboot
</span></span><span class="line"><span class="cl">system configuration save
</span></span><span class="line"><span class="cl">exit
</span></span></code></pre></div><p>It works, but it&rsquo;s a symptom mask — a nightly blip on Wi-Fi <em>and</em> phone to
paper over a bug I hadn&rsquo;t actually understood yet. Before building anything
around the symptom, I went looking for the cause. That&rsquo;s where it got
interesting.</p>
<h2 id="the-real-fix-100rel">The real fix: 100rel</h2>
<p>Sky&rsquo;s Italian VoIP service has a specific set of requirements that Keenetic
does <strong>not</strong> all enable by default: support for <code>100rel</code> (reliable provisional
responses), both G.711 and G.729 codecs, 20 ms packetization, a registration
expiry of 3600 s, voice activity detection off, a specific conference address,
and 802.1p QoS tagging to prioritize voice over data on the fiber.</p>
<p>The one that jumps out is <code>100rel</code>. Missing it produces exactly this shape of
bug: <strong>registration looks fine, but call handling silently breaks</strong>, and over
time that manifests as the line going dead for stretches. Keenetic&rsquo;s own fix
is two lines from the CLI:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">nvox sip-common 100rel
</span></span><span class="line"><span class="cl">copy running-config startup-config
</span></span></code></pre></div><p>The first turns it on; the second saves it so it survives a reboot. While
you&rsquo;re in there it&rsquo;s worth checking the rest of that list against your line
config — codecs, packet timing, registration timeout — because a mismatch on
any of them produces the same flaky, hard-to-pin-down behavior.</p>
<p>But <em>why</em> does a single flag matter this much? That&rsquo;s the part worth
understanding.</p>
<h2 id="what-is-100rel-actually">What is 100rel, actually?</h2>
<p><code>100rel</code> is a SIP extension (<a href="https://www.rfc-editor.org/rfc/rfc3262">RFC 3262</a>)
whose name is short for &ldquo;100% reliable.&rdquo; It makes certain in-progress call
messages reliable — the same guarantee the &ldquo;call answered&rdquo; message already
has.</p>
<p>SIP responses come in two flavors:</p>
<ul>
<li><strong>Final responses</strong> (<code>200 OK</code>, <code>4xx</code> errors, …) are always reliable. If the
other side doesn&rsquo;t acknowledge one, the sender keeps retransmitting until it
does.</li>
<li><strong>Provisional responses</strong> (<code>1xx</code> codes like <code>180 Ringing</code> or
<code>183 Session Progress</code>) are normally fire-and-forget. Sent once, no required
acknowledgment. If the UDP packet carrying one is lost, it&rsquo;s simply gone.</li>
</ul>
<p>Usually that&rsquo;s harmless — a dropped <code>180 Ringing</code> doesn&rsquo;t matter, the call
connects or fails regardless. But one provisional response carries real
payload: <code>183 Session Progress</code> often includes <strong>SDP</strong>, the media negotiation
used for <em>early media</em> — the actual network ringback tone, an announcement, or
precise codec setup <em>before</em> the call is even answered. If that packet
silently drops, the caller gets dead air, or the session gets wedged
half-finished.</p>
<p><code>100rel</code> closes that gap by adding:</p>
<ul>
<li>a <strong>PRACK</strong> method — an ACK, but for provisional responses instead of final
ones;</li>
<li><strong>RSeq / RAck</strong> headers so each PRACK matches the specific provisional
response it confirms.</li>
</ul>
<p>With <code>100rel</code> on, that important provisional response is retransmitted until
explicitly acknowledged, instead of hoped-for.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">Caller (Keenetic)                         Sky SBC
</span></span><span class="line"><span class="cl">      |                                      |
</span></span><span class="line"><span class="cl">      |  INVITE  -----------------------&gt;    |
</span></span><span class="line"><span class="cl">      |                                      |
</span></span><span class="line"><span class="cl">      |  &lt;-----------------  183 Session Progress (SDP)   [reliable now]
</span></span><span class="line"><span class="cl">      |                                      |
</span></span><span class="line"><span class="cl">      |  PRACK  ------------------------&gt;    |   &lt;- the step 100rel adds
</span></span><span class="line"><span class="cl">      |  &lt;-----------------------  200 OK (PRACK)          |
</span></span><span class="line"><span class="cl">      |                                      |
</span></span><span class="line"><span class="cl">      |  &lt;-----------------------  200 OK (INVITE)         |
</span></span><span class="line"><span class="cl">      |  ACK  --------------------------&gt;    |
</span></span><span class="line"><span class="cl">      |                                      |
</span></span></code></pre></div><p>Without <code>100rel</code>, that <code>183</code> is sent once over UDP and hoped for. With it,
Keenetic <em>must</em> send <code>PRACK</code> and Sky <em>must</em> confirm before setup proceeds —
the same reliability the final <code>200 OK</code> already enjoys.</p>
<h2 id="why-it-matches-the-symptom">Why it matches the symptom</h2>
<p>This is the satisfying part. <strong>Registration is a separate, simpler
transaction that keeps succeeding either way</strong> — which is precisely why the
line kept showing up as fine. But if Sky&rsquo;s network expects the PRACK handshake
during call setup and Keenetic never sends it, the parts of the flow built on
<code>100rel</code> stall or behave unpredictably: call setup hangs, sessions get stuck —
without ever touching registration.</p>
<p>&ldquo;Line shows up but the actual phone service quietly breaks for days&rdquo; is
exactly what a missing <code>100rel</code> looks like. The 503/DNS story was real and
worth ruling out, but it was the loud failure mode; the quiet one was a
protocol option nobody enabled.</p>
<h2 id="takeaways">Takeaways</h2>
<ol>
<li><strong>&ldquo;Registered&rdquo; is not &ldquo;working.&rdquo;</strong> Registration and call setup are
different SIP transactions. A line that shows <em>Registered</em> can still be
broken if the call-setup handshake it depends on isn&rsquo;t happening.</li>
<li><strong>Reach for the root cause before the cron job.</strong> A nightly bounce would
have &ldquo;fixed&rdquo; this forever while hiding it forever. Scheduled reboots are a
safety net, not a diagnosis.</li>
<li><strong>Default configs aren&rsquo;t carrier-tuned.</strong> Sky Italia has a specific VoIP
profile — <code>100rel</code>, codecs, packetization, registration expiry, QoS.
Matching the carrier&rsquo;s expectations matters more than any single toggle.</li>
<li><strong>Don&rsquo;t automate a guessed command.</strong> I could confirm the Phone Station
toggle and the scheduler existed, but not the exact CLI keyword — so I let
Tab-completion tell me rather than shipping a guess into unattended
automation on my only phone line.</li>
</ol>
<p>The DNS/IPv6 angle wasn&rsquo;t crazy — but this time the story was one flag deep in
the SIP stack.</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
