Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

[FREEBOX] Multiple IPv6 addresses still trigger false IP Changed events after #1273

Aperta
#1,831 2 commenti 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
4/5
Tempo stimato
3-5 giorni
Idoneità per principianti
48/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
python, sql

Direzione di ricerca

Start by tracing the scan-processing order and the three reductions described in the report: update_ipv4_ipv6(), LatestDeviceScan, and insert_events(); the issue does not name their files or tests. Compare how each handles multiple known addresses for one MAC, then inspect existing tests for scan processing and IP Changed events. Done means repeated scans with multiple known IPv6 addresses do not emit false IP Changed events, while genuine address changes still do.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

bug 🐛 next release/in dev image 🚀 Waiting for reply⏳
What installation are you running?

Production (netalertx) 📦

Is there an existing issue for this?
The issue occurs in the following browsers. Select at least 2.
  • Firefox
  • Chrome
  • Edge
  • Safari (unsupported) - PRs welcome
  • N/A - This is an issue with the backend
Current Behavior

I am seeing false IP Changed events for dual-stack devices discovered by the FREEBOX plugin even after the dual-stack persistence issue reported in #1804 was fixed.

The important difference from #1804 is that devPrimaryIPv4 and devPrimaryIPv6 are now populated correctly.

The remaining issue appears when the same MAC has:

  • one IPv4 address; and
  • multiple legitimate IPv6 addresses.

These IPv6 addresses are already known to NetAlertX and are not newly assigned addresses.

Relation to #1804

In #1804, the original problem was that FREEBOX discovered IPv6 addresses correctly, but devPrimaryIPv6 remained empty because scan observations were reduced before update_ipv4_ipv6() could preserve both address families.

That issue was fixed and I tested the updated build successfully.

In that test:

  • both devPrimaryIPv4 and devPrimaryIPv6 were populated;
  • dual-stack state remained stable across repeated scans; and
  • the previous flood of repeated IPv6 IP Changed events disappeared.

So I consider #1804 fixed.

This report concerns a remaining case that became visible afterwards: a device can have multiple valid IPv6 addresses at the same time, and NetAlertX can still generate IP Changed events as different already-known IPv6 addresses become selected by different parts of the scan-processing logic.

Observed IPv6 oscillation

For one affected device, I observed repeated transitions between two persistent IPv6 addresses:

IPv6-4 -> IPv6-2
IPv6-2 -> IPv6-4
IPv6-4 -> IPv6-2
IPv6-2 -> IPv6-4

Both addresses were already present in historical FREEBOX observations.

These were not newly discovered IPv6 addresses.

Different address-selection mechanisms

I investigated the current scan-processing path and found that several different reductions are applied to multiple addresses belonging to the same MAC.

1. Primary IPv4/IPv6 selection

update_ipv4_ipv6() selects the primary IPv4 and IPv6 independently from CurrentScan, ranking addresses within each address family using scanLastConnection:

ROW_NUMBER() OVER (
    PARTITION BY scanMac,
                 CASE WHEN instr(scanLastIP, ':') > 0 THEN 'v6' ELSE 'v4' END
    ORDER BY scanLastConnection DESC
)

This is the mechanism introduced as part of the #1804 fix.

It correctly allows IPv4 and IPv6 to be retained simultaneously.

However, when a device has more than one IPv6 address, whichever IPv6 currently has the newest scanLastConnection becomes the candidate for devPrimaryIPv6.

During live monitoring I observed different already-known IPv6 addresses changing rank as their scanLastConnection values changed.

2. LatestDeviceScan / devLastIP

LatestDeviceScan performs a different reduction:

ROW_NUMBER() OVER (
    PARTITION BY scanMac, scanSourcePlugin
    ORDER BY scanLastConnection DESC
)

This keeps one latest observation for a MAC/plugin across both IPv4 and IPv6 rows.

Therefore, the address used for the single-valued devLastIP can differ from the per-family address selected by update_ipv4_ipv6().

3. IP Changed event candidate

insert_events() uses another reduction again.

Present CurrentScan rows are grouped by MAC and reduced using:

SELECT scanMac, MIN(scanLastIP) AS scanLastIP
FROM CurrentScan
WHERE scanPresence = 1
  AND scanLastIP IS NOT NULL
GROUP BY scanMac

The resulting address is then compared against:

  • devPrimaryIPv4
  • devPrimaryIPv6
  • devLastIP

Importantly, insert_events() runs before update_ipv4_ipv6() in the scan-processing cycle.

So the current scan's reduced candidate is compared against device-level values from the previous processing state.

Captured event-producing cycle

I captured an actual event-producing cycle at high polling frequency.

The addresses below are anonymized but consistently labelled.

Immediately before the event, the affected device had:

Device-6

newest IPv6     = IPv6-2
MIN(scanLastIP) = IPv6-2
devLastIP       = IPv4
devPrimaryIPv4  = set
devPrimaryIPv6  = IPv6-4

The generated event was:

IP Changed
new      = IPv6-2
previous = IPv4

In other words:

  • IPv6-2 was the current MIN(scanLastIP) candidate;
  • the stored primary IPv6 was still IPv6-4;
  • devLastIP was IPv4.

The event conditions therefore effectively evaluated as:

IPv6-2 != devPrimaryIPv4
IPv6-2 != devPrimaryIPv6 (IPv6-4)
IPv6-2 != devLastIP      (IPv4)

All three comparisons were true, so an IP Changed event was generated.

However, IPv6-2 was not a newly discovered address.

Both IPv6-2 and IPv6-4 were already-known FREEBOX IPv6 addresses for the same device.

Live CurrentScan observations

A separate live CurrentScan capture also showed that these reductions can select different addresses for the same device during the same scan.

For example, several devices had:

LatestDeviceScan candidate = IPv6
Primary IPv6 candidate     = IPv6
MIN(scanLastIP) candidate  = IPv4

For the affected device during the event-producing scan, the state instead reached:

newest IPv6     = IPv6-2
MIN(scanLastIP) = IPv6-2
stored primary6 = IPv6-4

This suggests that changes in which already-known IPv6 rows are currently present/reachable, together with changes in their scanLastConnection values, can cause an existing IPv6 address to be treated as an IP change.

Equal scanLastConnection values

I also observed cases where two different IPv6 addresses for the same MAC had exactly the same scanLastConnection.

The ranking currently uses:

ORDER BY scanLastConnection DESC

without a secondary ordering criterion.

I am not describing the result as random, but when timestamps are equal there is no explicit tie-breaker.

Summary

The basic dual-stack persistence problem from #1804 appears fixed.

The remaining issue is that multiple IPv6 addresses for the same MAC are still reduced differently by different parts of the scan-processing pipeline:

  • update_ipv4_ipv6() selects the newest IPv6 by scanLastConnection;
  • LatestDeviceScan selects one newest address across the plugin's observations;
  • insert_events() selects MIN(scanLastIP) across present rows.

These different reduction rules can cause an already-known IPv6 address to be considered different from the stored device-level values and generate a false IP Changed event.

This may also interact with #1828 because temporary FREEBOX L3 reachability changes can alter which address rows are present in CurrentScan.

However, I believe the failure modes are distinct:

  • #1804 concerned missing dual-stack persistence.
  • #1828 concerns false Disconnected / Connected state.
  • This report concerns false IP Changed events when multiple already-known IPv6 addresses exist for the same MAC.
Expected Behavior

The dual-stack fix from #1804 should continue to preserve both address families correctly:

devPrimaryIPv4 -> IPv4
devPrimaryIPv6 -> IPv6

For devices that have multiple legitimate IPv6 addresses, NetAlertX should not generate an IP Changed event merely because a different already-known IPv6 address becomes:

  • the newest address by scanLastConnection;
  • the current primary IPv6;
  • the latest address selected for devLastIP; or
  • the lexical minimum returned by MIN(scanLastIP).

For a dual-stack device with one IPv4 address and multiple known IPv6 addresses, NetAlertX should distinguish between:

  • discovery of a genuinely new address;
  • disappearance/reappearance or temporary reachability changes of an already-known address;
  • selection of a different existing IPv6 address as the current/primary IPv6;
  • and an actual address change that should generate an IP Changed event.
Multiple known IPv6 addresses

For example, consider a device with these legitimate, already-known addresses:

IPv4-A
IPv6-A
IPv6-B

If subsequent scans cause the selected primary IPv6 to alternate between:

devPrimaryIPv6 = IPv6-A

and:

devPrimaryIPv6 = IPv6-B

this should not repeatedly produce:

IPv6-A -> IPv6-B
IPv6-B -> IPv6-A
IPv6-A -> IPv6-B

IP Changed events merely because reachability timestamps or current scan rows changed.

Event detection

Similarly, reducing all present addresses for a MAC to:

MIN(scanLastIP)

should not cause an IP Changed event simply because the currently present/reachable address set changes and a different already-known address becomes the lexical minimum.

An already-known/current IPv6 address for the same MAC should not be treated as a newly changed IP merely because it becomes the current event candidate.

Consistent multi-address handling

I would expect IP Changed detection to operate consistently with the multi-address model introduced by the #1804 dual-stack fix.

If NetAlertX now preserves IPv4 and IPv6 independently at the device level, event detection should also account for the fact that a device may legitimately own more than one address within the same family.

An IP Changed event should represent an actual address change according to NetAlertX's intended semantics, rather than a change in which one of several existing addresses wins a particular reduction rule.

Where ranking is required, equal scanLastConnection values should also have an explicitly defined deterministic tie-breaker.

I do not have a strong opinion on whether this requires a schema change.

The current implementation already demonstrates that multiple CurrentScan rows can be used to maintain both devPrimaryIPv4 and devPrimaryIPv6 without changing the schema.

The important expected outcome is that all parts of the scan-processing pipeline use a consistent multi-address model when deciding:

  • the current IPv4 address;
  • the current IPv6 address;
  • devLastIP; and
  • whether an IP Changed event should be generated.

A schema change may be appropriate if NetAlertX eventually wants to retain all addresses for a device as first-class persistent state, but it does not appear to be strictly required to address the false IP Changed behavior demonstrated here.

Steps To Reproduce
  1. Run a current NetAlertX production release.

  2. Have at least one FREEBOX client that has:

    • an IPv4 address; and
    • more than one IPv6 address associated with the same MAC.
  3. Allow the FREEBOX plugin to complete several scheduled scans.

  4. Confirm that the FREEBOX observations contain multiple addresses for the same MAC, for example:

    Device A
    ├── IPv4-A
    ├── IPv6-A
    └── IPv6-B
    
  5. Confirm that the #1804 dual-stack fix is working and that the device has both primary address families populated:

    devPrimaryIPv4 = IPv4-A
    devPrimaryIPv6 = IPv6-A or IPv6-B
    

    The issue described here does not require devPrimaryIPv6 to be empty.

  6. Continue running FREEBOX scans while the multiple IPv6 addresses remain known for the device.

  7. Observe that the per-address scanLastConnection values can change independently and that a different already-known IPv6 address can become the current IPv6 candidate.

  8. Check the Events table or the generated NetAlertX notifications/reports.

  9. Observe that an IP Changed event can be generated for an IPv6 address that was already known for that MAC.

    In my captured reproduction, the pre-event state was:

    newest IPv6     = IPv6-2
    MIN(scanLastIP) = IPv6-2
    devLastIP       = IPv4
    devPrimaryIPv4  = set
    devPrimaryIPv6  = IPv6-4
    

    NetAlertX then generated:

    IP Changed
    new      = IPv6-2
    previous = IPv4
    

    Both IPv6-2 and IPv6-4 were already-known FREEBOX addresses for the same device.

  10. Across subsequent scans, the same device can alternate between already-known IPv6 addresses, producing transitions such as:

    IPv6-4 -> IPv6-2
    IPv6-2 -> IPv6-4
    IPv6-4 -> IPv6-2
    IPv6-2 -> IPv6-4
    

    These transitions occur without the addresses being newly discovered.

Notes on reproducibility

The behavior depends on a device having multiple IPv6 observations and on which rows are present/reachable during a particular scan, so an IP Changed event may not occur on every FREEBOX cycle.

The issue is easier to observe over multiple scheduled scans.

Relevant app.conf settings
The relevant FREEBOX settings are:

    FREEBOX_RUN='schedule'
    FREEBOX_RUN_SCHD='*/5 * * * *'

    FREEBOX_address='mafreebox.freebox.fr'
    FREEBOX_api_version=6
    FREEBOX_api_port=443

    FREEBOX_CMD='python3 /app/server/plugins/freebox/freebox.py'
    FREEBOX_RUN_TIMEOUT=30

    FREEBOX_SET_ALWAYS=['devMac','devLastIP']
    FREEBOX_SET_EMPTY=[]

The current FREEBOX plugin configuration also maps its per-address reachability timestamp to:

    watchedValue4 -> scanLastConnection

This is the timestamp handling introduced as part of #1804.

The issue occurs with `scanLastConnection` populated.
docker-compose.yml
services:
  netalertx:
    image: ghcr.io/netalertx/netalertx:latest
    container_name: netalertx
    restart: unless-stopped

    network_mode: host

    volumes:
      - /path/to/netalertx/data:/data

    tmpfs:
      - "/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"

    cap_drop:
      - ALL

    cap_add:
      - NET_ADMIN
      - NET_RAW
      - NET_BIND_SERVICE
      - CHOWN
      - SETUID
      - SETGID

    read_only: true

    security_opt:
      - no-new-privileges:true

    environment:
      - TZ=Europe/Paris

    deploy:
      resources:
        reservations:
          memory: 1G
        limits:
          memory: 2G
Debug or Trace enabled
  • I have read and followed the steps in the wiki link above and provided the required debug logs and the log section covers the time when the issue occurs.
Relevant app.log section

N/A

The relevant Docker log section is provided below.

Docker Logs

The following is a privacy-sanitized extract from a DEBUG scan cycle in which the FREEBOX plugin supplied multiple IPv4/IPv6 observations for the same devices.

MAC addresses, IP addresses, and device names have been replaced with placeholders. The timestamps and processing order are preserved.

00:45:32 [Scheduler] run for FREEBOX: YES
00:45:32 [Plugins] CMD: python3 /app/server/plugins/freebox/freebox.py
00:45:32 [FREEBOX] In script
00:45:33 [Plugins] SUCCESS for FREEBOX received 36 entries

00:45:33 [Plugins] Mapping objects to database table: CurrentScan
00:45:33 [Plugins] SQL query for mapping:
INSERT OR IGNORE INTO CurrentScan
("scanMac", "scanLastIP", "scanName", "scanVendor", "scanType",
 "scanLastConnection", "scanSourcePlugin")
VALUES (?, ?, ?, ?, ?, ?, ?)

# The corresponding sqlParams contained multiple rows for individual
# devices, including one IPv4 address and multiple IPv6 addresses with
# independent scanLastConnection timestamps.

00:46:56 [Events] - 1a - Devices down (non-sleeping)
00:46:56 [Events] - 1b - Devices down (sleep expired)
00:46:56 [Events] - 2 - New Connections
00:46:56 [Events] - 2b - NIC-derived New Connections
00:46:56 [Events] - 3 - Disconnections
00:46:56 [Events] - 4 - IP Changes
00:46:56 [Events] - Events end

# Device updates occur after event processing.

00:46:56 [Update Devices] - (FREEBOX)
current_value: <IPv6-A> new_value: <IPv4-A> -> devLastIP

00:46:56 [Update Devices] - (FREEBOX)
scanLastIP -> devLastIP

[...]

00:46:56 [Update Devices] - (FREEBOX)
current_value: <IPv4-B> new_value: <IPv6-B> -> devLastIP

00:46:56 [Update Devices] - (FREEBOX)
scanLastIP -> devLastIP

[...]

00:46:56 [Process Scan] Updating devLastConnection from CurrentScan
00:46:56 [Process Scan] Updating Presence from CurrentScan

00:46:56 [Update Devices]
Updating devPrimaryIPv4 / devPrimaryIPv6 from CurrentScan

00:46:56 [Update Devices] Updated 17 IPv4/IPv6 entries

The relevant processing order observed in this cycle is:

  1. FREEBOX populates CurrentScan with multiple address observations.
  2. IP Changes event processing runs.
  3. devLastIP is subsequently updated.
  4. devPrimaryIPv4 / devPrimaryIPv6 are subsequently updated from CurrentScan.

The complete raw DEBUG log is not included because it contains MAC addresses, local/public IPv4/IPv6 addresses, and device names.

Lingua principale
Python
Stelle
7.2k
Fork
456
Merge medio
16h 25m
PR unite (30g)
30

Preparare l'ambiente

Apri in Codespaces

Avvia il container di sviluppo del progetto nel browser, con il tuo account GitHub.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di netalertx/NetAlertX

Tutte le issue di netalertx/NetAlertX

Issue simili

Altre issue su Python

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.