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(" " 1444e9c8ae7SLuigi Rizzo "mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n", 145cc4d3c30SLuigi Rizzo id->proto, 146cc4d3c30SLuigi Rizzo id->src_ip, id->src_port, 147cc4d3c30SLuigi Rizzo id->dst_ip, id->dst_port); 1484e9c8ae7SLuigi Rizzo 1494e9c8ae7SLuigi Rizzo printf("BKT Prot ___Source IP/port____ " 1504e9c8ae7SLuigi Rizzo "____Dest. IP/port____ " 1514e9c8ae7SLuigi Rizzo "Tot_pkt/bytes Pkt/Byte Drp\n"); 152cc4d3c30SLuigi Rizzo } else { 153cc4d3c30SLuigi Rizzo char buf[255]; 1544e9c8ae7SLuigi Rizzo printf("\n mask: proto: 0x%02x, flow_id: 0x%08x, ", 155cc4d3c30SLuigi Rizzo id->proto, id->flow_id6); 156cc4d3c30SLuigi Rizzo inet_ntop(AF_INET6, &(id->src_ip6), buf, sizeof(buf)); 157cc4d3c30SLuigi Rizzo printf("%s/0x%04x -> ", buf, id->src_port); 158cc4d3c30SLuigi Rizzo inet_ntop(AF_INET6, &(id->dst_ip6), buf, sizeof(buf)); 159cc4d3c30SLuigi Rizzo printf("%s/0x%04x\n", buf, id->dst_port); 1604e9c8ae7SLuigi Rizzo 1614e9c8ae7SLuigi Rizzo printf("BKT ___Prot___ _flow-id_ " 1624e9c8ae7SLuigi Rizzo "______________Source IPv6/port_______________ " 1634e9c8ae7SLuigi Rizzo "_______________Dest. IPv6/port_______________ " 1644e9c8ae7SLuigi Rizzo "Tot_pkt/bytes Pkt/Byte Drp\n"); 1654e9c8ae7SLuigi Rizzo } 1664e9c8ae7SLuigi Rizzo } 1674e9c8ae7SLuigi Rizzo 1684e9c8ae7SLuigi Rizzo static void 169cc4d3c30SLuigi Rizzo list_flow(struct dn_flow *ni) 170cc4d3c30SLuigi Rizzo { 171cc4d3c30SLuigi Rizzo char buff[255]; 172cc4d3c30SLuigi Rizzo struct protoent *pe; 173cc4d3c30SLuigi Rizzo struct in_addr ina; 174cc4d3c30SLuigi Rizzo struct ipfw_flow_id *id = &ni->fid; 175cc4d3c30SLuigi Rizzo 176cc4d3c30SLuigi Rizzo pe = getprotobynumber(id->proto); 177cc4d3c30SLuigi Rizzo /* XXX: Should check for IPv4 flows */ 178cc4d3c30SLuigi Rizzo printf("%3u ", (ni->oid.id) & 0xff); 179cc4d3c30SLuigi Rizzo if (!IS_IP6_FLOW_ID(id)) { 180cc4d3c30SLuigi Rizzo if (pe) 181cc4d3c30SLuigi Rizzo printf("%-4s ", pe->p_name); 182cc4d3c30SLuigi Rizzo else 183cc4d3c30SLuigi Rizzo printf("%4u ", id->proto); 184cc4d3c30SLuigi Rizzo ina.s_addr = htonl(id->src_ip); 185cc4d3c30SLuigi Rizzo printf("%15s/%-5d ", 186cc4d3c30SLuigi Rizzo inet_ntoa(ina), id->src_port); 187cc4d3c30SLuigi Rizzo ina.s_addr = htonl(id->dst_ip); 188cc4d3c30SLuigi Rizzo printf("%15s/%-5d ", 189cc4d3c30SLuigi Rizzo inet_ntoa(ina), id->dst_port); 190cc4d3c30SLuigi Rizzo } else { 191cc4d3c30SLuigi Rizzo /* Print IPv6 flows */ 192cc4d3c30SLuigi Rizzo if (pe != NULL) 193cc4d3c30SLuigi Rizzo printf("%9s ", pe->p_name); 194cc4d3c30SLuigi Rizzo else 195cc4d3c30SLuigi Rizzo printf("%9u ", id->proto); 196cc4d3c30SLuigi Rizzo printf("%7d %39s/%-5d ", id->flow_id6, 197cc4d3c30SLuigi Rizzo inet_ntop(AF_INET6, &(id->src_ip6), buff, sizeof(buff)), 198cc4d3c30SLuigi Rizzo id->src_port); 199cc4d3c30SLuigi Rizzo printf(" %39s/%-5d ", 200cc4d3c30SLuigi Rizzo inet_ntop(AF_INET6, &(id->dst_ip6), buff, sizeof(buff)), 201cc4d3c30SLuigi Rizzo id->dst_port); 202cc4d3c30SLuigi Rizzo } 203cc4d3c30SLuigi Rizzo printf("%4llu %8llu %2u %4u %3u\n", 204cc4d3c30SLuigi Rizzo align_uint64(&ni->tot_pkts), 205cc4d3c30SLuigi Rizzo align_uint64(&ni->tot_bytes), 206cc4d3c30SLuigi Rizzo ni->length, ni->len_bytes, ni->drops); 207cc4d3c30SLuigi Rizzo } 208cc4d3c30SLuigi Rizzo 209cc4d3c30SLuigi Rizzo static void 210cc4d3c30SLuigi Rizzo print_flowset_parms(struct dn_fs *fs, char *prefix) 2114e9c8ae7SLuigi Rizzo { 2124e9c8ae7SLuigi Rizzo int l; 2134e9c8ae7SLuigi Rizzo char qs[30]; 2144e9c8ae7SLuigi Rizzo char plr[30]; 2154e9c8ae7SLuigi Rizzo char red[90]; /* Display RED parameters */ 2164e9c8ae7SLuigi Rizzo 2174e9c8ae7SLuigi Rizzo l = fs->qsize; 218cc4d3c30SLuigi Rizzo if (fs->flags & DN_QSIZE_BYTES) { 2194e9c8ae7SLuigi Rizzo if (l >= 8192) 2204e9c8ae7SLuigi Rizzo sprintf(qs, "%d KB", l / 1024); 2214e9c8ae7SLuigi Rizzo else 2224e9c8ae7SLuigi Rizzo sprintf(qs, "%d B", l); 2234e9c8ae7SLuigi Rizzo } else 2244e9c8ae7SLuigi Rizzo sprintf(qs, "%3d sl.", l); 2254e9c8ae7SLuigi Rizzo if (fs->plr) 2264e9c8ae7SLuigi Rizzo sprintf(plr, "plr %f", 1.0 * fs->plr / (double)(0x7fffffff)); 2274e9c8ae7SLuigi Rizzo else 2284e9c8ae7SLuigi Rizzo plr[0] = '\0'; 229cc4d3c30SLuigi Rizzo 230cc4d3c30SLuigi Rizzo if (fs->flags & DN_IS_RED) /* RED parameters */ 2314e9c8ae7SLuigi Rizzo sprintf(red, 2324e9c8ae7SLuigi Rizzo "\n\t %cRED w_q %f min_th %d max_th %d max_p %f", 233cc4d3c30SLuigi Rizzo (fs->flags & DN_IS_GENTLE_RED) ? 'G' : ' ', 2344e9c8ae7SLuigi Rizzo 1.0 * fs->w_q / (double)(1 << SCALE_RED), 235cc4d3c30SLuigi Rizzo fs->min_th, 236cc4d3c30SLuigi Rizzo fs->max_th, 2374e9c8ae7SLuigi Rizzo 1.0 * fs->max_p / (double)(1 << SCALE_RED)); 2384e9c8ae7SLuigi Rizzo else 2394e9c8ae7SLuigi Rizzo sprintf(red, "droptail"); 2404e9c8ae7SLuigi Rizzo 241cc4d3c30SLuigi Rizzo if (prefix[0]) { 2424e9c8ae7SLuigi Rizzo printf("%s %s%s %d queues (%d buckets) %s\n", 243cc4d3c30SLuigi Rizzo prefix, qs, plr, fs->oid.id, fs->buckets, red); 244cc4d3c30SLuigi Rizzo prefix[0] = '\0'; 245cc4d3c30SLuigi Rizzo } else { 246cc4d3c30SLuigi Rizzo printf("q%05d %s%s %d flows (%d buckets) sched %d " 247cc4d3c30SLuigi Rizzo "weight %d lmax %d pri %d %s\n", 248cc4d3c30SLuigi Rizzo fs->fs_nr, qs, plr, fs->oid.id, fs->buckets, 249cc4d3c30SLuigi Rizzo fs->sched_nr, fs->par[0], fs->par[1], fs->par[2], red); 250cc4d3c30SLuigi Rizzo if (fs->flags & DN_HAVE_MASK) 251cc4d3c30SLuigi Rizzo print_mask(&fs->flow_mask); 252cc4d3c30SLuigi Rizzo } 2534e9c8ae7SLuigi Rizzo } 2544e9c8ae7SLuigi Rizzo 2554bb7ae9dSLuigi Rizzo static void 256cc4d3c30SLuigi Rizzo print_extra_delay_parms(struct dn_profile *p) 2574bb7ae9dSLuigi Rizzo { 2584bb7ae9dSLuigi Rizzo double loss; 2594bb7ae9dSLuigi Rizzo if (p->samples_no <= 0) 2604bb7ae9dSLuigi Rizzo return; 2614bb7ae9dSLuigi Rizzo 2624bb7ae9dSLuigi Rizzo loss = p->loss_level; 2634bb7ae9dSLuigi Rizzo loss /= p->samples_no; 2646882bf4dSOleg Bulyzhin printf("\t profile: name \"%s\" loss %f samples %d\n", 2656882bf4dSOleg Bulyzhin p->name, loss, p->samples_no); 2664bb7ae9dSLuigi Rizzo } 2674bb7ae9dSLuigi Rizzo 268cc4d3c30SLuigi Rizzo static void 269cc4d3c30SLuigi Rizzo flush_buf(char *buf) 2704e9c8ae7SLuigi Rizzo { 271cc4d3c30SLuigi Rizzo if (buf[0]) 272cc4d3c30SLuigi Rizzo printf("%s\n", buf); 273cc4d3c30SLuigi Rizzo buf[0] = '\0'; 274cc4d3c30SLuigi Rizzo } 2754e9c8ae7SLuigi Rizzo 276cc4d3c30SLuigi Rizzo /* 277cc4d3c30SLuigi Rizzo * generic list routine. We expect objects in a specific order, i.e. 278cc4d3c30SLuigi Rizzo * PIPES AND SCHEDULERS: 279cc4d3c30SLuigi Rizzo * link; scheduler; internal flowset if any; instances 280cc4d3c30SLuigi Rizzo * we can tell a pipe from the number. 281cc4d3c30SLuigi Rizzo * 282cc4d3c30SLuigi Rizzo * FLOWSETS: 283cc4d3c30SLuigi Rizzo * flowset; queues; 284cc4d3c30SLuigi Rizzo * link i (int queue); scheduler i; si(i) { flowsets() : queues } 285cc4d3c30SLuigi Rizzo */ 286cc4d3c30SLuigi Rizzo static void 287cc4d3c30SLuigi Rizzo list_pipes(struct dn_id *oid, struct dn_id *end) 288cc4d3c30SLuigi Rizzo { 289cc4d3c30SLuigi Rizzo char buf[160]; /* pending buffer */ 290cc4d3c30SLuigi Rizzo buf[0] = '\0'; 291cc4d3c30SLuigi Rizzo 292cc4d3c30SLuigi Rizzo for (; oid != end; oid = O_NEXT(oid, oid->len)) { 293cc4d3c30SLuigi Rizzo if (oid->len < sizeof(*oid)) 294cc4d3c30SLuigi Rizzo errx(1, "invalid oid len %d\n", oid->len); 295cc4d3c30SLuigi Rizzo 296cc4d3c30SLuigi Rizzo switch (oid->type) { 297cc4d3c30SLuigi Rizzo default: 298cc4d3c30SLuigi Rizzo flush_buf(buf); 299cc4d3c30SLuigi Rizzo printf("unrecognized object %d size %d\n", oid->type, oid->len); 300cc4d3c30SLuigi Rizzo break; 301cc4d3c30SLuigi Rizzo case DN_TEXT: /* list of attached flowsets */ 302cc4d3c30SLuigi Rizzo { 303cc4d3c30SLuigi Rizzo int i, l; 304cc4d3c30SLuigi Rizzo struct { 305cc4d3c30SLuigi Rizzo struct dn_id id; 306cc4d3c30SLuigi Rizzo uint32_t p[0]; 307cc4d3c30SLuigi Rizzo } *d = (void *)oid; 308cc4d3c30SLuigi Rizzo l = (oid->len - sizeof(*oid))/sizeof(d->p[0]); 309cc4d3c30SLuigi Rizzo if (l == 0) 310cc4d3c30SLuigi Rizzo break; 311cc4d3c30SLuigi Rizzo printf(" Children flowsets: "); 312cc4d3c30SLuigi Rizzo for (i = 0; i < l; i++) 313cc4d3c30SLuigi Rizzo printf("%u ", d->p[i]); 314cc4d3c30SLuigi Rizzo printf("\n"); 315cc4d3c30SLuigi Rizzo break; 316cc4d3c30SLuigi Rizzo } 317cc4d3c30SLuigi Rizzo case DN_CMD_GET: 318cc4d3c30SLuigi Rizzo if (co.verbose) 319cc4d3c30SLuigi Rizzo printf("answer for cmd %d, len %d\n", oid->type, oid->id); 320cc4d3c30SLuigi Rizzo break; 321cc4d3c30SLuigi Rizzo case DN_SCH: { 322cc4d3c30SLuigi Rizzo struct dn_sch *s = (struct dn_sch *)oid; 323cc4d3c30SLuigi Rizzo flush_buf(buf); 324cc4d3c30SLuigi Rizzo printf(" sched %d type %s flags 0x%x %d buckets %d active\n", 325cc4d3c30SLuigi Rizzo s->sched_nr, 326cc4d3c30SLuigi Rizzo s->name, s->flags, s->buckets, s->oid.id); 327cc4d3c30SLuigi Rizzo if (s->flags & DN_HAVE_MASK) 328cc4d3c30SLuigi Rizzo print_mask(&s->sched_mask); 329cc4d3c30SLuigi Rizzo } 330cc4d3c30SLuigi Rizzo break; 331cc4d3c30SLuigi Rizzo 332cc4d3c30SLuigi Rizzo case DN_FLOW: 333cc4d3c30SLuigi Rizzo list_flow((struct dn_flow *)oid); 334cc4d3c30SLuigi Rizzo break; 335cc4d3c30SLuigi Rizzo 336cc4d3c30SLuigi Rizzo case DN_LINK: { 337cc4d3c30SLuigi Rizzo struct dn_link *p = (struct dn_link *)oid; 3384e9c8ae7SLuigi Rizzo double b = p->bandwidth; 339cc4d3c30SLuigi Rizzo char bwbuf[30]; 3406882bf4dSOleg Bulyzhin char burst[5 + 7]; 3414e9c8ae7SLuigi Rizzo 342cc4d3c30SLuigi Rizzo /* This starts a new object so flush buffer */ 343cc4d3c30SLuigi Rizzo flush_buf(buf); 344cc4d3c30SLuigi Rizzo /* data rate */ 345cc4d3c30SLuigi Rizzo if (b == 0) 346cc4d3c30SLuigi Rizzo sprintf(bwbuf, "unlimited "); 3474e9c8ae7SLuigi Rizzo else if (b >= 1000000) 348cc4d3c30SLuigi Rizzo sprintf(bwbuf, "%7.3f Mbit/s", b/1000000); 3494e9c8ae7SLuigi Rizzo else if (b >= 1000) 350cc4d3c30SLuigi Rizzo sprintf(bwbuf, "%7.3f Kbit/s", b/1000); 3514e9c8ae7SLuigi Rizzo else 352cc4d3c30SLuigi Rizzo sprintf(bwbuf, "%7.3f bit/s ", b); 3534e9c8ae7SLuigi Rizzo 3546882bf4dSOleg Bulyzhin if (humanize_number(burst, sizeof(burst), p->burst, 355cc4d3c30SLuigi Rizzo "", HN_AUTOSCALE, 0) < 0 || co.verbose) 356cc4d3c30SLuigi Rizzo sprintf(burst, "%d", (int)p->burst); 357cc4d3c30SLuigi Rizzo sprintf(buf, "%05d: %s %4d ms burst %s", 358cc4d3c30SLuigi Rizzo p->link_nr % DN_MAX_ID, bwbuf, p->delay, burst); 3594e9c8ae7SLuigi Rizzo } 3604e9c8ae7SLuigi Rizzo break; 3614e9c8ae7SLuigi Rizzo 362cc4d3c30SLuigi Rizzo case DN_FS: 363cc4d3c30SLuigi Rizzo print_flowset_parms((struct dn_fs *)oid, buf); 364cc4d3c30SLuigi Rizzo break; 365cc4d3c30SLuigi Rizzo case DN_PROFILE: 366cc4d3c30SLuigi Rizzo flush_buf(buf); 367cc4d3c30SLuigi Rizzo print_extra_delay_parms((struct dn_profile *)oid); 3684e9c8ae7SLuigi Rizzo } 369cc4d3c30SLuigi Rizzo flush_buf(buf); // XXX does it really go here ? 3704e9c8ae7SLuigi Rizzo } 3714e9c8ae7SLuigi Rizzo } 3724e9c8ae7SLuigi Rizzo 3734e9c8ae7SLuigi Rizzo /* 374cc4d3c30SLuigi Rizzo * Delete pipe, queue or scheduler i 3754e9c8ae7SLuigi Rizzo */ 3764e9c8ae7SLuigi Rizzo int 377cc4d3c30SLuigi Rizzo ipfw_delete_pipe(int do_pipe, int i) 3784e9c8ae7SLuigi Rizzo { 379cc4d3c30SLuigi Rizzo struct { 380cc4d3c30SLuigi Rizzo struct dn_id oid; 381cc4d3c30SLuigi Rizzo uintptr_t a[1]; /* add more if we want a list */ 382cc4d3c30SLuigi Rizzo } cmd; 383cc4d3c30SLuigi Rizzo oid_fill((void *)&cmd, sizeof(cmd), DN_CMD_DELETE, DN_API_VERSION); 384cc4d3c30SLuigi Rizzo cmd.oid.subtype = (do_pipe == 1) ? DN_LINK : 385cc4d3c30SLuigi Rizzo ( (do_pipe == 2) ? DN_FS : DN_SCH); 386cc4d3c30SLuigi Rizzo cmd.a[0] = i; 387cc4d3c30SLuigi Rizzo i = do_cmd(IP_DUMMYNET3, &cmd, cmd.oid.len); 3884e9c8ae7SLuigi Rizzo if (i) { 3894e9c8ae7SLuigi Rizzo i = 1; 3904e9c8ae7SLuigi Rizzo warn("rule %u: setsockopt(IP_DUMMYNET_DEL)", i); 3914e9c8ae7SLuigi Rizzo } 3924e9c8ae7SLuigi Rizzo return i; 3934e9c8ae7SLuigi Rizzo } 3944e9c8ae7SLuigi Rizzo 3954bb7ae9dSLuigi Rizzo /* 3964bb7ae9dSLuigi Rizzo * Code to parse delay profiles. 3974bb7ae9dSLuigi Rizzo * 3984bb7ae9dSLuigi Rizzo * Some link types introduce extra delays in the transmission 3994bb7ae9dSLuigi Rizzo * of a packet, e.g. because of MAC level framing, contention on 4004bb7ae9dSLuigi Rizzo * the use of the channel, MAC level retransmissions and so on. 4014bb7ae9dSLuigi Rizzo * From our point of view, the channel is effectively unavailable 4024bb7ae9dSLuigi Rizzo * for this extra time, which is constant or variable depending 4034bb7ae9dSLuigi Rizzo * on the link type. Additionally, packets may be dropped after this 4044bb7ae9dSLuigi Rizzo * time (e.g. on a wireless link after too many retransmissions). 4054bb7ae9dSLuigi Rizzo * We can model the additional delay with an empirical curve 4064bb7ae9dSLuigi Rizzo * that represents its distribution. 4074bb7ae9dSLuigi Rizzo * 4084bb7ae9dSLuigi Rizzo * cumulative probability 4094bb7ae9dSLuigi Rizzo * 1.0 ^ 4104bb7ae9dSLuigi Rizzo * | 4114bb7ae9dSLuigi Rizzo * L +-- loss-level x 4124bb7ae9dSLuigi Rizzo * | ****** 4134bb7ae9dSLuigi Rizzo * | * 4144bb7ae9dSLuigi Rizzo * | ***** 4154bb7ae9dSLuigi Rizzo * | * 4164bb7ae9dSLuigi Rizzo * | ** 4174bb7ae9dSLuigi Rizzo * | * 4184bb7ae9dSLuigi Rizzo * +-------*-------------------> 4194bb7ae9dSLuigi Rizzo * delay 4204bb7ae9dSLuigi Rizzo * 4214bb7ae9dSLuigi Rizzo * The empirical curve may have both vertical and horizontal lines. 4224bb7ae9dSLuigi Rizzo * Vertical lines represent constant delay for a range of 4234bb7ae9dSLuigi Rizzo * probabilities; horizontal lines correspond to a discontinuty 424cc4d3c30SLuigi Rizzo * in the delay distribution: the link will use the largest delay 4254bb7ae9dSLuigi Rizzo * for a given probability. 4264bb7ae9dSLuigi Rizzo * 4274bb7ae9dSLuigi Rizzo * To pass the curve to dummynet, we must store the parameters 4284bb7ae9dSLuigi Rizzo * in a file as described below, and issue the command 4294bb7ae9dSLuigi Rizzo * 4304bb7ae9dSLuigi Rizzo * ipfw pipe <n> config ... bw XXX profile <filename> ... 4314bb7ae9dSLuigi Rizzo * 4324bb7ae9dSLuigi Rizzo * The file format is the following, with whitespace acting as 4334bb7ae9dSLuigi Rizzo * a separator and '#' indicating the beginning a comment: 4344bb7ae9dSLuigi Rizzo * 4354bb7ae9dSLuigi Rizzo * samples N 4364bb7ae9dSLuigi Rizzo * the number of samples used in the internal 4374bb7ae9dSLuigi Rizzo * representation (2..1024; default 100); 4384bb7ae9dSLuigi Rizzo * 4394bb7ae9dSLuigi Rizzo * loss-level L 4404bb7ae9dSLuigi Rizzo * The probability above which packets are lost. 4414bb7ae9dSLuigi Rizzo * (0.0 <= L <= 1.0, default 1.0 i.e. no loss); 4424bb7ae9dSLuigi Rizzo * 4434bb7ae9dSLuigi Rizzo * name identifier 4444bb7ae9dSLuigi Rizzo * Optional a name (listed by "ipfw pipe show") 4454bb7ae9dSLuigi Rizzo * to identify the distribution; 4464bb7ae9dSLuigi Rizzo * 4474bb7ae9dSLuigi Rizzo * "delay prob" | "prob delay" 4484bb7ae9dSLuigi Rizzo * One of these two lines is mandatory and defines 4494bb7ae9dSLuigi Rizzo * the format of the following lines with data points. 4504bb7ae9dSLuigi Rizzo * 4514bb7ae9dSLuigi Rizzo * XXX YYY 4524bb7ae9dSLuigi Rizzo * 2 or more lines representing points in the curve, 4534bb7ae9dSLuigi Rizzo * with either delay or probability first, according 4544bb7ae9dSLuigi Rizzo * to the chosen format. 4554bb7ae9dSLuigi Rizzo * The unit for delay is milliseconds. 4564bb7ae9dSLuigi Rizzo * 4574bb7ae9dSLuigi Rizzo * Data points does not need to be ordered or equal to the number 4584bb7ae9dSLuigi Rizzo * specified in the "samples" line. ipfw will sort and interpolate 4594bb7ae9dSLuigi Rizzo * the curve as needed. 4604bb7ae9dSLuigi Rizzo * 4614bb7ae9dSLuigi Rizzo * Example of a profile file: 4624bb7ae9dSLuigi Rizzo 4634bb7ae9dSLuigi Rizzo name bla_bla_bla 4644bb7ae9dSLuigi Rizzo samples 100 4654bb7ae9dSLuigi Rizzo loss-level 0.86 4664bb7ae9dSLuigi Rizzo prob delay 4674bb7ae9dSLuigi Rizzo 0 200 # minimum overhead is 200ms 4684bb7ae9dSLuigi Rizzo 0.5 200 4694bb7ae9dSLuigi Rizzo 0.5 300 4704bb7ae9dSLuigi Rizzo 0.8 1000 4714bb7ae9dSLuigi Rizzo 0.9 1300 4724bb7ae9dSLuigi Rizzo 1 1300 4734bb7ae9dSLuigi Rizzo 4744bb7ae9dSLuigi Rizzo * Internally, we will convert the curve to a fixed number of 4754bb7ae9dSLuigi Rizzo * samples, and when it is time to transmit a packet we will 4764bb7ae9dSLuigi Rizzo * model the extra delay as extra bits in the packet. 4774bb7ae9dSLuigi Rizzo * 4784bb7ae9dSLuigi Rizzo */ 4794bb7ae9dSLuigi Rizzo 4804bb7ae9dSLuigi Rizzo #define ED_MAX_LINE_LEN 256+ED_MAX_NAME_LEN 4814bb7ae9dSLuigi Rizzo #define ED_TOK_SAMPLES "samples" 4824bb7ae9dSLuigi Rizzo #define ED_TOK_LOSS "loss-level" 4834bb7ae9dSLuigi Rizzo #define ED_TOK_NAME "name" 4844bb7ae9dSLuigi Rizzo #define ED_TOK_DELAY "delay" 4854bb7ae9dSLuigi Rizzo #define ED_TOK_PROB "prob" 4867a459517SLuigi Rizzo #define ED_TOK_BW "bw" 4874bb7ae9dSLuigi Rizzo #define ED_SEPARATORS " \t\n" 4884bb7ae9dSLuigi Rizzo #define ED_MIN_SAMPLES_NO 2 4894bb7ae9dSLuigi Rizzo 4904bb7ae9dSLuigi Rizzo /* 4914bb7ae9dSLuigi Rizzo * returns 1 if s is a non-negative number, with at least one '.' 4924bb7ae9dSLuigi Rizzo */ 4934bb7ae9dSLuigi Rizzo static int 4944bb7ae9dSLuigi Rizzo is_valid_number(const char *s) 4954bb7ae9dSLuigi Rizzo { 4964bb7ae9dSLuigi Rizzo int i, dots_found = 0; 4974bb7ae9dSLuigi Rizzo int len = strlen(s); 4984bb7ae9dSLuigi Rizzo 4994bb7ae9dSLuigi Rizzo for (i = 0; i<len; ++i) 5004bb7ae9dSLuigi Rizzo if (!isdigit(s[i]) && (s[i] !='.' || ++dots_found > 1)) 5014bb7ae9dSLuigi Rizzo return 0; 5024bb7ae9dSLuigi Rizzo return 1; 5034bb7ae9dSLuigi Rizzo } 5044bb7ae9dSLuigi Rizzo 5057a459517SLuigi Rizzo /* 5067a459517SLuigi Rizzo * Take as input a string describing a bandwidth value 5077a459517SLuigi Rizzo * and return the numeric bandwidth value. 5087a459517SLuigi Rizzo * set clocking interface or bandwidth value 5097a459517SLuigi Rizzo */ 51001ab7632SLuigi Rizzo static void 5117a459517SLuigi Rizzo read_bandwidth(char *arg, int *bandwidth, char *if_name, int namelen) 5127a459517SLuigi Rizzo { 5137a459517SLuigi Rizzo if (*bandwidth != -1) 514cc4d3c30SLuigi Rizzo warnx("duplicate token, override bandwidth value!"); 5157a459517SLuigi Rizzo 5167a459517SLuigi Rizzo if (arg[0] >= 'a' && arg[0] <= 'z') { 517cc4d3c30SLuigi Rizzo if (!if_name) { 518cc4d3c30SLuigi Rizzo errx(1, "no if support"); 519cc4d3c30SLuigi Rizzo } 5207a459517SLuigi Rizzo if (namelen >= IFNAMSIZ) 5217a459517SLuigi Rizzo warn("interface name truncated"); 5227a459517SLuigi Rizzo namelen--; 5237a459517SLuigi Rizzo /* interface name */ 5247a459517SLuigi Rizzo strncpy(if_name, arg, namelen); 5257a459517SLuigi Rizzo if_name[namelen] = '\0'; 5267a459517SLuigi Rizzo *bandwidth = 0; 5277a459517SLuigi Rizzo } else { /* read bandwidth value */ 5287a459517SLuigi Rizzo int bw; 5297a459517SLuigi Rizzo char *end = NULL; 5307a459517SLuigi Rizzo 5317a459517SLuigi Rizzo bw = strtoul(arg, &end, 0); 5327a459517SLuigi Rizzo if (*end == 'K' || *end == 'k') { 5337a459517SLuigi Rizzo end++; 5347a459517SLuigi Rizzo bw *= 1000; 5357a459517SLuigi Rizzo } else if (*end == 'M') { 5367a459517SLuigi Rizzo end++; 5377a459517SLuigi Rizzo bw *= 1000000; 5387a459517SLuigi Rizzo } 5397a459517SLuigi Rizzo if ((*end == 'B' && 5407a459517SLuigi Rizzo _substrcmp2(end, "Bi", "Bit/s") != 0) || 5417a459517SLuigi Rizzo _substrcmp2(end, "by", "bytes") == 0) 5427a459517SLuigi Rizzo bw *= 8; 5437a459517SLuigi Rizzo 5447a459517SLuigi Rizzo if (bw < 0) 5457a459517SLuigi Rizzo errx(EX_DATAERR, "bandwidth too large"); 5467a459517SLuigi Rizzo 5477a459517SLuigi Rizzo *bandwidth = bw; 548cc4d3c30SLuigi Rizzo if (if_name) 5497a459517SLuigi Rizzo if_name[0] = '\0'; 5507a459517SLuigi Rizzo } 5517a459517SLuigi Rizzo } 5527a459517SLuigi Rizzo 5534bb7ae9dSLuigi Rizzo struct point { 5544bb7ae9dSLuigi Rizzo double prob; 5554bb7ae9dSLuigi Rizzo double delay; 5564bb7ae9dSLuigi Rizzo }; 5574bb7ae9dSLuigi Rizzo 55801ab7632SLuigi Rizzo static int 5594bb7ae9dSLuigi Rizzo compare_points(const void *vp1, const void *vp2) 5604bb7ae9dSLuigi Rizzo { 5614bb7ae9dSLuigi Rizzo const struct point *p1 = vp1; 5624bb7ae9dSLuigi Rizzo const struct point *p2 = vp2; 5634bb7ae9dSLuigi Rizzo double res = 0; 5644bb7ae9dSLuigi Rizzo 5654bb7ae9dSLuigi Rizzo res = p1->prob - p2->prob; 5664bb7ae9dSLuigi Rizzo if (res == 0) 5674bb7ae9dSLuigi Rizzo res = p1->delay - p2->delay; 5684bb7ae9dSLuigi Rizzo if (res < 0) 5694bb7ae9dSLuigi Rizzo return -1; 5704bb7ae9dSLuigi Rizzo else if (res > 0) 5714bb7ae9dSLuigi Rizzo return 1; 5724bb7ae9dSLuigi Rizzo else 5734bb7ae9dSLuigi Rizzo return 0; 5744bb7ae9dSLuigi Rizzo } 5754bb7ae9dSLuigi Rizzo 5764bb7ae9dSLuigi Rizzo #define ED_EFMT(s) EX_DATAERR,"error in %s at line %d: "#s,filename,lineno 5774bb7ae9dSLuigi Rizzo 5784bb7ae9dSLuigi Rizzo static void 579cc4d3c30SLuigi Rizzo load_extra_delays(const char *filename, struct dn_profile *p, 580cc4d3c30SLuigi Rizzo struct dn_link *link) 5814bb7ae9dSLuigi Rizzo { 5824bb7ae9dSLuigi Rizzo char line[ED_MAX_LINE_LEN]; 5834bb7ae9dSLuigi Rizzo FILE *f; 5844bb7ae9dSLuigi Rizzo int lineno = 0; 5854bb7ae9dSLuigi Rizzo int i; 5864bb7ae9dSLuigi Rizzo 5874bb7ae9dSLuigi Rizzo int samples = -1; 5884bb7ae9dSLuigi Rizzo double loss = -1.0; 5894bb7ae9dSLuigi Rizzo char profile_name[ED_MAX_NAME_LEN]; 5904bb7ae9dSLuigi Rizzo int delay_first = -1; 5914bb7ae9dSLuigi Rizzo int do_points = 0; 5924bb7ae9dSLuigi Rizzo struct point points[ED_MAX_SAMPLES_NO]; 5934bb7ae9dSLuigi Rizzo int points_no = 0; 5944bb7ae9dSLuigi Rizzo 595cc4d3c30SLuigi Rizzo /* XXX link never NULL? */ 596cc4d3c30SLuigi Rizzo p->link_nr = link->link_nr; 597cc4d3c30SLuigi Rizzo 5984bb7ae9dSLuigi Rizzo profile_name[0] = '\0'; 5994bb7ae9dSLuigi Rizzo f = fopen(filename, "r"); 6004bb7ae9dSLuigi Rizzo if (f == NULL) 6014bb7ae9dSLuigi Rizzo err(EX_UNAVAILABLE, "fopen: %s", filename); 6024bb7ae9dSLuigi Rizzo 6034bb7ae9dSLuigi Rizzo while (fgets(line, ED_MAX_LINE_LEN, f)) { /* read commands */ 6044bb7ae9dSLuigi Rizzo char *s, *cur = line, *name = NULL, *arg = NULL; 6054bb7ae9dSLuigi Rizzo 6064bb7ae9dSLuigi Rizzo ++lineno; 6074bb7ae9dSLuigi Rizzo 6084bb7ae9dSLuigi Rizzo /* parse the line */ 6094bb7ae9dSLuigi Rizzo while (cur) { 6104bb7ae9dSLuigi Rizzo s = strsep(&cur, ED_SEPARATORS); 6114bb7ae9dSLuigi Rizzo if (s == NULL || *s == '#') 6124bb7ae9dSLuigi Rizzo break; 6134bb7ae9dSLuigi Rizzo if (*s == '\0') 6144bb7ae9dSLuigi Rizzo continue; 6154bb7ae9dSLuigi Rizzo if (arg) 6164bb7ae9dSLuigi Rizzo errx(ED_EFMT("too many arguments")); 6174bb7ae9dSLuigi Rizzo if (name == NULL) 6184bb7ae9dSLuigi Rizzo name = s; 6194bb7ae9dSLuigi Rizzo else 6204bb7ae9dSLuigi Rizzo arg = s; 6214bb7ae9dSLuigi Rizzo } 6224bb7ae9dSLuigi Rizzo if (name == NULL) /* empty line */ 6234bb7ae9dSLuigi Rizzo continue; 6244bb7ae9dSLuigi Rizzo if (arg == NULL) 6254bb7ae9dSLuigi Rizzo errx(ED_EFMT("missing arg for %s"), name); 6264bb7ae9dSLuigi Rizzo 6274bb7ae9dSLuigi Rizzo if (!strcasecmp(name, ED_TOK_SAMPLES)) { 6284bb7ae9dSLuigi Rizzo if (samples > 0) 6294bb7ae9dSLuigi Rizzo errx(ED_EFMT("duplicate ``samples'' line")); 6304bb7ae9dSLuigi Rizzo if (atoi(arg) <=0) 6314bb7ae9dSLuigi Rizzo errx(ED_EFMT("invalid number of samples")); 6324bb7ae9dSLuigi Rizzo samples = atoi(arg); 6334bb7ae9dSLuigi Rizzo if (samples>ED_MAX_SAMPLES_NO) 6344bb7ae9dSLuigi Rizzo errx(ED_EFMT("too many samples, maximum is %d"), 6354bb7ae9dSLuigi Rizzo ED_MAX_SAMPLES_NO); 6364bb7ae9dSLuigi Rizzo do_points = 0; 6377a459517SLuigi Rizzo } else if (!strcasecmp(name, ED_TOK_BW)) { 638cc4d3c30SLuigi Rizzo char buf[IFNAMSIZ]; 639cc4d3c30SLuigi Rizzo read_bandwidth(arg, &link->bandwidth, buf, sizeof(buf)); 6404bb7ae9dSLuigi Rizzo } else if (!strcasecmp(name, ED_TOK_LOSS)) { 6414bb7ae9dSLuigi Rizzo if (loss != -1.0) 6424bb7ae9dSLuigi Rizzo errx(ED_EFMT("duplicated token: %s"), name); 6434bb7ae9dSLuigi Rizzo if (!is_valid_number(arg)) 6444bb7ae9dSLuigi Rizzo errx(ED_EFMT("invalid %s"), arg); 6454bb7ae9dSLuigi Rizzo loss = atof(arg); 6464bb7ae9dSLuigi Rizzo if (loss > 1) 6474bb7ae9dSLuigi Rizzo errx(ED_EFMT("%s greater than 1.0"), name); 6484bb7ae9dSLuigi Rizzo do_points = 0; 6494bb7ae9dSLuigi Rizzo } else if (!strcasecmp(name, ED_TOK_NAME)) { 6504bb7ae9dSLuigi Rizzo if (profile_name[0] != '\0') 6514bb7ae9dSLuigi Rizzo errx(ED_EFMT("duplicated token: %s"), name); 6524bb7ae9dSLuigi Rizzo strncpy(profile_name, arg, sizeof(profile_name) - 1); 6534bb7ae9dSLuigi Rizzo profile_name[sizeof(profile_name)-1] = '\0'; 6544bb7ae9dSLuigi Rizzo do_points = 0; 6554bb7ae9dSLuigi Rizzo } else if (!strcasecmp(name, ED_TOK_DELAY)) { 6564bb7ae9dSLuigi Rizzo if (do_points) 6574bb7ae9dSLuigi Rizzo errx(ED_EFMT("duplicated token: %s"), name); 6584bb7ae9dSLuigi Rizzo delay_first = 1; 6594bb7ae9dSLuigi Rizzo do_points = 1; 6604bb7ae9dSLuigi Rizzo } else if (!strcasecmp(name, ED_TOK_PROB)) { 6614bb7ae9dSLuigi Rizzo if (do_points) 6624bb7ae9dSLuigi Rizzo errx(ED_EFMT("duplicated token: %s"), name); 6634bb7ae9dSLuigi Rizzo delay_first = 0; 6644bb7ae9dSLuigi Rizzo do_points = 1; 6654bb7ae9dSLuigi Rizzo } else if (do_points) { 6664bb7ae9dSLuigi Rizzo if (!is_valid_number(name) || !is_valid_number(arg)) 6674bb7ae9dSLuigi Rizzo errx(ED_EFMT("invalid point found")); 6684bb7ae9dSLuigi Rizzo if (delay_first) { 6694bb7ae9dSLuigi Rizzo points[points_no].delay = atof(name); 6704bb7ae9dSLuigi Rizzo points[points_no].prob = atof(arg); 6714bb7ae9dSLuigi Rizzo } else { 6724bb7ae9dSLuigi Rizzo points[points_no].delay = atof(arg); 6734bb7ae9dSLuigi Rizzo points[points_no].prob = atof(name); 6744bb7ae9dSLuigi Rizzo } 6754bb7ae9dSLuigi Rizzo if (points[points_no].prob > 1.0) 6764bb7ae9dSLuigi Rizzo errx(ED_EFMT("probability greater than 1.0")); 6774bb7ae9dSLuigi Rizzo ++points_no; 6784bb7ae9dSLuigi Rizzo } else { 6794bb7ae9dSLuigi Rizzo errx(ED_EFMT("unrecognised command '%s'"), name); 6804bb7ae9dSLuigi Rizzo } 6814bb7ae9dSLuigi Rizzo } 6824bb7ae9dSLuigi Rizzo 683ac2e492bSAlexander Leidinger fclose (f); 684ac2e492bSAlexander Leidinger 6854bb7ae9dSLuigi Rizzo if (samples == -1) { 6864bb7ae9dSLuigi Rizzo warnx("'%s' not found, assuming 100", ED_TOK_SAMPLES); 6874bb7ae9dSLuigi Rizzo samples = 100; 6884bb7ae9dSLuigi Rizzo } 6894bb7ae9dSLuigi Rizzo 6904bb7ae9dSLuigi Rizzo if (loss == -1.0) { 6914bb7ae9dSLuigi Rizzo warnx("'%s' not found, assuming no loss", ED_TOK_LOSS); 6924bb7ae9dSLuigi Rizzo loss = 1; 6934bb7ae9dSLuigi Rizzo } 6944bb7ae9dSLuigi Rizzo 6954bb7ae9dSLuigi Rizzo /* make sure that there are enough points. */ 6964bb7ae9dSLuigi Rizzo if (points_no < ED_MIN_SAMPLES_NO) 6974bb7ae9dSLuigi Rizzo errx(ED_EFMT("too few samples, need at least %d"), 6984bb7ae9dSLuigi Rizzo ED_MIN_SAMPLES_NO); 6994bb7ae9dSLuigi Rizzo 7004bb7ae9dSLuigi Rizzo qsort(points, points_no, sizeof(struct point), compare_points); 7014bb7ae9dSLuigi Rizzo 7024bb7ae9dSLuigi Rizzo /* interpolation */ 7034bb7ae9dSLuigi Rizzo for (i = 0; i<points_no-1; ++i) { 7044bb7ae9dSLuigi Rizzo double y1 = points[i].prob * samples; 7054bb7ae9dSLuigi Rizzo double x1 = points[i].delay; 7064bb7ae9dSLuigi Rizzo double y2 = points[i+1].prob * samples; 7074bb7ae9dSLuigi Rizzo double x2 = points[i+1].delay; 7084bb7ae9dSLuigi Rizzo 709cc4d3c30SLuigi Rizzo int ix = y1; 7104bb7ae9dSLuigi Rizzo int stop = y2; 7114bb7ae9dSLuigi Rizzo 7124bb7ae9dSLuigi Rizzo if (x1 == x2) { 713cc4d3c30SLuigi Rizzo for (; ix<stop; ++ix) 714cc4d3c30SLuigi Rizzo p->samples[ix] = x1; 7154bb7ae9dSLuigi Rizzo } else { 7164bb7ae9dSLuigi Rizzo double m = (y2-y1)/(x2-x1); 7174bb7ae9dSLuigi Rizzo double c = y1 - m*x1; 718cc4d3c30SLuigi Rizzo for (; ix<stop ; ++ix) 719cc4d3c30SLuigi Rizzo p->samples[ix] = (ix - c)/m; 7204bb7ae9dSLuigi Rizzo } 7214bb7ae9dSLuigi Rizzo } 7224bb7ae9dSLuigi Rizzo p->samples_no = samples; 7234bb7ae9dSLuigi Rizzo p->loss_level = loss * samples; 7244bb7ae9dSLuigi Rizzo strncpy(p->name, profile_name, sizeof(p->name)); 7254bb7ae9dSLuigi Rizzo } 7264bb7ae9dSLuigi Rizzo 727cc4d3c30SLuigi Rizzo /* 728cc4d3c30SLuigi Rizzo * configuration of pipes, schedulers, flowsets. 729cc4d3c30SLuigi Rizzo * When we configure a new scheduler, an empty pipe is created, so: 730cc4d3c30SLuigi Rizzo * 731cc4d3c30SLuigi Rizzo * do_pipe = 1 -> "pipe N config ..." only for backward compatibility 732cc4d3c30SLuigi Rizzo * sched N+Delta type fifo sched_mask ... 733cc4d3c30SLuigi Rizzo * pipe N+Delta <parameters> 734cc4d3c30SLuigi Rizzo * flowset N+Delta pipe N+Delta (no parameters) 735cc4d3c30SLuigi Rizzo * sched N type wf2q+ sched_mask ... 736cc4d3c30SLuigi Rizzo * pipe N <parameters> 737cc4d3c30SLuigi Rizzo * 738cc4d3c30SLuigi Rizzo * do_pipe = 2 -> flowset N config 739cc4d3c30SLuigi Rizzo * flowset N parameters 740cc4d3c30SLuigi Rizzo * 741cc4d3c30SLuigi Rizzo * do_pipe = 3 -> sched N config 742cc4d3c30SLuigi Rizzo * sched N parameters (default no pipe) 743cc4d3c30SLuigi Rizzo * optional Pipe N config ... 744cc4d3c30SLuigi Rizzo * pipe ==> 745cc4d3c30SLuigi Rizzo */ 7464e9c8ae7SLuigi Rizzo void 7474e9c8ae7SLuigi Rizzo ipfw_config_pipe(int ac, char **av) 7484e9c8ae7SLuigi Rizzo { 749cc4d3c30SLuigi Rizzo int i, j; 7504e9c8ae7SLuigi Rizzo char *end; 7514e9c8ae7SLuigi Rizzo void *par = NULL; 752cc4d3c30SLuigi Rizzo struct dn_id *buf, *base; 753cc4d3c30SLuigi Rizzo struct dn_sch *sch = NULL; 754cc4d3c30SLuigi Rizzo struct dn_link *p = NULL; 755cc4d3c30SLuigi Rizzo struct dn_fs *fs = NULL; 756cc4d3c30SLuigi Rizzo struct dn_profile *pf = NULL; 757cc4d3c30SLuigi Rizzo struct ipfw_flow_id *mask = NULL; 758cc4d3c30SLuigi Rizzo int lmax; 759cc4d3c30SLuigi Rizzo uint32_t _foo = 0, *flags = &_foo , *buckets = &_foo; 7604e9c8ae7SLuigi Rizzo 761cc4d3c30SLuigi Rizzo /* 762cc4d3c30SLuigi Rizzo * allocate space for 1 header, 763cc4d3c30SLuigi Rizzo * 1 scheduler, 1 link, 1 flowset, 1 profile 764cc4d3c30SLuigi Rizzo */ 765cc4d3c30SLuigi Rizzo lmax = sizeof(struct dn_id); /* command header */ 766cc4d3c30SLuigi Rizzo lmax += sizeof(struct dn_sch) + sizeof(struct dn_link) + 767cc4d3c30SLuigi Rizzo sizeof(struct dn_fs) + sizeof(struct dn_profile); 7684e9c8ae7SLuigi Rizzo 7694e9c8ae7SLuigi Rizzo av++; ac--; 7704e9c8ae7SLuigi Rizzo /* Pipe number */ 7714e9c8ae7SLuigi Rizzo if (ac && isdigit(**av)) { 7724e9c8ae7SLuigi Rizzo i = atoi(*av); av++; ac--; 773cc4d3c30SLuigi Rizzo } else 774cc4d3c30SLuigi Rizzo i = -1; 775cc4d3c30SLuigi Rizzo if (i <= 0) 776cc4d3c30SLuigi Rizzo errx(EX_USAGE, "need a pipe/flowset/sched number"); 777cc4d3c30SLuigi Rizzo base = buf = safe_calloc(1, lmax); 778cc4d3c30SLuigi Rizzo /* all commands start with a 'CONFIGURE' and a version */ 779cc4d3c30SLuigi Rizzo o_next(&buf, sizeof(struct dn_id), DN_CMD_CONFIG); 780cc4d3c30SLuigi Rizzo base->id = DN_API_VERSION; 781cc4d3c30SLuigi Rizzo 782cc4d3c30SLuigi Rizzo switch (co.do_pipe) { 783cc4d3c30SLuigi Rizzo case 1: /* "pipe N config ..." */ 784cc4d3c30SLuigi Rizzo /* Allocate space for the WF2Q+ scheduler, its link 785cc4d3c30SLuigi Rizzo * and the FIFO flowset. Set the number, but leave 786cc4d3c30SLuigi Rizzo * the scheduler subtype and other parameters to 0 787cc4d3c30SLuigi Rizzo * so the kernel will use appropriate defaults. 788cc4d3c30SLuigi Rizzo * XXX todo: add a flag to record if a parameter 789cc4d3c30SLuigi Rizzo * is actually configured. 790cc4d3c30SLuigi Rizzo * If we do a 'pipe config' mask -> sched_mask. 791cc4d3c30SLuigi Rizzo * The FIFO scheduler and link are derived from the 792cc4d3c30SLuigi Rizzo * WF2Q+ one in the kernel. 793cc4d3c30SLuigi Rizzo */ 794cc4d3c30SLuigi Rizzo sch = o_next(&buf, sizeof(*sch), DN_SCH); 795cc4d3c30SLuigi Rizzo p = o_next(&buf, sizeof(*p), DN_LINK); 796cc4d3c30SLuigi Rizzo fs = o_next(&buf, sizeof(*fs), DN_FS); 797cc4d3c30SLuigi Rizzo 798cc4d3c30SLuigi Rizzo sch->sched_nr = i; 799cc4d3c30SLuigi Rizzo sch->oid.subtype = 0; /* defaults to WF2Q+ */ 800cc4d3c30SLuigi Rizzo mask = &sch->sched_mask; 801cc4d3c30SLuigi Rizzo flags = &sch->flags; 802cc4d3c30SLuigi Rizzo buckets = &sch->buckets; 803cc4d3c30SLuigi Rizzo *flags |= DN_PIPE_CMD; 804cc4d3c30SLuigi Rizzo 805cc4d3c30SLuigi Rizzo p->link_nr = i; 806cc4d3c30SLuigi Rizzo 807cc4d3c30SLuigi Rizzo /* This flowset is only for the FIFO scheduler */ 808cc4d3c30SLuigi Rizzo fs->fs_nr = i + 2*DN_MAX_ID; 809cc4d3c30SLuigi Rizzo fs->sched_nr = i + DN_MAX_ID; 810cc4d3c30SLuigi Rizzo break; 811cc4d3c30SLuigi Rizzo 812cc4d3c30SLuigi Rizzo case 2: /* "queue N config ... " */ 813cc4d3c30SLuigi Rizzo fs = o_next(&buf, sizeof(*fs), DN_FS); 814cc4d3c30SLuigi Rizzo fs->fs_nr = i; 815cc4d3c30SLuigi Rizzo mask = &fs->flow_mask; 816cc4d3c30SLuigi Rizzo flags = &fs->flags; 817cc4d3c30SLuigi Rizzo buckets = &fs->buckets; 818cc4d3c30SLuigi Rizzo break; 819cc4d3c30SLuigi Rizzo 820cc4d3c30SLuigi Rizzo case 3: /* "sched N config ..." */ 821cc4d3c30SLuigi Rizzo sch = o_next(&buf, sizeof(*sch), DN_SCH); 822cc4d3c30SLuigi Rizzo fs = o_next(&buf, sizeof(*fs), DN_FS); 823cc4d3c30SLuigi Rizzo sch->sched_nr = i; 824cc4d3c30SLuigi Rizzo mask = &sch->sched_mask; 825cc4d3c30SLuigi Rizzo flags = &sch->flags; 826cc4d3c30SLuigi Rizzo buckets = &sch->buckets; 827cc4d3c30SLuigi Rizzo /* fs is used only with !MULTIQUEUE schedulers */ 828cc4d3c30SLuigi Rizzo fs->fs_nr = i + DN_MAX_ID; 829cc4d3c30SLuigi Rizzo fs->sched_nr = i; 830cc4d3c30SLuigi Rizzo break; 8314e9c8ae7SLuigi Rizzo } 832cc4d3c30SLuigi Rizzo /* set to -1 those fields for which we want to reuse existing 833cc4d3c30SLuigi Rizzo * values from the kernel. 834cc4d3c30SLuigi Rizzo * Also, *_nr and subtype = 0 mean reuse the value from the kernel. 835cc4d3c30SLuigi Rizzo * XXX todo: support reuse of the mask. 836cc4d3c30SLuigi Rizzo */ 837cc4d3c30SLuigi Rizzo if (p) 838cc4d3c30SLuigi Rizzo p->bandwidth = -1; 839cc4d3c30SLuigi Rizzo for (j = 0; j < sizeof(fs->par)/sizeof(fs->par[0]); j++) 840cc4d3c30SLuigi Rizzo fs->par[j] = -1; 8414e9c8ae7SLuigi Rizzo while (ac > 0) { 8424e9c8ae7SLuigi Rizzo double d; 8434e9c8ae7SLuigi Rizzo int tok = match_token(dummynet_params, *av); 8444e9c8ae7SLuigi Rizzo ac--; av++; 8454e9c8ae7SLuigi Rizzo 8464e9c8ae7SLuigi Rizzo switch(tok) { 8474e9c8ae7SLuigi Rizzo case TOK_NOERROR: 848cc4d3c30SLuigi Rizzo NEED(fs, "noerror is only for pipes"); 849cc4d3c30SLuigi Rizzo fs->flags |= DN_NOERROR; 8504e9c8ae7SLuigi Rizzo break; 8514e9c8ae7SLuigi Rizzo 8524e9c8ae7SLuigi Rizzo case TOK_PLR: 853cc4d3c30SLuigi Rizzo NEED(fs, "plr is only for pipes"); 8544e9c8ae7SLuigi Rizzo NEED1("plr needs argument 0..1\n"); 8554e9c8ae7SLuigi Rizzo d = strtod(av[0], NULL); 8564e9c8ae7SLuigi Rizzo if (d > 1) 8574e9c8ae7SLuigi Rizzo d = 1; 8584e9c8ae7SLuigi Rizzo else if (d < 0) 8594e9c8ae7SLuigi Rizzo d = 0; 860cc4d3c30SLuigi Rizzo fs->plr = (int)(d*0x7fffffff); 8614e9c8ae7SLuigi Rizzo ac--; av++; 8624e9c8ae7SLuigi Rizzo break; 8634e9c8ae7SLuigi Rizzo 8644e9c8ae7SLuigi Rizzo case TOK_QUEUE: 865cc4d3c30SLuigi Rizzo NEED(fs, "queue is only for pipes or flowsets"); 8664e9c8ae7SLuigi Rizzo NEED1("queue needs queue size\n"); 8674e9c8ae7SLuigi Rizzo end = NULL; 868cc4d3c30SLuigi Rizzo fs->qsize = strtoul(av[0], &end, 0); 8694e9c8ae7SLuigi Rizzo if (*end == 'K' || *end == 'k') { 870cc4d3c30SLuigi Rizzo fs->flags |= DN_QSIZE_BYTES; 871cc4d3c30SLuigi Rizzo fs->qsize *= 1024; 8724e9c8ae7SLuigi Rizzo } else if (*end == 'B' || 8734e9c8ae7SLuigi Rizzo _substrcmp2(end, "by", "bytes") == 0) { 874cc4d3c30SLuigi Rizzo fs->flags |= DN_QSIZE_BYTES; 8754e9c8ae7SLuigi Rizzo } 8764e9c8ae7SLuigi Rizzo ac--; av++; 8774e9c8ae7SLuigi Rizzo break; 8784e9c8ae7SLuigi Rizzo 8794e9c8ae7SLuigi Rizzo case TOK_BUCKETS: 880cc4d3c30SLuigi Rizzo NEED(fs, "buckets is only for pipes or flowsets"); 8814e9c8ae7SLuigi Rizzo NEED1("buckets needs argument\n"); 882cc4d3c30SLuigi Rizzo *buckets = strtoul(av[0], NULL, 0); 8834e9c8ae7SLuigi Rizzo ac--; av++; 8844e9c8ae7SLuigi Rizzo break; 8854e9c8ae7SLuigi Rizzo 886cc4d3c30SLuigi Rizzo case TOK_FLOW_MASK: 887cc4d3c30SLuigi Rizzo case TOK_SCHED_MASK: 8884e9c8ae7SLuigi Rizzo case TOK_MASK: 889cc4d3c30SLuigi Rizzo NEED(mask, "tok_mask"); 8904e9c8ae7SLuigi Rizzo NEED1("mask needs mask specifier\n"); 8914e9c8ae7SLuigi Rizzo /* 8924e9c8ae7SLuigi Rizzo * per-flow queue, mask is dst_ip, dst_port, 8934e9c8ae7SLuigi Rizzo * src_ip, src_port, proto measured in bits 8944e9c8ae7SLuigi Rizzo */ 8954e9c8ae7SLuigi Rizzo par = NULL; 8964e9c8ae7SLuigi Rizzo 897cc4d3c30SLuigi Rizzo bzero(mask, sizeof(*mask)); 8984e9c8ae7SLuigi Rizzo end = NULL; 8994e9c8ae7SLuigi Rizzo 9004e9c8ae7SLuigi Rizzo while (ac >= 1) { 9014e9c8ae7SLuigi Rizzo uint32_t *p32 = NULL; 9024e9c8ae7SLuigi Rizzo uint16_t *p16 = NULL; 9034e9c8ae7SLuigi Rizzo uint32_t *p20 = NULL; 9044e9c8ae7SLuigi Rizzo struct in6_addr *pa6 = NULL; 9054e9c8ae7SLuigi Rizzo uint32_t a; 9064e9c8ae7SLuigi Rizzo 9074e9c8ae7SLuigi Rizzo tok = match_token(dummynet_params, *av); 9084e9c8ae7SLuigi Rizzo ac--; av++; 9094e9c8ae7SLuigi Rizzo switch(tok) { 9104e9c8ae7SLuigi Rizzo case TOK_ALL: 9114e9c8ae7SLuigi Rizzo /* 9124e9c8ae7SLuigi Rizzo * special case, all bits significant 9134e9c8ae7SLuigi Rizzo */ 914cc4d3c30SLuigi Rizzo mask->dst_ip = ~0; 915cc4d3c30SLuigi Rizzo mask->src_ip = ~0; 916cc4d3c30SLuigi Rizzo mask->dst_port = ~0; 917cc4d3c30SLuigi Rizzo mask->src_port = ~0; 918cc4d3c30SLuigi Rizzo mask->proto = ~0; 919cc4d3c30SLuigi Rizzo n2mask(&mask->dst_ip6, 128); 920cc4d3c30SLuigi Rizzo n2mask(&mask->src_ip6, 128); 921cc4d3c30SLuigi Rizzo mask->flow_id6 = ~0; 922cc4d3c30SLuigi Rizzo *flags |= DN_HAVE_MASK; 9234e9c8ae7SLuigi Rizzo goto end_mask; 9244e9c8ae7SLuigi Rizzo 9254e9c8ae7SLuigi Rizzo case TOK_DSTIP: 926cc4d3c30SLuigi Rizzo mask->addr_type = 4; 927cc4d3c30SLuigi Rizzo p32 = &mask->dst_ip; 9284e9c8ae7SLuigi Rizzo break; 9294e9c8ae7SLuigi Rizzo 9304e9c8ae7SLuigi Rizzo case TOK_SRCIP: 931cc4d3c30SLuigi Rizzo mask->addr_type = 4; 932cc4d3c30SLuigi Rizzo p32 = &mask->src_ip; 9334e9c8ae7SLuigi Rizzo break; 9344e9c8ae7SLuigi Rizzo 9354e9c8ae7SLuigi Rizzo case TOK_DSTIP6: 936cc4d3c30SLuigi Rizzo mask->addr_type = 6; 937cc4d3c30SLuigi Rizzo pa6 = &mask->dst_ip6; 9384e9c8ae7SLuigi Rizzo break; 9394e9c8ae7SLuigi Rizzo 9404e9c8ae7SLuigi Rizzo case TOK_SRCIP6: 941cc4d3c30SLuigi Rizzo mask->addr_type = 6; 942cc4d3c30SLuigi Rizzo pa6 = &mask->src_ip6; 9434e9c8ae7SLuigi Rizzo break; 9444e9c8ae7SLuigi Rizzo 9454e9c8ae7SLuigi Rizzo case TOK_FLOWID: 946cc4d3c30SLuigi Rizzo mask->addr_type = 6; 947cc4d3c30SLuigi Rizzo p20 = &mask->flow_id6; 9484e9c8ae7SLuigi Rizzo break; 9494e9c8ae7SLuigi Rizzo 9504e9c8ae7SLuigi Rizzo case TOK_DSTPORT: 951cc4d3c30SLuigi Rizzo p16 = &mask->dst_port; 9524e9c8ae7SLuigi Rizzo break; 9534e9c8ae7SLuigi Rizzo 9544e9c8ae7SLuigi Rizzo case TOK_SRCPORT: 955cc4d3c30SLuigi Rizzo p16 = &mask->src_port; 9564e9c8ae7SLuigi Rizzo break; 9574e9c8ae7SLuigi Rizzo 9584e9c8ae7SLuigi Rizzo case TOK_PROTO: 9594e9c8ae7SLuigi Rizzo break; 9604e9c8ae7SLuigi Rizzo 9614e9c8ae7SLuigi Rizzo default: 9624e9c8ae7SLuigi Rizzo ac++; av--; /* backtrack */ 9634e9c8ae7SLuigi Rizzo goto end_mask; 9644e9c8ae7SLuigi Rizzo } 9654e9c8ae7SLuigi Rizzo if (ac < 1) 9664e9c8ae7SLuigi Rizzo errx(EX_USAGE, "mask: value missing"); 9674e9c8ae7SLuigi Rizzo if (*av[0] == '/') { 9684e9c8ae7SLuigi Rizzo a = strtoul(av[0]+1, &end, 0); 9694e9c8ae7SLuigi Rizzo if (pa6 == NULL) 9704e9c8ae7SLuigi Rizzo a = (a == 32) ? ~0 : (1 << a) - 1; 9714e9c8ae7SLuigi Rizzo } else 9724e9c8ae7SLuigi Rizzo a = strtoul(av[0], &end, 0); 9734e9c8ae7SLuigi Rizzo if (p32 != NULL) 9744e9c8ae7SLuigi Rizzo *p32 = a; 9754e9c8ae7SLuigi Rizzo else if (p16 != NULL) { 9764e9c8ae7SLuigi Rizzo if (a > 0xFFFF) 9774e9c8ae7SLuigi Rizzo errx(EX_DATAERR, 9784e9c8ae7SLuigi Rizzo "port mask must be 16 bit"); 9794e9c8ae7SLuigi Rizzo *p16 = (uint16_t)a; 9804e9c8ae7SLuigi Rizzo } else if (p20 != NULL) { 9814e9c8ae7SLuigi Rizzo if (a > 0xfffff) 9824e9c8ae7SLuigi Rizzo errx(EX_DATAERR, 9834e9c8ae7SLuigi Rizzo "flow_id mask must be 20 bit"); 9844e9c8ae7SLuigi Rizzo *p20 = (uint32_t)a; 9854e9c8ae7SLuigi Rizzo } else if (pa6 != NULL) { 9864e9c8ae7SLuigi Rizzo if (a > 128) 9874e9c8ae7SLuigi Rizzo errx(EX_DATAERR, 9884e9c8ae7SLuigi Rizzo "in6addr invalid mask len"); 9894e9c8ae7SLuigi Rizzo else 9904e9c8ae7SLuigi Rizzo n2mask(pa6, a); 9914e9c8ae7SLuigi Rizzo } else { 9924e9c8ae7SLuigi Rizzo if (a > 0xFF) 9934e9c8ae7SLuigi Rizzo errx(EX_DATAERR, 9944e9c8ae7SLuigi Rizzo "proto mask must be 8 bit"); 995cc4d3c30SLuigi Rizzo fs->flow_mask.proto = (uint8_t)a; 9964e9c8ae7SLuigi Rizzo } 9974e9c8ae7SLuigi Rizzo if (a != 0) 998cc4d3c30SLuigi Rizzo *flags |= DN_HAVE_MASK; 9994e9c8ae7SLuigi Rizzo ac--; av++; 10004e9c8ae7SLuigi Rizzo } /* end while, config masks */ 10014e9c8ae7SLuigi Rizzo end_mask: 10024e9c8ae7SLuigi Rizzo break; 10034e9c8ae7SLuigi Rizzo 10044e9c8ae7SLuigi Rizzo case TOK_RED: 10054e9c8ae7SLuigi Rizzo case TOK_GRED: 10064e9c8ae7SLuigi Rizzo NEED1("red/gred needs w_q/min_th/max_th/max_p\n"); 1007cc4d3c30SLuigi Rizzo fs->flags |= DN_IS_RED; 10084e9c8ae7SLuigi Rizzo if (tok == TOK_GRED) 1009cc4d3c30SLuigi Rizzo fs->flags |= DN_IS_GENTLE_RED; 10104e9c8ae7SLuigi Rizzo /* 10114e9c8ae7SLuigi Rizzo * the format for parameters is w_q/min_th/max_th/max_p 10124e9c8ae7SLuigi Rizzo */ 10134e9c8ae7SLuigi Rizzo if ((end = strsep(&av[0], "/"))) { 10144e9c8ae7SLuigi Rizzo double w_q = strtod(end, NULL); 10154e9c8ae7SLuigi Rizzo if (w_q > 1 || w_q <= 0) 10164e9c8ae7SLuigi Rizzo errx(EX_DATAERR, "0 < w_q <= 1"); 1017cc4d3c30SLuigi Rizzo fs->w_q = (int) (w_q * (1 << SCALE_RED)); 10184e9c8ae7SLuigi Rizzo } 10194e9c8ae7SLuigi Rizzo if ((end = strsep(&av[0], "/"))) { 1020cc4d3c30SLuigi Rizzo fs->min_th = strtoul(end, &end, 0); 10214e9c8ae7SLuigi Rizzo if (*end == 'K' || *end == 'k') 1022cc4d3c30SLuigi Rizzo fs->min_th *= 1024; 10234e9c8ae7SLuigi Rizzo } 10244e9c8ae7SLuigi Rizzo if ((end = strsep(&av[0], "/"))) { 1025cc4d3c30SLuigi Rizzo fs->max_th = strtoul(end, &end, 0); 10264e9c8ae7SLuigi Rizzo if (*end == 'K' || *end == 'k') 1027cc4d3c30SLuigi Rizzo fs->max_th *= 1024; 10284e9c8ae7SLuigi Rizzo } 10294e9c8ae7SLuigi Rizzo if ((end = strsep(&av[0], "/"))) { 10304e9c8ae7SLuigi Rizzo double max_p = strtod(end, NULL); 10314e9c8ae7SLuigi Rizzo if (max_p > 1 || max_p <= 0) 10324e9c8ae7SLuigi Rizzo errx(EX_DATAERR, "0 < max_p <= 1"); 1033cc4d3c30SLuigi Rizzo fs->max_p = (int)(max_p * (1 << SCALE_RED)); 10344e9c8ae7SLuigi Rizzo } 10354e9c8ae7SLuigi Rizzo ac--; av++; 10364e9c8ae7SLuigi Rizzo break; 10374e9c8ae7SLuigi Rizzo 10384e9c8ae7SLuigi Rizzo case TOK_DROPTAIL: 1039cc4d3c30SLuigi Rizzo NEED(fs, "droptail is only for flowsets"); 1040cc4d3c30SLuigi Rizzo fs->flags &= ~(DN_IS_RED|DN_IS_GENTLE_RED); 10414e9c8ae7SLuigi Rizzo break; 10424e9c8ae7SLuigi Rizzo 10434e9c8ae7SLuigi Rizzo case TOK_BW: 1044cc4d3c30SLuigi Rizzo NEED(p, "bw is only for links"); 10454e9c8ae7SLuigi Rizzo NEED1("bw needs bandwidth or interface\n"); 1046cc4d3c30SLuigi Rizzo read_bandwidth(av[0], &p->bandwidth, NULL, 0); 10474e9c8ae7SLuigi Rizzo ac--; av++; 10484e9c8ae7SLuigi Rizzo break; 10494e9c8ae7SLuigi Rizzo 10504e9c8ae7SLuigi Rizzo case TOK_DELAY: 1051cc4d3c30SLuigi Rizzo NEED(p, "delay is only for links"); 10524e9c8ae7SLuigi Rizzo NEED1("delay needs argument 0..10000ms\n"); 1053cc4d3c30SLuigi Rizzo p->delay = strtoul(av[0], NULL, 0); 10544e9c8ae7SLuigi Rizzo ac--; av++; 10554e9c8ae7SLuigi Rizzo break; 10564e9c8ae7SLuigi Rizzo 1057cc4d3c30SLuigi Rizzo case TOK_TYPE: { 1058cc4d3c30SLuigi Rizzo int l; 1059cc4d3c30SLuigi Rizzo NEED(sch, "type is only for schedulers"); 1060cc4d3c30SLuigi Rizzo NEED1("type needs a string"); 1061cc4d3c30SLuigi Rizzo l = strlen(av[0]); 1062cc4d3c30SLuigi Rizzo if (l == 0 || l > 15) 1063cc4d3c30SLuigi Rizzo errx(1, "type %s too long\n", av[0]); 1064cc4d3c30SLuigi Rizzo strcpy(sch->name, av[0]); 1065cc4d3c30SLuigi Rizzo sch->oid.subtype = 0; /* use string */ 1066cc4d3c30SLuigi Rizzo ac--; av++; 1067cc4d3c30SLuigi Rizzo break; 1068cc4d3c30SLuigi Rizzo } 1069cc4d3c30SLuigi Rizzo 10704e9c8ae7SLuigi Rizzo case TOK_WEIGHT: 1071cc4d3c30SLuigi Rizzo NEED(fs, "weight is only for flowsets"); 1072cc4d3c30SLuigi Rizzo NEED1("weight needs argument\n"); 1073cc4d3c30SLuigi Rizzo fs->par[0] = strtol(av[0], &end, 0); 10744e9c8ae7SLuigi Rizzo ac--; av++; 10754e9c8ae7SLuigi Rizzo break; 10764e9c8ae7SLuigi Rizzo 1077cc4d3c30SLuigi Rizzo case TOK_LMAX: 1078cc4d3c30SLuigi Rizzo NEED(fs, "lmax is only for flowsets"); 1079cc4d3c30SLuigi Rizzo NEED1("lmax needs argument\n"); 1080cc4d3c30SLuigi Rizzo fs->par[1] = strtol(av[0], &end, 0); 1081cc4d3c30SLuigi Rizzo ac--; av++; 1082cc4d3c30SLuigi Rizzo break; 1083cc4d3c30SLuigi Rizzo 1084cc4d3c30SLuigi Rizzo case TOK_PRI: 1085cc4d3c30SLuigi Rizzo NEED(fs, "priority is only for flowsets"); 1086cc4d3c30SLuigi Rizzo NEED1("priority needs argument\n"); 1087cc4d3c30SLuigi Rizzo fs->par[2] = strtol(av[0], &end, 0); 1088cc4d3c30SLuigi Rizzo ac--; av++; 1089cc4d3c30SLuigi Rizzo break; 1090cc4d3c30SLuigi Rizzo 1091cc4d3c30SLuigi Rizzo case TOK_SCHED: 10924e9c8ae7SLuigi Rizzo case TOK_PIPE: 1093cc4d3c30SLuigi Rizzo NEED(fs, "pipe/sched"); 1094cc4d3c30SLuigi Rizzo NEED1("pipe/link/sched needs number\n"); 1095cc4d3c30SLuigi Rizzo fs->sched_nr = strtoul(av[0], &end, 0); 10964e9c8ae7SLuigi Rizzo ac--; av++; 10974e9c8ae7SLuigi Rizzo break; 10984e9c8ae7SLuigi Rizzo 1099cc4d3c30SLuigi Rizzo case TOK_PROFILE: 1100cc4d3c30SLuigi Rizzo NEED((!pf), "profile already set"); 1101cc4d3c30SLuigi Rizzo NEED(p, "profile"); 1102cc4d3c30SLuigi Rizzo { 11034bb7ae9dSLuigi Rizzo NEED1("extra delay needs the file name\n"); 1104cc4d3c30SLuigi Rizzo pf = o_next(&buf, sizeof(*pf), DN_PROFILE); 1105cc4d3c30SLuigi Rizzo load_extra_delays(av[0], pf, p); //XXX can't fail? 11064bb7ae9dSLuigi Rizzo --ac; ++av; 1107cc4d3c30SLuigi Rizzo } 11084bb7ae9dSLuigi Rizzo break; 11094bb7ae9dSLuigi Rizzo 11106882bf4dSOleg Bulyzhin case TOK_BURST: 1111cc4d3c30SLuigi Rizzo NEED(p, "burst"); 11126882bf4dSOleg Bulyzhin NEED1("burst needs argument\n"); 11136882bf4dSOleg Bulyzhin errno = 0; 1114cc4d3c30SLuigi Rizzo if (expand_number(av[0], (int64_t *)&p->burst) < 0) 11156882bf4dSOleg Bulyzhin if (errno != ERANGE) 11166882bf4dSOleg Bulyzhin errx(EX_DATAERR, 11176882bf4dSOleg Bulyzhin "burst: invalid argument"); 1118cc4d3c30SLuigi Rizzo if (errno || p->burst > (1ULL << 48) - 1) 11196882bf4dSOleg Bulyzhin errx(EX_DATAERR, 11206882bf4dSOleg Bulyzhin "burst: out of range (0..2^48-1)"); 11216882bf4dSOleg Bulyzhin ac--; av++; 11226882bf4dSOleg Bulyzhin break; 11236882bf4dSOleg Bulyzhin 11244e9c8ae7SLuigi Rizzo default: 11254e9c8ae7SLuigi Rizzo errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]); 11264e9c8ae7SLuigi Rizzo } 11274e9c8ae7SLuigi Rizzo } 1128cc4d3c30SLuigi Rizzo 1129cc4d3c30SLuigi Rizzo /* check validity of parameters */ 1130cc4d3c30SLuigi Rizzo if (p) { 1131cc4d3c30SLuigi Rizzo if (p->delay > 10000) 11324e9c8ae7SLuigi Rizzo errx(EX_DATAERR, "delay must be < 10000"); 1133cc4d3c30SLuigi Rizzo if (p->bandwidth == -1) 1134cc4d3c30SLuigi Rizzo p->bandwidth = 0; 11354e9c8ae7SLuigi Rizzo } 1136cc4d3c30SLuigi Rizzo if (fs) { 1137cc4d3c30SLuigi Rizzo /* XXX accept a 0 scheduler to keep the default */ 1138cc4d3c30SLuigi Rizzo if (fs->flags & DN_QSIZE_BYTES) { 11394e9c8ae7SLuigi Rizzo size_t len; 11404e9c8ae7SLuigi Rizzo long limit; 11414e9c8ae7SLuigi Rizzo 11424e9c8ae7SLuigi Rizzo len = sizeof(limit); 11434e9c8ae7SLuigi Rizzo if (sysctlbyname("net.inet.ip.dummynet.pipe_byte_limit", 11444e9c8ae7SLuigi Rizzo &limit, &len, NULL, 0) == -1) 11454e9c8ae7SLuigi Rizzo limit = 1024*1024; 1146cc4d3c30SLuigi Rizzo if (fs->qsize > limit) 11474e9c8ae7SLuigi Rizzo errx(EX_DATAERR, "queue size must be < %ldB", limit); 11484e9c8ae7SLuigi Rizzo } else { 11494e9c8ae7SLuigi Rizzo size_t len; 11504e9c8ae7SLuigi Rizzo long limit; 11514e9c8ae7SLuigi Rizzo 11524e9c8ae7SLuigi Rizzo len = sizeof(limit); 11534e9c8ae7SLuigi Rizzo if (sysctlbyname("net.inet.ip.dummynet.pipe_slot_limit", 11544e9c8ae7SLuigi Rizzo &limit, &len, NULL, 0) == -1) 11554e9c8ae7SLuigi Rizzo limit = 100; 1156cc4d3c30SLuigi Rizzo if (fs->qsize > limit) 11574e9c8ae7SLuigi Rizzo errx(EX_DATAERR, "2 <= queue size <= %ld", limit); 11584e9c8ae7SLuigi Rizzo } 1159cc4d3c30SLuigi Rizzo 1160cc4d3c30SLuigi Rizzo if (fs->flags & DN_IS_RED) { 11614e9c8ae7SLuigi Rizzo size_t len; 11624e9c8ae7SLuigi Rizzo int lookup_depth, avg_pkt_size; 1163cc4d3c30SLuigi Rizzo double w_q; 11644e9c8ae7SLuigi Rizzo 1165cc4d3c30SLuigi Rizzo if (fs->min_th >= fs->max_th) 11664e9c8ae7SLuigi Rizzo errx(EX_DATAERR, "min_th %d must be < than max_th %d", 1167cc4d3c30SLuigi Rizzo fs->min_th, fs->max_th); 1168cc4d3c30SLuigi Rizzo if (fs->max_th == 0) 11694e9c8ae7SLuigi Rizzo errx(EX_DATAERR, "max_th must be > 0"); 11704e9c8ae7SLuigi Rizzo 11714e9c8ae7SLuigi Rizzo len = sizeof(int); 11724e9c8ae7SLuigi Rizzo if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth", 11734e9c8ae7SLuigi Rizzo &lookup_depth, &len, NULL, 0) == -1) 1174cc4d3c30SLuigi Rizzo lookup_depth = 256; 11754e9c8ae7SLuigi Rizzo if (lookup_depth == 0) 11764e9c8ae7SLuigi Rizzo errx(EX_DATAERR, "net.inet.ip.dummynet.red_lookup_depth" 11774e9c8ae7SLuigi Rizzo " must be greater than zero"); 11784e9c8ae7SLuigi Rizzo 11794e9c8ae7SLuigi Rizzo len = sizeof(int); 11804e9c8ae7SLuigi Rizzo if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size", 11814e9c8ae7SLuigi Rizzo &avg_pkt_size, &len, NULL, 0) == -1) 1182cc4d3c30SLuigi Rizzo avg_pkt_size = 512; 11834e9c8ae7SLuigi Rizzo 11844e9c8ae7SLuigi Rizzo if (avg_pkt_size == 0) 11854e9c8ae7SLuigi Rizzo errx(EX_DATAERR, 11864e9c8ae7SLuigi Rizzo "net.inet.ip.dummynet.red_avg_pkt_size must" 11874e9c8ae7SLuigi Rizzo " be greater than zero"); 11884e9c8ae7SLuigi Rizzo 11894e9c8ae7SLuigi Rizzo /* 11904e9c8ae7SLuigi Rizzo * Ticks needed for sending a medium-sized packet. 11914e9c8ae7SLuigi Rizzo * Unfortunately, when we are configuring a WF2Q+ queue, we 11924e9c8ae7SLuigi Rizzo * do not have bandwidth information, because that is stored 11934e9c8ae7SLuigi Rizzo * in the parent pipe, and also we have multiple queues 11944e9c8ae7SLuigi Rizzo * competing for it. So we set s=0, which is not very 11954e9c8ae7SLuigi Rizzo * correct. But on the other hand, why do we want RED with 11964e9c8ae7SLuigi Rizzo * WF2Q+ ? 11974e9c8ae7SLuigi Rizzo */ 1198cc4d3c30SLuigi Rizzo #if 0 11994e9c8ae7SLuigi Rizzo if (p.bandwidth==0) /* this is a WF2Q+ queue */ 12004e9c8ae7SLuigi Rizzo s = 0; 12014e9c8ae7SLuigi Rizzo else 12024e9c8ae7SLuigi Rizzo s = (double)ck.hz * avg_pkt_size * 8 / p.bandwidth; 1203cc4d3c30SLuigi Rizzo #endif 12044e9c8ae7SLuigi Rizzo /* 12054e9c8ae7SLuigi Rizzo * max idle time (in ticks) before avg queue size becomes 0. 12064e9c8ae7SLuigi Rizzo * NOTA: (3/w_q) is approx the value x so that 12074e9c8ae7SLuigi Rizzo * (1-w_q)^x < 10^-3. 12084e9c8ae7SLuigi Rizzo */ 1209cc4d3c30SLuigi Rizzo w_q = ((double)fs->w_q) / (1 << SCALE_RED); 1210cc4d3c30SLuigi Rizzo #if 0 // go in kernel 12114e9c8ae7SLuigi Rizzo idle = s * 3. / w_q; 1212cc4d3c30SLuigi Rizzo fs->lookup_step = (int)idle / lookup_depth; 1213cc4d3c30SLuigi Rizzo if (!fs->lookup_step) 1214cc4d3c30SLuigi Rizzo fs->lookup_step = 1; 12154e9c8ae7SLuigi Rizzo weight = 1 - w_q; 1216cc4d3c30SLuigi Rizzo for (t = fs->lookup_step; t > 1; --t) 12174e9c8ae7SLuigi Rizzo weight *= 1 - w_q; 1218cc4d3c30SLuigi Rizzo fs->lookup_weight = (int)(weight * (1 << SCALE_RED)); 1219cc4d3c30SLuigi Rizzo #endif 12204e9c8ae7SLuigi Rizzo } 12214bb7ae9dSLuigi Rizzo } 12224bb7ae9dSLuigi Rizzo 1223cc4d3c30SLuigi Rizzo i = do_cmd(IP_DUMMYNET3, base, (char *)buf - (char *)base); 1224cc4d3c30SLuigi Rizzo 12254e9c8ae7SLuigi Rizzo if (i) 12264e9c8ae7SLuigi Rizzo err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE"); 12274e9c8ae7SLuigi Rizzo } 1228cc4d3c30SLuigi Rizzo 1229cc4d3c30SLuigi Rizzo void 1230cc4d3c30SLuigi Rizzo dummynet_flush(void) 1231cc4d3c30SLuigi Rizzo { 1232cc4d3c30SLuigi Rizzo struct dn_id oid; 1233cc4d3c30SLuigi Rizzo oid_fill(&oid, sizeof(oid), DN_CMD_FLUSH, DN_API_VERSION); 1234cc4d3c30SLuigi Rizzo do_cmd(IP_DUMMYNET3, &oid, oid.len); 1235cc4d3c30SLuigi Rizzo } 1236cc4d3c30SLuigi Rizzo 12375007b59fSLuigi Rizzo /* Parse input for 'ipfw [pipe|sched|queue] show [range list]' 12385007b59fSLuigi Rizzo * Returns the number of ranges, and possibly stores them 12395007b59fSLuigi Rizzo * in the array v of size len. 12405007b59fSLuigi Rizzo */ 12415007b59fSLuigi Rizzo static int 12425007b59fSLuigi Rizzo parse_range(int ac, char *av[], uint32_t *v, int len) 12435007b59fSLuigi Rizzo { 12445007b59fSLuigi Rizzo int n = 0; 12455007b59fSLuigi Rizzo char *endptr, *s; 12465007b59fSLuigi Rizzo uint32_t base[2]; 12475007b59fSLuigi Rizzo 12485007b59fSLuigi Rizzo if (v == NULL || len < 2) { 12495007b59fSLuigi Rizzo v = base; 12505007b59fSLuigi Rizzo len = 2; 12515007b59fSLuigi Rizzo } 12525007b59fSLuigi Rizzo 12535007b59fSLuigi Rizzo for (s = *av; s != NULL; av++, ac--) { 12545007b59fSLuigi Rizzo v[0] = strtoul(s, &endptr, 10); 12555007b59fSLuigi Rizzo v[1] = (*endptr != '-') ? v[0] : 12565007b59fSLuigi Rizzo strtoul(endptr+1, &endptr, 10); 12575007b59fSLuigi Rizzo if (*endptr == '\0') { /* prepare for next round */ 12585007b59fSLuigi Rizzo s = (ac > 0) ? *(av+1) : NULL; 12595007b59fSLuigi Rizzo } else { 12605007b59fSLuigi Rizzo if (*endptr != ',') { 12615007b59fSLuigi Rizzo warn("invalid number: %s", s); 12625007b59fSLuigi Rizzo s = ++endptr; 12635007b59fSLuigi Rizzo continue; 12645007b59fSLuigi Rizzo } 12655007b59fSLuigi Rizzo /* continue processing from here */ 12665007b59fSLuigi Rizzo s = ++endptr; 12675007b59fSLuigi Rizzo ac++; 12685007b59fSLuigi Rizzo av--; 12695007b59fSLuigi Rizzo } 12705007b59fSLuigi Rizzo if (v[1] < v[0] || 12715007b59fSLuigi Rizzo v[1] < 0 || v[1] >= DN_MAX_ID-1 || 12725007b59fSLuigi Rizzo v[0] < 0 || v[1] >= DN_MAX_ID-1) { 12735007b59fSLuigi Rizzo continue; /* invalid entry */ 12745007b59fSLuigi Rizzo } 12755007b59fSLuigi Rizzo n++; 12765007b59fSLuigi Rizzo /* translate if 'pipe list' */ 12775007b59fSLuigi Rizzo if (co.do_pipe == 1) { 12785007b59fSLuigi Rizzo v[0] += DN_MAX_ID; 12795007b59fSLuigi Rizzo v[1] += DN_MAX_ID; 12805007b59fSLuigi Rizzo } 12815007b59fSLuigi Rizzo v = (n*2 < len) ? v + 2 : base; 12825007b59fSLuigi Rizzo } 12835007b59fSLuigi Rizzo return n; 12845007b59fSLuigi Rizzo } 12855007b59fSLuigi Rizzo 1286cc4d3c30SLuigi Rizzo /* main entry point for dummynet list functions. co.do_pipe indicates 1287cc4d3c30SLuigi Rizzo * which function we want to support. 12885007b59fSLuigi Rizzo * av may contain filtering arguments, either individual entries 12895007b59fSLuigi Rizzo * or ranges, or lists (space or commas are valid separators). 12905007b59fSLuigi Rizzo * Format for a range can be n1-n2 or n3 n4 n5 ... 12915007b59fSLuigi Rizzo * In a range n1 must be <= n2, otherwise the range is ignored. 12925007b59fSLuigi Rizzo * A number 'n4' is translate in a range 'n4-n4' 12935007b59fSLuigi Rizzo * All number must be > 0 and < DN_MAX_ID-1 1294cc4d3c30SLuigi Rizzo */ 1295cc4d3c30SLuigi Rizzo void 1296cc4d3c30SLuigi Rizzo dummynet_list(int ac, char *av[], int show_counters) 1297cc4d3c30SLuigi Rizzo { 12985007b59fSLuigi Rizzo struct dn_id *oid, *x = NULL; 12995007b59fSLuigi Rizzo int ret, i, l; 13005007b59fSLuigi Rizzo int n; /* # of ranges */ 13015007b59fSLuigi Rizzo int buflen; 13025007b59fSLuigi Rizzo int max_size; /* largest obj passed up */ 1303cc4d3c30SLuigi Rizzo 13045007b59fSLuigi Rizzo ac--; 13055007b59fSLuigi Rizzo av++; /* skip 'list' | 'show' word */ 13065007b59fSLuigi Rizzo 13075007b59fSLuigi Rizzo n = parse_range(ac, av, NULL, 0); /* Count # of ranges. */ 13085007b59fSLuigi Rizzo 13095007b59fSLuigi Rizzo /* Allocate space to store ranges */ 13105007b59fSLuigi Rizzo l = sizeof(*oid) + sizeof(uint32_t) * n * 2; 13115007b59fSLuigi Rizzo oid = safe_calloc(1, l); 13125007b59fSLuigi Rizzo oid_fill(oid, l, DN_CMD_GET, DN_API_VERSION); 13135007b59fSLuigi Rizzo 13145007b59fSLuigi Rizzo if (n > 0) /* store ranges in idx */ 13155007b59fSLuigi Rizzo parse_range(ac, av, (uint32_t *)(oid + 1), n*2); 13165007b59fSLuigi Rizzo /* 13175007b59fSLuigi Rizzo * Compute the size of the largest object returned. If the 13185007b59fSLuigi Rizzo * response leaves at least this much spare space in the 13195007b59fSLuigi Rizzo * buffer, then surely the response is complete; otherwise 13205007b59fSLuigi Rizzo * there might be a risk of truncation and we will need to 13215007b59fSLuigi Rizzo * retry with a larger buffer. 13225007b59fSLuigi Rizzo * XXX don't bother with smaller structs. 13235007b59fSLuigi Rizzo */ 13245007b59fSLuigi Rizzo max_size = sizeof(struct dn_fs); 13255007b59fSLuigi Rizzo if (max_size < sizeof(struct dn_sch)) 13265007b59fSLuigi Rizzo max_size = sizeof(struct dn_sch); 13275007b59fSLuigi Rizzo if (max_size < sizeof(struct dn_flow)) 13285007b59fSLuigi Rizzo max_size = sizeof(struct dn_flow); 13295007b59fSLuigi Rizzo 1330cc4d3c30SLuigi Rizzo switch (co.do_pipe) { 1331cc4d3c30SLuigi Rizzo case 1: 13325007b59fSLuigi Rizzo oid->subtype = DN_LINK; /* list pipe */ 1333cc4d3c30SLuigi Rizzo break; 1334cc4d3c30SLuigi Rizzo case 2: 13355007b59fSLuigi Rizzo oid->subtype = DN_FS; /* list queue */ 1336cc4d3c30SLuigi Rizzo break; 1337cc4d3c30SLuigi Rizzo case 3: 13385007b59fSLuigi Rizzo oid->subtype = DN_SCH; /* list sched */ 1339cc4d3c30SLuigi Rizzo break; 1340cc4d3c30SLuigi Rizzo } 1341f10f583fSLuigi Rizzo 13425007b59fSLuigi Rizzo /* 13435007b59fSLuigi Rizzo * Ask the kernel an estimate of the required space (result 13445007b59fSLuigi Rizzo * in oid.id), unless we are requesting a subset of objects, 13455007b59fSLuigi Rizzo * in which case the kernel does not give an exact answer. 13465007b59fSLuigi Rizzo * In any case, space might grow in the meantime due to the 13475007b59fSLuigi Rizzo * creation of new queues, so we must be prepared to retry. 1348f10f583fSLuigi Rizzo */ 13495007b59fSLuigi Rizzo if (n > 0) { 13505007b59fSLuigi Rizzo buflen = 4*1024; 13515007b59fSLuigi Rizzo } else { 13525007b59fSLuigi Rizzo ret = do_cmd(-IP_DUMMYNET3, oid, (uintptr_t)&l); 13535007b59fSLuigi Rizzo if (ret != 0 || oid->id <= sizeof(*oid)) 13545007b59fSLuigi Rizzo goto done; 13555007b59fSLuigi Rizzo buflen = oid->id + max_size; 13565007b59fSLuigi Rizzo oid->len = sizeof(*oid); /* restore */ 1357f10f583fSLuigi Rizzo } 13585007b59fSLuigi Rizzo /* Try a few times, until the buffer fits */ 13595007b59fSLuigi Rizzo for (i = 0; i < 20; i++) { 13605007b59fSLuigi Rizzo l = buflen; 13615007b59fSLuigi Rizzo x = safe_realloc(x, l); 13625007b59fSLuigi Rizzo bcopy(oid, x, oid->len); 13635007b59fSLuigi Rizzo ret = do_cmd(-IP_DUMMYNET3, x, (uintptr_t)&l); 13645007b59fSLuigi Rizzo if (ret != 0 || x->id <= sizeof(*oid)) 13655007b59fSLuigi Rizzo goto done; /* no response */ 13665007b59fSLuigi Rizzo if (l + max_size <= buflen) 13675007b59fSLuigi Rizzo break; /* ok */ 13685007b59fSLuigi Rizzo buflen *= 2; /* double for next attempt */ 13695007b59fSLuigi Rizzo } 1370cc4d3c30SLuigi Rizzo list_pipes(x, O_NEXT(x, l)); 13715007b59fSLuigi Rizzo done: 13725007b59fSLuigi Rizzo if (x) 1373cc4d3c30SLuigi Rizzo free(x); 13745007b59fSLuigi Rizzo free(oid); 1375cc4d3c30SLuigi Rizzo } 1376