Writing Servers for Multi-homed Nodes

 < Day Day Up > 



A multi-homed node is simply a device that contains more than one network interface and, therefore, connects to more than one network. Writing simple servers for multi-homed nodes is commonly as simple as single-homed nodes, unless the server treats the network interfaces differently. For example, is the server accessible over each of the networks connected to the network interfaces, or only a subset of them?

Let’s now look at a few scenarios and the code patterns that are necessary to provide the needed functionality.

The first case is the simplest; the server accepts connections over any interface (from any connected network). This is the default of most server applications built today. Recall that the bind function is used to “bind” a socket to a given address. The application may specify the address of a given network interface, or a wildcard for all interfaces. This is shown in Listing 6.6.

Listing 6.6 Server accepting connections from all interfaces.

start example
int serverSock; struct sockaddr_in saddr; serverSock = socket( AF_INET, SOCK_STREAM, 0 ); memset(&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = htonl( INADDR_ANY ); saddr.sin_port = htons( MY_PORT ); bind( serverSock, (struct sockaddr *)&saddr, sizeof(saddr));
end example

The INADDR_ANY wildcard address specifies that any available network interface may be used to accept incoming connections. However, what if the server wanted to restrict connections only to those from a specific interface? This is common in devices that consist of a WAN and LAN interface. For example, configuration may only be possible through the LAN interface (internal local network). If the LAN interface had the IP address “192.168.1.1”, the following code pattern in Listing 6.7 could be used to restrict connections to only those from that interface.

Listing 6.7 Server accepting connections from a specific interface.

start example
 int serverSock; struct sockaddr_in saddr; serverSock = socket( AF_INET, SOCK_STREAM, 0 ); memset(&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = inet_aton( "192.168.1.1" ); saddr.sin_port = htons( MY_PORT ); bind( serverSock, (struct sockaddr *)&saddr, sizeof(saddr));
end example

The only difference here is that we bind the sin_addr element of our sockaddr_in structure with the network byte order of the address using inet_aton.

The final example is a bit more complicated. Let’s assume in this case that we want to present a server on multiple interfaces, but we’ll treat connections differently based upon the interface over which they arrived. The first interface is represented by “192.168.1.1” and the second by “10.0.0.1”. In the example shown in Listing 6.8, we create two sockets and bind each of them with a different sin_addr address, but both with port 8192. Then, using the select function, we identify whether a connect request has arrived by sensing that the server socket is readable. After determining that a socket is readable, we perform the accept on the given socket, and then do whatever action is necessary. This allows us to have a single server represented by a number of sockets with the ability to differentiate based upon the interface over which a connection is requested.

Listing 6.8 Server differentiating based upon connect interface (bindtest.c).

start example
#include <sys/socket.h> #include <sys/types.h> #include <arpa/inet.h> #include <unistd.h> #include <stdio.h> #define MAX(x, y)       ((x > y) ? x : y) main() {   int s1, s2, s3, ret, max, on;   struct sockaddr_in sa1, sa2;   fd_set rfds;   s1 = socket( AF_INET, SOCK_STREAM, 0 );   s2 = socket( AF_INET, SOCK_STREAM, 0 );   max = MAX(s1, s2) + 1;   on = 1;   ret = setsockopt( s1, SOL_SOCKET, SO_REUSEADDR,                      &on, sizeof(on) );   ret = setsockopt( s2, SOL_SOCKET, SO_REUSEADDR,                      &on, sizeof(on) );   memset( &sa1, 0, sizeof(sa1) );   memset( &sa2, 0, sizeof(sa2) );   /* Set up the two address structures for the same */   /* port, different interfaces */   sa1.sin_family = sa2.sin_family = AF_INET;   sa1.sin_port = sa2.sin_port = htons(8192);   sa1.sin_addr.s_addr = inet_addr( "192.168.1.1" );   sa2.sin_addr.s_addr = inet_addr( "10.0.0.1" );   /* Bind address1 to s1 */   ret = bind( s1, (struct sockaddr *)&sa1, sizeof(sa1) );   ret = listen(s1, 5);   /* Bind address2 to s2 */   ret = bind( s2, (struct sockaddr *)&sa2, sizeof(sa2) );   ret = listen(s2, 5);   while (1) {     /* Set up the read socket descriptor list */     FD_ZERO( &rfds );     FD_SET( s1, &rfds );     FD_SET( s2, &rfds );     /* Await an incoming connection */     ret = select( max, &rfds, NULL, NULL, NULL );     /* Check that a read (connect) arrived */     if (ret > 0) {       /* Was it socket1? */       if ( FD_ISSET( s1, &rfds ) ) {         printf("Received connect request over 192.168.1.1\n");         s3 = accept( s1, (struct sockaddr_in *)NULL, NULL);         close( s3 );       /* Was it socket2? */       } else if ( FD_ISSET( s2, &rfds ) ) {         printf("Received connect request over 10.0.0.1\n");         s3 = accept( s2, (struct sockaddr_in *)NULL, NULL);         close( s3 );       }     } else {       printf("Error!\n");     }   }   close(s1); close(s2); }
end example

Although two sockets were used in the multiple interface example (Listing 6.8), this is transparent to all external peers. The only difference seen at the peers is the externally observable behavior that is coded within the server.



 < Day Day Up > 



BSD Sockets Programming from a Multi-Language Perspective
Network Programming for Microsoft Windows , Second Edition (Microsoft Programming Series)
ISBN: 1584502681
EAN: 2147483647
Year: 2003
Pages: 225
Authors: Jim Ohlund

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net