diff options
Diffstat (limited to 'mtu.c')
-rw-r--r-- | mtu.c | 422 |
1 files changed, 110 insertions, 312 deletions
@@ -3,362 +3,160 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <unistd.h> -#include <asm/types.h> -#include <netinet/ip.h> #include <netinet/ip_icmp.h> -struct sockaddr_in whereto; -u_char inpack[0x10000]; -u_char outpack[0x10000]; -int mtu_size; +#include "icmp.h" -static char *pr_addr(__u32 addr) -{ - struct hostent *hp; - static char buf[4096]; +static int fd, mtu_size; +static struct sockaddr_in to; - sprintf(buf, "%s", inet_ntoa(*(struct in_addr *)&addr)); - return buf; -} - -static void pr_icmph(__u8 type, __u8 code, __u32 info, struct icmphdr *icp) +static void usage(void) { - switch(type) { - case ICMP_ECHOREPLY: - printf("Echo Reply\n"); - /* XXX ID + Seq + Data */ - break; - case ICMP_DEST_UNREACH: - switch(code) { - case ICMP_NET_UNREACH: - printf("Destination Net Unreachable\n"); - break; - case ICMP_HOST_UNREACH: - printf("Destination Host Unreachable\n"); - break; - case ICMP_PROT_UNREACH: - printf("Destination Protocol Unreachable\n"); - break; - case ICMP_PORT_UNREACH: - printf("Destination Port Unreachable\n"); - break; - case ICMP_FRAG_NEEDED: - printf("Frag needed and DF set (mtu = %u)\n", info); - break; - case ICMP_SR_FAILED: - printf("Source Route Failed\n"); - break; - case ICMP_PKT_FILTERED: - printf("Packet filtered\n"); - break; - default: - printf("Dest Unreachable, Bad Code: %d\n", code); - break; - } - break; - case ICMP_SOURCE_QUENCH: - printf("Source Quench\n"); - break; - case ICMP_REDIRECT: - switch(code) { - case ICMP_REDIR_NET: - printf("Redirect Network"); - break; - case ICMP_REDIR_HOST: - printf("Redirect Host"); - break; - case ICMP_REDIR_NETTOS: - printf("Redirect Type of Service and Network"); - break; - case ICMP_REDIR_HOSTTOS: - printf("Redirect Type of Service and Host"); - break; - default: - printf("Redirect, Bad Code: %d", code); - break; - } - if (icp) - printf("(New nexthop: %s)\n", pr_addr(icp->un.gateway)); - break; - case ICMP_ECHO: - printf("Echo Request\n"); - /* XXX ID + Seq + Data */ - break; - case ICMP_TIME_EXCEEDED: - switch(code) { - case ICMP_EXC_TTL: - printf("Time to live exceeded\n"); - break; - case ICMP_EXC_FRAGTIME: - printf("Frag reassembly time exceeded\n"); - break; - default: - printf("Time exceeded, Bad Code: %d\n", code); - break; - } - break; - case ICMP_PARAMETERPROB: - printf("Parameter problem: pointer = %u\n", icp ? (ntohl(icp->un.gateway)>>24) : info); - break; - case ICMP_TIMESTAMP: - printf("Timestamp\n"); - /* XXX ID + Seq + 3 timestamps */ - break; - case ICMP_TIMESTAMPREPLY: - printf("Timestamp Reply\n"); - /* XXX ID + Seq + 3 timestamps */ - break; - case ICMP_INFO_REQUEST: - printf("Information Request\n"); - /* XXX ID + Seq */ - break; - case ICMP_INFO_REPLY: - printf("Information Reply\n"); - /* XXX ID + Seq */ - break; -#ifdef ICMP_MASKREQ - case ICMP_MASKREQ: - printf("Address Mask Request\n"); - break; -#endif -#ifdef ICMP_MASKREPLY - case ICMP_MASKREPLY: - printf("Address Mask Reply\n"); - break; -#endif - default: - printf("Bad ICMP type: %d\n", type); - } + fprintf(stderr, + "usage: mtu -i <mtu-size> <host>\n" + " mtu -d <host>\n"); + exit(3); } -static u_short in_cksum(const u_short *addr, register int len, u_short csum) -{ - register int nleft = len; - const u_short *w = addr; - register u_short answer; - register int sum = csum; - - while (nleft > 1) { - sum += *w++; - nleft -= 2; - } - - if (nleft == 1) - sum += htons(*(u_char *)w << 8); - - sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ - sum += (sum >> 16); /* add carry */ - answer = ~sum; /* truncate to 16 bits */ - return (answer); -} - -int send_probe(int icmp_sock, int *seq) +static int do_ping(int seq, int size) { + __u8 buf[1500]; + struct iphdr *ip = (struct iphdr *) buf; struct icmphdr *icp; - int i, cc = mtu_size - sizeof(struct iphdr); - - icp = (struct icmphdr *)outpack; - icp->type = ICMP_ECHO; - icp->code = 0; - icp->checksum = 0; - icp->un.echo.sequence = htons(++(*seq)); - icp->un.echo.id = getpid(); - icp->checksum = in_cksum((u_short *)icp, cc, 0); + struct sockaddr_in from; + int len, retry; - printf("To %s: icmp_seq=%u bytes=%d\n", - pr_addr(whereto.sin_addr.s_addr), - ntohs(icp->un.echo.sequence), cc); + for (retry = 0; retry < 3; retry++) { + icmp_send_ping(fd, (struct sockaddr *) &to, sizeof(to), + seq, size); - i = sendto(icmp_sock, outpack, cc, 0, - (struct sockaddr *) &whereto, sizeof(whereto)); + if ((len = icmp_read_reply(fd, (struct sockaddr *) &from, + sizeof(from), buf, sizeof(buf))) <= 0) + continue; - return (cc == i ? 0 : i); -} - -int send_frag_needed(int icmp_sock, struct iphdr *iph, int newmtu) -{ - struct icmphdr *icp; - int i, cc = sizeof(struct icmphdr) + sizeof(struct iphdr) + 8; + if (icmp_parse_reply(buf, len, seq, + (struct sockaddr *) &from, + (struct sockaddr *) &to)) + return -1; - icp = (struct icmphdr *) outpack; - icp->type = ICMP_DEST_UNREACH; - icp->code = ICMP_FRAG_NEEDED; - icp->checksum = 0; - icp->un.frag.__unused = 0; - icp->un.frag.mtu = htons(newmtu); - - /* copy ip header + 64-bits of original packet */ - memcpy(icp + 1, iph, sizeof(struct iphdr) + 8); - - icp->checksum = in_cksum((u_short *)icp, cc, 0); - - printf("To %s: frag_needed mtu=%d\n", - pr_addr(whereto.sin_addr.s_addr), - mtu_size - 2); - - i = sendto(icmp_sock, outpack, cc, 0, - (struct sockaddr *) &whereto, sizeof(whereto)); + icp = (struct icmphdr *) &buf[ip->ihl * 4]; + switch (icp->type) { + case ICMP_ECHOREPLY: + return 0; + case ICMP_DEST_UNREACH: + if (icp->code != ICMP_FRAG_NEEDED) + return 0; + return ntohs(icp->un.frag.mtu); + default: + return -1; + } + } - return (cc == i ? 0 : i); + return -1; } -/* - * parse_reply -- - * Print out the packet, if it came from us. This logic is necessary - * because ALL readers of the ICMP socket get a copy of ALL ICMP packets - * which arrive ('tis only fair). This permits multiple copies of this - * program to be run without having intermingled output (or statistics!). - */ -int -parse_reply(int icmp_sock, __u8 *buf, int cc, void *addr) +static void do_discover(void) { - struct sockaddr_in *from = addr; - struct icmphdr *icp; - struct iphdr *ip; - int hlen; - int csfailed; + int seq = 1; + int low_mtu, high_mtu, try_mtu; - /* Check the IP header */ - ip = (struct iphdr *)buf; - hlen = ip->ihl*4; - if (cc < hlen + 8 || ip->ihl < 5) { - return 1; + /* Check if the host is up */ + if (do_ping(seq++, 0) < 0) { + fprintf(stderr, "Host is not up\n"); + return; } - /* Now the ICMP part */ - cc -= hlen; - icp = (struct icmphdr *)(buf + hlen); - csfailed = in_cksum((u_short *)icp, cc, 0); - - if (icp->type == ICMP_ECHOREPLY) { - if (icp->un.echo.id != getpid()) - return 1; /* 'Twas not our ECHO */ - - printf("From %s: icmp_seq=%u bytes=%d\n", - pr_addr(from->sin_addr.s_addr), - ntohs(icp->un.echo.sequence), - cc); - - if (cc+hlen >= mtu_size - 2) - send_frag_needed(icmp_sock, ip, mtu_size - 2); - } else { - /* We fall here when a redirect or source quench arrived. - * Also this branch processes icmp errors, when IP_RECVERR - * is broken. */ - - switch (icp->type) { - case ICMP_ECHO: - /* MUST NOT */ - return 1; - case ICMP_SOURCE_QUENCH: - case ICMP_REDIRECT: - case ICMP_DEST_UNREACH: - case ICMP_TIME_EXCEEDED: - case ICMP_PARAMETERPROB: - { - struct iphdr * iph = (struct iphdr *)(&icp[1]); - struct icmphdr *icp1 = (struct icmphdr*)((unsigned char *)iph + iph->ihl*4); - int error_pkt; - if (cc < 8+sizeof(struct iphdr)+8 || - cc < 8+iph->ihl*4+8) - return 1; - if (icp1->type != ICMP_ECHO || - iph->daddr != whereto.sin_addr.s_addr || - icp1->un.echo.id != getpid()) - return 1; - error_pkt = (icp->type != ICMP_REDIRECT && - icp->type != ICMP_SOURCE_QUENCH); - if (error_pkt) { - //acknowledge(ntohs(icp1->un.echo.sequence)); - } - - printf("From %s: icmp_seq=%u ", - pr_addr(from->sin_addr.s_addr), - ntohs(icp1->un.echo.sequence)); - if (csfailed) - printf("(BAD CHECKSUM)"); - pr_icmph(icp->type, icp->code, ntohl(icp->un.gateway), icp); - return !error_pkt; - } - default: - /* MUST NOT */ - break; + /* Check if there is no PMTU or if PMTU discovery works */ + low_mtu = do_ping(seq++, 1500); + if (low_mtu == -1) { + /* Binary search for working MTU */ + for (low_mtu = 68/2, high_mtu = 1500/2; low_mtu < high_mtu; ) { + try_mtu = low_mtu + (high_mtu - low_mtu + 1) / 2; + if (do_ping(seq++, try_mtu * 2) == 0) + low_mtu = try_mtu; + else + high_mtu = try_mtu - 1; } - printf("From %s: ", pr_addr(from->sin_addr.s_addr)); - pr_icmph(icp->type, icp->code, ntohl(icp->un.gateway), icp); - return 0; + low_mtu *= 2; + } else if (low_mtu == 0) { + low_mtu = 1500; } - return 0; + fprintf(stdout, "%d\n", low_mtu); } -static int read_reply(int icmp_sock, __u8 *packet, int packlen) +static void do_inject(void) { - struct iovec iov; - char addrbuf[128]; - socklen_t addrlen = sizeof(addrbuf); - int cc; + __u8 buf[1500]; + struct sockaddr_in from; + int len, seq = 0; - cc = recvfrom(icmp_sock, packet, packlen, 0, - (struct sockaddr *) addrbuf, &addrlen); - if (cc < 0) { - if (errno == EAGAIN || errno == EINTR) - return 0; - return -1; + icmp_send_ping(fd, (struct sockaddr *) &to, sizeof(to), + ++seq, mtu_size); + for (;;) { + if ((len = icmp_read_reply(fd, (struct sockaddr *) &from, + sizeof(from), + buf, sizeof(buf))) <= 0) + continue; + + if (icmp_parse_reply(buf, len, seq, + (struct sockaddr *) &from, + (struct sockaddr *) &to)) + continue; + + if (seq != 1) + sleep(1); + + icmp_send_ping(fd, (struct sockaddr *) &to, sizeof(to), + ++seq, mtu_size); + icmp_send_frag_needed(fd, (struct sockaddr *) &to, sizeof(to), + (struct iphdr *) buf, mtu_size - 2); } - - parse_reply(icmp_sock, packet, cc, addrbuf); } int main(int argc, char **argv) { - int pmtudisc = IP_PMTUDISC_DO; - int yes = 1; - int icmp_sock, raw_sock, seq = 0; - char *target; struct hostent *hp; + void (*action)(void) = NULL; + char *target; + int opt; - mtu_size = 1400; - - icmp_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); - if (icmp_sock < 0) { - perror("mtuinject: socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)"); - exit(1); + while ((opt = getopt(argc, argv, "di:")) != -1) { + switch (opt) { + case 'd': + action = do_discover; + break; + case 'i': + action = do_inject; + mtu_size = atoi(optarg); + break; + default: + usage(); + } } - if (setsockopt(icmp_sock, SOL_IP, IP_MTU_DISCOVER, - &pmtudisc, sizeof(pmtudisc)) == -1) { - perror("ping: IP_MTU_DISCOVER"); - exit(2); - } + if (action == NULL || optind >= argc) + usage(); - if (argc > 1) - target = argv[1]; - else { - fprintf(stderr, "usage: %s <host>\n", argv[0]); - exit(3); - } + target = argv[optind]; - memset((char *)&whereto, 0, sizeof(whereto)); - whereto.sin_family = AF_INET; - if (inet_aton(target, &whereto.sin_addr) != 1) { + fd = icmp_open(); + if (fd < 0) + exit(1); + + memset((char *) &to, 0, sizeof(to)); + to.sin_family = AF_INET; + if (inet_aton(target, &to.sin_addr) != 1) { hp = gethostbyname(target); if (!hp) { - fprintf(stderr, "ping: unknown host %s\n", target); + fprintf(stderr, "mtu: unknown host %s\n", target); exit(2); } - memcpy(&whereto.sin_addr, hp->h_addr, 4); + memcpy(&to.sin_addr, hp->h_addr, 4); } - printf("Injecting path MTU %d to %s\n", - mtu_size - 2, pr_addr(whereto.sin_addr.s_addr)); - send_probe(icmp_sock, &seq); - - for (;;) { - send_probe(icmp_sock, &seq); - read_reply(icmp_sock, inpack, sizeof(inpack)); - sleep(1); - } + action(); } |