]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netpfil/ipfw/ip_dn_glue.c
ipfw: use unsigned int for dummynet bandwidth
[FreeBSD/FreeBSD.git] / sys / netpfil / ipfw / ip_dn_glue.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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
199 /* FREEBSD8 ip_dummynet.h r196045 */
200 struct dn_flow_queue8 {
201         struct dn_flow_queue8 *next ;
202         struct ipfw_flow_id id ;
203
204         struct mbuf *head, *tail ;  /* queue of packets */
205         u_int len ;
206         u_int len_bytes ;
207
208         uint64_t numbytes ;     /* credit for transmission (dynamic queues) */
209         int64_t extra_bits;     /* extra bits simulating unavailable channel */
210
211         u_int64_t tot_pkts ;    /* statistics counters  */
212         u_int64_t tot_bytes ;
213         u_int32_t drops ;
214
215         int hash_slot ;     /* debugging/diagnostic */
216
217         /* RED parameters */
218         int avg ;                   /* average queue length est. (scaled) */
219         int count ;                 /* arrivals since last RED drop */
220         int random ;                /* random value (scaled) */
221         int64_t idle_time;       /* start of queue idle time */
222
223         /* WF2Q+ support */
224         struct dn_flow_set *fs ;    /* parent flow set */
225         int heap_pos ;      /* position (index) of struct in heap */
226         int64_t sched_time ;     /* current time when queue enters ready_heap */
227
228         int64_t S,F ;        /* start time, finish time */
229 };
230
231 struct dn_pipe8 {        /* a pipe */
232         SLIST_ENTRY(dn_pipe8)    next;   /* linked list in a hash slot */
233
234         int pipe_nr ;       /* number   */
235         uint32_t bandwidth;      /* really, bytes/tick.  */
236         int delay ;         /* really, ticks    */
237
238         struct  mbuf *head, *tail ; /* packets in delay line */
239
240         /* WF2Q+ */
241         struct dn_heap7 scheduler_heap ; /* top extract - key Finish time*/
242         struct dn_heap7 not_eligible_heap; /* top extract- key Start time */
243         struct dn_heap7 idle_heap ; /* random extract - key Start=Finish time */
244
245         int64_t V ;          /* virtual time */
246         int sum;            /* sum of weights of all active sessions */
247
248         /* Same as in dn_flow_queue, numbytes can become large */
249         int64_t numbytes;       /* bits I can transmit (more or less). */
250         uint64_t burst;     /* burst size, scaled: bits * hz */
251
252         int64_t sched_time ;     /* time pipe was scheduled in ready_heap */
253         int64_t idle_time;       /* start of pipe idle time */
254
255         char if_name[IFNAMSIZ];
256         struct ifnet *ifp ;
257         int ready ; /* set if ifp != NULL and we got a signal from it */
258
259         struct dn_flow_set fs ; /* used with fixed-rate flows */
260
261     /* fields to simulate a delay profile */
262 #define ED_MAX_NAME_LEN     32
263         char name[ED_MAX_NAME_LEN];
264         int loss_level;
265         int samples_no;
266         int *samples;
267 };
268
269 #define ED_MAX_SAMPLES_NO   1024
270 struct dn_pipe_max8 {
271         struct dn_pipe8 pipe;
272         int samples[ED_MAX_SAMPLES_NO];
273 };
274 SLIST_HEAD(dn_pipe_head8, dn_pipe8);
275
276 /*
277  * Changes from 7.2 to 8:
278  * dn_pipe:
279  *      numbytes from int to int64_t
280  *      add burst (int64_t)
281  *      add idle_time (int64_t)
282  *      add profile
283  *      add struct dn_pipe_max
284  *      add flag DN_HAS_PROFILE
285  *
286  * dn_flow_queue
287  *      numbytes from u_long to int64_t
288  *      add extra_bits (int64_t)
289  *      q_time from u_int32_t to int64_t and name idle_time
290  *
291  * dn_flow_set unchanged
292  *
293  */
294
295 /* NOTE:XXX copied from dummynet.c */
296 #define O_NEXT(p, len) ((void *)((char *)p + len))
297 static void
298 oid_fill(struct dn_id *oid, int len, int type, uintptr_t id)
299 {
300         oid->len = len;
301         oid->type = type;
302         oid->subtype = 0;
303         oid->id = id;
304 }
305 /* make room in the buffer and move the pointer forward */
306 static void *
307 o_next(struct dn_id **o, int len, int type)
308 {
309         struct dn_id *ret = *o;
310         oid_fill(ret, len, type, 0);
311         *o = O_NEXT(*o, len);
312         return ret;
313 }
314
315
316 static size_t pipesize7 = sizeof(struct dn_pipe7);
317 static size_t pipesize8 = sizeof(struct dn_pipe8);
318 static size_t pipesizemax8 = sizeof(struct dn_pipe_max8);
319
320 /* Indicate 'ipfw' version
321  * 1: from FreeBSD 7.2
322  * 0: from FreeBSD 8
323  * -1: unknown (for now is unused)
324  *
325  * It is update when a IP_DUMMYNET_DEL or IP_DUMMYNET_CONFIGURE request arrives
326  * NOTE: if a IP_DUMMYNET_GET arrives and the 'ipfw' version is unknown,
327  *       it is suppose to be the FreeBSD 8 version.
328  */
329 static int is7 = 0;
330
331 static int
332 convertflags2new(int src)
333 {
334         int dst = 0;
335
336         if (src & DNOLD_HAVE_FLOW_MASK)
337                 dst |= DN_HAVE_MASK;
338         if (src & DNOLD_QSIZE_IS_BYTES)
339                 dst |= DN_QSIZE_BYTES;
340         if (src & DNOLD_NOERROR)
341                 dst |= DN_NOERROR;
342         if (src & DNOLD_IS_RED)
343                 dst |= DN_IS_RED;
344         if (src & DNOLD_IS_GENTLE_RED)
345                 dst |= DN_IS_GENTLE_RED;
346         if (src & DNOLD_HAS_PROFILE)
347                 dst |= DN_HAS_PROFILE;
348
349         return dst;
350 }
351
352 static int
353 convertflags2old(int src)
354 {
355         int dst = 0;
356
357         if (src & DN_HAVE_MASK)
358                 dst |= DNOLD_HAVE_FLOW_MASK;
359         if (src & DN_IS_RED)
360                 dst |= DNOLD_IS_RED;
361         if (src & DN_IS_GENTLE_RED)
362                 dst |= DNOLD_IS_GENTLE_RED;
363         if (src & DN_NOERROR)
364                 dst |= DNOLD_NOERROR;
365         if (src & DN_HAS_PROFILE)
366                 dst |= DNOLD_HAS_PROFILE;
367         if (src & DN_QSIZE_BYTES)
368                 dst |= DNOLD_QSIZE_IS_BYTES;
369
370         return dst;
371 }
372
373 static int
374 dn_compat_del(void *v)
375 {
376         struct dn_pipe7 *p = (struct dn_pipe7 *) v;
377         struct dn_pipe8 *p8 = (struct dn_pipe8 *) v;
378         struct {
379                 struct dn_id oid;
380                 uintptr_t a[1]; /* add more if we want a list */
381         } cmd;
382
383         /* XXX DN_API_VERSION ??? */
384         oid_fill((void *)&cmd, sizeof(cmd), DN_CMD_DELETE, DN_API_VERSION);
385
386         if (is7) {
387                 if (p->pipe_nr == 0 && p->fs.fs_nr == 0)
388                         return EINVAL;
389                 if (p->pipe_nr != 0 && p->fs.fs_nr != 0)
390                         return EINVAL;
391         } else {
392                 if (p8->pipe_nr == 0 && p8->fs.fs_nr == 0)
393                         return EINVAL;
394                 if (p8->pipe_nr != 0 && p8->fs.fs_nr != 0)
395                         return EINVAL;
396         }
397
398         if (p->pipe_nr != 0) { /* pipe x delete */
399                 cmd.a[0] = p->pipe_nr;
400                 cmd.oid.subtype = DN_LINK;
401         } else { /* queue x delete */
402                 cmd.oid.subtype = DN_FS;
403                 cmd.a[0] = (is7) ? p->fs.fs_nr : p8->fs.fs_nr;
404         }
405
406         return do_config(&cmd, cmd.oid.len);
407 }
408
409 static int
410 dn_compat_config_queue(struct dn_fs *fs, void* v)
411 {
412         struct dn_pipe7 *p7 = (struct dn_pipe7 *)v;
413         struct dn_pipe8 *p8 = (struct dn_pipe8 *)v;
414         struct dn_flow_set *f;
415
416         if (is7)
417                 f = &p7->fs;
418         else
419                 f = &p8->fs;
420
421         fs->fs_nr = f->fs_nr;
422         fs->sched_nr = f->parent_nr;
423         fs->flow_mask = f->flow_mask;
424         fs->buckets = f->rq_size;
425         fs->qsize = f->qsize;
426         fs->plr = f->plr;
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
439 static int
440 dn_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
479 static int
480 dn_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  */
501 static int
502 dn_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
561 int
562 dn_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
580 int
581 dn_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
611 int
612 dn_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 = f->fs.plr;
652         fs->w_q = f->fs.w_q;
653         fs->max_th = f->max_th;
654         fs->min_th = f->min_th;
655         fs->max_p = f->fs.max_p;
656         fs->rq_elements = nq;
657
658         fs->flags_fs = convertflags2old(f->fs.flags);
659
660         *a->start += size;
661         return 0;
662 }
663
664
665 int
666 dn_compat_copy_pipe(struct copy_args *a, void *_o)
667 {
668         int have = a->end - *a->start;
669         int need = 0;
670         int pipe_size = sizeof(struct dn_pipe8);
671         int queue_size = sizeof(struct dn_flow_queue8);
672         int n_queue = 0; /* number of queues */
673
674         struct dn_schk *s = (struct dn_schk *)_o;
675         /* calculate needed space:
676          * - struct dn_pipe
677          * - if there are instances, dn_queue * n_instances
678          */
679         n_queue = (s->sch.flags & DN_HAVE_MASK ? dn_ht_entries(s->siht) :
680                                                 (s->siht ? 1 : 0));
681         need = pipe_size + queue_size * n_queue;
682         if (have < need) {
683                 D("have %d < need %d", have, need);
684                 return 1;
685         }
686         /* copy pipe */
687         dn_c_copy_pipe(s, a, n_queue);
688
689         /* copy queues */
690         if (s->sch.flags & DN_HAVE_MASK)
691                 dn_ht_scan(s->siht, dn_c_copy_q, a);
692         else if (s->siht)
693                 dn_c_copy_q(s->siht, a);
694         return 0;
695 }
696
697 int
698 dn_c_copy_fs(struct dn_fsk *f, struct copy_args *a, int nq)
699 {
700         struct dn_flow_set *fs = (struct dn_flow_set *)*a->start;
701
702         fs->next.sle_next = (struct dn_flow_set *)DN_IS_QUEUE;
703         fs->fs_nr = f->fs.fs_nr;
704         fs->qsize = f->fs.qsize;
705         fs->plr = f->fs.plr;
706         fs->w_q = f->fs.w_q;
707         fs->max_th = f->max_th;
708         fs->min_th = f->min_th;
709         fs->max_p = f->fs.max_p;
710         fs->flow_mask = f->fs.flow_mask;
711         fs->rq_elements = nq;
712         fs->rq_size = (f->fs.buckets ? f->fs.buckets : 1);
713         fs->parent_nr = f->fs.sched_nr;
714         fs->weight = f->fs.par[0];
715
716         fs->flags_fs = convertflags2old(f->fs.flags);
717         *a->start += sizeof(struct dn_flow_set);
718         return 0;
719 }
720
721 int
722 dn_compat_copy_queue(struct copy_args *a, void *_o)
723 {
724         int have = a->end - *a->start;
725         int need = 0;
726         int fs_size = sizeof(struct dn_flow_set);
727         int queue_size = sizeof(struct dn_flow_queue8);
728
729         struct dn_fsk *fs = (struct dn_fsk *)_o;
730         int n_queue = 0; /* number of queues */
731
732         n_queue = (fs->fs.flags & DN_HAVE_MASK ? dn_ht_entries(fs->qht) :
733                                                 (fs->qht ? 1 : 0));
734
735         need = fs_size + queue_size * n_queue;
736         if (have < need) {
737                 D("have < need");
738                 return 1;
739         }
740
741         /* copy flowset */
742         dn_c_copy_fs(fs, a, n_queue);
743
744         /* copy queues */
745         if (fs->fs.flags & DN_HAVE_MASK)
746                 dn_ht_scan(fs->qht, dn_c_copy_q, a);
747         else if (fs->qht)
748                 dn_c_copy_q(fs->qht, a);
749
750         return 0;
751 }
752
753 int
754 copy_data_helper_compat(void *_o, void *_arg)
755 {
756         struct copy_args *a = _arg;
757
758         if (a->type == DN_COMPAT_PIPE) {
759                 struct dn_schk *s = _o;
760                 if (s->sch.oid.subtype != 1 || s->sch.sched_nr <= DN_MAX_ID) {
761                         return 0;       /* not old type */
762                 }
763                 /* copy pipe parameters, and if instance exists, copy
764                  * other parameters and eventually queues.
765                  */
766                 if(dn_compat_copy_pipe(a, _o))
767                         return DNHT_SCAN_END;
768         } else if (a->type == DN_COMPAT_QUEUE) {
769                 struct dn_fsk *fs = _o;
770                 if (fs->fs.fs_nr >= DN_MAX_ID)
771                         return 0;
772                 if (dn_compat_copy_queue(a, _o))
773                         return DNHT_SCAN_END;
774         }
775         return 0;
776 }
777
778 /* Main function to manage old requests */
779 int
780 ip_dummynet_compat(struct sockopt *sopt)
781 {
782         int error=0;
783         void *v = NULL;
784         struct dn_id oid;
785
786         /* Length of data, used to found ipfw version... */
787         int len = sopt->sopt_valsize;
788
789         /* len can be 0 if command was dummynet_flush */
790         if (len == pipesize7) {
791                 D("setting compatibility with FreeBSD 7.2");
792                 is7 = 1;
793         }
794         else if (len == pipesize8 || len == pipesizemax8) {
795                 D("setting compatibility with FreeBSD 8");
796                 is7 = 0;
797         }
798
799         switch (sopt->sopt_name) {
800         default:
801                 printf("dummynet: -- unknown option %d", sopt->sopt_name);
802                 error = EINVAL;
803                 break;
804
805         case IP_DUMMYNET_FLUSH:
806                 oid_fill(&oid, sizeof(oid), DN_CMD_FLUSH, DN_API_VERSION);
807                 do_config(&oid, oid.len);
808                 break;
809
810         case IP_DUMMYNET_DEL:
811                 v = malloc(len, M_TEMP, M_WAITOK);
812                 error = sooptcopyin(sopt, v, len, len);
813                 if (error)
814                         break;
815                 error = dn_compat_del(v);
816                 free(v, M_TEMP);
817                 break;
818
819         case IP_DUMMYNET_CONFIGURE:
820                 v = malloc(len, M_TEMP, M_NOWAIT);
821                 if (v == NULL) {
822                         error = ENOMEM;
823                         break;
824                 }
825                 error = sooptcopyin(sopt, v, len, len);
826                 if (error)
827                         break;
828                 error = dn_compat_configure(v);
829                 free(v, M_TEMP);
830                 break;
831
832         case IP_DUMMYNET_GET: {
833                 void *buf;
834                 int ret;
835                 int original_size = sopt->sopt_valsize;
836                 int size;
837
838                 ret = dummynet_get(sopt, &buf);
839                 if (ret)
840                         return 0;//XXX ?
841                 size = sopt->sopt_valsize;
842                 sopt->sopt_valsize = original_size;
843                 D("size=%d, buf=%p", size, buf);
844                 ret = sooptcopyout(sopt, buf, size);
845                 if (ret)
846                         printf("  %s ERROR sooptcopyout\n", __FUNCTION__);
847                 if (buf)
848                         free(buf, M_DUMMYNET);
849             }
850         }
851
852         return error;
853 }
854
855