! UDP module for (Moose) INet process, GDMR, Feb/March 1987, Jan 1988.
! Ignore the checksum on input and don't generate one on output
! (4.2 BSD gets it wrong).

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

%externalrecord(queue fm)%spec IP outbound queue

%externalroutinespec UDP send to user(%record(buffer fm)%name b)
%externalroutinespec UDP open response(%integer unit, error, ra, rp, lp)
%externalroutinespec UDP claim response(%integer unit, error, allocated)
%externalroutinespec UDP enable(%integer unit)
%externalroutinespec UDP disable(%integer unit, error)
%externalpredicatespec UDP interpret user request(%record(buffer fm)%name b)

%externalintegerspec max UDP
%externalintegerspec our address
%externalintegerspec our network

%systemroutinespec phex(%integer x)

%externalrecord(UDP table fm)%namespec UDP table

%constinteger wild UDP port flag = 16_00010000
%constinteger UDP port mask      = 16_0000FFFF

%constinteger UDP privileged limit = 1024

%record(UDP entry fm)%map find UDP entry(%integer remote address, remote port,
                                         %integer local port)
   ! Try for an exact match if possible.  If not, try for a wild match.
   %record(UDP entry fm)%name t, possible == nil
   %integer i
      %for i = 1, 1, max UDP %cycle
         t == UDP table_UDP(i)
         !! printstring("Trying ");  print inet address(t_remote address)
         !! write(t_remote port, 1);  write(t_local port, 1);  newline
         %if t_local port = local port %start
            %result == t %if t_remote address = remote address %c
                         %and (t_remote port = remote port %c
                               %or t_remote port & wild UDP port flag # 0)
            possible == t %if t_remote address = 0
         %finish
      %repeat
      %result == possible
%end

%externalroutine UDP inbound(%record(buffer fm)%name b)
   %integer c, host, net, class
      %if b_UDP header_checksum = 0 %start
         !! printstring("No UDP checksum");  newline
         b_p_UDP no checksums = b_p_UDP no checksums + 1 %if b_p ## nil
      %else
         c = calculate pseudo checksum(b_UDP header, b_IP bytes,
                                       b_IP header_source, b_IP header_destination,
                                       IP UDP protocol, b_IP bytes)
         %if 0 # c # 16_FFFF %start
            !! printstring("UDP checksum error ");  phex(c);  newline
            c = calculate pseudo checksum(b_UDP header, b_IP bytes,
                                          b_IP header_source, b_IP header_destination,
                                          IP UDP protocol, b_UDP header_length)
            %if c = 0 %or c = 16_FFFF %start
               !! printstring("Bogus UDP checksum ");  phex(c);  newline
               b_p_UDP bogus checksums = b_p_UDP bogus checksums + 1 %if b_p ## nil
            %else
               !! printstring("UDP checksum error ");  phex(c);  newline
               b_p_UDP checksum errors = b_p_UDP checksum errors + 1 %if b_p ## nil
            %finish
         %finish
      %finish
!N!   net order short(b_UDP header_source)
!N!   net order short(b_UDP header_destination)
!N!   net order short(b_UDP header_length)
!N!   net order short(b_UDP header_checksum)
      %if b_UDP header_length # b_IP bytes %start
         ! Length wrong
         pdate
         printstring("UDP length error from ")
         print inet address(b_IP header_source)
         printstring(": UDP length ");  write(b_UDP header_length, 0)
         printstring(", IP length ");  write(b_IP bytes, 0);  newline
         b_p_UDP other errors = b_p_UDP other errors + 1 %if b_p ## nil
         release buffer(b)
         %return
      %finish
      b_data start == byteinteger(addr(b_UDP header) + 8)
      b_data bytes = b_UDP header_length - 8
      b_p_UDP packets in = b_p_UDP packets in + 1
      b_p_UDP bytes in = b_p_UDP bytes in + b_data bytes
      !! printstring("UDP inbound from ")
      !! print inet address(b_IP header_source)
      !! printstring(" port ");  write(b_UDP header_source, 0)
      !! printstring(" for port ");  write(b_UDP header_destination, 0)
      !! printstring(", data size ");  write(b_data bytes, 0)
      !! newline
      !! dump(b_data start, b_data bytes)
      b_UDP entry == find UDP entry(b_IP peer, b_UDP header_source,
                                    b_UDP header_destination)
      %if b_UDP entry == nil %start
         ! No-one there to receive it
         !! printstring("UDP: no recipient");  newline
         release buffer(b)
      %else
         ! Send it to the port's owner
         !! printstring("Sending UDP to ");  write(b_UDP entry_slot, 0);  newline
         %if b_UDP entry_remote address = 0 %start
            ! Wild context.  Should we convert it?
            split INet address(b_IP header_destination, host, net, class)
            %if 0 # host # new broadcast mask(class) %start
               ! Not a broadcast
               b_UDP entry_remote address = b_IP peer
               b_UDP entry_remote port = b_UDP header_source
               b_UDP entry_peer info == b_p
               !! printstring("Wild UDP context converted to ")
               !! print inet address(b_IP peer);  print symbol('.')
               !! write(b_UDP header_source, 0);  newline
            %finish
         %else %if b_UDP entry_remote port & wild UDP port flag # 0
            b_UDP entry_remote port = b_UDP header_source
            !! printstring("Wild remote port converted to ")
            !! write(b_UDP entry_remote port, 0);  newline
         %finish
         UDP send to user(b)
      %finish
%end

%externalroutine UDP outbound(%record(buffer fm)%name b)
   %owninteger UDP last privileged   = UDP privileged limit
   %owninteger UDP last unprivileged = UDP privileged limit
   %switch op(UDP first request : UDP last request)
   %recordformat connect fm(%integer remote address, remote port, local port)
   %record(connect fm)%name connect
   %record(peer info fm)%name p
   %integer i, which
      !! printstring("UDP outbound");  newline
      -> op(b_code) %if UDP interpret user request(b)
      !! printstring("Dud request from ");  write(b_UDP entry_slot, 0);  newline
      release buffer(b)
      %return

op(UDP define context request):
      connect == record(addr(b_data start))
      !! printstring("Define context from ");  write(b_UDP entry_slot, 0)
      !! printstring(": ");  print INet address(connect_remote address)
      !! space;  write(connect_remote port & UDP port mask, 0)
      !! space;  write(connect_local port, 0)
      !! printstring(", flags ");  phex(connect_remote port & 16_FFFF0000)
      !! newline
      %if b_privilege = 0 %and %c
            (connect_local port <= UDP privileged limit %or %c
             connect_remote address = 0) %start
         UDP open response(b_UDP entry_slot, privilege error, 0, 0, 0)
         release buffer(b)
         %return
      %finish
      %if connect_remote address = 0 %start
         ! Wild, map on to broadcast record
         p == find peer info(our network)
      %else
         ! Specific host
         p == find peer info(connect_remote address)
         p == new peer info(connect_remote address) %if p == nil
      %finish
      %if p == nil %start
         ! Still unknown
         UDP open response(b_UDP entry_slot, reset error, 0, 0, 0)
         release buffer(b)
         %return
      %finish
      b_UDP entry_remote address = connect_remote address
      b_UDP entry_remote port = connect_remote port
      b_UDP entry_local port = connect_local port
      b_UDP entry_peer info == p
op(UDP query context request):
      !! printstring("Responding with defined context");  newline
      UDP open response(b_UDP entry_slot, 0,
                        b_UDP entry_remote address, b_UDP entry_remote port,
                        b_UDP entry_local port)
      release buffer(b)
      %return

op(UDP forget context request):
      UDP disable(b_UDP entry_slot, 0)
      UDP enable(b_UDP entry_slot)
      b_UDP entry = 0
      release buffer(b)
      %return

op(UDP data request):
      !! printstring("Send to ");  print INet address(b_UDP entry_remote address)
      !! space;  write(b_UDP entry_remote port & UDP port mask, 0)
      !! space;  write(b_UDP entry_local port, 0);  newline
      %if b_UDP entry_local port = 0 %start
         ! Undefined address -- dump it silently
         ! (it's an unreliable protocol anyway!)
         release buffer(b)
         %return
      %finish
      b_IP peer = b_UDP entry_remote address
      b_IP peer = our network %if b_IP peer = 0;  ! Broadcast <<<<<<<<
      b_IP bytes = b_data bytes + 8
      b_protocol = IP UDP protocol
      b_p == b_UDP entry_peer info
      b_p_UDP packets out = b_p_UDP packets out + 1
      b_p_UDP bytes out = b_p_UDP bytes out + b_data bytes
      b_UDP header == record(addr(b_data start) - 8)
      b_UDP header_source = b_UDP entry_local port
      b_UDP header_destination = b_UDP entry_remote port & UDP port mask
      b_UDP header_length = b_IP bytes
      b_UDP header_checksum = 0;  ! i.e. none sent
      !! dump(byteinteger(addr(b_UDP header)), b_IP bytes)
!N!   net order short(b_UDP header_source)
!N!   net order short(b_UDP header_destination)
!N!   net order short(b_UDP header_length)
!N!   ! Checksum is zero, so same either way round
      enqueue buffer(b, IP outbound queue)
      %return

op(UDP claim request):
op(UDP claim priv request):
      !! printstring("Claim port request from ")
      !! write(b_UDP entry_slot, 0);  newline
try claim port again:
      %if b_code = UDP claim priv request %start
         %if b_privilege = 0 %start
            UDP claim response(b_UDP entry_slot, privilege error, 0)
            release buffer(b)
            %return
         %finish
         UDP last privileged = UDP last privileged - 1
         UDP last privileged = 1023 %if UDP last privileged < 768
         which = UDP last privileged
         !! printstring("Looking for privileged, trying ")
         !! write(which, 0);  newline
      %else
         UDP last unprivileged = UDP last unprivileged + 1
         UDP last unprivileged = 1025 %if UDP last unprivileged > 2048
         which = UDP last unprivileged
         !! printstring("Looking for unprivileged, trying ")
         !! write(which, 0);  newline
      %finish
      %for i = 1, 1, max UDP %cycle
         -> try claim port again %if UDP table_UDP(i)_local port = which
      %repeat
      ! If we fall through here then the port was free...
      !! printstring("Allocated ");  write(which, 0);  newline
      UDP claim response(b_UDP entry_slot, 0, which)
      release buffer(b)
%end

%end %of %file
