日期:2014-05-16  浏览次数:20772 次

Linux内核中的IPSEC实现(4)
本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn

6. XFRM的其他操作

6.1 HASH处理

关于HASH值的计算方法主要在net/xfrm/xfrm_hash.h中定义:
// IPV4地址HASH
static inline unsigned int __xfrm4_addr_hash(xfrm_address_t *addr)
{
// 就是地址本身
 return ntohl(addr->a4);
}
// IPV6地址HASH
static inline unsigned int __xfrm6_addr_hash(xfrm_address_t *addr)
{
// 取后2个32位数异或
 return ntohl(addr->a6[2] ^ addr->a6[3]);
}
// IPV4源,目的地址HASH
static inline unsigned int __xfrm4_daddr_saddr_hash(xfrm_address_t *daddr, xfrm_address_t *saddr)
{
// 将两个地址异或
 return ntohl(daddr->a4 ^ saddr->a4);
}
// IPV4源,目的地址HASH
static inline unsigned int __xfrm6_daddr_saddr_hash(xfrm_address_t *daddr, xfrm_address_t *saddr)
{
// 两个V6地址都取后2个32位数异或
 return ntohl(daddr->a6[2] ^ daddr->a6[3] ^
       saddr->a6[2] ^ saddr->a6[3]);
}
// 目的地址HASH
static inline unsigned int __xfrm_dst_hash(xfrm_address_t *daddr, xfrm_address_t *saddr,
        u32 reqid, unsigned short family,
        unsigned int hmask)
{
// 协议族和请求ID异或
 unsigned int h = family ^ reqid;
 switch (family) {
// HASH值再和源目的地址HASH结果进行异或
 case AF_INET:
  h ^= __xfrm4_daddr_saddr_hash(daddr, saddr);
  break;
 case AF_INET6:
  h ^= __xfrm6_daddr_saddr_hash(daddr, saddr);
  break;
 }
// 将HASH结果高低16位异或存低16位,高16位不动, 然后用HASH掩码相与
 return (h ^ (h >> 16)) & hmask;
}

// 源地址HASH, 只是没有请求ID项, 其他HASH过程和上面相同
static inline unsigned __xfrm_src_hash(xfrm_address_t *daddr,
           xfrm_address_t *saddr,
           unsigned short family,
           unsigned int hmask)
{
 unsigned int h = family;
 switch (family) {
 case AF_INET:
  h ^= __xfrm4_daddr_saddr_hash(daddr, saddr);
  break;
 case AF_INET6:
  h ^= __xfrm6_daddr_saddr_hash(daddr, saddr);
  break;
 };
 return (h ^ (h >> 16)) & hmask;
}

// 根据SPI计算HASH值
static inline unsigned int
__xfrm_spi_hash(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family,
  unsigned int hmask)
{
// 先将SPI和协议进行异或
 unsigned int h = (__force u32)spi ^ proto;
 switch (family) {
// HASH值再和目的地址进行单一地址HASH值异或
 case AF_INET:
  h ^= __xfrm4_addr_hash(daddr);
  break;
 case AF_INET6:
  h ^= __xfrm6_addr_hash(daddr);
  break;
 }
// HASH值再和本身的高22位, 高12位异或后再和掩码相与
 return (h ^ (h >> 10) ^ (h >> 20)) & hmask;
}

// 索引号HASH
static inline unsigned int __idx_hash(u32 index, unsigned int hmask)
{
// 低24位和高24位异或, 高8位不动, 再和掩码相与
 return (index ^ (index >> 8)) & hmask;
}

// 选择子HASH
static inline unsigned int __sel_hash(struct xfrm_selector *sel, unsigned short family, unsigned int hmask)
{
// 提前源和目的地址
 xfrm_address_t *daddr = &sel->daddr;
 xfrm_address_t *saddr = &sel->saddr;
 unsigned int h = 0;
 switch (family) {
// 用源,目的地址同时进行HASH
 case AF_INET:
  if (sel->prefixlen_d != 32 ||
      sel->prefixlen_s != 32)
   return hmask + 1;
  h = __xfrm4_daddr_saddr_hash(daddr, saddr);
  break;
 case AF_INET6:
  if (sel->prefixlen_d != 128 ||
      sel->prefixlen_s != 128)
   return hmask + 1;
  h = __xfrm6_daddr_saddr_hash(daddr, saddr);
  break;
 };
// 高16位与低16位异或,高16位不变
 h ^= (h >> 16);
// 与掩码相与, 其实HASH值中不带协议族因素, 因为地址本身就包含了
 return h & hmask;
}
// 地址HASH
static inline unsigned int __addr_hash(xfrm_address_t *daddr, xfrm_address_t *saddr, unsigned short family, unsigned int hmask)
{
 unsigned int h = 0;
 switch (family) {
// 用源,目的地址同时进行HASH
 case AF_INET:
  h = __xfrm4_daddr_saddr_hash(daddr, saddr);
  break;
 case AF_INET6:
  h = __xfrm6_daddr_saddr_hash(daddr, saddr);
  break;
 };
// 高16位与低16位异或,高16位不变
 h ^= (h >> 16);
// 与掩码相与
 return h & hmask;
}

在net/xfrm/xfrm_hash.c 文件中定义了HASH表的分配和释放函数:

struct hlist_head *xfrm_hash_alloc(unsigned int sz)
{
 struct hlist_head *n;
// 根据HASH表大小选择合适的分配方法
// 大小不超过PAGE_SIZE, 用kmalloc分配
 if (sz <= PAGE_SIZE)
  n = kmalloc(sz, GFP_KERNEL);
// 这是在内核定义NUMA和IA64下用vmalloc分配
 else if (hashdist)
  n = __vmalloc(sz, GFP_KERNEL, PAGE_KERNEL);
 else
// 其他类型的内