在接受到UDP包后,有时候我们需要根据所接收到得UDP包,获取它的路由目的IP地址和头标识目的地址。
(一)主要的步骤:
在setsockopt中设置IP_PKTINFO,然后通过recvmsg来获取struct in_pktinfo(struct in_pktinfo是struct msghdr中msg_control的成员).in_pktinfo 结构体(如下所示),我们可以从in_pktinfo中获取路由目的地址(destination address of the packet)、头标识目的地址(source address of the packet)。这种方法只能用于UDP(数据报)传输中。
struct in_pktinfo
{
unsigned int ipi_ifindex; /* 接口索引 */
struct in_addr ipi_spec_dst; /* 路由目的地址 */
struct in_addr ipi_addr; /* 头标识目的地址 */
};
ipi_ifindex指的是接收包的接口的唯一索引,ipi_spec_dst指的是路由表记录中的目的地址,而ipi_addr 指的是包头中的目的地址。如果给 setsockopt传递了IP_PKTINFO,那么外发的包会通过在ipi_ifindex中指定的接口发送出去,同时把ipi_spec_dst设置为目的地址。
(二)下面的例子简单地说明如何获取UDP包中的源地址(interface addresses)、目标地址(destination addresses)。为了代码的简单,下面代码段省去了错误检查。
// sock 使用AF_INET协议族, socket类型SOCK_DGRAM
setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt));
// 这里,控制数据是脏数据。
char cmbuf[0x100];
// 目标IP地址
struct sockaddr_in peeraddr;
//如果你想要获取UDP包中的数据,那么还需要为msg_iovec字段初始化
struct msghdr mh = {
.msg_name = &peeraddr,
.msg_namelen = sizeof(peeraddr),
.msg_control = cmbuf,
.msg_controllen = sizeof(cmbuf),
};
recvmsg(sock, &mh, 0);
struct cmsghdr *cmsg ;
for ( // 遍历所有的控制头(the control headers)
cmsg = CMSG_FIRSTHDR(&mh);
cmsg != NULL;
cmsg = CMSG_NXTHDR(&mh, cmsg))
{
// 忽略我们不需要的控制头(the control headers)
if (cmsg->cmsg_level != IPPROTO_IP ||
cmsg->cmsg_type != IP_PKTINFO)
{
continue;
}
struct in_pktinfo *pi = CMSG_DATA(cmsg);
// 在这里, peeraddr是本机的地址(the source sockaddr)
// pi->ipi_spec_dst 是UDP包中路由目的地址(the destination in_addr)
// pi->ipi_addr 是UDP包中的头标识目的地址(the receiving interface in_addr)
}
(三)下面我将给出一个完整可运行的例子,这个例子实现了接收UDP广播包,发送UDP广播包,并在接收的时候,打印出UDP包的路由目的IP地址和头标识目的地址。
#include
#include
#include
#include
#include
#define BUFLEN 255
int main ( int argc, char **argv )
{
struct sockaddr_in peeraddr, localaddr;
int sockfd;
int socklen, n;
//(1)创建UDP数据报socket描述符
sockfd = socket ( AF_INET, SOCK_DGRAM, 0 );
if ( sockfdcmsg_level != IPPROTO_IP ||
cmsg->cmsg_type != IP_PKTINFO )
{
continue;
}
struct in_pktinfo *pi = CMSG_DATA ( cmsg );
//(10)将地址信息转换后输出
char dst[100],ipi[100];//用来保存转化后的源IP地址,目标主机地址 // pi->ipi_spec_dst 是UDP包中的路由目的IP地址(the destination in_addr)
// pi->ipi_addr 是UDP包中的头标识目的地址(the receiving interface in_addr)
if ( ( inet_ntop ( AF_INET,& ( pi->ipi_spec_dst ),dst,sizeof ( dst )
) ) !=NULL )
{
printf ( "路由目的IP地址IPdst=%s\n",dst);
}
if ( ( inet_ntop ( AF_INET,& ( pi->ipi_addr ),ipi,sizeof ( ipi )
) ) !=NULL )
{
printf ("头标识目的地址ipi_addr=%s\n",ipi);
}
}
printf ( "Send Some Message To Server\n" );
if ( sendto ( sockfd, "Hello", strlen ( buffer ), 0, &peeraddr, socklen)
|