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

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

%include "INet:Formats.Inc"
%include "INet:Utility.Inc"
%include "INet:INet.Inc"
%include "INet:Stats.Inc"
%include "INet:Lights.Inc"

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

%externalroutinespec IP outbound(%record(buffer fm)%name b)

%externalrecord(INet statistics fm)%spec stats

%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
!L!   lights or A(ICMP light)
      !! 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)
      message = message . " (broadcast??)" %if b_flags & broadcast flag # 0
      message = message . " -- "
      %if b_IP bytes < 8 %start
         pdate
         printstring("Short ICMP message from ")
         print inet address(b_IP header_source);  newline
         stats_ICMP errors in = stats_ICMP errors in + 1
         release buffer(b)
         -> out
      %finish
      stats_ICMP in = stats_ICMP in + 1
      %if b_ICMP header_type = ICMP echo %start
         ! Send echo reply
         %if b_flags & broadcast flag = 0 %start
            ! Don't reply to broadcasts
            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
            stats_ICMP out = stats_ICMP out + 1
            !! printstring("Sending echo reply to ")
            !! print inet address(b_IP peer);  newline
            b_protocol = IP ICMP protocol
            b_next queue == nil
            IP outbound(b)
            -> out
         %finish
      %else %if b_ICMP header_type = ICMP echo reply
         message = message . "echo reply"
         tell network operator(message)
      %else %if b_ICMP header_type = ICMP timestamp
         ! Send timestamp reply
         %if b_flags & broadcast flag = 0 %start
            ! Don't reply to broadcasts
            x = b_ICMP header_originate timestamp
!N!         net order long(x)
            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
            stats_ICMP out = stats_ICMP out + 1
            !! printstring("Sending timestamp reply to ")
            !! print inet address(b_IP peer);  newline
            b_protocol = IP ICMP protocol
            b_next queue == nil
            IP outbound(b)
            -> out
         %finish
      %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)
         tell network operator(message)
      %finish
      release buffer(b)
out:
!L!   lights and A(\ICMP light)
%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
!L!   lights or B(ICMP light)
      request == record(addr(b_data start))
      !! printstring("ICMP outbound for ")
      !! print inet address(request_target);  newline
      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
         IP outbound(b)
         stats_ICMP out = stats_ICMP out + 1
      %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
         IP outbound(b)
         stats_ICMP out = stats_ICMP out + 1
      %else
         !! printstring("Unknown ICMP request code ")
         !! phex(request_code);  newline
         stats_ICMP errors out = stats_ICMP errors out + 1
         release buffer(b)
      %finish
!L!   lights and B(\ICMP light)
%end

%end %of %file
