Skip to content

Commit b6de7fa

Browse files
authored
Add configurable retry count for busy response handling (secdev#5001)
* Add configurable retry count for busy response handling AI-Assisted: yes (GitHub CoPilot Auto)
1 parent 30c07a1 commit b6de7fa

2 files changed

Lines changed: 39 additions & 4 deletions

File tree

scapy/contrib/automotive/scanner/enumerator.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ class ServiceEnumerator(AutomotiveTestCase, metaclass=abc.ABCMeta):
7676
'exit_if_service_not_supported': (bool, None),
7777
'exit_scan_on_first_negative_response': (bool, None),
7878
'retry_if_busy_returncode': (bool, None),
79+
'retry_if_busy_returncode_count': (int, lambda x: x >= 0),
7980
'stop_event': (threading.Event, None),
8081
'debug': (bool, None),
8182
'scan_range': ((list, tuple, range), None),
@@ -118,6 +119,10 @@ class ServiceEnumerator(AutomotiveTestCase, metaclass=abc.ABCMeta):
118119
:param bool retry_if_busy_returncode: Specifies to retry a request, if
119120
the 'busyRepeatRequest' negative
120121
response code is received.
122+
:param int retry_if_busy_returncode_count: Number of retries if a
123+
'busyRepeatRequest' negative
124+
response code is received.
125+
Default is 3.
121126
:param bool debug: Enables debug functions during execute.
122127
:param Event stop_event: Signals immediate stop of the execution.
123128
:param scan_range: Specifies the identifiers to be scanned.
@@ -135,6 +140,7 @@ def __init__(self):
135140
self._results = list() # type: List[_AutomotiveTestCaseScanResult]
136141
self._request_iterators = dict() # type: Dict[EcuState, Iterable[Packet]] # noqa: E501
137142
self._retry_pkt = defaultdict(list) # type: Dict[EcuState, Union[Packet, Iterable[Packet]]] # noqa: E501
143+
self._busy_repeat_request_count = defaultdict(int) # type: Dict[EcuState, int] # noqa: E501
138144
self._negative_response_blacklist = [0x10, 0x11] # type: List[int]
139145
self._requests_per_state_estimated = None # type: Optional[int]
140146
self._tester_present_sender = None # type: Optional[PeriodicSenderThread]
@@ -507,13 +513,27 @@ def _evaluate_retry(self,
507513
): # type: (...) -> bool
508514
retry_if_busy_returncode = \
509515
kwargs.pop("retry_if_busy_returncode", True)
516+
retry_if_busy_returncode_count = \
517+
cast(int, kwargs.pop("retry_if_busy_returncode_count", 3))
510518

511519
if retry_if_busy_returncode and response.service == 0x7f \
512520
and self._get_negative_response_code(response) == 0x21:
513-
log_automotive.debug(
514-
"Retry %s because retry_if_busy_returncode received",
515-
repr(request))
516-
return self._populate_retry(state, request)
521+
if self._busy_repeat_request_count[state] < \
522+
retry_if_busy_returncode_count:
523+
self._busy_repeat_request_count[state] += 1
524+
self._retry_pkt[state] = request
525+
log_automotive.debug(
526+
"Retry %s because retry_if_busy_returncode received",
527+
repr(request))
528+
return True
529+
else:
530+
log_automotive.debug(
531+
"Max busy repeat retries (%d) exceeded for %s",
532+
retry_if_busy_returncode_count, repr(request))
533+
self._busy_repeat_request_count[state] = 0
534+
return False
535+
536+
self._busy_repeat_request_count[state] = 0
517537
return False
518538

519539
def _compute_statistics(self):

test/contrib/automotive/scanner/enumerator.uts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,10 +175,25 @@ assert False == e._evaluate_response(EcuState(session=1), UDS(b"\x10\x03abcd"),
175175
assert False == e._evaluate_response(EcuState(session=1), UDS(b"\x10\x03abcd"), UDS(b"\x7f\x10\x11"), **conf)
176176
assert False == e._evaluate_response(EcuState(session=1), UDS(b"\x10\x03abcd"), UDS(b"\x7f\x10\x7f"), **conf)
177177
assert not e._retry_pkt[EcuState(session=1)]
178+
# Default retry_if_busy_returncode_count=3: three retries before giving up
179+
assert True == e._evaluate_response(EcuState(session=1), UDS(b"\x10\x03abcd"), UDS(b"\x7f\x10\x21"), **conf)
180+
assert e._retry_pkt[EcuState(session=1)] == UDS(b"\x10\x03abcd")
181+
assert True == e._evaluate_response(EcuState(session=1), UDS(b"\x10\x03abcd"), UDS(b"\x7f\x10\x21"), **conf)
182+
assert e._retry_pkt[EcuState(session=1)] == UDS(b"\x10\x03abcd")
178183
assert True == e._evaluate_response(EcuState(session=1), UDS(b"\x10\x03abcd"), UDS(b"\x7f\x10\x21"), **conf)
179184
assert e._retry_pkt[EcuState(session=1)] == UDS(b"\x10\x03abcd")
180185
assert False == e._evaluate_response(EcuState(session=1), UDS(b"\x10\x03abcd"), UDS(b"\x7f\x10\x21"), **conf)
181186
assert not e._retry_pkt[EcuState(session=1)]
187+
# retry_if_busy_returncode_count=1: single retry then give up
188+
conf_count1 = {"retry_if_busy_returncode": True, "retry_if_busy_returncode_count": 1}
189+
assert True == e._evaluate_response(EcuState(session=1), UDS(b"\x10\x03abcd"), UDS(b"\x7f\x10\x21"), **conf_count1)
190+
assert e._retry_pkt[EcuState(session=1)] == UDS(b"\x10\x03abcd")
191+
assert False == e._evaluate_response(EcuState(session=1), UDS(b"\x10\x03abcd"), UDS(b"\x7f\x10\x21"), **conf_count1)
192+
assert not e._retry_pkt[EcuState(session=1)]
193+
# retry_if_busy_returncode_count=0: no retries
194+
conf_count0 = {"retry_if_busy_returncode": True, "retry_if_busy_returncode_count": 0}
195+
assert False == e._evaluate_response(EcuState(session=1), UDS(b"\x10\x03abcd"), UDS(b"\x7f\x10\x21"), **conf_count0)
196+
assert not e._retry_pkt[EcuState(session=1)]
182197

183198
assert True == e._evaluate_response(EcuState(session=1), UDS(b"\x10\x03abcd"), UDS(b"\x50\x03\x00"), **conf)
184199
assert False == e._evaluate_response(EcuState(session=1), UDS(b"\x11\x03abcd"), UDS(b"\x51\x03\x00"), **conf)

0 commit comments

Comments
 (0)