Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

3 Commits

Folders and files

Repository files navigation

Android USB 테더링 (CDC-NCM) → macOS

케이블 하나로 맥이 안드로이드 폰의 인터넷을 쓰게 만드는 절차·스크립트·실측 근거. Redmi 14C(HyperOS / Android 15)에서 2026-08-18 완주했다.

핵심은 두 줄이다. macOS 에는 RNDIS 드라이버가 없으니 USB 가젯을 CDC-NCM 으로 바꿔야 하고, 그 전환은 settings put global tether_force_usb_functions 1 + 재부팅 1회로 해야 한다.

맥 앱

이 절차를 안내와 상태 점검으로 감싼 메뉴바 앱이 있다 — adb 를 번들해서 Android SDK 설치가 필요 없고, 6단계 중 어디서 막혔는지와 무엇을 해야 하는지를 보여준다. 소스는 비공개 레포이고 배포본은 릴리스에서 받는다.

빠른 시작 (스크립트)

./install.sh                                                   # 스킬 + adb 설치
# 폰 연결 → USB 디버깅 허용 → 폰 잠금해제(UI 탭이 필요하다)
~/.claude/skills/android-usb-tethering/scripts/ncm_tether.sh   # 세팅 + 검증

설정이 아직 반영되지 않았으면 스크립트가 exit 2 로 멈추고 재부팅을 요구한다. AUTO_REBOOT=1 을 붙여 실행하면 알아서 재부팅한다. 재부팅은 최초 1회뿐이다.

ncm_tether.sh status          # 현재 상태만
ncm_tether.sh off             # 테더링 끄고 USB 기능 원복
ncm_tether.sh --serial <SN>   # 기기가 여러 대일 때
USB_LABEL="USB tethering" ncm_tether.sh   # 영문 UI 기기

설치 후에는 Claude Code 에서 "안드로이드 USB 테더링 해줘" 라고만 해도 스킬이 잡힌다 (~/.claude/skills/ 전역 스킬이라 프로젝트와 무관하다).

왜 이 방법인가

RNDIS 가 아니라 NCM

안드로이드 USB 테더링의 기본 가젯은 RNDIS 다. macOS 에는 그 드라이버가 없고, 폰 쪽 rndis0 가 UP 이어도 맥 networksetup -listallhardwareports 에는 인터페이스가 생기지도 않는다. HoRNDIS·EasyTether 는 kext 라 SIP 완화가 필요하다. CDC-NCM 은 macOS 네이티브 지원이라 설치할 드라이버가 없다.

setFunctions 가 아니라 tether_force_usb_functions

svc usb setFunctions ncm settings put global tether_force_usb_functions 1
지속성 비영구 — HyperOS 가 수 초 내 adb 로 되돌린다 영구
필요한 곡예 폴링·재적용, 실패 시 상태 오염 없음
대가 — 최초 1회 재부팅

tether_force_usb_functions 는 부팅 시 한 번만 읽힌다. 설정 직후에는 반영되지 않는다.

재부팅 전에 벌어지는 일

usb0 가 NCM 버킷으로 잘못 분류돼, 상태머신은 TetheredState 로 가고 dumpsys 는 LinkAddresses: [10.x.x.x/24] 를 보고하는데 커널에는 주소가 없다. 맥은 IP 를 들고 있지만(=stale lease) 통신은 되지 않는다. 겉보기 초록의 대표적인 형태다.

성공 판정

맥에 IP 가 붙은 것으로 판정하면 안 된다. 죽은 lease 가 그대로 남기 때문이다. 세 가지를 본다.

# 1. 설정 반영 — [rndis\d] 가 아니라 [usb\d] 여야 한다
adb shell dumpsys tethering | grep tetherableUsbRegexs

# 2. 테더링 상태머신 — 이것이 "토글이 켜졌나" 의 참값이다
adb shell dumpsys tethering | grep -E '^ *usb[0-9] - '     # → usb0 - TetheredState

# 3. 실제 USB 가젯 — 재부팅 전에는 설정이 1 이어도 여기가 rndis 다
adb shell getprop sys.usb.state                             # → ncm,adb

# 4. 외부 통신 (ping 아님 — ICMP 가 차단돼 항상 100% 손실로 나온다)
curl -sS -o /dev/null -w '%{http_code}\n' --interface <enN> http://captive.apple.com/hotspot-detect.html

🔴 정정 (2026-09-01) — 앞선 판정 기준 두 개가 틀렸다

adb shell ip -4 addr show usb0 로는 판정할 수 없다. root 아닌 adb shell 은 인터페이스 주소를 다 읽지 못한다. 실측에서 테더링이 멀쩡히 붙어 있는데도(usb0 - TetheredState, 맥 10.171.42.230, http 200) ip -4 addr 는 lo 와 v4-ccmni1 만 보여줬다 — wlan0 조차 안 나온다. 이 명령의 빈 출력은 "주소가 없다"가 아니라 "볼 권한이 없다"이다. 위 2번을 쓴다.

맥 인터페이스를 이름으로 찾으면 속는다. macOS 는 한 번 꽂혔던 기기의 네트워크 서비스를 이름째 남겨둔다. networksetup -listnetworkserviceorder 에 Redmi 14C 가 en21 로 잡히는데 ifconfig en21 은 죽어 있는 상태가 실제로 나왔다. 이름 · 활성(status: active) · 링크로컬(169.254)이 아닌 주소, 셋을 모두 만족해야 인정한다. 아이폰·아이패드 테더링이 같이 붙어 있으면 하드웨어 포트 이름으로 걸러낸다.

⚠️ 폰 회선이 IPv6 전용일 수 있다 (NAT64/DNS64)

붙고 나서 겪는 함정이다. 국내 통신사 회선이 IPv6 전용 + NAT64 로 오면 도메인은 되는데 IPv4 주소를 직접 쓰는 것은 전부 실패한다. store.example.com 은 200 인데 ssh 140.245.71.178 은 No route to host 로 죽고, dig @1.1.1.1 도 타임아웃이다.

판별과 우회:

curl -s -o /dev/null -w '%{remote_ip}\n' https://example.com   # 64:ff9b:: 로 시작하면 NAT64

64:ff9b::/96 은 NAT64 표준 프리픽스로, 뒤 32비트가 원래 IPv4 다 (64:ff9b::8cf5:47b2 = 0x8c.0xf5.0x47.0xb2 = 140.245.71.178). SSH config 에 IP 리터럴을 박아두면 이 회선에서 못 붙는다 — DNS64 가 변환할 이름이 없기 때문이다. HostName 을 도메인으로 바꾸거나, 급하면 위 방식으로 계산한 IPv6 주소로 직접 붙는다.

안 될 때

증상 원인 확인
토글을 눌러도 꺼진 상태로 되돌아온다 누른 것이 "이더넷을 통해 공유" 다 720x1640 기준 USB 테더링 (624,830) / 이더넷 공유 (624,1138)
맥에 IP 는 있는데 통신이 안 된다 stale lease ipconfig getpacket en15 의 server_identifier 가 현재 폰 주소와 다르면 죽은 lease. arp -an -i en15 가 (incomplete)
dumpsys 는 주소를 보고하는데 안 된다 재부팅 전 상태 ip -4 addr show usb0 로 대조
맥에 인터페이스가 아예 안 뜬다 RNDIS 로 붙어 있다 tetherableUsbRegexs 가 [rndis\d]
여러 번 갈아끼운 뒤 계속 꼬인다 USB 가젯 상태 오염 svc usb resetUsbGadget 으로는 복구되지 않는다 — 재부팅이 유일
cmd tethering 이 없다 미구현 cmd connectivity·cmd ethernet 에도 없다. UI 탭이 유일한 경로다

그 밖에 알아둘 것:

  • 테더링이 켜진 채로 NCM 전환하면 꼬인다. 반드시 끄고 전환한다.
  • adb devices 와 테더링은 별개 층이다. adb 는 USB 디버깅 인터페이스, 테더링은 네트워크 클래스다.
  • 폰에서 ip route 가 비어 보이는 건 정상이다. 안드로이드는 정책 라우팅 테이블을 쓴다.
  • 좌표는 하드코딩하지 않는다. 스크립트가 uiautomator dump 로 라벨 옆 스위치를 찾는다. 행 전체를 덮는 Switch 노드(폭 > 300px)를 걸러야 실제 위젯이 잡힌다.

재시도 금지

세 경로는 실측으로 막힌 것이 확인됐다. 다시 시도할 이유가 없다.

  1. RNDIS 로 macOS 붙이기 — 맥에 인터페이스가 생기지 않는다.
  2. "이더넷을 통해 공유" 토글 — NCM 으로 usb0 가 이더넷 포트로 등록돼도 (dumpsys ethernet 에 EthernetPort(usb0...) 로 보인다) startTethering(TETHERING_ETHERNET) 이 Tethered interface requests: 0 에서 무시된다.
  3. root 경유 수동 IP 부여 — production build 에서 adb root 가 거부된다(adbd cannot run as root).

실측 결과 (2026-08-18)

항목 값
폰 usb0 10.55.97.19/24 (커널 실주소)
맥 포트 Redmi 14C = en15 = 10.55.97.115
기본 라우트 10.55.97.19 / en15
RTT 게이트웨이 1.23ms · 8.8.8.8 72ms
generate_204 204 / 0.26s
USB 상태 sys.usb.config = ncm,adb · mUsbTetheringFunction = NCM
폰 HyperOS / Android 15 / SDK 35 / 720x1640
맥 macOS 26.5.2 / arm64 · adb 1.0.41

아이폰 USB 테더링(en11)이 함께 붙어 있었고, 아이폰을 뽑자 기본 라우트가 en15 로 자동으로 넘어갔다. 수동 조작은 필요 없었다.

파일

경로 내용
HANDOFF.txt 원인 규명·실행 순서·함정 10개·폐기 경로 전체
install.sh 새 맥에 스킬 설치 + adb 확인/설치
skill/SKILL.md Claude Code 전역 스킬 본문
skill/scripts/ncm_tether.sh 실행 스크립트 — up(기본)/status/off/--serial
EVIDENCE-2026-08-18.txt 성공 시점 원본 실측 덤프
reference/00-* 선행 세션 핸드오프(미해결 상태). 계보 보존용
reference/01-*, 02-* 메모리 노트와 인덱스 한 줄

미검증

스크립트의 stale lease 감지 → sudo ipconfig DHCP 갱신 분기는 실행된 적이 없다. 맥이 매번 올바른 대역을 자동으로 받았기 때문이다. 이 분기만 실전 검증이 남았다.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages