[Back to NETWORK SWAG index] [Back to Main SWAG index] [Original]
How to program IPX on Netware systems
Written By Kenneth Johnson
July 29th 1996
Updated Dec 5th 1997
Table of contents
Section 0. General Info
Section 1. Network addresses and socket numbers
Section 2. Data types and packet format.
Section 3. The information in Section 2 in Pascal record types.
Section 4. The routines.
Section 5. Program flow
Section 6. Common questions.
Section 7. Other stuff.
Section 8. About Kenneth Johnson.
Section 9. Future works.
Hello! I wrote this document to give programmers a basic
understanding of the use of IPX, and what it can do. The
following information may work well, or may not work at all. If I
didn't think it would work then I probably wouldn't write this. I
can't test my IPX stuff because I am currently not on a network.
But it worked ok when I was in school so I am assuming it works now.
If you are a fairly good programmer, and understand networks and
interrupts then you should not have much trouble writing your own
routines from this text. You should have a good idea of record
types, and very basic assembly language. I've included the code you need
for the routines.
Another thing I should explain is that some data fields have
(hi-low) beside them. That means that the most
significant byte should be switched with the least significant
byte. If the variable X must be switched in this manner it would
look like this (in pascal): X := swap(X);
Section 0. General info
What does IPX stand for?
IPX stands for (I)nternetwork (P)acket E(x)change Protocol, it is
used by networks to send and receive information quickly to and
from the server and other computers.
Why use IPX and not SPX?
The reason I have wrote IPX routines and not SPX is that IPX is
faster. The problem with IPX is that it just sends the packet to
the destination computer. It may not necessarily get there, and
if you send multiple packets sequentially, the third packet may
get there before the first, whereas SPX (S)equenced (P)acket
E(x)change makes sure that they are delivered in the proper
order. These problems are easily overcome.
Section 1. Network addresses.
þ What is an address þ
A network address is comprised of a Network Number, and
a Node Number within a Network. This is how Netware recognizes
you on the network.
The format of a netware address:
1. Network number (hi-low) : =long integer=
2. node address (hi-low) : =array of six bytes=
3. Socket number (hi-low) : =word=
A socket is a "highway" or connection that this
information should be sent on. You can pick any number for the
socket, but Netware has some reserved. Also games like DOOM send
packets via IPX during a net game. DOOM sends on socket number
$869C. If you really want to ruin a net DOOM game, start sending
packets on this socket. (And see how unpopular you'll become!)
Getting your address and others is fairly simple.
Section 2. Data types and packet format
þ Inside IPX þ
I will now discuss some of the parts of IPX. This is the hardest
part in understanding IPX. To send information from one computer
to another, or to all computers that are on the network IPX uses
PACKETS. Packets are "pieces" of information. To send
a packet, you must set up something called an ECB or (E)vent
(C)ontrol (B)lock with an IPX header and datagram. This is what
IPX uses as information to send data. What are all these things?
Keep reading and I'll explain. The numbers beside each data field
correspond to the information that follows the format listing.
þ Format of IPX header þ
(1) Checksum (hi-low) : =word=
(2) Length of packet (hi-low) : =word=
(3) Transport control : =byte=
(4) packet type (hi-low) : =byte=
(5) Destination address : (address record type discussed above)
(6) The source address : (address record type discussed above)
1. Just set this to zero (don't worry about the hi-low stuff)
2. The length of the packet you are going to send. Just
Sizeof(IPXHEADER). (the size of the ipx header)
3. Just set to zero.
4. The type of packet you are going to send. I just set to zero,
but different values have different uses. You can also use
four.
5. The filled destination address, the computer(s) you want to
send this packet to. You must also fill in the socket number,
a value you choose for the socket that you are sending on, and
the other computer(s) are listening on.
6. The filled source address (your address) with the socket to
send on filled.
þ Format of ECB þ
Here's the format for an IPX Event Control Block.
(1) Link : =long integer=
(2) Pointer To Event Service Routine : =long integer=
(3) In-Use-Flag : =byte=
(4) Completion Code : =byte=
(5) Socket Number (hi-low) : =word=
(6) workspace : =four bytes=
(7) driver workspace : =twelve bytes=
(8) Immediate Local Node Address : =six bytes=
(9) Fragment Count : =word=
(10) Data Fragments : =array[1..2] of
fragments= (see below)
þ Format of a fragment record (Data Fragments) þ
Format for the fragments of data found in the ECB. This does not
necessarily have to be an array of two fragments, it could be
more, but this is what I use and I can guarantee that it works.
Pointer to Fragment : =pointer=
Size of fragment : =word=
Explanation of the ECB data fields:
1.When setting up an ECB I don't even bother with the "link".
I'm not even sure about it's purpose.
2.You don't have to fill this field either. This is an address
to the event service routine. When the ECB you have given to
IPX has been handled, it calls this procedure. You don't really
need it, but I guess it would make life easier.
3.The In-Use-Flag is used to determine whether or not IPX has
gotten around to actually sending the data off to the other
computer(s). If it has sent the packet then this is set as $0,
if IPX did not send it, it will be $ff. The in-use-flag could
have other values which will be discussed later. These two are
the ones that are important right now.
4.If the completion code is 0h then IPX delivered the packet
with no problems, if it is anything else then it did not send
it.
5. The socket that you are going to send the packet on.
6. Just fill with zeros.
7. Just fill with zeros.
8. Your local node address. We will be looking at routines that
get your network address (including node address)
9. Your fragment count. (In this document, we'll use 2)
10. You're information that you want IPX to send. It is contained
in DATAGRAMS. Datagrams is the actual information you want to
send, whether it's part of a file, for a chatting program, or
any other type of format.
A datagram can be maximum 546 bytes long. I assume that the
minimum would be 1 or 0, but that doesn't really matter. I
have the datagram as an array of bytes:
Datagram : array[1..546] of byte;
In the fragment pointer of TFRAG, you give it the datagram
segment and offset and in the Size field you give it the size
of the datagram, in this case 546 bytes.
What is interesting about IPX is that instead of giving IPX
variables to put information in, you give it their addresses,
and IPX will take care of everything.
In the Fragment array you must include the IPX header in the
first fragment. So you copy the stuff in the IPX
header into the datagram, and then your other info into the
second datagram and IPX handles it.
Section 3. The information in Section 2 in Pascal record types.
Here's what I babbled on about in section two in Pascal
Record Types. These are the actual things you send IPX so that it
can work:
type
{The datagram record type}
datagramRec = array[1..546] of Byte; {where you put the data
you want to send}
{IPX record type}
tipxheader = record {IPX uses this to determine where }
checksum : word; {it's going, from who and on}
Len : word; {which socket}
control : byte;
packettype : byte;
dest,
source : tinternetworkaddress;
end;
{network address record type without socket number}
networkaddressREC = record
network : longint;
nodeaddr : array[1..6] of byte;
end;
{network address record type with socket number}
tinternetworkaddressREC = record {full network address and socket}
network : longint;
nodeaddr : array[1..6] of byte;
socket : word;
end;
{the data fragment record type}
tfrag = record {where the data pointers goes}
FragPtr : pointer;
Size : Word;
end;
{Event control block record type}
Tecb = record {what you send to IPX}
Link : pointer;
ESR : pointer;
inuse : byte;
code : byte;
socketnum : word;
ipxworkspace : array[1..4] of byte;
driver : array[1..12] of byte;
localnode : array[1..6] of byte;
fragcount : word;
fragdata : array[1..2] of tfrag;{the first TFRAG should have}
end; {the IPX header}
Section 4.
The routines
Here's all the routines that you will be using to send packets,
listen for packets, get your address and others.
þ essential routines þ
{------------------------------------------------------------------------}
{sending a packet: ecbpointer is an ecb record type}
procedure sendpacket(var ecbpointer);assembler;
asm
push bp {if this isn't saved then the program will crash}
mov bx,0003h
les si,ecbpointer
int $7a
pop bp
end;
{------------------------------------------------------------------------}
{listen for a packet: ecb is an ecb record type}
procedure listenforpacket(Var ecb);assembler;
asm
push bp
Push si
mov bx,$0004
les si,ecb
int $7a
mov [byte ptr result],al
Pop si
pop bp
end;
{------------------------------------------------------------------------}
{check to see if IPX is installed}
function IPXinstalled : Boolean;
var stat : byte;
begin
asm
mov ax,7a00h
int $2f
mov stat,al
end;
if stat = $ff then ipxinstalled := true else
ipxinstalled := false;
end;
{------------------------------------------------------------------------}
{closes an open socket}
procedure closesocket(socketnum : word);
var sock : word;
begin
sock := swap(Socketnum);
asm
mov bx,0001h
mov dx,sock
int $7a
end;
end;
{------------------------------------------------------------------------}
{Opens a socket, (SocketType is the type of socket you want to
open:
00h = open until close or terminate
FFh = open until close}
procedure opensocket(socketnum : word);
var i : word;
begin
i := swap(socketnum);
asm
mov bx,0000h
mov dx,i
mov al,sockettype
int $7a
mov [byte ptr result],al
end;
end;
{------------------------------------------------------------------------}
{tells IPX that the program is idle and it can do some work}
Procedure RelinquishControl;assembler;
asm
mov bx,$000a
int $7a
end;
{------------------------------------------------------------------------}
þ Other routines þ
{------------------------------------------------------------------------}
{some of these routines use this.}
procedure Callint(Ahreg : byte;Var bufferin,bufferout;
Var error : Byte);assembler;
asm
push ds
mov ah,ahreg
lds si,bufferin
les di,bufferout
int 21h
mov [byte ptr error],al
pop ds
end;
{------------------------------------------------------------------------}
procedure getinternetaddress(Connect : byte;
Var net : tinternetworkaddressrec);
type
request = record
len : word;
sub : byte;
c: byte;
end;
reply = record
len : word;
n : tinternetworkaddressREC;
end;
var bufferin : request;
bufferout : reply;
i : byte;
begin
fillchar(Bufferout,sizeof(Bufferout),0);
bufferin.len := 2;
bufferin.sub := $13;
bufferin.c := connect;
bufferout.len := sizeof(Bufferout)-2;
callint($e3,bufferin,bufferout,error);
net.networkaddr := bufferout.n.networkaddr;
for i := 1 to 6 do
net.nodeaddr[i] := bufferout.n.nodeaddr[i];
net.socket := bufferout.n.socket;
end;
{------------------------------------------------------------------------}
procedure myaddrASM(var bufferout);assembler;
asm
mov bx,0009h
les si,bufferout
int $7a
end;
{------------------------------------------------------------------------}
{get your own address}
Procedure myaddress(Var n : tinternetworkaddressrec);
type
reply = record
net : longint;
node : array[1..6] of byte;
end;
var
bufferout : reply;
begin
fillchar(Bufferout,sizeof(Bufferout),0);
myaddrasm(Bufferout);
move(bufferout.node,n.nodeaddr,sizeof(N.nodeaddr));
n.networkaddr := bufferout.net;
end;
{------------------------------------------------------------------------}
Other routines that you should write are things that set up the ECB for
sending and receiving packets. I'll show you what you should be
doing for those routines.
Section 5.
Program flow
In this section I will show you the basic program flow for sending and
receiving an IPX packet.
For the socket numbers I just use the same socket for sending/receiving
data. A better way to do this would be to have one socket for receiving
and one for sending packets. (that gets a bit complicated)
SEND PACKET
The program flow is this:
variables used:
ecb : tecb;
IPX : tipxheader;
D : datagram;
1. Open socket your going to send on.
2. Setup your IPX header. (The fields listed here MUST be filled)
==> Get destination address & put it in IPX.dest
If you want to send a broadcast packet (to all computers)
then just leave the ipx.dest filled with zeros and the
ipx.dest.nodeaddr filled with $FF.
==> IPX.checksum must equal zero (hi-low)
==> IPX.len = Sizeof(ipx) (size of the ipx header) (hi-low)
==> IPX.dest.socket equals socket number that destination (hi-low)
computer is using to receive data. (I think)
==> the rest of the IPX header should be all zeros.
The fields with "hi-low" must have their bytes SWAPED.
3. Put whatever data you want to send into the datagram.
as an example:
S := 'This is a test!'
Move(S,D,Sizeof(S));
Now the datagram (D) contains the string "This is a test!".
When we set up the ECB and actually send the packet, this is what
we would be sending.
You can put ANY type of data in the datagram.
4. Setup your Event Control Block (ECB)
These fields must be filled:
==> ecb.socketnum equals socket your sending on (hi-low)
==> Fragcount equals the amount of data fragments you have.
In this document we are using two.
==> Fragdata[1].size := sizeof(IPX)
==> Fragdata[1].fragptr := @(IPX) (pointer to IPX header)
N.B The first fragment must always contain the ipx header
data.
==> fragdata[2].size := sizeof(D)
==> fragdata[2].fragptr := @(D)
The second fragment contains the datagram.
(the stuff your actually sending)
5. Send the packet.
You are now ready to send the packet on it's speedy little voyage
through the network to reach it's destination computer(s).
This is just a call to the routine Sendpacket. You give the
routine the filled ecb and it takes care of the rest.
SendPakcet(ecb);
repeat
relinquishcontrol;
until ecb.inuse = $00;
What is that "relinquishcontrol" stuff?
That's a routine to tell IPX that it can go ahead and send the packet.
Usually you repeat that routine until the ecb.inuse flag is set to
zero, which means that the packet was sent. If ECB.code is something
other than zero then the packet did not send successfully.
That's how an IPX packet is sent. You should probably write some procedures
that will set up the IPX header and ECB header by just sending up a
connection number and the header. In my IPXSETUP procedure, a connection
number that is zero tells it to set up the header's dest address as a
broadcast packet. Note that setting up ipx for receiving a packet is a bit
different. (A lot easier!)
RECEIVE PACKET (same variables used)
Basic program flow:
1. Fill IPX header with zeros
2. Setup ECB.
==> ecb.socketnum := swap(listensocket)
==> ecb.fragcount = 2
==> fragdata[1].size := sizeof(ipx);
fragdata[1].fragprt := @ipx;
==> fragdata[2].size := sizeof(d);
==> fragdata[2].fragptr := @d;
3. Wait for a packet to arrive.
Listenforpacket(ecB);
repeat
relinquishcontrol;
until ecb.inuse = 00;
4. data is now in Datagram
Notice in step 3 that the routine "listenforpacket" does not wait for a
packet to be send. Rather, you call that routine and then repeat
"relinquishcontrol" until a packet does arrive. When a packet is received
then IPX puts the packet information into the pointers specified. In this
case "IPX" and "D". Now you know who send the packet (you have their
address) and what they sent.
Section 6.
Common questions
Q. Why isn't my program working?
A. Could be a number of reasons. Step through your program and see exactly
what you are putting in the IPX header and ECB. (If your using Pascal
press ctrl-f4) Also, if you are setting up an ECB or header, any fields
that do not have to be filled with a value should be set to zero.
Make sure the socket you are sending on is open. (check the error code)
If you e-mail me with a question then it will probably end up in this
section.
Section 7.
Other stuff
Here's a list of the inuse flag codes,completion codes and values for
IPX packet types:
Values for ECB in-use flag:
00h available
E0h AES temporary
F6h \special IPX/SPX processing for v3.02+
F7h /
F8h IPX in critical section
F9h SPX listening
FAh processing
FBh holding
FCh AES waiting
FDh AES counting down delay time
FEh awaiting packet reception
FFh senfing packet
Values for ECB completion code:
00h success
ECh remote terminated connection without acknowledging packet
EDh abnormal connection termination
EEh invalid connection ID
EFh SPX connection table full
F9h event should not be canceled
FAh cannot establish connection with specified destination
FCh cancelled
FDh malformed packet
FEh packet undeliverable
FFh physical error
Values for IPX packet type:
00h unknown packet type
01h routing information packet
02h echo packet
03h error packet
04h packet exchange packet
05h SPX packet
11h Netware Core Protocol
14h Propagated Packet (for Netware), NetBIOS name packet
15h-1Eh experimental protocols
Section 8
About Ken Johnson
Hello! My name is Ken Johnson and I am a student at Carleton
University in Ottawa Ont. Canada. Currently I am in my first
year of a Computer Mathmatics degree.
You can visit my website at: wabakimi.carleton.ca/~kjohnso3
or email me at kjohnso3@chat.carleton.ca
Section 9
Future works
Hopefully somemore TCP/IP stuff but I haven't had the time to
do so. If you are interested in IPX/SPX code for C++ then send
me some mail.
[Back to NETWORK SWAG index] [Back to Main SWAG index] [Original]