1. 向指定的IP地址发送ARP请求
-
cong@msi:/work/busybox$ sudo ./_install/busybox arping 192.168.4.1
-
arping: interface eth0 not found: No such device
-
-
cong@msi:/work/busybox$ sudo ./_install/busybox arping -I eth2 192.168.4.1
-
ARPING to 192.168.4.1 from 192.168.4.68 via eth2
-
Unicast reply from 192.168.4.1 [10:d:f:1:2f:d0] 0.765ms
-
Unicast reply from 192.168.4.1 [10:d:f:1:2f:d0] 0.623ms
-
Unicast reply from 192.168.4.1 [10:d:f:1:2f:d0] 0.738ms
-
^CSent 3 probe(s) (1 broadcast(s))
-
Received 3 reply (0 request(s), 0 broadcast(s))
2. 源码
-
cong@msi:/work/test/busytest/3arping$ cat arping.c
-
#define _GNU_SOURCE /* See feature_test_macros(7) */
-
#include "utils.h"
-
#include <net/if.h>
-
#include <linux/sockios.h>
-
#include <linux/if_packet.h>
-
#include <linux/if_ether.h>
-
#include <net/if_arp.h>
-
#include <string.h>
-
#include <net/ethernet.h>
-
#include <netinet/ether.h>
-
# define move_from_unaligned32(v, u32p) (memcpy(&(v), (u32p), 4))
-
-
static int recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM, struct in_addr src,struct in_addr dst ,struct sockaddr_ll me, struct sockaddr_ll he)
-
{
-
//该函数对接收到的packet进行解析
-
struct arphdr *ah = (struct arphdr *) buf;
-
unsigned char *p = (unsigned char *) (ah + 1);
-
struct in_addr src_ip, dst_ip;
-
/* moves below assume in_addr is 4 bytes big, ensure that */
-
struct BUG_in_addr_must_be_4 {
-
char BUG_in_addr_must_be_4[
-
sizeof(struct in_addr) == 4 ? 1 : -1
-
];
-
char BUG_s_addr_must_be_4[
-
sizeof(src_ip.s_addr) == 4 ? 1 : -1
-
];
-
};
-
/* Filter out wild packets */
-
if (FROM->sll_pkttype != PACKET_HOST
-
&& FROM->sll_pkttype != PACKET_BROADCAST
-
&& FROM->sll_pkttype != PACKET_MULTICAST)
-
return -1;
-
-
//检查回复类型,此处应当是REPLY
-
if (ah->ar_op != htons(ARPOP_REQUEST) && ah->ar_op != htons(ARPOP_REPLY))
-
return -1;
-
-
/* ARPHRD check and this darned FDDI hack here :-( */
-
if (ah->ar_hrd != htons(FROM->sll_hatype)
-
&& (FROM->sll_hatype != ARPHRD_FDDI || ah->ar_hrd != htons(ARPHRD_ETHER)))
-
return -1;
-
//检查协议类型:必须是IP
-
if (ah->ar_pro != htons(ETH_P_IP)
-
|| (ah->ar_pln != 4)
-
|| (ah->ar_hln != me.sll_halen)
-
|| (len < (int)(sizeof(*ah) + 2 * (4 + ah->ar_hln))))
-
return -1;
-
-
move_from_unaligned32(src_ip.s_addr, p + ah->ar_hln);
-
move_from_unaligned32(dst_ip.s_addr, p + ah->ar_hln + 4 + ah->ar_hln);
-
-
if (dst.s_addr != src_ip.s_addr)
-
return -1;
-
if ((src.s_addr != dst_ip.s_addr)
-
|| (memcmp(p + ah->ar_hln + 4, &me.sll_addr, ah->ar_hln)))
-
return -1;
-
-
int s_printed = 0;
-
-
printf("%scast re%s from %s [%s]",
-
FROM->sll_pkttype == PACKET_HOST ? "Uni" : "Broad",
-
ah->ar_op == htons(ARPOP_REPLY) ? "ply" : "quest",
-
inet_ntoa(src_ip),
-
ether_ntoa((struct ether_addr *) p));
-
if (dst_ip.s_addr != src.s_addr) {
-
printf("for %s ", inet_ntoa(dst_ip));
-
s_printed = 1;
-
}
-
if (memcmp(p + ah->ar_hln + 4, me.sll_addr, ah->ar_hln)) {
-
if (!s_printed)
-
printf("for ");
-
printf("[%s]",
-
ether_ntoa((struct ether_addr *) p + ah->ar_hln + 4));
-
}
-
printf("\n");
-
-
memcpy(he.sll_addr, p, me.sll_halen);
-
return 0;
-
}
-
-
static int send_pack(int sock_fd, struct in_addr *src_addr,
-
struct in_addr *dst_addr, struct sockaddr_ll *ME,
-
struct sockaddr_ll *HE)
-
{
-
int err;
-
unsigned char buf[256];
-
struct arphdr *ah = (struct arphdr *) buf;
-
unsigned char *p = (unsigned char *) (ah + 1);
-
//以下的字符都是按照抓包的顺序填充的
-
//但是没有ARP字样,是不是跟bind有关?
-
ah->ar_hrd = htons(ARPHRD_ETHER); //00 01 :Ethernet
-
ah->ar_pro = htons(ETH_P_IP); //0800 :IP
-
ah->ar_hln = ME->sll_halen;
-
ah->ar_pln = 4;
-
ah->ar_op = htons(ARPOP_REQUEST);
-
-
p = mempcpy(p, &ME->sll_addr, ah->ar_hln);
-
p = mempcpy(p, src_addr, 4);
-
-
p = mempcpy(p, &HE->sll_addr, ah->ar_hln);
-
p = mempcpy(p, dst_addr, 4);
-
-
err = sendto(sock_fd, buf, p-buf, 0, (struct sockaddr *)HE, sizeof(*HE));
-
if(err<0)
-
{
-
dbmsg("%s",strerror(errno));
-
return -1;
-
}
-
return err;
-
}
-
-
int get_local_ipaddr(const char* iface, struct in_addr* inaddr)
-
{
-
int probe_fd;
-
int ret;
-
struct ifreq ifr;
-
if( (probe_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
-
{
-
dbmsg("%s",strerror(errno));
-
return -1;
-
}
-
strncpy(ifr.ifr_name, iface, strlen(iface)+1);
-
if(ioctl(probe_fd, SIOCGIFADDR, &ifr))
-
{
-
dbmsg("%s",strerror(errno));
-
return -1;
-
}
-
//dbmsg("%s", inet_ntoa(((struct sockaddr_in*)&(ifr.ifr_addr))->sin_addr));
-
mempcpy(inaddr, &(((struct sockaddr_in*)&(ifr.ifr_addr))->sin_addr), sizeof(struct in_addr));
-
close(probe_fd);
-
return 0;
-
}
-
-
int main ( int argc, char *argv[] )
-
{
-
char packet[4096];
-
int sockfd, ret;
-
struct ifreq ifr;
-
struct in_addr src,dst;
-
const char *device = "eth2";
-
struct sockaddr_ll me, he;
-
if(argc < 2)
-
{
-
dbmsg("usage: arping ");
-
return -1;
-
}
-
//1. create socket
-
if( (sockfd = socket(AF_PACKET, SOCK_DGRAM, 0)) < 0)
-
{
-
dbmsg("%s",strerror(errno));
-
return -1;
-
}
-
//2. check if eth2 exist, get eth2 index
-
memset(&ifr, 0, sizeof(ifr));
-
strncpy(ifr.ifr_name, device, strlen(device)+1);
-
if( (ret = ioctl(sockfd, SIOCGIFINDEX, &ifr)) < 0)
-
{
-
dbmsg("%s",strerror(errno));
-
return -1;
-
}
-
me.sll_ifindex = ifr.ifr_ifindex; //获取本机eth2的index,此处是2
-
-
ioctl(sockfd, SIOCGIFFLAGS, (char*)&ifr);
-
if(!(ifr.ifr_flags & IFF_UP)) //检查eth2是否是UP状态,DOWN的话就没法玩了
-
{
-
dbmsg("%s is down",device);
-
return -1;
-
}
-
if(ifr.ifr_flags & (IFF_NOARP|IFF_LOOPBACK)) //检查eth2是不是可以发送ARP
-
{
-
dbmsg("%s is not ARPable",device);
-
return -1;
-
}
-
-
//3. get local IP addr
-
get_local_ipaddr(device, &src); //获取本机eth2的ip地址
-
inet_aton(argv[1], &dst); //argv[1]是dst的ip地址
-
dbmsg("src=%s", inet_ntoa(src));
-
dbmsg("dst=%s", inet_ntoa(dst));
-
-
//4. send packet to dst
-
me.sll_family = AF_PACKET;
-
me.sll_protocol = htons(ETH_P_ARP);
-
bind(sockfd, (struct sockaddr*)&me, sizeof(me)); //这儿bind的作用可能很重要,不过没有太明白
-
socklen_t alen = sizeof(me);
-
getsockname(sockfd, (struct sockaddr *) &me, &alen);
-
if(me.sll_halen == 0)
-
{
-
dbmsg("not ARPable");
-
return -1;
-
}
-
he = me;
-
memset(he.sll_addr, -1, he.sll_halen); //dst的mac地址是ff:ff:ff:ff:ff,广播地址
-
send_pack(sockfd, &src, &dst, &me, &he);
-
-
-
//5. recv packet from dst
-
struct sockaddr_ll from;
-
alen = sizeof(from);
-
ret = recvfrom(sockfd, packet, 4096, 0, (struct sockaddr*)&from, &alen);
-
recv_pack(packet, ret, &from, src, dst, me, he);
-
-
return EXIT_SUCCESS;
-
}
3.运行结果:
-
cong@msi:/work/test/busytest/3arping$ sudo ./arping 192.168.4.5
-
arping.c:main[182]: src=192.168.4.112
-
arping.c:main[183]: dst=192.168.4.5
-
Unicast reply from 192.168.4.5 [12:9f:1c:d5:33:ac]
4.数据包解析
a.发送:
-
send: 1.818515000 Micro-St_99:f6:d8 Broadcast ARP 42 Who has 192.168.4.5? Tell 192.168.4.112
-
-
ff ff ff ff ff ff 44 8a 5b 99 f6 d8 08 06 00 01
-
08 00 06 04 00 01 44 8a 5b 99 f6 d8 c0 a8 04 70
-
ff ff ff ff ff ff c0 a8 04 05
-
-
ff ff ff ff ff ff --> dest broadcast
-
44 8a 5b 99 f6 d8 --> src mac
-
08 06 --> ARP
-
00 01 --> Ethernet
-
08 00 --> Protocol type: IP
-
06 --> hardware size
-
04 --> protocol size
-
00 01 --> opcode request: 1-->request
-
44 8a 5b 99 f6 d8 --> sender mac address
-
c0 a8 04 70 --> sender IP address 192.168.4.112
-
ff ff ff ff ff ff --> target mac address
-
c0 a8 04 05 --> target IP address
b.接收:
-
recv: 1.819112000 12:9f:1c:d5:33:ac Micro-St_99:f6:d8 ARP 60 192.168.4.5 is at 12:9f:1c:d5:33:ac
-
0000 44 8a 5b 99 f6 d8 12 9f 1c d5 33 ac 08 06 00 01
-
0010 08 00 06 04 00 02 12 9f 1c d5 33 ac c0 a8 04 05
-
0020 44 8a 5b 99 f6 d8 c0 a8 04 70 00 00 00 00 00 00
-
0030 00 00 00 00 00 00 00 00 00 00 00 00
-
-
44 8a 5b 99 f6 d8 -->dst mac
-
12 9f 1c d5 33 ac -->src mac
-
08 06 --> ARP
-
00 01 --> Ethernet
-
08 00 --> Protocol type: IP
-
06 --> hardware size
-
04 --> protocol size
-
00 02 --> opcode request: 2-->reply
-
12 9f 1c d5 33 ac --> sender mac address
-
c0 a8 04 05 --> sender IP address 192.168.4.112
-
44 8a 5b 99 f6 d8 --> target mac address
-
c0 a8 04 70 --> target IP address
-
00 00 ... --> padding
5.源码
3arping.rar (下载后改名为3arping.tar.gz)
6.补充
LINUX内核中有一个叫PF_PACKET的协议簇,它允许应用程序直接利用网络驱动程序发送和接收报文,避免了原来的协议栈处理过程。
在这种情况下,所有SOCKET
发送的报文直接送到以太网卡接口,而
接收到的任何报文将直接送到应用程序。
PF_PACKET协议簇的两个socket类型:
SOCK_DGRAM --> 让内核处理添加或者去除以太网报文头部工作
SOCK_RAW --> 让应用程序对以太网报文头部有完全的控制
阅读(2134) | 评论(0) | 转发(0) |