aboutsummaryrefslogtreecommitdiffstats
path: root/src/mtu.c
diff options
context:
space:
mode:
authorNatanael Copa <ncopa@alpinelinux.org>2013-08-22 14:43:24 +0200
committerNatanael Copa <ncopa@alpinelinux.org>2013-08-22 14:46:10 +0200
commitd3e49c4280f10584587fad83bc08c6a168c174d3 (patch)
tree9127d2f55556a0a049bef296c14da74e0b05904e /src/mtu.c
parent82ec45bb8abf8052a558e8ad589b66b34a0084fa (diff)
downloadpingu-d3e49c4280f10584587fad83bc08c6a168c174d3.tar.bz2
pingu-d3e49c4280f10584587fad83bc08c6a168c174d3.tar.xz
move soures to src subdir
Diffstat (limited to 'src/mtu.c')
-rw-r--r--src/mtu.c247
1 files changed, 247 insertions, 0 deletions
diff --git a/src/mtu.c b/src/mtu.c
new file mode 100644
index 0000000..77766eb
--- /dev/null
+++ b/src/mtu.c
@@ -0,0 +1,247 @@
+#include <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <linux/if.h>
+#include <linux/sockios.h>
+#include <netinet/ip_icmp.h>
+
+#include "icmp.h"
+
+static int fd, mtu_size;
+static struct sockaddr_in to;
+
+static void usage(void)
+{
+ fprintf(stderr,
+ "usage: mtu -i <mtu-size> <host>\n"
+ " mtu -I <host>\n"
+ " mtu -d <host>\n"
+ " mtu -D <host>\n"
+ "\n"
+ " -i <mtu-size> Inject <mtu-size> as PMTU to <host>\n"
+ " -I Inject local PMTU as PMTU to <host>\n"
+ " -d Discover PMTU to <host>\n"
+ " -D Discover PMTU to <host> and assign it to interface MTU\n"
+ "\n");
+ exit(3);
+}
+
+static int do_ping(int seq, int size)
+{
+ __u8 buf[1500];
+ struct iphdr *ip = (struct iphdr *) buf;
+ struct icmphdr *icp;
+ struct sockaddr_in from;
+ int len, retry;
+
+ for (retry = 0; retry < 3; retry++) {
+ icmp_send_ping(fd, (struct sockaddr *) &to, sizeof(to),
+ seq, size);
+
+not_mine:
+ 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))
+ goto not_mine;
+
+ 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 -1;
+}
+
+static int discover_mtu(void)
+{
+ int seq = 1, low_mtu, high_mtu, try_mtu, r;
+
+ /* Check if the host is up */
+ if (do_ping(seq++, 0) < 0)
+ return -1;
+
+ /* Discover PMTU */
+ low_mtu = 68/2;
+ high_mtu = 1500/2;
+ try_mtu = 1500/2;
+ while (1) {
+ r = do_ping(seq++, try_mtu * 2);
+ if (r > 0 && r < try_mtu * 2) {
+ /* pmtu */
+ high_mtu = r/2;
+ try_mtu = high_mtu;
+ continue;
+ }
+ if (r == 0)
+ low_mtu = try_mtu;
+ else
+ high_mtu = try_mtu - 1;
+ if (low_mtu >= high_mtu)
+ return 2 * low_mtu;
+
+ try_mtu = low_mtu + (high_mtu - low_mtu + 1) / 2;
+ }
+}
+
+static void do_discover(void)
+{
+ int mtu;
+
+ mtu = discover_mtu();
+ if (mtu > 0)
+ fprintf(stdout, "%d\n", mtu);
+ else
+ fprintf(stderr, "Host is not up\n");
+}
+
+static int set_mtu(const char *dev, int mtu)
+{
+ struct ifreq ifr;
+ int fd;
+
+ fd = socket(PF_INET, SOCK_DGRAM, 0);
+ if (fd < 0)
+ return -1;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, dev, IFNAMSIZ);
+ ifr.ifr_mtu = mtu;
+ if (ioctl(fd, SIOCSIFMTU, &ifr) < 0) {
+ perror("SIOCSIFMTU");
+ close(fd);
+ return -1;
+ }
+ close(fd);
+ return 0;
+}
+
+static void do_discover_and_write(void)
+{
+ int mtu;
+ char iface[IFNAMSIZ];
+
+ mtu = discover_mtu();
+ if (mtu < 0) {
+ fprintf(stderr, "Failed to determine MTU\n");
+ return;
+ }
+
+ if (!netlink_route_get(&to, NULL, iface)) {
+ fprintf(stderr, "Failed to determine route interface\n");
+ return;
+ }
+
+ printf("Writing %d to %s\n", mtu, iface);
+ set_mtu(iface, mtu);
+}
+
+static void do_inject(void)
+{
+ __u8 buf[1500];
+ struct sockaddr_in from;
+ int len, i, seq = 0;
+
+ icmp_send_ping(fd, (struct sockaddr *) &to, sizeof(to),
+ ++seq, mtu_size);
+ for (i = 0; i < 5; i++) {
+ 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);
+ }
+}
+
+static void do_inject_pmtu(void)
+{
+ u_int16_t mtu;
+
+ if (!netlink_route_get(&to, &mtu, NULL)) {
+ fprintf(stderr, "Failed to determine Path MTU\n");
+ return;
+ }
+ if (mtu == 1500)
+ return;
+
+ mtu_size = mtu;
+ do_inject();
+}
+
+int main(int argc, char **argv)
+{
+ struct hostent *hp;
+ void (*action)(void) = NULL;
+ char *target;
+ int opt;
+
+ while ((opt = getopt(argc, argv, "DdIi:")) != -1) {
+ switch (opt) {
+ case 'D':
+ action = do_discover_and_write;
+ break;
+ case 'd':
+ action = do_discover;
+ break;
+ case 'i':
+ action = do_inject;
+ mtu_size = atoi(optarg);
+ break;
+ case 'I':
+ action = do_inject_pmtu;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (action == NULL || optind >= argc)
+ usage();
+
+ target = argv[optind];
+
+ fd = icmp_open(1.0);
+ 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, "mtu: unknown host %s\n", target);
+ exit(2);
+ }
+ memcpy(&to.sin_addr, hp->h_addr, 4);
+ }
+
+ action();
+}
+