Вступление
В этой статье мы рассмотрим пример как сформировать и послать ICMP пакет средствами языка C на системе FreeBSD 8.2 используя BSD RAW сокеты. Данный пример скомпилируется и под любой другой Unix-подобной системой, но для этого придётся внести некоторые поправки(см. комментарии к коду)
Модель OSI
ICMP — это протокол сетевого уровня, работающий поверх IP протокола.
Протокол IP содержит следующие поля:
Название Длина(бит)
type, code, id, seq, cksum. Их размеры варьируются
Программная реализация
Как я уже писал, мы будем использовать RAW сокеты, собирая наш пакет по байтам, т.к. обычные сокеты не предоставляют возможности использовать ICMP протокол. Поэтому, например, в утилите ping используются RAW сокеты заместо обычных.
Приступим
Первый терминал:
Отключаем разгрузку и запускаем ещё раз
Снова включаем
В этой статье мы рассмотрим пример как сформировать и послать ICMP пакет средствами языка C на системе FreeBSD 8.2 используя BSD RAW сокеты. Данный пример скомпилируется и под любой другой Unix-подобной системой, но для этого придётся внести некоторые поправки(см. комментарии к коду)
Модель OSI
ICMP — это протокол сетевого уровня, работающий поверх IP протокола.
Протокол IP содержит следующие поля:
Название Длина(бит)
Version 4 Header length 4 Type of Service 8 Total length 16 Identification 16 Flags 3 Offset 13 Time to Live 8 Protocol 8 Checksum 16 Source IP address 32 Destination IP address 32
DataВ поле Data будет находиться наш ICMP пакет, который в свою очередь содержит поля:
type, code, id, seq, cksum. Их размеры варьируются
Программная реализация
Как я уже писал, мы будем использовать RAW сокеты, собирая наш пакет по байтам, т.к. обычные сокеты не предоставляют возможности использовать ICMP протокол. Поэтому, например, в утилите ping используются RAW сокеты заместо обычных.
Приступим
/* Файл ICMP.c */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <netdb.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/socket.h> #include <netinet/in_systm.h> #include <netinet/in.h> #include <netinet/ip.h> #include <netinet/udp.h> #include <netinet/ip_icmp.h> #include <netinet/tcp.h> #include <arpa/inet.h> /* Функция для расчёта контрольной суммы. Суммируем значения переданных данных, разбив данные на куски по 2 байта (u_short весит 2 байта, суммирование осуществляется в while). Если остался 1 символ лишний - прибавляем его к получившемуся значению. Сдвигаем результат на 16 разрядов(2 байта) вправо, прибавляя вытесненные разряды. Инвертируем и получаем контрольную сумму */ u_short in_cksum(addr, len) u_short *addr; int len; { int nleft, sum; u_short *w; union { u_short us; u_char uc[2]; } last; u_short answer; nleft = len; sum = 0; w = addr; while (nleft > 1) { sum += *w++; nleft -= 2; } if (nleft == 1) { last.uc[0] = *(u_char *)w; last.uc[1] = 0; sum += last.us; } sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); answer = ~sum; return(answer); } int main(int argc, char **argv) { struct ip ip; struct udphdr udp; struct icmp icmp; int sd; const int on = 1; struct sockaddr_in sin; u_char *packet; /* Выделяем память для нашего пакета */ packet = (u_char *)malloc(60); /* Длина заголовков(включая опции): 32 бита(4 байта) каждый. Учитывая что мы не будем посылать никаких опций, длина IP пакета будет равна 20 байт, поэтому нам нужно (20 / 4 = 5) */ ip.ip_hl = 0x5; /* Версия протокола 4(IPv4) */ ip.ip_v = 0x4; /* Приоритет пакета */ ip.ip_tos = 0x0; /* Общая длина пакета. */ ip.ip_len = 60; /* В linux htons(60) */ /* Уникальный идентефикатор пакета, посланного хостом */ ip.ip_id = htons(12830); /* Смещение пакета. Ставим 0x0, т.к. в данном случае не предусмотрено фрагментации */ ip.ip_off = 0x0; /* Время жизни, при проходе через маршрутизатор уменьшается на единицу, если равно нулю то отбрасывается маршрутизатором */ ip.ip_ttl = 64; /* Используемый протокол */ ip.ip_p = IPPROTO_ICMP; /* Устанавливаем в 0 до того как считать контрольную сумму. Здесь контрольная сумма считается только для IP пакета, протоколы верхнего уровня имеют собственные поля для контрольной суммы, которая должна быть посчитана отдельно */ ip.ip_sum = 0x0; /* Адрес отправителя. Если вписать в следующие 2 поля свой адрес, то пакет пойдёт через интерфейс lo0 */ ip.ip_src.s_addr = inet_addr("172.12.129.30"); /* Адрес получателя */ ip.ip_dst.s_addr = inet_addr("172.12.129.30"); /* Считаем контрольную сумму IP пакета. Некоторые драйверы поддерживают автоматическую разгрузку контрольной суммы, чтобы поля контрольной суммы не заполнялись нулями, её нужно отключить (ifconfig <имя интерфейса> -rxcsum -txcsum) */ ip.ip_sum = in_cksum((unsigned short *)&ip, sizeof(ip)); /* Тело IP пакета готово, копируем его в начало нашего пакета */ memcpy(packet, &ip, sizeof(ip)); /* ICMP пакет. Тип */ icmp.icmp_type = ICMP_ECHO; /* Код 0 - request */ icmp.icmp_code = 0; /* ID - Любое число */ icmp.icmp_id = htons(1000);/*В Linux htons не требуется*/ /* Последовательный номер */ icmp.icmp_seq = 0; /* Считаем контрольную сумму */ icmp.icmp_cksum = 0; icmp.icmp_cksum = in_cksum((unsigned short *)&icmp, 8); /* Копируем ICMP в наш пакет сразу после IP */ memcpy(packet + 20, &icmp, 8); /* Пакет готов, теперь нужно подготовить сокет */ if ((sd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { perror("socket error"); exit(1); } /* Сообщаем, что заголовок уже вложен */ if (setsockopt(sd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0) { perror("setsockopt error"); exit(1); } memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = ip.ip_dst.s_addr; /* Вместо send используем sendto, т.к. соединение не было установлено */ if (sendto(sd, packet, 60, 0, (struct sockaddr *)&sin, sizeof(struct sockaddr)) < 0) { perror("sendto error"); exit(1); } return 0; }Должно получиться примерно следующее:
Первый терминал:
# gcc ICMP.c -o ICMP
ICMP.c:122:2: warning: no newline at end of file
# ~/ICMPНа втором терминале tcpdump ругается что контрольная сумма не правильная(см. ниже)
Отключаем разгрузку и запускаем ещё раз
# ifconfig lo0 -rxcsum -txcsumНе ругается
# ~/ICMP
Снова включаем
# ifconfig lo0 rxcsum txcsumВторой терминал:
# ~/ICMP
#tcpdump -i lo0 -vvv 'icmp'
tcpdump: listening on lo0, link-type NULL (BSD loopback), capture size 96 bytes
21:21:00.057656 IP (tos 0x0, ttl 64, id 12830, offset 0, flags [none], proto ICMP (1), length 60, bad cksum 0 (->9e25)!)
<IP> > <IP>: ICMP echo request, id 1000, seq 0, length 40
21:21:00.057667 IP (tos 0x0, ttl 64, id 50209, offset 0, flags [none], proto ICMP (1), length 60, bad cksum 0 (->c22)!)
<IP> > <IP>: ICMP echo reply, id 1000, seq 0, length 40
21:21:19.243796 IP (tos 0x0, ttl 64, id 12830, offset 0, flags [none], proto ICMP (1), length 60)
<IP> > <IP>: ICMP echo request, id 1000, seq 0, length 40
21:21:19.243807 IP (tos 0x0, ttl 64, id 50260, offset 0, flags [none], proto ICMP (1), length 60)
<IP> > <IP>: ICMP echo reply, id 1000, seq 0, length 40
21:21:35.837038 IP (tos 0x0, ttl 64, id 12830, offset 0, flags [none], proto ICMP (1), length 60, bad cksum 0 (->9e25)!)
<IP> > <IP>: ICMP echo request, id 1000, seq 0, length 40
21:21:35.837049 IP (tos 0x0, ttl 64, id 50265, offset 0, flags [none], proto ICMP (1), length 60, bad cksum 0 (->bea)!)
<IP> > <IP>: ICMP echo reply, id 1000, seq 0, length 40
Комментариев нет:
Отправить комментарий