LINUX.ORG.RU

oops при работе модуля netfilter

 , ,


0

1

Здравствуйте. В целях самообразования пишу модуль к netfilter. В целом ничего полезного модуль не делает, просто перехватывает пакет и посылает в ответ сформированный пакет. Все это безобразие работает в PREROUTING хуке таблицы raw, дабы не трогать лишний раз conntrack. Так же решил формировать фрейм полностью и отправлять с помощью dev_queue_xmit(), вместо использования ip_local_out().

#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/netfilter.h>
#include <net/tcp.h>
#include <linux/ip.h>
#include <linux/if_ether.h>
#include <linux/netfilter/x_tables.h>

MODULE_ALIAS("ipt_TEST");
MODULE_LICENSE("GPL");

static int send(struct sk_buff *oldskb)

{
        struct sk_buff *newskb;
        struct ethhdr *neweth;
        struct ethhdr *oldeth;
        struct iphdr *newiph;
        struct iphdr *oldiph;
        struct tcphdr *newth;
        struct tcphdr *oldth;

        oldeth = eth_hdr(oldskb);
        oldiph = ip_hdr(oldskb);
        oldth = (struct tcphdr *)(oldskb->data + oldiph->ihl * 4);

        newskb = alloc_skb(sizeof(*neweth) + sizeof(*newiph) + sizeof(*newth), GFP_ATOMIC);
        if (!newskb) {
                goto out;
                }
        skb_reserve(newskb, sizeof(*neweth)+sizeof(*newiph)+sizeof(*newth));
        newth = (struct tcphdr *)skb_push(newskb, sizeof(*newth));
                newth->source      = oldth->dest;
                newth->dest        = oldth->source;
                newth->seq         = htonl(1234567);
                newth->ack_seq     = htonl(ntohl(oldth->seq)+1);
                tcp_flag_byte(newth) = (TCPHDR_RST);
                newth->doff        = sizeof(*newth) / 4;
                newth->window      = htons(32767);
                newth->urg_ptr     = 0;
                newskb->ip_summed = CHECKSUM_UNNECESSARY;
                newth->check    = 0;

         newiph =(struct iphdr *) skb_push(newskb, sizeof(*newiph));
                newiph->version    = 4;
                newiph->ihl        = sizeof(*newiph) / 4;
                newiph->tos        = oldiph->tos;
                newiph->tot_len    = oldiph->tot_len;
                newiph->id         = 0;
                newiph->frag_off   = htons(IP_DF);
                newiph->ttl        = 64;
                newiph->protocol   = IPPROTO_TCP;
                newiph->check      = 0;
                newiph->saddr      = oldiph->daddr;
                newiph->daddr      = oldiph->saddr;

                newth->check    = csum_tcpudp_magic(newiph->saddr, newiph->daddr,
                        sizeof(*newth), IPPROTO_TCP,
                        csum_partial(newth, sizeof(*newth), 0));

        neweth = (struct ethhdr *)skb_push(newskb, sizeof(*neweth));
        memcpy(neweth->h_dest, oldeth->h_source, ETH_ALEN);
        memcpy(neweth->h_source, oldeth->h_dest, ETH_ALEN);
        neweth->h_proto = oldeth->h_proto;
        newskb->priority = oldskb->priority;
        newskb->dev =  oldskb->dev;
        newskb->protocol = htons(ETH_P_IP);
        newskb->len = sizeof(*neweth)+sizeof(*newiph)+sizeof(*newth);
printk(KERN_CRIT "Begin dev_queue_xmit.\n");
        dev_queue_xmit(newskb);
        goto out;
out:
        if (oldskb);
        kfree_skb(oldskb);
        if (newskb)
        kfree_skb(newskb);
}

unsigned int test_tg(struct sk_buff *skb) {
        const struct iphdr *iph = ip_hdr(skb);
        const struct tcphdr *th;
        th = (const struct tcphdr *)(skb->data + iph->ihl * 4);
        if ((tcp_flag_byte(th) & (TCPHDR_FIN | TCPHDR_RST | TCPHDR_ACK | TCPHDR_SYN)) == TCPHDR_SYN)
        {
        send(skb);

        return NF_STOLEN;
        }

return NF_DROP;
}
static struct xt_target test_tg_reg __read_mostly = {
        .name           = "TEST",
        .revision       = 0,
        .family         = NFPROTO_IPV4,
        .target         = test_tg,
        .table          = "raw",
        .hooks          = 1 << NF_INET_PRE_ROUTING,
        .proto          = IPPROTO_TCP,
        .me             = THIS_MODULE,
};

static int __init test_tg_init(void) {
        return xt_register_target(&test_tg_reg);
        }

static void __exit test_tg_exit(void) {
         xt_unregister_target(&test_tg_reg);
        }

module_init(test_tg_init);
module_exit(test_tg_exit);

Загружается модуль без проблем, правда когда пакет попадает ему на обработку ядро ловит oops. Ниже привожу call trace:

[  506.836187] Begin dev_queue_xmit.
[  506.838360] protocol 0800 is buggy, dev eth0
[  512.018091] BUG: unable to handle kernel paging request at 0002807c
[  512.020217] IP: [<f810c79a>] scsi_dispatch_cmd+0x74/0x1d0 [scsi_mod]
[  512.020217] *pde = 00000000 
[  512.020217] Oops: 0000 [#1] SMP 
[  512.020217] last sysfs file: /sys/module/x_tables/initstate
[  512.020217] Modules linked in: xt_TEST xt_tcpudp iptable_raw ip_tables x_tables loop snd_intel8x0 tpm_tis tpm tpm_bios snd_ac97_codec ac97_bus snd_pcm joydev parport_pc pcspkr psmouse evdev parport snd_timer serio_raw battery ac button snd i2c_piix4 i2c_core soundcore snd_page_alloc ext3 jbd mbcache usbhid hid sd_mod ide_cd_mod cdrom crc_t10dif ata_generic ata_piix ahci libahci libata ohci_hcd ehci_hcd usbcore scsi_mod piix e1000 ide_core [last unloaded: scsi_wait_scan]
[  512.020217] 
[  512.020217] Pid: 144, comm: kjournald Not tainted 2.6.39.4 #1 innotek GmbH VirtualBox
[  512.020217] EIP: 0060:[<f810c79a>] EFLAGS: 00010206 CPU: 0
[  512.020217] EIP is at scsi_dispatch_cmd+0x74/0x1d0 [scsi_mod]
[  512.020217] EAX: 34800000 EBX: efcf3240 ECX: efc6a08c EDX: 34800000
[  512.020217] ESI: 00028000 EDI: f6704860 EBP: efcf3240 ESP: f6725e58
[  512.020217]  DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0069
[  512.020217] Process kjournald (pid: 144, ti=f6724000 task=efc10820 task.ti=f6724000)
[  512.020217] Stack:
[  512.020217]  efc1b400 efc1ac00 f6704860 f81112b2 efc1c408 efc1b428 efc6a000 efc1b4b8
[  512.020217]  efc1c5f4 f6725ec0 efc1c408 00000000 00000001 c112b14a c112f6a1 00000002
[  512.020217]  f6725ec0 efc1c408 00000002 00000000 c112f81b f6725ec0 00000246 00000000
[  512.020217] Call Trace:
[  512.020217]  [<f81112b2>] ? scsi_request_fn+0x31a/0x44e [scsi_mod]
[  512.020217]  [<c112b14a>] ? __blk_run_queue+0x11/0x12
[  512.020217]  [<c112f6a1>] ? queue_unplugged+0x64/0x72
[  512.020217]  [<c112f81b>] ? blk_flush_plug_list+0x16c/0x17e
[  512.020217]  [<c112f837>] ? blk_finish_plug+0xa/0x24
[  512.020217]  [<f8334c45>] ? journal_commit_transaction+0x412/0xc21 [jbd]
[  512.020217]  [<c10394f7>] ? try_to_del_timer_sync+0x5c/0x63
[  512.020217]  [<c10394f7>] ? try_to_del_timer_sync+0x5c/0x63
[  512.020217]  [<f8337104>] ? kjournald+0xb5/0x1cc [jbd]
[  512.020217]  [<c1044692>] ? wake_up_bit+0x56/0x56
[  512.020217]  [<f833704f>] ? commit_timeout+0x5/0x5 [jbd]
[  512.020217]  [<c104433c>] ? kthread+0x63/0x68
[  512.020217]  [<c10442d9>] ? kthread_worker_fn+0x114/0x114
[  512.020217]  [<c126517e>] ? kernel_thread_helper+0x6/0xd
[  512.020217] Code: 00 00 68 37 cd 11 f8 e9 54 01 00 00 8a 42 61 84 c0 74 18 3c 03 7f 14 8b 4b 30 8b 52 4c 8a 41 01 c1 e2 05 83 e0 1f 09 d0 88 41 01 
[  512.020217]  7e 7c 00 8b be 80 00 00 00 74 2c a1 40 5a 34 c1 81 c7 f4 01 
[  512.020217] EIP: [<f810c79a>] scsi_dispatch_cmd+0x74/0x1d0 [scsi_mod] SS:ESP 0069:f6725e58
[  512.020217] CR2: 000000000002807c
[  512.277079] ---[ end trace 51f1ebb813964968 ]---
[  512.280000] BUG: unable to handle kernel NULL pointer dereference at   (null)
[  512.280979] IP: [<f810ce27>] scsi_get_command+0x53/0x78 [scsi_mod]
[  512.280979] *pde = 00000000 
[  512.280979] Oops: 0002 [#2] SMP 
[  512.280979] last sysfs file: /sys/module/x_tables/initstate
[  512.280979] Modules linked in: xt_TEST xt_tcpudp iptable_raw ip_tables x_tables loop snd_intel8x0 tpm_tis tpm tpm_bios snd_ac97_codec ac97_bus snd_pcm joydev parport_pc pcspkr psmouse evdev parport snd_timer serio_raw battery ac button snd i2c_piix4 i2c_core soundcore snd_page_alloc ext3 jbd mbcache usbhid hid sd_mod ide_cd_mod cdrom crc_t10dif ata_generic ata_piix ahci libahci libata ohci_hcd ehci_hcd usbcore scsi_mod piix e1000 ide_core [last unloaded: scsi_wait_scan]
[  512.280979] 
[  512.280979] Pid: 1301, comm: kworker/0:0 Tainted: G      D     2.6.39.4 #1 innotek GmbH VirtualBox
[  512.280979] EIP: 0060:[<f810ce27>] EFLAGS: 00010082 CPU: 0
[  512.280979] EIP is at scsi_get_command+0x53/0x78 [scsi_mod]
[  512.280979] EAX: 000000d6 EBX: efc1b400 ECX: 00000000 EDX: 00009955
[  512.280979] ESI: efc1b420 EDI: efc1b41c EBP: efc1b404 ESP: f644beb8
[  512.280979]  DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068
[  512.280979] Process kworker/0:0 (pid: 1301, ti=f644a000 task=f652dc70 task.ti=f644a000)
[  512.280979] Stack:
[  512.280979]  00000020 00000000 efc6a948 efc1b400 00001000 f81118c2 015b19e8 00000000
[  512.280979]  f6756800 f828b0b7 efc6a948 efc6a948 efc6a948 feb28187 feb281a4 00000008
[  512.280979]  00000202 feb130ec efc1b400 f828ae19 f6803040 efc6a948 efc1c408 efc1c408
[  512.280979] Call Trace:
[  512.280979]  [<f81118c2>] ? scsi_setup_fs_cmnd+0x53/0x9b [scsi_mod]
[  512.280979]  [<f828b0b7>] ? sd_prep_fn+0x29e/0xb5a [sd_mod]
[  512.280979]  [<f828ae19>] ? sd_unprep_fn+0x22/0x22 [sd_mod]
[  512.280979]  [<c112e86a>] ? blk_peek_request+0xb6/0x182
[  512.280979]  [<f8111004>] ? scsi_request_fn+0x6c/0x44e [scsi_mod]
[  512.280979]  [<c112b14a>] ? __blk_run_queue+0x11/0x12
[  512.280979]  [<c113829b>] ? cfq_kick_queue+0x20/0x31
[  512.280979]  [<c10401dc>] ? process_one_work+0x181/0x25e
[  512.280979]  [<c113827b>] ? cfq_init_queue+0x28b/0x28b
[  512.280979]  [<c1041b68>] ? worker_thread+0xf3/0x1f4
[  512.280979]  [<c1041a75>] ? manage_workers+0x15a/0x15a
[  512.280979]  [<c104433c>] ? kthread+0x63/0x68
[  512.280979]  [<c10442d9>] ? kthread_worker_fn+0x114/0x114
[  512.280979]  [<c126517e>] ? kernel_thread_helper+0x6/0xd
[  512.280979] Code: ff 85 c0 89 c3 74 39 8d 68 04 8d 7e 1c 89 30 89 68 04 89 68 08 89 f8 e8 98 26 15 c9 8b 4e 24 89 6e 24 83 c6 20 89 73 04 89 4b 08 
[  512.280979]  29 89 c2 89 f8 e8 c4 26 15 c9 a1 40 5a 34 c1 89 43 1c eb 07 
[  512.280979] EIP: [<f810ce27>] scsi_get_command+0x53/0x78 [scsi_mod] SS:ESP 0068:f644beb8
[  512.280979] CR2: 0000000000000000
[  512.280979] ---[ end trace 51f1ebb813964969 ]---
[  512.955684] BUG: unable to handle kernel paging request at fffffffc
[  512.957861] IP: [<c1044062>] kthread_data+0x6/0xa
[  512.957861] *pde = 01401067 *pte = 00000000 
[  512.957861] Oops: 0000 [#3] SMP 
[  512.957861] last sysfs file: /sys/module/x_tables/initstate
[  512.957861] Modules linked in: xt_TEST xt_tcpudp iptable_raw ip_tables x_tables loop snd_intel8x0 tpm_tis tpm tpm_bios snd_ac97_codec ac97_bus snd_pcm joydev parport_pc pcspkr psmouse evdev parport snd_timer serio_raw battery ac button snd i2c_piix4 i2c_core soundcore snd_page_alloc ext3 jbd mbcache usbhid hid sd_mod ide_cd_mod cdrom crc_t10dif ata_generic ata_piix ahci libahci libata ohci_hcd ehci_hcd usbcore scsi_mod piix e1000 ide_core [last unloaded: scsi_wait_scan]
[  512.957861] 
[  512.957861] Pid: 1301, comm: kworker/0:0 Tainted: G      D     2.6.39.4 #1 innotek GmbH VirtualBox
[  512.957861] EIP: 0060:[<c1044062>] EFLAGS: 00010096 CPU: 0
[  512.957861] EIP is at kthread_data+0x6/0xa
[  512.957861] EAX: 00000000 EBX: 00000000 ECX: f652de14 EDX: 00000000
[  512.957861] ESI: 00000000 EDI: 00000000 EBP: f652dc70 ESP: f644bcfc
[  512.957861]  DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068
[  512.957861] Process kworker/0:0 (pid: 1301, ti=f644a000 task=f652dc70 task.ti=f644a000)
[  512.957861] Stack:
[  512.957861]  c1041716 f6806180 00000000 c125df8f f641c240 c10c6bab f644bd57 00000000
[  512.957861]  f6732700 c13f9180 f652de14 0000000d c13f9180 f652dc70 c13f9180 c13f9180
[  512.957861]  0000000d f6732710 00000001 00000001 00000086 c1345a80 00000ae0 00000000
[  512.957861] Call Trace:
[  512.957861]  [<c1041716>] ? wq_worker_sleeping+0x9/0x6d
[  512.957861]  [<c125df8f>] ? schedule+0x105/0x5be
[  512.957861]  [<c10c6bab>] ? d_lookup+0x1f/0x2f
[  512.957861]  [<c1031178>] ? release_task+0x375/0x384
[  512.957861]  [<c10326ba>] ? do_exit+0x655/0x663
[  512.957861]  [<c1260c9d>] ? oops_end+0x95/0x99
[  512.957861]  [<c101c6ad>] ? no_context+0x10d/0x116
[  512.957861]  [<c101c7ba>] ? bad_area_nosemaphore+0xa/0xc
[  512.957861]  [<c126239c>] ? do_page_fault+0x1b0/0x35d
[  512.957861]  [<f810cc87>] ? scsi_pool_alloc_command+0x29/0x41 [scsi_mod]
[  512.957861]  [<c12621ec>] ? spurious_fault+0xae/0xae
[  512.957861]  [<c12603af>] ? error_code+0x67/0x6c
[  512.957861]  [<f810ce27>] ? scsi_get_command+0x53/0x78 [scsi_mod]
[  512.957861]  [<f81118c2>] ? scsi_setup_fs_cmnd+0x53/0x9b [scsi_mod]
[  512.957861]  [<f828b0b7>] ? sd_prep_fn+0x29e/0xb5a [sd_mod]
[  512.957861]  [<f828ae19>] ? sd_unprep_fn+0x22/0x22 [sd_mod]
[  512.957861]  [<c112e86a>] ? blk_peek_request+0xb6/0x182
[  512.957861]  [<f8111004>] ? scsi_request_fn+0x6c/0x44e [scsi_mod]
[  512.957861]  [<c112b14a>] ? __blk_run_queue+0x11/0x12
[  512.957861]  [<c113829b>] ? cfq_kick_queue+0x20/0x31
[  512.957861]  [<c10401dc>] ? process_one_work+0x181/0x25e
[  512.957861]  [<c113827b>] ? cfq_init_queue+0x28b/0x28b
[  512.957861]  [<c1041b68>] ? worker_thread+0xf3/0x1f4
[  512.957861]  [<c1041a75>] ? manage_workers+0x15a/0x15a
[  512.957861]  [<c104433c>] ? kthread+0x63/0x68
[  512.957861]  [<c10442d9>] ? kthread_worker_fn+0x114/0x114
[  512.957861]  [<c126517e>] ? kernel_thread_helper+0x6/0xd
[  512.957861] Code: 01 46 10 8b 14 24 8d 43 08 e8 b1 b4 21 00 59 5b 5e 5f 5d c3 90 64 a1 c4 44 3f c1 8b 80 78 01 00 00 8b 40 f8 c3 8b 80 78 01 00 00 <8b> 40 fc c3 31 c0 c3 8d 50 04 c7 00 00 00 00 00 89 50 04 89 50 
[  512.957861] EIP: [<c1044062>] kthread_data+0x6/0xa SS:ESP 0068:f644bcfc
[  512.957861] CR2: 00000000fffffffc
[  512.957861] ---[ end trace 51f1ebb81396496a ]---
[  512.957861] Fixing recursive fault but reboot is needed!

Подскажите пожалуйста в чем проблема, куда копать?

oops происходит на любой пакет, или только когда срабатывает if() и вызывается send(skb)?

Вы уверены, что после (успешного?) вызова dev_queue_xmit(newskb) нужно делать kfree_skb(newskb)?

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

oops происходит на любой пакет, или только когда срабатывает if() и вызывается send(skb)?

правило в iptables выглядит следующим образом: iptables -t raw -A PREROUTING -p tcp --syn -j TEST , то есть я матчем уже 100% опередляю положительное срабатывание if{}, и oops происходит уже после dev_queue_xmit()(в кернел логе [ 506.836187] Begin dev_queue_xmit.).

Вы уверены, что после (успешного?) вызова dev_queue_xmit(newskb) нужно делать kfree_skb(newskb)?

Не уверен, поэтому перед kfree_skb() сделал проверку if(newskb)

Что интересно, после получения пакета система перестает отзываться не сразу, а спустя несколько секунд.

SoulReaver
() автор топика

И что, Call trace всегда одинаковый или список вызовов каждый раз разный?

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

Вы уверены, что после (успешного?) вызова dev_queue_xmit(newskb) нужно делать kfree_skb(newskb)?

Не уверен, поэтому перед kfree_skb() сделал проверку if(newskb)


Если вы заглянете в исходники ядра, то увидите, что после dev_queue_xmit() память освобождать не нужно. Это раз.
Способ проверки на необходимость освобождения памяти... немного сомнительный. Память вам освободят, а вот обнулят ли указатель...

Короче, я бы для начала убрал лишнюю пару строк и посмотрел на результат. Про проверки касается и oldskb.

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

Строчку if (oldskb); я бы перенес в самое начало функции. Там она нужнее.

delete83 ★★
()

Еще один вопрос на засыпку (и спать): А разве не должно быть что-то вроде этого:

static unsigned int test_tg(struct sk_buff *skb, const struct xt_action_param *par) {
        const struct iphdr *iph = ip_hdr(skb);
        const struct tcphdr *th;
        th = (const struct tcphdr *)(skb->data + iph->ihl * 4);
        if ((tcp_flag_byte(th) & (TCPHDR_FIN | TCPHDR_RST | TCPHDR_ACK | TCPHDR_SYN)) == TCPHDR_SYN)
        {
        send(skb);

        return NF_STOLEN;
        }

return NF_DROP;
}

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

Для меня проверка if(newskb) был понятна только если alloc_skb() не дал памяти. Если dev_queue_xmit() освободит newskb, то есть сделает kfree_skb(newskb), то указатель newskb в вашей функции не станет NULL, это же локальная переменная.

if (oldskb) kfree_skb(oldskb)

это вобще вобще как-то...
test_tg() получила указатель, почитала указанные им данные, передала этот указатель функции send(), функция send() тоже почитала данные по этому указателю, а потом засомневалась — а вдруг он NULL :-)

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

Спасибо за советы, вроде больше нет oops. Действительно, dev_queue_xmit() делает kfre_skb(), убрал из кода

out:
        if (oldskb)
        kfree_skb(oldskb);
        if (newskb)
        kfree_skb(newskb);
и в test_tg() return NF_STOLEN; заменил на return NF_DROP;

const struct xt_action_param *par

на сколько я помню, xt_action_param используется для match модулей, для target xt_target_param, и вроде он не обязателен, поправьте, если я не прав, все же первый модуль)

И да, в данный момент в кернел лог сыпятся protocol 0800 is buggy, dev eth0 на каждый отправляемый фрейм, в чем может быть проблема?

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

на сколько я помню, xt_action_param используется для match модулей, для target xt_target_param, и вроде он не обязателен, поправьте, если я не прав, все же первый модуль)

А черт его знает. Больше года уж прошло, как я последний раз netfilter ковырял, а документации под рукой нет подходящей, чтоб посмотреть. Вам виднее. Я просто предположение сделал.

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

А вот static все же надо поставить. Не помню, опять же, деталей, но помню, что надо. :)

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

в данный момент в кернел лог сыпятся protocol 0800 is buggy, dev eth0

Решил проблему, ответ был в net/core/dev.c. Забыл сделать skb_reset_network_header(). Всем еще раз спасибо)

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