UNPv1_r3学习日记: PF_KEY

本文档的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从中再学习了.

你可能感兴趣的:(数据结构,编程,算法,socket,ext)