LINUX.ORG.RU
ФорумAdmin

Изменение данных пакета и пересчет контрольной суммы

 , ,


0

1

Всем привет. Пишу модуль для ядра Linux. Перехватываю пакет через хук, меняю данные и хочу отправить в сеть. Проблема в том, что после изменения данных и пересчета контрольной суммы - пакет не отправляется. Помогите, пожалуйста. разобраться. Код:

Получаю адрес начала данных:

user_data_ptr = (char *)(skb->data + sizeof(struct iphdr) + sizeof(struct udphdr));

Вычисляю длину данных:

unsigned long iLenData = skb->len - (sizeof(struct iphdr) + sizeof(struct udphdr));

Модифицирую данные, например так:

user_data_ptr[iLenData-2] = ‘k’;

Пересчитываю контрольные суммы:

udp->check = 0;

udp->check = csum((uint16_t*) udp, (iLenData+sizeof(struct udphdr)));

ip->check = 0;

ip->check = csum((uint16_t*) ip, (ip->ihl << 1));

Еще вопросик: Как добавить свои данные к эти данным? Нужно ли менять tot_len и len в заголовках?

Если ничего не трогать, то пакет отправляется в сеть, но я хочу его модифицировать.

Спасибо за ответы!



Последнее исправление: WeSTMan (всего исправлений: 4)

Исправить КС в пакете мало. В sk_buff тоже нужно править.

есть хороший пример net/sched/act_csum.c

Есть в iptables target CHECKSUM. Посмотри его исходник.

vel ★★★★★
()
Ответ на: комментарий от rubic

Спасибо за статью. Подход работает, только я иногда ловлю панику ядра. Вот код, и я думаю, что в нем есть проблемы. Как минимум - я увидел, что tail > end, но как исправить - пока не могу додуматься. Помогите, пожалуйста, разобраться!

unsigned char extra_data[] = «MyData»;

unsigned char *temp;

data = (char *)(skb->data + sizeof(struct iphdr) + sizeof(struct udphdr));

temp = kmalloc(1550 * sizeof(char), GFP_ATOMIC);

data_len = skb->len - TOT_HDR_LEN;

memcpy(temp, data, data_len);

unsigned char *ptr = temp + data_len;

extra_data_len = sizeof(extra_data);

memcpy(ptr, extra_data, extra_data_len);

tot_data_len = data_len + extra_data_len - 1;

skb_put(skb, extra_data_len - 1);

memcpy(data, temp, tot_data_len);

kfree(temp);

ip->tot_len = htons(tot_data_len + TOT_HDR_LEN);

udp->len = htons(tot_data_len + UDP_HDR_LEN);

ip->daddr = in_aton(«IP_dest»);

udp->dest = htons(iPort);

И дальше пересчет контрольных сумм. Пару раз передаст пакеты и упадет в панику ядра.

Ошибка:

skbuff: skb_over_panic: text:ffffffffc0207181 len:675 put:20 head
:ffff8800d7030800 data:ffff8800d7030820 tail:0x2c3 end:0x2c0 dev:ppp0

WeSTMan
() автор топика
Последнее исправление: WeSTMan (всего исправлений: 1)
Ответ на: комментарий от WeSTMan

skb_over_panic() есть только в skb_put(). Загляни в исходники.

у тебя 3 байта не влезло.

А еще skb можент быть из нескольких фрагментов. Там все еще веселей.

vel ★★★★★
()
Ответ на: комментарий от vel

Я понял, что не влезло, но как расширить буфер - не понимаю… Подскажите?) Я бы предположил сдвинуть конец буфера, но думаю, что поймаю segfault

skb->end += (размер, которого не хватает)

WeSTMan
() автор топика
Последнее исправление: WeSTMan (всего исправлений: 1)
Ответ на: комментарий от vel

Спасибо, пошел читать, чтобы я делал без вас))

WeSTMan
() автор топика
Ответ на: комментарий от vel

Чего то я не понимаю, в исходниках нашел хороший пример:

skb2 = skb_copy_expand(skb, EEM_HLEN, ETH_FCS_LEN + padlen, GFP_ATOMIC);

dev_kfree_skb_any(skb);

skb = skb2;

if (!skb) return skb;

Сделал по примеру:

struct sk_buff *skb_copy;

skb_copy = skb_copy_expand(skb, skb_headroom(skb), skb->tail+extra_data_len, GFP_ATOMIC);

if (!skb_copy) {

printk("Not skb_copy :(\n");
						 
dev_kfree_skb(skb_copy);

return NF_DROP;

}

dev_kfree_skb(skb);

skb = skb_copy;

Получил ошибку:

BUG: unable to handle kernel NULL pointer dereference at 0000000000000048

А так же работу трейса

[31266.194001] [] ip_output+0x5f/0xd0

[31266.194515] [] ? __ip_local_out+0x52/0xf0

[31266.195050] [] ? ip_fragment.constprop.53+0x80/0x80

[31266.195570] [] ip_local_out+0x3b/0x50

[31266.196091] [] ip_send_skb+0x19/0x40

[31266.196611] [] udp_send_skb+0x16d/0x290

[31266.197128] [] udp_sendmsg+0x533/0xb20

[31266.197649] [] ? ip_reply_glue_bits+0x60/0x60

[31266.198172] [] ? aa_sock_msg_perm+0x5d/0x140

[31266.198727] [] inet_sendmsg+0x6d/0xa0

[31266.199094] [] sock_sendmsg+0x3e/0x50

[31266.199446] [] SYSC_sendto+0x102/0x190

[31266.199794] [] ? dentry_free+0x4e/0x90

[31266.200148] [] ? __dentry_kill+0x157/0x1e0

[31266.200623] [] SyS_sendto+0xe/0x10

[31266.201147] [] compat_SyS_socketcall+0x272/0x4c0

[31266.201673] [] ? do_gettimeofday+0x1a/0x50

[31266.202198] [] do_fast_syscall_32+0xb2/0x150

[31266.202723] [] sysenter_flags_fixed+0x8/0x15

Как я понял - идет фрагментация? Или что-то с указателем :(

И почему то я получил неверные данные:

tail - 176 | end - 192

[31986.663522] skb copy success copied

[31986.664141] tail - 196 | end - 704

Тут видно, что не хватает 4 байт, но почему end = 704?

WeSTMan
() автор топика
Последнее исправление: WeSTMan (всего исправлений: 3)
Ответ на: комментарий от WeSTMan

Зачем ты полез в ядро, если через NFQUEUE можно менять данные?

Работа с skb_buff очень непроста. Отладка ядерного кода требует специфических знаний.

Замена skb_buff через skb_copy_expand() возможна далеко не в любом месте сетевого стека.

Внутри iptables/nftables проще дропнуть оригинальный пакет и послать заново созданный пакет.

vel ★★★★★
()
Ответ на: комментарий от vel

Согласен, попробую через приложение. Спасибо за ваши ответы!

WeSTMan
() автор топика
Ответ на: комментарий от vel

Спасибо за подсказку. Вроде пока все работает :)

WeSTMan
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.