// 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(ðhdr, 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, ðhdr, 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;
}