项目中需要判断局域网内某个IP是否被占用,一开始想到的是ping命令,但是ping只能判断同一网段的IP。后来发现linux使用arping命令可以判断,如使用arping -D -f -w 1 x.x.x.x
但是对于经过裁剪的嵌入式linux,busybox中不一定还保留arping命令,而且C代码中调用shell命令需要临时创建一个子进程来执行,频繁操作会浪费资源。于是决定参考busybox源码中的arping.c自己实现C代码socket的ARP,以下代码是从busybox的arping.c中提取的。
需要能区分出以下6种情况:
- 同网段本IP且IP不冲突
- 同网段本IP且IP冲突
- 同网段其他IP且IP存在
- 同网段其他IP且IP不存在
- 跨网段IP存在
- 跨网段IP不存在
如何使用
调用函数arp_get_mac即可,参数if_name 是网卡名称,嵌入式中经常是eth0,str_src_ip是本设备的IP,str_dst_ip是需要判断的目标IP,dst_mac返回目标IP对应设备的MAC,timeout_ms指定超时时间,单位是ms。
返回值中,对于str_src_ip与str_dst_ip不相同的情况下,该IP正被占用返回1,未被占用返回0.对于str_src_ip与str_dst_ip相同的情况,该IP只有本设备在使用则返回2,本设备的IP与别的设备IP冲突,且该IP正被别的设备占用则返回1。
/**
返回值: -1 错误
0 不存在
1 存在
2 请求IP是本设备IP,且IP可用
*/
int arp_get_mac(char *if_name,char *str_src_ip,char *str_dst_ip,unsigned char *dst_mac,int timeout_ms)
流程解释
先按需求设置好socket,封装好struct sockaddr以及需要发送的数据,以UDP广播的方式发送出去,然后接收广播包,在接收到的数据包中分析提取需要的信息,如果能提取到则说明该IP正在被占用。
实例代码:
/**
根据指定IP地址获取MAC地址,可用于判断IP是否被占用
*/
#include <stdlib.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <linux/sockios.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <linux/if_arp.h>
#include <sys/uio.h>
#include <netdb.h>
#include <unistd.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <debug_util.h>
#include "arp_get_mac.h"
#define MS_TDIFF(tv1,tv2) ( ((tv1).tv_sec-(tv2).tv_sec)*1000 + ((tv1).tv_usec-(tv2).tv_usec)/1000 )
int send_pack(int fd, struct in_addr src, struct in_addr dst,
struct sockaddr_ll *ME, struct sockaddr_ll *HE)
{
int advert=0;
int err;
struct timeval now;
unsigned char buf[256];
struct arphdr *ah = (struct arphdr*)buf;
unsigned char *p = (unsigned char *)(ah+1);
ah->ar_hrd = htons(ME->sll_hatype); /* 硬件地址类型*/
if (ah->ar_hrd == htons(ARPHRD_FDDI))
ah->ar_hrd = htons(ARPHRD_ETHER);
ah->ar_pro = htons(ETH_P_IP); /* 协议地址类型 */
ah->ar_hln = ME->sll_halen; /* 硬件地址长度 */
ah->ar_pln = 4; /* 协议地址长度 */
a