lwIP에서 discovery나 간단한 beacon을 만들 때 UDP broadcast를 자주 쓴다.
그런데 코드상으로는 sendto()나 udp_sendto()를 호출했는데
패킷 캡처에는 아무것도 안 보일 때가 있다.
이때 payload 포맷부터 의심하기 쉽다.
하지만 broadcast가 아예 wire에 안 보이면 payload보다 앞단을 봐야 한다.
오늘 메모는 lwIP에서 UDP broadcast가 안 나갈 때 socket option, netif flag, source IP, 송신 인터페이스를 나눠 보는 순서다.
결론부터
UDP broadcast 문제는 먼저 세 층으로 나눠 본다.
API 사용 문제
-> SO_BROADCAST 또는 SOF_BROADCAST 설정 누락
netif 설정 문제
-> NETIF_FLAG_BROADCAST 누락, netif down, link down
주소와 route 문제
-> 아직 IP가 없음, 잘못된 subnet broadcast, 엉뚱한 netif 선택
payload가 틀렸다면 상대가 이상한 데이터를 받을 수는 있다.
하지만 캡처에 패킷 자체가 없다면
대부분은 송신 허가, 인터페이스 상태, route 쪽에서 막힌다.
그래서 “UDP가 안 된다”가 아니라
broadcast packet이 실제로 wire에 나갔는가를 먼저 확인해야 한다.
흔한 패턴 1: SO_BROADCAST를 켜지 않는다
socket API를 쓰면 broadcast 송신 전에 보통 SO_BROADCAST를 켜야 한다.
int yes = 1;
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes));
이 설정이 빠지면 broadcast 주소로 보낸 요청이 실패하거나,
lwIP 설정에 따라 송신 경로에서 거부될 수 있다.
RAW API를 쓰는 코드에서는 PCB option을 확인한다.
ip_set_option(upcb, SOF_BROADCAST);
프로젝트의 lwIP 버전과 옵션에 따라 세부 동작은 조금 다를 수 있다.
그래도 출발점은 같다.
255.255.255.255나 subnet broadcast 주소로 보낼 때는
해당 PCB나 socket이 broadcast 송신을 허용하는 상태인지 먼저 봐야 한다.
흔한 패턴 2: netif가 broadcast를 지원한다고 표시하지 않았다
Ethernet netif 초기화 코드에서 flag를 빠뜨리는 경우도 있다.
대략 이런 값을 확인한다.
NETIF_FLAG_UP
NETIF_FLAG_LINK_UP
NETIF_FLAG_BROADCAST
NETIF_FLAG_ETHARP
NETIF_FLAG_ETHERNET
특히 custom driver 포팅에서는 netif->flags를 최소값만 넣고 시작하다가
broadcast flag를 빼먹기 쉽다.
초기화 코드는 보통 이런 형태를 가진다.
netif->flags |= NETIF_FLAG_BROADCAST;
netif->flags |= NETIF_FLAG_ETHARP;
netif->flags |= NETIF_FLAG_ETHERNET;
무작정 flag를 추가하자는 뜻은 아니다.
실제 링크 타입과 driver 동작이 맞아야 한다.
다만 Ethernet 기반 lwIP 포팅에서 broadcast가 안 나가면
netif가 broadcast 가능한 인터페이스로 등록되어 있는지 확인해야 한다.
흔한 패턴 3: DHCP bound 전에 broadcast를 시작한다
앱 태스크가 너무 빨리 뜨면 아직 source IP가 준비되지 않았을 수 있다.
netif_set_up()
dhcp_start()
app starts discovery
udp broadcast send
dhcp bound
이 흐름에서는 broadcast packet의 source IP가 애매하거나,
route 선택이 기대와 달라질 수 있다.
bring-up 로그에는 아래를 같이 남기는 편이 좋다.
netif=en0 up=1 link=1 ip=192.168.0.10 mask=255.255.255.0 gw=192.168.0.1
udp_bcast dst=192.168.0.255 port=30000 ret=ERR_OK
반대로 앱 로그가 DHCP bound 로그보다 먼저 나오면
앱 시작 조건을 다시 봐야 한다.
DHCP와 netif 상태 순서는 lwIP에서 링크는 붙었는데 DHCP가 안 돈다: netif_set_up() / netif_set_link_up() 순서를 같이 보자와 이어진다.
흔한 패턴 4: limited broadcast와 subnet broadcast를 섞어 쓴다
broadcast 주소도 하나로만 보지 않는 편이 좋다.
255.255.255.255
-> limited broadcast
192.168.0.255
-> subnet-directed broadcast 예시
단일 인터페이스 보드에서는 둘 다 비슷하게 보일 수 있다.
하지만 multi-netif 환경이나 gateway가 있는 구조에서는 차이가 커진다.
255.255.255.255는 어느 인터페이스로 나갈지 기대와 다를 수 있다.
subnet broadcast는 IP와 netmask가 제대로 잡혀 있어야 계산이 맞다.
여러 netif가 있으면 송신 인터페이스를 명시하는 API를 쓰는 편이 디버깅이 쉽다.
udp_sendto_if(upcb, p, &dst, port, netif);
이렇게 하면 “broadcast가 안 나간다”와
“다른 포트나 다른 netif로 나갔다”를 분리할 수 있다.
흔한 패턴 5: router가 broadcast를 넘겨줄 거라고 기대한다
UDP broadcast는 로컬 링크 안에서 생각하는 편이 안전하다.
임베디드 장비 discovery를 만들 때 이런 기대가 들어가면 문제가 된다.
board A: 192.168.0.10/24
board B: 192.168.1.20/24
tester sends broadcast
라우터가 broadcast를 다른 subnet으로 넘겨줄 것이라고 기대하면 테스트가 흔들린다.
일반적인 네트워크에서는 broadcast forwarding이 제한되는 경우가 많다.
이때 lwIP 문제처럼 보이지만 실제로는 네트워크 설계 문제다.
같은 L2 구간에서 캡처했을 때 packet이 나가는지,
상대 장비가 같은 subnet에 있는지,
VLAN이나 스위치 정책이 broadcast를 막고 있지 않은지 따로 확인해야 한다.
DoIP discovery 쪽 문제는 DoIP Vehicle Discovery가 가끔 안 잡히는 이유와도 비슷한 흐름으로 볼 수 있다.
송신은 보이는데 상대가 못 받는 경우
캡처에 broadcast packet이 보이면 다음 단계는 수신 쪽이다.
이때는 송신 lwIP보다 상대 장비의 조건을 본다.
- UDP port가 맞는지
- 상대가 해당 interface에서 bind했는지
- firewall이나 OS 정책이 broadcast 수신을 막는지
- checksum offload나 checksum 설정이 맞는지
- VLAN, switch, AP isolation 같은 L2 정책이 있는지
반대로 캡처에 송신 packet이 없다면
수신 쪽을 보기 전에 lwIP 송신 경로를 끝까지 확인하는 편이 낫다.
checksum offload 문제는 lwIP에서 ping은 되는데 TCP나 UDP만 깨진다: checksum offload 설정을 먼저 의심하자와 같이 보면 좋다.
로그를 이렇게 남기면 빨라진다
broadcast 송신 실패는 return code 하나만 보면 부족하다.
나는 아래 항목을 한 줄에 모아 남긴다.
- destination IP와 port
- 선택된 netif 이름
- netif IP, netmask, gateway
- netif up/link/broadcast flag
- socket 또는 PCB broadcast option 상태
udp_sendto()또는sendto()반환값- 패킷 캡처에서 실제 TX가 보였는지
예를 들면 이런 식이다.
udp_bcast dst=192.168.0.255:30000 netif=en0
ip=192.168.0.10 mask=255.255.255.0 up=1 link=1 bcast=1 opt_bcast=1
ret=ERR_OK capture_tx=yes
이 로그가 있으면
“payload가 이상한가?”보다
“broadcast 송신 조건이 갖춰졌나?”를 먼저 볼 수 있다.
빠른 체크리스트
- socket API라면
SO_BROADCAST를 설정했는지 확인 - RAW API라면 PCB의 broadcast option을 확인
netif에NETIF_FLAG_BROADCAST가 있고 up/link 상태인지 확인- DHCP bound 또는 static IP 설정 완료 후 송신하는지 확인
255.255.255.255와 subnet broadcast 주소를 구분해 테스트- multi-netif 환경에서는 실제 송신 인터페이스를 명시하거나 로그에 남김
- 같은 L2 구간에서 packet capture로 wire TX 여부를 확인
함께 보면 좋은 글
- lwIP에서 링크는 붙었는데 DHCP가 안 돈다: netif_set_up() / netif_set_link_up() 순서를 같이 보자
- lwIP에서 UDP send가 ERR_RTE로 실패한다: payload보다 netif 상태와 route를 먼저 보자
- lwIP에서 링크 복구 후 UDP가 한동안 죽어 보인다: ARP 캐시와 netif 상태를 같이 보자
- lwIP에서 ping은 되는데 TCP나 UDP만 깨진다: checksum offload 설정을 먼저 의심하자
한 줄 요약
lwIP에서 UDP broadcast packet이 보이지 않으면 payload보다 먼저 SO_BROADCAST 또는 SOF_BROADCAST, NETIF_FLAG_BROADCAST, netif up/link, source IP와 송신 인터페이스를 같은 로그에서 확인하는 편이 빠르다.
추천 키워드
lwIP, UDP broadcast, SO_BROADCAST, netif, RTOS, Embedded Network
DevBJ | 오늘을살자, Log Today