Linux内核中netlink协议族的实现(上)

本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: [email protected]
来源: 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();
out:
 return err;
panic:
 panic("netlink_init: Cannot allocate nl_table/n");
}
core_initcall(netlink_proto_init);
 

4. 建立netlink套接口

4.1  建立对应客户端的套接口
// netlink协议族操作, 在用户程序使用socket打开netlink类型的socket时调用,
// 相应的create函数在__sock_create(net/socket.c)函数中调用:
static struct net_proto_family netlink_family_ops = {
 .family = PF_NETLINK,
 .create = netlink_create,
 .owner = THIS_MODULE, /* for consistency 8) */
};
// 在用户空间每次打开netlink socket时都会调用此函数:
static int netlink_create(struct socket *sock, int protocol)
{
 struct module *module = NULL;
 struct netlink_sock *nlk;
 unsigned int groups;
 int err = 0;
// sock状态初始化
 sock->state = SS_UNCONNECTED;
// 对netlink sock的类型和协议(实际是netlink_family类型)限制检查
 if (sock->type != SOCK_RAW && sock->type != SOCK_DGRAM)
  return -ESOCKTNOSUPPORT;
 if (protocol<0 || protocol >= MAX_LINKS)
  return -EPROTONOSUPPORT;
 netlink_lock_table();
#ifdef CONFIG_KMOD
// 如果相应的netlink协议是模块又没有加载的话先加载该模块
 if (!nl_table[protocol].registered) {
  netlink_unlock_table();
  request_module("net-pf-%d-proto-%d", PF_NETLINK, protocol);
  netlink_lock_table();
 }
#endif
 if (nl_table[protocol].registered &&
     try_module_get(nl_table[protocol].module))
  module = nl_table[protocol].module;
// groups这个值在函数后面也没见用上, 这句没意义
 groups = nl_table[protocol].groups;
 netlink_unlock_table();
// 真正的建立netlink sock的函数
 if ((err = __netlink_create(sock, protocol)) < 0)
  goto out_module;
 nlk = nlk_sk(sock->sk);
 nlk->module = module;
out:
 return err;
out_module:
 module_put(module);
 goto out;
}

// 基本函数
static int __netlink_create(struct socket *sock, int protocol)
{
 struct sock *sk;
 struct netlink_sock *nlk;
// netlink sock的基本操作
 sock->ops = &netlink_ops;
// 分配sock结构, 通过netlink_proto中的obj_size指出了netlink sock的大小
 sk = sk_alloc(PF_NETLINK, GFP_KERNEL, &netlink_proto, 1);
 if (!sk)
  return -ENOMEM;
// 初始化sock基本数据, 将sock和socket关联起来
 sock_init_data(sock, sk);
// 将普通sock转为netlink sock,实际只是重新定义的一下指针类型,指针本身值不变
 nlk = nlk_sk(sk);
// 初始化sock的锁
 spin_lock_init(&nlk->cb_lock);
// 初始化等待队列
 init_waitqueue_head(&nlk->wait);
// sock的析构函数,释放接收队列中的skb数据包
 sk->sk_destruct = netlink_sock_destruct;
 sk->sk_protocol = protocol;
// 注意这里没有重新定义sk的sk_data_ready函数
// 在sock_init_data()函数中将sk_data_ready定义为sock_def_readable()函数
 return 0;
}

用户空间使用socket(2)系统调用打开netlink类型的套接口时, 在内核中会调用sys_sock()函数, 然后是调用__sock_create()函数, 在其中调用netlink协议族的create()函数, 即netlink_create()函数.
 
4.2 建立服务器端的套接口

以前也介绍过另一个建立netlink sock的函数netlink_kernel_create, 一般是在netlink的各种协议类型模块初始化时调用的, 而不是socket系统调用时调用的, 每个netlink协议初始化是只调用一次, 建立一个内核中的netlink接口, 相当于服务器, 其中也调用了__netlink_create()函数:
/*
 * 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,
                      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);
 if (unit<0 || unit>=MAX_LINKS)
  return NULL;
// 这里的lite表示只是简单分配一个socket,没有真正初始化
 if (sock_create_lite(PF_NETLINK, SOCK_DGRAM, unit, &sock))
  return NULL;
// 用这个lite sock再建立netlink sock
 if (__netlink_create(sock, unit) < 0)
  goto out_sock_release;
 if (groups < 32)
  groups = 32;
// listerns是个位图对应groups中每个元素
 listeners = kzalloc(NLGRPSZ(groups), GFP_KERNEL);
 if (!listeners)
  goto out_sock_release;
 sk = sock->sk;
// 重新定义了sk_data_ready函数
 sk->sk_data_ready = netlink_data_ready;
// 这个是相应的各netlink协议数据处理函数
 if (input)
  nlk_sk(sk)->data_ready = input;
 if (netlink_insert(sk, 0))
  goto out_sock_release;
 nlk = nlk_sk(sk);
 nlk->flags |= NETLINK_KERNEL_SOCKET;
 netlink_table_grab();
// 注册到相应unit的netlink协议表中
 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;
}

5. netlink套接口的操作

在__netlink_create函数中定义了netlink套接口的操作结构为netlink_ops:
 sock->ops = &netlink_ops;
该结构定义如下:
static const struct proto_ops netlink_ops = {
 .family = PF_NETLINK,
 .owner = THIS_MODULE,
 .release = netlink_release,
 .bind =  netlink_bind,
 .connect = netlink_connect,
 .socketpair = sock_no_socketpair, // 无定义
 .accept = sock_no_accept, // 无定义
 .getname = netlink_getname,
 .poll =  datagram_poll,
 .ioctl = sock_no_ioctl, // 无定义
 .listen = sock_no_listen, // 无定义
 .shutdown = sock_no_shutdown, // 无定义
 .setsockopt = netlink_setsockopt,
 .getsockopt = netlink_getsockopt,
 .sendmsg = netlink_sendmsg,
 .recvmsg = netlink_recvmsg,
 .mmap =  sock_no_mmap, // 无定义
 .sendpage = sock_no_sendpage, // 无定义
};

5.1 释放
在close(2)时调用
static int netlink_release(struct socket *sock)
{
 struct sock *sk = sock->sk;
 struct netlink_sock *nlk;
 if (!sk)
  return 0;
// 将套接口sk从系统sk链表和绑定链表中断开
 netlink_remove(sk);
 nlk = nlk_sk(sk);
 spin_lock(&nlk->cb_lock);
 if (nlk->cb) {
// 释放netlink控制块处理
  if (nlk->cb->done)
   nlk->cb->done(nlk->cb);
  netlink_destroy_callback(nlk->cb);
  nlk->cb = NULL;
 }
 spin_unlock(&nlk->cb_lock);
 /* OK. Socket is unlinked, and, therefore,
    no new packets will arrive */
// 设置sk状态为SOCK_DEAD, 断开sock和sk的互指
 sock_orphan(sk);
 sock->sk = NULL;
// 唤醒所有等待队列
 wake_up_interruptible_all(&nlk->wait);
// 清空写队列
 skb_queue_purge(&sk->sk_write_queue);
 if (nlk->pid && !nlk->subscriptions) {
// 发送释放通知
  struct netlink_notify n = {
      .protocol = sk->sk_protocol,
      .pid = nlk->pid,
       };
  atomic_notifier_call_chain(&netlink_chain,
    NETLINK_URELEASE, &n);
 } 
// 减少模块计数
 if (nlk->module)
  module_put(nlk->module);
// 相当于加锁
 netlink_table_grab();
 if (nlk->flags & NETLINK_KERNEL_SOCKET) {
// 释放内核中的netlink服务器端
  kfree(nl_table[sk->sk_protocol].listeners);
  nl_table[sk->sk_protocol].module = NULL;
  nl_table[sk->sk_protocol].registered = 0;
 } else if (nlk->subscriptions)
  netlink_update_listeners(sk);
// 相当于解锁
 netlink_table_ungrab();
// 释放该netlink sock的多播组
 kfree(nlk->groups);
 nlk->groups = NULL;
// 释放sock
 sock_put(sk);
 return 0;
}

5.2 绑定bind
绑定通常是针对服务端
static int netlink_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
{
 struct sock *sk = sock->sk;
 struct netlink_sock *nlk = nlk_sk(sk);
 struct sockaddr_nl *nladdr = (struct sockaddr_nl *)addr;
 int err;
// 检查一下地址的协议族是否为AF_NETLINK 
 if (nladdr->nl_family != AF_NETLINK)
  return -EINVAL;
 /* Only superuser is allowed to listen multicasts */
 if (nladdr->nl_groups) {
// 指定了多播组, 这是需要root权限
  if (!netlink_capable(sock, NL_NONROOT_RECV))
   return -EPERM;
  if (nlk->groups == NULL) {
// 分配多播组空间
   err = netlink_alloc_groups(sk);
   if (err)
    return err;
  }
 }
 if (nlk->pid) {
// 如果sock的pid非0, 检查是否匹配在nladdr地址结构中指定的pid
  if (nladdr->nl_pid != nlk->pid)
   return -EINVAL;
 } else {
// sock的pid为0, 根据nladdr是否指定pid来执行插入或
  err = nladdr->nl_pid ?
   netlink_insert(sk, nladdr->nl_pid) :
   netlink_autobind(sock);
  if (err)
   return err;
 }
// 非多播情况时就可以返回成功了
 if (!nladdr->nl_groups && (nlk->groups == NULL || !(u32)nlk->groups[0]))
  return 0;
 netlink_table_grab();
// 多播情况下更新sock参数
 netlink_update_subscriptions(sk, nlk->subscriptions +
                                  hweight32(nladdr->nl_groups) -
                                  hweight32(nlk->groups[0]));
 nlk->groups[0] = (nlk->groups[0] & ~0xffffffffUL) | nladdr->nl_groups;
 netlink_update_listeners(sk);
 netlink_table_ungrab();
 return 0;
}

// 根据pid插入
static int netlink_insert(struct sock *sk, u32 pid)
{
// netlink相应协议的HASH结构
 struct nl_pid_hash *hash = &nl_table[sk->sk_protocol].hash;
 struct hlist_head *head;
// 缺省错误为地址已经被使用
 int err = -EADDRINUSE;
 struct sock *osk;
 struct hlist_node *node;
 int len;
 netlink_table_grab();
// 根据pid查找相应HASH链表头
 head = nl_pid_hashfn(hash, pid);
 len = 0;
// 检查pid是否已经在链表中, 有则失败
 sk_for_each(osk, node, head) {
  if (nlk_sk(osk)->pid == pid)
   break;
  len++;
 }
 if (node)
  goto err;
// 缺省错误改为系统忙
 err = -EBUSY;
// 如果sock的pid不为0, 错误, 只有pid为0的sock才能执行该函数
// sock的pid不为0时不会再进行insert操作了
 if (nlk_sk(sk)->pid)
  goto err;

// 缺省错误改为无内存空间
 err = -ENOMEM;
 if (BITS_PER_LONG > 32 && unlikely(hash->entries >= UINT_MAX))
  goto err;
// 如果链表不为空而且链表长度数量过长,会调整HASH表,重新获取HASH链表头
// 不过这种情况很少发生
 if (len && nl_pid_hash_dilute(hash, len))
  head = nl_pid_hashfn(hash, pid);
 hash->entries++;
// 将pid赋值给sock的pid参数
 nlk_sk(sk)->pid = pid;
// 将sock节点添加进HASH链表
 sk_add_node(sk, head);
 err = 0;
err:
 netlink_table_ungrab();
 return err;
}

// 未指定pid时的自动绑定
// 实际是选一个没用过的pid后再进行插入操作
static int netlink_autobind(struct socket *sock)
{
// 从socket找到sock
 struct sock *sk = sock->sk;
// netlink相应协议的HASH结构
 struct nl_pid_hash *hash = &nl_table[sk->sk_protocol].hash;
 struct hlist_head *head;
 struct sock *osk;
 struct hlist_node *node;
// pid取为当前进程的组ID
 s32 pid = current->tgid;
 int err;
// 有符号32位数
 static s32 rover = -4097;
retry:
 cond_resched();
 netlink_table_grab();
// 找合适的HASH链表头
 head = nl_pid_hashfn(hash, pid);
 sk_for_each(osk, node, head) {
// 查找链表中是否已经有该pid
  if (nlk_sk(osk)->pid == pid) {
// 存在, 则更新pid, 重新检查, 注意这时的pid是个负数
   /* Bind collision, search negative pid values. */
   pid = rover--;
   if (rover > -4097)
    rover = -4097;
   netlink_table_ungrab();
   goto retry;
  }
 }
 netlink_table_ungrab();
// 此时的pid是一个负数转换为无符号32位数, 将是一个非常大的数
// 执行正常的pid插入
 err = netlink_insert(sk, pid);
 if (err == -EADDRINUSE)
  goto retry;
 /* If 2 threads race to autobind, that is fine.  */
 if (err == -EBUSY)
  err = 0;
 return err;
}
// 更新subscriotions
static void
netlink_update_subscriptions(struct sock *sk, unsigned int subscriptions)
{
 struct netlink_sock *nlk = nlk_sk(sk);
 if (nlk->subscriptions && !subscriptions)
  __sk_del_bind_node(sk);
 else if (!nlk->subscriptions && subscriptions)
  sk_add_bind_node(sk, &nl_table[sk->sk_protocol].mc_list);
 nlk->subscriptions = subscriptions;
}
// 更新listeners
static void
netlink_update_listeners(struct sock *sk)
{
 struct netlink_table *tbl = &nl_table[sk->sk_protocol];
 struct hlist_node *node;
 unsigned long mask;
 unsigned int i;
 for (i = 0; i < NLGRPSZ(tbl->groups)/sizeof(unsigned long); i++) {
  mask = 0;
// 遍历多播链表生成多播组的掩码
  sk_for_each_bound(sk, node, &tbl->mc_list)
   mask |= nlk_sk(sk)->groups[i];
  tbl->listeners[i] = mask;
 }
 /* this function is only called with the netlink table "grabbed", which
  * makes sure updates are visible before bind or setsockopt return. */
}
 

你可能感兴趣的:(Linux,socket)