当前位置:操作系统 > Unix/Linux >>

PF_KEY接口在OpenBSD内核中的实现

PF_KEY协议是IPSec的重要组成部分。密钥管理进程利用PF_KEY与内核的SADB进行通信,实现SA(Security Association,安全联盟)和SP(Security Policy,安全策略)的管理。本文将从PF_KEY协议构造和PF_KEY相关系统调用等方面描述OpenBSD内核中的PF_KEY实现。

  利用IPSec技术可在IP层实现对数据包进行保护,降低了互联网通信的风险。IPsec的安全服务是由通信双方建立的安全联盟(SA)来提供的。IPsec系统在处理输入/输出IP 流时必须参考安全策略库(SPD),并根据从SPD中提取的策略对IP流进行不同的处理,例如拒绝、绕过和进行IPsec保护。SA和SPD的管理是利用PF_key API来实现用户进程和内核之间的通信,可以通过手工进行,也可以通过IKE来进行动态协商。

  PF_KEY是用户进程操作内核中的SADB(Security Associations Database)和SPD的编程接口。下面将从协议族构造、系统调用和PF_KEY socket实现三部分加以描述。

  协议族构造

  Net/3组把协议关联到一个域中,并且用一个协议族常量来标识每个域。在OpenBSD定义的域包含以下的四个,分别是路由协议族、IP协议族、Unix协议族和PF_KEY协议族。同时定义了全局指针型变量domains,由它将协议族结构链接在一起。

  初始化完成后,内核构建了图1所示的数据结构。pfkey_domain定义的协议族为PF_KEY,Socket地址为PF_KEY。

  在PF_KEY协议初始化过程中,系统还定义了指针数组,用于指向含有Socket操作函数的不同版本pfkey_version结构。pfkey_version结构定义在sys/net/pfkeyv2.h文件中。

  

  图1 初始化后的domain链表和protosw数组

  目前OpenBSD实现的PF_KEY版本是2.0,该指针数组结构定义如下:

  struct pfkey_version

  {

  int protocol;

  int (*create)(struct socket socket);

  int (*release)(struct socket *socket);

  int (*send)(struct socket *socket, void *message, int len);

  } = { PFKEYV2_PROTOCOL,

  &pfkeyv2_create, &pfkeyv2_release, &pfkeyv2_send };

  PF_KEY socket实现

  PF_KEY socket系统调用

  当一个应用调用Socket时,进程用系统调用机制将三个独立的整数传给内核。syscall将参数复制到32bit值的数组中,并将数组指针作为第二个参数传给Socket的内核版。内核版的Socket将第二个参数“uap”作为指向sys_socket_args结构的指针。图2显示了上述过程。

  

  图2 用户空间Socket参数到内核空间转换图

  当进程调用socket(PF_KEY,SOCK_RAW, PF_KEY_V2)时,内核sys_socket()向PF_KEY协议中pfkey_protosw(见图1)定义的pfkey_usrreq()发送PRU_ATTACH,调用如下:

  error=(*prp->pr_usrreq)(so,PRU_ATTACH,NULL,(struct mbuf *)(long)proto,NULL);

  Pfkey_usrreq()处理PRU_ATTACH请求,创建协议控制块。

  PF_KEY close()系统调用

  内核中sys_close()对应用户空间的close()系统调用,用来关闭各类描述符。当fd是引用对象的最后描述符时,与对象有关的close函数被调用如下:

  error=(*fp->f_ops->fo_close)(fp, p);

  Socket的fp->f_ops->fo_close是soo_close()函数。soo_close()是soclose()函数的封装器。soclose()取消Socket上相关连接并释放不需要的数据结构,发送PRU_DETACH请求断开Socket与PF_KEY协议的联系,最后调用sofree()释放PF_KEY socket。

  error2=(*so->so_proto->pr_usrreq)(so, PRU_DETACH, NULL,NULL, NULL);

  Pfkey_usrreq()响应soclose()的PRU_DETACH请求,释放该Socket协议控制块,并将该Socket从pfkeyv2_sockets链表中删除。

  PF_KEY 读写系统调用

  所有的写系统调用都要直接或间接地调用sosend。sosend的功能是将进程来的数据复制到内核,并将数据传递给与插口相关的协议,如下:

  error=(*so->so_proto->pr_usrreq)(so, PRU_SEND, top, addr, control);

  对于PF_KEY的Socket,pfkey_usrreq()间接调用pfkey_output(),pfkey_output()直接调用pfkeyv2_send()。

  所有处理用户消息的工作全部在pfkeyv2_send()内完成,包括SA的增加、删除、清空和请求操作等。pfkeyv2_send()通过pfkeyv2_sendup()将结果放入Socket的接收缓冲区中,并通知用户进程接收缓存已改变。

  同写调用一样,用户空间的读调用如read和recv等,都是由内核中的一个公共函数soreceive()来完成所有的工作。sorecevice()函数将数据从Socket的接口缓存传送到进程指定的缓存中。

  sorecevice是一个复杂的函数,导致其复杂的主要原因是繁琐的指针操作及对多种类型的数据(带外数据、地址、控制信息和正常数据)和多目标(进程缓存和mbuf链)的处理。

  最后在这里列出PF_KEY的接口实现的相关系统调用图,见图3。

  

  图3 PF_KEY内核调用图

  结束语

  本文主要描述了PF_KEY接口在OpenBSD内核中的实现。通过该接口,可以实现IPSec的SA和SP的自动和手工配置管理。作为IPSec的重要组成部分,SADB和SPD与PF_KEY有着紧密的联系,但本文中没有具体描述。想要深入了解的IPSec的整体实现,阅读源码是比较有效的方法之一。

  

上一个:用FreeBSD建立拨号网关
下一个:UNIX“login”程序存在安全漏洞

更多Unix/Linux疑问解答:
路由原理介绍
子网掩码快速算法
改变网络接口的速度和协商方式的工具miitool和ethtool
Loopback口的作用汇总
OSPF的童话
增强的ACL修改功能
三层交换机和路由器的比较
用三层交换机组建校园网
4到7层交换识别内容
SPARC中如何安装Linux系统(2)
SPARC中如何安装Linux系统(1)
用Swatch做Linux日志分析
实战多种Linux操作系统共存
浅析Linux系统帐户的管理和审计
Linux2.6对新型CPU的支持(2)
电脑通通透
玩转网络
IE/注册表
DOS/Win9x
Windows Xp
Windows 2000
Windows 2003
Windows Vista
Windows 2008
Windows7
Unix/Linux
苹果机Mac OS
windows8
安卓/Android
Windows10
如果你遇到操作系统难题:
请访问www.zzzyk.com 试试
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,