divert.c (2063B)
1 /* 2 * introduce artificial delay/packet drop in the networking stack 3 * using divert sockets. 4 * 5 * pass out quick inet divert-packet port 666 6 */ 7 8 #include <sys/types.h> 9 #include <sys/socket.h> 10 11 #include <netinet/in.h> 12 #include <netinet/ip.h> 13 #include <netinet/tcp.h> 14 #include <arpa/inet.h> 15 16 #include <err.h> 17 #include <stdio.h> 18 #include <stdlib.h> 19 #include <string.h> 20 #include <time.h> 21 22 #include "arg.h" 23 24 char *argv0; 25 int port = 666; 26 int drop; 27 int wait; 28 29 void 30 ms2tv(struct timeval *tv, int ms) 31 { 32 tv->tv_sec = ms / 1000; 33 tv->tv_usec = (ms % 1000) * 1000; 34 } 35 36 int 37 tvsleep(struct timeval *tv) 38 { 39 struct timespec ts; 40 41 ts.tv_sec = tv->tv_sec; 42 ts.tv_nsec = tv->tv_usec * 1000; 43 return nanosleep(&ts, NULL); 44 } 45 46 void 47 loop(void) 48 { 49 char pkt[65535]; 50 struct sockaddr_in sa; 51 socklen_t salen; 52 struct timeval tv; 53 int fd, n; 54 55 fd = socket(AF_INET, SOCK_RAW, IPPROTO_DIVERT); 56 if (fd < 0) 57 err(1, "socket"); 58 59 memset(&sa, 0, sizeof(sa)); 60 sa.sin_family = AF_INET; 61 sa.sin_port = htons(port); 62 sa.sin_addr.s_addr = 0; 63 64 salen = sizeof(sa); 65 if (bind(fd, (struct sockaddr *)&sa, salen) < 0) 66 err(1, "bind"); 67 68 ms2tv(&tv, wait); 69 for (;;) { 70 tvsleep(&tv); 71 n = recvfrom(fd, pkt, sizeof(pkt), 0, 72 (struct sockaddr *)&sa, &salen); 73 if (n < 0) { 74 warn("recvfrom"); 75 continue; 76 } 77 if (drop > arc4random_uniform(100)) 78 continue; 79 n = sendto(fd, pkt, n, 0, (struct sockaddr *)&sa, salen); 80 if (n < 0) 81 warn("sendto"); 82 } 83 } 84 85 void 86 usage(void) 87 { 88 fprintf(stderr, "usage: a.out [-d drop-percentage] [-i wait-interval] [-p port]\n"); 89 exit(1); 90 } 91 92 int 93 main(int argc, char *argv[]) 94 { 95 const char *errstr; 96 97 ARGBEGIN { 98 case 'd': 99 drop = strtonum(EARGF(usage()), 0, 100, &errstr); 100 if (errstr) 101 errx(1, "invalid packet drop percentage"); 102 break; 103 case 'i': 104 wait = strtonum(EARGF(usage()), 1, 1000, &errstr); 105 if (errstr) 106 errx(1, "invalid timing interval"); 107 break; 108 case 'p': 109 port = strtonum(EARGF(usage()), 1, 65535, &errstr); 110 if (errstr) 111 errx(1, "invalid port"); 112 break; 113 default: 114 usage(); 115 } ARGEND 116 if (argc) 117 usage(); 118 loop(); 119 }