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