netlink学习笔记(一)

最近在学习netlink相关的东西,现在还没找到特别好的书,学习的资料主要来自网络,今天主要参考以下三个:

http://linux.chinaunix.net/bbs/thread-1031932-1-1.html

http://prolqspace.spaces.live.com/blog/cns!7240DE3B63F5B841!939.entry

http://enchen.blog.51cto.com/716040/158058


第一篇:netlink socket编程之why &how是对国外一篇英文文档的翻译,论述比较全面而且系统,但是基于的内核版本比较早,里面的源程序编译会有错误


第二篇:如何在2.6.24及以后版本的Linux内核中使用netlink,针对新内核的变化,写了一个测试代码,编译可通过,但是rmmod时会有错误:

以下是原文:

最近做Netlink的实验,网上相关的教程也不少,有些写得还十分系统,不过我拿来一测试,居然没一个能编译。后来仔细一看才发现,还是内核版本的问题。经过一番折腾,终于弄好了,下面简要介绍一下经验。

我用的ubuntu 8.04,内核更新至2.6.24-23,而从2.6.24开始,linux内部对netlink的实现机制和调用接口进行了很大的调整,特别是函数 netlink_kernel_create(),最新的参数有6个之多。而网上大多数的教程和例子程序都是针对2.6.12以前版本的内核,甚至2.4 版来说的,因此连最基本的函数都无法成功编译,更不用说后面的了。

事实上,新版的Netlink简化了使用逻辑。当然我这里说的主要是内核空间编程上,对用户空间程序,netlink仍然像经典的socket程序那样简单易用。我没有去仔细研究其他细节的函数,其实其他变化影响不大,这里仅对最核心的,变化最重要的create函数介绍一下,以下是函数原型:

extern struct sock *netlink_kernel_create(struct net *net,
                          int unit,unsigned int groups,
                          void (*input)(struct sk_buff *skb),
                          struct mutex *cb_mutex,
                          struct module *module);

使用方法如下(nl_sk是一个sock指针):

nl_sk = netlink_kernel_create(&init_net, NETLINK_TEST, 0, nl_data_ready, NULL, THIS_MODULE);

第一个参数是最新引入的。具体是什么功能我未能弄清楚,但是我找到了使用它的方法:就是用&init_net。这个init_net结构体是在 linux/net/core/net_namespace.c 中定义的,此外在linux/include/net/net_namespace.h中也有外部定义,直接作为参数使用即可。第二个是以前版本也有的参数,即netlink类型。系统定义了16个,可以在net/netlink.h中找到。为了不和系统已有定义冲突(可能有些都被占用了),我们这里自己定义了一个值:#define NETLINK_TEST 22。后面的参数中,nl_data_ready比较重要,它有点类似于以前的初始化接收函数,但现在我们不需要自己调用调度的方法,让内核进程阻塞以便等待消息到来了。只要声明一个nl_data_ready函数,这里将其地址赋给此参数,netlink内部会自动处理消息到来事件,一旦收到来自用户空间的匹配消息,将自动调用nl_data_ready函数进行处理。因此这实际上是一大简化。最后一个参数THIS_MODULE是module.h中定义的,表示当前模块。这也是最新引入的参数,应该是为了配合自动调用数据接收函数nl_data_ready而加入的,毕竟,netlink需要知道这个接受函数到底是位于那个模块之中的,否则它怎么调用呢,呵呵。

好了,其他的大家直接看代码吧。我写的这个示例程序的功能是一个用户空间程序nltimer_u和内核模块nltimer_k进行交互。先启动内核模块,在启动用户程序,后者会发送一条Hello消息给内核模块。 nltimer_k收到后,启动定时器,开始每1秒发送一条消息给用户程序,用户收到后加一个echo:前缀,再回复给内核。循环规定时间后,发送 EXIT消息,双方收到后结束操作,用户进程退出。内核模块则准备接受下一次数据到来。不过此后都不会再进入定时器循环,而是只接收第一条hello消息,然后就发送EXIT。这个功能几乎涵盖了内核定时器和内核模块编程,以及新版netlink的大多数常用技巧,大家不妨一试。

环境是:ubuntu 8.04,linux kernel 2.6.24-23。开发环境配置完整,GCC为4.2版。



[root@macross nltimer]# rmmod nltimer_k.ko
段错误
[root@macross nltimer]#
Message from syslogd@localhost at Aug 27 16:24:02 ...
 kernel:------------[ cut here ]------------

Message from syslogd@localhost at Aug 27 16:24:02 ...
 kernel:invalid opcode: 0000 [#3] SMP

Message from syslogd@localhost at Aug 27 16:24:02 ...
 kernel:last sysfs file: /sys/devices/pci0000:00/0000:00:1e.0/0000:05:04.0/local_cpus

Message from syslogd@localhost at Aug 27 16:24:02 ...
 kernel:Process rmmod (pid: 20184, ti=e36ee000 task=e3719980 task.ti=e36ee000)

Message from syslogd@localhost at Aug 27 16:24:02 ...
 kernel:Stack:

Message from syslogd@localhost at Aug 27 16:24:02 ...
 kernel: c044dee9 69746c6e 5f72656d e36e006b c04806ed e366b3c8 e364e400 e36eefa0

Message from syslogd@localhost at Aug 27 16:24:02 ...
 kernel: e35f6b60 e35f6800 e36eef88 c049a27e 00000000 00000000 e35f6800 006eefb4

Message from syslogd@localhost at Aug 27 16:24:02 ...
 kernel:Call Trace:

Message from syslogd@localhost at Aug 27 16:24:02 ...
 kernel: [<c042eaf2>] ? put_online_cpus+0x42/0x44

Message from syslogd@localhost at Aug 27 16:24:02 ...
 kernel: [<f7f5a01f>] ? nltimer_exit+0xd/0x6a [nltimer_k]

Message from syslogd@localhost at Aug 27 16:24:02 ...
 kernel: [<c044dee9>] ? sys_delete_module+0x1ae/0x1f8

Message from syslogd@localhost at Aug 27 16:24:02 ...
 kernel: [<c04806ed>] ? remove_vma+0x55/0x5b

Message from syslogd@localhost at Aug 27 16:24:02 ...
 kernel: [<c049a27e>] ? path_put+0x15/0x18

Message from syslogd@localhost at Aug 27 16:24:02 ...
 kernel: [<c04039bb>] ? sysenter_do_call+0x12/0x34

Message from syslogd@localhost at Aug 27 16:24:02 ...
 kernel:Code: 83 ec 08 83 f8 10 0f 86 bd 00 00 00 e8 05 dd ff ff 89 c2 89 c3 b8 07 00 00 00 e8 dc dc ff ff 85 c0 75 18 f7 03 00 60 00 00 75 04 <0f> 0b eb fe 89 d8 e8 42 62 fe ff e9 8e 00 00 00 8b 45 04 89 45

Message from syslogd@localhost at Aug 27 16:24:02 ...
 kernel:EIP: [<c04907a0>] kfree+0x33/0xd9 SS:ESP 0068:e36eef34



第三篇:讲的是netlink和ip_queue的区别,把其中和netlink相关的部分分出来如下,其中net/netlink/af_netlink.c目录和代码分析那部分比较有用。


1. 前言

netlink是linux中实现内核与用户空间通信的一种方法,数据以类似网络数据包的形式在两者间传输,
这和以前所介绍的/proc,ioctl和setsockopt方式是不同的,另外一个区别是以前这几种方法都是用户
空间程序主动向内核发出请求,相当于客户端,内核相当于一个服务器;而netlink方法则是内核和用
户空间都可以主动向对方发送数据。
netlink现在已经是Linux内核中网络部分的一个基本协议族,具体代码在net/netlink中定义。
最初netlink接口主要是提供给路由程序来和内核通信修改系统的路由表的,从2.2内核开始在防火墙模
块中也有了netlink的支持。在2.4内核中,netlink支持是在ip_queue中实现的,另一种实现方式是
nfnetlink的,但只在POM中有补丁,没加入官方内核中;到了2.6,nfnetlink纳入官方内核中,并成为
推荐的防火墙模块的netlink支持,ip_queue虽然还在内核代码中,但已经不推荐使用(obsolete)。

通过netlink接口,netfilter可以向用户空间发送网络数据包,防火墙日志信息等,并能进行netlink
的连接跟踪,相关代码在net/netfilter/nfnetlink.c, net/netfilter/nfnetlink_conntrack.c,
net/netfilter/nfnetlink_queue.c, net/netfilter/nfnetlink_log.c等文件中。

以下内核代码版本为2.6.19.2。

2. netfilter中的netlink初始化

2.1 nfnetlink初始化
/* net/netfilter/nfnetlink.c */

static int __init nfnetlink_init(void)
{
 printk("Netfilter messages via NETLINK v%s./n", nfversion);
// 建立NETFILTER的NETLINK接口,组号是NFNLGRP_MAX
// 接收用户空间数据包函数是nfnetlink_rcv
 nfnl = netlink_kernel_create(NETLINK_NETFILTER, NFNLGRP_MAX,
                              nfnetlink_rcv, THIS_MODULE);
 if (!nfnl) {
  printk(KERN_ERR "cannot initialize nfnetlink!/n");
  return -1;
 }
 return 0;
}

2.3 nfnetlink_queue的初始化

/* net/netfilter/nfnetlink_queue.c */
static int __init nfnetlink_queue_init(void)
{
 int i, status = -ENOMEM;
#ifdef CONFIG_PROC_FS
 struct proc_dir_entry *proc_nfqueue;
#endif
 
 for (i = 0; i < INSTANCE_BUCKETS; i++)
  INIT_HLIST_HEAD(&instance_table[i]);
// 登记nfqueue的netlink套接口的通知
 netlink_register_notifier(&nfqnl_rtnl_notifier);
// 登记nfnetlink的nfqueue子系统
 status = nfnetlink_subsys_register(&nfqnl_subsys);
 if (status < 0) {
  printk(KERN_ERR "nf_queue: failed to create netlink socket/n");
  goto cleanup_netlink_notifier;
 }
......
}
 
2.3 ip_queue的初始化

/* net/ipv4/netfilter/ip_queue.c */
static int __init ip_queue_init(void)
{
 int status = -ENOMEM;
 struct proc_dir_entry *proc;
 
 netlink_register_notifier(&ipq_nl_notifier);
// 建立FIREWALL类型的NETLINK接口,组号是0
// 接收用户空间数据包函数是ipq_rcv_sk
 ipqnl = netlink_kernel_create(NETLINK_FIREWALL, 0, ipq_rcv_sk,
          THIS_MODULE);
 if (ipqnl == NULL) {
  printk(KERN_ERR "ip_queue: failed to create netlink socket/n");
  goto cleanup_netlink_notifier;
 }
......
}

2.4 小结
由此可见,nfnetlink和ip_queue两者都是通过调用 netlink_kernel_create函数来初始化netlink套接口的,ip_queue功能比较单纯,就是传输网络数据包,而nfnetlink有所扩展,不光是传递数据包,还
可传递其他数据,如日志信息的,不同类型数据处理是通过netlink子系统来区分的,不仅包括了
ip_queue的功能,还进行了扩展,这可能就是ip_queue被废的原因。

3. netlink_kernel_create
/* net/netlink/af_netlink.c */
/*
 * We export these functions to other modules. They provide a
 * complete set of kernel non-blocking support for message
 * queueing.
 */
struct sock *
netlink_kernel_create(int unit, unsigned int groups,
// unit即netlink接口类型,有ROUTE,FIREWALL,IP6_FW,XFRM等
// 最大值为MAX_LINKS
// groups为具体各种类型netlink接口中的组号
                      void (*input)(struct sock *sk, int len),
                      struct module *module)
{
 struct socket *sock;
 struct sock *sk;
 struct netlink_sock *nlk;
 unsigned long *listeners = NULL;
 BUG_ON(!nl_table);
// unit范围检查
 if (unit<0 || unit>=MAX_LINKS)
  return NULL;
// 建立netlink的socket
 if (sock_create_lite(PF_NETLINK, SOCK_DGRAM, unit, &sock))
  return NULL;
// 建立unit类型的netlink的sock
 if (__netlink_create(sock, unit) < 0)
  goto out_sock_release;
// 小于32的组号都设置为32
// ip_queue中该参数为0, nfnetlink中该参数为NFNLGRP_MAX,都不超过32
// 所以实际两者等价
 if (groups < 32)
  groups = 32;
// 监听者
 listeners = kzalloc(NLGRPSZ(groups), GFP_KERNEL);
 if (!listeners)
  goto out_sock_release;
 sk = sock->sk;
 sk->sk_data_ready = netlink_data_ready;
// netlink接口输入函数,也就是处理用户空间发送到内核方向的数据
 if (input)
  nlk_sk(sk)->data_ready = input;
// 将该sock插入HASH表
 if (netlink_insert(sk, 0))
  goto out_sock_release;
// 设置该netlink sock的一些基本参数
 nlk = nlk_sk(sk);
 nlk->flags |= NETLINK_KERNEL_SOCKET;
 netlink_table_grab();
 nl_table[unit].groups = groups;
 nl_table[unit].listeners = listeners;
 nl_table[unit].module = module;
 nl_table[unit].registered = 1;
 netlink_table_ungrab();
 return sk;
out_sock_release:
 kfree(listeners);
 sock_release(sock);
 return NULL;
}

4. 结论

nfnetlink使用NETFILTER类型的netlink套接口,ip_queue都使用FIREWALL类型的netlink 接口,理论上是可以区分的。但因为nfnetlink已经包含了ip_queue的功能并进行了扩展,使用推荐只使用nfnetlink即可。



阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页