1 /*        $NetBSD: altq_cbq.c,v 1.42 2025/01/08 13:00:04 joe Exp $    */
2 /*        $KAME: altq_cbq.c,v 1.21 2005/04/13 03:44:24 suz Exp $      */
3 
4 /*
5  * Copyright (c) Sun Microsystems, Inc. 1993-1998 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  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *      This product includes software developed by the SMCC Technology
21  *      Development Group at Sun Microsystems, Inc.
22  *
23  * 4. The name of the Sun Microsystems, Inc nor may not be used to endorse or
24  *      promote products derived from this software without specific prior
25  *      written permission.
26  *
27  * SUN MICROSYSTEMS DOES NOT CLAIM MERCHANTABILITY OF THIS SOFTWARE OR THE
28  * SUITABILITY OF THIS SOFTWARE FOR ANY PARTICULAR PURPOSE.  The software is
29  * provided "as is" without express or implied warranty of any kind.
30  *
31  * These notices must be retained in any copies of any part of this software.
32  */
33 
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: altq_cbq.c,v 1.42 2025/01/08 13:00:04 joe Exp $");
36 
37 #ifdef _KERNEL_OPT
38 #include "opt_altq.h"
39 #include "opt_inet.h"
40 #include "pf.h"
41 #endif
42 
43 #ifdef ALTQ_CBQ     /* cbq is enabled by ALTQ_CBQ option in opt_altq.h */
44 
45 #include <sys/param.h>
46 #include <sys/malloc.h>
47 #include <sys/mbuf.h>
48 #include <sys/socket.h>
49 #include <sys/systm.h>
50 #include <sys/proc.h>
51 #include <sys/errno.h>
52 #include <sys/time.h>
53 #ifdef ALTQ3_COMPAT
54 #include <sys/uio.h>
55 #include <sys/kernel.h>
56 #endif
57 #include <sys/kauth.h>
58 
59 #include <net/if.h>
60 #include <netinet/in.h>
61 
62 #if NPF > 0
63 #include <net/pfvar.h>
64 #endif
65 #include <altq/altq.h>
66 #include <altq/altq_cbq.h>
67 #ifdef ALTQ3_COMPAT
68 #include <altq/altq_conf.h>
69 #endif
70 
71 #ifdef ALTQ3_COMPAT
72 /*
73  * Local Data structures.
74  */
75 static cbq_state_t *cbq_list = NULL;
76 #endif
77 
78 /*
79  * Forward Declarations.
80  */
81 static int                     cbq_class_destroy(cbq_state_t *, struct rm_class *);
82 static struct rm_class  *clh_to_clp(cbq_state_t *, u_int32_t);
83 static int                     cbq_clear_interface(cbq_state_t *);
84 static int                     cbq_request(struct ifaltq *, int, void *);
85 static int                     cbq_enqueue(struct ifaltq *, struct mbuf *);
86 static struct mbuf  *cbq_dequeue(struct ifaltq *, int);
87 static void                    cbqrestart(struct ifaltq *);
88 static void                    get_class_stats(class_stats_t *, struct rm_class *);
89 static void                    cbq_purge(cbq_state_t *);
90 #ifdef ALTQ3_COMPAT
91 static int          cbq_add_class(struct cbq_add_class *);
92 static int          cbq_delete_class(struct cbq_delete_class *);
93 static int          cbq_modify_class(struct cbq_modify_class *);
94 static int          cbq_class_create(cbq_state_t *, struct cbq_add_class *,
95                                          struct rm_class *, struct rm_class *);
96 static int          cbq_clear_hierarchy(struct cbq_interface *);
97 static int          cbq_set_enable(struct cbq_interface *, int);
98 static int          cbq_ifattach(struct cbq_interface *);
99 static int          cbq_ifdetach(struct cbq_interface *);
100 static int          cbq_getstats(struct cbq_getstats *);
101 
102 static int          cbq_add_filter(struct cbq_add_filter *);
103 static int          cbq_delete_filter(struct cbq_delete_filter *);
104 #endif /* ALTQ3_COMPAT */
105 
106 /*
107  * int
108  * cbq_class_destroy(cbq_mod_state_t *, struct rm_class *) - This
109  *        function destroys a given traffic class.  Before destroying
110  *        the class, all traffic for that class is released.
111  */
112 static int
cbq_class_destroy(cbq_state_t * cbqp,struct rm_class * cl)113 cbq_class_destroy(cbq_state_t *cbqp, struct rm_class *cl)
114 {
115           int       i;
116 
117           /* delete the class */
118           rmc_delete_class(&cbqp->ifnp, cl);
119 
120           /*
121            * free the class handle
122            */
123           for (i = 0; i < CBQ_MAX_CLASSES; i++)
124                     if (cbqp->cbq_class_tbl[i] == cl)
125                               cbqp->cbq_class_tbl[i] = NULL;
126 
127           if (cl == cbqp->ifnp.root_)
128                     cbqp->ifnp.root_ = NULL;
129           if (cl == cbqp->ifnp.default_)
130                     cbqp->ifnp.default_ = NULL;
131 #ifdef ALTQ3_COMPAT
132           if (cl == cbqp->ifnp.ctl_)
133                     cbqp->ifnp.ctl_ = NULL;
134 #endif
135           return 0;
136 }
137 
138 /* convert class handle to class pointer */
139 static struct rm_class *
clh_to_clp(cbq_state_t * cbqp,u_int32_t chandle)140 clh_to_clp(cbq_state_t *cbqp, u_int32_t chandle)
141 {
142           int i;
143           struct rm_class *cl;
144 
145           if (chandle == 0)
146                     return NULL;
147           /*
148            * first, try optimistically the slot matching the lower bits of
149            * the handle.  if it fails, do the linear table search.
150            */
151           i = chandle % CBQ_MAX_CLASSES;
152           if ((cl = cbqp->cbq_class_tbl[i]) != NULL &&
153               cl->stats_.handle == chandle)
154                     return (cl);
155           for (i = 0; i < CBQ_MAX_CLASSES; i++)
156                     if ((cl = cbqp->cbq_class_tbl[i]) != NULL &&
157                         cl->stats_.handle == chandle)
158                               return cl;
159           return NULL;
160 }
161 
162 static int
cbq_clear_interface(cbq_state_t * cbqp)163 cbq_clear_interface(cbq_state_t *cbqp)
164 {
165           int                  again, i;
166           struct rm_class     *cl;
167 
168 #ifdef ALTQ3_CLFIER_COMPAT
169           /* free the filters for this interface */
170           acc_discard_filters(&cbqp->cbq_classifier, NULL, 1);
171 #endif
172 
173           /* clear out the classes now */
174           do {
175                     again = 0;
176                     for (i = 0; i < CBQ_MAX_CLASSES; i++) {
177                               if ((cl = cbqp->cbq_class_tbl[i]) != NULL) {
178                                         if (is_a_parent_class(cl))
179                                                   again++;
180                                         else {
181                                                   cbq_class_destroy(cbqp, cl);
182                                                   cbqp->cbq_class_tbl[i] = NULL;
183                                                   if (cl == cbqp->ifnp.root_)
184                                                             cbqp->ifnp.root_ = NULL;
185                                                   if (cl == cbqp->ifnp.default_)
186                                                             cbqp->ifnp.default_ = NULL;
187 #ifdef ALTQ3_COMPAT
188                                                   if (cl == cbqp->ifnp.ctl_)
189                                                             cbqp->ifnp.ctl_ = NULL;
190 #endif
191                                         }
192                               }
193                     }
194           } while (again);
195 
196           return 0;
197 }
198 
199 static int
cbq_request(struct ifaltq * ifq,int req,void * arg)200 cbq_request(struct ifaltq *ifq, int req, void *arg)
201 {
202           cbq_state_t         *cbqp = (cbq_state_t *)ifq->altq_disc;
203 
204           switch (req) {
205           case ALTRQ_PURGE:
206                     cbq_purge(cbqp);
207                     break;
208           }
209           return 0;
210 }
211 
212 /* copy the stats info in rm_class to class_states_t */
213 static void
get_class_stats(class_stats_t * statsp,struct rm_class * cl)214 get_class_stats(class_stats_t *statsp, struct rm_class *cl)
215 {
216           statsp->xmit_cnt    = cl->stats_.xmit_cnt;
217           statsp->drop_cnt    = cl->stats_.drop_cnt;
218           statsp->over                  = cl->stats_.over;
219           statsp->borrows               = cl->stats_.borrows;
220           statsp->overactions = cl->stats_.overactions;
221           statsp->delays                = cl->stats_.delays;
222 
223           statsp->depth                 = cl->depth_;
224           statsp->priority    = cl->pri_;
225           statsp->maxidle               = cl->maxidle_;
226           statsp->minidle               = cl->minidle_;
227           statsp->offtime               = cl->offtime_;
228           statsp->qmax                  = qlimit(cl->q_);
229           statsp->ps_per_byte = cl->ps_per_byte_;
230           statsp->wrr_allot   = cl->w_allotment_;
231           statsp->qcnt                  = qlen(cl->q_);
232           statsp->avgidle               = cl->avgidle_;
233 
234           statsp->qtype                 = qtype(cl->q_);
235 #ifdef ALTQ_RED
236           if (q_is_red(cl->q_))
237                     red_getstats(cl->red_, &statsp->red[0]);
238 #endif
239 #ifdef ALTQ_RIO
240           if (q_is_rio(cl->q_))
241                     rio_getstats((rio_t *)cl->red_, &statsp->red[0]);
242 #endif
243 }
244 
245 #if NPF > 0
246 int
cbq_pfattach(struct pf_altq * a)247 cbq_pfattach(struct pf_altq *a)
248 {
249           struct ifnet        *ifp;
250           int                  s, error;
251 
252           if ((ifp = ifunit(a->ifname)) == NULL || a->altq_disc == NULL)
253                     return (EINVAL);
254           s = splnet();
255           error = altq_attach(&ifp->if_snd, ALTQT_CBQ, a->altq_disc,
256               cbq_enqueue, cbq_dequeue, cbq_request, NULL, NULL);
257           splx(s);
258           return error;
259 }
260 
261 int
cbq_add_altq(struct pf_altq * a)262 cbq_add_altq(struct pf_altq *a)
263 {
264           cbq_state_t         *cbqp;
265           struct ifnet        *ifp;
266 
267           if ((ifp = ifunit(a->ifname)) == NULL)
268                     return EINVAL;
269           if (!ALTQ_IS_READY(&ifp->if_snd))
270                     return ENODEV;
271 
272           /* allocate and initialize cbq_state_t */
273           cbqp = malloc(sizeof(cbq_state_t), M_DEVBUF, M_WAITOK|M_ZERO);
274           if (cbqp == NULL)
275                     return ENOMEM;
276           (void)memset(cbqp, 0, sizeof(cbq_state_t));
277           CALLOUT_INIT(&cbqp->cbq_callout);
278           cbqp->cbq_qlen = 0;
279           cbqp->ifnp.ifq_ = &ifp->if_snd;             /* keep the ifq */
280 
281           /* keep the state in pf_altq */
282           a->altq_disc = cbqp;
283 
284           return 0;
285 }
286 
287 int
cbq_remove_altq(struct pf_altq * a)288 cbq_remove_altq(struct pf_altq *a)
289 {
290           cbq_state_t         *cbqp;
291 
292           if ((cbqp = a->altq_disc) == NULL)
293                     return EINVAL;
294           a->altq_disc = NULL;
295 
296           cbq_clear_interface(cbqp);
297 
298           if (cbqp->ifnp.default_)
299                     cbq_class_destroy(cbqp, cbqp->ifnp.default_);
300           if (cbqp->ifnp.root_)
301                     cbq_class_destroy(cbqp, cbqp->ifnp.root_);
302 
303           /* deallocate cbq_state_t */
304           free(cbqp, M_DEVBUF);
305 
306           return 0;
307 }
308 
309 #define NSEC_TO_PSEC(s)       ((uint64_t)(s) * 1000)
310 int
cbq_add_queue(struct pf_altq * a)311 cbq_add_queue(struct pf_altq *a)
312 {
313           struct rm_class     *borrow, *parent;
314           cbq_state_t         *cbqp;
315           struct rm_class     *cl;
316           struct cbq_opts     *opts;
317           int                 i, error;
318 
319           if ((cbqp = a->altq_disc) == NULL)
320                     return EINVAL;
321           if (a->qid == 0)
322                     return EINVAL;
323 
324           /*
325            * find a free slot in the class table.  if the slot matching
326            * the lower bits of qid is free, use this slot.  otherwise,
327            * use the first free slot.
328            */
329           i = a->qid % CBQ_MAX_CLASSES;
330           if (cbqp->cbq_class_tbl[i] != NULL) {
331                     for (i = 0; i < CBQ_MAX_CLASSES; i++)
332                               if (cbqp->cbq_class_tbl[i] == NULL)
333                                         break;
334                     if (i == CBQ_MAX_CLASSES)
335                               return (EINVAL);
336           }
337 
338           opts = &a->pq_u.cbq_opts;
339           /* check parameters */
340           if (a->priority >= CBQ_MAXPRI)
341                     return EINVAL;
342 
343           /* Get pointers to parent and borrow classes.  */
344           parent = clh_to_clp(cbqp, a->parent_qid);
345           if (opts->flags & CBQCLF_BORROW)
346                     borrow = parent;
347           else
348                     borrow = NULL;
349 
350           /*
351            * A class must borrow from its parent or it can not
352            * borrow at all.  Hence, borrow can be null.
353            */
354           if (parent == NULL && (opts->flags & CBQCLF_ROOTCLASS) == 0) {
355                     printf("cbq_add_queue: no parent class!\n");
356                     return EINVAL;
357           }
358 
359           if ((borrow != parent)  && (borrow != NULL)) {
360                     printf("cbq_add_class: borrow class != parent\n");
361                     return EINVAL;
362           }
363 
364           /*
365            * check parameters
366            */
367           if ((opts->flags & CBQCLF_ROOTCLASS) != 0) {
368                     if (parent != NULL)
369                               return EINVAL;
370                     if (cbqp->ifnp.root_)
371                               return EINVAL;
372           }
373           if ((opts->flags & CBQCLF_DEFCLASS) != 0) {
374                     if (cbqp->ifnp.default_)
375                               return EINVAL;
376           }
377           if ((opts->flags & CBQCLF_CLASSMASK) == 0) {
378                     if (a->qid == 0)
379                               return EINVAL;
380           }
381 
382           /*
383            * create a class.  if this is a root class, initialize the
384            * interface.
385            */
386           if ((opts->flags & CBQCLF_ROOTCLASS) != 0) {
387                     error = rmc_init(cbqp->ifnp.ifq_, &cbqp->ifnp,
388                         NSEC_TO_PSEC(opts->ns_per_byte), cbqrestart, a->qlimit, RM_MAXQUEUED,
389                         opts->maxidle, opts->minidle, opts->offtime,
390                         opts->flags);
391                     if (error != 0)
392                               return error;
393                     cl = cbqp->ifnp.root_;
394           } else {
395                     cl = rmc_newclass(a->priority,
396                                           &cbqp->ifnp, NSEC_TO_PSEC(opts->ns_per_byte),
397                                           rmc_delay_action, a->qlimit, parent, borrow,
398                                           opts->maxidle, opts->minidle, opts->offtime,
399                                           opts->pktsize, opts->flags);
400           }
401           if (cl == NULL)
402                     return ENOMEM;
403 
404           /* return handle to user space. */
405           cl->stats_.handle = a->qid;
406           cl->stats_.depth = cl->depth_;
407 
408           /* save the allocated class */
409           cbqp->cbq_class_tbl[i] = cl;
410 
411           if ((opts->flags & CBQCLF_DEFCLASS) != 0)
412                     cbqp->ifnp.default_ = cl;
413 
414           return 0;
415 }
416 
417 int
cbq_remove_queue(struct pf_altq * a)418 cbq_remove_queue(struct pf_altq *a)
419 {
420           struct rm_class     *cl;
421           cbq_state_t         *cbqp;
422           int                 i;
423 
424           if ((cbqp = a->altq_disc) == NULL)
425                     return EINVAL;
426 
427           if ((cl = clh_to_clp(cbqp, a->qid)) == NULL)
428                     return EINVAL;
429 
430           /* if we are a parent class, then return an error. */
431           if (is_a_parent_class(cl))
432                     return EINVAL;
433 
434           /* delete the class */
435           rmc_delete_class(&cbqp->ifnp, cl);
436 
437           /*
438            * free the class handle
439            */
440           for (i = 0; i < CBQ_MAX_CLASSES; i++)
441                     if (cbqp->cbq_class_tbl[i] == cl) {
442                               cbqp->cbq_class_tbl[i] = NULL;
443                               if (cl == cbqp->ifnp.root_)
444                                         cbqp->ifnp.root_ = NULL;
445                               if (cl == cbqp->ifnp.default_)
446                                         cbqp->ifnp.default_ = NULL;
447                               break;
448                     }
449 
450           return 0;
451 }
452 
453 int
cbq_getqstats(struct pf_altq * a,void * ubuf,int * nbytes)454 cbq_getqstats(struct pf_altq *a, void *ubuf, int *nbytes)
455 {
456           cbq_state_t         *cbqp;
457           struct rm_class     *cl;
458           class_stats_t        stats;
459           int                  error = 0;
460 
461           if ((cbqp = altq_lookup(a->ifname, ALTQT_CBQ)) == NULL)
462                     return EBADF;
463 
464           if ((cl = clh_to_clp(cbqp, a->qid)) == NULL)
465                     return EINVAL;
466 
467           if (*nbytes < sizeof(stats))
468                     return EINVAL;
469 
470           memset(&stats, 0, sizeof(stats));
471           get_class_stats(&stats, cl);
472 
473           if ((error = copyout((void *)&stats, ubuf, sizeof(stats))) != 0)
474                     return error;
475           *nbytes = sizeof(stats);
476           return (0);
477 }
478 #endif /* NPF > 0 */
479 
480 /*
481  * int
482  * cbq_enqueue(struct ifaltq *ifq, struct mbuf *m)
483  *                  - Queue data packets.
484  *
485  *        cbq_enqueue is set to ifp->if_altqenqueue and called by an upper
486  *        layer (e.g. ether_output).  cbq_enqueue queues the given packet
487  *        to the cbq, then invokes the driver's start routine.
488  *
489  *        Assumptions:        called in splnet
490  *        Returns:  0 if the queueing is successful.
491  *                            ENOBUFS if a packet dropping occurred as a result of
492  *                            the queueing.
493  */
494 
495 static int
cbq_enqueue(struct ifaltq * ifq,struct mbuf * m)496 cbq_enqueue(struct ifaltq *ifq, struct mbuf *m)
497 {
498           struct altq_pktattr pktattr;
499           cbq_state_t         *cbqp = (cbq_state_t *)ifq->altq_disc;
500           struct rm_class     *cl;
501           struct m_tag        *t;
502           int                  len;
503 
504           /* grab class set by classifier */
505           if ((m->m_flags & M_PKTHDR) == 0) {
506                     /* should not happen */
507                     printf("altq: packet for %s does not have pkthdr\n",
508                         ifq->altq_ifp->if_xname);
509                     m_freem(m);
510                     return ENOBUFS;
511           }
512           cl = NULL;
513           if ((t = m_tag_find(m, PACKET_TAG_ALTQ_QID)) != NULL)
514                     cl = clh_to_clp(cbqp, ((struct altq_tag *)(t+1))->qid);
515 #ifdef ALTQ3_COMPAT
516           else if (ifq->altq_flags & ALTQF_CLASSIFY)
517                     cl = m->m_pkthdr.pattr_class;
518 #endif
519           if (cl == NULL) {
520                     cl = cbqp->ifnp.default_;
521                     if (cl == NULL) {
522                               m_freem(m);
523                               return ENOBUFS;
524                     }
525           }
526 #ifdef ALTQ3_COMPAT
527           if (m->m_pkthdr.pattr_af != AF_UNSPEC) {
528                     pktattr.pattr_class = m->m_pkthdr.pattr_class;
529                     pktattr.pattr_af = m->m_pkthdr.pattr_af;
530                     pktattr.pattr_hdr = m->m_pkthdr.pattr_hdr;
531 
532                     cl->pktattr_ = &pktattr;  /* save proto hdr used by ECN */
533           } else
534 #endif
535                     cl->pktattr_ = NULL;
536           len = m_pktlen(m);
537           if (rmc_queue_packet(cl, m) != 0) {
538                     /* drop occurred.  some mbuf was freed in rmc_queue_packet. */
539                     PKTCNTR_ADD(&cl->stats_.drop_cnt, len);
540                     return ENOBUFS;
541           }
542 
543           /* successfully queued. */
544           ++cbqp->cbq_qlen;
545           IFQ_INC_LEN(ifq);
546           return 0;
547 }
548 
549 static struct mbuf *
cbq_dequeue(struct ifaltq * ifq,int op)550 cbq_dequeue(struct ifaltq *ifq, int op)
551 {
552           cbq_state_t         *cbqp = (cbq_state_t *)ifq->altq_disc;
553           struct mbuf         *m;
554 
555           m = rmc_dequeue_next(&cbqp->ifnp, op);
556 
557           if (m && op == ALTDQ_REMOVE) {
558                     --cbqp->cbq_qlen;  /* decrement # of packets in cbq */
559                     IFQ_DEC_LEN(ifq);
560 
561                     /* Update the class. */
562                     rmc_update_class_util(&cbqp->ifnp);
563           }
564           return m;
565 }
566 
567 /*
568  * void
569  * cbqrestart(queue_t *) - Restart sending of data.
570  * called from rmc_restart in splnet via timeout after waking up
571  * a suspended class.
572  *        Returns:  NONE
573  */
574 
575 static void
cbqrestart(struct ifaltq * ifq)576 cbqrestart(struct ifaltq *ifq)
577 {
578           cbq_state_t         *cbqp;
579           struct ifnet        *ifp;
580 
581           if (!ALTQ_IS_ENABLED(ifq))
582                     /* cbq must have been detached */
583                     return;
584 
585           if ((cbqp = (cbq_state_t *)ifq->altq_disc) == NULL)
586                     /* should not happen */
587                     return;
588 
589           ifp = ifq->altq_ifp;
590           if (ifp->if_start &&
591               cbqp->cbq_qlen > 0 && (ifp->if_flags & IFF_OACTIVE) == 0)
592                     if_start_lock(ifp);
593 }
594 
595 static void
cbq_purge(cbq_state_t * cbqp)596 cbq_purge(cbq_state_t *cbqp)
597 {
598           struct rm_class     *cl;
599           int                  i;
600 
601           for (i = 0; i < CBQ_MAX_CLASSES; i++)
602                     if ((cl = cbqp->cbq_class_tbl[i]) != NULL)
603                               rmc_dropall(cl);
604           if (ALTQ_IS_ENABLED(cbqp->ifnp.ifq_))
605                     cbqp->ifnp.ifq_->ifq_len = 0;
606 }
607 #ifdef ALTQ3_COMPAT
608 
609 static int
cbq_add_class(struct cbq_add_class * acp)610 cbq_add_class(struct cbq_add_class *acp)
611 {
612           char                *ifacename;
613           struct rm_class     *borrow, *parent;
614           cbq_state_t         *cbqp;
615 
616           ifacename = acp->cbq_iface.cbq_ifacename;
617           if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL)
618                     return EBADF;
619 
620           /* check parameters */
621           if (acp->cbq_class.priority >= CBQ_MAXPRI ||
622               acp->cbq_class.maxq > CBQ_MAXQSIZE)
623                     return EINVAL;
624 
625           /* Get pointers to parent and borrow classes.  */
626           parent = clh_to_clp(cbqp, acp->cbq_class.parent_class_handle);
627           borrow = clh_to_clp(cbqp, acp->cbq_class.borrow_class_handle);
628 
629           /*
630            * A class must borrow from its parent or it can not
631            * borrow at all.  Hence, borrow can be null.
632            */
633           if (parent == NULL && (acp->cbq_class.flags & CBQCLF_ROOTCLASS) == 0) {
634                     printf("cbq_add_class: no parent class!\n");
635                     return EINVAL;
636           }
637 
638           if ((borrow != parent)  && (borrow != NULL)) {
639                     printf("cbq_add_class: borrow class != parent\n");
640                     return EINVAL;
641           }
642 
643           return cbq_class_create(cbqp, acp, parent, borrow);
644 }
645 
646 static int
cbq_delete_class(struct cbq_delete_class * dcp)647 cbq_delete_class(struct cbq_delete_class *dcp)
648 {
649           char                *ifacename;
650           struct rm_class     *cl;
651           cbq_state_t         *cbqp;
652 
653           ifacename = dcp->cbq_iface.cbq_ifacename;
654           if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL)
655                     return EBADF;
656 
657           if ((cl = clh_to_clp(cbqp, dcp->cbq_class_handle)) == NULL)
658                     return EINVAL;
659 
660           /* if we are a parent class, then return an error. */
661           if (is_a_parent_class(cl))
662                     return EINVAL;
663 
664           /* if a filter has a reference to this class delete the filter */
665           acc_discard_filters(&cbqp->cbq_classifier, cl, 0);
666 
667           return cbq_class_destroy(cbqp, cl);
668 }
669 
670 static int
cbq_modify_class(struct cbq_modify_class * acp)671 cbq_modify_class(struct cbq_modify_class *acp)
672 {
673           char                *ifacename;
674           struct rm_class     *cl;
675           cbq_state_t         *cbqp;
676 
677           ifacename = acp->cbq_iface.cbq_ifacename;
678           if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL)
679                     return EBADF;
680 
681           /* Get pointer to this class */
682           if ((cl = clh_to_clp(cbqp, acp->cbq_class_handle)) == NULL)
683                     return EINVAL;
684 
685           if (rmc_modclass(cl, acp->cbq_class.pico_sec_per_byte,
686                                acp->cbq_class.maxq, acp->cbq_class.maxidle,
687                                acp->cbq_class.minidle, acp->cbq_class.offtime,
688                                acp->cbq_class.pktsize) < 0)
689                     return EINVAL;
690           return 0;
691 }
692 
693 /*
694  * struct rm_class *
695  * cbq_class_create(cbq_mod_state_t *cbqp, struct cbq_add_class *acp,
696  *                  struct rm_class *parent, struct rm_class *borrow)
697  *
698  * This function create a new traffic class in the CBQ class hierarchy of
699  * given parameters.  The class that created is either the root, default,
700  * or a new dynamic class.  If CBQ is not initialized, the root class
701  * will be created.
702  */
703 static int
cbq_class_create(cbq_state_t * cbqp,struct cbq_add_class * acp,struct rm_class * parent,struct rm_class * borrow)704 cbq_class_create(cbq_state_t *cbqp, struct cbq_add_class *acp,
705     struct rm_class *parent, struct rm_class *borrow)
706 {
707           struct rm_class     *cl;
708           cbq_class_spec_t *spec = &acp->cbq_class;
709           u_int32_t chandle;
710           int                 i, error;
711 
712           /*
713            * allocate class handle
714            */
715           for (i = 1; i < CBQ_MAX_CLASSES; i++)
716                     if (cbqp->cbq_class_tbl[i] == NULL)
717                               break;
718           if (i == CBQ_MAX_CLASSES)
719                     return EINVAL;
720           chandle = i;        /* use the slot number as class handle */
721 
722           /*
723            * create a class.  if this is a root class, initialize the
724            * interface.
725            */
726           if ((spec->flags & CBQCLF_ROOTCLASS) != 0) {
727                     error = rmc_init(cbqp->ifnp.ifq_, &cbqp->ifnp,
728                         spec->pico_sec_per_byte, cbqrestart, spec->maxq,
729                         RM_MAXQUEUED, spec->maxidle, spec->minidle, spec->offtime,
730                         spec->flags);
731                     if (error)
732                               return error;
733                     cl = cbqp->ifnp.root_;
734           } else {
735                     cl = rmc_newclass(spec->priority,
736                                           &cbqp->ifnp, spec->pico_sec_per_byte,
737                                           rmc_delay_action, spec->maxq, parent, borrow,
738                                           spec->maxidle, spec->minidle, spec->offtime,
739                                           spec->pktsize, spec->flags);
740           }
741           if (cl == NULL)
742                     return ENOMEM;
743 
744           /* return handle to user space. */
745           acp->cbq_class_handle = chandle;
746 
747           cl->stats_.handle = chandle;
748           cl->stats_.depth = cl->depth_;
749 
750           /* save the allocated class */
751           cbqp->cbq_class_tbl[i] = cl;
752 
753           if ((spec->flags & CBQCLF_DEFCLASS) != 0)
754                     cbqp->ifnp.default_ = cl;
755           if ((spec->flags & CBQCLF_CTLCLASS) != 0)
756                     cbqp->ifnp.ctl_ = cl;
757 
758           return 0;
759 }
760 
761 static int
cbq_add_filter(struct cbq_add_filter * afp)762 cbq_add_filter(struct cbq_add_filter *afp)
763 {
764           char                *ifacename;
765           cbq_state_t         *cbqp;
766           struct rm_class     *cl;
767 
768           ifacename = afp->cbq_iface.cbq_ifacename;
769           if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL)
770                     return EBADF;
771 
772           /* Get the pointer to class. */
773           if ((cl = clh_to_clp(cbqp, afp->cbq_class_handle)) == NULL)
774                     return EINVAL;
775 
776           return acc_add_filter(&cbqp->cbq_classifier, &afp->cbq_filter,
777                                     cl, &afp->cbq_filter_handle);
778 }
779 
780 static int
cbq_delete_filter(struct cbq_delete_filter * dfp)781 cbq_delete_filter(struct cbq_delete_filter *dfp)
782 {
783           char                *ifacename;
784           cbq_state_t         *cbqp;
785 
786           ifacename = dfp->cbq_iface.cbq_ifacename;
787           if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL)
788                     return EBADF;
789 
790           return acc_delete_filter(&cbqp->cbq_classifier,
791                                          dfp->cbq_filter_handle);
792 }
793 
794 /*
795  * cbq_clear_hierarchy deletes all classes and their filters on the
796  * given interface.
797  */
798 static int
cbq_clear_hierarchy(struct cbq_interface * ifacep)799 cbq_clear_hierarchy(struct cbq_interface *ifacep)
800 {
801           char                *ifacename;
802           cbq_state_t         *cbqp;
803 
804           ifacename = ifacep->cbq_ifacename;
805           if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL)
806                     return EBADF;
807 
808           return cbq_clear_interface(cbqp);
809 }
810 
811 /*
812  * static int
813  * cbq_set_enable(struct cbq_enable *ep) - this function processed the
814  *        ioctl request to enable class based queueing.  It searches the list
815  *        of interfaces for the specified interface and then enables CBQ on
816  *        that interface.
817  *
818  *        Returns:  0, for no error.
819  *                            EBADF, for specified interface not found.
820  */
821 
822 static int
cbq_set_enable(struct cbq_interface * ep,int enable)823 cbq_set_enable(struct cbq_interface *ep, int enable)
824 {
825           int       error = 0;
826           cbq_state_t         *cbqp;
827           char      *ifacename;
828 
829           ifacename = ep->cbq_ifacename;
830           if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL)
831                     return EBADF;
832 
833           switch (enable) {
834           case ENABLE:
835                     if (cbqp->ifnp.root_ == NULL || cbqp->ifnp.default_ == NULL) {
836                               if (cbqp->ifnp.root_ == NULL)
837                                         printf("No Root Class for %s\n", ifacename);
838                               if (cbqp->ifnp.default_ == NULL)
839                                         printf("No Default Class for %s\n", ifacename);
840                               error = EINVAL;
841                     } else if ((error = altq_enable(cbqp->ifnp.ifq_)) == 0) {
842                               cbqp->cbq_qlen = 0;
843                     }
844                     break;
845 
846           case DISABLE:
847                     error = altq_disable(cbqp->ifnp.ifq_);
848                     break;
849           }
850           return error;
851 }
852 
853 static int
cbq_getstats(struct cbq_getstats * gsp)854 cbq_getstats(struct cbq_getstats *gsp)
855 {
856           char                *ifacename;
857           int                 i, n, nclasses;
858           cbq_state_t         *cbqp;
859           struct rm_class     *cl;
860           class_stats_t       stats, *usp;
861           int error = 0;
862 
863           ifacename = gsp->iface.cbq_ifacename;
864           nclasses = gsp->nclasses;
865           usp = gsp->stats;
866 
867           if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL)
868                     return EBADF;
869           if (nclasses <= 0)
870                     return EINVAL;
871 
872           for (n = 0, i = 0; n < nclasses && i < CBQ_MAX_CLASSES; n++, i++) {
873                     while ((cl = cbqp->cbq_class_tbl[i]) == NULL)
874                               if (++i >= CBQ_MAX_CLASSES)
875                                         goto out;
876 
877                     memset(&stats, 0, sizeof(stats));
878                     get_class_stats(&stats, cl);
879                     stats.handle = cl->stats_.handle;
880 
881                     if ((error = copyout((void *)&stats, (void *)usp++,
882                         sizeof(stats))) != 0)
883                               return error;
884           }
885 
886  out:
887           gsp->nclasses = n;
888           return error;
889 }
890 
891 static int
cbq_ifattach(struct cbq_interface * ifacep)892 cbq_ifattach(struct cbq_interface *ifacep)
893 {
894           int                 error = 0;
895           char                *ifacename;
896           cbq_state_t         *new_cbqp;
897           struct ifnet        *ifp;
898 
899           ifacename = ifacep->cbq_ifacename;
900           if ((ifp = ifunit(ifacename)) == NULL)
901                     return ENXIO;
902           if (!ALTQ_IS_READY(&ifp->if_snd))
903                     return ENXIO;
904 
905           /* allocate and initialize cbq_state_t */
906           new_cbqp = malloc(sizeof(cbq_state_t), M_DEVBUF, M_WAITOK|M_ZERO);
907           if (new_cbqp == NULL)
908                     return ENOMEM;
909           CALLOUT_INIT(&new_cbqp->cbq_callout);
910 
911           new_cbqp->cbq_qlen = 0;
912           new_cbqp->ifnp.ifq_ = &ifp->if_snd;         /* keep the ifq */
913 
914           /*
915            * set CBQ to this ifnet structure.
916            */
917           error = altq_attach(&ifp->if_snd, ALTQT_CBQ, new_cbqp,
918                                   cbq_enqueue, cbq_dequeue, cbq_request,
919                                   &new_cbqp->cbq_classifier, acc_classify);
920           if (error) {
921                     free(new_cbqp, M_DEVBUF);
922                     return error;
923           }
924 
925           /* prepend to the list of cbq_state_t's. */
926           new_cbqp->cbq_next = cbq_list;
927           cbq_list = new_cbqp;
928 
929           return 0;
930 }
931 
932 static int
cbq_ifdetach(struct cbq_interface * ifacep)933 cbq_ifdetach(struct cbq_interface *ifacep)
934 {
935           char                *ifacename;
936           cbq_state_t         *cbqp;
937 
938           ifacename = ifacep->cbq_ifacename;
939           if ((cbqp = altq_lookup(ifacename, ALTQT_CBQ)) == NULL)
940                     return (EBADF);
941 
942           (void)cbq_set_enable(ifacep, DISABLE);
943 
944           cbq_clear_interface(cbqp);
945 
946           /* remove CBQ from the ifnet structure. */
947           (void)altq_detach(cbqp->ifnp.ifq_);
948 
949           /* remove from the list of cbq_state_t's. */
950           if (cbq_list == cbqp)
951                     cbq_list = cbqp->cbq_next;
952           else {
953                     cbq_state_t *cp;
954 
955                     for (cp = cbq_list; cp != NULL; cp = cp->cbq_next)
956                               if (cp->cbq_next == cbqp) {
957                                         cp->cbq_next = cbqp->cbq_next;
958                                         break;
959                               }
960                     ASSERT(cp != NULL);
961           }
962 
963           /* deallocate cbq_state_t */
964           free(cbqp, M_DEVBUF);
965 
966           return 0;
967 }
968 
969 /*
970  * cbq device interface
971  */
972 
973 altqdev_decl(cbq);
974 
975 int
cbqopen(dev_t dev,int flag,int fmt,struct lwp * l)976 cbqopen(dev_t dev, int flag, int fmt,
977     struct lwp *l)
978 {
979           return 0;
980 }
981 
982 int
cbqclose(dev_t dev,int flag,int fmt,struct lwp * l)983 cbqclose(dev_t dev, int flag, int fmt,
984     struct lwp *l)
985 {
986           struct ifnet *ifp;
987           struct cbq_interface iface;
988           int err, error = 0;
989 
990           while (cbq_list) {
991                     ifp = cbq_list->ifnp.ifq_->altq_ifp;
992                     snprintf(iface.cbq_ifacename, sizeof(iface.cbq_ifacename),
993                         "%s", ifp->if_xname);
994                     err = cbq_ifdetach(&iface);
995                     if (err != 0 && error == 0)
996                               error = err;
997           }
998 
999           return error;
1000 }
1001 
1002 int
cbqioctl(dev_t dev,ioctlcmd_t cmd,void * addr,int flag,struct lwp * l)1003 cbqioctl(dev_t dev, ioctlcmd_t cmd, void *addr, int flag,
1004     struct lwp *l)
1005 {
1006           int       error = 0;
1007 
1008           /* check cmd for superuser only */
1009           switch (cmd) {
1010           case CBQ_GETSTATS:
1011                     /* currently only command that an ordinary user can call */
1012                     break;
1013           default:
1014                     error = kauth_authorize_network(l->l_cred,
1015                         KAUTH_NETWORK_ALTQ, KAUTH_REQ_NETWORK_ALTQ_CBQ, NULL, NULL,
1016                         NULL);
1017                     if (error)
1018                               return error;
1019                     break;
1020           }
1021 
1022           switch (cmd) {
1023 
1024           case CBQ_ENABLE:
1025                     error = cbq_set_enable((struct cbq_interface *)addr, ENABLE);
1026                     break;
1027 
1028           case CBQ_DISABLE:
1029                     error = cbq_set_enable((struct cbq_interface *)addr, DISABLE);
1030                     break;
1031 
1032           case CBQ_ADD_FILTER:
1033                     error = cbq_add_filter((struct cbq_add_filter *)addr);
1034                     break;
1035 
1036           case CBQ_DEL_FILTER:
1037                     error = cbq_delete_filter((struct cbq_delete_filter *)addr);
1038                     break;
1039 
1040           case CBQ_ADD_CLASS:
1041                     error = cbq_add_class((struct cbq_add_class *)addr);
1042                     break;
1043 
1044           case CBQ_DEL_CLASS:
1045                     error = cbq_delete_class((struct cbq_delete_class *)addr);
1046                     break;
1047 
1048           case CBQ_MODIFY_CLASS:
1049                     error = cbq_modify_class((struct cbq_modify_class *)addr);
1050                     break;
1051 
1052           case CBQ_CLEAR_HIERARCHY:
1053                     error = cbq_clear_hierarchy((struct cbq_interface *)addr);
1054                     break;
1055 
1056           case CBQ_IF_ATTACH:
1057                     error = cbq_ifattach((struct cbq_interface *)addr);
1058                     break;
1059 
1060           case CBQ_IF_DETACH:
1061                     error = cbq_ifdetach((struct cbq_interface *)addr);
1062                     break;
1063 
1064           case CBQ_GETSTATS:
1065                     error = cbq_getstats((struct cbq_getstats *)addr);
1066                     break;
1067 
1068           default:
1069                     error = EINVAL;
1070                     break;
1071           }
1072 
1073           return error;
1074 }
1075 
1076 #if 0
1077 /* for debug */
1078 static void cbq_class_dump(int);
1079 
1080 static void
1081 cbq_class_dump(int i)
1082 {
1083           struct rm_class *cl;
1084           rm_class_stats_t *s;
1085           struct _class_queue_ *q;
1086 
1087           if (cbq_list == NULL) {
1088                     printf("cbq_class_dump: no cbq_state found\n");
1089                     return;
1090           }
1091           cl = cbq_list->cbq_class_tbl[i];
1092 
1093           printf("class %d cl=%p\n", i, cl);
1094           if (cl != NULL) {
1095                     s = &cl->stats_;
1096                     q = cl->q_;
1097 
1098                     printf("pri=%d, depth=%d, maxrate=%d, allotment=%d\n",
1099                            cl->pri_, cl->depth_, cl->maxrate_, cl->allotment_);
1100                     printf("w_allotment=%d, bytes_alloc=%d, avgidle=%d, maxidle=%d\n",
1101                            cl->w_allotment_, cl->bytes_alloc_, cl->avgidle_,
1102                            cl->maxidle_);
1103                     printf("minidle=%d, offtime=%d, sleeping=%d, leaf=%d\n",
1104                            cl->minidle_, cl->offtime_, cl->sleeping_, cl->leaf_);
1105                     printf("handle=%d, depth=%d, packets=%d, bytes=%d\n",
1106                            s->handle, s->depth,
1107                            (int)s->xmit_cnt.packets, (int)s->xmit_cnt.bytes);
1108                     printf("over=%d\n, borrows=%d, drops=%d, overactions=%d, delays=%d\n",
1109                            s->over, s->borrows, (int)s->drop_cnt.packets,
1110                            s->overactions, s->delays);
1111                     printf("tail=%p, head=%p, qlen=%d, qlim=%d, qthresh=%d,qtype=%d\n",
1112                            q->tail_, q->head_, q->qlen_, q->qlim_,
1113                            q->qthresh_, q->qtype_);
1114           }
1115 }
1116 #endif /* 0 */
1117 
1118 #ifdef KLD_MODULE
1119 
1120 static struct altqsw cbq_sw =
1121           {"cbq", cbqopen, cbqclose, cbqioctl};
1122 
1123 ALTQ_MODULE(altq_cbq, ALTQT_CBQ, &cbq_sw);
1124 MODULE_DEPEND(altq_cbq, altq_red, 1, 1, 1);
1125 MODULE_DEPEND(altq_cbq, altq_rio, 1, 1, 1);
1126 
1127 #endif /* KLD_MODULE */
1128 #endif /* ALTQ3_COMPAT */
1129 
1130 #endif /* ALTQ_CBQ */
1131