1 /*        $NetBSD: qop_cdnr.c,v 1.4 2001/08/22 08:52:37 itojun Exp $  */
2 /*        $KAME: qop_cdnr.c,v 1.9 2001/08/16 10:39:14 kjc Exp $       */
3 /*
4  * Copyright (C) 1999-2000
5  *        Sony Computer Science Laboratories, Inc.  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 SONY CSL 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 SONY CSL 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 #include <sys/param.h>
30 #include <sys/socket.h>
31 #include <sys/sockio.h>
32 #include <sys/ioctl.h>
33 #include <sys/fcntl.h>
34 #include <net/if.h>
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
37 
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <unistd.h>
41 #include <stddef.h>
42 #include <string.h>
43 #include <ctype.h>
44 #include <errno.h>
45 #include <syslog.h>
46 #include <netdb.h>
47 
48 #include <altq/altq.h>
49 #include <altq/altq_cdnr.h>
50 #include "altq_qop.h"
51 #include "qop_cdnr.h"
52 /*
53  * diffserve traffic conditioner support
54  *
55  * we use the existing qop interface to support conditioner.
56  */
57 
58 static struct ifinfo *cdnr_ifname2ifinfo(const char *);
59 static int cdnr_attach(struct ifinfo *);
60 static int cdnr_detach(struct ifinfo *);
61 static int cdnr_enable(struct ifinfo *);
62 static int cdnr_disable(struct ifinfo *);
63 static int cdnr_add_class(struct classinfo *);
64 static int cdnr_modify_class(struct classinfo *, void *);
65 static int cdnr_delete_class(struct classinfo *);
66 static int cdnr_add_filter(struct fltrinfo *);
67 static int cdnr_delete_filter(struct fltrinfo *);
68 static int verify_tbprofile(struct tb_profile *, const char *);
69 
70 #define CDNR_DEVICE "/dev/altq/cdnr"
71 
72 static int cdnr_fd = -1;
73 static int cdnr_refcount = 0;
74 
75 static struct qdisc_ops cdnr_qdisc = {
76           ALTQT_CDNR,
77           "cdnr",
78           cdnr_attach,
79           cdnr_detach,
80           NULL,                         /* clear */
81           cdnr_enable,
82           cdnr_disable,
83           cdnr_add_class,
84           cdnr_modify_class,
85           cdnr_delete_class,
86           cdnr_add_filter,
87           cdnr_delete_filter,
88 };
89 
90 u_long
cdnr_name2handle(const char * ifname,const char * cdnr_name)91 cdnr_name2handle(const char *ifname, const char *cdnr_name)
92 {
93           struct ifinfo                 *ifinfo;
94           struct classinfo    *clinfo;
95 
96           if ((ifinfo = cdnr_ifname2ifinfo(ifname)) == NULL)
97                     return (CDNR_NULL_HANDLE);
98 
99           if ((clinfo = clname2clinfo(ifinfo, cdnr_name)) == NULL)
100                     return (CDNR_NULL_HANDLE);
101 
102           return (clinfo->handle);
103 }
104 
105 static struct ifinfo *
cdnr_ifname2ifinfo(const char * ifname)106 cdnr_ifname2ifinfo(const char *ifname)
107 {
108           struct ifinfo       *ifinfo;
109           char input_ifname[64];
110 
111           /*
112            * search for an existing input interface
113            */
114           if ((ifinfo = input_ifname2ifinfo(ifname)) != NULL)
115                     return (ifinfo);
116 
117           /*
118            * if there is a corresponding output interface,
119            * create an input interface by prepending "_" to
120            * its name.
121            */
122           if ((ifinfo = ifname2ifinfo(ifname)) == NULL)
123                     return (NULL);
124 
125           input_ifname[0] = '_';
126           strlcpy(input_ifname+1, ifname, sizeof(input_ifname)-1);
127           if (qop_add_if(&ifinfo, input_ifname, 0, &cdnr_qdisc, NULL) != 0) {
128                     LOG(LOG_ERR, errno,
129                         "cdnr_ifname2ifinfo: can't add a input interface %s",
130                         ifname);
131                     return (NULL);
132           }
133           return (ifinfo);
134 }
135 
136 int
qcmd_cdnr_add_element(struct tc_action * rp,const char * ifname,const char * cdnr_name,struct tc_action * action)137 qcmd_cdnr_add_element(struct tc_action *rp, const char *ifname,
138                        const char *cdnr_name, struct tc_action *action)
139 {
140           struct ifinfo                 *ifinfo;
141           struct classinfo    *clinfo;
142           int error;
143 
144           if ((ifinfo = cdnr_ifname2ifinfo(ifname)) == NULL)
145                     return (QOPERR_BADIF);
146 
147           if ((error = qop_cdnr_add_element(&clinfo, cdnr_name, ifinfo,
148                                                     action)) != 0) {
149                     LOG(LOG_ERR, errno, "%s: add element failed!",
150                         qoperror(error));
151                     return (error);
152           }
153 
154           if (rp != NULL) {
155                     rp->tca_code = TCACODE_HANDLE;
156                     rp->tca_handle = clinfo->handle;
157           }
158           return (0);
159 }
160 
161 int
qcmd_cdnr_add_tbmeter(struct tc_action * rp,const char * ifname,const char * cdnr_name,struct tb_profile * profile,struct tc_action * in_action,struct tc_action * out_action)162 qcmd_cdnr_add_tbmeter(struct tc_action *rp, const char *ifname,
163                           const char *cdnr_name,
164                           struct tb_profile *profile,
165                           struct tc_action *in_action,
166                           struct tc_action *out_action)
167 {
168           struct ifinfo                 *ifinfo;
169           struct classinfo    *clinfo;
170           int error;
171 
172           if ((ifinfo = cdnr_ifname2ifinfo(ifname)) == NULL)
173                     return (QOPERR_BADIF);
174 
175           verify_tbprofile(profile, cdnr_name);
176 
177           if ((error = qop_cdnr_add_tbmeter(&clinfo, cdnr_name, ifinfo,
178                                           profile, in_action, out_action)) != 0) {
179                     LOG(LOG_ERR, errno, "%s: add tbmeter failed!",
180                         qoperror(error));
181                     return (error);
182           }
183 
184           if (rp != NULL) {
185                     rp->tca_code = TCACODE_HANDLE;
186                     rp->tca_handle = clinfo->handle;
187           }
188           return (0);
189 }
190 
191 int
qcmd_cdnr_add_trtcm(struct tc_action * rp,const char * ifname,const char * cdnr_name,struct tb_profile * cmtd_profile,struct tb_profile * peak_profile,struct tc_action * green_action,struct tc_action * yellow_action,struct tc_action * red_action,int coloraware)192 qcmd_cdnr_add_trtcm(struct tc_action *rp, const char *ifname,
193                         const char *cdnr_name,
194                         struct tb_profile *cmtd_profile,
195                         struct tb_profile *peak_profile,
196                         struct tc_action *green_action,
197                         struct tc_action *yellow_action,
198                         struct tc_action *red_action, int coloraware)
199 {
200           struct ifinfo                 *ifinfo;
201           struct classinfo    *clinfo;
202           int error;
203 
204           if ((ifinfo = cdnr_ifname2ifinfo(ifname)) == NULL)
205                     return (QOPERR_BADIF);
206 
207           verify_tbprofile(cmtd_profile, cdnr_name);
208           verify_tbprofile(peak_profile, cdnr_name);
209 
210           if ((error = qop_cdnr_add_trtcm(&clinfo, cdnr_name, ifinfo,
211                                 cmtd_profile, peak_profile,
212                                 green_action, yellow_action, red_action,
213                                 coloraware)) != 0) {
214                     LOG(LOG_ERR, errno, "%s: add trtcm failed!",
215                         qoperror(error));
216                     return (error);
217           }
218 
219           if (rp != NULL) {
220                     rp->tca_code = TCACODE_HANDLE;
221                     rp->tca_handle = clinfo->handle;
222           }
223           return (0);
224 }
225 
226 int
qcmd_cdnr_add_tswtcm(struct tc_action * rp,const char * ifname,const char * cdnr_name,const u_int32_t cmtd_rate,const u_int32_t peak_rate,const u_int32_t avg_interval,struct tc_action * green_action,struct tc_action * yellow_action,struct tc_action * red_action)227 qcmd_cdnr_add_tswtcm(struct tc_action *rp, const char *ifname,
228                          const char *cdnr_name, const u_int32_t cmtd_rate,
229                          const u_int32_t peak_rate, const u_int32_t avg_interval,
230                          struct tc_action *green_action,
231                          struct tc_action *yellow_action,
232                          struct tc_action *red_action)
233 {
234           struct ifinfo                 *ifinfo;
235           struct classinfo    *clinfo;
236           int error;
237 
238           if ((ifinfo = cdnr_ifname2ifinfo(ifname)) == NULL)
239                     return (QOPERR_BADIF);
240 
241           if (cmtd_rate > peak_rate) {
242                     LOG(LOG_ERR, 0,
243                         "add tswtcm: cmtd_rate larger than peak_rate!");
244                     return (QOPERR_INVAL);
245           }
246 
247           if ((error = qop_cdnr_add_tswtcm(&clinfo, cdnr_name, ifinfo,
248                                                   cmtd_rate, peak_rate, avg_interval,
249                                                   green_action, yellow_action,
250                                                   red_action)) != 0) {
251                     LOG(LOG_ERR, errno, "%s: add tswtcm failed!",
252                         qoperror(error));
253                     return (error);
254           }
255 
256           if (rp != NULL) {
257                     rp->tca_code = TCACODE_HANDLE;
258                     rp->tca_handle = clinfo->handle;
259           }
260           return (0);
261 }
262 
263 int
qcmd_cdnr_delete(const char * ifname,const char * cdnr_name)264 qcmd_cdnr_delete(const char *ifname, const char *cdnr_name)
265 {
266           struct ifinfo                 *ifinfo;
267           struct classinfo    *clinfo;
268 
269           if ((ifinfo = cdnr_ifname2ifinfo(ifname)) == NULL)
270                     return (QOPERR_BADIF);
271 
272           if ((clinfo = clname2clinfo(ifinfo, cdnr_name)) == NULL)
273                     return (QOPERR_BADCLASS);
274 
275           return qop_delete_cdnr(clinfo);
276 }
277 
278 /*
279  * class operations:
280  *        class structure is used to hold conditioners.
281  *        XXX
282  *        conditioners has dependencies in the reverse order; parent nodes
283  *        refere to child nodes, and thus, a child is created first and
284  *        parents should be removed first.
285  *        qop_add_cdnr() and qop_delete_cdnr() are wrapper functions
286  *        of qop_add_class() and qop_delete_class(), and takes care
287  *        of dependencies.
288  *        1. when adding a conditioner, it is created as a child of a
289  *           dummy root class.  then, the child conditioners are made
290  *           as its children.
291  *        2. when deleting a conditioner, its child conditioners are made
292  *           as children of the dummy root class.  then, the conditioner
293  *           is deleted.
294  */
295 
296 int
qop_add_cdnr(struct classinfo ** rp,const char * cdnr_name,struct ifinfo * ifinfo,struct classinfo ** childlist,void * cdnr_private)297 qop_add_cdnr(struct classinfo **rp, const char *cdnr_name,
298                struct ifinfo *ifinfo, struct classinfo **childlist,
299                void *cdnr_private)
300 {
301           struct classinfo    *clinfo, *root, *cl, *prev;
302           int error;
303 
304           /*
305            * if there is no root cdnr, create one.
306            */
307           if ((root = get_rootclass(ifinfo)) == NULL) {
308                     if ((error = qop_add_class(&root, "cdnr_root",
309                                                      ifinfo, NULL, NULL)) != 0) {
310                               LOG(LOG_ERR, errno,
311                                   "cdnr: %s: can't create dummy root cdnr on %s!",
312                                   qoperror(error), ifinfo->ifname);
313                               return (QOPERR_CLASS);
314                     }
315           }
316 
317           /*
318            * create a class as a child of a root class.
319            */
320           if ((error = qop_add_class(&clinfo, cdnr_name,
321                                            ifinfo, root, cdnr_private)) != 0)
322                     return (error);
323           /*
324            * move child nodes
325            */
326           for (cl = *childlist; cl != NULL; cl = *++childlist) {
327                     if (cl->parent != root) {
328                               /*
329                                * this conditioner already has a non-root parent.
330                                * we can't track down a multi-parent node by a
331                                * tree structure; leave it as it is.
332                                * (we need a mechanism similar to a symbolic link
333                                * in a file system)
334                                */
335                               continue;
336                     }
337                     /* remove this child from the root */
338                     if (root->child == cl)
339                               root->child = cl->sibling;
340                     else for (prev = root->child;
341                                 prev->sibling != NULL; prev = prev->sibling)
342                               if (prev->sibling == cl) {
343                                         prev->sibling = cl->sibling;
344                                         break;
345                               }
346 
347                     /* add as a child */
348                     cl->sibling = clinfo->child;
349                     clinfo->child = cl;
350                     cl->parent = clinfo;
351           }
352 
353           if (rp != NULL)
354                     *rp = clinfo;
355           return (0);
356 }
357 
358 int
qop_delete_cdnr(struct classinfo * clinfo)359 qop_delete_cdnr(struct classinfo *clinfo)
360 {
361           struct classinfo *cl, *root;
362           int error;
363 
364           if ((root = get_rootclass(clinfo->ifinfo)) == NULL) {
365                     LOG(LOG_ERR, 0, "qop_delete_cdnr: no root cdnr!");
366                     return (QOPERR_CLASS);
367           }
368 
369           if (clinfo->parent != root)
370                     return (QOPERR_CLASS_PERM);
371 
372           if ((cl = clinfo->child) != NULL) {
373                     /* change child's parent to root, find the last child */
374                     while (cl->sibling != NULL) {
375                               cl->parent = root;
376                               cl = cl->sibling;
377                     }
378                     cl->parent = root;
379 
380                     /* move children to siblings */
381                     cl->sibling = clinfo->sibling;
382                     clinfo->sibling = cl;
383                     clinfo->child = NULL;
384           }
385 
386           error = qop_delete_class(clinfo);
387 
388           if (error) {
389                     /* ick! restore the class tree */
390                     if (cl != NULL) {
391                               clinfo->child = clinfo->sibling;
392                               clinfo->sibling = cl->sibling;
393                               cl->sibling = NULL;
394                               /* restore parent field */
395                               for (cl = clinfo->child; cl != NULL; cl = cl->sibling)
396                                         cl->parent = clinfo;
397                     }
398           }
399           return (error);
400 }
401 
402 int
qop_cdnr_add_element(struct classinfo ** rp,const char * cdnr_name,struct ifinfo * ifinfo,struct tc_action * action)403 qop_cdnr_add_element(struct classinfo **rp, const char *cdnr_name,
404                          struct ifinfo *ifinfo, struct tc_action *action)
405 {
406           struct classinfo *clinfo, *clist[2];
407           struct cdnrinfo *cdnrinfo = NULL;
408           int error;
409 
410           if (action->tca_code == TCACODE_HANDLE) {
411                     clinfo = clhandle2clinfo(ifinfo, action->tca_handle);
412                     if (clinfo == NULL)
413                               return (QOPERR_BADCLASS);
414                     clist[0] = clinfo;
415                     clist[1] = NULL;
416 #if 1
417                     /*
418                      * if the conditioner referred to doesn't have a name,
419                      * this is called just to add a name to it.
420                      * we can simply add the name to the existing conditioner
421                      * and return it.
422                      */
423                     if (cdnr_name != NULL &&
424                         strcmp(clinfo->clname, "(null)") == 0) {
425                               free(clinfo->clname);
426                               clinfo->clname = strdup(cdnr_name);
427                               if (rp != NULL)
428                                         *rp = clinfo;
429                               return (0);
430                     }
431 #endif
432           } else
433                     clist[0] = NULL;
434 
435           if ((cdnrinfo = calloc(1, sizeof(*cdnrinfo))) == NULL)
436                     return (QOPERR_NOMEM);
437 
438           cdnrinfo->tce_type = TCETYPE_ELEMENT;
439           cdnrinfo->tce_un.element.action = *action;
440 
441           if ((error = qop_add_cdnr(&clinfo, cdnr_name, ifinfo, clist,
442                                           cdnrinfo)) != 0)
443                     goto err_ret;
444 
445           if (rp != NULL)
446                     *rp = clinfo;
447           return (0);
448 
449  err_ret:
450           if (cdnrinfo != NULL)
451                     free(cdnrinfo);
452           return (error);
453 }
454 
455 int
qop_cdnr_add_tbmeter(struct classinfo ** rp,const char * cdnr_name,struct ifinfo * ifinfo,struct tb_profile * profile,struct tc_action * in_action,struct tc_action * out_action)456 qop_cdnr_add_tbmeter(struct classinfo **rp, const char *cdnr_name,
457                          struct ifinfo *ifinfo,
458                          struct tb_profile *profile,
459                          struct tc_action *in_action,
460                          struct tc_action *out_action)
461 {
462           struct classinfo *clinfo, *clist[3];
463           struct cdnrinfo *cdnrinfo = NULL;
464           int n, error;
465 
466           n = 0;
467           if (in_action->tca_code == TCACODE_HANDLE) {
468                     clist[n] = clhandle2clinfo(ifinfo, in_action->tca_handle);
469                     if (clist[n] == NULL)
470                               return (QOPERR_BADCLASS);
471                     n++;
472           }
473           if (out_action->tca_code == TCACODE_HANDLE) {
474                     clist[n] = clhandle2clinfo(ifinfo, out_action->tca_handle);
475                     if (clist[n] == NULL)
476                               return (QOPERR_BADCLASS);
477                     n++;
478           }
479           clist[n] = NULL;
480 
481           if ((cdnrinfo = calloc(1, sizeof(*cdnrinfo))) == NULL)
482                     return (QOPERR_NOMEM);
483 
484           cdnrinfo->tce_type = TCETYPE_TBMETER;
485           cdnrinfo->tce_un.tbmeter.profile = *profile;
486           cdnrinfo->tce_un.tbmeter.in_action = *in_action;
487           cdnrinfo->tce_un.tbmeter.out_action = *out_action;
488 
489           if ((error = qop_add_cdnr(&clinfo, cdnr_name, ifinfo, clist,
490                                            cdnrinfo)) != 0)
491                     goto err_ret;
492 
493           if (rp != NULL)
494                     *rp = clinfo;
495           return (0);
496 
497  err_ret:
498           if (cdnrinfo != NULL)
499                     free(cdnrinfo);
500           return (error);
501 }
502 
503 int
qop_cdnr_modify_tbmeter(struct classinfo * clinfo,struct tb_profile * profile)504 qop_cdnr_modify_tbmeter(struct classinfo *clinfo, struct tb_profile *profile)
505 {
506           struct cdnrinfo *cdnrinfo = clinfo->private;
507 
508           if (cdnrinfo->tce_type != TCETYPE_TBMETER)
509                     return (QOPERR_CLASS_INVAL);
510           cdnrinfo->tce_un.tbmeter.profile = *profile;
511 
512           return qop_modify_class(clinfo, NULL);
513 }
514 
515 int
qop_cdnr_add_trtcm(struct classinfo ** rp,const char * cdnr_name,struct ifinfo * ifinfo,struct tb_profile * cmtd_profile,struct tb_profile * peak_profile,struct tc_action * green_action,struct tc_action * yellow_action,struct tc_action * red_action,int coloraware)516 qop_cdnr_add_trtcm(struct classinfo **rp, const char *cdnr_name,
517                        struct ifinfo *ifinfo,
518                        struct tb_profile *cmtd_profile,
519                        struct tb_profile *peak_profile,
520                        struct tc_action *green_action,
521                        struct tc_action *yellow_action,
522                        struct tc_action *red_action, int coloraware)
523 {
524           struct classinfo *clinfo, *clist[4];
525           struct cdnrinfo *cdnrinfo = NULL;
526           int n, error;
527 
528           n = 0;
529           if (green_action->tca_code == TCACODE_HANDLE) {
530                     clist[n] = clhandle2clinfo(ifinfo, green_action->tca_handle);
531                     if (clist[n] == NULL)
532                               return (QOPERR_BADCLASS);
533                     n++;
534           }
535           if (yellow_action->tca_code == TCACODE_HANDLE) {
536                     clist[n] = clhandle2clinfo(ifinfo, yellow_action->tca_handle);
537                     if (clist[n] == NULL)
538                               return (QOPERR_BADCLASS);
539                     n++;
540           }
541           if (red_action->tca_code == TCACODE_HANDLE) {
542                     clist[n] = clhandle2clinfo(ifinfo, yellow_action->tca_handle);
543                     if (clist[n] == NULL)
544                               return (QOPERR_BADCLASS);
545                     n++;
546           }
547           clist[n] = NULL;
548 
549           if ((cdnrinfo = calloc(1, sizeof(*cdnrinfo))) == NULL)
550                     return (QOPERR_NOMEM);
551 
552           cdnrinfo->tce_type = TCETYPE_TRTCM;
553           cdnrinfo->tce_un.trtcm.cmtd_profile = *cmtd_profile;
554           cdnrinfo->tce_un.trtcm.peak_profile = *peak_profile;
555           cdnrinfo->tce_un.trtcm.green_action = *green_action;
556           cdnrinfo->tce_un.trtcm.yellow_action = *yellow_action;
557           cdnrinfo->tce_un.trtcm.red_action = *red_action;
558           cdnrinfo->tce_un.trtcm.coloraware = coloraware;
559 
560           if ((error = qop_add_cdnr(&clinfo, cdnr_name, ifinfo, clist,
561                                           cdnrinfo)) != 0)
562                     goto err_ret;
563 
564           if (rp != NULL)
565                     *rp = clinfo;
566           return (0);
567 
568  err_ret:
569           if (cdnrinfo != NULL)
570                     free(cdnrinfo);
571           return (error);
572 }
573 
574 int
qop_cdnr_modify_trtcm(struct classinfo * clinfo,struct tb_profile * cmtd_profile,struct tb_profile * peak_profile,int coloraware)575 qop_cdnr_modify_trtcm(struct classinfo *clinfo,
576                           struct tb_profile *cmtd_profile,
577                           struct tb_profile *peak_profile, int coloraware)
578 {
579           struct cdnrinfo *cdnrinfo = clinfo->private;
580 
581           if (cdnrinfo->tce_type != TCETYPE_TRTCM)
582                     return (QOPERR_CLASS_INVAL);
583           cdnrinfo->tce_un.trtcm.cmtd_profile = *cmtd_profile;
584           cdnrinfo->tce_un.trtcm.peak_profile = *peak_profile;
585           cdnrinfo->tce_un.trtcm.coloraware = coloraware;
586 
587           return qop_modify_class(clinfo, NULL);
588 }
589 
590 int
qop_cdnr_add_tswtcm(struct classinfo ** rp,const char * cdnr_name,struct ifinfo * ifinfo,const u_int32_t cmtd_rate,const u_int32_t peak_rate,const u_int32_t avg_interval,struct tc_action * green_action,struct tc_action * yellow_action,struct tc_action * red_action)591 qop_cdnr_add_tswtcm(struct classinfo **rp, const char *cdnr_name,
592                         struct ifinfo *ifinfo, const u_int32_t cmtd_rate,
593                         const u_int32_t peak_rate, const u_int32_t avg_interval,
594                         struct tc_action *green_action,
595                         struct tc_action *yellow_action,
596                         struct tc_action *red_action)
597 {
598           struct classinfo *clinfo, *clist[4];
599           struct cdnrinfo *cdnrinfo = NULL;
600           int n, error;
601 
602           n = 0;
603           if (green_action->tca_code == TCACODE_HANDLE) {
604                     clist[n] = clhandle2clinfo(ifinfo, green_action->tca_handle);
605                     if (clist[n] == NULL)
606                               return (QOPERR_BADCLASS);
607                     n++;
608           }
609           if (yellow_action->tca_code == TCACODE_HANDLE) {
610                     clist[n] = clhandle2clinfo(ifinfo, yellow_action->tca_handle);
611                     if (clist[n] == NULL)
612                               return (QOPERR_BADCLASS);
613                     n++;
614           }
615           if (red_action->tca_code == TCACODE_HANDLE) {
616                     clist[n] = clhandle2clinfo(ifinfo, yellow_action->tca_handle);
617                     if (clist[n] == NULL)
618                               return (QOPERR_BADCLASS);
619                     n++;
620           }
621           clist[n] = NULL;
622 
623           if ((cdnrinfo = calloc(1, sizeof(*cdnrinfo))) == NULL)
624                     return (QOPERR_NOMEM);
625 
626           cdnrinfo->tce_type = TCETYPE_TSWTCM;
627           cdnrinfo->tce_un.tswtcm.cmtd_rate = cmtd_rate;
628           cdnrinfo->tce_un.tswtcm.peak_rate = peak_rate;
629           cdnrinfo->tce_un.tswtcm.avg_interval = avg_interval;
630           cdnrinfo->tce_un.tswtcm.green_action = *green_action;
631           cdnrinfo->tce_un.tswtcm.yellow_action = *yellow_action;
632           cdnrinfo->tce_un.tswtcm.red_action = *red_action;
633 
634           if ((error = qop_add_cdnr(&clinfo, cdnr_name, ifinfo, clist,
635                                           cdnrinfo)) != 0)
636                     goto err_ret;
637 
638           if (rp != NULL)
639                     *rp = clinfo;
640           return (0);
641 
642  err_ret:
643           if (cdnrinfo != NULL)
644                     free(cdnrinfo);
645           return (error);
646 }
647 
648 int
qop_cdnr_modify_tswtcm(struct classinfo * clinfo,const u_int32_t cmtd_rate,const u_int32_t peak_rate,const u_int32_t avg_interval)649 qop_cdnr_modify_tswtcm(struct classinfo *clinfo, const u_int32_t cmtd_rate,
650                            const u_int32_t peak_rate, const u_int32_t avg_interval)
651 {
652           struct cdnrinfo *cdnrinfo = clinfo->private;
653 
654           if (cdnrinfo->tce_type != TCETYPE_TSWTCM)
655                     return (QOPERR_CLASS_INVAL);
656           cdnrinfo->tce_un.tswtcm.cmtd_rate = cmtd_rate;
657           cdnrinfo->tce_un.tswtcm.peak_rate = peak_rate;
658           cdnrinfo->tce_un.tswtcm.avg_interval = avg_interval;
659 
660           return qop_modify_class(clinfo, NULL);
661 }
662 
663 /*
664  *  system call interfaces for qdisc_ops
665  */
666 static int
cdnr_attach(struct ifinfo * ifinfo)667 cdnr_attach(struct ifinfo *ifinfo)
668 {
669           struct cdnr_interface iface;
670 
671           if (cdnr_fd < 0 &&
672               (cdnr_fd = open(CDNR_DEVICE, O_RDWR)) < 0 &&
673               (cdnr_fd = open_module(CDNR_DEVICE, O_RDWR)) < 0) {
674                     LOG(LOG_ERR, errno, "CDNR open");
675                     return (QOPERR_SYSCALL);
676           }
677 
678           cdnr_refcount++;
679           memset(&iface, 0, sizeof(iface));
680           strncpy(iface.cdnr_ifname, ifinfo->ifname+1, IFNAMSIZ);
681 
682           if (ioctl(cdnr_fd, CDNR_IF_ATTACH, &iface) < 0)
683                     return (QOPERR_SYSCALL);
684 #if 1
685           LOG(LOG_INFO, 0, "conditioner attached to %s", iface.cdnr_ifname);
686 #endif
687           return (0);
688 }
689 
690 static int
cdnr_detach(struct ifinfo * ifinfo)691 cdnr_detach(struct ifinfo *ifinfo)
692 {
693           struct cdnr_interface iface;
694 
695           memset(&iface, 0, sizeof(iface));
696           strncpy(iface.cdnr_ifname, ifinfo->ifname+1, IFNAMSIZ);
697 
698           if (ioctl(cdnr_fd, CDNR_IF_DETACH, &iface) < 0)
699                     return (QOPERR_SYSCALL);
700 
701           if (--cdnr_refcount == 0) {
702                     close(cdnr_fd);
703                     cdnr_fd = -1;
704           }
705           return (0);
706 }
707 
708 static int
cdnr_enable(struct ifinfo * ifinfo)709 cdnr_enable(struct ifinfo *ifinfo)
710 {
711           struct cdnr_interface iface;
712 
713           memset(&iface, 0, sizeof(iface));
714           strncpy(iface.cdnr_ifname, ifinfo->ifname+1, IFNAMSIZ);
715 
716           if (ioctl(cdnr_fd, CDNR_ENABLE, &iface) < 0)
717                     return (QOPERR_SYSCALL);
718           return (0);
719 }
720 
721 static int
cdnr_disable(struct ifinfo * ifinfo)722 cdnr_disable(struct ifinfo *ifinfo)
723 {
724           struct cdnr_interface iface;
725 
726           memset(&iface, 0, sizeof(iface));
727           strncpy(iface.cdnr_ifname, ifinfo->ifname+1, IFNAMSIZ);
728 
729           if (ioctl(cdnr_fd, CDNR_DISABLE, &iface) < 0)
730                     return (QOPERR_SYSCALL);
731           return (0);
732 }
733 
734 static int
cdnr_add_class(struct classinfo * clinfo)735 cdnr_add_class(struct classinfo *clinfo)
736 {
737           struct cdnr_add_element element_add;
738           struct cdnr_add_tbmeter tbmeter_add;
739           struct cdnr_add_trtcm   trtcm_add;
740           struct cdnr_add_tswtcm  tswtcm_add;
741           struct cdnrinfo *cdnrinfo;
742 
743           cdnrinfo = clinfo->private;
744 
745           /* root class is a dummy class */
746           if (clinfo->parent == NULL) {
747                     clinfo->handle = 0;
748                     return (0);
749           }
750 
751           switch (cdnrinfo->tce_type) {
752           case TCETYPE_ELEMENT:
753                     memset(&element_add, 0, sizeof(element_add));
754                     strncpy(element_add.iface.cdnr_ifname,
755                               clinfo->ifinfo->ifname+1, IFNAMSIZ);
756                     element_add.action = cdnrinfo->tce_un.element.action;
757                     if (ioctl(cdnr_fd, CDNR_ADD_ELEM, &element_add) < 0) {
758                               clinfo->handle = CDNR_NULL_HANDLE;
759                               return (QOPERR_SYSCALL);
760                     }
761                     clinfo->handle = element_add.cdnr_handle;
762                     break;
763 
764           case TCETYPE_TBMETER:
765                     memset(&tbmeter_add, 0, sizeof(tbmeter_add));
766                     strncpy(tbmeter_add.iface.cdnr_ifname,
767                               clinfo->ifinfo->ifname+1, IFNAMSIZ);
768                     tbmeter_add.profile = cdnrinfo->tce_un.tbmeter.profile;
769                     tbmeter_add.in_action = cdnrinfo->tce_un.tbmeter.in_action;
770                     tbmeter_add.out_action = cdnrinfo->tce_un.tbmeter.out_action;
771                     if (ioctl(cdnr_fd, CDNR_ADD_TBM, &tbmeter_add) < 0) {
772                               clinfo->handle = CDNR_NULL_HANDLE;
773                               return (QOPERR_SYSCALL);
774                     }
775                     clinfo->handle = tbmeter_add.cdnr_handle;
776                     break;
777 
778           case TCETYPE_TRTCM:
779                     memset(&trtcm_add, 0, sizeof(trtcm_add));
780                     strncpy(trtcm_add.iface.cdnr_ifname,
781                               clinfo->ifinfo->ifname+1, IFNAMSIZ);
782                     trtcm_add.cmtd_profile = cdnrinfo->tce_un.trtcm.cmtd_profile;
783                     trtcm_add.peak_profile = cdnrinfo->tce_un.trtcm.peak_profile;
784                     trtcm_add.green_action = cdnrinfo->tce_un.trtcm.green_action;
785                     trtcm_add.yellow_action = cdnrinfo->tce_un.trtcm.yellow_action;
786                     trtcm_add.red_action = cdnrinfo->tce_un.trtcm.red_action;
787                     trtcm_add.coloraware = cdnrinfo->tce_un.trtcm.coloraware;
788                     if (ioctl(cdnr_fd, CDNR_ADD_TCM, &trtcm_add) < 0) {
789                               clinfo->handle = CDNR_NULL_HANDLE;
790                               return (QOPERR_SYSCALL);
791                     }
792                     clinfo->handle = trtcm_add.cdnr_handle;
793                     break;
794 
795           case TCETYPE_TSWTCM:
796                     memset(&tswtcm_add, 0, sizeof(tswtcm_add));
797                     strncpy(tswtcm_add.iface.cdnr_ifname,
798                               clinfo->ifinfo->ifname+1, IFNAMSIZ);
799                     tswtcm_add.cmtd_rate = cdnrinfo->tce_un.tswtcm.cmtd_rate;
800                     tswtcm_add.peak_rate = cdnrinfo->tce_un.tswtcm.peak_rate;
801                     tswtcm_add.avg_interval = cdnrinfo->tce_un.tswtcm.avg_interval;
802                     tswtcm_add.green_action = cdnrinfo->tce_un.tswtcm.green_action;
803                     tswtcm_add.yellow_action = cdnrinfo->tce_un.tswtcm.yellow_action;
804                     tswtcm_add.red_action = cdnrinfo->tce_un.tswtcm.red_action;
805                     if (ioctl(cdnr_fd, CDNR_ADD_TSW, &tswtcm_add) < 0) {
806                               clinfo->handle = CDNR_NULL_HANDLE;
807                               return (QOPERR_SYSCALL);
808                     }
809                     clinfo->handle = tswtcm_add.cdnr_handle;
810                     break;
811 
812           default:
813                     return (QOPERR_CLASS_INVAL);
814           }
815           return (0);
816 }
817 
818 static int
cdnr_modify_class(struct classinfo * clinfo,void * arg)819 cdnr_modify_class(struct classinfo *clinfo, void *arg)
820 {
821           struct cdnr_modify_tbmeter tbmeter_modify;
822           struct cdnr_modify_trtcm   trtcm_modify;
823           struct cdnr_modify_tswtcm  tswtcm_modify;
824           struct cdnrinfo *cdnrinfo;
825 
826           cdnrinfo = clinfo->private;
827 
828           switch (cdnrinfo->tce_type) {
829           case TCETYPE_TBMETER:
830                     memset(&tbmeter_modify, 0, sizeof(tbmeter_modify));
831                     strncpy(tbmeter_modify.iface.cdnr_ifname,
832                               clinfo->ifinfo->ifname+1, IFNAMSIZ);
833                     tbmeter_modify.cdnr_handle = clinfo->handle;
834                     tbmeter_modify.profile = cdnrinfo->tce_un.tbmeter.profile;
835                     if (ioctl(cdnr_fd, CDNR_MOD_TBM, &tbmeter_modify) < 0)
836                               return (QOPERR_SYSCALL);
837                     break;
838 
839           case TCETYPE_TRTCM:
840                     memset(&trtcm_modify, 0, sizeof(trtcm_modify));
841                     strncpy(trtcm_modify.iface.cdnr_ifname,
842                               clinfo->ifinfo->ifname+1, IFNAMSIZ);
843                     trtcm_modify.cdnr_handle = clinfo->handle;
844                     trtcm_modify.cmtd_profile =
845                               cdnrinfo->tce_un.trtcm.cmtd_profile;
846                     trtcm_modify.peak_profile =
847                               cdnrinfo->tce_un.trtcm.peak_profile;
848                     trtcm_modify.coloraware = cdnrinfo->tce_un.trtcm.coloraware;
849                     if (ioctl(cdnr_fd, CDNR_MOD_TCM, &trtcm_modify) < 0)
850                               return (QOPERR_SYSCALL);
851                     break;
852 
853           case TCETYPE_TSWTCM:
854                     memset(&tswtcm_modify, 0, sizeof(tswtcm_modify));
855                     strncpy(tswtcm_modify.iface.cdnr_ifname,
856                               clinfo->ifinfo->ifname+1, IFNAMSIZ);
857                     tswtcm_modify.cdnr_handle = clinfo->handle;
858                     tswtcm_modify.cmtd_rate = cdnrinfo->tce_un.tswtcm.cmtd_rate;
859                     tswtcm_modify.peak_rate = cdnrinfo->tce_un.tswtcm.peak_rate;
860                     tswtcm_modify.avg_interval = cdnrinfo->tce_un.tswtcm.avg_interval;
861                     if (ioctl(cdnr_fd, CDNR_MOD_TSW, &tswtcm_modify) < 0)
862                               return (QOPERR_SYSCALL);
863                     break;
864 
865           default:
866                     return (QOPERR_CLASS_INVAL);
867           }
868           return (0);
869 }
870 
871 static int
cdnr_delete_class(struct classinfo * clinfo)872 cdnr_delete_class(struct classinfo *clinfo)
873 {
874           struct cdnr_delete_element element_delete;
875 
876           if (clinfo->handle == CDNR_NULL_HANDLE)
877                     return (0);
878 
879           memset(&element_delete, 0, sizeof(element_delete));
880           strncpy(element_delete.iface.cdnr_ifname, clinfo->ifinfo->ifname+1,
881                     IFNAMSIZ);
882           element_delete.cdnr_handle = clinfo->handle;
883 
884           if (ioctl(cdnr_fd, CDNR_DEL_ELEM, &element_delete) < 0)
885                     return (QOPERR_SYSCALL);
886           return (0);
887 }
888 
889 static int
cdnr_add_filter(struct fltrinfo * fltrinfo)890 cdnr_add_filter(struct fltrinfo *fltrinfo)
891 {
892           struct cdnr_add_filter fltr_add;
893 
894           memset(&fltr_add, 0, sizeof(fltr_add));
895           strncpy(fltr_add.iface.cdnr_ifname,
896                     fltrinfo->clinfo->ifinfo->ifname+1, IFNAMSIZ);
897           fltr_add.cdnr_handle = fltrinfo->clinfo->handle;
898           fltr_add.filter = fltrinfo->fltr;
899 
900           if (ioctl(cdnr_fd, CDNR_ADD_FILTER, &fltr_add) < 0)
901                     return (QOPERR_SYSCALL);
902           fltrinfo->handle = fltr_add.filter_handle;
903           return (0);
904 }
905 
906 static int
cdnr_delete_filter(struct fltrinfo * fltrinfo)907 cdnr_delete_filter(struct fltrinfo *fltrinfo)
908 {
909           struct cdnr_delete_filter fltr_del;
910 
911           memset(&fltr_del, 0, sizeof(fltr_del));
912           strncpy(fltr_del.iface.cdnr_ifname,
913                     fltrinfo->clinfo->ifinfo->ifname+1, IFNAMSIZ);
914           fltr_del.filter_handle = fltrinfo->handle;
915 
916           if (ioctl(cdnr_fd, CDNR_DEL_FILTER, &fltr_del) < 0)
917                     return (QOPERR_SYSCALL);
918           return (0);
919 }
920 
921 
922 static int
verify_tbprofile(struct tb_profile * profile,const char * cdnr_name)923 verify_tbprofile(struct tb_profile *profile, const char *cdnr_name)
924 {
925           if (profile->depth < 1500) {
926                     LOG(LOG_WARNING, 0,
927                         "warning: token bucket depth for %s is too small (%d)",
928                         cdnr_name, profile->depth);
929                     return (-1);
930           }
931           return (0);
932 }
933 
934