The OpenNET Project / Index page

[ новости /+++ | форум | теги | ]

Создание модуля для iptables, изменяющего ID пакета
Данная статья основывается на материале "Разработка Match-модуля для iptables
своими руками" (http://www.linuxjournal.com/article/7184), но код работает на
ядрах 2.6.20+.

Мне потребовалось изменить ID IP пакетов, в интернете подходящей инструкции как
это сделать на ядре 2.6.24 я не нашел, из-за этого решил написать, как удалось
решить задачу.

Для реализации задуманного нам понадобится написать модуль ядра, который будет
выполнять проверку и модуль расширения для iptables, который будет работать с
модулем ядра - создавать новые цепочки,
использующие наш модуль, выводить информацию о критерии при выводе списка
правил на экран, а также проверять корректность передаваемых модулю параметров.

Сначала создадим общий заголовочный файл ipt_ID.h:

   #ifndef _IPT_ID_H
   #define _IPT_ID_H

   enum {
       IPT_ID_RAND = 0,
       IPT_ID_INC
   };

   #define IPT_ID_MAXMODE  IPT_ID_INC

   struct ipt_ID_info {
       u_int8_t        mode;
       u_int16_t       id;
   };

   #endif

Теперь скопируем его в исходники netfilter в директорию
linux/netfilter_ipv4/

Далее рассмотрим модуль ядра ipt_ID.с.

   #include <linux/module.h>
   #include <linux/skbuff.h>
   #include <linux/ip.h>
   #include <net/checksum.h>
   #include <linux/random.h>
   #include <linux/netfilter/x_tables.h>
   #include <linux/netfilter_ipv4/ipt_ID.h>

   MODULE_AUTHOR("Xlise <demonxlise@gmail.com>");
   MODULE_DESCRIPTION("Xtables: IPv4 ID field modification target");
   MODULE_LICENSE("GPL");
   static int count=0;
   static struct iphdr l_iph[5];
   static unsigned int
   id_tg(struct sk_buff *skb, const struct net_device *in,
        const struct net_device *out, unsigned int hooknum,
        const struct xt_target *target, const void *targinfo)
   {
         struct iphdr *iph;
         const struct ipt_ID_info *info = targinfo;
         u_int16_t new_id;
         int i=0;
         if (!skb_make_writable(skb, skb->len))
                 return NF_DROP;

         iph = ip_hdr(skb);

         switch (info->mode) {
                 case IPT_ID_RAND:
                         get_random_bytes(&info->id, sizeof(info->id));
                         new_id = info->id;
                         break;
                 case IPT_ID_INC:


   while (i<5)
   {
   if (l_iph[i].daddr == iph->daddr)
       {
       new_id = l_iph[i].id + htons(1);
       l_iph[i].id = new_id;
       }

   else
       {new_id = iph->id;
       l_iph[count] = *iph;
       count++;
       if (count > 4)
          count = 0;
       }
   i++;
   }
                 default:
                         new_id = iph->id;
                         break;
         }

         if (new_id != iph->id) {
                 csum_replace2(&iph->check, iph->id,
                                           new_id);
                 iph->id = new_id;
         }

         return XT_CONTINUE;
   }

   static bool
   id_tg_check(const char *tablename, const void *e,
              const struct xt_target *target, void *targinfo,
              unsigned int hook_mask)
   {
         const struct ipt_ID_info *info = targinfo;

         if (info->mode > IPT_ID_MAXMODE) {
                 printk(KERN_WARNING "ipt_ID: invalid or unknown Mode %u\n",
                         info->mode);
                 return false;
         }
         if (info->mode != IPT_ID_SET && info->id == 0)
                 return false;
         return true;
   }

   static struct xt_target id_tg_reg __read_mostly = {
         .name           = "ID",
         .family         = AF_INET,
         .target         = id_tg,
         .targetsize     = sizeof(struct ipt_ID_info),
         .table          = "mangle",
         .checkentry     = id_tg_check,
         .me             = THIS_MODULE,
   };

   static int __init id_tg_init(void)
   {
         return xt_register_target(&id_tg_reg);
   }

   static void __exit id_tg_exit(void)
   {
         xt_unregister_target(&id_tg_reg);
   }

   module_init(id_tg_init);
   module_exit(id_tg_exit);

Напишем Makefile для нашего модуля:

   obj-m := ipt_ID.o
   KDIR  := /lib/modules/$(shell uname -r)/build
   PWD   := $(shell pwd)
   $(MAKE) -C $(KDIR) M=$(PWD) modules

добавим модуль в ядро

   insmod ipt_ID.ko

Для создания модуля для iptables нам потребуются исходники для iptables-1.4.4

Создадим файл libipt_ID.c

   #include <stdio.h>
   #include <string.h>
   #include <stdlib.h>
   #include <getopt.h>
   #include <xtables.h>
   #include <linux/netfilter_ipv4/ipt_ID.h>

   #define IPT_ID_USED     1

   static void ID_help(void)
   {
       printf(
   "ID target options\n"
   "  --id-rand value              Set ID to \n"
   "  --id-inc value               Increment ID by \n");
   }

   static int ID_parse(int c, char **argv, int invert, unsigned int *flags,
                    const void *entry, struct xt_entry_target **target)
   {
       struct ipt_ID_info *info = (struct ipt_ID_info *) (*target)->data;
       u_int16_t value;

       if (*flags & IPT_ID_USED) {
               xtables_error(PARAMETER_PROBLEM,
                               "Can't specify ID option twice");
       }

       if (!optarg)
               xtables_error(PARAMETER_PROBLEM,
                               "ID: You must specify a value");

       if (xtables_check_inverse(optarg, &invert, NULL, 0))
               xtables_error(PARAMETER_PROBLEM,
                               "ID: unexpected `!'");

       if (!xtables_strtoui(optarg, NULL, &value, 0, UINT16_MAX))
               xtables_error(PARAMETER_PROBLEM,
                          "ID: Expected value between 0 and 255");

       switch (c) {

               case '1':
                       info->mode = IPT_ID_RAND;
                       break;

               case '2':
                       if (value == 0) {
                               xtables_error(PARAMETER_PROBLEM,
                                       "ID: increasing by 0?");
                       }

                       info->mode = IPT_ID_INC;
                       break;

               default:
                       return 0;

       }

       info->id = value;
       *flags |= IPT_ID_USED;

       return 1;
   }

   static void ID_check(unsigned int flags)
   {
       if (!(flags & IPT_ID_USED))
               xtables_error(PARAMETER_PROBLEM,
                               "TTL: You must specify an action");
   }

   static void ID_save(const void *ip, const struct xt_entry_target *target)
   {
       const struct ipt_ID_info *info =
               (struct ipt_ID_info *) target->data;

       switch (info->mode) {
               case IPT_ID_SET:
                       printf("--id-set ");
                       break;
               case IPT_ID_DEC:
                       printf("--id-dec ");
                       break;

               case IPT_ID_INC:
                       printf("--id-inc ");
                       break;
       }
       printf("%u ", info->id);
   }

   static void ID_print(const void *ip, const struct xt_entry_target *target,
                     int numeric)
   {
       const struct ipt_ID_info *info =
               (struct ipt_ID_info *) target->data;

       printf("ID ");
       switch (info->mode) {
               case IPT_ID_SET:
                       printf("set to ");
                       break;
               case IPT_ID_DEC:
                       printf("decrement by ");
                       break;
               case IPT_ID_INC:
                       printf("increment by ");
                       break;
       }
       printf("%u ", info->id);
   }

   static const struct option ID_opts[] = {
       { "id-set", 1, NULL, '1' },
       { "id-inc", 1, NULL, '2' },
       { .name = NULL }
   };

   static struct xtables_target id_tg_reg = {
       .name           = "ID",
       .version        = XTABLES_VERSION,
       .family         = NFPROTO_IPV4,
       .size           = XT_ALIGN(sizeof(struct ipt_ID_info)),
       .userspacesize  = XT_ALIGN(sizeof(struct ipt_ID_info)),
       .help           = ID_help,
       .parse          = ID_parse,
       .final_check    = ID_check,
       .print          = ID_print,
       .save           = ID_save,
       .extra_opts     = ID_opts,
   };

   void _init(void)
   {
       xtables_register_target(&id_tg_reg);
   }

Далее скопируем файл ipt_ID.h в iptables-1.4.4/include/linux/netfilter_ipv4/ и
файл libipt_ID.c в iptables-1.4.4/extensions/
теперь скомпилируем iptables и скопируем файл
iptables-1.4.4/extensions/libipt_ID.so в /lib/xtables/

Теперь можно создавать цепочки в iptables

пример:

   iptables -t mangle -A POSTROUTING -j ID --id-rand 1

Будет выдавть всем пакетам случайные ID (единица в конце ничего не обозначает
просто я не доделал модуль)

   iptables -t mangle -A POSTROUTING -j ID --id-inc 1

Будет пакетам направленным на один IP присваивать ID постоянно увеличивая на
единицу, может хранить в памяти пять таких цепочек (количество цепочек можно увеличить)

P.S. Если кого интересует данная тема, то я доделаю статью и допишу модуль,
просто пока всё работает и так не хочется ничего переделывать, но если нужно сделаю.
 
01.05.2011 , Автор: xlise , Источник: http://www.linuxjournal.com/article...
Ключи: iptables, match, module, linux, kernel
Раздел:    Корень / Программисту и web-разработчику / C/C++, сборка, отладка

Обсуждение [ RSS ]
  • 1.1, Hubbitus (ok), 13:24, 02/05/2011 [ответить]  
  • +/
    А для чего это вообще нужно? Ну в смысле менять ID пакета?
     
  • 1.2, segoon (ok), 15:15, 02/05/2011 [ответить]  
  • +/
    Гораздо полезнее было бы сделать U32 target для изменения произвольного поля пакета, а --id описать как синоним опр. смещения.
     
  • 1.3, Новичок (??), 00:34, 03/05/2011 [ответить]  
  • +/
    а зачем это?
     
  • 1.4, PavelR (??), 06:50, 03/05/2011 [ответить]  
  • +/
    могу подкинуть идею полезного и вроде как реально нужного патча / модуля:

    Итак, ситуация - вебсервер, (д)дос.
    Прилетает запрос к веб-серверу. Хотим делать фильтрацию iptables-ом по содержимому запроса. Чтобы получить запрос, соединение должно быть установлено.
    В тот момент, когда срабатывает правило

    -A INPUT -p tcp --dport 80 -m string -string .... -j REJECT --reject-with tcp-reset

    tcp-reset отправляется только в сторону, откуда пришел запрос, в итоге имеем подвешенное соединение с нашей стороны.

    Ну вот соответственно требуется модификация имеющегося или новый модуль :-)

     
     
  • 2.5, pavlinux (ok), 22:47, 03/05/2011 [^] [^^] [^^^] [ответить]  
  • +/
    > Итак, ситуация - вебсервер, (д)дос.
    > Прилетает запрос к веб-серверу. Хотим делать фильтрацию iptables-ом по содержимому запроса.
    > Чтобы получить запрос, соединение должно быть установлено.
    > В тот момент, когда срабатывает правило
    > -A INPUT -p tcp --dport 80 -m string -string .... -j REJECT
    > --reject-with tcp-reset

    Ну и будет там запрос  "GET / HTTP/1.1" и чё дальше?
    Как это спасёт от DoS/DDoS ?

    >  tcp-reset отправляется только в сторону, откуда пришел запрос, в итоге имеем
    > подвешенное соединение с нашей стороны.

    -j REJECT --reject-with tcp-reset  сбрасывает соединение, а не вешает.

     
     
  • 3.6, PavelR (??), 10:19, 04/05/2011 [^] [^^] [^^^] [ответить]  
  • +/
    >> Итак, ситуация - вебсервер, (д)дос.
    >> Прилетает запрос к веб-серверу. Хотим делать фильтрацию iptables-ом по содержимому запроса.
    >> Чтобы получить запрос, соединение должно быть установлено.
    >> В тот момент, когда срабатывает правило
    >> -A INPUT -p tcp --dport 80 -m string -string .... -j REJECT
    >> --reject-with tcp-reset
    > Ну и будет там запрос  "GET / HTTP/1.1" и чё дальше?
    > Как это спасёт от DoS/DDoS ?

    Допустим что там будет нечто специфичное и мы сможем выделить "нужные" :-)

    >>  tcp-reset отправляется только в сторону, откуда пришел запрос, в итоге имеем
    >> подвешенное соединение с нашей стороны.
    > -j REJECT --reject-with tcp-reset  сбрасывает соединение, а не вешает.

    :-) Ты уже посмотрел в код? Типовой use-case это посылка сброса на еще не установленном соединении (флаг определяет что полетит _не_ icmp пакет).
    В рассматриваемом случае использование несколько иное -  соединение будет уже установлено. И далее:


    >>  tcp-reset отправляется только в сторону, откуда пришел запрос, в итоге имеем
    >> подвешенное соединение с нашей стороны.

    так что соединение с нашей стороны "будет не проинформировано" и будет тихо мирно ждать таймаута.

    см ipt_REJECT.c / send_reset()

     
     
  • 4.7, pavlinux (ok), 14:07, 04/05/2011 [^] [^^] [^^^] [ответить]  
  • +/
    >> Ну и будет там запрос  "GET / HTTP/1.1" и чё дальше?
    >> Как это спасёт от DoS/DDoS ?
    > Допустим что там будет нечто специфичное и мы сможем выделить "нужные" :-)

    На месте DDoSеров я бы не повторялся,  

    "GET /1 HTTP/1.1"
    "GET /11 HTTP/1.1"
    "GET /111 HTTP/1.1"
    "GET /1111 HTTP/1.1"
    ...
    "GET /FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF HTTP/1.1"

    :)

    ---
    Можно с другой стороны конечно...

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




    -m string ! -string "GET /           HTTP/1.1" -j REJECT --reject-with tcp-reset
    -m string ! -string "GET /index.html HTTP/1.1" -j REJECT --reject-with tcp-reset
    -m string ! -string "GET /index.php  HTTP/1.1" -j REJECT --reject-with tcp-reset
    -m string ! -string "GET /main.php   HTTP/1.1" -j REJECT --reject-with tcp-reset
    -m string ! -string "GET /about.php  HTTP/1.1" -j REJECT --reject-with tcp-reset
    ...
    ...
    ...
    -m string ! -string "GET https://www.opennet.ru/cgi-bin/openforum/vsluhboard.cgi?quote=not_empty&az=pos HTTP/1.1" -j REJECT --reject-with tcp-reset



     
     
  • 5.8, PavelR (??), 19:10, 05/05/2011 [^] [^^] [^^^] [ответить]  
  • +/
    ну так как-бы согласен с тем, что соединение подвиснет ?
     
     
  • 6.9, pavlinux (ok), 23:17, 05/05/2011 [^] [^^] [^^^] [ответить]  
  • +/
    > ну так как-бы согласен с тем, что соединение подвиснет ?

    Если честно, лень смотреть, пущай будет сбрасываться :)

     

     Добавить комментарий
    Имя:
    E-Mail:
    Заголовок:
    Текст:




    Партнёры:
    PostgresPro
    Inferno Solutions
    Hosting by Hoster.ru
    Хостинг:

    Закладки на сайте
    Проследить за страницей
    Created 1996-2024 by Maxim Chirkov
    Добавить, Поддержать, Вебмастеру