日期:2014-05-16  浏览次数:20572 次

Linux内核中netlink协议族的实现(上)
本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn
1. 前言

netlink协议族是Linux内核网络部分的一个固定部分, 一旦在内核配置中选了网络支持就自动带了而不能单独去掉。
netlink的实现源码在net/netlink目录下,主要是net/netlink/af_netlink.c文件。

以下内核代码版本为2.6.19.2, 如无特别说明代码取自net/netlink/af_netlink.c。

2. 数据结构

netlink套接口结构:
/* net/netlink/af_netlink.c */
struct netlink_sock {
 /* struct sock has to be the first member of netlink_sock */
 struct sock  sk;
 u32   pid; // 自己的pid, 通常是0
 u32   dst_pid; // 对方的pid
 u32   dst_group; // 对方的组
 u32   flags;
 u32   subscriptions;
 u32   ngroups; // 多播组数量
 unsigned long  *groups; // 多播组号
 unsigned long  state;
 wait_queue_head_t wait; // 等待队列,用于处理接收发送包时的top half
 struct netlink_callback *cb;  // 回调结构,包含回调函数
 spinlock_t  cb_lock;
 void   (*data_ready)(struct sock *sk, int bytes); // 数据到达时
                                //的操作, netlink可有不同类型, 如ROUTE, FIREWALL, ARPD等,                                  //每种类型都自己定义的data_ready处理
 struct module  *module;
};
这个结构先是包含一个标准的struct sock结构,后面又跟和netlink相关的特有相关数据,内核中其他协议的sock也是类似定义的, 注意sock结构必须放在第一位,这是为了可以直接将sock的指针转为netlink_sock的指针。
 
netlink sock的表:
struct netlink_table {
 struct nl_pid_hash hash; // 根据pid进行HASH的netlink sock链表, 相当于客户端链表
 struct hlist_head mc_list; // 多播的sock链表
 unsigned long *listeners;  // 监听者标志
 unsigned int nl_nonroot;
 unsigned int groups; // 每个netlink的协议类型可以定义多个组, 8的倍数,最小是32
 struct module *module;
 int registered;
};
最大可有MAX_LINKS(32)个表,处理不同协议类型的netlink套接口, 注意由于是自身的通信, 本机同时作为服务器和客户端, 服务端需要一个套接口对应, 每个客户端也要有一个套接口对应, 多个客户端的套接口形成一个链表.
struct nl_pid_hash {
 struct hlist_head *table; // 链表节点
 unsigned long rehash_time; // 重新计算HASH的时间间隔
 unsigned int mask;
 unsigned int shift;
 unsigned int entries;  // 链表节点数
 unsigned int max_shift; // 最大幂值
 u32 rnd; // 随机数
};
其他和netlink数据相关的数据结构在include/linux/netlink.h中定义, 不过这些结构更多用在各具体的netlink对象的实现中, 在基本netlink套接口中到是用得不多。

3. af_netlink协议初始化

static int __init netlink_proto_init(void)
{
 struct sk_buff *dummy_skb;
 int i;
 unsigned long max;
 unsigned int order;
// 登记netlink_proto结构, 该结构定义如下:
// static struct proto netlink_proto = {
//  .name   = "NETLINK",
//  .owner   = THIS_MODULE,
//  .obj_size = sizeof(struct netlink_sock),
// };
// 最后一个参数为0, 表示不进行slab的分配, 只是简单的将netlink_proto结构
// 挂接到系统的网络协议链表中,这个结构最主要是告知了netlink sock结构的大小
 int err = proto_register(&netlink_proto, 0);
 if (err != 0)
  goto out;
 BUILD_BUG_ON(sizeof(struct netlink_skb_parms) > sizeof(dummy_skb->cb));
// 分配MAX_LINKS个netlink表结构
 nl_table = kcalloc(MAX_LINKS, sizeof(*nl_table), GFP_KERNEL);
 if (!nl_table)
  goto panic;
// 以下根据系统内存大小计算最大链表元素个数
// PAGE_SHIFT是每页大小的2的幂,对i386是12,即每页是4K,2^12
// 对于128M内存的机器,max计算值是(128*1024) >> (21-12) = 256
// 对于64M内存的机器,max计算值是(64*1024) >> (23-12) = 32
 if (num_physpages >= (128 * 1024))
  max = num_physpages >> (21 - PAGE_SHIFT);
 else
  max = num_physpages >> (23 - PAGE_SHIFT);
// 根据max再和PAGE_SHIFT计算总内存空间相应的幂值order
 order = get_bitmask_order(max) - 1 + PAGE_SHIFT;
// max是最大节点数
 max = (1UL << order) / sizeof(struct hlist_head);
// order是max对于2的幂数
 order = get_bitmask_order(max > UINT_MAX ? UINT_MAX : max) - 1;
 for (i = 0; i < MAX_LINKS; i++) {
  struct nl_pid_hash *hash = &nl_table[i].hash;
// 为netlink的每个协议类型分配HASH表链表头
  hash->table = nl_pid_hash_alloc(1 * sizeof(*hash->table));
  if (!hash->table) {
   while (i-- > 0)
    nl_pid_hash_free(nl_table[i].hash.table,
       1 * sizeof(*hash->table));
   kfree(nl_table);
   goto panic;
  }
// 初始化HASH表参数
  memset(hash->table, 0, 1 * sizeof(*hash->table));
// 最大幂数
  hash->max_shift = order;
  hash->shift = 0;
  hash->mask = 0;
  hash->rehash_time = jiffies;
 }
// 登记netlink协议族的的操作结构
 sock_register(&netlink_family_ops);
#ifdef CONFIG_PROC_FS
 proc_net_fops_create("netlink", 0, &netlink_seq_fops);
#endif
 /* The netlink device handler may be needed early. */
// 初始化路由netlink
 rtnetlink_init();
ou