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
42struct tcp_index {
43	struct asn_oid	index;
44	struct xtcpcb	*tp;
45};
46
47static uint64_t tcp_tick;
48static struct tcpstat tcpstat;
49static struct xinpgen *xinpgen;
50static size_t xinpgen_len;
51static u_int tcp_count;
52static u_int tcp_total;
53
54static u_int oidnum;
55static struct tcp_index *tcpoids;
56
57static int
58tcp_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
66static int
67fetch_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 */
167int
168op_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
254int
255op_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