문제를 해결하기위해 깃갤에 질문들을 올렸고, 답변을 통해서 많은 단서를 얻었음에 해결과정 및 소감을 쓴다...
[예전에 했던 질문들]
[해결 과정 및 소감]
지난 3월부터 끌어온 이슈를 5월 중순이 되어 해결했다.
원했던 기능은 NAT(공유기) 뒤에 호스트에서 실행되는 서버에 접속하는 것이다. 호스트는 우리가 openwrt로 펌웨어를 다시 올린 공유기이며, openwrt에서 동작하는 웹서버인 luci에 접속하는 것이 목적이었다.
이를 달성하는 가장 간단한 방법으로는 포트 포워딩이 있다. 공유기에서 제공하는 기능인 포트포워딩은 외부에서 접속하는 포트와 내부 포트를 연결해주는 기능이다. 그러나, 중첩된 NAT(공유기에 공유기를, 다시 또 공유기를 물리는 중첩된 상태)에서 포트 포워딩을 해줘야 하는 경우를 고려해야 했다. 또한, 설치하는 사람이 이러한 설정 방법을 알고 적용해야 하는 것도 문제였다.
따라서, 포트포워딩이 아닌 다른 방법이 필요했다. 다른 방법으로는 홀펀칭이 있다. 홀 펀칭은 NAT뒤에 있는 서버가 외부와 연결할 때, 잠시 생기는 포트를 내가 빌려서 마치 내 것처럼 사용하는 기술이다. UDP홀 펀칭은 HTTP를 사용하므로 탈락했다. TCP홀 펀칭은 구현 난이도가 높고, 공유기 차원에서 지원해주지 않는 경우가 너무 많아 탈락했다. 홀 펀칭 자체가 항상 실패의 가능성이 높아 중개서버로 보완이 필요했다.
그래서, 직접적으로 두 호스트 간에 연결하는 방법이 아닌, 중개 서버 방식을 고려했다. 중개 서버 방식은 호스트의 서버와 중개 서버를 연결하고, 클라이언트는 중개 서버를 바라보는 방식이다. 터널링이라고 부르기도 한다. 이러한 서비스를 제공해주는 상용 솔루션으로는 anydesk, ngrok 등이 있다. anydesk의 경우 타겟으로 잡은 NAT 장비의 칩셋과 호환되지 않았다. 남은 것은 ngrok이었고 다행이 우리의 칩셋과 호환되는 바이너리를 제공해주었다.
그러나, 최신 버전의 openwrt에서 동작하지 않았고, 수년 전의 구버전에서 동작하였다. 구버전을 쓰려고 하면 못쓸 것은 없었지만, 신버전의 기능들이 매우 편리했으므로 신버전에서 동작시킬 방법을 구상하였다. 발생한 에러는 illegal instruction으로 주로 cpu가 제공해주는 명령어 셋과 맞지 않을 경우 나오는 문제였다. 그러나 이것만으로 확신할 수 없어서 GDB를 이용하여 다시 분석을 하였다.
문제가 발생한 지점은 go 런타임을 못찾는 것이었다. ngrok은 go로 빌드된 서버와 클라이언트로, 실행 시에 go 런타임을 요구한 것이다. 요구하는 go 런타임을 복사하여 넣어줬으나, go의 소스 코드 중에 변수의 타입을 float32로 지정하는 부분에서 다시 오류가 생겼다. 타입 관련 문제이므로 명령어 셋이 맞지 않는 것인가... 하고 추측하였으나 이쯤에서 지식 부족과 검색의 실패로 더 이상 해결할 수 없었다.
시선을 돌려, ngrok이 실행되는 구버전의 openwrt를 분석하였다. 구버전의 openwrt에서도 go 런타임은 없었으므로, go 런타임 관련 문제보다 다른 문제라고 생각하였다. 계속된 검색 및 분석을 하는 도중에 상용 ngrok의 오픈소스 시절의 github를 발견할 수 있었다.
수년 전에 업데이트가 끝났지만, 더이상 앞으로 나아갈 수 없었던 상태이므로 해당 소스를 받아 빌드를 하였다. 빌드 도중에 역시 환경 설정, 경로 설정 등 문제가 발생하였으나, 해결하고 빌드를 성공할 수 있었다. 그런데, ngrok서버를 실행하려면 도메인이 필요했다. 내부에서 openssl을 이용하여 https까지 지원하였기 때문이다. AWS에서 개인적으로 구입하고(회사에 말해서 구입해서 내가 받기에는 며칠 이상이 걸렸을 것이라...) 서버 역시 따로 AWS에 올려서 실행하였다. 서버는 동작을 잘하였으나, 클라이언트는 동작을 하지 않았다.
클라이언트가 동작하지 않는 이유는 상용 ngrok 클라리언트와 동일했다. go 런타임을 요구했고, 특정 변수를 float32로 지정하는 부분에서 문제가 발생하였다. go가 아닌 다른 언어로 빌드할 수 있는 ngrok이 있는지 찾기 시작했다. 다행히 c버전의 ngrok 클라이언트가 있었다.
c버전의 ngrok 클라이언트는 다행이 최근까지 업데이트를 하고 있는 프로젝트였다. 해당 소스를 받아 빌드를 시도하였다. 역시 한 번에 되는 경우가 없다... 경로 설정 및 툴체인의 경로를 모두 수정하고, include 하는 라인을 수정해주어 빌드에 성공했다. 그리고 최신 버전의 openwrt에서 성공하는 것을 확인하였다. 혹시 몰라 장비의 펌웨어를 다시 초기화하고 ngrok-c 클라이언트를 실행했고 역시 성공하는 것을 알 수 있었다.
3월 부터 괴롭히던 이슈를 마침내 해결했다. IOT 개발에 참여하면서 가장 어려웠던 경험이었으나, 해결할 수 있었음에 감사한다...
깃갤에는 열정 많은 유입이 계속 들어오네
대체제로 찾은거 뭔지 알려 줄 수 있음? ssh tunnel 됐음 좋겠는데...
https://github.com/dosgo/ngrok-c
요거임?
어 그거 맞음
어셈관련 지식이없는데도 인스트럭션에러떴을때 지디비로분석할 도전을 한게멋지다..
AWS에서 VM을 산 시점에서 다 때려치우고 openwrt 내장 VPN으로 붙었어야지
중첩된 공유기... 그러니까 NAT - NAT - NAT(내꺼) 에서 NAT(내꺼)만 VPN 설정해도 적용되나? 안될거 같아서 걸렀는데
NAT(내꺼)만 설정해야 하는 이유는, 우리가 앞에것의 설정까지 건들일 수 없어서