카테고리 없음

윈속 로우패킷

컴공 2013. 6. 14. 11:50
반응형
void ARP()
{
 pkt_data[14] = 0x00;   // Hardware Type
 pkt_data[15] = 0x01;
 pkt_data[16] = 0x08;   // Protocol Type
 pkt_data[17] = 0x00;
 pkt_data[18] = 0x06;   // Hardware Length
 pkt_data[19] = 0x04;   // Protocol Length        
    
 pkt_data[20] = 0x00;   // Operation
 pkt_data[21] = 0x01;
 pkt_data[22] = pkt_data[6];  // Sender Mac
 pkt_data[23] = pkt_data[7];   
 pkt_data[24] = pkt_data[8];  
 pkt_data[25] = pkt_data[9];
 pkt_data[26] = pkt_data[10];   
 pkt_data[27] = pkt_data[11];
 pkt_data[28] = 0xC8;   // Sender Ip
 pkt_data[29] = 0x01;
 pkt_data[30] = 0x04;  
 pkt_data[31] = 0x14;
 pkt_data[32] = 0x00;   // Target Mac
 pkt_data[33] = 0x00;
 pkt_data[34] = 0x00;
 pkt_data[35] = 0x00;
 pkt_data[36] = 0x00;
 pkt_data[37] = 0x00;
 pkt_data[38] = 0xC8;   // Target Ip
 pkt_data[39] = 0x01;
 pkt_data[40] = 0x04;
 pkt_data[41] = 0x0E;
 index = 42;      
}
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <netinet/ip.h>

u_int16_t get_checksum(u_int16_t* buf, int nwords)
{
    u_int32_t sum;
    for(sum=0; nwords>0; nwords--) sum += *buf++;
    sum = (sum >> 16) + (sum & 0xffff);
    sum += (sum >> 16);
    return (u_int16_t)(~sum);
}

int main(int argc, const char** argv)
{
    /*
     * create a raw socket
     */
    int sd = socket(PF_INET, SOCK_RAW, 145 /* not used */);
    if ( sd < 0 ) {
        perror("socket() error");
        return -1;
    }

    int turn_on = 1;
    int turn_off = 0;

    /*
     * I will manipulate IP header myself
     */
    if ( setsockopt(sd,IPPROTO_IP,IP_HDRINCL,&turn_on,sizeof(turn_on)) < 0 ) {
        perror("setsockopt() error");
        return -1;
    }

    struct iphdr iphdr;
    struct sockaddr_in din;

    din.sin_family = AF_INET;
    din.sin_port = 0;
    din.sin_addr.s_addr = inet_addr("129.254.198.212");

    iphdr.ihl = 5;
    iphdr.version = 4;
    iphdr.tos = 0;
    iphdr.tot_len = htons( sizeof(struct iphdr) );
    iphdr.id = htons(rand() % 65535);
    iphdr.frag_off = 0;         
    iphdr.ttl = 64;
    iphdr.protocol = 145;
    iphdr.saddr = inet_addr("129.254.173.145");
    iphdr.daddr = inet_addr("129.254.198.212");
    // iphdr.check = get_checksum((u_int16_t*)&iphdr, sizeof(struct iphdr));
    // IP_HDRINCL인 경우에는 checksum 계산 필요 없음. 하위 layer에서 시행됨.

    if ( sendto(sd, &iphdr, sizeof(iphdr), 0,
            (struct sockaddr*)&din, sizeof(din)) < 0 ) {
        perror("sendto() error");
        return -1;
    }

    return 0;
}

이 코드에서 주의하여 볼 부분은 적색의 굵은 폰트로 표시하였다. 우선 (1) 소켓을 열 때 SOCK_RAW를 사용하여 열어야 하고, 프로토콜 번호로는 현재 시스템에서 사용하고 있지 않은 프로토콜 번호를 준다. 이 번호는 /etc/protocols 파일을 살펴보면 알 수 있는데, 현재 145는 사용되고 있지 않은 번호이므로 테스트를 위해 그 번호를 사용했다. 그리고 (2) setsockopt 함수를 사용해서 IP 헤더를 직접 조작하겠다는 설정을 하고 있다. 이렇게 설정한 경우, 전송할 패킷의 IP 헤더는 직접 구성해 주어야 한다.

나머지 코드는 아주 간단하기 짝이 없는데, 한 가지 유의할 부분은 sendto 대신 send를 사용해서 보내면 패킷이 전송되지 않는다는 것. "Destination address required"와 같은 오류 메시지가 출력될 것이다. IP 헤더에 목적지 주소를 적어주었음에도 굳이 sockaddr_in 구조체를 인자로 전달해 주어야 한다는 것은 분명 비효율적이고 이해되지 않는 부분이지만, 리눅스 시스템 콜이 그렇게 생겨 먹었으니 어쩔 수 없는 부분이다. 

받는 쪽 소스 코드는 이보다 더 간단한데 (IP 헤더 말고 다른 데이터를 전송하지 않도록 했기 때문에 더더욱 그러하다) 소켓 bind를 할 필요도 없고, listen 할 필요도 없고, accept 할 필요도 없기 때문이다. 그냥 recv를 하면 된다. 

#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <strings.h>

int main(int argc, const char** argv)
{
    /*
     * create a raw socket
     */
    int sd = socket(PF_INET, SOCK_RAW, 145);
    if ( sd < 0 ) {
        perror("socket() error");
        return -1;
    }

    char recvPacket[1024] = {0, };
    int pkt_size = 0;
    if ( (pkt_size = recv(sd, recvPacket, 1024, 0)) < 0 ) {
        perror("recvfrom() failed");
        return -1;
    }

    printf("read succeeded");

    printf("pkt_size = %d\n", pkt_size);
    struct iphdr* iphdr = (struct iphdr*)&recvPacket[0];
    printf("iphdr.ihl = %d\n", iphdr->ihl);
    printf("iphdr.version = %d\n", iphdr->version);
    printf("iphdr.tos = %d\n", iphdr->tos);
    printf("iphdr.tot_len = %d\n", htons(iphdr->tot_len));
    printf("iphdr.frag_off = %d\n", iphdr->frag_off);
    printf("iphdr.ttl = %d\n", iphdr->ttl);
    printf("iphdr.protocol = %d\n", iphdr->protocol);

    return 0;
}

패킷을 보낼 때 굳이 sendto를 통해서 보내도록 하고 있음에도, recvfrom을 하면 뭔가 알 수 없는 오류가 생긴다는 것은 이상한 일이다. "Invalid argument"와 같은 오류가 발생한다는 것. recvfrom 대신 recv를 하면 정상적으로 동작한다. 
반응형