! 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"

%constinteger ARP lives = 4

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

%externalintegerspec our network
%externalintegerspec our address

%externalintegerspec max TCP
%externalintegerspec max UDP

%externalrecord(queue fm)%spec TCP    inbound queue
%externalrecord(queue fm)%spec UDP    inbound queue
%externalrecord(queue fm)%spec ICMP   inbound queue
%externalrecord(queue fm)%spec ether outbound queue

%externalrecord(non peer info fm)%namespec sp

!! %include "INet:Dump.Inc"

!! %routine dump IP header(%record(IP header fm) h)
!!    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

%externalroutine IP inbound(%record(buffer fm)%name b)
   %integer checksum, header size, host, net, class
      !! 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)
         %return
      %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)
      !! dump IP header(b_IP header)
      %if b_IP header_source & 16_FF000000 = 0 %start
         ! A Sun booting -- throw it away
         !! printstring("Sun booting (probably)");  newline
         release buffer(b)
         %return
      %finish
      b_IP peer = b_IP header_source
      b_p == find peer info(b_IP peer)
      b_p == new peer info(b_IP peer) %if b_p == nil
      release buffer(b) %and %return %if b_p == nil;  ! Drop if (still) unknown
      %if b_IP header_fragment offset & 2_0011 1111 1111 1111 # 0 %start
         ! Fragmented -- dump it
         !! pdate
         !! printstring("Fragmented IP packet received from ")
         !! print inet address(b_IP peer)
         !! printstring(" -- dumping");  newline
         b_p_IP fragmented packets = b_p_IP fragmented packets + 1
         release buffer(b)
         %return
      %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
         b_p_IP dud lengths = b_p_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
         b_p_IP old broadcasts = b_p_IP old broadcasts + 1
      %else %if 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
         b_p_IP new broadcasts = b_p_IP new broadcasts + 1
      %else %if b_IP header_destination # our address
         ! Not for us (how did we come to receive it??)
         !! pdate
         !! printstring("Dud IP destination ")
         !! print inet address(b_IP header_destination)
         !! printstring(", ");  print ether address(b_ether addrs_destination)
         !! printstring(" from ");  print inet address(b_IP peer)
         !! printstring(", ");  print ether address(b_ether addrs_source)
         !! newline
         b_p_IP other errors = b_p_IP other errors + 1
         release buffer(b)
         %return
      %finish
      ! Can we find the ether address from this packet?
      %if net = our network %and b_p_flags & peer ether address known = 0 %start
         b_p_ether address = b_ether addrs_source
         b_p_flags = b_p_flags ! peer ether address known
      %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
      b_p_IP packets in = b_p_IP packets in + 1
      b_p_IP bytes in = b_p_IP bytes in + b_IP bytes
      %if b_IP header_protocol = IP TCP protocol %start
         !! printstring("IP TCP");  newline
         %if max TCP = 0 %then release buffer(b) %c
                         %else enqueue buffer(b, TCP inbound queue)
      %else %if b_IP header_protocol = IP UDP protocol
         !! printstring("IP UDP");  newline
         %if max UDP = 0 %then release buffer(b) %c
                         %else enqueue buffer(b, UDP inbound queue)
      %else %if b_IP header_protocol = IP ICMP protocol
         !! printstring("IP ICMP");  newline
         enqueue buffer(b, ICMP inbound queue)
      %else
         ! Something unknown
         !! pdate
         !! printstring("Unknown protocol type ");  write(b_IP header_protocol, 0)
         !! printstring(" from ");  print inet address(b_IP peer)
         !! newline
         b_p_IP dud protocols = b_p_IP dud protocols + 1
         release buffer(b)
      %finish
%end

%externalroutine IP outbound(%record(buffer fm)%name b)
   %ownshort outbound identification = 0
   %integer c
      !! printstring("IP outbound for ")
      !! print inet address(b_IP peer);  newline
      !! dump(byteinteger(addr(b_header 2)), b_IP bytes)
      b_p_IP packets out = b_p_IP packets out + 1
      b_p_IP bytes out = b_p_IP bytes out + b_IP bytes
      outbound identification <- outbound identification + 1
      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 = outbound identification
      b_IP header_fragment offset = 0
      b_IP header_TTL = 255
      b_IP header_protocol = b_protocol
      b_IP header_checksum = 0;  ! Meantime
      b_IP header_source = our address
      b_IP header_destination = b_IP peer
      !! dump IP header(b_IP header)
      ! No options
!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
      b_ARP lives = ARP lives
      enqueue buffer(b, ether outbound queue)
%end

%end %of %file
