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