]> mj.ucw.cz Git - netgrind.git/commitdiff
HTTP analyser works.
authorMartin Mares <mj@ucw.cz>
Sun, 8 Jun 2003 14:54:46 +0000 (14:54 +0000)
committerMartin Mares <mj@ucw.cz>
Sun, 8 Jun 2003 14:54:46 +0000 (14:54 +0000)
netgrind/http.c
netgrind/netgrind.h
netgrind/save.c
netgrind/tcp.c

index c6acd6bca6b59ef1776899a14329163b78edfd45..3aafc56a553b8cf927de4b2fd418f518dfc4aa48 100644 (file)
@@ -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 <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <alloca.h>
 #include <netinet/in.h>
 
 #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:
index a524499929f53eae9d81f47f5236f798b59ee540..df1af428e22bcaa5b088256dee4ea0f5bcaf1101 100644 (file)
@@ -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 */
index c1bfe2abc3c683ec1765bd094ec01d3ef28cb37d..bdaeda282a7c09442c567056a17c775393afe9d2 100644 (file)
@@ -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));
index 7ca74ee4c44a9460de918279f53b0f5ace30f826..dcd378c48a9549c98f7f3ab4abded8c09cd9d80a 100644 (file)
@@ -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;