To: perot@pallas.amp.uni-hannover.de
Subject: Re: [3] TCP patch for STiK 2                                                    


Hi Peter!

I thought I'd better add a little foreword today, since I have been
writing on this letter so long it feels more like having written a novel.
Further below in the text you will find a complete model for full IP
masquerading which I have constructed today.  This should be useful
regardless of how we chose to solve the TCP implementation.
It should work fine for both UDP and TCP, but not for 'pure' IP.
ICMP appears to be based on 'pure' IP  too, is that a problem...?
Do we ever need ICMP over the modem...?

It does not seem so from the RFC, but I still don't know for sure.



On Wed, 19 Feb 1997 09:50:00 Peter Rottengatter <perot wrote: 
>
>On Tue, 18 Feb 1997, Ronald Andersson wrote:
>
----- snip ----- re: proposal for STiK 2 use of STiK 1 TCP
>> 
>> 	ST1K does the modem-Internet networking
>> 	ST2K does the local Intranet networking
>
>Doesn't this mean that the Intranet must live without TCP for this kludge ?

Yep, but I didn't think of that originally, only a bit later as I said below.

>If I'm not mistaken here, then there is little point in doing 
>Intranetworking, as without TCP it is no fun.

Right, but the full method, which we separately deduced, will do the job.
The model for that is different.

    TCPSTX  interfaces to all TCP/IP clients on the same machine
      ''    passes client calls to ST1K
     ST1K   regards TCPSTX as a single client with many channels
      ''    passes TCP/IP packets to/from a dummy port
     ST2K   now does ALL networking treating the dummy port as any other !!!

There is a lot more to say about this model, but I'll do it further below.


----- snip ----- re: patching the ST1K cookie
>>
>> If not, it is not really that hard to find it anyway.  Immediate byte arguments
>> are usually compiled to $00xx with xx being the byte constant, so we can check
>> for that format too and shortly find all points to patch anyway.
>
>Aha. My knowledge of machine language never comprised such relations 
>between the various codes ...

On a 68000 CPU it isn't necessarily true, because the 'unused' byte is allowed
to have any value, but I'm unsure if that is allowed on newer CPU's.  A zero
is in any case the most logical value for an unused byte, though I suspect
some assemblers may generate $FF there when the byte value is negative.
But all 'our' bytes are positive, so this does not matter.


----- snip ----- re: TCP_STX

>> ST1K handles IP with Internet ONLY.  ST2K handles all local stuff, including
>> all contact with clients.  This means that any stuff from other machines to
>> Internet needs to be sent through ST1K, which enforces masquerading since it
>> only knows a single IP.  ST2K however knows what IP the real client of that
>> connection has in the local net, and can translate IP headers accordingly.
>
>Aha. This way all machines can be connected to the outside world, but 
>communication within Intranet via TCP is not possible, as, for instance, 
>the machine with the modem can only TCP towards the ISP, but not towards 
>another local machine ?

Right, and that is one of the reasons my simple approach was really worthless.
Oh, except for one thing...  I might never have written that mail if I had not
thought of it, since I then still saw the 'full' method as too complex.
So I guess the 'simple' idea filled a temporary purpose anyway.


>> But I've realized that the simple method wouldn't allow clients on the other
>> local machines anyway, because they would need a TCP STX too, and the one
>> used for the simple method only works on the machine with the modem...!
>> The complex method does allow that, since all machines then run the same STX,
>> and ST2K handles all IP traffic including that to the modem.
>
>Well, ST1K could be used there too in much the same manner, but only 
>giving connectivity towards Internet, so flexibility is limited. Funny 
>situation indeed, although traffic flows through the machine with the 
>modem, this machine cannot be reached via TCP from any other local 
>machine.

Ah, but this is where the difference of the 'full' method comes in,
because it will allow full TCP/IP between all ports.


>> >I guess this describes how to link a module on top of ST1K ?
>> 
>> Yes, exactly.
>
>Well, without the masquerading stuff, I guess I could set this up within 
>an hour. Question is : Do we really want it ?

NO!  We want the full method.
Well, I do anyway, and I think you do too.
That or a proper TCP STX.


>I thought about another way. Original STiK was based on KA9Q, which is 
>publicly available. Without having seen the source I can't tell how much 
>work it'd be to transform that into a TCP.STX. Maybe it's even quite easy ?
>Thus I decided to look for that package, and scrutinize it. As the ST1K 
>TCP is probably still quite similar to KA9Q TCP, this is about the same 
>as porting ST1K to TCP.STX. Difference to Dan is that my knowledge on it 
>is less, but my motivation is much higher ;-)

True, but the tricky bit will be to make the API match.
KA9Q may not be geared for handling data through such an API.
This mainly depends on whether Steve adapted to the code he found,
or adapted it to fit the API he wanted. I suspect the latter.

Also, I know Dan has spoken of TCP and IP being mixed together in STiK,
and that is likely true in KA9Q as well then, which will make it more
difficult to extract the TCP stuff to a module.  I don't mean to discourage
you, just to warn you about things that might make it more demanding.


----- snip ----- re: more on the 'full' TCP/IP patch method

>> This was my intention too, for both versions of my idea, though the STX will
>> also need to remember the local IP numbers of the various connections, since
>> ST1K has no support for this.  Note that this will be needed regardless of
>> any actual masquerading, since all outgoing packets will be given the source
>> IP which ST1K uses.  I do not know about incoming packets.  It is possible
>> that ST1K assumes they are all valid, ignoring the IP, but I doubt it.
>
>Whatever, it can be handled by the code that links to the IP end of ST1K. 
>I do not think on the TCP end local IP addresses must be remembered. It 
>is fine for the ST2K core if the TCP only uses one address, it is just 
>slightly less efficient.

You're right, ST1K should know which client should have what data from the TCP
channel numbers, and TCPSTX should pass these unaltered, so it is no problem.
I made it seem overcomplicated before.  A lot of things can also be simplified
by actually treating the dummy port at the IP end of ST1K as a real port.
It should have its own entry in the CPX and in ROUTE.TAB just like the others.
The IP of that port is of course the one that should be given to ST1K at init.

>> If not, then translation of destination IP's to the one used by ST1K is also
>> needed. The real local IP's should be remembered per channel however, so that
>> the correct stuff reaches the right client on the proper machine  (complex ?).
>> Again note that this has nothing to do with masquerading, but only with the
>> single-IP  nature of ST1K.
>
>As I said, it probably doesn't hurt anyway.

It should not be necessary, because the very fact that the packets reach the
dummy port means that they already have the correct IP.  Otherwise they would
be routed elsewhere.  So how did that IP get put there then...?

Well, I really think we should have IP masquerading for whatever port is used
as a connection to Internet, and since it is the router which sends packets
to the ports it is logical that it should handle this.  Note that this is
an improvement independent of the patch, necessary for full Internet access
from the Intranet machines even later, when we do have a proper TCP STX.

			----------------------
			MASQUERADE MODEL START
			----------------------

This could be done by having an additional flag for each port, which is set
only if that port is to be used for contact to Internet (or alien Intranet).
I'll call this flag "xnet_f" below (external network flag, that is).

Now we reach an area were my lacking network knowledge begins to matter.!!!
I think I'll have to use a series of attempts to formulate this.
Each new attempt will be based on conclusions from the one preceding it.
I hope there will not have to be too many...

			----- ATTEMPT 1 -----

For each outgoing packet routed to a port, the router tests the port's xnet_f
and unless it is set it just passes the packet as usual.  Otherwise, we must
masquerade this outgoing packet by storing the source IP in a table for this
port for later reference, and placing the port IP in the source IP field.

BUT,  the purpose for doing this is that any responses from outside to this
package should be sent to the correct local IP, even though they will all be
addressed to the IP of the same port.  But IP protocol does not have any
bidirectional channels, just simple datagrams, so for pure IP masquerading
doesn't work.  Somehow we must associate the outgoing packet with a channel
of some kind, such as the TCP/IP channel number (not present in the packets)
or something like that.  Also, it must be something present in outgoing
packets, guaranteed to also be present in incoming packets.

For TCP the only possible identifier is the TCP port value, which must be
patched anyway since we may have several TCP units sending stuff, and though
each will use unique ports internally, two of them may well have used the same.
That does not matter within the Intranet, but in passing through the ISP
the packets get the same source IP so the port values _must_ be patched.

From a very short glance at the UDP RFC I notice that it too allows arbitrary
port field values, so this may be a good solution for it too.  Perhaps other
protocols do the same too.  If so, this is definitely the method to use.

Please note that there would be some confusing parts below, because both
communication ports and these protocol header port fields are involved.
To avoid that I will use the term cport for communication ports here, and the
term pport for protocol port values.


			----- ATTEMPT 2 -----

For each outgoing packet routed to a cport, the router tests the cport's xnet_f
and unless it is set it just passes the packet as usual.  Otherwise, we must
masquerade this outgoing packet so we move the source IP to variable temp_IP
and patch the cport's IP into the source IP field.  Next we check if the
protocol is one with pports, and if not we just pass the packet to the cport
and _HOPE_ that no one is waiting for a response because we can't deliver it.
(Well perhaps we can, but only in systems with simple traffic. more later)

When the protocol has pports, we move the source pport value to a variable
temp_pp, which with temp_IP uniquely defines the local sender.  Actually
it may be better to assume that the two variables are part of a structure.
And for reasons that will become obvious we need a third element as well.
At this point in the algorithm this element/variable temp_time should receive
a copy of the current clock value (Internet time from timer C interrupt)

C declarations for this stuff could look like below

typedef struct masq_id{long masq_time; long masq_ip; short masq_pp} MASQ_ID;
MASQ_ID temp_id;
#define temp_time (temp_id.masq_time)
#define temp_ip (temp_id.masq_ip)
#define temp_pp (temp_id.masq_pp

We are going to have to store the temp_id of each NEW channel, for later
responses, in some array reserved for the cport and patch the source pport
field with a value based on the array index (if legal the index itself).
The array itself can be reserved as part of STiK's init, since there should
be one for each cport that can connect to an alien net (unrelated IPs).
More than one should be allowed, but it would be wasteful to reserve it for
all cports.


First we must search the array to see if the temp_id is already in there,
meaning of course that some structure has matching masq_ip and masq_pp,
if so we update the time element of the found structure, patch the source
pport based on the index and pass the packet to the cport, which completes
the handling of this packet.

When the temp_id is not in the array, we simply search the array for the entry
with the oldest time element, assume that this represents a lapsed channel,
and steal the entry by storing the temp_id structure in it.  Then we patch
the source pport based on that index, pass the packet to the cport, and have
again completed handling of a packet.

Note that these arrays should be cleared completely initially as well as
at modem hangup.  This ensures that even the oldest channel's entry will
not be 'stolen' until all free entries have been taken, since a 'time' of
zeroes is always 'oldest.'

So much for outgoing, now to handle incoming packets.

			--------------------

For each incoming packet from a cport the router tests the cport's xnet_f
and unless it is set continues with routing directly.  Otherwise the packet
needs to be unmasqued for local use.  This is done by the aid of the same
array used for masquerading outgoing packets.

First we check whether the protocol has pports, and if so we calculate an
index based on the destination pport of the packet (preferably no change).
Then we repatch the destination pport with the found masq_pp and likewise
patch the destination IP with the found masq_IP.  No changes are made to
the array, and we can proceed with routing as for normal packets.

If the packet protocol has no pports, then we simply locate the youngest
entry in the table, and repatch the packets destination pport and IP from
this entry.  Note that this should always work with simple TCP/IP on a
system that has no Intranet, and often would work elsewhere too, but not
when many Intranet users are communicating to Internet over the same port.

But since both UDP and TCP do have pports, this may never matter (does it ?)

I noticed that zero is a reserved value for 'undefined' source pport  in UDP,
so I guess we'll have to add 1 to the array indexes to get masquerade pport.

Do you know of any other reserved values ?


			--------------------
			MASQUERADE MODEL END
			--------------------


>> >The IP end, however, appears as a serial interface. We need a way to intercept
>> >it, extracting the (TCP) data from the IP datagram, and send it away using the
>> >ST2K core IP calls. The same way data coming in from the ST2K core for TCP is
>> >padded with a dummy IP header, and fed into the IP end of ST1K.
>> 
>> Yep, and for this I envisioned having to patch a dummy driver into BCONMAP etc.
>
>That was my idea too. We just need to make sure this does not unlink the 
>SERIAL.STX too ...
>
>
>> >This way, the final TCP.STX just replaces the dummy STX and the ST1K, 
>> >without changing anything else in the ST2K installation. Even better, all 
>> >flexibility of ST2K prevails, and you can network several machines as 
>> >before. However, the hard part is to gain proper access to the serial 
>> >interface of ST1K. Must be done by intercepting some TOS features ?
>> 
>> It could be done that way, but could have secondary effects (risky).
>> I prefer patching a dummy driver into the BCONMAP structure etc.  I won't go
>> into detail on this, because I think you know this stuff better than I do.
>> This way the device lib of ST1K will call our 'driver' routines directly,
>> to read/write data just as they would call corresponding routines of normal
>> serial drivers.  The only problem with this is that at this level the data
>> may not be packet oriented, or is it ?
>
>Aha, you mean inventing a virtual `Serial 42' (just in case there are 
>machines with `Serial 3', `Serial 4' ... already), that is used by ST1K, 
>but must not be used by SERIAL.STX. Good idea actually.

I think it solves a lot of problems.



>You're right, the data is stream oriented there, but that is not a major 
>problem. In SERIAL.STX exists some code that transforms and does the 
>SLIP decoding at the same time, it can be reused here.


Great!  And here I've been worrying about that as a major problem :-/


>I think we exactly agree now. Just let me see first if the port of KA9Q 
>is comparable in effort. If not (I guess it indeed is more effort) then 
>we can start on this scheme you suggested.

Fine, but I now think that the masquerade method I constructed should be
evaluated by you too, and if found valid implemented ASAP, because without
it we can't send any valid TCP traffic to the internet using this scheme.

Also, since it is needed with either TCP method, it should have priority.


>> This way we have a reserved device code, so the patched Fopen can ignore all
>> normal calls and leave them unaffected, but register the handle returned to
>> ST1K when the special device is active (Bconmap).  The patched Fread/Fwrite
>> etc simply leave all calls using other handles unaffected to the original TOS
>> routines, but transfer data for the special handle to/from STX packet buffers.  Bis
>> BIOS functions would also know when to leave calls unaffected by the active
>> device, so I see no problems arising from this.
>> 
>> Do we know at what level ST1K handles these data transfers ?
>> Is it enough to patch GEMDOS or must BIOS too be used ?
>
>I'm afraid, we need to patch both. The DEVICE-LIB uses the block calls 
>(Fopen/Fread/Fwrite/Fclose) if drive U: is available, and BIOS otherwise. 
>BIOS alone is not enough, as the GEMDOS accesses the port without 
>consulting BIOS. Hang on, this might be true only for the system ports. 
>Maybe the DRVIN from HSMODEM installs a port automagically if a BIOS 
>driver exists. This might be worth to be checked, i.e. a driver patched 
>into the BCONMAP in the AUTO folder prior to HSMODEM.

Yes, try it by all means.  It should not be a big problem though, even if
we do have to patch both.  The problem I worried most about was how to
transfer a bios character stream to packets, and since you have that solved
already the rest should be fairly routine.

-------------------------------------------------------------------------
Regards:  Ronald Andersson                     mailto:dlanor@oden.se
                                               http://www.oden.se/~dlanor
-------------------------------------------------------------------------
