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