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 uint64_t tcp_stats_tick;
49static struct tcpstat tcpstat;
50static uint64_t tcps_states[TCP_NSTATES];
51static struct xinpgen *xinpgen;
52static size_t xinpgen_len;
53static u_int tcp_total;
54
55static u_int oidnum;
56static struct tcp_index *tcpoids;
57
58static int
59tcp_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
67static int
68fetch_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
98static int
99fetch_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 */
184int
185op_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
272int
273op_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