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[4] ; /* 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[0] = f->plr[0]; 424 fs->plr[1] = f->plr[1]; 425 fs->plr[2] = f->plr[2]; 426 fs->plr[3] = f->plr[3]; 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 += V_dn_cfg.schk_count * sizeof(struct dn_pipe8) / 2; 573 need += V_dn_cfg.fsk_count * sizeof(struct dn_flow_set); 574 need += V_dn_cfg.si_count * sizeof(struct dn_flow_queue8); 575 need += V_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[0] = f->fs.plr[0]; 652 fs->plr[1] = f->fs.plr[1]; 653 fs->plr[2] = f->fs.plr[2]; 654 fs->plr[3] = f->fs.plr[3]; 655 fs->w_q = f->fs.w_q; 656 fs->max_th = f->max_th; 657 fs->min_th = f->min_th; 658 fs->max_p = f->fs.max_p; 659 fs->rq_elements = nq; 660 661 fs->flags_fs = convertflags2old(f->fs.flags); 662 663 *a->start += size; 664 return 0; 665 } 666 667 int 668 dn_compat_copy_pipe(struct copy_args *a, void *_o) 669 { 670 int have = a->end - *a->start; 671 int need = 0; 672 int pipe_size = sizeof(struct dn_pipe8); 673 int queue_size = sizeof(struct dn_flow_queue8); 674 int n_queue = 0; /* number of queues */ 675 676 struct dn_schk *s = (struct dn_schk *)_o; 677 /* calculate needed space: 678 * - struct dn_pipe 679 * - if there are instances, dn_queue * n_instances 680 */ 681 n_queue = (s->sch.flags & DN_HAVE_MASK ? dn_ht_entries(s->siht) : 682 (s->siht ? 1 : 0)); 683 need = pipe_size + queue_size * n_queue; 684 if (have < need) { 685 D("have %d < need %d", have, need); 686 return 1; 687 } 688 /* copy pipe */ 689 dn_c_copy_pipe(s, a, n_queue); 690 691 /* copy queues */ 692 if (s->sch.flags & DN_HAVE_MASK) 693 dn_ht_scan(s->siht, dn_c_copy_q, a); 694 else if (s->siht) 695 dn_c_copy_q(s->siht, a); 696 return 0; 697 } 698 699 int 700 dn_c_copy_fs(struct dn_fsk *f, struct copy_args *a, int nq) 701 { 702 struct dn_flow_set *fs = (struct dn_flow_set *)*a->start; 703 704 fs->next.sle_next = (struct dn_flow_set *)DN_IS_QUEUE; 705 fs->fs_nr = f->fs.fs_nr; 706 fs->qsize = f->fs.qsize; 707 fs->plr[0] = f->fs.plr[0]; 708 fs->plr[1] = f->fs.plr[1]; 709 fs->plr[2] = f->fs.plr[2]; 710 fs->plr[3] = f->fs.plr[3]; 711 fs->w_q = f->fs.w_q; 712 fs->max_th = f->max_th; 713 fs->min_th = f->min_th; 714 fs->max_p = f->fs.max_p; 715 fs->flow_mask = f->fs.flow_mask; 716 fs->rq_elements = nq; 717 fs->rq_size = (f->fs.buckets ? f->fs.buckets : 1); 718 fs->parent_nr = f->fs.sched_nr; 719 fs->weight = f->fs.par[0]; 720 721 fs->flags_fs = convertflags2old(f->fs.flags); 722 *a->start += sizeof(struct dn_flow_set); 723 return 0; 724 } 725 726 int 727 dn_compat_copy_queue(struct copy_args *a, void *_o) 728 { 729 int have = a->end - *a->start; 730 int need = 0; 731 int fs_size = sizeof(struct dn_flow_set); 732 int queue_size = sizeof(struct dn_flow_queue8); 733 734 struct dn_fsk *fs = (struct dn_fsk *)_o; 735 int n_queue = 0; /* number of queues */ 736 737 n_queue = (fs->fs.flags & DN_HAVE_MASK ? dn_ht_entries(fs->qht) : 738 (fs->qht ? 1 : 0)); 739 740 need = fs_size + queue_size * n_queue; 741 if (have < need) { 742 D("have < need"); 743 return 1; 744 } 745 746 /* copy flowset */ 747 dn_c_copy_fs(fs, a, n_queue); 748 749 /* copy queues */ 750 if (fs->fs.flags & DN_HAVE_MASK) 751 dn_ht_scan(fs->qht, dn_c_copy_q, a); 752 else if (fs->qht) 753 dn_c_copy_q(fs->qht, a); 754 755 return 0; 756 } 757 758 int 759 copy_data_helper_compat(void *_o, void *_arg) 760 { 761 struct copy_args *a = _arg; 762 763 if (a->type == DN_COMPAT_PIPE) { 764 struct dn_schk *s = _o; 765 if (s->sch.oid.subtype != 1 || s->sch.sched_nr <= DN_MAX_ID) { 766 return 0; /* not old type */ 767 } 768 /* copy pipe parameters, and if instance exists, copy 769 * other parameters and eventually queues. 770 */ 771 if(dn_compat_copy_pipe(a, _o)) 772 return DNHT_SCAN_END; 773 } else if (a->type == DN_COMPAT_QUEUE) { 774 struct dn_fsk *fs = _o; 775 if (fs->fs.fs_nr >= DN_MAX_ID) 776 return 0; 777 if (dn_compat_copy_queue(a, _o)) 778 return DNHT_SCAN_END; 779 } 780 return 0; 781 } 782 783 /* Main function to manage old requests */ 784 int 785 ip_dummynet_compat(struct sockopt *sopt) 786 { 787 int error=0; 788 void *v = NULL; 789 struct dn_id oid; 790 791 /* Length of data, used to found ipfw version... */ 792 int len = sopt->sopt_valsize; 793 794 /* len can be 0 if command was dummynet_flush */ 795 if (len == pipesize7) { 796 D("setting compatibility with FreeBSD 7.2"); 797 is7 = 1; 798 } 799 else if (len == pipesize8 || len == pipesizemax8) { 800 D("setting compatibility with FreeBSD 8"); 801 is7 = 0; 802 } 803 804 switch (sopt->sopt_name) { 805 default: 806 printf("dummynet: -- unknown option %d", sopt->sopt_name); 807 error = EINVAL; 808 break; 809 810 case IP_DUMMYNET_FLUSH: 811 oid_fill(&oid, sizeof(oid), DN_CMD_FLUSH, DN_API_VERSION); 812 do_config(&oid, oid.len); 813 break; 814 815 case IP_DUMMYNET_DEL: 816 v = malloc(len, M_TEMP, M_WAITOK); 817 error = sooptcopyin(sopt, v, len, len); 818 if (error) 819 break; 820 error = dn_compat_del(v); 821 free(v, M_TEMP); 822 break; 823 824 case IP_DUMMYNET_CONFIGURE: 825 v = malloc(len, M_TEMP, M_NOWAIT); 826 if (v == NULL) { 827 error = ENOMEM; 828 break; 829 } 830 error = sooptcopyin(sopt, v, len, len); 831 if (error) 832 break; 833 error = dn_compat_configure(v); 834 free(v, M_TEMP); 835 break; 836 837 case IP_DUMMYNET_GET: { 838 void *buf; 839 int ret; 840 int original_size = sopt->sopt_valsize; 841 int size; 842 843 ret = dummynet_get(sopt, &buf); 844 if (ret) 845 return 0;//XXX ? 846 size = sopt->sopt_valsize; 847 sopt->sopt_valsize = original_size; 848 D("size=%d, buf=%p", size, buf); 849 ret = sooptcopyout(sopt, buf, size); 850 if (ret) 851 printf(" %s ERROR sooptcopyout\n", __FUNCTION__); 852 if (buf) 853 free(buf, M_DUMMYNET); 854 } 855 } 856 857 return error; 858 } 859