1213267Sluigi/*- 2204591Sluigi * Copyright (c) 2010 Riccardo Panicucci, Universita` di Pisa 3204591Sluigi * All rights reserved 4204591Sluigi * 5204591Sluigi * Redistribution and use in source and binary forms, with or without 6204591Sluigi * modification, are permitted provided that the following conditions 7204591Sluigi * are met: 8204591Sluigi * 1. Redistributions of source code must retain the above copyright 9204591Sluigi * notice, this list of conditions and the following disclaimer. 10204591Sluigi * 2. Redistributions in binary form must reproduce the above copyright 11204591Sluigi * notice, this list of conditions and the following disclaimer in the 12204591Sluigi * documentation and/or other materials provided with the distribution. 13204591Sluigi * 14204591Sluigi * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15204591Sluigi * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16204591Sluigi * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17204591Sluigi * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18204591Sluigi * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19204591Sluigi * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20204591Sluigi * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21204591Sluigi * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22204591Sluigi * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23204591Sluigi * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24204591Sluigi * SUCH DAMAGE. 25204591Sluigi */ 26204591Sluigi 27204591Sluigi/* 28204591Sluigi * $FreeBSD$ 29204591Sluigi * 30204591Sluigi * Binary compatibility support for /sbin/ipfw RELENG_7 and RELENG_8 31204591Sluigi */ 32204591Sluigi 33204591Sluigi#include "opt_inet6.h" 34204591Sluigi 35204591Sluigi#include <sys/param.h> 36204591Sluigi#include <sys/systm.h> 37204591Sluigi#include <sys/malloc.h> 38204591Sluigi#include <sys/mbuf.h> 39204591Sluigi#include <sys/kernel.h> 40204591Sluigi#include <sys/lock.h> 41204591Sluigi#include <sys/module.h> 42204591Sluigi#include <sys/priv.h> 43204591Sluigi#include <sys/proc.h> 44204591Sluigi#include <sys/rwlock.h> 45204591Sluigi#include <sys/socket.h> 46204591Sluigi#include <sys/socketvar.h> 47204591Sluigi#include <sys/time.h> 48204591Sluigi#include <sys/taskqueue.h> 49204591Sluigi#include <net/if.h> /* IFNAMSIZ, struct ifaddr, ifq head, lock.h mutex.h */ 50204591Sluigi#include <netinet/in.h> 51204591Sluigi#include <netinet/ip_var.h> /* ip_output(), IP_FORWARDING */ 52204591Sluigi#include <netinet/ip_fw.h> 53204591Sluigi#include <netinet/ip_dummynet.h> 54204591Sluigi 55240494Sglebius#include <netpfil/ipfw/ip_fw_private.h> 56240494Sglebius#include <netpfil/ipfw/dn_heap.h> 57240494Sglebius#include <netpfil/ipfw/ip_dn_private.h> 58300779Struckman#ifdef NEW_AQM 59300779Struckman#include <netpfil/ipfw/dn_aqm.h> 60300779Struckman#endif 61240494Sglebius#include <netpfil/ipfw/dn_sched.h> 62240494Sglebius 63204591Sluigi/* FREEBSD7.2 ip_dummynet.h r191715*/ 64204591Sluigi 65204591Sluigistruct dn_heap_entry7 { 66204591Sluigi int64_t key; /* sorting key. Topmost element is smallest one */ 67204591Sluigi void *object; /* object pointer */ 68204591Sluigi}; 69204591Sluigi 70204591Sluigistruct dn_heap7 { 71204591Sluigi int size; 72204591Sluigi int elements; 73204591Sluigi int offset; /* XXX if > 0 this is the offset of direct ptr to obj */ 74204591Sluigi struct dn_heap_entry7 *p; /* really an array of "size" entries */ 75204591Sluigi}; 76204591Sluigi 77204591Sluigi/* Common to 7.2 and 8 */ 78204591Sluigistruct dn_flow_set { 79204591Sluigi SLIST_ENTRY(dn_flow_set) next; /* linked list in a hash slot */ 80204591Sluigi 81204591Sluigi u_short fs_nr ; /* flow_set number */ 82204591Sluigi u_short flags_fs; 83204591Sluigi#define DNOLD_HAVE_FLOW_MASK 0x0001 84204591Sluigi#define DNOLD_IS_RED 0x0002 85204591Sluigi#define DNOLD_IS_GENTLE_RED 0x0004 86204591Sluigi#define DNOLD_QSIZE_IS_BYTES 0x0008 /* queue size is measured in bytes */ 87204591Sluigi#define DNOLD_NOERROR 0x0010 /* do not report ENOBUFS on drops */ 88204591Sluigi#define DNOLD_HAS_PROFILE 0x0020 /* the pipe has a delay profile. */ 89204591Sluigi#define DNOLD_IS_PIPE 0x4000 90204591Sluigi#define DNOLD_IS_QUEUE 0x8000 91204591Sluigi 92204591Sluigi struct dn_pipe7 *pipe ; /* pointer to parent pipe */ 93204591Sluigi u_short parent_nr ; /* parent pipe#, 0 if local to a pipe */ 94204591Sluigi 95204591Sluigi int weight ; /* WFQ queue weight */ 96204591Sluigi int qsize ; /* queue size in slots or bytes */ 97204591Sluigi int plr ; /* pkt loss rate (2^31-1 means 100%) */ 98204591Sluigi 99204591Sluigi struct ipfw_flow_id flow_mask ; 100204591Sluigi 101204591Sluigi /* hash table of queues onto this flow_set */ 102204591Sluigi int rq_size ; /* number of slots */ 103204591Sluigi int rq_elements ; /* active elements */ 104204591Sluigi struct dn_flow_queue7 **rq; /* array of rq_size entries */ 105204591Sluigi 106204591Sluigi u_int32_t last_expired ; /* do not expire too frequently */ 107204591Sluigi int backlogged ; /* #active queues for this flowset */ 108204591Sluigi 109204591Sluigi /* RED parameters */ 110204591Sluigi#define SCALE_RED 16 111204591Sluigi#define SCALE(x) ( (x) << SCALE_RED ) 112204591Sluigi#define SCALE_VAL(x) ( (x) >> SCALE_RED ) 113204591Sluigi#define SCALE_MUL(x,y) ( ( (x) * (y) ) >> SCALE_RED ) 114204591Sluigi int w_q ; /* queue weight (scaled) */ 115204591Sluigi int max_th ; /* maximum threshold for queue (scaled) */ 116204591Sluigi int min_th ; /* minimum threshold for queue (scaled) */ 117204591Sluigi int max_p ; /* maximum value for p_b (scaled) */ 118204591Sluigi u_int c_1 ; /* max_p/(max_th-min_th) (scaled) */ 119204591Sluigi u_int c_2 ; /* max_p*min_th/(max_th-min_th) (scaled) */ 120204591Sluigi u_int c_3 ; /* for GRED, (1-max_p)/max_th (scaled) */ 121204591Sluigi u_int c_4 ; /* for GRED, 1 - 2*max_p (scaled) */ 122204591Sluigi u_int * w_q_lookup ; /* lookup table for computing (1-w_q)^t */ 123204591Sluigi u_int lookup_depth ; /* depth of lookup table */ 124204591Sluigi int lookup_step ; /* granularity inside the lookup table */ 125204591Sluigi int lookup_weight ; /* equal to (1-w_q)^t / (1-w_q)^(t+1) */ 126204591Sluigi int avg_pkt_size ; /* medium packet size */ 127204591Sluigi int max_pkt_size ; /* max packet size */ 128204591Sluigi}; 129204591SluigiSLIST_HEAD(dn_flow_set_head, dn_flow_set); 130204591Sluigi 131204591Sluigi#define DN_IS_PIPE 0x4000 132204591Sluigi#define DN_IS_QUEUE 0x8000 133204591Sluigistruct dn_flow_queue7 { 134204591Sluigi struct dn_flow_queue7 *next ; 135204591Sluigi struct ipfw_flow_id id ; 136204591Sluigi 137204591Sluigi struct mbuf *head, *tail ; /* queue of packets */ 138204591Sluigi u_int len ; 139204591Sluigi u_int len_bytes ; 140204591Sluigi 141204591Sluigi u_long numbytes; 142204591Sluigi 143204591Sluigi u_int64_t tot_pkts ; /* statistics counters */ 144204591Sluigi u_int64_t tot_bytes ; 145204591Sluigi u_int32_t drops ; 146204591Sluigi 147204591Sluigi int hash_slot ; /* debugging/diagnostic */ 148204591Sluigi 149204591Sluigi /* RED parameters */ 150204591Sluigi int avg ; /* average queue length est. (scaled) */ 151204591Sluigi int count ; /* arrivals since last RED drop */ 152204591Sluigi int random ; /* random value (scaled) */ 153204591Sluigi u_int32_t q_time; /* start of queue idle time */ 154204591Sluigi 155204591Sluigi /* WF2Q+ support */ 156204591Sluigi struct dn_flow_set *fs ; /* parent flow set */ 157204591Sluigi int heap_pos ; /* position (index) of struct in heap */ 158204591Sluigi int64_t sched_time ; /* current time when queue enters ready_heap */ 159204591Sluigi 160204591Sluigi int64_t S,F ; /* start time, finish time */ 161204591Sluigi}; 162204591Sluigi 163204591Sluigistruct dn_pipe7 { /* a pipe */ 164204591Sluigi SLIST_ENTRY(dn_pipe7) next; /* linked list in a hash slot */ 165204591Sluigi 166204591Sluigi int pipe_nr ; /* number */ 167204591Sluigi int bandwidth; /* really, bytes/tick. */ 168204591Sluigi int delay ; /* really, ticks */ 169204591Sluigi 170204591Sluigi struct mbuf *head, *tail ; /* packets in delay line */ 171204591Sluigi 172204591Sluigi /* WF2Q+ */ 173204591Sluigi struct dn_heap7 scheduler_heap ; /* top extract - key Finish time*/ 174204591Sluigi struct dn_heap7 not_eligible_heap; /* top extract- key Start time */ 175204591Sluigi struct dn_heap7 idle_heap ; /* random extract - key Start=Finish time */ 176204591Sluigi 177204591Sluigi int64_t V ; /* virtual time */ 178204591Sluigi int sum; /* sum of weights of all active sessions */ 179204591Sluigi 180204591Sluigi int numbytes; 181204591Sluigi 182204591Sluigi int64_t sched_time ; /* time pipe was scheduled in ready_heap */ 183204591Sluigi 184204591Sluigi /* 185204591Sluigi * When the tx clock come from an interface (if_name[0] != '\0'), its name 186204591Sluigi * is stored below, whereas the ifp is filled when the rule is configured. 187204591Sluigi */ 188204591Sluigi char if_name[IFNAMSIZ]; 189204591Sluigi struct ifnet *ifp ; 190204591Sluigi int ready ; /* set if ifp != NULL and we got a signal from it */ 191204591Sluigi 192204591Sluigi struct dn_flow_set fs ; /* used with fixed-rate flows */ 193204591Sluigi}; 194204591SluigiSLIST_HEAD(dn_pipe_head7, dn_pipe7); 195204591Sluigi 196204591Sluigi 197204591Sluigi/* FREEBSD8 ip_dummynet.h r196045 */ 198204591Sluigistruct dn_flow_queue8 { 199204591Sluigi struct dn_flow_queue8 *next ; 200204591Sluigi struct ipfw_flow_id id ; 201204591Sluigi 202204591Sluigi struct mbuf *head, *tail ; /* queue of packets */ 203204591Sluigi u_int len ; 204204591Sluigi u_int len_bytes ; 205204591Sluigi 206204591Sluigi uint64_t numbytes ; /* credit for transmission (dynamic queues) */ 207204591Sluigi int64_t extra_bits; /* extra bits simulating unavailable channel */ 208204591Sluigi 209204591Sluigi u_int64_t tot_pkts ; /* statistics counters */ 210204591Sluigi u_int64_t tot_bytes ; 211204591Sluigi u_int32_t drops ; 212204591Sluigi 213204591Sluigi int hash_slot ; /* debugging/diagnostic */ 214204591Sluigi 215204591Sluigi /* RED parameters */ 216204591Sluigi int avg ; /* average queue length est. (scaled) */ 217204591Sluigi int count ; /* arrivals since last RED drop */ 218204591Sluigi int random ; /* random value (scaled) */ 219204591Sluigi int64_t idle_time; /* start of queue idle time */ 220204591Sluigi 221204591Sluigi /* WF2Q+ support */ 222204591Sluigi struct dn_flow_set *fs ; /* parent flow set */ 223204591Sluigi int heap_pos ; /* position (index) of struct in heap */ 224204591Sluigi int64_t sched_time ; /* current time when queue enters ready_heap */ 225204591Sluigi 226204591Sluigi int64_t S,F ; /* start time, finish time */ 227204591Sluigi}; 228204591Sluigi 229204591Sluigistruct dn_pipe8 { /* a pipe */ 230204591Sluigi SLIST_ENTRY(dn_pipe8) next; /* linked list in a hash slot */ 231204591Sluigi 232204591Sluigi int pipe_nr ; /* number */ 233204591Sluigi int bandwidth; /* really, bytes/tick. */ 234204591Sluigi int delay ; /* really, ticks */ 235204591Sluigi 236204591Sluigi struct mbuf *head, *tail ; /* packets in delay line */ 237204591Sluigi 238204591Sluigi /* WF2Q+ */ 239204591Sluigi struct dn_heap7 scheduler_heap ; /* top extract - key Finish time*/ 240204591Sluigi struct dn_heap7 not_eligible_heap; /* top extract- key Start time */ 241204591Sluigi struct dn_heap7 idle_heap ; /* random extract - key Start=Finish time */ 242204591Sluigi 243204591Sluigi int64_t V ; /* virtual time */ 244204591Sluigi int sum; /* sum of weights of all active sessions */ 245204591Sluigi 246204591Sluigi /* Same as in dn_flow_queue, numbytes can become large */ 247204591Sluigi int64_t numbytes; /* bits I can transmit (more or less). */ 248204591Sluigi uint64_t burst; /* burst size, scaled: bits * hz */ 249204591Sluigi 250204591Sluigi int64_t sched_time ; /* time pipe was scheduled in ready_heap */ 251204591Sluigi int64_t idle_time; /* start of pipe idle time */ 252204591Sluigi 253204591Sluigi char if_name[IFNAMSIZ]; 254204591Sluigi struct ifnet *ifp ; 255204591Sluigi int ready ; /* set if ifp != NULL and we got a signal from it */ 256204591Sluigi 257204591Sluigi struct dn_flow_set fs ; /* used with fixed-rate flows */ 258204591Sluigi 259204591Sluigi /* fields to simulate a delay profile */ 260204591Sluigi#define ED_MAX_NAME_LEN 32 261204591Sluigi char name[ED_MAX_NAME_LEN]; 262204591Sluigi int loss_level; 263204591Sluigi int samples_no; 264204591Sluigi int *samples; 265204591Sluigi}; 266204591Sluigi 267204591Sluigi#define ED_MAX_SAMPLES_NO 1024 268204591Sluigistruct dn_pipe_max8 { 269204591Sluigi struct dn_pipe8 pipe; 270204591Sluigi int samples[ED_MAX_SAMPLES_NO]; 271204591Sluigi}; 272204591SluigiSLIST_HEAD(dn_pipe_head8, dn_pipe8); 273204591Sluigi 274204591Sluigi/* 275204591Sluigi * Changes from 7.2 to 8: 276204591Sluigi * dn_pipe: 277204591Sluigi * numbytes from int to int64_t 278204591Sluigi * add burst (int64_t) 279204591Sluigi * add idle_time (int64_t) 280204591Sluigi * add profile 281204591Sluigi * add struct dn_pipe_max 282204591Sluigi * add flag DN_HAS_PROFILE 283204591Sluigi * 284204591Sluigi * dn_flow_queue 285204591Sluigi * numbytes from u_long to int64_t 286204591Sluigi * add extra_bits (int64_t) 287204591Sluigi * q_time from u_int32_t to int64_t and name idle_time 288204591Sluigi * 289204591Sluigi * dn_flow_set unchanged 290204591Sluigi * 291204591Sluigi */ 292204591Sluigi 293204591Sluigi/* NOTE:XXX copied from dummynet.c */ 294204591Sluigi#define O_NEXT(p, len) ((void *)((char *)p + len)) 295204591Sluigistatic void 296204591Sluigioid_fill(struct dn_id *oid, int len, int type, uintptr_t id) 297204591Sluigi{ 298204591Sluigi oid->len = len; 299204591Sluigi oid->type = type; 300204591Sluigi oid->subtype = 0; 301204591Sluigi oid->id = id; 302204591Sluigi} 303204591Sluigi/* make room in the buffer and move the pointer forward */ 304204591Sluigistatic void * 305204591Sluigio_next(struct dn_id **o, int len, int type) 306204591Sluigi{ 307204591Sluigi struct dn_id *ret = *o; 308204591Sluigi oid_fill(ret, len, type, 0); 309204591Sluigi *o = O_NEXT(*o, len); 310204591Sluigi return ret; 311204591Sluigi} 312204591Sluigi 313204591Sluigi 314204591Sluigistatic size_t pipesize7 = sizeof(struct dn_pipe7); 315204591Sluigistatic size_t pipesize8 = sizeof(struct dn_pipe8); 316204591Sluigistatic size_t pipesizemax8 = sizeof(struct dn_pipe_max8); 317204591Sluigi 318204591Sluigi/* Indicate 'ipfw' version 319204591Sluigi * 1: from FreeBSD 7.2 320204591Sluigi * 0: from FreeBSD 8 321241369Skevlo * -1: unknown (for now is unused) 322204591Sluigi * 323204591Sluigi * It is update when a IP_DUMMYNET_DEL or IP_DUMMYNET_CONFIGURE request arrives 324241369Skevlo * NOTE: if a IP_DUMMYNET_GET arrives and the 'ipfw' version is unknown, 325204591Sluigi * it is suppose to be the FreeBSD 8 version. 326204591Sluigi */ 327204591Sluigistatic int is7 = 0; 328204591Sluigi 329204591Sluigistatic int 330204591Sluigiconvertflags2new(int src) 331204591Sluigi{ 332204591Sluigi int dst = 0; 333204591Sluigi 334204591Sluigi if (src & DNOLD_HAVE_FLOW_MASK) 335204591Sluigi dst |= DN_HAVE_MASK; 336204591Sluigi if (src & DNOLD_QSIZE_IS_BYTES) 337204591Sluigi dst |= DN_QSIZE_BYTES; 338204591Sluigi if (src & DNOLD_NOERROR) 339204591Sluigi dst |= DN_NOERROR; 340204591Sluigi if (src & DNOLD_IS_RED) 341204591Sluigi dst |= DN_IS_RED; 342204591Sluigi if (src & DNOLD_IS_GENTLE_RED) 343204591Sluigi dst |= DN_IS_GENTLE_RED; 344204591Sluigi if (src & DNOLD_HAS_PROFILE) 345204591Sluigi dst |= DN_HAS_PROFILE; 346204591Sluigi 347204591Sluigi return dst; 348204591Sluigi} 349204591Sluigi 350204591Sluigistatic int 351204591Sluigiconvertflags2old(int src) 352204591Sluigi{ 353204591Sluigi int dst = 0; 354204591Sluigi 355204591Sluigi if (src & DN_HAVE_MASK) 356204591Sluigi dst |= DNOLD_HAVE_FLOW_MASK; 357204591Sluigi if (src & DN_IS_RED) 358204591Sluigi dst |= DNOLD_IS_RED; 359204591Sluigi if (src & DN_IS_GENTLE_RED) 360204591Sluigi dst |= DNOLD_IS_GENTLE_RED; 361204591Sluigi if (src & DN_NOERROR) 362204591Sluigi dst |= DNOLD_NOERROR; 363204591Sluigi if (src & DN_HAS_PROFILE) 364204591Sluigi dst |= DNOLD_HAS_PROFILE; 365204591Sluigi if (src & DN_QSIZE_BYTES) 366204591Sluigi dst |= DNOLD_QSIZE_IS_BYTES; 367204591Sluigi 368204591Sluigi return dst; 369204591Sluigi} 370204591Sluigi 371204591Sluigistatic int 372204591Sluigidn_compat_del(void *v) 373204591Sluigi{ 374204591Sluigi struct dn_pipe7 *p = (struct dn_pipe7 *) v; 375204591Sluigi struct dn_pipe8 *p8 = (struct dn_pipe8 *) v; 376204591Sluigi struct { 377204591Sluigi struct dn_id oid; 378204591Sluigi uintptr_t a[1]; /* add more if we want a list */ 379204591Sluigi } cmd; 380204591Sluigi 381204591Sluigi /* XXX DN_API_VERSION ??? */ 382204591Sluigi oid_fill((void *)&cmd, sizeof(cmd), DN_CMD_DELETE, DN_API_VERSION); 383204591Sluigi 384204591Sluigi if (is7) { 385204591Sluigi if (p->pipe_nr == 0 && p->fs.fs_nr == 0) 386204591Sluigi return EINVAL; 387204591Sluigi if (p->pipe_nr != 0 && p->fs.fs_nr != 0) 388204591Sluigi return EINVAL; 389204591Sluigi } else { 390204591Sluigi if (p8->pipe_nr == 0 && p8->fs.fs_nr == 0) 391204591Sluigi return EINVAL; 392204591Sluigi if (p8->pipe_nr != 0 && p8->fs.fs_nr != 0) 393204591Sluigi return EINVAL; 394204591Sluigi } 395204591Sluigi 396204591Sluigi if (p->pipe_nr != 0) { /* pipe x delete */ 397204591Sluigi cmd.a[0] = p->pipe_nr; 398204591Sluigi cmd.oid.subtype = DN_LINK; 399204591Sluigi } else { /* queue x delete */ 400204591Sluigi cmd.oid.subtype = DN_FS; 401204591Sluigi cmd.a[0] = (is7) ? p->fs.fs_nr : p8->fs.fs_nr; 402204591Sluigi } 403204591Sluigi 404204591Sluigi return do_config(&cmd, cmd.oid.len); 405204591Sluigi} 406204591Sluigi 407204591Sluigistatic int 408204591Sluigidn_compat_config_queue(struct dn_fs *fs, void* v) 409204591Sluigi{ 410204591Sluigi struct dn_pipe7 *p7 = (struct dn_pipe7 *)v; 411204591Sluigi struct dn_pipe8 *p8 = (struct dn_pipe8 *)v; 412204591Sluigi struct dn_flow_set *f; 413204591Sluigi 414204591Sluigi if (is7) 415204591Sluigi f = &p7->fs; 416204591Sluigi else 417204591Sluigi f = &p8->fs; 418204591Sluigi 419204591Sluigi fs->fs_nr = f->fs_nr; 420204591Sluigi fs->sched_nr = f->parent_nr; 421204591Sluigi fs->flow_mask = f->flow_mask; 422204591Sluigi fs->buckets = f->rq_size; 423204591Sluigi fs->qsize = f->qsize; 424204591Sluigi fs->plr = f->plr; 425204591Sluigi fs->par[0] = f->weight; 426204591Sluigi fs->flags = convertflags2new(f->flags_fs); 427204591Sluigi if (fs->flags & DN_IS_GENTLE_RED || fs->flags & DN_IS_RED) { 428204591Sluigi fs->w_q = f->w_q; 429204591Sluigi fs->max_th = f->max_th; 430204591Sluigi fs->min_th = f->min_th; 431204591Sluigi fs->max_p = f->max_p; 432204591Sluigi } 433204591Sluigi 434204591Sluigi return 0; 435204591Sluigi} 436204591Sluigi 437204591Sluigistatic int 438204591Sluigidn_compat_config_pipe(struct dn_sch *sch, struct dn_link *p, 439204591Sluigi struct dn_fs *fs, void* v) 440204591Sluigi{ 441204591Sluigi struct dn_pipe7 *p7 = (struct dn_pipe7 *)v; 442204591Sluigi struct dn_pipe8 *p8 = (struct dn_pipe8 *)v; 443204591Sluigi int i = p7->pipe_nr; 444204591Sluigi 445204591Sluigi sch->sched_nr = i; 446204591Sluigi sch->oid.subtype = 0; 447204591Sluigi p->link_nr = i; 448204591Sluigi fs->fs_nr = i + 2*DN_MAX_ID; 449204591Sluigi fs->sched_nr = i + DN_MAX_ID; 450204591Sluigi 451204591Sluigi /* Common to 7 and 8 */ 452204591Sluigi p->bandwidth = p7->bandwidth; 453204591Sluigi p->delay = p7->delay; 454204591Sluigi if (!is7) { 455204591Sluigi /* FreeBSD 8 has burst */ 456204591Sluigi p->burst = p8->burst; 457204591Sluigi } 458204591Sluigi 459204591Sluigi /* fill the fifo flowset */ 460204591Sluigi dn_compat_config_queue(fs, v); 461204591Sluigi fs->fs_nr = i + 2*DN_MAX_ID; 462204591Sluigi fs->sched_nr = i + DN_MAX_ID; 463204591Sluigi 464204591Sluigi /* Move scheduler related parameter from fs to sch */ 465204591Sluigi sch->buckets = fs->buckets; /*XXX*/ 466204591Sluigi fs->buckets = 0; 467204591Sluigi if (fs->flags & DN_HAVE_MASK) { 468204591Sluigi sch->flags |= DN_HAVE_MASK; 469204591Sluigi fs->flags &= ~DN_HAVE_MASK; 470204591Sluigi sch->sched_mask = fs->flow_mask; 471204591Sluigi bzero(&fs->flow_mask, sizeof(struct ipfw_flow_id)); 472204591Sluigi } 473204591Sluigi 474204591Sluigi return 0; 475204591Sluigi} 476204591Sluigi 477204591Sluigistatic int 478204591Sluigidn_compat_config_profile(struct dn_profile *pf, struct dn_link *p, 479204591Sluigi void *v) 480204591Sluigi{ 481204591Sluigi struct dn_pipe8 *p8 = (struct dn_pipe8 *)v; 482204591Sluigi 483204591Sluigi p8->samples = &(((struct dn_pipe_max8 *)p8)->samples[0]); 484204591Sluigi 485204591Sluigi pf->link_nr = p->link_nr; 486204591Sluigi pf->loss_level = p8->loss_level; 487204591Sluigi// pf->bandwidth = p->bandwidth; //XXX bandwidth redundant? 488204591Sluigi pf->samples_no = p8->samples_no; 489204591Sluigi strncpy(pf->name, p8->name,sizeof(pf->name)); 490204591Sluigi bcopy(p8->samples, pf->samples, sizeof(pf->samples)); 491204591Sluigi 492204591Sluigi return 0; 493204591Sluigi} 494204591Sluigi 495204591Sluigi/* 496204591Sluigi * If p->pipe_nr != 0 the command is 'pipe x config', so need to create 497204591Sluigi * the three main struct, else only a flowset is created 498204591Sluigi */ 499204591Sluigistatic int 500204591Sluigidn_compat_configure(void *v) 501204591Sluigi{ 502204954Sluigi struct dn_id *buf = NULL, *base; 503204591Sluigi struct dn_sch *sch = NULL; 504204591Sluigi struct dn_link *p = NULL; 505204591Sluigi struct dn_fs *fs = NULL; 506204591Sluigi struct dn_profile *pf = NULL; 507204591Sluigi int lmax; 508204591Sluigi int error; 509204591Sluigi 510204591Sluigi struct dn_pipe7 *p7 = (struct dn_pipe7 *)v; 511204591Sluigi struct dn_pipe8 *p8 = (struct dn_pipe8 *)v; 512204591Sluigi 513204591Sluigi int i; /* number of object to configure */ 514204591Sluigi 515204591Sluigi lmax = sizeof(struct dn_id); /* command header */ 516204591Sluigi lmax += sizeof(struct dn_sch) + sizeof(struct dn_link) + 517204591Sluigi sizeof(struct dn_fs) + sizeof(struct dn_profile); 518204591Sluigi 519243882Sglebius base = buf = malloc(lmax, M_DUMMYNET, M_WAITOK|M_ZERO); 520204591Sluigi o_next(&buf, sizeof(struct dn_id), DN_CMD_CONFIG); 521204591Sluigi base->id = DN_API_VERSION; 522204591Sluigi 523204591Sluigi /* pipe_nr is the same in p7 and p8 */ 524204591Sluigi i = p7->pipe_nr; 525204591Sluigi if (i != 0) { /* pipe config */ 526204591Sluigi sch = o_next(&buf, sizeof(*sch), DN_SCH); 527204591Sluigi p = o_next(&buf, sizeof(*p), DN_LINK); 528204591Sluigi fs = o_next(&buf, sizeof(*fs), DN_FS); 529204591Sluigi 530204591Sluigi error = dn_compat_config_pipe(sch, p, fs, v); 531204591Sluigi if (error) { 532204591Sluigi free(buf, M_DUMMYNET); 533204591Sluigi return error; 534204591Sluigi } 535204591Sluigi if (!is7 && p8->samples_no > 0) { 536204591Sluigi /* Add profiles*/ 537204591Sluigi pf = o_next(&buf, sizeof(*pf), DN_PROFILE); 538204591Sluigi error = dn_compat_config_profile(pf, p, v); 539204591Sluigi if (error) { 540204591Sluigi free(buf, M_DUMMYNET); 541204591Sluigi return error; 542204591Sluigi } 543204591Sluigi } 544204591Sluigi } else { /* queue config */ 545204591Sluigi fs = o_next(&buf, sizeof(*fs), DN_FS); 546204591Sluigi error = dn_compat_config_queue(fs, v); 547204591Sluigi if (error) { 548204591Sluigi free(buf, M_DUMMYNET); 549204591Sluigi return error; 550204591Sluigi } 551204591Sluigi } 552204591Sluigi error = do_config(base, (char *)buf - (char *)base); 553204591Sluigi 554204954Sluigi if (buf) 555204954Sluigi free(buf, M_DUMMYNET); 556204591Sluigi return error; 557204591Sluigi} 558204591Sluigi 559204591Sluigiint 560206425Sluigidn_compat_calc_size(void) 561204591Sluigi{ 562204591Sluigi int need = 0; 563204591Sluigi /* XXX use FreeBSD 8 struct size */ 564204591Sluigi /* NOTE: 565204591Sluigi * - half scheduler: schk_count/2 566204591Sluigi * - all flowset: fsk_count 567204591Sluigi * - all flowset queues: queue_count 568204591Sluigi * - all pipe queue: si_count 569204591Sluigi */ 570204591Sluigi need += dn_cfg.schk_count * sizeof(struct dn_pipe8) / 2; 571204591Sluigi need += dn_cfg.fsk_count * sizeof(struct dn_flow_set); 572204591Sluigi need += dn_cfg.si_count * sizeof(struct dn_flow_queue8); 573204591Sluigi need += dn_cfg.queue_count * sizeof(struct dn_flow_queue8); 574204591Sluigi 575204591Sluigi return need; 576204591Sluigi} 577204591Sluigi 578204591Sluigiint 579204591Sluigidn_c_copy_q (void *_ni, void *arg) 580204591Sluigi{ 581204591Sluigi struct copy_args *a = arg; 582204591Sluigi struct dn_flow_queue7 *fq7 = (struct dn_flow_queue7 *)*a->start; 583204591Sluigi struct dn_flow_queue8 *fq8 = (struct dn_flow_queue8 *)*a->start; 584204591Sluigi struct dn_flow *ni = (struct dn_flow *)_ni; 585204591Sluigi int size = 0; 586204591Sluigi 587204591Sluigi /* XXX hash slot not set */ 588204591Sluigi /* No difference between 7.2/8 */ 589204591Sluigi fq7->len = ni->length; 590204591Sluigi fq7->len_bytes = ni->len_bytes; 591204591Sluigi fq7->id = ni->fid; 592204591Sluigi 593204591Sluigi if (is7) { 594204591Sluigi size = sizeof(struct dn_flow_queue7); 595204591Sluigi fq7->tot_pkts = ni->tot_pkts; 596204591Sluigi fq7->tot_bytes = ni->tot_bytes; 597204591Sluigi fq7->drops = ni->drops; 598204591Sluigi } else { 599204591Sluigi size = sizeof(struct dn_flow_queue8); 600204591Sluigi fq8->tot_pkts = ni->tot_pkts; 601204591Sluigi fq8->tot_bytes = ni->tot_bytes; 602204591Sluigi fq8->drops = ni->drops; 603204591Sluigi } 604204591Sluigi 605204591Sluigi *a->start += size; 606204591Sluigi return 0; 607204591Sluigi} 608204591Sluigi 609204591Sluigiint 610204591Sluigidn_c_copy_pipe(struct dn_schk *s, struct copy_args *a, int nq) 611204591Sluigi{ 612204591Sluigi struct dn_link *l = &s->link; 613204591Sluigi struct dn_fsk *f = s->fs; 614204591Sluigi 615204591Sluigi struct dn_pipe7 *pipe7 = (struct dn_pipe7 *)*a->start; 616204591Sluigi struct dn_pipe8 *pipe8 = (struct dn_pipe8 *)*a->start; 617204591Sluigi struct dn_flow_set *fs; 618204591Sluigi int size = 0; 619204591Sluigi 620204591Sluigi if (is7) { 621204591Sluigi fs = &pipe7->fs; 622204591Sluigi size = sizeof(struct dn_pipe7); 623204591Sluigi } else { 624204591Sluigi fs = &pipe8->fs; 625204591Sluigi size = sizeof(struct dn_pipe8); 626204591Sluigi } 627204591Sluigi 628204591Sluigi /* These 4 field are the same in pipe7 and pipe8 */ 629204591Sluigi pipe7->next.sle_next = (struct dn_pipe7 *)DN_IS_PIPE; 630204591Sluigi pipe7->bandwidth = l->bandwidth; 631221521Sae pipe7->delay = l->delay * 1000 / hz; 632204591Sluigi pipe7->pipe_nr = l->link_nr - DN_MAX_ID; 633204591Sluigi 634204591Sluigi if (!is7) { 635204591Sluigi if (s->profile) { 636204591Sluigi struct dn_profile *pf = s->profile; 637204591Sluigi strncpy(pipe8->name, pf->name, sizeof(pf->name)); 638204591Sluigi pipe8->loss_level = pf->loss_level; 639204591Sluigi pipe8->samples_no = pf->samples_no; 640204591Sluigi } 641204591Sluigi pipe8->burst = div64(l->burst , 8 * hz); 642204591Sluigi } 643204591Sluigi 644204591Sluigi fs->flow_mask = s->sch.sched_mask; 645204591Sluigi fs->rq_size = s->sch.buckets ? s->sch.buckets : 1; 646204591Sluigi 647204591Sluigi fs->parent_nr = l->link_nr - DN_MAX_ID; 648204591Sluigi fs->qsize = f->fs.qsize; 649204591Sluigi fs->plr = f->fs.plr; 650204591Sluigi fs->w_q = f->fs.w_q; 651204591Sluigi fs->max_th = f->max_th; 652204591Sluigi fs->min_th = f->min_th; 653204591Sluigi fs->max_p = f->fs.max_p; 654204591Sluigi fs->rq_elements = nq; 655204591Sluigi 656204591Sluigi fs->flags_fs = convertflags2old(f->fs.flags); 657204591Sluigi 658204591Sluigi *a->start += size; 659204591Sluigi return 0; 660204591Sluigi} 661204591Sluigi 662204591Sluigi 663204591Sluigiint 664204591Sluigidn_compat_copy_pipe(struct copy_args *a, void *_o) 665204591Sluigi{ 666204591Sluigi int have = a->end - *a->start; 667204591Sluigi int need = 0; 668204591Sluigi int pipe_size = sizeof(struct dn_pipe8); 669204591Sluigi int queue_size = sizeof(struct dn_flow_queue8); 670204591Sluigi int n_queue = 0; /* number of queues */ 671204591Sluigi 672204591Sluigi struct dn_schk *s = (struct dn_schk *)_o; 673204591Sluigi /* calculate needed space: 674204591Sluigi * - struct dn_pipe 675204591Sluigi * - if there are instances, dn_queue * n_instances 676204591Sluigi */ 677204591Sluigi n_queue = (s->sch.flags & DN_HAVE_MASK ? dn_ht_entries(s->siht) : 678204591Sluigi (s->siht ? 1 : 0)); 679204591Sluigi need = pipe_size + queue_size * n_queue; 680204591Sluigi if (have < need) { 681204591Sluigi D("have %d < need %d", have, need); 682204591Sluigi return 1; 683204591Sluigi } 684204591Sluigi /* copy pipe */ 685204591Sluigi dn_c_copy_pipe(s, a, n_queue); 686204591Sluigi 687204591Sluigi /* copy queues */ 688204591Sluigi if (s->sch.flags & DN_HAVE_MASK) 689204591Sluigi dn_ht_scan(s->siht, dn_c_copy_q, a); 690204591Sluigi else if (s->siht) 691204591Sluigi dn_c_copy_q(s->siht, a); 692204591Sluigi return 0; 693204591Sluigi} 694204591Sluigi 695204591Sluigiint 696204591Sluigidn_c_copy_fs(struct dn_fsk *f, struct copy_args *a, int nq) 697204591Sluigi{ 698204591Sluigi struct dn_flow_set *fs = (struct dn_flow_set *)*a->start; 699204591Sluigi 700204591Sluigi fs->next.sle_next = (struct dn_flow_set *)DN_IS_QUEUE; 701204591Sluigi fs->fs_nr = f->fs.fs_nr; 702204591Sluigi fs->qsize = f->fs.qsize; 703204591Sluigi fs->plr = f->fs.plr; 704204591Sluigi fs->w_q = f->fs.w_q; 705204591Sluigi fs->max_th = f->max_th; 706204591Sluigi fs->min_th = f->min_th; 707204591Sluigi fs->max_p = f->fs.max_p; 708204591Sluigi fs->flow_mask = f->fs.flow_mask; 709204591Sluigi fs->rq_elements = nq; 710204591Sluigi fs->rq_size = (f->fs.buckets ? f->fs.buckets : 1); 711204591Sluigi fs->parent_nr = f->fs.sched_nr; 712204591Sluigi fs->weight = f->fs.par[0]; 713204591Sluigi 714204591Sluigi fs->flags_fs = convertflags2old(f->fs.flags); 715204591Sluigi *a->start += sizeof(struct dn_flow_set); 716204591Sluigi return 0; 717204591Sluigi} 718204591Sluigi 719204591Sluigiint 720204591Sluigidn_compat_copy_queue(struct copy_args *a, void *_o) 721204591Sluigi{ 722204591Sluigi int have = a->end - *a->start; 723204591Sluigi int need = 0; 724204591Sluigi int fs_size = sizeof(struct dn_flow_set); 725204591Sluigi int queue_size = sizeof(struct dn_flow_queue8); 726204591Sluigi 727204591Sluigi struct dn_fsk *fs = (struct dn_fsk *)_o; 728204591Sluigi int n_queue = 0; /* number of queues */ 729204591Sluigi 730204591Sluigi n_queue = (fs->fs.flags & DN_HAVE_MASK ? dn_ht_entries(fs->qht) : 731204591Sluigi (fs->qht ? 1 : 0)); 732204591Sluigi 733204591Sluigi need = fs_size + queue_size * n_queue; 734204591Sluigi if (have < need) { 735204591Sluigi D("have < need"); 736204591Sluigi return 1; 737204591Sluigi } 738204591Sluigi 739204591Sluigi /* copy flowset */ 740204591Sluigi dn_c_copy_fs(fs, a, n_queue); 741204591Sluigi 742204591Sluigi /* copy queues */ 743204591Sluigi if (fs->fs.flags & DN_HAVE_MASK) 744204591Sluigi dn_ht_scan(fs->qht, dn_c_copy_q, a); 745204591Sluigi else if (fs->qht) 746204591Sluigi dn_c_copy_q(fs->qht, a); 747204591Sluigi 748204591Sluigi return 0; 749204591Sluigi} 750204591Sluigi 751204591Sluigiint 752204591Sluigicopy_data_helper_compat(void *_o, void *_arg) 753204591Sluigi{ 754204591Sluigi struct copy_args *a = _arg; 755204591Sluigi 756204591Sluigi if (a->type == DN_COMPAT_PIPE) { 757204591Sluigi struct dn_schk *s = _o; 758204591Sluigi if (s->sch.oid.subtype != 1 || s->sch.sched_nr <= DN_MAX_ID) { 759204591Sluigi return 0; /* not old type */ 760204591Sluigi } 761204591Sluigi /* copy pipe parameters, and if instance exists, copy 762204591Sluigi * other parameters and eventually queues. 763204591Sluigi */ 764204591Sluigi if(dn_compat_copy_pipe(a, _o)) 765204591Sluigi return DNHT_SCAN_END; 766204591Sluigi } else if (a->type == DN_COMPAT_QUEUE) { 767204591Sluigi struct dn_fsk *fs = _o; 768204591Sluigi if (fs->fs.fs_nr >= DN_MAX_ID) 769204591Sluigi return 0; 770204591Sluigi if (dn_compat_copy_queue(a, _o)) 771204591Sluigi return DNHT_SCAN_END; 772204591Sluigi } 773204591Sluigi return 0; 774204591Sluigi} 775204591Sluigi 776204591Sluigi/* Main function to manage old requests */ 777204591Sluigiint 778204591Sluigiip_dummynet_compat(struct sockopt *sopt) 779204591Sluigi{ 780204591Sluigi int error=0; 781204591Sluigi void *v = NULL; 782204591Sluigi struct dn_id oid; 783204591Sluigi 784298995Spfg /* Length of data, used to found ipfw version... */ 785204591Sluigi int len = sopt->sopt_valsize; 786204591Sluigi 787204591Sluigi /* len can be 0 if command was dummynet_flush */ 788204591Sluigi if (len == pipesize7) { 789204591Sluigi D("setting compatibility with FreeBSD 7.2"); 790204591Sluigi is7 = 1; 791204591Sluigi } 792204591Sluigi else if (len == pipesize8 || len == pipesizemax8) { 793204591Sluigi D("setting compatibility with FreeBSD 8"); 794204591Sluigi is7 = 0; 795204591Sluigi } 796204591Sluigi 797204591Sluigi switch (sopt->sopt_name) { 798204591Sluigi default: 799204591Sluigi printf("dummynet: -- unknown option %d", sopt->sopt_name); 800204591Sluigi error = EINVAL; 801204591Sluigi break; 802204591Sluigi 803204591Sluigi case IP_DUMMYNET_FLUSH: 804204591Sluigi oid_fill(&oid, sizeof(oid), DN_CMD_FLUSH, DN_API_VERSION); 805204591Sluigi do_config(&oid, oid.len); 806204591Sluigi break; 807204591Sluigi 808204591Sluigi case IP_DUMMYNET_DEL: 809204591Sluigi v = malloc(len, M_TEMP, M_WAITOK); 810204591Sluigi error = sooptcopyin(sopt, v, len, len); 811204591Sluigi if (error) 812204591Sluigi break; 813204591Sluigi error = dn_compat_del(v); 814220204Sae free(v, M_TEMP); 815204591Sluigi break; 816204591Sluigi 817204591Sluigi case IP_DUMMYNET_CONFIGURE: 818204591Sluigi v = malloc(len, M_TEMP, M_WAITOK); 819204591Sluigi error = sooptcopyin(sopt, v, len, len); 820204591Sluigi if (error) 821204591Sluigi break; 822204591Sluigi error = dn_compat_configure(v); 823220204Sae free(v, M_TEMP); 824204591Sluigi break; 825204591Sluigi 826204591Sluigi case IP_DUMMYNET_GET: { 827204591Sluigi void *buf; 828204591Sluigi int ret; 829204591Sluigi int original_size = sopt->sopt_valsize; 830204591Sluigi int size; 831204591Sluigi 832204591Sluigi ret = dummynet_get(sopt, &buf); 833204591Sluigi if (ret) 834204591Sluigi return 0;//XXX ? 835204591Sluigi size = sopt->sopt_valsize; 836204591Sluigi sopt->sopt_valsize = original_size; 837204591Sluigi D("size=%d, buf=%p", size, buf); 838204591Sluigi ret = sooptcopyout(sopt, buf, size); 839204591Sluigi if (ret) 840204591Sluigi printf(" %s ERROR sooptcopyout\n", __FUNCTION__); 841204591Sluigi if (buf) 842204591Sluigi free(buf, M_DUMMYNET); 843204591Sluigi } 844204591Sluigi } 845204591Sluigi 846204591Sluigi return error; 847204591Sluigi} 848204591Sluigi 849204591Sluigi 850