소켓프로그래밍

linux arp 요청 & 응답 받기

blackbearwow 2024. 3. 6. 00:22
// arp2.cpp
// g++ -o arp2.out arp2.cpp; sudo ./arp2.out
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/if_ether.h>
#include <arpa/inet.h>
#include <linux/if_packet.h>

#define ETHER_TYPE_ARP 0x0806
#define ARP_REQUEST 1
#define ARP_REPLY 2

struct arp_packet {
    uint16_t htype;
    uint16_t ptype;
    uint8_t hlen;
    uint8_t plen;
    uint16_t opcode;
    uint8_t sender_mac[6];
    uint8_t sender_ip[4];
    uint8_t target_mac[6];
    uint8_t target_ip[4];
};

int main() {
    int sockfd;
    struct ifreq if_idx;            //socket ioctl에 사용되는 인터페이스 요청 구조체
    struct sockaddr_ll dest_addr;   //sendto에 사용될 목적지 주소 구조체
    struct ether_header ethhdr;     //ethernet 헤더
    struct arp_packet arp_pkt;      //arp 헤더
    char buffer[1024];

    // Create raw socket
    // ARP프로토콜에 해당하는 소켓을 만든다. htons(ETH_P_ALL)은 모든 프로토콜을 받는다.
    if ((sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP))) == -1) {
        perror("socket");
        exit(1);
    }

    // Get interface index
    // linux는 ifconfig windows는 ipconfig로 interface name을 알아내 해당 이름으로 interface index를 알아낸다.
    // socket(AF_PACKET, SOCK_RAW, protocol)이기 때문에 필요한 과정이다. socket(AF_INET, SOCK_RAW, protocol)에서는 안해도 된다.
    strncpy(if_idx.ifr_name, "eno1", IFNAMSIZ);
    if (ioctl(sockfd, SIOCGIFINDEX, &if_idx) < 0) {
        perror("ioctl");
        close(sockfd);
        exit(1);
    }

    // Prepare ARP request
    memset(&ethhdr, 0, sizeof(struct ether_header));
    memset(&arp_pkt, 0, sizeof(struct arp_packet));
    memset(&dest_addr, 0, sizeof(struct sockaddr_ll));
    // "\x11\x22\x33\x44\x55\x66"
    ethhdr.ether_type = htons(ETHER_TYPE_ARP);
    memcpy(ethhdr.ether_shost, "\x11\x22\x33\x44\x55\x66", ETH_ALEN); // Source MAC address
    memset(ethhdr.ether_dhost, 0xff, ETH_ALEN); // Broadcast MAC address

    arp_pkt.htype = htons(ARPHRD_ETHER);
    arp_pkt.ptype = htons(ETH_P_IP);
    arp_pkt.hlen = ETH_ALEN;
    arp_pkt.plen = sizeof(in_addr_t);
    arp_pkt.opcode = htons(ARP_REQUEST);
    memcpy(arp_pkt.sender_mac, "\x11\x22\x33\x44\x55\x66", ETH_ALEN); // Source MAC address
    inet_pton(AF_INET, "192.168.219.104", arp_pkt.sender_ip); // Source IP address
    memset(arp_pkt.target_mac, 0, ETH_ALEN); // Broadcast MAC address
    inet_pton(AF_INET, "192.168.219.1", arp_pkt.target_ip); // Target IP address

    // Prepare ARP packet
    memcpy(buffer, &ethhdr, sizeof(struct ether_header));
    memcpy(buffer + sizeof(struct ether_header), &arp_pkt, sizeof(struct arp_packet));

    // Set destination address
    dest_addr.sll_family = AF_PACKET;
    dest_addr.sll_protocol = htons(ETH_P_ARP);
    dest_addr.sll_ifindex = if_idx.ifr_ifindex;
    dest_addr.sll_halen = ETH_ALEN;
    memcpy(dest_addr.sll_addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN); // Broadcast MAC address

    // Send ARP request
    if (sendto(sockfd, buffer, sizeof(struct ether_header) + sizeof(struct arp_packet), 0, (struct sockaddr*)&dest_addr, sizeof(struct sockaddr_ll)) < 0) {
        perror("sendto");
        close(sockfd);
        exit(1);
    }

    // Receive ARP reply
    while (true) {
        int bytes = recvfrom(sockfd, buffer, sizeof(buffer), 0, NULL, NULL);
        if (bytes < 0) {
            perror("recvfrom");
            close(sockfd);
            exit(1);
        }

        struct ether_header* recv_ethhdr = (struct ether_header*) buffer;
        struct arp_packet* recv_arp_pkt = (struct arp_packet*) (buffer + sizeof(struct ether_header));

        if (ntohs(recv_ethhdr->ether_type) == ETHER_TYPE_ARP && ntohs(recv_arp_pkt->opcode) == ARP_REPLY) {
            std::cout << "ARP Reply Received" << std::endl;
            for (int i=0; i<(sizeof(ether_header)+sizeof(arp_packet)); i++)
                printf("%02hhx ", buffer[i]);
            puts("");
            break;
        }
    }

    close(sockfd);
    return 0;
}