Subject: Re: [PATCH 0/2] netem: trace enhancement: iproute

From: Ariane Keller <[email-masked]>
Date: Sun, 23 Dec 2007 20:54:56 +0100

The iproute patch is to big to send on the mailing list,
since the distribution data have changed the directory.
For ease of discussion I add the important changes in this mail.

signed-of-by: Ariane Keller <[email-masked]

---

diff -uprN iproute2-2.6.23/netem/trace/flowseed.c 
iproute2-2.6.23_buf/netem/trace/flowseed.c
--- iproute2-2.6.23/netem/trace/flowseed.c	1970-01-01 01:00:00.000000000 
+0100
+++ iproute2-2.6.23_buf/netem/trace/flowseed.c	2007-12-12 
08:43:01.000000000 +0100
@@ -0,0 +1,209 @@
+/* flowseed.c    flowseedprocess to deliver values for packet delay,
+ *               duplication, loss and curruption form userspace to netem
+ *
+ *               This program is free software; you can redistribute it 
and/or
+ *               modify it under the terms of the GNU General Public 
License
+ *               as published by the Free Software Foundation; either 
version
+ *               2 of the License, or (at your option) any later version.
+ *
+ *  Authors:     Ariane Keller <[email-masked]> ETH Zurich
+ *               Rainer Baumann <[email-masked]> ETH Zurich
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <signal.h>
+
+#include "utils.h"
+#include "linux/pkt_sched.h"
+
+#define DATA_PACKAGE 4000
+#define DATA_PACKAGE_ID DATA_PACKAGE + sizeof(unsigned int) + sizeof(int)
+#define TCA_BUF_MAX  (64*1024)
+/* maximal amount of parallel flows */
+struct rtnl_handle rth;
+unsigned int loop;
+int infinity = 0;
+int fdflowseed;
+char *sendpkg;
+int fid;
+int initialized = 0;
+int semid;
+int moreData = 1, r = 0, rold = 0;
+FILE * file;
+
+
+int printfct(const struct sockaddr_nl *who,
+		       struct nlmsghdr *n,
+		       void *arg)
+{
+	struct {
+		struct nlmsghdr 	n;
+		struct tcmsg 		t;
+		char   			buf[TCA_BUF_MAX];
+	} req;
+	struct tcmsg *t = NLMSG_DATA(n);
+	struct rtattr *tail = NULL;
+	struct tc_netem_qopt opt;
+	memset(&opt, 0, sizeof(opt));
+
+	if(n->nlmsg_type == RTM_DELQDISC) {
+		goto outerr;
+	}
+	else if(n->nlmsg_type == RTM_NEWQDISC){
+		initialized = 1;
+	
+		memset(&req, 0, sizeof(req));
+		req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
+		req.n.nlmsg_flags = NLM_F_REQUEST;
+		req.n.nlmsg_type = RTM_NEWQDISC;
+		req.t.tcm_family = AF_UNSPEC;
+		req.t.tcm_handle = t->tcm_handle;
+		req.t.tcm_parent = t->tcm_parent;
+		req.t.tcm_ifindex = t->tcm_ifindex;
+
+		tail = NLMSG_TAIL(&req.n);
+again:
+		if (loop <= 0 && !infinity){
+			goto out;
+		}
+		if ((r = read(fdflowseed, sendpkg + rold, DATA_PACKAGE - rold)) >= 0) {
+			if (r + rold < DATA_PACKAGE) {
+			/* Tail of input file reached,
+			   set rest at start from next iteration */
+				rold = r;
+				fprintf(file, "flowseed: at end of file.\n");
+
+				if (lseek(fdflowseed, 0L, SEEK_SET) < 0){
+					perror("lseek reset");
+					goto out;
+				}
+				goto again;
+			}
+			r = 0;
+			rold = 0;
+			memcpy(sendpkg + DATA_PACKAGE, &fid, sizeof(int));
+			memcpy(sendpkg + DATA_PACKAGE + sizeof(int), &moreData, sizeof(int));
+		
+			/* opt has to be added for each netem request */
+			if (addattr_l(&req.n, TCA_BUF_MAX, TCA_OPTIONS, &opt, sizeof(opt)) < 0){
+				perror("add options");
+				return -1;
+			}
+
+			if(addattr_l(&req.n, TCA_BUF_MAX, TCA_NETEM_TRACE_DATA, sendpkg, 
DATA_PACKAGE_ID) < 0){
+				perror("add data\n");
+				return -1;
+			}
+
+			tail->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)tail;
+
+			if(rtnl_send(&rth, (char*)&req, req.n.nlmsg_len) < 0){
+				perror("send data");
+				return -1;
+			}
+			return 0;
+		}
+	}
+/* no more data, what to do? we send a notification to the kernel module */
+out:
+	fprintf(stderr, "flowseed: Tail of input file reached. Exit.\n");
+	fprintf(file, "flowseed: Tail of input file reached. Exit.\n");
+	moreData = 0;
+	memcpy(sendpkg + DATA_PACKAGE, &fid, sizeof(int));
+	memcpy(sendpkg + DATA_PACKAGE + sizeof(int), &moreData, sizeof(int));
+	if (addattr_l(&req.n, TCA_BUF_MAX, TCA_OPTIONS, &opt, sizeof(opt)) < 0){
+		perror("add options");
+		goto outerr;
+	}
+	if(addattr_l(&req.n, TCA_BUF_MAX, TCA_NETEM_TRACE_DATA, sendpkg, 
DATA_PACKAGE_ID) < 0){
+		perror("add data\n");
+		goto outerr;
+	}
+	
+	tail->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)tail;
+
+	if(rtnl_send(&rth, (char*)&req, req.n.nlmsg_len) < 0){
+		perror("rtnl_send");
+	}
+outerr:
+	fprintf(file, "flowseed: outerr Exit.\n");
+	fclose(file);
+	close(fdflowseed);
+	free(sendpkg);
+	rtnl_close(&rth);
+	exit(0);
+}
+
+void sigact(int signal){
+	if(initialized){
+		return;
+	}
+	else{
+		fprintf(stderr, "flowseed: not yet initialized. Exit\n");
+		exit(0);
+	}
+}
+int main(int argc, char *argv[])
+{
+	struct sembuf buf;
+        file = fopen("flowseedout.txt", "a+");
+	fprintf(file, "flowseed: initial msg.\n");
+
+	if (argc < 3) {
+		printf("usage: <tracefilename> <loop>");
+		return -1;
+	}
+	loop = strtoul(argv[2], NULL, 10);
+	if (loop == 0)
+		infinity = 1;
+
+	if ((fdflowseed = open(argv[1], O_RDONLY, 0)) < 0) {
+		perror("cannot open tracefile");
+		return -1;
+	}
+
+	fid = getpid();
+	sendpkg = malloc(DATA_PACKAGE_ID);
+
+	if (rtnl_open(&rth, 0) < 0) {
+		perror("Cannot open rtnetlink");
+		return -1;
+	}
+	ll_init_map(&rth);
+	/* we are ready to receive notifications */
+	if((semid = semget(0x12345678, 1, IPC_CREAT | 0666))<0){
+		perror("semget");
+		return -1;
+	}
+	buf.sem_num = 0;
+	buf.sem_op = +1;
+	buf.sem_flg = SEM_UNDO;
+	if(semop(semid, &buf, 1) < 0){
+		perror("semop");
+		return -1;
+	}
+	/* if the user typed an invalid command we cannot detect this
+ 	 * therefore we set a timer, if the timer expires before we receive
+ 	 * any message from the kernel module, we assume there was an
+ 	 * error and quit.
+ 	 */
+	signal(SIGALRM, sigact);
+	alarm(3);
+
+	/* listen to notifications from kernel */
+	if (rtnl_listen(&rth, printfct, NULL) < 0) {
+		perror("listen");
+		rtnl_close(&rth);
+		exit(2);
+	}
+	return 0;
+}

diff -uprN iproute2-2.6.23/tc/q_netem.c iproute2-2.6.23_buf/tc/q_netem.c
--- iproute2-2.6.23/tc/q_netem.c	2007-10-16 23:27:42.000000000 +0200
+++ iproute2-2.6.23_buf/tc/q_netem.c	2007-12-21 19:08:19.000000000 +0100
@@ -6,7 +6,12 @@
   *		as published by the Free Software Foundation; either version
   *		2 of the License, or (at your option) any later version.
   *
+ *		README files: 	iproute2/netem/distribution
+ *				iproute2/netem/trace
+ *
   * Authors:	Stephen Hemminger <[email-masked]>
+ *              netem trace: Ariane Keller <[email-masked]> ETH Zurich
+ *                           Rainer Baumann <[email-masked]> ETH Zurich
   *
   */

@@ -20,6 +25,9 @@
  #include <arpa/inet.h>
  #include <string.h>
  #include <errno.h>
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>

  #include "utils.h"
  #include "tc_util.h"
@@ -34,7 +42,8 @@ static void explain(void)
  "                 [ drop PERCENT [CORRELATION]] \n" \
  "                 [ corrupt PERCENT [CORRELATION]] \n" \
  "                 [ duplicate PERCENT [CORRELATION]]\n" \
-"                 [ reorder PRECENT [CORRELATION] [ gap DISTANCE ]]\n");
+"                 [ reorder PRECENT [CORRELATION] [ gap DISTANCE ]]\n" \
+"                 [ trace PATH buf NR_BUFS loops NR_LOOPS [DEFAULT]\n");
  }

  static void explain1(const char *arg)
@@ -42,6 +51,7 @@ static void explain1(const char *arg)
  	fprintf(stderr, "Illegal \"%s\"\n", arg);
  }

+#define FLOWPATH "/usr/local/bin/flowseed"
  #define usage() return(-1)

  /*
@@ -129,6 +139,7 @@ static int netem_parse_opt(struct qdisc_
  	struct tc_netem_corr cor;
  	struct tc_netem_reorder reorder;
  	struct tc_netem_corrupt corrupt;
+	struct tc_netem_trace traceopt;
  	__s16 *dist_data = NULL;
  	int present[__TCA_NETEM_MAX];

@@ -137,8 +148,12 @@ static int netem_parse_opt(struct qdisc_
  	memset(&cor, 0, sizeof(cor));
  	memset(&reorder, 0, sizeof(reorder));
  	memset(&corrupt, 0, sizeof(corrupt));
+	memset(&traceopt, 0, sizeof(traceopt));
  	memset(present, 0, sizeof(present));
-
+	if (argc == 0) {
+		explain();
+		return -1;
+	}
  	while (argc > 0) {
  		if (matches(*argv, "limit") == 0) {
  			NEXT_ARG();
@@ -164,7 +179,7 @@ static int netem_parse_opt(struct qdisc_
  				if (NEXT_IS_NUMBER()) {
  					NEXT_ARG();
  					++present[TCA_NETEM_CORR];
-					if (get_percent(&cor.delay_corr,							*argv)) {
+					if (get_percent(&cor.delay_corr, *argv)) {
  						explain1("latency");
  						return -1;
  					}
@@ -243,6 +258,75 @@ static int netem_parse_opt(struct qdisc_
  		} else if (strcmp(*argv, "help") == 0) {
  			explain();
  			return -1;
+		} else if (strcmp(*argv, "trace") == 0) {
+			int fd;
+			int execvl;
+			char *filename;
+			int pid;
+		
+			/*get ticks correct since tracefile is in us,
+			 *and ticks may not be equal to us
+			 */
+			get_ticks(&traceopt.ticks, "1000us");
+			NEXT_ARG();
+			filename = *argv;
+			if ((fd = open(filename, O_RDONLY, 0)) < 0) {
+				fprintf(stderr, "Cannot open trace file %s! \n", filename);
+				return -1;
+			}
+			close(fd);
+			NEXT_ARG();
+			if(strcmp(*argv, "buf") == 0) {
+				NEXT_ARG();
+				traceopt.nr_bufs = atoi(*argv);
+			}
+			else{
+				explain();
+				return -1;
+			}
+			NEXT_ARG();
+			if (strcmp(*argv, "loops") == 0 && NEXT_IS_NUMBER()) {
+				NEXT_ARG();
+				/*child will load tracefile to kernel */
+				switch (pid = fork()) {
+				case -1:{
+					fprintf(stderr,
+						"Cannot fork\n");
+					return -1;
+					}
+				case 0:{
+					execvl = execl(FLOWPATH, "flowseed", filename, *argv, NULL);
+					if (execvl < 0) {
+						fprintf(stderr,
+						"starting child failed\n");
+						return -1;
+					}
+					}
+				default:{
+					/* parent has to wait until child has done rtnl_open.
+ 					 * otherwise the kernel module cannot send a notification
+ 					 * to the child
+ 					 */
+					int semid = semget(0x12345678, 1, IPC_CREAT | 0666);
+					struct sembuf buf;
+					buf.sem_num = 0;
+					buf.sem_op = -1;
+					buf.sem_flg = SEM_UNDO;
+					semop(semid, &buf, 1);
+					semctl(semid, 0, IPC_RMID);
+					}
+				}
+			}
+			else {
+				explain();
+				return -1;
+			}
+			traceopt.def = 0;
+			if (NEXT_IS_NUMBER()) {
+				NEXT_ARG();
+				traceopt.def = atoi(*argv);
+			}
+			traceopt.fid = pid;
  		} else {
  			fprintf(stderr, "What is \"%s\"?\n", *argv);
  			explain();
@@ -291,7 +375,13 @@ static int netem_parse_opt(struct qdisc_
  			      dist_data, dist_size*sizeof(dist_data[0])) < 0)
  			return -1;
  	}
-	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
+	if (traceopt.fid) {
+		if (addattr_l(n, TCA_BUF_MAX, TCA_NETEM_TRACE, &traceopt,
+		     sizeof(traceopt)) < 0)
+			return -1;
+	}
+
+	tail->rta_len = (void *)NLMSG_TAIL(n) - (void *)tail;
  	return 0;
  }

@@ -300,6 +390,8 @@ static int netem_print_opt(struct qdisc_
  	const struct tc_netem_corr *cor = NULL;
  	const struct tc_netem_reorder *reorder = NULL;
  	const struct tc_netem_corrupt *corrupt = NULL;
+	const struct tc_netem_trace *traceopt = NULL;
+	const struct tc_netem_stats *tracestats = NULL;
  	struct tc_netem_qopt qopt;
  	int len = RTA_PAYLOAD(opt) - sizeof(qopt);
  	SPRINT_BUF(b1);
@@ -333,9 +425,31 @@ static int netem_print_opt(struct qdisc_
  				return -1;
  			corrupt = RTA_DATA(tb[TCA_NETEM_CORRUPT]);
  		}
+		if (tb[TCA_NETEM_TRACE]) {
+			if (RTA_PAYLOAD(tb[TCA_NETEM_TRACE]) < sizeof(*traceopt))
+				return -1;
+			traceopt = RTA_DATA(tb[TCA_NETEM_TRACE]);
+		}
+		if (tb[TCA_NETEM_STATS]) {
+			if (RTA_PAYLOAD(tb[TCA_NETEM_STATS]) < sizeof(*tracestats))
+				return -1;
+			tracestats = RTA_DATA(tb[TCA_NETEM_STATS]);
+		}
  	}

  	fprintf(f, "limit %d", qopt.limit);
+	if (traceopt && traceopt->fid) {
+		fprintf(f, " trace\n");
+
+		fprintf(f, "packetcount= %d\n", tracestats->packetcount);
+		fprintf(f, "packetok= %d\n", tracestats->packetok);
+		fprintf(f, "normaldelay= %d\n", tracestats->normaldelay);
+		fprintf(f, "drops= %d\n", tracestats->drops);
+		fprintf(f, "dupl= %d\n", tracestats->dupl);
+		fprintf(f, "corrupt= %d\n", tracestats->corrupt);
+		fprintf(f, "novaliddata= %d\n", tracestats->novaliddata);
+		fprintf(f, "bufferreload= %d\n", tracestats->reloadbuffer);
+		}

  	if (qopt.latency) {
  		fprintf(f, " delay %s", sprint_ticks(qopt.latency, b1));