本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严
禁用于任何商业用途。
msn:
[email protected]
来源:http://yfydz.cublog.cn
UNP第一卷的第3版是著名的RWS先生魂归上帝后由后人添加新的一些新的东西后发布的,增加的部分包括
PF_KEY和SCTP等的相关编程。今天重读UNP,看看PF_KEY一章。
PF_KEY协议族主要用来处理安全联盟SA的,对SADB(SA数据库)进行管理,主要用在IPSEC协议中,其他
协议如OSPF等用到密钥的协议中也可以用。
PF_KEY_v2的编程API在RFC2367中定义,其实在freeswan的pluto中有完整的PF_KEY运用实例。在UNP这
本书中只是很简单的介绍了一下,属于入门级,详细说明要看RFC,程序实例看pluto。
PF_KEY协议族只支持RAW模式,只能由ROOT权限的用户打开PF_KEY类型的套接口,这和netlink类型套接
口是类似的,而且数据格式也是以消息方式传输。
进程PF_KEY类型的套接口和netlink套接口一样,可以双向和内核进行通信。可以向内核和其他打开了
PF_KEY套接口发送消息,可进行SADB中的SA项的增加和删除;也可以从内核中接收消息。
PF_KEY的消息的数据格式都是有一个格式相同的数据头(12字节长), 说明数据类型等信息, 然后后面可
以跟0到多个扩展的结构用来描述不同类型的具体数据内容:
消息头:
struct sadb_msg {
u_int8_t sadb_msg_version; /* PF_KEY_V2 */
u_int8_t sadb_msg_type; /* see Figure 19.2 */
u_int8_t sadb_msg_errno; /* error indication */
u_int8_t sadb_msg_satype; /* see Figure 19.3 */
u_int16_t sadb_msg_len; /* length of header + extensions / 8 */
u_int16_t sadb_msg_reserved; /* zero on transmit, ignored on receive */
u_int32_t sadb_msg_seq; /* sequence number */
u_int32_t sadb_msg_pid; /* process ID of source or dest */
};
结构中的元素含义都比较直观, sadb_msg_type是消息的类型, 基本反映命令动作, 如读当前SADB, 增
加删除SA等, sadb_msg_satype表示的是要操作的SA的类型,表示要对哪些SA进行命令动作.
基本通信过程:
先打开PF_KEY的套接口, 填消息头信息, 必要时再填后续数据信息, 可以用write()函数将数据写到套
接口, 用read()函数从套接口读回应数据:
dump_sadb:
1 void
2 sadb_dump(int type)
3 {
4 int s;
5 char buf[4096];
6 struct sadb_msg msg;
7 int goteof;
8 s = Socket(PF_KEY, SOCK_RAW, PF_KEY_V2);
9 /* Build and write SADB_DUMP request */
10 bzero(&msg, sizeof (msg));
11 msg.sadb_msg_version = PF_KEY_V2;
12 msg.sadb_msg_type = SADB_DUMP;
13 msg.sadb_msg_satype = type;
14 msg.sadb_msg_len = sizeof (msg) / 8;
15 msg.sadb_msg_pid = getpid();
16 printf("Sending dump message:\n");
17 print_sadb_msg (&msg, sizeof (msg));
// write发送
18 Write(s, &msg, sizeof (msg));
19 printf("\nMessages returned:\n");
20 /* Read and print SADB_DUMP replies until done */
21 goteof = 0;
22 while (goteof == 0) {
23 int msglen;
24 struct sadb_msg *msgp;
// read读
25 msglen = Read(s, &buf, sizeof (buf));
26 msgp = (struct sadb_msg *) &buf;
27 print_sadb_msg(msgp, msglen);
// 注意判断消息结束的方法
28 if (msgp->sadb_msg_seq == 0)
29 goteof = 1;
30 }
31 close(s);
32 }
建立SA:
建立SA分两类, 一种是静态SA, 一般建立后就不变了; 一种是动态SA, SA有一定寿命, 需要周期性的更
新密钥。前者一般用在手工建立的IPSEC连接,一般只是调试时使用,后者用于自动密钥协商,是常用
的情况。
为建立一个静态SA,需要发送的消息除了消息头外,还必须要有三个扩展数据部分:SA、地址和密钥;
可选的还可有生命期(lifetime)、身份(identity)和敏感性(sensitivity)。
SA结构:
struct sadb_sa {
u_int16_t sadb_sa_len; /* length of extension / 8 */
u_int16_t sadb_sa_exttype; /* SADB_EXT_SA */
u_int32_t sadb_sa_spi; /* Security Parameters Index (SPI) */
u_int8_t sadb_sa_replay; /* replay window size, or zero */
u_int8_t sadb_sa_state; /* SA state, see Figure 19.7 */
u_int8_t sadb_sa_auth; /* authentication algorithm, see Figure 19.8 */
u_int8_t sadb_sa_encrypt; /* encryption algorithm, see Figure 19.8 */
u_int32_t sadb_sa_flags; /* bitmask of flags */
};
地址结构,该结构后面跟相关地址族的sockaddr结构(sockaddr_in, sockaddr_in6等):
struct sadb_address {
u_int16_t sadb_address_len; /* length of extension + address / 8 */
u_int16_t sadb_address_exttype; /* SADB_EXT_ADDRESS_{SRC,DST,PROXY} */
u_int8_t sadb_address_proto; /* IP protocol, or 0 for all */
u_int8_t sadb_address_prefixlen; /* # significant bits in address */
u_int16_t sadb_address_reserved; /* reserved for extension */
};
密钥结构, 密钥跟在该结构后面:
struct sadb_key {
u_int16_t sadb_key_len; /* length of extension + key / 8 */
u_int16_t sadb_key_exttype; /* SADB_EXT_KEY_{AUTH, ENCRYPT} */
u_int16_t sadb_key_bits; /* # bits in key */
u_int16_t sadb_key_reserved; /* reserved for extension */
};
要写程序实现建立SA过程,就是在一大块缓冲区中依次填入消息头结构、SA结构、相关地址信息、密钥
结构、密钥,然后发到套接口,然后接收数据,接收时要注意判断消息的进程号和类型是否和自己的相
符,相符了才是发给自己的,因为内核会向所有打开了PF_KEY套接口的进程发送消息。
对于动态SA,一般用户空间都是一个daemon来定时维护,一开始并没有设置算法密钥,需要协商决定,这
时daemon需要知道内核中支持了哪些算法, 因此这个daemon需要向内核登记注册自身,指出能处理的SA
类型,然后内核回应所支持的加密和认证算法的类型,daemon通过IKE协商出SA后将SA信息发送到内核,此
时一般会带生命期, SA到期后会从SADB中删除, 需要重新协商新的SA.
支持结构:
struct sadb_supported {
u_int16_t sadb_supported_len; /* length of extension + algorithms / 8 */
u_int16_t sadb_supported_exttype; /* SADB_EXT_SUPPORTED_{AUTH, ENCRYPT} */
u_int32_t sadb_supported_reserved; /* reserved for future expansion */
};
/* followed by algorithm list */
算法结构:
struct sadb_alg {
u_int8_t sadb_alg_id; /* algorithm ID from Figure 19.8 */
u_int8_t sadb_alg_ivlen; /* IV length, or zero */
u_int16_t sadb_alg_minbits; /* minimum key length */
u_int16_t sadb_alg_maxbits; /* maximum key length */
u_int16_t sadb_alg_reserved; /* reserved for future expansion */
};
后记: UNP中对PF_KEY的介绍太简单了, 以后将分析pluto从中再学习了.