Я изучаю программирование сокетов с помощью C/C++, и я думаю, что лучший способ - погрузиться в него. Я могу отправить данные в сокет, используя socket.h send(), поэтому хотелось бы глубже обработать сетевые пакеты.Создание пакетов TCP/IP
Я пытался, но все еще не мог понять, какая часть моих данных является недействительным, так как я получаю недопустимый аргумент ERRNO 22. Это мой IP заголовок в шестнадцатеричном:
45 00 28 00
d4 31 00 00
ff 06 3c 6e
c0 a8 01 06
c0 a8 01 01
И это мой TCP заголовок:
00 50 00 50
00 00 00 00
00 00 00 00
50 02 16 d0
15 1b 00 00
Я ценю любые советы.
NB: Я читаю beej.us и here для учебы.
Edit: Это мой код:
struct pseudo_header {
u_int32_t source_address;
u_int32_t dest_address;
u_int8_t placeholder;
u_int8_t protocol;
u_int16_t tcp_length;
};
int main(int argc, char* argv[]) {
int sockfd = socket (PF_INET, SOCK_RAW, IPPROTO_TCP);
if (sockfd == -1) {
perror("Failed to create socket");
exit(1);
}
// Datagram to represent the packet
char datagram[4096];
memset(datagram, 0, 4096); // zero out the packet buffer
//Data part
char *data = datagram + sizeof(struct ip) + sizeof(struct tcphdr);
strcpy(data, "");
// some address resolution
char source_ip[32];
strcpy(source_ip, "192.168.1.6");
struct sockaddr_in sai;
sai.sin_family = AF_INET;
sai.sin_port = htons(80);
sai.sin_addr.s_addr = inet_addr("192.168.1.1");
cout << "sai.sin_addr.s_addr=" << sai.sin_addr.s_addr << endl;
//Fill in the IP Header
struct ip *iph = (struct ip *) datagram;
iph->ip_hl = 5;
iph->ip_v = 4;
iph->ip_tos = 0;
iph->ip_len = sizeof(struct ip) + sizeof(struct tcphdr) + strlen(data);
iph->ip_id = htons(54321);
iph->ip_off = 0;
iph->ip_ttl = 255;
iph->ip_p = IPPROTO_TCP;
iph->ip_sum = 0;
iph->ip_src.s_addr = inet_addr(source_ip);
iph->ip_dst.s_addr = sai.sin_addr.s_addr;
//Ip checksum
unsigned short checksum = csum((unsigned short *) datagram, iph->ip_len);
iph->ip_sum = checksum;
cout << "iph->ip_sum=" << checksum << endl;
unsigned char *pIph = (unsigned char *) datagram;
for (int i = 0; i < 20; i++) {
cout << setfill('0') << setw(2) << hex << (int) pIph[i] << " ";
if (i + 1 >= 4 && (i + 1) % 4 == 0) {
cout << endl;
}
}
//TCP Header
struct tcphdr *tcph = (struct tcphdr *) (datagram + sizeof(struct ip));
struct pseudo_header psh;
tcph->th_sport = htons(80);
tcph->th_dport = htons(80);
tcph->th_seq = 0;
tcph->th_ack = 0;
tcph->th_off = 5;
tcph->th_flags = TH_SYN;
tcph->th_win = htons(5840); /* maximum allowed window size */
tcph->th_sum = 0;
tcph->th_urp = 0;
//Now the TCP checksum
psh.source_address = inet_addr(source_ip);
psh.dest_address = sai.sin_addr.s_addr;
psh.placeholder = 0;
psh.protocol = IPPROTO_TCP;
psh.tcp_length = htons(sizeof(struct tcphdr) + strlen(data));
int psize = sizeof(struct pseudo_header) +
sizeof(struct tcphdr) +
strlen(data);
char *pseudogram = malloc(psize);
memcpy(pseudogram, (char*) &psh, sizeof(struct pseudo_header));
memcpy(pseudogram + sizeof(struct pseudo_header), tcph, sizeof(struct tcphdr) + strlen(data));
checksum = csum((unsigned short*) pseudogram, psize);
tcph->th_sum = checksum;
cout << "tcph->th_sum=" << checksum << endl;
unsigned char *pTcph = (unsigned char *) tcph;
for (int i = 0; i < 20; i++) {
cout << setfill('0') << setw(2) << hex << (int) pTcph[i] << " ";
if (i + 1 >= 4 && (i + 1) % 4 == 0) {
cout << endl;
}
}
//IP_HDRINCL to tell the kernel that headers are included in the packet
int one = 1;
const int *val = &one;
if (setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, val, sizeof(one)) < 0) {
perror("Error setting IP_HDRINCL");
exit(0);
}
struct sockaddr *pSa = (struct sockaddr *) &sai;
// Send the packet
if (sendto(sockfd, datagram, iph->ip_len, 0, pSa, sizeof(sai)) < 0) { // failed here
perror("sendto failed");
} else { //Data send successfully
printf("Packet Send. Length : %d \n", iph->ip_len);
}
return 1;
}
Есть ли причина, вы хотите создать и обрабатывать свои собственные сырые пакеты? Если вы просто хотите изучить сетевое программирование, то это не очень хороший старт IMO. Вместо этого изучите, как использовать стандартные протоколы TCP и UDP, используя обычные сокеты SOCK_STREAM и SOCK_DGRAM. Затем, если вы хотите реализовать свой собственный сетевой стек (по какой-то мазохистской причине), вы можете это сделать, но делайте это медленно, шаг за шагом и *** начинайте с вершины ***. –
Где * точно * вы сталкиваетесь с 'errno', установленным на 22? Пожалуйста, разместите часть кода, где он не работает. –
Да, есть причина, по которой я хочу создавать сырые пакеты. Я хочу написать программу туннелирования ICMP. – anon