14e9c8ae7SLuigi Rizzo /* 2cc4d3c30SLuigi Rizzo * Copyright (c) 2002-2003,2010 Luigi Rizzo 34e9c8ae7SLuigi Rizzo * 44e9c8ae7SLuigi Rizzo * Redistribution and use in source forms, with and without modification, 54e9c8ae7SLuigi Rizzo * are permitted provided that this entire comment appears intact. 64e9c8ae7SLuigi Rizzo * 74e9c8ae7SLuigi Rizzo * Redistribution in binary form may occur without any restrictions. 84e9c8ae7SLuigi Rizzo * Obviously, it would be nice if you gave credit where credit is due 94e9c8ae7SLuigi Rizzo * but requiring it would be too onerous. 104e9c8ae7SLuigi Rizzo * 114e9c8ae7SLuigi Rizzo * This software is provided ``AS IS'' without any warranties of any kind. 124e9c8ae7SLuigi Rizzo * 134e9c8ae7SLuigi Rizzo * $FreeBSD$ 144e9c8ae7SLuigi Rizzo * 154e9c8ae7SLuigi Rizzo * dummynet support 164e9c8ae7SLuigi Rizzo */ 174e9c8ae7SLuigi Rizzo 184e9c8ae7SLuigi Rizzo #include <sys/types.h> 194e9c8ae7SLuigi Rizzo #include <sys/socket.h> 2023c608c8SLuigi Rizzo /* XXX there are several sysctl leftover here */ 214e9c8ae7SLuigi Rizzo #include <sys/sysctl.h> 224e9c8ae7SLuigi Rizzo 234e9c8ae7SLuigi Rizzo #include "ipfw2.h" 244e9c8ae7SLuigi Rizzo 254e9c8ae7SLuigi Rizzo #include <ctype.h> 264e9c8ae7SLuigi Rizzo #include <err.h> 276882bf4dSOleg Bulyzhin #include <errno.h> 286882bf4dSOleg Bulyzhin #include <libutil.h> 294e9c8ae7SLuigi Rizzo #include <netdb.h> 304e9c8ae7SLuigi Rizzo #include <stdio.h> 314e9c8ae7SLuigi Rizzo #include <stdlib.h> 324e9c8ae7SLuigi Rizzo #include <string.h> 334e9c8ae7SLuigi Rizzo #include <sysexits.h> 344e9c8ae7SLuigi Rizzo 354e9c8ae7SLuigi Rizzo #include <net/if.h> 364e9c8ae7SLuigi Rizzo #include <netinet/in.h> 374e9c8ae7SLuigi Rizzo #include <netinet/ip_fw.h> 384e9c8ae7SLuigi Rizzo #include <netinet/ip_dummynet.h> 3923c608c8SLuigi Rizzo #include <arpa/inet.h> /* inet_ntoa */ 404e9c8ae7SLuigi Rizzo 41cc4d3c30SLuigi Rizzo 424e9c8ae7SLuigi Rizzo static struct _s_x dummynet_params[] = { 434e9c8ae7SLuigi Rizzo { "plr", TOK_PLR }, 444e9c8ae7SLuigi Rizzo { "noerror", TOK_NOERROR }, 454e9c8ae7SLuigi Rizzo { "buckets", TOK_BUCKETS }, 464e9c8ae7SLuigi Rizzo { "dst-ip", TOK_DSTIP }, 474e9c8ae7SLuigi Rizzo { "src-ip", TOK_SRCIP }, 484e9c8ae7SLuigi Rizzo { "dst-port", TOK_DSTPORT }, 494e9c8ae7SLuigi Rizzo { "src-port", TOK_SRCPORT }, 504e9c8ae7SLuigi Rizzo { "proto", TOK_PROTO }, 514e9c8ae7SLuigi Rizzo { "weight", TOK_WEIGHT }, 52cc4d3c30SLuigi Rizzo { "lmax", TOK_LMAX }, 53cc4d3c30SLuigi Rizzo { "maxlen", TOK_LMAX }, 544e9c8ae7SLuigi Rizzo { "all", TOK_ALL }, 55cc4d3c30SLuigi Rizzo { "mask", TOK_MASK }, /* alias for both */ 56cc4d3c30SLuigi Rizzo { "sched_mask", TOK_SCHED_MASK }, 57cc4d3c30SLuigi Rizzo { "flow_mask", TOK_FLOW_MASK }, 584e9c8ae7SLuigi Rizzo { "droptail", TOK_DROPTAIL }, 594e9c8ae7SLuigi Rizzo { "red", TOK_RED }, 604e9c8ae7SLuigi Rizzo { "gred", TOK_GRED }, 614e9c8ae7SLuigi Rizzo { "bw", TOK_BW }, 624e9c8ae7SLuigi Rizzo { "bandwidth", TOK_BW }, 634e9c8ae7SLuigi Rizzo { "delay", TOK_DELAY }, 64cc4d3c30SLuigi Rizzo { "link", TOK_LINK }, 654e9c8ae7SLuigi Rizzo { "pipe", TOK_PIPE }, 664e9c8ae7SLuigi Rizzo { "queue", TOK_QUEUE }, 67cc4d3c30SLuigi Rizzo { "flowset", TOK_FLOWSET }, 68cc4d3c30SLuigi Rizzo { "sched", TOK_SCHED }, 69cc4d3c30SLuigi Rizzo { "pri", TOK_PRI }, 70cc4d3c30SLuigi Rizzo { "priority", TOK_PRI }, 71cc4d3c30SLuigi Rizzo { "type", TOK_TYPE }, 724e9c8ae7SLuigi Rizzo { "flow-id", TOK_FLOWID}, 734e9c8ae7SLuigi Rizzo { "dst-ipv6", TOK_DSTIP6}, 744e9c8ae7SLuigi Rizzo { "dst-ip6", TOK_DSTIP6}, 754e9c8ae7SLuigi Rizzo { "src-ipv6", TOK_SRCIP6}, 764e9c8ae7SLuigi Rizzo { "src-ip6", TOK_SRCIP6}, 77cc4d3c30SLuigi Rizzo { "profile", TOK_PROFILE}, 786882bf4dSOleg Bulyzhin { "burst", TOK_BURST}, 794e9c8ae7SLuigi Rizzo { "dummynet-params", TOK_NULL }, 804e9c8ae7SLuigi Rizzo { NULL, 0 } /* terminator */ 814e9c8ae7SLuigi Rizzo }; 824e9c8ae7SLuigi Rizzo 83cc4d3c30SLuigi Rizzo #define O_NEXT(p, len) ((void *)((char *)p + len)) 84cc4d3c30SLuigi Rizzo 85cc4d3c30SLuigi Rizzo static void 86cc4d3c30SLuigi Rizzo oid_fill(struct dn_id *oid, int len, int type, uintptr_t id) 87cc4d3c30SLuigi Rizzo { 88cc4d3c30SLuigi Rizzo oid->len = len; 89cc4d3c30SLuigi Rizzo oid->type = type; 90cc4d3c30SLuigi Rizzo oid->subtype = 0; 91cc4d3c30SLuigi Rizzo oid->id = id; 92cc4d3c30SLuigi Rizzo } 93cc4d3c30SLuigi Rizzo 94cc4d3c30SLuigi Rizzo /* make room in the buffer and move the pointer forward */ 95cc4d3c30SLuigi Rizzo static void * 96cc4d3c30SLuigi Rizzo o_next(struct dn_id **o, int len, int type) 97cc4d3c30SLuigi Rizzo { 98cc4d3c30SLuigi Rizzo struct dn_id *ret = *o; 99cc4d3c30SLuigi Rizzo oid_fill(ret, len, type, 0); 100cc4d3c30SLuigi Rizzo *o = O_NEXT(*o, len); 101cc4d3c30SLuigi Rizzo return ret; 102cc4d3c30SLuigi Rizzo } 103cc4d3c30SLuigi Rizzo 104cc4d3c30SLuigi Rizzo #if 0 1054e9c8ae7SLuigi Rizzo static int 10601ab7632SLuigi Rizzo sort_q(void *arg, const void *pa, const void *pb) 1074e9c8ae7SLuigi Rizzo { 1084e9c8ae7SLuigi Rizzo int rev = (co.do_sort < 0); 1094e9c8ae7SLuigi Rizzo int field = rev ? -co.do_sort : co.do_sort; 1104e9c8ae7SLuigi Rizzo long long res = 0; 1114e9c8ae7SLuigi Rizzo const struct dn_flow_queue *a = pa; 1124e9c8ae7SLuigi Rizzo const struct dn_flow_queue *b = pb; 1134e9c8ae7SLuigi Rizzo 1144e9c8ae7SLuigi Rizzo switch (field) { 1154e9c8ae7SLuigi Rizzo case 1: /* pkts */ 1164e9c8ae7SLuigi Rizzo res = a->len - b->len; 1174e9c8ae7SLuigi Rizzo break; 1184e9c8ae7SLuigi Rizzo case 2: /* bytes */ 1194e9c8ae7SLuigi Rizzo res = a->len_bytes - b->len_bytes; 1204e9c8ae7SLuigi Rizzo break; 1214e9c8ae7SLuigi Rizzo 1224e9c8ae7SLuigi Rizzo case 3: /* tot pkts */ 1234e9c8ae7SLuigi Rizzo res = a->tot_pkts - b->tot_pkts; 1244e9c8ae7SLuigi Rizzo break; 1254e9c8ae7SLuigi Rizzo 1264e9c8ae7SLuigi Rizzo case 4: /* tot bytes */ 1274e9c8ae7SLuigi Rizzo res = a->tot_bytes - b->tot_bytes; 1284e9c8ae7SLuigi Rizzo break; 1294e9c8ae7SLuigi Rizzo } 1304e9c8ae7SLuigi Rizzo if (res < 0) 1314e9c8ae7SLuigi Rizzo res = -1; 1324e9c8ae7SLuigi Rizzo if (res > 0) 1334e9c8ae7SLuigi Rizzo res = 1; 1344e9c8ae7SLuigi Rizzo return (int)(rev ? res : -res); 1354e9c8ae7SLuigi Rizzo } 136cc4d3c30SLuigi Rizzo #endif 1374e9c8ae7SLuigi Rizzo 138cc4d3c30SLuigi Rizzo /* print a mask and header for the subsequent list of flows */ 1394e9c8ae7SLuigi Rizzo static void 140cc4d3c30SLuigi Rizzo print_mask(struct ipfw_flow_id *id) 1414e9c8ae7SLuigi Rizzo { 142cc4d3c30SLuigi Rizzo if (!IS_IP6_FLOW_ID(id)) { 1434e9c8ae7SLuigi Rizzo printf(" " 144f9f7bde3SLuigi Rizzo "mask: %s 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n", 145f9f7bde3SLuigi Rizzo id->extra ? "queue," : "", 146cc4d3c30SLuigi Rizzo id->proto, 147cc4d3c30SLuigi Rizzo id->src_ip, id->src_port, 148cc4d3c30SLuigi Rizzo id->dst_ip, id->dst_port); 1494e9c8ae7SLuigi Rizzo 1504e9c8ae7SLuigi Rizzo printf("BKT Prot ___Source IP/port____ " 1514e9c8ae7SLuigi Rizzo "____Dest. IP/port____ " 1524e9c8ae7SLuigi Rizzo "Tot_pkt/bytes Pkt/Byte Drp\n"); 153cc4d3c30SLuigi Rizzo } else { 154cc4d3c30SLuigi Rizzo char buf[255]; 155f9f7bde3SLuigi Rizzo printf("\n mask: %sproto: 0x%02x, flow_id: 0x%08x, ", 156f9f7bde3SLuigi Rizzo id->extra ? "queue," : "", 157cc4d3c30SLuigi Rizzo id->proto, id->flow_id6); 158cc4d3c30SLuigi Rizzo inet_ntop(AF_INET6, &(id->src_ip6), buf, sizeof(buf)); 159cc4d3c30SLuigi Rizzo printf("%s/0x%04x -> ", buf, id->src_port); 160cc4d3c30SLuigi Rizzo inet_ntop(AF_INET6, &(id->dst_ip6), buf, sizeof(buf)); 161cc4d3c30SLuigi Rizzo printf("%s/0x%04x\n", buf, id->dst_port); 1624e9c8ae7SLuigi Rizzo 1634e9c8ae7SLuigi Rizzo printf("BKT ___Prot___ _flow-id_ " 1644e9c8ae7SLuigi Rizzo "______________Source IPv6/port_______________ " 1654e9c8ae7SLuigi Rizzo "_______________Dest. IPv6/port_______________ " 1664e9c8ae7SLuigi Rizzo "Tot_pkt/bytes Pkt/Byte Drp\n"); 1674e9c8ae7SLuigi Rizzo } 1684e9c8ae7SLuigi Rizzo } 1694e9c8ae7SLuigi Rizzo 1704e9c8ae7SLuigi Rizzo static void 171cc4d3c30SLuigi Rizzo list_flow(struct dn_flow *ni) 172cc4d3c30SLuigi Rizzo { 173cc4d3c30SLuigi Rizzo char buff[255]; 174cc4d3c30SLuigi Rizzo struct protoent *pe; 175cc4d3c30SLuigi Rizzo struct in_addr ina; 176cc4d3c30SLuigi Rizzo struct ipfw_flow_id *id = &ni->fid; 177cc4d3c30SLuigi Rizzo 178cc4d3c30SLuigi Rizzo pe = getprotobynumber(id->proto); 179cc4d3c30SLuigi Rizzo /* XXX: Should check for IPv4 flows */ 180f9f7bde3SLuigi Rizzo printf("%3u%c", (ni->oid.id) & 0xff, 181f9f7bde3SLuigi Rizzo id->extra ? '*' : ' '); 182cc4d3c30SLuigi Rizzo if (!IS_IP6_FLOW_ID(id)) { 183cc4d3c30SLuigi Rizzo if (pe) 184cc4d3c30SLuigi Rizzo printf("%-4s ", pe->p_name); 185cc4d3c30SLuigi Rizzo else 186cc4d3c30SLuigi Rizzo printf("%4u ", id->proto); 187cc4d3c30SLuigi Rizzo ina.s_addr = htonl(id->src_ip); 188cc4d3c30SLuigi Rizzo printf("%15s/%-5d ", 189cc4d3c30SLuigi Rizzo inet_ntoa(ina), id->src_port); 190cc4d3c30SLuigi Rizzo ina.s_addr = htonl(id->dst_ip); 191cc4d3c30SLuigi Rizzo printf("%15s/%-5d ", 192cc4d3c30SLuigi Rizzo inet_ntoa(ina), id->dst_port); 193cc4d3c30SLuigi Rizzo } else { 194cc4d3c30SLuigi Rizzo /* Print IPv6 flows */ 195cc4d3c30SLuigi Rizzo if (pe != NULL) 196cc4d3c30SLuigi Rizzo printf("%9s ", pe->p_name); 197cc4d3c30SLuigi Rizzo else 198cc4d3c30SLuigi Rizzo printf("%9u ", id->proto); 199cc4d3c30SLuigi Rizzo printf("%7d %39s/%-5d ", id->flow_id6, 200cc4d3c30SLuigi Rizzo inet_ntop(AF_INET6, &(id->src_ip6), buff, sizeof(buff)), 201cc4d3c30SLuigi Rizzo id->src_port); 202cc4d3c30SLuigi Rizzo printf(" %39s/%-5d ", 203cc4d3c30SLuigi Rizzo inet_ntop(AF_INET6, &(id->dst_ip6), buff, sizeof(buff)), 204cc4d3c30SLuigi Rizzo id->dst_port); 205cc4d3c30SLuigi Rizzo } 206cc4d3c30SLuigi Rizzo printf("%4llu %8llu %2u %4u %3u\n", 207cc4d3c30SLuigi Rizzo align_uint64(&ni->tot_pkts), 208cc4d3c30SLuigi Rizzo align_uint64(&ni->tot_bytes), 209cc4d3c30SLuigi Rizzo ni->length, ni->len_bytes, ni->drops); 210cc4d3c30SLuigi Rizzo } 211cc4d3c30SLuigi Rizzo 212cc4d3c30SLuigi Rizzo static void 213cc4d3c30SLuigi Rizzo print_flowset_parms(struct dn_fs *fs, char *prefix) 2144e9c8ae7SLuigi Rizzo { 2154e9c8ae7SLuigi Rizzo int l; 2164e9c8ae7SLuigi Rizzo char qs[30]; 2174e9c8ae7SLuigi Rizzo char plr[30]; 2184e9c8ae7SLuigi Rizzo char red[90]; /* Display RED parameters */ 2194e9c8ae7SLuigi Rizzo 2204e9c8ae7SLuigi Rizzo l = fs->qsize; 221cc4d3c30SLuigi Rizzo if (fs->flags & DN_QSIZE_BYTES) { 2224e9c8ae7SLuigi Rizzo if (l >= 8192) 2234e9c8ae7SLuigi Rizzo sprintf(qs, "%d KB", l / 1024); 2244e9c8ae7SLuigi Rizzo else 2254e9c8ae7SLuigi Rizzo sprintf(qs, "%d B", l); 2264e9c8ae7SLuigi Rizzo } else 2274e9c8ae7SLuigi Rizzo sprintf(qs, "%3d sl.", l); 2284e9c8ae7SLuigi Rizzo if (fs->plr) 2294e9c8ae7SLuigi Rizzo sprintf(plr, "plr %f", 1.0 * fs->plr / (double)(0x7fffffff)); 2304e9c8ae7SLuigi Rizzo else 2314e9c8ae7SLuigi Rizzo plr[0] = '\0'; 232cc4d3c30SLuigi Rizzo 233cc4d3c30SLuigi Rizzo if (fs->flags & DN_IS_RED) /* RED parameters */ 2344e9c8ae7SLuigi Rizzo sprintf(red, 2354e9c8ae7SLuigi Rizzo "\n\t %cRED w_q %f min_th %d max_th %d max_p %f", 236cc4d3c30SLuigi Rizzo (fs->flags & DN_IS_GENTLE_RED) ? 'G' : ' ', 2374e9c8ae7SLuigi Rizzo 1.0 * fs->w_q / (double)(1 << SCALE_RED), 238cc4d3c30SLuigi Rizzo fs->min_th, 239cc4d3c30SLuigi Rizzo fs->max_th, 2404e9c8ae7SLuigi Rizzo 1.0 * fs->max_p / (double)(1 << SCALE_RED)); 2414e9c8ae7SLuigi Rizzo else 2424e9c8ae7SLuigi Rizzo sprintf(red, "droptail"); 2434e9c8ae7SLuigi Rizzo 244cc4d3c30SLuigi Rizzo if (prefix[0]) { 2454e9c8ae7SLuigi Rizzo printf("%s %s%s %d queues (%d buckets) %s\n", 246cc4d3c30SLuigi Rizzo prefix, qs, plr, fs->oid.id, fs->buckets, red); 247cc4d3c30SLuigi Rizzo prefix[0] = '\0'; 248cc4d3c30SLuigi Rizzo } else { 249cc4d3c30SLuigi Rizzo printf("q%05d %s%s %d flows (%d buckets) sched %d " 250cc4d3c30SLuigi Rizzo "weight %d lmax %d pri %d %s\n", 251cc4d3c30SLuigi Rizzo fs->fs_nr, qs, plr, fs->oid.id, fs->buckets, 252cc4d3c30SLuigi Rizzo fs->sched_nr, fs->par[0], fs->par[1], fs->par[2], red); 253cc4d3c30SLuigi Rizzo if (fs->flags & DN_HAVE_MASK) 254cc4d3c30SLuigi Rizzo print_mask(&fs->flow_mask); 255cc4d3c30SLuigi Rizzo } 2564e9c8ae7SLuigi Rizzo } 2574e9c8ae7SLuigi Rizzo 2584bb7ae9dSLuigi Rizzo static void 259cc4d3c30SLuigi Rizzo print_extra_delay_parms(struct dn_profile *p) 2604bb7ae9dSLuigi Rizzo { 2614bb7ae9dSLuigi Rizzo double loss; 2624bb7ae9dSLuigi Rizzo if (p->samples_no <= 0) 2634bb7ae9dSLuigi Rizzo return; 2644bb7ae9dSLuigi Rizzo 2654bb7ae9dSLuigi Rizzo loss = p->loss_level; 2664bb7ae9dSLuigi Rizzo loss /= p->samples_no; 2676882bf4dSOleg Bulyzhin printf("\t profile: name \"%s\" loss %f samples %d\n", 2686882bf4dSOleg Bulyzhin p->name, loss, p->samples_no); 2694bb7ae9dSLuigi Rizzo } 2704bb7ae9dSLuigi Rizzo 271cc4d3c30SLuigi Rizzo static void 272cc4d3c30SLuigi Rizzo flush_buf(char *buf) 2734e9c8ae7SLuigi Rizzo { 274cc4d3c30SLuigi Rizzo if (buf[0]) 275cc4d3c30SLuigi Rizzo printf("%s\n", buf); 276cc4d3c30SLuigi Rizzo buf[0] = '\0'; 277cc4d3c30SLuigi Rizzo } 2784e9c8ae7SLuigi Rizzo 279cc4d3c30SLuigi Rizzo /* 280cc4d3c30SLuigi Rizzo * generic list routine. We expect objects in a specific order, i.e. 281cc4d3c30SLuigi Rizzo * PIPES AND SCHEDULERS: 282cc4d3c30SLuigi Rizzo * link; scheduler; internal flowset if any; instances 283cc4d3c30SLuigi Rizzo * we can tell a pipe from the number. 284cc4d3c30SLuigi Rizzo * 285cc4d3c30SLuigi Rizzo * FLOWSETS: 286cc4d3c30SLuigi Rizzo * flowset; queues; 287cc4d3c30SLuigi Rizzo * link i (int queue); scheduler i; si(i) { flowsets() : queues } 288cc4d3c30SLuigi Rizzo */ 289cc4d3c30SLuigi Rizzo static void 290cc4d3c30SLuigi Rizzo list_pipes(struct dn_id *oid, struct dn_id *end) 291cc4d3c30SLuigi Rizzo { 292cc4d3c30SLuigi Rizzo char buf[160]; /* pending buffer */ 293cc4d3c30SLuigi Rizzo buf[0] = '\0'; 294cc4d3c30SLuigi Rizzo 295cc4d3c30SLuigi Rizzo for (; oid != end; oid = O_NEXT(oid, oid->len)) { 296cc4d3c30SLuigi Rizzo if (oid->len < sizeof(*oid)) 297cc4d3c30SLuigi Rizzo errx(1, "invalid oid len %d\n", oid->len); 298cc4d3c30SLuigi Rizzo 299cc4d3c30SLuigi Rizzo switch (oid->type) { 300cc4d3c30SLuigi Rizzo default: 301cc4d3c30SLuigi Rizzo flush_buf(buf); 302cc4d3c30SLuigi Rizzo printf("unrecognized object %d size %d\n", oid->type, oid->len); 303cc4d3c30SLuigi Rizzo break; 304cc4d3c30SLuigi Rizzo case DN_TEXT: /* list of attached flowsets */ 305cc4d3c30SLuigi Rizzo { 306cc4d3c30SLuigi Rizzo int i, l; 307cc4d3c30SLuigi Rizzo struct { 308cc4d3c30SLuigi Rizzo struct dn_id id; 309cc4d3c30SLuigi Rizzo uint32_t p[0]; 310cc4d3c30SLuigi Rizzo } *d = (void *)oid; 311cc4d3c30SLuigi Rizzo l = (oid->len - sizeof(*oid))/sizeof(d->p[0]); 312cc4d3c30SLuigi Rizzo if (l == 0) 313cc4d3c30SLuigi Rizzo break; 314cc4d3c30SLuigi Rizzo printf(" Children flowsets: "); 315cc4d3c30SLuigi Rizzo for (i = 0; i < l; i++) 316cc4d3c30SLuigi Rizzo printf("%u ", d->p[i]); 317cc4d3c30SLuigi Rizzo printf("\n"); 318cc4d3c30SLuigi Rizzo break; 319cc4d3c30SLuigi Rizzo } 320cc4d3c30SLuigi Rizzo case DN_CMD_GET: 321cc4d3c30SLuigi Rizzo if (co.verbose) 322cc4d3c30SLuigi Rizzo printf("answer for cmd %d, len %d\n", oid->type, oid->id); 323cc4d3c30SLuigi Rizzo break; 324cc4d3c30SLuigi Rizzo case DN_SCH: { 325cc4d3c30SLuigi Rizzo struct dn_sch *s = (struct dn_sch *)oid; 326cc4d3c30SLuigi Rizzo flush_buf(buf); 327cc4d3c30SLuigi Rizzo printf(" sched %d type %s flags 0x%x %d buckets %d active\n", 328cc4d3c30SLuigi Rizzo s->sched_nr, 329cc4d3c30SLuigi Rizzo s->name, s->flags, s->buckets, s->oid.id); 330cc4d3c30SLuigi Rizzo if (s->flags & DN_HAVE_MASK) 331cc4d3c30SLuigi Rizzo print_mask(&s->sched_mask); 332cc4d3c30SLuigi Rizzo } 333cc4d3c30SLuigi Rizzo break; 334cc4d3c30SLuigi Rizzo 335cc4d3c30SLuigi Rizzo case DN_FLOW: 336cc4d3c30SLuigi Rizzo list_flow((struct dn_flow *)oid); 337cc4d3c30SLuigi Rizzo break; 338cc4d3c30SLuigi Rizzo 339cc4d3c30SLuigi Rizzo case DN_LINK: { 340cc4d3c30SLuigi Rizzo struct dn_link *p = (struct dn_link *)oid; 3414e9c8ae7SLuigi Rizzo double b = p->bandwidth; 342cc4d3c30SLuigi Rizzo char bwbuf[30]; 3436882bf4dSOleg Bulyzhin char burst[5 + 7]; 3444e9c8ae7SLuigi Rizzo 345cc4d3c30SLuigi Rizzo /* This starts a new object so flush buffer */ 346cc4d3c30SLuigi Rizzo flush_buf(buf); 347cc4d3c30SLuigi Rizzo /* data rate */ 348cc4d3c30SLuigi Rizzo if (b == 0) 349cc4d3c30SLuigi Rizzo sprintf(bwbuf, "unlimited "); 3504e9c8ae7SLuigi Rizzo else if (b >= 1000000) 351cc4d3c30SLuigi Rizzo sprintf(bwbuf, "%7.3f Mbit/s", b/1000000); 3524e9c8ae7SLuigi Rizzo else if (b >= 1000) 353cc4d3c30SLuigi Rizzo sprintf(bwbuf, "%7.3f Kbit/s", b/1000); 3544e9c8ae7SLuigi Rizzo else 355cc4d3c30SLuigi Rizzo sprintf(bwbuf, "%7.3f bit/s ", b); 3564e9c8ae7SLuigi Rizzo 3576882bf4dSOleg Bulyzhin if (humanize_number(burst, sizeof(burst), p->burst, 358cc4d3c30SLuigi Rizzo "", HN_AUTOSCALE, 0) < 0 || co.verbose) 359cc4d3c30SLuigi Rizzo sprintf(burst, "%d", (int)p->burst); 360cc4d3c30SLuigi Rizzo sprintf(buf, "%05d: %s %4d ms burst %s", 361cc4d3c30SLuigi Rizzo p->link_nr % DN_MAX_ID, bwbuf, p->delay, burst); 3624e9c8ae7SLuigi Rizzo } 3634e9c8ae7SLuigi Rizzo break; 3644e9c8ae7SLuigi Rizzo 365cc4d3c30SLuigi Rizzo case DN_FS: 366cc4d3c30SLuigi Rizzo print_flowset_parms((struct dn_fs *)oid, buf); 367cc4d3c30SLuigi Rizzo break; 368cc4d3c30SLuigi Rizzo case DN_PROFILE: 369cc4d3c30SLuigi Rizzo flush_buf(buf); 370cc4d3c30SLuigi Rizzo print_extra_delay_parms((struct dn_profile *)oid); 3714e9c8ae7SLuigi Rizzo } 372cc4d3c30SLuigi Rizzo flush_buf(buf); // XXX does it really go here ? 3734e9c8ae7SLuigi Rizzo } 3744e9c8ae7SLuigi Rizzo } 3754e9c8ae7SLuigi Rizzo 3764e9c8ae7SLuigi Rizzo /* 377cc4d3c30SLuigi Rizzo * Delete pipe, queue or scheduler i 3784e9c8ae7SLuigi Rizzo */ 3794e9c8ae7SLuigi Rizzo int 380cc4d3c30SLuigi Rizzo ipfw_delete_pipe(int do_pipe, int i) 3814e9c8ae7SLuigi Rizzo { 382cc4d3c30SLuigi Rizzo struct { 383cc4d3c30SLuigi Rizzo struct dn_id oid; 384cc4d3c30SLuigi Rizzo uintptr_t a[1]; /* add more if we want a list */ 385cc4d3c30SLuigi Rizzo } cmd; 386cc4d3c30SLuigi Rizzo oid_fill((void *)&cmd, sizeof(cmd), DN_CMD_DELETE, DN_API_VERSION); 387cc4d3c30SLuigi Rizzo cmd.oid.subtype = (do_pipe == 1) ? DN_LINK : 388cc4d3c30SLuigi Rizzo ( (do_pipe == 2) ? DN_FS : DN_SCH); 389cc4d3c30SLuigi Rizzo cmd.a[0] = i; 390cc4d3c30SLuigi Rizzo i = do_cmd(IP_DUMMYNET3, &cmd, cmd.oid.len); 3914e9c8ae7SLuigi Rizzo if (i) { 3924e9c8ae7SLuigi Rizzo i = 1; 3934e9c8ae7SLuigi Rizzo warn("rule %u: setsockopt(IP_DUMMYNET_DEL)", i); 3944e9c8ae7SLuigi Rizzo } 3954e9c8ae7SLuigi Rizzo return i; 3964e9c8ae7SLuigi Rizzo } 3974e9c8ae7SLuigi Rizzo 3984bb7ae9dSLuigi Rizzo /* 3994bb7ae9dSLuigi Rizzo * Code to parse delay profiles. 4004bb7ae9dSLuigi Rizzo * 4014bb7ae9dSLuigi Rizzo * Some link types introduce extra delays in the transmission 4024bb7ae9dSLuigi Rizzo * of a packet, e.g. because of MAC level framing, contention on 4034bb7ae9dSLuigi Rizzo * the use of the channel, MAC level retransmissions and so on. 4044bb7ae9dSLuigi Rizzo * From our point of view, the channel is effectively unavailable 4054bb7ae9dSLuigi Rizzo * for this extra time, which is constant or variable depending 4064bb7ae9dSLuigi Rizzo * on the link type. Additionally, packets may be dropped after this 4074bb7ae9dSLuigi Rizzo * time (e.g. on a wireless link after too many retransmissions). 4084bb7ae9dSLuigi Rizzo * We can model the additional delay with an empirical curve 4094bb7ae9dSLuigi Rizzo * that represents its distribution. 4104bb7ae9dSLuigi Rizzo * 4114bb7ae9dSLuigi Rizzo * cumulative probability 4124bb7ae9dSLuigi Rizzo * 1.0 ^ 4134bb7ae9dSLuigi Rizzo * | 4144bb7ae9dSLuigi Rizzo * L +-- loss-level x 4154bb7ae9dSLuigi Rizzo * | ****** 4164bb7ae9dSLuigi Rizzo * | * 4174bb7ae9dSLuigi Rizzo * | ***** 4184bb7ae9dSLuigi Rizzo * | * 4194bb7ae9dSLuigi Rizzo * | ** 4204bb7ae9dSLuigi Rizzo * | * 4214bb7ae9dSLuigi Rizzo * +-------*-------------------> 4224bb7ae9dSLuigi Rizzo * delay 4234bb7ae9dSLuigi Rizzo * 4244bb7ae9dSLuigi Rizzo * The empirical curve may have both vertical and horizontal lines. 4254bb7ae9dSLuigi Rizzo * Vertical lines represent constant delay for a range of 4264bb7ae9dSLuigi Rizzo * probabilities; horizontal lines correspond to a discontinuty 427cc4d3c30SLuigi Rizzo * in the delay distribution: the link will use the largest delay 4284bb7ae9dSLuigi Rizzo * for a given probability. 4294bb7ae9dSLuigi Rizzo * 4304bb7ae9dSLuigi Rizzo * To pass the curve to dummynet, we must store the parameters 4314bb7ae9dSLuigi Rizzo * in a file as described below, and issue the command 4324bb7ae9dSLuigi Rizzo * 4334bb7ae9dSLuigi Rizzo * ipfw pipe <n> config ... bw XXX profile <filename> ... 4344bb7ae9dSLuigi Rizzo * 4354bb7ae9dSLuigi Rizzo * The file format is the following, with whitespace acting as 4364bb7ae9dSLuigi Rizzo * a separator and '#' indicating the beginning a comment: 4374bb7ae9dSLuigi Rizzo * 4384bb7ae9dSLuigi Rizzo * samples N 4394bb7ae9dSLuigi Rizzo * the number of samples used in the internal 4404bb7ae9dSLuigi Rizzo * representation (2..1024; default 100); 4414bb7ae9dSLuigi Rizzo * 4424bb7ae9dSLuigi Rizzo * loss-level L 4434bb7ae9dSLuigi Rizzo * The probability above which packets are lost. 4444bb7ae9dSLuigi Rizzo * (0.0 <= L <= 1.0, default 1.0 i.e. no loss); 4454bb7ae9dSLuigi Rizzo * 4464bb7ae9dSLuigi Rizzo * name identifier 4474bb7ae9dSLuigi Rizzo * Optional a name (listed by "ipfw pipe show") 4484bb7ae9dSLuigi Rizzo * to identify the distribution; 4494bb7ae9dSLuigi Rizzo * 4504bb7ae9dSLuigi Rizzo * "delay prob" | "prob delay" 4514bb7ae9dSLuigi Rizzo * One of these two lines is mandatory and defines 4524bb7ae9dSLuigi Rizzo * the format of the following lines with data points. 4534bb7ae9dSLuigi Rizzo * 4544bb7ae9dSLuigi Rizzo * XXX YYY 4554bb7ae9dSLuigi Rizzo * 2 or more lines representing points in the curve, 4564bb7ae9dSLuigi Rizzo * with either delay or probability first, according 4574bb7ae9dSLuigi Rizzo * to the chosen format. 4584bb7ae9dSLuigi Rizzo * The unit for delay is milliseconds. 4594bb7ae9dSLuigi Rizzo * 4604bb7ae9dSLuigi Rizzo * Data points does not need to be ordered or equal to the number 4614bb7ae9dSLuigi Rizzo * specified in the "samples" line. ipfw will sort and interpolate 4624bb7ae9dSLuigi Rizzo * the curve as needed. 4634bb7ae9dSLuigi Rizzo * 4644bb7ae9dSLuigi Rizzo * Example of a profile file: 4654bb7ae9dSLuigi Rizzo 4664bb7ae9dSLuigi Rizzo name bla_bla_bla 4674bb7ae9dSLuigi Rizzo samples 100 4684bb7ae9dSLuigi Rizzo loss-level 0.86 4694bb7ae9dSLuigi Rizzo prob delay 4704bb7ae9dSLuigi Rizzo 0 200 # minimum overhead is 200ms 4714bb7ae9dSLuigi Rizzo 0.5 200 4724bb7ae9dSLuigi Rizzo 0.5 300 4734bb7ae9dSLuigi Rizzo 0.8 1000 4744bb7ae9dSLuigi Rizzo 0.9 1300 4754bb7ae9dSLuigi Rizzo 1 1300 4764bb7ae9dSLuigi Rizzo 4774bb7ae9dSLuigi Rizzo * Internally, we will convert the curve to a fixed number of 4784bb7ae9dSLuigi Rizzo * samples, and when it is time to transmit a packet we will 4794bb7ae9dSLuigi Rizzo * model the extra delay as extra bits in the packet. 4804bb7ae9dSLuigi Rizzo * 4814bb7ae9dSLuigi Rizzo */ 4824bb7ae9dSLuigi Rizzo 4834bb7ae9dSLuigi Rizzo #define ED_MAX_LINE_LEN 256+ED_MAX_NAME_LEN 4844bb7ae9dSLuigi Rizzo #define ED_TOK_SAMPLES "samples" 4854bb7ae9dSLuigi Rizzo #define ED_TOK_LOSS "loss-level" 4864bb7ae9dSLuigi Rizzo #define ED_TOK_NAME "name" 4874bb7ae9dSLuigi Rizzo #define ED_TOK_DELAY "delay" 4884bb7ae9dSLuigi Rizzo #define ED_TOK_PROB "prob" 4897a459517SLuigi Rizzo #define ED_TOK_BW "bw" 4904bb7ae9dSLuigi Rizzo #define ED_SEPARATORS " \t\n" 4914bb7ae9dSLuigi Rizzo #define ED_MIN_SAMPLES_NO 2 4924bb7ae9dSLuigi Rizzo 4934bb7ae9dSLuigi Rizzo /* 4944bb7ae9dSLuigi Rizzo * returns 1 if s is a non-negative number, with at least one '.' 4954bb7ae9dSLuigi Rizzo */ 4964bb7ae9dSLuigi Rizzo static int 4974bb7ae9dSLuigi Rizzo is_valid_number(const char *s) 4984bb7ae9dSLuigi Rizzo { 4994bb7ae9dSLuigi Rizzo int i, dots_found = 0; 5004bb7ae9dSLuigi Rizzo int len = strlen(s); 5014bb7ae9dSLuigi Rizzo 5024bb7ae9dSLuigi Rizzo for (i = 0; i<len; ++i) 5034bb7ae9dSLuigi Rizzo if (!isdigit(s[i]) && (s[i] !='.' || ++dots_found > 1)) 5044bb7ae9dSLuigi Rizzo return 0; 5054bb7ae9dSLuigi Rizzo return 1; 5064bb7ae9dSLuigi Rizzo } 5074bb7ae9dSLuigi Rizzo 5087a459517SLuigi Rizzo /* 5097a459517SLuigi Rizzo * Take as input a string describing a bandwidth value 5107a459517SLuigi Rizzo * and return the numeric bandwidth value. 5117a459517SLuigi Rizzo * set clocking interface or bandwidth value 5127a459517SLuigi Rizzo */ 51301ab7632SLuigi Rizzo static void 5147a459517SLuigi Rizzo read_bandwidth(char *arg, int *bandwidth, char *if_name, int namelen) 5157a459517SLuigi Rizzo { 5167a459517SLuigi Rizzo if (*bandwidth != -1) 517cc4d3c30SLuigi Rizzo warnx("duplicate token, override bandwidth value!"); 5187a459517SLuigi Rizzo 5197a459517SLuigi Rizzo if (arg[0] >= 'a' && arg[0] <= 'z') { 520cc4d3c30SLuigi Rizzo if (!if_name) { 521cc4d3c30SLuigi Rizzo errx(1, "no if support"); 522cc4d3c30SLuigi Rizzo } 5237a459517SLuigi Rizzo if (namelen >= IFNAMSIZ) 5247a459517SLuigi Rizzo warn("interface name truncated"); 5257a459517SLuigi Rizzo namelen--; 5267a459517SLuigi Rizzo /* interface name */ 5277a459517SLuigi Rizzo strncpy(if_name, arg, namelen); 5287a459517SLuigi Rizzo if_name[namelen] = '\0'; 5297a459517SLuigi Rizzo *bandwidth = 0; 5307a459517SLuigi Rizzo } else { /* read bandwidth value */ 5317a459517SLuigi Rizzo int bw; 5327a459517SLuigi Rizzo char *end = NULL; 5337a459517SLuigi Rizzo 5347a459517SLuigi Rizzo bw = strtoul(arg, &end, 0); 5357a459517SLuigi Rizzo if (*end == 'K' || *end == 'k') { 5367a459517SLuigi Rizzo end++; 5377a459517SLuigi Rizzo bw *= 1000; 538b74331bfSLuigi Rizzo } else if (*end == 'M' || *end == 'm') { 5397a459517SLuigi Rizzo end++; 5407a459517SLuigi Rizzo bw *= 1000000; 5417a459517SLuigi Rizzo } 5427a459517SLuigi Rizzo if ((*end == 'B' && 5437a459517SLuigi Rizzo _substrcmp2(end, "Bi", "Bit/s") != 0) || 5447a459517SLuigi Rizzo _substrcmp2(end, "by", "bytes") == 0) 5457a459517SLuigi Rizzo bw *= 8; 5467a459517SLuigi Rizzo 5477a459517SLuigi Rizzo if (bw < 0) 5487a459517SLuigi Rizzo errx(EX_DATAERR, "bandwidth too large"); 5497a459517SLuigi Rizzo 5507a459517SLuigi Rizzo *bandwidth = bw; 551cc4d3c30SLuigi Rizzo if (if_name) 5527a459517SLuigi Rizzo if_name[0] = '\0'; 5537a459517SLuigi Rizzo } 5547a459517SLuigi Rizzo } 5557a459517SLuigi Rizzo 5564bb7ae9dSLuigi Rizzo struct point { 5574bb7ae9dSLuigi Rizzo double prob; 5584bb7ae9dSLuigi Rizzo double delay; 5594bb7ae9dSLuigi Rizzo }; 5604bb7ae9dSLuigi Rizzo 56101ab7632SLuigi Rizzo static int 5624bb7ae9dSLuigi Rizzo compare_points(const void *vp1, const void *vp2) 5634bb7ae9dSLuigi Rizzo { 5644bb7ae9dSLuigi Rizzo const struct point *p1 = vp1; 5654bb7ae9dSLuigi Rizzo const struct point *p2 = vp2; 5664bb7ae9dSLuigi Rizzo double res = 0; 5674bb7ae9dSLuigi Rizzo 5684bb7ae9dSLuigi Rizzo res = p1->prob - p2->prob; 5694bb7ae9dSLuigi Rizzo if (res == 0) 5704bb7ae9dSLuigi Rizzo res = p1->delay - p2->delay; 5714bb7ae9dSLuigi Rizzo if (res < 0) 5724bb7ae9dSLuigi Rizzo return -1; 5734bb7ae9dSLuigi Rizzo else if (res > 0) 5744bb7ae9dSLuigi Rizzo return 1; 5754bb7ae9dSLuigi Rizzo else 5764bb7ae9dSLuigi Rizzo return 0; 5774bb7ae9dSLuigi Rizzo } 5784bb7ae9dSLuigi Rizzo 5794bb7ae9dSLuigi Rizzo #define ED_EFMT(s) EX_DATAERR,"error in %s at line %d: "#s,filename,lineno 5804bb7ae9dSLuigi Rizzo 5814bb7ae9dSLuigi Rizzo static void 582cc4d3c30SLuigi Rizzo load_extra_delays(const char *filename, struct dn_profile *p, 583cc4d3c30SLuigi Rizzo struct dn_link *link) 5844bb7ae9dSLuigi Rizzo { 5854bb7ae9dSLuigi Rizzo char line[ED_MAX_LINE_LEN]; 5864bb7ae9dSLuigi Rizzo FILE *f; 5874bb7ae9dSLuigi Rizzo int lineno = 0; 5884bb7ae9dSLuigi Rizzo int i; 5894bb7ae9dSLuigi Rizzo 5904bb7ae9dSLuigi Rizzo int samples = -1; 5914bb7ae9dSLuigi Rizzo double loss = -1.0; 5924bb7ae9dSLuigi Rizzo char profile_name[ED_MAX_NAME_LEN]; 5934bb7ae9dSLuigi Rizzo int delay_first = -1; 5944bb7ae9dSLuigi Rizzo int do_points = 0; 5954bb7ae9dSLuigi Rizzo struct point points[ED_MAX_SAMPLES_NO]; 5964bb7ae9dSLuigi Rizzo int points_no = 0; 5974bb7ae9dSLuigi Rizzo 598cc4d3c30SLuigi Rizzo /* XXX link never NULL? */ 599cc4d3c30SLuigi Rizzo p->link_nr = link->link_nr; 600cc4d3c30SLuigi Rizzo 6014bb7ae9dSLuigi Rizzo profile_name[0] = '\0'; 6024bb7ae9dSLuigi Rizzo f = fopen(filename, "r"); 6034bb7ae9dSLuigi Rizzo if (f == NULL) 6044bb7ae9dSLuigi Rizzo err(EX_UNAVAILABLE, "fopen: %s", filename); 6054bb7ae9dSLuigi Rizzo 6064bb7ae9dSLuigi Rizzo while (fgets(line, ED_MAX_LINE_LEN, f)) { /* read commands */ 6074bb7ae9dSLuigi Rizzo char *s, *cur = line, *name = NULL, *arg = NULL; 6084bb7ae9dSLuigi Rizzo 6094bb7ae9dSLuigi Rizzo ++lineno; 6104bb7ae9dSLuigi Rizzo 6114bb7ae9dSLuigi Rizzo /* parse the line */ 6124bb7ae9dSLuigi Rizzo while (cur) { 6134bb7ae9dSLuigi Rizzo s = strsep(&cur, ED_SEPARATORS); 6144bb7ae9dSLuigi Rizzo if (s == NULL || *s == '#') 6154bb7ae9dSLuigi Rizzo break; 6164bb7ae9dSLuigi Rizzo if (*s == '\0') 6174bb7ae9dSLuigi Rizzo continue; 6184bb7ae9dSLuigi Rizzo if (arg) 6194bb7ae9dSLuigi Rizzo errx(ED_EFMT("too many arguments")); 6204bb7ae9dSLuigi Rizzo if (name == NULL) 6214bb7ae9dSLuigi Rizzo name = s; 6224bb7ae9dSLuigi Rizzo else 6234bb7ae9dSLuigi Rizzo arg = s; 6244bb7ae9dSLuigi Rizzo } 6254bb7ae9dSLuigi Rizzo if (name == NULL) /* empty line */ 6264bb7ae9dSLuigi Rizzo continue; 6274bb7ae9dSLuigi Rizzo if (arg == NULL) 6284bb7ae9dSLuigi Rizzo errx(ED_EFMT("missing arg for %s"), name); 6294bb7ae9dSLuigi Rizzo 6304bb7ae9dSLuigi Rizzo if (!strcasecmp(name, ED_TOK_SAMPLES)) { 6314bb7ae9dSLuigi Rizzo if (samples > 0) 6324bb7ae9dSLuigi Rizzo errx(ED_EFMT("duplicate ``samples'' line")); 6334bb7ae9dSLuigi Rizzo if (atoi(arg) <=0) 6344bb7ae9dSLuigi Rizzo errx(ED_EFMT("invalid number of samples")); 6354bb7ae9dSLuigi Rizzo samples = atoi(arg); 6364bb7ae9dSLuigi Rizzo if (samples>ED_MAX_SAMPLES_NO) 6374bb7ae9dSLuigi Rizzo errx(ED_EFMT("too many samples, maximum is %d"), 6384bb7ae9dSLuigi Rizzo ED_MAX_SAMPLES_NO); 6394bb7ae9dSLuigi Rizzo do_points = 0; 6407a459517SLuigi Rizzo } else if (!strcasecmp(name, ED_TOK_BW)) { 641cc4d3c30SLuigi Rizzo char buf[IFNAMSIZ]; 642cc4d3c30SLuigi Rizzo read_bandwidth(arg, &link->bandwidth, buf, sizeof(buf)); 6434bb7ae9dSLuigi Rizzo } else if (!strcasecmp(name, ED_TOK_LOSS)) { 6444bb7ae9dSLuigi Rizzo if (loss != -1.0) 6454bb7ae9dSLuigi Rizzo errx(ED_EFMT("duplicated token: %s"), name); 6464bb7ae9dSLuigi Rizzo if (!is_valid_number(arg)) 6474bb7ae9dSLuigi Rizzo errx(ED_EFMT("invalid %s"), arg); 6484bb7ae9dSLuigi Rizzo loss = atof(arg); 6494bb7ae9dSLuigi Rizzo if (loss > 1) 6504bb7ae9dSLuigi Rizzo errx(ED_EFMT("%s greater than 1.0"), name); 6514bb7ae9dSLuigi Rizzo do_points = 0; 6524bb7ae9dSLuigi Rizzo } else if (!strcasecmp(name, ED_TOK_NAME)) { 6534bb7ae9dSLuigi Rizzo if (profile_name[0] != '\0') 6544bb7ae9dSLuigi Rizzo errx(ED_EFMT("duplicated token: %s"), name); 6554bb7ae9dSLuigi Rizzo strncpy(profile_name, arg, sizeof(profile_name) - 1); 6564bb7ae9dSLuigi Rizzo profile_name[sizeof(profile_name)-1] = '\0'; 6574bb7ae9dSLuigi Rizzo do_points = 0; 6584bb7ae9dSLuigi Rizzo } else if (!strcasecmp(name, ED_TOK_DELAY)) { 6594bb7ae9dSLuigi Rizzo if (do_points) 6604bb7ae9dSLuigi Rizzo errx(ED_EFMT("duplicated token: %s"), name); 6614bb7ae9dSLuigi Rizzo delay_first = 1; 6624bb7ae9dSLuigi Rizzo do_points = 1; 6634bb7ae9dSLuigi Rizzo } else if (!strcasecmp(name, ED_TOK_PROB)) { 6644bb7ae9dSLuigi Rizzo if (do_points) 6654bb7ae9dSLuigi Rizzo errx(ED_EFMT("duplicated token: %s"), name); 6664bb7ae9dSLuigi Rizzo delay_first = 0; 6674bb7ae9dSLuigi Rizzo do_points = 1; 6684bb7ae9dSLuigi Rizzo } else if (do_points) { 6694bb7ae9dSLuigi Rizzo if (!is_valid_number(name) || !is_valid_number(arg)) 6704bb7ae9dSLuigi Rizzo errx(ED_EFMT("invalid point found")); 6714bb7ae9dSLuigi Rizzo if (delay_first) { 6724bb7ae9dSLuigi Rizzo points[points_no].delay = atof(name); 6734bb7ae9dSLuigi Rizzo points[points_no].prob = atof(arg); 6744bb7ae9dSLuigi Rizzo } else { 6754bb7ae9dSLuigi Rizzo points[points_no].delay = atof(arg); 6764bb7ae9dSLuigi Rizzo points[points_no].prob = atof(name); 6774bb7ae9dSLuigi Rizzo } 6784bb7ae9dSLuigi Rizzo if (points[points_no].prob > 1.0) 6794bb7ae9dSLuigi Rizzo errx(ED_EFMT("probability greater than 1.0")); 6804bb7ae9dSLuigi Rizzo ++points_no; 6814bb7ae9dSLuigi Rizzo } else { 6824bb7ae9dSLuigi Rizzo errx(ED_EFMT("unrecognised command '%s'"), name); 6834bb7ae9dSLuigi Rizzo } 6844bb7ae9dSLuigi Rizzo } 6854bb7ae9dSLuigi Rizzo 686ac2e492bSAlexander Leidinger fclose (f); 687ac2e492bSAlexander Leidinger 6884bb7ae9dSLuigi Rizzo if (samples == -1) { 6894bb7ae9dSLuigi Rizzo warnx("'%s' not found, assuming 100", ED_TOK_SAMPLES); 6904bb7ae9dSLuigi Rizzo samples = 100; 6914bb7ae9dSLuigi Rizzo } 6924bb7ae9dSLuigi Rizzo 6934bb7ae9dSLuigi Rizzo if (loss == -1.0) { 6944bb7ae9dSLuigi Rizzo warnx("'%s' not found, assuming no loss", ED_TOK_LOSS); 6954bb7ae9dSLuigi Rizzo loss = 1; 6964bb7ae9dSLuigi Rizzo } 6974bb7ae9dSLuigi Rizzo 6984bb7ae9dSLuigi Rizzo /* make sure that there are enough points. */ 6994bb7ae9dSLuigi Rizzo if (points_no < ED_MIN_SAMPLES_NO) 7004bb7ae9dSLuigi Rizzo errx(ED_EFMT("too few samples, need at least %d"), 7014bb7ae9dSLuigi Rizzo ED_MIN_SAMPLES_NO); 7024bb7ae9dSLuigi Rizzo 7034bb7ae9dSLuigi Rizzo qsort(points, points_no, sizeof(struct point), compare_points); 7044bb7ae9dSLuigi Rizzo 7054bb7ae9dSLuigi Rizzo /* interpolation */ 7064bb7ae9dSLuigi Rizzo for (i = 0; i<points_no-1; ++i) { 7074bb7ae9dSLuigi Rizzo double y1 = points[i].prob * samples; 7084bb7ae9dSLuigi Rizzo double x1 = points[i].delay; 7094bb7ae9dSLuigi Rizzo double y2 = points[i+1].prob * samples; 7104bb7ae9dSLuigi Rizzo double x2 = points[i+1].delay; 7114bb7ae9dSLuigi Rizzo 712cc4d3c30SLuigi Rizzo int ix = y1; 7134bb7ae9dSLuigi Rizzo int stop = y2; 7144bb7ae9dSLuigi Rizzo 7154bb7ae9dSLuigi Rizzo if (x1 == x2) { 716cc4d3c30SLuigi Rizzo for (; ix<stop; ++ix) 717cc4d3c30SLuigi Rizzo p->samples[ix] = x1; 7184bb7ae9dSLuigi Rizzo } else { 7194bb7ae9dSLuigi Rizzo double m = (y2-y1)/(x2-x1); 7204bb7ae9dSLuigi Rizzo double c = y1 - m*x1; 721cc4d3c30SLuigi Rizzo for (; ix<stop ; ++ix) 722cc4d3c30SLuigi Rizzo p->samples[ix] = (ix - c)/m; 7234bb7ae9dSLuigi Rizzo } 7244bb7ae9dSLuigi Rizzo } 7254bb7ae9dSLuigi Rizzo p->samples_no = samples; 7264bb7ae9dSLuigi Rizzo p->loss_level = loss * samples; 7274bb7ae9dSLuigi Rizzo strncpy(p->name, profile_name, sizeof(p->name)); 7284bb7ae9dSLuigi Rizzo } 7294bb7ae9dSLuigi Rizzo 730cc4d3c30SLuigi Rizzo /* 731cc4d3c30SLuigi Rizzo * configuration of pipes, schedulers, flowsets. 732cc4d3c30SLuigi Rizzo * When we configure a new scheduler, an empty pipe is created, so: 733cc4d3c30SLuigi Rizzo * 734cc4d3c30SLuigi Rizzo * do_pipe = 1 -> "pipe N config ..." only for backward compatibility 735cc4d3c30SLuigi Rizzo * sched N+Delta type fifo sched_mask ... 736cc4d3c30SLuigi Rizzo * pipe N+Delta <parameters> 737cc4d3c30SLuigi Rizzo * flowset N+Delta pipe N+Delta (no parameters) 738cc4d3c30SLuigi Rizzo * sched N type wf2q+ sched_mask ... 739cc4d3c30SLuigi Rizzo * pipe N <parameters> 740cc4d3c30SLuigi Rizzo * 741cc4d3c30SLuigi Rizzo * do_pipe = 2 -> flowset N config 742cc4d3c30SLuigi Rizzo * flowset N parameters 743cc4d3c30SLuigi Rizzo * 744cc4d3c30SLuigi Rizzo * do_pipe = 3 -> sched N config 745cc4d3c30SLuigi Rizzo * sched N parameters (default no pipe) 746cc4d3c30SLuigi Rizzo * optional Pipe N config ... 747cc4d3c30SLuigi Rizzo * pipe ==> 748cc4d3c30SLuigi Rizzo */ 7494e9c8ae7SLuigi Rizzo void 7504e9c8ae7SLuigi Rizzo ipfw_config_pipe(int ac, char **av) 7514e9c8ae7SLuigi Rizzo { 752cc4d3c30SLuigi Rizzo int i, j; 7534e9c8ae7SLuigi Rizzo char *end; 7544e9c8ae7SLuigi Rizzo void *par = NULL; 755cc4d3c30SLuigi Rizzo struct dn_id *buf, *base; 756cc4d3c30SLuigi Rizzo struct dn_sch *sch = NULL; 757cc4d3c30SLuigi Rizzo struct dn_link *p = NULL; 758cc4d3c30SLuigi Rizzo struct dn_fs *fs = NULL; 759cc4d3c30SLuigi Rizzo struct dn_profile *pf = NULL; 760cc4d3c30SLuigi Rizzo struct ipfw_flow_id *mask = NULL; 761cc4d3c30SLuigi Rizzo int lmax; 762cc4d3c30SLuigi Rizzo uint32_t _foo = 0, *flags = &_foo , *buckets = &_foo; 7634e9c8ae7SLuigi Rizzo 764cc4d3c30SLuigi Rizzo /* 765cc4d3c30SLuigi Rizzo * allocate space for 1 header, 766cc4d3c30SLuigi Rizzo * 1 scheduler, 1 link, 1 flowset, 1 profile 767cc4d3c30SLuigi Rizzo */ 768cc4d3c30SLuigi Rizzo lmax = sizeof(struct dn_id); /* command header */ 769cc4d3c30SLuigi Rizzo lmax += sizeof(struct dn_sch) + sizeof(struct dn_link) + 770cc4d3c30SLuigi Rizzo sizeof(struct dn_fs) + sizeof(struct dn_profile); 7714e9c8ae7SLuigi Rizzo 7724e9c8ae7SLuigi Rizzo av++; ac--; 7734e9c8ae7SLuigi Rizzo /* Pipe number */ 7744e9c8ae7SLuigi Rizzo if (ac && isdigit(**av)) { 7754e9c8ae7SLuigi Rizzo i = atoi(*av); av++; ac--; 776cc4d3c30SLuigi Rizzo } else 777cc4d3c30SLuigi Rizzo i = -1; 778cc4d3c30SLuigi Rizzo if (i <= 0) 779cc4d3c30SLuigi Rizzo errx(EX_USAGE, "need a pipe/flowset/sched number"); 780cc4d3c30SLuigi Rizzo base = buf = safe_calloc(1, lmax); 781cc4d3c30SLuigi Rizzo /* all commands start with a 'CONFIGURE' and a version */ 782cc4d3c30SLuigi Rizzo o_next(&buf, sizeof(struct dn_id), DN_CMD_CONFIG); 783cc4d3c30SLuigi Rizzo base->id = DN_API_VERSION; 784cc4d3c30SLuigi Rizzo 785cc4d3c30SLuigi Rizzo switch (co.do_pipe) { 786cc4d3c30SLuigi Rizzo case 1: /* "pipe N config ..." */ 787cc4d3c30SLuigi Rizzo /* Allocate space for the WF2Q+ scheduler, its link 788cc4d3c30SLuigi Rizzo * and the FIFO flowset. Set the number, but leave 789cc4d3c30SLuigi Rizzo * the scheduler subtype and other parameters to 0 790cc4d3c30SLuigi Rizzo * so the kernel will use appropriate defaults. 791cc4d3c30SLuigi Rizzo * XXX todo: add a flag to record if a parameter 792cc4d3c30SLuigi Rizzo * is actually configured. 793cc4d3c30SLuigi Rizzo * If we do a 'pipe config' mask -> sched_mask. 794cc4d3c30SLuigi Rizzo * The FIFO scheduler and link are derived from the 795cc4d3c30SLuigi Rizzo * WF2Q+ one in the kernel. 796cc4d3c30SLuigi Rizzo */ 797cc4d3c30SLuigi Rizzo sch = o_next(&buf, sizeof(*sch), DN_SCH); 798cc4d3c30SLuigi Rizzo p = o_next(&buf, sizeof(*p), DN_LINK); 799cc4d3c30SLuigi Rizzo fs = o_next(&buf, sizeof(*fs), DN_FS); 800cc4d3c30SLuigi Rizzo 801cc4d3c30SLuigi Rizzo sch->sched_nr = i; 802cc4d3c30SLuigi Rizzo sch->oid.subtype = 0; /* defaults to WF2Q+ */ 803cc4d3c30SLuigi Rizzo mask = &sch->sched_mask; 804cc4d3c30SLuigi Rizzo flags = &sch->flags; 805cc4d3c30SLuigi Rizzo buckets = &sch->buckets; 806cc4d3c30SLuigi Rizzo *flags |= DN_PIPE_CMD; 807cc4d3c30SLuigi Rizzo 808cc4d3c30SLuigi Rizzo p->link_nr = i; 809cc4d3c30SLuigi Rizzo 810cc4d3c30SLuigi Rizzo /* This flowset is only for the FIFO scheduler */ 811cc4d3c30SLuigi Rizzo fs->fs_nr = i + 2*DN_MAX_ID; 812cc4d3c30SLuigi Rizzo fs->sched_nr = i + DN_MAX_ID; 813cc4d3c30SLuigi Rizzo break; 814cc4d3c30SLuigi Rizzo 815cc4d3c30SLuigi Rizzo case 2: /* "queue N config ... " */ 816cc4d3c30SLuigi Rizzo fs = o_next(&buf, sizeof(*fs), DN_FS); 817cc4d3c30SLuigi Rizzo fs->fs_nr = i; 818cc4d3c30SLuigi Rizzo mask = &fs->flow_mask; 819cc4d3c30SLuigi Rizzo flags = &fs->flags; 820cc4d3c30SLuigi Rizzo buckets = &fs->buckets; 821cc4d3c30SLuigi Rizzo break; 822cc4d3c30SLuigi Rizzo 823cc4d3c30SLuigi Rizzo case 3: /* "sched N config ..." */ 824cc4d3c30SLuigi Rizzo sch = o_next(&buf, sizeof(*sch), DN_SCH); 825cc4d3c30SLuigi Rizzo fs = o_next(&buf, sizeof(*fs), DN_FS); 826cc4d3c30SLuigi Rizzo sch->sched_nr = i; 827cc4d3c30SLuigi Rizzo mask = &sch->sched_mask; 828cc4d3c30SLuigi Rizzo flags = &sch->flags; 829cc4d3c30SLuigi Rizzo buckets = &sch->buckets; 830cc4d3c30SLuigi Rizzo /* fs is used only with !MULTIQUEUE schedulers */ 831cc4d3c30SLuigi Rizzo fs->fs_nr = i + DN_MAX_ID; 832cc4d3c30SLuigi Rizzo fs->sched_nr = i; 833cc4d3c30SLuigi Rizzo break; 8344e9c8ae7SLuigi Rizzo } 835cc4d3c30SLuigi Rizzo /* set to -1 those fields for which we want to reuse existing 836cc4d3c30SLuigi Rizzo * values from the kernel. 837cc4d3c30SLuigi Rizzo * Also, *_nr and subtype = 0 mean reuse the value from the kernel. 838cc4d3c30SLuigi Rizzo * XXX todo: support reuse of the mask. 839cc4d3c30SLuigi Rizzo */ 840cc4d3c30SLuigi Rizzo if (p) 841cc4d3c30SLuigi Rizzo p->bandwidth = -1; 842cc4d3c30SLuigi Rizzo for (j = 0; j < sizeof(fs->par)/sizeof(fs->par[0]); j++) 843cc4d3c30SLuigi Rizzo fs->par[j] = -1; 8444e9c8ae7SLuigi Rizzo while (ac > 0) { 8454e9c8ae7SLuigi Rizzo double d; 8464e9c8ae7SLuigi Rizzo int tok = match_token(dummynet_params, *av); 8474e9c8ae7SLuigi Rizzo ac--; av++; 8484e9c8ae7SLuigi Rizzo 8494e9c8ae7SLuigi Rizzo switch(tok) { 8504e9c8ae7SLuigi Rizzo case TOK_NOERROR: 851cc4d3c30SLuigi Rizzo NEED(fs, "noerror is only for pipes"); 852cc4d3c30SLuigi Rizzo fs->flags |= DN_NOERROR; 8534e9c8ae7SLuigi Rizzo break; 8544e9c8ae7SLuigi Rizzo 8554e9c8ae7SLuigi Rizzo case TOK_PLR: 856cc4d3c30SLuigi Rizzo NEED(fs, "plr is only for pipes"); 8574e9c8ae7SLuigi Rizzo NEED1("plr needs argument 0..1\n"); 8584e9c8ae7SLuigi Rizzo d = strtod(av[0], NULL); 8594e9c8ae7SLuigi Rizzo if (d > 1) 8604e9c8ae7SLuigi Rizzo d = 1; 8614e9c8ae7SLuigi Rizzo else if (d < 0) 8624e9c8ae7SLuigi Rizzo d = 0; 863cc4d3c30SLuigi Rizzo fs->plr = (int)(d*0x7fffffff); 8644e9c8ae7SLuigi Rizzo ac--; av++; 8654e9c8ae7SLuigi Rizzo break; 8664e9c8ae7SLuigi Rizzo 8674e9c8ae7SLuigi Rizzo case TOK_QUEUE: 868cc4d3c30SLuigi Rizzo NEED(fs, "queue is only for pipes or flowsets"); 8694e9c8ae7SLuigi Rizzo NEED1("queue needs queue size\n"); 8704e9c8ae7SLuigi Rizzo end = NULL; 871cc4d3c30SLuigi Rizzo fs->qsize = strtoul(av[0], &end, 0); 8724e9c8ae7SLuigi Rizzo if (*end == 'K' || *end == 'k') { 873cc4d3c30SLuigi Rizzo fs->flags |= DN_QSIZE_BYTES; 874cc4d3c30SLuigi Rizzo fs->qsize *= 1024; 8754e9c8ae7SLuigi Rizzo } else if (*end == 'B' || 8764e9c8ae7SLuigi Rizzo _substrcmp2(end, "by", "bytes") == 0) { 877cc4d3c30SLuigi Rizzo fs->flags |= DN_QSIZE_BYTES; 8784e9c8ae7SLuigi Rizzo } 8794e9c8ae7SLuigi Rizzo ac--; av++; 8804e9c8ae7SLuigi Rizzo break; 8814e9c8ae7SLuigi Rizzo 8824e9c8ae7SLuigi Rizzo case TOK_BUCKETS: 883cc4d3c30SLuigi Rizzo NEED(fs, "buckets is only for pipes or flowsets"); 8844e9c8ae7SLuigi Rizzo NEED1("buckets needs argument\n"); 885cc4d3c30SLuigi Rizzo *buckets = strtoul(av[0], NULL, 0); 8864e9c8ae7SLuigi Rizzo ac--; av++; 8874e9c8ae7SLuigi Rizzo break; 8884e9c8ae7SLuigi Rizzo 889cc4d3c30SLuigi Rizzo case TOK_FLOW_MASK: 890cc4d3c30SLuigi Rizzo case TOK_SCHED_MASK: 8914e9c8ae7SLuigi Rizzo case TOK_MASK: 892cc4d3c30SLuigi Rizzo NEED(mask, "tok_mask"); 8934e9c8ae7SLuigi Rizzo NEED1("mask needs mask specifier\n"); 8944e9c8ae7SLuigi Rizzo /* 8954e9c8ae7SLuigi Rizzo * per-flow queue, mask is dst_ip, dst_port, 8964e9c8ae7SLuigi Rizzo * src_ip, src_port, proto measured in bits 8974e9c8ae7SLuigi Rizzo */ 8984e9c8ae7SLuigi Rizzo par = NULL; 8994e9c8ae7SLuigi Rizzo 900cc4d3c30SLuigi Rizzo bzero(mask, sizeof(*mask)); 9014e9c8ae7SLuigi Rizzo end = NULL; 9024e9c8ae7SLuigi Rizzo 9034e9c8ae7SLuigi Rizzo while (ac >= 1) { 9044e9c8ae7SLuigi Rizzo uint32_t *p32 = NULL; 9054e9c8ae7SLuigi Rizzo uint16_t *p16 = NULL; 9064e9c8ae7SLuigi Rizzo uint32_t *p20 = NULL; 9074e9c8ae7SLuigi Rizzo struct in6_addr *pa6 = NULL; 9084e9c8ae7SLuigi Rizzo uint32_t a; 9094e9c8ae7SLuigi Rizzo 9104e9c8ae7SLuigi Rizzo tok = match_token(dummynet_params, *av); 9114e9c8ae7SLuigi Rizzo ac--; av++; 9124e9c8ae7SLuigi Rizzo switch(tok) { 9134e9c8ae7SLuigi Rizzo case TOK_ALL: 9144e9c8ae7SLuigi Rizzo /* 9154e9c8ae7SLuigi Rizzo * special case, all bits significant 916f9f7bde3SLuigi Rizzo * except 'extra' (the queue number) 9174e9c8ae7SLuigi Rizzo */ 918cc4d3c30SLuigi Rizzo mask->dst_ip = ~0; 919cc4d3c30SLuigi Rizzo mask->src_ip = ~0; 920cc4d3c30SLuigi Rizzo mask->dst_port = ~0; 921cc4d3c30SLuigi Rizzo mask->src_port = ~0; 922cc4d3c30SLuigi Rizzo mask->proto = ~0; 923cc4d3c30SLuigi Rizzo n2mask(&mask->dst_ip6, 128); 924cc4d3c30SLuigi Rizzo n2mask(&mask->src_ip6, 128); 925cc4d3c30SLuigi Rizzo mask->flow_id6 = ~0; 926cc4d3c30SLuigi Rizzo *flags |= DN_HAVE_MASK; 9274e9c8ae7SLuigi Rizzo goto end_mask; 9284e9c8ae7SLuigi Rizzo 929f9f7bde3SLuigi Rizzo case TOK_QUEUE: 930f9f7bde3SLuigi Rizzo mask->extra = ~0; 931f9f7bde3SLuigi Rizzo *flags |= DN_HAVE_MASK; 932f9f7bde3SLuigi Rizzo goto end_mask; 933f9f7bde3SLuigi Rizzo 9344e9c8ae7SLuigi Rizzo case TOK_DSTIP: 935cc4d3c30SLuigi Rizzo mask->addr_type = 4; 936cc4d3c30SLuigi Rizzo p32 = &mask->dst_ip; 9374e9c8ae7SLuigi Rizzo break; 9384e9c8ae7SLuigi Rizzo 9394e9c8ae7SLuigi Rizzo case TOK_SRCIP: 940cc4d3c30SLuigi Rizzo mask->addr_type = 4; 941cc4d3c30SLuigi Rizzo p32 = &mask->src_ip; 9424e9c8ae7SLuigi Rizzo break; 9434e9c8ae7SLuigi Rizzo 9444e9c8ae7SLuigi Rizzo case TOK_DSTIP6: 945cc4d3c30SLuigi Rizzo mask->addr_type = 6; 946cc4d3c30SLuigi Rizzo pa6 = &mask->dst_ip6; 9474e9c8ae7SLuigi Rizzo break; 9484e9c8ae7SLuigi Rizzo 9494e9c8ae7SLuigi Rizzo case TOK_SRCIP6: 950cc4d3c30SLuigi Rizzo mask->addr_type = 6; 951cc4d3c30SLuigi Rizzo pa6 = &mask->src_ip6; 9524e9c8ae7SLuigi Rizzo break; 9534e9c8ae7SLuigi Rizzo 9544e9c8ae7SLuigi Rizzo case TOK_FLOWID: 955cc4d3c30SLuigi Rizzo mask->addr_type = 6; 956cc4d3c30SLuigi Rizzo p20 = &mask->flow_id6; 9574e9c8ae7SLuigi Rizzo break; 9584e9c8ae7SLuigi Rizzo 9594e9c8ae7SLuigi Rizzo case TOK_DSTPORT: 960cc4d3c30SLuigi Rizzo p16 = &mask->dst_port; 9614e9c8ae7SLuigi Rizzo break; 9624e9c8ae7SLuigi Rizzo 9634e9c8ae7SLuigi Rizzo case TOK_SRCPORT: 964cc4d3c30SLuigi Rizzo p16 = &mask->src_port; 9654e9c8ae7SLuigi Rizzo break; 9664e9c8ae7SLuigi Rizzo 9674e9c8ae7SLuigi Rizzo case TOK_PROTO: 9684e9c8ae7SLuigi Rizzo break; 9694e9c8ae7SLuigi Rizzo 9704e9c8ae7SLuigi Rizzo default: 9714e9c8ae7SLuigi Rizzo ac++; av--; /* backtrack */ 9724e9c8ae7SLuigi Rizzo goto end_mask; 9734e9c8ae7SLuigi Rizzo } 9744e9c8ae7SLuigi Rizzo if (ac < 1) 9754e9c8ae7SLuigi Rizzo errx(EX_USAGE, "mask: value missing"); 9764e9c8ae7SLuigi Rizzo if (*av[0] == '/') { 9774e9c8ae7SLuigi Rizzo a = strtoul(av[0]+1, &end, 0); 9784e9c8ae7SLuigi Rizzo if (pa6 == NULL) 9794e9c8ae7SLuigi Rizzo a = (a == 32) ? ~0 : (1 << a) - 1; 9804e9c8ae7SLuigi Rizzo } else 9814e9c8ae7SLuigi Rizzo a = strtoul(av[0], &end, 0); 9824e9c8ae7SLuigi Rizzo if (p32 != NULL) 9834e9c8ae7SLuigi Rizzo *p32 = a; 9844e9c8ae7SLuigi Rizzo else if (p16 != NULL) { 9854e9c8ae7SLuigi Rizzo if (a > 0xFFFF) 9864e9c8ae7SLuigi Rizzo errx(EX_DATAERR, 9874e9c8ae7SLuigi Rizzo "port mask must be 16 bit"); 9884e9c8ae7SLuigi Rizzo *p16 = (uint16_t)a; 9894e9c8ae7SLuigi Rizzo } else if (p20 != NULL) { 9904e9c8ae7SLuigi Rizzo if (a > 0xfffff) 9914e9c8ae7SLuigi Rizzo errx(EX_DATAERR, 9924e9c8ae7SLuigi Rizzo "flow_id mask must be 20 bit"); 9934e9c8ae7SLuigi Rizzo *p20 = (uint32_t)a; 9944e9c8ae7SLuigi Rizzo } else if (pa6 != NULL) { 9954e9c8ae7SLuigi Rizzo if (a > 128) 9964e9c8ae7SLuigi Rizzo errx(EX_DATAERR, 9974e9c8ae7SLuigi Rizzo "in6addr invalid mask len"); 9984e9c8ae7SLuigi Rizzo else 9994e9c8ae7SLuigi Rizzo n2mask(pa6, a); 10004e9c8ae7SLuigi Rizzo } else { 10014e9c8ae7SLuigi Rizzo if (a > 0xFF) 10024e9c8ae7SLuigi Rizzo errx(EX_DATAERR, 10034e9c8ae7SLuigi Rizzo "proto mask must be 8 bit"); 1004f9f7bde3SLuigi Rizzo mask->proto = (uint8_t)a; 10054e9c8ae7SLuigi Rizzo } 10064e9c8ae7SLuigi Rizzo if (a != 0) 1007cc4d3c30SLuigi Rizzo *flags |= DN_HAVE_MASK; 10084e9c8ae7SLuigi Rizzo ac--; av++; 10094e9c8ae7SLuigi Rizzo } /* end while, config masks */ 10104e9c8ae7SLuigi Rizzo end_mask: 10114e9c8ae7SLuigi Rizzo break; 10124e9c8ae7SLuigi Rizzo 10134e9c8ae7SLuigi Rizzo case TOK_RED: 10144e9c8ae7SLuigi Rizzo case TOK_GRED: 10154e9c8ae7SLuigi Rizzo NEED1("red/gred needs w_q/min_th/max_th/max_p\n"); 1016cc4d3c30SLuigi Rizzo fs->flags |= DN_IS_RED; 10174e9c8ae7SLuigi Rizzo if (tok == TOK_GRED) 1018cc4d3c30SLuigi Rizzo fs->flags |= DN_IS_GENTLE_RED; 10194e9c8ae7SLuigi Rizzo /* 10204e9c8ae7SLuigi Rizzo * the format for parameters is w_q/min_th/max_th/max_p 10214e9c8ae7SLuigi Rizzo */ 10224e9c8ae7SLuigi Rizzo if ((end = strsep(&av[0], "/"))) { 10234e9c8ae7SLuigi Rizzo double w_q = strtod(end, NULL); 10244e9c8ae7SLuigi Rizzo if (w_q > 1 || w_q <= 0) 10254e9c8ae7SLuigi Rizzo errx(EX_DATAERR, "0 < w_q <= 1"); 1026cc4d3c30SLuigi Rizzo fs->w_q = (int) (w_q * (1 << SCALE_RED)); 10274e9c8ae7SLuigi Rizzo } 10284e9c8ae7SLuigi Rizzo if ((end = strsep(&av[0], "/"))) { 1029cc4d3c30SLuigi Rizzo fs->min_th = strtoul(end, &end, 0); 10304e9c8ae7SLuigi Rizzo if (*end == 'K' || *end == 'k') 1031cc4d3c30SLuigi Rizzo fs->min_th *= 1024; 10324e9c8ae7SLuigi Rizzo } 10334e9c8ae7SLuigi Rizzo if ((end = strsep(&av[0], "/"))) { 1034cc4d3c30SLuigi Rizzo fs->max_th = strtoul(end, &end, 0); 10354e9c8ae7SLuigi Rizzo if (*end == 'K' || *end == 'k') 1036cc4d3c30SLuigi Rizzo fs->max_th *= 1024; 10374e9c8ae7SLuigi Rizzo } 10384e9c8ae7SLuigi Rizzo if ((end = strsep(&av[0], "/"))) { 10394e9c8ae7SLuigi Rizzo double max_p = strtod(end, NULL); 10404e9c8ae7SLuigi Rizzo if (max_p > 1 || max_p <= 0) 10414e9c8ae7SLuigi Rizzo errx(EX_DATAERR, "0 < max_p <= 1"); 1042cc4d3c30SLuigi Rizzo fs->max_p = (int)(max_p * (1 << SCALE_RED)); 10434e9c8ae7SLuigi Rizzo } 10444e9c8ae7SLuigi Rizzo ac--; av++; 10454e9c8ae7SLuigi Rizzo break; 10464e9c8ae7SLuigi Rizzo 10474e9c8ae7SLuigi Rizzo case TOK_DROPTAIL: 1048cc4d3c30SLuigi Rizzo NEED(fs, "droptail is only for flowsets"); 1049cc4d3c30SLuigi Rizzo fs->flags &= ~(DN_IS_RED|DN_IS_GENTLE_RED); 10504e9c8ae7SLuigi Rizzo break; 10514e9c8ae7SLuigi Rizzo 10524e9c8ae7SLuigi Rizzo case TOK_BW: 1053cc4d3c30SLuigi Rizzo NEED(p, "bw is only for links"); 10544e9c8ae7SLuigi Rizzo NEED1("bw needs bandwidth or interface\n"); 1055cc4d3c30SLuigi Rizzo read_bandwidth(av[0], &p->bandwidth, NULL, 0); 10564e9c8ae7SLuigi Rizzo ac--; av++; 10574e9c8ae7SLuigi Rizzo break; 10584e9c8ae7SLuigi Rizzo 10594e9c8ae7SLuigi Rizzo case TOK_DELAY: 1060cc4d3c30SLuigi Rizzo NEED(p, "delay is only for links"); 10614e9c8ae7SLuigi Rizzo NEED1("delay needs argument 0..10000ms\n"); 1062cc4d3c30SLuigi Rizzo p->delay = strtoul(av[0], NULL, 0); 10634e9c8ae7SLuigi Rizzo ac--; av++; 10644e9c8ae7SLuigi Rizzo break; 10654e9c8ae7SLuigi Rizzo 1066cc4d3c30SLuigi Rizzo case TOK_TYPE: { 1067cc4d3c30SLuigi Rizzo int l; 1068cc4d3c30SLuigi Rizzo NEED(sch, "type is only for schedulers"); 1069cc4d3c30SLuigi Rizzo NEED1("type needs a string"); 1070cc4d3c30SLuigi Rizzo l = strlen(av[0]); 1071cc4d3c30SLuigi Rizzo if (l == 0 || l > 15) 1072cc4d3c30SLuigi Rizzo errx(1, "type %s too long\n", av[0]); 1073cc4d3c30SLuigi Rizzo strcpy(sch->name, av[0]); 1074cc4d3c30SLuigi Rizzo sch->oid.subtype = 0; /* use string */ 1075cc4d3c30SLuigi Rizzo ac--; av++; 1076cc4d3c30SLuigi Rizzo break; 1077cc4d3c30SLuigi Rizzo } 1078cc4d3c30SLuigi Rizzo 10794e9c8ae7SLuigi Rizzo case TOK_WEIGHT: 1080cc4d3c30SLuigi Rizzo NEED(fs, "weight is only for flowsets"); 1081cc4d3c30SLuigi Rizzo NEED1("weight needs argument\n"); 1082cc4d3c30SLuigi Rizzo fs->par[0] = strtol(av[0], &end, 0); 10834e9c8ae7SLuigi Rizzo ac--; av++; 10844e9c8ae7SLuigi Rizzo break; 10854e9c8ae7SLuigi Rizzo 1086cc4d3c30SLuigi Rizzo case TOK_LMAX: 1087cc4d3c30SLuigi Rizzo NEED(fs, "lmax is only for flowsets"); 1088cc4d3c30SLuigi Rizzo NEED1("lmax needs argument\n"); 1089cc4d3c30SLuigi Rizzo fs->par[1] = strtol(av[0], &end, 0); 1090cc4d3c30SLuigi Rizzo ac--; av++; 1091cc4d3c30SLuigi Rizzo break; 1092cc4d3c30SLuigi Rizzo 1093cc4d3c30SLuigi Rizzo case TOK_PRI: 1094cc4d3c30SLuigi Rizzo NEED(fs, "priority is only for flowsets"); 1095cc4d3c30SLuigi Rizzo NEED1("priority needs argument\n"); 1096cc4d3c30SLuigi Rizzo fs->par[2] = strtol(av[0], &end, 0); 1097cc4d3c30SLuigi Rizzo ac--; av++; 1098cc4d3c30SLuigi Rizzo break; 1099cc4d3c30SLuigi Rizzo 1100cc4d3c30SLuigi Rizzo case TOK_SCHED: 11014e9c8ae7SLuigi Rizzo case TOK_PIPE: 1102cc4d3c30SLuigi Rizzo NEED(fs, "pipe/sched"); 1103cc4d3c30SLuigi Rizzo NEED1("pipe/link/sched needs number\n"); 1104cc4d3c30SLuigi Rizzo fs->sched_nr = strtoul(av[0], &end, 0); 11054e9c8ae7SLuigi Rizzo ac--; av++; 11064e9c8ae7SLuigi Rizzo break; 11074e9c8ae7SLuigi Rizzo 1108cc4d3c30SLuigi Rizzo case TOK_PROFILE: 1109cc4d3c30SLuigi Rizzo NEED((!pf), "profile already set"); 1110cc4d3c30SLuigi Rizzo NEED(p, "profile"); 1111cc4d3c30SLuigi Rizzo { 11124bb7ae9dSLuigi Rizzo NEED1("extra delay needs the file name\n"); 1113cc4d3c30SLuigi Rizzo pf = o_next(&buf, sizeof(*pf), DN_PROFILE); 1114cc4d3c30SLuigi Rizzo load_extra_delays(av[0], pf, p); //XXX can't fail? 11154bb7ae9dSLuigi Rizzo --ac; ++av; 1116cc4d3c30SLuigi Rizzo } 11174bb7ae9dSLuigi Rizzo break; 11184bb7ae9dSLuigi Rizzo 11196882bf4dSOleg Bulyzhin case TOK_BURST: 1120cc4d3c30SLuigi Rizzo NEED(p, "burst"); 11216882bf4dSOleg Bulyzhin NEED1("burst needs argument\n"); 11226882bf4dSOleg Bulyzhin errno = 0; 1123cc4d3c30SLuigi Rizzo if (expand_number(av[0], (int64_t *)&p->burst) < 0) 11246882bf4dSOleg Bulyzhin if (errno != ERANGE) 11256882bf4dSOleg Bulyzhin errx(EX_DATAERR, 11266882bf4dSOleg Bulyzhin "burst: invalid argument"); 1127cc4d3c30SLuigi Rizzo if (errno || p->burst > (1ULL << 48) - 1) 11286882bf4dSOleg Bulyzhin errx(EX_DATAERR, 11296882bf4dSOleg Bulyzhin "burst: out of range (0..2^48-1)"); 11306882bf4dSOleg Bulyzhin ac--; av++; 11316882bf4dSOleg Bulyzhin break; 11326882bf4dSOleg Bulyzhin 11334e9c8ae7SLuigi Rizzo default: 11344e9c8ae7SLuigi Rizzo errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]); 11354e9c8ae7SLuigi Rizzo } 11364e9c8ae7SLuigi Rizzo } 1137cc4d3c30SLuigi Rizzo 1138cc4d3c30SLuigi Rizzo /* check validity of parameters */ 1139cc4d3c30SLuigi Rizzo if (p) { 1140cc4d3c30SLuigi Rizzo if (p->delay > 10000) 11414e9c8ae7SLuigi Rizzo errx(EX_DATAERR, "delay must be < 10000"); 1142cc4d3c30SLuigi Rizzo if (p->bandwidth == -1) 1143cc4d3c30SLuigi Rizzo p->bandwidth = 0; 11444e9c8ae7SLuigi Rizzo } 1145cc4d3c30SLuigi Rizzo if (fs) { 1146cc4d3c30SLuigi Rizzo /* XXX accept a 0 scheduler to keep the default */ 1147cc4d3c30SLuigi Rizzo if (fs->flags & DN_QSIZE_BYTES) { 11484e9c8ae7SLuigi Rizzo size_t len; 11494e9c8ae7SLuigi Rizzo long limit; 11504e9c8ae7SLuigi Rizzo 11514e9c8ae7SLuigi Rizzo len = sizeof(limit); 11524e9c8ae7SLuigi Rizzo if (sysctlbyname("net.inet.ip.dummynet.pipe_byte_limit", 11534e9c8ae7SLuigi Rizzo &limit, &len, NULL, 0) == -1) 11544e9c8ae7SLuigi Rizzo limit = 1024*1024; 1155cc4d3c30SLuigi Rizzo if (fs->qsize > limit) 11564e9c8ae7SLuigi Rizzo errx(EX_DATAERR, "queue size must be < %ldB", limit); 11574e9c8ae7SLuigi Rizzo } else { 11584e9c8ae7SLuigi Rizzo size_t len; 11594e9c8ae7SLuigi Rizzo long limit; 11604e9c8ae7SLuigi Rizzo 11614e9c8ae7SLuigi Rizzo len = sizeof(limit); 11624e9c8ae7SLuigi Rizzo if (sysctlbyname("net.inet.ip.dummynet.pipe_slot_limit", 11634e9c8ae7SLuigi Rizzo &limit, &len, NULL, 0) == -1) 11644e9c8ae7SLuigi Rizzo limit = 100; 1165cc4d3c30SLuigi Rizzo if (fs->qsize > limit) 11664e9c8ae7SLuigi Rizzo errx(EX_DATAERR, "2 <= queue size <= %ld", limit); 11674e9c8ae7SLuigi Rizzo } 1168cc4d3c30SLuigi Rizzo 1169cc4d3c30SLuigi Rizzo if (fs->flags & DN_IS_RED) { 11704e9c8ae7SLuigi Rizzo size_t len; 11714e9c8ae7SLuigi Rizzo int lookup_depth, avg_pkt_size; 1172cc4d3c30SLuigi Rizzo double w_q; 11734e9c8ae7SLuigi Rizzo 1174cc4d3c30SLuigi Rizzo if (fs->min_th >= fs->max_th) 11754e9c8ae7SLuigi Rizzo errx(EX_DATAERR, "min_th %d must be < than max_th %d", 1176cc4d3c30SLuigi Rizzo fs->min_th, fs->max_th); 1177cc4d3c30SLuigi Rizzo if (fs->max_th == 0) 11784e9c8ae7SLuigi Rizzo errx(EX_DATAERR, "max_th must be > 0"); 11794e9c8ae7SLuigi Rizzo 11804e9c8ae7SLuigi Rizzo len = sizeof(int); 11814e9c8ae7SLuigi Rizzo if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth", 11824e9c8ae7SLuigi Rizzo &lookup_depth, &len, NULL, 0) == -1) 1183cc4d3c30SLuigi Rizzo lookup_depth = 256; 11844e9c8ae7SLuigi Rizzo if (lookup_depth == 0) 11854e9c8ae7SLuigi Rizzo errx(EX_DATAERR, "net.inet.ip.dummynet.red_lookup_depth" 11864e9c8ae7SLuigi Rizzo " must be greater than zero"); 11874e9c8ae7SLuigi Rizzo 11884e9c8ae7SLuigi Rizzo len = sizeof(int); 11894e9c8ae7SLuigi Rizzo if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size", 11904e9c8ae7SLuigi Rizzo &avg_pkt_size, &len, NULL, 0) == -1) 1191cc4d3c30SLuigi Rizzo avg_pkt_size = 512; 11924e9c8ae7SLuigi Rizzo 11934e9c8ae7SLuigi Rizzo if (avg_pkt_size == 0) 11944e9c8ae7SLuigi Rizzo errx(EX_DATAERR, 11954e9c8ae7SLuigi Rizzo "net.inet.ip.dummynet.red_avg_pkt_size must" 11964e9c8ae7SLuigi Rizzo " be greater than zero"); 11974e9c8ae7SLuigi Rizzo 11984e9c8ae7SLuigi Rizzo /* 11994e9c8ae7SLuigi Rizzo * Ticks needed for sending a medium-sized packet. 12004e9c8ae7SLuigi Rizzo * Unfortunately, when we are configuring a WF2Q+ queue, we 12014e9c8ae7SLuigi Rizzo * do not have bandwidth information, because that is stored 12024e9c8ae7SLuigi Rizzo * in the parent pipe, and also we have multiple queues 12034e9c8ae7SLuigi Rizzo * competing for it. So we set s=0, which is not very 12044e9c8ae7SLuigi Rizzo * correct. But on the other hand, why do we want RED with 12054e9c8ae7SLuigi Rizzo * WF2Q+ ? 12064e9c8ae7SLuigi Rizzo */ 1207cc4d3c30SLuigi Rizzo #if 0 12084e9c8ae7SLuigi Rizzo if (p.bandwidth==0) /* this is a WF2Q+ queue */ 12094e9c8ae7SLuigi Rizzo s = 0; 12104e9c8ae7SLuigi Rizzo else 12114e9c8ae7SLuigi Rizzo s = (double)ck.hz * avg_pkt_size * 8 / p.bandwidth; 1212cc4d3c30SLuigi Rizzo #endif 12134e9c8ae7SLuigi Rizzo /* 12144e9c8ae7SLuigi Rizzo * max idle time (in ticks) before avg queue size becomes 0. 12154e9c8ae7SLuigi Rizzo * NOTA: (3/w_q) is approx the value x so that 12164e9c8ae7SLuigi Rizzo * (1-w_q)^x < 10^-3. 12174e9c8ae7SLuigi Rizzo */ 1218cc4d3c30SLuigi Rizzo w_q = ((double)fs->w_q) / (1 << SCALE_RED); 1219cc4d3c30SLuigi Rizzo #if 0 // go in kernel 12204e9c8ae7SLuigi Rizzo idle = s * 3. / w_q; 1221cc4d3c30SLuigi Rizzo fs->lookup_step = (int)idle / lookup_depth; 1222cc4d3c30SLuigi Rizzo if (!fs->lookup_step) 1223cc4d3c30SLuigi Rizzo fs->lookup_step = 1; 12244e9c8ae7SLuigi Rizzo weight = 1 - w_q; 1225cc4d3c30SLuigi Rizzo for (t = fs->lookup_step; t > 1; --t) 12264e9c8ae7SLuigi Rizzo weight *= 1 - w_q; 1227cc4d3c30SLuigi Rizzo fs->lookup_weight = (int)(weight * (1 << SCALE_RED)); 1228cc4d3c30SLuigi Rizzo #endif 12294e9c8ae7SLuigi Rizzo } 12304bb7ae9dSLuigi Rizzo } 12314bb7ae9dSLuigi Rizzo 1232cc4d3c30SLuigi Rizzo i = do_cmd(IP_DUMMYNET3, base, (char *)buf - (char *)base); 1233cc4d3c30SLuigi Rizzo 12344e9c8ae7SLuigi Rizzo if (i) 12354e9c8ae7SLuigi Rizzo err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE"); 12364e9c8ae7SLuigi Rizzo } 1237cc4d3c30SLuigi Rizzo 1238cc4d3c30SLuigi Rizzo void 1239cc4d3c30SLuigi Rizzo dummynet_flush(void) 1240cc4d3c30SLuigi Rizzo { 1241cc4d3c30SLuigi Rizzo struct dn_id oid; 1242cc4d3c30SLuigi Rizzo oid_fill(&oid, sizeof(oid), DN_CMD_FLUSH, DN_API_VERSION); 1243cc4d3c30SLuigi Rizzo do_cmd(IP_DUMMYNET3, &oid, oid.len); 1244cc4d3c30SLuigi Rizzo } 1245cc4d3c30SLuigi Rizzo 12465007b59fSLuigi Rizzo /* Parse input for 'ipfw [pipe|sched|queue] show [range list]' 12475007b59fSLuigi Rizzo * Returns the number of ranges, and possibly stores them 12485007b59fSLuigi Rizzo * in the array v of size len. 12495007b59fSLuigi Rizzo */ 12505007b59fSLuigi Rizzo static int 12515007b59fSLuigi Rizzo parse_range(int ac, char *av[], uint32_t *v, int len) 12525007b59fSLuigi Rizzo { 12535007b59fSLuigi Rizzo int n = 0; 12545007b59fSLuigi Rizzo char *endptr, *s; 12555007b59fSLuigi Rizzo uint32_t base[2]; 12565007b59fSLuigi Rizzo 12575007b59fSLuigi Rizzo if (v == NULL || len < 2) { 12585007b59fSLuigi Rizzo v = base; 12595007b59fSLuigi Rizzo len = 2; 12605007b59fSLuigi Rizzo } 12615007b59fSLuigi Rizzo 12625007b59fSLuigi Rizzo for (s = *av; s != NULL; av++, ac--) { 12635007b59fSLuigi Rizzo v[0] = strtoul(s, &endptr, 10); 12645007b59fSLuigi Rizzo v[1] = (*endptr != '-') ? v[0] : 12655007b59fSLuigi Rizzo strtoul(endptr+1, &endptr, 10); 12665007b59fSLuigi Rizzo if (*endptr == '\0') { /* prepare for next round */ 12675007b59fSLuigi Rizzo s = (ac > 0) ? *(av+1) : NULL; 12685007b59fSLuigi Rizzo } else { 12695007b59fSLuigi Rizzo if (*endptr != ',') { 12705007b59fSLuigi Rizzo warn("invalid number: %s", s); 12715007b59fSLuigi Rizzo s = ++endptr; 12725007b59fSLuigi Rizzo continue; 12735007b59fSLuigi Rizzo } 12745007b59fSLuigi Rizzo /* continue processing from here */ 12755007b59fSLuigi Rizzo s = ++endptr; 12765007b59fSLuigi Rizzo ac++; 12775007b59fSLuigi Rizzo av--; 12785007b59fSLuigi Rizzo } 12795007b59fSLuigi Rizzo if (v[1] < v[0] || 12805007b59fSLuigi Rizzo v[1] < 0 || v[1] >= DN_MAX_ID-1 || 12815007b59fSLuigi Rizzo v[0] < 0 || v[1] >= DN_MAX_ID-1) { 12825007b59fSLuigi Rizzo continue; /* invalid entry */ 12835007b59fSLuigi Rizzo } 12845007b59fSLuigi Rizzo n++; 12855007b59fSLuigi Rizzo /* translate if 'pipe list' */ 12865007b59fSLuigi Rizzo if (co.do_pipe == 1) { 12875007b59fSLuigi Rizzo v[0] += DN_MAX_ID; 12885007b59fSLuigi Rizzo v[1] += DN_MAX_ID; 12895007b59fSLuigi Rizzo } 12905007b59fSLuigi Rizzo v = (n*2 < len) ? v + 2 : base; 12915007b59fSLuigi Rizzo } 12925007b59fSLuigi Rizzo return n; 12935007b59fSLuigi Rizzo } 12945007b59fSLuigi Rizzo 1295cc4d3c30SLuigi Rizzo /* main entry point for dummynet list functions. co.do_pipe indicates 1296cc4d3c30SLuigi Rizzo * which function we want to support. 12975007b59fSLuigi Rizzo * av may contain filtering arguments, either individual entries 12985007b59fSLuigi Rizzo * or ranges, or lists (space or commas are valid separators). 12995007b59fSLuigi Rizzo * Format for a range can be n1-n2 or n3 n4 n5 ... 13005007b59fSLuigi Rizzo * In a range n1 must be <= n2, otherwise the range is ignored. 13015007b59fSLuigi Rizzo * A number 'n4' is translate in a range 'n4-n4' 13025007b59fSLuigi Rizzo * All number must be > 0 and < DN_MAX_ID-1 1303cc4d3c30SLuigi Rizzo */ 1304cc4d3c30SLuigi Rizzo void 1305cc4d3c30SLuigi Rizzo dummynet_list(int ac, char *av[], int show_counters) 1306cc4d3c30SLuigi Rizzo { 13075007b59fSLuigi Rizzo struct dn_id *oid, *x = NULL; 13085007b59fSLuigi Rizzo int ret, i, l; 13095007b59fSLuigi Rizzo int n; /* # of ranges */ 13105007b59fSLuigi Rizzo int buflen; 13115007b59fSLuigi Rizzo int max_size; /* largest obj passed up */ 1312cc4d3c30SLuigi Rizzo 13135007b59fSLuigi Rizzo ac--; 13145007b59fSLuigi Rizzo av++; /* skip 'list' | 'show' word */ 13155007b59fSLuigi Rizzo 13165007b59fSLuigi Rizzo n = parse_range(ac, av, NULL, 0); /* Count # of ranges. */ 13175007b59fSLuigi Rizzo 13185007b59fSLuigi Rizzo /* Allocate space to store ranges */ 13195007b59fSLuigi Rizzo l = sizeof(*oid) + sizeof(uint32_t) * n * 2; 13205007b59fSLuigi Rizzo oid = safe_calloc(1, l); 13215007b59fSLuigi Rizzo oid_fill(oid, l, DN_CMD_GET, DN_API_VERSION); 13225007b59fSLuigi Rizzo 13235007b59fSLuigi Rizzo if (n > 0) /* store ranges in idx */ 13245007b59fSLuigi Rizzo parse_range(ac, av, (uint32_t *)(oid + 1), n*2); 13255007b59fSLuigi Rizzo /* 13265007b59fSLuigi Rizzo * Compute the size of the largest object returned. If the 13275007b59fSLuigi Rizzo * response leaves at least this much spare space in the 13285007b59fSLuigi Rizzo * buffer, then surely the response is complete; otherwise 13295007b59fSLuigi Rizzo * there might be a risk of truncation and we will need to 13305007b59fSLuigi Rizzo * retry with a larger buffer. 13315007b59fSLuigi Rizzo * XXX don't bother with smaller structs. 13325007b59fSLuigi Rizzo */ 13335007b59fSLuigi Rizzo max_size = sizeof(struct dn_fs); 13345007b59fSLuigi Rizzo if (max_size < sizeof(struct dn_sch)) 13355007b59fSLuigi Rizzo max_size = sizeof(struct dn_sch); 13365007b59fSLuigi Rizzo if (max_size < sizeof(struct dn_flow)) 13375007b59fSLuigi Rizzo max_size = sizeof(struct dn_flow); 13385007b59fSLuigi Rizzo 1339cc4d3c30SLuigi Rizzo switch (co.do_pipe) { 1340cc4d3c30SLuigi Rizzo case 1: 13415007b59fSLuigi Rizzo oid->subtype = DN_LINK; /* list pipe */ 1342cc4d3c30SLuigi Rizzo break; 1343cc4d3c30SLuigi Rizzo case 2: 13445007b59fSLuigi Rizzo oid->subtype = DN_FS; /* list queue */ 1345cc4d3c30SLuigi Rizzo break; 1346cc4d3c30SLuigi Rizzo case 3: 13475007b59fSLuigi Rizzo oid->subtype = DN_SCH; /* list sched */ 1348cc4d3c30SLuigi Rizzo break; 1349cc4d3c30SLuigi Rizzo } 1350f10f583fSLuigi Rizzo 13515007b59fSLuigi Rizzo /* 13525007b59fSLuigi Rizzo * Ask the kernel an estimate of the required space (result 13535007b59fSLuigi Rizzo * in oid.id), unless we are requesting a subset of objects, 13545007b59fSLuigi Rizzo * in which case the kernel does not give an exact answer. 13555007b59fSLuigi Rizzo * In any case, space might grow in the meantime due to the 13565007b59fSLuigi Rizzo * creation of new queues, so we must be prepared to retry. 1357f10f583fSLuigi Rizzo */ 13585007b59fSLuigi Rizzo if (n > 0) { 13595007b59fSLuigi Rizzo buflen = 4*1024; 13605007b59fSLuigi Rizzo } else { 13615007b59fSLuigi Rizzo ret = do_cmd(-IP_DUMMYNET3, oid, (uintptr_t)&l); 13625007b59fSLuigi Rizzo if (ret != 0 || oid->id <= sizeof(*oid)) 13635007b59fSLuigi Rizzo goto done; 13645007b59fSLuigi Rizzo buflen = oid->id + max_size; 13655007b59fSLuigi Rizzo oid->len = sizeof(*oid); /* restore */ 1366f10f583fSLuigi Rizzo } 13675007b59fSLuigi Rizzo /* Try a few times, until the buffer fits */ 13685007b59fSLuigi Rizzo for (i = 0; i < 20; i++) { 13695007b59fSLuigi Rizzo l = buflen; 13705007b59fSLuigi Rizzo x = safe_realloc(x, l); 13715007b59fSLuigi Rizzo bcopy(oid, x, oid->len); 13725007b59fSLuigi Rizzo ret = do_cmd(-IP_DUMMYNET3, x, (uintptr_t)&l); 13735007b59fSLuigi Rizzo if (ret != 0 || x->id <= sizeof(*oid)) 13745007b59fSLuigi Rizzo goto done; /* no response */ 13755007b59fSLuigi Rizzo if (l + max_size <= buflen) 13765007b59fSLuigi Rizzo break; /* ok */ 13775007b59fSLuigi Rizzo buflen *= 2; /* double for next attempt */ 13785007b59fSLuigi Rizzo } 1379cc4d3c30SLuigi Rizzo list_pipes(x, O_NEXT(x, l)); 13805007b59fSLuigi Rizzo done: 13815007b59fSLuigi Rizzo if (x) 1382cc4d3c30SLuigi Rizzo free(x); 13835007b59fSLuigi Rizzo free(oid); 1384cc4d3c30SLuigi Rizzo } 1385