Мне поручили написать транслятор IPv4-IPv6 (RFC 6146) для ядра Linux. . Короче говоря, это шлюз, который стоит между сетями IPv4 и IPv6 и обеспечивает прозрачную связь между ними путем переключения заголовков пакетов и маскирования адресов.
Увидев, что в ядре есть Netfilter, фреймворк для изменения пакетов, изначально я подумал, что могу просто написать модуль Netfilter и транслировать оттуда. Я мог бы перехватить все пакеты, изменить их размер с помощью обычных операций skb_pull/skb_push, переопределить некоторые байты и, наконец, счастливо вернуть их ядру.
Как оказалось, переключение протоколов слишком экстремально для Netfilter. Поскольку код, обрабатывающий пакеты IPv4, полностью независим от кода, обрабатывающего пакеты IPv6, как предыдущий, так и последующий код модулей Netfilter предполагают, что заголовок сетевого протокола сохраняется. Поэтому, если я изменю заголовок IPv4 на заголовок IPv6, ядро сойдет с ума, потому что оно будет продолжать читать заголовок, как если бы он был заголовком IPv4.
(По крайней мере, так я считаю после прочтения net/ipv4/ip_input.c; первое, что делает ядро после вызова NF_HOOK, — извлекает заголовок IPv4 во время ip_rcv_finish().)
Я вижу второй вариант: вывести новый sk_buff с нуля, попросить Netfilter сделать NF_DROP исходный пакет, а затем каким-то образом отправить новый пакет.
Вот где мое ограниченное знакомство с принципами и стилем ядра застопорило меня: я хотел бы свести к минимуму неодобрение моего решения, но замена всего пакета вместо изменения его заголовков кажется неестественной, неэффективной и даже кощунственной для дизайна Netfilter. Но точно сказать не могу, хотелось бы совета опытных. Кроме того, я не вижу другого выхода.
Вопросы: Есть ли другие варианты? Какой подход был бы наиболее естественным?
Я хотел бы поддерживать все версии ядра, начиная с 2.6. Если это невозможно, новее лучше.