dummynet.c revision 204591
1172302Spjd/* 2172302Spjd * Copyright (c) 2002-2003,2010 Luigi Rizzo 3172302Spjd * 4172302Spjd * Redistribution and use in source forms, with and without modification, 5172302Spjd * are permitted provided that this entire comment appears intact. 6172302Spjd * 7172302Spjd * Redistribution in binary form may occur without any restrictions. 8172302Spjd * Obviously, it would be nice if you gave credit where credit is due 9172302Spjd * but requiring it would be too onerous. 10172302Spjd * 11172302Spjd * This software is provided ``AS IS'' without any warranties of any kind. 12172302Spjd * 13172302Spjd * NEW command line interface for IP firewall facility 14172302Spjd * 15172302Spjd * $FreeBSD: head/sbin/ipfw/dummynet.c 204591 2010-03-02 17:40:48Z luigi $ 16172302Spjd * 17172302Spjd * dummynet support 18172302Spjd */ 19172302Spjd 20172302Spjd#include <sys/types.h> 21172302Spjd#include <sys/socket.h> 22172302Spjd/* XXX there are several sysctl leftover here */ 23172302Spjd#include <sys/sysctl.h> 24172302Spjd 25172302Spjd#include "ipfw2.h" 26172302Spjd 27172302Spjd#include <ctype.h> 28172302Spjd#include <err.h> 29172302Spjd#include <errno.h> 30172302Spjd#include <libutil.h> 31172302Spjd#include <netdb.h> 32172302Spjd#include <stdio.h> 33172302Spjd#include <stdlib.h> 34172302Spjd#include <string.h> 35172302Spjd#include <sysexits.h> 36172302Spjd 37172302Spjd#include <net/if.h> 38172302Spjd#include <netinet/in.h> 39172302Spjd#include <netinet/ip_fw.h> 40172302Spjd#include <netinet/ip_dummynet.h> 41172302Spjd#include <arpa/inet.h> /* inet_ntoa */ 42172302Spjd 43172302Spjd 44172302Spjdstatic struct _s_x dummynet_params[] = { 45172302Spjd { "plr", TOK_PLR }, 46172302Spjd { "noerror", TOK_NOERROR }, 47172302Spjd { "buckets", TOK_BUCKETS }, 48172302Spjd { "dst-ip", TOK_DSTIP }, 49172302Spjd { "src-ip", TOK_SRCIP }, 50172302Spjd { "dst-port", TOK_DSTPORT }, 51172302Spjd { "src-port", TOK_SRCPORT }, 52172302Spjd { "proto", TOK_PROTO }, 53172302Spjd { "weight", TOK_WEIGHT }, 54172302Spjd { "lmax", TOK_LMAX }, 55172302Spjd { "maxlen", TOK_LMAX }, 56172302Spjd { "all", TOK_ALL }, 57172302Spjd { "mask", TOK_MASK }, /* alias for both */ 58172302Spjd { "sched_mask", TOK_SCHED_MASK }, 59172302Spjd { "flow_mask", TOK_FLOW_MASK }, 60172302Spjd { "droptail", TOK_DROPTAIL }, 61172302Spjd { "red", TOK_RED }, 62172302Spjd { "gred", TOK_GRED }, 63172302Spjd { "bw", TOK_BW }, 64172302Spjd { "bandwidth", TOK_BW }, 65172302Spjd { "delay", TOK_DELAY }, 66172302Spjd { "link", TOK_LINK }, 67172302Spjd { "pipe", TOK_PIPE }, 68172302Spjd { "queue", TOK_QUEUE }, 69172302Spjd { "flowset", TOK_FLOWSET }, 70172302Spjd { "sched", TOK_SCHED }, 71172302Spjd { "pri", TOK_PRI }, 72172302Spjd { "priority", TOK_PRI }, 73172302Spjd { "type", TOK_TYPE }, 74172302Spjd { "flow-id", TOK_FLOWID}, 75172302Spjd { "dst-ipv6", TOK_DSTIP6}, 76172302Spjd { "dst-ip6", TOK_DSTIP6}, 77172302Spjd { "src-ipv6", TOK_SRCIP6}, 78172302Spjd { "src-ip6", TOK_SRCIP6}, 79172302Spjd { "profile", TOK_PROFILE}, 80172302Spjd { "burst", TOK_BURST}, 81172302Spjd { "dummynet-params", TOK_NULL }, 82172302Spjd { NULL, 0 } /* terminator */ 83172302Spjd}; 84172302Spjd 85172302Spjd#define O_NEXT(p, len) ((void *)((char *)p + len)) 86172302Spjd 87172302Spjdstatic void 88172302Spjdoid_fill(struct dn_id *oid, int len, int type, uintptr_t id) 89172302Spjd{ 90172302Spjd oid->len = len; 91172302Spjd oid->type = type; 92172302Spjd oid->subtype = 0; 93172302Spjd oid->id = id; 94172302Spjd} 95172302Spjd 96172302Spjd/* make room in the buffer and move the pointer forward */ 97172302Spjdstatic void * 98172302Spjdo_next(struct dn_id **o, int len, int type) 99172302Spjd{ 100172302Spjd struct dn_id *ret = *o; 101172302Spjd oid_fill(ret, len, type, 0); 102172302Spjd *o = O_NEXT(*o, len); 103172302Spjd return ret; 104172302Spjd} 105172302Spjd 106172302Spjd#if 0 107172302Spjdstatic int 108172302Spjdsort_q(void *arg, const void *pa, const void *pb) 109172302Spjd{ 110172302Spjd int rev = (co.do_sort < 0); 111172302Spjd int field = rev ? -co.do_sort : co.do_sort; 112172302Spjd long long res = 0; 113172302Spjd const struct dn_flow_queue *a = pa; 114172302Spjd const struct dn_flow_queue *b = pb; 115172302Spjd 116172302Spjd switch (field) { 117172302Spjd case 1: /* pkts */ 118172302Spjd res = a->len - b->len; 119172302Spjd break; 120172302Spjd case 2: /* bytes */ 121172302Spjd res = a->len_bytes - b->len_bytes; 122172302Spjd break; 123172302Spjd 124172302Spjd case 3: /* tot pkts */ 125172302Spjd res = a->tot_pkts - b->tot_pkts; 126172302Spjd break; 127172302Spjd 128172302Spjd case 4: /* tot bytes */ 129172302Spjd res = a->tot_bytes - b->tot_bytes; 130172302Spjd break; 131172302Spjd } 132172302Spjd if (res < 0) 133172302Spjd res = -1; 134172302Spjd if (res > 0) 135172302Spjd res = 1; 136172302Spjd return (int)(rev ? res : -res); 137172302Spjd} 138172302Spjd#endif 139172302Spjd 140172302Spjd/* print a mask and header for the subsequent list of flows */ 141172302Spjdstatic void 142172302Spjdprint_mask(struct ipfw_flow_id *id) 143172302Spjd{ 144172302Spjd if (!IS_IP6_FLOW_ID(id)) { 145172302Spjd printf(" " 146172302Spjd "mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n", 147172302Spjd id->proto, 148172302Spjd id->src_ip, id->src_port, 149172302Spjd id->dst_ip, id->dst_port); 150172302Spjd 151172302Spjd printf("BKT Prot ___Source IP/port____ " 152172302Spjd "____Dest. IP/port____ " 153172302Spjd "Tot_pkt/bytes Pkt/Byte Drp\n"); 154172302Spjd } else { 155172302Spjd char buf[255]; 156172302Spjd printf("\n mask: proto: 0x%02x, flow_id: 0x%08x, ", 157172302Spjd id->proto, id->flow_id6); 158172302Spjd inet_ntop(AF_INET6, &(id->src_ip6), buf, sizeof(buf)); 159172302Spjd printf("%s/0x%04x -> ", buf, id->src_port); 160172302Spjd inet_ntop(AF_INET6, &(id->dst_ip6), buf, sizeof(buf)); 161172302Spjd printf("%s/0x%04x\n", buf, id->dst_port); 162172302Spjd 163172302Spjd printf("BKT ___Prot___ _flow-id_ " 164172302Spjd "______________Source IPv6/port_______________ " 165172302Spjd "_______________Dest. IPv6/port_______________ " 166172302Spjd "Tot_pkt/bytes Pkt/Byte Drp\n"); 167172302Spjd } 168172302Spjd} 169172302Spjd 170172302Spjdstatic void 171172302Spjdlist_flow(struct dn_flow *ni) 172172302Spjd{ 173172302Spjd char buff[255]; 174172302Spjd struct protoent *pe; 175172302Spjd struct in_addr ina; 176172302Spjd struct ipfw_flow_id *id = &ni->fid; 177172302Spjd 178172302Spjd pe = getprotobynumber(id->proto); 179172302Spjd /* XXX: Should check for IPv4 flows */ 180172302Spjd printf("%3u ", (ni->oid.id) & 0xff); 181172302Spjd if (!IS_IP6_FLOW_ID(id)) { 182172302Spjd if (pe) 183172302Spjd printf("%-4s ", pe->p_name); 184172302Spjd else 185172302Spjd printf("%4u ", id->proto); 186172302Spjd ina.s_addr = htonl(id->src_ip); 187172302Spjd printf("%15s/%-5d ", 188172302Spjd inet_ntoa(ina), id->src_port); 189172302Spjd ina.s_addr = htonl(id->dst_ip); 190172302Spjd printf("%15s/%-5d ", 191172302Spjd inet_ntoa(ina), id->dst_port); 192172302Spjd } else { 193172302Spjd /* Print IPv6 flows */ 194172302Spjd if (pe != NULL) 195172302Spjd printf("%9s ", pe->p_name); 196172302Spjd else 197172302Spjd printf("%9u ", id->proto); 198172302Spjd printf("%7d %39s/%-5d ", id->flow_id6, 199172302Spjd inet_ntop(AF_INET6, &(id->src_ip6), buff, sizeof(buff)), 200172302Spjd id->src_port); 201172302Spjd printf(" %39s/%-5d ", 202172302Spjd inet_ntop(AF_INET6, &(id->dst_ip6), buff, sizeof(buff)), 203172302Spjd id->dst_port); 204172302Spjd } 205172302Spjd printf("%4llu %8llu %2u %4u %3u\n", 206172302Spjd align_uint64(&ni->tot_pkts), 207172302Spjd align_uint64(&ni->tot_bytes), 208172302Spjd ni->length, ni->len_bytes, ni->drops); 209172302Spjd} 210172302Spjd 211172302Spjdstatic void 212172302Spjdprint_flowset_parms(struct dn_fs *fs, char *prefix) 213172302Spjd{ 214172302Spjd int l; 215172302Spjd char qs[30]; 216172302Spjd char plr[30]; 217172302Spjd char red[90]; /* Display RED parameters */ 218172302Spjd 219172302Spjd l = fs->qsize; 220172302Spjd if (fs->flags & DN_QSIZE_BYTES) { 221172302Spjd if (l >= 8192) 222172302Spjd sprintf(qs, "%d KB", l / 1024); 223172302Spjd else 224172302Spjd sprintf(qs, "%d B", l); 225172302Spjd } else 226172302Spjd sprintf(qs, "%3d sl.", l); 227172302Spjd if (fs->plr) 228172302Spjd sprintf(plr, "plr %f", 1.0 * fs->plr / (double)(0x7fffffff)); 229172302Spjd else 230172302Spjd plr[0] = '\0'; 231172302Spjd 232172302Spjd if (fs->flags & DN_IS_RED) /* RED parameters */ 233172302Spjd sprintf(red, 234172302Spjd "\n\t %cRED w_q %f min_th %d max_th %d max_p %f", 235172302Spjd (fs->flags & DN_IS_GENTLE_RED) ? 'G' : ' ', 236172302Spjd 1.0 * fs->w_q / (double)(1 << SCALE_RED), 237172302Spjd fs->min_th, 238172302Spjd fs->max_th, 239172302Spjd 1.0 * fs->max_p / (double)(1 << SCALE_RED)); 240172302Spjd else 241172302Spjd sprintf(red, "droptail"); 242172302Spjd 243172302Spjd if (prefix[0]) { 244172302Spjd printf("%s %s%s %d queues (%d buckets) %s\n", 245172302Spjd prefix, qs, plr, fs->oid.id, fs->buckets, red); 246172302Spjd prefix[0] = '\0'; 247172302Spjd } else { 248172302Spjd printf("q%05d %s%s %d flows (%d buckets) sched %d " 249172302Spjd "weight %d lmax %d pri %d %s\n", 250172302Spjd fs->fs_nr, qs, plr, fs->oid.id, fs->buckets, 251172302Spjd fs->sched_nr, fs->par[0], fs->par[1], fs->par[2], red); 252172302Spjd if (fs->flags & DN_HAVE_MASK) 253172302Spjd print_mask(&fs->flow_mask); 254172302Spjd } 255172302Spjd} 256172302Spjd 257172302Spjdstatic void 258172302Spjdprint_extra_delay_parms(struct dn_profile *p) 259172302Spjd{ 260172302Spjd double loss; 261172302Spjd if (p->samples_no <= 0) 262172302Spjd return; 263172302Spjd 264172302Spjd loss = p->loss_level; 265172302Spjd loss /= p->samples_no; 266172302Spjd printf("\t profile: name \"%s\" loss %f samples %d\n", 267172302Spjd p->name, loss, p->samples_no); 268172302Spjd} 269172302Spjd 270172302Spjdstatic void 271172302Spjdflush_buf(char *buf) 272172302Spjd{ 273172302Spjd if (buf[0]) 274172302Spjd printf("%s\n", buf); 275172302Spjd buf[0] = '\0'; 276172302Spjd} 277172302Spjd 278172302Spjd/* 279172302Spjd * generic list routine. We expect objects in a specific order, i.e. 280172302Spjd * PIPES AND SCHEDULERS: 281172302Spjd * link; scheduler; internal flowset if any; instances 282172302Spjd * we can tell a pipe from the number. 283172302Spjd * 284172302Spjd * FLOWSETS: 285172302Spjd * flowset; queues; 286172302Spjd * link i (int queue); scheduler i; si(i) { flowsets() : queues } 287172302Spjd */ 288172302Spjdstatic void 289172302Spjdlist_pipes(struct dn_id *oid, struct dn_id *end) 290172302Spjd{ 291172302Spjd char buf[160]; /* pending buffer */ 292172302Spjd buf[0] = '\0'; 293172302Spjd 294172302Spjd for (; oid != end; oid = O_NEXT(oid, oid->len)) { 295172302Spjd if (oid->len < sizeof(*oid)) 296172302Spjd errx(1, "invalid oid len %d\n", oid->len); 297172302Spjd 298172302Spjd switch (oid->type) { 299172302Spjd default: 300172302Spjd flush_buf(buf); 301172302Spjd printf("unrecognized object %d size %d\n", oid->type, oid->len); 302172302Spjd break; 303172302Spjd case DN_TEXT: /* list of attached flowsets */ 304172302Spjd { 305172302Spjd int i, l; 306172302Spjd struct { 307172302Spjd struct dn_id id; 308172302Spjd uint32_t p[0]; 309172302Spjd } *d = (void *)oid; 310172302Spjd l = (oid->len - sizeof(*oid))/sizeof(d->p[0]); 311172302Spjd if (l == 0) 312172302Spjd break; 313172302Spjd printf(" Children flowsets: "); 314172302Spjd for (i = 0; i < l; i++) 315172302Spjd printf("%u ", d->p[i]); 316172302Spjd printf("\n"); 317172302Spjd break; 318172302Spjd } 319172302Spjd case DN_CMD_GET: 320172302Spjd if (co.verbose) 321172302Spjd printf("answer for cmd %d, len %d\n", oid->type, oid->id); 322172302Spjd break; 323172302Spjd case DN_SCH: { 324172302Spjd struct dn_sch *s = (struct dn_sch *)oid; 325172302Spjd flush_buf(buf); 326172302Spjd printf(" sched %d type %s flags 0x%x %d buckets %d active\n", 327172302Spjd s->sched_nr, 328172302Spjd s->name, s->flags, s->buckets, s->oid.id); 329172302Spjd if (s->flags & DN_HAVE_MASK) 330172302Spjd print_mask(&s->sched_mask); 331172302Spjd } 332172302Spjd break; 333172302Spjd 334172302Spjd case DN_FLOW: 335172302Spjd list_flow((struct dn_flow *)oid); 336172302Spjd break; 337172302Spjd 338172302Spjd case DN_LINK: { 339172302Spjd struct dn_link *p = (struct dn_link *)oid; 340172302Spjd double b = p->bandwidth; 341172302Spjd char bwbuf[30]; 342172302Spjd char burst[5 + 7]; 343172302Spjd 344172302Spjd /* This starts a new object so flush buffer */ 345172302Spjd flush_buf(buf); 346172302Spjd /* data rate */ 347172302Spjd if (b == 0) 348172302Spjd sprintf(bwbuf, "unlimited "); 349172302Spjd else if (b >= 1000000) 350172302Spjd sprintf(bwbuf, "%7.3f Mbit/s", b/1000000); 351172302Spjd else if (b >= 1000) 352172302Spjd sprintf(bwbuf, "%7.3f Kbit/s", b/1000); 353172302Spjd else 354172302Spjd sprintf(bwbuf, "%7.3f bit/s ", b); 355172302Spjd 356172302Spjd if (humanize_number(burst, sizeof(burst), p->burst, 357172302Spjd "", HN_AUTOSCALE, 0) < 0 || co.verbose) 358172302Spjd sprintf(burst, "%d", (int)p->burst); 359172302Spjd sprintf(buf, "%05d: %s %4d ms burst %s", 360172302Spjd p->link_nr % DN_MAX_ID, bwbuf, p->delay, burst); 361172302Spjd } 362172302Spjd break; 363172302Spjd 364172302Spjd case DN_FS: 365172302Spjd print_flowset_parms((struct dn_fs *)oid, buf); 366172302Spjd break; 367172302Spjd case DN_PROFILE: 368172302Spjd flush_buf(buf); 369172302Spjd print_extra_delay_parms((struct dn_profile *)oid); 370172302Spjd } 371172302Spjd flush_buf(buf); // XXX does it really go here ? 372172302Spjd } 373172302Spjd} 374172302Spjd 375172302Spjd/* 376172302Spjd * Delete pipe, queue or scheduler i 377172302Spjd */ 378172302Spjdint 379172302Spjdipfw_delete_pipe(int do_pipe, int i) 380172302Spjd{ 381172302Spjd struct { 382172302Spjd struct dn_id oid; 383172302Spjd uintptr_t a[1]; /* add more if we want a list */ 384172302Spjd } cmd; 385172302Spjd oid_fill((void *)&cmd, sizeof(cmd), DN_CMD_DELETE, DN_API_VERSION); 386172302Spjd cmd.oid.subtype = (do_pipe == 1) ? DN_LINK : 387172302Spjd ( (do_pipe == 2) ? DN_FS : DN_SCH); 388172302Spjd cmd.a[0] = i; 389172302Spjd i = do_cmd(IP_DUMMYNET3, &cmd, cmd.oid.len); 390172302Spjd if (i) { 391172302Spjd i = 1; 392172302Spjd warn("rule %u: setsockopt(IP_DUMMYNET_DEL)", i); 393172302Spjd } 394172302Spjd return i; 395172302Spjd} 396172302Spjd 397172302Spjd/* 398172302Spjd * Code to parse delay profiles. 399172302Spjd * 400172302Spjd * Some link types introduce extra delays in the transmission 401172302Spjd * of a packet, e.g. because of MAC level framing, contention on 402172302Spjd * the use of the channel, MAC level retransmissions and so on. 403172302Spjd * From our point of view, the channel is effectively unavailable 404172302Spjd * for this extra time, which is constant or variable depending 405172302Spjd * on the link type. Additionally, packets may be dropped after this 406172302Spjd * time (e.g. on a wireless link after too many retransmissions). 407172302Spjd * We can model the additional delay with an empirical curve 408172302Spjd * that represents its distribution. 409172302Spjd * 410172302Spjd * cumulative probability 411172302Spjd * 1.0 ^ 412172302Spjd * | 413172302Spjd * L +-- loss-level x 414172302Spjd * | ****** 415172302Spjd * | * 416172302Spjd * | ***** 417172302Spjd * | * 418172302Spjd * | ** 419172302Spjd * | * 420172302Spjd * +-------*-------------------> 421172302Spjd * delay 422172302Spjd * 423172302Spjd * The empirical curve may have both vertical and horizontal lines. 424172302Spjd * Vertical lines represent constant delay for a range of 425172302Spjd * probabilities; horizontal lines correspond to a discontinuty 426172302Spjd * in the delay distribution: the link will use the largest delay 427172302Spjd * for a given probability. 428172302Spjd * 429172302Spjd * To pass the curve to dummynet, we must store the parameters 430172302Spjd * in a file as described below, and issue the command 431172302Spjd * 432172302Spjd * ipfw pipe <n> config ... bw XXX profile <filename> ... 433172302Spjd * 434172302Spjd * The file format is the following, with whitespace acting as 435172302Spjd * a separator and '#' indicating the beginning a comment: 436172302Spjd * 437172302Spjd * samples N 438172302Spjd * the number of samples used in the internal 439172302Spjd * representation (2..1024; default 100); 440172302Spjd * 441172302Spjd * loss-level L 442172302Spjd * The probability above which packets are lost. 443172302Spjd * (0.0 <= L <= 1.0, default 1.0 i.e. no loss); 444172302Spjd * 445172302Spjd * name identifier 446172302Spjd * Optional a name (listed by "ipfw pipe show") 447172302Spjd * to identify the distribution; 448172302Spjd * 449172302Spjd * "delay prob" | "prob delay" 450172302Spjd * One of these two lines is mandatory and defines 451172302Spjd * the format of the following lines with data points. 452172302Spjd * 453172302Spjd * XXX YYY 454172302Spjd * 2 or more lines representing points in the curve, 455172302Spjd * with either delay or probability first, according 456172302Spjd * to the chosen format. 457172302Spjd * The unit for delay is milliseconds. 458172302Spjd * 459172302Spjd * Data points does not need to be ordered or equal to the number 460172302Spjd * specified in the "samples" line. ipfw will sort and interpolate 461172302Spjd * the curve as needed. 462172302Spjd * 463172302Spjd * Example of a profile file: 464172302Spjd 465172302Spjd name bla_bla_bla 466172302Spjd samples 100 467172302Spjd loss-level 0.86 468172302Spjd prob delay 469172302Spjd 0 200 # minimum overhead is 200ms 470172302Spjd 0.5 200 471172302Spjd 0.5 300 472172302Spjd 0.8 1000 473172302Spjd 0.9 1300 474172302Spjd 1 1300 475172302Spjd 476172302Spjd * Internally, we will convert the curve to a fixed number of 477172302Spjd * samples, and when it is time to transmit a packet we will 478172302Spjd * model the extra delay as extra bits in the packet. 479172302Spjd * 480172302Spjd */ 481172302Spjd 482172302Spjd#define ED_MAX_LINE_LEN 256+ED_MAX_NAME_LEN 483172302Spjd#define ED_TOK_SAMPLES "samples" 484172302Spjd#define ED_TOK_LOSS "loss-level" 485172302Spjd#define ED_TOK_NAME "name" 486172302Spjd#define ED_TOK_DELAY "delay" 487172302Spjd#define ED_TOK_PROB "prob" 488172302Spjd#define ED_TOK_BW "bw" 489172302Spjd#define ED_SEPARATORS " \t\n" 490172302Spjd#define ED_MIN_SAMPLES_NO 2 491172302Spjd 492172302Spjd/* 493172302Spjd * returns 1 if s is a non-negative number, with at least one '.' 494172302Spjd */ 495172302Spjdstatic int 496172302Spjdis_valid_number(const char *s) 497172302Spjd{ 498172302Spjd int i, dots_found = 0; 499172302Spjd int len = strlen(s); 500172302Spjd 501172302Spjd for (i = 0; i<len; ++i) 502172302Spjd if (!isdigit(s[i]) && (s[i] !='.' || ++dots_found > 1)) 503172302Spjd return 0; 504172302Spjd return 1; 505172302Spjd} 506172302Spjd 507172302Spjd/* 508172302Spjd * Take as input a string describing a bandwidth value 509172302Spjd * and return the numeric bandwidth value. 510172302Spjd * set clocking interface or bandwidth value 511172302Spjd */ 512172302Spjdstatic void 513172302Spjdread_bandwidth(char *arg, int *bandwidth, char *if_name, int namelen) 514172302Spjd{ 515172302Spjd if (*bandwidth != -1) 516172302Spjd warnx("duplicate token, override bandwidth value!"); 517172302Spjd 518172302Spjd if (arg[0] >= 'a' && arg[0] <= 'z') { 519172302Spjd if (!if_name) { 520172302Spjd errx(1, "no if support"); 521172302Spjd } 522172302Spjd if (namelen >= IFNAMSIZ) 523172302Spjd warn("interface name truncated"); 524172302Spjd namelen--; 525172302Spjd /* interface name */ 526172302Spjd strncpy(if_name, arg, namelen); 527172302Spjd if_name[namelen] = '\0'; 528172302Spjd *bandwidth = 0; 529172302Spjd } else { /* read bandwidth value */ 530172302Spjd int bw; 531172302Spjd char *end = NULL; 532172302Spjd 533172302Spjd bw = strtoul(arg, &end, 0); 534172302Spjd if (*end == 'K' || *end == 'k') { 535172302Spjd end++; 536172302Spjd bw *= 1000; 537172302Spjd } else if (*end == 'M') { 538172302Spjd end++; 539172302Spjd bw *= 1000000; 540172302Spjd } 541172302Spjd if ((*end == 'B' && 542172302Spjd _substrcmp2(end, "Bi", "Bit/s") != 0) || 543172302Spjd _substrcmp2(end, "by", "bytes") == 0) 544172302Spjd bw *= 8; 545172302Spjd 546172302Spjd if (bw < 0) 547172302Spjd errx(EX_DATAERR, "bandwidth too large"); 548172302Spjd 549172302Spjd *bandwidth = bw; 550172302Spjd if (if_name) 551172302Spjd if_name[0] = '\0'; 552172302Spjd } 553172302Spjd} 554172302Spjd 555172302Spjdstruct point { 556172302Spjd double prob; 557172302Spjd double delay; 558172302Spjd}; 559172302Spjd 560172302Spjdstatic int 561172302Spjdcompare_points(const void *vp1, const void *vp2) 562172302Spjd{ 563172302Spjd const struct point *p1 = vp1; 564172302Spjd const struct point *p2 = vp2; 565172302Spjd double res = 0; 566172302Spjd 567172302Spjd res = p1->prob - p2->prob; 568172302Spjd if (res == 0) 569172302Spjd res = p1->delay - p2->delay; 570172302Spjd if (res < 0) 571172302Spjd return -1; 572172302Spjd else if (res > 0) 573172302Spjd return 1; 574172302Spjd else 575172302Spjd return 0; 576172302Spjd} 577172302Spjd 578172302Spjd#define ED_EFMT(s) EX_DATAERR,"error in %s at line %d: "#s,filename,lineno 579172302Spjd 580172302Spjdstatic void 581172302Spjdload_extra_delays(const char *filename, struct dn_profile *p, 582172302Spjd struct dn_link *link) 583172302Spjd{ 584172302Spjd char line[ED_MAX_LINE_LEN]; 585172302Spjd FILE *f; 586172302Spjd int lineno = 0; 587172302Spjd int i; 588172302Spjd 589172302Spjd int samples = -1; 590172302Spjd double loss = -1.0; 591172302Spjd char profile_name[ED_MAX_NAME_LEN]; 592172302Spjd int delay_first = -1; 593172302Spjd int do_points = 0; 594172302Spjd struct point points[ED_MAX_SAMPLES_NO]; 595172302Spjd int points_no = 0; 596172302Spjd 597172302Spjd /* XXX link never NULL? */ 598172302Spjd p->link_nr = link->link_nr; 599172302Spjd 600172302Spjd profile_name[0] = '\0'; 601172302Spjd f = fopen(filename, "r"); 602172302Spjd if (f == NULL) 603172302Spjd err(EX_UNAVAILABLE, "fopen: %s", filename); 604172302Spjd 605172302Spjd while (fgets(line, ED_MAX_LINE_LEN, f)) { /* read commands */ 606172302Spjd char *s, *cur = line, *name = NULL, *arg = NULL; 607172302Spjd 608172302Spjd ++lineno; 609172302Spjd 610172302Spjd /* parse the line */ 611172302Spjd while (cur) { 612172302Spjd s = strsep(&cur, ED_SEPARATORS); 613172302Spjd if (s == NULL || *s == '#') 614172302Spjd break; 615172302Spjd if (*s == '\0') 616172302Spjd continue; 617172302Spjd if (arg) 618172302Spjd errx(ED_EFMT("too many arguments")); 619172302Spjd if (name == NULL) 620172302Spjd name = s; 621172302Spjd else 622172302Spjd arg = s; 623172302Spjd } 624172302Spjd if (name == NULL) /* empty line */ 625172302Spjd continue; 626172302Spjd if (arg == NULL) 627172302Spjd errx(ED_EFMT("missing arg for %s"), name); 628172302Spjd 629172302Spjd if (!strcasecmp(name, ED_TOK_SAMPLES)) { 630172302Spjd if (samples > 0) 631172302Spjd errx(ED_EFMT("duplicate ``samples'' line")); 632172302Spjd if (atoi(arg) <=0) 633172302Spjd errx(ED_EFMT("invalid number of samples")); 634172302Spjd samples = atoi(arg); 635172302Spjd if (samples>ED_MAX_SAMPLES_NO) 636172302Spjd errx(ED_EFMT("too many samples, maximum is %d"), 637172302Spjd ED_MAX_SAMPLES_NO); 638172302Spjd do_points = 0; 639172302Spjd } else if (!strcasecmp(name, ED_TOK_BW)) { 640172302Spjd char buf[IFNAMSIZ]; 641172302Spjd read_bandwidth(arg, &link->bandwidth, buf, sizeof(buf)); 642172302Spjd } else if (!strcasecmp(name, ED_TOK_LOSS)) { 643172302Spjd if (loss != -1.0) 644172302Spjd errx(ED_EFMT("duplicated token: %s"), name); 645172302Spjd if (!is_valid_number(arg)) 646172302Spjd errx(ED_EFMT("invalid %s"), arg); 647172302Spjd loss = atof(arg); 648172302Spjd if (loss > 1) 649172302Spjd errx(ED_EFMT("%s greater than 1.0"), name); 650172302Spjd do_points = 0; 651172302Spjd } else if (!strcasecmp(name, ED_TOK_NAME)) { 652172302Spjd if (profile_name[0] != '\0') 653172302Spjd errx(ED_EFMT("duplicated token: %s"), name); 654172302Spjd strncpy(profile_name, arg, sizeof(profile_name) - 1); 655172302Spjd profile_name[sizeof(profile_name)-1] = '\0'; 656172302Spjd do_points = 0; 657172302Spjd } else if (!strcasecmp(name, ED_TOK_DELAY)) { 658172302Spjd if (do_points) 659172302Spjd errx(ED_EFMT("duplicated token: %s"), name); 660172302Spjd delay_first = 1; 661172302Spjd do_points = 1; 662172302Spjd } else if (!strcasecmp(name, ED_TOK_PROB)) { 663172302Spjd if (do_points) 664172302Spjd errx(ED_EFMT("duplicated token: %s"), name); 665172302Spjd delay_first = 0; 666172302Spjd do_points = 1; 667172302Spjd } else if (do_points) { 668172302Spjd if (!is_valid_number(name) || !is_valid_number(arg)) 669172302Spjd errx(ED_EFMT("invalid point found")); 670172302Spjd if (delay_first) { 671172302Spjd points[points_no].delay = atof(name); 672172302Spjd points[points_no].prob = atof(arg); 673172302Spjd } else { 674172302Spjd points[points_no].delay = atof(arg); 675172302Spjd points[points_no].prob = atof(name); 676172302Spjd } 677172302Spjd if (points[points_no].prob > 1.0) 678172302Spjd errx(ED_EFMT("probability greater than 1.0")); 679172302Spjd ++points_no; 680172302Spjd } else { 681172302Spjd errx(ED_EFMT("unrecognised command '%s'"), name); 682172302Spjd } 683172302Spjd } 684172302Spjd 685172302Spjd fclose (f); 686172302Spjd 687172302Spjd if (samples == -1) { 688172302Spjd warnx("'%s' not found, assuming 100", ED_TOK_SAMPLES); 689172302Spjd samples = 100; 690172302Spjd } 691172302Spjd 692172302Spjd if (loss == -1.0) { 693172302Spjd warnx("'%s' not found, assuming no loss", ED_TOK_LOSS); 694172302Spjd loss = 1; 695172302Spjd } 696172302Spjd 697172302Spjd /* make sure that there are enough points. */ 698172302Spjd if (points_no < ED_MIN_SAMPLES_NO) 699172302Spjd errx(ED_EFMT("too few samples, need at least %d"), 700172302Spjd ED_MIN_SAMPLES_NO); 701172302Spjd 702172302Spjd qsort(points, points_no, sizeof(struct point), compare_points); 703172302Spjd 704172302Spjd /* interpolation */ 705172302Spjd for (i = 0; i<points_no-1; ++i) { 706172302Spjd double y1 = points[i].prob * samples; 707172302Spjd double x1 = points[i].delay; 708172302Spjd double y2 = points[i+1].prob * samples; 709172302Spjd double x2 = points[i+1].delay; 710172302Spjd 711172302Spjd int ix = y1; 712172302Spjd int stop = y2; 713172302Spjd 714172302Spjd if (x1 == x2) { 715172302Spjd for (; ix<stop; ++ix) 716172302Spjd p->samples[ix] = x1; 717172302Spjd } else { 718172302Spjd double m = (y2-y1)/(x2-x1); 719172302Spjd double c = y1 - m*x1; 720172302Spjd for (; ix<stop ; ++ix) 721172302Spjd p->samples[ix] = (ix - c)/m; 722172302Spjd } 723172302Spjd } 724172302Spjd p->samples_no = samples; 725172302Spjd p->loss_level = loss * samples; 726172302Spjd strncpy(p->name, profile_name, sizeof(p->name)); 727172302Spjd} 728172302Spjd 729172302Spjd/* 730172302Spjd * configuration of pipes, schedulers, flowsets. 731172302Spjd * When we configure a new scheduler, an empty pipe is created, so: 732172304Spjd * 733172302Spjd * do_pipe = 1 -> "pipe N config ..." only for backward compatibility 734172302Spjd * sched N+Delta type fifo sched_mask ... 735172302Spjd * pipe N+Delta <parameters> 736172302Spjd * flowset N+Delta pipe N+Delta (no parameters) 737172302Spjd * sched N type wf2q+ sched_mask ... 738172302Spjd * pipe N <parameters> 739172302Spjd * 740172302Spjd * do_pipe = 2 -> flowset N config 741172302Spjd * flowset N parameters 742172302Spjd * 743172302Spjd * do_pipe = 3 -> sched N config 744172302Spjd * sched N parameters (default no pipe) 745172302Spjd * optional Pipe N config ... 746172302Spjd * pipe ==> 747172302Spjd */ 748172302Spjdvoid 749172302Spjdipfw_config_pipe(int ac, char **av) 750172302Spjd{ 751172302Spjd int i, j; 752172302Spjd char *end; 753172302Spjd void *par = NULL; 754172302Spjd struct dn_id *buf, *base; 755172302Spjd struct dn_sch *sch = NULL; 756172302Spjd struct dn_link *p = NULL; 757172302Spjd struct dn_fs *fs = NULL; 758172302Spjd struct dn_profile *pf = NULL; 759172302Spjd struct ipfw_flow_id *mask = NULL; 760172302Spjd int lmax; 761172302Spjd uint32_t _foo = 0, *flags = &_foo , *buckets = &_foo; 762172302Spjd 763172302Spjd /* 764172302Spjd * allocate space for 1 header, 765172302Spjd * 1 scheduler, 1 link, 1 flowset, 1 profile 766172302Spjd */ 767172302Spjd lmax = sizeof(struct dn_id); /* command header */ 768172302Spjd lmax += sizeof(struct dn_sch) + sizeof(struct dn_link) + 769172302Spjd sizeof(struct dn_fs) + sizeof(struct dn_profile); 770172302Spjd 771172302Spjd av++; ac--; 772172302Spjd /* Pipe number */ 773172302Spjd if (ac && isdigit(**av)) { 774172302Spjd i = atoi(*av); av++; ac--; 775172302Spjd } else 776172302Spjd i = -1; 777172302Spjd if (i <= 0) 778172302Spjd errx(EX_USAGE, "need a pipe/flowset/sched number"); 779172302Spjd base = buf = safe_calloc(1, lmax); 780172302Spjd /* all commands start with a 'CONFIGURE' and a version */ 781172302Spjd o_next(&buf, sizeof(struct dn_id), DN_CMD_CONFIG); 782172302Spjd base->id = DN_API_VERSION; 783172302Spjd 784172302Spjd switch (co.do_pipe) { 785172302Spjd case 1: /* "pipe N config ..." */ 786172302Spjd /* Allocate space for the WF2Q+ scheduler, its link 787172302Spjd * and the FIFO flowset. Set the number, but leave 788172302Spjd * the scheduler subtype and other parameters to 0 789172302Spjd * so the kernel will use appropriate defaults. 790172302Spjd * XXX todo: add a flag to record if a parameter 791172302Spjd * is actually configured. 792172302Spjd * If we do a 'pipe config' mask -> sched_mask. 793172302Spjd * The FIFO scheduler and link are derived from the 794172302Spjd * WF2Q+ one in the kernel. 795172302Spjd */ 796172302Spjd sch = o_next(&buf, sizeof(*sch), DN_SCH); 797172302Spjd p = o_next(&buf, sizeof(*p), DN_LINK); 798172302Spjd fs = o_next(&buf, sizeof(*fs), DN_FS); 799172302Spjd 800172302Spjd sch->sched_nr = i; 801172302Spjd sch->oid.subtype = 0; /* defaults to WF2Q+ */ 802172302Spjd mask = &sch->sched_mask; 803172302Spjd flags = &sch->flags; 804172302Spjd buckets = &sch->buckets; 805172302Spjd *flags |= DN_PIPE_CMD; 806172302Spjd 807172302Spjd p->link_nr = i; 808172302Spjd 809172302Spjd /* This flowset is only for the FIFO scheduler */ 810172302Spjd fs->fs_nr = i + 2*DN_MAX_ID; 811172302Spjd fs->sched_nr = i + DN_MAX_ID; 812172302Spjd break; 813172302Spjd 814172302Spjd case 2: /* "queue N config ... " */ 815172302Spjd fs = o_next(&buf, sizeof(*fs), DN_FS); 816172302Spjd fs->fs_nr = i; 817172302Spjd mask = &fs->flow_mask; 818172302Spjd flags = &fs->flags; 819172302Spjd buckets = &fs->buckets; 820172302Spjd break; 821172302Spjd 822172302Spjd case 3: /* "sched N config ..." */ 823172302Spjd sch = o_next(&buf, sizeof(*sch), DN_SCH); 824172302Spjd fs = o_next(&buf, sizeof(*fs), DN_FS); 825172302Spjd sch->sched_nr = i; 826172302Spjd mask = &sch->sched_mask; 827172302Spjd flags = &sch->flags; 828172302Spjd buckets = &sch->buckets; 829172302Spjd /* fs is used only with !MULTIQUEUE schedulers */ 830172302Spjd fs->fs_nr = i + DN_MAX_ID; 831172302Spjd fs->sched_nr = i; 832172302Spjd break; 833172302Spjd } 834172302Spjd /* set to -1 those fields for which we want to reuse existing 835172302Spjd * values from the kernel. 836172302Spjd * Also, *_nr and subtype = 0 mean reuse the value from the kernel. 837172302Spjd * XXX todo: support reuse of the mask. 838172302Spjd */ 839172302Spjd if (p) 840172302Spjd p->bandwidth = -1; 841172302Spjd for (j = 0; j < sizeof(fs->par)/sizeof(fs->par[0]); j++) 842172302Spjd fs->par[j] = -1; 843172302Spjd while (ac > 0) { 844172302Spjd double d; 845172302Spjd int tok = match_token(dummynet_params, *av); 846172302Spjd ac--; av++; 847172302Spjd 848172302Spjd switch(tok) { 849172302Spjd case TOK_NOERROR: 850172302Spjd NEED(fs, "noerror is only for pipes"); 851172302Spjd fs->flags |= DN_NOERROR; 852172302Spjd break; 853172302Spjd 854172302Spjd case TOK_PLR: 855172302Spjd NEED(fs, "plr is only for pipes"); 856172302Spjd NEED1("plr needs argument 0..1\n"); 857172302Spjd d = strtod(av[0], NULL); 858172302Spjd if (d > 1) 859172302Spjd d = 1; 860172302Spjd else if (d < 0) 861172302Spjd d = 0; 862172302Spjd fs->plr = (int)(d*0x7fffffff); 863172302Spjd ac--; av++; 864172302Spjd break; 865172302Spjd 866172302Spjd case TOK_QUEUE: 867172302Spjd NEED(fs, "queue is only for pipes or flowsets"); 868172302Spjd NEED1("queue needs queue size\n"); 869172302Spjd end = NULL; 870172302Spjd fs->qsize = strtoul(av[0], &end, 0); 871172302Spjd if (*end == 'K' || *end == 'k') { 872172302Spjd fs->flags |= DN_QSIZE_BYTES; 873172302Spjd fs->qsize *= 1024; 874172302Spjd } else if (*end == 'B' || 875172302Spjd _substrcmp2(end, "by", "bytes") == 0) { 876172302Spjd fs->flags |= DN_QSIZE_BYTES; 877172302Spjd } 878172302Spjd ac--; av++; 879172302Spjd break; 880172302Spjd 881172302Spjd case TOK_BUCKETS: 882172302Spjd NEED(fs, "buckets is only for pipes or flowsets"); 883172302Spjd NEED1("buckets needs argument\n"); 884172302Spjd *buckets = strtoul(av[0], NULL, 0); 885172302Spjd ac--; av++; 886172302Spjd break; 887172302Spjd 888172302Spjd case TOK_FLOW_MASK: 889172302Spjd case TOK_SCHED_MASK: 890172302Spjd case TOK_MASK: 891172302Spjd NEED(mask, "tok_mask"); 892172302Spjd NEED1("mask needs mask specifier\n"); 893172302Spjd /* 894172302Spjd * per-flow queue, mask is dst_ip, dst_port, 895172302Spjd * src_ip, src_port, proto measured in bits 896172302Spjd */ 897172302Spjd par = NULL; 898172302Spjd 899172302Spjd bzero(mask, sizeof(*mask)); 900172302Spjd end = NULL; 901172302Spjd 902172302Spjd while (ac >= 1) { 903172302Spjd uint32_t *p32 = NULL; 904172302Spjd uint16_t *p16 = NULL; 905172302Spjd uint32_t *p20 = NULL; 906172302Spjd struct in6_addr *pa6 = NULL; 907172302Spjd uint32_t a; 908172302Spjd 909172302Spjd tok = match_token(dummynet_params, *av); 910172302Spjd ac--; av++; 911172302Spjd switch(tok) { 912172302Spjd case TOK_ALL: 913172302Spjd /* 914172302Spjd * special case, all bits significant 915172302Spjd */ 916172302Spjd mask->dst_ip = ~0; 917172302Spjd mask->src_ip = ~0; 918172302Spjd mask->dst_port = ~0; 919172302Spjd mask->src_port = ~0; 920172302Spjd mask->proto = ~0; 921172302Spjd n2mask(&mask->dst_ip6, 128); 922172302Spjd n2mask(&mask->src_ip6, 128); 923172302Spjd mask->flow_id6 = ~0; 924172302Spjd *flags |= DN_HAVE_MASK; 925172302Spjd goto end_mask; 926172302Spjd 927172302Spjd case TOK_DSTIP: 928172302Spjd mask->addr_type = 4; 929172302Spjd p32 = &mask->dst_ip; 930172302Spjd break; 931172302Spjd 932172302Spjd case TOK_SRCIP: 933172302Spjd mask->addr_type = 4; 934172302Spjd p32 = &mask->src_ip; 935172302Spjd break; 936172302Spjd 937172302Spjd case TOK_DSTIP6: 938172302Spjd mask->addr_type = 6; 939172302Spjd pa6 = &mask->dst_ip6; 940172302Spjd break; 941172302Spjd 942172302Spjd case TOK_SRCIP6: 943172302Spjd mask->addr_type = 6; 944172302Spjd pa6 = &mask->src_ip6; 945172302Spjd break; 946172302Spjd 947172302Spjd case TOK_FLOWID: 948172302Spjd mask->addr_type = 6; 949172302Spjd p20 = &mask->flow_id6; 950172302Spjd break; 951172302Spjd 952172302Spjd case TOK_DSTPORT: 953172302Spjd p16 = &mask->dst_port; 954172302Spjd break; 955172302Spjd 956172302Spjd case TOK_SRCPORT: 957172302Spjd p16 = &mask->src_port; 958172302Spjd break; 959172302Spjd 960172302Spjd case TOK_PROTO: 961172302Spjd break; 962172302Spjd 963172302Spjd default: 964172302Spjd ac++; av--; /* backtrack */ 965172302Spjd goto end_mask; 966172302Spjd } 967172302Spjd if (ac < 1) 968172302Spjd errx(EX_USAGE, "mask: value missing"); 969172302Spjd if (*av[0] == '/') { 970172302Spjd a = strtoul(av[0]+1, &end, 0); 971172302Spjd if (pa6 == NULL) 972172302Spjd a = (a == 32) ? ~0 : (1 << a) - 1; 973172302Spjd } else 974172302Spjd a = strtoul(av[0], &end, 0); 975172302Spjd if (p32 != NULL) 976172302Spjd *p32 = a; 977172302Spjd else if (p16 != NULL) { 978172302Spjd if (a > 0xFFFF) 979172302Spjd errx(EX_DATAERR, 980172302Spjd "port mask must be 16 bit"); 981172302Spjd *p16 = (uint16_t)a; 982172302Spjd } else if (p20 != NULL) { 983172302Spjd if (a > 0xfffff) 984172302Spjd errx(EX_DATAERR, 985172302Spjd "flow_id mask must be 20 bit"); 986172302Spjd *p20 = (uint32_t)a; 987172302Spjd } else if (pa6 != NULL) { 988172302Spjd if (a > 128) 989172302Spjd errx(EX_DATAERR, 990172302Spjd "in6addr invalid mask len"); 991172302Spjd else 992172302Spjd n2mask(pa6, a); 993172302Spjd } else { 994172302Spjd if (a > 0xFF) 995172302Spjd errx(EX_DATAERR, 996172302Spjd "proto mask must be 8 bit"); 997172302Spjd fs->flow_mask.proto = (uint8_t)a; 998172302Spjd } 999172302Spjd if (a != 0) 1000172302Spjd *flags |= DN_HAVE_MASK; 1001172302Spjd ac--; av++; 1002172302Spjd } /* end while, config masks */ 1003172302Spjdend_mask: 1004172302Spjd break; 1005172302Spjd 1006172302Spjd case TOK_RED: 1007172302Spjd case TOK_GRED: 1008172302Spjd NEED1("red/gred needs w_q/min_th/max_th/max_p\n"); 1009172302Spjd fs->flags |= DN_IS_RED; 1010172302Spjd if (tok == TOK_GRED) 1011172302Spjd fs->flags |= DN_IS_GENTLE_RED; 1012172302Spjd /* 1013172302Spjd * the format for parameters is w_q/min_th/max_th/max_p 1014172302Spjd */ 1015172302Spjd if ((end = strsep(&av[0], "/"))) { 1016172302Spjd double w_q = strtod(end, NULL); 1017172302Spjd if (w_q > 1 || w_q <= 0) 1018172302Spjd errx(EX_DATAERR, "0 < w_q <= 1"); 1019172302Spjd fs->w_q = (int) (w_q * (1 << SCALE_RED)); 1020172302Spjd } 1021172302Spjd if ((end = strsep(&av[0], "/"))) { 1022172302Spjd fs->min_th = strtoul(end, &end, 0); 1023172302Spjd if (*end == 'K' || *end == 'k') 1024172302Spjd fs->min_th *= 1024; 1025172302Spjd } 1026172302Spjd if ((end = strsep(&av[0], "/"))) { 1027172302Spjd fs->max_th = strtoul(end, &end, 0); 1028172302Spjd if (*end == 'K' || *end == 'k') 1029172302Spjd fs->max_th *= 1024; 1030172302Spjd } 1031172302Spjd if ((end = strsep(&av[0], "/"))) { 1032172302Spjd double max_p = strtod(end, NULL); 1033172302Spjd if (max_p > 1 || max_p <= 0) 1034172302Spjd errx(EX_DATAERR, "0 < max_p <= 1"); 1035172302Spjd fs->max_p = (int)(max_p * (1 << SCALE_RED)); 1036172302Spjd } 1037172302Spjd ac--; av++; 1038172302Spjd break; 1039172302Spjd 1040172302Spjd case TOK_DROPTAIL: 1041172302Spjd NEED(fs, "droptail is only for flowsets"); 1042172302Spjd fs->flags &= ~(DN_IS_RED|DN_IS_GENTLE_RED); 1043172302Spjd break; 1044172302Spjd 1045172302Spjd case TOK_BW: 1046172302Spjd NEED(p, "bw is only for links"); 1047172302Spjd NEED1("bw needs bandwidth or interface\n"); 1048172302Spjd read_bandwidth(av[0], &p->bandwidth, NULL, 0); 1049172302Spjd ac--; av++; 1050172302Spjd break; 1051172302Spjd 1052172302Spjd case TOK_DELAY: 1053172302Spjd NEED(p, "delay is only for links"); 1054172302Spjd NEED1("delay needs argument 0..10000ms\n"); 1055172302Spjd p->delay = strtoul(av[0], NULL, 0); 1056172302Spjd ac--; av++; 1057172302Spjd break; 1058172302Spjd 1059172302Spjd case TOK_TYPE: { 1060172302Spjd int l; 1061172302Spjd NEED(sch, "type is only for schedulers"); 1062172302Spjd NEED1("type needs a string"); 1063172302Spjd l = strlen(av[0]); 1064172302Spjd if (l == 0 || l > 15) 1065172302Spjd errx(1, "type %s too long\n", av[0]); 1066172302Spjd strcpy(sch->name, av[0]); 1067172302Spjd sch->oid.subtype = 0; /* use string */ 1068172302Spjd ac--; av++; 1069172302Spjd break; 1070172302Spjd } 1071172302Spjd 1072172302Spjd case TOK_WEIGHT: 1073172302Spjd NEED(fs, "weight is only for flowsets"); 1074172302Spjd NEED1("weight needs argument\n"); 1075172302Spjd fs->par[0] = strtol(av[0], &end, 0); 1076172302Spjd ac--; av++; 1077172302Spjd break; 1078172302Spjd 1079172302Spjd case TOK_LMAX: 1080172302Spjd NEED(fs, "lmax is only for flowsets"); 1081172302Spjd NEED1("lmax needs argument\n"); 1082172302Spjd fs->par[1] = strtol(av[0], &end, 0); 1083172302Spjd ac--; av++; 1084172302Spjd break; 1085172302Spjd 1086172302Spjd case TOK_PRI: 1087172302Spjd NEED(fs, "priority is only for flowsets"); 1088172302Spjd NEED1("priority needs argument\n"); 1089172302Spjd fs->par[2] = strtol(av[0], &end, 0); 1090172302Spjd ac--; av++; 1091172302Spjd break; 1092172302Spjd 1093172302Spjd case TOK_SCHED: 1094172302Spjd case TOK_PIPE: 1095172302Spjd NEED(fs, "pipe/sched"); 1096172302Spjd NEED1("pipe/link/sched needs number\n"); 1097172302Spjd fs->sched_nr = strtoul(av[0], &end, 0); 1098172302Spjd ac--; av++; 1099172302Spjd break; 1100172302Spjd 1101172302Spjd case TOK_PROFILE: 1102172302Spjd NEED((!pf), "profile already set"); 1103172302Spjd NEED(p, "profile"); 1104172302Spjd { 1105172302Spjd NEED1("extra delay needs the file name\n"); 1106172302Spjd pf = o_next(&buf, sizeof(*pf), DN_PROFILE); 1107172302Spjd load_extra_delays(av[0], pf, p); //XXX can't fail? 1108172302Spjd --ac; ++av; 1109172302Spjd } 1110172302Spjd break; 1111172302Spjd 1112172302Spjd case TOK_BURST: 1113172302Spjd NEED(p, "burst"); 1114172302Spjd NEED1("burst needs argument\n"); 1115172302Spjd errno = 0; 1116172302Spjd if (expand_number(av[0], (int64_t *)&p->burst) < 0) 1117172302Spjd if (errno != ERANGE) 1118172302Spjd errx(EX_DATAERR, 1119172302Spjd "burst: invalid argument"); 1120172302Spjd if (errno || p->burst > (1ULL << 48) - 1) 1121172302Spjd errx(EX_DATAERR, 1122172302Spjd "burst: out of range (0..2^48-1)"); 1123172302Spjd ac--; av++; 1124172302Spjd break; 1125172302Spjd 1126172302Spjd default: 1127172302Spjd errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]); 1128172302Spjd } 1129172302Spjd } 1130172302Spjd 1131172302Spjd /* check validity of parameters */ 1132172302Spjd if (p) { 1133172302Spjd if (p->delay > 10000) 1134172302Spjd errx(EX_DATAERR, "delay must be < 10000"); 1135172302Spjd if (p->bandwidth == -1) 1136172302Spjd p->bandwidth = 0; 1137172302Spjd } 1138172302Spjd if (fs) { 1139172302Spjd /* XXX accept a 0 scheduler to keep the default */ 1140172302Spjd if (fs->flags & DN_QSIZE_BYTES) { 1141172302Spjd size_t len; 1142172302Spjd long limit; 1143172302Spjd 1144172302Spjd len = sizeof(limit); 1145172302Spjd if (sysctlbyname("net.inet.ip.dummynet.pipe_byte_limit", 1146172302Spjd &limit, &len, NULL, 0) == -1) 1147172302Spjd limit = 1024*1024; 1148172302Spjd if (fs->qsize > limit) 1149172302Spjd errx(EX_DATAERR, "queue size must be < %ldB", limit); 1150172302Spjd } else { 1151172302Spjd size_t len; 1152172302Spjd long limit; 1153172302Spjd 1154172302Spjd len = sizeof(limit); 1155172302Spjd if (sysctlbyname("net.inet.ip.dummynet.pipe_slot_limit", 1156172302Spjd &limit, &len, NULL, 0) == -1) 1157172302Spjd limit = 100; 1158172302Spjd if (fs->qsize > limit) 1159172302Spjd errx(EX_DATAERR, "2 <= queue size <= %ld", limit); 1160172302Spjd } 1161172302Spjd 1162172302Spjd if (fs->flags & DN_IS_RED) { 1163172302Spjd size_t len; 1164172302Spjd int lookup_depth, avg_pkt_size; 1165172302Spjd double w_q; 1166172302Spjd 1167172302Spjd if (fs->min_th >= fs->max_th) 1168172302Spjd errx(EX_DATAERR, "min_th %d must be < than max_th %d", 1169172302Spjd fs->min_th, fs->max_th); 1170172302Spjd if (fs->max_th == 0) 1171172302Spjd errx(EX_DATAERR, "max_th must be > 0"); 1172172302Spjd 1173172302Spjd len = sizeof(int); 1174172302Spjd if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth", 1175172302Spjd &lookup_depth, &len, NULL, 0) == -1) 1176172302Spjd lookup_depth = 256; 1177172302Spjd if (lookup_depth == 0) 1178172302Spjd errx(EX_DATAERR, "net.inet.ip.dummynet.red_lookup_depth" 1179172302Spjd " must be greater than zero"); 1180172302Spjd 1181172302Spjd len = sizeof(int); 1182172302Spjd if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size", 1183172302Spjd &avg_pkt_size, &len, NULL, 0) == -1) 1184172302Spjd avg_pkt_size = 512; 1185172302Spjd 1186172302Spjd if (avg_pkt_size == 0) 1187172302Spjd errx(EX_DATAERR, 1188172302Spjd "net.inet.ip.dummynet.red_avg_pkt_size must" 1189172302Spjd " be greater than zero"); 1190172302Spjd 1191172302Spjd /* 1192172302Spjd * Ticks needed for sending a medium-sized packet. 1193172302Spjd * Unfortunately, when we are configuring a WF2Q+ queue, we 1194172302Spjd * do not have bandwidth information, because that is stored 1195172302Spjd * in the parent pipe, and also we have multiple queues 1196172302Spjd * competing for it. So we set s=0, which is not very 1197172302Spjd * correct. But on the other hand, why do we want RED with 1198172302Spjd * WF2Q+ ? 1199172302Spjd */ 1200172302Spjd#if 0 1201172302Spjd if (p.bandwidth==0) /* this is a WF2Q+ queue */ 1202172302Spjd s = 0; 1203172302Spjd else 1204172302Spjd s = (double)ck.hz * avg_pkt_size * 8 / p.bandwidth; 1205172302Spjd#endif 1206172302Spjd /* 1207172302Spjd * max idle time (in ticks) before avg queue size becomes 0. 1208172302Spjd * NOTA: (3/w_q) is approx the value x so that 1209172302Spjd * (1-w_q)^x < 10^-3. 1210172302Spjd */ 1211172302Spjd w_q = ((double)fs->w_q) / (1 << SCALE_RED); 1212172302Spjd#if 0 // go in kernel 1213172302Spjd idle = s * 3. / w_q; 1214172302Spjd fs->lookup_step = (int)idle / lookup_depth; 1215172302Spjd if (!fs->lookup_step) 1216172302Spjd fs->lookup_step = 1; 1217172302Spjd weight = 1 - w_q; 1218172302Spjd for (t = fs->lookup_step; t > 1; --t) 1219172302Spjd weight *= 1 - w_q; 1220172302Spjd fs->lookup_weight = (int)(weight * (1 << SCALE_RED)); 1221172302Spjd#endif 1222172302Spjd } 1223172302Spjd } 1224172302Spjd 1225172302Spjd i = do_cmd(IP_DUMMYNET3, base, (char *)buf - (char *)base); 1226172302Spjd 1227172302Spjd if (i) 1228172302Spjd err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE"); 1229172302Spjd} 1230172302Spjd 1231172302Spjdvoid 1232172302Spjddummynet_flush(void) 1233172302Spjd{ 1234172302Spjd struct dn_id oid; 1235172302Spjd oid_fill(&oid, sizeof(oid), DN_CMD_FLUSH, DN_API_VERSION); 1236172302Spjd do_cmd(IP_DUMMYNET3, &oid, oid.len); 1237172302Spjd} 1238172302Spjd 1239172302Spjd/* main entry point for dummynet list functions. co.do_pipe indicates 1240172302Spjd * which function we want to support. 1241172302Spjd * XXX todo- accept filtering arguments. 1242172302Spjd */ 1243172302Spjdvoid 1244172302Spjddummynet_list(int ac, char *av[], int show_counters) 1245172302Spjd{ 1246172302Spjd struct dn_id oid, *x; 1247172302Spjd int ret, l = sizeof(oid); 1248172302Spjd 1249172302Spjd oid_fill(&oid, l, DN_CMD_GET, DN_API_VERSION); 1250172302Spjd switch (co.do_pipe) { 1251172302Spjd case 1: 1252172302Spjd oid.subtype = DN_LINK; /* list pipe */ 1253172302Spjd break; 1254172302Spjd case 2: 1255172302Spjd oid.subtype = DN_FS; /* list queue */ 1256172302Spjd break; 1257172302Spjd case 3: 1258172302Spjd oid.subtype = DN_SCH; /* list sched */ 1259172302Spjd break; 1260172302Spjd } 1261172302Spjd ret = do_cmd(-IP_DUMMYNET3, &oid, (uintptr_t)&l); 1262172302Spjd // printf("%s returns %d need %d\n", __FUNCTION__, ret, oid.id); 1263172302Spjd if (ret != 0 || oid.id <= sizeof(oid)) 1264172302Spjd return; 1265172302Spjd l = oid.id; 1266172302Spjd x = safe_calloc(1, l); 1267172302Spjd *x = oid; 1268172302Spjd ret = do_cmd(-IP_DUMMYNET3, x, (uintptr_t)&l); 1269172302Spjd // printf("%s returns %d need %d\n", __FUNCTION__, ret, oid.id); 1270172302Spjd // XXX filter on ac, av 1271172302Spjd list_pipes(x, O_NEXT(x, l)); 1272172302Spjd free(x); 1273172302Spjd} 1274172302Spjd