Skip to content

[Feature request] Reject spoofed GPS positions and protect the RTC from spoofed GPS time #3733

Open

Description

@AlexandrTyshchenko

Problem

GPS spoofing is common in parts of Europe (Ukraine, the Baltics) and the Middle East. MeshCore currently trusts any NMEA fix:

  • the position goes straight into telemetry/adverts, and
  • the GPS time is written to the RTC every 30 minutes (MicroNMEALocationProvider).

Real case on a solar repeater in Ukraine (ProMicro + PCF8563 RTC + serial GNSS):

  • shortly after boot the module reported a "fix" at -12.04, -77.06 (Lima, Peru) with 0 satellites in view a few seconds later;
  • the RTC was set to 10 March 2028 by a spoofed GPS time. Because time / clock sync only allow the clock to move forward, there was no CLI way to recover, and the node's adverts carried 2028 timestamps.

Proposal (opt-in, e.g. GPS_GUARD)

Position plausibility — accept a fix for node_lat/lon and telemetry only if:

  • satellites ≥ 4 and HDOP ≤ 4.0;
  • inside an optional build-time bounding box (region);
  • within an optional geofence around the configured node position (set lat/lon), e.g. 1 km for a fixed repeater;
  • no "impossible jump" from the last trusted fix (e.g. > 250 km/h);
  • N consecutive consistent fixes (e.g. 5 within 100 m).
    Until then, no GPS position is reported.

Time guard — write GPS time to the RTC only if:

  • a trusted fix is current;
  • the time is not earlier than the firmware build date and not absurdly far ahead;
  • once the clock is valid, the correction stays within real clock drift (first sync ≤ 10 min, later ones ≤ 5 s + 100 ppm of elapsed time) — so a spoofer cannot walk the clock away in small steps.

Operator tools

  • an alert (status + telemetry channel) when a trusted fix keeps disagreeing with the clock by the same offset for an hour or more;
  • a way to set the clock backwards on purpose (time force <epoch>) and to accept the alerted GPS time.

Status

A working implementation runs on our node (repeater/sensor/companion, MicroNMEA provider): https://github.com/AlexandrTyshchenko/FakeSolar_sx1268_meshcore — it rejected the spoofed time and position above in practice.

Before preparing PRs against dev, I'd like to know:

  1. Is this direction welcome, and should it be opt-in or on by default?
  2. Is a deliberate "set clock backwards" command acceptable, given the forward-only rule?
  3. Should it build on GPS power cycling between fixes (companion) 🤖🤖 #3684 (GPS duty cycle), which touches the same loop?

Related: #2416, #3265, #3684.

Activity

  1. AlexandrTyshchenko commented on Oct 29, 2026

    @AlexandrTyshchenko
    Author

    Another real-world data point from the same node, running the guard from the linked implementation (2026-10-29, ~22:35 UTC):

    gps_guard: rej region   t:rej nofix
    gps diag:  sat:12 fix:1 ok:7057 bad:1
    

    The GNSS module reported a valid fix with 12 satellites — it would pass any satellite/HDOP check — but the position was outside the configured region (bounding box for Ukraine), so it was rejected. As a result:

    • no GPS position appeared in telemetry/adverts,
    • the GPS time was not written to the RTC (time sync requires a trusted fix),
    • the RTC stayed correct (checked against NTP-synced PC time).

    Earlier the same evening this node received a genuine fix inside the region (6 sats) and accepted it together with a +1 s time correction, so the guard does not block normal operation.

    This shows that satellite count / HDOP alone are not enough against spoofing — a spoofer produces a "perfect" fix. The region/geofence check is what caught it.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions