From: Martin Mares Date: Sun, 8 Jun 2003 14:54:46 +0000 (+0000) Subject: HTTP analyser works. X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=fea6ea1bc388b858d28df8cef83fce0de432c03c;p=netgrind.git HTTP analyser works. --- diff --git a/netgrind/http.c b/netgrind/http.c index c6acd6b..3aafc56 100644 --- a/netgrind/http.c +++ b/netgrind/http.c @@ -7,7 +7,7 @@ * of the GNU General Public License. */ -#define LOCAL_DEBUG +#undef LOCAL_DEBUG #include "lib/lib.h" #include "lib/pools.h" @@ -17,6 +17,7 @@ #include #include #include +#include #include #define MAXLINE 4096 @@ -29,7 +30,7 @@ struct http_header { struct http_state { enum { - HTTP_INIT, /* initialized, waiting for request */ + HTTP_IDLE, /* initialized, waiting for request */ HTTP_ERROR, /* protocol error, ignoring everything else */ HTTP_CUT, /* unexpected EOF in one direction, ignoring everything else */ HTTP_REQUEST, /* parsing request */ @@ -41,7 +42,7 @@ struct http_state { HTTP_CONNECT, /* inside CONNECT transaction */ } state; byte *error; - u64 init_time; + u64 req_start_time, resp_start_time; uns id; struct mempool *pool; list tx_queue, rx_queue; @@ -53,6 +54,8 @@ struct http_state { uns body_trailer; list *body_queue; uns body_end_state; + uns body_total_size; + uns req_counter; }; static void http_open(struct flow *f, u64 when) @@ -60,18 +63,115 @@ static void http_open(struct flow *f, u64 when) static int http_counter; struct http_state *s = xmalloc_zero(sizeof(*s)); f->appl_data = s; - s->init_time = when; s->id = http_counter++; DBG("HTTP: %d NEW %d.%d.%d.%d:%d -> %d.%d.%d.%d:%d\n", s->id, IPQUAD(f->saddr), ntohs(f->sport), IPQUAD(f->daddr), ntohs(f->dport)); list_init(&s->tx_queue); list_init(&s->rx_queue); + s->req_start_time = when; +} + +static byte *http_lookup_hdr(list *l, byte *name) +{ + struct http_header *h; + WALK_LIST(h, *l) + if (!strcasecmp(h->name, name)) + return h->value; + return NULL; +} + +static void http_report(struct flow *f, struct http_state *s, u64 when, byte *reason) +{ + byte *method, *url, *x, *y, *stat; + static uns http_counter; + + if (!(method = s->req_line)) + return; + + /* Analyse request line */ + url = method; + while (*url && *url != ' ') + url++; + while (*url == ' ') + *url++ = 0; + x = url; + while (*x != ' ') + x++; + *x = 0; + + /* Analyse response line */ + if (stat = s->resp_line) + { + while (*stat && *stat != ' ') + stat++; + while (*stat == ' ') + stat++; + x = stat; + while (*x && *x != ' ') + x++; + *x = 0; + } + else + stat = ""; + if (!reason) + reason = stat[0] ? stat : (byte*)"???"; + + /* Reconstruct full URL */ + if (!strstr(url, "://") && strcasecmp(method, "CONNECT")) + { + if (!(x = http_lookup_hdr(&s->req_headers, "Host:"))) + x = "???"; + y = url; + url = alloca(7 + strlen(x) + strlen(y) + 1); + sprintf(url, "http://%s%s", x, y); + } + + byte stamp[TIMESTAMP_LEN], src[22], dst[22]; + sprintf(src, "%d.%d.%d.%d:%d", IPQUAD(f->saddr), ntohs(f->sport)); + sprintf(dst, "%d.%d.%d.%d:%d", IPQUAD(f->daddr), ntohs(f->dport)); + format_timestamp(stamp, s->req_start_time); + u64 ttotal = when - s->req_start_time; + u64 tresp = (s->resp_line ? (s->resp_start_time - s->req_start_time) : 0); + if (!http_counter++) + printf("# timestamp source destination result que length total time wait time method URL\n"); + /* 2003-06-06 22:53:38.642 81.27.194.19:1175 205.217.153.53:80 200 0 14030 0.957 0.444 GET http://... */ + printf("%s %-21s %-21s %-3s %3d %8d %6d.%03d %6d.%03d %s %s\n", + stamp, src, dst, reason, + s->req_counter, + s->body_total_size, + (uns)(ttotal/1000000), (uns)(ttotal%1000000)/1000, + (uns)(tresp/1000000), (uns)(tresp%1000000)/1000, + method, url); + + s->req_counter++; } static void http_close(struct flow *f, int cause, u64 when) { struct http_state *s = f->appl_data; DBG("HTTP: %d CLOSE in state %d (cause %d)\n", s->id, s->state, cause); + if (cause != CAUSE_CLOSE) + { + if (s->state != HTTP_IDLE) + { + byte buf[16]; + sprintf(buf, "T%s", flow_cause_names_short[cause]); + http_report(f, s, when, buf); + } + } + else + switch (s->state) + { + case HTTP_ERROR: + http_report(f, s, when, "ERR"); + break; + case HTTP_CUT: + http_report(f, s, when, "CUT"); + break; + case HTTP_CONNECT: + http_report(f, s, when, "FIN"); + break; + } pkt_flush_queue(&s->rx_queue); pkt_flush_queue(&s->tx_queue); if (s->pool) @@ -114,19 +214,20 @@ static struct http_header *http_get_line(struct http_state *s, list *l) } } -static int http_skip_bytes(list *l, uns *pcnt) +static int http_skip_body_bytes(struct http_state *s) { for(;;) { - struct pkt *p = list_head(l); + struct pkt *p = list_head(s->body_queue); if (!p) return 0; uns avail = pkt_len(p); - uns want = *pcnt; + uns want = s->body_len; uns go = MIN(avail, want); p->data += go; - *pcnt -= go; - if (!*pcnt) + s->body_len -= go; + s->body_total_size += go; + if (!s->body_len) return 1; if (!pkt_len(p)) { @@ -160,6 +261,7 @@ static void http_init_xact(struct http_state *s) s->pool = mp_new(4096); s->req_line = s->resp_line = NULL; s->line_len = 0; + s->body_total_size = 0; } static void http_parse_hdr(list *l, struct http_header *h) @@ -174,15 +276,6 @@ static void http_parse_hdr(list *l, struct http_header *h) list_add_tail(l, &h->n); } -static byte *http_lookup_hdr(list *l, byte *name) -{ - struct http_header *h; - WALK_LIST(h, *l) - if (!strcasecmp(h->name, name)) - return h->value; - return NULL; -} - static int http_ask_body(struct http_state *s, list *hdr) { byte *x; @@ -234,7 +327,12 @@ static void http_parse_req(struct http_state *s) static void http_parse_resp(struct http_state *s) { - if (http_ask_body(s, &s->resp_headers)) + if (!strncasecmp(s->req_line, "HEAD ", 5)) + { + DBG("\tHEAD has no body :)\n"); + s->state = HTTP_DONE; + } + else if (http_ask_body(s, &s->resp_headers)) ; else if (!strncasecmp(s->req_line, "GET ", 4) && strstr(s->resp_line, " 200 ")) { @@ -271,11 +369,13 @@ static void http_input(struct flow *f, int dir, struct pkt *p) DBG("HTTP: %d STATE %d\n", s->id, s->state); switch (s->state) { - case HTTP_INIT: + case HTTP_IDLE: if (fin_tx || !http_have_input(&s->tx_queue)) return; s->state = HTTP_REQUEST; http_init_xact(s); + if (!s->req_start_time) + s->req_start_time = p->timestamp; break; case HTTP_REQUEST: if (fin_tx || fin_rx) @@ -297,7 +397,7 @@ static void http_input(struct flow *f, int dir, struct pkt *p) case HTTP_BODY_LENGTH: if (fin_rx) goto cut; - if (!http_skip_bytes(s->body_queue, &s->body_len)) + if (!http_skip_body_bytes(s)) return; DBG("\tEnd of body\n"); s->state = s->body_end_state; @@ -307,7 +407,7 @@ static void http_input(struct flow *f, int dir, struct pkt *p) goto cut; if (s->body_len) { - if (!http_skip_bytes(s->body_queue, &s->body_len)) + if (!http_skip_body_bytes(s)) return; } else if (s->body_trailer) @@ -334,7 +434,7 @@ static void http_input(struct flow *f, int dir, struct pkt *p) break; case HTTP_BODY_INF: s->body_len = ~0U; - http_skip_bytes(s->body_queue, &s->body_len); + http_skip_body_bytes(s); if (fin_rx) { DBG("\tEnd of FIN-delimited body\n"); @@ -354,6 +454,7 @@ static void http_input(struct flow *f, int dir, struct pkt *p) if (!h->buf[0]) goto err; s->resp_line = h->buf; + s->resp_start_time = p->timestamp; } else if (h->buf[0]) http_parse_hdr(&s->resp_headers, h); @@ -367,13 +468,17 @@ static void http_input(struct flow *f, int dir, struct pkt *p) s->state = HTTP_CONNECT; return; } - s->state = HTTP_INIT; + http_report(f, s, p->timestamp, NULL); + s->state = HTTP_IDLE; + s->req_start_time = 0; break; case HTTP_CONNECT: s->body_len = ~0U; - http_skip_bytes(&s->rx_queue, &s->body_len); + s->body_queue = &s->rx_queue; + http_skip_body_bytes(s); s->body_len = ~0U; - http_skip_bytes(&s->tx_queue, &s->body_len); + s->body_queue = &s->tx_queue; + http_skip_body_bytes(s); return; case HTTP_ERROR: case HTTP_CUT: diff --git a/netgrind/netgrind.h b/netgrind/netgrind.h index a524499..df1af42 100644 --- a/netgrind/netgrind.h +++ b/netgrind/netgrind.h @@ -70,6 +70,9 @@ enum close_cause { CAUSE_MAX }; +extern byte *flow_state_names[]; +extern byte *flow_cause_names[], *flow_cause_names_short[]; + struct appl_hooks { void (*open)(struct flow *f, u64 when); void (*input)(struct flow *f, int dir, struct pkt *p); /* dir0 = sent by initiator, pkt_len(p)==0 for close */ diff --git a/netgrind/save.c b/netgrind/save.c index c1bfe2a..bdaeda2 100644 --- a/netgrind/save.c +++ b/netgrind/save.c @@ -130,8 +130,7 @@ static void asave_open(struct flow *f, u64 when) static void asave_close(struct flow *f, int cause, u64 when) { struct asave_state *s = f->appl_data; - static byte *close_reasons[] = { "Close", "Connection reset", "Timeout", "Doomsday", "Corrupted" }; - asave_event(s, when, "Terminated: %s\n", close_reasons[cause]); + asave_event(s, when, "Terminated: %s\n", flow_cause_names[cause]); asave_event(s, when, "TX %Ld bytes in %Ld packets, RX %Ld bytes in %Ld packets\n", f->pipe[1].stat.bytes, f->pipe[1].stat.packets, f->pipe[0].stat.bytes, f->pipe[0].stat.packets); @@ -216,7 +215,6 @@ static void summary_close(struct flow *f, int cause, u64 when) u64 duration = when - s->estab_time; byte src[22], dst[22]; byte stamp[TIMESTAMP_LEN]; - static byte *sum_causes[] = { "OK", "RE", "TO", "DD", "CO" }; format_timestamp(stamp, s->estab_time); sprintf(src, "%d.%d.%d.%d:%d", IPQUAD(f->saddr), ntohs(f->sport)); sprintf(dst, "%d.%d.%d.%d:%d", IPQUAD(f->daddr), ntohs(f->dport)); @@ -226,7 +224,7 @@ static void summary_close(struct flow *f, int cause, u64 when) double avs = duration ? ((beff / ((double)duration/1000000)) / 1024) : 1e30; printf("%s %-21s %-21s %6d.%03d %s %7Ld %7Ld %3d%% ", stamp, src, dst, - (uns)(duration/1000000), (uns)(duration%1000000)/1000, sum_causes[cause], + (uns)(duration/1000000), (uns)(duration%1000000)/1000, flow_cause_names_short[cause], f->pipe[1].stat.bytes, f->pipe[0].stat.bytes, CLAMP((int)over,0,100)); printf((avs < 100) ? "%6.3f" : "%6.0f", MIN(avs, 999999)); diff --git a/netgrind/tcp.c b/netgrind/tcp.c index 7ca74ee..dcd378c 100644 --- a/netgrind/tcp.c +++ b/netgrind/tcp.c @@ -30,9 +30,9 @@ uns tcp_arrival_times, tcp_wait_for_ack; struct appl_hooks *tcp_default_appl; -#ifdef LOCAL_DEBUG -static byte *pipe_state_names[] = { "IDLE", "SYNSENT", "SYNACK", "ESTAB", "FINSENT", "FINISH" }; -#endif +byte *flow_state_names[] = { "IDLE", "SYNSENT", "SYNACK", "ESTAB", "FINSENT", "FINISH" }; +byte *flow_cause_names[] = { "Close", "Reset", "Timeout", "Doomsday", "Corrupted" }; +byte *flow_cause_names_short[] = { "OK", "RE", "TO", "DD", "CO" }; uns tcp_num_flows, tcp_max_flows; static struct flow **flow_hash;