! IP module for INet process, GDMR, Feb/March 1987
! Ignores options on input, never sends any options.
! Should do fragmentation/reassembly

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

%constinteger ARP lives = 4

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

%externalintegerspec control

%externalinteger  slow IP address   = 0
%externalinteger  slow network      = 0
%externalinteger  ether IP address  = 0

%externalroutinespec ICMP inbound(%record(buffer fm)%name b)
%externalroutinespec TCP  inbound(%record(buffer fm)%name b)
%externalroutinespec UDP  inbound(%record(buffer fm)%name b)

%externalroutinespec slow ether outbound(%record(buffer fm)%name b)
%externalroutinespec      ether outbound(%record(buffer fm)%name b)

%externalrecord(INet statistics fm)%spec stats

!! %include "INet:Dump.Inc"

%routine dump IP header(%record(IP header fm) h, %string(31) text)
   printstring(text)
   printstring(" IP header version ");  write(h_IHL >> 4, 0)
   printstring(", size ");  write(h_IHL & 15, 0)
   printstring(", TOS ");  write(h_type of service, 0)
   printstring(", claimed length ");  write(h_total length, 0)
   newline
   printstring("ID ");  write(h_identification, 0)
   printstring(", fragment ");  write(h_fragment offset, 0)
   printstring(", TTL ");  write(h_TTL, 0)
   printstring(", prot ");  write(h_protocol, 0)
   newline
   printstring("Source ");  print inet address(h_source)
   printstring(", destination ");  print inet address(h_destination)
   newline
%end

%externalroutinespec find route(%integer target, %integername gate, via)

%externalintegerfn IP source for(%integer d)
   %result = slow IP address %if d & 16_FFFFFF00 = slow network
   %result = slow IP address %if ether IP address = 0;  ! No 10Meg, use slow
   %result = ether IP address
%end

%routine IP send(%record(buffer fm)%name b)
   %integer c, via
      !! printstring("IP send to ");  print INet address(b_IP peer);  newline
      %if control & trace IP out # 0 %start
         dump IP header(b_IP header, "Outbound")
      %finish
      b_IP header_TTL = b_IP header_TTL - 1
      release buffer(b) %and %return %if b_IP header_TTL = 0;  ! Expired
      stats_IP packets out = stats_IP packets out + 1
      stats_IP bytes out = stats_IP bytes out + b_IP bytes
      b_IP header_checksum = 0;  ! Meantime
!N!   net order short(b_IP header_total length)
!N!   ! fragment offset assumed 0
!N!   net order long(b_IP header_source)
!N!   net order long(b_IP header_destination)
      c = calculate checksum(b_IP header, 20);  ! Header checksum only
      b_IP header_checksum <- \c
      b_IP header_checksum <- 16_FFFF %if b_IP header_checksum = 0;  ! Use -0
      find route(b_IP peer, b_IP target, via)
      b_IP target = b_IP peer %if b_IP target = 0;  ! Direct
      !! %if b_IP target # b_IP peer %start
      !!    printstring("Route for ");  print INet address(b_IP peer)
      !!    printstring(" via ");  print INet address(b_IP target);  newline
      !! %finish
      %if b_IP target & 16_FFFFFF00 = slow network %start
         slow ether outbound(b)
      %else
         ether outbound(b)
      %finish
%end

%externalroutine IP inbound(%record(buffer fm)%name b)
   %integer checksum, header size, host, net, class
!L!   lights or A(IP light)
      !! printstring("Inbound IP packet, size ")
      !! write(b_bytes, 0);  newline
      !! dump(byteinteger(addr(b_IP header)), b_bytes)
      header size = (b_IP header_IHL & 15) * 4
      !! printstring("Header size is ");  write(header size, 0);  newline
      ! check the checksum.
      checksum = calculate checksum(b_IP header, header size)
      %if checksum # 0 %and checksum # 16_FFFF %start
         ! Dud checksum (beware ONES-complement)
!N!      net order long(b_IP header_source)
         !! pdate
         !! printstring("Dud IP checksum ");  write(checksum, 0)
         !! printstring(" from ");  print inet address(b_IP header_source)
         !! newline
         ! If the checksum was wrong then maybe the source address is wrong
         ! too, so we log it to the non-peer area.
       ! sp_IP checksum errors = sp_IP checksum errors + 1
         release buffer(b)
         -> out
      %finish
      ! Convert things from net order
!N!   net order short(b_IP header_total length)
!N!   net order short(b_IP header_fragment offset)
!N!   net order long(b_IP header_source)
!N!   net order long(b_IP header_destination)
      %if control & trace IP in # 0 %start
      !! printstring("IP from ");  print INet address(b_IP header_source)
      !! printstring(" for ");  print INet address(b_IP header_destination)
      !! printstring(" TTL ");  write(b_IP header_TTL, 0);  newline
         dump IP header(b_IP header, "Inbound")
      %finish
      %if b_IP header_source & 16_FF000000 = 0 %start
         ! A Sun booting -- throw it away
         !! printstring("Sun booting (probably)");  newline
         stats_IP other errors = stats_IP other errors + 1
         release buffer(b)
         -> out
      %finish
      b_IP peer = b_IP header_source
      %if b_IP header_fragment offset & 2_0011 1111 1111 1111 # 0 %start
         ! Fragmented -- dump it (*meantime*)
         !! pdate
         !! printstring("Fragmented IP packet received from ")
         !! print inet address(b_IP peer)
         !! printstring(" -- dumping");  newline
         stats_IP fragmented packets = stats_IP fragmented packets + 1
         stats_IP fragmented bytes = stats_IP fragmented bytes + b_bytes
         ! Drop it unconditionally, for now
         stats_IP fragments dropped = stats_IP fragments dropped + 1
         stats_IP fragmented bytes dropped = %c
            stats_IP fragmented bytes dropped + b_bytes
         release buffer(b)
         -> out
      %finish
    ! %if 46 <= b_IP header_total length # b_bytes %start
    !    ! Received length wrong
    !    !! pdate
    !    !! printstring("Length mismatch from ")
    !    !! print inet address(b_IP peer)
    !    !! printstring(": claimed ");  write(b_IP header_total length, 0)
    !    !! printstring(", actual ");  write(b_bytes, 0);  newline
    !    stats_IP dud lengths = stats_IP dud lengths + 1
    !    ! Carry on -- assume that the IP length is the correct one.
    !    ! Some systems, notably cheops, appear to be padding packets to
    !    ! an even boundary....
    ! %finish
      ! Validate the destination
      split INet address(b_IP header_destination, host, net, class)
      %if host = 0 %start
         ! Old-style ...0.0 broadcast
         !! printstring("Old-style IP broadcast from ")
         !! print inet address(b_IP peer)
         !! printstring(", protocol ");  write(b_IP header_protocol, 0)
         !! newline
         stats_IP old broadcasts = stats_IP old broadcasts + 1
      %else %if host = -1 %or host = new broadcast mask(class)
         ! New-style ...255.255 broadcast
         !! printstring("New-style IP broadcast from ")
         !! print inet address(b_IP peer)
         !! printstring(", protocol ");  write(b_IP header_protocol, 0)
         !! newline
         stats_IP new broadcasts = stats_IP new broadcasts + 1
      %else %if b_IP header_destination # ether IP address %c
            %and b_IP header_destination # slow IP address
         ! Not for us.  Forward it.
         !! pdate
         !! printstring("Forwarding IP: destination ")
         !! print inet address(b_IP header_destination)
         !! printstring(", source ");  print inet address(b_IP header_source)
         !! newline
         stats_IP packets routed = stats_IP packets routed + 1
         stats_IP bytes routed = stats_IP bytes routed + b_bytes
         b_IP peer = b_IP header_destination
         IP send(b)
         -> out
      %finish
      ! Now forward the validated header...
      b_header2 == record(addr(b_IP header) + header size)
      b_IP bytes = b_IP header_total length - header size
      stats_IP packets in = stats_IP packets in + 1
      stats_IP bytes in = stats_IP bytes in + b_IP bytes
      %if b_IP header_protocol = IP TCP protocol %start
         !! printstring("IP TCP");  newline
         TCP inbound(b)
      %else %if b_IP header_protocol = IP UDP protocol
         !! printstring("IP UDP");  newline
         UDP inbound(b)
      %else %if b_IP header_protocol = IP ICMP protocol
         !! printstring("IP ICMP");  newline
         ICMP inbound(b)
      %else
         ! Something unknown
         !! pdate
         !! printstring("Unknown protocol type ");  write(b_IP header_protocol, 0)
         !! printstring(" from ");  print inet address(b_IP peer)
         !! newline
         stats_IP dud protocols = stats_IP dud protocols + 1
         release buffer(b)
      %finish
out:
!L!   lights and A(\IP light)
%end

%externalroutine IP outbound(%record(buffer fm)%name b)
   %owninteger outbound identification = 0
!L!   lights or B(IP light)
      !! printstring("IP outbound for ")
      !! print inet address(b_IP peer);  newline
      !! dump(byteinteger(addr(b_header 2)), b_IP bytes)
      %if b_IP tag = 0 %start
         outbound identification = (outbound identification + 1) & 16_FFFF
         outbound identification = 1 %if outbound identification = 0
         b_IP tag = outbound identification
      %finish
      b_bytes = b_IP bytes + 20
      b_IP header == record(addr(b_header 2) - 20)
      b_IP header_IHL = 16_45
      b_IP header_type of service = 0
      b_IP header_total length = b_bytes
      b_IP header_identification <- b_IP tag
      b_IP header_fragment offset = 0
      b_IP header_TTL = 255
      b_IP header_protocol = b_protocol
      b_IP header_source = IP source for(b_IP peer)
      b_IP header_destination = b_IP peer
      !! dump IP header(b_IP header)
      ! No options
      IP send(b)
!L!   lights and B(\IP light)
%end

%end %of %file
