Cel:Jaki jest prawidłowy proces dla żądania echa ICMP w nieosiągalnych miejscach docelowych?
Potrzebuję móc pingować przełącznik sieciowy, aby określić, czy jest on dostępny. Ma to na celu poinformowanie użytkownika, że okablowanie sieciowe jest odłączone, przełącznik sieciowy jest niedostępny lub jakiś inny problem leży w ścieżce komunikacji sieciowej. Rozumiem, że to nie jest kompleksowe narzędzie diagnostyczne, ale coś jest lepsze niż nic.
Projekt:
planowałem na temat korzystania z gniazd surowych ICMP do wysyłania wiadomości pięciu (5) ping do konkretnego adresu w IPv4 dot-notacji. Skonfiguruję filtr ICMP na gnieździe i nie utworzę własnego nagłówka IP. Transmisja ICMP odbywa się za pomocą metody sendto i odbioru za pomocą metody recvfrom. Będzie to miało miejsce w pojedynczym wątku (chociaż inny wątek może zostać użyty do rozbicia transmisji i odbioru). Odbieranie wiadomości będzie dalej filtrowane przez dopasowanie identyfikatora odebranej wiadomości do przesłanego identyfikatora. Zapisany identyfikator będzie bieżącym identyfikatorem procesu aplikacji. Jeśli zostanie odebrany komunikat ICMP_CUTOREPLY, a identyfikator wiadomości i zapisany identyfikator są zgodne, to licznik jest zwiększany aż do osiągnięcia pięciu (4) (licznik jest zerowy). Spróbuję wysłać ping, poczekać na odpowiedź i powtórzyć ten proces pięć (5) razy.
Problem:
Po wdrożeniu mój projekt, ilekroć ping szczególną poprawny adres sieciowy (słownie 192.168.11.15) z aktywnego uczestnika sieci, otrzymuję wiadomości ICMP_ECHOREPLY dla każdego z pięciu (5) pingi . Jednak zawsze, gdy pinguję poprawny adres sieciowy (powiedzmy 192.168.30.30) z nieaktywnymi uczestnikami sieci (co oznacza, że żadne urządzenie nie jest podłączone do konkretnego adresu), otrzymuję jedną (1) komunikatów ICMP_DEST_UNREACH i cztery (4) komunikaty ICMP_ACTOREPLY. Identyfikator w odpowiedziach odpowiada identyfikatorowi zapisanemu w oprogramowaniu. Ilekroć wykonuję polecenie "ping 192.168.30.30" z wiersza poleceń, otrzymuję komunikat "From 192.168.40.50 icmp_seq = xx Destination Host Unreachable". Czy nie powinienem odbierać komunikatów ICMP_DEST_UNREACH zamiast komunikatów ICMP_ACTOREPLY?
Kod:
Ping.h:
#include <netinet/in.h>
#include <linux/ip.h>
#include <linux/ipmc.h>
#include <arpa/inet.h>
#include <cstdio>
#include <cstdlib>
#include <stdint.h>
#include <time.h>
#include <errno.h>
#include <string>
#include <cstring>
#include <netdb.h>
class Ping
{
public:
Ping(std::string host) : _host(host) {}
~Ping() {}
void start()
{
int sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if(sock < 0)
{
printf("Failed to create socket!\n");
close(sock);
exit(1);
}
setuid(getuid());
sockaddr_in pingaddr;
memset(&pingaddr, 0, sizeof(sockaddr_in));
pingaddr.sin_family = AF_INET;
hostent *h = gethostbyname(_host.c_str());
if(not h)
{
printf("Failed to get host by name!\n");
close(sock);
exit(1);
}
memcpy(&pingaddr.sin_addr, h->h_addr, sizeof(pingaddr.sin_addr));
// Set the ID of the sender (will go into the ID of the echo msg)
int pid = getpid();
// Only want to receive the following messages
icmp_filter filter;
filter.data = ~((1<<ICMP_SOURCE_QUENCH) |
(1<<ICMP_DEST_UNREACH) |
(1<<ICMP_TIME_EXCEEDED) |
(1<<ICMP_REDIRECT) |
(1<<ICMP_ECHOREPLY));
if(setsockopt(sock, SOL_RAW, ICMP_FILTER, (char *)&filter, sizeof(filter)) < 0)
{
perror("setsockopt(ICMP_FILTER)");
exit(3);
}
// Number of valid echo receptions
int nrec = 0;
// Send the packet
for(int i = 0; i < 5; ++i)
{
char packet[sizeof(icmphdr)];
memset(packet, 0, sizeof(packet));
icmphdr *pkt = (icmphdr *)packet;
pkt->type = ICMP_ECHO;
pkt->code = 0;
pkt->checksum = 0;
pkt->un.echo.id = htons(pid & 0xFFFF);
pkt->un.echo.sequence = i;
pkt->checksum = checksum((uint16_t *)pkt, sizeof(packet));
int bytes = sendto(sock, packet, sizeof(packet), 0, (sockaddr *)&pingaddr, sizeof(sockaddr_in));
if(bytes < 0)
{
printf("Failed to send to receiver\n");
close(sock);
exit(1);
}
else if(bytes != sizeof(packet))
{
printf("Failed to write the whole packet --- bytes: %d, sizeof(packet): %d\n", bytes, sizeof(packet));
close(sock);
exit(1);
}
while(1)
{
char inbuf[192];
memset(inbuf, 0, sizeof(inbuf));
int addrlen = sizeof(sockaddr_in);
bytes = recvfrom(sock, inbuf, sizeof(inbuf), 0, (sockaddr *)&pingaddr, (socklen_t *)&addrlen);
if(bytes < 0)
{
printf("Error on recvfrom\n");
exit(1);
}
else
{
if(bytes < sizeof(iphdr) + sizeof(icmphdr))
{
printf("Incorrect read bytes!\n");
continue;
}
iphdr *iph = (iphdr *)inbuf;
int hlen = (iph->ihl << 2);
bytes -= hlen;
pkt = (icmphdr *)(inbuf + hlen);
int id = ntohs(pkt->un.echo.id);
if(pkt->type == ICMP_ECHOREPLY)
{
printf(" ICMP_ECHOREPLY\n");
if(id == pid)
{
nrec++;
if(i < 5) break;
}
}
else if(pkt->type == ICMP_DEST_UNREACH)
{
printf(" ICMP_DEST_UNREACH\n");
// Extract the original data out of the received message
int offset = sizeof(iphdr) + sizeof(icmphdr) + sizeof(iphdr);
if(((bytes + hlen) - offset) == sizeof(icmphdr))
{
icmphdr *p = reinterpret_cast<icmphdr *>(inbuf + offset);
id = ntohs(p->un.echo.id);
if(origid == pid)
{
printf(" IDs match!\n");
break;
}
}
}
}
}
}
printf("nrec: %d\n", nrec);
}
private:
int32_t checksum(uint16_t *buf, int32_t len)
{
int32_t nleft = len;
int32_t sum = 0;
uint16_t *w = buf;
uint16_t answer = 0;
while(nleft > 1)
{
sum += *w++;
nleft -= 2;
}
if(nleft == 1)
{
*(uint16_t *)(&answer) = *(uint8_t *)w;
sum += answer;
}
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
answer = ~sum;
return answer;
}
std::string _host;
};
main.cpp:
#include "Ping.h"
int main()
{
// Ping ping("192.168.11.15");
Ping ping("192.168.30.30");
ping.start();
while(1) sleep(10);
}
W celu kompilacji, wystarczy wpisać 'g ++ -o main.cpp ping' w wierszu polecenia w Linuksie i powinna się skompilować (to znaczy, jeśli zainstalowany jest cały kod źródłowy).
Wniosek:
Czy ktoś może mi powiedzieć, dlaczego jestem otrzymaniu jednego (1) ICMP_DEST_UNREACH i cztery (4) komunikaty ICMP_ECHOREPLY z urządzenia, które nie znajduje się na danym adresie sieciowym?
UWAGA: Możesz zmienić adres IP sieci z pliku main.cpp. Po prostu zmień adres IP na urządzenie, które faktycznie istnieje w Twojej sieci lub urządzenie, które nie istnieje w Twojej sieci.
Nie interesują mnie również krytyka dotycząca stylu kodowania. Wiem, że to nie jest ładne, ma styl castowania w stylu C z obsadą C++, ma kiepskie zarządzanie pamięcią, itp., Ale to tylko prototypowy kod. Nie ma być ładna.
Czy struktura ICMP_ECHO_REPLY ma ten sam adres co adres docelowy? A może ma router pośredni? – Beached
Jeśli używasz '/ bin/ping -c5 192.168.30.30', co otrzymujesz? Drukowanie zawartości pakietu odpowiedzi może dostarczyć więcej informacji. Jeśli sam rozwiązałeś problem, opublikuj to, czego się nauczyłeś. –
To brzmi bardziej jak problem z siecią niż pytanie programistyczne. – cxxl