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

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

5.3 连接

连接通常是针对客户端连接服务器
static int netlink_connect(struct socket *sock, struct sockaddr *addr,
      int alen, int flags)
{
 int err = 0;
 struct sock *sk = sock->sk;
 struct netlink_sock *nlk = nlk_sk(sk);
 struct sockaddr_nl *nladdr=(struct sockaddr_nl*)addr;
 if (addr->sa_family == AF_UNSPEC) {
// 目的地址协议族为AF_UNSPEC(未指定), 简单返回成功
  sk->sk_state = NETLINK_UNCONNECTED;
  nlk->dst_pid = 0;
  nlk->dst_group  = 0;
  return 0;
 }
// 限制目的地址协议族类型为AF_NETLINK
 if (addr->sa_family != AF_NETLINK)
  return -EINVAL;
 /* Only superuser is allowed to send multicasts */
// 只有ROOT权限才能多播
 if (nladdr->nl_groups && !netlink_capable(sock, NL_NONROOT_SEND))
  return -EPERM;
// 没指定pid的话自动绑定一个pid
 if (!nlk->pid)
  err = netlink_autobind(sock);
 if (err == 0) {
// 已经指定了pid或者自动绑定成功时设置sock的对方参数, 状态为连接成功
  sk->sk_state = NETLINK_CONNECTED;
  nlk->dst_pid  = nladdr->nl_pid;
  nlk->dst_group  = ffs(nladdr->nl_groups);
 }
 return err;
}
 
5.4 获取sock名称

// 填充sockaddr_nl结构中的数据 
static int netlink_getname(struct socket *sock, struct sockaddr *addr, int *addr_len, int
peer)
{
 struct sock *sk = sock->sk;
 struct netlink_sock *nlk = nlk_sk(sk);
 struct sockaddr_nl *nladdr=(struct sockaddr_nl *)addr;
// 协议族
 nladdr->nl_family = AF_NETLINK;
 nladdr->nl_pad = 0;
 *addr_len = sizeof(*nladdr);
 if (peer) {
// 对方sock的pid和groups
  nladdr->nl_pid = nlk->dst_pid;
  nladdr->nl_groups = netlink_group_mask(nlk->dst_group);
 } else {
// 自己sock的pid和groups
  nladdr->nl_pid = nlk->pid;
  nladdr->nl_groups = nlk->groups ? nlk->groups[0] : 0;
 }
 return 0;
}

5.5 poll

poll是用poll(2)或select(2)系统调用选择套接口数据是否准备好时的处理函数,netlink用的是通用
的数据报的poll处理函数dategram_poll(), 说明略。
 
5.6 setsockopt

设置netlink sock的各种控制参数:
static int netlink_setsockopt(struct socket *sock, int level, int optname,
                              char __user *optval, int optlen)
{
 struct sock *sk = sock->sk;
 struct netlink_sock *nlk = nlk_sk(sk);
 int val = 0, err;
// sock层次要为SOL_NETLINK
 if (level != SOL_NETLINK)
  return -ENOPROTOOPT;
// 读取用户空间的设置信息
 if (optlen >= sizeof(int) &&
     get_user(val, (int __user *)optval))
  return -EFAULT;
 switch (optname) {
 case NETLINK_PKTINFO:
// 处理NETLINK_RECV_PKTINFO标志, 非0设置, 0为清除
  if (val)
   nlk->flags |= NETLINK_RECV_PKTINFO;
  else
   nlk->flags &= ~NETLINK_RECV_PKTINFO;
  err = 0;
  break;
 case NETLINK_ADD_MEMBERSHIP:
 case NETLINK_DROP_MEMBERSHIP: {
// 加入或退出多播组
  unsigned int subscriptions;
  int old, new = optname == NETLINK_ADD_MEMBERSHIP ? 1 : 0;
// 检查权限
  if (!netlink_capable(sock, NL_NONROOT_RECV))
   return -EPERM;
// 如果当前sock的多播组为空是分配空间
  if (nlk->groups == NULL) {
   err = netlink_alloc_groups(sk);
   if (err)
    return err;
  }
// 检查数据范围
  if (!val || val - 1 >= nlk->ngroups)
   return -EINVAL;
  netlink_table_grab();
// 原来的状态标志
  old = test_bit(val - 1, nlk->groups);
// 如果old=1, new=0, subscriptions-1
// 如果old=0, new=1, subscriptions+1
  subscriptions = nlk->subscriptions - old + new;
// 设置或清除相应状态标志
  if (new)
   __set_bit(val - 1, nlk->groups);
  else
   __clear_bit(val - 1, nlk->groups);
// 更新sock参数
  netlink_update_subscriptions(sk, subscriptions);
  netlink_update_listeners(sk);
  netlink_table_ungrab();
  err = 0;
  break;
 }
 default:
  err = -ENOPROTOOPT;
 }
 return err;
}

// 分配netlink sock的多播组空间
static int netlink_alloc_groups(struct sock *sk)
{
 struct netlink_sock *nlk = nlk_sk(sk);
 unsigned int groups;
 int err = 0;
 netlink_lock_table();
// 组的数量是内核初始化时固定的, 最小值32, 尽量是8的倍数
 groups = nl_table[sk->sk_protocol].groups;
 if (!nl_table[sk->sk_protocol].registered)
  err = -ENOENT;
 netlink_unlock_table();
 if (err)
  return err;
// NLGRPSZ(groups)进行8字节对齐
 nlk->groups = kzalloc(NLGRPSZ(groups), GFP_KERNEL);
 if (nlk