dummynet.c revision 301231
118334Speter/* 290075Sobrien * Copyright (c) 2002-2003,2010 Luigi Rizzo 3169689Skan * 418334Speter * Redistribution and use in source forms, with and without modification, 590075Sobrien * are permitted provided that this entire comment appears intact. 618334Speter * 790075Sobrien * Redistribution in binary form may occur without any restrictions. 890075Sobrien * Obviously, it would be nice if you gave credit where credit is due 990075Sobrien * but requiring it would be too onerous. 1090075Sobrien * 1118334Speter * This software is provided ``AS IS'' without any warranties of any kind. 1290075Sobrien * 1390075Sobrien * $FreeBSD: stable/10/sbin/ipfw/dummynet.c 301231 2016-06-03 00:48:50Z truckman $ 1490075Sobrien * 1590075Sobrien * dummynet support 1618334Speter */ 1718334Speter 1890075Sobrien#include <sys/types.h> 19169689Skan#include <sys/socket.h> 20169689Skan/* XXX there are several sysctl leftover here */ 2118334Speter#include <sys/sysctl.h> 2218334Speter 2318334Speter#include "ipfw2.h" 2418334Speter 2518334Speter#include <ctype.h> 2618334Speter#include <err.h> 2718334Speter#include <errno.h> 2818334Speter#include <libutil.h> 2918334Speter#include <netdb.h> 3018334Speter#include <stdio.h> 3118334Speter#include <stdlib.h> 3218334Speter#include <string.h> 3318334Speter#include <sysexits.h> 3418334Speter 3518334Speter#include <net/if.h> 3618334Speter#include <netinet/in.h> 3718334Speter#include <netinet/ip_fw.h> 3818334Speter#include <netinet/ip_dummynet.h> 3918334Speter#include <arpa/inet.h> /* inet_ntoa */ 4018334Speter 4118334Speter 4218334Speterstatic struct _s_x dummynet_params[] = { 4318334Speter { "plr", TOK_PLR }, 4418334Speter { "noerror", TOK_NOERROR }, 45117395Skan { "buckets", TOK_BUCKETS }, 46132718Skan { "dst-ip", TOK_DSTIP }, 47132718Skan { "src-ip", TOK_SRCIP }, 48117395Skan { "dst-port", TOK_DSTPORT }, 49117395Skan { "src-port", TOK_SRCPORT }, 50117395Skan { "proto", TOK_PROTO }, 51169689Skan { "weight", TOK_WEIGHT }, 5218334Speter { "lmax", TOK_LMAX }, 53117395Skan { "maxlen", TOK_LMAX }, 54117395Skan { "all", TOK_ALL }, 55132718Skan { "mask", TOK_MASK }, /* alias for both */ 56132718Skan { "sched_mask", TOK_SCHED_MASK }, 57132718Skan { "flow_mask", TOK_FLOW_MASK }, 58132718Skan { "droptail", TOK_DROPTAIL }, 59169689Skan { "ecn", TOK_ECN }, 60169689Skan { "red", TOK_RED }, 61169689Skan { "gred", TOK_GRED }, 62169689Skan { "bw", TOK_BW }, 63169689Skan { "bandwidth", TOK_BW }, 64169689Skan { "delay", TOK_DELAY }, 65169689Skan { "link", TOK_LINK }, 66169689Skan { "pipe", TOK_PIPE }, 67169689Skan { "queue", TOK_QUEUE }, 68169689Skan { "flowset", TOK_FLOWSET }, 69169689Skan { "sched", TOK_SCHED }, 7018334Speter { "pri", TOK_PRI }, 7118334Speter { "priority", TOK_PRI }, 7218334Speter { "type", TOK_TYPE }, 7318334Speter { "flow-id", TOK_FLOWID}, 7418334Speter { "dst-ipv6", TOK_DSTIP6}, 7518334Speter { "dst-ip6", TOK_DSTIP6}, 7618334Speter { "src-ipv6", TOK_SRCIP6}, 7750397Sobrien { "src-ip6", TOK_SRCIP6}, 7850397Sobrien { "profile", TOK_PROFILE}, 7990075Sobrien { "burst", TOK_BURST}, 8018334Speter { "dummynet-params", TOK_NULL }, 81117395Skan { NULL, 0 } /* terminator */ 82132718Skan}; 8318334Speter 8418334Speter#define O_NEXT(p, len) ((void *)((char *)p + len)) 8518334Speter 8618334Speterstatic void 87169689Skanoid_fill(struct dn_id *oid, int len, int type, uintptr_t id) 8818334Speter{ 8918334Speter oid->len = len; 9018334Speter oid->type = type; 9118334Speter oid->subtype = 0; 9218334Speter oid->id = id; 9318334Speter} 9418334Speter 9518334Speter/* make room in the buffer and move the pointer forward */ 9618334Speterstatic void * 9718334Spetero_next(struct dn_id **o, int len, int type) 9818334Speter{ 9918334Speter struct dn_id *ret = *o; 10018334Speter oid_fill(ret, len, type, 0); 10118334Speter *o = O_NEXT(*o, len); 10218334Speter return ret; 10318334Speter} 10418334Speter 10518334Speter#if 0 10618334Speterstatic int 10718334Spetersort_q(void *arg, const void *pa, const void *pb) 10818334Speter{ 10918334Speter int rev = (co.do_sort < 0); 11018334Speter int field = rev ? -co.do_sort : co.do_sort; 11118334Speter long long res = 0; 11250397Sobrien const struct dn_flow_queue *a = pa; 11318334Speter const struct dn_flow_queue *b = pb; 114132718Skan 115132718Skan switch (field) { 116132718Skan case 1: /* pkts */ 117132718Skan res = a->len - b->len; 118132718Skan break; 119132718Skan case 2: /* bytes */ 120132718Skan res = a->len_bytes - b->len_bytes; 121132718Skan break; 122132718Skan 12390075Sobrien case 3: /* tot pkts */ 124132718Skan res = a->tot_pkts - b->tot_pkts; 12518334Speter break; 126132718Skan 127132718Skan case 4: /* tot bytes */ 128132718Skan res = a->tot_bytes - b->tot_bytes; 129132718Skan break; 130132718Skan } 131132718Skan if (res < 0) 132132718Skan res = -1; 133132718Skan if (res > 0) 134132718Skan res = 1; 135132718Skan return (int)(rev ? res : -res); 136132718Skan} 137132718Skan#endif 13890075Sobrien 139132718Skan/* print a mask and header for the subsequent list of flows */ 140132718Skanstatic void 14118334Speterprint_mask(struct ipfw_flow_id *id) 142132718Skan{ 143132718Skan if (!IS_IP6_FLOW_ID(id)) { 144132718Skan printf(" " 145132718Skan "mask: %s 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n", 146132718Skan id->extra ? "queue," : "", 147132718Skan id->proto, 148132718Skan id->src_ip, id->src_port, 14918334Speter id->dst_ip, id->dst_port); 15018334Speter } else { 15118334Speter char buf[255]; 15218334Speter printf("\n mask: %sproto: 0x%02x, flow_id: 0x%08x, ", 15318334Speter id->extra ? "queue," : "", 15418334Speter id->proto, id->flow_id6); 15518334Speter inet_ntop(AF_INET6, &(id->src_ip6), buf, sizeof(buf)); 15618334Speter printf("%s/0x%04x -> ", buf, id->src_port); 15718334Speter inet_ntop(AF_INET6, &(id->dst_ip6), buf, sizeof(buf)); 15818334Speter printf("%s/0x%04x\n", buf, id->dst_port); 15918334Speter } 16018334Speter} 16118334Speter 16218334Speterstatic void 16318334Speterprint_header(struct ipfw_flow_id *id) 16418334Speter{ 16518334Speter if (!IS_IP6_FLOW_ID(id)) 16650397Sobrien printf("BKT Prot ___Source IP/port____ " 16750397Sobrien "____Dest. IP/port____ " 168132718Skan "Tot_pkt/bytes Pkt/Byte Drp\n"); 169132718Skan else 17050397Sobrien printf("BKT ___Prot___ _flow-id_ " 17150397Sobrien "______________Source IPv6/port_______________ " 17218334Speter "_______________Dest. IPv6/port_______________ " 17318334Speter "Tot_pkt/bytes Pkt/Byte Drp\n"); 17418334Speter} 17518334Speter 17618334Speterstatic void 17718334Speterlist_flow(struct dn_flow *ni, int *print) 17818334Speter{ 17918334Speter char buff[255]; 18018334Speter struct protoent *pe = NULL; 18118334Speter struct in_addr ina; 18218334Speter struct ipfw_flow_id *id = &ni->fid; 18318334Speter 184132718Skan if (*print) { 18518334Speter print_header(&ni->fid); 18618334Speter *print = 0; 18718334Speter } 18818334Speter pe = getprotobynumber(id->proto); 18918334Speter /* XXX: Should check for IPv4 flows */ 19018334Speter printf("%3u%c", (ni->oid.id) & 0xff, 19118334Speter id->extra ? '*' : ' '); 19218334Speter if (!IS_IP6_FLOW_ID(id)) { 19318334Speter if (pe) 19418334Speter printf("%-4s ", pe->p_name); 19518334Speter else 19618334Speter printf("%4u ", id->proto); 19718334Speter ina.s_addr = htonl(id->src_ip); 19818334Speter printf("%15s/%-5d ", 19918334Speter inet_ntoa(ina), id->src_port); 20018334Speter ina.s_addr = htonl(id->dst_ip); 20150397Sobrien printf("%15s/%-5d ", 20250397Sobrien inet_ntoa(ina), id->dst_port); 203132718Skan } else { 204132718Skan /* Print IPv6 flows */ 20550397Sobrien if (pe != NULL) 20618334Speter printf("%9s ", pe->p_name); 20718334Speter else 20818334Speter printf("%9u ", id->proto); 20918334Speter printf("%7d %39s/%-5d ", id->flow_id6, 21018334Speter inet_ntop(AF_INET6, &(id->src_ip6), buff, sizeof(buff)), 21118334Speter id->src_port); 21218334Speter printf(" %39s/%-5d ", 21318334Speter inet_ntop(AF_INET6, &(id->dst_ip6), buff, sizeof(buff)), 21418334Speter id->dst_port); 21518334Speter } 21618334Speter pr_u64(&ni->tot_pkts, 4); 21718334Speter pr_u64(&ni->tot_bytes, 8); 21818334Speter printf("%2u %4u %3u\n", 21918334Speter ni->length, ni->len_bytes, ni->drops); 22018334Speter} 22118334Speter 22218334Speterstatic void 22390075Sobrienprint_flowset_parms(struct dn_fs *fs, char *prefix) 22418334Speter{ 22518334Speter int l; 22618334Speter char qs[30]; 22718334Speter char plr[30]; 22818334Speter char red[90]; /* Display RED parameters */ 22918334Speter 23018334Speter l = fs->qsize; 23118334Speter if (fs->flags & DN_QSIZE_BYTES) { 23218334Speter if (l >= 8192) 23318334Speter sprintf(qs, "%d KB", l / 1024); 23418334Speter else 23518334Speter sprintf(qs, "%d B", l); 23618334Speter } else 23718334Speter sprintf(qs, "%3d sl.", l); 23818334Speter if (fs->plr) 23918334Speter sprintf(plr, "plr %f", 1.0 * fs->plr / (double)(0x7fffffff)); 24018334Speter else 24118334Speter plr[0] = '\0'; 24218334Speter 24318334Speter if (fs->flags & DN_IS_RED) { /* RED parameters */ 24418334Speter sprintf(red, 24518334Speter "\n\t %cRED w_q %f min_th %d max_th %d max_p %f", 24618334Speter (fs->flags & DN_IS_GENTLE_RED) ? 'G' : ' ', 24718334Speter 1.0 * fs->w_q / (double)(1 << SCALE_RED), 24818334Speter fs->min_th, 24918334Speter fs->max_th, 25018334Speter 1.0 * fs->max_p / (double)(1 << SCALE_RED)); 25118334Speter if (fs->flags & DN_IS_ECN) 25218334Speter strncat(red, " (ecn)", 6); 25318334Speter } else 25418334Speter sprintf(red, "droptail"); 25518334Speter 25690075Sobrien if (prefix[0]) { 25718334Speter printf("%s %s%s %d queues (%d buckets) %s\n", 25818334Speter prefix, qs, plr, fs->oid.id, fs->buckets, red); 25918334Speter prefix[0] = '\0'; 26018334Speter } else { 26118334Speter printf("q%05d %s%s %d flows (%d buckets) sched %d " 26218334Speter "weight %d lmax %d pri %d %s\n", 263132718Skan fs->fs_nr, qs, plr, fs->oid.id, fs->buckets, 26418334Speter fs->sched_nr, fs->par[0], fs->par[1], fs->par[2], red); 26518334Speter if (fs->flags & DN_HAVE_MASK) 26618334Speter print_mask(&fs->flow_mask); 26718334Speter } 26818334Speter} 26918334Speter 27018334Speterstatic void 27118334Speterprint_extra_delay_parms(struct dn_profile *p) 27218334Speter{ 27318334Speter double loss; 27418334Speter if (p->samples_no <= 0) 27590075Sobrien return; 27690075Sobrien 27750397Sobrien loss = p->loss_level; 27850397Sobrien loss /= p->samples_no; 27950397Sobrien printf("\t profile: name \"%s\" loss %f samples %d\n", 28050397Sobrien p->name, loss, p->samples_no); 28150397Sobrien} 28250397Sobrien 28350397Sobrienstatic void 28450397Sobrienflush_buf(char *buf) 28550397Sobrien{ 28650397Sobrien if (buf[0]) 28750397Sobrien printf("%s\n", buf); 28850397Sobrien buf[0] = '\0'; 28950397Sobrien} 29050397Sobrien 29150397Sobrien/* 29250397Sobrien * generic list routine. We expect objects in a specific order, i.e. 29350397Sobrien * PIPES AND SCHEDULERS: 29450397Sobrien * link; scheduler; internal flowset if any; instances 29550397Sobrien * we can tell a pipe from the number. 29690075Sobrien * 29750397Sobrien * FLOWSETS: 29850397Sobrien * flowset; queues; 29950397Sobrien * link i (int queue); scheduler i; si(i) { flowsets() : queues } 30050397Sobrien */ 30150397Sobrienstatic void 30250397Sobrienlist_pipes(struct dn_id *oid, struct dn_id *end) 30350397Sobrien{ 304132718Skan char buf[160]; /* pending buffer */ 30590075Sobrien int toPrint = 1; /* print header */ 306117395Skan 30718334Speter buf[0] = '\0'; 308169689Skan for (; oid != end; oid = O_NEXT(oid, oid->len)) { 309169689Skan if (oid->len < sizeof(*oid)) 310169689Skan errx(1, "invalid oid len %d\n", oid->len); 311169689Skan 312169689Skan switch (oid->type) { 313169689Skan default: 314169689Skan flush_buf(buf); 315169689Skan printf("unrecognized object %d size %d\n", oid->type, oid->len); 316169689Skan break; 317169689Skan case DN_TEXT: /* list of attached flowsets */ 31850397Sobrien { 31990075Sobrien int i, l; 32090075Sobrien struct { 321169689Skan struct dn_id id; 322169689Skan uint32_t p[0]; 32390075Sobrien } *d = (void *)oid; 324169689Skan l = (oid->len - sizeof(*oid))/sizeof(d->p[0]); 325169689Skan if (l == 0) 32650397Sobrien break; 327169689Skan printf(" Children flowsets: "); 328169689Skan for (i = 0; i < l; i++) 329169689Skan printf("%u ", d->p[i]); 330169689Skan printf("\n"); 331169689Skan break; 332169689Skan } 333169689Skan case DN_CMD_GET: 334169689Skan if (co.verbose) 335169689Skan printf("answer for cmd %d, len %d\n", oid->type, oid->id); 336169689Skan break; 337169689Skan case DN_SCH: { 338169689Skan struct dn_sch *s = (struct dn_sch *)oid; 339169689Skan flush_buf(buf); 340169689Skan printf(" sched %d type %s flags 0x%x %d buckets %d active\n", 34190075Sobrien s->sched_nr, 34218334Speter s->name, s->flags, s->buckets, s->oid.id); 34318334Speter if (s->flags & DN_HAVE_MASK) 34418334Speter print_mask(&s->sched_mask); 34518334Speter } 346132718Skan break; 34718334Speter 34818334Speter case DN_FLOW: 34918334Speter list_flow((struct dn_flow *)oid, &toPrint); 350169689Skan break; 35118334Speter 352117395Skan case DN_LINK: { 35318334Speter struct dn_link *p = (struct dn_link *)oid; 35418334Speter double b = p->bandwidth; 355132718Skan char bwbuf[30]; 35618334Speter char burst[5 + 7]; 35718334Speter 35818334Speter /* This starts a new object so flush buffer */ 35918334Speter flush_buf(buf); 36018334Speter /* data rate */ 36118334Speter if (b == 0) 36218334Speter sprintf(bwbuf, "unlimited "); 36318334Speter else if (b >= 1000000) 36418334Speter sprintf(bwbuf, "%7.3f Mbit/s", b/1000000); 36518334Speter else if (b >= 1000) 36618334Speter sprintf(bwbuf, "%7.3f Kbit/s", b/1000); 36718334Speter else 36818334Speter sprintf(bwbuf, "%7.3f bit/s ", b); 36918334Speter 37018334Speter if (humanize_number(burst, sizeof(burst), p->burst, 37118334Speter "", HN_AUTOSCALE, 0) < 0 || co.verbose) 37218334Speter sprintf(burst, "%d", (int)p->burst); 37318334Speter sprintf(buf, "%05d: %s %4d ms burst %s", 37490075Sobrien p->link_nr % DN_MAX_ID, bwbuf, p->delay, burst); 37590075Sobrien } 37690075Sobrien break; 37718334Speter 37818334Speter case DN_FS: 37918334Speter print_flowset_parms((struct dn_fs *)oid, buf); 38018334Speter break; 38118334Speter case DN_PROFILE: 38218334Speter flush_buf(buf); 38318334Speter print_extra_delay_parms((struct dn_profile *)oid); 38418334Speter } 38518334Speter flush_buf(buf); // XXX does it really go here ? 38618334Speter } 38718334Speter} 388132718Skan 38918334Speter/* 39018334Speter * Delete pipe, queue or scheduler i 39118334Speter */ 39218334Speterint 39318334Speteripfw_delete_pipe(int do_pipe, int i) 39418334Speter{ 39518334Speter struct { 39618334Speter struct dn_id oid; 39718334Speter uintptr_t a[1]; /* add more if we want a list */ 39818334Speter } cmd; 39918334Speter oid_fill((void *)&cmd, sizeof(cmd), DN_CMD_DELETE, DN_API_VERSION); 40018334Speter cmd.oid.subtype = (do_pipe == 1) ? DN_LINK : 40118334Speter ( (do_pipe == 2) ? DN_FS : DN_SCH); 40218334Speter cmd.a[0] = i; 40318334Speter i = do_cmd(IP_DUMMYNET3, &cmd, cmd.oid.len); 40418334Speter if (i) { 40518334Speter i = 1; 40618334Speter warn("rule %u: setsockopt(IP_DUMMYNET_DEL)", i); 40718334Speter } 40818334Speter return i; 40918334Speter} 41018334Speter 41118334Speter/* 41218334Speter * Code to parse delay profiles. 41318334Speter * 41418334Speter * Some link types introduce extra delays in the transmission 41518334Speter * of a packet, e.g. because of MAC level framing, contention on 41618334Speter * the use of the channel, MAC level retransmissions and so on. 41718334Speter * From our point of view, the channel is effectively unavailable 418132718Skan * for this extra time, which is constant or variable depending 41918334Speter * on the link type. Additionally, packets may be dropped after this 42090075Sobrien * time (e.g. on a wireless link after too many retransmissions). 42118334Speter * We can model the additional delay with an empirical curve 42218334Speter * that represents its distribution. 42318334Speter * 42418334Speter * cumulative probability 42518334Speter * 1.0 ^ 42618334Speter * | 42718334Speter * L +-- loss-level x 428132718Skan * | ****** 42918334Speter * | * 43090075Sobrien * | ***** 43118334Speter * | * 43218334Speter * | ** 43318334Speter * | * 43418334Speter * +-------*-------------------> 43518334Speter * delay 43618334Speter * 43718334Speter * The empirical curve may have both vertical and horizontal lines. 43818334Speter * Vertical lines represent constant delay for a range of 43990075Sobrien * probabilities; horizontal lines correspond to a discontinuty 44018334Speter * in the delay distribution: the link will use the largest delay 44118334Speter * for a given probability. 44218334Speter * 44318334Speter * To pass the curve to dummynet, we must store the parameters 44418334Speter * in a file as described below, and issue the command 44518334Speter * 44618334Speter * ipfw pipe <n> config ... bw XXX profile <filename> ... 44718334Speter * 44818334Speter * The file format is the following, with whitespace acting as 44918334Speter * a separator and '#' indicating the beginning a comment: 45018334Speter * 45118334Speter * samples N 45218334Speter * the number of samples used in the internal 45318334Speter * representation (2..1024; default 100); 45418334Speter * 45518334Speter * loss-level L 45618334Speter * The probability above which packets are lost. 45718334Speter * (0.0 <= L <= 1.0, default 1.0 i.e. no loss); 45818334Speter * 45918334Speter * name identifier 46018334Speter * Optional a name (listed by "ipfw pipe show") 46118334Speter * to identify the distribution; 46218334Speter * 46318334Speter * "delay prob" | "prob delay" 46418334Speter * One of these two lines is mandatory and defines 46518334Speter * the format of the following lines with data points. 46618334Speter * 46718334Speter * XXX YYY 46818334Speter * 2 or more lines representing points in the curve, 46918334Speter * with either delay or probability first, according 47018334Speter * to the chosen format. 47118334Speter * The unit for delay is milliseconds. 47218334Speter * 47318334Speter * Data points does not need to be ordered or equal to the number 47418334Speter * specified in the "samples" line. ipfw will sort and interpolate 47518334Speter * the curve as needed. 47618334Speter * 47718334Speter * Example of a profile file: 478132718Skan 47918334Speter name bla_bla_bla 48018334Speter samples 100 48118334Speter loss-level 0.86 48218334Speter prob delay 48318334Speter 0 200 # minimum overhead is 200ms 48418334Speter 0.5 200 48518334Speter 0.5 300 48618334Speter 0.8 1000 48718334Speter 0.9 1300 48818334Speter 1 1300 48918334Speter 49018334Speter * Internally, we will convert the curve to a fixed number of 49152284Sobrien * samples, and when it is time to transmit a packet we will 49218334Speter * model the extra delay as extra bits in the packet. 49318334Speter * 49418334Speter */ 49518334Speter 49618334Speter#define ED_MAX_LINE_LEN 256+ED_MAX_NAME_LEN 49718334Speter#define ED_TOK_SAMPLES "samples" 49818334Speter#define ED_TOK_LOSS "loss-level" 49918334Speter#define ED_TOK_NAME "name" 50018334Speter#define ED_TOK_DELAY "delay" 50118334Speter#define ED_TOK_PROB "prob" 50218334Speter#define ED_TOK_BW "bw" 50318334Speter#define ED_SEPARATORS " \t\n" 50418334Speter#define ED_MIN_SAMPLES_NO 2 50590075Sobrien 50690075Sobrien/* 50718334Speter * returns 1 if s is a non-negative number, with at least one '.' 50850397Sobrien */ 50950397Sobrienstatic int 51018334Speteris_valid_number(const char *s) 51118334Speter{ 51218334Speter int i, dots_found = 0; 51318334Speter int len = strlen(s); 51450397Sobrien 51550397Sobrien for (i = 0; i<len; ++i) 51618334Speter if (!isdigit(s[i]) && (s[i] !='.' || ++dots_found > 1)) 51718334Speter return 0; 51818334Speter return 1; 51918334Speter} 52018334Speter 52118334Speter/* 52218334Speter * Take as input a string describing a bandwidth value 52318334Speter * and return the numeric bandwidth value. 52418334Speter * set clocking interface or bandwidth value 52518334Speter */ 52618334Speterstatic void 52718334Speterread_bandwidth(char *arg, int *bandwidth, char *if_name, int namelen) 52850397Sobrien{ 529169689Skan if (*bandwidth != -1) 53018334Speter warnx("duplicate token, override bandwidth value!"); 531169689Skan 53218334Speter if (arg[0] >= 'a' && arg[0] <= 'z') { 533169689Skan if (!if_name) { 53418334Speter errx(1, "no if support"); 535169689Skan } 53618334Speter if (namelen >= IFNAMSIZ) 537169689Skan warn("interface name truncated"); 53818334Speter namelen--; 53918334Speter /* interface name */ 54018334Speter strncpy(if_name, arg, namelen); 54118334Speter if_name[namelen] = '\0'; 54218334Speter *bandwidth = 0; 54350397Sobrien } else { /* read bandwidth value */ 54450397Sobrien int bw; 54518334Speter char *end = NULL; 54650397Sobrien 54718334Speter bw = strtoul(arg, &end, 0); 54850397Sobrien if (*end == 'K' || *end == 'k') { 54950397Sobrien end++; 55050397Sobrien bw *= 1000; 55150397Sobrien } else if (*end == 'M' || *end == 'm') { 55250397Sobrien end++; 55350397Sobrien bw *= 1000000; 55450397Sobrien } 55518334Speter if ((*end == 'B' && 55618334Speter _substrcmp2(end, "Bi", "Bit/s") != 0) || 55718334Speter _substrcmp2(end, "by", "bytes") == 0) 55818334Speter bw *= 8; 55918334Speter 56018334Speter if (bw < 0) 56118334Speter errx(EX_DATAERR, "bandwidth too large"); 56218334Speter 56318334Speter *bandwidth = bw; 56418334Speter if (if_name) 56518334Speter if_name[0] = '\0'; 56618334Speter } 56718334Speter} 56890075Sobrien 56990075Sobrienstruct point { 57090075Sobrien double prob; 57190075Sobrien double delay; 57290075Sobrien}; 57390075Sobrien 57418334Speterstatic int 57590075Sobriencompare_points(const void *vp1, const void *vp2) 57618334Speter{ 57718334Speter const struct point *p1 = vp1; 57818334Speter const struct point *p2 = vp2; 57918334Speter double res = 0; 58018334Speter 58118334Speter res = p1->prob - p2->prob; 58218334Speter if (res == 0) 58318334Speter res = p1->delay - p2->delay; 58418334Speter if (res < 0) 58518334Speter return -1; 58618334Speter else if (res > 0) 58718334Speter return 1; 58818334Speter else 58918334Speter return 0; 59018334Speter} 59118334Speter 59218334Speter#define ED_EFMT(s) EX_DATAERR,"error in %s at line %d: "#s,filename,lineno 59318334Speter 59418334Speterstatic void 59518334Speterload_extra_delays(const char *filename, struct dn_profile *p, 59618334Speter struct dn_link *link) 59718334Speter{ 59818334Speter char line[ED_MAX_LINE_LEN]; 59918334Speter FILE *f; 60050397Sobrien int lineno = 0; 60150397Sobrien int i; 60218334Speter 60318334Speter int samples = -1; 60418334Speter double loss = -1.0; 60518334Speter char profile_name[ED_MAX_NAME_LEN]; 60618334Speter int delay_first = -1; 60718334Speter int do_points = 0; 60818334Speter struct point points[ED_MAX_SAMPLES_NO]; 60918334Speter int points_no = 0; 61018334Speter 61118334Speter /* XXX link never NULL? */ 61218334Speter p->link_nr = link->link_nr; 61318334Speter 61418334Speter profile_name[0] = '\0'; 61518334Speter f = fopen(filename, "r"); 61618334Speter if (f == NULL) 61718334Speter err(EX_UNAVAILABLE, "fopen: %s", filename); 61818334Speter 61918334Speter while (fgets(line, ED_MAX_LINE_LEN, f)) { /* read commands */ 62018334Speter char *s, *cur = line, *name = NULL, *arg = NULL; 62118334Speter 62218334Speter ++lineno; 62318334Speter 62418334Speter /* parse the line */ 62518334Speter while (cur) { 62618334Speter s = strsep(&cur, ED_SEPARATORS); 62718334Speter if (s == NULL || *s == '#') 62818334Speter break; 62918334Speter if (*s == '\0') 63018334Speter continue; 63118334Speter if (arg) 63218334Speter errx(ED_EFMT("too many arguments")); 63318334Speter if (name == NULL) 63418334Speter name = s; 63518334Speter else 63618334Speter arg = s; 63718334Speter } 63818334Speter if (name == NULL) /* empty line */ 63918334Speter continue; 640132718Skan if (arg == NULL) 64118334Speter errx(ED_EFMT("missing arg for %s"), name); 64218334Speter 64318334Speter if (!strcasecmp(name, ED_TOK_SAMPLES)) { 64418334Speter if (samples > 0) 64518334Speter errx(ED_EFMT("duplicate ``samples'' line")); 64618334Speter if (atoi(arg) <=0) 64718334Speter errx(ED_EFMT("invalid number of samples")); 64818334Speter samples = atoi(arg); 649132718Skan if (samples>ED_MAX_SAMPLES_NO) 65018334Speter errx(ED_EFMT("too many samples, maximum is %d"), 65118334Speter ED_MAX_SAMPLES_NO); 65218334Speter do_points = 0; 65318334Speter } else if (!strcasecmp(name, ED_TOK_BW)) { 65418334Speter char buf[IFNAMSIZ]; 65518334Speter read_bandwidth(arg, &link->bandwidth, buf, sizeof(buf)); 65618334Speter } else if (!strcasecmp(name, ED_TOK_LOSS)) { 65790075Sobrien if (loss != -1.0) 65890075Sobrien errx(ED_EFMT("duplicated token: %s"), name); 65918334Speter if (!is_valid_number(arg)) 66018334Speter errx(ED_EFMT("invalid %s"), arg); 66190075Sobrien loss = atof(arg); 66218334Speter if (loss > 1) 66318334Speter errx(ED_EFMT("%s greater than 1.0"), name); 66418334Speter do_points = 0; 66518334Speter } else if (!strcasecmp(name, ED_TOK_NAME)) { 66618334Speter if (profile_name[0] != '\0') 66718334Speter errx(ED_EFMT("duplicated token: %s"), name); 66818334Speter strncpy(profile_name, arg, sizeof(profile_name) - 1); 66918334Speter profile_name[sizeof(profile_name)-1] = '\0'; 67018334Speter do_points = 0; 671132718Skan } else if (!strcasecmp(name, ED_TOK_DELAY)) { 67218334Speter if (do_points) 67318334Speter errx(ED_EFMT("duplicated token: %s"), name); 67418334Speter delay_first = 1; 675132718Skan do_points = 1; 67618334Speter } else if (!strcasecmp(name, ED_TOK_PROB)) { 67718334Speter if (do_points) 67818334Speter errx(ED_EFMT("duplicated token: %s"), name); 67918334Speter delay_first = 0; 68018334Speter do_points = 1; 68118334Speter } else if (do_points) { 68218334Speter if (!is_valid_number(name) || !is_valid_number(arg)) 68318334Speter errx(ED_EFMT("invalid point found")); 68418334Speter if (delay_first) { 68518334Speter points[points_no].delay = atof(name); 68618334Speter points[points_no].prob = atof(arg); 68718334Speter } else { 68818334Speter points[points_no].delay = atof(arg); 689132718Skan points[points_no].prob = atof(name); 69018334Speter } 69118334Speter if (points[points_no].prob > 1.0) 69218334Speter errx(ED_EFMT("probability greater than 1.0")); 69318334Speter ++points_no; 69418334Speter } else { 69590075Sobrien errx(ED_EFMT("unrecognised command '%s'"), name); 69618334Speter } 697169689Skan } 698169689Skan 699169689Skan fclose (f); 700169689Skan 701169689Skan if (samples == -1) { 702169689Skan warnx("'%s' not found, assuming 100", ED_TOK_SAMPLES); 703169689Skan samples = 100; 704169689Skan } 70518334Speter 70618334Speter if (loss == -1.0) { 70718334Speter warnx("'%s' not found, assuming no loss", ED_TOK_LOSS); 70818334Speter loss = 1; 70918334Speter } 71018334Speter 71118334Speter /* make sure that there are enough points. */ 71218334Speter if (points_no < ED_MIN_SAMPLES_NO) 71318334Speter errx(ED_EFMT("too few samples, need at least %d"), 71418334Speter ED_MIN_SAMPLES_NO); 71518334Speter 71618334Speter qsort(points, points_no, sizeof(struct point), compare_points); 71718334Speter 71850397Sobrien /* interpolation */ 71950397Sobrien for (i = 0; i<points_no-1; ++i) { 72050397Sobrien double y1 = points[i].prob * samples; 72150397Sobrien double x1 = points[i].delay; 72218334Speter double y2 = points[i+1].prob * samples; 723169689Skan double x2 = points[i+1].delay; 72418334Speter 72518334Speter int ix = y1; 72618334Speter int stop = y2; 72718334Speter 72818334Speter if (x1 == x2) { 72918334Speter for (; ix<stop; ++ix) 73018334Speter p->samples[ix] = x1; 73118334Speter } else { 73218334Speter double m = (y2-y1)/(x2-x1); 73318334Speter double c = y1 - m*x1; 73418334Speter for (; ix<stop ; ++ix) 73518334Speter p->samples[ix] = (ix - c)/m; 73618334Speter } 737169689Skan } 738169689Skan p->samples_no = samples; 739169689Skan p->loss_level = loss * samples; 740169689Skan strncpy(p->name, profile_name, sizeof(p->name)); 741169689Skan} 74218334Speter 74318334Speter/* 74418334Speter * configuration of pipes, schedulers, flowsets. 74518334Speter * When we configure a new scheduler, an empty pipe is created, so: 74618334Speter * 74718334Speter * do_pipe = 1 -> "pipe N config ..." only for backward compatibility 74818334Speter * sched N+Delta type fifo sched_mask ... 74918334Speter * pipe N+Delta <parameters> 75018334Speter * flowset N+Delta pipe N+Delta (no parameters) 75118334Speter * sched N type wf2q+ sched_mask ... 75218334Speter * pipe N <parameters> 75318334Speter * 754169689Skan * do_pipe = 2 -> flowset N config 75518334Speter * flowset N parameters 75618334Speter * 75718334Speter * do_pipe = 3 -> sched N config 75818334Speter * sched N parameters (default no pipe) 75918334Speter * optional Pipe N config ... 76018334Speter * pipe ==> 76118334Speter */ 76218334Spetervoid 76318334Speteripfw_config_pipe(int ac, char **av) 76418334Speter{ 76518334Speter int i; 76618334Speter u_int j; 76718334Speter char *end; 76818334Speter struct dn_id *buf, *base; 76990075Sobrien struct dn_sch *sch = NULL; 77018334Speter struct dn_link *p = NULL; 77118334Speter struct dn_fs *fs = NULL; 772117395Skan struct dn_profile *pf = NULL; 77390075Sobrien struct ipfw_flow_id *mask = NULL; 77418334Speter int lmax; 77552284Sobrien uint32_t _foo = 0, *flags = &_foo , *buckets = &_foo; 77618334Speter 77718334Speter /* 77818334Speter * allocate space for 1 header, 77918334Speter * 1 scheduler, 1 link, 1 flowset, 1 profile 78018334Speter */ 78118334Speter lmax = sizeof(struct dn_id); /* command header */ 78218334Speter lmax += sizeof(struct dn_sch) + sizeof(struct dn_link) + 78318334Speter sizeof(struct dn_fs) + sizeof(struct dn_profile); 78418334Speter 78518334Speter av++; ac--; 786169689Skan /* Pipe number */ 78718334Speter if (ac && isdigit(**av)) { 78890075Sobrien i = atoi(*av); av++; ac--; 78918334Speter } else 79018334Speter i = -1; 79118334Speter if (i <= 0) 79218334Speter errx(EX_USAGE, "need a pipe/flowset/sched number"); 79318334Speter base = buf = safe_calloc(1, lmax); 79418334Speter /* all commands start with a 'CONFIGURE' and a version */ 79590075Sobrien o_next(&buf, sizeof(struct dn_id), DN_CMD_CONFIG); 796169689Skan base->id = DN_API_VERSION; 79718334Speter 79890075Sobrien switch (co.do_pipe) { 79918334Speter case 1: /* "pipe N config ..." */ 80018334Speter /* Allocate space for the WF2Q+ scheduler, its link 80190075Sobrien * and the FIFO flowset. Set the number, but leave 80290075Sobrien * the scheduler subtype and other parameters to 0 80318334Speter * so the kernel will use appropriate defaults. 80418334Speter * XXX todo: add a flag to record if a parameter 80518334Speter * is actually configured. 80618334Speter * If we do a 'pipe config' mask -> sched_mask. 807169689Skan * The FIFO scheduler and link are derived from the 80818334Speter * WF2Q+ one in the kernel. 80918334Speter */ 81018334Speter sch = o_next(&buf, sizeof(*sch), DN_SCH); 81118334Speter p = o_next(&buf, sizeof(*p), DN_LINK); 81218334Speter fs = o_next(&buf, sizeof(*fs), DN_FS); 81318334Speter 81418334Speter sch->sched_nr = i; 81518334Speter sch->oid.subtype = 0; /* defaults to WF2Q+ */ 81618334Speter mask = &sch->sched_mask; 81718334Speter flags = &sch->flags; 81818334Speter buckets = &sch->buckets; 81990075Sobrien *flags |= DN_PIPE_CMD; 82018334Speter 82118334Speter p->link_nr = i; 82218334Speter 82318334Speter /* This flowset is only for the FIFO scheduler */ 82418334Speter fs->fs_nr = i + 2*DN_MAX_ID; 82518334Speter fs->sched_nr = i + DN_MAX_ID; 826169689Skan break; 82718334Speter 82818334Speter case 2: /* "queue N config ... " */ 82918334Speter fs = o_next(&buf, sizeof(*fs), DN_FS); 83018334Speter fs->fs_nr = i; 83118334Speter mask = &fs->flow_mask; 83218334Speter flags = &fs->flags; 83318334Speter buckets = &fs->buckets; 83418334Speter break; 83518334Speter 83690075Sobrien case 3: /* "sched N config ..." */ 83790075Sobrien sch = o_next(&buf, sizeof(*sch), DN_SCH); 83890075Sobrien fs = o_next(&buf, sizeof(*fs), DN_FS); 83990075Sobrien sch->sched_nr = i; 84018334Speter mask = &sch->sched_mask; 841169689Skan flags = &sch->flags; 84218334Speter buckets = &sch->buckets; 84318334Speter /* fs is used only with !MULTIQUEUE schedulers */ 84418334Speter fs->fs_nr = i + DN_MAX_ID; 84518334Speter fs->sched_nr = i; 84618334Speter break; 84718334Speter } 848117395Skan /* set to -1 those fields for which we want to reuse existing 84918334Speter * values from the kernel. 85018334Speter * Also, *_nr and subtype = 0 mean reuse the value from the kernel. 85118334Speter * XXX todo: support reuse of the mask. 85218334Speter */ 853117395Skan if (p) 85418334Speter p->bandwidth = -1; 85518334Speter for (j = 0; j < sizeof(fs->par)/sizeof(fs->par[0]); j++) 85618334Speter fs->par[j] = -1; 85718334Speter while (ac > 0) { 85818334Speter double d; 85918334Speter int tok = match_token(dummynet_params, *av); 86018334Speter ac--; av++; 86118334Speter 862169689Skan switch(tok) { 863169689Skan case TOK_NOERROR: 864169689Skan NEED(fs, "noerror is only for pipes"); 86518334Speter fs->flags |= DN_NOERROR; 86618334Speter break; 86718334Speter 86818334Speter case TOK_PLR: 86918334Speter NEED(fs, "plr is only for pipes"); 87018334Speter NEED1("plr needs argument 0..1\n"); 87118334Speter d = strtod(av[0], NULL); 87218334Speter if (d > 1) 87318334Speter d = 1; 874169689Skan else if (d < 0) 87518334Speter d = 0; 87618334Speter fs->plr = (int)(d*0x7fffffff); 87718334Speter ac--; av++; 87818334Speter break; 87918334Speter 88018334Speter case TOK_QUEUE: 88118334Speter NEED(fs, "queue is only for pipes or flowsets"); 88218334Speter NEED1("queue needs queue size\n"); 88318334Speter end = NULL; 88418334Speter fs->qsize = strtoul(av[0], &end, 0); 88518334Speter if (*end == 'K' || *end == 'k') { 88618334Speter fs->flags |= DN_QSIZE_BYTES; 88718334Speter fs->qsize *= 1024; 88818334Speter } else if (*end == 'B' || 88918334Speter _substrcmp2(end, "by", "bytes") == 0) { 89052284Sobrien fs->flags |= DN_QSIZE_BYTES; 89152284Sobrien } 89252284Sobrien ac--; av++; 89352284Sobrien break; 89452284Sobrien 89552284Sobrien case TOK_BUCKETS: 89618334Speter NEED(fs, "buckets is only for pipes or flowsets"); 897169689Skan NEED1("buckets needs argument\n"); 89818334Speter *buckets = strtoul(av[0], NULL, 0); 899169689Skan ac--; av++; 90018334Speter break; 90118334Speter 90218334Speter case TOK_FLOW_MASK: 90318334Speter case TOK_SCHED_MASK: 904169689Skan case TOK_MASK: 90518334Speter NEED(mask, "tok_mask"); 90618334Speter NEED1("mask needs mask specifier\n"); 90718334Speter /* 90818334Speter * per-flow queue, mask is dst_ip, dst_port, 90918334Speter * src_ip, src_port, proto measured in bits 91018334Speter */ 91118334Speter 91218334Speter bzero(mask, sizeof(*mask)); 91318334Speter end = NULL; 91418334Speter 91518334Speter while (ac >= 1) { 91618334Speter uint32_t *p32 = NULL; 91718334Speter uint16_t *p16 = NULL; 91818334Speter uint32_t *p20 = NULL; 91950397Sobrien struct in6_addr *pa6 = NULL; 92050397Sobrien uint32_t a; 92150397Sobrien 92218334Speter tok = match_token(dummynet_params, *av); 92318334Speter ac--; av++; 92418334Speter switch(tok) { 92518334Speter case TOK_ALL: 926132718Skan /* 92718334Speter * special case, all bits significant 92818334Speter * except 'extra' (the queue number) 92918334Speter */ 93090075Sobrien mask->dst_ip = ~0; 931132718Skan mask->src_ip = ~0; 93218334Speter mask->dst_port = ~0; 93318334Speter mask->src_port = ~0; 93418334Speter mask->proto = ~0; 93518334Speter n2mask(&mask->dst_ip6, 128); 93618334Speter n2mask(&mask->src_ip6, 128); 93718334Speter mask->flow_id6 = ~0; 938169689Skan *flags |= DN_HAVE_MASK; 939169689Skan goto end_mask; 940169689Skan 94118334Speter case TOK_QUEUE: 94218334Speter mask->extra = ~0; 94318334Speter *flags |= DN_HAVE_MASK; 94418334Speter goto end_mask; 94518334Speter 94618334Speter case TOK_DSTIP: 94718334Speter mask->addr_type = 4; 94818334Speter p32 = &mask->dst_ip; 94918334Speter break; 95018334Speter 95118334Speter case TOK_SRCIP: 95218334Speter mask->addr_type = 4; 95318334Speter p32 = &mask->src_ip; 95418334Speter break; 955132718Skan 95618334Speter case TOK_DSTIP6: 95718334Speter mask->addr_type = 6; 95850397Sobrien pa6 = &mask->dst_ip6; 95918334Speter break; 96018334Speter 961132718Skan case TOK_SRCIP6: 96218334Speter mask->addr_type = 6; 96390075Sobrien pa6 = &mask->src_ip6; 96418334Speter break; 96518334Speter 96618334Speter case TOK_FLOWID: 967132718Skan mask->addr_type = 6; 96818334Speter p20 = &mask->flow_id6; 96990075Sobrien break; 97018334Speter 97118334Speter case TOK_DSTPORT: 97218334Speter p16 = &mask->dst_port; 97318334Speter break; 97418334Speter 97518334Speter case TOK_SRCPORT: 97618334Speter p16 = &mask->src_port; 97718334Speter break; 97890075Sobrien 97918334Speter case TOK_PROTO: 98018334Speter break; 98118334Speter 98218334Speter default: 98318334Speter ac++; av--; /* backtrack */ 98418334Speter goto end_mask; 98518334Speter } 98618334Speter if (ac < 1) 987132718Skan errx(EX_USAGE, "mask: value missing"); 98818334Speter if (*av[0] == '/') { 98918334Speter a = strtoul(av[0]+1, &end, 0); 99018334Speter if (pa6 == NULL) 99118334Speter a = (a == 32) ? ~0 : (1 << a) - 1; 992132718Skan } else 99318334Speter a = strtoul(av[0], &end, 0); 99490075Sobrien if (p32 != NULL) 99518334Speter *p32 = a; 99618334Speter else if (p16 != NULL) { 99718334Speter if (a > 0xFFFF) 99818334Speter errx(EX_DATAERR, 99918334Speter "port mask must be 16 bit"); 100018334Speter *p16 = (uint16_t)a; 100118334Speter } else if (p20 != NULL) { 100218334Speter if (a > 0xfffff) 100318334Speter errx(EX_DATAERR, 100418334Speter "flow_id mask must be 20 bit"); 1005132718Skan *p20 = (uint32_t)a; 100618334Speter } else if (pa6 != NULL) { 100718334Speter if (a > 128) 100818334Speter errx(EX_DATAERR, 100918334Speter "in6addr invalid mask len"); 101018334Speter else 101118334Speter n2mask(pa6, a); 101218334Speter } else { 101318334Speter if (a > 0xFF) 101418334Speter errx(EX_DATAERR, 101518334Speter "proto mask must be 8 bit"); 101618334Speter mask->proto = (uint8_t)a; 101718334Speter } 101818334Speter if (a != 0) 1019132718Skan *flags |= DN_HAVE_MASK; 102018334Speter ac--; av++; 102118334Speter } /* end while, config masks */ 102250397Sobrienend_mask: 102318334Speter break; 102490075Sobrien 102590075Sobrien case TOK_RED: 102652284Sobrien case TOK_GRED: 102790075Sobrien NEED1("red/gred needs w_q/min_th/max_th/max_p\n"); 102890075Sobrien fs->flags |= DN_IS_RED; 102990075Sobrien if (tok == TOK_GRED) 103090075Sobrien fs->flags |= DN_IS_GENTLE_RED; 103152284Sobrien /* 103252284Sobrien * the format for parameters is w_q/min_th/max_th/max_p 103352284Sobrien */ 103452284Sobrien if ((end = strsep(&av[0], "/"))) { 103552284Sobrien double w_q = strtod(end, NULL); 103652284Sobrien if (w_q > 1 || w_q <= 0) 103718334Speter errx(EX_DATAERR, "0 < w_q <= 1"); 103818334Speter fs->w_q = (int) (w_q * (1 << SCALE_RED)); 103918334Speter } 104018334Speter if ((end = strsep(&av[0], "/"))) { 104118334Speter fs->min_th = strtoul(end, &end, 0); 104218334Speter if (*end == 'K' || *end == 'k') 104318334Speter fs->min_th *= 1024; 104418334Speter } 104518334Speter if ((end = strsep(&av[0], "/"))) { 104618334Speter fs->max_th = strtoul(end, &end, 0); 1047132718Skan if (*end == 'K' || *end == 'k') 104818334Speter fs->max_th *= 1024; 104918334Speter } 105018334Speter if ((end = strsep(&av[0], "/"))) { 105118334Speter double max_p = strtod(end, NULL); 105218334Speter if (max_p > 1 || max_p < 0) 1053169689Skan errx(EX_DATAERR, "0 <= max_p <= 1"); 105418334Speter fs->max_p = (int)(max_p * (1 << SCALE_RED)); 105518334Speter } 105618334Speter ac--; av++; 105718334Speter break; 105818334Speter 105918334Speter case TOK_ECN: 106018334Speter fs->flags |= DN_IS_ECN; 106118334Speter break; 106218334Speter 106318334Speter case TOK_DROPTAIL: 106418334Speter NEED(fs, "droptail is only for flowsets"); 106518334Speter fs->flags &= ~(DN_IS_RED|DN_IS_GENTLE_RED); 106618334Speter break; 106718334Speter 106890075Sobrien case TOK_BW: 106918334Speter NEED(p, "bw is only for links"); 107018334Speter NEED1("bw needs bandwidth or interface\n"); 107118334Speter read_bandwidth(av[0], &p->bandwidth, NULL, 0); 1072132718Skan ac--; av++; 107318334Speter break; 107418334Speter 107518334Speter case TOK_DELAY: 1076132718Skan NEED(p, "delay is only for links"); 107718334Speter NEED1("delay needs argument 0..10000ms\n"); 107818334Speter p->delay = strtoul(av[0], NULL, 0); 107918334Speter ac--; av++; 108018334Speter break; 108118334Speter 108218334Speter case TOK_TYPE: { 108318334Speter int l; 108418334Speter NEED(sch, "type is only for schedulers"); 108518334Speter NEED1("type needs a string"); 108618334Speter l = strlen(av[0]); 108718334Speter if (l == 0 || l > 15) 108818334Speter errx(1, "type %s too long\n", av[0]); 108918334Speter strcpy(sch->name, av[0]); 109018334Speter sch->oid.subtype = 0; /* use string */ 109118334Speter ac--; av++; 109218334Speter break; 109318334Speter } 109418334Speter 109518334Speter case TOK_WEIGHT: 109618334Speter NEED(fs, "weight is only for flowsets"); 109718334Speter NEED1("weight needs argument\n"); 109818334Speter fs->par[0] = strtol(av[0], &end, 0); 109918334Speter ac--; av++; 110018334Speter break; 110118334Speter 110290075Sobrien case TOK_LMAX: 110318334Speter NEED(fs, "lmax is only for flowsets"); 110418334Speter NEED1("lmax needs argument\n"); 110518334Speter fs->par[1] = strtol(av[0], &end, 0); 110618334Speter ac--; av++; 110718334Speter break; 110818334Speter 110918334Speter case TOK_PRI: 111018334Speter NEED(fs, "priority is only for flowsets"); 111118334Speter NEED1("priority needs argument\n"); 111218334Speter fs->par[2] = strtol(av[0], &end, 0); 111318334Speter ac--; av++; 111418334Speter break; 111518334Speter 111618334Speter case TOK_SCHED: 111718334Speter case TOK_PIPE: 111818334Speter NEED(fs, "pipe/sched"); 111918334Speter NEED1("pipe/link/sched needs number\n"); 112018334Speter fs->sched_nr = strtoul(av[0], &end, 0); 112118334Speter ac--; av++; 112218334Speter break; 112318334Speter 112418334Speter case TOK_PROFILE: 112518334Speter NEED((!pf), "profile already set"); 112618334Speter NEED(p, "profile"); 112718334Speter { 112818334Speter NEED1("extra delay needs the file name\n"); 112918334Speter pf = o_next(&buf, sizeof(*pf), DN_PROFILE); 113050397Sobrien load_extra_delays(av[0], pf, p); //XXX can't fail? 113150397Sobrien --ac; ++av; 113250397Sobrien } 113318334Speter break; 113418334Speter 113518334Speter case TOK_BURST: 113618334Speter NEED(p, "burst"); 113718334Speter NEED1("burst needs argument\n"); 113818334Speter errno = 0; 113918334Speter if (expand_number(av[0], &p->burst) < 0) 114090075Sobrien if (errno != ERANGE) 114152284Sobrien errx(EX_DATAERR, 114252284Sobrien "burst: invalid argument"); 1143169689Skan if (errno || p->burst > (1ULL << 48) - 1) 114418334Speter errx(EX_DATAERR, 1145169689Skan "burst: out of range (0..2^48-1)"); 1146169689Skan ac--; av++; 1147169689Skan break; 1148169689Skan 1149169689Skan default: 115018334Speter errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]); 115152284Sobrien } 115252284Sobrien } 1153169689Skan 115452284Sobrien /* check validity of parameters */ 115552284Sobrien if (p) { 115652284Sobrien if (p->delay > 10000) 115752284Sobrien errx(EX_DATAERR, "delay must be < 10000"); 115852284Sobrien if (p->bandwidth == -1) 115952284Sobrien p->bandwidth = 0; 116052284Sobrien } 116152284Sobrien if (fs) { 116252284Sobrien /* XXX accept a 0 scheduler to keep the default */ 116352284Sobrien if (fs->flags & DN_QSIZE_BYTES) { 116452284Sobrien size_t len; 116552284Sobrien long limit; 116652284Sobrien 116752284Sobrien len = sizeof(limit); 116852284Sobrien if (sysctlbyname("net.inet.ip.dummynet.pipe_byte_limit", 116990075Sobrien &limit, &len, NULL, 0) == -1) 117052284Sobrien limit = 1024*1024; 117152284Sobrien if (fs->qsize > limit) 117252284Sobrien errx(EX_DATAERR, "queue size must be < %ldB", limit); 117318334Speter } else { 117418334Speter size_t len; 117518334Speter long limit; 1176132718Skan 117718334Speter len = sizeof(limit); 117818334Speter if (sysctlbyname("net.inet.ip.dummynet.pipe_slot_limit", 117990075Sobrien &limit, &len, NULL, 0) == -1) 1180169689Skan limit = 100; 118190075Sobrien if (fs->qsize > limit) 118290075Sobrien errx(EX_DATAERR, "2 <= queue size <= %ld", limit); 118390075Sobrien } 118490075Sobrien 118590075Sobrien if ((fs->flags & DN_IS_ECN) && !(fs->flags & DN_IS_RED)) 118690075Sobrien errx(EX_USAGE, "enable red/gred for ECN"); 118790075Sobrien 118890075Sobrien if (fs->flags & DN_IS_RED) { 118990075Sobrien size_t len; 119018334Speter int lookup_depth, avg_pkt_size; 119118334Speter 119218334Speter if (!(fs->flags & DN_IS_ECN) && (fs->min_th >= fs->max_th)) 119318334Speter errx(EX_DATAERR, "min_th %d must be < than max_th %d", 119418334Speter fs->min_th, fs->max_th); 119518334Speter else if ((fs->flags & DN_IS_ECN) && (fs->min_th > fs->max_th)) 119618334Speter errx(EX_DATAERR, "min_th %d must be =< than max_th %d", 119718334Speter fs->min_th, fs->max_th); 119818334Speter 119990075Sobrien if (fs->max_th == 0) 120090075Sobrien errx(EX_DATAERR, "max_th must be > 0"); 120190075Sobrien 120290075Sobrien len = sizeof(int); 120318334Speter if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth", 120490075Sobrien &lookup_depth, &len, NULL, 0) == -1) 120518334Speter lookup_depth = 256; 120690075Sobrien if (lookup_depth == 0) 120718334Speter errx(EX_DATAERR, "net.inet.ip.dummynet.red_lookup_depth" 120818334Speter " must be greater than zero"); 120918334Speter 121090075Sobrien len = sizeof(int); 121118334Speter if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size", 121218334Speter &avg_pkt_size, &len, NULL, 0) == -1) 121390075Sobrien avg_pkt_size = 512; 121418334Speter 121518334Speter if (avg_pkt_size == 0) 121618334Speter errx(EX_DATAERR, 121790075Sobrien "net.inet.ip.dummynet.red_avg_pkt_size must" 121818334Speter " be greater than zero"); 121918334Speter 122018334Speter#if 0 /* the following computation is now done in the kernel */ 122118334Speter /* 122218334Speter * Ticks needed for sending a medium-sized packet. 1223132718Skan * Unfortunately, when we are configuring a WF2Q+ queue, we 122418334Speter * do not have bandwidth information, because that is stored 122518334Speter * in the parent pipe, and also we have multiple queues 122618334Speter * competing for it. So we set s=0, which is not very 122718334Speter * correct. But on the other hand, why do we want RED with 122818334Speter * WF2Q+ ? 122918334Speter */ 123018334Speter if (p.bandwidth==0) /* this is a WF2Q+ queue */ 123118334Speter s = 0; 123250397Sobrien else 123350397Sobrien s = (double)ck.hz * avg_pkt_size * 8 / p.bandwidth; 123450397Sobrien /* 123518334Speter * max idle time (in ticks) before avg queue size becomes 0. 123618334Speter * NOTA: (3/w_q) is approx the value x so that 123718334Speter * (1-w_q)^x < 10^-3. 1238132718Skan */ 123918334Speter w_q = ((double)fs->w_q) / (1 << SCALE_RED); 124018334Speter idle = s * 3. / w_q; 124118334Speter fs->lookup_step = (int)idle / lookup_depth; 124218334Speter if (!fs->lookup_step) 124318334Speter fs->lookup_step = 1; 124418334Speter weight = 1 - w_q; 124518334Speter for (t = fs->lookup_step; t > 1; --t) 124618334Speter weight *= 1 - w_q; 124718334Speter fs->lookup_weight = (int)(weight * (1 << SCALE_RED)); 124818334Speter#endif /* code moved in the kernel */ 124918334Speter } 125018334Speter } 125118334Speter 1252132718Skan i = do_cmd(IP_DUMMYNET3, base, (char *)buf - (char *)base); 125318334Speter 125418334Speter if (i) 125518334Speter err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE"); 125618334Speter} 125718334Speter 125890075Sobrienvoid 125918334Speterdummynet_flush(void) 126018334Speter{ 126118334Speter struct dn_id oid; 126218334Speter oid_fill(&oid, sizeof(oid), DN_CMD_FLUSH, DN_API_VERSION); 126318334Speter do_cmd(IP_DUMMYNET3, &oid, oid.len); 126418334Speter} 126550397Sobrien 126650397Sobrien/* Parse input for 'ipfw [pipe|sched|queue] show [range list]' 126790075Sobrien * Returns the number of ranges, and possibly stores them 126890075Sobrien * in the array v of size len. 126918334Speter */ 127018334Speterstatic int 127118334Speterparse_range(int ac, char *av[], uint32_t *v, int len) 127218334Speter{ 127318334Speter int n = 0; 127418334Speter char *endptr, *s; 127518334Speter uint32_t base[2]; 127618334Speter 127718334Speter if (v == NULL || len < 2) { 127818334Speter v = base; 127918334Speter len = 2; 128018334Speter } 128118334Speter 128218334Speter for (s = *av; s != NULL; av++, ac--) { 128318334Speter v[0] = strtoul(s, &endptr, 10); 128418334Speter v[1] = (*endptr != '-') ? v[0] : 1285169689Skan strtoul(endptr+1, &endptr, 10); 128618334Speter if (*endptr == '\0') { /* prepare for next round */ 128718334Speter s = (ac > 0) ? *(av+1) : NULL; 128818334Speter } else { 128918334Speter if (*endptr != ',') { 129018334Speter warn("invalid number: %s", s); 129118334Speter s = ++endptr; 129218334Speter continue; 129318334Speter } 129418334Speter /* continue processing from here */ 129518334Speter s = ++endptr; 129618334Speter ac++; 129718334Speter av--; 129818334Speter } 129918334Speter if (v[1] < v[0] || 130018334Speter v[1] >= DN_MAX_ID-1 || 130118334Speter v[1] >= DN_MAX_ID-1) { 130218334Speter continue; /* invalid entry */ 130318334Speter } 1304169689Skan n++; 130518334Speter /* translate if 'pipe list' */ 130618334Speter if (co.do_pipe == 1) { 130718334Speter v[0] += DN_MAX_ID; 130818334Speter v[1] += DN_MAX_ID; 130918334Speter } 131018334Speter v = (n*2 < len) ? v + 2 : base; 131118334Speter } 131218334Speter return n; 131318334Speter} 131418334Speter 131518334Speter/* main entry point for dummynet list functions. co.do_pipe indicates 131618334Speter * which function we want to support. 131718334Speter * av may contain filtering arguments, either individual entries 131818334Speter * or ranges, or lists (space or commas are valid separators). 131918334Speter * Format for a range can be n1-n2 or n3 n4 n5 ... 132018334Speter * In a range n1 must be <= n2, otherwise the range is ignored. 132118334Speter * A number 'n4' is translate in a range 'n4-n4' 132218334Speter * All number must be > 0 and < DN_MAX_ID-1 132318334Speter */ 1324169689Skanvoid 132518334Speterdummynet_list(int ac, char *av[], int show_counters) 132618334Speter{ 132718334Speter struct dn_id *oid, *x = NULL; 132818334Speter int ret, i; 132918334Speter int n; /* # of ranges */ 133018334Speter u_int buflen, l; 133118334Speter u_int max_size; /* largest obj passed up */ 133218334Speter 133390075Sobrien (void)show_counters; // XXX unused, but we should use it. 133418334Speter ac--; 133518334Speter av++; /* skip 'list' | 'show' word */ 133618334Speter 133718334Speter n = parse_range(ac, av, NULL, 0); /* Count # of ranges. */ 133818334Speter 133918334Speter /* Allocate space to store ranges */ 134018334Speter l = sizeof(*oid) + sizeof(uint32_t) * n * 2; 134118334Speter oid = safe_calloc(1, l); 134218334Speter oid_fill(oid, l, DN_CMD_GET, DN_API_VERSION); 134350397Sobrien 134418334Speter if (n > 0) /* store ranges in idx */ 134518334Speter parse_range(ac, av, (uint32_t *)(oid + 1), n*2); 1346169689Skan /* 134718334Speter * Compute the size of the largest object returned. If the 134818334Speter * response leaves at least this much spare space in the 134918334Speter * buffer, then surely the response is complete; otherwise 135018334Speter * there might be a risk of truncation and we will need to 135118334Speter * retry with a larger buffer. 135218334Speter * XXX don't bother with smaller structs. 135318334Speter */ 135418334Speter max_size = sizeof(struct dn_fs); 1355169689Skan if (max_size < sizeof(struct dn_sch)) 1356169689Skan max_size = sizeof(struct dn_sch); 135718334Speter if (max_size < sizeof(struct dn_flow)) 135818334Speter max_size = sizeof(struct dn_flow); 135918334Speter 136018334Speter switch (co.do_pipe) { 136118334Speter case 1: 136218334Speter oid->subtype = DN_LINK; /* list pipe */ 136318334Speter break; 136418334Speter case 2: 136518334Speter oid->subtype = DN_FS; /* list queue */ 136650397Sobrien break; 136718334Speter case 3: 136818334Speter oid->subtype = DN_SCH; /* list sched */ 136918334Speter break; 137018334Speter } 137118334Speter 137218334Speter /* 137318334Speter * Ask the kernel an estimate of the required space (result 137418334Speter * in oid.id), unless we are requesting a subset of objects, 137518334Speter * in which case the kernel does not give an exact answer. 137618334Speter * In any case, space might grow in the meantime due to the 137718334Speter * creation of new queues, so we must be prepared to retry. 137818334Speter */ 137918334Speter if (n > 0) { 138018334Speter buflen = 4*1024; 138118334Speter } else { 138218334Speter ret = do_cmd(-IP_DUMMYNET3, oid, (uintptr_t)&l); 138318334Speter if (ret != 0 || oid->id <= sizeof(*oid)) 1384132718Skan goto done; 138518334Speter buflen = oid->id + max_size; 138618334Speter oid->len = sizeof(*oid); /* restore */ 138718334Speter } 138818334Speter /* Try a few times, until the buffer fits */ 138990075Sobrien for (i = 0; i < 20; i++) { 139018334Speter l = buflen; 139118334Speter x = safe_realloc(x, l); 139218334Speter bcopy(oid, x, oid->len); 1393169689Skan ret = do_cmd(-IP_DUMMYNET3, x, (uintptr_t)&l); 139418334Speter if (ret != 0 || x->id <= sizeof(*oid)) 139518334Speter goto done; /* no response */ 139618334Speter if (l + max_size <= buflen) 139718334Speter break; /* ok */ 139818334Speter buflen *= 2; /* double for next attempt */ 139918334Speter } 140018334Speter list_pipes(x, O_NEXT(x, l)); 140118334Speterdone: 140250397Sobrien if (x) 140318334Speter free(x); 140418334Speter free(oid); 140518334Speter} 1406169689Skan