DoIP로 UDS 요청을 보냈는데 이런 응답이 돌아올 때가 있다.
7F xx 31
처음에는 ECU가 요청을 못 받은 것처럼 보인다.
하지만 NRC 0x31 Request Out Of Range가 명확히 왔다면
대부분은 통신층보다 요청한 값이 ECU가 허용하는 범위 밖이라는 쪽에 가깝다.
오늘 메모는 DoIP에서 NRC 0x31이 반복될 때 DID, Routine ID, 세션 조건을 어떻게 나눠 봐야 하는지다.
결론부터
0x31은 먼저 이렇게 보는 편이 빠르다.
DoIP transport 문제
-> TCP timeout, DoIP NACK, payload length mismatch, 무응답
UDS 요청 범위 문제
-> 7F xx 31
즉 7F xx 31이 왔다면 요청은 보통 ECU 애플리케이션까지 도착했다.
이때 처음 볼 것은 socket reconnect가 아니라
그 ECU가 그 세션에서 그 DID, Routine ID, sub-function, address range를 지원하는가다.
흔한 패턴 1: DID가 ECU별로 다르다
ReadDataByIdentifier(0x22)에서 가장 자주 만난다.
테스터는 같은 DID를 여러 ECU에 던진다.
그런데 실제 지원 범위는 ECU마다 다를 수 있다.
Tester -> ECU A: 22 F1 90
ECU A -> Tester: 62 F1 90 ...
Tester -> ECU B: 22 F1 90
ECU B -> Tester: 7F 22 31
이건 ECU B가 죽었다는 뜻이 아니다.
그 ECU가 0xF190을 지원하지 않거나,
현재 variant에서 막혀 있거나,
다른 세션에서만 열리는 DID일 수 있다.
그래서 functional request나 gateway 뒤 여러 ECU를 볼 때는
응답을 하나로 뭉개면 안 된다.
SA/TA별로 어떤 ECU가 어떤 DID를 거부했는지 나눠야 한다.
관련 흐름은 DoIP Functional Address는 왜 응답이 이상하게 보일까와 잘 맞는다.
흔한 패턴 2: Routine ID는 맞는데 control type이 맞지 않는다
RoutineControl(0x31)에서도 0x31이 자주 보인다.
이때 헷갈리는 부분은 이름이 같다.
UDS service id: 0x31 RoutineControl
NRC code: 0x31 Request Out Of Range
로그에 31이 두 번 나오니 처음 보면 더 헷갈린다.
예를 들어 이런 요청이 있다고 하자.
31 01 02 03
대략 이렇게 읽는다.
0x31 -> RoutineControl
0x01 -> startRoutine
0x0203 -> Routine ID
여기서 ECU가 7F 31 31을 돌려주면
보통 아래 중 하나를 먼저 봐야 한다.
- 그 Routine ID를 지원하는가
- start/stop/result control type을 모두 지원하는가
- option record 길이나 값이 허용 범위 안인가
- 현재 세션에서 그 routine이 열려 있는가
즉 서비스 ID가 맞는 것과
그 안의 식별자 범위가 맞는 것은 다른 문제다.
흔한 패턴 3: 세션 문제를 범위 문제처럼 받는다
0x31은 단순히 “없는 ID”만 뜻한다고 보면 부족하다.
일부 ECU는 같은 DID나 Routine ID라도 세션에 따라 응답이 달라진다.
default session
-> 7F 22 31
extended session
-> 62 22 ...
이 경우 요청 ID 자체는 존재한다.
다만 지금 세션에서 접근 가능한 범위가 아닐 뿐이다.
그래서 0x31을 보면
항상 아래를 같이 묶어서 봐야 한다.
- 요청한 DID 또는 Routine ID
- 현재 diagnostic session
- Security Access level
- ECU reset 또는 reconnect 이후 세션이 유지됐는지
세션 흐름은 DoIP에서 Session Control 먼저 이해해야 하는 이유와
DoIP Security Access, 여기서부터 ECU 성격이 확 달라진다에서 이어진다.
흔한 패턴 4: address range가 서비스 정책 밖이다
Upload/Download, memory read/write, routine 계열에서는
식별자뿐 아니라 주소나 길이 범위도 영향을 준다.
예를 들면 이런 식이다.
RequestDownload
-> address 또는 length가 ECU 정책 밖
-> 7F 34 31
ReadMemoryByAddress
-> 허용되지 않은 address range
-> 7F 23 31
이때 payload length가 틀린 것은 0x13 쪽에 가깝다.
반면 포맷은 맞지만 값의 범위가 정책 밖이면 0x31이 자연스럽다.
그래서 0x13과 0x31은 로그에서 분리해 두는 편이 좋다.
0x13
-> 형식 또는 길이가 맞지 않음
0x31
-> 형식은 읽었지만 값이 허용 범위 밖
길이 쪽 디버깅은 DoIP에서 NRC 0x13이 뜬다: 메시지 길이 오류를 TCP 조각 문제로 착각하지 말자와 같이 보면 된다.
0x22와도 다르게 봐야 한다
0x22 Conditions Not Correct와 0x31 Request Out Of Range는 현장에서 자주 섞인다.
둘 다 “지금 요청이 안 된다”처럼 보이기 때문이다.
하지만 디버깅 출발점은 조금 다르다.
0x22
-> ECU 상태 조건을 먼저 본다
-> 전원 모드, 세션 유지, 이전 작업 진행 중 여부
0x31
-> 요청 값의 지원 범위를 먼저 본다
-> DID, Routine ID, address, sub-function, variant
물론 실제 ECU 정책에서는 둘이 겹쳐 보일 수 있다.
그래도 로그와 테스터 정책에서는 구분해 두는 편이 낫다.
관련해서는 DoIP에서 NRC 0x22가 뜬다: 조건 미충족을 통신 timeout처럼 보지 말자와 이어진다.
로그를 이렇게 남기면 빨라진다
0x31은 응답 코드만 찍으면 별 도움이 안 된다.
나는 보통 아래 항목을 같이 남긴다.
- 원 요청 SID
- DID, Routine ID, address, sub-function 같은 식별자 값
- SA와 TA
- 현재 session과 security level
- functional request인지 physical request인지
- ECU variant나 target logical address
예를 들면 이런 식이다.
tx sid=0x22 did=0xF190 sa=0x0E00 ta=0x1002
ctx session=extended security=locked request=physical
rx 7F 22 31
이 정도만 있어도
“통신이 안 갔나?”보다
“이 ECU가 이 DID를 이 조건에서 지원하나?”로 바로 넘어갈 수 있다.
빠른 체크리스트
7F xx 31이 명확하면 DoIP transport 실패와 먼저 분리- 요청한 DID/Routine ID/sub-function이 대상 ECU에서 지원되는지 확인
- 같은 요청을 다른 ECU나 functional request 결과와 섞어 판단하지 않는지 확인
- 현재 session/security level에서 접근 가능한 범위인지 확인
- 주소, 길이, option record 값이 서비스 정책 범위 안인지 확인
0x13,0x22,0x31을 같은 실패 코드로 뭉개지 않는지 확인
함께 보면 좋은 글
- DoIP에서 응답이 이상할 때, Negative Response부터 봐야 한다
- DoIP에서 NRC 0x13이 뜬다: 메시지 길이 오류를 TCP 조각 문제로 착각하지 말자
- DoIP에서 NRC 0x22가 뜬다: 조건 미충족을 통신 timeout처럼 보지 말자
- DoIP Functional Address는 왜 응답이 이상하게 보일까
한 줄 요약
DoIP에서 NRC 0x31이 반복되면 TCP나 Routing Activation보다 먼저, 요청한 DID/Routine ID/address가 그 ECU와 현재 세션에서 지원되는 범위인지 확인하는 편이 빠르다.
추천 키워드
DoIP, UDS, NRC 0x31, Request Out Of Range, ReadDataByIdentifier, RoutineControl
DevBJ | 오늘을살자, Log Today