Raw Socket Linux отправка / получение пакета
есть некоторые проблемы при получении пакетов.
Я могу принимать и читать входящие пакеты, но я думаю, что не получаю рукопожатия с любым хостом.
Я только хочу отправить пакет на удаленный компьютер с открытым портом при получении ответа, чтобы увидеть TTL(время жизни) и размер окна.
Кто-нибудь знает, где ошибки? (у меня нет очень глубоких знаний в программировании на C)
код:
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <errno.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <net/ethernet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
struct pseudohdr {
u_int32_t src_addr;
u_int32_t dst_addr;
u_int8_t padding;
u_int8_t proto;
u_int16_t length;
};
struct data_4_checksum {
struct pseudohdr pshd;
struct tcphdr tcphdr;
char payload[1024];
};
unsigned short comp_chksum(unsigned short *addr, int len) {
long sum = 0;
while (len > 1) {
sum += *(addr++);
len -= 2;
}
if (len > 0)
sum += *addr;
while (sum >> 16)
sum = ((sum & 0xffff) + (sum >> 16));
sum = ~sum;
return ((u_short) sum);
}
int main(int argc, char *argv[]) {
int sock, bytes, on = 1;
char buffer[1024];
struct iphdr *ip;
struct tcphdr *tcp;
struct sockaddr_in to;
struct pseudohdr pseudoheader;
struct data_4_checksum tcp_chk_construct;
if (argc != 2) {
fprintf(stderr, "Usage: %s ", argv[0]);
fprintf(stderr, "<dest-addr>n");
return 1;
}
sock = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
if (sock == -1) {
perror("socket() failed");
return 1;
}else{
printf("socket() okn");
}
if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) == -1) {
perror("setsockopt() failed");
return 2;
}else{
printf("setsockopt() okn");
}
ip = (struct iphdr*) buffer;
tcp = (struct tcphdr*) (buffer + sizeof(struct tcphdr));
int iphdrlen = sizeof(struct iphdr);
int tcphdrlen = sizeof(struct tcphdr);
int datalen = 0;
printf("Typecasting okn");
ip->frag_off = 0;
ip->version = 4;
ip->ihl = 5;
ip->tot_len = htons(iphdrlen + tcphdrlen);
ip->id = 0;
ip->ttl = 40;
ip->protocol = IPPROTO_TCP;
ip->saddr = inet_addr("192.168.165.135");
ip->daddr = inet_addr(argv[1]);
ip->check = 0;
tcp->source = htons(12345);
tcp->dest = htons(80);
tcp->seq = random();
tcp->doff = 5;
tcp->ack = 0;
tcp->psh = 0;
tcp->rst = 0;
tcp->urg = 0;
tcp->syn = 1;
tcp->fin = 0;
tcp->window = htons(65535);
pseudoheader.src_addr = ip->saddr;
pseudoheader.dst_addr = ip->daddr;
pseudoheader.padding = 0;
pseudoheader.proto = ip->protocol;
pseudoheader.length = htons(tcphdrlen + datalen);
tcp_chk_construct.pshd = pseudoheader;
tcp_chk_construct.tcphdr = *tcp;
int checksum = comp_chksum((unsigned short*) &tcp_chk_construct,
sizeof(struct pseudohdr) + tcphdrlen + datalen);
tcp->check = checksum;
printf("TCP Checksum: %in", checksum);
printf("Destination : %in", ntohs(tcp->dest));
printf("Source: %in", ntohs(tcp->source));
to.sin_addr.s_addr = ip->daddr;
to.sin_family = AF_INET;
to.sin_port = tcp->dest;
bytes = sendto(sock, buffer, ntohs(ip->tot_len), 0, (struct sockaddr*) &to,
sizeof(to));
if (bytes == -1) {
perror("sendto() failed");
return 1;
}
recv(sock, buffer, sizeof(buffer), 0);
printf("TTL= %dn", ip->ttl);
printf("Window= %dn", tcp->window);
printf("ACK= %dn", tcp->ack);
printf("%s:%dt --> t%s:%d tSeq: %d tAck: %dn",
inet_ntoa(*(struct in_addr*) &ip->saddr), ntohs(tcp->source),
inet_ntoa(*(struct in_addr *) &ip->daddr), ntohs(tcp->dest),
ntohl(tcp->seq), ntohl(tcp->ack_seq));
return 0;
}
2 ответов
- вы получаете и храните пакеты в
buffer
, а вы печатаете данные изip
иtcp
без разбора этого буфера. Вы должны разобрать пакет изbuffer
после получения, и перед печатью. - ваш код предполагает, что все пакеты являются TCP, что не так. Необработанные сокеты поддерживают только протоколы уровня 3 (IP, ICMP и т. д.). Другими словами, используя
IPPROTO_TCP
вводит в заблуждение при создании сокетов. ПридерживатьсяIPPROTO_IP
, и добавьте необходимые условия к вашему коду для каждого протокола, о котором вы заботитесь (TCP, UDP и т. д.). Это работает, потому что ядро Linux проверяет номер протокола и резервируетIPPROTO_IP
. Однако это может не сработать в других системах. - Проверьте, использует ли ваша сетевая связь правильный порядок байтов. Порядок байтов сети является большим, в то время как порядок байтов хоста зависит от вашей архитектуры, поэтому вам может потребоваться преобразовать многобайтовые поля обратно и вперед.
- код
tcp->seq
может иметь недопустимое значение, потому что TCP принимает только значения до 65535, в то время какrandom()
возвращает значения от 0 доRAND_MAX
(0x7fffffff включительно). Попробуй!--10--> - ваш расчет смещения для заголовка TCP неверен. Он должен быть!--11-->, а не
sizeof(struct tcphdr)
.
ip = (struct iphdr*) buffer;
tcp = (struct tcphdr*) (buffer + sizeof(struct tcphdr));
здесь, чтобы получить индекс массива заголовка tcp в buffer
, вам необходимо добавить sizeof(struct iphdr)
to buffer
, как указано ниже.
ip = (struct iphdr*) buffer;
tcp = (struct tcphdr*) (buffer + sizeof(struct iphdr));