[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]