quhr 发表于 2011-12-21 08:43

Linux内核中的IPSEC实现(4)

本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。<br>msn: <a href="mailto:yfydz_no1@hotmail.com" target="_blank">yfydz_no1@hotmail.com</a><br>来源:<a href="http://yfydz.cublog.cn/" target="_blank">http://yfydz.cublog.cn</a>
<div>&nbsp;</div>
<div>6. XFRM的其他操作</div>
<div><br>6.1 HASH处理</div>
<div><br>关于HASH值的计算方法主要在net/xfrm/xfrm_hash.h中定义:</div>
<div>// IPV4地址HASH<br>static inline unsigned int __xfrm4_addr_hash(xfrm_address_t *addr)<br>{<br>// 就是地址本身<br>&nbsp;return ntohl(addr-&gt;a4);<br>}</div>
<div>// IPV6地址HASH<br>static inline unsigned int __xfrm6_addr_hash(xfrm_address_t *addr)<br>{<br>// 取后2个32位数异或<br>&nbsp;return ntohl(addr-&gt;a6 ^ addr-&gt;a6);<br>}</div>
<div>// IPV4源,目的地址HASH<br>static inline unsigned int __xfrm4_daddr_saddr_hash(xfrm_address_t *daddr, xfrm_address_t *saddr)<br>{<br>// 将两个地址异或<br>&nbsp;return ntohl(daddr-&gt;a4 ^ saddr-&gt;a4);<br>}</div>
<div>// IPV4源,目的地址HASH<br>static inline unsigned int __xfrm6_daddr_saddr_hash(xfrm_address_t *daddr, xfrm_address_t *saddr)<br>{<br>// 两个V6地址都取后2个32位数异或<br>&nbsp;return ntohl(daddr-&gt;a6 ^ daddr-&gt;a6 ^<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; saddr-&gt;a6 ^ saddr-&gt;a6);<br>}</div>
<div>// 目的地址HASH<br>static inline unsigned int __xfrm_dst_hash(xfrm_address_t *daddr, xfrm_address_t *saddr,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; u32 reqid, unsigned short family,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned int hmask)<br>{<br>// 协议族和请求ID异或<br>&nbsp;unsigned int h = family ^ reqid;<br>&nbsp;switch (family) {<br>// HASH值再和源目的地址HASH结果进行异或<br>&nbsp;case AF_INET:<br>&nbsp;&nbsp;h ^= __xfrm4_daddr_saddr_hash(daddr, saddr);<br>&nbsp;&nbsp;break;<br>&nbsp;case AF_INET6:<br>&nbsp;&nbsp;h ^= __xfrm6_daddr_saddr_hash(daddr, saddr);<br>&nbsp;&nbsp;break;<br>&nbsp;}<br>// 将HASH结果高低16位异或存低16位,高16位不动, 然后用HASH掩码相与<br>&nbsp;return (h ^ (h &gt;&gt; 16)) &amp; hmask;<br>}</div>
<div><br>// 源地址HASH, 只是没有请求ID项, 其他HASH过程和上面相同<br>static inline unsigned __xfrm_src_hash(xfrm_address_t *daddr,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xfrm_address_t *saddr,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned short family,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned int hmask)<br>{<br>&nbsp;unsigned int h = family;<br>&nbsp;switch (family) {<br>&nbsp;case AF_INET:<br>&nbsp;&nbsp;h ^= __xfrm4_daddr_saddr_hash(daddr, saddr);<br>&nbsp;&nbsp;break;<br>&nbsp;case AF_INET6:<br>&nbsp;&nbsp;h ^= __xfrm6_daddr_saddr_hash(daddr, saddr);<br>&nbsp;&nbsp;break;<br>&nbsp;};<br>&nbsp;return (h ^ (h &gt;&gt; 16)) &amp; hmask;<br>}</div>
<div><br>// 根据SPI计算HASH值<br>static inline unsigned int<br>__xfrm_spi_hash(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family,<br>&nbsp;&nbsp;unsigned int hmask)<br>{<br>// 先将SPI和协议进行异或<br>&nbsp;unsigned int h = (__force u32)spi ^ proto;<br>&nbsp;switch (family) {<br>// HASH值再和目的地址进行单一地址HASH值异或<br>&nbsp;case AF_INET:<br>&nbsp;&nbsp;h ^= __xfrm4_addr_hash(daddr);<br>&nbsp;&nbsp;break;<br>&nbsp;case AF_INET6:<br>&nbsp;&nbsp;h ^= __xfrm6_addr_hash(daddr);<br>&nbsp;&nbsp;break;<br>&nbsp;}<br>// HASH值再和本身的高22位, 高12位异或后再和掩码相与<br>&nbsp;return (h ^ (h &gt;&gt; 10) ^ (h &gt;&gt; 20)) &amp; hmask;<br>}</div>
<div><br>// 索引号HASH<br>static inline unsigned int __idx_hash(u32 index, unsigned int hmask)<br>{<br>// 低24位和高24位异或, 高8位不动, 再和掩码相与<br>&nbsp;return (index ^ (index &gt;&gt; 8)) &amp; hmask;<br>}</div>
<div><br>// 选择子HASH<br>static inline unsigned int __sel_hash(struct xfrm_selector *sel, unsigned short family, unsigned int hmask)<br>{<br>// 提前源和目的地址<br>&nbsp;xfrm_address_t *daddr = &amp;sel-&gt;daddr;<br>&nbsp;xfrm_address_t *saddr = &amp;sel-&gt;saddr;<br>&nbsp;unsigned int h = 0;</div>
<div>&nbsp;switch (family) {<br>// 用源,目的地址同时进行HASH<br>&nbsp;case AF_INET:<br>&nbsp;&nbsp;if (sel-&gt;prefixlen_d != 32 ||<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sel-&gt;prefixlen_s != 32)<br>&nbsp;&nbsp;&nbsp;return hmask + 1;<br>&nbsp;&nbsp;h = __xfrm4_daddr_saddr_hash(daddr, saddr);<br>&nbsp;&nbsp;break;</div>
<div>&nbsp;case AF_INET6:<br>&nbsp;&nbsp;if (sel-&gt;prefixlen_d != 128 ||<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sel-&gt;prefixlen_s != 128)<br>&nbsp;&nbsp;&nbsp;return hmask + 1;</div>
<div>&nbsp;&nbsp;h = __xfrm6_daddr_saddr_hash(daddr, saddr);<br>&nbsp;&nbsp;break;<br>&nbsp;};<br>// 高16位与低16位异或,高16位不变<br>&nbsp;h ^= (h &gt;&gt; 16);<br>// 与掩码相与, 其实HASH值中不带协议族因素, 因为地址本身就包含了<br>&nbsp;return h &amp; hmask;<br>}</div>
<div>// 地址HASH<br>static inline unsigned int __addr_hash(xfrm_address_t *daddr, xfrm_address_t *saddr, unsigned short family, unsigned int hmask)<br>{<br>&nbsp;unsigned int h = 0;</div>
<div>&nbsp;switch (family) {<br>// 用源,目的地址同时进行HASH<br>&nbsp;case AF_INET:<br>&nbsp;&nbsp;h = __xfrm4_daddr_saddr_hash(daddr, saddr);<br>&nbsp;&nbsp;break;</div>
<div>&nbsp;case AF_INET6:<br>&nbsp;&nbsp;h = __xfrm6_daddr_saddr_hash(daddr, saddr);<br>&nbsp;&nbsp;break;<br>&nbsp;};<br>// 高16位与低16位异或,高16位不变<br>&nbsp;h ^= (h &gt;&gt; 16);<br>// 与掩码相与<br>&nbsp;return h &amp; hmask;<br>}</div>
<div><br>在net/xfrm/xfrm_hash.c 文件中定义了HASH表的分配和释放函数:</div>
<div><br>struct hlist_head *xfrm_hash_alloc(unsigned int sz)<br>{<br>&nbsp;struct hlist_head *n;<br>// 根据HASH表大小选择合适的分配方法</div>
<div>// 大小不超过PAGE_SIZE, 用kmalloc分配<br>&nbsp;if (sz &lt;= PAGE_SIZE)<br>&nbsp;&nbsp;n = kmalloc(sz, GFP_KERNEL);<br>// 这是在内核定义NUMA和IA64下用vmalloc分配<br>&nbsp;else if (hashdist)<br>&nbsp;&nbsp;n = __vmalloc(sz, GFP_KERNEL, PAGE_KERNEL);<br>&nbsp;else<br>// 其他类型的内核用get_free_page分配<br>&nbsp;&nbsp;n = (struct hlist_head *)<br>&nbsp;&nbsp;&nbsp;__get_free_pages(GFP_KERNEL, get_order(sz));</div>
<div>// 空间清零<br>&nbsp;if (n)<br>&nbsp;&nbsp;memset(n, 0, sz);</div>
<div>&nbsp;return n;<br>}</div>
<div>// 释放HASH表空间<br>void xfrm_hash_free(struct hlist_head *n, unsigned int sz)<br>{<br>&nbsp;if (sz &lt;= PAGE_SIZE)<br>&nbsp;&nbsp;kfree(n);<br>&nbsp;else if (hashdist)<br>&nbsp;&nbsp;vfree(n);<br>&nbsp;else<br>&nbsp;&nbsp;free_pages((unsigned long)n, get_order(sz));<br>}</div>
<div><br>6.2 算法操作</div>
<div><br>IPSEC操作中用到的认证, 加密, 压缩等算法具体实现是在crypto目录下, 而在xfrm中只是定义这些算法的说明, 表示最大可以支持这些算法, 在使用时会探测这些算法是否在内核中存在从而确定可使用的算法.</div>
<div>关于算法的数据结构如下:</div>
<div>/* include/net/xfrm.h */</div>
<div>// 认证算法参数<br>struct xfrm_algo_auth_info {<br>&nbsp;u16 icv_truncbits; // 初始向量截断位数<br>&nbsp;u16 icv_fullbits;&nbsp; // 初始向量总的位数<br>};</div>
<div>// 加密算法参数<br>struct xfrm_algo_encr_info {<br>&nbsp;u16 blockbits;&nbsp; // 块位数<br>&nbsp;u16 defkeybits; // 密钥长度位数<br>};</div>
<div>// 压缩算法参数<br>struct xfrm_algo_comp_info {<br>&nbsp;u16 threshold;&nbsp; // 阈值<br>};</div>
<div>// xfrm算法描述<br>struct xfrm_algo_desc {<br>&nbsp;char *name;&nbsp; // 名称<br>&nbsp;char *compat; // 名称缩写<br>&nbsp;u8 available:1; // 算法是否可用(是否在内核中)<br>&nbsp;union {<br>&nbsp;&nbsp;struct xfrm_algo_auth_info auth;<br>&nbsp;&nbsp;struct xfrm_algo_encr_info encr;<br>&nbsp;&nbsp;struct xfrm_algo_comp_info comp;<br>&nbsp;} uinfo; // 算法信息联合<br>&nbsp;struct sadb_alg desc; // 通用算法描述<br>};</div>
<div><br>6.2.1 认证算法</div>
<div>可用的认证算法通过下面的数组来描述, 包含NULL, MD5, SHA1, SHA256, RIPEMD160等认证算法:</div>
<div>static struct xfrm_algo_desc aalg_list[] = {<br>......<br>{<br>&nbsp;.name = "hmac(sha1)",<br>&nbsp;.compat = "sha1",</div>
<div>&nbsp;.uinfo = {<br>&nbsp;&nbsp;.auth = {<br>&nbsp;&nbsp;&nbsp;.icv_truncbits = 96,// 96位截断<br>&nbsp;&nbsp;&nbsp;.icv_fullbits = 160, // 总共160位<br>&nbsp;&nbsp;}<br>&nbsp;},</div>
<div>&nbsp;.desc = { // 这是对SHA1认证算法的标准描述参数<br>&nbsp;&nbsp;.sadb_alg_id = SADB_AALG_SHA1HMAC, // 算法ID值<br>&nbsp;&nbsp;.sadb_alg_ivlen = 0,<br>&nbsp;&nbsp;.sadb_alg_minbits = 160,<br>&nbsp;&nbsp;.sadb_alg_maxbits = 160<br>&nbsp;}<br>},<br>......</div>
<div><br>相关操作函数:</div>
<div>// 通过算法ID查找认证算法<br>struct xfrm_algo_desc *xfrm_aalg_get_byid(int alg_id)<br>{<br>&nbsp;int i;<br>// 遍历认证数组<br>&nbsp;for (i = 0; i &lt; aalg_entries(); i++) {<br>// 查找和指定算法ID相同的算法<br>&nbsp;&nbsp;if (aalg_list.desc.sadb_alg_id == alg_id) {<br>// 检查该算法是否可用<br>&nbsp;&nbsp;&nbsp;if (aalg_list.available)<br>&nbsp;&nbsp;&nbsp;&nbsp;return &amp;aalg_list;<br>&nbsp;&nbsp;&nbsp;else<br>&nbsp;&nbsp;&nbsp;&nbsp;break;<br>&nbsp;&nbsp;}<br>&nbsp;}<br>&nbsp;return NULL;<br>}<br>EXPORT_SYMBOL_GPL(xfrm_aalg_get_byid);</div>
<div><br>// 统计可用的认证算法数量, 就是available的认证算法数量累加<br>int xfrm_count_auth_supported(void)<br>{<br>&nbsp;int i, n;</div>
<div>&nbsp;for (i = 0, n = 0; i &lt; aalg_entries(); i++)<br>&nbsp;&nbsp;if (aalg_list.available)<br>&nbsp;&nbsp;&nbsp;n++;<br>&nbsp;return n;<br>}<br>EXPORT_SYMBOL_GPL(xfrm_count_auth_supported);</div>
<div>6.2.2 加密算法</div>
<div><br>可用的认证算法通过下面的数组来描述, 包含NULL, DES, 3DES, CAST, AES, BLOWFISH, TWOFISH, SERPENT等加密算法:</div>
<div>static struct xfrm_algo_desc ealg_list[] = {<br>......<br>{<br>&nbsp;.name = "cbc(des3_ede)",<br>&nbsp;.compat = "des3_ede",</div>
<div>&nbsp;.uinfo = {<br>&nbsp;&nbsp;.encr = {<br>&nbsp;&nbsp;&nbsp;.blockbits = 64,<br>&nbsp;&nbsp;&nbsp;.defkeybits = 192,<br>&nbsp;&nbsp;}<br>&nbsp;},</div>
<div>&nbsp;.desc = {<br>&nbsp;&nbsp;.sadb_alg_id = SADB_EALG_3DESCBC,<br>&nbsp;&nbsp;.sadb_alg_ivlen = 8,<br>&nbsp;&nbsp;.sadb_alg_minbits = 192,<br>&nbsp;&nbsp;.sadb_alg_maxbits = 192<br>&nbsp;}<br>},<br>......</div>
<div><br>// 通过算法ID查找加密算法, 和认证算法查找类似<br>struct xfrm_algo_desc *xfrm_ealg_get_byid(int alg_id)<br>{<br>&nbsp;int i;</div>
<div>&nbsp;for (i = 0; i &lt; ealg_entries(); i++) {<br>&nbsp;&nbsp;if (ealg_list.desc.sadb_alg_id == alg_id) {<br>&nbsp;&nbsp;&nbsp;if (ealg_list.available)<br>&nbsp;&nbsp;&nbsp;&nbsp;return &amp;ealg_list;<br>&nbsp;&nbsp;&nbsp;else<br>&nbsp;&nbsp;&nbsp;&nbsp;break;<br>&nbsp;&nbsp;}<br>&nbsp;}<br>&nbsp;return NULL;<br>}<br>EXPORT_SYMBOL_GPL(xfrm_ealg_get_byid);</div>
<div><br>// 统计可用的加密算法数量, 就是available的加密算法数量累加<br>int xfrm_count_enc_supported(void)<br>{<br>&nbsp;int i, n;</div>
<div>&nbsp;for (i = 0, n = 0; i &lt; ealg_entries(); i++)<br>&nbsp;&nbsp;if (ealg_list.available)<br>&nbsp;&nbsp;&nbsp;n++;<br>&nbsp;return n;<br>}<br>EXPORT_SYMBOL_GPL(xfrm_count_enc_supported);</div>
<div><br>6.2.3 压缩算法</div>
<div><br>可用的压缩算法通过下面的数组来描述, 包含DELFATE, LZS, LZJH等压缩算法:</div>
<div>static struct xfrm_algo_desc calg_list[] = {<br>......<br>{<br>&nbsp;.name = "lzs",<br>&nbsp;.uinfo = {<br>&nbsp;&nbsp;.comp = {<br>&nbsp;&nbsp;&nbsp;.threshold = 90,<br>&nbsp;&nbsp;}<br>&nbsp;},<br>&nbsp;.desc = { .sadb_alg_id = SADB_X_CALG_LZS }<br>},<br>......</div>
<div><br>// 通过算法ID查找加密算法, 和认证算法查找类似<br>struct xfrm_algo_desc *xfrm_calg_get_byid(int alg_id)<br>{<br>&nbsp;int i;</div>
<div>&nbsp;for (i = 0; i &lt; calg_entries(); i++) {<br>&nbsp;&nbsp;if (calg_list.desc.sadb_alg_id == alg_id) {<br>&nbsp;&nbsp;&nbsp;if (calg_list.available)<br>&nbsp;&nbsp;&nbsp;&nbsp;return &amp;calg_list;<br>&nbsp;&nbsp;&nbsp;else<br>&nbsp;&nbsp;&nbsp;&nbsp;break;<br>&nbsp;&nbsp;}<br>&nbsp;}<br>&nbsp;return NULL;<br>}<br>EXPORT_SYMBOL_GPL(xfrm_calg_get_byid);</div>
<div><br>6.2.4 通过名称查找算法</div>
<div><br>// 输入参数为算法数组, 数组元素个数, 类型, 掩码, 名称和是否探测在内核中存在<br>static struct xfrm_algo_desc *xfrm_get_byname(struct xfrm_algo_desc *list,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int entries, u32 type, u32 mask,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char *name, int probe)<br>{<br>&nbsp;int i, status;</div>
<div>&nbsp;if (!name)<br>&nbsp;&nbsp;return NULL;<br>// 遍历数组<br>&nbsp;for (i = 0; i &lt; entries; i++) {<br>// 比较算法名称或缩写名称是否和指定名称相同<br>&nbsp;&nbsp;if (strcmp(name, list.name) &amp;&amp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (!list.compat || strcmp(name, list.compat)))<br>&nbsp;&nbsp;&nbsp;continue;<br>// 找到算法结构<br>// 检查算法是否在内核可用, 可用的话成功返回<br>&nbsp;&nbsp;if (list.available)<br>&nbsp;&nbsp;&nbsp;return &amp;list;<br>// 如果不需要探测, 将返回空<br>&nbsp;&nbsp;if (!probe)<br>&nbsp;&nbsp;&nbsp;break;<br>// 需要探测算法算法存在内核, 调用crypto_has_alg()函数探测<br>// 返回0表示失败, 非0表示成功<br>&nbsp;&nbsp;status = crypto_has_alg(name, type, mask | CRYPTO_ALG_ASYNC);<br>&nbsp;&nbsp;if (!status)<br>&nbsp;&nbsp;&nbsp;break;<br>// 算法可用, 返回<br>&nbsp;&nbsp;list.available = status;<br>&nbsp;&nbsp;return &amp;list;<br>&nbsp;}<br>&nbsp;return NULL;<br>}</div>
<div><br>/* crypto/api.c */<br>// 算法探测<br>int crypto_has_alg(const char *name, u32 type, u32 mask)<br>{<br>&nbsp;int ret = 0;<br>// 根据名称, 类型和掩码探测算法模块<br>&nbsp;struct crypto_alg *alg = crypto_alg_mod_lookup(name, type, mask);</div>
<div>// 正确返回找到&nbsp;<br>&nbsp;if (!IS_ERR(alg)) {<br>// 减少模块计数, 返回1<br>&nbsp;&nbsp;crypto_mod_put(alg);<br>&nbsp;&nbsp;ret = 1;<br>&nbsp;}<br>&nbsp;<br>&nbsp;return ret;<br>}</div>
<div><br>有了xfrm_get_byname()这个通用基本函数, 具体类型的算法查找函数就很简单了:</div>
<div><br>// 通过名称查找认证算法<br>struct xfrm_algo_desc *xfrm_aalg_get_byname(char *name, int probe)<br>{<br>&nbsp;return xfrm_get_byname(aalg_list, aalg_entries(),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CRYPTO_ALG_TYPE_HASH, CRYPTO_ALG_TYPE_HASH_MASK,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; name, probe);<br>}<br>EXPORT_SYMBOL_GPL(xfrm_aalg_get_byname);</div>
<div>// 通过名称查找加密算法<br>struct xfrm_algo_desc *xfrm_ealg_get_byname(char *name, int probe)<br>{<br>&nbsp;return xfrm_get_byname(ealg_list, ealg_entries(),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CRYPTO_ALG_TYPE_BLKCIPHER, CRYPTO_ALG_TYPE_MASK,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; name, probe);<br>}<br>EXPORT_SYMBOL_GPL(xfrm_ealg_get_byname);</div>
<div>// 通过名称查找压缩算法<br>struct xfrm_algo_desc *xfrm_calg_get_byname(char *name, int probe)<br>{<br>&nbsp;return xfrm_get_byname(calg_list, calg_entries(),<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CRYPTO_ALG_TYPE_COMPRESS, CRYPTO_ALG_TYPE_MASK,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; name, probe);<br>}<br>EXPORT_SYMBOL_GPL(xfrm_calg_get_byname);</div>
<div>&nbsp;</div>
<div>以下是通过索引号来查找算法, 就是直接返回相应数组指定位置的算法:</div>
<div><br>struct xfrm_algo_desc *xfrm_aalg_get_byidx(unsigned int idx)<br>{<br>&nbsp;if (idx &gt;= aalg_entries())<br>&nbsp;&nbsp;return NULL;</div>
<div>&nbsp;return &amp;aalg_list;<br>}<br>EXPORT_SYMBOL_GPL(xfrm_aalg_get_byidx);</div>
<div>struct xfrm_algo_desc *xfrm_ealg_get_byidx(unsigned int idx)<br>{<br>&nbsp;if (idx &gt;= ealg_entries())<br>&nbsp;&nbsp;return NULL;</div>
<div>&nbsp;return &amp;ealg_list;<br>}<br>EXPORT_SYMBOL_GPL(xfrm_ealg_get_byidx);</div>
<div><br>6.2.5 xfrm算法探测</div>
<div><br>该函数在SA进行调整时会调用来查看当前内核中支持的各种算法</div>
<div>/*<br>&nbsp;* Probe for the availability of crypto algorithms, and set the available<br>&nbsp;* flag for any algorithms found on the system.&nbsp; This is typically called by<br>&nbsp;* pfkey during userspace SA add, update or register.<br>&nbsp;*/<br>void xfrm_probe_algs(void)<br>{<br>// 内核必须定义CRYPTO选项, 否则就是空函数了<br>#ifdef CONFIG_CRYPTO</div>
<div>&nbsp;int i, status;<br>&nbsp;<br>&nbsp;BUG_ON(in_softirq());<br>// 遍历认证算法数组<br>&nbsp;for (i = 0; i &lt; aalg_entries(); i++) {<br>// 根据算法名称确定该HASH算法是否存在, 返回0不存在, 非0存在<br>&nbsp;&nbsp;status = crypto_has_hash(aalg_list.name, 0,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CRYPTO_ALG_ASYNC);<br>// 如果状态和原来的状态不同, 更改<br>&nbsp;&nbsp;if (aalg_list.available != status)<br>&nbsp;&nbsp;&nbsp;aalg_list.available = status;<br>&nbsp;}<br>&nbsp;<br>// 遍历加密算法数组<br>&nbsp;for (i = 0; i &lt; ealg_entries(); i++) {<br>// 根据算法名称确定该加密算法是否存在, 返回0不存在, 非0存在<br>&nbsp;&nbsp;status = crypto_has_blkcipher(ealg_list.name, 0,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CRYPTO_ALG_ASYNC);<br>// 如果状态和原来的状态不同, 更改<br>&nbsp;&nbsp;if (ealg_list.available != status)<br>&nbsp;&nbsp;&nbsp;ealg_list.available = status;<br>&nbsp;}<br>&nbsp;<br>// 遍历压缩算法数组<br>&nbsp;for (i = 0; i &lt; calg_entries(); i++) {<br>// 根据算法名称确定该压缩算法是否存在, 返回0不存在, 非0存在<br>&nbsp;&nbsp;status = crypto_has_comp(calg_list.name, 0,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CRYPTO_ALG_ASYNC);<br>// 如果状态和原来的状态不同, 更改<br>&nbsp;&nbsp;if (calg_list.available != status)<br>&nbsp;&nbsp;&nbsp;calg_list.available = status;<br>&nbsp;}<br>#endif<br>}<br>EXPORT_SYMBOL_GPL(xfrm_probe_algs);</div>
<div><br>6.3 通过netlink套接口访问xfrm</div>
<div><br>通过netlink套接口访问xfrm的处理函数在net/xfrm/xfrm_user.c中,
提供了Linux特色的非标准PF_KEY接口的SA, SP控制方法, 能完成和PF_KEY一样控制功能,
目前iproute2中的ip工具中新增加的xfrm命令就是通过这种netlink接口来完成的, 因为netlink操作以前已经介绍过,
xfrm的操作又都是一样的, 因此本文不再分析其实现过程.</div>
<div><br>6.4 xfrm_input</div>
<div><br>在net/xfrm/xfrm_input.c文件中定义了关于安全路径(struct sec_path)的几个处理函数, 用于对输入的IPSEC包进行解析构造安全路径使用.</div>
<div>// 释放安全路径<br>void __secpath_destroy(struct sec_path *sp)<br>{<br>&nbsp;int i;<br>// 减少安全路径中所有SA的使用计数<br>&nbsp;for (i = 0; i &lt; sp-&gt;len; i++)<br>&nbsp;&nbsp;xfrm_state_put(sp-&gt;xvec);<br>// 释放安全路径空间<br>&nbsp;kmem_cache_free(secpath_cachep, sp);<br>}<br>EXPORT_SYMBOL(__secpath_destroy);</div>
<div>// 安全路径复制<br>struct sec_path *secpath_dup(struct sec_path *src)<br>{<br>&nbsp;struct sec_path *sp;<br>// 先分配安全路径结构<br>&nbsp;sp = kmem_cache_alloc(secpath_cachep, SLAB_ATOMIC);<br>&nbsp;if (!sp)<br>&nbsp;&nbsp;return NULL;</div>
<div>&nbsp;sp-&gt;len = 0;<br>&nbsp;if (src) {<br>&nbsp;&nbsp;int i;<br>// 如果源安全路径结构非空, 将其全部复制到新结构中<br>&nbsp;&nbsp;memcpy(sp, src, sizeof(*sp));<br>// 增加安全路径中所有SA的使用计数<br>&nbsp;&nbsp;for (i = 0; i &lt; sp-&gt;len; i++)<br>&nbsp;&nbsp;&nbsp;xfrm_state_hold(sp-&gt;xvec);<br>&nbsp;}<br>// 设置该引用计数初始值位1<br>&nbsp;atomic_set(&amp;sp-&gt;refcnt, 1);<br>&nbsp;return sp;<br>}<br>EXPORT_SYMBOL(secpath_dup);</div>
<div>&nbsp;</div>
<div>/* Fetch spi and seq from ipsec header */<br>// 从数据包中解析SPI和序号, 返回值是网络序的<br>int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, __be32 *spi, __be32 *seq)<br>{<br>&nbsp;int offset, offset_seq;<br>// 通过nexthdr参数来判断协议类型, nexthdr是IPV6里的说法, 在IPV4中就是IP头里的协议字段<br>// 根据不同协议确定数据中SPI和序列号相对数据起始点的偏移<br>&nbsp;switch (nexthdr) {<br>&nbsp;case IPPROTO_AH:<br>&nbsp;&nbsp;offset = offsetof(struct ip_auth_hdr, spi);<br>&nbsp;&nbsp;offset_seq = offsetof(struct ip_auth_hdr, seq_no);<br>&nbsp;&nbsp;break;<br>&nbsp;case IPPROTO_ESP:<br>&nbsp;&nbsp;offset = offsetof(struct ip_esp_hdr, spi);<br>&nbsp;&nbsp;offset_seq = offsetof(struct ip_esp_hdr, seq_no);<br>&nbsp;&nbsp;break;<br>&nbsp;case IPPROTO_COMP:<br>// 对应压缩协议单独处理<br>// 数据头准备出IP压缩头结构长度<br>&nbsp;&nbsp;if (!pskb_may_pull(skb, sizeof(struct ip_comp_hdr)))<br>&nbsp;&nbsp;&nbsp;return -EINVAL;<br>// SPI值取第3,4字节的数据, 序号为0<br>&nbsp;&nbsp;*spi = htonl(ntohs(*(__be16*)(skb-&gt;h.raw + 2)));<br>&nbsp;&nbsp;*seq = 0;<br>&nbsp;&nbsp;return 0;<br>&nbsp;default:<br>&nbsp;&nbsp;return 1;<br>&nbsp;}<br>// 数据头准备16字节空间, 这是ip_auth_hdr和ip_esp_hdr结构最小长度<br>&nbsp;if (!pskb_may_pull(skb, 16))<br>&nbsp;&nbsp;return -EINVAL;<br>// 根据偏移获取SPI和序号, 注意是网络序的值<br>&nbsp;*spi = *(__be32*)(skb-&gt;h.raw + offset);<br>&nbsp;*seq = *(__be32*)(skb-&gt;h.raw + offset_seq);<br>&nbsp;return 0;<br>}<br>EXPORT_SYMBOL(xfrm_parse_spi);</div>
<div><br>...... 待续 ......</div>

meijusan123 发表于 2012-08-13 21:08

写了非常的好,谢谢楼主分享。

小弟,看了帖子,很想问个相关的问题:
int crypto_has_alg(const char *name, u32 type, u32 mask),这个API是用来检查内核是否支持相关的算法,请问一个小问题,type和mask这两个参数是怎么用的?

type指什么?mask做什么?

谢谢!
页: [1]
查看完整版本: Linux内核中的IPSEC实现(4)