Turning a netgear r7000 into an augmented traffic control router

At work we’re developing apps that are being used in developing countries and half of the office works out of Vancouver where our networks are very good. Unfortunately, this means that we often don’t think about user experience problems and bugs that occur only when the app is operating with a poor quality connection.

To combat this, we are taking motivation from Facebook, which offers “2G Tuesdays” to employees so they can experience what it’s like for people in other parts of the world. Facebook also released a tool call Augmented Traffic Control which allows you to simulate these types of conditions with your own equipment.

At our office, we’d like to have a dedicated device that provides this – and I had an old netgear R7000 router at home. Here’s a bit of a guide on how to get everything up and running.

(more…)

Ubuntu / Raspian / Rapsberry Pi Connection Problems

So today I was messing around with a Raspberry Pi using Raspian. It turns out if you have an ethernet connection without Internet and a Wi-Fi connection with Internet, the Pi / Raspian is too dumb to figure out that it should use the Wi-Fi connection for things like dns lookups and web traffic. Slightly annoying. I’m sure there’s a way to fix this – but time is not on my side today.

Update: It turns out it’s more of a widespread linux problem. This morning on Ubuntu I tried connecting one Wi-Fi adapter to an AP that was not connected to the Internet and a second one that was. It seemed that again the traffic tried to take the path through the first adapter.

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!

Ns-3.2 on Ubuntu 8.04 Hardy Heron

For the next stage of my thesis instead of using the well known ns-2 simulator as originally planned, I have opted to instead use ns-3. I have chosen this particular environment because it seems to be designed from the ground up to be a wireless simulation tool while ns-2 was originally designed solely for wired network simulation. I have made some use of ns-2 however, so if you are looking for a guide to getting that working in Ubuntu see my post “NS-2.33 on Ubuntu 8.04 Hardy Heron“. In addition to this, ns-3 no longer uses the tcl scripting language. Instead the choice of pure c++ or a combination of c++ and python may be used to define the simulation parameters. I prefer this approach because I have no desire to learn tcl and find the c++ ns-3 ot be more intuitive than ns-2. Additionally, this guide will be used as a reference for myself while I learn the ns-3 environment. Check back for regular updates and useful infromation.

(more…)

DD-Wrt Micro on Linksys WRT54Gv6

WRT54Gv6

Today on a whim I decided to pickup a Linksys WRT54Gv6 wireless router from Staples. It was on sale for $20 off and looked like the last one there. I’ve been looking at the DD-Wrt software for some time since I have an old junky wireless router buried in my closet at home. Unfortunately the old router I have cannot run DD-Wrt so when I saw this one on sale I thought I’d give the WRT54G a try.

(more…)