mibII_tcp.c revision 124861
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
46struct tcp_index {
47	struct asn_oid	index;
48	struct xtcpcb	*tp;
49};
50
51static u_int32_t tcp_tick;
52static struct tcpstat tcpstat;
53static struct xinpgen *xinpgen;
54static size_t xinpgen_len;
55static u_int tcp_count;
56static u_int tcp_total;
57
58static u_int oidnum;
59static struct tcp_index *tcpoids;
60
61static int
62tcp_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
70static int
71fetch_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 */
169int
170op_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
256int
257op_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