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