프로그램이 CoAP 프로토콜을 정말 지원하는지를 패킷을 통해 직접 확인할 수 있는 방법을 공유드립니다.

presentation_title

CoAP(The Constrained Application Protocol)

최근에 기기간 통신에 대해 리서치를 하던 중 CoAP 프로토콜에 대해 알게 되었습니다. CoAP는 성능이 매우 작은 기기들 사이에 통신을 주고 받을 때, 아주 적은 페이로드로 빠르고 정확하게 통신을 하기 위한 프로토콜입니다. (자세한 스펙은 해당 링크를 참조해주세요 :slightly_smiling_face:)

해당 프로토콜이 어떻게 동작하는지는 스펙 문서를 통해 확인할 수 있었는데요, 실제로 동작을 시켜보면서 살펴보려면 해당 프로토콜의 구현체가 필요하기 마련입니다. 마침 제가 좋아하는 Golang으로 구현된 라이브러리 go-coap가 있어 직접 실행시켜보며 확인해볼 수 있었습니다.

그런데 문득 그런 생각이 들었습니다. “잘 동작하는 것 같긴한데 정말 CoAP 프로토콜을 제대로 구현했을까? 패킷을 통해 확인해보면 확실하게 알 수 있지 않을까?”

궁금증이 생겼으니 이 궁금증을 해결해야겠죠? 제가 궁금증을 해결할 때 사용했던 방법론과 그 과정을 공유드립니다. (가끔 이런 걸 해줘야 개발할 때 지루하지 않게 할 수 있는 것 같습니다. 크크…)

Let’s See CoAP Packets

조사하는 방법은 꽤나 심플합니다. 로컬에서 CoAP서버와 클라이언트를 각각 실행시키고, 이 과정에서 발생하는 패킷을 캡처하면 끝입니다.

  1. 루프백 인터페이스에 대해 패킷캡처를 시작합니다. sudo tcpdump -vi lo0 -w coap_cap.pcap
  2. go-coap 서버(Port: 5688)를 실행시켜줍니다.
  3. go-coap 클라이언트(Port: 65136)로 서버에 요청을 보내고 서버로부터 응답을 받습니다.
  4. Ctrl + c 로 실행 중이었던 tcpdump를 인터럽트 해줍니다.
  5. 추출된 coap_cap.pcap 파일을 Wireshark로 열어 분석합니다.

Use Wireshark to see packets

coap_packets

파랗게 칠한 부분이 go-coap서버와 클라이언트가 통신을 주고받은 패킷들입니다. 색칠되어 있는 4개의 패킷의 내용을 요약하자면 아래와 같습니다.

No. 3: Client(Port: 65136) sends GET request to Server(Port: 5688)

No. 4: Server sends response to Client

No. 5: Client echo MessageID from Server

No. 6: Server echo MessageID from Client

그럼 지금부터 3 ~ 6에 해당되는 패킷들을 하나 하나 살펴보도록 하겠습니다.

Added at 2020 July 6

CoAP프로토콜의 표준 포트 5683을 사용하면 와이어샤크에서 기본으로 제공해주는 필터를 사용해서 아주 편리하게 분석하실 수 있습니다. 표준 포트가 5688이라는 걸 모른 채 삽질을 했던 거였네요.

그럼에도 불구하고 패킷을 하나하나 대조하며 분석하는 건 1번 쯤은 할만한 의미있는 시간이었다고 생각합니다. 하하… :sweat_smile:

3: Client sends GET request to Server

msg_format

프로토콜 스펙에 정의된 대로 구현이 잘 되었다면 위와 같은 메시지 포맷으로 패킷이 구성되어 있어야 합니다. 과연 go-coap는 이를 잘 지키고 있는지 확인해볼까요? 위 그림에 색칠된 부분은 아래 패킷과 매칭되는 구간들을 색칠한 것이니 눈여겨 봐주세요!

client_packet

가장 먼저 좌측에 초록색 박스안의 8을 보겠습니다. TKL(=ToKen Length)을 뜻하는 것으로 우측에 등장하는 토큰의 길이가 8바이트임을 뜻합니다. 실제로 우측에 나타난 초록색 토큰 구간을 보시면 정확하게 8바이트라는 것을 확인하실 수 있습니다.

초록색으로 색칠된 8옆에 4도 프로토콜에 정의된 것처럼 의미를 가지고 있습니다. 16진수 4를 이진수로 표현하면 0100 인데요, 위에 보여드렸던 메시지 스펙에 따르면 2비트씩 각각 버전과(Ver) 타입(T)으로 해석을 할 수 있습니다. 즉 앞에 2비트 01 은 버전을, 뒤에 2비트 00 은 타입을 뜻한다고 볼 수 있습니다.

2020년 6월 28일 기준으로 CoAP 문서를 확인해보시면, 이 프로토콜을 구현할 때는 버전을 1로 명시해야 한다고 되어있고 go-coap 이를 잘 준수하고 있군요.

그 다음으로 우측의 빨간 박스에는 Code 가 들어있는데요, 클라이언트가 어떤 리퀘스트를 날렸는지 그 유형을 확인해볼 수 있습니다. 프로토콜에 정의된 대로 16진수 01이 의미하는 건 GET 요청이므로 클라이언트가 GET 요청을 했다는 걸 확인할 수 있습니다.

그 우측에 있는 파란색 박스는 메시지의 고유 아이디를 뜻하는데요, 예상 가능하듯 UDP로 정신없이 오고 가는 메시지들이 중복되지 않게 구분하기 위해 존재합니다.

4: Server sends response to Client

3번 내용을 잘 따라오셨다면 이 파트는 편하고 빠르게 보실 수 있을 겁니다. 이번에는 그림을 먼저 한꺼번에 보여드리겠습니다.

msg_format

 server_packet

메시지 포맷은 동일하므로 3번에서의 내용과 유사합니다. 달라진 점이 있다면 페이로드 노란색 박스가 감싸고 있는 Payload 부분인데요, 서버가 클라이언트에게 “hello world” 로 응답하는 것을 확인해볼 수 있습니다.

5~6: Client / Server echo received MessageID

echo_messageid

프로토콜에 따르면 서버와 클라이언트는 각각 수신한 메시지 ID를 에코해야합니다. 위 사진을 보시면 각각 MessageID를 잘 에코해주고 있음을 확인해볼 수 있습니다.

Summary

프로토콜을 잘 따르고 있는지를 직접 확인해보는 건 지루할 수 있지만 꽤 중요한 작업이라고 생각합니다. 프로그램이 제대로 동작하는지 디버깅을 하는 것처럼, 네트워크에 오고 가는 패킷들도 직접 살펴보면서 내가 예상한 대로 통신을 주고 받고 있는지, 프로토콜을 잘 준수하고 있는지 확인하는 과정도 필요한 것 같습니다.