Skip to contentSkip to conversation

uml_switch: configurable network delay

PATCH Superseded Morten Hustveit 2 participants 4 messages Nov 25, 2010 – Dec 2, 2010 User-mode Linux (UML)

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 uml_switch: configurable network delay

Add the "-delay" option to configure network delay in milliseconds

When using UML to test networking code, it would be helpful if the switch daemon could provide configurable delay, in order to simulate ordinary network delay. This patch provides that feature by storing scheduled packets in a linked list, and using the poll() timeout parameter to pop items from the list at the appropriate time.

Presently, the size of the backlog is unbounded, but I think this is acceptable for a testing/debugging feature.

PS: I could not find a public SCM with the most recent development version of the utilities.
---

uml_switch/port.c +138 -5
diff -ur tools-20070815/uml_switch/port.c tools-20070815.my/uml_switch/port.c
@@ -8,6 +8,8 @@
 #include "hash.h"
 #include "port.h"
 
+typedef void (*sender_t)(int fd, void *packet, int len, void *data);
+
 struct packet {
   struct {
     unsigned char dest[ETH_ALEN];
@@ -24,15 +26,57 @@
   void *data;
   int data_len;
   unsigned char src[ETH_ALEN];
-  void (*sender)(int fd, void *packet, int len, void *data);
+  sender_t sender;
 };
 
 static struct port *head = NULL;
 
+struct queued_packet {
+  struct queued_packet *next;
+
+  struct timeval delivery_time;
+
+  sender_t sender;
+  void *data;
+
+  int fd;
+  int len;
+  struct sockaddr_un sock;
+
+  char packet[1];
+};
+
+static struct queued_packet *first_packet = NULL;
+static struct queued_packet *last_packet = NULL;
+
+extern int simulated_delay;
+
 #define IS_BROADCAST(addr) ((addr[0] & 1) == 1)
 
 static void free_port(struct port *port)
 {
+  struct queued_packet *qp, *prev = 0, *next;
+
+  /* Remove queued packets belonging to `port' */
+  qp = first_packet;
+
+  while (qp) {
+    if (qp->fd == port->control) {
+      next = qp->next;
+      if (prev)
+	prev->next = next;
+      else
+	first_packet = next;
+      free (qp);
+      qp = next;
+    } else {
+      prev = qp;
+      qp = qp->next;
+    }
+  }
+
+  last_packet = prev;
+
   if(port->prev) port->prev->next = port->next;
   else head = port->next;
   if(port->next) port->next->prev = port->prev;
@@ -83,6 +127,76 @@
   update_entry_time(p->header.src);
 }
 
+static void send_dst_packet(sender_t sender, int fd, void *packet, int len, void *data)
+{
+  struct queued_packet *qp;
+  if (!simulated_delay) {
+    sender(fd, data, len, data);
+  } else {
+    qp = malloc(sizeof(*qp) - sizeof(qp->packet) + len);
+
+    if(qp == NULL) {
+      perror("malloc");
+      return;
+    }
+
+    qp->next = 0;
+
+    gettimeofday(&qp->delivery_time, 0);
+
+    qp->delivery_time.tv_usec += simulated_delay * 1000;
+
+    if (qp->delivery_time.tv_usec > 1000000) {
+      qp->delivery_time.tv_sec += qp->delivery_time.tv_usec / 1000000;
+      qp->delivery_time.tv_usec %= 1000000;
+    }
+
+    qp->sender = sender;
+    qp->data = data;
+
+    qp->fd = fd;
+    qp->len = len;
+    memcpy(qp->packet, packet, len);
+
+    if (!first_packet)
+      first_packet = qp;
+    else
+      last_packet->next = qp;
+
+    last_packet = qp;
+  }
+}
+
+void process_queued_packets()
+{
+  struct queued_packet *qp;
+  struct timeval now;
+  int err;
+
+  if (!first_packet)
+    return;
+
+  gettimeofday(&now, 0);
+
+  while (first_packet)
+    {
+      if (first_packet->delivery_time.tv_sec > now.tv_sec
+	  || (first_packet->delivery_time.tv_sec == now.tv_sec
+	      && first_packet->delivery_time.tv_usec > now.tv_usec))
+	break;
+
+      qp = first_packet;
+
+      if (qp == last_packet)
+	last_packet = 0;
+      first_packet = qp->next;
+
+      (*qp->sender)(qp->fd, qp->packet, qp->len, qp->data);
+
+      free(qp);
+    }
+}
+
 static void send_dst(struct port *port, struct packet *packet, int len, 
 		     int hub)
 {
@@ -104,10 +218,11 @@
       /* don't send it back the port it came in */
       if(p == port) continue;
 
-      (*p->sender)(p->control, packet, len, p->data);
+      send_dst_packet(p->sender, p->control, packet, len, p->data);  
     }
   }
-  else (*target->sender)(target->control, packet, len, target->data);
+  else
+    send_dst_packet(target->sender, target->control, packet, len, target->data);  
 }
 
 static void handle_data(int fd, int hub, struct packet *packet, int len, 
@@ -148,8 +263,7 @@
   handle_data(fd, hub, &packet, len, NULL, match_tap);
 }
 
-int setup_port(int fd, void (*sender)(int fd, void *packet, int len, 
-				      void *data), void *data, int data_len)
+int setup_port(int fd, sender_t sender)
 {
   struct port *port;
 
@@ -188,6 +302,25 @@
   }
 }
 
+int next_queued_packet()
+{
+  struct timeval now;
+  int result = -1;
+
+  if (first_packet)
+    {
+      gettimeofday(&now, 0);
+
+      result = (first_packet->delivery_time.tv_sec - now.tv_sec) * 1000
+	+ (first_packet->delivery_time.tv_usec - now.tv_usec) / 1000;
+
+      if (result < 0)
+	result = 0;
+    }
+
+  return result;
+}
+
 static int match_sock(int port_fd, int data_fd, void *port_data, 
 		      int port_data_len, void *data)
 {
uml_switch/port.h +2 -0
diff -ur tools-20070815/uml_switch/port.h tools-20070815.my/uml_switch/port.h
@@ -16,5 +16,7 @@
 		      int data_len);
 extern void handle_tap_data(int fd, int hub);
 extern void handle_sock_data(int fd, int hub);
+extern int next_queued_packet();
+extern void process_queued_packets();
 
 #endif
uml_switch/uml_switch.c +14 -3
diff -ur tools-20070815/uml_switch/uml_switch.c tools-20070815.my/uml_switch/uml_switch.c
@@ -26,6 +26,7 @@
 
 static int hub = 0;
 static int compat_v0 = 0;
+int simulated_delay = 0;
 
 enum request_type { REQ_NEW_CONTROL };
 
@@ -392,9 +393,10 @@
   tap_str = "[ -tap tap-device ]";
 #endif
 
-  fprintf(stderr, "Usage : %s [ -unix control-socket ] [ -hub ] %s\n"
+  fprintf(stderr, "Usage : %s [ -unix control-socket ] [ -hub ] "
+	  "[ -delay ms ] %s\n"
 	  "or : %s -compat-v0 [ -unix control-socket data-socket ] "
-	  "[ -hub ] %s\n", prog, tap_str, prog, tap_str);
+	  "[ -hub ] [ -delay ms ] %s\n", prog, tap_str, prog, tap_str);
 
   exit(1);
 }
@@ -455,6 +457,13 @@
       argc--;
       argv++;
     }
+    else if(!strcmp(argv[0], "-delay")){
+      if(argc < 2) 
+	Usage();
+      simulated_delay = atoi(argv[1]);
+      argv += 2;
+      argc -= 2;
+    }
     else Usage();
   }
 
@@ -521,7 +530,7 @@
   while(1){
     char buf[128];
 
-    n = poll(fds, nfds, -1);
+    n = poll(fds, nfds, next_queued_packet());
     if(n < 0){
       if(errno == EINTR) continue;
       perror("poll");
@@ -562,6 +571,8 @@
 	else close_descriptor(fds[i].fd);
       }
     }
+
+    process_queued_packets();
   }
  out:
   cleanup();

------------------------------------------------------------------------------
Increase Visibility of Your 3D Game App & Earn a Chance To Win $500! Tap into the largest installed PC base & get more eyes on your game by optimizing for Intel(R) Graphics Technology. Get started today with the Intel(R) Software Partner Program. Five $500 cash prizes are up for grabs.
http://p.sf.net/sfu/intelisp-dev2dev
_______________________________________________
User-mode-linux-devel mailing list [email protected]
https://lists.sourceforge.net/lists/listinfo/user-mode-linux-devel

#1 lore raw
Quoted 1 line · Morten Hustveit

Add the "-delay" option to configure network delay in milliseconds

If you use vde instead of uml_switch, vde provides wirefilter
which implements packet delay as well as several other networking testing features:

packet loss
packet dup
bandwidth limit
interface speed
channel capacity
channel noise
fifoness errors

renzo

------------------------------------------------------------------------------
Increase Visibility of Your 3D Game App & Earn a Chance To Win $500! Tap into the largest installed PC base & get more eyes on your game by optimizing for Intel(R) Graphics Technology. Get started today with the Intel(R) Software Partner Program. Five $500 cash prizes are up for grabs.
http://p.sf.net/sfu/intelisp-dev2dev
_______________________________________________
User-mode-linux-devel mailing list [email protected]
https://lists.sourceforge.net/lists/listinfo/user-mode-linux-devel

#2 lore raw
Quoted 1 line · Morten Hustveit
Quoted 1 line

Add the "-delay" option to configure network delay in milliseconds

Quoted 2 lines · Renzo Davoli

If you use vde instead of uml_switch, vde provides wirefilter
which implements packet delay as well as several other networking testing features:

That looks nice, but I couldn't understand how to use it. It seemed to me that to do something like

uml_switch -delay 100

with vde, you would need to write several long command lines and perhaps a configuration file. I understand this is not a problem for most people, but I would like to have a simple way of doing things as well. I'm hoping to contribute other similarly simple testing features to uml_switch, but first I need to know whether such changes are welcome.

------------------------------------------------------------------------------
Increase Visibility of Your 3D Game App & Earn a Chance To Win $500! Tap into the largest installed PC base & get more eyes on your game by optimizing for Intel(R) Graphics Technology. Get started today with the Intel(R) Software Partner Program. Five $500 cash prizes are up for grabs.
http://p.sf.net/sfu/intelisp-dev2dev
_______________________________________________
User-mode-linux-devel mailing list [email protected]
https://lists.sourceforge.net/lists/listinfo/user-mode-linux-devel

#3 lore raw

PATCH v2 1/1 uml_switch: configurable network delay

In reply to Morten Hustveit

Add the "-delay" option to configure network delay in milliseconds

When using UML to test networking code, it would be helpful if the switch daemon could provide configurable delay, in order to simulate ordinary network delay. This patch provides that feature by storing scheduled packets in a linked list, and using the poll() timeout parameter to pop items from the list at the appropriate time.

The size of the backlog is unbounded, but I think this is acceptable for a testing/debugging feature.
---
Changes for v2:
- The previous version was incorrectly submitted, and didn't even compile. Apologies to anyone who tried to use it.

uml_switch/port.c +136 -5
diff -ur tools-20070815/uml_switch/port.c tools-20070815.mortehu/uml_switch/port.c
@@ -24,15 +24,57 @@
   void *data;
   int data_len;
   unsigned char src[ETH_ALEN];
-  void (*sender)(int fd, void *packet, int len, void *data);
+  sender_t sender;
 };
 
 static struct port *head = NULL;
 
+struct queued_packet {
+  struct queued_packet *next;
+
+  struct timeval delivery_time;
+
+  sender_t sender;
+  void *data;
+
+  int fd;
+  int len;
+  struct sockaddr_un sock;
+
+  char packet[1];
+};
+
+static struct queued_packet *first_packet = NULL;
+static struct queued_packet *last_packet = NULL;
+
+extern int simulated_delay;
+
 #define IS_BROADCAST(addr) ((addr[0] & 1) == 1)
 
 static void free_port(struct port *port)
 {
+  struct queued_packet *qp, *prev = 0, *next;
+
+  /* Remove queued packets belonging to `port' */
+  qp = first_packet;
+
+  while (qp) {
+    if (qp->fd == port->control) {
+      next = qp->next;
+      if (prev)
+	prev->next = next;
+      else
+	first_packet = next;
+      free (qp);
+      qp = next;
+    } else {
+      prev = qp;
+      qp = qp->next;
+    }
+  }
+
+  last_packet = prev;
+
   if(port->prev) port->prev->next = port->next;
   else head = port->next;
   if(port->next) port->next->prev = port->prev;
@@ -83,6 +125,76 @@
   update_entry_time(p->header.src);
 }
 
+static void send_dst_packet(sender_t sender, int fd, void *packet, int len, void *data)
+{
+  struct queued_packet *qp;
+  if (!simulated_delay) {
+    sender(fd, packet, len, data);
+  } else {
+    qp = malloc(sizeof(*qp) - sizeof(qp->packet) + len);
+
+    if(qp == NULL) {
+      perror("malloc");
+      return;
+    }
+
+    qp->next = 0;
+
+    gettimeofday(&qp->delivery_time, 0);
+
+    qp->delivery_time.tv_usec += simulated_delay * 1000;
+
+    if (qp->delivery_time.tv_usec > 1000000) {
+      qp->delivery_time.tv_sec += qp->delivery_time.tv_usec / 1000000;
+      qp->delivery_time.tv_usec %= 1000000;
+    }
+
+    qp->sender = sender;
+    qp->data = data;
+
+    qp->fd = fd;
+    qp->len = len;
+    memcpy(qp->packet, packet, len);
+
+    if (!first_packet)
+      first_packet = qp;
+    else
+      last_packet->next = qp;
+
+    last_packet = qp;
+  }
+}
+
+void process_queued_packets()
+{
+  struct queued_packet *qp;
+  struct timeval now;
+  int err;
+
+  if (!first_packet)
+    return;
+
+  gettimeofday(&now, 0);
+
+  while (first_packet)
+    {
+      if (first_packet->delivery_time.tv_sec > now.tv_sec
+	  || (first_packet->delivery_time.tv_sec == now.tv_sec
+	      && first_packet->delivery_time.tv_usec > now.tv_usec))
+	break;
+
+      qp = first_packet;
+
+      if (qp == last_packet)
+	last_packet = 0;
+      first_packet = qp->next;
+
+      (*qp->sender)(qp->fd, qp->packet, qp->len, qp->data);
+
+      free(qp);
+    }
+}
+
 static void send_dst(struct port *port, struct packet *packet, int len, 
 		     int hub)
 {
@@ -104,10 +216,11 @@
       /* don't send it back the port it came in */
       if(p == port) continue;
 
-      (*p->sender)(p->control, packet, len, p->data);
+      send_dst_packet(p->sender, p->control, packet, len, p->data);  
     }
   }
-  else (*target->sender)(target->control, packet, len, target->data);
+  else
+    send_dst_packet(target->sender, target->control, packet, len, target->data);  
 }
 
 static void handle_data(int fd, int hub, struct packet *packet, int len, 
@@ -148,8 +261,7 @@
   handle_data(fd, hub, &packet, len, NULL, match_tap);
 }
 
-int setup_port(int fd, void (*sender)(int fd, void *packet, int len, 
-				      void *data), void *data, int data_len)
+int setup_port(int fd, sender_t sender, void *data, int data_len)
 {
   struct port *port;
 
@@ -188,6 +300,25 @@
   }
 }
 
+int next_queued_packet()
+{
+  struct timeval now;
+  int result = -1;
+
+  if (first_packet)
+    {
+      gettimeofday(&now, 0);
+
+      result = (first_packet->delivery_time.tv_sec - now.tv_sec) * 1000
+	+ (first_packet->delivery_time.tv_usec - now.tv_usec) / 1000;
+
+      if (result < 0)
+	result = 0;
+    }
+
+  return result;
+}
+
 static int match_sock(int port_fd, int data_fd, void *port_data, 
 		      int port_data_len, void *data)
 {
uml_switch/port.h +5 -3
diff -ur tools-20070815/uml_switch/port.h tools-20070815.mortehu/uml_switch/port.h
@@ -8,13 +8,15 @@
 #include <sys/socket.h>
 #include <sys/un.h>
 
+typedef void (*sender_t)(int fd, void *packet, int len, void *data);
+
 extern int handle_port(int fd);
 extern void close_port(int fd);
 extern int setup_sock_port(int fd, struct sockaddr_un *name, int data_fd);
-extern int setup_port(int fd, void (*sender)(int fd, void *packet, int len, 
-					     void *data), void *data, 
-		      int data_len);
+extern int setup_port(int fd, sender_t sender, void *data, int data_len);
 extern void handle_tap_data(int fd, int hub);
 extern void handle_sock_data(int fd, int hub);
+extern int next_queued_packet();
+extern void process_queued_packets();
 
 #endif
uml_switch/uml_switch.c +14 -3
diff -ur tools-20070815/uml_switch/uml_switch.c tools-20070815.mortehu/uml_switch/uml_switch.c
@@ -26,6 +26,7 @@
 
 static int hub = 0;
 static int compat_v0 = 0;
+int simulated_delay = 0;
 
 enum request_type { REQ_NEW_CONTROL };
 
@@ -392,9 +393,10 @@
   tap_str = "[ -tap tap-device ]";
 #endif
 
-  fprintf(stderr, "Usage : %s [ -unix control-socket ] [ -hub ] %s\n"
+  fprintf(stderr, "Usage : %s [ -unix control-socket ] [ -hub ] "
+	  "[ -delay ms ] %s\n"
 	  "or : %s -compat-v0 [ -unix control-socket data-socket ] "
-	  "[ -hub ] %s\n", prog, tap_str, prog, tap_str);
+	  "[ -hub ] [ -delay ms ] %s\n", prog, tap_str, prog, tap_str);
 
   exit(1);
 }
@@ -455,6 +457,13 @@
       argc--;
       argv++;
     }
+    else if(!strcmp(argv[0], "-delay")){
+      if(argc < 2) 
+	Usage();
+      simulated_delay = atoi(argv[1]);
+      argv += 2;
+      argc -= 2;
+    }
     else Usage();
   }
 
@@ -521,7 +530,7 @@
   while(1){
     char buf[128];
 
-    n = poll(fds, nfds, -1);
+    n = poll(fds, nfds, next_queued_packet());
     if(n < 0){
       if(errno == EINTR) continue;
       perror("poll");
@@ -562,6 +571,8 @@
 	else close_descriptor(fds[i].fd);
       }
     }
+
+    process_queued_packets();
   }
  out:
   cleanup();

------------------------------------------------------------------------------
Increase Visibility of Your 3D Game App & Earn a Chance To Win $500! Tap into the largest installed PC base & get more eyes on your game by optimizing for Intel(R) Graphics Technology. Get started today with the Intel(R) Software Partner Program. Five $500 cash prizes are up for grabs.
http://p.sf.net/sfu/intelisp-dev2dev
_______________________________________________
User-mode-linux-devel mailing list [email protected]
https://lists.sourceforge.net/lists/listinfo/user-mode-linux-devel