Vous devez recalculer la somme de contrôle dans l'en-tête ICMP avec les octets que vous ajoutez à l'en-tête.
La meilleure façon d'y parvenir est d'initialiser * icmp_send
pour pointer vers la chaîne buff
, puis de supprimer vos champs. Cela vous évitera memcpy
de * icmp_send
à buff
. Voici un exemple.
unsigned char buff[2000];
unsigned char *p = buff;
struct icmp *icmp_send;
icmp_send = (struct icmp *)buff;
icmp_send->icmp_type = ICMP_ECHO;
icmp_send->icmp_code = 0;
icmp_send->icmp_id = getpid();
icmp_send->icmp_cksum = 0;
icmp_send->icmp_seq = htons(1);
p += sizeof(icmp_send);
*p = 'A';
icmp_send->icmp_cksum = checksum(buff, sizeof(icmp_send) + 1);
if (sendto(sd, (unsigned char*)buff, sizeof(icmp_send) + 1, 0, (struct sockaddr*)addr, sizeof(*addr)) <= 0) {
printf("Send err.\n");
}
Dans l'exemple ci-dessus, vous pouvez ajuster la taille de la charge utile pour le transport (sizeof(icmp_send) + 1
) à toute valeur.
MISE À JOUR: Comme commenté par Matteo, jetant buff
à un pointeur struct icmp
pour accéder puis modifier les champs ICMP pourrait ne pas être sûr si votre plate-forme vous oblige à gérer l'accès aux champs dans des structures mal alignées correctement. Ecrire votre structure séparément, puis memcpy
au buff
avant de l'envoyer sur le réseau est la voie à suivre dans ce cas.
Retour à votre code, vous obtiendrez plus de détails dans l'exemple de code et compléter le code ci-après. L'idée ici est de calculer et de copier les valeurs entre pointeur car * icmp_send
et buff
ne sont pas liés.
Ce code envoie une requête d'écho ICMP avec des données, reçoit une réponse d'écho ICMP. Testé avec tcpdump
pour valider que les paquets sont échangés entre les hôtes.
Code complet ci-dessous (OT: assurez-vous d'utiliser les fonctions de Hton lors de l'envoi des données sur le réseau, le cas échéant)
unsigned short checksum(void *b, int len)
{ unsigned short *buf = b;
unsigned int sum=0;
unsigned short result;
for (sum = 0; len > 1; len -= 2)
sum += *buf++;
if (len == 1)
sum += *(unsigned char*)buf;
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
result = ~sum;
return result;
}
void ping(struct sockaddr_in *addr) {
int sd;
const int val=255;
struct sockaddr_in r_addr;
sd = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
if (sd < 0) {
perror("socket");
return;
}
if (setsockopt(sd, SOL_IP, IP_TTL, &val, sizeof(val)) != 0)
perror("Set TTL option");
if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0)
perror("Request nonblocking I/O");
socklen_t len=sizeof(r_addr);
struct icmp *icmp_send, *icmp_recv;
icmp_send->icmp_type = ICMP_ECHO;
icmp_send->icmp_code = 0;
icmp_send->icmp_id = getpid();
icmp_send->icmp_seq = htons(1);
unsigned char buff[2000];
unsigned char*p = buff;
p += sizeof(icmp_send);
*p = 'A';
icmp_send->icmp_cksum = 0;
memcpy(buff, icmp_send, sizeof(icmp_send)) ;
icmp_send->icmp_cksum = checksum(buff, sizeof(icmp_send) + 1);
memcpy(buff, icmp_send, sizeof(icmp_send)) ;
if (sendto(sd, (unsigned char*)buff, sizeof(icmp_send) + 1, 0, (struct sockaddr*)addr, sizeof(*addr)) <= 0) {
printf("Send err.\n");
}
}
int main(int argc, char *argv[])
{
if (argc != 2) {
printf("usage: %s destination_ip\n", argv[0]);
return 1;
}
struct sockaddr_in addr;
struct in_addr dst;
if (inet_aton(argv[1], &dst) == 0) {
perror("inet_aton");
printf("%s isn't a valid IP address\n", argv[1]);
return 1;
}
memset(&addr, 0, sizeof addr);
addr.sin_family = AF_INET;
addr.sin_addr = dst;
ping(&addr);
return 0;
}
Hope this helps!
C! = C++. Marquer uniquement avec la langue que vous utilisez, sauf si les deux sont réellement pertinents. – tambre
Votre '* icmp_send' n'est pas initialisé. Et vous connaissez évidemment 'perror', pourquoi imprimez-vous" Send err. " au lieu? – rustyx
Vous n'avez pas juste besoin de recalculer la somme de contrôle après avoir modifié le tampon et juste avant d'envoyer le paquet? Un bon exemple est donné ici: http://www.binarytides.com/icmp-ping-flood-code-sockets-c-linux/ –