%include "INet:Common_Formats.Inc"

%externalpredicatespec FS lookup(%string(31) what, %integername value)

%systemroutinespec phex(%integer x)
%systemstring(127)%fnspec itos(%integer n, p)

%constinteger max TCP trace = 63;  ! Suitable for masking
%ownstring(31) TCP trace name = "INET__TCP_TRACE_BUFFER"

%constinteger TCP trace data  = 96

%constinteger TCP trace in    = 1
%constinteger TCP trace out   = 2

%recordformat TCP trace fm(%integer inout, data bytes,
                           %record(TCB fm) TCB,
                           %record(TCP header fm) TCP header,
                           %bytearray data(1 : TCP trace data))
%constinteger TCP trace size = 4 + 4 + TCB size + %c
                               TCP header size + 4 {options} + TCP trace data

%recordformat TCP trace buffer fm(%integer next,
                                  %record(TCP trace fm)%array t(0 : max TCP trace))

%include "INet:Dump.Inc"

%routine print INet address(%integer addr)
   write(addr >> 24 & 255, 0);  print symbol('.')
   write(addr >> 16 & 255, 0);  print symbol('.')
   write(addr >>  8 & 255, 0);  print symbol('.')
   write(addr       & 255, 0)
%end

%routine print TCB address(%record(TCB fm)%name t)
   print inet address(t_remote address)
   print symbol('.');  write(t_remote port, 0)
   print symbol('/');  write(t_local port, 0)
%end

%routine show TCP header(%record(TCP header fm)%name h,
                         %integer n, %string(15) s)
   printstring("TCP ");  printstring(s)
   printstring(" -- source: ");  write(h_source, 0)
   printstring(", dest: ");  write(h_destination, 0)
   printstring(", seq: ");  phex(h_seq)
   printstring(", ack: ");  phex(h_ack)
   printstring(" FIN") %if h_flags & FIN bit # 0
   printstring(" SYN") %if h_flags & SYN bit # 0
   printstring(" RST") %if h_flags & RST bit # 0
   printstring(" PSH") %if h_flags & PSH bit # 0
   printstring(" ACK") %if h_flags & ACK bit # 0
   printstring(" URG") %if h_flags & URG bit # 0
   %if n # 0 %start
      printstring(" + ");  write(n, 0)
   %finish
   newline
   printstring("Window: ");  write(h_window, 0)
   printstring(", urgent: ") %and write(h_urgent, 0) %if h_flags & URG bit # 0
   newline
   %if (h_data offset >> 4) & 15 > 5 %start
      ! Options
      printstring("Options: ");  phex(h_options);  newline
   %finish
%end

%routine show TCB(%record(TCB fm)%name TCB)
   %integer now, offset
      %if TCB_local port = 0 %start
         printstring("*free*");  newline
         %return
      %finish
      print inet address(TCB_remote address);  print symbol('.')
      write(TCB_remote port, 0);  printstring(" <--> ")
      write(TCB_local port, 0);  newline
      printstring("State ");  printstring(TCP state name(TCB_state))
      printstring(", from ");  printstring(TCP state name(TCB_previous state))
    ! now = msecs timestamp
    ! offset = now - TCB_state change stamp
    ! printstring(", changed ")
    ! %if offset >= 0 %start
    !    write(offset, 0);  printstring(" msec")
    !    print symbol('s') %if offset # 1
    ! %else
    !    printstring("ages")
    ! %finish
    ! printstring(" ago")
      newline
      printstring("Snd nxt: ");  phex(TCB_snd nxt)
      printstring(", ISS: ");  phex(TCB_iss)
      printstring(", sent: ");  write(TCB_snd nxt - TCB_iss, 0)
      printstring(", una: ");  phex(TCB_snd una)
      printstring(", window: ");  write(TCB_snd wnd, 0);  newline
      printstring("Rcv nxt: ");  phex(TCB_rcv nxt)
      printstring(", IRS: ");  phex(TCB_irs)
      printstring(", received: ");  write(TCB_rcv nxt - TCB_irs, 0)
      printstring(", window: ");  write(TCB_rcv wnd, 0);  newline
%end

%routine show one(%record(TCP trace fm)%name t)
   %integer dumping, diff
      %return %if t_TCB_local port = 0;  ! Unused
      !! printstring("One at ");  phex(addr(t));  newline
      !! print TCB address(t_TCB);  newline
      %if t_inout = TCP trace in %start
         show TCP header(t_TCP header, t_data bytes, "in")
      %else %if t_inout = TCP trace out
         show TCP header(t_TCP header, t_data bytes, "out")
      %else
         show TCP header(t_TCP header, t_data bytes, itos(t_inout, 0) . " ??")
      %finish
      dumping = t_data bytes
      dumping = TCP trace data %if dumping > TCP trace data
      dump(t_data(1), dumping) %if dumping > 0
      show TCB(t_TCB)
      %if t_inout = TCP trace in %start
         diff = t_TCP header_seq - t_TCB_rcv nxt
         %if diff # 0 %start
            printstring("Receive: ")
            print symbol('+') %if diff > 0
            write(diff, 0);  newline
         %finish
         diff = t_TCP header_ack - t_TCB_snd nxt
         %if diff # 0 %start
            printstring("ACK: ")
            print symbol('+') %if diff > 0
            write(diff, 0);  newline
         %finish
      %else %if t_inout = TCP trace out
         diff = t_TCP header_seq - t_TCB_snd nxt
         %if diff # 0 %start
            printstring("Transmit: ")
            print symbol('+') %if diff > 0
            write(diff, 0);  newline
         %finish
      %finish
      newline
%end

%begin
   %record(TCP trace buffer fm)%name TCP trace buffer
   %integer status, i
      %if FS lookup(TCP trace name, i) %start
         TCP trace buffer == record(i)
      %else
         printstring("No TCP trace buffer");  newline
         %stop
      %finish
      !! printstring("TCP trace buffer mapped at ")
      !! phex(addr(TCP trace buffer));  newline
      show one(TCP trace buffer_t(i & max TCP trace)) %c
         %for i = TCP trace buffer_next, 1, TCP trace buffer_next + max TCP trace + 1
%end %of %program
