3
votes

I have been tasked with coding a IPv4-IPv6 translator (RFC 6146) for the Linux kernel. In short, it's a gateway that stands between IPv4 and IPv6 networks and allows transparent communication between them by switching packet headers and masking addresses.

Seeing that the kernel has Netfilter, a framework for altering packets, originally I thought I could just write a Netfilter module and translate from there. I could intercept all packets, resize them using the usual skb_pull/skb_push operations, override some bytes and finally return them happily to the kernel.

As it turns out, switching protocols is too extreme for Netfilter. Because the code that processes IPv4 packets is completely independent from the one that processes IPv6 packets, code both prior and subsequent of Netfilter modules assume the header of the network protocol survives. So if I change the IPv4 header for a IPv6 one, the kernel will go nuts because it will keep reading the header as if it was a IPv4 one.

(Or at least, that's what I believe after reading net/ipv4/ip_input.c; the first thing the kernel does after the call to NF_HOOK is extract the IPv4 header during ip_rcv_finish().)

I see a second option: Deducing a new sk_buff from scratch, ask Netfilter to NF_DROP the original packet, and then send the new packet somehow.

Here's where my limited familiarity with the kernel's principles and style stagnate me: I'd like to minimize frowning upon my solution, yet replacing the entire packet instead of altering its headers seems unnatural, inneficient, and even blasphemous to Netfilter's design. But I can't tell for sure, and I would like experienced advise. Also, I see no other choice.

Questions: Are there other options? What would the most natural approach be?

I would like to support all kernel versions since 2.6. If that's impossible, newer is better.

1
I don't see how you can avoid replacing the entire packet, since you're swapping different network headers. You will have to re-inject the new packet into the stack, and it will be expected to have data link headers (e.g. ethernet) in place. NAT64 is a dumb hack anyway; by trying to somehow make it efficient, you're effectively polishing a turd. Just make it work first.Kaz

1 Answers

1
votes

I've written once a ebtables target that job was to tag an Ethernet frame with VLAN tag. This required shifting and altering Ethernet header before the frame data. So it's doing similar thing, but at the lower level. skb's can be enlarged at the beginning in-place (most of the time at least, I guess) so performance should be OK.

Direct link: http://goo.gl/3DPEB

It's "008-ebt-vlan_t-0.1.diff.bz2" under:

http://goo.gl/S1tVv

Please note that I haven't cared for this code for more than 4 years and I wasn't working with Linux networking code since that time. Ebtables has been merged into netfilter in the meantime, AFAIK. But I think the basic approach should still work and can help you.