IP报文内包装了一个ICMP报文,不过还是没法重组报文。想要练习重组报文似乎得直接抓链路层的包才行。
#include#include #include #include #include #include #include #include #include #include #include #include #include struct iphdr ip; //IP报头 char buf[128]; //ICMP报头 //校验和,网上挖过来的代码 unsigned short checksum(unsigned short *buffer, int size) { unsigned long cksum = 0; while (size > 1) { cksum += *buffer++; size -= sizeof(unsigned short); } if (size) cksum += *(unsigned short*) buffer; cksum = (cksum >> 16) + (cksum & 0xffff); cksum += (cksum >> 16); return (unsigned short) (~cksum); } //获取网络地址 int getaddr(const char *name, struct sockaddr_in *addr) { struct addrinfo hint = { 0, AF_INET, SOCK_RAW, IPPROTO_ICMP }; struct addrinfo *res; int rv; if ((rv = getaddrinfo(name, 0, &hint, &res)) != 0) { fprintf(stderr, "error at gethostinfo:%s\n", gai_strerror(rv)); return -1; } memcpy(addr, res->ai_addr, res->ai_addrlen); freeaddrinfo(res); return 0; } //获取原始套接字 int ip_socket() { int rv, sock; if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1) { fprintf(stderr, "error at socket:%s\n", strerror(errno)); return -1; } setuid(getuid()); rv = 1; //自己操纵IP报头 if ((rv = setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &rv, sizeof rv) == -1)) { fprintf(stderr, "error at setsockopt:%s\n", strerror(errno)); return -1; } return sock; } void init(const struct sockaddr_in *src, const struct sockaddr_in *dest) { char c = 0, *p; struct icmphdr *icmp = (struct icmphdr *) buf; for (p = (char *) (icmp + 1); p < buf + sizeof buf; ++p) { *p = c++; } //配置ICMP报头 icmp->type = ICMP_ECHO; icmp->code = 0; icmp->checksum = 0; icmp->un.echo.id = getpid(); icmp->un.echo.sequence = 0; icmp->checksum = checksum((unsigned short *) buf, sizeof buf); //配置IP报头,所有单元皆为大端序 ip.version = 4; ip.ihl = 5; ip.tos = 0; ip.tot_len = htons((ip.ihl << 2) + sizeof buf); ip.id = getpid(); ip.frag_off = 0; ip.ttl = 223; ip.protocol = IPPROTO_ICMP; ip.check = 0; memcpy(&ip.saddr, &src->sin_addr, sizeof(src->sin_addr)); memcpy(&ip.daddr, &dest->sin_addr, sizeof(dest->sin_addr)); } //发送分片,last为1时结束 int sendfrag(int sock, const struct sockaddr_in *dest, char *base, uint off, uint size, int last) { char flag; if (last) { flag = 0x40; //DF位, 0100 0000,大端序 } else { flag = 0x60; //MF|DF位,0110 0000,大端序 } int tot_len = sizeof(ip) + size; ip.tot_len = htons(tot_len); ip.frag_off = htons(off >> 3); ip.frag_off |= flag; ip.check = checksum((unsigned short *) &ip, sizeof ip); char sendb[tot_len]; //自己设置IP报头时,MSG_MORE似乎无效,所以还是得拷贝数据 memcpy(sendb, &ip, sizeof ip); memcpy(sendb + sizeof ip, base + off, size); long int rv; if ((rv = sendto(sock, sendb, tot_len, 0, (struct sockaddr*) dest, sizeof(struct sockaddr_in))) == -1) { fprintf(stderr, "error at sendto1:%s\n", strerror(errno)); return -1; } return rv; } int main(int argc, char *argv[]) { struct sockaddr_in src, dest; char buf2[256]; getaddr("127.0.0.1", &src); getaddr(argv[1], &dest); printf("from host:%s --->", inet_ntoa(src.sin_addr)); printf(" to host: %s\n", inet_ntoa(dest.sin_addr)); int sock = ip_socket(); if (sock == -1) { fprintf(stderr, "sock err and exit.\n"); return -1; } init(&src, &dest); uint i, len = sizeof dest; for (i = 0; i < 1024; ++i) { printf("ping dest: %s...", inet_ntoa(dest.sin_addr)); sendfrag(sock, &dest, buf, 0, 32, 0); sendfrag(sock, &dest, buf, 32, 32, 0); sendfrag(sock, &dest, buf, 64, 32, 0); sendfrag(sock, &dest, buf, 96, 32, 1); recvfrom(sock, buf2, 256, 0, (struct sockaddr *) &dest, &len); printf("recved.\n"); sleep(1); } printf("done.\n"); return 0; }