dummynet.c revision 266941
1187769Sluigi/* 2204591Sluigi * Copyright (c) 2002-2003,2010 Luigi Rizzo 3187769Sluigi * 4187769Sluigi * Redistribution and use in source forms, with and without modification, 5187769Sluigi * are permitted provided that this entire comment appears intact. 6187769Sluigi * 7187769Sluigi * Redistribution in binary form may occur without any restrictions. 8187769Sluigi * Obviously, it would be nice if you gave credit where credit is due 9187769Sluigi * but requiring it would be too onerous. 10187769Sluigi * 11187769Sluigi * This software is provided ``AS IS'' without any warranties of any kind. 12187769Sluigi * 13187769Sluigi * $FreeBSD: head/sbin/ipfw/dummynet.c 266941 2014-06-01 07:28:24Z hiren $ 14187769Sluigi * 15187769Sluigi * dummynet support 16187769Sluigi */ 17187769Sluigi 18187769Sluigi#include <sys/types.h> 19187769Sluigi#include <sys/socket.h> 20187983Sluigi/* XXX there are several sysctl leftover here */ 21187769Sluigi#include <sys/sysctl.h> 22187769Sluigi 23187769Sluigi#include "ipfw2.h" 24187769Sluigi 25187769Sluigi#include <ctype.h> 26187769Sluigi#include <err.h> 27194930Soleg#include <errno.h> 28194930Soleg#include <libutil.h> 29187769Sluigi#include <netdb.h> 30187769Sluigi#include <stdio.h> 31187769Sluigi#include <stdlib.h> 32187769Sluigi#include <string.h> 33187769Sluigi#include <sysexits.h> 34187769Sluigi 35187769Sluigi#include <net/if.h> 36187769Sluigi#include <netinet/in.h> 37187769Sluigi#include <netinet/ip_fw.h> 38187769Sluigi#include <netinet/ip_dummynet.h> 39187983Sluigi#include <arpa/inet.h> /* inet_ntoa */ 40187769Sluigi 41204591Sluigi 42187769Sluigistatic struct _s_x dummynet_params[] = { 43187769Sluigi { "plr", TOK_PLR }, 44187769Sluigi { "noerror", TOK_NOERROR }, 45187769Sluigi { "buckets", TOK_BUCKETS }, 46187769Sluigi { "dst-ip", TOK_DSTIP }, 47187769Sluigi { "src-ip", TOK_SRCIP }, 48187769Sluigi { "dst-port", TOK_DSTPORT }, 49187769Sluigi { "src-port", TOK_SRCPORT }, 50187769Sluigi { "proto", TOK_PROTO }, 51187769Sluigi { "weight", TOK_WEIGHT }, 52204591Sluigi { "lmax", TOK_LMAX }, 53204591Sluigi { "maxlen", TOK_LMAX }, 54187769Sluigi { "all", TOK_ALL }, 55204591Sluigi { "mask", TOK_MASK }, /* alias for both */ 56204591Sluigi { "sched_mask", TOK_SCHED_MASK }, 57204591Sluigi { "flow_mask", TOK_FLOW_MASK }, 58187769Sluigi { "droptail", TOK_DROPTAIL }, 59266941Shiren { "ecn", TOK_ECN }, 60187769Sluigi { "red", TOK_RED }, 61187769Sluigi { "gred", TOK_GRED }, 62187769Sluigi { "bw", TOK_BW }, 63187769Sluigi { "bandwidth", TOK_BW }, 64187769Sluigi { "delay", TOK_DELAY }, 65204591Sluigi { "link", TOK_LINK }, 66187769Sluigi { "pipe", TOK_PIPE }, 67187769Sluigi { "queue", TOK_QUEUE }, 68204591Sluigi { "flowset", TOK_FLOWSET }, 69204591Sluigi { "sched", TOK_SCHED }, 70204591Sluigi { "pri", TOK_PRI }, 71204591Sluigi { "priority", TOK_PRI }, 72204591Sluigi { "type", TOK_TYPE }, 73187769Sluigi { "flow-id", TOK_FLOWID}, 74187769Sluigi { "dst-ipv6", TOK_DSTIP6}, 75187769Sluigi { "dst-ip6", TOK_DSTIP6}, 76187769Sluigi { "src-ipv6", TOK_SRCIP6}, 77187769Sluigi { "src-ip6", TOK_SRCIP6}, 78204591Sluigi { "profile", TOK_PROFILE}, 79194930Soleg { "burst", TOK_BURST}, 80187769Sluigi { "dummynet-params", TOK_NULL }, 81187769Sluigi { NULL, 0 } /* terminator */ 82187769Sluigi}; 83187769Sluigi 84204591Sluigi#define O_NEXT(p, len) ((void *)((char *)p + len)) 85204591Sluigi 86204591Sluigistatic void 87204591Sluigioid_fill(struct dn_id *oid, int len, int type, uintptr_t id) 88204591Sluigi{ 89204591Sluigi oid->len = len; 90204591Sluigi oid->type = type; 91204591Sluigi oid->subtype = 0; 92204591Sluigi oid->id = id; 93204591Sluigi} 94204591Sluigi 95204591Sluigi/* make room in the buffer and move the pointer forward */ 96204591Sluigistatic void * 97204591Sluigio_next(struct dn_id **o, int len, int type) 98204591Sluigi{ 99204591Sluigi struct dn_id *ret = *o; 100204591Sluigi oid_fill(ret, len, type, 0); 101204591Sluigi *o = O_NEXT(*o, len); 102204591Sluigi return ret; 103204591Sluigi} 104204591Sluigi 105204591Sluigi#if 0 106187769Sluigistatic int 107200056Sluigisort_q(void *arg, const void *pa, const void *pb) 108187769Sluigi{ 109187769Sluigi int rev = (co.do_sort < 0); 110187769Sluigi int field = rev ? -co.do_sort : co.do_sort; 111187769Sluigi long long res = 0; 112187769Sluigi const struct dn_flow_queue *a = pa; 113187769Sluigi const struct dn_flow_queue *b = pb; 114187769Sluigi 115187769Sluigi switch (field) { 116187769Sluigi case 1: /* pkts */ 117187769Sluigi res = a->len - b->len; 118187769Sluigi break; 119187769Sluigi case 2: /* bytes */ 120187769Sluigi res = a->len_bytes - b->len_bytes; 121187769Sluigi break; 122187769Sluigi 123187769Sluigi case 3: /* tot pkts */ 124187769Sluigi res = a->tot_pkts - b->tot_pkts; 125187769Sluigi break; 126187769Sluigi 127187769Sluigi case 4: /* tot bytes */ 128187769Sluigi res = a->tot_bytes - b->tot_bytes; 129187769Sluigi break; 130187769Sluigi } 131187769Sluigi if (res < 0) 132187769Sluigi res = -1; 133187769Sluigi if (res > 0) 134187769Sluigi res = 1; 135187769Sluigi return (int)(rev ? res : -res); 136187769Sluigi} 137204591Sluigi#endif 138187769Sluigi 139204591Sluigi/* print a mask and header for the subsequent list of flows */ 140187769Sluigistatic void 141204591Sluigiprint_mask(struct ipfw_flow_id *id) 142187769Sluigi{ 143204591Sluigi if (!IS_IP6_FLOW_ID(id)) { 144204591Sluigi printf(" " 145205173Sluigi "mask: %s 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n", 146205173Sluigi id->extra ? "queue," : "", 147204591Sluigi id->proto, 148204591Sluigi id->src_ip, id->src_port, 149204591Sluigi id->dst_ip, id->dst_port); 150204591Sluigi } else { 151204591Sluigi char buf[255]; 152205173Sluigi printf("\n mask: %sproto: 0x%02x, flow_id: 0x%08x, ", 153205173Sluigi id->extra ? "queue," : "", 154204591Sluigi id->proto, id->flow_id6); 155204591Sluigi inet_ntop(AF_INET6, &(id->src_ip6), buf, sizeof(buf)); 156204591Sluigi printf("%s/0x%04x -> ", buf, id->src_port); 157204591Sluigi inet_ntop(AF_INET6, &(id->dst_ip6), buf, sizeof(buf)); 158204591Sluigi printf("%s/0x%04x\n", buf, id->dst_port); 159210118Sluigi } 160210118Sluigi} 161187769Sluigi 162210118Sluigistatic void 163210118Sluigiprint_header(struct ipfw_flow_id *id) 164210118Sluigi{ 165210118Sluigi if (!IS_IP6_FLOW_ID(id)) 166210118Sluigi printf("BKT Prot ___Source IP/port____ " 167210118Sluigi "____Dest. IP/port____ " 168210118Sluigi "Tot_pkt/bytes Pkt/Byte Drp\n"); 169210118Sluigi else 170204591Sluigi printf("BKT ___Prot___ _flow-id_ " 171204591Sluigi "______________Source IPv6/port_______________ " 172204591Sluigi "_______________Dest. IPv6/port_______________ " 173204591Sluigi "Tot_pkt/bytes Pkt/Byte Drp\n"); 174204591Sluigi} 175187769Sluigi 176204591Sluigistatic void 177210118Sluigilist_flow(struct dn_flow *ni, int *print) 178204591Sluigi{ 179204591Sluigi char buff[255]; 180210118Sluigi struct protoent *pe = NULL; 181204591Sluigi struct in_addr ina; 182204591Sluigi struct ipfw_flow_id *id = &ni->fid; 183187769Sluigi 184210118Sluigi if (*print) { 185210118Sluigi print_header(&ni->fid); 186210118Sluigi *print = 0; 187210118Sluigi } 188204591Sluigi pe = getprotobynumber(id->proto); 189187769Sluigi /* XXX: Should check for IPv4 flows */ 190205173Sluigi printf("%3u%c", (ni->oid.id) & 0xff, 191205173Sluigi id->extra ? '*' : ' '); 192204591Sluigi if (!IS_IP6_FLOW_ID(id)) { 193187769Sluigi if (pe) 194187769Sluigi printf("%-4s ", pe->p_name); 195187769Sluigi else 196204591Sluigi printf("%4u ", id->proto); 197204591Sluigi ina.s_addr = htonl(id->src_ip); 198187769Sluigi printf("%15s/%-5d ", 199204591Sluigi inet_ntoa(ina), id->src_port); 200204591Sluigi ina.s_addr = htonl(id->dst_ip); 201187769Sluigi printf("%15s/%-5d ", 202204591Sluigi inet_ntoa(ina), id->dst_port); 203204591Sluigi } else { 204204591Sluigi /* Print IPv6 flows */ 205187769Sluigi if (pe != NULL) 206187769Sluigi printf("%9s ", pe->p_name); 207187769Sluigi else 208204591Sluigi printf("%9u ", id->proto); 209204591Sluigi printf("%7d %39s/%-5d ", id->flow_id6, 210204591Sluigi inet_ntop(AF_INET6, &(id->src_ip6), buff, sizeof(buff)), 211204591Sluigi id->src_port); 212187769Sluigi printf(" %39s/%-5d ", 213204591Sluigi inet_ntop(AF_INET6, &(id->dst_ip6), buff, sizeof(buff)), 214204591Sluigi id->dst_port); 215187769Sluigi } 216206843Sluigi pr_u64(&ni->tot_pkts, 4); 217206843Sluigi pr_u64(&ni->tot_bytes, 8); 218206843Sluigi printf("%2u %4u %3u\n", 219204591Sluigi ni->length, ni->len_bytes, ni->drops); 220187769Sluigi} 221187769Sluigi 222187769Sluigistatic void 223204591Sluigiprint_flowset_parms(struct dn_fs *fs, char *prefix) 224187769Sluigi{ 225187769Sluigi int l; 226187769Sluigi char qs[30]; 227187769Sluigi char plr[30]; 228187769Sluigi char red[90]; /* Display RED parameters */ 229187769Sluigi 230187769Sluigi l = fs->qsize; 231204591Sluigi if (fs->flags & DN_QSIZE_BYTES) { 232187769Sluigi if (l >= 8192) 233187769Sluigi sprintf(qs, "%d KB", l / 1024); 234187769Sluigi else 235187769Sluigi sprintf(qs, "%d B", l); 236187769Sluigi } else 237187769Sluigi sprintf(qs, "%3d sl.", l); 238187769Sluigi if (fs->plr) 239187769Sluigi sprintf(plr, "plr %f", 1.0 * fs->plr / (double)(0x7fffffff)); 240187769Sluigi else 241187769Sluigi plr[0] = '\0'; 242204591Sluigi 243266941Shiren if (fs->flags & DN_IS_RED) { /* RED parameters */ 244187769Sluigi sprintf(red, 245194930Soleg "\n\t %cRED w_q %f min_th %d max_th %d max_p %f", 246204591Sluigi (fs->flags & DN_IS_GENTLE_RED) ? 'G' : ' ', 247187769Sluigi 1.0 * fs->w_q / (double)(1 << SCALE_RED), 248204591Sluigi fs->min_th, 249204591Sluigi fs->max_th, 250187769Sluigi 1.0 * fs->max_p / (double)(1 << SCALE_RED)); 251266941Shiren if (fs->flags & DN_IS_ECN) 252266941Shiren strncat(red, " (ecn)", 6); 253266941Shiren } else 254187769Sluigi sprintf(red, "droptail"); 255187769Sluigi 256204591Sluigi if (prefix[0]) { 257204591Sluigi printf("%s %s%s %d queues (%d buckets) %s\n", 258204591Sluigi prefix, qs, plr, fs->oid.id, fs->buckets, red); 259204591Sluigi prefix[0] = '\0'; 260204591Sluigi } else { 261204591Sluigi printf("q%05d %s%s %d flows (%d buckets) sched %d " 262204591Sluigi "weight %d lmax %d pri %d %s\n", 263204591Sluigi fs->fs_nr, qs, plr, fs->oid.id, fs->buckets, 264204591Sluigi fs->sched_nr, fs->par[0], fs->par[1], fs->par[2], red); 265204591Sluigi if (fs->flags & DN_HAVE_MASK) 266204591Sluigi print_mask(&fs->flow_mask); 267204591Sluigi } 268187769Sluigi} 269187769Sluigi 270190865Sluigistatic void 271204591Sluigiprint_extra_delay_parms(struct dn_profile *p) 272190865Sluigi{ 273190865Sluigi double loss; 274190865Sluigi if (p->samples_no <= 0) 275190865Sluigi return; 276190865Sluigi 277190865Sluigi loss = p->loss_level; 278190865Sluigi loss /= p->samples_no; 279194930Soleg printf("\t profile: name \"%s\" loss %f samples %d\n", 280194930Soleg p->name, loss, p->samples_no); 281190865Sluigi} 282190865Sluigi 283204591Sluigistatic void 284204591Sluigiflush_buf(char *buf) 285187769Sluigi{ 286204591Sluigi if (buf[0]) 287204591Sluigi printf("%s\n", buf); 288204591Sluigi buf[0] = '\0'; 289204591Sluigi} 290220804Sglebius 291204591Sluigi/* 292204591Sluigi * generic list routine. We expect objects in a specific order, i.e. 293204591Sluigi * PIPES AND SCHEDULERS: 294204591Sluigi * link; scheduler; internal flowset if any; instances 295204591Sluigi * we can tell a pipe from the number. 296204591Sluigi * 297204591Sluigi * FLOWSETS: 298204591Sluigi * flowset; queues; 299204591Sluigi * link i (int queue); scheduler i; si(i) { flowsets() : queues } 300204591Sluigi */ 301204591Sluigistatic void 302204591Sluigilist_pipes(struct dn_id *oid, struct dn_id *end) 303204591Sluigi{ 304204591Sluigi char buf[160]; /* pending buffer */ 305210118Sluigi int toPrint = 1; /* print header */ 306206843Sluigi 307204591Sluigi buf[0] = '\0'; 308204591Sluigi for (; oid != end; oid = O_NEXT(oid, oid->len)) { 309204591Sluigi if (oid->len < sizeof(*oid)) 310204591Sluigi errx(1, "invalid oid len %d\n", oid->len); 311187769Sluigi 312204591Sluigi switch (oid->type) { 313204591Sluigi default: 314204591Sluigi flush_buf(buf); 315204591Sluigi printf("unrecognized object %d size %d\n", oid->type, oid->len); 316204591Sluigi break; 317204591Sluigi case DN_TEXT: /* list of attached flowsets */ 318204591Sluigi { 319204591Sluigi int i, l; 320204591Sluigi struct { 321204591Sluigi struct dn_id id; 322204591Sluigi uint32_t p[0]; 323204591Sluigi } *d = (void *)oid; 324204591Sluigi l = (oid->len - sizeof(*oid))/sizeof(d->p[0]); 325204591Sluigi if (l == 0) 326204591Sluigi break; 327204591Sluigi printf(" Children flowsets: "); 328204591Sluigi for (i = 0; i < l; i++) 329204591Sluigi printf("%u ", d->p[i]); 330204591Sluigi printf("\n"); 331204591Sluigi break; 332204591Sluigi } 333204591Sluigi case DN_CMD_GET: 334204591Sluigi if (co.verbose) 335204591Sluigi printf("answer for cmd %d, len %d\n", oid->type, oid->id); 336204591Sluigi break; 337204591Sluigi case DN_SCH: { 338204591Sluigi struct dn_sch *s = (struct dn_sch *)oid; 339204591Sluigi flush_buf(buf); 340204591Sluigi printf(" sched %d type %s flags 0x%x %d buckets %d active\n", 341204591Sluigi s->sched_nr, 342204591Sluigi s->name, s->flags, s->buckets, s->oid.id); 343204591Sluigi if (s->flags & DN_HAVE_MASK) 344204591Sluigi print_mask(&s->sched_mask); 345204591Sluigi } 346204591Sluigi break; 347187769Sluigi 348204591Sluigi case DN_FLOW: 349210118Sluigi list_flow((struct dn_flow *)oid, &toPrint); 350204591Sluigi break; 351187769Sluigi 352204591Sluigi case DN_LINK: { 353204591Sluigi struct dn_link *p = (struct dn_link *)oid; 354204591Sluigi double b = p->bandwidth; 355204591Sluigi char bwbuf[30]; 356204591Sluigi char burst[5 + 7]; 357187769Sluigi 358204591Sluigi /* This starts a new object so flush buffer */ 359204591Sluigi flush_buf(buf); 360204591Sluigi /* data rate */ 361204591Sluigi if (b == 0) 362204591Sluigi sprintf(bwbuf, "unlimited "); 363204591Sluigi else if (b >= 1000000) 364204591Sluigi sprintf(bwbuf, "%7.3f Mbit/s", b/1000000); 365204591Sluigi else if (b >= 1000) 366204591Sluigi sprintf(bwbuf, "%7.3f Kbit/s", b/1000); 367204591Sluigi else 368204591Sluigi sprintf(bwbuf, "%7.3f bit/s ", b); 369187769Sluigi 370204591Sluigi if (humanize_number(burst, sizeof(burst), p->burst, 371204591Sluigi "", HN_AUTOSCALE, 0) < 0 || co.verbose) 372204591Sluigi sprintf(burst, "%d", (int)p->burst); 373204591Sluigi sprintf(buf, "%05d: %s %4d ms burst %s", 374204591Sluigi p->link_nr % DN_MAX_ID, bwbuf, p->delay, burst); 375204591Sluigi } 376204591Sluigi break; 377190865Sluigi 378204591Sluigi case DN_FS: 379204591Sluigi print_flowset_parms((struct dn_fs *)oid, buf); 380204591Sluigi break; 381204591Sluigi case DN_PROFILE: 382204591Sluigi flush_buf(buf); 383204591Sluigi print_extra_delay_parms((struct dn_profile *)oid); 384187769Sluigi } 385204591Sluigi flush_buf(buf); // XXX does it really go here ? 386204591Sluigi } 387187769Sluigi} 388187769Sluigi 389187769Sluigi/* 390204591Sluigi * Delete pipe, queue or scheduler i 391187769Sluigi */ 392187769Sluigiint 393204591Sluigiipfw_delete_pipe(int do_pipe, int i) 394187769Sluigi{ 395204591Sluigi struct { 396204591Sluigi struct dn_id oid; 397204591Sluigi uintptr_t a[1]; /* add more if we want a list */ 398204591Sluigi } cmd; 399204591Sluigi oid_fill((void *)&cmd, sizeof(cmd), DN_CMD_DELETE, DN_API_VERSION); 400204591Sluigi cmd.oid.subtype = (do_pipe == 1) ? DN_LINK : 401204591Sluigi ( (do_pipe == 2) ? DN_FS : DN_SCH); 402204591Sluigi cmd.a[0] = i; 403204591Sluigi i = do_cmd(IP_DUMMYNET3, &cmd, cmd.oid.len); 404187769Sluigi if (i) { 405187769Sluigi i = 1; 406187769Sluigi warn("rule %u: setsockopt(IP_DUMMYNET_DEL)", i); 407187769Sluigi } 408187769Sluigi return i; 409187769Sluigi} 410187769Sluigi 411190865Sluigi/* 412190865Sluigi * Code to parse delay profiles. 413190865Sluigi * 414190865Sluigi * Some link types introduce extra delays in the transmission 415190865Sluigi * of a packet, e.g. because of MAC level framing, contention on 416190865Sluigi * the use of the channel, MAC level retransmissions and so on. 417190865Sluigi * From our point of view, the channel is effectively unavailable 418190865Sluigi * for this extra time, which is constant or variable depending 419190865Sluigi * on the link type. Additionally, packets may be dropped after this 420190865Sluigi * time (e.g. on a wireless link after too many retransmissions). 421190865Sluigi * We can model the additional delay with an empirical curve 422190865Sluigi * that represents its distribution. 423190865Sluigi * 424220802Sglebius * cumulative probability 425220802Sglebius * 1.0 ^ 426220802Sglebius * | 427220802Sglebius * L +-- loss-level x 428220802Sglebius * | ****** 429220802Sglebius * | * 430220802Sglebius * | ***** 431220802Sglebius * | * 432220802Sglebius * | ** 433220802Sglebius * | * 434220802Sglebius * +-------*-------------------> 435220802Sglebius * delay 436190865Sluigi * 437190865Sluigi * The empirical curve may have both vertical and horizontal lines. 438190865Sluigi * Vertical lines represent constant delay for a range of 439190865Sluigi * probabilities; horizontal lines correspond to a discontinuty 440204591Sluigi * in the delay distribution: the link will use the largest delay 441190865Sluigi * for a given probability. 442220802Sglebius * 443190865Sluigi * To pass the curve to dummynet, we must store the parameters 444190865Sluigi * in a file as described below, and issue the command 445190865Sluigi * 446190865Sluigi * ipfw pipe <n> config ... bw XXX profile <filename> ... 447190865Sluigi * 448190865Sluigi * The file format is the following, with whitespace acting as 449190865Sluigi * a separator and '#' indicating the beginning a comment: 450190865Sluigi * 451190865Sluigi * samples N 452190865Sluigi * the number of samples used in the internal 453190865Sluigi * representation (2..1024; default 100); 454190865Sluigi * 455220802Sglebius * loss-level L 456190865Sluigi * The probability above which packets are lost. 457220802Sglebius * (0.0 <= L <= 1.0, default 1.0 i.e. no loss); 458190865Sluigi * 459190865Sluigi * name identifier 460190865Sluigi * Optional a name (listed by "ipfw pipe show") 461190865Sluigi * to identify the distribution; 462190865Sluigi * 463190865Sluigi * "delay prob" | "prob delay" 464190865Sluigi * One of these two lines is mandatory and defines 465190865Sluigi * the format of the following lines with data points. 466190865Sluigi * 467190865Sluigi * XXX YYY 468190865Sluigi * 2 or more lines representing points in the curve, 469190865Sluigi * with either delay or probability first, according 470190865Sluigi * to the chosen format. 471190865Sluigi * The unit for delay is milliseconds. 472190865Sluigi * 473190865Sluigi * Data points does not need to be ordered or equal to the number 474190865Sluigi * specified in the "samples" line. ipfw will sort and interpolate 475190865Sluigi * the curve as needed. 476190865Sluigi * 477190865Sluigi * Example of a profile file: 478220802Sglebius 479220802Sglebius name bla_bla_bla 480220802Sglebius samples 100 481220802Sglebius loss-level 0.86 482220802Sglebius prob delay 483220802Sglebius 0 200 # minimum overhead is 200ms 484220802Sglebius 0.5 200 485220802Sglebius 0.5 300 486220802Sglebius 0.8 1000 487220802Sglebius 0.9 1300 488220802Sglebius 1 1300 489220802Sglebius 490190865Sluigi * Internally, we will convert the curve to a fixed number of 491190865Sluigi * samples, and when it is time to transmit a packet we will 492190865Sluigi * model the extra delay as extra bits in the packet. 493190865Sluigi * 494190865Sluigi */ 495190865Sluigi 496190865Sluigi#define ED_MAX_LINE_LEN 256+ED_MAX_NAME_LEN 497190865Sluigi#define ED_TOK_SAMPLES "samples" 498190865Sluigi#define ED_TOK_LOSS "loss-level" 499190865Sluigi#define ED_TOK_NAME "name" 500190865Sluigi#define ED_TOK_DELAY "delay" 501190865Sluigi#define ED_TOK_PROB "prob" 502193715Sluigi#define ED_TOK_BW "bw" 503190865Sluigi#define ED_SEPARATORS " \t\n" 504190865Sluigi#define ED_MIN_SAMPLES_NO 2 505190865Sluigi 506190865Sluigi/* 507190865Sluigi * returns 1 if s is a non-negative number, with at least one '.' 508190865Sluigi */ 509190865Sluigistatic int 510190865Sluigiis_valid_number(const char *s) 511190865Sluigi{ 512190865Sluigi int i, dots_found = 0; 513190865Sluigi int len = strlen(s); 514190865Sluigi 515190865Sluigi for (i = 0; i<len; ++i) 516190865Sluigi if (!isdigit(s[i]) && (s[i] !='.' || ++dots_found > 1)) 517190865Sluigi return 0; 518190865Sluigi return 1; 519190865Sluigi} 520190865Sluigi 521193715Sluigi/* 522193715Sluigi * Take as input a string describing a bandwidth value 523193715Sluigi * and return the numeric bandwidth value. 524193715Sluigi * set clocking interface or bandwidth value 525193715Sluigi */ 526200056Sluigistatic void 527193715Sluigiread_bandwidth(char *arg, int *bandwidth, char *if_name, int namelen) 528193715Sluigi{ 529193715Sluigi if (*bandwidth != -1) 530204591Sluigi warnx("duplicate token, override bandwidth value!"); 531193715Sluigi 532193715Sluigi if (arg[0] >= 'a' && arg[0] <= 'z') { 533204591Sluigi if (!if_name) { 534204591Sluigi errx(1, "no if support"); 535204591Sluigi } 536193715Sluigi if (namelen >= IFNAMSIZ) 537193715Sluigi warn("interface name truncated"); 538193715Sluigi namelen--; 539193715Sluigi /* interface name */ 540193715Sluigi strncpy(if_name, arg, namelen); 541193715Sluigi if_name[namelen] = '\0'; 542193715Sluigi *bandwidth = 0; 543193715Sluigi } else { /* read bandwidth value */ 544193715Sluigi int bw; 545193715Sluigi char *end = NULL; 546193715Sluigi 547193715Sluigi bw = strtoul(arg, &end, 0); 548193715Sluigi if (*end == 'K' || *end == 'k') { 549193715Sluigi end++; 550193715Sluigi bw *= 1000; 551205181Sluigi } else if (*end == 'M' || *end == 'm') { 552193715Sluigi end++; 553193715Sluigi bw *= 1000000; 554193715Sluigi } 555193715Sluigi if ((*end == 'B' && 556193715Sluigi _substrcmp2(end, "Bi", "Bit/s") != 0) || 557193715Sluigi _substrcmp2(end, "by", "bytes") == 0) 558193715Sluigi bw *= 8; 559193715Sluigi 560193715Sluigi if (bw < 0) 561193715Sluigi errx(EX_DATAERR, "bandwidth too large"); 562193715Sluigi 563193715Sluigi *bandwidth = bw; 564204591Sluigi if (if_name) 565204591Sluigi if_name[0] = '\0'; 566193715Sluigi } 567193715Sluigi} 568193715Sluigi 569190865Sluigistruct point { 570190865Sluigi double prob; 571190865Sluigi double delay; 572190865Sluigi}; 573190865Sluigi 574200056Sluigistatic int 575190865Sluigicompare_points(const void *vp1, const void *vp2) 576190865Sluigi{ 577190865Sluigi const struct point *p1 = vp1; 578190865Sluigi const struct point *p2 = vp2; 579190865Sluigi double res = 0; 580190865Sluigi 581190865Sluigi res = p1->prob - p2->prob; 582190865Sluigi if (res == 0) 583190865Sluigi res = p1->delay - p2->delay; 584190865Sluigi if (res < 0) 585190865Sluigi return -1; 586190865Sluigi else if (res > 0) 587190865Sluigi return 1; 588190865Sluigi else 589190865Sluigi return 0; 590190865Sluigi} 591190865Sluigi 592190865Sluigi#define ED_EFMT(s) EX_DATAERR,"error in %s at line %d: "#s,filename,lineno 593190865Sluigi 594190865Sluigistatic void 595204591Sluigiload_extra_delays(const char *filename, struct dn_profile *p, 596204591Sluigi struct dn_link *link) 597190865Sluigi{ 598190865Sluigi char line[ED_MAX_LINE_LEN]; 599190865Sluigi FILE *f; 600190865Sluigi int lineno = 0; 601190865Sluigi int i; 602190865Sluigi 603190865Sluigi int samples = -1; 604190865Sluigi double loss = -1.0; 605190865Sluigi char profile_name[ED_MAX_NAME_LEN]; 606190865Sluigi int delay_first = -1; 607190865Sluigi int do_points = 0; 608190865Sluigi struct point points[ED_MAX_SAMPLES_NO]; 609190865Sluigi int points_no = 0; 610190865Sluigi 611204591Sluigi /* XXX link never NULL? */ 612204591Sluigi p->link_nr = link->link_nr; 613204591Sluigi 614190865Sluigi profile_name[0] = '\0'; 615190865Sluigi f = fopen(filename, "r"); 616190865Sluigi if (f == NULL) 617190865Sluigi err(EX_UNAVAILABLE, "fopen: %s", filename); 618190865Sluigi 619220802Sglebius while (fgets(line, ED_MAX_LINE_LEN, f)) { /* read commands */ 620190865Sluigi char *s, *cur = line, *name = NULL, *arg = NULL; 621190865Sluigi 622190865Sluigi ++lineno; 623190865Sluigi 624190865Sluigi /* parse the line */ 625190865Sluigi while (cur) { 626190865Sluigi s = strsep(&cur, ED_SEPARATORS); 627190865Sluigi if (s == NULL || *s == '#') 628190865Sluigi break; 629190865Sluigi if (*s == '\0') 630190865Sluigi continue; 631190865Sluigi if (arg) 632190865Sluigi errx(ED_EFMT("too many arguments")); 633190865Sluigi if (name == NULL) 634190865Sluigi name = s; 635190865Sluigi else 636190865Sluigi arg = s; 637190865Sluigi } 638190865Sluigi if (name == NULL) /* empty line */ 639190865Sluigi continue; 640190865Sluigi if (arg == NULL) 641190865Sluigi errx(ED_EFMT("missing arg for %s"), name); 642190865Sluigi 643190865Sluigi if (!strcasecmp(name, ED_TOK_SAMPLES)) { 644190865Sluigi if (samples > 0) 645190865Sluigi errx(ED_EFMT("duplicate ``samples'' line")); 646190865Sluigi if (atoi(arg) <=0) 647190865Sluigi errx(ED_EFMT("invalid number of samples")); 648190865Sluigi samples = atoi(arg); 649190865Sluigi if (samples>ED_MAX_SAMPLES_NO) 650190865Sluigi errx(ED_EFMT("too many samples, maximum is %d"), 651190865Sluigi ED_MAX_SAMPLES_NO); 652190865Sluigi do_points = 0; 653193715Sluigi } else if (!strcasecmp(name, ED_TOK_BW)) { 654204591Sluigi char buf[IFNAMSIZ]; 655204591Sluigi read_bandwidth(arg, &link->bandwidth, buf, sizeof(buf)); 656190865Sluigi } else if (!strcasecmp(name, ED_TOK_LOSS)) { 657190865Sluigi if (loss != -1.0) 658190865Sluigi errx(ED_EFMT("duplicated token: %s"), name); 659190865Sluigi if (!is_valid_number(arg)) 660190865Sluigi errx(ED_EFMT("invalid %s"), arg); 661190865Sluigi loss = atof(arg); 662190865Sluigi if (loss > 1) 663190865Sluigi errx(ED_EFMT("%s greater than 1.0"), name); 664190865Sluigi do_points = 0; 665190865Sluigi } else if (!strcasecmp(name, ED_TOK_NAME)) { 666190865Sluigi if (profile_name[0] != '\0') 667190865Sluigi errx(ED_EFMT("duplicated token: %s"), name); 668190865Sluigi strncpy(profile_name, arg, sizeof(profile_name) - 1); 669190865Sluigi profile_name[sizeof(profile_name)-1] = '\0'; 670190865Sluigi do_points = 0; 671190865Sluigi } else if (!strcasecmp(name, ED_TOK_DELAY)) { 672190865Sluigi if (do_points) 673190865Sluigi errx(ED_EFMT("duplicated token: %s"), name); 674190865Sluigi delay_first = 1; 675190865Sluigi do_points = 1; 676190865Sluigi } else if (!strcasecmp(name, ED_TOK_PROB)) { 677190865Sluigi if (do_points) 678190865Sluigi errx(ED_EFMT("duplicated token: %s"), name); 679190865Sluigi delay_first = 0; 680190865Sluigi do_points = 1; 681190865Sluigi } else if (do_points) { 682190865Sluigi if (!is_valid_number(name) || !is_valid_number(arg)) 683190865Sluigi errx(ED_EFMT("invalid point found")); 684190865Sluigi if (delay_first) { 685190865Sluigi points[points_no].delay = atof(name); 686190865Sluigi points[points_no].prob = atof(arg); 687190865Sluigi } else { 688190865Sluigi points[points_no].delay = atof(arg); 689190865Sluigi points[points_no].prob = atof(name); 690190865Sluigi } 691190865Sluigi if (points[points_no].prob > 1.0) 692190865Sluigi errx(ED_EFMT("probability greater than 1.0")); 693190865Sluigi ++points_no; 694190865Sluigi } else { 695190865Sluigi errx(ED_EFMT("unrecognised command '%s'"), name); 696190865Sluigi } 697190865Sluigi } 698190865Sluigi 699199626Snetchild fclose (f); 700199626Snetchild 701190865Sluigi if (samples == -1) { 702190865Sluigi warnx("'%s' not found, assuming 100", ED_TOK_SAMPLES); 703190865Sluigi samples = 100; 704190865Sluigi } 705190865Sluigi 706190865Sluigi if (loss == -1.0) { 707190865Sluigi warnx("'%s' not found, assuming no loss", ED_TOK_LOSS); 708190865Sluigi loss = 1; 709190865Sluigi } 710190865Sluigi 711190865Sluigi /* make sure that there are enough points. */ 712190865Sluigi if (points_no < ED_MIN_SAMPLES_NO) 713190865Sluigi errx(ED_EFMT("too few samples, need at least %d"), 714190865Sluigi ED_MIN_SAMPLES_NO); 715190865Sluigi 716190865Sluigi qsort(points, points_no, sizeof(struct point), compare_points); 717190865Sluigi 718190865Sluigi /* interpolation */ 719190865Sluigi for (i = 0; i<points_no-1; ++i) { 720190865Sluigi double y1 = points[i].prob * samples; 721190865Sluigi double x1 = points[i].delay; 722190865Sluigi double y2 = points[i+1].prob * samples; 723190865Sluigi double x2 = points[i+1].delay; 724190865Sluigi 725204591Sluigi int ix = y1; 726190865Sluigi int stop = y2; 727190865Sluigi 728190865Sluigi if (x1 == x2) { 729204591Sluigi for (; ix<stop; ++ix) 730204591Sluigi p->samples[ix] = x1; 731190865Sluigi } else { 732190865Sluigi double m = (y2-y1)/(x2-x1); 733190865Sluigi double c = y1 - m*x1; 734204591Sluigi for (; ix<stop ; ++ix) 735204591Sluigi p->samples[ix] = (ix - c)/m; 736190865Sluigi } 737190865Sluigi } 738190865Sluigi p->samples_no = samples; 739190865Sluigi p->loss_level = loss * samples; 740190865Sluigi strncpy(p->name, profile_name, sizeof(p->name)); 741190865Sluigi} 742190865Sluigi 743204591Sluigi/* 744204591Sluigi * configuration of pipes, schedulers, flowsets. 745204591Sluigi * When we configure a new scheduler, an empty pipe is created, so: 746220802Sglebius * 747204591Sluigi * do_pipe = 1 -> "pipe N config ..." only for backward compatibility 748204591Sluigi * sched N+Delta type fifo sched_mask ... 749204591Sluigi * pipe N+Delta <parameters> 750204591Sluigi * flowset N+Delta pipe N+Delta (no parameters) 751204591Sluigi * sched N type wf2q+ sched_mask ... 752204591Sluigi * pipe N <parameters> 753204591Sluigi * 754204591Sluigi * do_pipe = 2 -> flowset N config 755204591Sluigi * flowset N parameters 756204591Sluigi * 757204591Sluigi * do_pipe = 3 -> sched N config 758204591Sluigi * sched N parameters (default no pipe) 759204591Sluigi * optional Pipe N config ... 760204591Sluigi * pipe ==> 761204591Sluigi */ 762187769Sluigivoid 763187769Sluigiipfw_config_pipe(int ac, char **av) 764187769Sluigi{ 765238900Sluigi int i; 766238900Sluigi u_int j; 767187769Sluigi char *end; 768204591Sluigi struct dn_id *buf, *base; 769204591Sluigi struct dn_sch *sch = NULL; 770204591Sluigi struct dn_link *p = NULL; 771204591Sluigi struct dn_fs *fs = NULL; 772204591Sluigi struct dn_profile *pf = NULL; 773204591Sluigi struct ipfw_flow_id *mask = NULL; 774204591Sluigi int lmax; 775204591Sluigi uint32_t _foo = 0, *flags = &_foo , *buckets = &_foo; 776187769Sluigi 777204591Sluigi /* 778204591Sluigi * allocate space for 1 header, 779204591Sluigi * 1 scheduler, 1 link, 1 flowset, 1 profile 780204591Sluigi */ 781204591Sluigi lmax = sizeof(struct dn_id); /* command header */ 782204591Sluigi lmax += sizeof(struct dn_sch) + sizeof(struct dn_link) + 783204591Sluigi sizeof(struct dn_fs) + sizeof(struct dn_profile); 784187769Sluigi 785187769Sluigi av++; ac--; 786187769Sluigi /* Pipe number */ 787187769Sluigi if (ac && isdigit(**av)) { 788187769Sluigi i = atoi(*av); av++; ac--; 789204591Sluigi } else 790204591Sluigi i = -1; 791204591Sluigi if (i <= 0) 792204591Sluigi errx(EX_USAGE, "need a pipe/flowset/sched number"); 793204591Sluigi base = buf = safe_calloc(1, lmax); 794204591Sluigi /* all commands start with a 'CONFIGURE' and a version */ 795204591Sluigi o_next(&buf, sizeof(struct dn_id), DN_CMD_CONFIG); 796204591Sluigi base->id = DN_API_VERSION; 797204591Sluigi 798204591Sluigi switch (co.do_pipe) { 799204591Sluigi case 1: /* "pipe N config ..." */ 800204591Sluigi /* Allocate space for the WF2Q+ scheduler, its link 801204591Sluigi * and the FIFO flowset. Set the number, but leave 802204591Sluigi * the scheduler subtype and other parameters to 0 803204591Sluigi * so the kernel will use appropriate defaults. 804204591Sluigi * XXX todo: add a flag to record if a parameter 805204591Sluigi * is actually configured. 806204591Sluigi * If we do a 'pipe config' mask -> sched_mask. 807204591Sluigi * The FIFO scheduler and link are derived from the 808204591Sluigi * WF2Q+ one in the kernel. 809204591Sluigi */ 810204591Sluigi sch = o_next(&buf, sizeof(*sch), DN_SCH); 811204591Sluigi p = o_next(&buf, sizeof(*p), DN_LINK); 812204591Sluigi fs = o_next(&buf, sizeof(*fs), DN_FS); 813204591Sluigi 814204591Sluigi sch->sched_nr = i; 815204591Sluigi sch->oid.subtype = 0; /* defaults to WF2Q+ */ 816204591Sluigi mask = &sch->sched_mask; 817204591Sluigi flags = &sch->flags; 818204591Sluigi buckets = &sch->buckets; 819204591Sluigi *flags |= DN_PIPE_CMD; 820204591Sluigi 821204591Sluigi p->link_nr = i; 822204591Sluigi 823204591Sluigi /* This flowset is only for the FIFO scheduler */ 824204591Sluigi fs->fs_nr = i + 2*DN_MAX_ID; 825204591Sluigi fs->sched_nr = i + DN_MAX_ID; 826204591Sluigi break; 827204591Sluigi 828204591Sluigi case 2: /* "queue N config ... " */ 829204591Sluigi fs = o_next(&buf, sizeof(*fs), DN_FS); 830204591Sluigi fs->fs_nr = i; 831204591Sluigi mask = &fs->flow_mask; 832204591Sluigi flags = &fs->flags; 833204591Sluigi buckets = &fs->buckets; 834204591Sluigi break; 835204591Sluigi 836204591Sluigi case 3: /* "sched N config ..." */ 837204591Sluigi sch = o_next(&buf, sizeof(*sch), DN_SCH); 838204591Sluigi fs = o_next(&buf, sizeof(*fs), DN_FS); 839204591Sluigi sch->sched_nr = i; 840204591Sluigi mask = &sch->sched_mask; 841204591Sluigi flags = &sch->flags; 842204591Sluigi buckets = &sch->buckets; 843204591Sluigi /* fs is used only with !MULTIQUEUE schedulers */ 844204591Sluigi fs->fs_nr = i + DN_MAX_ID; 845204591Sluigi fs->sched_nr = i; 846204591Sluigi break; 847187769Sluigi } 848204591Sluigi /* set to -1 those fields for which we want to reuse existing 849204591Sluigi * values from the kernel. 850204591Sluigi * Also, *_nr and subtype = 0 mean reuse the value from the kernel. 851204591Sluigi * XXX todo: support reuse of the mask. 852204591Sluigi */ 853204591Sluigi if (p) 854204591Sluigi p->bandwidth = -1; 855204591Sluigi for (j = 0; j < sizeof(fs->par)/sizeof(fs->par[0]); j++) 856204591Sluigi fs->par[j] = -1; 857187769Sluigi while (ac > 0) { 858187769Sluigi double d; 859187769Sluigi int tok = match_token(dummynet_params, *av); 860187769Sluigi ac--; av++; 861187769Sluigi 862187769Sluigi switch(tok) { 863187769Sluigi case TOK_NOERROR: 864204591Sluigi NEED(fs, "noerror is only for pipes"); 865204591Sluigi fs->flags |= DN_NOERROR; 866187769Sluigi break; 867187769Sluigi 868187769Sluigi case TOK_PLR: 869204591Sluigi NEED(fs, "plr is only for pipes"); 870187769Sluigi NEED1("plr needs argument 0..1\n"); 871187769Sluigi d = strtod(av[0], NULL); 872187769Sluigi if (d > 1) 873187769Sluigi d = 1; 874187769Sluigi else if (d < 0) 875187769Sluigi d = 0; 876204591Sluigi fs->plr = (int)(d*0x7fffffff); 877187769Sluigi ac--; av++; 878187769Sluigi break; 879187769Sluigi 880187769Sluigi case TOK_QUEUE: 881204591Sluigi NEED(fs, "queue is only for pipes or flowsets"); 882187769Sluigi NEED1("queue needs queue size\n"); 883187769Sluigi end = NULL; 884204591Sluigi fs->qsize = strtoul(av[0], &end, 0); 885187769Sluigi if (*end == 'K' || *end == 'k') { 886204591Sluigi fs->flags |= DN_QSIZE_BYTES; 887204591Sluigi fs->qsize *= 1024; 888187769Sluigi } else if (*end == 'B' || 889187769Sluigi _substrcmp2(end, "by", "bytes") == 0) { 890204591Sluigi fs->flags |= DN_QSIZE_BYTES; 891187769Sluigi } 892187769Sluigi ac--; av++; 893187769Sluigi break; 894187769Sluigi 895187769Sluigi case TOK_BUCKETS: 896204591Sluigi NEED(fs, "buckets is only for pipes or flowsets"); 897187769Sluigi NEED1("buckets needs argument\n"); 898204591Sluigi *buckets = strtoul(av[0], NULL, 0); 899187769Sluigi ac--; av++; 900187769Sluigi break; 901187769Sluigi 902204591Sluigi case TOK_FLOW_MASK: 903204591Sluigi case TOK_SCHED_MASK: 904187769Sluigi case TOK_MASK: 905204591Sluigi NEED(mask, "tok_mask"); 906187769Sluigi NEED1("mask needs mask specifier\n"); 907187769Sluigi /* 908187769Sluigi * per-flow queue, mask is dst_ip, dst_port, 909187769Sluigi * src_ip, src_port, proto measured in bits 910187769Sluigi */ 911187769Sluigi 912204591Sluigi bzero(mask, sizeof(*mask)); 913187769Sluigi end = NULL; 914187769Sluigi 915187769Sluigi while (ac >= 1) { 916187769Sluigi uint32_t *p32 = NULL; 917187769Sluigi uint16_t *p16 = NULL; 918187769Sluigi uint32_t *p20 = NULL; 919187769Sluigi struct in6_addr *pa6 = NULL; 920187769Sluigi uint32_t a; 921187769Sluigi 922187769Sluigi tok = match_token(dummynet_params, *av); 923187769Sluigi ac--; av++; 924187769Sluigi switch(tok) { 925187769Sluigi case TOK_ALL: 926187769Sluigi /* 927187769Sluigi * special case, all bits significant 928205173Sluigi * except 'extra' (the queue number) 929187769Sluigi */ 930204591Sluigi mask->dst_ip = ~0; 931204591Sluigi mask->src_ip = ~0; 932204591Sluigi mask->dst_port = ~0; 933204591Sluigi mask->src_port = ~0; 934204591Sluigi mask->proto = ~0; 935204591Sluigi n2mask(&mask->dst_ip6, 128); 936204591Sluigi n2mask(&mask->src_ip6, 128); 937204591Sluigi mask->flow_id6 = ~0; 938204591Sluigi *flags |= DN_HAVE_MASK; 939187769Sluigi goto end_mask; 940187769Sluigi 941205173Sluigi case TOK_QUEUE: 942205173Sluigi mask->extra = ~0; 943205173Sluigi *flags |= DN_HAVE_MASK; 944205173Sluigi goto end_mask; 945205173Sluigi 946187769Sluigi case TOK_DSTIP: 947204591Sluigi mask->addr_type = 4; 948204591Sluigi p32 = &mask->dst_ip; 949187769Sluigi break; 950187769Sluigi 951187769Sluigi case TOK_SRCIP: 952204591Sluigi mask->addr_type = 4; 953204591Sluigi p32 = &mask->src_ip; 954187769Sluigi break; 955187769Sluigi 956187769Sluigi case TOK_DSTIP6: 957204591Sluigi mask->addr_type = 6; 958204591Sluigi pa6 = &mask->dst_ip6; 959187769Sluigi break; 960220802Sglebius 961187769Sluigi case TOK_SRCIP6: 962204591Sluigi mask->addr_type = 6; 963204591Sluigi pa6 = &mask->src_ip6; 964187769Sluigi break; 965187769Sluigi 966187769Sluigi case TOK_FLOWID: 967204591Sluigi mask->addr_type = 6; 968204591Sluigi p20 = &mask->flow_id6; 969187769Sluigi break; 970187769Sluigi 971187769Sluigi case TOK_DSTPORT: 972204591Sluigi p16 = &mask->dst_port; 973187769Sluigi break; 974187769Sluigi 975187769Sluigi case TOK_SRCPORT: 976204591Sluigi p16 = &mask->src_port; 977187769Sluigi break; 978187769Sluigi 979187769Sluigi case TOK_PROTO: 980187769Sluigi break; 981187769Sluigi 982187769Sluigi default: 983187769Sluigi ac++; av--; /* backtrack */ 984187769Sluigi goto end_mask; 985187769Sluigi } 986187769Sluigi if (ac < 1) 987187769Sluigi errx(EX_USAGE, "mask: value missing"); 988187769Sluigi if (*av[0] == '/') { 989187769Sluigi a = strtoul(av[0]+1, &end, 0); 990187769Sluigi if (pa6 == NULL) 991187769Sluigi a = (a == 32) ? ~0 : (1 << a) - 1; 992187769Sluigi } else 993187769Sluigi a = strtoul(av[0], &end, 0); 994187769Sluigi if (p32 != NULL) 995187769Sluigi *p32 = a; 996187769Sluigi else if (p16 != NULL) { 997187769Sluigi if (a > 0xFFFF) 998187769Sluigi errx(EX_DATAERR, 999187769Sluigi "port mask must be 16 bit"); 1000187769Sluigi *p16 = (uint16_t)a; 1001187769Sluigi } else if (p20 != NULL) { 1002187769Sluigi if (a > 0xfffff) 1003187769Sluigi errx(EX_DATAERR, 1004187769Sluigi "flow_id mask must be 20 bit"); 1005187769Sluigi *p20 = (uint32_t)a; 1006187769Sluigi } else if (pa6 != NULL) { 1007187769Sluigi if (a > 128) 1008187769Sluigi errx(EX_DATAERR, 1009187769Sluigi "in6addr invalid mask len"); 1010187769Sluigi else 1011187769Sluigi n2mask(pa6, a); 1012187769Sluigi } else { 1013187769Sluigi if (a > 0xFF) 1014187769Sluigi errx(EX_DATAERR, 1015187769Sluigi "proto mask must be 8 bit"); 1016205173Sluigi mask->proto = (uint8_t)a; 1017187769Sluigi } 1018187769Sluigi if (a != 0) 1019204591Sluigi *flags |= DN_HAVE_MASK; 1020187769Sluigi ac--; av++; 1021187769Sluigi } /* end while, config masks */ 1022187769Sluigiend_mask: 1023187769Sluigi break; 1024187769Sluigi 1025187769Sluigi case TOK_RED: 1026187769Sluigi case TOK_GRED: 1027187769Sluigi NEED1("red/gred needs w_q/min_th/max_th/max_p\n"); 1028204591Sluigi fs->flags |= DN_IS_RED; 1029187769Sluigi if (tok == TOK_GRED) 1030204591Sluigi fs->flags |= DN_IS_GENTLE_RED; 1031187769Sluigi /* 1032187769Sluigi * the format for parameters is w_q/min_th/max_th/max_p 1033187769Sluigi */ 1034187769Sluigi if ((end = strsep(&av[0], "/"))) { 1035187769Sluigi double w_q = strtod(end, NULL); 1036187769Sluigi if (w_q > 1 || w_q <= 0) 1037187769Sluigi errx(EX_DATAERR, "0 < w_q <= 1"); 1038204591Sluigi fs->w_q = (int) (w_q * (1 << SCALE_RED)); 1039187769Sluigi } 1040187769Sluigi if ((end = strsep(&av[0], "/"))) { 1041204591Sluigi fs->min_th = strtoul(end, &end, 0); 1042187769Sluigi if (*end == 'K' || *end == 'k') 1043204591Sluigi fs->min_th *= 1024; 1044187769Sluigi } 1045187769Sluigi if ((end = strsep(&av[0], "/"))) { 1046204591Sluigi fs->max_th = strtoul(end, &end, 0); 1047187769Sluigi if (*end == 'K' || *end == 'k') 1048204591Sluigi fs->max_th *= 1024; 1049187769Sluigi } 1050187769Sluigi if ((end = strsep(&av[0], "/"))) { 1051187769Sluigi double max_p = strtod(end, NULL); 1052266941Shiren if (max_p > 1 || max_p < 0) 1053266941Shiren errx(EX_DATAERR, "0 <= max_p <= 1"); 1054204591Sluigi fs->max_p = (int)(max_p * (1 << SCALE_RED)); 1055187769Sluigi } 1056187769Sluigi ac--; av++; 1057187769Sluigi break; 1058187769Sluigi 1059266941Shiren case TOK_ECN: 1060266941Shiren fs->flags |= DN_IS_ECN; 1061266941Shiren break; 1062266941Shiren 1063187769Sluigi case TOK_DROPTAIL: 1064204591Sluigi NEED(fs, "droptail is only for flowsets"); 1065204591Sluigi fs->flags &= ~(DN_IS_RED|DN_IS_GENTLE_RED); 1066187769Sluigi break; 1067187769Sluigi 1068187769Sluigi case TOK_BW: 1069204591Sluigi NEED(p, "bw is only for links"); 1070187769Sluigi NEED1("bw needs bandwidth or interface\n"); 1071204591Sluigi read_bandwidth(av[0], &p->bandwidth, NULL, 0); 1072187769Sluigi ac--; av++; 1073187769Sluigi break; 1074187769Sluigi 1075187769Sluigi case TOK_DELAY: 1076204591Sluigi NEED(p, "delay is only for links"); 1077187769Sluigi NEED1("delay needs argument 0..10000ms\n"); 1078204591Sluigi p->delay = strtoul(av[0], NULL, 0); 1079187769Sluigi ac--; av++; 1080187769Sluigi break; 1081187769Sluigi 1082204591Sluigi case TOK_TYPE: { 1083204591Sluigi int l; 1084204591Sluigi NEED(sch, "type is only for schedulers"); 1085204591Sluigi NEED1("type needs a string"); 1086204591Sluigi l = strlen(av[0]); 1087204591Sluigi if (l == 0 || l > 15) 1088204591Sluigi errx(1, "type %s too long\n", av[0]); 1089204591Sluigi strcpy(sch->name, av[0]); 1090204591Sluigi sch->oid.subtype = 0; /* use string */ 1091204591Sluigi ac--; av++; 1092204591Sluigi break; 1093204591Sluigi } 1094204591Sluigi 1095187769Sluigi case TOK_WEIGHT: 1096204591Sluigi NEED(fs, "weight is only for flowsets"); 1097204591Sluigi NEED1("weight needs argument\n"); 1098204591Sluigi fs->par[0] = strtol(av[0], &end, 0); 1099187769Sluigi ac--; av++; 1100187769Sluigi break; 1101187769Sluigi 1102204591Sluigi case TOK_LMAX: 1103204591Sluigi NEED(fs, "lmax is only for flowsets"); 1104204591Sluigi NEED1("lmax needs argument\n"); 1105204591Sluigi fs->par[1] = strtol(av[0], &end, 0); 1106204591Sluigi ac--; av++; 1107204591Sluigi break; 1108204591Sluigi 1109204591Sluigi case TOK_PRI: 1110204591Sluigi NEED(fs, "priority is only for flowsets"); 1111204591Sluigi NEED1("priority needs argument\n"); 1112204591Sluigi fs->par[2] = strtol(av[0], &end, 0); 1113204591Sluigi ac--; av++; 1114204591Sluigi break; 1115204591Sluigi 1116204591Sluigi case TOK_SCHED: 1117187769Sluigi case TOK_PIPE: 1118204591Sluigi NEED(fs, "pipe/sched"); 1119204591Sluigi NEED1("pipe/link/sched needs number\n"); 1120204591Sluigi fs->sched_nr = strtoul(av[0], &end, 0); 1121187769Sluigi ac--; av++; 1122187769Sluigi break; 1123187769Sluigi 1124204591Sluigi case TOK_PROFILE: 1125204591Sluigi NEED((!pf), "profile already set"); 1126204591Sluigi NEED(p, "profile"); 1127204591Sluigi { 1128190865Sluigi NEED1("extra delay needs the file name\n"); 1129204591Sluigi pf = o_next(&buf, sizeof(*pf), DN_PROFILE); 1130204591Sluigi load_extra_delays(av[0], pf, p); //XXX can't fail? 1131190865Sluigi --ac; ++av; 1132204591Sluigi } 1133190865Sluigi break; 1134190865Sluigi 1135194930Soleg case TOK_BURST: 1136204591Sluigi NEED(p, "burst"); 1137194930Soleg NEED1("burst needs argument\n"); 1138194930Soleg errno = 0; 1139211499Sdes if (expand_number(av[0], &p->burst) < 0) 1140194930Soleg if (errno != ERANGE) 1141194930Soleg errx(EX_DATAERR, 1142194930Soleg "burst: invalid argument"); 1143204591Sluigi if (errno || p->burst > (1ULL << 48) - 1) 1144194930Soleg errx(EX_DATAERR, 1145194930Soleg "burst: out of range (0..2^48-1)"); 1146194930Soleg ac--; av++; 1147194930Soleg break; 1148194930Soleg 1149187769Sluigi default: 1150187769Sluigi errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]); 1151187769Sluigi } 1152187769Sluigi } 1153204591Sluigi 1154204591Sluigi /* check validity of parameters */ 1155204591Sluigi if (p) { 1156204591Sluigi if (p->delay > 10000) 1157187769Sluigi errx(EX_DATAERR, "delay must be < 10000"); 1158204591Sluigi if (p->bandwidth == -1) 1159204591Sluigi p->bandwidth = 0; 1160187769Sluigi } 1161204591Sluigi if (fs) { 1162204591Sluigi /* XXX accept a 0 scheduler to keep the default */ 1163204591Sluigi if (fs->flags & DN_QSIZE_BYTES) { 1164187769Sluigi size_t len; 1165187769Sluigi long limit; 1166187769Sluigi 1167187769Sluigi len = sizeof(limit); 1168187769Sluigi if (sysctlbyname("net.inet.ip.dummynet.pipe_byte_limit", 1169187769Sluigi &limit, &len, NULL, 0) == -1) 1170187769Sluigi limit = 1024*1024; 1171204591Sluigi if (fs->qsize > limit) 1172187769Sluigi errx(EX_DATAERR, "queue size must be < %ldB", limit); 1173204591Sluigi } else { 1174187769Sluigi size_t len; 1175187769Sluigi long limit; 1176187769Sluigi 1177187769Sluigi len = sizeof(limit); 1178187769Sluigi if (sysctlbyname("net.inet.ip.dummynet.pipe_slot_limit", 1179187769Sluigi &limit, &len, NULL, 0) == -1) 1180187769Sluigi limit = 100; 1181204591Sluigi if (fs->qsize > limit) 1182187769Sluigi errx(EX_DATAERR, "2 <= queue size <= %ld", limit); 1183204591Sluigi } 1184204591Sluigi 1185266941Shiren if ((fs->flags & DN_IS_ECN) && !(fs->flags & DN_IS_RED)) 1186266941Shiren errx(EX_USAGE, "enable red/gred for ECN"); 1187266941Shiren 1188204591Sluigi if (fs->flags & DN_IS_RED) { 1189187769Sluigi size_t len; 1190187769Sluigi int lookup_depth, avg_pkt_size; 1191187769Sluigi 1192266941Shiren if (!(fs->flags & DN_IS_ECN) && (fs->min_th >= fs->max_th)) 1193187769Sluigi errx(EX_DATAERR, "min_th %d must be < than max_th %d", 1194204591Sluigi fs->min_th, fs->max_th); 1195266941Shiren else if ((fs->flags & DN_IS_ECN) && (fs->min_th > fs->max_th)) 1196266941Shiren errx(EX_DATAERR, "min_th %d must be =< than max_th %d", 1197266941Shiren fs->min_th, fs->max_th); 1198266941Shiren 1199204591Sluigi if (fs->max_th == 0) 1200187769Sluigi errx(EX_DATAERR, "max_th must be > 0"); 1201187769Sluigi 1202187769Sluigi len = sizeof(int); 1203187769Sluigi if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth", 1204187769Sluigi &lookup_depth, &len, NULL, 0) == -1) 1205204591Sluigi lookup_depth = 256; 1206187769Sluigi if (lookup_depth == 0) 1207187769Sluigi errx(EX_DATAERR, "net.inet.ip.dummynet.red_lookup_depth" 1208187769Sluigi " must be greater than zero"); 1209187769Sluigi 1210187769Sluigi len = sizeof(int); 1211187769Sluigi if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size", 1212187769Sluigi &avg_pkt_size, &len, NULL, 0) == -1) 1213204591Sluigi avg_pkt_size = 512; 1214187769Sluigi 1215187769Sluigi if (avg_pkt_size == 0) 1216187769Sluigi errx(EX_DATAERR, 1217187769Sluigi "net.inet.ip.dummynet.red_avg_pkt_size must" 1218187769Sluigi " be greater than zero"); 1219187769Sluigi 1220232347Sluigi#if 0 /* the following computation is now done in the kernel */ 1221187769Sluigi /* 1222187769Sluigi * Ticks needed for sending a medium-sized packet. 1223187769Sluigi * Unfortunately, when we are configuring a WF2Q+ queue, we 1224187769Sluigi * do not have bandwidth information, because that is stored 1225187769Sluigi * in the parent pipe, and also we have multiple queues 1226187769Sluigi * competing for it. So we set s=0, which is not very 1227187769Sluigi * correct. But on the other hand, why do we want RED with 1228187769Sluigi * WF2Q+ ? 1229187769Sluigi */ 1230187769Sluigi if (p.bandwidth==0) /* this is a WF2Q+ queue */ 1231187769Sluigi s = 0; 1232187769Sluigi else 1233187769Sluigi s = (double)ck.hz * avg_pkt_size * 8 / p.bandwidth; 1234187769Sluigi /* 1235187769Sluigi * max idle time (in ticks) before avg queue size becomes 0. 1236187769Sluigi * NOTA: (3/w_q) is approx the value x so that 1237187769Sluigi * (1-w_q)^x < 10^-3. 1238187769Sluigi */ 1239204591Sluigi w_q = ((double)fs->w_q) / (1 << SCALE_RED); 1240187769Sluigi idle = s * 3. / w_q; 1241204591Sluigi fs->lookup_step = (int)idle / lookup_depth; 1242204591Sluigi if (!fs->lookup_step) 1243204591Sluigi fs->lookup_step = 1; 1244187769Sluigi weight = 1 - w_q; 1245204591Sluigi for (t = fs->lookup_step; t > 1; --t) 1246187769Sluigi weight *= 1 - w_q; 1247204591Sluigi fs->lookup_weight = (int)(weight * (1 << SCALE_RED)); 1248232347Sluigi#endif /* code moved in the kernel */ 1249204591Sluigi } 1250187769Sluigi } 1251190865Sluigi 1252204591Sluigi i = do_cmd(IP_DUMMYNET3, base, (char *)buf - (char *)base); 1253190865Sluigi 1254187769Sluigi if (i) 1255187769Sluigi err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE"); 1256187769Sluigi} 1257204591Sluigi 1258204591Sluigivoid 1259204591Sluigidummynet_flush(void) 1260204591Sluigi{ 1261204591Sluigi struct dn_id oid; 1262204591Sluigi oid_fill(&oid, sizeof(oid), DN_CMD_FLUSH, DN_API_VERSION); 1263204591Sluigi do_cmd(IP_DUMMYNET3, &oid, oid.len); 1264204591Sluigi} 1265204591Sluigi 1266205050Sluigi/* Parse input for 'ipfw [pipe|sched|queue] show [range list]' 1267205050Sluigi * Returns the number of ranges, and possibly stores them 1268205050Sluigi * in the array v of size len. 1269205050Sluigi */ 1270205050Sluigistatic int 1271205050Sluigiparse_range(int ac, char *av[], uint32_t *v, int len) 1272205050Sluigi{ 1273205050Sluigi int n = 0; 1274205050Sluigi char *endptr, *s; 1275205050Sluigi uint32_t base[2]; 1276205050Sluigi 1277205050Sluigi if (v == NULL || len < 2) { 1278205050Sluigi v = base; 1279205050Sluigi len = 2; 1280205050Sluigi } 1281205050Sluigi 1282205050Sluigi for (s = *av; s != NULL; av++, ac--) { 1283205050Sluigi v[0] = strtoul(s, &endptr, 10); 1284205050Sluigi v[1] = (*endptr != '-') ? v[0] : 1285205050Sluigi strtoul(endptr+1, &endptr, 10); 1286205050Sluigi if (*endptr == '\0') { /* prepare for next round */ 1287205050Sluigi s = (ac > 0) ? *(av+1) : NULL; 1288205050Sluigi } else { 1289205050Sluigi if (*endptr != ',') { 1290205050Sluigi warn("invalid number: %s", s); 1291205050Sluigi s = ++endptr; 1292205050Sluigi continue; 1293205050Sluigi } 1294205050Sluigi /* continue processing from here */ 1295205050Sluigi s = ++endptr; 1296205050Sluigi ac++; 1297205050Sluigi av--; 1298205050Sluigi } 1299205050Sluigi if (v[1] < v[0] || 1300238900Sluigi v[1] >= DN_MAX_ID-1 || 1301238900Sluigi v[1] >= DN_MAX_ID-1) { 1302205050Sluigi continue; /* invalid entry */ 1303205050Sluigi } 1304205050Sluigi n++; 1305205050Sluigi /* translate if 'pipe list' */ 1306205050Sluigi if (co.do_pipe == 1) { 1307205050Sluigi v[0] += DN_MAX_ID; 1308205050Sluigi v[1] += DN_MAX_ID; 1309205050Sluigi } 1310205050Sluigi v = (n*2 < len) ? v + 2 : base; 1311205050Sluigi } 1312205050Sluigi return n; 1313205050Sluigi} 1314205050Sluigi 1315204591Sluigi/* main entry point for dummynet list functions. co.do_pipe indicates 1316204591Sluigi * which function we want to support. 1317205050Sluigi * av may contain filtering arguments, either individual entries 1318205050Sluigi * or ranges, or lists (space or commas are valid separators). 1319205050Sluigi * Format for a range can be n1-n2 or n3 n4 n5 ... 1320205050Sluigi * In a range n1 must be <= n2, otherwise the range is ignored. 1321205050Sluigi * A number 'n4' is translate in a range 'n4-n4' 1322205050Sluigi * All number must be > 0 and < DN_MAX_ID-1 1323204591Sluigi */ 1324204591Sluigivoid 1325204591Sluigidummynet_list(int ac, char *av[], int show_counters) 1326204591Sluigi{ 1327205050Sluigi struct dn_id *oid, *x = NULL; 1328238900Sluigi int ret, i; 1329205050Sluigi int n; /* # of ranges */ 1330238900Sluigi u_int buflen, l; 1331238900Sluigi u_int max_size; /* largest obj passed up */ 1332204591Sluigi 1333238900Sluigi (void)show_counters; // XXX unused, but we should use it. 1334205050Sluigi ac--; 1335205050Sluigi av++; /* skip 'list' | 'show' word */ 1336205050Sluigi 1337205050Sluigi n = parse_range(ac, av, NULL, 0); /* Count # of ranges. */ 1338205050Sluigi 1339205050Sluigi /* Allocate space to store ranges */ 1340205050Sluigi l = sizeof(*oid) + sizeof(uint32_t) * n * 2; 1341205050Sluigi oid = safe_calloc(1, l); 1342205050Sluigi oid_fill(oid, l, DN_CMD_GET, DN_API_VERSION); 1343205050Sluigi 1344205050Sluigi if (n > 0) /* store ranges in idx */ 1345205050Sluigi parse_range(ac, av, (uint32_t *)(oid + 1), n*2); 1346205050Sluigi /* 1347205050Sluigi * Compute the size of the largest object returned. If the 1348205050Sluigi * response leaves at least this much spare space in the 1349205050Sluigi * buffer, then surely the response is complete; otherwise 1350205050Sluigi * there might be a risk of truncation and we will need to 1351205050Sluigi * retry with a larger buffer. 1352205050Sluigi * XXX don't bother with smaller structs. 1353205050Sluigi */ 1354205050Sluigi max_size = sizeof(struct dn_fs); 1355205050Sluigi if (max_size < sizeof(struct dn_sch)) 1356205050Sluigi max_size = sizeof(struct dn_sch); 1357205050Sluigi if (max_size < sizeof(struct dn_flow)) 1358205050Sluigi max_size = sizeof(struct dn_flow); 1359205050Sluigi 1360204591Sluigi switch (co.do_pipe) { 1361204591Sluigi case 1: 1362205050Sluigi oid->subtype = DN_LINK; /* list pipe */ 1363204591Sluigi break; 1364204591Sluigi case 2: 1365205050Sluigi oid->subtype = DN_FS; /* list queue */ 1366204591Sluigi break; 1367204591Sluigi case 3: 1368205050Sluigi oid->subtype = DN_SCH; /* list sched */ 1369204591Sluigi break; 1370204591Sluigi } 1371204718Sluigi 1372205050Sluigi /* 1373205050Sluigi * Ask the kernel an estimate of the required space (result 1374205050Sluigi * in oid.id), unless we are requesting a subset of objects, 1375205050Sluigi * in which case the kernel does not give an exact answer. 1376205050Sluigi * In any case, space might grow in the meantime due to the 1377205050Sluigi * creation of new queues, so we must be prepared to retry. 1378204718Sluigi */ 1379205050Sluigi if (n > 0) { 1380205050Sluigi buflen = 4*1024; 1381205050Sluigi } else { 1382205050Sluigi ret = do_cmd(-IP_DUMMYNET3, oid, (uintptr_t)&l); 1383205050Sluigi if (ret != 0 || oid->id <= sizeof(*oid)) 1384205050Sluigi goto done; 1385205050Sluigi buflen = oid->id + max_size; 1386205050Sluigi oid->len = sizeof(*oid); /* restore */ 1387205050Sluigi } 1388205050Sluigi /* Try a few times, until the buffer fits */ 1389205050Sluigi for (i = 0; i < 20; i++) { 1390205050Sluigi l = buflen; 1391204718Sluigi x = safe_realloc(x, l); 1392205050Sluigi bcopy(oid, x, oid->len); 1393205050Sluigi ret = do_cmd(-IP_DUMMYNET3, x, (uintptr_t)&l); 1394205050Sluigi if (ret != 0 || x->id <= sizeof(*oid)) 1395205050Sluigi goto done; /* no response */ 1396205050Sluigi if (l + max_size <= buflen) 1397204718Sluigi break; /* ok */ 1398205050Sluigi buflen *= 2; /* double for next attempt */ 1399204718Sluigi } 1400204591Sluigi list_pipes(x, O_NEXT(x, l)); 1401205050Sluigidone: 1402205050Sluigi if (x) 1403205050Sluigi free(x); 1404205050Sluigi free(oid); 1405204591Sluigi} 1406