Почему сервер не отвечает syn-ack-пакетами, когда я отправляю syn-пакеты с необработанными сокетами?

Я экспериментирую с необработанными сокетами, и я только что написал небольшую программу, которая отправляет TCP-пакеты с syn флаг установлен. Я вижу пакеты, поступающие с Wireshark на стороне сервера, и они выглядят хорошо, но сервер никогда не отвечает ни на какие syn-ack пакеты.

Я сравнил syn пакеты, которые создает моя программа (см. код ниже) с теми, которые hping3 отправляет (потому что пакеты hping3 всегда получают syn-ack). Единственное, что отличается между моими пакетами синхронизации и пакетами синхронизации hping3, это ip identification число, tcp source port (который рандомизирован в hping3), tcp sequence number (который также рандомизирован в hping3) и ip checksum поле. Все эти четыре поля основаны на некоторых случайных числах, поэтому они отличаются. Все остальные поля равны! Но моя программа не получает никаких синаков, а hping3 делает!

Я использую Kali Linux для отправки пакетов (конечно, как root) и CentOS для сервера.

Я что-то упустил или просто что-то не так понял?

Удаленный код

РЕДАКТИРОВАТЬ
Вот весь пакет, захваченный Wireshark на стороне клиента (разделен на 4 изображения ниже). Обратите внимание, что пакеты, отправленные hping3, полностью равны, за исключением значений для идентификации ip, порта источника, порядкового номера и контрольной суммы:

Изображения удалены

Вот шестнадцатеричный дамп пакета.

Hexdump удален

РЕДАКТИРОВАТЬ 2

Хорошо, теперь я создал псевдо заголовок в соответствии с RFC793. Псевдо заголовок просто используется для вычисления контрольной суммы tcp. Теперь заголовок IP кажется правильным, но Wireshark жалуется на то, что пакет не содержит полного заголовка TCP, и он действительно кажется поврежденным, потому что некоторые поля содержат странные значения, которые я не установил.

Сначала я выделяю буфер (называется tcp_header) с пространством для заголовка tcp и псевдо заголовка. Во-вторых, я создаю буфер для заголовка ip, содержащий пространство для заголовков ip, tcp и pseudo.
Сначала я заполняю tcp_header с его данными, а затем я копирую его в ip_header перед отправкой с sendto функция.

Что-то идет не так, когда я копирую содержимое tcp_packet в ip_packet или я что-то не так делаю?

#include <cstdlib>
#include <stdio.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#define __FAVOR_BSD 1
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <sys/ioctl.h>
#include <string.h>
#include <iostream>
#include <net/ethernet.h>
#include <time.h>

#define PCKT_LEN 1024

struct pseudohdr
{
__u32 saddr;
__u32 daddr;
__u8  zero;
__u8  protocol;
__u16 lenght;
};

#define PSEUDOHDR_SIZE sizeof(struct pseudohdr)

unsigned short csum(unsigned short *buf, int len) {
unsigned long sum;
for(sum=0; len>0; len-=2)
sum += *buf++;
sum = (sum >> 16) + (sum &0xffff);
sum += (sum >> 16);
return (unsigned short)(~sum);
}int main(int argc, char** argv) {

srand(time(NULL));

char *ip_packet = new char[sizeof(struct iphdr) +
sizeof(struct tcphdr)]();

char *tcp_packet = new char[sizeof(struct pseudohdr) +
sizeof(struct tcphdr)]();

struct pseudohdr *pseudoheader = (struct pseudohdr*) tcp_packet;
class tcphdr *tcp = (struct tcphdr *) (tcp_packet + sizeof(struct pseudohdr));
class iphdr *ip = (struct iphdr *) ip_packet;class sockaddr_in sin, din;

int sd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
if(sd < 0) {
perror("socket() error");
exit(-1);
} else {
printf("socket()-SOCK_RAW and tcp protocol is OK.\n");
}

// Randomize src port
int srcport = rand()%100+25000;

sin.sin_family = AF_INET;           // Address family
sin.sin_addr.s_addr = inet_addr("192.168.2.80");
sin.sin_port = htons(srcport); // Source port

din.sin_family = AF_INET;
din.sin_addr.s_addr = inet_addr("192.168.2.6");
din.sin_port = htons(80); // Destination port

/* tcp pseudo header */
memcpy(&pseudoheader->saddr, &sin.sin_addr.s_addr, 4);
memcpy(&pseudoheader->daddr, &din.sin_addr.s_addr, 4);
pseudoheader->protocol      = 6; /* tcp */
pseudoheader->lenght        = htons(sizeof(struct pseudohdr) + sizeof(struct tcphdr));ip->ihl = 5;
ip->version = 4;
ip->tos = 0;
ip->tot_len = sizeof(class iphdr) + sizeof(class tcphdr);
ip->id = htons((getpid() & 255) + rand()%100+30000);
ip->frag_off = 0;
ip->ttl = 32;
ip->protocol = 6; // TCP
ip->check = 0; // Done by kernel
memcpy(&ip->saddr, (char*)&sin.sin_addr, sizeof(ip->saddr));
memcpy(&ip->daddr, (char*)&din.sin_addr, sizeof(ip->daddr));// The TCP structure
tcp->th_sport = htons(srcport);
tcp->th_dport = htons(80);      // Destination port
tcp->th_seq = htonl(rand()%100+1000);
tcp->th_ack = htonl(rand()%30);
tcp->th_off = 5;
tcp->th_flags = TH_SYN;
tcp->th_win = htons(1024);
tcp->th_urp = 0;

// Now calculate tcp checksum
tcp->th_sum = csum((unsigned short *) tcp_packet, sizeof(struct pseudohdr) + sizeof(struct tcphdr));

// Copy tcp_packet to ip_packet
memcpy(ip_packet + sizeof(struct iphdr), tcp_packet+sizeof(struct pseudohdr), sizeof(struct tcphdr));

// Bind socket to interface
int one = 1;
const int *val = &one;
const char opt[] = "eth0";

if(setsockopt(sd, IPPROTO_IP, IP_HDRINCL, (char *)&one, sizeof(one)) < 0) {
perror("setsockopt() error");
exit(-1);
}
else
printf("setsockopt() is OK\n");

if(sendto(sd, ip_packet, ip->tot_len, 0, (sockaddr*)&din, sizeof(din)) < 0) {
perror("sendto() error");
exit(-1);
}
else
printf("Send OK!");

close(sd);
return 0;
}

Содержимое tcp пакета:

Изображения удалены

Редактировать 3

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

Контрольная сумма находится в сетевом порядке и, следовательно, должна читаться в обратном порядке, как 0x06c0 (а не так, как указано выше как 0xc006). Это равно десятичному значению 1728, Wireshark говорит, что правильный чексум должен быть 0x12c0 который дает десятичное значение 4800,
4800-1728=3072, В этом разница между фактический контрольная сумма и правильный Контрольная сумма рассчитывается Wireshark во всех пакетах, которые отправляет моя программа.

Итак, если я просто добавлю это значение к результату чексума:

tcp->th_sum = csum((unsigned short *) tcp_packet, sizeof(struct pseudohdr) + sizeof(struct tcphdr)) + 3072;

…тогда все пакеты получают правильную контрольную сумму и получают соответствующую SYN-ACK,

Почему магическое число 3072 ???

7

Решение

Я не доволен алгоритмом контрольной суммы, который вы используете. Тот, который предложил Стивенс:

uint16_t
in_cksum(uint16_t *addr, int len)
{
int                     nleft = len;
uint32_t                sum = 0;
uint16_t                *w = addr;
uint16_t                answer = 0;

/*
* Our algorithm is simple, using a 32 bit accumulator (sum), we add
* sequential 16 bit words to it, and at the end, fold back all the
* carry bits from the top 16 bits into the lower 16 bits.
*/
while (nleft > 1)  {
sum += *w++;
nleft -= 2;
}

/* 4mop up an odd byte, if necessary */
if (nleft == 1) {
*(unsigned char *)(&answer) = *(unsigned char *)w ;
sum += answer;
}

/* 4add back carry outs from top 16 bits to low 16 bits */
sum = (sum >> 16) + (sum & 0xffff);     /* add hi 16 to low 16 */
sum += (sum >> 16);                     /* add carry */
answer = ~sum;                          /* truncate to 16 bits */
return(answer);
}
1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]