Prevent same-bus HKG advisory packet retransmission

This commit is contained in:
firestar5683
2026-11-03 12:33:24 -06:00
parent fc45eb6862
commit 7920339796
4 changed files with 57 additions and 10 deletions
+2 -1
View File
@@ -35,7 +35,8 @@ class DawSource:
self.data, self.stamp = bytes(data), stamp
def command(self, now_ns):
if not self.enabled or self.data is None or not 0 < self.stamp <= now_ns <= self.stamp + 100_000_000:
if (not self.enabled or self.source_bus == self.target_bus or self.data is None or
not 0 < self.stamp <= now_ns <= self.stamp + 100_000_000):
return []
data = bytearray(self.data)
if self.canfd:
@@ -25,9 +25,9 @@ def source(canfd, status):
return bytes(data)
@pytest.mark.parametrize('canfd', (False, True))
@pytest.mark.parametrize('status', range(8))
def test_exact_current_advisory_only_transform(canfd, status):
def test_exact_current_advisory_only_transform(status):
canfd = False
owner = DawSource(params(canfd))
data = source(canfd, status)
assert not owner.command(BASE)
@@ -102,3 +102,38 @@ def test_unsupported_ioniq6_native_profiles_cannot_emit_advisories(word):
assert not owner.enabled
owner.update([(BASE, [CanData(0x11A, source(True, 1), owner.source_bus)])])
assert not owner.command(BASE)
@pytest.mark.parametrize('word', (0x11, 0x91, 0x811, 0x891, 0x8015, 0x8095, 0x8815, 0x8895))
@pytest.mark.parametrize('status', range(8))
def test_live_same_bus_canfd_source_is_never_retransmitted(word, status):
cp = params(True)
cp.safetyConfigs[-1].safetyParam = word
owner = DawSource(cp)
assert owner.enabled and owner.source_bus == owner.target_bus
for tick in range(20):
stamp = BASE + tick * 10_000_000
data = bytearray(source(True, status))
data[2] = tick
data[:2] = hkg_can_fd_checksum(owner.address, None, data).to_bytes(2, 'little')
owner.update([(stamp, [CanData(owner.address, bytes(data), owner.source_bus)])])
assert owner.data == bytes(data)
assert owner.command(stamp) == []
assert owner.command(stamp + 5_000_000) == []
@pytest.mark.parametrize('alternate', (False, True))
def test_actual_ioniq6_controller_keeps_live_source_off_sendcan(alternate):
cp, _ = ioniq6_params(alternate)
before = cp.to_dict()
first, second = CarInterface(cp.as_reader().as_builder()), CarInterface(cp.as_reader().as_builder())
data = source(True, 1)
plain = first.update([(BASE, [])])
observed = second.update([(BASE, [CanData(0x11A, data, 1)])])
assert observed.to_dict() == plain.to_dict()
control = structs.CarControl()
baseline = first.apply(control.as_reader(), BASE)[1]
actual = second.apply(control.as_reader(), BASE)[1]
assert actual == baseline
assert all(frame[0] != 0x11A for frame in actual)
assert cp.to_dict() == before
@@ -15,6 +15,17 @@ def packet(addr, data, bus):
return libsafety_py.make_CANPacket(addr, bus, data)
def advisory_candidate(owner, now):
if not owner.canfd:
return owner.command(now)[0]
assert owner.command(now) == []
data = bytearray(owner.data)
if (data[7] >> 3) & 7 == 1:
data[7] &= ~0x38
data[:2] = hkg_can_fd_checksum(owner.address, None, data).to_bytes(2, 'little')
return owner.address, bytes(data), owner.target_bus
def setup(canfd, word):
safety = libsafety_py.libsafety
debug = safety.set_safety_hooks(structs.CarParams.SafetyModel.allOutput, 0) == 0
@@ -33,7 +44,7 @@ def test_actual_host_commands_obey_native_source_payload_and_expiry(canfd, word,
owner = DawSource(params(canfd))
data = source(canfd, status)
owner.update([(BASE, [(owner.address, data, owner.source_bus)])])
addr, output, bus = owner.command(BASE)[0]
addr, output, bus = advisory_candidate(owner, BASE)
assert not safety.safety_tx_hook(packet(addr, output, bus))
safety.safety_rx_hook(packet(addr, data, owner.source_bus))
assert bool(safety.safety_tx_hook(packet(addr, output, bus))) == supported
@@ -60,7 +71,7 @@ def test_source_rejection_reset_and_relay_are_preserved(canfd, fault):
owner = DawSource(params(canfd))
data = source(canfd, 1)
owner.update([(BASE, [(owner.address, data, owner.source_bus)])])
addr, output, bus = owner.command(BASE)[0]
addr, output, bus = advisory_candidate(owner, BASE)
incoming = data[:-4] if fault == 'length' and canfd else data[:-1] if fault == 'length' else data
rx_bus = owner.source_bus + 1 if fault == 'bus' else owner.source_bus
if fault == 'crc':
@@ -95,7 +106,7 @@ def test_one_genuine_counter_predecessor_cannot_hide_newer_hands_off_request():
owner = DawSource(params(True))
data = source(True, 1)
owner.update([(BASE, [(0x11A, data, 1)])])
_, output, _ = owner.command(BASE)[0]
_, output, _ = advisory_candidate(owner, BASE)
safety.safety_rx_hook(packet(0x11A, data, 1))
following = bytearray(data)
following[2] += 1
@@ -110,7 +121,7 @@ def test_one_genuine_counter_predecessor_cannot_hide_newer_hands_off_request():
safety.safety_rx_hook(packet(0x11A, newer, 1))
assert not safety.safety_tx_hook(packet(0x11A, output, 1))
owner.update([(BASE + 10_000_000, [(0x11A, bytes(following), 1)])])
_, prior_output, _ = owner.command(BASE + 10_000_000)[0]
_, prior_output, _ = advisory_candidate(owner, BASE + 10_000_000)
assert safety.safety_tx_hook(packet(0x11A, prior_output, 1))
hands_off = bytearray(newer)
hands_off[2] += 1
@@ -120,8 +131,8 @@ def test_one_genuine_counter_predecessor_cannot_hide_newer_hands_off_request():
safety.safety_rx_hook(packet(0x11A, hands_off, 1))
assert not safety.safety_tx_hook(packet(0x11A, prior_output, 1))
owner.update([(BASE + 20_000_000, [(0x11A, bytes(newer), 1)])])
_, prior_output, _ = owner.command(BASE + 20_000_000)[0]
_, prior_output, _ = advisory_candidate(owner, BASE + 20_000_000)
assert not safety.safety_tx_hook(packet(0x11A, prior_output, 1))
owner.update([(BASE + 30_000_000, [(0x11A, bytes(hands_off), 1)])])
_, current_output, _ = owner.command(BASE + 30_000_000)[0]
_, current_output, _ = advisory_candidate(owner, BASE + 30_000_000)
assert safety.safety_tx_hook(packet(0x11A, current_output, 1))
+1 -1
View File
@@ -1 +1 @@
{"protected_inventory_sha256": "16b87cb64ab51930429e6251dad19d486b669b0c86e4f005f97fa7a5f9045661", "schema_version": 1}
{"protected_inventory_sha256": "fabbd14a4c2dfdaf5c55e515d9b8ba8e9b35f495f28b017bde6ec5df54406c34", "schema_version": 1}