Skip to content
오늘을살자
Go back

lwIP에서 UDP 길이가 가끔 잘못 읽힌다: pbuf 체인에서는 len 말고 tot_len을 봐야 한다

Edit page

lwIP로 UDP 수신 코드를 붙였는데 데이터 길이가 이상하게 보일 때가 있다.

이럴 때 드라이버나 DMA부터 의심하기 쉽지만,
먼저 확인할 곳은 의외로 단순하다.

pbuf->len을 전체 길이처럼 쓰고 있지 않은가

오늘 메모는 pbuf 체인에서 lentot_len을 헷갈릴 때 생기는 버그다.

두 필드는 역할이 다르다

lwIP의 pbuf는 한 덩어리일 수도 있고,
여러 버퍼가 체인으로 이어진 형태일 수도 있다.

이때 의미는 이렇게 나뉜다.

즉 첫 노드에서:

p->len     = 첫 조각 길이
p->tot_len = 패킷 전체 길이

작은 패킷에서는 둘이 같을 수 있어서
초기 bring-up 때는 문제를 못 느끼기 쉽다.

왜 가끔만 터지나

버그가 늦게 드러나는 이유는 체인이 항상 생기지 않기 때문이다.

그래서 테스트 초반에는 잘 되다가,
특정 길이 이상에서만 갑자기 잘리는 증상이 나온다.

현장에서 자주 보는 실수

1) 복사 길이에 len을 그대로 쓴다

가장 흔한 패턴은 이거다.

memcpy(app_buf, p->payload, p->len);
app_parse(app_buf, p->len);

작은 패킷이면 통과한다.
하지만 체인이 생기면 첫 조각만 복사하고 끝난다.

그 결과는 대개 이런 식이다.

2) p->payload가 연속 메모리라고 가정한다

체인 pbuf에서는 전체 패킷이 한 연속 버퍼가 아닐 수 있다.

즉 이런 가정이 위험하다.

p->payload부터 p->tot_len 바이트가 한 번에 이어져 있다

실제로는 다음 조각이 p->next에 있을 수 있다.
그래서 전체 payload가 필요하면:

3) 로그에는 tot_len, 실제 파싱은 len

이것도 자주 헷갈린다.

log: rx len = p->tot_len
copy: memcpy(..., p->payload, p->len)

그러면 로그상으로는 300바이트를 받았는데
실제 앱 버퍼에는 128바이트만 들어간 식이 된다.

디버깅할 때 “중간에 누가 데이터를 잃어버렸나”로 잘못 흘러가기 쉽다.

간단한 예로 보면

예를 들어 UDP payload 300바이트가
아래처럼 둘로 나뉘어 들어왔다고 하자.

첫 pbuf:  len=128, tot_len=300
둘째 pbuf: len=172, tot_len=172

여기서 첫 노드만 보고 처리하면
앱은 128바이트짜리 패킷을 받은 것처럼 행동한다.

반면 전체 길이 검증은 300으로 찍힐 수 있어서
로그와 동작이 서로 안 맞는다.

구현 쪽에서 무난한 패턴

전체 패킷이 연속 버퍼로 꼭 필요하다면
먼저 전체 길이를 tot_len으로 확보하고,
체인을 끝까지 순회하며 복사하는 편이 안전하다.

needed = p->tot_len
for each node in chain:
  copy node->len bytes

반대로 헤더 몇 바이트만 보면 되는 프로토콜이면
굳이 전체 복사를 하지 말고,
체인을 순회하면서 필요한 필드만 읽는 쪽이 메모리 낭비가 적다.

핵심은 단순하다.

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

이 문제는 길이 하나만 찍으면 잘 안 보인다.
나는 보통 아래 항목을 같이 본다.

  1. 첫 pbuf의 len
  2. 첫 pbuf의 tot_len
  3. 체인 노드 개수
  4. 실제 앱 버퍼에 복사한 길이
  5. 파서가 소비한 길이

이 다섯 개가 있으면
“네트워크가 잘랐다”와
“앱이 첫 조각만 읽었다”를 금방 구분할 수 있다.

빠른 체크리스트

  1. 수신 코드가 p->len을 전체 패킷 길이처럼 쓰는지 확인
  2. 큰 UDP payload에서만 문제 재현되는지 확인
  3. p->payload를 연속 메모리로 가정하는 코드가 있는지 확인
  4. 체인 순회 없이 첫 노드만 memcpy 하는 경로가 있는지 확인
  5. 로그 길이와 실제 복사 길이를 분리해서 기록하는지 확인

한 줄 요약

lwIP에서 UDP 길이가 가끔 잘못 읽히면 드라이버보다 먼저 pbuf 체인 처리 코드를 보고, 전체 길이는 tot_len, 현재 조각 길이는 len으로 분리해서 다루는 게 안전하다.

추천 키워드

lwIP, pbuf, UDP, tot_len, RX buffer, embedded network


DevBJ | 오늘을살자, Log Today


Edit page
Share this post on:

Next Post
DoIP에서 ECU Reset 후 다시 안 붙는다: 소켓 종료와 재연결 순서를 같이 봐야 한다