1 /*- 2 * Copyright (c) 2010 Riccardo Panicucci, Universita` di Pisa 3 * All rights reserved 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 /* 28 * $FreeBSD$ 29 * 30 * Binary compatibility support for /sbin/ipfw RELENG_7 and RELENG_8 31 */ 32 33 #include "opt_inet6.h" 34 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/malloc.h> 38 #include <sys/mbuf.h> 39 #include <sys/kernel.h> 40 #include <sys/lock.h> 41 #include <sys/module.h> 42 #include <sys/priv.h> 43 #include <sys/proc.h> 44 #include <sys/rwlock.h> 45 #include <sys/socket.h> 46 #include <sys/socketvar.h> 47 #include <sys/time.h> 48 #include <sys/taskqueue.h> 49 #include <net/if.h> /* IFNAMSIZ, struct ifaddr, ifq head, lock.h mutex.h */ 50 #include <netinet/in.h> 51 #include <netinet/ip_var.h> /* ip_output(), IP_FORWARDING */ 52 #include <netinet/ip_fw.h> 53 #include <netinet/ip_dummynet.h> 54 55 #include <netpfil/ipfw/ip_fw_private.h> 56 #include <netpfil/ipfw/dn_heap.h> 57 #include <netpfil/ipfw/ip_dn_private.h> 58 #ifdef NEW_AQM 59 #include <netpfil/ipfw/dn_aqm.h> 60 #endif 61 #include <netpfil/ipfw/dn_sched.h> 62 63 /* FREEBSD7.2 ip_dummynet.h r191715*/ 64 65 struct dn_heap_entry7 { 66 int64_t key; /* sorting key. Topmost element is smallest one */ 67 void *object; /* object pointer */ 68 }; 69 70 struct dn_heap7 { 71 int size; 72 int elements; 73 int offset; /* XXX if > 0 this is the offset of direct ptr to obj */ 74 struct dn_heap_entry7 *p; /* really an array of "size" entries */ 75 }; 76 77 /* Common to 7.2 and 8 */ 78 struct dn_flow_set { 79 SLIST_ENTRY(dn_flow_set) next; /* linked list in a hash slot */ 80 81 u_short fs_nr ; /* flow_set number */ 82 u_short flags_fs; 83 #define DNOLD_HAVE_FLOW_MASK 0x0001 84 #define DNOLD_IS_RED 0x0002 85 #define DNOLD_IS_GENTLE_RED 0x0004 86 #define DNOLD_QSIZE_IS_BYTES 0x0008 /* queue size is measured in bytes */ 87 #define DNOLD_NOERROR 0x0010 /* do not report ENOBUFS on drops */ 88 #define DNOLD_HAS_PROFILE 0x0020 /* the pipe has a delay profile. */ 89 #define DNOLD_IS_PIPE 0x4000 90 #define DNOLD_IS_QUEUE 0x8000 91 92 struct dn_pipe7 *pipe ; /* pointer to parent pipe */ 93 u_short parent_nr ; /* parent pipe#, 0 if local to a pipe */ 94 95 int weight ; /* WFQ queue weight */ 96 int qsize ; /* queue size in slots or bytes */ 97 int plr ; /* pkt loss rate (2^31-1 means 100%) */ 98 99 struct ipfw_flow_id flow_mask ; 100 101 /* hash table of queues onto this flow_set */ 102 int rq_size ; /* number of slots */ 103 int rq_elements ; /* active elements */ 104 struct dn_flow_queue7 **rq; /* array of rq_size entries */ 105 106 u_int32_t last_expired ; /* do not expire too frequently */ 107 int backlogged ; /* #active queues for this flowset */ 108 109 /* RED parameters */ 110 #define SCALE_RED 16 111 #define SCALE(x) ( (x) << SCALE_RED ) 112 #define SCALE_VAL(x) ( (x) >> SCALE_RED ) 113 #define SCALE_MUL(x,y) ( ( (x) * (y) ) >> SCALE_RED ) 114 int w_q ; /* queue weight (scaled) */ 115 int max_th ; /* maximum threshold for queue (scaled) */ 116 int min_th ; /* minimum threshold for queue (scaled) */ 117 int max_p ; /* maximum value for p_b (scaled) */ 118 u_int c_1 ; /* max_p/(max_th-min_th) (scaled) */ 119 u_int c_2 ; /* max_p*min_th/(max_th-min_th) (scaled) */ 120 u_int c_3 ; /* for GRED, (1-max_p)/max_th (scaled) */ 121 u_int c_4 ; /* for GRED, 1 - 2*max_p (scaled) */ 122 u_int * w_q_lookup ; /* lookup table for computing (1-w_q)^t */ 123 u_int lookup_depth ; /* depth of lookup table */ 124 int lookup_step ; /* granularity inside the lookup table */ 125 int lookup_weight ; /* equal to (1-w_q)^t / (1-w_q)^(t+1) */ 126 int avg_pkt_size ; /* medium packet size */ 127 int max_pkt_size ; /* max packet size */ 128 }; 129 SLIST_HEAD(dn_flow_set_head, dn_flow_set); 130 131 #define DN_IS_PIPE 0x4000 132 #define DN_IS_QUEUE 0x8000 133 struct dn_flow_queue7 { 134 struct dn_flow_queue7 *next ; 135 struct ipfw_flow_id id ; 136 137 struct mbuf *head, *tail ; /* queue of packets */ 138 u_int len ; 139 u_int len_bytes ; 140 141 u_long numbytes; 142 143 u_int64_t tot_pkts ; /* statistics counters */ 144 u_int64_t tot_bytes ; 145 u_int32_t drops ; 146 147 int hash_slot ; /* debugging/diagnostic */ 148 149 /* RED parameters */ 150 int avg ; /* average queue length est. (scaled) */ 151 int count ; /* arrivals since last RED drop */ 152 int random ; /* random value (scaled) */ 153 u_int32_t q_time; /* start of queue idle time */ 154 155 /* WF2Q+ support */ 156 struct dn_flow_set *fs ; /* parent flow set */ 157 int heap_pos ; /* position (index) of struct in heap */ 158 int64_t sched_time ; /* current time when queue enters ready_heap */ 159 160 int64_t S,F ; /* start time, finish time */ 161 }; 162 163 struct dn_pipe7 { /* a pipe */ 164 SLIST_ENTRY(dn_pipe7) next; /* linked list in a hash slot */ 165 166 int pipe_nr ; /* number */ 167 int bandwidth; /* really, bytes/tick. */ 168 int delay ; /* really, ticks */ 169 170 struct mbuf *head, *tail ; /* packets in delay line */ 171 172 /* WF2Q+ */ 173 struct dn_heap7 scheduler_heap ; /* top extract - key Finish time*/ 174 struct dn_heap7 not_eligible_heap; /* top extract- key Start time */ 175 struct dn_heap7 idle_heap ; /* random extract - key Start=Finish time */ 176 177 int64_t V ; /* virtual time */ 178 int sum; /* sum of weights of all active sessions */ 179 180 int numbytes; 181 182 int64_t sched_time ; /* time pipe was scheduled in ready_heap */ 183 184 /* 185 * When the tx clock come from an interface (if_name[0] != '\0'), its name 186 * is stored below, whereas the ifp is filled when the rule is configured. 187 */ 188 char if_name[IFNAMSIZ]; 189 struct ifnet *ifp ; 190 int ready ; /* set if ifp != NULL and we got a signal from it */ 191 192 struct dn_flow_set fs ; /* used with fixed-rate flows */ 193 }; 194 SLIST_HEAD(dn_pipe_head7, dn_pipe7); 195 196 197 /* FREEBSD8 ip_dummynet.h r196045 */ 198 struct dn_flow_queue8 { 199 struct dn_flow_queue8 *next ; 200 struct ipfw_flow_id id ; 201 202 struct mbuf *head, *tail ; /* queue of packets */ 203 u_int len ; 204 u_int len_bytes ; 205 206 uint64_t numbytes ; /* credit for transmission (dynamic queues) */ 207 int64_t extra_bits; /* extra bits simulating unavailable channel */ 208 209 u_int64_t tot_pkts ; /* statistics counters */ 210 u_int64_t tot_bytes ; 211 u_int32_t drops ; 212 213 int hash_slot ; /* debugging/diagnostic */ 214 215 /* RED parameters */ 216 int avg ; /* average queue length est. (scaled) */ 217 int count ; /* arrivals since last RED drop */ 218 int random ; /* random value (scaled) */ 219 int64_t idle_time; /* start of queue idle time */ 220 221 /* WF2Q+ support */ 222 struct dn_flow_set *fs ; /* parent flow set */ 223 int heap_pos ; /* position (index) of struct in heap */ 224 int64_t sched_time ; /* current time when queue enters ready_heap */ 225 226 int64_t S,F ; /* start time, finish time */ 227 }; 228 229 struct dn_pipe8 { /* a pipe */ 230 SLIST_ENTRY(dn_pipe8) next; /* linked list in a hash slot */ 231 232 int pipe_nr ; /* number */ 233 int bandwidth; /* really, bytes/tick. */ 234 int delay ; /* really, ticks */ 235 236 struct mbuf *head, *tail ; /* packets in delay line */ 237 238 /* WF2Q+ */ 239 struct dn_heap7 scheduler_heap ; /* top extract - key Finish time*/ 240 struct dn_heap7 not_eligible_heap; /* top extract- key Start time */ 241 struct dn_heap7 idle_heap ; /* random extract - key Start=Finish time */ 242 243 int64_t V ; /* virtual time */ 244 int sum; /* sum of weights of all active sessions */ 245 246 /* Same as in dn_flow_queue, numbytes can become large */ 247 int64_t numbytes; /* bits I can transmit (more or less). */ 248 uint64_t burst; /* burst size, scaled: bits * hz */ 249 250 int64_t sched_time ; /* time pipe was scheduled in ready_heap */ 251 int64_t idle_time; /* start of pipe idle time */ 252 253 char if_name[IFNAMSIZ]; 254 struct ifnet *ifp ; 255 int ready ; /* set if ifp != NULL and we got a signal from it */ 256 257 struct dn_flow_set fs ; /* used with fixed-rate flows */ 258 259 /* fields to simulate a delay profile */ 260 #define ED_MAX_NAME_LEN 32 261 char name[ED_MAX_NAME_LEN]; 262 int loss_level; 263 int samples_no; 264 int *samples; 265 }; 266 267 #define ED_MAX_SAMPLES_NO 1024 268 struct dn_pipe_max8 { 269 struct dn_pipe8 pipe; 270 int samples[ED_MAX_SAMPLES_NO]; 271 }; 272 SLIST_HEAD(dn_pipe_head8, dn_pipe8); 273 274 /* 275 * Changes from 7.2 to 8: 276 * dn_pipe: 277 * numbytes from int to int64_t 278 * add burst (int64_t) 279 * add idle_time (int64_t) 280 * add profile 281 * add struct dn_pipe_max 282 * add flag DN_HAS_PROFILE 283 * 284 * dn_flow_queue 285 * numbytes from u_long to int64_t 286 * add extra_bits (int64_t) 287 * q_time from u_int32_t to int64_t and name idle_time 288 * 289 * dn_flow_set unchanged 290 * 291 */ 292 293 /* NOTE:XXX copied from dummynet.c */ 294 #define O_NEXT(p, len) ((void *)((char *)p + len)) 295 static void 296 oid_fill(struct dn_id *oid, int len, int type, uintptr_t id) 297 { 298 oid->len = len; 299 oid->type = type; 300 oid->subtype = 0; 301 oid->id = id; 302 } 303 /* make room in the buffer and move the pointer forward */ 304 static void * 305 o_next(struct dn_id **o, int len, int type) 306 { 307 struct dn_id *ret = *o; 308 oid_fill(ret, len, type, 0); 309 *o = O_NEXT(*o, len); 310 return ret; 311 } 312 313 314 static size_t pipesize7 = sizeof(struct dn_pipe7); 315 static size_t pipesize8 = sizeof(struct dn_pipe8); 316 static size_t pipesizemax8 = sizeof(struct dn_pipe_max8); 317 318 /* Indicate 'ipfw' version 319 * 1: from FreeBSD 7.2 320 * 0: from FreeBSD 8 321 * -1: unknown (for now is unused) 322 * 323 * It is update when a IP_DUMMYNET_DEL or IP_DUMMYNET_CONFIGURE request arrives 324 * NOTE: if a IP_DUMMYNET_GET arrives and the 'ipfw' version is unknown, 325 * it is suppose to be the FreeBSD 8 version. 326 */ 327 static int is7 = 0; 328 329 static int 330 convertflags2new(int src) 331 { 332 int dst = 0; 333 334 if (src & DNOLD_HAVE_FLOW_MASK) 335 dst |= DN_HAVE_MASK; 336 if (src & DNOLD_QSIZE_IS_BYTES) 337 dst |= DN_QSIZE_BYTES; 338 if (src & DNOLD_NOERROR) 339 dst |= DN_NOERROR; 340 if (src & DNOLD_IS_RED) 341 dst |= DN_IS_RED; 342 if (src & DNOLD_IS_GENTLE_RED) 343 dst |= DN_IS_GENTLE_RED; 344 if (src & DNOLD_HAS_PROFILE) 345 dst |= DN_HAS_PROFILE; 346 347 return dst; 348 } 349 350 static int 351 convertflags2old(int src) 352 { 353 int dst = 0; 354 355 if (src & DN_HAVE_MASK) 356 dst |= DNOLD_HAVE_FLOW_MASK; 357 if (src & DN_IS_RED) 358 dst |= DNOLD_IS_RED; 359 if (src & DN_IS_GENTLE_RED) 360 dst |= DNOLD_IS_GENTLE_RED; 361 if (src & DN_NOERROR) 362 dst |= DNOLD_NOERROR; 363 if (src & DN_HAS_PROFILE) 364 dst |= DNOLD_HAS_PROFILE; 365 if (src & DN_QSIZE_BYTES) 366 dst |= DNOLD_QSIZE_IS_BYTES; 367 368 return dst; 369 } 370 371 static int 372 dn_compat_del(void *v) 373 { 374 struct dn_pipe7 *p = (struct dn_pipe7 *) v; 375 struct dn_pipe8 *p8 = (struct dn_pipe8 *) v; 376 struct { 377 struct dn_id oid; 378 uintptr_t a[1]; /* add more if we want a list */ 379 } cmd; 380 381 /* XXX DN_API_VERSION ??? */ 382 oid_fill((void *)&cmd, sizeof(cmd), DN_CMD_DELETE, DN_API_VERSION); 383 384 if (is7) { 385 if (p->pipe_nr == 0 && p->fs.fs_nr == 0) 386 return EINVAL; 387 if (p->pipe_nr != 0 && p->fs.fs_nr != 0) 388 return EINVAL; 389 } else { 390 if (p8->pipe_nr == 0 && p8->fs.fs_nr == 0) 391 return EINVAL; 392 if (p8->pipe_nr != 0 && p8->fs.fs_nr != 0) 393 return EINVAL; 394 } 395 396 if (p->pipe_nr != 0) { /* pipe x delete */ 397 cmd.a[0] = p->pipe_nr; 398 cmd.oid.subtype = DN_LINK; 399 } else { /* queue x delete */ 400 cmd.oid.subtype = DN_FS; 401 cmd.a[0] = (is7) ? p->fs.fs_nr : p8->fs.fs_nr; 402 } 403 404 return do_config(&cmd, cmd.oid.len); 405 } 406 407 static int 408 dn_compat_config_queue(struct dn_fs *fs, void* v) 409 { 410 struct dn_pipe7 *p7 = (struct dn_pipe7 *)v; 411 struct dn_pipe8 *p8 = (struct dn_pipe8 *)v; 412 struct dn_flow_set *f; 413 414 if (is7) 415 f = &p7->fs; 416 else 417 f = &p8->fs; 418 419 fs->fs_nr = f->fs_nr; 420 fs->sched_nr = f->parent_nr; 421 fs->flow_mask = f->flow_mask; 422 fs->buckets = f->rq_size; 423 fs->qsize = f->qsize; 424 fs->plr = f->plr; 425 fs->par[0] = f->weight; 426 fs->flags = convertflags2new(f->flags_fs); 427 if (fs->flags & DN_IS_GENTLE_RED || fs->flags & DN_IS_RED) { 428 fs->w_q = f->w_q; 429 fs->max_th = f->max_th; 430 fs->min_th = f->min_th; 431 fs->max_p = f->max_p; 432 } 433 434 return 0; 435 } 436 437 static int 438 dn_compat_config_pipe(struct dn_sch *sch, struct dn_link *p, 439 struct dn_fs *fs, void* v) 440 { 441 struct dn_pipe7 *p7 = (struct dn_pipe7 *)v; 442 struct dn_pipe8 *p8 = (struct dn_pipe8 *)v; 443 int i = p7->pipe_nr; 444 445 sch->sched_nr = i; 446 sch->oid.subtype = 0; 447 p->link_nr = i; 448 fs->fs_nr = i + 2*DN_MAX_ID; 449 fs->sched_nr = i + DN_MAX_ID; 450 451 /* Common to 7 and 8 */ 452 p->bandwidth = p7->bandwidth; 453 p->delay = p7->delay; 454 if (!is7) { 455 /* FreeBSD 8 has burst */ 456 p->burst = p8->burst; 457 } 458 459 /* fill the fifo flowset */ 460 dn_compat_config_queue(fs, v); 461 fs->fs_nr = i + 2*DN_MAX_ID; 462 fs->sched_nr = i + DN_MAX_ID; 463 464 /* Move scheduler related parameter from fs to sch */ 465 sch->buckets = fs->buckets; /*XXX*/ 466 fs->buckets = 0; 467 if (fs->flags & DN_HAVE_MASK) { 468 sch->flags |= DN_HAVE_MASK; 469 fs->flags &= ~DN_HAVE_MASK; 470 sch->sched_mask = fs->flow_mask; 471 bzero(&fs->flow_mask, sizeof(struct ipfw_flow_id)); 472 } 473 474 return 0; 475 } 476 477 static int 478 dn_compat_config_profile(struct dn_profile *pf, struct dn_link *p, 479 void *v) 480 { 481 struct dn_pipe8 *p8 = (struct dn_pipe8 *)v; 482 483 p8->samples = &(((struct dn_pipe_max8 *)p8)->samples[0]); 484 485 pf->link_nr = p->link_nr; 486 pf->loss_level = p8->loss_level; 487 // pf->bandwidth = p->bandwidth; //XXX bandwidth redundant? 488 pf->samples_no = p8->samples_no; 489 strncpy(pf->name, p8->name,sizeof(pf->name)); 490 bcopy(p8->samples, pf->samples, sizeof(pf->samples)); 491 492 return 0; 493 } 494 495 /* 496 * If p->pipe_nr != 0 the command is 'pipe x config', so need to create 497 * the three main struct, else only a flowset is created 498 */ 499 static int 500 dn_compat_configure(void *v) 501 { 502 struct dn_id *buf = NULL, *base; 503 struct dn_sch *sch = NULL; 504 struct dn_link *p = NULL; 505 struct dn_fs *fs = NULL; 506 struct dn_profile *pf = NULL; 507 int lmax; 508 int error; 509 510 struct dn_pipe7 *p7 = (struct dn_pipe7 *)v; 511 struct dn_pipe8 *p8 = (struct dn_pipe8 *)v; 512 513 int i; /* number of object to configure */ 514 515 lmax = sizeof(struct dn_id); /* command header */ 516 lmax += sizeof(struct dn_sch) + sizeof(struct dn_link) + 517 sizeof(struct dn_fs) + sizeof(struct dn_profile); 518 519 base = buf = malloc(lmax, M_DUMMYNET, M_WAITOK|M_ZERO); 520 o_next(&buf, sizeof(struct dn_id), DN_CMD_CONFIG); 521 base->id = DN_API_VERSION; 522 523 /* pipe_nr is the same in p7 and p8 */ 524 i = p7->pipe_nr; 525 if (i != 0) { /* pipe config */ 526 sch = o_next(&buf, sizeof(*sch), DN_SCH); 527 p = o_next(&buf, sizeof(*p), DN_LINK); 528 fs = o_next(&buf, sizeof(*fs), DN_FS); 529 530 error = dn_compat_config_pipe(sch, p, fs, v); 531 if (error) { 532 free(buf, M_DUMMYNET); 533 return error; 534 } 535 if (!is7 && p8->samples_no > 0) { 536 /* Add profiles*/ 537 pf = o_next(&buf, sizeof(*pf), DN_PROFILE); 538 error = dn_compat_config_profile(pf, p, v); 539 if (error) { 540 free(buf, M_DUMMYNET); 541 return error; 542 } 543 } 544 } else { /* queue config */ 545 fs = o_next(&buf, sizeof(*fs), DN_FS); 546 error = dn_compat_config_queue(fs, v); 547 if (error) { 548 free(buf, M_DUMMYNET); 549 return error; 550 } 551 } 552 error = do_config(base, (char *)buf - (char *)base); 553 554 if (buf) 555 free(buf, M_DUMMYNET); 556 return error; 557 } 558 559 int 560 dn_compat_calc_size(void) 561 { 562 int need = 0; 563 /* XXX use FreeBSD 8 struct size */ 564 /* NOTE: 565 * - half scheduler: schk_count/2 566 * - all flowset: fsk_count 567 * - all flowset queues: queue_count 568 * - all pipe queue: si_count 569 */ 570 need += dn_cfg.schk_count * sizeof(struct dn_pipe8) / 2; 571 need += dn_cfg.fsk_count * sizeof(struct dn_flow_set); 572 need += dn_cfg.si_count * sizeof(struct dn_flow_queue8); 573 need += dn_cfg.queue_count * sizeof(struct dn_flow_queue8); 574 575 return need; 576 } 577 578 int 579 dn_c_copy_q (void *_ni, void *arg) 580 { 581 struct copy_args *a = arg; 582 struct dn_flow_queue7 *fq7 = (struct dn_flow_queue7 *)*a->start; 583 struct dn_flow_queue8 *fq8 = (struct dn_flow_queue8 *)*a->start; 584 struct dn_flow *ni = (struct dn_flow *)_ni; 585 int size = 0; 586 587 /* XXX hash slot not set */ 588 /* No difference between 7.2/8 */ 589 fq7->len = ni->length; 590 fq7->len_bytes = ni->len_bytes; 591 fq7->id = ni->fid; 592 593 if (is7) { 594 size = sizeof(struct dn_flow_queue7); 595 fq7->tot_pkts = ni->tot_pkts; 596 fq7->tot_bytes = ni->tot_bytes; 597 fq7->drops = ni->drops; 598 } else { 599 size = sizeof(struct dn_flow_queue8); 600 fq8->tot_pkts = ni->tot_pkts; 601 fq8->tot_bytes = ni->tot_bytes; 602 fq8->drops = ni->drops; 603 } 604 605 *a->start += size; 606 return 0; 607 } 608 609 int 610 dn_c_copy_pipe(struct dn_schk *s, struct copy_args *a, int nq) 611 { 612 struct dn_link *l = &s->link; 613 struct dn_fsk *f = s->fs; 614 615 struct dn_pipe7 *pipe7 = (struct dn_pipe7 *)*a->start; 616 struct dn_pipe8 *pipe8 = (struct dn_pipe8 *)*a->start; 617 struct dn_flow_set *fs; 618 int size = 0; 619 620 if (is7) { 621 fs = &pipe7->fs; 622 size = sizeof(struct dn_pipe7); 623 } else { 624 fs = &pipe8->fs; 625 size = sizeof(struct dn_pipe8); 626 } 627 628 /* These 4 field are the same in pipe7 and pipe8 */ 629 pipe7->next.sle_next = (struct dn_pipe7 *)DN_IS_PIPE; 630 pipe7->bandwidth = l->bandwidth; 631 pipe7->delay = l->delay * 1000 / hz; 632 pipe7->pipe_nr = l->link_nr - DN_MAX_ID; 633 634 if (!is7) { 635 if (s->profile) { 636 struct dn_profile *pf = s->profile; 637 strncpy(pipe8->name, pf->name, sizeof(pf->name)); 638 pipe8->loss_level = pf->loss_level; 639 pipe8->samples_no = pf->samples_no; 640 } 641 pipe8->burst = div64(l->burst , 8 * hz); 642 } 643 644 fs->flow_mask = s->sch.sched_mask; 645 fs->rq_size = s->sch.buckets ? s->sch.buckets : 1; 646 647 fs->parent_nr = l->link_nr - DN_MAX_ID; 648 fs->qsize = f->fs.qsize; 649 fs->plr = f->fs.plr; 650 fs->w_q = f->fs.w_q; 651 fs->max_th = f->max_th; 652 fs->min_th = f->min_th; 653 fs->max_p = f->fs.max_p; 654 fs->rq_elements = nq; 655 656 fs->flags_fs = convertflags2old(f->fs.flags); 657 658 *a->start += size; 659 return 0; 660 } 661 662 663 int 664 dn_compat_copy_pipe(struct copy_args *a, void *_o) 665 { 666 int have = a->end - *a->start; 667 int need = 0; 668 int pipe_size = sizeof(struct dn_pipe8); 669 int queue_size = sizeof(struct dn_flow_queue8); 670 int n_queue = 0; /* number of queues */ 671 672 struct dn_schk *s = (struct dn_schk *)_o; 673 /* calculate needed space: 674 * - struct dn_pipe 675 * - if there are instances, dn_queue * n_instances 676 */ 677 n_queue = (s->sch.flags & DN_HAVE_MASK ? dn_ht_entries(s->siht) : 678 (s->siht ? 1 : 0)); 679 need = pipe_size + queue_size * n_queue; 680 if (have < need) { 681 D("have %d < need %d", have, need); 682 return 1; 683 } 684 /* copy pipe */ 685 dn_c_copy_pipe(s, a, n_queue); 686 687 /* copy queues */ 688 if (s->sch.flags & DN_HAVE_MASK) 689 dn_ht_scan(s->siht, dn_c_copy_q, a); 690 else if (s->siht) 691 dn_c_copy_q(s->siht, a); 692 return 0; 693 } 694 695 int 696 dn_c_copy_fs(struct dn_fsk *f, struct copy_args *a, int nq) 697 { 698 struct dn_flow_set *fs = (struct dn_flow_set *)*a->start; 699 700 fs->next.sle_next = (struct dn_flow_set *)DN_IS_QUEUE; 701 fs->fs_nr = f->fs.fs_nr; 702 fs->qsize = f->fs.qsize; 703 fs->plr = f->fs.plr; 704 fs->w_q = f->fs.w_q; 705 fs->max_th = f->max_th; 706 fs->min_th = f->min_th; 707 fs->max_p = f->fs.max_p; 708 fs->flow_mask = f->fs.flow_mask; 709 fs->rq_elements = nq; 710 fs->rq_size = (f->fs.buckets ? f->fs.buckets : 1); 711 fs->parent_nr = f->fs.sched_nr; 712 fs->weight = f->fs.par[0]; 713 714 fs->flags_fs = convertflags2old(f->fs.flags); 715 *a->start += sizeof(struct dn_flow_set); 716 return 0; 717 } 718 719 int 720 dn_compat_copy_queue(struct copy_args *a, void *_o) 721 { 722 int have = a->end - *a->start; 723 int need = 0; 724 int fs_size = sizeof(struct dn_flow_set); 725 int queue_size = sizeof(struct dn_flow_queue8); 726 727 struct dn_fsk *fs = (struct dn_fsk *)_o; 728 int n_queue = 0; /* number of queues */ 729 730 n_queue = (fs->fs.flags & DN_HAVE_MASK ? dn_ht_entries(fs->qht) : 731 (fs->qht ? 1 : 0)); 732 733 need = fs_size + queue_size * n_queue; 734 if (have < need) { 735 D("have < need"); 736 return 1; 737 } 738 739 /* copy flowset */ 740 dn_c_copy_fs(fs, a, n_queue); 741 742 /* copy queues */ 743 if (fs->fs.flags & DN_HAVE_MASK) 744 dn_ht_scan(fs->qht, dn_c_copy_q, a); 745 else if (fs->qht) 746 dn_c_copy_q(fs->qht, a); 747 748 return 0; 749 } 750 751 int 752 copy_data_helper_compat(void *_o, void *_arg) 753 { 754 struct copy_args *a = _arg; 755 756 if (a->type == DN_COMPAT_PIPE) { 757 struct dn_schk *s = _o; 758 if (s->sch.oid.subtype != 1 || s->sch.sched_nr <= DN_MAX_ID) { 759 return 0; /* not old type */ 760 } 761 /* copy pipe parameters, and if instance exists, copy 762 * other parameters and eventually queues. 763 */ 764 if(dn_compat_copy_pipe(a, _o)) 765 return DNHT_SCAN_END; 766 } else if (a->type == DN_COMPAT_QUEUE) { 767 struct dn_fsk *fs = _o; 768 if (fs->fs.fs_nr >= DN_MAX_ID) 769 return 0; 770 if (dn_compat_copy_queue(a, _o)) 771 return DNHT_SCAN_END; 772 } 773 return 0; 774 } 775 776 /* Main function to manage old requests */ 777 int 778 ip_dummynet_compat(struct sockopt *sopt) 779 { 780 int error=0; 781 void *v = NULL; 782 struct dn_id oid; 783 784 /* Length of data, used to found ipfw version... */ 785 int len = sopt->sopt_valsize; 786 787 /* len can be 0 if command was dummynet_flush */ 788 if (len == pipesize7) { 789 D("setting compatibility with FreeBSD 7.2"); 790 is7 = 1; 791 } 792 else if (len == pipesize8 || len == pipesizemax8) { 793 D("setting compatibility with FreeBSD 8"); 794 is7 = 0; 795 } 796 797 switch (sopt->sopt_name) { 798 default: 799 printf("dummynet: -- unknown option %d", sopt->sopt_name); 800 error = EINVAL; 801 break; 802 803 case IP_DUMMYNET_FLUSH: 804 oid_fill(&oid, sizeof(oid), DN_CMD_FLUSH, DN_API_VERSION); 805 do_config(&oid, oid.len); 806 break; 807 808 case IP_DUMMYNET_DEL: 809 v = malloc(len, M_TEMP, M_WAITOK); 810 error = sooptcopyin(sopt, v, len, len); 811 if (error) 812 break; 813 error = dn_compat_del(v); 814 free(v, M_TEMP); 815 break; 816 817 case IP_DUMMYNET_CONFIGURE: 818 v = malloc(len, M_TEMP, M_WAITOK); 819 error = sooptcopyin(sopt, v, len, len); 820 if (error) 821 break; 822 error = dn_compat_configure(v); 823 free(v, M_TEMP); 824 break; 825 826 case IP_DUMMYNET_GET: { 827 void *buf; 828 int ret; 829 int original_size = sopt->sopt_valsize; 830 int size; 831 832 ret = dummynet_get(sopt, &buf); 833 if (ret) 834 return 0;//XXX ? 835 size = sopt->sopt_valsize; 836 sopt->sopt_valsize = original_size; 837 D("size=%d, buf=%p", size, buf); 838 ret = sooptcopyout(sopt, buf, size); 839 if (ret) 840 printf(" %s ERROR sooptcopyout\n", __FUNCTION__); 841 if (buf) 842 free(buf, M_DUMMYNET); 843 } 844 } 845 846 return error; 847 } 848 849 850