callback.c 21.7 KB
Newer Older
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1
2
/*
 * EMULAB-COPYRIGHT
3
 * Copyright (c) 2000-2003 University of Utah and the Flux Group.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
4
5
6
 * All rights reserved.
 */

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
 * agent-callback.c --
 *
 *      Delay node agent callback handling.
 *
 */


/******************************* INCLUDES **************************/
#include "main.h"
/******************************* INCLUDES **************************/


/******************************* EXTERNS **************************/
extern structlink_map link_map[MAX_LINKS];
extern int link_index;
extern int s_dummy; 
24
extern int debug;
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/******************************* EXTERNS **************************/


/********************************FUNCTION DEFS *******************/


/*************************** agent_callback **********************
 This function is called from the event system when an event
 notification is recd. from the server. It checks whether the 
 notification is valid (sanity check). If not print a warning,else
 call handle_pipes which does the rest of thejob
 *************************** agent_callback **********************/

void agent_callback(event_handle_t handle,
		    event_notification_t notification, void *data)
{
  #define MAX_LEN 50

  char objname[MAX_LEN];
  char eventtype[MAX_LEN];
45
  int i;
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

    /* get the name of the object, eg. link0 or link1*/
  if(event_notification_get_string(handle,
                                   notification,
                                   "OBJNAME", objname, MAX_LEN) == 0){
    error("could not get the objname \n");
    return;
  }

  /* get the eventtype, eg up/down/modify*/
  if(event_notification_get_string(handle,
                                   notification,
                                   "EVENTTYPE", eventtype, MAX_LEN) == 0){
    error("could not get the eventtype \n");
    return;
  }

63
64
65
66
67
68
69
70
71
72
73
  /*
   * We could be an agent for several nodes on the same lan, so need to
   * loop over the pipe sets and possibly repeat all the work multiple
   * times. Sigh.
   */
  for(i = 0; i < link_index; i++){
    if(!strcmp(link_map[i].linkname, objname) ||
       !strcmp(link_map[i].linkvnodes[0], objname) ||
       !strcmp(link_map[i].linkvnodes[1], objname))
      handle_pipes(objname, eventtype, notification, handle, i);
  }
74
75
76
77
78
79
80
81
82
83
84
85
86
87
}

/******************** handle_pipes ***************************************
This dispatch function checks the event type and dispatches to the appropriate
routine to handle
 ******************** handle_pipes ***************************************/

void handle_pipes (char *objname, char *eventtype,
		   event_notification_t notification, event_handle_t handle,
		   int l_index)
{
  
  /*link_map[index] contains the relevant info*/

88
89
90
91
92
93
94
95
  if(strcmp(eventtype, TBDB_EVENTTYPE_UP) == 0){
    handle_link_up(objname, l_index);
  }
  else if(strcmp(eventtype, TBDB_EVENTTYPE_DOWN) == 0){
    handle_link_down(objname, l_index);
  }
  else if(strcmp(eventtype, TBDB_EVENTTYPE_MODIFY) == 0){
    handle_link_modify(objname, l_index, handle, notification);
96
  }
97
  else error("unknown link event type\n");
98
99
100
101
102

  if(debug){
    system ("echo ======================================== >> /tmp/ipfw.log"); 
    system("(date;echo PARAMS ; ipfw pipe show all) >> /tmp/ipfw.log");
  }
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
}

/******************* handle_link_up **************************
This handles the link_up event. If link is already up, it returns
without doing anything. If link is down, it calls set_link_params
for looking up the link params in the link_table and configuring 
them into dummynet
 ******************* handle_link_up ***************************/

void handle_link_up(char * linkname, int l_index)
{
  /* get the pipe params from the params field of the
     link_map table. Set the pipe params in dummynet
   */
  info("==========================================\n");
118
  info("recd. UP event for link = %s\n", linkname);
119

120
121
122
  /* no need to do anything if link is already up*/
  if(link_map[l_index].stat == LINK_UP)
    return;
123
   
124
125
  link_map[l_index].stat = LINK_UP;
  set_link_params(l_index, 0, -1);
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
}

/******************* handle_link_down **************************
This handles the link_down event. If link is already down, it returns
without doing anything. If link is up, it calls get_link_params
to populate the link_map table with current params. It then calls
set_link_params to write back the pipe params, but with plr = 1.0,
so that the link goes down
 ******************* handle_link_down***************************/

void handle_link_down(char * linkname, int l_index)
{
  /* get the pipe params from dummynet
   * Store them in the params field of the link_map table.
   * Change the pipe config so that plr = 1.0
   * so that packets are blackholed
   */
143
144
145
146
147
  info("==========================================\n");
  info("recd. DOWN event for link = %s\n", linkname);

  if(link_map[l_index].stat == LINK_DOWN)
    return;
148
149
150

  if(get_link_params(l_index) == 1){
    link_map[l_index].stat = LINK_DOWN;
151
    set_link_params(l_index, 1, -1);
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
  }
  else error("could not get params\n");
}

/*********** handle_link_modify *****************************
It gets the new link params from the notification and updates
the link_map table with these params. If the link is down,
it just returns. IF the link is up, then it calls set_link_params
to set the new params
 *********** handle_link_modify *****************************/

void handle_link_modify(char * linkname, int l_index,
			event_handle_t handle,
			event_notification_t notification)
{
  /* Get the new pipe params from the notification, and then
     update the new set of params by setting the params in
     dummynet
   */
171
  int i, p_which = -1;
172
173
174

  info("==========================================\n");
  info("recd. MODIFY event for link = %s\n", linkname);
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193

  /*
   * As a convience to the user, we create virt_agents entries
   * for each "link-vnode" so that users can talk to a specific
   * side of a duplex link, or a specific node in a lan (in which
   * case it refers to both pipes, not just one). Look at the
   * object name to determine ahead of time which pipe. Note that
   * for the lan node case, the virt agent entry exists strictly
   * to direct the event to this agent; there might be an actual
   * pipe spec inside the event if the user wants to one side
   * of the link (to the switch or from the switch).
   */
  if (!link_map[l_index].islan) {
    for (i = 0; i < link_map[l_index].numpipes; i++) {
      if(!strcmp(link_map[l_index].linkvnodes[i], linkname)) {
	p_which = i;
      }
    }
  }
194
  
195
196
197
198
199
200
  /* if the link is up, then get the params from dummynet,
     get the params from the notification and then merge
     and write back to dummynet
   */
  if(link_map[l_index].stat == LINK_UP){
    if(get_link_params(l_index) == 1)
201
202
      if(get_new_link_params(l_index, handle, notification, &p_which) == 1)
	set_link_params(l_index, 0, p_which);
203
204
  } else
    /* link is down, so just change in the link_map*/
205
    get_new_link_params(l_index, handle, notification, &p_which);
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
}

/****************** get_link_params ***************************
for both the pipes of the duplex link, get the pipe params
sing getsockopt and store these params in the link map
table
****************** get_link_params ***************************/

int get_link_params(int l_index)
{
  struct dn_pipe *pipes;
  int fix_size;
  int num_bytes, num_alloc;
  void * data = NULL;
  int p_index = 0;
  int pipe_num;
222
  while( p_index < link_map[l_index].numpipes ){
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
    
    pipe_num = link_map[l_index].pipes[p_index];

    /* get the pipe structure from Dummynet, resizing the data
       as required
     */
    fix_size = sizeof(*pipes);
    num_alloc = fix_size;
    num_bytes = num_alloc;

    data = malloc(num_bytes);
    if(data == NULL){
      error("malloc: cant allocate memory\n");
      return 0;
    }
    
    while (num_bytes >= num_alloc) {
      num_alloc = num_alloc * 2 + 200;
      num_bytes = num_alloc ;
      if ((data = realloc(data, num_bytes)) == NULL){
	error("cant alloc memory\n");
	return 0;
      }
    
      if (getsockopt(s_dummy, IPPROTO_IP, IP_DUMMYNET_GET,
		     data, &num_bytes) < 0){
	error("error in getsockopt\n");
	return 0;
      }

    }

    /* now search the pipe list for the required pipe*/
    {

    void *next = data ;
    struct dn_pipe *p = (struct dn_pipe *) data;
    struct dn_flow_queue *q ;
    int l ;

    for ( ; num_bytes >= sizeof(*p) ; p = (struct dn_pipe *)next ) {
       
     
       if ( p->next != (struct dn_pipe *)DN_IS_PIPE )
	  break ;

       l = sizeof(*p) + p->fs.rq_elements * sizeof(*q) ;
       next = (void *)p  + l ;
       num_bytes -= l ;
       q = (struct dn_flow_queue *)(p+1) ;

       if (pipe_num != p->pipe_nr)
	   continue;

       /* grab pipe delay and bandwidth */
       link_map[l_index].params[p_index].delay = p->delay;
       link_map[l_index].params[p_index].bw = p->bandwidth;

       /* get flow set parameters*/
       get_flowset_params( &(p->fs), l_index, p_index);

       /* get dynamic queue parameters*/
       get_queue_params( &(p->fs), l_index, p_index);
     }
  }

    free(data);
    /* go for the next pipe in the duplex link*/
    p_index++; 
  }

  return 1;
}

/************ get_flowset_params ********************************
  get the flowset params from the dummnet pipe data structure
************** get_flowset_params ******************************/

void get_flowset_params(struct dn_flow_set *fs, int l_index,
			int p_index)
{
  
  /* grab pointer to the params structure of the pipe in the
     link_map table
   */
    structpipe_params *p_params
      = &(link_map[l_index].params[p_index]);


  /* q size*/
    p_params->q_size = fs->qsize;

    /* initialise flags */
    p_params->flags_p = 0;

    /* q unit */
    if (fs->flags_fs & DN_QSIZE_IS_BYTES)
      p_params->flags_p |= PIPE_QSIZE_IN_BYTES;

    /* q plr*/
   #if 0 
    p_params->plr = fs->plr;
   #endif

    p_params->plr = 1.0*fs->plr/(double)(0x7fffffff);
    
    /* hash table/bucket size */
    p_params->buckets = fs->rq_size;

#if 0
    /* number of queues in the pipe */
    p_params->n_qs = fs->rq_elements;
#endif
    
    /* q type and corresponding parameters*/

339
340
    /* internally dummynet sets DN_IS_RED (only) if the queue is RED and
       sets DN_IS_RED and DN_IS_GENTLE_RED(both) if queue is GRED*/
341

342
343
    if (fs->flags_fs & DN_IS_RED) {
      /* get GRED/RED params */
344
345
346

      p_params->red_gred_params.w_q =  1.0*fs->w_q/(double)(1 << SCALE_RED);
      p_params->red_gred_params.max_p = 1.0*fs->max_p/(double)(1 << SCALE_RED);
347
348
349
      p_params->red_gred_params.min_th = SCALE_VAL(fs->min_th);
      p_params->red_gred_params.max_th = SCALE_VAL(fs->max_th);

350
351
352
      if(fs->flags_fs & DN_IS_GENTLE_RED) 
	p_params->flags_p |= PIPE_Q_IS_GRED;
      else
353
	p_params->flags_p |= PIPE_Q_IS_RED;
354
355
      
    }
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
    /* else droptail*/

  }

/********* get_queue_params ******************************
 get the queue specific params like the pipe flow mask 
 which is used to create dynamic queues as new flows are
 formed
 ********* get_queue_params ******************************/

void get_queue_params(struct dn_flow_set *fs,
		      int l_index, int p_index)
{
  /* grab the queue mask. Actually a pipe can have multiple masks,
     HANDLE THIS LATER
   */
  
  structpipe_params *p_params
    = &(link_map[l_index].params[p_index]);
  
376
377
  
  if(fs->flags_fs & DN_HAVE_FLOW_MASK){
378
    p_params->flags_p    |= PIPE_HAS_FLOW_MASK;
379
380
381
382
383
384
    p_params->id.proto     = fs->flow_mask.proto;
    p_params->id.src_ip    = fs->flow_mask.src_ip;
    p_params->id.src_port  = fs->flow_mask.src_port;
    p_params->id.dst_ip    = fs->flow_mask.dst_ip;
    p_params->id.dst_port  = fs->flow_mask.dst_port;
  }
385
386
387
388
389
390
391
392
393

  return;
}

/****************** set_link_params ***************************
for both the pipes of the duplex link, set the pipe params
sing setsockopt getting the param values from the link_map table
**************************************************************/

394
void set_link_params(int l_index, int blackhole, int p_which)
395
396
397
398
{
  /* Grab the pipe params from the link_map table and then
     set them into dummynet by calling setsockopt
   */
399
400
401
402
403
404
405
406
  int p_index;

  for (p_index = 0; p_index < link_map[l_index].numpipes; p_index++) {
      /*
       * Want to do all the pipes, or just the one pipe that was
       * specified.
       */
       if(p_which == -1 || p_which == p_index) {
407
408
409
410
411
412
	    struct dn_pipe pipe;
	    /* get the params stored in the link table*/
	    structpipe_params *p_params
	      = &(link_map[l_index].params[p_index]);

	    info("entered the loop, pindex = %d\n", p_index);
413
	
414
	    memset(&pipe, 0, sizeof pipe);
415

416
417
418
	    /* set the bandwidth and delay*/
	    pipe.bandwidth = p_params->bw;
	    pipe.delay = p_params->delay;
419

420
421
	    /* set the pipe number*/
	    pipe.pipe_nr = link_map[l_index].pipes[p_index];
422

423
424
425
426
	    /* if we want to blackhole (in the case of EVENT_DOWN,
	       then we set all other pipe params same, but change the
	       plr to 1.0
	       */
427
428
429
430
431
432
	    {
	      double d = p_params->plr;

	      if (blackhole)
		d = 1.0;
	      
433
434
	      pipe.fs.plr = (int)(d*0x7fffffff);
	    }
435

436
437
438
439
	    /* set the queue size*/
	    pipe.fs.qsize = p_params->q_size;
	    /* set the number of buckets used for dynamic queues*/
	    pipe.fs.rq_size = p_params->buckets;
440
#if 0
441
	    pipe.fs.rq_elements = num_qs;
442
#endif
443
444
	    /* initialise pipe flags to zero */
	    pipe.fs.flags_fs = 0;
445

446
447
448
	    /* set if the q size is in slots or in bytes*/
	    if(p_params->flags_p & PIPE_QSIZE_IN_BYTES)
	      pipe.fs.flags_fs |= DN_QSIZE_IS_BYTES;
449
450
   
#if 0
451
452
453
454
455
	    pipe.fs.flow_mask.proto = 0;
	    pipe.fs.flow_mask.src_ip = 0;
	    pipe.fs.flow_mask.dst_ip = 0;
	    pipe.fs.flow_mask.src_port = 0;
	    pipe.fs.flow_mask.dst_port = 0;
456
457
458

#endif

459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
	    /* set if the pipe has a flow mask*/
	    if(p_params->flags_p & PIPE_HAS_FLOW_MASK){
	      pipe.fs.flags_fs |= DN_HAVE_FLOW_MASK;

	      pipe.fs.flow_mask.proto = p_params->id.proto;
	      pipe.fs.flow_mask.src_ip = p_params->id.src_ip;
	      pipe.fs.flow_mask.dst_ip = p_params->id.dst_ip;
	      pipe.fs.flow_mask.src_port = p_params->id.src_port;
	      pipe.fs.flow_mask.dst_port = p_params->id.dst_port;
	    }
	    /* set the queing discipline and other relevant params*/

	    if(p_params->flags_p & (PIPE_Q_IS_GRED | PIPE_Q_IS_RED)){
	      /* set GRED params */
	      pipe.fs.flags_fs |= DN_IS_RED;
	      pipe.fs.max_th = p_params->red_gred_params.max_th;
	      pipe.fs.min_th = p_params->red_gred_params.min_th;
	      pipe.fs.w_q = (int) ( p_params->red_gred_params.w_q * (1 << SCALE_RED) ) ;
	      pipe.fs.max_p = (int) ( p_params->red_gred_params.max_p * (1 << SCALE_RED) );

	      if(p_params->flags_p & PIPE_Q_IS_GRED)
		pipe.fs.flags_fs |= DN_IS_GENTLE_RED;

	      if(pipe.bandwidth){
		size_t len ; 
		int lookup_depth, avg_pkt_size ;
		double s, idle, weight, w_q ;
		struct clockinfo clock ;
		int t ;

		len = sizeof(int) ;
		if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth", 
				 &lookup_depth, &len, NULL, 0) == -1){
		  error("cant get net.inet.ip.dummynet.red_lookup_depth");
		  return;
		}
		if (lookup_depth == 0) {
		  info("net.inet.ip.dummynet.red_lookup_depth must" 
497
			    "greater than zero") ;
498
499
		  return;
		}
500
	   
501
502
		len = sizeof(int) ;
		if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size", 
503
504
			&avg_pkt_size, &len, NULL, 0) == -1){

505
506
507
508
509
		  error("cant get net.inet.ip.dummynet.red_avg_pkt_size");
		  return;
		}
		if (avg_pkt_size == 0){
		  info("net.inet.ip.dummynet.red_avg_pkt_size must" 
510
				"greater than zero") ;
511
512
		  return;
		}
513

514
		len = sizeof(struct clockinfo) ;
515

516
517
518
519
520
		if (sysctlbyname("kern.clockrate", 
				 &clock, &len, NULL, 0) == -1) {
		  error("cant get kern.clockrate") ;
		  return;
		}
521
522
	   

523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
		/* ticks needed for sending a medium-sized packet */
		s = clock.hz * avg_pkt_size * 8 / pipe.bandwidth;

		/*
		 * max idle time (in ticks) before avg queue size becomes 0. 
		 * NOTA:  (3/w_q) is approx the value x so that 
		 * (1-w_q)^x < 10^-3. 
		 */
		w_q = ((double) pipe.fs.w_q) / (1 << SCALE_RED) ; 
		idle = s * 3. / w_q ;
		pipe.fs.lookup_step = (int) idle / lookup_depth ;
		if (!pipe.fs.lookup_step) 
		  pipe.fs.lookup_step = 1 ;
		weight = 1 - w_q ;
		for ( t = pipe.fs.lookup_step ; t > 0 ; --t ) 
		  weight *= weight ;
		pipe.fs.lookup_weight = (int) (weight * (1 << SCALE_RED)) ;
	     
	      }
542
	 
543
544
545
546
547
548
549
550
	    }
	    /*  else DROPTAIL*/

	    /* now call setsockopt*/
	    if (setsockopt(s_dummy,IPPROTO_IP, IP_DUMMYNET_CONFIGURE, &pipe,sizeof pipe)
		< 0)
	      error("IP_DUMMYNET_CONFIGURE setsockopt failed\n");
	  }
551
552
553
554
555
556
557
558
    }
}

/********* get_new_link_params ***************************
  For a modify event, this function gets the parameters
  from the event notification
 ********************************************************/

559
int get_new_link_params(int l_index, event_handle_t handle,
560
			event_notification_t notification, int *pipe_which)
561
562
563
564
565
{
  /* get the params of the pipe that were sent in the notification and
     store those values into the link_map table
   */

566
  char argstring[256];
567
568
  char * argtype = NULL;
  char * argvalue = NULL;
569
570
571
  int p_num = 0;
  int gotpipe = 0;
  int islan = link_map[l_index].islan;
572
  char *temp = NULL;
573
574
575
576
577
578

  /* Allow upper level to init the pipe */
  if (*pipe_which >= 0) {
      gotpipe = 1;
      p_num   = *pipe_which;
  }
579
  
580
581
  if(event_notification_get_string(handle, notification,
                                   "ARGS", argstring, sizeof(argstring)) != 0){
582
583
    info("ARGS = %s\n", argstring);
    temp = argstring;
584
    
585
586
587
    argtype = strsep(&temp,"=");
    
    while((argvalue = strsep(&temp," \n"))){
588
589
590

      if(strcmp(argtype,"BANDWIDTH")== 0){
	info("Bandwidth = %d\n", atoi(argvalue) * 1000);
591
592
593
594
	link_map[l_index].params[p_num].bw = atoi(argvalue) * 1000;
	if (! gotpipe) {
	  link_map[l_index].params[1].bw = link_map[l_index].params[0].bw;
	}
595
      }
596

597
598
      else if (strcmp(argtype,"DELAY")== 0){
	 info("Delay = %d\n", atoi(argvalue));
599
600
	 link_map[l_index].params[p_num].delay = atoi(argvalue);
	 if (! gotpipe) {
601
	   link_map[l_index].params[1].delay =
602
		   link_map[l_index].params[0].delay;
603
	 }
604
605
      }
      else if (strcmp(argtype,"PLR")== 0){
606
	 info("Plr = %f\n", atof(argvalue));
607
608
609
	 link_map[l_index].params[p_num].plr = atof(argvalue);
	 if (! gotpipe) {
	   link_map[l_index].params[1].plr = link_map[l_index].params[0].plr;
610
	 }
611
      }
612

613
614
615
616
       /* Queue parameters. Slightly different since we do not want
	  to set the queue params for a lan node in the from-switch
	  direction. Note, by convention the 0 pipe is to the switch
          and the 1 pipe is from the switch. */
617
       
618
       else if (strcmp(argtype,"LIMIT")== 0){
619
	 info("QSize/Limit = %d\n", atoi(argvalue));
620
621
622
623
624
625
	 /* set the PIPE_QSIZE_IN_BYTES flag to 0,
	    since we assume that limit is in slots/packets by default
	 */
	 link_map[l_index].params[p_num].q_size = atoi(argvalue);
	 link_map[l_index].params[p_num].flags_p &= ~PIPE_QSIZE_IN_BYTES;
	 if (!gotpipe && !islan) {
626
	   link_map[l_index].params[1].q_size =
627
628
629
630
		   link_map[l_index].params[0].q_size;
	   link_map[l_index].params[0].flags_p =
		   link_map[l_index].params[1].flags_p;
	 }
631
632
633
634
635
       }

       else if (strcmp(argtype,"QUEUE-IN-BYTES")== 0){
	 int qsztype = atoi(argvalue);
	 if(qsztype == 0){
636
	   info("QSize in slots/packets");
637
638
639
640
641
642
643
644
645
	   link_map[l_index].params[p_num].flags_p &= ~PIPE_QSIZE_IN_BYTES;
	 }
	 else {
	   info("QSize in bytes\n");
	   link_map[l_index].params[p_num].flags_p |= PIPE_QSIZE_IN_BYTES;
	 }
	 if (!gotpipe && !islan) {
	   link_map[l_index].params[1].flags_p =
		   link_map[l_index].params[0].flags_p;
646
647
648
	 }
       }
       else if(strcmp(argtype,"MAXTHRESH")== 0){
649
	 info("Maxthresh = %d \n", atoi(argvalue));
650
651
652
653
654
655
	 link_map[l_index].params[p_num].red_gred_params.max_th =
		 atoi(argvalue);
	 if (!gotpipe && !islan) {
	   link_map[l_index].params[1].red_gred_params.max_th =
		   link_map[l_index].params[0].red_gred_params.max_th;
	 }
656
657
       }
       else if(strcmp(argtype,"THRESH")== 0){
658
	 info("Thresh = %d \n", atoi(argvalue));
659
660
661
662
663
664
	 link_map[l_index].params[p_num].red_gred_params.min_th =
		 atoi(argvalue);
	 if (!gotpipe && !islan) {
	    link_map[l_index].params[1].red_gred_params.min_th =
		    link_map[l_index].params[0].red_gred_params.min_th;
	 }
665
666
       }
       else if(strcmp(argtype,"LINTERM")== 0){
667
	 info("Linterm = %f\n", 1.0 / atof(argvalue));
668
669
670
671
672
673
	 link_map[l_index].params[p_num].red_gred_params.max_p =
		 1.0 / atof(argvalue);
	 if (!gotpipe && !islan) {
	   link_map[l_index].params[1].red_gred_params.max_p =
		   link_map[l_index].params[0].red_gred_params.max_p;
	 }
674
675
       }
       else if(strcmp(argtype,"Q_WEIGHT")== 0){
676
	 info("Qweight = %f\n", atof(argvalue));
677
678
679
680
681
682
	 link_map[l_index].params[p_num].red_gred_params.w_q =
		 atof(argvalue);
	 if (!gotpipe && !islan) {
	   link_map[l_index].params[1].red_gred_params.w_q =
		   link_map[l_index].params[0].red_gred_params.w_q;
	 }
683
684
685
686
       }
       else if(strcmp(argtype,"PIPE")== 0){
	 info("PIPE = %s\n", argvalue);

687
	 gotpipe++;
688
	 if(strcmp(argvalue, "pipe0") == 0)
689
	   p_num = 0;
690
691
	 else if(strcmp(argvalue, "pipe1") == 0)
	   p_num = 1;
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
	 else if(isdigit(argvalue[0])) {
	   int pipeno = atoi(argvalue);

	   if(link_map[l_index].pipes[0] == pipeno)
	     p_num = 0;
	   else if(link_map[l_index].pipes[1] == pipeno)
	     p_num = 1;
	   else {
	     error("unrecognized pipe number\n");
	     return -1;
	   }
	 }
	 else {
	   error("unrecognized pipe argument\n");
	   return -1;
	 }
708
709
710
711
       }
       else {
	 error("unrecognized argument\n");
	 error("%s -> %s \n", argtype, argvalue);
712
       }
713
       argtype = strsep(&temp,"=");
714
715
     }
  }
716
717
  if (gotpipe)
    *pipe_which = p_num;
718
  
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
  return 1;
}
 /**************** get_link_info ****************************
  This builds up the initial link_map table's pipe params
   by getting the parameters from Dummynet
 **************** get_link_info ****************************/

int get_link_info(){
  int l_index = 0;
  while(l_index < link_index){
    if(get_link_params(l_index) == 0)
      return 0;
    if((int)(link_map[l_index].params[0].plr) == 1
       || (int)(link_map[l_index].params[1].plr) == 1)
      link_map[l_index].stat = LINK_DOWN;
    else
      link_map[l_index].stat = LINK_UP;
    l_index++;
  }
  return 1;
}
/********************************FUNCTION DEFS *******************/