Назад | Перейти на главную страницу

Использование u32 вместе с заголовками расширений (как их перепрыгнуть?)

Я пытаюсь отфильтровать некоторые части полезной нагрузки для пакета IPv6 с участием заголовки расширений (например, Параметры назначения).

ip6tables отлично работает с такими условиями, как --proto udp или --dport 109, даже если у пакета есть расширенные заголовки. Netfilter четко знает, как перескочить через Параметры назначения, чтобы найти заголовок UDP.

Теперь я хотел бы использовать модуль u32 для сопоставления байта полезной нагрузки (скажем, «Я хочу, чтобы третий байт полезной нагрузки был 42). Если пакет не имеет заголовков расширения, что-то вроде --u32 "48&0x0000ff00=0x2800"̀ (48 = 40 байтов для заголовка IPv6 + 8 для заголовка UDP) работает нормально. Если пакет имеет параметры назначения, он больше не соответствует. Я хотел бы написать правило, которое будет работать независимо от того, есть у пакета параметры назначения или нет.

Я не нахожу способа указать Netfilter на синтаксический анализ, пока не появится заголовок UDP (то, что он может сделать, иначе --dport 109 не получится) потом оставить u32 разобрать остальное.

Я ищу просто В противном случае, как упоминает BatchyX, я мог бы написать модуль ядра, делающий то, что я хочу.

Я столкнулся с той же проблемой и пропатчил u32-модуль. У меня есть два патча для ядра 2.6.30, один для ядра и один для пользователя. Они, вероятно, не будут обрабатывать фрагментированные полезные данные лучше, чем исходный сопоставитель u32, но он сработал для моей проблемы.


Патч ядра:

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");

Участок земли пользователя:

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);
 }

Ответ сосредоточен на решении проблемы в случае пакета IPv4 и не решает проблему (нескольких) заголовков расширений, которые необходимо учитывать до того, как будет вычислено правильное смещение, чтобы добраться до реальной части данных.

Следующее правило состоит из 2 частей.

  • проверь, если UDP 6&0xFF=17
  • проверьте, установлен ли 3-й байт данных на 42 (шестнадцатеричный 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

Проверить 3-й байт сложно, так как вам нужно динамически позиционировать его в заголовке UDP. Заголовок UDP начинается после заголовка IP;

Проверь это модель заголовка чтобы лучше понять, что где.

Как вы можете видеть, 20 байтов - это минимальная длина IP-заголовка, но тогда, если есть варианты каких-либо данных, она может быть больше 20 байтов, поэтому вам нужно динамически позиционировать себя в начале заголовка UDP.

Считывание позиции из первых 4 байтов (32 бита / u32)

цитата из:

Чтобы получить длину заголовка, нам нужен первый байт: «0 >> 24», но нам нужно получить только нижний полубайт, и нам нужно умножить это число на 4, чтобы получить фактическое количество байтов в заголовке. Чтобы выполнить умножение, мы сдвинем вправо на 22 вместо 24. Для этого сдвига нам нужно будет использовать маску 0x3C вместо 0x0F, которую мы использовали бы. Выражение на данный момент: «0 >> 22 & 0x3C».

Теперь, когда у нас есть смещение, где начинается заголовок UDP (0 >> 22 & 0x3C), мы можем просто позволить себе прочитать байты 7,8,9,10 (где байты данных 8,9,10) и обрезать с помощью маски 0xFF, последний из 4 (3-й байт данных). «7 & 0xFF»