Skip to content
Go DevBJ
Go back

DoIP Functional Address는 왜 응답이 이상하게 보일까

Edit page

DoIP에서 처음에는 대부분 physical request만 테스트한다.

예를 들면:

Tester -> 특정 ECU

이런 형태다.

그런데 실제 차량에서는 functional request도 꽤 자주 사용된다.

예를 들면:

모든 ECU에게 DiagnosticSessionControl 요청

같은 경우다.

이때부터 로그가 갑자기 복잡해진다.

왜냐하면 functional addressing은 point-to-point가 아니라
group diagnostic에 가깝기 때문이다.

같이 보면 좋은 글

Physical vs Functional

먼저 차이부터 단순하게 보면 이렇다.

Physical
= 특정 ECU 대상

Functional
= ECU 그룹 대상

예를 들면:

TA = 0x1001

이면 특정 ECU 대상이고,

TA = Functional Group

이면 여러 ECU가 동시에 받을 수 있다.

흐름 자체가 달라진다

Physical request는 단순하다.

Tester
  |
  | Request
  v
Single ECU
  |
  | Response
  v
Tester

Functional request는 흐름이 달라진다.

Tester
  |
  | Functional Request
  v
Multiple ECU
  ├── Response #1
  ├── Response #2
  ├── Response #3
  └── ...

즉:

1 request
→ multiple responses

구조가 된다.

그래서 로그가 복잡해진다

초기 구현은 보통 이런 가정을 한다.

send_request()
wait_response()

하지만 functional request에서는 이 가정이 위험하다.

왜냐하면:

어떤 ECU가 먼저 응답할지 모름

상황이기 때문이다.

응답 순서는 보장되지 않는다

예를 들어:

ECU A -> 10ms
ECU B -> 30ms
ECU C -> 120ms

일 수 있다.

심지어 부하 상태에 따라 매번 달라진다.

그래서:

response ordering assumption

을 넣으면 나중에 흔들린다.

테스트 환경에서는 항상 ECU A가 먼저 왔는데,
실차에서는 ECU B가 먼저 오고,
가끔 ECU C가 늦게 따라오는 식이다.

이걸 single response 모델로 처리하면
정상 응답도 이상한 로그처럼 보이기 시작한다.

현장에서 자주 보는 문제

1) 첫 응답만 읽고 종료

예를 들면 이런 구조다.

resp = recv()
return resp

그러면 뒤에 오는 ECU 응답을 놓친다.

특히 ECU scan 기능 만들 때 자주 발생한다.

첫 번째 응답만 보면:

functional request 성공

처럼 보일 수 있다.

하지만 실제로는 뒤에 온 ECU 응답을 버린 상태일 수 있다.

2) timeout 기준이 애매함

Functional request는 결국 이 질문으로 간다.

언제 응답 수집을 종료할 것인가

예를 들면:

100ms wait?
500ms wait?
응답 없으면 종료?

정책이 필요하다.

실제로는 이런 기준을 섞어서 많이 본다.

단순히 전체 timeout 하나만 두면
너무 빨리 끝나거나 너무 오래 기다리게 된다.

3) ECU별 response 분리가 안 됨

Payload만 보면 안 된다.

반드시:

SA = source address

를 같이 봐야 한다.

예시 로그:

[doip] rx sa=0x1001
[doip] rx sa=0x1002
[doip] rx sa=0x1003

이렇게 남겨야 누가 응답했는지 보인다.

Functional request에서 source address를 로그에 안 남기면
나중에 trace를 봐도 구분이 어렵다.

Gateway 환경에서는 더 복잡해진다

Gateway가 functional request를 내부 bus로 forwarding할 수 있다.

예를 들면:

Ethernet
  |
Gateway
  ├── CAN ECU
  ├── LIN ECU
  └── Ethernet ECU

이 경우 응답 timing은 더 흔들린다.

이런 것들이 영향을 준다.

그래서 같은 functional request를 보내도
매번 응답 순서가 달라질 수 있다.

이건 꼭 비정상이라고 볼 수 없다.

Broadcast처럼 보이지만 완전히 같지는 않다

가끔 이렇게 이해하는 경우가 있다.

functional = broadcast

완전히 틀린 느낌은 아니지만,
실제로는 diagnostic policy가 포함된다.

예를 들어 ECU마다:

이 다를 수 있다.

즉:

모든 ECU가 반드시 응답하는 건 아님

이다.

그래서 functional request를 보냈는데
응답이 일부 ECU에서만 와도,
그 자체만으로 실패라고 단정하면 안 된다.

suppress positive response도 영향을 준다

UDS에는 이런 개념이 있다.

Suppress Positive Response

Functional request와 같이 쓰이면 이런 상황이 나온다.

request는 처리
BUT
positive response는 생략

그래서:

응답 없음 = 실패

로 보면 안 되는 경우가 있다.

물론 negative response는 별도로 봐야 한다.

핵심은 이거다.

추천하는 수집 구조

Functional request는 보통 이런 흐름이 안전하다.

send request
→ collect responses
→ inactivity timeout
→ finalize

예시 느낌은 이렇다.

responses = []

send_functional_request()

while not total_timeout_expired():
    resp = try_recv()

    if resp:
        responses.append(resp)
        reset_inactivity_timer()

    if inactivity_timeout_expired():
        break

finalize(responses)

즉:

single response model

보다:

response collection model

이 더 맞다.

Wireshark에서도 주의할 점

Functional request는 패킷 수가 갑자기 늘어난다.

특히:

1 request
→ many responses

라서 trace 읽기가 어려워진다.

이럴 때는 이런 기준으로 보면 좋다.

개인적으로는 먼저 source address별로 나눠서 본다.

그래야:

누가 빨랐고
누가 늦었고
누가 응답하지 않았는지

가 보인다.

오늘 포인트

DoIP Functional Address는 단순 single ECU request가 아니라
multi-response diagnostic 흐름에 가깝다.

실제로 중요한 건 이 부분이다.

Functional diagnostic 흐름을 이해하기 시작하면
multi ECU 환경 로그가 훨씬 읽히기 쉬워진다.

다음 글에서는 DoIP에서 concurrent diagnostic session이
왜 충돌하는지 이어서 다뤄보면 좋겠다.

추천 대상

한 줄 요약

DoIP Functional Address는 하나의 요청에 여러 ECU 응답이 연결되는 multi-response diagnostic 구조에 가깝다.

추천 키워드

doip, functional address, uds functional request, automotive ethernet, gateway routing, multi ecu


DevBJ | No Bio, Just Log #오늘을살자


Edit page
Share this post on:

Next Post
lwIP에서 ISR에서 바로 보내면 가끔 터진다: tcpip_thread로 넘기는 패턴 정리