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