xref: /freebsd/contrib/bsnmp/snmp_mibII/mibII_tcp.c (revision df4b9fa460ab7f31d11b046d6f3335be101bc576)
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_fsm.h>
40 
41 struct tcp_index {
42 	struct asn_oid	index;
43 	struct xtcpcb	*tp;
44 };
45 
46 static uint64_t tcp_tick;
47 static uint64_t tcp_stats_tick;
48 static struct tcpstat tcpstat;
49 static uint64_t tcps_states[TCP_NSTATES];
50 static struct xinpgen *xinpgen;
51 static size_t xinpgen_len;
52 static u_int tcp_total;
53 static int tcp_rexmit_min;
54 static int tcp_rexmit_max;
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 	len = sizeof(tcp_rexmit_min);
94 	if (sysctlbyname("net.inet.tcp.rexmit_min", &tcp_rexmit_min, &len,
95 	    NULL, 0) == -1) {
96 		syslog(LOG_ERR, "net.inet.tcp.rexmit_min: %m");
97 		return (-1);
98 	}
99 	if (len != sizeof(tcp_rexmit_min)) {
100 		syslog(LOG_ERR, "net.inet.tcp.rexmit_min: wrong size");
101 		return (-1);
102 	}
103 
104 	len = sizeof(tcp_rexmit_max);
105 	if (sysctlbyname("net.inet.tcp.rexmit_max", &tcp_rexmit_max, &len,
106 	    NULL, 0) == -1) {
107 		syslog(LOG_ERR, "net.inet.tcp.rexmit_max: %m");
108 		return (-1);
109 	}
110 	if (len != sizeof(tcp_rexmit_max)) {
111 		syslog(LOG_ERR, "net.inet.tcp.rexmit_max: wrong size");
112 		return (-1);
113 	}
114 
115 	tcp_stats_tick = get_ticks();
116 
117 	return (0);
118 }
119 
120 static int
fetch_tcp(void)121 fetch_tcp(void)
122 {
123 	size_t len;
124 	struct xinpgen *ptr;
125 	struct xtcpcb *tp;
126 	struct tcp_index *oid;
127 	in_addr_t inaddr;
128 
129 	len = 0;
130 	if (sysctlbyname("net.inet.tcp.pcblist", NULL, &len, NULL, 0) == -1) {
131 		syslog(LOG_ERR, "net.inet.tcp.pcblist: %m");
132 		return (-1);
133 	}
134 	if (len > xinpgen_len) {
135 		if ((ptr = realloc(xinpgen, len)) == NULL) {
136 			syslog(LOG_ERR, "%zu: %m", len);
137 			return (-1);
138 		}
139 		xinpgen = ptr;
140 		xinpgen_len = len;
141 	}
142 	if (sysctlbyname("net.inet.tcp.pcblist", xinpgen, &len, NULL, 0) == -1) {
143 		syslog(LOG_ERR, "net.inet.tcp.pcblist: %m");
144 		return (-1);
145 	}
146 
147 	tcp_tick = get_ticks();
148 
149 	tcp_total = 0;
150 	for (ptr = (struct xinpgen *)(void *)((char *)xinpgen + xinpgen->xig_len);
151 	     ptr->xig_len > sizeof(struct xinpgen);
152              ptr = (struct xinpgen *)(void *)((char *)ptr + ptr->xig_len)) {
153 		tp = (struct xtcpcb *)ptr;
154 		if (tp->xt_inp.inp_gencnt > xinpgen->xig_gen ||
155 		    (tp->xt_inp.inp_vflag & (INP_IPV4|INP_IPV6)) == 0)
156 			continue;
157 
158 		if (tp->xt_inp.inp_vflag & INP_IPV4)
159 			tcp_total++;
160 	}
161 
162 	if (oidnum < tcp_total) {
163 		oid = realloc(tcpoids, tcp_total * sizeof(tcpoids[0]));
164 		if (oid == NULL) {
165 			free(tcpoids);
166 			oidnum = 0;
167 			return (0);
168 		}
169 		tcpoids = oid;
170 		oidnum = tcp_total;
171 	}
172 
173 	oid = tcpoids;
174 	for (ptr = (struct xinpgen *)(void *)((char *)xinpgen + xinpgen->xig_len);
175 	     ptr->xig_len > sizeof(struct xinpgen);
176              ptr = (struct xinpgen *)(void *)((char *)ptr + ptr->xig_len)) {
177 		tp = (struct xtcpcb *)ptr;
178 		if (tp->xt_inp.inp_gencnt > xinpgen->xig_gen ||
179 		    (tp->xt_inp.inp_vflag & INP_IPV4) == 0)
180 			continue;
181 		oid->tp = tp;
182 		oid->index.len = 10;
183 		inaddr = ntohl(tp->xt_inp.inp_laddr.s_addr);
184 		oid->index.subs[0] = (inaddr >> 24) & 0xff;
185 		oid->index.subs[1] = (inaddr >> 16) & 0xff;
186 		oid->index.subs[2] = (inaddr >>  8) & 0xff;
187 		oid->index.subs[3] = (inaddr >>  0) & 0xff;
188 		oid->index.subs[4] = ntohs(tp->xt_inp.inp_lport);
189 		inaddr = ntohl(tp->xt_inp.inp_faddr.s_addr);
190 		oid->index.subs[5] = (inaddr >> 24) & 0xff;
191 		oid->index.subs[6] = (inaddr >> 16) & 0xff;
192 		oid->index.subs[7] = (inaddr >>  8) & 0xff;
193 		oid->index.subs[8] = (inaddr >>  0) & 0xff;
194 		oid->index.subs[9] = ntohs(tp->xt_inp.inp_fport);
195 		oid++;
196 	}
197 
198 	qsort(tcpoids, tcp_total, sizeof(tcpoids[0]), tcp_compare);
199 
200 	return (0);
201 }
202 
203 /*
204  * Scalars
205  */
206 int
op_tcp(struct snmp_context * ctx __unused,struct snmp_value * value,u_int sub,u_int iidx __unused,enum snmp_op op)207 op_tcp(struct snmp_context *ctx __unused, struct snmp_value *value,
208     u_int sub, u_int iidx __unused, enum snmp_op op)
209 {
210 	switch (op) {
211 
212 	  case SNMP_OP_GETNEXT:
213 		abort();
214 
215 	  case SNMP_OP_GET:
216 		break;
217 
218 	  case SNMP_OP_SET:
219 		return (SNMP_ERR_NOT_WRITEABLE);
220 
221 	  case SNMP_OP_ROLLBACK:
222 	  case SNMP_OP_COMMIT:
223 		abort();
224 	}
225 
226 	if (tcp_stats_tick < this_tick)
227 		if (fetch_tcp_stats() == -1)
228 			return (SNMP_ERR_GENERR);
229 
230 	switch (value->var.subs[sub - 1]) {
231 
232 	  case LEAF_tcpRtoAlgorithm:
233 		value->v.integer = 4;	/* Van Jacobson */
234 		break;
235 
236 	  case LEAF_tcpRtoMin:
237 		value->v.integer = tcp_rexmit_min;
238 		break;
239 
240 	  case LEAF_tcpRtoMax:
241 		value->v.integer = tcp_rexmit_max;
242 		break;
243 
244 	  case LEAF_tcpMaxConn:
245 		value->v.integer = -1;
246 		break;
247 
248 	  case LEAF_tcpActiveOpens:
249 		value->v.uint32 = tcpstat.tcps_connattempt;
250 		break;
251 
252 	  case LEAF_tcpPassiveOpens:
253 		value->v.uint32 = tcpstat.tcps_accepts;
254 		break;
255 
256 	  case LEAF_tcpAttemptFails:
257 		value->v.uint32 = tcpstat.tcps_conndrops;
258 		break;
259 
260 	  case LEAF_tcpEstabResets:
261 		value->v.uint32 = tcpstat.tcps_drops;
262 		break;
263 
264 	  case LEAF_tcpCurrEstab:
265 		value->v.uint32 = tcps_states[TCPS_ESTABLISHED] +
266 		    tcps_states[TCPS_CLOSE_WAIT];
267 		break;
268 
269 	  case LEAF_tcpInSegs:
270 		value->v.uint32 = tcpstat.tcps_rcvtotal;
271 		break;
272 
273 	  case LEAF_tcpOutSegs:
274 		value->v.uint32 = tcpstat.tcps_sndtotal -
275 		    tcpstat.tcps_sndrexmitpack;
276 		break;
277 
278 	  case LEAF_tcpRetransSegs:
279 		value->v.uint32 = tcpstat.tcps_sndrexmitpack;
280 		break;
281 
282 	  case LEAF_tcpInErrs:
283 		value->v.uint32 = tcpstat.tcps_rcvbadsum +
284 		    tcpstat.tcps_rcvbadoff +
285 		    tcpstat.tcps_rcvshort;
286 		break;
287 	}
288 	return (SNMP_ERR_NOERROR);
289 }
290 
291 int
op_tcpconn(struct snmp_context * ctx __unused,struct snmp_value * value,u_int sub,u_int iidx __unused,enum snmp_op op)292 op_tcpconn(struct snmp_context *ctx __unused, struct snmp_value *value,
293     u_int sub, u_int iidx __unused, enum snmp_op op)
294 {
295 	u_int i;
296 
297 	if (tcp_tick < this_tick)
298 		if (fetch_tcp() == -1)
299 			return (SNMP_ERR_GENERR);
300 
301 	switch (op) {
302 
303 	  case SNMP_OP_GETNEXT:
304 		for (i = 0; i < tcp_total; i++)
305 			if (index_compare(&value->var, sub, &tcpoids[i].index) < 0)
306 				break;
307 		if (i == tcp_total)
308 			return (SNMP_ERR_NOSUCHNAME);
309 		index_append(&value->var, sub, &tcpoids[i].index);
310 		break;
311 
312 	  case SNMP_OP_GET:
313 		for (i = 0; i < tcp_total; i++)
314 			if (index_compare(&value->var, sub, &tcpoids[i].index) == 0)
315 				break;
316 		if (i == tcp_total)
317 			return (SNMP_ERR_NOSUCHNAME);
318 		break;
319 
320 	  case SNMP_OP_SET:
321 		return (SNMP_ERR_NOT_WRITEABLE);
322 
323 	  case SNMP_OP_ROLLBACK:
324 	  case SNMP_OP_COMMIT:
325 	  default:
326 		abort();
327 	}
328 
329 	switch (value->var.subs[sub - 1]) {
330 
331 	  case LEAF_tcpConnState:
332 		switch (tcpoids[i].tp->t_state) {
333 
334 		  case TCPS_CLOSED:
335 			value->v.integer = 1;
336 			break;
337 		  case TCPS_LISTEN:
338 			value->v.integer = 2;
339 			break;
340 		  case TCPS_SYN_SENT:
341 			value->v.integer = 3;
342 			break;
343 		  case TCPS_SYN_RECEIVED:
344 			value->v.integer = 4;
345 			break;
346 		  case TCPS_ESTABLISHED:
347 			value->v.integer = 5;
348 			break;
349 		  case TCPS_CLOSE_WAIT:
350 			value->v.integer = 8;
351 			break;
352 		  case TCPS_FIN_WAIT_1:
353 			value->v.integer = 6;
354 			break;
355 		  case TCPS_CLOSING:
356 			value->v.integer = 10;
357 			break;
358 		  case TCPS_LAST_ACK:
359 			value->v.integer = 9;
360 			break;
361 		  case TCPS_FIN_WAIT_2:
362 			value->v.integer = 7;
363 			break;
364 		  case TCPS_TIME_WAIT:
365 			value->v.integer = 11;
366 			break;
367 		  default:
368 			value->v.integer = 0;
369 			break;
370 		}
371 		break;
372 
373 	  case LEAF_tcpConnLocalAddress:
374 		value->v.ipaddress[0] = tcpoids[i].index.subs[0];
375 		value->v.ipaddress[1] = tcpoids[i].index.subs[1];
376 		value->v.ipaddress[2] = tcpoids[i].index.subs[2];
377 		value->v.ipaddress[3] = tcpoids[i].index.subs[3];
378 		break;
379 
380 	  case LEAF_tcpConnLocalPort:
381 		value->v.integer = tcpoids[i].index.subs[4];
382 		break;
383 
384 	  case LEAF_tcpConnRemAddress:
385 		value->v.ipaddress[0] = tcpoids[i].index.subs[5];
386 		value->v.ipaddress[1] = tcpoids[i].index.subs[6];
387 		value->v.ipaddress[2] = tcpoids[i].index.subs[7];
388 		value->v.ipaddress[3] = tcpoids[i].index.subs[8];
389 		break;
390 
391 	  case LEAF_tcpConnRemPort:
392 		value->v.integer = tcpoids[i].index.subs[9];
393 		break;
394 	}
395 	return (SNMP_ERR_NOERROR);
396 }
397