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