! UDP module for (Moose) INet process, GDMR, Feb/March 1987, Jan 1988.
! TX checksums now turned on ({C}/!C!)

!%option "-NonStandard-NoCheck-Nodiag-NoLine"
%option "-NonStandard-NoCheck"

%include "INet:Control.Inc"
%externalintegerspec control

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

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

%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)

%externalintegerfnspec IP source for(%integer d)

%externalintegerspec max UDP

%systemroutinespec phex(%integer x)

%externalrecord(INet statistics fm)%spec stats

%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
!L!   lights or A(UDP light)
      %if b_UDP header_checksum = 0 %start
         !! printstring("No UDP checksum");  newline
         stats_UDP no checksums = stats_UDP no checksums + 1
      %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
               stats_UDP bogus checksums = stats_UDP bogus checksums + 1
            %else
               !! printstring("UDP checksum error ");  phex(c);  newline
               stats_UDP checksum errors = stats_UDP checksum errors + 1
            %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
         stats_UDP other errors = stats_UDP other errors + 1
         release buffer(b)
         -> out
      %finish
      b_data start == byteinteger(addr(b_UDP header) + 8)
      b_data bytes = b_UDP header_length - 8
      stats_UDP packets in = stats_UDP packets in + 1
      stats_UDP bytes in = stats_UDP bytes in + b_data bytes
      %if control & trace UDP in # 0 %start
         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)
      %finish
      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
         %if control & trace orphan UDP # 0 %start
            printstring("UDP ")
            printstring("broadcast ") %if b_flags & broadcast flag # 0
            print INet address(b_IP header_source)
            print symbol('.');  write(b_UDP header_source, 0)
            print symbol('/');  write(b_UDP header_destination, 0)
            printstring(" dropped -- no recipient")
            newline
            dump(b_data start, b_data bytes)
         %finish
         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
               !! 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
out:
!L!   lights and A(\UDP light)
%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
   %integer i, which, c
!L!   lights or B(UDP light)
      !! 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)
      -> out

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)
         -> out
      %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
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)
      -> out

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)
      -> out

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 %or b_UDP entry_remote port = 0 %start
         ! Undefined address -- dump it silently
         ! (it's an unreliable protocol anyway!)
         release buffer(b)
         -> out
      %finish
      b_IP peer = b_UDP entry_remote address
      b_IP peer = -1 %if b_IP peer = 0;  ! Broadcast <<<<<<<<
      b_IP bytes = b_data bytes + 8
      b_protocol = IP UDP protocol
      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;  ! Initially
      !! 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)
{C}   c = calculate pseudo checksum(b_UDP header, b_IP bytes,
{C}                                 IP source for(b_IP peer), b_IP peer,
{C}                                 IP UDP protocol, b_IP bytes)
{C}   b_UDP header_checksum <- \c
{C}   b_UDP header_checksum <- 16_FFFF %if b_UDP header_checksum = 0
      IP outbound(b)
      -> out

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)
            -> out
         %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)

out:
!L!   lights and B(\UDP light)
%end

%end %of %file
