1 /*
2 * Copyright (c) 2001-2003
3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4 * All rights reserved.
5 *
6 * Author: Harti Brandt <harti@freebsd.org>
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $Begemot: bsnmp/snmp_mibII/mibII_tcp.c,v 1.7 2005/05/23 09:03:42 brandt_h Exp $
30 *
31 * tcp
32 */
33 #include "mibII.h"
34 #include "mibII_oid.h"
35 #include <sys/socketvar.h>
36 #include <netinet/in_pcb.h>
37 #include <netinet/tcp.h>
38 #include <netinet/tcp_var.h>
39 #include <netinet/tcp_timer.h>
40 #include <netinet/tcp_fsm.h>
41
42 struct tcp_index {
43 struct asn_oid index;
44 struct xtcpcb *tp;
45 };
46
47 static uint64_t tcp_tick;
48 static uint64_t tcp_stats_tick;
49 static struct tcpstat tcpstat;
50 static uint64_t tcps_states[TCP_NSTATES];
51 static struct xinpgen *xinpgen;
52 static size_t xinpgen_len;
53 static u_int tcp_total;
54
55 static u_int oidnum;
56 static struct tcp_index *tcpoids;
57
58 static int
tcp_compare(const void * p1,const void * p2)59 tcp_compare(const void *p1, const void *p2)
60 {
61 const struct tcp_index *t1 = p1;
62 const struct tcp_index *t2 = p2;
63
64 return (asn_compare_oid(&t1->index, &t2->index));
65 }
66
67 static int
fetch_tcp_stats(void)68 fetch_tcp_stats(void)
69 {
70 size_t len;
71
72 len = sizeof(tcpstat);
73 if (sysctlbyname("net.inet.tcp.stats", &tcpstat, &len, NULL, 0) == -1) {
74 syslog(LOG_ERR, "net.inet.tcp.stats: %m");
75 return (-1);
76 }
77 if (len != sizeof(tcpstat)) {
78 syslog(LOG_ERR, "net.inet.tcp.stats: wrong size");
79 return (-1);
80 }
81
82 len = sizeof(tcps_states);
83 if (sysctlbyname("net.inet.tcp.states", &tcps_states, &len, NULL,
84 0) == -1) {
85 syslog(LOG_ERR, "net.inet.tcp.states: %m");
86 return (-1);
87 }
88 if (len != sizeof(tcps_states)) {
89 syslog(LOG_ERR, "net.inet.tcp.states: wrong size");
90 return (-1);
91 }
92
93 tcp_stats_tick = get_ticks();
94
95 return (0);
96 }
97
98 static int
fetch_tcp(void)99 fetch_tcp(void)
100 {
101 size_t len;
102 struct xinpgen *ptr;
103 struct xtcpcb *tp;
104 struct tcp_index *oid;
105 in_addr_t inaddr;
106
107 len = 0;
108 if (sysctlbyname("net.inet.tcp.pcblist", NULL, &len, NULL, 0) == -1) {
109 syslog(LOG_ERR, "net.inet.tcp.pcblist: %m");
110 return (-1);
111 }
112 if (len > xinpgen_len) {
113 if ((ptr = realloc(xinpgen, len)) == NULL) {
114 syslog(LOG_ERR, "%zu: %m", len);
115 return (-1);
116 }
117 xinpgen = ptr;
118 xinpgen_len = len;
119 }
120 if (sysctlbyname("net.inet.tcp.pcblist", xinpgen, &len, NULL, 0) == -1) {
121 syslog(LOG_ERR, "net.inet.tcp.pcblist: %m");
122 return (-1);
123 }
124
125 tcp_tick = get_ticks();
126
127 tcp_total = 0;
128 for (ptr = (struct xinpgen *)(void *)((char *)xinpgen + xinpgen->xig_len);
129 ptr->xig_len > sizeof(struct xinpgen);
130 ptr = (struct xinpgen *)(void *)((char *)ptr + ptr->xig_len)) {
131 tp = (struct xtcpcb *)ptr;
132 if (tp->xt_inp.inp_gencnt > xinpgen->xig_gen ||
133 (tp->xt_inp.inp_vflag & (INP_IPV4|INP_IPV6)) == 0)
134 continue;
135
136 if (tp->xt_inp.inp_vflag & INP_IPV4)
137 tcp_total++;
138 }
139
140 if (oidnum < tcp_total) {
141 oid = realloc(tcpoids, tcp_total * sizeof(tcpoids[0]));
142 if (oid == NULL) {
143 free(tcpoids);
144 oidnum = 0;
145 return (0);
146 }
147 tcpoids = oid;
148 oidnum = tcp_total;
149 }
150
151 oid = tcpoids;
152 for (ptr = (struct xinpgen *)(void *)((char *)xinpgen + xinpgen->xig_len);
153 ptr->xig_len > sizeof(struct xinpgen);
154 ptr = (struct xinpgen *)(void *)((char *)ptr + ptr->xig_len)) {
155 tp = (struct xtcpcb *)ptr;
156 if (tp->xt_inp.inp_gencnt > xinpgen->xig_gen ||
157 (tp->xt_inp.inp_vflag & INP_IPV4) == 0)
158 continue;
159 oid->tp = tp;
160 oid->index.len = 10;
161 inaddr = ntohl(tp->xt_inp.inp_laddr.s_addr);
162 oid->index.subs[0] = (inaddr >> 24) & 0xff;
163 oid->index.subs[1] = (inaddr >> 16) & 0xff;
164 oid->index.subs[2] = (inaddr >> 8) & 0xff;
165 oid->index.subs[3] = (inaddr >> 0) & 0xff;
166 oid->index.subs[4] = ntohs(tp->xt_inp.inp_lport);
167 inaddr = ntohl(tp->xt_inp.inp_faddr.s_addr);
168 oid->index.subs[5] = (inaddr >> 24) & 0xff;
169 oid->index.subs[6] = (inaddr >> 16) & 0xff;
170 oid->index.subs[7] = (inaddr >> 8) & 0xff;
171 oid->index.subs[8] = (inaddr >> 0) & 0xff;
172 oid->index.subs[9] = ntohs(tp->xt_inp.inp_fport);
173 oid++;
174 }
175
176 qsort(tcpoids, tcp_total, sizeof(tcpoids[0]), tcp_compare);
177
178 return (0);
179 }
180
181 /*
182 * Scalars
183 */
184 int
op_tcp(struct snmp_context * ctx __unused,struct snmp_value * value,u_int sub,u_int iidx __unused,enum snmp_op op)185 op_tcp(struct snmp_context *ctx __unused, struct snmp_value *value,
186 u_int sub, u_int iidx __unused, enum snmp_op op)
187 {
188 switch (op) {
189
190 case SNMP_OP_GETNEXT:
191 abort();
192
193 case SNMP_OP_GET:
194 break;
195
196 case SNMP_OP_SET:
197 return (SNMP_ERR_NOT_WRITEABLE);
198
199 case SNMP_OP_ROLLBACK:
200 case SNMP_OP_COMMIT:
201 abort();
202 }
203
204 if (tcp_stats_tick < this_tick)
205 if (fetch_tcp_stats() == -1)
206 return (SNMP_ERR_GENERR);
207
208 switch (value->var.subs[sub - 1]) {
209
210 case LEAF_tcpRtoAlgorithm:
211 value->v.integer = 4; /* Van Jacobson */
212 break;
213
214 #define hz clockinfo.hz
215
216 case LEAF_tcpRtoMin:
217 value->v.integer = 1000 * TCPTV_MIN / hz;
218 break;
219
220 case LEAF_tcpRtoMax:
221 value->v.integer = 1000 * TCPTV_REXMTMAX / hz;
222 break;
223 #undef hz
224
225 case LEAF_tcpMaxConn:
226 value->v.integer = -1;
227 break;
228
229 case LEAF_tcpActiveOpens:
230 value->v.uint32 = tcpstat.tcps_connattempt;
231 break;
232
233 case LEAF_tcpPassiveOpens:
234 value->v.uint32 = tcpstat.tcps_accepts;
235 break;
236
237 case LEAF_tcpAttemptFails:
238 value->v.uint32 = tcpstat.tcps_conndrops;
239 break;
240
241 case LEAF_tcpEstabResets:
242 value->v.uint32 = tcpstat.tcps_drops;
243 break;
244
245 case LEAF_tcpCurrEstab:
246 value->v.uint32 = tcps_states[TCPS_ESTABLISHED] +
247 tcps_states[TCPS_CLOSE_WAIT];
248 break;
249
250 case LEAF_tcpInSegs:
251 value->v.uint32 = tcpstat.tcps_rcvtotal;
252 break;
253
254 case LEAF_tcpOutSegs:
255 value->v.uint32 = tcpstat.tcps_sndtotal -
256 tcpstat.tcps_sndrexmitpack;
257 break;
258
259 case LEAF_tcpRetransSegs:
260 value->v.uint32 = tcpstat.tcps_sndrexmitpack;
261 break;
262
263 case LEAF_tcpInErrs:
264 value->v.uint32 = tcpstat.tcps_rcvbadsum +
265 tcpstat.tcps_rcvbadoff +
266 tcpstat.tcps_rcvshort;
267 break;
268 }
269 return (SNMP_ERR_NOERROR);
270 }
271
272 int
op_tcpconn(struct snmp_context * ctx __unused,struct snmp_value * value,u_int sub,u_int iidx __unused,enum snmp_op op)273 op_tcpconn(struct snmp_context *ctx __unused, struct snmp_value *value,
274 u_int sub, u_int iidx __unused, enum snmp_op op)
275 {
276 u_int i;
277
278 if (tcp_tick < this_tick)
279 if (fetch_tcp() == -1)
280 return (SNMP_ERR_GENERR);
281
282 switch (op) {
283
284 case SNMP_OP_GETNEXT:
285 for (i = 0; i < tcp_total; i++)
286 if (index_compare(&value->var, sub, &tcpoids[i].index) < 0)
287 break;
288 if (i == tcp_total)
289 return (SNMP_ERR_NOSUCHNAME);
290 index_append(&value->var, sub, &tcpoids[i].index);
291 break;
292
293 case SNMP_OP_GET:
294 for (i = 0; i < tcp_total; i++)
295 if (index_compare(&value->var, sub, &tcpoids[i].index) == 0)
296 break;
297 if (i == tcp_total)
298 return (SNMP_ERR_NOSUCHNAME);
299 break;
300
301 case SNMP_OP_SET:
302 return (SNMP_ERR_NOT_WRITEABLE);
303
304 case SNMP_OP_ROLLBACK:
305 case SNMP_OP_COMMIT:
306 default:
307 abort();
308 }
309
310 switch (value->var.subs[sub - 1]) {
311
312 case LEAF_tcpConnState:
313 switch (tcpoids[i].tp->t_state) {
314
315 case TCPS_CLOSED:
316 value->v.integer = 1;
317 break;
318 case TCPS_LISTEN:
319 value->v.integer = 2;
320 break;
321 case TCPS_SYN_SENT:
322 value->v.integer = 3;
323 break;
324 case TCPS_SYN_RECEIVED:
325 value->v.integer = 4;
326 break;
327 case TCPS_ESTABLISHED:
328 value->v.integer = 5;
329 break;
330 case TCPS_CLOSE_WAIT:
331 value->v.integer = 8;
332 break;
333 case TCPS_FIN_WAIT_1:
334 value->v.integer = 6;
335 break;
336 case TCPS_CLOSING:
337 value->v.integer = 10;
338 break;
339 case TCPS_LAST_ACK:
340 value->v.integer = 9;
341 break;
342 case TCPS_FIN_WAIT_2:
343 value->v.integer = 7;
344 break;
345 case TCPS_TIME_WAIT:
346 value->v.integer = 11;
347 break;
348 default:
349 value->v.integer = 0;
350 break;
351 }
352 break;
353
354 case LEAF_tcpConnLocalAddress:
355 value->v.ipaddress[0] = tcpoids[i].index.subs[0];
356 value->v.ipaddress[1] = tcpoids[i].index.subs[1];
357 value->v.ipaddress[2] = tcpoids[i].index.subs[2];
358 value->v.ipaddress[3] = tcpoids[i].index.subs[3];
359 break;
360
361 case LEAF_tcpConnLocalPort:
362 value->v.integer = tcpoids[i].index.subs[4];
363 break;
364
365 case LEAF_tcpConnRemAddress:
366 value->v.ipaddress[0] = tcpoids[i].index.subs[5];
367 value->v.ipaddress[1] = tcpoids[i].index.subs[6];
368 value->v.ipaddress[2] = tcpoids[i].index.subs[7];
369 value->v.ipaddress[3] = tcpoids[i].index.subs[8];
370 break;
371
372 case LEAF_tcpConnRemPort:
373 value->v.integer = tcpoids[i].index.subs[9];
374 break;
375 }
376 return (SNMP_ERR_NOERROR);
377 }
378