Features ======== * IP layer. * ICMP. * TCP layer. * UDP layer. * Local loopback. * Ethernet interface for NE2000 compatible cards. See Ne2000.txt. * Dynamic ARP table. What is not implemented ======================= The IP layer does not process header options. The IP layer does not support routing. How to configure Menuet for Ethernet ==================================== First, you need to have a supported ethernet card fitted, or present on your motherboard. If you are uncertain what type of hardware you have, try to configue the stack. If you have supported hardware it will be found, and enabled. Setting Up the ARP Table ------------------------ Menuets ARP table is dynamically created and maintained; You can see what hosts Menuet has communicated with by running the ARPSTAT application. Enabling Ethernet ----------------- Boot Menuet, then select STACKCFG from the NET menu. Press the 'Read' Button, then select 'Packet Driver'. Press 'Change' next to the IP address, and enter the IP address you want to use. Make sure it is on the same sub-net as the LAN to which you are connected. Press 'Apply' to action the changes. Close the program. You can also pre-set the values in file Config.mnt. The stack is now running, which you can test by pinging Menuet from a remote host. The simplest way to connect two PC's together is using a 'Null Modem' Ethernet cable. These simply cross certain wires over. They can be purchased from PC stores, but are simple to make. Details can be found on the web. Look on google for 'ethernet cross over cable' or similar. How to use TCP/IP locally, with no Network ========================================== Menuet supports a form of local loopback that means applications on the same PC can communicate with each other via sockets as though they were on separate hosts. To connect to an application on the same machine, specify the local IP address as the destination address. You do not even need to configure the stack for ethernet; local loopback will work without any network hardware. It's great for development and testing. Application Programming Interface ================================= The developer can access the stack through interrupt 0x40, function 53. The file TFTPC.ASM gives a good example of how to use the programming interface ( at least, for UDP), but as network communication is complex I'll give an overview. Sockets ======= Applications connect to each other and pass information between themselves through a mechanism called a 'socket'. Sockets are end-points for communication; You need one at each application to communicate. Using sockets is a little like using files on an OS; You open them, read and write to them, then close them. The only thing that makes life slightly more complicated is that unlike with a file, you have something intelligent at the other end ( which for example may not want to close when you do! ) Lets deal with the terminology before we go any further. socket A unique identifier used by the application for communication. local port A number that identifies this application on the local host. Ports are a way to allow multiple applications to communicate with other hosts without the data becoming mixed up. ( The technical term is 'multiplex' ). Port numbers are 16 bit. remote port A number that identifies the application on the remote host to which we are communicating with. To the remote host, this is it's 'local port'. Port numbers are 16 bit. IP Address A 32 bit number that identifies the remote host PC that we are communicating with. Passive Refers to the mode by which a socket is opened; When opening in passive mode, the local PC is awaiting an incoming connection. Active Refers to the mode by which a socket is opened; When opening in active mode, the local PC will attempt to connect to a remote PC. When you connect to a socket on a remote PC, you need to specify more than just the IP address, otherwise the remote stack will not know to which application it should send your data. You must fully qualify the address, which means you specify the IP address and the port number. This would be written like this 192.168.1.10:80 ; Connect to port 80 on the machine 192.168.1.10 Port numbers are important. Some are 'well known' and provide access to common applications. For example port 80 is used by HTTP servers; That way I can connect to a webserver on a host without having to find out what port number the application is listening on. This brings me to the way in which you open a socket; As I said earlier, there are two modes, Passive and Active. A webserver would open a passive socket, as it is waiting for incoming connection requests. A web browser would open an active socket because it is attempting to connect to a specified remote host. Access to programming interface =============================== The developer accesses the stack functions through interrupt 0x40, function 53. Some functions may be accessed through function 52, but these are mainly for stack configuration. Here is a summary of the functions that you may use and the parameter definitions. Get Local IP Address -------------------- rax = 52 rbx = 1 IP address returned in rax ( in internet byte order ) Write to stack input queue -------------------------- rax = 52 rbx = 6 rdx = number of bytes to write rsi = pointer to data ( in application space ) On return, rax holds 0 for OK, or 0xFFFFFFFF for error. This interface is for slow network drivers only ( PPP, SLIP ) Read data from network output queue ----------------------------------- rax = 52 rbx = 8 rsi = pointer to data ( in application space ) On return, rax holds number of bytes transferred. This interface is for slow network drivers only ( PPP, SLIP ) Open a UDP socket ----------------- rax = 53 rbx = 0 rcx = local port rdx = remote port rsi = remote ip address ( in internet byte order ) The socket number allocated is returned in rax. A return value of 0xFFFFFFFF means no socket could be opened. Open a TCP socket ----------------- rax = 53 rbx = 5 rcx = local port rdx = remote port rsi = remote ip address ( in internet byte order ) rdi = mode : SOCKET_PASSIVE or SOCKET_ACTIVE ( defined in stack.inc ) The socket number allocated is returned in rax. A return value of 0xFFFFFFFF means no socket could be opened. Close a socket (UDP Only ) -------------------------- rax = 53 rbx = 1 rcx = socket number On return, rax holds 0 for OK, or 0xFFFFFFFF for error. Close a socket (TCP Only ) -------------------------- rax = 53 rbx = 8 rcx = socket number On return, rax holds 0 for OK, or 0xFFFFFFFF for error. Poll socket ----------- rax = 53 rbx = 2 rcx = socket number On return, rax holds the number of bytes in the receive buffer. Read socket data ---------------- rax = 53 rbx = 3 rcx = socket number On return, rax holds the number of bytes remaining, bl holds a data byte. Write to socket ( UDP only ) ---------------------------- rax = 53 rbx = 4 rcx = socket number rdx = number of bytes to write rsi = pointer to data ( in application space ) On return, rax holds 0 for OK, or 0xFFFFFFFF for error. Return socket status ( TCP only ) --------------------------------- rax = 53 rbx = 6 rcx = socket number On return, rax holds the sockets TCP status. This function can be used to determine when a socket has actually connected to another socket - data cannot be written to a socket until the connection is established (TCB_ESTABLISHED). The states a socket can be in are defined in stack.inc as TCB_ Write to socket ( TCP only ) ---------------------------- rax = 53 rbx = 7 rcx = socket number rdx = number of bytes to write rsi = pointer to data ( in application space ) On return, rax holds 0 for OK, or 0xFFFFFFFF for error. Check port number ----------------- rax = 53 rbx = 9 rcx = port number This function is used to determine if a port number is in use by any sockets as a local port number. Local port numbers are normally unique. On return, rax is 1 for port number not in use, 0 otherwise. Opening a TCP socket in Menuet ============================== There are two ways to open a socket - Passive or Active. In a Passive connection your application 'listens' for incoming requests from remote applications. Typically this will be done when you are implementing a server application that allows any other application to connect to it. You would specify a 'known' local port number, such as 80 for a web server. You would leave the remote IP and remote port number as 0, which indicates any remote application may connect. Once the socket has been opened you would wait for an incoming connection before doing anything. This can be by either checking the socket status for TCB_ESTABLISHED, or waiting for data in the receive buffer. In an Active connection, you are making a connection to a specified remote port. The remote IP and remote port parameters must be filled in with non-zero values ( otherwise, what are you connecting to? ). You also specify a unique local port number so the remote application can uniquely identify you - after all, there may be several applications on your machine connected to the same remote host. See below for finding a unique port number. How to find an unused local port number ======================================= Typically when you are creating an active connection to a remote socket you will want to choose a unique local port number. Local port numbers normally start from 1000; The following code may be used to obtain an unused port number prior to making the open socket call. mov rcx, 1000 ; local port starting at 1000 getlp: inc rcx push rcx mov rax, 53 mov rbx, 9 int 0x60 pop rcx cmp rax, 0 ; is this local port in use? jz getlp ; yes - so try next ; rcx contains a free local port number Writing data to a socket ======================== There are two functions available depending on whether the socket was opened for TCP or UDP protocol; The call parameters are the same however. When the socket is being opened for TCP, use the status function to poll for a connection - data cannot be written to a socket until another socket has connected to it, and the state of the socket is TCB_ESTABLISHED. When you write data, the call results in a single IP packet being created and transmitted. Thus the user application is responsible for the size of transmitted packets. Keep the packet sizes under 768 bytes. If you are writing a terminal program like telnet, you may want to send a packet for each keystroke ( inefficient ) or use a timer to send data periodically ( say, every second ). Reading data from a socket ========================== There is one function to read data from a sockets receive buffer. This function retrieves one byte at a time. You can use the poll function to test the receive buffer for data. Closing a socket ================ Simply call the appropriate function - there is one for TCP, and another for UDP. When closing a TCP socket, don't forget that the other end may continue to send data, so the socket may remain active for a few seconds after your call.