xref: /freebsd/sys/netpfil/ipfw/ip_dn_glue.c (revision f4b37ed0f8b307b1f3f0f630ca725d68f1dff30d)
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$
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/ip_dummynet.h>
54 
55 #include <netpfil/ipfw/ip_fw_private.h>
56 #include <netpfil/ipfw/dn_heap.h>
57 #include <netpfil/ipfw/ip_dn_private.h>
58 #include <netpfil/ipfw/dn_sched.h>
59 
60 /* FREEBSD7.2 ip_dummynet.h r191715*/
61 
62 struct dn_heap_entry7 {
63 	int64_t key;        /* sorting key. Topmost element is smallest one */
64 	void *object;      /* object pointer */
65 };
66 
67 struct dn_heap7 {
68 	int size;
69 	int elements;
70 	int offset; /* XXX if > 0 this is the offset of direct ptr to obj */
71 	struct dn_heap_entry7 *p;   /* really an array of "size" entries */
72 };
73 
74 /* Common to 7.2 and 8 */
75 struct dn_flow_set {
76 	SLIST_ENTRY(dn_flow_set)    next;   /* linked list in a hash slot */
77 
78 	u_short fs_nr ;             /* flow_set number       */
79 	u_short flags_fs;
80 #define DNOLD_HAVE_FLOW_MASK   0x0001
81 #define DNOLD_IS_RED       0x0002
82 #define DNOLD_IS_GENTLE_RED    0x0004
83 #define DNOLD_QSIZE_IS_BYTES   0x0008  /* queue size is measured in bytes */
84 #define DNOLD_NOERROR      0x0010  /* do not report ENOBUFS on drops  */
85 #define DNOLD_HAS_PROFILE      0x0020  /* the pipe has a delay profile. */
86 #define DNOLD_IS_PIPE      0x4000
87 #define DNOLD_IS_QUEUE     0x8000
88 
89 	struct dn_pipe7 *pipe ;  /* pointer to parent pipe */
90 	u_short parent_nr ;     /* parent pipe#, 0 if local to a pipe */
91 
92 	int weight ;        /* WFQ queue weight */
93 	int qsize ;         /* queue size in slots or bytes */
94 	int plr ;           /* pkt loss rate (2^31-1 means 100%) */
95 
96 	struct ipfw_flow_id flow_mask ;
97 
98 	/* hash table of queues onto this flow_set */
99 	int rq_size ;       /* number of slots */
100 	int rq_elements ;       /* active elements */
101 	struct dn_flow_queue7 **rq;  /* array of rq_size entries */
102 
103 	u_int32_t last_expired ;    /* do not expire too frequently */
104 	int backlogged ;        /* #active queues for this flowset */
105 
106         /* RED parameters */
107 #define SCALE_RED               16
108 #define SCALE(x)                ( (x) << SCALE_RED )
109 #define SCALE_VAL(x)            ( (x) >> SCALE_RED )
110 #define SCALE_MUL(x,y)          ( ( (x) * (y) ) >> SCALE_RED )
111 	int w_q ;           /* queue weight (scaled) */
112 	int max_th ;        /* maximum threshold for queue (scaled) */
113 	int min_th ;        /* minimum threshold for queue (scaled) */
114 	int max_p ;         /* maximum value for p_b (scaled) */
115 	u_int c_1 ;         /* max_p/(max_th-min_th) (scaled) */
116 	u_int c_2 ;         /* max_p*min_th/(max_th-min_th) (scaled) */
117 	u_int c_3 ;         /* for GRED, (1-max_p)/max_th (scaled) */
118 	u_int c_4 ;         /* for GRED, 1 - 2*max_p (scaled) */
119 	u_int * w_q_lookup ;    /* lookup table for computing (1-w_q)^t */
120 	u_int lookup_depth ;    /* depth of lookup table */
121 	int lookup_step ;       /* granularity inside the lookup table */
122 	int lookup_weight ;     /* equal to (1-w_q)^t / (1-w_q)^(t+1) */
123 	int avg_pkt_size ;      /* medium packet size */
124 	int max_pkt_size ;      /* max packet size */
125 };
126 SLIST_HEAD(dn_flow_set_head, dn_flow_set);
127 
128 #define DN_IS_PIPE		0x4000
129 #define DN_IS_QUEUE		0x8000
130 struct dn_flow_queue7 {
131 	struct dn_flow_queue7 *next ;
132 	struct ipfw_flow_id id ;
133 
134 	struct mbuf *head, *tail ;  /* queue of packets */
135 	u_int len ;
136 	u_int len_bytes ;
137 
138 	u_long numbytes;
139 
140 	u_int64_t tot_pkts ;    /* statistics counters  */
141 	u_int64_t tot_bytes ;
142 	u_int32_t drops ;
143 
144 	int hash_slot ;     /* debugging/diagnostic */
145 
146 	/* RED parameters */
147 	int avg ;                   /* average queue length est. (scaled) */
148 	int count ;                 /* arrivals since last RED drop */
149 	int random ;                /* random value (scaled) */
150 	u_int32_t q_time;      /* start of queue idle time */
151 
152 	/* WF2Q+ support */
153 	struct dn_flow_set *fs ;    /* parent flow set */
154 	int heap_pos ;      /* position (index) of struct in heap */
155 	int64_t sched_time ;     /* current time when queue enters ready_heap */
156 
157 	int64_t S,F ;        /* start time, finish time */
158 };
159 
160 struct dn_pipe7 {        /* a pipe */
161 	SLIST_ENTRY(dn_pipe7)    next;   /* linked list in a hash slot */
162 
163 	int pipe_nr ;       /* number   */
164 	int bandwidth;      /* really, bytes/tick.  */
165 	int delay ;         /* really, ticks    */
166 
167 	struct  mbuf *head, *tail ; /* packets in delay line */
168 
169 	/* WF2Q+ */
170 	struct dn_heap7 scheduler_heap ; /* top extract - key Finish time*/
171 	struct dn_heap7 not_eligible_heap; /* top extract- key Start time */
172 	struct dn_heap7 idle_heap ; /* random extract - key Start=Finish time */
173 
174 	int64_t V ;          /* virtual time */
175 	int sum;            /* sum of weights of all active sessions */
176 
177 	int numbytes;
178 
179 	int64_t sched_time ;     /* time pipe was scheduled in ready_heap */
180 
181 	/*
182 	* When the tx clock come from an interface (if_name[0] != '\0'), its name
183 	* is stored below, whereas the ifp is filled when the rule is configured.
184 	*/
185 	char if_name[IFNAMSIZ];
186 	struct ifnet *ifp ;
187 	int ready ; /* set if ifp != NULL and we got a signal from it */
188 
189 	struct dn_flow_set fs ; /* used with fixed-rate flows */
190 };
191 SLIST_HEAD(dn_pipe_head7, dn_pipe7);
192 
193 
194 /* FREEBSD8 ip_dummynet.h r196045 */
195 struct dn_flow_queue8 {
196 	struct dn_flow_queue8 *next ;
197 	struct ipfw_flow_id id ;
198 
199 	struct mbuf *head, *tail ;  /* queue of packets */
200 	u_int len ;
201 	u_int len_bytes ;
202 
203 	uint64_t numbytes ;     /* credit for transmission (dynamic queues) */
204 	int64_t extra_bits;     /* extra bits simulating unavailable channel */
205 
206 	u_int64_t tot_pkts ;    /* statistics counters  */
207 	u_int64_t tot_bytes ;
208 	u_int32_t drops ;
209 
210 	int hash_slot ;     /* debugging/diagnostic */
211 
212 	/* RED parameters */
213 	int avg ;                   /* average queue length est. (scaled) */
214 	int count ;                 /* arrivals since last RED drop */
215 	int random ;                /* random value (scaled) */
216 	int64_t idle_time;       /* start of queue idle time */
217 
218 	/* WF2Q+ support */
219 	struct dn_flow_set *fs ;    /* parent flow set */
220 	int heap_pos ;      /* position (index) of struct in heap */
221 	int64_t sched_time ;     /* current time when queue enters ready_heap */
222 
223 	int64_t S,F ;        /* start time, finish time */
224 };
225 
226 struct dn_pipe8 {        /* a pipe */
227 	SLIST_ENTRY(dn_pipe8)    next;   /* linked list in a hash slot */
228 
229 	int pipe_nr ;       /* number   */
230 	int bandwidth;      /* really, bytes/tick.  */
231 	int delay ;         /* really, ticks    */
232 
233 	struct  mbuf *head, *tail ; /* packets in delay line */
234 
235 	/* WF2Q+ */
236 	struct dn_heap7 scheduler_heap ; /* top extract - key Finish time*/
237 	struct dn_heap7 not_eligible_heap; /* top extract- key Start time */
238 	struct dn_heap7 idle_heap ; /* random extract - key Start=Finish time */
239 
240 	int64_t V ;          /* virtual time */
241 	int sum;            /* sum of weights of all active sessions */
242 
243 	/* Same as in dn_flow_queue, numbytes can become large */
244 	int64_t numbytes;       /* bits I can transmit (more or less). */
245 	uint64_t burst;     /* burst size, scaled: bits * hz */
246 
247 	int64_t sched_time ;     /* time pipe was scheduled in ready_heap */
248 	int64_t idle_time;       /* start of pipe idle time */
249 
250 	char if_name[IFNAMSIZ];
251 	struct ifnet *ifp ;
252 	int ready ; /* set if ifp != NULL and we got a signal from it */
253 
254 	struct dn_flow_set fs ; /* used with fixed-rate flows */
255 
256     /* fields to simulate a delay profile */
257 #define ED_MAX_NAME_LEN     32
258 	char name[ED_MAX_NAME_LEN];
259 	int loss_level;
260 	int samples_no;
261 	int *samples;
262 };
263 
264 #define ED_MAX_SAMPLES_NO   1024
265 struct dn_pipe_max8 {
266 	struct dn_pipe8 pipe;
267 	int samples[ED_MAX_SAMPLES_NO];
268 };
269 SLIST_HEAD(dn_pipe_head8, dn_pipe8);
270 
271 /*
272  * Changes from 7.2 to 8:
273  * dn_pipe:
274  *      numbytes from int to int64_t
275  *      add burst (int64_t)
276  *      add idle_time (int64_t)
277  *      add profile
278  *      add struct dn_pipe_max
279  *      add flag DN_HAS_PROFILE
280  *
281  * dn_flow_queue
282  *      numbytes from u_long to int64_t
283  *      add extra_bits (int64_t)
284  *      q_time from u_int32_t to int64_t and name idle_time
285  *
286  * dn_flow_set unchanged
287  *
288  */
289 
290 /* NOTE:XXX copied from dummynet.c */
291 #define O_NEXT(p, len) ((void *)((char *)p + len))
292 static void
293 oid_fill(struct dn_id *oid, int len, int type, uintptr_t id)
294 {
295 	oid->len = len;
296 	oid->type = type;
297 	oid->subtype = 0;
298 	oid->id = id;
299 }
300 /* make room in the buffer and move the pointer forward */
301 static void *
302 o_next(struct dn_id **o, int len, int type)
303 {
304 	struct dn_id *ret = *o;
305 	oid_fill(ret, len, type, 0);
306 	*o = O_NEXT(*o, len);
307 	return ret;
308 }
309 
310 
311 static size_t pipesize7 = sizeof(struct dn_pipe7);
312 static size_t pipesize8 = sizeof(struct dn_pipe8);
313 static size_t pipesizemax8 = sizeof(struct dn_pipe_max8);
314 
315 /* Indicate 'ipfw' version
316  * 1: from FreeBSD 7.2
317  * 0: from FreeBSD 8
318  * -1: unknown (for now is unused)
319  *
320  * It is update when a IP_DUMMYNET_DEL or IP_DUMMYNET_CONFIGURE request arrives
321  * NOTE: if a IP_DUMMYNET_GET arrives and the 'ipfw' version is unknown,
322  *       it is suppose to be the FreeBSD 8 version.
323  */
324 static int is7 = 0;
325 
326 static int
327 convertflags2new(int src)
328 {
329 	int dst = 0;
330 
331 	if (src & DNOLD_HAVE_FLOW_MASK)
332 		dst |= DN_HAVE_MASK;
333 	if (src & DNOLD_QSIZE_IS_BYTES)
334 		dst |= DN_QSIZE_BYTES;
335 	if (src & DNOLD_NOERROR)
336 		dst |= DN_NOERROR;
337 	if (src & DNOLD_IS_RED)
338 		dst |= DN_IS_RED;
339 	if (src & DNOLD_IS_GENTLE_RED)
340 		dst |= DN_IS_GENTLE_RED;
341 	if (src & DNOLD_HAS_PROFILE)
342 		dst |= DN_HAS_PROFILE;
343 
344 	return dst;
345 }
346 
347 static int
348 convertflags2old(int src)
349 {
350 	int dst = 0;
351 
352 	if (src & DN_HAVE_MASK)
353 		dst |= DNOLD_HAVE_FLOW_MASK;
354 	if (src & DN_IS_RED)
355 		dst |= DNOLD_IS_RED;
356 	if (src & DN_IS_GENTLE_RED)
357 		dst |= DNOLD_IS_GENTLE_RED;
358 	if (src & DN_NOERROR)
359 		dst |= DNOLD_NOERROR;
360 	if (src & DN_HAS_PROFILE)
361 		dst |= DNOLD_HAS_PROFILE;
362 	if (src & DN_QSIZE_BYTES)
363 		dst |= DNOLD_QSIZE_IS_BYTES;
364 
365 	return dst;
366 }
367 
368 static int
369 dn_compat_del(void *v)
370 {
371 	struct dn_pipe7 *p = (struct dn_pipe7 *) v;
372 	struct dn_pipe8 *p8 = (struct dn_pipe8 *) v;
373 	struct {
374 		struct dn_id oid;
375 		uintptr_t a[1];	/* add more if we want a list */
376 	} cmd;
377 
378 	/* XXX DN_API_VERSION ??? */
379 	oid_fill((void *)&cmd, sizeof(cmd), DN_CMD_DELETE, DN_API_VERSION);
380 
381 	if (is7) {
382 		if (p->pipe_nr == 0 && p->fs.fs_nr == 0)
383 			return EINVAL;
384 		if (p->pipe_nr != 0 && p->fs.fs_nr != 0)
385 			return EINVAL;
386 	} else {
387 		if (p8->pipe_nr == 0 && p8->fs.fs_nr == 0)
388 			return EINVAL;
389 		if (p8->pipe_nr != 0 && p8->fs.fs_nr != 0)
390 			return EINVAL;
391 	}
392 
393 	if (p->pipe_nr != 0) { /* pipe x delete */
394 		cmd.a[0] = p->pipe_nr;
395 		cmd.oid.subtype = DN_LINK;
396 	} else { /* queue x delete */
397 		cmd.oid.subtype = DN_FS;
398 		cmd.a[0] = (is7) ? p->fs.fs_nr : p8->fs.fs_nr;
399 	}
400 
401 	return do_config(&cmd, cmd.oid.len);
402 }
403 
404 static int
405 dn_compat_config_queue(struct dn_fs *fs, void* v)
406 {
407 	struct dn_pipe7 *p7 = (struct dn_pipe7 *)v;
408 	struct dn_pipe8 *p8 = (struct dn_pipe8 *)v;
409 	struct dn_flow_set *f;
410 
411 	if (is7)
412 		f = &p7->fs;
413 	else
414 		f = &p8->fs;
415 
416 	fs->fs_nr = f->fs_nr;
417 	fs->sched_nr = f->parent_nr;
418 	fs->flow_mask = f->flow_mask;
419 	fs->buckets = f->rq_size;
420 	fs->qsize = f->qsize;
421 	fs->plr = f->plr;
422 	fs->par[0] = f->weight;
423 	fs->flags = convertflags2new(f->flags_fs);
424 	if (fs->flags & DN_IS_GENTLE_RED || fs->flags & DN_IS_RED) {
425 		fs->w_q = f->w_q;
426 		fs->max_th = f->max_th;
427 		fs->min_th = f->min_th;
428 		fs->max_p = f->max_p;
429 	}
430 
431 	return 0;
432 }
433 
434 static int
435 dn_compat_config_pipe(struct dn_sch *sch, struct dn_link *p,
436 		      struct dn_fs *fs, void* v)
437 {
438 	struct dn_pipe7 *p7 = (struct dn_pipe7 *)v;
439 	struct dn_pipe8 *p8 = (struct dn_pipe8 *)v;
440 	int i = p7->pipe_nr;
441 
442 	sch->sched_nr = i;
443 	sch->oid.subtype = 0;
444 	p->link_nr = i;
445 	fs->fs_nr = i + 2*DN_MAX_ID;
446 	fs->sched_nr = i + DN_MAX_ID;
447 
448 	/* Common to 7 and 8 */
449 	p->bandwidth = p7->bandwidth;
450 	p->delay = p7->delay;
451 	if (!is7) {
452 		/* FreeBSD 8 has burst  */
453 		p->burst = p8->burst;
454 	}
455 
456 	/* fill the fifo flowset */
457 	dn_compat_config_queue(fs, v);
458 	fs->fs_nr = i + 2*DN_MAX_ID;
459 	fs->sched_nr = i + DN_MAX_ID;
460 
461 	/* Move scheduler related parameter from fs to sch */
462 	sch->buckets = fs->buckets; /*XXX*/
463 	fs->buckets = 0;
464 	if (fs->flags & DN_HAVE_MASK) {
465 		sch->flags |= DN_HAVE_MASK;
466 		fs->flags &= ~DN_HAVE_MASK;
467 		sch->sched_mask = fs->flow_mask;
468 		bzero(&fs->flow_mask, sizeof(struct ipfw_flow_id));
469 	}
470 
471 	return 0;
472 }
473 
474 static int
475 dn_compat_config_profile(struct dn_profile *pf, struct dn_link *p,
476 			 void *v)
477 {
478 	struct dn_pipe8 *p8 = (struct dn_pipe8 *)v;
479 
480 	p8->samples = &(((struct dn_pipe_max8 *)p8)->samples[0]);
481 
482 	pf->link_nr = p->link_nr;
483 	pf->loss_level = p8->loss_level;
484 // 	pf->bandwidth = p->bandwidth; //XXX bandwidth redundant?
485 	pf->samples_no = p8->samples_no;
486 	strncpy(pf->name, p8->name,sizeof(pf->name));
487 	bcopy(p8->samples, pf->samples, sizeof(pf->samples));
488 
489 	return 0;
490 }
491 
492 /*
493  * If p->pipe_nr != 0 the command is 'pipe x config', so need to create
494  * the three main struct, else only a flowset is created
495  */
496 static int
497 dn_compat_configure(void *v)
498 {
499 	struct dn_id *buf = NULL, *base;
500 	struct dn_sch *sch = NULL;
501 	struct dn_link *p = NULL;
502 	struct dn_fs *fs = NULL;
503 	struct dn_profile *pf = NULL;
504 	int lmax;
505 	int error;
506 
507 	struct dn_pipe7 *p7 = (struct dn_pipe7 *)v;
508 	struct dn_pipe8 *p8 = (struct dn_pipe8 *)v;
509 
510 	int i; /* number of object to configure */
511 
512 	lmax = sizeof(struct dn_id);	/* command header */
513 	lmax += sizeof(struct dn_sch) + sizeof(struct dn_link) +
514 		sizeof(struct dn_fs) + sizeof(struct dn_profile);
515 
516 	base = buf = malloc(lmax, M_DUMMYNET, M_WAITOK|M_ZERO);
517 	o_next(&buf, sizeof(struct dn_id), DN_CMD_CONFIG);
518 	base->id = DN_API_VERSION;
519 
520 	/* pipe_nr is the same in p7 and p8 */
521 	i = p7->pipe_nr;
522 	if (i != 0) { /* pipe config */
523 		sch = o_next(&buf, sizeof(*sch), DN_SCH);
524 		p = o_next(&buf, sizeof(*p), DN_LINK);
525 		fs = o_next(&buf, sizeof(*fs), DN_FS);
526 
527 		error = dn_compat_config_pipe(sch, p, fs, v);
528 		if (error) {
529 			free(buf, M_DUMMYNET);
530 			return error;
531 		}
532 		if (!is7 && p8->samples_no > 0) {
533 			/* Add profiles*/
534 			pf = o_next(&buf, sizeof(*pf), DN_PROFILE);
535 			error = dn_compat_config_profile(pf, p, v);
536 			if (error) {
537 				free(buf, M_DUMMYNET);
538 				return error;
539 			}
540 		}
541 	} else { /* queue config */
542 		fs = o_next(&buf, sizeof(*fs), DN_FS);
543 		error = dn_compat_config_queue(fs, v);
544 		if (error) {
545 			free(buf, M_DUMMYNET);
546 			return error;
547 		}
548 	}
549 	error = do_config(base, (char *)buf - (char *)base);
550 
551 	if (buf)
552 		free(buf, M_DUMMYNET);
553 	return error;
554 }
555 
556 int
557 dn_compat_calc_size(void)
558 {
559 	int need = 0;
560 	/* XXX use FreeBSD 8 struct size */
561 	/* NOTE:
562 	 * - half scheduler: 		schk_count/2
563 	 * - all flowset:		fsk_count
564 	 * - all flowset queues:	queue_count
565 	 * - all pipe queue:		si_count
566 	 */
567 	need += dn_cfg.schk_count * sizeof(struct dn_pipe8) / 2;
568 	need += dn_cfg.fsk_count * sizeof(struct dn_flow_set);
569 	need += dn_cfg.si_count * sizeof(struct dn_flow_queue8);
570 	need += dn_cfg.queue_count * sizeof(struct dn_flow_queue8);
571 
572 	return need;
573 }
574 
575 int
576 dn_c_copy_q (void *_ni, void *arg)
577 {
578 	struct copy_args *a = arg;
579 	struct dn_flow_queue7 *fq7 = (struct dn_flow_queue7 *)*a->start;
580 	struct dn_flow_queue8 *fq8 = (struct dn_flow_queue8 *)*a->start;
581 	struct dn_flow *ni = (struct dn_flow *)_ni;
582 	int size = 0;
583 
584 	/* XXX hash slot not set */
585 	/* No difference between 7.2/8 */
586 	fq7->len = ni->length;
587 	fq7->len_bytes = ni->len_bytes;
588 	fq7->id = ni->fid;
589 
590 	if (is7) {
591 		size = sizeof(struct dn_flow_queue7);
592 		fq7->tot_pkts = ni->tot_pkts;
593 		fq7->tot_bytes = ni->tot_bytes;
594 		fq7->drops = ni->drops;
595 	} else {
596 		size = sizeof(struct dn_flow_queue8);
597 		fq8->tot_pkts = ni->tot_pkts;
598 		fq8->tot_bytes = ni->tot_bytes;
599 		fq8->drops = ni->drops;
600 	}
601 
602 	*a->start += size;
603 	return 0;
604 }
605 
606 int
607 dn_c_copy_pipe(struct dn_schk *s, struct copy_args *a, int nq)
608 {
609 	struct dn_link *l = &s->link;
610 	struct dn_fsk *f = s->fs;
611 
612 	struct dn_pipe7 *pipe7 = (struct dn_pipe7 *)*a->start;
613 	struct dn_pipe8 *pipe8 = (struct dn_pipe8 *)*a->start;
614 	struct dn_flow_set *fs;
615 	int size = 0;
616 
617 	if (is7) {
618 		fs = &pipe7->fs;
619 		size = sizeof(struct dn_pipe7);
620 	} else {
621 		fs = &pipe8->fs;
622 		size = sizeof(struct dn_pipe8);
623 	}
624 
625 	/* These 4 field are the same in pipe7 and pipe8 */
626 	pipe7->next.sle_next = (struct dn_pipe7 *)DN_IS_PIPE;
627 	pipe7->bandwidth = l->bandwidth;
628 	pipe7->delay = l->delay * 1000 / hz;
629 	pipe7->pipe_nr = l->link_nr - DN_MAX_ID;
630 
631 	if (!is7) {
632 		if (s->profile) {
633 			struct dn_profile *pf = s->profile;
634 			strncpy(pipe8->name, pf->name, sizeof(pf->name));
635 			pipe8->loss_level = pf->loss_level;
636 			pipe8->samples_no = pf->samples_no;
637 		}
638 		pipe8->burst = div64(l->burst , 8 * hz);
639 	}
640 
641 	fs->flow_mask = s->sch.sched_mask;
642 	fs->rq_size = s->sch.buckets ? s->sch.buckets : 1;
643 
644 	fs->parent_nr = l->link_nr - DN_MAX_ID;
645 	fs->qsize = f->fs.qsize;
646 	fs->plr = f->fs.plr;
647 	fs->w_q = f->fs.w_q;
648 	fs->max_th = f->max_th;
649 	fs->min_th = f->min_th;
650 	fs->max_p = f->fs.max_p;
651 	fs->rq_elements = nq;
652 
653 	fs->flags_fs = convertflags2old(f->fs.flags);
654 
655 	*a->start += size;
656 	return 0;
657 }
658 
659 
660 int
661 dn_compat_copy_pipe(struct copy_args *a, void *_o)
662 {
663 	int have = a->end - *a->start;
664 	int need = 0;
665 	int pipe_size = sizeof(struct dn_pipe8);
666 	int queue_size = sizeof(struct dn_flow_queue8);
667 	int n_queue = 0; /* number of queues */
668 
669 	struct dn_schk *s = (struct dn_schk *)_o;
670 	/* calculate needed space:
671 	 * - struct dn_pipe
672 	 * - if there are instances, dn_queue * n_instances
673 	 */
674 	n_queue = (s->sch.flags & DN_HAVE_MASK ? dn_ht_entries(s->siht) :
675 						(s->siht ? 1 : 0));
676 	need = pipe_size + queue_size * n_queue;
677 	if (have < need) {
678 		D("have %d < need %d", have, need);
679 		return 1;
680 	}
681 	/* copy pipe */
682 	dn_c_copy_pipe(s, a, n_queue);
683 
684 	/* copy queues */
685 	if (s->sch.flags & DN_HAVE_MASK)
686 		dn_ht_scan(s->siht, dn_c_copy_q, a);
687 	else if (s->siht)
688 		dn_c_copy_q(s->siht, a);
689 	return 0;
690 }
691 
692 int
693 dn_c_copy_fs(struct dn_fsk *f, struct copy_args *a, int nq)
694 {
695 	struct dn_flow_set *fs = (struct dn_flow_set *)*a->start;
696 
697 	fs->next.sle_next = (struct dn_flow_set *)DN_IS_QUEUE;
698 	fs->fs_nr = f->fs.fs_nr;
699 	fs->qsize = f->fs.qsize;
700 	fs->plr = f->fs.plr;
701 	fs->w_q = f->fs.w_q;
702 	fs->max_th = f->max_th;
703 	fs->min_th = f->min_th;
704 	fs->max_p = f->fs.max_p;
705 	fs->flow_mask = f->fs.flow_mask;
706 	fs->rq_elements = nq;
707 	fs->rq_size = (f->fs.buckets ? f->fs.buckets : 1);
708 	fs->parent_nr = f->fs.sched_nr;
709 	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 
716 int
717 dn_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 
748 int
749 copy_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 */
774 int
775 ip_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