[Network] TCP: SYN, SYN/ACK, ACK
Всем привет. Как известно, этап установления TCP-соединения, реализуемый как "*трехшаговое рукопожатие*" (three-way handshake), представляет из себя обмен фреймами с SYN, SYN/ACK и ACK-флагами. Знает кто-нибудь как изменить стандартное поведение клиента, и отправить только первый запрос т.е. фрейм с SYN-флагом, и после подтверждения от сервера, т.е. получения фрейма с SYN/ACK флагом, не отправлять ACK? Дурими словами - не устанавливать нормальное соединение, но отправить запрос и получить на него ответ? Alex
Привет, Сказать ядру не слать последний ACK, скорее всего, нельзя, потому что это будет нарушением протокола. А вот выкинуть этот самый ACK на уровне выше можно попытаться с помощью iptables. Например, можно выбрасывать исходящие tcp пакеты на конкретный ip/порт, в которых не установлен флаг SYN. Или выбрасывать второй по счету пакет. Думаю, способов можно придумать множество. В Mon, 24 Nov 2014 08:01:58 +0100 Alex Naumov <alexander_naumov@opensuse.org> пишет:
Всем привет.
Как известно, этап установления TCP-соединения, реализуемый как "*трехшаговое рукопожатие*" (three-way handshake), представляет из себя обмен фреймами с SYN, SYN/ACK и ACK-флагами. Знает кто-нибудь как изменить стандартное поведение клиента, и отправить только первый запрос т.е. фрейм с SYN-флагом, и после подтверждения от сервера, т.е. получения фрейма с SYN/ACK флагом, не отправлять ACK? Дурими словами - не устанавливать нормальное соединение, но отправить запрос и получить на него ответ?
Alex
2014-11-24 9:23 GMT+01:00 Dmitry Podgorny <pasis.ua@gmail.com>:
Привет,
Привет, Дмитрий.
Сказать ядру не слать последний ACK, скорее всего, нельзя, потому что это будет нарушением протокола. А вот выкинуть этот самый ACK на уровне выше можно попытаться с помощью iptables.
Например, можно выбрасывать исходящие tcp пакеты на конкретный ip/порт, в которых не установлен флаг SYN. Или выбрасывать второй по счету пакет. Думаю, способов можно придумать множество.
Меня итресесует возможность запрограммировать подобный запрос. Возможно какой-то параметр для connect() или изменения в какой-то из структур (надо будет посмотреть повнимательнее). Наверное будет лучше, если я покажу конкретный пример запроса, который я делаю. Дальше уже будет легче сказать, что именно можно изменить в коде. Код выложу сегодня-завтра.
On 11/24/14, Alex Naumov <alexander_naumov@opensuse.org> wrote:
Всем привет.
Как известно, этап установления TCP-соединения, реализуемый как "*трехшаговое рукопожатие*" (three-way handshake), представляет из себя обмен фреймами с SYN, SYN/ACK и ACK-флагами. Знает кто-нибудь как изменить стандартное поведение клиента, и отправить только первый запрос т.е. фрейм с SYN-флагом, и после подтверждения от сервера, т.е. получения фрейма с SYN/ACK флагом, не отправлять ACK? Дурими словами - не устанавливать нормальное соединение, но отправить запрос и получить на него ответ?
Легко! :) Ест ьтакая штука Scapy: http://resources.infosecinstitute.com/scapy-all-in-one-networking-tool/
Alex
-- Regards, Denis
точно так же, как в случае с программой ping, надо открыть пару raw сокетов, "смастерить" tcp syn пакет, только при открытии сокета надо указывать IPPROTO_TCP и нужно привязывать сокет к определенному tcp порту, ответ распарсить так же, как ответ на пинг. 24.11.14, Alex Naumov<alexander_naumov@opensuse.org> написал(а):
Всем привет.
Как известно, этап установления TCP-соединения, реализуемый как "*трехшаговое рукопожатие*" (three-way handshake), представляет из себя обмен фреймами с SYN, SYN/ACK и ACK-флагами. Знает кто-нибудь как изменить стандартное поведение клиента, и отправить только первый запрос т.е. фрейм с SYN-флагом, и после подтверждения от сервера, т.е. получения фрейма с SYN/ACK флагом, не отправлять ACK? Дурими словами - не устанавливать нормальное соединение, но отправить запрос и получить на него ответ?
Alex
2014-11-24 11:43 GMT+01:00 Ivan Riabtsov <ivriabtsov@gmail.com>:
точно так же, как в случае с программой ping, надо открыть пару raw сокетов, "смастерить" tcp syn пакет, только при открытии сокета надо указывать IPPROTO_TCP и нужно привязывать сокет к определенному tcp порту, ответ распарсить так же, как ответ на пинг.
Звучит интересно конечно, но я ни разу пока не "мастерил" свои пакеты. Попробую на днях. Кстати, было бы здорово, если бы ты закомитил свой ping. Пока же мой TCP-запрос (трехшаговый, на 12489 порт) можно найти тут: https://github.com/ru-kernelnewbies-org/Network/blob/master/sockets/TCP/nscl...
пинг закоммитил, там в программе есть одна особенность, если пинговать localhost или любой адрес, где src и dest одинаковые, то почему-то не сходится sequence в принятом icmp пакете, если пинговать любой удаленный хост, такой проблему нет, кто-нибудь может пояснить, почему так происходит, я что-то не понял 24.11.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 порту, ответ распарсить так же, как ответ на пинг.
Звучит интересно конечно, но я ни разу пока не "мастерил" свои пакеты. Попробую на днях. Кстати, было бы здорово, если бы ты закомитил свой ping.
Пока же мой TCP-запрос (трехшаговый, на 12489 порт) можно найти тут: https://github.com/ru-kernelnewbies-org/Network/blob/master/sockets/TCP/nscl...
2014-11-24 11:43 GMT+01:00 Ivan Riabtsov <ivriabtsov@gmail.com>:
точно так же, как в случае с программой ping, надо открыть пару raw сокетов, "смастерить" tcp syn пакет, только при открытии сокета надо указывать IPPROTO_TCP и нужно привязывать сокет к определенному tcp порту, ответ распарсить так же, как ответ на пинг.
Как именно смастерить tcp syn? Был бы тебе признателен за пример.
я не знаю, но предполагаю, что аналогично пингу. то есть где в пинге открываем сокет: 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? Был бы тебе признателен за пример.
в общем написал небольшой пример, как послать 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? Был бы тебе признателен за пример.
получается довольно интересная ситуация, если я использую 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? Был бы тебе признателен за пример.
я нашел решение 11.12.14, Ivan Riabtsov<ivriabtsov@gmail.com> написал(а):
получается довольно интересная ситуация, если я использую 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? Был бы тебе признателен за пример.
ну все, можете смотреть, посылает tcp. https://github.com/ru-kernelnewbies-org/Network/blob/master/simple_examples/... 11.12.14, Ivan Riabtsov<ivriabtsov@gmail.com> написал(а):
я нашел решение
11.12.14, Ivan Riabtsov<ivriabtsov@gmail.com> написал(а):
получается довольно интересная ситуация, если я использую 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? Был бы тебе признателен за пример.
в общем нет способа нормально использовать socket raw и tcp, поскольку нельзя связать raw сокет и tcp порт, а это значит, что когда удаленный хост отправляет syn/ack в ответ на наш syn, ядро принимает этот пакет, видит, что не было отправленных syn на этот адрес и посылает rst пакет, который разрывает соединение. Однако есть способ это дело обойти, надо написать правило iptables которое разрешает нашему процессу посылать любые tcp пакеты, но запрещает все остальные tcp пакеты например на этот адрес, тогда можно написать свою реализацию tcp на уровне пользователя. Кажется так, если все же есть способ связать raw сокет с портом, то поделитесь 11.12.14, Ivan Riabtsov<ivriabtsov@gmail.com> написал(а):
ну все, можете смотреть, посылает tcp. https://github.com/ru-kernelnewbies-org/Network/blob/master/simple_examples/...
11.12.14, Ivan Riabtsov<ivriabtsov@gmail.com> написал(а):
я нашел решение
11.12.14, Ivan Riabtsov<ivriabtsov@gmail.com> написал(а):
получается довольно интересная ситуация, если я использую 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? Был бы тебе признателен за пример.
participants (4)
-
Alex Naumov -
Denis Kirjanov -
Dmitry Podgorny -
Ivan Riabtsov