Re: Access packet payload in TC egress programs


Federico Parola
 

One update on point 2.
I found out that every time a pointer to the packet is increased of a variable value stored in a variable greater than 1 byte, subsequent checks against packet boundaries become ineffective.
In my example if I change payload_offset from unsigned to u8 the program is accepted, even though my offset can now only be 256 bytes at maximum.
Here is a toy example to test the problem:

int test(struct __sk_buff *ctx) {
void *data = (void *)(long)ctx->data;
void *data_end = (void *)(long)ctx->data_end;

/* Skipping an amount of bytes stored in __u8 works */
if (data + sizeof(__u8) > data_end)
return TC_ACT_OK;
bpf_trace_printk("Skipping %d bytes", *(__u8 *)data);
data += *(__u8 *)data;

/* Skipping an amount of bytes stored in __u16 works but... */
if (data + sizeof(__u16) > data_end)
return TC_ACT_OK;
bpf_trace_printk("Skipping %d bytes", *(__u16 *)data);
data += *(__u16 *)data;

/* ...this check is not effective and packet access is rejected */
if (data + sizeof(__u8) > data_end)
return TC_ACT_OK;
bpf_trace_printk("Next byte is %x", *(__u8 *)data);

return TC_ACT_OK;
}

My practical use case would be skipping varaible-size TLS header extensions until I reach the desired one.

On 22/10/21 09:31, Federico Parola wrote:
Thanks for the answer, I wasn't aware of the existence of that helper.
I have two additional comments:
1. The documentation of the helper says that passing a length of zero should pull the whole length of the packet [1], however with that parameter the length of direct accessible data stays unchanged. I think there is a mismatch in the behavior and the documentation.
2. I'd like to avoid re-parsing all the headers after I have pulled new data. To do so I save the offset I just reached (the end of the TCP header), pull data, get the new data and data_end pointers and add the offset to data. However the verifier does not accept my accesses to the packet from this point on. Here is some example code:
unsigned payload_offset = (void *)tcph + (tcph->doff << 2) - data;
bpf_skb_pull_data(ctx, ctx->len);
data = (void *)(long)ctx->data;
data_end = (void *)(long)ctx->data_end;
struct tls_record_hdr *rech = data + payload_offset;
if ((void *)(rech + 1) > data_end)
    return TC_ACT_OK;
if (rech->type == TLS_CONTENT_TYPE_HANDSAHKE)
    bpf_trace_printk("It's a handshake");
Running this code gives me the error "R1 offset is outside of the packet" even if I performed the correct check on packet boundaries. If I re-parse all header the code is accepted. Is there a way to solve the problem?
[1] https://github.com/torvalds/linux/blob/master/include/uapi/linux/bpf.h#L2312 On 20/10/21 08:11, Y Song wrote:
On Tue, Oct 19, 2021 at 8:13 AM Federico Parola
<federico.parola@...> wrote:

Dear all,
how can I access the payload of the packet in a program attached to the
TC egress hook (SCHED_CLS attached to clsact qdisc)?
ctx->data_end points to the end of the L4 header, while on the ingress
hook it points to the end of the packet (tested on kernel v5.14).
This could be the case that linear data only covers up to the end of
L4 header. In such cases, you can use bpf_skb_pull_data() helper
to get more data into linear region and after that your ctx->data_end
will point to much later packet data.


Best regards,
Federico Parola




Join iovisor-dev@lists.iovisor.org to automatically receive all group messages.