Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add XDP redirect example #5

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

astoycos
Copy link

@astoycos astoycos commented Nov 1, 2022

No description provided.

Signed-off-by: astoycos <[email protected]>
Add the ability to update the Dport of the packet,
and add some documentation.

Signed-off-by: astoycos <[email protected]>
@astoycos astoycos marked this pull request as draft November 1, 2022 19:35
@astoycos
Copy link
Author

astoycos commented Nov 1, 2022

Not at all ready to go in yet, XDP bits are working but userspace is still hardcoded :)

bpf/xdp_udp.bpf.c Show resolved Hide resolved
// Here we only want to iterate through payload
// NOT trailing bits
for (int i = 0; i <= MAX_UDP_LENGTH; i += 2) {
if (i > udp_packet_nibbles) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this test will ever be true because nibbles is 2 * number of bytes. The if statement on line 78 will end the loop.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I'm still getting confused here... maybe it's because i'm using the term nibble incorrectly

So the issue is, data_end is a PTR which includes the payload AND any trailing bits, while I forced udp_packet_nibbles to be the number of "digits" in the raw hex payload...

For example in the following packet

13:23:15.756911 06:56:87:ec:fd:1f > 86:ad:33:29:ff:5e, ethertype IPv4 (0x0800), length 60: (tos 0x0, ttl 57, id 20891, offset 0, flags [DF], proto UDP (17), length 33)
    10.8.125.12.58980 > 192.168.10.2.sapv1: [bad udp cksum 0xd301 -> 0xaf43!] UDP, length 5
        0x0000:  86ad 3329 ff5e 0656 87ec fd1f 0800 4500
        0x0010:  0021 519b 4000 3911 9e72 0a08 7d0c c0a8
        0x0020:  0a02 e664 2693 000d d301 7465 7374 0a00
        0x0030:  0000 0000 d2f2 935d 0000 0000

The UDP header + Data is

e664 2693 000d d301 7465 7374 0a00
0000 0000 d2f2 935d 0000 0000

so

    for (int i = 0; i <= MAX_UDP_LENGTH; i += 2) {
        if ((void *)(buf + 1) > data_end) {
            break;
        }
        csum_total += *buf;
        buf++;
    }

would iterate through all of that ^^ (i.e increment buff and read 13 times == 26 bytes of data)

Here packet length is 000d(13 bytes) so I know that I want to exit after incrementing buff 6.5 times ignoring those last 6.5 iterations (13 bytes of data)

The confusing part here is that i is a nibble index (1 hex digit (4 bytes)) (maybe) here, so that we can represent those 6 16 bit iterations and 1 8 bit iteration

I guess I'm confused trying to calculate 6 from the packet data

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A nibble is half a byte, i.e. 4 bits, 0..15 (what I think you are calling a hex digit). It's not useful to think about nibbles or try and do anything in terms of nibbles, except if you are writing your own code to convert numbers to hex strings.

__u16 *buf = (void *)udp;

This declares buf to be a pointer that will point to __u16 width values. buf++ will increment it by 2, to point at the next __u16 width value:

__u16 *buf = 0xf00;
printf("%x\n", buf);
printf("%x\n", buf + 1);
f00
f02

This means that if ((void *)(buf + 1) > data_end) would trigger too early because buf + 1 is actually +2 and would exit even if there were 2 bytes to be read.

It's safer to do all the arithmetic in bytes since the payload is a byte stream and the udp->len is in bytes.

Copy link
Author

@astoycos astoycos Nov 3, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated this to be much simpler

bpf/xdp_udp.bpf.c Show resolved Hide resolved
bpf/xdp_udp.bpf.c Outdated Show resolved Hide resolved

static __always_inline __u16 iph_csum(struct iphdr *iph) {
iph->check = 0;
unsigned long long csum = bpf_csum_diff(0, 0, (unsigned int *)iph, sizeof(struct iphdr), 0);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be possible to use bpf_csum_diff to just recompute the checksum for the changed fields, i.e. just the src and dest ip pseudo headers in the UDP csum. Need to see if we can find an example of a bpf program that does this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed I had tried that in the past with little luck which is why I did it so manually here, but it would definitely be much easier

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I tried this

static __always_inline __u16 udp_csum_diff(struct udphdr *udp) {
    udp->check = 0;
    unsigned long long csum = bpf_csum_diff(0, 0, (unsigned int *)udp, sizeof(struct udphdr), 0);
    return csum_fold_helper(csum);
}

But it doesn't return the right values (I know the Manual Cksum calc is working)

[016] dNs3. 354704.109793: bpf_trace_printk: Manual Cksum: C8A7 Diff Cksum 8B14

// Last byte
if (i + 1 == udp_len) {
csum_total += (*(__u8 *)buf);
// Verifier fails without this print statement, I have no Idea why :/
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@donaldh this was baffling

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants