임베디드 장비에서 링크가 한번 끊겼다가 돌아오면 이상한 상태가 남을 때가 있다.
- PHY 링크는 다시 올라왔다
- IP 주소도 그대로 있다
- RX는 들어오는 것 같다
- 그런데 UDP 송신만 한동안 안 되거나 첫 패킷이 사라진다
이때 DHCP부터 의심하기 쉽지만,
정작 원인은 더 아래에 있는 경우가 많다.
오늘 메모는 링크 복구 뒤 남아 있는 ARP 캐시와 netif 상태 이야기다.
먼저 현상을 나눠 보자
이 이슈는 보통 두 형태로 보인다.
1) 첫 UDP 한두 개만 사라진다
애플리케이션 로그에는 sendto()가 성공처럼 보이는데,
상대는 못 받는다.
잠시 뒤 다시 보내면 정상으로 붙는다.
2) 한동안 계속 안 되다가 갑자기 풀린다
- 링크는 up
- 인터페이스도 up
- 그런데 ARP 해상도가 끝날 때까지 실제 payload 전송이 지연
이걸 “UDP가 불안정하다”로 보면 원인을 놓치기 쉽다.
왜 링크 복구 뒤에만 이런가
링크가 끊겼다가 올라오면
물리 상태만 바뀌는 게 아니다.
- 스위치 포트 상태가 초기화될 수 있다
- 상대 장비 ARP 테이블도 비워질 수 있다
- 내 장비의 오래된 ARP 엔트리가 아직 남아 있을 수 있다
즉 netif_set_link_up()이 호출됐다고 해서
바로 “상대 MAC을 알고 있고 바로 보낼 수 있다”가 아니다.
특히 고정 IP + UDP 주기 송신 장비는
DHCP 이벤트가 없어서 더 늦게 눈치채는 경우가 있다.
흔한 오해: IP가 있으니 바로 나가야 한다
IP가 살아 있어도
이더넷 송신은 결국 목적지 MAC이 있어야 한다.
ARP 캐시가 애매한 상태면 흐름은 대충 이렇게 된다.
UDP payload 송신 시도
-> 목적지 MAC 확인 필요
-> ARP 엔트리 stale 또는 없음
-> ARP 요청 먼저 발생
-> 응답 오기 전까지 실제 UDP payload는 대기 또는 드롭처럼 보임
그래서 “첫 패킷만 사라진다”는 체감이 나온다.
실제로는 UDP 자체보다
그 전에 필요한 L2 해상도가 늦은 것이다.
자주 틀리는 구현 포인트
1) link down 때 상태를 너무 적게 내린다
케이블 빠짐을 감지했는데도
애플리케이션이 계속 송신 루프를 유지하면,
상위에서는 계속 성공처럼 보이는 로그가 쌓일 수 있다.
이 상태에서 link up이 돌아오면
이전 ARP 상태와 새 링크 상태가 섞여서
첫 복구 구간이 더 지저분해진다.
2) link up 직후 첫 전송을 너무 빨리 시작한다
링크 up 이벤트만 보고 곧바로 주기 UDP를 재개하면,
ARP 요청/응답이 끝나기 전에 첫 payload가 걸린다.
그러면 현상은 이렇게 보인다.
- 앱: 송신 성공
- 네트워크: 아직 목적지 MAC 해상도 중
- 사용자: 첫 패킷 유실
3) 상대도 링크 복구 중이라는 점을 빼먹는다
내 장비 링크가 올라온 시점과
상대 스위치/상대 보드가 실제로 패킷을 받을 준비가 된 시점은 다를 수 있다.
특히 보드 간 직접 연결이 아니라
중간 스위치나 게이트웨이가 있는 환경에서는
이 시간차가 더 커진다.
실무에서 보는 패킷 흐름
이 문제를 캡처로 보면 대개 이런 순서가 나온다.
link down
-> link up
-> 첫 UDP 송신 시도
-> ARP who-has 발생
-> ARP reply 수신
-> 그 다음 UDP부터 정상
즉 첫 UDP가 없어 보이더라도
ARP가 먼저 움직였는지 확인하면 그림이 맞아떨어진다.
반대로 ARP 자체가 안 보이면,
그때는 netif 상태 전환이나 드라이버 송신 경로를 더 의심해야 한다.
로그를 이렇게 남기면 빨라진다
링크 복구 이슈는 앱 로그만 보면 거의 안 풀린다.
나는 보통 아래 항목을 같은 시간축에 둔다.
netif_set_link_down()/netif_set_link_up()시각netif_set_down()/netif_set_up()사용 여부- 첫 UDP 송신 시각
- ARP request/reply 시각
- 상대가 실제 첫 UDP를 받은 시각
이걸 붙여 보면
“UDP가 죽었다”가 아니라
“ARP 해상도 끝나기 전에 첫 송신이 들어갔다”는 식으로 바로 바뀐다.
구현 쪽에서 무난한 패턴
안전하게 가려면 보통 아래 원칙이 좋다.
link down
-> 상위 송신 루프에 상태 전달
-> 필요하면 재전송/큐 정책 정리
link up
-> netif 상태 갱신
-> 첫 주기 송신을 바로 시작하지 말고 복구 상태 진입
-> ARP 해상도 또는 준비 시간 이후 정상 주기 복귀
프로젝트마다 구현은 다르지만,
핵심은 link recovery 구간을 평상시 송신과 분리하는 것이다.
예를 들면:
- 첫 주기 패킷만 짧게 지연
- 상대 IP에 대해 준비 패킷 또는 ARP 해상도 완료 후 본 송신 시작
- 복구 중 상태를 앱에서 명시적으로 표시
빠른 체크리스트
- 링크 복구 직후 첫 UDP 유실이 항상 같은 패턴인지 확인
- 그 시점에 ARP request/reply가 보이는지 확인
- link down 동안에도 앱이 계속 송신 중이지 않았는지 확인
- link up 직후 바로 주기 송신을 재개하고 있지 않은지 확인
- 상대 장비도 같은 시점에 복구 중인지 확인
한 줄 요약
lwIP에서 링크 복구 뒤 UDP가 한동안 죽어 보이면, UDP 스택보다 먼저 netif 링크 상태 전환과 ARP 캐시 재해상도 구간을 같이 보는 게 훨씬 빠르다.
추천 키워드
lwIP, ARP, UDP, netif, link recovery, embedded network
DevBJ | 오늘을살자, Log Today