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