aboutsummaryrefslogtreecommitdiffstats
path: root/mtu.c
diff options
context:
space:
mode:
Diffstat (limited to 'mtu.c')
-rw-r--r--mtu.c422
1 files changed, 110 insertions, 312 deletions
diff --git a/mtu.c b/mtu.c
index b1f7ae7..01af3ed 100644
--- a/mtu.c
+++ b/mtu.c
@@ -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();
}