linux socket 發送ARP數據包

火星人 @ 2014-03-26 , reply:0
←手機掃碼閱讀


Linux 中獲得本機網卡MAC地址很簡單,ioctl()就OK。可如何獲得區域網其他主機的MAC地址呢…鏈路層廣播ARP請求,然後接收ARP響應。實現介個功能其實已有現成三方庫可用,如libnet。不過想加深對協議的理解和Linux網路編程API的理解,jiayi 還是決定用Linux的PF_PACKET自己實現,改啊改,終於成功~以下是大體思路
1. 為數據報分配空間,創建相應的結構體 req,arp 數據報

2. 創建 PF_PACKET 原始套接字,發送套接字為 reqfd,接收套接字為 recvfd
3. 填寫鏈路層通用結構體 reqsa
3. get_ifi()獲取本機網路介面數據,填寫要發送的ARP數據報 req 結構體,sendto()發送
4. 循環recvfrom()接收ARP響應,濾掉經由本地介面的其他ARP數據報
發送ARP請求能做的事不僅僅獲取MAC地址吧…其他「有意義」的事也可以嘗試一下下……下面是代碼
C code

/**
* @send_arp.c
* @This software is intended to be used as a example to show how to send and receive arp request with Linux * PF_PACKET interface
* @Author:jiayi,http://www.jiayii.com
**/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include /* 需要裡面的 glibc 版本號 */
#if __GLIBC__ >= 2 && __GLIBC_MINOR >= 1
#include
#include /* 鏈路層(L2)協議 */
#else
#include
#include
#include /* 鏈路層協議 */
#endif
#include
#define INLEN 4
#define MAC_BCAST_ADDR (uint8_t *) "\xff\xff\xff\xff\xff\xff"
void usage_quit(char *arg0);
int get_ifi(char *dev, char *mac, int macln, struct in_addr *lc_addr, int ipln);
void prmac(u_char *ptr);
int main(int argc, char **argv)
{
if(argc != 2)
usage_quit(argv[0]);
int reqfd, recvfd, salen, n;
u_char *mac;
char recv_buf[120], rep_addr[16];
struct in_addr lc_addr, req_addr;
struct sockaddr_ll reqsa, repsa;
struct arp_pkt {
struct ether_header eh;
struct ether_arp ea;
u_char padding[18];
} req;

bzero(&reqsa, sizeof(reqsa));
reqsa.sll_family = PF_PACKET;
reqsa.sll_ifindex = if_nametoindex("eth0");
if((reqfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_RARP))) < 0) {
perror("Socket error");
exit(1);
}
mac = (char *)malloc(ETH_ALEN);
bzero(&req, sizeof(req));
if(get_ifi("eth0", mac, ETH_ALEN, &lc_addr, INLEN)) {
fprintf(stderr, "Error: Get host』s information failed\n");
exit(0);
}
/* 填寫乙太網頭部*/
memcpy(req.eh.ether_dhost, MAC_BCAST_ADDR, ETH_ALEN);
memcpy(req.eh.ether_shost, mac, ETH_ALEN);
req.eh.ether_type = htons(ETHERTYPE_ARP);
/* 填寫arp數據 */
req.ea.arp_hrd = htons(ARPHRD_ETHER);
req.ea.arp_pro = htons(ETHERTYPE_IP);
req.ea.arp_hln = ETH_ALEN;
req.ea.arp_pln = INLEN;
req.ea.arp_op = htons(ARPOP_REQUEST);
memcpy(req.ea.arp_sha, mac, ETH_ALEN);
memcpy(req.ea.arp_spa, &lc_addr, INLEN);
inet_aton(argv[1], req.ea.arp_tpa);
if((n = sendto(reqfd, &req, sizeof(req), 0, (struct sockaddr *)&reqsa, sizeof(reqsa))) <= 0) {
perror("Sendto error");
exit(1);
}
printf("Broadcast arp request of %s, %d bytes be sent\n\n", argv[1], n);
recvfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ARP));
bzero(recv_buf, sizeof(recv_buf));
bzero(&repsa, sizeof(repsa));
salen = sizeof(struct sockaddr_ll);
while(1) {
if((n = recvfrom(recvfd, recv_buf, sizeof(req), 0, (struct sockaddr *)&repsa, &salen)) <= 0) {
perror("Recvfrom error");
exit(1);
}
if( ntohs(*(__be16 *)(recv_buf + 20))==2 && !memcmp(req.ea.arp_tpa, recv_buf + 28, 4) ) {
printf("Response from %s, %d bytes received\n", argv[1], n);
printf(" Peer IP is: %s\n", inet_ntop(AF_INET, (struct in_addr *)(recv_buf + 28), rep_addr, 1024));
prmac( (u_char *)(recv_buf + 22) ); //prmac( (u_char *)(recv_buf + 6) );
break;
}
}
free(mac);
}
int get_ifi(char *dev, char * mac, int macln, struct in_addr *lc_addr, int ipln)
{
int reqfd, n;
struct ifreq macreq;
reqfd = socket(AF_INET, SOCK_DGRAM, 0);
strcpy(macreq.ifr_name, dev);
/* 獲取本地介面MAC地址*/
if(ioctl(reqfd, SIOCGIFHWADDR, ¯eq) != 0)
return 1;
memcpy(mac, macreq.ifr_hwaddr.sa_data, macln);
/* 獲取本地介面IP地址*/
if(ioctl(reqfd, SIOCGIFADDR, ¯eq) != 0)
return 1;
memcpy(lc_addr, &((struct sockaddr_in *)(¯eq.ifr_addr))->sin_addr, ipln);
return 0;
}
void prmac(u_char *ptr)
{
printf(" Peer MAC is: %02x:%02x:%02x:%02x:%02x:%02x\n",*ptr,*(ptr+1),*(ptr+2),*(ptr+3),*(ptr+4),*(ptr+5));
}
void usage_quit(char *arg0)
{
fprintf(stderr, "Usage: %s \n", arg0);
exit(1);
}
此程序需要root許可權運行,或者設置suid。
此程序用到的結構體和宏,在/usr/include/linux/if_ether.h /usr/include/linux/if_arp.h /usr/include/net/ethernet.h /usr/include/netinet/if_ether.h 中有相應的聲明。
其他參考: man packet,《TCP/IP 詳解 卷一》第四章
如果想偷偷的實驗此程序,tcpdump 能夠幫你找到接入區域網的其他主機(額,陰暗心理又暴露了…)。
程序運行如下

另一終端 tcpdump 探嗅




[火星人 via ] linux socket 發送ARP數據包已經有7185次圍觀

http://www.coctec.com/docs/linux/show-post-184099.html