; NETWORK.TXT -- A NetWare and IPX primer for IPXLIB version 0.20
; by Allen Brunson  06/01/94


******************************************************************************
*                                                                            *
***  Introduction to NetWare and IPX                                       ***
*                                                                            *
******************************************************************************

If you're interested in using IPXLIB, chances are good that you're not
particularly keen on learning all the ins and outs of NetWare or the IPX
protocol; you want the library to insulate you from all that.  It will, as
much as is possible, but to use the library effectively, you must have at
least a working knowledge of NetWare networks and IPX.

IPX stands for Internetwork Packet eXchange.  It is a protocol developed by
Novell for use in their networks and is based on the XNS protocol developed
by Xerox.  There is another related Novell protocol, SPX, Sequenced Packet
eXchange.  SPX uses IPX as its transport protocol and guarantees delivery,
but it is slower than IPX.  IPXLIB makes no use of SPX whatever.


The History of IPX Drivers and Shells
-------------------------------------

In very old versions of NetWare, up to Advanced NetWare v2.0a, the TSR that
had to be loaded on client PCs was called ANETx.COM, where x was 2 for DOS
version 2.x, 3 for version 3.x, and so on.  You needed a separate version
of ANETx.COM for each different type of NIC (Network Interface Card).  It
contained both the IPX driver and the shell; this one TSR was all that was
needed to communicate with a NetWare server or with other PCs via IPX.

When Advanced NetWare 2.1 was released, the IPX driver and the shell were
broken up into two parts.  The first part was called IPX.COM.  The end-user
was given an object file and a bunch of drivers and the installation program
linked the final IPX.COM for you that was specific for your type of NIC.
NETx.COM, where x again represents the DOS version, was provided already
linked and stayed the same regardless of what type of NIC you had.

The shell continued to evolve.  The various NETx.COMs were replaced with
only one, NETX.COM, that worked with all supported DOS versions.  There was
also EMSNETX.COM and XMSNETX.COM, which make use of EMS and XMS memory,
respectively.  IPX.COM stayed the same.

A few years ago Novell went to an entirely new set of TSRs, called the ODI
drivers (for Open Datalink Interface).  They allow the user to accommodate
more than one protocol on a single network card.  The user first loads
LSL.COM, then a .COM file that has the name of the network card, like for
instance NE2000.COM, then IPXODI.COM, and finally NETX.COM.

The final step in this evolution is that NETX.COM (or, in later versions,
NETX.EXE) was replaced by VLM.EXE.  This is a complicated beast that loads
one or more .VLMs, or Virtual Load Modules.  VLM has only been around for
a year or so and has proven to be somewhat buggy in some people's
environments; therefore it hasn't completely caught on yet.  It no doubt
will eventually, due to the fact that Novell no longer supports NETX.


Networks, Nodes, and Sockets
----------------------------

The concept behind NetWare networks is fairly simple.  All PCs that will
participate in the network have NICs installed; all the NICs are cabled
together -- the method and type of cabling depends on the type of NICs -- so
they can send data to each other.  One PC is set up to act as a "server,"
meaning that it shares its resources, such as hard disk space, printers,
and installed applications, with other PCs.  Users at other PCs "log in" to
the file server to access its resources.

NetWare LANs (Local Area Networks) can be incredibly huge and complicated.
For this reason, they can be broken up into network "segments."  Novell calls
each of these segments a "network."  (Yes, this is confusing use of
terminology, but you'll get used to it.)  It is common for medium- to
large-sized NetWare networks to be broken up into two or more segments.  This
has the effect of isolating potential problems to only one part of the
network as a whole, and for some network types (Ethernet, for example),
having several smaller segments can increase network speed.

As an example, let's consider a hypothetical medium-sized NetWare network
with one NetWare file server that's broken up into three thin-Ethernet
segments.  Each segment is just a long strand of coax cable running from one
PC to the next; let's say there are ten PCs on each segment.  The server has
three Ethernet cards installed, each of them connected to one of the
segments.  So we have one file server, 30 client PCs, and three Ethernet
segments; the whole ball of wax is referred to as the "internetwork."

When the person who installs the network sets up the server, she assigns
an 8-digit hexadecimal number to each network segment to distinguish it from
all others.  In any given internetwork, each segment number must be unique.
Let's say she assigns the numbers 1, 2, and 3 to the segments (a common
occurrence).

Each PC on a network segment must have a "node number," which is 12
hexadecimal digits long.  All PCs on a segment must have a unique node number
(otherwise, there would be no way to tell them apart).  How this number is
derived depends on the type of NICs.  For instance, ARCnet cards can only
have one of 255 node numbers, 1 to 255, which are usually settable with DIP
switches on the board.  Ethernet cards have a unique (and usually quite
large) number burned into a PROM; manufacturers must request a block of
numbers to use from the IEEE, which ensures that all Ethernet cards' numbers
will be unique.  Two PCs on an internetwork can have the same node number
if they are on different network segments.

So, any "node" (any PC or any other device that communicates on the network,
such as a network printer) can be uniquely identified by its network number
and node number combined.  The entire address is often represented as
xxxxxxxx:yyyyyyyyyyyy, where the x's are the network number and the y's are
the node number.  Many of NetWare's own utilities use a convention similar
to this to display node addresses; for instance, try typing USERLIST /A at
a DOS prompt while logged in to a NetWare server, which will show all
logged-in users along with the network and node of their PCs.

Next comes the concept of a "socket."  There can be more than one process on
a node that is participating in IPX communications at any one time.
Therefore, each process must use a "socket" for communication to uniquely
distinguish it from all other processes using IPX.  A socket is identified
by a four-digit hex number.  A program can open as many sockets as it likes.
These can be likened to channels on a two-way radio: A bunch of people can
be talking on the same frequency, and by using more than one radio, you can
communicate on several frequencies with several groups of people at one time.

So, to form the full address of one specific process running on a PC, you
need three numbers: network, node, and socket.  In the remainder of this
document, I will sometimes use three numbers together, separated by
colons, which represent the network number, node number, and socket number.
Two numbers and a colon will be the network and node numbers.


Using Sockets, And The Problems That Arise
------------------------------------------

To send IPX data packets from one PC to another, an agreed-upon socket must
be used.  As an example, let's say that a program on PC A wants to send
packets to a program on PC B, address 2h:D0h.  The program on PC B opens up
socket number 5700h and listens for packets on it.  PC A sends packets to
2h:D0h:5701h.  PC B will never get them, because it's listening on socket
5700h, and PC A sent the packets to socket 5701h.  For communication to
occur, all three numbers must be exactly right.

One socket can be used for both sending and receiving, or a different
socket can be used for each purpose.  One PC can send packets to another
even if they're not using the same socket numbers for sending and receiving.
An example: PC A, address 01h:20h, opens up socket 6000h and listens for
packets on it.  PC B opens up socket 6500h to send packets on, and sends the
packets to address 01h:20h:6000h.  PC A will get the packets just fine, even
though they were sent from a different socket number than they were received
on.  All that's necessary is that the sender knows the right socket number
to send to.

Using multiple sockets adds flexibility, but also a lot of complication, so
the routines in IPXLIB don't take advantage of this feature.  Only one socket
(with only one socket number) is used for both sending and receiving.  If
you really know what you're doing, IPXLIB can be used to send and receive
to other socket numbers, but this is a topic that is outside the scope of
this document.

This stickiest issue having to do with sockets is this: a socket number MUST
be agreed upon by the sender and receiver before ANY communication can occur.
The network itself can't be used to pass a socket number, because until a
socket number is determined, no communication can take place.

To get around this problem, most IPX programmers hard-code a specific socket
number into their programs.  But what if another program that uses IPX
communications is used on the same network, and the developer of that
program decided to hard-code the exact same socket number that you did?
Bedlam might well result; your program might get packets meant for the other
program, and vice versa.  If both programs are run at once on the same
machine (if one program is a TSR, for instance),  the two will bump heads
severely; the second program trying to open up the (already open) socket
will get an error from the IPX driver.  If it chooses to ignore this error,
then packets will CERTAINLY end up in the wrong place.  As the two programs
are probably not using packets of the same size, some packets might be
lost.  You'd have a bloody mess that would probably lead to a crash.


Choosing a Socket Number
------------------------

Fortunately, there are some reasonable guidelines for choosing and using a
socket number for your program.

NetWare's IPX protocol is based on a protocol developed by Xerox called
XNS.  Xerox has laid out the blueprint for how socket numbers should be
used, and Novell follows those guidelines.  Here's the officially assigned
socket numbers:

    1          Routing Information Packet
    2          Echo Protocol Packet
    3          Error Handler Packet
   20h -  3Fh  Experimental
    1h - BB8h  Registered with Xerox
  BB9h -       Dynamically assignable

Xerox has assigned Novell a set of sockets for use in NetWare:

  451h         File Service Packet
  452h         Service Advertising Packet
  453h         Routing Information Packet
  455h         NetBIOS packet
  456h         Diagnostic packet

For instance, NetWare file servers listen for file service requests on
socket number 451h.

According to Novell's documentation, "dynamic" socket numbers, or those
that are free for anybody to use, start at 4000h and go up to (but not
including) 8000h.  All socket numbers 8000h and above are reserved by
Novell.  You can ask Novell to officially assign you socket numbers in this
range for use by your application, so that they are "yours" and nobody else
can use them.  (Not without breaking the rules, at least).

My suggestion is that you "hard-code" a socket number into your program in
the range 4000h to 7FFFh.  Pick something oddball, like 6A15h; no nice even
numbers with a lot of zeros, which other programmers are likely to have used.
If your program has a configuration file, store the socket number in there;
that way your tech support people will have something to try if the socket
number you picked conflicts with one already in use at the customer's site.

Another issue.  What if two separate groups of people want to use your
program on the same network?  Let's say your program is a flight simulator
game that allows up to four people to play at once, and two groups of
four people would like to play it at the same time.  Everybody will bump
heads; how will the game know who it should allow in and who it shouldn't?
The solution would be to make the socket number a settable parameter; the
first group of people would use one socket while the second group would use
another.

If you write a different program that uses IPX, it stands to reason that
some of the people that bought your older program might get the new one as
well, and so your old and new programs could also have a socket conflict.
Each new program should have a different settable range of socket numbers.

Finally, given the possibility of a worst-case scenario where a socket number
conflict causes your program to receive unwanted packets from a "foreign"
program, it would be wise to mark your packets somehow so that, when they
arrive, you know they're really yours.  I would suggest setting a word-sized
"signature" as the first two data bytes of each and every packet sent.  Then,
when receiving packets, if the first word ISN'T your signature, then you
should simply discard the packet.


Immediate Addresses
-------------------

As if network numbers, node numbers, and socket numbers weren't enough, there
is one more number that needs to be known about a node before you can
communicate with it: the "immediate address."

In order to keep IPX small, lean, and mean, quite a bit of the complexity of
communication is left with the programmer.  That there even has to be such
a thing as an "immediate address" is a perfect example.

Let's go back to our hypothetical network with one server and three Ethernet
segments.  Let's say that PC A, on segment 1, wants to send a packet to PC
B, on segment 2.  PC A must know the network number and node number of PC B,
as well as what socket it is listening on.  It must ALSO know the
"immediate address" to send the packet to.

Therein lies the rub: If you want to send a packet to a PC that's on a
different network segment, you DON'T send it directly to that PC; you must
send the packet to a "bridge."  Then the bridge might send the packet to
another bridge, and so on, until the last bridge in the chain sends it
to the destination PC.

Before we go any farther, a note about the terms "bridge" and "router."  For
many years, Novell called a "bridge" that thing which almost all other
network vendors called a "router," and vice versa.  Then, when NetWare v2.2
came out, they changed their minds and went with the terminology everybody
else was using.  To simplify matters, I only use the term "bridge" for the
remainder of this document, which reflects the fact that I've been using
NetWare a long time and that's the term I got comfortable with, and I make
no distinction between the two.

What is a "bridge," you might ask.  Simply put, a "bridge" is a device that
forwards packets from one network segment to another network segment.  In
NetWare environments, a NetWare file server that has more than one NIC
installed is also functioning as a bridge: it will route packets from one
segment to another segment when necessary.  Also, a bridge might simply be
a small stand-alone box designed specifically for the purpose of forwarding
packets in a NetWare environment (but these are rare in all but the largest
of networks).  All the 2.x versions of NetWare came with the tools necessary
to generate a program called BRIDGE.EXE (or ROUTE.EXE in verion 2.2) that
would allow a PC with two or more NICs installed to function as a bridge.

So, for one PC to send packets to another PC on another network segment, it
must know the route to get there.  To find that route, a program using IPX
can broadcast packets on its segment, looking for bridges; each bridge on
that segment will respond with its node address and a list of all other
networks it is connected to.  Then, to send packets to those other networks,
the program must send the packets to the bridge that knows about them, and
the bridge will forward the packet on its way.

So, if a program wants to send a packet to a PC that is on the same network
segment, the "immediate address" is the same as the node address of the
destination PC; the packets are sent directly to the recipient.  If the
PC that is to receive the packets is on a different network segment, then
the "immediate address" to send to is the node address of the bridge that
can send the packet on its way.


How Many "Hops" Away?
---------------------

Using Novell's terminology, the distance between any two PCs on an
internetwork can be described as a number of "hops."  Two PCs on the same
network segment are zero hops apart; packets can go directly from the first
PC to the second.  If the two PCs are on different network segments, but
there is one bridge that connects to both of those segments, then a packet
going from one to another would have to "hop" through that bridge; therefore
those two PCs are said to be one "hop" apart.  (Remember that a "bridge"
can be a stand-alone bridge or a file server.)

Now let's consider a more complicated example.  Let's say that PC A is on
network segment 1.  Bridge Y is connected to segment 1, and also to segment
2.  Segment 2 has one more bridge on it, bridge Z.  Bridge Z is also
connected to segment 3.  PC B is on segment 3.  PC A is said to be two "hops"
away from PC B, because a packet would have to go from PC A to bridge Y,
"hopping" through it as bridge Y sends it to bridge Z on segment 2, and then
from there it would have to hop through bridge Z to segment 3 as that bridge
sends it to its destination, PC B.

               <-----+------- Segment 1 -------+----->
                     |                         |
                  --------               ------------
                  |      |               |          |
                  | PC A |               | Bridge Y |
                  |      |               |          |
                  --------               ------------
                                               |
               <-----+------- Segment 2 -------+----->
                     |
                ------------               --------
                |          |               |      |
                | Bridge Z |               | PC B |
                |          |               |      |
                ------------               --------
                     |                         |
               <-----+------- Segment 3 -------+----->

As you might guess, NetWare networks can go on nearly infinitely like this,
adding segments, bridges, and PCs in unfettered abandon.  An internetwork
can grow to encompass groups of PCs all across the nation, or even all
around the world.  The distance between any two PCs might be an
astronomically high number of hops.

As the number of hops between any two PCs grows, the amount of time it takes
to send a packet between them grows as well.  Obviously it takes at least a
small amount of time for a bridge to collect a packet from one segment and
put it on another; in the case of T1 or satellite links possibly used in
large networks, it might take a LOT of time.  The delay imposed before a
packet arrives might stretch to several seconds.  For many types of programs,
this is unacceptable.

So, IPXLIB takes this into account by allowing the user to set the maximum
number of hops between any two PCs that will communicate.  My personal
feeling is that a good setting for programs that need fast response is
three; packet send speed won't be slowed very much by having to go through
three bridges, and any PC farther than three hops away is probably in
another building, if not another state.

Since the number of hops is kept as a run-time variable, it is possible
to default to a certain number but allow the user to change that number on
the fly, if need be.


Programming IPX
---------------

To use IPX communications, the PCs involved do not have to be logged in to
a NetWare server.  In fact, the PCs don't even have to have NETX.COM or
VLM.EXE loaded.  All you need is the IPX driver, either the older IPX.COM,
or the newer LSL.COM, <NICname>.COM, and IPXODI.COM.  Keeping this in mind,
it would be possible for the users of this IPX library to communicate with
only NICs, network cabling, and Novell's IPX driver TSRs.  (In fact, I used
just such a setup to write IPXLIB.)

IPX programming is dominated by the use of two fairly complicated structures:
The Event Control Block (or ECB), a 48-byte request form, and the data
packet, which has a 30-byte header full of IPX-related information.

A note about byte ordering.  Intel microprocessors, such as the one you'll
be using to execute IPXLIB, store word-sized (two-byte) variables in a
rather curious order: The least significant byte is stored first, then the
most significant.  This is commonly referred to as "back-words" storage, or
low-high order.  As a counterexample, microprocessors from Motorola store
words in a more conventional way, most significant byte first, or high-low
order.

The IPX protocol was based heavily on Xerox's XNS protocol, right down to
the order and size of the fields in an IPX data packet header.  XNS was
apparently developed on processors that use high-low byte ordering, because
in some places where a word-sized variable is needed, high-low byte-ordering
is used.  This is contrary to the way that Intel PCs do things; therefore
these certain word-sized variables must have their bytes swapped before
being saved in an IPX structure, and they must have their bytes swapped
again when pulled out of a structure and used for something.  The structure
definitions in IPX.H show which fields are high-low and which are low-high.

The ECB structure is used to submit events to IPX.  There is an important
distinction to be made here: when you "send" a packet via IPX, you're not
really sending it at that moment; you're merely putting in a request.  IPX
will get around to it at the first available opportunity, probably only a
matter of microseconds after your request, but keep in mind that the IPX
driver might at some point be quite busy doing other things, like receiving
incoming packets, so some requests might take longer than others.

The ECB has 13 fields (note the ECB structure defined in IPX.H).  It has a
pointer to an Event Service Routine (ESR) that will be called when the
requested event has been completed; an inUse flag, which will be nonzero
if IPX is currently doing something with this ECB; a completion code, which
will be zero if the event completes okay or contain an error code otherwise;
a socket number that is associated with the event; 16 bytes reserved for
the IPX driver to use as scratch space; the immediate address; the "fragment
count" (explained in a moment), a far pointer to the packet header; the size
of the packet header; a far pointer to the packet data; and the size of the
packet data.

It is possible to break up the buffer for the packet data into as many
"fragments" as you want.  That's what the "fragment count" specifies.  For
each fragment, the ECB must have a corresponding far pointer and size field
(therefore, the ECB is a variable-sized structure).  For convenience's sake,
I decided to standardize on two fragments, the first for the packet header
and the second containing the actual data.

Next we come to the IPX data packet structure.  The smallest possible IPX
packet is 30 bytes, or just the size of the header.  The maximum size is 576
bytes, the 30-byte header and up to 546 bytes of data.  The header contains a
dummy checksum, probably there for XNS compatibility, which is always set
to FFFFh; the length of the entire packet; a transport control byte that
is used by bridges to route the packet; a byte that describes the packet
type (always set to 4, Packet Exchange Packet, for the purposes of IPXLIB);
and the network number, node number, and socket of both the sending and
receiving nodes.  (See the structure IPXPKTHDR in IPX.H for specifics.)
The bytes in the data portion of the packet can be anything the user
wants.

To make calls to the IPX driver, the programmer must use the DOS multiplex
interrupt to see if IPX is installed (you will be spared the details).  If
it is installed, then it will return a far address.  To make IPX calls,
the IPX function number goes in register BX, other registers are loaded
with pointers or values as necessary, and a far call is made to the address
IPX provided during the install check.

To receive a packet, a socket must be opened through a call to IPX, and
then an ECB must be posted, again through an IPX call, that points to an
IPX packet buffer that will receive the data.  IPX will set the inUse field
in the ECB to a nonzero value, meaning it is currently being used.  When a
packet arrives, IPX will call the Event Service Routine associated with that
ECB which can make note of the receive somehow.  At this point, the ECB's
inUse flag will have been set to zero and the completion code field will be
set to some return value (hopefully zero, meaning success).  Note that an ESR
isn't strictly necessary, as the programmer could just check the inUse fields
of posted ECBs occasionally to see if they've become zero, but notification
via interrupt is far superior to polling, in my opinion.

If it is suspected that a large number of packets will arrive faster than
the program can comfortably handle them, then several receive ECBs and
associated packet buffers can be posted at one time.  When a packet arrives,
IPX will pick one of the posted ECB/packet pairs to receive the data; the
others are still available to receive subsequent packets.  This provides for
a buffering scheme.  It's far better to have too many ECBs for receiving
packets than not enough, because if a packet arrives and there are no more
listen ECBs to put it in, the packet is simply thrown away and no error is
returned anywhere.  (After all, the ECB is where IPX returns error
information.)

Sending a packet is much like receiving one.  An ECB is posted, along with
a packet buffer that contains the data to be sent.  The additional step of
filling out the fields in the ECB and packet header having to do with the
sending address must be completed.  Sending a packet to node address
FFFFFFFFFFFFh will "broadcast" the packet to all nodes on one network
segment.  IPX sets the ECB's inUse field to a nonzero value until the packet
is actually sent.  After the packet has been transmitted, a completion code
will be placed in the ECB (hopefully zero, indicating success), the inUse
field will be set to zero, and the Event Service Routine associated with
that ECB is called.

To send a packet to another PC, the sending program must know the address of
the intended recipient.  Therefore, at some point it must go through a
configuration phase, where all or some copies of the program currently
running on all the PCs on the network broadcast "is anybody there?" packets.
What exactly constitutes an "is anybody there?" packet is up to you, the
programmer, to decide.  Perhaps it is nothing but a short, word-sized
variable of a certain value that is universally accepted by your program to
mean "is anybody there?"  When other copies of the program receive those
packets, they can examine the sending address fields and keep them for use
later when a send is necessary.  Once everybody has "found" each other, again
a determination that must be made by you, the main work of your program can
begin.  It is possible to design a program so that if somebody starts it up
long after everybody else has "found" each other, it can send "are you
there?" packets and temporarily continue the configuration phase.  This can
be made completely transparent to the end users.  See the IPXTEST demo
program for ideas on finding other PC's addresses.

One final note.  Broadcasting packets by sending them to node address
FFFFFFFFFFFFh should be done as little as possible, only during the
configuration phase of your program.  If you want to send a packet to all
users, perform a send for each intended recipient rather than doing a
broadcast.  This is more work and takes longer, of course, but if you don't
follow this rule, then the network managers at the sites where your program
is used will be unhappy with you.  Broadcast packets are received by all PCs
on a given segment, whether there's a program on that PC that wants the
packet or not.  The IPX driver is forced to waste a lot of time receiving
and processing unwanted broadcasts.  This affects all PCs, not just the ones
running your program.  Unnecessary broadcasts is a very sloppy programming
practice.
