

Host A에서 B로 Packet을 보낸다고 가정할 때 어떤 과정을 통해 Packet이 전달될까요?
위 사진과 같이 Host A가 보낸 Packet은 Router를 통해 우리가 원하는 목적지까지 Packet을 전달할 수 있게 됩니다.
Router는 어떤 경로로 Packet을 보낼지에 대한 (1)Routing과 실질적으로 packet을 보내는 (2)Forwarding을 수행합니다. Router에 대한 더 자세한 내용은 나중에 SDN 내용을 정리하면서 다루도록 하겠습니다.
Router는 Router별로 가지고 있는 Routing table에 따라 정해진 경로로 Packet을 전송합니다.
먼저 User space의 Data가 Socket을 통해 Kernel Space로 내려가서 각 Layer에서 Port Number, IP, MAC header가 붙여져 Packer이 구성되어 NIC(Network Interface Card)를 통해 Packet이 나가게 됩니다.
MAC Header 구성
MAC Header는 14byte의 크기를 가지고 있고, src(Sender), dst(Destination), Ether type으로 구성됩니다.
Packet을 받는 상황을 가정했을 때, NIC에서 MAC Header를 먼저 까보고 Packet을 (1) 해당 Network로 올리거나, (2) Drop하게 되는데, 이 과정의 편의성을 위해
Dst + Src + Ether type 순으로 MAC Header를 구성합니다.
- MAC Sender Address: 6byte
- MAC Reciever Address: 6byte
- Ether type: 2byte
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/ip.h>
void main()
{
struct sockaddr_in dest_info;
char *data = "Hello Server.\n";
// 1) 네트워크 소켓 생성
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
// 2) 목적지 정보 초기화 및 설정
memset((char *)&dest_info, 0, sizeof(dest_info));
dest_info.sin_family = AF_INET;
dest_info.sin_addr.s_addr = inet_addr("10.0.2.5");
dest_info.sin_port = htons(9090);
// 3) UDP 패킷 전송
sendto(
sock,
data,
strlen(data),
0,
(struct sockaddr *)&dest_info,
sizeof(dest_info)
);
// 4) 소켓 닫기
close(sock);
}
C언어로 작성된 간단한 Packet 전송 예시입니다.
이 코드의 동작은 아래와 같습니다.
1. 소켓 생성: socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)로 UDP용 네트워크 소켓을 만든다.
2. 목적지 설정: sockaddr_in 구조체에 IPv4, IP 주소(10.0.2.5), 포트(9090)를 지정하고 초기화한다.
3. 데이터 전송: Dummy 문자열을 sendto() 호출을 통해 위에서 설정한 목적지로 한 번 보낸다.
4. 소켓 종료: close()로 사용이 끝난 소켓을 닫고 프로그램을 종료한다.
즉, 지정된 서버에 한 줄 메시지를 UDP 방식으로 전송하고 바로 연결을 해제하는 역할을 합니다.
📌 Packet을 구성할 때 왜 src port를 설정하지 않았을까?
src port는 직접 지정하지 않아도 OS에서 random하게 지정하게 됩니다.
src port가 없다면 packet을 다시 받지 못하기 때문입니다.
Packet을 받을 때 Packet은 Binary 형태로 NIC을 통해 들어옵니다. MAC Layer에서 MAC Header의 dst 정보를 확인하고, Network Layer에서 IP Header를 확인하여 Transport Layer로 보내거나, Routing을 통해 다른 Network로 보낸다.
각 Layer에서 Header 확인 후 마지막으로 Data가 Application으로 보내지게 된다.
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/ip.h>
void main()
{
struct sockaddr_in server;
struct sockaddr_in client;
int clientlen;
char buf[1500];
// 1) 소켓 생성 (UDP)
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
// 2) 서버 주소 정보 초기화 및 설정
memset((char *)&server, 0, sizeof(server));
server.sin_family = AF_INET; // IPv4
server.sin_addr.s_addr = htonl(INADDR_ANY); // 모든 인터페이스에서 수신
server.sin_port = htons(9090); // 포트 9090
// 3) 소켓에 주소 할당(bind)
if (bind(sock, (struct sockaddr *)&server, sizeof(server)) < 0)
error("ERROR on binding");
// 4) 무한 루프: 클라이언트로부터 오는 UDP 메시지 수신 → 화면 출력
while (1) {
bzero(buf, 1500);
recvfrom(
sock,
buf,
1500 - 1,
0,
(struct sockaddr *)&client,
&clientlen
);
printf("%s\n", buf);
}
// 5) 소켓 닫기
close(sock);
}
위 코드는 Packet을 받기 위해 C로 작성된 code입니다.
socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) 호출로 UDP용 소켓을 엽니다.server.sin_addr.s_addr = htonl(INADDR_ANY)로 모든 네트워크 인터페이스에서 오는 패킷을 받도록 설정합니다.server.sin_port = htons(9090)로 포트 9090을 지정bind()로 소켓에 이 주소를 할당합니다.recvfrom()를 이용해 최대 1499바이트까지 들어오는 UDP 패킷을 블록 모드로 기다립니다.close(sock)로 소켓을 닫습니다.즉, 9090 port에서 접근하는 UDP 클라이언트의 메시지를 받아서 표준 출력(터미널)에 찍어 주는 단순한 “UDP 리스닝 서버” 역할을 합니다.
📌 주의사항
- 특정 port, ip에서 들어오는 패킷만 receive 해야하기 때문에 bind 과정이 필요합니다.
- socket을 열었으면 항상 닫아야 합니다. 다른 상황에서 같은 port number를 사용할 때 Error 발생 == Bind 안됨.
정리하다보니 내용이 너무 길어져서 끊고 다음 포스트에 이어서 Sniffing과 Spoofing에 대해 정리하겠습니다.
아니 그럼 Sniffing과 Spoofing에 대한 글이 아니라, 패킷의 구조와 패킷을 주고받는 과정의 글이잖아요. ㅋㅋㅋㅋㅋㅋㅋㅋㅋ