如果 A (192.168.1.1 )向 B (192.168.1.2 )发送一个数据包,那么需要的条件有 ip、port、使用的协议(TCP/UDP)之外还需要 MAC 地址,因为在以太网数据包中 MAC 地址是必须要有的。那么怎样才能知道对方的 MAC 地址?答案是:它通过 ARP 协议来获取对方的 MAC 地址。
ARP(Address Resolution Protocol,地址解析协议),是 TCP/IP 协议族中的一个,主要用于查询指定 ip 所对应的的 MAC(通过 ip 找 MAC)。
请求方使用广播来发送请求,应答方使用单播来回送数据。收到返回消息后将该 IP 地址和物理地址存入本机 ARP 缓存中并保留一定时间,下次请求时直接查询 ARP 缓存以节约资源。
以机器 A 获取机器 B 的 MAC 为例,A 广播发送一个 ARP 请求包,和 A 同在一个局域网的主机都会收到这个请求包,每个机器都会比较自己的 ip 和请求包的目的 ip 是不是一样的,如果不一样,就丢弃这个请求包,结果,只有 B 机器符合条件,B 机器单独给 A 发送 ARP 应答包,应答包带上了 B 的 ip 所对应的 MAC 地址,当 A 收到这个应答包后,就把 B 的 ip 以及其对应的 MAC 地址存入本机 ARP 缓存中。
在 Linux 查看 ARP 缓存表:arp
在 Windows 查看 ARP 缓存表:arp -a
ARP头部
1、Dest MAC:目的 MAC 地址
2、Src MAC:源 MAC 地址
3、帧类型:0x0806
4、硬件类型:1(以太网)
5、协议类型:0x0800(IP地址)
6、硬件地址长度:6
7、协议地址长度:4
8、OP:1(ARP请求),2(ARP应答),3(RARP请求),4(RARP应答)
接下来这个例子为,虚拟机(ubuntu)获取 PC 机的 MAC 地址:
先查看 ubuntu 的 ip 和 MAC 地址:
完整代码如下:
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <net/if.h> //struct ifreq
- #include <sys/ioctl.h> //ioctl、SIOCGIFADDR
- #include <sys/socket.h>
- #include <netinet/ether.h> //ETH_P_ALL
- #include <netpacket/packet.h> //struct sockaddr_ll
- #include <netinet/in.h>
-
- int main(int argc,charchar *argv[])
- {
-
- int sock_raw_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
-
-
- unsigned char send_msg[1024] = {
-
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0x00, 0x0c, 0x29, 0x97, 0xc7,0xc1,
- 0x08, 0x06,
-
-
- 0x00, 0x01, 0x08, 0x00,
- 0x06, 0x04, 0x00, 0x01,
- 0x00, 0x0c, 0x29, 0x97, 0xc7,0xc1,
- 10, 221, 0, 11,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 10, 221, 20, 10
- };
-
-
- struct sockaddr_ll sll;
- struct ifreq ethreq;
- strncpy(ethreq.ifr_name, "eth0", IFNAMSIZ);
-
-
- ioctl(sock_raw_fd, SIOCGIFINDEX, (charchar *)ðreq);
- bzero(&sll, sizeof(sll));
- sll.sll_ifindex = ethreq.ifr_ifindex;
-
-
- int len = sendto(sock_raw_fd, send_msg, 42, 0 , (struct sockaddr *)&sll, sizeof(sll));
- if(len == -1)
- {
- perror("sendto");
- }
-
-
- unsigned char recv_msg[1024] = {0};
- recvfrom(sock_raw_fd, recv_msg, sizeof(recv_msg), 0, NULL, NULL);
- if(recv_msg[21] == 2)
- {
- char resp_mac[18] = "";
- char resp_ip[16] = "";
-
- sprintf(resp_mac, "%02x:%02x:%02x:%02x:%02x:%02x", \
- recv_msg[22],recv_msg[23],recv_msg[24],recv_msg[25],recv_msg[26],recv_msg[27]);
- sprintf(resp_ip, "%d.%d.%d.%d", recv_msg[28], recv_msg[29], recv_msg[30], recv_msg[31]);
- printf("IP:%s - MAC:%s\n",resp_ip, resp_mac);
- }
-
- return 0;
- }
程序运行结果如下:
查看 PC 的网卡信息:
下面的例子能够获取指定网段所有机器的 MAC 地址:
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <net/if.h> //struct ifreq
- #include <sys/ioctl.h> //ioctl、SIOCGIFADDR
- #include <sys/socket.h>
- #include <netinet/ether.h> //ETH_P_ALL
- #include <netpacket/packet.h> //struct sockaddr_ll
- #include <pthread.h>
- #include <netinet/in.h>
- voidvoid *send_arp_ask(voidvoid *arg);
- int main(int argc,charchar *argv[])
- {
-
- int sock_raw_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
-
-
- pthread_t tid;
- pthread_create(&tid, NULL, (voidvoid *)send_arp_ask, (voidvoid *)sock_raw_fd);
-
- while(1)
- {
-
- unsigned char recv_msg[1024] = "";
- recvfrom(sock_raw_fd, recv_msg, sizeof(recv_msg), 0, NULL, NULL);
- if(recv_msg[21] == 2)
- {
- char resp_mac[18] = "";
- char resp_ip[16] = "";
-
- sprintf(resp_mac, "%02x:%02x:%02x:%02x:%02x:%02x", \
- recv_msg[22],recv_msg[23],recv_msg[24],recv_msg[25],recv_msg[26],recv_msg[27]);
- sprintf(resp_ip, "%d.%d.%d.%d", recv_msg[28], recv_msg[29], recv_msg[30], recv_msg[31]);
- printf("IP:%s - MAC:%s\n",resp_ip, resp_mac);
- }
- }
-
- return 0;
- }
-
- voidvoid *send_arp_ask(voidvoid *arg)
- {
- int i = 0;
- int sock_raw_fd = (int)arg;
-
- unsigned char send_msg[1024] = {
-
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0x00, 0x0c, 0x29, 0x75, 0xa6, 0x51,
- 0x08, 0x06,
-
-
- 0x00, 0x01, 0x08, 0x00,
- 0x06, 0x04, 0x00, 0x01,
- 0x00, 0x0c, 0x29, 0x75, 0xa6, 0x51,
- 172, 20, 226, 12,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 172, 20, 226, 11
- };
-
-
- struct sockaddr_ll sll;
- struct ifreq ethreq;
- strncpy(ethreq.ifr_name, "eth0", IFNAMSIZ);
-
-
- ioctl(sock_raw_fd, SIOCGIFINDEX, (charchar *)ðreq);
- bzero(&sll, sizeof(sll));
- sll.sll_ifindex = ethreq.ifr_ifindex;
-
-
- if(!(ioctl(sock_raw_fd, SIOCGIFADDR, (charchar *)ðreq)))
- {
- int num = ntohl(((struct sockaddr_in*) (ðreq.ifr_addr))->sin_addr.s_addr);
- for(i=0; i<4; i++)
- {
- send_msg[31-i] = num>>8*i & 0xff;
- }
- }
-
-
- if (!(ioctl(sock_raw_fd, SIOCGIFHWADDR, (charchar *) ðreq)))
- {
- for(i=0; i<6; i++)
- {
-
- send_msg[22+i] = send_msg[6+i] = (unsigned char) ethreq.ifr_hwaddr.sa_data[i];
- }
- }
-
- while(1)
- {
- int i = 0;
- int num[4] = {0};
- unsigned char input_buf[1024] = "";
-
-
- printf("input_dst_Network:172.20.226.0\n");
- fgets(input_buf, sizeof(input_buf), stdin);
- sscanf(input_buf, "%d.%d.%d.", &num[0], &num[1], &num[2]
- );
-
-
- for(i=0;i<4;i++)
- send_msg[38+i] = num[i];
-
-
- for(i=1; i<255; i++)
- {
- send_msg[41] = i;
- int len = sendto(sock_raw_fd, send_msg, 42, 0 , (struct sockaddr *)&sll, sizeof(sll));
- if(len == -1)
- {
- perror("sendto");
- }
- }
- sleep(1);
- }
- return;
- }
程序运行结果如下:
转自:http://blog.csdn.net/tennysonsky/article/details/44923355