! ICMP module for (Moose) INet process, GDMR, Jan 1988

%option "-NonStandard-NoCheck-NoTrace-NoDiag"

%include "INet:Common_Formats.Inc"
%include "INet:Utility.Inc"
%include "INet:INet.Inc"

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

%externalrecord(queue fm)%spec IP outbound queue

%constinteger ICMP echo reply               = 0
%constinteger ICMP destination unreachable  = 3
%constinteger ICMP source quench            = 4
%constinteger ICMP redirect                 = 5
%constinteger ICMP echo                     = 8
%constinteger ICMP time exceeded            = 11
%constinteger ICMP parameter problem        = 12
%constinteger ICMP timestamp                = 13
%constinteger ICMP timestamp reply          = 14
%constinteger ICMP information request      = 15
%constinteger ICMP information reply        = 16

%constinteger ICMP protocol unreachable     = 2
%constinteger ICMP port unreachable         = 3

%conststring(31)%array ICMP message text(0 : 16) =
   { 0}  "Echo reply",
   { 1}  "?? 1",
   { 2}  "?? 2",
   { 3}  "Destination unreachable",
   { 4}  "Source quench",
   { 5}  "Redirect",
   { 6}  "?? 6",
   { 7}  "?? 7",
   { 8}  "Echo",
   { 9}  "?? 9",
   {10}  "?? 10",
   {11}  "Time exceeded",
   {12}  "Parameter problem",
   {13}  "Timestamp",
   {14}  "Timestamp reply",
   {15}  "Information request",
   {16}  "Information reply"

%owninteger ICMP echo sequence = 0

%externalroutine ICMP inbound(%record(buffer fm)%name b)
   %string(127) message, source
   %integer c, x, host, net, class
      !! printstring("ICMP inbound from ")
      !! print inet address(b_IP header_source);  newline
      !! dump(byteinteger(addr(b_ICMP header)), b_IP bytes)
      message = "ICMP: from " . inet address to S(b_IP header_source)
      source = INet address to name(b_IP header_source)
      message = message . " (" . source . ")" %if source # ""
      split INet address(b_IP header_destination, host, net, class)
      %if host = 0 %or host = new broadcast mask(class) %start
         b_p_ICMP broadcasts = b_p_ICMP broadcasts + 1 %if b_p ## nil
         message = message . " (broadcast??)"
      %finish
      message = message . " -- "
      %if b_IP bytes < 8 %start
         pdate
         printstring("Short ICMP message from ")
         print inet address(b_IP header_source);  newline
         b_p_ICMP errors in = b_p_ICMP errors in + 1 %if b_p ## nil
         release buffer(b)
         %return
      %finish
      b_p_ICMP in = b_p_ICMP in + 1 %if b_p ## nil
      %if b_ICMP header_type = ICMP echo %start
         ! Send echo reply
         message = message . "echo request"
         !pdate
         !printstring(message);  newline
         tell network operator(message)
         b_ICMP header_type = ICMP echo reply
         b_ICMP header_checksum = 0
         c = calculate checksum(b_ICMP header, b_IP bytes)
         b_ICMP header_checksum <- \c
         b_p_ICMP out = b_p_ICMP out + 1 %if b_p ## nil
         !! printstring("Sending echo reply to ")
         !! print inet address(b_IP peer);  newline
         b_protocol = IP ICMP protocol
         b_next queue == nil
         enqueue buffer(b, IP outbound queue)
         %return
      %else %if b_ICMP header_type = ICMP echo reply
         message = message . "echo reply"
      %else %if b_ICMP header_type = ICMP timestamp
         ! Send timestamp reply
         x = b_ICMP header_originate timestamp
!N!      net order long(x)
         message = message . "timestamp request: " . convert timestamp(x)
         !pdate
         !printstring(message);  newline
         tell network operator(message)
         b_ICMP header_type = ICMP timestamp reply
         b_ICMP header_receive timestamp = generate timestamp
!N!      net order long(b_ICMP header_receive timestamp)
         b_ICMP header_transmit timestamp = b_ICMP header_receive timestamp
         b_ICMP header_checksum = 0
         c = calculate checksum(b_ICMP header, b_IP bytes)
         b_ICMP header_checksum <- \c
         b_p_ICMP out = b_p_ICMP out + 1 %if b_p ## nil
         !! printstring("Sending timestamp reply to ")
         !! print inet address(b_IP peer);  newline
         b_protocol = IP ICMP protocol
         b_next queue == nil
         enqueue buffer(b, IP outbound queue)
         %return
      %else %if b_ICMP header_type = ICMP timestamp reply
!N!      net order long(b_ICMP header_originate timestamp)
!N!      net order long(b_ICMP header_receive timestamp)
!N!      net order long(b_ICMP header_transmit timestamp)
         message = message . "timestamp reply: " . %c
                   convert timestamp(b_ICMP header_originate timestamp) . ", " . %c
                   convert timestamp(b_ICMP header_receive   timestamp) . ", " . %c
                   convert timestamp(b_ICMP header_transmit  timestamp)
      %else %if 0 <= b_ICMP header_type <= 16
         message = message . ICMP message text(b_ICMP header_type)
      %else
         message = message . " Unknown code " . itos(b_ICMP header_type, 0)
      %finish
      !pdate
      !printstring(message);  newline
      tell network operator(message)
      release buffer(b)
%end


! ICMP outbound (user requests)

%recordformat ICMP request fm(%integer code, target)

%externalroutine ICMP outbound(%record(buffer fm)%name b)
   %record(ICMP request fm)%name request
   %integer c
      request == record(addr(b_data start))
      !! printstring("ICMP outbound for ")
      !! print inet address(request_target);  newline
      b_p == find peer info(request_target)
      b_p == new peer info(request_target) %if b_p == nil
      release buffer(b) %and %return %if b_p == nil;  ! Tables must be full
      b_ICMP header == record(addr(b_data(32)))
      b_ICMP header = 0
      b_IP peer = request_target
      %if request_code = ICMP ping request %start
         b_ICMP header_type = ICMP echo
         b_ICMP header_code = 0
         ICMP echo sequence = ICMP echo sequence + 1
         b_ICMP header_sequence number = ICMP echo sequence
         c = calculate checksum(b_ICMP header, 8)
         b_ICMP header_checksum <- \c
         b_data bytes = 0;  b_IP bytes = 8;  b_protocol = IP ICMP protocol
         b_next queue == nil
         enqueue buffer(b, IP outbound queue)
         b_p_ICMP out = b_p_ICMP out + 1 %if b_p ## nil
      %else %if request_code = ICMP timestamp request
         b_ICMP header_type = ICMP timestamp
         b_ICMP header_code = 0
         ICMP echo sequence = ICMP echo sequence + 1
         b_ICMP header_sequence number = ICMP echo sequence
         b_ICMP header_originate timestamp = generate timestamp
!N!      net order long(b_ICMP header_originate timestamp)
         c = calculate checksum(b_ICMP header, 20)
         b_ICMP header_checksum <- \c
         b_data bytes = 0;  b_IP bytes = 20;  b_protocol = IP ICMP protocol
         b_next queue == nil
         enqueue buffer(b, IP outbound queue)
         b_p_ICMP out = b_p_ICMP out + 1 %if b_p ## nil
      %else
         !! printstring("Unknown ICMP request code ")
         !! phex(request_code);  newline
         b_p_ICMP errors out = b_p_ICMP errors out + 1 %if b_p ## nil
         release buffer(b)
      %finish
%end

%end %of %file
