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