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