UNPv1_r3学习日记: PF_KEY
本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严
禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源: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从中再学习了.