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