最近遇到一个需求,或者说是一个用户现场问题。

  我们设备先安装,设置dhcp模式获取ip进行联网,后来又安装了其他设备,但该设备是手动设置的静态ip地址,正好与我们设备冲突,造成网络故障。

  那我们就需要有一个能够检测ip冲突的方法,这个可以使用ARP协议的ACD功能(Address Conflict Detection,在RFC 5227中提出)。

  (ARP协议主要用于局域网IP地址与MAC地址转换,因为我们网络主机之间收发数据其实是使用的硬件地址而非IP地址;向外网发数据时其实就是发给网关,网关再发给网关以此类推)

  ACD功能中使用ARP Request的不同两种参数填充方式分别作为ARP Probe和ARP Announcement,后者是告诉别人我要用某个IP地址了,前者是闻讯局域网内是否有人已经用了某个IP地址。

  因此我们可以使用ARP Probe来检测IP冲突。

  当我们通过网卡向局域网广播ARP Probe时,如果其他主机使用了我们闻讯的IP地址,则会响应,我们就知道IP冲突了。

  代码如下

  1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <sys/types.h>
6 #include <sys/socket.h>
7 #include <linux/if_packet.h>
8 #include <netdb.h>
9 #include <unistd.h>
10 #include <arpa/inet.h>
11 #include <sys/ioctl.h>
12 #include <net/ethernet.h>
13 #include <netinet/ether.h>
14 #include <net/if.h>
15 #include <netinet/ip.h>
16 #include <sys/ioctl.h>
17 #include <net/if.h>
18 #include <netinet/in.h>
19 #include <netdb.h>
20 #include <sys/time.h>
21
22 static int get_mac(const char *if_name, char *mac);
23 static int get_ifidx(const char *if_name, int *idx);
24
25 int main(int argc, char *argv[])
26 {
27 if (argc < 3)
28 {
29 printf("Usage: %s if_name detect_ip\n\tLike: %s wlan0 192.168.0.211\n", argv[0], argv[0]);
30 exit(EXIT_FAILURE);
31 }
32
33 const char *if_name = argv[1];
34 const char *dst_ip = argv[2];
35
36 unsigned int ip4bit = 0;
37 {
38 struct in_addr addr = {0};
39 if (inet_aton(dst_ip, &addr) == 0)
40 {
41 perror("inet_aton");
42 exit(EXIT_FAILURE);
43 }
44 ip4bit = addr.s_addr;
45 }
46
47 char mac[6] = {0};
48 if (get_mac(if_name, mac) != 0)
49 {
50 perror("inet_aton");
51 exit(EXIT_FAILURE);
52 }
53
54 int sock_client = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
55 if (sock_client < 0)
56 {
57 perror("socket");
58 exit(EXIT_FAILURE);
59 }
60 int sock_server;
61 if ((sock_server = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP))) < 0)
62 {
63 perror("cannot create socket");
64 exit(EXIT_FAILURE);
65 }
66 struct timeval tv_out;
67 tv_out.tv_sec = 1;
68 tv_out.tv_usec = 0;
69 setsockopt(sock_server, SOL_SOCKET, SO_RCVTIMEO, &tv_out, sizeof(tv_out));
70
71 unsigned char arp_probe_msg[] = {
72 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /*目的mac地址*/
73 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*源mac地址*/
74 0x08, 0x06, /*帧类型*/
75
76 /*ARP报文头部(28个字节)*/
77 0x00, 0x01, /*硬件类型*/
78 0x08, 0x00, /*协议类型*/
79 6, /*硬件地址长度*/
80 4, /*协议地址长度*/
81 0x00, 0x01, /*ARP请求*/
82 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*源mac地址*/
83 0, 0, 0, 0, /*源IP*/
84 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /*目的mac地址*/
85 0, 0, 0, 0 /*目的IP*/
86 };
87
88 memcpy(arp_probe_msg + 6, mac, 6);
89 memcpy(arp_probe_msg + 22, mac, 6);
90 memcpy(arp_probe_msg + 38, &ip4bit, 4);
91
92 int if_idx;
93 if (get_ifidx(if_name, &if_idx) != 0)
94 exit(EXIT_FAILURE);
95
96 // 发送5次
97 for (int i = 0; i < 5; ++i)
98 {
99 struct sockaddr_ll sll;
100 bzero(&sll, sizeof(sll));
101 sll.sll_ifindex = if_idx;
102
103 if (sendto(sock_client, arp_probe_msg, sizeof arp_probe_msg, 0, (struct sockaddr *)&sll, sizeof(sll)) < sizeof arp_probe_msg)
104 {
105 perror("sendto");
106 exit(EXIT_FAILURE);
107 }
108 }
109
110 char buffer[42] = {0};
111 int recv_count = 0;
112 // 接受最多100条或3秒超时
113 struct timeval recv_start_time;
114 gettimeofday(&recv_start_time, NULL);
115 while (recv_count++ < 100 && recv(sock_server, buffer, sizeof(buffer), 0))
116 {
117 if ((((buffer[12]) << 8) + buffer[13]) != ETH_P_ARP)
118 continue;
119 struct timeval now_time;
120 gettimeofday(&now_time, NULL);
121 if (now_time.tv_sec - recv_start_time.tv_sec > 2)
122 break;
123 char arp_rsp_mac[18] = {0};
124 char arp_rsp_ip[18] = {0};
125 sprintf(arp_rsp_mac, "%02x:%02x:%02x:%02x:%02x:%02x", buffer[22], buffer[23], buffer[24], buffer[25], buffer[26], buffer[27]);
126 sprintf(arp_rsp_ip, "%d.%d.%d.%d", buffer[28], buffer[29], buffer[30], buffer[31]);
127 // printf("%s %s\n", arp_rsp_mac, arp_rsp_ip);
128 if (strcmp(arp_rsp_ip, dst_ip) == 0)
129 {
130 printf("%s", arp_rsp_mac);
131 return 0;
132 }
133 }
134
135 return 0;
136 }
137
138 int get_mac(const char *if_name, char *mac)
139 {
140 int fd, rtn;
141 struct ifreq ifr;
142
143 if (!if_name || !mac)
144 {
145 return -1;
146 }
147 fd = socket(AF_INET, SOCK_DGRAM, 0);
148 if (fd < 0)
149 {
150 perror("socket");
151 return -1;
152 }
153 ifr.ifr_addr.sa_family = AF_INET;
154 strncpy(ifr.ifr_name, (const char *)if_name, IFNAMSIZ - 1);
155
156 if ((rtn = ioctl(fd, SIOCGIFHWADDR, &ifr)) == 0)
157 memcpy(mac, (unsigned char *)ifr.ifr_hwaddr.sa_data, 6);
158 close(fd);
159 return rtn;
160 }
161
162 int get_ifidx(const char *if_name, int *idx)
163 {
164 int fd, rtn;
165 struct ifreq ifr;
166
167 if (!if_name || !idx)
168 {
169 return -1;
170 }
171 fd = socket(AF_INET, SOCK_DGRAM, 0);
172 if (fd < 0)
173 {
174 perror("socket");
175 return -1;
176 }
177 ifr.ifr_addr.sa_family = AF_INET;
178 strncpy(ifr.ifr_name, (const char *)if_name, IFNAMSIZ - 1);
179
180 if ((rtn = ioctl(fd, SIOCGIFINDEX, &ifr)) == 0)
181 *idx = ifr.ifr_ifindex;
182 close(fd);
183 return rtn;
184 }

  代码并不复杂,大家注意看71-90行,构造了ARP Probe数据封包。这个数据包就是向局域网广播,闻讯特定IP是否被使用。

  数据封包发送之后,我们就等待其他主机的响应。如果被使用了,则会命中130行代码。

  这个代码不仅可以在手动设置IP之前探测该IP是否被使用,也能在主机正常工作途中实时探测有没有其他设备设置了与我们冲突的IP。

  linux下使用原始套接字需要root权限哦。

  最后修改时间 2022-09-28 19:27:37

如何在linux下检测(自身)IP冲突的更多相关文章

  1. 如何在linux下检测内存泄漏

    之前的文章应用 Valgrind 发现 Linux 程序的内存问题中介绍了利用Linux系统工具valgrind检测内存泄露的简单用法,本文实现了一个检测内存泄露的工具,包括了原理说明以及实现细节. ...

  2. 如何在linux下检测内存泄漏(转)

    本文转自:http://www.ibm.com/developerworks/cn/linux/l-mleak/ 本文针对 linux 下的 C++ 程序的内存泄漏的检测方法及其实现进行探讨.其中包括 ...

  3. 如何在Linux下使用Rsync

    如何在Linux下使用Rsync 吐槽 昨天对scp进行总结之后看到最后有说到Rsync,俗语有云:好奇心害死猫.抱着学习的态度将Rsync给找了出来,然后进行了一些简单的学习.下面介绍一些个常用的命 ...

  4. 一步一步教你如何在linux下配置apache+tomcat(转)

    一步一步教你如何在linux下配置apache+tomcat   一.安装前准备. 1.   所有组件都安装到/usr/local/e789目录下 2.   解压缩命令:tar —vxzf 文件名(. ...

  5. 如何在Linux下安装Tomcat

    上篇文章写到了Linux下安装JDK1.8,这篇文章详细阐述一下 如何在Linux下安装Tomcat!!!有啥问题可以留言,博主每天都会看博客的. 准备步骤和方法和以前一样,博主用的工具是XShell ...

  6. 如何在Linux下安装JDK1.8

    本文会详细介绍如何在Linux下安装JDK1.8 首先要设置虚拟机的IP地址,不知道如何设置的话可以 翻看我的前一篇博客   http://www.cnblogs.com/xiaoxiaoSMILE/ ...

  7. 如何在linux下安装tomcat服务器

    linux作为现在比较主流的服务器操作系统,使用的机器广泛,安全稳定.tomcat作为应用容器当然可以有linux版本的tomcat.在linux上安装tomcat的方式也很简单,只需要运行脚本基本配 ...

  8. Linux下如何修改ip地址

    在Linux的系统下如何才能修改IP信息 以前总是用ifconfig修改,重启后总是得重做.如果修改配置文件,就不用那么麻烦了- A.修改ip地址 即时生效: # ifconfig eth0 192. ...

  9. 如何在Linux下拷贝一个目录呢

    cp -af newadmin/movie/.   uploadfile/mallvideo/ 如何在Linux下拷贝一个目录呢?这好像是再简单不过的问题了. 比如要把/home/usera拷贝到/m ...

随机推荐

  1. 【USACO 2012 Open】奶牛赛跑_题解

    奶牛赛跑 目录 奶牛赛跑 题目描述 输入格式 输出格式 样例 样例输入#1 样例输出#1 题解 代码 题目描述 约翰有头奶牛,他为这些奶牛准备了一个周长为的环形跑牛场.所有奶牛从起点同时起跑,奶牛在比 ...

  2. Keep In Line_via牛客网

    题目 链接:https://ac.nowcoder.com/acm/contest/28537/H 来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其他语 ...

  3. 闭包类型(Fn,FnMut,FnOnce)和move关键字

    move关键字是强制让环境变量的所有权转移到闭包中而不管是不是发生了所有权的转移 move关键字和匿名函数是否是FnOnce没有必然联系,之和匿名函数体有关 当匿名函数体里转移了环境变量的所有权的时候 ...

  4. java 向上,向下取整详解

    向上取整函数:Math.ceil(double a); 向下取整函数:Math.floor(double a); 需要注意的是:取整是对小数的取整,由于java自动转型机制,两个整数的运算结果依然是整 ...

  5. 关于标准IO缓冲区的问题

    关于标准IO缓冲区的问题 按照标准IO缓冲区可以分为三类: 不缓存类型: 一旦有数据,直接将数据写入到文件 行缓冲类型: 同全缓冲类型 遇到\n时,将数据写入文件 全缓冲类型: 当程序结束,将数据冲洗 ...

  6. scrollTop实例

    <!DOCTYPE html><html><head lang="en"> <meta charset="UTF-8" ...

  7. 万答#18,MySQL8.0 如何快速回收膨胀的UNDO表空间

    欢迎来到 GreatSQL社区分享的MySQL技术文章,如有疑问或想学习的内容,可以在下方评论区留言,看到后会进行解答 GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. 背 ...

  8. RunCat 怎么白嫖付费图标?这篇文章告诉你!

    RunCat 怎么白嫖付费图标?这篇文章告诉你! 什么是RunCat 为什么要破解RunCat 开始破解 所以可以看出第一个文件就是我们运行图标选项的数据,而stocks.json的就是图标商店的数据 ...

  9. 来开源吧!发布开源组件到 MavenCentral 仓库超详细攻略

    请点赞关注,你的支持对我意义重大. Hi,我是小彭.本文已收录到 GitHub · AndroidFamily 中.这里有 Android 进阶成长知识体系,有志同道合的朋友,关注公众号 [彭旭锐] ...

  10. 【Java】学习路径44-多线程入门篇

    这一章,我们学习线程的创建.线程的启动.线程的名字设置.线程的休眠.线程的加入.守护线程. 一个线程是一个单独的类的对象. 想让一个普通的类变成多线程,那么这个类需要继承Thread. 创建多线程的步 ...