1 /*****************************************************************************
2  *   main.c - the body for RUDE
3  *
4  *   Copyright (C) 1999 Juha Laine and Sampo Saaristo
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  *
20  *   Authors:      Juha Laine     <james@cs.tut.fi>
21  *                 Sampo Saaristo <sambo@cc.tut.fi>
22  *
23  *****************************************************************************/
24 #include <config.h>
25 #include <rude.h>
26 
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <signal.h>
30 #include <errno.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <fcntl.h>
34 #include <sys/mman.h>
35 #include <sys/time.h>
36 #include <sys/wait.h>
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <sched.h>
40 #include <arpa/inet.h>
41 #include <netinet/in.h>
42 
43 
44 /*
45  *  Function prototypes
46  */
47 static void             usage(char *);
48 static void             dump_config(void);
49 static void             print_results(void);
50 static int              open_sockets(void);
51 static int              init_rude(void);
52 void                    rude_handler(int);
53 
54 extern int              read_cfg(FILE *);  /* parse.c     */
55 extern void             clean_up(void);    /* flow_cntl.c */
56 extern struct flow_cfg* find_next(void);   /* flow_cntl.c */
57 
58 
59 /*
60  * Global variables
61  */
62 struct flow_cfg *head        = NULL;   /* Our global linked list            */
63 struct flow_cfg *done        = NULL;   /* Our 2nd global linked list        */
64 struct timeval  tester_start = {0,0};  /* Absolute process START TIME       */
65 struct udp_data *data        = NULL;   /* */
66 char *buffer                 = NULL;   /* */
67 int max_packet_size          = 0;      /* Size of the largest packet        */
68 
69 
70 /****************************************************************************/
71 /*                           MAIN LOOP FUNCTIONS                            */
72 /****************************************************************************/
main(int argc,char ** argv)73 int main(int argc, char **argv)
74 {
75   extern char *optarg;
76   extern int  optind, opterr, optopt, errno;
77 
78   struct flow_cfg *flow = NULL;
79   FILE *scriptfile      = NULL;
80   int priority          = 0;
81   int opt_ret           = 0;
82   int retval            = 0;
83   uid_t user_id         = getuid();
84   struct sigaction action;
85   struct sched_param p;
86 
87   printf("rude version %s, Copyright (C) 1999 Juha Laine and Sampo Saaristo\n"
88 	 "rude comes with ABSOLUTELY NO WARRANTY!\n"
89 	 "This is free software, and you are welcome to redistribute it\n"
90 	 "under GNU GENERAL PUBLIC LICENSE Version 2.\n",VERSION);
91 
92   if(argc < 2){
93     fprintf(stderr,"rude: at least 1 argument expected\n");
94     usage(argv[0]);
95     exit(1);
96   }
97 
98   while(((opt_ret=getopt(argc,argv,"s:P:hv")) != EOF) && (retval == 0)){
99     switch(opt_ret){
100     case 'v':
101       if((optind == 2) && (argc == 2)){
102 	printf("rude version is %s\n",VERSION);
103 	retval = -1;
104       } else {
105 	RUDEBUG1("rude: invalid commandline arguments!\n");
106 	retval = -2;
107       }
108       break;
109 
110     case 'h':
111       if((optind == 2) && (argc == 2)){
112 	usage(argv[0]);
113 	retval = -1;
114       } else {
115 	RUDEBUG1("rude: invalid commandline arguments!\n");
116 	retval = -2;
117       }
118       break;
119 
120     case 's':
121       if(optarg != NULL){
122 	if((scriptfile=fopen((const char *)optarg,"r")) == NULL){
123 	  fprintf(stderr,"rude: could not open %s: %s\n",
124 		  optarg,strerror(errno));
125 	  retval = -2;
126 	}
127       } else {
128 	RUDEBUG1("rude: invalid commandline arguments!\n");
129 	retval = -2;
130       }
131       break;
132 
133     case 'P':
134       if(optarg != NULL){
135 	priority = atoi(optarg);
136 	if((priority < 1) || (priority > 90)){
137 	  fprintf(stderr,"rude: priority must be between 1 to 90\n");
138 	  retval = -2;
139 	}
140 	if(user_id != 0){
141 	  fprintf(stderr,"rude: must be root to set the priority level\n");
142 	  retval = -2;
143 	}
144       } else {
145 	RUDEBUG1("rude: invalid commandline arguments!\n");
146 	retval = -2;
147       }
148       break;
149 
150     default:
151       usage(argv[0]);
152       retval = -2;
153       break;
154     }
155   }
156 
157   if((retval < 0) || (scriptfile == NULL)){
158     if(scriptfile != NULL){ fclose(scriptfile); }
159     exit(-2);
160   }
161 
162   /* Read & parse the given configuration file */
163   retval = read_cfg(scriptfile);
164   fclose(scriptfile);
165 
166   if(retval <= 0){
167     RUDEBUG1("rude: EXIT - %d parse errors in configuration file\n",retval);
168     clean_up();
169     exit(3);
170   }
171   dump_config();
172 
173   /* Add the SIGNAL HANDLER function initialization(s) here... */
174   /* FIXME: SIGSTOP ??? SIGQUIT ???                            */
175   memset(&action,0,sizeof(struct sigaction));
176   action.sa_handler = rude_handler;
177 
178   if(sigaction(SIGHUP,&action,NULL)){
179     RUDEBUG1("rude: signal SIGHUP catching failed\n");
180     retval = -1;
181     goto rude_exit1;
182   }
183   if(sigaction(SIGINT,&action,NULL)){
184     RUDEBUG1("rude: signal SIGINT catching failed\n");
185     retval = -1;
186     goto rude_exit1;
187   }
188   if(sigaction(SIGTERM,&action,NULL)){
189     RUDEBUG1("rude: signal SIGTERM catching failed\n");
190     retval = -1;
191     goto rude_exit1;
192   }
193 
194   /* Initialize the system... */
195   if(init_rude() != 0){
196     RUDEBUG1("rude: EXIT - initialization failed\n");
197     retval = -1;
198     goto rude_exit1;
199   }
200 
201   /*
202    * If this process is owned by root we can do some tricks to
203    * improve the performance... (the -P option)
204    */
205   if((user_id == 0) && (priority > 0)){
206     /* Try to lock the memory to avoid paging delays */
207       RUDEBUG1("rude: memory lock unsupported\n");
208 
209     /* Switch to Round-Robin-Real-Time Scheduling */
210     p.sched_priority = priority;
211     if(sched_setscheduler(0, SCHED_RR, &p) < 0){
212       RUDEBUG1("rude: sched_setscheduler() failed: %s\n",strerror(errno));
213       retval = -1;
214       goto rude_exit;
215     }
216     RUDEBUG7("rude: program priority set to %d\n", p.sched_priority);
217   }
218 
219   /*
220    * All is fine - start looping & transmitting
221    */
222   RUDEBUG7("rude: start LOOPING\n");
223   while((flow = find_next()) != NULL){
224     flow->send_func(flow);
225   }
226   RUDEBUG7("rude: stop LOOPING\n");
227 
228  rude_exit:
229 
230   /*
231    * Restore the tweaked settings...
232    */
233   if((user_id == 0) && (priority > 0)){
234     /* Restore the program priority */
235     p.sched_priority = 0;
236     if(sched_setscheduler(0, SCHED_OTHER, &p) < 0){
237       RUDEBUG1("rude: program priority restoring failed: %s\n",strerror(errno));
238       retval = -1;
239     } else {
240       RUDEBUG7("rude: program priority restored\n");
241     }
242 
243   }
244 
245  rude_exit1:
246 
247   /* FIXME: Remove SIGNAL HANDLERS ??? */
248   /* dump_config(); */
249   print_results();
250   clean_up();
251   exit(retval);
252 }
253 
254 
255 /*
256  * rude_handler(): Smar clean-up function for several signals...
257  */
rude_handler(int value)258 void rude_handler(int value)
259 {
260   int ret_val = (value * (-1));
261   struct sched_param pri;
262   RUDEBUG7("\nrude_handler(): receiver SIGNAL %d - clean-up & exit\n",value);
263 
264   /* Check & restore process priority */
265   if((getuid() == 0) && (sched_getscheduler(0) != SCHED_OTHER)){
266     pri.sched_priority = 0;
267     if(sched_setscheduler(0, SCHED_OTHER, &pri) < 0){
268       RUDEBUG1("rude_handler: rude priority failure: %s\n",strerror(errno));
269     } else {
270       RUDEBUG7("rude_handler: rude priority restored\n");
271     }
272   }
273 
274   clean_up();
275   exit(ret_val);
276 } /* main() */
277 
278 
279 /*
280  * usage(): Print the usage information
281  */
usage(char * name)282 static void usage(char *name)
283 {
284   printf("\nusage: %s -h | -v | -s scriptfile [-p priority]\n\n"
285 	 "\t-h            = print (this) short help and usage information\n"
286 	 "\t-v            = print the version number and exit\n"
287 	 "\t-s scriptfile = path to the flow configuration file\n"
288 	 "\t-P priority   = process realtime priority {1-90}\n\n",name);
289 } /* usage() */
290 
291 
292 /*
293  * dump_config(): Dumps the current flow configuration to the stderr
294  *                but only if DEBUG level is set to 7 (or higher...).
295  *                This function requires attention if one adds new flow
296  *                types to this program...
297  */
dump_config(void)298 static void dump_config(void)
299 {
300 #if (DEBUG > 6)
301   struct flow_cfg *flow   = head;
302   struct flow_cfg *flow_m = head;
303 
304   /* Print the header, if there is any data to dump */
305   if(flow != NULL){
306     fprintf(stderr,"RUDE START = %ld.%06ld\n\nF_ID: F_START: F_STOP: "
307 	    "F_SPORT: F_DADD: F_DPORT: F_TYPE: [+ type params]\n",
308 	    tester_start.tv_sec,tester_start.tv_usec);
309   }
310 
311   while(flow_m != NULL){
312     fprintf(stderr,"%ld %ld.%06ld %ld.%06ld %hu %s %hu ",
313 	    flow->flow_id, flow->flow_start.tv_sec,flow->flow_start.tv_usec,
314 	    flow->flow_stop.tv_sec,flow->flow_stop.tv_usec,
315 	    flow->flow_sport,inet_ntoa(flow->dst.sin_addr),
316 	    ntohs(flow->dst.sin_port));
317 
318     switch(flow->params.ftype){
319     case(CBR):
320       fprintf(stderr,"CBR [r:%d s:%d]\n",
321 	      flow->params.cbr.rate,flow->params.cbr.psize);
322       break;
323     case(TRACE):
324       fprintf(stderr,"TRACE [list_size:%u]\n",flow->params.trace.list_size);
325       break;
326     default:
327       fprintf(stderr,"<UNKNOWN TYPE>\n");
328       break;
329     }
330 
331     /* Set flow to the next possible flow_cfg (either modified or next id) */
332     if(flow->mod_flow != NULL){
333       flow = flow->mod_flow;
334     } else {
335       flow   = flow_m->next;
336       flow_m = flow;
337     }
338   }
339   fprintf(stderr,"\n");
340 
341 
342   /* Do the same for the already sent flows... */
343   if((flow = done) != NULL){
344     fprintf(stderr,"\nF_ID: F_START: F_STOP: F_SPORT: F_DADD: F_DPORT:"
345 	    " F_err: F_suc: F_seq: F_TYPE: [+ type params]\n");
346   }
347 
348   while(flow != NULL){
349     fprintf(stderr,"%ld %ld.%06ld %ld.%06ld %hu %s %hu %d %d %d ",
350 	    flow->flow_id, flow->flow_start.tv_sec, flow->flow_start.tv_usec,
351 	    flow->flow_stop.tv_sec, flow->flow_stop.tv_usec,
352 	    flow->flow_sport, inet_ntoa(flow->dst.sin_addr),
353 	    ntohs(flow->dst.sin_port), flow->errors, flow->success,
354 	    flow->sequence_nmbr);
355 
356     switch(flow->params.ftype){
357     case(CBR):
358       fprintf(stderr,"CBR [r:%d s:%d]\n",
359 	      flow->params.cbr.rate,flow->params.cbr.psize);
360       break;
361     case(TRACE):
362       fprintf(stderr,"TRACE [list_size:%u]\n",flow->params.trace.list_size);
363       break;
364     default:
365       fprintf(stderr,"<UNKNOWN TYPE>\n");
366       break;
367     }
368     flow = flow->next;
369   }
370   fprintf(stderr,"\n");
371 #endif
372   return;
373 } /* dump_config() */
374 
375 
376 /*
377  * print_results(): Print the Xmit status after completed. This function
378  *                  requires attention if one adds new flow types to this
379  *                  program...
380  */
print_results(void)381 static void print_results(void)
382 {
383   struct flow_cfg *flow   = NULL;
384 
385   if((flow = done) != NULL){
386     printf("\nF_ID: F_START: F_STOP: F_SPORT: F_DADD: F_DPORT:"
387 	   " F_err: F_suc: F_seq: F_TYPE: [+ type params]\n");
388   }
389 
390   while(flow != NULL){
391     printf("%ld %ld.%06ld %ld.%06ld %hu %s %hu %d %d %d ",
392 	   flow->flow_id, flow->flow_start.tv_sec, flow->flow_start.tv_usec,
393 	   flow->flow_stop.tv_sec, flow->flow_stop.tv_usec,
394 	   flow->flow_sport, inet_ntoa(flow->dst.sin_addr),
395 	   ntohs(flow->dst.sin_port), flow->errors, flow->success,
396 	   flow->sequence_nmbr);
397 
398     switch(flow->params.ftype){
399     case(CBR):
400       printf("CBR [r:%d s:%d]\n",
401 	     flow->params.cbr.rate,flow->params.cbr.psize);
402       break;
403     case(TRACE):
404       printf("TRACE [list_size:%u]\n",flow->params.trace.list_size);
405       break;
406     default:
407       printf("<UNKNOWN TYPE>\n");
408       break;
409     }
410     flow = flow->next;
411   }
412   return;
413 } /* print_results() */
414 
415 
416 /*
417  * open_sockets(): Open socket for every flow
418  */
open_sockets(void)419 static int open_sockets(void)
420 {
421   struct sockaddr our_addr;
422   struct flow_cfg *flow = head;
423   int retval = 0;
424   int flags  = 0;
425   int tos;
426 
427   memset(&our_addr, 0, sizeof(struct sockaddr));
428   ((struct sockaddr_in *)&our_addr)->sin_family = AF_INET;
429   ((struct sockaddr_in *)&our_addr)->sin_addr.s_addr = htonl(INADDR_ANY);
430 
431   while(flow != NULL){
432     if((flow->send_sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0){
433       RUDEBUG1("open_sockets() - socket() failed for flow %ld: %s\n",
434 	       flow->flow_id,strerror(errno));
435       retval--;
436       goto socket_error;
437     }
438 
439     ((struct sockaddr_in *)&our_addr)->sin_port = htons(flow->flow_sport);
440     if(bind(flow->send_sock, &our_addr, sizeof(struct sockaddr)) < 0){
441       close(flow->send_sock);
442       flow->send_sock = 0;
443       RUDEBUG1("open_sockets() - bind() failed for flow %ld: %s\n",
444 	       flow->flow_id,strerror(errno));
445       retval--;
446       goto socket_error;
447     }
448 
449     if((flags = fcntl(flow->send_sock, F_GETFL, 0)) < 0) {
450       RUDEBUG1("open_sockets() - GETFLAGS error\n");
451       retval--;
452     } else if((fcntl(flow->send_sock, F_SETFL, flags | O_NONBLOCK)) < 0){
453       RUDEBUG1("open_sockets() - SETFLAGS O_NONBLOCK error\n");
454       retval--;
455     }
456 #ifndef SOL_IP
457 #define SOL_IP IPPROTO_IP
458 #endif
459     if(flow->tos > 0){
460       tos = (unsigned char) flow->tos;
461       if(setsockopt(flow->send_sock, SOL_IP, IP_TOS, &tos, sizeof(tos)) == -1){
462         RUDEBUG1("Can't set TOS for flow %ld: using default...\n",
463                  flow->flow_id);
464       }
465     }
466 
467   socket_error:
468     flow = flow->next;
469   }
470 
471   RUDEBUG7("open_sockets() - EXIT(%d)\n",retval);
472   return(retval);
473 } /* open_sockets() */
474 
475 
476 /*
477  * init_rude(): initializes sockets and packet "buffer"
478  */
init_rude(void)479 static int init_rude(void)
480 {
481   struct timeval now  = {0,0};
482   unsigned long wait  = 0;
483   int retval          = 0;
484 
485   /* Initialize/Check the global variables */
486   if(max_packet_size <= 0){
487     RUDEBUG1("init_rude() - max_packet_size not set\n");
488     retval -= 1;
489     goto init_exit;
490   }
491 
492   if((buffer = malloc(max_packet_size)) == NULL){
493     RUDEBUG1("init_rude() - malloc() failed: %s\n", strerror(errno));
494     retval -= 2;
495     goto init_exit;
496   }
497   memset(buffer,0,max_packet_size);
498   data = (struct udp_data *)buffer;
499 
500   if(open_sockets() != 0){
501     retval -= 4;
502     goto init_exit;
503   }
504 
505   /* Check the time when we should start/have started transmission */
506   gettimeofday(&now,NULL);
507   if(timercmp(&tester_start,&now,>)){
508     wait = ((tester_start.tv_sec - now.tv_sec)*1000000) +
509       (tester_start.tv_usec - now.tv_usec);
510     RUDEBUG7("init_rude() - wait for start %lu microseconds...",wait);
511     usleep(wait);
512     RUDEBUG7("done!\n");
513   }
514 
515  init_exit:
516   RUDEBUG7("init_rude() - EXIT(%d)\n",retval);
517   return(retval);
518 }
519