BIND9新引入了视图的概念,简单的来讲就是能根据不同的来源IP来返回不同的数据。其中网段的存储,网段的快速匹配都是用基数树来实现的。下面是BIND创建基数树的代码。
相关结构体 BIND的IP地址结构
1 2 3 4 5 6 7 8 9 10 11 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表示掩码位
1 2 3 4 5 6 7 8 9 typedef struct isc_prefix { unsigned int family; unsigned int bitlen; isc_refcount_t refcount; union { struct in_addr sin ; struct in6_addr sin6 ; } add; } isc_prefix_t ;
IP结构转换为节点结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #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的插入和匹配过程都会用到它
1 2 3 4 5 6 7 8 9 typedef struct isc_radix_node { isc_uint32_t bit ; isc_prefix_t *prefix; struct isc_radix_node *l , *r ; struct isc_radix_node *parent ; void *data[2 ]; int node_num[2 ]; } isc_radix_node_t ;
根节点结构
1 2 3 4 5 6 7 8 typedef struct isc_radix_tree { unsigned int magic; isc_mem_t *mctx; isc_radix_node_t *head; isc_uint32_t maxbits; int num_active_node; int num_added_node; } isc_radix_tree_t ;
函数 插入节点函数,其中redix是树的根节点,prefix是要插入的节点。
1 2 3 4 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大的在右边)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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,找到第一个不同的位
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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 ; } for (j = 0 ; j < 8 ; j++) { if (BIT_TEST (r, (0x80 >> j))) break ; } INSIST(j < 8 ); differ_bit = i * 8 + j; break ; }
如果differ_bit小于node->parent->bit,说明新节点和node不在同一个网段,node节点上移,直到node和新节点 同处于node的父节点网段下
1 2 3 4 5 6 7 8 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是同一网段
1 2 3 if (differ_bit == bitlen && node->bit == bitlen) { ...... }
2、 新节点是node的子网段
1 2 3 if (node->bit == differ_bit) { ...... }
3、 node是新节点的子网段
1 2 3 if (bitlen == differ_bit) { ...... }
4、新节点和node是两个不相关的网段