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