WOL Magic 패킷을 만들어 원격으로 컴퓨터 키기

WOL 패킷 구조 + C언어 코드

WOL?

대부분의 LAN 드라이버는 WOL(Wake On Lan) 기능을 제공하여 원격으로 전원을 키는 기능을 제공합니다.

이 기능을 사용하기 위해서는 BIOS에서 Wake On Lan 설정을 활성화 한 후 WOL Magic Paket 이라는 구조의 데이터를 전송해주면 됩니다.

이번 포스팅에서는 WOL Magic Paket의 구조와 C언어로 구현한 코드를 정리 하려고 합니다.

WOL Magic Paket?

WOL 패킷은 아래 이미지에서 보이는거 처럼 Magic 헤더 6byte + MAC 주소 16개의 단순한 구조로 되어있습니다.

Magic 헤더는 0xFFFFFF, MAC 주소는 전원을 킬 장치의 MAC주소 값을 넣으면 됩니다.

WOL-packet

위 패킷을 UDP datagram으로 해당 컴퓨터의 0, 7, 9 포트로 송신하면 정상적으로 WOL 가 동작하게 됩니다.

보통 해당 패킷을 브로트케스트 IP로 전송하여 사용합니다.

(IPTIME 공유기 기준 컴퓨터 종료 후 잠깐 동안 컴퓨터 IP로 전송해도 정상 동작, 하지만 약 10분 안에 동작하지 않음)

동작 C언어 코드

해당 패킷을 리눅스에서 C언어를 이용하여 전송하는 예제 입니다.

아래 코드에서 설정한 정보는 다음과 같습니다.

브로트케스트 IP : 192.168.100.255
포트 : 7
MAC 주소 : F2:FD:21:01:22:11
#include <stdio.h>

#include <stdlib.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <unistd.h>

#include <string.h>

#include <arpa/inet.h>

#define SERVER_PORT 7

#define MAC_ADDR_FMT "%02X:%02X:%02X:%02X:%02X:%02X"

#define MAC_ADDR_FMT_ARGS(addr) addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]

struct WOL_PACKET{
	uint8_t Magic[6];
	uint8_t MAC_ADDR[6 * 16];
};

int main(int argc, char *argv[]){
    struct sockaddr_in server_addr;
	struct WOL_PACKET wol_packet;
	int server_fd;
	int i, j;
	uint64_t MAC_ADDR = 0xF2FD21012211;
	// 해당 컴퓨터의 MAC 주소 

	int len, msg_size;
	void *udp_ptr;

    memset(&server_addr, 0x00, sizeof(server_addr));
	server_addr.sin_family = AF_INET;

    // 입력 인자 값으로 서버 설정
	if(argc <= 1){
		printf("---- 기본 서버 주소로 설정 됨 ----\n");
		server_addr.sin_addr.s_addr = inet_addr("192.168.100.255");
		server_addr.sin_port = htons(SERVER_PORT);
	}
	else if(argc == 2){
		printf("---- 서버 주소 %s 설정 ----\n", argv[1]);
		server_addr.sin_addr.s_addr = inet_addr(argv[1]);
		server_addr.sin_port = htons(SERVER_PORT);

	}
	else if(argc == 3){
		printf("---- 서버 주소 %s : %s 설정 ----\n", argv[1],argv[2]);
		server_addr.sin_addr.s_addr = inet_addr(argv[1]);
		server_addr.sin_port = htons(atoi(argv[2]));
	}	
	else{
		printf("인자 값 오류 %s \n",argv[0]);
	}

    // 소켓 파일디스크립터 
	if((server_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1){
		perror("sock");
		exit(0);
	}

	int broadcastEnable=1;
	int ret=setsockopt(server_fd, SOL_SOCKET, SO_BROADCAST, &broadcastEnable, sizeof(broadcastEnable));

	udp_ptr = &wol_packet;
	memset(udp_ptr, 0xFF, 6); // magic Packet Start bit
	udp_ptr += 6;

	for(i = 0; i < 16; i++){
		// MAC_ADDR
		memset(udp_ptr++, (MAC_ADDR & 0xFF0000000000) >> 40, 1);

		memset(udp_ptr++, (MAC_ADDR & 0xFF00000000) >> 32, 1);

		memset(udp_ptr++, (MAC_ADDR & 0xFF000000) >> 24, 1);

		memset(udp_ptr++, (MAC_ADDR & 0xFF0000) >> 16, 1);

		memset(udp_ptr++, (MAC_ADDR & 0xFF00) >> 8, 1);

		memset(udp_ptr++, MAC_ADDR & 0xFF, 1);
	}
	printf("Debug : " MAC_ADDR_FMT "\n", MAC_ADDR_FMT_ARGS(wol_packet.MAC_ADDR));

	if(sendto(server_fd, &wol_packet, sizeof(wol_packet), 0,(struct sockaddr *)&server_addr, sizeof(server_addr))){
	 	perror("send");
	 	exit(0);
	}

	close(server_fd);
    return 0;
}