今天發現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進行排序。