DDNS 的工作原理及其在 Linux 上的实现Word文件下载.docx

上传人:b****5 文档编号:21414633 上传时间:2023-01-30 格式:DOCX 页数:14 大小:48.22KB
下载 相关 举报
DDNS 的工作原理及其在 Linux 上的实现Word文件下载.docx_第1页
第1页 / 共14页
DDNS 的工作原理及其在 Linux 上的实现Word文件下载.docx_第2页
第2页 / 共14页
DDNS 的工作原理及其在 Linux 上的实现Word文件下载.docx_第3页
第3页 / 共14页
DDNS 的工作原理及其在 Linux 上的实现Word文件下载.docx_第4页
第4页 / 共14页
DDNS 的工作原理及其在 Linux 上的实现Word文件下载.docx_第5页
第5页 / 共14页
点击查看更多>>
下载资源
资源描述

DDNS 的工作原理及其在 Linux 上的实现Word文件下载.docx

《DDNS 的工作原理及其在 Linux 上的实现Word文件下载.docx》由会员分享,可在线阅读,更多相关《DDNS 的工作原理及其在 Linux 上的实现Word文件下载.docx(14页珍藏版)》请在冰豆网上搜索。

DDNS 的工作原理及其在 Linux 上的实现Word文件下载.docx

DDNS工作流程的简单介绍

结合上述对DDNS工作原理的分析,我们可以将DDNS的工作流程简单地用图1来表示:

图1.DDNS的工作流程图

从图1中可以看到,DDNS的工作流程主要有三个部分:

1.应用程序实时感知到IP地址发生了变化,如上介绍,利用基于netlink的异步通知机制可以让应用程序及时得到内核空间对这些事件的“通知”,具体可以分为如下5个步骤:

o1、内核空间初始化rtnetlink模块,创建NETLINK_ROUTE协议簇类型的netlink套接字;

o2、用户空间创建NETLINK_ROUTE协议簇类型的netlink套接字,并且绑定到RTMGRP_IPV4_IFADDR组播group中;

o3、用户空间接收从内核空间发来的消息,如果没有消息,则阻塞自身;

o4、当主机被分配了新的IPV4地址,内核空间通过netlink_broadcast,将RTM_NEWADDR消息发送到RTNLGRP_IPV4_IFADDR组播group中;

o5、用户空间接收消息,进行验证、处理;

2.应用程序接收到“通知”后,把DNSupdate信息发送给DNS服务器,目的是将更新后的IP地址及时地通知DNS服务器,以便网络上的主机仍然能够通过原来的域名访问到自己,通用的做法是利用开源软件nsupdate发送DNSupdate信息给DNS服务器以实现DNS信息的动态更新。

3.最后,对应于第一部分netlink套接字的创建,用户空间和内核空间关闭所创建的netlink套接字。

下文将详细阐述其中的每一环节及其实现。

内核空间rtnetlink检测IP地址变化的实现与分析

在我们开始利用netlink套接字、实现与内核通信的应用程序之前,先来分析一下内核空间的rtnetlink模块是如何工作的。

内核空间rtnetlink的初始化

清单1.rtnetlink的初始化

/*

以下代码摘自Linuxkernel2.6.18,net/core/rtnetlink.c文件,

并只选择了与本主题相关的最重要的部分,其他的都用省略号略过,之后的各清单也一样。

*/

void__initrtnetlink_init(void)

{

......

rtnl=netlink_kernel_create(NETLINK_ROUTE,RTNLGRP_MAX,rtnetlink_rcv,THIS_MODULE);

if(rtnl==NULL)

panic("

rtnetlink_init:

cannotinitializertnetlink\n"

);

}

从清单1中可以看到:

在rtnetlink进行初始化的时候,首先会调用netlink_kernel_create来创建一个NETLINK_ROUTE类型的netlink套接字,并指定接收函数为rtnetlink_rcv,有关rtnetlink_rcv的实现细节可以查阅内核net/core/rtnetlink.c文件。

这里需要指出的是,netlink提供了包括NETLINK_ROUTE、NETLINK_FIREWALL、NETLINK_INET_DIAG等在内的多种协议簇(详细列表及各协议簇的含义可以自行查看参考资源),其中NETLINK_ROUTE类型提供了网络地址发生变化的消息,这正是DDNS需要用到的。

内核空间IP地址变化事件的通知过程

引起主机IP地址变化的原因有很多种,如:

DHCP分配的IP过期、用户手动修改了IP等等。

无论何种原因,最终都会触发内核空间对相应事件的通知机制,这里以最常用的修改IPV4地址的工具ifconfig为例。

ifconfig先是创建一个AF_INET的socket,然后通过系统调用ioctl来完成配置的,ioctl在内核中对应的函数是sys_ioctl,对于IP地址、子网掩码、默认网关等配置的修改,其最终会调用devinet_ioctl。

devinet_ioctl函数处理包括get、set在内的多种命令,与DDNS应用有关的是set类命令,图2给出了SIOCSIFADDR命令(设置网络地址)的ifconfig调用树:

图2.SIOCSIFADDR命令的ifconfig调用树

从图2中可以看到,当用户使用ifconfig对主机的IP地址作了修改,内核在进行了新地址的设置之后,会调用rtmsg_ifa,传递的事件为RTM_NEWADDR。

清单2.rtmsg_ifa发送IP地址变化消息

以下代码摘自Linuxkernel2.6.18,net/ipv4/devinet.c文件

staticvoidrtmsg_ifa(intevent,structin_ifaddr*ifa)

intsize=NLMSG_SPACE(sizeof(structifaddrmsg)+128);

structsk_buff*skb=alloc_skb(size,GFP_KERNEL);

if(!

skb)

netlink_set_err(rtnl,0,RTNLGRP_IPV4_IFADDR,ENOBUFS);

elseif(inet_fill_ifaddr(skb,ifa,0,0,event,0)<

0){

kfree_skb(skb);

netlink_set_err(rtnl,0,RTNLGRP_IPV4_IFADDR,EINVAL);

}else{

netlink_broadcast(rtnl,skb,0,RTNLGRP_IPV4_IFADDR,GFP_KERNEL);

从清单2中可以看到,rtmsg_ifa的实现主要包括:

1.首先分配了一块类型为structsk_buff的空间用于存放需要发送的消息内容。

2.随后,调用inet_fill_ifaddr将消息填充至上述缓存(有关消息的格式,您可以自行查看参考资源)。

值得注意的是,RTM_NEWADDR被作为nlmsg_type封装到了内核发送给应用程序的netlink消息头nlmsghdr中,这样用户空间的应用程序在接收后就能够根据type来分别处理不同类型的消息了。

3.rtmsg_ifa的最后是调用了netlink_broadcast将上述封装完毕的sk_buff结构广播到RTNLGRP_IPV4_IFADDR这个group,以下是内核空间组播group与用户空间组播group的对应关系:

清单3.内核空间组播group与用户空间组播group的对应关系

以下代码摘自Linuxkernel2.6.18,include/linux/rtnetlink.h文件

/*RTnetlinkmulticastgroups*/

enumrtnetlink_groups{

RTNLGRP_NONE,

#defineRTNLGRP_NONERTNLGRP_NONE

RTNLGRP_LINK,

#defineRTNLGRP_LINKRTNLGRP_LINK

.....

RTNLGRP_IPV4_IFADDR,

#defineRTNLGRP_IPV4_IFADDRRTNLGRP_IPV4_IFADDR

};

#ifndef__KERNEL__

/*RTnetlinkmulticastgroups-backwardscompatibilityforuserspace*/

#defineRTMGRP_LINK1

#defineRTMGRP_NOTIFY2

#defineRTMGRP_IPV4_IFADDR0x10

#endif

综上所述,当主机的IP地址发生变化时,内核会向所有RTNLGRP_IPV4_IFADDR组播成员发送RTM_NEWADDR消息。

因此,在用户空间创建netlink套接字时,只需要加入到RTMGRP_IPV4_IFADDR这个组播group中,就可以实现当本机IP地址有更新的时候,DDNS应用程序能够异步地收到内核空间发来的通知消息了。

###NextPage###

用户空间netlinksocket的创建、绑定与消息接收处理

用户空间创建netlink套接字

用户空间的netlinksocket相关操作与标准socketAPI完全一致,因此可以像使用标准socket来进行两台主机间的IP协议通信一样地来使用它,这也是netlink之所以能够得到越来越广泛应用的一个重要原因。

清单4.用户空间创建netlinksocket

#include

intmain(void)

if((nl_socket=socket(PF_NETLINK,SOCK_DGRAM,NETLINK_ROUTE))==-1)

//指定通信域、通信方式以及通信协议

exit

(1);

在创建netlink套接字时:

我们指定了通信域为PF_NETLINK,表明这是一个netlink套接字。

其定义可以在如下所示的内核include/linux/socket.h文件中找到。

从中我们也可以看到自己非常熟悉的AF_INET:

清单5.清单4中使用到的宏定义

/*以下代码摘自include/linux/socket.h文件*/

/*Supportedaddressfamilies.*/

#defineAF_UNSPEC0

#defineAF_UNIX1/*Unixdomainsockets*/

#defineAF_LOCAL1/*POSIXnameforAF_UNIX*/

#defineAF_INET2/*InternetIPProtocol*/

#defineAF_NETLINK16

/*Protocolfamilies,sameasaddressfamilies.*/

#definePF_NETLINKAF_NETLINK

对于通信方式,我们选择了SOCK_DGRAM。

事实上对于netlink这种基于无连接的socket,使用SOCK_DGRAM或者SOCK_RAW都是可以的。

对于通信协议,我们使用了NETLINK_ROUTE。

这是因为在清单1中,内核空间创建netlink套接字、用于发送IP地址发生变化的消息时使用的是它,所以这里需要保持一致以进行双方间的通信。

用户空间绑定netlink套接字

与标准的socket使用方法相似,在建立netlink套接字之后,也需要绑定到一个netlink地址才能够进行消息的发送与接收。

netlink地址在structsockaddr_nl结构中定义,各结构成员的含义可参见附录3。

清单6.用户空间bindnetlinksocket

structsockaddr_nladdr//在include/linux/netlink.h中定义,结构各成员的含义可参见附录3

memset(&

addr,0,sizeof(addr));

addr.nl_family=PF_NETLINK;

//定义协议簇为PF_NETLINK

addr.nl_groups=RTMGRP_IPV4_IFADDR//加入到RTMGRP_IPV4_IFADDR组播group中

addr.nl_pid=0;

//让kernel来分配pid

//将清单5中创建的netlink套接字与上述协议地址进行绑定

if(bind(nl_socket,(structsockaddr*)&

addr,sizeof(addr))==-1)

close(nl_socket);

从清单6中可以看到,在绑定应用程序的netlink套接字时,我们将自己加入到了RTMGRP_IPV4_IFADDR组播group中,这与前文我们对内核空间IP地址变化事件的通知过程的分析是一致的。

用户空间接收并处理内核空间消息

同样与标准的socket使用方法类似,用户空间接收内核空间发来的netlink消息可以使用recv、recvfrom或recvmsg。

值得一提的是,netlink套接字有自己的消息头:

nlmsghdr结构(该结构具体各成员变量的含义请查看参考资源),而其中的nlmsg_type正是我们需要用到的包含了消息类型的字段。

清单7.用户空间接收内核空间消息

#defineMAX_MSG_SIZE1024

structif_info

intindex;

//interface的序号

charname[IFNAMSIZ];

//interface的名称,Linux内核include/linux/if.h中定义了IFNAMSIZ

uint8_tmac[ETH_ALEN];

//interface的mac地址,Linux内核include/linux/if_ether.h中定义了ETH_ALEN

......//interface的其他信息

structif_info*next;

//指向下一个if_info结构的指针

staticstructif_info*if_list=NULL;

//存放现有的interface列表,在每次程序初始化时更新

intreceive_netlink_message(structnlmsghdr*nl);

//用于接收内核空间发来的消息的函数

handle_newaddr(structifinfomsg*ifi,intlen);

//用于处理向DNS服务器发送更新的函数

intlen=0;

structnlmsghdr*nl;

//结构体定义可以参考内核include/linux/netlink.h文件

while((len=receive_netlink_message(&

nl))>

0)

while(NLMSG_OK(nl,len))//NLMSG相关的宏定义可以参考内核include/linux/netlink.h文件

switch(nl->

nlmsg_type)

caseRTM_NEWADDR:

//处理RTM_NEWADDR的netlink消息类型

//ifinfomsg结构可以参考内核include/linux/rtnetlink.h文件

handle_newaddr((structifinfomsg*)NLMSG_DATA(nl),

NLMSG_PAYLOAD(nl,sizeof(structifinfomsg)));

break;

......//处理其他netlink消息类型,如:

RTM_NEWLINK,这里略过

default:

printf("

Unknownnetlinkmessagetype:

%d"

nl->

nlmsg_type);

nl=NLMSG_NEXT(nl,len);

if(nl!

=NULL)

free(nl);

}

intreceive_netlink_message(structnlmsghdr**nl)

structioveciov;

//使用iovec进行接收

structmsghdrmsg={NULL,0,&

iov,1,NULL,0,0};

//初始化msghdr

intlength;

*nl=NULL;

if((*nl=(structnlmsghdr*)malloc(MAX_MSG_SIZE))==NULL)

return0;

iov.iov_base=*nl;

//封装nlmsghdr

iov.iov_len=MAX_MSG_SIZE;

//指定长度

length=recvmsg(nl_socket,&

msg,0);

if(length

应用程序在收到了RTM_NEWADDR类型的netlink消息后,需要根据IP的变化进行处理。

这里使用了handle_newaddr函数,对IP的变化分为了两种情况:

一种是interface已经存在、仅仅是IP发生了变化;

另一种是interface是新添加的。

无论是哪种情况,handle_newaddr函数在进行了相应的处理之后,都需要调用update_dns.sh这个脚本通知DNS服务器。

关于update_dns.sh的实现参见下一章。

清单8.用户空间处理内核空间消息

voidhandle_newaddr(structifinfomsg*ifinfo,intlen)

structif_info*i;

for(i=if_list;

i;

i=i->

next)//遍历in_list,找到ip发生变化的interface

if(i->

index==ifinfo->

ifi_index)

if(i!

=NULL){//找到了相应的interface,执行update_dns.sh

system(update_dns.sh);

return;

//没有找到对应的interface,说明该interface是新添加的

if((i=calloc(sizeof(structif_info),1))==NULL)//分配一个if_info结构用于添加新的interface

//根据ifinfo->

ifi_index等信息更新if_info结构i,考虑到与ddns应用关系不大,限于篇幅,这里略过

//执行update_dns.sh

i->

next=if_list;

//在if_list的末尾添加新发现的interface

if_list=i;

应用程序与DNS服务器的交互

应用程序可以利用开源工具nsupdate来向DNS服务器发送DNSupdate消息。

nsupdate的详细用法及特性可以请查看参考资源,受篇幅所限,本章将会结合例子简单介绍这个工具的基本用法。

nsupdate可以从终端或文件中读取命令,每个命令一行。

一个空行或一个"

send"

命令,则会将先前输入的命

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 法学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1