Skip to content
오늘을살자
Go back

DoIP에서 NRC 0x31이 뜬다: DID와 Routine ID 범위를 세션 문제와 나눠 보자

Edit page

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을 돌려주면
보통 아래 중 하나를 먼저 봐야 한다.

즉 서비스 ID가 맞는 것과
그 안의 식별자 범위가 맞는 것은 다른 문제다.

흔한 패턴 3: 세션 문제를 범위 문제처럼 받는다

0x31은 단순히 “없는 ID”만 뜻한다고 보면 부족하다.

일부 ECU는 같은 DID나 Routine ID라도 세션에 따라 응답이 달라진다.

default session
-> 7F 22 31

extended session
-> 62 22 ...

이 경우 요청 ID 자체는 존재한다.
다만 지금 세션에서 접근 가능한 범위가 아닐 뿐이다.

그래서 0x31을 보면
항상 아래를 같이 묶어서 봐야 한다.

  1. 요청한 DID 또는 Routine ID
  2. 현재 diagnostic session
  3. Security Access level
  4. 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이 자연스럽다.

그래서 0x130x31은 로그에서 분리해 두는 편이 좋다.

0x13
-> 형식 또는 길이가 맞지 않음

0x31
-> 형식은 읽었지만 값이 허용 범위 밖

길이 쪽 디버깅은 DoIP에서 NRC 0x13이 뜬다: 메시지 길이 오류를 TCP 조각 문제로 착각하지 말자와 같이 보면 된다.

0x22와도 다르게 봐야 한다

0x22 Conditions Not Correct0x31 Request Out Of Range는 현장에서 자주 섞인다.

둘 다 “지금 요청이 안 된다”처럼 보이기 때문이다.

하지만 디버깅 출발점은 조금 다르다.

0x22
-> ECU 상태 조건을 먼저 본다
-> 전원 모드, 세션 유지, 이전 작업 진행 중 여부

0x31
-> 요청 값의 지원 범위를 먼저 본다
-> DID, Routine ID, address, sub-function, variant

물론 실제 ECU 정책에서는 둘이 겹쳐 보일 수 있다.
그래도 로그와 테스터 정책에서는 구분해 두는 편이 낫다.

관련해서는 DoIP에서 NRC 0x22가 뜬다: 조건 미충족을 통신 timeout처럼 보지 말자와 이어진다.

로그를 이렇게 남기면 빨라진다

0x31은 응답 코드만 찍으면 별 도움이 안 된다.

나는 보통 아래 항목을 같이 남긴다.

  1. 원 요청 SID
  2. DID, Routine ID, address, sub-function 같은 식별자 값
  3. SA와 TA
  4. 현재 session과 security level
  5. functional request인지 physical request인지
  6. 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를 이 조건에서 지원하나?”로 바로 넘어갈 수 있다.

빠른 체크리스트

  1. 7F xx 31이 명확하면 DoIP transport 실패와 먼저 분리
  2. 요청한 DID/Routine ID/sub-function이 대상 ECU에서 지원되는지 확인
  3. 같은 요청을 다른 ECU나 functional request 결과와 섞어 판단하지 않는지 확인
  4. 현재 session/security level에서 접근 가능한 범위인지 확인
  5. 주소, 길이, option record 값이 서비스 정책 범위 안인지 확인
  6. 0x13, 0x22, 0x31을 같은 실패 코드로 뭉개지 않는지 확인

함께 보면 좋은 글

한 줄 요약

DoIP에서 NRC 0x31이 반복되면 TCP나 Routing Activation보다 먼저, 요청한 DID/Routine ID/address가 그 ECU와 현재 세션에서 지원되는 범위인지 확인하는 편이 빠르다.

추천 키워드

DoIP, UDS, NRC 0x31, Request Out Of Range, ReadDataByIdentifier, RoutineControl


DevBJ | 오늘을살자, Log Today


Edit page
Share this post on:

Previous Post
lwIP에서 송신이 가끔 멈춘다: linkoutput에서 TX descriptor와 pbuf 수명을 같이 보자
Next Post
lwIP에서 RX pbuf를 넘긴 뒤 가끔 깨진다: tcpip_input ownership을 먼저 정리하자