Zephyr v3.7.0 · 네트워크 상태와 알림

인터페이스는 UP인데, 왜 아직 연결되지 않았을까?

net_if_up()이 성공했다고 해서 IP 통신이 성공한 것은 아닙니다. 드라이버의 carrier·association, 주소 할당, Connection Manager의 준비 판정이 각각 다른 관측점이기 때문입니다.

본문 기준: Zephyr 3.7.0 LTS3 · 이후 유지보수 패치는 별도입니다.

기준은 Zephyr v3.7.0 문서와 해당 태그의 공개 API다. 이 글은 상태 관리 경로를 좁혀 설명한다. 패킷 버퍼·소켓 데이터 경로는 Networking 개요에 있다. 아래 흐름은 API 조건을 설명하기 위해 다시 그린 개념도이며 보드 실측 기록이나 인터넷 도달성 보증은 아니다.

1. UP라는 한 단어로는 네 관문을 구별할 수 없다

Zephyr의 struct net_if는 네트워크 장치와 상위 스택 사이의 접점이다. net_if_up()·net_if_down()은 애플리케이션이 관리 상태(admin state)를 바꾸는 API다. 관리상 UP이어도 장치 준비가 안 됐거나 케이블이 빠졌거나 Wi-Fi 연결이 끝나지 않았다면 운영 상태(oper state)는 UP이 아닐 수 있다. 드라이버 또는 L2가 net_if_carrier_on/off()와 net_if_dormant_on/off()로 하위 링크와 association 상태를 알린다.

그림 1. 한 인터페이스가 Connection Manager의 준비 조건에 이르는 관문

왼쪽에서 오른쪽으로 조건을 더합니다. 좁은 화면에서는 가로로 이동하거나 키보드로 영역을 스크롤하세요.

① admin-upnet_if_up() 또는 자동 시작이 인터페이스 사용을 허용합니다.관리 명령일 뿐 물리 링크 확인은 아님
② device · link장치 준비 여부와 드라이버/L2의 carrier·association 보고를 확인합니다.device ready, carrier OK, dormant 아님
③ oper-up앞 조건이 맞으면 NET_IF_OPER_UP가 되고 NET_IF_RUNNING이 설정됩니다.IP 주소는 아직 별도 조건
④ 주소 · watchedIPv4/IPv6 주소가 하나 이상 있고 감시 제외가 아니면 monitor가 ready로 봅니다.ready ≠ 원격 서버 응답 성공

초기화된 인터페이스의 carrier는 기본적으로 켜져 있고 dormant는 기본적으로 꺼져 있다. 이 초기값만 보고 실제 케이블·무선 association을 검증했다고 판단하면 안 된다. 하드웨어와 L2 구현이 변화를 정확히 보고하는지 확인해야 한다. v3.7.0 net_if.c는 관리상 UP뿐 아니라 device_is_ready(), carrier OK, dormant 해제를 살펴 NET_IF_OPER_UP으로 전환한다. Connection Manager의 monitoring을 켰다면 여기에 할당된 IP 주소와 “ignore하지 않음” 조건을 더해 ready를 판정한다.

표 1. 같은 UP처럼 보여도 통과한 조건이 다르다
관측점만드는 주체·API말해 주는 것아직 말하지 않는 것
admin-up앱의 net_if_up(); 설정에 따른 자동 시작인터페이스 사용 허용케이블·association·주소
carrier OK드라이버/L2의 net_if_carrier_on()하위 링크 상태 보고Wi-Fi association·IP 주소
not dormant드라이버/L2의 net_if_dormant_off()대기 중인 연결 절차 해소IP 주소·원격 서비스
oper-upnet_if가 admin·device·link 상태를 조합인터페이스 운용 가능주소 할당과 라우팅 성공
monitor readyConnection Manager monitoringadmin/oper-up + 주소 ≥1 + watchedDNS·게이트웨이·인터넷 왕복

근거: 3.7.0 Network Interface 상태 관리, v3.7.0 net_if.c의 update_operational_state(), 3.7.0 Connection Manager ready 조건.

2. 상태 이벤트는 패킷이나 소켓의 성공 응답이 아니다

net_mgmt는 네트워크 관리 요청과 이벤트를 다룬다. net_mgmt_add_event_callback()으로 등록한 리스너는 관심 이벤트의 알림을 받고, 소켓의 send()·recv()는 따로 데이터 경로를 지난다. 따라서 NET_EVENT_L4_CONNECTED를 보았다고 해서 DNS 질의나 TLS 연결이 성공했다고 기록하면 관측 범위를 넘는다.

표 2. 어느 층의 변화인지 이벤트 이름으로 읽기
이벤트의미확인할 다음 상태
NET_EVENT_IF_ADMIN_UP/DOWN인터페이스를 관리상 켜거나 끔carrier·dormant·oper 상태
NET_EVENT_IF_UP/DOWN인터페이스 운용 상태 변화IP 주소가 있는지
NET_EVENT_IPV4_ADDR_ADD/DELIPv4 주소 추가·삭제monitor가 ready로 판단했는지
NET_EVENT_IPV4_DHCP_BOUNDDHCPv4 바인딩 성립할당된 주소·경로를 별도 확인
NET_EVENT_L4_CONNECTED/DISCONNECTED감시 대상 중 ready 인터페이스가 0↔1 이상으로 바뀜어느 인터페이스·주소 계열인지
NET_EVENT_L4_IPV4_CONNECTED / NET_EVENT_L4_IPV6_CONNECTED해당 주소 계열에서 ready 경계 통과필요한 계열의 실제 요청을 시험

3.7.0의 IPv4·IPv6별 L4 연결/해제 이벤트는 같은 릴리스에서 추가됐다. 하나의 인터페이스가 NET_EVENT_IF_UP을 보낸 뒤 주소를 얻는 경로를 흔히 보지만, 앱 로직을 모든 기술에서 고정된 이벤트 순서 하나에 묶지 말자. DHCP, 정적 주소, 자동 연결, 앱 리스너 등록 시점이 다르다. 이벤트를 받으면 실제 관심 상태를 다시 확인하고, 데이터 통신 성공은 실제 소켓 호출에서 판단한다.

근거: 3.7.0 Network Management 이벤트 정의, 3.7.0 릴리스의 주소 계열별 이벤트 추가.

3. 콜백은 실제 이벤트 코드를 확인하고 짧게 끝낸다

런타임 리스너는 net_mgmt_init_event_callback()으로 마스크와 핸들러를 묶고 net_mgmt_add_event_callback()으로 등록한다. 3.7.0의 핸들러 인수는 uint32_t event다. 마스크는 명령 부분을 비트 집합으로 다루므로 같은 layer/layer-code의 다른 이벤트에도 콜백이 호출될 수 있다. 핸들러 안에서 실제 event 값을 반드시 비교한다. L2의 인터페이스 이벤트와 L4의 연결 이벤트를 함께 보려면 두 리스너를 따로 등록해야 한다.

아래는 CONFIG_NET_MGMT·CONFIG_NET_MGMT_EVENT·CONFIG_NET_CONNECTION_MANAGER를 사용하는 구성에서, Connection Manager monitoring이 이미 초기화된 뒤 앱 초기화에서 호출하는 핵심 패턴이다. 실제 앱은 주석 자리에 작업 큐·상태 머신 통지를 넣고, 리스너 구조체 수명은 등록 기간 내내 유지해야 한다.

#include <zephyr/net/net_mgmt.h> #include <zephyr/net/net_event.h> #include <zephyr/net/conn_mgr_monitor.h> static struct net_mgmt_event_callback l4_cb; static void on_l4(struct net_mgmt_event_callback *cb, uint32_t event, struct net_if *iface) { (void)cb; (void)iface; /* 집계 이벤트를 특정 iface 성공으로 읽지 않는다. */ if (event == NET_EVENT_L4_CONNECTED) { /* 짧은 상태 통지만 남긴다. */ } else if (event == NET_EVENT_L4_DISCONNECTED) { /* 재시도 정책은 앱 작업 문맥에서 결정한다. */ } } static void register_l4_listener_after_monitor_init(void) { net_mgmt_init_event_callback(&l4_cb, on_l4, NET_EVENT_L4_CONNECTED | NET_EVENT_L4_DISCONNECTED); net_mgmt_add_event_callback(&l4_cb); conn_mgr_mon_resend_status(); /* 놓친 최초 상태를 다시 요청 */ }

제거할 때는 net_mgmt_del_event_callback(&l4_cb)를 호출한다. 정적 등록이 필요한 경우에는 NET_MGMT_REGISTER_EVENT_HANDLER도 있지만 핸들러 시그니처와 사용 가능한 추가 정보는 동적 콜백과 다르다. 이벤트 추가 정보는 CONFIG_NET_MGMT_EVENT_INFO가 활성화되고 생성자가 실제로 제공할 때만 사용할 수 있다. 일반 콜백에서 오래 막히는 소켓 작업을 수행하면 이벤트 처리를 지연시킬 수 있다.

표 3. 이벤트 소비자의 계약
방법등록·수명꼭 확인할 것
동적 콜백net_mgmt_init_event_callback() → net_mgmt_add_event_callback(); 필요하면 삭제3.7.0 uint32_t 코드, 구조체 수명, 실제 코드 비교
정적 핸들러NET_MGMT_REGISTER_EVENT_HANDLER로 빌드할 때 등록동적 핸들러와 다른 인수 형태, info 조건
서로 다른 층의 알림layer/layer-code마다 리스너 분리L2 IF와 L4 CONNECTED를 마스크 하나로 합치지 않기
핸들러 본문상태 기록·작업 큐 통지 중심긴 차단 작업과 패킷 처리 경로 혼동 피하기

근거: 3.7.0 콜백·false positive 경고, v3.7.0 net_mgmt.h 시그니처, 이벤트 콜백을 짧게 유지하는 이유.

4. 늦게 등록한 리스너는 최초 연결을 놓칠 수 있다

Connection Manager monitoring은 초기화 직후에도 연결 이벤트를 발생시킬 수 있다. 앱이 그 뒤에 콜백을 등록하면 이미 지나간 첫 변화가 자동으로 보관되어 전달되는 것은 아니다. 3.7.0 문서는 monitoring의 SYS_INIT APPLICATION 우선순위(CONFIG_NET_CONNECTION_MANAGER_MONITOR_PRIORITY)보다 먼저 리스너를 등록하거나, 늦게 등록했으면 conn_mgr_mon_resend_status()로 현재 상태 알림을 다시 요청하도록 안내한다.

그림 2. 같은 ready 상태라도 구독 시점이 다르면 관측이 달라진다

아래 칸은 개념적 순서 예시입니다. DHCP와 드라이버의 실제 이벤트 간격을 측정한 시간표가 아닙니다.

먼저 등록
① callback 등록monitor 초기화 전
② ready 경계 통과L4_CONNECTED 발생 가능
③ 앱이 관측콜백으로 현재 변화 수신
나중에 등록
① ready 경계 통과아직 리스너가 없음
② callback 등록최초 알림을 놓칠 수 있음
③ resend 요청현재 집계 상태를 다시 알림

재전송은 “발생했던 모든 과거 이벤트를 재생”하는 기능이 아니라 현재 connectivity 상태를 다시 알리는 방법이다. 주소 획득 로그, 인터페이스 상태, L4 상태를 서로 다른 관측치로 기록하고, 앱의 처리 함수는 동일 상태 알림이 다시 와도 안전하게 동작하도록 만든다. CONFIG_NET_MGMT_EVENT가 꺼져 있다면 콜백 기반 관측 자체를 사용할 수 없으므로 빌드 설정을 먼저 확인한다.

근거: 3.7.0 최초 알림 누락과 resend, v3.7.0의 실제 선언 헤더 conn_mgr_monitor.h. 3.7.0 설명 문서의 conn_mgr_monitoring.h 표기는 태그된 소스와 달라서 예시에는 실제 헤더 이름을 사용했다.

5. 여러 인터페이스에서 “연결됨”은 집계 판정이다

NET_EVENT_L4_CONNECTED는 감시 중인 ready 인터페이스가 하나 이상 있다는 뜻이다. Wi-Fi와 Ethernet이 모두 ready라면 Wi-Fi만 내려가도 전체 ready는 유지된다. 이때 aggregate NET_EVENT_L4_DISCONNECTED가 반드시 나와야 한다고 기다리면 안 된다. 반대로 감시 대상으로 보는 인터페이스가 모두 준비되지 않으면 전체 disconnected 경계를 지난다.

그림 3. Wi-Fi 손실에도 Ethernet이 남으면 집계 연결은 유지

예시의 두 인터페이스는 모두 IP 주소가 있는 감시 대상이라고 가정합니다.

Wi-Fi iface처음: oper-up + IPv4 주소 → ready이후: association 손실 → unready
Ethernet iface처음: oper-up + IPv4 주소 → ready이후: 여전히 ready
monitor 집계처음: ready 2개 → L4 connected이후: ready 1개 → 집계 connected 유지

3.7.0의 family-specific NET_EVENT_L4_IPV4_CONNECTED와 NET_EVENT_L4_IPV6_CONNECTED는 주소 계열별 준비를 분리해서 본다. 이것도 특정 서버에 대한 성공 응답은 아니다. conn_mgr_ignore_iface()로 감시에서 제외한 인터페이스는 실제 admin/oper 상태가 유지되어도 monitor의 ready 판정에서는 빠진다. “인터넷이 끊긴 것처럼” 보일 때 ignore 설정도 확인할 이유다.

표 4. 이벤트가 안 나온 것이 반드시 누락은 아니다
변화ready 개수집계 L4 알림 해석
첫 인터페이스가 ready0 → 1CONNECTED 경계
두 번째도 ready1 → 2전체 연결 상태는 계속 유지
첫 번째가 unready, 두 번째는 ready2 → 1전체 DISCONNECTED를 기대하지 않음
남은 것도 unready 또는 ignored1 → 0DISCONNECTED 경계

근거: ready 1개 이상과 집계 이벤트, ignore/watch의 효과.

6. “UP인데 통신 안 됨”을 좁히는 순서

먼저 net_if의 관리·동작 상태를 확인하고, 다음에 주소와 Connection Manager monitoring 상태를 확인한다. 그 후 필요한 주소 계열의 DNS, 경로, 원격 서비스 응답을 따로 시험한다. 개발 빌드에서 network shell을 켰다면 net iface로 인터페이스 상태와 주소를 살펴볼 수 있다. 앱에서는 관심 이벤트의 실제 코드와 현재 상태를 따로 기록해 비교한다.

표 5. 알림을 증거로 바꾸는 진단 질문
보이는 현상우선 확인다음 실험
ADMIN_UP만 보임carrier OK, dormant, oper state케이블·association·드라이버 상태 보고 확인
IF_UP인데 L4가 없음주소, ignored 여부, monitor 설정DHCP/정적 주소와 CONFIG_NET_CONNECTION_MANAGER 확인
주소가 있는데 첫 L4 알림 없음리스너 등록 시점등록 후 conn_mgr_mon_resend_status()
Wi-Fi 손실 후 전체 L4 해제 없음다른 ready iface 존재각 인터페이스와 주소 계열별 상태 확인
L4 connected인데 서버 요청 실패DNS·라우트·방화벽·원격 서비스실제 socket 오류와 패킷 경로 추적

구분해야 할 동사: net_if_up()은 관리상 활성화, conn_mgr_if_connect()는 이 API를 지원하도록 바인딩된 인터페이스의 네트워크 association 요청, NET_EVENT_L4_CONNECTED는 monitoring의 IP 준비 관측이다. 서로 대신 호출하거나 성공의 동의어로 쓰지 않는다.

근거: 3.7.0 connectivity control의 바인딩·connect 조건, 3.7.0 Network Shell 명령.

7. Zephyr v3.7.0에 고정한 근거

본문의 API 이름·핸들러 인수·상태 판정은 3.7.0 문서와 v3.7.0 태그를 기준으로 했다. 최신 개발 브랜치에서는 시그니처와 상태 이벤트가 달라질 수 있으므로, 실제 제품의 체크아웃 태그와 생성된 .config를 대조해야 한다. 이 글은 Zephyr 펌웨어를 실제로 빌드·실행한 결과가 아니다.