arp_scanner.c:/* Copyright (c) 2009-2025 TP-LINK Technologies CO.,LTD.
*
* file None
* brief None
* details None
*
* author Wei Gan <ganwei@tp-link.com.hk>
* version 1.0.0
* date 31Jul25
*
* history \arg version, date, author, modification
*/
/**************************************************************************************************/
/* INCLUDE */
/**************************************************************************************************/
#include <sys/socket.h>
#include <linux/if_packet.h>
#include <net/ethernet.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "arp_scanner.h"
/**************************************************************************************************/
/* DEFINES */
/**************************************************************************************************/
/**************************************************************************************************/
/* TYPES */
/**************************************************************************************************/
/**************************************************************************************************/
/* VARIABLES */
/**************************************************************************************************/
/**************************************************************************************************/
/* FUNCTIONS */
/**************************************************************************************************/
/* 创建原始socket */
int create_arp_socket()
{
int sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
if (sock < 0) {
perror("socket");
return -1;
}
return sock;
}
/*
* fn static int send_arp_request(int sockfd, const char *src_ip, const char *dest_ip, const char *ifname)
* brief 发送ARP请求
* details None
*
* param[in] sockfd 套接字
* param[in] src_ip 源ip
* param[in] ifname 网卡名
* param[out]None
* return None
*
* retval None
* note None
*/
static int send_arp_request(int sockfd, const char *src_ip, const char *dest_ip, const char *ifname)
{
// 获取接口索引
struct ifreq ifr;
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
if (ioctl(sockfd, SIOCGIFINDEX, &ifr) < 0) {
perror("ioctl(SIOCGIFINDEX)");
return -1;
}
// 获取接口MAC地址
if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) < 0) {
perror("ioctl(SIOCGIFHWADDR)");
return -1;
}
uint8_t src_mac[6];
memcpy(src_mac, ifr.ifr_hwaddr.sa_data, 6);
// 构建以太网帧和ARP包
unsigned char packet[60] = {0};
struct ether_header *eth = (struct ether_header *)packet;
struct ether_arp *arp = (struct ether_arp *)(packet + sizeof(struct ether_header));
/************** 填充以太网帧头 **************/
// 目标MAC: 广播地址 (FF:FF:FF:FF:FF:FF)
memset(eth->ether_dhost, 0xFF, 6);
// 源MAC: 接口MAC地址
memcpy(eth->ether_shost, src_mac, 6);
// 以太网类型: ARP (0x0806)
eth->ether_type = htons(ETHERTYPE_ARP);
/************** 填充ARP包 **************/
// 硬件类型: 以太网 (1)
arp->arp_hrd = htons(ARPHRD_ETHER);
// 协议类型: IPv4 (0x0800)
arp->arp_pro = htons(ETHERTYPE_IP);
// 硬件地址长度: 6字节 (MAC)
arp->arp_hln = 6;
// 协议地址长度: 4字节 (IPv4)
arp->arp_pln = 4;
// 操作码: ARP请求 (1)
arp->arp_op = htons(ARPOP_REQUEST);
// 源MAC: 接口MAC地址
memcpy(arp->arp_sha, src_mac, 6);
// 源IP: 将字符串转换为网络字节序
struct in_addr src_addr;
if (inet_pton(AF_INET, src_ip, &src_addr) <= 0) {
perror("inet_pton(src_ip)");
return -1;
}
memcpy(arp->arp_spa, &src_addr.s_addr, 4);
// 目标MAC: ARP请求中设为全0 (未知)
memset(arp->arp_tha, 0, 6);
// 目标IP: 将字符串转换为网络字节序
struct in_addr dest_addr;
if (inet_pton(AF_INET, dest_ip, &dest_addr) <= 0) {
perror("inet_pton(dest_ip)");
return -1;
}
memcpy(arp->arp_tpa, &dest_addr.s_addr, 4);
/************** 发送数据包 **************/
struct sockaddr_ll sa = {
.sll_family = AF_PACKET,
.sll_ifindex = ifr.ifr_ifindex,
.sll_halen = ETH_ALEN,
.sll_addr = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} // 广播
};
if (sendto(sockfd, packet, sizeof(packet), 0,
(struct sockaddr*)&sa, sizeof(sa)) < 0) {
perror("sendto");
return -1;
}
return 0;
}
/* 处理ARP响应 */
static void process_arp_response(arp_scanner_t *scanner, unsigned char *buffer, size_t len)
{
struct ether_arp *arp = (struct ether_arp *)(buffer + sizeof(struct ether_header));
// 提取IP和MAC
char ip_str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, arp->arp_spa, ip_str, sizeof(ip_str));
pthread_mutex_lock(&scanner->lock);
// 检查是否是新主机
int is_new = 1;
arp_entry_t *entry = scanner->entries;
while (entry) {
if (strcmp(entry->ip, ip_str) == 0) {
memcpy(entry->mac, arp->arp_sha, 6);
entry->last_seen = time(NULL);
is_new = 0;
break;
}
entry = entry->next;
}
// 发现新主机
if (is_new) {
arp_entry_t *new_entry = malloc(sizeof(arp_entry_t));
strcpy(new_entry->ip, ip_str);
memcpy(new_entry->mac, arp->arp_sha, 6);
new_entry->last_seen = time(NULL);
new_entry->next = scanner->entries;
scanner->entries = new_entry;
// 打印到串口
printf("[NEW HOST] IP: %s, MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
ip_str,
arp->arp_sha[0], arp->arp_sha[1], arp->arp_sha[2],
arp->arp_sha[3], arp->arp_sha[4], arp->arp_sha[5]);
}
pthread_mutex_unlock(&scanner->lock);
}
/* 清理过期条目 */
static void clean_expired_entries(arp_scanner_t *scanner)
{
time_t now = time(NULL);
pthread_mutex_lock(&scanner->lock);
arp_entry_t **pp = &scanner->entries; // **pp 保存的是链表头节点地址
while (*pp) {
arp_entry_t *entry = *pp;
if (now - entry->last_seen > scanner->config.valid_time) {
*pp = entry->next;
free(entry);
} else {
pp = &entry->next;
}
}
pthread_mutex_unlock(&scanner->lock);
}
/* 接收ARP响应 */
void *arp_scan_thread(void *arg)
{
arp_scanner_t *scanner = (arp_scanner_t *)arg;
scanner->scanning = SCANNING_ON;
while (scanner->scanning) {
// 清理过期条目
clean_expired_entries(scanner);
// 遍历IP范围
for (uint32_t ip = inet_addr(scanner->config.start_ip);
ip <= inet_addr(scanner->config.end_ip);
ip = ntohl(ntohl(ip) + 1))
{
if (!scanner->scanning) break; // scanning为0,停止扫描
// 发送ARP请求
char dest_ip[16];
inet_ntop(AF_INET, &ip, dest_ip, sizeof(dest_ip));
send_arp_request(scanner->sockfd, SOURCE_IP, dest_ip, IFNAME);
// 接收响应
unsigned char buffer[ETH_FRAME_LEN];
struct sockaddr_ll saddr;
socklen_t saddr_len = sizeof(saddr);
fd_set fds;
// 设置发包间隔
struct timeval tv = {
.tv_usec = scanner->config.send_interval * 1000
};
FD_ZERO(&fds);
FD_SET(scanner->sockfd, &fds);
if (select(scanner->sockfd + 1, &fds, NULL, NULL, &tv) > 0)
{
ssize_t len = recvfrom(scanner->sockfd, buffer, sizeof(buffer), 0,
(struct sockaddr*)&saddr, &saddr_len);
if (len > 0)
{
process_arp_response(scanner, buffer, len);
}
}
}
// 等待扫描间隔
int sleep_count = scanner->config.scan_interval;
while (sleep_count > 0 && scanner->scanning) {
sleep(1);
sleep_count--;
}
}
close(scanner->sockfd);
scanner->sockfd = -1;
scanner->scanning = SCANNING_OFF;
return NULL;
}
最新发布