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
66 struct dn_heap_entry7 {
67 int64_t key; /* sorting key. Topmost element is smallest one */
68 void *object; /* object pointer */
69 };
70
71 struct 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 */
79 struct 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 };
130 SLIST_HEAD(dn_flow_set_head, dn_flow_set);
131
132 #define DN_IS_PIPE 0x4000
133 #define DN_IS_QUEUE 0x8000
134 struct 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
164 struct 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 };
195 SLIST_HEAD(dn_pipe_head7, dn_pipe7);
196
197 /* FREEBSD8 ip_dummynet.h r196045 */
198 struct 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
229 struct 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
268 struct dn_pipe_max8 {
269 struct dn_pipe8 pipe;
270 int samples[ED_MAX_SAMPLES_NO];
271 };
272 SLIST_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))
295 static void
oid_fill(struct dn_id * oid,int len,int type,uintptr_t id)296 oid_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 */
304 static void *
o_next(struct dn_id ** o,int len,int type)305 o_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
313 static size_t pipesize7 = sizeof(struct dn_pipe7);
314 static size_t pipesize8 = sizeof(struct dn_pipe8);
315 static 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 */
326 static int is7 = 0;
327
328 static int
convertflags2new(int src)329 convertflags2new(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
349 static int
convertflags2old(int src)350 convertflags2old(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
370 static int
dn_compat_del(void * v)371 dn_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
406 static int
dn_compat_config_queue(struct dn_fs * fs,void * v)407 dn_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
439 static int
dn_compat_config_pipe(struct dn_sch * sch,struct dn_link * p,struct dn_fs * fs,void * v)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
dn_compat_config_profile(struct dn_profile * pf,struct dn_link * p,void * v)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
dn_compat_configure(void * v)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
dn_compat_calc_size(void)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
dn_c_copy_q(void * _ni,void * arg)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
dn_c_copy_pipe(struct dn_schk * s,struct copy_args * a,int nq)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[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
667 int
dn_compat_copy_pipe(struct copy_args * a,void * _o)668 dn_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
699 int
dn_c_copy_fs(struct dn_fsk * f,struct copy_args * a,int nq)700 dn_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
726 int
dn_compat_copy_queue(struct copy_args * a,void * _o)727 dn_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
758 int
copy_data_helper_compat(void * _o,void * _arg)759 copy_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 */
784 int
ip_dummynet_compat(struct sockopt * sopt)785 ip_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