3.4 ICMPv6 初始化
1. ICMPv6 简述
ICMPv6 除了跟 ICMPv4 一样负责错误处理和诊断之外,还负责邻居发现( Neighbour Discovery ND )
和组播侦听者发现(Multicast Listener Discover MLD)
。
- 邻居发现(ND) -- ARP(IPV4)
- 组播侦听者发现(MLD) --IGMP(IPV4)
但是这两个功能会放在后面去解析,本章节还是学习 ICMPv6 和 ICMPv4 相同的部分。另外同ICMPv4 一样,不能将ICMPv6 构建成内核模块。原因见 ICMPv4 附录。
2. ICMPv6 初始化
首先就是 ICMPv6 在网络协议栈中的初始化。ICMPv6 同 ICMPv4 一样,附属于IP层三层网络协议,所以和TCPv6、UDPv6一样,在inet6_init()
中完成初始化。
static int __init inet6_init(void)
{
...
err = icmpv6_init();
if (err)
goto icmp_fail;
...
}
在inet6_init()
中,调用 icmpv6_init()
函数对ICMPv6 进行初始化。跟 ICMPv4 初始化有一点点的区别是,在 ICMPv4中,协议注册和内核初始化是分开的,而 ICMPv6 中,它们都在 icmpv6_init() 中进行处理。怎么说呢,毕竟是后者,进行代码整理和优化也是应该的。ICMPv6 很多地方都体现了编码优化的身影。
int __init icmpv6_init(void)
{
int err;
err = register_pernet_subsys(&icmpv6_sk_ops);
if (err < 0)
return err;
err = -EAGAIN;
if (inet6_add_protocol(&icmpv6_protocol, IPPROTO_ICMPV6) < 0)
goto fail;
err = inet6_register_icmp_sender(icmp6_send);
if (err)
goto sender_reg_err;
return 0;
...
}
2.1 ICMPv6 内核模块初始化
在 ICMPv6 中,首先执行的是 ICMPv6 的内核套接字创建与初始化。
static struct pernet_operations icmpv6_sk_ops = {
.init = icmpv6_sk_init,
.exit = icmpv6_sk_exit,
};
int __init icmpv6_init(void)
{
err = register_pernet_subsys(&icmpv6_sk_ops);
if (err < 0)
return err;
...
}
最终ICMPv6 内核模块初始化函数注册在了icmpv6_sk_init()
上。这个函数主要完成如下功能:
-
为每个CPU创建 ICMPv6 原始套接字,并储存在数组中。
static int __net_init icmpv6_sk_init(struct net *net) { struct sock *sk; int err, i, j; // 申请足够空间 net->ipv6.icmp_sk = kcalloc(nr_cpu_ids, sizeof(struct sock *), GFP_KERNEL); if (!net->ipv6.icmp_sk) return -ENOMEM; // 遍历每个CPU,为每个CPU创建ICMPv6原始套接字 for_each_possible_cpu (i) { err = inet_ctl_sock_create(&sk, PF_INET6, SOCK_RAW, IPPROTO_ICMPV6, net); if (err < 0) { pr_err("Failed to initialize the ICMP6 control socket (err %d)\n", err); goto fail; } // 将套接字存放在ICMPv6 原始套接字的数组中 net->ipv6.icmp_sk[i] = sk; /* Enough space for 2 64K ICMP packets, including * sk_buff struct overhead. */ sk->sk_sndbuf = 2 * SKB_TRUESIZE(64 * 1024); } return 0; fail: // 创建失败则销毁所有ICMPv6 原始套接字,并释放数组空间 for (j = 0; j < i; j++) inet_ctl_sock_destroy(net->ipv6.icmp_sk[j]); kfree(net->ipv6.icmp_sk); return err; }
和ICMPv4 不同的是,ICMPv6 似乎并没有一些协议上参数的限制。比如限速等。
2.2 ICMPv6 收包处理函数初始化
ICMPv6 协议结构体:
struct inet6_protocol icmpv6_protocol = {
.handler = icmpv6_rcv,
.err_handler = icmpv6_err,
.flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
};
int __init icmpv6_init(void)
{
...
err = -EAGAIN;
if (inet6_add_protocol(&icmpv6_protocol, IPPROTO_ICMPV6) < 0)
goto fail;
...
}
icmpv6_rcv
icmpv6 的数据包处理函数,在处理IP报文时,协议字段为IPPROTO_ICMPV6(58)
时,就是用icmpv6_rcv()
来处理。INET6_PROTO_NOPOLICY
标志着不使用ipsec进行安全策略检查。INET6_PROTO_FINAL
标志着该协议在这一层是最终的协议封装,不会再有其他协议。以避免意外的协议嵌套。
2.3 注册 ICMPv6 数据包发送函数
static ip6_icmp_send_t __rcu *ip6_icmp_send;
int inet6_register_icmp_sender(ip6_icmp_send_t *fn)
{
return (cmpxchg((ip6_icmp_send_t **)&ip6_icmp_send, NULL, fn) == NULL) ?
0 : -EBUSY;
}
int __init icmpv6_init(void)
{
...
err = inet6_register_icmp_sender(icmp6_send);
...
}
这个处理比较简单,仅仅是为 ICMPv6 的发送报文函数指针ip6_icmp_send
注册发送函数为 icmp6_send()
, 发送函数的实现将在后面展开。
初始化部分完成。下一节将介绍ICMPv6 报文格式和报文类型。
标签:初始化,err,icmpv6,init,sk,ICMPv6,3.4,icmp From: https://www.cnblogs.com/kmist/p/18116013