1 /*
2  * Copyright (c) 2005 J. Martin Petersen
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  *    - Redistributions of source code must retain the above copyright
10  *      notice, this list of conditions and the following disclaimer.
11  *    - Redistributions in binary form must reproduce the above
12  *      copyright notice, this list of conditions and the following
13  *      disclaimer in the documentation and/or other materials provided
14  *      with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  *
29  */
30 
31 /*
32  * Get current altq statistics from pf and return them in symon_buf as
33  * sent_bytes : sent_packets : drop_bytes : drop_packets
34  */
35 
36 #include "conf.h"
37 
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <sys/ioctl.h>
41 #include <net/if.h>
42 #include <netinet/in.h>
43 #include <err.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 
47 #ifdef HAS_PFVAR_H
48 #include <net/pfvar.h>
49 #include <altq/altq.h>
50 #include <altq/altq_cbq.h>
51 #include <altq/altq_priq.h>
52 #include <altq/altq_hfsc.h>
53 #endif
54 
55 #include <errno.h>
56 #include <string.h>
57 #include <fcntl.h>
58 
59 #include "error.h"
60 #include "symon.h"
61 #include "xmalloc.h"
62 
63 #ifndef HAS_PFVAR_H
64 void
privinit_pfq()65 privinit_pfq()
66 {
67     fatal("pf support not available");
68 }
69 void
init_pfq(struct stream * st)70 init_pfq(struct stream *st)
71 {
72     fatal("pf support not available");
73 }
74 void
gets_pfq()75 gets_pfq()
76 {
77     fatal("pf support not available");
78 }
79 
80 int
get_pfq(char * b,int l,struct stream * st)81 get_pfq(char *b, int l, struct stream *st)
82 {
83   fatal("pf support not available");
84   return 0;
85 }
86 
87 #else
88 
89 union class_stats {
90     class_stats_t cbq;
91 #ifdef HAS_ALTQ_BASIC_STATS
92     struct priq_basic_class_stats priq;
93     struct hfsc_basic_class_stats hfsc;
94 #else
95     struct priq_classstats priq;
96     struct hfsc_classstats hfsc;
97 #endif
98 };
99 
100 /*
101  * We do not use the data structures from altq/altq_{cbq|hfsc|priq}.h as they
102  * are overly complex. For now we only grab the interesting stuff.
103  */
104 
105 struct altq_stats {
106     char qname[PF_QNAME_SIZE + IFNAMSIZ + 1];
107     u_int64_t sent_bytes;
108     u_int64_t sent_packets;
109     u_int64_t drop_bytes;
110     u_int64_t drop_packets;
111 };
112 
113 static struct altq_stats *pfq_stats = NULL;
114 static int pfq_cur = 0;
115 static int pfq_max = 0;
116 int pfq_dev = -1;
117 
118 void
privinit_pfq()119 privinit_pfq()
120 {
121     if ((pfq_dev = open("/dev/pf", O_RDONLY)) == -1) {
122         warning("pfq: could not open \"/dev/pf\", %.200s", strerror(errno));
123     }
124 }
125 
126 void
init_pfq(struct stream * st)127 init_pfq(struct stream *st)
128 {
129     if (pfq_dev == -1) {
130         privinit_pfq();
131     }
132 
133     info("started module pfq(%.200s)", st->arg);
134 }
135 
136 void
gets_pfq()137 gets_pfq()
138 {
139     struct pfioc_altq qs;
140     struct pfioc_qstats stats;
141     union class_stats q;
142     unsigned int nqs;
143     unsigned int i;
144 
145     bzero(&qs, sizeof(qs));
146     bzero(&stats, sizeof(stats));
147     bzero(&q, sizeof(q));
148 
149     if (ioctl(pfq_dev, DIOCGETALTQS, &qs)) {
150         fatal("pfq: DIOCGETALTQS failed");
151     }
152     nqs = qs.nr;
153 
154     /* Allocate memory for info for the nqs queues */
155     if (nqs > pfq_max) {
156         if (pfq_stats) {
157             xfree(pfq_stats);
158         }
159 
160         pfq_max = 2 * nqs;
161 
162         if (pfq_max > SYMON_MAX_DOBJECTS) {
163             fatal("%s:%d: dynamic object limit (%d) exceeded for pf queue structures",
164                   __FILE__, __LINE__, SYMON_MAX_DOBJECTS);
165         }
166 
167         pfq_stats = xmalloc(pfq_max * sizeof(struct altq_stats));
168     }
169 
170     pfq_cur = 0;
171 
172     /* Loop through the queues, copy info */
173     for (i = 0; i < nqs; i++) {
174         qs.nr = i;
175         if (ioctl(pfq_dev, DIOCGETALTQ, &qs)) {
176             fatal("pfq: DIOCGETALTQ failed");
177         }
178 
179         /* only process the non-empty queues */
180         if (qs.altq.qid > 0) {
181             stats.nr = qs.nr;
182             stats.ticket = qs.ticket;
183             stats.buf = &q;
184             stats.nbytes = sizeof(q);
185 
186             if (ioctl(pfq_dev, DIOCGETQSTATS, &stats)) {
187                 fatal("pfq: DIOCGETQSTATS failed");
188             }
189 
190             /* We're now ready to copy the data we want. */
191             snprintf(pfq_stats[pfq_cur].qname, sizeof(pfq_stats[0].qname),
192                      "%s/%s", qs.altq.ifname, qs.altq.qname);
193 
194             switch (qs.altq.scheduler) {
195             case ALTQT_CBQ:
196                 pfq_stats[pfq_cur].sent_bytes = q.cbq.xmit_cnt.bytes;
197                 pfq_stats[pfq_cur].sent_packets = q.cbq.xmit_cnt.packets;
198                 pfq_stats[pfq_cur].drop_bytes = q.cbq.drop_cnt.bytes;
199                 pfq_stats[pfq_cur].drop_packets = q.cbq.drop_cnt.packets;
200                 break;
201 
202             case ALTQT_PRIQ:
203                 pfq_stats[pfq_cur].sent_bytes = q.priq.xmitcnt.bytes;
204                 pfq_stats[pfq_cur].sent_packets = q.priq.xmitcnt.packets;
205                 pfq_stats[pfq_cur].drop_bytes = q.priq.dropcnt.bytes;
206                 pfq_stats[pfq_cur].drop_packets = q.priq.dropcnt.packets;
207                 break;
208 
209             case ALTQT_HFSC:
210                 pfq_stats[pfq_cur].sent_bytes = q.hfsc.xmit_cnt.bytes;
211                 pfq_stats[pfq_cur].sent_packets = q.hfsc.xmit_cnt.packets;
212                 pfq_stats[pfq_cur].drop_bytes = q.hfsc.drop_cnt.bytes;
213                 pfq_stats[pfq_cur].drop_packets = q.hfsc.drop_cnt.packets;
214                 break;
215 
216             default:
217                 warning("pfq: unknown altq scheduler type encountered");
218                 break;
219             }
220             pfq_cur++;
221         }
222     }
223 }
224 
225 int
get_pfq(char * symon_buf,int maxlen,struct stream * st)226 get_pfq(char *symon_buf, int maxlen, struct stream *st)
227 {
228     unsigned int i;
229 
230     for (i = 0; i < pfq_cur; i++) {
231         if (strncmp(pfq_stats[i].qname, st->arg, sizeof(pfq_stats[0].qname)) == 0) {
232             return snpack(symon_buf, maxlen, st->arg, MT_PFQ,
233                           pfq_stats[i].sent_bytes,
234                           pfq_stats[i].sent_packets,
235                           pfq_stats[i].drop_bytes,
236                           pfq_stats[i].drop_packets
237                 );
238         }
239     }
240 
241     return 0;
242 }
243 #endif
244