[개발환경 세팅]VS Code에서 C++ 패킷 프로그래밍 설정 가이드

C++로 네트워크 패킷 프로그래밍을 시작하기 위해서, Visual Studio Code를 사용하여 개발환경을 세팅하는 방법에 대해 정리해봅니다. 각 운영체제에 맞는 컴파일러 설치 및 패킷 캡쳐 라이브러리 연동을 설명합니다. 패킷 캡처 예제 코드를 제공하여 실습을 할 수 있도록 정리해봅니다.

1. VS Code 설치

vscode는 범용성 있는 MS에서 개발한 오픈 소스 코드 편집기로, 다양한 프로그래밍 언어를 지원하며 뛰어난 확장 기능을 제공하는 편집기입니다. VS Code 공식 웹사이트에서 설치 파일을 다운로드하고 설치합니다.

2. C++ 확장 설치

3. 컴파일러 설치

C++ 코드를 컴파일하기 위해 컴파일러가 필요합니다. Windows, Mac, Linux에 따라 각기 다른 컴파일러를 설치해야 합니다.

Windows

Mac

Linux

4. Build 설정

Visual Studio를 사용하는 것과 다르게 VSCode에서는 task.json을 통해 빌드 컴파일 하는 방법을 정의하는 방식으로 작동시킬수 있습니다. 이 프로젝트에서는 Packet 트레이싱을 위한 라이브러리를 연결시켜주는 것이 필요합니다. 필자는 현재 맥(Apple Slicon)을 기준으로 설정하였습니다. 다른 운영체제에 대한 내용도 작성하였지만 정확하지 않을 수 있습니다.

Mac(Apple Slicon)

맥에는 xcode를 통해 기본으로 내장된 libpcap이라는 라이브러리가 존재합니다. 그러나 이 라이브러리는 기본적으로 x64와 호환되는 라이브러리로 ARM이 내장된 실리콘 맥에는 적용되지 않습니다. 따라서 실리콘 맥에서는 homebrew를 통한 설치 후에 라이브러리를 연동하여 실행시킬 수 있습니다.

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "build project",
      "type": "shell",
      "command": "/opt/homebrew/bin/g++-14",
      "args": [
        "main.cpp",
        "-o",
        "main",
        "-I/opt/homebrew/opt/libpcap/include",
        "-L/opt/homebrew/opt/libpcap/lib",
        "-lpcap"
      ],
      "options": {
        "cwd": "${workspaceFolder}"
      },
      "group": {
        "kind": "build",
        "isDefault": true
      }
    }
  ]
}

다음은 각각의 테스크에 대한 설명입니다.


Linux & Mac(Intel)

리눅스와 인텔맥에는 기본적으로 libpcap이 존재합니다. 따라서 따로 설치할 필요없이 설정이 가능합니다. 만약 옵션 추가를 통해 실행이 제대로 되지 않는다면 실리콘 맥과 같이 경로를 포함해주는 방식으로 설정해야 합니다.

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "build",
      "type": "shell",
      "command": "g++",
      "args": [
        "-g",
        "${file}",
        "-o",
        "${fileDirname}/${fileBasenameNoExtension}",
        "-lpcap" // libpcap 라이브러리 참조
      ],
      "group": {
        "kind": "build",
        "isDefault": true
      },
      "problemMatcher": ["$gcc"],
      "detail": "컴파일 명령 설정 (Linux)"
    }
  ]
}

Windows

윈도우의 경우에는 Visual Studio를 통해서 프로그래밍 개발을 하는 것이 권장되지만, 좀 더 가볍게 개발하기 위해서는 Visual Studio Code도 좋은 선택지라고 생각합니다. 윈도우는 기본으로 내장된 패킷 라이브러리가 존재하지 않습니다. 이에 두가지 라이브러리를 활용하는 경우가 많습니다. Npcap:


설치방법

{
  "version": "2.0.0",
  "tasks": [
    {
      "label": "build project",
      "type": "shell",
      "command": "g++", // Windows에서는 MinGW-w64 설치된 g++ 경로 사용
      "args": [
        "main.cpp",
        "-o",
        "main.exe", // Windows에서는 실행 파일 확장자로 .exe 사용
        "-I\"C:/Npcap/Include\"", // Npcap 헤더 파일 경로
        "-L\"C:/Npcap/Lib\"", // Npcap 라이브러리 파일 경로
        "-lwpcap" // Npcap 라이브러리 링크 (WinPcap API 호환)
      ],
      "options": {
        "cwd": "${workspaceFolder}"
      },
      "group": {
        "kind": "build",
        "isDefault": true
      }
    }
  ]
}

4. 예제 코드 빌드 및 테스트

빌드 설정이 완료된 후에는 간단한 프로젝트를 빌드할 수 있는 예제 코드를 실행해본다. 아래는 간단한 패킷 프로그래밍 예제입니다.

Mac & Linux

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <pcap.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

void packet_handler(u_char *user_data, const struct pcap_pkthdr *pkthdr, const u_char *packet)
{
  std::cout << "Packet capture length: " << pkthdr->caplen << std::endl;
  std::cout << "Packet total length: " << pkthdr->len << std::endl;
}

int main()
{
  char errbuf[PCAP_ERRBUF_SIZE];
  pcap_if_t *alldevs;
  pcap_if_t *d;
  pcap_t *handle;

  // 모든 네트워크 디바이스 찾기
  if (pcap_findalldevs(&alldevs, errbuf) == -1)
  {
    std::cerr << "Error finding devices: " << errbuf << std::endl;
    return 1;
  }

  // 사용 가능한 디바이스 출력
  for (d = alldevs; d != NULL; d = d->next)
  {
    std::cout << d->name;
    if (d->description)
      std::cout << " (" << d->description << ")" << std::endl;
    else
      std::cout << " (No description available)" << std::endl;
  }

  // 첫 번째 디바이스 사용
  if (alldevs == NULL)
  {
    std::cerr << "No devices found" << std::endl;
    return 1;
  }

  // 디바이스 열기
  const int snaplen = 65535;
  handle = pcap_open_live(alldevs->name, snaplen, 1, 1000, errbuf);
  if (handle == NULL)
  {
    std::cerr << "Could not open device " << alldevs->name << ": " << errbuf << std::endl;
    pcap_freealldevs(alldevs);
    return 1;
  }

  // 패킷 캡처 시작
  std::cout << "Starting packet capture on " << alldevs->name << "..." << std::endl;
  pcap_loop(handle, -1, packet_handler, NULL);

  pcap_freealldevs(alldevs);
  pcap_close(handle);
  return 0;
}

Windows(Npcap 라이브러리 활용)

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <pcap.h>
#include <winsock2.h>  // Windows 소켓 헤더

void packet_handler(u_char *user_data, const struct pcap_pkthdr *pkthdr, const u_char *packet)
{
  std::cout << "Packet capture length: " << pkthdr->caplen << std::endl;
  std::cout << "Packet total length: " << pkthdr->len << std::endl;
}

int main()
{
  char errbuf[PCAP_ERRBUF_SIZE];
  pcap_if_t *alldevs;
  pcap_if_t *d;
  pcap_t *handle;

  // 모든 네트워크 디바이스 찾기
  if (pcap_findalldevs(&alldevs, errbuf) == -1)
  {
    std::cerr << "Error finding devices: " << errbuf << std::endl;
    return 1;
  }

  // 사용 가능한 디바이스 출력
  for (d = alldevs; d != NULL; d = d->next)
  {
    std::cout << d->name;
    if (d->description)
      std::cout << " (" << d->description << ")" << std::endl;
    else
      std::cout << " (No description available)" << std::endl;
  }

  // 첫 번째 디바이스 사용
  if (alldevs == NULL)
  {
    std::cerr << "No devices found" << std::endl;
    return 1;
  }

  // 디바이스 열기
  const int snaplen = 65535;
  handle = pcap_open_live(alldevs->name, snaplen, 1, 1000, errbuf);
  if (handle == NULL)
  {
    std::cerr << "Could not open device " << alldevs->name << ": " << errbuf << std::endl;
    pcap_freealldevs(alldevs);
    return 1;
  }

  // 패킷 캡처 시작
  std::cout << "Starting packet capture on " << alldevs->name << "..." << std::endl;
  pcap_loop(handle, -1, packet_handler, NULL);

  pcap_freealldevs(alldevs);
  pcap_close(handle);
  return 0;
}