Использование перехвата соединений

Использование перехвата соединений

В этой главе обсуждаются возможности применения divert-cокетов, и чем они отличаются от других существующих механизмов перехвата соединений.

Divert-сокеты и другие подобные системы

Существуют другие пакеты, позволяющие производит перехват IP-пакетов. Ниже описано, чем они отличаются от divert-сокетов:

Сокеты Netlink

Сокеты Netlink могут перехватывать IP-пакеты так же, как и divert sockets - используя firewall. Для них существует специальный тип (AF_NETLINK) и с первого взгляда они ничем не отличаются от divert. Но, на самом деле, существуют два серьезных отличия:

Если честно, netlink-сокеты существуют не только для перехвата. В общих словах, механизм netlink предназначен для осуществления связи между ядром и пользователем. Существуют, например, netlink-сокеты маршрутизатора, позволяющие вам работать с подсистемой маршрутизации пакетов. Однако, с точки зрения перехвата пакетов, netlink-сокеты не настолько гибки, как divert.

Потоки firewall

В Linux существует три потока пакетов: входящий (input), исходящий (output) и проходящий (forward). Существуют также учетные потоки, но они нас не интересуют. В зависимости от происхождения пакета, он проходит через один или несколько из следующих потоков:

Переадресованный пакет проходит через все три потока в следующем порядке:

  1. Входящий

  2. Проходящий

  3. Исходящий

Иногда из-за этого возникают проблемы - вас могут интересовать только пакеты, предназначенные для этой машины, или наоборот - те, которые должны быть переадресованы. Часто бывает не совсем ясно, какой поток использовать.

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

Использование ipchains

Модифицированная версия ipchains, которую вы возьмете на указанном выше веб-сайте - это утилита, позволяющая изменять правила firewall из командной строки. Существует также способ задать эти правила из программы. В примере программы перехвата будет использоваться именно этот способ - настройка правила DIVERT аналогична настройке правила REDIRECT - указываете DIVERT в роли получателя, номер divert-порта, и у вас все готово.

Синтаксис команды ipchains для настройки правил firewall не изменился. Для использования правила DIVERT, вы должны использовать опцию -j DIVERT <port num> в роли получателя, а все остальное остается без изменений. Например команда

ipchains -A input -p ICMP -j DIVERT 1234
настроит правило divert для ICMP-пакетов - они будут передаваться из входящего потока на порт 1234.

В следующей главе мы опишем, как использовать ipchains совместно с программой перехвата пакетов.

Пример программы

Текст программы

Здесь приведен пример программы, которая читает пакеты с divert-сокета, выводит их содержимое на экран и затем пересылает дальше. В командной строке ей надо указать номер divert-порта для перехвата.

#include <stdio.h>
#include <errno.h>
#include <limits.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <signal.h>

#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <net/if.h>
#include <sys/param.h>

#include <linux/types.h>
#include <linux/icmp.h>
#include <linux/ip_fw.h>

#define IPPROTO_DIVERT 254
#define BUFSIZE 65535

char *progname;

#ifdef FIREWALL

char *fw_policy="DIVERT";
char *fw_chain="output";
struct ip_fw fw;
struct ip_fwuser ipfu;
struct ip_fwchange ipfc;
int fw_sock;

/* удаляем все существующие правила firewall */
void intHandler (int signo) {

  if (setsockopt(fw_sock, IPPROTO_IP, IP_FW_DELETE, &ipfc, sizeof(ipfc))==-1) {
    fprintf(stderr, "%s: невозможно удалить правило: %s\n", progname, strerror(errno));
    exit(2);
  }

  close(fw_sock);
  exit(0);
}

#endif

int main(int argc, char** argv) {
  int fd, rawfd, fdfw, ret, n;
  int on=1;
  struct sockaddr_in bindPort, sin;
  int sinlen;
  struct iphdr *hdr;
  unsigned char packet[BUFSIZE];
  struct in_addr addr;
  int i, direction;
  struct ip_mreq mreq;

  if (argc!=2) {
    fprintf(stderr, "Использование: %s <port number>\n", argv[0]);
    exit(1); 
  }
  progname=argv[0];

  fprintf(stderr,"%s:Создание сокета\n",argv[0]);
  /* открываем divert-сокет */
  fd=socket(AF_INET, SOCK_RAW, IPPROTO_DIVERT);

  if (fd==-1) {
    fprintf(stderr,"%s:Невозможно открыть divert-сокет\n",argv[0]);
    exit(1);
  }

  bindPort.sin_family=AF_INET;
  bindPort.sin_port=htons(atol(argv[1]));
  bindPort.sin_addr.s_addr=0;

  fprintf(stderr,"%s:Подключение сокета\n",argv[0]);
  ret=bind(fd, &bindPort, sizeof(struct sockaddr_in));

  if (ret!=0) {
    close(fd);
    fprintf(stderr, "%s: Ошибка bind(): %s",argv[0],strerror(ret));
    exit(2);
  }
#ifdef FIREWALL
  /* сначала заполняем поля правила */
  bzero(&fw, sizeof (struct ip_fw));
  fw.fw_proto=1; /* ICMP */
  fw.fw_redirpt=htons(bindPort.sin_port);
  fw.fw_spts[1]=0xffff;
  fw.fw_dpts[1]=0xffff;
  fw.fw_outputsize=0xffff;

  /* заполняем структуру fwuser */
  ipfu.ipfw=fw;
  memcpy(ipfu.label, fw_policy, strlen(fw_policy));

  /* заполняем структуру fwchange */
  ipfc.fwc_rule=ipfu;
  memcpy(ipfc.fwc_label, fw_chain, strlen(fw_chain));

  /* открываем сокет */
  if ((fw_sock=socket(AF_INET, SOCK_RAW, IPPROTO_RAW))==-1) {
    fprintf(stderr, "%s: невозможно создать raw-сокет: %s\n", argv[0], strerror(errno));
    exit(2);
  }

  /* записываем правило */
  if (setsockopt(fw_sock, IPPROTO_IP, IP_FW_APPEND, &ipfc, sizeof(ipfc))==-1) {
    fprintf(stderr, "%s невозможно установить правило firewall: %s\n", argv[0], strerror(errno));
    exit(2);
  }
 
  /* устанавливаем обработчик сигнала для удаления правила */
  signal(SIGINT, intHandler);
#endif /* FIREWALL */
  
  printf("%s: Ожидание данных...\n",argv[0]);
  /* читаем данные */
  sinlen=sizeof(struct sockaddr_in);
  while(1) {
    n=recvfrom(fd, packet, BUFSIZE, 0, &sin, &sinlen);
    hdr=(struct iphdr*)packet;
    
    printf("%s: Содержимое пакета:\n",argv[0]);
	for( i=0; i<40; i++) {
		printf("%02x ", (int)*(packet+i));
		if (!((i+1)%16)) printf("\n");
	};
    printf("\n"); 

    addr.s_addr=hdr->saddr;
    printf("%s: Адрес отправителя: %s\n",argv[0], inet_ntoa(addr));
    addr.s_addr=hdr->daddr;
    printf("%s: Адрес получателя: %s\n", argv[0], inet_ntoa(addr));
    printf("%s: IF-адрес получателя: %s\n", argv[0], inet_ntoa(sin.sin_addr));
    printf("%s: Номер протокола: %i\n", argv[0], hdr->protocol);

    /* пересылка */

#ifdef MULTICAST 
   if (IN_MULTICAST((ntohl(hdr->daddr)))) {
	printf("%s: Multicast-адрес!\n", argv[0]);
	addr.s_addr = hdr->saddr;
	errno = 0;
	if (sin.sin_addr.s_addr == 0)
	    printf("%s: set_interface вернул %i ошибку номер =%i\n", argv[0], setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &addr, sizeof(addr)), errno);
    }
#endif

#ifdef REINJECT
   printf("%s Пересылка DIVERT %i байт\n", argv[0], n);
   n=sendto(fd, packet, n ,0, &sin, sinlen);
   printf("%s: переслано %i байт.\n", argv[0], n); 

   if (n<=0) 
     printf("%s: Ошибка номер %i\n", argv[0], errno);
   if (errno == EBADRQC)
     printf("errno == EBADRQC\n");
   if (errno == ENETUNREACH)
     printf("errno == ENETUNREACH\n");
#endif
  }
}

Вы можете просто скопировать эту программу и откомпилировать ее. Если вы хотите разрешить пересылку - соберите ее с флагом -DREINJECT, в противном случае, она будет только перехватывать пакеты.

Чтобы использовать эту программу, соберите ядро и ipchains-1.3.8 (как это сделать, описано выше. Установите правило в любой из потоков firewall: входящий, исходящий или проходящий, затем пошлите пакеты, соответствующие правилу, и смотрите их содержимое, которое будет появляться на экране. После этого пакеты будут посылаться дальше, если вы использовали соответствующую опцию при компиляции.

Например, после команды:

ipchains -A output -p TCP -s 172.16.128.10 -j DIVERT 4321
interceptor 4321
будут перехвачены все TCP-пакеты, идущие от машины 172.16.128.10 (предположим, что ваша машина - это шлюз). Программа перехватит их, выдаст на экран и только затем отправит дальше.

Если вы не использовали опцию pass-through при сборке ядра, то добавление в первой строчке правила firewall приведет к тому, что пакеты, отвечающие этому правилу, будут отбрасываться, если нет программы перехвата. Подробнее читайте выше.

Если вы хотите, чтобы правило firewall задавалось вашей программой - соберите ее с опцией -DFIREWALL, и она будет перехватывать все ICMP-пакеты из входящего потока. Она также автоматически удалит правило DIVERT по окончании работы. В этом случае опция pass-through ядра не влияет на поведение программы.



Наш баннер
Вы можете установить наш баннер на своем сайте или блоге, скопировав этот код:
RSS новости