linux 服务 ddns,DDNS 的工作原理及其在 Linux 上的实现

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

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

内核空间 rtnetlink 的初始化

清单 1. rtnetlink 的初始化

/*

以下代码摘自 Linux kernel 2.6.18, net/core/rtnetlink.c 文件,

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

*/

void __init rtnetlink_init(void)

{

......

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

if (rtnl == NULL)

panic("rtnetlink_init: cannot initialize rtnetlink\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 调用树

377ddb663e73c8da53edd131758c71b0.gif

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

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

/*

以下代码摘自 Linux kernel 2.6.18, net/ipv4/devinet.c 文件

*/

static void rtmsg_ifa(int event, struct in_ifaddr* ifa)

{

int size = NLMSG_SPACE(sizeof(struct ifaddrmsg) + 128);

struct sk_buff *skb = alloc_skb(size, GFP_KERNEL);

if (!skb)

netlink_set_err(rtnl, 0, RTNLGRP_IPV4_IFADDR, ENOBUFS);

else if (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 的实现主要包括:

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

随后,调用 inet_fill_ifaddr 将消息填充至上述缓存(有关消息的格式,您可以自行查看参考资源)。值得注意的是,RTM_NEWADDR 被作为 nlmsg_type 封装到了内核发送给应用程序的 netlink 消息头 nlmsghdr 中,这样用户空间的应用程序在接收后就能够根据 type 来分别处理不同类型的消息了。

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

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

/*

以下代码摘自 Linux kernel 2.6.18, include/linux/rtnetlink.h 文件

*/

/* RTnetlink multicast groups */

enum rtnetlink_groups {

RTNLGRP_NONE,

#define RTNLGRP_NONE RTNLGRP_NONE

RTNLGRP_LINK,

#define RTNLGRP_LINK RTNLGRP_LINK

.....

RTNLGRP_IPV4_IFADDR,

#define RTNLGRP_IPV4_IFADDR RTNLGRP_IPV4_IFADDR

......

};

#ifndef __KERNEL__

/* RTnetlink multicast groups - backwards compatibility for userspace */

#define RTMGRP_LINK 1

#define RTMGRP_NOTIFY 2

......

#define RTMGRP_IPV4_IFADDR 0x10

......

#endif

综上所述,当主机的 IP 地址发生变化时,内核会向所有 RTNLGRP_IPV4_IFADDR 组播成员发送 RTM_NEWADDR 消息。因此,在用户空间创建 netlink 套接字时,只需要加入到 RTMGRP_IPV4_IFADDR 这个组播 group 中,就可以实现当本机 IP 地址有更新的时候,DDNS 应用程序能够异步地收到内核空间发来的通知消息了。0b1331709591d260c1c78e86d0c51c18.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值