P1ll0wTh13f Ответов: 2

Попытка написать traceroute с помощью raw сокета получает бесконечный цикл


Я пытаюсь написать очень простую версию программы traceroute, используя необработанные сокеты и ICMP. Но когда я запускаю его, я просто всегда получаю бесконечный цикл Эхо-запросов сам по себе.

#include <netinet/ip.h>         
#include <netinet/ip_icmp.h>        
#include <sys/socket.h>         
#include <stdlib.h>             
#include <stdio.h>              
#include <sys/types.h>
#include <errno.h>
#include <netdb.h> 
#include <arpa/inet.h>
#include <ifaddrs.h>

int mysock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 
int ttl = 0;                    
char buffr[4069] = { 0 };
struct ip *myIpHeader = (struct ip*)buffr;      
struct ifaddrs *ifa;
getifaddrs(&ifa);   


затем я просто делаю некоторые проверки ввода и устанавливаю setsocketopt в HDRINCL

struct sockaddr_in cliAddr; 
cliAddr.sin_port = htons(7);   
cliAddr.sin_family = AF_INET;   
inet_pton(AF_INET, argv[1], &(cliAddr.sin_addr));


затем бесконечный цикл while, в котором я настраиваю заголовок ip и icmp следующим образом

myIpHeader->ip_v = 4;                           
myIpHeader->ip_hl = 5;                      
myIpHeader->ip_tos = 0;                                             
myIpHeader->ip_len = 20+8;
myIpHeader->ip_off = 0;                     
myIpHeader->ip_p = IPPROTO_ICMP;                
inet_pton(AF_INET, argv[1], &(myIpHeader->ip_dst));
inet_pton(AF_INET, ifa->ifa_name, &(myIpHeader->ip_src));
myIpHeader->ip_sum = 0;
myIpHeader->ip_id = htonl(12345);                               
myIpHeader->ip_ttl = ttl;

struct icmphdr *myicmphead = (struct icmphdr *) (buffr + 20);        
myicmphead->type = 8; 
myicmphead->code = 0;               
myicmphead->checksum = 0;


затем следует send и recv

sendto(mysock, buffr , sizeof (struct ip) + sizeof (struct icmphdr), 0, (struct sockaddr*)&cliAddr, sizeof cliAddr);
char buff[4096] = { 0 };            
struct sockaddr_in serverAddr;      
socklen_t inlen = sizeof(struct sockaddr_in);

recvfrom(mysock, buff, sizeof(buff), 0, (struct sockaddr*) & serverAddr, &inlen);
struct icmphdr *icmphd2 = (struct icmphdr *) (buff + 20);


а потом я просто проверяю, равен ли тип, который я получаю, 0, если да, то пункт назначения достигнут, и я выхожу, если нет, я собираюсь напечатать ttl и адрес и увеличить ttl

printf("ttl: %d Address:%s\n", ttl, inet_ntoa(serverAddr.sin_addr));


Но, как я уже сказал, я просто распечатываю исходный адрес, который помещаю в ip-головку в бесконечном цикле. В основном это выглядит так: (я просто ставлю заполнители для ip-адресов и так далее)

trace route to '...' (IP: 160.....) 
ICMP msgtype=8, code=0 
ttl limit: 0 Address 127.0.0.1 
ICMP msgtype=8, code=0 
ttl limit: 1 Address 127.0.0.1 
ICMP msgtype=8, code=0 
ttl limit: 2 Address 127.0.0.1
ICMP msgtype=8, code=0 
ttl limit: 3 Address 127.0.0.1 
ICMP msgtype=8, code=0 
ttl limit: 4 Address 127.0.0.1


и так далее в качестве примера что я получаю
Я был бы очень признателен за любые советы, как я мог бы решить эту проблему.

Что я уже пробовал:

Благодаря Йохену Арндту я смог решить эту проблему и теперь ставлю правильные адреса. Но теперь у меня появилась новая проблема. Программа каким-то образом просто получает первый rcv, так что я думаю, что забыл что-то сделать, прежде чем снова получить его, потому что он говорит, что ресурс временно недоступен как ошибка для получения(когда я ставлю его, чтобы не ждать ответа). Есть идеи, что я забыл?

2 Ответов

Рейтинг:
2

P1ll0wTh13f

по какой-то причине

inet_ntoa(serverAddr.sin_addr)
я использовал в печати переписанный адрес dest на тот, который я получил в качестве источника от icmp. Теперь это работает Тхо.


Рейтинг:
15

Jochen Arndt

Вы не перечисляете существующие сетевые интерфейсы, а используете только первый, возвращенный getifaddrs() Вы должны проверить, является ли это существующим интерфейсом IPv4 INET, а не чем-то другим или интерфейсом обратной связи.

Вы тоже проходите ifa->ifa_name к inet_pton(). Но это имя интерфейса (например, локальный хост, интерфейс eth0) пока inet_pton() ожидает IP-адрес в виде строки (например, "127.0.0.1"). Вы, вероятно, хотите использовать ifa->ifa_addr вместо этого он уже находится в двоичном формате.

Могут быть и другие ошибки, но вышеперечисленные должны быть исправлены до проверки правильности ваших отправленных данных.

Предлагаю ознакомиться с документацией используемых функций:
getifaddrs(3) - Страница руководства по Linux[^]
inet_pton(3) - Страница руководства по Linux[^]

Вы также всегда должны проверять возвращаемое значение функций, возвращающих состояние. Если бы вы сделали это для inet_pton()- вы бы поняли, что звонок не удался.


P1ll0wTh13f

Спасибо, что это очень помогло, я должен был изучить это больше с самого начала и проверить возвращаемые значения. Теперь я настроил их правильно, но у меня все еще есть проблема, потому что программа каким-то образом просто получает первый rcv и после этого никогда не продолжается, но и не завершается, так что я думаю, что забыл сделать что-то перед тем, как снова получить, потому что он говорит, что ресурс временно недоступен как ошибка для получения. Есть идеи, что я забыл?