3.2.1.3.1 int
init_module(void)
3.2.1.3.2 int
cleanup_module(void)
3.2.1.3.4 int
ipsec_cleanup(void)
3.2.2.3.1 int
ipsec_tunnel_init_devices(void)
3.2.2.3.2 int
ipsec_tunnel_init(struct device *dev) 23
3.2.2.3.3 int
ipsec_tunnel_attach (struct device* tndev, struct ipsecpriv* prv,struct device
*dev)
3.2.2.3.4 int
ipsec_open(struct device *dev)
3.2.2.3.5 int
ipsec_tunnel_start_xmit(struct sk_buff *skb,struct device *dev)
3.2.2.3.6 int
ipsec_tunnel_detach(struct device *dev,struct ipsecpriv *prv)
3.2.2.3.7 int
ipsec_tunnel_clear(void)
3.2.2.3.8 int
ipsec_tunnel_cleanup_devices(void)
3.2.3.2.1 int
ipsec_rcv(struct sk_buff *skb,unsighed short xlen)
3.2.4.3.1 int
ipsec_tdbinit(void)
3.2.4.3.2 int
tdb_init(struct tdb *tdbp,struct encap_msghdr *em)
3.2.4.3.3 int
puttdb(struct tdb *tdbp)
3.2.4.3.4 struct
tdb* gettdb(struct sa_id *said)
3.2.4.3.5 int
deltdb(struct tdb *tdbp)
3.2.4.3.6 int
deltdbchain(struct tdb *tdbp)
3.2.4.3.7 int
ipsec_tdbwipe(struct tdb *tdbp)
3.2.5.3.1 int
ipsec_radijinit(void)
3.2.5.3.3 int
ipsec_breakroute(struct sockaddr_encap *eaddr, struct sockaddr_encap *emask)
3.2.5.3.4 struct
eroute *ipsec_findroute(struct sockaddr_encap *eaddr)
3.2.5.3.5 int
ipsec_cleareroutes(void)
3.2.5.3.6 int
ipsec_radijcleanup(void)
3.3.2.3.1 int
main(int argc, char **argv)
3.3.2.3.2 int
create_lock(void)
3.3.2.3.3 int
delete_lock(void)
3.3.2.3.4 void
exit_pluto(int status)
3.3.3.2.1 void
call_server(void)
3.3.3.2.2 void
find_ifaces(void)
3.3.3.3.3 static
int init_pfkeyfd(void)
3.3.3.3.4 static
int init_whackfd(void)
3.3.4.2.1.1 消息1:发起方的Cookie值CKY-I、发起方提出的一个或多个对ISAKMP消息的保护方案
3.3.4.2.1.2 消息2:响应方的Cookie值CKY-R、响应方选择的保护方案
3.3.4.2.1.3 消息3:发起方的Diffie-Hellman公开值gx mod p、随机数nounce
Ni
3.3.4.2.1.4 消息4:响应方的Diffie-Hellman公开值gy mod p、随机数nounce
Nr
3.3.4.2.1.5 消息5:发起方的身份标识IDi、散列函数值HASH-I及对HASH-I的签名(加密)结果
3.3.4.2.1.6 消息6:响应方的身份标识IDr、散列函数值HASH-R及对HASH-R的签名(加密)结果
3.3.4.2.2.1 消息1:发起方为协商IPSEC
SA发出的第一个消息
3.3.4.2.2.2 消息2:响应方为协商IPSEC
SA发出的第一个消息
3.3.4.2.2.3 消息3:发起方为协商IPSEC
SA发出的第二个消息
3.4.3.2.3 描述连接的模块(connection.c)
3.4.3.2.3.1.1... connections ―― 描述了一个连接的各个属性... 54
3.4.3.2.3.1.2 end ―― /*此结构描述了一方主机的各种状态*/
3.4.3.2.3.1.3 host_pair ―― /*该结构描述了一个主机对的各种状态*/
3.4.3.2.3.2.1
void initiate_connection(const char *name, int whackfd)
3.4.3.2.3.2.2
void add_connection(const struct whack_message *wm)
3.4.3.2.3.2.3
void delete_connection(struct connection *c)
3.4.3.2.3.2.4
void delete_every_connection(void)
3.4.3.2.3.2.5 void
terminate_connection(const char *nm)
3.4.3.2.3.2..6 bool
orient(struct connection *c, bool must)
3.3.5.2.1 void
pfkey_handle(void)
3.3.5.2.2 ipsec_spi_t
get_ipsec_spi(ipsec_spi_t avoid)
3.3.5.2.3 bool
do_command(struct connection *c, const char *verb)
3.3.5.2.6 bool
do_eroute(struct state *st, unsigned op, const char *opname UNUSED)
3.3.5.2.7 bool
del_spi(ipsec_spi_t spi, int proto, struct in_addr src, struct in_addr dest)
3.3.5.2.8 bool
setup_half_ipsec_sa(struct state *st, bool inbound)
3.3.5.2.9 bool
teardown_half_ipsec_sa(struct state *st, bool inbound)
3.3.5.2.10 bool
could_eroute(struct connection *c, struct connection *ero)
3.3.5.2.11 bool
install_inbound_ipsec_sa(struct state *st)
3.3.5.2.12 bool
install_ipsec_sa(struct state *st, bool inbound_alse)
3.3.5.2.13 bool
delete_ipsec_sa(struct state *st, bool inbound_only)
3.4.2.2.9
Supported Algorithms(支持的算法)扩展
3.4.4.1
pfkey_v2_parser.c中的函数(对各种对SADB的操作的分析和处理)
3.4.4.1.1 int
pfkey_alloc_tdb(struct tdb** tdb)
3.4.4.1.2 int
pfkey_alloc_eroute(struct eroute ** eroute)
3.4.4.1.15 int
pfkey_tdb_init(struct tdb* tdbp, sturct sadb_ext **extensions)
3.4.4.1.33 int
pfkey_msg_interp(struct sock *sk, struct sadb_msg *pfkey_msg)
3.4.4.2 pfkey_v2.c中的函数(对pfkey协议的实现)
3.4.4.2.1 int
pfkey_init(void)
3.4.4.2.2 int
pfkey_cleanup(void)
3.4.4.2.3 int
pfkey_create(struct socket *sock, int protocol)
3.4.4.2.4 static
int pfkey_dup(struct socket *newsock, struct socket *oldsock)
3.4.4.2.5 int
pfkey_release(struct socket *sock)
3.4.4.2.6 int
pfkey_shutdown(struct socket *sock, int mode)
3.4.4.2.11 pfkey_insert_socket(struct
sock *sk)
3.4.4.2.12 static
void pfkey_remove_socket(struct sock *sk)
3.4.4.2.13 static
void pfkey_destroy_socket(struct sock *sk)
3.4.4.3
pfkey_v2_build.c中的函数(构造各种pfkey消息结构)
3.4.4.3.11 int
pfkey_x_kmprivate_build(struct sadb_ext** pfkey_ext)
3.4.4.3.12 int
pfkey_x_satype_build(struct sadb_ext**pfkey_ext,uint8_t satype)
3.4.4.3.14 int
pfkey_msg_build(struct sadb_msg **pfkey_msg, struct sadb_ext * extensions[],
int dir)
3.4.4.3.15 void
pfkey_extensions_init(struct sadb_ext *extensions[SADB_EXT_MAX + 1])
3.4.4.3.16 void
pfkey_extensions_free(struct sadb_ext *extensions[SADB_EXT_MAX] + 1)
3.4.4.3.17 void
pfkey_msg_free(struct sadb_msg **pfkey_msg)
3.4.4.4
pfkey_v2_parse.c中的函数(对pfkey_msg的各种数据结构进行分析)... 100
3.4.4.4.1 int
pfkey_sa_parse(struct sadb_ext *pfkey_ext)
3.4.4.4.2 int
pfkey_lifetime_parse(struct sadb_ext *pfkey_ext)
3.4.4.4.3 int
pfkey_address_parse(struct sadb_ext *pfkey_ext)
3.4.4.4.4 int
pfkey_key_parse(struct sadb_ext *pfkey_ext)
3.4.4.4.5 int
pfkey_ident_parse(struct sadb_ext *pfkey_ext)
3.4.4.4.6 int
pfkey_sens_parse(struct sadb_ext *pfkey_ext)
3.4.4.4.7 int
pfkey_prop_parse(struct sadb_ext *pfkey_ext)
3.4.4.4.8 int
pfkey_supported_parse(struct sadb_ext *pfkey_ext)
3.4.4.4.9 int
pfkey_spirange_parse(struct sadb_ext *pfkey_ext)
3.4.4.4.10 int
pfkey_x_kmprivate_parse(struct sadb_ext *pfkey_ext)
3.4.4.4.11 int
pfkey_x_satype_parse(struct sadb_ext *pfkey_ext)
3.4.4.4.12 int
pfkey_x_ext_debug_parse(struct sadb_ext *pfkey_ext)
本说明书提供了freeswan各个模块部件的说明,以供编码人员具体实现及今后的维护工作。
在确定目标系统的过程中,主要遵循了以下几个原则:
Ø 目标系统基本上完整地实现IPSec协议族,完全支持VPN的要求;
Ø 目标系统的服务器端一定要建立在具有自主版权的内核操作系统之上;
Ø 目标系统的客户端使用方便、界面友好、配置和管理简单灵活。
该软件由如下几个模块组成:
Ø KLIPS模块 ―― 实现对进入或外出IP包的安全处理,如:加密、认证等(运行在内核空间)
Ø Pluto模块 ―― 实现IKE协议,完成安全联盟的协商(运行在用户空间)
Ø PF_KEY模块―― 实现pfkey2协议,完成上述两个模块间关于SA的通信
本程序由两大模块组成,一部分是klips,它运行在操作系统内核空间,主要负责安全联盟和密钥的管理工作,以及对数据报的加密、解密的处理工作;一部分是pluto,它是一个运行在用户空间的守护进程,主要负责安全联盟的协商工作。下面分别是它们的文件组成:
|―――freeswan 源文件目录
|――klips
|――libdes
|――pluto
|――utils
--―――klips ipsec的核心实现模块
|――net
|――ipsec
|――ipsec_init.c 登记模块,并初始化
|――ipsec_tunnel. 数据包的处理并发送模块
|――ipsec_rcv.c 数据包接收并处理模块
|――ipsec_xform.c 管理SA的模块
|――ipsec_sha1.c sha1实现模块,由sha1.c改编
|――ipsec_md5c.c md5.c实现模块
|――ipcomp.c
|――radij.c 路由表的radix数的实现模块
|――ipsec_radij.c 对上个文件的改编
|――pfkey_v2.c 实现PF_KEY2协议的模块
|――pfkey_v2_parser.c 对上个文件的改编
|――sysctl_net_ipsec.c
|――utils
|――eroute.c 用户操作eroute表的模块
|――spi.c 用户操作SA库的模块
|――spigrp.c 同上
|――tncfg.c 用户操作虚接口的模块
|――klipsdebug.c
向内核系统登记几个proc文件,以便于向内核空间中查询安全联盟和eoute表,以及虚接口的状况;
初始化SA数据库(tdb链);
初始化SPDB数据库(eroute表);
初始化pf_key(PF_KEY套接口);
ipsec_init.c: int ipsec_init(void)
ipsec_xform.c: int ipsec_tdbinit(void)
ipsec_radij.c: int ipsec_radijinit(void)
pfkey_v2.c: int pfkey_init(void)
将下列结构登记到内核系统中,则内核就可以通过proc文件系统向应用程序提供一个安全的界面来存取如SA、eroute表等资料。
struct proc_dir_entry ipsec_eroute ;
struct proc_dir_entry ipsec_spi ;
struct proc_dir_entry ipsec_spigrp ;
struct proc_dir_entry ipsec_tncfg ;
struct proc_dir_entry ipsec_spinew ;
struct proc_dir_entry ipsec_klipsdebug ;
struct notifier_block结构是在include/linux/notifier.h里面的:
struct notifier_block
{
int (*notifier_call)(struct notifier_block *self, unsigned long, void *);
struct notifier_block *next;
int priority;
};
而register_netdevice_notifier函数在net/core/dev.c里面,是这样的:
int register_netdevice_notifier(struct notifier_block *nb)
{
return notifier_chain_register(&netdev_chain, nb);
}
而notifier_chain_register函数在include/linux/notifier.h里面,是这样的:
extern __inline__ int notifier_chain_register(
struct notifier_block **list, struct notifier_block *n)
{
while(*list)
{
if(n->priority > (*list)->priority)
break;
list= &((*list)->next);
}
n->next = *list;
*list=n;
return 0;
}
显然就是根据每个block的优先级把这个block排列在一个block的链表里面,在notifier_chain_register函数里面我们可以发现这个链表是netdev_chain。实际上这个链表的作用就是在每个interface打开,关闭状态改变或者外界调用相应的ioctl的时候通知这个链表上面的所有相关的设备,而每一个协议都调用register_netdevice_notifier注册了一个netdev_notifier的结构体,这样就可以在interface改变的时候得到通知了(通过调用每个notifier_call函数)。
struct inet_protocol ah_protocol =
{
ipsec_rcv, /* AH handler ,定义此协议处理函数*/
NULL, /* TUNNEL error control,错误处理函数 */
0, /* next */
IPPROTO_AH, /* protocol ID */
0, /* copy */
NULL, /* data */
"AH" /* name */
};
ipsec_rcv函数是用来接收数据的callback函数,第二个是错误处理函数,其它的copy是用来协议共享的,这个以后再说,data当然就是这个结构体的私有数据了。
struct inet_protocol esp_protocol 同上;
inet_add_protocol函数在net/ipv4/protocol.c里实现:
void inet_add_protocol(struct inet_protocol *prot)
{
unsigned char hash;
struct inet_protocol *p2;
hash = prot->protocol & (MAX_INET_PROTOS - 1);
prot ->next = inet_protos[hash];
inet_protos[hash] = prot;
prot->copy = 0;
p2 = (struct inet_protocol *) prot->next;
while(p2 != NULL)
{
if (p2->protocol == prot->protocol)
{
prot->copy = 1;
break;
}
p2 = (struct inet_protocol *) p2->next;
}
}
这个函数是生成了一个hash表,然后每个hash表项都是一个链表头,然后通过这个hash表加链表的方式访问每个协议结构体。
登记及初始化模块包括以下一些函数:
init_modules(), ipsec_init(),cleanup_module(), ipsec_cleanup(),
ipsec_eroute_get_info(),ipsec_spi_get_info(),ipsec_spigrp_get_info(),
ipsec_tncfg_get_info(),ipsec_version_get_info(),ipsec_spi_get_new()
目的: 装载ipsec时进行登记及初始化工作。
参数: 无
返回值: 0 ―― 初始化成功,非0值 ―― 初始化未成功
算法描述: 调用ipsec_init()。
目的: 卸载ipsec进行清除工作。
参数: 无
返回值: 0 ―― 初始化成功,非0值――初始化未成功
算法描述: 调用ipsec_cleanup()。
目的: 装载ipsec时进行登记及初始化工作。。
参数: 无
返回值: 0 ―― 初始化成功,非0值――初始化未成功
算法描述:
1.将定义好的几个proc_dir_entry结构注册到系统中,如:
proc_register(proc_net,&ipsec_eroute);/*PROC_FS_21*/ 或
proc_register_dynamic(&proc_net,&ipsec_eroute); 或
proc_net_create(“ipsec_eroute”,0,ipsec_eroute_get_info);
proc_register为系统调用,在fs/proc/root.c中实现,主要就是在proc_net对应的目录下面生成每个协议的子目录。用户可以通过访问/proc/net目录下面的相应目录得到相关的资料。
2.调用ipsec_tdbinit()函数初始化SA数据库,此函数在ipsec_tdb.c文件中实现;
3.调用ipsec_radijinit()函数初始化SPD数据库,此函数在ipsec_radij.c文件中实现;
4.调用pfkey_init()函数初始化PFKEY,此函数在pfkey_v2.c文件中实现;
5.调用register_netdevice_notifier(&ipsec_dev_notifier)函数向系统中注册已定义的ipsec_dev_notifier结构,register_netdevice_ntifier()为系统调用;
6.调用inet_add_protocol(&esp_protocol)和inet_add_protocol(&ah_protocol)函数向系统中注册ESP协议和AH协议,inet_add_protocol()为系统调用;
7.调用ipsec_tunnel_init_devices()函数,登记并初始化ipsec虚接口,此函数定义在ipsec_tunnel.c文件中。
目的: 卸载ipsec进行清除工作。
参数: 无
返回值: 0 ―― 清除成功,非0值――清除未成功
算法描述:
1.调用ipsec_tunnel_cleanup_device(void)函数,清除向系统登记的ipsec虚接口,此函数在ipsec_xform.c中实现;
2.调用inet_del_protocol(&ah_protocol)和inet_del_protocol(&esp_protocol)清除掉在系统中注册的ESP协议和AH协议;
3.调用系统调用unregister_netdevice_notifier(&ipsec_dev_notifier)清除掉系统中注册的ipsec_dev_notifier;
4.调用ipsec_tdbcleanup(0)清除系统中的SA数据库,此函数在ipsec_tdb.c中文件实现;
5.调用ipsec_radijcleanup()清除系统中的SPD数据库,此函数在ipsec_radij.c文件中实现;
6.调用pfkey_cleanup()做pfkey清除工作;
7.调用系统调用proc_net_unregister()清除freeswan在系统中登记的各个proc文件。
为了在不改变现有的操作系统的网络协议栈的状况下,更好地将IPsec嵌入,本程序采用了虚接口的概念。实现中,将创建4个ipsec虚接口,可以将虚接口绑定在物理接口上。对于从tcp/udp协议层传下来的数据,将首先查询eroute表,根据eroute表项决定将数据包发往哪一个接口,物理接口与虚接口将被视为一致的。此时,如果数据包发往虚接口,则调用函数进行处理。(该程序类似于网卡驱动程序的编写)
ipsec_tunnel.c
虚接口定义为struct device结构,如:
static struct device dev_ipsec0 =
{
"ipsec0\0 ", /* name */
0, /* recv memory end */
0, /* recv memory start */
0, /* memory end */
0, /* memory start */
0x0, /* base I/O address */
0, /* IRQ */
0, 0, 0, /* flags */
NULL, /* next device */
ipsec_tunnel_probe /* setup */
};
struct ipsecpriv
{
struct sk_buff_head sendq;
struct device *dev;
struct wait_queue *wait_queue;
char locked;
int (*hard_start_xmit) (struct sk_buff *skb, struct device *dev);
int (*hard_header) (struct sk_buff *skb,
struct device *dev,
unsigned short type,
void *daddr,
void *saddr,
unsigned len);
#ifdef NET_21
int (*rebuild_header)(struct sk_buff *skb);
#else /* NET_21 */
int (*rebuild_header)(void *buff, struct device *dev,
unsigned long raddr, struct sk_buff *skb);
#endif /* NET_21 */
int (*set_mac_address)(struct device *dev, void *addr);
#ifndef NET_21
void (*header_cache_bind)(struct hh_cache **hhp, struct device *dev,
unsigned short htype, __u32 daddr);
#endif /* !NET_21 */
void (*header_cache_update)(struct hh_cache *hh, struct device *dev, unsigned char * haddr);
struct enet_statistics *(*get_stats)(struct device *dev);
struct enet_statistics mystats;
int mtu; /* What is the desired MTU? */
};
目的: 登记ipsec虚接口。
参数: 无
返回值: 0 ―― 登记成功,非0值――登记未成功
算法描述:
调用系统调用register_netdev(&dev_ipsec0~3),向系统登记4个ipsec虚接口。
目的: 初始化ipsec虚接口,完成device中变量的初始化和系统资源的申请。
参数:
返回值: 0 ―― 清除成功,非0值――清除未成功
算法描述:
1.填充device结构中的几个函数,如:
a) dev->open = ipsec_tunnel_open;
b) dev->stop = ipsec_tunnel_stop;
c) dev->hard_start_xmit = ipsec_tunnel_start_xmit;
d) dev->get_stats = ipsec_tunnel_get_stats;
2.给dev配空间,dev->priv = kmalloc(sizeof(struct ipsecpriv),GFP_KERNEL);
3.初始化dev结构:skb_queue_head_init(&dev->buffs[i]);
4.填充dev结构的其它值,如:dev->set_mac_address = NULL等等。
目的: 将某个虚接口绑定到指定的物理接口上。
参数:
返回值: 0 ―― 绑定成功,非0值――绑定未成功
算法描述:
目的:
参数: 要open的device
返回值: 0 ―― open成功,非0值――open未成功
算法描述:
1. 此方法在网络设备驱动程序里是网络设备被激活的时候被调用(即设备状态由down->up)。
2.驱动程序作为一个模块被装入:如果虚接口尚未绑定到物理接口,返回错误信息;否则调用MOD_INC_USE_COUNT宏,以防止模块卸载是设备处于打开状态。
目的: 假定此函数由dev_queue_xmit()函数调用,主要负责发送从tcp/udp协议层传来的数据包
参数: 由dev_queue_xmit()函数填充好的skb,及要发往的网络接口
返回值: 0 ―― open成功,非0值――open未成功
算法描述:
1. skb, dev 有效吗? 有效继续,无效返回;prv = dev->priv 获取ipsec接口私有数据;若prv为空,返回;获取系列参数:物理接口参数physdev, physmtu, stats;
2. 如果需要,拷贝skb:Ifdef NET_21, then skb_cloned(skb);
3. 获取Ip头iph,判断该包是否是IPV4,若不是则丢弃;
4. 计算硬件头长度(hard_header_len);
5. 将ip包的ttl减小,并计算校验和。
6. 填充查找键键值,根据键值查找eroute表,获取eroute,若找到,取出SA,即outgonging_said;
7. 判断此IP包是否是IKE协商包(UDP port 500),若是,则跳出;
8. 进入封装处理大循环(几乎一切处理都由outgoing_said决定,一个said对应一个或多个tdb(通道描述块),根据每个tdb对其进行处理):
(1) check for DROP or missing eroute
(2) check for REJECT eroute
(3) check for PASS eroute
(4) check for HOLD eroute
(5) check for TRAP eroute, signal PF_KEY, swap to HOLD eroute
(6) acquire lock for walking tdb chain
(7) calculate headroom required for chain
1) check if SA is in larval, drop
2) check if SA is dead, drop
3) check if replay overflowed, expire SA
4) check if lifetime counters have overflowed, expire SA
5) switch on protocol type, to calculate headroom size.
I. if ESP switch on protocol type to calculate tailroom size.
(8) alculate mtudiff, send ICMP fragment needed. Mark ``note2''
(9) ack MSS if desired
(10) copy upper (layer 2) header to safety if it was present
(11) check if data fits in existing skb, else expand.
(12) apply grouped transforms
1) apply disaster of #ifdefs.
2) switch by protocol type, calculate headroom for this stage
I. if ESP, then switch by cipher get headroom
II. if ESP, then switch by hash to get tailroom
3) double check (not in NDEBUG) if there is enough headroom
4) push the data ahead
5) double check (not in NDEBUG) if there is enough tailroom
6) extend the data behind
7) see if packet has become too long (bigger than 64K)
8) finally move the plaintext as appropriate
9) switch on protocol type
10) case: ESP
I. switch on cipher type, prepare IV
II. prepare self-describing padding
III. switch on cipher type, do encryption
IV. switch on cipher type, update IV
V. switch on hash type, do authentication
11) case: AH
I. prep replay info, headroom
II. switch on hash type, do authentication
12) case: IPIP, apply encap
13) case: IPCOMP
I. call skb_compress
II. do some debugging
14) recalculate header checksum
⒀lookup eroute by new outer header, if we found something and the src/dst have changed
9. end
ICMP if packet has become too big
10. re-apply link layer header if there was one.
11. attempt to re-route the packet
12. drop packet if new route leads to us again.
13. do connection tracking
14. do netfilter localout output call
15. call ip_send or IP_SEND depending on kernel version
⑴. 取得一些初始值, 如dst, src, iphlen, pyldsz,
⑵. 接下来是一系列合法性和安全性检查,对不合理部分有两种处理方法:
1) 不进行安全处理,直接从物理接口上发送并返回;
2) 丢弃数据包,返回。
⑶. 有eroute吗? 没有,则从physdev发送数据包并返回,有则继续;
⑷. 若指定隧道模式,却找不到spi,不做处理,按1)发送,返回;
⑸. 根据said取得tdbp链,和sa_len,若tdbp链为空,报错返回;
⑹. 进入tdb处理循环,求出ipsec处理所需的headroom和tailroom;
对tdbp链中每一个tdb进行如下处理:
① 大量的合法性判断,包括tdb状态是否对,SA是否过时,PFKEYv2处理等;
② 根据tdbp->tdb_said.proto计算headroom和tailroom。方法是,根据tdb_said.proto不同而算法不同:
IPPROTO_AH: headroom += sizeof(struct ah);
IPPROTO_ESP:
Ⅰ.tdbp->tdb_encalg(加密算法)
ESP_DES: headroom += sizeof(struct esp);
ESP_3DES: headroom += sizeof(struct esp);
ESP_NULL: headroom += offsetof(struct esp, esp_iv); ?????
default: 丢弃包。
Ⅱ.tdbp->tdb_autoalg(认证算法)
AH_MD5: tailroom += AHHMAC_HASHLEN;
AH_SHA: tailroom += AHHMAC_HASHLEN;
AH_NONE: NOP(不处理);
default: 不对,丢弃包。
Ⅲ.计算:(数据包要是16(?)字节的倍数,不然加空补齐。
tailroom += ((8 - ((pyldsz + 2 * sizeof(unsigned char)) % 8)) % 8) + 2;
IPPROTO_IPIP:
headroom += sizeof(struct iphdr);
default: 出错,丢弃。
最后,对链表中每一个tdb求出的headroom、tailroom求和得出max_headroom、max_tailroom,pyldsz也相应改变(加上headroom, tailroom)。(Ln 1016)
⑺. 求出tot_headroom, tot_tailroom, mtu_diff;若mtu_diff>0,说明prv->mtu计算不合适,重新计算;
⑻. 调整mss;
⑼. hard_header_stripped处理;(Ln 1077)
⑽. !。原skb空间够用吗?不够就重新分配一个skb并进行extend_copy;
⑾. 进入tdb链循环,进行真正的ipsec安全处理。
① 定义变量并赋初值;
② 根据tdbp->tdb_said.proto确定headroom, tailroom;与(6)的处理类似。
③ 处理skb,移动数据指针( skb_push, skb_put 函数);
④ ip头前移;
⑤ 根据不同的协议类型进行不同的处理:
switch(tdbp->tdb_said.proto)
IPPROTO_ESP:
填写espp (spi, rpl,... );
求出iv[0], iv[1] (初始化向量?);
求出idat, ilen (内部数据区域?) 要加密的数据地址及长度;
求出pad, padlen. (为了认证);(pad前面两个字节分别存放pad长度和前iph->protocol.
由tdbp->tdb_encalg决定调用加密算法:
ESP_DES:
des_cbc_encrypt(idat,idat, ilen,(caddr_t)tdbp->tdb_key_e,(caddr_t)iv, 1);
ESP_3DES:
des_ede3_cbc_encrypt(idat, idat, ilen,(caddr_t)(&((struct des_eks*)(tdbp->tdb_key_e))[0]),
(caddr_t)(&((struct des_eks*)(tdbp->tdb_key_e))[1]),
(caddr_t)(&((struct des_eks*)(tdbp->tdb_key_e))[2]),
(caddr_t)iv, 1);
由tdbp->tdb_encalg决定如何进行认证:
AH_MD5:
AH_SHA:
IPPROTO_AH:
填写ahp;...
由tdbp->tdb_encalg决定如何进行认证:
AH_MD5:
AH_SHA:
IPPROTO_IPIP:
构造IP头;
⑥ 计算IP头校验,调整skb内部参数,tdb循环处理;
⑿. 取出源、目的地址,再次求er;
⒀. 还要继续循环吗?
⒁. while(/*((orgdst != newdst) || (orgsrc != newsrc))*/(orgedst != outgoing_said.dst.s_addr) &&outgoing_said.dst.s_addr &&er);
⒂. 硬件头处理;
⒃. 求物理接口,寻路由;
⒄. 发送。ip_send(skb)
目的: 将某个绑定了的虚接口解除绑定。
参数:
返回值: 0 ――成功,非0值――未成功
算法描述: 将prv的各个值清空
目的: 清除所有的绑定虚接口。
参数: 无
返回值: 0 ――成功,非0值――未成功
算法描述: 将prv的各个值清空
目的: 清除4个注册的ipsec虚接口。
参数: 无
返回值: 0 ―― 清除成功,非0值――清除未成功
算法描述:
1.调用系统调用unregister_netdev(&dev_ipsec0~3)清除虚拟接口;
2.调用系统调用kfree_s(dev_ipsec0~3.priv,sizeof(struct ipsecpriv))释放为虚接口分配的空间。
主要负责处理接收到的ipsec数据包。
ipsec_rcv.c文件
如图3-1所示。

目的: 接收并处理ipsec数据包。
参数: skb - 接收到的数据包,xlen - ?
返回值: 0 ―― 登记成功,非0值――登记未成功
算法描述:
1. SKB正确性判断(有无数据,头长度等);
2. 如果包被克隆过,对包进行一个留头处理,为后面做准备;
3. 是AH或ESP格式吗(只支持这两种格式)?不是则丢弃包;
4. 进入解报循环;
⑴ 是ESP包但长度不是4字节的倍数,丢弃。(因ESP包会有补齐验证,见组报);
⑵ 计算ESP或AH头,求出said (spi, proto, 等);
⑶ 若为AH头,计算头长度和下一个头;
⑷ 为tdb链加锁;
⑸ 取出tdbp(TDB头),若为空,丢弃包;
⑹ tdb的状态对吗? larval, dead 以及超时(有多种情况)均将丢弃包;
⑺ 根据认证标志tdbp->tdb_authalg对数据包进行验证;
⑻ 对ESP包进行解密;
⑼ 计算新的IP数据包头;
⑽ 解锁; (中间丢弃包时也先解锁)
⑾ 如果新的数据包还是IPSec包,循环处理;
5. 对tdb链进行处理;
6. 把解开的包送入IP接收队列。 netifrx(skb)。
实现了对SA数据库的初始化,添加、删除或更新SA。
ipsec_tdb.h, ipsec_tdb.c
struct tdb /* tunnel descriptor block */
{
struct tdb *tdb_hnext; /* next in hash chain */
struct tdb *tdb_onext; /* next in output */
struct tdb *tdb_inext; /* next in input (prev!) */
struct ifnet *tdb_rcvif; /* related rcv encap interface */
struct sa_id tdb_said; /* SA ID */
__u32 tdb_seq; /* seq num of msg that set this SA */
__u32 tdb_pid; /* PID of process that set this SA */
#if 1
struct xformsw *tdb_xform; /* transformation to use (host order)*/
caddr_t tdb_xdata; /* transformation data (opaque) */
#endif
__u8 tdb_authalg; /* auth algorithm for this SA */
__u8 tdb_encalg; /* enc algorithm for this SA */
__u32 tdb_alg_errs; /* number of algorithm errors */
__u32 tdb_auth_errs; /* number of authentication errors */
__u32 tdb_encsize_errs; /* number of encryption size errors */
__u32 tdb_encpad_errs; /* number of encryption size errors */
__u32 tdb_replaywin_errs; /* number of pkt sequence errors */
__u8 tdb_replaywin; /* replay window size */
__u8 tdb_state; /* state of SA */
__u32 tdb_replaywin_lastseq; /* last pkt sequence num */
__u64 tdb_replaywin_bitmap; /* bitmap of received pkts */
__u32 tdb_replaywin_maxdiff; /* maximum pkt sequence difference */
__u32 tdb_flags; /* generic xform flags */
__u32 tdb_lifetime_allocations_c; /* see rfc2367 */
__u32 tdb_lifetime_allocations_s;
__u32 tdb_lifetime_allocations_h;
__u64 tdb_lifetime_bytes_c;
__u64 tdb_lifetime_bytes_s;
__u64 tdb_lifetime_bytes_h;
__u64 tdb_lifetime_addtime_c;
__u64 tdb_lifetime_addtime_s;
__u64 tdb_lifetime_addtime_h;
__u64 tdb_lifetime_usetime_c;
__u64 tdb_lifetime_usetime_s;
__u64 tdb_lifetime_usetime_h;
__u64 tdb_lifetime_packets_c;
__u64 tdb_lifetime_packets_s;
__u64 tdb_lifetime_packets_h;
__u64 tdb_lifetime_usetime_l; /* last time transform was used */
struct sockaddr *tdb_addr_s; /* src sockaddr */
struct sockaddr *tdb_addr_d; /* dst sockaddr */
struct sockaddr *tdb_addr_p; /* proxy sockaddr */
__u16 tdb_addr_s_size;
__u16 tdb_addr_d_size;
__u16 tdb_addr_p_size;
__u16 tdb_key_bits_a; /* size of authkey in bits */
__u16 tdb_auth_bits; /* size of authenticator in bits */
__u16 tdb_key_bits_e; /* size of enckey in bits */
__u16 tdb_iv_bits; /* size of IV in bits */
__u8 tdb_iv_size;
__u16 tdb_key_a_size;
__u16 tdb_key_e_size;
caddr_t tdb_key_a; /* authentication key */
caddr_t tdb_key_e; /* encryption key */
caddr_t tdb_iv; /* Initialisation Vector */
__u16 tdb_ident_type_s; /* src identity type */
__u16 tdb_ident_type_d; /* dst identity type */
__u64 tdb_ident_id_s; /* src identity id */
__u64 tdb_ident_id_d; /* dst identity id */
__u8 tdb_ident_len_s; /* src identity type */
__u8 tdb_ident_len_d; /* dst identity type */
caddr_t tdb_ident_data_s; /* src identity data */
caddr_t tdb_ident_data_d; /* dst identity data */
#if 0
sens
#endif
};
目的: 初始化tdb链。
参数: 无
返回值: 0 ―― 成功,非0值――失败
算法描述: 将tdb链清空。
目的:
参数:
返回值: 0 ―― 成功,非0值――失败
算法描述:
目的: 将一个新的tdb块-tdbp加入在tdbh链中
参数: tdbp ―― 要添加的tdb块
返回值: 0 ―― 成功,非0值――失败
算法描述:
1. 判断传入的tdbp是否为空,若为空,返回失败信息;
tdbh[hashval]=tdbp;
目的: 根据给出的said值查找相应的tdb块。
参数: said ―― 与要查找的tdb块相应的said值
返回值: struct tdb ―― 找到的tdb块,NULL ―― 失败
算法描述:
1.判断said值是否有效,若为空,返回NULL;
2.根据said计算hashval;
3.根据hashval值找到所要的tdb块,失败则返回NULL。
目的: 删除某个指定的tdb块。
参数: tdbp ―― 要删除的tdb块
返回值: 0 ―― 成功,非0值―― 失败
算法描述:
1.判断tdbp的有效性,若无效,则返回失败信息;
2.计算hashval值;
3.根据hashval找到tdbp,并删除它,若失败,返回错误。
目的: 删除整个tdbp链。
参数: tdbp ―― 要删除的tdb链
返回值: 0 ―― 成功,非0值―― 失败
算法描述:
1.判断tdbp是否有效,若无效,返回错误信息;
2.将tdbp移至最后的tdbp->tdb_onext;
3.删除所有的tdb块。
目的: 将指定tdb块中的所有值清空。
参数: tdbp ―― 要清空的tdb块
返回值: 0 ――成功,非0值―― 失败
算法描述: 将所有值置为NULL。
实现了对SPD数据库的初始化,添加、删除eroute。
radij.c, ipsec_radij.c
struct eroute
{
struct rjtentry er_rjt;
struct sa_id er_said;
struct sockaddr_encap er_eaddr;
struct sockaddr_encap er_emask;
};
目的: 初始化radij树
参数: 无
返回值: 0 ―― 成功,非0值―― 失败
算法描述:调用rj_init()函数初始化。
目的: 根据said值,生成新的eroute项。
参数: eaddr ―― 封装的有效目的地址,
emask ―― 封装的目的地址掩码,
said ―― 传入的said值
返回值: 0 ――成功,非0值―― 失败
算法描述:
1.分配eroute空间――retrt,并先清0;
2.给retrt赋值:retrt->er_eaddr = *eaddr; retrt->er_emask = *emask; retrt->er_said = said;
3.给eroute表加锁;
4.调用函数rj_addroute(&(retrt->er_eaddr), &(retrt->er_emask), rnh,
retrt->er_rjt.rd_nodes),向eroute表中加入新生成的该项;
5.解锁,返回。
目的: 删除指定的route。
参数: eaddr ―― 有效目的地址,emask - 目的地址的掩码
返回值: 0 ―― 成功,非0值―― 失败
算法描述:
1.锁住eroute表;
2.调用函数rj_delete(eaddr, emask, rnh, &rn)删除这个指定route;
3.解锁,将该route项清0,调用系统调用kfree释放该空间。
目的: 接收并处理ipsec数据包。
参数: eaddr ―― 要查找的route所具有的eaddr值
返回值: struct eroute ―― 找到的eroute值,NULL ―― 未找到
算法描述:
1.调用函数rj_match((caddr_t)eaddr, rnh),根据eaddr查找相应的eroute项;
2.返回找到的eroute项。
目的: 清空eroute表。
参数: 无
返回值: 0 ―― 成功,非0值―― 失败
算法描述:首先,锁住eroute表;调用函数radijcleartree()函数;解锁,返回。
目的:
参数: 无
返回值: 0 ―― 成功,非0值―― 失败
算法描述:首先,锁住eroute表;调用函数radijcleanup()函数;解锁,返回。
前面所描述的KLIPS模块,基本完成了对输入、输出数据包的加密、认证工作,但其前提是处理数据包的SA已经协商完毕,而这个SA的协商工作正是由IKE所负责的。在本程序中,Pluto模块是IKE的一个实现,它可以自动完成两个主机或网关间的安全联盟的协商工作。
――――Pluto 源文件目录
|
|――main.c 启动后台进程pluto的主程序
|――server.c 监听IKE消息、whack消息和pfkey消息,调用相应处理过程
|
|――demux.c 由server调用,处理IKE消息
|――demux.h 处理IKE消息的头文件,主要定义消息的结构
|――ipsec_doi.c 描述建立ISAKMP SA和IPSEC SA的过程
|――state.h state头文件,主要定义state结构
|――state.c 描述对state的各种操作,为建立SA服务
|――connections.h connection头文件,定义connection结构
|――connections.c 描述对connection的各种操作,为建立SA服务
|――spdb.c 构造和解析SA载荷,为建立SA服务
|――packet.c 定义了各种ISAKMP包的消息头格式,以及构造消息头的算法和解析消息头的算法
|――constants.c 与建立SA相关的各种定义
|――cookie.c 描述对cookies值的各种操作
|――crypto.c 调用加密算法的接口模块
|――id.c ID的表示及操作
|――timer.c 处理时间事件
|――rnd.c 随机数函数模块
|――sha1.c SHA-1散列函数模块
|――md5.c MD5散列函数模块
|――dsa.c DSA签名模块
|――preshared.c 共享密钥的处理模块
|――defs.c 其它一些函数的定义
|――dnskey.c
|――primegen.c
|――elgamal.c
|――gcryptfix.c
|
|――kernel_comm.c 由server调用,处理whack消息
|――whack.c 接收ipsec whack命令,构造相应的whack消息,发送到whackfd,由server.c监听,发到kernel_comm.c处理
|
|――kernel.c 由server调用,处理pfkey消息,操作内核中的SADB库
|
|――log.c 日志处理模块
|――version.c
启动Pluto后台进程。
main.c
目的:分析使用程序的参数,做pluto进程的初始化工作
参数: argc
argv
返回值:0 ―― 成功,非0值 ―― 失败
算法:
1.分析使用pluto的命令行参数,并做相应的操作;
2.调用函数init_log(),初始化日志;
3.调用函数init_rnd_pool(),初始化随机数池;
4.调用函数init_secret(),生成密钥值;
5.调用函数init_states(),初始化状态表(state table);
3.调用函数init_crypto(),做好加密准备;
4.调用函数init_demux(),填充state_microcode_table;
5.调用函数call_server(),启动服务器,监听到来的ISAKMP包和Whcak信息。
目的:生成一个lockfile文件,以保证同时只有一个pluto进程运行
参数: 无
返回值:0 ―― 成功,非0值 ―― 失败
算法:略
目的:删除lockfile文件
参数: 无
返回值:0 ―― 成功,非0值 ―― 失败
算法:略
目的:以status的状态离开pluto进程。
参数: status ―― 0 ok
1 general discomfor
10 lock file exists
返回值:无
算法:略
该模块主要用于监听SA协商消息――IKE消息、whack消息和pfkey消息,收到上述几种消息后,再调用相应处理模块。
server.c文件
目的:监听SA协商消息――IKE消息、whack消息和pfkey消息,收到上述几种消息后,再调用相应处理模块。
参数:无
返回值:无
算法:
1.调用函数init_pfkeyed(),创建PF_KEY类型的套接口;
2.调用函数init_whackfd(),创建AF_UNIX型套接口,用于通信;
2.进入循环等待,接收到来的消息:
如果收到IKE协商消息,调用函数comm_handle()处理;
如果收到whack消息,调用函数whack_handle()处理;
如果收到pfkey消息,调用函数pfkey_handle()处理。
目的:
参数:无
返回值:无
算法:
目的:创建PF_KEY型的套接口
参数:无
返回值:-1 ―― 失败, 其它 ―― 成功
算法:调用系统调用socket()创建socket,返回之。
目的:创建AF_UNIX型的套接口。
参数:无
返回值: -1 ―― 失败, 其它 ―― 成功
算法:调用系统调用socket()创建套接口,并用bind()绑定到ctl_addr上,调用系统调用listen()监听。
协商ISAKMP SA和IPSEC SA。
static stf_status main_outI1(int whack_sock, struct connection *c, bool pending_quick, lset_t policy, unsigned long try)
目的:发起连接c的SA的协商,在这个初始协商消息中,将发送发起方的cookie 值,和对ISAKMP消息的保护方案。
参数:
whack_sock ―― whack套接口
c ―― 这个初始协商消息是为c这个连接发出的
pending_quick ―― 标识在生成ISAKMP SA后,是否需要立即协商IPSEC SA
policy ―― 安全策略
try ―― 发起方最多可以重试的协商次数
返回值:STF_NO_REPLY ―― 成功;其它 ―― 有错误
算法:
1.调用函数new_state(),为这个connection-c构造一个新的state;
2.给这个state结构st的成员变量赋值,其中部分值如下:
st->st_connection = c; //这个state是属于c的
st->st_pending_quick = pending_quick; //st_pending_quick是个bool型变量,其含意为:是否need to build IPsec SA after ISAKMP SA
st->st_policy = policy; // policy for IPsec SA
st->st_whack_sock = whack_sock; // fd for our Whack TCP socket.
st->st_try = try; // number of times rekeying attempted
st->st_state = STATE_MAIN_I1; // State of exchange;
3.调用函数get_cookie,得到发起方的初次cookie值;
4.调用函数insert_state(st),根据cookies值、connection和msgid值,将这个state插入hash表中;
5.调用函数event_schedule(),将事件插入事件队列;
6.调用函数init_pbs(),准备构造第一条ISAKMP消息,初始化包结构;
7.构造ISAKMP头:
⑴.定义一个类型为isakmp_hdr的变量hdr,先清空;
⑵.填充hdr:
hdr.isa_version = ISAKMP_MAJOR_VERSION << ISA_MAJ_SHIFT | ISAKMP_MINOR_VERSION; //ISAKMP消息头格式的版本号
hdr.isa_np = ISAKMP_NEXT_SA; //下一载荷
hdr.isa_xchg = ISAKMP_XCHG_IDPROT; //交换类型
memcpy(hdr.isa_icookie, st->st_icookie, COOKIE_SIZE);//填充cookies
⑶.由于上述生成的包为主机格式的,应该转化为网络字节格式,调用函数out_struct()完成;
8.构造SA:
⑴.求auth_policy,即使用何种认证方式;
⑵.调用函数out_sa(),构造SA载荷头、proposal载荷;
⑶.保留发起方的SA;
9.调用函数close_message()、close_output_pbs(),至此生成ISAKMP消息完毕;
10.调用函数send_packet(),将包发送出去;
11.建立重发机制;
12.返回状态为:STF_NO_REPLY。
stf_status main_inI1_outR1(struct msg_digest *md)
目的:响应方接受到发起方的第一个协商消息后,发出自己的cookies值,和选择自己可以接受的保护方案
参数: md ―― 消息摘要,接收到的消息
返回值:STF_REPLY ―― 成功, 其它 ―― 有错误
算法:
1.根据收到的消息,查找相应的connection-c;
2.调用函数new_state(),生成一个新的state;
3.给state的成员变量赋值,如下:
st->st_connection = c;
st->st_try = 0; //由于是响应方,重试的工作不需要我们负责
st->st_policy = c->policy;
memcpy(st->st_icookie, md->hdr.isa_icookie, COOKIE_SIZE);//st_icookie
get_cookie(ISAKMP_RESPONDER, st->st_rcookie, COOKIE_SIZE, md->sin.sin_addr); //填充st_rcookie
4.调用函数insert_state(),将这个state插入hash表中;
5.构造ISAKMP消息头:
⑴.定义一个类型为isakmp_hdr的变量r_hdr;
⑵.将st_rcookie值拷贝至r_hdr中;
⑶.将下一载荷头设为SA载荷头;
⑷.由于上述生成的包为主机格式的,应该转化为网络字节格式,调用函数out_struct()完成;
6.构造SA:
⑴.定义类型为isakmp_sa的变量r_sa,给它赋值;
⑵.给r_sa的下一载荷赋值为NULL,即后面不再跟有载荷;
⑶.调用函数out_struct(),构造网络字节顺序的r_sa_pbs;
7.调用函数parse_isakmp_sa_body(),选择要采用的保护方案;
6.调用函数close_message(),完成消息的填写,返回状态:STF_REPLY。
stf_status main_inR1_outI2(struct msg_digest *md)
目的:发起方的第二个协商消息,发送D-H公开值和随机数。
参数: md ―― 消息摘要,接收到的消息
返回值:STF_REPLY ―― 成功, 其它 ―― 有错误
算法:
1.调用函数parse_isakmp_sa_body(),核实我们收到的proposal与我们发出的相符;
2.构造外出包的HDR,KE和Ni:
⑴.定义类型为isakmp_hdr的变量r_hdr,将它的下一载荷头设为KE(密钥交换)载荷;
⑵.调用函数out_struct();
⑶.构造KE(密钥交换)载荷,且将它的下一载荷头设为NONCE载荷,调用函数build_and_ship_KE();
⑷.构造NONCE载荷,其后不再跟有载荷,调用函数build_and_ship_nonce();
3.结束消息构造,调用函数close_message();
4.将原来的state从hash表中删掉,加入现在这个含有了st_rcookie值的state,且将state的交换状态设为STATE_MAIN_I2;
5.返回STF_REPLY。
stf_status main_inI2_outR2(struct msg_digest *md)
目的:响应方的第二个协商消息,发送D-H公开值和随机数。
参数:md ―― 消息摘要,接收到的消息
返回值:STF_REPLY ―― 成功, 其它 ―― 有错误
算法:
1.检查并接受对方消息中的DH公开值Gi,调用函数accept_KE();
2.检查并接受对方消息中的随机数NONCE-Ni,调用函数accept_nonce();
3.构造外出包的构造外出包的HDR,KE和Ni:
⑴.构造KE(密钥交换)载荷,且将它的下一载荷头设为NONCE载荷,调用函数build_and_ship_KE();
⑵.构造NONCE载荷Nr,其后不再跟有载荷,调用函数build_and_ship_nonce();
4.结束消息构造,调用函数close_message();
5.计算dh值,调用函数compute_dh_shared(),得到state->st_shared,该值为双方根据公开值Gi/Gr和随机数Ni/Nr生成的一个公有秘密值;
6.调用函数gernerate_skeyid(),生成下列各值:
SKEYID(密钥材料,用于生成后几个密钥值),
SKEYID_D(用于产生IPsec SA的密钥),
SKEYID_A(用于认证(后继的)ISAKMP消息的密钥),
SKEYID_E(用于加密ISAKMP交换的密钥),
新的IV值;
7.将state中的iv值更新为刚刚生成的iv值,更新state的状态为STATE_MAIN_R2,返回STF_REPLY。
stf_status main_inR2_outI3(struct msg_digest *md)
目的:收到响应方的D-H公开值和随机数,计算D-H值和其它密钥材料材料。
参数:md ―― 消息摘要,接收到的消息
返回值:STF_REPLY ―― 成功, 其它 ―― 有错误
算法:
1.检查并接受对方消息中的DH公开值Gr,调用函数accept_KE();
2.检查并接受对方消息中的随机数NONCE-Nr,调用函数accept_nonce();
3.计算dh值,调用函数compute_dh_shared(),得到state->st_shared,该值为双方根据公开值Gi/Gr和随机数Ni/Nr生成的一个公有秘密值;
4.调用函数gernerate_skeyid(),生成下列各值:
SKEYID(密钥材料,用于生成后几个密钥值),
SKEYID_D(用于产生IPsec SA的密钥),
SKEYID_A(用于认证(后继的)ISAKMP消息的密钥),
SKEYID_E(用于加密ISAKMP交换的密钥),
新的IV值;
5.构造外出包的IDii(发起方的身份标识):
调用函数build_id_payload()构造身份载荷,调用函数out_struct();
6.构造外出包的HASH_I或SIG_I;
7.加密ISAKMP包,除了ISAKMP包头部分;
8.更新state的状态为STATE_MAIN_I3,返回STF_REPLY。
stf_status main_inI3_outR3(struct msg_digest *md)
目的:响应方的第三个协商消息,验证发起方的身份,发送自己的身份消息。
参数:md ―― 消息摘要,接收到的消息
返回值:STF_REPLY ―― 成功, 其它 ―― 有错误
算法:
1.根据收到的消息的认证方式判断,它是使用的HASH载荷还是签名载荷;
2.解析对方的身份(ID),调用函数decode_peer_id();
3.检验HASH载荷或签名载荷,返回一个notification_t类型的结果;
4.计算外出的IDir(响应方的身份标识):
调用函数build_id_payload()构造身份载荷,调用函数out_struct();
5.构造外出包的HASH_R或SIG_R;
6.加密ISAKMP包,除了ISAKMP包头部分;
5.更新state的状态为STATE_MAIN_I3,返回STF_REPLY。
目的:接收到响应方的最后一个消息后,做相应的处理工作。
参数:md ―― 消息摘要,接收到的消息
返回值:STF_REPLY ―― 成功, 其它 ―― 有错误
算法:
1.根据收到的消息的认证方式判断,它是使用的HASH载荷还是签名载荷;
2.解析对方的身份(ID),调用函数decode_peer_id();
3.检验HASH载荷或签名载荷,返回一个notification_t类型的结果,使用函数check_main_authenticator();
4.更新state状态为STATE_MAIN_I4,将这个state的序列号通知给这个connection;
5.返回STF_UPEND_QUICH,这表示第一阶段已经成功完成,可以初始化第二阶段。
stf_status quick_outI1(int whack_sock, struct state *isakmp_sa, struct connection *c, unsigned long try)
目的:发起方开始协商IPsec SA的第一个消息,在此消息中,发送散列值HASH-1、一个或多个供响应方选择的安全联盟方案(每个安全联盟方案包括随机生成的安全参数索引SPI、提案—安全协议AH或ESP、变换—具体的加密算法/散列算法及其SA属性)、发送方的nounce Nii、发送方的D-H公开值gix、发送方的IDii 。其中,gix、IDii及IDir可选(用于提供PFS服务)。
参数:
whack_sock ―― whack套接口
isakmp_sa ―― 已经协商好的ISAKMP SA
c ―― 拥有这个IPSEC SA的连接(connection)
policy ―― 安全策略
try ―― 协商时的最大重试次数
返回值:STF_NO_REPLY ―― 成功 , 其它 ―― 有错误
算法:
1.调用函数duplicate_state(isakmp_sa),复制一个协商好的ISAKMP 的state;
2.填充state的部分成员变量值,调用函数insert_state()将这个st插入hash表中;
3.调用函数init_pbs(),开始构造外出的协商消息,初始化;
4.构造HDR头;
5.构造SA载荷,调用函数out_sa();
6.构造NONCE载荷,调用函数build_and_ship_nonce();
7.如果选择了pfs(完美向前保密),则构造密钥交换载荷(KE),调用 函数build_and_ship_KE();
8.构造IDci和IDcr;
9.调用函数close_output_pbs(),完成消息的构造;
10.调用函数quick_mode_hashI2(),计算HSAH(1),放到output中;
11.调用函数init_phase2_iv(),生成iv值,调用函数encrypt_message(),加密信息;
12.保存该包,并调用函数send_packet(),发送此消息包,返回STF_NO_REPLY。
stf_status quick_inI1_outR1(struct msg_digest *md)
目的:散列值HASH-2、响应方选择的安全联盟方案(其中的SPI可与消息1中同一提案的SPI不同)、响应方的nounce Nir、响应方的D-H公开值giy、响应方的IDir 。
参数:md ―― 接收到的对方发送的协商消息
返回值:STF_REPLY ―― 成功 , 其它 ―― 有错误
算法:
1.取出并赋值ISAKMP的state,使用函数duplicate_state();
2.填充state的部分成员变量值;
3.检测收到的HASH(1)是否正确,调用宏CHECK_QUICK_HASH();
4.构造SA载荷,选择接受的策略;
5.调用函数accept_nonce(),检查发起方的随机数;
6.调用函数accept_PFS_KE(),检查发起方D-H公开值;
7.确认对方身份,使用函数decode_net_id();
8.调用函数find_client_connection(),查找相应的connection;
9.构造响应消息,包括NONCE、KE、IDci、IDcr等等,过程与上个消息中的构造过程基本相同;
10.调用函数compute_keymats,计算密钥材料;
11.调用函数install_inbound_ipsec_sa(),安装进入的SA;
12.调用函数encrypt_message(),将构造好的协商消息加密;
13.更新st的状态,返回STF_REPLY。
stf_status quick_inR1_outI2(struct msg_digest *md)
目的:
参数:md ―― 接收到的对方发送的协商消息
返回值:STF_REPLY ―― 成功 , 其它 ―― 有错误
算法:
1.验证响应方的身份,与2.2中的3~6步基本相同;
2.构造响应消息,包括HDR、HASH(3)等等;
3.调用函数compute_keymats(),计算密钥材料;
4.调用函数install_ipsec_sa(),安装SA;
5.调用函数encrypt_message()加密响应消息,更新st的状态,返回STF_REPLY。
stf_status quick_inI2(struct msg_digest *md)
目的:协商结束,安装IPSEC SA。
参数:md ―― 接收到的对方发送的协商消息
返回值:STF_NO_REPLY ―― 成功 , 其它 ―― 有错误
算法:
1.调用函数install_ipsec_sa(),安装IPSEC SA;
2.更新st的状态,返回STF_NO_REPLY。
3.4.3.2.3.1.1 connections ―― 描述了一个连接的各个属性
struct connection {
char name; /*连接的名称,是一个唯一的字符串*/
lset_t policy; /*typedef unsigned long lset_t,安全策略*/
time_t sa_ike_life_seconds; /*ISAKMP SA 的存活时间*/
time_t sa_ipsec_life_seconds; /*IPSEC SA的存活时间*/
time_t sa_rekey_margin; /*在SA作废之前多长的时间那,发起方应该重新协商*/
unsigned long sa_rekey_fuzz; /**/
unsigned long sa_keying_tries; /*重新连接的重试次数*/
struct end
this, /*连接的这一端*/
that; /*连接的另外一端*/
/* internal fields: */
const struct iface *interface; /* filled in iff oriented */
bool routed; /*是否已有到对方的路由?*/
so_serial_t /* state object serial number ,是一个state 对象的序列号*/
newest_isakmp_sa, /*最新的ISAKMP的state的序列号*/
newest_ipsec_sa, /*最新的IPSEC的state的序列号*/
eroute_owner; /*只有一个SA(IPsec state)可以拥有(own)这个eroute,eroute_owner是这个state的序列号,如果没有,则为SOS_NOBODY*/
enum rwcs {
rwcs_permanent, /* normal connection */
rwcs_instance, /* instance created for a particular attempt */
rwcs_going_away /* being deleted -- don't delete again */
};
enum rwcs rw_state;
#ifdef DEBUG
unsigned int extra_debugging;
#endif
struct host_pair *host_pair;
struct connection *hp_next;
struct connection *next;
};
3.4.3.2.3.1.2 end ―― /*此结构描述了一方主机的各种状态*/
struct end {
struct id id; /*ID*/
struct in_addr /* network order */
host_addr, /*本机IP*/
host_nexthop, /*下一跳IP*/
client_net, client_mask; /*客户子网IP,掩码*/
bool has_client; /*是否有客户子网*/
char *updown; /*防火墙脚本*/
u_int16_t host_port; /* host order */
};
3.4.3.2.3.1.3 host_pair ―― /*该结构描述了一个主机对的各种状态*/
struct host_pair {
struct
{
struct in_addr addr; /* network order */
u_int16_t port; /* host order */
} a, b;
bool initial_connection_sent;
struct connection *connections; /* connections with this pair */
struct host_pair *next;
};
目的:初始化一个指定名称的connection。
参数:
name ―― 要初始化的connection的名称
whackfd ――
返回值:无
算法:
1.调用函数con_by_name(name, TRUE),获得该连接c;
2.如果该连接存在,调用宏SET_CUR_CONNECTION()将其设为当前连接;
3.调用函数orient(c, TRUE),判断该连接是否有???若无,跳出;
4.调用函数HasWildcardIP(*c),判断是否对方IP是通配符,若是,跳出;
5.调用函数ipsecdoi_initiate(whackfd,c,…),???;
6.调用宏UNSET_CUR_CONNECTION()。
目的:检查并分析whack_message,根据它,加入一个connection。
参数:
wm ――
返回值:无
算法:
1.合法性检查,如:是否要添加的connection的名称已经存在,connection中的IP地址都合法,等等;如果有错误,返回;
2.使用alloc_thing()分配一个结构为connection的空间c;
3.对c的各个属性项赋值;
4.调用函数unshare_connection(c);
5.调用函数connect_to_host_pair(c),找到这个connection的host_pair
目的:删除一个connection。
参数:
c ―― 要删除的connection
返回值:无
算法:
1.保留当前连接,将这个c设为当前连接;
2.从connections list中,删除c;
3.从host pair list中,删除c;
4.释放c的空间。
目的:清空connections list。
参数:无
返回值:无
算法:略
目的:终止一个connection。
参数:
nm ―― connection的名称
返回值:无
算法:
根据nm找到connection,调用delete_states_by_connection(),删除之。
目的:终止一个connection。
参数:
c ―― 一个指向connection的指针
返回值:
TRUE
―― c.interface
== NULL ,FALSE ―― c.interface != NULL
算法:
如果(*c).interface等于NULL,返回TRUE;
将协商好的SA安装到内核中的SADB库中,或按照要求从SADB中删除作废的SA,等等。原理是,构造pfkey消息,发送到pfkey套接口,内核的SADB引擎收到后,解析消息,完成相应操作。(pfkey的详情参见PF_KEY模块)
kernel.c 文件
目的: 从读取pfkeyfd套接口读取pfkey消息,并分析此消息
参数: 无
返回值:无
算法:
1.调用系统调用read(),从pfkeyfd套接口读取pfkey消息;
2.调用函数pfkey_msg_parse()分析收到的消息;
目的: 生成唯一的SPI值
参数: avoid
返回值:ipsec_spi_t
算法:略
目的: 启动_updown脚本,执行其中的防火墙命令规则
参数:
返回值:
算法:
目的: 调用其它构造函数,如果构造成功,返回TRUE,失败,在释放extensions的空间返回FALSE。
参数:
返回值:TRUE ―― ,FALSE ――
算法:略
目的: 结束pfkey消息的构造。
参数:
extensions[] ――
description ――
text_said ――
返回值:FALSE ―― 失败,TURE ―― 成功
算法:
1.调用函数pfkey_msg_build(&pfkey_msg,extensions,EXT_BITS_IN),构造pfkey消息,若构造失败,返回FALSE;
2.调用系统调用write(pfkeyfd,pfkey_msg,len),将pfkey消息发送出去如发送失败,则返回FALSE;
3.调用函数pfkey_extensions_free(extensions)和pfkey_msg_free(&pfkey_msg)释放空间。
目的: 根据要建立的一个SA,构造一个eroute表项(构造和发送的都是地址消息)。
参数:
st ―― 描述一个SA的状态
op ――
UNUSED ――
返回值:TRUE ―― 成功,FALSE ―― 失败
算法:
1.根据st获得SPI值和protocol;
2.调用函数set_text_said(),将计算所得的said放入text_said中;
3.将六个地址转化为结构的变量:this.host_addr.s_addr、this.client_net.s_addr、this.client_mask.s_addr、that.host_addr.s_addr、that.client_net.s_addr、that.client_mask.s_addr;
4.调用函数pfkey_extensions_init(extensions),初始化extensions;
5.调用消息构造函数pfkey_build构造消息,并调用finish_pfkey_build()完成构造,把消息发送出去。
目的: 删除一个SA。
参数:
spi ―― SPI值
proto ―― 协议
src ―― 源地址
dst ―― 目的地址
返回值:TRUE ―― 成功,FALSE ―― 失败
算法:
1.调用函数pfkey_extensions_init(extensions),进行构造消息之前的初始化;
2.调用函数set_text_said(),设置text_said值;
3.调用函数pfkey_build(),构造删除一个SA的pfkey消息,调用函数finish_pfkey_build()结束构造,并发送。
目的: 建立半个SA(即,两个网关或主机之间,对于其中某一个来说,数据包进入或出去的所用SA)
参数:
st ―― 描述一个SA的状态
INBOUND ――表示此SA是进入的还是外出的
返回值:TRUE ―― 成功,FALSE ―― 失败
算法:
根据st,调用函数pfkey_build()构造合适的pfkey消息,再调用函数finish_pfkey_build()发送;若此过程中有错误发生,则返回FALSE,否则,返回TRUE。
目的: 删除半个SA(即,两个网关或主机之间,对于其中某一个来说,数据包进入或出去的所用SA)
参数:
st ―― 描述一个SA的状态
INBOUND ――表示此SA是进入的还是外出的
返回值:TRUE ―― 成功,FALSE ―― 失败
算法:
根据st,取出该SA中设置的spi、proto,调用函数del_spi删除它。
目的: ???
参数:
c ―― 描述一个connection
ero ――
返回值:TRUE ―― 成功,FALSE ―― 失败
算法:
目的: 安装SA,仅被被响应方使用,响应方接着会用install_ipsec_sa来安装outbound的SA;而发起方使用install_ipsec_sa来安装inbound和outbound的SA。
参数:
st ―― 描述一个SA的状态
返回值:TRUE ―― 成功,FALSE ―― 失败
算法:
1.如果对方的client有固定的IP地址,检查我们是否有一条达到那里的路由,这条路由与现在的相矛盾(不知道这样说对不对);如果有,则删除之;
2.调用函数could_eroute(c, route_owner(c,TRUE)),检查我们是否有eroute表项,如果没有,返回FALSE;
3.调用函数route_connection(c, FALSE),检查我们是否可以将外出的数据包路由到某个ipsec虚接口。事实上,这只检查了我们的peer不在他的子网中,或者如果他在,我们使用UDP 500端口的IKE消息,使得它不被处理;
4.调用函数setup_half_ipsec_sa(st),建立进入的SA。
目的: 安装SA。
参数:
st ―― 描述一个SA的状态
inbound_also ―― 标识这个SA是inbound,或outbound
返回值:TRUE ―― 成功,FALSE ―― 失败
算法:
1.调用函数route_owner(c, TRUE),查找是谁拥有这个eroute;
2.调用函数could_eroute(c, ero),???;
3.调用函数route_connection(c, TRUE),检查我们是否可以将外出的数据包路由到某个ipsec虚接口;
4.调用函数setup_half_ipsec_sa(),根据inbound_also,建立进入的或外出的SA;
5.如果建立成功,如果c->eroute_owner为SOS_NOBODY,则调用函数do_eroute(…,”add”)添加eroute表项,再调用函数do_command()启动脚本;如果不是SOS_NOBODY,则调用do_eroute(…,”replace”)更新eroute表项。
目的: 安装SA。
参数:
st ―― 描述一个SA的状态
inbound_only ―― 是否只删inbound的SA
返回值:TRUE ―― 成功,FALSE ―― 失败
算法:
如果inbound_only为TRUE,则调用函数teardown_half_ipsec_sa()删除半个SA;
如果inbound_only为FALSE,判断是否c->eroute_owner == st->st_serialno,如果相等,调用函数do_command(…,”down”)启动脚本关闭,及调用do_eroute(…,”delete”)删除eroute;如果上述步骤成功,再调用teardown_half_ipsec_sa()删除SA。
PF_KEY是一种新型的协议族,由PF_ROUTE衍生而来。在协议族中,PF_KEY被定义为15。目前,实现所依据的时PF_KEY的第二个版本。密钥管理进程可以使用它与操作系统中的“key engine”或安全联盟数据库(SADB)通信,并使用PF_INET通过网络与远程密钥管理进程进行通信。密钥管理进程与PF_KEY的关系如图所示:
+----------------------+
|Key Mgmt Daemon|
+----------------------+
| |
| | Applications =====[PF_KEY]====[PF_INET]==========================
| | OS Kernel
+------------+ +-----------------+
| Key Engine | | TCP/IP, |
| or SADB |---| including IPsec |
+------------+ | |
+-----------------+
|
+-----------+
| Network |
| Interface |
+-----------+
Figure 1: Relationship of Key Mgmt to PF_KEY
该模块大致由四部分组成:
pfkey_v2.c ―― PF_KEYv2 Key management API domain socket I/F
pfkey_v2_build.c ―― PF_KEYv2 Key management API message parser
提供了一个可用于IPsec密钥管理函数库(key management API)。
pfkey_v2_parse.c ―― PF_KEYv2 Key management API message parser
pfkey_v2_parser.c ―― 实现用户层与核心之间对于密钥协商的交互处理
实现pfkey_v2协议族,用于密钥协商进程pluto与内核的管理SADB的进程间通信。
pfkey.h, pfkeyv2.h, pfkey_v2.c , pfkey_v2_build.c , pfkey_v2_parse.c , pfkey_v2_parser.c
PF_KEY有一套独特的消息行为,不像其他大多数BSD socket采用的bind()、connect()、accept()、listen()等操作来实现通信。正常情况下,正确格式的消息发给内核,内核将应答消息返回到PF_KEY socket中。如果内核检测到错误,错误标识会随应答消息一起被发送出去。
PF_KEY有如下几种消息行为:
1.SADB_REGISTER消息:
进程向内核注册一个socket以便能够从内核获得新的SA。消息格式为:<base>,即只包含消息基本头。内核将发送SADB_REGISTER消息进行应答,格式为:<base,supported>,supported扩展表明内核所支持的验证算法和密钥算法。
2.SADB_ACQUIRE消息:
这个消息是由内核发出的,可以用于两种情况:
⑴.内核向密钥管理进程申请创建一个SA。消息格式为:<base,address,proposal>,其中,proposal扩展给出了SA的几种提案,即采用什么协议(AH或ESP)、采用什么验证方法和加密方法等;扩展数据中还可包含的选项有:Identity扩展,Sensitivity扩展。但,密钥管理进程并不向内核发送SADB_ACQUIRE应答消息。
⑵.当密钥协商过程失败后,内核通过发送SADB_ACQUIRE消息来通知密钥管理进程。消息格式为:<base>,即只包含一个消息基本头,其中,消息错误号代表了错误的原因。密钥管理进程不向内核发送SADB_ACQUIRE应答消息。
3.SADB_GETSPI消息:
密钥管理进程向内核申请一个SPI。消息格式为:<base,address,SPI range>,SPI range扩展指出了所申请的SPI的范围。内核发送SADB_GETSPI应答消息,格式为:<base,SA(*),address>,其中,SA(*)表示SA扩展的各个字段,除了扩展长度、扩展类型和SPI外,其余字段应设为0被忽略掉。
4.SADB_UPDATE消息:
密钥管理进程请求内核更新(或)增加一条SA记录,该消息随SADB_GETSPI消息一起使用。消息格式为:<base,SA,address,key>,扩展数据中还可包含的选项有:lifetime扩展、identity扩展、sensitivity扩展。内核发送SADB_UPDATE应答消息,格式为:<base,SA,address>,或者还包含与请求消息相应的扩展数据。
5.SADB_ADD消息:
在手工配置密钥或知道SPI的情况下,无须先使用GETSPI消息而直接申请内核更新(或增加)一条SA记录。消息格式与SADB_UPDATE消息相同。内核发送的SADB_ADD应答消息与SADB_UPDATE应答消息相同。
6.SADB_DELETE消息:
密钥管理进程请求内核删除一条SA记录。消息格式为:<base,SA(*),address>,即根据SA类型、SPI、地址三要素删除相应的SA。内核发送SADB_DELETE应答消息为:<base,SA(*),address>。
7. SADB_GET消息:
密钥管理进程请求从内核检索一条SA记录。消息格式为:<base,SA(*),address>。内核发送SADB_GET应答消息为:<base,SA,address,key>,扩展数据中还可包含的选项有:lifetime扩展、identity扩展、sensitivity扩展,即应答消息给出SA的详细信息。
8. SADB_EXPIRE消息:
内核向密钥管理进程发送SA过期消息。消息格式为:<base,SA,lifetime,address>,lifetime扩展给出了SA的当前时间和过期时间(软过期和硬过期)。密钥管理进程不向内核发送SADB_EXPIRE应答消息。
9. SADB_FLUSH消息:
密钥管理进程请求内核将某种类型的SA从SADB中全部删除。消息格式为:<base>,消息基本头中SA类型字段给出了所要删除的SA类型的值。内核发送SADB_REGISTER应答消息,格式为:<base>。
10. SADB_DUMP消息:
一般用于PF_KEY实现的调试。
下面介绍使用PF_KEY的基本原理:
1.REGISGTER:当密钥管理进程(pluto)开始运行时,它需要通知PF_KEY它要接收两种IPsec的消息 —— AH和ESP(It must tell PF_KEY that it is willing to accept message for the two IPsec services, AH and ESP.):
Pluto -》Kernel: SADB_REGISTER for ESP
Kernel-》Registered: SADB_REGISTER for ESP,Supporeted Algorithms
Pluto -》Kernel: SADB_REGISTER for AH
Kernel-》Registered: SADB_REGISTER for AH,Supporeted Algorithms
每一个REGISTER消息都会导致对所有PF_SOCKET套接口的回应(包括请求者)。
2.SADB_ACQUIRE:
当Kernel IPsec需要数据经AH处理时,它需要有可以使用的SA,则进行如下操作:
1).Kernel -》Registered:SADB_ACQUIRE for AH, addrs, ID, sens, proposals;
2).Pluto读取到这个ACQUIRE消息(尤其注意sadb_msg_seq)后,它就会开始协商SA:
①首先,它向内核发送一个SADB_GETSPI消息,其中的sadb_msg_seq值与刚刚收到的消息中该值相等;即:
Pluto -》Kernel: SADB_GETSPI for AH,addr,SPI range
②内核把GETSPI的结果返回给所有的listening sockets;即:
Kernel -》All: SADB_GETSPI for AH,assoc,addrs
③如果需要双向的SPI值的话,pluto还会再次进行GETSPI操作;
④此时,Pluto已经有了可以使用的SPI值,他可以进行协商了;
⑤在Pluto获取密钥材料,协商好其它参数之后,它将发送一个或多个SADB_UPDATE消息,其中的sadb_msg_seq的值与之前的相同。如果在协商期间发生错误,则:
Pluto -》Kernel: SADB_ACQUIRE for AH,assoc(with an error)
Kernel-》All : SADB_ACQUIRE for AH,assoc(same error)
如果协商成功,则:
Pluto -》Kernel: SADB_UPDATE for AH,assoc,addrs,keys,etc.
Kernel-》All : SADB_UPDATE for AH,assoc,addrs,etc.
即,Kernel会把UPDATE的结果(除了密钥)返回给所有的监听端口。
3.SADB_ADD:
如果只在本地确定了一个SPI值,则另一方必须用SADB_ADD消息添加SA:
Pluto -》Kernel:SADB_ADD for AH,assoc,addrs,keys,etc.
Kernel-》All :SADB_ADD for AH,assoc,addrs,etc.
4.SADB_EXPIRE:
PF_KEY消息是由基本消息头及后面跟随的SA相关数据组成的。
struct sadb_msg {
uint8_t sadb_msg_version; /*PF_KEY消息的版本号,应该为PF_KEY_V2,否则视为错误*/
uint8_t sadb_msg_type; /*描述了该消息的类型,详见下一节*/
uint8_t sadb_msg_errno; /*发送方应该将该变量置为0,而响应方则应在此填写错误代码(如果有错)*/
uint8_t sadb_msg_satype; /*描述了SA的类型*/
uint16_t sadb_msg_len; /*整个消息(包括基本消息头和附近数据)的长度,以64-bit为单位*/
uint16_t sadb_msg_reserved; /*保留值。发送方必须置0。*/
uint32_t sadb_msg_seq; /*这个消息的序列号。这个序列号和sadb_msg_pid一起,可以用于唯一地表示一个对一个进程地请求。发送方负责填写这个字段*/
uint32_t sadb_msg_pid; /*产生这个消息进程的进程号,比如,假设一个进程号为2112的进程向内核发送一个SADB_UPDATE的消息,则必须在此消息中将此位置为2112,而内核的回应消息中,也要置位为2112。*/
};
由于所有的扩展定义都包括这两个字段(len和exttype),所以产生了这个通用扩展头。
跟随在基本消息头后的附加数据可能由不同类型、长度的字段组成。但,头32-bits必须是如下的固定格式:
struct sadb_ext {
uint16_t sadb_ext_len; /*扩展头的长度,以64-bit为单位*/
uint16_t sadb_ext_type; /*扩展头的类型,0值为保留值*/
};
/* sizeof(struct sadb_ext) == 4 */
扩展头的类型有:Association, Lifetime(s),Address(s), Key(s), Identity(ies), Sensitivity, Proposal, and Supported。在一个消息中,同时只能有一个扩展类型(如:<base,key,lifetime>是不允许的)。如果一个消息中有重复的扩展类型,将返回EINVAL。
如果遇到不知类型的扩展头,会被忽略。
此扩展结构定义了一个单独SA的所有属性。只有在发送控制消息(如SADB_FLUSH和SADB_REGISTER)和SADB_ACQUIRE消息中,才不用使用此扩展消息。SA扩展结构如下:
struct sadb_sa {
uint16_t sadb_sa_len;
uint16_t sadb_sa_exttype;
uint32_t sadb_sa_spi; /*SPI值,必须是网络字节顺序*/
uint8_t sadb_sa_replay; /*replay窗口的大小,如果为0,表示没有使用replay window*/
uint8_t sadb_sa_state; /*SA的状态*/
uint8_t sadb_sa_auth; /*该SA采用的认证算法*/
uint8_t sadb_sa_encrypt; /*该SA采用的加密算法*/
uint32_t sadb_sa_flags; /*为SA定义的选项的bitmap*/
};
/* sizeof(struct sadb_sa) == 16 */
lifetime扩展定义了这个SA的一个和几个lifetime变量。如果没有lifetime扩展,则该SA有无限的生命。Lifetime变量有三种类型:硬(HARD)-硬的过期时间,软(SOFT)-软的过期时间,当前(CURRENT)-表示SA的当前状态。Lifetime扩展结构如下:
struct sadb_lifetime {
uint16_t sadb_lifetime_len;
uint16_t sadb_lifetime_exttype;
uint32_t sadb_lifetime_allocations;
uint64_t sadb_lifetime_bytes;
uint64_t sadb_lifetime_addtime;
uint64_t sadb_lifetime_usetime;
};
/* sizeof(struct sadb_lifetime) == 32 */
address扩展定义了SA相关的一个或多个地址(addresses)。如果需要,源和目的地的地址扩展都必须同时提供。其格式如下:
struct sadb_address {
uint16_t sadb_address_len;
uint16_t sadb_address_exttype;
uint8_t sadb_address_proto;
uint8_t sadb_address_prefixlen;
uint16_t sadb_address_reserved;
};
/* sizeof(struct sadb_address) == 8 */
key扩展定义了SA相关的一个或多个key。出于安全考虑,并不总是提供key扩展。其格式如下:
struct sadb_key {
uint16_t sadb_key_len;
uint16_t sadb_key_exttype;
uint16_t sadb_key_bits;/*有效密钥数据的长度,int bits*/
uint16_t sadb_key_reserved;/**/
};
/* sizeof(struct sadb_key) == 8 */
Identity扩展包括端点(endpoint)的身份。密钥管理进程在协商时可以使用其中的信息来选择身份认证方式。这个信息要由内核提供给密钥管理进程,以确认远端实体(remote entity)。如果没有提供这个扩展,则密钥管理进程假定在address扩展中的地址是这个SA中的唯一身份。其结构如下:
struct sadb_ident {
uint16_t sadb_ident_len;
uint16_t sadb_ident_exttype;/*身份信息的类型*/
uint16_t sadb_ident_type; /*标识符*/
uint16_t sadb_ident_reserved; /**/
uint64_t sadb_ident_id; /**/
};
/* sizeof(struct sadb_ident) == 16 */
此扩展包含了一个SA的安全标识信息(security labeling information)。其格式如下:
struct sadb_sens {
uint16_t sadb_sens_len;
uint16_t sadb_sens_exttype;
uint32_t sadb_sens_dpd;
uint8_t sadb_sens_sens_level;
uint8_t sadb_sens_sens_len;
uint8_t sadb_sens_integ_level;
uint8_t sadb_sens_integ_len;
uint32_t sadb_sens_reserved;
};
/* sizeof(struct sadb_sens) == 16 */
此扩展包括了提议的算法,格式如下:
struct sadb_prop {
uint16_t sadb_prop_len;
uint16_t sadb_prop_exttype;
uint8_t sadb_prop_replay;
uint8_t sadb_prop_reserved[3];
};
/* sizeof(struct sadb_prop) == 8 */
在这个扩展头后,跟随struct sadb_comb sadb_combs[(sadb_prop_len *
sizeof(uint64_t) - sizeof(struct sadb_prop)) /
sizeof(struct sadb_comb)];
此扩展包含了本系统支持的所有算法的列表。它告诉了密钥管理进程可以协商哪些算法。支持的认证算法列在SUPPORTED_AUTH中,而加密算法列在SUPPORTED_ENCRYPT中。其格式如下:
struct sadb_supported {
uint16_t sadb_supported_len;
uint16_t sadb_supported_exttype;
uint32_t sadb_supported_reserved;
};
/* sizeof(struct sadb_supported) == 8 */
/* followed by:
struct sadb_alg sadb_algs[(sadb_supported_len *
sizeof(uint64_t) - sizeof(struct sadb_supported)) /
sizeof(struct sadb_alg)]; */
这个头后跟一个或多个算法描述。算法描述如下表示:
struct sadb_alg {
uint8_t sadb_alg_id;
uint8_t sadb_alg_ivlen;
uint16_t sadb_alg_minbits;
uint16_t sadb_alg_maxbits;
uint16_t sadb_alg_reserved;
};
一个PF_KEY消息,比如:SADB_GETSPI,需要合适的SPI值的范围,这个扩展就提供了这样一个功能。其格式如下:
struct sadb_spirange {
uint16_t sadb_spirange_len;
uint16_t sadb_spirange_exttype;
uint32_t sadb_spirange_min;/*可以接受的最小SPI值*/
uint32_t sadb_spirange_max; /*可以接受的最大SPI值*/
uint32_t sadb_spirange_reserved;
};
/* sizeof(struct sadb_spirange) == 16 */
PF_KEY使用以下消息类型,它们在</lib/pfkeyv2.h>中定义:
#define SADB_RESERVED 0
#define SADB_GETSPI 1
#define SADB_UPDATE 2
#define SADB_ADD 3
#define SADB_DELETE 4
#define SADB_GET 5
#define SADB_ACQUIRE 6
#define SADB_REGISTER 7
#define SADB_EXPIRE 8
#define SADB_FLUSH 9
#define SADB_DUMP 10 /* not used normally */
除了以上几种必须实现的消息类型外,PF_KEY还有几种附加的消息类型,如下:
1.SADB_X_PROMISC消息:
应用程序可以通过使用这种消息,以混杂模式(promiscuous mode)发送和接收消息。这个消息有两种形式:控制(control)和数据(data)。控制形式仅由消息头构成,这个消息用于栓牢混杂-接收功能(toggle the promiscuous-receive function)。数据形式(data form)用于发送和接收消息in their raw form。
2.SADB_X_PCHANGE消息:
3.SADB_X_KMPRIVATE消息:
4、SADB_X_DEBUG消息:
目的: 分配一个tdb空间,并用tdb指针指向它。
参数: struct tdb** tdb ―― 一个指向tdb块指针的指针
返回值: 0 ―― 成功,非0值―― 失败
算法描述:
1.判断tdb指针所指向的指针是否为空,若为空,返回错误;
2.调用系统调用kmalloc分配tdb大小的空间,若分配失败,则返回错误;
3.将该空间清0,返回0。
目的: 分配一个eroute空间,并用eroute指向它。
参数: struct eroute** eroute ―― 一个指向eroute项指针的指针
返回值: 0 ―― 成功,非0值―― 失败
算法描述:
1.判断tdb指针所指向的指针是否为空,若为空,返回错误;
2.调用系统调用kmalloc分配tdb大小的空间,若分配失败,则返回错误;
3.将该空间清0,返回0。
目的: 处理SA消息,把SA的数据提取出来,放入extr中。
参数: pfkey_ext ―― pfkey消息
extr ―― 从pfkey消息中提取的相关数据
返回值: 0 ―― 成功,非0值―― 失败
算法描述:
1.对传入的两个参数做有效性判断,无效,即返回错误;
2.如果pfkey_ext->sadb_ext_type == SADB_EXT_SA,则取出extr->tdb;
如果pfkey_ext->sadb_ext_type == SADB_X_EXT_SA2,则调用函数pfkey_alloc()分配一个tdb空间,作为extr->tdb2;
如果pfkey_ext->sadb_ext_type 不等于这两种中任一种,则出错返回;
3.给extr->tdb或extr->tdb2赋值,如:
extr->tdb->tdb_said.spi = pfkey_ext->sadb_sa_spi;
extr->tdb->tdb_replaywin = pfkey_ext->sadb_sa_replay;
4.根据extr->tdb->tdb_said.proto取不同值(IPPROTO_AH、IPPROTO_ESP或IPPROTO_IPIP),对extr->tdb->tdb_authalg和extr->tdb->tdb_encalg赋值,至此,提取数据完毕,返回0。
目的: 处理lifetime消息,把与lifetime相关的数据提取出来,放入extr中。
参数: pfkey_ext ―― pfkey消息
extr ―― 从pfkey消息中提取的相关数据
返回值: 0 ―― 成功,非0值―― 失败
算法描述:
1.首先,对extr进行有效性检查,若无效,返回错误信息;
2. struct sadb_lifetime *pfkey_lifetime = (struct sadb_lifetime *)pfkey_ext;
若pfkey_lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT,则返回出错信息(这种情况现在不能处理);
若pfkey_lifetime->sadb_lifetime_exttype
= SADB_EXT_LIFETIME_HARD 或
pfkey_lifetime->sadb_lifetime_exttype = SADB_EXT_LIFETIME_SOFT,
则提取出其中的相应值填写到extr中去;
3.提取完毕,返回0;
目的: 处理address消息,把address相关的数据提取出来,放入extr中。
参数: pfkey_ext ―― pfkey消息
extr ―― 从pfkey消息中提取的相关数据
返回值: 0 ―― 成功,非0值―― 失败
算法描述:同上,略
目的: 处理key消息,把与key相关的数据提取出来,放入extr中。
参数: pfkey_ext ―― pfkey消息
extr ―― 从pfkey消息中提取的相关数据
返回值: 0 ―― 成功,非0值―― 失败
算法描述:同上,略
目的: 处理ident消息,把与ident相关的数据提取出来,放入extr中。
参数: pfkey_ext ―― pfkey消息
extr ―― 从pfkey消息中提取的相关数据
返回值: 0 ―― 成功,非0值―― 失败
算法描述:同上,略
目的: 处理satype消息,把与satype相关的数据提取出来,放入extr中。
参数: pfkey_ext ―― pfkey消息
extr ―― 从pfkey消息中提取的相关数据
返回值: 0 ―― 成功,非0值―― 失败
算法描述:同上,略
目的: 处理debug消息,把与之相关的数据提取出来,放入extr中。
参数: pfkey_ext ―― pfkey消息
extr ―― 从pfkey消息中提取的相关数据
返回值: 0 ―― 成功,非0值―― 失败
算法描述:同上,略
目的: 处理sens消息,把与sens相关的数据提取出来,放入extr中。
参数: pfkey_ext ―― pfkey消息
extr ―― 从pfkey消息中提取的相关数据
返回值: 0 ―― 成功,非0值―― 失败
算法描述:尚未实现
目的: 处理prop消息,把与prop相关的数据提取出来,放入extr中。
参数: pfkey_ext ―― pfkey消息
extr ―― 从pfkey消息中提取的相关数据
返回值: 0 ―― 成功,非0值―― 失败
算法描述:尚未实现
目的: 处理supported消息,把与supported相关的数据提取出来,放入extr中。
参数: pfkey_ext ―― pfkey消息
extr ―― 从pfkey消息中提取的相关数据
返回值: 0 ―― 成功,非0值―― 失败
算法描述:尚未实现
目的: 处理spirange消息,把与spirange相关的数据提取出来,放入extr中。
参数: pfkey_ext ―― pfkey消息
extr ―― 从pfkey消息中提取的相关数据
返回值: 0 ―― 成功,非0值―― 失败
算法描述:尚未实现
目的: 处理kmprivate消息,把与之相关的数据提取出来,放入extr中。
参数: pfkey_ext ―― pfkey消息
extr ―― 从pfkey消息中提取的相关数据
返回值: 0 ―― 成功,非0值―― 失败
算法描述:尚未实现
目的: 初始化tdb块。
参数: tdbp ―― 要初始化的tdb块
extensions ――
返回值: 0 ―― 成功,非0值―― 失败
算法描述:
1.判断传入的tdb块tdbp是否有效,若无效,返回错误信息;
2.根据tdbp->tdb_said.proto值的不同做不同处理:
如果tdbp->tdb_said.proto = IPPROTO_IPIP,则调用函数addrtoa()将tdbp->tdb_addr_s(网络字节顺序的二进制表示)转换为文本方式的字符串;
如果tdbp->tdb_said.proto = IPPROTO_AH,再根据tdbp->tdb_authalg的不同,对tdbp进行不同方式的初始化;
如果如果tdbp->tdb_said.proto = IPPROTO_ESP,再根据tdbp->tdb_encalg的不同,对tdbp进行不同方式的初始化;
3.若上述过程均没有出错,则返回0。
目的: 分析getspi消息
参数: sk ―― pfkey消息
extensions ―― 从pfkey消息中提取的相关数据
extr ―― 需要得到的数据
返回值: 0 ―― 成功,非0值―― 失败
算法描述:
1.判断extr和extr->tdb的有效性,若无效,返回错误信息;
2.取出extensions中的minspi和maxspi;
3.如果minspi = maxspi,使extr->tdb->tdb_spi = maxspi/minspi,调用函数gettdb(&(extr->tdb->tdb_said))获得tdb块 - tdbp;
4.如果minspi != maxspi,计算extr->tdb->tdb_spi,再调用函数gettdb gettdb(&(extr->tdb->tdb_said))获得tdb块 - tdbp;
5.如果未找到tdbp,则出错,返回;
6.检查extr->tdb->tdb_dst.s_addr是否是本机地址,若是,说明该spi为INBOUND;
7.对extr->tdb的部分成员变量赋值,其中,extr->tdb->tdb_state = SADB_SASTATE_LARVAL;
8.调用函数puttdb(extr->tdb),即将extr->tdb加入tdbh链中;
9.如果上述过程未出错,则返回0,否则,extr->tdb->tdb_state = SADB_SASTATE_DEAD,返回错误。
目的: 分析update(更新SA)命令,并更新SA库。
参数: sk ――
extensions ――
extr ――
返回值: 0 ―― 成功,非0值―― 失败
算法描述:
1.判断extensions中的SA状态是否是SADB_SASTATE_MATURE,如果不是,则返回错误;
2.判断extr和extr->tdb是否有效,如果无效,则返回错误;
3.对tdb链加锁;
4.调用函数gettdb(&(extr->tdb->tdb_said)),获得满足要求的tdb块 - tdbp, 并判断tdbp的有效性;
5.检查extr->tdb->tdb_dst.s_addr是否是本机地址,若是,说明该spi为INBOUND,置标志位:extr->tdb->tdb_flags |= EMT_INBOUND;
6.调用函数pfkey_tdb_init(extr->tdb,extensions),初始化extr->tdb,如果该函数返回失败,则将tdb链解锁,调用ipsec_tdbwipe(extr->tdb),将其删除;
7.调用函数deltdbchain(tdbq),清除这个中间变量,如果该函数返回失败,则将tdb链解锁;
8.将tdb链解锁;
9.调用函数puttdb(extr->tdb),将extr->tdb加入tdb链中,并调用函数ipsec_tdbwipe(tdbq)释放这个中间变量;
10.若上述过程未出错,返回0,否则,返回错误。
目的: 添加。
参数: sk ――
extensions ――
extr ――
返回值: 0 ―― 成功,非0值―― 失败
算法描述:
1.判断extensions中的SA的状态是否为SADB_STATE_MATURE,如果不是,则返回错误;
2.判断extr和extr->tdb是否有效,若无效,则返回错误;
3.调用函数gettdb(&(extr->tdb->tdb_said))查找,看是否已经存在一个与要添加的SA具有相同said的tdb块,若有,返回错误;
4.调用函数ip_chk_addr((unsigned long)extr->tdb->tdb_dst.s_addr),查看是否该地址与本机地址相同,若相同,则置标志位:extr->tdb->tdb_flags |=EMT_INBOUND;
5.调用函数pfkey_tdb_init(extr->tdb,extensions),初始化extr->tdb;
目的: 删除。
参数: sk ――
extensions ――
extr ――
返回值: 0 ―― 成功,非0值―― 失败
算法描述:
1.判断extr和extr->tdb是否有效,若无效,则返回错误;
2.调用函数spin_lock_irqsave(&tdb_lock,flags)锁住tdb链;
3.调用函数gettdb(&(extr->tdb->tdb_said)),获取满足相应的tdb块 - tdbp;
4.若tdbp为NULL,则返回错误;
5.调用函数deltdbchain(tdbp),删除该块;
6.调用函数spin_unlock_irqrestore(&tdb_lock,flags)解锁,返回0。
目的: 什么也没干!!!
参数: sk ――
extensions ――
extr ――
返回值: 0 ―― 成功,非0值―― 失败
算法描述:
1.判断extr和extr->tdb是否有效,若无效,返回错误;
2.调用函数spin_lock_irqsave(&tdb_lock,flags),加锁;
3.调用函数gettdb(&(extr->tdb->tdb_said)),获得相应的tdb块 - tdbp;
4.若tdbp为NULL,返回错误;
5.调用函数,spi_unlock_irqrestore(&tdb_lock,flags)解锁,返回。
目的: 什么也没干!!!
参数: sk ――
extensions ――
extr ――
返回值: 0 ―― 成功,非0值―― 失败
算法描述:略
目的: ???
参数: sk ――
extensions ――
extr ――
返回值: 0 ―― 成功,非0值―― 失败
算法描述:略
目的: ???
参数: sk ――
extensions ――
extr ――
返回值: 0 ―― 成功,非0值―― 失败
算法描述:略
目的: ???
参数: sk ――
extensions ――
extr ――
返回值: 0 ―― 成功,非0值―― 失败
算法描述:略
目的: 清除整个tdb链。
参数: sk ――
extensions ――
extr ――
返回值: 0 ―― 成功,非0值―― 失败
算法描述:
调用函数ipsec_tdbcleanup(),清除整个tdb链,返回。
目的:
参数: sk ――
extensions ――
extr ――
返回值: 0 ―― 成功,非0值―― 失败
算法描述:略
目的:
参数: sk ――
extensions ――
extr ――
返回值: 0 ―― 成功,非0值―― 失败
算法描述:略
目的:
参数: sk ――
extensions ――
extr ――
返回值: 0 ―― 成功,非0值―― 失败
算法描述:略
目的: 将两个SA group起来,即两个tdb块group起来。
参数: sk ――
extensions ――
extr ――
返回值: 0 ―― 成功,非0值―― 失败
算法描述:
1. 调用函数spin_lock_irqsave(&tdb_lock,flags)加锁;
2. 判断extr和extr->tdb有效,若无效,返回错误;
3. 赋值,是两个SA绑起来。
目的:
参数: sk ――
extensions ――
extr ――
返回值: 0 ―― 成功,非0值―― 失败
算法描述:
目的:
参数: sk ――
extensions ――
extr ――
返回值: 0 ―― 成功,非0值―― 失败
算法描述:
目的:
参数: sk ――
extensions ――
extr ――
返回值: 0 ―― 成功,非0值―― 失败
算法描述:略
目的: 接收消息,解析消息,根据消息做出处理。
参数: sk ――
pfkey_msg ―― 接收到的pfkey_msg
返回值: 0 ―― 成功,非0值―― 失败
算法描述:
1.定义变量extensions,extr,并初始化它们 ――调用函数pfkey_extensions_init(extensions)将extensions初始化;
2.调用函数pfkey_alloc_tdb(&(extr.tdb)),给extb.tdb分配空间并初始化,若失败,返回错误信息;
3.判断pfkey_msg是否有效,若无效,返回错误信息;
4.填充extr.tdb的各项成员变量值;
6.调用函数pfkey_msg_parse(),对传入的pfkey_msg进行分析,并检查其有效性,将分析结果放入extensions[]中;
7.进入处理循环,调用ext_processors[]分别对extensions[]中的所有项都进行处理;
8.根据由上述过程生成的extensions和extr,调用msg_parsers[]进行处理,返回。
变量说明
1.Pfkey_family_ops ,定义pfkey协议
struct net_proto_family pfkey_family_ops = {
PF_KEY, //协议族
pfkey_create //创建该协议族时的回调函数
};
注:其中net_proto_families在include/linux/net.h里面定义:
struct net_proto_family
{
int family;
int (*create)(struct socket *sock, int protocol);
/* These are counters for the number of different methods of each we support */
short authentication;
short encryption;
short encrypt_net;
};
其中有用的只有前两项,那个create的callback函数是每个协议――AF_INET等初始化上层协议如TCP/ICMP协议需要的。
2.pfkey_proto_ops ,定义pfekey协议操作函数
struct proto_ops pfkey_proto_ops = {
PF_KEY,
pfkey_create,
pfkey_dup,
pfkey_release,
pfkey_bind,
pfkey_connect,
pfkey_socketpair,
pfkey_accept,
pfkey_getname,
pfkey_select,
pfkey_ioctl,
pfkey_listen,
pfkey_shutdown,
pfkey_setsockopt,
pfkey_getsockopt,
pfkey_fcntl,
pfkey_sendmsg,
pfkey_recvmsg
};
3.proc_net_pfkey , proc文件系统结构
struct proc_dir_entry proc_net_pfkey =
{
0,
6, "pf_key",
S_IFREG | S_IRUGO, 1, 0, 0,
0, &proc_net_inode_operations,
pfkey_get_info
};
4.struct sock * pfkey_sock_list;
5.struct socket_list *pfkey_open_sockets;
6. struct socket_list *pfkey_registered_sockets;
目的: 向系统登记pfkey协议族。
参数: 无
返回值: 0 ―― 成功,非0值―― 失败
算法描述:
1.调用系统调用sock_register(),登记pfkey协议族;
注:其中sock_register函数在net/socket.c里面,就是设置net_families数组中间对应的值:
int sock_register(struct net_proto_family *ops)
{
if (ops->family >= NPROTO) {
printk(KERN_CRIT "protocol %d >= NPROTO(%d)\n",
ops->family, NPROTO);
return -ENOBUFS;
}
net_families[ops->family]=ops;
return 0;
}
2.调用系统调用proc_register(),向系统注册proc_net_pfkey,返回。
目的: 注销pfkey协议族。
参数: 无
返回值: 0 ―― 成功,非0值―― 失败
算法描述:
1.调用系统调用sock_unregister(PF_KEY),注销pfkey协议族;
2.调用系统调用proc_net_unregister(),注销proc_net_pfkey项,返回。
目的: 构造sk,并插入队列中。
参数: sock
protocol
返回值: 0 ―― 成功,非0值―― 失败
算法描述:
1.对sock、ptotocol和当前用户的UID做有效性判断;
2.将sock状态设为未连接,调用系统调用sk_alloc(),创建一个类型为sock的结构sk;
3.调用系统调用sock_init_data(sock,sk),初始化sk;
4.对sk的各个成员变量赋值;
5.调用函数pfkey_insert_socket(sk),将此sk插入队列;
6.调用函数pfkey_list_insert_socket(sock,pfkey_open_sockets),返回。
目的: 复制一个sock。
参数: newsock
oldsock
返回值: 0 ―― 成功,非0值―― 失败
算法描述:
1.判断各个参数的有效性;
2.调用函数pfkey_create(newsock,sk->protocol),则得到newsock。
目的: 是否指定sock。
参数: sock ―― 要释放的套接口
返回值: 0 ―― 成功,非0值―― 失败
算法描述:
1.检查socket的有效性;
2.调用系统调用sklist_destroy_socket(),释放该socket;
3.调用函数pfkey_list_remove_socket(),从sock_list中删除,返回。
目的: 关闭套接口。
参数: sock ―― 要关闭的套接口
mode ―― ???
返回值: 0 ―― 成功,非0值―― 失败
算法描述:
1.对socket做有效性判断;
2.根据mode,改变socket的状态,即关闭,返回。
目的: 设置套接口参数
参数: sock ―― 要被设置属性的套接口
level ――
optname ――
optval ――
optlen ――
返回值: 0 ―― 成功,非0值―― 失败
算法描述:
1.对socket做有效性判断;
2.调用系统调用sock_stsockopt(),设置socket的属性。
目的: 获取socket的属性。
参数: sock ―― 将被获取属性的socket
level ――
optname ――
optval ――
optlen ――
返回值: 0 ―― 成功,非0值―― 失败
算法描述:
1.对socket做有效性检查;
2.调用系统调用sock_getsockopt(),获取socket的属性。
目的: 发送PFKEY消息。
参数: sock ―― 发送信息的套接口
msg ―― 要发送的信息
len ―― 发送的信息长度
scm ――
返回值: len ―― 发送的msg长度
算法描述:
1.对参数做有效性判断;
2.定义一个类型为sadb_msg的变量 - pfkey_msg,调用系统调用kmalloc,为其分配空间,调用系统调用memcpy_fromiovec((void *)pfkey_msg, msg->iov, len),将msg->iov内容拷入pfkey_msg;
3.检查pfkey_msg的有效性;
4.调用函数pfkey_msg_interp(sk,pfkey_msg),注意:程序在此处就调用了此函数,表明在pfkey_sendmsg中就进行了消息的解析和执行,而非在pfkey_rcvmsg中才进行;
5.如果pfkey_msg不为空,取出pfkey_sock_list,进入循环处理:只要它不为空,就调用函数pfkey_upmsg,后者又调用sock_queue_rcv_skb()用于处理接受到的数据并有流控处理;退出循环后,调用系统调用kfree_s释放pfkey_msg;
6.返回发送的长度。
目的: 接收PFKEY消息。
参数: sock ―― 接收信息的套接口
msg ―― 接收送的信息
len ―― 接收的信息长度
scm ――
返回值: len ―― 接收的msg长度
算法描述:
目的: 将sk插入sock队列pfkey_sock_list。
参数: sk ―― 要插入的sk
返回值: 无
算法描述: 略
目的: 从sock队列pfkey_sock_list中删除sk。
参数: sk ―― 要删除的sk
返回值: 无
算法描述: 在pfkey_sock_list中找到sk,删除。
目的:
参数: sk ―― 要删除的sk
返回值: 无
算法描述: 略
目的: 构造PF_KEY基本消息头。
参数: pfkey_ext ――PF_KEY消息扩展头
msg_type ―― 此PF_KEY消息的类型
satype ―― SA的类型
msg_errno ―― 错误代码
seq ―― 此消息的序列号
pid ―― 此消息的进程号
返回值: 0 ―― 成功,非0值―― 失败
算法描述:
1.做常规的有效性检查,定义类型为sadb_msg的变量pfkey_msg;
2.给结构类型为sadb_msg的变量分配空间,将其初始化为0;
3.根据传入的参数给pfkey_msg赋值,消息构造完毕,返回。
目的: 构造SA消息。
参数: pfkey_ext ―― PF_KEY消息扩展头
exttype ―― 此扩展消息的类型
spi ―― 此SA的安全参数索引值
replay_window ――
sa_state ―― 此SA所处的状态
auth ―― 此SA所采用的认证算法
encrypt ――此SA所采用的加密算法
flags ――
返回值: 0 ―― 成功,非0值―― 失败
算法描述:
1.做常规的有效性检查,定义一个类型为sadb_sa的变量;
2.给结构类型为sadb_msg的变量分配空间,将其初始化为0;
3.根据传入的参数给pfkey_msg赋值,消息构造完毕,返回。
目的: 构造lifetime消息。
参数: pfkey_ext ―― PF_KEY消息扩展头
exttype ―― 此扩展消息的类型
allocations ――
bytes ――
addtime ――
usetime ――
返回值: 0 ―― 成功,非0值―― 失败
算法描述:同上,略
目的: 构造address消息。
参数: pfkey_ext ―― PF_KEY消息扩展头
exttype ―― 此扩展消息的类型
proto ――
prefixlen ――
address ――
返回值: 0 ―― 成功,非0值―― 失败
算法描述:同上,略
目的: 构造key消息。
参数: pfkey_ext ―― PF_KEY消息扩展头
exttype ―― 此扩展消息的类型
key_bits ――
key ――
返回值: 0 ―― 成功,非0值―― 失败
算法描述:同上,略
目的: 构造ident消息。
参数: pfkey_ext ―― PF_KEY消息扩展头
exttype ―― 此扩展消息的类型
ident_type ――
ident_id ――
ident_string ――
返回值: 0 ―― 成功,非0值―― 失败
算法描述:同上,略
目的: 构造sens消息。
参数: pfkey_ext ―― PF_KEY消息扩展头
exttype ―― 此扩展消息的类型
dpd ――
sens_level ――
sens_len ――
sens_bitmap ――
integ_level ――
integ_len ――
integ_bitmap――
返回值: 0 ―― 成功,非0值―― 失败
算法描述:同上,略
目的: 构造prop消息。
参数: pfkey_ext ―― PF_KEY消息扩展头
exttype ―― 此扩展消息的类型
replay ――
comb_num ――
comb ――
返回值: 0 ―― 成功,非0值―― 失败
算法描述:同上,略
目的: 构造prop消息。
参数: pfkey_ext ―― PF_KEY消息扩展头
exttype ―― 此扩展消息的类型
alg_num ――
alg ――
返回值: 0 ―― 成功,非0值―― 失败
算法描述:同上,略
目的: 构造spirange消息。
参数: pfkey_ext ―― PF_KEY消息扩展头
exttype ―― 此扩展消息的类型
min ―― 所能接收的最小的SPI值
max ――所能接收的最小的SPI值
返回值: 0 ―― 成功,非0值―― 失败
算法描述:同上,略
目的: 构造PFKEY_X_KMPRIVATE消息。
参数: pfkey_ext ―― PF_KEY消息扩展头
返回值: 0 ―― 成功,非0值―― 失败
算法描述:同上,略
目的: 构造PFKEY_X_KMPRIVATE消息。
参数: pfkey_ext ―― PF_KEY消息扩展头
satype ――
返回值: 0 ―― 成功,非0值―― 失败
算法描述:同上,略
目的: 构造PFKEY_X_DEBUG消息。
参数: pfkey_ext ―― PF_KEY消息扩展头
tunnel ――
netlink ――
xform
eroute
spi
radij
esp
ah
rcv
pfkey
返回值: 0 ―― 成功,非0值―― 失败
算法描述:同上,略
目的: 构造PFKEY消息。
参数: pfkey_msg ―― PF_KEY消息扩展头
extensions[] ――
返回值: 0 ―― 成功,非0值―― 失败
算法描述:
1.对传入的参数做有效性检查;
2.计算要构造的sadb_msg的总长度;
7.为要构造的sadb_msg分配空间,并初始化;
8.将extensions[]数组中的扩展头依次拷贝到pfkey_ext中,消息构造完毕;
9.调用函数pfkey_msg_parse(*pfkey_msg, NULL, extensions_check, dir),对构造好的pfkey_msg消息进行分析及有效性检查,将各个头分别放入extensions[]数组中,返回。
目的: pfkey扩展消息头的初始化。
参数: extensions[] ―― PF_KEY消息扩展头
返回值: 无
算法描述:将数组中的每个指针置为NULL。
目的: 释放pfkey扩展消息头空间。
参数: extensions[] ―― PF_KEY消息扩展头
返回值: 无
算法描述: 略
目的: 释放pfkey消息空间。
参数: pfkey_msg ―― pfkey消息
返回值: 无
算法描述: 略
目的: 分析并检查类型为sadb_sa的pfkey扩展消息,对其有效性进行检查。
参数: pfkey_ext ―― pfkey扩展消息
返回值: 无
算法描述: 主要是对传入的pfkey消息进行有效性检查,比如,长度是否有效,其加密算法和认证算法是否支持,SA的状态是否正确等等。
目的: 分析并检查类型为sadb_lifetime的pfkey扩展消息,对其有效性进行检查。
参数: pfkey_ext ―― pfkey扩展消息
返回值: 0 ―― 正确,非0值 ――出错
算法描述: 略
目的: 分析并检查类型为sadb_address的pfkey扩展消息,对其有效性进行检查。
参数: pfkey_ext ―― pfkey扩展消息
返回值: 0 ―― 正确,非0值 ――出错
算法描述: 略
目的: 分析并检查类型为sadb_address的pfkey扩展消息,对其有效性进行检查。
参数: pfkey_ext ―― pfkey扩展消息
返回值: 0 ―― 正确,非0值 ――出错
算法描述: 略
目的: 分析并检查类型为sadb_ident的扩展消息,对其有效性进行检查。
参数: pfkey_ext ―― pfkey扩展消息
返回值: 0 ―― 正确,非0值 ――出错
算法描述: 略
目的: 分析并检查类型为sadb_sens的消息,对其有效性进行检查。
参数: pfkey_ext ―― pfkey扩展消息
返回值: 0 ―― 正确,非0值 ――出错
算法描述: 略
目的: 分析并检查类型为sadb_prop的消息,对其有效性进行检查。
参数: pfkey_ext ―― pfkey扩展消息
返回值: 0 ―― 正确,非0值 ――出错
算法描述: 略
目的: 分析并检查类型为sadb_supported的消息,对其有效性进行检查。
参数: pfkey_ext ―― pfkey扩展消息
返回值: 0 ―― 正确,非0值 ――出错
算法描述: 略
目的: 分析并检查类型为sadb_spirange的消息,对其有效性进行检查。
参数: pfkey_ext ―― pfkey扩展消息
返回值: 0 ―― 正确,非0值 ――出错
算法描述: 略
目的: 分析并检查类型为sadb_x_kmprivate的消息,对其有效性进行检查。
参数: pfkey_ext ―― pfkey扩展消息
返回值: 0 ―― 正确,非0值 ――出错
算法描述: 略
目的: 分析并检查类型为sadb_x_satype的消息,对其有效性进行检查。
参数: pfkey_ext ―― pfkey扩展消息
返回值: 0 ―― 正确,非0值 ――出错
算法描述: 略
目的: 分析并检查类型为sadb_x_debug消息,对其有效性进行检查。
参数: pfkey_ext ―― pfkey扩展消息
返回值: 0 ―― 正确,非0值 ――出错
算法描述: 略
目的: 分析并检查类型为sadb_msg的pfkey消息,对其有效性进行检查。
参数: pfkey_msg ―― pfkey消息
sadb_ext* ―― 处理扩展头的函数数组指针
extensions[] ―― 扩展头数组
dir ―― 消息的发送方向?
返回值: 0 ―― 正确,非0值 ――出错
算法描述: