Usando o u32 junto com cabeçalhos de extensão (como pular sobre eles?)

1

Estou tentando filtrar algumas partes da carga, para um pacote IPv6 com cabeçalhos de extensão (por exemplo, Opções de destino).

ip6tables funciona bem com condições como --proto udp ou --dport 109 , mesmo quando o pacote tem cabeçalhos de extensão. O Netfilter sabe claramente como saltar sobre as Opções de Destino para encontre o cabeçalho UDP.

Agora, gostaria de usar o módulo u32 para corresponder a um byte no payload (diga "Eu quero que o terceiro byte da carga seja 42). Se o pacote não tiver cabeçalhos de extensão, algo como --u32 "48&0x0000ff00=0x2800"̀ (48 = 40 bytes para o cabeçalho IPv6 + 8 para o cabeçalho UDP) funciona bem, pacote tem uma opção de destino, não correspondências mais longas. Eu gostaria de escrever uma regra que funcionará se o pacote tem opções de destino ou não.

Eu não encontro uma maneira de dizer ao Netfilter para analisar até o cabeçalho UDP (algo que é capaz de fazer, senão --dport 109 não funcionaria) então deixar o u32 analisar o resto.

Eu estou procurando uma maneira simples , caso contrário, como BatchyX menciona, eu poderia escrever um módulo do kernel fazendo o que eu quero.

    
por bortzmeyer 07.10.2012 / 15:54

2 respostas

1

A resposta está se concentrando em resolver o problema no caso do pacote IPv4 e não resolve o problema de cabeçalhos de extensão (múltiplos) que precisam ser contabilizados antes que o deslocamento adequado seja calculado para chegar à parte de dados real.

A regra a seguir é composta por 2 partes

  • verifique se o udp 6&0xFF=17
  • verifique se o terceiro byte de dados está definido como 42 (hex 0x2A) 0>>22&0x3C@7&0xFF=0x2A

iptables -I INPUT -m u32 --u32 "6&0xFF=17&&0>>22&0x3C@7&0xFF=0x2A" -m comment --comment "Match udp packet with 3rd data byte set to 42" -j LOG

Verificar o byte 3 é complicado, pois é necessário posicionar dinamicamente no cabeçalho UDP. Cabeçalho UDP é iniciado após o cabeçalho IP;

verifique este modelo de cabeçalho para ter uma ideia melhor do que é o local.

Como você pode ver, 20 bytes é o tamanho mínimo do cabeçalho IP, mas se houver opções de dados, ele pode ter mais de 20 bytes, então você precisa posicionar-se dinamicamente para iniciar o cabeçalho UDP.

Possibilidade de leitura dos primeiros 4 bytes (32 bits / u32)

cite de :

Para obter o comprimento do cabeçalho, precisamos do primeiro byte: "0 > > 24", mas precisamos apenas pegar o nibble inferior e precisamos multiplicar esse número por 4 para obter o número real de bytes no cabeçalho. Para fazer a multiplicação, nós iremos deslocar 22 em vez de 24. Com essa mudança, precisaremos usar uma máscara de 0x3C ao invés do 0x0F que teríamos usado. A expressão até agora é: "0 > > 22 & 0x3C".

Agora que temos o deslocamento onde o cabeçalho UDP é iniciado (0 > > 22 & 0x3C), podemos apenas ter nossos eus para ler os bytes 7,8,9,10 (onde os bytes de dados são 8,9,10) e cortar off com máscara 0xFF último de 4 (terceiro byte de dados). "7 & 0xFF"

    
por 07.10.2012 / 16:40
1

Eu encontrei o mesmo problema e atualizei o módulo u32. Eu tenho dois patches para o kernel 2.6.30, um para o kernel e outro para o usuário. Eles provavelmente não lidarão com cargas úteis fragmentadas melhor do que o correspondente do u32 original, mas funcionou para o meu problema.

Patch de kernel:

diff -ruNd linux-2.6.30.4patch/net/netfilter/xt_u32.c linux-2.6.30/net/netfilter/xt_u32.c
--- linux-2.6.30.4patch/net/netfilter/xt_u32.c  2014-03-19 13:24:06.000000000 +0100
+++ linux-2.6.30/net/netfilter/xt_u32.c 2014-10-02 13:12:33.244444192 +0200
@@ -13,9 +13,13 @@
 #include <linux/types.h>
 #include <linux/netfilter/x_tables.h>
 #include <linux/netfilter/xt_u32.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+

 static bool u32_match_it(const struct xt_u32 *data,
-            const struct sk_buff *skb)
+            const struct sk_buff *skb,
+            unsigned int PayloadOffset)
 {
    const struct xt_u32_test *ct;
    unsigned int testind;
@@ -34,7 +38,7 @@
    for (testind = 0; testind < data->ntests; ++testind) {
        ct  = &data->tests[testind];
        at  = 0;
-       pos = ct->location[0].number;
+       pos = PayloadOffset + ct->location[0].number;

        if (skb->len < 4 || pos > skb->len - 4)
            return false;
@@ -92,27 +96,91 @@
    const struct xt_u32 *data = par->matchinfo;
    bool ret;

-   ret = u32_match_it(data, skb);
+   ret = u32_match_it(data, skb, 0);
    return ret ^ data->invert;
 }

-static struct xt_match xt_u32_mt_reg __read_mostly = {
-   .name       = "u32",
-   .revision   = 0,
-   .family     = NFPROTO_UNSPEC,
-   .match      = u32_mt,
-   .matchsize  = sizeof(struct xt_u32),
-   .me         = THIS_MODULE,
+static bool u32_tcp_mt(const struct sk_buff *skb, const struct xt_match_param *par)
+{
+   const struct xt_u32 *data = par->matchinfo;
+   const struct tcphdr *th;
+   struct tcphdr _tcph;
+   bool ret;
+
+   th = skb_header_pointer(skb, par->thoff, sizeof(_tcph), &_tcph);
+   if (th == NULL)
+       return false;
+
+   if (th->doff*4 < sizeof(*th))
+       return false;
+
+   /* printk("TCP payload match @%d\n", par->thoff + 4*th->doff); */
+   ret = u32_match_it(data, skb, par->thoff + 4*th->doff);
+   return ret ^ data->invert;
+}
+
+static bool u32_udp_mt(const struct sk_buff *skb, const struct xt_match_param *par)
+{
+   const struct xt_u32 *data = par->matchinfo;
+   bool ret;
+
+   /* printk("UDP payload match @%d\n", par->thoff + sizeof(struct udphdr) ); */
+   ret = u32_match_it(data, skb, par->thoff + sizeof(struct udphdr) );
+   return ret ^ data->invert;
+}
+
+static struct xt_match xt_u32_mt_reg[] __read_mostly = {
+   {
+       .name           = "u32",
+       .revision       = 0,
+       .family         = NFPROTO_UNSPEC,
+       .match          = u32_mt,
+       .matchsize      = sizeof(struct xt_u32),
+       .me             = THIS_MODULE,
+   },
+        {
+                .name           = "payload",
+                .family         = NFPROTO_IPV4,
+                .match          = u32_tcp_mt,
+                .matchsize      = sizeof(struct xt_u32),
+                .proto          = IPPROTO_TCP,
+                .me             = THIS_MODULE,
+        },
+        {
+                .name           = "payload",
+                .family         = NFPROTO_IPV6,
+                .match          = u32_tcp_mt,
+                .matchsize      = sizeof(struct xt_u32),
+                .proto          = IPPROTO_TCP,
+                .me             = THIS_MODULE,
+        },
+        {
+                .name           = "payload",
+                .family         = NFPROTO_IPV4,
+                .match          = u32_udp_mt,
+                .matchsize      = sizeof(struct xt_u32),
+                .proto          = IPPROTO_UDP,
+                .me             = THIS_MODULE,
+        },
+        {
+                .name           = "payload",
+                .family         = NFPROTO_IPV6,
+                .match          = u32_udp_mt,
+                .matchsize      = sizeof(struct xt_u32),
+                .proto          = IPPROTO_UDP,
+                .me             = THIS_MODULE,
+        },
+
 };

 static int __init u32_mt_init(void)
 {
-   return xt_register_match(&xt_u32_mt_reg);
+   return xt_register_matches(xt_u32_mt_reg, ARRAY_SIZE(xt_u32_mt_reg));
 }

 static void __exit u32_mt_exit(void)
 {
-   xt_unregister_match(&xt_u32_mt_reg);
+   xt_unregister_matches(xt_u32_mt_reg, ARRAY_SIZE(xt_u32_mt_reg));
 }

 module_init(u32_mt_init);
@@ -122,3 +190,5 @@
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("ipt_u32");
 MODULE_ALIAS("ip6t_u32");
+MODULE_ALIAS("ipt_payload");
+MODULE_ALIAS("ip6t_payload");

Patch de terreno do usuário:

diff -ruNd iptables-1.4.12.1.4patch/extensions/GNUmakefile.in iptables-1.4.12.1/extensions/GNUmakefile.in
--- iptables-1.4.12.1.4patch/extensions/GNUmakefile.in  2014-10-02 14:43:19.000000000 +0200
+++ iptables-1.4.12.1/extensions/GNUmakefile.in 2014-10-02 14:29:54.000000000 +0200
@@ -73,6 +73,7 @@
 install: ${targets_install}
    @mkdir -p "${DESTDIR}${xtlibdir}";
    if test -n "${targets_install}"; then install -pm0755 $^ "${DESTDIR}${xtlibdir}/"; fi;
+   test -f "${DESTDIR}${xtlibdir}/libxt_u32.so" && ln -s "${DESTDIR}${xtlibdir}/libxt_u32.so" "${DESTDIR}${xtlibdir}/libxt_payload.so"

 clean:
    rm -f *.o *.oo *.so *.a {matches,targets}[46].man initext.c initext4.c initext6.c;
diff -ruNd iptables-1.4.12.1.4patch/extensions/libxt_u32.c iptables-1.4.12.1/extensions/libxt_u32.c
--- iptables-1.4.12.1.4patch/extensions/libxt_u32.c 2014-10-02 14:43:19.000000000 +0200
+++ iptables-1.4.12.1/extensions/libxt_u32.c    2014-10-02 13:57:24.000000000 +0200
@@ -31,7 +31,7 @@
 static void u32_help(void)
 {
    printf(
-       "u32 match options:\n"
+       "u32/payload match options:\n"
        "[!] --u32 tests\n"
        "\t\t""tests := location \"=\" value | tests \"&&\" location \"=\" value\n"
        "\t\t""value := range | value \",\" range\n"
@@ -249,7 +249,7 @@
                       int numeric)
 {
    const struct xt_u32 *data = (const void *)match->data;
-   printf(" u32");
+   printf(" %s", match->u.user.name);
    if (data->invert)
        printf(" !");
    u32_dump(data);
@@ -277,7 +277,21 @@
    .x6_options    = u32_opts,
 };

+static struct xtables_match payload_match = {
+   .name          = "payload",
+   .family        = NFPROTO_UNSPEC,
+   .version       = XTABLES_VERSION,
+   .size          = XT_ALIGN(sizeof(struct xt_u32)),
+   .userspacesize = XT_ALIGN(sizeof(struct xt_u32)),
+   .help          = u32_help,
+   .print         = u32_print,
+   .save          = u32_save,
+   .x6_parse      = u32_parse,
+   .x6_options    = u32_opts,
+};
+
 void _init(void)
 {
    xtables_register_match(&u32_match);
+   xtables_register_match(&payload_match);
 }
    
por 07.10.2014 / 16:17