Я пытаюсь отфильтровать некоторые части полезной нагрузки для пакета 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);
}
Следующее правило состоит из 2 частей.
6&0xFF=17
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»