Obiettivo:Qual è il processo corretto per la richiesta/risposta echo ICMP su destinazioni non raggiungibili?
Devo essere in grado di eseguire il ping di un interruttore di rete per determinare se è disponibile o meno. Lo scopo è quello di dire all'utente che il cablaggio di rete è scollegato, lo switch di rete non è disponibile o che qualche altro problema si trova all'interno del percorso di comunicazione di rete. Mi rendo conto che questo non è uno strumento di diagnosi completo, ma qualcosa è meglio di niente.
Design:
Ho programmato di usare ICMP con prese prime per inviare cinque messaggi (5) ping ad un indirizzo IPv4 in dot-notazione. Installerò un filtro ICMP sul socket e non creerò la mia intestazione IP. La trasmissione dell'ICMP avverrà attraverso il metodo sendto e la ricezione attraverso il metodo recvfrom. Ciò si verifica su un singolo thread (sebbene sia possibile utilizzare un altro thread per interrompere la trasmissione e la ricezione). La ricezione di un messaggio verrà ulteriormente filtrata associando l'ID del messaggio ricevuto all'ID trasmesso. L'ID memorizzato sarà l'ID del processo in esecuzione dell'applicazione. Se viene ricevuto un messaggio ICMP_ECHOREPLY e l'ID del messaggio e l'ID memorizzato corrispondono, allora un contatore viene incrementato fino a raggiungere cinque (4) (il contatore è a base zero). Cercherò di inviare un ping, attendere la sua risposta e ripetere questo processo cinque (5) volte.
Il problema:
Dopo aver implementato il mio disegno, ogni volta che il ping di un particolare indirizzo valido di rete (ad esempio 192.168.11.15) con un partecipante rete attiva, ricevo messaggi ICMP_ECHOREPLY per ciascuno dei cinque (5) ping . Tuttavia, ogni volta che eseguo il ping di un indirizzo di rete valido (ad esempio 192.168.30.30) con partecipanti di rete inattivi (il che significa che nessun dispositivo è connesso al particolare indirizzo), ottengo uno (1) ICMP_DEST_UNREACH e quattro (4) messaggi ICMP_ECHOREPLY. L'ID nei messaggi di risposta corrisponde all'ID archiviato nel software. Ogni volta che eseguo un 'ping 192.168.30.30' dalla riga di comando, ottengo 'Da 192.168.40.50 icmp_seq = xx Host di destinazione irraggiungibile'. Non dovrei ricevere messaggi ICMP_DEST_UNREACH invece dei messaggi ICMP_ECHOREPLY?
Il Codice:
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);
}
Per compilare, basta digitare 'g ++ -o main.cpp ping' in la riga di comando di una scatola Linux, e dovrebbe compilare (cioè se tutto il codice sorgente è installato).
Conclusione:
Qualcuno può dirmi il motivo per cui sto ricevendo un (1) ICMP_DEST_UNREACH e quattro (4) i messaggi ICMP_ECHOREPLY da un dispositivo che non è su quel particolare indirizzo di rete?
NOTA: è possibile modificare l'indirizzo IP di rete dal file main.cpp. Basta modificare l'IP su un dispositivo effettivamente esistente sulla rete o su un dispositivo che non esiste sulla rete.
Inoltre, non mi interessano le critiche sullo stile di codifica. So che non è bello, ha un casting in stile "C" misto a cast di C++, ha una scarsa gestione della memoria, ecc., Ma questo è solo un codice prototipo. Non è pensato per essere carino.
La struttura ICMP_ECHO_REPLY ha lo stesso indirizzo dell'indirizzo di destinazione? O ha il router intermedio? – Beached
Se si utilizza '/ bin/ping -c5 192.168.30.30', cosa ottieni? La stampa del contenuto del pacchetto di risposta può fornire ulteriori informazioni. Se hai risolto il problema da solo, per favore pubblica ciò che hai imparato. –
Sembra più un problema di rete che una domanda di programmazione. – cxxl