получается довольно интересная ситуация, если я использую SOCK_RAW, то система сама собирает ip пакет и вставляет нужный source адрес в заголовок ip (то есть если я посылаю tcp пакет на localhost, то source у меня 127.0.0.1, если посылаю запрос на google.ru, то source у меня это мой wlan0, у кого-то будет ethX или еще что-то), но для рассчета контрольной суммы tcp пакета нужен так называемый псевдозаголовок, а для его нормального формирования нужен source ip address, который фиг знает как получить, может кто-то подскажет? 10.12.14, Ivan Riabtsov<ivriabtsov@gmail.com> написал(а):
в общем написал небольшой пример, как послать tcp пакет, правда сейчас вместо tcp заголовка посылается мусор, обратите внимание на использование функции getaddrinfo, с ней не надо больше создавать структуру sockaddr_in и при этом она может работать как с ipv4, так и с ipv6 (структура sockaddr_in вне зависимости от протокола создаетсу уже внутри этой функции и длину структуры тоже легко получить, она тоже задается внутри функции), очень удобная штука, правда вывести на печать из нее адрес ipv6 немного сложнее, чем адрес ipv4. Создал на гитхабе папку simple_examples, туда положил пример с tcp и getaddrinfo
10.12.14, Ivan Riabtsov<ivriabtsov@gmail.com> написал(а):
я не знаю, но предполагаю, что аналогично пингу.
то есть где в пинге открываем сокет: socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)
в случае с tcp надо так: socket(AF_INET, SOCK_RAW, IPPROTO_TCP)
в этом случае система сама собирает ip пакет, а в ту часть заголовка ip заголовка, где определяется вложенный протокол, ставится номер протокола, определнный константой IPPROTO_TCP или IPPROTO_ICMP или любой другой константой из <netinet/in.h>
далее мы можем на самом деле послать любой мусор в том месте, где должен быть tcp заголовок с данными, а можем сами сделать нужный нам заголовок и отправить его вызовом: sendto(conn->send_sock, send_frame, sizeof(struct s_frame), 0, (struct sockaddr *)&conn-> send_sockaddr, sizeof(struct sockaddr_in))
в моем пинге делается пакет icvp: struct s_frame { struct icmphdr _icmphdr; char payload[LOAD_SIZ]; }; ... ... send_frame = malloc(sizeof(struct s_frame)); if (!send_frame) { perror("malloc: "); goto end; } ... ... send_frame->_icmphdr.type = ICMP_ECHO; send_frame->_icmphdr.code = ICMP_ECHOREPLY; send_frame->_icmphdr.un.echo.id = rand() % (USHRT_MAX + 1); send_frame->_icmphdr.un.echo.sequence = htons(1); ... ... send_frame->_icmphdr.checksum = 0; send_frame->_icmphdr.checksum = in_cksum((unsigned short *)send_frame, sizeof(struct s_frame)); и далее по коду идет вызов sendto определение icmphdr находится в <netinet/ip_icmp.h>
найти структуру tcp заголовка можно тут: <netinet/tcp.h> struct tcphdr надо так же инициализировать необходимый для заголовка tcp буфер памяти, посмотреть по rfc или в википедии, каким должен быть пакет syn, можно вообще в wireshark посмотреть и заполнить все необходимые поля буфера, потом отправить в сеть этот буфер и можно посмотреть опять же в wireshark, если на пакет придет какой-то ответ, тоо значит пакет собран верно, если ничего не придет, значит не верно.
с ipv6 все сложнее, я сам толком не разобрался, как там надо делать, пинг у меня так и не получилось послать.
Сейчас как-то не до того, может на выходных напишу рабочую программу, но не уверен.
10.12.14, Alex Naumov<alexander_naumov@opensuse.org> написал(а):
2014-11-24 11:43 GMT+01:00 Ivan Riabtsov <ivriabtsov@gmail.com>:
точно так же, как в случае с программой ping, надо открыть пару raw сокетов, "смастерить" tcp syn пакет, только при открытии сокета надо указывать IPPROTO_TCP и нужно привязывать сокет к определенному tcp порту, ответ распарсить так же, как ответ на пинг.
Как именно смастерить tcp syn? Был бы тебе признателен за пример.