本文介绍dpvs中黑名单的哈希表的实现
哈希表初始化,申请有2^16个双向链表头的空间,next和prev都指向head。
static int blklst_lcore_init(void *args)
{
int i;
if (!rte_lcore_is_enabled(rte_lcore_id()))
return EDPVS_DISABLED;
this_blklst_tab = rte_malloc(NULL,
sizeof(struct list_head) * DPVS_BLKLST_TAB_SIZE,
RTE_CACHE_LINE_SIZE);
if (!this_blklst_tab)
return EDPVS_NOMEM;
for (i = 0; i < DPVS_BLKLST_TAB_SIZE; i++)
INIT_LIST_HEAD(&this_blklst_tab[i]);
return EDPVS_OK;
}
哈希散列函数,目的ip和黑名单ip转为主机序后分别与各自数据类型的长度相乘,加上一个随机数,最后取低16位。DPVS_BLKLST_TAB_MASK是16位的全1。
static inline uint32_t blklst_hashkey(const union inet_addr *vaddr,
const union inet_addr *blklst)
{
/* jhash hurts performance, we do not use rte_jhash_2words here */
return ((rte_be_to_cpu_32(vaddr->in.s_addr) * 31
+ rte_be_to_cpu_32(blklst->in.s_addr)) * 31
+ dp_vs_blklst_rnd) & DPVS_BLKLST_TAB_MASK;
}
添加黑名单,先检查是否已存在。不重复的话,通过散列函数计算hashkey。申请一个entry空间存储黑名单信息,this_blklst_tab表中hashkey为下标的位置作为链表头,下一跳指向刚申请的entry空间。
static int dp_vs_blklst_add_lcore(int af, uint8_t proto, const union inet_addr *vaddr,
uint16_t vport, const union inet_addr *blklst)
{
unsigned hashkey;
struct blklst_entry *new, *blklst_node;
blklst_node = dp_vs_blklst_lookup(af, proto, vaddr, vport, blklst);
if (blklst_node) {
return EDPVS_EXIST;
}
hashkey = blklst_hashkey(vaddr, blklst);
new = rte_zmalloc("new_blklst_entry", sizeof(struct blklst_entry), 0);
if (new == NULL)
return EDPVS_NOMEM;
new->af = af;
new->proto = proto;
new->vport = vport;
memcpy(&new->vaddr, vaddr, sizeof(union inet_addr));
memcpy(&new->blklst, blklst, sizeof(union inet_addr));
list_add(&new->list, &this_blklst_tab[hashkey]);
rte_atomic32_inc(&this_num_blklsts);
return EDPVS_OK;
}
list_add要使用 \dpvs\include\list.h的,跑偏了就看不明白了。
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
哈希查找,通过目的ip和黑名单ip计算hashkey,从this_blklst_tab表中hashkey为下标的位置的链表node开始查找,找到目的ip和黑名单ip都相同的node。
struct blklst_entry *dp_vs_blklst_lookup(int af, uint8_t proto, const union inet_addr *vaddr,
uint16_t vport, const union inet_addr *blklst)
{
unsigned hashkey;
struct blklst_entry *blklst_node;
hashkey = blklst_hashkey(vaddr, blklst);
list_for_each_entry(blklst_node, &this_blklst_tab[hashkey], list) {
if (blklst_node->af == af && blklst_node->proto == proto &&
blklst_node->vport == vport &&
inet_addr_equal(af, &blklst_node->vaddr, vaddr) &&
inet_addr_equal(af, &blklst_node->blklst, blklst))
return blklst_node;
}
return NULL;
}
理论上支持O(1) 查找的黑名单条目最多2^16个,若出现hashkey重复的情况那就是哈希冲突了。hashkey相同,目的ip和黑名单ip不同,这个entry会在this_blklst_tab[hashkey]这条链表下面顺延。典型的哈希表结构。