; NETWORK.TXT -- A NetWare and IPX primer for LANLIB ; Copyright (c) 1994 by Allen Brunson version 1.00 06/30/94 ****************************************************************************** * * *** Trademarks *** * * ****************************************************************************** Borland is a registered trademark of Borland International, Inc. Microsoft and MS-DOS are registered trademarks and Windows is a trademark of Microsoft Corporation. Netware and Novell are registered trademarks and IPX is a trademark of Novell, Inc. ****************************************************************************** * * *** Introduction to NetWare and IPX *** * * ****************************************************************************** If you're interested in using LANLIB, 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. LANLIB makes no use of SPX whatever. The concept behind NetWare networks is fairly simple. All PCs that will participate in the network have NICs (Network Interface Cards) 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 "file 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. The server DOES NOT run DOS; it instead executes Novell's proprietary server program, and its disk drives do not have normal DOS partitions on them. Each client PC must have TSRs loaded to allow IPX communication (the IPX driver) and to trick DOS into recognizing the server's hard drives as local drive letters (the NetWare shell). 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 to allow network communcation 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. 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 and to make the server's hard drives appear as drive letters on the client PC. 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 that was specific to a particular 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 was in use. 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; 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 ---------------------------- 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 (a common type of network) 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, a standard that used to be quite common until the advent of Ethernet, 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, a committee which sets and maintains many types of electrical standards, 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. 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 00000002:0000000000D0. The program on PC B opens up socket number 5700h and listens for packets on it. PC A sends packets to 00000002:0000000000D0:5701. 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 00000001:000000000020, 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 00000001:000000000020:6000. 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 LANLIB 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, LANLIB can be used to send and receive with multiple socket numbers, but this is a topic that is outside the scope of this document. The 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. 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. Many low-numbered sockets are assigned for specific purposes. Socket numbers 4000h through 7FFFh are "dynamic" socket numbers, or ones that are free for anybody to use. 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 zeroes which other programmers are likely to have used. Also don't choose a number that has the same two hex digits repeated twice, like 7878h or 6565h. Such socket numbers are often used by programmers who don't want to have to deal with the fact that socket numbers must be passed to IPX in high-low byte order, the opposite of the native format of Intel microprocessors, which is low-high (more on this later). 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. (This could also be handled by the program itself. The user could choose whether to belong to group one or group two, and the program could react accordingly.) If you write a second program that uses IPX, it stands to reason that some of the people that bought your first 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, 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 "router." Then the router might send the packet to another router, and so on, until the last router in the chain sends it to the destination PC. Before we go any further, 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 "router" for the remainder of this document and I make no distinction between the two. What is a "router," you might ask. Simply put, a "router" 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 router: it will route packets from one segment to another segment when necessary. Also, a router 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 router. 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 "diagnostic" packets on its segment, looking for routers; each router 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 router that knows about them, and the router will forward the packet on its way. 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 router 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 router that connects to both of those segments, then a packet going from one to another would have to "hop" through that router; therefore those two PCs are said to be one "hop" apart. (Remember that a "router" can be a stand-alone router or a file server.) Now let's consider a more complicated example. Let's say that PC A is on network segment 1. Router Y is connected to segment 1, and also to segment 2. Segment 2 has one more router on it, router Z. Router 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 router Y, "hopping" through it as router Y sends it to router Z on segment 2, and then from there it would have to hop through router Z to segment 3 as that router sends it to its destination, PC B. <-----+------- Segment 1 -------+-----> | | -------- ------------ | | | | | PC A | | Router Y | | | | | -------- ------------ | <-----+------- Segment 2 -------+-----> | ------------ -------- | | | | | Router Z | | PC B | | | | | ------------ -------- | | <-----+------- Segment 3 -------+-----> As you might guess, NetWare networks can go on nearly infinitely like this, adding segments, routers, 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 a very 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 router 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. LANLIB takes this into account by allowing the user to set the maximum number of networks that are recognized; indirectly, this allows a limit on the number of hops. If you tell LANLIB to keep track of only the first four networks it finds, then none of them can be more than four hops away, for instance. Since the number of networks is kept as a run-time variable, it is possible to default to a certain number but allow the program 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, .COM, and IPXODI.COM. Keeping this in mind, it would be possible for the users of this library to communicate with only NICs, network cabling, and Novell's IPX driver TSRs. (In fact, I used just such a setup to write LANLIB.) 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 LANLIB, 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 LANLIB.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 LANLIB.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 packet 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 routers to route the packet; a byte that describes the packet type (always set to 4, Packet Exchange Packet, for the purposes of LANLIB); and the network number, node number, and socket of both the sending and receiving nodes. (See the structure IPXPKTHDR in LANLIB.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 program 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 end users. See the LANTEST 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; doing so only during the configuration phase of your program is ideal. 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 time receiving and processing unwanted broadcasts. This affects all PCs, not just the ones running your program. Unnecessary broadcasts constitute a sloppy programming practice.