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
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
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
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
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
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