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