Тъй, готово. Експериментът....донякъде успешен, за справка:
Естествено не прави точно това което се иска, а е просто PoC експеримент. Ако на някой му се играе:
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <netdb.h>
#include <string.h>
#include <netinet/in.h>
#include <netinet/ip_icmp.h>
#define PACKETSIZE 64
struct packet
{
struct iphdr iphdr;
struct icmphdr hdr;
char msg[PACKETSIZE-sizeof(struct icmphdr)];
};
struct protoent *proto=NULL;
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;
}
unsigned short reverse2 (unsigned short nValue)
{
return (((nValue>> 8)) | (nValue << 8));
}
void fuckicmp(struct sockaddr_in *addr)
{ const int val=255;
int i, sd, cnt=1;
short cs;
struct packet pckt;
struct sockaddr_in r_addr;
char icmppacket[64];
int sum;
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(setsockopt(sd, IPPROTO_IP, IP_HDRINCL, &val, sizeof(val)) < 0)
perror("setsockopt() for IP_HDRINCL error");
for (;;)
{
int len=sizeof(r_addr);
if ( recvfrom(sd, &pckt, sizeof(pckt), sizeof(pckt), (struct sockaddr*)&r_addr, &len) > 0 )
if (pckt.hdr.type==ICMP_ECHO)
{
printf("Got icmp type=%d id=%d seq=%d\n",pckt.hdr.type,reverse2(pckt.hdr.un.echo.id),reverse2(pckt.hdr.un.echo.sequence));
pckt.hdr.type = ICMP_ECHOREPLY;
pckt.hdr.checksum = 0;
int src,dst;
src=pckt.iphdr.saddr;
dst=pckt.iphdr.daddr;
pckt.iphdr.daddr=src;
pckt.iphdr.saddr=dst;
//Uncomment to lie about pings, decrease response time by ~650ms
pckt.msg[6]+=10;
//Uncomment to make fun of users
//pckt.msg[3]+=20;
memcpy(icmppacket,&pckt.hdr,sizeof(struct icmphdr));
memcpy(&icmppacket[sizeof(struct icmphdr)],&pckt.msg,64-sizeof(struct icmphdr));
pckt.hdr.checksum=checksum(icmppacket,64);
if ( sendto(sd, &pckt, sizeof(pckt), 0, (struct sockaddr*)&r_addr, sizeof(r_addr)) <= 0 )
perror("sendto");
}
}
}
int main(int count, char *strings[])
{ struct hostent *hname;
struct sockaddr_in addr;
char *str="127.0.0.1";
proto = getprotobyname("ICMP");
hname = gethostbyname(str);
bzero(&addr, sizeof(addr));
addr.sin_family = hname->h_addrtype;
addr.sin_port = 0;
addr.sin_addr.s_addr = *(long*)hname->h_addr;
addr.sin_addr.s_addr = INADDR_ANY;
fuckicmp(&addr);
return 0;
}
За да тествате, първо забранявате echo response-ите от ядрото:
echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all
После компилирате и пускате горната програма като root.
Сега къде са проблемите и защо мисля, че подходът няма да доведе до решение на проблема:
1) Работи само при packet size=64, дефолтната в линукс. При други вероятно ще се счупи. Това лесно се оправя.
2) Правено е на базата на сорса на ping в линукс, където timespec структурата е първото нещо в payload-a и знаем точно къде да бутнем, за да променим резултата. Структурата между другото е архитектурно-зависима, така че ping от powerpc система примерно ще трябва да се handle-ва по друг начин. По-лошото е че windows ползва друг формат, не знам как и къде се пази timestamp-а, нямам сорса и единственият начин е да се снифи трафик и да се пробва.
Горните проблеми са разрешими, но най-големият проблем е че не можем лесно да "намаляме" response time-а освен ако часовниците на двете машини са синхронизирани, което обикновено не е случая. Тоест (както сигурно сте забелязали) можем да намаляме response time-a с милисекунди, но не можем да го вкараме в някакви разумни мярки и когато клиентът забележи, че timestamp-а е в бъдещето, се оплаква.
За целта трябва да се направи state таблица и промените да стават на база делтите между timestamp-овете на пристигналите icmp пакет-и, които могат да варират доста при бавна връзка. В идеалният случай може да се направи нещо което сработва след няколко получени пакети, но за първите пристигнали не можем да излъжим, няма начин освен ако не се сдобием с някакви свръхестествени способности.