BIND中基数树的建立

BIND9新引入了视图的概念,简单的来讲就是能根据不同的来源IP来返回不同的数据。其中网段的存储,网段的快速匹配都是用基数树来实现的。下面是BIND创建基数树的代码。

BIND的IP地址结构

struct isc_netaddr {
    unsigned int family;
    union {
        struct in_addr in;
        struct in6_addr in6;
#ifdef ISC_PLATFORM_HAVESYSUNH
        char un[sizeof(((struct sockaddr_un *)0)->sun_path)];
#endif
    } type;
    isc_uint32_t zone;
};

BIND 二叉树中表示网段的结构,该结构由sturct in_addr结构处理转换而来, bitlen表示掩码位

typedef struct isc_prefix {
    unsigned int family;    /* AF_INET | AF_INET6, or AF_UNSPEC for "any" */
    unsigned int bitlen;    /* 0 for "any" */
    isc_refcount_t refcount;
    union {
        struct in_addr sin;
        struct in6_addr sin6;
    } add;
} isc_prefix_t;

IP结构转换为节点结构

#define NETADDR_TO_PREFIX_T(na,pt,bits) \
    do { \
        memset(&(pt), 0, sizeof(pt)); \
        if((na) != NULL) { \
            (pt).family = (na)->family; \
            (pt).bitlen = (bits); \
            if ((pt).family == AF_INET6) { \
                memcpy(&(pt).add.sin6, &(na)->type.in6, \
                       ((bits)+7)/8); \
            } else \
                memcpy(&(pt).add.sin, &(na)->type.in, \
                       ((bits)+7)/8); \
        } else { \
            (pt).family = AF_UNSPEC; \
            (pt).bitlen = 0; \
        } \
        isc_refcount_init(&(pt).refcount, 0); \
    } while(0)

节点结构,其中的bit表示网段的掩码位,决定着redix tree的结构,redix tree的插入和匹配过程都会用到它

typedef struct isc_radix_node {
   isc_uint32_t bit;            /* bit length of the prefix */
   isc_prefix_t *prefix;        /* who we are in radix tree */
   struct isc_radix_node *l, *r;    /* left and right children */
   struct isc_radix_node *parent;   /* may be used */
   void *data[2];           /* pointers to IPv4 and IPV6 data */
   int node_num[2];         /* which node this was in the tree,
                       or -1 for glue nodes */
} isc_radix_node_t;

根节点结构

typedef struct isc_radix_tree {
   unsigned int     magic;
   isc_mem_t        *mctx;
   isc_radix_node_t     *head;
   isc_uint32_t     maxbits;    /* for IP, 32 bit addresses */
   int num_active_node;         /* for debugging purposes */
   int num_added_node;          /* total number of nodes */
} isc_radix_tree_t;

插入节点函数,其中redix是树的根节点,prefix是要插入的节点。

isc_result_t
isc_radix_insert(isc_radix_tree_t *radix, isc_radix_node_t **target,
         isc_radix_node_t *source, isc_prefix_t *prefix)
{

从根节点开始往下搜寻插入位置,bitlen是新插入节点的掩码位。addr是4字节的char型字符串,占32位, 存储着要插入的节点IP地址。根据bit,addr来查找“插入位置”: 判断addr的第bit位是不是为1,如果是,则与右节点进行比较,否则与左节点进行比较。直到匹配到bit值大于或者等于bitlen的叶节点。(内部节点的IP是网段的中间值,比如网段是192.168.8.0/24,那么节点的ip就是192.168.8.128, 比较时先假设网段相同,IP小的在左边,IP大的在右边)

while (node->bit < bitlen || node->prefix == NULL) {
    if (node->bit < radix->maxbits &&
        BIT_TEST(addr[node->bit >> 3], 0x80 >> (node->bit & 0x07)))
    {
        if (node->r == NULL)
            break;
        node = node->r;
    } else {

        if (node->l == NULL)
            break;
        node = node->l;
    }

    INSIST(node != NULL);
}

比较新节点和node,找到第一个不同的位

/* Find the first bit different. */
check_bit = (node->bit < bitlen) ? node->bit : bitlen;
differ_bit = 0;

for (i = 0; i*8 < check_bit; i++) {
    if ((r = (addr[i] ^ test_addr[i])) == 0) {

        differ_bit = (i + 1) * 8;
        continue;
    }
    /* I know the better way, but for now. */
    for (j = 0; j < 8; j++) {
        if (BIT_TEST (r, (0x80 >> j)))
            break;
    }
    /* Must be found. */
    INSIST(j < 8);
    differ_bit = i * 8 + j;
    break;
}

如果differ_bit小于node->parent->bit,说明新节点和node不在同一个网段,node节点上移,直到node和新节点 同处于node的父节点网段下

if (differ_bit > check_bit)
    differ_bit = check_bit;

parent = node->parent;
while (parent != NULL && parent->bit >= differ_bit) {
    node = parent;
    parent = node->parent;
}

下面就分情况,具体就是确定父节点,子节点 和为节点的prefix赋值

1、 新节点和node是同一网段

if (differ_bit == bitlen && node->bit == bitlen) {
    ......
}

2、 新节点是node的子网段

if (node->bit == differ_bit) {
    ......
}

3、 node是新节点的子网段

if (bitlen == differ_bit) {
    ......
}

4、新节点和node是两个不相关的网段

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值