Skip to content

Releases: MddIdd/mdd-sim-gateway

MDD Sim Gateway v1.5.1

Choose a tag to compare

@github-actions github-actions released this 25 Aug 18:19
v1.5.1
f03488a

升级提示: ARM64 设备首次升级至 v1.5.x 时,Engine 镜像会在“正在重建并重启服务…”阶段下载;受网络速度影响,此阶段可能持续较长时间,请勿断电或重复发起升级。

新增功能

  • 新增余额与号码保活页面,可集中查看号码状态、余额、套餐续费需求和到期时间。
  • 新增定时号码保活,可按线路类型发送计费短信或监测套餐续费余额。
  • 新增语音信箱,未接听的来电可以在本机录音并直接从通话记录播放。
  • 新增未接来电通知,留言通知会替代未接通知,避免同一通来电重复提醒。
  • 新增版本更新通知,可通过 Webhook、Telegram 和 PushPlus 推送新版本消息。
  • 新增可选的自动更新,并通过独立发布策略控制允许升级的版本和最早升级时间。
  • 新增“保持登录 30 天”选项,登录会话可在服务重启和软件升级后继续有效。
  • 新增 Engine 的 GitHub Release 升级通道,支持直连与代理自动切换、下载进度、完整性校验和失败回滚。

修复 Bug

  • 修复 Issue #13:amd64 主机升级时会误下载 ARM64 Engine 镜像,并在源文件已替换后才因架构不符而失败;现在 amd64 会在本机刷新原生架构的 Engine 和 Docker Control 镜像,Release 镜像仅供 ARM64 使用。
  • 修复未知运营商的余量查询提示用户到已经移除该功能的“短信”页面设置的问题;现在保号页面会直接显示“查询设置”,缺少规则时也会在本页展开编辑器。
  • 修复保留的 Python 虚拟环境中 pip 启动文件失去执行权限时,服务重载会不必要地失败的问题;现在通过虚拟环境的 Python 解释器安装依赖。
  • 修复 Issue #10:多个相同型号且没有 USB 序列号的读卡器可能把不同 SIM 错误匹配到同一个 Instance。
  • 修复 Issue #12:Docker 自更新重建控制容器后丢失 sing-box、xray 和 host 映射,导致出口节点测试无法使用。
  • 修复控制服务重启或软件升级后登录状态丢失、所有浏览器被强制退出的问题。
  • 修复更新 Engine 后部分 WiFi Calling 线路保持停止状态、无法自动恢复注册的问题。
  • 修复手动重新构建 Engine 后 reload 仍可能继续使用旧镜像的问题。
  • 修复本地取消运营商服务代码时,界面可能错误显示为运营商拒绝的问题。
  • 修复浏览器禁用 WebRTC 时,软电话没有显示明确故障原因的问题。
  • 修复部分设备状态仍显示英文或原始状态代码的问题。
  • 修复通知缺少产品名称、无法判断消息来自哪个网关的问题。
  • 修复 ARM64 Engine 发布构建可能因上游资源下载过慢而长时间无响应的问题,现支持超时、低速检测、重试和断点续传。

Upgrade notice: On an ARM64 device's first upgrade to v1.5.x, the Engine image is downloaded during “Rebuilding and restarting services…”; depending on network speed, this stage may take an extended time, so do not power off the gateway or start another update.

New features

  • Added a Balance and Number Keeping page for viewing line status, balance, renewal requirements, and expiry dates in one place.
  • Added scheduled number keeping, using a chargeable SMS or renewal-balance monitoring according to the line type.
  • Added voicemail, allowing unanswered calls to be recorded locally and played directly from the call history.
  • Added missed-call notifications, with voicemail notifications replacing the missed-call alert to avoid duplicate messages for the same call.
  • Added release notifications through Webhook, Telegram, and PushPlus.
  • Added optional automatic updates controlled by a separate policy that specifies the approved version and earliest rollout time.
  • Added a “Keep me signed in for 30 days” option, with sessions surviving service restarts and software updates.
  • Added a GitHub Release upgrade channel for Engine images with direct/proxy fallback, download progress, integrity verification, and rollback on failure.

Bug fixes

  • Fixed Issue #13: amd64 upgrades incorrectly downloaded an ARM64 Engine image and failed the architecture check only after replacing the source; amd64 now refreshes native Engine and Docker Control images locally, while Release images remain ARM64-only.
  • Fixed unknown carriers directing users to configure allowance queries on the Messages page after that editor had been removed; Query Settings are now available on the Number Keeping page and the editor opens there when a rule is missing.
  • Fixed service reloads failing unnecessarily when a preserved Python virtual environment's pip launcher had lost its executable permission; dependencies are now installed through the virtual environment's Python interpreter.
  • Fixed Issue #10: multiple identical USB readers without serial numbers could incorrectly assign different SIM cards to the same Instance.
  • Fixed Issue #12: Docker self-updates could recreate the control container without the sing-box, xray, and host mappings required for network-exit tests.
  • Fixed sign-in sessions being lost after a control-service restart or software update, which signed every browser out.
  • Fixed affected WiFi Calling lines remaining stopped instead of registering again after an Engine update.
  • Fixed reload potentially continuing to use the previous Engine image after a manual rebuild.
  • Fixed locally cancelled carrier service codes sometimes being reported as rejected by the carrier.
  • Fixed the softphone not explaining that WebRTC was disabled by the browser.
  • Fixed several device states appearing in English or as raw status codes.
  • Fixed notifications not identifying which gateway sent the message.
  • Fixed ARM64 Engine release builds potentially hanging on slow upstream downloads by adding timeouts, low-speed detection, retries, and resume support.

MDD Sim Gateway v1.5.0

Choose a tag to compare

@github-actions github-actions released this 25 Aug 16:39
v1.5.0
305f025

升级提示: 首次升级至 v1.5.x 时,Engine 镜像会在“正在重建并重启服务…”阶段下载;受网络速度影响,此阶段可能持续较长时间,请勿断电或重复发起升级。

新增功能

  • 新增余额与号码保活页面,可集中查看号码状态、余额、套餐续费需求和到期时间。
  • 新增定时号码保活,可按线路类型发送计费短信或监测套餐续费余额。
  • 新增语音信箱,未接听的来电可以在本机录音并直接从通话记录播放。
  • 新增未接来电通知,留言通知会替代未接通知,避免同一通来电重复提醒。
  • 新增版本更新通知,可通过 Webhook、Telegram 和 PushPlus 推送新版本消息。
  • 新增可选的自动更新,并通过独立发布策略控制允许升级的版本和最早升级时间。
  • 新增“保持登录 30 天”选项,登录会话可在服务重启和软件升级后继续有效。
  • 新增 Engine 的 GitHub Release 升级通道,支持直连与代理自动切换、下载进度、完整性校验和失败回滚。

修复 Bug

  • 修复 Issue #10:多个相同型号且没有 USB 序列号的读卡器可能把不同 SIM 错误匹配到同一个 Instance。
  • 修复 Issue #12:Docker 自更新重建控制容器后丢失 sing-box、xray 和 host 映射,导致出口节点测试无法使用。
  • 修复控制服务重启或软件升级后登录状态丢失、所有浏览器被强制退出的问题。
  • 修复更新 Engine 后部分 WiFi Calling 线路保持停止状态、无法自动恢复注册的问题。
  • 修复手动重新构建 Engine 后 reload 仍可能继续使用旧镜像的问题。
  • 修复本地取消运营商服务代码时,界面可能错误显示为运营商拒绝的问题。
  • 修复浏览器禁用 WebRTC 时,软电话没有显示明确故障原因的问题。
  • 修复部分设备状态仍显示英文或原始状态代码的问题。
  • 修复通知缺少产品名称、无法判断消息来自哪个网关的问题。
  • 修复 ARM64 Engine 发布构建可能因上游资源下载过慢而长时间无响应的问题,现支持超时、低速检测、重试和断点续传。

Upgrade notice: On the first upgrade to v1.5.x, the Engine image is downloaded during “Rebuilding and restarting services…”; depending on network speed, this stage may take an extended time, so do not power off the gateway or start another update.

New features

  • Added a Balance and Number Keeping page for viewing line status, balance, renewal requirements, and expiry dates in one place.
  • Added scheduled number keeping, using a chargeable SMS or renewal-balance monitoring according to the line type.
  • Added voicemail, allowing unanswered calls to be recorded locally and played directly from the call history.
  • Added missed-call notifications, with voicemail notifications replacing the missed-call alert to avoid duplicate messages for the same call.
  • Added release notifications through Webhook, Telegram, and PushPlus.
  • Added optional automatic updates controlled by a separate policy that specifies the approved version and earliest rollout time.
  • Added a “Keep me signed in for 30 days” option, with sessions surviving service restarts and software updates.
  • Added a GitHub Release upgrade channel for Engine images with direct/proxy fallback, download progress, integrity verification, and rollback on failure.

Bug fixes

  • Fixed Issue #10: multiple identical USB readers without serial numbers could incorrectly assign different SIM cards to the same Instance.
  • Fixed Issue #12: Docker self-updates could recreate the control container without the sing-box, xray, and host mappings required for network-exit tests.
  • Fixed sign-in sessions being lost after a control-service restart or software update, which signed every browser out.
  • Fixed affected WiFi Calling lines remaining stopped instead of registering again after an Engine update.
  • Fixed reload potentially continuing to use the previous Engine image after a manual rebuild.
  • Fixed locally cancelled carrier service codes sometimes being reported as rejected by the carrier.
  • Fixed the softphone not explaining that WebRTC was disabled by the browser.
  • Fixed several device states appearing in English or as raw status codes.
  • Fixed notifications not identifying which gateway sent the message.
  • Fixed ARM64 Engine release builds potentially hanging on slow upstream downloads by adding timeouts, low-speed detection, retries, and resume support.

MDD Sim Gateway v1.4.1

Choose a tag to compare

@github-actions github-actions released this 22 Aug 10:07
v1.4.1
b4eeed2

修复

服务码的提示不再抢在答案前面

拨运营商服务码时,判决(运营商是否受理)和回复文本是分头到达的,判决通常先到一步。界面拿到先到的那个就下结论,于是会在文本到达前一秒宣称「没有可用响应」或「此类代码不返回文本」,然后当着你的面改口。

一句稍等,好过一个会变的答案。现在它等到真有东西可报再说,并且区分两种情况:运营商已接受——回复可能随后就到,继续等;运营商拒绝或无法处理——不会再有内容,立即出结论。

结果不再来回跳变

每个事件都会刷新一次通话列表,而这些并发请求的响应可能乱序返回——一个较早发出、较晚返回的响应会把旧列表盖回去,结果就在「等待中」和答案之间反复横跳。

现在刷新带序号,只有最新的那次允许写入;并且运营商给出的判决一旦显示就不再收回

其他

  • 返回按钮的 30 秒倒计时不再因为回复晚到而被重置
  • 服务码不再显示静音、拨号键盘、录音——它们都作用于音频,而服务码是纯信令,没有音频
  • 快速连拨两个代码时,先返回的结果不会再被记到另一通电话头上

本版只改动 WebUI 与控制面的提示逻辑,引擎镜像与 v1.4.0 相同,无需重建。


Fixed

A service code's status no longer answers before the answer does

The verdict (whether the carrier took the request) and the reply text arrive
separately, and the verdict usually wins the race. The screen concluded from whichever came
first, so it would announce "no usable response" or "this kind of code returns no text" a
second before the text appeared — correcting itself in front of you.

A brief wait beats an answer that changes. It now holds until there is something real to
report, and it tells the two cases apart: accepted — a reply may still follow, keep
waiting; refused or could not be handled — nothing more is coming, conclude now.

The outcome no longer flips back and forth

Every event refreshed the call list, and those concurrent requests could return out of
order
— an older response landing last put a stale list back on screen, so the result
alternated between "waiting" and the answer.

Refreshes now carry a sequence number and only the newest may write; and once a carrier's
verdict has been shown, it is never withdrawn.

Also

  • The Back button's 30-second countdown no longer restarts when the reply lands late
  • Mute, keypad and record are hidden for service codes — they act on audio, and a service
    code is pure signalling with none
  • Dialling two codes in quick succession no longer files the first result under the wrong call

This release changes only the WebUI and control plane. The engine image is identical to
v1.4.0 — no rebuild needed.

MDD Sim Gateway v1.4.0

Choose a tag to compare

@github-actions github-actions released this 21 Aug 19:01
v1.4.0
e799127

新增

可以拨运营商服务码

*21*<号码>#(设置转移)、*#21#(查询转移)、#225#(查余额)这类代码现在能正常拨出。此前有三层各自独立地把它们拦掉:浏览器把输入判定为「短号码或 E.164 号码」二选一、JsSIP 拒绝构造含 # 的 SIP URI、拨号方案的号码模式只认 + 或数字开头。三层现在都原样放行,传输时转义 #、送出前还原,到达网络的就是你拨的

*#06# 这类手机本机应答的代码,直接由本线路的配置作答,不再发往网络。服务码在蜂窝模块通道上会被拒绝而不是当语音呼叫拨出——那条路要走 AT+CUSD,本网关没有实现。

结果也不再套用通话的词汇。以前显示的是「已拒接」「无人接听」、还跑着通话时长,没有一个能描述「一秒内应答并挂断」的请求,反而掩盖了唯一重要的信息:这家运营商到底支不支持这个码。现在按 Q.850 原因值区分:未知代码(404)、格式错误(484)、未实现(501/488)读作「不支持」;403/603 读作「被拒绝」——那是另一个问题、另一种解法,请求到达了运营商,是账户策略拒的。没有任何响应就如实报「无响应」,不替它下结论。

服务码会显示运营商的真实回复

#225# 的答复不是语音,而是放在已建立对话内的 SIP 请求体里(T-Mobile US 放在 BYE 上)——这正是这种「通话」无声且不到一秒的原因。以前这个 body 被丢弃,所以只能确认运营商响应了,不知道它说了什么。

现在引擎把 application/vnd.3gpp.ussd+xml 载荷取出,控制面按 3GPP TS 24.390 解析,余额或转移状态就显示在你拨号的地方。两端都有边界:引擎在拷贝到栈上之前拒绝大于 4KB 的 body,解析器对存储文本长度封顶;不可解码的内容记录后忽略而非入库。

运营商的机器短信不再以乱码混进会话

不是每条短信都是给人看的:运营商和服务方也发机器载荷——SIM 卡数据下载、应用静默推送——内容是任意字节而非字符。PDU 头里写明了这一点,但 Asterisk 把 8 位数据按每字节一个字符展开成字符串,于是它们混进消息列表,看着像编码坏掉的短信,每条还弹一次通知。

现在引擎上报 TP-PID、TP-DCS、用户数据头和原始 PDU,控制面把 8 位编码、发往 SIM(class 2)、或标记为 SIM 数据下载的内容归档到独立存储。已在数据库里的载荷会在启动时迁移过去,没有丢弃任何东西,字节原样保留——识别一个加密载荷只能靠它到达时的 PDU,而不是对它的解码。

短信页新增可折叠的「非文本载荷」区,列出归档内容、归档原因和原始字节。这一步是必要的:判定读的是 PDU 头,而运营商可能给一条真实短信标错编码方案,那样它会被永久隐藏且无从察觉。

修复

  • 线路可能对着一条已死的出口连接无限重传。 出口被归咎但无法更换时(strikes 不够、节点池耗尽、或节点被固定),保持不动对节点选择是对的,却留下一个本可恢复的故障没人管。sing-box 按 5 元组标识 UDP 会话、用空闲计时器回收,而重建隧道的线路每次 IKE 重传都会刷新这个计时器——出站已死的会话,恰恰被本该恢复它的重传保持着。重建容器也没用,它产生相同的 5 元组、落到同一条死会话上。现在控制面在归咎出口又拒绝更换时会指明国家,编排器关掉该国家的连接,下一个包只能重新拨。承载着已注册兄弟线路的出口绝不会被动。

  • 极短的通话可能永远停在「拨号中」。 拨号方案用后台进程分别上报通话的开始和结果,两者没有顺序保证。通话不到一秒结束时,结果可能早于它要关闭的那条记录到达,然后被静默丢弃。服务码在 BYE 上被应答正是这种情况,问题因此暴露,但这个竞态从来不是服务码特有的,对任何短通话都潜伏着。

  • # 开头的服务码不产生任何通话记录。 拨号方案经由 shell 上报通话,而词首的未加引号 # 会开启注释:拨 #225# 因此丢掉了号码和其后所有参数,记录根本没有创建——不是状态不对,是完全不存在。*#21# 因为 # 在词中间而幸免,这正是故障看起来毫无规律的原因。

  • 普通 USB 读卡器里的 SIM 可能报 NO_CARD,而隧道明明已经建立issue #8)。模块桥把一张 SIM 呈现为三个逻辑槽位,普通读卡器只有一个槽位。但引擎契约仍用模块的布局去填未设置的槽位号,把 IMS-AKA 发到了槽位 2——单读卡器机器上并不存在,于是 SIM 读作「不存在」,而同一张卡在隧道那边应答完美。现在每个角色都跟随本线路实际绑定的读卡器。两个及以上读卡器的机器不受影响。

  • 读取线路号码不再危及刚建立的连接issue #8)。号码此前是靠开启 SIP 抓包、再额外发一次 REGISTER 制造出可读响应来获得的,而某些运营商 IMS 核心网会在刚接受注册几秒后拒绝这次主动的重新注册,返回 503。Asterisk 把它报成注册被拒,健康策略据此把线路拆掉——一条健康的连接,被「询问它自己的号码」这个动作杀掉了。运营商在本来就会做的那次注册里已通过 P-Associated-URI 告知了号码,现在直接从那里读,不再额外发送任何东西,也不再打开会把认证头写进日志的 SIP 抓包。

升级说明

需要重建引擎镜像。沿用旧引擎则:服务码回复不显示、二进制短信按内容判定会有漏网、号码学不了新的(手动填写 MSISDN 可覆盖)。


Added

  • The dialler now accepts carrier service codes such as *21*<number># (divert), *#21#
    (check divert) and #225# (balance). Three separate layers rejected them before, each
    looking like the last: the browser validated the input as either a short numeric code or an
    E.164 number, JsSIP refused to build a request URI because # is not a legal user character
    in a SIP URI, and the dialplan's outgoing extension pattern matched only + or a digit in
    first position. All three now pass the code through untouched, escaping # for transport and
    restoring it on the way out, so what reaches the network is what was dialled. What a code
    then means is the carrier's IMS to decide, not this gateway's: supplementary-service codes
    are the ones a TAS normally answers, whereas USSD codes need a USSI gateway the carrier may
    no longer operate, and a carrier that has moved self-service into an app may simply decline.
    Codes a handset answers by itself, currently *#06#, are answered from the line's own
    provisioning instead of being dialled, since no network ever replies to those. Service codes
    are refused on the cellular-modem transport rather than dialled as a voice call, which is all
    that path can do; reaching them through a modem would need AT+CUSD, which is not built.

  • A dialled service code now reports whether the carrier served it. Because the code travels
    as a call, the outcome used to arrive on a call's vocabulary — "declined", "no answer", a
    running duration timer — none of which describe a request that is answered and torn down in
    the same second, and all of which hide the one thing worth knowing: whether this carrier
    supports the code at all. The Q.850 cause already reaching the control plane distinguishes
    the cases, so codes are now scored on their own scale. An unknown code (404 Not Found),
    a malformed one (484) and an unimplemented service (501/488) read as not supported; a 403
    or 603 reads as refused, which is a different problem with a different fix — the request
    reached the carrier and was declined by account policy rather than missing from the network.
    Silence stays "no response" instead of being reported as unsupported, since nothing came
    back to justify that claim.

  • A service code now shows what the carrier actually replied. The answer to #225# or *#21#
    is not audio: the carrier puts it in the body of a SIP request inside the established dialog
    — T-Mobile US uses the BYE — which is why such a "call" is silent and over in under a second.
    That body was discarded, so an accepted code could confirm only that the carrier acted, never
    what it said. The engine now copies the application/vnd.3gpp.ussd+xml payload onto the
    channel and the control plane parses it (3GPP TS 24.390), storing the text with the call so
    the balance or divert status appears where the code was dialled. The payload is bounded on
    both sides — the engine refuses a body larger than 4 KB before copying it onto the stack, and
    the parser caps the text it will store — and a body that is not decodable text is logged and
    ignored rather than stored. Carriers that namespace the XML differently are handled; one that
    returns no payload at all still reports the outcome as before.

Fixed

  • A line could retransmit forever against an exit connection that had already died. When the
    exit is blamed but cannot be moved — strikes still short, pool exhausted, or the node pinned
    — holding is the right call for node selection, but it used to leave one recoverable failure
    unattended. sing-box keys a UDP session on its 5-tuple and retires it on an idle timer, and a
    line rebuilding its tunnel refreshes that timer with every IKE retransmit: a session whose
    outbound is dead is held open by the very retries meant to recover it, every later packet
    goes to the same dead connection, no new session is ever created, and nothing is logged.
    Rebuilding the container does not help, because it produces the same 5-tuple and lands on the
    same dead session. Seen after a self-update restarted the orchestrator: two lines sharing the
    only GB exit dialled before the route was up, got "no route to host", and stayed stuck for
    twenty minutes while their tunnels reported CONNECTING and the exit itself tested fine. The
    control plane now names the country when it blames an exit and declines to move it, and the
    orchestrator closes that country's connections so the next packet has to dial afresh. This is
    deliberately the weaker sibling of switching nodes: it changes no node, respects a pin, and
    is gated on the same check, so an exit carrying a registered sibling line is never touched.

  • A service code beginning with # produced no call-log entry at all. The dialplan reports a
    call through a shell, where an unquoted # at the start of a word opens a comment: dialling
    #225# therefore discarded the number and every argument after it, and the record was never
    created — not stuck in a wrong state, simply absent. *#21# was unaffected, its # falling
    mid-word, which is why the failure looked arbitrary. The argument is now quoted.

  • A very short call could stay on "dialing" in the call log forever. The dialplan reports a
    call's start and its outcome from separate backgrounded processes, so nothing orders the two:
    when a call ends in under a second, the outcome can reach the manager before the record it is
    meant to close, and it was then dropped silently — the call never left "dialing" even though
    it had completed. A dialled service code answered on the BYE does exactly that, which is how
    this surfaced, but the race was never specific to service codes and had been latent for any
    short call. The outcome now waits briefly for the record it belongs to instead of being
    discarded.

  • A SIM in an ordinary USB smart-card reader could report NO_CARD with the tunnel already up
    (issue #8). A modem bridge presents one SIM on three logical slots so PIN keeping, tunnel
    authentication and IMS-AKA can work independently; an ordinary reader has a single slot and
    does all three through it. The engine contract nevertheless filled the unset slot numbers with
    the modem layout, sending IMS-AKA to slot 2 — which on a one-reader gateway does not exist, so
    the SIM read as absent while the same card answered the tunnel perfectly. Each role now
    follows the reader the line is actually bound to, and the engine falls back to the only reader
    present rather than refusing a slot number that names nothing. A modem line keeps its
    dedicated channels. Gateways with two or more readers were unaffected: the stable USB-port
    binding already resolved the right one there.

  • Learning a line's phone number no longer risks the connection it just made (issue #8). WiFi
    Calling came up, held for a few seconds and was then torn down and re-established, with the
    carrier answering "503 Service Unavailable" — the number was learned by enabling SIP tracing
    and sending an extra REGISTER purely to produce a response that could be read, and some
    carrier IMS cores decline an unsolicited re-registration seconds after accepting one. Asterisk
    reports that as a rejected registration, and the health policy acts on rejections. The carrier
    already announces the number in the registration the line makes anyway, so the engine now
    records it from that response and the control plane reads it from the log: nothing extra is
    sent, and SIP tracing — which als...

Read more

MDD Sim Gateway v1.3.15

Choose a tag to compare

@github-actions github-actions released this 20 Aug 09:49
v1.3.15
e633b05

修复:经调制解调器桥接的线路可能无法注册

如果某条线路的 SIM 是通过 modem 桥(VPCD)访问的,重建引擎镜像后该线路可能再也注册不上,表现为每两分钟重建一次,而每次隧道都正常建立——因此故障看起来与出口节点无关,却也没有任何信息指向真正的原因。

起因是 1.3.13 引入的读卡器绑定校验。它读取 EF.ICCID 来确认"这个槽位里的卡确实属于本线路",而这个读取调用没有超时。在 modem 桥的逻辑通道上,该读取不会失败,而是挂起——桥把多个通道复用到同一张物理 SIM,未接通的通道会让读取永远停住。三个组件都写了"卡不应答属于故障,而非换卡证据"的容错规则,但这条规则的前提是读取会返回。

本次修复两处,缺一不可:

  • 所有读卡操作加上时限,让挂起变成各调用方本就会处理的"读不出"情形;
  • 绑定校验改为在 SIM 桥启动时判定一次,不再每次鉴权都重做。运营商给整个鉴权交换只有三秒,在这个窗口内重复回答一个已有答案的问题,本身就足以让注册失败——只加超时而不挪位置,仍然会以"响应来得太晚"的方式失败。

校验对所有读卡器保持完整效力,modem 桥通道也不例外:两个外观相同、没有 USB 序列号的模块互换端口,正是这项校验存在的意义。

是否受影响

只有重建过引擎镜像的安装会遇到(全新安装,或手动强制重建)。自更新会保留现有引擎镜像,因此不会触及这一路径。使用物理读卡器的线路不受影响。


Fixed: a line reached through a modem bridge could stop registering

When a line's SIM is reached through a modem bridge (VPCD), rebuilding the engine image could leave that line unable to register — it rebuilt every couple of minutes with its tunnel established each time, so the failure looked unrelated to the exit node while nothing pointed at the actual cause.

The card-binding check added in 1.3.13 reads EF.ICCID to confirm the slot really holds this line's SIM, through a call that has no timeout. On a bridge channel that read does not fail — it hangs: the bridge multiplexes several channels onto one physical SIM, and a channel it has not wired up leaves the read parked forever. All three components implement a "a card that will not answer is a fault, not proof of a swap" rule, but that rule only applies once the read comes back.

Two changes, both required:

  • Every card read is now bounded, turning the hang into the "could not read" case the existing handlers already cover.
  • The binding question is settled once, when the SIM bridge starts, instead of on every authentication. The carrier allows three seconds for that whole exchange; re-answering a settled question inside it was enough to lose the registration on its own — bounding the read without moving it still failed, just as "the answer arrived after the carrier gave up".

The check keeps its full force on every reader, bridge channels included: two look-alike modems with no USB serial swapping ports is exactly the mix-up it exists to catch.

Who is affected

Only installations that rebuilt their engine image (a fresh install, or a manual forced rebuild). A self-update preserves the existing engine image and cannot reach this path. Lines on physical readers are unaffected.


Full Changelog: v1.3.14...v1.3.15

MDD Sim Gateway v1.3.14

Choose a tag to compare

@github-actions github-actions released this 20 Aug 05:58
v1.3.14
1be7750

新增:超长短信自动合并

超过一条短信长度的文本,现在会作为一条完整消息收到,不再是散开的好几条。

运营商的短信中心会把长文本拆成多条独立的 SMS-DELIVER,每条带一个说明"我是第几段"的头部(UDH)。引擎里的 Asterisk 其实已经解出了这个头部,却直接丢弃,于是每一段都变成了独立的一条消息——而且顺序还是乱的(分段不保证按顺序送达),每一段各推送一次通知。

现在引擎会把分段的 编号/总数/序号 暴露出来,控制面据此缓冲并拼回原文,只入库一次、只通知一次。

其他行为:

  • 重复推送会被吸收:运营商没收到确认时会重推分段,不会因此产生重复消息,也不会让同一条消息被拼装两次。
  • 缺段不会丢:某一段始终没到的话,三分钟后仍会把已收到的内容入库,缺口用 […] 标出,而不是无限期扣住。
  • 单条短信不受影响:普通短信不带分段头部,走的还是原来的代码路径。

升级提示:此功能需要重新构建引擎镜像。控制面可以先单独升级——旧引擎不发送分段字段,控制面会按单条处理,行为与升级前一致,两边可以分开部署。


Added: multi-part SMS reassembly

A text longer than one SMS now arrives as one message instead of several.

The SMSC splits such a text into separate SMS-DELIVER PDUs, each carrying a User Data Header saying which part it is. Asterisk already unpacked that header and then discarded it, so every part surfaced as its own message — out of order, since parts are not delivered in sequence, each raising its own notification.

The engine now exposes each part's reference/total/sequence, and the control plane buffers the parts until the whole text can be assembled, then stores and notifies once.

  • Re-pushed parts are absorbed. Parts a carrier re-sends when an acknowledgement is missed cannot duplicate a message or complete a group twice.
  • A missing part does not lose the message. After three minutes the rest is stored with the gap marked […] rather than held indefinitely.
  • Single-part SMS is untouched. An ordinary message carries no concatenation header and takes exactly the previous code path.

Upgrade note: this requires a rebuilt engine image. The control plane can be updated on its own — an older engine sends no segment fields and is treated as single-part, so the two halves deploy independently.


Full Changelog: v1.3.13...v1.3.14

MDD Sim Gateway v1.3.13

Choose a tag to compare

@github-actions github-actions released this 17 Aug 13:48
v1.3.13
c71eb90

Full Changelog: v1.3.12...v1.3.13

MDD Sim Gateway v1.3.12

Choose a tag to compare

@github-actions github-actions released this 16 Aug 20:31

Full Changelog: v1.3.11...v1.3.12

MDD Sim Gateway v1.3.11

Choose a tag to compare

@MddIdd MddIdd released this 16 Aug 02:17
v1.3.11
fac6b1a

Full Changelog: v1.3.10...v1.3.11

MDD Sim Gateway v1.3.10

Choose a tag to compare

@github-actions github-actions released this 15 Aug 15:38
v1.3.10
6ed8765

本版新增「纯 VoWiFi 模式」开关与支持包增强,均源自 #1 的双模块现场排障。

纯 VoWiFi 模式(系统设置 → 常规 → 硬件,默认关闭)

在部分虚拟化环境(QEMU/PVE 的 USB 直通)中,ModemManager 虽能认领模块,但其 modem 对象会因模拟 USB 层的不稳定而周期性消失重建。SIM 访问经由 ModemManager 时,对象每消失一次,隧道就断一次 —— 现场表现为在线率被锯成 90% / 5% 的碎片。手动停止 ModemManager 会被编排服务拉回,屏蔽它则会卡死整个流程。

开启本开关后:

  • ModemManager 彻底不再运行(其 systemd 单元同时被停用,重启不复活)
  • 所有 SIM 桥接直接驱动模块串口 —— 消除了对 ModemManager 对象存活的依赖
  • VoWiFi 完整可用;4G 数据、飞行模式、蜂窝短信/呼叫在界面上呈现为「不支持」并注明原因,而不是永远转圈的开关
  • 切换时会重启一次读卡路径并复位模块(约 30 秒,有确认弹窗)

适用:虚拟机、容器等 ModemManager 不稳定的环境。ModemManager 工作正常的部署无需开启,默认行为完全不变。

支持包增强

脱敏支持包现在直接回答排障最常问的几件事,之前每一件都要多一轮往返:

  • 每个 SIM 桥接的完整命令行近期输出(桥接输出改为落到独立文件,不再受 journal 轮转影响)
  • pcscd 是否真的在监听每个分配的虚拟读卡器端口(从 /proc/net/tcp 读取 —— 探测式连接可能抢占读卡器槽位,读文件不会)
  • 读卡器定义目录清单、当前配置的 modem 后端、pcscd 实时暴露的读卡器列表

install.sh diagnose 保留主动探测职责(逐读卡器 lpac 读取),并同样收录桥接日志文件。

升级

cd /path/to/mdd-sim-gateway
sudo git pull
sudo ./install.sh reload

或 WebUI 一键更新。参考设备实测升级无线路中断,默认(auto)路径行为无变化。