如果 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,char *argv[])
{//1.创建通信用的原始套接字int sock_raw_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));//2. 根据各种协议首部格式构建发送数据报unsigned char send_msg[1024] = {//--------------组MAC--------14------0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //dst_mac: FF:FF:FF:FF:FF:FF0x00, 0x0c, 0x29, 0x97, 0xc7,0xc1, //src_mac: 00:0c:29:97:c7:c10x08, 0x06,                          //类型:0x0806 ARP协议//--------------组ARP--------28-----0x00, 0x01, 0x08, 0x00,              //硬件类型1(以太网地址),协议类型0x0800(IP)   0x06, 0x04, 0x00, 0x01,             //硬件、协议地址分别是6、4,op:(1:arp请求,2:arp应答)0x00, 0x0c, 0x29, 0x97, 0xc7,0xc1,  //发送端的MAC地址10,  221,  0, 11,                //发送端的IP地址0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   //目的MAC地址(由于要获取对方的MAC,所以目的MAC置零)10, 221, 20, 10               //目的IP地址};//3.数据初始化struct sockaddr_ll sll;                  //原始套接字地址结构struct ifreq req;                    //网络接口地址strncpy(req.ifr_name, "eth0", IFNAMSIZ);  //指定网卡名称//4.将网络接口赋值给原始套接字地址结构ioctl(sock_raw_fd, SIOCGIFINDEX, &req);bzero(&sll, sizeof(sll));sll.sll_ifindex = req.ifr_ifindex;//5. 发送 ARP 请求包int len = sendto(sock_raw_fd, send_msg, 42, 0 , (struct sockaddr *)&sll, sizeof(sll));if(len == -1){perror("sendto");}//6.接收对方的ARP应答unsigned char recv_msg[1024] = {0};recvfrom(sock_raw_fd, recv_msg, sizeof(recv_msg), 0, NULL, NULL);if(recv_msg[21] == 2)            //ARP应答{char resp_mac[18] = "";      //arp响应的MACchar resp_ip[16] = "";        //arp响应的IPsprintf(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>
void *send_arp_ask(void *arg);
int main(int argc,char *argv[])
{//1.创建通信用的原始套接字int sock_raw_fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));//2.创建发送线程pthread_t tid;pthread_create(&tid, NULL, (void *)send_arp_ask, (void *)sock_raw_fd);while(1){//3.接收对方的ARP应答unsigned char recv_msg[1024] = "";recvfrom(sock_raw_fd, recv_msg, sizeof(recv_msg), 0, NULL, NULL);if(recv_msg[21] == 2)         //ARP应答{char resp_mac[18] = "";      //arp响应的MACchar resp_ip[16] = "";        //arp响应的IPsprintf(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;
}void *send_arp_ask(void *arg)
{int i = 0;int sock_raw_fd = (int)arg;//1.根据各种协议首部格式构建发送数据报unsigned char send_msg[1024] = {//--------------组MAC--------14------0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //dst_mac: FF:FF:FF:FF:FF:FF0x00, 0x0c, 0x29, 0x75, 0xa6, 0x51, //src_mac: 00:0c:29:75:a6:510x08, 0x06,                           //类型:0x0806 ARP协议//--------------组ARP--------28-----0x00, 0x01, 0x08, 0x00,              //硬件类型1(以太网地址),协议类型0x0800(IP)   0x06, 0x04, 0x00, 0x01,             //硬件、协议地址分别是6、4,op:(1:arp请求,2:arp应答)0x00, 0x0c, 0x29, 0x75, 0xa6, 0x51, //发送端的MAC地址172,  20,   226,  12,                //发送端的IP地址0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   //目的MAC地址(由于要获取对方的MAC,所以目的MAC置零)172,  20,   226,  11              //目的IP地址};//2.数据初始化struct sockaddr_ll sll;                  //原始套接字地址结构struct ifreq req;                    //网络接口地址strncpy(req.ifr_name, "eth0", IFNAMSIZ);  //指定网卡名称//3.将网络接口赋值给原始套接字地址结构ioctl(sock_raw_fd, SIOCGIFINDEX, &req);bzero(&sll, sizeof(sll));sll.sll_ifindex = req.ifr_ifindex;//4.本地机的IPif(!(ioctl(sock_raw_fd, SIOCGIFADDR, &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;    //将发送端的IP地址组包}}//5.获取本地机(eth0)的MACif (!(ioctl(sock_raw_fd, SIOCGIFHWADDR, (char *) &req))){for(i=0; i<6; i++){//将src_mac、发送端的MAC地址组包send_msg[22+i] = send_msg[6+i] = (unsigned char) req.ifr_hwaddr.sa_data[i];           }}while(1){int i = 0;int num[4] = {0};unsigned char input_buf[1024] = "";//6.获取所要扫描的网段(172.20.226.0)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]//目的IP地址 );//7.将键盘输入的信息组包for(i=0;i<4;i++)send_msg[38+i] = num[i];//将目的IP地址组包//8.给1~254的IP发送ARP请求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;
}

程序运行结果如下:

源代码下载请点此次。



Linux 网络编程——原始套接字实例:MAC 地址扫描器相关推荐

  1. Linux网络编程——原始套接字编程

    Linux网络编程--原始套接字编程 转自:http://blog.csdn.net/tennysonsky/article/details/44676377 原始套接字编程和之前的 UDP 编程差不 ...

  2. linux串口编程实例_Linux 网络编程——原始套接字实例:发送 UDP 数据包

    以太网报文格式: IP 报文格式: UDP 报文格式: 校验和函数: /*******************************************************功能:校验和函数参 ...

  3. Linux网络编程——原始套接字能干什么?

    一.知识回顾: 通常情况下程序员接所接触到的套接字(Socket)为两类: (1)流式套接字(SOCK_STREAM):一种面向连接的 Socket,针对于面向连接的TCP 服务应用: (2)数据报式 ...

  4. Linux原始网络编程,Linux操作系统网络编程 原始套接字 (1)

    Linux操作系统网络编程--原始套接字 (1) http://soft.zdnet.com.cn/software_zone/2007/1020/568223.shtml 我们在前面已经学习过了网络 ...

  5. Linux网络编程之套接字基础

    Linux网络编程之套接字基础 1.套接字的基本结构 struct sockaddr 这个结构用来存储套接字地址. 数据定义: struct sockaddr { unsigned short sa_ ...

  6. 【Linux网络编程】套接字简介

    00. 目录 文章目录 00. 目录 01. 概述 02. 套接字属性 03. socket函数 04. 套接字地址结构 05. 附录 01. 概述 Socket套接字由远景研究规划局(Advance ...

  7. 【Linux网络编程】套接字的介绍

    套接字是一种通信机制(通信的两方的一种约定),凭借这种机制,不同主机之间的进程可以进行通信.我们可以用套接字中的相关函数来完成通信过程. 套接字的特性有三个属性确定,它们是:域(domain),类型( ...

  8. Linux网络编程 之 套接字(四)

    目录 1. 套接字的定义 2. 套接字的创建方法 3. 套接字的地址 本地套接字 网络套接字 1. 套接字的定义 套接字是一种通信机制(通信的两方的一种约定),凭借这种机制,不同主机之间的进程可以进行 ...

  9. linux网络编程 华清,Linux网络编程之套接字

    一 :套接字属性 套接字由域(domain),类型(type)和协议(protocol)三个属性确定其特性. 1)套接字的域 域指定套接字通信中使用的网络 介质,常见的套接字域是AF_INET,它指的 ...

最新文章

  1. 三星超过台积电 成为全球市值最高半导体公司
  2. myNote app debug - page render
  3. 如何使用Chrome开发者工具调试web socket应用
  4. 异步日志系统设计demo
  5. 四、mysql数据常用命令
  6. cocos2d(粒子效果编辑器)
  7. 协同过滤算法_《推荐系统实践》3.基于物品的协同过滤算法
  8. Python开源机器学习项目实战
  9. 吸烟 打电话 行为 图片 数据集
  10. 计算机内存不足黑屏怎么办,win10内存不足会黑屏怎么办
  11. TF-IDF算法总结
  12. Java文件操作——简单文件搜索
  13. (问题)双击页面时,会出现蓝色背景,选中文字,css 样式解决问题
  14. OSChina 周三乱弹 ——发福利的日子到了!来领妹子!
  15. tensorflow2.0 dqn 深度强化学习 AI自动玩游戏,有详细注解
  16. LeetCode 387、字符串中的第一个唯一字符
  17. 10行代码DIY一个类USB Rubber Ducky来遥控自己电脑
  18. 2016全域大数据应用论坛11位嘉宾核心观点
  19. 基于QT实现的多媒体播放器
  20. HYKSVCAO2V4F3电液伺服阀控制器

热门文章

  1. REVISITING DYNAMIC CONVOLUTION VIA MATRIXDECOMPOSITION 官方代码解析心得
  2. css wangeditor 修改_HTML富文本编辑器wangEditor的使用
  3. 史上最牛逼的音乐播放器—仿网易云音乐(已开源)
  4. Gatsby中怎么使用emotion?
  5. 一起背英语单词(一):新概念英语I - 长度1到3的英语单词
  6. 【转载】林萧教会你--如何做到招聘要求中的“要有扎实的Java基础”。
  7. mysql批量添加卡号_mysql数据库卡号卡密批量生成写入验证附易辅助模块
  8. 【CAD开发】gltf文件格式的转换工具汇总(js、python、c++)
  9. 六、T100库存管理之定期盘点
  10. BZOJ3039-玉蟾宫