From 1e14551ab904b85db4e1f11c1a37c18ed0a0ac58 Mon Sep 17 00:00:00 2001 Message-Id: <1e14551ab904b85db4e1f11c1a37c18ed0a0ac58.1714788584.git.mdw@distorted.org.uk> From: Mark Wooding Date: Sat, 20 Dec 2008 11:39:32 +0000 Subject: [PATCH] uslip: New options for flooding tripe. Organization: Straylight/Edgeware From: Mark Wooding I have a bug to hunt which manifests under heavy load. --- uslip/tripe-uslip.1.in | 25 +++++- uslip/uslip.c | 179 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 193 insertions(+), 11 deletions(-) diff --git a/uslip/tripe-uslip.1.in b/uslip/tripe-uslip.1.in index 16b0a7e5..47268f4e 100644 --- a/uslip/tripe-uslip.1.in +++ b/uslip/tripe-uslip.1.in @@ -38,7 +38,7 @@ tripe-uslip \- fake SLIP interface for testing tripe .SH "SYNOPSIS" . .B tripe-uslip -.RB [ \-gp ] +.RB [ \-fgps ] .I socket . .\"-------------------------------------------------------------------------- @@ -174,6 +174,24 @@ read a packet from the socket and write it to standard output. Connect to .IR socket , read a packet from standard input and write it to the socket. +.TP +.B \-f, \-\-flood +Connect to +.IR socket , +and send packets as fast as possible. The packets sent aren't very +interesting, and there's no way to configure their contents. +.TP +.B \-s, \-\-sink +Connect to +.IR socket +and read packets as the become available. The packets are discarded, +though if stdout is a terminal, a simple spinning-baton animation is +updated once for each group of packets. If you are flooding one end of +a TrIPE connection, it's advisable to attach a sink to the other: +otherwise the destination +.B tripe-uslip +will attempt to consume all available memory, storing incoming packets +until someone retrieves them. . .\"-------------------------------------------------------------------------- .SH "BUGS" @@ -185,7 +203,10 @@ program is intended as a tool for testing the server. It is not expected to be useful in production environments. In particular, it intentionally imposes no limits on queue lengths or packet sizes, and its internals and interface (one packet per client -connection) are not well-suited for high performance. +connection) are not well-suited for high performance. That said, the +flood option has worked well enough to expose bugs in +.BR tripe 's +behaviour under heavy loads. .PP If .B tripe-uslip diff --git a/uslip/uslip.c b/uslip/uslip.c index dee9f94d..74a5b17e 100644 --- a/uslip/uslip.c +++ b/uslip/uslip.c @@ -47,6 +47,8 @@ #include #include +#include +#include #include #include #include @@ -54,6 +56,7 @@ #include #include #include +#include #include "slip.h" @@ -136,12 +139,11 @@ static pkq_node *make_pkqnode(void *p, size_t n) { pkq_node *pn; - if (!n) - return (0); + if (!n) return (0); pn = CREATE(pkq_node); pn->next = 0; pn->buf = xmalloc(n); - memcpy(pn->buf, p, n); + if (p) memcpy(pn->buf, p, n); pn->sz = n; pn->n = 0; return (pn); @@ -151,8 +153,7 @@ static int enqueue(pkq *q, pkq_node *pn) { int rc = 0; - if (!pn) - return (0); + if (!pn) return (0); rc = !q->head; pn->next = 0; *q->tail = pn; @@ -679,9 +680,163 @@ static void shovel(int from, int to) static void put(void) { shovel(STDIN_FILENO, make_sock('>')); } static void get(void) { shovel(make_sock('<'), STDOUT_FILENO); } +/*----- Flooding and sinking ----------------------------------------------*/ + +/* --- Connection jobs --- */ + +typedef struct conninfo { + struct conninfo *next; + conn c; +} conninfo; + +#define MAXCONN 32 +static conninfo conns[MAXCONN], *freeconns; + +static void (*connhook)(int, conninfo *); + +static void connrecycle(conninfo *c) { c->next = freeconns; freeconns = c; } + +static void connerr(conninfo *c) +{ + if (errno == EAGAIN) connrecycle(c); + else die(EXIT_FAILURE, "connect: %s", strerror(errno)); +} + +static void connected(int fd, void *p) + { if (fd == -1) connerr(p); else connhook(fd, p); } + +static void conndoconnect(conninfo *c) +{ + int fd; + struct sockaddr_un sun; + size_t sz; + + if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) + die(EXIT_FAILURE, "socket: %s", strerror(errno)); + socketaddr(&sun, &sz); + if (conn_init(&c->c, &sel, fd, (struct sockaddr *)&sun, sz, connected, c)) + connerr(c); +} + +static int timerflag; + +static void conndrip(struct timeval *tv, void *p) +{ + conninfo *c, *cc; + + timerflag = 0; + while (freeconns) { + c = freeconns; freeconns = 0; + while (c) { cc = c->next; conndoconnect(c); c = cc; } + } +} + +static void connloop(void (*connected)(int, conninfo *)) +{ + int i; + conninfo **cc; + sel_timer t; + struct timeval tv; + + connhook = connected; + for (i = 0, cc = &freeconns; i < MAXCONN; i++) { + *cc = &conns[i]; + cc = &conns[i].next; + } + *cc = 0; + + for (;;) { + if (freeconns && !timerflag) { + gettimeofday(&tv, 0); + TV_ADDL(&tv, &tv, 0, 10000); + sel_addtimer(&sel, &t, &tv, conndrip, 0); + timerflag = 1; + } + if (sel_select(&sel)) + die(EXIT_FAILURE, "select: %s", strerror(errno)); + } +} + +/* --- Sinking (glug glug) --- */ + +static void sink_close(gobbler *g, int err, void *p) +{ + static char baton[4] = "/-\\|"; + static int pos = 0; + static int count = 0; + static int state = '?'; + + conninfo *c = p; + + if (!err) { + if (state == '?') + state = isatty(STDOUT_FILENO) ? 'y' : 'n'; + if (state == 'y') { + if (count) count--; + else { + putchar(baton[pos]); + putchar('\b'); + fflush(stdout); + pos++; + if (pos >= sizeof(baton)) pos = 0; + count = 128; + } + } + } + connrecycle(c); +} + +static void sink_connected(int fd, conninfo *c) +{ + char dir = '<'; + + if (write(fd, &dir, 1) != 1) { + moan("write: %s (continuing)", strerror(errno)); + close(fd); + connrecycle(c); + return; + } + make_gobbler(fd, sink_close, c); +} + +static void sink(void) { connloop(sink_connected); } + +/* --- Flooding --- */ + +static void flood_close(dribbler *d, int err, void *p) +{ + conninfo *c = p; + + if (err) moan("write: %s (continuing)\n", strerror(errno)); + destroy_dribbler(d); + connrecycle(c); +} + +static void flood_connected(int fd, conninfo *c) +{ + static uint32 seq; + + dribbler *d; + pkq_node *pn; + int i; + +#define FLOOD_PKSZ 1024 + + pn = make_pkqnode(0, 1 + FLOOD_PKSZ); + pn->buf[0] = '>'; + STORE32(pn->buf + 1, seq); + for (i = 4; i < FLOOD_PKSZ; i++) + pn->buf[i + 1] = i & 0xff; + seq++; + d = make_dribbler(fd, flood_close, c); + enqueue_dribble(d, pn); +} + +static void flood(void) { connloop(flood_connected); } + /*----- Main code ---------------------------------------------------------*/ -static void usage(FILE *fp) { pquis(fp, "Usage: $ [-pg] SOCKET\n"); } +static void usage(FILE *fp) { pquis(fp, "Usage: $ [-fgps] SOCKET\n"); } static void version(void) { pquis(stdout, "$ (" PACKAGE " version " VERSION")\n"); } @@ -695,8 +850,10 @@ static void help(void) With no options, provides a SLIP interface for TrIPE.\n\ \n\ Options:\n\ + -f, --flood Send packets to TrIPE as fast as possible.\n\ + -g, --get Receive packet from TrIPE and write to stdout.\n\ -p, --put Send packet on stdin to TrIPE.\n\ - -g, --get Receive packet from TrIPE and write to stdout."); + -s, --sink Slurp packets out of TrIPE and display progress."); } int main(int argc, char *argv[]) @@ -711,15 +868,17 @@ int main(int argc, char *argv[]) { "version", 0, 0, 'v' }, { "put", 0, 0, 'p' }, { "get", 0, 0, 'g' }, + { "flood", 0, 0, 'f' }, + { "sink", 0, 0, 's' }, { 0, 0, 0, 0 } }; - i = mdwopt(argc, argv, "hvpg", opt, 0, 0, 0); + i = mdwopt(argc, argv, "hvpgfs", opt, 0, 0, 0); if (i < 0) break; switch (i) { case 'h': help(); return (0); case 'v': version(); return (0); - case 'p': case 'g': mode = i; break; + case 'p': case 'g': case 's': case 'f': mode = i; break; default: usage(stderr); exit(EXIT_FAILURE); break; } } @@ -730,6 +889,8 @@ int main(int argc, char *argv[]) case 'd': slipif(); break; case 'p': put(); break; case 'g': get(); break; + case 'f': flood(); break; + case 's': sink(); break; } return (0); } -- [mdw]