IPv4报文分片代码

IP报文内包装了一个ICMP报文,不过还是没法重组报文。想要练习重组报文似乎得直接抓链路层的包才行。

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <netdb.h>
#include <linux/icmp.h>
#include <linux/ip.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>

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;
}



你可能感兴趣的:(ipv4)