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