dummynet.c revision 200056
1187769Sluigi/* 2187769Sluigi * Copyright (c) 2002-2003 Luigi Rizzo 3187769Sluigi * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp 4187769Sluigi * Copyright (c) 1994 Ugen J.S.Antsilevich 5187769Sluigi * 6187769Sluigi * Idea and grammar partially left from: 7187769Sluigi * Copyright (c) 1993 Daniel Boulet 8187769Sluigi * 9187769Sluigi * Redistribution and use in source forms, with and without modification, 10187769Sluigi * are permitted provided that this entire comment appears intact. 11187769Sluigi * 12187769Sluigi * Redistribution in binary form may occur without any restrictions. 13187769Sluigi * Obviously, it would be nice if you gave credit where credit is due 14187769Sluigi * but requiring it would be too onerous. 15187769Sluigi * 16187769Sluigi * This software is provided ``AS IS'' without any warranties of any kind. 17187769Sluigi * 18187769Sluigi * NEW command line interface for IP firewall facility 19187769Sluigi * 20187769Sluigi * $FreeBSD: head/sbin/ipfw/dummynet.c 200056 2009-12-03 12:23:48Z luigi $ 21187769Sluigi * 22187769Sluigi * dummynet support 23187769Sluigi */ 24187769Sluigi 25187769Sluigi#include <sys/types.h> 26187769Sluigi#include <sys/socket.h> 27187983Sluigi#include <sys/queue.h> 28187983Sluigi/* XXX there are several sysctl leftover here */ 29187769Sluigi#include <sys/sysctl.h> 30187769Sluigi 31187769Sluigi#include "ipfw2.h" 32187769Sluigi 33187769Sluigi#include <ctype.h> 34187769Sluigi#include <err.h> 35194930Soleg#include <errno.h> 36194930Soleg#include <libutil.h> 37187769Sluigi#include <netdb.h> 38187769Sluigi#include <stdio.h> 39187769Sluigi#include <stdlib.h> 40187769Sluigi#include <string.h> 41187769Sluigi#include <sysexits.h> 42187769Sluigi 43187769Sluigi#include <net/if.h> 44187769Sluigi#include <netinet/in.h> 45187769Sluigi#include <netinet/ip_fw.h> 46187769Sluigi#include <netinet/ip_dummynet.h> 47187983Sluigi#include <arpa/inet.h> /* inet_ntoa */ 48187769Sluigi 49187769Sluigistatic struct _s_x dummynet_params[] = { 50187769Sluigi { "plr", TOK_PLR }, 51187769Sluigi { "noerror", TOK_NOERROR }, 52187769Sluigi { "buckets", TOK_BUCKETS }, 53187769Sluigi { "dst-ip", TOK_DSTIP }, 54187769Sluigi { "src-ip", TOK_SRCIP }, 55187769Sluigi { "dst-port", TOK_DSTPORT }, 56187769Sluigi { "src-port", TOK_SRCPORT }, 57187769Sluigi { "proto", TOK_PROTO }, 58187769Sluigi { "weight", TOK_WEIGHT }, 59187769Sluigi { "all", TOK_ALL }, 60187769Sluigi { "mask", TOK_MASK }, 61187769Sluigi { "droptail", TOK_DROPTAIL }, 62187769Sluigi { "red", TOK_RED }, 63187769Sluigi { "gred", TOK_GRED }, 64187769Sluigi { "bw", TOK_BW }, 65187769Sluigi { "bandwidth", TOK_BW }, 66187769Sluigi { "delay", TOK_DELAY }, 67187769Sluigi { "pipe", TOK_PIPE }, 68187769Sluigi { "queue", TOK_QUEUE }, 69187769Sluigi { "flow-id", TOK_FLOWID}, 70187769Sluigi { "dst-ipv6", TOK_DSTIP6}, 71187769Sluigi { "dst-ip6", TOK_DSTIP6}, 72187769Sluigi { "src-ipv6", TOK_SRCIP6}, 73187769Sluigi { "src-ip6", TOK_SRCIP6}, 74190865Sluigi { "profile", TOK_PIPE_PROFILE}, 75194930Soleg { "burst", TOK_BURST}, 76187769Sluigi { "dummynet-params", TOK_NULL }, 77187769Sluigi { NULL, 0 } /* terminator */ 78187769Sluigi}; 79187769Sluigi 80187769Sluigistatic int 81200056Sluigisort_q(void *arg, const void *pa, const void *pb) 82187769Sluigi{ 83187769Sluigi int rev = (co.do_sort < 0); 84187769Sluigi int field = rev ? -co.do_sort : co.do_sort; 85187769Sluigi long long res = 0; 86187769Sluigi const struct dn_flow_queue *a = pa; 87187769Sluigi const struct dn_flow_queue *b = pb; 88187769Sluigi 89187769Sluigi switch (field) { 90187769Sluigi case 1: /* pkts */ 91187769Sluigi res = a->len - b->len; 92187769Sluigi break; 93187769Sluigi case 2: /* bytes */ 94187769Sluigi res = a->len_bytes - b->len_bytes; 95187769Sluigi break; 96187769Sluigi 97187769Sluigi case 3: /* tot pkts */ 98187769Sluigi res = a->tot_pkts - b->tot_pkts; 99187769Sluigi break; 100187769Sluigi 101187769Sluigi case 4: /* tot bytes */ 102187769Sluigi res = a->tot_bytes - b->tot_bytes; 103187769Sluigi break; 104187769Sluigi } 105187769Sluigi if (res < 0) 106187769Sluigi res = -1; 107187769Sluigi if (res > 0) 108187769Sluigi res = 1; 109187769Sluigi return (int)(rev ? res : -res); 110187769Sluigi} 111187769Sluigi 112187769Sluigistatic void 113187769Sluigilist_queues(struct dn_flow_set *fs, struct dn_flow_queue *q) 114187769Sluigi{ 115187769Sluigi int l; 116187769Sluigi int index_printed, indexes = 0; 117187769Sluigi char buff[255]; 118187769Sluigi struct protoent *pe; 119187769Sluigi 120187769Sluigi if (fs->rq_elements == 0) 121187769Sluigi return; 122187769Sluigi 123187769Sluigi if (co.do_sort != 0) 124200056Sluigi qsort_r(q, fs->rq_elements, sizeof *q, NULL, sort_q); 125187769Sluigi 126187769Sluigi /* Print IPv4 flows */ 127187769Sluigi index_printed = 0; 128187769Sluigi for (l = 0; l < fs->rq_elements; l++) { 129187769Sluigi struct in_addr ina; 130187769Sluigi 131187769Sluigi /* XXX: Should check for IPv4 flows */ 132187769Sluigi if (IS_IP6_FLOW_ID(&(q[l].id))) 133187769Sluigi continue; 134187769Sluigi 135187769Sluigi if (!index_printed) { 136187769Sluigi index_printed = 1; 137187769Sluigi if (indexes > 0) /* currently a no-op */ 138187769Sluigi printf("\n"); 139187769Sluigi indexes++; 140187769Sluigi printf(" " 141187769Sluigi "mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n", 142187769Sluigi fs->flow_mask.proto, 143187769Sluigi fs->flow_mask.src_ip, fs->flow_mask.src_port, 144187769Sluigi fs->flow_mask.dst_ip, fs->flow_mask.dst_port); 145187769Sluigi 146187769Sluigi printf("BKT Prot ___Source IP/port____ " 147187769Sluigi "____Dest. IP/port____ " 148187769Sluigi "Tot_pkt/bytes Pkt/Byte Drp\n"); 149187769Sluigi } 150187769Sluigi 151187769Sluigi printf("%3d ", q[l].hash_slot); 152187769Sluigi pe = getprotobynumber(q[l].id.proto); 153187769Sluigi if (pe) 154187769Sluigi printf("%-4s ", pe->p_name); 155187769Sluigi else 156187769Sluigi printf("%4u ", q[l].id.proto); 157187769Sluigi ina.s_addr = htonl(q[l].id.src_ip); 158187769Sluigi printf("%15s/%-5d ", 159187769Sluigi inet_ntoa(ina), q[l].id.src_port); 160187769Sluigi ina.s_addr = htonl(q[l].id.dst_ip); 161187769Sluigi printf("%15s/%-5d ", 162187769Sluigi inet_ntoa(ina), q[l].id.dst_port); 163187787Sluigi printf("%4llu %8llu %2u %4u %3u\n", 164187787Sluigi align_uint64(&q[l].tot_pkts), 165187787Sluigi align_uint64(&q[l].tot_bytes), 166187769Sluigi q[l].len, q[l].len_bytes, q[l].drops); 167187769Sluigi if (co.verbose) 168187787Sluigi printf(" S %20llu F %20llu\n", 169187787Sluigi align_uint64(&q[l].S), align_uint64(&q[l].F)); 170187769Sluigi } 171187769Sluigi 172187769Sluigi /* Print IPv6 flows */ 173187769Sluigi index_printed = 0; 174187769Sluigi for (l = 0; l < fs->rq_elements; l++) { 175187769Sluigi if (!IS_IP6_FLOW_ID(&(q[l].id))) 176187769Sluigi continue; 177187769Sluigi 178187769Sluigi if (!index_printed) { 179187769Sluigi index_printed = 1; 180187769Sluigi if (indexes > 0) 181187769Sluigi printf("\n"); 182187769Sluigi indexes++; 183187769Sluigi printf("\n mask: proto: 0x%02x, flow_id: 0x%08x, ", 184187769Sluigi fs->flow_mask.proto, fs->flow_mask.flow_id6); 185187769Sluigi inet_ntop(AF_INET6, &(fs->flow_mask.src_ip6), 186187769Sluigi buff, sizeof(buff)); 187187769Sluigi printf("%s/0x%04x -> ", buff, fs->flow_mask.src_port); 188187769Sluigi inet_ntop( AF_INET6, &(fs->flow_mask.dst_ip6), 189187769Sluigi buff, sizeof(buff) ); 190187769Sluigi printf("%s/0x%04x\n", buff, fs->flow_mask.dst_port); 191187769Sluigi 192187769Sluigi printf("BKT ___Prot___ _flow-id_ " 193187769Sluigi "______________Source IPv6/port_______________ " 194187769Sluigi "_______________Dest. IPv6/port_______________ " 195187769Sluigi "Tot_pkt/bytes Pkt/Byte Drp\n"); 196187769Sluigi } 197187769Sluigi printf("%3d ", q[l].hash_slot); 198187769Sluigi pe = getprotobynumber(q[l].id.proto); 199187769Sluigi if (pe != NULL) 200187769Sluigi printf("%9s ", pe->p_name); 201187769Sluigi else 202187769Sluigi printf("%9u ", q[l].id.proto); 203187769Sluigi printf("%7d %39s/%-5d ", q[l].id.flow_id6, 204187769Sluigi inet_ntop(AF_INET6, &(q[l].id.src_ip6), buff, sizeof(buff)), 205187769Sluigi q[l].id.src_port); 206187769Sluigi printf(" %39s/%-5d ", 207187769Sluigi inet_ntop(AF_INET6, &(q[l].id.dst_ip6), buff, sizeof(buff)), 208187769Sluigi q[l].id.dst_port); 209187787Sluigi printf(" %4llu %8llu %2u %4u %3u\n", 210187787Sluigi align_uint64(&q[l].tot_pkts), 211187787Sluigi align_uint64(&q[l].tot_bytes), 212187769Sluigi q[l].len, q[l].len_bytes, q[l].drops); 213187769Sluigi if (co.verbose) 214187787Sluigi printf(" S %20llu F %20llu\n", 215187787Sluigi align_uint64(&q[l].S), 216187787Sluigi align_uint64(&q[l].F)); 217187769Sluigi } 218187769Sluigi} 219187769Sluigi 220187769Sluigistatic void 221187769Sluigiprint_flowset_parms(struct dn_flow_set *fs, char *prefix) 222187769Sluigi{ 223187769Sluigi int l; 224187769Sluigi char qs[30]; 225187769Sluigi char plr[30]; 226187769Sluigi char red[90]; /* Display RED parameters */ 227187769Sluigi 228187769Sluigi l = fs->qsize; 229187769Sluigi if (fs->flags_fs & DN_QSIZE_IS_BYTES) { 230187769Sluigi if (l >= 8192) 231187769Sluigi sprintf(qs, "%d KB", l / 1024); 232187769Sluigi else 233187769Sluigi sprintf(qs, "%d B", l); 234187769Sluigi } else 235187769Sluigi sprintf(qs, "%3d sl.", l); 236187769Sluigi if (fs->plr) 237187769Sluigi sprintf(plr, "plr %f", 1.0 * fs->plr / (double)(0x7fffffff)); 238187769Sluigi else 239187769Sluigi plr[0] = '\0'; 240187769Sluigi if (fs->flags_fs & DN_IS_RED) /* RED parameters */ 241187769Sluigi sprintf(red, 242194930Soleg "\n\t %cRED w_q %f min_th %d max_th %d max_p %f", 243187769Sluigi (fs->flags_fs & DN_IS_GENTLE_RED) ? 'G' : ' ', 244187769Sluigi 1.0 * fs->w_q / (double)(1 << SCALE_RED), 245187769Sluigi SCALE_VAL(fs->min_th), 246187769Sluigi SCALE_VAL(fs->max_th), 247187769Sluigi 1.0 * fs->max_p / (double)(1 << SCALE_RED)); 248187769Sluigi else 249187769Sluigi sprintf(red, "droptail"); 250187769Sluigi 251187769Sluigi printf("%s %s%s %d queues (%d buckets) %s\n", 252187769Sluigi prefix, qs, plr, fs->rq_elements, fs->rq_size, red); 253187769Sluigi} 254187769Sluigi 255190865Sluigistatic void 256194930Solegprint_extra_delay_parms(struct dn_pipe *p) 257190865Sluigi{ 258190865Sluigi double loss; 259190865Sluigi if (p->samples_no <= 0) 260190865Sluigi return; 261190865Sluigi 262190865Sluigi loss = p->loss_level; 263190865Sluigi loss /= p->samples_no; 264194930Soleg printf("\t profile: name \"%s\" loss %f samples %d\n", 265194930Soleg p->name, loss, p->samples_no); 266190865Sluigi} 267190865Sluigi 268187769Sluigivoid 269187769Sluigiipfw_list_pipes(void *data, uint nbytes, int ac, char *av[]) 270187769Sluigi{ 271187769Sluigi int rulenum; 272187769Sluigi void *next = data; 273187769Sluigi struct dn_pipe *p = (struct dn_pipe *) data; 274187769Sluigi struct dn_flow_set *fs; 275187769Sluigi struct dn_flow_queue *q; 276187769Sluigi int l; 277187769Sluigi 278187769Sluigi if (ac > 0) 279187769Sluigi rulenum = strtoul(*av++, NULL, 10); 280187769Sluigi else 281187769Sluigi rulenum = 0; 282187769Sluigi for (; nbytes >= sizeof *p; p = (struct dn_pipe *)next) { 283187769Sluigi double b = p->bandwidth; 284187769Sluigi char buf[30]; 285187769Sluigi char prefix[80]; 286194930Soleg char burst[5 + 7]; 287187769Sluigi 288187769Sluigi if (SLIST_NEXT(p, next) != (struct dn_pipe *)DN_IS_PIPE) 289187769Sluigi break; /* done with pipes, now queues */ 290187769Sluigi 291187769Sluigi /* 292187769Sluigi * compute length, as pipe have variable size 293187769Sluigi */ 294187769Sluigi l = sizeof(*p) + p->fs.rq_elements * sizeof(*q); 295187769Sluigi next = (char *)p + l; 296187769Sluigi nbytes -= l; 297187769Sluigi 298187769Sluigi if ((rulenum != 0 && rulenum != p->pipe_nr) || co.do_pipe == 2) 299187769Sluigi continue; 300187769Sluigi 301187769Sluigi /* 302187769Sluigi * Print rate (or clocking interface) 303187769Sluigi */ 304187769Sluigi if (p->if_name[0] != '\0') 305187769Sluigi sprintf(buf, "%s", p->if_name); 306187769Sluigi else if (b == 0) 307187769Sluigi sprintf(buf, "unlimited"); 308187769Sluigi else if (b >= 1000000) 309187769Sluigi sprintf(buf, "%7.3f Mbit/s", b/1000000); 310187769Sluigi else if (b >= 1000) 311187769Sluigi sprintf(buf, "%7.3f Kbit/s", b/1000); 312187769Sluigi else 313187769Sluigi sprintf(buf, "%7.3f bit/s ", b); 314187769Sluigi 315187769Sluigi sprintf(prefix, "%05d: %s %4d ms ", 316187769Sluigi p->pipe_nr, buf, p->delay); 317190865Sluigi 318187769Sluigi print_flowset_parms(&(p->fs), prefix); 319187769Sluigi 320194930Soleg if (humanize_number(burst, sizeof(burst), p->burst, 321194930Soleg "Byte", HN_AUTOSCALE, 0) < 0 || co.verbose) 322194930Soleg printf("\t burst: %ju Byte\n", p->burst); 323194930Soleg else 324194930Soleg printf("\t burst: %s\n", burst); 325194930Soleg 326194930Soleg print_extra_delay_parms(p); 327194930Soleg 328187769Sluigi q = (struct dn_flow_queue *)(p+1); 329187769Sluigi list_queues(&(p->fs), q); 330187769Sluigi } 331187769Sluigi for (fs = next; nbytes >= sizeof *fs; fs = next) { 332187769Sluigi char prefix[80]; 333187769Sluigi 334187769Sluigi if (SLIST_NEXT(fs, next) != (struct dn_flow_set *)DN_IS_QUEUE) 335187769Sluigi break; 336187769Sluigi l = sizeof(*fs) + fs->rq_elements * sizeof(*q); 337187769Sluigi next = (char *)fs + l; 338187769Sluigi nbytes -= l; 339187769Sluigi 340187769Sluigi if (rulenum != 0 && ((rulenum != fs->fs_nr && co.do_pipe == 2) || 341187769Sluigi (rulenum != fs->parent_nr && co.do_pipe == 1))) { 342187769Sluigi continue; 343187769Sluigi } 344187769Sluigi 345187769Sluigi q = (struct dn_flow_queue *)(fs+1); 346187769Sluigi sprintf(prefix, "q%05d: weight %d pipe %d ", 347187769Sluigi fs->fs_nr, fs->weight, fs->parent_nr); 348187769Sluigi print_flowset_parms(fs, prefix); 349187769Sluigi list_queues(fs, q); 350187769Sluigi } 351187769Sluigi} 352187769Sluigi 353187769Sluigi/* 354187769Sluigi * Delete pipe or queue i 355187769Sluigi */ 356187769Sluigiint 357187771Sluigiipfw_delete_pipe(int pipe_or_queue, int i) 358187769Sluigi{ 359187769Sluigi struct dn_pipe p; 360187769Sluigi 361187769Sluigi memset(&p, 0, sizeof p); 362187769Sluigi if (pipe_or_queue == 1) 363187769Sluigi p.pipe_nr = i; /* pipe */ 364187769Sluigi else 365187769Sluigi p.fs.fs_nr = i; /* queue */ 366187769Sluigi i = do_cmd(IP_DUMMYNET_DEL, &p, sizeof p); 367187769Sluigi if (i) { 368187769Sluigi i = 1; 369187769Sluigi warn("rule %u: setsockopt(IP_DUMMYNET_DEL)", i); 370187769Sluigi } 371187769Sluigi return i; 372187769Sluigi} 373187769Sluigi 374190865Sluigi/* 375190865Sluigi * Code to parse delay profiles. 376190865Sluigi * 377190865Sluigi * Some link types introduce extra delays in the transmission 378190865Sluigi * of a packet, e.g. because of MAC level framing, contention on 379190865Sluigi * the use of the channel, MAC level retransmissions and so on. 380190865Sluigi * From our point of view, the channel is effectively unavailable 381190865Sluigi * for this extra time, which is constant or variable depending 382190865Sluigi * on the link type. Additionally, packets may be dropped after this 383190865Sluigi * time (e.g. on a wireless link after too many retransmissions). 384190865Sluigi * We can model the additional delay with an empirical curve 385190865Sluigi * that represents its distribution. 386190865Sluigi * 387190865Sluigi * cumulative probability 388190865Sluigi * 1.0 ^ 389190865Sluigi * | 390190865Sluigi * L +-- loss-level x 391190865Sluigi * | ****** 392190865Sluigi * | * 393190865Sluigi * | ***** 394190865Sluigi * | * 395190865Sluigi * | ** 396190865Sluigi * | * 397190865Sluigi * +-------*-------------------> 398190865Sluigi * delay 399190865Sluigi * 400190865Sluigi * The empirical curve may have both vertical and horizontal lines. 401190865Sluigi * Vertical lines represent constant delay for a range of 402190865Sluigi * probabilities; horizontal lines correspond to a discontinuty 403190865Sluigi * in the delay distribution: the pipe will use the largest delay 404190865Sluigi * for a given probability. 405190865Sluigi * 406190865Sluigi * To pass the curve to dummynet, we must store the parameters 407190865Sluigi * in a file as described below, and issue the command 408190865Sluigi * 409190865Sluigi * ipfw pipe <n> config ... bw XXX profile <filename> ... 410190865Sluigi * 411190865Sluigi * The file format is the following, with whitespace acting as 412190865Sluigi * a separator and '#' indicating the beginning a comment: 413190865Sluigi * 414190865Sluigi * samples N 415190865Sluigi * the number of samples used in the internal 416190865Sluigi * representation (2..1024; default 100); 417190865Sluigi * 418190865Sluigi * loss-level L 419190865Sluigi * The probability above which packets are lost. 420190865Sluigi * (0.0 <= L <= 1.0, default 1.0 i.e. no loss); 421190865Sluigi * 422190865Sluigi * name identifier 423190865Sluigi * Optional a name (listed by "ipfw pipe show") 424190865Sluigi * to identify the distribution; 425190865Sluigi * 426190865Sluigi * "delay prob" | "prob delay" 427190865Sluigi * One of these two lines is mandatory and defines 428190865Sluigi * the format of the following lines with data points. 429190865Sluigi * 430190865Sluigi * XXX YYY 431190865Sluigi * 2 or more lines representing points in the curve, 432190865Sluigi * with either delay or probability first, according 433190865Sluigi * to the chosen format. 434190865Sluigi * The unit for delay is milliseconds. 435190865Sluigi * 436190865Sluigi * Data points does not need to be ordered or equal to the number 437190865Sluigi * specified in the "samples" line. ipfw will sort and interpolate 438190865Sluigi * the curve as needed. 439190865Sluigi * 440190865Sluigi * Example of a profile file: 441190865Sluigi 442190865Sluigi name bla_bla_bla 443190865Sluigi samples 100 444190865Sluigi loss-level 0.86 445190865Sluigi prob delay 446190865Sluigi 0 200 # minimum overhead is 200ms 447190865Sluigi 0.5 200 448190865Sluigi 0.5 300 449190865Sluigi 0.8 1000 450190865Sluigi 0.9 1300 451190865Sluigi 1 1300 452190865Sluigi 453190865Sluigi * Internally, we will convert the curve to a fixed number of 454190865Sluigi * samples, and when it is time to transmit a packet we will 455190865Sluigi * model the extra delay as extra bits in the packet. 456190865Sluigi * 457190865Sluigi */ 458190865Sluigi 459190865Sluigi#define ED_MAX_LINE_LEN 256+ED_MAX_NAME_LEN 460190865Sluigi#define ED_TOK_SAMPLES "samples" 461190865Sluigi#define ED_TOK_LOSS "loss-level" 462190865Sluigi#define ED_TOK_NAME "name" 463190865Sluigi#define ED_TOK_DELAY "delay" 464190865Sluigi#define ED_TOK_PROB "prob" 465193715Sluigi#define ED_TOK_BW "bw" 466190865Sluigi#define ED_SEPARATORS " \t\n" 467190865Sluigi#define ED_MIN_SAMPLES_NO 2 468190865Sluigi 469190865Sluigi/* 470190865Sluigi * returns 1 if s is a non-negative number, with at least one '.' 471190865Sluigi */ 472190865Sluigistatic int 473190865Sluigiis_valid_number(const char *s) 474190865Sluigi{ 475190865Sluigi int i, dots_found = 0; 476190865Sluigi int len = strlen(s); 477190865Sluigi 478190865Sluigi for (i = 0; i<len; ++i) 479190865Sluigi if (!isdigit(s[i]) && (s[i] !='.' || ++dots_found > 1)) 480190865Sluigi return 0; 481190865Sluigi return 1; 482190865Sluigi} 483190865Sluigi 484193715Sluigi/* 485193715Sluigi * Take as input a string describing a bandwidth value 486193715Sluigi * and return the numeric bandwidth value. 487193715Sluigi * set clocking interface or bandwidth value 488193715Sluigi */ 489200056Sluigistatic void 490193715Sluigiread_bandwidth(char *arg, int *bandwidth, char *if_name, int namelen) 491193715Sluigi{ 492193715Sluigi if (*bandwidth != -1) 493193715Sluigi warn("duplicate token, override bandwidth value!"); 494193715Sluigi 495193715Sluigi if (arg[0] >= 'a' && arg[0] <= 'z') { 496193715Sluigi if (namelen >= IFNAMSIZ) 497193715Sluigi warn("interface name truncated"); 498193715Sluigi namelen--; 499193715Sluigi /* interface name */ 500193715Sluigi strncpy(if_name, arg, namelen); 501193715Sluigi if_name[namelen] = '\0'; 502193715Sluigi *bandwidth = 0; 503193715Sluigi } else { /* read bandwidth value */ 504193715Sluigi int bw; 505193715Sluigi char *end = NULL; 506193715Sluigi 507193715Sluigi bw = strtoul(arg, &end, 0); 508193715Sluigi if (*end == 'K' || *end == 'k') { 509193715Sluigi end++; 510193715Sluigi bw *= 1000; 511193715Sluigi } else if (*end == 'M') { 512193715Sluigi end++; 513193715Sluigi bw *= 1000000; 514193715Sluigi } 515193715Sluigi if ((*end == 'B' && 516193715Sluigi _substrcmp2(end, "Bi", "Bit/s") != 0) || 517193715Sluigi _substrcmp2(end, "by", "bytes") == 0) 518193715Sluigi bw *= 8; 519193715Sluigi 520193715Sluigi if (bw < 0) 521193715Sluigi errx(EX_DATAERR, "bandwidth too large"); 522193715Sluigi 523193715Sluigi *bandwidth = bw; 524193715Sluigi if_name[0] = '\0'; 525193715Sluigi } 526193715Sluigi} 527193715Sluigi 528190865Sluigistruct point { 529190865Sluigi double prob; 530190865Sluigi double delay; 531190865Sluigi}; 532190865Sluigi 533200056Sluigistatic int 534190865Sluigicompare_points(const void *vp1, const void *vp2) 535190865Sluigi{ 536190865Sluigi const struct point *p1 = vp1; 537190865Sluigi const struct point *p2 = vp2; 538190865Sluigi double res = 0; 539190865Sluigi 540190865Sluigi res = p1->prob - p2->prob; 541190865Sluigi if (res == 0) 542190865Sluigi res = p1->delay - p2->delay; 543190865Sluigi if (res < 0) 544190865Sluigi return -1; 545190865Sluigi else if (res > 0) 546190865Sluigi return 1; 547190865Sluigi else 548190865Sluigi return 0; 549190865Sluigi} 550190865Sluigi 551190865Sluigi#define ED_EFMT(s) EX_DATAERR,"error in %s at line %d: "#s,filename,lineno 552190865Sluigi 553190865Sluigistatic void 554190865Sluigiload_extra_delays(const char *filename, struct dn_pipe *p) 555190865Sluigi{ 556190865Sluigi char line[ED_MAX_LINE_LEN]; 557190865Sluigi FILE *f; 558190865Sluigi int lineno = 0; 559190865Sluigi int i; 560190865Sluigi 561190865Sluigi int samples = -1; 562190865Sluigi double loss = -1.0; 563190865Sluigi char profile_name[ED_MAX_NAME_LEN]; 564190865Sluigi int delay_first = -1; 565190865Sluigi int do_points = 0; 566190865Sluigi struct point points[ED_MAX_SAMPLES_NO]; 567190865Sluigi int points_no = 0; 568190865Sluigi 569190865Sluigi profile_name[0] = '\0'; 570190865Sluigi f = fopen(filename, "r"); 571190865Sluigi if (f == NULL) 572190865Sluigi err(EX_UNAVAILABLE, "fopen: %s", filename); 573190865Sluigi 574190865Sluigi while (fgets(line, ED_MAX_LINE_LEN, f)) { /* read commands */ 575190865Sluigi char *s, *cur = line, *name = NULL, *arg = NULL; 576190865Sluigi 577190865Sluigi ++lineno; 578190865Sluigi 579190865Sluigi /* parse the line */ 580190865Sluigi while (cur) { 581190865Sluigi s = strsep(&cur, ED_SEPARATORS); 582190865Sluigi if (s == NULL || *s == '#') 583190865Sluigi break; 584190865Sluigi if (*s == '\0') 585190865Sluigi continue; 586190865Sluigi if (arg) 587190865Sluigi errx(ED_EFMT("too many arguments")); 588190865Sluigi if (name == NULL) 589190865Sluigi name = s; 590190865Sluigi else 591190865Sluigi arg = s; 592190865Sluigi } 593190865Sluigi if (name == NULL) /* empty line */ 594190865Sluigi continue; 595190865Sluigi if (arg == NULL) 596190865Sluigi errx(ED_EFMT("missing arg for %s"), name); 597190865Sluigi 598190865Sluigi if (!strcasecmp(name, ED_TOK_SAMPLES)) { 599190865Sluigi if (samples > 0) 600190865Sluigi errx(ED_EFMT("duplicate ``samples'' line")); 601190865Sluigi if (atoi(arg) <=0) 602190865Sluigi errx(ED_EFMT("invalid number of samples")); 603190865Sluigi samples = atoi(arg); 604190865Sluigi if (samples>ED_MAX_SAMPLES_NO) 605190865Sluigi errx(ED_EFMT("too many samples, maximum is %d"), 606190865Sluigi ED_MAX_SAMPLES_NO); 607190865Sluigi do_points = 0; 608193715Sluigi } else if (!strcasecmp(name, ED_TOK_BW)) { 609193715Sluigi read_bandwidth(arg, &p->bandwidth, p->if_name, sizeof(p->if_name)); 610190865Sluigi } else if (!strcasecmp(name, ED_TOK_LOSS)) { 611190865Sluigi if (loss != -1.0) 612190865Sluigi errx(ED_EFMT("duplicated token: %s"), name); 613190865Sluigi if (!is_valid_number(arg)) 614190865Sluigi errx(ED_EFMT("invalid %s"), arg); 615190865Sluigi loss = atof(arg); 616190865Sluigi if (loss > 1) 617190865Sluigi errx(ED_EFMT("%s greater than 1.0"), name); 618190865Sluigi do_points = 0; 619190865Sluigi } else if (!strcasecmp(name, ED_TOK_NAME)) { 620190865Sluigi if (profile_name[0] != '\0') 621190865Sluigi errx(ED_EFMT("duplicated token: %s"), name); 622190865Sluigi strncpy(profile_name, arg, sizeof(profile_name) - 1); 623190865Sluigi profile_name[sizeof(profile_name)-1] = '\0'; 624190865Sluigi do_points = 0; 625190865Sluigi } else if (!strcasecmp(name, ED_TOK_DELAY)) { 626190865Sluigi if (do_points) 627190865Sluigi errx(ED_EFMT("duplicated token: %s"), name); 628190865Sluigi delay_first = 1; 629190865Sluigi do_points = 1; 630190865Sluigi } else if (!strcasecmp(name, ED_TOK_PROB)) { 631190865Sluigi if (do_points) 632190865Sluigi errx(ED_EFMT("duplicated token: %s"), name); 633190865Sluigi delay_first = 0; 634190865Sluigi do_points = 1; 635190865Sluigi } else if (do_points) { 636190865Sluigi if (!is_valid_number(name) || !is_valid_number(arg)) 637190865Sluigi errx(ED_EFMT("invalid point found")); 638190865Sluigi if (delay_first) { 639190865Sluigi points[points_no].delay = atof(name); 640190865Sluigi points[points_no].prob = atof(arg); 641190865Sluigi } else { 642190865Sluigi points[points_no].delay = atof(arg); 643190865Sluigi points[points_no].prob = atof(name); 644190865Sluigi } 645190865Sluigi if (points[points_no].prob > 1.0) 646190865Sluigi errx(ED_EFMT("probability greater than 1.0")); 647190865Sluigi ++points_no; 648190865Sluigi } else { 649190865Sluigi errx(ED_EFMT("unrecognised command '%s'"), name); 650190865Sluigi } 651190865Sluigi } 652190865Sluigi 653199626Snetchild fclose (f); 654199626Snetchild 655190865Sluigi if (samples == -1) { 656190865Sluigi warnx("'%s' not found, assuming 100", ED_TOK_SAMPLES); 657190865Sluigi samples = 100; 658190865Sluigi } 659190865Sluigi 660190865Sluigi if (loss == -1.0) { 661190865Sluigi warnx("'%s' not found, assuming no loss", ED_TOK_LOSS); 662190865Sluigi loss = 1; 663190865Sluigi } 664190865Sluigi 665190865Sluigi /* make sure that there are enough points. */ 666190865Sluigi if (points_no < ED_MIN_SAMPLES_NO) 667190865Sluigi errx(ED_EFMT("too few samples, need at least %d"), 668190865Sluigi ED_MIN_SAMPLES_NO); 669190865Sluigi 670190865Sluigi qsort(points, points_no, sizeof(struct point), compare_points); 671190865Sluigi 672190865Sluigi /* interpolation */ 673190865Sluigi for (i = 0; i<points_no-1; ++i) { 674190865Sluigi double y1 = points[i].prob * samples; 675190865Sluigi double x1 = points[i].delay; 676190865Sluigi double y2 = points[i+1].prob * samples; 677190865Sluigi double x2 = points[i+1].delay; 678190865Sluigi 679190865Sluigi int index = y1; 680190865Sluigi int stop = y2; 681190865Sluigi 682190865Sluigi if (x1 == x2) { 683190865Sluigi for (; index<stop; ++index) 684190865Sluigi p->samples[index] = x1; 685190865Sluigi } else { 686190865Sluigi double m = (y2-y1)/(x2-x1); 687190865Sluigi double c = y1 - m*x1; 688190865Sluigi for (; index<stop ; ++index) 689190865Sluigi p->samples[index] = (index - c)/m; 690190865Sluigi } 691190865Sluigi } 692190865Sluigi p->samples_no = samples; 693190865Sluigi p->loss_level = loss * samples; 694190865Sluigi strncpy(p->name, profile_name, sizeof(p->name)); 695190865Sluigi} 696190865Sluigi 697187769Sluigivoid 698187769Sluigiipfw_config_pipe(int ac, char **av) 699187769Sluigi{ 700190865Sluigi int samples[ED_MAX_SAMPLES_NO]; 701187769Sluigi struct dn_pipe p; 702187769Sluigi int i; 703187769Sluigi char *end; 704187769Sluigi void *par = NULL; 705187769Sluigi 706187769Sluigi memset(&p, 0, sizeof p); 707193715Sluigi p.bandwidth = -1; 708187769Sluigi 709187769Sluigi av++; ac--; 710187769Sluigi /* Pipe number */ 711187769Sluigi if (ac && isdigit(**av)) { 712187769Sluigi i = atoi(*av); av++; ac--; 713187769Sluigi if (co.do_pipe == 1) 714187769Sluigi p.pipe_nr = i; 715187769Sluigi else 716187769Sluigi p.fs.fs_nr = i; 717187769Sluigi } 718187769Sluigi while (ac > 0) { 719187769Sluigi double d; 720187769Sluigi int tok = match_token(dummynet_params, *av); 721187769Sluigi ac--; av++; 722187769Sluigi 723187769Sluigi switch(tok) { 724187769Sluigi case TOK_NOERROR: 725187769Sluigi p.fs.flags_fs |= DN_NOERROR; 726187769Sluigi break; 727187769Sluigi 728187769Sluigi case TOK_PLR: 729187769Sluigi NEED1("plr needs argument 0..1\n"); 730187769Sluigi d = strtod(av[0], NULL); 731187769Sluigi if (d > 1) 732187769Sluigi d = 1; 733187769Sluigi else if (d < 0) 734187769Sluigi d = 0; 735187769Sluigi p.fs.plr = (int)(d*0x7fffffff); 736187769Sluigi ac--; av++; 737187769Sluigi break; 738187769Sluigi 739187769Sluigi case TOK_QUEUE: 740187769Sluigi NEED1("queue needs queue size\n"); 741187769Sluigi end = NULL; 742187769Sluigi p.fs.qsize = strtoul(av[0], &end, 0); 743187769Sluigi if (*end == 'K' || *end == 'k') { 744187769Sluigi p.fs.flags_fs |= DN_QSIZE_IS_BYTES; 745187769Sluigi p.fs.qsize *= 1024; 746187769Sluigi } else if (*end == 'B' || 747187769Sluigi _substrcmp2(end, "by", "bytes") == 0) { 748187769Sluigi p.fs.flags_fs |= DN_QSIZE_IS_BYTES; 749187769Sluigi } 750187769Sluigi ac--; av++; 751187769Sluigi break; 752187769Sluigi 753187769Sluigi case TOK_BUCKETS: 754187769Sluigi NEED1("buckets needs argument\n"); 755187769Sluigi p.fs.rq_size = strtoul(av[0], NULL, 0); 756187769Sluigi ac--; av++; 757187769Sluigi break; 758187769Sluigi 759187769Sluigi case TOK_MASK: 760187769Sluigi NEED1("mask needs mask specifier\n"); 761187769Sluigi /* 762187769Sluigi * per-flow queue, mask is dst_ip, dst_port, 763187769Sluigi * src_ip, src_port, proto measured in bits 764187769Sluigi */ 765187769Sluigi par = NULL; 766187769Sluigi 767187769Sluigi bzero(&p.fs.flow_mask, sizeof(p.fs.flow_mask)); 768187769Sluigi end = NULL; 769187769Sluigi 770187769Sluigi while (ac >= 1) { 771187769Sluigi uint32_t *p32 = NULL; 772187769Sluigi uint16_t *p16 = NULL; 773187769Sluigi uint32_t *p20 = NULL; 774187769Sluigi struct in6_addr *pa6 = NULL; 775187769Sluigi uint32_t a; 776187769Sluigi 777187769Sluigi tok = match_token(dummynet_params, *av); 778187769Sluigi ac--; av++; 779187769Sluigi switch(tok) { 780187769Sluigi case TOK_ALL: 781187769Sluigi /* 782187769Sluigi * special case, all bits significant 783187769Sluigi */ 784187769Sluigi p.fs.flow_mask.dst_ip = ~0; 785187769Sluigi p.fs.flow_mask.src_ip = ~0; 786187769Sluigi p.fs.flow_mask.dst_port = ~0; 787187769Sluigi p.fs.flow_mask.src_port = ~0; 788187769Sluigi p.fs.flow_mask.proto = ~0; 789187769Sluigi n2mask(&(p.fs.flow_mask.dst_ip6), 128); 790187769Sluigi n2mask(&(p.fs.flow_mask.src_ip6), 128); 791187769Sluigi p.fs.flow_mask.flow_id6 = ~0; 792187769Sluigi p.fs.flags_fs |= DN_HAVE_FLOW_MASK; 793187769Sluigi goto end_mask; 794187769Sluigi 795187769Sluigi case TOK_DSTIP: 796187769Sluigi p32 = &p.fs.flow_mask.dst_ip; 797187769Sluigi break; 798187769Sluigi 799187769Sluigi case TOK_SRCIP: 800187769Sluigi p32 = &p.fs.flow_mask.src_ip; 801187769Sluigi break; 802187769Sluigi 803187769Sluigi case TOK_DSTIP6: 804187769Sluigi pa6 = &(p.fs.flow_mask.dst_ip6); 805187769Sluigi break; 806187769Sluigi 807187769Sluigi case TOK_SRCIP6: 808187769Sluigi pa6 = &(p.fs.flow_mask.src_ip6); 809187769Sluigi break; 810187769Sluigi 811187769Sluigi case TOK_FLOWID: 812187769Sluigi p20 = &p.fs.flow_mask.flow_id6; 813187769Sluigi break; 814187769Sluigi 815187769Sluigi case TOK_DSTPORT: 816187769Sluigi p16 = &p.fs.flow_mask.dst_port; 817187769Sluigi break; 818187769Sluigi 819187769Sluigi case TOK_SRCPORT: 820187769Sluigi p16 = &p.fs.flow_mask.src_port; 821187769Sluigi break; 822187769Sluigi 823187769Sluigi case TOK_PROTO: 824187769Sluigi break; 825187769Sluigi 826187769Sluigi default: 827187769Sluigi ac++; av--; /* backtrack */ 828187769Sluigi goto end_mask; 829187769Sluigi } 830187769Sluigi if (ac < 1) 831187769Sluigi errx(EX_USAGE, "mask: value missing"); 832187769Sluigi if (*av[0] == '/') { 833187769Sluigi a = strtoul(av[0]+1, &end, 0); 834187769Sluigi if (pa6 == NULL) 835187769Sluigi a = (a == 32) ? ~0 : (1 << a) - 1; 836187769Sluigi } else 837187769Sluigi a = strtoul(av[0], &end, 0); 838187769Sluigi if (p32 != NULL) 839187769Sluigi *p32 = a; 840187769Sluigi else if (p16 != NULL) { 841187769Sluigi if (a > 0xFFFF) 842187769Sluigi errx(EX_DATAERR, 843187769Sluigi "port mask must be 16 bit"); 844187769Sluigi *p16 = (uint16_t)a; 845187769Sluigi } else if (p20 != NULL) { 846187769Sluigi if (a > 0xfffff) 847187769Sluigi errx(EX_DATAERR, 848187769Sluigi "flow_id mask must be 20 bit"); 849187769Sluigi *p20 = (uint32_t)a; 850187769Sluigi } else if (pa6 != NULL) { 851187769Sluigi if (a > 128) 852187769Sluigi errx(EX_DATAERR, 853187769Sluigi "in6addr invalid mask len"); 854187769Sluigi else 855187769Sluigi n2mask(pa6, a); 856187769Sluigi } else { 857187769Sluigi if (a > 0xFF) 858187769Sluigi errx(EX_DATAERR, 859187769Sluigi "proto mask must be 8 bit"); 860187769Sluigi p.fs.flow_mask.proto = (uint8_t)a; 861187769Sluigi } 862187769Sluigi if (a != 0) 863187769Sluigi p.fs.flags_fs |= DN_HAVE_FLOW_MASK; 864187769Sluigi ac--; av++; 865187769Sluigi } /* end while, config masks */ 866187769Sluigiend_mask: 867187769Sluigi break; 868187769Sluigi 869187769Sluigi case TOK_RED: 870187769Sluigi case TOK_GRED: 871187769Sluigi NEED1("red/gred needs w_q/min_th/max_th/max_p\n"); 872187769Sluigi p.fs.flags_fs |= DN_IS_RED; 873187769Sluigi if (tok == TOK_GRED) 874187769Sluigi p.fs.flags_fs |= DN_IS_GENTLE_RED; 875187769Sluigi /* 876187769Sluigi * the format for parameters is w_q/min_th/max_th/max_p 877187769Sluigi */ 878187769Sluigi if ((end = strsep(&av[0], "/"))) { 879187769Sluigi double w_q = strtod(end, NULL); 880187769Sluigi if (w_q > 1 || w_q <= 0) 881187769Sluigi errx(EX_DATAERR, "0 < w_q <= 1"); 882187769Sluigi p.fs.w_q = (int) (w_q * (1 << SCALE_RED)); 883187769Sluigi } 884187769Sluigi if ((end = strsep(&av[0], "/"))) { 885187769Sluigi p.fs.min_th = strtoul(end, &end, 0); 886187769Sluigi if (*end == 'K' || *end == 'k') 887187769Sluigi p.fs.min_th *= 1024; 888187769Sluigi } 889187769Sluigi if ((end = strsep(&av[0], "/"))) { 890187769Sluigi p.fs.max_th = strtoul(end, &end, 0); 891187769Sluigi if (*end == 'K' || *end == 'k') 892187769Sluigi p.fs.max_th *= 1024; 893187769Sluigi } 894187769Sluigi if ((end = strsep(&av[0], "/"))) { 895187769Sluigi double max_p = strtod(end, NULL); 896187769Sluigi if (max_p > 1 || max_p <= 0) 897187769Sluigi errx(EX_DATAERR, "0 < max_p <= 1"); 898187769Sluigi p.fs.max_p = (int)(max_p * (1 << SCALE_RED)); 899187769Sluigi } 900187769Sluigi ac--; av++; 901187769Sluigi break; 902187769Sluigi 903187769Sluigi case TOK_DROPTAIL: 904187769Sluigi p.fs.flags_fs &= ~(DN_IS_RED|DN_IS_GENTLE_RED); 905187769Sluigi break; 906187769Sluigi 907187769Sluigi case TOK_BW: 908187769Sluigi NEED1("bw needs bandwidth or interface\n"); 909187769Sluigi if (co.do_pipe != 1) 910187769Sluigi errx(EX_DATAERR, "bandwidth only valid for pipes"); 911193715Sluigi read_bandwidth(av[0], &p.bandwidth, p.if_name, sizeof(p.if_name)); 912187769Sluigi ac--; av++; 913187769Sluigi break; 914187769Sluigi 915187769Sluigi case TOK_DELAY: 916187769Sluigi if (co.do_pipe != 1) 917187769Sluigi errx(EX_DATAERR, "delay only valid for pipes"); 918187769Sluigi NEED1("delay needs argument 0..10000ms\n"); 919187769Sluigi p.delay = strtoul(av[0], NULL, 0); 920187769Sluigi ac--; av++; 921187769Sluigi break; 922187769Sluigi 923187769Sluigi case TOK_WEIGHT: 924187769Sluigi if (co.do_pipe == 1) 925187769Sluigi errx(EX_DATAERR,"weight only valid for queues"); 926187769Sluigi NEED1("weight needs argument 0..100\n"); 927187769Sluigi p.fs.weight = strtoul(av[0], &end, 0); 928187769Sluigi ac--; av++; 929187769Sluigi break; 930187769Sluigi 931187769Sluigi case TOK_PIPE: 932187769Sluigi if (co.do_pipe == 1) 933187769Sluigi errx(EX_DATAERR,"pipe only valid for queues"); 934187769Sluigi NEED1("pipe needs pipe_number\n"); 935187769Sluigi p.fs.parent_nr = strtoul(av[0], &end, 0); 936187769Sluigi ac--; av++; 937187769Sluigi break; 938187769Sluigi 939190865Sluigi case TOK_PIPE_PROFILE: 940190865Sluigi if (co.do_pipe != 1) 941190865Sluigi errx(EX_DATAERR, "extra delay only valid for pipes"); 942190865Sluigi NEED1("extra delay needs the file name\n"); 943190865Sluigi p.samples = &samples[0]; 944190865Sluigi load_extra_delays(av[0], &p); 945190865Sluigi --ac; ++av; 946190865Sluigi break; 947190865Sluigi 948194930Soleg case TOK_BURST: 949194930Soleg if (co.do_pipe != 1) 950194930Soleg errx(EX_DATAERR, "burst only valid for pipes"); 951194930Soleg NEED1("burst needs argument\n"); 952194930Soleg errno = 0; 953194930Soleg if (expand_number(av[0], &p.burst) < 0) 954194930Soleg if (errno != ERANGE) 955194930Soleg errx(EX_DATAERR, 956194930Soleg "burst: invalid argument"); 957194930Soleg if (errno || p.burst > (1ULL << 48) - 1) 958194930Soleg errx(EX_DATAERR, 959194930Soleg "burst: out of range (0..2^48-1)"); 960194930Soleg ac--; av++; 961194930Soleg break; 962194930Soleg 963187769Sluigi default: 964187769Sluigi errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]); 965187769Sluigi } 966187769Sluigi } 967187769Sluigi if (co.do_pipe == 1) { 968187769Sluigi if (p.pipe_nr == 0) 969187769Sluigi errx(EX_DATAERR, "pipe_nr must be > 0"); 970187769Sluigi if (p.delay > 10000) 971187769Sluigi errx(EX_DATAERR, "delay must be < 10000"); 972187769Sluigi } else { /* co.do_pipe == 2, queue */ 973187769Sluigi if (p.fs.parent_nr == 0) 974187769Sluigi errx(EX_DATAERR, "pipe must be > 0"); 975187769Sluigi if (p.fs.weight >100) 976187769Sluigi errx(EX_DATAERR, "weight must be <= 100"); 977187769Sluigi } 978193715Sluigi 979193715Sluigi /* check for bandwidth value */ 980193715Sluigi if (p.bandwidth == -1) { 981193715Sluigi p.bandwidth = 0; 982193715Sluigi if (p.samples_no > 0) 983193715Sluigi errx(EX_DATAERR, "profile requires a bandwidth limit"); 984193715Sluigi } 985193715Sluigi 986187769Sluigi if (p.fs.flags_fs & DN_QSIZE_IS_BYTES) { 987187769Sluigi size_t len; 988187769Sluigi long limit; 989187769Sluigi 990187769Sluigi len = sizeof(limit); 991187769Sluigi if (sysctlbyname("net.inet.ip.dummynet.pipe_byte_limit", 992187769Sluigi &limit, &len, NULL, 0) == -1) 993187769Sluigi limit = 1024*1024; 994187769Sluigi if (p.fs.qsize > limit) 995187769Sluigi errx(EX_DATAERR, "queue size must be < %ldB", limit); 996187769Sluigi } else { 997187769Sluigi size_t len; 998187769Sluigi long limit; 999187769Sluigi 1000187769Sluigi len = sizeof(limit); 1001187769Sluigi if (sysctlbyname("net.inet.ip.dummynet.pipe_slot_limit", 1002187769Sluigi &limit, &len, NULL, 0) == -1) 1003187769Sluigi limit = 100; 1004187769Sluigi if (p.fs.qsize > limit) 1005187769Sluigi errx(EX_DATAERR, "2 <= queue size <= %ld", limit); 1006187769Sluigi } 1007187769Sluigi if (p.fs.flags_fs & DN_IS_RED) { 1008187769Sluigi size_t len; 1009187769Sluigi int lookup_depth, avg_pkt_size; 1010187769Sluigi double s, idle, weight, w_q; 1011187769Sluigi struct clockinfo ck; 1012187769Sluigi int t; 1013187769Sluigi 1014187769Sluigi if (p.fs.min_th >= p.fs.max_th) 1015187769Sluigi errx(EX_DATAERR, "min_th %d must be < than max_th %d", 1016187769Sluigi p.fs.min_th, p.fs.max_th); 1017187769Sluigi if (p.fs.max_th == 0) 1018187769Sluigi errx(EX_DATAERR, "max_th must be > 0"); 1019187769Sluigi 1020187769Sluigi len = sizeof(int); 1021187769Sluigi if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth", 1022187769Sluigi &lookup_depth, &len, NULL, 0) == -1) 1023187769Sluigi errx(1, "sysctlbyname(\"%s\")", 1024187769Sluigi "net.inet.ip.dummynet.red_lookup_depth"); 1025187769Sluigi if (lookup_depth == 0) 1026187769Sluigi errx(EX_DATAERR, "net.inet.ip.dummynet.red_lookup_depth" 1027187769Sluigi " must be greater than zero"); 1028187769Sluigi 1029187769Sluigi len = sizeof(int); 1030187769Sluigi if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size", 1031187769Sluigi &avg_pkt_size, &len, NULL, 0) == -1) 1032187769Sluigi 1033187769Sluigi errx(1, "sysctlbyname(\"%s\")", 1034187769Sluigi "net.inet.ip.dummynet.red_avg_pkt_size"); 1035187769Sluigi if (avg_pkt_size == 0) 1036187769Sluigi errx(EX_DATAERR, 1037187769Sluigi "net.inet.ip.dummynet.red_avg_pkt_size must" 1038187769Sluigi " be greater than zero"); 1039187769Sluigi 1040187769Sluigi len = sizeof(struct clockinfo); 1041187769Sluigi if (sysctlbyname("kern.clockrate", &ck, &len, NULL, 0) == -1) 1042187769Sluigi errx(1, "sysctlbyname(\"%s\")", "kern.clockrate"); 1043187769Sluigi 1044187769Sluigi /* 1045187769Sluigi * Ticks needed for sending a medium-sized packet. 1046187769Sluigi * Unfortunately, when we are configuring a WF2Q+ queue, we 1047187769Sluigi * do not have bandwidth information, because that is stored 1048187769Sluigi * in the parent pipe, and also we have multiple queues 1049187769Sluigi * competing for it. So we set s=0, which is not very 1050187769Sluigi * correct. But on the other hand, why do we want RED with 1051187769Sluigi * WF2Q+ ? 1052187769Sluigi */ 1053187769Sluigi if (p.bandwidth==0) /* this is a WF2Q+ queue */ 1054187769Sluigi s = 0; 1055187769Sluigi else 1056187769Sluigi s = (double)ck.hz * avg_pkt_size * 8 / p.bandwidth; 1057187769Sluigi 1058187769Sluigi /* 1059187769Sluigi * max idle time (in ticks) before avg queue size becomes 0. 1060187769Sluigi * NOTA: (3/w_q) is approx the value x so that 1061187769Sluigi * (1-w_q)^x < 10^-3. 1062187769Sluigi */ 1063187769Sluigi w_q = ((double)p.fs.w_q) / (1 << SCALE_RED); 1064187769Sluigi idle = s * 3. / w_q; 1065187769Sluigi p.fs.lookup_step = (int)idle / lookup_depth; 1066187769Sluigi if (!p.fs.lookup_step) 1067187769Sluigi p.fs.lookup_step = 1; 1068187769Sluigi weight = 1 - w_q; 1069187769Sluigi for (t = p.fs.lookup_step; t > 1; --t) 1070187769Sluigi weight *= 1 - w_q; 1071187769Sluigi p.fs.lookup_weight = (int)(weight * (1 << SCALE_RED)); 1072187769Sluigi } 1073190865Sluigi if (p.samples_no <= 0) { 1074190865Sluigi i = do_cmd(IP_DUMMYNET_CONFIGURE, &p, sizeof p); 1075190865Sluigi } else { 1076190865Sluigi struct dn_pipe_max pm; 1077190865Sluigi int len = sizeof(pm); 1078190865Sluigi 1079190865Sluigi memcpy(&pm.pipe, &p, sizeof(pm.pipe)); 1080190865Sluigi memcpy(&pm.samples, samples, sizeof(pm.samples)); 1081190865Sluigi 1082190865Sluigi i = do_cmd(IP_DUMMYNET_CONFIGURE, &pm, len); 1083190865Sluigi } 1084190865Sluigi 1085187769Sluigi if (i) 1086187769Sluigi err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE"); 1087187769Sluigi} 1088