详细设计说明书示例

萌到你眼炸
638次浏览
2021年02月08日 17:38
最佳经验
本文由作者推荐

维修打印机-

2021年2月8日发(作者:advertised)


1


.引言



编写目的



本说明书提供了

< p>
freeswan


各个模块部件的说明,以供编码人员具体实现及今后的< /p>


维护工作。



2


.总体设计



设计原则



在确定目标系统的过程中,主要遵循了以下几个原则:




目标系统基本上完整地实现


IPSe c


协议族,完全支持


VPN


的要求;< /p>




目标系统的服务器端一定要建立在具 有自主版权的内核操作系统之上;




目标系统的客户端使用方便、界面友好、配置和管理简单灵活。




软件结构



该软件由如下几个模块组成:




KLIPS


模块――



实现对进入或外出


IP


包的安全处理,如:加密、认证 等(运行在内核


空间)




Pluto


模块――



实现


IKE


协议,完成安全联盟的协商(运行在用户空 间)




PF_KEY


模块――



实现


pfkey2


协议,完成上述两个模块间关于< /p>


SA


的通信



3


.程序描述



源代码文件组成



本程序由两大模块组 成,一部分是


klips


,它运行在操作系统内核空间,主要负 责安全


联盟和密钥的管理工作,以及对数据报的加密、解密的处理工作;一部分是


pluto


,它是一


个运行在用户空间的守护 进程,


主要负责安全联盟的协商工作。


下面分别是它们的文件组 成:



源文件目录




|―――freeswan




|


――


kl ips









|


――


libdes



|


――


pluto



|


――


utils




KLIPS


(内核

< br>IPsec


)详细设计



--―――


klips









ipsec


的核心实现模块



|


――


net



|


――


ipsec


|


――




登记模块,并初始化



数据包的处理并发送模块



|


――


ipsec_tunnel.



|


――



|


――



|


――



|


――



|


――



|


――



|


――



|


――



|


――



|


――



|< /p>


――


utils



|


――



|


――



|


――



|


――



|


――
























数据包接收并处理模块



管理


SA


的模块



sha1


实现模块,由改编



实现模块



路由表的

< br>radix


数的实现模块



对上个文件的改编




实现


PF_KEY2


协议的模块



对上个文件的改编






用户操作


eroute


表的模块



< p>
用户操作


SA


库的模块



同上



用户操作虚接口的模块



登记及初始化模块



概要说明



功能



向内核系统登记几个

< p>
proc


文件,以便于向内核空间中查询安全联盟和


eoute


表,以及


虚接口的状况;



初始化


SA


数据库(

< br>tdb


链)




初始化


SPDB


数据库(


er oute


表)



初始化


pf_key



PF_KE Y


套接口)




模块所涉及的文件



:


:


:


:






int



ipsec_init(void)



int



ipsec_tdbinit(void)



int



ipsec_radijinit(void)







int



pfkey_init(void)



变量说明




proc


文件结构


< br>将下列结构登记到内核系统中,则内核就可以通过


proc


文件系统向应用程序提供一个


安全的界面来存取如


SA



eroute


表等资料。

< br>


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




notifier_block


结构



struct notifier_block


结构是在


include/linux/


里面的:



struct notifier_block



{



int (*notifier_call)(struct notifier_block *self, unsigned long, void *);



struct notifier_block *next;



int priority;



};




re gister_netdevice_notifier


函数在


net/core/


里面,是这样的:



int register_netdevice_notifier(struct notifier_block *nb)



{



return notifier_chain_register(&netdev_chain, nb);



}




notifier_chain_register


函数在


include/linux/


里面,是这样的:



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


注册了一个


netde v_notifier


的结构体,这样就可以在


interfa ce


改变的时候得到通知了


(


通过调用 每个


notifier_call


函数


)





inet_protocol


结构



struct inet_protocol ah_protocol =



{



ipsec_rcv,



NULL,


0,








/* AH handler ,


定义此协议处理函数


*/





/* TUNNEL error control


,错误处理函数



*/



/* next */



IPPROTO_AH,



0,









/* protocol ID */






/* copy */



/* data */



/* name */



NULL,



};



ips ec_rcv


函数是用来接收数据的


callback


函数,第二个是错误处理函数,其它的


copy


是用


来协议共享的,这个以后再说,


data

< br>当然就是这个结构体的私有数据了。




struct inet_protocol esp_protocol


同上;




i net_add_protocol


函数在


net/ipv4< /p>


/


里实现:



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()



< /p>


ipsec_init()



clean up_module()



ipsec_cleanup()




ipsec_eroute_get_info()



ipse c_spi_get_info()



ipsec_spigr p_get_info()




ips ec_tncfg_get_info()



ipsec_ve rsion_get_info()



ipsec_spi_g et_new()




int init_module(void)



装载

< br>ipsec


时进行登记及初始化工作。





目的:




参数:




返回值:



0


――



初始化成功,非


0




――



初始化未成功



算法描述:



调用

ipsec_init()





int cleanup_module(void)



卸载


ipsec


进行清除工作。






目的:




参数:




返回值:



0


――



初始化成功,非


0


值――初始化未成功



算法描述:



调用

ipsec_cleanup()





int ipsec_int(void)



装载


ipsec


时进行登记及初始化工作。






目的:




参数:




返回值:



0


――



初始化成功,非


0


值――初始化未成功



算法描述:




1


.将定义好的几个


proc_dir_entry


结构注册到系统中,如:



proc_regi ster(proc_net,&ipsec_eroute);/*PROC_FS_21*/





pro c_register_dynamic(&proc_net,&ipsec_eroute);





pro c_net_create(“ipsec_eroute”,0,ipsec_eroute_get_inf o)




proc_register


为系统调用,在


fs/proc/


中实 现,主要就是在


proc_net


对应的目录下面生成每


个协议的子目录。用户可以通过访问


/proc/net


目录下面的相应目录得到相关的资料。



2


.调用


ipsec_tdbinit()


函数初 始化


SA


数据库,此函数在文件中实现;



3


.调用


ipsec_radij init()


函数初始化


SPD


数据库 ,此函数在文件中实现;



4


.调用< /p>


pfkey_init()


函数初始化


P FKEY


,此函数在文件中实现;



5





reg ister_netdevice_notifier(&ipsec_dev_notifier)

< p>












ipsec_dev_notifier


结构,


registe r_netdevice_ntifier()


为系统调用;



6



调用


in et_add_protocol(&esp_protocol)


< br>inet_add_protocol(&ah_protocol)


函数向系统 中注



ESP


协议和

< br>AH


协议,


inet_add_protocol()< /p>


为系统调用;



7



调用


ipsec_tunnel_init_device s()


函数,


登记并初始化


ipsec


虚接口,


此函数定义在文件中。




int ipsec_cleanup(void)



卸载


ipsec


进行清除工作。






目的:




参数:




返回值:



0


――



清除成功,非

< br>0


值――清除未成功



算法描述:




1


.调用


ipsec_tunnel_cleanup_de vice(void)


函数,清除向系统登记的


ipsec


虚接口,此函数在


中实现;



2


.调用


inet_del_protocol(& ah_protocol)



inet_del_protoc ol(&esp_protocol)


清除掉在系统中


注册的< /p>


ESP


协议和


AH


协议;



3


.调用系统调用


unregister_netdevice_notifier(&ipsec_dev_no tifier)


清除掉系统中注册的


ipsec_dev_no tifier




4

< br>.调用


ipsec_tdbcleanup(0)


清除系 统中的


SA


数据库,此函数在中文件实现;


5


.调用


ipsec_radi jcleanup()


清除系统中的


SPD

数据库,此函数在文件中实现;



6


.调用


pfkey_cleanup()


< br>pfkey


清除工作;



7


.调用系统调用


proc_net_unregister()


清除


freeswan


在系统中登记的各个


proc


文件。



虚接口模块



概要说明



功能



为了在不改变现有的操作系统的 网络协议栈的状况下,更好地将


IPsec


嵌入,本程序采


用了虚接口的概念。实现中,将创建


4


个< /p>


ipsec


虚接口,可以将虚接口绑定在物理接口


上。对于从


tcp/udp


协议层传下来的数据,将首 先查询


eroute


表,根据


erou te


表项决


定将数据包发往哪一个接口,


物理接口与虚接口将被视为一致的。


此时,


如果数据包发


往虚接口,则调用函数进行处理。


(该程序类似于网卡驱动程序的编 写)



构成文件




变量说明




device


结构



虚接口定义为


struct device


结构,如:



static struct device dev_ipsec0 =



{







0,


0,


0,


0,



























/* name */



/* recv memory end */



/* recv memory start */



/* memory end */



/* memory start */



0x0,


0,



/* base I/O address */






/* IRQ */



/* flags */



/* next device */



0, 0, 0,


NULL,


ipsec_tunnel_probe


/* setup */



};





ipsecpriv


结构



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 *


struct enet_statistics *(*get_stats)(struct device *dev);



struct enet_statistics mystats;



int mtu;


/* What is the desired MTU */



};



haddr);




函数说明




int ipsec_tunnel_init_devices(void)



目的:



登记


ipsec


虚接口。




参数:






返回值:



0


――



登 记成功,非


0


值――登记未成功



算法描述:




调用系统调用


register_netdev(&dev_ipsec0~3)


,向系统登记


4



ipsec


虚接口。




int ipsec_tunnel_init(struct device *dev)



初始化


ipsec


虚接口,完成


device


中变量的初始化和 系统资源的申请。




0


――



清除成功,非

< br>0


值――清除未成功



目的:




参数:




返回值:



算法描述:




1


.填充


device


结构中的几个 函数,如:



a)dev->open


= ipsec_tunnel_open;



b)dev->stop


= ipsec_tunnel_stop;



c)dev->hard_start_xmit



d)dev->get_stats


= ipsec_tunnel_start_xmit;



= ipsec_tunnel_get_stats;



2


.给


dev


配空间,


dev->priv = kmalloc(sizeof(struct ipsecpriv),GFP_KERNEL);



3


.初始化


dev


结构:


skb_queue_head_init(&dev->buffs[i]);


< /p>


4


.填充


dev


结构的其它值,如:


dev->set_mac_address = NULL


等等。




int ipsec_tunnel_attach (struct device* tndev, struct ipsecpriv* prv,struct device *dev)



目的:



将某个虚接口绑定到指定的物理接口上。



参数:





返回值:



0


――



绑定成功,非

< br>0


值――绑定未成功



算法描述:





int ipsec_open(struct device *dev)



目的:




参数:





open



device



返回值:



0


――



o pen


成功,非


0


值――


open


未成功



算法描述:




此方法在网络设备驱动程序里是网络设备被激活的时候被调用(即设备状态由


down ->up





驱动程序作为一个模块被装入:


如果虚接口尚未绑定到物理接口,

返回错误信息;


否则


调用


MOD_ INC_USE_COUNT


宏,以防止模块卸载是设备处于打开状态。




int ipsec_tunnel_start_xmit(struct sk_buff *skb,struct device *dev)



目的:



假定此函数由


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




获取< /p>


Ip



iph


, 判断该包是否是


IPV4


,若不是则丢弃;


4




计算硬件头长度(


hard_header_len





5





ip


包的


ttl


减 小,并计算校验和。



6




填充查 找键键值,根据键值查找


eroute


表,获取


eroute


,若找到,取出


SA

,即


outgonging_said


< br>


7




判断此


IP


包是否是


IKE


协商包(


UDP port 500



,若是,则跳出;



8




进入封 装处理大循环(几乎一切处理都由


outgoing_said


决定,一个


said


对应一个或多


个< /p>


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


发送数据包并返回,有则继续;



⑷.


< br>若指定隧道模式,却找不到


spi


,不做处理,按


1)


发送,返回;



⑸.



根据


said


取得


tdbp


链,和


sa_len


,若


tdbp< /p>


链为空,报错返回;



⑹.



进入


tdb


处理循环,求出


ipsec

< p>
处理所需的


headroom


< br>tailroom





tdbp


链中每一个


tdb

< br>进行如下处理:




< /p>


大量的合法性判断,包括


tdb


状态是否 对,


SA


是否过时,


PFKEYv2< /p>


处理等;





根据


tdbp->


计算


headroom



tailroom


。方法是,根据不同而算法不同:



IPPROTO_AH: headroom += sizeof(struct ah);



IPPROTO_ESP:




.tdbp->tdb_enca lg


(加密算法)



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_tailroo m



pyldsz


也相应改变(加上< /p>


headroom, tailroom





Ln 1016




⑺.



求出


tot_headroom,


tot_tailroom,


mtu_diff


;若


mtu_diff>0


,说明

< br>prv->mtu


计算不


合适,重新计算;



⑻.



调整


mss




⑼.



hard_h eader_stripped


处理;



Ln 1077




⑽.



< p>
。原


skb


空间够用吗不够就重新分配一个


skb


并进行


extend_copy




⑾.



进入


tdb


链循环,进行真正的


ipsec


安全处理。





定义变量并赋初值;





根据

tdbp->


确定


headroom, tailroom


;与


(6)


的处理类似。





处理


skb


,移动数据指针


( skb_push, skb_put


函数


)






ip


头前移;





根据不同的协议类型进行不同的处理:



switch(tdbp->



IPPROTO_ESP:



填写


espp



spi, rpl,...





求出


iv[0], iv[1] (


初 始化向量


)




求出


idat, ilen (


内部数据区域


)


要加密的数据地址及长度;



求出


pad, padlen.


(为 了认证)




pad

< br>前面两个字节分别存放


pad


长度和前

< br>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 !=



&& &&er);



⒂.



硬件头处理;



⒃.



求物理接口,寻路由;



⒄.



发送。


ip_send(skb)




int ipsec_tunnel_detach(struct device *dev,struct ipsecpriv *prv)



目的:



将某个绑定了的虚接口解除绑定。



参数:





返回值:



0


――成功,非


0


值――未成功



算法描述:




prv


的各个值清空




int ipsec_tunnel_clear(void)



目的:



清除所有的绑定虚接口。



参数:






返回值:



0


――成功,非


0

< br>值――未成功



算法描述:


< /p>



prv


的各个值清空

< br>



int ipsec_tunnel_cleanup_devices(void)



目的:



清除


4


个注册的


ipsec


虚接口。



参数:






返回值:



0


――



清 除成功,非


0


值――清除未成功



算法描述:




1


.调用系统调用


unregister_netdev(& dev_ipsec0~3)


清除虚拟接口;



2


.调用系统调用


kfree_s(dev_ipse c0~,sizeof(struct ipsecpriv))


释放为虚接口分配的空间。



接收处理模块



概要说明



功能



主要负责处理接收到的


ipsec


数据包。



构成文件



文件




流程



如图


3



1


所示。







传输层处理






















常包处理
















包处理













重组外出包




查找



SA







加认证





加密






进入策略处理







根据



SA


进行处理





检查



SA




IPSec


处理




IP


路由






态、生存期





检查



SA


状态、重








播窗口、生存期




根据



SA


进行处理








IPSec





处理






查找



SA







解密







认证




分片重组处理


















本地包





绕过




应用




丢弃






重组进入包





转发到本网






















IP


路由







IP


分片处理





关内的包


策略处理















链路层接收包


链路层发送包


























正常包处理







包处理





策略库查找










物理接口驱动





物理接口驱动





虚接口驱动





虚接口驱动




































链路层











































3.1


传统



IPSec


网关接收和转发包示意图



函数说明




int ipsec_rcv(struct sk_buff *skb,unsighed short xlen)



目的:



接收并处理

< br>ipsec


数据包。



参数:




skb




接收到的数据包,


xlen





返回值:



0


――



登记成功,非

< br>0


值――登记未成功



算法描述:




1



SKB


正确性判断(有无数据, 头长度等)




2

.如果包被克隆过,对包进行一个留头处理,为后面做准备;


3


.是


AH


ESP


格式吗(只支持这两种格式)不是则丢弃包;



4


.进入解报循环;



⑴是


ESP


包但长度不是


4< /p>


字节的倍数,丢弃。


(因


ESP


包会有补齐验证,见组报)




⑵计算


ESP



AH


头,求出


said



spi, proto,


等)




⑶若 为


AH


头,计算头长度和下一个头;



⑷为


tdb


链加锁;

< br>


⑸取出


tdbp


< p>
TDB


头)


,若为空,丢弃包;

< br>



tdb


的状态对吗



larval, dead


以及超时(有多种情况)均将丢弃包;



⑺根据认证标志


tdbp->tdb_authalg


对数据 包进行验证;



⑻对


ESP

< p>
包进行解密;



⑼计算新的


IP


数据包头;



⑽解锁;



(中间丢弃包时也先解锁)



⑾如果新 的数据包还是


IPSec


包,循环处理;



5


.对


tdb

链进行处理;



6


.把解开的包送 入


IP


接收队列。



netifrx(skb)




安全联盟的管理模块



概要说明



功能



实现了对


SA


数据库的初始化,添加、删除或更新


SA




组成文件



,



变量说明




tdb


结构



struct tdb



{



struct tdb


*


tdb_hnext;


/* next in hash chain */



struct tdb


*


tdb_onext;


/* next in output */



struct tdb


*


tdb_inext;


struct ifnet


struct sa_id


__u32


/* next in input (prev!) */



/* related rcv encap interface */





/* tunnel descriptor block */



*tdb_rcvif;


tdb_said; /* SA ID */



tdb_seq;


/* seq num of msg that set this SA */



__u32


#if 1



tdb_pid;


/* PID of process that set this SA */



struct xformsw


caddr_t



#endif



__u8


__u8



__u32


__u32


__u32


__u32


__u32



__u8


__u8


__u32


__u64


__u32



__u32



__u32


__u32


__u32


__u64


__u64


__u64


__u64


__u64


__u64


__u64


__u64


__u64


__u64


__u64







*tdb_xform;


/* transformation to use (host order)*/



/* transformation data (opaque) */



tdb_xdata;


tdb_authalg;


/* auth algorithm for this SA */



tdb_encalg;


/* enc algorithm for this SA */



tdb_alg_errs;


/* number of algorithm errors */



/* number of authentication errors */



tdb_auth_errs;


tdb_encsize_errs;


/* number of encryption size errors */



tdb_encpad_errs;


/* number of encryption size errors */



tdb_replaywin_errs;


/* number of pkt sequence errors */



tdb_replaywin;


tdb_state;


/* replay window size */



/* state of SA */



tdb_replaywin_lastseq;


/* last pkt sequence num */



tdb_replaywin_bitmap;


/* bitmap of received pkts */



tdb_replaywin_maxdiff; /* maximum pkt sequence difference */



tdb_flags;


/* generic xform flags */



tdb_lifetime_allocations_c;


/* see rfc2367 */



tdb_lifetime_allocations_s;



tdb_lifetime_allocations_h;



tdb_lifetime_bytes_c;



tdb_lifetime_bytes_s;



tdb_lifetime_bytes_h;



tdb_lifetime_addtime_c;



tdb_lifetime_addtime_s;



tdb_lifetime_addtime_h;



tdb_lifetime_usetime_c;



tdb_lifetime_usetime_s;



tdb_lifetime_usetime_h;



tdb_lifetime_packets_c;



tdb_lifetime_packets_s;



__u64


__u64


tdb_lifetime_packets_h;



tdb_lifetime_usetime_l; /* last time transform was used */



*tdb_addr_s;


/* src sockaddr */



*tdb_addr_d;


/* dst sockaddr */



*tdb_addr_p;


/* proxy sockaddr */



struct sockaddr


struct sockaddr


struct sockaddr


__u16


__u16


__u16


__u16


__u16


__u16


__u16



__u8


__u16


__u16


tdb_iv_size;



tdb_addr_s_size;



tdb_addr_d_size;



tdb_addr_p_size;



tdb_key_bits_a;


/* size of authkey in bits */



tdb_auth_bits;


/


* size of authenticator in bits */



tdb_key_bits_e;


/* size of enckey in bits */



tdb_iv_bits;


/* size of IV in bits */



tdb_key_a_size;



tdb_key_e_size;



/* authentication key */



/* encryption key */



/* Initialisation Vector */



caddr_t


tdb_key_a;


caddr_t


tdb_key_e;


caddr_t


tdb_iv;



__u16


__u16


__u64


__u64


__u8


__u8


tdb_ident_type_s;


/* src identity type */



tdb_ident_type_d;


/* dst identity type */



tdb_ident_id_s;


tdb_ident_id_d;


/* src identity id */



/* dst identity id */



tdb_ident_len_s;


/* src identity type */



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



};



函数说明




int ipsec_tdbinit(void)



目的:



初始化


tdb


链。



参数:






返回值:



0


――



成 功,非


0


值――失败



算法描述:




tdb


链清空。




int tdb_init(struct tdb *tdbp,struct encap_msghdr *em)



目的:




参数:





返回值:



0


――



成功,非


0


值――失败



算法描述:





int puttdb(struct tdb *tdbp)



目的:



将一个新的


tdb


块-


td bp


加入在


tdbh


链中



参数:




tdbp


――


要添加的


tdb




返回值:



0


――



成功,非


0


值――失败



算法描述:




判断传入的


tdbp


是否为空,若为空,返回失败信息;



计算根据


tdbp->tdb_spi



tdbp->


< br>tdbp->tdb_proto


计算出



hashval






tdb


链 加锁,以免同时有其它存取


tdb


链的行为;

< br>




tdbp


插入


tdb


链中:


tdbp- >tdb_hnext = tdbh[hashval];



tdbh[hashval]=tdbp





tdb


链解锁。





struct tdb* gettdb(struct sa_id *said)



目的:



根据给出的

< br>said


值查找相应的


tdb


块 。



参数:




said


――


与要查找的


tdb


块相应的


sai d




返回值:



struct tdb


――



找到的


tdb


块,


NULL


――



失败



算法描述:




判断


said


值是否有效,若为空,返回

NULL




根据


said


计算


hashval




根据


hashval


值找到所要的


tdb


块,失败则返回


NULL





int deltdb(struct tdb *tdbp)



目的:



删除某个指定的


tdb


块。



参数:




tdbp


――


要删除的


tdb




返回值:



0


――



成功,非


0


值――



失败



算法描述:




判断


tdbp


的有效性,若无效,则返回失败信息;



计算


hashval


值;



根据


hashval

< p>
找到


tdbp


,并删除它,若失败,返回错误。< /p>




int deltdbchain(struct tdb *tdbp)



目的:



删除整个

tdbp


链。



参数:




tdbp


――


要删除的


tdb




返回值:



0


――



成功,非


0


值――



失败



算法描述:




判断


tdbp


是否有效,若无效,返回错误信息;

< p>



tdbp


移至最后的


tdbp->tdb_onext




删除所有的


tdb


块。




int ipsec_tdbwipe(struct tdb *tdbp)



目的:



将指定


tdb


块中的所有值清空。



参数:




tdbp


――


要清空的


tdb




返回值:



0


――成功,非


0


值――



失败



算法描述:



将所有值置为

< p>
NULL




安全策略数据库的管理模块



概要说明



功能



实现了对


SPD


数据库的初始化,添加、删除


eroute

< p>



组成文件



,



变量说明




eroute



struct eroute



{



struct rjtentry er_rjt;



struct sa_id er_said;



struct sockaddr_encap er_eaddr;



struct sockaddr_encap er_emask;



};



函数说明




int ipsec_radijinit(void)



目的:



初始化


radij




参数:






返回值:



0


――



成 功,非


0


值――



失败



算法描述:调用


rj_init()


函数初始化。




int


ipsec_makeroute(struct


sockaddr_encap


*eaddr,


struct


sockaddr_encap


*emask,


struct


sa_id said)



目的:



根据


said


值,生成新的


eroute


项 。



参数:




eaddr


――



封装的有效目的地址,



emask


――



封装的目的地址掩码,



said


――



传入的


said




返回值:



0


――成功,非


0


值――



失败



算法描述:



分配

eroute


空间――


retrt


,并先清


0





retrt



< br>:


retrt->er_eaddr


=


*eaddr;


retrt->er_emask


=


*emask;


retrt->er_said = said;


< p>


eroute


表加锁;



调用函数


rj_addroute(&(retrt->er_ eaddr), &(retrt->er_emask), rnh,



retrt->


,向


eroute

< br>表中加入新生成的该项;



解锁,返回。




int ipsec_breakroute(struct sockaddr_encap *eaddr, struct sockaddr_encap *emask)



目的:


< p>
删除指定的


route




参数:




eaddr


――



有效目的地址,


emask




目的地址的掩码



返回值:



0


――



成功,非


0


值――



失败



算法描述:



锁住

eroute


表;



调用函数


rj_delete(eaddr, emask, rnh, &rn)


删除这个指定


route

< br>;



解锁,将该


route


项清


0


,调用系统调用

kfree


释放该空间。




struct eroute *ipsec_findroute(struct sockaddr_encap *eaddr)



目的:



接收并处理

< br>ipsec


数据包。



参数:




eaddr


――


< br>要查找的


route


所具有的


e addr




返回值:



struct eroute


――



找到的


eroute


值,


NULL


――



未找到



算法描述:



调用函数


rj_match((caddr_t)eaddr, rnh)



根据


eaddr

< p>
查找相应的


eroute


项;


返回找到的


eroute


项。< /p>




int ipsec_cleareroutes(void)



目的:



清空


eroute


表。



参数:






返回值:



0


――



成 功,非


0


值――



失败



算法描述:首先,锁住


eroute


表;调用函数


radijclea rtree()


函数;解锁,返回。




int ipsec_radijcleanup(void)



目的:




参数:






返回值:



0


――



成 功,非


0


值――



失败



算法描述:首先,锁住


eroute


表;调用函数


radijclea nup()


函数;解锁,返回。



密钥协商及管理模块




概要说明




功能



前面所描述的

< br>KLIPS


模块,基本完成了对输入、输出数据包的加密、认证工作,但其前


提是处理数据包的


SA


已经协商完毕,


而这个


SA


的协商工作正是由


IKE


所负责的。


在本


程序 中,


Pluto


模块是


IKE


的一个实现,它可以自动完成两个主机或网关间的安全联盟


的协商工作。




源代码组成



――――


Pluto



|



|


――




|


――




|



|


――




|


――




|


――




|


――




|


――




|


――




|


――




|


――




|


――












源文件目录




启动后台进程


pluto


的主程序



监听


IKE


消息、

whack


消息和


pfkey


消息 ,


调用相应处理过程




server


调用,处理


IKE

消息



处理


IKE


消息的头文件,主要定义消息的结构



描述建立


ISAKMP SA



IPSEC SA


的过程





state


头文件,主要定义


state


结构



描 述对


state


的各种操作,为建立


S A


服务



connection


头文件,定义


connection


结构



描述对


connection

< p>
的各种操作,为建立


SA


服务




构造和解析


SA


载荷,为建立


SA


服务< /p>



定义了各种


ISAKMP


包的消息头格式,以及构造消息头的算


法和解析消息头的算法



|


――




|


――




|


――




|


――




|


――




|


――




|


――




|


――




|


――




|


――




|


――




|


――



|


――



|


――





与建立


SA


相关的各种定义



描述对


cookie s


值的各种操作



调用加密算法的接口模块

















ID


的表示及操作



处理时间事件



随机数函数模块



SHA



1


散列函数模块



MD5


散列函数模块



DSA


签名模块



共享密钥的处理模块




其它一些函数的定义



|


――



|



|


――




|


――



< /p>



server


调用,处理


whack


消息



接收


ipsec


whack


命令,构造相应的


whack


消息,发送到


whackfd


,由监听,发到处理



|



|


――




|



|


――




|


――




启动模块




概要说明




server


调用,处理


pfkey


消 息,操作内核中的


SADB






日志处理模块




功能



启动


P luto


后台进程。




源代码组成






函数说明




int main(int argc, char **argv)



目的:分析使用程序的参数,做


pluto


进程的初始化工作



参数:



argc



argv



返回值:


0


――



成功,非


0




――



失败



算法:



分析使用

pluto


的命令行参数,并做相应的操作;


< p>
调用函数


init_log()


,初始化日志;< /p>



调用函数


init_rnd_pool ()


,初始化随机数池;



调用函数< /p>


init_secret()


,生成密钥值;


调用函数


init_states()

< br>,初始化状态表(


state table




3


.调用函数


init_crypto()


,做好加密准备;



4


.调用 函数


init_demux()


,填充


state_microcode_table



< p>
5


.调用函数


call_server()


,启动服务器,监听到来的


ISAKMP


包和


Whcak


信息。




int create_lock(void)



目的:生成一个


lockfile


文件,以保证同时只有一个


pluto


进程运行



参数:





返回值:


0


――



成功,非


0




――



失败



算法:略




int delete_lock(void)



目的:删除


lockfile


文件

< br>


参数:





返回值:


0


――



成功,非


0




――



失败



算法:略




void exit_pluto(int status)



目的:以


status


的状态离开


pluto


进程。



参数:



status


――



0 ok



1 general discomfor



10 lock file exists



返回值:无



算法:略




监听模块



概要说明




功能



该模块主要用于监听

< p>
SA


协商消息――


IKE


消息、


whack


消息和


pfkey< /p>


消息,收到上述几


种消息后,再调用相应处理模块。




源代码组成



文件




函数说明




void call_server(void)


< p>
目的:监听


SA


协商消息――

IKE


消息、


whack


消息和< /p>


pfkey


消息,收到上述几种消息后,再


调用相应处理模块。



参数:无



返回值:无



算法:



调用函数

init_pfkeyed()


,创建


PF_KEY


类型的套接口;



调用函数

< br>init_whackfd()


,创建


AF_UNIX< /p>


型套接口,用于通信;



进入循环等待,接收到来的消息:



如 果收到


IKE


协商消息,调用函数


co mm_handle()


处理;



如果 收到


whack


消息,调用函数


wha ck_handle()


处理;



如果 收到


pfkey


消息,调用函数


pfk ey_handle()


处理。




void find_ifaces(void)



目的:



参数:无



返回值:无



算法:




static int init_pfkeyfd(void)



目的:创建


PF_KEY


型的套接口



参数:无



返回值:-


1


――



失败,



其它



――



成功



算法:调用系统调用

< p>
socket()


创建


socket


,返回之。




static int init_whackfd(void)



目的:创建


AF_UNIX


型的套接口。



参数:无



返回值:




1


――



失败,



其它



――



成功



算法:


调用系统调用


socket()


创建套接口,


并用


bind()


绑定到


ctl_addr


上,


调用系统调用


listen()


监听。




协商安全联盟的模块




概要说明




功能



协商


ISAKMP SA



IPSEC SA





源代码组成




详细说明




ISAKMP SA


的建立过程


(


主模式


)




消息


1


:发起方的


Cookie



CKY-I


、发起方提 出的一个或多个对


ISAKMP


消息的保护方案



static


stf_status


main_outI1(int


whack_sock,


struct


connection


*c,


bool


pending_quick,


lset_t


policy, unsigned long try)


< /p>


目的:发起连接


c


SA


的协商,在这个初始协商消息中,将发送发起方的


co okie


值,


和对


ISAKMP


消息的保护方案。



参数:



whack_sock


――



whack


套接口



c




――



这个初始协商消息是为


c


这个连接发出的



标识在生成


ISAKMP SA


后,是否需要立即协商


IPSEC SA



pending_quick


――



policy







――



安全策略



try




――



发起方最多可以重试的协商次数



返回值:


STF_NO_REPLY


――



成功;其它



――



有错误



算法:



调用函数

new_state()


,为这个


connection< /p>



c


构造一个新的


state




给这个


state


结构


st


的成员 变量赋值,其中部分值如下:



st->st_connection = c;


st->st_try = try;




nterface


等于


NULL


,返回


TRUE





与内核


S ADB


引擎的交互模块




概要说明




功能



将协商好的

SA


安装到内核中的


SADB


库中 ,或按照要求从


SADB


中删除作废的


SA


,等


等。原理是,构造


pfkey


消息,发送到


pfkey


套接口,内核 的


SADB


引擎收到后,解析


消息,完 成相应操作。



pfkey


的详情参见


PF_KEY


模块)




源代码组成




文件




函数说明




void pfkey_handle(void)



目的:



从读取


pfkeyfd


套接口读取


pfkey


消息,并分析此消息



参数:





返回值:无



算法:



调用系统调用


read()


,从


pfkeyfd

套接口读取


pfkey


消息;


< /p>


调用函数


pfkey_msg_parse()

< br>分析收到的消息;




ipsec_spi_t get_ipsec_spi(ipsec_spi_t avoid)



目的:



生成唯一的


SPI




参数:



avoid



返回值:


ipsec_spi_t



算法:略




bool do_command(struct connection *c, const char *verb)



目的:



启动


_updown


脚本,执行其中的防火墙命令规则



参数:




返回值:



算法:




bool


pfkey_build(int


error,


const


char


*description,


const


char


*text_said,


sturct sadb_ext *extensions[SADB_EXT_MAX + 1])



目的:



调用其它构造函数,如果构造 成功,返回


TRUE


,失败,在释放


e xtensions


的空间


返回


FAL SE




参数:




返回值:


TRUE


――




FALSE


――



算法:略




bool finish_pfkey_msg(struct sadb_ext *extensions[SADB_EXT_MAX + 1], const


char * description, const char *text_said)



目的:


< /p>


结束


pfkey


消息的构造。

< p>


参数:




extensions[]


――



description


――



text_said


――



返回值:


FALSE


――



失败,


TURE


――



成功



算法:



调用函数

pfkey_msg_build(&pfkey_msg,extensions,EXT_BITS_I N)


,构造


pfkey


消息,若构


造失败,返回


FALSE




调用系统调用


write(pfkeyfd,pfk ey_msg,len)




pfke y


消息发送出去如发送失败,则返回


FALSE




调用函数


pfkey_e xtensions_free(extensions)



p fkey_msg_free(&pfkey_msg)


释放空间。



bool do_eroute(struct state *st, unsigned op, const char *opname UNUSED)



目的:


< p>
根据要建立的一个


SA


,构造一个


eroute


表项(构造和发送的都是地址消息)


。< /p>



参数:




st


――



描述一个


SA


的状态



op


――



UNUSED


――



返回值:


TRUE


――



成功,


FALSE


――



失败



算法:



根据


st


获得


SPI


值和

< br>protocol




调用函数


set_text_said()


,将计算所得的


said


放入


text_said

< br>中;



将六个地址转化为结构的变量:

< br>、








调用函数

pfkey_extensions_init(extensions)


,初始化


extensions



< p>
调用消息构造函数


pfkey_build


构造消 息,并调用


finish_pfkey_build()


完成构 造,把消息


发送出去。




bool del_spi(ipsec_spi_t spi, int proto, struct in_addr src, struct in_addr dest)



目的:



删除一个


SA




参数:




spi


――



SPI




proto


――



协议



src


――



源地址



dst


――



目的地址



返回值:


TRUE


――



成功,


FALSE


――



失败



算法:



调用函数

pfkey_extensions_init(extensions)


,进行构 造消息之前的初始化;



调用函数


se t_text_said()


,设置


text_said


值;



调用函数


pf key_build()


,构造删除一个


SA

< br>的


pfkey


消息,调用函数


f inish_pfkey_build()


结束构造,并发送。




bool setup_half_ipsec_sa(struct state *st, bool inbound)



目的:



建立半个


SA


(即,两个网关或主机之间,对于 其中某一个来说,数据包进入或出


去的所用


SA




参数:




st


――



描述一个


SA


的状态

< br>


――表示此


SA


是进入的还是 外出的



INBOUND


返回值:


TRUE


――



成功,


FALSE


――



失败



算法:



根据


st


,调用函数


pfkey_build()

< br>构造合适的


pfkey


消息,再调用函数


finish_pfkey_build()


发送;若此过程中有错误发生,则 返回


FALSE


,否则,返回


TRUE





bool teardown_half_ipsec_sa(struct state *st, bool inbound)



目的:



删除半个

SA


(即,两个网关或主机之间,对于其中某一个来说,数据包进入或出

< p>
去的所用


SA




参数:




st


――



描述一个


SA


的状态



――表示此


SA


是进入的还是外出的

< br>


INBOUND


返回值:


TRUE


――



成功,


FALSE


――



失败



算法:



根据


st


,取出该


SA


中设置的

< p>
spi



proto


,调 用函数


del_spi


删除它。




bool could_eroute(struct connection *c, struct connection *ero)



目的:




参数:




c


――



描述一个


connection



ero


――




返回值:


TRUE


――



成功,


FALSE


――



失败



算法:




bool install_inbound_ipsec_sa(struct state *st)



目的:



安装


SA


,仅被被响应方使用,响应方接着 会用


install_ipsec_sa


来安装


outbound



SA


;而 发起方使用


install_ipsec_sa


来安装


inbound



outbound

< p>


SA




参数:




st


――



描述一个


SA


的状态



返回值:


TRUE


――



成功,


FALSE


――



失败



算法:



如果对方的

< br>client


有固定的


IP


地址 ,


检查我们是否有一条达到那里的路由,


这条路由与

< p>
现在的相矛盾(不知道这样说对不对)


;如果有,则删除之;



调用函数


could_eroute(c,


route_owner(c,TRUE))


,检查我们是否有


eroute


表项,如果没


有,返回


FALSE




调用函数


route_connection(c,


FALSE)


,检查我们是否可以将外出的数据包路由到某个

< p>
ipsec


虚接口。事实上,这只检查了我们的


p eer


不在他的子网中,或者如果他在,我们


使用


UDP 500


端口的


IKE


消息,使得它不被处理;



调用函数


setup_half_ipsec_sa(st)


,建立进入的


SA





bool install_ipsec_sa(struct state *st, bool inbound_alse)



目的:



安装


SA




参数:




st


――



描述一个


SA


的状态



inbound_also


――


< /p>


标识这个


SA



inbound


,或


outbound



返回值:


TRUE


――



成功,


FALSE


――



失败



算法:



调用函数


route_owner(c, TRUE)


,查找是谁拥有这个


eroute


< p>


调用函数


could_eroute(c, ero)





调用函数


route_connection(c,


TRUE)


,检查我们是否可以将外出的数据包路由到某个


ipsec


虚接口;



调用 函数


setup_half_ipsec_sa()


,根据


inbound_also


,建立进入的或外出的

< br>SA




如果建立成功,如果< /p>


c->eroute_owner



SO S_NOBODY


,则调用函数


do_eroute(…,”a dd”)


添加


eroute


表项,再调 用函数


do_command()


启动脚本;如果不是


SOS_NOBODY


,则调



do_eroute(…,”replace”)


更新


eroute


表项。




bool delete_ipsec_sa(struct state *st, bool inbound_only)



目的:



安装


SA




参数:




st


――



描述一个


SA


的状态



inbound_only


――


< /p>


是否只删


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


;如果上述步骤成功,再调用


t eardown_half_ipsec_sa()


删除


SA< /p>





PF_KEY


模块



概要说明



PF_KEY


是一种新型的协议族,由


PF_ROUTE


衍生而来 。在协议族中,


PF_KEY


被定义为


15


。目前,实现所依据的时


PF_KEY

的第二个版本。密钥管理进程可以使用它与操作系


统中的“


key


engine


”或安全联盟数据库(

< br>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




该模块大致由四部分组成:




――



PF_KEYv2 Key management API domain socket I/F




――



PF_KEYv2 Key management API message parser



提供了一个可用于


IP sec


密钥管理函数库(


key management API






――



PF_KEYv2 Key management API message parser




――



实现用户层与核心之间对于密钥协商的交互处理


维修打印机-


维修打印机-


维修打印机-


维修打印机-


维修打印机-


维修打印机-


维修打印机-


维修打印机-