AF-XDP: Как мне получить `ctx-> data_meta` из ядра в пространство пользователя? - PullRequest
0 голосов
/ 02 марта 2020

Я хочу измерить задержку пакетов для моей программы AF-XDP. Я смотрел на эту ссылку: https://github.com/xdp-project/xdp-project/blob/master/areas/arm64/xdp_for_tsn.org

и адаптировал ее к этому:

SEC("xdp_sock")
int xdp_sock_prog(struct xdp_md *ctx) {

    int index = ctx->rx_queue_index;

    void *data_end = (void *)(long)ctx->data_end;
    void *data = (void *)(long)ctx->data;

    const unsigned long long kstamp = bpf_ktime_get_ns();

    if(data - sizeof(unsigned long long) <= data_end) {
        ctx->data_meta = ctx->data - sizeof(unsigned long long);
        memcpy(&ctx->data_meta, &kstamp, sizeof(unsigned long long));
    }
    ...
}

В таком случае доступ в пользовательское пространство происходит следующим образом:

static bool process_packet(struct xsk_socket_info *xsk, uint64_t addr, uint32_t len) {

    uint8_t *pkt = xsk_umem__get_data(xsk->umem->buffer, addr);
    uint8_t *pkt_meta = xsk_umem__get_data(xsk->umem->buffer, addr - sizeof(unsigned long long));

    const unsigned long long kstamp = (uint64_t) pkt_meta[7] << 56 | (uint64_t) pkt_meta[6] << 48 | (uint64_t) pkt_meta[5] << 40
                                        | (uint64_t) pkt_meta[4] << 32 | (uint64_t) pkt_meta[3] << 24 | (uint64_t) pkt_meta[2] << 16
                                        | (uint64_t) pkt_meta[1] << 8  | (uint64_t) pkt_meta[0];
...
}

Но я не могу загрузить XDP-программу в ядро:

Load XDP program...
libbpf: load bpf program failed: Permission denied
libbpf: -- BEGIN DUMP LOG ---
libbpf: 
0: (bf) r6 = r1
1: (61) r1 = *(u32 *)(r6 +16)
2: (63) *(u32 *)(r10 -4) = r1
3: (61) r7 = *(u32 *)(r6 +4)
4: (61) r8 = *(u32 *)(r6 +0)
5: (85) call bpf_ktime_get_ns#5
6: (bf) r1 = r8
7: (07) r1 += -8
8: (2d) if r1 > r7 goto pc+3
 R0_w=inv(id=0) R1_w=pkt(id=0,off=-8,r=0,imm=0) R6_w=ctx(id=0,off=0,imm=0) R7_w=pkt_end(id=0,off=0,imm=0) R8_w=pkt(id=0,off=0,r=0,imm=0) R10=fp0 fp-8=mmmm????
9: (63) *(u32 *)(r6 +8) = r0
invalid bpf_context access off=8 size=4
processed 10 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0

libbpf: -- END LOG --

Я имею в виду, это не должно быть возможно, верно? Потому что XDP - это все о допустимых доступах к памяти и почему доступ за пределами определенных границ data и data_end будет действительным?

Imho, это может работать, если ctx->data_meta оставить без изменений - но тогда у меня есть проблема в пользовательском пространстве, потому что я не знаю, где находится data_meta. Есть ли какая-нибудь вспомогательная функция libbpf для получения доступа к метаданным пакета?

Возможно также, что доступ действителен, но моя проверка диапазона неверна ...

1 Ответ

1 голос
/ 03 марта 2020

Используемый вами фрагмент описывается в руководстве как псевдокод, он не является полным и функциональным примером. Давайте посмотрим на журнал верификатора:

Load XDP program...
libbpf: load bpf program failed: Permission denied
libbpf: -- BEGIN DUMP LOG ---
libbpf: 
0: (bf) r6 = r1                 // r6 points to ctx
1: (61) r1 = *(u32 *)(r6 +16)   // [let's ignore that]
2: (63) *(u32 *)(r10 -4) = r1   // [and that]
3: (61) r7 = *(u32 *)(r6 +4)    // r7 points to ctx->data_end
4: (61) r8 = *(u32 *)(r6 +0)    // r8 points to ctx->data
5: (85) call bpf_ktime_get_ns#5 // r0 now contains timestamp
6: (bf) r1 = r8                 // r1 points to ctx->data
7: (07) r1 += -8                // r1 = data - sizeof(unsigned long long)
8: (2d) if r1 > r7 goto pc+3    // if(data-sizeof(unsigned long long)>data_end) jump;

Теперь самое интересное. Мы пытаемся сделать:

        ctx->data_meta = ctx->data - sizeof(unsigned long long);
        memcpy(&ctx->data_meta, &kstamp, sizeof(unsigned long long));

И он производит:

 R0_w=inv(id=0) R1_w=pkt(id=0,off=-8,r=0,imm=0) R6_w=ctx(id=0,off=0,imm=0)
 R7_w=pkt_end(id=0,off=0,imm=0) R8_w=pkt(id=0,off=0,r=0,imm=0) R10=fp0 fp-8=mmmm????
9: (63) *(u32 *)(r6 +8) = r0
invalid bpf_context access off=8 size=4

r6 указывает на ctx типа struct xdp_md, поэтому r6 +8 равно ctx->data - sizeof(unsigned long long). Это потому, что по умолчанию ctx->data_meta указывает на ctx->data. Поэтому, когда вы пытаетесь скопировать метку времени в ctx->data_meta, вы фактически пытаетесь записать ее за 8 байтов до начала пакетных данных. У вас нет определенного c места, зарезервированного для размещения метаданных, так что это внешний доступ: верификатор жалуется, что это неверно.

Но как мы можем создать, выделить некоторые место для метаданных, тогда? Это делается благодаря помощнику bpf_xdp_adjust_meta() BPF, как в примере, на который вы ссылались .

Однако не все драйверы NI C поддерживают метаданные XDP , Некоторые из них вызывают внутреннюю функцию xdp_set_data_meta_invalid(), которая устанавливает ctx->data_meta в ctx->data + 1 вместо ctx->data. Если bpf_xdp_adjust_meta(), вызванный из программы BPF, обнаруживает, что эта настройка выполнена, выдает ошибку с -ENOTSUPP и отказывается от настройки ctx->data_meta. Исходя из обсуждения в комментариях, похоже, что происходит в вашем случае.

Если у вас нет поддержки метаданных XDP, вы можете попытаться закодировать метку времени в другом поле вашего пакета. BPF-карты также могут быть опцией, но вам потребуется одна запись для каждого пакета, поэтому вы не уверены в ее влиянии на память / производительность.

...