Notes on NS3 – IEEE 802.11 Wi-Fi Association

For some of my research I have been delving into the details on how exactly station devices in an IEEE 802.11 network associate with an access point. As far as I understand from the standard station nodes have two possibilities.

1. Active
In this case the station nodes send out probe requests to see if any access points respond during a timeout period

2. Passive
In this case the station nodes just sit and listen over a timeout period to see if any aps send out beacons.

In either case, according to the standard, the station node is supposed to rank the APs according to received signal strength (RSS). While this is recognized as a bad method for various reasons – it is a simple and quick way to select an AP.

In NS3 (3.16), as far as I can tell, there is no measure of RSS. The station node simply associates with the first AP that it receives either a probe response or beacon from (depending on active or passive). Usually this ends up being the closest AP to the user station because it has the lowest propagation delay due to the lower distance. However, depending on when the stations start up, a farther station is sometimes selected – at least initially until enough beacons are missed that an AP disassociation occurs, and the process starts over with the closer AP being selected.

In NS 3.16, the code which controls AP association is located in src/wifi/model/sta-wifi-mac.cc.

Looking at the code details, and confirming with some extra logging I added – a Wi-Fi station when starting, broadcasts a probe request (in active probing mode). It then waits for a probe response.

If you look in the “StaWifiMac::Receive (Ptr packet, const WifiMacHeader *hdr)” function, you can see that immediately when it receives one of these responses, it sends an association request to the AP. This is one place where changes must occur if one wishes to make a smarter decision for selecting an AP. In my case, instead of immediately sending an association request, I record the probe response and continue to wait for more until the probe timeout occurs.

If you look at “StaWifiMac::ProbeRequestTimeout (void)” you can see this is where you can now implement the association decision. After the timeout has occured, we now have a list of all of the received probe packets and can make the decision and associate like before.

IEEE ICC 2012 – Ottawa

Last week I presented a paper at IEEE ICC, and since its been a while since I have posted I thought I put up a bit of a summary about my work. For people who have looked at my site a bit, you might know my research is on Heterogeneous Wireless Networking (making wireless technologies work together seamlessly). The goal of my work is to enable devices with Bluetooth, Wi-Fi, 3G, 4G, LTE and future radios to be able to switch easily between connections or use more than one at a time. There are still many problems which make this impossible right now. For instance, if you are using Skype on a Wi-Fi connection on your phone and you leave a building and switch to the 4G network outside, chance are you will be disconnected from Skype (Seamless Handover is not supported).

Another problem may be a lack of co-ordination between radio access technologies (RATs). For instance, while Bluetooth supports adaptive frequency hopping (AFH) to try to avoid the same channels Wi-Fi is being used on, this may not be enough to ensure interference between the technologies is avoided. What happens when you are in a dense area where Wi-Fi is being used across all channels, or there are many devices? (Apartment buildings, dense cities etc.)

As you may know, lots of wireless research is done using simulation because in many cases it is faster and cheaper. Many simulation tools support interference within a particular technology (ie Wi-Fi nodes interfering with other Wi-Fi nodes) but not many support interference between technologies (ie Wi-Fi nodes interfering with Bluetooth nodes). The paper I presented at ICC tries to understand how much of an impact this makes.

I’ll spare most of the details since they are in the paper, but essentially the findings are that Wi-Fi -> Bluetooth interference causes a drop in around 10-15kbps of the total 50kpbs Bluetooth was able to achieve in our lab setup (About 30% drop).

In the other direction, the interference was mostly insignificant. However, this was expected since the setup used close ranges (Wi-Fi power is much greater than that of Bluetooth).

The future work includes looking at varying distances (It seems like it will be interesting when we use a range that makes the Wi-Fi and Bluetooth powers similar). Eventually the goal is to create an interference model that can be used in simulation. If you want more details – see the attached paper and slides.

icc2012 (slides pptx)
icc20122 (paper pdf)

Tutorial: sockets – 3 ways to listen

As you may know, I have been ta-ing a course in operating systems. We just finished covering sockets and in the last lab I gave a socket demo where I show three different ways a server can listen on a socket. First is a very basic case where the server can only accept and process one connection at a time. Second I show the fork()ing case where multiple connections can be processed concurrently using multiple processes. Lastly, the multiple connection case is handled with select() so that everything can be handled in a single connection.

Single connection server
This server is a basic echo server. A client connects to it on a specific port and enters strings on the stdin. The server maintains the connection until the client terminates the connection with a blank line.

The major weakness of this server is that it can only handle a single connection at once. So if the first connection is very slow, any subsequent connections must wait, even if they are ready to be handled. In the case of this server, the first user to connect may be very slow to type their strings, and even if the second user has already entered a string, it will not be displayed until the first user connection in completed.

#include
#include
#include
#include
#include
#include
#include
#include
#include

#define ERROR(x) do { perror(x); exit(1); } while(0)

#define NAME 257
#define BUFSIZE 257
#define MAXPENDING 1

int main(int argc, char *argv[])
{
unsigned short port; /* Port to which server will bind */
char servhost[NAME]; /* Local host name */
struct sockaddr_in sock; /* INTERNET socket space */
struct hostent *server; /* Local host information */
int S; /* fd for socket */
int NS; /* fd for connected socket */
char buf[BUFSIZE]; /* Input buffer */
FILE *fp; /* Stream (converted file des.) */

if (argc != 2)
{
fprintf(stderr,"usage: server \n");
exit(1);
}

port = atoi(argv[1]);

/*
* Get socket - INTERNET DOMAIN - TCP
*/
if ((S = socket(AF_INET, SOCK_STREAM, 0)) < 0) ERROR("server: socket"); /* * Obtain host name & network address */ gethostname(servhost, sizeof(servhost)); if ((server = gethostbyname(servhost)) == NULL) { fprintf(stderr,"%s: unknown host\n",servhost); exit(1); } /* * Bind to server address - in network byte order */ sock.sin_family = AF_INET; sock.sin_port = htons(port); memcpy(&sock.sin_addr, server->h_addr, server->h_length);

/*
* Bind socket to port/addr
*/
if (bind(S, (struct sockaddr *)&sock, sizeof(sock)) < 0) ERROR("server: bind"); /* * Listen on this socket */ if (listen(S,MAXPENDING) < 0) ERROR("server: listen"); //loop to continue handling connections while(1) { /* * Accept connections. Once connected, the client will be * connected on fd NS, a second and third parameter may be passed * to accept which will be filled in with information regarding the * client connection if desired. * * In this example, once connected the server is done with the * master socket (so closes it). */ if ((NS = accept(S,NULL,NULL)) < 0) ERROR("server: accept"); /* * Using stdio library to read from socket */ if (!(fp = fdopen(NS,"r"))) { fprintf(stderr,">>> Error converting file des. to stream <<<\n"); exit(1); } while (fgets(buf,BUFSIZE,fp)) printf("%s", buf); /* * DONE - simply close() connection */ fclose(fp); close(NS); } return(0); }

fork() - Multiple connection, multiple process
In this case, the server can now handle multiple connections successfully. If the first connection has a long processing time or the user is slow at entering data, the second connection may now continue freely without waiting in line. The downside to this approach is: 1) If the server must handle a large number of connections simultaneously, the server may run out of processes since each connection fork()s. 2) Since a fork() call duplicates variables, file descriptors etc., the server may run out of memory if each connection requires any significant processing.

Notice that the processing portion of the code has been moved into a function which is called when the fork() is executing within the child processes.


Fork Server

#include
#include
#include
#include
#include
#include
#include
#include
#include

#define ERROR(x) do { perror(x); exit(1); } while(0)

#define NAME 257
#define BUFSIZE 257
#define MAXPENDING 1

void handle_connection(int NS);

int main(int argc, char *argv[])
{
unsigned short port; /* Port to which server will bind */
char servhost[NAME]; /* Local host name */
struct sockaddr_in sock; /* INTERNET socket space */
struct hostent *server; /* Local host information */
int S; /* fd for socket */
int NS; /* fd for connected socket */
int pid; /* used to determine parent or child */

if (argc != 2)
{
fprintf(stderr,"usage: fork-server \n");
exit(1);
}

port = atoi(argv[1]);

/*
* Get socket - INTERNET DOMAIN - TCP
*/
if ((S = socket(AF_INET, SOCK_STREAM, 0)) < 0) ERROR("fork-server: socket"); /* * Obtain host name & network address */ gethostname(servhost, sizeof(servhost)); if ((server = gethostbyname(servhost)) == NULL) { fprintf(stderr,"%s: unknown host\n",servhost); exit(1); } /* * Bind to server address - in network byte order */ sock.sin_family = AF_INET; sock.sin_port = htons(port); memcpy(&sock.sin_addr, server->h_addr, server->h_length);

/*
* Bind socket to port/addr
*/
if (bind(S, (struct sockaddr *)&sock, sizeof(sock)) < 0) ERROR("server: bind"); /* * Listen on this socket */ if (listen(S,MAXPENDING) < 0) ERROR("server: listen"); while(1) { /* * Accept connections. Once connected, the client will be * connected on fd NS, a second and third parameter may be passed * to accept which will be filled in with information regarding the * client connection if desired. * * In this example, once connected the server is done with the * master socket (so closes it). */ if ((NS = accept(S,NULL,NULL)) < 0) ERROR("server: accept"); if((pid = fork()) < 0) ERROR("server: fork"); if(pid == 0) { handle_connection(NS); exit(0); } else close(NS); } return(0); } void handle_connection(int NS) { char buf[BUFSIZE]; /* Input buffer */ FILE *fp; /* Stream (converted file des.) */ /* * Using stdio library to read from socket */ if (!(fp = fdopen(NS,"r"))) { fprintf(stderr,">>> Error converting file des. to stream <<<\n"); exit(1); } while (fgets(buf,BUFSIZE,fp)) printf("%s", buf); /* * DONE - simply close() connection */ fclose(fp); close(NS); }

select() - Multiple connection, single process
The last case still has the benefits of fork() but does so in a single process. In this case, select() is used. In this case, each socket descriptor is monitored by select which determines if any of the sockets are ready for I/O. Rather than sitting idly waiting for input on sockets that are not ready, data is processed as it arrives. The drawback to this approach is that it is little trickier to implement and understand, but if you start with a basic case like this it can be quite simple to get the hang of it. When using select(), we need to specify the groups of sockets we wish to monitor. This is done using the fd_set which you can see in the source.


Select Server

#include
#include
#include
#include
#include
#include
#include
#include
#include

#define ERROR(x) do { perror(x); exit(1); } while(0)

#define NAME 257
#define BUFSIZE 257
#define MAXPENDING 1

void handle_connection(int NS, fd_set * activefds);

int main(int argc, char *argv[])
{
fd_set readfds, activefds; /* the set of read descriptors */
unsigned short port; /* Port to which server will bind */
char servhost[NAME]; /* Local host name */
struct sockaddr_in sock; /* INTERNET socket space */
struct hostent *server; /* Local host information */
int S; /* fd for socket */
int NS; /* fd for connected socket */
int i; /* counter to go through FDs in fdset */

if (argc != 2)
{
fprintf(stderr,"usage: select-server \n");
exit(1);
}

port = atoi(argv[1]);

/*
* Get socket - INTERNET DOMAIN - TCP
*/
if ((S = socket(AF_INET, SOCK_STREAM, 0)) < 0) ERROR("select-server: socket"); /* * Obtain host name & network address */ gethostname(servhost, sizeof(servhost)); if ((server = gethostbyname(servhost)) == NULL) { fprintf(stderr,"%s: unknown host\n",servhost); exit(1); } /* * Bind to server address - in network byte order */ sock.sin_family = AF_INET; sock.sin_port = htons(port); memcpy(&sock.sin_addr, server->h_addr, server->h_length);

/*
* Bind socket to port/addr
*/
if (bind(S, (struct sockaddr *)&sock, sizeof(sock)) < 0) ERROR("server: bind"); /* * Listen on this socket */ if (listen(S,MAXPENDING) < 0) ERROR("server: listen"); /* Initialize the set of active sockets. */ FD_ZERO (&activefds); FD_SET (S, &activefds); while(1) { /* Block until input arrives on one or more active sockets. */ readfds = activefds; if (select (FD_SETSIZE, &readfds, NULL, NULL, NULL) < 0) { perror ("select"); exit (EXIT_FAILURE); } /* Service all the sockets with input pending. */ for (i = 0; i < FD_SETSIZE; ++i) { if (FD_ISSET (i, &readfds)) { if (i == S) { /* Connection request on original socket. */ /* * Accept connections. Once connected, the client will be * connected on fd NS, a second and third parameter may be passed * to accept which will be filled in with information regarding the * client connection if desired. * * In this example, once connected the server is done with the * master socket (so closes it). */ if ((NS = accept(S,NULL,NULL)) < 0) ERROR("server: accept"); FD_SET(NS, &activefds); //add the new socket desc to our active connections set } else { /* Data arriving on an already-connected socket. */ handle_connection(i, &activefds); } } } /* //end of for */ } /* //end of while */ return(0); } void handle_connection(int NS, fd_set * activefds) { char buf[BUFSIZE]; /* Input buffer */ FILE *fp; /* Stream (converted file des.) */ /* * Using stdio library to read from socket */ if (!(fp = fdopen(NS,"r"))) { fprintf(stderr,">>> Error converting file des. to stream <<<\n"); exit(1); } //if fgets fails we have end of line, quit if(!(fgets(buf,BUFSIZE,fp))) { /* * DONE - simply close() connection */ fclose(fp); close(NS); FD_CLR(NS, activefds); } else printf("%s", buf); }

Additional Information
http://www.uoguelph.ca/~jernst/3110/cis3310-lab4.pdf
http://www.socs.uoguelph.ca/~dbm/teaching/CIS3110/
http://beej.us/guide/bgnet/
manpages!