linux 实现主动arp resolve 功能

本文介绍了如何在Linux系统中实现主动ARP解析功能,通过发送ICMP请求触发ARP请求,最终获取目的IP的MAC地址。首先查找出口设备,然后利用socket发送ICMP请求,通过netlink RTM_GETROUTE和ioctl SIOCGARP操作获取ARP表项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 实现目的:

提供一个接口,根据目的ip地址,获取相应的mac地址。

  • 实现方法:

首先,根据目的ip,通过路由表找到出口设备;然后,通过socket发送icmp echo request报文,因为目的ip对应的mac地址还没有,所以linux tcp/ip协议栈会发送arp request报文,这样linux协议栈收到目的ip主机发送的arp reply报文后,就会学到目的ip对应得mac地址,从而添加一条arp信息到arp table里;最后,利用目的ip和出口设备的ifname找到目的ip的mac地址。

  • 实现步骤:
  1. 参考iproute2源码中ip route get命令实现根据目的ip查找dev name【netlink  RTM_GETROUTE】

  2. 利用socket发送imcp echo request报文来触发协议栈发送arp request 报文,从而是linux邻居子系统获取目的ip对应得arp表项

  3. 利用ioctl SIOCGARP查找对应得arp表项

  • 实现根据目的ip查找出口设备方法

    就是利用netlink 送内核发送路由请求消息,接收内核发送的路由信息,解析该路由信息,得到所需的信息。

    1.构造netlink route request消息,消息结构体定义及格式如下:

    

    2.该结构体是iproute源码定义的ip route 命令发送route cmd的netlink消息

    3.创建netlink socket,发送netlink route request消息,接收内核发送的reponse消息

    4.解析内核发出的reponse消息,找到目的ip的出口设备的ifname

  • 实现间接触发arp request报文的方法

利用socket发送imcp echo request报文,来间接触发协议栈发送arp request 报文【在没有arp的情况下】,从而是linux邻居子系统获取目的ip对应得arp表项

利用socket发送imcp echo request报文来间接触发协议栈发送arp request 报文,而不是直接构造arp request报文的方法的原因,是基于linux的安全考虑的【在默认情况下,如果一台主机收到一个ARPOP_REPLY,但它没有对应得未决ARPOP_REQUEST,那么它就会将该应答丢弃】,所以采用这种间接的方法。

  • 利用ioctl SIOCGARP 查找对应的arp表项

利用ioctl及 SIOCGARP查找arp的方法比较固定,这里就不详细讲解了。

源码如下:

  1. 根据目的ip查找出口设备
/*根据目的ip查找出口设备*/
struct rtnl_handle
{
    int            fd;
    struct sockaddr_nl    local;
    struct sockaddr_nl    peer;
    unsigned int        seq;
};

/*
    function:
        @get interface name by index
    parameter:
        @ifindex: interface index
        @ifname : for storage interface name
    return:
        @OK     : get interface name by index successd
        @ERROR  : failed
*/
int os_index_to_name(int ifindex, char *ifname)
{
    int sockfd;
    struct ifconf ifc;
    char buf[4000] = {0x0};
    int i = 0;
    
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        printf("in %s in %d socket error",__FUNCTION__,__LINE__);
        return ERROR;
    }
                    
    /* Init struct ifconf. */
    ifc.ifc_len = 4000;
    ifc.ifc_buf = (caddr_t)buf;
        
    /* Get all interface info. */
    if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0)
    {
        printf("in %s in %d ioctl execute failed\n",__FUNCTION__,__LINE__);
        close(sockfd);
        return ERROR;
    }
        
    /* Match ifindex for get ifname. */
    struct ifreq *ifr;
    struct ifreq ifrcopy;
    ifr = (struct ifreq*)buf;
    for(i = (ifc.ifc_len/sizeof(struct ifreq)); i>0; i--)
    {      
        ifrcopy = *ifr;
        if(ioctl(sockfd, SIOCGIFINDEX, &ifrcopy) < 0)
        {
            printf("in %s in %d ioctl execute failed\n",__FUNCTION__,__LINE__);
            close(sockfd);
            return ERROR;            
        }
        if(ifrcopy.ifr_ifru.ifru_ivalue == ifindex)
        {
            strcpy(ifname, ifr->ifr_ifrn.ifrn_name);
            printf("ifindex[%d] to ifname[%s]\n",ifindex,ifname);
            close(sockfd);
            return OK;
        }
        ifr++;
    }
    close(sockfd);
    return ERROR;
}
int os_index_to_name_test(int ifindex)
{
    int sockfd;
    struct ifconf ifc;
    char buf[1024] = {0x0};
    char ifname[100] = {0x0};
    int i = 0;
    
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        printf("in %s in %d socket error",__FUNCTION__,__LINE__);
        return ERROR;
    }
                    
    //初始化ifconf结构
    ifc.ifc_len = 1024;
    ifc.ifc_buf = (caddr_t)buf;
        
    //获取所有接口信息
    if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0)
    {
        printf("in %s in %d ioctl execute failed\n",__FUNCTION__,__LINE__);
        close(sockfd);
        return ERROR;
    }
        
    //遍历每一个ifreq结构
    struct ifreq *ifr;
    struct ifreq ifrcopy;
    ifr = (struct ifreq*)buf;
    for(i = (ifc.ifc_len/sizeof(struct ifreq)); i>0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值