ip_dn_glue.c revision 240494
1285242Sachim/*-
2285242Sachim * Copyright (c) 2010 Riccardo Panicucci, Universita` di Pisa
3285242Sachim * All rights reserved
4285242Sachim *
5285242Sachim * Redistribution and use in source and binary forms, with or without
6285242Sachim * modification, are permitted provided that the following conditions
7285242Sachim * are met:
8285242Sachim * 1. Redistributions of source code must retain the above copyright
9285242Sachim *    notice, this list of conditions and the following disclaimer.
10285242Sachim * 2. Redistributions in binary form must reproduce the above copyright
11285242Sachim *    notice, this list of conditions and the following disclaimer in the
12285242Sachim *    documentation and/or other materials provided with the distribution.
13285242Sachim *
14285242Sachim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15285242Sachim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16285242Sachim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17285242Sachim * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18285242Sachim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19285242Sachim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20285242Sachim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21285242Sachim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22285242Sachim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23285242Sachim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24285242Sachim * SUCH DAMAGE.
25285242Sachim */
26285242Sachim
27285242Sachim/*
28285242Sachim * $FreeBSD: head/sys/netpfil/ipfw/ip_dn_glue.c 240494 2012-09-14 11:51:49Z glebius $
29285242Sachim *
30285242Sachim * Binary compatibility support for /sbin/ipfw RELENG_7 and RELENG_8
31285242Sachim */
32285242Sachim
33285242Sachim#include "opt_inet6.h"
34285242Sachim
35285242Sachim#include <sys/param.h>
36285242Sachim#include <sys/systm.h>
37285242Sachim#include <sys/malloc.h>
38285242Sachim#include <sys/mbuf.h>
39285242Sachim#include <sys/kernel.h>
40285242Sachim#include <sys/lock.h>
41285242Sachim#include <sys/module.h>
42285242Sachim#include <sys/priv.h>
43285242Sachim#include <sys/proc.h>
44285242Sachim#include <sys/rwlock.h>
45285242Sachim#include <sys/socket.h>
46285242Sachim#include <sys/socketvar.h>
47285242Sachim#include <sys/time.h>
48285242Sachim#include <sys/taskqueue.h>
49285242Sachim#include <net/if.h>	/* IFNAMSIZ, struct ifaddr, ifq head, lock.h mutex.h */
50285242Sachim#include <netinet/in.h>
51285242Sachim#include <netinet/ip_var.h>	/* ip_output(), IP_FORWARDING */
52285242Sachim#include <netinet/ip_fw.h>
53285242Sachim#include <netinet/ip_dummynet.h>
54285242Sachim
55285242Sachim#include <netpfil/ipfw/ip_fw_private.h>
56285242Sachim#include <netpfil/ipfw/dn_heap.h>
57285242Sachim#include <netpfil/ipfw/ip_dn_private.h>
58285242Sachim#include <netpfil/ipfw/dn_sched.h>
59285242Sachim
60285242Sachim/* FREEBSD7.2 ip_dummynet.h r191715*/
61285242Sachim
62285242Sachimstruct dn_heap_entry7 {
63285242Sachim	int64_t key;        /* sorting key. Topmost element is smallest one */
64285242Sachim	void *object;      /* object pointer */
65285242Sachim};
66285242Sachim
67285242Sachimstruct dn_heap7 {
68285242Sachim	int size;
69285242Sachim	int elements;
70285242Sachim	int offset; /* XXX if > 0 this is the offset of direct ptr to obj */
71285242Sachim	struct dn_heap_entry7 *p;   /* really an array of "size" entries */
72285242Sachim};
73285242Sachim
74285242Sachim/* Common to 7.2 and 8 */
75285242Sachimstruct dn_flow_set {
76285242Sachim	SLIST_ENTRY(dn_flow_set)    next;   /* linked list in a hash slot */
77285242Sachim
78285242Sachim	u_short fs_nr ;             /* flow_set number       */
79285242Sachim	u_short flags_fs;
80285242Sachim#define DNOLD_HAVE_FLOW_MASK   0x0001
81285242Sachim#define DNOLD_IS_RED       0x0002
82285242Sachim#define DNOLD_IS_GENTLE_RED    0x0004
83285242Sachim#define DNOLD_QSIZE_IS_BYTES   0x0008  /* queue size is measured in bytes */
84285242Sachim#define DNOLD_NOERROR      0x0010  /* do not report ENOBUFS on drops  */
85285242Sachim#define DNOLD_HAS_PROFILE      0x0020  /* the pipe has a delay profile. */
86285242Sachim#define DNOLD_IS_PIPE      0x4000
87285242Sachim#define DNOLD_IS_QUEUE     0x8000
88285242Sachim
89285242Sachim	struct dn_pipe7 *pipe ;  /* pointer to parent pipe */
90285242Sachim	u_short parent_nr ;     /* parent pipe#, 0 if local to a pipe */
91285242Sachim
92285242Sachim	int weight ;        /* WFQ queue weight */
93285242Sachim	int qsize ;         /* queue size in slots or bytes */
94285242Sachim	int plr ;           /* pkt loss rate (2^31-1 means 100%) */
95285242Sachim
96285242Sachim	struct ipfw_flow_id flow_mask ;
97285242Sachim
98285242Sachim	/* hash table of queues onto this flow_set */
99285242Sachim	int rq_size ;       /* number of slots */
100285242Sachim	int rq_elements ;       /* active elements */
101285242Sachim	struct dn_flow_queue7 **rq;  /* array of rq_size entries */
102285242Sachim
103285242Sachim	u_int32_t last_expired ;    /* do not expire too frequently */
104285242Sachim	int backlogged ;        /* #active queues for this flowset */
105285242Sachim
106285242Sachim        /* RED parameters */
107285242Sachim#define SCALE_RED               16
108285242Sachim#define SCALE(x)                ( (x) << SCALE_RED )
109285242Sachim#define SCALE_VAL(x)            ( (x) >> SCALE_RED )
110285242Sachim#define SCALE_MUL(x,y)          ( ( (x) * (y) ) >> SCALE_RED )
111285242Sachim	int w_q ;           /* queue weight (scaled) */
112285242Sachim	int max_th ;        /* maximum threshold for queue (scaled) */
113285242Sachim	int min_th ;        /* minimum threshold for queue (scaled) */
114285242Sachim	int max_p ;         /* maximum value for p_b (scaled) */
115285242Sachim	u_int c_1 ;         /* max_p/(max_th-min_th) (scaled) */
116285242Sachim	u_int c_2 ;         /* max_p*min_th/(max_th-min_th) (scaled) */
117285242Sachim	u_int c_3 ;         /* for GRED, (1-max_p)/max_th (scaled) */
118285242Sachim	u_int c_4 ;         /* for GRED, 1 - 2*max_p (scaled) */
119285242Sachim	u_int * w_q_lookup ;    /* lookup table for computing (1-w_q)^t */
120285242Sachim	u_int lookup_depth ;    /* depth of lookup table */
121285242Sachim	int lookup_step ;       /* granularity inside the lookup table */
122285242Sachim	int lookup_weight ;     /* equal to (1-w_q)^t / (1-w_q)^(t+1) */
123285242Sachim	int avg_pkt_size ;      /* medium packet size */
124285242Sachim	int max_pkt_size ;      /* max packet size */
125285242Sachim};
126285242SachimSLIST_HEAD(dn_flow_set_head, dn_flow_set);
127285242Sachim
128285242Sachim#define DN_IS_PIPE		0x4000
129285242Sachim#define DN_IS_QUEUE		0x8000
130285242Sachimstruct dn_flow_queue7 {
131285242Sachim	struct dn_flow_queue7 *next ;
132285242Sachim	struct ipfw_flow_id id ;
133285242Sachim
134285242Sachim	struct mbuf *head, *tail ;  /* queue of packets */
135285242Sachim	u_int len ;
136285242Sachim	u_int len_bytes ;
137285242Sachim
138285242Sachim	u_long numbytes;
139285242Sachim
140285242Sachim	u_int64_t tot_pkts ;    /* statistics counters  */
141285242Sachim	u_int64_t tot_bytes ;
142285242Sachim	u_int32_t drops ;
143285242Sachim
144285242Sachim	int hash_slot ;     /* debugging/diagnostic */
145285242Sachim
146285242Sachim	/* RED parameters */
147285242Sachim	int avg ;                   /* average queue length est. (scaled) */
148285242Sachim	int count ;                 /* arrivals since last RED drop */
149285242Sachim	int random ;                /* random value (scaled) */
150285242Sachim	u_int32_t q_time;      /* start of queue idle time */
151285242Sachim
152285242Sachim	/* WF2Q+ support */
153285242Sachim	struct dn_flow_set *fs ;    /* parent flow set */
154285242Sachim	int heap_pos ;      /* position (index) of struct in heap */
155285242Sachim	int64_t sched_time ;     /* current time when queue enters ready_heap */
156285242Sachim
157285242Sachim	int64_t S,F ;        /* start time, finish time */
158285242Sachim};
159285242Sachim
160285242Sachimstruct dn_pipe7 {        /* a pipe */
161285242Sachim	SLIST_ENTRY(dn_pipe7)    next;   /* linked list in a hash slot */
162285242Sachim
163285242Sachim	int pipe_nr ;       /* number   */
164285242Sachim	int bandwidth;      /* really, bytes/tick.  */
165285242Sachim	int delay ;         /* really, ticks    */
166285242Sachim
167285242Sachim	struct  mbuf *head, *tail ; /* packets in delay line */
168285242Sachim
169285242Sachim	/* WF2Q+ */
170285242Sachim	struct dn_heap7 scheduler_heap ; /* top extract - key Finish time*/
171285242Sachim	struct dn_heap7 not_eligible_heap; /* top extract- key Start time */
172285242Sachim	struct dn_heap7 idle_heap ; /* random extract - key Start=Finish time */
173285242Sachim
174285242Sachim	int64_t V ;          /* virtual time */
175285242Sachim	int sum;            /* sum of weights of all active sessions */
176285242Sachim
177285242Sachim	int numbytes;
178285242Sachim
179285242Sachim	int64_t sched_time ;     /* time pipe was scheduled in ready_heap */
180285242Sachim
181285242Sachim	/*
182285242Sachim	* When the tx clock come from an interface (if_name[0] != '\0'), its name
183285242Sachim	* is stored below, whereas the ifp is filled when the rule is configured.
184285242Sachim	*/
185285242Sachim	char if_name[IFNAMSIZ];
186285242Sachim	struct ifnet *ifp ;
187285242Sachim	int ready ; /* set if ifp != NULL and we got a signal from it */
188285242Sachim
189285242Sachim	struct dn_flow_set fs ; /* used with fixed-rate flows */
190285242Sachim};
191285242SachimSLIST_HEAD(dn_pipe_head7, dn_pipe7);
192285242Sachim
193285242Sachim
194285242Sachim/* FREEBSD8 ip_dummynet.h r196045 */
195285242Sachimstruct dn_flow_queue8 {
196285242Sachim	struct dn_flow_queue8 *next ;
197285242Sachim	struct ipfw_flow_id id ;
198285242Sachim
199285242Sachim	struct mbuf *head, *tail ;  /* queue of packets */
200285242Sachim	u_int len ;
201285242Sachim	u_int len_bytes ;
202285242Sachim
203285242Sachim	uint64_t numbytes ;     /* credit for transmission (dynamic queues) */
204285242Sachim	int64_t extra_bits;     /* extra bits simulating unavailable channel */
205285242Sachim
206285242Sachim	u_int64_t tot_pkts ;    /* statistics counters  */
207285242Sachim	u_int64_t tot_bytes ;
208285242Sachim	u_int32_t drops ;
209285242Sachim
210285242Sachim	int hash_slot ;     /* debugging/diagnostic */
211285242Sachim
212285242Sachim	/* RED parameters */
213285242Sachim	int avg ;                   /* average queue length est. (scaled) */
214285242Sachim	int count ;                 /* arrivals since last RED drop */
215285242Sachim	int random ;                /* random value (scaled) */
216285242Sachim	int64_t idle_time;       /* start of queue idle time */
217285242Sachim
218285242Sachim	/* WF2Q+ support */
219285242Sachim	struct dn_flow_set *fs ;    /* parent flow set */
220285242Sachim	int heap_pos ;      /* position (index) of struct in heap */
221285242Sachim	int64_t sched_time ;     /* current time when queue enters ready_heap */
222285242Sachim
223285242Sachim	int64_t S,F ;        /* start time, finish time */
224285242Sachim};
225285242Sachim
226285242Sachimstruct dn_pipe8 {        /* a pipe */
227285242Sachim	SLIST_ENTRY(dn_pipe8)    next;   /* linked list in a hash slot */
228285242Sachim
229285242Sachim	int pipe_nr ;       /* number   */
230285242Sachim	int bandwidth;      /* really, bytes/tick.  */
231285242Sachim	int delay ;         /* really, ticks    */
232285242Sachim
233285242Sachim	struct  mbuf *head, *tail ; /* packets in delay line */
234285242Sachim
235285242Sachim	/* WF2Q+ */
236285242Sachim	struct dn_heap7 scheduler_heap ; /* top extract - key Finish time*/
237285242Sachim	struct dn_heap7 not_eligible_heap; /* top extract- key Start time */
238285242Sachim	struct dn_heap7 idle_heap ; /* random extract - key Start=Finish time */
239285242Sachim
240285242Sachim	int64_t V ;          /* virtual time */
241285242Sachim	int sum;            /* sum of weights of all active sessions */
242285242Sachim
243285242Sachim	/* Same as in dn_flow_queue, numbytes can become large */
244285242Sachim	int64_t numbytes;       /* bits I can transmit (more or less). */
245285242Sachim	uint64_t burst;     /* burst size, scaled: bits * hz */
246285242Sachim
247285242Sachim	int64_t sched_time ;     /* time pipe was scheduled in ready_heap */
248285242Sachim	int64_t idle_time;       /* start of pipe idle time */
249285242Sachim
250285242Sachim	char if_name[IFNAMSIZ];
251285242Sachim	struct ifnet *ifp ;
252285242Sachim	int ready ; /* set if ifp != NULL and we got a signal from it */
253285242Sachim
254285242Sachim	struct dn_flow_set fs ; /* used with fixed-rate flows */
255285242Sachim
256285242Sachim    /* fields to simulate a delay profile */
257285242Sachim#define ED_MAX_NAME_LEN     32
258285242Sachim	char name[ED_MAX_NAME_LEN];
259285242Sachim	int loss_level;
260285242Sachim	int samples_no;
261285242Sachim	int *samples;
262285242Sachim};
263285242Sachim
264285242Sachim#define ED_MAX_SAMPLES_NO   1024
265285242Sachimstruct dn_pipe_max8 {
266285242Sachim	struct dn_pipe8 pipe;
267285242Sachim	int samples[ED_MAX_SAMPLES_NO];
268285242Sachim};
269285242SachimSLIST_HEAD(dn_pipe_head8, dn_pipe8);
270285242Sachim
271285242Sachim/*
272285242Sachim * Changes from 7.2 to 8:
273285242Sachim * dn_pipe:
274285242Sachim *      numbytes from int to int64_t
275285242Sachim *      add burst (int64_t)
276285242Sachim *      add idle_time (int64_t)
277285242Sachim *      add profile
278285242Sachim *      add struct dn_pipe_max
279285242Sachim *      add flag DN_HAS_PROFILE
280285242Sachim *
281285242Sachim * dn_flow_queue
282285242Sachim *      numbytes from u_long to int64_t
283285242Sachim *      add extra_bits (int64_t)
284285242Sachim *      q_time from u_int32_t to int64_t and name idle_time
285285242Sachim *
286285242Sachim * dn_flow_set unchanged
287285242Sachim *
288285242Sachim */
289285242Sachim
290285242Sachim/* NOTE:XXX copied from dummynet.c */
291285242Sachim#define O_NEXT(p, len) ((void *)((char *)p + len))
292285242Sachimstatic void
293285242Sachimoid_fill(struct dn_id *oid, int len, int type, uintptr_t id)
294285242Sachim{
295285242Sachim	oid->len = len;
296285242Sachim	oid->type = type;
297285242Sachim	oid->subtype = 0;
298285242Sachim	oid->id = id;
299285242Sachim}
300285242Sachim/* make room in the buffer and move the pointer forward */
301285242Sachimstatic void *
302285242Sachimo_next(struct dn_id **o, int len, int type)
303285242Sachim{
304285242Sachim	struct dn_id *ret = *o;
305285242Sachim	oid_fill(ret, len, type, 0);
306285242Sachim	*o = O_NEXT(*o, len);
307285242Sachim	return ret;
308285242Sachim}
309285242Sachim
310285242Sachim
311285242Sachimstatic size_t pipesize7 = sizeof(struct dn_pipe7);
312285242Sachimstatic size_t pipesize8 = sizeof(struct dn_pipe8);
313285242Sachimstatic size_t pipesizemax8 = sizeof(struct dn_pipe_max8);
314285242Sachim
315285242Sachim/* Indicate 'ipfw' version
316285242Sachim * 1: from FreeBSD 7.2
317285242Sachim * 0: from FreeBSD 8
318285242Sachim * -1: unknow (for now is unused)
319285242Sachim *
320285242Sachim * It is update when a IP_DUMMYNET_DEL or IP_DUMMYNET_CONFIGURE request arrives
321285242Sachim * NOTE: if a IP_DUMMYNET_GET arrives and the 'ipfw' version is unknow,
322285242Sachim *       it is suppose to be the FreeBSD 8 version.
323285242Sachim */
324285242Sachimstatic int is7 = 0;
325285242Sachim
326285242Sachimstatic int
327285242Sachimconvertflags2new(int src)
328285242Sachim{
329285242Sachim	int dst = 0;
330285242Sachim
331285242Sachim	if (src & DNOLD_HAVE_FLOW_MASK)
332285242Sachim		dst |= DN_HAVE_MASK;
333285242Sachim	if (src & DNOLD_QSIZE_IS_BYTES)
334285242Sachim		dst |= DN_QSIZE_BYTES;
335285242Sachim	if (src & DNOLD_NOERROR)
336285242Sachim		dst |= DN_NOERROR;
337285242Sachim	if (src & DNOLD_IS_RED)
338285242Sachim		dst |= DN_IS_RED;
339285242Sachim	if (src & DNOLD_IS_GENTLE_RED)
340285242Sachim		dst |= DN_IS_GENTLE_RED;
341285242Sachim	if (src & DNOLD_HAS_PROFILE)
342285242Sachim		dst |= DN_HAS_PROFILE;
343285242Sachim
344285242Sachim	return dst;
345285242Sachim}
346285242Sachim
347285242Sachimstatic int
348285242Sachimconvertflags2old(int src)
349285242Sachim{
350285242Sachim	int dst = 0;
351285242Sachim
352285242Sachim	if (src & DN_HAVE_MASK)
353285242Sachim		dst |= DNOLD_HAVE_FLOW_MASK;
354285242Sachim	if (src & DN_IS_RED)
355285242Sachim		dst |= DNOLD_IS_RED;
356285242Sachim	if (src & DN_IS_GENTLE_RED)
357285242Sachim		dst |= DNOLD_IS_GENTLE_RED;
358285242Sachim	if (src & DN_NOERROR)
359285242Sachim		dst |= DNOLD_NOERROR;
360285242Sachim	if (src & DN_HAS_PROFILE)
361285242Sachim		dst |= DNOLD_HAS_PROFILE;
362285242Sachim	if (src & DN_QSIZE_BYTES)
363285242Sachim		dst |= DNOLD_QSIZE_IS_BYTES;
364285242Sachim
365285242Sachim	return dst;
366285242Sachim}
367285242Sachim
368285242Sachimstatic int
369285242Sachimdn_compat_del(void *v)
370285242Sachim{
371285242Sachim	struct dn_pipe7 *p = (struct dn_pipe7 *) v;
372285242Sachim	struct dn_pipe8 *p8 = (struct dn_pipe8 *) v;
373285242Sachim	struct {
374285242Sachim		struct dn_id oid;
375285242Sachim		uintptr_t a[1];	/* add more if we want a list */
376285242Sachim	} cmd;
377285242Sachim
378285242Sachim	/* XXX DN_API_VERSION ??? */
379285242Sachim	oid_fill((void *)&cmd, sizeof(cmd), DN_CMD_DELETE, DN_API_VERSION);
380285242Sachim
381285242Sachim	if (is7) {
382285242Sachim		if (p->pipe_nr == 0 && p->fs.fs_nr == 0)
383285242Sachim			return EINVAL;
384285242Sachim		if (p->pipe_nr != 0 && p->fs.fs_nr != 0)
385285242Sachim			return EINVAL;
386285242Sachim	} else {
387285242Sachim		if (p8->pipe_nr == 0 && p8->fs.fs_nr == 0)
388285242Sachim			return EINVAL;
389285242Sachim		if (p8->pipe_nr != 0 && p8->fs.fs_nr != 0)
390285242Sachim			return EINVAL;
391285242Sachim	}
392285242Sachim
393285242Sachim	if (p->pipe_nr != 0) { /* pipe x delete */
394285242Sachim		cmd.a[0] = p->pipe_nr;
395285242Sachim		cmd.oid.subtype = DN_LINK;
396285242Sachim	} else { /* queue x delete */
397285242Sachim		cmd.oid.subtype = DN_FS;
398285242Sachim		cmd.a[0] = (is7) ? p->fs.fs_nr : p8->fs.fs_nr;
399285242Sachim	}
400285242Sachim
401285242Sachim	return do_config(&cmd, cmd.oid.len);
402285242Sachim}
403285242Sachim
404285242Sachimstatic int
405285242Sachimdn_compat_config_queue(struct dn_fs *fs, void* v)
406285242Sachim{
407285242Sachim	struct dn_pipe7 *p7 = (struct dn_pipe7 *)v;
408285242Sachim	struct dn_pipe8 *p8 = (struct dn_pipe8 *)v;
409285242Sachim	struct dn_flow_set *f;
410285242Sachim
411285242Sachim	if (is7)
412285242Sachim		f = &p7->fs;
413285242Sachim	else
414285242Sachim		f = &p8->fs;
415285242Sachim
416285242Sachim	fs->fs_nr = f->fs_nr;
417285242Sachim	fs->sched_nr = f->parent_nr;
418285242Sachim	fs->flow_mask = f->flow_mask;
419285242Sachim	fs->buckets = f->rq_size;
420285242Sachim	fs->qsize = f->qsize;
421285242Sachim	fs->plr = f->plr;
422285242Sachim	fs->par[0] = f->weight;
423285242Sachim	fs->flags = convertflags2new(f->flags_fs);
424285242Sachim	if (fs->flags & DN_IS_GENTLE_RED || fs->flags & DN_IS_RED) {
425285242Sachim		fs->w_q = f->w_q;
426285242Sachim		fs->max_th = f->max_th;
427285242Sachim		fs->min_th = f->min_th;
428285242Sachim		fs->max_p = f->max_p;
429285242Sachim	}
430285242Sachim
431285242Sachim	return 0;
432285242Sachim}
433285242Sachim
434285242Sachimstatic int
435285242Sachimdn_compat_config_pipe(struct dn_sch *sch, struct dn_link *p,
436285242Sachim		      struct dn_fs *fs, void* v)
437285242Sachim{
438285242Sachim	struct dn_pipe7 *p7 = (struct dn_pipe7 *)v;
439285242Sachim	struct dn_pipe8 *p8 = (struct dn_pipe8 *)v;
440285242Sachim	int i = p7->pipe_nr;
441285242Sachim
442285242Sachim	sch->sched_nr = i;
443285242Sachim	sch->oid.subtype = 0;
444285242Sachim	p->link_nr = i;
445285242Sachim	fs->fs_nr = i + 2*DN_MAX_ID;
446285242Sachim	fs->sched_nr = i + DN_MAX_ID;
447285242Sachim
448285242Sachim	/* Common to 7 and 8 */
449285242Sachim	p->bandwidth = p7->bandwidth;
450285242Sachim	p->delay = p7->delay;
451285242Sachim	if (!is7) {
452285242Sachim		/* FreeBSD 8 has burst  */
453285242Sachim		p->burst = p8->burst;
454285242Sachim	}
455285242Sachim
456285242Sachim	/* fill the fifo flowset */
457285242Sachim	dn_compat_config_queue(fs, v);
458285242Sachim	fs->fs_nr = i + 2*DN_MAX_ID;
459285242Sachim	fs->sched_nr = i + DN_MAX_ID;
460285242Sachim
461285242Sachim	/* Move scheduler related parameter from fs to sch */
462285242Sachim	sch->buckets = fs->buckets; /*XXX*/
463285242Sachim	fs->buckets = 0;
464285242Sachim	if (fs->flags & DN_HAVE_MASK) {
465285242Sachim		sch->flags |= DN_HAVE_MASK;
466285242Sachim		fs->flags &= ~DN_HAVE_MASK;
467285242Sachim		sch->sched_mask = fs->flow_mask;
468285242Sachim		bzero(&fs->flow_mask, sizeof(struct ipfw_flow_id));
469285242Sachim	}
470285242Sachim
471285242Sachim	return 0;
472285242Sachim}
473285242Sachim
474285242Sachimstatic int
475285242Sachimdn_compat_config_profile(struct dn_profile *pf, struct dn_link *p,
476285242Sachim			 void *v)
477285242Sachim{
478285242Sachim	struct dn_pipe8 *p8 = (struct dn_pipe8 *)v;
479285242Sachim
480285242Sachim	p8->samples = &(((struct dn_pipe_max8 *)p8)->samples[0]);
481285242Sachim
482285242Sachim	pf->link_nr = p->link_nr;
483285242Sachim	pf->loss_level = p8->loss_level;
484285242Sachim// 	pf->bandwidth = p->bandwidth; //XXX bandwidth redundant?
485285242Sachim	pf->samples_no = p8->samples_no;
486285242Sachim	strncpy(pf->name, p8->name,sizeof(pf->name));
487285242Sachim	bcopy(p8->samples, pf->samples, sizeof(pf->samples));
488285242Sachim
489285242Sachim	return 0;
490285242Sachim}
491285242Sachim
492285242Sachim/*
493285242Sachim * If p->pipe_nr != 0 the command is 'pipe x config', so need to create
494285242Sachim * the three main struct, else only a flowset is created
495285242Sachim */
496285242Sachimstatic int
497285242Sachimdn_compat_configure(void *v)
498285242Sachim{
499285242Sachim	struct dn_id *buf = NULL, *base;
500285242Sachim	struct dn_sch *sch = NULL;
501285242Sachim	struct dn_link *p = NULL;
502285242Sachim	struct dn_fs *fs = NULL;
503285242Sachim	struct dn_profile *pf = NULL;
504285242Sachim	int lmax;
505285242Sachim	int error;
506285242Sachim
507285242Sachim	struct dn_pipe7 *p7 = (struct dn_pipe7 *)v;
508285242Sachim	struct dn_pipe8 *p8 = (struct dn_pipe8 *)v;
509285242Sachim
510285242Sachim	int i; /* number of object to configure */
511285242Sachim
512285242Sachim	lmax = sizeof(struct dn_id);	/* command header */
513285242Sachim	lmax += sizeof(struct dn_sch) + sizeof(struct dn_link) +
514285242Sachim		sizeof(struct dn_fs) + sizeof(struct dn_profile);
515285242Sachim
516285242Sachim	base = buf = malloc(lmax, M_DUMMYNET, M_WAIT|M_ZERO);
517285242Sachim	o_next(&buf, sizeof(struct dn_id), DN_CMD_CONFIG);
518285242Sachim	base->id = DN_API_VERSION;
519285242Sachim
520285242Sachim	/* pipe_nr is the same in p7 and p8 */
521285242Sachim	i = p7->pipe_nr;
522285242Sachim	if (i != 0) { /* pipe config */
523285242Sachim		sch = o_next(&buf, sizeof(*sch), DN_SCH);
524285242Sachim		p = o_next(&buf, sizeof(*p), DN_LINK);
525285242Sachim		fs = o_next(&buf, sizeof(*fs), DN_FS);
526285242Sachim
527285242Sachim		error = dn_compat_config_pipe(sch, p, fs, v);
528285242Sachim		if (error) {
529285242Sachim			free(buf, M_DUMMYNET);
530285242Sachim			return error;
531285242Sachim		}
532285242Sachim		if (!is7 && p8->samples_no > 0) {
533285242Sachim			/* Add profiles*/
534285242Sachim			pf = o_next(&buf, sizeof(*pf), DN_PROFILE);
535285242Sachim			error = dn_compat_config_profile(pf, p, v);
536285242Sachim			if (error) {
537285242Sachim				free(buf, M_DUMMYNET);
538285242Sachim				return error;
539285242Sachim			}
540285242Sachim		}
541285242Sachim	} else { /* queue config */
542285242Sachim		fs = o_next(&buf, sizeof(*fs), DN_FS);
543285242Sachim		error = dn_compat_config_queue(fs, v);
544285242Sachim		if (error) {
545285242Sachim			free(buf, M_DUMMYNET);
546285242Sachim			return error;
547285242Sachim		}
548285242Sachim	}
549285242Sachim	error = do_config(base, (char *)buf - (char *)base);
550285242Sachim
551285242Sachim	if (buf)
552285242Sachim		free(buf, M_DUMMYNET);
553285242Sachim	return error;
554285242Sachim}
555285242Sachim
556285242Sachimint
557285242Sachimdn_compat_calc_size(void)
558285242Sachim{
559285242Sachim	int need = 0;
560285242Sachim	/* XXX use FreeBSD 8 struct size */
561285242Sachim	/* NOTE:
562285242Sachim	 * - half scheduler: 		schk_count/2
563285242Sachim	 * - all flowset:		fsk_count
564285242Sachim	 * - all flowset queues:	queue_count
565285242Sachim	 * - all pipe queue:		si_count
566285242Sachim	 */
567285242Sachim	need += dn_cfg.schk_count * sizeof(struct dn_pipe8) / 2;
568285242Sachim	need += dn_cfg.fsk_count * sizeof(struct dn_flow_set);
569285242Sachim	need += dn_cfg.si_count * sizeof(struct dn_flow_queue8);
570285242Sachim	need += dn_cfg.queue_count * sizeof(struct dn_flow_queue8);
571285242Sachim
572285242Sachim	return need;
573285242Sachim}
574285242Sachim
575285242Sachimint
576285242Sachimdn_c_copy_q (void *_ni, void *arg)
577285242Sachim{
578285242Sachim	struct copy_args *a = arg;
579285242Sachim	struct dn_flow_queue7 *fq7 = (struct dn_flow_queue7 *)*a->start;
580285242Sachim	struct dn_flow_queue8 *fq8 = (struct dn_flow_queue8 *)*a->start;
581285242Sachim	struct dn_flow *ni = (struct dn_flow *)_ni;
582285242Sachim	int size = 0;
583285242Sachim
584285242Sachim	/* XXX hash slot not set */
585285242Sachim	/* No difference between 7.2/8 */
586285242Sachim	fq7->len = ni->length;
587285242Sachim	fq7->len_bytes = ni->len_bytes;
588285242Sachim	fq7->id = ni->fid;
589285242Sachim
590285242Sachim	if (is7) {
591285242Sachim		size = sizeof(struct dn_flow_queue7);
592285242Sachim		fq7->tot_pkts = ni->tot_pkts;
593285242Sachim		fq7->tot_bytes = ni->tot_bytes;
594285242Sachim		fq7->drops = ni->drops;
595285242Sachim	} else {
596285242Sachim		size = sizeof(struct dn_flow_queue8);
597285242Sachim		fq8->tot_pkts = ni->tot_pkts;
598285242Sachim		fq8->tot_bytes = ni->tot_bytes;
599285242Sachim		fq8->drops = ni->drops;
600285242Sachim	}
601285242Sachim
602285242Sachim	*a->start += size;
603285242Sachim	return 0;
604285242Sachim}
605285242Sachim
606285242Sachimint
607285242Sachimdn_c_copy_pipe(struct dn_schk *s, struct copy_args *a, int nq)
608285242Sachim{
609285242Sachim	struct dn_link *l = &s->link;
610285242Sachim	struct dn_fsk *f = s->fs;
611285242Sachim
612285242Sachim	struct dn_pipe7 *pipe7 = (struct dn_pipe7 *)*a->start;
613285242Sachim	struct dn_pipe8 *pipe8 = (struct dn_pipe8 *)*a->start;
614285242Sachim	struct dn_flow_set *fs;
615285242Sachim	int size = 0;
616285242Sachim
617285242Sachim	if (is7) {
618285242Sachim		fs = &pipe7->fs;
619285242Sachim		size = sizeof(struct dn_pipe7);
620285242Sachim	} else {
621285242Sachim		fs = &pipe8->fs;
622285242Sachim		size = sizeof(struct dn_pipe8);
623285242Sachim	}
624285242Sachim
625285242Sachim	/* These 4 field are the same in pipe7 and pipe8 */
626285242Sachim	pipe7->next.sle_next = (struct dn_pipe7 *)DN_IS_PIPE;
627285242Sachim	pipe7->bandwidth = l->bandwidth;
628285242Sachim	pipe7->delay = l->delay * 1000 / hz;
629285242Sachim	pipe7->pipe_nr = l->link_nr - DN_MAX_ID;
630285242Sachim
631285242Sachim	if (!is7) {
632285242Sachim		if (s->profile) {
633285242Sachim			struct dn_profile *pf = s->profile;
634285242Sachim			strncpy(pipe8->name, pf->name, sizeof(pf->name));
635285242Sachim			pipe8->loss_level = pf->loss_level;
636285242Sachim			pipe8->samples_no = pf->samples_no;
637285242Sachim		}
638285242Sachim		pipe8->burst = div64(l->burst , 8 * hz);
639285242Sachim	}
640285242Sachim
641285242Sachim	fs->flow_mask = s->sch.sched_mask;
642285242Sachim	fs->rq_size = s->sch.buckets ? s->sch.buckets : 1;
643285242Sachim
644285242Sachim	fs->parent_nr = l->link_nr - DN_MAX_ID;
645285242Sachim	fs->qsize = f->fs.qsize;
646285242Sachim	fs->plr = f->fs.plr;
647285242Sachim	fs->w_q = f->fs.w_q;
648285242Sachim	fs->max_th = f->max_th;
649285242Sachim	fs->min_th = f->min_th;
650285242Sachim	fs->max_p = f->fs.max_p;
651285242Sachim	fs->rq_elements = nq;
652285242Sachim
653285242Sachim	fs->flags_fs = convertflags2old(f->fs.flags);
654285242Sachim
655285242Sachim	*a->start += size;
656285242Sachim	return 0;
657285242Sachim}
658285242Sachim
659285242Sachim
660285242Sachimint
661285242Sachimdn_compat_copy_pipe(struct copy_args *a, void *_o)
662285242Sachim{
663285242Sachim	int have = a->end - *a->start;
664285242Sachim	int need = 0;
665285242Sachim	int pipe_size = sizeof(struct dn_pipe8);
666285242Sachim	int queue_size = sizeof(struct dn_flow_queue8);
667285242Sachim	int n_queue = 0; /* number of queues */
668285242Sachim
669285242Sachim	struct dn_schk *s = (struct dn_schk *)_o;
670285242Sachim	/* calculate needed space:
671285242Sachim	 * - struct dn_pipe
672285242Sachim	 * - if there are instances, dn_queue * n_instances
673285242Sachim	 */
674285242Sachim	n_queue = (s->sch.flags & DN_HAVE_MASK ? dn_ht_entries(s->siht) :
675285242Sachim						(s->siht ? 1 : 0));
676285242Sachim	need = pipe_size + queue_size * n_queue;
677285242Sachim	if (have < need) {
678285242Sachim		D("have %d < need %d", have, need);
679285242Sachim		return 1;
680285242Sachim	}
681285242Sachim	/* copy pipe */
682285242Sachim	dn_c_copy_pipe(s, a, n_queue);
683285242Sachim
684285242Sachim	/* copy queues */
685285242Sachim	if (s->sch.flags & DN_HAVE_MASK)
686285242Sachim		dn_ht_scan(s->siht, dn_c_copy_q, a);
687285242Sachim	else if (s->siht)
688285242Sachim		dn_c_copy_q(s->siht, a);
689285242Sachim	return 0;
690285242Sachim}
691285242Sachim
692285242Sachimint
693285242Sachimdn_c_copy_fs(struct dn_fsk *f, struct copy_args *a, int nq)
694285242Sachim{
695285242Sachim	struct dn_flow_set *fs = (struct dn_flow_set *)*a->start;
696285242Sachim
697285242Sachim	fs->next.sle_next = (struct dn_flow_set *)DN_IS_QUEUE;
698285242Sachim	fs->fs_nr = f->fs.fs_nr;
699285242Sachim	fs->qsize = f->fs.qsize;
700285242Sachim	fs->plr = f->fs.plr;
701285242Sachim	fs->w_q = f->fs.w_q;
702285242Sachim	fs->max_th = f->max_th;
703285242Sachim	fs->min_th = f->min_th;
704285242Sachim	fs->max_p = f->fs.max_p;
705285242Sachim	fs->flow_mask = f->fs.flow_mask;
706285242Sachim	fs->rq_elements = nq;
707285242Sachim	fs->rq_size = (f->fs.buckets ? f->fs.buckets : 1);
708285242Sachim	fs->parent_nr = f->fs.sched_nr;
709285242Sachim	fs->weight = f->fs.par[0];
710
711	fs->flags_fs = convertflags2old(f->fs.flags);
712	*a->start += sizeof(struct dn_flow_set);
713	return 0;
714}
715
716int
717dn_compat_copy_queue(struct copy_args *a, void *_o)
718{
719	int have = a->end - *a->start;
720	int need = 0;
721	int fs_size = sizeof(struct dn_flow_set);
722	int queue_size = sizeof(struct dn_flow_queue8);
723
724	struct dn_fsk *fs = (struct dn_fsk *)_o;
725	int n_queue = 0; /* number of queues */
726
727	n_queue = (fs->fs.flags & DN_HAVE_MASK ? dn_ht_entries(fs->qht) :
728						(fs->qht ? 1 : 0));
729
730	need = fs_size + queue_size * n_queue;
731	if (have < need) {
732		D("have < need");
733		return 1;
734	}
735
736	/* copy flowset */
737	dn_c_copy_fs(fs, a, n_queue);
738
739	/* copy queues */
740	if (fs->fs.flags & DN_HAVE_MASK)
741		dn_ht_scan(fs->qht, dn_c_copy_q, a);
742	else if (fs->qht)
743		dn_c_copy_q(fs->qht, a);
744
745	return 0;
746}
747
748int
749copy_data_helper_compat(void *_o, void *_arg)
750{
751	struct copy_args *a = _arg;
752
753	if (a->type == DN_COMPAT_PIPE) {
754		struct dn_schk *s = _o;
755		if (s->sch.oid.subtype != 1 || s->sch.sched_nr <= DN_MAX_ID) {
756			return 0;	/* not old type */
757		}
758		/* copy pipe parameters, and if instance exists, copy
759		 * other parameters and eventually queues.
760		 */
761		if(dn_compat_copy_pipe(a, _o))
762			return DNHT_SCAN_END;
763	} else if (a->type == DN_COMPAT_QUEUE) {
764		struct dn_fsk *fs = _o;
765		if (fs->fs.fs_nr >= DN_MAX_ID)
766			return 0;
767		if (dn_compat_copy_queue(a, _o))
768			return DNHT_SCAN_END;
769	}
770	return 0;
771}
772
773/* Main function to manage old requests */
774int
775ip_dummynet_compat(struct sockopt *sopt)
776{
777	int error=0;
778	void *v = NULL;
779	struct dn_id oid;
780
781	/* Lenght of data, used to found ipfw version... */
782	int len = sopt->sopt_valsize;
783
784	/* len can be 0 if command was dummynet_flush */
785	if (len == pipesize7) {
786		D("setting compatibility with FreeBSD 7.2");
787		is7 = 1;
788	}
789	else if (len == pipesize8 || len == pipesizemax8) {
790		D("setting compatibility with FreeBSD 8");
791		is7 = 0;
792	}
793
794	switch (sopt->sopt_name) {
795	default:
796		printf("dummynet: -- unknown option %d", sopt->sopt_name);
797		error = EINVAL;
798		break;
799
800	case IP_DUMMYNET_FLUSH:
801		oid_fill(&oid, sizeof(oid), DN_CMD_FLUSH, DN_API_VERSION);
802		do_config(&oid, oid.len);
803		break;
804
805	case IP_DUMMYNET_DEL:
806		v = malloc(len, M_TEMP, M_WAITOK);
807		error = sooptcopyin(sopt, v, len, len);
808		if (error)
809			break;
810		error = dn_compat_del(v);
811		free(v, M_TEMP);
812		break;
813
814	case IP_DUMMYNET_CONFIGURE:
815		v = malloc(len, M_TEMP, M_WAITOK);
816		error = sooptcopyin(sopt, v, len, len);
817		if (error)
818			break;
819		error = dn_compat_configure(v);
820		free(v, M_TEMP);
821		break;
822
823	case IP_DUMMYNET_GET: {
824		void *buf;
825		int ret;
826		int original_size = sopt->sopt_valsize;
827		int size;
828
829		ret = dummynet_get(sopt, &buf);
830		if (ret)
831			return 0;//XXX ?
832		size = sopt->sopt_valsize;
833		sopt->sopt_valsize = original_size;
834		D("size=%d, buf=%p", size, buf);
835		ret = sooptcopyout(sopt, buf, size);
836		if (ret)
837			printf("  %s ERROR sooptcopyout\n", __FUNCTION__);
838		if (buf)
839			free(buf, M_DUMMYNET);
840	    }
841	}
842
843	return error;
844}
845
846
847