天天看點

Knot-DNS-sorted-rrset

今天發現Knot2.7.4新增了answer rrset rotation.(官網介紹是在2.7.3就已經支援,但實際在2.7.4中才有該功能)

經過實際測試發現,該版本僅對非ECS查詢的普通響應進行了排序,針對ECS的響應仍然是按照配置檔案的書寫順序固定傳回。

其中排序的核心是這個函數,主要邏輯如下:

根據參數 rotate 标定的起始位置,将配置的記錄不改變前後次序的條件下,重新排序填充到響應封包

_public_
int knot_rrset_to_wire_extra(const knot_rrset_t *rrset, uint8_t *wire,
                             uint16_t max_size, uint16_t rotate,
                             knot_compr_t *compr, uint16_t flags)
{
	if (rrset == NULL || wire == NULL) {
		return KNOT_EINVAL;
	}
	if (rrset->rrs.count == 0) {
		return 0;
	}
	if (rotate != 0) {
		rotate %= rrset->rrs.count;
	}

	uint8_t *write = wire;
	size_t capacity = max_size;

	uint16_t count = rrset->rrs.count;
	for (uint16_t i = rotate; i < count + rotate; i++) {
		uint16_t pos = (i < count) ? i : (i - count);
		int ret = write_rr(rrset, pos, &write, &capacity, compr, flags);
		if (ret != KNOT_EOK) {
			return ret;
		}
	}

	return write - wire;
}
           

對于非ECS的普通查詢:

put_answer() -> process_query_put_rr() -> knot_pkt_put_rotate() -> knot_rrset_to_wire_extra()

uint16_t rotate = conf()->cache.srv_ans_rotate ? knot_wire_get_id(qdata->query->wire) : 0;
	uint16_t prev_count = pkt->rrset_count;
	ret = knot_pkt_put_rotate(pkt, compr_hint, &to_add, rotate, flags);
	if (ret != KNOT_EOK && (flags & KNOT_PF_FREE)) {
		knot_rrset_clear(&to_add, &pkt->mm);
		return ret;
	}
           

從這段代碼可以看出,在 process_query_put_rr() 中根據查詢ID産生rotate,通過層層傳遞給 knot_rrset_to_wire_extra() 排序的起始位置。

對于ECS的查詢:

geoip_process() -> knot_pkt_put() -> knot_pkt_put_rotate() -> knot_rrset_to_wire_extra()

/*! \brief Same as knot_pkt_put_rotate but without rrset rotation. */
static inline int knot_pkt_put(knot_pkt_t *pkt, uint16_t compr_hint,
                               const knot_rrset_t *rr, uint16_t flags)
{
	return knot_pkt_put_rotate(pkt, compr_hint, rr, 0, flags);
}
           

從這段代碼可以看出,在knot_pkt_put() 中 傳遞進去的rotate參數是0,也就是說這裡并沒有對響應rr進行排序。

繼續閱讀