как xdp ebpf изменяет контрольную сумму tcphdr после обновления порта назначения - PullRequest
2 голосов
/ 28 мая 2020

как xdp ebpf изменяет контрольную сумму tcphdr после обновления порта назначения?

// Check tcp header size
struct tcphdr *tcph = data + nh_off;
nh_off += sizeof(struct tcphdr);
if (data + nh_off > data_end) {
    return XDP_PASS;
}
tcph->dest = bpf_ntohs(5555);
// ... i'm trying change checksum of tcphdr, it's not work for me. 
tcph->check = 0;
tcph->check = checksum((unsigned short *)tcph, sizeof(struct tcphdr));

return XDP_TX;

это код функции, с помощью которого я пытаюсь изменить контрольную сумму tcp-пакета

static inline unsigned short checksum(unsigned short *buf, int bufsz) {
    unsigned long sum = 0;

    while (bufsz > 1) {
        sum += *buf;
        buf++;
        bufsz -= 2;
    }

    if (bufsz == 1) {
        sum += *(unsigned char *)buf;
    }

    sum = (sum & 0xffff) + (sum >> 16);
    sum = (sum & 0xffff) + (sum >> 16);

    return ~sum;
}

Я загружаю программу xdp для интерфейса lo и хотите проксировать пакет на порт 5555 в том же сетевом интерфейсе.

Ответы [ 3 ]

1 голос
/ 28 мая 2020

Если вы не работаете с аппаратной разгрузкой, вы, вероятно, захотите использовать соответствующий помощник BPF bpf_l4_csum_replace() (или, альтернативно, bpf_csum_diff()).

 * int bpf_l4_csum_replace(struct sk_buff *skb, u32 offset, u64 from, u64 to, u64 flags)
 *  Description
 *      Recompute the layer 4 (e.g. TCP, UDP or ICMP) checksum for the
 *      packet associated to *skb*. Computation is incremental, so the
 *      helper must know the former value of the header field that was
 *      modified (*from*), the new value of this field (*to*), and the
 *      number of bytes (2 or 4) for this field, stored on the lowest
 *      four bits of *flags*. Alternatively, it is possible to store
 *      the difference between the previous and the new values of the
 *      header field in *to*, by setting *from* and the four lowest
 *      bits of *flags* to 0. For both methods, *offset* indicates the
 *      location of the IP checksum within the packet. In addition to
 *      the size of the field, *flags* can be added (bitwise OR) actual
 *      flags. With **BPF_F_MARK_MANGLED_0**, a null checksum is left
 *      untouched (unless **BPF_F_MARK_ENFORCE** is added as well), and
 *      for updates resulting in a null checksum the value is set to
 *      **CSUM_MANGLED_0** instead. Flag **BPF_F_PSEUDO_HDR** indicates
 *      the checksum is to be computed against a pseudo-header.
 *
 *      This helper works in combination with **bpf_csum_diff**\ (),
 *      which does not update the checksum in-place, but offers more
 *      flexibility and can handle sizes larger than 2 or 4 for the
 *      checksum to update.
 *
 *      A call to this helper is susceptible to change the underlying
 *      packet buffer. Therefore, at load time, all checks on pointers
 *      previously done by the verifier are invalidated and must be
 *      performed again, if the helper is used in combination with
 *      direct packet access.
 *  Return
 *      0 on success, or a negative error in case of failure.

Примеры ядра или Cilium покажет пример использования.

Если вы не можете его использовать, существует реализация eBPF от Netronome , доступная здесь , которая может вам помочь.

0 голосов
/ 02 июня 2020
INTERNAL void update_tcp_header_port(struct tcphdr* tcp, __u16 *new_val)
{
    __u16 old_check = tcp->check;
    __u32 new_csum_value;
    __u32 new_csum_comp;
    __u32 undo;

    /* Get old sum of headers by getting one's compliment and adding
     * one's compliment of old header value (effectively subtracking)
     */
    undo = ~((__u32) tcp->check) + ~((__u32) tcp->dest);

    /* Check for old header overflow and compensate
     * Add new header value
     */
    new_csum_value = undo + (undo < ~((__u32) tcp->dest)) + (__u32) *new_val;

    /* Check for new header overflow and compensate */
    new_csum_comp = new_csum_value + (new_csum_value < ((__u32) *new_val));

    /* Add any overflow of the 16 bit value to itself */
    new_csum_comp = (new_csum_comp & 0xFFFF) + (new_csum_comp >> 16);

    /* Check that overflow added above did not cause another overflow */
    new_csum_comp = (new_csum_comp & 0xFFFF) + (new_csum_comp >> 16);

    /* Cast to 16 bit one's compliment of sum of headers */
    // tcp->check = (__u16) ~new_csum_comp;
    tcp->check = (__u16)10494;

    printt("old check: %d, old dest: %d, new port: %d\n", old_check, (__u16)bpf_ntohs(tcp->dest), *new_val);
    /* Update header to new value */
    tcp->dest = (__u16)bpf_ntohs(*new_val);
    return;
}
0 голосов
/ 28 мая 2020

возможно, я не понял этого, но из предложенного вами примера я изменяю только свойство нужной мне структуры и пересчитываю контрольную сумму пакета ipv4:

    __u16 new_port = bpf_ntohs(5555);
    __u32 csum = 0;
    update_header_field(&tcph->check, &tcph->dest, &new_port);
    /* Update IPv4 header checksum */
    iph->check = 0;
    __u16 *p_iph_16 = (__u16 *)iph;
    #pragma clang loop unroll(full)
    for (int i = 0; i < (int)sizeof(*iph) >> 1; i++)
        csum += *p_iph_16++;
    iph->check = ~((csum & 0xffff) + (csum >> 16));

    return XDP_TX;

, но результат то же самое, запрос не доходит до сервера, работающего на порту 5555.

...