- 实现目的:
提供一个接口,根据目的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地址。
- 实现步骤:
-
参考iproute2源码中ip route get命令实现根据目的ip查找dev name【netlink RTM_GETROUTE】
-
利用socket发送imcp echo request报文来触发协议栈发送arp request 报文,从而是linux邻居子系统获取目的ip对应得arp表项
-
利用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的方法比较固定,这里就不详细讲解了。
源码如下:
- 根据目的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