Skip to content

P0: make SIPROTEC Clock Sync survive Windows UDP/123 conflicts - #152

Merged
masarray merged 13 commits into
mainfrom
agent/raw-sntp-fallback
Aug 13, 2026
Merged

P0: make SIPROTEC Clock Sync survive Windows UDP/123 conflicts#152
masarray merged 13 commits into
mainfrom
agent/raw-sntp-fallback

Conversation

@masarray

@masarray masarray commented Aug 13, 2026

Copy link
Copy Markdown
Owner

What changed

  • keep normal station-bus UDP/123 SNTP when the socket can bind
  • automatically fall back to raw Npcap Ethernet when Windows Time or another service already owns UDP/123
  • build valid Ethernet/IPv4/UDP SNTP frames, including IPv4 and UDP checksums and single-tag VLAN reply preservation
  • capture Mode 3 client requests and inject Mode 4 replies without stopping or reconfiguring Windows Time
  • split FAT evidence into broadcast sent, client request observed, and Mode 4 reply sent counters
  • explicitly keep relay synchronization status as not proven until device-side evidence confirms it
  • add raw-frame regression tests for endpoint swapping, checksums, VLAN preservation, broadcast Mode 5, and request filtering

Root cause

ARSAS 1.6.24 treated exclusive UDP/123 ownership as a hard prerequisite. On normal Windows commissioning laptops, W32Time can already own that port, so IEC 61850 stayed connected while SNTP silently degraded to PortUnavailable. The previous UX also made it too easy to interpret a Mode 5 broadcast as successful synchronization.

Safety invariants

  • never stop, restart, or reconfigure Windows Time
  • Clock Sync remains fail-open for IEC 61850
  • raw fallback is limited to the station-bus adapter selected by the existing Windows route resolver
  • no claim that broadcast/request/reply proves the relay clock actually synchronized
  • no automatic broad Windows Firewall policy changes

Validation

Final head 45afd3203bfbbd5cf1ef0f1ec415ae00025a057e passed:

  • Build ARSAS #1000: source/license invariants, solution build, application regression tests, portable publish and portable smoke test
  • Validate ARSAS Windows installer Fix IO FAT live binding and relay fascia #137: restore/build/tests, installer compile, silent install/uninstall smoke test, checksums and artifact upload
  • Validate SV evidence bundles #197: focused build and regression tests

An earlier CI pass exposed one ReadOnlyMemory<byte>ReadOnlySpan<byte> integration mismatch at the Npcap capture boundary; it was corrected to use captured.Frame.Span and the final head is green.

@masarray
masarray marked this pull request as ready for review August 13, 2026 06:20
@masarray
masarray merged commit dc66034 into main Aug 13, 2026
3 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 45afd3203b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

{
var options = new ProcessBusCaptureOptions
{
Filter = "udp dst port 123",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Admit VLAN-tagged SNTP through the capture filter

On an 802.1Q/802.1ad station bus, the bare udp dst port 123 BPF expression rejects tagged Ethernet frames before TryParseClientRequest can inspect them because the IPv4/UDP offsets are shifted by the VLAN header. Consequently, the newly added VLAN parsing and tag-preserving reply path is unreachable and RAW Clock Sync receives no client requests on those networks; include VLAN-aware filter alternatives as the existing process-bus capture filters do.

Useful? React with 👍 / 👎.

Comment on lines 305 to +307
SetState(
SntpClockServiceState.Serving,
binding.DirectedBroadcast == null
? $"SNTP unicast server active on {binding.LocalAddress}:123 with SIPROTEC compatibility stratum {_profile.Stratum}. This subnet has no usable directed broadcast address."
: $"SNTP server active on {binding.LocalAddress}:123 with SIPROTEC compatibility stratum {_profile.Stratum}; Mode 5 broadcast targets {binding.DirectedBroadcast}:123.");

_receiveTask = ReceiveLoopAsync(udp, _serviceCancellation.Token);
_broadcastTask = BroadcastLoopAsync(udp, binding, _serviceCancellation.Token);
RequestImmediateBroadcast();
return Task.CompletedTask;
SntpClockServiceState.Faulted,
$"Npcap RAW SNTP capture failed: {exception.Message}. IEC 61850 remains unaffected.");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Clear or restart the transport after a capture fault

When the Npcap capture loop terminates with a runtime error, this handler only changes the displayed state and leaves _rawTransport non-null. Subsequent EnsureStartedAsync calls therefore take the existing-transport shortcut and never reopen capture, so Mode 3 requests remain unanswered until the user manually disables Clock Sync or restarts the application; tear down the faulted transport or make the active check account for a completed capture task.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant